diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2010-10-28 09:44:56 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-10-28 09:44:56 -0700 |
commit | e4c5bf8e3dca827a1b3a6fac494eae8c74b7e1e7 (patch) | |
tree | ea51b391f7d74ca695dcb9f5e46eb02688a92ed9 /drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c | |
parent | 81280572ca6f54009edfa4deee563e8678784218 (diff) | |
parent | a4ac0d847af9dd34d5953a5e264400326144b6b2 (diff) | |
download | op-kernel-dev-e4c5bf8e3dca827a1b3a6fac494eae8c74b7e1e7.zip op-kernel-dev-e4c5bf8e3dca827a1b3a6fac494eae8c74b7e1e7.tar.gz |
Merge 'staging-next' to Linus's tree
This merges the staging-next tree to Linus's tree and resolves
some conflicts that were present due to changes in other trees that were
affected by files here.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c')
-rw-r--r-- | drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c | 2294 |
1 files changed, 2294 insertions, 0 deletions
diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c new file mode 100644 index 0000000..eed7e94 --- /dev/null +++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c @@ -0,0 +1,2294 @@ +/*--------------------------------------------------------------------------- + FT1000 driver for Flarion Flash OFDM NIC Device + + Copyright (C) 2002 Flarion Technologies, All rights reserved. + Copyright (C) 2006 Patrik Ostrihon, All rights reserved. + Copyright (C) 2006 ProWeb Consulting, a.s, All rights reserved. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) any + later version. This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License along with this program; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - + Suite 330, Boston, MA 02111-1307, USA. +-----------------------------------------------------------------------------*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/proc_fs.h> + +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/in.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/bitops.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/if_arp.h> +#include <linux/ioport.h> +#include <linux/wait.h> +#include <linux/vmalloc.h> + +#include <linux/firmware.h> +#include <linux/ethtool.h> + +#ifdef FT_DEBUG +#define DEBUG(n, args...) printk(KERN_DEBUG args); +#else +#define DEBUG(n, args...) +#endif + +#include <linux/delay.h> +#include "ft1000_dev.h" +#include "ft1000.h" + +int card_download(struct net_device *dev, void *pFileStart, UINT FileLength); + +void ft1000InitProc(struct net_device *dev); +void ft1000CleanupProc(struct net_device *dev); + +const struct firmware *fw_entry; + +static void ft1000_hbchk(u_long data); +static struct timer_list poll_timer = { + function:ft1000_hbchk +}; + +static u16 cmdbuffer[1024]; +static u8 tempbuffer[1600]; +static u8 ft1000_card_present = 0; +static u8 flarion_ft1000_cnt = 0; + +static irqreturn_t ft1000_interrupt(int irq, void *dev_id); +static void ft1000_enable_interrupts(struct net_device *dev); +static void ft1000_disable_interrupts(struct net_device *dev); + +/* new kernel */ +MODULE_AUTHOR(""); +MODULE_DESCRIPTION + ("Support for Flarion Flash OFDM NIC Device. Support for PCMCIA when used with ft1000_cs."); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("FT1000"); + +#define MAX_RCV_LOOP 100 + +//--------------------------------------------------------------------------- +// +// Function: ft1000_asic_read +// Descripton: This function will retrieve the value of a specific ASIC +// register. +// Input: +// dev - network device structure +// offset - ASIC register to read +// Output: +// value - value of ASIC register +// +//--------------------------------------------------------------------------- +inline u16 ft1000_asic_read(struct net_device *dev, u16 offset) +{ + return (ft1000_read_reg(dev, offset)); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_asic_write +// Descripton: This function will set the value of a specific ASIC +// register. +// Input: +// dev - network device structure +// value - value to set ASIC register +// Output: +// none +// +//--------------------------------------------------------------------------- +inline void ft1000_asic_write(struct net_device *dev, u16 offset, u16 value) +{ + ft1000_write_reg(dev, offset, value); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_read_fifo_len +// Descripton: This function will read the ASIC Uplink FIFO status register +// which will return the number of bytes remaining in the Uplink FIFO. +// Sixteen bytes are subtracted to make sure that the ASIC does not +// reach its threshold. +// Input: +// dev - network device structure +// Output: +// value - number of bytes available in the ASIC Uplink FIFO. +// +//--------------------------------------------------------------------------- +static inline u16 ft1000_read_fifo_len(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + + if (info->AsicID == ELECTRABUZZ_ID) { + return (ft1000_read_reg(dev, FT1000_REG_UFIFO_STAT) - 16); + } else { + return (ft1000_read_reg(dev, FT1000_REG_MAG_UFSR) - 16); + } +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_read_dpram +// Descripton: This function will read the specific area of dpram +// (Electrabuzz ASIC only) +// Input: +// dev - device structure +// offset - index of dpram +// Output: +// value - value of dpram +// +//--------------------------------------------------------------------------- +u16 ft1000_read_dpram(struct net_device * dev, int offset) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + unsigned long flags; + u16 data; + + // Provide mutual exclusive access while reading ASIC registers. + spin_lock_irqsave(&info->dpram_lock, flags); + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); + data = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); + spin_unlock_irqrestore(&info->dpram_lock, flags); + + return (data); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_write_dpram +// Descripton: This function will write to a specific area of dpram +// (Electrabuzz ASIC only) +// Input: +// dev - device structure +// offset - index of dpram +// value - value to write +// Output: +// none. +// +//--------------------------------------------------------------------------- +static inline void ft1000_write_dpram(struct net_device *dev, + int offset, u16 value) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + unsigned long flags; + + // Provide mutual exclusive access while reading ASIC registers. + spin_lock_irqsave(&info->dpram_lock, flags); + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, value); + spin_unlock_irqrestore(&info->dpram_lock, flags); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_read_dpram_mag_16 +// Descripton: This function will read the specific area of dpram +// (Magnemite ASIC only) +// Input: +// dev - device structure +// offset - index of dpram +// Output: +// value - value of dpram +// +//--------------------------------------------------------------------------- +u16 ft1000_read_dpram_mag_16(struct net_device *dev, int offset, int Index) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + unsigned long flags; + u16 data; + + // Provide mutual exclusive access while reading ASIC registers. + spin_lock_irqsave(&info->dpram_lock, flags); + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); + // check if we want to read upper or lower 32-bit word + if (Index) { + data = ft1000_read_reg(dev, FT1000_REG_MAG_DPDATAL); + } else { + data = ft1000_read_reg(dev, FT1000_REG_MAG_DPDATAH); + } + spin_unlock_irqrestore(&info->dpram_lock, flags); + + return (data); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_write_dpram_mag_16 +// Descripton: This function will write to a specific area of dpram +// (Magnemite ASIC only) +// Input: +// dev - device structure +// offset - index of dpram +// value - value to write +// Output: +// none. +// +//--------------------------------------------------------------------------- +static inline void ft1000_write_dpram_mag_16(struct net_device *dev, + int offset, u16 value, int Index) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + unsigned long flags; + + // Provide mutual exclusive access while reading ASIC registers. + spin_lock_irqsave(&info->dpram_lock, flags); + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); + if (Index) { + ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAL, value); + } else { + ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAH, value); + } + spin_unlock_irqrestore(&info->dpram_lock, flags); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_read_dpram_mag_32 +// Descripton: This function will read the specific area of dpram +// (Magnemite ASIC only) +// Input: +// dev - device structure +// offset - index of dpram +// Output: +// value - value of dpram +// +//--------------------------------------------------------------------------- +u32 ft1000_read_dpram_mag_32(struct net_device *dev, int offset) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + unsigned long flags; + u32 data; + + // Provide mutual exclusive access while reading ASIC registers. + spin_lock_irqsave(&info->dpram_lock, flags); + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); + data = inl(dev->base_addr + FT1000_REG_MAG_DPDATAL); + spin_unlock_irqrestore(&info->dpram_lock, flags); + + return (data); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_write_dpram_mag_32 +// Descripton: This function will write to a specific area of dpram +// (Magnemite ASIC only) +// Input: +// dev - device structure +// offset - index of dpram +// value - value to write +// Output: +// none. +// +//--------------------------------------------------------------------------- +void ft1000_write_dpram_mag_32(struct net_device *dev, int offset, u32 value) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + unsigned long flags; + + // Provide mutual exclusive access while reading ASIC registers. + spin_lock_irqsave(&info->dpram_lock, flags); + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); + outl(value, dev->base_addr + FT1000_REG_MAG_DPDATAL); + spin_unlock_irqrestore(&info->dpram_lock, flags); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_enable_interrupts +// Descripton: This function will enable interrupts base on the current interrupt mask. +// Input: +// dev - device structure +// Output: +// None. +// +//--------------------------------------------------------------------------- +static void ft1000_enable_interrupts(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 tempword; + + DEBUG(1, "ft1000_hw:ft1000_enable_interrupts()\n"); + ft1000_write_reg(dev, FT1000_REG_SUP_IMASK, + info->CurrentInterruptEnableMask); + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK); + DEBUG(1, + "ft1000_hw:ft1000_enable_interrupts:current interrupt enable mask = 0x%x\n", + tempword); + info->InterruptsEnabled = TRUE; +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_disable_interrupts +// Descripton: This function will disable all interrupts. +// Input: +// dev - device structure +// Output: +// None. +// +//--------------------------------------------------------------------------- +static void ft1000_disable_interrupts(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 tempword; + + DEBUG(1, "ft1000_hw: ft1000_disable_interrupts()\n"); + ft1000_write_reg(dev, FT1000_REG_SUP_IMASK, ISR_MASK_ALL); + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK); + DEBUG(1, + "ft1000_hw:ft1000_disable_interrupts:current interrupt enable mask = 0x%x\n", + tempword); + info->InterruptsEnabled = FALSE; +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_reset_asic +// Descripton: This function will call the Card Service function to reset the +// ASIC. +// Input: +// dev - device structure +// Output: +// none +// +//--------------------------------------------------------------------------- +static void ft1000_reset_asic(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 tempword; + + DEBUG(1, "ft1000_hw:ft1000_reset_asic called\n"); + + (*info->ft1000_reset) (info->link); + info->ASICResetNum++; + + // Let's use the register provided by the Magnemite ASIC to reset the + // ASIC and DSP. + if (info->AsicID == MAGNEMITE_ID) { + ft1000_write_reg(dev, FT1000_REG_RESET, + (DSP_RESET_BIT | ASIC_RESET_BIT)); + } + mdelay(1); + if (info->AsicID == ELECTRABUZZ_ID) { + // set watermark to -1 in order to not generate an interrrupt + ft1000_write_reg(dev, FT1000_REG_WATERMARK, 0xffff); + } else { + // set watermark to -1 in order to not generate an interrrupt + ft1000_write_reg(dev, FT1000_REG_MAG_WATERMARK, 0xffff); + } + // clear interrupts + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR); + DEBUG(1, "ft1000_hw: interrupt status register = 0x%x\n", tempword); + ft1000_write_reg(dev, FT1000_REG_SUP_ISR, tempword); + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR); + DEBUG(1, "ft1000_hw: interrupt status register = 0x%x\n", tempword); + +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_reset_card +// Descripton: This function will reset the card +// Input: +// dev - device structure +// Output: +// status - FALSE (card reset fail) +// TRUE (card reset successful) +// +//--------------------------------------------------------------------------- +static int ft1000_reset_card(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 tempword; + int i; + unsigned long flags; + PPROV_RECORD ptr; + + DEBUG(1, "ft1000_hw:ft1000_reset_card called.....\n"); + + info->CardReady = 0; + info->ProgConStat = 0; + info->squeseqnum = 0; + ft1000_disable_interrupts(dev); + +// del_timer(&poll_timer); + + // Make sure we free any memory reserve for provisioning + while (list_empty(&info->prov_list) == 0) { + DEBUG(0, + "ft1000_hw:ft1000_reset_card:deleting provisioning record\n"); + ptr = list_entry(info->prov_list.next, PROV_RECORD, list); + list_del(&ptr->list); + kfree(ptr->pprov_data); + kfree(ptr); + } + + if (info->AsicID == ELECTRABUZZ_ID) { + DEBUG(1, "ft1000_hw:ft1000_reset_card:resetting DSP\n"); + ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT); + } else { + DEBUG(1, + "ft1000_hw:ft1000_reset_card:resetting ASIC and DSP\n"); + ft1000_write_reg(dev, FT1000_REG_RESET, + (DSP_RESET_BIT | ASIC_RESET_BIT)); + } + + // Copy DSP session record into info block if this is not a coldstart + if (ft1000_card_present == 1) { + spin_lock_irqsave(&info->dpram_lock, flags); + if (info->AsicID == ELECTRABUZZ_ID) { + if (info->DspHibernateFlag == 0) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_RX_BASE); + for (i = 0; i < MAX_DSP_SESS_REC; i++) { + info->DSPSess.Rec[i] = + ft1000_read_reg(dev, + FT1000_REG_DPRAM_DATA); + } + } + } else { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_MAG_RX_BASE); + for (i = 0; i < MAX_DSP_SESS_REC / 2; i++) { + info->DSPSess.MagRec[i] = + inl(dev->base_addr + FT1000_REG_MAG_DPDATA); + } + } + spin_unlock_irqrestore(&info->dpram_lock, flags); + } + + DEBUG(1, "ft1000_hw:ft1000_reset_card:resetting ASIC\n"); + mdelay(10); + //reset ASIC + ft1000_reset_asic(dev); + + info->DSPResetNum++; + + DEBUG(1, "ft1000_hw:ft1000_reset_card:downloading dsp image\n"); + + if (info->AsicID == MAGNEMITE_ID) { + // Put dsp in reset and take ASIC out of reset + DEBUG(0, + "ft1000_hw:ft1000_reset_card:Put DSP in reset and take ASIC out of reset\n"); + ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT); + + // Setting MAGNEMITE ASIC to big endian mode + ft1000_write_reg(dev, FT1000_REG_SUP_CTRL, HOST_INTF_BE); + // Download bootloader + card_bootload(dev); + + // Take DSP out of reset + ft1000_write_reg(dev, FT1000_REG_RESET, 0); + // FLARION_DSP_ACTIVE; + mdelay(10); + DEBUG(0, "ft1000_hw:ft1000_reset_card:Take DSP out of reset\n"); + + // Wait for 0xfefe indicating dsp ready before starting download + for (i = 0; i < 50; i++) { + tempword = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DPRAM_FEFE, + FT1000_MAG_DPRAM_FEFE_INDX); + if (tempword == 0xfefe) { + break; + } + mdelay(20); + } + + if (i == 50) { + DEBUG(0, + "ft1000_hw:ft1000_reset_card:No FEFE detected from DSP\n"); + return FALSE; + } + + } else { + // Take DSP out of reset + ft1000_write_reg(dev, FT1000_REG_RESET, ~DSP_RESET_BIT); + mdelay(10); + } + + if (card_download(dev, fw_entry->data, fw_entry->size)) { + DEBUG(1, "card download unsuccessful\n"); + return FALSE; + } else { + DEBUG(1, "card download successful\n"); + } + + mdelay(10); + + if (info->AsicID == ELECTRABUZZ_ID) { + // Need to initialize the FIFO length counter to zero in order to sync up + // with the DSP + info->fifo_cnt = 0; + ft1000_write_dpram(dev, FT1000_FIFO_LEN, info->fifo_cnt); + // Initialize DSP heartbeat area to ho + ft1000_write_dpram(dev, FT1000_HI_HO, ho); + tempword = ft1000_read_dpram(dev, FT1000_HI_HO); + DEBUG(1, "ft1000_hw:ft1000_reset_asic:hi_ho value = 0x%x\n", + tempword); + } else { + // Initialize DSP heartbeat area to ho + ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, ho_mag, + FT1000_MAG_HI_HO_INDX); + tempword = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_HI_HO, + FT1000_MAG_HI_HO_INDX); + DEBUG(1, "ft1000_hw:ft1000_reset_card:hi_ho value = 0x%x\n", + tempword); + } + + info->CardReady = 1; + ft1000_enable_interrupts(dev); + + /* Schedule heartbeat process to run every 2 seconds */ +// poll_timer.expires = jiffies + (2*HZ); +// poll_timer.data = (u_long)dev; +// add_timer(&poll_timer); + + return TRUE; + +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_chkcard +// Descripton: This function will check if the device is presently available on +// the system. +// Input: +// dev - device structure +// Output: +// status - FALSE (device is not present) +// TRUE (device is present) +// +//--------------------------------------------------------------------------- +static int ft1000_chkcard(struct net_device *dev) +{ + u16 tempword; + + // Mask register is used to check for device presence since it is never + // set to zero. + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK); + if (tempword == 0) { + DEBUG(1, + "ft1000_hw:ft1000_chkcard: IMASK = 0 Card not detected\n"); + return FALSE; + } + // The system will return the value of 0xffff for the version register + // if the device is not present. + tempword = ft1000_read_reg(dev, FT1000_REG_ASIC_ID); + if (tempword == 0xffff) { + DEBUG(1, + "ft1000_hw:ft1000_chkcard: Version = 0xffff Card not detected\n"); + return FALSE; + } + return TRUE; +} + + +//--------------------------------------------------------------------------- +// +// Function: ft1000_hbchk +// Descripton: This function will perform the heart beat check of the DSP as +// well as the ASIC. +// Input: +// dev - device structure +// Output: +// none +// +//--------------------------------------------------------------------------- +static void ft1000_hbchk(u_long data) +{ + struct net_device *dev = (struct net_device *)data; + + FT1000_INFO *info; + USHORT tempword; + + info = (FT1000_INFO *) netdev_priv(dev); + + if (info->CardReady == 1) { + // Perform dsp heartbeat check + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = ft1000_read_dpram(dev, FT1000_HI_HO); + } else { + tempword = + ntohs(ft1000_read_dpram_mag_16 + (dev, FT1000_MAG_HI_HO, + FT1000_MAG_HI_HO_INDX)); + } + DEBUG(1, "ft1000_hw:ft1000_hbchk:hi_ho value = 0x%x\n", + tempword); + // Let's perform another check if ho is not detected + if (tempword != ho) { + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = ft1000_read_dpram(dev, FT1000_HI_HO); + } + else { + tempword = ntohs(ft1000_read_dpram_mag_16(dev, FT1000_MAG_HI_HO, FT1000_MAG_HI_HO_INDX)); + } + } + if (tempword != ho) { + printk(KERN_INFO + "ft1000: heartbeat failed - no ho detected\n"); + if (info->AsicID == ELECTRABUZZ_ID) { + info->DSP_TIME[0] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER0); + info->DSP_TIME[1] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER1); + info->DSP_TIME[2] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER2); + info->DSP_TIME[3] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER3); + } else { + info->DSP_TIME[0] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER0, + FT1000_MAG_DSP_TIMER0_INDX); + info->DSP_TIME[1] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER1, + FT1000_MAG_DSP_TIMER1_INDX); + info->DSP_TIME[2] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER2, + FT1000_MAG_DSP_TIMER2_INDX); + info->DSP_TIME[3] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER3, + FT1000_MAG_DSP_TIMER3_INDX); + } + info->DrvErrNum = DSP_HB_INFO; + if (ft1000_reset_card(dev) == 0) { + printk(KERN_INFO + "ft1000: Hardware Failure Detected - PC Card disabled\n"); + info->ProgConStat = 0xff; + return; + } + /* Schedule this module to run every 2 seconds */ + poll_timer.expires = jiffies + (2*HZ); + poll_timer.data = (u_long)dev; + add_timer(&poll_timer); + return; + } + + tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + // Let's check doorbell again if fail + if (tempword & FT1000_DB_HB) { + tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + } + if (tempword & FT1000_DB_HB) { + printk(KERN_INFO + "ft1000: heartbeat doorbell not clear by firmware\n"); + if (info->AsicID == ELECTRABUZZ_ID) { + info->DSP_TIME[0] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER0); + info->DSP_TIME[1] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER1); + info->DSP_TIME[2] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER2); + info->DSP_TIME[3] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER3); + } else { + info->DSP_TIME[0] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER0, + FT1000_MAG_DSP_TIMER0_INDX); + info->DSP_TIME[1] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER1, + FT1000_MAG_DSP_TIMER1_INDX); + info->DSP_TIME[2] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER2, + FT1000_MAG_DSP_TIMER2_INDX); + info->DSP_TIME[3] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER3, + FT1000_MAG_DSP_TIMER3_INDX); + } + info->DrvErrNum = DSP_HB_INFO; + if (ft1000_reset_card(dev) == 0) { + printk(KERN_INFO + "ft1000: Hardware Failure Detected - PC Card disabled\n"); + info->ProgConStat = 0xff; + return; + } + /* Schedule this module to run every 2 seconds */ + poll_timer.expires = jiffies + (2*HZ); + poll_timer.data = (u_long)dev; + add_timer(&poll_timer); + return; + } + // Set dedicated area to hi and ring appropriate doorbell according + // to hi/ho heartbeat protocol + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_dpram(dev, FT1000_HI_HO, hi); + } else { + ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, hi_mag, + FT1000_MAG_HI_HO_INDX); + } + + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = ft1000_read_dpram(dev, FT1000_HI_HO); + } else { + tempword = + ntohs(ft1000_read_dpram_mag_16 + (dev, FT1000_MAG_HI_HO, + FT1000_MAG_HI_HO_INDX)); + } + // Let's write hi again if fail + if (tempword != hi) { + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_dpram(dev, FT1000_HI_HO, hi); + } + else { + ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, hi_mag, FT1000_MAG_HI_HO_INDX); + } + + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = ft1000_read_dpram(dev, FT1000_HI_HO); + } + else { + tempword = ntohs(ft1000_read_dpram_mag_16(dev, FT1000_MAG_HI_HO, FT1000_MAG_HI_HO_INDX)); + } + + } + + if (tempword != hi) { + printk(KERN_INFO + "ft1000: heartbeat failed - cannot write hi into DPRAM\n"); + if (info->AsicID == ELECTRABUZZ_ID) { + info->DSP_TIME[0] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER0); + info->DSP_TIME[1] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER1); + info->DSP_TIME[2] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER2); + info->DSP_TIME[3] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER3); + } else { + info->DSP_TIME[0] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER0, + FT1000_MAG_DSP_TIMER0_INDX); + info->DSP_TIME[1] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER1, + FT1000_MAG_DSP_TIMER1_INDX); + info->DSP_TIME[2] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER2, + FT1000_MAG_DSP_TIMER2_INDX); + info->DSP_TIME[3] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER3, + FT1000_MAG_DSP_TIMER3_INDX); + } + info->DrvErrNum = DSP_HB_INFO; + if (ft1000_reset_card(dev) == 0) { + printk(KERN_INFO + "ft1000: Hardware Failure Detected - PC Card disabled\n"); + info->ProgConStat = 0xff; + return; + } + /* Schedule this module to run every 2 seconds */ + poll_timer.expires = jiffies + (2*HZ); + poll_timer.data = (u_long)dev; + add_timer(&poll_timer); + return; + } + ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_HB); + + } + + /* Schedule this module to run every 2 seconds */ + poll_timer.expires = jiffies + (2 * HZ); + poll_timer.data = (u_long) dev; + add_timer(&poll_timer); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_send_cmd +// Descripton: +// Input: +// Output: +// +//--------------------------------------------------------------------------- +void ft1000_send_cmd (struct net_device *dev, u16 *ptempbuffer, int size, u16 qtype) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + int i; + u16 tempword; + unsigned long flags; + + size += PSEUDOSZ; + // check for odd byte and increment to 16-bit word align value + if ((size & 0x0001)) { + size++; + } + DEBUG(1, "FT1000:ft1000_send_cmd:total length = %d\n", size); + DEBUG(1, "FT1000:ft1000_send_cmd:length = %d\n", ntohs(*ptempbuffer)); + // put message into slow queue area + // All messages are in the form total_len + pseudo header + message body + spin_lock_irqsave(&info->dpram_lock, flags); + + // Make sure SLOWQ doorbell is clear + tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + i=0; + while (tempword & FT1000_DB_DPRAM_TX) { + mdelay(10); + i++; + if (i==10) { + spin_unlock_irqrestore(&info->dpram_lock, flags); + return; + } + tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + } + + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_TX_BASE); + // Write total length to dpram + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size); + // Write pseudo header and messgae body + for (i = 0; i < (size >> 1); i++) { + DEBUG(1, "FT1000:ft1000_send_cmd:data %d = 0x%x\n", i, + *ptempbuffer); + tempword = htons(*ptempbuffer++); + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, tempword); + } + } else { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_MAG_TX_BASE); + // Write total length to dpram + ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAH, htons(size)); + // Write pseudo header and messgae body + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_MAG_TX_BASE + 1); + for (i = 0; i < (size >> 2); i++) { + DEBUG(1, "FT1000:ft1000_send_cmd:data = 0x%x\n", + *ptempbuffer); + outw(*ptempbuffer++, + dev->base_addr + FT1000_REG_MAG_DPDATAL); + DEBUG(1, "FT1000:ft1000_send_cmd:data = 0x%x\n", + *ptempbuffer); + outw(*ptempbuffer++, + dev->base_addr + FT1000_REG_MAG_DPDATAH); + } + DEBUG(1, "FT1000:ft1000_send_cmd:data = 0x%x\n", *ptempbuffer); + outw(*ptempbuffer++, dev->base_addr + FT1000_REG_MAG_DPDATAL); + DEBUG(1, "FT1000:ft1000_send_cmd:data = 0x%x\n", *ptempbuffer); + outw(*ptempbuffer++, dev->base_addr + FT1000_REG_MAG_DPDATAH); + } + spin_unlock_irqrestore(&info->dpram_lock, flags); + + // ring doorbell to notify DSP that we have a message ready + ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_DPRAM_TX); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_receive_cmd +// Descripton: This function will read a message from the dpram area. +// Input: +// dev - network device structure +// pbuffer - caller supply address to buffer +// pnxtph - pointer to next pseudo header +// Output: +// Status = 0 (unsuccessful) +// = 1 (successful) +// +//--------------------------------------------------------------------------- +BOOLEAN ft1000_receive_cmd(struct net_device *dev, u16 * pbuffer, int maxsz, u16 *pnxtph) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 size; + u16 *ppseudohdr; + int i; + u16 tempword; + unsigned long flags; + + if (info->AsicID == ELECTRABUZZ_ID) { + size = ( ft1000_read_dpram(dev, *pnxtph) ) + PSEUDOSZ; + } else { + size = + ntohs(ft1000_read_dpram_mag_16 + (dev, FT1000_MAG_PH_LEN, + FT1000_MAG_PH_LEN_INDX)) + PSEUDOSZ; + } + if (size > maxsz) { + DEBUG(1, + "FT1000:ft1000_receive_cmd:Invalid command length = %d\n", + size); + return FALSE; + } else { + ppseudohdr = (u16 *) pbuffer; + spin_lock_irqsave(&info->dpram_lock, flags); + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_RX_BASE + 2); + for (i = 0; i <= (size >> 1); i++) { + tempword = + ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); + *pbuffer++ = ntohs(tempword); + } + } else { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_MAG_RX_BASE); + *pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAH); + DEBUG(1, "ft1000_hw:received data = 0x%x\n", *pbuffer); + pbuffer++; + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_MAG_RX_BASE + 1); + for (i = 0; i <= (size >> 2); i++) { + *pbuffer = + inw(dev->base_addr + + FT1000_REG_MAG_DPDATAL); + pbuffer++; + *pbuffer = + inw(dev->base_addr + + FT1000_REG_MAG_DPDATAH); + pbuffer++; + } + //copy odd aligned word + *pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAL); + DEBUG(1, "ft1000_hw:received data = 0x%x\n", *pbuffer); + pbuffer++; + *pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAH); + DEBUG(1, "ft1000_hw:received data = 0x%x\n", *pbuffer); + pbuffer++; + } + if (size & 0x0001) { + //copy odd byte from fifo + tempword = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); + *pbuffer = ntohs(tempword); + } + spin_unlock_irqrestore(&info->dpram_lock, flags); + + // Check if pseudo header checksum is good + // Calculate pseudo header checksum + tempword = *ppseudohdr++; + for (i = 1; i < 7; i++) { + tempword ^= *ppseudohdr++; + } + if ((tempword != *ppseudohdr)) { + DEBUG(1, + "FT1000:ft1000_receive_cmd:Pseudo header checksum mismatch\n"); + // Drop this message + return FALSE; + } + return TRUE; + } +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_proc_drvmsg +// Descripton: This function will process the various driver messages. +// Input: +// dev - device structure +// pnxtph - pointer to next pseudo header +// Output: +// none +// +//--------------------------------------------------------------------------- +void ft1000_proc_drvmsg(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 msgtype; + u16 tempword; + PMEDIAMSG pmediamsg; + PDSPINITMSG pdspinitmsg; + PDRVMSG pdrvmsg; + u16 len; + u16 i; + PPROV_RECORD ptr; + PPSEUDO_HDR ppseudo_hdr; + PUSHORT pmsg; + struct timeval tv; + union { + u8 byte[2]; + u16 wrd; + } convert; + + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = FT1000_DPRAM_RX_BASE+2; + } + else { + tempword = FT1000_DPRAM_MAG_RX_BASE; + } + if ( ft1000_receive_cmd(dev, &cmdbuffer[0], MAX_CMD_SQSIZE, &tempword) ) { + + // Get the message type which is total_len + PSEUDO header + msgtype + message body + pdrvmsg = (PDRVMSG) & cmdbuffer[0]; + msgtype = ntohs(pdrvmsg->type); + DEBUG(1, "Command message type = 0x%x\n", msgtype); + switch (msgtype) { + case DSP_PROVISION: + DEBUG(0, + "Got a provisioning request message from DSP\n"); + mdelay(25); + while (list_empty(&info->prov_list) == 0) { + DEBUG(0, "Sending a provisioning message\n"); + // Make sure SLOWQ doorbell is clear + tempword = + ft1000_read_reg(dev, FT1000_REG_DOORBELL); + i = 0; + while (tempword & FT1000_DB_DPRAM_TX) { + mdelay(5); + i++; + if (i == 10) { + break; + } + } + ptr = + list_entry(info->prov_list.next, + PROV_RECORD, list); + len = *(u16 *) ptr->pprov_data; + len = htons(len); + + pmsg = (PUSHORT) ptr->pprov_data; + ppseudo_hdr = (PPSEUDO_HDR) pmsg; + // Insert slow queue sequence number + ppseudo_hdr->seq_num = info->squeseqnum++; + ppseudo_hdr->portsrc = 0; + // Calculate new checksum + ppseudo_hdr->checksum = *pmsg++; + DEBUG(1, "checksum = 0x%x\n", + ppseudo_hdr->checksum); + for (i = 1; i < 7; i++) { + ppseudo_hdr->checksum ^= *pmsg++; + DEBUG(1, "checksum = 0x%x\n", + ppseudo_hdr->checksum); + } + + ft1000_send_cmd (dev, (u16 *)ptr->pprov_data, len, SLOWQ_TYPE); + list_del(&ptr->list); + kfree(ptr->pprov_data); + kfree(ptr); + } + // Indicate adapter is ready to take application messages after all + // provisioning messages are sent + info->CardReady = 1; + break; + case MEDIA_STATE: + pmediamsg = (PMEDIAMSG) & cmdbuffer[0]; + if (info->ProgConStat != 0xFF) { + if (pmediamsg->state) { + DEBUG(1, "Media is up\n"); + if (info->mediastate == 0) { + netif_carrier_on(dev); + netif_wake_queue(dev); + info->mediastate = 1; + do_gettimeofday(&tv); + info->ConTm = tv.tv_sec; + } + } else { + DEBUG(1, "Media is down\n"); + if (info->mediastate == 1) { + info->mediastate = 0; + netif_carrier_off(dev); + netif_stop_queue(dev); + info->ConTm = 0; + } + } + } + else { + DEBUG(1,"Media is down\n"); + if (info->mediastate == 1) { + info->mediastate = 0; + netif_carrier_off(dev); + netif_stop_queue(dev); + info->ConTm = 0; + } + } + break; + case DSP_INIT_MSG: + pdspinitmsg = (PDSPINITMSG) & cmdbuffer[0]; + memcpy(info->DspVer, pdspinitmsg->DspVer, DSPVERSZ); + DEBUG(1, "DSPVER = 0x%2x 0x%2x 0x%2x 0x%2x\n", + info->DspVer[0], info->DspVer[1], info->DspVer[2], + info->DspVer[3]); + memcpy(info->HwSerNum, pdspinitmsg->HwSerNum, + HWSERNUMSZ); + memcpy(info->Sku, pdspinitmsg->Sku, SKUSZ); + memcpy(info->eui64, pdspinitmsg->eui64, EUISZ); + dev->dev_addr[0] = info->eui64[0]; + dev->dev_addr[1] = info->eui64[1]; + dev->dev_addr[2] = info->eui64[2]; + dev->dev_addr[3] = info->eui64[5]; + dev->dev_addr[4] = info->eui64[6]; + dev->dev_addr[5] = info->eui64[7]; + + if (ntohs(pdspinitmsg->length) == + (sizeof(DSPINITMSG) - 20)) { + memcpy(info->ProductMode, + pdspinitmsg->ProductMode, MODESZ); + memcpy(info->RfCalVer, pdspinitmsg->RfCalVer, + CALVERSZ); + memcpy(info->RfCalDate, pdspinitmsg->RfCalDate, + CALDATESZ); + DEBUG(1, "RFCalVer = 0x%2x 0x%2x\n", + info->RfCalVer[0], info->RfCalVer[1]); + } + + break ; + case DSP_STORE_INFO: + DEBUG(1, "FT1000:drivermsg:Got DSP_STORE_INFO\n"); + tempword = ntohs(pdrvmsg->length); + info->DSPInfoBlklen = tempword; + if (tempword < (MAX_DSP_SESS_REC - 4)) { + pmsg = (PUSHORT) & pdrvmsg->data[0]; + for (i = 0; i < ((tempword + 1) / 2); i++) { + DEBUG(1, + "FT1000:drivermsg:dsp info data = 0x%x\n", + *pmsg); + info->DSPInfoBlk[i + 10] = *pmsg++; + } + } + break; + case DSP_GET_INFO: + DEBUG(1, "FT1000:drivermsg:Got DSP_GET_INFO\n"); + // copy dsp info block to dsp + info->DrvMsgPend = 1; + // allow any outstanding ioctl to finish + mdelay(10); + tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + if (tempword & FT1000_DB_DPRAM_TX) { + mdelay(10); + tempword = + ft1000_read_reg(dev, FT1000_REG_DOORBELL); + if (tempword & FT1000_DB_DPRAM_TX) { + mdelay(10); + } + } + + if ((tempword & FT1000_DB_DPRAM_TX) == 0) { + // Put message into Slow Queue + // Form Pseudo header + pmsg = (PUSHORT) info->DSPInfoBlk; + ppseudo_hdr = (PPSEUDO_HDR) pmsg; + ppseudo_hdr->length = + htons(info->DSPInfoBlklen + 4); + ppseudo_hdr->source = 0x10; + ppseudo_hdr->destination = 0x20; + ppseudo_hdr->portdest = 0; + ppseudo_hdr->portsrc = 0; + ppseudo_hdr->sh_str_id = 0; + ppseudo_hdr->control = 0; + ppseudo_hdr->rsvd1 = 0; + ppseudo_hdr->rsvd2 = 0; + ppseudo_hdr->qos_class = 0; + // Insert slow queue sequence number + ppseudo_hdr->seq_num = info->squeseqnum++; + // Insert application id + ppseudo_hdr->portsrc = 0; + // Calculate new checksum + ppseudo_hdr->checksum = *pmsg++; + for (i = 1; i < 7; i++) { + ppseudo_hdr->checksum ^= *pmsg++; + } + info->DSPInfoBlk[8] = 0x7200; + info->DSPInfoBlk[9] = + htons(info->DSPInfoBlklen); + ft1000_send_cmd (dev, (PUSHORT)info->DSPInfoBlk, (USHORT)(info->DSPInfoBlklen+4), 0); + } + info->DrvMsgPend = 0; + + break; + case GET_DRV_ERR_RPT_MSG: + DEBUG(1, "FT1000:drivermsg:Got GET_DRV_ERR_RPT_MSG\n"); + // copy driver error message to dsp + info->DrvMsgPend = 1; + // allow any outstanding ioctl to finish + mdelay(10); + tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + if (tempword & FT1000_DB_DPRAM_TX) { + mdelay(10); + tempword = + ft1000_read_reg(dev, FT1000_REG_DOORBELL); + if (tempword & FT1000_DB_DPRAM_TX) { + mdelay(10); + } + } + + if ((tempword & FT1000_DB_DPRAM_TX) == 0) { + // Put message into Slow Queue + // Form Pseudo header + pmsg = (PUSHORT) & tempbuffer[0]; + ppseudo_hdr = (PPSEUDO_HDR) pmsg; + ppseudo_hdr->length = htons(0x0012); + ppseudo_hdr->source = 0x10; + ppseudo_hdr->destination = 0x20; + ppseudo_hdr->portdest = 0; + ppseudo_hdr->portsrc = 0; + ppseudo_hdr->sh_str_id = 0; + ppseudo_hdr->control = 0; + ppseudo_hdr->rsvd1 = 0; + ppseudo_hdr->rsvd2 = 0; + ppseudo_hdr->qos_class = 0; + // Insert slow queue sequence number + ppseudo_hdr->seq_num = info->squeseqnum++; + // Insert application id + ppseudo_hdr->portsrc = 0; + // Calculate new checksum + ppseudo_hdr->checksum = *pmsg++; + for (i=1; i<7; i++) { + ppseudo_hdr->checksum ^= *pmsg++; + } + pmsg = (PUSHORT) & tempbuffer[16]; + *pmsg++ = htons(RSP_DRV_ERR_RPT_MSG); + *pmsg++ = htons(0x000e); + *pmsg++ = htons(info->DSP_TIME[0]); + *pmsg++ = htons(info->DSP_TIME[1]); + *pmsg++ = htons(info->DSP_TIME[2]); + *pmsg++ = htons(info->DSP_TIME[3]); + convert.byte[0] = info->DspVer[0]; + convert.byte[1] = info->DspVer[1]; + *pmsg++ = convert.wrd; + convert.byte[0] = info->DspVer[2]; + convert.byte[1] = info->DspVer[3]; + *pmsg++ = convert.wrd; + *pmsg++ = htons(info->DrvErrNum); + + ft1000_send_cmd (dev, (PUSHORT)&tempbuffer[0], (USHORT)(0x0012), 0); + info->DrvErrNum = 0; + } + info->DrvMsgPend = 0; + + break; + default: + break; + } + } +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_parse_dpram_msg +// Descripton: This function will parse the message received from the DSP +// via the DPRAM interface. +// Input: +// dev - device structure +// Output: +// status - FAILURE +// SUCCESS +// +//--------------------------------------------------------------------------- +int ft1000_parse_dpram_msg(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 doorbell; + u16 portid; + u16 nxtph; + u16 total_len; + int i = 0; + int cnt; + unsigned long flags; + + doorbell = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + DEBUG(1, "Doorbell = 0x%x\n", doorbell); + + if (doorbell & FT1000_ASIC_RESET_REQ) { + // Copy DSP session record from info block + spin_lock_irqsave(&info->dpram_lock, flags); + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_RX_BASE); + for (i = 0; i < MAX_DSP_SESS_REC; i++) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, + info->DSPSess.Rec[i]); + } + } else { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_MAG_RX_BASE); + for (i = 0; i < MAX_DSP_SESS_REC / 2; i++) { + outl(info->DSPSess.MagRec[i], + dev->base_addr + FT1000_REG_MAG_DPDATA); + } + } + spin_unlock_irqrestore(&info->dpram_lock, flags); + + // clear ASIC RESET request + ft1000_write_reg(dev, FT1000_REG_DOORBELL, + FT1000_ASIC_RESET_REQ); + DEBUG(1, "Got an ASIC RESET Request\n"); + ft1000_write_reg(dev, FT1000_REG_DOORBELL, + FT1000_ASIC_RESET_DSP); + + if (info->AsicID == MAGNEMITE_ID) { + // Setting MAGNEMITE ASIC to big endian mode + ft1000_write_reg(dev, FT1000_REG_SUP_CTRL, + HOST_INTF_BE); + } + info->DspAsicReset = 0; + } + + if (doorbell & FT1000_DSP_ASIC_RESET) { + DEBUG(0, + "FT1000:ft1000_parse_dpram_msg: Got a dsp ASIC reset message\n"); + info->DspAsicReset = 1; + ft1000_write_reg(dev, FT1000_REG_DOORBELL, + FT1000_DSP_ASIC_RESET); + udelay(200); + return SUCCESS; + } + + if (doorbell & FT1000_DB_DPRAM_RX) { + DEBUG(1, + "FT1000:ft1000_parse_dpram_msg: Got a slow queue message\n"); + nxtph = FT1000_DPRAM_RX_BASE + 2; + if (info->AsicID == ELECTRABUZZ_ID) { + total_len = + ft1000_read_dpram(dev, FT1000_DPRAM_RX_BASE); + } else { + total_len = + ntohs(ft1000_read_dpram_mag_16 + (dev, FT1000_MAG_TOTAL_LEN, + FT1000_MAG_TOTAL_LEN_INDX)); + } + DEBUG(1, "FT1000:ft1000_parse_dpram_msg:total length = %d\n", + total_len); + if ((total_len < MAX_CMD_SQSIZE) && (total_len > PSEUDOSZ)) { + total_len += nxtph; + cnt = 0; + // ft1000_read_reg will return a value that needs to be byteswap + // in order to get DSP_QID_OFFSET. + if (info->AsicID == ELECTRABUZZ_ID) { + portid = + (ft1000_read_dpram + (dev, + DSP_QID_OFFSET + FT1000_DPRAM_RX_BASE + + 2) >> 8) & 0xff; + } else { + portid = + (ft1000_read_dpram_mag_16 + (dev, FT1000_MAG_PORT_ID, + FT1000_MAG_PORT_ID_INDX) & 0xff); + } + DEBUG(1, "DSP_QID = 0x%x\n", portid); + + if (portid == DRIVERID) { + // We are assumming one driver message from the DSP at a time. + ft1000_proc_drvmsg(dev); + } + } + ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_DPRAM_RX); + } + + if (doorbell & FT1000_DB_COND_RESET) { + // Reset ASIC and DSP + if (info->AsicID == ELECTRABUZZ_ID) { + info->DSP_TIME[0] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER0); + info->DSP_TIME[1] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER1); + info->DSP_TIME[2] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER2); + info->DSP_TIME[3] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER3); + } else { + info->DSP_TIME[0] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER0, + FT1000_MAG_DSP_TIMER0_INDX); + info->DSP_TIME[1] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER1, + FT1000_MAG_DSP_TIMER1_INDX); + info->DSP_TIME[2] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER2, + FT1000_MAG_DSP_TIMER2_INDX); + info->DSP_TIME[3] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER3, + FT1000_MAG_DSP_TIMER3_INDX); + } + info->DrvErrNum = DSP_CONDRESET_INFO; + DEBUG(1, "ft1000_hw:DSP conditional reset requested\n"); + ft1000_reset_card(dev); + ft1000_write_reg(dev, FT1000_REG_DOORBELL, + FT1000_DB_COND_RESET); + } + // let's clear any unexpected doorbells from DSP + doorbell = + doorbell & ~(FT1000_DB_DPRAM_RX | FT1000_ASIC_RESET_REQ | + FT1000_DB_COND_RESET | 0xff00); + if (doorbell) { + DEBUG(1, "Clearing unexpected doorbell = 0x%x\n", doorbell); + ft1000_write_reg(dev, FT1000_REG_DOORBELL, doorbell); + } + + return SUCCESS; + +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_flush_fifo +// Descripton: This function will flush one packet from the downlink +// FIFO. +// Input: +// dev - device structure +// drv_err - driver error causing the flush fifo +// Output: +// None. +// +//--------------------------------------------------------------------------- +static void ft1000_flush_fifo(struct net_device *dev, u16 DrvErrNum) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 i; + u32 templong; + u16 tempword; + + DEBUG(1, "ft1000:ft1000_hw:ft1000_flush_fifo called\n"); + if (info->PktIntfErr > MAX_PH_ERR) { + if (info->AsicID == ELECTRABUZZ_ID) { + info->DSP_TIME[0] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER0); + info->DSP_TIME[1] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER1); + info->DSP_TIME[2] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER2); + info->DSP_TIME[3] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER3); + } else { + info->DSP_TIME[0] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER0, + FT1000_MAG_DSP_TIMER0_INDX); + info->DSP_TIME[1] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER1, + FT1000_MAG_DSP_TIMER1_INDX); + info->DSP_TIME[2] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER2, + FT1000_MAG_DSP_TIMER2_INDX); + info->DSP_TIME[3] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER3, + FT1000_MAG_DSP_TIMER3_INDX); + } + info->DrvErrNum = DrvErrNum; + ft1000_reset_card(dev); + return; + } else { + // Flush corrupted pkt from FIFO + i = 0; + do { + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = + ft1000_read_reg(dev, FT1000_REG_DFIFO); + tempword = + ft1000_read_reg(dev, FT1000_REG_DFIFO_STAT); + } else { + templong = + inl(dev->base_addr + FT1000_REG_MAG_DFR); + tempword = + inw(dev->base_addr + FT1000_REG_MAG_DFSR); + } + i++; + // This should never happen unless the ASIC is broken. + // We must reset to recover. + if ((i > 2048) || (tempword == 0)) { + if (info->AsicID == ELECTRABUZZ_ID) { + info->DSP_TIME[0] = + ft1000_read_dpram(dev, + FT1000_DSP_TIMER0); + info->DSP_TIME[1] = + ft1000_read_dpram(dev, + FT1000_DSP_TIMER1); + info->DSP_TIME[2] = + ft1000_read_dpram(dev, + FT1000_DSP_TIMER2); + info->DSP_TIME[3] = + ft1000_read_dpram(dev, + FT1000_DSP_TIMER3); + } else { + info->DSP_TIME[0] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER0, + FT1000_MAG_DSP_TIMER0_INDX); + info->DSP_TIME[1] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER1, + FT1000_MAG_DSP_TIMER1_INDX); + info->DSP_TIME[2] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER2, + FT1000_MAG_DSP_TIMER2_INDX); + info->DSP_TIME[3] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER3, + FT1000_MAG_DSP_TIMER3_INDX); + } + if (tempword == 0) { + // Let's check if ASIC reads are still ok by reading the Mask register + // which is never zero at this point of the code. + tempword = + inw(dev->base_addr + + FT1000_REG_SUP_IMASK); + if (tempword == 0) { + // This indicates that we can not communicate with the ASIC + info->DrvErrNum = + FIFO_FLUSH_BADCNT; + } else { + // Let's assume that we really flush the FIFO + info->PktIntfErr++; + return; + } + } else { + info->DrvErrNum = FIFO_FLUSH_MAXLIMIT; + } + return; + } + tempword = inw(dev->base_addr + FT1000_REG_SUP_STAT); + } while ((tempword & 0x03) != 0x03); + if (info->AsicID == ELECTRABUZZ_ID) { + i++; + DEBUG(0, "Flushing FIFO complete = %x\n", tempword); + // Flush last word in FIFO. + tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); + // Update FIFO counter for DSP + i = i * 2; + DEBUG(0, "Flush Data byte count to dsp = %d\n", i); + info->fifo_cnt += i; + ft1000_write_dpram(dev, FT1000_FIFO_LEN, + info->fifo_cnt); + } else { + DEBUG(0, "Flushing FIFO complete = %x\n", tempword); + // Flush last word in FIFO + templong = inl(dev->base_addr + FT1000_REG_MAG_DFR); + tempword = inw(dev->base_addr + FT1000_REG_SUP_STAT); + DEBUG(0, "FT1000_REG_SUP_STAT = 0x%x\n", tempword); + tempword = inw(dev->base_addr + FT1000_REG_MAG_DFSR); + DEBUG(0, "FT1000_REG_MAG_DFSR = 0x%x\n", tempword); + } + if (DrvErrNum) { + info->PktIntfErr++; + } + } +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_copy_up_pkt +// Descripton: This function will pull Flarion packets out of the Downlink +// FIFO and convert it to an ethernet packet. The ethernet packet will +// then be deliver to the TCP/IP stack. +// Input: +// dev - device structure +// Output: +// status - FAILURE +// SUCCESS +// +//--------------------------------------------------------------------------- +int ft1000_copy_up_pkt(struct net_device *dev) +{ + u16 tempword; + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 len; + struct sk_buff *skb; + u16 i; + u8 *pbuffer = NULL; + u8 *ptemp = NULL; + u16 chksum; + u32 *ptemplong; + u32 templong; + + DEBUG(1, "ft1000_copy_up_pkt\n"); + // Read length + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); + len = tempword; + } else { + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); + len = ntohs(tempword); + } + chksum = tempword; + DEBUG(1, "Number of Bytes in FIFO = %d\n", len); + + if (len > ENET_MAX_SIZE) { + DEBUG(0, "size of ethernet packet invalid\n"); + if (info->AsicID == MAGNEMITE_ID) { + // Read High word to complete 32 bit access + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + } + ft1000_flush_fifo(dev, DSP_PKTLEN_INFO); + info->stats.rx_errors++; + return FAILURE; + } + + skb = dev_alloc_skb(len + 12 + 2); + + if (skb == NULL) { + DEBUG(0, "No Network buffers available\n"); + // Read High word to complete 32 bit access + if (info->AsicID == MAGNEMITE_ID) { + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + } + ft1000_flush_fifo(dev, 0); + info->stats.rx_errors++; + return FAILURE; + } + pbuffer = (u8 *) skb_put(skb, len + 12); + + // Pseudo header + if (info->AsicID == ELECTRABUZZ_ID) { + for (i = 1; i < 7; i++) { + tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); + chksum ^= tempword; + } + // read checksum value + tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); + } else { + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + DEBUG(1, "Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); + DEBUG(1, "Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + DEBUG(1, "Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); + DEBUG(1, "Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + DEBUG(1, "Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); + DEBUG(1, "Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + // read checksum value + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + DEBUG(1, "Pseudo = 0x%x\n", tempword); + } + + if (chksum != tempword) { + DEBUG(0, "Packet checksum mismatch 0x%x 0x%x\n", chksum, + tempword); + ft1000_flush_fifo(dev, DSP_PKTPHCKSUM_INFO); + info->stats.rx_errors++; + kfree_skb(skb); + return FAILURE; + } + //subtract the number of bytes read already + ptemp = pbuffer; + + // fake MAC address + *pbuffer++ = dev->dev_addr[0]; + *pbuffer++ = dev->dev_addr[1]; + *pbuffer++ = dev->dev_addr[2]; + *pbuffer++ = dev->dev_addr[3]; + *pbuffer++ = dev->dev_addr[4]; + *pbuffer++ = dev->dev_addr[5]; + *pbuffer++ = 0x00; + *pbuffer++ = 0x07; + *pbuffer++ = 0x35; + *pbuffer++ = 0xff; + *pbuffer++ = 0xff; + *pbuffer++ = 0xfe; + + if (info->AsicID == ELECTRABUZZ_ID) { + for (i = 0; i < len / 2; i++) { + tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); + *pbuffer++ = (u8) (tempword >> 8); + *pbuffer++ = (u8) tempword; + if (ft1000_chkcard(dev) == FALSE) { + kfree_skb(skb); + return FAILURE; + } + } + + // Need to read one more word if odd byte + if (len & 0x0001) { + tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); + *pbuffer++ = (u8) (tempword >> 8); + } + } else { + ptemplong = (u32 *) pbuffer; + for (i = 0; i < len / 4; i++) { + templong = inl(dev->base_addr + FT1000_REG_MAG_DFR); + DEBUG(1, "Data = 0x%8x\n", templong); + *ptemplong++ = templong; + } + + // Need to read one more word if odd align. + if (len & 0x0003) { + templong = inl(dev->base_addr + FT1000_REG_MAG_DFR); + DEBUG(1, "Data = 0x%8x\n", templong); + *ptemplong++ = templong; + } + + } + + DEBUG(1, "Data passed to Protocol layer:\n"); + for (i = 0; i < len + 12; i++) { + DEBUG(1, "Protocol Data: 0x%x\n ", *ptemp++); + } + + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_UNNECESSARY; + netif_rx(skb); + + info->stats.rx_packets++; + // Add on 12 bytes for MAC address which was removed + info->stats.rx_bytes += (len + 12); + + if (info->AsicID == ELECTRABUZZ_ID) { + // track how many bytes have been read from FIFO - round up to 16 bit word + tempword = len + 16; + if (tempword & 0x01) + tempword++; + info->fifo_cnt += tempword; + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_FIFO_LEN); + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, info->fifo_cnt); + } + + return SUCCESS; +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_copy_down_pkt +// Descripton: This function will take an ethernet packet and convert it to +// a Flarion packet prior to sending it to the ASIC Downlink +// FIFO. +// Input: +// dev - device structure +// packet - address of ethernet packet +// len - length of IP packet +// Output: +// status - FAILURE +// SUCCESS +// +//--------------------------------------------------------------------------- +int ft1000_copy_down_pkt(struct net_device *dev, u16 * packet, u16 len) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + union { + PSEUDO_HDR blk; + u16 buff[sizeof(PSEUDO_HDR) >> 1]; + u8 buffc[sizeof(PSEUDO_HDR)]; + } pseudo; + int i; + u32 *plong; + + DEBUG(1, "ft1000_hw: copy_down_pkt()\n"); + + // Check if there is room on the FIFO + if (len > ft1000_read_fifo_len(dev)) { + udelay(10); + if (len > ft1000_read_fifo_len(dev)) { + udelay(20); + } + if (len > ft1000_read_fifo_len(dev)) { + udelay(20); + } + if (len > ft1000_read_fifo_len(dev)) { + udelay(20); + } + if (len > ft1000_read_fifo_len(dev)) { + udelay(20); + } + if (len > ft1000_read_fifo_len(dev)) { + udelay(20); + } + if (len > ft1000_read_fifo_len(dev)) { + DEBUG(1, + "ft1000_hw:ft1000_copy_down_pkt:Transmit FIFO is fulli - pkt drop\n"); + info->stats.tx_errors++; + return SUCCESS; + } + } + // Create pseudo header and send pseudo/ip to hardware + if (info->AsicID == ELECTRABUZZ_ID) { + pseudo.blk.length = len; + } else { + pseudo.blk.length = ntohs(len); + } + pseudo.blk.source = DSPID; // Need to swap to get in correct order + pseudo.blk.destination = HOSTID; + pseudo.blk.portdest = NETWORKID; // Need to swap to get in correct order + pseudo.blk.portsrc = DSPAIRID; + pseudo.blk.sh_str_id = 0; + pseudo.blk.control = 0; + pseudo.blk.rsvd1 = 0; + pseudo.blk.seq_num = 0; + pseudo.blk.rsvd2 = info->packetseqnum++; + pseudo.blk.qos_class = 0; + /* Calculate pseudo header checksum */ + pseudo.blk.checksum = pseudo.buff[0]; + for (i = 1; i < 7; i++) { + pseudo.blk.checksum ^= pseudo.buff[i]; + } + + // Production Mode + if (info->AsicID == ELECTRABUZZ_ID) { + // copy first word to UFIFO_BEG reg + ft1000_write_reg(dev, FT1000_REG_UFIFO_BEG, pseudo.buff[0]); + DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 0 BEG = 0x%04x\n", + pseudo.buff[0]); + + // copy subsequent words to UFIFO_MID reg + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[1]); + DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 1 MID = 0x%04x\n", + pseudo.buff[1]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[2]); + DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 2 MID = 0x%04x\n", + pseudo.buff[2]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[3]); + DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 3 MID = 0x%04x\n", + pseudo.buff[3]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[4]); + DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 4 MID = 0x%04x\n", + pseudo.buff[4]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[5]); + DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 5 MID = 0x%04x\n", + pseudo.buff[5]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[6]); + DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 6 MID = 0x%04x\n", + pseudo.buff[6]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[7]); + DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 7 MID = 0x%04x\n", + pseudo.buff[7]); + + // Write PPP type + IP Packet into Downlink FIFO + for (i = 0; i < (len >> 1) - 1; i++) { + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, + htons(*packet)); + DEBUG(1, + "ft1000_hw:ft1000_copy_down_pkt:data %d MID = 0x%04x\n", + i + 8, htons(*packet)); + packet++; + } + + // Check for odd byte + if (len & 0x0001) { + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, + htons(*packet)); + DEBUG(1, + "ft1000_hw:ft1000_copy_down_pkt:data MID = 0x%04x\n", + htons(*packet)); + packet++; + ft1000_write_reg(dev, FT1000_REG_UFIFO_END, + htons(*packet)); + DEBUG(1, + "ft1000_hw:ft1000_copy_down_pkt:data %d MID = 0x%04x\n", + i + 8, htons(*packet)); + } else { + ft1000_write_reg(dev, FT1000_REG_UFIFO_END, + htons(*packet)); + DEBUG(1, + "ft1000_hw:ft1000_copy_down_pkt:data %d MID = 0x%04x\n", + i + 8, htons(*packet)); + } + } else { + outl(*(u32 *) & pseudo.buff[0], + dev->base_addr + FT1000_REG_MAG_UFDR); + DEBUG(1, "ft1000_copy_down_pkt: Pseudo = 0x%8x\n", + *(u32 *) & pseudo.buff[0]); + outl(*(u32 *) & pseudo.buff[2], + dev->base_addr + FT1000_REG_MAG_UFDR); + DEBUG(1, "ft1000_copy_down_pkt: Pseudo = 0x%8x\n", + *(u32 *) & pseudo.buff[2]); + outl(*(u32 *) & pseudo.buff[4], + dev->base_addr + FT1000_REG_MAG_UFDR); + DEBUG(1, "ft1000_copy_down_pkt: Pseudo = 0x%8x\n", + *(u32 *) & pseudo.buff[4]); + outl(*(u32 *) & pseudo.buff[6], + dev->base_addr + FT1000_REG_MAG_UFDR); + DEBUG(1, "ft1000_copy_down_pkt: Pseudo = 0x%8x\n", + *(u32 *) & pseudo.buff[6]); + + plong = (u32 *) packet; + // Write PPP type + IP Packet into Downlink FIFO + for (i = 0; i < (len >> 2); i++) { + outl(*plong++, dev->base_addr + FT1000_REG_MAG_UFDR); + } + + // Check for odd alignment + if (len & 0x0003) { + DEBUG(1, + "ft1000_hw:ft1000_copy_down_pkt:data = 0x%8x\n", + *plong); + outl(*plong++, dev->base_addr + FT1000_REG_MAG_UFDR); + } + outl(1, dev->base_addr + FT1000_REG_MAG_UFER); + } + + info->stats.tx_packets++; + // Add 14 bytes for MAC adddress plus ethernet type + info->stats.tx_bytes += (len + 14); + return SUCCESS; +} + +static struct net_device_stats *ft1000_stats(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + return (&info->stats); +} + +static int ft1000_open(struct net_device *dev) +{ + + DEBUG(0, "ft1000_hw: ft1000_open is called\n"); + + ft1000_reset_card(dev); + DEBUG(0, "ft1000_hw: ft1000_open is ended\n"); + + /* schedule ft1000_hbchk to perform periodic heartbeat checks on DSP and ASIC */ + init_timer(&poll_timer); + poll_timer.expires = jiffies + (2 * HZ); + poll_timer.data = (u_long) dev; + add_timer(&poll_timer); + + DEBUG(0, "ft1000_hw: ft1000_open is ended2\n"); + return 0; +} + +static int ft1000_close(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + + DEBUG(0, "ft1000_hw: ft1000_close()\n"); + + info->CardReady = 0; + del_timer(&poll_timer); + + if (ft1000_card_present == 1) { + DEBUG(0, "Media is down\n"); + netif_stop_queue(dev); + + ft1000_disable_interrupts(dev); + ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT); + + //reset ASIC + ft1000_reset_asic(dev); + } + return 0; +} + +static int ft1000_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u8 *pdata; + + DEBUG(1, "ft1000_hw: ft1000_start_xmit()\n"); + if (skb == NULL) { + DEBUG(1, "ft1000_hw: ft1000_start_xmit:skb == NULL!!!\n"); + return 0; + } + + DEBUG(1, "ft1000_hw: ft1000_start_xmit:length of packet = %d\n", + skb->len); + + pdata = (u8 *) skb->data; + + if (info->mediastate == 0) { + /* Drop packet is mediastate is down */ + DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:mediastate is down\n"); + return SUCCESS; + } + + if ((skb->len < ENET_HEADER_SIZE) || (skb->len > ENET_MAX_SIZE)) { + /* Drop packet which has invalid size */ + DEBUG(1, + "ft1000_hw:ft1000_copy_down_pkt:invalid ethernet length\n"); + return SUCCESS; + } + ft1000_copy_down_pkt(dev, (u16 *) (pdata + ENET_HEADER_SIZE - 2), + skb->len - ENET_HEADER_SIZE + 2); + + dev_kfree_skb(skb); + + return 0; +} + +static irqreturn_t ft1000_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 tempword; + u16 inttype; + int cnt; + + DEBUG(1, "ft1000_hw: ft1000_interrupt()\n"); + + if (info->CardReady == 0) { + ft1000_disable_interrupts(dev); + return IRQ_HANDLED; + } + + if (ft1000_chkcard(dev) == FALSE) { + ft1000_disable_interrupts(dev); + return IRQ_HANDLED; + } + + ft1000_disable_interrupts(dev); + + // Read interrupt type + inttype = ft1000_read_reg(dev, FT1000_REG_SUP_ISR); + + // Make sure we process all interrupt before leaving the ISR due to the edge trigger interrupt type + while (inttype) { + if (inttype & ISR_DOORBELL_PEND) { + ft1000_parse_dpram_msg(dev); + } + + if (inttype & ISR_RCV) { + DEBUG(1, "Data in FIFO\n"); + + cnt = 0; + do { + // Check if we have packets in the Downlink FIFO + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = + ft1000_read_reg(dev, FT1000_REG_DFIFO_STAT); + } else { + tempword = + ft1000_read_reg(dev, FT1000_REG_MAG_DFSR); + } + if (tempword & 0x1f) { + ft1000_copy_up_pkt(dev); + } else { + break; + } + cnt++; + } while (cnt < MAX_RCV_LOOP); + + } + // clear interrupts + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR); + DEBUG(1, "ft1000_hw: interrupt status register = 0x%x\n", tempword); + ft1000_write_reg(dev, FT1000_REG_SUP_ISR, tempword); + + // Read interrupt type + inttype = ft1000_read_reg (dev, FT1000_REG_SUP_ISR); + DEBUG(1,"ft1000_hw: interrupt status register after clear = 0x%x\n",inttype); + } + ft1000_enable_interrupts(dev); + return IRQ_HANDLED; +} + +void stop_ft1000_card(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + PPROV_RECORD ptr; +// int cnt; + + DEBUG(0, "ft1000_hw: stop_ft1000_card()\n"); + + info->CardReady = 0; + ft1000_card_present = 0; + netif_stop_queue(dev); + ft1000_disable_interrupts(dev); + + // Make sure we free any memory reserve for provisioning + while (list_empty(&info->prov_list) == 0) { + ptr = list_entry(info->prov_list.next, PROV_RECORD, list); + list_del(&ptr->list); + kfree(ptr->pprov_data); + kfree(ptr); + } + + if (info->registered) { + unregister_netdev(dev); + info->registered = 0; + } + + free_irq(dev->irq, dev); + release_region(dev->base_addr,256); + release_firmware(fw_entry); + flarion_ft1000_cnt--; + ft1000CleanupProc(dev); + +} + +static void ft1000_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + FT1000_INFO *ft_info; + ft_info = (FT1000_INFO *) netdev_priv(dev); + + snprintf(info->driver, 32, "ft1000"); + snprintf(info->bus_info, ETHTOOL_BUSINFO_LEN, "PCMCIA 0x%lx", + dev->base_addr); + snprintf(info->fw_version, 32, "%d.%d.%d.%d", ft_info->DspVer[0], + ft_info->DspVer[1], ft_info->DspVer[2], ft_info->DspVer[3]); +} + +static u32 ft1000_get_link(struct net_device *dev) +{ + FT1000_INFO *info; + info = (FT1000_INFO *) netdev_priv(dev); + return info->mediastate; +} + +static const struct ethtool_ops ops = { + .get_drvinfo = ft1000_get_drvinfo, + .get_link = ft1000_get_link +}; + +struct net_device *init_ft1000_card(unsigned short irq, int port, + unsigned char *mac_addr, void *ft1000_reset, + void *link, struct device *fdev) +{ + FT1000_INFO *info; + struct net_device *dev; + int i; + + static const struct net_device_ops ft1000ops = // Slavius 21.10.2009 due to kernel changes + { + .ndo_open = &ft1000_open, + .ndo_stop = &ft1000_close, + .ndo_start_xmit = &ft1000_start_xmit, + .ndo_get_stats = &ft1000_stats, + }; + + DEBUG(1, "ft1000_hw: init_ft1000_card()\n"); + DEBUG(1, "ft1000_hw: irq = %d\n", irq); + DEBUG(1, "ft1000_hw: port = 0x%04x\n", port); + + flarion_ft1000_cnt++; + + if (flarion_ft1000_cnt > 1) { + flarion_ft1000_cnt--; + + printk(KERN_INFO + "ft1000: This driver can not support more than one instance\n"); + return NULL; + } + + dev = alloc_etherdev(sizeof(FT1000_INFO)); + if (!dev) { + printk(KERN_ERR "ft1000: failed to allocate etherdev\n"); + return NULL; + } + + SET_NETDEV_DEV(dev, fdev); + info = (FT1000_INFO *) netdev_priv(dev); + + memset(info, 0, sizeof(FT1000_INFO)); + + DEBUG(1, "address of dev = 0x%8x\n", (u32) dev); + DEBUG(1, "address of dev info = 0x%8x\n", (u32) info); + DEBUG(0, "device name = %s\n", dev->name); + + memset(&info->stats, 0, sizeof(struct net_device_stats)); + + spin_lock_init(&info->dpram_lock); + info->DrvErrNum = 0; + info->ASICResetNum = 0; + info->registered = 1; + info->link = link; + info->ft1000_reset = ft1000_reset; + info->mediastate = 0; + info->fifo_cnt = 0; + info->DeviceCreated = FALSE; + info->DeviceMajor = 0; + info->CurrentInterruptEnableMask = ISR_DEFAULT_MASK; + info->InterruptsEnabled = FALSE; + info->CardReady = 0; + info->DSP_TIME[0] = 0; + info->DSP_TIME[1] = 0; + info->DSP_TIME[2] = 0; + info->DSP_TIME[3] = 0; + flarion_ft1000_cnt = 0; + + INIT_LIST_HEAD(&info->prov_list); + + info->squeseqnum = 0; + +// dev->hard_start_xmit = &ft1000_start_xmit; +// dev->get_stats = &ft1000_stats; +// dev->open = &ft1000_open; +// dev->stop = &ft1000_close; + + dev->netdev_ops = &ft1000ops; // Slavius 21.10.2009 due to kernel changes + + DEBUG(0, "device name = %s\n", dev->name); + + for (i = 0; i < 6; i++) { + dev->dev_addr[i] = mac_addr[i]; + DEBUG(1, "ft1000_hw: mac_addr %d = 0x%02x\n", i, mac_addr[i]); + } + + netif_stop_queue(dev); + dev->irq = irq; + dev->base_addr = port; + + if (request_irq(dev->irq, ft1000_interrupt, IRQF_SHARED, dev->name, dev)) { + printk(KERN_ERR "ft1000: Could not request_irq\n"); + goto err_dev; + } + + if (request_region(dev->base_addr, 256, dev->name) == NULL) { + printk(KERN_ERR "ft1000: Could not request_region\n"); + goto err_irq; + } + + if (register_netdev(dev) != 0) { + DEBUG(0, "ft1000: Could not register netdev"); + goto err_reg; + } + + info->AsicID = ft1000_read_reg(dev, FT1000_REG_ASIC_ID); + if (info->AsicID == ELECTRABUZZ_ID) { + DEBUG(0, "ft1000_hw: ELECTRABUZZ ASIC\n"); + if (request_firmware(&fw_entry, "ft1000.img", fdev) != 0) { + printk(KERN_INFO "ft1000: Could not open ft1000.img\n"); + goto err_unreg; + } + } else { + DEBUG(0, "ft1000_hw: MAGNEMITE ASIC\n"); + if (request_firmware(&fw_entry, "ft2000.img", fdev) != 0) { + printk(KERN_INFO "ft1000: Could not open ft2000.img\n"); + goto err_unreg; + } + } + + ft1000_enable_interrupts(dev); + + ft1000InitProc(dev); + ft1000_card_present = 1; + SET_ETHTOOL_OPS(dev, &ops); + printk(KERN_INFO + "ft1000: %s: addr 0x%04lx irq %d, MAC addr %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->name, dev->base_addr, dev->irq, dev->dev_addr[0], + dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], + dev->dev_addr[4], dev->dev_addr[5]); + return dev; + +err_unreg: + unregister_netdev(dev); +err_reg: + release_region(dev->base_addr, 256); +err_irq: + free_irq(dev->irq, dev); +err_dev: + free_netdev(dev); + return NULL; +} + +EXPORT_SYMBOL(init_ft1000_card); +EXPORT_SYMBOL(stop_ft1000_card); +EXPORT_SYMBOL(flarion_ft1000_cnt); |