From: Marcus Barrow <mbarrow@redhat.com> Date: Wed, 18 Nov 2009 14:42:10 -0500 Subject: [net] qlge: updates and fixes for RHEL-5.5 Message-id: <20091118144210.23069.16580.sendpatchset@file.bos.redhat.com> Patchwork-id: 21415 O-Subject: [rhel 5.5 patch] qlge - updates and fixes from upstream or testing. Bugzilla: 519453 RH-Acked-by: Neil Horman <nhorman@redhat.com> RH-Acked-by: David S. Miller <davem@redhat.com> RH-Acked-by: Stanislaw Gruszka <sgruszka@redhat.com> BZ 519453 This patch provides updates and fixes from upstream or testing for the QLGE 10 GigE driver. It applies and builds cleanly on 2.6.18-170 with the QLGE KVM boot crash patch. It has been tested at QLogic and where possible patches have been pushed upstream or are being tested for upstream. - Fixed WOL Do not change frame routing during suspend. commit 4fc9b8697cdb42f7df322fb97e635491e182ae65 - Fixed ethtool loopback test hang issue commit 9dfbbaa6b0b9f7c4d6c9dc3a59006f44e6521138 - Backward compatibility for rh5.3 sles10.2 - Added standard pause support using ethtool commit 1d30df24ec85477368e6e38fe1b4d1b67b3be9d4 - Showing nic rx fifo drop Per priority transmit receive pause frame counters in statistics commit 6abd23468deaf3f6215a80b564f85df934209721 - Added Force Coredump to log file and dumping 64 nic registers to ethtool users space - Fixed bonding issue by setting Receive Routing bit and make it load param commit a3b71939a9c7a942323bfc0c8f97d0fd2bf22f95 - Fixed bonding mode tlb and alb In testing, we don't know if this is required upstream or not yet. - Adding new reset functionality stop management traffic and wait for the NIC and MGMNT FIFOs to empty commit 84087f4d482c5d554e1b26a557d424761213e5dc= - Adding New Coredump We are currently supporting core dump in distros only. - Adding missing bonding fixed We are in the process of pushing this upstream now. It's just a deletion of 2 lines - Fix frame routing for multicast frames Multicast should always be routed to the default (zeroeth) rx ring. Broadcast frames are already routed correctly. commit e163d7f2775624fcd7d21a060e18171fe006106d - Fix RX multicast filter settings This adds enable bit to filter write. commit 76b26694cc9eb8c7ea1004b0601a5953cfa57b89 - Fixed bonding mode 5 tlb by saving current We are in the process of testing this to see if it's required upstream. - Fix mailbox timed out error The mailbox command process would only process a maximum of 5 unrelated firmware events while waiting for it's command completion status. It should process an unlimited number of events while waiting for a maximum of 5 seconds. - add dcbx firmware async event handler The driver has nothing to do, but this marker prevents the event from showing up 'not handled'. - Fix WOL issue commit 4fc9b8697cdb42f7df322fb97e635491e182ae65 - Version -> p23 diff --git a/drivers/net/qlge/kcompat.h b/drivers/net/qlge/kcompat.h index 7606d89..77cc503 100644 --- a/drivers/net/qlge/kcompat.h +++ b/drivers/net/qlge/kcompat.h @@ -30,13 +30,13 @@ #include <asm/io.h> /*****************************************************************************/ -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) #ifndef NETIF_F_GRO #define vlan_gro_receive(_napi, _vlgrp, _vlan, _skb) \ vlan_hwaccel_receive_skb(_skb, _vlgrp, _vlan) #define napi_gro_receive(_napi, _skb) netif_receive_skb(_skb) #endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) #ifndef CHECKSUM_PARTIAL #define CHECKSUM_PARTIAL CHECKSUM_HW #define CHECKSUM_COMPLETE CHECKSUM_HW diff --git a/drivers/net/qlge/qlge.h b/drivers/net/qlge/qlge.h index 9e64f6f..ab2a2d6 100644 --- a/drivers/net/qlge/qlge.h +++ b/drivers/net/qlge/qlge.h @@ -17,9 +17,9 @@ */ #define DRV_NAME "qlge" #define DRV_STRING "QLogic 10 Gigabit PCI-E Ethernet Driver " -#define DRV_VERSION "1.00.00.22" -#define DIS_VERSION "2.6.16-2.6.18-p22" -#define REL_DATE "090819" +#define DRV_VERSION "1.00.00.23" +#define DIS_VERSION "2.6.16-2.6.18-p23" +#define REL_DATE "091109" #define PFX "qlge: " #define QPRINTK(qdev, nlevel, klevel, fmt, args...) \ @@ -163,9 +163,9 @@ enum { RST_FO_TFO = (1 << 0), RST_FO_RR_MASK = 0x00060000, RST_FO_RR_CQ_CAM = 0x00000000, - RST_FO_RR_DROP = 0x00000001, - RST_FO_RR_DQ = 0x00000002, - RST_FO_RR_RCV_FUNC_CQ = 0x00000003, + RST_FO_RR_DROP = 0x00000002, + RST_FO_RR_DQ = 0x00000004, + RST_FO_RR_RCV_FUNC_CQ = 0x00000006, RST_FO_FRB = (1 << 12), RST_FO_MOP = (1 << 13), RST_FO_REG = (1 << 14), @@ -841,6 +841,12 @@ enum { MB_CMD_GET_LINK_STS = 0x00000124, MB_CMD_SET_LED_CFG = 0x00000125, /* Set LED Configuration Register */ MB_CMD_GET_LED_CFG = 0x00000126, /* Get LED Configuration Register */ + MB_CMD_SET_MGMNT_TFK_CTL = 0x00000160, /* Set Mgmnt Traffic Control */ + MB_SET_MPI_TFK_STOP = (1 << 0), + MB_SET_MPI_TFK_RESUME = (1 << 1), + MB_CMD_GET_MGMNT_TFK_CTL = 0x00000161, /* Get Mgmnt Traffic Control */ + MB_GET_MPI_TFK_STOPPED = (1 << 0), + MB_GET_MPI_TFK_FIFO_EMPTY = (1 << 1), /* Sub-commands for IDC request. * This describes the reason for the * IDC request. @@ -1423,6 +1429,28 @@ struct nic_stats { u64 rx_1024_to_1518_pkts; u64 rx_1519_to_max_pkts; u64 rx_len_err_pkts; + + /* + * These stats come from offset 500h to 5C8h + * in the XGMAC register. + */ + u64 tx_cbfc_pause_frames0; + u64 tx_cbfc_pause_frames1; + u64 tx_cbfc_pause_frames2; + u64 tx_cbfc_pause_frames3; + u64 tx_cbfc_pause_frames4; + u64 tx_cbfc_pause_frames5; + u64 tx_cbfc_pause_frames6; + u64 tx_cbfc_pause_frames7; + u64 rx_cbfc_pause_frames0; + u64 rx_cbfc_pause_frames1; + u64 rx_cbfc_pause_frames2; + u64 rx_cbfc_pause_frames3; + u64 rx_cbfc_pause_frames4; + u64 rx_cbfc_pause_frames5; + u64 rx_cbfc_pause_frames6; + u64 rx_cbfc_pause_frames7; + u64 rx_nic_fifo_drop; }; /* Address/Length pairs for the coredump. */ @@ -1518,7 +1546,17 @@ enum { ETS_SEG_NUM = 34, PROBE_DUMP_SEG_NUM = 35, ROUTING_INDEX_SEG_NUM = 36, - MAC_PROTOCOL_SEG_NUM = 37 + MAC_PROTOCOL_SEG_NUM = 37, + XAUI2_AN_SEG_NUM = 38, + XAUI2_HSS_PCS_SEG_NUM = 39, + XFI2_AN_SEG_NUM = 40, + XFI2_TRAIN_SEG_NUM = 41, + XFI2_HSS_PCS_SEG_NUM = 42, + XFI2_HSS_TX_SEG_NUM = 43, + XFI2_HSS_RX_SEG_NUM = 44, + XFI2_HSS_PLL_SEG_NUM = 45, + SEM_REGS_SEG_NUM = 50 + }; /* Probe dump constants */ @@ -1532,17 +1570,47 @@ enum { /* Save both the address and data register */ #define WORDS_PER_MAC_PROT_ENTRY 2 +#define MAX_SEMAPHORE_FUNCTIONS 5 +#define WQC_WORD_SIZE 6 +#define NUMBER_OF_WQCS 128 +#define CQC_WORD_SIZE 13 +#define NUMBER_OF_CQCS 128 + +#define MPI_READ 0x00000000 +#define REG_BLOCK 0x00020000 +#define TEST_LOGIC_FUNC_PORT_CONFIG 0x1002 +#define NIC1_FUNCTION_ENABLE 0x00000001 +#define NIC1_FUNCTION_MASK 0x0000000e +#define NIC1_FUNCTION_SHIFT 1 +#define NIC2_FUNCTION_ENABLE 0x00000010 +#define NIC2_FUNCTION_MASK 0x000000e0 +#define NIC2_FUNCTION_SHIFT 5 +#define FC1_FUNCTION_ENABLE 0x00000100 +#define FC1_FUNCTION_MASK 0x00000e00 +#define FC1_FUNCTION_SHIFT 9 +#define FC2_FUNCTION_ENABLE 0x00001000 +#define FC2_FUNCTION_MASK 0x0000e000 +#define FC2_FUNCTION_SHIFT 13 +#define FUNCTION_SHIFT 6 + +#define XFI1_POWERED_UP 0x00000005 +#define XFI2_POWERED_UP 0x0000000A +#define XAUI_POWERED_DOWN 0x00000001 + +#define RISC_124 0x0003007c +#define RISC_127 0x0003007f +#define SHADOW_OFFSET 0xb0000000 #define SYS_CLOCK (0x00) #define PCI_CLOCK (0x80) #define FC_CLOCK (0x140) #define XGM_CLOCK (0x180) #define ADDRESS_REGISTER_ENABLE 0x00010000 -#define UP 0x00008000 -#define MAX_MUX 0x40 -#define MAX_MODULES 0x1F -#define RS_AND_ADR 0x06000000 -#define RS_ONLY 0x04000000 +#define UP 0x00008000 +#define MAX_MUX 0x40 +#define MAX_MODULES 0x1F +#define RS_AND_ADR 0x06000000 +#define RS_ONLY 0x04000000 #define NUM_TYPES 10 struct ql_nic_misc { @@ -1621,9 +1689,25 @@ struct ql_mpi_coredump { struct mpi_coredump_segment_header nic_regs_seg_hdr; u32 nic_regs[64]; + /* segment 17 */ + struct mpi_coredump_segment_header nic2_regs_seg_hdr; + u32 nic2_regs[64]; + /* segment 18 */ struct mpi_coredump_segment_header xgmac1_seg_hdr; - u32 xgmac1[((XGMAC_REGISTER_END - PAUSE_SRC_LO) >> 2)]; + u32 xgmac1[XGMAC_REGISTER_END / 4]; + + /* segment 19 */ + struct mpi_coredump_segment_header xgmac2_seg_hdr; + u32 xgmac2[XGMAC_REGISTER_END / 4]; + + /* segment 20 */ + struct mpi_coredump_segment_header code_ram_seg_hdr; + u32 code_ram[CODE_RAM_CNT]; + + /* segment 21 */ + struct mpi_coredump_segment_header memc_ram_seg_hdr; + u32 memc_ram[MEMC_RAM_CNT]; /* segment 22 */ struct mpi_coredump_segment_header xaui_an_hdr; @@ -1657,7 +1741,7 @@ struct ql_mpi_coredump { struct mpi_coredump_segment_header xfi_hss_pll_hdr; u32 serdes_xfi_hss_pll[32]; - /* segment ?? */ + /* segment 30 */ struct mpi_coredump_segment_header misc_nic_seg_hdr; struct ql_nic_misc misc_nic_info; @@ -1666,12 +1750,14 @@ struct ql_mpi_coredump { struct mpi_coredump_segment_header intr_states_seg_hdr; u32 intr_states[MAX_RX_RINGS]; + /* segment 32 */ /* 3 cam words each for 16 unicast, * 2 cam words for each of 32 multicast. */ struct mpi_coredump_segment_header cam_entries_seg_hdr; u32 cam_entries[(16 * 3) + (32 * 3)]; + /* segment 33 */ struct mpi_coredump_segment_header nic_routing_words_seg_hdr; u32 nic_routing_words[16]; @@ -1693,13 +1779,54 @@ struct ql_mpi_coredump { u32 mac_prot_regs[MAC_PROTOCOL_REGISTER_WORDS * WORDS_PER_MAC_PROT_ENTRY]; - /* segment 20 */ - struct mpi_coredump_segment_header code_ram_seg_hdr; - u32 code_ram[CODE_RAM_CNT]; + /* segment 38 */ + struct mpi_coredump_segment_header xaui2_an_hdr; + u32 serdes2_xaui_an[14]; - /* segment 21 */ - struct mpi_coredump_segment_header memc_ram_seg_hdr; - u32 memc_ram[MEMC_RAM_CNT]; + /* segment 39 */ + struct mpi_coredump_segment_header xaui2_hss_pcs_hdr; + u32 serdes2_xaui_hss_pcs[33]; + + /* segment 40 */ + struct mpi_coredump_segment_header xfi2_an_hdr; + u32 serdes2_xfi_an[14]; + + /* segment 41 */ + struct mpi_coredump_segment_header xfi2_train_hdr; + u32 serdes2_xfi_train[12]; + + /* segment 42 */ + struct mpi_coredump_segment_header xfi2_hss_pcs_hdr; + u32 serdes2_xfi_hss_pcs[15]; + + /* segment 43 */ + struct mpi_coredump_segment_header xfi2_hss_tx_hdr; + u32 serdes2_xfi_hss_tx[32]; + + /* segment 44 */ + struct mpi_coredump_segment_header xfi2_hss_rx_hdr; + u32 serdes2_xfi_hss_rx[32]; + + /* segment 45 */ + struct mpi_coredump_segment_header xfi2_hss_pll_hdr; + u32 serdes2_xfi_hss_pll[32]; + + /* segment 50 */ + /* semaphore register for all 5 functions */ + struct mpi_coredump_segment_header sem_regs_seg_hdr; + u32 sem_regs[MAX_SEMAPHORE_FUNCTIONS]; + + struct mpi_coredump_segment_header wqc1_seg_hdr; + u32 wqc1[WQC_WORD_SIZE * NUMBER_OF_WQCS]; + + struct mpi_coredump_segment_header cqc1_seg_hdr; + u32 cqc1[CQC_WORD_SIZE * NUMBER_OF_CQCS]; + + struct mpi_coredump_segment_header wqc2_seg_hdr; + u32 wqc2[WQC_WORD_SIZE * NUMBER_OF_WQCS]; + + struct mpi_coredump_segment_header cqc2_seg_hdr; + u32 cqc2[CQC_WORD_SIZE * NUMBER_OF_CQCS]; }; /* @@ -1738,6 +1865,7 @@ enum { QL_TESTING = 10, QL_IN_FW_RST = 11, QL_SPOOL_LOG = 12, + QL_LINK_UP = 13, }; /* link_status bit definitions */ @@ -1876,11 +2004,14 @@ struct ql_adapter { struct work_struct mpi_port_cfg_work; struct work_struct mpi_idc_work; struct work_struct mpi_core_to_log; + struct work_struct link_work; struct completion ide_completion; struct nic_operations *nic_ops; u16 device_id; struct timer_list eeh_timer; uint32_t *config_space; + /* Saving mac addr */ + char current_mac_addr[6]; }; /* @@ -1981,6 +2112,9 @@ int ql_mb_get_port_cfg(struct ql_adapter *qdev); int ql_mb_set_port_cfg(struct ql_adapter *qdev); int qlge_send(struct sk_buff *skb, struct net_device *ndev); void ql_check_receive_frame(struct sk_buff *skb); +int ql_own_firmware(struct ql_adapter *qdev); +int ql_wait_fifo_empty(struct ql_adapter *); +int ql_mb_set_mgmnt_traffic_ctl(struct ql_adapter *, u32); #if 1 #define QL_ALL_DUMP diff --git a/drivers/net/qlge/qlge_dbg.c b/drivers/net/qlge/qlge_dbg.c index 031292c..7740ef2 100644 --- a/drivers/net/qlge/qlge_dbg.c +++ b/drivers/net/qlge/qlge_dbg.c @@ -1,142 +1,446 @@ #include "qlge.h" -/* Read the 400 xgmac control/statistics registers - * skipping unused locations. - */ -static int ql_get_xgmac_regs(struct ql_adapter *qdev, u32 * buf) +unsigned int get_other_func_num(struct ql_adapter *qdev) { - int status = 0; - int i; + unsigned int other_function; + unsigned int status; + unsigned int test_logic; + unsigned int nic1_function_num = 0xffffffff; + unsigned int nic2_function_num = 0xffffffff; + unsigned int fc1_function_num = 0xffffffff; + unsigned int fc2_function_num = 0xffffffff; + + QPRINTK_DBG(qdev, DRV, ERR, "Trying to figure out function mapping, " + "my function = %d\n", qdev->func); + status = ql_read_mpi_reg(qdev, TEST_LOGIC_FUNC_PORT_CONFIG, + &test_logic); + if (status) + return(0xffffffff); + + QPRINTK_DBG(qdev, DRV, ERR, "Test logic 0x%08x = 0x%08x\n", + TEST_LOGIC_FUNC_PORT_CONFIG, test_logic); + + /* First, establish the function number of the other PCI function */ + if (test_logic & FC1_FUNCTION_ENABLE) + fc1_function_num = (test_logic & FC1_FUNCTION_MASK) >> + FC1_FUNCTION_SHIFT; + if (test_logic & FC2_FUNCTION_ENABLE) + fc2_function_num = (test_logic & FC2_FUNCTION_MASK) >> + FC2_FUNCTION_SHIFT; + if (test_logic & NIC1_FUNCTION_ENABLE) + nic1_function_num = (test_logic & NIC1_FUNCTION_MASK) >> + NIC1_FUNCTION_SHIFT; + if (test_logic & NIC2_FUNCTION_ENABLE) + nic2_function_num = (test_logic & NIC2_FUNCTION_MASK) >> + NIC2_FUNCTION_SHIFT; + if (qdev->func == nic1_function_num) + other_function = nic2_function_num; + else + other_function = nic1_function_num; - for (i = PAUSE_SRC_LO; i < XGMAC_REGISTER_END; i += 4, buf++) { - /* We're reading 400 xgmac registers, but we filter out - * serveral locations that are non-responsive to reads. - */ - if ((i == 0x00000114) || - (i == 0x00000118) || - (i == 0x0000013c) || - (i == 0x00000140) || - (i > 0x00000150 && i < 0x000001fc) || - (i > 0x00000278 && i < 0x000002a0) || - (i > 0x000002c0 && i < 0x000002cf) || - (i > 0x000002dc && i < 0x000002f0) || - (i > 0x000003c8 && i < 0x00000400) || - (i > 0x00000400 && i < 0x00000410) || - (i > 0x00000410 && i < 0x00000420) || - (i > 0x00000420 && i < 0x00000430) || - (i > 0x00000430 && i < 0x00000440) || - (i > 0x00000440 && i < 0x00000450) || - (i > 0x00000450 && i < 0x00000500) || - (i > 0x0000054c && i < 0x00000568) || - (i > 0x000005c8 && i < 0x00000600)) { - *buf = 0xdeadbeef; - continue; - } + QPRINTK_DBG(qdev, DRV, ERR, "My function = %d, other NIC function = %d," + "FC1 Func = %d, FC2 Func = %d\n", qdev->func, + other_function, fc1_function_num, fc2_function_num); + return(other_function); +} - status = ql_read_xgmac_reg(qdev, i, buf); - if (status) - goto err; +static unsigned int read_other_function_reg(struct ql_adapter *qdev, + unsigned int reg) +{ + unsigned int other_function; + unsigned int register_to_read; + unsigned int reg_val; + unsigned int status = 0; + + other_function = get_other_func_num(qdev); + + /* The other function is not enabled so we don't need + * to read anything out of it. + */ + if (other_function == 0xffffffff) + return(other_function); + + /* The other function is there so now we need to + * read the requested register. + */ + register_to_read = REG_BLOCK | MPI_READ | + (other_function << FUNCTION_SHIFT) | reg; + status = ql_read_mpi_reg(qdev, register_to_read, ®_val); + if (status != 0) + return(0xffffffff); + + return(reg_val); +} + +static unsigned int write_other_function_reg(struct ql_adapter *qdev, + unsigned int reg, unsigned int reg_val) +{ + unsigned int other_function; + unsigned int register_to_read; + unsigned int status = 0; + + other_function = get_other_func_num(qdev); + + /* + * The other function is not enabled so we don't + * need to read anything out of it. + */ + if (other_function == 0xffffffff) + return(-1); + + /* + * The other function is there so now we need + * to read the requested register. + */ + register_to_read = REG_BLOCK | MPI_READ | + (other_function << FUNCTION_SHIFT) | reg; + status = ql_write_mpi_reg(qdev, register_to_read, reg_val); + + return(status); +} + +int wait_other_function_reg_rdy(struct ql_adapter *qdev, unsigned int reg, + unsigned int bit, unsigned int err_bit) +{ + unsigned int temp; + int count = 10; + + while (count) { + temp = read_other_function_reg(qdev, reg); + + /* check for errors */ + if (temp & err_bit) + return -1; + else if (temp & bit) + return 0; + mdelay(10); + count--; } -err: + return -1; +} + +int read_other_function_serdes_reg(struct ql_adapter *qdev, unsigned int reg, + unsigned int *data) +{ + int status; + + /* wait for reg to come ready */ + status = wait_other_function_reg_rdy(qdev, XG_SERDES_ADDR / 4, + XG_SERDES_ADDR_RDY, 0); + if (status) + goto exit; + + /* set up for reg read */ + write_other_function_reg(qdev, XG_SERDES_ADDR / 4, reg | PROC_ADDR_R); + + /* wait for reg to come ready */ + status = wait_other_function_reg_rdy(qdev, XG_SERDES_ADDR / 4, + XG_SERDES_ADDR_RDY, 0); + if (status) + goto exit; + + /* get the data */ + *data = read_other_function_reg(qdev, (XG_SERDES_DATA / 4)); +exit: return status; } +int ql_read_other_function_xgmac_reg(struct ql_adapter *qdev, unsigned int reg, + unsigned int *data) +{ + int status = 0; + + /* wait for reg to come ready */ + status = wait_other_function_reg_rdy(qdev, XGMAC_ADDR / 4, + XGMAC_ADDR_RDY, XGMAC_ADDR_XME); + if (status) + goto exit; + + /* set up for reg read */ + write_other_function_reg(qdev, XGMAC_ADDR / 4, reg | XGMAC_ADDR_R); + + /* wait for reg to come ready */ + status = wait_other_function_reg_rdy(qdev, XGMAC_ADDR / 4, + XGMAC_ADDR_RDY, XGMAC_ADDR_XME); + if (status) + goto exit; + + /* get the data */ + *data = read_other_function_reg(qdev, XGMAC_DATA / 4); +exit: + return status; +} + +/* Read out the SERDES registers */ int ql_read_serdes_reg(struct ql_adapter *qdev, u32 reg, u32 * data) { int status; + /* wait for reg to come ready */ status = ql_wait_reg_rdy(qdev, XG_SERDES_ADDR, XG_SERDES_ADDR_RDY, 0); if (status) goto exit; + /* set up for reg read */ ql_write32(qdev, XG_SERDES_ADDR, reg | PROC_ADDR_R); + /* wait for reg to come ready */ status = ql_wait_reg_rdy(qdev, XG_SERDES_ADDR, XG_SERDES_ADDR_RDY, 0); if (status) goto exit; + /* get the data */ *data = ql_read32(qdev, XG_SERDES_DATA); exit: return status; } +void get_both_serdes(struct ql_adapter *qdev, unsigned int addr, + unsigned int *direct_ptr, unsigned int *indirect_ptr, + unsigned int direct_valid, unsigned int indirect_valid) +{ + unsigned int status; + + status = 0xffffffff; + if(direct_valid) + status = ql_read_serdes_reg(qdev, addr, direct_ptr); + + if (status) + *direct_ptr = 0xDEADBEEF; + + status = 0xffffffff; + if(indirect_valid) + status = read_other_function_serdes_reg(qdev, addr, indirect_ptr); + + if (status) + *indirect_ptr = 0xDEADBEEF; +} + static int ql_get_serdes_regs(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) { - u32 i, j; int status; + unsigned int xfi_direct_valid, xfi_indirect_valid, xaui_direct_valid; + unsigned int xaui_indirect_valid, temp, xaui_reg, i; + unsigned int *direct_ptr; + unsigned int *indirect_ptr; + + xfi_direct_valid = xfi_indirect_valid = 0; + xaui_direct_valid = xaui_indirect_valid = 1; + + /* The XAUI needs to be read out per port */ + xaui_reg = 0x800; + if (qdev->func & 1) { + /* We are NIC 2 */ + status = read_other_function_serdes_reg(qdev, xaui_reg, &temp); + if(status) + temp = XAUI_POWERED_DOWN; + if((temp & XAUI_POWERED_DOWN) == XAUI_POWERED_DOWN) + xaui_indirect_valid = 0; + + status = ql_read_serdes_reg(qdev, xaui_reg, &temp); + if(status) + temp = XAUI_POWERED_DOWN; + + if((temp & XAUI_POWERED_DOWN) == XAUI_POWERED_DOWN) + xaui_direct_valid = 0; + } else { + /* We are NIC 1 */ + status = read_other_function_serdes_reg(qdev, xaui_reg, &temp); + if(status) + temp = XAUI_POWERED_DOWN; + if((temp & XAUI_POWERED_DOWN) == XAUI_POWERED_DOWN) + xaui_indirect_valid = 0; + + status = ql_read_serdes_reg(qdev, xaui_reg, &temp); + if(status) + temp = XAUI_POWERED_DOWN; + if((temp & XAUI_POWERED_DOWN) == XAUI_POWERED_DOWN) + xaui_direct_valid = 0; + } - for (i = 0, j = 0; i <= 0x000000034; i += 4) { - status = ql_read_serdes_reg(qdev, i, - &mpi_coredump->serdes_xaui_an[j++]); - if (status) - goto err; - + /* + * XFI register is shared so only need to read one + * functions and then check the bits. + */ + status = ql_read_serdes_reg(qdev, 0x1E06, &temp); + if(status) + temp = 0; + + if((temp & XFI1_POWERED_UP) == XFI1_POWERED_UP) { + /* now see if i'm NIC 1 or NIC 2 */ + if(qdev->func & 1) + /* I'm NIC 2, so the indirect (NIC1) xfi is up. */ + xfi_indirect_valid = 1; + else + xfi_direct_valid = 1; + } + if((temp & XFI2_POWERED_UP) == XFI2_POWERED_UP) { + /* now see if i'm NIC 1 or NIC 2 */ + if(qdev->func & 1) + /* I'm NIC 2, so the indirect (NIC1) xfi is up. */ + xfi_direct_valid = 1; + else + xfi_indirect_valid = 1; } - for (i = 0x800, j = 0; i <= 0x880; i += 4) { - status = ql_read_serdes_reg(qdev, i, - &mpi_coredump-> - serdes_xaui_hss_pcs[j++]); - if (status) - goto err; + if (qdev->func & 1) { + /* Function 2 is direct */ + direct_ptr = (unsigned int *)(&mpi_coredump->serdes2_xaui_an); + indirect_ptr = (unsigned int *)(&mpi_coredump->serdes_xaui_an); + } else { + /* Function 1 is direct */ + direct_ptr = (unsigned int *)(&mpi_coredump->serdes_xaui_an); + indirect_ptr = (unsigned int *)(&mpi_coredump->serdes2_xaui_an); } - for (i = 0x1000, j = 0; i <= 0x1034; i += 4) { - status = ql_read_serdes_reg(qdev, i, - &mpi_coredump->serdes_xfi_an[j++]); - if (status) - goto err; + for (i = 0; i <= 0x000000034; i += 4, direct_ptr ++, indirect_ptr ++) + get_both_serdes(qdev, i, direct_ptr, indirect_ptr, + xaui_direct_valid, xaui_indirect_valid); + if (qdev->func & 1) { + direct_ptr = + (unsigned int *)(&mpi_coredump->serdes2_xaui_hss_pcs); + indirect_ptr = + (unsigned int *)(&mpi_coredump->serdes_xaui_hss_pcs); + } else { + direct_ptr = + (unsigned int *)(&mpi_coredump->serdes_xaui_hss_pcs); + indirect_ptr = + (unsigned int *)(&mpi_coredump->serdes2_xaui_hss_pcs); } - for (i = 0x1050, j = 0; i <= 0x107c; i += 4) { - status = ql_read_serdes_reg(qdev, i, - &mpi_coredump-> - serdes_xfi_train[j++]); - if (status) - goto err; + for (i = 0x800; i <= 0x880; i += 4, direct_ptr ++, indirect_ptr ++) + get_both_serdes(qdev, i, direct_ptr, indirect_ptr, + xaui_direct_valid, xaui_indirect_valid); + if (qdev->func & 1) { + direct_ptr = (unsigned int *)(&mpi_coredump->serdes2_xfi_an); + indirect_ptr = (unsigned int *)(&mpi_coredump->serdes_xfi_an); + } else { + direct_ptr = (unsigned int *)(&mpi_coredump->serdes_xfi_an); + indirect_ptr = (unsigned int *)(&mpi_coredump->serdes2_xfi_an); } - for (i = 0x1800, j = 0; i <= 0x1838; i += 4) { - status = ql_read_serdes_reg(qdev, i, - &mpi_coredump-> - serdes_xfi_hss_pcs[j++]); - if (status) - goto err; + for (i = 0x1000; i <= 0x1034; i += 4, direct_ptr ++, indirect_ptr ++) + get_both_serdes(qdev, i, direct_ptr, indirect_ptr, + xfi_direct_valid, xfi_indirect_valid); + if (qdev->func & 1) { + direct_ptr = (unsigned int *)(&mpi_coredump->serdes2_xfi_train); + indirect_ptr = + (unsigned int *)(&mpi_coredump->serdes_xfi_train); + } else { + direct_ptr = (unsigned int *)(&mpi_coredump->serdes_xfi_train); + indirect_ptr = + (unsigned int *)(&mpi_coredump->serdes2_xfi_train); } - for (i = 0x1c00, j = 0; i <= 0x1c1f; i++) { - status = ql_read_serdes_reg(qdev, i, - &mpi_coredump-> - serdes_xfi_hss_tx[j++]); - if (status) - goto err; + for (i = 0x1050; i <= 0x107c; i += 4, direct_ptr ++, indirect_ptr ++) + get_both_serdes(qdev, i, direct_ptr, indirect_ptr, + xfi_direct_valid, xfi_indirect_valid); + if (qdev->func & 1){ + direct_ptr = + (unsigned int *)(&mpi_coredump->serdes2_xfi_hss_pcs); + indirect_ptr = + (unsigned int *)(&mpi_coredump->serdes_xfi_hss_pcs); + } else { + direct_ptr = + (unsigned int *)(&mpi_coredump->serdes_xfi_hss_pcs); + indirect_ptr = + (unsigned int *)(&mpi_coredump->serdes2_xfi_hss_pcs); } - for (i = 0x1c40, j = 0; i <= 0x1c5f; i++) { - status = ql_read_serdes_reg(qdev, i, - &mpi_coredump-> - serdes_xfi_hss_rx[j++]); - if (status) - goto err; + for (i = 0x1800; i <= 0x1838; i += 4, direct_ptr++, indirect_ptr ++) + get_both_serdes(qdev, i, direct_ptr, indirect_ptr, + xfi_direct_valid, xfi_indirect_valid); + if (qdev->func & 1) { + direct_ptr = + (unsigned int *)(&mpi_coredump->serdes2_xfi_hss_tx); + indirect_ptr = + (unsigned int *)(&mpi_coredump->serdes_xfi_hss_tx); + } else { + direct_ptr = (unsigned int *)(&mpi_coredump->serdes_xfi_hss_tx); + indirect_ptr = + (unsigned int *)(&mpi_coredump->serdes2_xfi_hss_tx); + } + for (i = 0x1c00; i <= 0x1c1f; i++, direct_ptr ++, indirect_ptr ++) + get_both_serdes(qdev, i, direct_ptr, indirect_ptr, + xfi_direct_valid, xfi_indirect_valid); + if (qdev->func & 1) { + direct_ptr = + (unsigned int *)(&mpi_coredump->serdes2_xfi_hss_rx); + indirect_ptr = + (unsigned int *)(&mpi_coredump->serdes_xfi_hss_rx); + } else { + direct_ptr = (unsigned int *)(&mpi_coredump->serdes_xfi_hss_rx); + indirect_ptr = + (unsigned int *)(&mpi_coredump->serdes2_xfi_hss_rx); } - for (i = 0x1e00, j = 0; i <= 0x1e1f; i++) { - status = ql_read_serdes_reg(qdev, i, - &mpi_coredump-> - serdes_xfi_hss_pll[j++]); - if (status) - goto err; + for (i = 0x1c40; i <= 0x1c5f; i++, direct_ptr ++, indirect_ptr ++) + get_both_serdes(qdev, i, direct_ptr, indirect_ptr, + xfi_direct_valid, xfi_indirect_valid); + + if (qdev->func & 1) { + direct_ptr = + (unsigned int *)(&mpi_coredump->serdes2_xfi_hss_pll); + indirect_ptr = + (unsigned int *)(&mpi_coredump->serdes_xfi_hss_pll); + } else { + direct_ptr = + (unsigned int *)(&mpi_coredump->serdes_xfi_hss_pll); + indirect_ptr = + (unsigned int *)(&mpi_coredump->serdes2_xfi_hss_pll); } + for (i = 0x1e00; i <= 0x1e1f; i++, direct_ptr ++, indirect_ptr ++) + get_both_serdes(qdev, i, direct_ptr, indirect_ptr, + xfi_direct_valid, xfi_indirect_valid); + return(0); +} -err: - if (status) - QPRINTK(qdev, DRV, ERR, - "Serdes register 0x%.08x access error\n", i); +/* Read the 400 xgmac control/statistics registers + * skipping unused locations. + */ +static int ql_get_xgmac_regs(struct ql_adapter *qdev, u32 * buf, + unsigned int other_function) +{ + int status = 0; + int i; + for (i = PAUSE_SRC_LO; i < XGMAC_REGISTER_END; i += 4, buf++) { + /* We're reading 400 xgmac registers, but we filter out + * serveral locations that are non-responsive to reads. + */ + if ((i == 0x00000114) || + (i == 0x00000118) || + (i == 0x0000013c) || + (i == 0x00000140) || + (i > 0x00000150 && i < 0x000001fc) || + (i > 0x00000278 && i < 0x000002a0) || + (i > 0x000002c0 && i < 0x000002cf) || + (i > 0x000002dc && i < 0x000002f0) || + (i > 0x000003c8 && i < 0x00000400) || + (i > 0x00000400 && i < 0x00000410) || + (i > 0x00000410 && i < 0x00000420) || + (i > 0x00000420 && i < 0x00000430) || + (i > 0x00000430 && i < 0x00000440) || + (i > 0x00000440 && i < 0x00000450) || + (i > 0x00000450 && i < 0x00000500) || + (i > 0x0000054c && i < 0x00000568) || + (i > 0x000005c8 && i < 0x00000600)) { + if (other_function) + status = + ql_read_other_function_xgmac_reg(qdev, i, buf); + else + status = ql_read_xgmac_reg(qdev, i, buf); + + if (status) + *buf = 0xdeadbeef; + break; + } + } return status; } +/* Read out the ETS registers */ static int ql_get_ets_regs(struct ql_adapter *qdev, u32 * buf) { int status = 0; @@ -155,6 +459,7 @@ static int ql_get_ets_regs(struct ql_adapter *qdev, u32 * buf) return status; } +/* For each RX Ring, read the interrupt enable register */ static void ql_get_intr_states(struct ql_adapter *qdev, u32 * buf) { int i; @@ -227,15 +532,12 @@ err: return status; } +/* Read the MPI Processor shadow registers */ static int ql_get_mpi_shadow_regs(struct ql_adapter *qdev, u32 * buf) { u32 i; int status; -#define RISC_124 0x0003007c -#define RISC_127 0x0003007f -#define SHADOW_OFFSET 0xb0000000 - for (i = 0; i < MPI_CORE_SH_REGS_CNT; i++, buf++) { status = ql_write_mpi_reg(qdev, RISC_124, (SHADOW_OFFSET | i << 20)); @@ -249,6 +551,7 @@ end: return status; } +/* Read the MPI Processor core registers */ static int ql_get_mpi_regs(struct ql_adapter *qdev, u32 * buf, u32 offset, u32 count) { @@ -261,6 +564,7 @@ static int ql_get_mpi_regs(struct ql_adapter *qdev, u32 * buf, return status; } +/* Read the ASIC probe dump */ static unsigned int *ql_get_probe(struct ql_adapter *qdev, u32 clock, u8 *valid, u32 *buf) { @@ -445,6 +749,7 @@ static int ql_get_probe_dump(struct ql_adapter *qdev, unsigned int *buf) } +/* Read out the routing index registers */ static int ql_get_routing_index_registers(struct ql_adapter *qdev, u32 *buf) { int status; @@ -483,6 +788,7 @@ static int ql_get_routing_index_registers(struct ql_adapter *qdev, u32 *buf) return status; } +/* Read out the MAC protocol registers */ void ql_get_mac_protocol_registers(struct ql_adapter *qdev, u32 *buf) { u32 result_index, result_data; @@ -569,6 +875,7 @@ void ql_get_mac_protocol_registers(struct ql_adapter *qdev, u32 *buf) } } +/* Create a coredump segment header */ static void ql_build_coredump_seg_header( struct mpi_coredump_segment_header *seg_hdr, u32 seg_number, u32 seg_size, u8 *desc) @@ -580,10 +887,18 @@ static void ql_build_coredump_seg_header( memcpy(seg_hdr->description, desc, (sizeof(seg_hdr->description)) - 1); } +/* + * This function should be called when a coredump / probedump + * is to be extracted from the HBA. It is assumed there is a + * qdev structure that contains the base address of the register + * space for this function as well as a coredump structure that + * will contain the dump. + */ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) { int status; int i; + unsigned int func_num, reg, reg_val; if (!mpi_coredump) { QPRINTK(qdev, DRV, ERR, @@ -604,6 +919,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) goto err; } + /* Insert the global header */ memset(&(mpi_coredump->mpi_global_header), 0, sizeof(struct mpi_coredump_global_header)); mpi_coredump->mpi_global_header.cookie = MPI_COREDUMP_COOKIE; @@ -614,12 +930,166 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) memcpy(mpi_coredump->mpi_global_header.idString, "MPI Coredump", sizeof(mpi_coredump->mpi_global_header.idString)); + /* Get generic NIC reg dump */ + ql_build_coredump_seg_header(&mpi_coredump->nic_regs_seg_hdr, + NIC1_CONTROL_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->nic_regs), "NIC1 Registers"); + + ql_build_coredump_seg_header(&mpi_coredump->nic2_regs_seg_hdr, + NIC2_CONTROL_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->nic2_regs), "NIC2 Registers"); + + /* Get XGMac registers. (Segment 18, Rev C. step 21) */ + ql_build_coredump_seg_header(&mpi_coredump->xgmac1_seg_hdr, + NIC1_XGMAC_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->xgmac1), "NIC1 XGMac Registers"); + + ql_build_coredump_seg_header(&mpi_coredump->xgmac2_seg_hdr, + NIC2_XGMAC_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->xgmac2), "NIC2 XGMac Registers"); + + if (qdev->func & 1){ + /* Odd means our function is NIC 2 */ + for (i = 0; i < 64; i++) + mpi_coredump->nic2_regs[i] = + ql_read32(qdev, i * sizeof(u32)); + + for (i = 0; i < 64; i++) + mpi_coredump->nic_regs[i] = + read_other_function_reg(qdev, (i * sizeof(u32)) / 4); + + ql_get_xgmac_regs(qdev, &mpi_coredump->xgmac2[0], 0); + ql_get_xgmac_regs(qdev, &mpi_coredump->xgmac1[0], 1); + } else { + /* Even means our function is NIC 1 */ + for (i = 0; i < 64; i++) + mpi_coredump->nic_regs[i] = + ql_read32(qdev, i * sizeof(u32)); + + for (i = 0; i < 64; i++) + mpi_coredump->nic2_regs[i] = + read_other_function_reg(qdev, (i * sizeof(u32)) / 4); + + ql_get_xgmac_regs(qdev, &mpi_coredump->xgmac1[0], 0); + ql_get_xgmac_regs(qdev, &mpi_coredump->xgmac2[0], 1); + } + + /* Rev C. Step 20a */ + ql_build_coredump_seg_header(&mpi_coredump->xaui_an_hdr, + XAUI_AN_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->serdes_xaui_an), + "XAUI AN Registers"); + + /* Rev C. Step 20b */ + ql_build_coredump_seg_header(&mpi_coredump->xaui_hss_pcs_hdr, + XAUI_HSS_PCS_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->serdes_xaui_hss_pcs), + "XAUI HSS PCS Registers"); + + ql_build_coredump_seg_header(&mpi_coredump->xfi_an_hdr, XFI_AN_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->serdes_xfi_an), + "XFI AN Registers"); + + ql_build_coredump_seg_header(&mpi_coredump->xfi_train_hdr, + XFI_TRAIN_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->serdes_xfi_train), + "XFI TRAIN Registers"); + + ql_build_coredump_seg_header(&mpi_coredump->xfi_hss_pcs_hdr, + XFI_HSS_PCS_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->serdes_xfi_hss_pcs), + "XFI HSS PCS Registers"); + + ql_build_coredump_seg_header(&mpi_coredump->xfi_hss_tx_hdr, + XFI_HSS_TX_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->serdes_xfi_hss_tx), + "XFI HSS TX Registers"); + + ql_build_coredump_seg_header(&mpi_coredump->xfi_hss_rx_hdr, + XFI_HSS_RX_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->serdes_xfi_hss_rx), + "XFI HSS RX Registers"); + + ql_build_coredump_seg_header(&mpi_coredump->xfi_hss_pll_hdr, + XFI_HSS_PLL_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->serdes_xfi_hss_pll), + "XFI HSS PLL Registers"); + + ql_build_coredump_seg_header(&mpi_coredump->xaui2_an_hdr, + XAUI2_AN_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->serdes2_xaui_an), + "XAUI2 AN Registers"); + + ql_build_coredump_seg_header(&mpi_coredump->xaui2_hss_pcs_hdr, + XAUI2_HSS_PCS_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->serdes2_xaui_hss_pcs), + "XAUI2 HSS PCS Registers"); + + ql_build_coredump_seg_header(&mpi_coredump->xfi2_an_hdr, + XFI2_AN_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->serdes2_xfi_an), + "XFI2 AN Registers"); + + ql_build_coredump_seg_header(&mpi_coredump->xfi2_train_hdr, + XFI2_TRAIN_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->serdes2_xfi_train), + "XFI2 TRAIN Registers"); + + ql_build_coredump_seg_header(&mpi_coredump->xfi2_hss_pcs_hdr, + XFI2_HSS_PCS_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->serdes2_xfi_hss_pcs), + "XFI2 HSS PCS Registers"); + + ql_build_coredump_seg_header(&mpi_coredump->xfi2_hss_tx_hdr, + XFI2_HSS_TX_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->serdes2_xfi_hss_tx), + "XFI2 HSS TX Registers"); + + ql_build_coredump_seg_header(&mpi_coredump->xfi2_hss_rx_hdr, + XFI2_HSS_RX_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->serdes2_xfi_hss_rx), + "XFI2 HSS RX Registers"); + + ql_build_coredump_seg_header(&mpi_coredump->xfi2_hss_pll_hdr, + XFI2_HSS_PLL_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->serdes2_xfi_hss_pll), + "XFI2 HSS PLL Registers"); + + status = ql_get_serdes_regs(qdev, mpi_coredump); + if (status) { + QPRINTK(qdev, DRV, ERR, + "Failed Dump of Serdes Registers. Status = 0x%.08x\n", + status); + goto err; + } + ql_build_coredump_seg_header(&mpi_coredump->core_regs_seg_hdr, CORE_SEG_NUM, sizeof(mpi_coredump->core_regs_seg_hdr) + sizeof(mpi_coredump->mpi_core_regs) + sizeof(mpi_coredump->mpi_core_sh_regs), "Core Registers"); + /* Get the MPI Core Registers */ status = ql_get_mpi_regs(qdev, &mpi_coredump->mpi_core_regs[0], MPI_CORE_REGS_ADDR, MPI_CORE_REGS_CNT); @@ -787,78 +1257,6 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) if (status) goto err; - /* Get generic NIC reg dump */ - /* Segment 16, Rev C. Step 18 */ - ql_build_coredump_seg_header(&mpi_coredump->nic_regs_seg_hdr, - NIC1_CONTROL_SEG_NUM, - sizeof(struct mpi_coredump_segment_header) - + sizeof(mpi_coredump->nic_regs), - "NIC Registers"); - - for (i = 0; i < 64; i++) - mpi_coredump->nic_regs[i] = ql_read32(qdev, i * sizeof(u32)); - - - /* Rev C. Step 19 */ - /* NIC2_CONTROL_SEG_NUM = 17 */ - - /* Rev C. Step 20a */ - ql_build_coredump_seg_header(&mpi_coredump->xaui_an_hdr, - XAUI_AN_SEG_NUM, - sizeof(struct mpi_coredump_segment_header) - + sizeof(mpi_coredump->serdes_xaui_an), - "XAUI AN Registers"); - - /* Rev C. Step 20b */ - ql_build_coredump_seg_header(&mpi_coredump->xaui_hss_pcs_hdr, - XAUI_HSS_PCS_SEG_NUM, - sizeof(struct mpi_coredump_segment_header) - + sizeof(mpi_coredump->serdes_xaui_hss_pcs), - "XAUI HSS PCS Registers"); - - ql_build_coredump_seg_header(&mpi_coredump->xfi_an_hdr, XFI_AN_SEG_NUM, - sizeof(struct mpi_coredump_segment_header) - + sizeof(mpi_coredump->serdes_xfi_an), - "XFI AN Registers"); - - ql_build_coredump_seg_header(&mpi_coredump->xfi_train_hdr, - XFI_TRAIN_SEG_NUM, - sizeof(struct mpi_coredump_segment_header) - + sizeof(mpi_coredump->serdes_xfi_train), - "XFI TRAIN Registers"); - - ql_build_coredump_seg_header(&mpi_coredump->xfi_hss_pcs_hdr, - XFI_HSS_PCS_SEG_NUM, - sizeof(struct mpi_coredump_segment_header) - + sizeof(mpi_coredump->serdes_xfi_hss_pcs), - "XFI HSS PCS Registers"); - - ql_build_coredump_seg_header(&mpi_coredump->xfi_hss_tx_hdr, - XFI_HSS_TX_SEG_NUM, - sizeof(struct mpi_coredump_segment_header) - + sizeof(mpi_coredump->serdes_xfi_hss_tx), - "XFI HSS TX Registers"); - - ql_build_coredump_seg_header(&mpi_coredump->xfi_hss_rx_hdr, - XFI_HSS_RX_SEG_NUM, - sizeof(struct mpi_coredump_segment_header) - + sizeof(mpi_coredump->serdes_xfi_hss_rx), - "XFI HSS RX Registers"); - - ql_build_coredump_seg_header(&mpi_coredump->xfi_hss_pll_hdr, - XFI_HSS_PLL_SEG_NUM, - sizeof(struct mpi_coredump_segment_header) - + sizeof(mpi_coredump->serdes_xfi_hss_pll), - "XFI HSS PLL Registers"); - - status = ql_get_serdes_regs(qdev, mpi_coredump); - if (status) { - QPRINTK(qdev, DRV, ERR, - "Failed Dump of Serdes Registers. Status = 0x%.08x\n", - status); - goto err; - } - ql_build_coredump_seg_header(&mpi_coredump->misc_nic_seg_hdr, MISC_NIC_INFO_SEG_NUM, sizeof(struct mpi_coredump_segment_header) @@ -869,16 +1267,6 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) mpi_coredump->misc_nic_info.intr_count = qdev->intr_count; mpi_coredump->misc_nic_info.function = qdev->func; - /* Get XGMac registers. (Segment 18, Rev C. step 21) */ - ql_build_coredump_seg_header(&mpi_coredump->xgmac1_seg_hdr, - NIC1_XGMAC_SEG_NUM, - sizeof(struct mpi_coredump_segment_header) - + sizeof(mpi_coredump->xgmac1), - "NIC core XGMAC Registers port 1"); - status = ql_get_xgmac_regs(qdev, &mpi_coredump->xgmac1[0]); - if (status) - goto err; - /* Segment 31 */ /* Get indexed register values. */ ql_build_coredump_seg_header(&mpi_coredump->intr_states_seg_hdr, @@ -941,6 +1329,20 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) "MAC Prot Regs"); ql_get_mac_protocol_registers(qdev, &mpi_coredump->mac_prot_regs[0]); + /* Get the semaphore registers for all 5 functions */ + ql_build_coredump_seg_header(&mpi_coredump->sem_regs_seg_hdr, + SEM_REGS_SEG_NUM, + sizeof(struct mpi_coredump_segment_header) + + sizeof(mpi_coredump->sem_regs), "Sem Registers"); + + for(func_num = 0; func_num < MAX_SEMAPHORE_FUNCTIONS ; func_num ++) { + reg = REG_BLOCK | (func_num << FUNCTION_SHIFT) | (SEM / 4); + status = ql_read_mpi_reg(qdev, reg, ®_val); + mpi_coredump->sem_regs[func_num] = reg_val; + if (status != 0) + mpi_coredump->sem_regs[func_num] = 0xdeadbeef; + } + /* not in the spec... but we need to prevent the mpi restarting * while we dump the memory */ @@ -975,6 +1377,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) goto err; } + /* Insert the segment header */ ql_build_coredump_seg_header(&mpi_coredump->memc_ram_seg_hdr, MEMC_RAM_SEG_NUM, sizeof(struct mpi_coredump_segment_header) @@ -998,24 +1401,6 @@ static void ql_gen_reg_dump(struct ql_adapter *qdev, { int i; - memset(mpi_coredump, 0, sizeof(struct ql_mpi_coredump)); - /* Fill in the global dump header. */ - memset(&(mpi_coredump->mpi_global_header), 0, - sizeof(struct mpi_coredump_global_header)); - mpi_coredump->mpi_global_header.cookie = MPI_COREDUMP_COOKIE; - mpi_coredump->mpi_global_header.headerSize = - sizeof(struct mpi_coredump_global_header); - mpi_coredump->mpi_global_header.imageSize = - sizeof(struct ql_mpi_coredump); - memcpy(mpi_coredump->mpi_global_header.idString, "MPI Coredump", - sizeof(mpi_coredump->mpi_global_header.idString)); - - /* Fill in the generic dump segment header. */ - ql_build_coredump_seg_header(&mpi_coredump->nic_regs_seg_hdr, - NIC1_CONTROL_SEG_NUM, - sizeof(struct mpi_coredump_segment_header) - + sizeof(mpi_coredump->nic_regs), - "NIC Registers"); /* Get generic reg dump */ for (i = 0; i < 64; i++) mpi_coredump->nic_regs[i] = ql_read32(qdev, i * sizeof(u32)); @@ -1028,7 +1413,7 @@ void ql_mpi_core_to_log(struct work_struct *work) container_of(work, struct ql_adapter, mpi_core_to_log); u32 *tmp, count; int i; - + count = sizeof(struct ql_mpi_coredump) / sizeof(u32); tmp = (u32 *)qdev->mpi_coredump; QPRINTK(qdev, DRV, DEBUG, "Core is dumping to log file!\n"); @@ -1044,7 +1429,49 @@ void ql_mpi_core_to_log(struct work_struct *work) tmp[i + 5], tmp[i + 6], tmp[i + 7]); - msleep(10); + msleep(5); + } +} + +static int ql_get_core_dump(struct ql_adapter *qdev) +{ + int count = 10; + + if (!ql_own_firmware(qdev)) { + QPRINTK(qdev, DRV, ERR, "%s: Don't own firmware!\n", + qdev->ndev->name); + return -EINVAL; + } + if (!netif_running(qdev->ndev)){ + QPRINTK(qdev, IFUP, ERR, + "Force Coredump can only be done from interface " + "that is up.\n"); + return -EINVAL; + } + if(ql_mb_sys_err(qdev)) { + QPRINTK(qdev, IFUP, ERR, + "Fail force coredump with ql_mb_sys_err().\n"); + return -EINVAL; + } + + /* Wait for core dump to complete. */ + do { + ssleep(1); + QPRINTK_DBG(qdev, IFUP, ERR, + "Waiting for force Coredump to complete.\n"); + if (qdev->core_is_dumped) + break; + count--; + } while (count); + + if (!count) { + QPRINTK(qdev, IFUP, ERR, "Force Coredump timed out.\n"); + return -EINVAL; + } else { + QPRINTK(qdev, IFUP, ERR, + "Force Coredump done, starting spool to log.\n"); + set_bit(QL_SPOOL_LOG, &qdev->flags); + return 0; } } @@ -1052,15 +1479,13 @@ void ql_get_dump(struct ql_adapter *qdev, void *buff) { /* * If the dump has already been taken and is stored - * in our internal buffer, then just copy it to - * the user's buffer. Otherwise, take a snapshot - * of the general regs. + * n our internal buffer, then just start the spool to + * dump it to the log file and also, take a snapshot + * of the general regs to the user's buffer. */ - if (qdev->core_is_dumped && qdev->mpi_coredump) - memcpy(buff, qdev->mpi_coredump, - sizeof(struct ql_mpi_coredump)); - else - ql_gen_reg_dump(qdev, buff); + if (test_bit(QL_SPOOL_LOG, &qdev->flags)) + ql_get_core_dump(qdev); + ql_gen_reg_dump(qdev, buff); } #ifdef QL_REG_DUMP diff --git a/drivers/net/qlge/qlge_ethtool.c b/drivers/net/qlge/qlge_ethtool.c index fcde5cc..ebc0a4a 100644 --- a/drivers/net/qlge/qlge_ethtool.c +++ b/drivers/net/qlge/qlge_ethtool.c @@ -142,6 +142,41 @@ static void ql_update_stats(struct ql_adapter *qdev) iter++; } + /* + * Get Per-priority TX pause frame counter statistics. + */ + for (i = 0x500; i < 0x540; i += 8) { + if (ql_read_xgmac_reg64(qdev, i, &data)) { + QPRINTK(qdev, DRV, ERR, + "Error reading status register 0x%.04x.\n", i); + goto end; + } else + *iter = data; + iter++; + } + + /* + * Get Per-priority RX pause frame counter statistics. + */ + for (i = 0x568; i < 0x5a8; i += 8) { + if (ql_read_xgmac_reg64(qdev, i, &data)) { + QPRINTK(qdev, DRV, ERR, + "Error reading status register 0x%.04x.\n", i); + goto end; + } else + *iter = data; + iter++; + } + + /* + * Get RX NIC FIFO DROP statistics. + */ + if (ql_read_xgmac_reg64(qdev, 0x5b8, &data)) { + QPRINTK(qdev, DRV, ERR, + "Error reading status register 0x%.04x.\n", i); + goto end; + } else + *iter = data; end: ql_sem_unlock(qdev, qdev->xg_sem_mask); quit: @@ -195,6 +230,23 @@ static char ql_stats_str_arr[][ETH_GSTRING_LEN] = { {"rx_1024_to_1518_pkts"}, {"rx_1519_to_max_pkts"}, {"rx_len_err_pkts"}, + {"tx_cbfc_pause_frames0"}, + {"tx_cbfc_pause_frames1"}, + {"tx_cbfc_pause_frames2"}, + {"tx_cbfc_pause_frames3"}, + {"tx_cbfc_pause_frames4"}, + {"tx_cbfc_pause_frames5"}, + {"tx_cbfc_pause_frames6"}, + {"tx_cbfc_pause_frames7"}, + {"rx_cbfc_pause_frames0"}, + {"rx_cbfc_pause_frames1"}, + {"rx_cbfc_pause_frames2"}, + {"rx_cbfc_pause_frames3"}, + {"rx_cbfc_pause_frames4"}, + {"rx_cbfc_pause_frames5"}, + {"rx_cbfc_pause_frames6"}, + {"rx_cbfc_pause_frames7"}, + {"rx_nic_fifo_drop"}, }; static void ql_get_strings(struct net_device *dev, u32 stringset, u8 *buf) @@ -267,6 +319,23 @@ ql_get_ethtool_stats(struct net_device *ndev, *data++ = s->rx_1024_to_1518_pkts; *data++ = s->rx_1519_to_max_pkts; *data++ = s->rx_len_err_pkts; + *data++ = s->tx_cbfc_pause_frames0; + *data++ = s->tx_cbfc_pause_frames1; + *data++ = s->tx_cbfc_pause_frames2; + *data++ = s->tx_cbfc_pause_frames3; + *data++ = s->tx_cbfc_pause_frames4; + *data++ = s->tx_cbfc_pause_frames5; + *data++ = s->tx_cbfc_pause_frames6; + *data++ = s->tx_cbfc_pause_frames7; + *data++ = s->rx_cbfc_pause_frames0; + *data++ = s->rx_cbfc_pause_frames1; + *data++ = s->rx_cbfc_pause_frames2; + *data++ = s->rx_cbfc_pause_frames3; + *data++ = s->rx_cbfc_pause_frames4; + *data++ = s->rx_cbfc_pause_frames5; + *data++ = s->rx_cbfc_pause_frames6; + *data++ = s->rx_cbfc_pause_frames7; + *data++ = s->rx_nic_fifo_drop; } static int ql_get_settings(struct net_device *ndev, @@ -308,7 +377,7 @@ static void ql_get_drvinfo(struct net_device *ndev, strncpy(drvinfo->bus_info, pci_name(qdev->pdev), 32); drvinfo->n_stats = ARRAY_SIZE(ql_stats_str_arr); drvinfo->testinfo_len = QLGE_TEST_LEN; - drvinfo->regdump_len = sizeof(struct ql_mpi_coredump); + drvinfo->regdump_len = 1 * 512; drvinfo->eedump_len = 0; } @@ -330,32 +399,12 @@ static int ql_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) return -EINVAL; qdev->wol = wol->wolopts; - QPRINTK(qdev, DRV, DEBUG, "Set wol option 0x%x on %s\n", + QPRINTK(qdev, DRV, INFO, "Set wol option 0x%x on %s\n", qdev->wol, ndev->name); if (!qdev->wol) { u32 wol = 0; - u8 zero_mac_addr[6]; - - memset(zero_mac_addr, 0, sizeof(zero_mac_addr)); - /* Disable WOL does not work, - * so enable WOL with Invalid mac address - */ - status = ql_mb_wol_set_magic(qdev, 0); - if (status) { - QPRINTK(qdev, IFDOWN, ERR, - "Failed to clear magic packet on %s\n", - qdev->ndev->name); - return status; - } else - QPRINTK(qdev, DRV, INFO, - "Cleared magic packet successfully on %s!\n", - qdev->ndev->name); - - wol |= MB_WOL_MAGIC_PKT; - wol |= MB_WOL_MODE_ON; status = ql_mb_wol_mode(qdev, wol); - - QPRINTK(qdev, DRV, DEBUG, "WOL %s (wol code 0x%x) on %s\n", + QPRINTK(qdev, DRV, ERR, "WOL %s (wol code 0x%x) on %s\n", (status == 0) ? "cleared sucessfully" : "clear failed", wol, qdev->ndev->name); } @@ -398,8 +447,11 @@ static int ql_setup_loopback_test(struct ql_adapter *qdev) if (status) return status; qdev->link_config |= CFG_LOOPBACK_PCS; - if (netif_carrier_ok(qdev->ndev)) + if (netif_carrier_ok(qdev->ndev)) { + set_bit(QL_LINK_UP, &qdev->flags); netif_carrier_off(qdev->ndev); + } else + clear_bit(QL_LINK_UP, &qdev->flags); status = ql_mb_set_port_cfg(qdev); if (status) @@ -410,7 +462,7 @@ void ql_loopback_cleanup(struct ql_adapter *qdev) { qdev->link_config &= ~CFG_LOOPBACK_PCS; ql_mb_set_port_cfg(qdev); - if (!netif_carrier_ok(qdev->ndev)) + if (test_bit(QL_LINK_UP, &qdev->flags)) netif_carrier_on(qdev->ndev); } @@ -525,7 +577,7 @@ static void ql_self_test(struct net_device *ndev, static int ql_get_regs_len(struct net_device *ndev) { - return sizeof(struct ql_mpi_coredump); + return 1 * 512; } static void ql_get_regs(struct net_device *ndev, @@ -535,7 +587,7 @@ static void ql_get_regs(struct net_device *ndev, ql_get_dump(qdev, p); qdev->core_is_dumped = 0; - regs->len = sizeof(struct ql_mpi_coredump); + regs->len = 1 * 512; } @@ -592,6 +644,37 @@ static int ql_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *c) return ql_update_ring_coalescing(qdev); } +static void ql_get_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct ql_adapter *qdev = netdev_priv(netdev); + + ql_mb_get_port_cfg(qdev); + if (qdev->link_config & CFG_PAUSE_STD) { + pause->rx_pause = 1; + pause->tx_pause = 1; + } +} + +static int ql_set_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct ql_adapter *qdev = netdev_priv(netdev); + int status = 0; + + if ((pause->rx_pause) || (pause->tx_pause)) + qdev->link_config |= CFG_PAUSE_STD; + else if (!(pause->rx_pause && pause->tx_pause)) + qdev->link_config &= ~CFG_PAUSE_STD; + else + return -EINVAL; + + status = ql_mb_set_port_cfg(qdev); + if (status) + return status; + return status; +} + static u32 ql_get_rx_csum(struct net_device *netdev) { struct ql_adapter *qdev = netdev_priv(netdev); @@ -661,6 +744,8 @@ struct ethtool_ops qlge_ethtool_ops = { .phys_id = ql_phys_id, .self_test_count = ql_self_test_count, .self_test = ql_self_test, + .get_pauseparam = ql_get_pauseparam, + .set_pauseparam = ql_set_pauseparam, .get_rx_csum = ql_get_rx_csum, .set_rx_csum = ql_set_rx_csum, .get_tx_csum = ql_get_tx_csum, diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c index 274440f..7a5698b 100644 --- a/drivers/net/qlge/qlge_main.c +++ b/drivers/net/qlge/qlge_main.c @@ -82,6 +82,12 @@ MODULE_PARM_DESC(qlge_spool_coredump, "Option to enable spooling of firmware dump. " "to log. Default is 0 - do not spool."); +static int qlge_receive_routing = 1; +module_param(qlge_receive_routing, int, S_IRUGO|S_IRUSR); +MODULE_PARM_DESC(qlge_receive_routing, + "Option to Set/Clear receive routing bit. " + "Default is 1 - set receive routing."); + static struct pci_device_id qlge_pci_tbl[] __devinitdata = { {PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, QLGE_DEVICE_ID_8012)}, {PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, QLGE_DEVICE_ID_8000)}, @@ -271,8 +277,15 @@ void ql_link_on(struct ql_adapter *qdev) qdev->ndev->name); return; } + + /* Using the current mac address when link up */ spin_lock_irqsave(&qdev->hw_lock, hw_flags); - if (ql_set_mac_addr_reg(qdev, (u8 *) qdev->ndev->dev_addr, + QPRINTK_DBG(qdev, DRV, ERR, + "Current mac addr %02x:%02x:%02x:%02x:%02x:%02x\n", + qdev->current_mac_addr[0], qdev->current_mac_addr[1], qdev->current_mac_addr[2], + qdev->current_mac_addr[3], qdev->current_mac_addr[4], qdev->current_mac_addr[5]); + + if (ql_set_mac_addr_reg(qdev, (u8 *) qdev->current_mac_addr, MAC_ADDR_TYPE_CAM_MAC, qdev->func*MAX_CQ)) { QPRINTK(qdev, IFUP, ERR, "Failed to restore mac address.\n"); @@ -392,6 +405,37 @@ static int ql_set_mac_addr_reg(struct ql_adapter *qdev, u8 *addr, u32 type, switch (type) { case MAC_ADDR_TYPE_MULTI_MAC: + { + u32 upper = (addr[0] << 8) | addr[1]; + u32 lower = (addr[2] << 24) | (addr[3] << 16) | + (addr[4] << 8) | (addr[5]); + + status = + ql_wait_reg_rdy(qdev, + MAC_ADDR_IDX, MAC_ADDR_MW, 0); + if (status) + goto exit; + ql_write32(qdev, MAC_ADDR_IDX, (offset++) | + (index << MAC_ADDR_IDX_SHIFT) | + type | MAC_ADDR_E); + ql_write32(qdev, MAC_ADDR_DATA, lower); + status = + ql_wait_reg_rdy(qdev, + MAC_ADDR_IDX, MAC_ADDR_MW, 0); + if (status) + goto exit; + ql_write32(qdev, MAC_ADDR_IDX, (offset++) | + (index << MAC_ADDR_IDX_SHIFT) | + type | MAC_ADDR_E); + + ql_write32(qdev, MAC_ADDR_DATA, upper); + status = + ql_wait_reg_rdy(qdev, + MAC_ADDR_IDX, MAC_ADDR_MW, 0); + if (status) + goto exit; + break; + } case MAC_ADDR_TYPE_CAM_MAC: { u32 cam_output; @@ -435,15 +479,13 @@ static int ql_set_mac_addr_reg(struct ql_adapter *qdev, u8 *addr, u32 type, * and possibly the function id. Right now we hardcode * the route field to NIC core. */ - if (type == MAC_ADDR_TYPE_CAM_MAC) { - cam_output = (CAM_OUT_ROUTE_NIC | + cam_output = (CAM_OUT_ROUTE_NIC | (qdev->func << CAM_OUT_FUNC_SHIFT) | (0 << CAM_OUT_CQ_ID_SHIFT)); - if (qdev->vlgrp) - cam_output |= CAM_OUT_RV; - /* route to NIC core */ - ql_write32(qdev, MAC_ADDR_DATA, cam_output); - } + if (qdev->vlgrp) + cam_output |= CAM_OUT_RV; + /* route to NIC core */ + ql_write32(qdev, MAC_ADDR_DATA, cam_output); break; } case MAC_ADDR_TYPE_VLAN: @@ -569,7 +611,7 @@ static int ql_set_routing_reg(struct ql_adapter *qdev, u32 index, u32 mask, } case RT_IDX_MCAST: /* Pass up All Multicast frames. */ { - value = RT_IDX_DST_CAM_Q | /* dest */ + value = RT_IDX_DST_DFLT_Q | /* dest */ RT_IDX_TYPE_NICQ | /* type */ (RT_IDX_ALLMULTI_SLOT << RT_IDX_IDX_SHIFT); /* index */ @@ -577,7 +619,7 @@ static int ql_set_routing_reg(struct ql_adapter *qdev, u32 index, u32 mask, } case RT_IDX_MCAST_MATCH: /* Pass up matched Multicast frames. */ { - value = RT_IDX_DST_CAM_Q | /* dest */ + value = RT_IDX_DST_DFLT_Q | /* dest */ RT_IDX_TYPE_NICQ | /* type */ (RT_IDX_MCAST_MATCH_SLOT << RT_IDX_IDX_SHIFT); /* index */ @@ -696,6 +738,21 @@ static void ql_enable_all_completion_interrupts(struct ql_adapter *qdev) } } +/* link state work function. Delaying link up by 2 second because + * of bonding mode (tlb/alb) modifies the mac addresses. + */ +static void ql_link_work(struct work_struct *work) +{ + struct ql_adapter *qdev = + container_of(work, struct ql_adapter, link_work); + + if ((ql_read32(qdev, STS) & qdev->port_init) && + (ql_read32(qdev, STS) & qdev->port_link_up)) + ql_link_on(qdev); + + return; +} + static int ql_validate_flash(struct ql_adapter *qdev, u32 size, const char *str) { int status, i; @@ -3370,6 +3427,15 @@ static int ql_adapter_initialize(struct ql_adapter *qdev) FSC_DBL_MASK | FSC_DBRST_MASK | (value << 16); ql_write32(qdev, FSC, mask | value); + /* Set Receive Routing bit in reset/failover register to use function + * number and CQ from CAM lookup + */ + if (qlge_receive_routing) + value = RST_FO_RR_RCV_FUNC_CQ; + else + value = 0; + ql_write32(qdev, RST_FO, RST_FO_RR_MASK | value); + /* Reroute all packets to our Interface. * They may have been routed to MPI firmware * due to WOL. @@ -3382,6 +3448,11 @@ static int ql_adapter_initialize(struct ql_adapter *qdev) ql_write32(qdev, MGMT_RCV_CFG, mask); ql_write32(qdev, MGMT_RCV_CFG, mask | value); + /* Default WOL is enable on Mezz cards */ + if (qdev->pdev->subsystem_device == 0x0068 || + qdev->pdev->subsystem_device == 0x0180 ) + qdev->wol = WAKE_MAGIC; + /* Start up the rx queues. */ for (i = 0; i < qdev->rx_ring_count; i++) { status = ql_start_rx_ring(qdev, &qdev->rx_ring[i]); @@ -3444,6 +3515,11 @@ static int ql_adapter_reset(struct ql_adapter *qdev) QPRINTK(qdev, IFUP, ERR, "Failed to clear Routing tables.\n"); return status; } + /* Stop management traffic. */ + ql_mb_set_mgmnt_traffic_ctl(qdev, MB_SET_MPI_TFK_STOP); + + /* Wait for the NIC and MGMNT FIFOs to empty. */ + ql_wait_fifo_empty(qdev); ql_write32(qdev, RST_FO, (RST_FO_FR << 16) | RST_FO_FR); @@ -3460,7 +3536,8 @@ static int ql_adapter_reset(struct ql_adapter *qdev) "ETIMEOUT!!! errored out of resetting the chip!\n"); status = -ETIMEDOUT; } - + /* Resume management traffic. */ + ql_mb_set_mgmnt_traffic_ctl(qdev, MB_SET_MPI_TFK_RESUME); return status; } @@ -3487,10 +3564,12 @@ int ql_wol(struct ql_adapter *qdev) * settings. */ - if (qdev->wol & WAKE_ARP) { + if (qdev->wol & (WAKE_ARP | WAKE_MAGICSECURE | WAKE_PHY | WAKE_UCAST | + WAKE_MCAST | WAKE_BCAST)) { QPRINTK(qdev, IFDOWN, ERR, - "ARP request WOL not support yet.\n"); - return status; + "Unsupported WOL paramter. qdev->wol = 0x%x.\n", + qdev->wol); + return -EINVAL; } if (qdev->wol & WAKE_MAGIC) { @@ -3508,28 +3587,7 @@ int ql_wol(struct ql_adapter *qdev) wol |= MB_WOL_MAGIC_PKT; } - if (qdev->wol & WAKE_MAGICSECURE) { - QPRINTK(qdev, IFDOWN, ERR, - "Secure Magic packet WOL not support yet.\n"); - return status; - } - - if (qdev->wol & WAKE_PHY) - wol |= (MB_WOL_LINK_UP | MB_WOL_LINK_DOWN); - - if (qdev->wol & WAKE_UCAST) - wol |= MB_WOL_UCAST; - - if (qdev->wol & WAKE_MCAST) - wol |= MB_WOL_MCAST; - - if (qdev->wol & WAKE_BCAST) - wol |= MB_WOL_BCAST; - if (qdev->wol) { - /* Reroute all packets to Management Interface */ - ql_write32(qdev, MGMT_RCV_CFG, (MGMT_RCV_CFG_RM | - (MGMT_RCV_CFG_RM << 16))); wol |= MB_WOL_MODE_ON; status = ql_mb_wol_mode(qdev, wol); QPRINTK(qdev, DRV, ERR, "WOL %s (wol code 0x%x) on %s\n", @@ -3577,6 +3635,7 @@ static int ql_adapter_down(struct ql_adapter *qdev) cancel_delayed_work_sync(&qdev->mpi_idc_work); cancel_delayed_work_sync(&qdev->mpi_core_to_log); cancel_delayed_work_sync(&qdev->mpi_port_cfg_work); + cancel_delayed_work_sync(&qdev->link_work); for (i = 0; i < qdev->tx_ring_count; i++) del_timer_sync(&qdev->tx_ring[i].txq_clean_timer); @@ -3608,9 +3667,10 @@ static int ql_adapter_up(struct ql_adapter *qdev) ql_enable_napi(qdev); ql_alloc_rx_buffers(qdev); ql_enable_all_completion_interrupts(qdev); - if ((ql_read32(qdev, STS) & qdev->port_init) && - (ql_read32(qdev, STS) & qdev->port_link_up)) - ql_link_on(qdev); + + /* trigger link work function*/ + queue_delayed_work(qdev->workqueue, &qdev->link_work, + msecs_to_jiffies(2000)); ql_enable_interrupts(qdev); return 0; err_init: @@ -3993,12 +4053,6 @@ static int qlge_set_mac_address(struct net_device *ndev, void *p) struct sockaddr *addr = p; unsigned long hw_flags = 0; int status; - if (netif_running(ndev)) { - QPRINTK(qdev, DRV, ERR, - "Interface already active, aborting change mac address " - "request !!!\n"); - return -EBUSY; - } if (!is_valid_ether_addr(addr->sa_data)) { QPRINTK_DBG(qdev, DRV, ERR, @@ -4027,7 +4081,9 @@ static int qlge_set_mac_address(struct net_device *ndev, void *p) QPRINTK(qdev, HW, ERR, "Failed to load MAC address.\n"); ql_sem_unlock(qdev, SEM_MAC_ADDR_MASK); - + /* Saving the current mac address */ + memcpy(qdev->current_mac_addr, ndev->dev_addr, ndev->addr_len); + return status; } @@ -4266,6 +4322,8 @@ static int __devinit ql_init_device(struct pci_dev *pdev, } memcpy(ndev->perm_addr, ndev->dev_addr, ndev->addr_len); + /* Initializing current mac address */ + memcpy(qdev->current_mac_addr, ndev->dev_addr, ndev->addr_len); /* Set up the default ring sizes. */ qdev->tx_ring_size = NUM_TX_RING_ENTRIES; @@ -4289,6 +4347,7 @@ static int __devinit ql_init_device(struct pci_dev *pdev, INIT_DELAYED_WORK(&qdev->mpi_idc_work, ql_mpi_idc_work); INIT_DELAYED_WORK(&qdev->mpi_core_to_log, ql_mpi_core_to_log); INIT_DELAYED_WORK(&qdev->mpi_port_cfg_work, ql_mpi_port_cfg_work); + INIT_DELAYED_WORK(&qdev->link_work, ql_link_work); init_completion(&qdev->ide_completion); mutex_init(&qdev->mpi_mutex); @@ -4414,9 +4473,11 @@ static int __devinit qlge_probe(struct pci_dev *pdev, return err; } +#if ((LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 16)) && \ + (RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(5, 3))) /* Set EEH reset type to fundamental for this device */ pdev->fndmntl_rst_rqd = 1; - +#endif qdev = netdev_priv(ndev); SET_NETDEV_DEV(ndev, &pdev->dev); ndev->features = (0 @@ -4535,6 +4596,7 @@ static void ql_eeh_close(struct net_device *ndev) cancel_delayed_work_sync(&qdev->mpi_idc_work); cancel_delayed_work_sync(&qdev->mpi_core_to_log); cancel_delayed_work_sync(&qdev->mpi_port_cfg_work); + cancel_delayed_work_sync(&qdev->link_work); for (i = 0; i < qdev->tx_ring_count; i++) del_timer_sync(&qdev->tx_ring[i].txq_clean_timer); diff --git a/drivers/net/qlge/qlge_mpi.c b/drivers/net/qlge/qlge_mpi.c index 056bc91..ba90141 100644 --- a/drivers/net/qlge/qlge_mpi.c +++ b/drivers/net/qlge/qlge_mpi.c @@ -104,7 +104,7 @@ static int ql_soft_reset_mpi_risc(struct ql_adapter *qdev) * we are the higher function and the lower function * is not enabled. */ -static int ql_own_firmware(struct ql_adapter *qdev) +int ql_own_firmware(struct ql_adapter *qdev) { u32 temp; @@ -401,6 +401,29 @@ static void ql_init_fw_done(struct ql_adapter *qdev, struct mbox_params *mbcp) } } +/* DCBX firmware async event handler */ +static int ql_dcbx_chg(struct ql_adapter *qdev, struct mbox_params *mbcp) +{ + int status, i; + + /* Get the status data and start up a thread to + * handle the request. + */ + mbcp->out_count = 5; + status = ql_get_mb_sts(qdev, mbcp); + if (status) { + QPRINTK(qdev, DRV, ERR, + "Could not read MPI regs!\n"); + } else { + QPRINTK_DBG(qdev, DRV, INFO, + "Got DCBX ANE!\n"); + for (i = 0; i < mbcp->out_count; i++) + QPRINTK_DBG(qdev, DRV, ERR, "mbox_out[%d] = 0x%.08x.\n", + i, mbcp->mbox_out[i]); + } + return status; +} + /* Process an async event and clear it unless it's an * error condition. * This can get called iteratively from the mpi_work thread @@ -524,7 +547,7 @@ static int ql_mpi_handler(struct ql_adapter *qdev, struct mbox_params *mbcp) break; case AEN_DCBX_CHG: - /* Need to support AEN 8110 */ + ql_dcbx_chg(qdev, mbcp); break; default: QPRINTK(qdev, DRV, ERR, @@ -548,7 +571,12 @@ end: */ static int ql_mailbox_command(struct ql_adapter *qdev, struct mbox_params *mbcp) { - int status, count; + int status, i; + unsigned long count; + + for (i = 0; i < mbcp->in_count; i++) + QPRINTK_DBG(qdev, DRV, DEBUG, + "mbcp->mbox_in[%d] = 0x%x. \n", i, mbcp->mbox_in[i]); mutex_lock(&qdev->mpi_mutex); @@ -557,9 +585,13 @@ static int ql_mailbox_command(struct ql_adapter *qdev, struct mbox_params *mbcp) /* Load the mailbox registers and wake up MPI RISC. */ status = ql_exec_mb_cmd(qdev, mbcp); - if (status) + if (status) { + QPRINTK(qdev, DRV, ERR, + "Failed ql_exec_mb_cmd()," + " status = %d.\n", + status); goto end; - + } /* If we're generating a system error, then there's nothing * to wait for. @@ -570,9 +602,9 @@ static int ql_mailbox_command(struct ql_adapter *qdev, struct mbox_params *mbcp) /* Wait for the command to complete. We loop * here because some AEN might arrive while * we're waiting for the mailbox command to - * complete. If more than 5 arrive then we can - * assume something is wrong. */ - count = 5; + * complete. No mailbox command should take + * longer than 5 seconds. */ + count = jiffies + (HZ * 5); do { /* Wait for the interrupt to come in. */ status = ql_wait_mbx_cmd_cmplt(qdev); @@ -605,15 +637,16 @@ static int ql_mailbox_command(struct ql_adapter *qdev, struct mbox_params *mbcp) MB_CMD_STS_GOOD) || ((mbcp->mbox_out[0] & 0x0000f000) == MB_CMD_STS_INTRMDT)) - break; - } while (--count); + goto done; + } while (time_before(jiffies, count)); if (!count) { QPRINTK(qdev, DRV, ERR, - "Timed out waiting for mailbox complet.\n"); + "Timed out waiting for mailbox complete.\n"); status = -ETIMEDOUT; goto end; } +done: /* Now we can clear the interrupt condition * and look at our status. @@ -630,7 +663,9 @@ end: /* End polled mode for MPI */ ql_write32(qdev, INTR_MASK, (INTR_MASK_PI << 16) | INTR_MASK_PI); mutex_unlock(&qdev->mpi_mutex); - + for (i = 0; i < mbcp->out_count; i++) + QPRINTK_DBG(qdev, DRV, DEBUG, "mbox_out[%d] = 0x%.08x.\n", + i, mbcp->mbox_out[i]); return status; } @@ -824,6 +859,7 @@ int ql_mb_dump_ram(struct ql_adapter *qdev, u64 req_dma, u32 addr, return status; } +/* Issue a mailbox command to dump RISC RAM. */ int ql_dump_risc_ram_area(struct ql_adapter *qdev, void *buf, u32 ram_addr, int word_count) { @@ -1000,6 +1036,95 @@ int ql_mb_get_led_cfg(struct ql_adapter *qdev) return status; } +int ql_mb_set_mgmnt_traffic_ctl(struct ql_adapter *qdev, u32 control) +{ + struct mbox_params mbc; + struct mbox_params *mbcp = &mbc; + int status; + + memset(mbcp, 0, sizeof(struct mbox_params)); + + mbcp->in_count = 1; + mbcp->out_count = 2; + + mbcp->mbox_in[0] = MB_CMD_SET_MGMNT_TFK_CTL; + mbcp->mbox_in[1] = control; + + status = ql_mailbox_command(qdev, mbcp); + if (status) + return status; + + if (mbcp->mbox_out[0] == MB_CMD_STS_GOOD) + return status; + + if (mbcp->mbox_out[0] == MB_CMD_STS_INVLD_CMD) { + QPRINTK_DBG(qdev, DRV, ERR, + "Command not supported by firmware.\n"); + status = -EINVAL; + } else if (mbcp->mbox_out[0] == MB_CMD_STS_ERR) { + /* This indicates that the firmware is + * already in the state we are trying to + * change it to. + */ + QPRINTK_DBG(qdev, DRV, ERR, + "Command parameters make no change.\n"); + } + return status; +} + +/* Returns a negative error code or the mailbox command status. */ +static int ql_mb_get_mgmnt_traffic_ctl(struct ql_adapter *qdev, u32 *control) +{ + struct mbox_params mbc; + struct mbox_params *mbcp = &mbc; + int status; + + memset(mbcp, 0, sizeof(struct mbox_params)); + *control = 0; + + mbcp->in_count = 1; + mbcp->out_count = 1; + + mbcp->mbox_in[0] = MB_CMD_GET_MGMNT_TFK_CTL; + + status = ql_mailbox_command(qdev, mbcp); + if (status) + return status; + + if (mbcp->mbox_out[0] == MB_CMD_STS_GOOD) { + *control = mbcp->mbox_in[1]; + return status; + } + + if (mbcp->mbox_out[0] == MB_CMD_STS_INVLD_CMD) { + QPRINTK_DBG(qdev, DRV, ERR, + "Command not supported by firmware.\n"); + status = -EINVAL; + } else if (mbcp->mbox_out[0] == MB_CMD_STS_ERR) { + QPRINTK(qdev, DRV, ERR, + "Failed to get MPI traffic control.\n"); + status = -EIO; + } + return status; +} + +int ql_wait_fifo_empty(struct ql_adapter *qdev) +{ + int count = 5; + u32 mgmnt_fifo_empty; + u32 nic_fifo_empty; + + do { + nic_fifo_empty = ql_read32(qdev, STS) & STS_NFE; + ql_mb_get_mgmnt_traffic_ctl(qdev, &mgmnt_fifo_empty); + mgmnt_fifo_empty &= MB_GET_MPI_TFK_FIFO_EMPTY; + if (nic_fifo_empty && mgmnt_fifo_empty) + return 0; + msleep(100); + } while (count-- > 0); + return -ETIMEDOUT; +} + /* API called in work thread context to set new TX/RX * maximum frame size values to match MTU. */ @@ -1158,6 +1283,7 @@ void ql_mpi_reset_work(struct work_struct *work) cancel_delayed_work_sync(&qdev->mpi_port_cfg_work); cancel_delayed_work_sync(&qdev->mpi_core_to_log); cancel_delayed_work_sync(&qdev->mpi_idc_work); + cancel_delayed_work_sync(&qdev->link_work); /* If we're not the dominant NIC function, * then there is nothing to do. @@ -1177,7 +1303,7 @@ void ql_mpi_reset_work(struct work_struct *work) qdev->core_is_dumped = 1; if (test_bit(QL_SPOOL_LOG, &qdev->flags)) queue_delayed_work(qdev->workqueue, - &qdev->mpi_core_to_log, 0); + &qdev->mpi_core_to_log, 5 * HZ); } ql_soft_reset_mpi_risc(qdev); clear_bit(QL_IN_FW_RST, &qdev->flags);