blob: eecfa377054dcc88fe50009c9041dd9200db1616 [file] [log] [blame]
/*---------------------------------------------------------------------------
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;
}