diff options
Diffstat (limited to 'drivers/staging/ft1000/ft1000-pcmcia')
-rw-r--r-- | drivers/staging/ft1000/ft1000-pcmcia/Makefile | 3 | ||||
-rw-r--r-- | drivers/staging/ft1000/ft1000-pcmcia/boot.h | 158 | ||||
-rw-r--r-- | drivers/staging/ft1000/ft1000-pcmcia/ft1000.conf | 14 | ||||
-rw-r--r-- | drivers/staging/ft1000/ft1000-pcmcia/ft1000.h | 409 | ||||
-rw-r--r-- | drivers/staging/ft1000/ft1000-pcmcia/ft1000.img | bin | 0 -> 305770 bytes | |||
-rw-r--r-- | drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.c | 513 | ||||
-rw-r--r-- | drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.h | 1 | ||||
-rw-r--r-- | drivers/staging/ft1000/ft1000-pcmcia/ft1000_dev.h | 66 | ||||
-rw-r--r-- | drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c | 940 | ||||
-rw-r--r-- | drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c | 2294 | ||||
-rw-r--r-- | drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c | 219 |
11 files changed, 4617 insertions, 0 deletions
diff --git a/drivers/staging/ft1000/ft1000-pcmcia/Makefile b/drivers/staging/ft1000/ft1000-pcmcia/Makefile new file mode 100644 index 0000000..660b7a5 --- /dev/null +++ b/drivers/staging/ft1000/ft1000-pcmcia/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_FT1000_PCMCIA) = ft1000_pcmcia.o +ft1000_pcmcia-y := ft1000_hw.o ft1000_dnld.o ft1000_proc.o ft1000_cs.o + diff --git a/drivers/staging/ft1000/ft1000-pcmcia/boot.h b/drivers/staging/ft1000/ft1000-pcmcia/boot.h new file mode 100644 index 0000000..1fc4ac1 --- /dev/null +++ b/drivers/staging/ft1000/ft1000-pcmcia/boot.h @@ -0,0 +1,158 @@ +//--------------------------------------------------------------------------- +// FT1000 driver for Flarion Flash OFDM NIC Device +// +// Copyright (C) 2002 Flarion Technologies, 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. +//--------------------------------------------------------------------------- +// +// File: boot.h +// +// Description: boatloader +// +// History: +// 1/11/05 Whc Ported to Linux. +// +//--------------------------------------------------------------------------- +#ifndef _BOOTH_ +#define _BOOTH_ + +// Official bootloader +unsigned char bootimage [] = { +0x00,0x00,0x01,0x5E,0x00,0x00 +,0x00,0x00,0x00,0x00,0x02,0xD7 +,0x00,0x00,0x01,0x5E,0x46,0xB3 +,0xE6,0x02,0x00,0x98,0xE6,0x8C +,0x00,0x98,0xFB,0x92,0xFF,0xFF +,0x98,0xFB,0x94,0xFF,0xFF,0x98 +,0xFB,0x06,0x08,0x00,0x98,0xFB +,0x96,0x84,0x00,0x98,0xFB,0x08 +,0x1C,0x00,0x98,0xFB,0x51,0x25 +,0x10,0x1C,0x00,0xE6,0x51,0x01 +,0x07,0xFD,0x4C,0xFF,0x20,0xF5 +,0x51,0x02,0x20,0x08,0x00,0x4C +,0xFF,0x20,0x3C,0x00,0xC0,0x64 +,0x98,0xC0,0x66,0x98,0xC0,0x68 +,0x98,0xC0,0x6A,0x98,0xC0,0x6C +,0x98,0x90,0x08,0x90,0x09,0x90 +,0x0A,0x90,0x0B,0x90,0x0C,0x90 +,0x0D,0x90,0x0E,0x90,0x0F,0x90 +,0x04,0x90,0x06,0xFB,0x51,0x22 +,0x16,0x08,0x03,0xFB,0x51,0x52 +,0x16,0x08,0x04,0xFB,0x51,0x24 +,0x2B,0x08,0x06,0xFB,0x51,0x54 +,0x2B,0x08,0x07,0xFB,0x51,0x24 +,0x2B,0x08,0x09,0xFB,0x51,0x54 +,0x2B,0x08,0x0A,0xFB,0x51,0x12 +,0x16,0x08,0x0C,0xFB,0x51,0x52 +,0x16,0x08,0x0D,0x78,0x00,0x00 +,0x00,0x16,0x00,0x00,0xEC,0x31 +,0xAE,0x00,0x00,0x81,0x4C,0x0F +,0xE6,0x43,0xFF,0xEC,0x31,0x4E +,0x00,0x00,0x91,0xEC,0x31,0xAE +,0x00,0x00,0x91,0x4C,0x0F,0xE6 +,0x43,0xFF,0xEC,0x31,0x5E,0x00 +,0x00,0xA1,0xEB,0x31,0x08,0x00 +,0x00,0xA6,0xEB,0x31,0x08,0x00 +,0x00,0xAC,0x3C,0x00,0xEB,0x31 +,0x08,0x00,0x00,0xA8,0x76,0xFE +,0xFE,0x08,0xEB,0x31,0x08,0x20 +,0x00,0x00,0x76,0xFF,0xFF,0x18 +,0xED,0x31,0x08,0x20,0x00,0x00 +,0x26,0x10,0x04,0x10,0xF5,0x3C +,0x01,0x3C,0x00,0x08,0x01,0x12 +,0x3C,0x11,0x3C,0x00,0x08,0x01 +,0x0B,0x08,0x00,0x6D,0xEC,0x31 +,0xAE,0x20,0x00,0x06,0xED,0x4D +,0x08,0x00,0x00,0x67,0x80,0x6F +,0x00,0x01,0x0B,0x6F,0x00,0x02 +,0x2E,0x76,0xEE,0x01,0x48,0x06 +,0x01,0x39,0xED,0x4D,0x18,0x00 +,0x02,0xED,0x4D,0x08,0x00,0x04 +,0x14,0x06,0xA4,0xED,0x31,0x22 +,0x00,0x00,0xAC,0x76,0xEE,0x07 +,0x48,0x6D,0x22,0x01,0x1E,0x08 +,0x01,0x58,0xEB,0x31,0x08,0x00 +,0x00,0xAC,0x06,0xFF,0xBA,0x3C +,0x00,0xEB,0x31,0x08,0x20,0x00 +,0x04,0x3C,0x30,0xEB,0x31,0x08 +,0x20,0x00,0x02,0x3C,0x10,0xEB +,0x31,0x08,0x20,0x00,0x00,0xED +,0x31,0x08,0x20,0x00,0x00,0x04 +,0x10,0xF7,0xED,0x31,0x08,0x00 +,0x00,0xA2,0x91,0x00,0x9C,0x3C +,0x80,0xEB,0x31,0x08,0x20,0x00 +,0x04,0x3C,0x20,0xEB,0x31,0x08 +,0x20,0x00,0x02,0x3C,0x10,0xEB +,0x31,0x08,0x20,0x00,0x00,0xED +,0x31,0x08,0x20,0x00,0x00,0x04 +,0x10,0xF7,0xED,0x31,0x08,0x20 +,0x00,0x04,0x42,0x10,0x90,0x08 +,0xEC,0x31,0xAE,0x20,0x00,0x06 +,0xA4,0x41,0x08,0x00,0xB6,0xED +,0x41,0x28,0x7D,0xFF,0xFF,0x22 +,0xB3,0x40,0x98,0x2A,0x32,0xEB +,0x41,0x28,0xB4,0x43,0xFC,0x05 +,0xFF,0xE6,0xA0,0x31,0x20,0x00 +,0x06,0xEB,0x31,0x08,0x20,0x00 +,0x04,0x3C,0x20,0xEB,0x31,0x08 +,0x20,0x00,0x02,0x3C,0x10,0xEB +,0x31,0x08,0x20,0x00,0x00,0xED +,0x31,0x08,0x20,0x00,0x00,0x04 +,0x10,0xF7,0xED,0x31,0x08,0x20 +,0x00,0x04,0x42,0x10,0x90,0x08 +,0xEC,0x31,0xAE,0x20,0x00,0x06 +,0xA4,0x41,0x08,0x00,0x68,0xED +,0x41,0x28,0x7D,0xFF,0xFF,0x22 +,0xB3,0x40,0x98,0x2A,0x32,0xEB +,0x41,0x28,0xB4,0x43,0xFC,0x05 +,0xFF,0xE6,0x48,0x04,0xEB,0x31 +,0x08,0x20,0x00,0x04,0xEB,0x31 +,0x18,0x20,0x00,0x02,0x3C,0x11 +,0xEB,0x31,0x18,0x20,0x00,0x00 +,0xED,0x31,0x08,0x20,0x00,0x00 +,0x04,0x10,0xF7,0xED,0x31,0x08 +,0x20,0x00,0x02,0x66,0x00,0x6F +,0x00,0x01,0x16,0x76,0xEE,0x06 +,0x48,0x4A,0x1E,0x48,0x04,0xED +,0x31,0x08,0x20,0x00,0x04,0xEB +,0x31,0x08,0x00,0x00,0xA4,0x48 +,0x04,0xED,0x31,0x08,0x20,0x00 +,0x04,0xEB,0x31,0x08,0x00,0x00 +,0xA2,0x48,0x04,0x20,0x20,0x4A +,0x7C,0x46,0x82,0x50,0x05,0x50 +,0x15,0xB5,0x1E,0x98,0xED,0x31 +,0x08,0x00,0x00,0xA8,0x10,0x47 +,0x3B,0x2C,0x01,0xDB,0x40,0x11 +,0x98,0xC1,0x1E,0x98,0x10,0x07 +,0x30,0xF9,0x40,0x07,0x18,0x98 +,0x2A,0x10,0xEB,0x31,0x08,0x00 +,0x00,0xA8,0xA4,0x1E,0x98,0xBB +,0x1E,0x98,0x50,0x14,0x50,0x04 +,0x46,0x83,0x48,0x04,0x02,0x01 +,0x00,0x50,0x05,0x50,0x15,0x10 +,0x87,0x3F,0x90,0x2B,0x18,0x01 +,0x00,0xC0,0x31,0x00,0x00,0xAE +,0xDF,0x41,0x00,0x08,0x00,0x1A +,0x42,0x11,0x67,0x01,0xDF,0x41 +,0x02,0x08,0x00,0x10,0x42,0x11 +,0x62,0x01,0xB4,0x43,0x4A,0x68 +,0x50,0x14,0x50,0x04,0x24,0x10 +,0x48,0x04,0xF2,0x31,0x00,0x01 +,0x00,0x00,0xAE,0xF6,0x31,0x00 +,0x01,0x00,0x00,0xAE,0x62,0xE4 +,0xE5,0x61,0x04,0x48,0x04,0xE5 +,0x63,0x05,0x48,0x04,0x20,0x20 +,0x00,0x00,0x00,0x00 +}; + +#endif diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000.conf b/drivers/staging/ft1000/ft1000-pcmcia/ft1000.conf new file mode 100644 index 0000000..e2321a4 --- /dev/null +++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000.conf @@ -0,0 +1,14 @@ +device "ft1000_cs" + class "network" module "ft1000","ft1000_cs" + +card "flarion FT1000" + manfid 0x02cc, 0x0100 + bind "ft1000_cs" + +card "flarion FT1000" + manfid 0x02cc, 0x1000 + bind "ft1000_cs" + +card "flarion FT1000" + manfid 0x02cc, 0x1300 + bind "ft1000_cs" diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000.h b/drivers/staging/ft1000/ft1000-pcmcia/ft1000.h new file mode 100644 index 0000000..61e1cfc --- /dev/null +++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000.h @@ -0,0 +1,409 @@ +//--------------------------------------------------------------------------- +// FT1000 driver for Flarion Flash OFDM NIC Device +// +// Copyright (C) 2002 Flarion Technologies, 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. +//--------------------------------------------------------------------------- +// +// File: ft1000.h +// +// Description: Common structures and defines +// +// History: +// 8/29/02 Whc Ported to Linux. +// 7/19/04 Whc Drop packet and cmd msg with pseudo header +// checksum +// 10/27/04 Whc Added dynamic downloading of test image. +// 01/11/04 Whc Added support for Magnemite ASIC +// +//--------------------------------------------------------------------------- +#ifndef _FT1000H_ +#define _FT1000H_ + + +#define FT1000_DRV_VER 0x01010300 + +#define DSPVERSZ 4 +#define HWSERNUMSZ 16 +#define SKUSZ 20 +#define EUISZ 8 +#define MODESZ 2 +#define CALVERSZ 2 +#define CALDATESZ 6 + +// Pseudo Header structure +typedef struct _PSEUDO_HDR +{ + unsigned short length; // length of msg body + unsigned char source; // hardware source id + // Host = 0x10 + // Dsp = 0x20 + unsigned char destination; // hardware destination id (refer to source) + unsigned char portdest; // software destination port id + // Host = 0x00 + // Applicaton Broadcast = 0x10 + // Network Stack = 0x20 + // Dsp OAM = 0x80 + // Dsp Airlink = 0x90 + // Dsp Loader = 0xa0 + // Dsp MIP = 0xb0 + unsigned char portsrc; // software source port id (refer to portdest) + unsigned short sh_str_id; // not used + unsigned char control; // not used + unsigned char rsvd1; + unsigned char seq_num; // message sequence number + unsigned char rsvd2; + unsigned short qos_class; // not used + unsigned short checksum; // pseudo header checksum +} __attribute__ ((packed)) PSEUDO_HDR, *PPSEUDO_HDR; + +// Definitions to maintain compatibility between other platforms +#define UCHAR u8 +#define USHORT u16 +#define ULONG u32 +#define BOOLEAN u8 +#define PULONG u32 * +#define PUSHORT u16 * +#define PUCHAR u8 * +#define PCHAR u8 * +#define UINT u32 + +#define ELECTRABUZZ_ID 0 // ASIC ID for Electrabuzz +#define MAGNEMITE_ID 0x1a01 // ASIC ID for Magnemite + +// MEMORY MAP common to both ELECTRABUZZ and MAGNEMITE +#define FT1000_REG_DPRAM_ADDR 0x000E // DPADR - Dual Port Ram Indirect Address Register +#define FT1000_REG_SUP_CTRL 0x0020 // HCTR - Host Control Register +#define FT1000_REG_SUP_STAT 0x0022 // HSTAT - Host Status Register +#define FT1000_REG_RESET 0x0024 // HCTR - Host Control Register +#define FT1000_REG_SUP_ISR 0x0026 // HISR - Host Interrupt Status Register +#define FT1000_REG_SUP_IMASK 0x0028 // HIMASK - Host Interrupt Mask +#define FT1000_REG_DOORBELL 0x002a // DBELL - Door Bell Register +#define FT1000_REG_ASIC_ID 0x002e // ASICID - ASIC Identification Number + // (Electrabuzz=0 Magnemite=0x1A01) + +// MEMORY MAP FOR ELECTRABUZZ ASIC + +#define FT1000_REG_UFIFO_STAT 0x0000 // UFSR - Uplink FIFO status register +#define FT1000_REG_UFIFO_BEG 0x0002 // UFBR - Uplink FIFO beginning register +#define FT1000_REG_UFIFO_MID 0x0004 // UFMR - Uplink FIFO middle register +#define FT1000_REG_UFIFO_END 0x0006 // UFER - Uplink FIFO end register +#define FT1000_REG_DFIFO_STAT 0x0008 // DFSR - Downlink FIFO status register +#define FT1000_REG_DFIFO 0x000A // DFR - Downlink FIFO Register +#define FT1000_REG_DPRAM_DATA 0x000C // DPRAM - Dual Port Indirect Data Register +#define FT1000_REG_WATERMARK 0x0010 // WMARK - Watermark Register + +// MEMORY MAP FOR MAGNEMITE +#define FT1000_REG_MAG_UFDR 0x0000 // UFDR - Uplink FIFO Data Register (32-bits) +#define FT1000_REG_MAG_UFDRL 0x0000 // UFDRL - Uplink FIFO Data Register low-word (16-bits) +#define FT1000_REG_MAG_UFDRH 0x0002 // UFDRH - Uplink FIFO Data Register high-word (16-bits) +#define FT1000_REG_MAG_UFER 0x0004 // UFER - Uplink FIFO End Register +#define FT1000_REG_MAG_UFSR 0x0006 // UFSR - Uplink FIFO Status Register +#define FT1000_REG_MAG_DFR 0x0008 // DFR - Downlink FIFO Register (32-bits) +#define FT1000_REG_MAG_DFRL 0x0008 // DFRL - Downlink FIFO Register low-word (16-bits) +#define FT1000_REG_MAG_DFRH 0x000a // DFRH - Downlink FIFO Register high-word (16-bits) +#define FT1000_REG_MAG_DFSR 0x000c // DFSR - Downlink FIFO Status Register +#define FT1000_REG_MAG_DPDATA 0x0010 // DPDATA - Dual Port RAM Indirect Data Register (32-bits) +#define FT1000_REG_MAG_DPDATAL 0x0010 // DPDATAL - Dual Port RAM Indirect Data Register low-word (16-bits) +#define FT1000_REG_MAG_DPDATAH 0x0012 // DPDATAH - Dual Port RAM Indirect Data Register high-word (16-bits) +#define FT1000_REG_MAG_WATERMARK 0x002c // WMARK - Watermark Register + +// Reserved Dual Port RAM offsets for Electrabuzz +#define FT1000_DPRAM_TX_BASE 0x0002 // Host to PC Card Messaging Area +#define FT1000_DPRAM_RX_BASE 0x0800 // PC Card to Host Messaging Area +#define FT1000_FIFO_LEN 0x7FC // total length for DSP FIFO tracking +#define FT1000_HI_HO 0x7FE // heartbeat with HI/HO +#define FT1000_DSP_STATUS 0xFFE // dsp status - non-zero is a request to reset dsp +#define FT1000_DSP_LED 0xFFA // dsp led status for PAD device +#define FT1000_DSP_CON_STATE 0xFF8 // DSP Connection Status Info +#define FT1000_DPRAM_FEFE 0x002 // location for dsp ready indicator +#define FT1000_DSP_TIMER0 0x1FF0 // Timer Field from Basestation +#define FT1000_DSP_TIMER1 0x1FF2 // Timer Field from Basestation +#define FT1000_DSP_TIMER2 0x1FF4 // Timer Field from Basestation +#define FT1000_DSP_TIMER3 0x1FF6 // Timer Field from Basestation + +// Reserved Dual Port RAM offsets for Magnemite +#define FT1000_DPRAM_MAG_TX_BASE 0x0000 // Host to PC Card Messaging Area +#define FT1000_DPRAM_MAG_RX_BASE 0x0200 // PC Card to Host Messaging Area +#define FT1000_MAG_FIFO_LEN 0x1FF // total length for DSP FIFO tracking +#define FT1000_MAG_FIFO_LEN_INDX 0x1 // low-word index +#define FT1000_MAG_HI_HO 0x1FF // heartbeat with HI/HO +#define FT1000_MAG_HI_HO_INDX 0x0 // high-word index +#define FT1000_MAG_DSP_LED 0x3FE // dsp led status for PAD device +#define FT1000_MAG_DSP_LED_INDX 0x0 // dsp led status for PAD device + +#define FT1000_MAG_DSP_CON_STATE 0x3FE // DSP Connection Status Info +#define FT1000_MAG_DSP_CON_STATE_INDX 0x1 // DSP Connection Status Info + +#define FT1000_MAG_DPRAM_FEFE 0x000 // location for dsp ready indicator +#define FT1000_MAG_DPRAM_FEFE_INDX 0x0 // location for dsp ready indicator + +#define FT1000_MAG_DSP_TIMER0 0x3FC // Timer Field from Basestation +#define FT1000_MAG_DSP_TIMER0_INDX 0x1 + +#define FT1000_MAG_DSP_TIMER1 0x3FC // Timer Field from Basestation +#define FT1000_MAG_DSP_TIMER1_INDX 0x0 + +#define FT1000_MAG_DSP_TIMER2 0x3FD // Timer Field from Basestation +#define FT1000_MAG_DSP_TIMER2_INDX 0x1 + +#define FT1000_MAG_DSP_TIMER3 0x3FD // Timer Field from Basestation +#define FT1000_MAG_DSP_TIMER3_INDX 0x0 + +#define FT1000_MAG_TOTAL_LEN 0x200 +#define FT1000_MAG_TOTAL_LEN_INDX 0x1 + +#define FT1000_MAG_PH_LEN 0x200 +#define FT1000_MAG_PH_LEN_INDX 0x0 + +#define FT1000_MAG_PORT_ID 0x201 +#define FT1000_MAG_PORT_ID_INDX 0x0 + +#define HOST_INTF_LE 0x0 // Host interface little endian mode +#define HOST_INTF_BE 0x1 // Host interface big endian mode + +// PC Card to Host Doorbell assignments +#define FT1000_DB_DPRAM_RX 0x0001 // this value indicates that DSP has + // data for host in DPRAM +#define FT1000_ASIC_RESET_REQ 0x0004 // DSP requesting host to reset the ASIC +#define FT1000_DSP_ASIC_RESET 0x0008 // DSP indicating host that it will reset the ASIC +#define FT1000_DB_COND_RESET 0x0010 // DSP request for a card reset. + +// Host to PC Card Doorbell assignments +#define FT1000_DB_DPRAM_TX 0x0100 // this value indicates that host has + // data for DSP in DPRAM. +#define FT1000_ASIC_RESET_DSP 0x0400 // Responds to FT1000_ASIC_RESET_REQ +#define FT1000_DB_HB 0x1000 // Indicates that supervisor + // has a heartbeat message for DSP. + +#define FT1000_DPRAM_BASE 0x0000 // Dual Port RAM starting offset + +#define hi 0x6869 // PC Card heartbeat values +#define ho 0x686f // PC Card heartbeat values + +// Magnemite specific defines +#define hi_mag 0x6968 // Byte swap hi to avoid additional system call +#define ho_mag 0x6f68 // Byte swap ho to avoid additional system call + +// +// Bit field definitions for Host Interrupt Status Register +// +// Indicate the cause of an interrupt. +// +#define ISR_EMPTY 0x00 // no bits set +#define ISR_DOORBELL_ACK 0x01 // Doorbell acknowledge from DSP +#define ISR_DOORBELL_PEND 0x02 // Doorbell pending from DSP +#define ISR_RCV 0x04 // Packet available in Downlink FIFO +#define ISR_WATERMARK 0x08 // Watermark requirements satisfied + +// Bit field definition for Host Interrupt Mask +#define ISR_MASK_NONE 0x0000 // no bits set +#define ISR_MASK_DOORBELL_ACK 0x0001 // Doorbell acknowledge mask +#define ISR_MASK_DOORBELL_PEND 0x0002 // Doorbell pending mask +#define ISR_MASK_RCV 0x0004 // Downlink Packet available mask +#define ISR_MASK_WATERMARK 0x0008 // Watermark interrupt mask +#define ISR_MASK_ALL 0xffff // Mask all interrupts + +// Bit field definition for Host Control Register +#define DSP_RESET_BIT 0x0001 // Bit field to control dsp reset state + // (0 = out of reset 1 = reset) +#define ASIC_RESET_BIT 0x0002 // Bit field to control ASIC reset state + // (0 = out of reset 1 = reset) + +// Default interrupt mask (Enable Doorbell pending and Packet available interrupts) +#define ISR_DEFAULT_MASK 0x7ff9 + +// Application specific IDs +#define DSPID 0x20 +#define HOSTID 0x10 +#define DSPAIRID 0x90 +#define DRIVERID 0x00 +#define NETWORKID 0x20 + +// Size of DPRAM Message +#define MAX_CMD_SQSIZE 1780 + +#define ENET_MAX_SIZE 1514 +#define ENET_HEADER_SIZE 14 + +#define SLOWQ_TYPE 0 +#define FASTQ_TYPE 1 + +#define MAX_DSP_SESS_REC 1024 + +#define DSP_QID_OFFSET 4 +#define PSEUDOSZ 16 +#define PSEUDOSZWRD 8 + +// Maximum number of occurrence of pseudo header errors before resetting PC Card. +#define MAX_PH_ERR 300 + +// Driver message types +#define MEDIA_STATE 0x0010 +#define TIME_UPDATE 0x0020 +#define DSP_PROVISION 0x0030 +#define DSP_INIT_MSG 0x0050 +#define DSP_HIBERNATE 0x0060 + +#define DSP_STORE_INFO 0x0070 +#define DSP_GET_INFO 0x0071 +#define GET_DRV_ERR_RPT_MSG 0x0073 +#define RSP_DRV_ERR_RPT_MSG 0x0074 + +// Driver Error Messages for DSP +#define DSP_HB_INFO 0x7ef0 +#define DSP_FIFO_INFO 0x7ef1 +#define DSP_CONDRESET_INFO 0x7ef2 +#define DSP_CMDLEN_INFO 0x7ef3 +#define DSP_CMDPHCKSUM_INFO 0x7ef4 +#define DSP_PKTPHCKSUM_INFO 0x7ef5 +#define DSP_PKTLEN_INFO 0x7ef6 +#define DSP_USER_RESET 0x7ef7 +#define FIFO_FLUSH_MAXLIMIT 0x7ef8 +#define FIFO_FLUSH_BADCNT 0x7ef9 +#define FIFO_ZERO_LEN 0x7efa + +#define HOST_QID_OFFSET 5 +#define QTYPE_OFFSET 13 + +#define SUCCESS 0x00 +#define FAILURE 0x01 +#define TRUE 0x1 +#define FALSE 0x0 + +#define MAX_NUM_APP 6 + +#define MAXIMUM_ASIC_HB_CNT 15 + +typedef struct _DRVMSG { + PSEUDO_HDR pseudo; + u16 type; + u16 length; + u8 data[0]; +} __attribute__ ((packed)) DRVMSG, *PDRVMSG; + +typedef struct _MEDIAMSG { + PSEUDO_HDR pseudo; + u16 type; + u16 length; + u16 state; + u32 ip_addr; + u32 net_mask; + u32 gateway; + u32 dns_1; + u32 dns_2; +} __attribute__ ((packed)) MEDIAMSG, *PMEDIAMSG; + +typedef struct _TIMEMSG { + PSEUDO_HDR pseudo; + u16 type; + u16 length; + u8 timeval[8]; +} __attribute__ ((packed)) TIMEMSG, *PTIMEMSG; + +typedef struct _DSPINITMSG { + PSEUDO_HDR pseudo; + u16 type; + u16 length; + u8 DspVer[DSPVERSZ]; // DSP version number + u8 HwSerNum[HWSERNUMSZ]; // Hardware Serial Number + u8 Sku[SKUSZ]; // SKU + u8 eui64[EUISZ]; // EUI64 + u8 ProductMode[MODESZ]; // Product Mode (Market/Production) + u8 RfCalVer[CALVERSZ]; // Rf Calibration version + u8 RfCalDate[CALDATESZ]; // Rf Calibration date +} __attribute__ ((packed)) DSPINITMSG, *PDSPINITMSG; + +typedef struct _DSPHIBERNATE { + PSEUDO_HDR pseudo; + u16 type; + u16 length; + u32 timeout; + u16 sess_info[0]; +} DSPHIBERNATE, *PDSPHIBERNATE; + +typedef struct _APP_INFO_BLOCK +{ + u32 fileobject; // Application's file object + u16 app_id; // Application id +} APP_INFO_BLOCK, *PAPP_INFO_BLOCK; + +typedef struct _PROV_RECORD { + struct list_head list; + u8 *pprov_data; +} PROV_RECORD, *PPROV_RECORD; + +typedef struct _FT1000_INFO { + struct net_device_stats stats; + u16 DrvErrNum; + u16 AsicID; + int ASICResetNum; + int DspAsicReset; + int PktIntfErr; + int DSPResetNum; + int NumIOCTLBufs; + int IOCTLBufLvl; + int DeviceCreated; + int CardReady; + int DspHibernateFlag; + int DSPReady; + u8 DeviceName[15]; + int DeviceMajor; + int registered; + int mediastate; + u16 packetseqnum; + u8 squeseqnum; // sequence number on slow queue + spinlock_t dpram_lock; + u16 CurrentInterruptEnableMask; + int InterruptsEnabled; + u16 fifo_cnt; + u8 DspVer[DSPVERSZ]; // DSP version number + u8 HwSerNum[HWSERNUMSZ]; // Hardware Serial Number + u8 Sku[SKUSZ]; // SKU + u8 eui64[EUISZ]; // EUI64 + time_t ConTm; // Connection Time + u16 LedStat; + u16 ConStat; + u16 ProgConStat; + u8 ProductMode[MODESZ]; + u8 RfCalVer[CALVERSZ]; + u8 RfCalDate[CALDATESZ]; + u16 DSP_TIME[4]; + struct list_head prov_list; + int appcnt; + APP_INFO_BLOCK app_info[MAX_NUM_APP]; + u16 DSPInfoBlklen; + u16 DrvMsgPend; + int (*ft1000_reset)(void *); + void *link; + u16 DSPInfoBlk[MAX_DSP_SESS_REC]; + union { + u16 Rec[MAX_DSP_SESS_REC]; + u32 MagRec[MAX_DSP_SESS_REC/2]; + } DSPSess; + struct proc_dir_entry *proc_ft1000; + char netdevname[IFNAMSIZ]; +} FT1000_INFO, *PFT1000_INFO; + +typedef struct _DPRAM_BLK { + struct list_head list; + u16 *pbuffer; +} __attribute__ ((packed)) DPRAM_BLK, *PDPRAM_BLK; + +extern u16 ft1000_read_dpram (struct net_device *dev, int offset); +extern void card_bootload(struct net_device *dev); +extern u16 ft1000_read_dpram_mag_16 (struct net_device *dev, int offset, int Index); +extern u32 ft1000_read_dpram_mag_32 (struct net_device *dev, int offset); +void ft1000_write_dpram_mag_32 (struct net_device *dev, int offset, u32 value); + +#endif // _FT1000H_ diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000.img b/drivers/staging/ft1000/ft1000-pcmcia/ft1000.img Binary files differnew file mode 100644 index 0000000..aad3c80 --- /dev/null +++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000.img diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.c b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.c new file mode 100644 index 0000000..2163eae --- /dev/null +++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.c @@ -0,0 +1,513 @@ +/*--------------------------------------------------------------------------- + FT1000 driver for Flarion Flash OFDM NIC Device + + Copyright (C) 1999 David A. Hinds. All Rights Reserved. + 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. + + The initial developer of the original code is David A. Hinds + <dahinds@users.sourceforge.net>. Portions created by David A. Hinds. + + This file was modified to support the Flarion Flash OFDM NIC Device + by Wai Chan (w.chan@flarion.com). + + Port for kernel 2.6 created by Patrik Ostrihon (patrik.ostrihon@pwc.sk) + + 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/init.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/ioport.h> +#include <linux/delay.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> + +//#include <pcmcia/version.h> // Slavius 21.10.2009 removed from kernel +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/cisreg.h> +#include <pcmcia/ds.h> + +#include <asm/io.h> +#include <asm/system.h> +#include <asm/byteorder.h> +#include <asm/uaccess.h> + +#include "ft1000_cs.h" // Slavius 21.10.2009 because CS_SUCCESS constant is missing due to removed pcmcia/version.h + +/*====================================================================*/ + +/* Module parameters */ + +#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i") + +MODULE_AUTHOR("Wai Chan"); +MODULE_DESCRIPTION("FT1000 PCMCIA driver"); +MODULE_LICENSE("GPL"); + +/* Newer, simpler way of listing specific interrupts */ + +/* The old way: bit map of interrupts to choose from */ +/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */ + +/* + All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If + you do not define PCMCIA_DEBUG at all, all the debug code will be + left out. If you compile with PCMCIA_DEBUG=0, the debug code will + be present but disabled. +*/ +#ifdef FT_DEBUG +#define DEBUG(n, args...) printk(KERN_DEBUG args) +#else +#define DEBUG(n, args...) +#endif + +/*====================================================================*/ + +struct net_device *init_ft1000_card(int, int, unsigned char *, + void *ft1000_reset, struct pcmcia_device * link, + struct device *fdev); +void stop_ft1000_card(struct net_device *); + +static int ft1000_config(struct pcmcia_device *link); +static void ft1000_release(struct pcmcia_device *link); + +/* + The attach() and detach() entry points are used to create and destroy + "instances" of the driver, where each instance represents everything + needed to manage one actual PCMCIA card. +*/ + +static void ft1000_detach(struct pcmcia_device *link); +static int ft1000_attach(struct pcmcia_device *link); + +typedef struct local_info_t { + struct pcmcia_device *link; + struct net_device *dev; +} local_info_t; + +#define MAX_ASIC_RESET_CNT 10 +#define COR_DEFAULT 0x55 + +/*====================================================================*/ + +static void ft1000_reset(struct pcmcia_device * link) +{ + conf_reg_t reg; + + DEBUG(0, "ft1000_cs:ft1000_reset is called................\n"); + + /* Soft-Reset card */ + reg.Action = CS_WRITE; + reg.Offset = CISREG_COR; + reg.Value = COR_SOFT_RESET; + pcmcia_access_configuration_register(link, ®); + + /* Wait until the card has acknowledged our reset */ + udelay(2); + + /* Restore original COR configuration index */ + /* Need at least 2 write to respond */ + reg.Action = CS_WRITE; + reg.Offset = CISREG_COR; + reg.Value = COR_DEFAULT; + pcmcia_access_configuration_register(link, ®); + + /* Wait until the card has finished restarting */ + udelay(1); + + reg.Action = CS_WRITE; + reg.Offset = CISREG_COR; + reg.Value = COR_DEFAULT; + pcmcia_access_configuration_register(link, ®); + + /* Wait until the card has finished restarting */ + udelay(1); + + reg.Action = CS_WRITE; + reg.Offset = CISREG_COR; + reg.Value = COR_DEFAULT; + pcmcia_access_configuration_register(link, ®); + + /* Wait until the card has finished restarting */ + udelay(1); + +} + +/*====================================================================*/ + +static int get_tuple_first(struct pcmcia_device *link, tuple_t * tuple, + cisparse_t * parse) +{ + int i; + i = pcmcia_get_first_tuple(link, tuple); + if (i != CS_SUCCESS) + return i; + i = pcmcia_get_tuple_data(link, tuple); + if (i != CS_SUCCESS) + return i; + return pcmcia_parse_tuple(tuple, parse); // Slavius 21.10.2009 removed unused link parameter +} + +static int get_tuple_next(struct pcmcia_device *link, tuple_t * tuple, + cisparse_t * parse) +{ + int i; + i = pcmcia_get_next_tuple(link, tuple); + if (i != CS_SUCCESS) + return i; + i = pcmcia_get_tuple_data(link, tuple); + if (i != CS_SUCCESS) + return i; + return pcmcia_parse_tuple(tuple, parse); // Slavius 21.10.2009 removed unused link parameter +} + +/*====================================================================== + + +======================================================================*/ + +static int ft1000_attach(struct pcmcia_device *link) +{ + + local_info_t *local; + + DEBUG(0, "ft1000_cs: ft1000_attach()\n"); + + local = kmalloc(sizeof(local_info_t), GFP_KERNEL); + if (!local) { + return -ENOMEM; + } + memset(local, 0, sizeof(local_info_t)); + local->link = link; + + link->priv = local; + local->dev = NULL; + + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.IRQInfo1 = IRQ_LEVEL_ID; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.IntType = INT_MEMORY_AND_IO; + link->irq.Handler = NULL; + + return ft1000_config(link); +} /* ft1000_attach */ + +/*====================================================================== + + This deletes a driver "instance". The device is de-registered + with Card Services. If it has been released, all local data + structures are freed. Otherwise, the structures will be freed + when the device is released. + +======================================================================*/ + +static void ft1000_detach(struct pcmcia_device *link) +{ + struct net_device *dev = ((local_info_t *) link->priv)->dev; + + DEBUG(0, "ft1000_cs: ft1000_detach(0x%p)\n", link); + + if (link == NULL) { + DEBUG(0,"ft1000_cs:ft1000_detach: Got a NULL pointer\n"); + return; + } + + if (dev) { + stop_ft1000_card(dev); + } + + ft1000_release(link); + + /* This points to the parent local_info_t struct */ + free_netdev(dev); + +} /* ft1000_detach */ + +/*====================================================================== + + ft1000_config() is scheduled to run after a CARD_INSERTION event + is received, to configure the PCMCIA socket, and to make the + device available to the system. + +======================================================================*/ + +#define CS_CHECK(fn, ret) \ + do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) + +#define CFG_CHECK(fn, ret) \ + last_fn = (fn); if ((last_ret = (ret)) != 0) goto next_entry + +static int ft1000_config(struct pcmcia_device * link) +{ + tuple_t tuple; + cisparse_t parse; + int last_fn, last_ret, i; + u_char buf[64]; + cistpl_lan_node_id_t *node_id; + cistpl_cftable_entry_t dflt = { 0 }; + cistpl_cftable_entry_t *cfg; + unsigned char mac_address[6]; + + DEBUG(0, "ft1000_cs: ft1000_config(0x%p)\n", link); + + /* + This reads the card's CONFIG tuple to find its configuration + registers. + */ +// tuple.DesiredTuple = CISTPL_CONFIG; +// tuple.Attributes = 0; + tuple.TupleData = buf; + tuple.TupleDataMax = sizeof(buf); + tuple.TupleOffset = 0; +// CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); +// CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); +// CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); +// link->conf.ConfigBase = parse.config.base; +// link->conf.Present = parse.config.rmask[0]; + + /* + In this loop, we scan the CIS for configuration table entries, + each of which describes a valid card configuration, including + voltage, IO window, memory window, and interrupt settings. + + We make no assumptions about the card to be configured: we use + just the information available in the CIS. In an ideal world, + this would work for any PCMCIA card, but it requires a complete + and accurate CIS. In practice, a driver usually "knows" most of + these things without consulting the CIS, and most client drivers + will only use the CIS to fill in implementation-defined details. + */ + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + tuple.Attributes = 0; + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + while (1) { + cfg = &(parse.cftable_entry); + CFG_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CFG_CHECK(ParseTuple, + pcmcia_parse_tuple(&tuple, &parse)); // Slavius 21.10.2009 removed unused link parameter + + if (cfg->flags & CISTPL_CFTABLE_DEFAULT) + dflt = *cfg; + if (cfg->index == 0) + goto next_entry; + link->conf.ConfigIndex = cfg->index; + + /* Do we need to allocate an interrupt? */ + if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) + link->conf.Attributes |= CONF_ENABLE_IRQ; + + /* IO window settings */ + link->io.NumPorts1 = link->io.NumPorts2 = 0; + if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) { + cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + if (!(io->flags & CISTPL_IO_8BIT)) { + DEBUG(0, "ft1000_cs: IO_DATA_PATH_WIDTH_16\n"); + link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; + } + if (!(io->flags & CISTPL_IO_16BIT)) { + DEBUG(0, "ft1000_cs: IO_DATA_PATH_WIDTH_8\n"); + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + } + link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK; + link->io.BasePort1 = io->win[0].base; + link->io.NumPorts1 = io->win[0].len; + if (io->nwin > 1) { + link->io.Attributes2 = link->io.Attributes1; + link->io.BasePort2 = io->win[1].base; + link->io.NumPorts2 = io->win[1].len; + } + /* This reserves IO space but doesn't actually enable it */ + pcmcia_request_io(link, &link->io); + } + + break; + + next_entry: + last_ret = pcmcia_get_next_tuple(link, &tuple); + } + if (last_ret != CS_SUCCESS) { + cs_error(link, RequestIO, last_ret); + goto failed; + } + + /* + Allocate an interrupt line. Note that this does not assign a + handler to the interrupt, unless the 'Handler' member of the + irq structure is initialized. + */ + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); + + /* + This actually configures the PCMCIA socket -- setting up + the I/O windows and the interrupt mapping, and putting the + card and host interface into "Memory and IO" mode. + */ + CS_CHECK(RequestConfiguration, + pcmcia_request_configuration(link, &link->conf)); + + /* Get MAC address from tuples */ + + tuple.Attributes = tuple.TupleOffset = 0; + tuple.TupleData = buf; + tuple.TupleDataMax = sizeof(buf); + + /* Check for a LAN function extension tuple */ + tuple.DesiredTuple = CISTPL_FUNCE; + i = get_tuple_first(link, &tuple, &parse); + while (i == CS_SUCCESS) { + if (parse.funce.type == CISTPL_FUNCE_LAN_NODE_ID) + break; + i = get_tuple_next(link, &tuple, &parse); + } + + if (i == CS_SUCCESS) { + node_id = (cistpl_lan_node_id_t *) parse.funce.data; + if (node_id->nb == 6) { + for (i = 0; i < 6; i++) + mac_address[i] = node_id->id[i]; + } + } + + ((local_info_t *) link->priv)->dev = + init_ft1000_card(link->irq.AssignedIRQ, link->io.BasePort1, + &mac_address[0], ft1000_reset, link, + &handle_to_dev(link)); + + /* + At this point, the dev_node_t structure(s) need to be + initialized and arranged in a linked list at link->dev. + */ + + /* Finally, report what we've done */ + + return 0; + +cs_failed: + cs_error(link, last_fn, last_ret); +failed: + ft1000_release(link); + return -ENODEV; + +} /* ft1000_config */ + +/*====================================================================== + + After a card is removed, ft1000_release() will unregister the + device, and release the PCMCIA configuration. If the device is + still open, this will be postponed until it is closed. + +======================================================================*/ + +static void ft1000_release(struct pcmcia_device * link) +{ + + DEBUG(0, "ft1000_cs: ft1000_release(0x%p)\n", link); + + /* + If the device is currently in use, we won't release until it + is actually closed, because until then, we can't be sure that + no one will try to access the device or its data structures. + */ + + /* Unlink the device chain */ + link->dev_node = NULL; + + /* + In a normal driver, additional code may be needed to release + other kernel data structures associated with this device. + */ + + /* Don't bother checking to see if these succeed or not */ + + pcmcia_disable_device(link); +} /* ft1000_release */ + +/*====================================================================== + + The card status event handler. Mostly, this schedules other + stuff to run after an event is received. + + When a CARD_REMOVAL event is received, we immediately set a + private flag to block future accesses to this device. All the + functions that actually access the device should check this flag + to make sure the card is still present. + +======================================================================*/ + +static int ft1000_suspend(struct pcmcia_device *link) +{ + struct net_device *dev = ((local_info_t *) link->priv)->dev; + + DEBUG(1, "ft1000_cs: ft1000_event(0x%06x)\n", event); + + if (link->open) + netif_device_detach(dev); + return 0; +} + +static int ft1000_resume(struct pcmcia_device *link) +{ +/* struct net_device *dev = link->priv; + */ + return 0; +} + + + +/*====================================================================*/ + +static struct pcmcia_device_id ft1000_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x02cc, 0x0100), + PCMCIA_DEVICE_MANF_CARD(0x02cc, 0x1000), + PCMCIA_DEVICE_MANF_CARD(0x02cc, 0x1300), + PCMCIA_DEVICE_NULL, +}; + +MODULE_DEVICE_TABLE(pcmcia, ft1000_ids); + +static struct pcmcia_driver ft1000_cs_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "ft1000_cs", + }, + .probe = ft1000_attach, + .remove = ft1000_detach, + .id_table = ft1000_ids, + .suspend = ft1000_suspend, + .resume = ft1000_resume, +}; + +static int __init init_ft1000_cs(void) +{ + DEBUG(0, "ft1000_cs: loading\n"); + return pcmcia_register_driver(&ft1000_cs_driver); +} + +static void __exit exit_ft1000_cs(void) +{ + DEBUG(0, "ft1000_cs: unloading\n"); + pcmcia_unregister_driver(&ft1000_cs_driver); +} + +module_init(init_ft1000_cs); +module_exit(exit_ft1000_cs); diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.h b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.h new file mode 100644 index 0000000..2b5e383 --- /dev/null +++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.h @@ -0,0 +1 @@ +#define CS_SUCCESS 0x00 diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dev.h b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dev.h new file mode 100644 index 0000000..4a89bd1 --- /dev/null +++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dev.h @@ -0,0 +1,66 @@ +//--------------------------------------------------------------------------- +// FT1000 driver for Flarion Flash OFDM NIC Device +// +// Copyright (C) 2002 Flarion Technologies, 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. +//--------------------------------------------------------------------------- +// +// File: ft1000_dev.h +// +// Description: Register definitions and bit masks for the FT1000 NIC +// +// History: +// 2/5/02 Ivan Bohannon Written. +// 8/29/02 Whc Ported to Linux. +// +//--------------------------------------------------------------------------- +#ifndef _FT1000_DEVH_ +#define _FT1000_DEVH_ + +//--------------------------------------------------------------------------- +// +// Function: ft1000_read_reg +// Descripton: This function will read the value of a given ASIC register. +// Input: +// dev - device structure +// offset - ASIC register offset +// Output: +// data - ASIC register value +// +//--------------------------------------------------------------------------- +static inline u16 ft1000_read_reg (struct net_device *dev, u16 offset) { + u16 data = 0; + + data = inw(dev->base_addr + offset); + + return (data); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_write_reg +// Descripton: This function will set the value for a given ASIC register. +// Input: +// dev - device structure +// offset - ASIC register offset +// value - value to write +// Output: +// None. +// +//--------------------------------------------------------------------------- +static inline void ft1000_write_reg (struct net_device *dev, u16 offset, u16 value) { + outw (value, dev->base_addr + offset); +} + +#endif // _FT1000_DEVH_ + diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c new file mode 100644 index 0000000..0bf398d --- /dev/null +++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c @@ -0,0 +1,940 @@ +/*--------------------------------------------------------------------------- + FT1000 driver for Flarion Flash OFDM NIC Device + + Copyright (C) 2002 Flarion Technologies, 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. + -------------------------------------------------------------------------- + + Description: This module will handshake with the DSP bootloader to + download the DSP runtime image. + +---------------------------------------------------------------------------*/ + +#define __KERNEL_SYSCALLS__ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/unistd.h> +#include <linux/netdevice.h> +#include <linux/timer.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <linux/vmalloc.h> + +#include "ft1000_dev.h" +#include "ft1000.h" +#include "boot.h" + +#ifdef FT_DEBUG +#define DEBUG(n, args...) printk(KERN_DEBUG args); +#else +#define DEBUG(n, args...) +#endif + +#define MAX_DSP_WAIT_LOOPS 100 +#define DSP_WAIT_SLEEP_TIME 1 /* 1 millisecond */ + +#define MAX_LENGTH 0x7f0 + +#define DWNLD_MAG_HANDSHAKE_LOC 0x00 +#define DWNLD_MAG_TYPE_LOC 0x01 +#define DWNLD_MAG_SIZE_LOC 0x02 +#define DWNLD_MAG_PS_HDR_LOC 0x03 + +#define DWNLD_HANDSHAKE_LOC 0x02 +#define DWNLD_TYPE_LOC 0x04 +#define DWNLD_SIZE_MSW_LOC 0x06 +#define DWNLD_SIZE_LSW_LOC 0x08 +#define DWNLD_PS_HDR_LOC 0x0A + +#define HANDSHAKE_TIMEOUT_VALUE 0xF1F1 +#define HANDSHAKE_RESET_VALUE 0xFEFE /* When DSP requests startover */ +#define HANDSHAKE_DSP_BL_READY 0xFEFE /* At start DSP writes this when bootloader ready */ +#define HANDSHAKE_DRIVER_READY 0xFFFF /* Driver writes after receiving 0xFEFE */ +#define HANDSHAKE_SEND_DATA 0x0000 /* DSP writes this when ready for more data */ + +#define HANDSHAKE_REQUEST 0x0001 /* Request from DSP */ +#define HANDSHAKE_RESPONSE 0x0000 /* Satisfied DSP request */ + +#define REQUEST_CODE_LENGTH 0x0000 +#define REQUEST_RUN_ADDRESS 0x0001 +#define REQUEST_CODE_SEGMENT 0x0002 /* In WORD count */ +#define REQUEST_DONE_BL 0x0003 +#define REQUEST_DONE_CL 0x0004 +#define REQUEST_VERSION_INFO 0x0005 +#define REQUEST_CODE_BY_VERSION 0x0006 +#define REQUEST_MAILBOX_DATA 0x0007 +#define REQUEST_FILE_CHECKSUM 0x0008 + +#define STATE_START_DWNLD 0x01 +#define STATE_BOOT_DWNLD 0x02 +#define STATE_CODE_DWNLD 0x03 +#define STATE_DONE_DWNLD 0x04 +#define STATE_SECTION_PROV 0x05 +#define STATE_DONE_PROV 0x06 +#define STATE_DONE_FILE 0x07 + +USHORT get_handshake(struct net_device *dev, USHORT expected_value); +void put_handshake(struct net_device *dev, USHORT handshake_value); +USHORT get_request_type(struct net_device *dev); +long get_request_value(struct net_device *dev); +void put_request_value(struct net_device *dev, long lvalue); +USHORT hdr_checksum(PPSEUDO_HDR pHdr); + +typedef struct _DSP_FILE_HDR { + long build_date; + long dsp_coff_date; + long loader_code_address; + long loader_code_size; + long loader_code_end; + long dsp_code_address; + long dsp_code_size; + long dsp_code_end; + long reserved[8]; +} __attribute__ ((packed)) DSP_FILE_HDR, *PDSP_FILE_HDR; + +typedef struct _DSP_FILE_HDR_5 { + long version_id; // Version ID of this image format. + long package_id; // Package ID of code release. + long build_date; // Date/time stamp when file was built. + long commands_offset; // Offset to attached commands in Pseudo Hdr format. + long loader_offset; // Offset to bootloader code. + long loader_code_address; // Start address of bootloader. + long loader_code_end; // Where bootloader code ends. + long loader_code_size; + long version_data_offset; // Offset were scrambled version data begins. + long version_data_size; // Size, in words, of scrambled version data. + long nDspImages; // Number of DSP images in file. +} __attribute__ ((packed)) DSP_FILE_HDR_5, *PDSP_FILE_HDR_5; + +typedef struct _DSP_IMAGE_INFO { + long coff_date; // Date/time when DSP Coff image was built. + long begin_offset; // Offset in file where image begins. + long end_offset; // Offset in file where image begins. + long run_address; // On chip Start address of DSP code. + long image_size; // Size of image. + long version; // Embedded version # of DSP code. +} __attribute__ ((packed)) DSP_IMAGE_INFO, *PDSP_IMAGE_INFO; + +typedef struct _DSP_IMAGE_INFO_V6 { + long coff_date; // Date/time when DSP Coff image was built. + long begin_offset; // Offset in file where image begins. + long end_offset; // Offset in file where image begins. + long run_address; // On chip Start address of DSP code. + long image_size; // Size of image. + long version; // Embedded version # of DSP code. + unsigned short checksum; // Dsp File checksum + unsigned short pad1; +} __attribute__ ((packed)) DSP_IMAGE_INFO_V6, *PDSP_IMAGE_INFO_V6; + +void card_bootload(struct net_device *dev) +{ + FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev); + unsigned long flags; + PULONG pdata; + UINT size; + UINT i; + ULONG templong; + + DEBUG(0, "card_bootload is called\n"); + + pdata = (PULONG) bootimage; + size = sizeof(bootimage); + + // check for odd word + if (size & 0x0003) { + size += 4; + } + // Provide mutual exclusive access while reading ASIC registers. + spin_lock_irqsave(&info->dpram_lock, flags); + + // need to set i/o base address initially and hardware will autoincrement + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_BASE); + // write bytes + for (i = 0; i < (size >> 2); i++) { + templong = *pdata++; + outl(templong, dev->base_addr + FT1000_REG_MAG_DPDATA); + } + + spin_unlock_irqrestore(&info->dpram_lock, flags); +} + +USHORT get_handshake(struct net_device *dev, USHORT expected_value) +{ + FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev); + USHORT handshake; + ULONG tempx; + int loopcnt; + + loopcnt = 0; + while (loopcnt < MAX_DSP_WAIT_LOOPS) { + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + DWNLD_HANDSHAKE_LOC); + + handshake = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); + } else { + tempx = + ntohl(ft1000_read_dpram_mag_32 + (dev, DWNLD_MAG_HANDSHAKE_LOC)); + handshake = (USHORT) tempx; + } + + if ((handshake == expected_value) + || (handshake == HANDSHAKE_RESET_VALUE)) { + return handshake; + } else { + loopcnt++; + mdelay(DSP_WAIT_SLEEP_TIME); + } + + } + + return HANDSHAKE_TIMEOUT_VALUE; + +} + +void put_handshake(struct net_device *dev, USHORT handshake_value) +{ + FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev); + ULONG tempx; + + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + DWNLD_HANDSHAKE_LOC); + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, handshake_value); /* Handshake */ + } else { + tempx = (ULONG) handshake_value; + tempx = ntohl(tempx); + ft1000_write_dpram_mag_32(dev, DWNLD_MAG_HANDSHAKE_LOC, tempx); /* Handshake */ + } +} + +USHORT get_request_type(struct net_device *dev) +{ + FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev); + USHORT request_type; + ULONG tempx; + + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, DWNLD_TYPE_LOC); + request_type = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); + } else { + tempx = ft1000_read_dpram_mag_32(dev, DWNLD_MAG_TYPE_LOC); + tempx = ntohl(tempx); + request_type = (USHORT) tempx; + } + + return request_type; + +} + +long get_request_value(struct net_device *dev) +{ + FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev); + long value; + USHORT w_val; + + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + DWNLD_SIZE_MSW_LOC); + + w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); + + value = (long)(w_val << 16); + + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + DWNLD_SIZE_LSW_LOC); + + w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); + + value = (long)(value | w_val); + } else { + value = ft1000_read_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC); + value = ntohl(value); + } + + return value; + +} + +void put_request_value(struct net_device *dev, long lvalue) +{ + FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev); + USHORT size; + ULONG tempx; + + if (info->AsicID == ELECTRABUZZ_ID) { + size = (USHORT) (lvalue >> 16); + + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + DWNLD_SIZE_MSW_LOC); + + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size); + + size = (USHORT) (lvalue); + + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + DWNLD_SIZE_LSW_LOC); + + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size); + } else { + tempx = ntohl(lvalue); + ft1000_write_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC, tempx); /* Handshake */ + } + +} + +USHORT hdr_checksum(PPSEUDO_HDR pHdr) +{ + USHORT *usPtr = (USHORT *) pHdr; + USHORT chksum; + + chksum = ((((((usPtr[0] ^ usPtr[1]) ^ usPtr[2]) ^ usPtr[3]) ^ + usPtr[4]) ^ usPtr[5]) ^ usPtr[6]); + + return chksum; +} + +int card_download(struct net_device *dev, void *pFileStart, UINT FileLength) +{ + FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev); + int Status = SUCCESS; + USHORT DspWordCnt = 0; + UINT uiState; + USHORT handshake; + PPSEUDO_HDR pHdr; + USHORT usHdrLength; + PDSP_FILE_HDR pFileHdr; + long word_length; + USHORT request; + USHORT temp; + PPROV_RECORD pprov_record; + PUCHAR pbuffer; + PDSP_FILE_HDR_5 pFileHdr5; + PDSP_IMAGE_INFO pDspImageInfo = NULL; + PDSP_IMAGE_INFO_V6 pDspImageInfoV6 = NULL; + long requested_version; + BOOLEAN bGoodVersion = 0; + PDRVMSG pMailBoxData; + USHORT *pUsData = NULL; + USHORT *pUsFile = NULL; + UCHAR *pUcFile = NULL; + UCHAR *pBootEnd = NULL; + UCHAR *pCodeEnd = NULL; + int imageN; + long file_version; + long loader_code_address = 0; + long loader_code_size = 0; + long run_address = 0; + long run_size = 0; + unsigned long flags; + unsigned long templong; + unsigned long image_chksum = 0; + + // + // Get version id of file, at first 4 bytes of file, for newer files. + // + file_version = *(long *)pFileStart; + + uiState = STATE_START_DWNLD; + + pFileHdr = (PDSP_FILE_HDR) pFileStart; + pFileHdr5 = (PDSP_FILE_HDR_5) pFileStart; + + switch (file_version) { + case 5: + case 6: + pUsFile = + (USHORT *) ((long)pFileStart + pFileHdr5->loader_offset); + pUcFile = + (UCHAR *) ((long)pFileStart + pFileHdr5->loader_offset); + + pBootEnd = + (UCHAR *) ((long)pFileStart + pFileHdr5->loader_code_end); + + loader_code_address = pFileHdr5->loader_code_address; + loader_code_size = pFileHdr5->loader_code_size; + bGoodVersion = FALSE; + break; + + default: + Status = FAILURE; + break; + } + + while ((Status == SUCCESS) && (uiState != STATE_DONE_FILE)) { + + switch (uiState) { + case STATE_START_DWNLD: + + handshake = get_handshake(dev, HANDSHAKE_DSP_BL_READY); + + if (handshake == HANDSHAKE_DSP_BL_READY) { + put_handshake(dev, HANDSHAKE_DRIVER_READY); + } else { + Status = FAILURE; + } + + uiState = STATE_BOOT_DWNLD; + + break; + + case STATE_BOOT_DWNLD: + handshake = get_handshake(dev, HANDSHAKE_REQUEST); + if (handshake == HANDSHAKE_REQUEST) { + /* + * Get type associated with the request. + */ + request = get_request_type(dev); + switch (request) { + case REQUEST_RUN_ADDRESS: + put_request_value(dev, + loader_code_address); + break; + case REQUEST_CODE_LENGTH: + put_request_value(dev, + loader_code_size); + break; + case REQUEST_DONE_BL: + /* Reposition ptrs to beginning of code section */ + pUsFile = (USHORT *) ((long)pBootEnd); + pUcFile = (UCHAR *) ((long)pBootEnd); + uiState = STATE_CODE_DWNLD; + break; + case REQUEST_CODE_SEGMENT: + word_length = get_request_value(dev); + if (word_length > MAX_LENGTH) { + Status = FAILURE; + break; + } + if ((word_length * 2 + (long)pUcFile) > + (long)pBootEnd) { + /* + * Error, beyond boot code range. + */ + Status = FAILURE; + break; + } + // Provide mutual exclusive access while reading ASIC registers. + spin_lock_irqsave(&info->dpram_lock, + flags); + if (file_version == 5) { + /* + * Position ASIC DPRAM auto-increment pointer. + */ + ft1000_write_reg(dev, + FT1000_REG_DPRAM_ADDR, + DWNLD_PS_HDR_LOC); + + for (; word_length > 0; word_length--) { /* In words */ + //temp = *pUsFile; + //temp = RtlUshortByteSwap(temp); + ft1000_write_reg(dev, + FT1000_REG_DPRAM_DATA, + *pUsFile); + pUsFile++; + pUcFile += 2; + DspWordCnt++; + } + } else { + /* + * Position ASIC DPRAM auto-increment pointer. + */ + outw(DWNLD_MAG_PS_HDR_LOC, + dev->base_addr + + FT1000_REG_DPRAM_ADDR); + if (word_length & 0x01) { + word_length++; + } + word_length = word_length / 2; + + for (; word_length > 0; word_length--) { /* In words */ + templong = *pUsFile++; + templong |= + (*pUsFile++ << 16); + pUcFile += 4; + outl(templong, + dev->base_addr + + FT1000_REG_MAG_DPDATAL); + } + } + spin_unlock_irqrestore(&info-> + dpram_lock, + flags); + break; + default: + Status = FAILURE; + break; + } + put_handshake(dev, HANDSHAKE_RESPONSE); + } else { + Status = FAILURE; + } + + break; + + case STATE_CODE_DWNLD: + handshake = get_handshake(dev, HANDSHAKE_REQUEST); + if (handshake == HANDSHAKE_REQUEST) { + /* + * Get type associated with the request. + */ + request = get_request_type(dev); + switch (request) { + case REQUEST_FILE_CHECKSUM: + DEBUG(0, + "ft1000_dnld: REQUEST_FOR_CHECKSUM\n"); + put_request_value(dev, image_chksum); + break; + case REQUEST_RUN_ADDRESS: + if (bGoodVersion) { + put_request_value(dev, + run_address); + } else { + Status = FAILURE; + break; + } + break; + case REQUEST_CODE_LENGTH: + if (bGoodVersion) { + put_request_value(dev, + run_size); + } else { + Status = FAILURE; + break; + } + break; + case REQUEST_DONE_CL: + /* Reposition ptrs to beginning of provisioning section */ + switch (file_version) { + case 5: + case 6: + pUsFile = + (USHORT *) ((long)pFileStart + + + pFileHdr5-> + commands_offset); + pUcFile = + (UCHAR *) ((long)pFileStart + + + pFileHdr5-> + commands_offset); + break; + default: + Status = FAILURE; + break; + } + uiState = STATE_DONE_DWNLD; + break; + case REQUEST_CODE_SEGMENT: + if (!bGoodVersion) { + Status = FAILURE; + break; + } + word_length = get_request_value(dev); + if (word_length > MAX_LENGTH) { + Status = FAILURE; + break; + } + if ((word_length * 2 + (long)pUcFile) > + (long)pCodeEnd) { + /* + * Error, beyond boot code range. + */ + Status = FAILURE; + break; + } + if (file_version == 5) { + /* + * Position ASIC DPRAM auto-increment pointer. + */ + ft1000_write_reg(dev, + FT1000_REG_DPRAM_ADDR, + DWNLD_PS_HDR_LOC); + + for (; word_length > 0; word_length--) { /* In words */ + //temp = *pUsFile; + //temp = RtlUshortByteSwap(temp); + ft1000_write_reg(dev, + FT1000_REG_DPRAM_DATA, + *pUsFile); + pUsFile++; + pUcFile += 2; + DspWordCnt++; + } + } else { + /* + * Position ASIC DPRAM auto-increment pointer. + */ + outw(DWNLD_MAG_PS_HDR_LOC, + dev->base_addr + + FT1000_REG_DPRAM_ADDR); + if (word_length & 0x01) { + word_length++; + } + word_length = word_length / 2; + + for (; word_length > 0; word_length--) { /* In words */ + templong = *pUsFile++; + templong |= + (*pUsFile++ << 16); + pUcFile += 4; + outl(templong, + dev->base_addr + + FT1000_REG_MAG_DPDATAL); + } + } + break; + + case REQUEST_MAILBOX_DATA: + // Convert length from byte count to word count. Make sure we round up. + word_length = + (long)(info->DSPInfoBlklen + 1) / 2; + put_request_value(dev, word_length); + pMailBoxData = + (PDRVMSG) & info->DSPInfoBlk[0]; + pUsData = + (USHORT *) & pMailBoxData->data[0]; + // Provide mutual exclusive access while reading ASIC registers. + spin_lock_irqsave(&info->dpram_lock, + flags); + if (file_version == 5) { + /* + * Position ASIC DPRAM auto-increment pointer. + */ + ft1000_write_reg(dev, + FT1000_REG_DPRAM_ADDR, + DWNLD_PS_HDR_LOC); + + for (; word_length > 0; word_length--) { /* In words */ + temp = ntohs(*pUsData); + ft1000_write_reg(dev, + FT1000_REG_DPRAM_DATA, + temp); + pUsData++; + } + } else { + /* + * Position ASIC DPRAM auto-increment pointer. + */ + outw(DWNLD_MAG_PS_HDR_LOC, + dev->base_addr + + FT1000_REG_DPRAM_ADDR); + if (word_length & 0x01) { + word_length++; + } + word_length = word_length / 2; + + for (; word_length > 0; word_length--) { /* In words */ + templong = *pUsData++; + templong |= + (*pUsData++ << 16); + outl(templong, + dev->base_addr + + FT1000_REG_MAG_DPDATAL); + } + } + spin_unlock_irqrestore(&info-> + dpram_lock, + flags); + break; + + case REQUEST_VERSION_INFO: + word_length = + pFileHdr5->version_data_size; + put_request_value(dev, word_length); + pUsFile = + (USHORT *) ((long)pFileStart + + pFileHdr5-> + version_data_offset); + // Provide mutual exclusive access while reading ASIC registers. + spin_lock_irqsave(&info->dpram_lock, + flags); + if (file_version == 5) { + /* + * Position ASIC DPRAM auto-increment pointer. + */ + ft1000_write_reg(dev, + FT1000_REG_DPRAM_ADDR, + DWNLD_PS_HDR_LOC); + + for (; word_length > 0; word_length--) { /* In words */ + ft1000_write_reg(dev, + FT1000_REG_DPRAM_DATA, + *pUsFile + /*temp */ + ); + pUsFile++; + } + } else { + /* + * Position ASIC DPRAM auto-increment pointer. + */ + outw(DWNLD_MAG_PS_HDR_LOC, + dev->base_addr + + FT1000_REG_DPRAM_ADDR); + if (word_length & 0x01) { + word_length++; + } + word_length = word_length / 2; + + for (; word_length > 0; word_length--) { /* In words */ + templong = + ntohs(*pUsFile++); + temp = + ntohs(*pUsFile++); + templong |= + (temp << 16); + outl(templong, + dev->base_addr + + FT1000_REG_MAG_DPDATAL); + } + } + spin_unlock_irqrestore(&info-> + dpram_lock, + flags); + break; + + case REQUEST_CODE_BY_VERSION: + bGoodVersion = FALSE; + requested_version = + get_request_value(dev); + if (file_version == 5) { + pDspImageInfo = + (PDSP_IMAGE_INFO) ((long) + pFileStart + + + sizeof + (DSP_FILE_HDR_5)); + for (imageN = 0; + imageN < + pFileHdr5->nDspImages; + imageN++) { + if (pDspImageInfo-> + version == + requested_version) { + bGoodVersion = + TRUE; + pUsFile = + (USHORT + *) ((long) + pFileStart + + + pDspImageInfo-> + begin_offset); + pUcFile = + (UCHAR + *) ((long) + pFileStart + + + pDspImageInfo-> + begin_offset); + pCodeEnd = + (UCHAR + *) ((long) + pFileStart + + + pDspImageInfo-> + end_offset); + run_address = + pDspImageInfo-> + run_address; + run_size = + pDspImageInfo-> + image_size; + break; + } + pDspImageInfo++; + } + } else { + pDspImageInfoV6 = + (PDSP_IMAGE_INFO_V6) ((long) + pFileStart + + + sizeof + (DSP_FILE_HDR_5)); + for (imageN = 0; + imageN < + pFileHdr5->nDspImages; + imageN++) { + temp = (USHORT) + (pDspImageInfoV6-> + version); + templong = temp; + temp = (USHORT) + (pDspImageInfoV6-> + version >> 16); + templong |= + (temp << 16); + if (templong == + requested_version) { + bGoodVersion = + TRUE; + pUsFile = + (USHORT + *) ((long) + pFileStart + + + pDspImageInfoV6-> + begin_offset); + pUcFile = + (UCHAR + *) ((long) + pFileStart + + + pDspImageInfoV6-> + begin_offset); + pCodeEnd = + (UCHAR + *) ((long) + pFileStart + + + pDspImageInfoV6-> + end_offset); + run_address = + pDspImageInfoV6-> + run_address; + run_size = + pDspImageInfoV6-> + image_size; + image_chksum = + (ULONG) + pDspImageInfoV6-> + checksum; + DEBUG(0, + "ft1000_dnld: image_chksum = 0x%8x\n", + (unsigned + int) + image_chksum); + break; + } + pDspImageInfoV6++; + } + } + if (!bGoodVersion) { + /* + * Error, beyond boot code range. + */ + Status = FAILURE; + break; + } + break; + + default: + Status = FAILURE; + break; + } + put_handshake(dev, HANDSHAKE_RESPONSE); + } else { + Status = FAILURE; + } + + break; + + case STATE_DONE_DWNLD: + if (((UINT) (pUcFile) - (UINT) pFileStart) >= + (UINT) FileLength) { + uiState = STATE_DONE_FILE; + break; + } + + pHdr = (PPSEUDO_HDR) pUsFile; + + if (pHdr->portdest == 0x80 /* DspOAM */ + && (pHdr->portsrc == 0x00 /* Driver */ + || pHdr->portsrc == 0x10 /* FMM */ )) { + uiState = STATE_SECTION_PROV; + } else { + DEBUG(1, + "FT1000:download:Download error: Bad Port IDs in Pseudo Record\n"); + DEBUG(1, "\t Port Source = 0x%2.2x\n", + pHdr->portsrc); + DEBUG(1, "\t Port Destination = 0x%2.2x\n", + pHdr->portdest); + Status = FAILURE; + } + + break; + + case STATE_SECTION_PROV: + + pHdr = (PPSEUDO_HDR) pUcFile; + + if (pHdr->checksum == hdr_checksum(pHdr)) { + if (pHdr->portdest != 0x80 /* Dsp OAM */ ) { + uiState = STATE_DONE_PROV; + break; + } + usHdrLength = ntohs(pHdr->length); /* Byte length for PROV records */ + + // Get buffer for provisioning data + pbuffer = + kmalloc((usHdrLength + sizeof(PSEUDO_HDR)), + GFP_ATOMIC); + if (pbuffer) { + memcpy(pbuffer, (void *)pUcFile, + (UINT) (usHdrLength + + sizeof(PSEUDO_HDR))); + // link provisioning data + pprov_record = + kmalloc(sizeof(PROV_RECORD), + GFP_ATOMIC); + if (pprov_record) { + pprov_record->pprov_data = + pbuffer; + list_add_tail(&pprov_record-> + list, + &info->prov_list); + // Move to next entry if available + pUcFile = + (UCHAR *) ((UINT) pUcFile + + (UINT) ((usHdrLength + 1) & 0xFFFFFFFE) + sizeof(PSEUDO_HDR)); + if ((UINT) (pUcFile) - + (UINT) (pFileStart) >= + (UINT) FileLength) { + uiState = + STATE_DONE_FILE; + } + } else { + kfree(pbuffer); + Status = FAILURE; + } + } else { + Status = FAILURE; + } + } else { + /* Checksum did not compute */ + Status = FAILURE; + } + + break; + + case STATE_DONE_PROV: + uiState = STATE_DONE_FILE; + break; + + default: + Status = FAILURE; + break; + } /* End Switch */ + + } /* End while */ + + return Status; + +} diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c new file mode 100644 index 0000000..eed7e94 --- /dev/null +++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c @@ -0,0 +1,2294 @@ +/*--------------------------------------------------------------------------- + FT1000 driver for Flarion Flash OFDM NIC Device + + Copyright (C) 2002 Flarion Technologies, All rights reserved. + Copyright (C) 2006 Patrik Ostrihon, All rights reserved. + Copyright (C) 2006 ProWeb Consulting, a.s, All rights reserved. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) any + later version. This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License along with this program; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - + Suite 330, Boston, MA 02111-1307, USA. +-----------------------------------------------------------------------------*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/proc_fs.h> + +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/in.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/bitops.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/if_arp.h> +#include <linux/ioport.h> +#include <linux/wait.h> +#include <linux/vmalloc.h> + +#include <linux/firmware.h> +#include <linux/ethtool.h> + +#ifdef FT_DEBUG +#define DEBUG(n, args...) printk(KERN_DEBUG args); +#else +#define DEBUG(n, args...) +#endif + +#include <linux/delay.h> +#include "ft1000_dev.h" +#include "ft1000.h" + +int card_download(struct net_device *dev, void *pFileStart, UINT FileLength); + +void ft1000InitProc(struct net_device *dev); +void ft1000CleanupProc(struct net_device *dev); + +const struct firmware *fw_entry; + +static void ft1000_hbchk(u_long data); +static struct timer_list poll_timer = { + function:ft1000_hbchk +}; + +static u16 cmdbuffer[1024]; +static u8 tempbuffer[1600]; +static u8 ft1000_card_present = 0; +static u8 flarion_ft1000_cnt = 0; + +static irqreturn_t ft1000_interrupt(int irq, void *dev_id); +static void ft1000_enable_interrupts(struct net_device *dev); +static void ft1000_disable_interrupts(struct net_device *dev); + +/* new kernel */ +MODULE_AUTHOR(""); +MODULE_DESCRIPTION + ("Support for Flarion Flash OFDM NIC Device. Support for PCMCIA when used with ft1000_cs."); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("FT1000"); + +#define MAX_RCV_LOOP 100 + +//--------------------------------------------------------------------------- +// +// Function: ft1000_asic_read +// Descripton: This function will retrieve the value of a specific ASIC +// register. +// Input: +// dev - network device structure +// offset - ASIC register to read +// Output: +// value - value of ASIC register +// +//--------------------------------------------------------------------------- +inline u16 ft1000_asic_read(struct net_device *dev, u16 offset) +{ + return (ft1000_read_reg(dev, offset)); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_asic_write +// Descripton: This function will set the value of a specific ASIC +// register. +// Input: +// dev - network device structure +// value - value to set ASIC register +// Output: +// none +// +//--------------------------------------------------------------------------- +inline void ft1000_asic_write(struct net_device *dev, u16 offset, u16 value) +{ + ft1000_write_reg(dev, offset, value); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_read_fifo_len +// Descripton: This function will read the ASIC Uplink FIFO status register +// which will return the number of bytes remaining in the Uplink FIFO. +// Sixteen bytes are subtracted to make sure that the ASIC does not +// reach its threshold. +// Input: +// dev - network device structure +// Output: +// value - number of bytes available in the ASIC Uplink FIFO. +// +//--------------------------------------------------------------------------- +static inline u16 ft1000_read_fifo_len(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + + if (info->AsicID == ELECTRABUZZ_ID) { + return (ft1000_read_reg(dev, FT1000_REG_UFIFO_STAT) - 16); + } else { + return (ft1000_read_reg(dev, FT1000_REG_MAG_UFSR) - 16); + } +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_read_dpram +// Descripton: This function will read the specific area of dpram +// (Electrabuzz ASIC only) +// Input: +// dev - device structure +// offset - index of dpram +// Output: +// value - value of dpram +// +//--------------------------------------------------------------------------- +u16 ft1000_read_dpram(struct net_device * dev, int offset) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + unsigned long flags; + u16 data; + + // Provide mutual exclusive access while reading ASIC registers. + spin_lock_irqsave(&info->dpram_lock, flags); + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); + data = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); + spin_unlock_irqrestore(&info->dpram_lock, flags); + + return (data); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_write_dpram +// Descripton: This function will write to a specific area of dpram +// (Electrabuzz ASIC only) +// Input: +// dev - device structure +// offset - index of dpram +// value - value to write +// Output: +// none. +// +//--------------------------------------------------------------------------- +static inline void ft1000_write_dpram(struct net_device *dev, + int offset, u16 value) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + unsigned long flags; + + // Provide mutual exclusive access while reading ASIC registers. + spin_lock_irqsave(&info->dpram_lock, flags); + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, value); + spin_unlock_irqrestore(&info->dpram_lock, flags); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_read_dpram_mag_16 +// Descripton: This function will read the specific area of dpram +// (Magnemite ASIC only) +// Input: +// dev - device structure +// offset - index of dpram +// Output: +// value - value of dpram +// +//--------------------------------------------------------------------------- +u16 ft1000_read_dpram_mag_16(struct net_device *dev, int offset, int Index) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + unsigned long flags; + u16 data; + + // Provide mutual exclusive access while reading ASIC registers. + spin_lock_irqsave(&info->dpram_lock, flags); + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); + // check if we want to read upper or lower 32-bit word + if (Index) { + data = ft1000_read_reg(dev, FT1000_REG_MAG_DPDATAL); + } else { + data = ft1000_read_reg(dev, FT1000_REG_MAG_DPDATAH); + } + spin_unlock_irqrestore(&info->dpram_lock, flags); + + return (data); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_write_dpram_mag_16 +// Descripton: This function will write to a specific area of dpram +// (Magnemite ASIC only) +// Input: +// dev - device structure +// offset - index of dpram +// value - value to write +// Output: +// none. +// +//--------------------------------------------------------------------------- +static inline void ft1000_write_dpram_mag_16(struct net_device *dev, + int offset, u16 value, int Index) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + unsigned long flags; + + // Provide mutual exclusive access while reading ASIC registers. + spin_lock_irqsave(&info->dpram_lock, flags); + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); + if (Index) { + ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAL, value); + } else { + ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAH, value); + } + spin_unlock_irqrestore(&info->dpram_lock, flags); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_read_dpram_mag_32 +// Descripton: This function will read the specific area of dpram +// (Magnemite ASIC only) +// Input: +// dev - device structure +// offset - index of dpram +// Output: +// value - value of dpram +// +//--------------------------------------------------------------------------- +u32 ft1000_read_dpram_mag_32(struct net_device *dev, int offset) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + unsigned long flags; + u32 data; + + // Provide mutual exclusive access while reading ASIC registers. + spin_lock_irqsave(&info->dpram_lock, flags); + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); + data = inl(dev->base_addr + FT1000_REG_MAG_DPDATAL); + spin_unlock_irqrestore(&info->dpram_lock, flags); + + return (data); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_write_dpram_mag_32 +// Descripton: This function will write to a specific area of dpram +// (Magnemite ASIC only) +// Input: +// dev - device structure +// offset - index of dpram +// value - value to write +// Output: +// none. +// +//--------------------------------------------------------------------------- +void ft1000_write_dpram_mag_32(struct net_device *dev, int offset, u32 value) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + unsigned long flags; + + // Provide mutual exclusive access while reading ASIC registers. + spin_lock_irqsave(&info->dpram_lock, flags); + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); + outl(value, dev->base_addr + FT1000_REG_MAG_DPDATAL); + spin_unlock_irqrestore(&info->dpram_lock, flags); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_enable_interrupts +// Descripton: This function will enable interrupts base on the current interrupt mask. +// Input: +// dev - device structure +// Output: +// None. +// +//--------------------------------------------------------------------------- +static void ft1000_enable_interrupts(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 tempword; + + DEBUG(1, "ft1000_hw:ft1000_enable_interrupts()\n"); + ft1000_write_reg(dev, FT1000_REG_SUP_IMASK, + info->CurrentInterruptEnableMask); + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK); + DEBUG(1, + "ft1000_hw:ft1000_enable_interrupts:current interrupt enable mask = 0x%x\n", + tempword); + info->InterruptsEnabled = TRUE; +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_disable_interrupts +// Descripton: This function will disable all interrupts. +// Input: +// dev - device structure +// Output: +// None. +// +//--------------------------------------------------------------------------- +static void ft1000_disable_interrupts(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 tempword; + + DEBUG(1, "ft1000_hw: ft1000_disable_interrupts()\n"); + ft1000_write_reg(dev, FT1000_REG_SUP_IMASK, ISR_MASK_ALL); + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK); + DEBUG(1, + "ft1000_hw:ft1000_disable_interrupts:current interrupt enable mask = 0x%x\n", + tempword); + info->InterruptsEnabled = FALSE; +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_reset_asic +// Descripton: This function will call the Card Service function to reset the +// ASIC. +// Input: +// dev - device structure +// Output: +// none +// +//--------------------------------------------------------------------------- +static void ft1000_reset_asic(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 tempword; + + DEBUG(1, "ft1000_hw:ft1000_reset_asic called\n"); + + (*info->ft1000_reset) (info->link); + info->ASICResetNum++; + + // Let's use the register provided by the Magnemite ASIC to reset the + // ASIC and DSP. + if (info->AsicID == MAGNEMITE_ID) { + ft1000_write_reg(dev, FT1000_REG_RESET, + (DSP_RESET_BIT | ASIC_RESET_BIT)); + } + mdelay(1); + if (info->AsicID == ELECTRABUZZ_ID) { + // set watermark to -1 in order to not generate an interrrupt + ft1000_write_reg(dev, FT1000_REG_WATERMARK, 0xffff); + } else { + // set watermark to -1 in order to not generate an interrrupt + ft1000_write_reg(dev, FT1000_REG_MAG_WATERMARK, 0xffff); + } + // clear interrupts + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR); + DEBUG(1, "ft1000_hw: interrupt status register = 0x%x\n", tempword); + ft1000_write_reg(dev, FT1000_REG_SUP_ISR, tempword); + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR); + DEBUG(1, "ft1000_hw: interrupt status register = 0x%x\n", tempword); + +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_reset_card +// Descripton: This function will reset the card +// Input: +// dev - device structure +// Output: +// status - FALSE (card reset fail) +// TRUE (card reset successful) +// +//--------------------------------------------------------------------------- +static int ft1000_reset_card(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 tempword; + int i; + unsigned long flags; + PPROV_RECORD ptr; + + DEBUG(1, "ft1000_hw:ft1000_reset_card called.....\n"); + + info->CardReady = 0; + info->ProgConStat = 0; + info->squeseqnum = 0; + ft1000_disable_interrupts(dev); + +// del_timer(&poll_timer); + + // Make sure we free any memory reserve for provisioning + while (list_empty(&info->prov_list) == 0) { + DEBUG(0, + "ft1000_hw:ft1000_reset_card:deleting provisioning record\n"); + ptr = list_entry(info->prov_list.next, PROV_RECORD, list); + list_del(&ptr->list); + kfree(ptr->pprov_data); + kfree(ptr); + } + + if (info->AsicID == ELECTRABUZZ_ID) { + DEBUG(1, "ft1000_hw:ft1000_reset_card:resetting DSP\n"); + ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT); + } else { + DEBUG(1, + "ft1000_hw:ft1000_reset_card:resetting ASIC and DSP\n"); + ft1000_write_reg(dev, FT1000_REG_RESET, + (DSP_RESET_BIT | ASIC_RESET_BIT)); + } + + // Copy DSP session record into info block if this is not a coldstart + if (ft1000_card_present == 1) { + spin_lock_irqsave(&info->dpram_lock, flags); + if (info->AsicID == ELECTRABUZZ_ID) { + if (info->DspHibernateFlag == 0) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_RX_BASE); + for (i = 0; i < MAX_DSP_SESS_REC; i++) { + info->DSPSess.Rec[i] = + ft1000_read_reg(dev, + FT1000_REG_DPRAM_DATA); + } + } + } else { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_MAG_RX_BASE); + for (i = 0; i < MAX_DSP_SESS_REC / 2; i++) { + info->DSPSess.MagRec[i] = + inl(dev->base_addr + FT1000_REG_MAG_DPDATA); + } + } + spin_unlock_irqrestore(&info->dpram_lock, flags); + } + + DEBUG(1, "ft1000_hw:ft1000_reset_card:resetting ASIC\n"); + mdelay(10); + //reset ASIC + ft1000_reset_asic(dev); + + info->DSPResetNum++; + + DEBUG(1, "ft1000_hw:ft1000_reset_card:downloading dsp image\n"); + + if (info->AsicID == MAGNEMITE_ID) { + // Put dsp in reset and take ASIC out of reset + DEBUG(0, + "ft1000_hw:ft1000_reset_card:Put DSP in reset and take ASIC out of reset\n"); + ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT); + + // Setting MAGNEMITE ASIC to big endian mode + ft1000_write_reg(dev, FT1000_REG_SUP_CTRL, HOST_INTF_BE); + // Download bootloader + card_bootload(dev); + + // Take DSP out of reset + ft1000_write_reg(dev, FT1000_REG_RESET, 0); + // FLARION_DSP_ACTIVE; + mdelay(10); + DEBUG(0, "ft1000_hw:ft1000_reset_card:Take DSP out of reset\n"); + + // Wait for 0xfefe indicating dsp ready before starting download + for (i = 0; i < 50; i++) { + tempword = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DPRAM_FEFE, + FT1000_MAG_DPRAM_FEFE_INDX); + if (tempword == 0xfefe) { + break; + } + mdelay(20); + } + + if (i == 50) { + DEBUG(0, + "ft1000_hw:ft1000_reset_card:No FEFE detected from DSP\n"); + return FALSE; + } + + } else { + // Take DSP out of reset + ft1000_write_reg(dev, FT1000_REG_RESET, ~DSP_RESET_BIT); + mdelay(10); + } + + if (card_download(dev, fw_entry->data, fw_entry->size)) { + DEBUG(1, "card download unsuccessful\n"); + return FALSE; + } else { + DEBUG(1, "card download successful\n"); + } + + mdelay(10); + + if (info->AsicID == ELECTRABUZZ_ID) { + // Need to initialize the FIFO length counter to zero in order to sync up + // with the DSP + info->fifo_cnt = 0; + ft1000_write_dpram(dev, FT1000_FIFO_LEN, info->fifo_cnt); + // Initialize DSP heartbeat area to ho + ft1000_write_dpram(dev, FT1000_HI_HO, ho); + tempword = ft1000_read_dpram(dev, FT1000_HI_HO); + DEBUG(1, "ft1000_hw:ft1000_reset_asic:hi_ho value = 0x%x\n", + tempword); + } else { + // Initialize DSP heartbeat area to ho + ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, ho_mag, + FT1000_MAG_HI_HO_INDX); + tempword = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_HI_HO, + FT1000_MAG_HI_HO_INDX); + DEBUG(1, "ft1000_hw:ft1000_reset_card:hi_ho value = 0x%x\n", + tempword); + } + + info->CardReady = 1; + ft1000_enable_interrupts(dev); + + /* Schedule heartbeat process to run every 2 seconds */ +// poll_timer.expires = jiffies + (2*HZ); +// poll_timer.data = (u_long)dev; +// add_timer(&poll_timer); + + return TRUE; + +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_chkcard +// Descripton: This function will check if the device is presently available on +// the system. +// Input: +// dev - device structure +// Output: +// status - FALSE (device is not present) +// TRUE (device is present) +// +//--------------------------------------------------------------------------- +static int ft1000_chkcard(struct net_device *dev) +{ + u16 tempword; + + // Mask register is used to check for device presence since it is never + // set to zero. + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK); + if (tempword == 0) { + DEBUG(1, + "ft1000_hw:ft1000_chkcard: IMASK = 0 Card not detected\n"); + return FALSE; + } + // The system will return the value of 0xffff for the version register + // if the device is not present. + tempword = ft1000_read_reg(dev, FT1000_REG_ASIC_ID); + if (tempword == 0xffff) { + DEBUG(1, + "ft1000_hw:ft1000_chkcard: Version = 0xffff Card not detected\n"); + return FALSE; + } + return TRUE; +} + + +//--------------------------------------------------------------------------- +// +// Function: ft1000_hbchk +// Descripton: This function will perform the heart beat check of the DSP as +// well as the ASIC. +// Input: +// dev - device structure +// Output: +// none +// +//--------------------------------------------------------------------------- +static void ft1000_hbchk(u_long data) +{ + struct net_device *dev = (struct net_device *)data; + + FT1000_INFO *info; + USHORT tempword; + + info = (FT1000_INFO *) netdev_priv(dev); + + if (info->CardReady == 1) { + // Perform dsp heartbeat check + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = ft1000_read_dpram(dev, FT1000_HI_HO); + } else { + tempword = + ntohs(ft1000_read_dpram_mag_16 + (dev, FT1000_MAG_HI_HO, + FT1000_MAG_HI_HO_INDX)); + } + DEBUG(1, "ft1000_hw:ft1000_hbchk:hi_ho value = 0x%x\n", + tempword); + // Let's perform another check if ho is not detected + if (tempword != ho) { + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = ft1000_read_dpram(dev, FT1000_HI_HO); + } + else { + tempword = ntohs(ft1000_read_dpram_mag_16(dev, FT1000_MAG_HI_HO, FT1000_MAG_HI_HO_INDX)); + } + } + if (tempword != ho) { + printk(KERN_INFO + "ft1000: heartbeat failed - no ho detected\n"); + if (info->AsicID == ELECTRABUZZ_ID) { + info->DSP_TIME[0] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER0); + info->DSP_TIME[1] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER1); + info->DSP_TIME[2] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER2); + info->DSP_TIME[3] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER3); + } else { + info->DSP_TIME[0] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER0, + FT1000_MAG_DSP_TIMER0_INDX); + info->DSP_TIME[1] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER1, + FT1000_MAG_DSP_TIMER1_INDX); + info->DSP_TIME[2] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER2, + FT1000_MAG_DSP_TIMER2_INDX); + info->DSP_TIME[3] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER3, + FT1000_MAG_DSP_TIMER3_INDX); + } + info->DrvErrNum = DSP_HB_INFO; + if (ft1000_reset_card(dev) == 0) { + printk(KERN_INFO + "ft1000: Hardware Failure Detected - PC Card disabled\n"); + info->ProgConStat = 0xff; + return; + } + /* Schedule this module to run every 2 seconds */ + poll_timer.expires = jiffies + (2*HZ); + poll_timer.data = (u_long)dev; + add_timer(&poll_timer); + return; + } + + tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + // Let's check doorbell again if fail + if (tempword & FT1000_DB_HB) { + tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + } + if (tempword & FT1000_DB_HB) { + printk(KERN_INFO + "ft1000: heartbeat doorbell not clear by firmware\n"); + if (info->AsicID == ELECTRABUZZ_ID) { + info->DSP_TIME[0] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER0); + info->DSP_TIME[1] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER1); + info->DSP_TIME[2] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER2); + info->DSP_TIME[3] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER3); + } else { + info->DSP_TIME[0] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER0, + FT1000_MAG_DSP_TIMER0_INDX); + info->DSP_TIME[1] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER1, + FT1000_MAG_DSP_TIMER1_INDX); + info->DSP_TIME[2] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER2, + FT1000_MAG_DSP_TIMER2_INDX); + info->DSP_TIME[3] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER3, + FT1000_MAG_DSP_TIMER3_INDX); + } + info->DrvErrNum = DSP_HB_INFO; + if (ft1000_reset_card(dev) == 0) { + printk(KERN_INFO + "ft1000: Hardware Failure Detected - PC Card disabled\n"); + info->ProgConStat = 0xff; + return; + } + /* Schedule this module to run every 2 seconds */ + poll_timer.expires = jiffies + (2*HZ); + poll_timer.data = (u_long)dev; + add_timer(&poll_timer); + return; + } + // Set dedicated area to hi and ring appropriate doorbell according + // to hi/ho heartbeat protocol + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_dpram(dev, FT1000_HI_HO, hi); + } else { + ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, hi_mag, + FT1000_MAG_HI_HO_INDX); + } + + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = ft1000_read_dpram(dev, FT1000_HI_HO); + } else { + tempword = + ntohs(ft1000_read_dpram_mag_16 + (dev, FT1000_MAG_HI_HO, + FT1000_MAG_HI_HO_INDX)); + } + // Let's write hi again if fail + if (tempword != hi) { + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_dpram(dev, FT1000_HI_HO, hi); + } + else { + ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, hi_mag, FT1000_MAG_HI_HO_INDX); + } + + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = ft1000_read_dpram(dev, FT1000_HI_HO); + } + else { + tempword = ntohs(ft1000_read_dpram_mag_16(dev, FT1000_MAG_HI_HO, FT1000_MAG_HI_HO_INDX)); + } + + } + + if (tempword != hi) { + printk(KERN_INFO + "ft1000: heartbeat failed - cannot write hi into DPRAM\n"); + if (info->AsicID == ELECTRABUZZ_ID) { + info->DSP_TIME[0] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER0); + info->DSP_TIME[1] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER1); + info->DSP_TIME[2] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER2); + info->DSP_TIME[3] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER3); + } else { + info->DSP_TIME[0] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER0, + FT1000_MAG_DSP_TIMER0_INDX); + info->DSP_TIME[1] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER1, + FT1000_MAG_DSP_TIMER1_INDX); + info->DSP_TIME[2] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER2, + FT1000_MAG_DSP_TIMER2_INDX); + info->DSP_TIME[3] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER3, + FT1000_MAG_DSP_TIMER3_INDX); + } + info->DrvErrNum = DSP_HB_INFO; + if (ft1000_reset_card(dev) == 0) { + printk(KERN_INFO + "ft1000: Hardware Failure Detected - PC Card disabled\n"); + info->ProgConStat = 0xff; + return; + } + /* Schedule this module to run every 2 seconds */ + poll_timer.expires = jiffies + (2*HZ); + poll_timer.data = (u_long)dev; + add_timer(&poll_timer); + return; + } + ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_HB); + + } + + /* Schedule this module to run every 2 seconds */ + poll_timer.expires = jiffies + (2 * HZ); + poll_timer.data = (u_long) dev; + add_timer(&poll_timer); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_send_cmd +// Descripton: +// Input: +// Output: +// +//--------------------------------------------------------------------------- +void ft1000_send_cmd (struct net_device *dev, u16 *ptempbuffer, int size, u16 qtype) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + int i; + u16 tempword; + unsigned long flags; + + size += PSEUDOSZ; + // check for odd byte and increment to 16-bit word align value + if ((size & 0x0001)) { + size++; + } + DEBUG(1, "FT1000:ft1000_send_cmd:total length = %d\n", size); + DEBUG(1, "FT1000:ft1000_send_cmd:length = %d\n", ntohs(*ptempbuffer)); + // put message into slow queue area + // All messages are in the form total_len + pseudo header + message body + spin_lock_irqsave(&info->dpram_lock, flags); + + // Make sure SLOWQ doorbell is clear + tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + i=0; + while (tempword & FT1000_DB_DPRAM_TX) { + mdelay(10); + i++; + if (i==10) { + spin_unlock_irqrestore(&info->dpram_lock, flags); + return; + } + tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + } + + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_TX_BASE); + // Write total length to dpram + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size); + // Write pseudo header and messgae body + for (i = 0; i < (size >> 1); i++) { + DEBUG(1, "FT1000:ft1000_send_cmd:data %d = 0x%x\n", i, + *ptempbuffer); + tempword = htons(*ptempbuffer++); + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, tempword); + } + } else { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_MAG_TX_BASE); + // Write total length to dpram + ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAH, htons(size)); + // Write pseudo header and messgae body + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_MAG_TX_BASE + 1); + for (i = 0; i < (size >> 2); i++) { + DEBUG(1, "FT1000:ft1000_send_cmd:data = 0x%x\n", + *ptempbuffer); + outw(*ptempbuffer++, + dev->base_addr + FT1000_REG_MAG_DPDATAL); + DEBUG(1, "FT1000:ft1000_send_cmd:data = 0x%x\n", + *ptempbuffer); + outw(*ptempbuffer++, + dev->base_addr + FT1000_REG_MAG_DPDATAH); + } + DEBUG(1, "FT1000:ft1000_send_cmd:data = 0x%x\n", *ptempbuffer); + outw(*ptempbuffer++, dev->base_addr + FT1000_REG_MAG_DPDATAL); + DEBUG(1, "FT1000:ft1000_send_cmd:data = 0x%x\n", *ptempbuffer); + outw(*ptempbuffer++, dev->base_addr + FT1000_REG_MAG_DPDATAH); + } + spin_unlock_irqrestore(&info->dpram_lock, flags); + + // ring doorbell to notify DSP that we have a message ready + ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_DPRAM_TX); +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_receive_cmd +// Descripton: This function will read a message from the dpram area. +// Input: +// dev - network device structure +// pbuffer - caller supply address to buffer +// pnxtph - pointer to next pseudo header +// Output: +// Status = 0 (unsuccessful) +// = 1 (successful) +// +//--------------------------------------------------------------------------- +BOOLEAN ft1000_receive_cmd(struct net_device *dev, u16 * pbuffer, int maxsz, u16 *pnxtph) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 size; + u16 *ppseudohdr; + int i; + u16 tempword; + unsigned long flags; + + if (info->AsicID == ELECTRABUZZ_ID) { + size = ( ft1000_read_dpram(dev, *pnxtph) ) + PSEUDOSZ; + } else { + size = + ntohs(ft1000_read_dpram_mag_16 + (dev, FT1000_MAG_PH_LEN, + FT1000_MAG_PH_LEN_INDX)) + PSEUDOSZ; + } + if (size > maxsz) { + DEBUG(1, + "FT1000:ft1000_receive_cmd:Invalid command length = %d\n", + size); + return FALSE; + } else { + ppseudohdr = (u16 *) pbuffer; + spin_lock_irqsave(&info->dpram_lock, flags); + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_RX_BASE + 2); + for (i = 0; i <= (size >> 1); i++) { + tempword = + ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); + *pbuffer++ = ntohs(tempword); + } + } else { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_MAG_RX_BASE); + *pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAH); + DEBUG(1, "ft1000_hw:received data = 0x%x\n", *pbuffer); + pbuffer++; + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_MAG_RX_BASE + 1); + for (i = 0; i <= (size >> 2); i++) { + *pbuffer = + inw(dev->base_addr + + FT1000_REG_MAG_DPDATAL); + pbuffer++; + *pbuffer = + inw(dev->base_addr + + FT1000_REG_MAG_DPDATAH); + pbuffer++; + } + //copy odd aligned word + *pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAL); + DEBUG(1, "ft1000_hw:received data = 0x%x\n", *pbuffer); + pbuffer++; + *pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAH); + DEBUG(1, "ft1000_hw:received data = 0x%x\n", *pbuffer); + pbuffer++; + } + if (size & 0x0001) { + //copy odd byte from fifo + tempword = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); + *pbuffer = ntohs(tempword); + } + spin_unlock_irqrestore(&info->dpram_lock, flags); + + // Check if pseudo header checksum is good + // Calculate pseudo header checksum + tempword = *ppseudohdr++; + for (i = 1; i < 7; i++) { + tempword ^= *ppseudohdr++; + } + if ((tempword != *ppseudohdr)) { + DEBUG(1, + "FT1000:ft1000_receive_cmd:Pseudo header checksum mismatch\n"); + // Drop this message + return FALSE; + } + return TRUE; + } +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_proc_drvmsg +// Descripton: This function will process the various driver messages. +// Input: +// dev - device structure +// pnxtph - pointer to next pseudo header +// Output: +// none +// +//--------------------------------------------------------------------------- +void ft1000_proc_drvmsg(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 msgtype; + u16 tempword; + PMEDIAMSG pmediamsg; + PDSPINITMSG pdspinitmsg; + PDRVMSG pdrvmsg; + u16 len; + u16 i; + PPROV_RECORD ptr; + PPSEUDO_HDR ppseudo_hdr; + PUSHORT pmsg; + struct timeval tv; + union { + u8 byte[2]; + u16 wrd; + } convert; + + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = FT1000_DPRAM_RX_BASE+2; + } + else { + tempword = FT1000_DPRAM_MAG_RX_BASE; + } + if ( ft1000_receive_cmd(dev, &cmdbuffer[0], MAX_CMD_SQSIZE, &tempword) ) { + + // Get the message type which is total_len + PSEUDO header + msgtype + message body + pdrvmsg = (PDRVMSG) & cmdbuffer[0]; + msgtype = ntohs(pdrvmsg->type); + DEBUG(1, "Command message type = 0x%x\n", msgtype); + switch (msgtype) { + case DSP_PROVISION: + DEBUG(0, + "Got a provisioning request message from DSP\n"); + mdelay(25); + while (list_empty(&info->prov_list) == 0) { + DEBUG(0, "Sending a provisioning message\n"); + // Make sure SLOWQ doorbell is clear + tempword = + ft1000_read_reg(dev, FT1000_REG_DOORBELL); + i = 0; + while (tempword & FT1000_DB_DPRAM_TX) { + mdelay(5); + i++; + if (i == 10) { + break; + } + } + ptr = + list_entry(info->prov_list.next, + PROV_RECORD, list); + len = *(u16 *) ptr->pprov_data; + len = htons(len); + + pmsg = (PUSHORT) ptr->pprov_data; + ppseudo_hdr = (PPSEUDO_HDR) pmsg; + // Insert slow queue sequence number + ppseudo_hdr->seq_num = info->squeseqnum++; + ppseudo_hdr->portsrc = 0; + // Calculate new checksum + ppseudo_hdr->checksum = *pmsg++; + DEBUG(1, "checksum = 0x%x\n", + ppseudo_hdr->checksum); + for (i = 1; i < 7; i++) { + ppseudo_hdr->checksum ^= *pmsg++; + DEBUG(1, "checksum = 0x%x\n", + ppseudo_hdr->checksum); + } + + ft1000_send_cmd (dev, (u16 *)ptr->pprov_data, len, SLOWQ_TYPE); + list_del(&ptr->list); + kfree(ptr->pprov_data); + kfree(ptr); + } + // Indicate adapter is ready to take application messages after all + // provisioning messages are sent + info->CardReady = 1; + break; + case MEDIA_STATE: + pmediamsg = (PMEDIAMSG) & cmdbuffer[0]; + if (info->ProgConStat != 0xFF) { + if (pmediamsg->state) { + DEBUG(1, "Media is up\n"); + if (info->mediastate == 0) { + netif_carrier_on(dev); + netif_wake_queue(dev); + info->mediastate = 1; + do_gettimeofday(&tv); + info->ConTm = tv.tv_sec; + } + } else { + DEBUG(1, "Media is down\n"); + if (info->mediastate == 1) { + info->mediastate = 0; + netif_carrier_off(dev); + netif_stop_queue(dev); + info->ConTm = 0; + } + } + } + else { + DEBUG(1,"Media is down\n"); + if (info->mediastate == 1) { + info->mediastate = 0; + netif_carrier_off(dev); + netif_stop_queue(dev); + info->ConTm = 0; + } + } + break; + case DSP_INIT_MSG: + pdspinitmsg = (PDSPINITMSG) & cmdbuffer[0]; + memcpy(info->DspVer, pdspinitmsg->DspVer, DSPVERSZ); + DEBUG(1, "DSPVER = 0x%2x 0x%2x 0x%2x 0x%2x\n", + info->DspVer[0], info->DspVer[1], info->DspVer[2], + info->DspVer[3]); + memcpy(info->HwSerNum, pdspinitmsg->HwSerNum, + HWSERNUMSZ); + memcpy(info->Sku, pdspinitmsg->Sku, SKUSZ); + memcpy(info->eui64, pdspinitmsg->eui64, EUISZ); + dev->dev_addr[0] = info->eui64[0]; + dev->dev_addr[1] = info->eui64[1]; + dev->dev_addr[2] = info->eui64[2]; + dev->dev_addr[3] = info->eui64[5]; + dev->dev_addr[4] = info->eui64[6]; + dev->dev_addr[5] = info->eui64[7]; + + if (ntohs(pdspinitmsg->length) == + (sizeof(DSPINITMSG) - 20)) { + memcpy(info->ProductMode, + pdspinitmsg->ProductMode, MODESZ); + memcpy(info->RfCalVer, pdspinitmsg->RfCalVer, + CALVERSZ); + memcpy(info->RfCalDate, pdspinitmsg->RfCalDate, + CALDATESZ); + DEBUG(1, "RFCalVer = 0x%2x 0x%2x\n", + info->RfCalVer[0], info->RfCalVer[1]); + } + + break ; + case DSP_STORE_INFO: + DEBUG(1, "FT1000:drivermsg:Got DSP_STORE_INFO\n"); + tempword = ntohs(pdrvmsg->length); + info->DSPInfoBlklen = tempword; + if (tempword < (MAX_DSP_SESS_REC - 4)) { + pmsg = (PUSHORT) & pdrvmsg->data[0]; + for (i = 0; i < ((tempword + 1) / 2); i++) { + DEBUG(1, + "FT1000:drivermsg:dsp info data = 0x%x\n", + *pmsg); + info->DSPInfoBlk[i + 10] = *pmsg++; + } + } + break; + case DSP_GET_INFO: + DEBUG(1, "FT1000:drivermsg:Got DSP_GET_INFO\n"); + // copy dsp info block to dsp + info->DrvMsgPend = 1; + // allow any outstanding ioctl to finish + mdelay(10); + tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + if (tempword & FT1000_DB_DPRAM_TX) { + mdelay(10); + tempword = + ft1000_read_reg(dev, FT1000_REG_DOORBELL); + if (tempword & FT1000_DB_DPRAM_TX) { + mdelay(10); + } + } + + if ((tempword & FT1000_DB_DPRAM_TX) == 0) { + // Put message into Slow Queue + // Form Pseudo header + pmsg = (PUSHORT) info->DSPInfoBlk; + ppseudo_hdr = (PPSEUDO_HDR) pmsg; + ppseudo_hdr->length = + htons(info->DSPInfoBlklen + 4); + ppseudo_hdr->source = 0x10; + ppseudo_hdr->destination = 0x20; + ppseudo_hdr->portdest = 0; + ppseudo_hdr->portsrc = 0; + ppseudo_hdr->sh_str_id = 0; + ppseudo_hdr->control = 0; + ppseudo_hdr->rsvd1 = 0; + ppseudo_hdr->rsvd2 = 0; + ppseudo_hdr->qos_class = 0; + // Insert slow queue sequence number + ppseudo_hdr->seq_num = info->squeseqnum++; + // Insert application id + ppseudo_hdr->portsrc = 0; + // Calculate new checksum + ppseudo_hdr->checksum = *pmsg++; + for (i = 1; i < 7; i++) { + ppseudo_hdr->checksum ^= *pmsg++; + } + info->DSPInfoBlk[8] = 0x7200; + info->DSPInfoBlk[9] = + htons(info->DSPInfoBlklen); + ft1000_send_cmd (dev, (PUSHORT)info->DSPInfoBlk, (USHORT)(info->DSPInfoBlklen+4), 0); + } + info->DrvMsgPend = 0; + + break; + case GET_DRV_ERR_RPT_MSG: + DEBUG(1, "FT1000:drivermsg:Got GET_DRV_ERR_RPT_MSG\n"); + // copy driver error message to dsp + info->DrvMsgPend = 1; + // allow any outstanding ioctl to finish + mdelay(10); + tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + if (tempword & FT1000_DB_DPRAM_TX) { + mdelay(10); + tempword = + ft1000_read_reg(dev, FT1000_REG_DOORBELL); + if (tempword & FT1000_DB_DPRAM_TX) { + mdelay(10); + } + } + + if ((tempword & FT1000_DB_DPRAM_TX) == 0) { + // Put message into Slow Queue + // Form Pseudo header + pmsg = (PUSHORT) & tempbuffer[0]; + ppseudo_hdr = (PPSEUDO_HDR) pmsg; + ppseudo_hdr->length = htons(0x0012); + ppseudo_hdr->source = 0x10; + ppseudo_hdr->destination = 0x20; + ppseudo_hdr->portdest = 0; + ppseudo_hdr->portsrc = 0; + ppseudo_hdr->sh_str_id = 0; + ppseudo_hdr->control = 0; + ppseudo_hdr->rsvd1 = 0; + ppseudo_hdr->rsvd2 = 0; + ppseudo_hdr->qos_class = 0; + // Insert slow queue sequence number + ppseudo_hdr->seq_num = info->squeseqnum++; + // Insert application id + ppseudo_hdr->portsrc = 0; + // Calculate new checksum + ppseudo_hdr->checksum = *pmsg++; + for (i=1; i<7; i++) { + ppseudo_hdr->checksum ^= *pmsg++; + } + pmsg = (PUSHORT) & tempbuffer[16]; + *pmsg++ = htons(RSP_DRV_ERR_RPT_MSG); + *pmsg++ = htons(0x000e); + *pmsg++ = htons(info->DSP_TIME[0]); + *pmsg++ = htons(info->DSP_TIME[1]); + *pmsg++ = htons(info->DSP_TIME[2]); + *pmsg++ = htons(info->DSP_TIME[3]); + convert.byte[0] = info->DspVer[0]; + convert.byte[1] = info->DspVer[1]; + *pmsg++ = convert.wrd; + convert.byte[0] = info->DspVer[2]; + convert.byte[1] = info->DspVer[3]; + *pmsg++ = convert.wrd; + *pmsg++ = htons(info->DrvErrNum); + + ft1000_send_cmd (dev, (PUSHORT)&tempbuffer[0], (USHORT)(0x0012), 0); + info->DrvErrNum = 0; + } + info->DrvMsgPend = 0; + + break; + default: + break; + } + } +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_parse_dpram_msg +// Descripton: This function will parse the message received from the DSP +// via the DPRAM interface. +// Input: +// dev - device structure +// Output: +// status - FAILURE +// SUCCESS +// +//--------------------------------------------------------------------------- +int ft1000_parse_dpram_msg(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 doorbell; + u16 portid; + u16 nxtph; + u16 total_len; + int i = 0; + int cnt; + unsigned long flags; + + doorbell = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + DEBUG(1, "Doorbell = 0x%x\n", doorbell); + + if (doorbell & FT1000_ASIC_RESET_REQ) { + // Copy DSP session record from info block + spin_lock_irqsave(&info->dpram_lock, flags); + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_RX_BASE); + for (i = 0; i < MAX_DSP_SESS_REC; i++) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, + info->DSPSess.Rec[i]); + } + } else { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_MAG_RX_BASE); + for (i = 0; i < MAX_DSP_SESS_REC / 2; i++) { + outl(info->DSPSess.MagRec[i], + dev->base_addr + FT1000_REG_MAG_DPDATA); + } + } + spin_unlock_irqrestore(&info->dpram_lock, flags); + + // clear ASIC RESET request + ft1000_write_reg(dev, FT1000_REG_DOORBELL, + FT1000_ASIC_RESET_REQ); + DEBUG(1, "Got an ASIC RESET Request\n"); + ft1000_write_reg(dev, FT1000_REG_DOORBELL, + FT1000_ASIC_RESET_DSP); + + if (info->AsicID == MAGNEMITE_ID) { + // Setting MAGNEMITE ASIC to big endian mode + ft1000_write_reg(dev, FT1000_REG_SUP_CTRL, + HOST_INTF_BE); + } + info->DspAsicReset = 0; + } + + if (doorbell & FT1000_DSP_ASIC_RESET) { + DEBUG(0, + "FT1000:ft1000_parse_dpram_msg: Got a dsp ASIC reset message\n"); + info->DspAsicReset = 1; + ft1000_write_reg(dev, FT1000_REG_DOORBELL, + FT1000_DSP_ASIC_RESET); + udelay(200); + return SUCCESS; + } + + if (doorbell & FT1000_DB_DPRAM_RX) { + DEBUG(1, + "FT1000:ft1000_parse_dpram_msg: Got a slow queue message\n"); + nxtph = FT1000_DPRAM_RX_BASE + 2; + if (info->AsicID == ELECTRABUZZ_ID) { + total_len = + ft1000_read_dpram(dev, FT1000_DPRAM_RX_BASE); + } else { + total_len = + ntohs(ft1000_read_dpram_mag_16 + (dev, FT1000_MAG_TOTAL_LEN, + FT1000_MAG_TOTAL_LEN_INDX)); + } + DEBUG(1, "FT1000:ft1000_parse_dpram_msg:total length = %d\n", + total_len); + if ((total_len < MAX_CMD_SQSIZE) && (total_len > PSEUDOSZ)) { + total_len += nxtph; + cnt = 0; + // ft1000_read_reg will return a value that needs to be byteswap + // in order to get DSP_QID_OFFSET. + if (info->AsicID == ELECTRABUZZ_ID) { + portid = + (ft1000_read_dpram + (dev, + DSP_QID_OFFSET + FT1000_DPRAM_RX_BASE + + 2) >> 8) & 0xff; + } else { + portid = + (ft1000_read_dpram_mag_16 + (dev, FT1000_MAG_PORT_ID, + FT1000_MAG_PORT_ID_INDX) & 0xff); + } + DEBUG(1, "DSP_QID = 0x%x\n", portid); + + if (portid == DRIVERID) { + // We are assumming one driver message from the DSP at a time. + ft1000_proc_drvmsg(dev); + } + } + ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_DPRAM_RX); + } + + if (doorbell & FT1000_DB_COND_RESET) { + // Reset ASIC and DSP + if (info->AsicID == ELECTRABUZZ_ID) { + info->DSP_TIME[0] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER0); + info->DSP_TIME[1] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER1); + info->DSP_TIME[2] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER2); + info->DSP_TIME[3] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER3); + } else { + info->DSP_TIME[0] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER0, + FT1000_MAG_DSP_TIMER0_INDX); + info->DSP_TIME[1] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER1, + FT1000_MAG_DSP_TIMER1_INDX); + info->DSP_TIME[2] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER2, + FT1000_MAG_DSP_TIMER2_INDX); + info->DSP_TIME[3] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER3, + FT1000_MAG_DSP_TIMER3_INDX); + } + info->DrvErrNum = DSP_CONDRESET_INFO; + DEBUG(1, "ft1000_hw:DSP conditional reset requested\n"); + ft1000_reset_card(dev); + ft1000_write_reg(dev, FT1000_REG_DOORBELL, + FT1000_DB_COND_RESET); + } + // let's clear any unexpected doorbells from DSP + doorbell = + doorbell & ~(FT1000_DB_DPRAM_RX | FT1000_ASIC_RESET_REQ | + FT1000_DB_COND_RESET | 0xff00); + if (doorbell) { + DEBUG(1, "Clearing unexpected doorbell = 0x%x\n", doorbell); + ft1000_write_reg(dev, FT1000_REG_DOORBELL, doorbell); + } + + return SUCCESS; + +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_flush_fifo +// Descripton: This function will flush one packet from the downlink +// FIFO. +// Input: +// dev - device structure +// drv_err - driver error causing the flush fifo +// Output: +// None. +// +//--------------------------------------------------------------------------- +static void ft1000_flush_fifo(struct net_device *dev, u16 DrvErrNum) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 i; + u32 templong; + u16 tempword; + + DEBUG(1, "ft1000:ft1000_hw:ft1000_flush_fifo called\n"); + if (info->PktIntfErr > MAX_PH_ERR) { + if (info->AsicID == ELECTRABUZZ_ID) { + info->DSP_TIME[0] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER0); + info->DSP_TIME[1] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER1); + info->DSP_TIME[2] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER2); + info->DSP_TIME[3] = + ft1000_read_dpram(dev, FT1000_DSP_TIMER3); + } else { + info->DSP_TIME[0] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER0, + FT1000_MAG_DSP_TIMER0_INDX); + info->DSP_TIME[1] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER1, + FT1000_MAG_DSP_TIMER1_INDX); + info->DSP_TIME[2] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER2, + FT1000_MAG_DSP_TIMER2_INDX); + info->DSP_TIME[3] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER3, + FT1000_MAG_DSP_TIMER3_INDX); + } + info->DrvErrNum = DrvErrNum; + ft1000_reset_card(dev); + return; + } else { + // Flush corrupted pkt from FIFO + i = 0; + do { + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = + ft1000_read_reg(dev, FT1000_REG_DFIFO); + tempword = + ft1000_read_reg(dev, FT1000_REG_DFIFO_STAT); + } else { + templong = + inl(dev->base_addr + FT1000_REG_MAG_DFR); + tempword = + inw(dev->base_addr + FT1000_REG_MAG_DFSR); + } + i++; + // This should never happen unless the ASIC is broken. + // We must reset to recover. + if ((i > 2048) || (tempword == 0)) { + if (info->AsicID == ELECTRABUZZ_ID) { + info->DSP_TIME[0] = + ft1000_read_dpram(dev, + FT1000_DSP_TIMER0); + info->DSP_TIME[1] = + ft1000_read_dpram(dev, + FT1000_DSP_TIMER1); + info->DSP_TIME[2] = + ft1000_read_dpram(dev, + FT1000_DSP_TIMER2); + info->DSP_TIME[3] = + ft1000_read_dpram(dev, + FT1000_DSP_TIMER3); + } else { + info->DSP_TIME[0] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER0, + FT1000_MAG_DSP_TIMER0_INDX); + info->DSP_TIME[1] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER1, + FT1000_MAG_DSP_TIMER1_INDX); + info->DSP_TIME[2] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER2, + FT1000_MAG_DSP_TIMER2_INDX); + info->DSP_TIME[3] = + ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DSP_TIMER3, + FT1000_MAG_DSP_TIMER3_INDX); + } + if (tempword == 0) { + // Let's check if ASIC reads are still ok by reading the Mask register + // which is never zero at this point of the code. + tempword = + inw(dev->base_addr + + FT1000_REG_SUP_IMASK); + if (tempword == 0) { + // This indicates that we can not communicate with the ASIC + info->DrvErrNum = + FIFO_FLUSH_BADCNT; + } else { + // Let's assume that we really flush the FIFO + info->PktIntfErr++; + return; + } + } else { + info->DrvErrNum = FIFO_FLUSH_MAXLIMIT; + } + return; + } + tempword = inw(dev->base_addr + FT1000_REG_SUP_STAT); + } while ((tempword & 0x03) != 0x03); + if (info->AsicID == ELECTRABUZZ_ID) { + i++; + DEBUG(0, "Flushing FIFO complete = %x\n", tempword); + // Flush last word in FIFO. + tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); + // Update FIFO counter for DSP + i = i * 2; + DEBUG(0, "Flush Data byte count to dsp = %d\n", i); + info->fifo_cnt += i; + ft1000_write_dpram(dev, FT1000_FIFO_LEN, + info->fifo_cnt); + } else { + DEBUG(0, "Flushing FIFO complete = %x\n", tempword); + // Flush last word in FIFO + templong = inl(dev->base_addr + FT1000_REG_MAG_DFR); + tempword = inw(dev->base_addr + FT1000_REG_SUP_STAT); + DEBUG(0, "FT1000_REG_SUP_STAT = 0x%x\n", tempword); + tempword = inw(dev->base_addr + FT1000_REG_MAG_DFSR); + DEBUG(0, "FT1000_REG_MAG_DFSR = 0x%x\n", tempword); + } + if (DrvErrNum) { + info->PktIntfErr++; + } + } +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_copy_up_pkt +// Descripton: This function will pull Flarion packets out of the Downlink +// FIFO and convert it to an ethernet packet. The ethernet packet will +// then be deliver to the TCP/IP stack. +// Input: +// dev - device structure +// Output: +// status - FAILURE +// SUCCESS +// +//--------------------------------------------------------------------------- +int ft1000_copy_up_pkt(struct net_device *dev) +{ + u16 tempword; + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 len; + struct sk_buff *skb; + u16 i; + u8 *pbuffer = NULL; + u8 *ptemp = NULL; + u16 chksum; + u32 *ptemplong; + u32 templong; + + DEBUG(1, "ft1000_copy_up_pkt\n"); + // Read length + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); + len = tempword; + } else { + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); + len = ntohs(tempword); + } + chksum = tempword; + DEBUG(1, "Number of Bytes in FIFO = %d\n", len); + + if (len > ENET_MAX_SIZE) { + DEBUG(0, "size of ethernet packet invalid\n"); + if (info->AsicID == MAGNEMITE_ID) { + // Read High word to complete 32 bit access + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + } + ft1000_flush_fifo(dev, DSP_PKTLEN_INFO); + info->stats.rx_errors++; + return FAILURE; + } + + skb = dev_alloc_skb(len + 12 + 2); + + if (skb == NULL) { + DEBUG(0, "No Network buffers available\n"); + // Read High word to complete 32 bit access + if (info->AsicID == MAGNEMITE_ID) { + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + } + ft1000_flush_fifo(dev, 0); + info->stats.rx_errors++; + return FAILURE; + } + pbuffer = (u8 *) skb_put(skb, len + 12); + + // Pseudo header + if (info->AsicID == ELECTRABUZZ_ID) { + for (i = 1; i < 7; i++) { + tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); + chksum ^= tempword; + } + // read checksum value + tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); + } else { + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + DEBUG(1, "Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); + DEBUG(1, "Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + DEBUG(1, "Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); + DEBUG(1, "Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + DEBUG(1, "Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); + DEBUG(1, "Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + // read checksum value + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + DEBUG(1, "Pseudo = 0x%x\n", tempword); + } + + if (chksum != tempword) { + DEBUG(0, "Packet checksum mismatch 0x%x 0x%x\n", chksum, + tempword); + ft1000_flush_fifo(dev, DSP_PKTPHCKSUM_INFO); + info->stats.rx_errors++; + kfree_skb(skb); + return FAILURE; + } + //subtract the number of bytes read already + ptemp = pbuffer; + + // fake MAC address + *pbuffer++ = dev->dev_addr[0]; + *pbuffer++ = dev->dev_addr[1]; + *pbuffer++ = dev->dev_addr[2]; + *pbuffer++ = dev->dev_addr[3]; + *pbuffer++ = dev->dev_addr[4]; + *pbuffer++ = dev->dev_addr[5]; + *pbuffer++ = 0x00; + *pbuffer++ = 0x07; + *pbuffer++ = 0x35; + *pbuffer++ = 0xff; + *pbuffer++ = 0xff; + *pbuffer++ = 0xfe; + + if (info->AsicID == ELECTRABUZZ_ID) { + for (i = 0; i < len / 2; i++) { + tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); + *pbuffer++ = (u8) (tempword >> 8); + *pbuffer++ = (u8) tempword; + if (ft1000_chkcard(dev) == FALSE) { + kfree_skb(skb); + return FAILURE; + } + } + + // Need to read one more word if odd byte + if (len & 0x0001) { + tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); + *pbuffer++ = (u8) (tempword >> 8); + } + } else { + ptemplong = (u32 *) pbuffer; + for (i = 0; i < len / 4; i++) { + templong = inl(dev->base_addr + FT1000_REG_MAG_DFR); + DEBUG(1, "Data = 0x%8x\n", templong); + *ptemplong++ = templong; + } + + // Need to read one more word if odd align. + if (len & 0x0003) { + templong = inl(dev->base_addr + FT1000_REG_MAG_DFR); + DEBUG(1, "Data = 0x%8x\n", templong); + *ptemplong++ = templong; + } + + } + + DEBUG(1, "Data passed to Protocol layer:\n"); + for (i = 0; i < len + 12; i++) { + DEBUG(1, "Protocol Data: 0x%x\n ", *ptemp++); + } + + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_UNNECESSARY; + netif_rx(skb); + + info->stats.rx_packets++; + // Add on 12 bytes for MAC address which was removed + info->stats.rx_bytes += (len + 12); + + if (info->AsicID == ELECTRABUZZ_ID) { + // track how many bytes have been read from FIFO - round up to 16 bit word + tempword = len + 16; + if (tempword & 0x01) + tempword++; + info->fifo_cnt += tempword; + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_FIFO_LEN); + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, info->fifo_cnt); + } + + return SUCCESS; +} + +//--------------------------------------------------------------------------- +// +// Function: ft1000_copy_down_pkt +// Descripton: This function will take an ethernet packet and convert it to +// a Flarion packet prior to sending it to the ASIC Downlink +// FIFO. +// Input: +// dev - device structure +// packet - address of ethernet packet +// len - length of IP packet +// Output: +// status - FAILURE +// SUCCESS +// +//--------------------------------------------------------------------------- +int ft1000_copy_down_pkt(struct net_device *dev, u16 * packet, u16 len) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + union { + PSEUDO_HDR blk; + u16 buff[sizeof(PSEUDO_HDR) >> 1]; + u8 buffc[sizeof(PSEUDO_HDR)]; + } pseudo; + int i; + u32 *plong; + + DEBUG(1, "ft1000_hw: copy_down_pkt()\n"); + + // Check if there is room on the FIFO + if (len > ft1000_read_fifo_len(dev)) { + udelay(10); + if (len > ft1000_read_fifo_len(dev)) { + udelay(20); + } + if (len > ft1000_read_fifo_len(dev)) { + udelay(20); + } + if (len > ft1000_read_fifo_len(dev)) { + udelay(20); + } + if (len > ft1000_read_fifo_len(dev)) { + udelay(20); + } + if (len > ft1000_read_fifo_len(dev)) { + udelay(20); + } + if (len > ft1000_read_fifo_len(dev)) { + DEBUG(1, + "ft1000_hw:ft1000_copy_down_pkt:Transmit FIFO is fulli - pkt drop\n"); + info->stats.tx_errors++; + return SUCCESS; + } + } + // Create pseudo header and send pseudo/ip to hardware + if (info->AsicID == ELECTRABUZZ_ID) { + pseudo.blk.length = len; + } else { + pseudo.blk.length = ntohs(len); + } + pseudo.blk.source = DSPID; // Need to swap to get in correct order + pseudo.blk.destination = HOSTID; + pseudo.blk.portdest = NETWORKID; // Need to swap to get in correct order + pseudo.blk.portsrc = DSPAIRID; + pseudo.blk.sh_str_id = 0; + pseudo.blk.control = 0; + pseudo.blk.rsvd1 = 0; + pseudo.blk.seq_num = 0; + pseudo.blk.rsvd2 = info->packetseqnum++; + pseudo.blk.qos_class = 0; + /* Calculate pseudo header checksum */ + pseudo.blk.checksum = pseudo.buff[0]; + for (i = 1; i < 7; i++) { + pseudo.blk.checksum ^= pseudo.buff[i]; + } + + // Production Mode + if (info->AsicID == ELECTRABUZZ_ID) { + // copy first word to UFIFO_BEG reg + ft1000_write_reg(dev, FT1000_REG_UFIFO_BEG, pseudo.buff[0]); + DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 0 BEG = 0x%04x\n", + pseudo.buff[0]); + + // copy subsequent words to UFIFO_MID reg + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[1]); + DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 1 MID = 0x%04x\n", + pseudo.buff[1]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[2]); + DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 2 MID = 0x%04x\n", + pseudo.buff[2]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[3]); + DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 3 MID = 0x%04x\n", + pseudo.buff[3]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[4]); + DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 4 MID = 0x%04x\n", + pseudo.buff[4]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[5]); + DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 5 MID = 0x%04x\n", + pseudo.buff[5]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[6]); + DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 6 MID = 0x%04x\n", + pseudo.buff[6]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[7]); + DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 7 MID = 0x%04x\n", + pseudo.buff[7]); + + // Write PPP type + IP Packet into Downlink FIFO + for (i = 0; i < (len >> 1) - 1; i++) { + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, + htons(*packet)); + DEBUG(1, + "ft1000_hw:ft1000_copy_down_pkt:data %d MID = 0x%04x\n", + i + 8, htons(*packet)); + packet++; + } + + // Check for odd byte + if (len & 0x0001) { + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, + htons(*packet)); + DEBUG(1, + "ft1000_hw:ft1000_copy_down_pkt:data MID = 0x%04x\n", + htons(*packet)); + packet++; + ft1000_write_reg(dev, FT1000_REG_UFIFO_END, + htons(*packet)); + DEBUG(1, + "ft1000_hw:ft1000_copy_down_pkt:data %d MID = 0x%04x\n", + i + 8, htons(*packet)); + } else { + ft1000_write_reg(dev, FT1000_REG_UFIFO_END, + htons(*packet)); + DEBUG(1, + "ft1000_hw:ft1000_copy_down_pkt:data %d MID = 0x%04x\n", + i + 8, htons(*packet)); + } + } else { + outl(*(u32 *) & pseudo.buff[0], + dev->base_addr + FT1000_REG_MAG_UFDR); + DEBUG(1, "ft1000_copy_down_pkt: Pseudo = 0x%8x\n", + *(u32 *) & pseudo.buff[0]); + outl(*(u32 *) & pseudo.buff[2], + dev->base_addr + FT1000_REG_MAG_UFDR); + DEBUG(1, "ft1000_copy_down_pkt: Pseudo = 0x%8x\n", + *(u32 *) & pseudo.buff[2]); + outl(*(u32 *) & pseudo.buff[4], + dev->base_addr + FT1000_REG_MAG_UFDR); + DEBUG(1, "ft1000_copy_down_pkt: Pseudo = 0x%8x\n", + *(u32 *) & pseudo.buff[4]); + outl(*(u32 *) & pseudo.buff[6], + dev->base_addr + FT1000_REG_MAG_UFDR); + DEBUG(1, "ft1000_copy_down_pkt: Pseudo = 0x%8x\n", + *(u32 *) & pseudo.buff[6]); + + plong = (u32 *) packet; + // Write PPP type + IP Packet into Downlink FIFO + for (i = 0; i < (len >> 2); i++) { + outl(*plong++, dev->base_addr + FT1000_REG_MAG_UFDR); + } + + // Check for odd alignment + if (len & 0x0003) { + DEBUG(1, + "ft1000_hw:ft1000_copy_down_pkt:data = 0x%8x\n", + *plong); + outl(*plong++, dev->base_addr + FT1000_REG_MAG_UFDR); + } + outl(1, dev->base_addr + FT1000_REG_MAG_UFER); + } + + info->stats.tx_packets++; + // Add 14 bytes for MAC adddress plus ethernet type + info->stats.tx_bytes += (len + 14); + return SUCCESS; +} + +static struct net_device_stats *ft1000_stats(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + return (&info->stats); +} + +static int ft1000_open(struct net_device *dev) +{ + + DEBUG(0, "ft1000_hw: ft1000_open is called\n"); + + ft1000_reset_card(dev); + DEBUG(0, "ft1000_hw: ft1000_open is ended\n"); + + /* schedule ft1000_hbchk to perform periodic heartbeat checks on DSP and ASIC */ + init_timer(&poll_timer); + poll_timer.expires = jiffies + (2 * HZ); + poll_timer.data = (u_long) dev; + add_timer(&poll_timer); + + DEBUG(0, "ft1000_hw: ft1000_open is ended2\n"); + return 0; +} + +static int ft1000_close(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + + DEBUG(0, "ft1000_hw: ft1000_close()\n"); + + info->CardReady = 0; + del_timer(&poll_timer); + + if (ft1000_card_present == 1) { + DEBUG(0, "Media is down\n"); + netif_stop_queue(dev); + + ft1000_disable_interrupts(dev); + ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT); + + //reset ASIC + ft1000_reset_asic(dev); + } + return 0; +} + +static int ft1000_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u8 *pdata; + + DEBUG(1, "ft1000_hw: ft1000_start_xmit()\n"); + if (skb == NULL) { + DEBUG(1, "ft1000_hw: ft1000_start_xmit:skb == NULL!!!\n"); + return 0; + } + + DEBUG(1, "ft1000_hw: ft1000_start_xmit:length of packet = %d\n", + skb->len); + + pdata = (u8 *) skb->data; + + if (info->mediastate == 0) { + /* Drop packet is mediastate is down */ + DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:mediastate is down\n"); + return SUCCESS; + } + + if ((skb->len < ENET_HEADER_SIZE) || (skb->len > ENET_MAX_SIZE)) { + /* Drop packet which has invalid size */ + DEBUG(1, + "ft1000_hw:ft1000_copy_down_pkt:invalid ethernet length\n"); + return SUCCESS; + } + ft1000_copy_down_pkt(dev, (u16 *) (pdata + ENET_HEADER_SIZE - 2), + skb->len - ENET_HEADER_SIZE + 2); + + dev_kfree_skb(skb); + + return 0; +} + +static irqreturn_t ft1000_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + u16 tempword; + u16 inttype; + int cnt; + + DEBUG(1, "ft1000_hw: ft1000_interrupt()\n"); + + if (info->CardReady == 0) { + ft1000_disable_interrupts(dev); + return IRQ_HANDLED; + } + + if (ft1000_chkcard(dev) == FALSE) { + ft1000_disable_interrupts(dev); + return IRQ_HANDLED; + } + + ft1000_disable_interrupts(dev); + + // Read interrupt type + inttype = ft1000_read_reg(dev, FT1000_REG_SUP_ISR); + + // Make sure we process all interrupt before leaving the ISR due to the edge trigger interrupt type + while (inttype) { + if (inttype & ISR_DOORBELL_PEND) { + ft1000_parse_dpram_msg(dev); + } + + if (inttype & ISR_RCV) { + DEBUG(1, "Data in FIFO\n"); + + cnt = 0; + do { + // Check if we have packets in the Downlink FIFO + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = + ft1000_read_reg(dev, FT1000_REG_DFIFO_STAT); + } else { + tempword = + ft1000_read_reg(dev, FT1000_REG_MAG_DFSR); + } + if (tempword & 0x1f) { + ft1000_copy_up_pkt(dev); + } else { + break; + } + cnt++; + } while (cnt < MAX_RCV_LOOP); + + } + // clear interrupts + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR); + DEBUG(1, "ft1000_hw: interrupt status register = 0x%x\n", tempword); + ft1000_write_reg(dev, FT1000_REG_SUP_ISR, tempword); + + // Read interrupt type + inttype = ft1000_read_reg (dev, FT1000_REG_SUP_ISR); + DEBUG(1,"ft1000_hw: interrupt status register after clear = 0x%x\n",inttype); + } + ft1000_enable_interrupts(dev); + return IRQ_HANDLED; +} + +void stop_ft1000_card(struct net_device *dev) +{ + FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev); + PPROV_RECORD ptr; +// int cnt; + + DEBUG(0, "ft1000_hw: stop_ft1000_card()\n"); + + info->CardReady = 0; + ft1000_card_present = 0; + netif_stop_queue(dev); + ft1000_disable_interrupts(dev); + + // Make sure we free any memory reserve for provisioning + while (list_empty(&info->prov_list) == 0) { + ptr = list_entry(info->prov_list.next, PROV_RECORD, list); + list_del(&ptr->list); + kfree(ptr->pprov_data); + kfree(ptr); + } + + if (info->registered) { + unregister_netdev(dev); + info->registered = 0; + } + + free_irq(dev->irq, dev); + release_region(dev->base_addr,256); + release_firmware(fw_entry); + flarion_ft1000_cnt--; + ft1000CleanupProc(dev); + +} + +static void ft1000_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + FT1000_INFO *ft_info; + ft_info = (FT1000_INFO *) netdev_priv(dev); + + snprintf(info->driver, 32, "ft1000"); + snprintf(info->bus_info, ETHTOOL_BUSINFO_LEN, "PCMCIA 0x%lx", + dev->base_addr); + snprintf(info->fw_version, 32, "%d.%d.%d.%d", ft_info->DspVer[0], + ft_info->DspVer[1], ft_info->DspVer[2], ft_info->DspVer[3]); +} + +static u32 ft1000_get_link(struct net_device *dev) +{ + FT1000_INFO *info; + info = (FT1000_INFO *) netdev_priv(dev); + return info->mediastate; +} + +static const struct ethtool_ops ops = { + .get_drvinfo = ft1000_get_drvinfo, + .get_link = ft1000_get_link +}; + +struct net_device *init_ft1000_card(unsigned short irq, int port, + unsigned char *mac_addr, void *ft1000_reset, + void *link, struct device *fdev) +{ + FT1000_INFO *info; + struct net_device *dev; + int i; + + static const struct net_device_ops ft1000ops = // Slavius 21.10.2009 due to kernel changes + { + .ndo_open = &ft1000_open, + .ndo_stop = &ft1000_close, + .ndo_start_xmit = &ft1000_start_xmit, + .ndo_get_stats = &ft1000_stats, + }; + + DEBUG(1, "ft1000_hw: init_ft1000_card()\n"); + DEBUG(1, "ft1000_hw: irq = %d\n", irq); + DEBUG(1, "ft1000_hw: port = 0x%04x\n", port); + + flarion_ft1000_cnt++; + + if (flarion_ft1000_cnt > 1) { + flarion_ft1000_cnt--; + + printk(KERN_INFO + "ft1000: This driver can not support more than one instance\n"); + return NULL; + } + + dev = alloc_etherdev(sizeof(FT1000_INFO)); + if (!dev) { + printk(KERN_ERR "ft1000: failed to allocate etherdev\n"); + return NULL; + } + + SET_NETDEV_DEV(dev, fdev); + info = (FT1000_INFO *) netdev_priv(dev); + + memset(info, 0, sizeof(FT1000_INFO)); + + DEBUG(1, "address of dev = 0x%8x\n", (u32) dev); + DEBUG(1, "address of dev info = 0x%8x\n", (u32) info); + DEBUG(0, "device name = %s\n", dev->name); + + memset(&info->stats, 0, sizeof(struct net_device_stats)); + + spin_lock_init(&info->dpram_lock); + info->DrvErrNum = 0; + info->ASICResetNum = 0; + info->registered = 1; + info->link = link; + info->ft1000_reset = ft1000_reset; + info->mediastate = 0; + info->fifo_cnt = 0; + info->DeviceCreated = FALSE; + info->DeviceMajor = 0; + info->CurrentInterruptEnableMask = ISR_DEFAULT_MASK; + info->InterruptsEnabled = FALSE; + info->CardReady = 0; + info->DSP_TIME[0] = 0; + info->DSP_TIME[1] = 0; + info->DSP_TIME[2] = 0; + info->DSP_TIME[3] = 0; + flarion_ft1000_cnt = 0; + + INIT_LIST_HEAD(&info->prov_list); + + info->squeseqnum = 0; + +// dev->hard_start_xmit = &ft1000_start_xmit; +// dev->get_stats = &ft1000_stats; +// dev->open = &ft1000_open; +// dev->stop = &ft1000_close; + + dev->netdev_ops = &ft1000ops; // Slavius 21.10.2009 due to kernel changes + + DEBUG(0, "device name = %s\n", dev->name); + + for (i = 0; i < 6; i++) { + dev->dev_addr[i] = mac_addr[i]; + DEBUG(1, "ft1000_hw: mac_addr %d = 0x%02x\n", i, mac_addr[i]); + } + + netif_stop_queue(dev); + dev->irq = irq; + dev->base_addr = port; + + if (request_irq(dev->irq, ft1000_interrupt, IRQF_SHARED, dev->name, dev)) { + printk(KERN_ERR "ft1000: Could not request_irq\n"); + goto err_dev; + } + + if (request_region(dev->base_addr, 256, dev->name) == NULL) { + printk(KERN_ERR "ft1000: Could not request_region\n"); + goto err_irq; + } + + if (register_netdev(dev) != 0) { + DEBUG(0, "ft1000: Could not register netdev"); + goto err_reg; + } + + info->AsicID = ft1000_read_reg(dev, FT1000_REG_ASIC_ID); + if (info->AsicID == ELECTRABUZZ_ID) { + DEBUG(0, "ft1000_hw: ELECTRABUZZ ASIC\n"); + if (request_firmware(&fw_entry, "ft1000.img", fdev) != 0) { + printk(KERN_INFO "ft1000: Could not open ft1000.img\n"); + goto err_unreg; + } + } else { + DEBUG(0, "ft1000_hw: MAGNEMITE ASIC\n"); + if (request_firmware(&fw_entry, "ft2000.img", fdev) != 0) { + printk(KERN_INFO "ft1000: Could not open ft2000.img\n"); + goto err_unreg; + } + } + + ft1000_enable_interrupts(dev); + + ft1000InitProc(dev); + ft1000_card_present = 1; + SET_ETHTOOL_OPS(dev, &ops); + printk(KERN_INFO + "ft1000: %s: addr 0x%04lx irq %d, MAC addr %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->name, dev->base_addr, dev->irq, dev->dev_addr[0], + dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], + dev->dev_addr[4], dev->dev_addr[5]); + return dev; + +err_unreg: + unregister_netdev(dev); +err_reg: + release_region(dev->base_addr, 256); +err_irq: + free_irq(dev->irq, dev); +err_dev: + free_netdev(dev); + return NULL; +} + +EXPORT_SYMBOL(init_ft1000_card); +EXPORT_SYMBOL(stop_ft1000_card); +EXPORT_SYMBOL(flarion_ft1000_cnt); diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c new file mode 100644 index 0000000..b45de9b --- /dev/null +++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c @@ -0,0 +1,219 @@ +/*--------------------------------------------------------------------------- + FT1000 driver for Flarion Flash OFDM NIC Device + + 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/module.h> +#include <linux/kernel.h> +#include <linux/proc_fs.h> +#include <linux/string.h> +#include <linux/vmalloc.h> +#include <linux/netdevice.h> +#include <asm/uaccess.h> +#include "ft1000.h" + +#define FT1000_PROC "ft1000" +#define MAX_FILE_LEN 255 + +#define PUTM_TO_PAGE(len,page,args...) \ + len += snprintf(page+len, PAGE_SIZE - len, args) + +#define PUTX_TO_PAGE(len,page,message,size,var) \ + len += snprintf(page+len, PAGE_SIZE - len, message); \ + for(i = 0; i < (size - 1); i++) \ + { \ + len += snprintf(page+len, PAGE_SIZE - len, "%02x:", var[i]); \ + } \ + len += snprintf(page+len, PAGE_SIZE - len, "%02x\n", var[i]) + +#define PUTD_TO_PAGE(len,page,message,size,var) \ + len += snprintf(page+len, PAGE_SIZE - len, message); \ + for(i = 0; i < (size - 1); i++) \ + { \ + len += snprintf(page+len, PAGE_SIZE - len, "%d.", var[i]); \ + } \ + len += snprintf(page+len, PAGE_SIZE - len, "%d\n", var[i]) + +int ft1000ReadProc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct net_device *dev; + int len; + int i; + FT1000_INFO *info; + char *status[] = + { "Idle (Disconnect)", "Searching", "Active (Connected)", + "Waiting for L2", "Sleep", "No Coverage", "", "" + }; + char *signal[] = { "", "*", "**", "***", "****" }; + int strength; + int quality; + struct timeval tv; + time_t delta; + + dev = (struct net_device *)data; + info = (FT1000_INFO *) netdev_priv(dev); + + if (off > 0) { + *eof = 1; + return 0; + } + + /* Wrap-around */ + + if (info->AsicID == ELECTRABUZZ_ID) { + if (info->DspHibernateFlag == 0) { + if (info->ProgConStat != 0xFF) { + info->LedStat = + ft1000_read_dpram(dev, FT1000_DSP_LED); + info->ConStat = + ft1000_read_dpram(dev, + FT1000_DSP_CON_STATE); + } else { + info->ConStat = 0xf; + } + } + } else { + if (info->ProgConStat != 0xFF) { + info->LedStat = + ntohs(ft1000_read_dpram_mag_16 + (dev, FT1000_MAG_DSP_LED, + FT1000_MAG_DSP_LED_INDX)); + info->ConStat = + ntohs(ft1000_read_dpram_mag_16 + (dev, FT1000_MAG_DSP_CON_STATE, + FT1000_MAG_DSP_CON_STATE_INDX)); + } else { + info->ConStat = 0xf; + } + } + + i = (info->LedStat) & 0xf; + switch (i) { + case 0x1: + strength = 1; + break; + case 0x3: + strength = 2; + break; + case 0x7: + strength = 3; + break; + case 0xf: + strength = 4; + break; + default: + strength = 0; + } + + i = (info->LedStat >> 8) & 0xf; + switch (i) { + case 0x1: + quality = 1; + break; + case 0x3: + quality = 2; + break; + case 0x7: + quality = 3; + break; + case 0xf: + quality = 4; + break; + default: + quality = 0; + } + + do_gettimeofday(&tv); + delta = (tv.tv_sec - info->ConTm); + len = 0; + PUTM_TO_PAGE(len, page, "Connection Time: %02ld:%02ld:%02ld\n", + ((delta / 3600) % 24), ((delta / 60) % 60), (delta % 60)); + PUTM_TO_PAGE(len, page, "Connection Time[s]: %ld\n", delta); + PUTM_TO_PAGE(len, page, "Asic ID: %s\n", + (info->AsicID) == + ELECTRABUZZ_ID ? "ELECTRABUZZ ASIC" : "MAGNEMITE ASIC"); + PUTX_TO_PAGE(len, page, "SKU: ", SKUSZ, info->Sku); + PUTX_TO_PAGE(len, page, "EUI64: ", EUISZ, info->eui64); + PUTD_TO_PAGE(len, page, "DSP version number: ", DSPVERSZ, info->DspVer); + PUTX_TO_PAGE(len, page, "Hardware Serial Number: ", HWSERNUMSZ, + info->HwSerNum); + PUTX_TO_PAGE(len, page, "Caliberation Version: ", CALVERSZ, + info->RfCalVer); + PUTD_TO_PAGE(len, page, "Caliberation Date: ", CALDATESZ, + info->RfCalDate); + PUTM_TO_PAGE(len, page, "Media State: %s\n", + (info->mediastate) ? "link" : "no link"); + PUTM_TO_PAGE(len, page, "Connection Status: %s\n", + status[((info->ConStat) & 0x7)]); + PUTM_TO_PAGE(len, page, "RX packets: %ld\n", info->stats.rx_packets); + PUTM_TO_PAGE(len, page, "TX packets: %ld\n", info->stats.tx_packets); + PUTM_TO_PAGE(len, page, "RX bytes: %ld\n", info->stats.rx_bytes); + PUTM_TO_PAGE(len, page, "TX bytes: %ld\n", info->stats.tx_bytes); + PUTM_TO_PAGE(len, page, "Signal Strength: %s\n", signal[strength]); + PUTM_TO_PAGE(len, page, "Signal Quality: %s\n", signal[quality]); + return len; +} + +static int ft1000NotifyProc(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct net_device *dev = ptr; + FT1000_INFO *info; + + info = (FT1000_INFO *) netdev_priv(dev); + + switch (event) { + case NETDEV_CHANGENAME: + remove_proc_entry(info->netdevname, info->proc_ft1000); + create_proc_read_entry(dev->name, 0644, info->proc_ft1000, + ft1000ReadProc, dev); + snprintf(info->netdevname, IFNAMSIZ, "%s", dev->name); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block ft1000_netdev_notifier = { + .notifier_call = ft1000NotifyProc +}; + +void ft1000InitProc(struct net_device *dev) +{ + FT1000_INFO *info; + + info = (FT1000_INFO *) netdev_priv(dev); + + info->proc_ft1000 = proc_mkdir(FT1000_PROC, init_net.proc_net); + create_proc_read_entry(dev->name, 0644, info->proc_ft1000, + ft1000ReadProc, dev); + snprintf(info->netdevname, IFNAMSIZ, "%s", dev->name); + register_netdevice_notifier(&ft1000_netdev_notifier); +} + +void ft1000CleanupProc(struct net_device *dev) +{ + FT1000_INFO *info; + + info = (FT1000_INFO *) netdev_priv(dev); + + remove_proc_entry(dev->name, info->proc_ft1000); + remove_proc_entry(FT1000_PROC, init_net.proc_net); + unregister_netdevice_notifier(&ft1000_netdev_notifier); +} + +EXPORT_SYMBOL(ft1000InitProc); +EXPORT_SYMBOL(ft1000CleanupProc); |