2231 lines
64 KiB
C
2231 lines
64 KiB
C
/*---------------------------------------------------------------------------
|
|
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>
|
|
|
|
#include <pcmcia/cistpl.h>
|
|
#include <pcmcia/cisreg.h>
|
|
#include <pcmcia/ds.h>
|
|
|
|
#ifdef FT_DEBUG
|
|
#define DEBUG(n, args...) printk(KERN_DEBUG args);
|
|
#else
|
|
#define DEBUG(n, args...)
|
|
#endif
|
|
|
|
#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 = 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_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;
|
|
|
|
DEBUG(1, "ft1000_hw:ft1000_enable_interrupts()\n");
|
|
ft1000_write_reg(dev, FT1000_REG_SUP_IMASK, ISR_DEFAULT_MASK);
|
|
tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK);
|
|
DEBUG(1,
|
|
"ft1000_hw:ft1000_enable_interrupts: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;
|
|
|
|
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);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// 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);
|
|
u16 tempword;
|
|
|
|
DEBUG(1, "ft1000_hw:ft1000_reset_asic called\n");
|
|
|
|
(*info->ft1000_reset) (info->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 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
|
|
// 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;
|
|
|
|
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, struct 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) {
|
|
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);
|
|
|
|
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
|
|
// 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) {
|
|
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
|
|
// 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));
|
|
}
|
|
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
|
|
// 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++;
|
|
}
|
|
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
|
|
// 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) {
|
|
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
|
|
// 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);
|
|
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,
|
|
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++;
|
|
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 = (struct media_msg *) & 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 = (struct dsp_init_msg *) & 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(struct dsp_init_msg) - 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 = (u16 *) & 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
|
|
// 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, (u16 *)info->DSPInfoBlk, (u16)(info->DSPInfoBlklen+4), 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
|
|
// 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;
|
|
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);
|
|
}
|
|
}
|
|
|
|
if (doorbell & FT1000_DSP_ASIC_RESET) {
|
|
DEBUG(0,
|
|
"FT1000:ft1000_parse_dpram_msg: 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) {
|
|
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 > sizeof(struct pseudo_hdr))) {
|
|
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
|
|
// 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);
|
|
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
|
|
// 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;
|
|
|
|
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
|
|
// 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);
|
|
union {
|
|
struct pseudo_hdr blk;
|
|
u16 buff[sizeof(struct pseudo_hdr) >> 1];
|
|
u8 buffc[sizeof(struct 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 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)
|
|
{
|
|
|
|
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)
|
|
{
|
|
struct ft1000_info *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)
|
|
{
|
|
struct ft1000_info *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;
|
|
struct ft1000_info *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)
|
|
{
|
|
struct ft1000_info *info = netdev_priv(dev);
|
|
struct prov_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, struct 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)
|
|
{
|
|
struct ft1000_info *ft_info;
|
|
ft_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)
|
|
{
|
|
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 net_device *dev;
|
|
|
|
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", link->irq);
|
|
DEBUG(1, "ft1000_hw: port = 0x%04x\n", link->resource[0]->start);
|
|
|
|
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(struct ft1000_info));
|
|
if (!dev) {
|
|
printk(KERN_ERR "ft1000: failed to allocate etherdev\n");
|
|
return NULL;
|
|
}
|
|
|
|
SET_NETDEV_DEV(dev, &link->dev);
|
|
info = netdev_priv(dev);
|
|
|
|
memset(info, 0, sizeof(struct 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->registered = 1;
|
|
info->link = link;
|
|
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; // Slavius 21.10.2009 due to kernel changes
|
|
|
|
DEBUG(0, "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)) {
|
|
printk(KERN_ERR "ft1000: Could not read mac address\n");
|
|
goto err_dev;
|
|
}
|
|
|
|
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", &link->dev) != 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", &link->dev) != 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;
|
|
}
|