From 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 Mon Sep 17 00:00:00 2001 From: Yunhong Jiang Date: Tue, 4 Aug 2015 12:17:53 -0700 Subject: Add the rt linux 4.1.3-rt3 as base Import the rt linux 4.1.3-rt3 as OPNFV kvm base. It's from git://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-rt-devel.git linux-4.1.y-rt and the base is: commit 0917f823c59692d751951bf5ea699a2d1e2f26a2 Author: Sebastian Andrzej Siewior Date: Sat Jul 25 12:13:34 2015 +0200 Prepare v4.1.3-rt3 Signed-off-by: Sebastian Andrzej Siewior We lose all the git history this way and it's not good. We should apply another opnfv project repo in future. Change-Id: I87543d81c9df70d99c5001fbdf646b202c19f423 Signed-off-by: Yunhong Jiang --- .../drivers/staging/ft1000/ft1000-pcmcia/Makefile | 2 + kernel/drivers/staging/ft1000/ft1000-pcmcia/boot.h | 158 ++ .../drivers/staging/ft1000/ft1000-pcmcia/ft1000.h | 71 + .../staging/ft1000/ft1000-pcmcia/ft1000.img | Bin 0 -> 305770 bytes .../staging/ft1000/ft1000-pcmcia/ft1000_cs.c | 158 ++ .../staging/ft1000/ft1000-pcmcia/ft1000_dnld.c | 769 ++++++++ .../staging/ft1000/ft1000-pcmcia/ft1000_hw.c | 2068 ++++++++++++++++++++ 7 files changed, 3226 insertions(+) create mode 100644 kernel/drivers/staging/ft1000/ft1000-pcmcia/Makefile create mode 100644 kernel/drivers/staging/ft1000/ft1000-pcmcia/boot.h create mode 100644 kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000.h create mode 100644 kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000.img create mode 100644 kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.c create mode 100644 kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c create mode 100644 kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c (limited to 'kernel/drivers/staging/ft1000/ft1000-pcmcia') diff --git a/kernel/drivers/staging/ft1000/ft1000-pcmcia/Makefile b/kernel/drivers/staging/ft1000/ft1000-pcmcia/Makefile new file mode 100644 index 000000000..715de3f00 --- /dev/null +++ b/kernel/drivers/staging/ft1000/ft1000-pcmcia/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_FT1000_PCMCIA) = ft1000_pcmcia.o +ft1000_pcmcia-y := ft1000_hw.o ft1000_dnld.o ft1000_cs.o diff --git a/kernel/drivers/staging/ft1000/ft1000-pcmcia/boot.h b/kernel/drivers/staging/ft1000/ft1000-pcmcia/boot.h new file mode 100644 index 000000000..e4a698528 --- /dev/null +++ b/kernel/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 */ +static 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/kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000.h b/kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000.h new file mode 100644 index 000000000..5992670f7 --- /dev/null +++ b/kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000.h @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------- + 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: Common structures and defines + ---------------------------------------------------------------------------*/ +#ifndef _FT1000H_ +#define _FT1000H_ + +#include "../ft1000.h" + +#define FT1000_DRV_VER 0x01010300 + +#define FT1000_DPRAM_BASE 0x0000 /* Dual Port RAM starting offset */ + +/* + * Maximum number of occurrence of pseudo header errors before resetting PC + * Card. + */ +#define MAX_PH_ERR 300 + +#define SUCCESS 0x00 +#define FAILURE 0x01 + +struct ft1000_pcmcia { + int PktIntfErr; + u16 packetseqnum; + void *link; +}; + +struct pcmcia_device; +struct net_device; +extern struct net_device *init_ft1000_card(struct pcmcia_device *link, + void *ft1000_reset); +extern void stop_ft1000_card(struct net_device *dev); +extern int card_download(struct net_device *dev, const u8 *pFileStart, + size_t FileLength); + +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); + +/* Read the value of a given ASIC register. */ +static inline u16 ft1000_read_reg(struct net_device *dev, u16 offset) +{ + return inw(dev->base_addr + offset); +} + +/* Set the value of a given ASIC register. */ +static inline void ft1000_write_reg(struct net_device *dev, u16 offset, + u16 value) +{ + outw(value, dev->base_addr + offset); +} + +#endif diff --git a/kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000.img b/kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000.img new file mode 100644 index 000000000..aad3c80d0 Binary files /dev/null and b/kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000.img differ diff --git a/kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.c b/kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.c new file mode 100644 index 000000000..e5cc5bedf --- /dev/null +++ b/kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.c @@ -0,0 +1,158 @@ +/*--------------------------------------------------------------------------- + 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 + . 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 +#include +#include +#include +#include +#include + +/*====================================================================*/ + +MODULE_AUTHOR("Wai Chan"); +MODULE_DESCRIPTION("FT1000 PCMCIA driver"); +MODULE_LICENSE("GPL"); + +/*====================================================================*/ + +static int ft1000_config(struct pcmcia_device *link); +static void ft1000_detach(struct pcmcia_device *link); +static int ft1000_attach(struct pcmcia_device *link); + +#include "ft1000.h" + +/*====================================================================*/ + +static void ft1000_reset(struct pcmcia_device *link) +{ + pcmcia_reset_card(link->socket); +} + +static int ft1000_attach(struct pcmcia_device *link) +{ + link->priv = NULL; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + + return ft1000_config(link); +} + +static void ft1000_detach(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + if (dev) + stop_ft1000_card(dev); + + pcmcia_disable_device(link); + free_netdev(dev); +} + +static int ft1000_confcheck(struct pcmcia_device *link, void *priv_data) +{ + return pcmcia_request_io(link); +} + +/*====================================================================== + + 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. + + ======================================================================*/ + +static int ft1000_config(struct pcmcia_device *link) +{ + int ret; + + dev_dbg(&link->dev, "ft1000_cs: ft1000_config(0x%p)\n", link); + + /* setup IO window */ + ret = pcmcia_loop_config(link, ft1000_confcheck, NULL); + if (ret) { + dev_err(&link->dev, "Could not configure pcmcia\n"); + return -ENODEV; + } + + /* configure device */ + ret = pcmcia_enable_device(link); + if (ret) { + dev_err(&link->dev, "Could not enable pcmcia\n"); + goto failed; + } + + link->priv = init_ft1000_card(link, &ft1000_reset); + if (!link->priv) { + dev_err(&link->dev, "Could not register as network device\n"); + goto failed; + } + + /* Finally, report what we've done */ + + return 0; +failed: + pcmcia_disable_device(link); + return -ENODEV; +} + +static int ft1000_suspend(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + if (link->open) + netif_device_detach(dev); + return 0; +} + +static int ft1000_resume(struct pcmcia_device *link) +{ + return 0; +} + +/*====================================================================*/ + +static const 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, + .name = "ft1000_cs", + .probe = ft1000_attach, + .remove = ft1000_detach, + .id_table = ft1000_ids, + .suspend = ft1000_suspend, + .resume = ft1000_resume, +}; + +module_pcmcia_driver(ft1000_cs_driver); diff --git a/kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c b/kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c new file mode 100644 index 000000000..83683e9a1 --- /dev/null +++ b/kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c @@ -0,0 +1,769 @@ +/*--------------------------------------------------------------------------- + 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define __KERNEL_SYSCALLS__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ft1000.h" +#include "boot.h" + +#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 + +u16 get_handshake(struct net_device *dev, u16 expected_value); +void put_handshake(struct net_device *dev, u16 handshake_value); +u16 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); +u16 hdr_checksum(struct pseudo_hdr *pHdr); + +struct dsp_file_hdr { + u32 version_id; /* Version ID of this image format. */ + u32 package_id; /* Package ID of code release. */ + u32 build_date; /* Date/time stamp when file was built. */ + u32 commands_offset; /* Offset to attached commands in Pseudo Hdr format. */ + u32 loader_offset; /* Offset to bootloader code. */ + u32 loader_code_address; /* Start address of bootloader. */ + u32 loader_code_end; /* Where bootloader code ends. */ + u32 loader_code_size; + u32 version_data_offset; /* Offset were scrambled version data begins. */ + u32 version_data_size; /* Size, in words, of scrambled version data. */ + u32 nDspImages; /* Number of DSP images in file. */ +} __packed; + +struct dsp_image_info { + u32 coff_date; /* Date/time when DSP Coff image was built. */ + u32 begin_offset; /* Offset in file where image begins. */ + u32 end_offset; /* Offset in file where image begins. */ + u32 run_address; /* On chip Start address of DSP code. */ + u32 image_size; /* Size of image. */ + u32 version; /* Embedded version # of DSP code. */ + unsigned short checksum; /* Dsp File checksum */ + unsigned short pad1; +} __packed; + +void card_bootload(struct net_device *dev) +{ + struct ft1000_info *info = netdev_priv(dev); + unsigned long flags; + u32 *pdata; + u32 size; + u32 i; + u32 templong; + + netdev_dbg(dev, "card_bootload is called\n"); + + pdata = (u32 *)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); +} + +u16 get_handshake(struct net_device *dev, u16 expected_value) +{ + struct ft1000_info *info = netdev_priv(dev); + u16 handshake; + u32 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 = (u16)tempx; + } + + if ((handshake == expected_value) + || (handshake == HANDSHAKE_RESET_VALUE)) { + return handshake; + } + loopcnt++; + mdelay(DSP_WAIT_SLEEP_TIME); + + } + + return HANDSHAKE_TIMEOUT_VALUE; + +} + +void put_handshake(struct net_device *dev, u16 handshake_value) +{ + struct ft1000_info *info = netdev_priv(dev); + u32 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 = (u32)handshake_value; + tempx = ntohl(tempx); + ft1000_write_dpram_mag_32(dev, DWNLD_MAG_HANDSHAKE_LOC, tempx); /* Handshake */ + } +} + +u16 get_request_type(struct net_device *dev) +{ + struct ft1000_info *info = netdev_priv(dev); + u16 request_type; + u32 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 = (u16)tempx; + } + + return request_type; + +} + +long get_request_value(struct net_device *dev) +{ + struct ft1000_info *info = netdev_priv(dev); + long value; + u16 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) +{ + struct ft1000_info *info = netdev_priv(dev); + u16 size; + u32 tempx; + + if (info->AsicID == ELECTRABUZZ_ID) { + size = (u16) (lvalue >> 16); + + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + DWNLD_SIZE_MSW_LOC); + + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size); + + size = (u16) (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 */ + } + +} + +u16 hdr_checksum(struct pseudo_hdr *pHdr) +{ + u16 *usPtr = (u16 *)pHdr; + u16 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, const u8 *pFileStart, + size_t FileLength) +{ + struct ft1000_info *info = netdev_priv(dev); + int Status = SUCCESS; + u32 uiState; + u16 handshake; + struct pseudo_hdr *pHdr; + u16 usHdrLength; + long word_length; + u16 request; + u16 temp; + struct prov_record *pprov_record; + u8 *pbuffer; + struct dsp_file_hdr *pFileHdr5; + struct dsp_image_info *pDspImageInfoV6 = NULL; + long requested_version; + bool bGoodVersion = false; + struct drv_msg *pMailBoxData; + u16 *pUsData = NULL; + u16 *pUsFile = NULL; + u8 *pUcFile = NULL; + u8 *pBootEnd = NULL; + u8 *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; + + file_version = *(long *)pFileStart; + if (file_version != 6) { + pr_err("unsupported firmware version %ld\n", file_version); + Status = FAILURE; + } + + uiState = STATE_START_DWNLD; + + pFileHdr5 = (struct dsp_file_hdr *)pFileStart; + + pUsFile = (u16 *) ((long)pFileStart + pFileHdr5->loader_offset); + pUcFile = (u8 *) ((long)pFileStart + pFileHdr5->loader_offset); + pBootEnd = (u8 *) ((long)pFileStart + pFileHdr5->loader_code_end); + loader_code_address = pFileHdr5->loader_code_address; + loader_code_size = pFileHdr5->loader_code_size; + bGoodVersion = false; + + 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 = (u16 *) ((long)pBootEnd); + pUcFile = (u8 *) ((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); + /* + * 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: + netdev_dbg(dev, + "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 */ + pUsFile = (u16 *) ((long)pFileStart + pFileHdr5->commands_offset); + pUcFile = (u8 *) ((long)pFileStart + pFileHdr5->commands_offset); + 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; + } + /* + * 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 = + (struct drv_msg *)&info->DSPInfoBlk[0]; + pUsData = + (u16 *)&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 = + (u16 *) ((long)pFileStart + + pFileHdr5-> + version_data_offset); + /* Provide mutual exclusive access while reading ASIC registers. */ + spin_lock_irqsave(&info->dpram_lock, + flags); + /* + * 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); + pDspImageInfoV6 = + (struct dsp_image_info *) ((long) + pFileStart + + + sizeof + (struct dsp_file_hdr)); + for (imageN = 0; + imageN < + pFileHdr5->nDspImages; + imageN++) { + temp = (u16) + (pDspImageInfoV6-> + version); + templong = temp; + temp = (u16) + (pDspImageInfoV6-> + version >> 16); + templong |= + (temp << 16); + if (templong == + requested_version) { + bGoodVersion = + true; + pUsFile = + (u16 + *) ((long) + pFileStart + + + pDspImageInfoV6-> + begin_offset); + pUcFile = + (u8 + *) ((long) + pFileStart + + + pDspImageInfoV6-> + begin_offset); + pCodeEnd = + (u8 + *) ((long) + pFileStart + + + pDspImageInfoV6-> + end_offset); + run_address = + pDspImageInfoV6-> + run_address; + run_size = + pDspImageInfoV6-> + image_size; + image_chksum = + (u32) + pDspImageInfoV6-> + checksum; + netdev_dbg(dev, + "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 (((unsigned long)(pUcFile) - (unsigned long) pFileStart) >= + (unsigned long)FileLength) { + uiState = STATE_DONE_FILE; + break; + } + + pHdr = (struct pseudo_hdr *)pUsFile; + + if (pHdr->portdest == 0x80 /* DspOAM */ + && (pHdr->portsrc == 0x00 /* Driver */ + || pHdr->portsrc == 0x10 /* FMM */)) { + uiState = STATE_SECTION_PROV; + } else { + netdev_dbg(dev, + "Download error: Bad Port IDs in Pseudo Record\n"); + netdev_dbg(dev, "\t Port Source = 0x%2.2x\n", + pHdr->portsrc); + netdev_dbg(dev, "\t Port Destination = 0x%2.2x\n", + pHdr->portdest); + Status = FAILURE; + } + + break; + + case STATE_SECTION_PROV: + + pHdr = (struct pseudo_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(struct pseudo_hdr), + GFP_ATOMIC); + if (pbuffer) { + memcpy(pbuffer, pUcFile, + (u32) (usHdrLength + + sizeof(struct pseudo_hdr))); + /* link provisioning data */ + pprov_record = + kmalloc(sizeof(struct 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 = + (u8 *)((unsigned long) pUcFile + + (unsigned long) ((usHdrLength + 1) & 0xFFFFFFFE) + sizeof(struct pseudo_hdr)); + if ((unsigned long) (pUcFile) - + (unsigned long) (pFileStart) >= + (unsigned long)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/kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c b/kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c new file mode 100644 index 000000000..eecfa3770 --- /dev/null +++ b/kernel/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c @@ -0,0 +1,2068 @@ +/*--------------------------------------------------------------------------- + FT1000 driver for Flarion Flash OFDM NIC Device + + Copyright (C) 2002 Flarion Technologies, All rights reserved. + Copyright (C) 2006 Patrik Ostrihon, All rights reserved. + Copyright (C) 2006 ProWeb Consulting, a.s, All rights reserved. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) any + later version. This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License along with this program; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - + Suite 330, Boston, MA 02111-1307, USA. + -------------------------------------------------------------------------*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include "ft1000.h" + +static const struct firmware *fw_entry; + +static void ft1000_hbchk(u_long data); +static struct timer_list poll_timer = { + .function = ft1000_hbchk +}; + +static u16 cmdbuffer[1024]; +static u8 tempbuffer[1600]; +static u8 ft1000_card_present; +static u8 flarion_ft1000_cnt; + +static irqreturn_t ft1000_interrupt(int irq, void *dev_id); +static void ft1000_enable_interrupts(struct net_device *dev); +static void ft1000_disable_interrupts(struct net_device *dev); + +/* new kernel */ +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("Support for Flarion Flash OFDM NIC Device. Support for PCMCIA when used with ft1000_cs."); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("FT1000"); + +#define MAX_RCV_LOOP 100 + +/*--------------------------------------------------------------------------- + + Function: ft1000_read_fifo_len + Description: This function will read the ASIC Uplink FIFO status register + which will return the number of bytes remaining in the Uplink FIFO. + Sixteen bytes are subtracted to make sure that the ASIC does not + reach its threshold. + Input: + dev - network device structure + Output: + value - number of bytes available in the ASIC Uplink FIFO. + + -------------------------------------------------------------------------*/ +static inline u16 ft1000_read_fifo_len(struct net_device *dev) +{ + struct ft1000_info *info = netdev_priv(dev); + + if (info->AsicID == ELECTRABUZZ_ID) + return (ft1000_read_reg(dev, FT1000_REG_UFIFO_STAT) - 16); + else + return (ft1000_read_reg(dev, FT1000_REG_MAG_UFSR) - 16); +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_read_dpram + Description: This function will read the specific area of dpram + (Electrabuzz ASIC only) + Input: + dev - device structure + offset - index of dpram + Output: + value - value of dpram + + -------------------------------------------------------------------------*/ +u16 ft1000_read_dpram(struct net_device *dev, int offset) +{ + struct ft1000_info *info = netdev_priv(dev); + unsigned long flags; + u16 data; + + /* Provide mutual exclusive access while reading ASIC registers. */ + spin_lock_irqsave(&info->dpram_lock, flags); + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); + data = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); + spin_unlock_irqrestore(&info->dpram_lock, flags); + + return data; +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_write_dpram + Description: This function will write to a specific area of dpram + (Electrabuzz ASIC only) + Input: + dev - device structure + offset - index of dpram + value - value to write + Output: + none. + + -------------------------------------------------------------------------*/ +static inline void ft1000_write_dpram(struct net_device *dev, + int offset, u16 value) +{ + struct ft1000_info *info = netdev_priv(dev); + unsigned long flags; + + /* Provide mutual exclusive access while reading ASIC registers. */ + spin_lock_irqsave(&info->dpram_lock, flags); + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, value); + spin_unlock_irqrestore(&info->dpram_lock, flags); +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_read_dpram_mag_16 + Description: This function will read the specific area of dpram + (Magnemite ASIC only) + Input: + dev - device structure + offset - index of dpram + Output: + value - value of dpram + + -------------------------------------------------------------------------*/ +u16 ft1000_read_dpram_mag_16(struct net_device *dev, int offset, int Index) +{ + struct ft1000_info *info = netdev_priv(dev); + unsigned long flags; + u16 data; + + /* Provide mutual exclusive access while reading ASIC registers. */ + spin_lock_irqsave(&info->dpram_lock, flags); + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); + /* check if we want to read upper or lower 32-bit word */ + if (Index) + data = ft1000_read_reg(dev, FT1000_REG_MAG_DPDATAL); + else + data = ft1000_read_reg(dev, FT1000_REG_MAG_DPDATAH); + + spin_unlock_irqrestore(&info->dpram_lock, flags); + + return data; +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_write_dpram_mag_16 + Description: This function will write to a specific area of dpram + (Magnemite ASIC only) + Input: + dev - device structure + offset - index of dpram + value - value to write + Output: + none. + + -------------------------------------------------------------------------*/ +static inline void ft1000_write_dpram_mag_16(struct net_device *dev, + int offset, u16 value, int Index) +{ + struct ft1000_info *info = netdev_priv(dev); + unsigned long flags; + + /* Provide mutual exclusive access while reading ASIC registers. */ + spin_lock_irqsave(&info->dpram_lock, flags); + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); + if (Index) + ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAL, value); + else + ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAH, value); + + spin_unlock_irqrestore(&info->dpram_lock, flags); +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_read_dpram_mag_32 + Description: This function will read the specific area of dpram + (Magnemite ASIC only) + Input: + dev - device structure + offset - index of dpram + Output: + value - value of dpram + + -------------------------------------------------------------------------*/ +u32 ft1000_read_dpram_mag_32(struct net_device *dev, int offset) +{ + struct ft1000_info *info = netdev_priv(dev); + unsigned long flags; + u32 data; + + /* Provide mutual exclusive access while reading ASIC registers. */ + spin_lock_irqsave(&info->dpram_lock, flags); + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); + data = inl(dev->base_addr + FT1000_REG_MAG_DPDATAL); + spin_unlock_irqrestore(&info->dpram_lock, flags); + + return data; +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_write_dpram_mag_32 + Description: This function will write to a specific area of dpram + (Magnemite ASIC only) + Input: + dev - device structure + offset - index of dpram + value - value to write + Output: + none. + + -------------------------------------------------------------------------*/ +void ft1000_write_dpram_mag_32(struct net_device *dev, int offset, u32 value) +{ + struct ft1000_info *info = netdev_priv(dev); + unsigned long flags; + + /* Provide mutual exclusive access while reading ASIC registers. */ + spin_lock_irqsave(&info->dpram_lock, flags); + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); + outl(value, dev->base_addr + FT1000_REG_MAG_DPDATAL); + spin_unlock_irqrestore(&info->dpram_lock, flags); +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_enable_interrupts + Description: This function will enable interrupts base on the current + interrupt mask. + Input: + dev - device structure + Output: + None. + + -------------------------------------------------------------------------*/ +static void ft1000_enable_interrupts(struct net_device *dev) +{ + u16 tempword; + + ft1000_write_reg(dev, FT1000_REG_SUP_IMASK, ISR_DEFAULT_MASK); + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK); + pr_debug("current interrupt enable mask = 0x%x\n", tempword); +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_disable_interrupts + Description: This function will disable all interrupts. + Input: + dev - device structure + Output: + None. + + -------------------------------------------------------------------------*/ +static void ft1000_disable_interrupts(struct net_device *dev) +{ + u16 tempword; + + ft1000_write_reg(dev, FT1000_REG_SUP_IMASK, ISR_MASK_ALL); + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK); + pr_debug("current interrupt enable mask = 0x%x\n", tempword); +} + +/*--------------------------------------------------------------------------- + Function: ft1000_read_dsp_timer + Description: This function reads the DSP timer and stores its value in the + DSP_TIME field of the ft1000_info struct passed as argument + Input: + dev - device structure + info - ft1000_info structure + Output: + None. + + -------------------------------------------------------------------------*/ +static void ft1000_read_dsp_timer(struct net_device *dev, + struct ft1000_info *info) +{ + if (info->AsicID == ELECTRABUZZ_ID) { + info->DSP_TIME[0] = ft1000_read_dpram(dev, FT1000_DSP_TIMER0); + info->DSP_TIME[1] = ft1000_read_dpram(dev, FT1000_DSP_TIMER1); + info->DSP_TIME[2] = ft1000_read_dpram(dev, FT1000_DSP_TIMER2); + info->DSP_TIME[3] = ft1000_read_dpram(dev, FT1000_DSP_TIMER3); + } else { + info->DSP_TIME[0] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER0, + FT1000_MAG_DSP_TIMER0_INDX); + info->DSP_TIME[1] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER1, + FT1000_MAG_DSP_TIMER1_INDX); + info->DSP_TIME[2] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER2, + FT1000_MAG_DSP_TIMER2_INDX); + info->DSP_TIME[3] = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER3, + FT1000_MAG_DSP_TIMER3_INDX); + } +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_reset_asic + Description: This function will call the Card Service function to reset the + ASIC. + Input: + dev - device structure + Output: + none + + -------------------------------------------------------------------------*/ +static void ft1000_reset_asic(struct net_device *dev) +{ + struct ft1000_info *info = netdev_priv(dev); + struct ft1000_pcmcia *pcmcia = info->priv; + u16 tempword; + + (*info->ft1000_reset) (pcmcia->link); + + /* + * Let's use the register provided by the Magnemite ASIC to reset the + * ASIC and DSP. + */ + if (info->AsicID == MAGNEMITE_ID) { + ft1000_write_reg(dev, FT1000_REG_RESET, + DSP_RESET_BIT | ASIC_RESET_BIT); + } + mdelay(1); + if (info->AsicID == ELECTRABUZZ_ID) { + /* set watermark to -1 in order to not generate an interrupt */ + ft1000_write_reg(dev, FT1000_REG_WATERMARK, 0xffff); + } else { + /* set watermark to -1 in order to not generate an interrupt */ + ft1000_write_reg(dev, FT1000_REG_MAG_WATERMARK, 0xffff); + } + /* clear interrupts */ + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR); + pr_debug("interrupt status register = 0x%x\n", tempword); + ft1000_write_reg(dev, FT1000_REG_SUP_ISR, tempword); + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR); + pr_debug("interrupt status register = 0x%x\n", tempword); + +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_reset_card + Description: This function will reset the card + Input: + dev - device structure + Output: + status - false (card reset fail) + true (card reset successful) + + -------------------------------------------------------------------------*/ +static int ft1000_reset_card(struct net_device *dev) +{ + struct ft1000_info *info = netdev_priv(dev); + u16 tempword; + int i; + unsigned long flags; + struct prov_record *ptr; + struct prov_record *tmp; + + info->CardReady = 0; + info->ProgConStat = 0; + info->squeseqnum = 0; + ft1000_disable_interrupts(dev); + + /* del_timer(&poll_timer); */ + + /* Make sure we free any memory reserve for provisioning */ + list_for_each_entry_safe(ptr, tmp, &info->prov_list, list) { + pr_debug("deleting provisioning record\n"); + list_del(&ptr->list); + kfree(ptr->pprov_data); + kfree(ptr); + } + + if (info->AsicID == ELECTRABUZZ_ID) { + pr_debug("resetting DSP\n"); + ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT); + } else { + pr_debug("resetting ASIC and DSP\n"); + ft1000_write_reg(dev, FT1000_REG_RESET, + DSP_RESET_BIT | ASIC_RESET_BIT); + } + + /* Copy DSP session record into info block if this is not a coldstart */ + if (ft1000_card_present == 1) { + spin_lock_irqsave(&info->dpram_lock, flags); + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_RX_BASE); + for (i = 0; i < MAX_DSP_SESS_REC; i++) { + info->DSPSess.Rec[i] = + ft1000_read_reg(dev, + FT1000_REG_DPRAM_DATA); + } + } else { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_MAG_RX_BASE); + for (i = 0; i < MAX_DSP_SESS_REC / 2; i++) { + info->DSPSess.MagRec[i] = + inl(dev->base_addr + + FT1000_REG_MAG_DPDATA); + } + } + spin_unlock_irqrestore(&info->dpram_lock, flags); + } + + pr_debug("resetting ASIC\n"); + mdelay(10); + /* reset ASIC */ + ft1000_reset_asic(dev); + + pr_debug("downloading dsp image\n"); + + if (info->AsicID == MAGNEMITE_ID) { + /* Put dsp in reset and take ASIC out of reset */ + pr_debug("Put DSP in reset and take ASIC out of reset\n"); + ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT); + + /* Setting MAGNEMITE ASIC to big endian mode */ + ft1000_write_reg(dev, FT1000_REG_SUP_CTRL, HOST_INTF_BE); + /* Download bootloader */ + card_bootload(dev); + + /* Take DSP out of reset */ + ft1000_write_reg(dev, FT1000_REG_RESET, 0); + /* FLARION_DSP_ACTIVE; */ + mdelay(10); + pr_debug("Take DSP out of reset\n"); + + /* + * Wait for 0xfefe indicating dsp ready before starting + * download + */ + for (i = 0; i < 50; i++) { + tempword = ft1000_read_dpram_mag_16(dev, + FT1000_MAG_DPRAM_FEFE, + FT1000_MAG_DPRAM_FEFE_INDX); + if (tempword == 0xfefe) + break; + mdelay(20); + } + + if (i == 50) { + pr_debug("No FEFE detected from DSP\n"); + return false; + } + + } else { + /* Take DSP out of reset */ + ft1000_write_reg(dev, FT1000_REG_RESET, ~DSP_RESET_BIT); + mdelay(10); + } + + if (card_download(dev, fw_entry->data, fw_entry->size)) { + pr_debug("card download unsuccessful\n"); + return false; + } + pr_debug("card download successful\n"); + + mdelay(10); + + if (info->AsicID == ELECTRABUZZ_ID) { + /* + * Need to initialize the FIFO length counter to zero in order + * to sync up with the DSP + */ + info->fifo_cnt = 0; + ft1000_write_dpram(dev, FT1000_FIFO_LEN, info->fifo_cnt); + /* Initialize DSP heartbeat area to ho */ + ft1000_write_dpram(dev, FT1000_HI_HO, ho); + tempword = ft1000_read_dpram(dev, FT1000_HI_HO); + pr_debug("hi_ho value = 0x%x\n", tempword); + } else { + /* Initialize DSP heartbeat area to ho */ + ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, ho_mag, + FT1000_MAG_HI_HO_INDX); + tempword = + ft1000_read_dpram_mag_16(dev, FT1000_MAG_HI_HO, + FT1000_MAG_HI_HO_INDX); + pr_debug("hi_ho value = 0x%x\n", tempword); + } + + info->CardReady = 1; + ft1000_enable_interrupts(dev); + + /* Schedule heartbeat process to run every 2 seconds */ + /* poll_timer.expires = jiffies + (2*HZ); */ + /* poll_timer.data = (u_long)dev; */ + /* add_timer(&poll_timer); */ + + return true; + +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_chkcard + Description: This function will check if the device is presently available on + the system. + Input: + dev - device structure + Output: + status - false (device is not present) + true (device is present) + + -------------------------------------------------------------------------*/ +static int ft1000_chkcard(struct net_device *dev) +{ + u16 tempword; + + /* + * Mask register is used to check for device presence since it is never + * set to zero. + */ + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK); + if (tempword == 0) { + pr_debug("IMASK = 0 Card not detected\n"); + return false; + } + /* + * The system will return the value of 0xffff for the version register + * if the device is not present. + */ + tempword = ft1000_read_reg(dev, FT1000_REG_ASIC_ID); + if (tempword == 0xffff) { + pr_debug("Version = 0xffff Card not detected\n"); + return false; + } + return true; +} + + +/*--------------------------------------------------------------------------- + + Function: ft1000_hbchk + Description: This function will perform the heart beat check of the DSP as + well as the ASIC. + Input: + dev - device structure + Output: + none + + -------------------------------------------------------------------------*/ +static void ft1000_hbchk(u_long data) +{ + struct net_device *dev = (struct net_device *)data; + + struct ft1000_info *info; + u16 tempword; + + info = netdev_priv(dev); + + if (info->CardReady == 1) { + /* Perform dsp heartbeat check */ + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = ft1000_read_dpram(dev, FT1000_HI_HO); + } else { + tempword = + ntohs(ft1000_read_dpram_mag_16 + (dev, FT1000_MAG_HI_HO, + FT1000_MAG_HI_HO_INDX)); + } + pr_debug("hi_ho value = 0x%x\n", tempword); + /* Let's perform another check if ho is not detected */ + if (tempword != ho) { + if (info->AsicID == ELECTRABUZZ_ID) + tempword = ft1000_read_dpram(dev, FT1000_HI_HO); + else + tempword = ntohs(ft1000_read_dpram_mag_16(dev, + FT1000_MAG_HI_HO, + FT1000_MAG_HI_HO_INDX)); + } + if (tempword != ho) { + pr_info("heartbeat failed - no ho detected\n"); + ft1000_read_dsp_timer(dev, info); + info->DrvErrNum = DSP_HB_INFO; + if (ft1000_reset_card(dev) == 0) { + pr_info("Hardware Failure Detected - PC Card disabled\n"); + info->ProgConStat = 0xff; + return; + } + /* Schedule this module to run every 2 seconds */ + poll_timer.expires = jiffies + (2*HZ); + poll_timer.data = (u_long)dev; + add_timer(&poll_timer); + return; + } + + tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + /* Let's check doorbell again if fail */ + if (tempword & FT1000_DB_HB) + tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + + if (tempword & FT1000_DB_HB) { + pr_info("heartbeat doorbell not clear by firmware\n"); + ft1000_read_dsp_timer(dev, info); + info->DrvErrNum = DSP_HB_INFO; + if (ft1000_reset_card(dev) == 0) { + pr_info("Hardware Failure Detected - PC Card disabled\n"); + info->ProgConStat = 0xff; + return; + } + /* Schedule this module to run every 2 seconds */ + poll_timer.expires = jiffies + (2*HZ); + poll_timer.data = (u_long)dev; + add_timer(&poll_timer); + return; + } + /* + * Set dedicated area to hi and ring appropriate doorbell + * according to hi/ho heartbeat protocol + */ + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_dpram(dev, FT1000_HI_HO, hi); + } else { + ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, hi_mag, + FT1000_MAG_HI_HO_INDX); + } + + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = ft1000_read_dpram(dev, FT1000_HI_HO); + } else { + tempword = + ntohs(ft1000_read_dpram_mag_16 + (dev, FT1000_MAG_HI_HO, + FT1000_MAG_HI_HO_INDX)); + } + /* Let's write hi again if fail */ + if (tempword != hi) { + if (info->AsicID == ELECTRABUZZ_ID) + ft1000_write_dpram(dev, FT1000_HI_HO, hi); + else + ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, + hi_mag, FT1000_MAG_HI_HO_INDX); + + if (info->AsicID == ELECTRABUZZ_ID) + tempword = ft1000_read_dpram(dev, FT1000_HI_HO); + else + tempword = ntohs(ft1000_read_dpram_mag_16(dev, + FT1000_MAG_HI_HO, + FT1000_MAG_HI_HO_INDX)); + } + + if (tempword != hi) { + pr_info("heartbeat failed - cannot write hi into DPRAM\n"); + ft1000_read_dsp_timer(dev, info); + info->DrvErrNum = DSP_HB_INFO; + if (ft1000_reset_card(dev) == 0) { + pr_info("Hardware Failure Detected - PC Card disabled\n"); + info->ProgConStat = 0xff; + return; + } + /* Schedule this module to run every 2 seconds */ + poll_timer.expires = jiffies + (2*HZ); + poll_timer.data = (u_long)dev; + add_timer(&poll_timer); + return; + } + ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_HB); + + } + + /* Schedule this module to run every 2 seconds */ + poll_timer.expires = jiffies + (2 * HZ); + poll_timer.data = (u_long)dev; + add_timer(&poll_timer); +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_send_cmd + Description: + Input: + Output: + + -------------------------------------------------------------------------*/ +static void ft1000_send_cmd(struct net_device *dev, u16 *ptempbuffer, int size, + u16 qtype) +{ + struct ft1000_info *info = netdev_priv(dev); + int i; + u16 tempword; + unsigned long flags; + + size += sizeof(struct pseudo_hdr); + /* check for odd byte and increment to 16-bit word align value */ + if ((size & 0x0001)) + size++; + pr_debug("total length = %d\n", size); + pr_debug("length = %d\n", ntohs(*ptempbuffer)); + /* + * put message into slow queue area + * All messages are in the form total_len + pseudo header + message body + */ + spin_lock_irqsave(&info->dpram_lock, flags); + + /* Make sure SLOWQ doorbell is clear */ + tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + i = 0; + while (tempword & FT1000_DB_DPRAM_TX) { + mdelay(10); + i++; + if (i == 10) { + spin_unlock_irqrestore(&info->dpram_lock, flags); + return; + } + tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + } + + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_TX_BASE); + /* Write total length to dpram */ + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size); + /* Write pseudo header and messgae body */ + for (i = 0; i < (size >> 1); i++) { + pr_debug("data %d = 0x%x\n", i, *ptempbuffer); + tempword = htons(*ptempbuffer++); + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, tempword); + } + } else { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_MAG_TX_BASE); + /* Write total length to dpram */ + ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAH, htons(size)); + /* Write pseudo header and messgae body */ + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_MAG_TX_BASE + 1); + for (i = 0; i < (size >> 2); i++) { + pr_debug("data = 0x%x\n", *ptempbuffer); + outw(*ptempbuffer++, + dev->base_addr + FT1000_REG_MAG_DPDATAL); + pr_debug("data = 0x%x\n", *ptempbuffer); + outw(*ptempbuffer++, + dev->base_addr + FT1000_REG_MAG_DPDATAH); + } + pr_debug("data = 0x%x\n", *ptempbuffer); + outw(*ptempbuffer++, dev->base_addr + FT1000_REG_MAG_DPDATAL); + pr_debug("data = 0x%x\n", *ptempbuffer); + outw(*ptempbuffer++, dev->base_addr + FT1000_REG_MAG_DPDATAH); + } + spin_unlock_irqrestore(&info->dpram_lock, flags); + + /* ring doorbell to notify DSP that we have a message ready */ + ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_DPRAM_TX); +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_receive_cmd + Description: This function will read a message from the dpram area. + Input: + dev - network device structure + pbuffer - caller supply address to buffer + pnxtph - pointer to next pseudo header + Output: + Status = 0 (unsuccessful) + = 1 (successful) + + -------------------------------------------------------------------------*/ +static bool ft1000_receive_cmd(struct net_device *dev, u16 *pbuffer, + int maxsz, u16 *pnxtph) +{ + struct ft1000_info *info = netdev_priv(dev); + u16 size; + u16 *ppseudohdr; + int i; + u16 tempword; + unsigned long flags; + + if (info->AsicID == ELECTRABUZZ_ID) { + size = ft1000_read_dpram(dev, *pnxtph) + + sizeof(struct pseudo_hdr); + } else { + size = ntohs(ft1000_read_dpram_mag_16(dev, FT1000_MAG_PH_LEN, + FT1000_MAG_PH_LEN_INDX)) + + sizeof(struct pseudo_hdr); + } + if (size > maxsz) { + pr_debug("Invalid command length = %d\n", size); + return false; + } + ppseudohdr = (u16 *)pbuffer; + spin_lock_irqsave(&info->dpram_lock, flags); + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_RX_BASE + 2); + for (i = 0; i <= (size >> 1); i++) { + tempword = + ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); + *pbuffer++ = ntohs(tempword); + } + } else { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_MAG_RX_BASE); + *pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAH); + pr_debug("received data = 0x%x\n", *pbuffer); + pbuffer++; + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_MAG_RX_BASE + 1); + for (i = 0; i <= (size >> 2); i++) { + *pbuffer = + inw(dev->base_addr + + FT1000_REG_MAG_DPDATAL); + pbuffer++; + *pbuffer = + inw(dev->base_addr + + FT1000_REG_MAG_DPDATAH); + pbuffer++; + } + /* copy odd aligned word */ + *pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAL); + pr_debug("received data = 0x%x\n", *pbuffer); + pbuffer++; + *pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAH); + pr_debug("received data = 0x%x\n", *pbuffer); + pbuffer++; + } + if (size & 0x0001) { + /* copy odd byte from fifo */ + tempword = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); + *pbuffer = ntohs(tempword); + } + spin_unlock_irqrestore(&info->dpram_lock, flags); + + /* + * Check if pseudo header checksum is good + * Calculate pseudo header checksum + */ + tempword = *ppseudohdr++; + for (i = 1; i < 7; i++) + tempword ^= *ppseudohdr++; + if (tempword != *ppseudohdr) { + pr_debug("Pseudo header checksum mismatch\n"); + /* Drop this message */ + return false; + } + return true; +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_proc_drvmsg + Description: This function will process the various driver messages. + Input: + dev - device structure + pnxtph - pointer to next pseudo header + Output: + none + + -------------------------------------------------------------------------*/ +static void ft1000_proc_drvmsg(struct net_device *dev) +{ + struct ft1000_info *info = netdev_priv(dev); + u16 msgtype; + u16 tempword; + struct media_msg *pmediamsg; + struct dsp_init_msg *pdspinitmsg; + struct drv_msg *pdrvmsg; + u16 len; + u16 i; + struct prov_record *ptr; + struct pseudo_hdr *ppseudo_hdr; + u16 *pmsg; + struct timeval tv; + union { + u8 byte[2]; + u16 wrd; + } convert; + + if (info->AsicID == ELECTRABUZZ_ID) + tempword = FT1000_DPRAM_RX_BASE+2; + else + tempword = FT1000_DPRAM_MAG_RX_BASE; + + if (ft1000_receive_cmd(dev, &cmdbuffer[0], MAX_CMD_SQSIZE, &tempword)) { + + /* + * Get the message type which is total_len + PSEUDO header + * + msgtype + message body + */ + pdrvmsg = (struct drv_msg *)&cmdbuffer[0]; + msgtype = ntohs(pdrvmsg->type); + pr_debug("Command message type = 0x%x\n", msgtype); + switch (msgtype) { + case DSP_PROVISION: + pr_debug("Got a provisioning request message from DSP\n"); + mdelay(25); + while (list_empty(&info->prov_list) == 0) { + pr_debug("Sending a provisioning message\n"); + /* Make sure SLOWQ doorbell is clear */ + tempword = ft1000_read_reg(dev, + FT1000_REG_DOORBELL); + i = 0; + while (tempword & FT1000_DB_DPRAM_TX) { + mdelay(5); + i++; + if (i == 10) + break; + } + ptr = list_entry(info->prov_list.next, + struct prov_record, list); + len = *(u16 *)ptr->pprov_data; + len = htons(len); + + pmsg = (u16 *)ptr->pprov_data; + ppseudo_hdr = (struct pseudo_hdr *)pmsg; + /* Insert slow queue sequence number */ + ppseudo_hdr->seq_num = info->squeseqnum++; + ppseudo_hdr->portsrc = 0; + /* Calculate new checksum */ + ppseudo_hdr->checksum = *pmsg++; + pr_debug("checksum = 0x%x\n", + ppseudo_hdr->checksum); + for (i = 1; i < 7; i++) { + ppseudo_hdr->checksum ^= *pmsg++; + pr_debug("checksum = 0x%x\n", + ppseudo_hdr->checksum); + } + + ft1000_send_cmd(dev, (u16 *)ptr->pprov_data, + len, SLOWQ_TYPE); + list_del(&ptr->list); + kfree(ptr->pprov_data); + kfree(ptr); + } + /* + * Indicate adapter is ready to take application + * messages after all provisioning messages are sent + */ + info->CardReady = 1; + break; + case MEDIA_STATE: + pmediamsg = (struct media_msg *)&cmdbuffer[0]; + if (info->ProgConStat != 0xFF) { + if (pmediamsg->state) { + pr_debug("Media is up\n"); + if (info->mediastate == 0) { + netif_carrier_on(dev); + netif_wake_queue(dev); + info->mediastate = 1; + do_gettimeofday(&tv); + info->ConTm = tv.tv_sec; + } + } else { + pr_debug("Media is down\n"); + if (info->mediastate == 1) { + info->mediastate = 0; + netif_carrier_off(dev); + netif_stop_queue(dev); + info->ConTm = 0; + } + } + } else { + pr_debug("Media is down\n"); + if (info->mediastate == 1) { + info->mediastate = 0; + netif_carrier_off(dev); + netif_stop_queue(dev); + info->ConTm = 0; + } + } + break; + case DSP_INIT_MSG: + pdspinitmsg = (struct dsp_init_msg *)&cmdbuffer[0]; + memcpy(info->DspVer, pdspinitmsg->DspVer, DSPVERSZ); + pr_debug("DSPVER = 0x%2x 0x%2x 0x%2x 0x%2x\n", + info->DspVer[0], info->DspVer[1], + info->DspVer[2], info->DspVer[3]); + memcpy(info->HwSerNum, pdspinitmsg->HwSerNum, + HWSERNUMSZ); + memcpy(info->Sku, pdspinitmsg->Sku, SKUSZ); + memcpy(info->eui64, pdspinitmsg->eui64, EUISZ); + dev->dev_addr[0] = info->eui64[0]; + dev->dev_addr[1] = info->eui64[1]; + dev->dev_addr[2] = info->eui64[2]; + dev->dev_addr[3] = info->eui64[5]; + dev->dev_addr[4] = info->eui64[6]; + dev->dev_addr[5] = info->eui64[7]; + + if (ntohs(pdspinitmsg->length) == + (sizeof(struct dsp_init_msg) - 20)) { + memcpy(info->ProductMode, + pdspinitmsg->ProductMode, MODESZ); + memcpy(info->RfCalVer, pdspinitmsg->RfCalVer, + CALVERSZ); + memcpy(info->RfCalDate, pdspinitmsg->RfCalDate, + CALDATESZ); + pr_debug("RFCalVer = 0x%2x 0x%2x\n", + info->RfCalVer[0], info->RfCalVer[1]); + } + + break; + case DSP_STORE_INFO: + pr_debug("Got DSP_STORE_INFO\n"); + tempword = ntohs(pdrvmsg->length); + info->DSPInfoBlklen = tempword; + if (tempword < (MAX_DSP_SESS_REC - 4)) { + pmsg = (u16 *)&pdrvmsg->data[0]; + for (i = 0; i < ((tempword + 1) / 2); i++) { + pr_debug("dsp info data = 0x%x\n", + *pmsg); + info->DSPInfoBlk[i + 10] = *pmsg++; + } + } + break; + case DSP_GET_INFO: + pr_debug("Got DSP_GET_INFO\n"); + /* + * copy dsp info block to dsp + * allow any outstanding ioctl to finish + */ + mdelay(10); + tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + if (tempword & FT1000_DB_DPRAM_TX) { + mdelay(10); + tempword = ft1000_read_reg(dev, + FT1000_REG_DOORBELL); + if (tempword & FT1000_DB_DPRAM_TX) + mdelay(10); + } + + if ((tempword & FT1000_DB_DPRAM_TX) == 0) { + /* + * Put message into Slow Queue + * Form Pseudo header + */ + pmsg = (u16 *)info->DSPInfoBlk; + ppseudo_hdr = (struct pseudo_hdr *)pmsg; + ppseudo_hdr->length = + htons(info->DSPInfoBlklen + 4); + ppseudo_hdr->source = 0x10; + ppseudo_hdr->destination = 0x20; + ppseudo_hdr->portdest = 0; + ppseudo_hdr->portsrc = 0; + ppseudo_hdr->sh_str_id = 0; + ppseudo_hdr->control = 0; + ppseudo_hdr->rsvd1 = 0; + ppseudo_hdr->rsvd2 = 0; + ppseudo_hdr->qos_class = 0; + /* Insert slow queue sequence number */ + ppseudo_hdr->seq_num = info->squeseqnum++; + /* Insert application id */ + ppseudo_hdr->portsrc = 0; + /* Calculate new checksum */ + ppseudo_hdr->checksum = *pmsg++; + for (i = 1; i < 7; i++) + ppseudo_hdr->checksum ^= *pmsg++; + + info->DSPInfoBlk[8] = 0x7200; + info->DSPInfoBlk[9] = + htons(info->DSPInfoBlklen); + ft1000_send_cmd(dev, info->DSPInfoBlk, + (u16)(info->DSPInfoBlklen+4), + 0); + } + + break; + case GET_DRV_ERR_RPT_MSG: + pr_debug("Got GET_DRV_ERR_RPT_MSG\n"); + /* + * copy driver error message to dsp + * allow any outstanding ioctl to finish + */ + mdelay(10); + tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + if (tempword & FT1000_DB_DPRAM_TX) { + mdelay(10); + tempword = ft1000_read_reg(dev, + FT1000_REG_DOORBELL); + if (tempword & FT1000_DB_DPRAM_TX) + mdelay(10); + } + + if ((tempword & FT1000_DB_DPRAM_TX) == 0) { + /* + * Put message into Slow Queue + * Form Pseudo header + */ + pmsg = (u16 *)&tempbuffer[0]; + ppseudo_hdr = (struct pseudo_hdr *)pmsg; + ppseudo_hdr->length = htons(0x0012); + ppseudo_hdr->source = 0x10; + ppseudo_hdr->destination = 0x20; + ppseudo_hdr->portdest = 0; + ppseudo_hdr->portsrc = 0; + ppseudo_hdr->sh_str_id = 0; + ppseudo_hdr->control = 0; + ppseudo_hdr->rsvd1 = 0; + ppseudo_hdr->rsvd2 = 0; + ppseudo_hdr->qos_class = 0; + /* Insert slow queue sequence number */ + ppseudo_hdr->seq_num = info->squeseqnum++; + /* Insert application id */ + ppseudo_hdr->portsrc = 0; + /* Calculate new checksum */ + ppseudo_hdr->checksum = *pmsg++; + for (i = 1; i < 7; i++) + ppseudo_hdr->checksum ^= *pmsg++; + + pmsg = (u16 *)&tempbuffer[16]; + *pmsg++ = htons(RSP_DRV_ERR_RPT_MSG); + *pmsg++ = htons(0x000e); + *pmsg++ = htons(info->DSP_TIME[0]); + *pmsg++ = htons(info->DSP_TIME[1]); + *pmsg++ = htons(info->DSP_TIME[2]); + *pmsg++ = htons(info->DSP_TIME[3]); + convert.byte[0] = info->DspVer[0]; + convert.byte[1] = info->DspVer[1]; + *pmsg++ = convert.wrd; + convert.byte[0] = info->DspVer[2]; + convert.byte[1] = info->DspVer[3]; + *pmsg++ = convert.wrd; + *pmsg++ = htons(info->DrvErrNum); + + ft1000_send_cmd(dev, (u16 *)&tempbuffer[0], + (u16)(0x0012), 0); + info->DrvErrNum = 0; + } + + break; + default: + break; + } + } +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_parse_dpram_msg + Description: This function will parse the message received from the DSP + via the DPRAM interface. + Input: + dev - device structure + Output: + status - FAILURE + SUCCESS + + -------------------------------------------------------------------------*/ +static int ft1000_parse_dpram_msg(struct net_device *dev) +{ + struct ft1000_info *info = netdev_priv(dev); + u16 doorbell; + u16 portid; + u16 nxtph; + u16 total_len; + int i = 0; + unsigned long flags; + + doorbell = ft1000_read_reg(dev, FT1000_REG_DOORBELL); + pr_debug("Doorbell = 0x%x\n", doorbell); + + if (doorbell & FT1000_ASIC_RESET_REQ) { + /* Copy DSP session record from info block */ + spin_lock_irqsave(&info->dpram_lock, flags); + if (info->AsicID == ELECTRABUZZ_ID) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_RX_BASE); + for (i = 0; i < MAX_DSP_SESS_REC; i++) { + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, + info->DSPSess.Rec[i]); + } + } else { + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, + FT1000_DPRAM_MAG_RX_BASE); + for (i = 0; i < MAX_DSP_SESS_REC / 2; i++) { + outl(info->DSPSess.MagRec[i], + dev->base_addr + FT1000_REG_MAG_DPDATA); + } + } + spin_unlock_irqrestore(&info->dpram_lock, flags); + + /* clear ASIC RESET request */ + ft1000_write_reg(dev, FT1000_REG_DOORBELL, + FT1000_ASIC_RESET_REQ); + pr_debug("Got an ASIC RESET Request\n"); + ft1000_write_reg(dev, FT1000_REG_DOORBELL, + FT1000_ASIC_RESET_DSP); + + if (info->AsicID == MAGNEMITE_ID) { + /* Setting MAGNEMITE ASIC to big endian mode */ + ft1000_write_reg(dev, FT1000_REG_SUP_CTRL, + HOST_INTF_BE); + } + } + + if (doorbell & FT1000_DSP_ASIC_RESET) { + pr_debug("Got a dsp ASIC reset message\n"); + ft1000_write_reg(dev, FT1000_REG_DOORBELL, + FT1000_DSP_ASIC_RESET); + udelay(200); + return SUCCESS; + } + + if (doorbell & FT1000_DB_DPRAM_RX) { + pr_debug("Got a slow queue message\n"); + nxtph = FT1000_DPRAM_RX_BASE + 2; + if (info->AsicID == ELECTRABUZZ_ID) { + total_len = + ft1000_read_dpram(dev, FT1000_DPRAM_RX_BASE); + } else { + total_len = + ntohs(ft1000_read_dpram_mag_16 + (dev, FT1000_MAG_TOTAL_LEN, + FT1000_MAG_TOTAL_LEN_INDX)); + } + pr_debug("total length = %d\n", total_len); + if ((total_len < MAX_CMD_SQSIZE) + && (total_len > sizeof(struct pseudo_hdr))) { + total_len += nxtph; + /* + * ft1000_read_reg will return a value that needs to be + * byteswap in order to get DSP_QID_OFFSET. + */ + if (info->AsicID == ELECTRABUZZ_ID) { + portid = (ft1000_read_dpram(dev, DSP_QID_OFFSET + + FT1000_DPRAM_RX_BASE + 2) + >> 8) & 0xff; + } else { + portid = + ft1000_read_dpram_mag_16 + (dev, FT1000_MAG_PORT_ID, + FT1000_MAG_PORT_ID_INDX) & 0xff; + } + pr_debug("DSP_QID = 0x%x\n", portid); + + if (portid == DRIVERID) { + /* + * We are assumming one driver message from the + * DSP at a time. + */ + ft1000_proc_drvmsg(dev); + } + } + ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_DPRAM_RX); + } + + if (doorbell & FT1000_DB_COND_RESET) { + /* Reset ASIC and DSP */ + ft1000_read_dsp_timer(dev, info); + info->DrvErrNum = DSP_CONDRESET_INFO; + pr_debug("DSP conditional reset requested\n"); + ft1000_reset_card(dev); + ft1000_write_reg(dev, FT1000_REG_DOORBELL, + FT1000_DB_COND_RESET); + } + /* let's clear any unexpected doorbells from DSP */ + doorbell = + doorbell & ~(FT1000_DB_DPRAM_RX | FT1000_ASIC_RESET_REQ | + FT1000_DB_COND_RESET | 0xff00); + if (doorbell) { + pr_debug("Clearing unexpected doorbell = 0x%x\n", doorbell); + ft1000_write_reg(dev, FT1000_REG_DOORBELL, doorbell); + } + + return SUCCESS; + +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_flush_fifo + Description: This function will flush one packet from the downlink + FIFO. + Input: + dev - device structure + drv_err - driver error causing the flush fifo + Output: + None. + + -------------------------------------------------------------------------*/ +static void ft1000_flush_fifo(struct net_device *dev, u16 DrvErrNum) +{ + struct ft1000_info *info = netdev_priv(dev); + struct ft1000_pcmcia *pcmcia = info->priv; + u16 i; + u32 templong; + u16 tempword; + + if (pcmcia->PktIntfErr > MAX_PH_ERR) { + ft1000_read_dsp_timer(dev, info); + info->DrvErrNum = DrvErrNum; + ft1000_reset_card(dev); + return; + } + /* Flush corrupted pkt from FIFO */ + i = 0; + do { + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = + ft1000_read_reg(dev, FT1000_REG_DFIFO); + tempword = + ft1000_read_reg(dev, FT1000_REG_DFIFO_STAT); + } else { + templong = + inl(dev->base_addr + FT1000_REG_MAG_DFR); + tempword = + inw(dev->base_addr + FT1000_REG_MAG_DFSR); + } + i++; + /* + * This should never happen unless the ASIC is broken. + * We must reset to recover. + */ + if ((i > 2048) || (tempword == 0)) { + ft1000_read_dsp_timer(dev, info); + if (tempword == 0) { + /* + * Let's check if ASIC reads are still ok by + * reading the Mask register which is never zero + * at this point of the code. + */ + tempword = + inw(dev->base_addr + + FT1000_REG_SUP_IMASK); + if (tempword == 0) { + /* + * This indicates that we can not + * communicate with the ASIC + */ + info->DrvErrNum = FIFO_FLUSH_BADCNT; + } else { + /* + * Let's assume that we really flush + * the FIFO + */ + pcmcia->PktIntfErr++; + return; + } + } else { + info->DrvErrNum = FIFO_FLUSH_MAXLIMIT; + } + return; + } + tempword = inw(dev->base_addr + FT1000_REG_SUP_STAT); + } while ((tempword & 0x03) != 0x03); + if (info->AsicID == ELECTRABUZZ_ID) { + i++; + pr_debug("Flushing FIFO complete = %x\n", tempword); + /* Flush last word in FIFO. */ + tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); + /* Update FIFO counter for DSP */ + i = i * 2; + pr_debug("Flush Data byte count to dsp = %d\n", i); + info->fifo_cnt += i; + ft1000_write_dpram(dev, FT1000_FIFO_LEN, + info->fifo_cnt); + } else { + pr_debug("Flushing FIFO complete = %x\n", tempword); + /* Flush last word in FIFO */ + templong = inl(dev->base_addr + FT1000_REG_MAG_DFR); + tempword = inw(dev->base_addr + FT1000_REG_SUP_STAT); + pr_debug("FT1000_REG_SUP_STAT = 0x%x\n", tempword); + tempword = inw(dev->base_addr + FT1000_REG_MAG_DFSR); + pr_debug("FT1000_REG_MAG_DFSR = 0x%x\n", tempword); + } + if (DrvErrNum) + pcmcia->PktIntfErr++; +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_copy_up_pkt + Description: This function will pull Flarion packets out of the Downlink + FIFO and convert it to an ethernet packet. The ethernet packet will + then be deliver to the TCP/IP stack. + Input: + dev - device structure + Output: + status - FAILURE + SUCCESS + + -------------------------------------------------------------------------*/ +static int ft1000_copy_up_pkt(struct net_device *dev) +{ + u16 tempword; + struct ft1000_info *info = netdev_priv(dev); + u16 len; + struct sk_buff *skb; + u16 i; + u8 *pbuffer = NULL; + u8 *ptemp = NULL; + u16 chksum; + u32 *ptemplong; + u32 templong; + + /* Read length */ + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); + len = tempword; + } else { + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); + len = ntohs(tempword); + } + chksum = tempword; + pr_debug("Number of Bytes in FIFO = %d\n", len); + + if (len > ENET_MAX_SIZE) { + pr_debug("size of ethernet packet invalid\n"); + if (info->AsicID == MAGNEMITE_ID) { + /* Read High word to complete 32 bit access */ + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + } + ft1000_flush_fifo(dev, DSP_PKTLEN_INFO); + info->stats.rx_errors++; + return FAILURE; + } + + skb = dev_alloc_skb(len + 12 + 2); + + if (skb == NULL) { + /* Read High word to complete 32 bit access */ + if (info->AsicID == MAGNEMITE_ID) + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + + ft1000_flush_fifo(dev, 0); + info->stats.rx_errors++; + return FAILURE; + } + pbuffer = (u8 *)skb_put(skb, len + 12); + + /* Pseudo header */ + if (info->AsicID == ELECTRABUZZ_ID) { + for (i = 1; i < 7; i++) { + tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); + chksum ^= tempword; + } + /* read checksum value */ + tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); + } else { + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + pr_debug("Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); + pr_debug("Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + pr_debug("Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); + pr_debug("Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + pr_debug("Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); + pr_debug("Pseudo = 0x%x\n", tempword); + chksum ^= tempword; + + /* read checksum value */ + tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); + pr_debug("Pseudo = 0x%x\n", tempword); + } + + if (chksum != tempword) { + pr_debug("Packet checksum mismatch 0x%x 0x%x\n", + chksum, tempword); + ft1000_flush_fifo(dev, DSP_PKTPHCKSUM_INFO); + info->stats.rx_errors++; + kfree_skb(skb); + return FAILURE; + } + /* subtract the number of bytes read already */ + ptemp = pbuffer; + + /* fake MAC address */ + *pbuffer++ = dev->dev_addr[0]; + *pbuffer++ = dev->dev_addr[1]; + *pbuffer++ = dev->dev_addr[2]; + *pbuffer++ = dev->dev_addr[3]; + *pbuffer++ = dev->dev_addr[4]; + *pbuffer++ = dev->dev_addr[5]; + *pbuffer++ = 0x00; + *pbuffer++ = 0x07; + *pbuffer++ = 0x35; + *pbuffer++ = 0xff; + *pbuffer++ = 0xff; + *pbuffer++ = 0xfe; + + if (info->AsicID == ELECTRABUZZ_ID) { + for (i = 0; i < len / 2; i++) { + tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); + *pbuffer++ = (u8) (tempword >> 8); + *pbuffer++ = (u8)tempword; + if (ft1000_chkcard(dev) == false) { + kfree_skb(skb); + return FAILURE; + } + } + + /* Need to read one more word if odd byte */ + if (len & 0x0001) { + tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); + *pbuffer++ = (u8) (tempword >> 8); + } + } else { + ptemplong = (u32 *)pbuffer; + for (i = 0; i < len / 4; i++) { + templong = inl(dev->base_addr + FT1000_REG_MAG_DFR); + pr_debug("Data = 0x%8x\n", templong); + *ptemplong++ = templong; + } + + /* Need to read one more word if odd align. */ + if (len & 0x0003) { + templong = inl(dev->base_addr + FT1000_REG_MAG_DFR); + pr_debug("Data = 0x%8x\n", templong); + *ptemplong++ = templong; + } + + } + + pr_debug("Data passed to Protocol layer:\n"); + for (i = 0; i < len + 12; i++) + pr_debug("Protocol Data: 0x%x\n", *ptemp++); + + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_UNNECESSARY; + netif_rx(skb); + + info->stats.rx_packets++; + /* Add on 12 bytes for MAC address which was removed */ + info->stats.rx_bytes += (len + 12); + + if (info->AsicID == ELECTRABUZZ_ID) { + /* track how many bytes have been read from FIFO - round up to + * 16 bit word */ + tempword = len + 16; + if (tempword & 0x01) + tempword++; + info->fifo_cnt += tempword; + ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_FIFO_LEN); + ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, info->fifo_cnt); + } + + return SUCCESS; +} + +/*--------------------------------------------------------------------------- + + Function: ft1000_copy_down_pkt + Description: This function will take an ethernet packet and convert it to + a Flarion packet prior to sending it to the ASIC Downlink + FIFO. + Input: + dev - device structure + packet - address of ethernet packet + len - length of IP packet + Output: + status - FAILURE + SUCCESS + + -------------------------------------------------------------------------*/ +static int ft1000_copy_down_pkt(struct net_device *dev, u16 *packet, u16 len) +{ + struct ft1000_info *info = netdev_priv(dev); + struct ft1000_pcmcia *pcmcia = info->priv; + union { + struct pseudo_hdr blk; + u16 buff[sizeof(struct pseudo_hdr) >> 1]; + u8 buffc[sizeof(struct pseudo_hdr)]; + } pseudo; + int i; + u32 *plong; + + /* Check if there is room on the FIFO */ + if (len > ft1000_read_fifo_len(dev)) { + udelay(10); + if (len > ft1000_read_fifo_len(dev)) + udelay(20); + if (len > ft1000_read_fifo_len(dev)) + udelay(20); + if (len > ft1000_read_fifo_len(dev)) + udelay(20); + if (len > ft1000_read_fifo_len(dev)) + udelay(20); + if (len > ft1000_read_fifo_len(dev)) + udelay(20); + if (len > ft1000_read_fifo_len(dev)) { + pr_debug("Transmit FIFO is full - pkt drop\n"); + info->stats.tx_errors++; + return SUCCESS; + } + } + /* Create pseudo header and send pseudo/ip to hardware */ + if (info->AsicID == ELECTRABUZZ_ID) + pseudo.blk.length = len; + else + pseudo.blk.length = ntohs(len); + + pseudo.blk.source = DSPID; /* Need to swap to get in correct + order */ + pseudo.blk.destination = HOSTID; + pseudo.blk.portdest = NETWORKID; /* Need to swap to get in + correct order */ + pseudo.blk.portsrc = DSPAIRID; + pseudo.blk.sh_str_id = 0; + pseudo.blk.control = 0; + pseudo.blk.rsvd1 = 0; + pseudo.blk.seq_num = 0; + pseudo.blk.rsvd2 = pcmcia->packetseqnum++; + pseudo.blk.qos_class = 0; + /* Calculate pseudo header checksum */ + pseudo.blk.checksum = pseudo.buff[0]; + for (i = 1; i < 7; i++) + pseudo.blk.checksum ^= pseudo.buff[i]; + + /* Production Mode */ + if (info->AsicID == ELECTRABUZZ_ID) { + /* copy first word to UFIFO_BEG reg */ + ft1000_write_reg(dev, FT1000_REG_UFIFO_BEG, pseudo.buff[0]); + pr_debug("data 0 BEG = 0x%04x\n", pseudo.buff[0]); + + /* copy subsequent words to UFIFO_MID reg */ + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[1]); + pr_debug("data 1 MID = 0x%04x\n", pseudo.buff[1]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[2]); + pr_debug("data 2 MID = 0x%04x\n", pseudo.buff[2]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[3]); + pr_debug("data 3 MID = 0x%04x\n", pseudo.buff[3]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[4]); + pr_debug("data 4 MID = 0x%04x\n", pseudo.buff[4]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[5]); + pr_debug("data 5 MID = 0x%04x\n", pseudo.buff[5]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[6]); + pr_debug("data 6 MID = 0x%04x\n", pseudo.buff[6]); + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[7]); + pr_debug("data 7 MID = 0x%04x\n", pseudo.buff[7]); + + /* Write PPP type + IP Packet into Downlink FIFO */ + for (i = 0; i < (len >> 1) - 1; i++) { + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, + htons(*packet)); + pr_debug("data %d MID = 0x%04x\n", + i + 8, htons(*packet)); + packet++; + } + + /* Check for odd byte */ + if (len & 0x0001) { + ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, + htons(*packet)); + pr_debug("data MID = 0x%04x\n", htons(*packet)); + packet++; + ft1000_write_reg(dev, FT1000_REG_UFIFO_END, + htons(*packet)); + pr_debug("data %d MID = 0x%04x\n", + i + 8, htons(*packet)); + } else { + ft1000_write_reg(dev, FT1000_REG_UFIFO_END, + htons(*packet)); + pr_debug("data %d MID = 0x%04x\n", + i + 8, htons(*packet)); + } + } else { + outl(*(u32 *)&pseudo.buff[0], + dev->base_addr + FT1000_REG_MAG_UFDR); + pr_debug("Pseudo = 0x%8x\n", *(u32 *)&pseudo.buff[0]); + outl(*(u32 *)&pseudo.buff[2], + dev->base_addr + FT1000_REG_MAG_UFDR); + pr_debug("Pseudo = 0x%8x\n", *(u32 *)&pseudo.buff[2]); + outl(*(u32 *)&pseudo.buff[4], + dev->base_addr + FT1000_REG_MAG_UFDR); + pr_debug("Pseudo = 0x%8x\n", *(u32 *)&pseudo.buff[4]); + outl(*(u32 *)&pseudo.buff[6], + dev->base_addr + FT1000_REG_MAG_UFDR); + pr_debug("Pseudo = 0x%8x\n", *(u32 *)&pseudo.buff[6]); + + plong = (u32 *)packet; + /* Write PPP type + IP Packet into Downlink FIFO */ + for (i = 0; i < (len >> 2); i++) + outl(*plong++, dev->base_addr + FT1000_REG_MAG_UFDR); + + /* Check for odd alignment */ + if (len & 0x0003) { + pr_debug("data = 0x%8x\n", *plong); + outl(*plong++, dev->base_addr + FT1000_REG_MAG_UFDR); + } + outl(1, dev->base_addr + FT1000_REG_MAG_UFER); + } + + info->stats.tx_packets++; + /* Add 14 bytes for MAC address plus ethernet type */ + info->stats.tx_bytes += (len + 14); + return SUCCESS; +} + +static struct net_device_stats *ft1000_stats(struct net_device *dev) +{ + struct ft1000_info *info = netdev_priv(dev); + + return &info->stats; +} + +static int ft1000_open(struct net_device *dev) +{ + ft1000_reset_card(dev); + + /* schedule ft1000_hbchk to perform periodic heartbeat checks on DSP + * and ASIC */ + init_timer(&poll_timer); + poll_timer.expires = jiffies + (2 * HZ); + poll_timer.data = (u_long)dev; + add_timer(&poll_timer); + + return 0; +} + +static int ft1000_close(struct net_device *dev) +{ + struct ft1000_info *info = netdev_priv(dev); + + info->CardReady = 0; + del_timer(&poll_timer); + + if (ft1000_card_present == 1) { + pr_debug("Media is down\n"); + netif_stop_queue(dev); + + ft1000_disable_interrupts(dev); + ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT); + + /* reset ASIC */ + ft1000_reset_asic(dev); + } + return 0; +} + +static int ft1000_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ft1000_info *info = netdev_priv(dev); + u8 *pdata; + + if (skb == NULL) { + pr_debug("skb == NULL!!!\n"); + return 0; + } + + pr_debug("length of packet = %d\n", skb->len); + + pdata = (u8 *)skb->data; + + if (info->mediastate == 0) { + /* Drop packet is mediastate is down */ + pr_debug("mediastate is down\n"); + return SUCCESS; + } + + if ((skb->len < ENET_HEADER_SIZE) || (skb->len > ENET_MAX_SIZE)) { + /* Drop packet which has invalid size */ + pr_debug("invalid ethernet length\n"); + return SUCCESS; + } + ft1000_copy_down_pkt(dev, (u16 *) (pdata + ENET_HEADER_SIZE - 2), + skb->len - ENET_HEADER_SIZE + 2); + + dev_kfree_skb(skb); + + return 0; +} + +static irqreturn_t ft1000_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct ft1000_info *info = netdev_priv(dev); + u16 tempword; + u16 inttype; + int cnt; + + if (info->CardReady == 0) { + ft1000_disable_interrupts(dev); + return IRQ_HANDLED; + } + + if (ft1000_chkcard(dev) == false) { + ft1000_disable_interrupts(dev); + return IRQ_HANDLED; + } + + ft1000_disable_interrupts(dev); + + /* Read interrupt type */ + inttype = ft1000_read_reg(dev, FT1000_REG_SUP_ISR); + + /* Make sure we process all interrupt before leaving the ISR due to the + * edge trigger interrupt type */ + while (inttype) { + if (inttype & ISR_DOORBELL_PEND) + ft1000_parse_dpram_msg(dev); + + if (inttype & ISR_RCV) { + pr_debug("Data in FIFO\n"); + + cnt = 0; + do { + /* Check if we have packets in the Downlink + * FIFO */ + if (info->AsicID == ELECTRABUZZ_ID) { + tempword = ft1000_read_reg(dev, + FT1000_REG_DFIFO_STAT); + } else { + tempword = ft1000_read_reg(dev, + FT1000_REG_MAG_DFSR); + } + if (!(tempword & 0x1f)) + break; + ft1000_copy_up_pkt(dev); + cnt++; + } while (cnt < MAX_RCV_LOOP); + + } + /* clear interrupts */ + tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR); + pr_debug("interrupt status register = 0x%x\n", tempword); + ft1000_write_reg(dev, FT1000_REG_SUP_ISR, tempword); + + /* Read interrupt type */ + inttype = ft1000_read_reg(dev, FT1000_REG_SUP_ISR); + pr_debug("interrupt status register after clear = 0x%x\n", + inttype); + } + ft1000_enable_interrupts(dev); + return IRQ_HANDLED; +} + +void stop_ft1000_card(struct net_device *dev) +{ + struct ft1000_info *info = netdev_priv(dev); + struct prov_record *ptr; + struct prov_record *tmp; + /* int cnt; */ + + info->CardReady = 0; + ft1000_card_present = 0; + netif_stop_queue(dev); + ft1000_disable_interrupts(dev); + + /* Make sure we free any memory reserve for provisioning */ + list_for_each_entry_safe(ptr, tmp, &info->prov_list, list) { + list_del(&ptr->list); + kfree(ptr->pprov_data); + kfree(ptr); + } + + kfree(info->priv); + + if (info->registered) { + unregister_netdev(dev); + info->registered = 0; + } + + free_irq(dev->irq, dev); + release_region(dev->base_addr, 256); + release_firmware(fw_entry); + flarion_ft1000_cnt--; + +} + +static void ft1000_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct ft1000_info *ft_info; + + ft_info = netdev_priv(dev); + + strlcpy(info->driver, "ft1000", sizeof(info->driver)); + snprintf(info->bus_info, sizeof(info->bus_info), "PCMCIA 0x%lx", + dev->base_addr); + snprintf(info->fw_version, sizeof(info->fw_version), "%d.%d.%d.%d", + ft_info->DspVer[0], ft_info->DspVer[1], ft_info->DspVer[2], + ft_info->DspVer[3]); +} + +static u32 ft1000_get_link(struct net_device *dev) +{ + struct ft1000_info *info; + + info = netdev_priv(dev); + return info->mediastate; +} + +static const struct ethtool_ops ops = { + .get_drvinfo = ft1000_get_drvinfo, + .get_link = ft1000_get_link +}; + +struct net_device *init_ft1000_card(struct pcmcia_device *link, + void *ft1000_reset) +{ + struct ft1000_info *info; + struct ft1000_pcmcia *pcmcia; + struct net_device *dev; + + static const struct net_device_ops ft1000ops = { + .ndo_open = &ft1000_open, + .ndo_stop = &ft1000_close, + .ndo_start_xmit = &ft1000_start_xmit, + .ndo_get_stats = &ft1000_stats, + }; + + pr_debug("irq = %d, port = 0x%04llx\n", + link->irq, (unsigned long long)link->resource[0]->start); + + flarion_ft1000_cnt++; + + if (flarion_ft1000_cnt > 1) { + flarion_ft1000_cnt--; + + dev_info(&link->dev, + "This driver can not support more than one instance\n"); + return NULL; + } + + dev = alloc_etherdev(sizeof(struct ft1000_info)); + if (!dev) { + dev_err(&link->dev, "Failed to allocate etherdev\n"); + return NULL; + } + + SET_NETDEV_DEV(dev, &link->dev); + info = netdev_priv(dev); + + memset(info, 0, sizeof(struct ft1000_info)); + + pr_debug("address of dev = 0x%p\n", dev); + pr_debug("address of dev info = 0x%p\n", info); + pr_debug("device name = %s\n", dev->name); + + memset(&info->stats, 0, sizeof(struct net_device_stats)); + + info->priv = kzalloc(sizeof(struct ft1000_pcmcia), GFP_KERNEL); + pcmcia = info->priv; + pcmcia->link = link; + + spin_lock_init(&info->dpram_lock); + info->DrvErrNum = 0; + info->registered = 1; + info->ft1000_reset = ft1000_reset; + info->mediastate = 0; + info->fifo_cnt = 0; + info->CardReady = 0; + info->DSP_TIME[0] = 0; + info->DSP_TIME[1] = 0; + info->DSP_TIME[2] = 0; + info->DSP_TIME[3] = 0; + flarion_ft1000_cnt = 0; + + INIT_LIST_HEAD(&info->prov_list); + + info->squeseqnum = 0; + + /* dev->hard_start_xmit = &ft1000_start_xmit; */ + /* dev->get_stats = &ft1000_stats; */ + /* dev->open = &ft1000_open; */ + /* dev->stop = &ft1000_close; */ + + dev->netdev_ops = &ft1000ops; + + pr_debug("device name = %s\n", dev->name); + + dev->irq = link->irq; + dev->base_addr = link->resource[0]->start; + if (pcmcia_get_mac_from_cis(link, dev)) { + netdev_err(dev, "Could not read mac address\n"); + goto err_dev; + } + + if (request_irq(dev->irq, ft1000_interrupt, IRQF_SHARED, dev->name, + dev)) { + netdev_err(dev, "Could not request_irq\n"); + goto err_dev; + } + + if (request_region(dev->base_addr, 256, dev->name) == NULL) { + netdev_err(dev, "Could not request_region\n"); + goto err_irq; + } + + if (register_netdev(dev)) { + pr_debug("Could not register netdev\n"); + goto err_reg; + } + + info->AsicID = ft1000_read_reg(dev, FT1000_REG_ASIC_ID); + if (info->AsicID == ELECTRABUZZ_ID) { + pr_debug("ELECTRABUZZ ASIC\n"); + if (request_firmware(&fw_entry, "ft1000.img", + &link->dev) != 0) { + pr_info("Could not open ft1000.img\n"); + goto err_unreg; + } + } else { + pr_debug("MAGNEMITE ASIC\n"); + if (request_firmware(&fw_entry, "ft2000.img", + &link->dev) != 0) { + pr_info("Could not open ft2000.img\n"); + goto err_unreg; + } + } + + ft1000_enable_interrupts(dev); + + ft1000_card_present = 1; + dev->ethtool_ops = &ops; + pr_info("%s: addr 0x%04lx irq %d, MAC addr %pM\n", + dev->name, dev->base_addr, dev->irq, dev->dev_addr); + return dev; + +err_unreg: + unregister_netdev(dev); +err_reg: + release_region(dev->base_addr, 256); +err_irq: + free_irq(dev->irq, dev); +err_dev: + free_netdev(dev); + return NULL; +} -- cgit 1.2.3-korg