/*--------------------------------------------------------------------------- FT1000 driver for Flarion Flash OFDM NIC Device Copyright (C) 2002 Flarion Technologies, All rights reserved. Copyright (C) 2006 Patrik Ostrihon, All rights reserved. Copyright (C) 2006 ProWeb Consulting, a.s, All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -----------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef FT_DEBUG #define DEBUG(n, args...) printk(KERN_DEBUG args); #else #define DEBUG(n, args...) #endif #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 = 0; static u8 flarion_ft1000_cnt = 0; static irqreturn_t ft1000_interrupt(int irq, void *dev_id); static void ft1000_enable_interrupts(struct net_device *dev); static void ft1000_disable_interrupts(struct net_device *dev); /* new kernel */ MODULE_AUTHOR(""); MODULE_DESCRIPTION ("Support for Flarion Flash OFDM NIC Device. Support for PCMCIA when used with ft1000_cs."); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("FT1000"); #define MAX_RCV_LOOP 100 //--------------------------------------------------------------------------- // // Function: ft1000_read_fifo_len // Description: This function will read the ASIC Uplink FIFO status register // which will return the number of bytes remaining in the Uplink FIFO. // Sixteen bytes are subtracted to make sure that the ASIC does not // reach its threshold. // Input: // dev - network device structure // Output: // value - number of bytes available in the ASIC Uplink FIFO. // //--------------------------------------------------------------------------- static inline u16 ft1000_read_fifo_len(struct net_device *dev) { struct ft1000_info *info = netdev_priv(dev); if (info->AsicID == ELECTRABUZZ_ID) { return (ft1000_read_reg(dev, FT1000_REG_UFIFO_STAT) - 16); } else { return (ft1000_read_reg(dev, FT1000_REG_MAG_UFSR) - 16); } } //--------------------------------------------------------------------------- // // Function: ft1000_read_dpram // Description: This function will read the specific area of dpram // (Electrabuzz ASIC only) // Input: // dev - device structure // offset - index of dpram // Output: // value - value of dpram // //--------------------------------------------------------------------------- u16 ft1000_read_dpram(struct net_device * dev, int offset) { struct ft1000_info *info = netdev_priv(dev); unsigned long flags; u16 data; // Provide mutual exclusive access while reading ASIC registers. spin_lock_irqsave(&info->dpram_lock, flags); ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); data = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); spin_unlock_irqrestore(&info->dpram_lock, flags); return (data); } //--------------------------------------------------------------------------- // // Function: ft1000_write_dpram // Description: This function will write to a specific area of dpram // (Electrabuzz ASIC only) // Input: // dev - device structure // offset - index of dpram // value - value to write // Output: // none. // //--------------------------------------------------------------------------- static inline void ft1000_write_dpram(struct net_device *dev, int offset, u16 value) { struct ft1000_info *info = netdev_priv(dev); unsigned long flags; // Provide mutual exclusive access while reading ASIC registers. spin_lock_irqsave(&info->dpram_lock, flags); ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, value); spin_unlock_irqrestore(&info->dpram_lock, flags); } //--------------------------------------------------------------------------- // // Function: ft1000_read_dpram_mag_16 // Description: This function will read the specific area of dpram // (Magnemite ASIC only) // Input: // dev - device structure // offset - index of dpram // Output: // value - value of dpram // //--------------------------------------------------------------------------- u16 ft1000_read_dpram_mag_16(struct net_device *dev, int offset, int Index) { struct ft1000_info *info = netdev_priv(dev); unsigned long flags; u16 data; // Provide mutual exclusive access while reading ASIC registers. spin_lock_irqsave(&info->dpram_lock, flags); ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); // check if we want to read upper or lower 32-bit word if (Index) { data = ft1000_read_reg(dev, FT1000_REG_MAG_DPDATAL); } else { data = ft1000_read_reg(dev, FT1000_REG_MAG_DPDATAH); } spin_unlock_irqrestore(&info->dpram_lock, flags); return (data); } //--------------------------------------------------------------------------- // // Function: ft1000_write_dpram_mag_16 // Description: This function will write to a specific area of dpram // (Magnemite ASIC only) // Input: // dev - device structure // offset - index of dpram // value - value to write // Output: // none. // //--------------------------------------------------------------------------- static inline void ft1000_write_dpram_mag_16(struct net_device *dev, int offset, u16 value, int Index) { struct ft1000_info *info = netdev_priv(dev); unsigned long flags; // Provide mutual exclusive access while reading ASIC registers. spin_lock_irqsave(&info->dpram_lock, flags); ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); if (Index) { ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAL, value); } else { ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAH, value); } spin_unlock_irqrestore(&info->dpram_lock, flags); } //--------------------------------------------------------------------------- // // Function: ft1000_read_dpram_mag_32 // Description: This function will read the specific area of dpram // (Magnemite ASIC only) // Input: // dev - device structure // offset - index of dpram // Output: // value - value of dpram // //--------------------------------------------------------------------------- u32 ft1000_read_dpram_mag_32(struct net_device *dev, int offset) { struct ft1000_info *info = netdev_priv(dev); unsigned long flags; u32 data; // Provide mutual exclusive access while reading ASIC registers. spin_lock_irqsave(&info->dpram_lock, flags); ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); data = inl(dev->base_addr + FT1000_REG_MAG_DPDATAL); spin_unlock_irqrestore(&info->dpram_lock, flags); return (data); } //--------------------------------------------------------------------------- // // Function: ft1000_write_dpram_mag_32 // Description: This function will write to a specific area of dpram // (Magnemite ASIC only) // Input: // dev - device structure // offset - index of dpram // value - value to write // Output: // none. // //--------------------------------------------------------------------------- void ft1000_write_dpram_mag_32(struct net_device *dev, int offset, u32 value) { struct ft1000_info *info = netdev_priv(dev); unsigned long flags; // Provide mutual exclusive access while reading ASIC registers. spin_lock_irqsave(&info->dpram_lock, flags); ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset); outl(value, dev->base_addr + FT1000_REG_MAG_DPDATAL); spin_unlock_irqrestore(&info->dpram_lock, flags); } //--------------------------------------------------------------------------- // // Function: ft1000_enable_interrupts // Description: This function will enable interrupts base on the current interrupt mask. // Input: // dev - device structure // Output: // None. // //--------------------------------------------------------------------------- static void ft1000_enable_interrupts(struct net_device *dev) { u16 tempword; DEBUG(1, "ft1000_hw:ft1000_enable_interrupts()\n"); ft1000_write_reg(dev, FT1000_REG_SUP_IMASK, ISR_DEFAULT_MASK); tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK); DEBUG(1, "ft1000_hw:ft1000_enable_interrupts:current interrupt enable mask = 0x%x\n", tempword); } //--------------------------------------------------------------------------- // // Function: ft1000_disable_interrupts // Description: This function will disable all interrupts. // Input: // dev - device structure // Output: // None. // //--------------------------------------------------------------------------- static void ft1000_disable_interrupts(struct net_device *dev) { u16 tempword; DEBUG(1, "ft1000_hw: ft1000_disable_interrupts()\n"); ft1000_write_reg(dev, FT1000_REG_SUP_IMASK, ISR_MASK_ALL); tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK); DEBUG(1, "ft1000_hw:ft1000_disable_interrupts:current interrupt enable mask = 0x%x\n", tempword); } //--------------------------------------------------------------------------- // // Function: ft1000_reset_asic // Description: This function will call the Card Service function to reset the // ASIC. // Input: // dev - device structure // Output: // none // //--------------------------------------------------------------------------- static void ft1000_reset_asic(struct net_device *dev) { struct ft1000_info *info = netdev_priv(dev); u16 tempword; DEBUG(1, "ft1000_hw:ft1000_reset_asic called\n"); (*info->ft1000_reset) (info->link); // Let's use the register provided by the Magnemite ASIC to reset the // ASIC and DSP. if (info->AsicID == MAGNEMITE_ID) { ft1000_write_reg(dev, FT1000_REG_RESET, (DSP_RESET_BIT | ASIC_RESET_BIT)); } mdelay(1); if (info->AsicID == ELECTRABUZZ_ID) { // set watermark to -1 in order to not generate an 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); DEBUG(1, "ft1000_hw: interrupt status register = 0x%x\n", tempword); ft1000_write_reg(dev, FT1000_REG_SUP_ISR, tempword); tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR); DEBUG(1, "ft1000_hw: interrupt status register = 0x%x\n", tempword); } //--------------------------------------------------------------------------- // // Function: ft1000_reset_card // Description: This function will reset the card // Input: // dev - device structure // Output: // status - false (card reset fail) // true (card reset successful) // //--------------------------------------------------------------------------- static int ft1000_reset_card(struct net_device *dev) { struct ft1000_info *info = netdev_priv(dev); u16 tempword; int i; unsigned long flags; struct prov_record *ptr; DEBUG(1, "ft1000_hw:ft1000_reset_card called.....\n"); info->CardReady = 0; info->ProgConStat = 0; info->squeseqnum = 0; ft1000_disable_interrupts(dev); // del_timer(&poll_timer); // Make sure we free any memory reserve for provisioning while (list_empty(&info->prov_list) == 0) { DEBUG(0, "ft1000_hw:ft1000_reset_card:deleting provisioning record\n"); ptr = list_entry(info->prov_list.next, struct prov_record, list); list_del(&ptr->list); kfree(ptr->pprov_data); kfree(ptr); } if (info->AsicID == ELECTRABUZZ_ID) { DEBUG(1, "ft1000_hw:ft1000_reset_card:resetting DSP\n"); ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT); } else { DEBUG(1, "ft1000_hw:ft1000_reset_card:resetting ASIC and DSP\n"); ft1000_write_reg(dev, FT1000_REG_RESET, (DSP_RESET_BIT | ASIC_RESET_BIT)); } // Copy DSP session record into info block if this is not a coldstart if (ft1000_card_present == 1) { spin_lock_irqsave(&info->dpram_lock, flags); if (info->AsicID == ELECTRABUZZ_ID) { ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_RX_BASE); for (i = 0; i < MAX_DSP_SESS_REC; i++) { info->DSPSess.Rec[i] = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); } } else { ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_MAG_RX_BASE); for (i = 0; i < MAX_DSP_SESS_REC / 2; i++) { info->DSPSess.MagRec[i] = inl(dev->base_addr + FT1000_REG_MAG_DPDATA); } } spin_unlock_irqrestore(&info->dpram_lock, flags); } DEBUG(1, "ft1000_hw:ft1000_reset_card:resetting ASIC\n"); mdelay(10); //reset ASIC ft1000_reset_asic(dev); DEBUG(1, "ft1000_hw:ft1000_reset_card:downloading dsp image\n"); if (info->AsicID == MAGNEMITE_ID) { // Put dsp in reset and take ASIC out of reset DEBUG(0, "ft1000_hw:ft1000_reset_card:Put DSP in reset and take ASIC out of reset\n"); ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT); // Setting MAGNEMITE ASIC to big endian mode ft1000_write_reg(dev, FT1000_REG_SUP_CTRL, HOST_INTF_BE); // Download bootloader card_bootload(dev); // Take DSP out of reset ft1000_write_reg(dev, FT1000_REG_RESET, 0); // FLARION_DSP_ACTIVE; mdelay(10); DEBUG(0, "ft1000_hw:ft1000_reset_card:Take DSP out of reset\n"); // Wait for 0xfefe indicating dsp ready before starting download for (i = 0; i < 50; i++) { tempword = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DPRAM_FEFE, FT1000_MAG_DPRAM_FEFE_INDX); if (tempword == 0xfefe) { break; } mdelay(20); } if (i == 50) { DEBUG(0, "ft1000_hw:ft1000_reset_card:No FEFE detected from DSP\n"); return false; } } else { // Take DSP out of reset ft1000_write_reg(dev, FT1000_REG_RESET, ~DSP_RESET_BIT); mdelay(10); } if (card_download(dev, fw_entry->data, fw_entry->size)) { DEBUG(1, "card download unsuccessful\n"); return false; } else { DEBUG(1, "card download successful\n"); } mdelay(10); if (info->AsicID == ELECTRABUZZ_ID) { // Need to initialize the FIFO length counter to zero in order to sync up // with the DSP info->fifo_cnt = 0; ft1000_write_dpram(dev, FT1000_FIFO_LEN, info->fifo_cnt); // Initialize DSP heartbeat area to ho ft1000_write_dpram(dev, FT1000_HI_HO, ho); tempword = ft1000_read_dpram(dev, FT1000_HI_HO); DEBUG(1, "ft1000_hw:ft1000_reset_asic:hi_ho value = 0x%x\n", tempword); } else { // Initialize DSP heartbeat area to ho ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, ho_mag, FT1000_MAG_HI_HO_INDX); tempword = ft1000_read_dpram_mag_16(dev, FT1000_MAG_HI_HO, FT1000_MAG_HI_HO_INDX); DEBUG(1, "ft1000_hw:ft1000_reset_card:hi_ho value = 0x%x\n", tempword); } info->CardReady = 1; ft1000_enable_interrupts(dev); /* Schedule heartbeat process to run every 2 seconds */ // poll_timer.expires = jiffies + (2*HZ); // poll_timer.data = (u_long)dev; // add_timer(&poll_timer); return true; } //--------------------------------------------------------------------------- // // Function: ft1000_chkcard // Description: This function will check if the device is presently available on // the system. // Input: // dev - device structure // Output: // status - false (device is not present) // true (device is present) // //--------------------------------------------------------------------------- static int ft1000_chkcard(struct net_device *dev) { u16 tempword; // Mask register is used to check for device presence since it is never // set to zero. tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK); if (tempword == 0) { DEBUG(1, "ft1000_hw:ft1000_chkcard: IMASK = 0 Card not detected\n"); return false; } // The system will return the value of 0xffff for the version register // if the device is not present. tempword = ft1000_read_reg(dev, FT1000_REG_ASIC_ID); if (tempword == 0xffff) { DEBUG(1, "ft1000_hw:ft1000_chkcard: Version = 0xffff Card not detected\n"); return false; } return true; } //--------------------------------------------------------------------------- // // Function: ft1000_hbchk // Description: This function will perform the heart beat check of the DSP as // well as the ASIC. // Input: // dev - device structure // Output: // none // //--------------------------------------------------------------------------- static void ft1000_hbchk(u_long data) { struct net_device *dev = (struct net_device *)data; struct ft1000_info *info; u16 tempword; info = netdev_priv(dev); if (info->CardReady == 1) { // Perform dsp heartbeat check if (info->AsicID == ELECTRABUZZ_ID) { tempword = ft1000_read_dpram(dev, FT1000_HI_HO); } else { tempword = ntohs(ft1000_read_dpram_mag_16 (dev, FT1000_MAG_HI_HO, FT1000_MAG_HI_HO_INDX)); } DEBUG(1, "ft1000_hw:ft1000_hbchk:hi_ho value = 0x%x\n", tempword); // Let's perform another check if ho is not detected if (tempword != ho) { if (info->AsicID == ELECTRABUZZ_ID) { tempword = ft1000_read_dpram(dev, FT1000_HI_HO); } else { tempword = ntohs(ft1000_read_dpram_mag_16(dev, FT1000_MAG_HI_HO, FT1000_MAG_HI_HO_INDX)); } } if (tempword != ho) { printk(KERN_INFO "ft1000: heartbeat failed - no ho detected\n"); if (info->AsicID == ELECTRABUZZ_ID) { info->DSP_TIME[0] = ft1000_read_dpram(dev, FT1000_DSP_TIMER0); info->DSP_TIME[1] = ft1000_read_dpram(dev, FT1000_DSP_TIMER1); info->DSP_TIME[2] = ft1000_read_dpram(dev, FT1000_DSP_TIMER2); info->DSP_TIME[3] = ft1000_read_dpram(dev, FT1000_DSP_TIMER3); } else { info->DSP_TIME[0] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER0, FT1000_MAG_DSP_TIMER0_INDX); info->DSP_TIME[1] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER1, FT1000_MAG_DSP_TIMER1_INDX); info->DSP_TIME[2] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER2, FT1000_MAG_DSP_TIMER2_INDX); info->DSP_TIME[3] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER3, FT1000_MAG_DSP_TIMER3_INDX); } info->DrvErrNum = DSP_HB_INFO; if (ft1000_reset_card(dev) == 0) { printk(KERN_INFO "ft1000: Hardware Failure Detected - PC Card disabled\n"); info->ProgConStat = 0xff; return; } /* Schedule this module to run every 2 seconds */ poll_timer.expires = jiffies + (2*HZ); poll_timer.data = (u_long)dev; add_timer(&poll_timer); return; } tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); // Let's check doorbell again if fail if (tempword & FT1000_DB_HB) { tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); } if (tempword & FT1000_DB_HB) { printk(KERN_INFO "ft1000: heartbeat doorbell not clear by firmware\n"); if (info->AsicID == ELECTRABUZZ_ID) { info->DSP_TIME[0] = ft1000_read_dpram(dev, FT1000_DSP_TIMER0); info->DSP_TIME[1] = ft1000_read_dpram(dev, FT1000_DSP_TIMER1); info->DSP_TIME[2] = ft1000_read_dpram(dev, FT1000_DSP_TIMER2); info->DSP_TIME[3] = ft1000_read_dpram(dev, FT1000_DSP_TIMER3); } else { info->DSP_TIME[0] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER0, FT1000_MAG_DSP_TIMER0_INDX); info->DSP_TIME[1] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER1, FT1000_MAG_DSP_TIMER1_INDX); info->DSP_TIME[2] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER2, FT1000_MAG_DSP_TIMER2_INDX); info->DSP_TIME[3] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER3, FT1000_MAG_DSP_TIMER3_INDX); } info->DrvErrNum = DSP_HB_INFO; if (ft1000_reset_card(dev) == 0) { printk(KERN_INFO "ft1000: Hardware Failure Detected - PC Card disabled\n"); info->ProgConStat = 0xff; return; } /* Schedule this module to run every 2 seconds */ poll_timer.expires = jiffies + (2*HZ); poll_timer.data = (u_long)dev; add_timer(&poll_timer); return; } // Set dedicated area to hi and ring appropriate doorbell according // to hi/ho heartbeat protocol if (info->AsicID == ELECTRABUZZ_ID) { ft1000_write_dpram(dev, FT1000_HI_HO, hi); } else { ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, hi_mag, FT1000_MAG_HI_HO_INDX); } if (info->AsicID == ELECTRABUZZ_ID) { tempword = ft1000_read_dpram(dev, FT1000_HI_HO); } else { tempword = ntohs(ft1000_read_dpram_mag_16 (dev, FT1000_MAG_HI_HO, FT1000_MAG_HI_HO_INDX)); } // Let's write hi again if fail if (tempword != hi) { if (info->AsicID == ELECTRABUZZ_ID) { ft1000_write_dpram(dev, FT1000_HI_HO, hi); } else { ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, hi_mag, FT1000_MAG_HI_HO_INDX); } if (info->AsicID == ELECTRABUZZ_ID) { tempword = ft1000_read_dpram(dev, FT1000_HI_HO); } else { tempword = ntohs(ft1000_read_dpram_mag_16(dev, FT1000_MAG_HI_HO, FT1000_MAG_HI_HO_INDX)); } } if (tempword != hi) { printk(KERN_INFO "ft1000: heartbeat failed - cannot write hi into DPRAM\n"); if (info->AsicID == ELECTRABUZZ_ID) { info->DSP_TIME[0] = ft1000_read_dpram(dev, FT1000_DSP_TIMER0); info->DSP_TIME[1] = ft1000_read_dpram(dev, FT1000_DSP_TIMER1); info->DSP_TIME[2] = ft1000_read_dpram(dev, FT1000_DSP_TIMER2); info->DSP_TIME[3] = ft1000_read_dpram(dev, FT1000_DSP_TIMER3); } else { info->DSP_TIME[0] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER0, FT1000_MAG_DSP_TIMER0_INDX); info->DSP_TIME[1] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER1, FT1000_MAG_DSP_TIMER1_INDX); info->DSP_TIME[2] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER2, FT1000_MAG_DSP_TIMER2_INDX); info->DSP_TIME[3] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER3, FT1000_MAG_DSP_TIMER3_INDX); } info->DrvErrNum = DSP_HB_INFO; if (ft1000_reset_card(dev) == 0) { printk(KERN_INFO "ft1000: Hardware Failure Detected - PC Card disabled\n"); info->ProgConStat = 0xff; return; } /* Schedule this module to run every 2 seconds */ poll_timer.expires = jiffies + (2*HZ); poll_timer.data = (u_long)dev; add_timer(&poll_timer); return; } ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_HB); } /* Schedule this module to run every 2 seconds */ poll_timer.expires = jiffies + (2 * HZ); poll_timer.data = (u_long) dev; add_timer(&poll_timer); } //--------------------------------------------------------------------------- // // Function: ft1000_send_cmd // Description: // Input: // Output: // //--------------------------------------------------------------------------- static void ft1000_send_cmd (struct net_device *dev, u16 *ptempbuffer, int size, u16 qtype) { struct ft1000_info *info = netdev_priv(dev); int i; u16 tempword; unsigned long flags; size += sizeof(struct pseudo_hdr); // check for odd byte and increment to 16-bit word align value if ((size & 0x0001)) { size++; } DEBUG(1, "FT1000:ft1000_send_cmd:total length = %d\n", size); DEBUG(1, "FT1000:ft1000_send_cmd:length = %d\n", ntohs(*ptempbuffer)); // put message into slow queue area // All messages are in the form total_len + pseudo header + message body spin_lock_irqsave(&info->dpram_lock, flags); // Make sure SLOWQ doorbell is clear tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); i=0; while (tempword & FT1000_DB_DPRAM_TX) { mdelay(10); i++; if (i==10) { spin_unlock_irqrestore(&info->dpram_lock, flags); return; } tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); } if (info->AsicID == ELECTRABUZZ_ID) { ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_TX_BASE); // Write total length to dpram ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size); // Write pseudo header and messgae body for (i = 0; i < (size >> 1); i++) { DEBUG(1, "FT1000:ft1000_send_cmd:data %d = 0x%x\n", i, *ptempbuffer); tempword = htons(*ptempbuffer++); ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, tempword); } } else { ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_MAG_TX_BASE); // Write total length to dpram ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAH, htons(size)); // Write pseudo header and messgae body ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_MAG_TX_BASE + 1); for (i = 0; i < (size >> 2); i++) { DEBUG(1, "FT1000:ft1000_send_cmd:data = 0x%x\n", *ptempbuffer); outw(*ptempbuffer++, dev->base_addr + FT1000_REG_MAG_DPDATAL); DEBUG(1, "FT1000:ft1000_send_cmd:data = 0x%x\n", *ptempbuffer); outw(*ptempbuffer++, dev->base_addr + FT1000_REG_MAG_DPDATAH); } DEBUG(1, "FT1000:ft1000_send_cmd:data = 0x%x\n", *ptempbuffer); outw(*ptempbuffer++, dev->base_addr + FT1000_REG_MAG_DPDATAL); DEBUG(1, "FT1000:ft1000_send_cmd:data = 0x%x\n", *ptempbuffer); outw(*ptempbuffer++, dev->base_addr + FT1000_REG_MAG_DPDATAH); } spin_unlock_irqrestore(&info->dpram_lock, flags); // ring doorbell to notify DSP that we have a message ready ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_DPRAM_TX); } //--------------------------------------------------------------------------- // // Function: ft1000_receive_cmd // Description: This function will read a message from the dpram area. // Input: // dev - network device structure // pbuffer - caller supply address to buffer // pnxtph - pointer to next pseudo header // Output: // Status = 0 (unsuccessful) // = 1 (successful) // //--------------------------------------------------------------------------- static bool ft1000_receive_cmd(struct net_device *dev, u16 *pbuffer, int maxsz, u16 *pnxtph) { struct ft1000_info *info = netdev_priv(dev); u16 size; u16 *ppseudohdr; int i; u16 tempword; unsigned long flags; if (info->AsicID == ELECTRABUZZ_ID) { size = ( ft1000_read_dpram(dev, *pnxtph) ) + sizeof(struct pseudo_hdr); } else { size = ntohs(ft1000_read_dpram_mag_16 (dev, FT1000_MAG_PH_LEN, FT1000_MAG_PH_LEN_INDX)) + sizeof(struct pseudo_hdr); } if (size > maxsz) { DEBUG(1, "FT1000:ft1000_receive_cmd:Invalid command length = %d\n", size); return false; } else { ppseudohdr = (u16 *) pbuffer; spin_lock_irqsave(&info->dpram_lock, flags); if (info->AsicID == ELECTRABUZZ_ID) { ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_RX_BASE + 2); for (i = 0; i <= (size >> 1); i++) { tempword = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); *pbuffer++ = ntohs(tempword); } } else { ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_MAG_RX_BASE); *pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAH); DEBUG(1, "ft1000_hw:received data = 0x%x\n", *pbuffer); pbuffer++; ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_MAG_RX_BASE + 1); for (i = 0; i <= (size >> 2); i++) { *pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAL); pbuffer++; *pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAH); pbuffer++; } //copy odd aligned word *pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAL); DEBUG(1, "ft1000_hw:received data = 0x%x\n", *pbuffer); pbuffer++; *pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAH); DEBUG(1, "ft1000_hw:received data = 0x%x\n", *pbuffer); pbuffer++; } if (size & 0x0001) { //copy odd byte from fifo tempword = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); *pbuffer = ntohs(tempword); } spin_unlock_irqrestore(&info->dpram_lock, flags); // Check if pseudo header checksum is good // Calculate pseudo header checksum tempword = *ppseudohdr++; for (i = 1; i < 7; i++) { tempword ^= *ppseudohdr++; } if ((tempword != *ppseudohdr)) { DEBUG(1, "FT1000:ft1000_receive_cmd:Pseudo header checksum mismatch\n"); // Drop this message return false; } return true; } } //--------------------------------------------------------------------------- // // Function: ft1000_proc_drvmsg // Description: This function will process the various driver messages. // Input: // dev - device structure // pnxtph - pointer to next pseudo header // Output: // none // //--------------------------------------------------------------------------- static void ft1000_proc_drvmsg(struct net_device *dev) { struct ft1000_info *info = netdev_priv(dev); u16 msgtype; u16 tempword; struct media_msg *pmediamsg; struct dsp_init_msg *pdspinitmsg; struct drv_msg *pdrvmsg; u16 len; u16 i; struct prov_record *ptr; struct pseudo_hdr *ppseudo_hdr; u16 *pmsg; struct timeval tv; union { u8 byte[2]; u16 wrd; } convert; if (info->AsicID == ELECTRABUZZ_ID) { tempword = FT1000_DPRAM_RX_BASE+2; } else { tempword = FT1000_DPRAM_MAG_RX_BASE; } if ( ft1000_receive_cmd(dev, &cmdbuffer[0], MAX_CMD_SQSIZE, &tempword) ) { // Get the message type which is total_len + PSEUDO header + msgtype + message body pdrvmsg = (struct drv_msg *) & cmdbuffer[0]; msgtype = ntohs(pdrvmsg->type); DEBUG(1, "Command message type = 0x%x\n", msgtype); switch (msgtype) { case DSP_PROVISION: DEBUG(0, "Got a provisioning request message from DSP\n"); mdelay(25); while (list_empty(&info->prov_list) == 0) { DEBUG(0, "Sending a provisioning message\n"); // Make sure SLOWQ doorbell is clear tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); i = 0; while (tempword & FT1000_DB_DPRAM_TX) { mdelay(5); i++; if (i == 10) { break; } } ptr = list_entry(info->prov_list.next, struct prov_record, list); len = *(u16 *) ptr->pprov_data; len = htons(len); pmsg = (u16 *) ptr->pprov_data; ppseudo_hdr = (struct pseudo_hdr *) pmsg; // Insert slow queue sequence number ppseudo_hdr->seq_num = info->squeseqnum++; ppseudo_hdr->portsrc = 0; // Calculate new checksum ppseudo_hdr->checksum = *pmsg++; DEBUG(1, "checksum = 0x%x\n", ppseudo_hdr->checksum); for (i = 1; i < 7; i++) { ppseudo_hdr->checksum ^= *pmsg++; DEBUG(1, "checksum = 0x%x\n", ppseudo_hdr->checksum); } ft1000_send_cmd (dev, (u16 *)ptr->pprov_data, len, SLOWQ_TYPE); list_del(&ptr->list); kfree(ptr->pprov_data); kfree(ptr); } // Indicate adapter is ready to take application messages after all // provisioning messages are sent info->CardReady = 1; break; case MEDIA_STATE: pmediamsg = (struct media_msg *) & cmdbuffer[0]; if (info->ProgConStat != 0xFF) { if (pmediamsg->state) { DEBUG(1, "Media is up\n"); if (info->mediastate == 0) { netif_carrier_on(dev); netif_wake_queue(dev); info->mediastate = 1; do_gettimeofday(&tv); info->ConTm = tv.tv_sec; } } else { DEBUG(1, "Media is down\n"); if (info->mediastate == 1) { info->mediastate = 0; netif_carrier_off(dev); netif_stop_queue(dev); info->ConTm = 0; } } } else { DEBUG(1,"Media is down\n"); if (info->mediastate == 1) { info->mediastate = 0; netif_carrier_off(dev); netif_stop_queue(dev); info->ConTm = 0; } } break; case DSP_INIT_MSG: pdspinitmsg = (struct dsp_init_msg *) & cmdbuffer[0]; memcpy(info->DspVer, pdspinitmsg->DspVer, DSPVERSZ); DEBUG(1, "DSPVER = 0x%2x 0x%2x 0x%2x 0x%2x\n", info->DspVer[0], info->DspVer[1], info->DspVer[2], info->DspVer[3]); memcpy(info->HwSerNum, pdspinitmsg->HwSerNum, HWSERNUMSZ); memcpy(info->Sku, pdspinitmsg->Sku, SKUSZ); memcpy(info->eui64, pdspinitmsg->eui64, EUISZ); dev->dev_addr[0] = info->eui64[0]; dev->dev_addr[1] = info->eui64[1]; dev->dev_addr[2] = info->eui64[2]; dev->dev_addr[3] = info->eui64[5]; dev->dev_addr[4] = info->eui64[6]; dev->dev_addr[5] = info->eui64[7]; if (ntohs(pdspinitmsg->length) == (sizeof(struct dsp_init_msg) - 20)) { memcpy(info->ProductMode, pdspinitmsg->ProductMode, MODESZ); memcpy(info->RfCalVer, pdspinitmsg->RfCalVer, CALVERSZ); memcpy(info->RfCalDate, pdspinitmsg->RfCalDate, CALDATESZ); DEBUG(1, "RFCalVer = 0x%2x 0x%2x\n", info->RfCalVer[0], info->RfCalVer[1]); } break ; case DSP_STORE_INFO: DEBUG(1, "FT1000:drivermsg:Got DSP_STORE_INFO\n"); tempword = ntohs(pdrvmsg->length); info->DSPInfoBlklen = tempword; if (tempword < (MAX_DSP_SESS_REC - 4)) { pmsg = (u16 *) & pdrvmsg->data[0]; for (i = 0; i < ((tempword + 1) / 2); i++) { DEBUG(1, "FT1000:drivermsg:dsp info data = 0x%x\n", *pmsg); info->DSPInfoBlk[i + 10] = *pmsg++; } } break; case DSP_GET_INFO: DEBUG(1, "FT1000:drivermsg:Got DSP_GET_INFO\n"); // copy dsp info block to dsp // allow any outstanding ioctl to finish mdelay(10); tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); if (tempword & FT1000_DB_DPRAM_TX) { mdelay(10); tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); if (tempword & FT1000_DB_DPRAM_TX) { mdelay(10); } } if ((tempword & FT1000_DB_DPRAM_TX) == 0) { // Put message into Slow Queue // Form Pseudo header pmsg = (u16 *) info->DSPInfoBlk; ppseudo_hdr = (struct pseudo_hdr *) pmsg; ppseudo_hdr->length = htons(info->DSPInfoBlklen + 4); ppseudo_hdr->source = 0x10; ppseudo_hdr->destination = 0x20; ppseudo_hdr->portdest = 0; ppseudo_hdr->portsrc = 0; ppseudo_hdr->sh_str_id = 0; ppseudo_hdr->control = 0; ppseudo_hdr->rsvd1 = 0; ppseudo_hdr->rsvd2 = 0; ppseudo_hdr->qos_class = 0; // Insert slow queue sequence number ppseudo_hdr->seq_num = info->squeseqnum++; // Insert application id ppseudo_hdr->portsrc = 0; // Calculate new checksum ppseudo_hdr->checksum = *pmsg++; for (i = 1; i < 7; i++) { ppseudo_hdr->checksum ^= *pmsg++; } info->DSPInfoBlk[8] = 0x7200; info->DSPInfoBlk[9] = htons(info->DSPInfoBlklen); ft1000_send_cmd (dev, (u16 *)info->DSPInfoBlk, (u16)(info->DSPInfoBlklen+4), 0); } break; case GET_DRV_ERR_RPT_MSG: DEBUG(1, "FT1000:drivermsg:Got GET_DRV_ERR_RPT_MSG\n"); // copy driver error message to dsp // allow any outstanding ioctl to finish mdelay(10); tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); if (tempword & FT1000_DB_DPRAM_TX) { mdelay(10); tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL); if (tempword & FT1000_DB_DPRAM_TX) { mdelay(10); } } if ((tempword & FT1000_DB_DPRAM_TX) == 0) { // Put message into Slow Queue // Form Pseudo header pmsg = (u16 *) & tempbuffer[0]; ppseudo_hdr = (struct pseudo_hdr *) pmsg; ppseudo_hdr->length = htons(0x0012); ppseudo_hdr->source = 0x10; ppseudo_hdr->destination = 0x20; ppseudo_hdr->portdest = 0; ppseudo_hdr->portsrc = 0; ppseudo_hdr->sh_str_id = 0; ppseudo_hdr->control = 0; ppseudo_hdr->rsvd1 = 0; ppseudo_hdr->rsvd2 = 0; ppseudo_hdr->qos_class = 0; // Insert slow queue sequence number ppseudo_hdr->seq_num = info->squeseqnum++; // Insert application id ppseudo_hdr->portsrc = 0; // Calculate new checksum ppseudo_hdr->checksum = *pmsg++; for (i=1; i<7; i++) { ppseudo_hdr->checksum ^= *pmsg++; } pmsg = (u16 *) & tempbuffer[16]; *pmsg++ = htons(RSP_DRV_ERR_RPT_MSG); *pmsg++ = htons(0x000e); *pmsg++ = htons(info->DSP_TIME[0]); *pmsg++ = htons(info->DSP_TIME[1]); *pmsg++ = htons(info->DSP_TIME[2]); *pmsg++ = htons(info->DSP_TIME[3]); convert.byte[0] = info->DspVer[0]; convert.byte[1] = info->DspVer[1]; *pmsg++ = convert.wrd; convert.byte[0] = info->DspVer[2]; convert.byte[1] = info->DspVer[3]; *pmsg++ = convert.wrd; *pmsg++ = htons(info->DrvErrNum); ft1000_send_cmd (dev, (u16 *)&tempbuffer[0], (u16)(0x0012), 0); info->DrvErrNum = 0; } break; default: break; } } } //--------------------------------------------------------------------------- // // Function: ft1000_parse_dpram_msg // Description: This function will parse the message received from the DSP // via the DPRAM interface. // Input: // dev - device structure // Output: // status - FAILURE // SUCCESS // //--------------------------------------------------------------------------- static int ft1000_parse_dpram_msg(struct net_device *dev) { struct ft1000_info *info = netdev_priv(dev); u16 doorbell; u16 portid; u16 nxtph; u16 total_len; int i = 0; int cnt; unsigned long flags; doorbell = ft1000_read_reg(dev, FT1000_REG_DOORBELL); DEBUG(1, "Doorbell = 0x%x\n", doorbell); if (doorbell & FT1000_ASIC_RESET_REQ) { // Copy DSP session record from info block spin_lock_irqsave(&info->dpram_lock, flags); if (info->AsicID == ELECTRABUZZ_ID) { ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_RX_BASE); for (i = 0; i < MAX_DSP_SESS_REC; i++) { ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, info->DSPSess.Rec[i]); } } else { ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_MAG_RX_BASE); for (i = 0; i < MAX_DSP_SESS_REC / 2; i++) { outl(info->DSPSess.MagRec[i], dev->base_addr + FT1000_REG_MAG_DPDATA); } } spin_unlock_irqrestore(&info->dpram_lock, flags); // clear ASIC RESET request ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_ASIC_RESET_REQ); DEBUG(1, "Got an ASIC RESET Request\n"); ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_ASIC_RESET_DSP); if (info->AsicID == MAGNEMITE_ID) { // Setting MAGNEMITE ASIC to big endian mode ft1000_write_reg(dev, FT1000_REG_SUP_CTRL, HOST_INTF_BE); } } if (doorbell & FT1000_DSP_ASIC_RESET) { DEBUG(0, "FT1000:ft1000_parse_dpram_msg: Got a dsp ASIC reset message\n"); ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DSP_ASIC_RESET); udelay(200); return SUCCESS; } if (doorbell & FT1000_DB_DPRAM_RX) { DEBUG(1, "FT1000:ft1000_parse_dpram_msg: Got a slow queue message\n"); nxtph = FT1000_DPRAM_RX_BASE + 2; if (info->AsicID == ELECTRABUZZ_ID) { total_len = ft1000_read_dpram(dev, FT1000_DPRAM_RX_BASE); } else { total_len = ntohs(ft1000_read_dpram_mag_16 (dev, FT1000_MAG_TOTAL_LEN, FT1000_MAG_TOTAL_LEN_INDX)); } DEBUG(1, "FT1000:ft1000_parse_dpram_msg:total length = %d\n", total_len); if ((total_len < MAX_CMD_SQSIZE) && (total_len > sizeof(struct pseudo_hdr))) { total_len += nxtph; cnt = 0; // ft1000_read_reg will return a value that needs to be byteswap // in order to get DSP_QID_OFFSET. if (info->AsicID == ELECTRABUZZ_ID) { portid = (ft1000_read_dpram (dev, DSP_QID_OFFSET + FT1000_DPRAM_RX_BASE + 2) >> 8) & 0xff; } else { portid = (ft1000_read_dpram_mag_16 (dev, FT1000_MAG_PORT_ID, FT1000_MAG_PORT_ID_INDX) & 0xff); } DEBUG(1, "DSP_QID = 0x%x\n", portid); if (portid == DRIVERID) { // We are assumming one driver message from the DSP at a time. ft1000_proc_drvmsg(dev); } } ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_DPRAM_RX); } if (doorbell & FT1000_DB_COND_RESET) { // Reset ASIC and DSP if (info->AsicID == ELECTRABUZZ_ID) { info->DSP_TIME[0] = ft1000_read_dpram(dev, FT1000_DSP_TIMER0); info->DSP_TIME[1] = ft1000_read_dpram(dev, FT1000_DSP_TIMER1); info->DSP_TIME[2] = ft1000_read_dpram(dev, FT1000_DSP_TIMER2); info->DSP_TIME[3] = ft1000_read_dpram(dev, FT1000_DSP_TIMER3); } else { info->DSP_TIME[0] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER0, FT1000_MAG_DSP_TIMER0_INDX); info->DSP_TIME[1] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER1, FT1000_MAG_DSP_TIMER1_INDX); info->DSP_TIME[2] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER2, FT1000_MAG_DSP_TIMER2_INDX); info->DSP_TIME[3] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER3, FT1000_MAG_DSP_TIMER3_INDX); } info->DrvErrNum = DSP_CONDRESET_INFO; DEBUG(1, "ft1000_hw:DSP conditional reset requested\n"); ft1000_reset_card(dev); ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_COND_RESET); } // let's clear any unexpected doorbells from DSP doorbell = doorbell & ~(FT1000_DB_DPRAM_RX | FT1000_ASIC_RESET_REQ | FT1000_DB_COND_RESET | 0xff00); if (doorbell) { DEBUG(1, "Clearing unexpected doorbell = 0x%x\n", doorbell); ft1000_write_reg(dev, FT1000_REG_DOORBELL, doorbell); } return SUCCESS; } //--------------------------------------------------------------------------- // // Function: ft1000_flush_fifo // Description: This function will flush one packet from the downlink // FIFO. // Input: // dev - device structure // drv_err - driver error causing the flush fifo // Output: // None. // //--------------------------------------------------------------------------- static void ft1000_flush_fifo(struct net_device *dev, u16 DrvErrNum) { struct ft1000_info *info = netdev_priv(dev); u16 i; u32 templong; u16 tempword; DEBUG(1, "ft1000:ft1000_hw:ft1000_flush_fifo called\n"); if (info->PktIntfErr > MAX_PH_ERR) { if (info->AsicID == ELECTRABUZZ_ID) { info->DSP_TIME[0] = ft1000_read_dpram(dev, FT1000_DSP_TIMER0); info->DSP_TIME[1] = ft1000_read_dpram(dev, FT1000_DSP_TIMER1); info->DSP_TIME[2] = ft1000_read_dpram(dev, FT1000_DSP_TIMER2); info->DSP_TIME[3] = ft1000_read_dpram(dev, FT1000_DSP_TIMER3); } else { info->DSP_TIME[0] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER0, FT1000_MAG_DSP_TIMER0_INDX); info->DSP_TIME[1] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER1, FT1000_MAG_DSP_TIMER1_INDX); info->DSP_TIME[2] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER2, FT1000_MAG_DSP_TIMER2_INDX); info->DSP_TIME[3] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER3, FT1000_MAG_DSP_TIMER3_INDX); } info->DrvErrNum = DrvErrNum; ft1000_reset_card(dev); return; } else { // Flush corrupted pkt from FIFO i = 0; do { if (info->AsicID == ELECTRABUZZ_ID) { tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO_STAT); } else { templong = inl(dev->base_addr + FT1000_REG_MAG_DFR); tempword = inw(dev->base_addr + FT1000_REG_MAG_DFSR); } i++; // This should never happen unless the ASIC is broken. // We must reset to recover. if ((i > 2048) || (tempword == 0)) { if (info->AsicID == ELECTRABUZZ_ID) { info->DSP_TIME[0] = ft1000_read_dpram(dev, FT1000_DSP_TIMER0); info->DSP_TIME[1] = ft1000_read_dpram(dev, FT1000_DSP_TIMER1); info->DSP_TIME[2] = ft1000_read_dpram(dev, FT1000_DSP_TIMER2); info->DSP_TIME[3] = ft1000_read_dpram(dev, FT1000_DSP_TIMER3); } else { info->DSP_TIME[0] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER0, FT1000_MAG_DSP_TIMER0_INDX); info->DSP_TIME[1] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER1, FT1000_MAG_DSP_TIMER1_INDX); info->DSP_TIME[2] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER2, FT1000_MAG_DSP_TIMER2_INDX); info->DSP_TIME[3] = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER3, FT1000_MAG_DSP_TIMER3_INDX); } if (tempword == 0) { // Let's check if ASIC reads are still ok by reading the Mask register // which is never zero at this point of the code. tempword = inw(dev->base_addr + FT1000_REG_SUP_IMASK); if (tempword == 0) { // This indicates that we can not communicate with the ASIC info->DrvErrNum = FIFO_FLUSH_BADCNT; } else { // Let's assume that we really flush the FIFO info->PktIntfErr++; return; } } else { info->DrvErrNum = FIFO_FLUSH_MAXLIMIT; } return; } tempword = inw(dev->base_addr + FT1000_REG_SUP_STAT); } while ((tempword & 0x03) != 0x03); if (info->AsicID == ELECTRABUZZ_ID) { i++; DEBUG(0, "Flushing FIFO complete = %x\n", tempword); // Flush last word in FIFO. tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); // Update FIFO counter for DSP i = i * 2; DEBUG(0, "Flush Data byte count to dsp = %d\n", i); info->fifo_cnt += i; ft1000_write_dpram(dev, FT1000_FIFO_LEN, info->fifo_cnt); } else { DEBUG(0, "Flushing FIFO complete = %x\n", tempword); // Flush last word in FIFO templong = inl(dev->base_addr + FT1000_REG_MAG_DFR); tempword = inw(dev->base_addr + FT1000_REG_SUP_STAT); DEBUG(0, "FT1000_REG_SUP_STAT = 0x%x\n", tempword); tempword = inw(dev->base_addr + FT1000_REG_MAG_DFSR); DEBUG(0, "FT1000_REG_MAG_DFSR = 0x%x\n", tempword); } if (DrvErrNum) { info->PktIntfErr++; } } } //--------------------------------------------------------------------------- // // Function: ft1000_copy_up_pkt // Description: This function will pull Flarion packets out of the Downlink // FIFO and convert it to an ethernet packet. The ethernet packet will // then be deliver to the TCP/IP stack. // Input: // dev - device structure // Output: // status - FAILURE // SUCCESS // //--------------------------------------------------------------------------- static int ft1000_copy_up_pkt(struct net_device *dev) { u16 tempword; struct ft1000_info *info = netdev_priv(dev); u16 len; struct sk_buff *skb; u16 i; u8 *pbuffer = NULL; u8 *ptemp = NULL; u16 chksum; u32 *ptemplong; u32 templong; DEBUG(1, "ft1000_copy_up_pkt\n"); // Read length if (info->AsicID == ELECTRABUZZ_ID) { tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); len = tempword; } else { tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); len = ntohs(tempword); } chksum = tempword; DEBUG(1, "Number of Bytes in FIFO = %d\n", len); if (len > ENET_MAX_SIZE) { DEBUG(0, "size of ethernet packet invalid\n"); if (info->AsicID == MAGNEMITE_ID) { // Read High word to complete 32 bit access tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); } ft1000_flush_fifo(dev, DSP_PKTLEN_INFO); info->stats.rx_errors++; return FAILURE; } skb = dev_alloc_skb(len + 12 + 2); if (skb == NULL) { DEBUG(0, "No Network buffers available\n"); // Read High word to complete 32 bit access if (info->AsicID == MAGNEMITE_ID) { tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); } ft1000_flush_fifo(dev, 0); info->stats.rx_errors++; return FAILURE; } pbuffer = (u8 *) skb_put(skb, len + 12); // Pseudo header if (info->AsicID == ELECTRABUZZ_ID) { for (i = 1; i < 7; i++) { tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); chksum ^= tempword; } // read checksum value tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); } else { tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); DEBUG(1, "Pseudo = 0x%x\n", tempword); chksum ^= tempword; tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); DEBUG(1, "Pseudo = 0x%x\n", tempword); chksum ^= tempword; tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); DEBUG(1, "Pseudo = 0x%x\n", tempword); chksum ^= tempword; tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); DEBUG(1, "Pseudo = 0x%x\n", tempword); chksum ^= tempword; tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); DEBUG(1, "Pseudo = 0x%x\n", tempword); chksum ^= tempword; tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL); DEBUG(1, "Pseudo = 0x%x\n", tempword); chksum ^= tempword; // read checksum value tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH); DEBUG(1, "Pseudo = 0x%x\n", tempword); } if (chksum != tempword) { DEBUG(0, "Packet checksum mismatch 0x%x 0x%x\n", chksum, tempword); ft1000_flush_fifo(dev, DSP_PKTPHCKSUM_INFO); info->stats.rx_errors++; kfree_skb(skb); return FAILURE; } //subtract the number of bytes read already ptemp = pbuffer; // fake MAC address *pbuffer++ = dev->dev_addr[0]; *pbuffer++ = dev->dev_addr[1]; *pbuffer++ = dev->dev_addr[2]; *pbuffer++ = dev->dev_addr[3]; *pbuffer++ = dev->dev_addr[4]; *pbuffer++ = dev->dev_addr[5]; *pbuffer++ = 0x00; *pbuffer++ = 0x07; *pbuffer++ = 0x35; *pbuffer++ = 0xff; *pbuffer++ = 0xff; *pbuffer++ = 0xfe; if (info->AsicID == ELECTRABUZZ_ID) { for (i = 0; i < len / 2; i++) { tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); *pbuffer++ = (u8) (tempword >> 8); *pbuffer++ = (u8) tempword; if (ft1000_chkcard(dev) == false) { kfree_skb(skb); return FAILURE; } } // Need to read one more word if odd byte if (len & 0x0001) { tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO); *pbuffer++ = (u8) (tempword >> 8); } } else { ptemplong = (u32 *) pbuffer; for (i = 0; i < len / 4; i++) { templong = inl(dev->base_addr + FT1000_REG_MAG_DFR); DEBUG(1, "Data = 0x%8x\n", templong); *ptemplong++ = templong; } // Need to read one more word if odd align. if (len & 0x0003) { templong = inl(dev->base_addr + FT1000_REG_MAG_DFR); DEBUG(1, "Data = 0x%8x\n", templong); *ptemplong++ = templong; } } DEBUG(1, "Data passed to Protocol layer:\n"); for (i = 0; i < len + 12; i++) { DEBUG(1, "Protocol Data: 0x%x\n ", *ptemp++); } skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_UNNECESSARY; netif_rx(skb); info->stats.rx_packets++; // Add on 12 bytes for MAC address which was removed info->stats.rx_bytes += (len + 12); if (info->AsicID == ELECTRABUZZ_ID) { // track how many bytes have been read from FIFO - round up to 16 bit word tempword = len + 16; if (tempword & 0x01) tempword++; info->fifo_cnt += tempword; ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_FIFO_LEN); ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, info->fifo_cnt); } return SUCCESS; } //--------------------------------------------------------------------------- // // Function: ft1000_copy_down_pkt // Description: This function will take an ethernet packet and convert it to // a Flarion packet prior to sending it to the ASIC Downlink // FIFO. // Input: // dev - device structure // packet - address of ethernet packet // len - length of IP packet // Output: // status - FAILURE // SUCCESS // //--------------------------------------------------------------------------- static int ft1000_copy_down_pkt(struct net_device *dev, u16 * packet, u16 len) { struct ft1000_info *info = netdev_priv(dev); union { struct pseudo_hdr blk; u16 buff[sizeof(struct pseudo_hdr) >> 1]; u8 buffc[sizeof(struct pseudo_hdr)]; } pseudo; int i; u32 *plong; DEBUG(1, "ft1000_hw: copy_down_pkt()\n"); // Check if there is room on the FIFO if (len > ft1000_read_fifo_len(dev)) { udelay(10); if (len > ft1000_read_fifo_len(dev)) { udelay(20); } if (len > ft1000_read_fifo_len(dev)) { udelay(20); } if (len > ft1000_read_fifo_len(dev)) { udelay(20); } if (len > ft1000_read_fifo_len(dev)) { udelay(20); } if (len > ft1000_read_fifo_len(dev)) { udelay(20); } if (len > ft1000_read_fifo_len(dev)) { DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:Transmit FIFO is fulli - pkt drop\n"); info->stats.tx_errors++; return SUCCESS; } } // Create pseudo header and send pseudo/ip to hardware if (info->AsicID == ELECTRABUZZ_ID) { pseudo.blk.length = len; } else { pseudo.blk.length = ntohs(len); } pseudo.blk.source = DSPID; // Need to swap to get in correct order pseudo.blk.destination = HOSTID; pseudo.blk.portdest = NETWORKID; // Need to swap to get in correct order pseudo.blk.portsrc = DSPAIRID; pseudo.blk.sh_str_id = 0; pseudo.blk.control = 0; pseudo.blk.rsvd1 = 0; pseudo.blk.seq_num = 0; pseudo.blk.rsvd2 = info->packetseqnum++; pseudo.blk.qos_class = 0; /* Calculate pseudo header checksum */ pseudo.blk.checksum = pseudo.buff[0]; for (i = 1; i < 7; i++) { pseudo.blk.checksum ^= pseudo.buff[i]; } // Production Mode if (info->AsicID == ELECTRABUZZ_ID) { // copy first word to UFIFO_BEG reg ft1000_write_reg(dev, FT1000_REG_UFIFO_BEG, pseudo.buff[0]); DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 0 BEG = 0x%04x\n", pseudo.buff[0]); // copy subsequent words to UFIFO_MID reg ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[1]); DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 1 MID = 0x%04x\n", pseudo.buff[1]); ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[2]); DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 2 MID = 0x%04x\n", pseudo.buff[2]); ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[3]); DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 3 MID = 0x%04x\n", pseudo.buff[3]); ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[4]); DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 4 MID = 0x%04x\n", pseudo.buff[4]); ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[5]); DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 5 MID = 0x%04x\n", pseudo.buff[5]); ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[6]); DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 6 MID = 0x%04x\n", pseudo.buff[6]); ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[7]); DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 7 MID = 0x%04x\n", pseudo.buff[7]); // Write PPP type + IP Packet into Downlink FIFO for (i = 0; i < (len >> 1) - 1; i++) { ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, htons(*packet)); DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data %d MID = 0x%04x\n", i + 8, htons(*packet)); packet++; } // Check for odd byte if (len & 0x0001) { ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, htons(*packet)); DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data MID = 0x%04x\n", htons(*packet)); packet++; ft1000_write_reg(dev, FT1000_REG_UFIFO_END, htons(*packet)); DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data %d MID = 0x%04x\n", i + 8, htons(*packet)); } else { ft1000_write_reg(dev, FT1000_REG_UFIFO_END, htons(*packet)); DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data %d MID = 0x%04x\n", i + 8, htons(*packet)); } } else { outl(*(u32 *) & pseudo.buff[0], dev->base_addr + FT1000_REG_MAG_UFDR); DEBUG(1, "ft1000_copy_down_pkt: Pseudo = 0x%8x\n", *(u32 *) & pseudo.buff[0]); outl(*(u32 *) & pseudo.buff[2], dev->base_addr + FT1000_REG_MAG_UFDR); DEBUG(1, "ft1000_copy_down_pkt: Pseudo = 0x%8x\n", *(u32 *) & pseudo.buff[2]); outl(*(u32 *) & pseudo.buff[4], dev->base_addr + FT1000_REG_MAG_UFDR); DEBUG(1, "ft1000_copy_down_pkt: Pseudo = 0x%8x\n", *(u32 *) & pseudo.buff[4]); outl(*(u32 *) & pseudo.buff[6], dev->base_addr + FT1000_REG_MAG_UFDR); DEBUG(1, "ft1000_copy_down_pkt: Pseudo = 0x%8x\n", *(u32 *) & pseudo.buff[6]); plong = (u32 *) packet; // Write PPP type + IP Packet into Downlink FIFO for (i = 0; i < (len >> 2); i++) { outl(*plong++, dev->base_addr + FT1000_REG_MAG_UFDR); } // Check for odd alignment if (len & 0x0003) { DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data = 0x%8x\n", *plong); outl(*plong++, dev->base_addr + FT1000_REG_MAG_UFDR); } outl(1, dev->base_addr + FT1000_REG_MAG_UFER); } info->stats.tx_packets++; // Add 14 bytes for MAC address plus ethernet type info->stats.tx_bytes += (len + 14); return SUCCESS; } static struct net_device_stats *ft1000_stats(struct net_device *dev) { struct ft1000_info *info = netdev_priv(dev); return (&info->stats); } static int ft1000_open(struct net_device *dev) { DEBUG(0, "ft1000_hw: ft1000_open is called\n"); ft1000_reset_card(dev); DEBUG(0, "ft1000_hw: ft1000_open is ended\n"); /* schedule ft1000_hbchk to perform periodic heartbeat checks on DSP and ASIC */ init_timer(&poll_timer); poll_timer.expires = jiffies + (2 * HZ); poll_timer.data = (u_long) dev; add_timer(&poll_timer); DEBUG(0, "ft1000_hw: ft1000_open is ended2\n"); return 0; } static int ft1000_close(struct net_device *dev) { struct ft1000_info *info = netdev_priv(dev); DEBUG(0, "ft1000_hw: ft1000_close()\n"); info->CardReady = 0; del_timer(&poll_timer); if (ft1000_card_present == 1) { DEBUG(0, "Media is down\n"); netif_stop_queue(dev); ft1000_disable_interrupts(dev); ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT); //reset ASIC ft1000_reset_asic(dev); } return 0; } static int ft1000_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ft1000_info *info = netdev_priv(dev); u8 *pdata; DEBUG(1, "ft1000_hw: ft1000_start_xmit()\n"); if (skb == NULL) { DEBUG(1, "ft1000_hw: ft1000_start_xmit:skb == NULL!!!\n"); return 0; } DEBUG(1, "ft1000_hw: ft1000_start_xmit:length of packet = %d\n", skb->len); pdata = (u8 *) skb->data; if (info->mediastate == 0) { /* Drop packet is mediastate is down */ DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:mediastate is down\n"); return SUCCESS; } if ((skb->len < ENET_HEADER_SIZE) || (skb->len > ENET_MAX_SIZE)) { /* Drop packet which has invalid size */ DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:invalid ethernet length\n"); return SUCCESS; } ft1000_copy_down_pkt(dev, (u16 *) (pdata + ENET_HEADER_SIZE - 2), skb->len - ENET_HEADER_SIZE + 2); dev_kfree_skb(skb); return 0; } static irqreturn_t ft1000_interrupt(int irq, void *dev_id) { struct net_device *dev = (struct net_device *)dev_id; struct ft1000_info *info = netdev_priv(dev); u16 tempword; u16 inttype; int cnt; DEBUG(1, "ft1000_hw: ft1000_interrupt()\n"); if (info->CardReady == 0) { ft1000_disable_interrupts(dev); return IRQ_HANDLED; } if (ft1000_chkcard(dev) == false) { ft1000_disable_interrupts(dev); return IRQ_HANDLED; } ft1000_disable_interrupts(dev); // Read interrupt type inttype = ft1000_read_reg(dev, FT1000_REG_SUP_ISR); // Make sure we process all interrupt before leaving the ISR due to the edge trigger interrupt type while (inttype) { if (inttype & ISR_DOORBELL_PEND) { ft1000_parse_dpram_msg(dev); } if (inttype & ISR_RCV) { DEBUG(1, "Data in FIFO\n"); cnt = 0; do { // Check if we have packets in the Downlink FIFO if (info->AsicID == ELECTRABUZZ_ID) { tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO_STAT); } else { tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFSR); } if (tempword & 0x1f) { ft1000_copy_up_pkt(dev); } else { break; } cnt++; } while (cnt < MAX_RCV_LOOP); } // clear interrupts tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR); DEBUG(1, "ft1000_hw: interrupt status register = 0x%x\n", tempword); ft1000_write_reg(dev, FT1000_REG_SUP_ISR, tempword); // Read interrupt type inttype = ft1000_read_reg (dev, FT1000_REG_SUP_ISR); DEBUG(1,"ft1000_hw: interrupt status register after clear = 0x%x\n",inttype); } ft1000_enable_interrupts(dev); return IRQ_HANDLED; } void stop_ft1000_card(struct net_device *dev) { struct ft1000_info *info = netdev_priv(dev); struct prov_record *ptr; // int cnt; DEBUG(0, "ft1000_hw: stop_ft1000_card()\n"); info->CardReady = 0; ft1000_card_present = 0; netif_stop_queue(dev); ft1000_disable_interrupts(dev); // Make sure we free any memory reserve for provisioning while (list_empty(&info->prov_list) == 0) { ptr = list_entry(info->prov_list.next, struct prov_record, list); list_del(&ptr->list); kfree(ptr->pprov_data); kfree(ptr); } if (info->registered) { unregister_netdev(dev); info->registered = 0; } free_irq(dev->irq, dev); release_region(dev->base_addr,256); release_firmware(fw_entry); flarion_ft1000_cnt--; ft1000CleanupProc(dev); } static void ft1000_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct ft1000_info *ft_info; ft_info = netdev_priv(dev); snprintf(info->driver, 32, "ft1000"); snprintf(info->bus_info, ETHTOOL_BUSINFO_LEN, "PCMCIA 0x%lx", dev->base_addr); snprintf(info->fw_version, 32, "%d.%d.%d.%d", ft_info->DspVer[0], ft_info->DspVer[1], ft_info->DspVer[2], ft_info->DspVer[3]); } static u32 ft1000_get_link(struct net_device *dev) { struct ft1000_info *info; info = netdev_priv(dev); return info->mediastate; } static const struct ethtool_ops ops = { .get_drvinfo = ft1000_get_drvinfo, .get_link = ft1000_get_link }; struct net_device *init_ft1000_card(struct pcmcia_device *link, void *ft1000_reset) { struct ft1000_info *info; struct net_device *dev; static const struct net_device_ops ft1000ops = // Slavius 21.10.2009 due to kernel changes { .ndo_open = &ft1000_open, .ndo_stop = &ft1000_close, .ndo_start_xmit = &ft1000_start_xmit, .ndo_get_stats = &ft1000_stats, }; DEBUG(1, "ft1000_hw: init_ft1000_card()\n"); DEBUG(1, "ft1000_hw: irq = %d\n", link->irq); DEBUG(1, "ft1000_hw: port = 0x%04x\n", link->resource[0]->start); flarion_ft1000_cnt++; if (flarion_ft1000_cnt > 1) { flarion_ft1000_cnt--; printk(KERN_INFO "ft1000: This driver can not support more than one instance\n"); return NULL; } dev = alloc_etherdev(sizeof(struct ft1000_info)); if (!dev) { printk(KERN_ERR "ft1000: failed to allocate etherdev\n"); return NULL; } SET_NETDEV_DEV(dev, &link->dev); info = netdev_priv(dev); memset(info, 0, sizeof(struct ft1000_info)); DEBUG(1, "address of dev = 0x%8x\n", (u32) dev); DEBUG(1, "address of dev info = 0x%8x\n", (u32) info); DEBUG(0, "device name = %s\n", dev->name); memset(&info->stats, 0, sizeof(struct net_device_stats)); spin_lock_init(&info->dpram_lock); info->DrvErrNum = 0; info->registered = 1; info->link = link; info->ft1000_reset = ft1000_reset; info->mediastate = 0; info->fifo_cnt = 0; info->CardReady = 0; info->DSP_TIME[0] = 0; info->DSP_TIME[1] = 0; info->DSP_TIME[2] = 0; info->DSP_TIME[3] = 0; flarion_ft1000_cnt = 0; INIT_LIST_HEAD(&info->prov_list); info->squeseqnum = 0; // dev->hard_start_xmit = &ft1000_start_xmit; // dev->get_stats = &ft1000_stats; // dev->open = &ft1000_open; // dev->stop = &ft1000_close; dev->netdev_ops = &ft1000ops; // Slavius 21.10.2009 due to kernel changes DEBUG(0, "device name = %s\n", dev->name); dev->irq = link->irq; dev->base_addr = link->resource[0]->start; if (pcmcia_get_mac_from_cis(link, dev)) { printk(KERN_ERR "ft1000: Could not read mac address\n"); goto err_dev; } if (request_irq(dev->irq, ft1000_interrupt, IRQF_SHARED, dev->name, dev)) { printk(KERN_ERR "ft1000: Could not request_irq\n"); goto err_dev; } if (request_region(dev->base_addr, 256, dev->name) == NULL) { printk(KERN_ERR "ft1000: Could not request_region\n"); goto err_irq; } if (register_netdev(dev) != 0) { DEBUG(0, "ft1000: Could not register netdev"); goto err_reg; } info->AsicID = ft1000_read_reg(dev, FT1000_REG_ASIC_ID); if (info->AsicID == ELECTRABUZZ_ID) { DEBUG(0, "ft1000_hw: ELECTRABUZZ ASIC\n"); if (request_firmware(&fw_entry, "ft1000.img", &link->dev) != 0) { printk(KERN_INFO "ft1000: Could not open ft1000.img\n"); goto err_unreg; } } else { DEBUG(0, "ft1000_hw: MAGNEMITE ASIC\n"); if (request_firmware(&fw_entry, "ft2000.img", &link->dev) != 0) { printk(KERN_INFO "ft1000: Could not open ft2000.img\n"); goto err_unreg; } } ft1000_enable_interrupts(dev); ft1000InitProc(dev); ft1000_card_present = 1; SET_ETHTOOL_OPS(dev, &ops); printk(KERN_INFO "ft1000: %s: addr 0x%04lx irq %d, MAC addr %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name, dev->base_addr, dev->irq, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); return dev; err_unreg: unregister_netdev(dev); err_reg: release_region(dev->base_addr, 256); err_irq: free_irq(dev->irq, dev); err_dev: free_netdev(dev); return NULL; }