ab392d2d6d
The IRQF_SAMPLE_RANDOM flag is marked as deprecated and will be removed. Every input point to the kernel's entropy pool have to better document the type of entropy source it is. drivers/char/random.c now implements a set of interfaces that can be used for devices to collect enviromental noise. IRQF_SAMPLE_RANDOM will be replaced with these add_*_randomness exported functions. Network drivers are not a good source of entropy. They use as a source of entropy essentially a remote host. Which means that the source of entropy can be potentially controlled by an attacker. Also, with heavy workloads the entropy decreases due to less hardware interrupts happening thanks to irq mitigation and NAPI. If a system relies in its network interface as a entropy source it has a false sense of security. Systems that don't have devices whose drivers are good sources of entropy, should either use a hardware random number generator or feed the kernel's entropy pool from userspace using other sources of entropy such as EGD, video_entropyd, timer_entropyd and audio-entropyd. Signed-off-by: Javier Martinez Canillas <martinez.javier@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
3971 lines
102 KiB
C
3971 lines
102 KiB
C
/*
|
|
* QLogic QLA3xxx NIC HBA Driver
|
|
* Copyright (c) 2003-2006 QLogic Corporation
|
|
*
|
|
* See LICENSE.qla3xxx for copyright and licensing details.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#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>
|
|
#include <linux/in.h>
|
|
#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/delay.h>
|
|
#include <linux/mm.h>
|
|
|
|
#include "qla3xxx.h"
|
|
|
|
#define DRV_NAME "qla3xxx"
|
|
#define DRV_STRING "QLogic ISP3XXX Network Driver"
|
|
#define DRV_VERSION "v2.03.00-k5"
|
|
|
|
static const char ql3xxx_driver_name[] = DRV_NAME;
|
|
static const char ql3xxx_driver_version[] = DRV_VERSION;
|
|
|
|
#define TIMED_OUT_MSG \
|
|
"Timed out waiting for management port to get free before issuing command\n"
|
|
|
|
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 DEFINE_PCI_DEVICE_TABLE(ql3xxx_pci_tbl) = {
|
|
{PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, QL3022_DEVICE_ID)},
|
|
{PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, QL3032_DEVICE_ID)},
|
|
/* required last entry */
|
|
{0,}
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pci, ql3xxx_pci_tbl);
|
|
|
|
/*
|
|
* These are the known PHY's which are used
|
|
*/
|
|
enum PHY_DEVICE_TYPE {
|
|
PHY_TYPE_UNKNOWN = 0,
|
|
PHY_VITESSE_VSC8211,
|
|
PHY_AGERE_ET1011C,
|
|
MAX_PHY_DEV_TYPES
|
|
};
|
|
|
|
struct PHY_DEVICE_INFO {
|
|
const enum PHY_DEVICE_TYPE phyDevice;
|
|
const u32 phyIdOUI;
|
|
const u16 phyIdModel;
|
|
const char *name;
|
|
};
|
|
|
|
static const struct PHY_DEVICE_INFO PHY_DEVICES[] = {
|
|
{PHY_TYPE_UNKNOWN, 0x000000, 0x0, "PHY_TYPE_UNKNOWN"},
|
|
{PHY_VITESSE_VSC8211, 0x0003f1, 0xb, "PHY_VITESSE_VSC8211"},
|
|
{PHY_AGERE_ET1011C, 0x00a0bc, 0x1, "PHY_AGERE_ET1011C"},
|
|
};
|
|
|
|
|
|
/*
|
|
* 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 (i < 10) {
|
|
if (i)
|
|
ssleep(1);
|
|
|
|
if (ql_sem_lock(qdev,
|
|
QL_DRVR_SEM_MASK,
|
|
(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index)
|
|
* 2) << 1)) {
|
|
netdev_printk(KERN_DEBUG, qdev->ndev,
|
|
"driver lock acquired\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
netdev_err(qdev->ndev, "Timed out waiting for driver lock...\n");
|
|
return 0;
|
|
}
|
|
|
|
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,
|
|
u32 __iomem *reg, u32 value)
|
|
{
|
|
unsigned long hw_flags;
|
|
|
|
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
|
|
writel(value, reg);
|
|
readl(reg);
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
}
|
|
|
|
static void ql_write_common_reg(struct ql3_adapter *qdev,
|
|
u32 __iomem *reg, u32 value)
|
|
{
|
|
writel(value, reg);
|
|
readl(reg);
|
|
}
|
|
|
|
static void ql_write_nvram_reg(struct ql3_adapter *qdev,
|
|
u32 __iomem *reg, u32 value)
|
|
{
|
|
writel(value, reg);
|
|
readl(reg);
|
|
udelay(1);
|
|
}
|
|
|
|
static void ql_write_page0_reg(struct ql3_adapter *qdev,
|
|
u32 __iomem *reg, u32 value)
|
|
{
|
|
if (qdev->current_page != 0)
|
|
ql_set_register_page(qdev, 0);
|
|
writel(value, reg);
|
|
readl(reg);
|
|
}
|
|
|
|
/*
|
|
* Caller holds hw_lock. Only called during init.
|
|
*/
|
|
static void ql_write_page1_reg(struct ql3_adapter *qdev,
|
|
u32 __iomem *reg, u32 value)
|
|
{
|
|
if (qdev->current_page != 1)
|
|
ql_set_register_page(qdev, 1);
|
|
writel(value, reg);
|
|
readl(reg);
|
|
}
|
|
|
|
/*
|
|
* Caller holds hw_lock. Only called during init.
|
|
*/
|
|
static void ql_write_page2_reg(struct ql3_adapter *qdev,
|
|
u32 __iomem *reg, u32 value)
|
|
{
|
|
if (qdev->current_page != 2)
|
|
ql_set_register_page(qdev, 2);
|
|
writel(value, reg);
|
|
readl(reg);
|
|
}
|
|
|
|
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)
|
|
{
|
|
dma_addr_t map;
|
|
int err;
|
|
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) {
|
|
lrg_buf_cb->skb = netdev_alloc_skb(qdev->ndev,
|
|
qdev->lrg_buffer_len);
|
|
if (unlikely(!lrg_buf_cb->skb)) {
|
|
netdev_err(qdev->ndev, "failed netdev_alloc_skb()\n");
|
|
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);
|
|
err = pci_dma_mapping_error(qdev->pdev, map);
|
|
if (err) {
|
|
netdev_err(qdev->ndev,
|
|
"PCI mapping failed with error: %d\n",
|
|
err);
|
|
dev_kfree_skb(lrg_buf_cb->skb);
|
|
lrg_buf_cb->skb = NULL;
|
|
|
|
qdev->lrg_buf_skb_check++;
|
|
return;
|
|
}
|
|
|
|
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));
|
|
dma_unmap_addr_set(lrg_buf_cb, mapaddr, map);
|
|
dma_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 = qdev->lrg_buf_free_head;
|
|
|
|
if (lrg_buf_cb != NULL) {
|
|
qdev->lrg_buf_free_head = lrg_buf_cb->next;
|
|
if (qdev->lrg_buf_free_head == 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;
|
|
__iomem u32 *spir = &port_regs->CommonRegs.serialPortInterfaceReg;
|
|
|
|
qdev->eeprom_cmd_data = AUBURN_EEPROM_CS_1;
|
|
ql_write_nvram_reg(qdev, spir, ISP_NVRAM_MASK | qdev->eeprom_cmd_data);
|
|
ql_write_nvram_reg(qdev, spir,
|
|
((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;
|
|
__iomem u32 *spir = &port_regs->CommonRegs.serialPortInterfaceReg;
|
|
|
|
/* Clock in a zero, then do the start bit */
|
|
ql_write_nvram_reg(qdev, spir,
|
|
(ISP_NVRAM_MASK | qdev->eeprom_cmd_data |
|
|
AUBURN_EEPROM_DO_1));
|
|
ql_write_nvram_reg(qdev, spir,
|
|
(ISP_NVRAM_MASK | qdev->eeprom_cmd_data |
|
|
AUBURN_EEPROM_DO_1 | AUBURN_EEPROM_CLK_RISE));
|
|
ql_write_nvram_reg(qdev, spir,
|
|
(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, change the DO state to match */
|
|
ql_write_nvram_reg(qdev, spir,
|
|
(ISP_NVRAM_MASK |
|
|
qdev->eeprom_cmd_data | dataBit));
|
|
previousBit = dataBit;
|
|
}
|
|
ql_write_nvram_reg(qdev, spir,
|
|
(ISP_NVRAM_MASK | qdev->eeprom_cmd_data |
|
|
dataBit | AUBURN_EEPROM_CLK_RISE));
|
|
ql_write_nvram_reg(qdev, spir,
|
|
(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
|
|
*/
|
|
ql_write_nvram_reg(qdev, spir,
|
|
(ISP_NVRAM_MASK |
|
|
qdev->eeprom_cmd_data | dataBit));
|
|
previousBit = dataBit;
|
|
}
|
|
ql_write_nvram_reg(qdev, spir,
|
|
(ISP_NVRAM_MASK | qdev->eeprom_cmd_data |
|
|
dataBit | AUBURN_EEPROM_CLK_RISE));
|
|
ql_write_nvram_reg(qdev, spir,
|
|
(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;
|
|
__iomem u32 *spir = &port_regs->CommonRegs.serialPortInterfaceReg;
|
|
|
|
qdev->eeprom_cmd_data = AUBURN_EEPROM_CS_0;
|
|
ql_write_nvram_reg(qdev, spir, 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;
|
|
__iomem u32 *spir = &port_regs->CommonRegs.serialPortInterfaceReg;
|
|
|
|
/* Read the data bits */
|
|
/* The first bit is a dummy. Clock right over it. */
|
|
for (i = 0; i < dataBits; i++) {
|
|
ql_write_nvram_reg(qdev, spir,
|
|
ISP_NVRAM_MASK | qdev->eeprom_cmd_data |
|
|
AUBURN_EEPROM_CLK_RISE);
|
|
ql_write_nvram_reg(qdev, spir,
|
|
ISP_NVRAM_MASK | qdev->eeprom_cmd_data |
|
|
AUBURN_EEPROM_CLK_FALL);
|
|
dataBit = (ql_read_common_reg(qdev, spir) &
|
|
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_set_mac_addr(struct net_device *ndev, u16 *addr)
|
|
{
|
|
__le16 *p = (__le16 *)ndev->dev_addr;
|
|
p[0] = cpu_to_le16(addr[0]);
|
|
p[1] = cpu_to_le16(addr[1]);
|
|
p[2] = cpu_to_le16(addr[2]);
|
|
}
|
|
|
|
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)) {
|
|
pr_err("%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) {
|
|
netdev_err(qdev->ndev, "checksum should be zero, is %x!!\n",
|
|
checksum);
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
return -1;
|
|
}
|
|
|
|
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,
|
|
u16 regAddr, u16 value, u32 phyAddr)
|
|
{
|
|
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)) {
|
|
netif_warn(qdev, link, qdev->ndev, TIMED_OUT_MSG);
|
|
return -1;
|
|
}
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg,
|
|
phyAddr | regAddr);
|
|
|
|
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)) {
|
|
netif_warn(qdev, link, qdev->ndev, TIMED_OUT_MSG);
|
|
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,
|
|
u16 *value, u32 phyAddr)
|
|
{
|
|
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)) {
|
|
netif_warn(qdev, link, qdev->ndev, TIMED_OUT_MSG);
|
|
return -1;
|
|
}
|
|
|
|
ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg,
|
|
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)) {
|
|
netif_warn(qdev, link, qdev->ndev, TIMED_OUT_MSG);
|
|
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)) {
|
|
netif_warn(qdev, link, qdev->ndev, TIMED_OUT_MSG);
|
|
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)) {
|
|
netif_warn(qdev, link, qdev->ndev, TIMED_OUT_MSG);
|
|
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)) {
|
|
netif_warn(qdev, link, qdev->ndev, TIMED_OUT_MSG);
|
|
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)) {
|
|
netif_warn(qdev, link, qdev->ndev, TIMED_OUT_MSG);
|
|
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);
|
|
|
|
}
|
|
|
|
static void ql_petbi_reset_ex(struct ql3_adapter *qdev)
|
|
{
|
|
ql_mii_write_reg_ex(qdev, PETBI_CONTROL_REG, PETBI_CTRL_SOFT_RESET,
|
|
PHYAddr[qdev->mac_index]);
|
|
}
|
|
|
|
static void ql_petbi_start_neg_ex(struct ql3_adapter *qdev)
|
|
{
|
|
u16 reg;
|
|
|
|
/* Enable Auto-negotiation sense */
|
|
ql_mii_read_reg_ex(qdev, PETBI_TBI_CTRL, ®,
|
|
PHYAddr[qdev->mac_index]);
|
|
reg |= PETBI_TBI_AUTO_SENSE;
|
|
ql_mii_write_reg_ex(qdev, PETBI_TBI_CTRL, reg,
|
|
PHYAddr[qdev->mac_index]);
|
|
|
|
ql_mii_write_reg_ex(qdev, PETBI_NEG_ADVER,
|
|
PETBI_NEG_PAUSE | PETBI_NEG_DUPLEX,
|
|
PHYAddr[qdev->mac_index]);
|
|
|
|
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,
|
|
PHYAddr[qdev->mac_index]);
|
|
}
|
|
|
|
static void ql_petbi_init(struct ql3_adapter *qdev)
|
|
{
|
|
ql_petbi_reset(qdev);
|
|
ql_petbi_start_neg(qdev);
|
|
}
|
|
|
|
static void ql_petbi_init_ex(struct ql3_adapter *qdev)
|
|
{
|
|
ql_petbi_reset_ex(qdev);
|
|
ql_petbi_start_neg_ex(qdev);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static void phyAgereSpecificInit(struct ql3_adapter *qdev, u32 miiAddr)
|
|
{
|
|
netdev_info(qdev->ndev, "enabling Agere specific PHY\n");
|
|
/* 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 enum PHY_DEVICE_TYPE getPhyType(struct ql3_adapter *qdev,
|
|
u16 phyIdReg0, u16 phyIdReg1)
|
|
{
|
|
enum PHY_DEVICE_TYPE 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)) {
|
|
netdev_info(qdev->ndev, "Phy: %s\n",
|
|
PHY_DEVICES[i].name);
|
|
result = PHY_DEVICES[i].phyDevice;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int ql_phy_get_speed(struct ql3_adapter *qdev)
|
|
{
|
|
u16 reg;
|
|
|
|
switch (qdev->phyType) {
|
|
case PHY_AGERE_ET1011C: {
|
|
if (ql_mii_read_reg(qdev, 0x1A, ®) < 0)
|
|
return 0;
|
|
|
|
reg = (reg >> 8) & 3;
|
|
break;
|
|
}
|
|
default:
|
|
if (ql_mii_read_reg(qdev, AUX_CONTROL_STATUS, ®) < 0)
|
|
return 0;
|
|
|
|
reg = (((reg & 0x18) >> 3) & 3);
|
|
}
|
|
|
|
switch (reg) {
|
|
case 2:
|
|
return SPEED_1000;
|
|
case 1:
|
|
return SPEED_100;
|
|
case 0:
|
|
return SPEED_10;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static int ql_is_full_dup(struct ql3_adapter *qdev)
|
|
{
|
|
u16 reg;
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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) {
|
|
netdev_err(qdev->ndev, "Could not read from reg PHY_ID_0_REG\n");
|
|
return err;
|
|
}
|
|
|
|
err = ql_mii_read_reg(qdev, PHY_ID_1_REG, ®2);
|
|
if (err != 0) {
|
|
netdev_err(qdev->ndev, "Could not read from reg PHY_ID_1_REG\n");
|
|
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) {
|
|
netdev_err(qdev->ndev,
|
|
"Could not read from reg PHY_ID_0_REG after Agere detected\n");
|
|
return err;
|
|
}
|
|
|
|
err = ql_mii_read_reg_ex(qdev, PHY_ID_1_REG, ®2, miiAddr);
|
|
if (err != 0) {
|
|
netdev_err(qdev->ndev, "Could not read from reg PHY_ID_1_REG after Agere detected\n");
|
|
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) {
|
|
netdev_err(qdev->ndev, "PHY is unknown\n");
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* 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) {
|
|
netif_info(qdev, link, qdev->ndev, "Auto-Negotiate complete\n");
|
|
return 1;
|
|
}
|
|
netif_info(qdev, link, qdev->ndev, "Auto-Negotiate incomplete\n");
|
|
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.
|
|
*/
|
|
static int ql_this_adapter_controls_port(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_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) {
|
|
netif_printk(qdev, link, KERN_DEBUG, qdev->ndev,
|
|
"not link master\n");
|
|
return 0;
|
|
}
|
|
|
|
netif_printk(qdev, link, KERN_DEBUG, qdev->ndev, "link master\n");
|
|
return 1;
|
|
}
|
|
|
|
static void ql_phy_reset_ex(struct ql3_adapter *qdev)
|
|
{
|
|
ql_mii_write_reg_ex(qdev, CONTROL_REG, PHY_CTRL_SOFT_RESET,
|
|
PHYAddr[qdev->mac_index]);
|
|
}
|
|
|
|
static void ql_phy_start_neg_ex(struct ql3_adapter *qdev)
|
|
{
|
|
u16 reg;
|
|
u16 portConfiguration;
|
|
|
|
if (qdev->phyType == PHY_AGERE_ET1011C)
|
|
ql_mii_write_reg(qdev, 0x13, 0x0000);
|
|
/* turn off external loopback */
|
|
|
|
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_1000MB_SPEED) {
|
|
if (portConfiguration & PORT_CONFIG_FULL_DUPLEX_ENABLED)
|
|
reg |= PHY_GIG_ADV_1000F;
|
|
else
|
|
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]);
|
|
|
|
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]);
|
|
}
|
|
|
|
static void ql_phy_init_ex(struct ql3_adapter *qdev)
|
|
{
|
|
ql_phy_reset_ex(qdev);
|
|
PHY_Setup(qdev);
|
|
ql_phy_start_neg_ex(qdev);
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
|
|
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) *
|
|
2) << 7)) {
|
|
netdev_err(qdev->ndev, "Could not get hw lock for GIO\n");
|
|
return -1;
|
|
}
|
|
|
|
if (ql_is_fiber(qdev)) {
|
|
ql_petbi_init(qdev);
|
|
} else {
|
|
/* Copper port */
|
|
ql_phy_init_ex(qdev);
|
|
}
|
|
|
|
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 */
|
|
netif_printk(qdev, link, KERN_DEBUG, qdev->ndev,
|
|
"Configuring link\n");
|
|
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 */
|
|
netif_printk(qdev, link, KERN_DEBUG, qdev->ndev,
|
|
"Enabling mac\n");
|
|
ql_mac_enable(qdev, 1);
|
|
}
|
|
|
|
qdev->port_link_state = LS_UP;
|
|
netif_start_queue(qdev->ndev);
|
|
netif_carrier_on(qdev->ndev);
|
|
netif_info(qdev, link, qdev->ndev,
|
|
"Link is up at %d Mbps, %s duplex\n",
|
|
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)) {
|
|
netif_printk(qdev, link, KERN_DEBUG, qdev->ndev,
|
|
"Remote error detected. Calling ql_port_start()\n");
|
|
/*
|
|
* 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;
|
|
return 0;
|
|
}
|
|
}
|
|
ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK);
|
|
return 0;
|
|
}
|
|
|
|
static void ql_link_state_machine_work(struct work_struct *work)
|
|
{
|
|
struct ql3_adapter *qdev =
|
|
container_of(work, struct ql3_adapter, link_state_work.work);
|
|
|
|
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)) {
|
|
netif_info(qdev, link, qdev->ndev,
|
|
"Reset in progress, skip processing link state\n");
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
|
|
/* Restart timer on 2 second interval. */
|
|
mod_timer(&qdev->adapter_timer, jiffies + HZ * 1);
|
|
|
|
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 (curr_link_state == LS_UP) {
|
|
netif_info(qdev, link, qdev->ndev, "Link is up\n");
|
|
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);
|
|
|
|
qdev->port_link_state = LS_UP;
|
|
}
|
|
break;
|
|
|
|
case LS_UP:
|
|
/*
|
|
* See if the link is currently down or went down and came
|
|
* back up
|
|
*/
|
|
if (curr_link_state == LS_DOWN) {
|
|
netif_info(qdev, link, qdev->ndev, "Link is down\n");
|
|
qdev->port_link_state = LS_DOWN;
|
|
}
|
|
if (ql_link_down_detect(qdev))
|
|
qdev->port_link_state = LS_DOWN;
|
|
break;
|
|
}
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
|
|
/* Restart timer on 2 second interval. */
|
|
mod_timer(&qdev->adapter_timer, jiffies + HZ * 1);
|
|
}
|
|
|
|
/*
|
|
* Caller must take hw_lock and QL_PHY_GIO_SEM.
|
|
*/
|
|
static void ql_get_phy_owner(struct ql3_adapter *qdev)
|
|
{
|
|
if (ql_this_adapter_controls_port(qdev))
|
|
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)) {
|
|
if (ql_this_adapter_controls_port(qdev))
|
|
ql_petbi_init_ex(qdev);
|
|
} else {
|
|
if (ql_this_adapter_controls_port(qdev))
|
|
ql_phy_init_ex(qdev);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
|
|
if (qdev->device_id == QL3032_DEVICE_ID)
|
|
ql_write_page0_reg(qdev,
|
|
&port_regs->macMIIMgmtControlReg, 0x0f00000);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
#define SUPPORTED_OPTICAL_MODES (SUPPORTED_1000baseT_Full | \
|
|
SUPPORTED_FIBRE | \
|
|
SUPPORTED_Autoneg)
|
|
#define SUPPORTED_TP_MODES (SUPPORTED_10baseT_Half | \
|
|
SUPPORTED_10baseT_Full | \
|
|
SUPPORTED_100baseT_Half | \
|
|
SUPPORTED_100baseT_Full | \
|
|
SUPPORTED_1000baseT_Half | \
|
|
SUPPORTED_1000baseT_Full | \
|
|
SUPPORTED_Autoneg | \
|
|
SUPPORTED_TP); \
|
|
|
|
static u32 ql_supported_modes(struct ql3_adapter *qdev)
|
|
{
|
|
if (test_bit(QL_LINK_OPTICAL, &qdev->flags))
|
|
return SUPPORTED_OPTICAL_MODES;
|
|
|
|
return SUPPORTED_TP_MODES;
|
|
}
|
|
|
|
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) * 2) << 7)) {
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
return 0;
|
|
}
|
|
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) * 2) << 7)) {
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
return 0;
|
|
}
|
|
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) * 2) << 7)) {
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
return 0;
|
|
}
|
|
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->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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static const struct ethtool_ops ql3xxx_ethtool_ops = {
|
|
.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,
|
|
.get_pauseparam = ql_get_pauseparam,
|
|
};
|
|
|
|
static int ql_populate_free_queue(struct ql3_adapter *qdev)
|
|
{
|
|
struct ql_rcv_buf_cb *lrg_buf_cb = qdev->lrg_buf_free_head;
|
|
dma_addr_t map;
|
|
int err;
|
|
|
|
while (lrg_buf_cb) {
|
|
if (!lrg_buf_cb->skb) {
|
|
lrg_buf_cb->skb =
|
|
netdev_alloc_skb(qdev->ndev,
|
|
qdev->lrg_buffer_len);
|
|
if (unlikely(!lrg_buf_cb->skb)) {
|
|
netdev_printk(KERN_DEBUG, qdev->ndev,
|
|
"Failed netdev_alloc_skb()\n");
|
|
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);
|
|
|
|
err = pci_dma_mapping_error(qdev->pdev, map);
|
|
if (err) {
|
|
netdev_err(qdev->ndev,
|
|
"PCI mapping failed with error: %d\n",
|
|
err);
|
|
dev_kfree_skb(lrg_buf_cb->skb);
|
|
lrg_buf_cb->skb = NULL;
|
|
break;
|
|
}
|
|
|
|
|
|
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));
|
|
dma_unmap_addr_set(lrg_buf_cb, mapaddr, map);
|
|
dma_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;
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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++;
|
|
|
|
if (qdev->lrg_buf_q_producer_index ==
|
|
qdev->num_lbufq_entries)
|
|
qdev->lrg_buf_q_producer_index = 0;
|
|
|
|
if (qdev->lrg_buf_q_producer_index ==
|
|
(qdev->num_lbufq_entries - 1)) {
|
|
lrg_buf_q_ele = qdev->lrg_buf_q_virt_addr;
|
|
}
|
|
}
|
|
wmb();
|
|
qdev->lrg_buf_next_free = lrg_buf_q_ele;
|
|
writel(qdev->lrg_buf_q_producer_index,
|
|
&port_regs->CommonRegs.rxLargeQProducerIndex);
|
|
}
|
|
}
|
|
|
|
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;
|
|
int i;
|
|
int retval = 0;
|
|
|
|
if (mac_rsp->flags & OB_MAC_IOCB_RSP_S) {
|
|
netdev_warn(qdev->ndev,
|
|
"Frame too short but it was padded and sent\n");
|
|
}
|
|
|
|
tx_cb = &qdev->tx_buf[mac_rsp->transaction_id];
|
|
|
|
/* Check the transmit response flags for any errors */
|
|
if (mac_rsp->flags & OB_MAC_IOCB_RSP_S) {
|
|
netdev_err(qdev->ndev,
|
|
"Frame too short to be legal, frame not sent\n");
|
|
|
|
qdev->ndev->stats.tx_errors++;
|
|
retval = -EIO;
|
|
goto frame_not_sent;
|
|
}
|
|
|
|
if (tx_cb->seg_count == 0) {
|
|
netdev_err(qdev->ndev, "tx_cb->seg_count == 0: %d\n",
|
|
mac_rsp->transaction_id);
|
|
|
|
qdev->ndev->stats.tx_errors++;
|
|
retval = -EIO;
|
|
goto invalid_seg_count;
|
|
}
|
|
|
|
pci_unmap_single(qdev->pdev,
|
|
dma_unmap_addr(&tx_cb->map[0], mapaddr),
|
|
dma_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,
|
|
dma_unmap_addr(&tx_cb->map[i],
|
|
mapaddr),
|
|
dma_unmap_len(&tx_cb->map[i], maplen),
|
|
PCI_DMA_TODEVICE);
|
|
}
|
|
}
|
|
qdev->ndev->stats.tx_packets++;
|
|
qdev->ndev->stats.tx_bytes += tx_cb->skb->len;
|
|
|
|
frame_not_sent:
|
|
dev_kfree_skb_irq(tx_cb->skb);
|
|
tx_cb->skb = NULL;
|
|
|
|
invalid_seg_count:
|
|
atomic_inc(&qdev->tx_count);
|
|
}
|
|
|
|
static void ql_get_sbuf(struct ql3_adapter *qdev)
|
|
{
|
|
if (++qdev->small_buf_index == NUM_SMALL_BUFFERS)
|
|
qdev->small_buf_index = 0;
|
|
qdev->small_buf_release_cnt++;
|
|
}
|
|
|
|
static struct ql_rcv_buf_cb *ql_get_lbuf(struct ql3_adapter *qdev)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* 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().
|
|
*/
|
|
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).
|
|
*/
|
|
ql_get_sbuf(qdev);
|
|
|
|
if (qdev->device_id == QL3022_DEVICE_ID)
|
|
lrg_buf_cb1 = ql_get_lbuf(qdev);
|
|
|
|
/* start of second buffer */
|
|
lrg_buf_cb2 = ql_get_lbuf(qdev);
|
|
skb = lrg_buf_cb2->skb;
|
|
|
|
qdev->ndev->stats.rx_packets++;
|
|
qdev->ndev->stats.rx_bytes += length;
|
|
|
|
skb_put(skb, length);
|
|
pci_unmap_single(qdev->pdev,
|
|
dma_unmap_addr(lrg_buf_cb2, mapaddr),
|
|
dma_unmap_len(lrg_buf_cb2, maplen),
|
|
PCI_DMA_FROMDEVICE);
|
|
prefetch(skb->data);
|
|
skb_checksum_none_assert(skb);
|
|
skb->protocol = eth_type_trans(skb, qdev->ndev);
|
|
|
|
netif_receive_skb(skb);
|
|
lrg_buf_cb2->skb = NULL;
|
|
|
|
if (qdev->device_id == QL3022_DEVICE_ID)
|
|
ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb1);
|
|
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;
|
|
struct sk_buff *skb1 = NULL, *skb2;
|
|
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).
|
|
*/
|
|
|
|
ql_get_sbuf(qdev);
|
|
|
|
if (qdev->device_id == QL3022_DEVICE_ID) {
|
|
/* start of first buffer on 3022 */
|
|
lrg_buf_cb1 = ql_get_lbuf(qdev);
|
|
skb1 = lrg_buf_cb1->skb;
|
|
size = ETH_HLEN;
|
|
if (*((u16 *) skb1->data) != 0xFFFF)
|
|
size += VLAN_ETH_HLEN - ETH_HLEN;
|
|
}
|
|
|
|
/* start of second buffer */
|
|
lrg_buf_cb2 = ql_get_lbuf(qdev);
|
|
skb2 = lrg_buf_cb2->skb;
|
|
|
|
skb_put(skb2, length); /* Just the second buffer length here. */
|
|
pci_unmap_single(qdev->pdev,
|
|
dma_unmap_addr(lrg_buf_cb2, mapaddr),
|
|
dma_unmap_len(lrg_buf_cb2, maplen),
|
|
PCI_DMA_FROMDEVICE);
|
|
prefetch(skb2->data);
|
|
|
|
skb_checksum_none_assert(skb2);
|
|
if (qdev->device_id == QL3022_DEVICE_ID) {
|
|
/*
|
|
* Copy the ethhdr from first buffer to second. This
|
|
* is necessary for 3022 IP completions.
|
|
*/
|
|
skb_copy_from_linear_data_offset(skb1, VLAN_ID_LEN,
|
|
skb_push(skb2, size), size);
|
|
} else {
|
|
u16 checksum = le16_to_cpu(ib_ip_rsp_ptr->checksum);
|
|
if (checksum &
|
|
(IB_IP_IOCB_RSP_3032_ICE |
|
|
IB_IP_IOCB_RSP_3032_CE)) {
|
|
netdev_err(ndev,
|
|
"%s: Bad checksum for this %s packet, checksum = %x\n",
|
|
__func__,
|
|
((checksum & IB_IP_IOCB_RSP_3032_TCP) ?
|
|
"TCP" : "UDP"), checksum);
|
|
} else if ((checksum & IB_IP_IOCB_RSP_3032_TCP) ||
|
|
(checksum & IB_IP_IOCB_RSP_3032_UDP &&
|
|
!(checksum & IB_IP_IOCB_RSP_3032_NUC))) {
|
|
skb2->ip_summed = CHECKSUM_UNNECESSARY;
|
|
}
|
|
}
|
|
skb2->protocol = eth_type_trans(skb2, qdev->ndev);
|
|
|
|
netif_receive_skb(skb2);
|
|
ndev->stats.rx_packets++;
|
|
ndev->stats.rx_bytes += length;
|
|
lrg_buf_cb2->skb = NULL;
|
|
|
|
if (qdev->device_id == QL3022_DEVICE_ID)
|
|
ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb1);
|
|
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;
|
|
int work_done = 0;
|
|
|
|
/* While there are entries in the completion queue. */
|
|
while ((le32_to_cpu(*(qdev->prsp_producer_index)) !=
|
|
qdev->rsp_consumer_index) && (work_done < work_to_do)) {
|
|
|
|
net_rsp = qdev->rsp_current;
|
|
rmb();
|
|
/*
|
|
* Fix 4032 chip's 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;
|
|
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:
|
|
case OPCODE_IB_3032_MAC_IOCB:
|
|
ql_process_mac_rx_intr(qdev, (struct ib_mac_iocb_rsp *)
|
|
net_rsp);
|
|
(*rx_cleaned)++;
|
|
break;
|
|
|
|
case OPCODE_IB_IP_IOCB:
|
|
case OPCODE_IB_3032_IP_IOCB:
|
|
ql_process_macip_rx_intr(qdev, (struct ib_ip_iocb_rsp *)
|
|
net_rsp);
|
|
(*rx_cleaned)++;
|
|
break;
|
|
default: {
|
|
u32 *tmp = (u32 *)net_rsp;
|
|
netdev_err(ndev,
|
|
"Hit default case, not handled!\n"
|
|
" dropping the packet, opcode = %x\n"
|
|
"0x%08lx 0x%08lx 0x%08lx 0x%08lx\n",
|
|
net_rsp->opcode,
|
|
(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++;
|
|
}
|
|
|
|
work_done = *tx_cleaned + *rx_cleaned;
|
|
}
|
|
|
|
return work_done;
|
|
}
|
|
|
|
static int ql_poll(struct napi_struct *napi, int budget)
|
|
{
|
|
struct ql3_adapter *qdev = container_of(napi, struct ql3_adapter, napi);
|
|
int rx_cleaned = 0, tx_cleaned = 0;
|
|
unsigned long hw_flags;
|
|
struct ql3xxx_port_registers __iomem *port_regs =
|
|
qdev->mem_map_registers;
|
|
|
|
ql_tx_rx_clean(qdev, &tx_cleaned, &rx_cleaned, budget);
|
|
|
|
if (tx_cleaned + rx_cleaned != budget) {
|
|
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
|
|
__napi_complete(napi);
|
|
ql_update_small_bufq_prod_index(qdev);
|
|
ql_update_lrg_bufq_prod_index(qdev);
|
|
writel(qdev->rsp_consumer_index,
|
|
&port_regs->CommonRegs.rspQConsumerIndex);
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
|
|
ql_enable_interrupts(qdev);
|
|
}
|
|
return tx_cleaned + rx_cleaned;
|
|
}
|
|
|
|
static irqreturn_t ql3xxx_isr(int irq, void *dev_id)
|
|
{
|
|
|
|
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;
|
|
|
|
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);
|
|
netdev_warn(ndev,
|
|
"Resetting chip. PortFatalErrStatus register = 0x%x\n",
|
|
var);
|
|
set_bit(QL_RESET_START, &qdev->flags) ;
|
|
} else {
|
|
/*
|
|
* Soft Reset Requested.
|
|
*/
|
|
set_bit(QL_RESET_PER_SCSI, &qdev->flags) ;
|
|
netdev_err(ndev,
|
|
"Another function issued a reset to the chip. ISR value = %x\n",
|
|
value);
|
|
}
|
|
queue_delayed_work(qdev->workqueue, &qdev->reset_work, 0);
|
|
spin_unlock(&qdev->adapter_lock);
|
|
} else if (value & ISP_IMR_DISABLE_CMPL_INT) {
|
|
ql_disable_interrupts(qdev);
|
|
if (likely(napi_schedule_prep(&qdev->napi)))
|
|
__napi_schedule(&qdev->napi);
|
|
} else
|
|
return IRQ_NONE;
|
|
|
|
return IRQ_RETVAL(handled);
|
|
}
|
|
|
|
/*
|
|
* 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 OAL is used to point to the next OAL if more frags
|
|
* are coming. That is why the frags:segment count ratio is not linear.
|
|
*/
|
|
static int ql_get_seg_count(struct ql3_adapter *qdev, unsigned short frags)
|
|
{
|
|
if (qdev->device_id == QL3022_DEVICE_ID)
|
|
return 1;
|
|
|
|
if (frags <= 2)
|
|
return frags + 1;
|
|
else if (frags <= 6)
|
|
return frags + 2;
|
|
else if (frags <= 10)
|
|
return frags + 3;
|
|
else if (frags <= 14)
|
|
return frags + 4;
|
|
else if (frags <= 18)
|
|
return frags + 5;
|
|
return -1;
|
|
}
|
|
|
|
static void ql_hw_csum_setup(const struct sk_buff *skb,
|
|
struct ob_mac_iocb_req *mac_iocb_ptr)
|
|
{
|
|
const struct iphdr *ip = ip_hdr(skb);
|
|
|
|
mac_iocb_ptr->ip_hdr_off = skb_network_offset(skb);
|
|
mac_iocb_ptr->ip_hdr_len = ip->ihl;
|
|
|
|
if (ip->protocol == IPPROTO_TCP) {
|
|
mac_iocb_ptr->flags1 |= OB_3032MAC_IOCB_REQ_TC |
|
|
OB_3032MAC_IOCB_REQ_IC;
|
|
} else {
|
|
mac_iocb_ptr->flags1 |= OB_3032MAC_IOCB_REQ_UC |
|
|
OB_3032MAC_IOCB_REQ_IC;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Map the buffers for this transmit.
|
|
* This will return NETDEV_TX_BUSY or NETDEV_TX_OK based on success.
|
|
*/
|
|
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)
|
|
{
|
|
struct oal *oal;
|
|
struct oal_entry *oal_entry;
|
|
int len = skb_headlen(skb);
|
|
dma_addr_t map;
|
|
int err;
|
|
int completed_segs, i;
|
|
int seg_cnt, seg = 0;
|
|
int frag_cnt = (int)skb_shinfo(skb)->nr_frags;
|
|
|
|
seg_cnt = tx_cb->seg_count;
|
|
/*
|
|
* Map the skb buffer first.
|
|
*/
|
|
map = pci_map_single(qdev->pdev, skb->data, len, PCI_DMA_TODEVICE);
|
|
|
|
err = pci_dma_mapping_error(qdev->pdev, map);
|
|
if (err) {
|
|
netdev_err(qdev->ndev, "PCI mapping failed with error: %d\n",
|
|
err);
|
|
|
|
return NETDEV_TX_BUSY;
|
|
}
|
|
|
|
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);
|
|
dma_unmap_addr_set(&tx_cb->map[seg], mapaddr, map);
|
|
dma_unmap_len_set(&tx_cb->map[seg], maplen, len);
|
|
seg++;
|
|
|
|
if (seg_cnt == 1) {
|
|
/* Terminate the last segment. */
|
|
oal_entry->len |= cpu_to_le32(OAL_LAST_ENTRY);
|
|
return NETDEV_TX_OK;
|
|
}
|
|
oal = tx_cb->oal;
|
|
for (completed_segs = 0;
|
|
completed_segs < frag_cnt;
|
|
completed_segs++, seg++) {
|
|
skb_frag_t *frag = &skb_shinfo(skb)->frags[completed_segs];
|
|
oal_entry++;
|
|
/*
|
|
* Check for continuation requirements.
|
|
* It's strange but necessary.
|
|
* Continuation entry points to outbound address list.
|
|
*/
|
|
if ((seg == 2 && seg_cnt > 3) ||
|
|
(seg == 7 && seg_cnt > 8) ||
|
|
(seg == 12 && seg_cnt > 13) ||
|
|
(seg == 17 && seg_cnt > 18)) {
|
|
map = pci_map_single(qdev->pdev, oal,
|
|
sizeof(struct oal),
|
|
PCI_DMA_TODEVICE);
|
|
|
|
err = pci_dma_mapping_error(qdev->pdev, map);
|
|
if (err) {
|
|
netdev_err(qdev->ndev,
|
|
"PCI mapping outbound address list with error: %d\n",
|
|
err);
|
|
goto map_error;
|
|
}
|
|
|
|
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);
|
|
dma_unmap_addr_set(&tx_cb->map[seg], mapaddr, map);
|
|
dma_unmap_len_set(&tx_cb->map[seg], maplen,
|
|
sizeof(struct oal));
|
|
oal_entry = (struct oal_entry *)oal;
|
|
oal++;
|
|
seg++;
|
|
}
|
|
|
|
map = pci_map_page(qdev->pdev, frag->page,
|
|
frag->page_offset, frag->size,
|
|
PCI_DMA_TODEVICE);
|
|
|
|
err = pci_dma_mapping_error(qdev->pdev, map);
|
|
if (err) {
|
|
netdev_err(qdev->ndev,
|
|
"PCI mapping frags failed with error: %d\n",
|
|
err);
|
|
goto map_error;
|
|
}
|
|
|
|
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);
|
|
dma_unmap_addr_set(&tx_cb->map[seg], mapaddr, map);
|
|
dma_unmap_len_set(&tx_cb->map[seg], maplen, frag->size);
|
|
}
|
|
/* Terminate the last segment. */
|
|
oal_entry->len |= cpu_to_le32(OAL_LAST_ENTRY);
|
|
return NETDEV_TX_OK;
|
|
|
|
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++;
|
|
|
|
/*
|
|
* Check for continuation requirements.
|
|
* It's strange but necessary.
|
|
*/
|
|
|
|
if ((seg == 2 && seg_cnt > 3) ||
|
|
(seg == 7 && seg_cnt > 8) ||
|
|
(seg == 12 && seg_cnt > 13) ||
|
|
(seg == 17 && seg_cnt > 18)) {
|
|
pci_unmap_single(qdev->pdev,
|
|
dma_unmap_addr(&tx_cb->map[seg], mapaddr),
|
|
dma_unmap_len(&tx_cb->map[seg], maplen),
|
|
PCI_DMA_TODEVICE);
|
|
oal++;
|
|
seg++;
|
|
}
|
|
|
|
pci_unmap_page(qdev->pdev,
|
|
dma_unmap_addr(&tx_cb->map[seg], mapaddr),
|
|
dma_unmap_len(&tx_cb->map[seg], maplen),
|
|
PCI_DMA_TODEVICE);
|
|
}
|
|
|
|
pci_unmap_single(qdev->pdev,
|
|
dma_unmap_addr(&tx_cb->map[0], mapaddr),
|
|
dma_unmap_addr(&tx_cb->map[0], maplen),
|
|
PCI_DMA_TODEVICE);
|
|
|
|
return NETDEV_TX_BUSY;
|
|
|
|
}
|
|
|
|
/*
|
|
* 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 be 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 netdev_tx_t ql3xxx_send(struct sk_buff *skb,
|
|
struct net_device *ndev)
|
|
{
|
|
struct ql3_adapter *qdev = 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];
|
|
tx_cb->seg_count = ql_get_seg_count(qdev,
|
|
skb_shinfo(skb)->nr_frags);
|
|
if (tx_cb->seg_count == -1) {
|
|
netdev_err(ndev, "%s: invalid segment count!\n", __func__);
|
|
return NETDEV_TX_OK;
|
|
}
|
|
|
|
mac_iocb_ptr = tx_cb->queue_entry;
|
|
memset((void *)mac_iocb_ptr, 0, sizeof(struct ob_mac_iocb_req));
|
|
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;
|
|
if (qdev->device_id == QL3032_DEVICE_ID &&
|
|
skb->ip_summed == CHECKSUM_PARTIAL)
|
|
ql_hw_csum_setup(skb, mac_iocb_ptr);
|
|
|
|
if (ql_send_map(qdev, mac_iocb_ptr, tx_cb, skb) != NETDEV_TX_OK) {
|
|
netdev_err(ndev, "%s: Could not map the segments!\n", __func__);
|
|
return NETDEV_TX_BUSY;
|
|
}
|
|
|
|
wmb();
|
|
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,
|
|
&port_regs->CommonRegs.reqQProducerIndex,
|
|
qdev->req_producer_index);
|
|
|
|
netif_printk(qdev, tx_queued, KERN_DEBUG, ndev,
|
|
"tx queued, slot %d, len %d\n",
|
|
qdev->req_producer_index, skb->len);
|
|
|
|
atomic_dec(&qdev->tx_count);
|
|
return NETDEV_TX_OK;
|
|
}
|
|
|
|
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)) {
|
|
netdev_err(qdev->ndev, "reqQ failed\n");
|
|
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)) {
|
|
netdev_err(qdev->ndev, "rspQ allocation failed\n");
|
|
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)) {
|
|
netdev_info(qdev->ndev, "Already done\n");
|
|
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 =
|
|
qdev->num_lbufq_entries * sizeof(struct lrg_buf_q_entry);
|
|
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;
|
|
|
|
qdev->lrg_buf =
|
|
kmalloc(qdev->num_large_buffers * sizeof(struct ql_rcv_buf_cb),
|
|
GFP_KERNEL);
|
|
if (qdev->lrg_buf == NULL) {
|
|
netdev_err(qdev->ndev, "qdev->lrg_buf alloc failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
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) {
|
|
netdev_err(qdev->ndev, "lBufQ failed\n");
|
|
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) {
|
|
netdev_err(qdev->ndev, "Small Buffer Queue allocation failed\n");
|
|
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)) {
|
|
netdev_info(qdev->ndev, "Already done\n");
|
|
return;
|
|
}
|
|
kfree(qdev->lrg_buf);
|
|
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) {
|
|
netdev_err(qdev->ndev, "Failed to get small buffer memory\n");
|
|
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)) {
|
|
netdev_info(qdev->ndev, "Already done\n");
|
|
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;
|
|
|
|
for (i = 0; i < qdev->num_large_buffers; i++) {
|
|
lrg_buf_cb = &qdev->lrg_buf[i];
|
|
if (lrg_buf_cb->skb) {
|
|
dev_kfree_skb(lrg_buf_cb->skb);
|
|
pci_unmap_single(qdev->pdev,
|
|
dma_unmap_addr(lrg_buf_cb, mapaddr),
|
|
dma_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;
|
|
|
|
for (i = 0; i < qdev->num_large_buffers; i++) {
|
|
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;
|
|
dma_addr_t map;
|
|
int err;
|
|
|
|
for (i = 0; i < qdev->num_large_buffers; i++) {
|
|
skb = netdev_alloc_skb(qdev->ndev,
|
|
qdev->lrg_buffer_len);
|
|
if (unlikely(!skb)) {
|
|
/* Better luck next round */
|
|
netdev_err(qdev->ndev,
|
|
"large buff alloc failed for %d bytes at index %d\n",
|
|
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);
|
|
|
|
err = pci_dma_mapping_error(qdev->pdev, map);
|
|
if (err) {
|
|
netdev_err(qdev->ndev,
|
|
"PCI mapping failed with error: %d\n",
|
|
err);
|
|
ql_free_large_buffers(qdev);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
dma_unmap_addr_set(lrg_buf_cb, mapaddr, map);
|
|
dma_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;
|
|
}
|
|
|
|
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++) {
|
|
kfree(tx_cb->oal);
|
|
tx_cb->oal = NULL;
|
|
tx_cb++;
|
|
}
|
|
}
|
|
|
|
static int ql_create_send_free_list(struct ql3_adapter *qdev)
|
|
{
|
|
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++) {
|
|
|
|
tx_cb = &qdev->tx_buf[i];
|
|
tx_cb->skb = NULL;
|
|
tx_cb->queue_entry = req_q_curr;
|
|
req_q_curr++;
|
|
tx_cb->oal = kmalloc(512, GFP_KERNEL);
|
|
if (tx_cb->oal == NULL)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int ql_alloc_mem_resources(struct ql3_adapter *qdev)
|
|
{
|
|
if (qdev->ndev->mtu == NORMAL_MTU_SIZE) {
|
|
qdev->num_lbufq_entries = NUM_LBUFQ_ENTRIES;
|
|
qdev->lrg_buffer_len = NORMAL_MTU_SIZE;
|
|
} else if (qdev->ndev->mtu == JUMBO_MTU_SIZE) {
|
|
/*
|
|
* Bigger buffers, so less of them.
|
|
*/
|
|
qdev->num_lbufq_entries = JUMBO_NUM_LBUFQ_ENTRIES;
|
|
qdev->lrg_buffer_len = JUMBO_MTU_SIZE;
|
|
} else {
|
|
netdev_err(qdev->ndev, "Invalid mtu size: %d. Only %d and %d are accepted.\n",
|
|
qdev->ndev->mtu, NORMAL_MTU_SIZE, JUMBO_MTU_SIZE);
|
|
return -ENOMEM;
|
|
}
|
|
qdev->num_large_buffers =
|
|
qdev->num_lbufq_entries * QL_ADDR_ELE_PER_BUFQ_ENTRY;
|
|
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 =
|
|
(__le32 *) (((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 {
|
|
netdev_err(qdev->ndev, "shadowReg Alloc failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (ql_alloc_net_req_rsp_queues(qdev) != 0) {
|
|
netdev_err(qdev->ndev, "ql_alloc_net_req_rsp_queues failed\n");
|
|
goto err_req_rsp;
|
|
}
|
|
|
|
if (ql_alloc_buffer_queues(qdev) != 0) {
|
|
netdev_err(qdev->ndev, "ql_alloc_buffer_queues failed\n");
|
|
goto err_buffer_queues;
|
|
}
|
|
|
|
if (ql_alloc_small_buffers(qdev) != 0) {
|
|
netdev_err(qdev->ndev, "ql_alloc_small_buffers failed\n");
|
|
goto err_small_buffers;
|
|
}
|
|
|
|
if (ql_alloc_large_buffers(qdev) != 0) {
|
|
netdev_err(qdev->ndev, "ql_alloc_large_buffers failed\n");
|
|
goto err_small_buffers;
|
|
}
|
|
|
|
/* Initialize the large buffer queue. */
|
|
ql_init_large_buffers(qdev);
|
|
if (ql_create_send_free_list(qdev))
|
|
goto err_free_list;
|
|
|
|
qdev->rsp_current = qdev->rsp_q_virt_addr;
|
|
|
|
return 0;
|
|
err_free_list:
|
|
ql_free_send_free_list(qdev);
|
|
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)
|
|
{
|
|
ql_free_send_free_list(qdev);
|
|
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)
|
|
{
|
|
struct ql3xxx_local_ram_registers __iomem *local_ram =
|
|
(void __iomem *)qdev->mem_map_registers;
|
|
|
|
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;
|
|
__iomem u32 *spir = &port_regs->CommonRegs.serialPortInterfaceReg;
|
|
struct ql3xxx_host_memory_registers __iomem *hmem_regs =
|
|
(void __iomem *)port_regs;
|
|
u32 delay = 10;
|
|
int status = 0;
|
|
unsigned long hw_flags = 0;
|
|
|
|
if (ql_mii_setup(qdev))
|
|
return -1;
|
|
|
|
/* Bring out PHY out of reset */
|
|
ql_write_common_reg(qdev, spir,
|
|
(ISP_SERIAL_PORT_IF_WE |
|
|
(ISP_SERIAL_PORT_IF_WE << 16)));
|
|
/* Give the PHY time to come out of reset. */
|
|
mdelay(100);
|
|
qdev->port_link_state = LS_DOWN;
|
|
netif_carrier_off(qdev->ndev);
|
|
|
|
/* V2 chip fix for ARS-39168. */
|
|
ql_write_common_reg(qdev, spir,
|
|
(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 */
|
|
*((__le16 *) (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));
|
|
|
|
ql_write_page1_reg(qdev,
|
|
&hmem_regs->rxLargeQLength,
|
|
qdev->num_lbufq_entries);
|
|
|
|
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;
|
|
qdev->lrg_buf_q_producer_index = qdev->num_lbufq_entries - 1;
|
|
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,
|
|
&port_regs->CommonRegs.
|
|
rxSmallQProducerIndex,
|
|
qdev->small_buf_q_producer_index);
|
|
ql_write_common_reg(qdev,
|
|
&port_regs->CommonRegs.
|
|
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);
|
|
}
|
|
|
|
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);
|
|
|
|
if (ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK,
|
|
(QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) *
|
|
2) << 7)) {
|
|
status = -1;
|
|
goto out;
|
|
}
|
|
|
|
PHY_Setup(qdev);
|
|
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;
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
msleep(500);
|
|
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
|
|
} while (--delay);
|
|
|
|
if (delay == 0) {
|
|
netdev_err(qdev->ndev, "Hw Initialization timeout\n");
|
|
status = -1;
|
|
goto out;
|
|
}
|
|
|
|
/* Enable Ethernet Function */
|
|
if (qdev->device_id == QL3032_DEVICE_ID) {
|
|
value =
|
|
(QL3032_PORT_CONTROL_EF | QL3032_PORT_CONTROL_KIE |
|
|
QL3032_PORT_CONTROL_EIv6 | QL3032_PORT_CONTROL_EIv4 |
|
|
QL3032_PORT_CONTROL_ET);
|
|
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));
|
|
}
|
|
|
|
|
|
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.
|
|
*/
|
|
netdev_printk(KERN_DEBUG, qdev->ndev, "Issue soft reset to chip\n");
|
|
ql_write_common_reg(qdev,
|
|
&port_regs->CommonRegs.ispControlStatus,
|
|
((ISP_CONTROL_SR << 16) | ISP_CONTROL_SR));
|
|
|
|
/* Wait 3 seconds for reset to complete. */
|
|
netdev_printk(KERN_DEBUG, qdev->ndev,
|
|
"Wait 10 milliseconds for reset to complete\n");
|
|
|
|
/* 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) {
|
|
netdev_printk(KERN_DEBUG, qdev->ndev,
|
|
"clearing RI after reset\n");
|
|
ql_write_common_reg(qdev,
|
|
&port_regs->CommonRegs.
|
|
ispControlStatus,
|
|
((ISP_CONTROL_RI << 16) | ISP_CONTROL_RI));
|
|
}
|
|
|
|
if (max_wait_time == 0) {
|
|
/* Issue Force Soft Reset */
|
|
ql_write_common_reg(qdev,
|
|
&port_regs->CommonRegs.
|
|
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->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->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:
|
|
netdev_printk(KERN_DEBUG, qdev->ndev,
|
|
"Invalid function number, ispControlStatus = 0x%x\n",
|
|
value);
|
|
break;
|
|
}
|
|
qdev->numPorts = qdev->nvram_data.version_and_numPorts >> 8;
|
|
}
|
|
|
|
static void ql_display_dev_info(struct net_device *ndev)
|
|
{
|
|
struct ql3_adapter *qdev = netdev_priv(ndev);
|
|
struct pci_dev *pdev = qdev->pdev;
|
|
|
|
netdev_info(ndev,
|
|
"%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);
|
|
netdev_info(ndev, "%s Interface\n",
|
|
test_bit(QL_LINK_OPTICAL, &qdev->flags) ? "OPTICAL" : "COPPER");
|
|
|
|
/*
|
|
* Print PCI bus width/type.
|
|
*/
|
|
netdev_info(ndev, "Bus interface is %s %s\n",
|
|
((qdev->pci_width == 64) ? "64-bit" : "32-bit"),
|
|
((qdev->pci_x) ? "PCI-X" : "PCI"));
|
|
|
|
netdev_info(ndev, "mem IO base address adjusted = 0x%p\n",
|
|
qdev->mem_map_registers);
|
|
netdev_info(ndev, "Interrupt number = %d\n", pdev->irq);
|
|
|
|
netif_info(qdev, probe, ndev, "MAC address %pM\n", ndev->dev_addr);
|
|
}
|
|
|
|
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)) {
|
|
netdev_info(qdev->ndev, "calling pci_disable_msi()\n");
|
|
clear_bit(QL_MSI_ENABLED, &qdev->flags);
|
|
pci_disable_msi(qdev->pdev);
|
|
}
|
|
|
|
del_timer_sync(&qdev->adapter_timer);
|
|
|
|
napi_disable(&qdev->napi);
|
|
|
|
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)) {
|
|
soft_reset = ql_adapter_reset(qdev);
|
|
if (soft_reset) {
|
|
netdev_err(ndev, "ql_adapter_reset(%d) FAILED!\n",
|
|
qdev->index);
|
|
}
|
|
netdev_err(ndev,
|
|
"Releasing driver lock via chip reset\n");
|
|
} else {
|
|
netdev_err(ndev,
|
|
"Could not acquire driver lock to do reset!\n");
|
|
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;
|
|
unsigned long irq_flags = IRQF_SHARED;
|
|
unsigned long hw_flags;
|
|
|
|
if (ql_alloc_mem_resources(qdev)) {
|
|
netdev_err(ndev, "Unable to allocate buffers\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (qdev->msi) {
|
|
if (pci_enable_msi(qdev->pdev)) {
|
|
netdev_err(ndev,
|
|
"User requested MSI, but MSI failed to initialize. Continuing without MSI.\n");
|
|
qdev->msi = 0;
|
|
} else {
|
|
netdev_info(ndev, "MSI Enabled...\n");
|
|
set_bit(QL_MSI_ENABLED, &qdev->flags);
|
|
irq_flags &= ~IRQF_SHARED;
|
|
}
|
|
}
|
|
|
|
err = request_irq(qdev->pdev->irq, ql3xxx_isr,
|
|
irq_flags, ndev->name, ndev);
|
|
if (err) {
|
|
netdev_err(ndev,
|
|
"Failed to reserve interrupt %d - already in use\n",
|
|
qdev->pdev->irq);
|
|
goto err_irq;
|
|
}
|
|
|
|
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
|
|
|
|
err = ql_wait_for_drvr_lock(qdev);
|
|
if (err) {
|
|
err = ql_adapter_initialize(qdev);
|
|
if (err) {
|
|
netdev_err(ndev, "Unable to initialize adapter\n");
|
|
goto err_init;
|
|
}
|
|
netdev_err(ndev, "Releasing driver lock\n");
|
|
ql_sem_unlock(qdev, QL_DRVR_SEM_MASK);
|
|
} else {
|
|
netdev_err(ndev, "Could not acquire driver lock\n");
|
|
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);
|
|
|
|
napi_enable(&qdev->napi);
|
|
ql_enable_interrupts(qdev);
|
|
return 0;
|
|
|
|
err_init:
|
|
ql_sem_unlock(qdev, QL_DRVR_SEM_MASK);
|
|
err_lock:
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
free_irq(qdev->pdev->irq, ndev);
|
|
err_irq:
|
|
if (qdev->msi && test_bit(QL_MSI_ENABLED, &qdev->flags)) {
|
|
netdev_info(ndev, "calling pci_disable_msi()\n");
|
|
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)) {
|
|
netdev_err(qdev->ndev,
|
|
"Driver up/down cycle failed, closing device\n");
|
|
rtnl_lock();
|
|
dev_close(qdev->ndev);
|
|
rtnl_unlock();
|
|
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 int ql3xxx_set_mac_address(struct net_device *ndev, void *p)
|
|
{
|
|
struct ql3_adapter *qdev = 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 = netdev_priv(ndev);
|
|
|
|
netdev_err(ndev, "Resetting...\n");
|
|
/*
|
|
* Stop the queues, we've got a problem.
|
|
*/
|
|
netif_stop_queue(ndev);
|
|
|
|
/*
|
|
* Wake up the worker to process this event.
|
|
*/
|
|
queue_delayed_work(qdev->workqueue, &qdev->tx_timeout_work, 0);
|
|
}
|
|
|
|
static void ql_reset_work(struct work_struct *work)
|
|
{
|
|
struct ql3_adapter *qdev =
|
|
container_of(work, struct ql3_adapter, reset_work.work);
|
|
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++) {
|
|
int j;
|
|
tx_cb = &qdev->tx_buf[i];
|
|
if (tx_cb->skb) {
|
|
netdev_printk(KERN_DEBUG, ndev,
|
|
"Freeing lost SKB\n");
|
|
pci_unmap_single(qdev->pdev,
|
|
dma_unmap_addr(&tx_cb->map[0],
|
|
mapaddr),
|
|
dma_unmap_len(&tx_cb->map[0], maplen),
|
|
PCI_DMA_TODEVICE);
|
|
for (j = 1; j < tx_cb->seg_count; j++) {
|
|
pci_unmap_page(qdev->pdev,
|
|
dma_unmap_addr(&tx_cb->map[j],
|
|
mapaddr),
|
|
dma_unmap_len(&tx_cb->map[j],
|
|
maplen),
|
|
PCI_DMA_TODEVICE);
|
|
}
|
|
dev_kfree_skb(tx_cb->skb);
|
|
tx_cb->skb = NULL;
|
|
}
|
|
}
|
|
|
|
netdev_err(ndev, "Clearing NRI after reset\n");
|
|
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) {
|
|
netdev_printk(KERN_DEBUG, ndev,
|
|
"reset completed\n");
|
|
break;
|
|
}
|
|
|
|
if (value & ISP_CONTROL_RI) {
|
|
netdev_printk(KERN_DEBUG, ndev,
|
|
"clearing NRI after reset\n");
|
|
ql_write_common_reg(qdev,
|
|
&port_regs->
|
|
CommonRegs.
|
|
ispControlStatus,
|
|
((ISP_CONTROL_RI <<
|
|
16) | ISP_CONTROL_RI));
|
|
}
|
|
|
|
spin_unlock_irqrestore(&qdev->hw_lock, hw_flags);
|
|
ssleep(1);
|
|
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
|
|
} 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...
|
|
*/
|
|
netdev_err(ndev,
|
|
"Timed out waiting for reset to complete\n");
|
|
netdev_err(ndev, "Do a reset\n");
|
|
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);
|
|
}
|
|
}
|
|
|
|
static void ql_tx_timeout_work(struct work_struct *work)
|
|
{
|
|
struct ql3_adapter *qdev =
|
|
container_of(work, struct ql3_adapter, tx_timeout_work.work);
|
|
|
|
ql_cycle_adapter(qdev, QL_DO_RESET);
|
|
}
|
|
|
|
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;
|
|
queue_delayed_work(qdev->workqueue, &qdev->link_state_work, 0);
|
|
}
|
|
|
|
static const struct net_device_ops ql3xxx_netdev_ops = {
|
|
.ndo_open = ql3xxx_open,
|
|
.ndo_start_xmit = ql3xxx_send,
|
|
.ndo_stop = ql3xxx_close,
|
|
.ndo_set_multicast_list = NULL, /* not allowed on NIC side */
|
|
.ndo_change_mtu = eth_change_mtu,
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
.ndo_set_mac_address = ql3xxx_set_mac_address,
|
|
.ndo_tx_timeout = ql3xxx_tx_timeout,
|
|
};
|
|
|
|
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;
|
|
int uninitialized_var(pci_using_dac), err;
|
|
|
|
err = pci_enable_device(pdev);
|
|
if (err) {
|
|
pr_err("%s cannot enable PCI device\n", pci_name(pdev));
|
|
goto err_out;
|
|
}
|
|
|
|
err = pci_request_regions(pdev, DRV_NAME);
|
|
if (err) {
|
|
pr_err("%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_BIT_MASK(64))) {
|
|
pci_using_dac = 1;
|
|
err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
|
|
} else if (!(err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)))) {
|
|
pci_using_dac = 0;
|
|
err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
|
|
}
|
|
|
|
if (err) {
|
|
pr_err("%s no usable DMA configuration\n", pci_name(pdev));
|
|
goto err_out_free_regions;
|
|
}
|
|
|
|
ndev = alloc_etherdev(sizeof(struct ql3_adapter));
|
|
if (!ndev) {
|
|
pr_err("%s could not alloc etherdev\n", pci_name(pdev));
|
|
err = -ENOMEM;
|
|
goto err_out_free_regions;
|
|
}
|
|
|
|
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;
|
|
qdev->device_id = pci_entry->device;
|
|
qdev->port_link_state = LS_DOWN;
|
|
if (msi)
|
|
qdev->msi = 1;
|
|
|
|
qdev->msg_enable = netif_msg_init(debug, default_msg);
|
|
|
|
if (pci_using_dac)
|
|
ndev->features |= NETIF_F_HIGHDMA;
|
|
if (qdev->device_id == QL3032_DEVICE_ID)
|
|
ndev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
|
|
|
|
qdev->mem_map_registers = pci_ioremap_bar(pdev, 1);
|
|
if (!qdev->mem_map_registers) {
|
|
pr_err("%s: cannot map device registers\n", pci_name(pdev));
|
|
err = -EIO;
|
|
goto err_out_free_ndev;
|
|
}
|
|
|
|
spin_lock_init(&qdev->adapter_lock);
|
|
spin_lock_init(&qdev->hw_lock);
|
|
|
|
/* Set driver entry points */
|
|
ndev->netdev_ops = &ql3xxx_netdev_ops;
|
|
SET_ETHTOOL_OPS(ndev, &ql3xxx_ethtool_ops);
|
|
ndev->watchdog_timeo = 5 * HZ;
|
|
|
|
netif_napi_add(ndev, &qdev->napi, ql_poll, 64);
|
|
|
|
ndev->irq = pdev->irq;
|
|
|
|
/* make sure the EEPROM is good */
|
|
if (ql_get_nvram_params(qdev)) {
|
|
pr_alert("%s: Adapter #%d, Invalid NVRAM parameters\n",
|
|
__func__, qdev->index);
|
|
err = -EIO;
|
|
goto err_out_iounmap;
|
|
}
|
|
|
|
ql_set_mac_info(qdev);
|
|
|
|
/* Validate and set parameters */
|
|
if (qdev->mac_index) {
|
|
ndev->mtu = qdev->nvram_data.macCfg_port1.etherMtu_mac ;
|
|
ql_set_mac_addr(ndev, qdev->nvram_data.funcCfg_fn2.macAddress);
|
|
} else {
|
|
ndev->mtu = qdev->nvram_data.macCfg_port0.etherMtu_mac ;
|
|
ql_set_mac_addr(ndev, qdev->nvram_data.funcCfg_fn0.macAddress);
|
|
}
|
|
memcpy(ndev->perm_addr, ndev->dev_addr, ndev->addr_len);
|
|
|
|
ndev->tx_queue_len = NUM_REQ_Q_ENTRIES;
|
|
|
|
/* 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) {
|
|
pr_err("%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);
|
|
INIT_DELAYED_WORK(&qdev->reset_work, ql_reset_work);
|
|
INIT_DELAYED_WORK(&qdev->tx_timeout_work, ql_tx_timeout_work);
|
|
INIT_DELAYED_WORK(&qdev->link_state_work, ql_link_state_machine_work);
|
|
|
|
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) {
|
|
pr_alert("%s\n", DRV_STRING);
|
|
pr_alert("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);
|
|
|
|
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;
|
|
}
|
|
|
|
iounmap(qdev->mem_map_registers);
|
|
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);
|