2006-07-25 15:40:21 +08:00
|
|
|
/*
|
|
|
|
* QLogic QLA3xxx NIC HBA Driver
|
|
|
|
* Copyright (c) 2003-2006 QLogic Corporation
|
|
|
|
*
|
|
|
|
* See LICENSE.qla3xxx for copyright and licensing details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/pci.h>
|
|
|
|
#include <linux/dma-mapping.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/dmapool.h>
|
|
|
|
#include <linux/mempool.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <linux/kthread.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/ioport.h>
|
|
|
|
#include <linux/ip.h>
|
2007-01-04 08:26:08 +08:00
|
|
|
#include <linux/in.h>
|
2006-07-25 15:40:21 +08:00
|
|
|
#include <linux/if_arp.h>
|
|
|
|
#include <linux/if_ether.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
#include <linux/ethtool.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include <linux/if_vlan.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
|
|
|
|
#include "qla3xxx.h"
|
|
|
|
|
|
|
|
#define DRV_NAME "qla3xxx"
|
|
|
|
#define DRV_STRING "QLogic ISP3XXX Network Driver"
|
2007-03-27 04:43:03 +08:00
|
|
|
#define DRV_VERSION "v2.03.00-k4"
|
2006-07-25 15:40:21 +08:00
|
|
|
#define PFX DRV_NAME " "
|
|
|
|
|
|
|
|
static const char ql3xxx_driver_name[] = DRV_NAME;
|
|
|
|
static const char ql3xxx_driver_version[] = DRV_VERSION;
|
|
|
|
|
|
|
|
MODULE_AUTHOR("QLogic Corporation");
|
|
|
|
MODULE_DESCRIPTION("QLogic ISP3XXX Network Driver " DRV_VERSION " ");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_VERSION(DRV_VERSION);
|
|
|
|
|
|
|
|
static const u32 default_msg
|
|
|
|
= NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK
|
|
|
|
| NETIF_MSG_IFUP | NETIF_MSG_IFDOWN;
|
|
|
|
|
|
|
|
static int debug = -1; /* defaults above */
|
|
|
|
module_param(debug, int, 0);
|
|
|
|
MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
|
|
|
|
|
|
|
|
static int msi;
|
|
|
|
module_param(msi, int, 0);
|
|
|
|
MODULE_PARM_DESC(msi, "Turn on Message Signaled Interrupts.");
|
|
|
|
|
|
|
|
static struct pci_device_id ql3xxx_pci_tbl[] __devinitdata = {
|
|
|
|
{PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, QL3022_DEVICE_ID)},
|
2007-01-04 08:26:08 +08:00
|
|
|
{PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, QL3032_DEVICE_ID)},
|
2006-07-25 15:40:21 +08:00
|
|
|
/* required last entry */
|
|
|
|
{0,}
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(pci, ql3xxx_pci_tbl);
|
|
|
|
|
2007-03-27 03:43:52 +08:00
|
|
|
/*
|
|
|
|
* These are the known PHY's which are used
|
|
|
|
*/
|
|
|
|
typedef enum {
|
|
|
|
PHY_TYPE_UNKNOWN = 0,
|
|
|
|
PHY_VITESSE_VSC8211,
|
|
|
|
PHY_AGERE_ET1011C,
|
|
|
|
MAX_PHY_DEV_TYPES
|
|
|
|
} PHY_DEVICE_et;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
PHY_DEVICE_et phyDevice;
|
|
|
|
u32 phyIdOUI;
|
|
|
|
u16 phyIdModel;
|
|
|
|
char *name;
|
|
|
|
} PHY_DEVICE_INFO_t;
|
|
|
|
|
2007-04-01 04:55:40 +08:00
|
|
|
static const PHY_DEVICE_INFO_t PHY_DEVICES[] =
|
2007-03-27 03:43:52 +08:00
|
|
|
{{PHY_TYPE_UNKNOWN, 0x000000, 0x0, "PHY_TYPE_UNKNOWN"},
|
|
|
|
{PHY_VITESSE_VSC8211, 0x0003f1, 0xb, "PHY_VITESSE_VSC8211"},
|
|
|
|
{PHY_AGERE_ET1011C, 0x00a0bc, 0x1, "PHY_AGERE_ET1011C"},
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2006-07-25 15:40:21 +08:00
|
|
|
/*
|
|
|
|
* Caller must take hw_lock.
|
|
|
|
*/
|
|
|
|
static int ql_sem_spinlock(struct ql3_adapter *qdev,
|
|
|
|
u32 sem_mask, u32 sem_bits)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
|
|
|
|
u32 value;
|
|
|
|
unsigned int seconds = 3;
|
|
|
|
|
|
|
|
do {
|
|
|
|
writel((sem_mask | sem_bits),
|
|
|
|
&port_regs->CommonRegs.semaphoreReg);
|
|
|
|
value = readl(&port_regs->CommonRegs.semaphoreReg);
|
|
|
|
if ((value & (sem_mask >> 16)) == sem_bits)
|
|
|
|
return 0;
|
|
|
|
ssleep(1);
|
|
|
|
} while(--seconds);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_sem_unlock(struct ql3_adapter *qdev, u32 sem_mask)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
|
|
|
|
writel(sem_mask, &port_regs->CommonRegs.semaphoreReg);
|
|
|
|
readl(&port_regs->CommonRegs.semaphoreReg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_sem_lock(struct ql3_adapter *qdev, u32 sem_mask, u32 sem_bits)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
|
|
|
|
u32 value;
|
|
|
|
|
|
|
|
writel((sem_mask | sem_bits), &port_regs->CommonRegs.semaphoreReg);
|
|
|
|
value = readl(&port_regs->CommonRegs.semaphoreReg);
|
|
|
|
return ((value & (sem_mask >> 16)) == sem_bits);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static int ql_wait_for_drvr_lock(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
if (!ql_sem_lock(qdev,
|
|
|
|
QL_DRVR_SEM_MASK,
|
|
|
|
(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index)
|
|
|
|
* 2) << 1)) {
|
|
|
|
if (i < 10) {
|
|
|
|
ssleep(1);
|
|
|
|
i++;
|
|
|
|
} else {
|
|
|
|
printk(KERN_ERR PFX "%s: Timed out waiting for "
|
|
|
|
"driver lock...\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
printk(KERN_DEBUG PFX
|
|
|
|
"%s: driver lock acquired.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_set_register_page(struct ql3_adapter *qdev, u32 page)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
|
|
|
|
|
|
|
|
writel(((ISP_CONTROL_NP_MASK << 16) | page),
|
|
|
|
&port_regs->CommonRegs.ispControlStatus);
|
|
|
|
readl(&port_regs->CommonRegs.ispControlStatus);
|
|
|
|
qdev->current_page = page;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 ql_read_common_reg_l(struct ql3_adapter *qdev,
|
|
|
|
u32 __iomem * reg)
|
|
|
|
{
|
|
|
|
u32 value;
|
|
|
|
unsigned long hw_flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
|
|
|
|
value = readl(reg);
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 ql_read_common_reg(struct ql3_adapter *qdev,
|
|
|
|
u32 __iomem * reg)
|
|
|
|
{
|
|
|
|
return readl(reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 ql_read_page0_reg_l(struct ql3_adapter *qdev, u32 __iomem *reg)
|
|
|
|
{
|
|
|
|
u32 value;
|
|
|
|
unsigned long hw_flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
|
|
|
|
|
|
|
|
if (qdev->current_page != 0)
|
|
|
|
ql_set_register_page(qdev,0);
|
|
|
|
value = readl(reg);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 ql_read_page0_reg(struct ql3_adapter *qdev, u32 __iomem *reg)
|
|
|
|
{
|
|
|
|
if (qdev->current_page != 0)
|
|
|
|
ql_set_register_page(qdev,0);
|
|
|
|
return readl(reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_write_common_reg_l(struct ql3_adapter *qdev,
|
2006-09-25 09:53:53 +08:00
|
|
|
u32 __iomem *reg, u32 value)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
|
|
|
unsigned long hw_flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
|
2006-09-25 09:53:53 +08:00
|
|
|
writel(value, reg);
|
2006-07-25 15:40:21 +08:00
|
|
|
readl(reg);
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_write_common_reg(struct ql3_adapter *qdev,
|
2006-09-25 09:53:53 +08:00
|
|
|
u32 __iomem *reg, u32 value)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
2006-09-25 09:53:53 +08:00
|
|
|
writel(value, reg);
|
2006-07-25 15:40:21 +08:00
|
|
|
readl(reg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-01-04 08:26:07 +08:00
|
|
|
static void ql_write_nvram_reg(struct ql3_adapter *qdev,
|
|
|
|
u32 __iomem *reg, u32 value)
|
|
|
|
{
|
|
|
|
writel(value, reg);
|
|
|
|
readl(reg);
|
|
|
|
udelay(1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-07-25 15:40:21 +08:00
|
|
|
static void ql_write_page0_reg(struct ql3_adapter *qdev,
|
2006-09-25 09:53:53 +08:00
|
|
|
u32 __iomem *reg, u32 value)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
|
|
|
if (qdev->current_page != 0)
|
|
|
|
ql_set_register_page(qdev,0);
|
2006-09-25 09:53:53 +08:00
|
|
|
writel(value, reg);
|
2006-07-25 15:40:21 +08:00
|
|
|
readl(reg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock. Only called during init.
|
|
|
|
*/
|
|
|
|
static void ql_write_page1_reg(struct ql3_adapter *qdev,
|
2006-09-25 09:53:53 +08:00
|
|
|
u32 __iomem *reg, u32 value)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
|
|
|
if (qdev->current_page != 1)
|
|
|
|
ql_set_register_page(qdev,1);
|
2006-09-25 09:53:53 +08:00
|
|
|
writel(value, reg);
|
2006-07-25 15:40:21 +08:00
|
|
|
readl(reg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock. Only called during init.
|
|
|
|
*/
|
|
|
|
static void ql_write_page2_reg(struct ql3_adapter *qdev,
|
2006-09-25 09:53:53 +08:00
|
|
|
u32 __iomem *reg, u32 value)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
|
|
|
if (qdev->current_page != 2)
|
|
|
|
ql_set_register_page(qdev,2);
|
2006-09-25 09:53:53 +08:00
|
|
|
writel(value, reg);
|
2006-07-25 15:40:21 +08:00
|
|
|
readl(reg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_disable_interrupts(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
|
|
|
|
|
|
|
|
ql_write_common_reg_l(qdev, &port_regs->CommonRegs.ispInterruptMaskReg,
|
|
|
|
(ISP_IMR_ENABLE_INT << 16));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_enable_interrupts(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
|
|
|
|
|
|
|
|
ql_write_common_reg_l(qdev, &port_regs->CommonRegs.ispInterruptMaskReg,
|
|
|
|
((0xff << 16) | ISP_IMR_ENABLE_INT));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_release_to_lrg_buf_free_list(struct ql3_adapter *qdev,
|
|
|
|
struct ql_rcv_buf_cb *lrg_buf_cb)
|
|
|
|
{
|
2007-02-27 03:06:40 +08:00
|
|
|
dma_addr_t map;
|
|
|
|
int err;
|
2006-07-25 15:40:21 +08:00
|
|
|
lrg_buf_cb->next = NULL;
|
|
|
|
|
|
|
|
if (qdev->lrg_buf_free_tail == NULL) { /* The list is empty */
|
|
|
|
qdev->lrg_buf_free_head = qdev->lrg_buf_free_tail = lrg_buf_cb;
|
|
|
|
} else {
|
|
|
|
qdev->lrg_buf_free_tail->next = lrg_buf_cb;
|
|
|
|
qdev->lrg_buf_free_tail = lrg_buf_cb;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!lrg_buf_cb->skb) {
|
2007-02-27 03:06:33 +08:00
|
|
|
lrg_buf_cb->skb = netdev_alloc_skb(qdev->ndev,
|
|
|
|
qdev->lrg_buffer_len);
|
2006-07-25 15:40:21 +08:00
|
|
|
if (unlikely(!lrg_buf_cb->skb)) {
|
2007-02-27 03:06:33 +08:00
|
|
|
printk(KERN_ERR PFX "%s: failed netdev_alloc_skb().\n",
|
2006-07-25 15:40:21 +08:00
|
|
|
qdev->ndev->name);
|
|
|
|
qdev->lrg_buf_skb_check++;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We save some space to copy the ethhdr from first
|
|
|
|
* buffer
|
|
|
|
*/
|
|
|
|
skb_reserve(lrg_buf_cb->skb, QL_HEADER_SPACE);
|
|
|
|
map = pci_map_single(qdev->pdev,
|
|
|
|
lrg_buf_cb->skb->data,
|
|
|
|
qdev->lrg_buffer_len -
|
|
|
|
QL_HEADER_SPACE,
|
|
|
|
PCI_DMA_FROMDEVICE);
|
2007-02-27 03:06:40 +08:00
|
|
|
err = pci_dma_mapping_error(map);
|
|
|
|
if(err) {
|
|
|
|
printk(KERN_ERR "%s: PCI mapping failed with error: %d\n",
|
|
|
|
qdev->ndev->name, err);
|
|
|
|
dev_kfree_skb(lrg_buf_cb->skb);
|
|
|
|
lrg_buf_cb->skb = NULL;
|
|
|
|
|
|
|
|
qdev->lrg_buf_skb_check++;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-07-25 15:40:21 +08:00
|
|
|
lrg_buf_cb->buf_phy_addr_low =
|
|
|
|
cpu_to_le32(LS_64BITS(map));
|
|
|
|
lrg_buf_cb->buf_phy_addr_high =
|
|
|
|
cpu_to_le32(MS_64BITS(map));
|
|
|
|
pci_unmap_addr_set(lrg_buf_cb, mapaddr, map);
|
|
|
|
pci_unmap_len_set(lrg_buf_cb, maplen,
|
|
|
|
qdev->lrg_buffer_len -
|
|
|
|
QL_HEADER_SPACE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
qdev->lrg_buf_free_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ql_rcv_buf_cb *ql_get_from_lrg_buf_free_list(struct ql3_adapter
|
|
|
|
*qdev)
|
|
|
|
{
|
|
|
|
struct ql_rcv_buf_cb *lrg_buf_cb;
|
|
|
|
|
|
|
|
if ((lrg_buf_cb = qdev->lrg_buf_free_head) != NULL) {
|
|
|
|
if ((qdev->lrg_buf_free_head = lrg_buf_cb->next) == NULL)
|
|
|
|
qdev->lrg_buf_free_tail = NULL;
|
|
|
|
qdev->lrg_buf_free_count--;
|
|
|
|
}
|
|
|
|
|
|
|
|
return lrg_buf_cb;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 addrBits = EEPROM_NO_ADDR_BITS;
|
|
|
|
static u32 dataBits = EEPROM_NO_DATA_BITS;
|
|
|
|
|
|
|
|
static void fm93c56a_deselect(struct ql3_adapter *qdev);
|
|
|
|
static void eeprom_readword(struct ql3_adapter *qdev, u32 eepromAddr,
|
|
|
|
unsigned short *value);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static void fm93c56a_select(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
|
|
|
|
qdev->eeprom_cmd_data = AUBURN_EEPROM_CS_1;
|
2007-01-04 08:26:07 +08:00
|
|
|
ql_write_nvram_reg(qdev, &port_regs->CommonRegs.serialPortInterfaceReg,
|
2006-07-25 15:40:21 +08:00
|
|
|
ISP_NVRAM_MASK | qdev->eeprom_cmd_data);
|
2007-01-04 08:26:07 +08:00
|
|
|
ql_write_nvram_reg(qdev, &port_regs->CommonRegs.serialPortInterfaceReg,
|
2006-07-25 15:40:21 +08:00
|
|
|
((ISP_NVRAM_MASK << 16) | qdev->eeprom_cmd_data));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static void fm93c56a_cmd(struct ql3_adapter *qdev, u32 cmd, u32 eepromAddr)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
u32 mask;
|
|
|
|
u32 dataBit;
|
|
|
|
u32 previousBit;
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
|
|
|
|
/* Clock in a zero, then do the start bit */
|
2007-01-04 08:26:07 +08:00
|
|
|
ql_write_nvram_reg(qdev, &port_regs->CommonRegs.serialPortInterfaceReg,
|
2006-07-25 15:40:21 +08:00
|
|
|
ISP_NVRAM_MASK | qdev->eeprom_cmd_data |
|
|
|
|
AUBURN_EEPROM_DO_1);
|
2007-01-04 08:26:07 +08:00
|
|
|
ql_write_nvram_reg(qdev, &port_regs->CommonRegs.serialPortInterfaceReg,
|
2006-07-25 15:40:21 +08:00
|
|
|
ISP_NVRAM_MASK | qdev->
|
|
|
|
eeprom_cmd_data | AUBURN_EEPROM_DO_1 |
|
|
|
|
AUBURN_EEPROM_CLK_RISE);
|
2007-01-04 08:26:07 +08:00
|
|
|
ql_write_nvram_reg(qdev, &port_regs->CommonRegs.serialPortInterfaceReg,
|
2006-07-25 15:40:21 +08:00
|
|
|
ISP_NVRAM_MASK | qdev->
|
|
|
|
eeprom_cmd_data | AUBURN_EEPROM_DO_1 |
|
|
|
|
AUBURN_EEPROM_CLK_FALL);
|
|
|
|
|
|
|
|
mask = 1 << (FM93C56A_CMD_BITS - 1);
|
|
|
|
/* Force the previous data bit to be different */
|
|
|
|
previousBit = 0xffff;
|
|
|
|
for (i = 0; i < FM93C56A_CMD_BITS; i++) {
|
|
|
|
dataBit =
|
|
|
|
(cmd & mask) ? AUBURN_EEPROM_DO_1 : AUBURN_EEPROM_DO_0;
|
|
|
|
if (previousBit != dataBit) {
|
|
|
|
/*
|
|
|
|
* If the bit changed, then change the DO state to
|
|
|
|
* match
|
|
|
|
*/
|
2007-01-04 08:26:07 +08:00
|
|
|
ql_write_nvram_reg(qdev,
|
2006-07-25 15:40:21 +08:00
|
|
|
&port_regs->CommonRegs.
|
|
|
|
serialPortInterfaceReg,
|
|
|
|
ISP_NVRAM_MASK | qdev->
|
|
|
|
eeprom_cmd_data | dataBit);
|
|
|
|
previousBit = dataBit;
|
|
|
|
}
|
2007-01-04 08:26:07 +08:00
|
|
|
ql_write_nvram_reg(qdev,
|
2006-07-25 15:40:21 +08:00
|
|
|
&port_regs->CommonRegs.
|
|
|
|
serialPortInterfaceReg,
|
|
|
|
ISP_NVRAM_MASK | qdev->
|
|
|
|
eeprom_cmd_data | dataBit |
|
|
|
|
AUBURN_EEPROM_CLK_RISE);
|
2007-01-04 08:26:07 +08:00
|
|
|
ql_write_nvram_reg(qdev,
|
2006-07-25 15:40:21 +08:00
|
|
|
&port_regs->CommonRegs.
|
|
|
|
serialPortInterfaceReg,
|
|
|
|
ISP_NVRAM_MASK | qdev->
|
|
|
|
eeprom_cmd_data | dataBit |
|
|
|
|
AUBURN_EEPROM_CLK_FALL);
|
|
|
|
cmd = cmd << 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
mask = 1 << (addrBits - 1);
|
|
|
|
/* Force the previous data bit to be different */
|
|
|
|
previousBit = 0xffff;
|
|
|
|
for (i = 0; i < addrBits; i++) {
|
|
|
|
dataBit =
|
|
|
|
(eepromAddr & mask) ? AUBURN_EEPROM_DO_1 :
|
|
|
|
AUBURN_EEPROM_DO_0;
|
|
|
|
if (previousBit != dataBit) {
|
|
|
|
/*
|
|
|
|
* If the bit changed, then change the DO state to
|
|
|
|
* match
|
|
|
|
*/
|
2007-01-04 08:26:07 +08:00
|
|
|
ql_write_nvram_reg(qdev,
|
2006-07-25 15:40:21 +08:00
|
|
|
&port_regs->CommonRegs.
|
|
|
|
serialPortInterfaceReg,
|
|
|
|
ISP_NVRAM_MASK | qdev->
|
|
|
|
eeprom_cmd_data | dataBit);
|
|
|
|
previousBit = dataBit;
|
|
|
|
}
|
2007-01-04 08:26:07 +08:00
|
|
|
ql_write_nvram_reg(qdev,
|
2006-07-25 15:40:21 +08:00
|
|
|
&port_regs->CommonRegs.
|
|
|
|
serialPortInterfaceReg,
|
|
|
|
ISP_NVRAM_MASK | qdev->
|
|
|
|
eeprom_cmd_data | dataBit |
|
|
|
|
AUBURN_EEPROM_CLK_RISE);
|
2007-01-04 08:26:07 +08:00
|
|
|
ql_write_nvram_reg(qdev,
|
2006-07-25 15:40:21 +08:00
|
|
|
&port_regs->CommonRegs.
|
|
|
|
serialPortInterfaceReg,
|
|
|
|
ISP_NVRAM_MASK | qdev->
|
|
|
|
eeprom_cmd_data | dataBit |
|
|
|
|
AUBURN_EEPROM_CLK_FALL);
|
|
|
|
eepromAddr = eepromAddr << 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static void fm93c56a_deselect(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
qdev->eeprom_cmd_data = AUBURN_EEPROM_CS_0;
|
2007-01-04 08:26:07 +08:00
|
|
|
ql_write_nvram_reg(qdev, &port_regs->CommonRegs.serialPortInterfaceReg,
|
2006-07-25 15:40:21 +08:00
|
|
|
ISP_NVRAM_MASK | qdev->eeprom_cmd_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static void fm93c56a_datain(struct ql3_adapter *qdev, unsigned short *value)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
u32 data = 0;
|
|
|
|
u32 dataBit;
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
|
|
|
|
/* Read the data bits */
|
|
|
|
/* The first bit is a dummy. Clock right over it. */
|
|
|
|
for (i = 0; i < dataBits; i++) {
|
2007-01-04 08:26:07 +08:00
|
|
|
ql_write_nvram_reg(qdev,
|
2006-07-25 15:40:21 +08:00
|
|
|
&port_regs->CommonRegs.
|
|
|
|
serialPortInterfaceReg,
|
|
|
|
ISP_NVRAM_MASK | qdev->eeprom_cmd_data |
|
|
|
|
AUBURN_EEPROM_CLK_RISE);
|
2007-01-04 08:26:07 +08:00
|
|
|
ql_write_nvram_reg(qdev,
|
2006-07-25 15:40:21 +08:00
|
|
|
&port_regs->CommonRegs.
|
|
|
|
serialPortInterfaceReg,
|
|
|
|
ISP_NVRAM_MASK | qdev->eeprom_cmd_data |
|
|
|
|
AUBURN_EEPROM_CLK_FALL);
|
|
|
|
dataBit =
|
|
|
|
(ql_read_common_reg
|
|
|
|
(qdev,
|
|
|
|
&port_regs->CommonRegs.
|
|
|
|
serialPortInterfaceReg) & AUBURN_EEPROM_DI_1) ? 1 : 0;
|
|
|
|
data = (data << 1) | dataBit;
|
|
|
|
}
|
|
|
|
*value = (u16) data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static void eeprom_readword(struct ql3_adapter *qdev,
|
|
|
|
u32 eepromAddr, unsigned short *value)
|
|
|
|
{
|
|
|
|
fm93c56a_select(qdev);
|
|
|
|
fm93c56a_cmd(qdev, (int)FM93C56A_READ, eepromAddr);
|
|
|
|
fm93c56a_datain(qdev, value);
|
|
|
|
fm93c56a_deselect(qdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_swap_mac_addr(u8 * macAddress)
|
|
|
|
{
|
|
|
|
#ifdef __BIG_ENDIAN
|
|
|
|
u8 temp;
|
|
|
|
temp = macAddress[0];
|
|
|
|
macAddress[0] = macAddress[1];
|
|
|
|
macAddress[1] = temp;
|
|
|
|
temp = macAddress[2];
|
|
|
|
macAddress[2] = macAddress[3];
|
|
|
|
macAddress[3] = temp;
|
|
|
|
temp = macAddress[4];
|
|
|
|
macAddress[4] = macAddress[5];
|
|
|
|
macAddress[5] = temp;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_get_nvram_params(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
u16 *pEEPROMData;
|
|
|
|
u16 checksum = 0;
|
|
|
|
u32 index;
|
|
|
|
unsigned long hw_flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
|
|
|
|
|
|
|
|
pEEPROMData = (u16 *) & qdev->nvram_data;
|
|
|
|
qdev->eeprom_cmd_data = 0;
|
|
|
|
if(ql_sem_spinlock(qdev, QL_NVRAM_SEM_MASK,
|
|
|
|
(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) *
|
|
|
|
2) << 10)) {
|
|
|
|
printk(KERN_ERR PFX"%s: Failed ql_sem_spinlock().\n",
|
|
|
|
__func__);
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (index = 0; index < EEPROM_SIZE; index++) {
|
|
|
|
eeprom_readword(qdev, index, pEEPROMData);
|
|
|
|
checksum += *pEEPROMData;
|
|
|
|
pEEPROMData++;
|
|
|
|
}
|
|
|
|
ql_sem_unlock(qdev, QL_NVRAM_SEM_MASK);
|
|
|
|
|
|
|
|
if (checksum != 0) {
|
|
|
|
printk(KERN_ERR PFX "%s: checksum should be zero, is %x!!\n",
|
|
|
|
qdev->ndev->name, checksum);
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We have a problem with endianness for the MAC addresses
|
|
|
|
* and the two 8-bit values version, and numPorts. We
|
|
|
|
* have to swap them on big endian systems.
|
|
|
|
*/
|
|
|
|
ql_swap_mac_addr(qdev->nvram_data.funcCfg_fn0.macAddress);
|
|
|
|
ql_swap_mac_addr(qdev->nvram_data.funcCfg_fn1.macAddress);
|
|
|
|
ql_swap_mac_addr(qdev->nvram_data.funcCfg_fn2.macAddress);
|
|
|
|
ql_swap_mac_addr(qdev->nvram_data.funcCfg_fn3.macAddress);
|
|
|
|
pEEPROMData = (u16 *) & qdev->nvram_data.version;
|
|
|
|
*pEEPROMData = le16_to_cpu(*pEEPROMData);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
|
|
return checksum;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const u32 PHYAddr[2] = {
|
|
|
|
PORT0_PHY_ADDRESS, PORT1_PHY_ADDRESS
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ql_wait_for_mii_ready(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
u32 temp;
|
|
|
|
int count = 1000;
|
|
|
|
|
|
|
|
while (count) {
|
|
|
|
temp = ql_read_page0_reg(qdev, &port_regs->macMIIStatusReg);
|
|
|
|
if (!(temp & MAC_MII_STATUS_BSY))
|
|
|
|
return 0;
|
|
|
|
udelay(10);
|
|
|
|
count--;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_mii_enable_scan_mode(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
u32 scanControl;
|
|
|
|
|
|
|
|
if (qdev->numPorts > 1) {
|
|
|
|
/* Auto scan will cycle through multiple ports */
|
|
|
|
scanControl = MAC_MII_CONTROL_AS | MAC_MII_CONTROL_SC;
|
|
|
|
} else {
|
|
|
|
scanControl = MAC_MII_CONTROL_SC;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan register 1 of PHY/PETBI,
|
|
|
|
* Set up to scan both devices
|
|
|
|
* The autoscan starts from the first register, completes
|
|
|
|
* the last one before rolling over to the first
|
|
|
|
*/
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg,
|
|
|
|
PHYAddr[0] | MII_SCAN_REGISTER);
|
|
|
|
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg,
|
|
|
|
(scanControl) |
|
|
|
|
((MAC_MII_CONTROL_SC | MAC_MII_CONTROL_AS) << 16));
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8 ql_mii_disable_scan_mode(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
u8 ret;
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
|
|
|
|
/* See if scan mode is enabled before we turn it off */
|
|
|
|
if (ql_read_page0_reg(qdev, &port_regs->macMIIMgmtControlReg) &
|
|
|
|
(MAC_MII_CONTROL_AS | MAC_MII_CONTROL_SC)) {
|
|
|
|
/* Scan is enabled */
|
|
|
|
ret = 1;
|
|
|
|
} else {
|
|
|
|
/* Scan is disabled */
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When disabling scan mode you must first change the MII register
|
|
|
|
* address
|
|
|
|
*/
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg,
|
|
|
|
PHYAddr[0] | MII_SCAN_REGISTER);
|
|
|
|
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg,
|
|
|
|
((MAC_MII_CONTROL_SC | MAC_MII_CONTROL_AS |
|
|
|
|
MAC_MII_CONTROL_RC) << 16));
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_mii_write_reg_ex(struct ql3_adapter *qdev,
|
2007-03-27 03:43:52 +08:00
|
|
|
u16 regAddr, u16 value, u32 phyAddr)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
u8 scanWasEnabled;
|
|
|
|
|
|
|
|
scanWasEnabled = ql_mii_disable_scan_mode(qdev);
|
|
|
|
|
|
|
|
if (ql_wait_for_mii_ready(qdev)) {
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_WARNING PFX
|
|
|
|
"%s Timed out waiting for management port to "
|
|
|
|
"get free before issuing command.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg,
|
2007-03-27 03:43:52 +08:00
|
|
|
phyAddr | regAddr);
|
2006-07-25 15:40:21 +08:00
|
|
|
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macMIIMgmtDataReg, value);
|
|
|
|
|
|
|
|
/* Wait for write to complete 9/10/04 SJP */
|
|
|
|
if (ql_wait_for_mii_ready(qdev)) {
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_WARNING PFX
|
|
|
|
"%s: Timed out waiting for management port to"
|
|
|
|
"get free before issuing command.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (scanWasEnabled)
|
|
|
|
ql_mii_enable_scan_mode(qdev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_mii_read_reg_ex(struct ql3_adapter *qdev, u16 regAddr,
|
2007-03-27 03:43:52 +08:00
|
|
|
u16 * value, u32 phyAddr)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
u8 scanWasEnabled;
|
|
|
|
u32 temp;
|
|
|
|
|
|
|
|
scanWasEnabled = ql_mii_disable_scan_mode(qdev);
|
|
|
|
|
|
|
|
if (ql_wait_for_mii_ready(qdev)) {
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_WARNING PFX
|
|
|
|
"%s: Timed out waiting for management port to "
|
|
|
|
"get free before issuing command.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg,
|
2007-03-27 03:43:52 +08:00
|
|
|
phyAddr | regAddr);
|
2006-07-25 15:40:21 +08:00
|
|
|
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg,
|
|
|
|
(MAC_MII_CONTROL_RC << 16));
|
|
|
|
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg,
|
|
|
|
(MAC_MII_CONTROL_RC << 16) | MAC_MII_CONTROL_RC);
|
|
|
|
|
|
|
|
/* Wait for the read to complete */
|
|
|
|
if (ql_wait_for_mii_ready(qdev)) {
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_WARNING PFX
|
|
|
|
"%s: Timed out waiting for management port to "
|
|
|
|
"get free after issuing command.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
temp = ql_read_page0_reg(qdev, &port_regs->macMIIMgmtDataReg);
|
|
|
|
*value = (u16) temp;
|
|
|
|
|
|
|
|
if (scanWasEnabled)
|
|
|
|
ql_mii_enable_scan_mode(qdev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_mii_write_reg(struct ql3_adapter *qdev, u16 regAddr, u16 value)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
|
|
|
|
ql_mii_disable_scan_mode(qdev);
|
|
|
|
|
|
|
|
if (ql_wait_for_mii_ready(qdev)) {
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_WARNING PFX
|
|
|
|
"%s: Timed out waiting for management port to "
|
|
|
|
"get free before issuing command.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg,
|
|
|
|
qdev->PHYAddr | regAddr);
|
|
|
|
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macMIIMgmtDataReg, value);
|
|
|
|
|
|
|
|
/* Wait for write to complete. */
|
|
|
|
if (ql_wait_for_mii_ready(qdev)) {
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_WARNING PFX
|
|
|
|
"%s: Timed out waiting for management port to "
|
|
|
|
"get free before issuing command.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ql_mii_enable_scan_mode(qdev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_mii_read_reg(struct ql3_adapter *qdev, u16 regAddr, u16 *value)
|
|
|
|
{
|
|
|
|
u32 temp;
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
|
|
|
|
ql_mii_disable_scan_mode(qdev);
|
|
|
|
|
|
|
|
if (ql_wait_for_mii_ready(qdev)) {
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_WARNING PFX
|
|
|
|
"%s: Timed out waiting for management port to "
|
|
|
|
"get free before issuing command.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg,
|
|
|
|
qdev->PHYAddr | regAddr);
|
|
|
|
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg,
|
|
|
|
(MAC_MII_CONTROL_RC << 16));
|
|
|
|
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg,
|
|
|
|
(MAC_MII_CONTROL_RC << 16) | MAC_MII_CONTROL_RC);
|
|
|
|
|
|
|
|
/* Wait for the read to complete */
|
|
|
|
if (ql_wait_for_mii_ready(qdev)) {
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_WARNING PFX
|
|
|
|
"%s: Timed out waiting for management port to "
|
|
|
|
"get free before issuing command.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
temp = ql_read_page0_reg(qdev, &port_regs->macMIIMgmtDataReg);
|
|
|
|
*value = (u16) temp;
|
|
|
|
|
|
|
|
ql_mii_enable_scan_mode(qdev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_petbi_reset(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
ql_mii_write_reg(qdev, PETBI_CONTROL_REG, PETBI_CTRL_SOFT_RESET);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_petbi_start_neg(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
|
|
|
|
/* Enable Auto-negotiation sense */
|
|
|
|
ql_mii_read_reg(qdev, PETBI_TBI_CTRL, ®);
|
|
|
|
reg |= PETBI_TBI_AUTO_SENSE;
|
|
|
|
ql_mii_write_reg(qdev, PETBI_TBI_CTRL, reg);
|
|
|
|
|
|
|
|
ql_mii_write_reg(qdev, PETBI_NEG_ADVER,
|
|
|
|
PETBI_NEG_PAUSE | PETBI_NEG_DUPLEX);
|
|
|
|
|
|
|
|
ql_mii_write_reg(qdev, PETBI_CONTROL_REG,
|
|
|
|
PETBI_CTRL_AUTO_NEG | PETBI_CTRL_RESTART_NEG |
|
|
|
|
PETBI_CTRL_FULL_DUPLEX | PETBI_CTRL_SPEED_1000);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2007-03-27 03:43:52 +08:00
|
|
|
static void ql_petbi_reset_ex(struct ql3_adapter *qdev)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
|
|
|
ql_mii_write_reg_ex(qdev, PETBI_CONTROL_REG, PETBI_CTRL_SOFT_RESET,
|
2007-03-27 03:43:52 +08:00
|
|
|
PHYAddr[qdev->mac_index]);
|
2006-07-25 15:40:21 +08:00
|
|
|
}
|
|
|
|
|
2007-03-27 03:43:52 +08:00
|
|
|
static void ql_petbi_start_neg_ex(struct ql3_adapter *qdev)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
|
|
|
|
/* Enable Auto-negotiation sense */
|
2007-03-27 03:43:52 +08:00
|
|
|
ql_mii_read_reg_ex(qdev, PETBI_TBI_CTRL, ®,
|
|
|
|
PHYAddr[qdev->mac_index]);
|
2006-07-25 15:40:21 +08:00
|
|
|
reg |= PETBI_TBI_AUTO_SENSE;
|
2007-03-27 03:43:52 +08:00
|
|
|
ql_mii_write_reg_ex(qdev, PETBI_TBI_CTRL, reg,
|
|
|
|
PHYAddr[qdev->mac_index]);
|
2006-07-25 15:40:21 +08:00
|
|
|
|
|
|
|
ql_mii_write_reg_ex(qdev, PETBI_NEG_ADVER,
|
2007-03-27 03:43:52 +08:00
|
|
|
PETBI_NEG_PAUSE | PETBI_NEG_DUPLEX,
|
|
|
|
PHYAddr[qdev->mac_index]);
|
2006-07-25 15:40:21 +08:00
|
|
|
|
|
|
|
ql_mii_write_reg_ex(qdev, PETBI_CONTROL_REG,
|
|
|
|
PETBI_CTRL_AUTO_NEG | PETBI_CTRL_RESTART_NEG |
|
|
|
|
PETBI_CTRL_FULL_DUPLEX | PETBI_CTRL_SPEED_1000,
|
2007-03-27 03:43:52 +08:00
|
|
|
PHYAddr[qdev->mac_index]);
|
2006-07-25 15:40:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_petbi_init(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
ql_petbi_reset(qdev);
|
|
|
|
ql_petbi_start_neg(qdev);
|
|
|
|
}
|
|
|
|
|
2007-03-27 03:43:52 +08:00
|
|
|
static void ql_petbi_init_ex(struct ql3_adapter *qdev)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
2007-03-27 03:43:52 +08:00
|
|
|
ql_petbi_reset_ex(qdev);
|
|
|
|
ql_petbi_start_neg_ex(qdev);
|
2006-07-25 15:40:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_is_petbi_neg_pause(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
|
|
|
|
if (ql_mii_read_reg(qdev, PETBI_NEG_PARTNER, ®) < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return (reg & PETBI_NEG_PAUSE_MASK) == PETBI_NEG_PAUSE;
|
|
|
|
}
|
|
|
|
|
2007-03-27 03:43:52 +08:00
|
|
|
static void phyAgereSpecificInit(struct ql3_adapter *qdev, u32 miiAddr)
|
|
|
|
{
|
|
|
|
printk(KERN_INFO "%s: enabling Agere specific PHY\n", qdev->ndev->name);
|
|
|
|
/* power down device bit 11 = 1 */
|
|
|
|
ql_mii_write_reg_ex(qdev, 0x00, 0x1940, miiAddr);
|
|
|
|
/* enable diagnostic mode bit 2 = 1 */
|
|
|
|
ql_mii_write_reg_ex(qdev, 0x12, 0x840e, miiAddr);
|
|
|
|
/* 1000MB amplitude adjust (see Agere errata) */
|
|
|
|
ql_mii_write_reg_ex(qdev, 0x10, 0x8805, miiAddr);
|
|
|
|
/* 1000MB amplitude adjust (see Agere errata) */
|
|
|
|
ql_mii_write_reg_ex(qdev, 0x11, 0xf03e, miiAddr);
|
|
|
|
/* 100MB amplitude adjust (see Agere errata) */
|
|
|
|
ql_mii_write_reg_ex(qdev, 0x10, 0x8806, miiAddr);
|
|
|
|
/* 100MB amplitude adjust (see Agere errata) */
|
|
|
|
ql_mii_write_reg_ex(qdev, 0x11, 0x003e, miiAddr);
|
|
|
|
/* 10MB amplitude adjust (see Agere errata) */
|
|
|
|
ql_mii_write_reg_ex(qdev, 0x10, 0x8807, miiAddr);
|
|
|
|
/* 10MB amplitude adjust (see Agere errata) */
|
|
|
|
ql_mii_write_reg_ex(qdev, 0x11, 0x1f00, miiAddr);
|
|
|
|
/* point to hidden reg 0x2806 */
|
|
|
|
ql_mii_write_reg_ex(qdev, 0x10, 0x2806, miiAddr);
|
|
|
|
/* Write new PHYAD w/bit 5 set */
|
|
|
|
ql_mii_write_reg_ex(qdev, 0x11, 0x0020 | (PHYAddr[qdev->mac_index] >> 8), miiAddr);
|
|
|
|
/*
|
|
|
|
* Disable diagnostic mode bit 2 = 0
|
|
|
|
* Power up device bit 11 = 0
|
|
|
|
* Link up (on) and activity (blink)
|
|
|
|
*/
|
|
|
|
ql_mii_write_reg(qdev, 0x12, 0x840a);
|
|
|
|
ql_mii_write_reg(qdev, 0x00, 0x1140);
|
|
|
|
ql_mii_write_reg(qdev, 0x1c, 0xfaf0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PHY_DEVICE_et getPhyType (struct ql3_adapter *qdev,
|
|
|
|
u16 phyIdReg0, u16 phyIdReg1)
|
|
|
|
{
|
|
|
|
PHY_DEVICE_et result = PHY_TYPE_UNKNOWN;
|
|
|
|
u32 oui;
|
|
|
|
u16 model;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (phyIdReg0 == 0xffff) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (phyIdReg1 == 0xffff) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* oui is split between two registers */
|
|
|
|
oui = (phyIdReg0 << 6) | ((phyIdReg1 & PHY_OUI_1_MASK) >> 10);
|
|
|
|
|
|
|
|
model = (phyIdReg1 & PHY_MODEL_MASK) >> 4;
|
|
|
|
|
|
|
|
/* Scan table for this PHY */
|
|
|
|
for(i = 0; i < MAX_PHY_DEV_TYPES; i++) {
|
|
|
|
if ((oui == PHY_DEVICES[i].phyIdOUI) && (model == PHY_DEVICES[i].phyIdModel))
|
|
|
|
{
|
|
|
|
result = PHY_DEVICES[i].phyDevice;
|
|
|
|
|
|
|
|
printk(KERN_INFO "%s: Phy: %s\n",
|
|
|
|
qdev->ndev->name, PHY_DEVICES[i].name);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2006-07-25 15:40:21 +08:00
|
|
|
static int ql_phy_get_speed(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
|
2007-03-27 03:43:52 +08:00
|
|
|
switch(qdev->phyType) {
|
|
|
|
case PHY_AGERE_ET1011C:
|
|
|
|
{
|
|
|
|
if (ql_mii_read_reg(qdev, 0x1A, ®) < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
reg = (reg >> 8) & 3;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
2006-07-25 15:40:21 +08:00
|
|
|
if (ql_mii_read_reg(qdev, AUX_CONTROL_STATUS, ®) < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
reg = (((reg & 0x18) >> 3) & 3);
|
2007-03-27 03:43:52 +08:00
|
|
|
}
|
2006-07-25 15:40:21 +08:00
|
|
|
|
2007-03-27 03:43:52 +08:00
|
|
|
switch(reg) {
|
|
|
|
case 2:
|
2006-07-25 15:40:21 +08:00
|
|
|
return SPEED_1000;
|
2007-03-27 03:43:52 +08:00
|
|
|
case 1:
|
2006-07-25 15:40:21 +08:00
|
|
|
return SPEED_100;
|
2007-03-27 03:43:52 +08:00
|
|
|
case 0:
|
2006-07-25 15:40:21 +08:00
|
|
|
return SPEED_10;
|
2007-03-27 03:43:52 +08:00
|
|
|
default:
|
2006-07-25 15:40:21 +08:00
|
|
|
return -1;
|
2007-03-27 03:43:52 +08:00
|
|
|
}
|
2006-07-25 15:40:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_is_full_dup(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
|
2007-03-27 03:43:52 +08:00
|
|
|
switch(qdev->phyType) {
|
|
|
|
case PHY_AGERE_ET1011C:
|
|
|
|
{
|
|
|
|
if (ql_mii_read_reg(qdev, 0x1A, ®))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return ((reg & 0x0080) && (reg & 0x1000)) != 0;
|
|
|
|
}
|
|
|
|
case PHY_VITESSE_VSC8211:
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
if (ql_mii_read_reg(qdev, AUX_CONTROL_STATUS, ®) < 0)
|
|
|
|
return 0;
|
|
|
|
return (reg & PHY_AUX_DUPLEX_STAT) != 0;
|
|
|
|
}
|
|
|
|
}
|
2006-07-25 15:40:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_is_phy_neg_pause(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
|
|
|
|
if (ql_mii_read_reg(qdev, PHY_NEG_PARTNER, ®) < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return (reg & PHY_NEG_PAUSE) != 0;
|
|
|
|
}
|
|
|
|
|
2007-03-27 03:43:52 +08:00
|
|
|
static int PHY_Setup(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
u16 reg1;
|
|
|
|
u16 reg2;
|
|
|
|
bool agereAddrChangeNeeded = false;
|
|
|
|
u32 miiAddr = 0;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* Determine the PHY we are using by reading the ID's */
|
|
|
|
err = ql_mii_read_reg(qdev, PHY_ID_0_REG, ®1);
|
|
|
|
if(err != 0) {
|
|
|
|
printk(KERN_ERR "%s: Could not read from reg PHY_ID_0_REG\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = ql_mii_read_reg(qdev, PHY_ID_1_REG, ®2);
|
|
|
|
if(err != 0) {
|
|
|
|
printk(KERN_ERR "%s: Could not read from reg PHY_ID_0_REG\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if we have a Agere PHY */
|
|
|
|
if ((reg1 == 0xffff) || (reg2 == 0xffff)) {
|
|
|
|
|
|
|
|
/* Determine which MII address we should be using
|
|
|
|
determined by the index of the card */
|
|
|
|
if (qdev->mac_index == 0) {
|
|
|
|
miiAddr = MII_AGERE_ADDR_1;
|
|
|
|
} else {
|
|
|
|
miiAddr = MII_AGERE_ADDR_2;
|
|
|
|
}
|
|
|
|
|
|
|
|
err =ql_mii_read_reg_ex(qdev, PHY_ID_0_REG, ®1, miiAddr);
|
|
|
|
if(err != 0) {
|
|
|
|
printk(KERN_ERR "%s: Could not read from reg PHY_ID_0_REG after Agere detected\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = ql_mii_read_reg_ex(qdev, PHY_ID_1_REG, ®2, miiAddr);
|
|
|
|
if(err != 0) {
|
|
|
|
printk(KERN_ERR "%s: Could not read from reg PHY_ID_0_REG after Agere detected\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We need to remember to initialize the Agere PHY */
|
|
|
|
agereAddrChangeNeeded = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Determine the particular PHY we have on board to apply
|
|
|
|
PHY specific initializations */
|
|
|
|
qdev->phyType = getPhyType(qdev, reg1, reg2);
|
|
|
|
|
|
|
|
if ((qdev->phyType == PHY_AGERE_ET1011C) && agereAddrChangeNeeded) {
|
|
|
|
/* need this here so address gets changed */
|
|
|
|
phyAgereSpecificInit(qdev, miiAddr);
|
|
|
|
} else if (qdev->phyType == PHY_TYPE_UNKNOWN) {
|
|
|
|
printk(KERN_ERR "%s: PHY is unknown\n", qdev->ndev->name);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-07-25 15:40:21 +08:00
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static void ql_mac_enable(struct ql3_adapter *qdev, u32 enable)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
u32 value;
|
|
|
|
|
|
|
|
if (enable)
|
|
|
|
value = (MAC_CONFIG_REG_PE | (MAC_CONFIG_REG_PE << 16));
|
|
|
|
else
|
|
|
|
value = (MAC_CONFIG_REG_PE << 16);
|
|
|
|
|
|
|
|
if (qdev->mac_index)
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->mac1ConfigReg, value);
|
|
|
|
else
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->mac0ConfigReg, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static void ql_mac_cfg_soft_reset(struct ql3_adapter *qdev, u32 enable)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
u32 value;
|
|
|
|
|
|
|
|
if (enable)
|
|
|
|
value = (MAC_CONFIG_REG_SR | (MAC_CONFIG_REG_SR << 16));
|
|
|
|
else
|
|
|
|
value = (MAC_CONFIG_REG_SR << 16);
|
|
|
|
|
|
|
|
if (qdev->mac_index)
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->mac1ConfigReg, value);
|
|
|
|
else
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->mac0ConfigReg, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static void ql_mac_cfg_gig(struct ql3_adapter *qdev, u32 enable)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
u32 value;
|
|
|
|
|
|
|
|
if (enable)
|
|
|
|
value = (MAC_CONFIG_REG_GM | (MAC_CONFIG_REG_GM << 16));
|
|
|
|
else
|
|
|
|
value = (MAC_CONFIG_REG_GM << 16);
|
|
|
|
|
|
|
|
if (qdev->mac_index)
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->mac1ConfigReg, value);
|
|
|
|
else
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->mac0ConfigReg, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static void ql_mac_cfg_full_dup(struct ql3_adapter *qdev, u32 enable)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
u32 value;
|
|
|
|
|
|
|
|
if (enable)
|
|
|
|
value = (MAC_CONFIG_REG_FD | (MAC_CONFIG_REG_FD << 16));
|
|
|
|
else
|
|
|
|
value = (MAC_CONFIG_REG_FD << 16);
|
|
|
|
|
|
|
|
if (qdev->mac_index)
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->mac1ConfigReg, value);
|
|
|
|
else
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->mac0ConfigReg, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static void ql_mac_cfg_pause(struct ql3_adapter *qdev, u32 enable)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
u32 value;
|
|
|
|
|
|
|
|
if (enable)
|
|
|
|
value =
|
|
|
|
((MAC_CONFIG_REG_TF | MAC_CONFIG_REG_RF) |
|
|
|
|
((MAC_CONFIG_REG_TF | MAC_CONFIG_REG_RF) << 16));
|
|
|
|
else
|
|
|
|
value = ((MAC_CONFIG_REG_TF | MAC_CONFIG_REG_RF) << 16);
|
|
|
|
|
|
|
|
if (qdev->mac_index)
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->mac1ConfigReg, value);
|
|
|
|
else
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->mac0ConfigReg, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static int ql_is_fiber(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
u32 bitToCheck = 0;
|
|
|
|
u32 temp;
|
|
|
|
|
|
|
|
switch (qdev->mac_index) {
|
|
|
|
case 0:
|
|
|
|
bitToCheck = PORT_STATUS_SM0;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
bitToCheck = PORT_STATUS_SM1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
temp = ql_read_page0_reg(qdev, &port_regs->portStatus);
|
|
|
|
return (temp & bitToCheck) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_is_auto_cfg(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
ql_mii_read_reg(qdev, 0x00, ®);
|
|
|
|
return (reg & 0x1000) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static int ql_is_auto_neg_complete(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
u32 bitToCheck = 0;
|
|
|
|
u32 temp;
|
|
|
|
|
|
|
|
switch (qdev->mac_index) {
|
|
|
|
case 0:
|
|
|
|
bitToCheck = PORT_STATUS_AC0;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
bitToCheck = PORT_STATUS_AC1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
temp = ql_read_page0_reg(qdev, &port_regs->portStatus);
|
|
|
|
if (temp & bitToCheck) {
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_INFO PFX
|
|
|
|
"%s: Auto-Negotiate complete.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_WARNING PFX
|
|
|
|
"%s: Auto-Negotiate incomplete.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ql_is_neg_pause() returns 1 if pause was negotiated to be on
|
|
|
|
*/
|
|
|
|
static int ql_is_neg_pause(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
if (ql_is_fiber(qdev))
|
|
|
|
return ql_is_petbi_neg_pause(qdev);
|
|
|
|
else
|
|
|
|
return ql_is_phy_neg_pause(qdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_auto_neg_error(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
u32 bitToCheck = 0;
|
|
|
|
u32 temp;
|
|
|
|
|
|
|
|
switch (qdev->mac_index) {
|
|
|
|
case 0:
|
|
|
|
bitToCheck = PORT_STATUS_AE0;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
bitToCheck = PORT_STATUS_AE1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
temp = ql_read_page0_reg(qdev, &port_regs->portStatus);
|
|
|
|
return (temp & bitToCheck) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 ql_get_link_speed(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
if (ql_is_fiber(qdev))
|
|
|
|
return SPEED_1000;
|
|
|
|
else
|
|
|
|
return ql_phy_get_speed(qdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_is_link_full_dup(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
if (ql_is_fiber(qdev))
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return ql_is_full_dup(qdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static int ql_link_down_detect(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
u32 bitToCheck = 0;
|
|
|
|
u32 temp;
|
|
|
|
|
|
|
|
switch (qdev->mac_index) {
|
|
|
|
case 0:
|
|
|
|
bitToCheck = ISP_CONTROL_LINK_DN_0;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
bitToCheck = ISP_CONTROL_LINK_DN_1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
temp =
|
|
|
|
ql_read_common_reg(qdev, &port_regs->CommonRegs.ispControlStatus);
|
|
|
|
return (temp & bitToCheck) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static int ql_link_down_detect_clear(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
|
|
|
|
switch (qdev->mac_index) {
|
|
|
|
case 0:
|
|
|
|
ql_write_common_reg(qdev,
|
|
|
|
&port_regs->CommonRegs.ispControlStatus,
|
|
|
|
(ISP_CONTROL_LINK_DN_0) |
|
|
|
|
(ISP_CONTROL_LINK_DN_0 << 16));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
ql_write_common_reg(qdev,
|
|
|
|
&port_regs->CommonRegs.ispControlStatus,
|
|
|
|
(ISP_CONTROL_LINK_DN_1) |
|
|
|
|
(ISP_CONTROL_LINK_DN_1 << 16));
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
2007-03-27 03:43:52 +08:00
|
|
|
static int ql_this_adapter_controls_port(struct ql3_adapter *qdev)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
u32 bitToCheck = 0;
|
|
|
|
u32 temp;
|
|
|
|
|
2007-03-27 03:43:52 +08:00
|
|
|
switch (qdev->mac_index) {
|
2006-07-25 15:40:21 +08:00
|
|
|
case 0:
|
|
|
|
bitToCheck = PORT_STATUS_F1_ENABLED;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
bitToCheck = PORT_STATUS_F3_ENABLED;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
temp = ql_read_page0_reg(qdev, &port_regs->portStatus);
|
|
|
|
if (temp & bitToCheck) {
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_DEBUG PFX
|
|
|
|
"%s: is not link master.\n", qdev->ndev->name);
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_DEBUG PFX
|
|
|
|
"%s: is link master.\n", qdev->ndev->name);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-27 03:43:52 +08:00
|
|
|
static void ql_phy_reset_ex(struct ql3_adapter *qdev)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
2007-03-27 03:43:52 +08:00
|
|
|
ql_mii_write_reg_ex(qdev, CONTROL_REG, PHY_CTRL_SOFT_RESET,
|
|
|
|
PHYAddr[qdev->mac_index]);
|
2006-07-25 15:40:21 +08:00
|
|
|
}
|
|
|
|
|
2007-03-27 03:43:52 +08:00
|
|
|
static void ql_phy_start_neg_ex(struct ql3_adapter *qdev)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
|
|
|
u16 reg;
|
2007-03-27 03:43:52 +08:00
|
|
|
u16 portConfiguration;
|
|
|
|
|
|
|
|
if(qdev->phyType == PHY_AGERE_ET1011C) {
|
|
|
|
/* turn off external loopback */
|
|
|
|
ql_mii_write_reg(qdev, 0x13, 0x0000);
|
|
|
|
}
|
2006-07-25 15:40:21 +08:00
|
|
|
|
2007-03-27 03:43:52 +08:00
|
|
|
if(qdev->mac_index == 0)
|
|
|
|
portConfiguration = qdev->nvram_data.macCfg_port0.portConfiguration;
|
|
|
|
else
|
|
|
|
portConfiguration = qdev->nvram_data.macCfg_port1.portConfiguration;
|
|
|
|
|
|
|
|
/* Some HBA's in the field are set to 0 and they need to
|
|
|
|
be reinterpreted with a default value */
|
|
|
|
if(portConfiguration == 0)
|
|
|
|
portConfiguration = PORT_CONFIG_DEFAULT;
|
|
|
|
|
|
|
|
/* Set the 1000 advertisements */
|
|
|
|
ql_mii_read_reg_ex(qdev, PHY_GIG_CONTROL, ®,
|
|
|
|
PHYAddr[qdev->mac_index]);
|
|
|
|
reg &= ~PHY_GIG_ALL_PARAMS;
|
|
|
|
|
|
|
|
if(portConfiguration &
|
|
|
|
PORT_CONFIG_FULL_DUPLEX_ENABLED &
|
|
|
|
PORT_CONFIG_1000MB_SPEED) {
|
|
|
|
reg |= PHY_GIG_ADV_1000F;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(portConfiguration &
|
|
|
|
PORT_CONFIG_HALF_DUPLEX_ENABLED &
|
|
|
|
PORT_CONFIG_1000MB_SPEED) {
|
|
|
|
reg |= PHY_GIG_ADV_1000H;
|
|
|
|
}
|
|
|
|
|
|
|
|
ql_mii_write_reg_ex(qdev, PHY_GIG_CONTROL, reg,
|
|
|
|
PHYAddr[qdev->mac_index]);
|
|
|
|
|
|
|
|
/* Set the 10/100 & pause negotiation advertisements */
|
|
|
|
ql_mii_read_reg_ex(qdev, PHY_NEG_ADVER, ®,
|
|
|
|
PHYAddr[qdev->mac_index]);
|
|
|
|
reg &= ~PHY_NEG_ALL_PARAMS;
|
|
|
|
|
|
|
|
if(portConfiguration & PORT_CONFIG_SYM_PAUSE_ENABLED)
|
|
|
|
reg |= PHY_NEG_ASY_PAUSE | PHY_NEG_SYM_PAUSE;
|
|
|
|
|
|
|
|
if(portConfiguration & PORT_CONFIG_FULL_DUPLEX_ENABLED) {
|
|
|
|
if(portConfiguration & PORT_CONFIG_100MB_SPEED)
|
|
|
|
reg |= PHY_NEG_ADV_100F;
|
|
|
|
|
|
|
|
if(portConfiguration & PORT_CONFIG_10MB_SPEED)
|
|
|
|
reg |= PHY_NEG_ADV_10F;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(portConfiguration & PORT_CONFIG_HALF_DUPLEX_ENABLED) {
|
|
|
|
if(portConfiguration & PORT_CONFIG_100MB_SPEED)
|
|
|
|
reg |= PHY_NEG_ADV_100H;
|
|
|
|
|
|
|
|
if(portConfiguration & PORT_CONFIG_10MB_SPEED)
|
|
|
|
reg |= PHY_NEG_ADV_10H;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(portConfiguration &
|
|
|
|
PORT_CONFIG_1000MB_SPEED) {
|
|
|
|
reg |= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ql_mii_write_reg_ex(qdev, PHY_NEG_ADVER, reg,
|
|
|
|
PHYAddr[qdev->mac_index]);
|
2006-07-25 15:40:21 +08:00
|
|
|
|
2007-03-27 03:43:52 +08:00
|
|
|
ql_mii_read_reg_ex(qdev, CONTROL_REG, ®, PHYAddr[qdev->mac_index]);
|
|
|
|
|
|
|
|
ql_mii_write_reg_ex(qdev, CONTROL_REG,
|
|
|
|
reg | PHY_CTRL_RESTART_NEG | PHY_CTRL_AUTO_NEG,
|
|
|
|
PHYAddr[qdev->mac_index]);
|
2006-07-25 15:40:21 +08:00
|
|
|
}
|
|
|
|
|
2007-03-27 03:43:52 +08:00
|
|
|
static void ql_phy_init_ex(struct ql3_adapter *qdev)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
2007-03-27 03:43:52 +08:00
|
|
|
ql_phy_reset_ex(qdev);
|
|
|
|
PHY_Setup(qdev);
|
|
|
|
ql_phy_start_neg_ex(qdev);
|
2006-07-25 15:40:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static u32 ql_get_link_state(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
u32 bitToCheck = 0;
|
|
|
|
u32 temp, linkState;
|
|
|
|
|
|
|
|
switch (qdev->mac_index) {
|
|
|
|
case 0:
|
|
|
|
bitToCheck = PORT_STATUS_UP0;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
bitToCheck = PORT_STATUS_UP1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
temp = ql_read_page0_reg(qdev, &port_regs->portStatus);
|
|
|
|
if (temp & bitToCheck) {
|
|
|
|
linkState = LS_UP;
|
|
|
|
} else {
|
|
|
|
linkState = LS_DOWN;
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_WARNING PFX
|
|
|
|
"%s: Link is down.\n", qdev->ndev->name);
|
|
|
|
}
|
|
|
|
return linkState;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_port_start(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK,
|
|
|
|
(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) *
|
2007-03-27 03:43:52 +08:00
|
|
|
2) << 7)) {
|
|
|
|
printk(KERN_ERR "%s: Could not get hw lock for GIO\n",
|
|
|
|
qdev->ndev->name);
|
2006-07-25 15:40:21 +08:00
|
|
|
return -1;
|
2007-03-27 03:43:52 +08:00
|
|
|
}
|
2006-07-25 15:40:21 +08:00
|
|
|
|
|
|
|
if (ql_is_fiber(qdev)) {
|
|
|
|
ql_petbi_init(qdev);
|
|
|
|
} else {
|
|
|
|
/* Copper port */
|
2007-03-27 03:43:52 +08:00
|
|
|
ql_phy_init_ex(qdev);
|
2006-07-25 15:40:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_finish_auto_neg(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
|
|
|
|
if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK,
|
|
|
|
(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) *
|
|
|
|
2) << 7))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!ql_auto_neg_error(qdev)) {
|
|
|
|
if (test_bit(QL_LINK_MASTER,&qdev->flags)) {
|
|
|
|
/* configure the MAC */
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_DEBUG PFX
|
|
|
|
"%s: Configuring link.\n",
|
|
|
|
qdev->ndev->
|
|
|
|
name);
|
|
|
|
ql_mac_cfg_soft_reset(qdev, 1);
|
|
|
|
ql_mac_cfg_gig(qdev,
|
|
|
|
(ql_get_link_speed
|
|
|
|
(qdev) ==
|
|
|
|
SPEED_1000));
|
|
|
|
ql_mac_cfg_full_dup(qdev,
|
|
|
|
ql_is_link_full_dup
|
|
|
|
(qdev));
|
|
|
|
ql_mac_cfg_pause(qdev,
|
|
|
|
ql_is_neg_pause
|
|
|
|
(qdev));
|
|
|
|
ql_mac_cfg_soft_reset(qdev, 0);
|
|
|
|
|
|
|
|
/* enable the MAC */
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_DEBUG PFX
|
|
|
|
"%s: Enabling mac.\n",
|
|
|
|
qdev->ndev->
|
|
|
|
name);
|
|
|
|
ql_mac_enable(qdev, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_DEBUG PFX
|
|
|
|
"%s: Change port_link_state LS_DOWN to LS_UP.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
qdev->port_link_state = LS_UP;
|
|
|
|
netif_start_queue(qdev->ndev);
|
|
|
|
netif_carrier_on(qdev->ndev);
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_INFO PFX
|
|
|
|
"%s: Link is up at %d Mbps, %s duplex.\n",
|
|
|
|
qdev->ndev->name,
|
|
|
|
ql_get_link_speed(qdev),
|
|
|
|
ql_is_link_full_dup(qdev)
|
|
|
|
? "full" : "half");
|
|
|
|
|
|
|
|
} else { /* Remote error detected */
|
|
|
|
|
|
|
|
if (test_bit(QL_LINK_MASTER,&qdev->flags)) {
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_DEBUG PFX
|
|
|
|
"%s: Remote error detected. "
|
|
|
|
"Calling ql_port_start().\n",
|
|
|
|
qdev->ndev->
|
|
|
|
name);
|
|
|
|
/*
|
|
|
|
* ql_port_start() is shared code and needs
|
|
|
|
* to lock the PHY on it's own.
|
|
|
|
*/
|
|
|
|
ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK);
|
|
|
|
if(ql_port_start(qdev)) {/* Restart port */
|
|
|
|
return -1;
|
|
|
|
} else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_link_state_machine(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
u32 curr_link_state;
|
|
|
|
unsigned long hw_flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
|
|
|
|
|
|
|
|
curr_link_state = ql_get_link_state(qdev);
|
|
|
|
|
|
|
|
if (test_bit(QL_RESET_ACTIVE,&qdev->flags)) {
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_INFO PFX
|
|
|
|
"%s: Reset in progress, skip processing link "
|
|
|
|
"state.\n", qdev->ndev->name);
|
2007-02-27 03:06:35 +08:00
|
|
|
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
2006-07-25 15:40:21 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (qdev->port_link_state) {
|
|
|
|
default:
|
|
|
|
if (test_bit(QL_LINK_MASTER,&qdev->flags)) {
|
|
|
|
ql_port_start(qdev);
|
|
|
|
}
|
|
|
|
qdev->port_link_state = LS_DOWN;
|
|
|
|
/* Fall Through */
|
|
|
|
|
|
|
|
case LS_DOWN:
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_DEBUG PFX
|
|
|
|
"%s: port_link_state = LS_DOWN.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
if (curr_link_state == LS_UP) {
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_DEBUG PFX
|
|
|
|
"%s: curr_link_state = LS_UP.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
if (ql_is_auto_neg_complete(qdev))
|
|
|
|
ql_finish_auto_neg(qdev);
|
|
|
|
|
|
|
|
if (qdev->port_link_state == LS_UP)
|
|
|
|
ql_link_down_detect_clear(qdev);
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LS_UP:
|
|
|
|
/*
|
|
|
|
* See if the link is currently down or went down and came
|
|
|
|
* back up
|
|
|
|
*/
|
|
|
|
if ((curr_link_state == LS_DOWN) || ql_link_down_detect(qdev)) {
|
|
|
|
if (netif_msg_link(qdev))
|
|
|
|
printk(KERN_INFO PFX "%s: Link is down.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
qdev->port_link_state = LS_DOWN;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller must take hw_lock and QL_PHY_GIO_SEM.
|
|
|
|
*/
|
|
|
|
static void ql_get_phy_owner(struct ql3_adapter *qdev)
|
|
|
|
{
|
2007-03-27 03:43:52 +08:00
|
|
|
if (ql_this_adapter_controls_port(qdev))
|
2006-07-25 15:40:21 +08:00
|
|
|
set_bit(QL_LINK_MASTER,&qdev->flags);
|
|
|
|
else
|
|
|
|
clear_bit(QL_LINK_MASTER,&qdev->flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller must take hw_lock and QL_PHY_GIO_SEM.
|
|
|
|
*/
|
|
|
|
static void ql_init_scan_mode(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
ql_mii_enable_scan_mode(qdev);
|
|
|
|
|
|
|
|
if (test_bit(QL_LINK_OPTICAL,&qdev->flags)) {
|
2007-03-27 03:43:52 +08:00
|
|
|
if (ql_this_adapter_controls_port(qdev))
|
|
|
|
ql_petbi_init_ex(qdev);
|
2006-07-25 15:40:21 +08:00
|
|
|
} else {
|
2007-03-27 03:43:52 +08:00
|
|
|
if (ql_this_adapter_controls_port(qdev))
|
|
|
|
ql_phy_init_ex(qdev);
|
2006-07-25 15:40:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* MII_Setup needs to be called before taking the PHY out of reset so that the
|
|
|
|
* management interface clock speed can be set properly. It would be better if
|
|
|
|
* we had a way to disable MDC until after the PHY is out of reset, but we
|
|
|
|
* don't have that capability.
|
|
|
|
*/
|
|
|
|
static int ql_mii_setup(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
u32 reg;
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
|
|
|
|
if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK,
|
|
|
|
(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) *
|
|
|
|
2) << 7))
|
|
|
|
return -1;
|
|
|
|
|
2007-01-04 08:26:08 +08:00
|
|
|
if (qdev->device_id == QL3032_DEVICE_ID)
|
|
|
|
ql_write_page0_reg(qdev,
|
|
|
|
&port_regs->macMIIMgmtControlReg, 0x0f00000);
|
|
|
|
|
2006-07-25 15:40:21 +08:00
|
|
|
/* Divide 125MHz clock by 28 to meet PHY timing requirements */
|
|
|
|
reg = MAC_MII_CONTROL_CLK_SEL_DIV28;
|
|
|
|
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg,
|
|
|
|
reg | ((MAC_MII_CONTROL_CLK_SEL_MASK) << 16));
|
|
|
|
|
|
|
|
ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 ql_supported_modes(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
u32 supported;
|
|
|
|
|
|
|
|
if (test_bit(QL_LINK_OPTICAL,&qdev->flags)) {
|
|
|
|
supported = SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE
|
|
|
|
| SUPPORTED_Autoneg;
|
|
|
|
} else {
|
|
|
|
supported = SUPPORTED_10baseT_Half
|
|
|
|
| SUPPORTED_10baseT_Full
|
|
|
|
| SUPPORTED_100baseT_Half
|
|
|
|
| SUPPORTED_100baseT_Full
|
|
|
|
| SUPPORTED_1000baseT_Half
|
|
|
|
| SUPPORTED_1000baseT_Full
|
|
|
|
| SUPPORTED_Autoneg | SUPPORTED_TP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return supported;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_get_auto_cfg_status(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
unsigned long hw_flags;
|
|
|
|
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
|
|
|
|
if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK,
|
|
|
|
(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) *
|
2007-02-27 03:06:35 +08:00
|
|
|
2) << 7)) {
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
2006-07-25 15:40:21 +08:00
|
|
|
return 0;
|
2007-02-27 03:06:35 +08:00
|
|
|
}
|
2006-07-25 15:40:21 +08:00
|
|
|
status = ql_is_auto_cfg(qdev);
|
|
|
|
ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK);
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 ql_get_speed(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
u32 status;
|
|
|
|
unsigned long hw_flags;
|
|
|
|
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
|
|
|
|
if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK,
|
|
|
|
(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) *
|
2007-02-27 03:06:35 +08:00
|
|
|
2) << 7)) {
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
2006-07-25 15:40:21 +08:00
|
|
|
return 0;
|
2007-02-27 03:06:35 +08:00
|
|
|
}
|
2006-07-25 15:40:21 +08:00
|
|
|
status = ql_get_link_speed(qdev);
|
|
|
|
ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK);
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_get_full_dup(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
unsigned long hw_flags;
|
|
|
|
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
|
|
|
|
if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK,
|
|
|
|
(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) *
|
2007-02-27 03:06:35 +08:00
|
|
|
2) << 7)) {
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
2006-07-25 15:40:21 +08:00
|
|
|
return 0;
|
2007-02-27 03:06:35 +08:00
|
|
|
}
|
2006-07-25 15:40:21 +08:00
|
|
|
status = ql_is_link_full_dup(qdev);
|
|
|
|
ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK);
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int ql_get_settings(struct net_device *ndev, struct ethtool_cmd *ecmd)
|
|
|
|
{
|
|
|
|
struct ql3_adapter *qdev = netdev_priv(ndev);
|
|
|
|
|
|
|
|
ecmd->transceiver = XCVR_INTERNAL;
|
|
|
|
ecmd->supported = ql_supported_modes(qdev);
|
|
|
|
|
|
|
|
if (test_bit(QL_LINK_OPTICAL,&qdev->flags)) {
|
|
|
|
ecmd->port = PORT_FIBRE;
|
|
|
|
} else {
|
|
|
|
ecmd->port = PORT_TP;
|
|
|
|
ecmd->phy_address = qdev->PHYAddr;
|
|
|
|
}
|
|
|
|
ecmd->advertising = ql_supported_modes(qdev);
|
|
|
|
ecmd->autoneg = ql_get_auto_cfg_status(qdev);
|
|
|
|
ecmd->speed = ql_get_speed(qdev);
|
|
|
|
ecmd->duplex = ql_get_full_dup(qdev);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_get_drvinfo(struct net_device *ndev,
|
|
|
|
struct ethtool_drvinfo *drvinfo)
|
|
|
|
{
|
|
|
|
struct ql3_adapter *qdev = netdev_priv(ndev);
|
|
|
|
strncpy(drvinfo->driver, ql3xxx_driver_name, 32);
|
|
|
|
strncpy(drvinfo->version, ql3xxx_driver_version, 32);
|
|
|
|
strncpy(drvinfo->fw_version, "N/A", 32);
|
|
|
|
strncpy(drvinfo->bus_info, pci_name(qdev->pdev), 32);
|
|
|
|
drvinfo->n_stats = 0;
|
|
|
|
drvinfo->testinfo_len = 0;
|
|
|
|
drvinfo->regdump_len = 0;
|
|
|
|
drvinfo->eedump_len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 ql_get_msglevel(struct net_device *ndev)
|
|
|
|
{
|
|
|
|
struct ql3_adapter *qdev = netdev_priv(ndev);
|
|
|
|
return qdev->msg_enable;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_set_msglevel(struct net_device *ndev, u32 value)
|
|
|
|
{
|
|
|
|
struct ql3_adapter *qdev = netdev_priv(ndev);
|
|
|
|
qdev->msg_enable = value;
|
|
|
|
}
|
|
|
|
|
2007-03-27 04:43:01 +08:00
|
|
|
static void ql_get_pauseparam(struct net_device *ndev,
|
|
|
|
struct ethtool_pauseparam *pause)
|
|
|
|
{
|
|
|
|
struct ql3_adapter *qdev = netdev_priv(ndev);
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
|
|
|
|
|
|
|
|
u32 reg;
|
|
|
|
if(qdev->mac_index == 0)
|
|
|
|
reg = ql_read_page0_reg(qdev, &port_regs->mac0ConfigReg);
|
|
|
|
else
|
|
|
|
reg = ql_read_page0_reg(qdev, &port_regs->mac1ConfigReg);
|
|
|
|
|
|
|
|
pause->autoneg = ql_get_auto_cfg_status(qdev);
|
|
|
|
pause->rx_pause = (reg & MAC_CONFIG_REG_RF) >> 2;
|
|
|
|
pause->tx_pause = (reg & MAC_CONFIG_REG_TF) >> 1;
|
|
|
|
}
|
|
|
|
|
2006-09-14 02:30:00 +08:00
|
|
|
static const struct ethtool_ops ql3xxx_ethtool_ops = {
|
2006-07-25 15:40:21 +08:00
|
|
|
.get_settings = ql_get_settings,
|
|
|
|
.get_drvinfo = ql_get_drvinfo,
|
|
|
|
.get_link = ethtool_op_get_link,
|
|
|
|
.get_msglevel = ql_get_msglevel,
|
|
|
|
.set_msglevel = ql_set_msglevel,
|
2007-03-27 04:43:01 +08:00
|
|
|
.get_pauseparam = ql_get_pauseparam,
|
2006-07-25 15:40:21 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static int ql_populate_free_queue(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct ql_rcv_buf_cb *lrg_buf_cb = qdev->lrg_buf_free_head;
|
2007-02-27 03:06:40 +08:00
|
|
|
dma_addr_t map;
|
|
|
|
int err;
|
2006-07-25 15:40:21 +08:00
|
|
|
|
|
|
|
while (lrg_buf_cb) {
|
|
|
|
if (!lrg_buf_cb->skb) {
|
2007-02-27 03:06:33 +08:00
|
|
|
lrg_buf_cb->skb = netdev_alloc_skb(qdev->ndev,
|
|
|
|
qdev->lrg_buffer_len);
|
2006-07-25 15:40:21 +08:00
|
|
|
if (unlikely(!lrg_buf_cb->skb)) {
|
|
|
|
printk(KERN_DEBUG PFX
|
2007-02-27 03:06:33 +08:00
|
|
|
"%s: Failed netdev_alloc_skb().\n",
|
2006-07-25 15:40:21 +08:00
|
|
|
qdev->ndev->name);
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We save some space to copy the ethhdr from
|
|
|
|
* first buffer
|
|
|
|
*/
|
|
|
|
skb_reserve(lrg_buf_cb->skb, QL_HEADER_SPACE);
|
|
|
|
map = pci_map_single(qdev->pdev,
|
|
|
|
lrg_buf_cb->skb->data,
|
|
|
|
qdev->lrg_buffer_len -
|
|
|
|
QL_HEADER_SPACE,
|
|
|
|
PCI_DMA_FROMDEVICE);
|
2007-02-27 03:06:40 +08:00
|
|
|
|
|
|
|
err = pci_dma_mapping_error(map);
|
|
|
|
if(err) {
|
|
|
|
printk(KERN_ERR "%s: PCI mapping failed with error: %d\n",
|
|
|
|
qdev->ndev->name, err);
|
|
|
|
dev_kfree_skb(lrg_buf_cb->skb);
|
|
|
|
lrg_buf_cb->skb = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-07-25 15:40:21 +08:00
|
|
|
lrg_buf_cb->buf_phy_addr_low =
|
|
|
|
cpu_to_le32(LS_64BITS(map));
|
|
|
|
lrg_buf_cb->buf_phy_addr_high =
|
|
|
|
cpu_to_le32(MS_64BITS(map));
|
|
|
|
pci_unmap_addr_set(lrg_buf_cb, mapaddr, map);
|
|
|
|
pci_unmap_len_set(lrg_buf_cb, maplen,
|
|
|
|
qdev->lrg_buffer_len -
|
|
|
|
QL_HEADER_SPACE);
|
|
|
|
--qdev->lrg_buf_skb_check;
|
|
|
|
if (!qdev->lrg_buf_skb_check)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lrg_buf_cb = lrg_buf_cb->next;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-03-27 04:42:59 +08:00
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static void ql_update_small_bufq_prod_index(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
|
|
|
|
if (qdev->small_buf_release_cnt >= 16) {
|
|
|
|
while (qdev->small_buf_release_cnt >= 16) {
|
|
|
|
qdev->small_buf_q_producer_index++;
|
|
|
|
|
|
|
|
if (qdev->small_buf_q_producer_index ==
|
|
|
|
NUM_SBUFQ_ENTRIES)
|
|
|
|
qdev->small_buf_q_producer_index = 0;
|
|
|
|
qdev->small_buf_release_cnt -= 8;
|
|
|
|
}
|
|
|
|
wmb();
|
|
|
|
writel(qdev->small_buf_q_producer_index,
|
|
|
|
&port_regs->CommonRegs.rxSmallQProducerIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-25 15:40:21 +08:00
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static void ql_update_lrg_bufq_prod_index(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct bufq_addr_element *lrg_buf_q_ele;
|
|
|
|
int i;
|
|
|
|
struct ql_rcv_buf_cb *lrg_buf_cb;
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
|
|
|
|
|
|
|
|
if ((qdev->lrg_buf_free_count >= 8)
|
|
|
|
&& (qdev->lrg_buf_release_cnt >= 16)) {
|
|
|
|
|
|
|
|
if (qdev->lrg_buf_skb_check)
|
|
|
|
if (!ql_populate_free_queue(qdev))
|
|
|
|
return;
|
|
|
|
|
|
|
|
lrg_buf_q_ele = qdev->lrg_buf_next_free;
|
|
|
|
|
|
|
|
while ((qdev->lrg_buf_release_cnt >= 16)
|
|
|
|
&& (qdev->lrg_buf_free_count >= 8)) {
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
|
|
lrg_buf_cb =
|
|
|
|
ql_get_from_lrg_buf_free_list(qdev);
|
|
|
|
lrg_buf_q_ele->addr_high =
|
|
|
|
lrg_buf_cb->buf_phy_addr_high;
|
|
|
|
lrg_buf_q_ele->addr_low =
|
|
|
|
lrg_buf_cb->buf_phy_addr_low;
|
|
|
|
lrg_buf_q_ele++;
|
|
|
|
|
|
|
|
qdev->lrg_buf_release_cnt--;
|
|
|
|
}
|
|
|
|
|
|
|
|
qdev->lrg_buf_q_producer_index++;
|
|
|
|
|
2007-02-27 03:06:37 +08:00
|
|
|
if (qdev->lrg_buf_q_producer_index == qdev->num_lbufq_entries)
|
2006-07-25 15:40:21 +08:00
|
|
|
qdev->lrg_buf_q_producer_index = 0;
|
|
|
|
|
|
|
|
if (qdev->lrg_buf_q_producer_index ==
|
2007-02-27 03:06:37 +08:00
|
|
|
(qdev->num_lbufq_entries - 1)) {
|
2006-07-25 15:40:21 +08:00
|
|
|
lrg_buf_q_ele = qdev->lrg_buf_q_virt_addr;
|
|
|
|
}
|
|
|
|
}
|
2007-03-27 04:42:59 +08:00
|
|
|
wmb();
|
2006-07-25 15:40:21 +08:00
|
|
|
qdev->lrg_buf_next_free = lrg_buf_q_ele;
|
2007-03-27 04:42:59 +08:00
|
|
|
writel(qdev->lrg_buf_q_producer_index,
|
|
|
|
&port_regs->CommonRegs.rxLargeQProducerIndex);
|
2006-07-25 15:40:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_process_mac_tx_intr(struct ql3_adapter *qdev,
|
|
|
|
struct ob_mac_iocb_rsp *mac_rsp)
|
|
|
|
{
|
|
|
|
struct ql_tx_buf_cb *tx_cb;
|
2007-01-04 08:26:08 +08:00
|
|
|
int i;
|
2007-02-27 03:06:42 +08:00
|
|
|
int retval = 0;
|
2006-07-25 15:40:21 +08:00
|
|
|
|
2007-02-27 03:06:42 +08:00
|
|
|
if(mac_rsp->flags & OB_MAC_IOCB_RSP_S) {
|
|
|
|
printk(KERN_WARNING "Frame short but, frame was padded and sent.\n");
|
|
|
|
}
|
|
|
|
|
2006-07-25 15:40:21 +08:00
|
|
|
tx_cb = &qdev->tx_buf[mac_rsp->transaction_id];
|
2007-02-27 03:06:42 +08:00
|
|
|
|
|
|
|
/* Check the transmit response flags for any errors */
|
|
|
|
if(mac_rsp->flags & OB_MAC_IOCB_RSP_S) {
|
|
|
|
printk(KERN_ERR "Frame too short to be legal, frame not sent.\n");
|
|
|
|
|
|
|
|
qdev->stats.tx_errors++;
|
|
|
|
retval = -EIO;
|
|
|
|
goto frame_not_sent;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(tx_cb->seg_count == 0) {
|
|
|
|
printk(KERN_ERR "tx_cb->seg_count == 0: %d\n", mac_rsp->transaction_id);
|
|
|
|
|
|
|
|
qdev->stats.tx_errors++;
|
|
|
|
retval = -EIO;
|
|
|
|
goto invalid_seg_count;
|
|
|
|
}
|
|
|
|
|
2006-07-25 15:40:21 +08:00
|
|
|
pci_unmap_single(qdev->pdev,
|
2007-01-04 08:26:08 +08:00
|
|
|
pci_unmap_addr(&tx_cb->map[0], mapaddr),
|
|
|
|
pci_unmap_len(&tx_cb->map[0], maplen),
|
|
|
|
PCI_DMA_TODEVICE);
|
|
|
|
tx_cb->seg_count--;
|
|
|
|
if (tx_cb->seg_count) {
|
|
|
|
for (i = 1; i < tx_cb->seg_count; i++) {
|
|
|
|
pci_unmap_page(qdev->pdev,
|
|
|
|
pci_unmap_addr(&tx_cb->map[i],
|
|
|
|
mapaddr),
|
|
|
|
pci_unmap_len(&tx_cb->map[i], maplen),
|
|
|
|
PCI_DMA_TODEVICE);
|
|
|
|
}
|
|
|
|
}
|
2006-07-25 15:40:21 +08:00
|
|
|
qdev->stats.tx_packets++;
|
|
|
|
qdev->stats.tx_bytes += tx_cb->skb->len;
|
2007-02-27 03:06:42 +08:00
|
|
|
|
|
|
|
frame_not_sent:
|
2007-01-04 08:26:08 +08:00
|
|
|
dev_kfree_skb_irq(tx_cb->skb);
|
2006-07-25 15:40:21 +08:00
|
|
|
tx_cb->skb = NULL;
|
2007-02-27 03:06:42 +08:00
|
|
|
|
|
|
|
invalid_seg_count:
|
2006-07-25 15:40:21 +08:00
|
|
|
atomic_inc(&qdev->tx_count);
|
|
|
|
}
|
|
|
|
|
2007-03-05 09:49:27 +08:00
|
|
|
static void ql_get_sbuf(struct ql3_adapter *qdev)
|
2007-02-27 03:06:38 +08:00
|
|
|
{
|
|
|
|
if (++qdev->small_buf_index == NUM_SMALL_BUFFERS)
|
|
|
|
qdev->small_buf_index = 0;
|
|
|
|
qdev->small_buf_release_cnt++;
|
|
|
|
}
|
|
|
|
|
2007-03-05 09:49:27 +08:00
|
|
|
static struct ql_rcv_buf_cb *ql_get_lbuf(struct ql3_adapter *qdev)
|
2007-02-27 03:06:38 +08:00
|
|
|
{
|
|
|
|
struct ql_rcv_buf_cb *lrg_buf_cb = NULL;
|
|
|
|
lrg_buf_cb = &qdev->lrg_buf[qdev->lrg_buf_index];
|
|
|
|
qdev->lrg_buf_release_cnt++;
|
|
|
|
if (++qdev->lrg_buf_index == qdev->num_large_buffers)
|
|
|
|
qdev->lrg_buf_index = 0;
|
|
|
|
return(lrg_buf_cb);
|
|
|
|
}
|
|
|
|
|
2007-01-04 08:26:08 +08:00
|
|
|
/*
|
|
|
|
* The difference between 3022 and 3032 for inbound completions:
|
|
|
|
* 3022 uses two buffers per completion. The first buffer contains
|
|
|
|
* (some) header info, the second the remainder of the headers plus
|
|
|
|
* the data. For this chip we reserve some space at the top of the
|
|
|
|
* receive buffer so that the header info in buffer one can be
|
|
|
|
* prepended to the buffer two. Buffer two is the sent up while
|
|
|
|
* buffer one is returned to the hardware to be reused.
|
|
|
|
* 3032 receives all of it's data and headers in one buffer for a
|
|
|
|
* simpler process. 3032 also supports checksum verification as
|
|
|
|
* can be seen in ql_process_macip_rx_intr().
|
|
|
|
*/
|
2006-07-25 15:40:21 +08:00
|
|
|
static void ql_process_mac_rx_intr(struct ql3_adapter *qdev,
|
|
|
|
struct ib_mac_iocb_rsp *ib_mac_rsp_ptr)
|
|
|
|
{
|
|
|
|
struct ql_rcv_buf_cb *lrg_buf_cb1 = NULL;
|
|
|
|
struct ql_rcv_buf_cb *lrg_buf_cb2 = NULL;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
u16 length = le16_to_cpu(ib_mac_rsp_ptr->length);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the inbound address list (small buffer).
|
|
|
|
*/
|
2007-02-27 03:06:38 +08:00
|
|
|
ql_get_sbuf(qdev);
|
2006-07-25 15:40:21 +08:00
|
|
|
|
2007-02-27 03:06:38 +08:00
|
|
|
if (qdev->device_id == QL3022_DEVICE_ID)
|
|
|
|
lrg_buf_cb1 = ql_get_lbuf(qdev);
|
2006-07-25 15:40:21 +08:00
|
|
|
|
|
|
|
/* start of second buffer */
|
2007-02-27 03:06:38 +08:00
|
|
|
lrg_buf_cb2 = ql_get_lbuf(qdev);
|
2006-07-25 15:40:21 +08:00
|
|
|
skb = lrg_buf_cb2->skb;
|
|
|
|
|
|
|
|
qdev->stats.rx_packets++;
|
|
|
|
qdev->stats.rx_bytes += length;
|
|
|
|
|
|
|
|
skb_put(skb, length);
|
|
|
|
pci_unmap_single(qdev->pdev,
|
|
|
|
pci_unmap_addr(lrg_buf_cb2, mapaddr),
|
|
|
|
pci_unmap_len(lrg_buf_cb2, maplen),
|
|
|
|
PCI_DMA_FROMDEVICE);
|
|
|
|
prefetch(skb->data);
|
|
|
|
skb->ip_summed = CHECKSUM_NONE;
|
|
|
|
skb->protocol = eth_type_trans(skb, qdev->ndev);
|
|
|
|
|
|
|
|
netif_receive_skb(skb);
|
|
|
|
qdev->ndev->last_rx = jiffies;
|
|
|
|
lrg_buf_cb2->skb = NULL;
|
|
|
|
|
2007-01-04 08:26:08 +08:00
|
|
|
if (qdev->device_id == QL3022_DEVICE_ID)
|
|
|
|
ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb1);
|
2006-07-25 15:40:21 +08:00
|
|
|
ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_process_macip_rx_intr(struct ql3_adapter *qdev,
|
|
|
|
struct ib_ip_iocb_rsp *ib_ip_rsp_ptr)
|
|
|
|
{
|
|
|
|
struct ql_rcv_buf_cb *lrg_buf_cb1 = NULL;
|
|
|
|
struct ql_rcv_buf_cb *lrg_buf_cb2 = NULL;
|
2007-01-04 08:26:08 +08:00
|
|
|
struct sk_buff *skb1 = NULL, *skb2;
|
2006-07-25 15:40:21 +08:00
|
|
|
struct net_device *ndev = qdev->ndev;
|
|
|
|
u16 length = le16_to_cpu(ib_ip_rsp_ptr->length);
|
|
|
|
u16 size = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the inbound address list (small buffer).
|
|
|
|
*/
|
|
|
|
|
2007-02-27 03:06:38 +08:00
|
|
|
ql_get_sbuf(qdev);
|
2006-07-25 15:40:21 +08:00
|
|
|
|
2007-01-04 08:26:08 +08:00
|
|
|
if (qdev->device_id == QL3022_DEVICE_ID) {
|
|
|
|
/* start of first buffer on 3022 */
|
2007-02-27 03:06:38 +08:00
|
|
|
lrg_buf_cb1 = ql_get_lbuf(qdev);
|
2007-01-04 08:26:08 +08:00
|
|
|
skb1 = lrg_buf_cb1->skb;
|
|
|
|
size = ETH_HLEN;
|
|
|
|
if (*((u16 *) skb1->data) != 0xFFFF)
|
|
|
|
size += VLAN_ETH_HLEN - ETH_HLEN;
|
|
|
|
}
|
2006-07-25 15:40:21 +08:00
|
|
|
|
|
|
|
/* start of second buffer */
|
2007-02-27 03:06:38 +08:00
|
|
|
lrg_buf_cb2 = ql_get_lbuf(qdev);
|
2006-07-25 15:40:21 +08:00
|
|
|
skb2 = lrg_buf_cb2->skb;
|
|
|
|
|
|
|
|
skb_put(skb2, length); /* Just the second buffer length here. */
|
|
|
|
pci_unmap_single(qdev->pdev,
|
|
|
|
pci_unmap_addr(lrg_buf_cb2, mapaddr),
|
|
|
|
pci_unmap_len(lrg_buf_cb2, maplen),
|
|
|
|
PCI_DMA_FROMDEVICE);
|
|
|
|
prefetch(skb2->data);
|
|
|
|
|
|
|
|
skb2->ip_summed = CHECKSUM_NONE;
|
2007-01-04 08:26:08 +08:00
|
|
|
if (qdev->device_id == QL3022_DEVICE_ID) {
|
|
|
|
/*
|
|
|
|
* Copy the ethhdr from first buffer to second. This
|
|
|
|
* is necessary for 3022 IP completions.
|
|
|
|
*/
|
2007-03-28 05:55:52 +08:00
|
|
|
skb_copy_from_linear_data_offset(skb1, VLAN_ID_LEN,
|
|
|
|
skb_push(skb2, size), size);
|
2007-01-04 08:26:08 +08:00
|
|
|
} else {
|
|
|
|
u16 checksum = le16_to_cpu(ib_ip_rsp_ptr->checksum);
|
|
|
|
if (checksum &
|
|
|
|
(IB_IP_IOCB_RSP_3032_ICE |
|
2007-03-27 04:43:00 +08:00
|
|
|
IB_IP_IOCB_RSP_3032_CE)) {
|
2007-01-04 08:26:08 +08:00
|
|
|
printk(KERN_ERR
|
|
|
|
"%s: Bad checksum for this %s packet, checksum = %x.\n",
|
|
|
|
__func__,
|
|
|
|
((checksum &
|
|
|
|
IB_IP_IOCB_RSP_3032_TCP) ? "TCP" :
|
|
|
|
"UDP"),checksum);
|
2007-03-27 04:43:00 +08:00
|
|
|
} else if ((checksum & IB_IP_IOCB_RSP_3032_TCP) ||
|
|
|
|
(checksum & IB_IP_IOCB_RSP_3032_UDP &&
|
|
|
|
!(checksum & IB_IP_IOCB_RSP_3032_NUC))) {
|
2007-01-04 08:26:08 +08:00
|
|
|
skb2->ip_summed = CHECKSUM_UNNECESSARY;
|
2007-03-27 04:43:00 +08:00
|
|
|
}
|
2007-01-04 08:26:08 +08:00
|
|
|
}
|
2006-07-25 15:40:21 +08:00
|
|
|
skb2->protocol = eth_type_trans(skb2, qdev->ndev);
|
|
|
|
|
|
|
|
netif_receive_skb(skb2);
|
2007-01-04 08:26:08 +08:00
|
|
|
qdev->stats.rx_packets++;
|
|
|
|
qdev->stats.rx_bytes += length;
|
2006-07-25 15:40:21 +08:00
|
|
|
ndev->last_rx = jiffies;
|
|
|
|
lrg_buf_cb2->skb = NULL;
|
|
|
|
|
2007-01-04 08:26:08 +08:00
|
|
|
if (qdev->device_id == QL3022_DEVICE_ID)
|
|
|
|
ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb1);
|
2006-07-25 15:40:21 +08:00
|
|
|
ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_tx_rx_clean(struct ql3_adapter *qdev,
|
|
|
|
int *tx_cleaned, int *rx_cleaned, int work_to_do)
|
|
|
|
{
|
|
|
|
struct net_rsp_iocb *net_rsp;
|
|
|
|
struct net_device *ndev = qdev->ndev;
|
2007-02-27 03:06:41 +08:00
|
|
|
int work_done = 0;
|
2006-07-25 15:40:21 +08:00
|
|
|
|
|
|
|
/* While there are entries in the completion queue. */
|
2007-03-27 04:42:59 +08:00
|
|
|
while ((le32_to_cpu(*(qdev->prsp_producer_index)) !=
|
2007-02-27 03:06:41 +08:00
|
|
|
qdev->rsp_consumer_index) && (work_done < work_to_do)) {
|
2006-07-25 15:40:21 +08:00
|
|
|
|
|
|
|
net_rsp = qdev->rsp_current;
|
2007-10-02 02:43:22 +08:00
|
|
|
rmb();
|
2007-10-02 02:43:23 +08:00
|
|
|
/*
|
|
|
|
* Fix 4032 chipe undocumented "feature" where bit-8 is set if the
|
|
|
|
* inbound completion is for a VLAN.
|
|
|
|
*/
|
|
|
|
if (qdev->device_id == QL3032_DEVICE_ID)
|
|
|
|
net_rsp->opcode &= 0x7f;
|
2006-07-25 15:40:21 +08:00
|
|
|
switch (net_rsp->opcode) {
|
|
|
|
|
|
|
|
case OPCODE_OB_MAC_IOCB_FN0:
|
|
|
|
case OPCODE_OB_MAC_IOCB_FN2:
|
|
|
|
ql_process_mac_tx_intr(qdev, (struct ob_mac_iocb_rsp *)
|
|
|
|
net_rsp);
|
|
|
|
(*tx_cleaned)++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OPCODE_IB_MAC_IOCB:
|
2007-01-04 08:26:08 +08:00
|
|
|
case OPCODE_IB_3032_MAC_IOCB:
|
2006-07-25 15:40:21 +08:00
|
|
|
ql_process_mac_rx_intr(qdev, (struct ib_mac_iocb_rsp *)
|
|
|
|
net_rsp);
|
|
|
|
(*rx_cleaned)++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OPCODE_IB_IP_IOCB:
|
2007-01-04 08:26:08 +08:00
|
|
|
case OPCODE_IB_3032_IP_IOCB:
|
2006-07-25 15:40:21 +08:00
|
|
|
ql_process_macip_rx_intr(qdev, (struct ib_ip_iocb_rsp *)
|
|
|
|
net_rsp);
|
|
|
|
(*rx_cleaned)++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
u32 *tmp = (u32 *) net_rsp;
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: Hit default case, not "
|
|
|
|
"handled!\n"
|
|
|
|
" dropping the packet, opcode = "
|
|
|
|
"%x.\n",
|
|
|
|
ndev->name, net_rsp->opcode);
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"0x%08lx 0x%08lx 0x%08lx 0x%08lx \n",
|
|
|
|
(unsigned long int)tmp[0],
|
|
|
|
(unsigned long int)tmp[1],
|
|
|
|
(unsigned long int)tmp[2],
|
|
|
|
(unsigned long int)tmp[3]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
qdev->rsp_consumer_index++;
|
|
|
|
|
|
|
|
if (qdev->rsp_consumer_index == NUM_RSP_Q_ENTRIES) {
|
|
|
|
qdev->rsp_consumer_index = 0;
|
|
|
|
qdev->rsp_current = qdev->rsp_q_virt_addr;
|
|
|
|
} else {
|
|
|
|
qdev->rsp_current++;
|
|
|
|
}
|
2007-02-27 03:06:41 +08:00
|
|
|
|
|
|
|
work_done = *tx_cleaned + *rx_cleaned;
|
2006-07-25 15:40:21 +08:00
|
|
|
}
|
|
|
|
|
2007-03-27 04:42:59 +08:00
|
|
|
return work_done;
|
2006-07-25 15:40:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_poll(struct net_device *ndev, int *budget)
|
|
|
|
{
|
|
|
|
struct ql3_adapter *qdev = netdev_priv(ndev);
|
|
|
|
int work_to_do = min(*budget, ndev->quota);
|
|
|
|
int rx_cleaned = 0, tx_cleaned = 0;
|
2007-02-27 03:06:41 +08:00
|
|
|
unsigned long hw_flags;
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
|
2006-07-25 15:40:21 +08:00
|
|
|
|
|
|
|
if (!netif_carrier_ok(ndev))
|
|
|
|
goto quit_polling;
|
|
|
|
|
|
|
|
ql_tx_rx_clean(qdev, &tx_cleaned, &rx_cleaned, work_to_do);
|
|
|
|
*budget -= rx_cleaned;
|
|
|
|
ndev->quota -= rx_cleaned;
|
|
|
|
|
2007-02-27 03:06:42 +08:00
|
|
|
if( tx_cleaned + rx_cleaned != work_to_do ||
|
|
|
|
!netif_running(ndev)) {
|
2006-07-25 15:40:21 +08:00
|
|
|
quit_polling:
|
|
|
|
netif_rx_complete(ndev);
|
2007-02-27 03:06:41 +08:00
|
|
|
|
|
|
|
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
|
2007-03-27 04:42:59 +08:00
|
|
|
ql_update_small_bufq_prod_index(qdev);
|
|
|
|
ql_update_lrg_bufq_prod_index(qdev);
|
|
|
|
writel(qdev->rsp_consumer_index,
|
|
|
|
&port_regs->CommonRegs.rspQConsumerIndex);
|
2007-02-27 03:06:41 +08:00
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
|
|
|
2006-07-25 15:40:21 +08:00
|
|
|
ql_enable_interrupts(qdev);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
static irqreturn_t ql3xxx_isr(int irq, void *dev_id)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
|
|
|
|
|
|
|
struct net_device *ndev = dev_id;
|
|
|
|
struct ql3_adapter *qdev = netdev_priv(ndev);
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
|
|
|
|
u32 value;
|
|
|
|
int handled = 1;
|
|
|
|
u32 var;
|
|
|
|
|
|
|
|
port_regs = qdev->mem_map_registers;
|
|
|
|
|
|
|
|
value =
|
|
|
|
ql_read_common_reg_l(qdev, &port_regs->CommonRegs.ispControlStatus);
|
|
|
|
|
|
|
|
if (value & (ISP_CONTROL_FE | ISP_CONTROL_RI)) {
|
|
|
|
spin_lock(&qdev->adapter_lock);
|
|
|
|
netif_stop_queue(qdev->ndev);
|
|
|
|
netif_carrier_off(qdev->ndev);
|
|
|
|
ql_disable_interrupts(qdev);
|
|
|
|
qdev->port_link_state = LS_DOWN;
|
|
|
|
set_bit(QL_RESET_ACTIVE,&qdev->flags) ;
|
|
|
|
|
|
|
|
if (value & ISP_CONTROL_FE) {
|
|
|
|
/*
|
|
|
|
* Chip Fatal Error.
|
|
|
|
*/
|
|
|
|
var =
|
|
|
|
ql_read_page0_reg_l(qdev,
|
|
|
|
&port_regs->PortFatalErrStatus);
|
|
|
|
printk(KERN_WARNING PFX
|
|
|
|
"%s: Resetting chip. PortFatalErrStatus "
|
|
|
|
"register = 0x%x\n", ndev->name, var);
|
|
|
|
set_bit(QL_RESET_START,&qdev->flags) ;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Soft Reset Requested.
|
|
|
|
*/
|
|
|
|
set_bit(QL_RESET_PER_SCSI,&qdev->flags) ;
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: Another function issued a reset to the "
|
|
|
|
"chip. ISR value = %x.\n", ndev->name, value);
|
|
|
|
}
|
2006-11-22 22:57:56 +08:00
|
|
|
queue_delayed_work(qdev->workqueue, &qdev->reset_work, 0);
|
2006-07-25 15:40:21 +08:00
|
|
|
spin_unlock(&qdev->adapter_lock);
|
|
|
|
} else if (value & ISP_IMR_DISABLE_CMPL_INT) {
|
2007-02-27 03:06:42 +08:00
|
|
|
ql_disable_interrupts(qdev);
|
2007-02-27 03:06:41 +08:00
|
|
|
if (likely(netif_rx_schedule_prep(ndev))) {
|
2006-07-25 15:40:21 +08:00
|
|
|
__netif_rx_schedule(ndev);
|
2007-02-27 03:06:41 +08:00
|
|
|
}
|
2006-07-25 15:40:21 +08:00
|
|
|
} else {
|
|
|
|
return IRQ_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return IRQ_RETVAL(handled);
|
|
|
|
}
|
|
|
|
|
2007-01-04 08:26:08 +08:00
|
|
|
/*
|
|
|
|
* Get the total number of segments needed for the
|
|
|
|
* given number of fragments. This is necessary because
|
|
|
|
* outbound address lists (OAL) will be used when more than
|
|
|
|
* two frags are given. Each address list has 5 addr/len
|
|
|
|
* pairs. The 5th pair in each AOL is used to point to
|
|
|
|
* the next AOL if more frags are coming.
|
|
|
|
* That is why the frags:segment count ratio is not linear.
|
|
|
|
*/
|
2007-02-27 03:06:42 +08:00
|
|
|
static int ql_get_seg_count(struct ql3_adapter *qdev,
|
|
|
|
unsigned short frags)
|
2007-01-04 08:26:08 +08:00
|
|
|
{
|
2007-02-27 03:06:42 +08:00
|
|
|
if (qdev->device_id == QL3022_DEVICE_ID)
|
|
|
|
return 1;
|
|
|
|
|
2007-01-04 08:26:08 +08:00
|
|
|
switch(frags) {
|
|
|
|
case 0: return 1; /* just the skb->data seg */
|
|
|
|
case 1: return 2; /* skb->data + 1 frag */
|
|
|
|
case 2: return 3; /* skb->data + 2 frags */
|
|
|
|
case 3: return 5; /* skb->data + 1 frag + 1 AOL containting 2 frags */
|
|
|
|
case 4: return 6;
|
|
|
|
case 5: return 7;
|
|
|
|
case 6: return 8;
|
|
|
|
case 7: return 10;
|
|
|
|
case 8: return 11;
|
|
|
|
case 9: return 12;
|
|
|
|
case 10: return 13;
|
|
|
|
case 11: return 15;
|
|
|
|
case 12: return 16;
|
|
|
|
case 13: return 17;
|
|
|
|
case 14: return 18;
|
|
|
|
case 15: return 20;
|
|
|
|
case 16: return 21;
|
|
|
|
case 17: return 22;
|
|
|
|
case 18: return 23;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-05-31 05:23:18 +08:00
|
|
|
static void ql_hw_csum_setup(const struct sk_buff *skb,
|
2007-01-04 08:26:08 +08:00
|
|
|
struct ob_mac_iocb_req *mac_iocb_ptr)
|
|
|
|
{
|
2007-05-31 05:23:18 +08:00
|
|
|
const struct iphdr *ip = ip_hdr(skb);
|
2007-01-04 08:26:08 +08:00
|
|
|
|
2007-05-31 05:23:18 +08:00
|
|
|
mac_iocb_ptr->ip_hdr_off = skb_network_offset(skb);
|
|
|
|
mac_iocb_ptr->ip_hdr_len = ip->ihl;
|
2007-01-04 08:26:08 +08:00
|
|
|
|
2007-05-31 05:23:18 +08:00
|
|
|
if (ip->protocol == IPPROTO_TCP) {
|
|
|
|
mac_iocb_ptr->flags1 |= OB_3032MAC_IOCB_REQ_TC |
|
2007-02-27 03:06:39 +08:00
|
|
|
OB_3032MAC_IOCB_REQ_IC;
|
2007-05-31 05:23:18 +08:00
|
|
|
} else {
|
|
|
|
mac_iocb_ptr->flags1 |= OB_3032MAC_IOCB_REQ_UC |
|
2007-02-27 03:06:39 +08:00
|
|
|
OB_3032MAC_IOCB_REQ_IC;
|
2007-01-04 08:26:08 +08:00
|
|
|
}
|
2007-05-31 05:23:18 +08:00
|
|
|
|
2007-01-04 08:26:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2007-02-27 03:06:39 +08:00
|
|
|
* Map the buffers for this transmit. This will return
|
|
|
|
* NETDEV_TX_BUSY or NETDEV_TX_OK based on success.
|
2007-01-04 08:26:08 +08:00
|
|
|
*/
|
2007-02-27 03:06:39 +08:00
|
|
|
static int ql_send_map(struct ql3_adapter *qdev,
|
|
|
|
struct ob_mac_iocb_req *mac_iocb_ptr,
|
|
|
|
struct ql_tx_buf_cb *tx_cb,
|
|
|
|
struct sk_buff *skb)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
2007-01-04 08:26:08 +08:00
|
|
|
struct oal *oal;
|
|
|
|
struct oal_entry *oal_entry;
|
2007-03-01 08:42:17 +08:00
|
|
|
int len = skb_headlen(skb);
|
2007-02-27 03:06:40 +08:00
|
|
|
dma_addr_t map;
|
|
|
|
int err;
|
|
|
|
int completed_segs, i;
|
2007-01-04 08:26:08 +08:00
|
|
|
int seg_cnt, seg = 0;
|
|
|
|
int frag_cnt = (int)skb_shinfo(skb)->nr_frags;
|
2006-07-25 15:40:21 +08:00
|
|
|
|
2007-03-27 04:42:58 +08:00
|
|
|
seg_cnt = tx_cb->seg_count;
|
2007-02-27 03:06:39 +08:00
|
|
|
/*
|
|
|
|
* Map the skb buffer first.
|
|
|
|
*/
|
2007-01-04 08:26:08 +08:00
|
|
|
map = pci_map_single(qdev->pdev, skb->data, len, PCI_DMA_TODEVICE);
|
2007-02-27 03:06:40 +08:00
|
|
|
|
|
|
|
err = pci_dma_mapping_error(map);
|
|
|
|
if(err) {
|
|
|
|
printk(KERN_ERR "%s: PCI mapping failed with error: %d\n",
|
|
|
|
qdev->ndev->name, err);
|
|
|
|
|
|
|
|
return NETDEV_TX_BUSY;
|
|
|
|
}
|
|
|
|
|
2007-01-04 08:26:08 +08:00
|
|
|
oal_entry = (struct oal_entry *)&mac_iocb_ptr->buf_addr0_low;
|
|
|
|
oal_entry->dma_lo = cpu_to_le32(LS_64BITS(map));
|
|
|
|
oal_entry->dma_hi = cpu_to_le32(MS_64BITS(map));
|
|
|
|
oal_entry->len = cpu_to_le32(len);
|
|
|
|
pci_unmap_addr_set(&tx_cb->map[seg], mapaddr, map);
|
|
|
|
pci_unmap_len_set(&tx_cb->map[seg], maplen, len);
|
|
|
|
seg++;
|
|
|
|
|
2007-02-27 03:06:42 +08:00
|
|
|
if (seg_cnt == 1) {
|
2007-01-04 08:26:08 +08:00
|
|
|
/* Terminate the last segment. */
|
|
|
|
oal_entry->len =
|
|
|
|
cpu_to_le32(le32_to_cpu(oal_entry->len) | OAL_LAST_ENTRY);
|
|
|
|
} else {
|
|
|
|
oal = tx_cb->oal;
|
2007-02-27 03:06:40 +08:00
|
|
|
for (completed_segs=0; completed_segs<frag_cnt; completed_segs++,seg++) {
|
|
|
|
skb_frag_t *frag = &skb_shinfo(skb)->frags[completed_segs];
|
2007-01-04 08:26:08 +08:00
|
|
|
oal_entry++;
|
|
|
|
if ((seg == 2 && seg_cnt > 3) || /* Check for continuation */
|
|
|
|
(seg == 7 && seg_cnt > 8) || /* requirements. It's strange */
|
|
|
|
(seg == 12 && seg_cnt > 13) || /* but necessary. */
|
|
|
|
(seg == 17 && seg_cnt > 18)) {
|
|
|
|
/* Continuation entry points to outbound address list. */
|
|
|
|
map = pci_map_single(qdev->pdev, oal,
|
|
|
|
sizeof(struct oal),
|
|
|
|
PCI_DMA_TODEVICE);
|
2007-02-27 03:06:40 +08:00
|
|
|
|
|
|
|
err = pci_dma_mapping_error(map);
|
|
|
|
if(err) {
|
|
|
|
|
|
|
|
printk(KERN_ERR "%s: PCI mapping outbound address list with error: %d\n",
|
|
|
|
qdev->ndev->name, err);
|
|
|
|
goto map_error;
|
|
|
|
}
|
|
|
|
|
2007-01-04 08:26:08 +08:00
|
|
|
oal_entry->dma_lo = cpu_to_le32(LS_64BITS(map));
|
|
|
|
oal_entry->dma_hi = cpu_to_le32(MS_64BITS(map));
|
|
|
|
oal_entry->len =
|
|
|
|
cpu_to_le32(sizeof(struct oal) |
|
|
|
|
OAL_CONT_ENTRY);
|
|
|
|
pci_unmap_addr_set(&tx_cb->map[seg], mapaddr,
|
|
|
|
map);
|
|
|
|
pci_unmap_len_set(&tx_cb->map[seg], maplen,
|
2007-03-27 04:42:58 +08:00
|
|
|
sizeof(struct oal));
|
2007-01-04 08:26:08 +08:00
|
|
|
oal_entry = (struct oal_entry *)oal;
|
|
|
|
oal++;
|
|
|
|
seg++;
|
|
|
|
}
|
2006-07-25 15:40:21 +08:00
|
|
|
|
2007-01-04 08:26:08 +08:00
|
|
|
map =
|
|
|
|
pci_map_page(qdev->pdev, frag->page,
|
|
|
|
frag->page_offset, frag->size,
|
|
|
|
PCI_DMA_TODEVICE);
|
2007-02-27 03:06:40 +08:00
|
|
|
|
|
|
|
err = pci_dma_mapping_error(map);
|
|
|
|
if(err) {
|
|
|
|
printk(KERN_ERR "%s: PCI mapping frags failed with error: %d\n",
|
|
|
|
qdev->ndev->name, err);
|
|
|
|
goto map_error;
|
|
|
|
}
|
|
|
|
|
2007-01-04 08:26:08 +08:00
|
|
|
oal_entry->dma_lo = cpu_to_le32(LS_64BITS(map));
|
|
|
|
oal_entry->dma_hi = cpu_to_le32(MS_64BITS(map));
|
|
|
|
oal_entry->len = cpu_to_le32(frag->size);
|
|
|
|
pci_unmap_addr_set(&tx_cb->map[seg], mapaddr, map);
|
|
|
|
pci_unmap_len_set(&tx_cb->map[seg], maplen,
|
|
|
|
frag->size);
|
|
|
|
}
|
|
|
|
/* Terminate the last segment. */
|
|
|
|
oal_entry->len =
|
|
|
|
cpu_to_le32(le32_to_cpu(oal_entry->len) | OAL_LAST_ENTRY);
|
|
|
|
}
|
2007-02-27 03:06:40 +08:00
|
|
|
|
2007-02-27 03:06:39 +08:00
|
|
|
return NETDEV_TX_OK;
|
2007-02-27 03:06:40 +08:00
|
|
|
|
|
|
|
map_error:
|
|
|
|
/* A PCI mapping failed and now we will need to back out
|
|
|
|
* We need to traverse through the oal's and associated pages which
|
|
|
|
* have been mapped and now we must unmap them to clean up properly
|
|
|
|
*/
|
|
|
|
|
|
|
|
seg = 1;
|
|
|
|
oal_entry = (struct oal_entry *)&mac_iocb_ptr->buf_addr0_low;
|
|
|
|
oal = tx_cb->oal;
|
|
|
|
for (i=0; i<completed_segs; i++,seg++) {
|
|
|
|
oal_entry++;
|
|
|
|
|
|
|
|
if((seg == 2 && seg_cnt > 3) || /* Check for continuation */
|
|
|
|
(seg == 7 && seg_cnt > 8) || /* requirements. It's strange */
|
|
|
|
(seg == 12 && seg_cnt > 13) || /* but necessary. */
|
|
|
|
(seg == 17 && seg_cnt > 18)) {
|
|
|
|
pci_unmap_single(qdev->pdev,
|
|
|
|
pci_unmap_addr(&tx_cb->map[seg], mapaddr),
|
|
|
|
pci_unmap_len(&tx_cb->map[seg], maplen),
|
|
|
|
PCI_DMA_TODEVICE);
|
|
|
|
oal++;
|
|
|
|
seg++;
|
|
|
|
}
|
|
|
|
|
|
|
|
pci_unmap_page(qdev->pdev,
|
|
|
|
pci_unmap_addr(&tx_cb->map[seg], mapaddr),
|
|
|
|
pci_unmap_len(&tx_cb->map[seg], maplen),
|
|
|
|
PCI_DMA_TODEVICE);
|
|
|
|
}
|
|
|
|
|
|
|
|
pci_unmap_single(qdev->pdev,
|
|
|
|
pci_unmap_addr(&tx_cb->map[0], mapaddr),
|
|
|
|
pci_unmap_addr(&tx_cb->map[0], maplen),
|
|
|
|
PCI_DMA_TODEVICE);
|
|
|
|
|
|
|
|
return NETDEV_TX_BUSY;
|
|
|
|
|
2007-02-27 03:06:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The difference between 3022 and 3032 sends:
|
|
|
|
* 3022 only supports a simple single segment transmission.
|
|
|
|
* 3032 supports checksumming and scatter/gather lists (fragments).
|
|
|
|
* The 3032 supports sglists by using the 3 addr/len pairs (ALP)
|
|
|
|
* in the IOCB plus a chain of outbound address lists (OAL) that
|
|
|
|
* each contain 5 ALPs. The last ALP of the IOCB (3rd) or OAL (5th)
|
|
|
|
* will used to point to an OAL when more ALP entries are required.
|
|
|
|
* The IOCB is always the top of the chain followed by one or more
|
|
|
|
* OALs (when necessary).
|
|
|
|
*/
|
|
|
|
static int ql3xxx_send(struct sk_buff *skb, struct net_device *ndev)
|
|
|
|
{
|
|
|
|
struct ql3_adapter *qdev = (struct ql3_adapter *)netdev_priv(ndev);
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
|
|
|
|
struct ql_tx_buf_cb *tx_cb;
|
|
|
|
u32 tot_len = skb->len;
|
|
|
|
struct ob_mac_iocb_req *mac_iocb_ptr;
|
|
|
|
|
|
|
|
if (unlikely(atomic_read(&qdev->tx_count) < 2)) {
|
|
|
|
return NETDEV_TX_BUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
tx_cb = &qdev->tx_buf[qdev->req_producer_index] ;
|
2007-02-27 03:06:42 +08:00
|
|
|
if((tx_cb->seg_count = ql_get_seg_count(qdev,
|
|
|
|
(skb_shinfo(skb)->nr_frags))) == -1) {
|
2007-02-27 03:06:39 +08:00
|
|
|
printk(KERN_ERR PFX"%s: invalid segment count!\n",__func__);
|
|
|
|
return NETDEV_TX_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
mac_iocb_ptr = tx_cb->queue_entry;
|
2007-03-27 04:42:57 +08:00
|
|
|
memset((void *)mac_iocb_ptr, 0, sizeof(struct ob_mac_iocb_req));
|
2007-02-27 03:06:39 +08:00
|
|
|
mac_iocb_ptr->opcode = qdev->mac_ob_opcode;
|
|
|
|
mac_iocb_ptr->flags = OB_MAC_IOCB_REQ_X;
|
|
|
|
mac_iocb_ptr->flags |= qdev->mb_bit_mask;
|
|
|
|
mac_iocb_ptr->transaction_id = qdev->req_producer_index;
|
|
|
|
mac_iocb_ptr->data_len = cpu_to_le16((u16) tot_len);
|
|
|
|
tx_cb->skb = skb;
|
2007-02-27 03:06:42 +08:00
|
|
|
if (qdev->device_id == QL3032_DEVICE_ID &&
|
|
|
|
skb->ip_summed == CHECKSUM_PARTIAL)
|
2007-02-27 03:06:39 +08:00
|
|
|
ql_hw_csum_setup(skb, mac_iocb_ptr);
|
|
|
|
|
|
|
|
if(ql_send_map(qdev,mac_iocb_ptr,tx_cb,skb) != NETDEV_TX_OK) {
|
|
|
|
printk(KERN_ERR PFX"%s: Could not map the segments!\n",__func__);
|
|
|
|
return NETDEV_TX_BUSY;
|
|
|
|
}
|
|
|
|
|
2007-01-04 08:26:08 +08:00
|
|
|
wmb();
|
2006-07-25 15:40:21 +08:00
|
|
|
qdev->req_producer_index++;
|
|
|
|
if (qdev->req_producer_index == NUM_REQ_Q_ENTRIES)
|
|
|
|
qdev->req_producer_index = 0;
|
|
|
|
wmb();
|
|
|
|
ql_write_common_reg_l(qdev,
|
2006-09-25 09:53:53 +08:00
|
|
|
&port_regs->CommonRegs.reqQProducerIndex,
|
2006-07-25 15:40:21 +08:00
|
|
|
qdev->req_producer_index);
|
|
|
|
|
|
|
|
ndev->trans_start = jiffies;
|
|
|
|
if (netif_msg_tx_queued(qdev))
|
|
|
|
printk(KERN_DEBUG PFX "%s: tx queued, slot %d, len %d\n",
|
|
|
|
ndev->name, qdev->req_producer_index, skb->len);
|
|
|
|
|
2007-01-04 08:26:08 +08:00
|
|
|
atomic_dec(&qdev->tx_count);
|
2006-07-25 15:40:21 +08:00
|
|
|
return NETDEV_TX_OK;
|
|
|
|
}
|
2007-01-04 08:26:08 +08:00
|
|
|
|
2006-07-25 15:40:21 +08:00
|
|
|
static int ql_alloc_net_req_rsp_queues(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
qdev->req_q_size =
|
|
|
|
(u32) (NUM_REQ_Q_ENTRIES * sizeof(struct ob_mac_iocb_req));
|
|
|
|
|
|
|
|
qdev->req_q_virt_addr =
|
|
|
|
pci_alloc_consistent(qdev->pdev,
|
|
|
|
(size_t) qdev->req_q_size,
|
|
|
|
&qdev->req_q_phy_addr);
|
|
|
|
|
|
|
|
if ((qdev->req_q_virt_addr == NULL) ||
|
|
|
|
LS_64BITS(qdev->req_q_phy_addr) & (qdev->req_q_size - 1)) {
|
|
|
|
printk(KERN_ERR PFX "%s: reqQ failed.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
qdev->rsp_q_size = NUM_RSP_Q_ENTRIES * sizeof(struct net_rsp_iocb);
|
|
|
|
|
|
|
|
qdev->rsp_q_virt_addr =
|
|
|
|
pci_alloc_consistent(qdev->pdev,
|
|
|
|
(size_t) qdev->rsp_q_size,
|
|
|
|
&qdev->rsp_q_phy_addr);
|
|
|
|
|
|
|
|
if ((qdev->rsp_q_virt_addr == NULL) ||
|
|
|
|
LS_64BITS(qdev->rsp_q_phy_addr) & (qdev->rsp_q_size - 1)) {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: rspQ allocation failed\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
pci_free_consistent(qdev->pdev, (size_t) qdev->req_q_size,
|
|
|
|
qdev->req_q_virt_addr,
|
|
|
|
qdev->req_q_phy_addr);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_bit(QL_ALLOC_REQ_RSP_Q_DONE,&qdev->flags);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_free_net_req_rsp_queues(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
if (!test_bit(QL_ALLOC_REQ_RSP_Q_DONE,&qdev->flags)) {
|
|
|
|
printk(KERN_INFO PFX
|
|
|
|
"%s: Already done.\n", qdev->ndev->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pci_free_consistent(qdev->pdev,
|
|
|
|
qdev->req_q_size,
|
|
|
|
qdev->req_q_virt_addr, qdev->req_q_phy_addr);
|
|
|
|
|
|
|
|
qdev->req_q_virt_addr = NULL;
|
|
|
|
|
|
|
|
pci_free_consistent(qdev->pdev,
|
|
|
|
qdev->rsp_q_size,
|
|
|
|
qdev->rsp_q_virt_addr, qdev->rsp_q_phy_addr);
|
|
|
|
|
|
|
|
qdev->rsp_q_virt_addr = NULL;
|
|
|
|
|
|
|
|
clear_bit(QL_ALLOC_REQ_RSP_Q_DONE,&qdev->flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_alloc_buffer_queues(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
/* Create Large Buffer Queue */
|
|
|
|
qdev->lrg_buf_q_size =
|
2007-02-27 03:06:37 +08:00
|
|
|
qdev->num_lbufq_entries * sizeof(struct lrg_buf_q_entry);
|
2006-07-25 15:40:21 +08:00
|
|
|
if (qdev->lrg_buf_q_size < PAGE_SIZE)
|
|
|
|
qdev->lrg_buf_q_alloc_size = PAGE_SIZE;
|
|
|
|
else
|
|
|
|
qdev->lrg_buf_q_alloc_size = qdev->lrg_buf_q_size * 2;
|
|
|
|
|
2007-02-27 03:06:37 +08:00
|
|
|
qdev->lrg_buf = kmalloc(qdev->num_large_buffers * sizeof(struct ql_rcv_buf_cb),GFP_KERNEL);
|
|
|
|
if (qdev->lrg_buf == NULL) {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: qdev->lrg_buf alloc failed.\n", qdev->ndev->name);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2006-07-25 15:40:21 +08:00
|
|
|
qdev->lrg_buf_q_alloc_virt_addr =
|
|
|
|
pci_alloc_consistent(qdev->pdev,
|
|
|
|
qdev->lrg_buf_q_alloc_size,
|
|
|
|
&qdev->lrg_buf_q_alloc_phy_addr);
|
|
|
|
|
|
|
|
if (qdev->lrg_buf_q_alloc_virt_addr == NULL) {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: lBufQ failed\n", qdev->ndev->name);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
qdev->lrg_buf_q_virt_addr = qdev->lrg_buf_q_alloc_virt_addr;
|
|
|
|
qdev->lrg_buf_q_phy_addr = qdev->lrg_buf_q_alloc_phy_addr;
|
|
|
|
|
|
|
|
/* Create Small Buffer Queue */
|
|
|
|
qdev->small_buf_q_size =
|
|
|
|
NUM_SBUFQ_ENTRIES * sizeof(struct lrg_buf_q_entry);
|
|
|
|
if (qdev->small_buf_q_size < PAGE_SIZE)
|
|
|
|
qdev->small_buf_q_alloc_size = PAGE_SIZE;
|
|
|
|
else
|
|
|
|
qdev->small_buf_q_alloc_size = qdev->small_buf_q_size * 2;
|
|
|
|
|
|
|
|
qdev->small_buf_q_alloc_virt_addr =
|
|
|
|
pci_alloc_consistent(qdev->pdev,
|
|
|
|
qdev->small_buf_q_alloc_size,
|
|
|
|
&qdev->small_buf_q_alloc_phy_addr);
|
|
|
|
|
|
|
|
if (qdev->small_buf_q_alloc_virt_addr == NULL) {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: Small Buffer Queue allocation failed.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
pci_free_consistent(qdev->pdev, qdev->lrg_buf_q_alloc_size,
|
|
|
|
qdev->lrg_buf_q_alloc_virt_addr,
|
|
|
|
qdev->lrg_buf_q_alloc_phy_addr);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
qdev->small_buf_q_virt_addr = qdev->small_buf_q_alloc_virt_addr;
|
|
|
|
qdev->small_buf_q_phy_addr = qdev->small_buf_q_alloc_phy_addr;
|
|
|
|
set_bit(QL_ALLOC_BUFQS_DONE,&qdev->flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_free_buffer_queues(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
if (!test_bit(QL_ALLOC_BUFQS_DONE,&qdev->flags)) {
|
|
|
|
printk(KERN_INFO PFX
|
|
|
|
"%s: Already done.\n", qdev->ndev->name);
|
|
|
|
return;
|
|
|
|
}
|
2007-02-27 03:06:37 +08:00
|
|
|
if(qdev->lrg_buf) kfree(qdev->lrg_buf);
|
2006-07-25 15:40:21 +08:00
|
|
|
pci_free_consistent(qdev->pdev,
|
|
|
|
qdev->lrg_buf_q_alloc_size,
|
|
|
|
qdev->lrg_buf_q_alloc_virt_addr,
|
|
|
|
qdev->lrg_buf_q_alloc_phy_addr);
|
|
|
|
|
|
|
|
qdev->lrg_buf_q_virt_addr = NULL;
|
|
|
|
|
|
|
|
pci_free_consistent(qdev->pdev,
|
|
|
|
qdev->small_buf_q_alloc_size,
|
|
|
|
qdev->small_buf_q_alloc_virt_addr,
|
|
|
|
qdev->small_buf_q_alloc_phy_addr);
|
|
|
|
|
|
|
|
qdev->small_buf_q_virt_addr = NULL;
|
|
|
|
|
|
|
|
clear_bit(QL_ALLOC_BUFQS_DONE,&qdev->flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_alloc_small_buffers(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct bufq_addr_element *small_buf_q_entry;
|
|
|
|
|
|
|
|
/* Currently we allocate on one of memory and use it for smallbuffers */
|
|
|
|
qdev->small_buf_total_size =
|
|
|
|
(QL_ADDR_ELE_PER_BUFQ_ENTRY * NUM_SBUFQ_ENTRIES *
|
|
|
|
QL_SMALL_BUFFER_SIZE);
|
|
|
|
|
|
|
|
qdev->small_buf_virt_addr =
|
|
|
|
pci_alloc_consistent(qdev->pdev,
|
|
|
|
qdev->small_buf_total_size,
|
|
|
|
&qdev->small_buf_phy_addr);
|
|
|
|
|
|
|
|
if (qdev->small_buf_virt_addr == NULL) {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: Failed to get small buffer memory.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
qdev->small_buf_phy_addr_low = LS_64BITS(qdev->small_buf_phy_addr);
|
|
|
|
qdev->small_buf_phy_addr_high = MS_64BITS(qdev->small_buf_phy_addr);
|
|
|
|
|
|
|
|
small_buf_q_entry = qdev->small_buf_q_virt_addr;
|
|
|
|
|
|
|
|
/* Initialize the small buffer queue. */
|
|
|
|
for (i = 0; i < (QL_ADDR_ELE_PER_BUFQ_ENTRY * NUM_SBUFQ_ENTRIES); i++) {
|
|
|
|
small_buf_q_entry->addr_high =
|
|
|
|
cpu_to_le32(qdev->small_buf_phy_addr_high);
|
|
|
|
small_buf_q_entry->addr_low =
|
|
|
|
cpu_to_le32(qdev->small_buf_phy_addr_low +
|
|
|
|
(i * QL_SMALL_BUFFER_SIZE));
|
|
|
|
small_buf_q_entry++;
|
|
|
|
}
|
|
|
|
qdev->small_buf_index = 0;
|
|
|
|
set_bit(QL_ALLOC_SMALL_BUF_DONE,&qdev->flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_free_small_buffers(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
if (!test_bit(QL_ALLOC_SMALL_BUF_DONE,&qdev->flags)) {
|
|
|
|
printk(KERN_INFO PFX
|
|
|
|
"%s: Already done.\n", qdev->ndev->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (qdev->small_buf_virt_addr != NULL) {
|
|
|
|
pci_free_consistent(qdev->pdev,
|
|
|
|
qdev->small_buf_total_size,
|
|
|
|
qdev->small_buf_virt_addr,
|
|
|
|
qdev->small_buf_phy_addr);
|
|
|
|
|
|
|
|
qdev->small_buf_virt_addr = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_free_large_buffers(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
struct ql_rcv_buf_cb *lrg_buf_cb;
|
|
|
|
|
2007-02-27 03:06:37 +08:00
|
|
|
for (i = 0; i < qdev->num_large_buffers; i++) {
|
2006-07-25 15:40:21 +08:00
|
|
|
lrg_buf_cb = &qdev->lrg_buf[i];
|
|
|
|
if (lrg_buf_cb->skb) {
|
|
|
|
dev_kfree_skb(lrg_buf_cb->skb);
|
|
|
|
pci_unmap_single(qdev->pdev,
|
|
|
|
pci_unmap_addr(lrg_buf_cb, mapaddr),
|
|
|
|
pci_unmap_len(lrg_buf_cb, maplen),
|
|
|
|
PCI_DMA_FROMDEVICE);
|
|
|
|
memset(lrg_buf_cb, 0, sizeof(struct ql_rcv_buf_cb));
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_init_large_buffers(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct ql_rcv_buf_cb *lrg_buf_cb;
|
|
|
|
struct bufq_addr_element *buf_addr_ele = qdev->lrg_buf_q_virt_addr;
|
|
|
|
|
2007-02-27 03:06:37 +08:00
|
|
|
for (i = 0; i < qdev->num_large_buffers; i++) {
|
2006-07-25 15:40:21 +08:00
|
|
|
lrg_buf_cb = &qdev->lrg_buf[i];
|
|
|
|
buf_addr_ele->addr_high = lrg_buf_cb->buf_phy_addr_high;
|
|
|
|
buf_addr_ele->addr_low = lrg_buf_cb->buf_phy_addr_low;
|
|
|
|
buf_addr_ele++;
|
|
|
|
}
|
|
|
|
qdev->lrg_buf_index = 0;
|
|
|
|
qdev->lrg_buf_skb_check = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_alloc_large_buffers(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct ql_rcv_buf_cb *lrg_buf_cb;
|
|
|
|
struct sk_buff *skb;
|
2007-02-27 03:06:40 +08:00
|
|
|
dma_addr_t map;
|
|
|
|
int err;
|
2006-07-25 15:40:21 +08:00
|
|
|
|
2007-02-27 03:06:37 +08:00
|
|
|
for (i = 0; i < qdev->num_large_buffers; i++) {
|
2007-02-27 03:06:33 +08:00
|
|
|
skb = netdev_alloc_skb(qdev->ndev,
|
|
|
|
qdev->lrg_buffer_len);
|
2006-07-25 15:40:21 +08:00
|
|
|
if (unlikely(!skb)) {
|
|
|
|
/* Better luck next round */
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: large buff alloc failed, "
|
|
|
|
"for %d bytes at index %d.\n",
|
|
|
|
qdev->ndev->name,
|
|
|
|
qdev->lrg_buffer_len * 2, i);
|
|
|
|
ql_free_large_buffers(qdev);
|
|
|
|
return -ENOMEM;
|
|
|
|
} else {
|
|
|
|
|
|
|
|
lrg_buf_cb = &qdev->lrg_buf[i];
|
|
|
|
memset(lrg_buf_cb, 0, sizeof(struct ql_rcv_buf_cb));
|
|
|
|
lrg_buf_cb->index = i;
|
|
|
|
lrg_buf_cb->skb = skb;
|
|
|
|
/*
|
|
|
|
* We save some space to copy the ethhdr from first
|
|
|
|
* buffer
|
|
|
|
*/
|
|
|
|
skb_reserve(skb, QL_HEADER_SPACE);
|
|
|
|
map = pci_map_single(qdev->pdev,
|
|
|
|
skb->data,
|
|
|
|
qdev->lrg_buffer_len -
|
|
|
|
QL_HEADER_SPACE,
|
|
|
|
PCI_DMA_FROMDEVICE);
|
2007-02-27 03:06:40 +08:00
|
|
|
|
|
|
|
err = pci_dma_mapping_error(map);
|
|
|
|
if(err) {
|
|
|
|
printk(KERN_ERR "%s: PCI mapping failed with error: %d\n",
|
|
|
|
qdev->ndev->name, err);
|
|
|
|
ql_free_large_buffers(qdev);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2006-07-25 15:40:21 +08:00
|
|
|
pci_unmap_addr_set(lrg_buf_cb, mapaddr, map);
|
|
|
|
pci_unmap_len_set(lrg_buf_cb, maplen,
|
|
|
|
qdev->lrg_buffer_len -
|
|
|
|
QL_HEADER_SPACE);
|
|
|
|
lrg_buf_cb->buf_phy_addr_low =
|
|
|
|
cpu_to_le32(LS_64BITS(map));
|
|
|
|
lrg_buf_cb->buf_phy_addr_high =
|
|
|
|
cpu_to_le32(MS_64BITS(map));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-01-04 08:26:08 +08:00
|
|
|
static void ql_free_send_free_list(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct ql_tx_buf_cb *tx_cb;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
tx_cb = &qdev->tx_buf[0];
|
|
|
|
for (i = 0; i < NUM_REQ_Q_ENTRIES; i++) {
|
|
|
|
if (tx_cb->oal) {
|
|
|
|
kfree(tx_cb->oal);
|
|
|
|
tx_cb->oal = NULL;
|
|
|
|
}
|
|
|
|
tx_cb++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_create_send_free_list(struct ql3_adapter *qdev)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
|
|
|
struct ql_tx_buf_cb *tx_cb;
|
|
|
|
int i;
|
|
|
|
struct ob_mac_iocb_req *req_q_curr =
|
|
|
|
qdev->req_q_virt_addr;
|
|
|
|
|
|
|
|
/* Create free list of transmit buffers */
|
|
|
|
for (i = 0; i < NUM_REQ_Q_ENTRIES; i++) {
|
2007-01-04 08:26:08 +08:00
|
|
|
|
2006-07-25 15:40:21 +08:00
|
|
|
tx_cb = &qdev->tx_buf[i];
|
|
|
|
tx_cb->skb = NULL;
|
|
|
|
tx_cb->queue_entry = req_q_curr;
|
|
|
|
req_q_curr++;
|
2007-01-04 08:26:08 +08:00
|
|
|
tx_cb->oal = kmalloc(512, GFP_KERNEL);
|
|
|
|
if (tx_cb->oal == NULL)
|
|
|
|
return -1;
|
2006-07-25 15:40:21 +08:00
|
|
|
}
|
2007-01-04 08:26:08 +08:00
|
|
|
return 0;
|
2006-07-25 15:40:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_alloc_mem_resources(struct ql3_adapter *qdev)
|
|
|
|
{
|
2007-02-27 03:06:37 +08:00
|
|
|
if (qdev->ndev->mtu == NORMAL_MTU_SIZE) {
|
|
|
|
qdev->num_lbufq_entries = NUM_LBUFQ_ENTRIES;
|
2006-07-25 15:40:21 +08:00
|
|
|
qdev->lrg_buffer_len = NORMAL_MTU_SIZE;
|
2007-02-27 03:06:37 +08:00
|
|
|
}
|
2006-07-25 15:40:21 +08:00
|
|
|
else if (qdev->ndev->mtu == JUMBO_MTU_SIZE) {
|
2007-02-27 03:06:37 +08:00
|
|
|
/*
|
|
|
|
* Bigger buffers, so less of them.
|
|
|
|
*/
|
|
|
|
qdev->num_lbufq_entries = JUMBO_NUM_LBUFQ_ENTRIES;
|
2006-07-25 15:40:21 +08:00
|
|
|
qdev->lrg_buffer_len = JUMBO_MTU_SIZE;
|
|
|
|
} else {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: Invalid mtu size. Only 1500 and 9000 are accepted.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2007-02-27 03:06:37 +08:00
|
|
|
qdev->num_large_buffers = qdev->num_lbufq_entries * QL_ADDR_ELE_PER_BUFQ_ENTRY;
|
2006-07-25 15:40:21 +08:00
|
|
|
qdev->lrg_buffer_len += VLAN_ETH_HLEN + VLAN_ID_LEN + QL_HEADER_SPACE;
|
|
|
|
qdev->max_frame_size =
|
|
|
|
(qdev->lrg_buffer_len - QL_HEADER_SPACE) + ETHERNET_CRC_SIZE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First allocate a page of shared memory and use it for shadow
|
|
|
|
* locations of Network Request Queue Consumer Address Register and
|
|
|
|
* Network Completion Queue Producer Index Register
|
|
|
|
*/
|
|
|
|
qdev->shadow_reg_virt_addr =
|
|
|
|
pci_alloc_consistent(qdev->pdev,
|
|
|
|
PAGE_SIZE, &qdev->shadow_reg_phy_addr);
|
|
|
|
|
|
|
|
if (qdev->shadow_reg_virt_addr != NULL) {
|
|
|
|
qdev->preq_consumer_index = (u16 *) qdev->shadow_reg_virt_addr;
|
|
|
|
qdev->req_consumer_index_phy_addr_high =
|
|
|
|
MS_64BITS(qdev->shadow_reg_phy_addr);
|
|
|
|
qdev->req_consumer_index_phy_addr_low =
|
|
|
|
LS_64BITS(qdev->shadow_reg_phy_addr);
|
|
|
|
|
|
|
|
qdev->prsp_producer_index =
|
|
|
|
(u32 *) (((u8 *) qdev->preq_consumer_index) + 8);
|
|
|
|
qdev->rsp_producer_index_phy_addr_high =
|
|
|
|
qdev->req_consumer_index_phy_addr_high;
|
|
|
|
qdev->rsp_producer_index_phy_addr_low =
|
|
|
|
qdev->req_consumer_index_phy_addr_low + 8;
|
|
|
|
} else {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: shadowReg Alloc failed.\n", qdev->ndev->name);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ql_alloc_net_req_rsp_queues(qdev) != 0) {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: ql_alloc_net_req_rsp_queues failed.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
goto err_req_rsp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ql_alloc_buffer_queues(qdev) != 0) {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: ql_alloc_buffer_queues failed.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
goto err_buffer_queues;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ql_alloc_small_buffers(qdev) != 0) {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: ql_alloc_small_buffers failed\n", qdev->ndev->name);
|
|
|
|
goto err_small_buffers;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ql_alloc_large_buffers(qdev) != 0) {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: ql_alloc_large_buffers failed\n", qdev->ndev->name);
|
|
|
|
goto err_small_buffers;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize the large buffer queue. */
|
|
|
|
ql_init_large_buffers(qdev);
|
2007-01-04 08:26:08 +08:00
|
|
|
if (ql_create_send_free_list(qdev))
|
|
|
|
goto err_free_list;
|
2006-07-25 15:40:21 +08:00
|
|
|
|
|
|
|
qdev->rsp_current = qdev->rsp_q_virt_addr;
|
|
|
|
|
|
|
|
return 0;
|
2007-01-04 08:26:08 +08:00
|
|
|
err_free_list:
|
|
|
|
ql_free_send_free_list(qdev);
|
2006-07-25 15:40:21 +08:00
|
|
|
err_small_buffers:
|
|
|
|
ql_free_buffer_queues(qdev);
|
|
|
|
err_buffer_queues:
|
|
|
|
ql_free_net_req_rsp_queues(qdev);
|
|
|
|
err_req_rsp:
|
|
|
|
pci_free_consistent(qdev->pdev,
|
|
|
|
PAGE_SIZE,
|
|
|
|
qdev->shadow_reg_virt_addr,
|
|
|
|
qdev->shadow_reg_phy_addr);
|
|
|
|
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_free_mem_resources(struct ql3_adapter *qdev)
|
|
|
|
{
|
2007-01-04 08:26:08 +08:00
|
|
|
ql_free_send_free_list(qdev);
|
2006-07-25 15:40:21 +08:00
|
|
|
ql_free_large_buffers(qdev);
|
|
|
|
ql_free_small_buffers(qdev);
|
|
|
|
ql_free_buffer_queues(qdev);
|
|
|
|
ql_free_net_req_rsp_queues(qdev);
|
|
|
|
if (qdev->shadow_reg_virt_addr != NULL) {
|
|
|
|
pci_free_consistent(qdev->pdev,
|
|
|
|
PAGE_SIZE,
|
|
|
|
qdev->shadow_reg_virt_addr,
|
|
|
|
qdev->shadow_reg_phy_addr);
|
|
|
|
qdev->shadow_reg_virt_addr = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_init_misc_registers(struct ql3_adapter *qdev)
|
|
|
|
{
|
2006-09-25 09:53:53 +08:00
|
|
|
struct ql3xxx_local_ram_registers __iomem *local_ram =
|
|
|
|
(void __iomem *)qdev->mem_map_registers;
|
2006-07-25 15:40:21 +08:00
|
|
|
|
|
|
|
if(ql_sem_spinlock(qdev, QL_DDR_RAM_SEM_MASK,
|
|
|
|
(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) *
|
|
|
|
2) << 4))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ql_write_page2_reg(qdev,
|
|
|
|
&local_ram->bufletSize, qdev->nvram_data.bufletSize);
|
|
|
|
|
|
|
|
ql_write_page2_reg(qdev,
|
|
|
|
&local_ram->maxBufletCount,
|
|
|
|
qdev->nvram_data.bufletCount);
|
|
|
|
|
|
|
|
ql_write_page2_reg(qdev,
|
|
|
|
&local_ram->freeBufletThresholdLow,
|
|
|
|
(qdev->nvram_data.tcpWindowThreshold25 << 16) |
|
|
|
|
(qdev->nvram_data.tcpWindowThreshold0));
|
|
|
|
|
|
|
|
ql_write_page2_reg(qdev,
|
|
|
|
&local_ram->freeBufletThresholdHigh,
|
|
|
|
qdev->nvram_data.tcpWindowThreshold50);
|
|
|
|
|
|
|
|
ql_write_page2_reg(qdev,
|
|
|
|
&local_ram->ipHashTableBase,
|
|
|
|
(qdev->nvram_data.ipHashTableBaseHi << 16) |
|
|
|
|
qdev->nvram_data.ipHashTableBaseLo);
|
|
|
|
ql_write_page2_reg(qdev,
|
|
|
|
&local_ram->ipHashTableCount,
|
|
|
|
qdev->nvram_data.ipHashTableSize);
|
|
|
|
ql_write_page2_reg(qdev,
|
|
|
|
&local_ram->tcpHashTableBase,
|
|
|
|
(qdev->nvram_data.tcpHashTableBaseHi << 16) |
|
|
|
|
qdev->nvram_data.tcpHashTableBaseLo);
|
|
|
|
ql_write_page2_reg(qdev,
|
|
|
|
&local_ram->tcpHashTableCount,
|
|
|
|
qdev->nvram_data.tcpHashTableSize);
|
|
|
|
ql_write_page2_reg(qdev,
|
|
|
|
&local_ram->ncbBase,
|
|
|
|
(qdev->nvram_data.ncbTableBaseHi << 16) |
|
|
|
|
qdev->nvram_data.ncbTableBaseLo);
|
|
|
|
ql_write_page2_reg(qdev,
|
|
|
|
&local_ram->maxNcbCount,
|
|
|
|
qdev->nvram_data.ncbTableSize);
|
|
|
|
ql_write_page2_reg(qdev,
|
|
|
|
&local_ram->drbBase,
|
|
|
|
(qdev->nvram_data.drbTableBaseHi << 16) |
|
|
|
|
qdev->nvram_data.drbTableBaseLo);
|
|
|
|
ql_write_page2_reg(qdev,
|
|
|
|
&local_ram->maxDrbCount,
|
|
|
|
qdev->nvram_data.drbTableSize);
|
|
|
|
ql_sem_unlock(qdev, QL_DDR_RAM_SEM_MASK);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_adapter_initialize(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
u32 value;
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
|
|
|
|
struct ql3xxx_host_memory_registers __iomem *hmem_regs =
|
2006-09-25 09:53:53 +08:00
|
|
|
(void __iomem *)port_regs;
|
2006-07-25 15:40:21 +08:00
|
|
|
u32 delay = 10;
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
if(ql_mii_setup(qdev))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Bring out PHY out of reset */
|
|
|
|
ql_write_common_reg(qdev, &port_regs->CommonRegs.serialPortInterfaceReg,
|
|
|
|
(ISP_SERIAL_PORT_IF_WE |
|
|
|
|
(ISP_SERIAL_PORT_IF_WE << 16)));
|
|
|
|
|
|
|
|
qdev->port_link_state = LS_DOWN;
|
|
|
|
netif_carrier_off(qdev->ndev);
|
|
|
|
|
|
|
|
/* V2 chip fix for ARS-39168. */
|
|
|
|
ql_write_common_reg(qdev, &port_regs->CommonRegs.serialPortInterfaceReg,
|
|
|
|
(ISP_SERIAL_PORT_IF_SDE |
|
|
|
|
(ISP_SERIAL_PORT_IF_SDE << 16)));
|
|
|
|
|
|
|
|
/* Request Queue Registers */
|
|
|
|
*((u32 *) (qdev->preq_consumer_index)) = 0;
|
|
|
|
atomic_set(&qdev->tx_count,NUM_REQ_Q_ENTRIES);
|
|
|
|
qdev->req_producer_index = 0;
|
|
|
|
|
|
|
|
ql_write_page1_reg(qdev,
|
|
|
|
&hmem_regs->reqConsumerIndexAddrHigh,
|
|
|
|
qdev->req_consumer_index_phy_addr_high);
|
|
|
|
ql_write_page1_reg(qdev,
|
|
|
|
&hmem_regs->reqConsumerIndexAddrLow,
|
|
|
|
qdev->req_consumer_index_phy_addr_low);
|
|
|
|
|
|
|
|
ql_write_page1_reg(qdev,
|
|
|
|
&hmem_regs->reqBaseAddrHigh,
|
|
|
|
MS_64BITS(qdev->req_q_phy_addr));
|
|
|
|
ql_write_page1_reg(qdev,
|
|
|
|
&hmem_regs->reqBaseAddrLow,
|
|
|
|
LS_64BITS(qdev->req_q_phy_addr));
|
|
|
|
ql_write_page1_reg(qdev, &hmem_regs->reqLength, NUM_REQ_Q_ENTRIES);
|
|
|
|
|
|
|
|
/* Response Queue Registers */
|
|
|
|
*((u16 *) (qdev->prsp_producer_index)) = 0;
|
|
|
|
qdev->rsp_consumer_index = 0;
|
|
|
|
qdev->rsp_current = qdev->rsp_q_virt_addr;
|
|
|
|
|
|
|
|
ql_write_page1_reg(qdev,
|
|
|
|
&hmem_regs->rspProducerIndexAddrHigh,
|
|
|
|
qdev->rsp_producer_index_phy_addr_high);
|
|
|
|
|
|
|
|
ql_write_page1_reg(qdev,
|
|
|
|
&hmem_regs->rspProducerIndexAddrLow,
|
|
|
|
qdev->rsp_producer_index_phy_addr_low);
|
|
|
|
|
|
|
|
ql_write_page1_reg(qdev,
|
|
|
|
&hmem_regs->rspBaseAddrHigh,
|
|
|
|
MS_64BITS(qdev->rsp_q_phy_addr));
|
|
|
|
|
|
|
|
ql_write_page1_reg(qdev,
|
|
|
|
&hmem_regs->rspBaseAddrLow,
|
|
|
|
LS_64BITS(qdev->rsp_q_phy_addr));
|
|
|
|
|
|
|
|
ql_write_page1_reg(qdev, &hmem_regs->rspLength, NUM_RSP_Q_ENTRIES);
|
|
|
|
|
|
|
|
/* Large Buffer Queue */
|
|
|
|
ql_write_page1_reg(qdev,
|
|
|
|
&hmem_regs->rxLargeQBaseAddrHigh,
|
|
|
|
MS_64BITS(qdev->lrg_buf_q_phy_addr));
|
|
|
|
|
|
|
|
ql_write_page1_reg(qdev,
|
|
|
|
&hmem_regs->rxLargeQBaseAddrLow,
|
|
|
|
LS_64BITS(qdev->lrg_buf_q_phy_addr));
|
|
|
|
|
2007-02-27 03:06:37 +08:00
|
|
|
ql_write_page1_reg(qdev, &hmem_regs->rxLargeQLength, qdev->num_lbufq_entries);
|
2006-07-25 15:40:21 +08:00
|
|
|
|
|
|
|
ql_write_page1_reg(qdev,
|
|
|
|
&hmem_regs->rxLargeBufferLength,
|
|
|
|
qdev->lrg_buffer_len);
|
|
|
|
|
|
|
|
/* Small Buffer Queue */
|
|
|
|
ql_write_page1_reg(qdev,
|
|
|
|
&hmem_regs->rxSmallQBaseAddrHigh,
|
|
|
|
MS_64BITS(qdev->small_buf_q_phy_addr));
|
|
|
|
|
|
|
|
ql_write_page1_reg(qdev,
|
|
|
|
&hmem_regs->rxSmallQBaseAddrLow,
|
|
|
|
LS_64BITS(qdev->small_buf_q_phy_addr));
|
|
|
|
|
|
|
|
ql_write_page1_reg(qdev, &hmem_regs->rxSmallQLength, NUM_SBUFQ_ENTRIES);
|
|
|
|
ql_write_page1_reg(qdev,
|
|
|
|
&hmem_regs->rxSmallBufferLength,
|
|
|
|
QL_SMALL_BUFFER_SIZE);
|
|
|
|
|
|
|
|
qdev->small_buf_q_producer_index = NUM_SBUFQ_ENTRIES - 1;
|
|
|
|
qdev->small_buf_release_cnt = 8;
|
2007-02-27 03:06:37 +08:00
|
|
|
qdev->lrg_buf_q_producer_index = qdev->num_lbufq_entries - 1;
|
2006-07-25 15:40:21 +08:00
|
|
|
qdev->lrg_buf_release_cnt = 8;
|
|
|
|
qdev->lrg_buf_next_free =
|
|
|
|
(struct bufq_addr_element *)qdev->lrg_buf_q_virt_addr;
|
|
|
|
qdev->small_buf_index = 0;
|
|
|
|
qdev->lrg_buf_index = 0;
|
|
|
|
qdev->lrg_buf_free_count = 0;
|
|
|
|
qdev->lrg_buf_free_head = NULL;
|
|
|
|
qdev->lrg_buf_free_tail = NULL;
|
|
|
|
|
|
|
|
ql_write_common_reg(qdev,
|
2006-09-25 09:53:53 +08:00
|
|
|
&port_regs->CommonRegs.
|
2006-07-25 15:40:21 +08:00
|
|
|
rxSmallQProducerIndex,
|
|
|
|
qdev->small_buf_q_producer_index);
|
|
|
|
ql_write_common_reg(qdev,
|
2006-09-25 09:53:53 +08:00
|
|
|
&port_regs->CommonRegs.
|
2006-07-25 15:40:21 +08:00
|
|
|
rxLargeQProducerIndex,
|
|
|
|
qdev->lrg_buf_q_producer_index);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find out if the chip has already been initialized. If it has, then
|
|
|
|
* we skip some of the initialization.
|
|
|
|
*/
|
|
|
|
clear_bit(QL_LINK_MASTER, &qdev->flags);
|
|
|
|
value = ql_read_page0_reg(qdev, &port_regs->portStatus);
|
|
|
|
if ((value & PORT_STATUS_IC) == 0) {
|
|
|
|
|
|
|
|
/* Chip has not been configured yet, so let it rip. */
|
|
|
|
if(ql_init_misc_registers(qdev)) {
|
|
|
|
status = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
value = qdev->nvram_data.tcpMaxWindowSize;
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->tcpMaxWindow, value);
|
|
|
|
|
|
|
|
value = (0xFFFF << 16) | qdev->nvram_data.extHwConfig;
|
|
|
|
|
|
|
|
if(ql_sem_spinlock(qdev, QL_FLASH_SEM_MASK,
|
|
|
|
(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index)
|
|
|
|
* 2) << 13)) {
|
|
|
|
status = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->ExternalHWConfig, value);
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->InternalChipConfig,
|
|
|
|
(((INTERNAL_CHIP_SD | INTERNAL_CHIP_WE) <<
|
|
|
|
16) | (INTERNAL_CHIP_SD |
|
|
|
|
INTERNAL_CHIP_WE)));
|
|
|
|
ql_sem_unlock(qdev, QL_FLASH_SEM_MASK);
|
|
|
|
}
|
|
|
|
|
2007-03-27 04:43:00 +08:00
|
|
|
if (qdev->mac_index)
|
|
|
|
ql_write_page0_reg(qdev,
|
|
|
|
&port_regs->mac1MaxFrameLengthReg,
|
|
|
|
qdev->max_frame_size);
|
|
|
|
else
|
|
|
|
ql_write_page0_reg(qdev,
|
|
|
|
&port_regs->mac0MaxFrameLengthReg,
|
|
|
|
qdev->max_frame_size);
|
2006-07-25 15:40:21 +08:00
|
|
|
|
|
|
|
if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK,
|
|
|
|
(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) *
|
|
|
|
2) << 7)) {
|
|
|
|
status = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2007-03-27 03:43:52 +08:00
|
|
|
PHY_Setup(qdev);
|
2006-07-25 15:40:21 +08:00
|
|
|
ql_init_scan_mode(qdev);
|
|
|
|
ql_get_phy_owner(qdev);
|
|
|
|
|
|
|
|
/* Load the MAC Configuration */
|
|
|
|
|
|
|
|
/* Program lower 32 bits of the MAC address */
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macAddrIndirectPtrReg,
|
|
|
|
(MAC_ADDR_INDIRECT_PTR_REG_RP_MASK << 16));
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macAddrDataReg,
|
|
|
|
((qdev->ndev->dev_addr[2] << 24)
|
|
|
|
| (qdev->ndev->dev_addr[3] << 16)
|
|
|
|
| (qdev->ndev->dev_addr[4] << 8)
|
|
|
|
| qdev->ndev->dev_addr[5]));
|
|
|
|
|
|
|
|
/* Program top 16 bits of the MAC address */
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macAddrIndirectPtrReg,
|
|
|
|
((MAC_ADDR_INDIRECT_PTR_REG_RP_MASK << 16) | 1));
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macAddrDataReg,
|
|
|
|
((qdev->ndev->dev_addr[0] << 8)
|
|
|
|
| qdev->ndev->dev_addr[1]));
|
|
|
|
|
|
|
|
/* Enable Primary MAC */
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macAddrIndirectPtrReg,
|
|
|
|
((MAC_ADDR_INDIRECT_PTR_REG_PE << 16) |
|
|
|
|
MAC_ADDR_INDIRECT_PTR_REG_PE));
|
|
|
|
|
|
|
|
/* Clear Primary and Secondary IP addresses */
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->ipAddrIndexReg,
|
|
|
|
((IP_ADDR_INDEX_REG_MASK << 16) |
|
|
|
|
(qdev->mac_index << 2)));
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->ipAddrDataReg, 0);
|
|
|
|
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->ipAddrIndexReg,
|
|
|
|
((IP_ADDR_INDEX_REG_MASK << 16) |
|
|
|
|
((qdev->mac_index << 2) + 1)));
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->ipAddrDataReg, 0);
|
|
|
|
|
|
|
|
ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK);
|
|
|
|
|
|
|
|
/* Indicate Configuration Complete */
|
|
|
|
ql_write_page0_reg(qdev,
|
|
|
|
&port_regs->portControl,
|
|
|
|
((PORT_CONTROL_CC << 16) | PORT_CONTROL_CC));
|
|
|
|
|
|
|
|
do {
|
|
|
|
value = ql_read_page0_reg(qdev, &port_regs->portStatus);
|
|
|
|
if (value & PORT_STATUS_IC)
|
|
|
|
break;
|
|
|
|
msleep(500);
|
|
|
|
} while (--delay);
|
|
|
|
|
|
|
|
if (delay == 0) {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: Hw Initialization timeout.\n", qdev->ndev->name);
|
|
|
|
status = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Enable Ethernet Function */
|
2007-01-04 08:26:08 +08:00
|
|
|
if (qdev->device_id == QL3032_DEVICE_ID) {
|
|
|
|
value =
|
|
|
|
(QL3032_PORT_CONTROL_EF | QL3032_PORT_CONTROL_KIE |
|
2007-03-27 04:43:00 +08:00
|
|
|
QL3032_PORT_CONTROL_EIv6 | QL3032_PORT_CONTROL_EIv4 |
|
|
|
|
QL3032_PORT_CONTROL_ET);
|
2007-01-04 08:26:08 +08:00
|
|
|
ql_write_page0_reg(qdev, &port_regs->functionControl,
|
|
|
|
((value << 16) | value));
|
|
|
|
} else {
|
|
|
|
value =
|
|
|
|
(PORT_CONTROL_EF | PORT_CONTROL_ET | PORT_CONTROL_EI |
|
|
|
|
PORT_CONTROL_HH);
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->portControl,
|
|
|
|
((value << 16) | value));
|
|
|
|
}
|
|
|
|
|
2006-07-25 15:40:21 +08:00
|
|
|
|
|
|
|
out:
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller holds hw_lock.
|
|
|
|
*/
|
|
|
|
static int ql_adapter_reset(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
|
|
|
|
int status = 0;
|
|
|
|
u16 value;
|
|
|
|
int max_wait_time;
|
|
|
|
|
|
|
|
set_bit(QL_RESET_ACTIVE, &qdev->flags);
|
|
|
|
clear_bit(QL_RESET_DONE, &qdev->flags);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Issue soft reset to chip.
|
|
|
|
*/
|
|
|
|
printk(KERN_DEBUG PFX
|
|
|
|
"%s: Issue soft reset to chip.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
ql_write_common_reg(qdev,
|
2006-09-25 09:53:53 +08:00
|
|
|
&port_regs->CommonRegs.ispControlStatus,
|
2006-07-25 15:40:21 +08:00
|
|
|
((ISP_CONTROL_SR << 16) | ISP_CONTROL_SR));
|
|
|
|
|
|
|
|
/* Wait 3 seconds for reset to complete. */
|
|
|
|
printk(KERN_DEBUG PFX
|
|
|
|
"%s: Wait 10 milliseconds for reset to complete.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
|
|
|
|
/* Wait until the firmware tells us the Soft Reset is done */
|
|
|
|
max_wait_time = 5;
|
|
|
|
do {
|
|
|
|
value =
|
|
|
|
ql_read_common_reg(qdev,
|
|
|
|
&port_regs->CommonRegs.ispControlStatus);
|
|
|
|
if ((value & ISP_CONTROL_SR) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ssleep(1);
|
|
|
|
} while ((--max_wait_time));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Also, make sure that the Network Reset Interrupt bit has been
|
|
|
|
* cleared after the soft reset has taken place.
|
|
|
|
*/
|
|
|
|
value =
|
|
|
|
ql_read_common_reg(qdev, &port_regs->CommonRegs.ispControlStatus);
|
|
|
|
if (value & ISP_CONTROL_RI) {
|
|
|
|
printk(KERN_DEBUG PFX
|
|
|
|
"ql_adapter_reset: clearing RI after reset.\n");
|
|
|
|
ql_write_common_reg(qdev,
|
2006-09-25 09:53:53 +08:00
|
|
|
&port_regs->CommonRegs.
|
2006-07-25 15:40:21 +08:00
|
|
|
ispControlStatus,
|
|
|
|
((ISP_CONTROL_RI << 16) | ISP_CONTROL_RI));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (max_wait_time == 0) {
|
|
|
|
/* Issue Force Soft Reset */
|
|
|
|
ql_write_common_reg(qdev,
|
2006-09-25 09:53:53 +08:00
|
|
|
&port_regs->CommonRegs.
|
2006-07-25 15:40:21 +08:00
|
|
|
ispControlStatus,
|
|
|
|
((ISP_CONTROL_FSR << 16) |
|
|
|
|
ISP_CONTROL_FSR));
|
|
|
|
/*
|
|
|
|
* Wait until the firmware tells us the Force Soft Reset is
|
|
|
|
* done
|
|
|
|
*/
|
|
|
|
max_wait_time = 5;
|
|
|
|
do {
|
|
|
|
value =
|
|
|
|
ql_read_common_reg(qdev,
|
|
|
|
&port_regs->CommonRegs.
|
|
|
|
ispControlStatus);
|
|
|
|
if ((value & ISP_CONTROL_FSR) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ssleep(1);
|
|
|
|
} while ((--max_wait_time));
|
|
|
|
}
|
|
|
|
if (max_wait_time == 0)
|
|
|
|
status = 1;
|
|
|
|
|
|
|
|
clear_bit(QL_RESET_ACTIVE, &qdev->flags);
|
|
|
|
set_bit(QL_RESET_DONE, &qdev->flags);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_set_mac_info(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
|
|
|
|
u32 value, port_status;
|
|
|
|
u8 func_number;
|
|
|
|
|
|
|
|
/* Get the function number */
|
|
|
|
value =
|
|
|
|
ql_read_common_reg_l(qdev, &port_regs->CommonRegs.ispControlStatus);
|
|
|
|
func_number = (u8) ((value >> 4) & OPCODE_FUNC_ID_MASK);
|
|
|
|
port_status = ql_read_page0_reg(qdev, &port_regs->portStatus);
|
|
|
|
switch (value & ISP_CONTROL_FN_MASK) {
|
|
|
|
case ISP_CONTROL_FN0_NET:
|
|
|
|
qdev->mac_index = 0;
|
|
|
|
qdev->mac_ob_opcode = OUTBOUND_MAC_IOCB | func_number;
|
|
|
|
qdev->tcp_ob_opcode = OUTBOUND_TCP_IOCB | func_number;
|
|
|
|
qdev->update_ob_opcode = UPDATE_NCB_IOCB | func_number;
|
|
|
|
qdev->mb_bit_mask = FN0_MA_BITS_MASK;
|
|
|
|
qdev->PHYAddr = PORT0_PHY_ADDRESS;
|
|
|
|
if (port_status & PORT_STATUS_SM0)
|
|
|
|
set_bit(QL_LINK_OPTICAL,&qdev->flags);
|
|
|
|
else
|
|
|
|
clear_bit(QL_LINK_OPTICAL,&qdev->flags);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ISP_CONTROL_FN1_NET:
|
|
|
|
qdev->mac_index = 1;
|
|
|
|
qdev->mac_ob_opcode = OUTBOUND_MAC_IOCB | func_number;
|
|
|
|
qdev->tcp_ob_opcode = OUTBOUND_TCP_IOCB | func_number;
|
|
|
|
qdev->update_ob_opcode = UPDATE_NCB_IOCB | func_number;
|
|
|
|
qdev->mb_bit_mask = FN1_MA_BITS_MASK;
|
|
|
|
qdev->PHYAddr = PORT1_PHY_ADDRESS;
|
|
|
|
if (port_status & PORT_STATUS_SM1)
|
|
|
|
set_bit(QL_LINK_OPTICAL,&qdev->flags);
|
|
|
|
else
|
|
|
|
clear_bit(QL_LINK_OPTICAL,&qdev->flags);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ISP_CONTROL_FN0_SCSI:
|
|
|
|
case ISP_CONTROL_FN1_SCSI:
|
|
|
|
default:
|
|
|
|
printk(KERN_DEBUG PFX
|
|
|
|
"%s: Invalid function number, ispControlStatus = 0x%x\n",
|
|
|
|
qdev->ndev->name,value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
qdev->numPorts = qdev->nvram_data.numPorts;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_display_dev_info(struct net_device *ndev)
|
|
|
|
{
|
|
|
|
struct ql3_adapter *qdev = (struct ql3_adapter *)netdev_priv(ndev);
|
|
|
|
struct pci_dev *pdev = qdev->pdev;
|
|
|
|
|
|
|
|
printk(KERN_INFO PFX
|
2007-01-04 08:26:08 +08:00
|
|
|
"\n%s Adapter %d RevisionID %d found %s on PCI slot %d.\n",
|
|
|
|
DRV_NAME, qdev->index, qdev->chip_rev_id,
|
|
|
|
(qdev->device_id == QL3032_DEVICE_ID) ? "QLA3032" : "QLA3022",
|
|
|
|
qdev->pci_slot);
|
2006-07-25 15:40:21 +08:00
|
|
|
printk(KERN_INFO PFX
|
|
|
|
"%s Interface.\n",
|
|
|
|
test_bit(QL_LINK_OPTICAL,&qdev->flags) ? "OPTICAL" : "COPPER");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Print PCI bus width/type.
|
|
|
|
*/
|
|
|
|
printk(KERN_INFO PFX
|
|
|
|
"Bus interface is %s %s.\n",
|
|
|
|
((qdev->pci_width == 64) ? "64-bit" : "32-bit"),
|
|
|
|
((qdev->pci_x) ? "PCI-X" : "PCI"));
|
|
|
|
|
|
|
|
printk(KERN_INFO PFX
|
|
|
|
"mem IO base address adjusted = 0x%p\n",
|
|
|
|
qdev->mem_map_registers);
|
|
|
|
printk(KERN_INFO PFX "Interrupt number = %d\n", pdev->irq);
|
|
|
|
|
|
|
|
if (netif_msg_probe(qdev))
|
|
|
|
printk(KERN_INFO PFX
|
|
|
|
"%s: MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
|
|
|
|
ndev->name, ndev->dev_addr[0], ndev->dev_addr[1],
|
|
|
|
ndev->dev_addr[2], ndev->dev_addr[3], ndev->dev_addr[4],
|
|
|
|
ndev->dev_addr[5]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_adapter_down(struct ql3_adapter *qdev, int do_reset)
|
|
|
|
{
|
|
|
|
struct net_device *ndev = qdev->ndev;
|
|
|
|
int retval = 0;
|
|
|
|
|
|
|
|
netif_stop_queue(ndev);
|
|
|
|
netif_carrier_off(ndev);
|
|
|
|
|
|
|
|
clear_bit(QL_ADAPTER_UP,&qdev->flags);
|
|
|
|
clear_bit(QL_LINK_MASTER,&qdev->flags);
|
|
|
|
|
|
|
|
ql_disable_interrupts(qdev);
|
|
|
|
|
|
|
|
free_irq(qdev->pdev->irq, ndev);
|
|
|
|
|
|
|
|
if (qdev->msi && test_bit(QL_MSI_ENABLED,&qdev->flags)) {
|
|
|
|
printk(KERN_INFO PFX
|
|
|
|
"%s: calling pci_disable_msi().\n", qdev->ndev->name);
|
|
|
|
clear_bit(QL_MSI_ENABLED,&qdev->flags);
|
|
|
|
pci_disable_msi(qdev->pdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
del_timer_sync(&qdev->adapter_timer);
|
|
|
|
|
|
|
|
netif_poll_disable(ndev);
|
|
|
|
|
|
|
|
if (do_reset) {
|
|
|
|
int soft_reset;
|
|
|
|
unsigned long hw_flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
|
|
|
|
if (ql_wait_for_drvr_lock(qdev)) {
|
|
|
|
if ((soft_reset = ql_adapter_reset(qdev))) {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: ql_adapter_reset(%d) FAILED!\n",
|
|
|
|
ndev->name, qdev->index);
|
|
|
|
}
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: Releaseing driver lock via chip reset.\n",ndev->name);
|
|
|
|
} else {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: Could not acquire driver lock to do "
|
|
|
|
"reset!\n", ndev->name);
|
|
|
|
retval = -1;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
|
|
}
|
|
|
|
ql_free_mem_resources(qdev);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_adapter_up(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct net_device *ndev = qdev->ndev;
|
|
|
|
int err;
|
2007-02-14 16:33:16 +08:00
|
|
|
unsigned long irq_flags = IRQF_SAMPLE_RANDOM | IRQF_SHARED;
|
2006-07-25 15:40:21 +08:00
|
|
|
unsigned long hw_flags;
|
|
|
|
|
|
|
|
if (ql_alloc_mem_resources(qdev)) {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s Unable to allocate buffers.\n", ndev->name);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qdev->msi) {
|
|
|
|
if (pci_enable_msi(qdev->pdev)) {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: User requested MSI, but MSI failed to "
|
|
|
|
"initialize. Continuing without MSI.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
qdev->msi = 0;
|
|
|
|
} else {
|
|
|
|
printk(KERN_INFO PFX "%s: MSI Enabled...\n", qdev->ndev->name);
|
|
|
|
set_bit(QL_MSI_ENABLED,&qdev->flags);
|
2007-02-14 16:33:16 +08:00
|
|
|
irq_flags &= ~IRQF_SHARED;
|
2006-07-25 15:40:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((err = request_irq(qdev->pdev->irq,
|
|
|
|
ql3xxx_isr,
|
|
|
|
irq_flags, ndev->name, ndev))) {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: Failed to reserve interrupt %d already in use.\n",
|
|
|
|
ndev->name, qdev->pdev->irq);
|
|
|
|
goto err_irq;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
|
|
|
|
|
|
|
|
if ((err = ql_wait_for_drvr_lock(qdev))) {
|
|
|
|
if ((err = ql_adapter_initialize(qdev))) {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: Unable to initialize adapter.\n",
|
|
|
|
ndev->name);
|
|
|
|
goto err_init;
|
|
|
|
}
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: Releaseing driver lock.\n",ndev->name);
|
|
|
|
ql_sem_unlock(qdev, QL_DRVR_SEM_MASK);
|
|
|
|
} else {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: Could not aquire driver lock.\n",
|
|
|
|
ndev->name);
|
|
|
|
goto err_lock;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
|
|
|
|
|
|
set_bit(QL_ADAPTER_UP,&qdev->flags);
|
|
|
|
|
|
|
|
mod_timer(&qdev->adapter_timer, jiffies + HZ * 1);
|
|
|
|
|
|
|
|
netif_poll_enable(ndev);
|
|
|
|
ql_enable_interrupts(qdev);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_init:
|
|
|
|
ql_sem_unlock(qdev, QL_DRVR_SEM_MASK);
|
|
|
|
err_lock:
|
2007-02-27 03:06:35 +08:00
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
2006-07-25 15:40:21 +08:00
|
|
|
free_irq(qdev->pdev->irq, ndev);
|
|
|
|
err_irq:
|
|
|
|
if (qdev->msi && test_bit(QL_MSI_ENABLED,&qdev->flags)) {
|
|
|
|
printk(KERN_INFO PFX
|
|
|
|
"%s: calling pci_disable_msi().\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
clear_bit(QL_MSI_ENABLED,&qdev->flags);
|
|
|
|
pci_disable_msi(qdev->pdev);
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql_cycle_adapter(struct ql3_adapter *qdev, int reset)
|
|
|
|
{
|
|
|
|
if( ql_adapter_down(qdev,reset) || ql_adapter_up(qdev)) {
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: Driver up/down cycle failed, "
|
|
|
|
"closing device\n",qdev->ndev->name);
|
|
|
|
dev_close(qdev->ndev);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql3xxx_close(struct net_device *ndev)
|
|
|
|
{
|
|
|
|
struct ql3_adapter *qdev = netdev_priv(ndev);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Wait for device to recover from a reset.
|
|
|
|
* (Rarely happens, but possible.)
|
|
|
|
*/
|
|
|
|
while (!test_bit(QL_ADAPTER_UP,&qdev->flags))
|
|
|
|
msleep(50);
|
|
|
|
|
|
|
|
ql_adapter_down(qdev,QL_DO_RESET);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql3xxx_open(struct net_device *ndev)
|
|
|
|
{
|
|
|
|
struct ql3_adapter *qdev = netdev_priv(ndev);
|
|
|
|
return (ql_adapter_up(qdev));
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct net_device_stats *ql3xxx_get_stats(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct ql3_adapter *qdev = (struct ql3_adapter *)dev->priv;
|
|
|
|
return &qdev->stats;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql3xxx_set_multicast_list(struct net_device *ndev)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We are manually parsing the list in the net_device structure.
|
|
|
|
*/
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ql3xxx_set_mac_address(struct net_device *ndev, void *p)
|
|
|
|
{
|
|
|
|
struct ql3_adapter *qdev = (struct ql3_adapter *)netdev_priv(ndev);
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
|
|
qdev->mem_map_registers;
|
|
|
|
struct sockaddr *addr = p;
|
|
|
|
unsigned long hw_flags;
|
|
|
|
|
|
|
|
if (netif_running(ndev))
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
if (!is_valid_ether_addr(addr->sa_data))
|
|
|
|
return -EADDRNOTAVAIL;
|
|
|
|
|
|
|
|
memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
|
|
|
|
/* Program lower 32 bits of the MAC address */
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macAddrIndirectPtrReg,
|
|
|
|
(MAC_ADDR_INDIRECT_PTR_REG_RP_MASK << 16));
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macAddrDataReg,
|
|
|
|
((ndev->dev_addr[2] << 24) | (ndev->
|
|
|
|
dev_addr[3] << 16) |
|
|
|
|
(ndev->dev_addr[4] << 8) | ndev->dev_addr[5]));
|
|
|
|
|
|
|
|
/* Program top 16 bits of the MAC address */
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macAddrIndirectPtrReg,
|
|
|
|
((MAC_ADDR_INDIRECT_PTR_REG_RP_MASK << 16) | 1));
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macAddrDataReg,
|
|
|
|
((ndev->dev_addr[0] << 8) | ndev->dev_addr[1]));
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql3xxx_tx_timeout(struct net_device *ndev)
|
|
|
|
{
|
|
|
|
struct ql3_adapter *qdev = (struct ql3_adapter *)netdev_priv(ndev);
|
|
|
|
|
|
|
|
printk(KERN_ERR PFX "%s: Resetting...\n", ndev->name);
|
|
|
|
/*
|
|
|
|
* Stop the queues, we've got a problem.
|
|
|
|
*/
|
|
|
|
netif_stop_queue(ndev);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Wake up the worker to process this event.
|
|
|
|
*/
|
2006-11-22 22:57:56 +08:00
|
|
|
queue_delayed_work(qdev->workqueue, &qdev->tx_timeout_work, 0);
|
2006-07-25 15:40:21 +08:00
|
|
|
}
|
|
|
|
|
2006-11-22 22:57:56 +08:00
|
|
|
static void ql_reset_work(struct work_struct *work)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
2006-11-22 22:57:56 +08:00
|
|
|
struct ql3_adapter *qdev =
|
|
|
|
container_of(work, struct ql3_adapter, reset_work.work);
|
2006-07-25 15:40:21 +08:00
|
|
|
struct net_device *ndev = qdev->ndev;
|
|
|
|
u32 value;
|
|
|
|
struct ql_tx_buf_cb *tx_cb;
|
|
|
|
int max_wait_time, i;
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
|
|
|
|
unsigned long hw_flags;
|
|
|
|
|
|
|
|
if (test_bit((QL_RESET_PER_SCSI | QL_RESET_START),&qdev->flags)) {
|
|
|
|
clear_bit(QL_LINK_MASTER,&qdev->flags);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Loop through the active list and return the skb.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < NUM_REQ_Q_ENTRIES; i++) {
|
2007-01-04 08:26:08 +08:00
|
|
|
int j;
|
2006-07-25 15:40:21 +08:00
|
|
|
tx_cb = &qdev->tx_buf[i];
|
|
|
|
if (tx_cb->skb) {
|
|
|
|
printk(KERN_DEBUG PFX
|
|
|
|
"%s: Freeing lost SKB.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
pci_unmap_single(qdev->pdev,
|
2007-01-04 08:26:08 +08:00
|
|
|
pci_unmap_addr(&tx_cb->map[0], mapaddr),
|
|
|
|
pci_unmap_len(&tx_cb->map[0], maplen),
|
|
|
|
PCI_DMA_TODEVICE);
|
|
|
|
for(j=1;j<tx_cb->seg_count;j++) {
|
|
|
|
pci_unmap_page(qdev->pdev,
|
|
|
|
pci_unmap_addr(&tx_cb->map[j],mapaddr),
|
|
|
|
pci_unmap_len(&tx_cb->map[j],maplen),
|
|
|
|
PCI_DMA_TODEVICE);
|
|
|
|
}
|
2006-07-25 15:40:21 +08:00
|
|
|
dev_kfree_skb(tx_cb->skb);
|
|
|
|
tx_cb->skb = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: Clearing NRI after reset.\n", qdev->ndev->name);
|
|
|
|
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
|
|
|
|
ql_write_common_reg(qdev,
|
|
|
|
&port_regs->CommonRegs.
|
|
|
|
ispControlStatus,
|
|
|
|
((ISP_CONTROL_RI << 16) | ISP_CONTROL_RI));
|
|
|
|
/*
|
|
|
|
* Wait the for Soft Reset to Complete.
|
|
|
|
*/
|
|
|
|
max_wait_time = 10;
|
|
|
|
do {
|
|
|
|
value = ql_read_common_reg(qdev,
|
|
|
|
&port_regs->CommonRegs.
|
|
|
|
|
|
|
|
ispControlStatus);
|
|
|
|
if ((value & ISP_CONTROL_SR) == 0) {
|
|
|
|
printk(KERN_DEBUG PFX
|
|
|
|
"%s: reset completed.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (value & ISP_CONTROL_RI) {
|
|
|
|
printk(KERN_DEBUG PFX
|
|
|
|
"%s: clearing NRI after reset.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
ql_write_common_reg(qdev,
|
2006-09-25 09:53:53 +08:00
|
|
|
&port_regs->
|
2006-07-25 15:40:21 +08:00
|
|
|
CommonRegs.
|
|
|
|
ispControlStatus,
|
|
|
|
((ISP_CONTROL_RI <<
|
|
|
|
16) | ISP_CONTROL_RI));
|
|
|
|
}
|
|
|
|
|
|
|
|
ssleep(1);
|
|
|
|
} while (--max_wait_time);
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
|
|
|
|
|
|
if (value & ISP_CONTROL_SR) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the reset flags and clear the board again.
|
|
|
|
* Nothing else to do...
|
|
|
|
*/
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: Timed out waiting for reset to "
|
|
|
|
"complete.\n", ndev->name);
|
|
|
|
printk(KERN_ERR PFX
|
|
|
|
"%s: Do a reset.\n", ndev->name);
|
|
|
|
clear_bit(QL_RESET_PER_SCSI,&qdev->flags);
|
|
|
|
clear_bit(QL_RESET_START,&qdev->flags);
|
|
|
|
ql_cycle_adapter(qdev,QL_DO_RESET);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
clear_bit(QL_RESET_ACTIVE,&qdev->flags);
|
|
|
|
clear_bit(QL_RESET_PER_SCSI,&qdev->flags);
|
|
|
|
clear_bit(QL_RESET_START,&qdev->flags);
|
|
|
|
ql_cycle_adapter(qdev,QL_NO_RESET);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-11-22 22:57:56 +08:00
|
|
|
static void ql_tx_timeout_work(struct work_struct *work)
|
2006-07-25 15:40:21 +08:00
|
|
|
{
|
2006-11-22 22:57:56 +08:00
|
|
|
struct ql3_adapter *qdev =
|
|
|
|
container_of(work, struct ql3_adapter, tx_timeout_work.work);
|
|
|
|
|
|
|
|
ql_cycle_adapter(qdev, QL_DO_RESET);
|
2006-07-25 15:40:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ql_get_board_info(struct ql3_adapter *qdev)
|
|
|
|
{
|
|
|
|
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
|
|
|
|
u32 value;
|
|
|
|
|
|
|
|
value = ql_read_page0_reg_l(qdev, &port_regs->portStatus);
|
|
|
|
|
|
|
|
qdev->chip_rev_id = ((value & PORT_STATUS_REV_ID_MASK) >> 12);
|
|
|
|
if (value & PORT_STATUS_64)
|
|
|
|
qdev->pci_width = 64;
|
|
|
|
else
|
|
|
|
qdev->pci_width = 32;
|
|
|
|
if (value & PORT_STATUS_X)
|
|
|
|
qdev->pci_x = 1;
|
|
|
|
else
|
|
|
|
qdev->pci_x = 0;
|
|
|
|
qdev->pci_slot = (u8) PCI_SLOT(qdev->pdev->devfn);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ql3xxx_timer(unsigned long ptr)
|
|
|
|
{
|
|
|
|
struct ql3_adapter *qdev = (struct ql3_adapter *)ptr;
|
|
|
|
|
|
|
|
if (test_bit(QL_RESET_ACTIVE,&qdev->flags)) {
|
|
|
|
printk(KERN_DEBUG PFX
|
|
|
|
"%s: Reset in progress.\n",
|
|
|
|
qdev->ndev->name);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
ql_link_state_machine(qdev);
|
|
|
|
|
|
|
|
/* Restart timer on 2 second interval. */
|
|
|
|
end:
|
|
|
|
mod_timer(&qdev->adapter_timer, jiffies + HZ * 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __devinit ql3xxx_probe(struct pci_dev *pdev,
|
|
|
|
const struct pci_device_id *pci_entry)
|
|
|
|
{
|
|
|
|
struct net_device *ndev = NULL;
|
|
|
|
struct ql3_adapter *qdev = NULL;
|
|
|
|
static int cards_found = 0;
|
|
|
|
int pci_using_dac, err;
|
|
|
|
|
|
|
|
err = pci_enable_device(pdev);
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_ERR PFX "%s cannot enable PCI device\n",
|
|
|
|
pci_name(pdev));
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = pci_request_regions(pdev, DRV_NAME);
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_ERR PFX "%s cannot obtain PCI resources\n",
|
|
|
|
pci_name(pdev));
|
|
|
|
goto err_out_disable_pdev;
|
|
|
|
}
|
|
|
|
|
|
|
|
pci_set_master(pdev);
|
|
|
|
|
|
|
|
if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) {
|
|
|
|
pci_using_dac = 1;
|
|
|
|
err = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
|
|
|
|
} else if (!(err = pci_set_dma_mask(pdev, DMA_32BIT_MASK))) {
|
|
|
|
pci_using_dac = 0;
|
|
|
|
err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_ERR PFX "%s no usable DMA configuration\n",
|
|
|
|
pci_name(pdev));
|
|
|
|
goto err_out_free_regions;
|
|
|
|
}
|
|
|
|
|
|
|
|
ndev = alloc_etherdev(sizeof(struct ql3_adapter));
|
2007-02-27 03:06:31 +08:00
|
|
|
if (!ndev) {
|
|
|
|
printk(KERN_ERR PFX "%s could not alloc etherdev\n",
|
|
|
|
pci_name(pdev));
|
|
|
|
err = -ENOMEM;
|
2006-07-25 15:40:21 +08:00
|
|
|
goto err_out_free_regions;
|
2007-02-27 03:06:31 +08:00
|
|
|
}
|
2006-07-25 15:40:21 +08:00
|
|
|
|
|
|
|
SET_MODULE_OWNER(ndev);
|
|
|
|
SET_NETDEV_DEV(ndev, &pdev->dev);
|
|
|
|
|
|
|
|
pci_set_drvdata(pdev, ndev);
|
|
|
|
|
|
|
|
qdev = netdev_priv(ndev);
|
|
|
|
qdev->index = cards_found;
|
|
|
|
qdev->ndev = ndev;
|
|
|
|
qdev->pdev = pdev;
|
2007-01-04 08:26:08 +08:00
|
|
|
qdev->device_id = pci_entry->device;
|
2006-07-25 15:40:21 +08:00
|
|
|
qdev->port_link_state = LS_DOWN;
|
|
|
|
if (msi)
|
|
|
|
qdev->msi = 1;
|
|
|
|
|
|
|
|
qdev->msg_enable = netif_msg_init(debug, default_msg);
|
|
|
|
|
2007-01-04 08:26:08 +08:00
|
|
|
if (pci_using_dac)
|
|
|
|
ndev->features |= NETIF_F_HIGHDMA;
|
|
|
|
if (qdev->device_id == QL3032_DEVICE_ID)
|
2007-05-31 05:23:17 +08:00
|
|
|
ndev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
|
2007-01-04 08:26:08 +08:00
|
|
|
|
2006-07-25 15:40:21 +08:00
|
|
|
qdev->mem_map_registers =
|
|
|
|
ioremap_nocache(pci_resource_start(pdev, 1),
|
|
|
|
pci_resource_len(qdev->pdev, 1));
|
|
|
|
if (!qdev->mem_map_registers) {
|
|
|
|
printk(KERN_ERR PFX "%s: cannot map device registers\n",
|
|
|
|
pci_name(pdev));
|
2007-02-27 03:06:31 +08:00
|
|
|
err = -EIO;
|
2006-07-25 15:40:21 +08:00
|
|
|
goto err_out_free_ndev;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock_init(&qdev->adapter_lock);
|
|
|
|
spin_lock_init(&qdev->hw_lock);
|
|
|
|
|
|
|
|
/* Set driver entry points */
|
|
|
|
ndev->open = ql3xxx_open;
|
|
|
|
ndev->hard_start_xmit = ql3xxx_send;
|
|
|
|
ndev->stop = ql3xxx_close;
|
|
|
|
ndev->get_stats = ql3xxx_get_stats;
|
|
|
|
ndev->set_multicast_list = ql3xxx_set_multicast_list;
|
|
|
|
SET_ETHTOOL_OPS(ndev, &ql3xxx_ethtool_ops);
|
|
|
|
ndev->set_mac_address = ql3xxx_set_mac_address;
|
|
|
|
ndev->tx_timeout = ql3xxx_tx_timeout;
|
|
|
|
ndev->watchdog_timeo = 5 * HZ;
|
|
|
|
|
|
|
|
ndev->poll = &ql_poll;
|
|
|
|
ndev->weight = 64;
|
|
|
|
|
|
|
|
ndev->irq = pdev->irq;
|
|
|
|
|
|
|
|
/* make sure the EEPROM is good */
|
|
|
|
if (ql_get_nvram_params(qdev)) {
|
|
|
|
printk(KERN_ALERT PFX
|
|
|
|
"ql3xxx_probe: Adapter #%d, Invalid NVRAM parameters.\n",
|
|
|
|
qdev->index);
|
2007-02-27 03:06:31 +08:00
|
|
|
err = -EIO;
|
2006-07-25 15:40:21 +08:00
|
|
|
goto err_out_iounmap;
|
|
|
|
}
|
|
|
|
|
|
|
|
ql_set_mac_info(qdev);
|
|
|
|
|
|
|
|
/* Validate and set parameters */
|
|
|
|
if (qdev->mac_index) {
|
2007-02-27 03:06:36 +08:00
|
|
|
ndev->mtu = qdev->nvram_data.macCfg_port1.etherMtu_mac ;
|
2006-07-25 15:40:21 +08:00
|
|
|
memcpy(ndev->dev_addr, &qdev->nvram_data.funcCfg_fn2.macAddress,
|
|
|
|
ETH_ALEN);
|
|
|
|
} else {
|
2007-02-27 03:06:36 +08:00
|
|
|
ndev->mtu = qdev->nvram_data.macCfg_port0.etherMtu_mac ;
|
2006-07-25 15:40:21 +08:00
|
|
|
memcpy(ndev->dev_addr, &qdev->nvram_data.funcCfg_fn0.macAddress,
|
|
|
|
ETH_ALEN);
|
|
|
|
}
|
|
|
|
memcpy(ndev->perm_addr, ndev->dev_addr, ndev->addr_len);
|
|
|
|
|
|
|
|
ndev->tx_queue_len = NUM_REQ_Q_ENTRIES;
|
|
|
|
|
|
|
|
/* Turn off support for multicasting */
|
|
|
|
ndev->flags &= ~IFF_MULTICAST;
|
|
|
|
|
|
|
|
/* Record PCI bus information. */
|
|
|
|
ql_get_board_info(qdev);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the Maximum Memory Read Byte Count value. We do this to handle
|
|
|
|
* jumbo frames.
|
|
|
|
*/
|
|
|
|
if (qdev->pci_x) {
|
|
|
|
pci_write_config_word(pdev, (int)0x4e, (u16) 0x0036);
|
|
|
|
}
|
|
|
|
|
|
|
|
err = register_netdev(ndev);
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_ERR PFX "%s: cannot register net device\n",
|
|
|
|
pci_name(pdev));
|
|
|
|
goto err_out_iounmap;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we're going to reset, so assume we have no link for now */
|
|
|
|
|
|
|
|
netif_carrier_off(ndev);
|
|
|
|
netif_stop_queue(ndev);
|
|
|
|
|
|
|
|
qdev->workqueue = create_singlethread_workqueue(ndev->name);
|
2006-11-22 22:57:56 +08:00
|
|
|
INIT_DELAYED_WORK(&qdev->reset_work, ql_reset_work);
|
|
|
|
INIT_DELAYED_WORK(&qdev->tx_timeout_work, ql_tx_timeout_work);
|
2006-07-25 15:40:21 +08:00
|
|
|
|
|
|
|
init_timer(&qdev->adapter_timer);
|
|
|
|
qdev->adapter_timer.function = ql3xxx_timer;
|
|
|
|
qdev->adapter_timer.expires = jiffies + HZ * 2; /* two second delay */
|
|
|
|
qdev->adapter_timer.data = (unsigned long)qdev;
|
|
|
|
|
|
|
|
if(!cards_found) {
|
|
|
|
printk(KERN_ALERT PFX "%s\n", DRV_STRING);
|
|
|
|
printk(KERN_ALERT PFX "Driver name: %s, Version: %s.\n",
|
|
|
|
DRV_NAME, DRV_VERSION);
|
|
|
|
}
|
|
|
|
ql_display_dev_info(ndev);
|
|
|
|
|
|
|
|
cards_found++;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_out_iounmap:
|
|
|
|
iounmap(qdev->mem_map_registers);
|
|
|
|
err_out_free_ndev:
|
|
|
|
free_netdev(ndev);
|
|
|
|
err_out_free_regions:
|
|
|
|
pci_release_regions(pdev);
|
|
|
|
err_out_disable_pdev:
|
|
|
|
pci_disable_device(pdev);
|
|
|
|
pci_set_drvdata(pdev, NULL);
|
|
|
|
err_out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __devexit ql3xxx_remove(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
struct net_device *ndev = pci_get_drvdata(pdev);
|
|
|
|
struct ql3_adapter *qdev = netdev_priv(ndev);
|
|
|
|
|
|
|
|
unregister_netdev(ndev);
|
|
|
|
qdev = netdev_priv(ndev);
|
|
|
|
|
|
|
|
ql_disable_interrupts(qdev);
|
|
|
|
|
|
|
|
if (qdev->workqueue) {
|
|
|
|
cancel_delayed_work(&qdev->reset_work);
|
|
|
|
cancel_delayed_work(&qdev->tx_timeout_work);
|
|
|
|
destroy_workqueue(qdev->workqueue);
|
|
|
|
qdev->workqueue = NULL;
|
|
|
|
}
|
|
|
|
|
2006-09-25 09:54:46 +08:00
|
|
|
iounmap(qdev->mem_map_registers);
|
2006-07-25 15:40:21 +08:00
|
|
|
pci_release_regions(pdev);
|
|
|
|
pci_set_drvdata(pdev, NULL);
|
|
|
|
free_netdev(ndev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct pci_driver ql3xxx_driver = {
|
|
|
|
|
|
|
|
.name = DRV_NAME,
|
|
|
|
.id_table = ql3xxx_pci_tbl,
|
|
|
|
.probe = ql3xxx_probe,
|
|
|
|
.remove = __devexit_p(ql3xxx_remove),
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init ql3xxx_init_module(void)
|
|
|
|
{
|
|
|
|
return pci_register_driver(&ql3xxx_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit ql3xxx_exit(void)
|
|
|
|
{
|
|
|
|
pci_unregister_driver(&ql3xxx_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(ql3xxx_init_module);
|
|
|
|
module_exit(ql3xxx_exit);
|