* [PATCH 02/10] Alchemy: Au1300 new interrupt controller
2009-03-06 16:20 ` [PATCH 01/10] Initial Au1300 and DBAu1300 support Kevin Hickey
@ 2009-03-06 16:20 ` Kevin Hickey
2009-03-07 9:49 ` Manuel Lauss
2009-03-06 16:20 ` [PATCH 03/10] Alchemy: Au1300/DB1300 UART support Kevin Hickey
` (8 subsequent siblings)
9 siblings, 1 reply; 26+ messages in thread
From: Kevin Hickey @ 2009-03-06 16:20 UTC (permalink / raw)
To: ralf, linux-mips; +Cc: Kevin Hickey
The Au1300 has a new interrupt controller (relative to the rest of the Alchemy
line). The differences were great enough to justify adding a whole new module.
Included in this patch is the new interrupt controller, a new implementation of
the cascade interrupt controller on the DB1300 board and some code to drive
LEDs on the DB1300 that is used by the interrupt controller.
A small change was made to the existing interrupt controller; it is "ifdef'd
out" for Au1300.
Since the cascade interrupt controller is virtually indentical (with the
exception of some constants) between the DB1300 and DB1200, a future
optimization may be to use the same code for both boards.
Signed-off-by: Kevin Hickey <khickey@rmicorp.com>
---
arch/mips/alchemy/Kconfig | 4 +
arch/mips/alchemy/common/Makefile | 4 +-
arch/mips/alchemy/common/gpio_int.c | 268 ++++++++++++++++++++++++++
arch/mips/alchemy/common/irq.c | 3 +
arch/mips/alchemy/devboards/Makefile | 5 +
arch/mips/alchemy/devboards/cascade_irq.c | 142 ++++++++++++++
arch/mips/alchemy/devboards/leds.c | 58 ++++++
arch/mips/include/asm/mach-au1x00/gpio_int.h | 239 +++++++++++++++++++++++
arch/mips/include/asm/mach-au1x00/irq.h | 34 ++++
9 files changed, 756 insertions(+), 1 deletions(-)
create mode 100644 arch/mips/alchemy/common/gpio_int.c
create mode 100644 arch/mips/alchemy/devboards/cascade_irq.c
create mode 100644 arch/mips/alchemy/devboards/leds.c
create mode 100644 arch/mips/include/asm/mach-au1x00/gpio_int.h
create mode 100644 arch/mips/include/asm/mach-au1x00/irq.h
diff --git a/arch/mips/alchemy/Kconfig b/arch/mips/alchemy/Kconfig
index 7198a88..2e189c2 100644
--- a/arch/mips/alchemy/Kconfig
+++ b/arch/mips/alchemy/Kconfig
@@ -114,18 +114,22 @@ endchoice
config SOC_AU1000
bool
select SOC_AU1X00
+ select AU_INT_CNTLR
config SOC_AU1100
bool
select SOC_AU1X00
+ select AU_INT_CNTLR
config SOC_AU1500
bool
select SOC_AU1X00
+ select AU_INT_CNTLR
config SOC_AU1550
bool
select SOC_AU1X00
+ select AU_INT_CNTLR
config SOC_AU1200
bool
diff --git a/arch/mips/alchemy/common/Makefile b/arch/mips/alchemy/common/Makefile
index d50d476..85ffa2e 100644
--- a/arch/mips/alchemy/common/Makefile
+++ b/arch/mips/alchemy/common/Makefile
@@ -7,7 +7,9 @@
obj-y += prom.o irq.o puts.o time.o reset.o \
clocks.o platform.o power.o setup.o \
- sleeper.o dma.o dbdma.o gpio.o
+ sleeper.o dma.o dbdma.o gpio.o gpio_int.o
+
+obj-$(CONFIG_SOC_AU13XX) += au13xx_res.o
obj-$(CONFIG_PCI) += pci.o
diff --git a/arch/mips/alchemy/common/gpio_int.c b/arch/mips/alchemy/common/gpio_int.c
new file mode 100644
index 0000000..c09b793
--- /dev/null
+++ b/arch/mips/alchemy/common/gpio_int.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2008 RMI Corporation
+ * Author: Kevin Hickey <khickey@rmicorp.com>
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifdef CONFIG_AU_GPIO_INT_CNTLR
+
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/interrupt.h> /* For functions called by do_IRQ */
+#include <asm/irq_cpu.h>
+
+#include <asm/mach-au1x00/gpio_int.h>
+#include <asm/mach-au1x00/au1000.h>
+
+#include <dev_boards.h>
+
+volatile struct gpio_int_regs *const gpio_int =
+ (struct gpio_int_regs *)(GPIO_INT_CTRLR_BASE + KSEG1_OFFSET);
+
+static struct gpio_int_cfg __initdata basic_irqs[];
+
+#ifdef CONFIG_SOC_AU13XX
+static struct gpio_int_cfg __initdata basic_irqs[] = {
+ { AU1300_IRQ_DDMA, 1, LEVEL_HIGH, HW_INT_1, DEV_CTRL },
+
+ { AU1300_IRQ_RTC_TICK, 1, RISING, HW_INT_1, DEV_CTRL },
+ { AU1300_IRQ_TOY_TICK, 1, RISING, HW_INT_1, DEV_CTRL },
+
+ { AU1300_IRQ_LCD, 1, LEVEL_HIGH, HW_INT_1, DEV_CTRL },
+
+ { AU1300_IRQ_UART1, 1, LEVEL_HIGH, HW_INT_1, DEV_CTRL },
+ { AU1300_IRQ_UART1, 1, LEVEL_HIGH, HW_INT_1, DEV_CTRL },
+ { AU1300_IRQ_UART2, 1, LEVEL_HIGH, HW_INT_1, DEV_CTRL },
+ { AU1300_IRQ_UART3, 1, LEVEL_HIGH, HW_INT_1, DEV_CTRL },
+
+ { AU1300_IRQ_SD1, 1, LEVEL_HIGH, HW_INT_1, DEV_CTRL },
+ { AU1300_IRQ_SD2, 1, LEVEL_HIGH, HW_INT_1, DEV_CTRL },
+
+ { AU1300_IRQ_USB, 1, LEVEL_HIGH, HW_INT_1, DEV_CTRL },
+
+ { AU1300_IRQ_BSA, 1, LEVEL_HIGH, HW_INT_1, DEV_CTRL },
+ { AU1300_IRQ_MPE, 1, RISING, HW_INT_1, DEV_CTRL },
+ { AU1300_IRQ_ITE, 1, LEVEL_HIGH, HW_INT_1, DEV_CTRL },
+
+ { AU1300_IRQ_RTCMATCH_1, 1, RISING, HW_INT_1, DEV_CTRL },
+ { AU1300_IRQ_RTCMATCH_1, 1, RISING, HW_INT_1, DEV_CTRL },
+ { AU1300_IRQ_RTCMATCH_2, 0, RISING, HW_INT_0, DEV_CTRL },
+
+ { AU1300_IRQ_TOYMATCH_1, 1, RISING, HW_INT_1, DEV_CTRL },
+ { AU1300_IRQ_TOYMATCH_1, 1, RISING, HW_INT_1, DEV_CTRL },
+ { AU1300_IRQ_TOYMATCH_2, 1, RISING, HW_INT_1, DEV_CTRL },
+
+
+ // KH: TODO - Move this to the board file.
+ { 5, 0, LEVEL_HIGH, HW_INT_0, GPIO_IN },
+};
+
+/*
+ * KH: TODO - Consider moving to board specific location...
+ */
+static struct gpio_int_cfg __initdata basic_gpios[] = {
+ { 32, 0, DISABLED, HW_INT_0, DEV_CTRL },
+ { 33, 0, DISABLED, HW_INT_0, DEV_CTRL },
+ { 34, 0, DISABLED, HW_INT_0, DEV_CTRL },
+ { 35, 0, DISABLED, HW_INT_0, DEV_CTRL },
+ { 36, 0, DISABLED, HW_INT_0, DEV_CTRL },
+ { 37, 0, DISABLED, HW_INT_0, DEV_CTRL },
+};
+#endif
+
+int __initdata nr_basic_irqs = ARRAY_SIZE(basic_irqs);
+
+/*
+ ****************************************************************************
+ * Functions and delcaration for irq_chip
+ ****************************************************************************
+ */
+void gpio_int_ack(unsigned int irq)
+{
+ u32 intr = irq - GPINT_LINUX_IRQ_OFFSET;
+ u32 bank = GPINT_BANK_FROM_INT(intr);
+ u32 bit = GPINT_BIT_FROM_INT(bank, intr);
+
+ au_iowrite32(bit, &gpio_int->int_pend[bank]);
+}
+
+void gpio_int_mask(unsigned int irq)
+{
+ u32 intr = irq - GPINT_LINUX_IRQ_OFFSET;
+ u32 bank = GPINT_BANK_FROM_INT(intr);
+ u32 bit = GPINT_BIT_FROM_INT(bank, intr);
+
+ au_iowrite32(bit, &gpio_int->int_maskclr[bank]);
+}
+
+void gpio_int_unmask(unsigned int irq)
+{
+ u32 intr = irq - GPINT_LINUX_IRQ_OFFSET;
+ u32 bank = GPINT_BANK_FROM_INT(intr);
+ u32 bit = GPINT_BIT_FROM_INT(bank, intr);
+
+ au_iowrite32(bit, &gpio_int->int_mask[bank]);
+}
+
+void gpio_int_mask_ack(unsigned int irq)
+{
+ u32 intr = irq - GPINT_LINUX_IRQ_OFFSET;
+ u32 bank = GPINT_BANK_FROM_INT(intr);
+ u32 bit = GPINT_BIT_FROM_INT(bank, intr);
+
+ au_iowrite32(bit, &gpio_int->int_maskclr[bank]);
+ au_iowrite32(bit, &gpio_int->int_pend[bank]);
+}
+
+static struct irq_chip gpio_int_irq_type = {
+ .name = "Au GPIO/INT",
+ .ack = gpio_int_ack,
+ .mask = gpio_int_mask,
+ .unmask = gpio_int_unmask,
+ .mask_ack = gpio_int_mask_ack
+};
+/*****************************************************************************/
+
+void set_pin_cfg(const struct gpio_int_cfg *cfg)
+{
+ u32 tmp;
+ tmp = GPINT_PINCTL_N(cfg->pinctl);
+ tmp |= GPINT_INTLINE_N(cfg->intline);
+ tmp |= GPINT_INTCFG_N(cfg->intcfg);
+ tmp |= cfg->intwake ? GPINT_INTWAKE_ENABLE : 0;
+ au_iowrite32(tmp, &gpio_int->gp_int[cfg->number]);
+}
+
+void set_gpio(u8 gpio, u8 value)
+{
+ u32 bank = GPINT_BANK_FROM_GPIO(gpio);
+ u32 bit = GPINT_BIT_FROM_GPIO(bank, gpio);
+
+ if (value == 0)
+ au_iowrite32(1 << bit, &gpio_int->pin_valclr[bank]);
+ else
+ au_iowrite32(1 << bit, &gpio_int->pin_val[bank]);
+}
+
+u8 get_gpio(u8 gpio)
+{
+ u32 bank = GPINT_BANK_FROM_GPIO(gpio);
+ u32 bit = GPINT_BIT_FROM_GPIO(bank, gpio);
+ u32 tmp;
+
+ tmp = au_ioread32(&gpio_int->pin_val[bank]);
+ return tmp >> bit;
+}
+
+
+void __init arch_init_irq(void)
+{
+ int i;
+
+ /*
+ * Initialize the basic MIPS interrupt components.
+ */
+ mips_cpu_irq_init();
+
+ for (i = 0; i < GPINT_NUM_BANKS; ++i)
+ gpio_int->int_maskclr[i] = ~0UL;
+
+
+ for (i = 0; i < ARRAY_SIZE(basic_gpios); ++i) {
+ set_pin_cfg(&basic_gpios[i]);
+ }
+
+ for (i = 0; i < nr_basic_irqs; ++i) {
+ printk("Initializing IRQ %d\n", basic_irqs[i].number);
+ set_pin_cfg(&basic_irqs[i]);
+ if (basic_irqs[i].intcfg == LEVEL_LOW)
+ set_irq_chip_and_handler_name(
+ basic_irqs[i].number + GPINT_LINUX_IRQ_OFFSET,
+ &gpio_int_irq_type,
+ handle_level_irq,
+ "lowlevel");
+ else if (basic_irqs[i].intcfg == LEVEL_HIGH)
+ set_irq_chip_and_handler_name(
+ basic_irqs[i].number + GPINT_LINUX_IRQ_OFFSET,
+ &gpio_int_irq_type,
+ handle_level_irq,
+ "highlevel");
+ else if (basic_irqs[i].intcfg == FALLING)
+ set_irq_chip_and_handler_name(
+ basic_irqs[i].number + GPINT_LINUX_IRQ_OFFSET,
+ &gpio_int_irq_type,
+ handle_edge_irq,
+ "fallingedge");
+ else if (basic_irqs[i].intcfg == RISING)
+ set_irq_chip_and_handler_name(
+ basic_irqs[i].number + GPINT_LINUX_IRQ_OFFSET,
+ &gpio_int_irq_type,
+ handle_edge_irq,
+ "risingedge");
+ else if (basic_irqs[i].intcfg == ANY_CHANGE)
+ set_irq_chip_and_handler_name(
+ basic_irqs[i].number + GPINT_LINUX_IRQ_OFFSET,
+ &gpio_int_irq_type,
+ handle_edge_irq,
+ "bothedge");
+ else
+ set_irq_chip(
+ basic_irqs[i].number + GPINT_LINUX_IRQ_OFFSET,
+ &gpio_int_irq_type);
+ }
+
+ set_c0_status(IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4);
+
+ board_init_irq();
+}
+
+asmlinkage void plat_irq_dispatch(void)
+{
+ unsigned int intr;
+ u32 bank;
+ u32 reg_msk;
+ unsigned int pending = read_c0_status() & read_c0_cause();
+ /*
+ * C0 timer tick
+ */
+ if (pending & CAUSEF_IP7)
+ do_IRQ(MIPS_CPU_IRQ_BASE + 7);
+ else if (pending & (CAUSEF_IP2 | CAUSEF_IP3)) {
+ intr = au_ioread32(&gpio_int->pri_enc);
+ bank = GPINT_BANK_FROM_INT(intr);
+ reg_msk = GPINT_BIT_FROM_INT(bank, intr);
+
+ if (intr != 127) {
+ if (pending & CAUSEF_IP3)
+ board_irq_dispatch(intr);
+
+ do_IRQ(GPINT_LINUX_IRQ_OFFSET + intr);
+ }
+ } else {
+ printk(KERN_WARNING
+ "ALCHEMY GPIO_INT: Unexpected cause was set. %08x\n",
+ pending);
+ }
+
+}
+
+#endif
diff --git a/arch/mips/alchemy/common/irq.c b/arch/mips/alchemy/common/irq.c
index c88c821..f8742dd 100644
--- a/arch/mips/alchemy/common/irq.c
+++ b/arch/mips/alchemy/common/irq.c
@@ -24,6 +24,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#ifdef CONFIG_AU_INT_CNTLR
#include <linux/bitops.h>
#include <linux/init.h>
@@ -609,3 +610,5 @@ void __init arch_init_irq(void)
set_c0_status(IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3);
}
+
+#endif
diff --git a/arch/mips/alchemy/devboards/Makefile b/arch/mips/alchemy/devboards/Makefile
index 0d2d224..8cce4d0 100644
--- a/arch/mips/alchemy/devboards/Makefile
+++ b/arch/mips/alchemy/devboards/Makefile
@@ -17,3 +17,8 @@ obj-$(CONFIG_MIPS_DB1500) += db1x00/
obj-$(CONFIG_MIPS_DB1550) += db1x00/
obj-$(CONFIG_MIPS_BOSPORUS) += db1x00/
obj-$(CONFIG_MIPS_MIRAGE) += db1x00/
+
+# These two files are used only by DB1300 today but will be used by DB1200 and
+# possibly others in the future.
+obj-$(CONFIG_MIPS_DB1300) += cascade_irq.o
+obj-$(CONFIG_MIPS_DB1300) += leds.o
diff --git a/arch/mips/alchemy/devboards/cascade_irq.c b/arch/mips/alchemy/devboards/cascade_irq.c
new file mode 100644
index 0000000..6d0a965
--- /dev/null
+++ b/arch/mips/alchemy/devboards/cascade_irq.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2003-2008 RMI Corporation. All rights reserved.
+ * Author: Kevin Hickey <khickey@rmicorp.com>
+ *
+ * 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 SOFTWARE IS PROVIDED BY RMI Corporation 'AS IS' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL RMI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mips-boards/db1300.h>
+
+#include <asm/mach-au1x00/dev_boards.h>
+
+/*
+ * The following must be declared/defined in an included file:
+ * - volatile struct bcsr_regs (declared)
+ * (which much include fields int_status, intset_mask, intclr_mask, intset,
+ * and intclr)
+ * - volatile struct bcsr_regs *const bcsr (defined)
+ * - CASCADE_IRQ_MIN
+ * - CASCADE_IRQ_MAX
+ * - CASCADE_IRQ_TYPE_STRING
+ * - CASCADE_IRQ (System IRQ to which the cascade is connected)
+ */
+
+void __init board_init_irq(void);
+
+irqreturn_t cascade_handler(int irq, void *dev_id)
+{
+ u16 int_status = au_ioread16(&db_bcsr->int_status);
+ int irq_in_service;
+
+ au_iowrite16(int_status, &db_bcsr->int_status);
+ for ( ; int_status; int_status &= int_status - 1) {
+ irq_in_service = CASCADE_IRQ_MIN + __ffs(int_status);
+ db_set_hex((u8)(irq_in_service));
+ do_IRQ(irq_in_service);
+ }
+
+ return IRQ_RETVAL(1);
+}
+
+DEFINE_MUTEX(cascade_use_count_mutex);
+static int cascade_use_count = 0;
+
+static void cascade_mask(unsigned int irq)
+{
+ au_iowrite16(1 << (irq - CASCADE_IRQ_MIN), &db_bcsr->intclr_mask);
+}
+
+static void cascade_unmask(unsigned int irq)
+{
+ au_iowrite16(1 << (irq - CASCADE_IRQ_MIN), &db_bcsr->intset_mask);
+}
+
+static void cascade_enable(unsigned int irq)
+{
+ au_iowrite16(1 << (irq - CASCADE_IRQ_MIN), &db_bcsr->intset);
+ cascade_unmask(irq);
+}
+
+static void cascade_disable(unsigned int irq)
+{
+ au_iowrite16(1 << (irq - CASCADE_IRQ_MIN), &db_bcsr->intclr);
+ cascade_mask(irq);
+}
+
+
+static unsigned int cascade_startup(unsigned int irq)
+{
+ int retval = 0;
+
+ mutex_lock(&cascade_use_count_mutex);
+ ++cascade_use_count;
+ if (cascade_use_count == 1)
+ retval = request_irq(CASCADE_IRQ,
+ &cascade_handler, 0, "Cascade",
+ &cascade_handler);
+ mutex_unlock(&cascade_use_count_mutex);
+
+ cascade_enable(irq);
+ cascade_unmask(irq);
+
+ return retval;
+}
+
+static void cascade_shutdown(unsigned int irq)
+{
+ cascade_mask(irq);
+ cascade_disable(irq);
+
+ mutex_lock(&cascade_use_count_mutex);
+ --cascade_use_count;
+ if (cascade_use_count == 0)
+ free_irq(CASCADE_IRQ, &cascade_handler);
+ mutex_unlock(&cascade_use_count_mutex);
+}
+
+static struct irq_chip cascade_irq_type = {
+ .name = CASCADE_IRQ_TYPE_STRING,
+ .startup = cascade_startup,
+ .shutdown = cascade_shutdown,
+ .mask = cascade_mask,
+ .enable = cascade_enable,
+ .disable = cascade_disable,
+ .unmask = cascade_unmask,
+ .mask_ack = cascade_mask
+};
+
+void __init board_init_irq(void)
+{
+ int irq;
+
+ for (irq = CASCADE_IRQ_MIN;
+ irq < CASCADE_IRQ_MAX; ++irq ) {
+ printk("Initializing IRQ %d\n", irq);
+ set_irq_chip_and_handler(irq, &cascade_irq_type,
+ handle_level_irq);
+ cascade_disable(irq);
+ }
+}
diff --git a/arch/mips/alchemy/devboards/leds.c b/arch/mips/alchemy/devboards/leds.c
new file mode 100644
index 0000000..75be345
--- /dev/null
+++ b/arch/mips/alchemy/devboards/leds.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2003-2008 RMI Corporation. All rights reserved.
+ * Author: Kevin Hickey <khickey@rmicorp.com>
+ *
+ * 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 SOFTWARE IS PROVIDED BY RMI Corporation 'AS IS' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL RMI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/dev_boards.h>
+
+/*
+ * Requires the following to be defined in the board-specifc .h file:
+ * - HEX_REGS_KSEG1_ADDR
+ * - struct hex_regs with members:
+ * - hex (set the hex value)
+ * - BCSR_REGS_KSEG1_ADDR
+ * - struct bcsr_regs
+ */
+
+static volatile hex_regs *const hex = (hex_regs *)(HEX_REGS_KSEG1_ADDR);
+
+/*
+ * Takes a u8 because though the register is 16 bits, only 8 appear
+ */
+void db_set_hex(u8 val)
+{
+ au_iowrite16((u16)val, &hex->hex);
+}
+
+/*
+ * 2 dots use the least significant 2 bits
+ * Setting a bit lights the LED (opposite of the register)
+ */
+void db_set_hex_dots(u8 val)
+{
+ u16 leds = au_ioread16(&db_bcsr->disk_leds);
+ leds |= 0x3;
+ leds &= (~(val & 0x3));
+ au_iowrite16(leds, &db_bcsr->disk_leds);
+}
diff --git a/arch/mips/include/asm/mach-au1x00/gpio_int.h b/arch/mips/include/asm/mach-au1x00/gpio_int.h
new file mode 100644
index 0000000..85df296
--- /dev/null
+++ b/arch/mips/include/asm/mach-au1x00/gpio_int.h
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2008 RMI Corporation
+ * Author: Kevin Hickey <khickey@rmicorp.com>
+ *
+ * Defines and macros for the GPIO and Interrupt controller for Alchemy,
+ * introduced in the Au13xx series.
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _GPIO_INT_H
+#define _GPIO_INT_H
+
+#include <linux/types.h>
+
+/*
+ * There are a total 128 'channels' defined by the Au13xx databook. However,
+ * this requires 4 sperate 32bit registers for programming. Each register is
+ * called a 'bank' for ease of use.
+ */
+#define GPINT_BANK0 0
+#define GPINT_BANK1 1
+#define GPINT_BANK2 2
+#define GPINT_BANK3 3
+
+#define GPINT_NUM_BANKS 4 /* 0-3 */
+#define GPINT_MAX_BANK (GPINT_BANK3)
+
+#define GPINT_GPIO_PER_BANK 32
+#define GPINT_INTS_PER_BANK GPINT_GPIO_PER_BANK
+
+/* Total number of interrupts our architecture allows */
+#define GPINT_MAX_INTS (GPINT_NUM_BANKS*GPINT_INTS_PER_BANK)
+
+/* Current maximum supported GPIO/INTERRUPTs */
+#define GPINT_NUM_GPIO GPINT_MAX_INTS
+#define GPINT_NUM_INTERRUPTS GPINT_MAX_INTS
+
+/* Starting GPIO/INTERRUPT for each bank */
+#define GPINT_BANK0_START 0
+#define GPINT_BANK1_START 32
+#define GPINT_BANK2_START 64
+#define GPINT_BANK3_START 96
+
+/* divide by 32 to get bank */
+#define GPINT_BANK_FROM_GPIO(n) (n>>5)
+#define GPINT_BANK_FROM_INT(n) GPINT_BANK_FROM_GPIO(n)
+/* multiply by 32 to get base */
+#define GPINT_BIT_FROM_GPIO(b, n) (1<<(n-(b<<5)))
+#define GPINT_BIT_FROM_INT(b, n) GPINT_BIT_FROM_GPIO(b, n)
+
+struct gpio_int_regs {
+ /* R/W1S */
+ /* u32 pin_val0; 0x00 */
+ /* u32 pin_val1; 0x04 */
+ /* u32 pin_val2; 0x08 */
+ /* u32 pin_val3; 0x0C */
+ u32 pin_val[GPINT_NUM_BANKS];
+
+ /* W1C */
+ /* u32 pin_valclr0 0x10 */
+ /* u32 pin_valclr1; 0x14 */
+ /* u32 pin_valclr2; 0x18 */
+ /* u32 pin_valclr3; 0x1C */
+ u32 pin_valclr[GPINT_NUM_BANKS];
+
+ /* R/W1C */
+ /* u32 int_pend0; 0x20 */
+ /* u32 int_pend1; 0x24 */
+ /* u32 int_pend2; 0x28 */
+ /* u32 int_pend3; 0x2c */
+ u32 int_pend[GPINT_NUM_BANKS];
+
+ u32 pri_enc; /* 0x30 */
+ u32 _resvd0[3]; /* 0x34-0x3c */
+
+ /* R/W1S */
+ /* u32 int_mask0; 0x40 */
+ /* u32 int_mask1; 0x44 */
+ /* u32 int_mask2; 0x48 */
+ /* u32 int_mask3; 0x4c */
+ u32 int_mask[GPINT_NUM_BANKS];
+
+ /* W1C */
+ /* u32 int_maskclr0; 0x50 */
+ /* u32 int_maskclr1; 0x54 */
+ /* u32 int_maskclr2; 0x58 */
+ /* u32 int_maskclr3; 0x5C */
+ u32 int_maskclr[GPINT_NUM_BANKS];
+
+ /* R/W */
+ u32 dma_sel; /* 0x60 */
+ u32 _resvd1[(0x80-0x64)/4]; /* 0x64-0x7C */
+
+ /* W */
+ /* u32 dev_sel0; 0x80 */
+ /* u32 dev_sel1; 0x84 */
+ /* u32 dev_sel2; 0x88 */
+ /* u32 dev_sel3; 0x8C */
+ u32 dev_sel[GPINT_NUM_BANKS];
+
+ /* W */
+ /* u32 dev_selclr0; 0x90 */
+ /* u32 dev_selclr1; 0x94 */
+ /* u32 dev_selclr2; 0x98 */
+ /* u32 dev_selclr3; 0x9C */
+ u32 dev_selclr[GPINT_NUM_BANKS];
+
+ /* R */
+ /* u32 reset_val0; 0xA0 */
+ /* u32 reset_val1; 0xA4 */
+ /* u32 reset_val2; 0xA8 */
+ /* u32 reset_val3; 0xAC */
+ u32 reset_val[GPINT_NUM_BANKS];
+
+ /* 0xB0 - 0xFFC */
+ u32 _resvd2[(0x1000-0xB0)/4];
+
+ /* R/W -- when interrupt mask is clear */
+ /* R -- when interrupt mask is set */
+ /* u32 gp_int0; 0x1000 */
+ /* u32 gp_int1; 0x1004 */
+ /* u32 gp_int2; 0x1008 */
+ /* u32 gp_int2; 0x100C */
+ /* u32 gp_intN; 0x1000 + (N*4) */
+ u32 gp_int[GPINT_MAX_INTS];
+};
+
+extern volatile struct gpio_int_regs *const gpio_int;
+
+#define GPINT_DMASEL_DMA0 (0)
+#define GPINT_DMASEL_DMA0_N(n) (((n)&0xFF)<<GPINT_DMASEL_DMA0)
+#define GPINT_DMASEL_DMA1 (8)
+#define GPINT_DMASEL_DMA1_N(n) (((n)&0xFF)<<GPINT_DMASEL_DMA1)
+
+#define GPINT_PINCTL (0)
+#define GPINT_PINCTL_N(n) (((n)&0x3)<<GPINT_PINCTL)
+#define GPINT_PINCTL_GPIOINPUT GPINT_PINCTL_N(0)
+#define GPINT_PINCTL_INTERRUPT GPINT_PINCTL_N(1)
+#define GPINT_PINCTL_GPIOOUT_0 GPINT_PINCTL_N(2)
+#define GPINT_PINCTL_GPIOOUT_1 GPINT_PINCTL_N(3)
+
+#define GPINT_INTLINE (2)
+#define GPINT_INTLINE_N(n) (((n)&0x3)<<GPINT_INTLINE)
+#define GPINT_INTLINE_CPUINT_0 GPINT_INTLINE_N(0)
+#define GPINT_INTLINE_CPUINT_1 GPINT_INTLINE_N(1)
+#define GPINT_INTLINE_CPUINT_2 GPINT_INTLINE_N(2)
+#define GPINT_INTLINE_CPUINT_3 GPINT_INTLINE_N(3)
+
+#define GPINT_INTCFG (4)
+#define GPINT_INTCFG_N(n) (((n)&0x7)<<GPINT_INTCFG)
+#define GPINT_INTCFG_DISABLE GPINT_INTCFG_N(0)
+#define GPINT_INTCFG_LL GPINT_INTCFG_N(1)
+#define GPINT_INTCFG_HL GPINT_INTCFG_N(2)
+#define GPINT_INTCFG_FE GPINT_INTCFG_N(5)
+#define GPINT_INTCFG_RE GPINT_INTCFG_N(6)
+#define GPINT_INTCFG_CHANGE GPINT_INTCFG_N(7)
+
+#define GPINT_INTWAKE (7)
+#define GPINT_INTWAKE_ENABLE ((1)<<GPINT_INTWAKE)
+
+/* GPIO */
+#define GPIO_N(N) (1 << (N))
+
+/*
+ * Take caution when reordering or changing values; used directly in pin
+ * configuration register
+ */
+enum intcfg_vals { DISABLED = 0, LEVEL_LOW, LEVEL_HIGH,
+ FALLING = 5, RISING, ANY_CHANGE };
+enum intline_vals { HW_INT_0 = 0, HW_INT_1, HW_INT_2, HW_INT_3 };
+enum pinctl_vals { GPIO_IN = 0, DEV_CTRL, GPIO_OUT_0, GPIO_OUT_1 };
+
+/*
+ * Defines the settings for a given interrupt "channel"
+ */
+struct gpio_int_cfg {
+ int number;
+ bool intwake;
+ enum intcfg_vals intcfg;
+ enum intline_vals intline;
+ enum pinctl_vals pinctl;
+};
+
+/*
+ * Linux uses IRQ 0-7 for the 8 causes. That means that all of our channel
+ * bits need to be offset by 8 either when passed to do_IRQ or when received
+ * through the irq_chip calls
+ */
+#define GPINT_LINUX_IRQ_OFFSET 8
+
+void board_irq_dispatch(unsigned int irq);
+
+/*
+ * Configure a GPIO/Interrupt pin. Many of the defined interrupt pins as
+ * decribed in the Au1300 data book are configured during platform
+ * initialization, however drivers may wish to repurpose those or other GPIO
+ * pins later.
+ *
+ * Changing the behavior of an interrupt pin after a handler has been
+ * installed is ill advised and should be avoided.
+ */
+void set_pin_cfg(const struct gpio_int_cfg *cfg);
+
+/*
+ * Set the GPIO to the specified value. The value must be 0 or 1. Any other
+ * value results in a no-op.
+ *
+ * This call will implicitly reconfigure the pin to be a GPIO if it is
+ * configured as a device pin.
+ */
+void set_gpio(u8 gpio, u8 value);
+
+/*
+ * Get the value of any GPIO pin (including those controlled by devices).
+ *
+ * This will not change the pin configuration
+ */
+u8 get_gpio(u8 gpio);
+
+#endif /* _GPIO_INT_H */
+
diff --git a/arch/mips/include/asm/mach-au1x00/irq.h b/arch/mips/include/asm/mach-au1x00/irq.h
new file mode 100644
index 0000000..91d06a5
--- /dev/null
+++ b/arch/mips/include/asm/mach-au1x00/irq.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2008 RMI Corporation
+ * Author: Kevin Hickey <khickey@rmicorp.com>
+ *
+ * Defines and macros for the GPIO and Interrupt controller for Alchemy,
+ * introduced in the Au13xx series.
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _MACH_AU1X00_INT_H
+#define _MACH_AU1X00_INT_H
+
+#define NR_IRQS 255
+#define MIPS_CPU_IRQ_BASE 0
+
+#endif /* _MACH_AU1X00_INT_H */
--
1.5.4.3
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH 07/10] Alchemy: SMSC 9210 Ethernet support
2009-03-06 16:20 ` [PATCH 01/10] Initial Au1300 and DBAu1300 support Kevin Hickey
` (4 preceding siblings ...)
2009-03-06 16:20 ` [PATCH 06/10] Alchemy: Au1300 USB support Kevin Hickey
@ 2009-03-06 16:20 ` Kevin Hickey
2009-03-07 9:35 ` Manuel Lauss
2009-03-06 16:20 ` [PATCH 08/10] Alchemy: DB1300 blink leds on timer tick Kevin Hickey
` (3 subsequent siblings)
9 siblings, 1 reply; 26+ messages in thread
From: Kevin Hickey @ 2009-03-06 16:20 UTC (permalink / raw)
To: ralf, linux-mips; +Cc: Kevin Hickey
This patch adds support for the SMSC 9210 Ethernet chip, specialized for
Alchemy platforms (including the DB1300). The ethernet driver code was
provided by SMSC; the platform shim by RMI.
Signed-off-by: Kevin Hickey <khickey@rmicorp.com>
---
drivers/net/Kconfig | 6 +
drivers/net/Makefile | 3 +
drivers/net/smsc9210/Makefile | 9 +
drivers/net/smsc9210/ioctl_118.h | 298 ++
drivers/net/smsc9210/platform_alchemy.c | 88 +
drivers/net/smsc9210/platform_alchemy.h | 117 +
drivers/net/smsc9210/smsc9210.h | 23 +
drivers/net/smsc9210/smsc9210_main.c | 7189 +++++++++++++++++++++++++++++++
8 files changed, 7733 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/smsc9210/Makefile
create mode 100644 drivers/net/smsc9210/ioctl_118.h
create mode 100644 drivers/net/smsc9210/platform_alchemy.c
create mode 100644 drivers/net/smsc9210/platform_alchemy.h
create mode 100644 drivers/net/smsc9210/smsc9210.h
create mode 100644 drivers/net/smsc9210/smsc9210_main.c
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 8b13c5d..791735e 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -907,6 +907,12 @@ config SMC91X
inserted in and removed from the running kernel whenever you want).
The module will be called smc91x. If you want to compile it as a
module, say M here and read <file:Documentation/kbuild/modules.txt>.
+
+config SMSC9210
+ tristate "SMSC 9210 support"
+ depends on SOC_AU1X00
+ help
+ TODO
config NET_NETX
tristate "NetX Ethernet support"
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 3c627d0..7c4052f 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -222,6 +222,9 @@ obj-$(CONFIG_IBMVETH) += ibmveth.o
obj-$(CONFIG_S2IO) += s2io.o
obj-$(CONFIG_MYRI10GE) += myri10ge/
obj-$(CONFIG_SMC91X) += smc91x.o
+
+obj-$(CONFIG_SMSC9210) += smsc9210/
+
obj-$(CONFIG_SMC911X) += smc911x.o
obj-$(CONFIG_SMSC911X) += smsc911x.o
obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
diff --git a/drivers/net/smsc9210/Makefile b/drivers/net/smsc9210/Makefile
new file mode 100644
index 0000000..76a1217
--- /dev/null
+++ b/drivers/net/smsc9210/Makefile
@@ -0,0 +1,9 @@
+#
+# Copyright 2008 RMI Corporation. All rights reserved.
+# Author: Kevin Hickey <khickey@rmicorp.com>
+#
+
+obj-$(CONFIG_SMSC9210) += smsc9210.o
+
+smsc9210-objs := smsc9210_main.o platform_alchemy.o
+
diff --git a/drivers/net/smsc9210/ioctl_118.h b/drivers/net/smsc9210/ioctl_118.h
new file mode 100644
index 0000000..07187d7
--- /dev/null
+++ b/drivers/net/smsc9210/ioctl_118.h
@@ -0,0 +1,298 @@
+/***************************************************************************
+
+ *
+
+ * Copyright (C) 2004-2005 SMSC
+
+ *
+
+ * 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.
+
+ *
+
+ ***************************************************************************
+
+ * File: ioctl_118.h
+
+ */
+
+
+
+#ifndef IOCTL_118_H
+
+#define IOCTL_118_H
+
+
+
+#define DRIVER_VERSION (0x00000155UL)
+
+
+
+#define SMSC9118_DRIVER_SIGNATURE (0x82745BACUL+DRIVER_VERSION)
+
+#define SMSC9118_APP_SIGNATURE (0x987BEF28UL+DRIVER_VERSION)
+
+
+
+#define SMSC9118_IOCTL (SIOCDEVPRIVATE + 0xB)
+
+
+
+#define COMMAND_BASE (0x974FB832UL)
+
+
+
+#define COMMAND_GET_SIGNATURE (COMMAND_BASE+0)
+
+
+
+#define COMMAND_LAN_GET_REG (COMMAND_BASE+1)
+
+#define COMMAND_LAN_SET_REG (COMMAND_BASE+2)
+
+
+
+#define COMMAND_MAC_GET_REG (COMMAND_BASE+3)
+
+#define COMMAND_MAC_SET_REG (COMMAND_BASE+4)
+
+
+
+#define COMMAND_PHY_GET_REG (COMMAND_BASE+5)
+
+#define COMMAND_PHY_SET_REG (COMMAND_BASE+6)
+
+
+
+#define COMMAND_DUMP_LAN_REGS (COMMAND_BASE+7)
+
+#define LAN_REG_ID_REV (0)
+
+#define LAN_REG_INT_CFG (1)
+
+#define LAN_REG_INT_STS (2)
+
+#define LAN_REG_INT_EN (3)
+
+#define LAN_REG_BYTE_TEST (4)
+
+#define LAN_REG_FIFO_INT (5)
+
+#define LAN_REG_RX_CFG (6)
+
+#define LAN_REG_TX_CFG (7)
+
+#define LAN_REG_HW_CFG (8)
+
+#define LAN_REG_RX_DP_CTRL (9)
+
+#define LAN_REG_RX_FIFO_INF (10)
+
+#define LAN_REG_TX_FIFO_INF (11)
+
+#define LAN_REG_PMT_CTRL (12)
+
+#define LAN_REG_GPIO_CFG (13)
+
+#define LAN_REG_GPT_CFG (14)
+
+#define LAN_REG_GPT_CNT (15)
+
+#define LAN_REG_FPGA_REV (16)
+
+#define LAN_REG_WORD_SWAP (17)
+
+#define LAN_REG_FREE_RUN (18)
+
+#define LAN_REG_RX_DROP (19)
+
+#define LAN_REG_MAC_CSR_CMD (21)
+
+#define LAN_REG_MAC_CSR_DATA (22)
+
+#define LAN_REG_AFC_CFG (23)
+
+#define LAN_REG_E2P_CMD (24)
+
+#define LAN_REG_E2P_DATA (25)
+
+
+
+#define COMMAND_DUMP_MAC_REGS (COMMAND_BASE+8)
+
+#define MAC_REG_MAC_CR (0)
+
+#define MAC_REG_ADDRH (1)
+
+#define MAC_REG_ADDRL (2)
+
+#define MAC_REG_HASHH (3)
+
+#define MAC_REG_HASHL (4)
+
+#define MAC_REG_MII_ACC (5)
+
+#define MAC_REG_MII_DATA (6)
+
+#define MAC_REG_FLOW (7)
+
+#define MAC_REG_VLAN1 (8)
+
+#define MAC_REG_VLAN2 (9)
+
+#define MAC_REG_WUFF (10)
+
+#define MAC_REG_WUCSR (11)
+
+
+
+#define COMMAND_DUMP_PHY_REGS (COMMAND_BASE+9)
+
+#define PHY_REG_0 (0)
+
+#define PHY_REG_1 (1)
+
+#define PHY_REG_2 (2)
+
+#define PHY_REG_3 (3)
+
+#define PHY_REG_4 (4)
+
+#define PHY_REG_5 (5)
+
+#define PHY_REG_6 (6)
+
+#define PHY_REG_16 (7)
+
+#define PHY_REG_17 (8)
+
+#define PHY_REG_18 (9)
+
+#define PHY_REG_20 (10)
+
+#define PHY_REG_21 (11)
+
+#define PHY_REG_22 (12)
+
+#define PHY_REG_23 (13)
+
+#define PHY_REG_27 (14)
+
+#define PHY_REG_28 (15)
+
+#define PHY_REG_29 (16)
+
+#define PHY_REG_30 (17)
+
+#define PHY_REG_31 (18)
+
+
+
+#define COMMAND_DUMP_EEPROM (COMMAND_BASE+10)
+
+
+
+#define COMMAND_GET_MAC_ADDRESS (COMMAND_BASE+11)
+
+#define COMMAND_SET_MAC_ADDRESS (COMMAND_BASE+12)
+
+#define COMMAND_LOAD_MAC_ADDRESS (COMMAND_BASE+13)
+
+#define COMMAND_SAVE_MAC_ADDRESS (COMMAND_BASE+14)
+
+#define COMMAND_SET_DEBUG_MODE (COMMAND_BASE+15)
+
+
+
+#define COMMAND_SET_POWER_MODE (COMMAND_BASE+16)
+
+#define COMMAND_GET_POWER_MODE (COMMAND_BASE+17)
+
+
+
+#define COMMAND_SET_LINK_MODE (COMMAND_BASE+18)
+
+#define COMMAND_GET_LINK_MODE (COMMAND_BASE+19)
+
+#define COMMAND_GET_CONFIGURATION (COMMAND_BASE+20)
+
+#define COMMAND_DUMP_TEMP (COMMAND_BASE+21)
+
+#define COMMAND_READ_BYTE (COMMAND_BASE+22)
+
+#define COMMAND_READ_WORD (COMMAND_BASE+23)
+
+#define COMMAND_READ_DWORD (COMMAND_BASE+24)
+
+#define COMMAND_WRITE_BYTE (COMMAND_BASE+25)
+
+#define COMMAND_WRITE_WORD (COMMAND_BASE+26)
+
+#define COMMAND_WRITE_DWORD (COMMAND_BASE+27)
+
+#define COMMAND_CHECK_LINK (COMMAND_BASE+28)
+
+
+
+//the following codes are intended for cmd9118 only
+
+// they are not intended to have any use in the driver
+
+#define COMMAND_RUN_SERVER (COMMAND_BASE+29)
+
+#define COMMAND_RUN_TUNER (COMMAND_BASE+30)
+
+
+
+#define COMMAND_GET_FLOW_PARAMS (COMMAND_BASE+31)
+
+#define COMMAND_SET_FLOW_PARAMS (COMMAND_BASE+32)
+
+#define COMMAND_SET_AMDIX_STS (COMMAND_BASE+33)
+
+#define COMMAND_GET_AMDIX_STS (COMMAND_BASE+34)
+
+typedef struct _SMSC9118_IOCTL_DATA {
+
+ unsigned long dwSignature;
+
+ unsigned long dwCommand;
+
+ unsigned long Data[0x60];
+
+ char Strng1[30];
+
+ char Strng2[10];
+
+} SMSC9118_IOCTL_DATA, *PSMSC9118_IOCTL_DATA;
+
+
+
+#endif
+
+
+
diff --git a/drivers/net/smsc9210/platform_alchemy.c b/drivers/net/smsc9210/platform_alchemy.c
new file mode 100644
index 0000000..1da19a6
--- /dev/null
+++ b/drivers/net/smsc9210/platform_alchemy.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2003-2008 RMI Corporation. All rights reserved.
+ * Author: Kevin Hickey <khickey@rmicorp.com>
+ *
+ * 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 SOFTWARE IS PROVIDED BY RMI Corporation 'AS IS' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL RMI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/interrupt.h>
+#include "platform_alchemy.h"
+
+u32 Platform_Initialize(PPLATFORM_DATA pd, u32 lan_base, u32 bus_width)
+{
+ return BASE_ADDRESS;
+}
+
+
+/*
+ * Ignores the passed in irq in favor of the one we know to be correct...
+ */
+bool Platform_RequestIRQ(PPLATFORM_DATA pd,
+ u32 irq,
+ irqreturn_t (*pIsr)(int irq,void *dev_id),
+ void* dev_id )
+{
+ int retval = request_irq(PLATFORM_IRQ, pIsr, 0, "SMSC 9210 Ethernet", dev_id);
+ pd->irq_dev_id = dev_id;
+
+ return retval == 0 ? true : false;
+}
+
+void Platform_FreeIRQ(PPLATFORM_DATA pd)
+{
+ free_irq(PLATFORM_IRQ, pd->irq_dev_id);
+}
+
+void Platform_WriteFifo(u32 lan_base, u32 *buf, u32 count)
+{
+ int i;
+ for(i = 0; i < count; ++i)
+ {
+ au_iowrite32(*buf, (u32*)(lan_base+0x20));
+ ++buf;
+ }
+}
+
+void Platform_ReadFifo(u32 lan_base, u32 *buf, u32 count)
+{
+ int i;
+ for(i = 0; i < count; ++i)
+ {
+ *buf = au_ioread32((u32*)lan_base);
+ ++buf;
+ }
+
+}
+
+void Platform_GetFlowControlParameters(PPLATFORM_DATA pd, PFLOW_CONTROL_PARAMETERS fcp, bool useDma)
+{
+ /*
+ * Borrowed from xscale_linux.c in the 16-bit PIO 8210 section
+ */
+ memset(fcp,0,sizeof(FLOW_CONTROL_PARAMETERS));
+ fcp->BurstPeriod=100;
+ fcp->IntDeas=0;
+ fcp->MaxThroughput=0x74378UL;
+ fcp->MaxPacketCount=0x13AUL;
+ fcp->PacketCost=0x02UL;
+ fcp->BurstPeriod=0x76UL;
+ fcp->IntDeas=0x18UL;
+}
diff --git a/drivers/net/smsc9210/platform_alchemy.h b/drivers/net/smsc9210/platform_alchemy.h
new file mode 100644
index 0000000..17f42e3
--- /dev/null
+++ b/drivers/net/smsc9210/platform_alchemy.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2003-2008 RMI Corporation. All rights reserved.
+ * Author: Kevin Hickey <khickey@rmicorp.com>
+ *
+ * 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 SOFTWARE IS PROVIDED BY RMI Corporation 'AS IS' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL RMI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef PLATFORM_ALCHEMY_H
+#define PLATFORM_ALCHEMY_H
+
+#include <asm/mach-au1x00/au1000.h>
+#include <linux/types.h>
+
+#if defined(CONFIG_MIPS_DB1200)
+#include <asm/mach-db1x00/db1200.h>
+#define PLATFORM_IRQ DB1200_DC_INT
+#elif defined(CONFIG_MIPS_HMP10)
+#include <asm/mips-boards/hmp10.h>
+#define PLATFORM_IRQ HMP10_ETH_IRQ
+#elif defined(CONFIG_MIPS_DB1300)
+#include <asm/mips-boards/db1300.h>
+#define PLATFORM_IRQ DB1300_ETHERNET_IRQ
+#endif
+
+#include "smsc9210.h"
+/*
+ * Make the IRQ a push-pull; on DB1300 there is no pull up so open-drain will
+ * always be low.
+ */
+#define PLATFORM_IRQ_POL 0
+#define PLATFORM_IRQ_TYPE 1
+#define PLATFORM_CACHE_LINE_BYTES 0
+#define PLATFORM_RX_DMA TRANSFER_PIO
+#define PLATFORM_TX_DMA TRANSFER_PIO
+#define PLATFORM_DMA_THRESHOLD 0
+#define PLATFORM_NAME "Alchemy"
+
+#ifdef CONFIG_MIPS_DB1200
+#define BASE_ADDRESS 0xBA000000
+#else
+#define BASE_ADDRESS 0xB9000000
+#endif
+
+typedef struct {
+ void *irq_dev_id;
+} PLATFORM_DATA, *PPLATFORM_DATA;
+
+u32 Platform_Initialize(PPLATFORM_DATA pd, u32 lan_base, u32 bus_width);
+
+static void Platform_CleanUp(PPLATFORM_DATA pd) { }
+static u32 Platform_CurrentIRQ(PPLATFORM_DATA pd)
+{
+ return PLATFORM_IRQ;
+}
+
+static inline void Platform_SetRegDW(u32 lan_base, u32 offset, u32 val)
+{
+ au_writel(val, (lan_base + offset));
+}
+
+static inline u32 Platform_GetRegDW(u32 lan_base, u32 offset)
+{
+ return au_readl(lan_base + offset);
+}
+
+bool Platform_RequestIRQ(PPLATFORM_DATA pd,
+ u32 irq,
+ irqreturn_t (*pIsr)(int irq, void *dev_id),
+ void* dev_id );
+
+void Platform_FreeIRQ(PPLATFORM_DATA pd);
+void Platform_GetFlowControlParameters(PPLATFORM_DATA pd, PFLOW_CONTROL_PARAMETERS fcp, bool useDma);
+void Platform_WriteFifo(u32 lan_base, u32 *buf, u32 count);
+void Platform_ReadFifo(u32 lan_base, u32 *buf, u32 count);
+
+/* * We're not supporting DMA at this time, so degenerate all functions to return
+ * errors.
+ */
+static inline bool Platform_IsValidDmaChannel(u32 chan) { return false; }
+static inline bool Platform_DmaInitialize(PPLATFORM_DATA pd, u32 chan) { return false; }
+static inline bool Platform_DmaDisable(PPLATFORM_DATA pd, u32 chan) { return false; }
+static inline void Platform_CacheInvalidate(PPLATFORM_DATA pd, const void *const sa,
+ const u32 len) { }
+static inline void Platform_CachePurge(PPLATFORM_DATA pd, const void *const sa,
+ const u32 len) { }
+static inline u32 Platform_RequestDmaChannel(PPLATFORM_DATA pd) { return TRANSFER_PIO; }
+static inline void Platform_ReleaseDmaChannel(PPLATFORM_DATA pd, u32 chan) { }
+static inline bool Platform_DmaStartXfer(PPLATFORM_DATA pd, const DMA_XFER * const pDmaXfer)
+{
+ return false;
+}
+static inline u32 Platform_DmaGetDwCnt(PPLATFORM_DATA platformData, const u32 dwDmaCh)
+{
+ return 0;
+}
+static inline void Platform_DmaComplete(PPLATFORM_DATA platformData, const u32 dwDmaCh) { }
+
+
+#endif /* PLATFORM_ALCHEMY_H */
diff --git a/drivers/net/smsc9210/smsc9210.h b/drivers/net/smsc9210/smsc9210.h
new file mode 100644
index 0000000..903b156
--- /dev/null
+++ b/drivers/net/smsc9210/smsc9210.h
@@ -0,0 +1,23 @@
+#ifndef SMSC9210_H
+#define SMSC9210_H
+
+#define TRANSFER_PIO (256UL)
+
+typedef struct _DMA_XFER
+{
+ u32 dwLanReg;
+ u32 *pdwBuf;
+ u32 dwDmaCh;
+ u32 dwDwCnt;
+ bool fMemWr;
+} DMA_XFER;
+
+typedef struct _FLOW_CONTROL_PARAMETERS
+{
+ u32 MaxThroughput;
+ u32 MaxPacketCount;
+ u32 PacketCost;
+ u32 BurstPeriod;
+ u32 IntDeas;
+} FLOW_CONTROL_PARAMETERS, *PFLOW_CONTROL_PARAMETERS;
+#endif /* SMSC9210_H */
diff --git a/drivers/net/smsc9210/smsc9210_main.c b/drivers/net/smsc9210/smsc9210_main.c
new file mode 100644
index 0000000..15fbf7f
--- /dev/null
+++ b/drivers/net/smsc9210/smsc9210_main.c
@@ -0,0 +1,7189 @@
+/***************************************************************************
+ *
+ * Copyright (C) 2004-2005 SMSC
+ *
+ * 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.
+ *
+ ***************************************************************************
+ * File: smsc9118.c
+ * see readme.txt for programmers guide
+ */
+
+
+//#define UseScatterGather
+//#define UseTxCsum
+//#define UseRxCsum
+
+#define USE_DEBUG
+
+#ifndef __KERNEL__
+# define __KERNEL__
+#endif
+
+#ifdef USING_LINT
+#include "lint.h"
+#else //not USING_LINT
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/mii.h>
+#include <linux/timer.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <linux/version.h>
+
+#include <asm/mach-au1x00/prom.h>
+#include "smsc9210.h"
+
+#endif //not USING_LINT
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+typedef void irqreturn_t;
+#define IRQ_NONE
+#define IRQ_HANDLED
+#define IRQ_RETVAL(x)
+#else
+#define LINUX_2_6_OR_NEWER
+#endif
+
+#ifdef USE_DEBUG
+static u32 debug_mode=0x7UL;
+#else
+static u32 debug_mode=0x0UL;
+#endif
+
+#ifdef USE_DEBUG
+//select debug modes
+#define USE_WARNING
+#define USE_TRACE
+#define USE_ASSERT
+#endif //USE_DEBUG
+
+#define USE_LED1_WORK_AROUND // 10/100 LED link-state inversion
+#define USE_PHY_WORK_AROUND // output polarity inversion
+
+typedef long TIME_SPAN;
+#define MAX_TIME_SPAN ((TIME_SPAN)(0x7FFFFFFFUL))
+typedef unsigned short WORD;
+typedef unsigned char BYTE;
+//typedef unsigned char bool;
+#define true ((bool)1)
+#define false ((bool)0)
+
+//unsigned char testbuff[1600]={0};
+//u32 * p= NULL;
+
+
+#define HIBYTE(word) ((BYTE)(((WORD)(word))>>8))
+#define LOBYTE(word) ((BYTE)(((WORD)(word))&0x00FFU))
+#define HIWORD(dWord) ((WORD)(((u32)(dWord))>>16))
+#define LOWORD(dWord) ((WORD)(((u32)(dWord))&0x0000FFFFUL))
+
+#define TRANSFER_PIO (256UL)
+#define TRANSFER_REQUEST_DMA (255UL)
+//these are values that can be assigned to
+//PLATFORM_RX_DMA
+//PLATFORM_TX_DMA
+// in addition to any specific dma channel
+
+/*******************************************************
+ * Macro: SMSC_TRACE
+ * Description:
+ * This macro is used like printf.
+ * It can be used anywhere you want to display information
+ * For any release version it should not be left in
+ * performance sensitive Tx and Rx code paths.
+ * To use this macro define USE_TRACE and set bit 0 of debug_mode
+ *******************************************************/
+#ifdef USING_LINT
+extern void SMSC_TRACE(const char * a, ...);
+#else //not USING_LINT
+#ifdef USE_TRACE
+#ifndef USE_WARNING
+#define USE_WARNING
+#endif
+# define SMSC_TRACE(msg,args...) \
+ if(debug_mode&0x01UL) { \
+ printk("SMSC: " msg "\n", ## args); \
+ }
+#else
+# define SMSC_TRACE(msg,args...)
+#endif
+#endif //not USING_LINT
+
+/*******************************************************
+ * Macro: SMSC_WARNING
+ * Description:
+ * This macro is used like printf.
+ * It can be used anywhere you want to display warning information
+ * For any release version it should not be left in
+ * performance sensitive Tx and Rx code paths.
+ * To use this macro define USE_TRACE or
+ * USE_WARNING and set bit 1 of debug_mode
+ *******************************************************/
+
+
+#ifdef USING_LINT
+extern void SMSC_WARNING(const char * a, ...);
+#else //not USING_LINT
+#ifdef USE_WARNING
+#ifndef USE_ASSERT
+#define USE_ASSERT
+#endif
+# define SMSC_WARNING(msg, args...) \
+ if(debug_mode&0x02UL) { \
+ printk("SMSC_WARNING: " msg "\n",## args); \
+ }
+#else
+# define SMSC_WARNING(msg, args...)
+#endif
+#endif //not USING_LINT
+
+
+/*******************************************************
+ * Macro: SMSC_ASSERT
+ * Description:
+ * This macro is used to test assumptions made when coding.
+ * It can be used anywhere, but is intended only for situations
+ * where a failure is fatal.
+ * If code execution where allowed to continue it is assumed that
+ * only further unrecoverable errors would occur and so this macro
+ * includes an infinite loop to prevent further corruption.
+ * Assertions are only intended for use during developement to
+ * insure consistency of logic through out the driver.
+ * A driver should not be released if assertion failures are
+ * still occuring.
+ * To use this macro define USE_TRACE or USE_WARNING or
+ * USE_ASSERT
+ *******************************************************/
+#ifdef USING_LINT
+extern void SMSC_ASSERT(bool condition);
+#else //not USING_LINT
+#ifdef USE_ASSERT
+# define SMSC_ASSERT(condition) \
+ if(!(condition)) { \
+ printk("SMSC_ASSERTION_FAILURE: File=" __FILE__ ", Line=%d\n",__LINE__); \
+ while(1); \
+ }
+#else
+# define SMSC_ASSERT(condition)
+#endif
+#endif //not USING_LINT
+
+//Below are the register offsets and bit definitions
+// of the Lan9118 memory space
+#define RX_DATA_FIFO (0x00UL)
+#define TX_DATA_FIFO (0x20UL)
+#define TX_CMD_A_INT_ON_COMP_ (0x80000000UL)
+#define TX_CMD_A_INT_BUF_END_ALGN_ (0x03000000UL)
+#define TX_CMD_A_INT_4_BYTE_ALGN_ (0x00000000UL)
+#define TX_CMD_A_INT_16_BYTE_ALGN_ (0x01000000UL)
+#define TX_CMD_A_INT_32_BYTE_ALGN_ (0x02000000UL)
+#define TX_CMD_A_INT_DATA_OFFSET_ (0x001F0000UL)
+#define TX_CMD_A_INT_FIRST_SEG_ (0x00002000UL)
+#define TX_CMD_A_INT_LAST_SEG_ (0x00001000UL)
+#define TX_CMD_A_BUF_SIZE_ (0x000007FFUL)
+#define TX_CMD_B_PKT_TAG_ (0xFFFF0000UL)
+#define TX_CMD_B_CSUM_ENABLE (0x00004000UL)
+#define TX_CMD_B_ADD_CRC_DISABLE_ (0x00002000UL)
+#define TX_CMD_B_DISABLE_PADDING_ (0x00001000UL)
+#define TX_CMD_B_PKT_BYTE_LENGTH_ (0x000007FFUL)
+
+#define RX_STATUS_FIFO (0x40UL)
+#define RX_STS_ES_ (0x00008000UL)
+#define RX_STS_MCAST_ (0x00000400UL)
+#define RX_STATUS_FIFO_PEEK (0x44UL)
+#define TX_STATUS_FIFO (0x48UL)
+#define TX_STATUS_FIFO_PEEK (0x4CUL)
+#define ID_REV (0x50UL)
+#define ID_REV_CHIP_ID_ (0xFFFF0000UL) // RO
+#define ID_REV_REV_ID_ (0x0000FFFFUL) // RO
+
+#define INT_CFG (0x54UL)
+#define INT_CFG_INT_DEAS_ (0xFF000000UL) // R/W
+#define INT_CFG_IRQ_INT_ (0x00001000UL) // RO
+#define INT_CFG_IRQ_EN_ (0x00000100UL) // R/W
+#define INT_CFG_IRQ_POL_ (0x00000010UL) // R/W Not Affected by SW Reset
+#define INT_CFG_IRQ_TYPE_ (0x00000001UL) // R/W Not Affected by SW Reset
+
+#define INT_STS (0x58UL)
+#define INT_STS_SW_INT_ (0x80000000UL) // R/WC
+#define INT_STS_TXSTOP_INT_ (0x02000000UL) // R/WC
+#define INT_STS_RXSTOP_INT_ (0x01000000UL) // R/WC
+#define INT_STS_RXDFH_INT_ (0x00800000UL) // R/WC
+#define INT_STS_RXDF_INT_ (0x00400000UL) // R/WC
+#define INT_STS_TX_IOC_ (0x00200000UL) // R/WC
+#define INT_STS_RXD_INT_ (0x00100000UL) // R/WC
+#define INT_STS_GPT_INT_ (0x00080000UL) // R/WC
+#define INT_STS_PHY_INT_ (0x00040000UL) // RO
+#define INT_STS_PME_INT_ (0x00020000UL) // R/WC
+#define INT_STS_TXSO_ (0x00010000UL) // R/WC
+#define INT_STS_RWT_ (0x00008000UL) // R/WC
+#define INT_STS_RXE_ (0x00004000UL) // R/WC
+#define INT_STS_TXE_ (0x00002000UL) // R/WC
+#define INT_STS_ERX_ (0x00001000UL) // R/WC
+#define INT_STS_TDFU_ (0x00000800UL) // R/WC
+#define INT_STS_TDFO_ (0x00000400UL) // R/WC
+#define INT_STS_TDFA_ (0x00000200UL) // R/WC
+#define INT_STS_TSFF_ (0x00000100UL) // R/WC
+#define INT_STS_TSFL_ (0x00000080UL) // R/WC
+#define INT_STS_RDFO_ (0x00000040UL) // R/WC
+#define INT_STS_RDFL_ (0x00000020UL) // R/WC
+#define INT_STS_RSFF_ (0x00000010UL) // R/WC
+#define INT_STS_RSFL_ (0x00000008UL) // R/WC
+#define INT_STS_GPIO2_INT_ (0x00000004UL) // R/WC
+#define INT_STS_GPIO1_INT_ (0x00000002UL) // R/WC
+#define INT_STS_GPIO0_INT_ (0x00000001UL) // R/WC
+
+#define INT_EN (0x5CUL)
+#define INT_EN_SW_INT_EN_ (0x80000000UL) // R/W
+#define INT_EN_TXSTOP_INT_EN_ (0x02000000UL) // R/W
+#define INT_EN_RXSTOP_INT_EN_ (0x01000000UL) // R/W
+#define INT_EN_RXDFH_INT_EN_ (0x00800000UL) // R/W
+#define INT_EN_RXDF_INT_EN_ (0x00400000UL) // R/W
+#define INT_EN_TIOC_INT_EN_ (0x00200000UL) // R/W
+#define INT_EN_RXD_INT_EN_ (0x00100000UL) // R/W
+#define INT_EN_GPT_INT_EN_ (0x00080000UL) // R/W
+#define INT_EN_PHY_INT_EN_ (0x00040000UL) // R/W
+#define INT_EN_PME_INT_EN_ (0x00020000UL) // R/W
+#define INT_EN_TXSO_EN_ (0x00010000UL) // R/W
+#define INT_EN_RWT_EN_ (0x00008000UL) // R/W
+#define INT_EN_RXE_EN_ (0x00004000UL) // R/W
+#define INT_EN_TXE_EN_ (0x00002000UL) // R/W
+#define INT_EN_ERX_EN_ (0x00001000UL) // R/W
+#define INT_EN_TDFU_EN_ (0x00000800UL) // R/W
+#define INT_EN_TDFO_EN_ (0x00000400UL) // R/W
+#define INT_EN_TDFA_EN_ (0x00000200UL) // R/W
+#define INT_EN_TSFF_EN_ (0x00000100UL) // R/W
+#define INT_EN_TSFL_EN_ (0x00000080UL) // R/W
+#define INT_EN_RDFO_EN_ (0x00000040UL) // R/W
+#define INT_EN_RDFL_EN_ (0x00000020UL) // R/W
+#define INT_EN_RSFF_EN_ (0x00000010UL) // R/W
+#define INT_EN_RSFL_EN_ (0x00000008UL) // R/W
+#define INT_EN_GPIO2_INT_ (0x00000004UL) // R/W
+#define INT_EN_GPIO1_INT_ (0x00000002UL) // R/W
+#define INT_EN_GPIO0_INT_ (0x00000001UL) // R/W
+
+#define BYTE_TEST (0x64UL)
+#define FIFO_INT (0x68UL)
+#define FIFO_INT_TX_AVAIL_LEVEL_ (0xFF000000UL) // R/W
+#define FIFO_INT_TX_STS_LEVEL_ (0x00FF0000UL) // R/W
+#define FIFO_INT_RX_AVAIL_LEVEL_ (0x0000FF00UL) // R/W
+#define FIFO_INT_RX_STS_LEVEL_ (0x000000FFUL) // R/W
+
+#define RX_CFG (0x6CUL)
+#define RX_CFG_RX_END_ALGN_ (0xC0000000UL) // R/W
+#define RX_CFG_RX_END_ALGN4_ (0x00000000UL) // R/W
+#define RX_CFG_RX_END_ALGN16_ (0x40000000UL) // R/W
+#define RX_CFG_RX_END_ALGN32_ (0x80000000UL) // R/W
+#define RX_CFG_RX_DMA_CNT_ (0x0FFF0000UL) // R/W
+#define RX_CFG_RX_DUMP_ (0x00008000UL) // R/W
+#define RX_CFG_RXDOFF_ (0x00001F00UL) // R/W
+#define RX_CFG_RXDOFF_2_ (0x00000200UL) //Rx data offset is 2
+#define RX_CFG_RXDOFF_18_ (0x00001200UL) //Rx data offset is 0x12
+#define RX_CFG_RXBAD_ (0x00000001UL) // R/W
+
+#define TX_CFG (0x70UL)
+#define TX_CFG_TX_DMA_LVL_ (0xE0000000UL) // R/W
+#define TX_CFG_TX_DMA_CNT_ (0x0FFF0000UL) // R/W Self Clearing
+#define TX_CFG_TXS_DUMP_ (0x00008000UL) // Self Clearing
+#define TX_CFG_TXD_DUMP_ (0x00004000UL) // Self Clearing
+#define TX_CFG_TXSAO_ (0x00000004UL) // R/W
+#define TX_CFG_TX_ON_ (0x00000002UL) // R/W
+#define TX_CFG_STOP_TX_ (0x00000001UL) // Self Clearing
+
+#define HW_CFG (0x74UL)
+#define HW_CFG_AMDIX_EN_STRAP_STS_ (0x01000000UL)
+#define HW_CFG_TTM_ (0x00200000UL) // R/W
+#define HW_CFG_SF_ (0x00100000UL) // R/W
+#define HW_CFG_TX_FIF_SZ_ (0x000F0000UL) // R/W
+#define HW_CFG_TR_ (0x00003000UL) // R/W
+#define HW_CFG_PHY_CLK_SEL_ (0x00000060UL) // R/W
+#define HW_CFG_PHY_CLK_SEL_INT_PHY_ (0x00000000UL) // R/W
+#define HW_CFG_PHY_CLK_SEL_EXT_PHY_ (0x00000020UL) // R/W
+#define HW_CFG_PHY_CLK_SEL_CLK_DIS_ (0x00000040UL) // R/W
+#define HW_CFG_SMI_SEL_ (0x00000010UL) // R/W
+#define HW_CFG_EXT_PHY_DET_ (0x00000008UL) // RO
+#define HW_CFG_EXT_PHY_EN_ (0x00000004UL) // R/W
+#define HW_CFG_32_16_BIT_MODE_ (0x00000004UL) // RO
+#define HW_CFG_SRST_TO_ (0x00000002UL) // RO
+#define HW_CFG_SRST_ (0x00000001UL) // Self Clearing
+
+#define RX_DP_CTRL (0x78UL)
+#define RX_DP_CTRL_RX_FFWD_ (0x00000FFFUL) // R/W
+#define RX_DP_CTRL_FFWD_BUSY_ (0x80000000UL) // RO
+
+#define RX_FIFO_INF (0x7CUL)
+#define RX_FIFO_INF_RXSUSED_ (0x00FF0000UL) // RO
+#define RX_FIFO_INF_RXDUSED_ (0x0000FFFFUL) // RO
+
+#define TX_FIFO_INF (0x80UL)
+#define TX_FIFO_INF_TSUSED_ (0x00FF0000UL) // RO
+#define TX_FIFO_INF_TSFREE_ (0x00FF0000UL) // RO
+#define TX_FIFO_INF_TDFREE_ (0x0000FFFFUL) // RO
+
+#define PMT_CTRL (0x84UL)
+#define PMT_CTRL_PM_MODE_ (0x00018000UL) // Self Clearing
+#define PMT_CTRL_PHY_RST_ (0x00000400UL) // Self Clearing
+#define PMT_CTRL_WOL_EN_ (0x00000200UL) // R/W
+#define PMT_CTRL_ED_EN_ (0x00000100UL) // R/W
+#define PMT_CTRL_PME_TYPE_ (0x00000040UL) // R/W Not Affected by SW Reset
+#define PMT_CTRL_WUPS_ (0x00000030UL) // R/WC
+#define PMT_CTRL_WUPS_NOWAKE_ (0x00000000UL) // R/WC
+#define PMT_CTRL_WUPS_ED_ (0x00000010UL) // R/WC
+#define PMT_CTRL_WUPS_WOL_ (0x00000020UL) // R/WC
+#define PMT_CTRL_WUPS_MULTI_ (0x00000030UL) // R/WC
+#define PMT_CTRL_PME_IND_ (0x00000008UL) // R/W
+#define PMT_CTRL_PME_POL_ (0x00000004UL) // R/W
+#define PMT_CTRL_PME_EN_ (0x00000002UL) // R/W Not Affected by SW Reset
+#define PMT_CTRL_READY_ (0x00000001UL) // RO
+
+#define GPIO_CFG (0x88UL)
+#define GPIO_CFG_LED3_EN_ (0x40000000UL) // R/W
+#define GPIO_CFG_LED2_EN_ (0x20000000UL) // R/W
+#define GPIO_CFG_LED1_EN_ (0x10000000UL) // R/W
+#define GPIO_CFG_GPIO2_INT_POL_ (0x04000000UL) // R/W
+#define GPIO_CFG_GPIO1_INT_POL_ (0x02000000UL) // R/W
+#define GPIO_CFG_GPIO0_INT_POL_ (0x01000000UL) // R/W
+#define GPIO_CFG_EEPR_EN_ (0x00E00000UL) // R/W
+#define GPIO_CFG_GPIOBUF2_ (0x00040000UL) // R/W
+#define GPIO_CFG_GPIOBUF1_ (0x00020000UL) // R/W
+#define GPIO_CFG_GPIOBUF0_ (0x00010000UL) // R/W
+#define GPIO_CFG_GPIODIR2_ (0x00000400UL) // R/W
+#define GPIO_CFG_GPIODIR1_ (0x00000200UL) // R/W
+#define GPIO_CFG_GPIODIR0_ (0x00000100UL) // R/W
+#define GPIO_CFG_GPIOD4_ (0x00000020UL) // R/W
+#define GPIO_CFG_GPIOD3_ (0x00000010UL) // R/W
+#define GPIO_CFG_GPIOD2_ (0x00000004UL) // R/W
+#define GPIO_CFG_GPIOD1_ (0x00000002UL) // R/W
+#define GPIO_CFG_GPIOD0_ (0x00000001UL) // R/W
+
+#define GPT_CFG (0x8CUL)
+#define GPT_CFG_TIMER_EN_ (0x20000000UL) // R/W
+#define GPT_CFG_GPT_LOAD_ (0x0000FFFFUL) // R/W
+
+#define GPT_CNT (0x90UL)
+#define GPT_CNT_GPT_CNT_ (0x0000FFFFUL) // RO
+
+#define FPGA_REV (0x94UL)
+#define FPGA_REV_FPGA_REV_ (0x0000FFFFUL) // RO
+
+#define WORD_SWAP (0x98UL)
+#define FREE_RUN (0x9CUL)
+#define RX_DROP (0xA0UL)
+#define MAC_CSR_CMD (0xA4UL)
+#define MAC_CSR_CMD_CSR_BUSY_ (0x80000000UL) // Self Clearing
+#define MAC_CSR_CMD_R_NOT_W_ (0x40000000UL) // R/W
+#define MAC_CSR_CMD_CSR_ADDR_ (0x000000FFUL) // R/W
+
+#define MAC_CSR_DATA (0xA8UL)
+#define AFC_CFG (0xACUL)
+#define AFC_CFG_AFC_HI_ (0x00FF0000UL) // R/W
+#define AFC_CFG_AFC_LO_ (0x0000FF00UL) // R/W
+#define AFC_CFG_BACK_DUR_ (0x000000F0UL) // R/W
+#define AFC_CFG_FCMULT_ (0x00000008UL) // R/W
+#define AFC_CFG_FCBRD_ (0x00000004UL) // R/W
+#define AFC_CFG_FCADD_ (0x00000002UL) // R/W
+#define AFC_CFG_FCANY_ (0x00000001UL) // R/W
+
+#define E2P_CMD (0xB0UL)
+#define E2P_CMD_EPC_BUSY_ (0x80000000UL) // Self Clearing
+#define E2P_CMD_EPC_CMD_ (0x70000000UL) // R/W
+#define E2P_CMD_EPC_CMD_READ_ (0x00000000UL) // R/W
+#define E2P_CMD_EPC_CMD_EWDS_ (0x10000000UL) // R/W
+#define E2P_CMD_EPC_CMD_EWEN_ (0x20000000UL) // R/W
+#define E2P_CMD_EPC_CMD_WRITE_ (0x30000000UL) // R/W
+#define E2P_CMD_EPC_CMD_WRAL_ (0x40000000UL) // R/W
+#define E2P_CMD_EPC_CMD_ERASE_ (0x50000000UL) // R/W
+#define E2P_CMD_EPC_CMD_ERAL_ (0x60000000UL) // R/W
+#define E2P_CMD_EPC_CMD_RELOAD_ (0x70000000UL) // R/W
+#define E2P_CMD_EPC_TIMEOUT_ (0x00000200UL) // R
+#define E2P_CMD_MAC_ADDR_LOADED_ (0x00000100UL) // RO
+#define E2P_CMD_EPC_ADDR_ (0x000000FFUL) // R/W
+
+#define E2P_DATA (0xB4UL)
+#define E2P_DATA_EEPROM_DATA_ (0x000000FFUL) // R/W
+//end of lan register offsets and bit definitions
+
+#define LAN_REGISTER_EXTENT (0x00002000UL)
+
+//The following describes the synchronization policies used in this driver.
+//Register Name Policy
+//RX_DATA_FIFO Only used by the Rx Thread, Rx_ProcessPackets
+//TX_DATA_FIFO Only used by the Tx Thread, Tx_SendSkb
+//RX_STATUS_FIFO Only used by the Rx Thread, Rx_ProcessPackets
+//RX_STATUS_FIFO_PEEK Not used.
+//TX_STATUS_FIFO Used in Tx_CompleteTx in Tx_UpdateTxCounters.
+// protected by TxCounterLock
+//TX_STATUS_FIFO_PEEK Not used.
+//ID_REV Read only
+//INT_CFG Set in Lan_Initialize,
+// protected by IntEnableLock
+//INT_STS Sharable,
+//INT_EN Initialized at startup,
+// Used in Rx_ProcessPackets
+// otherwise protected by IntEnableLock
+//BYTE_TEST Read Only
+//FIFO_INT Initialized at startup,
+// During run time only accessed by
+// Tx_HandleInterrupt, and Tx_SendSkb and done in a safe manner
+//RX_CFG Used during initialization
+// During runtime only used by Rx Thread
+//TX_CFG Only used during initialization
+//HW_CFG Only used during initialization
+//RX_DP_CTRL Only used in Rx Thread, in Rx_FastForward
+//RX_FIFO_INF Read Only, Only used in Rx Thread, in Rx_PopRxStatus
+//TX_FIFO_INF Read Only, Only used in Tx Thread, in Tx_GetTxStatusCount, Tx_SendSkb, Tx_CompleteTx
+//PMT_CTRL Not Used
+//GPIO_CFG used during initialization, in Lan_Initialize
+// used for debugging
+// used during EEPROM access.
+// safe enough to not require a lock
+//GPT_CFG protected by GpTimerLock
+//GPT_CNT Not Used
+//WORD_SWAP Not Used
+//FREE_RUN Read only
+//RX_DROP Used in Rx Interrupt Handler,
+// and get_stats.
+// safe enough to not require a lock.
+//MAC_CSR_CMD Protected by MacPhyLock
+//MAC_CSR_DATA Protected by MacPhyLock
+// Because the two previous MAC_CSR_ registers are protected
+// All MAC, and PHY registers are protected as well.
+//AFC_CFG Used during initialization, in Lan_Initialize
+// During run time, used in timer call back, in Phy_UpdateLinkMode
+//E2P_CMD Used during initialization, in Lan_Initialize
+// Used in EEPROM functions
+//E2P_DATA Used in EEPROM functions
+
+#include "platform_alchemy.h"
+
+#define Lan_GetRegDW(dwOffset) \
+ Platform_GetRegDW(privateData->dwLanBase,dwOffset)
+
+#define Lan_SetRegDW(dwOffset,dwVal) \
+ Platform_SetRegDW(privateData->dwLanBase,dwOffset,dwVal)
+
+#define Lan_ClrBitsDW(dwOffset,dwBits) \
+ Platform_SetRegDW(privateData->dwLanBase, \
+ dwOffset,Platform_GetRegDW(privateData->dwLanBase, \
+ dwOffset)&(~dwBits))
+
+#define Lan_SetBitsDW(dwOffset,dwBits) \
+ Platform_SetRegDW(privateData->dwLanBase, \
+ dwOffset,Platform_GetRegDW(privateData->dwLanBase, \
+ dwOffset)|dwBits);
+
+#define LINK_OFF (0x00UL)
+#define LINK_SPEED_10HD (0x01UL)
+#define LINK_SPEED_10FD (0x02UL)
+#define LINK_SPEED_100HD (0x04UL)
+#define LINK_SPEED_100FD (0x08UL)
+#define LINK_SYMMETRIC_PAUSE (0x10UL)
+#define LINK_ASYMMETRIC_PAUSE (0x20UL)
+#define LINK_AUTO_NEGOTIATE (0x40UL)
+
+typedef unsigned long VL_KEY;
+typedef struct _VERIFIABLE_LOCK {
+ spinlock_t Lock;
+ VL_KEY KeyCode;
+} VERIFIABLE_LOCK, * PVERIFIABLE_LOCK;
+
+void Vl_InitLock(PVERIFIABLE_LOCK pVl);
+bool Vl_CheckLock(PVERIFIABLE_LOCK pVl,VL_KEY keyCode);
+VL_KEY Vl_WaitForLock(PVERIFIABLE_LOCK pVl,unsigned long *pdwIntFlags);
+void Vl_ReleaseLock(PVERIFIABLE_LOCK pVl,VL_KEY keyCode,unsigned long *pdwIntFlags);
+
+typedef struct _PRIVATE_DATA {
+ u32 dwLanBase;
+ u32 dwIdRev;
+ u32 dwFpgaRev;
+ struct net_device *dev;
+ u32 dwGeneration;//used to decide which workarounds apply
+ bool UseScatterGather;
+ bool UseTxCsum;
+ bool UseRxCsum;
+
+
+ spinlock_t IntEnableLock;
+ bool LanInitialized;
+ VERIFIABLE_LOCK MacPhyLock;
+ bool ExtPhy; //
+
+ u32 dwTxDmaCh;
+ bool TxDmaChReserved;
+ DMA_XFER TxDmaXfer;
+ u32 dwTxDmaThreshold;
+ u32 dwTxQueueDisableMask;
+ struct sk_buff *TxSkb;
+ spinlock_t TxSkbLock;
+ spinlock_t TxQueueLock;
+ spinlock_t TxCounterLock;
+ bool TxInitialized;
+
+
+
+ // BYTE bCoalesceBuf[1600];
+
+
+
+ // struct sk_buff *TxSkbPending;
+
+
+ u32 dwRxDmaCh;
+ struct sk_buff *RxSkb;
+ bool RxDmaChReserved;
+ u32 dwRxDmaThreshold;
+ bool RxCongested;
+ u32 dwRxOffCount;
+ bool RxOverrun;
+ u32 RxOverrunCount;
+ u32 RxStatusDWReadCount;
+ u32 RxDataDWReadCount;
+ u32 RxPacketReadCount;
+ u32 RxFastForwardCount;
+ u32 RxPioReadCount;
+ u32 RxDmaReadCount;
+ u32 RxCongestedCount;
+ u32 RxDumpCount;
+ u32 LastReasonForReleasingCPU;
+ u32 LastRxStatus1;
+ u32 LastRxStatus2;
+ u32 LastRxStatus3;
+ u32 LastIntStatus1;
+ u32 LastIntStatus2;
+ u32 LastIntStatus3;
+ u32 RxUnloadProgress;
+ u32 RxUnloadPacketProgress;
+ u32 RxMaxDataFifoSize;
+ // bool RxVLanPkt;
+
+ //NAPI
+ int RxWorkLimit;
+ int RxPacketsReceived;
+ // bool RxDone;
+
+
+ u32 RxFlowCurrentThroughput;
+ u32 RxFlowCurrentPacketCount;
+ u32 RxFlowCurrentWorkLoad;
+ bool MeasuringRxThroughput;
+ u32 RxFlowMeasuredMaxThroughput;
+ u32 RxFlowMeasuredMaxPacketCount;
+
+ //RX_FLOW_ACTIVATION specifies the percentage that RxFlowCurrentWorkLoad must exceed
+ // RxFlowMaxWorkLoad in order to activate flow control
+#define RX_FLOW_ACTIVATION (4UL)
+
+ //RX_FLOW_DEACTIVATION specifies the percentage that RxFlowCurrentWorkLoad must reduce
+ // from RxFlowMaxWorkLoad in order to deactivate flow control
+#define RX_FLOW_DEACTIVATION (25UL)
+ u32 RxFlowMaxWorkLoad;
+
+ FLOW_CONTROL_PARAMETERS RxFlowParameters;
+
+ u32 RxFlowBurstWorkLoad;
+ u32 RxFlowBurstMaxWorkLoad;
+ bool RxFlowControlActive;
+ bool RxFlowBurstActive;
+ u32 RxInterrupts;
+
+#define GPT_SCHEDULE_DEPTH (3)
+ void *GptFunction[GPT_SCHEDULE_DEPTH];
+ u32 GptCallTime[GPT_SCHEDULE_DEPTH];
+ u32 Gpt_scheduled_slot_index;
+ spinlock_t GpTimerLock;
+
+ bool Running;
+ struct net_device_stats stats;
+
+ u32 dwPhyAddress;
+ u32 dwPhyId;
+#ifdef USE_LED1_WORK_AROUND
+ u32 NotUsingExtPhy;
+#endif
+ BYTE bPhyModel;
+ BYTE bPhyRev;
+ u32 dwLinkSpeed;
+ u32 dwLinkSettings;
+ u32 dwRemoteFaultCount;
+ struct timer_list LinkPollingTimer;
+ bool StopLinkPolling;
+ WORD wLastADV;
+ WORD wLastADVatRestart;
+#ifdef USE_PHY_WORK_AROUND
+#define MIN_PACKET_SIZE (64)
+ u32 dwTxStartMargen;
+ BYTE LoopBackTxPacket[MIN_PACKET_SIZE];
+ u32 dwTxEndMargen;
+ u32 dwRxStartMargen;
+ BYTE LoopBackRxPacket[MIN_PACKET_SIZE];
+ u32 dwRxEndMargen;
+ u32 dwResetCount;
+#endif
+
+ bool SoftwareInterruptSignal;
+
+ PLATFORM_DATA PlatformData;
+
+#define SMSC_IF_NAME_SIZE (10)
+ char ifName[SMSC_IF_NAME_SIZE];
+
+ /* for Rx Multicast work around */
+ volatile u32 HashLo;
+ volatile u32 HashHi;
+ volatile bool MulticastUpdatePending;
+ volatile u32 set_bits_mask;
+ volatile u32 clear_bits_mask;
+
+} PRIVATE_DATA, *PPRIVATE_DATA;
+
+
+/*
+ ****************************************************************************
+ ****************************************************************************
+ * MAC Control and Status Register (Indirect Address)
+ * Offset (through the MAC_CSR CMD and DATA port)
+ ****************************************************************************
+ ****************************************************************************
+ *
+ */
+#define MAC_CR (0x01UL) // R/W
+
+/* MAC_CR - MAC Control Register */
+#define MAC_CR_RXALL_ (0x80000000UL)
+#define MAC_CR_HBDIS_ (0x10000000UL)
+#define MAC_CR_RCVOWN_ (0x00800000UL)
+#define MAC_CR_LOOPBK_ (0x00200000UL)
+#define MAC_CR_FDPX_ (0x00100000UL)
+#define MAC_CR_MCPAS_ (0x00080000UL)
+#define MAC_CR_PRMS_ (0x00040000UL)
+#define MAC_CR_INVFILT_ (0x00020000UL)
+#define MAC_CR_PASSBAD_ (0x00010000UL)
+#define MAC_CR_HFILT_ (0x00008000UL)
+#define MAC_CR_HPFILT_ (0x00002000UL)
+#define MAC_CR_LCOLL_ (0x00001000UL)
+#define MAC_CR_BCAST_ (0x00000800UL)
+#define MAC_CR_DISRTY_ (0x00000400UL)
+#define MAC_CR_PADSTR_ (0x00000100UL)
+#define MAC_CR_BOLMT_MASK_ (0x000000C0UL)
+#define MAC_CR_DFCHK_ (0x00000020UL)
+#define MAC_CR_TXEN_ (0x00000008UL)
+#define MAC_CR_RXEN_ (0x00000004UL)
+
+#define ADDRH (0x02UL) // R/W mask 0x0000FFFFUL
+#define ADDRL (0x03UL) // R/W mask 0xFFFFFFFFUL
+#define HASHH (0x04UL) // R/W
+#define HASHL (0x05UL) // R/W
+
+#define MII_ACC (0x06UL) // R/W
+#define MII_ACC_PHY_ADDR_ (0x0000F800UL)
+#define MII_ACC_MIIRINDA_ (0x000007C0UL)
+#define MII_ACC_MII_WRITE_ (0x00000002UL)
+#define MII_ACC_MII_BUSY_ (0x00000001UL)
+
+#define MII_DATA (0x07UL) // R/W mask 0x0000FFFFUL
+
+#define FLOW (0x08UL) // R/W
+#define FLOW_FCPT_ (0xFFFF0000UL)
+#define FLOW_FCPASS_ (0x00000004UL)
+#define FLOW_FCEN_ (0x00000002UL)
+#define FLOW_FCBSY_ (0x00000001UL)
+
+#define VLAN1 (0x09UL) // R/W mask 0x0000FFFFUL
+#define VLAN2 (0x0AUL) // R/W mask 0x0000FFFFUL
+
+#define WUFF (0x0BUL) // WO
+
+#define WUCSR (0x0CUL) // R/W
+#define WUCSR_GUE_ (0x00000200UL)
+#define WUCSR_WUFR_ (0x00000040UL)
+#define WUCSR_MPR_ (0x00000020UL)
+#define WUCSR_WAKE_EN_ (0x00000004UL)
+#define WUCSR_MPEN_ (0x00000002UL)
+
+#define COE_CR 0xDUL
+#define TX_COE_EN 0x00010000UL
+#define RX_COE_MODE 0x00000002UL
+#define RX_COE_EN 0x00000001UL
+
+
+bool Mac_Initialize(PPRIVATE_DATA privateData);
+static bool MacNotBusy(PPRIVATE_DATA privateData,VL_KEY keyCode);
+u32 Mac_GetRegDW(PPRIVATE_DATA privateData,u32 dwRegOffset,VL_KEY keyCode);
+void Mac_SetRegDW(PPRIVATE_DATA privateData,u32 dwRegOffset,u32 dwVal,VL_KEY keyCode);
+
+/*
+ ****************************************************************************
+ * Chip Specific MII Defines
+ ****************************************************************************
+ *
+ * Phy register offsets and bit definitions
+ *
+ */
+#define LAN9118_PHY_ID (0x00C0001C)
+
+#define PHY_BCR ((u32)0U)
+#define PHY_BCR_RESET_ ((WORD)0x8000U)
+#define PHY_BCR_LOOPBACK_ ((WORD)0x4000U)
+#define PHY_BCR_SPEED_SELECT_ ((WORD)0x2000U)
+#define PHY_BCR_AUTO_NEG_ENABLE_ ((WORD)0x1000U)
+#define PHY_BCR_RESTART_AUTO_NEG_ ((WORD)0x0200U)
+#define PHY_BCR_DUPLEX_MODE_ ((WORD)0x0100U)
+
+#define PHY_BSR ((u32)1U)
+#define PHY_BSR_LINK_STATUS_ ((WORD)0x0004U)
+#define PHY_BSR_REMOTE_FAULT_ ((WORD)0x0010U)
+#define PHY_BSR_AUTO_NEG_COMP_ ((WORD)0x0020U)
+
+#define PHY_ID_1 ((u32)2U)
+#define PHY_ID_2 ((u32)3U)
+
+#define PHY_ANEG_ADV ((u32)4U)
+#define PHY_ANEG_ADV_PAUSE_ ((WORD)0x0C00)
+#define PHY_ANEG_ADV_ASYMP_ ((WORD)0x0800)
+#define PHY_ANEG_ADV_SYMP_ ((WORD)0x0400)
+#define PHY_ANEG_ADV_10H_ ((WORD)0x20)
+#define PHY_ANEG_ADV_10F_ ((WORD)0x40)
+#define PHY_ANEG_ADV_100H_ ((WORD)0x80)
+#define PHY_ANEG_ADV_100F_ ((WORD)0x100)
+#define PHY_ANEG_ADV_SPEED_ ((WORD)0x1E0)
+
+#define PHY_ANEG_LPA ((u32)5U)
+#define PHY_ANEG_LPA_ASYMP_ ((WORD)0x0800)
+#define PHY_ANEG_LPA_SYMP_ ((WORD)0x0400)
+#define PHY_ANEG_LPA_100FDX_ ((WORD)0x0100)
+#define PHY_ANEG_LPA_100HDX_ ((WORD)0x0080)
+#define PHY_ANEG_LPA_10FDX_ ((WORD)0x0040)
+#define PHY_ANEG_LPA_10HDX_ ((WORD)0x0020)
+
+#define PHY_MODE_CTRL_STS ((u32)17) // Mode Control/Status Register
+#define MODE_CTRL_STS_FASTRIP_ ((WORD)0x4000U)
+#define MODE_CTRL_STS_EDPWRDOWN_ ((WORD)0x2000U)
+#define MODE_CTRL_STS_LOWSQEN_ ((WORD)0x0800U)
+#define MODE_CTRL_STS_MDPREBP_ ((WORD)0x0400U)
+#define MODE_CTRL_STS_FARLOOPBACK_ ((WORD)0x0200U)
+#define MODE_CTRL_STS_FASTEST_ ((WORD)0x0100U)
+#define MODE_CTRL_STS_REFCLKEN_ ((WORD)0x0010U)
+#define MODE_CTRL_STS_PHYADBP_ ((WORD)0x0008U)
+#define MODE_CTRL_STS_FORCE_G_LINK_ ((WORD)0x0004U)
+#define MODE_CTRL_STS_ENERGYON_ ((WORD)0x0002U)
+
+#define SPECIAL_CTRL_STS ((u32)27)
+#define SPECIAL_CTRL_STS_OVRRD_AMDIX_ ((WORD)0x8000U)
+#define SPECIAL_CTRL_STS_AMDIX_ENABLE_ ((WORD)0x4000U)
+#define SPECIAL_CTRL_STS_AMDIX_STATE_ ((WORD)0x2000U)
+
+#define PHY_INT_SRC ((u32)29)
+#define PHY_INT_SRC_ENERGY_ON_ ((WORD)0x0080U)
+#define PHY_INT_SRC_ANEG_COMP_ ((WORD)0x0040U)
+#define PHY_INT_SRC_REMOTE_FAULT_ ((WORD)0x0020U)
+#define PHY_INT_SRC_LINK_DOWN_ ((WORD)0x0010U)
+
+#define PHY_INT_MASK ((u32)30)
+#define PHY_INT_MASK_ENERGY_ON_ ((WORD)0x0080U)
+#define PHY_INT_MASK_ANEG_COMP_ ((WORD)0x0040U)
+#define PHY_INT_MASK_REMOTE_FAULT_ ((WORD)0x0020U)
+#define PHY_INT_MASK_LINK_DOWN_ ((WORD)0x0010U)
+
+#define PHY_SPECIAL ((u32)31)
+#define PHY_SPECIAL_SPD_ ((WORD)0x001CU)
+#define PHY_SPECIAL_SPD_10HALF_ ((WORD)0x0004U)
+#define PHY_SPECIAL_SPD_10FULL_ ((WORD)0x0014U)
+#define PHY_SPECIAL_SPD_100HALF_ ((WORD)0x0008U)
+#define PHY_SPECIAL_SPD_100FULL_ ((WORD)0x0018U)
+
+#define AMDIX_DISABLE_STRAIGHT ((WORD)0x0U)
+#define AMDIX_DISABLE_CROSSOVER ((WORD)0x01U)
+#define AMDIX_ENABLE ((WORD)0x02U)
+
+bool Phy_Initialize(
+ PPRIVATE_DATA privateData,
+ u32 dwPhyAddress,
+ u32 dwLinkMode);
+void Phy_SetLink(PPRIVATE_DATA privateData,
+ u32 dwLinkRequest);
+WORD Phy_GetRegW(
+ PPRIVATE_DATA privateData,
+ u32 dwRegIndex,
+ VL_KEY keyCode);
+void Phy_SetRegW(
+ PPRIVATE_DATA privateData,
+ u32 dwRegIndex,
+ WORD wVal,
+ VL_KEY keyCode);
+void Phy_UpdateLinkMode(
+ PPRIVATE_DATA privateData);
+void Phy_GetLinkMode(
+ PPRIVATE_DATA privateData,
+ VL_KEY keyCode);
+void Phy_CheckLink(unsigned long ptr);
+void Phy_SetAutoMdixSts(PPRIVATE_DATA privateData,
+ WORD wAutoMdixSts);
+void Phy_GetAutoMdixSts(PPRIVATE_DATA privateData);
+
+
+
+
+
+TIME_SPAN Gpt_FreeRunCompare(u32 time1,u32 time2);
+void Gpt_ScheduleInterrupt(PPRIVATE_DATA privateData,TIME_SPAN timeSpan);
+void Gpt_CancelInterrupt(PPRIVATE_DATA privateData);
+void Gpt_CancelCallBack(
+ PPRIVATE_DATA privateData,
+ void (*callBackFunction)(PPRIVATE_DATA privateData));
+void Gpt_ScheduleCallBack(
+ PPRIVATE_DATA privateData,
+ void (*callBackFunction)(PPRIVATE_DATA privateData),
+ u32 callBackTime);//100uS units relative to now
+bool Gpt_HandleInterrupt(
+ PPRIVATE_DATA privateData,u32 dwIntSts);
+void GptCB_RxCompleteMulticast(PPRIVATE_DATA privateData);
+void GptCB_RestartBurst(PPRIVATE_DATA privateData);
+void GptCB_MeasureRxThroughput(PPRIVATE_DATA privateData);
+
+void Tx_Initialize(
+ PPRIVATE_DATA privateData,
+ u32 dwTxDmaCh,
+ u32 dwTxDmaThreshold);
+void Tx_SendSkb(
+ PPRIVATE_DATA privateData,
+ struct sk_buff *skb);
+bool Tx_HandleInterrupt(
+ PPRIVATE_DATA privateData,u32 dwIntSts);
+
+void Tx_StopQueue(
+ PPRIVATE_DATA privateData,u32 dwSource);
+void Tx_WakeQueue(
+ PPRIVATE_DATA privateData,u32 dwSource);
+
+static u32 Tx_GetTxStatusCount(
+ PPRIVATE_DATA privateData);
+static u32 Tx_CompleteTx(
+ PPRIVATE_DATA privateData);
+void Tx_UpdateTxCounters(
+ PPRIVATE_DATA privateData);
+
+void Tx_CompleteDma(
+ PPRIVATE_DATA privateData);
+
+void CalculateTxChecksumOffset(
+ struct sk_buff *skb,
+ int *csum_start_offset);
+
+void rkdump(unsigned char *p, unsigned short len);
+
+void Rx_Initialize(
+ PPRIVATE_DATA privateData,
+ u32 dwRxDmaCh,
+ u32 dwDmaThreshold);
+
+void Rx_CompleteMulticastUpdate (PPRIVATE_DATA privateData);
+static void Rx_HandleOverrun(PPRIVATE_DATA privateData);
+static void Rx_HandOffSkb(
+ PPRIVATE_DATA privateData,
+ struct sk_buff *skb);
+static u32 Rx_PopRxStatus(
+ PPRIVATE_DATA privateData);
+void Rx_CountErrors(PPRIVATE_DATA privateData,u32 dwRxStatus);
+void Rx_FastForward(PPRIVATE_DATA privateData,u32 dwDwordCount);
+void Rx_ProcessPackets(PPRIVATE_DATA privateData);
+void Rx_BeginMulticastUpdate (PPRIVATE_DATA privateData);
+
+unsigned long Rx_TaskletParameter=0;
+
+void Rx_ProcessPacketsTasklet(unsigned long data);
+DECLARE_TASKLET(Rx_Tasklet,Rx_ProcessPacketsTasklet,0);
+
+#ifdef LINUX_2_6_OR_NEWER
+int Smsc9118_rx_poll(struct net_device *dev,int * budget);
+#endif
+
+bool RxStop_HandleInterrupt(
+ PPRIVATE_DATA privateData,
+ u32 dwIntSts);
+bool Rx_HandleInterrupt(
+ PPRIVATE_DATA privateData,
+ u32 dwIntSts);
+static u32 Rx_Hash(BYTE addr[6]);
+
+void Rx_SetMulticastList(
+ struct net_device *dev);
+void Rx_ReceiverOff(
+ PPRIVATE_DATA privateData);
+void Rx_ReceiverOn(
+ PPRIVATE_DATA privateData, VL_KEY callerKeyCode);
+
+
+void Eeprom_EnableAccess(PPRIVATE_DATA privateData);
+void Eeprom_DisableAccess(PPRIVATE_DATA privateData);
+
+bool Eeprom_IsMacAddressLoaded(PPRIVATE_DATA privateData);
+bool Eeprom_IsBusy(PPRIVATE_DATA privateData);
+bool Eeprom_Timeout(PPRIVATE_DATA privateData);
+
+bool Eeprom_ReadLocation(
+ PPRIVATE_DATA privateData,BYTE address, BYTE * data);
+bool Eeprom_EnableEraseAndWrite(
+ PPRIVATE_DATA privateData);
+bool Eeprom_DisableEraseAndWrite(
+ PPRIVATE_DATA privateData);
+bool Eeprom_WriteLocation(
+ PPRIVATE_DATA privateData,BYTE address,BYTE data);
+bool Eeprom_EraseAll(
+ PPRIVATE_DATA privateData);
+bool Eeprom_Reload(
+ PPRIVATE_DATA privateData);
+
+bool Eeprom_SaveMacAddress(
+ PPRIVATE_DATA privateData,
+ u32 dwHi16,u32 dwLo32);
+
+
+#define OLD_REGISTERS(privData) (((privData->dwIdRev)==0x01180000UL)&& \
+ ((privData->dwFpgaRev)>=0x01)&& \
+ ((privData->dwFpgaRev)<=0x25))
+
+extern volatile u32 g_GpioSetting;
+#define GP_0 (0x01UL)
+#define GP_1 (0x02UL)
+#define GP_2 (0x04UL)
+#define GP_3 (0x08UL)
+#define GP_4 (0x10UL)
+#define GP_OFF (0x00UL)
+#define GP_ISR GP_OFF
+#define GP_RX GP_OFF
+#define GP_TX GP_OFF
+#define GP_BEGIN_MULTICAST_UPDATE GP_OFF
+#define GP_COMPLETE_MULTICAST_UPDATE GP_OFF
+
+#define SET_GPIO(gpioBit) \
+ if(debug_mode&0x04UL) { \
+ g_GpioSetting|=gpioBit; \
+ Lan_SetRegDW(GPIO_CFG,g_GpioSetting); \
+ }
+
+#define CLEAR_GPIO(gpioBit) \
+ if(debug_mode&0x04UL) { \
+ g_GpioSetting&=(~gpioBit); \
+ Lan_SetRegDW(GPIO_CFG,g_GpioSetting); \
+ }
+
+#define PULSE_GPIO(gpioBit,count) \
+ if(debug_mode&0x04UL) { \
+ u32 pulseNum=0; \
+ /*make first pulse longer */ \
+ SET_GPIO(gpioBit); \
+ while(pulseNum<count) { \
+ SET_GPIO(gpioBit); \
+ CLEAR_GPIO(gpioBit); \
+ pulseNum++; \
+ } \
+ }
+#ifdef USE_LED1_WORK_AROUND
+volatile u32 g_GpioSettingOriginal;
+#endif
+
+bool Lan_Initialize(
+ PPRIVATE_DATA privateData,u32 dwIntCfg,
+ u32 dwTxFifSz,u32 dwAfcCfg);
+void Lan_EnableInterrupt(PPRIVATE_DATA privateData,u32 dwIntEnMask);
+void Lan_DisableInterrupt(PPRIVATE_DATA privateData,u32 dwIntEnMask);
+void Lan_EnableIRQ(PPRIVATE_DATA privateData);
+void Lan_DisableIRQ(PPRIVATE_DATA privateData);
+void Lan_SetIntDeas(PPRIVATE_DATA privateData,u32 dwIntDeas);
+void Lan_SetTDFL(PPRIVATE_DATA privateData,BYTE level);
+void Lan_SetTSFL(PPRIVATE_DATA privateData,BYTE level);
+void Lan_SetRDFL(PPRIVATE_DATA privateData,BYTE level);
+void Lan_SetRSFL(PPRIVATE_DATA privateData,BYTE level);
+
+void Lan_SignalSoftwareInterrupt(PPRIVATE_DATA privateData);
+bool Lan_HandleSoftwareInterrupt(PPRIVATE_DATA privateData,u32 dwIntSts);
+
+void Lan_ShowRegs(PPRIVATE_DATA privateData);
+
+#include "ioctl_118.h"
+
+static u32 lan_base=0x0UL;
+module_param(lan_base, int, S_IRUGO);
+MODULE_PARM_DESC(lan_base,"Base Address of LAN9118, (default: choosen by platform code)");
+
+static u32 bus_width=0UL;
+module_param(bus_width, int, S_IRUGO);
+MODULE_PARM_DESC(bus_width,"Force bus width of 16 or 32 bits, default: autodetect");
+
+static u32 link_mode=0x7FUL;
+module_param(link_mode, int, S_IRUGO);
+MODULE_PARM_DESC(link_mode,"Set Link speed and Duplex, 1=10HD,2=10FD,4=100HD,8=100FD,default=0xF");
+
+static u32 AutoMdix=0x3U;
+module_param(AutoMdix, int, S_IRUGO);
+MODULE_PARM_DESC(AutoMdix,"Set Auto-MDIX state, 0=StraightCable,1=CrossOver,2=Enable AMDIX,3=controlled by Strap");
+
+static u32 irq=PLATFORM_IRQ;
+module_param(irq, int, S_IRUGO);
+MODULE_PARM_DESC(irq,"Force use of specific IRQ, (default: choosen by platform code)");
+
+static u32 int_deas=0xFFFFFFFFUL;
+module_param(int_deas, int, S_IRUGO);
+MODULE_PARM_DESC(int_deas,"Interrupt Deassertion Interval in 10uS units");
+
+static u32 irq_pol=PLATFORM_IRQ_POL;
+module_param(irq_pol, int, S_IRUGO);
+MODULE_PARM_DESC(irq_pol,"IRQ Polarity bit, see definition of INT_CFG register");
+
+static u32 irq_type=PLATFORM_IRQ_TYPE;
+module_param(irq_type, int, S_IRUGO);
+MODULE_PARM_DESC(irq_type,"IRQ Buffer Type bit, see definition of INT_CFG register");
+
+static u32 rx_dma=PLATFORM_RX_DMA;
+module_param(rx_dma, int, S_IRUGO);
+MODULE_PARM_DESC(rx_dma,"Receiver DMA Channel, 255=find available channel, 256=use PIO");
+
+static u32 tx_dma=PLATFORM_TX_DMA;
+module_param(tx_dma, int, S_IRUGO);
+MODULE_PARM_DESC(tx_dma,"Transmitter DMA Channel, 255=find available channel, 256=use PIO");
+
+static u32 dma_threshold=PLATFORM_DMA_THRESHOLD;
+module_param(dma_threshold, int, S_IRUGO);
+MODULE_PARM_DESC(dma_threshold,"Specifies the minimum packet size for DMA to be used.");
+
+static u32 mac_addr_hi16=0xFFFFFFFF;
+module_param(mac_addr_hi16, int, S_IRUGO);
+MODULE_PARM_DESC(mac_addr_hi16,"Specifies the high 16 bits of the mac address");
+
+static u32 mac_addr_lo32=0xFFFFFFFF;
+module_param(mac_addr_lo32, int, S_IRUGO);
+MODULE_PARM_DESC(mac_addr_lo32,"Specifies the low 32 bits of the mac address");
+
+module_param(debug_mode, int, S_IRUGO);
+MODULE_PARM_DESC(debug_mode,"bit 0 enables trace points, bit 1 enables warning points, bit 2 enables gpios");
+
+static u32 tx_fif_sz=0x00050000UL;
+module_param(tx_fif_sz, int, S_IRUGO);
+MODULE_PARM_DESC(tx_fif_sz,"Specifies TX_FIF_SZ of the HW_CFG register");
+
+static u32 afc_cfg=0xFFFFFFFFUL;
+module_param(afc_cfg, int, S_IRUGO);
+MODULE_PARM_DESC(afc_cfg,"Specifies the setting for the AFC_CFG register");
+
+//static u32 tasklets=1UL;
+//module_param(tasklets, int, S_IRUGO);
+//MODULE_PARM_DESC(tasklets,"non-zero== use tasklets for receiving packets, zero==receive packets in ISR");
+
+#define PROCESSING_MODE_IDLE (0UL)
+#define PROCESSING_MODE_TASKLET (1UL)
+#define PROCESSING_MODE_NAPI (2UL)
+
+
+static u32 rx_mode=PROCESSING_MODE_TASKLET;
+module_param(rx_mode, int, S_IRUGO);
+MODULE_PARM_DESC(rx_mode,"0==use ISR, 1==use Rx Tasklet, 2==use NAPI");
+
+#ifdef LINUX_2_6_OR_NEWER
+static u32 napi_weight=4UL;
+module_param(napi_weight, int, S_IRUGO);
+MODULE_PARM_DESC(napi_weight,"The weight value to use if NAPI is used");
+#endif
+
+
+
+static u32 phy_addr=0xFFFFFFFFUL;
+module_param(phy_addr, int, S_IRUGO);
+MODULE_PARM_DESC(phy_addr,"phy_addr, 0xFFFFFFFF=use interal phy, 0-31=use external phy with specified address, else autodetect external phy addr");
+
+static u32 max_throughput=0xFFFFFFFFUL;
+module_param(max_throughput, int, S_IRUGO);
+MODULE_PARM_DESC(max_throughput,"See readme.txt");
+
+static u32 max_packet_count=0xFFFFFFFFUL;
+module_param(max_packet_count, int, S_IRUGO);
+MODULE_PARM_DESC(max_packet_count,"See Readme.txt");
+
+static u32 packet_cost=0xFFFFFFFFUL;
+module_param(packet_cost, int, S_IRUGO);
+MODULE_PARM_DESC(packet_cost,"See Readme.txt");
+
+static u32 burst_period=0xFFFFFFFFUL;
+module_param(burst_period, int, S_IRUGO);
+MODULE_PARM_DESC(burst_period,"See Readme.txt");
+
+static u32 max_work_load=0xFFFFFFFFUL;
+module_param(max_work_load, int, S_IRUGO);
+MODULE_PARM_DESC(max_work_load,"See Readme.txt");
+
+static int Scatter_gather=false;
+module_param(Scatter_gather, bool, S_IRUGO);
+MODULE_PARM_DESC(Scatter_gather,"Enable Scatter Gather");
+
+static int tx_Csum=false;
+module_param(tx_Csum, bool, S_IRUGO);
+MODULE_PARM_DESC(tx_Csum,"Enable Tx Hardware Checksum Offload");
+
+static int rx_Csum=false;
+module_param(rx_Csum, bool, S_IRUGO);
+MODULE_PARM_DESC(rx_Csum,"Enable Rx Hardware Checksum Offload");
+
+/* The three parameters below are used for the new unkonw chip before we formally add them in the driver
+ So that in the future we can just immediately support chips with new IDs by passing the new id and timing at load time.
+ But we still need to add the supported new Chip id and the flow control parameters formally later.
+ */
+static u32 id_reg=0x0UL;
+module_param(id_reg, int, S_IRUGO);
+MODULE_PARM_DESC(id_reg,"Chip Id");
+
+static u32 bus_timing=0UL;
+module_param(bus_timing, int, S_IRUGO);
+MODULE_PARM_DESC(bus_timing,"bus timing");
+
+
+static int Csum_Support=false;
+module_param(Csum_Support, int, S_IRUGO);
+MODULE_PARM_DESC(Csum_Support,"The chip has the ability of Checksum offload");
+
+
+MODULE_LICENSE("GPL");
+
+int Smsc9118_init_module(void);
+void Smsc9118_cleanup_module(void);
+void Smsc9118_init(struct net_device *dev);
+int Smsc9118_open(struct net_device *dev);
+int Smsc9118_stop(struct net_device *dev);
+int Smsc9118_hard_start_xmit(struct sk_buff *skb, struct net_device *dev);
+struct net_device_stats * Smsc9118_get_stats(struct net_device *dev);
+void Smsc9118_set_multicast_list(struct net_device *dev);
+int Smsc9118_private_ioctl(PPRIVATE_DATA privateData,void *useraddr);
+int Smsc9118_ethtool_ioctl(PPRIVATE_DATA privateData, void * userAddr);
+int Smsc9118_do_ioctl(struct net_device *dev, struct ifreq *ifr,int cmd);
+irqreturn_t Smsc9118_ISR(int irq,void *dev_id);
+
+#ifdef USING_LINT
+//struct net_device SMSC9118;
+#else //not USING_LINT
+/* KH: Don't initialize here. */
+/* struct net_device SMSC9118 = {init: Smsc9118_init,}; */
+struct net_device *SMSC9118;
+#endif //not USING_LINT
+
+int Smsc9118_init_module(void)
+{
+ int result=0;
+ int device_present=0;
+
+ SMSC_TRACE("--> init_module()");
+ SMSC_TRACE("Compiled: %s, %s",__DATE__,__TIME__);
+ SMSC_TRACE("Platform: %s",PLATFORM_NAME);
+ SMSC_TRACE("Driver Parameters");
+
+ if(lan_base==0UL) {
+ SMSC_TRACE(" lan_base = 0x%08X, driver will decide",lan_base);
+ } else {
+ SMSC_TRACE(" lan_base = 0x%08X",lan_base);
+ }
+ if((bus_width==16UL)||(bus_width==32UL)) {
+ SMSC_TRACE(" bus_width = %d",bus_width);
+ } else {
+ SMSC_TRACE(" bus_width = %d, driver will autodetect",bus_width);
+ }
+ if(link_mode>0x7FUL) {
+ SMSC_WARNING(" link_mode = %d, Unknown",link_mode);
+ link_mode=0x7FUL;
+ SMSC_WARNING(" resetting link_mode to %d, 100FD,100HD,10FD,10HD,ASYMP,SYMP,ANEG",link_mode);
+ } else if(link_mode==0UL) {
+ SMSC_TRACE(" link_mode = %d, LINK_OFF",link_mode);
+ } else {
+ SMSC_TRACE(" link_mode = 0x%X, %s,%s,%s,%s,%s,%s,%s",
+ link_mode,
+ (link_mode&LINK_SPEED_10HD)?"10HD":"",
+ (link_mode&LINK_SPEED_10FD)?"10FD":"",
+ (link_mode&LINK_SPEED_100HD)?"100HD":"",
+ (link_mode&LINK_SPEED_100FD)?"100FD":"",
+ (link_mode&LINK_ASYMMETRIC_PAUSE)?"ASYMP":"",
+ (link_mode&LINK_SYMMETRIC_PAUSE)?"SYMP":"",
+ (link_mode&LINK_AUTO_NEGOTIATE)?"ANEG":"");
+ }
+ SMSC_TRACE( " irq = %d",irq);
+ if(int_deas!=0xFFFFFFFFUL) {
+ if(int_deas>0xFFUL) {
+ SMSC_WARNING(" int_deas = %d, too high",int_deas);
+ int_deas=0xFFFFFFFFUL;
+ SMSC_WARNING(" resetting int_deas to %d",int_deas);
+ }
+ }
+ if(int_deas==0xFFFFFFFFUL) {
+ SMSC_TRACE( " int_deas = 0x%08X, use platform default",int_deas);
+ } else {
+ SMSC_TRACE( " int_deas = %d, %duS",int_deas,(unsigned int)(10UL*int_deas));
+ }
+ if(irq_pol) {
+ SMSC_TRACE(" irq_pol = %d, IRQ output is active high",irq_pol);
+ } else {
+ SMSC_TRACE(" irq_pol = %d, IRQ output is active low",irq_pol);
+ }
+ if(irq_type) {
+ SMSC_TRACE(" irq_type = %d, IRQ output is Push-Pull driver",irq_type);
+ } else {
+ SMSC_TRACE(" irq_type = %d, IRQ output is Open-Drain buffer",irq_type);
+ }
+ if(rx_dma<TRANSFER_REQUEST_DMA) {
+ if(Platform_IsValidDmaChannel(rx_dma)) {
+ SMSC_TRACE(
+ " rx_dma = %d, DMA Channel %d",rx_dma,rx_dma);
+ } else {
+ SMSC_WARNING(" rx_dma = %d, Invalid Dma Channel",rx_dma);
+ rx_dma=TRANSFER_PIO;
+ SMSC_WARNING(" resetting rx_dma to %d, RX will use PIO",rx_dma);
+ }
+ } else if(rx_dma==TRANSFER_REQUEST_DMA) {
+ SMSC_TRACE(" rx_dma = %d, RX will try to find available channel",rx_dma);
+ } else {
+ SMSC_TRACE(" rx_dma = %d, RX will use PIO",rx_dma);
+ }
+ if(tx_dma<TRANSFER_REQUEST_DMA) {
+ if(Platform_IsValidDmaChannel(tx_dma)) {
+ if(tx_dma!=rx_dma) {
+ SMSC_TRACE(
+ " tx_dma = %d, DMA Channel %d",tx_dma,tx_dma);
+ } else {
+ SMSC_WARNING(" tx_dma == rx_dma");
+ tx_dma=TRANSFER_PIO;
+ SMSC_WARNING(" resetting tx_dma to %d, TX will use PIO",tx_dma);
+ }
+ } else {
+ SMSC_WARNING(" tx_dma = %d, Invalid Dma Channel",tx_dma);
+ tx_dma=TRANSFER_PIO;
+ SMSC_WARNING(" resetting tx_dma to %d, TX will use PIO",tx_dma);
+ }
+ } else if(tx_dma==TRANSFER_REQUEST_DMA) {
+ SMSC_TRACE(" tx_dma = %d, TX will try to find available channel",tx_dma);
+ } else {
+ SMSC_TRACE(" tx_dma = %d, TX will use PIO",tx_dma);
+ }
+ SMSC_TRACE( " dma_threshold = %d",dma_threshold);
+
+ if(mac_addr_hi16==0xFFFFFFFFUL) {
+ SMSC_TRACE(" mac_addr_hi16 = 0x%08X, will attempt to read from LAN9118",mac_addr_hi16);
+ SMSC_TRACE(" mac_addr_lo32 = 0x%08X, will attempt to read from LAN9118",mac_addr_lo32);
+ } else {
+ if(mac_addr_hi16&0xFFFF0000UL) {
+ //The high word is reserved
+ SMSC_WARNING(" mac_addr_hi16 = 0x%08X, reserved bits are high.",mac_addr_hi16);
+ mac_addr_hi16&=0x0000FFFFUL;
+ SMSC_WARNING(" reseting to mac_addr_hi16 = 0x%08X",mac_addr_hi16);
+ }
+ if(mac_addr_lo32&0x00000001UL) {
+ //bit 0 is the I/G bit
+ SMSC_WARNING(" mac_addr_lo32 = 0x%08X, I/G bit is set.",mac_addr_lo32);
+ mac_addr_lo32&=0xFFFFFFFEUL;
+ SMSC_WARNING(" reseting to mac_addr_lo32 = 0x%08X",mac_addr_lo32);
+ }
+ SMSC_TRACE(" mac_addr_hi16 = 0x%08X",mac_addr_hi16);
+ SMSC_TRACE(" mac_addr_lo32 = 0x%08X",mac_addr_lo32);
+ }
+ SMSC_TRACE( " debug_mode = 0x%08X",debug_mode);
+ if(tx_fif_sz&(~HW_CFG_TX_FIF_SZ_)) {
+ SMSC_WARNING("tx_fif_sz = 0x%08X is invalid",tx_fif_sz);
+ tx_fif_sz&=HW_CFG_TX_FIF_SZ_;
+ SMSC_WARNING(" resetting tx_fif_sz to 0x%08X",tx_fif_sz);
+ }
+ if(tx_fif_sz>0x000E0000UL) {
+ SMSC_WARNING("tx_fif_sz = 0x%08X is too high",tx_fif_sz);
+ tx_fif_sz=0x000E0000UL;
+ SMSC_WARNING(" resetting tx_fif_sz to 0x%08X",tx_fif_sz);
+ }
+ if(tx_fif_sz<0x00020000UL) {
+ SMSC_WARNING("tx_fif_sz = 0x%08X is too low",tx_fif_sz);
+ tx_fif_sz=0x00020000UL;
+ SMSC_WARNING(" resetting tx_fif_sz to 0x%08X",tx_fif_sz);
+ }
+ SMSC_TRACE( " tx_fif_sz = 0x%08X",tx_fif_sz);
+ if(afc_cfg==0xFFFFFFFFUL) {
+ SMSC_TRACE(" afc_cfg = 0x%08X, driver will decide",afc_cfg);
+ } else {
+ if(afc_cfg&0xFF000000UL) {
+ SMSC_WARNING("afc_cfg = 0x%08X is invalid",afc_cfg);
+ afc_cfg&=0xFFFFFFFFUL;
+ SMSC_WARNING(" resetting to afc_cfg = 0x%08X, driver will decide",afc_cfg);
+ } else {
+ SMSC_TRACE(
+ " afc_cfg = 0x%08X",afc_cfg);
+ }
+ }
+ if(rx_mode==PROCESSING_MODE_TASKLET) {
+ SMSC_TRACE(" rx_mode = 0x%08X, Tasklets enabled",rx_mode);
+ } else if(rx_mode==PROCESSING_MODE_NAPI) {
+#ifndef LINUX_2_6_OR_NEWER
+ SMSC_WARNING(" rx_mode = 0x%08X requires Linux 2.6 or newer", rx_mode);
+ rx_mode=PROCESSING_MODE_TASKLET;
+ SMSC_WARNING(" resetting to rx_mode = 0x%08X, Tasklets enabled", rx_mode);
+#else
+ SMSC_TRACE(" rx_mode = 0x%08X, NAPI enabled",rx_mode);
+#endif
+ } else {
+ SMSC_TRACE(" rx_mode = 0, use ISR");
+ }
+
+ if(phy_addr==0xFFFFFFFFUL) {
+ SMSC_TRACE(" phy_addr = 0xFFFFFFFF, Use internal phy");
+ } else if(phy_addr<=31UL) {
+ SMSC_TRACE(" phy_addr = 0x%08X, use this address for external phy",phy_addr);
+ } else {
+ SMSC_TRACE(" phy_addr = 0x%08X, auto detect external phy",phy_addr);
+ }
+ if(max_throughput) {
+ SMSC_TRACE(" max_throughput = 0x%08X, Use platform default",max_throughput);
+ } else {
+ SMSC_TRACE(" max_throughput = 0x%08X",max_throughput);
+ }
+ if(max_packet_count) {
+ SMSC_TRACE(" max_packet_count = 0x%08X, Use platform default",max_packet_count);
+ } else {
+ SMSC_TRACE(" max_packet_count = 0x%08X",max_packet_count);
+ }
+ if(packet_cost) {
+ SMSC_TRACE(" packet_cost = 0x%08X, Use platform default",packet_cost);
+ } else {
+ SMSC_TRACE(" packet_cost = 0x%08X",packet_cost);
+ }
+ if(burst_period) {
+ SMSC_TRACE(" burst_period = 0x%08X, Use platform default",burst_period);
+ } else {
+ SMSC_TRACE(" burst_period = 0x%08X",burst_period);
+ }
+ if(max_work_load) {
+ SMSC_TRACE(" max_work_load = 0x%08X, Use platform default",max_work_load);
+ } else {
+ SMSC_TRACE(" max_work_load = 0x%08X",max_work_load);
+ }
+
+ SMSC9118 = alloc_netdev_mq(0, "eth%d", Smsc9118_init, 1);
+ SMSC_TRACE(" alloc_netdev complete. SMSC9118 = 0x%08X\n", (u32)SMSC9118);
+ result=register_netdev(SMSC9118);
+ if(result) {
+ SMSC_WARNING("error %i registering device",result);
+ } else {
+ device_present=1;
+ SMSC_TRACE(" Interface Name = \"%s\"",SMSC9118->name);
+ }
+ result=result;//make lint happy
+ SMSC_TRACE("<-- init_module()");
+ return device_present ? 0 : -ENODEV;
+}
+
+void Smsc9118_cleanup_module(void)
+{
+ SMSC_TRACE("--> cleanup_module()");
+ if(SMSC9118->ml_priv!=NULL) {
+ PPRIVATE_DATA privateData=(PPRIVATE_DATA)SMSC9118->ml_priv;
+ PPLATFORM_DATA platformData=(PPLATFORM_DATA)&(privateData->PlatformData);
+ Platform_CleanUp(platformData);
+ kfree(SMSC9118->ml_priv);
+ SMSC9118->ml_priv=NULL;
+ }
+ unregister_netdev(SMSC9118);
+ SMSC_TRACE("<-- cleanup_module()");
+}
+
+void Smsc9118_init(struct net_device *dev)
+{
+ u32 dwLanBase=0UL;
+ u32 dwIdRev=0UL;
+ u32 dwFpgaRev=0UL;
+ // WORD SpecialCtrlSts=0U;
+ PPRIVATE_DATA privateData=NULL;
+ PPLATFORM_DATA platformData=NULL;
+ bool platformInitialized=false;
+ int result=-ENODEV;
+
+ int i;
+ bool acquired_mem_region=false;
+ bool acquired_isr=false;
+
+ SMSC_TRACE("-->Smsc9118_init(dev=0x%08X)",(u32)dev);
+
+ if(dev==NULL) {
+ SMSC_WARNING("Smsc9118_init(dev==NULL)");
+ result=-EFAULT;
+ goto DONE;
+ }
+
+ if(dev->ml_priv!=NULL) {
+ SMSC_WARNING("dev->ml_priv!=NULL, going to overwrite pointer");
+ }
+ dev->ml_priv=kmalloc(sizeof(PRIVATE_DATA),GFP_KERNEL);
+ if(dev->ml_priv==NULL) {
+ SMSC_WARNING("Unable to allocate PRIVATE_DATA");
+ result=-ENOMEM;
+ goto DONE;
+ }
+ memset(dev->ml_priv,0,sizeof(PRIVATE_DATA));
+ privateData=(PPRIVATE_DATA)(dev->ml_priv);
+ platformData=&(privateData->PlatformData);
+
+ dwLanBase=Platform_Initialize(
+ platformData,
+ lan_base,bus_width);
+
+ if(dwLanBase==0UL) {
+ SMSC_WARNING("dwLanBase==0x00000000");
+ result=-ENODEV;
+ goto DONE;
+ }
+ platformInitialized=true;
+ SMSC_TRACE("dwLanBase=0x%08X",dwLanBase);
+
+ if(check_mem_region(dwLanBase,LAN_REGISTER_EXTENT)!=0) {
+ SMSC_WARNING(" Memory Region specified (0x%08X to 0x%08X) is not available.",
+ dwLanBase,(u32)(dwLanBase+LAN_REGISTER_EXTENT-1UL));
+ result=-ENOMEM;
+ goto DONE;
+ }
+
+ privateData->dwLanBase=dwLanBase;
+ dwIdRev=Lan_GetRegDW(ID_REV);
+ if(HIWORD(dwIdRev)==LOWORD(dwIdRev)) {
+ //this may mean the chip is set for 32 bit
+ // while the bus is reading as 16 bit
+UNKNOWN_CHIP:
+ SMSC_WARNING(" LAN9118 Family NOT Identified, dwIdRev==0x%08X",dwIdRev);
+ result=-ENODEV;
+ goto DONE;
+ }
+ switch(dwIdRev&0xFFFF0000UL) {
+
+ case 0x93120000UL:
+ if (Scatter_gather | tx_Csum | rx_Csum)
+ SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default");
+ privateData->UseScatterGather=false;
+ privateData->UseTxCsum=false;
+ privateData->UseRxCsum=false;
+
+ switch(dwIdRev&0x0000FFFFUL) {
+ case 1UL:
+ SMSC_TRACE(" Hydra identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=3;
+ break;
+ default:
+ SMSC_TRACE(" Hydra identified (NEW), dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=3;
+ break;
+ };break;
+
+
+ case 0x01180000UL:
+ if (Scatter_gather | tx_Csum | rx_Csum)
+ SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default");
+ privateData->UseScatterGather=false;
+ privateData->UseTxCsum=false;
+ privateData->UseRxCsum=false;
+
+ switch(dwIdRev&0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE(" LAN9118 Beacon identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=0;
+ break;
+ case 1UL:
+ SMSC_TRACE(" LAN9118 Concord A0 identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=1;
+ break;
+ case 2UL:
+ SMSC_TRACE(" LAN9118 Concord A1 identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=2;
+ break;
+ default:
+ SMSC_TRACE(" LAN9118 Concord A1 identified (NEW), dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=2;
+ break;
+ };break;
+
+ case 0x01170000UL:
+ if (Scatter_gather | tx_Csum | rx_Csum)
+ SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default");
+ privateData->UseScatterGather=false;
+ privateData->UseTxCsum=false;
+ privateData->UseRxCsum=false;
+
+ switch(dwIdRev&0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE(" LAN9117 Beacon identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=0;
+ break;
+ case 1UL:
+ SMSC_TRACE(" LAN9117 Concord A0 identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=1;
+ break;
+ case 2UL:
+ SMSC_TRACE(" LAN9117 Concord A1 identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=2;
+ break;
+ default:
+ SMSC_TRACE(" LAN9117 Concord A1 identified (NEW), dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=2;
+ break;
+ };break;
+
+ case 0x01160000UL:
+ if (Scatter_gather | tx_Csum | rx_Csum)
+ SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default");
+ privateData->UseScatterGather=false;
+ privateData->UseTxCsum=false;
+ privateData->UseRxCsum=false;
+
+ switch(dwIdRev&0x0000FFFFUL) {
+ case 0UL:
+ goto UNKNOWN_CHIP;
+ case 1UL:
+ SMSC_TRACE(" LAN9116 Concord A0 identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=1;
+ break;
+ case 2UL:
+ SMSC_TRACE(" LAN9116 Concord A1 identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=2;
+ break;
+ default:
+ SMSC_TRACE(" LAN9116 Concord A1 identified (NEW), dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=2;
+ break;
+ };break;
+
+ case 0x01150000UL:
+ if (Scatter_gather | tx_Csum | rx_Csum)
+ SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default");
+ privateData->UseScatterGather=false;
+ privateData->UseTxCsum=false;
+ privateData->UseRxCsum=false;
+
+ switch(dwIdRev&0x0000FFFFUL) {
+ case 0UL:
+ goto UNKNOWN_CHIP;
+ case 1UL:
+ SMSC_TRACE(" LAN9115 Concord A0 identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=1;
+ break;
+ case 2UL:
+ SMSC_TRACE(" LAN9115 Concord A1 identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=2;
+ break;
+ default:
+ SMSC_TRACE(" LAN9115 Concord A1 identified (NEW), dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=2;
+ break;
+ };break;
+
+ case 0x118A0000UL:
+ if (Scatter_gather | tx_Csum | rx_Csum)
+ SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default");
+ privateData->UseScatterGather=false;
+ privateData->UseTxCsum=false;
+ privateData->UseRxCsum=false;
+
+ switch(dwIdRev&0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE(" LAN9218 Boylston identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=3;
+ break;
+ default:
+ SMSC_TRACE(" LAN9218 Boylston identified (NEW), dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=3;
+ break;
+ };break;
+
+ case 0x117A0000UL:
+ if (Scatter_gather | tx_Csum | rx_Csum)
+ SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default");
+ privateData->UseScatterGather=false;
+ privateData->UseTxCsum=false;
+ privateData->UseRxCsum=false;
+
+ switch(dwIdRev&0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE(" LAN9217 Boylston identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=3;
+ break;
+ default:
+ SMSC_TRACE(" LAN9217 Boylston identified (NEW), dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=3;
+ break;
+ };break;
+
+ case 0x116A0000UL:
+ if (Scatter_gather | tx_Csum | rx_Csum)
+ SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default");
+ privateData->UseScatterGather=false;
+ privateData->UseTxCsum=false;
+ privateData->UseRxCsum=false;
+
+ switch(dwIdRev&0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE(" LAN9216 Boylston identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=3;
+ break;
+ default:
+ SMSC_TRACE(" LAN9216 Boylston identified (NEW), dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=3;
+ break;
+ };break;
+
+ case 0x115A0000UL:
+ if (Scatter_gather | tx_Csum | rx_Csum)
+ SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default");
+ privateData->UseScatterGather=false;
+ privateData->UseTxCsum=false;
+ privateData->UseRxCsum=false;
+
+ switch(dwIdRev&0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE(" LAN9215 Boylston identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=3;
+ break;
+ default:
+ SMSC_TRACE(" LAN9215 Boylston identified (NEW), dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=3;
+ break;
+ };break;
+
+
+ case 0x92100000UL:
+ privateData->UseScatterGather=Scatter_gather;
+ privateData->UseTxCsum=tx_Csum;
+ privateData->UseRxCsum=rx_Csum;
+ if (Scatter_gather)
+ SMSC_TRACE(" Tx Scatter-Gather");
+ if (tx_Csum)
+ SMSC_TRACE(" Tx HW Checksum");
+ if (rx_Csum)
+ SMSC_TRACE(" Rx HW Checksum");
+
+ switch(dwIdRev&0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE(" LAN9210 Boylston Lite identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=4;
+ break;
+
+ default:
+ SMSC_TRACE(" LAN9210 Boylston Lite identified (NEW), dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=4;
+ break;
+ };break;
+
+ case 0x92110000UL:
+ privateData->UseScatterGather=Scatter_gather;
+ privateData->UseTxCsum=tx_Csum;
+ privateData->UseRxCsum=rx_Csum;
+ if (Scatter_gather)
+ SMSC_TRACE(" Tx Scatter-Gather");
+ if (tx_Csum)
+ SMSC_TRACE(" Tx HW Checksum");
+ if (rx_Csum)
+ SMSC_TRACE(" Rx HW Checksum");
+
+ switch(dwIdRev&0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE(" LAN9211 Boylston Lite identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=4;
+ break;
+
+ default:
+ SMSC_TRACE(" LAN9211 Boylston Lite identified (NEW), dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=4;
+ break;
+ };break;
+
+ case 0x215A0000UL:
+ privateData->UseScatterGather=Scatter_gather;
+ privateData->UseTxCsum=tx_Csum;
+ privateData->UseRxCsum=rx_Csum;
+ if (Scatter_gather)
+ SMSC_TRACE(" Tx Scatter-Gather");
+ if (tx_Csum)
+ SMSC_TRACE(" Tx HW Checksum");
+ if (rx_Csum)
+ SMSC_TRACE(" Rx HW Checksum");
+
+ switch(dwIdRev&0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE(" LAN9215A Boylston Auto identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=4;
+ break;
+
+ default:
+ SMSC_TRACE(" LAN9215A Boylston Auto identified (NEW), dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=4;
+ break;
+ };break;
+
+ case 0x216A0000UL:
+ privateData->UseScatterGather=Scatter_gather;
+ privateData->UseTxCsum=tx_Csum;
+ privateData->UseRxCsum=rx_Csum;
+ if (Scatter_gather)
+ SMSC_TRACE(" Tx Scatter-Gather");
+ if (tx_Csum)
+ SMSC_TRACE(" Tx HW Checksum");
+ if (rx_Csum)
+ SMSC_TRACE(" Rx HW Checksum");
+
+ switch(dwIdRev&0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE(" LAN9216A Boylston Auto identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=4;
+ break;
+
+ default:
+ SMSC_TRACE(" LAN9216A Boylston Auto identified (NEW), dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=4;
+ break;
+ };break;
+
+ case 0x217A0000UL:
+ privateData->UseScatterGather=Scatter_gather;
+ privateData->UseTxCsum=tx_Csum;
+ privateData->UseRxCsum=rx_Csum;
+ if (Scatter_gather)
+ SMSC_TRACE(" Tx Scatter-Gather");
+ if (tx_Csum)
+ SMSC_TRACE(" Tx HW Checksum");
+ if (rx_Csum)
+ SMSC_TRACE(" Rx HW Checksum");
+
+ switch(dwIdRev&0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE(" LAN9217A Boylston Auto identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=4;
+ break;
+
+ default:
+ SMSC_TRACE(" LAN9217A Boylston Auto identified (NEW), dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=4;
+ break;
+ };break;
+
+
+ case 0x218A0000UL:
+ privateData->UseScatterGather=Scatter_gather;
+ privateData->UseTxCsum=tx_Csum;
+ privateData->UseRxCsum=rx_Csum;
+ if (Scatter_gather)
+ SMSC_TRACE(" Tx Scatter-Gather");
+ if (tx_Csum)
+ SMSC_TRACE(" Tx HW Checksum");
+ if (rx_Csum)
+ SMSC_TRACE(" Rx HW Checksum");
+
+ switch(dwIdRev&0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE(" LAN9218A Boylston Auto identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=4;
+ break;
+
+ default:
+ SMSC_TRACE(" LAN9218A Boylston Auto identified (NEW), dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=4;
+ break;
+ };break;
+
+ default:
+ //This is used for the new unkonw chip before we formally add them in the driver
+ if (id_reg==dwIdRev) {
+ if (Csum_Support) {
+ privateData->UseScatterGather=Scatter_gather;
+ privateData->UseTxCsum=tx_Csum;
+ privateData->UseRxCsum=rx_Csum;
+ } else {
+ SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default");
+ privateData->UseScatterGather=false;
+ privateData->UseTxCsum=false;
+ privateData->UseRxCsum=false;
+ }
+
+ SMSC_TRACE(" New Chip identified, dwIdRev==0x%08X",dwIdRev);
+ privateData->dwGeneration=5;
+
+ } else {
+
+ SMSC_WARNING("unknow chip, dwIdRev==0x%08X",dwIdRev);
+
+ }; break;
+
+
+ }
+
+ //printk("dwGeneration = %d\n", privateData->dwGeneration);
+
+ dwFpgaRev=Lan_GetRegDW(FPGA_REV);
+ SMSC_TRACE(" FPGA_REV == 0x%08X",dwFpgaRev);
+
+
+ ether_setup(dev);
+ dev->open= Smsc9118_open;
+ dev->stop= Smsc9118_stop;
+ dev->hard_start_xmit= Smsc9118_hard_start_xmit;
+ dev->get_stats= Smsc9118_get_stats;
+ dev->do_ioctl= Smsc9118_do_ioctl;
+ dev->set_multicast_list=Smsc9118_set_multicast_list;
+ dev->flags|=IFF_MULTICAST;
+#ifdef LINUX_2_6_OR_NEWER
+ /*
+ if (rx_mode==PROCESSING_MODE_NAPI) {
+ dev->poll=Smsc9118_rx_poll;
+ dev->weight=napi_weight;
+ }
+ */
+#endif
+ if(privateData->UseScatterGather) {
+
+ if(privateData->UseTxCsum)
+ dev->features = (NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST);
+ else
+ dev->features = (NETIF_F_SG | NETIF_F_FRAGLIST); // Kernel will turn off SG in this case.
+ }
+
+ else {
+
+ if(privateData->UseTxCsum)
+ dev->features = (NETIF_F_HW_CSUM);
+ else
+ dev->features = 0;
+ }
+
+ if(dev==NULL) {
+ SMSC_WARNING("Smsc9118_open(dev==NULL)");
+ result=-EFAULT;
+ goto DONE;
+ }
+ privateData=(PPRIVATE_DATA)(dev->ml_priv);
+ if(privateData==NULL) {
+ SMSC_WARNING("Smsc9118_open(privateData==NULL)");
+ result=-EFAULT;
+ goto DONE;
+ }
+ platformData=&(privateData->PlatformData);
+
+ for (i = 0; i < GPT_SCHEDULE_DEPTH; i++) {
+ privateData->GptFunction [i] = NULL;
+ }
+ privateData->Gpt_scheduled_slot_index = GPT_SCHEDULE_DEPTH;
+
+ //get memory region
+ if(check_mem_region(privateData->dwLanBase,LAN_REGISTER_EXTENT)!=0)
+ {
+ SMSC_WARNING("Device memory is already in use.");
+ result=-ENOMEM;
+ goto DONE;
+ }
+ request_mem_region(privateData->dwLanBase,LAN_REGISTER_EXTENT,"SMSC_LAN9118");
+ acquired_mem_region=true;
+
+ //initialize the LAN9118
+ {
+ u32 dwIntCfg=0;
+ if(irq_pol) {
+ dwIntCfg|=INT_CFG_IRQ_POL_;
+ }
+ if(irq_type) {
+ dwIntCfg|=INT_CFG_IRQ_TYPE_;
+ }
+ if(!Lan_Initialize(privateData,dwIntCfg,tx_fif_sz,afc_cfg))
+ {
+ SMSC_WARNING("Failed Lan_Initialize");
+ result=-ENODEV;
+ goto DONE;
+ }
+ }
+
+ if(!Platform_RequestIRQ(platformData,irq,Smsc9118_ISR,privateData)) {
+ result=-ENODEV;
+ goto DONE;
+ }
+ acquired_isr=true;
+
+ //must now test the IRQ connection to the ISR
+ SMSC_TRACE("Testing ISR using IRQ %d",Platform_CurrentIRQ(platformData));
+ {
+ u32 dwTimeOut=1000000;
+ Lan_SignalSoftwareInterrupt(privateData);
+ SMSC_TRACE("privateData=%08X", (u32)privateData);
+ do {
+ udelay(10);
+ dwTimeOut--;
+ } while((dwTimeOut)&&(!(privateData->SoftwareInterruptSignal)));
+ if(!(privateData->SoftwareInterruptSignal)) {
+ SMSC_WARNING("ISR failed signaling test");
+ result=-ENODEV;
+ goto DONE;
+ }
+ }
+ SMSC_TRACE("ISR passed test using IRQ %d",Platform_CurrentIRQ(platformData));
+
+ if(!Mac_Initialize(privateData)) {
+ SMSC_WARNING("Failed Mac_Initialize");
+ result=-ENODEV;
+ goto DONE;
+ }
+ {//get mac address
+ u32 dwHigh16=0;
+ u32 dwLow32=0;
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ if(mac_addr_hi16==0xFFFFFFFF) {
+ dwHigh16=Mac_GetRegDW(privateData,ADDRH,keyCode);
+ dwLow32=Mac_GetRegDW(privateData,ADDRL,keyCode);
+ if((dwHigh16==0x0000FFFFUL)&&(dwLow32==0xFFFFFFFF))
+ {
+ dwHigh16=0x00000070UL;
+ dwLow32=0x110F8000UL;
+ Mac_SetRegDW(privateData,ADDRH,dwHigh16,keyCode);
+ Mac_SetRegDW(privateData,ADDRL,dwLow32,keyCode);
+ SMSC_TRACE("Mac Address is set by default to 0x%04X%08X",
+ dwHigh16,dwLow32);
+ } else {
+ SMSC_TRACE("Mac Address is read from LAN9118 as 0x%04X%08X",
+ dwHigh16,dwLow32);
+ }
+ } else {
+ //SMSC_ASSERT((mac_addr_hi16&0xFFFF8000UL)==0);
+ dwHigh16=mac_addr_hi16;
+ dwLow32=mac_addr_lo32;
+ Mac_SetRegDW(privateData,ADDRH,dwHigh16,keyCode);
+ Mac_SetRegDW(privateData,ADDRL,dwLow32,keyCode);
+ SMSC_TRACE("Mac Address is set by parameter to 0x%04X%08X",
+ dwHigh16,dwLow32);
+ }
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ dev->dev_addr[0]=LOBYTE(LOWORD(dwLow32));
+ dev->dev_addr[1]=HIBYTE(LOWORD(dwLow32));
+ dev->dev_addr[2]=LOBYTE(HIWORD(dwLow32));
+ dev->dev_addr[3]=HIBYTE(HIWORD(dwLow32));
+ dev->dev_addr[4]=LOBYTE(LOWORD(dwHigh16));
+ dev->dev_addr[5]=HIBYTE(LOWORD(dwHigh16));
+ }
+
+ privateData->dwIdRev=dwIdRev;
+ privateData->dwFpgaRev=dwFpgaRev&(0x000000FFUL);
+ privateData->dev=dev;
+
+ sprintf(privateData->ifName,"%s","eth1");
+ SMSC_TRACE("privateData->ifName = %s\n", privateData->ifName);
+ result=0;
+
+DONE:
+ if(result!=0) {
+ if(dev!=NULL) {
+ if(dev->ml_priv!=NULL) {
+ if(platformInitialized) {
+ Platform_CleanUp(platformData);
+ }
+ kfree(dev->ml_priv);
+ dev->ml_priv=NULL;
+ }
+ }
+ }
+ SMSC_TRACE("<--Smsc9118_init(), result=%d",result);
+}
+
+int Smsc9118_open(struct net_device *dev)
+{
+ int result=-ENODEV;
+ PPRIVATE_DATA privateData=NULL;
+ PPLATFORM_DATA platformData=NULL;
+ bool acquired_mem_region=false;
+ bool acquired_isr=false;
+ SMSC_TRACE("-->Smsc9118_open(dev=0x%08X)",(u32)dev);
+ if(dev==NULL) {
+ SMSC_WARNING("Smsc9118_open(dev==NULL)");
+ result=-EFAULT;
+ goto DONE;
+ }
+ privateData=(PPRIVATE_DATA)(dev->ml_priv);
+ if(privateData==NULL) {
+ SMSC_WARNING("Smsc9118_open(privateData==NULL)");
+ result=-EFAULT;
+ goto DONE;
+ }
+ platformData=&(privateData->PlatformData);
+
+ privateData->MulticastUpdatePending = false;
+#ifdef USE_PHY_WORK_AROUND
+ netif_carrier_off(dev);
+ if(!Phy_Initialize(
+ privateData,
+ phy_addr,
+ link_mode))
+ {
+ SMSC_WARNING("Failed to initialize Phy");
+ result=-ENODEV;
+ goto DONE;
+ }
+#endif
+
+ {
+ u32 dwRxDmaCh=rx_dma;
+ u32 dwTxDmaCh=tx_dma;
+ privateData->RxDmaChReserved=false;
+
+
+ if(rx_dma==TRANSFER_REQUEST_DMA) {
+ dwRxDmaCh=Platform_RequestDmaChannel(&(privateData->PlatformData));
+ SMSC_ASSERT(dwRxDmaCh!=TRANSFER_REQUEST_DMA);
+ if(dwRxDmaCh<TRANSFER_REQUEST_DMA) {
+ privateData->RxDmaChReserved=true;
+ }
+ }
+ privateData->TxDmaChReserved=false;
+ if(tx_dma==TRANSFER_REQUEST_DMA) {
+ dwTxDmaCh=Platform_RequestDmaChannel(&(privateData->PlatformData));
+ SMSC_ASSERT(dwTxDmaCh!=TRANSFER_REQUEST_DMA);
+ if(dwTxDmaCh<TRANSFER_REQUEST_DMA) {
+ privateData->TxDmaChReserved=true;
+ }
+ }
+ Tx_Initialize(privateData,dwTxDmaCh,dma_threshold);
+ Rx_Initialize(privateData,dwRxDmaCh,dma_threshold);
+
+ }
+
+#ifndef LINUX_2_6_OR_NEWER
+ MOD_INC_USE_COUNT;
+#endif
+ privateData->Running=true;
+ netif_start_queue(dev);
+ Tx_StopQueue(privateData,0x01UL);
+
+
+ spin_lock_init(&(privateData->GpTimerLock));
+ Lan_EnableInterrupt(privateData,INT_EN_GPT_INT_EN_);
+
+#ifndef USE_PHY_WORK_AROUND
+ netif_carrier_off(dev);
+ if(!Phy_Initialize(
+ privateData,
+ phy_addr,
+ link_mode))
+ {
+ SMSC_WARNING("Failed to initialize Phy");
+ result=-ENODEV;
+ goto DONE;
+ }
+#endif
+
+ result=0;
+
+DONE:
+ if(result!=0) {
+#ifndef LINUX_2_6_OR_NEWER
+ MOD_DEC_USE_COUNT;
+#endif
+ if(privateData!=NULL) {
+ if(privateData->TxDmaChReserved) {
+ Platform_ReleaseDmaChannel(platformData,
+ privateData->dwTxDmaCh);
+ privateData->TxDmaChReserved=false;
+ }
+ if(privateData->RxDmaChReserved) {
+ Platform_ReleaseDmaChannel(platformData,
+ privateData->dwRxDmaCh);
+ privateData->RxDmaChReserved=false;
+ }
+ if(acquired_isr) {
+ Platform_FreeIRQ(platformData);
+ }
+ if(acquired_mem_region) {
+ release_mem_region(
+ privateData->dwLanBase,
+ LAN_REGISTER_EXTENT);
+ }
+ }
+ }
+ SMSC_TRACE("<--Smsc9118_open, result=%d",result);
+ return result;
+}
+
+int Smsc9118_stop(struct net_device *dev)
+{
+ int result=0;
+ PPRIVATE_DATA privateData=NULL;
+ SMSC_TRACE("-->Smsc9118_stop(dev=0x%08X)",(u32)dev);
+ if(dev==NULL) {
+ SMSC_WARNING("Smsc9118_stop(dev==NULL)");
+ result=-EFAULT;
+ goto DONE;
+ }
+ privateData=(PPRIVATE_DATA)(dev->ml_priv);
+ if(privateData==NULL) {
+ SMSC_WARNING("Smsc9118_stop(privateData==NULL)");
+ result=-EFAULT;
+ goto DONE;
+ }
+
+ privateData->StopLinkPolling=true;
+ del_timer_sync(&(privateData->LinkPollingTimer));
+
+ Lan_DisableInterrupt(privateData,INT_EN_GPT_INT_EN_);
+
+ Tx_UpdateTxCounters(privateData);
+ privateData->Running=false;
+ Lan_DisableIRQ(privateData);
+
+ Tx_CompleteDma(privateData);
+
+ Tx_StopQueue(privateData,0x01UL);
+
+#ifndef LINUX_2_6_OR_NEWER
+ MOD_DEC_USE_COUNT;
+#endif
+
+ if(privateData->TxDmaChReserved) {
+ Platform_ReleaseDmaChannel(
+ &(privateData->PlatformData),
+ privateData->dwTxDmaCh);
+ privateData->TxDmaChReserved=false;
+ }
+ if(privateData->RxDmaChReserved) {
+ Platform_ReleaseDmaChannel(
+ &(privateData->PlatformData),
+ privateData->dwRxDmaCh);
+ privateData->RxDmaChReserved=false;
+ }
+
+ Platform_FreeIRQ(&(privateData->PlatformData));
+ release_mem_region(privateData->dwLanBase,LAN_REGISTER_EXTENT);
+
+ {
+ const u32 dwLanBase=privateData->dwLanBase;
+ const u32 dwIdRev=privateData->dwIdRev;
+ const u32 dwFpgaRev=privateData->dwFpgaRev;
+ struct net_device * const tempDev=privateData->dev;
+ char ifName[SMSC_IF_NAME_SIZE];
+ PLATFORM_DATA platformDataBackup;
+ memcpy(ifName,privateData->ifName,SMSC_IF_NAME_SIZE);
+ memcpy(&platformDataBackup,&(privateData->PlatformData),sizeof(PLATFORM_DATA));
+
+ memset(privateData,0,sizeof(PRIVATE_DATA));
+
+ privateData->dwLanBase=dwLanBase;
+ privateData->dwIdRev=dwIdRev;
+ privateData->dwFpgaRev=dwFpgaRev;
+ privateData->dev=tempDev;
+ memcpy(privateData->ifName,ifName,SMSC_IF_NAME_SIZE);
+ memcpy(&(privateData->PlatformData),&platformDataBackup,sizeof(PLATFORM_DATA));
+ }
+
+DONE:
+ SMSC_TRACE("<--Smsc9118_stop, result=%d",result);
+ return result;
+}
+
+int Smsc9118_hard_start_xmit(
+ struct sk_buff *skb, struct net_device * const dev)
+{
+ int result=0;
+ PPRIVATE_DATA privateData=NULL;
+ //SMSC_TRACE("-->Smsc9118_hard_start_xmit(skb=0x%08X,dev=0x%08X)",(u32)skb,(u32)dev);
+ if(skb==NULL) {
+ SMSC_WARNING("Smsc9118_hard_start_xmit(skb==NULL)");
+ result=-EFAULT;
+ goto DONE;
+ }
+ if(dev==NULL) {
+ SMSC_WARNING("Smsc9118_hard_start_xmit(dev==NULL)");
+ result=-EFAULT;
+ goto DONE;
+ }
+ if(dev->ml_priv==NULL) {
+ SMSC_WARNING("Smsc9118_hard_start_xmit(dev->ml_priv==NULL)");
+ result=-EFAULT;
+ goto DONE;
+ }
+ privateData=(PPRIVATE_DATA)(dev->ml_priv);
+ // SET_GPIO(GP_TX);
+
+ Tx_SendSkb(privateData,skb);
+
+ // CLEAR_GPIO(GP_TX);
+DONE:
+ //SMSC_TRACE("<--Smsc9118_hard_start_xmit, result=%d",result);
+ return result;
+}
+
+struct net_device_stats * Smsc9118_get_stats(struct net_device * const dev)
+{
+ PPRIVATE_DATA privateData=NULL;
+ if(dev==NULL) {
+ SMSC_WARNING("Smsc9118_get_stats(dev==NULL)");
+ return NULL;
+ }
+ if(dev->ml_priv==NULL) {
+ // SMSC_WARNING("Smsc9118_get_stats(dev->ml_priv==NULL)");
+ return NULL;
+ }
+
+ privateData=(PPRIVATE_DATA)(dev->ml_priv);
+ if(privateData->Running) {
+ privateData->stats.rx_dropped+=Lan_GetRegDW(RX_DROP);
+ Tx_UpdateTxCounters(privateData);
+ }
+ return &(privateData->stats);
+}
+
+void Smsc9118_set_multicast_list(struct net_device *dev)
+{
+ SMSC_ASSERT(dev!=NULL);
+ Rx_SetMulticastList(dev);
+}
+
+
+int Smsc9118_do_ioctl(
+ struct net_device *dev,
+ struct ifreq *ifr,
+ int cmd)
+{
+ int result=0;
+ PPRIVATE_DATA privateData=NULL;
+ void *userAddr=NULL;
+
+ // bool success=false;
+
+ SMSC_TRACE("-->Smsc9118_do_ioctl");
+ SMSC_TRACE("cmd=%d,SIOCGMIIPHY=%d,SIOCDEVPRIVATE=%d",
+ cmd,SIOCGMIIPHY,SIOCDEVPRIVATE);
+
+
+ if(dev==NULL) {
+ SMSC_WARNING("dev==NULL");
+ result=-EFAULT;
+ goto DONE;
+ }
+ if(dev->ml_priv==NULL) {
+ SMSC_WARNING("dev->ml_priv==NULL");
+ result=-EFAULT;
+ goto DONE;
+ }
+ privateData=((PPRIVATE_DATA)dev->ml_priv);
+ if(ifr==NULL) {
+ SMSC_WARNING("ifr==NULL");
+ result=-EFAULT;
+ goto DONE;
+ }
+ userAddr=ifr->ifr_data;
+
+
+ if(privateData->LanInitialized) {
+ // standard MII IOC's
+ struct mii_ioctl_data * const data=
+ (struct mii_ioctl_data *) & ifr->ifr_data;
+ switch(cmd) {
+ case SIOCGMIIPHY:
+
+ case SIOCDEVPRIVATE:
+ data->phy_id=1;
+ SMSC_TRACE("SIOCGMIIPHY: phy_id set to 0x%04X",data->phy_id);
+ break;
+ case SIOCGMIIREG:
+ case SIOCDEVPRIVATE+1:
+ {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ data->val_out=Phy_GetRegW(
+ privateData,data->reg_num,keyCode);
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ }
+ SMSC_TRACE("SIOCGMIIREG: phy_id=0x%04X, reg_num=0x%04X, val_out set to 0x%04X",
+ data->phy_id,data->reg_num,data->val_out);
+ break;
+ case SIOCSMIIREG:
+ case SIOCDEVPRIVATE+2:
+ SMSC_TRACE("SIOCSMIIREG: phy_id=0x%04X, reg_num=0x%04X, val_in=0x%04X",
+ data->phy_id,data->reg_num,data->val_in);
+ {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ Phy_SetRegW(
+ privateData,data->reg_num,((WORD)(data->val_in)),keyCode);
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ }
+ break;
+
+ case SIOCETHTOOL:
+ result=Smsc9118_ethtool_ioctl(privateData,userAddr);
+ break;
+ case SMSC9118_IOCTL:
+ result=Smsc9118_private_ioctl(privateData,userAddr);
+ break;
+
+ default:
+ result=-EOPNOTSUPP;
+ break;//make lint happy
+ }
+ }
+
+DONE:
+
+ return result;
+}
+
+
+
+int Smsc9118_private_ioctl(PPRIVATE_DATA privateData,void *useraddr)
+{
+
+
+ bool success=false;
+ int result=-EFAULT;
+ SMSC9118_IOCTL_DATA ioctlData;
+
+ if(useraddr==NULL) {
+ SMSC_WARNING("useraddr==NULL");
+ result=-EFAULT;
+ goto DONE;
+ }
+
+ if(copy_from_user(&ioctlData,useraddr,sizeof(ioctlData))) {
+ SMSC_WARNING("copy_from_user failed");
+ result=-EFAULT;
+ goto DONE;
+ }
+
+ if(ioctlData.dwSignature!=SMSC9118_APP_SIGNATURE) {
+ SMSC_WARNING("invalid application signature");
+ result=-EFAULT;
+ goto DONE;
+ }
+
+ switch(ioctlData.dwCommand) {
+ case COMMAND_GET_SIGNATURE:
+ success=true;
+ break;
+ case COMMAND_GET_FLOW_PARAMS:
+ ioctlData.Data[0]=privateData->RxFlowMeasuredMaxThroughput;
+ ioctlData.Data[1]=privateData->RxFlowMeasuredMaxPacketCount;
+ ioctlData.Data[2]=privateData->RxFlowParameters.MaxThroughput;
+ ioctlData.Data[3]=privateData->RxFlowParameters.MaxPacketCount;
+ ioctlData.Data[4]=privateData->RxFlowParameters.PacketCost;
+ ioctlData.Data[5]=privateData->RxFlowParameters.BurstPeriod;
+ ioctlData.Data[6]=privateData->RxFlowMaxWorkLoad;
+ ioctlData.Data[7]=Lan_GetRegDW(INT_CFG)>>24;
+ privateData->RxFlowMeasuredMaxThroughput=0;
+ privateData->RxFlowMeasuredMaxPacketCount=0;
+ success=true;
+ break;
+ case COMMAND_SET_FLOW_PARAMS:
+ if(!(privateData->RxFlowControlActive)) {
+ privateData->RxFlowParameters.MaxThroughput=ioctlData.Data[2];
+ privateData->RxFlowParameters.MaxPacketCount=ioctlData.Data[3];
+ privateData->RxFlowParameters.PacketCost=ioctlData.Data[4];
+ privateData->RxFlowParameters.BurstPeriod=ioctlData.Data[5];
+ if(ioctlData.Data[6]==0xFFFFFFFFUL) {
+ privateData->RxFlowMaxWorkLoad=
+ privateData->RxFlowParameters.MaxThroughput+
+ (privateData->RxFlowParameters.MaxPacketCount*
+ privateData->RxFlowParameters.PacketCost);
+ } else {
+ privateData->RxFlowMaxWorkLoad=ioctlData.Data[6];
+ }
+ Lan_SetIntDeas(privateData,ioctlData.Data[7]);
+ privateData->RxFlowBurstMaxWorkLoad=
+ (privateData->RxFlowMaxWorkLoad*
+ privateData->RxFlowParameters.BurstPeriod)/1000;
+ success=true;
+ };break;
+ case COMMAND_GET_CONFIGURATION:
+ ioctlData.Data[0]=DRIVER_VERSION;
+ ioctlData.Data[1]=lan_base;
+ ioctlData.Data[2]=bus_width;
+ ioctlData.Data[3]=link_mode;
+ ioctlData.Data[4]=irq;
+ ioctlData.Data[5]=int_deas;
+ ioctlData.Data[6]=irq_pol;
+ ioctlData.Data[7]=irq_type;
+ ioctlData.Data[8]=rx_dma;
+ ioctlData.Data[9]=tx_dma;
+ ioctlData.Data[10]=dma_threshold;
+ ioctlData.Data[11]=mac_addr_hi16;
+ ioctlData.Data[12]=mac_addr_lo32;
+ ioctlData.Data[13]=debug_mode;
+ ioctlData.Data[14]=tx_fif_sz;
+ ioctlData.Data[15]=afc_cfg;
+ ioctlData.Data[16]=rx_mode;
+ ioctlData.Data[17]=max_throughput;
+ ioctlData.Data[18]=max_packet_count;
+ ioctlData.Data[19]=packet_cost;
+ ioctlData.Data[20]=burst_period;
+ ioctlData.Data[21]=max_work_load;
+ ioctlData.Data[22]=privateData->dwIdRev;
+ ioctlData.Data[23]=privateData->dwFpgaRev;
+ ioctlData.Data[24]=1;
+ ioctlData.Data[25]=privateData->dwPhyId;
+ ioctlData.Data[26]=privateData->bPhyModel;
+ ioctlData.Data[27]=privateData->bPhyRev;
+ ioctlData.Data[28]=privateData->dwLinkSpeed;
+ ioctlData.Data[29]=privateData->RxFlowMeasuredMaxThroughput;
+ ioctlData.Data[30]=privateData->RxFlowMeasuredMaxPacketCount;
+ ioctlData.Data[31]=privateData->RxFlowParameters.MaxThroughput;
+ ioctlData.Data[32]=privateData->RxFlowParameters.MaxPacketCount;
+ ioctlData.Data[33]=privateData->RxFlowParameters.PacketCost;
+ ioctlData.Data[34]=privateData->RxFlowParameters.BurstPeriod;
+ ioctlData.Data[35]=privateData->RxFlowMaxWorkLoad;
+ sprintf(ioctlData.Strng1,"%s, %s",__DATE__,__TIME__);
+ sprintf(ioctlData.Strng2,"%s",privateData->ifName);
+ privateData->RxFlowMeasuredMaxThroughput=0;
+ privateData->RxFlowMeasuredMaxPacketCount=0;
+ success=true;
+ break;
+ case COMMAND_LAN_GET_REG:
+ if((ioctlData.Data[0]<LAN_REGISTER_EXTENT)&&
+ ((ioctlData.Data[0]&0x3UL)==0))
+ {
+ ioctlData.Data[1]=
+ (*((volatile u32 *)(privateData->dwLanBase+
+ ioctlData.Data[0])));
+ success=true;
+ } else {
+ SMSC_WARNING("Reading LAN9118 Mem Map Failed");
+ goto MEM_MAP_ACCESS_FAILED;
+ }
+ break;
+ case COMMAND_LAN_SET_REG:
+ if((ioctlData.Data[0]<LAN_REGISTER_EXTENT)&&
+ ((ioctlData.Data[0]&0x3UL)==0))
+ {
+ (*((volatile u32 *)(privateData->dwLanBase+
+ ioctlData.Data[0])))=ioctlData.Data[1];
+ success=true;
+ } else {
+ SMSC_WARNING("Reading LAN9118 Mem Map Failed");
+MEM_MAP_ACCESS_FAILED:
+ SMSC_WARNING(" Invalid offset == 0x%08X",(u32)(ioctlData.Data[0]));
+ if(ioctlData.Data[0]>=LAN_REGISTER_EXTENT) {
+ SMSC_WARNING(" Out of range");
+ }
+ if(ioctlData.Data[0]&0x3UL) {
+ SMSC_WARNING(" Not u32 aligned");
+ }
+ }
+ break;
+ case COMMAND_MAC_GET_REG:
+ if((ioctlData.Data[0]<=0xC)&&(privateData->LanInitialized)) {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ ioctlData.Data[1]=
+ Mac_GetRegDW(privateData,ioctlData.Data[0],keyCode);
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ success=true;
+ } else {
+ SMSC_WARNING("Reading Mac Register Failed");
+ goto MAC_ACCESS_FAILURE;
+ }
+ break;
+ case COMMAND_MAC_SET_REG:
+ if((ioctlData.Data[0]<=0xC)&&(privateData->LanInitialized)) {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ Mac_SetRegDW(
+ privateData,
+ ioctlData.Data[0],
+ ioctlData.Data[1],
+ keyCode);
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ success=true;
+ } else {
+ SMSC_WARNING("Writing Mac Register Failed");
+MAC_ACCESS_FAILURE:
+ if(!(privateData->LanInitialized)) {
+
+ SMSC_WARNING(" LAN Not Initialized,");
+ SMSC_WARNING(" Use ifconfig to bring interface UP");
+ }
+ if(!(ioctlData.Data[0]<=0xC)) {
+ SMSC_WARNING(" Invalid index == 0x%08X",(u32)(ioctlData.Data[0]));
+ }
+ }
+ break;
+ case COMMAND_PHY_GET_REG:
+ if((ioctlData.Data[0]<32)&&(privateData->LanInitialized)) {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ ioctlData.Data[1]=((u32)
+ Phy_GetRegW(privateData,ioctlData.Data[0],keyCode));
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ success=true;
+ } else {
+ SMSC_WARNING("Reading Phy Register Failed");
+ goto PHY_ACCESS_FAILURE;
+ }
+ break;
+ case COMMAND_PHY_SET_REG:
+ if((ioctlData.Data[0]<32)&&(privateData->LanInitialized)) {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ Phy_SetRegW(
+ privateData,
+ ioctlData.Data[0],
+ ((WORD)(ioctlData.Data[1])),
+ keyCode);
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ success=true;
+ } else {
+ SMSC_WARNING("Writing Phy Register Failed");
+PHY_ACCESS_FAILURE:
+ if(!(privateData->LanInitialized)) {
+ SMSC_WARNING(" Lan Not Initialized,");
+ SMSC_WARNING(" Use ifconfig to bring interface UP");
+ }
+ if(!(ioctlData.Data[0]<32)) {
+ SMSC_WARNING(" Invalid index == 0x%d",(u32)(ioctlData.Data[0]));
+ }
+ }
+ break;
+ // case COMMAND_DUMP_TEMP:
+ // {
+ // u32 c=0;
+ // for(c=0;c<0x40;c++)
+ // ioctlData.Data[c]=privateData->temp[c];
+ // }
+ // success=true;
+ // break;
+ case COMMAND_DUMP_LAN_REGS:
+ ioctlData.Data[LAN_REG_ID_REV]=Lan_GetRegDW(ID_REV);
+ ioctlData.Data[LAN_REG_INT_CFG]=Lan_GetRegDW(INT_CFG);
+ ioctlData.Data[LAN_REG_INT_STS]=Lan_GetRegDW(INT_STS);
+ ioctlData.Data[LAN_REG_INT_EN]=Lan_GetRegDW(INT_EN);
+ ioctlData.Data[LAN_REG_BYTE_TEST]=Lan_GetRegDW(BYTE_TEST);
+ ioctlData.Data[LAN_REG_FIFO_INT]=Lan_GetRegDW(FIFO_INT);
+ ioctlData.Data[LAN_REG_RX_CFG]=Lan_GetRegDW(RX_CFG);
+ ioctlData.Data[LAN_REG_TX_CFG]=Lan_GetRegDW(TX_CFG);
+ ioctlData.Data[LAN_REG_HW_CFG]=Lan_GetRegDW(HW_CFG);
+ ioctlData.Data[LAN_REG_RX_DP_CTRL]=Lan_GetRegDW(RX_DP_CTRL);
+ ioctlData.Data[LAN_REG_RX_FIFO_INF]=Lan_GetRegDW(RX_FIFO_INF);
+ ioctlData.Data[LAN_REG_TX_FIFO_INF]=Lan_GetRegDW(TX_FIFO_INF);
+ ioctlData.Data[LAN_REG_PMT_CTRL]=Lan_GetRegDW(PMT_CTRL);
+ ioctlData.Data[LAN_REG_GPIO_CFG]=Lan_GetRegDW(GPIO_CFG);
+ ioctlData.Data[LAN_REG_GPT_CFG]=Lan_GetRegDW(GPT_CFG);
+ ioctlData.Data[LAN_REG_GPT_CNT]=Lan_GetRegDW(GPT_CNT);
+ ioctlData.Data[LAN_REG_FPGA_REV]=Lan_GetRegDW(FPGA_REV);
+ ioctlData.Data[LAN_REG_WORD_SWAP]=Lan_GetRegDW(WORD_SWAP);
+ ioctlData.Data[LAN_REG_FREE_RUN]=Lan_GetRegDW(FREE_RUN);
+ ioctlData.Data[LAN_REG_RX_DROP]=Lan_GetRegDW(RX_DROP);
+ if(privateData->LanInitialized) {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ ioctlData.Data[LAN_REG_MAC_CSR_CMD]=Lan_GetRegDW(MAC_CSR_CMD);
+ ioctlData.Data[LAN_REG_MAC_CSR_DATA]=Lan_GetRegDW(MAC_CSR_DATA);
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ } else {
+ ioctlData.Data[LAN_REG_MAC_CSR_CMD]=Lan_GetRegDW(MAC_CSR_CMD);
+ ioctlData.Data[LAN_REG_MAC_CSR_DATA]=Lan_GetRegDW(MAC_CSR_DATA);
+ }
+ ioctlData.Data[LAN_REG_AFC_CFG]=Lan_GetRegDW(AFC_CFG);
+ ioctlData.Data[LAN_REG_E2P_CMD]=Lan_GetRegDW(E2P_CMD);
+ ioctlData.Data[LAN_REG_E2P_DATA]=Lan_GetRegDW(E2P_DATA);
+ success=true;
+ break;
+ case COMMAND_DUMP_MAC_REGS:
+ if(privateData->LanInitialized) {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ ioctlData.Data[MAC_REG_MAC_CR]=Mac_GetRegDW(privateData,MAC_CR,keyCode);
+ ioctlData.Data[MAC_REG_ADDRH]=Mac_GetRegDW(privateData,ADDRH,keyCode);
+ ioctlData.Data[MAC_REG_ADDRL]=Mac_GetRegDW(privateData,ADDRL,keyCode);
+ ioctlData.Data[MAC_REG_HASHH]=Mac_GetRegDW(privateData,HASHH,keyCode);
+ ioctlData.Data[MAC_REG_HASHL]=Mac_GetRegDW(privateData,HASHL,keyCode);
+ ioctlData.Data[MAC_REG_MII_ACC]=Mac_GetRegDW(privateData,MII_ACC,keyCode);
+ ioctlData.Data[MAC_REG_MII_DATA]=Mac_GetRegDW(privateData,MII_DATA,keyCode);
+ ioctlData.Data[MAC_REG_FLOW]=Mac_GetRegDW(privateData,FLOW,keyCode);
+ ioctlData.Data[MAC_REG_VLAN1]=Mac_GetRegDW(privateData,VLAN1,keyCode);
+ ioctlData.Data[MAC_REG_VLAN2]=Mac_GetRegDW(privateData,VLAN2,keyCode);
+ ioctlData.Data[MAC_REG_WUFF]=Mac_GetRegDW(privateData,WUFF,keyCode);
+ ioctlData.Data[MAC_REG_WUCSR]=Mac_GetRegDW(privateData,WUCSR,keyCode);
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ success=true;
+ } else {
+ SMSC_WARNING("Mac Not Initialized,");
+ SMSC_WARNING(" Use ifconfig to bring interface UP");
+ }
+ break;
+ case COMMAND_DUMP_PHY_REGS:
+ if(privateData->LanInitialized) {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ ioctlData.Data[PHY_REG_0]=Phy_GetRegW(privateData,0,keyCode);
+ ioctlData.Data[PHY_REG_1]=Phy_GetRegW(privateData,1,keyCode);
+ ioctlData.Data[PHY_REG_2]=Phy_GetRegW(privateData,2,keyCode);
+ ioctlData.Data[PHY_REG_3]=Phy_GetRegW(privateData,3,keyCode);
+ ioctlData.Data[PHY_REG_4]=Phy_GetRegW(privateData,4,keyCode);
+ ioctlData.Data[PHY_REG_5]=Phy_GetRegW(privateData,5,keyCode);
+ ioctlData.Data[PHY_REG_6]=Phy_GetRegW(privateData,6,keyCode);
+ ioctlData.Data[PHY_REG_16]=Phy_GetRegW(privateData,16,keyCode);
+ ioctlData.Data[PHY_REG_17]=Phy_GetRegW(privateData,17,keyCode);
+ ioctlData.Data[PHY_REG_18]=Phy_GetRegW(privateData,18,keyCode);
+ ioctlData.Data[PHY_REG_20]=Phy_GetRegW(privateData,20,keyCode);
+ ioctlData.Data[PHY_REG_21]=Phy_GetRegW(privateData,21,keyCode);
+ ioctlData.Data[PHY_REG_22]=Phy_GetRegW(privateData,22,keyCode);
+ ioctlData.Data[PHY_REG_23]=Phy_GetRegW(privateData,23,keyCode);
+ ioctlData.Data[PHY_REG_27]=Phy_GetRegW(privateData,27,keyCode);
+ ioctlData.Data[PHY_REG_28]=Phy_GetRegW(privateData,28,keyCode);
+ ioctlData.Data[PHY_REG_29]=Phy_GetRegW(privateData,29,keyCode);
+ ioctlData.Data[PHY_REG_30]=Phy_GetRegW(privateData,30,keyCode);
+ ioctlData.Data[PHY_REG_31]=Phy_GetRegW(privateData,31,keyCode);
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ success=true;
+ } else {
+ SMSC_WARNING("Phy Not Initialized,");
+ SMSC_WARNING(" Use ifconfig to bring interface UP");
+ }
+ break;
+ case COMMAND_DUMP_EEPROM:
+ {
+ BYTE data=0;
+ BYTE index=0;
+ Eeprom_EnableAccess(privateData);
+ success=true;
+ for(index=0;index<8;index++) {
+ if(Eeprom_ReadLocation(privateData,index,&data)) {
+ ioctlData.Data[index]=(u32)data;
+ } else {
+ success=false;
+ break;
+ }
+ }
+ Eeprom_DisableAccess(privateData);
+ };break;
+ case COMMAND_GET_MAC_ADDRESS:
+ if(privateData->LanInitialized) {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ ioctlData.Data[0]=Mac_GetRegDW(privateData,ADDRH,keyCode);
+ ioctlData.Data[1]=Mac_GetRegDW(privateData,ADDRL,keyCode);
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ success=true;
+ } else {
+ SMSC_WARNING("Lan Not Initialized,");
+ SMSC_WARNING(" Use ifconfig to bring interface UP");
+ }
+ break;
+
+ case COMMAND_SET_MAC_ADDRESS:
+ if(privateData->LanInitialized)
+ {
+ u32 dwLow32=ioctlData.Data[1];
+ u32 dwHigh16=ioctlData.Data[0];
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ Mac_SetRegDW(privateData,ADDRH,dwHigh16,keyCode);
+ Mac_SetRegDW(privateData,ADDRL,dwLow32,keyCode);
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ success=true;
+ } else {
+ SMSC_WARNING("Lan Not Initialized,");
+ SMSC_WARNING(" Use ifconfig to bring interface UP");
+ };break;
+ case COMMAND_LOAD_MAC_ADDRESS:
+ if(privateData->LanInitialized) {
+ Eeprom_EnableAccess(privateData);
+ if(Eeprom_Reload(privateData)) {
+ if(Eeprom_IsMacAddressLoaded(privateData)) {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ ioctlData.Data[0]=Mac_GetRegDW(privateData,ADDRH,keyCode);
+ ioctlData.Data[1]=Mac_GetRegDW(privateData,ADDRL,keyCode);
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ success=true;
+ } else {
+ SMSC_WARNING("Failed to Load Mac Address(1)");
+ }
+ } else {
+ SMSC_WARNING("Failed to Load Mac Address(2)");
+ }
+ Eeprom_DisableAccess(privateData);
+ } else {
+ SMSC_WARNING("Lan Not Initialized,");
+ SMSC_WARNING(" Use ifconfig to bring interface UP");
+ };break;
+ case COMMAND_SAVE_MAC_ADDRESS:
+ if(privateData->LanInitialized) {
+ if(Eeprom_SaveMacAddress(privateData,
+ ioctlData.Data[0],ioctlData.Data[1])) {
+ success=true;
+ }
+ } else {
+ SMSC_WARNING("Lan Not Initialized,");
+ SMSC_WARNING(" Use ifconfig to bring interface UP");
+ };break;
+ case COMMAND_SET_DEBUG_MODE:
+ debug_mode=ioctlData.Data[0];
+ if(debug_mode&0x04UL) {
+ if(OLD_REGISTERS(privateData))
+ {
+ g_GpioSetting=0x00270700UL;
+ } else {
+ g_GpioSetting=0x00670700UL;
+ }
+ Lan_SetRegDW(GPIO_CFG,g_GpioSetting);
+ } else {
+ Lan_SetRegDW(GPIO_CFG,0x70070000);
+ }
+ success=true;
+ break;
+ case COMMAND_SET_LINK_MODE:
+ link_mode=(ioctlData.Data[0]&0x7FUL);
+ if(privateData->LanInitialized) {
+ Phy_SetLink(privateData,link_mode);
+ }
+ success=true;
+ break;
+ case COMMAND_GET_LINK_MODE:
+ ioctlData.Data[0]=link_mode;
+ success=true;
+ break;
+ case COMMAND_CHECK_LINK:
+ Phy_UpdateLinkMode(privateData);
+ success=true;
+ break;
+ case COMMAND_READ_BYTE:
+ ioctlData.Data[1]=(*((volatile BYTE *)(ioctlData.Data[0])));
+ success=true;
+ break;
+ case COMMAND_READ_WORD:
+ ioctlData.Data[1]=(*((volatile WORD *)(ioctlData.Data[0])));
+ success=true;
+ break;
+ case COMMAND_READ_DWORD:
+ ioctlData.Data[1]=(*((volatile u32 *)(ioctlData.Data[0])));
+ success=true;
+ break;
+ case COMMAND_WRITE_BYTE:
+ (*((volatile BYTE *)(ioctlData.Data[0])))=
+ ((BYTE)(ioctlData.Data[1]));
+ success=true;
+ break;
+ case COMMAND_WRITE_WORD:
+ (*((volatile WORD *)(ioctlData.Data[0])))=
+ ((WORD)(ioctlData.Data[1]));
+ success=true;
+ break;
+ case COMMAND_WRITE_DWORD:
+ (*((volatile u32 *)(ioctlData.Data[0])))=
+ ((u32)(ioctlData.Data[1]));
+ success=true;
+ break;
+ case COMMAND_SET_AMDIX_STS:
+ AutoMdix=(ioctlData.Data[0]);
+ if(privateData->LanInitialized) {
+ Phy_SetAutoMdixSts(privateData,AutoMdix);
+ }
+ success=true;
+ break;
+ case COMMAND_GET_AMDIX_STS:
+ ioctlData.Data[0]=AutoMdix;
+ if(privateData->LanInitialized) {
+ Phy_GetAutoMdixSts(privateData);
+ }
+ success=true;
+ break;
+
+ default:return -EOPNOTSUPP;
+ }
+
+DONE:
+ if(success) {
+ ioctlData.dwSignature=SMSC9118_DRIVER_SIGNATURE;
+ if(copy_to_user(useraddr, &ioctlData, sizeof(ioctlData))) {
+ SMSC_WARNING("copy_to_user failed");
+ result=-EFAULT;
+ }
+ result=0;
+ }
+ // SMSC_TRACE("<--Smsc9118_do_ioctl");
+ return result;
+
+}
+
+
+
+int Smsc9118_ethtool_ioctl(PPRIVATE_DATA privateData, void * userAddr)
+{
+ int result=-EFAULT;
+ u32 ethcmd=0;
+ if(copy_from_user(ðcmd,userAddr,sizeof(ethcmd)))
+ {
+ result=-EFAULT;
+ goto DONE;
+ }
+ switch(ethcmd) {
+ case ETHTOOL_GSET:// Get settings.
+ // SMSC_TRACE("ETHTOOL_GSET");
+ {
+ struct ethtool_cmd settings={ETHTOOL_GSET};
+ settings.supported=
+ SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_Autoneg |
+ SUPPORTED_MII;
+ settings.advertising=ADVERTISED_MII;
+ if(privateData->dwLinkSettings & LINK_SPEED_10HD)
+ settings.advertising|=ADVERTISED_10baseT_Half;
+ if(privateData->dwLinkSettings & LINK_SPEED_10FD)
+ settings.advertising|=ADVERTISED_10baseT_Full;
+ if(privateData->dwLinkSettings & LINK_SPEED_100HD)
+ settings.advertising|=ADVERTISED_100baseT_Half;
+ if(privateData->dwLinkSettings & LINK_SPEED_100FD)
+ settings.advertising|=ADVERTISED_100baseT_Full;
+ if(privateData->dwLinkSettings & LINK_AUTO_NEGOTIATE) {
+ settings.advertising|=ADVERTISED_Autoneg;
+ settings.autoneg=AUTONEG_ENABLE;
+ } else settings.autoneg=AUTONEG_DISABLE;
+ if(privateData->dwLinkSpeed & (LINK_SPEED_100HD|LINK_SPEED_100FD))
+ settings.speed=SPEED_100;
+ else settings.speed=SPEED_10;
+ if(privateData->dwLinkSpeed & (LINK_SPEED_10FD|LINK_SPEED_100FD))
+ settings.duplex=DUPLEX_FULL;
+ else settings.duplex=DUPLEX_HALF;
+ settings.port=PORT_MII;
+ settings.phy_address=(u8)privateData->dwPhyAddress;
+ settings.transceiver=XCVR_INTERNAL;
+ settings.maxtxpkt=0;
+ settings.maxrxpkt=0;
+ if(copy_to_user(userAddr,&settings,sizeof(settings))) {
+ result=-EFAULT;
+ } else {
+ result=0;
+ }
+ }
+ break;
+ case ETHTOOL_SSET:// Set settings.
+ // SMSC_TRACE("ETHTOOL_SSET");
+ {
+ struct ethtool_cmd settings;
+ u16 speed=0;
+ u8 duplex=0;
+ u8 autoneg=0;
+ if(copy_from_user(&settings,userAddr,sizeof(settings))) {
+ result=-EFAULT;
+ goto DONE;
+ }
+ if(privateData->dwLinkSettings&LINK_AUTO_NEGOTIATE) {
+ autoneg=AUTONEG_ENABLE;
+ } else {
+ autoneg=AUTONEG_DISABLE;
+ }
+ if(privateData->dwLinkSpeed&(LINK_SPEED_100HD|LINK_SPEED_100FD))
+ {
+ speed=SPEED_100;
+ } else {
+ speed=SPEED_10;
+ }
+ if(privateData->dwLinkSpeed&(LINK_SPEED_10FD|LINK_SPEED_100FD))
+ {
+ duplex=DUPLEX_FULL;
+ } else {
+ duplex=DUPLEX_HALF;
+ }
+ if((settings.speed!=100)&&(settings.speed!=10)) {
+ result=-EOPNOTSUPP;
+ goto DONE;
+ }
+ if((settings.duplex!=DUPLEX_FULL)&&(settings.duplex!=DUPLEX_HALF)) {
+ result=-EOPNOTSUPP;
+ goto DONE;
+ }
+ if((settings.autoneg!=AUTONEG_ENABLE)&&(settings.autoneg!=AUTONEG_DISABLE)) {
+ result=-EOPNOTSUPP;
+ goto DONE;
+ }
+ if((settings.autoneg!=autoneg)||
+ (settings.speed!=speed)||
+ (settings.duplex!=duplex))
+ {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ if(settings.autoneg==AUTONEG_ENABLE) {
+
+
+
+ WORD wAdvertisement=Phy_GetRegW(privateData,PHY_ANEG_ADV,keyCode);
+ wAdvertisement&=(~PHY_ANEG_ADV_SPEED_);
+ if(settings.speed==SPEED_100) {
+ if(settings.duplex==DUPLEX_FULL) {
+ wAdvertisement|=PHY_ANEG_ADV_100F_;
+ } else {
+ wAdvertisement|=PHY_ANEG_ADV_100H_;
+ }
+ } else {
+ if(settings.duplex==DUPLEX_FULL) {
+ wAdvertisement|=PHY_ANEG_ADV_10F_;
+ } else {
+ wAdvertisement|=PHY_ANEG_ADV_10H_;
+ }
+ }
+
+
+
+ Phy_SetRegW(privateData,PHY_ANEG_ADV,wAdvertisement,keyCode);
+ Phy_SetRegW(privateData,PHY_BCR,
+ (PHY_BCR_AUTO_NEG_ENABLE_|PHY_BCR_RESTART_AUTO_NEG_),keyCode);
+
+
+ } else {
+ WORD wBcr=Phy_GetRegW(privateData,PHY_BCR,keyCode);
+ if(settings.speed==SPEED_100) {
+ wBcr|=PHY_BCR_SPEED_SELECT_;
+ } else {
+ wBcr&=(~PHY_BCR_SPEED_SELECT_);
+ }
+ if(settings.duplex==DUPLEX_FULL) {
+ wBcr|=PHY_BCR_DUPLEX_MODE_;
+ } else {
+ wBcr&=(~PHY_BCR_DUPLEX_MODE_);
+ }
+ Phy_SetRegW(privateData,PHY_BCR,wBcr,keyCode);
+ }
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+
+ }
+ result=0;
+ }
+ break;
+ case ETHTOOL_GDRVINFO:// Get driver info.
+ // SMSC_TRACE("ETHTOOL_GDRVINFO");
+ {
+ struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};
+ strcpy(info.driver,"Smsc9131_eth");
+ memset(&info.version,0,sizeof(info.version));
+ memset(&info.fw_version,0,sizeof(info.fw_version));
+ sprintf(info.fw_version,"%u",(privateData->dwIdRev)&0xFFFF);
+ memset(&info.bus_info,0,sizeof(info.bus_info));
+ memset(&info.reserved1,0,sizeof(info.reserved1));
+ memset(&info.reserved2,0,sizeof(info.reserved2));
+#ifdef LINUX_2_6_OR_NEWER
+ info.n_stats=0;
+ info.testinfo_len=0;
+#endif
+ info.eedump_len=0;
+ info.regdump_len=0;
+ if(copy_to_user(userAddr,&info,sizeof(info))) {
+ result=-EFAULT;
+ } else {
+ result=0;
+ }
+ }
+ break;
+ case ETHTOOL_GREGS:// Get NIC registers.
+ // SMSC_TRACE("ETHTOOL_GREGS");
+ result=-EOPNOTSUPP;
+ break;
+ /*
+ case ETHTOOL_GWOL:// Get wake-on-lan options.
+ SMSC_TRACE("ETHTOOL_GWOL");
+ {
+ struct ethtool_wolinfo wol_info={ETHTOOL_GWOL};
+ //wol_info.supported=true;
+ wol_info.supported=(WAKE_PHY | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_MAGIC);
+ wol_info.wolopts= privateData->WolWakeupOpts;
+ memset(&wol_info.sopass,0,sizeof(wol_info.sopass));
+ if(copy_to_user(userAddr,&wol_info,sizeof(wol_info))) {
+ result=-EFAULT;
+ } else {
+ result=0;
+ }
+ }
+ break;
+ case ETHTOOL_SWOL:// Set wake-on-lan options.
+ SMSC_TRACE("ETHTOOL_SWOL");
+ {
+ unsigned long dwIntFlags=0;
+ struct ethtool_wolinfo wol_info;
+ if(copy_from_user(&wol_info,userAddr,sizeof(wol_info)))
+ {
+ result=-EFAULT;
+ } else {
+ SMSC_TRACE(DBG_IOCTL,"WOL OPTS = 0x%x", wol_info.wolopts);
+ spin_lock_irqsave(&(privateData->PhyLock),dwIntFlags);
+ privateData->WolWakeupOpts = wol_info.wolopts;
+ spin_unlock_irqrestore(&(privateData->PhyLock),dwIntFlags);
+ result=0;
+ }
+ }
+ break;
+ */
+ case ETHTOOL_GMSGLVL:// Get driver message level
+ // SMSC_TRACE("ETHTOOL_GMSGLVL");
+ {
+ struct ethtool_value msgLevel={ETHTOOL_GMSGLVL};
+ msgLevel.data=debug_mode;
+ if(copy_to_user(userAddr, &msgLevel,sizeof(msgLevel))) {
+ result=-EFAULT;
+ } else {
+ result=0;
+ }
+ }
+ break;
+ case ETHTOOL_SMSGLVL:// Set driver msg level.
+ // SMSC_TRACE("ETHTOOL_SMSGLVL");
+ {
+ struct ethtool_value msgLevel;
+ if(copy_from_user(&msgLevel,userAddr,sizeof(msgLevel)))
+ {
+ result=-EFAULT;
+ } else {
+ debug_mode=msgLevel.data;
+ result=0;
+ }
+ }
+ break;
+ case ETHTOOL_NWAY_RST:// Restart autonegotiation.
+ // SMSC_TRACE("ETHTOOL_NWAY_RST");
+ {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ Phy_SetRegW(privateData,PHY_BCR,
+ (PHY_BCR_AUTO_NEG_ENABLE_|PHY_BCR_RESTART_AUTO_NEG_),keyCode);
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ result=0;
+ }
+ break;
+ case ETHTOOL_GLINK:// Get link status (ethtool_value)
+ // SMSC_TRACE("ETHTOOL_GLINK");
+ {
+ struct ethtool_value linkStatus={ETHTOOL_GLINK};
+ if(privateData->dwLinkSpeed!=LINK_OFF) {
+ linkStatus.data=1;
+ } else {
+ linkStatus.data=0;
+ }
+ if(copy_to_user(userAddr,&linkStatus,sizeof(linkStatus)))
+ {
+ result=-EFAULT;
+ } else {
+ result=0;
+ }
+ }
+ break;
+ case ETHTOOL_GEEPROM:// Get EEPROM data
+ // SMSC_TRACE("ETHTOOL_GEEPROM");
+ result=-EOPNOTSUPP;
+ break;
+ case ETHTOOL_SEEPROM:// Set EEPROM data.
+ // SMSC_TRACE("ETHTOOL_SEEPROM");
+ result=-EOPNOTSUPP;
+ break;
+
+#ifdef LINUX_2_6_OR_NEWER
+
+ case ETHTOOL_GCOALESCE:// Get coalesce config
+ // SMSC_TRACE("ETHTOOL_GCOALESCE");
+ result=-EOPNOTSUPP;
+ break;
+ case ETHTOOL_SCOALESCE:// Set coalesce config.
+ // SMSC_TRACE("ETHTOOL_SCOALESCE");
+ result=-EOPNOTSUPP;
+ break;
+ case ETHTOOL_GRINGPARAM:// Get ring parameters
+ // SMSC_TRACE("ETHTOOL_GRINGPARAM");
+ result=-EOPNOTSUPP;
+ break;
+ case ETHTOOL_SRINGPARAM:// Set ring parameters.
+ // SMSC_TRACE("ETHTOOL_SRINGPARAM");
+ result=-EOPNOTSUPP;
+ break;
+ case ETHTOOL_GPAUSEPARAM:// Get pause parameters
+ // SMSC_TRACE("ETHTOOL_GPAUSEPARAM");
+ result=-EOPNOTSUPP;
+ break;
+ case ETHTOOL_SPAUSEPARAM:// Set pause parameters.
+ // SMSC_TRACE("ETHTOOL_SPAUSEPARAM");
+ result=-EOPNOTSUPP;
+ break;
+ case ETHTOOL_GRXCSUM:// Get RX hw csum enable (ethtool_value)
+ // SMSC_TRACE("ETHTOOL_GRXCSUM");
+ {
+ struct ethtool_value rxCsum={ETHTOOL_GRXCSUM};
+ rxCsum.data=0;
+ if(copy_to_user(userAddr,&rxCsum,sizeof(rxCsum))) {
+ result=-EFAULT;
+ } else {
+ result=0;
+ }
+ }
+ break;
+ case ETHTOOL_SRXCSUM:// Set RX hw csum enable (ethtool_value)
+ // SMSC_TRACE("ETHTOOL_SRXCSUM");
+ result=-EOPNOTSUPP;
+ break;
+ case ETHTOOL_GTXCSUM:// Get TX hw csum enable (ethtool_value)
+ // SMSC_TRACE("ETHTOOL_GTXCSUM");
+ {
+ struct ethtool_value txCsum={ETHTOOL_GTXCSUM};
+ txCsum.data=0;
+ if(copy_to_user(userAddr,&txCsum,sizeof(txCsum))) {
+ result=-EFAULT;
+ } else {
+ result=0;
+ }
+ }
+ break;
+ case ETHTOOL_STXCSUM:// Set TX hw csum enable (ethtool_value)
+ // SMSC_TRACE("ETHTOOL_STXCSUM");
+ result=-EOPNOTSUPP;
+ break;
+ case ETHTOOL_GSG:// Get scatter-gather enable (ethtool_value)
+ // SMSC_TRACE("ETHTOOL_GSG");
+ {
+ struct ethtool_value sg={ETHTOOL_GSG};
+ sg.data=0;
+ if(copy_to_user(userAddr,&sg,sizeof(sg))) {
+ result=-EFAULT;
+ } else {
+ result=0;
+ }
+ }
+ break;
+ case ETHTOOL_SSG:// Set scatter-gather enable (ethtool_value).
+ // SMSC_TRACE("ETHTOOL_SSG");
+ result=-EOPNOTSUPP;
+ break;
+ case ETHTOOL_TEST:// execute NIC self-test.
+ // SMSC_TRACE("ETHTOOL_TEST");
+ result=-EOPNOTSUPP;
+ break;
+ case ETHTOOL_GSTRINGS:// get specified string set
+ // SMSC_TRACE("ETHTOOL_GSTRINGS");
+ result=-EOPNOTSUPP;
+ break;
+ case ETHTOOL_PHYS_ID:// identify the NIC
+ // SMSC_TRACE("ETHTOOL_PHYS_ID");
+ result=-EOPNOTSUPP;
+ break;
+ case ETHTOOL_GSTATS:// get NIC-specific statistics
+ // SMSC_TRACE("ETHTOOL_GSTATS");
+ result=-EOPNOTSUPP;
+ break;
+
+ case ETHTOOL_GTSO:// Get TSO enable (ethtool_value)
+ // SMSC_TRACE("ETHTOOL_GTSO");
+ {
+ struct ethtool_value tso={ETHTOOL_GTSO};
+ tso.data=0;
+ if(copy_to_user(userAddr,&tso,sizeof(tso))) {
+ result=-EFAULT;
+ } else {
+ result=0;
+ }
+ }
+ break;
+ case ETHTOOL_STSO:// Set TSO enable (ethtool_value)
+ // SMSC_TRACE("ETHTOOL_STS0");
+ result=-EOPNOTSUPP;
+ break;
+#endif
+ default:
+ // SMSC_WARNING("unknown ethcmd=0x%08X",ethcmd);
+ result=-EOPNOTSUPP;
+ break;
+ }
+DONE:
+ return result;
+}
+
+
+//returns time1-time2;
+TIME_SPAN Gpt_FreeRunCompare(u32 time1,u32 time2)
+{
+ return ((TIME_SPAN)(time1-time2));
+}
+void Gpt_ScheduleInterrupt(PPRIVATE_DATA privateData,TIME_SPAN timeSpan)
+{
+ u32 timerValue=0;
+ if(timeSpan<0) timeSpan=0;
+ timerValue=(u32)timeSpan;
+ if((timerValue%2500)>=1250) {
+ timerValue=(timerValue/2500)+1;
+ } else {
+ timerValue=(timerValue/2500);
+ }
+ if(timerValue>0x0000FFFFUL) {
+ timerValue=0x0000FFFF;
+ }
+ Lan_SetRegDW(GPT_CFG,(timerValue|GPT_CFG_TIMER_EN_));
+ Lan_SetRegDW(INT_STS,INT_STS_GPT_INT_);
+}
+
+void Gpt_CancelInterrupt(PPRIVATE_DATA privateData)
+{
+ Lan_SetRegDW(GPT_CFG,0UL);
+ Lan_SetRegDW(INT_STS,INT_STS_GPT_INT_);
+}
+
+void Gpt_ScheduleCallBack(
+ PPRIVATE_DATA privateData,
+ void (*callBackFunction)(PPRIVATE_DATA privateData),
+ u32 callBackTime)
+{
+ u32 slot_index=GPT_SCHEDULE_DEPTH;
+ bool result=false;
+ if((callBackFunction!=NULL)&&(callBackTime!=0)) {
+ unsigned long dwIntFlags=0;
+ SMSC_ASSERT(privateData!=NULL);
+ spin_lock_irqsave(&privateData->GpTimerLock,dwIntFlags);
+ {
+ u32 index=0;
+ u32 currentTime=Lan_GetRegDW(FREE_RUN);
+ TIME_SPAN nextCallTime=MAX_TIME_SPAN;
+ TIME_SPAN timeSpan=MAX_TIME_SPAN;
+ bool rescheduleRequired=false;
+ for(index=0;index<GPT_SCHEDULE_DEPTH;index++) {
+ if(privateData->GptFunction[index]==NULL) {
+ if(!result) {
+ result=true;
+ //lint -save
+ //lint -e611 //suspicious cast
+ privateData->GptFunction[index]=(void *)callBackFunction;
+ //lint -restore_
+ privateData->GptCallTime[index]=currentTime+(2500*callBackTime);
+ timeSpan=Gpt_FreeRunCompare(privateData->GptCallTime[index],currentTime);
+ if(nextCallTime>timeSpan) {
+ nextCallTime=timeSpan;
+ rescheduleRequired=true;
+ slot_index = index;
+ }
+ }
+ } else {
+ timeSpan=Gpt_FreeRunCompare(privateData->GptCallTime[index],currentTime);
+ if(nextCallTime>=timeSpan) {
+ nextCallTime=timeSpan;
+ rescheduleRequired=false;
+ }
+ }
+ }
+ if(rescheduleRequired) {
+ privateData->Gpt_scheduled_slot_index = slot_index;
+ Gpt_ScheduleInterrupt(privateData,nextCallTime);
+ }
+ }
+ spin_unlock_irqrestore(&(privateData->GpTimerLock),dwIntFlags);
+ }
+ if(!result) {
+ SMSC_WARNING("Gpt_ScheduleCallBack: Failed");
+ }
+}
+
+void Gpt_CancelCallBack(
+ PPRIVATE_DATA privateData,
+ void (*callBackFunction)(PPRIVATE_DATA privateData))
+{
+ bool result=false;
+ if(callBackFunction!=NULL) {
+ unsigned long dwIntFlags=0;
+ SMSC_ASSERT(privateData!=NULL);
+ spin_lock_irqsave(&(privateData->GpTimerLock),dwIntFlags);
+ {
+ u32 index=0;
+ u32 currentTime=Lan_GetRegDW(FREE_RUN);
+ TIME_SPAN nextCallTime=MAX_TIME_SPAN;
+ TIME_SPAN timeSpan=MAX_TIME_SPAN;
+ bool rescheduleRequired=false;
+ for(index=0;index<GPT_SCHEDULE_DEPTH;index++) {
+ if(privateData->GptFunction[index]==callBackFunction) {
+ result=true;
+ //lint -save
+ //lint -e611 //suspicious cast
+ privateData->GptFunction[index]=(void *)NULL;
+ // cancelled time will not need a
+ // re-scheduled
+
+ // re-scheduled is done at other
+ // non-null slots
+ }
+ else if(privateData->GptFunction[index]!=NULL) {
+ timeSpan=Gpt_FreeRunCompare(privateData->GptCallTime[index],currentTime);
+ // if this scheduled time is earlier
+ // than current scheduled time
+ // AND not a duplicated one
+ if(nextCallTime>=timeSpan && privateData->Gpt_scheduled_slot_index != index) {
+ nextCallTime=timeSpan;
+ rescheduleRequired=true;
+ privateData->Gpt_scheduled_slot_index = index;
+ }
+ }
+ }
+ if(rescheduleRequired) {
+ Gpt_ScheduleInterrupt(privateData,nextCallTime);
+ }
+ else if (privateData->Gpt_scheduled_slot_index==GPT_SCHEDULE_DEPTH) {
+ Gpt_CancelInterrupt(privateData);
+ }
+ }
+ spin_unlock_irqrestore(&(privateData->GpTimerLock),dwIntFlags);
+ }
+ if(!result) {
+ SMSC_WARNING("Gpt_CancelCallBack: Failed");
+ }
+}
+
+bool Gpt_HandleInterrupt(
+ PPRIVATE_DATA privateData,u32 dwIntSts)
+{
+ SMSC_ASSERT(privateData!=NULL);
+ if(dwIntSts&INT_STS_GPT_INT_)
+ {
+ unsigned long dwIntFlags=0;
+ Lan_SetRegDW(INT_STS,INT_STS_GPT_INT_);
+ spin_lock_irqsave(&(privateData->GpTimerLock),dwIntFlags);
+ {
+ u32 index=0;
+ u32 currentTime=Lan_GetRegDW(FREE_RUN);
+ TIME_SPAN timeSpan=MAX_TIME_SPAN;
+ TIME_SPAN nextCallTime=MAX_TIME_SPAN;
+ bool rescheduleRequired=false;
+ for(index=0;index<GPT_SCHEDULE_DEPTH;index++) {
+ if(privateData->GptFunction[index]!=NULL) {
+ timeSpan=Gpt_FreeRunCompare(privateData->GptCallTime[index],currentTime);
+ if(timeSpan<1250) {
+ void (*callBackFunction)(PPRIVATE_DATA privateData);
+ callBackFunction=privateData->GptFunction[index];
+ privateData->GptFunction[index]=NULL;
+ spin_unlock_irqrestore(&(privateData->GpTimerLock),dwIntFlags);
+ privateData->Gpt_scheduled_slot_index = GPT_SCHEDULE_DEPTH;
+ callBackFunction(privateData);
+ spin_lock_irqsave(&(privateData->GpTimerLock),dwIntFlags);
+ }
+ }
+ }
+ for(index=0;index<GPT_SCHEDULE_DEPTH;index++) {
+ if(privateData->GptFunction[index]!=NULL) {
+ rescheduleRequired=true;
+ timeSpan=Gpt_FreeRunCompare(privateData->GptCallTime[index],currentTime);
+ if(nextCallTime>timeSpan) {
+ nextCallTime=timeSpan;
+ privateData->Gpt_scheduled_slot_index = index;
+ }
+ }
+ }
+ if(rescheduleRequired) {
+ Gpt_ScheduleInterrupt(privateData,nextCallTime);
+ }
+ }
+ spin_unlock_irqrestore(&(privateData->GpTimerLock),dwIntFlags);
+ return true;
+ }
+ return false;
+}
+
+void GptCB_RxCompleteMulticast(PPRIVATE_DATA privateData)
+{
+ Rx_CompleteMulticastUpdate (privateData);
+}
+
+void GptCB_RestartBurst(PPRIVATE_DATA privateData)
+{
+ if(privateData->RxFlowControlActive) {
+ privateData->RxFlowBurstActive=true;
+ if(privateData->RxFlowBurstWorkLoad>privateData->RxFlowBurstMaxWorkLoad) {
+ privateData->RxFlowBurstWorkLoad-=privateData->RxFlowBurstMaxWorkLoad;
+ } else {
+ privateData->RxFlowBurstWorkLoad=0;
+ }
+ Gpt_ScheduleCallBack(privateData,GptCB_RestartBurst,
+ privateData->RxFlowParameters.BurstPeriod);
+ }
+ Lan_EnableInterrupt(privateData,privateData->RxInterrupts);
+}
+
+void GptCB_MeasureRxThroughput(PPRIVATE_DATA privateData)
+{
+ if(privateData->RxFlowMeasuredMaxThroughput<privateData->RxFlowCurrentThroughput) {
+ privateData->RxFlowMeasuredMaxThroughput=privateData->RxFlowCurrentThroughput;
+ }
+ if(privateData->RxFlowMeasuredMaxPacketCount<privateData->RxFlowCurrentPacketCount) {
+ privateData->RxFlowMeasuredMaxPacketCount=privateData->RxFlowCurrentPacketCount;
+ }
+ if(privateData->RxFlowCurrentThroughput!=0) {
+ if(privateData->RxFlowMaxWorkLoad!=0) {
+ if(!(privateData->RxFlowControlActive)) {
+ u32 activationLevel=
+ (privateData->RxFlowMaxWorkLoad*(100+RX_FLOW_ACTIVATION))/100;
+ if(privateData->RxFlowCurrentWorkLoad>=activationLevel) {
+ privateData->RxFlowControlActive=true;
+ privateData->RxFlowBurstActive=true;
+ privateData->RxFlowBurstWorkLoad=0;
+ Gpt_ScheduleCallBack(privateData,GptCB_RestartBurst,
+ privateData->RxFlowParameters.BurstPeriod);
+ //SET_GPIO(GP_TX);
+ }
+ } else {
+ u32 deactivationLevel=
+ (privateData->RxFlowMaxWorkLoad*(100-RX_FLOW_DEACTIVATION))/100;
+ if(privateData->RxFlowCurrentWorkLoad<=deactivationLevel) {
+ privateData->RxFlowControlActive=false;
+ //CLEAR_GPIO(GP_TX);
+ }
+ }
+ }
+ privateData->RxFlowCurrentThroughput=0;
+ privateData->RxFlowCurrentPacketCount=0;
+ privateData->RxFlowCurrentWorkLoad=0;
+ Gpt_ScheduleCallBack(privateData,GptCB_MeasureRxThroughput,1000);
+ } else {
+ if(privateData->RxFlowMaxWorkLoad!=0) {
+ if(privateData->RxFlowControlActive) {
+ privateData->RxFlowControlActive=false;
+ //CLEAR_GPIO(GP_TX);
+ }
+ }
+ privateData->MeasuringRxThroughput=false;
+ }
+}
+
+irqreturn_t Smsc9118_ISR(int Irq, void *dev_id)
+{
+ u32 dwIntCfg=0;
+ u32 dwIntSts=0;
+ u32 dwIntEn=0;
+ u32 dwIntBits=0;
+ PPRIVATE_DATA privateData=(PPRIVATE_DATA)dev_id;
+ bool serviced=false;
+
+ Irq=Irq;//make lint happy
+
+ if(privateData==NULL) {
+ SMSC_WARNING("Smsc9118_ISR(privateData==NULL)");
+ goto DONE;
+ }
+ if(privateData->dwLanBase==0) {
+ SMSC_WARNING("Smsc9118_ISR(dwLanBase==0)");
+ goto DONE;
+ }
+ SET_GPIO(GP_ISR);
+ dwIntCfg=Lan_GetRegDW(INT_CFG);
+ if((dwIntCfg&0x00001100)!=0x00001100) {
+ SMSC_TRACE("In ISR, not my interrupt, dwIntCfg=0x%08X",
+ dwIntCfg);
+ goto ALMOST_DONE;
+ }
+
+ {
+ /*
+ * KH: Neither is true for 9210
+ u32 reservedBits;
+ if(OLD_REGISTERS(privateData)) {
+ reservedBits=0x00FFEEEEUL;
+ } else {
+ reservedBits=0x00FFCEEEUL;
+ }
+ */
+ /*
+ reservedBits = 0x00FF8EEEUL;
+ if(dwIntCfg&reservedBits) {
+ SMSC_WARNING("In ISR, reserved bits are high.\n");
+ SMSC_TRACE("(reserved=0x%08X int=0x%08X)\n", reservedBits, dwIntCfg);
+ //this could mean surprise removal
+ goto ALMOST_DONE;
+ }
+ */
+ }
+
+ dwIntSts=Lan_GetRegDW(INT_STS);
+ dwIntEn=Lan_GetRegDW(INT_EN);
+ dwIntBits=dwIntSts&dwIntEn;
+ //SMSC_TRACE("dwIntBits= 0x%x8l \n", dwIntBits);
+ privateData->LastIntStatus3=privateData->LastIntStatus2;
+ privateData->LastIntStatus2=privateData->LastIntStatus1;
+ privateData->LastIntStatus1=dwIntBits;
+ if(Lan_HandleSoftwareInterrupt(privateData,dwIntBits)) {
+ serviced=true;
+ }
+ if(Gpt_HandleInterrupt(privateData,dwIntBits)) {
+ serviced=true;
+ }
+ if(Tx_HandleInterrupt(privateData,dwIntBits)) {
+ serviced=true;
+ }
+
+ if(RxStop_HandleInterrupt(privateData,dwIntBits)) {
+ serviced=true;
+ }
+
+
+ if(Rx_HandleInterrupt(privateData,dwIntBits)) {
+ serviced=true;
+ }
+
+ if(!serviced) {
+ SMSC_WARNING("unserviced interrupt dwIntCfg=0x%08X,dwIntSts=0x%08X,dwIntEn=0x%08X,dwIntBits=0x%08X",
+ dwIntCfg,dwIntSts,dwIntEn,dwIntBits);
+ }
+
+ALMOST_DONE:
+ CLEAR_GPIO(GP_ISR);
+DONE:
+ return IRQ_RETVAL(serviced);
+}
+
+#ifdef USE_PHY_WORK_AROUND
+bool Phy_Reset(PPRIVATE_DATA privateData,VL_KEY keyCode)
+{
+ bool result=false;
+ WORD wTemp=0;
+ u32 dwLoopCount=100000;
+ SMSC_TRACE("Performing PHY BCR Reset");
+ Phy_SetRegW(privateData,PHY_BCR,PHY_BCR_RESET_,keyCode);
+ do {
+ udelay(10);
+ wTemp=Phy_GetRegW(privateData,PHY_BCR,keyCode);
+ dwLoopCount--;
+ } while((dwLoopCount>0)&&(wTemp&PHY_BCR_RESET_));
+ if(wTemp&PHY_BCR_RESET_) {
+ SMSC_WARNING("Phy Reset failed to complete.");
+ goto DONE;
+ }
+ //extra delay required because the phy may not be completed with its reset
+ // when PHY_BCR_RESET_ is cleared.
+ // They say 256 uS is enough delay but I'm using 500 here to be safe
+ udelay(500);
+ result=true;
+DONE:
+ return result;
+}
+
+u32 Phy_LBT_GetTxStatus(PPRIVATE_DATA privateData)
+{
+ u32 result=Lan_GetRegDW(TX_FIFO_INF);
+ if(OLD_REGISTERS(privateData)) {
+ result&=TX_FIFO_INF_TSFREE_;
+ if(result!=0x00800000UL) {
+ result=Lan_GetRegDW(TX_STATUS_FIFO);
+ } else {
+ result=0;
+ }
+ } else {
+ result&=TX_FIFO_INF_TSUSED_;
+ if(result!=0x00000000UL) {
+ result=Lan_GetRegDW(TX_STATUS_FIFO);
+ } else {
+ result=0;
+ }
+ }
+ return result;
+}
+
+u32 Phy_LBT_GetRxStatus(PPRIVATE_DATA privateData)
+{
+ u32 result=Lan_GetRegDW(RX_FIFO_INF);
+ if(result&0x00FF0000UL) {
+ //Rx status is available, read it
+ result=Lan_GetRegDW(RX_STATUS_FIFO);
+ } else {
+ result=0;
+ }
+ return result;
+}
+
+bool Phy_TransmitTestPacket(PPRIVATE_DATA privateData)
+{
+ bool result=false;
+ u32 dwLoopCount=0;
+ u32 dwTxCmdA=0;
+ u32 dwTxCmdB=0;
+ u32 dwStatus=0;
+
+ //write Tx Packet to 118
+ dwTxCmdA=
+ ((((u32)(privateData->LoopBackTxPacket))&0x03UL)<<16) | //u32 alignment adjustment
+ TX_CMD_A_INT_FIRST_SEG_ | TX_CMD_A_INT_LAST_SEG_ |
+ ((u32)(MIN_PACKET_SIZE));
+ dwTxCmdB=
+ (((u32)(MIN_PACKET_SIZE))<<16) |
+ ((u32)(MIN_PACKET_SIZE));
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA);
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB);
+ Platform_WriteFifo(
+ privateData->dwLanBase,
+ (u32 *)(((u32)(privateData->LoopBackTxPacket))&0xFFFFFFFCUL),
+ (((u32)(MIN_PACKET_SIZE))+3+
+ (((u32)(privateData->LoopBackTxPacket))&0x03UL))>>2);
+
+ //wait till transmit is done
+ dwLoopCount=60;
+ while((dwLoopCount>0)&&((dwStatus=Phy_LBT_GetTxStatus(privateData))==0)) {
+ udelay(5);
+ dwLoopCount--;
+ }
+ if(dwStatus==0) {
+ SMSC_WARNING("Failed to Transmit during Packet Test");
+ goto DONE;
+ }
+ if(dwStatus&0x00008000UL) {
+ SMSC_WARNING("Transmit encountered errors during Packet Test");
+ goto DONE;
+ }
+DONE:
+ return result;
+}
+
+bool Phy_CheckLoopBackPacket(PPRIVATE_DATA privateData)
+
+{
+ bool result=false;
+ u32 tryCount=0;
+ u32 dwLoopCount=0;
+ for(tryCount=0;tryCount<10;tryCount++)
+ {
+ u32 dwTxCmdA=0;
+ u32 dwTxCmdB=0;
+ u32 dwStatus=0;
+ u32 dwPacketLength=0;
+
+ //zero-out Rx Packet memory
+ memset(privateData->LoopBackRxPacket,0,MIN_PACKET_SIZE);
+
+ //write Tx Packet to 118
+ dwTxCmdA=
+ ((((u32)(privateData->LoopBackTxPacket))&0x03UL)<<16) | //u32 alignment adjustment
+ TX_CMD_A_INT_FIRST_SEG_ | TX_CMD_A_INT_LAST_SEG_ |
+ ((u32)(MIN_PACKET_SIZE));
+ dwTxCmdB=
+ (((u32)(MIN_PACKET_SIZE))<<16) |
+ ((u32)(MIN_PACKET_SIZE));
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA);
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB);
+ Platform_WriteFifo(
+ privateData->dwLanBase,
+ (u32 *)(((u32)(privateData->LoopBackTxPacket))&0xFFFFFFFCUL),
+ (((u32)(MIN_PACKET_SIZE))+3+
+ (((u32)(privateData->LoopBackTxPacket))&0x03UL))>>2);
+
+ //wait till transmit is done
+ dwLoopCount=60;
+ while((dwLoopCount>0)&&((dwStatus=Phy_LBT_GetTxStatus(privateData))==0)) {
+ udelay(5);
+ dwLoopCount--;
+ }
+ if(dwStatus==0) {
+ SMSC_WARNING("Failed to Transmit during Loop Back Test");
+ continue;
+ }
+ if(dwStatus&0x00008000UL) {
+ SMSC_WARNING("Transmit encountered errors during Loop Back Test");
+ continue;
+ }
+
+ //wait till receive is done
+ dwLoopCount=60;
+ while((dwLoopCount>0)&&((dwStatus=Phy_LBT_GetRxStatus(privateData))==0))
+ {
+ udelay(5);
+ dwLoopCount--;
+ }
+ if(dwStatus==0) {
+ SMSC_WARNING("Failed to Receive during Loop Back Test");
+ continue;
+ }
+ if(dwStatus&RX_STS_ES_)
+ {
+ SMSC_WARNING("Receive encountered errors during Loop Back Test");
+ continue;
+ }
+
+ dwPacketLength=((dwStatus&0x3FFF0000UL)>>16);
+
+ Platform_ReadFifo(
+ privateData->dwLanBase,
+ ((u32 *)(privateData->LoopBackRxPacket)),
+ (dwPacketLength+3+(((u32)(privateData->LoopBackRxPacket))&0x03UL))>>2);
+
+ if(dwPacketLength!=(MIN_PACKET_SIZE+4)) {
+ SMSC_WARNING("Unexpected packet size during loop back test, size=%d, will retry",dwPacketLength);
+ } else {
+ u32 byteIndex=0;
+ bool foundMissMatch=false;
+ for(byteIndex=0;byteIndex<MIN_PACKET_SIZE;byteIndex++) {
+ if(privateData->LoopBackTxPacket[byteIndex]!=privateData->LoopBackRxPacket[byteIndex])
+ {
+ foundMissMatch=true;
+ break;
+ }
+ }
+ if(!foundMissMatch) {
+ SMSC_TRACE("Successfully Verified Loop Back Packet");
+ result=true;
+ goto DONE;
+ } else {
+ SMSC_WARNING("Data miss match during loop back test, will retry.");
+ }
+ }
+ }
+DONE:
+ return result;
+}
+
+bool Phy_LoopBackTest(PPRIVATE_DATA privateData)
+{
+ bool result=false;
+ u32 byteIndex=0;
+ u32 tryCount=0;
+ // u32 failed=0;
+ //Initialize Tx Packet
+ for(byteIndex=0;byteIndex<6;byteIndex++) {
+ //use broadcast destination address
+ privateData->LoopBackTxPacket[byteIndex]=(BYTE)0xFF;
+ }
+ for(byteIndex=6;byteIndex<12;byteIndex++) {
+ //use incrementing source address
+ privateData->LoopBackTxPacket[byteIndex]=(BYTE)byteIndex;
+ }
+ //Set length type field
+ privateData->LoopBackTxPacket[12]=0x00;
+ privateData->LoopBackTxPacket[13]=0x00;
+ for(byteIndex=14;byteIndex<MIN_PACKET_SIZE;byteIndex++)
+ {
+ privateData->LoopBackTxPacket[byteIndex]=(BYTE)byteIndex;
+ }
+ //TRY_AGAIN:
+ {
+ u32 dwRegVal=Lan_GetRegDW(HW_CFG);
+ dwRegVal&=(HW_CFG_TX_FIF_SZ_|0x00000FFFUL);
+ dwRegVal|=HW_CFG_SF_;
+ Lan_SetRegDW(HW_CFG,dwRegVal);
+ }
+ Lan_SetRegDW(TX_CFG,TX_CFG_TX_ON_);
+
+ Lan_SetRegDW(RX_CFG,(((u32)(privateData->LoopBackRxPacket))&0x03)<<8);
+
+ {
+
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ //Set Phy to 10/FD, no ANEG,
+ Phy_SetRegW(privateData,PHY_BCR,0x0100,keyCode);
+
+ //enable MAC Tx/Rx, FD
+ Mac_SetRegDW(privateData,MAC_CR,MAC_CR_FDPX_|MAC_CR_TXEN_|MAC_CR_RXEN_,keyCode);
+
+ // Phy_TransmitTestPacket(privateData);
+
+ //set Phy to loopback mode
+ Phy_SetRegW(privateData,PHY_BCR,0x4100,keyCode);
+
+ for(tryCount=0;tryCount<10;tryCount++) {
+ if(Phy_CheckLoopBackPacket(privateData))
+ {
+ result=true;
+ goto DONE;
+ }
+ privateData->dwResetCount++;
+ //disable MAC rx
+ Mac_SetRegDW(privateData,MAC_CR,0UL,keyCode);
+ Phy_Reset(privateData,keyCode);
+
+ //Set Phy to 10/FD, no ANEG, and Loopbackmode
+ Phy_SetRegW(privateData,PHY_BCR,0x4100,keyCode);
+
+ //enable MAC Tx/Rx, FD
+ Mac_SetRegDW(privateData,MAC_CR,MAC_CR_FDPX_|MAC_CR_TXEN_|MAC_CR_RXEN_,keyCode);
+ }
+ // if(failed<2) {
+ // if(tryCount>=10) {
+ // u32 timeOut=10000;
+ // Lan_ShowRegs(privateData);
+ // SMSC_TRACE("Performing full reset");
+ // privateData->Lan9118->HW_CFG=HW_CFG_SRST_;
+ // while((timeOut>0)&&(privateData->Lan9118->HW_CFG&HW_CFG_SRST_)) {
+ // udelay(1);
+ // timeOut--;
+ // }
+ // failed++;
+ // goto TRY_AGAIN;
+ // }
+ // }
+DONE:
+ //disable MAC
+ Mac_SetRegDW(privateData,MAC_CR,0UL,keyCode);
+ //Cancel Phy loopback mode
+ Phy_SetRegW(privateData,PHY_BCR,0U,keyCode);
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ }
+
+ Lan_SetRegDW(TX_CFG,0UL);
+ Lan_SetRegDW(RX_CFG,0UL);
+
+ return result;
+}
+
+#endif //USE_PHY_WORK_AROUND
+void Phy_SetLink(PPRIVATE_DATA privateData,
+ u32 dwLinkRequest)
+{
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ if(dwLinkRequest&LINK_AUTO_NEGOTIATE) {
+ WORD wTemp;
+ wTemp=Phy_GetRegW(privateData,
+ PHY_ANEG_ADV,keyCode);
+ wTemp&=~PHY_ANEG_ADV_PAUSE_;
+ if(dwLinkRequest&LINK_ASYMMETRIC_PAUSE) {
+ wTemp|=PHY_ANEG_ADV_ASYMP_;
+ }
+ if(dwLinkRequest&LINK_SYMMETRIC_PAUSE) {
+ wTemp|=PHY_ANEG_ADV_SYMP_;
+ }
+ wTemp&=~PHY_ANEG_ADV_SPEED_;
+ if(dwLinkRequest&LINK_SPEED_10HD) {
+ wTemp|=PHY_ANEG_ADV_10H_;
+ }
+ if(dwLinkRequest&LINK_SPEED_10FD) {
+ wTemp|=PHY_ANEG_ADV_10F_;
+ }
+ if(dwLinkRequest&LINK_SPEED_100HD) {
+ wTemp|=PHY_ANEG_ADV_100H_;
+ }
+ if(dwLinkRequest&LINK_SPEED_100FD) {
+ wTemp|=PHY_ANEG_ADV_100F_;
+ }
+ Phy_SetRegW(privateData,PHY_ANEG_ADV,wTemp,keyCode);
+
+ // begin to establish link
+ privateData->dwRemoteFaultCount=0;
+ Phy_SetRegW(privateData,
+ PHY_BCR,
+ PHY_BCR_AUTO_NEG_ENABLE_|
+ PHY_BCR_RESTART_AUTO_NEG_,
+ keyCode);
+ } else {
+ WORD wTemp=0;
+ if(dwLinkRequest&(LINK_SPEED_100FD)) {
+ dwLinkRequest=LINK_SPEED_100FD;
+ } else if(dwLinkRequest&(LINK_SPEED_100HD)) {
+ dwLinkRequest=LINK_SPEED_100HD;
+ } else if(dwLinkRequest&(LINK_SPEED_10FD)) {
+ dwLinkRequest=LINK_SPEED_10FD;
+ } else if(dwLinkRequest&(LINK_SPEED_10HD)) {
+ dwLinkRequest=LINK_SPEED_10HD;
+ }
+ if(dwLinkRequest&(LINK_SPEED_10FD|LINK_SPEED_100FD)) {
+ wTemp|=PHY_BCR_DUPLEX_MODE_;
+ }
+ if(dwLinkRequest&(LINK_SPEED_100HD|LINK_SPEED_100FD)) {
+ wTemp|=PHY_BCR_SPEED_SELECT_;
+ }
+ Phy_SetRegW(privateData,PHY_BCR,wTemp,keyCode);
+ }
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+}
+
+void Phy_SetAutoMdixSts(PPRIVATE_DATA privateData,
+ WORD wAutoMdixSts)
+{
+ WORD SpecialCtrlSts=0U;
+
+ if (((privateData->dwGeneration)>2) && (!(privateData->ExtPhy)))
+ {
+ if (wAutoMdixSts > 2)
+ {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ SpecialCtrlSts=Phy_GetRegW(privateData, SPECIAL_CTRL_STS,keyCode);
+ SpecialCtrlSts = (SpecialCtrlSts&0x1FFF);
+ Phy_SetRegW(privateData, SPECIAL_CTRL_STS,SpecialCtrlSts,keyCode);
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+
+ if (Lan_GetRegDW(HW_CFG) & HW_CFG_AMDIX_EN_STRAP_STS_) {
+ SMSC_TRACE("Auto-MDIX Enable by default!!!");
+ }
+ else {
+ SMSC_TRACE("Auto-MDIX Disable by default!!!");
+ }
+ }
+ else
+ {
+
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ SpecialCtrlSts=Phy_GetRegW(privateData, SPECIAL_CTRL_STS,keyCode);
+ SpecialCtrlSts = (((wAutoMdixSts+4) << 13) | (SpecialCtrlSts&0x1FFF));
+ Phy_SetRegW(privateData, SPECIAL_CTRL_STS,SpecialCtrlSts,keyCode);
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+
+ if (wAutoMdixSts & AMDIX_ENABLE) {
+ SMSC_TRACE("Override Strap, Enable Auto-MDIX ");
+ } else if (wAutoMdixSts & AMDIX_DISABLE_CROSSOVER) {
+ SMSC_TRACE("Override Strap, Disable Auto-MDIX, CrossOver Cable");
+ } else {
+ SMSC_TRACE("Override Strap, Disable Auto-MDIX, Straight Cable");
+ }
+
+ }
+ }
+
+ else {
+ SMSC_TRACE("This chip or PHY doesn't support HP AMDIX!!!");
+ }
+
+}
+
+void Phy_GetAutoMdixSts(PPRIVATE_DATA privateData)
+{
+
+
+ WORD SpecialCtrlSts=0U;
+ unsigned long dwIntFlags=0;
+
+ if (((privateData->dwGeneration)>2) && (!(privateData->ExtPhy)))
+ {
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ SpecialCtrlSts=Phy_GetRegW(privateData, SPECIAL_CTRL_STS,keyCode);
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+
+ if (SpecialCtrlSts & SPECIAL_CTRL_STS_OVRRD_AMDIX_) {
+
+ if (SpecialCtrlSts & SPECIAL_CTRL_STS_AMDIX_ENABLE_) {
+ SMSC_TRACE("AutoMdix Status: Override Strap, Enable Auto Mdix");
+ }
+ else if (SpecialCtrlSts & SPECIAL_CTRL_STS_AMDIX_STATE_) {
+
+ SMSC_TRACE("AutoMdix Status: Override Strap, Disable Auto Mdix, CrossOver Cable");
+
+ } else {
+
+ SMSC_TRACE("AutoMdix Status: Override Strap, Disable Auto Mdix, Straight Cable");
+ }
+
+ }
+ else {
+ if (Lan_GetRegDW(HW_CFG) & HW_CFG_AMDIX_EN_STRAP_STS_) {
+ SMSC_TRACE("AutoMdix Status: Enable by default!!!");
+ }
+ else {
+ SMSC_TRACE("AutoMdix Status: Disable by default!!!");
+ }
+ }
+
+ }
+ else {
+ SMSC_TRACE("This chip or PHY doesn't support HP AMDIX!!!");
+ }
+
+}
+
+
+bool Phy_Initialize(
+ PPRIVATE_DATA privateData,
+ u32 dwPhyAddr,
+ u32 dwLinkRequest)
+{
+ bool result=false;
+ u32 dwTemp=0;
+ WORD wTemp=0;
+ u32 dwLoopCount=0;
+ // WORD SpecialCtrlSts=0U;
+
+ SMSC_TRACE("-->Phy_Initialize");
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(privateData->dwLanBase!=0);
+ SMSC_ASSERT(dwLinkRequest<=0x7FUL);
+ privateData->ExtPhy=false;
+
+ if(dwPhyAddr!=0xFFFFFFFFUL) {
+ switch(privateData->dwIdRev&0xFFFF0000) {
+ case 0x117A0000UL:
+ case 0x115A0000UL:
+ goto EXTERNAL_PHY_SUPPORTED;
+ case 0x01170000UL:
+ case 0x01150000UL:
+ if(privateData->dwIdRev&0x0000FFFF) {
+ u32 dwHwCfg=0;
+EXTERNAL_PHY_SUPPORTED:
+ dwHwCfg=Lan_GetRegDW(HW_CFG);
+ if(dwHwCfg&HW_CFG_EXT_PHY_DET_) {
+ //External phy is requested, supported, and detected
+ //Attempt to switch
+ //NOTE: Assuming Rx and Tx are stopped
+ // because Phy_Initialize is called before
+ // Rx_Initialize and Tx_Initialize
+ WORD wPhyId1=0;
+ WORD wPhyId2=0;
+
+ //Disable phy clocks to the mac
+ dwHwCfg&= (~HW_CFG_PHY_CLK_SEL_);
+ dwHwCfg|= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
+ Lan_SetRegDW(HW_CFG,dwHwCfg);
+ udelay(10);//wait for clocks to acutally stop
+
+ dwHwCfg|=HW_CFG_EXT_PHY_EN_;
+ Lan_SetRegDW(HW_CFG,dwHwCfg);
+
+ dwHwCfg&= (~HW_CFG_PHY_CLK_SEL_);
+ dwHwCfg|= HW_CFG_PHY_CLK_SEL_EXT_PHY_;
+ Lan_SetRegDW(HW_CFG,dwHwCfg);
+ udelay(10);//wait for clocks to actually start
+
+ dwHwCfg|=HW_CFG_SMI_SEL_;
+ Lan_SetRegDW(HW_CFG,dwHwCfg);
+
+ {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ if(dwPhyAddr<=31) {
+ //only check the phy address specified
+ privateData->dwPhyAddress=dwPhyAddr;
+ wPhyId1=Phy_GetRegW(privateData,PHY_ID_1,keyCode);
+ wPhyId2=Phy_GetRegW(privateData,PHY_ID_2,keyCode);
+ } else {
+ //auto detect phy
+ u32 address=0;
+ for(address=0;address<=31;address++) {
+ privateData->dwPhyAddress=address;
+ wPhyId1=Phy_GetRegW(privateData,PHY_ID_1,keyCode);
+ wPhyId2=Phy_GetRegW(privateData,PHY_ID_2,keyCode);
+ if((wPhyId1!=0xFFFFU)||(wPhyId2!=0xFFFFU)) {
+ SMSC_TRACE("Detected Phy at address = 0x%02X = %d",
+ address,address);
+ break;
+ }
+ }
+ if(address>=32) {
+ SMSC_WARNING("Failed to auto detect external phy");
+ }
+ }
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ }
+ if((wPhyId1==0xFFFFU)&&(wPhyId2==0xFFFFU)) {
+ SMSC_WARNING("External Phy is not accessable");
+ SMSC_WARNING(" using internal phy instead");
+ //revert back to interal phy settings.
+
+ //Disable phy clocks to the mac
+ dwHwCfg&= (~HW_CFG_PHY_CLK_SEL_);
+ dwHwCfg|= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
+ Lan_SetRegDW(HW_CFG,dwHwCfg);
+ udelay(10);//wait for clocks to actually stop
+
+ dwHwCfg&=(~HW_CFG_EXT_PHY_EN_);
+ Lan_SetRegDW(HW_CFG,dwHwCfg);
+
+ dwHwCfg&= (~HW_CFG_PHY_CLK_SEL_);
+ dwHwCfg|= HW_CFG_PHY_CLK_SEL_INT_PHY_;
+ Lan_SetRegDW(HW_CFG,dwHwCfg);
+ udelay(10);//wait for clocks to actually start
+
+ dwHwCfg&=(~HW_CFG_SMI_SEL_);
+ Lan_SetRegDW(HW_CFG,dwHwCfg);
+ goto USE_INTERNAL_PHY;
+ } else {
+ SMSC_TRACE("Successfully switched to external phy");
+ privateData->ExtPhy=true;
+#ifdef USE_LED1_WORK_AROUND
+ privateData->NotUsingExtPhy=0;
+#endif
+ }
+ } else {
+ SMSC_WARNING("No External Phy Detected");
+ SMSC_WARNING(" using internal phy instead");
+ goto USE_INTERNAL_PHY;
+ }
+ } else {
+ SMSC_WARNING("External Phy is not supported");
+ SMSC_WARNING(" using internal phy instead");
+ goto USE_INTERNAL_PHY;
+ };break;
+ default:
+ SMSC_WARNING("External Phy is not supported");
+ SMSC_WARNING(" using internal phy instead");
+ goto USE_INTERNAL_PHY;
+ }
+ } else {
+USE_INTERNAL_PHY:
+
+ privateData->dwPhyAddress=1;
+ privateData->ExtPhy=false;
+#ifdef USE_LED1_WORK_AROUND
+ if(privateData->dwGeneration<=2) {
+ privateData->NotUsingExtPhy=1;
+ } else {
+ //Generation 3 or higher has the LED problem fixed
+ // to disable the workaround pretend the phy is external
+ privateData->NotUsingExtPhy=0;
+ }
+#endif
+
+ Phy_SetAutoMdixSts(privateData,AutoMdix);
+ }
+
+ {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ dwTemp=Phy_GetRegW(privateData,PHY_ID_2,keyCode);
+ privateData->bPhyRev=((BYTE)(dwTemp&(0x0FUL)));
+ privateData->bPhyModel=((BYTE)((dwTemp>>4)&(0x3FUL)));
+ privateData->dwPhyId=((dwTemp&(0xFC00UL))<<8);
+ dwTemp=Phy_GetRegW(privateData,PHY_ID_1,keyCode);
+ privateData->dwPhyId|=((dwTemp&(0x0000FFFFUL))<<2);
+
+ SMSC_TRACE("dwPhyId==0x%08X,bPhyModel==0x%02X,bPhyRev==0x%02X",
+ privateData->dwPhyId,
+ privateData->bPhyModel,
+ privateData->bPhyRev);
+
+ privateData->dwLinkSpeed=LINK_OFF;
+ privateData->dwLinkSettings=LINK_OFF;
+ //reset the PHY
+ Phy_SetRegW(privateData,PHY_BCR,PHY_BCR_RESET_,keyCode);
+ dwLoopCount=100000;
+ do {
+
+ udelay(10);
+ wTemp=Phy_GetRegW(privateData,PHY_BCR,keyCode);
+ dwLoopCount--;
+ } while((dwLoopCount>0) && (wTemp&PHY_BCR_RESET_));
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ }
+
+ if(wTemp&PHY_BCR_RESET_) {
+ SMSC_WARNING("PHY reset failed to complete.");
+ goto DONE;
+ }
+
+#ifdef USE_PHY_WORK_AROUND
+ if(privateData->dwGeneration<=2) {
+ // printk("phy_LoopBackTest\n");
+ if(!Phy_LoopBackTest(privateData)) {
+ SMSC_WARNING("Failed Loop back test");
+ goto DONE;
+ } else {
+ SMSC_TRACE("Passed Loop Back Test");
+ }
+ }
+#endif
+
+ Phy_SetLink(privateData,dwLinkRequest);
+
+ init_timer(&(privateData->LinkPollingTimer));
+ privateData->LinkPollingTimer.function=Phy_CheckLink;
+ privateData->LinkPollingTimer.data=(unsigned long)privateData;
+ privateData->LinkPollingTimer.expires=jiffies+HZ;
+ add_timer(&(privateData->LinkPollingTimer));
+
+ result=true;
+DONE:
+ SMSC_TRACE("<--Phy_Initialize, result=%s",result?"true":"false");
+ return result;
+}
+
+WORD Phy_GetRegW(
+ PPRIVATE_DATA privateData,
+ u32 dwRegIndex,
+ VL_KEY keyCode)
+{
+ u32 dwAddr=0;
+ int i=0;
+ WORD result=0xFFFFU;
+
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(privateData->LanInitialized==true);
+ SMSC_ASSERT(Vl_CheckLock(&(privateData->MacPhyLock),keyCode));
+
+ // confirm MII not busy
+ if ((Mac_GetRegDW(privateData, MII_ACC,keyCode) & MII_ACC_MII_BUSY_) != 0UL)
+ {
+ SMSC_WARNING("MII is busy in Phy_GetRegW???");
+ result=0;
+ goto DONE;
+ }
+
+ // set the address, index & direction (read from PHY)
+ dwAddr = ((privateData->dwPhyAddress&0x1FUL)<<11) | ((dwRegIndex & 0x1FUL)<<6);
+ Mac_SetRegDW(privateData, MII_ACC, dwAddr,keyCode);
+
+ // wait for read to complete w/ timeout
+ for(i=0;i<100;i++) {
+ // see if MII is finished yet
+ if ((Mac_GetRegDW(privateData, MII_ACC,keyCode) & MII_ACC_MII_BUSY_) == 0UL)
+ {
+ // get the read data from the MAC & return i
+ result=((WORD)Mac_GetRegDW(privateData, MII_DATA,keyCode));
+ goto DONE;
+ }
+ }
+ SMSC_WARNING("timeout waiting for MII write to finish");
+
+DONE:
+ return result;
+}
+
+void Phy_SetRegW(
+ PPRIVATE_DATA privateData,
+ u32 dwRegIndex,WORD wVal,
+ VL_KEY keyCode)
+{
+ u32 dwAddr=0;
+ int i=0;
+
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(privateData->LanInitialized==true);
+
+ SMSC_ASSERT(Vl_CheckLock(&(privateData->MacPhyLock),keyCode));
+
+ if(dwRegIndex==0) {
+ if((wVal&0x1200)==0x1200) {
+ privateData->wLastADVatRestart=privateData->wLastADV;
+ }
+ }
+ if(dwRegIndex==4) {
+ privateData->wLastADV=wVal;
+ }
+
+ // confirm MII not busy
+ if ((Mac_GetRegDW(privateData, MII_ACC,keyCode) & MII_ACC_MII_BUSY_) != 0UL)
+ {
+ SMSC_WARNING("MII is busy in Phy_SetRegW???");
+ goto DONE;
+ }
+
+ // put the data to write in the MAC
+ Mac_SetRegDW(privateData, MII_DATA, (u32)wVal,keyCode);
+
+ // set the address, index & direction (write to PHY)
+ dwAddr = ((privateData->dwPhyAddress&0x1FUL)<<11) | ((dwRegIndex & 0x1FUL)<<6) | MII_ACC_MII_WRITE_;
+ Mac_SetRegDW(privateData, MII_ACC, dwAddr,keyCode);
+
+ // wait for write to complete w/ timeout
+ for(i=0;i<100;i++) {
+ // see if MII is finished yet
+ if ((Mac_GetRegDW(privateData, MII_ACC,keyCode) & MII_ACC_MII_BUSY_) == 0UL)
+ {
+ goto DONE;
+ }
+ }
+ SMSC_WARNING("timeout waiting for MII write to finish");
+DONE:
+ return;
+}
+
+void Phy_UpdateLinkMode(PPRIVATE_DATA privateData)
+{
+ u32 dwOldLinkSpeed=privateData->dwLinkSpeed;
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+
+ Phy_GetLinkMode(privateData,keyCode);
+
+ if(dwOldLinkSpeed!=(privateData->dwLinkSpeed)) {
+ if(privateData->dwLinkSpeed!=LINK_OFF) {
+ u32 dwRegVal=0;
+ switch(privateData->dwLinkSpeed) {
+ case LINK_SPEED_10HD:
+ SMSC_TRACE("Link is now UP at 10Mbps HD");
+ break;
+ case LINK_SPEED_10FD:
+ SMSC_TRACE("Link is now UP at 10Mbps FD");
+ break;
+ case LINK_SPEED_100HD:
+ SMSC_TRACE("Link is now UP at 100Mbps HD");
+ break;
+ case LINK_SPEED_100FD:
+ SMSC_TRACE("Link is now UP at 100Mbps FD");
+ break;
+ default:
+ SMSC_WARNING("Link is now UP at Unknown Link Speed, dwLinkSpeed=0x%08X",
+ privateData->dwLinkSpeed);
+ break;
+ }
+
+ dwRegVal=Mac_GetRegDW(privateData,MAC_CR,keyCode);
+ dwRegVal&=~(MAC_CR_FDPX_|MAC_CR_RCVOWN_);
+ switch(privateData->dwLinkSpeed) {
+ case LINK_SPEED_10HD:
+ case LINK_SPEED_100HD:
+ dwRegVal|=MAC_CR_RCVOWN_;
+ break;
+ case LINK_SPEED_10FD:
+ case LINK_SPEED_100FD:
+ dwRegVal|=MAC_CR_FDPX_;
+ break;
+ default:break;//make lint happy
+ }
+
+ Mac_SetRegDW(privateData,
+ MAC_CR,dwRegVal,keyCode);
+
+ if(privateData->dwLinkSettings&LINK_AUTO_NEGOTIATE) {
+ WORD linkPartner=0;
+ WORD localLink=0;
+ localLink=Phy_GetRegW(privateData,4,keyCode);
+ linkPartner=Phy_GetRegW(privateData,5,keyCode);
+ switch(privateData->dwLinkSpeed) {
+ case LINK_SPEED_10FD:
+ case LINK_SPEED_100FD:
+ if(((localLink&linkPartner)&((WORD)0x0400U)) != ((WORD)0U)) {
+ //Enable PAUSE receive and transmit
+ Mac_SetRegDW(privateData,FLOW,0xFFFF0002UL,keyCode);
+ Lan_SetBitsDW(AFC_CFG,(afc_cfg&0x0000000FUL));
+ } else if(((localLink&((WORD)0x0C00U))==((WORD)0x0C00U)) &&
+ ((linkPartner&((WORD)0x0C00U))==((WORD)0x0800U)))
+ {
+ //Enable PAUSE receive, disable PAUSE transmit
+ Mac_SetRegDW(privateData,FLOW,0xFFFF0002UL,keyCode);
+ Lan_ClrBitsDW(AFC_CFG,0x0000000FUL);
+ } else {
+ //Disable PAUSE receive and transmit
+ Mac_SetRegDW(privateData,FLOW,0UL,keyCode);
+ Lan_ClrBitsDW(AFC_CFG,0x0000000FUL);
+ };break;
+ case LINK_SPEED_10HD:
+ case LINK_SPEED_100HD:
+ Mac_SetRegDW(privateData,FLOW,0UL,keyCode);
+ Lan_SetBitsDW(AFC_CFG,0x0000000FUL);
+ break;
+ default:break;//make lint happy
+ }
+ SMSC_TRACE("LAN9118: %s,%s,%s,%s,%s,%s",
+ (localLink&PHY_ANEG_ADV_ASYMP_)?"ASYMP":" ",
+ (localLink&PHY_ANEG_ADV_SYMP_)?"SYMP ":" ",
+ (localLink&PHY_ANEG_ADV_100F_)?"100FD":" ",
+ (localLink&PHY_ANEG_ADV_100H_)?"100HD":" ",
+ (localLink&PHY_ANEG_ADV_10F_)?"10FD ":" ",
+ (localLink&PHY_ANEG_ADV_10H_)?"10HD ":" ");
+
+ SMSC_TRACE("Partner: %s,%s,%s,%s,%s,%s",
+ (linkPartner&PHY_ANEG_LPA_ASYMP_)?"ASYMP":" ",
+ (linkPartner&PHY_ANEG_LPA_SYMP_)?"SYMP ":" ",
+ (linkPartner&PHY_ANEG_LPA_100FDX_)?"100FD":" ",
+ (linkPartner&PHY_ANEG_LPA_100HDX_)?"100HD":" ",
+ (linkPartner&PHY_ANEG_LPA_10FDX_)?"10FD ":" ",
+ (linkPartner&PHY_ANEG_LPA_10HDX_)?"10HD ":" ");
+ } else {
+ switch(privateData->dwLinkSpeed) {
+ case LINK_SPEED_10HD:
+ case LINK_SPEED_100HD:
+ Mac_SetRegDW(privateData,FLOW,0x0UL,keyCode);
+ Lan_SetBitsDW(AFC_CFG,0x0000000FUL);
+ break;
+ default:
+ Mac_SetRegDW(privateData,FLOW,0x0UL,keyCode);
+ Lan_ClrBitsDW(AFC_CFG,0x0000000FUL);
+ break;
+ }
+ }
+ netif_carrier_on(privateData->dev);
+ Tx_WakeQueue(privateData,0x01);
+#ifdef USE_LED1_WORK_AROUND
+ if ((g_GpioSettingOriginal & GPIO_CFG_LED1_EN_) &&
+ privateData->NotUsingExtPhy)
+ {
+ // Restore orginal GPIO configuration
+ g_GpioSetting = g_GpioSettingOriginal;
+ Lan_SetRegDW(GPIO_CFG,g_GpioSetting);
+ }
+#endif // USE_LED1_WORK_AROUND
+ } else {
+ SMSC_TRACE("Link is now DOWN");
+ Tx_StopQueue(privateData,0x01);
+ netif_carrier_off(privateData->dev);
+ Mac_SetRegDW(privateData,FLOW,0UL,keyCode);
+ Lan_ClrBitsDW(AFC_CFG,0x0000000FUL);
+#ifdef USE_LED1_WORK_AROUND
+ // Check global setting that LED1 usage is 10/100 indicator
+ // g_GpioSetting = Lan_GetRegDW(GPIO_CFG);
+ if ((g_GpioSetting & GPIO_CFG_LED1_EN_) &&
+ privateData->NotUsingExtPhy)
+ {
+ //Force 10/100 LED off, after saving orginal GPIO configuration
+ g_GpioSettingOriginal = g_GpioSetting;
+
+ g_GpioSetting &= ~GPIO_CFG_LED1_EN_;
+ g_GpioSetting |=
+ (GPIO_CFG_GPIOBUF0_|GPIO_CFG_GPIODIR0_|GPIO_CFG_GPIOD0_);
+ Lan_SetRegDW(GPIO_CFG,g_GpioSetting);
+ }
+#endif // USE_LED1_WORK_AROUND
+ }
+ }
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+}
+
+void Phy_CheckLink(unsigned long ptr)
+{
+ PPRIVATE_DATA privateData=(PPRIVATE_DATA)ptr;
+ if(privateData==NULL) {
+ SMSC_WARNING("Phy_CheckLink(ptr==0)");
+ return;
+ }
+
+ //must call this twice
+ Phy_UpdateLinkMode(privateData);
+ Phy_UpdateLinkMode(privateData);
+
+ if(!(privateData->StopLinkPolling)) {
+ privateData->LinkPollingTimer.expires=jiffies+HZ;
+ add_timer(&(privateData->LinkPollingTimer));
+ }
+}
+
+void Phy_GetLinkMode(
+ PPRIVATE_DATA privateData,
+ VL_KEY keyCode)
+{
+ u32 result=LINK_OFF;
+ WORD wRegVal=0;
+ WORD wRegBSR=Phy_GetRegW(
+ privateData,
+ PHY_BSR,keyCode);
+ privateData->dwLinkSettings=LINK_OFF;
+ if(wRegBSR&PHY_BSR_LINK_STATUS_) {
+ wRegVal=Phy_GetRegW(
+ privateData,
+ PHY_BCR,keyCode);
+ if(wRegVal&PHY_BCR_AUTO_NEG_ENABLE_) {
+ u32 linkSettings=LINK_AUTO_NEGOTIATE;
+ WORD wRegADV=privateData->wLastADVatRestart;
+ // Phy_GetRegW(
+ // privateData,
+ // PHY_ANEG_ADV,keyCode);
+ WORD wRegLPA=Phy_GetRegW(
+ privateData,
+ PHY_ANEG_LPA,keyCode);
+ if(wRegADV&PHY_ANEG_ADV_ASYMP_) {
+ linkSettings|=LINK_ASYMMETRIC_PAUSE;
+ }
+ if(wRegADV&PHY_ANEG_ADV_SYMP_) {
+ linkSettings|=LINK_SYMMETRIC_PAUSE;
+ }
+ if(wRegADV&PHY_ANEG_LPA_100FDX_) {
+ linkSettings|=LINK_SPEED_100FD;
+ }
+ if(wRegADV&PHY_ANEG_LPA_100HDX_) {
+ linkSettings|=LINK_SPEED_100HD;
+ }
+ if(wRegADV&PHY_ANEG_LPA_10FDX_) {
+ linkSettings|=LINK_SPEED_10FD;
+ }
+ if(wRegADV&PHY_ANEG_LPA_10HDX_) {
+ linkSettings|=LINK_SPEED_10HD;
+ }
+ privateData->dwLinkSettings=linkSettings;
+ wRegLPA&=wRegADV;
+ if(wRegLPA&PHY_ANEG_LPA_100FDX_) {
+ result=LINK_SPEED_100FD;
+ } else if(wRegLPA&PHY_ANEG_LPA_100HDX_) {
+ result=LINK_SPEED_100HD;
+ } else if(wRegLPA&PHY_ANEG_LPA_10FDX_) {
+ result=LINK_SPEED_10FD;
+ } else if(wRegLPA&PHY_ANEG_LPA_10HDX_) {
+ result=LINK_SPEED_10HD;
+ }
+ } else {
+ if(wRegVal&PHY_BCR_SPEED_SELECT_) {
+ if(wRegVal&PHY_BCR_DUPLEX_MODE_) {
+ privateData->dwLinkSettings=result=LINK_SPEED_100FD;
+ } else {
+ privateData->dwLinkSettings=result=LINK_SPEED_100HD;
+ }
+ } else {
+ if(wRegVal&PHY_BCR_DUPLEX_MODE_) {
+ privateData->dwLinkSettings=result=LINK_SPEED_10FD;
+ } else {
+ privateData->dwLinkSettings=result=LINK_SPEED_10HD;
+ }
+ }
+ }
+ }
+ privateData->dwLinkSpeed=result;
+}
+
+extern int smsc_prom_get_ethernet_mac_addr(char *addr);
+
+bool Mac_Initialize(PPRIVATE_DATA privateData)
+{
+ unsigned char ea[6];
+ int result;
+ u32 dwHigh16, dwLow32;
+ unsigned long dwIntFlags = 0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+
+ SMSC_ASSERT(privateData!=NULL);
+#ifdef CONFIG_MIPS_HMP10
+ result = smsc_prom_get_ethernet_mac_addr(ea);
+#else
+ result = prom_get_ethernet_addr(ea);
+#endif
+
+ if (result == 0) {
+ SMSC_TRACE("Got ethernet addr %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X from prom\n",
+ ea[0], ea[1], ea[2], ea[3], ea[4], ea[5] );
+ dwHigh16 = (ea[5] << 8) | ea[4];
+ Mac_SetRegDW(privateData, ADDRH, dwHigh16, keyCode);
+ dwLow32 = (ea[3] << 24) | (ea[2] << 16) | (ea[1] << 8) | ea[0];
+ Mac_SetRegDW(privateData, ADDRL, dwLow32, keyCode);
+ } else {
+ SMSC_TRACE("Failed to get ethernet addr from prom\n");
+ return result;
+ }
+
+ return true;
+}
+
+static bool MacNotBusy(PPRIVATE_DATA privateData, VL_KEY keyCode)
+{
+ int i=0;
+ SMSC_ASSERT(Vl_CheckLock(&(privateData->MacPhyLock),keyCode));
+ // wait for MAC not busy, w/ timeout
+ for(i=0;i<40;i++)
+ {
+ if((Lan_GetRegDW(MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY_)==(0UL)) {
+ return true;
+ }
+ }
+ SMSC_WARNING("timeout waiting for MAC not BUSY. MAC_CSR_CMD = 0x%08X",
+ Lan_GetRegDW(MAC_CSR_CMD));
+ return false;
+}
+
+u32 Mac_GetRegDW(PPRIVATE_DATA privateData,u32 dwRegOffset,VL_KEY keyCode)
+{
+ u32 result=0xFFFFFFFFUL;
+ u32 dwTemp=0;
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(privateData->LanInitialized==true);
+ SMSC_ASSERT(Vl_CheckLock(&(privateData->MacPhyLock),keyCode));
+ SMSC_ASSERT(privateData->dwLanBase!=0);
+
+ // wait until not busy
+ if (Lan_GetRegDW(MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY_)
+ {
+ SMSC_WARNING("Mac_GetRegDW() failed, MAC already busy at entry");
+ goto DONE;
+ }
+
+ // send the MAC Cmd w/ offset
+ Lan_SetRegDW(MAC_CSR_CMD,
+ ((dwRegOffset & 0x000000FFUL) |
+ MAC_CSR_CMD_CSR_BUSY_ | MAC_CSR_CMD_R_NOT_W_));
+ dwTemp=Lan_GetRegDW(BYTE_TEST);//to flush previous write
+ dwTemp=dwTemp;
+
+ // wait for the read to happen, w/ timeout
+ if (!MacNotBusy(privateData,keyCode))
+ {
+ SMSC_WARNING("Mac_GetRegDW() failed, waiting for MAC not busy after read");
+ goto DONE;
+ } else {
+ // finally, return the read data
+ result=Lan_GetRegDW(MAC_CSR_DATA);
+ }
+DONE:
+ return result;
+}
+
+void Mac_SetRegDW(PPRIVATE_DATA privateData,u32 dwRegOffset,u32 dwVal,VL_KEY keyCode)
+{
+ u32 dwTemp=0;
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(privateData->LanInitialized==true);
+ SMSC_ASSERT(Vl_CheckLock(&(privateData->MacPhyLock),keyCode));
+ SMSC_ASSERT(privateData->dwLanBase!=0);
+
+ if (Lan_GetRegDW(MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY_)
+ {
+ SMSC_WARNING("Mac_SetRegDW() failed, MAC already busy at entry");
+ goto DONE;
+ }
+
+ // send the data to write
+ Lan_SetRegDW(MAC_CSR_DATA,dwVal);
+
+ // do the actual write
+ Lan_SetRegDW(MAC_CSR_CMD,((dwRegOffset & 0x000000FFUL) | MAC_CSR_CMD_CSR_BUSY_));
+ dwTemp=Lan_GetRegDW(BYTE_TEST);//force flush of previous write
+ dwTemp=dwTemp;
+
+ // wait for the write to complete, w/ timeout
+ if (!MacNotBusy(privateData,keyCode))
+ {
+ SMSC_WARNING("Mac_SetRegDW() failed, waiting for MAC not busy after write");
+ }
+DONE:
+ return;
+}
+
+#define TX_FIFO_LOW_THRESHOLD (1600)
+//#define Tx_Max_Fragments (86) //every fragment needs 6 bytes overhead. 1514+2(alignment)+6*86=2032 < 2036
+
+void Tx_Initialize(
+ PPRIVATE_DATA privateData,
+ u32 dwTxDmaCh,
+ u32 dwDmaThreshold)
+{
+ u32 dwRegVal=0;
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(privateData->dwLanBase!=0);
+
+ dwRegVal=Lan_GetRegDW(HW_CFG);
+ dwRegVal&=(HW_CFG_TX_FIF_SZ_|0x00000FFFUL);
+ dwRegVal|=HW_CFG_SF_;
+ Lan_SetRegDW(HW_CFG,dwRegVal);
+
+
+ if(privateData->UseTxCsum)
+
+ //Set TX COE
+ {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ u32 dwCoeCr=Mac_GetRegDW(privateData,COE_CR,keyCode);
+ dwCoeCr|=(TX_COE_EN);
+ Mac_SetRegDW(privateData,COE_CR,dwCoeCr,keyCode);
+ //printk("COE_CR = 0x%08x\n", Mac_GetRegDW(privateData,COE_CR,keyCode));
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+
+ }
+
+
+
+ Lan_SetTDFL(privateData,0xFF);
+ Lan_EnableInterrupt(privateData,INT_EN_TDFA_EN_);
+
+ privateData->dwTxDmaThreshold=dwDmaThreshold;
+ privateData->dwTxDmaCh=dwTxDmaCh;
+ if(dwTxDmaCh>=TRANSFER_PIO) {
+ SMSC_TRACE("Tx will use PIO");
+ } else {
+ SMSC_TRACE("Tx will use DMA channel %d",dwTxDmaCh);
+ SMSC_ASSERT(Platform_IsValidDmaChannel(dwTxDmaCh));
+ if(!Platform_DmaInitialize(
+ &(privateData->PlatformData),
+ dwTxDmaCh))
+ {
+ SMSC_WARNING("Failed Platform_DmaInitialize, dwTxDmaCh=%u",dwTxDmaCh);
+ }
+ privateData->TxDmaXfer.dwLanReg=privateData->dwLanBase+TX_DATA_FIFO;
+ privateData->TxDmaXfer.pdwBuf=NULL;//this will be reset per dma request
+ privateData->TxDmaXfer.dwDmaCh=privateData->dwTxDmaCh;
+ privateData->TxDmaXfer.dwDwCnt=0;//this will be reset per dma request
+ privateData->TxDmaXfer.fMemWr=false;
+ }
+
+ {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ u32 dwMacCr=Mac_GetRegDW(privateData,MAC_CR,keyCode);
+ dwMacCr|=(MAC_CR_TXEN_|MAC_CR_HBDIS_);
+ Mac_SetRegDW(privateData,MAC_CR,dwMacCr,keyCode);
+ Lan_SetRegDW(TX_CFG,TX_CFG_TX_ON_);
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ }
+
+ privateData->TxSkb=NULL;
+ spin_lock_init(&(privateData->TxSkbLock));
+ privateData->dwTxQueueDisableMask=0;
+ spin_lock_init(&(privateData->TxQueueLock));
+ spin_lock_init(&(privateData->TxCounterLock));
+ privateData->TxInitialized=true;
+
+}
+
+bool Tx_HandleInterrupt(
+ PPRIVATE_DATA privateData,u32 dwIntSts)
+{
+ SMSC_ASSERT(privateData!=NULL);
+ if(dwIntSts&INT_STS_TDFA_)
+ {
+ Lan_SetTDFL(privateData,0xFF);
+ Lan_SetRegDW(INT_STS,INT_STS_TDFA_);
+ Tx_WakeQueue(privateData,0x02UL);
+ return true;
+ }
+ return false;
+}
+
+void Tx_StopQueue(
+ PPRIVATE_DATA privateData,u32 dwSource)
+{
+ unsigned long intFlags=0;
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(privateData->dev!=NULL);
+ SMSC_ASSERT(privateData->TxInitialized);
+ spin_lock_irqsave(&(privateData->TxQueueLock),intFlags);
+ if(privateData->dwTxQueueDisableMask==0) {
+ netif_stop_queue(privateData->dev);
+ }
+ privateData->dwTxQueueDisableMask|=dwSource;
+ spin_unlock_irqrestore(&(privateData->TxQueueLock),intFlags);
+}
+
+void Tx_WakeQueue(
+ PPRIVATE_DATA privateData,u32 dwSource)
+{
+ unsigned long intFlags=0;
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(privateData->dev!=NULL);
+ SMSC_ASSERT(privateData->TxInitialized);
+ spin_lock_irqsave(&(privateData->TxQueueLock),intFlags);
+ privateData->dwTxQueueDisableMask&=(~dwSource);
+ if(privateData->dwTxQueueDisableMask==0) {
+ netif_wake_queue(privateData->dev);
+ }
+ spin_unlock_irqrestore(&(privateData->TxQueueLock),intFlags);
+}
+
+static u32 Tx_GetTxStatusCount(
+ PPRIVATE_DATA privateData)
+{
+ u32 result=0;
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(privateData->dwLanBase!=0);
+ result=Lan_GetRegDW(TX_FIFO_INF);
+ if(OLD_REGISTERS(privateData)) {
+ result&=TX_FIFO_INF_TSFREE_;
+ result>>=16;
+ if(result>0x80) {
+ SMSC_WARNING("TX_FIFO_INF_TSFREE_>0x80");
+ result=0x80;
+ }
+ result=0x80-result;
+ } else {
+ result&=TX_FIFO_INF_TSUSED_;
+ result>>=16;
+ }
+ return result;
+}
+
+
+
+void Tx_SendSkb(
+ PPRIVATE_DATA privateData,
+ struct sk_buff *skb)
+{
+ u32 dwFreeSpace=0;
+ unsigned int i=0, TxFrag=0, skbFragCnt = skb_shinfo(skb)->nr_frags + 1;
+
+ //if (skbFragCnt>1)
+ // printk("skbFrsgCnt = (%d)\n", skbFragCnt);
+
+ // if (privateData->UseTxCsum) {
+ int Chsum_start_offset=0;
+ u32 dwTxCsumPreamble=0;
+ // }
+
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(privateData->dwLanBase!=0);
+
+ if(privateData->UseTxCsum){
+
+ if(skb->ip_summed == CHECKSUM_PARTIAL)
+ {
+ //printk("ip summed!\n");
+ TxFrag = skbFragCnt + 1;
+ }
+ else
+ {
+ TxFrag = skbFragCnt;
+ }
+ }
+ else {
+ TxFrag = skbFragCnt;
+ }
+ TxFrag = skbFragCnt;
+
+
+ if (privateData->UseTxCsum) {
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ {
+ CalculateTxChecksumOffset(
+ skb,
+ &Chsum_start_offset);
+
+
+ dwTxCsumPreamble=(((WORD) (Chsum_start_offset + skb->csum)) << 16) | ((WORD) Chsum_start_offset);
+
+
+ }
+ }
+
+ //printk("Tx skb->len = 0x%08x\n", (u32) skb->len);
+
+
+
+ if(privateData->dwTxDmaCh>=TRANSFER_PIO)
+ {
+ //Use PIO only
+
+ //printk("Tx using pio only\n");
+
+ u32 dwTxCmdA=0;
+ u32 dwTxCmdB=0;
+
+
+ dwFreeSpace=Lan_GetRegDW(TX_FIFO_INF);
+ dwFreeSpace&=TX_FIFO_INF_TDFREE_;
+ if(dwFreeSpace<TX_FIFO_LOW_THRESHOLD) {
+ SMSC_WARNING("Tx Data Fifo Low, space available = %d",dwFreeSpace);
+ }
+
+
+ if(privateData->UseTxCsum) {
+ if(skb->ip_summed == CHECKSUM_PARTIAL)
+ {
+
+
+ dwTxCmdA=TX_CMD_A_INT_FIRST_SEG_ |((u32)sizeof(u32)) ;
+
+ dwTxCmdB=
+ (((u32)(skb->len+4))<<16) | TX_CMD_B_CSUM_ENABLE |
+ (((u32)(skb->len+4)&0x7FFUL));
+
+
+
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA);
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB);
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCsumPreamble);
+
+
+ }
+ }
+
+ if (skbFragCnt == 1){
+
+ if(skb->ip_summed == CHECKSUM_PARTIAL){
+
+ dwTxCmdA =((((u32)(skb->data))&0x03UL)<<16) |
+ TX_CMD_A_INT_LAST_SEG_|((u32)(skb->len));
+ dwTxCmdB=
+ (((u32)(skb->len+4))<<16) |
+ (((u32)(skb->len+4)&0x7FFUL));
+
+
+
+ }
+ else{
+ dwTxCmdA =
+ ((((u32)(skb->data))&0x03UL)<<16) | TX_CMD_A_INT_FIRST_SEG_ |
+ TX_CMD_A_INT_LAST_SEG_|((u32)(skb->len));
+
+ dwTxCmdB=
+ (((u32)(skb->len))<<16) |
+ (((u32)(skb->len)&0x7FFUL));
+
+ }
+
+ //printk("dwTxCmdA = 0x%08x\n", (u32) dwTxCmdA);
+ //printk("dwTxCmdB = 0x%08x\n", (u32) dwTxCmdB);
+
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA);
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB);
+
+
+ // rkdump(skb->data, skb->len);
+ Platform_WriteFifo(
+ privateData->dwLanBase,
+ (u32 *)(((u32)(skb->data))&0xFFFFFFFCUL),
+ (((u32)(skb->len))+3+
+ (((u32)(skb->data))&0x03UL))>>2);
+
+ //printk("Tx skb->len2 = 0x%08x\n", (u32) skb->len);
+ dwFreeSpace-=(skb->len+32);
+ dev_kfree_skb(skb);
+
+ }
+
+ else {
+
+ if(skb->ip_summed == CHECKSUM_PARTIAL){
+ dwTxCmdA =
+ ((((u32)(skb->data))&0x03UL)<<16) | //u32 alignment adjustment
+ ((u32)((skb->len)-(skb->data_len)));
+ dwTxCmdB=
+ (((u32)(skb->len+4))<<16) |
+ (((u32)(skb->len+4)&0x7FFUL));
+
+ }
+ else{
+ dwTxCmdA =
+ ((((u32)(skb->data))&0x03UL)<<16) |TX_CMD_A_INT_FIRST_SEG_ |
+ ((u32)((skb->len)-(skb->data_len)));
+ dwTxCmdB=
+ (((u32)(skb->len))<<16) |
+ (((u32)(skb->len)&0x7FFUL));
+
+ }
+ //printk("first frag. \n");
+ //rkdump(skb->data, ((skb->len)-(skb->data_len)));
+
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA);
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB);
+
+
+ Platform_WriteFifo(
+ privateData->dwLanBase,
+ (u32 *)(((u32)(skb->data))&0xFFFFFFFCUL),
+ (((u32)((skb->len)-(skb->data_len)))+3+
+ (((u32)(skb->data))&0x03UL))>>2);
+ // dwFreeSpace-=((skb->len-skb->data_len)+32);
+
+
+ for(i=1;i<skbFragCnt;i++)
+ {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1];
+ void *frag_addr = page_address(frag->page) + frag->page_offset;
+
+
+ dwTxCmdA=
+ ((((u32)(frag_addr))&0x03UL)<<16) | //u32 alignment adjustment
+ ((u32)(frag->size));
+
+
+ if (i==(skbFragCnt-1)){
+ dwTxCmdA |= TX_CMD_A_INT_LAST_SEG_ ;
+ }
+
+ if(skb->ip_summed == CHECKSUM_PARTIAL) {
+ dwTxCmdB=
+ (((u32)(skb->len+4))<<16) |
+ (((u32)(skb->len+4)&0x7FFUL));
+ }
+ else {
+ dwTxCmdB=
+ (((u32)(skb->len))<<16) |
+ (((u32)(skb->len)&0x7FFUL));
+ }
+
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA);
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB);
+ //printk("i= %d\n", i);
+ //rkdump(frag_addr, frag->size);
+
+ Platform_WriteFifo(
+ privateData->dwLanBase,
+ (u32 *)(((u32)(frag_addr))&0xFFFFFFFCUL),
+ (((u32)(frag->size))+3+
+ (((u32)(frag_addr))&0x03UL))>>2);
+ // dwFreeSpace-=(frag->size+8);
+
+ }
+
+ dwFreeSpace-=skb->len+12*TxFrag+16;
+ dev_kfree_skb(skb);
+ }
+
+ }
+
+
+ else
+ {
+ //Use DMA and PIO
+
+ //printk("Tx using dma!!\n");
+
+ u32 dwDmaCh=privateData->dwTxDmaCh;
+ PPLATFORM_DATA platformData=&(privateData->PlatformData);
+ SMSC_ASSERT(TX_FIFO_LOW_THRESHOLD>(skb->len+32));
+
+ if(((skb->len)>=(privateData->dwTxDmaThreshold)) && (skbFragCnt == 1))
+ {
+
+ //printk("Tx using dma!!\n");
+ // if(privateData->UseTxCsum) {
+ u32 dwTxCmdA1=0;
+ u32 dwTxCmdB1=0;
+ // }
+ u32 dwTxCmdA=0;
+ u32 dwTxCmdB=0;
+
+
+ if(privateData->UseTxCsum) {
+ if(skb->ip_summed == CHECKSUM_PARTIAL)
+
+ {
+
+
+ dwTxCmdA1=
+ TX_CMD_A_INT_FIRST_SEG_ |
+ ((u32)sizeof(u32));//buffer length
+
+ dwTxCmdB1=
+ (((u32)(skb->len+4))<<16) |TX_CMD_B_CSUM_ENABLE |
+ (((u32)(skb->len+4)&0x7FFUL));
+
+
+ }
+ }
+
+ if (skbFragCnt == 1){
+
+ dwTxCmdA =
+#if (PLATFORM_CACHE_LINE_BYTES == 16)
+ (0x01UL<<24)|//16 byte end alignment
+#endif
+#if (PLATFORM_CACHE_LINE_BYTES == 32)
+ (0x02UL<<24)|//32 byte end alignment
+#endif
+ ((((u32)(skb->data))&(PLATFORM_CACHE_LINE_BYTES-1))<<16) |//16 Byte start alignment
+ TX_CMD_A_INT_LAST_SEG_ |
+ ((u32)(skb->len));//buffer length
+
+ if(skb->ip_summed != CHECKSUM_PARTIAL)
+ dwTxCmdA |= TX_CMD_A_INT_FIRST_SEG_;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL){
+ dwTxCmdB=
+ (((u32)(skb->len+4))<<16) |
+ (((u32)(skb->len+4)&0x7FFUL));
+ }
+ else{
+ dwTxCmdB=
+ (((u32)(skb->len))<<16) |
+ ((u32)(skb->len));
+ }
+
+
+
+ privateData->TxDmaXfer.pdwBuf=
+ (u32 *)(((u32)(skb->data))&
+ (~(PLATFORM_CACHE_LINE_BYTES-1)));
+ privateData->TxDmaXfer.dwDwCnt=
+ ((((u32)(skb->len))+
+ (PLATFORM_CACHE_LINE_BYTES-1)+
+ (((u32)(skb->data))&
+ (PLATFORM_CACHE_LINE_BYTES-1)))&
+ (~(PLATFORM_CACHE_LINE_BYTES-1)))>>2;
+ Platform_CachePurge(
+ platformData,
+ privateData->TxDmaXfer.pdwBuf,
+ (privateData->TxDmaXfer.dwDwCnt)<<2);
+
+ spin_lock(&(privateData->TxSkbLock));
+ {
+ if(privateData->TxSkb)
+ Platform_DmaComplete(platformData,dwDmaCh);
+
+ dwFreeSpace=Lan_GetRegDW(TX_FIFO_INF);
+ dwFreeSpace&=TX_FIFO_INF_TDFREE_;
+ if(dwFreeSpace<TX_FIFO_LOW_THRESHOLD) {
+ SMSC_WARNING("Tx DATA FIFO LOW, space available = %d",dwFreeSpace);
+ }
+
+ if (privateData->UseTxCsum) {
+ if(skb->ip_summed == CHECKSUM_PARTIAL)
+
+ {
+
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA1);
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB1);
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCsumPreamble);
+ }
+ }
+
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA);
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB);
+ if(!Platform_DmaStartXfer(platformData,&(privateData->TxDmaXfer)))
+ {
+ SMSC_WARNING("Failed Platform_DmaStartXfer");
+ }
+
+ dwFreeSpace-=(skb->len+32);
+ if(privateData->TxSkb)
+ dev_kfree_skb(privateData->TxSkb);
+
+ privateData->TxSkb=skb;
+ }
+ spin_unlock(&(privateData->TxSkbLock));
+
+ }
+
+
+
+ }
+
+ else
+ {
+
+
+ //Use PIO
+
+ //printk("skb->len (%d)\n", skb->len);
+ //printk("Tx using pio\n");
+
+ u32 dwTxCmdA=0;
+ u32 dwTxCmdB=0;
+
+
+ spin_lock(&(privateData->TxSkbLock));
+ if(privateData->TxSkb) {
+ Platform_DmaComplete(platformData,dwDmaCh);
+ dev_kfree_skb(privateData->TxSkb);
+ privateData->TxSkb=NULL;
+ }
+ spin_unlock(&(privateData->TxSkbLock));
+
+
+ dwFreeSpace=Lan_GetRegDW(TX_FIFO_INF);
+ dwFreeSpace&=TX_FIFO_INF_TDFREE_;
+ if(dwFreeSpace<TX_FIFO_LOW_THRESHOLD) {
+ SMSC_WARNING("Tx Data Fifo Low, space available = %d",dwFreeSpace);
+ }
+
+ if(privateData->UseTxCsum) {
+
+ if(skb->ip_summed == CHECKSUM_PARTIAL)
+ {
+
+ dwTxCmdA=TX_CMD_A_INT_FIRST_SEG_ |((u32)sizeof(u32)) ;
+
+
+ dwTxCmdB=
+ (((u32)(skb->len+4))<<16) |TX_CMD_B_CSUM_ENABLE |
+ (((u32)(skb->len+4)&0x7FFUL));
+
+
+ //printk("dwTxCmdA = 0x%08x\n", (u32) dwTxCmdA);
+ //printk("dwTxCmdB = 0x%08x\n", (u32) dwTxCmdB);
+ //printk("dwTxCsumPreamble = 0x%08x\n", (u32) dwTxCsumPreamble);
+
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA);
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB);
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCsumPreamble);
+
+ }
+
+ }
+
+ if (skbFragCnt == 1){
+
+ if(skb->ip_summed == CHECKSUM_PARTIAL){
+ dwTxCmdA =
+ ((((u32)(skb->data))&0x03UL)<<16) | //u32 alignment adjustment
+ TX_CMD_A_INT_LAST_SEG_|((u32)(skb->len));
+ dwTxCmdB=
+ (((u32)(skb->len+4))<<16) |
+ (((u32)(skb->len+4)&0x7FFUL));
+
+ }
+ else{
+ dwTxCmdA =
+ ((((u32)(skb->data))&0x03UL)<<16) | TX_CMD_A_INT_FIRST_SEG_ |
+ TX_CMD_A_INT_LAST_SEG_|((u32)(skb->len));
+ dwTxCmdB=
+ (((u32)(skb->len))<<16) |
+ ((u32)(skb->len));
+
+ }
+
+
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA);
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB);
+
+ Platform_WriteFifo(
+ privateData->dwLanBase,
+ (u32 *)(((u32)(skb->data))&0xFFFFFFFCUL),
+ (((u32)(skb->len))+3+
+ (((u32)(skb->data))&0x03UL))>>2);
+ dwFreeSpace-=(skb->len+32);
+ dev_kfree_skb(skb);
+ }
+
+ else {
+
+ if(skb->ip_summed == CHECKSUM_PARTIAL){
+ dwTxCmdA =
+ ((((u32)(skb->data))&0x03UL)<<16) | //u32 alignment adjustment
+ ((u32)((skb->len)-(skb->data_len)));
+
+ dwTxCmdB=
+ (((u32)(skb->len+4))<<16) |
+ (((u32)(skb->len+4)&0x7FFUL));
+ }
+ else{
+ dwTxCmdA =
+ ((((u32)(skb->data))&0x03UL)<<16) |TX_CMD_A_INT_FIRST_SEG_ | //u32 alignment adjustment
+ ((u32)((skb->len)-(skb->data_len)));
+
+ dwTxCmdB=
+ (((u32)(skb->len))<<16) |
+ (((u32)(skb->len)&0x7FFUL));
+
+ }
+
+ //printk("first frag. \n");
+ //rkdump(skb->data, ((skb->len)-(skb->data_len)));
+
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA);
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB);
+
+ Platform_WriteFifo(
+ privateData->dwLanBase,
+ (u32 *)(((u32)(skb->data))&0xFFFFFFFCUL),
+ (((u32)((skb->len)-(skb->data_len)))+3+
+ (((u32)(skb->data))&0x03UL))>>2);
+ // dwFreeSpace-=((skb->len-skb->data_len)+32);
+
+
+ for(i=1;i<skbFragCnt;i++)
+ {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1];
+ void *frag_addr = page_address(frag->page) + frag->page_offset;
+
+
+ dwTxCmdA=
+ ((((u32)(frag_addr))&0x03UL)<<16) | //u32 alignment adjustment
+ ((u32)(frag->size));
+
+
+ if (i==(skbFragCnt-1)){
+ dwTxCmdA |= TX_CMD_A_INT_LAST_SEG_ ;
+ }
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL){
+ dwTxCmdB=
+ (((u32)(skb->len+4))<<16) |
+ (((u32)(skb->len+4)&0x7FFUL));
+ }
+ else{
+ dwTxCmdB=
+ (((u32)(skb->len))<<16) |
+ ((u32)(skb->len));
+ }
+
+ //printk("i= %d\n", i);
+ //rkdump(frag_addr, frag->size);
+
+
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA);
+ Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB);
+
+ Platform_WriteFifo(
+ privateData->dwLanBase,
+ (u32 *)(((u32)(frag_addr))&0xFFFFFFFCUL),
+ (((u32)(frag->size))+3+
+ (((u32)(frag_addr))&0x03UL))>>2);
+ // dwFreeSpace-=(frag->size+8);
+
+ }
+
+ dwFreeSpace-=skb->len+12*TxFrag+16;
+ dev_kfree_skb(skb);
+
+ }
+
+ }
+
+ }
+
+ //printk("finish.\n");
+
+
+ if(Tx_GetTxStatusCount(privateData)>=30)
+ {
+ Tx_UpdateTxCounters(privateData);
+ }
+
+ if(dwFreeSpace<TX_FIFO_LOW_THRESHOLD) {
+ Tx_StopQueue(privateData,0x02UL);
+ Lan_SetTDFL(privateData,0x32);
+ }
+
+
+}
+
+
+
+
+
+
+
+
+
+static u32 Tx_CompleteTx(
+ PPRIVATE_DATA privateData)
+{
+ u32 result=0;
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(privateData->dwLanBase!=0);
+ SMSC_ASSERT(privateData->TxInitialized==true);
+ result=Lan_GetRegDW(TX_FIFO_INF);
+ if(OLD_REGISTERS(privateData)) {
+ result&=TX_FIFO_INF_TSFREE_;
+ if(result!=0x00800000UL) {
+ result=Lan_GetRegDW(TX_STATUS_FIFO);
+ } else {
+ result=0;
+ }
+ } else {
+ result&=TX_FIFO_INF_TSUSED_;
+ if(result!=0x00000000UL) {
+ result=Lan_GetRegDW(TX_STATUS_FIFO);
+ } else {
+ result=0;
+ }
+ }
+ return result;
+}
+
+void Tx_UpdateTxCounters(
+ PPRIVATE_DATA privateData)
+{
+
+ u32 dwTxStatus=0;
+ SMSC_ASSERT(privateData!=NULL);
+ spin_lock(&(privateData->TxCounterLock));
+ while((dwTxStatus=Tx_CompleteTx(privateData))!=0)
+ {
+ if(dwTxStatus&0x80000000UL) {
+ SMSC_WARNING("Packet tag reserved bit is high");
+ privateData->stats.tx_errors++;
+ } else if(dwTxStatus&0x00007080UL) {
+ SMSC_WARNING("Tx Status reserved bits are high");
+ privateData->stats.tx_errors++;
+ } else {
+ if(dwTxStatus&0x00008000UL) {
+ privateData->stats.tx_errors++;
+ } else {
+ privateData->stats.tx_packets++;
+ privateData->stats.tx_bytes+=(dwTxStatus>>16);
+ }
+ if(dwTxStatus&0x00000100UL) {
+ privateData->stats.collisions+=16;
+ privateData->stats.tx_aborted_errors+=1;
+ } else {
+ privateData->stats.collisions+=
+ ((dwTxStatus>>3)&0xFUL);
+ }
+ if(dwTxStatus&0x00000800UL) {
+ privateData->stats.tx_carrier_errors+=1;
+ }
+ if(dwTxStatus&0x00000200UL) {
+ privateData->stats.collisions++;
+ privateData->stats.tx_aborted_errors++;
+ }
+ }
+ }
+ spin_unlock(&(privateData->TxCounterLock));
+}
+
+void Tx_CompleteDma(
+ PPRIVATE_DATA privateData)
+{
+ SMSC_ASSERT(privateData!=NULL);
+
+ spin_lock(&(privateData->TxSkbLock));
+ if(privateData->TxSkb) {
+ Platform_DmaComplete(
+ &(privateData->PlatformData),
+ privateData->dwTxDmaCh);
+ dev_kfree_skb(privateData->TxSkb);
+ privateData->TxSkb=NULL;
+ }
+ spin_unlock(&(privateData->TxSkbLock));
+}
+
+
+void CalculateTxChecksumOffset(
+ struct sk_buff *skb,
+ int *csum_start_offset
+ )
+{
+ unsigned int skbFragCnt;
+
+ skbFragCnt = skb_shinfo(skb)->nr_frags + 1;
+ *csum_start_offset = skb->csum_offset;
+}
+
+
+
+
+void rkdump(unsigned char *p, unsigned short len)
+{
+ int i;
+
+ for(i=0; i<len; i++)
+ {
+ if (i%16 == 0)
+ {
+ printk("\n0x%08x: ", (u32) (p+i));
+ }
+
+ printk("%02x ", *(p+i));
+ }
+
+ printk("\n");
+}
+
+
+
+void Rx_Initialize(
+ PPRIVATE_DATA privateData,
+ u32 dwRxDmaCh,
+ u32 dwDmaThreshold)
+{
+ SMSC_ASSERT(privateData!=NULL);
+
+ privateData->dwRxDmaCh=dwRxDmaCh;
+ if(dwRxDmaCh>=TRANSFER_PIO) {
+ SMSC_TRACE("Rx will use PIO");
+ Platform_GetFlowControlParameters(
+ &(privateData->PlatformData),
+ &(privateData->RxFlowParameters),
+ false);
+ } else {
+ SMSC_TRACE("Rx will use DMA Channel %d",dwRxDmaCh);
+ SMSC_ASSERT(Platform_IsValidDmaChannel(dwRxDmaCh));
+ if(!Platform_DmaInitialize(
+ &(privateData->PlatformData),
+ dwRxDmaCh))
+ {
+ SMSC_WARNING("Failed Platform_DmaInitialize, dwRxDmaCh=%u",dwRxDmaCh);
+ }
+ Platform_GetFlowControlParameters(
+ &(privateData->PlatformData),
+ &(privateData->RxFlowParameters),
+ true);
+ }
+ if(max_throughput!=0xFFFFFFFFUL) {
+ privateData->RxFlowParameters.MaxThroughput=max_throughput;
+ }
+ if(max_packet_count!=0xFFFFFFFFUL) {
+ privateData->RxFlowParameters.MaxPacketCount=max_packet_count;
+ }
+ if(packet_cost!=0xFFFFFFFFUL) {
+ privateData->RxFlowParameters.PacketCost=packet_cost;
+ }
+ if(burst_period!=0xFFFFFFFFUL) {
+ privateData->RxFlowParameters.BurstPeriod=burst_period;
+ }
+ if(privateData->RxFlowParameters.BurstPeriod==0) {
+ SMSC_WARNING("burst_period of 0 is not allowed");
+ SMSC_WARNING(" resetting burst_period to 100");
+ privateData->RxFlowParameters.BurstPeriod=100;
+ }
+ if(max_work_load!=0xFFFFFFFFUL) {
+ privateData->RxFlowMaxWorkLoad=max_work_load;
+ } else {
+ privateData->RxFlowMaxWorkLoad=
+ privateData->RxFlowParameters.MaxThroughput+
+ (privateData->RxFlowParameters.MaxPacketCount*
+ privateData->RxFlowParameters.PacketCost);
+ }
+ privateData->RxFlowBurstMaxWorkLoad=
+ (privateData->RxFlowMaxWorkLoad*
+ privateData->RxFlowParameters.BurstPeriod)/1000;
+ if(int_deas!=0xFFFFFFFFUL) {
+ Lan_SetIntDeas(privateData,int_deas);
+ } else {
+ Lan_SetIntDeas(privateData,privateData->RxFlowParameters.IntDeas);
+ }
+
+
+ //Set RX COE
+ {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ Mac_SetRegDW(privateData,VLAN1,ETH_P_8021Q,keyCode);
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ // privateData->RxVLanPkt=true;
+
+ }
+
+
+ if (privateData->UseRxCsum) {
+
+ {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ u32 dwCoeCr=Mac_GetRegDW(privateData,COE_CR,keyCode);
+ dwCoeCr|=(RX_COE_EN | RX_COE_MODE);
+ Mac_SetRegDW(privateData,COE_CR,dwCoeCr,keyCode);
+ //printk("COE_CR2 = 0x%08x\n", Mac_GetRegDW(privateData,COE_CR,keyCode));
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+
+ }
+
+
+ }
+
+
+
+ //initially the receiver is off
+ // a following link up detection will turn the receiver on
+ privateData->dwRxOffCount=1;
+ Lan_SetRegDW(RX_CFG,RX_CFG_RXDOFF_2_);
+ Rx_ReceiverOn(privateData, 0);
+
+ privateData->dwRxDmaThreshold=dwDmaThreshold;
+ Lan_SetRDFL(privateData,0x01);
+ Lan_SetRSFL(privateData,0x00);
+ privateData->RxInterrupts=INT_EN_RSFL_EN_;
+ privateData->RxInterrupts|=INT_EN_RXE_EN_;
+ if(privateData->dwGeneration==0) {
+ privateData->RxInterrupts|=INT_EN_RDFL_EN_;
+ } else {
+ privateData->RxInterrupts|=INT_EN_RDFO_EN_;
+ }
+ privateData->RxInterrupts|=INT_EN_RXDFH_INT_EN_;
+ Lan_EnableInterrupt(privateData,privateData->RxInterrupts);
+
+}
+
+static void Rx_HandleOverrun(PPRIVATE_DATA privateData)
+{
+ if(privateData->dwGeneration==0) {
+ if(privateData->RxOverrun==false) {
+ Rx_ReceiverOff(privateData);
+ privateData->RxUnloadProgress=
+ (((((privateData->LastRxStatus1)&0x3FFF0000UL)>>16)+2+3)&0xFFFFFFFCUL);
+ if(privateData->dwRxDmaCh<TRANSFER_REQUEST_DMA) {
+ privateData->RxUnloadProgress+=
+ (((((privateData->LastRxStatus2)&0x3FFF0000UL)>>16)+2+3)&0xFFFFFFFCUL);
+ }
+ privateData->RxUnloadPacketProgress=0;
+ privateData->RxOverrun=true;
+ privateData->RxOverrunCount++;
+ }
+ } else {
+ privateData->RxOverrunCount++;
+ }
+}
+
+static void Rx_HandOffSkb(
+ PPRIVATE_DATA privateData,
+ struct sk_buff *skb)
+{
+ int result=0;
+
+ skb->dev=privateData->dev;
+ skb->protocol= eth_type_trans(skb,privateData->dev);
+
+ // #ifdef UseRxCsum
+ // WORD wHwCsum = 0;
+ // #endif
+
+ //printk(" Handoff skb->len = 0x%08x\n",(u32) skb->len);
+ if (privateData->UseRxCsum) {
+
+ WORD wHwCsum = *(WORD *)(skb->tail +4);
+ skb->csum = wHwCsum;
+ //printk(" HW csum = 0x%04x\n", skb->csum);
+
+ }
+
+
+ if (privateData->UseRxCsum)
+
+ skb->ip_summed = CHECKSUM_PARTIAL;
+
+ else
+ skb->ip_summed = CHECKSUM_NONE;
+#ifdef LINUX_2_6_OR_NEWER
+ if(rx_mode==PROCESSING_MODE_NAPI) {
+ result=netif_receive_skb(skb);
+ privateData->RxWorkLimit--;
+ privateData->RxPacketsReceived++;
+ if(privateData->RxWorkLimit<=0) {
+ privateData->RxCongested=true;
+ }
+
+ } else {
+ result=netif_rx(skb);//hand off the Received packet to higher layer
+ }
+#else
+ result=netif_rx(skb);
+#endif
+
+ // result=netif_rx(skb);
+
+ switch(result)
+ {
+ case NET_RX_SUCCESS:
+ break;
+ case NET_RX_CN_LOW:
+ case NET_RX_CN_MOD:
+ case NET_RX_CN_HIGH:
+ case NET_RX_DROP:
+ privateData->RxCongested=true;
+ privateData->RxCongestedCount++;
+ break;
+ default:
+ privateData->RxCongested=true;
+ privateData->RxCongestedCount++;
+ SMSC_WARNING("Unknown return value from netif_rx, result=%d",result);
+ break;
+ }
+}
+
+void Rx_CompleteMulticastUpdate (PPRIVATE_DATA privateData)
+{
+ u32 local_MACCR;
+ VL_KEY keyCode=0;
+ unsigned long dwIntFlags=0;
+
+ keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ if (privateData->MulticastUpdatePending) {
+ SET_GPIO(GP_COMPLETE_MULTICAST_UPDATE);
+ Mac_SetRegDW(privateData,HASHH,privateData->HashHi,keyCode);
+ Mac_SetRegDW(privateData,HASHL,privateData->HashLo,keyCode);
+ local_MACCR = Mac_GetRegDW(privateData,MAC_CR,keyCode);
+ local_MACCR |= privateData->set_bits_mask;
+ local_MACCR &= ~(privateData->clear_bits_mask);
+ Mac_SetRegDW(privateData,MAC_CR,local_MACCR,keyCode);
+ Rx_ReceiverOn(privateData, keyCode);
+ privateData->MulticastUpdatePending = false;
+ CLEAR_GPIO(GP_COMPLETE_MULTICAST_UPDATE);
+ }
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+}
+
+void Rx_BeginMulticastUpdate (PPRIVATE_DATA privateData)
+{
+ u32 startTime, currentTime;
+ u32 timeout;
+ unsigned long flags;
+
+ SET_GPIO(GP_BEGIN_MULTICAST_UPDATE);
+
+ //NOTE: we can't rely on privateData->dwLinkSpeed because
+ // it updates only once per second and may be out dated.
+
+ local_irq_save(flags);
+ Rx_ReceiverOff(privateData);
+ if(privateData->dwGeneration>0) {
+ //since this is concord or later there is no
+ // overrun processing that might turn off the receiver.
+ // there for we can rely on RxStop Int.
+
+ //if the speed is 100Mb then lets poll rx stop to get the
+ // quickest response.
+ timeout = 200UL;
+ while ((timeout)&&(!(Lan_GetRegDW(INT_STS)&(INT_STS_RXSTOP_INT_)))) {
+ // wait 1 uSec
+ startTime=Lan_GetRegDW(FREE_RUN);
+ while (1) {
+ currentTime=Lan_GetRegDW(FREE_RUN);
+ if (currentTime-startTime >= 25UL)
+ break;
+ }
+ timeout--;
+ }
+ if(timeout==0) {
+ //this is probably a 10Mb link, therefore prepare
+ // interrupt for update later.
+ Lan_EnableInterrupt(privateData,INT_EN_RXSTOP_INT_EN_);
+
+ // if this is a 10Mbps half duplex connection
+ // then Rx stop is only 99.6% reliable
+ // Therefor we must schedule Gpt callback as
+ // back up
+
+ // using 18*(100uS) because we already waited 200uS
+ Gpt_ScheduleCallBack(privateData,GptCB_RxCompleteMulticast, 18UL);
+ } else {
+ //Rx is stopped
+ Lan_SetRegDW(INT_STS,INT_STS_RXSTOP_INT_);//clear interrupt signal
+ Rx_CompleteMulticastUpdate(privateData);
+ }
+ } else {
+ // for generation 0 we can't rely on Rx stop because
+ // the receiver may have already been stopped due to
+ // overflow processing
+
+ // for the same reason we can't just wait 200uS and
+ // check stopped status there for we must rely on GP timer
+ // and we must assume a worse case of 10Mb speed
+
+ Gpt_ScheduleCallBack(privateData,GptCB_RxCompleteMulticast, 20UL);
+ }
+ local_irq_restore (flags);
+ CLEAR_GPIO(GP_BEGIN_MULTICAST_UPDATE);
+}
+
+static u32 Rx_PopRxStatus(
+ PPRIVATE_DATA privateData)
+{
+ u32 result=Lan_GetRegDW(RX_FIFO_INF);
+ if((privateData->RxCongested==false)||
+ ((privateData->RxCongested==true)&&((result&0x00FF0000UL)==0UL)))
+ {
+ if(result&0x00FF0000UL) {
+ u32 dwIntSts=Lan_GetRegDW(INT_STS);
+ if(privateData->dwGeneration==0) {
+ if(dwIntSts&INT_STS_RDFL_) {
+ Lan_SetRegDW(INT_STS,INT_STS_RDFL_);
+ Rx_HandleOverrun(privateData);
+ }
+ } else {
+ if(dwIntSts&INT_STS_RDFO_) {
+ Lan_SetRegDW(INT_STS,INT_STS_RDFO_);
+ Rx_HandleOverrun(privateData);
+ }
+ }
+ if((privateData->RxFlowControlActive==false)||
+ ((privateData->RxFlowControlActive==true)&&
+ (privateData->RxFlowBurstActive==true)))
+ {
+ //Rx status is available, read it
+ result=Lan_GetRegDW(RX_STATUS_FIFO);
+ privateData->RxStatusDWReadCount++;
+ privateData->LastRxStatus3=
+ privateData->LastRxStatus2;
+ privateData->LastRxStatus2=
+ privateData->LastRxStatus1;
+ privateData->LastRxStatus1=result;
+
+ if(privateData->RxOverrun) {
+ u32 dwPacketLength=((result&0x3FFF0000UL)>>16);
+ u32 dwByteCount=((dwPacketLength+2+3)&0xFFFFFFFCUL);
+ if((privateData->RxUnloadProgress+dwByteCount)>=
+ ((privateData->RxMaxDataFifoSize)-16))
+ {
+ //This is the packet that crosses the corruption point
+ // so just ignore it and complete the overrun processing.
+ result=0;
+ goto FINISH_OVERRUN_PROCESSING;
+ }
+ privateData->RxUnloadProgress+=dwByteCount;
+ privateData->RxUnloadPacketProgress++;
+ }
+
+ privateData->RxFlowCurrentThroughput+=
+ ((((result&0x3FFF0000UL)>>16)-4UL));
+ privateData->RxFlowCurrentPacketCount++;
+ privateData->RxFlowCurrentWorkLoad+=
+ ((((result&0x3FFF0000UL)>>16)-4UL)+privateData->RxFlowParameters.PacketCost);
+ if(privateData->RxFlowControlActive) {
+ privateData->RxFlowBurstWorkLoad+=
+ ((((result&0x3FFF0000UL)>>16)-4UL)+privateData->RxFlowParameters.PacketCost);
+ if(privateData->RxFlowBurstWorkLoad>=
+ privateData->RxFlowBurstMaxWorkLoad)
+ {
+ privateData->RxFlowBurstActive=false;
+ Lan_DisableInterrupt(privateData,privateData->RxInterrupts);
+ }
+ }
+ } else {
+ result=0;
+ }
+ }
+ else
+ {
+ if(privateData->RxOverrun) {
+ u32 timeOut;
+ u32 temp;
+FINISH_OVERRUN_PROCESSING:
+ temp=0;
+ {
+ timeOut=2000;
+ while((timeOut>0)&&(!(Lan_GetRegDW(INT_STS)&(INT_STS_RXSTOP_INT_)))) {
+ udelay(1);
+ timeOut--;
+ }
+ if(timeOut==0) {
+ // privateData->RxStopTimeOutCount++;
+ // PULSE_GPIO(GP_TX,1);
+ SMSC_WARNING("Timed out waiting for Rx to Stop\n");
+ }
+ Lan_SetRegDW(INT_STS,INT_STS_RXSTOP_INT_);
+ }
+
+ if(privateData->dwRxDmaCh<TRANSFER_REQUEST_DMA) {
+ //make sure DMA has stopped before doing RX Dump
+ if(privateData->RxSkb) {
+ Platform_DmaComplete(
+ &(privateData->PlatformData),
+ privateData->dwRxDmaCh);
+
+ Rx_HandOffSkb(privateData,privateData->RxSkb);
+ privateData->RxSkb=NULL;
+ }
+ }
+
+ temp=Lan_GetRegDW(RX_CFG);
+ Lan_SetRegDW(RX_CFG,(temp&0x3FFFFFFFUL));
+ timeOut=10000000;
+ Lan_SetBitsDW(RX_CFG,RX_CFG_RX_DUMP_);
+ while((timeOut>0)&&(Lan_GetRegDW(RX_CFG)&(RX_CFG_RX_DUMP_))) {
+ udelay(1);
+ timeOut--;
+ }
+ if(timeOut==0) {
+ SMSC_WARNING("Timed out waiting for Rx Dump to complete\n");
+ }
+ Lan_SetRegDW(RX_CFG,temp);
+
+ privateData->RxDumpCount++;
+ Lan_SetRegDW(INT_STS,INT_STS_RDFL_);
+ Rx_ReceiverOn(privateData, 0);
+ privateData->RxOverrun=false;
+ }
+ result=0;
+ privateData->LastReasonForReleasingCPU=1;//Status FIFO Empty
+ }
+ } else {
+ //disable and reenable the INT_EN
+ // This will allow the deassertion interval to begin
+ u32 temp=Lan_GetRegDW(INT_EN);
+ Lan_SetRegDW(INT_EN,0);
+ Lan_SetRegDW(INT_EN,temp);
+ result=0;
+ privateData->LastReasonForReleasingCPU=2;//High Congestion
+ }
+ return result;
+}
+
+void Rx_CountErrors(PPRIVATE_DATA privateData,u32 dwRxStatus)
+{
+ bool crcError=false;
+ if(dwRxStatus&0x00008000UL) {
+ privateData->stats.rx_errors++;
+ if(dwRxStatus&0x00000002UL) {
+ privateData->stats.rx_crc_errors++;
+ crcError=true;
+ }
+ }
+ if(!crcError) {
+ if((dwRxStatus&0x00001020UL)==0x00001020UL) {
+ //Frame type indicates length, and length error is set
+ privateData->stats.rx_length_errors++;
+ }
+ if(dwRxStatus&RX_STS_MCAST_) {
+ privateData->stats.multicast++;
+ }
+ }
+}
+
+void Rx_FastForward(PPRIVATE_DATA privateData,u32 dwDwordCount)
+{
+ privateData->RxFastForwardCount++;
+ if((dwDwordCount>=4)
+ && (
+ (((privateData->dwIdRev&0x0000FFFFUL)==0x00000000UL)
+ && (privateData->dwFpgaRev>=0x36))
+ ||
+ ((privateData->dwIdRev&0x0000FFFFUL)!=0UL)
+ )
+ )
+ {
+ u32 dwTimeOut=500;
+ Lan_SetRegDW(RX_DP_CTRL,(dwDwordCount|RX_DP_CTRL_FFWD_BUSY_));
+ while((dwTimeOut)&&(Lan_GetRegDW(RX_DP_CTRL)&
+ RX_DP_CTRL_FFWD_BUSY_))
+ {
+ udelay(1);
+ dwTimeOut--;
+ }
+ if(dwTimeOut==0) {
+
+ SMSC_WARNING("timed out waiting for RX FFWD to finish, RX_DP_CTRL=0x%08X",
+ Lan_GetRegDW(RX_DP_CTRL));
+ }
+ } else {
+ while(dwDwordCount) {
+ u32 dwTemp=Lan_GetRegDW(RX_DATA_FIFO);
+ dwTemp=dwTemp;
+ dwDwordCount--;
+ }
+ }
+}
+
+//Rx_ReceiverOff, and Rx_ReceiverOn use a reference counter
+// because they are used in both the Rx code and the link management count
+void Rx_ReceiverOff(PPRIVATE_DATA privateData)
+{
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ if(privateData->dwRxOffCount==0) {
+ u32 dwMacCr=Mac_GetRegDW(privateData,MAC_CR,keyCode);
+ if(!(dwMacCr&MAC_CR_RXEN_)) {
+ SMSC_WARNING("Rx_ReceiverOff: Receiver is already Off");
+ }
+ dwMacCr&=(~MAC_CR_RXEN_);
+ Mac_SetRegDW(privateData,MAC_CR,dwMacCr,keyCode);
+ //CLEAR_GPIO(GP_RX);
+ }
+ privateData->dwRxOffCount++;
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+}
+
+//Rx_ReceiverOff, and Rx_ReceiverOn use a reference counter
+// because they are used in both the Rx code and the link management count
+void Rx_ReceiverOn(PPRIVATE_DATA privateData, VL_KEY callerKeyCode)
+{
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=0;
+
+ if (callerKeyCode == 0) {
+ keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ }
+ else {
+ SMSC_ASSERT(Vl_CheckLock(&(privateData->MacPhyLock),callerKeyCode));
+ keyCode = callerKeyCode;
+ }
+ if(privateData->dwRxOffCount>0) {
+ privateData->dwRxOffCount--;
+ if(privateData->dwRxOffCount==0) {
+ u32 dwMacCr=Mac_GetRegDW(privateData,MAC_CR,keyCode);
+ if(dwMacCr&MAC_CR_RXEN_) {
+ SMSC_WARNING("Rx_ReceiverOn: Receiver is already on");
+ }
+ dwMacCr|=MAC_CR_RXEN_;
+ Mac_SetRegDW(privateData,MAC_CR,dwMacCr,keyCode);
+ //SET_GPIO(GP_RX);
+ }
+ } else {
+ SMSC_ASSERT(false);
+ }
+ if (callerKeyCode == 0) {
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ }
+}
+
+void Rx_ProcessPackets(PPRIVATE_DATA privateData)
+{
+ u32 dwRxStatus=0;
+ PPLATFORM_DATA platformData=NULL;
+
+ /*
+#ifdef UseRxCsum
+WORD wHwCsum = 0;
+#endif
+*/
+
+ // SET_GPIO(GP_RX);
+
+ privateData->RxCongested=false;
+ platformData=&(privateData->PlatformData);
+ if(privateData->dwRxDmaCh>=TRANSFER_PIO) {
+ //Use PIO only
+
+ //WARNING: the two LSBs of RXDOFF cannot be changed with the receiver running ,
+ // however we are here re-writing the same value it already had, so it's ok"
+ Lan_SetRegDW(RX_CFG,RX_CFG_RXDOFF_2_);
+ while((dwRxStatus=Rx_PopRxStatus(privateData))!=0)
+ {
+ u32 dwPacketLength=((dwRxStatus&0x3FFF0000UL)>>16);
+
+ //printk("dwPacketLength = 0x%08x\n", (u32) dwPacketLength);
+ //printk("dwRxStatus = 0x%08x\n", (u32) dwRxStatus);
+
+ Rx_CountErrors(privateData,dwRxStatus);
+
+
+ if((dwRxStatus&RX_STS_ES_)==0)
+ //||(((dwRxStatus&0x00001080)==0x00001080)&&(privateData->RxVLanPkt==true)))
+ {
+ struct sk_buff *skb=NULL;
+ skb=dev_alloc_skb(dwPacketLength+2);
+ if(skb!=NULL) {
+ skb->data=skb->head;
+ skb->tail=skb->head;
+ skb_reserve(skb,2); // align IP on 16B boundary
+
+ if (privateData->UseRxCsum)
+ {
+ skb_put(skb,dwPacketLength-2UL-4UL);
+ }
+ else
+ {
+ skb_put(skb,dwPacketLength-4UL);
+ }
+
+
+ //update counters
+ privateData->stats.rx_packets++;
+
+
+ if (privateData->UseRxCsum)
+ {
+ privateData->stats.rx_bytes+=(dwPacketLength-4-2);
+
+ }
+ else
+ {
+ privateData->stats.rx_bytes+=(dwPacketLength-4);
+ }
+
+
+ privateData->RxPacketReadCount++;
+ privateData->RxPioReadCount++;
+ privateData->RxDataDWReadCount+=
+ (dwPacketLength+2+3)>>2;
+
+
+
+ Platform_ReadFifo(
+ privateData->dwLanBase,
+ ((u32 *)(skb->head)),
+ (dwPacketLength+2+3)>>2);
+
+
+ //printk("Rx skb->len = 0x%08x\n", (u32) skb->len);
+
+ Rx_HandOffSkb(privateData,skb);
+ continue;
+ } else {
+ SMSC_WARNING("Unable to allocate sk_buff for RX Packet, in PIO path");
+ privateData->stats.rx_dropped++;
+ }
+ }
+ //if we get here then the packet is to be read
+ // out of the fifo and discarded
+ //printk("fast forward\n");
+ dwPacketLength+=(2+3);
+ dwPacketLength>>=2;
+ Rx_FastForward(privateData,dwPacketLength);
+ }
+ }
+ else {
+
+ //Use DMA and PIO
+ u32 dwDmaCh=privateData->dwRxDmaCh;
+ //struct sk_buff *dmaSkb=NULL;//use privateData->RxDmaSkb
+ DMA_XFER dmaXfer;
+ dmaXfer.dwLanReg=privateData->dwLanBase+RX_DATA_FIFO;
+ dmaXfer.pdwBuf=NULL;// this will be reset per dma request
+ dmaXfer.dwDmaCh=dwDmaCh;
+ dmaXfer.dwDwCnt=0;// this will be reset per dma request
+ dmaXfer.fMemWr=true;
+ while((dwRxStatus=Rx_PopRxStatus(privateData))!=0)
+ {
+ u32 dwPacketLength;
+RUN_AGAIN:
+ Rx_CountErrors(privateData,dwRxStatus);
+ dwPacketLength=((dwRxStatus&0x3FFF0000UL)>>16);
+
+
+ if((dwRxStatus&RX_STS_ES_)==0)
+ {
+ struct sk_buff *skb=dev_alloc_skb(dwPacketLength+2*PLATFORM_CACHE_LINE_BYTES);
+ if(skb!=NULL)
+ {
+ skb->data=skb->head;
+ skb->tail=skb->head;
+
+ //align IP on cache line boundary
+ privateData->stats.rx_packets++;
+ privateData->stats.rx_bytes+=(dwPacketLength-4UL);
+ if(dwPacketLength>=privateData->dwRxDmaThreshold)
+ {
+ //use DMA
+ //printk("Rx using DMA\n");
+ u32 dwDwordCount;
+ skb_reserve(skb,PLATFORM_CACHE_LINE_BYTES-14);
+
+ if (privateData->UseRxCsum)
+ {
+ skb_put(skb,dwPacketLength-2UL-4UL);
+ }
+ else
+ {
+ skb_put(skb,dwPacketLength-4UL);
+ }
+
+
+
+ // skb_put(skb,dwPacketLength-4UL);
+ dwDwordCount=((dwPacketLength+
+ (PLATFORM_CACHE_LINE_BYTES-14)+
+ PLATFORM_CACHE_LINE_BYTES-1)&
+ (~(PLATFORM_CACHE_LINE_BYTES-1)))>>2;
+ Platform_CacheInvalidate(
+ platformData,
+ skb->head,dwDwordCount<<2);
+ dmaXfer.pdwBuf=(u32 *)(skb->head);
+ dmaXfer.dwDwCnt=dwDwordCount;
+ privateData->RxDataDWReadCount+=dwDwordCount;
+ privateData->RxPacketReadCount++;
+ privateData->RxDmaReadCount++;
+ if(privateData->RxSkb)
+ Platform_DmaComplete(platformData,dwDmaCh);
+
+ //set end alignment and offset
+ switch(PLATFORM_CACHE_LINE_BYTES)
+ {
+ //case 4: Lan_SetRegDW(RX_CFG,0x00000200UL);break;
+ //WARNING: the two LSBs of RXDOFF cannot be changed with the receiver running ,
+ // however we are here re-writing the same value it already had, so it's ok"
+ case 16:Lan_SetRegDW(RX_CFG, RX_CFG_RX_END_ALGN16_ |RX_CFG_RXDOFF_2_ );break;
+ case 32:Lan_SetRegDW(RX_CFG, RX_CFG_RX_END_ALGN32_ |RX_CFG_RXDOFF_18_);break;
+ default:SMSC_ASSERT(false);
+ }
+ if(!Platform_DmaStartXfer(platformData,&dmaXfer)) {
+ SMSC_WARNING("Failed Platform_DmaStartXfer");
+ }
+
+
+
+
+ if(privateData->RxSkb) {
+
+ Rx_HandOffSkb(privateData,privateData->RxSkb);
+ }
+ privateData->RxSkb=skb;
+ }
+ else
+ {
+ //use PIO
+
+ // printk("Rx using PIO\n");
+ if(privateData->RxSkb) {
+ Platform_DmaComplete(platformData,dwDmaCh);
+ Rx_HandOffSkb(privateData,privateData->RxSkb);
+ }
+
+ privateData->RxSkb=skb;
+
+ skb_reserve(skb,2);
+
+ if (privateData->UseRxCsum)
+ {
+ skb_put(skb,dwPacketLength-2UL-4UL);
+ }
+ else
+ {
+ skb_put(skb,dwPacketLength-4UL);
+ }
+
+
+
+ // skb_put(skb,dwPacketLength-4UL);
+ //set end alignment and offset
+ //WARNING: the two LSBs of RXDOFF cannot be changed with the receiver running ,
+ // however we are here re-writing the same value it already had, so it's ok"
+ Lan_SetRegDW(RX_CFG, RX_CFG_RXDOFF_2_);//4 byte end alignment
+ privateData->RxPacketReadCount++;
+ privateData->RxPioReadCount++;
+ privateData->RxDataDWReadCount+=
+ ((dwPacketLength+2+3)>>2);
+ Platform_ReadFifo(
+ privateData->dwLanBase,
+ ((u32 *)(skb->head)),
+ (dwPacketLength+2+3)>>2);
+
+
+
+ }
+ continue;
+ }
+ else
+ {
+ SMSC_WARNING("Unable to allocate sk_buff for RX Packet, in DMA path");
+ privateData->stats.rx_dropped++;
+ }
+ }
+ //if we get here then the packet is to be read
+ // out of the fifo and discarded
+ if(privateData->RxSkb) Platform_DmaComplete(platformData,dwDmaCh);
+ //delay returning the dmaSkb to OS till later
+ dwPacketLength+=(2+3);
+ dwPacketLength>>=2;
+ //WARNING: the two LSBs of RXDOFF cannot be changed with the receiver running ,
+ // however we are here re-writing the same value it already had, so it's ok"
+ Lan_SetRegDW(RX_CFG,RX_CFG_RXDOFF_2_);//4 byte end alignment
+ Rx_FastForward(privateData,dwPacketLength);
+ }
+ if(privateData->RxSkb) {
+ //while waiting for dma to complete,
+ // check if another packet arrives
+ u32 dwTimeOut=1000000;
+ while((Platform_DmaGetDwCnt(platformData,dwDmaCh))&&
+ (dwTimeOut))
+ {
+ if((dwRxStatus=Rx_PopRxStatus(privateData))!=0) {
+ goto RUN_AGAIN;
+ }
+ udelay(1);
+ dwTimeOut--;
+ }
+ if(dwTimeOut==0) {
+ SMSC_WARNING("Timed out while waiting for final Dma to complete");
+ }
+ if((dwRxStatus=Rx_PopRxStatus(privateData))!=0) {
+ goto RUN_AGAIN;
+ }
+
+
+ Rx_HandOffSkb(privateData,privateData->RxSkb);
+ privateData->RxSkb=NULL;
+
+ //check one last time for another packet.
+ if((dwRxStatus=Rx_PopRxStatus(privateData))!=0) {
+ goto RUN_AGAIN;
+ }
+ }
+ }
+ Lan_SetRegDW(INT_STS,INT_STS_RSFL_);
+ // CLEAR_GPIO(GP_RX);
+}
+
+void Rx_ProcessPacketsTasklet(unsigned long data)
+{
+ PPRIVATE_DATA privateData=(PPRIVATE_DATA)Rx_TaskletParameter;
+ data=data;//make lint happy
+ if(privateData==NULL) {
+ SMSC_WARNING("Rx_ProcessPacketsTasklet(privateData==NULL)");
+ return;
+ }
+ Rx_ProcessPackets(privateData);
+ Lan_EnableIRQ(privateData);
+}
+
+#ifdef LINUX_2_6_OR_NEWER
+/*
+int Smsc9118_rx_poll(struct net_device *dev,int * budget)
+{
+ int result=0;
+ int limit=0;
+
+ PPRIVATE_DATA privateData=NULL;
+ SMSC_ASSERT(dev!=NULL);
+ SMSC_ASSERT(budget!=NULL);
+ privateData=((PPRIVATE_DATA)(dev->ml_priv));
+ SMSC_ASSERT(privateData!=NULL);
+
+ privateData->RxWorkLimit=dev->quota;
+ if((privateData->RxWorkLimit)>(*budget)) {
+ privateData->RxWorkLimit=(*budget);
+ }
+
+ limit=privateData->RxWorkLimit;
+
+ privateData->RxPacketsReceived=0;
+ // privateData->RxDone=false;
+ Rx_ProcessPackets(privateData);
+
+ dev->quota-=privateData->RxPacketsReceived;
+ (*budget)-=privateData->RxPacketsReceived;
+
+ if(privateData->RxPacketsReceived < limit) {
+ netif_rx_complete(dev);
+ Lan_EnableIRQ(privateData);
+ } else {
+ result=1;
+ }
+
+ return result;
+}
+*/
+#endif
+
+
+
+
+
+bool Rx_HandleInterrupt(
+ PPRIVATE_DATA privateData,
+ u32 dwIntSts)
+{
+ bool result=false;
+ SMSC_ASSERT(privateData!=NULL);
+
+ privateData->LastReasonForReleasingCPU=0;
+
+ if(dwIntSts&INT_STS_RXE_) {
+ SMSC_TRACE("Rx_HandleInterrupt: RXE signalled");
+ privateData->stats.rx_errors++;
+ Lan_SetRegDW(INT_STS,INT_STS_RXE_);
+ result=true;
+ }
+
+ if(dwIntSts&INT_STS_RXDFH_INT_) {
+ privateData->stats.rx_dropped+=Lan_GetRegDW(RX_DROP);
+ Lan_SetRegDW(INT_STS,INT_STS_RXDFH_INT_);
+ result=true;
+ }
+
+ if(privateData->dwGeneration==0) {
+ if(dwIntSts&(INT_STS_RDFL_)) {
+ Lan_SetRegDW(INT_STS,INT_STS_RDFL_);
+ Rx_HandleOverrun(privateData);
+ result=true;
+ }
+ } else {
+ if(dwIntSts&(INT_STS_RDFO_)) {
+ Lan_SetRegDW(INT_STS,INT_STS_RDFO_);
+ Rx_HandleOverrun(privateData);
+ result=true;
+ }
+ }
+
+ if((!(dwIntSts&INT_STS_RSFL_))&&(privateData->RxOverrun==false)) {
+ return result;
+ }
+ result=true;
+
+ if(privateData->MeasuringRxThroughput==false) {
+ privateData->MeasuringRxThroughput=true;
+ Gpt_ScheduleCallBack(privateData,GptCB_MeasureRxThroughput,1000);
+ privateData->RxFlowCurrentThroughput=0;
+ privateData->RxFlowCurrentPacketCount=0;
+ privateData->RxFlowCurrentWorkLoad=0;
+ }
+
+
+#ifdef LINUX_2_6_OR_NEWER
+ if(rx_mode==PROCESSING_MODE_TASKLET) {
+ Lan_DisableIRQ(privateData);
+ Rx_TaskletParameter=(unsigned long)privateData;
+ tasklet_schedule(&Rx_Tasklet);
+ }else if (rx_mode==PROCESSING_MODE_NAPI) {
+ /*
+ Lan_DisableIRQ(privateData);
+ netif_rx_schedule(privateData->dev);
+ */
+ }else {
+ Rx_ProcessPackets(privateData);
+ }
+#else
+ if(rx_mode==PROCESSING_MODE_TASKLET) {
+ Lan_DisableIRQ(privateData);
+ Rx_TaskletParameter=(unsigned long)privateData;
+ tasklet_schedule(&Rx_Tasklet);
+ }else {
+ Rx_ProcessPackets(privateData);
+ }
+#endif
+
+ return result;
+}
+
+bool RxStop_HandleInterrupt(
+ PPRIVATE_DATA privateData,
+ u32 dwIntSts)
+{
+ bool result=false;
+ SMSC_ASSERT(privateData!=NULL);
+
+ if(dwIntSts&INT_STS_RXSTOP_INT_) {
+ result=true;
+ Gpt_CancelCallBack (privateData, GptCB_RxCompleteMulticast);
+ Rx_CompleteMulticastUpdate (privateData);
+ Lan_SetRegDW(INT_STS,INT_STS_RXSTOP_INT_);
+ Lan_DisableInterrupt(privateData,INT_EN_RXSTOP_INT_EN_);
+ }
+ return result;
+}
+
+//returns hash bit number for given MAC address
+//example:
+// 01 00 5E 00 00 01 -> returns bit number 31
+static u32 Rx_Hash(BYTE addr[6])
+{
+ int i;
+ u32 crc=0xFFFFFFFFUL;
+ u32 poly=0xEDB88320UL;
+ u32 result=0;
+ for(i=0;i<6;i++)
+ {
+ int bit;
+ u32 data=((u32)addr[i]);
+ for(bit=0;bit<8;bit++)
+ {
+ u32 p = (crc^((u32)data))&1UL;
+ crc >>= 1;
+ if(p!=0) crc ^= poly;
+ data >>=1;
+ }
+ }
+ result=((crc&0x01UL)<<5)|
+ ((crc&0x02UL)<<3)|
+ ((crc&0x04UL)<<1)|
+ ((crc&0x08UL)>>1)|
+ ((crc&0x10UL)>>3)|
+ ((crc&0x20UL)>>5);
+ return result;
+}
+
+void Rx_SetMulticastList(
+ struct net_device *dev)
+{
+ PPRIVATE_DATA privateData=NULL;
+ VL_KEY keyCode=0;
+ unsigned long dwIntFlags=0;
+ SMSC_ASSERT(dev!=NULL);
+
+ privateData=((PPRIVATE_DATA)(dev->ml_priv));
+ SMSC_ASSERT(privateData!=NULL);
+ keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+
+ if(dev->flags & IFF_PROMISC) {
+ // SMSC_TRACE("Promiscuous Mode Enabled");
+ privateData->set_bits_mask = MAC_CR_PRMS_;
+ privateData->clear_bits_mask = (MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+
+ privateData->HashHi = 0UL;
+ privateData->HashLo = 0UL;
+ goto PREPARE;
+ }
+
+ if(dev->flags & IFF_ALLMULTI) {
+ // SMSC_TRACE("Receive all Multicast Enabled");
+ privateData->set_bits_mask = MAC_CR_MCPAS_;
+ privateData->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_HPFILT_);
+
+ privateData->HashHi = 0UL;
+ privateData->HashLo = 0UL;
+ goto PREPARE;
+ }
+
+
+ if(dev->mc_count>0) {
+ u32 dwHashH=0;
+ u32 dwHashL=0;
+ u32 dwCount=0;
+ struct dev_mc_list *mc_list=dev->mc_list;
+
+ privateData->set_bits_mask = MAC_CR_HPFILT_;
+ privateData->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_MCPAS_);
+
+ while(mc_list!=NULL) {
+ dwCount++;
+ if((mc_list->dmi_addrlen)==6) {
+ u32 dwMask=0x01UL;
+ u32 dwBitNum=Rx_Hash(mc_list->dmi_addr);
+ // SMSC_TRACE("Multicast: enable dwBitNum=%d,addr=%02X %02X %02X %02X %02X %02X",
+ // dwBitNum,
+ // ((BYTE *)(mc_list->dmi_addr))[0],
+ // ((BYTE *)(mc_list->dmi_addr))[1],
+ // ((BYTE *)(mc_list->dmi_addr))[2],
+ // ((BYTE *)(mc_list->dmi_addr))[3],
+ // ((BYTE *)(mc_list->dmi_addr))[4],
+ // ((BYTE *)(mc_list->dmi_addr))[5]);
+ dwMask<<=(dwBitNum&0x1FUL);
+ if(dwBitNum&0x20UL) {
+ dwHashH|=dwMask;
+ } else {
+ dwHashL|=dwMask;
+ }
+ } else {
+ SMSC_WARNING("dmi_addrlen!=6");
+ }
+ mc_list=mc_list->next;
+ }
+ if(dwCount!=((u32)(dev->mc_count))) {
+ SMSC_WARNING("dwCount!=dev->mc_count");
+ }
+ // SMSC_TRACE("Multicast: HASHH=0x%08X,HASHL=0x%08X",dwHashH,dwHashL);
+ privateData->HashHi = dwHashH;
+ privateData->HashLo = dwHashL;
+ }
+ else
+ {
+ privateData->set_bits_mask = 0L;
+ privateData->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+
+ // SMSC_TRACE("Receive own packets only.");
+ privateData->HashHi = 0UL;
+ privateData->HashLo = 0UL;
+ }
+
+PREPARE:
+ if(privateData->dwGeneration<=1) {
+ if (privateData->MulticastUpdatePending == false) {
+ privateData->MulticastUpdatePending = true;
+ // prepare to signal software interrupt
+ Lan_SignalSoftwareInterrupt(privateData);
+ }
+ else {
+ // Rx_CompleteMulticastUpdate has not yet been called
+ // therefore these latest settings will be used instead
+ }
+ } else {
+ u32 local_MACCR;
+ Mac_SetRegDW(privateData,HASHH,privateData->HashHi,keyCode);
+ Mac_SetRegDW(privateData,HASHL,privateData->HashLo,keyCode);
+ local_MACCR = Mac_GetRegDW(privateData,MAC_CR,keyCode);
+ local_MACCR |= privateData->set_bits_mask;
+ local_MACCR &= ~(privateData->clear_bits_mask);
+ Mac_SetRegDW(privateData,MAC_CR,local_MACCR,keyCode);
+ }
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ return;
+}
+
+void Eeprom_EnableAccess(PPRIVATE_DATA privateData)
+{
+ SMSC_ASSERT(privateData!=NULL);
+ if(debug_mode&0x04UL) {
+ Lan_SetRegDW(GPIO_CFG,(g_GpioSetting&0xFF0FFFFFUL));
+ } else {
+ Lan_ClrBitsDW(GPIO_CFG,0x00F00000UL);
+ }
+ udelay(100);
+}
+
+void Eeprom_DisableAccess(PPRIVATE_DATA privateData)
+{
+ SMSC_ASSERT(privateData!=NULL);
+ if(debug_mode&0x04UL) {
+ Lan_SetRegDW(GPIO_CFG,g_GpioSetting);
+ }
+}
+
+bool Eeprom_IsMacAddressLoaded(PPRIVATE_DATA privateData)
+{
+ SMSC_ASSERT(privateData!=NULL);
+ return (Lan_GetRegDW(E2P_CMD)&
+ E2P_CMD_MAC_ADDR_LOADED_)?true:false;
+}
+
+bool Eeprom_IsBusy(PPRIVATE_DATA privateData)
+{
+ SMSC_ASSERT(privateData!=NULL);
+ return (Lan_GetRegDW(E2P_CMD)&
+ E2P_CMD_EPC_BUSY_)?true:false;
+}
+
+bool Eeprom_Timeout(PPRIVATE_DATA privateData)
+{
+ SMSC_ASSERT(privateData!=NULL);
+ return (Lan_GetRegDW(E2P_CMD)&
+ E2P_CMD_EPC_TIMEOUT_)?true:false;
+}
+
+bool Eeprom_ReadLocation(
+ PPRIVATE_DATA privateData,
+ BYTE address, BYTE * data)
+{
+ u32 timeout=100000;
+ u32 temp=0;
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(data!=NULL);
+ if((temp=Lan_GetRegDW(E2P_CMD))&E2P_CMD_EPC_BUSY_) {
+ SMSC_WARNING("Eeprom_ReadLocation: Busy at start, E2P_CMD=0x%08X",temp);
+ return false;
+ }
+ Lan_SetRegDW(E2P_CMD,
+ (E2P_CMD_EPC_BUSY_|E2P_CMD_EPC_CMD_READ_|((u32)address)));
+ while((timeout>0)&&
+ (Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_))
+ {
+ udelay(10);
+ timeout--;
+ }
+ if(timeout==0) {
+ return false;
+ }
+ (*data)=(BYTE)(Lan_GetRegDW(E2P_DATA));
+ return true;
+}
+
+bool Eeprom_EnableEraseAndWrite(
+ PPRIVATE_DATA privateData)
+{
+ u32 timeout=100000;
+ SMSC_ASSERT(privateData!=NULL);
+ if(Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_) {
+ SMSC_WARNING("Eeprom_EnableEraseAndWrite: Busy at start");
+ return false;
+ }
+ Lan_SetRegDW(E2P_CMD,
+ (E2P_CMD_EPC_BUSY_|E2P_CMD_EPC_CMD_EWEN_));
+
+ while((timeout>0)&&
+ (Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_))
+ {
+ udelay(10);
+ timeout--;
+ }
+ if(timeout==0) {
+ return false;
+ }
+ return true;
+}
+
+bool Eeprom_DisableEraseAndWrite(
+ PPRIVATE_DATA privateData)
+{
+ u32 timeout=100000;
+ SMSC_ASSERT(privateData!=NULL);
+ if(Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_) {
+ SMSC_WARNING("Eeprom_DisableEraseAndWrite: Busy at start");
+ return false;
+ }
+ Lan_SetRegDW(E2P_CMD,
+ (E2P_CMD_EPC_BUSY_|E2P_CMD_EPC_CMD_EWDS_));
+
+ while((timeout>0)&&
+ (Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_))
+ {
+ udelay(10);
+ timeout--;
+ }
+ if(timeout==0) {
+ return false;
+ }
+ return true;
+}
+
+bool Eeprom_WriteLocation(
+ PPRIVATE_DATA privateData,BYTE address,BYTE data)
+{
+ u32 timeout=100000;
+ SMSC_ASSERT(privateData!=NULL);
+ if(Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_) {
+ SMSC_WARNING("Eeprom_WriteLocation: Busy at start");
+ return false;
+ }
+ Lan_SetRegDW(E2P_DATA,((u32)data));
+ Lan_SetRegDW(E2P_CMD,
+ (E2P_CMD_EPC_BUSY_|E2P_CMD_EPC_CMD_WRITE_|((u32)address)));
+
+ while((timeout>0)&&
+ (Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_))
+ {
+ udelay(10);
+ timeout--;
+ }
+ if(timeout==0) {
+ return false;
+ }
+ return true;
+}
+
+bool Eeprom_EraseAll(
+ PPRIVATE_DATA privateData)
+{
+ u32 timeout=100000;
+ SMSC_ASSERT(privateData!=NULL);
+ if(Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_) {
+ SMSC_WARNING("Eeprom_EraseAll: Busy at start");
+ return false;
+ }
+ Lan_SetRegDW(E2P_CMD,
+ (E2P_CMD_EPC_BUSY_|E2P_CMD_EPC_CMD_ERAL_));
+
+ while((timeout>0)&&
+ (Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_))
+ {
+ udelay(10);
+ timeout--;
+ }
+ if(timeout==0) {
+ return false;
+ }
+ return true;
+}
+
+bool Eeprom_Reload(
+ PPRIVATE_DATA privateData)
+{
+ u32 timeout=100000;
+ SMSC_ASSERT(privateData!=NULL);
+ if(Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_) {
+ SMSC_WARNING("Eeprom_Reload: Busy at start");
+ return false;
+ }
+ Lan_SetRegDW(E2P_CMD,
+ (E2P_CMD_EPC_BUSY_|E2P_CMD_EPC_CMD_RELOAD_));
+
+ while((timeout>0)&&
+ (Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_))
+ {
+ udelay(10);
+ timeout--;
+ }
+ if(timeout==0) {
+ return false;
+ }
+ return true;
+}
+
+bool Eeprom_SaveMacAddress(
+ PPRIVATE_DATA privateData,
+ u32 dwHi16,u32 dwLo32)
+{
+ bool result=false;
+ SMSC_ASSERT(privateData!=NULL);
+ Eeprom_EnableAccess(privateData);
+ if(!Eeprom_EnableEraseAndWrite(privateData)) goto DONE;
+ if(!Eeprom_EraseAll(privateData)) goto DONE;
+ if(privateData->dwGeneration==0) {
+ if(!Eeprom_EnableEraseAndWrite(privateData)) goto DONE;
+ if(!Eeprom_WriteLocation(privateData,0,0xA5)) goto DONE;
+ if(!Eeprom_EnableEraseAndWrite(privateData)) goto DONE;
+ if(!Eeprom_WriteLocation(privateData,1,LOBYTE(LOWORD(dwLo32)))) goto DONE;
+ if(!Eeprom_EnableEraseAndWrite(privateData)) goto DONE;
+ if(!Eeprom_WriteLocation(privateData,2,HIBYTE(LOWORD(dwLo32)))) goto DONE;
+ if(!Eeprom_EnableEraseAndWrite(privateData)) goto DONE;
+ if(!Eeprom_WriteLocation(privateData,3,LOBYTE(HIWORD(dwLo32)))) goto DONE;
+ if(!Eeprom_EnableEraseAndWrite(privateData)) goto DONE;
+ if(!Eeprom_WriteLocation(privateData,4,HIBYTE(HIWORD(dwLo32)))) goto DONE;
+ if(!Eeprom_EnableEraseAndWrite(privateData)) goto DONE;
+ if(!Eeprom_WriteLocation(privateData,5,LOBYTE(LOWORD(dwHi16)))) goto DONE;
+ if(!Eeprom_EnableEraseAndWrite(privateData)) goto DONE;
+ if(!Eeprom_WriteLocation(privateData,6,HIBYTE(LOWORD(dwHi16)))) goto DONE;
+ } else {
+ if(!Eeprom_WriteLocation(privateData,0,0xA5)) goto DONE;
+ if(!Eeprom_WriteLocation(privateData,1,LOBYTE(LOWORD(dwLo32)))) goto DONE;
+ if(!Eeprom_WriteLocation(privateData,2,HIBYTE(LOWORD(dwLo32)))) goto DONE;
+ if(!Eeprom_WriteLocation(privateData,3,LOBYTE(HIWORD(dwLo32)))) goto DONE;
+ if(!Eeprom_WriteLocation(privateData,4,HIBYTE(HIWORD(dwLo32)))) goto DONE;
+ if(!Eeprom_WriteLocation(privateData,5,LOBYTE(LOWORD(dwHi16)))) goto DONE;
+ if(!Eeprom_WriteLocation(privateData,6,HIBYTE(LOWORD(dwHi16)))) goto DONE;
+ }
+ if(!Eeprom_DisableEraseAndWrite(privateData)) goto DONE;
+
+ if(!Eeprom_Reload(privateData)) goto DONE;
+ if(!Eeprom_IsMacAddressLoaded(privateData)) goto DONE;
+ {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ if(dwHi16!=Mac_GetRegDW(privateData,ADDRH,keyCode)) goto DONE;
+ if(dwLo32!=Mac_GetRegDW(privateData,ADDRL,keyCode)) goto DONE;
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ }
+ result=true;
+DONE:
+ Eeprom_DisableAccess(privateData);
+ return result;
+}
+
+volatile u32 g_GpioSetting=0x00000000UL;
+#ifdef USE_LED1_WORK_AROUND
+volatile u32 g_GpioSettingOriginal=0x00000000UL;
+#endif
+
+bool Lan_Initialize(
+ PPRIVATE_DATA privateData,
+ u32 dwIntCfg,
+ u32 dwTxFifSz,
+ u32 dwAfcCfg)
+{
+ bool result=false;
+ u32 dwTimeOut=0;
+ u32 dwTemp=0;
+ u32 dwResetCount=3;
+
+ SMSC_TRACE("-->Lan_Initialize(dwIntCfg=0x%08X)",dwIntCfg);
+ SMSC_ASSERT(privateData!=NULL);
+
+ //Reset the LAN9118
+ if(privateData->dwGeneration>0) {
+ dwResetCount=1;
+ }
+ while(dwResetCount>0) {
+ Lan_SetRegDW(HW_CFG,HW_CFG_SRST_);
+ dwTimeOut=1000000;
+ do {
+ udelay(10);
+ dwTemp=Lan_GetRegDW(HW_CFG);
+ dwTimeOut--;
+ } while((dwTimeOut>0)&&(dwTemp&HW_CFG_SRST_));
+ if(dwTemp&HW_CFG_SRST_) {
+ SMSC_WARNING(" Failed to complete reset.");
+ goto DONE;
+ }
+ dwResetCount--;
+ }
+
+ SMSC_ASSERT(dwTxFifSz>=0x00020000UL);
+ SMSC_ASSERT(dwTxFifSz<=0x000E0000UL);
+ SMSC_ASSERT((dwTxFifSz&(~HW_CFG_TX_FIF_SZ_))==0);
+ Lan_SetRegDW(HW_CFG,dwTxFifSz);
+ privateData->RxMaxDataFifoSize=0;
+ switch(dwTxFifSz>>16) {
+ case 2:privateData->RxMaxDataFifoSize=13440;break;
+ case 3:privateData->RxMaxDataFifoSize=12480;break;
+ case 4:privateData->RxMaxDataFifoSize=11520;break;
+ case 5:privateData->RxMaxDataFifoSize=10560;break;
+ case 6:privateData->RxMaxDataFifoSize=9600;break;
+ case 7:privateData->RxMaxDataFifoSize=8640;break;
+ case 8:privateData->RxMaxDataFifoSize=7680;break;
+ case 9:privateData->RxMaxDataFifoSize=6720;break;
+ case 10:privateData->RxMaxDataFifoSize=5760;break;
+ case 11:privateData->RxMaxDataFifoSize=4800;break;
+ case 12:privateData->RxMaxDataFifoSize=3840;break;
+ case 13:privateData->RxMaxDataFifoSize=2880;break;
+ case 14:privateData->RxMaxDataFifoSize=1920;break;
+ default:SMSC_ASSERT(false);break;
+ }
+
+ if(dwAfcCfg==0xFFFFFFFF) {
+ switch(dwTxFifSz) {
+
+ //AFC_HI is about ((Rx Data Fifo Size)*2/3)/64
+ //AFC_LO is AFC_HI/2
+ //BACK_DUR is about 5uS*(AFC_LO) rounded down
+ case 0x00020000UL://13440 Rx Data Fifo Size
+ dwAfcCfg=0x008C46AF;break;
+ case 0x00030000UL://12480 Rx Data Fifo Size
+ dwAfcCfg=0x0082419F;break;
+ case 0x00040000UL://11520 Rx Data Fifo Size
+
+ dwAfcCfg=0x00783C9F;break;
+ case 0x00050000UL://10560 Rx Data Fifo Size
+ // dwAfcCfg=0x006E378F;break;
+ dwAfcCfg=0x006E374F;break;
+ case 0x00060000UL:// 9600 Rx Data Fifo Size
+ dwAfcCfg=0x0064328F;break;
+ case 0x00070000UL:// 8640 Rx Data Fifo Size
+ dwAfcCfg=0x005A2D7F;break;
+ case 0x00080000UL:// 7680 Rx Data Fifo Size
+ dwAfcCfg=0x0050287F;break;
+ case 0x00090000UL:// 6720 Rx Data Fifo Size
+ dwAfcCfg=0x0046236F;break;
+ case 0x000A0000UL:// 5760 Rx Data Fifo Size
+ dwAfcCfg=0x003C1E6F;break;
+ case 0x000B0000UL:// 4800 Rx Data Fifo Size
+ dwAfcCfg=0x0032195F;break;
+
+ //AFC_HI is ~1520 bytes less than RX Data Fifo Size
+ //AFC_LO is AFC_HI/2
+ //BACK_DUR is about 5uS*(AFC_LO) rounded down
+ case 0x000C0000UL:// 3840 Rx Data Fifo Size
+ dwAfcCfg=0x0024124F;break;
+ case 0x000D0000UL:// 2880 Rx Data Fifo Size
+ dwAfcCfg=0x0015073F;break;
+ case 0x000E0000UL:// 1920 Rx Data Fifo Size
+ dwAfcCfg=0x0006032F;break;
+ default:SMSC_ASSERT(false);break;
+ }
+ }
+ Lan_SetRegDW(AFC_CFG,(dwAfcCfg&0xFFFFFFF0UL));
+
+ //make sure EEPROM has finished loading before setting GPIO_CFG
+ dwTimeOut=1000;
+ while((dwTimeOut>0)&&(Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_)) {
+ udelay(5);
+ dwTimeOut--;
+ }
+ if(dwTimeOut==0) {
+ SMSC_WARNING("Lan_Initialize: Timed out waiting for EEPROM busy bit to clear\n");
+ }
+
+ if(debug_mode&0x04UL) {
+ if(OLD_REGISTERS(privateData))
+ {
+ g_GpioSetting=0x00270700UL;
+ } else {
+ g_GpioSetting=0x00670700UL;
+ }
+ } else {
+ g_GpioSetting = 0x70070000UL;
+ }
+ Lan_SetRegDW(GPIO_CFG,g_GpioSetting);
+
+ //initialize interrupts
+ Lan_SetRegDW(INT_EN,0);
+ Lan_SetRegDW(INT_STS,0xFFFFFFFFUL);
+ dwIntCfg|=INT_CFG_IRQ_EN_;
+ Lan_SetRegDW(INT_CFG,dwIntCfg);
+
+ Vl_InitLock(&(privateData->MacPhyLock));
+ spin_lock_init(&(privateData->IntEnableLock));
+ privateData->LanInitialized=true;
+
+ result=true;
+
+DONE:
+ SMSC_TRACE("<--Lan_Initialize");
+ return result;
+}
+
+void Lan_EnableInterrupt(PPRIVATE_DATA privateData,u32 dwIntEnMask)
+{
+ unsigned long dwIntFlags=0;
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(privateData->LanInitialized==true);
+ spin_lock_irqsave(&(privateData->IntEnableLock),dwIntFlags);
+ Lan_SetBitsDW(INT_EN,dwIntEnMask);
+ spin_unlock_irqrestore(&(privateData->IntEnableLock),dwIntFlags);
+}
+
+void Lan_DisableInterrupt(PPRIVATE_DATA privateData,u32 dwIntEnMask)
+{
+ unsigned long dwIntFlags=0;
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(privateData->LanInitialized==true);
+ spin_lock_irqsave(&(privateData->IntEnableLock),dwIntFlags);
+ Lan_ClrBitsDW(INT_EN,dwIntEnMask);
+ spin_unlock_irqrestore(&(privateData->IntEnableLock),dwIntFlags);
+}
+
+//Spin locks for the following functions have been commented out
+// because at this time they are not necessary.
+//These function are
+// Lan_SetTDFL
+// Lan_SetTSFL
+// Lan_SetRDFL
+// Lan_SetRSFL
+//Both the Rx and Tx side of the driver use the FIFO_INT,
+// but the Rx side only touches is during initialization,
+// so it is sufficient that Tx side simple preserve the Rx setting
+
+void Lan_SetTDFL(PPRIVATE_DATA privateData,BYTE level) {
+ // unsigned long dwIntFlags=0;
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(privateData->LanInitialized==true);
+ // spin_lock_irqsave(&(privateData->IntEnableLock),dwIntFlags);
+ {
+ u32 temp=Lan_GetRegDW(FIFO_INT);
+ temp&=0x00FFFFFFUL;
+ temp|=((u32)level)<<24;
+ Lan_SetRegDW(FIFO_INT,temp);
+ }
+ // spin_unlock_irqrestore(&(privateData->IntEnableLock),dwIntFlags);
+}
+
+void Lan_SetTSFL(PPRIVATE_DATA privateData,BYTE level) {
+ // unsigned long dwIntFlags=0;
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(privateData->LanInitialized==true);
+ // spin_lock_irqsave(&(privateData->IntEnableLock),dwIntFlags);
+ {
+ u32 temp=Lan_GetRegDW(FIFO_INT);
+ temp&=0xFF00FFFFUL;
+ temp|=((u32)level)<<16;
+ Lan_SetRegDW(FIFO_INT,temp);
+ }
+ // spin_unlock_irqrestore(&(privateData->IntEnableLock),dwIntFlags);
+}
+
+void Lan_SetRDFL(PPRIVATE_DATA privateData,BYTE level) {
+ // unsigned long dwIntFlags=0;
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(privateData->LanInitialized==true);
+ // spin_lock_irqsave(&(privateData->IntEnableLock),dwIntFlags);
+ {
+ u32 temp=Lan_GetRegDW(FIFO_INT);
+ temp&=0xFFFF00FFUL;
+ temp|=((u32)level)<<8;
+ Lan_SetRegDW(FIFO_INT,temp);
+ }
+ // spin_unlock_irqrestore(&(privateData->IntEnableLock),dwIntFlags);
+}
+
+void Lan_SetRSFL(PPRIVATE_DATA privateData,BYTE level) {
+ // unsigned long dwIntFlags=0;
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(privateData->LanInitialized==true);
+ // spin_lock_irqsave(&(privateData->IntEnableLock),dwIntFlags);
+ {
+ u32 temp=Lan_GetRegDW(FIFO_INT);
+ temp&=0xFFFFFF00UL;
+ temp|=((u32)level);
+ Lan_SetRegDW(FIFO_INT,temp);
+ }
+ // spin_unlock_irqrestore(&(privateData->IntEnableLock),dwIntFlags);
+}
+
+void Lan_EnableIRQ(PPRIVATE_DATA privateData)
+{
+ unsigned long dwIntFlags=0;
+ spin_lock_irqsave(&(privateData->IntEnableLock),dwIntFlags);
+ {
+ Lan_SetBitsDW(INT_CFG,INT_CFG_IRQ_EN_);
+ }
+ spin_unlock_irqrestore(&(privateData->IntEnableLock),dwIntFlags);
+}
+
+void Lan_DisableIRQ(PPRIVATE_DATA privateData)
+{
+ unsigned long dwIntFlags=0;
+ spin_lock_irqsave(&(privateData->IntEnableLock),dwIntFlags);
+ {
+ Lan_ClrBitsDW(INT_CFG,INT_CFG_IRQ_EN_);
+ }
+ spin_unlock_irqrestore(&(privateData->IntEnableLock),dwIntFlags);
+}
+
+void Lan_SetIntDeas(PPRIVATE_DATA privateData, u32 dwIntDeas)
+{
+ unsigned long dwIntFlags=0;
+ spin_lock_irqsave(&(privateData->IntEnableLock),dwIntFlags);
+ {
+ Lan_ClrBitsDW(INT_CFG,INT_CFG_INT_DEAS_);
+ Lan_SetBitsDW(INT_CFG,(dwIntDeas<<24));
+ }
+ spin_unlock_irqrestore(&(privateData->IntEnableLock),dwIntFlags);
+}
+
+void Lan_SignalSoftwareInterrupt(PPRIVATE_DATA privateData)
+{
+ SMSC_ASSERT(privateData!=NULL);
+ SMSC_ASSERT(privateData->dwLanBase!=0);
+ privateData->SoftwareInterruptSignal=false;
+ Lan_EnableInterrupt(privateData,INT_EN_SW_INT_EN_);
+}
+
+bool Lan_HandleSoftwareInterrupt(
+ PPRIVATE_DATA privateData,
+ u32 dwIntSts)
+{
+ if(dwIntSts&INT_STS_SW_INT_) {
+ SMSC_TRACE("Got SW Interrupt (privateData=%08X)", (u32)privateData);
+ SMSC_ASSERT(privateData!=NULL);
+ Lan_DisableInterrupt(privateData,INT_EN_SW_INT_EN_);
+ Lan_SetRegDW(INT_STS,INT_STS_SW_INT_);
+ privateData->SoftwareInterruptSignal=true;
+ if (privateData->MulticastUpdatePending) {
+ Rx_BeginMulticastUpdate (privateData);
+ }
+ return true;
+ }
+ return false;
+}
+
+typedef struct _SHOW_REG
+{
+ char szName[20];
+ u32 dwOffset;
+} SHOW_REG;
+/*
+FUNCTION: Lan_ShowRegs
+This function is used to display the registers.
+Except the phy.
+*/
+void Lan_ShowRegs(PPRIVATE_DATA privateData)
+{
+ // Make these const struct's static to keep them off the stack.
+ // Otherwise, gcc will try to use _memcpy() to initialize them,
+ // which will *NOT* work in our RunTime environment.
+ static const SHOW_REG sysCsr[] = {
+ { "ID_REV", 0x50UL },
+ { "INT_CFG", 0x54UL },
+ { "INT_STS", 0x58UL },
+ { "INT_EN", 0x5CUL },
+ { "DMA_CFG", 0x60UL },
+ { "BYTE_TEST", 0x64UL },
+ { "FIFO_INT", 0x68UL },
+ { "RX_CFG", 0x6CUL },
+ { "TX_CFG", 0x70UL },
+ { "HW_CFG", 0x74UL },
+ { "RX_DP_CTRL", 0x78UL },
+ { "RX_FIFO_INF", 0x7CUL },
+ { "TX_FIFO_INF", 0x80UL },
+ { "PMT_CTRL", 0x84UL },
+ { "GPIO_CFG", 0x88UL },
+ { "GPT_CFG", 0x8CUL },
+ { "GPT_CNT", 0x90UL },
+ { "FPGA_REV", 0x94UL },
+ { "WORD_SWAP", 0x98UL },
+ { "FREE_RUN", 0x9CUL },
+ { "RX_DROP", 0xA0UL },
+ { "MAC_CSR_CMD", 0xA4UL },
+ { "MAC_CSR_DATA", 0xA8UL },
+ { "AFC_CFG", 0xACUL },
+ { "E2P_CMD", 0xB0UL },
+ { "E2P_DATA", 0xB4UL },
+ { "TEST_REG_A", 0xC0UL }};
+
+ static const SHOW_REG macCsr[] = {
+ { "MAC_CR", MAC_CR },
+ { "ADDRH", ADDRH },
+ { "ADDRL", ADDRL },
+ { "HASHH", HASHH },
+ { "HASHL", HASHL },
+ { "MII_ACC", MII_ACC },
+ { "MII_DATA", MII_DATA },
+ { "FLOW", FLOW },
+ { "VLAN1", VLAN1 },
+ { "VLAN2", VLAN2 },
+ { "WUFF", WUFF },
+ { "WUCSR", WUCSR }};
+
+ int i, iNumSysRegs, iNumMacRegs;
+ u32 dwOldMacCmdReg, dwOldMacDataReg;
+
+ iNumSysRegs = (int)(sizeof(sysCsr) / sizeof(SHOW_REG));
+ iNumMacRegs = (int)(sizeof(macCsr) / sizeof(SHOW_REG));
+
+ // preserve MAC cmd/data reg's
+ dwOldMacCmdReg = Lan_GetRegDW(MAC_CSR_CMD);
+ dwOldMacDataReg = Lan_GetRegDW(MAC_CSR_DATA);
+
+ SMSC_TRACE("");
+ SMSC_TRACE(" LAN91C118 CSR's");
+ SMSC_TRACE(" SYS CSR's MAC CSR's");
+
+ {
+ unsigned long dwIntFlags=0;
+ VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags);
+ for (i=0; i<iNumMacRegs; i++)
+ {
+ SMSC_TRACE(
+ "%16s (0x%02X) = 0x%08X, %8s (0x%02X) + 0x%08X",
+ sysCsr[i].szName,
+ sysCsr[i].dwOffset,
+ *((volatile u32 *)(privateData->dwLanBase+sysCsr[i].dwOffset)),
+ macCsr[i].szName,
+ macCsr[i].dwOffset,
+ Mac_GetRegDW(privateData,macCsr[i].dwOffset,keyCode));
+
+ // restore original mac cmd/data reg's after each usage
+ Lan_SetRegDW(MAC_CSR_CMD,dwOldMacCmdReg);
+ Lan_SetRegDW(MAC_CSR_DATA,dwOldMacDataReg);
+ }
+ Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags);
+ }
+ for (i=iNumMacRegs; i<iNumSysRegs; i++)
+ {
+ SMSC_TRACE("%16s (0x%02X) = 0x%08X",
+ sysCsr[i].szName,
+ sysCsr[i].dwOffset,
+ *((volatile u32 *)(privateData->dwLanBase+sysCsr[i].dwOffset)));
+ }
+}
+
+void Vl_InitLock(PVERIFIABLE_LOCK pVl)
+{
+ SMSC_ASSERT(pVl!=NULL);
+ spin_lock_init(&(pVl->Lock));
+ pVl->KeyCode=0;
+}
+
+bool Vl_CheckLock(PVERIFIABLE_LOCK pVl,VL_KEY keyCode)
+{
+ bool result=false;
+ SMSC_ASSERT(pVl!=NULL);
+ if(keyCode==pVl->KeyCode)
+ result=true;
+ return result;
+}
+
+VL_KEY Vl_WaitForLock(PVERIFIABLE_LOCK pVl,unsigned long *pdwIntFlags)
+{
+ VL_KEY result=0;
+ SMSC_ASSERT(pVl!=NULL);
+ spin_lock_irqsave(
+ &(pVl->Lock),
+ (*pdwIntFlags));
+ pVl->KeyCode++;
+ if(pVl->KeyCode>0x80000000UL) {
+ pVl->KeyCode=1;
+ }
+ result=pVl->KeyCode;
+ return result;
+}
+
+void Vl_ReleaseLock(PVERIFIABLE_LOCK pVl,VL_KEY keyCode,unsigned long *pdwIntFlags)
+{
+ SMSC_ASSERT(pVl!=NULL);
+ SMSC_ASSERT(pVl->KeyCode==keyCode);
+ spin_unlock_irqrestore(&(pVl->Lock),(*pdwIntFlags));
+}
+
+#ifndef USING_LINT
+module_init(Smsc9118_init_module);
+module_exit(Smsc9118_cleanup_module);
+#endif
--
1.5.4.3
^ permalink raw reply related [flat|nested] 26+ messages in thread