diff options
author | 2015-08-04 12:17:53 -0700 | |
---|---|---|
committer | 2015-08-04 15:44:42 -0700 | |
commit | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (patch) | |
tree | 1c9cafbcd35f783a87880a10f85d1a060db1a563 /kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c | |
parent | 98260f3884f4a202f9ca5eabed40b1354c489b29 (diff) |
Add the rt linux 4.1.3-rt3 as base
Import the rt linux 4.1.3-rt3 as OPNFV kvm base.
It's from git://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-rt-devel.git linux-4.1.y-rt and
the base is:
commit 0917f823c59692d751951bf5ea699a2d1e2f26a2
Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Date: Sat Jul 25 12:13:34 2015 +0200
Prepare v4.1.3-rt3
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
We lose all the git history this way and it's not good. We
should apply another opnfv project repo in future.
Change-Id: I87543d81c9df70d99c5001fbdf646b202c19f423
Signed-off-by: Yunhong Jiang <yunhong.jiang@intel.com>
Diffstat (limited to 'kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c')
-rw-r--r-- | kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c | 2068 |
1 files changed, 2068 insertions, 0 deletions
diff --git a/kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c b/kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c new file mode 100644 index 000000000..eecfa3770 --- /dev/null +++ b/kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c @@ -0,0 +1,2068 @@ +/*--------------------------------------------------------------------------- + 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. + -------------------------------------------------------------------------*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.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 <linux/io.h> +#include <linux/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> + +#include <pcmcia/cistpl.h> +#include <pcmcia/cisreg.h> +#include <pcmcia/ds.h> + +#include <linux/delay.h> +#include "ft1000.h" + +static 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; +static u8 flarion_ft1000_cnt; + +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_read_fifo_len + Description: 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) +{ + struct ft1000_info *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 + Description: 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) +{ + struct ft1000_info *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 + Description: 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) +{ + struct ft1000_info *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 + Description: 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) +{ + struct ft1000_info *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 + Description: 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) +{ + struct ft1000_info *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 + Description: 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) +{ + struct ft1000_info *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 + Description: 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) +{ + struct ft1000_info *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 + Description: 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) +{ + u16 tempword; + + ft1000_write_reg(dev, FT1000_REG_SUP_IMASK, ISR_DEFAULT_MASK); + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK); + pr_debug("current interrupt enable mask = 0x%x\n", tempword); +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_disable_interrupts + Description: This function will disable all interrupts. + Input: + dev - device structure + Output: + None. + + -------------------------------------------------------------------------*/ +static void ft1000_disable_interrupts(struct net_device *dev) +{ + u16 tempword; + + ft1000_write_reg(dev, FT1000_REG_SUP_IMASK, ISR_MASK_ALL); + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK); + pr_debug("current interrupt enable mask = 0x%x\n", tempword); +} + +/*--------------------------------------------------------------------------- + Function: ft1000_read_dsp_timer + Description: This function reads the DSP timer and stores its value in the + DSP_TIME field of the ft1000_info struct passed as argument + Input: + dev - device structure + info - ft1000_info structure + Output: + None. + + -------------------------------------------------------------------------*/ +static void ft1000_read_dsp_timer(struct net_device *dev, + struct ft1000_info *info) +{ + 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); + } +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_reset_asic + Description: 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) +{ + struct ft1000_info *info = netdev_priv(dev); + struct ft1000_pcmcia *pcmcia = info->priv; + u16 tempword; + + (*info->ft1000_reset) (pcmcia->link); + + /* + * 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 interrupt */ + ft1000_write_reg(dev, FT1000_REG_WATERMARK, 0xffff); + } else { + /* set watermark to -1 in order to not generate an interrupt */ + ft1000_write_reg(dev, FT1000_REG_MAG_WATERMARK, 0xffff); + } + /* clear interrupts */ + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR); + pr_debug("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); + pr_debug("interrupt status register = 0x%x\n", tempword); + +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_reset_card + Description: 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) +{ + struct ft1000_info *info = netdev_priv(dev); + u16 tempword; + int i; + unsigned long flags; + struct prov_record *ptr; + struct prov_record *tmp; + + 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 */ + list_for_each_entry_safe(ptr, tmp, &info->prov_list, list) { + pr_debug("deleting provisioning record\n"); + list_del(&ptr->list); + kfree(ptr->pprov_data); + kfree(ptr); + } + + if (info->AsicID == ELECTRABUZZ_ID) { + pr_debug("resetting DSP\n"); + ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT); + } else { + pr_debug("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) { + 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); + } + + pr_debug("resetting ASIC\n"); + mdelay(10); + /* reset ASIC */ + ft1000_reset_asic(dev); + + pr_debug("downloading dsp image\n"); + + if (info->AsicID == MAGNEMITE_ID) { + /* Put dsp in reset and take ASIC out of reset */ + pr_debug("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); + pr_debug("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) { + pr_debug("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)) { + pr_debug("card download unsuccessful\n"); + return false; + } + pr_debug("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); + pr_debug("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); + pr_debug("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 + Description: 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) { + pr_debug("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) { + pr_debug("Version = 0xffff Card not detected\n"); + return false; + } + return true; +} + + +/*--------------------------------------------------------------------------- + + Function: ft1000_hbchk + Description: 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; + + struct ft1000_info *info; + u16 tempword; + + 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)); + } + pr_debug("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) { + pr_info("heartbeat failed - no ho detected\n"); + ft1000_read_dsp_timer(dev, info); + info->DrvErrNum = DSP_HB_INFO; + if (ft1000_reset_card(dev) == 0) { + pr_info("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) { + pr_info("heartbeat doorbell not clear by firmware\n"); + ft1000_read_dsp_timer(dev, info); + info->DrvErrNum = DSP_HB_INFO; + if (ft1000_reset_card(dev) == 0) { + pr_info("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) { + pr_info("heartbeat failed - cannot write hi into DPRAM\n"); + ft1000_read_dsp_timer(dev, info); + info->DrvErrNum = DSP_HB_INFO; + if (ft1000_reset_card(dev) == 0) { + pr_info("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 + Description: + Input: + Output: + + -------------------------------------------------------------------------*/ +static void ft1000_send_cmd(struct net_device *dev, u16 *ptempbuffer, int size, + u16 qtype) +{ + struct ft1000_info *info = netdev_priv(dev); + int i; + u16 tempword; + unsigned long flags; + + size += sizeof(struct pseudo_hdr); + /* check for odd byte and increment to 16-bit word align value */ + if ((size & 0x0001)) + size++; + pr_debug("total length = %d\n", size); + pr_debug("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++) { + pr_debug("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++) { + pr_debug("data = 0x%x\n", *ptempbuffer); + outw(*ptempbuffer++, + dev->base_addr + FT1000_REG_MAG_DPDATAL); + pr_debug("data = 0x%x\n", *ptempbuffer); + outw(*ptempbuffer++, + dev->base_addr + FT1000_REG_MAG_DPDATAH); + } + pr_debug("data = 0x%x\n", *ptempbuffer); + outw(*ptempbuffer++, dev->base_addr + FT1000_REG_MAG_DPDATAL); + pr_debug("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 + Description: 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) + + -------------------------------------------------------------------------*/ +static bool ft1000_receive_cmd(struct net_device *dev, u16 *pbuffer, + int maxsz, u16 *pnxtph) +{ + struct ft1000_info *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) + + sizeof(struct pseudo_hdr); + } else { + size = ntohs(ft1000_read_dpram_mag_16(dev, FT1000_MAG_PH_LEN, + FT1000_MAG_PH_LEN_INDX)) + + sizeof(struct pseudo_hdr); + } + if (size > maxsz) { + pr_debug("Invalid command length = %d\n", size); + return false; + } + 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); + pr_debug("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); + pr_debug("received data = 0x%x\n", *pbuffer); + pbuffer++; + *pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAH); + pr_debug("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) { + pr_debug("Pseudo header checksum mismatch\n"); + /* Drop this message */ + return false; + } + return true; +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_proc_drvmsg + Description: This function will process the various driver messages. + Input: + dev - device structure + pnxtph - pointer to next pseudo header + Output: + none + + -------------------------------------------------------------------------*/ +static void ft1000_proc_drvmsg(struct net_device *dev) +{ + struct ft1000_info *info = netdev_priv(dev); + u16 msgtype; + u16 tempword; + struct media_msg *pmediamsg; + struct dsp_init_msg *pdspinitmsg; + struct drv_msg *pdrvmsg; + u16 len; + u16 i; + struct prov_record *ptr; + struct pseudo_hdr *ppseudo_hdr; + u16 *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 = (struct drv_msg *)&cmdbuffer[0]; + msgtype = ntohs(pdrvmsg->type); + pr_debug("Command message type = 0x%x\n", msgtype); + switch (msgtype) { + case DSP_PROVISION: + pr_debug("Got a provisioning request message from DSP\n"); + mdelay(25); + while (list_empty(&info->prov_list) == 0) { + pr_debug("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, + struct prov_record, list); + len = *(u16 *)ptr->pprov_data; + len = htons(len); + + pmsg = (u16 *)ptr->pprov_data; + ppseudo_hdr = (struct pseudo_hdr *)pmsg; + /* Insert slow queue sequence number */ + ppseudo_hdr->seq_num = info->squeseqnum++; + ppseudo_hdr->portsrc = 0; + /* Calculate new checksum */ + ppseudo_hdr->checksum = *pmsg++; + pr_debug("checksum = 0x%x\n", + ppseudo_hdr->checksum); + for (i = 1; i < 7; i++) { + ppseudo_hdr->checksum ^= *pmsg++; + pr_debug("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 = (struct media_msg *)&cmdbuffer[0]; + if (info->ProgConStat != 0xFF) { + if (pmediamsg->state) { + pr_debug("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 { + pr_debug("Media is down\n"); + if (info->mediastate == 1) { + info->mediastate = 0; + netif_carrier_off(dev); + netif_stop_queue(dev); + info->ConTm = 0; + } + } + } else { + pr_debug("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 = (struct dsp_init_msg *)&cmdbuffer[0]; + memcpy(info->DspVer, pdspinitmsg->DspVer, DSPVERSZ); + pr_debug("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(struct dsp_init_msg) - 20)) { + memcpy(info->ProductMode, + pdspinitmsg->ProductMode, MODESZ); + memcpy(info->RfCalVer, pdspinitmsg->RfCalVer, + CALVERSZ); + memcpy(info->RfCalDate, pdspinitmsg->RfCalDate, + CALDATESZ); + pr_debug("RFCalVer = 0x%2x 0x%2x\n", + info->RfCalVer[0], info->RfCalVer[1]); + } + + break; + case DSP_STORE_INFO: + pr_debug("Got DSP_STORE_INFO\n"); + tempword = ntohs(pdrvmsg->length); + info->DSPInfoBlklen = tempword; + if (tempword < (MAX_DSP_SESS_REC - 4)) { + pmsg = (u16 *)&pdrvmsg->data[0]; + for (i = 0; i < ((tempword + 1) / 2); i++) { + pr_debug("dsp info data = 0x%x\n", + *pmsg); + info->DSPInfoBlk[i + 10] = *pmsg++; + } + } + break; + case DSP_GET_INFO: + pr_debug("Got DSP_GET_INFO\n"); + /* + * copy dsp info block to dsp + * 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 = (u16 *)info->DSPInfoBlk; + ppseudo_hdr = (struct pseudo_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, info->DSPInfoBlk, + (u16)(info->DSPInfoBlklen+4), + 0); + } + + break; + case GET_DRV_ERR_RPT_MSG: + pr_debug("Got GET_DRV_ERR_RPT_MSG\n"); + /* + * copy driver error message to dsp + * 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 = (u16 *)&tempbuffer[0]; + ppseudo_hdr = (struct pseudo_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 = (u16 *)&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, (u16 *)&tempbuffer[0], + (u16)(0x0012), 0); + info->DrvErrNum = 0; + } + + break; + default: + break; + } + } +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_parse_dpram_msg + Description: This function will parse the message received from the DSP + via the DPRAM interface. + Input: + dev - device structure + Output: + status - FAILURE + SUCCESS + + -------------------------------------------------------------------------*/ +static int ft1000_parse_dpram_msg(struct net_device *dev) +{ + struct ft1000_info *info = netdev_priv(dev); + u16 doorbell; + u16 portid; + u16 nxtph; + u16 total_len; + int i = 0; + unsigned long flags; + + doorbell = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + pr_debug("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); + pr_debug("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); + } + } + + if (doorbell & FT1000_DSP_ASIC_RESET) { + pr_debug("Got a dsp ASIC reset message\n"); + ft1000_write_reg(dev, FT1000_REG_DOORBELL, + FT1000_DSP_ASIC_RESET); + udelay(200); + return SUCCESS; + } + + if (doorbell & FT1000_DB_DPRAM_RX) { + pr_debug("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)); + } + pr_debug("total length = %d\n", total_len); + if ((total_len < MAX_CMD_SQSIZE) + && (total_len > sizeof(struct pseudo_hdr))) { + total_len += nxtph; + /* + * 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; + } + pr_debug("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 */ + ft1000_read_dsp_timer(dev, info); + info->DrvErrNum = DSP_CONDRESET_INFO; + pr_debug("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) { + pr_debug("Clearing unexpected doorbell = 0x%x\n", doorbell); + ft1000_write_reg(dev, FT1000_REG_DOORBELL, doorbell); + } + + return SUCCESS; + +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_flush_fifo + Description: 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) +{ + struct ft1000_info *info = netdev_priv(dev); + struct ft1000_pcmcia *pcmcia = info->priv; + u16 i; + u32 templong; + u16 tempword; + + if (pcmcia->PktIntfErr > MAX_PH_ERR) { + ft1000_read_dsp_timer(dev, info); + info->DrvErrNum = DrvErrNum; + ft1000_reset_card(dev); + return; + } + /* 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)) { + ft1000_read_dsp_timer(dev, info); + 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 + */ + pcmcia->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++; + pr_debug("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; + pr_debug("Flush Data byte count to dsp = %d\n", i); + info->fifo_cnt += i; + ft1000_write_dpram(dev, FT1000_FIFO_LEN, + info->fifo_cnt); + } else { + pr_debug("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); + pr_debug("FT1000_REG_SUP_STAT = 0x%x\n", tempword); + tempword = inw(dev->base_addr + FT1000_REG_MAG_DFSR); + pr_debug("FT1000_REG_MAG_DFSR = 0x%x\n", tempword); + } + if (DrvErrNum) + pcmcia->PktIntfErr++; +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_copy_up_pkt + Description: 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 + + -------------------------------------------------------------------------*/ +static int ft1000_copy_up_pkt(struct net_device *dev) +{ + u16 tempword; + struct ft1000_info *info = netdev_priv(dev); + u16 len; + struct sk_buff *skb; + u16 i; + u8 *pbuffer = NULL; + u8 *ptemp = NULL; + u16 chksum; + u32 *ptemplong; + u32 templong; + + /* 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; + pr_debug("Number of Bytes in FIFO = %d\n", len); + + if (len > ENET_MAX_SIZE) { + pr_debug("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) { + /* 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); + pr_debug("Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); + pr_debug("Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + pr_debug("Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); + pr_debug("Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + pr_debug("Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); + pr_debug("Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + /* read checksum value */ + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + pr_debug("Pseudo = 0x%x\n", tempword); + } + + if (chksum != tempword) { + pr_debug("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); + pr_debug("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); + pr_debug("Data = 0x%8x\n", templong); + *ptemplong++ = templong; + } + + } + + pr_debug("Data passed to Protocol layer:\n"); + for (i = 0; i < len + 12; i++) + pr_debug("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 + Description: 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 + + -------------------------------------------------------------------------*/ +static int ft1000_copy_down_pkt(struct net_device *dev, u16 *packet, u16 len) +{ + struct ft1000_info *info = netdev_priv(dev); + struct ft1000_pcmcia *pcmcia = info->priv; + union { + struct pseudo_hdr blk; + u16 buff[sizeof(struct pseudo_hdr) >> 1]; + u8 buffc[sizeof(struct pseudo_hdr)]; + } pseudo; + int i; + u32 *plong; + + /* 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)) { + pr_debug("Transmit FIFO is full - 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 = pcmcia->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]); + pr_debug("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]); + pr_debug("data 1 MID = 0x%04x\n", pseudo.buff[1]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[2]); + pr_debug("data 2 MID = 0x%04x\n", pseudo.buff[2]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[3]); + pr_debug("data 3 MID = 0x%04x\n", pseudo.buff[3]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[4]); + pr_debug("data 4 MID = 0x%04x\n", pseudo.buff[4]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[5]); + pr_debug("data 5 MID = 0x%04x\n", pseudo.buff[5]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[6]); + pr_debug("data 6 MID = 0x%04x\n", pseudo.buff[6]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[7]); + pr_debug("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)); + pr_debug("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)); + pr_debug("data MID = 0x%04x\n", htons(*packet)); + packet++; + ft1000_write_reg(dev, FT1000_REG_UFIFO_END, + htons(*packet)); + pr_debug("data %d MID = 0x%04x\n", + i + 8, htons(*packet)); + } else { + ft1000_write_reg(dev, FT1000_REG_UFIFO_END, + htons(*packet)); + pr_debug("data %d MID = 0x%04x\n", + i + 8, htons(*packet)); + } + } else { + outl(*(u32 *)&pseudo.buff[0], + dev->base_addr + FT1000_REG_MAG_UFDR); + pr_debug("Pseudo = 0x%8x\n", *(u32 *)&pseudo.buff[0]); + outl(*(u32 *)&pseudo.buff[2], + dev->base_addr + FT1000_REG_MAG_UFDR); + pr_debug("Pseudo = 0x%8x\n", *(u32 *)&pseudo.buff[2]); + outl(*(u32 *)&pseudo.buff[4], + dev->base_addr + FT1000_REG_MAG_UFDR); + pr_debug("Pseudo = 0x%8x\n", *(u32 *)&pseudo.buff[4]); + outl(*(u32 *)&pseudo.buff[6], + dev->base_addr + FT1000_REG_MAG_UFDR); + pr_debug("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) { + pr_debug("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 address plus ethernet type */ + info->stats.tx_bytes += (len + 14); + return SUCCESS; +} + +static struct net_device_stats *ft1000_stats(struct net_device *dev) +{ + struct ft1000_info *info = netdev_priv(dev); + + return &info->stats; +} + +static int ft1000_open(struct net_device *dev) +{ + ft1000_reset_card(dev); + + /* 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); + + return 0; +} + +static int ft1000_close(struct net_device *dev) +{ + struct ft1000_info *info = netdev_priv(dev); + + info->CardReady = 0; + del_timer(&poll_timer); + + if (ft1000_card_present == 1) { + pr_debug("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) +{ + struct ft1000_info *info = netdev_priv(dev); + u8 *pdata; + + if (skb == NULL) { + pr_debug("skb == NULL!!!\n"); + return 0; + } + + pr_debug("length of packet = %d\n", skb->len); + + pdata = (u8 *)skb->data; + + if (info->mediastate == 0) { + /* Drop packet is mediastate is down */ + pr_debug("mediastate is down\n"); + return SUCCESS; + } + + if ((skb->len < ENET_HEADER_SIZE) || (skb->len > ENET_MAX_SIZE)) { + /* Drop packet which has invalid size */ + pr_debug("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 = dev_id; + struct ft1000_info *info = netdev_priv(dev); + u16 tempword; + u16 inttype; + int cnt; + + 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) { + pr_debug("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)) + break; + ft1000_copy_up_pkt(dev); + cnt++; + } while (cnt < MAX_RCV_LOOP); + + } + /* clear interrupts */ + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR); + pr_debug("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); + pr_debug("interrupt status register after clear = 0x%x\n", + inttype); + } + ft1000_enable_interrupts(dev); + return IRQ_HANDLED; +} + +void stop_ft1000_card(struct net_device *dev) +{ + struct ft1000_info *info = netdev_priv(dev); + struct prov_record *ptr; + struct prov_record *tmp; + /* int cnt; */ + + info->CardReady = 0; + ft1000_card_present = 0; + netif_stop_queue(dev); + ft1000_disable_interrupts(dev); + + /* Make sure we free any memory reserve for provisioning */ + list_for_each_entry_safe(ptr, tmp, &info->prov_list, list) { + list_del(&ptr->list); + kfree(ptr->pprov_data); + kfree(ptr); + } + + kfree(info->priv); + + 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--; + +} + +static void ft1000_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct ft1000_info *ft_info; + + ft_info = netdev_priv(dev); + + strlcpy(info->driver, "ft1000", sizeof(info->driver)); + snprintf(info->bus_info, sizeof(info->bus_info), "PCMCIA 0x%lx", + dev->base_addr); + snprintf(info->fw_version, sizeof(info->fw_version), "%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) +{ + struct ft1000_info *info; + + 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(struct pcmcia_device *link, + void *ft1000_reset) +{ + struct ft1000_info *info; + struct ft1000_pcmcia *pcmcia; + struct net_device *dev; + + static const struct net_device_ops ft1000ops = { + .ndo_open = &ft1000_open, + .ndo_stop = &ft1000_close, + .ndo_start_xmit = &ft1000_start_xmit, + .ndo_get_stats = &ft1000_stats, + }; + + pr_debug("irq = %d, port = 0x%04llx\n", + link->irq, (unsigned long long)link->resource[0]->start); + + flarion_ft1000_cnt++; + + if (flarion_ft1000_cnt > 1) { + flarion_ft1000_cnt--; + + dev_info(&link->dev, + "This driver can not support more than one instance\n"); + return NULL; + } + + dev = alloc_etherdev(sizeof(struct ft1000_info)); + if (!dev) { + dev_err(&link->dev, "Failed to allocate etherdev\n"); + return NULL; + } + + SET_NETDEV_DEV(dev, &link->dev); + info = netdev_priv(dev); + + memset(info, 0, sizeof(struct ft1000_info)); + + pr_debug("address of dev = 0x%p\n", dev); + pr_debug("address of dev info = 0x%p\n", info); + pr_debug("device name = %s\n", dev->name); + + memset(&info->stats, 0, sizeof(struct net_device_stats)); + + info->priv = kzalloc(sizeof(struct ft1000_pcmcia), GFP_KERNEL); + pcmcia = info->priv; + pcmcia->link = link; + + spin_lock_init(&info->dpram_lock); + info->DrvErrNum = 0; + info->registered = 1; + info->ft1000_reset = ft1000_reset; + info->mediastate = 0; + info->fifo_cnt = 0; + 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; + + pr_debug("device name = %s\n", dev->name); + + dev->irq = link->irq; + dev->base_addr = link->resource[0]->start; + if (pcmcia_get_mac_from_cis(link, dev)) { + netdev_err(dev, "Could not read mac address\n"); + goto err_dev; + } + + if (request_irq(dev->irq, ft1000_interrupt, IRQF_SHARED, dev->name, + dev)) { + netdev_err(dev, "Could not request_irq\n"); + goto err_dev; + } + + if (request_region(dev->base_addr, 256, dev->name) == NULL) { + netdev_err(dev, "Could not request_region\n"); + goto err_irq; + } + + if (register_netdev(dev)) { + pr_debug("Could not register netdev\n"); + goto err_reg; + } + + info->AsicID = ft1000_read_reg(dev, FT1000_REG_ASIC_ID); + if (info->AsicID == ELECTRABUZZ_ID) { + pr_debug("ELECTRABUZZ ASIC\n"); + if (request_firmware(&fw_entry, "ft1000.img", + &link->dev) != 0) { + pr_info("Could not open ft1000.img\n"); + goto err_unreg; + } + } else { + pr_debug("MAGNEMITE ASIC\n"); + if (request_firmware(&fw_entry, "ft2000.img", + &link->dev) != 0) { + pr_info("Could not open ft2000.img\n"); + goto err_unreg; + } + } + + ft1000_enable_interrupts(dev); + + ft1000_card_present = 1; + dev->ethtool_ops = &ops; + pr_info("%s: addr 0x%04lx irq %d, MAC addr %pM\n", + dev->name, dev->base_addr, dev->irq, dev->dev_addr); + 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; +} |