From 732a4dba9f9f67b2649639e06ae228f8fa7ba6c5 Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Fri, 5 Oct 2012 03:32:32 -0700 Subject: [PATCH] cxgb4: Add support for T4 configuration file This patch adds patch series from linux-next tree to add support for T4 configuration file. Starting with T4 firmware version 1.3.11.0 the firmware now supports device configuration via a Firmware Configuration File. The Firmware Configuration File was primarily developed in order to centralize all of the configuration, resource allocation, etc. for Unified Wire operation where multiple Physical / Virtual Function Drivers would be using a T4 adapter simultaneously. The Firmware Configuration file can live in three locations as shown below in order of precedence. 1) User defined configuration file: /lib/firmware/cxgb4/t4-config.txt 2) Factory Default configuration file written to FLASH within the manufacturing process. 3) Hardwired driver configuration. Signed-off-by: Vipul Pandya --- ...xgb4-Fix-incorrect-values-for-MEMWIN.patch | 32 + ...ons-to-read-memory-via-PCIE-memory-w.patch | 280 ++++ ...de-cleanup-to-enable-T4-Configuratio.patch | 813 ++++++++++ ...dd-support-for-T4-configuration-file.patch | 1435 +++++++++++++++++ ...t-for-T4-hardwired-driver-configurat.patch | 651 ++++++++ ...ler-if-driver-didn-t-upgrade-firmwar.patch | 39 + ...mpt-to-upgrade-T4-firmware-when-cxgb.patch | 323 ++++ 7 files changed, 3573 insertions(+) create mode 100644 linux-next-cherry-picks/0002-cxgb4-Fix-incorrect-values-for-MEMWIN.patch create mode 100644 linux-next-cherry-picks/0003-cxgb4-Add-functions-to-read-memory-via-PCIE-memory-w.patch create mode 100644 linux-next-cherry-picks/0004-cxgb4-cxgb4vf-Code-cleanup-to-enable-T4-Configuratio.patch create mode 100644 linux-next-cherry-picks/0005-cxgb4-Add-support-for-T4-configuration-file.patch create mode 100644 linux-next-cherry-picks/0006-cxgb4-Add-support-for-T4-hardwired-driver-configurat.patch create mode 100644 linux-next-cherry-picks/0007-cxgb4-Inform-caller-if-driver-didn-t-upgrade-firmwar.patch create mode 100644 linux-next-cherry-picks/0008-cxgb4-Don-t-attempt-to-upgrade-T4-firmware-when-cxgb.patch diff --git a/linux-next-cherry-picks/0002-cxgb4-Fix-incorrect-values-for-MEMWIN.patch b/linux-next-cherry-picks/0002-cxgb4-Fix-incorrect-values-for-MEMWIN.patch new file mode 100644 index 0000000..1490c39 --- /dev/null +++ b/linux-next-cherry-picks/0002-cxgb4-Fix-incorrect-values-for-MEMWIN.patch @@ -0,0 +1,32 @@ +commit 3eb4afbfceb44750d9afb832e6c992adec9fc571 +Author: Vipul Pandya +Date: Wed Sep 26 02:39:36 2012 +0000 + +cxgb4: Fix incorrect values for MEMWIN*_APERTURE and MEMWIN*_BASE + +Signed-off-by: Jay Hernandez +Signed-off-by: Vipul Pandya +Signed-off-by: David S. Miller + +diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h ++++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +@@ -67,12 +67,12 @@ enum { + }; + + enum { +- MEMWIN0_APERTURE = 65536, +- MEMWIN0_BASE = 0x30000, ++ MEMWIN0_APERTURE = 2048, ++ MEMWIN0_BASE = 0x1b800, + MEMWIN1_APERTURE = 32768, + MEMWIN1_BASE = 0x28000, +- MEMWIN2_APERTURE = 2048, +- MEMWIN2_BASE = 0x1b800, ++ MEMWIN2_APERTURE = 65536, ++ MEMWIN2_BASE = 0x30000, + }; + + enum dev_master { +-- +1.7.1 diff --git a/linux-next-cherry-picks/0003-cxgb4-Add-functions-to-read-memory-via-PCIE-memory-w.patch b/linux-next-cherry-picks/0003-cxgb4-Add-functions-to-read-memory-via-PCIE-memory-w.patch new file mode 100644 index 0000000..323fd0c --- /dev/null +++ b/linux-next-cherry-picks/0003-cxgb4-Add-functions-to-read-memory-via-PCIE-memory-w.patch @@ -0,0 +1,280 @@ +From 5afc8b84eb7b29e4646d6e8ca7e6d7196031d6f7 Mon Sep 17 00:00:00 2001 +From: Vipul Pandya +Date: Wed, 26 Sep 2012 02:39:37 +0000 +Subject: [PATCH 1/6] cxgb4: Add functions to read memory via PCIE memory window + +This patch implements two new functions t4_mem_win_read and t4_memory_read. +These new functions can be used to read memory via the PCIE memory window. +Please note, for proper execution of these functions PCIE_MEM_ACCESS_BASE_WIN +registers must be setup correctly like how setup_memwin in the cxgb4 driver +does it. + +Signed-off-by: Jay Hernandez +Signed-off-by: Vipul Pandya +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 2 + + drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 137 ++++++++++++++++++++++++++++ + drivers/net/ethernet/chelsio/cxgb4/t4_hw.h | 80 ++++++++++++++++ + 3 files changed, 219 insertions(+), 0 deletions(-) + +diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +index f3fe236..7de740a 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h ++++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +@@ -664,6 +664,8 @@ int t4_wait_dev_ready(struct adapter *adap); + int t4_link_start(struct adapter *adap, unsigned int mbox, unsigned int port, + struct link_config *lc); + int t4_restart_aneg(struct adapter *adap, unsigned int mbox, unsigned int port); ++int t4_memory_write(struct adapter *adap, int mtype, u32 addr, u32 len, ++ __be32 *buf); + int t4_seeprom_wp(struct adapter *adapter, bool enable); + int t4_load_fw(struct adapter *adapter, const u8 *fw_data, unsigned int size); + int t4_check_fw_version(struct adapter *adapter); +diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +index 8e988d6..259d0dc 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c ++++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +@@ -330,6 +330,143 @@ int t4_edc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *ecc) + return 0; + } + ++/* ++ * t4_mem_win_rw - read/write memory through PCIE memory window ++ * @adap: the adapter ++ * @addr: address of first byte requested ++ * @data: MEMWIN0_APERTURE bytes of data containing the requested address ++ * @dir: direction of transfer 1 => read, 0 => write ++ * ++ * Read/write MEMWIN0_APERTURE bytes of data from MC starting at a ++ * MEMWIN0_APERTURE-byte-aligned address that covers the requested ++ * address @addr. ++ */ ++static int t4_mem_win_rw(struct adapter *adap, u32 addr, __be32 *data, int dir) ++{ ++ int i; ++ ++ /* ++ * Setup offset into PCIE memory window. Address must be a ++ * MEMWIN0_APERTURE-byte-aligned address. (Read back MA register to ++ * ensure that changes propagate before we attempt to use the new ++ * values.) ++ */ ++ t4_write_reg(adap, PCIE_MEM_ACCESS_OFFSET, ++ addr & ~(MEMWIN0_APERTURE - 1)); ++ t4_read_reg(adap, PCIE_MEM_ACCESS_OFFSET); ++ ++ /* Collecting data 4 bytes at a time upto MEMWIN0_APERTURE */ ++ for (i = 0; i < MEMWIN0_APERTURE; i = i+0x4) { ++ if (dir) ++ *data++ = t4_read_reg(adap, (MEMWIN0_BASE + i)); ++ else ++ t4_write_reg(adap, (MEMWIN0_BASE + i), *data++); ++ } ++ ++ return 0; ++} ++ ++/** ++ * t4_memory_rw - read/write EDC 0, EDC 1 or MC via PCIE memory window ++ * @adap: the adapter ++ * @mtype: memory type: MEM_EDC0, MEM_EDC1 or MEM_MC ++ * @addr: address within indicated memory type ++ * @len: amount of memory to transfer ++ * @buf: host memory buffer ++ * @dir: direction of transfer 1 => read, 0 => write ++ * ++ * Reads/writes an [almost] arbitrary memory region in the firmware: the ++ * firmware memory address, length and host buffer must be aligned on ++ * 32-bit boudaries. The memory is transferred as a raw byte sequence ++ * from/to the firmware's memory. If this memory contains data ++ * structures which contain multi-byte integers, it's the callers ++ * responsibility to perform appropriate byte order conversions. ++ */ ++static int t4_memory_rw(struct adapter *adap, int mtype, u32 addr, u32 len, ++ __be32 *buf, int dir) ++{ ++ u32 pos, start, end, offset, memoffset; ++ int ret; ++ ++ /* ++ * Argument sanity checks ... ++ */ ++ if ((addr & 0x3) || (len & 0x3)) ++ return -EINVAL; ++ ++ /* ++ * Offset into the region of memory which is being accessed ++ * MEM_EDC0 = 0 ++ * MEM_EDC1 = 1 ++ * MEM_MC = 2 ++ */ ++ memoffset = (mtype * (5 * 1024 * 1024)); ++ ++ /* Determine the PCIE_MEM_ACCESS_OFFSET */ ++ addr = addr + memoffset; ++ ++ /* ++ * The underlaying EDC/MC read routines read MEMWIN0_APERTURE bytes ++ * at a time so we need to round down the start and round up the end. ++ * We'll start copying out of the first line at (addr - start) a word ++ * at a time. ++ */ ++ start = addr & ~(MEMWIN0_APERTURE-1); ++ end = (addr + len + MEMWIN0_APERTURE-1) & ~(MEMWIN0_APERTURE-1); ++ offset = (addr - start)/sizeof(__be32); ++ ++ for (pos = start; pos < end; pos += MEMWIN0_APERTURE, offset = 0) { ++ __be32 data[MEMWIN0_APERTURE/sizeof(__be32)]; ++ ++ /* ++ * If we're writing, copy the data from the caller's memory ++ * buffer ++ */ ++ if (!dir) { ++ /* ++ * If we're doing a partial write, then we need to do ++ * a read-modify-write ... ++ */ ++ if (offset || len < MEMWIN0_APERTURE) { ++ ret = t4_mem_win_rw(adap, pos, data, 1); ++ if (ret) ++ return ret; ++ } ++ while (offset < (MEMWIN0_APERTURE/sizeof(__be32)) && ++ len > 0) { ++ data[offset++] = *buf++; ++ len -= sizeof(__be32); ++ } ++ } ++ ++ /* ++ * Transfer a block of memory and bail if there's an error. ++ */ ++ ret = t4_mem_win_rw(adap, pos, data, dir); ++ if (ret) ++ return ret; ++ ++ /* ++ * If we're reading, copy the data into the caller's memory ++ * buffer. ++ */ ++ if (dir) ++ while (offset < (MEMWIN0_APERTURE/sizeof(__be32)) && ++ len > 0) { ++ *buf++ = data[offset++]; ++ len -= sizeof(__be32); ++ } ++ } ++ ++ return 0; ++} ++ ++int t4_memory_write(struct adapter *adap, int mtype, u32 addr, u32 len, ++ __be32 *buf) ++{ ++ return t4_memory_rw(adap, mtype, addr, len, buf, 0); ++} ++ + #define EEPROM_STAT_ADDR 0x7bfc + #define VPD_BASE 0 + #define VPD_LEN 512 +diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h +index c26b455..f534ed7 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h ++++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h +@@ -58,6 +58,7 @@ enum { + + enum { + SF_PAGE_SIZE = 256, /* serial flash page size */ ++ SF_SEC_SIZE = 64 * 1024, /* serial flash sector size */ + }; + + enum { RSP_TYPE_FLBUF, RSP_TYPE_CPL, RSP_TYPE_INTR }; /* response entry types */ +@@ -137,4 +138,83 @@ struct rsp_ctrl { + #define QINTR_CNT_EN 0x1 + #define QINTR_TIMER_IDX(x) ((x) << 1) + #define QINTR_TIMER_IDX_GET(x) (((x) >> 1) & 0x7) ++ ++/* ++ * Flash layout. ++ */ ++#define FLASH_START(start) ((start) * SF_SEC_SIZE) ++#define FLASH_MAX_SIZE(nsecs) ((nsecs) * SF_SEC_SIZE) ++ ++enum { ++ /* ++ * Various Expansion-ROM boot images, etc. ++ */ ++ FLASH_EXP_ROM_START_SEC = 0, ++ FLASH_EXP_ROM_NSECS = 6, ++ FLASH_EXP_ROM_START = FLASH_START(FLASH_EXP_ROM_START_SEC), ++ FLASH_EXP_ROM_MAX_SIZE = FLASH_MAX_SIZE(FLASH_EXP_ROM_NSECS), ++ ++ /* ++ * iSCSI Boot Firmware Table (iBFT) and other driver-related ++ * parameters ... ++ */ ++ FLASH_IBFT_START_SEC = 6, ++ FLASH_IBFT_NSECS = 1, ++ FLASH_IBFT_START = FLASH_START(FLASH_IBFT_START_SEC), ++ FLASH_IBFT_MAX_SIZE = FLASH_MAX_SIZE(FLASH_IBFT_NSECS), ++ ++ /* ++ * Boot configuration data. ++ */ ++ FLASH_BOOTCFG_START_SEC = 7, ++ FLASH_BOOTCFG_NSECS = 1, ++ FLASH_BOOTCFG_START = FLASH_START(FLASH_BOOTCFG_START_SEC), ++ FLASH_BOOTCFG_MAX_SIZE = FLASH_MAX_SIZE(FLASH_BOOTCFG_NSECS), ++ ++ /* ++ * Location of firmware image in FLASH. ++ */ ++ FLASH_FW_START_SEC = 8, ++ FLASH_FW_NSECS = 8, ++ FLASH_FW_START = FLASH_START(FLASH_FW_START_SEC), ++ FLASH_FW_MAX_SIZE = FLASH_MAX_SIZE(FLASH_FW_NSECS), ++ ++ /* ++ * iSCSI persistent/crash information. ++ */ ++ FLASH_ISCSI_CRASH_START_SEC = 29, ++ FLASH_ISCSI_CRASH_NSECS = 1, ++ FLASH_ISCSI_CRASH_START = FLASH_START(FLASH_ISCSI_CRASH_START_SEC), ++ FLASH_ISCSI_CRASH_MAX_SIZE = FLASH_MAX_SIZE(FLASH_ISCSI_CRASH_NSECS), ++ ++ /* ++ * FCoE persistent/crash information. ++ */ ++ FLASH_FCOE_CRASH_START_SEC = 30, ++ FLASH_FCOE_CRASH_NSECS = 1, ++ FLASH_FCOE_CRASH_START = FLASH_START(FLASH_FCOE_CRASH_START_SEC), ++ FLASH_FCOE_CRASH_MAX_SIZE = FLASH_MAX_SIZE(FLASH_FCOE_CRASH_NSECS), ++ ++ /* ++ * Location of Firmware Configuration File in FLASH. Since the FPGA ++ * "FLASH" is smaller we need to store the Configuration File in a ++ * different location -- which will overlap the end of the firmware ++ * image if firmware ever gets that large ... ++ */ ++ FLASH_CFG_START_SEC = 31, ++ FLASH_CFG_NSECS = 1, ++ FLASH_CFG_START = FLASH_START(FLASH_CFG_START_SEC), ++ FLASH_CFG_MAX_SIZE = FLASH_MAX_SIZE(FLASH_CFG_NSECS), ++ ++ FLASH_FPGA_CFG_START_SEC = 15, ++ FLASH_FPGA_CFG_START = FLASH_START(FLASH_FPGA_CFG_START_SEC), ++ ++ /* ++ * Sectors 32-63 are reserved for FLASH failover. ++ */ ++}; ++ ++#undef FLASH_START ++#undef FLASH_MAX_SIZE ++ + #endif /* __T4_HW_H */ +-- +1.7.1 + diff --git a/linux-next-cherry-picks/0004-cxgb4-cxgb4vf-Code-cleanup-to-enable-T4-Configuratio.patch b/linux-next-cherry-picks/0004-cxgb4-cxgb4vf-Code-cleanup-to-enable-T4-Configuratio.patch new file mode 100644 index 0000000..e9a42f3 --- /dev/null +++ b/linux-next-cherry-picks/0004-cxgb4-cxgb4vf-Code-cleanup-to-enable-T4-Configuratio.patch @@ -0,0 +1,813 @@ +From 52367a763d8046190754ab43743e42638564a2d1 Mon Sep 17 00:00:00 2001 +From: Vipul Pandya +Date: Wed, 26 Sep 2012 02:39:38 +0000 +Subject: [PATCH 2/6] cxgb4/cxgb4vf: Code cleanup to enable T4 Configuration File support + +This patch adds new enums and macros to enable T4 configuration file support. It +also removes duplicate macro definitions. + +It fixes the build failure in cxgb4vf driver introduced because of old macro +definition removal. + +It also performs SGE initialization based on T4 configuration file is provided +or not. If it is provided then it uses the parameters provided in it otherwise +it uses hard coded values. + +Signed-off-by: Jay Hernandez +Signed-off-by: Vipul Pandya +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 18 ++- + drivers/net/ethernet/chelsio/cxgb4/sge.c | 337 ++++++++++++++++++++----- + drivers/net/ethernet/chelsio/cxgb4/t4_regs.h | 35 +++- + drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h | 41 +++- + drivers/net/ethernet/chelsio/cxgb4vf/sge.c | 5 +- + 5 files changed, 365 insertions(+), 71 deletions(-) + +diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +index 7de740a..ae040cf 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h ++++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +@@ -315,6 +315,9 @@ enum { /* adapter flags */ + USING_MSI = (1 << 1), + USING_MSIX = (1 << 2), + FW_OK = (1 << 4), ++ USING_SOFT_PARAMS = (1 << 6), ++ MASTER_PF = (1 << 7), ++ FW_OFLD_CONN = (1 << 9), + }; + + struct rx_sw_desc; +@@ -467,6 +470,11 @@ struct sge { + u16 rdma_rxq[NCHAN]; + u16 timer_val[SGE_NTIMERS]; + u8 counter_val[SGE_NCOUNTERS]; ++ u32 fl_pg_order; /* large page allocation size */ ++ u32 stat_len; /* length of status page at ring end */ ++ u32 pktshift; /* padding between CPL & packet data */ ++ u32 fl_align; /* response queue message alignment */ ++ u32 fl_starve_thres; /* Free List starvation threshold */ + unsigned int starve_thres; + u8 idma_state[2]; + unsigned int egr_start; +@@ -619,7 +627,7 @@ int t4_sge_alloc_ctrl_txq(struct adapter *adap, struct sge_ctrl_txq *txq, + int t4_sge_alloc_ofld_txq(struct adapter *adap, struct sge_ofld_txq *txq, + struct net_device *dev, unsigned int iqid); + irqreturn_t t4_sge_intr_msix(int irq, void *cookie); +-void t4_sge_init(struct adapter *adap); ++int t4_sge_init(struct adapter *adap); + void t4_sge_start(struct adapter *adap); + void t4_sge_stop(struct adapter *adap); + extern int dbfifo_int_thresh; +@@ -638,6 +646,14 @@ static inline unsigned int us_to_core_ticks(const struct adapter *adap, + return (us * adap->params.vpd.cclk) / 1000; + } + ++static inline unsigned int core_ticks_to_us(const struct adapter *adapter, ++ unsigned int ticks) ++{ ++ /* add Core Clock / 2 to round ticks to nearest uS */ ++ return ((ticks * 1000 + adapter->params.vpd.cclk/2) / ++ adapter->params.vpd.cclk); ++} ++ + void t4_set_reg_field(struct adapter *adap, unsigned int addr, u32 mask, + u32 val); + +diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c +index 1fde57d..3ecc087 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/sge.c ++++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c +@@ -68,9 +68,6 @@ + */ + #define RX_PKT_SKB_LEN 512 + +-/* Ethernet header padding prepended to RX_PKTs */ +-#define RX_PKT_PAD 2 +- + /* + * Max number of Tx descriptors we clean up at a time. Should be modest as + * freeing skbs isn't cheap and it happens while holding locks. We just need +@@ -137,13 +134,6 @@ + */ + #define MAX_CTRL_WR_LEN SGE_MAX_WR_LEN + +-enum { +- /* packet alignment in FL buffers */ +- FL_ALIGN = L1_CACHE_BYTES < 32 ? 32 : L1_CACHE_BYTES, +- /* egress status entry size */ +- STAT_LEN = L1_CACHE_BYTES > 64 ? 128 : 64 +-}; +- + struct tx_sw_desc { /* SW state per Tx descriptor */ + struct sk_buff *skb; + struct ulptx_sgl *sgl; +@@ -155,16 +145,57 @@ struct rx_sw_desc { /* SW state per Rx descriptor */ + }; + + /* +- * The low bits of rx_sw_desc.dma_addr have special meaning. ++ * Rx buffer sizes for "useskbs" Free List buffers (one ingress packet pe skb ++ * buffer). We currently only support two sizes for 1500- and 9000-byte MTUs. ++ * We could easily support more but there doesn't seem to be much need for ++ * that ... ++ */ ++#define FL_MTU_SMALL 1500 ++#define FL_MTU_LARGE 9000 ++ ++static inline unsigned int fl_mtu_bufsize(struct adapter *adapter, ++ unsigned int mtu) ++{ ++ struct sge *s = &adapter->sge; ++ ++ return ALIGN(s->pktshift + ETH_HLEN + VLAN_HLEN + mtu, s->fl_align); ++} ++ ++#define FL_MTU_SMALL_BUFSIZE(adapter) fl_mtu_bufsize(adapter, FL_MTU_SMALL) ++#define FL_MTU_LARGE_BUFSIZE(adapter) fl_mtu_bufsize(adapter, FL_MTU_LARGE) ++ ++/* ++ * Bits 0..3 of rx_sw_desc.dma_addr have special meaning. The hardware uses ++ * these to specify the buffer size as an index into the SGE Free List Buffer ++ * Size register array. We also use bit 4, when the buffer has been unmapped ++ * for DMA, but this is of course never sent to the hardware and is only used ++ * to prevent double unmappings. All of the above requires that the Free List ++ * Buffers which we allocate have the bottom 5 bits free (0) -- i.e. are ++ * 32-byte or or a power of 2 greater in alignment. Since the SGE's minimal ++ * Free List Buffer alignment is 32 bytes, this works out for us ... + */ + enum { +- RX_LARGE_BUF = 1 << 0, /* buffer is larger than PAGE_SIZE */ +- RX_UNMAPPED_BUF = 1 << 1, /* buffer is not mapped */ ++ RX_BUF_FLAGS = 0x1f, /* bottom five bits are special */ ++ RX_BUF_SIZE = 0x0f, /* bottom three bits are for buf sizes */ ++ RX_UNMAPPED_BUF = 0x10, /* buffer is not mapped */ ++ ++ /* ++ * XXX We shouldn't depend on being able to use these indices. ++ * XXX Especially when some other Master PF has initialized the ++ * XXX adapter or we use the Firmware Configuration File. We ++ * XXX should really search through the Host Buffer Size register ++ * XXX array for the appropriately sized buffer indices. ++ */ ++ RX_SMALL_PG_BUF = 0x0, /* small (PAGE_SIZE) page buffer */ ++ RX_LARGE_PG_BUF = 0x1, /* buffer large (FL_PG_ORDER) page buffer */ ++ ++ RX_SMALL_MTU_BUF = 0x2, /* small MTU buffer */ ++ RX_LARGE_MTU_BUF = 0x3, /* large MTU buffer */ + }; + + static inline dma_addr_t get_buf_addr(const struct rx_sw_desc *d) + { +- return d->dma_addr & ~(dma_addr_t)(RX_LARGE_BUF | RX_UNMAPPED_BUF); ++ return d->dma_addr & ~(dma_addr_t)RX_BUF_FLAGS; + } + + static inline bool is_buf_mapped(const struct rx_sw_desc *d) +@@ -392,14 +423,35 @@ static inline void reclaim_completed_tx(struct adapter *adap, struct sge_txq *q, + } + } + +-static inline int get_buf_size(const struct rx_sw_desc *d) ++static inline int get_buf_size(struct adapter *adapter, ++ const struct rx_sw_desc *d) + { +-#if FL_PG_ORDER > 0 +- return (d->dma_addr & RX_LARGE_BUF) ? (PAGE_SIZE << FL_PG_ORDER) : +- PAGE_SIZE; +-#else +- return PAGE_SIZE; +-#endif ++ struct sge *s = &adapter->sge; ++ unsigned int rx_buf_size_idx = d->dma_addr & RX_BUF_SIZE; ++ int buf_size; ++ ++ switch (rx_buf_size_idx) { ++ case RX_SMALL_PG_BUF: ++ buf_size = PAGE_SIZE; ++ break; ++ ++ case RX_LARGE_PG_BUF: ++ buf_size = PAGE_SIZE << s->fl_pg_order; ++ break; ++ ++ case RX_SMALL_MTU_BUF: ++ buf_size = FL_MTU_SMALL_BUFSIZE(adapter); ++ break; ++ ++ case RX_LARGE_MTU_BUF: ++ buf_size = FL_MTU_LARGE_BUFSIZE(adapter); ++ break; ++ ++ default: ++ BUG_ON(1); ++ } ++ ++ return buf_size; + } + + /** +@@ -418,7 +470,8 @@ static void free_rx_bufs(struct adapter *adap, struct sge_fl *q, int n) + + if (is_buf_mapped(d)) + dma_unmap_page(adap->pdev_dev, get_buf_addr(d), +- get_buf_size(d), PCI_DMA_FROMDEVICE); ++ get_buf_size(adap, d), ++ PCI_DMA_FROMDEVICE); + put_page(d->page); + d->page = NULL; + if (++q->cidx == q->size) +@@ -444,7 +497,7 @@ static void unmap_rx_buf(struct adapter *adap, struct sge_fl *q) + + if (is_buf_mapped(d)) + dma_unmap_page(adap->pdev_dev, get_buf_addr(d), +- get_buf_size(d), PCI_DMA_FROMDEVICE); ++ get_buf_size(adap, d), PCI_DMA_FROMDEVICE); + d->page = NULL; + if (++q->cidx == q->size) + q->cidx = 0; +@@ -485,6 +538,7 @@ static inline void set_rx_sw_desc(struct rx_sw_desc *sd, struct page *pg, + static unsigned int refill_fl(struct adapter *adap, struct sge_fl *q, int n, + gfp_t gfp) + { ++ struct sge *s = &adap->sge; + struct page *pg; + dma_addr_t mapping; + unsigned int cred = q->avail; +@@ -493,25 +547,27 @@ static unsigned int refill_fl(struct adapter *adap, struct sge_fl *q, int n, + + gfp |= __GFP_NOWARN | __GFP_COLD; + +-#if FL_PG_ORDER > 0 ++ if (s->fl_pg_order == 0) ++ goto alloc_small_pages; ++ + /* + * Prefer large buffers + */ + while (n) { +- pg = alloc_pages(gfp | __GFP_COMP, FL_PG_ORDER); ++ pg = alloc_pages(gfp | __GFP_COMP, s->fl_pg_order); + if (unlikely(!pg)) { + q->large_alloc_failed++; + break; /* fall back to single pages */ + } + + mapping = dma_map_page(adap->pdev_dev, pg, 0, +- PAGE_SIZE << FL_PG_ORDER, ++ PAGE_SIZE << s->fl_pg_order, + PCI_DMA_FROMDEVICE); + if (unlikely(dma_mapping_error(adap->pdev_dev, mapping))) { +- __free_pages(pg, FL_PG_ORDER); ++ __free_pages(pg, s->fl_pg_order); + goto out; /* do not try small pages for this error */ + } +- mapping |= RX_LARGE_BUF; ++ mapping |= RX_LARGE_PG_BUF; + *d++ = cpu_to_be64(mapping); + + set_rx_sw_desc(sd, pg, mapping); +@@ -525,8 +581,8 @@ static unsigned int refill_fl(struct adapter *adap, struct sge_fl *q, int n, + } + n--; + } +-#endif + ++alloc_small_pages: + while (n--) { + pg = __skb_alloc_page(gfp, NULL); + if (unlikely(!pg)) { +@@ -1519,6 +1575,8 @@ static noinline int handle_trace_pkt(struct adapter *adap, + static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl, + const struct cpl_rx_pkt *pkt) + { ++ struct adapter *adapter = rxq->rspq.adap; ++ struct sge *s = &adapter->sge; + int ret; + struct sk_buff *skb; + +@@ -1529,11 +1587,11 @@ static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl, + } + + #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +- copy_frags(skb, gl, RX_PKT_PAD); ++ copy_frags(skb, gl, s->pktshift); + #else + copy_frags(skb_shinfo(skb), gl, RX_PKT_PAD); + #endif +- skb->len = gl->tot_len - RX_PKT_PAD; ++ skb->len = gl->tot_len - s->pktshift; + skb->data_len = skb->len; + skb->truesize += skb->data_len; + skb->ip_summed = CHECKSUM_UNNECESSARY; +@@ -1566,6 +1624,7 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, + struct sk_buff *skb; + const struct cpl_rx_pkt *pkt; + struct sge_eth_rxq *rxq = container_of(q, struct sge_eth_rxq, rspq); ++ struct sge *s = &q->adap->sge; + + if (unlikely(*(u8 *)rsp == CPL_TRACE_PKT)) + return handle_trace_pkt(q->adap, si); +@@ -1585,7 +1644,7 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, + return 0; + } + +- __skb_pull(skb, RX_PKT_PAD); /* remove ethernet header padding */ ++ __skb_pull(skb, s->pktshift); /* remove ethernet header padding */ + skb->protocol = eth_type_trans(skb, q->netdev); + skb_record_rx_queue(skb, q->idx); + if (skb->dev->features & NETIF_F_RXHASH) +@@ -1696,6 +1755,8 @@ static int process_responses(struct sge_rspq *q, int budget) + int budget_left = budget; + const struct rsp_ctrl *rc; + struct sge_eth_rxq *rxq = container_of(q, struct sge_eth_rxq, rspq); ++ struct adapter *adapter = q->adap; ++ struct sge *s = &adapter->sge; + + while (likely(budget_left)) { + rc = (void *)q->cur_desc + (q->iqe_len - sizeof(*rc)); +@@ -1722,7 +1783,7 @@ static int process_responses(struct sge_rspq *q, int budget) + /* gather packet fragments */ + for (frags = 0, fp = si.frags; ; frags++, fp++) { + rsd = &rxq->fl.sdesc[rxq->fl.cidx]; +- bufsz = get_buf_size(rsd); ++ bufsz = get_buf_size(adapter, rsd); + fp->page = rsd->page; + fp->offset = q->offset; + fp->size = min(bufsz, len); +@@ -1747,7 +1808,7 @@ static int process_responses(struct sge_rspq *q, int budget) + ret = q->handler(q, q->cur_desc, &si); + if (likely(ret == 0)) + #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +- q->offset += ALIGN(fp->size, FL_ALIGN); ++ q->offset += ALIGN(fp->size, s->fl_align); + #else + q->offset += ALIGN(skb_frag_size(fp), FL_ALIGN); + #endif +@@ -1983,6 +2044,7 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq, + { + int ret, flsz = 0; + struct fw_iq_cmd c; ++ struct sge *s = &adap->sge; + struct port_info *pi = netdev_priv(dev); + + /* Size needs to be multiple of 16, including status entry. */ +@@ -2015,11 +2077,11 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq, + fl->size = roundup(fl->size, 8); + fl->desc = alloc_ring(adap->pdev_dev, fl->size, sizeof(__be64), + sizeof(struct rx_sw_desc), &fl->addr, +- &fl->sdesc, STAT_LEN, NUMA_NO_NODE); ++ &fl->sdesc, s->stat_len, NUMA_NO_NODE); + if (!fl->desc) + goto fl_nomem; + +- flsz = fl->size / 8 + STAT_LEN / sizeof(struct tx_desc); ++ flsz = fl->size / 8 + s->stat_len / sizeof(struct tx_desc); + c.iqns_to_fl0congen = htonl(FW_IQ_CMD_FL0PACKEN | + FW_IQ_CMD_FL0FETCHRO(1) | + FW_IQ_CMD_FL0DATARO(1) | +@@ -2096,14 +2158,15 @@ int t4_sge_alloc_eth_txq(struct adapter *adap, struct sge_eth_txq *txq, + { + int ret, nentries; + struct fw_eq_eth_cmd c; ++ struct sge *s = &adap->sge; + struct port_info *pi = netdev_priv(dev); + + /* Add status entries */ +- nentries = txq->q.size + STAT_LEN / sizeof(struct tx_desc); ++ nentries = txq->q.size + s->stat_len / sizeof(struct tx_desc); + + txq->q.desc = alloc_ring(adap->pdev_dev, txq->q.size, + sizeof(struct tx_desc), sizeof(struct tx_sw_desc), +- &txq->q.phys_addr, &txq->q.sdesc, STAT_LEN, ++ &txq->q.phys_addr, &txq->q.sdesc, s->stat_len, + netdev_queue_numa_node_read(netdevq)); + if (!txq->q.desc) + return -ENOMEM; +@@ -2149,10 +2212,11 @@ int t4_sge_alloc_ctrl_txq(struct adapter *adap, struct sge_ctrl_txq *txq, + { + int ret, nentries; + struct fw_eq_ctrl_cmd c; ++ struct sge *s = &adap->sge; + struct port_info *pi = netdev_priv(dev); + + /* Add status entries */ +- nentries = txq->q.size + STAT_LEN / sizeof(struct tx_desc); ++ nentries = txq->q.size + s->stat_len / sizeof(struct tx_desc); + + txq->q.desc = alloc_ring(adap->pdev_dev, nentries, + sizeof(struct tx_desc), 0, &txq->q.phys_addr, +@@ -2200,14 +2264,15 @@ int t4_sge_alloc_ofld_txq(struct adapter *adap, struct sge_ofld_txq *txq, + { + int ret, nentries; + struct fw_eq_ofld_cmd c; ++ struct sge *s = &adap->sge; + struct port_info *pi = netdev_priv(dev); + + /* Add status entries */ +- nentries = txq->q.size + STAT_LEN / sizeof(struct tx_desc); ++ nentries = txq->q.size + s->stat_len / sizeof(struct tx_desc); + + txq->q.desc = alloc_ring(adap->pdev_dev, txq->q.size, + sizeof(struct tx_desc), sizeof(struct tx_sw_desc), +- &txq->q.phys_addr, &txq->q.sdesc, STAT_LEN, ++ &txq->q.phys_addr, &txq->q.sdesc, s->stat_len, + NUMA_NO_NODE); + if (!txq->q.desc) + return -ENOMEM; +@@ -2251,8 +2316,10 @@ int t4_sge_alloc_ofld_txq(struct adapter *adap, struct sge_ofld_txq *txq, + + static void free_txq(struct adapter *adap, struct sge_txq *q) + { ++ struct sge *s = &adap->sge; ++ + dma_free_coherent(adap->pdev_dev, +- q->size * sizeof(struct tx_desc) + STAT_LEN, ++ q->size * sizeof(struct tx_desc) + s->stat_len, + q->desc, q->phys_addr); + q->cntxt_id = 0; + q->sdesc = NULL; +@@ -2262,6 +2329,7 @@ static void free_txq(struct adapter *adap, struct sge_txq *q) + static void free_rspq_fl(struct adapter *adap, struct sge_rspq *rq, + struct sge_fl *fl) + { ++ struct sge *s = &adap->sge; + unsigned int fl_id = fl ? fl->cntxt_id : 0xffff; + + adap->sge.ingr_map[rq->cntxt_id - adap->sge.ingr_start] = NULL; +@@ -2276,7 +2344,7 @@ static void free_rspq_fl(struct adapter *adap, struct sge_rspq *rq, + + if (fl) { + free_rx_bufs(adap, fl, fl->avail); +- dma_free_coherent(adap->pdev_dev, fl->size * 8 + STAT_LEN, ++ dma_free_coherent(adap->pdev_dev, fl->size * 8 + s->stat_len, + fl->desc, fl->addr); + kfree(fl->sdesc); + fl->sdesc = NULL; +@@ -2408,18 +2476,112 @@ void t4_sge_stop(struct adapter *adap) + * Performs SGE initialization needed every time after a chip reset. + * We do not initialize any of the queues here, instead the driver + * top-level must request them individually. ++ * ++ * Called in two different modes: ++ * ++ * 1. Perform actual hardware initialization and record hard-coded ++ * parameters which were used. This gets used when we're the ++ * Master PF and the Firmware Configuration File support didn't ++ * work for some reason. ++ * ++ * 2. We're not the Master PF or initialization was performed with ++ * a Firmware Configuration File. In this case we need to grab ++ * any of the SGE operating parameters that we need to have in ++ * order to do our job and make sure we can live with them ... + */ +-void t4_sge_init(struct adapter *adap) ++ ++static int t4_sge_init_soft(struct adapter *adap) + { +- unsigned int i, v; + struct sge *s = &adap->sge; +- unsigned int fl_align_log = ilog2(FL_ALIGN); ++ u32 fl_small_pg, fl_large_pg, fl_small_mtu, fl_large_mtu; ++ u32 timer_value_0_and_1, timer_value_2_and_3, timer_value_4_and_5; ++ u32 ingress_rx_threshold; + +- t4_set_reg_field(adap, SGE_CONTROL, PKTSHIFT_MASK | +- INGPADBOUNDARY_MASK | EGRSTATUSPAGESIZE, +- INGPADBOUNDARY(fl_align_log - 5) | PKTSHIFT(2) | +- RXPKTCPLMODE | +- (STAT_LEN == 128 ? EGRSTATUSPAGESIZE : 0)); ++ /* ++ * Verify that CPL messages are going to the Ingress Queue for ++ * process_responses() and that only packet data is going to the ++ * Free Lists. ++ */ ++ if ((t4_read_reg(adap, SGE_CONTROL) & RXPKTCPLMODE_MASK) != ++ RXPKTCPLMODE(X_RXPKTCPLMODE_SPLIT)) { ++ dev_err(adap->pdev_dev, "bad SGE CPL MODE\n"); ++ return -EINVAL; ++ } ++ ++ /* ++ * Validate the Host Buffer Register Array indices that we want to ++ * use ... ++ * ++ * XXX Note that we should really read through the Host Buffer Size ++ * XXX register array and find the indices of the Buffer Sizes which ++ * XXX meet our needs! ++ */ ++ #define READ_FL_BUF(x) \ ++ t4_read_reg(adap, SGE_FL_BUFFER_SIZE0+(x)*sizeof(u32)) ++ ++ fl_small_pg = READ_FL_BUF(RX_SMALL_PG_BUF); ++ fl_large_pg = READ_FL_BUF(RX_LARGE_PG_BUF); ++ fl_small_mtu = READ_FL_BUF(RX_SMALL_MTU_BUF); ++ fl_large_mtu = READ_FL_BUF(RX_LARGE_MTU_BUF); ++ ++ #undef READ_FL_BUF ++ ++ if (fl_small_pg != PAGE_SIZE || ++ (fl_large_pg != 0 && (fl_large_pg <= fl_small_pg || ++ (fl_large_pg & (fl_large_pg-1)) != 0))) { ++ dev_err(adap->pdev_dev, "bad SGE FL page buffer sizes [%d, %d]\n", ++ fl_small_pg, fl_large_pg); ++ return -EINVAL; ++ } ++ if (fl_large_pg) ++ s->fl_pg_order = ilog2(fl_large_pg) - PAGE_SHIFT; ++ ++ if (fl_small_mtu < FL_MTU_SMALL_BUFSIZE(adap) || ++ fl_large_mtu < FL_MTU_LARGE_BUFSIZE(adap)) { ++ dev_err(adap->pdev_dev, "bad SGE FL MTU sizes [%d, %d]\n", ++ fl_small_mtu, fl_large_mtu); ++ return -EINVAL; ++ } ++ ++ /* ++ * Retrieve our RX interrupt holdoff timer values and counter ++ * threshold values from the SGE parameters. ++ */ ++ timer_value_0_and_1 = t4_read_reg(adap, SGE_TIMER_VALUE_0_AND_1); ++ timer_value_2_and_3 = t4_read_reg(adap, SGE_TIMER_VALUE_2_AND_3); ++ timer_value_4_and_5 = t4_read_reg(adap, SGE_TIMER_VALUE_4_AND_5); ++ s->timer_val[0] = core_ticks_to_us(adap, ++ TIMERVALUE0_GET(timer_value_0_and_1)); ++ s->timer_val[1] = core_ticks_to_us(adap, ++ TIMERVALUE1_GET(timer_value_0_and_1)); ++ s->timer_val[2] = core_ticks_to_us(adap, ++ TIMERVALUE2_GET(timer_value_2_and_3)); ++ s->timer_val[3] = core_ticks_to_us(adap, ++ TIMERVALUE3_GET(timer_value_2_and_3)); ++ s->timer_val[4] = core_ticks_to_us(adap, ++ TIMERVALUE4_GET(timer_value_4_and_5)); ++ s->timer_val[5] = core_ticks_to_us(adap, ++ TIMERVALUE5_GET(timer_value_4_and_5)); ++ ++ ingress_rx_threshold = t4_read_reg(adap, SGE_INGRESS_RX_THRESHOLD); ++ s->counter_val[0] = THRESHOLD_0_GET(ingress_rx_threshold); ++ s->counter_val[1] = THRESHOLD_1_GET(ingress_rx_threshold); ++ s->counter_val[2] = THRESHOLD_2_GET(ingress_rx_threshold); ++ s->counter_val[3] = THRESHOLD_3_GET(ingress_rx_threshold); ++ ++ return 0; ++} ++ ++static int t4_sge_init_hard(struct adapter *adap) ++{ ++ struct sge *s = &adap->sge; ++ ++ /* ++ * Set up our basic SGE mode to deliver CPL messages to our Ingress ++ * Queue and Packet Date to the Free List. ++ */ ++ t4_set_reg_field(adap, SGE_CONTROL, RXPKTCPLMODE_MASK, ++ RXPKTCPLMODE_MASK); + + /* + * Set up to drop DOORBELL writes when the DOORBELL FIFO overflows +@@ -2433,13 +2595,24 @@ void t4_sge_init(struct adapter *adap) + t4_set_reg_field(adap, A_SGE_DOORBELL_CONTROL, F_ENABLE_DROP, + F_ENABLE_DROP); + +- for (i = v = 0; i < 32; i += 4) +- v |= (PAGE_SHIFT - 10) << i; +- t4_write_reg(adap, SGE_HOST_PAGE_SIZE, v); +- t4_write_reg(adap, SGE_FL_BUFFER_SIZE0, PAGE_SIZE); +-#if FL_PG_ORDER > 0 +- t4_write_reg(adap, SGE_FL_BUFFER_SIZE1, PAGE_SIZE << FL_PG_ORDER); +-#endif ++ /* ++ * SGE_FL_BUFFER_SIZE0 (RX_SMALL_PG_BUF) is set up by ++ * t4_fixup_host_params(). ++ */ ++ s->fl_pg_order = FL_PG_ORDER; ++ if (s->fl_pg_order) ++ t4_write_reg(adap, ++ SGE_FL_BUFFER_SIZE0+RX_LARGE_PG_BUF*sizeof(u32), ++ PAGE_SIZE << FL_PG_ORDER); ++ t4_write_reg(adap, SGE_FL_BUFFER_SIZE0+RX_SMALL_MTU_BUF*sizeof(u32), ++ FL_MTU_SMALL_BUFSIZE(adap)); ++ t4_write_reg(adap, SGE_FL_BUFFER_SIZE0+RX_LARGE_MTU_BUF*sizeof(u32), ++ FL_MTU_LARGE_BUFSIZE(adap)); ++ ++ /* ++ * Note that the SGE Ingress Packet Count Interrupt Threshold and ++ * Timer Holdoff values must be supplied by our caller. ++ */ + t4_write_reg(adap, SGE_INGRESS_RX_THRESHOLD, + THRESHOLD_0(s->counter_val[0]) | + THRESHOLD_1(s->counter_val[1]) | +@@ -2449,14 +2622,54 @@ void t4_sge_init(struct adapter *adap) + TIMERVALUE0(us_to_core_ticks(adap, s->timer_val[0])) | + TIMERVALUE1(us_to_core_ticks(adap, s->timer_val[1]))); + t4_write_reg(adap, SGE_TIMER_VALUE_2_AND_3, +- TIMERVALUE0(us_to_core_ticks(adap, s->timer_val[2])) | +- TIMERVALUE1(us_to_core_ticks(adap, s->timer_val[3]))); ++ TIMERVALUE2(us_to_core_ticks(adap, s->timer_val[2])) | ++ TIMERVALUE3(us_to_core_ticks(adap, s->timer_val[3]))); + t4_write_reg(adap, SGE_TIMER_VALUE_4_AND_5, +- TIMERVALUE0(us_to_core_ticks(adap, s->timer_val[4])) | +- TIMERVALUE1(us_to_core_ticks(adap, s->timer_val[5]))); ++ TIMERVALUE4(us_to_core_ticks(adap, s->timer_val[4])) | ++ TIMERVALUE5(us_to_core_ticks(adap, s->timer_val[5]))); ++ ++ return 0; ++} ++ ++int t4_sge_init(struct adapter *adap) ++{ ++ struct sge *s = &adap->sge; ++ u32 sge_control; ++ int ret; ++ ++ /* ++ * Ingress Padding Boundary and Egress Status Page Size are set up by ++ * t4_fixup_host_params(). ++ */ ++ sge_control = t4_read_reg(adap, SGE_CONTROL); ++ s->pktshift = PKTSHIFT_GET(sge_control); ++ s->stat_len = (sge_control & EGRSTATUSPAGESIZE_MASK) ? 128 : 64; ++ s->fl_align = 1 << (INGPADBOUNDARY_GET(sge_control) + ++ X_INGPADBOUNDARY_SHIFT); ++ ++ if (adap->flags & USING_SOFT_PARAMS) ++ ret = t4_sge_init_soft(adap); ++ else ++ ret = t4_sge_init_hard(adap); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * A FL with <= fl_starve_thres buffers is starving and a periodic ++ * timer will attempt to refill it. This needs to be larger than the ++ * SGE's Egress Congestion Threshold. If it isn't, then we can get ++ * stuck waiting for new packets while the SGE is waiting for us to ++ * give it more Free List entries. (Note that the SGE's Egress ++ * Congestion Threshold is in units of 2 Free List pointers.) ++ */ ++ s->fl_starve_thres ++ = EGRTHRESHOLD_GET(t4_read_reg(adap, SGE_CONM_CTRL))*2 + 1; ++ + setup_timer(&s->rx_timer, sge_rx_timer_cb, (unsigned long)adap); + setup_timer(&s->tx_timer, sge_tx_timer_cb, (unsigned long)adap); + s->starve_thres = core_ticks_per_usec(adap) * 1000000; /* 1 s */ + s->idma_state[0] = s->idma_state[1] = 0; + spin_lock_init(&s->intrq_lock); ++ ++ return 0; + } +diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +index 8e814bc..2767ca6 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h ++++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +@@ -86,10 +86,17 @@ + #define CIDXINC_SHIFT 0 + #define CIDXINC(x) ((x) << CIDXINC_SHIFT) + ++#define X_RXPKTCPLMODE_SPLIT 1 ++#define X_INGPADBOUNDARY_SHIFT 5 ++ + #define SGE_CONTROL 0x1008 + #define DCASYSTYPE 0x00080000U +-#define RXPKTCPLMODE 0x00040000U +-#define EGRSTATUSPAGESIZE 0x00020000U ++#define RXPKTCPLMODE_MASK 0x00040000U ++#define RXPKTCPLMODE_SHIFT 18 ++#define RXPKTCPLMODE(x) ((x) << RXPKTCPLMODE_SHIFT) ++#define EGRSTATUSPAGESIZE_MASK 0x00020000U ++#define EGRSTATUSPAGESIZE_SHIFT 17 ++#define EGRSTATUSPAGESIZE(x) ((x) << EGRSTATUSPAGESIZE_SHIFT) + #define PKTSHIFT_MASK 0x00001c00U + #define PKTSHIFT_SHIFT 10 + #define PKTSHIFT(x) ((x) << PKTSHIFT_SHIFT) +@@ -173,6 +180,12 @@ + #define THRESHOLD_3(x) ((x) << THRESHOLD_3_SHIFT) + #define THRESHOLD_3_GET(x) (((x) & THRESHOLD_3_MASK) >> THRESHOLD_3_SHIFT) + ++#define SGE_CONM_CTRL 0x1094 ++#define EGRTHRESHOLD_MASK 0x00003f00U ++#define EGRTHRESHOLDshift 8 ++#define EGRTHRESHOLD(x) ((x) << EGRTHRESHOLDshift) ++#define EGRTHRESHOLD_GET(x) (((x) & EGRTHRESHOLD_MASK) >> EGRTHRESHOLDshift) ++ + #define SGE_TIMER_VALUE_0_AND_1 0x10b8 + #define TIMERVALUE0_MASK 0xffff0000U + #define TIMERVALUE0_SHIFT 16 +@@ -184,7 +197,25 @@ + #define TIMERVALUE1_GET(x) (((x) & TIMERVALUE1_MASK) >> TIMERVALUE1_SHIFT) + + #define SGE_TIMER_VALUE_2_AND_3 0x10bc ++#define TIMERVALUE2_MASK 0xffff0000U ++#define TIMERVALUE2_SHIFT 16 ++#define TIMERVALUE2(x) ((x) << TIMERVALUE2_SHIFT) ++#define TIMERVALUE2_GET(x) (((x) & TIMERVALUE2_MASK) >> TIMERVALUE2_SHIFT) ++#define TIMERVALUE3_MASK 0x0000ffffU ++#define TIMERVALUE3_SHIFT 0 ++#define TIMERVALUE3(x) ((x) << TIMERVALUE3_SHIFT) ++#define TIMERVALUE3_GET(x) (((x) & TIMERVALUE3_MASK) >> TIMERVALUE3_SHIFT) ++ + #define SGE_TIMER_VALUE_4_AND_5 0x10c0 ++#define TIMERVALUE4_MASK 0xffff0000U ++#define TIMERVALUE4_SHIFT 16 ++#define TIMERVALUE4(x) ((x) << TIMERVALUE4_SHIFT) ++#define TIMERVALUE4_GET(x) (((x) & TIMERVALUE4_MASK) >> TIMERVALUE4_SHIFT) ++#define TIMERVALUE5_MASK 0x0000ffffU ++#define TIMERVALUE5_SHIFT 0 ++#define TIMERVALUE5(x) ((x) << TIMERVALUE5_SHIFT) ++#define TIMERVALUE5_GET(x) (((x) & TIMERVALUE5_MASK) >> TIMERVALUE5_SHIFT) ++ + #define SGE_DEBUG_INDEX 0x10cc + #define SGE_DEBUG_DATA_HIGH 0x10d0 + #define SGE_DEBUG_DATA_LOW 0x10d4 +diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +index ad53f79..94e3484 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h ++++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +@@ -401,6 +401,14 @@ enum fw_caps_config_fcoe { + FW_CAPS_CONFIG_FCOE_TARGET = 0x00000002, + }; + ++enum fw_memtype_cf { ++ FW_MEMTYPE_CF_EDC0 = 0x0, ++ FW_MEMTYPE_CF_EDC1 = 0x1, ++ FW_MEMTYPE_CF_EXTMEM = 0x2, ++ FW_MEMTYPE_CF_FLASH = 0x4, ++ FW_MEMTYPE_CF_INTERNAL = 0x5, ++}; ++ + struct fw_caps_config_cmd { + __be32 op_to_write; + __be32 retval_len16; +@@ -416,10 +424,15 @@ struct fw_caps_config_cmd { + __be16 r4; + __be16 iscsicaps; + __be16 fcoecaps; +- __be32 r5; +- __be64 r6; ++ __be32 cfcsum; ++ __be32 finiver; ++ __be32 finicsum; + }; + ++#define FW_CAPS_CONFIG_CMD_CFVALID (1U << 27) ++#define FW_CAPS_CONFIG_CMD_MEMTYPE_CF(x) ((x) << 24) ++#define FW_CAPS_CONFIG_CMD_MEMADDR64K_CF(x) ((x) << 16) ++ + /* + * params command mnemonics + */ +@@ -451,6 +464,7 @@ enum fw_params_param_dev { + FW_PARAMS_PARAM_DEV_INTVER_FCOE = 0x0A, + FW_PARAMS_PARAM_DEV_FWREV = 0x0B, + FW_PARAMS_PARAM_DEV_TPREV = 0x0C, ++ FW_PARAMS_PARAM_DEV_CF = 0x0D, + }; + + /* +@@ -492,6 +506,8 @@ enum fw_params_param_pfvf { + FW_PARAMS_PARAM_PFVF_IQFLINT_END = 0x2A, + FW_PARAMS_PARAM_PFVF_EQ_START = 0x2B, + FW_PARAMS_PARAM_PFVF_EQ_END = 0x2C, ++ FW_PARAMS_PARAM_PFVF_ACTIVE_FILTER_START = 0x2D, ++ FW_PARAMS_PARAM_PFVF_ACTIVE_FILTER_END = 0x2E + }; + + /* +@@ -507,8 +523,16 @@ enum fw_params_param_dmaq { + + #define FW_PARAMS_MNEM(x) ((x) << 24) + #define FW_PARAMS_PARAM_X(x) ((x) << 16) +-#define FW_PARAMS_PARAM_Y(x) ((x) << 8) +-#define FW_PARAMS_PARAM_Z(x) ((x) << 0) ++#define FW_PARAMS_PARAM_Y_SHIFT 8 ++#define FW_PARAMS_PARAM_Y_MASK 0xffU ++#define FW_PARAMS_PARAM_Y(x) ((x) << FW_PARAMS_PARAM_Y_SHIFT) ++#define FW_PARAMS_PARAM_Y_GET(x) (((x) >> FW_PARAMS_PARAM_Y_SHIFT) &\ ++ FW_PARAMS_PARAM_Y_MASK) ++#define FW_PARAMS_PARAM_Z_SHIFT 0 ++#define FW_PARAMS_PARAM_Z_MASK 0xffu ++#define FW_PARAMS_PARAM_Z(x) ((x) << FW_PARAMS_PARAM_Z_SHIFT) ++#define FW_PARAMS_PARAM_Z_GET(x) (((x) >> FW_PARAMS_PARAM_Z_SHIFT) &\ ++ FW_PARAMS_PARAM_Z_MASK) + #define FW_PARAMS_PARAM_XYZ(x) ((x) << 0) + #define FW_PARAMS_PARAM_YZ(x) ((x) << 0) + +@@ -1599,6 +1623,15 @@ struct fw_debug_cmd { + } u; + }; + ++#define FW_PCIE_FW_ERR (1U << 31) ++#define FW_PCIE_FW_INIT (1U << 30) ++#define FW_PCIE_FW_MASTER_VLD (1U << 15) ++#define FW_PCIE_FW_MASTER_MASK 0x7 ++#define FW_PCIE_FW_MASTER_SHIFT 12 ++#define FW_PCIE_FW_MASTER(x) ((x) << FW_PCIE_FW_MASTER_SHIFT) ++#define FW_PCIE_FW_MASTER_GET(x) (((x) >> FW_PCIE_FW_MASTER_SHIFT) & \ ++ FW_PCIE_FW_MASTER_MASK) ++ + struct fw_hdr { + u8 ver; + u8 reserved1; +-- +1.7.1 + diff --git a/linux-next-cherry-picks/0005-cxgb4-Add-support-for-T4-configuration-file.patch b/linux-next-cherry-picks/0005-cxgb4-Add-support-for-T4-configuration-file.patch new file mode 100644 index 0000000..9f94811 --- /dev/null +++ b/linux-next-cherry-picks/0005-cxgb4-Add-support-for-T4-configuration-file.patch @@ -0,0 +1,1435 @@ +From 636f9d371f70f22961fd598fe18380057518ca31 Mon Sep 17 00:00:00 2001 +From: Vipul Pandya +Date: Wed, 26 Sep 2012 02:39:39 +0000 +Subject: [PATCH 3/6] cxgb4: Add support for T4 configuration file + +Starting with T4 firmware version 1.3.11.0 the firmware now supports device +configuration via a Firmware Configuration File. The Firmware Configuration +File was primarily developed in order to centralize all of the configuration, +resource allocation, etc. for Unified Wire operation where multiple +Physical / Virtual Function Drivers would be using a T4 adapter simultaneously. + +The Firmware Configuration file can live in three locations as shown below +in order of precedence. +1) User defined configuration file: /lib/firmware/cxgb4/t4-config.txt +2) Factory Default configuration file written to FLASH within + the manufacturing process. +3) Hardwired driver configuration. + +Signed-off-by: Jay Hernandez +Signed-off-by: Vipul Pandya +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 15 + + drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 570 +++++++++++++++++++---- + drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h | 2 + + drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 392 +++++++++++++++- + drivers/net/ethernet/chelsio/cxgb4/t4_regs.h | 37 ++- + drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h | 37 +- + 6 files changed, 920 insertions(+), 133 deletions(-) + +diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +index ae040cf..777cbb4 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h ++++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +@@ -211,6 +211,9 @@ struct tp_err_stats { + struct tp_params { + unsigned int ntxchan; /* # of Tx channels */ + unsigned int tre; /* log2 of core clocks per TP tick */ ++ ++ uint32_t dack_re; /* DACK timer resolution */ ++ unsigned short tx_modq[NCHAN]; /* channel to modulation queue map */ + }; + + struct vpd_params { +@@ -519,6 +522,8 @@ struct adapter { + struct net_device *port[MAX_NPORTS]; + u8 chan_map[NCHAN]; /* channel -> port map */ + ++ unsigned int l2t_start; ++ unsigned int l2t_end; + struct l2t_data *l2t; + void *uld_handle[CXGB4_ULD_MAX]; + struct list_head list_node; +@@ -683,7 +688,9 @@ int t4_restart_aneg(struct adapter *adap, unsigned int mbox, unsigned int port); + int t4_memory_write(struct adapter *adap, int mtype, u32 addr, u32 len, + __be32 *buf); + int t4_seeprom_wp(struct adapter *adapter, bool enable); ++int get_vpd_params(struct adapter *adapter, struct vpd_params *p); + int t4_load_fw(struct adapter *adapter, const u8 *fw_data, unsigned int size); ++unsigned int t4_flash_cfg_addr(struct adapter *adapter); + int t4_check_fw_version(struct adapter *adapter); + int t4_prep_adapter(struct adapter *adapter); + int t4_port_init(struct adapter *adap, int mbox, int pf, int vf); +@@ -698,6 +705,8 @@ int t4_edc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, + + void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p); + void t4_read_mtu_tbl(struct adapter *adap, u16 *mtus, u8 *mtu_log); ++void t4_tp_wr_bits_indirect(struct adapter *adap, unsigned int addr, ++ unsigned int mask, unsigned int val); + void t4_tp_get_tcp_stats(struct adapter *adap, struct tp_tcp_stats *v4, + struct tp_tcp_stats *v6); + void t4_load_mtus(struct adapter *adap, const unsigned short *mtus, +@@ -713,6 +722,12 @@ int t4_fw_hello(struct adapter *adap, unsigned int mbox, unsigned int evt_mbox, + int t4_fw_bye(struct adapter *adap, unsigned int mbox); + int t4_early_init(struct adapter *adap, unsigned int mbox); + int t4_fw_reset(struct adapter *adap, unsigned int mbox, int reset); ++int t4_fw_config_file(struct adapter *adap, unsigned int mbox, ++ unsigned int mtype, unsigned int maddr, ++ u32 *finiver, u32 *finicsum, u32 *cfcsum); ++int t4_fixup_host_params(struct adapter *adap, unsigned int page_size, ++ unsigned int cache_line_size); ++int t4_fw_initialize(struct adapter *adap, unsigned int mbox); + int t4_query_params(struct adapter *adap, unsigned int mbox, unsigned int pf, + unsigned int vf, unsigned int nparams, const u32 *params, + u32 *val); +diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +index 34d510d..cb3e663 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c ++++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +@@ -193,6 +193,7 @@ static DEFINE_PCI_DEVICE_TABLE(cxgb4_pci_tbl) = { + }; + + #define FW_FNAME "cxgb4/t4fw.bin" ++#define FW_CFNAME "cxgb4/t4-config.txt" + + MODULE_DESCRIPTION(DRV_DESC); + MODULE_AUTHOR("Chelsio Communications"); +@@ -201,6 +202,17 @@ MODULE_VERSION(DRV_VERSION); + MODULE_DEVICE_TABLE(pci, cxgb4_pci_tbl); + MODULE_FIRMWARE(FW_FNAME); + ++/* ++ * Normally we're willing to become the firmware's Master PF but will be happy ++ * if another PF has already become the Master and initialized the adapter. ++ * Setting "force_init" will cause this driver to forcibly establish itself as ++ * the Master PF and initialize the adapter. ++ */ ++static uint force_init; ++ ++module_param(force_init, uint, 0644); ++MODULE_PARM_DESC(force_init, "Forcibly become Master PF and initialize adapter"); ++ + static int dflt_msg_enable = DFLT_MSG_ENABLE; + + module_param(dflt_msg_enable, int, 0644); +@@ -236,6 +248,20 @@ module_param_array(intr_cnt, uint, NULL, 0644); + MODULE_PARM_DESC(intr_cnt, + "thresholds 1..3 for queue interrupt packet counters"); + ++/* ++ * Normally we tell the chip to deliver Ingress Packets into our DMA buffers ++ * offset by 2 bytes in order to have the IP headers line up on 4-byte ++ * boundaries. This is a requirement for many architectures which will throw ++ * a machine check fault if an attempt is made to access one of the 4-byte IP ++ * header fields on a non-4-byte boundary. And it's a major performance issue ++ * even on some architectures which allow it like some implementations of the ++ * x86 ISA. However, some architectures don't mind this and for some very ++ * edge-case performance sensitive applications (like forwarding large volumes ++ * of small packets), setting this DMA offset to 0 will decrease the number of ++ * PCI-E Bus transfers enough to measurably affect performance. ++ */ ++static int rx_dma_offset = 2; ++ + static bool vf_acls; + + #ifdef CONFIG_PCI_IOV +@@ -3076,6 +3102,10 @@ static void setup_memwin(struct adapter *adap) + t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 2), + (bar0 + MEMWIN2_BASE) | BIR(0) | + WINDOW(ilog2(MEMWIN2_APERTURE) - 10)); ++} ++ ++static void setup_memwin_rdma(struct adapter *adap) ++{ + if (adap->vres.ocq.size) { + unsigned int start, sz_kb; + +@@ -3155,6 +3185,232 @@ static int adap_init1(struct adapter *adap, struct fw_caps_config_cmd *c) + + /* + * Phase 0 of initialization: contact FW, obtain config, perform basic init. ++ * ++ * If the firmware we're dealing with has Configuration File support, then ++ * we use that to perform all configuration ++ */ ++ ++/* ++ * Tweak configuration based on module parameters, etc. Most of these have ++ * defaults assigned to them by Firmware Configuration Files (if we're using ++ * them) but need to be explicitly set if we're using hard-coded ++ * initialization. But even in the case of using Firmware Configuration ++ * Files, we'd like to expose the ability to change these via module ++ * parameters so these are essentially common tweaks/settings for ++ * Configuration Files and hard-coded initialization ... ++ */ ++static int adap_init0_tweaks(struct adapter *adapter) ++{ ++ /* ++ * Fix up various Host-Dependent Parameters like Page Size, Cache ++ * Line Size, etc. The firmware default is for a 4KB Page Size and ++ * 64B Cache Line Size ... ++ */ ++ t4_fixup_host_params(adapter, PAGE_SIZE, L1_CACHE_BYTES); ++ ++ /* ++ * Process module parameters which affect early initialization. ++ */ ++ if (rx_dma_offset != 2 && rx_dma_offset != 0) { ++ dev_err(&adapter->pdev->dev, ++ "Ignoring illegal rx_dma_offset=%d, using 2\n", ++ rx_dma_offset); ++ rx_dma_offset = 2; ++ } ++ t4_set_reg_field(adapter, SGE_CONTROL, ++ PKTSHIFT_MASK, ++ PKTSHIFT(rx_dma_offset)); ++ ++ /* ++ * Don't include the "IP Pseudo Header" in CPL_RX_PKT checksums: Linux ++ * adds the pseudo header itself. ++ */ ++ t4_tp_wr_bits_indirect(adapter, TP_INGRESS_CONFIG, ++ CSUM_HAS_PSEUDO_HDR, 0); ++ ++ return 0; ++} ++ ++/* ++ * Attempt to initialize the adapter via a Firmware Configuration File. ++ */ ++static int adap_init0_config(struct adapter *adapter, int reset) ++{ ++ struct fw_caps_config_cmd caps_cmd; ++ const struct firmware *cf; ++ unsigned long mtype = 0, maddr = 0; ++ u32 finiver, finicsum, cfcsum; ++ int ret, using_flash; ++ ++ /* ++ * Reset device if necessary. ++ */ ++ if (reset) { ++ ret = t4_fw_reset(adapter, adapter->mbox, ++ PIORSTMODE | PIORST); ++ if (ret < 0) ++ goto bye; ++ } ++ ++ /* ++ * If we have a T4 configuration file under /lib/firmware/cxgb4/, ++ * then use that. Otherwise, use the configuration file stored ++ * in the adapter flash ... ++ */ ++ ret = request_firmware(&cf, FW_CFNAME, adapter->pdev_dev); ++ if (ret < 0) { ++ using_flash = 1; ++ mtype = FW_MEMTYPE_CF_FLASH; ++ maddr = t4_flash_cfg_addr(adapter); ++ } else { ++ u32 params[7], val[7]; ++ ++ using_flash = 0; ++ if (cf->size >= FLASH_CFG_MAX_SIZE) ++ ret = -ENOMEM; ++ else { ++ params[0] = (FW_PARAMS_MNEM(FW_PARAMS_MNEM_DEV) | ++ FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DEV_CF)); ++ ret = t4_query_params(adapter, adapter->mbox, ++ adapter->fn, 0, 1, params, val); ++ if (ret == 0) { ++ /* ++ * For t4_memory_write() below addresses and ++ * sizes have to be in terms of multiples of 4 ++ * bytes. So, if the Configuration File isn't ++ * a multiple of 4 bytes in length we'll have ++ * to write that out separately since we can't ++ * guarantee that the bytes following the ++ * residual byte in the buffer returned by ++ * request_firmware() are zeroed out ... ++ */ ++ size_t resid = cf->size & 0x3; ++ size_t size = cf->size & ~0x3; ++ __be32 *data = (__be32 *)cf->data; ++ ++ mtype = FW_PARAMS_PARAM_Y_GET(val[0]); ++ maddr = FW_PARAMS_PARAM_Z_GET(val[0]) << 16; ++ ++ ret = t4_memory_write(adapter, mtype, maddr, ++ size, data); ++ if (ret == 0 && resid != 0) { ++ union { ++ __be32 word; ++ char buf[4]; ++ } last; ++ int i; ++ ++ last.word = data[size >> 2]; ++ for (i = resid; i < 4; i++) ++ last.buf[i] = 0; ++ ret = t4_memory_write(adapter, mtype, ++ maddr + size, ++ 4, &last.word); ++ } ++ } ++ } ++ ++ release_firmware(cf); ++ if (ret) ++ goto bye; ++ } ++ ++ /* ++ * Issue a Capability Configuration command to the firmware to get it ++ * to parse the Configuration File. We don't use t4_fw_config_file() ++ * because we want the ability to modify various features after we've ++ * processed the configuration file ... ++ */ ++ memset(&caps_cmd, 0, sizeof(caps_cmd)); ++ caps_cmd.op_to_write = ++ htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) | ++ FW_CMD_REQUEST | ++ FW_CMD_READ); ++ caps_cmd.retval_len16 = ++ htonl(FW_CAPS_CONFIG_CMD_CFVALID | ++ FW_CAPS_CONFIG_CMD_MEMTYPE_CF(mtype) | ++ FW_CAPS_CONFIG_CMD_MEMADDR64K_CF(maddr >> 16) | ++ FW_LEN16(caps_cmd)); ++ ret = t4_wr_mbox(adapter, adapter->mbox, &caps_cmd, sizeof(caps_cmd), ++ &caps_cmd); ++ if (ret < 0) ++ goto bye; ++ ++ finiver = ntohl(caps_cmd.finiver); ++ finicsum = ntohl(caps_cmd.finicsum); ++ cfcsum = ntohl(caps_cmd.cfcsum); ++ if (finicsum != cfcsum) ++ dev_warn(adapter->pdev_dev, "Configuration File checksum "\ ++ "mismatch: [fini] csum=%#x, computed csum=%#x\n", ++ finicsum, cfcsum); ++ ++ /* ++ * If we're a pure NIC driver then disable all offloading facilities. ++ * This will allow the firmware to optimize aspects of the hardware ++ * configuration which will result in improved performance. ++ */ ++ caps_cmd.ofldcaps = 0; ++ caps_cmd.iscsicaps = 0; ++ caps_cmd.rdmacaps = 0; ++ caps_cmd.fcoecaps = 0; ++ ++ /* ++ * And now tell the firmware to use the configuration we just loaded. ++ */ ++ caps_cmd.op_to_write = ++ htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) | ++ FW_CMD_REQUEST | ++ FW_CMD_WRITE); ++ caps_cmd.retval_len16 = htonl(FW_LEN16(caps_cmd)); ++ ret = t4_wr_mbox(adapter, adapter->mbox, &caps_cmd, sizeof(caps_cmd), ++ NULL); ++ if (ret < 0) ++ goto bye; ++ ++ /* ++ * Tweak configuration based on system architecture, module ++ * parameters, etc. ++ */ ++ ret = adap_init0_tweaks(adapter); ++ if (ret < 0) ++ goto bye; ++ ++ /* ++ * And finally tell the firmware to initialize itself using the ++ * parameters from the Configuration File. ++ */ ++ ret = t4_fw_initialize(adapter, adapter->mbox); ++ if (ret < 0) ++ goto bye; ++ ++ /* ++ * Return successfully and note that we're operating with parameters ++ * not supplied by the driver, rather than from hard-wired ++ * initialization constants burried in the driver. ++ */ ++ adapter->flags |= USING_SOFT_PARAMS; ++ dev_info(adapter->pdev_dev, "Successfully configured using Firmware "\ ++ "Configuration File %s, version %#x, computed checksum %#x\n", ++ (using_flash ++ ? "in device FLASH" ++ : "/lib/firmware/" FW_CFNAME), ++ finiver, cfcsum); ++ return 0; ++ ++ /* ++ * Something bad happened. Return the error ... (If the "error" ++ * is that there's no Configuration File on the adapter we don't ++ * want to issue a warning since this is fairly common.) ++ */ ++bye: ++ if (ret != -ENOENT) ++ dev_warn(adapter->pdev_dev, "Configuration file error %d\n", ++ -ret); ++ return ret; ++} ++ ++/* ++ * Phase 0 of initialization: contact FW, obtain config, perform basic init. + */ + static int adap_init0(struct adapter *adap) + { +@@ -3162,72 +3418,197 @@ static int adap_init0(struct adapter *adap) + u32 v, port_vec; + enum dev_state state; + u32 params[7], val[7]; +- struct fw_caps_config_cmd c; ++ int reset = 1, j; + +- ret = t4_check_fw_version(adap); +- if (ret == -EINVAL || ret > 0) { +- if (upgrade_fw(adap) >= 0) /* recache FW version */ +- ret = t4_check_fw_version(adap); +- } +- if (ret < 0) +- return ret; +- +- /* contact FW, request master */ +- ret = t4_fw_hello(adap, adap->fn, adap->fn, MASTER_MUST, &state); ++ /* ++ * Contact FW, advertising Master capability (and potentially forcing ++ * ourselves as the Master PF if our module parameter force_init is ++ * set). ++ */ ++ ret = t4_fw_hello(adap, adap->mbox, adap->fn, ++ force_init ? MASTER_MUST : MASTER_MAY, ++ &state); + if (ret < 0) { + dev_err(adap->pdev_dev, "could not connect to FW, error %d\n", + ret); + return ret; + } ++ if (ret == adap->mbox) ++ adap->flags |= MASTER_PF; ++ if (force_init && state == DEV_STATE_INIT) ++ state = DEV_STATE_UNINIT; + +- /* reset device */ +- ret = t4_fw_reset(adap, adap->fn, PIORSTMODE | PIORST); +- if (ret < 0) +- goto bye; +- +- for (v = 0; v < SGE_NTIMERS - 1; v++) +- adap->sge.timer_val[v] = min(intr_holdoff[v], MAX_SGE_TIMERVAL); +- adap->sge.timer_val[SGE_NTIMERS - 1] = MAX_SGE_TIMERVAL; +- adap->sge.counter_val[0] = 1; +- for (v = 1; v < SGE_NCOUNTERS; v++) +- adap->sge.counter_val[v] = min(intr_cnt[v - 1], +- THRESHOLD_3_MASK); +-#define FW_PARAM_DEV(param) \ +- (FW_PARAMS_MNEM(FW_PARAMS_MNEM_DEV) | \ +- FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DEV_##param)) ++ /* ++ * If we're the Master PF Driver and the device is uninitialized, ++ * then let's consider upgrading the firmware ... (We always want ++ * to check the firmware version number in order to A. get it for ++ * later reporting and B. to warn if the currently loaded firmware ++ * is excessively mismatched relative to the driver.) ++ */ ++ ret = t4_check_fw_version(adap); ++ if ((adap->flags & MASTER_PF) && state != DEV_STATE_INIT) { ++ if (ret == -EINVAL || ret > 0) { ++ if (upgrade_fw(adap) >= 0) { ++ /* ++ * Note that the chip was reset as part of the ++ * firmware upgrade so we don't reset it again ++ * below and grab the new firmware version. ++ */ ++ reset = 0; ++ ret = t4_check_fw_version(adap); ++ } ++ } ++ if (ret < 0) ++ return ret; ++ } + +- params[0] = FW_PARAM_DEV(CCLK); +- ret = t4_query_params(adap, adap->fn, adap->fn, 0, 1, params, val); ++ /* ++ * Grab VPD parameters. This should be done after we establish a ++ * connection to the firmware since some of the VPD parameters ++ * (notably the Core Clock frequency) are retrieved via requests to ++ * the firmware. On the other hand, we need these fairly early on ++ * so we do this right after getting ahold of the firmware. ++ */ ++ ret = get_vpd_params(adap, &adap->params.vpd); + if (ret < 0) + goto bye; +- adap->params.vpd.cclk = val[0]; + +- ret = adap_init1(adap, &c); ++ /* ++ * Find out what ports are available to us. ++ */ ++ v = ++ FW_PARAMS_MNEM(FW_PARAMS_MNEM_DEV) | ++ FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DEV_PORTVEC); ++ ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 1, &v, &port_vec); + if (ret < 0) + goto bye; + ++ adap->params.nports = hweight32(port_vec); ++ adap->params.portvec = port_vec; ++ ++ /* ++ * If the firmware is initialized already (and we're not forcing a ++ * master initialization), note that we're living with existing ++ * adapter parameters. Otherwise, it's time to try initializing the ++ * adapter ... ++ */ ++ if (state == DEV_STATE_INIT) { ++ dev_info(adap->pdev_dev, "Coming up as %s: "\ ++ "Adapter already initialized\n", ++ adap->flags & MASTER_PF ? "MASTER" : "SLAVE"); ++ adap->flags |= USING_SOFT_PARAMS; ++ } else { ++ dev_info(adap->pdev_dev, "Coming up as MASTER: "\ ++ "Initializing adapter\n"); ++ /* ++ * Find out whether we're dealing with a version of ++ * the firmware which has configuration file support. ++ */ ++ params[0] = (FW_PARAMS_MNEM(FW_PARAMS_MNEM_DEV) | ++ FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DEV_CF)); ++ ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 1, ++ params, val); ++ ++ /* ++ * If the firmware doesn't support Configuration ++ * Files warn user and exit, ++ */ ++ if (ret < 0) ++ dev_warn(adap->pdev_dev, "Firmware doesn't support "\ ++ "configuration file.\n"); ++ else { ++ /* ++ * The firmware provides us with a memory ++ * buffer where we can load a Configuration ++ * File from the host if we want to override ++ * the Configuration File in flash. ++ */ ++ ++ ret = adap_init0_config(adap, reset); ++ if (ret == -ENOENT) { ++ dev_info(adap->pdev_dev, ++ "No Configuration File present " ++ "on adapter.\n"); ++ } ++ } ++ if (ret < 0) { ++ dev_err(adap->pdev_dev, ++ "could not initialize adapter, error %d\n", ++ -ret); ++ goto bye; ++ } ++ } ++ ++ /* ++ * If we're living with non-hard-coded parameters (either from a ++ * Firmware Configuration File or values programmed by a different PF ++ * Driver), give the SGE code a chance to pull in anything that it ++ * needs ... Note that this must be called after we retrieve our VPD ++ * parameters in order to know how to convert core ticks to seconds. ++ */ ++ if (adap->flags & USING_SOFT_PARAMS) { ++ ret = t4_sge_init(adap); ++ if (ret < 0) ++ goto bye; ++ } ++ ++ /* ++ * Grab some of our basic fundamental operating parameters. ++ */ ++#define FW_PARAM_DEV(param) \ ++ (FW_PARAMS_MNEM(FW_PARAMS_MNEM_DEV) | \ ++ FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DEV_##param)) ++ + #define FW_PARAM_PFVF(param) \ +- (FW_PARAMS_MNEM(FW_PARAMS_MNEM_PFVF) | \ +- FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_PFVF_##param) | \ +- FW_PARAMS_PARAM_Y(adap->fn)) ++ FW_PARAMS_MNEM(FW_PARAMS_MNEM_PFVF) | \ ++ FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_PFVF_##param)| \ ++ FW_PARAMS_PARAM_Y(0) | \ ++ FW_PARAMS_PARAM_Z(0) + +- params[0] = FW_PARAM_DEV(PORTVEC); ++ params[0] = FW_PARAM_PFVF(EQ_START); + params[1] = FW_PARAM_PFVF(L2T_START); + params[2] = FW_PARAM_PFVF(L2T_END); + params[3] = FW_PARAM_PFVF(FILTER_START); + params[4] = FW_PARAM_PFVF(FILTER_END); + params[5] = FW_PARAM_PFVF(IQFLINT_START); +- params[6] = FW_PARAM_PFVF(EQ_START); +- ret = t4_query_params(adap, adap->fn, adap->fn, 0, 7, params, val); ++ ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 6, params, val); + if (ret < 0) + goto bye; +- port_vec = val[0]; ++ adap->sge.egr_start = val[0]; ++ adap->l2t_start = val[1]; ++ adap->l2t_end = val[2]; + adap->tids.ftid_base = val[3]; + adap->tids.nftids = val[4] - val[3] + 1; + adap->sge.ingr_start = val[5]; +- adap->sge.egr_start = val[6]; + +- if (c.ofldcaps) { ++ /* query params related to active filter region */ ++ params[0] = FW_PARAM_PFVF(ACTIVE_FILTER_START); ++ params[1] = FW_PARAM_PFVF(ACTIVE_FILTER_END); ++ ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 2, params, val); ++ /* If Active filter size is set we enable establishing ++ * offload connection through firmware work request ++ */ ++ if ((val[0] != val[1]) && (ret >= 0)) { ++ adap->flags |= FW_OFLD_CONN; ++ adap->tids.aftid_base = val[0]; ++ adap->tids.aftid_end = val[1]; ++ } ++ ++#ifdef CONFIG_CHELSIO_T4_OFFLOAD ++ /* ++ * Get device capabilities so we can determine what resources we need ++ * to manage. ++ */ ++ memset(&caps_cmd, 0, sizeof(caps_cmd)); ++ caps_cmd.op_to_write = htonl(V_FW_CMD_OP(FW_CAPS_CONFIG_CMD) | ++ F_FW_CMD_REQUEST | F_FW_CMD_READ); ++ caps_cmd.cfvalid_to_len16 = htonl(FW_LEN16(caps_cmd)); ++ ret = t4_wr_mbox(adap, adap->mbox, &caps_cmd, sizeof(caps_cmd), ++ &caps_cmd); ++ if (ret < 0) ++ goto bye; ++ ++ if (caps_cmd.toecaps) { + /* query offload-related parameters */ + params[0] = FW_PARAM_DEV(NTID); + params[1] = FW_PARAM_PFVF(SERVER_START); +@@ -3235,28 +3616,55 @@ static int adap_init0(struct adapter *adap) + params[3] = FW_PARAM_PFVF(TDDP_START); + params[4] = FW_PARAM_PFVF(TDDP_END); + params[5] = FW_PARAM_DEV(FLOWC_BUFFIFO_SZ); +- ret = t4_query_params(adap, adap->fn, adap->fn, 0, 6, params, +- val); ++ ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 6, ++ params, val); + if (ret < 0) + goto bye; + adap->tids.ntids = val[0]; + adap->tids.natids = min(adap->tids.ntids / 2, MAX_ATIDS); + adap->tids.stid_base = val[1]; + adap->tids.nstids = val[2] - val[1] + 1; ++ /* ++ * Setup server filter region. Divide the availble filter ++ * region into two parts. Regular filters get 1/3rd and server ++ * filters get 2/3rd part. This is only enabled if workarond ++ * path is enabled. ++ * 1. For regular filters. ++ * 2. Server filter: This are special filters which are used ++ * to redirect SYN packets to offload queue. ++ */ ++ if (adap->flags & FW_OFLD_CONN && !is_bypass(adap)) { ++ adap->tids.sftid_base = adap->tids.ftid_base + ++ DIV_ROUND_UP(adap->tids.nftids, 3); ++ adap->tids.nsftids = adap->tids.nftids - ++ DIV_ROUND_UP(adap->tids.nftids, 3); ++ adap->tids.nftids = adap->tids.sftid_base - ++ adap->tids.ftid_base; ++ } + adap->vres.ddp.start = val[3]; + adap->vres.ddp.size = val[4] - val[3] + 1; + adap->params.ofldq_wr_cred = val[5]; ++ ++ params[0] = FW_PARAM_PFVF(ETHOFLD_START); ++ params[1] = FW_PARAM_PFVF(ETHOFLD_END); ++ ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 2, ++ params, val); ++ if ((val[0] != val[1]) && (ret >= 0)) { ++ adap->tids.uotid_base = val[0]; ++ adap->tids.nuotids = val[1] - val[0] + 1; ++ } ++ + adap->params.offload = 1; + } +- if (c.rdmacaps) { ++ if (caps_cmd.rdmacaps) { + params[0] = FW_PARAM_PFVF(STAG_START); + params[1] = FW_PARAM_PFVF(STAG_END); + params[2] = FW_PARAM_PFVF(RQ_START); + params[3] = FW_PARAM_PFVF(RQ_END); + params[4] = FW_PARAM_PFVF(PBL_START); + params[5] = FW_PARAM_PFVF(PBL_END); +- ret = t4_query_params(adap, adap->fn, adap->fn, 0, 6, params, +- val); ++ ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 6, ++ params, val); + if (ret < 0) + goto bye; + adap->vres.stag.start = val[0]; +@@ -3272,8 +3680,7 @@ static int adap_init0(struct adapter *adap) + params[3] = FW_PARAM_PFVF(CQ_END); + params[4] = FW_PARAM_PFVF(OCQ_START); + params[5] = FW_PARAM_PFVF(OCQ_END); +- ret = t4_query_params(adap, adap->fn, adap->fn, 0, 6, params, +- val); ++ ret = t4_query_params(adap, 0, 0, 0, 6, params, val); + if (ret < 0) + goto bye; + adap->vres.qp.start = val[0]; +@@ -3283,11 +3690,11 @@ static int adap_init0(struct adapter *adap) + adap->vres.ocq.start = val[4]; + adap->vres.ocq.size = val[5] - val[4] + 1; + } +- if (c.iscsicaps) { ++ if (caps_cmd.iscsicaps) { + params[0] = FW_PARAM_PFVF(ISCSI_START); + params[1] = FW_PARAM_PFVF(ISCSI_END); +- ret = t4_query_params(adap, adap->fn, adap->fn, 0, 2, params, +- val); ++ ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 2, ++ params, val); + if (ret < 0) + goto bye; + adap->vres.iscsi.start = val[0]; +@@ -3295,63 +3702,33 @@ static int adap_init0(struct adapter *adap) + } + #undef FW_PARAM_PFVF + #undef FW_PARAM_DEV ++#endif /* CONFIG_CHELSIO_T4_OFFLOAD */ + +- adap->params.nports = hweight32(port_vec); +- adap->params.portvec = port_vec; +- adap->flags |= FW_OK; +- +- /* These are finalized by FW initialization, load their values now */ ++ /* ++ * These are finalized by FW initialization, load their values now. ++ */ + v = t4_read_reg(adap, TP_TIMER_RESOLUTION); + adap->params.tp.tre = TIMERRESOLUTION_GET(v); ++ adap->params.tp.dack_re = DELAYEDACKRESOLUTION_GET(v); + t4_read_mtu_tbl(adap, adap->params.mtus, NULL); + t4_load_mtus(adap, adap->params.mtus, adap->params.a_wnd, + adap->params.b_wnd); + +-#ifdef CONFIG_PCI_IOV +- /* +- * Provision resource limits for Virtual Functions. We currently +- * grant them all the same static resource limits except for the Port +- * Access Rights Mask which we're assigning based on the PF. All of +- * the static provisioning stuff for both the PF and VF really needs +- * to be managed in a persistent manner for each device which the +- * firmware controls. +- */ +- { +- int pf, vf; +- +- for (pf = 0; pf < ARRAY_SIZE(num_vf); pf++) { +- if (num_vf[pf] <= 0) +- continue; +- +- /* VF numbering starts at 1! */ +- for (vf = 1; vf <= num_vf[pf]; vf++) { +- ret = t4_cfg_pfvf(adap, adap->fn, pf, vf, +- VFRES_NEQ, VFRES_NETHCTRL, +- VFRES_NIQFLINT, VFRES_NIQ, +- VFRES_TC, VFRES_NVI, +- FW_PFVF_CMD_CMASK_MASK, +- pfvfres_pmask(adap, pf, vf), +- VFRES_NEXACTF, +- VFRES_R_CAPS, VFRES_WX_CAPS); +- if (ret < 0) +- dev_warn(adap->pdev_dev, "failed to " +- "provision pf/vf=%d/%d; " +- "err=%d\n", pf, vf, ret); +- } +- } +- } +-#endif ++ /* MODQ_REQ_MAP defaults to setting queues 0-3 to chan 0-3 */ ++ for (j = 0; j < NCHAN; j++) ++ adap->params.tp.tx_modq[j] = j; + +- setup_memwin(adap); ++ adap->flags |= FW_OK; + return 0; + + /* +- * If a command timed out or failed with EIO FW does not operate within +- * its spec or something catastrophic happened to HW/FW, stop issuing +- * commands. ++ * Something bad happened. If a command timed out or failed with EIO ++ * FW does not operate within its spec or something catastrophic ++ * happened to HW/FW, stop issuing commands. + */ +-bye: if (ret != -ETIMEDOUT && ret != -EIO) +- t4_fw_bye(adap, adap->fn); ++bye: ++ if (ret != -ETIMEDOUT && ret != -EIO) ++ t4_fw_bye(adap, adap->mbox); + return ret; + } + +@@ -3814,7 +4191,9 @@ static int __devinit init_one(struct pci_dev *pdev, + err = t4_prep_adapter(adapter); + if (err) + goto out_unmap_bar; ++ setup_memwin(adapter); + err = adap_init0(adapter); ++ setup_memwin_rdma(adapter); + if (err) + goto out_unmap_bar; + +@@ -3956,8 +4335,11 @@ static void __devexit remove_one(struct pci_dev *pdev) + { + struct adapter *adapter = pci_get_drvdata(pdev); + ++#ifdef CONFIG_PCI_IOV + pci_disable_sriov(pdev); + ++#endif ++ + if (adapter) { + int i; + +diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h +index d79980c..1b899fe 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h ++++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h +@@ -100,6 +100,8 @@ struct tid_info { + + unsigned int nftids; + unsigned int ftid_base; ++ unsigned int aftid_base; ++ unsigned int aftid_end; + + spinlock_t atid_lock ____cacheline_aligned_in_smp; + union aopen_entry *afree; +diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +index 259d0dc..419432d 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c ++++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +@@ -492,8 +492,9 @@ int t4_seeprom_wp(struct adapter *adapter, bool enable) + * + * Reads card parameters stored in VPD EEPROM. + */ +-static int get_vpd_params(struct adapter *adapter, struct vpd_params *p) ++int get_vpd_params(struct adapter *adapter, struct vpd_params *p) + { ++ u32 cclk_param, cclk_val; + int i, ret; + int ec, sn; + u8 vpd[VPD_LEN], csum; +@@ -555,6 +556,19 @@ static int get_vpd_params(struct adapter *adapter, struct vpd_params *p) + i = pci_vpd_info_field_size(vpd + sn - PCI_VPD_INFO_FLD_HDR_SIZE); + memcpy(p->sn, vpd + sn, min(i, SERNUM_LEN)); + strim(p->sn); ++ ++ /* ++ * Ask firmware for the Core Clock since it knows how to translate the ++ * Reference Clock ('V2') VPD field into a Core Clock value ... ++ */ ++ cclk_param = (FW_PARAMS_MNEM(FW_PARAMS_MNEM_DEV) | ++ FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DEV_CCLK)); ++ ret = t4_query_params(adapter, adapter->mbox, 0, 0, ++ 1, &cclk_param, &cclk_val); ++ if (ret) ++ return ret; ++ p->cclk = cclk_val; ++ + return 0; + } + +@@ -855,6 +869,77 @@ static int t4_flash_erase_sectors(struct adapter *adapter, int start, int end) + } + + /** ++ * t4_flash_cfg_addr - return the address of the flash configuration file ++ * @adapter: the adapter ++ * ++ * Return the address within the flash where the Firmware Configuration ++ * File is stored. ++ */ ++unsigned int t4_flash_cfg_addr(struct adapter *adapter) ++{ ++ if (adapter->params.sf_size == 0x100000) ++ return FLASH_FPGA_CFG_START; ++ else ++ return FLASH_CFG_START; ++} ++ ++/** ++ * t4_load_cfg - download config file ++ * @adap: the adapter ++ * @cfg_data: the cfg text file to write ++ * @size: text file size ++ * ++ * Write the supplied config text file to the card's serial flash. ++ */ ++int t4_load_cfg(struct adapter *adap, const u8 *cfg_data, unsigned int size) ++{ ++ int ret, i, n; ++ unsigned int addr; ++ unsigned int flash_cfg_start_sec; ++ unsigned int sf_sec_size = adap->params.sf_size / adap->params.sf_nsec; ++ ++ addr = t4_flash_cfg_addr(adap); ++ flash_cfg_start_sec = addr / SF_SEC_SIZE; ++ ++ if (size > FLASH_CFG_MAX_SIZE) { ++ dev_err(adap->pdev_dev, "cfg file too large, max is %u bytes\n", ++ FLASH_CFG_MAX_SIZE); ++ return -EFBIG; ++ } ++ ++ i = DIV_ROUND_UP(FLASH_CFG_MAX_SIZE, /* # of sectors spanned */ ++ sf_sec_size); ++ ret = t4_flash_erase_sectors(adap, flash_cfg_start_sec, ++ flash_cfg_start_sec + i - 1); ++ /* ++ * If size == 0 then we're simply erasing the FLASH sectors associated ++ * with the on-adapter Firmware Configuration File. ++ */ ++ if (ret || size == 0) ++ goto out; ++ ++ /* this will write to the flash up to SF_PAGE_SIZE at a time */ ++ for (i = 0; i < size; i += SF_PAGE_SIZE) { ++ if ((size - i) < SF_PAGE_SIZE) ++ n = size - i; ++ else ++ n = SF_PAGE_SIZE; ++ ret = t4_write_flash(adap, addr, n, cfg_data); ++ if (ret) ++ goto out; ++ ++ addr += SF_PAGE_SIZE; ++ cfg_data += SF_PAGE_SIZE; ++ } ++ ++out: ++ if (ret) ++ dev_err(adap->pdev_dev, "config file %s failed %d\n", ++ (size == 0 ? "clear" : "download"), ret); ++ return ret; ++} ++ ++/** + * t4_load_fw - download firmware + * @adap: the adapter + * @fw_data: the firmware image to write +@@ -1854,6 +1939,23 @@ void t4_read_mtu_tbl(struct adapter *adap, u16 *mtus, u8 *mtu_log) + } + + /** ++ * t4_tp_wr_bits_indirect - set/clear bits in an indirect TP register ++ * @adap: the adapter ++ * @addr: the indirect TP register address ++ * @mask: specifies the field within the register to modify ++ * @val: new value for the field ++ * ++ * Sets a field of an indirect TP register to the given value. ++ */ ++void t4_tp_wr_bits_indirect(struct adapter *adap, unsigned int addr, ++ unsigned int mask, unsigned int val) ++{ ++ t4_write_reg(adap, TP_PIO_ADDR, addr); ++ val |= t4_read_reg(adap, TP_PIO_DATA) & ~mask; ++ t4_write_reg(adap, TP_PIO_DATA, val); ++} ++ ++/** + * init_cong_ctrl - initialize congestion control parameters + * @a: the alpha values for congestion control + * @b: the beta values for congestion control +@@ -2137,9 +2239,9 @@ int t4_fwaddrspace_write(struct adapter *adap, unsigned int mbox, + struct fw_ldst_cmd c; + + memset(&c, 0, sizeof(c)); +- c.op_to_addrspace = htonl(V_FW_CMD_OP(FW_LDST_CMD) | F_FW_CMD_REQUEST | +- F_FW_CMD_WRITE | +- V_FW_LDST_CMD_ADDRSPACE(FW_LDST_ADDRSPC_FIRMWARE)); ++ c.op_to_addrspace = htonl(FW_CMD_OP(FW_LDST_CMD) | FW_CMD_REQUEST | ++ FW_CMD_WRITE | ++ FW_LDST_CMD_ADDRSPACE(FW_LDST_ADDRSPC_FIRMWARE)); + c.cycles_to_len16 = htonl(FW_LEN16(c)); + c.u.addrval.addr = htonl(addr); + c.u.addrval.val = htonl(val); +@@ -2239,39 +2341,129 @@ int t4_mdio_wr(struct adapter *adap, unsigned int mbox, unsigned int phy_addr, + } + + /** +- * t4_fw_hello - establish communication with FW +- * @adap: the adapter +- * @mbox: mailbox to use for the FW command +- * @evt_mbox: mailbox to receive async FW events +- * @master: specifies the caller's willingness to be the device master +- * @state: returns the current device state ++ * t4_fw_hello - establish communication with FW ++ * @adap: the adapter ++ * @mbox: mailbox to use for the FW command ++ * @evt_mbox: mailbox to receive async FW events ++ * @master: specifies the caller's willingness to be the device master ++ * @state: returns the current device state (if non-NULL) + * +- * Issues a command to establish communication with FW. ++ * Issues a command to establish communication with FW. Returns either ++ * an error (negative integer) or the mailbox of the Master PF. + */ + int t4_fw_hello(struct adapter *adap, unsigned int mbox, unsigned int evt_mbox, + enum dev_master master, enum dev_state *state) + { + int ret; + struct fw_hello_cmd c; ++ u32 v; ++ unsigned int master_mbox; ++ int retries = FW_CMD_HELLO_RETRIES; + ++retry: ++ memset(&c, 0, sizeof(c)); + INIT_CMD(c, HELLO, WRITE); + c.err_to_mbasyncnot = htonl( + FW_HELLO_CMD_MASTERDIS(master == MASTER_CANT) | + FW_HELLO_CMD_MASTERFORCE(master == MASTER_MUST) | +- FW_HELLO_CMD_MBMASTER(master == MASTER_MUST ? mbox : 0xff) | +- FW_HELLO_CMD_MBASYNCNOT(evt_mbox)); ++ FW_HELLO_CMD_MBMASTER(master == MASTER_MUST ? mbox : ++ FW_HELLO_CMD_MBMASTER_MASK) | ++ FW_HELLO_CMD_MBASYNCNOT(evt_mbox) | ++ FW_HELLO_CMD_STAGE(fw_hello_cmd_stage_os) | ++ FW_HELLO_CMD_CLEARINIT); + ++ /* ++ * Issue the HELLO command to the firmware. If it's not successful ++ * but indicates that we got a "busy" or "timeout" condition, retry ++ * the HELLO until we exhaust our retry limit. ++ */ + ret = t4_wr_mbox(adap, mbox, &c, sizeof(c), &c); +- if (ret == 0 && state) { +- u32 v = ntohl(c.err_to_mbasyncnot); +- if (v & FW_HELLO_CMD_INIT) +- *state = DEV_STATE_INIT; +- else if (v & FW_HELLO_CMD_ERR) ++ if (ret < 0) { ++ if ((ret == -EBUSY || ret == -ETIMEDOUT) && retries-- > 0) ++ goto retry; ++ return ret; ++ } ++ ++ v = ntohl(c.err_to_mbasyncnot); ++ master_mbox = FW_HELLO_CMD_MBMASTER_GET(v); ++ if (state) { ++ if (v & FW_HELLO_CMD_ERR) + *state = DEV_STATE_ERR; ++ else if (v & FW_HELLO_CMD_INIT) ++ *state = DEV_STATE_INIT; + else + *state = DEV_STATE_UNINIT; + } +- return ret; ++ ++ /* ++ * If we're not the Master PF then we need to wait around for the ++ * Master PF Driver to finish setting up the adapter. ++ * ++ * Note that we also do this wait if we're a non-Master-capable PF and ++ * there is no current Master PF; a Master PF may show up momentarily ++ * and we wouldn't want to fail pointlessly. (This can happen when an ++ * OS loads lots of different drivers rapidly at the same time). In ++ * this case, the Master PF returned by the firmware will be ++ * FW_PCIE_FW_MASTER_MASK so the test below will work ... ++ */ ++ if ((v & (FW_HELLO_CMD_ERR|FW_HELLO_CMD_INIT)) == 0 && ++ master_mbox != mbox) { ++ int waiting = FW_CMD_HELLO_TIMEOUT; ++ ++ /* ++ * Wait for the firmware to either indicate an error or ++ * initialized state. If we see either of these we bail out ++ * and report the issue to the caller. If we exhaust the ++ * "hello timeout" and we haven't exhausted our retries, try ++ * again. Otherwise bail with a timeout error. ++ */ ++ for (;;) { ++ u32 pcie_fw; ++ ++ msleep(50); ++ waiting -= 50; ++ ++ /* ++ * If neither Error nor Initialialized are indicated ++ * by the firmware keep waiting till we exaust our ++ * timeout ... and then retry if we haven't exhausted ++ * our retries ... ++ */ ++ pcie_fw = t4_read_reg(adap, MA_PCIE_FW); ++ if (!(pcie_fw & (FW_PCIE_FW_ERR|FW_PCIE_FW_INIT))) { ++ if (waiting <= 0) { ++ if (retries-- > 0) ++ goto retry; ++ ++ return -ETIMEDOUT; ++ } ++ continue; ++ } ++ ++ /* ++ * We either have an Error or Initialized condition ++ * report errors preferentially. ++ */ ++ if (state) { ++ if (pcie_fw & FW_PCIE_FW_ERR) ++ *state = DEV_STATE_ERR; ++ else if (pcie_fw & FW_PCIE_FW_INIT) ++ *state = DEV_STATE_INIT; ++ } ++ ++ /* ++ * If we arrived before a Master PF was selected and ++ * there's not a valid Master PF, grab its identity ++ * for our caller. ++ */ ++ if (master_mbox == FW_PCIE_FW_MASTER_MASK && ++ (pcie_fw & FW_PCIE_FW_MASTER_VLD)) ++ master_mbox = FW_PCIE_FW_MASTER_GET(pcie_fw); ++ break; ++ } ++ } ++ ++ return master_mbox; + } + + /** +@@ -2323,6 +2515,163 @@ int t4_fw_reset(struct adapter *adap, unsigned int mbox, int reset) + } + + /** ++ * t4_fw_config_file - setup an adapter via a Configuration File ++ * @adap: the adapter ++ * @mbox: mailbox to use for the FW command ++ * @mtype: the memory type where the Configuration File is located ++ * @maddr: the memory address where the Configuration File is located ++ * @finiver: return value for CF [fini] version ++ * @finicsum: return value for CF [fini] checksum ++ * @cfcsum: return value for CF computed checksum ++ * ++ * Issue a command to get the firmware to process the Configuration ++ * File located at the specified mtype/maddress. If the Configuration ++ * File is processed successfully and return value pointers are ++ * provided, the Configuration File "[fini] section version and ++ * checksum values will be returned along with the computed checksum. ++ * It's up to the caller to decide how it wants to respond to the ++ * checksums not matching but it recommended that a prominant warning ++ * be emitted in order to help people rapidly identify changed or ++ * corrupted Configuration Files. ++ * ++ * Also note that it's possible to modify things like "niccaps", ++ * "toecaps",etc. between processing the Configuration File and telling ++ * the firmware to use the new configuration. Callers which want to ++ * do this will need to "hand-roll" their own CAPS_CONFIGS commands for ++ * Configuration Files if they want to do this. ++ */ ++int t4_fw_config_file(struct adapter *adap, unsigned int mbox, ++ unsigned int mtype, unsigned int maddr, ++ u32 *finiver, u32 *finicsum, u32 *cfcsum) ++{ ++ struct fw_caps_config_cmd caps_cmd; ++ int ret; ++ ++ /* ++ * Tell the firmware to process the indicated Configuration File. ++ * If there are no errors and the caller has provided return value ++ * pointers for the [fini] section version, checksum and computed ++ * checksum, pass those back to the caller. ++ */ ++ memset(&caps_cmd, 0, sizeof(caps_cmd)); ++ caps_cmd.op_to_write = ++ htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) | ++ FW_CMD_REQUEST | ++ FW_CMD_READ); ++ caps_cmd.retval_len16 = ++ htonl(FW_CAPS_CONFIG_CMD_CFVALID | ++ FW_CAPS_CONFIG_CMD_MEMTYPE_CF(mtype) | ++ FW_CAPS_CONFIG_CMD_MEMADDR64K_CF(maddr >> 16) | ++ FW_LEN16(caps_cmd)); ++ ret = t4_wr_mbox(adap, mbox, &caps_cmd, sizeof(caps_cmd), &caps_cmd); ++ if (ret < 0) ++ return ret; ++ ++ if (finiver) ++ *finiver = ntohl(caps_cmd.finiver); ++ if (finicsum) ++ *finicsum = ntohl(caps_cmd.finicsum); ++ if (cfcsum) ++ *cfcsum = ntohl(caps_cmd.cfcsum); ++ ++ /* ++ * And now tell the firmware to use the configuration we just loaded. ++ */ ++ caps_cmd.op_to_write = ++ htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) | ++ FW_CMD_REQUEST | ++ FW_CMD_WRITE); ++ caps_cmd.retval_len16 = htonl(FW_LEN16(caps_cmd)); ++ return t4_wr_mbox(adap, mbox, &caps_cmd, sizeof(caps_cmd), NULL); ++} ++ ++/** ++ * t4_fixup_host_params - fix up host-dependent parameters ++ * @adap: the adapter ++ * @page_size: the host's Base Page Size ++ * @cache_line_size: the host's Cache Line Size ++ * ++ * Various registers in T4 contain values which are dependent on the ++ * host's Base Page and Cache Line Sizes. This function will fix all of ++ * those registers with the appropriate values as passed in ... ++ */ ++int t4_fixup_host_params(struct adapter *adap, unsigned int page_size, ++ unsigned int cache_line_size) ++{ ++ unsigned int page_shift = fls(page_size) - 1; ++ unsigned int sge_hps = page_shift - 10; ++ unsigned int stat_len = cache_line_size > 64 ? 128 : 64; ++ unsigned int fl_align = cache_line_size < 32 ? 32 : cache_line_size; ++ unsigned int fl_align_log = fls(fl_align) - 1; ++ ++ t4_write_reg(adap, SGE_HOST_PAGE_SIZE, ++ HOSTPAGESIZEPF0(sge_hps) | ++ HOSTPAGESIZEPF1(sge_hps) | ++ HOSTPAGESIZEPF2(sge_hps) | ++ HOSTPAGESIZEPF3(sge_hps) | ++ HOSTPAGESIZEPF4(sge_hps) | ++ HOSTPAGESIZEPF5(sge_hps) | ++ HOSTPAGESIZEPF6(sge_hps) | ++ HOSTPAGESIZEPF7(sge_hps)); ++ ++ t4_set_reg_field(adap, SGE_CONTROL, ++ INGPADBOUNDARY(INGPADBOUNDARY_MASK) | ++ EGRSTATUSPAGESIZE_MASK, ++ INGPADBOUNDARY(fl_align_log - 5) | ++ EGRSTATUSPAGESIZE(stat_len != 64)); ++ ++ /* ++ * Adjust various SGE Free List Host Buffer Sizes. ++ * ++ * This is something of a crock since we're using fixed indices into ++ * the array which are also known by the sge.c code and the T4 ++ * Firmware Configuration File. We need to come up with a much better ++ * approach to managing this array. For now, the first four entries ++ * are: ++ * ++ * 0: Host Page Size ++ * 1: 64KB ++ * 2: Buffer size corresponding to 1500 byte MTU (unpacked mode) ++ * 3: Buffer size corresponding to 9000 byte MTU (unpacked mode) ++ * ++ * For the single-MTU buffers in unpacked mode we need to include ++ * space for the SGE Control Packet Shift, 14 byte Ethernet header, ++ * possible 4 byte VLAN tag, all rounded up to the next Ingress Packet ++ * Padding boundry. All of these are accommodated in the Factory ++ * Default Firmware Configuration File but we need to adjust it for ++ * this host's cache line size. ++ */ ++ t4_write_reg(adap, SGE_FL_BUFFER_SIZE0, page_size); ++ t4_write_reg(adap, SGE_FL_BUFFER_SIZE2, ++ (t4_read_reg(adap, SGE_FL_BUFFER_SIZE2) + fl_align-1) ++ & ~(fl_align-1)); ++ t4_write_reg(adap, SGE_FL_BUFFER_SIZE3, ++ (t4_read_reg(adap, SGE_FL_BUFFER_SIZE3) + fl_align-1) ++ & ~(fl_align-1)); ++ ++ t4_write_reg(adap, ULP_RX_TDDP_PSZ, HPZ0(page_shift - 12)); ++ ++ return 0; ++} ++ ++/** ++ * t4_fw_initialize - ask FW to initialize the device ++ * @adap: the adapter ++ * @mbox: mailbox to use for the FW command ++ * ++ * Issues a command to FW to partially initialize the device. This ++ * performs initialization that generally doesn't depend on user input. ++ */ ++int t4_fw_initialize(struct adapter *adap, unsigned int mbox) ++{ ++ struct fw_initialize_cmd c; ++ ++ memset(&c, 0, sizeof(c)); ++ INIT_CMD(c, INITIALIZE, WRITE); ++ return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); ++} ++ ++/** + * t4_query_params - query FW or device parameters + * @adap: the adapter + * @mbox: mailbox to use for the FW command +@@ -2974,10 +3323,6 @@ int __devinit t4_prep_adapter(struct adapter *adapter) + return ret; + } + +- ret = get_vpd_params(adapter, &adapter->params.vpd); +- if (ret < 0) +- return ret; +- + init_cong_ctrl(adapter->params.a_wnd, adapter->params.b_wnd); + + /* +@@ -2985,6 +3330,7 @@ int __devinit t4_prep_adapter(struct adapter *adapter) + */ + adapter->params.nports = 1; + adapter->params.portvec = 1; ++ adapter->params.vpd.cclk = 50000; + return 0; + } + +diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +index 2767ca6..779b23f 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h ++++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +@@ -115,6 +115,35 @@ + #define GLOBALENABLE 0x00000001U + + #define SGE_HOST_PAGE_SIZE 0x100c ++ ++#define HOSTPAGESIZEPF7_MASK 0x0000000fU ++#define HOSTPAGESIZEPF7_SHIFT 28 ++#define HOSTPAGESIZEPF7(x) ((x) << HOSTPAGESIZEPF7_SHIFT) ++ ++#define HOSTPAGESIZEPF6_MASK 0x0000000fU ++#define HOSTPAGESIZEPF6_SHIFT 24 ++#define HOSTPAGESIZEPF6(x) ((x) << HOSTPAGESIZEPF6_SHIFT) ++ ++#define HOSTPAGESIZEPF5_MASK 0x0000000fU ++#define HOSTPAGESIZEPF5_SHIFT 20 ++#define HOSTPAGESIZEPF5(x) ((x) << HOSTPAGESIZEPF5_SHIFT) ++ ++#define HOSTPAGESIZEPF4_MASK 0x0000000fU ++#define HOSTPAGESIZEPF4_SHIFT 16 ++#define HOSTPAGESIZEPF4(x) ((x) << HOSTPAGESIZEPF4_SHIFT) ++ ++#define HOSTPAGESIZEPF3_MASK 0x0000000fU ++#define HOSTPAGESIZEPF3_SHIFT 12 ++#define HOSTPAGESIZEPF3(x) ((x) << HOSTPAGESIZEPF3_SHIFT) ++ ++#define HOSTPAGESIZEPF2_MASK 0x0000000fU ++#define HOSTPAGESIZEPF2_SHIFT 8 ++#define HOSTPAGESIZEPF2(x) ((x) << HOSTPAGESIZEPF2_SHIFT) ++ ++#define HOSTPAGESIZEPF1_MASK 0x0000000fU ++#define HOSTPAGESIZEPF1_SHIFT 4 ++#define HOSTPAGESIZEPF1(x) ((x) << HOSTPAGESIZEPF1_SHIFT) ++ + #define HOSTPAGESIZEPF0_MASK 0x0000000fU + #define HOSTPAGESIZEPF0_SHIFT 0 + #define HOSTPAGESIZEPF0(x) ((x) << HOSTPAGESIZEPF0_SHIFT) +@@ -162,6 +191,8 @@ + #define SGE_INT_ENABLE3 0x1040 + #define SGE_FL_BUFFER_SIZE0 0x1044 + #define SGE_FL_BUFFER_SIZE1 0x1048 ++#define SGE_FL_BUFFER_SIZE2 0x104c ++#define SGE_FL_BUFFER_SIZE3 0x1050 + #define SGE_INGRESS_RX_THRESHOLD 0x10a0 + #define THRESHOLD_0_MASK 0x3f000000U + #define THRESHOLD_0_SHIFT 24 +@@ -367,7 +398,7 @@ + #define MEM_WRAP_CLIENT_NUM_MASK 0x0000000fU + #define MEM_WRAP_CLIENT_NUM_SHIFT 0 + #define MEM_WRAP_CLIENT_NUM_GET(x) (((x) & MEM_WRAP_CLIENT_NUM_MASK) >> MEM_WRAP_CLIENT_NUM_SHIFT) +- ++#define MA_PCIE_FW 0x30b8 + #define MA_PARITY_ERROR_STATUS 0x77f4 + + #define EDC_0_BASE_ADDR 0x7900 +@@ -469,6 +500,10 @@ + #define TIMERRESOLUTION_MASK 0x00ff0000U + #define TIMERRESOLUTION_SHIFT 16 + #define TIMERRESOLUTION_GET(x) (((x) & TIMERRESOLUTION_MASK) >> TIMERRESOLUTION_SHIFT) ++#define DELAYEDACKRESOLUTION_MASK 0x000000ffU ++#define DELAYEDACKRESOLUTION_SHIFT 0 ++#define DELAYEDACKRESOLUTION_GET(x) \ ++ (((x) & DELAYEDACKRESOLUTION_MASK) >> DELAYEDACKRESOLUTION_SHIFT) + + #define TP_SHIFT_CNT 0x7dc0 + +diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +index 94e3484..3f85019 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h ++++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +@@ -155,6 +155,17 @@ struct fw_eth_tx_pkt_vm_wr { + + #define FW_CMD_MAX_TIMEOUT 3000 + ++/* ++ * If a host driver does a HELLO and discovers that there's already a MASTER ++ * selected, we may have to wait for that MASTER to finish issuing RESET, ++ * configuration and INITIALIZE commands. Also, there's a possibility that ++ * our own HELLO may get lost if it happens right as the MASTER is issuign a ++ * RESET command, so we need to be willing to make a few retries of our HELLO. ++ */ ++#define FW_CMD_HELLO_TIMEOUT (3 * FW_CMD_MAX_TIMEOUT) ++#define FW_CMD_HELLO_RETRIES 3 ++ ++ + enum fw_cmd_opcodes { + FW_LDST_CMD = 0x01, + FW_RESET_CMD = 0x03, +@@ -307,6 +318,10 @@ struct fw_reset_cmd { + __be32 r3; + }; + ++enum fw_hellow_cmd { ++ fw_hello_cmd_stage_os = 0x0 ++}; ++ + struct fw_hello_cmd { + __be32 op_to_write; + __be32 retval_len16; +@@ -315,8 +330,14 @@ struct fw_hello_cmd { + #define FW_HELLO_CMD_INIT (1U << 30) + #define FW_HELLO_CMD_MASTERDIS(x) ((x) << 29) + #define FW_HELLO_CMD_MASTERFORCE(x) ((x) << 28) +-#define FW_HELLO_CMD_MBMASTER(x) ((x) << 24) ++#define FW_HELLO_CMD_MBMASTER_MASK 0xfU ++#define FW_HELLO_CMD_MBMASTER_SHIFT 24 ++#define FW_HELLO_CMD_MBMASTER(x) ((x) << FW_HELLO_CMD_MBMASTER_SHIFT) ++#define FW_HELLO_CMD_MBMASTER_GET(x) \ ++ (((x) >> FW_HELLO_CMD_MBMASTER_SHIFT) & FW_HELLO_CMD_MBMASTER_MASK) + #define FW_HELLO_CMD_MBASYNCNOT(x) ((x) << 20) ++#define FW_HELLO_CMD_STAGE(x) ((x) << 17) ++#define FW_HELLO_CMD_CLEARINIT (1U << 16) + __be32 fwrev; + }; + +@@ -1654,18 +1675,4 @@ struct fw_hdr { + #define FW_HDR_FW_VER_MICRO_GET(x) (((x) >> 8) & 0xff) + #define FW_HDR_FW_VER_BUILD_GET(x) (((x) >> 0) & 0xff) + +-#define S_FW_CMD_OP 24 +-#define V_FW_CMD_OP(x) ((x) << S_FW_CMD_OP) +- +-#define S_FW_CMD_REQUEST 23 +-#define V_FW_CMD_REQUEST(x) ((x) << S_FW_CMD_REQUEST) +-#define F_FW_CMD_REQUEST V_FW_CMD_REQUEST(1U) +- +-#define S_FW_CMD_WRITE 21 +-#define V_FW_CMD_WRITE(x) ((x) << S_FW_CMD_WRITE) +-#define F_FW_CMD_WRITE V_FW_CMD_WRITE(1U) +- +-#define S_FW_LDST_CMD_ADDRSPACE 0 +-#define V_FW_LDST_CMD_ADDRSPACE(x) ((x) << S_FW_LDST_CMD_ADDRSPACE) +- + #endif /* _T4FW_INTERFACE_H_ */ +-- +1.7.1 + diff --git a/linux-next-cherry-picks/0006-cxgb4-Add-support-for-T4-hardwired-driver-configurat.patch b/linux-next-cherry-picks/0006-cxgb4-Add-support-for-T4-hardwired-driver-configurat.patch new file mode 100644 index 0000000..1f6aee4 --- /dev/null +++ b/linux-next-cherry-picks/0006-cxgb4-Add-support-for-T4-hardwired-driver-configurat.patch @@ -0,0 +1,651 @@ +From 13ee15d396da78079918c5be7510ea1847393d4c Mon Sep 17 00:00:00 2001 +From: Vipul Pandya +Date: Wed, 26 Sep 2012 02:39:40 +0000 +Subject: [PATCH 4/6] cxgb4: Add support for T4 hardwired driver configuration settings + +In case if user defined configuration file at /lib/firmware/cxgb4/t4-config.txt +location and also factory default configuration file written to FLASH are not +present then driver will use hardwired configuration settings. + +Signed-off-by: Jay Hernandez +Signed-off-by: Vipul Pandya +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 4 + + drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 390 +++++++++++++++++++++-- + drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 22 ++ + drivers/net/ethernet/chelsio/cxgb4/t4_regs.h | 56 ++++ + drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h | 2 + + 5 files changed, 442 insertions(+), 32 deletions(-) + +diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +index 777cbb4..6827ce3 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h ++++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +@@ -318,6 +318,7 @@ enum { /* adapter flags */ + USING_MSI = (1 << 1), + USING_MSIX = (1 << 2), + FW_OK = (1 << 4), ++ RSS_TNLALLLOOKUP = (1 << 5), + USING_SOFT_PARAMS = (1 << 6), + MASTER_PF = (1 << 7), + FW_OFLD_CONN = (1 << 9), +@@ -677,6 +678,9 @@ static inline int t4_wr_mbox_ns(struct adapter *adap, int mbox, const void *cmd, + return t4_wr_mbox_meat(adap, mbox, cmd, size, rpl, false); + } + ++void t4_write_indirect(struct adapter *adap, unsigned int addr_reg, ++ unsigned int data_reg, const u32 *vals, ++ unsigned int nregs, unsigned int start_idx); + void t4_intr_enable(struct adapter *adapter); + void t4_intr_disable(struct adapter *adapter); + int t4_slow_intr_handler(struct adapter *adapter); +diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +index cb3e663..b9cd08d 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c ++++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +@@ -78,28 +78,45 @@ + */ + #define MAX_SGE_TIMERVAL 200U + +-#ifdef CONFIG_PCI_IOV +-/* +- * Virtual Function provisioning constants. We need two extra Ingress Queues +- * with Interrupt capability to serve as the VF's Firmware Event Queue and +- * Forwarded Interrupt Queue (when using MSI mode) -- neither will have Free +- * Lists associated with them). For each Ethernet/Control Egress Queue and +- * for each Free List, we need an Egress Context. +- */ + enum { ++ /* ++ * Physical Function provisioning constants. ++ */ ++ PFRES_NVI = 4, /* # of Virtual Interfaces */ ++ PFRES_NETHCTRL = 128, /* # of EQs used for ETH or CTRL Qs */ ++ PFRES_NIQFLINT = 128, /* # of ingress Qs/w Free List(s)/intr ++ */ ++ PFRES_NEQ = 256, /* # of egress queues */ ++ PFRES_NIQ = 0, /* # of ingress queues */ ++ PFRES_TC = 0, /* PCI-E traffic class */ ++ PFRES_NEXACTF = 128, /* # of exact MPS filters */ ++ ++ PFRES_R_CAPS = FW_CMD_CAP_PF, ++ PFRES_WX_CAPS = FW_CMD_CAP_PF, ++ ++#ifdef CONFIG_PCI_IOV ++ /* ++ * Virtual Function provisioning constants. We need two extra Ingress ++ * Queues with Interrupt capability to serve as the VF's Firmware ++ * Event Queue and Forwarded Interrupt Queue (when using MSI mode) -- ++ * neither will have Free Lists associated with them). For each ++ * Ethernet/Control Egress Queue and for each Free List, we need an ++ * Egress Context. ++ */ + VFRES_NPORTS = 1, /* # of "ports" per VF */ + VFRES_NQSETS = 2, /* # of "Queue Sets" per VF */ + + VFRES_NVI = VFRES_NPORTS, /* # of Virtual Interfaces */ + VFRES_NETHCTRL = VFRES_NQSETS, /* # of EQs used for ETH or CTRL Qs */ + VFRES_NIQFLINT = VFRES_NQSETS+2,/* # of ingress Qs/w Free List(s)/intr */ +- VFRES_NIQ = 0, /* # of non-fl/int ingress queues */ + VFRES_NEQ = VFRES_NQSETS*2, /* # of egress queues */ ++ VFRES_NIQ = 0, /* # of non-fl/int ingress queues */ + VFRES_TC = 0, /* PCI-E traffic class */ + VFRES_NEXACTF = 16, /* # of exact MPS filters */ + + VFRES_R_CAPS = FW_CMD_CAP_DMAQ|FW_CMD_CAP_VF|FW_CMD_CAP_PORT, + VFRES_WX_CAPS = FW_CMD_CAP_DMAQ|FW_CMD_CAP_VF, ++#endif + }; + + /* +@@ -146,7 +163,6 @@ static unsigned int pfvfres_pmask(struct adapter *adapter, + } + /*NOTREACHED*/ + } +-#endif + + enum { + MAX_TXQ_ENTRIES = 16384, +@@ -213,6 +229,17 @@ static uint force_init; + module_param(force_init, uint, 0644); + MODULE_PARM_DESC(force_init, "Forcibly become Master PF and initialize adapter"); + ++/* ++ * Normally if the firmware we connect to has Configuration File support, we ++ * use that and only fall back to the old Driver-based initialization if the ++ * Configuration File fails for some reason. If force_old_init is set, then ++ * we'll always use the old Driver-based initialization sequence. ++ */ ++static uint force_old_init; ++ ++module_param(force_old_init, uint, 0644); ++MODULE_PARM_DESC(force_old_init, "Force old initialization sequence"); ++ + static int dflt_msg_enable = DFLT_MSG_ENABLE; + + module_param(dflt_msg_enable, int, 0644); +@@ -274,6 +301,30 @@ module_param_array(num_vf, uint, NULL, 0644); + MODULE_PARM_DESC(num_vf, "number of VFs for each of PFs 0-3"); + #endif + ++/* ++ * The filter TCAM has a fixed portion and a variable portion. The fixed ++ * portion can match on source/destination IP IPv4/IPv6 addresses and TCP/UDP ++ * ports. The variable portion is 36 bits which can include things like Exact ++ * Match MAC Index (9 bits), Ether Type (16 bits), IP Protocol (8 bits), ++ * [Inner] VLAN Tag (17 bits), etc. which, if all were somehow selected, would ++ * far exceed the 36-bit budget for this "compressed" header portion of the ++ * filter. Thus, we have a scarce resource which must be carefully managed. ++ * ++ * By default we set this up to mostly match the set of filter matching ++ * capabilities of T3 but with accommodations for some of T4's more ++ * interesting features: ++ * ++ * { IP Fragment (1), MPS Match Type (3), IP Protocol (8), ++ * [Inner] VLAN (17), Port (3), FCoE (1) } ++ */ ++enum { ++ TP_VLAN_PRI_MAP_DEFAULT = HW_TPL_FR_MT_PR_IV_P_FC, ++ TP_VLAN_PRI_MAP_FIRST = FCOE_SHIFT, ++ TP_VLAN_PRI_MAP_LAST = FRAGMENTATION_SHIFT, ++}; ++ ++static unsigned int tp_vlan_pri_map = TP_VLAN_PRI_MAP_DEFAULT; ++ + static struct dentry *cxgb4_debugfs_root; + + static LIST_HEAD(adapter_list); +@@ -3410,6 +3461,262 @@ bye: + } + + /* ++ * Attempt to initialize the adapter via hard-coded, driver supplied ++ * parameters ... ++ */ ++static int adap_init0_no_config(struct adapter *adapter, int reset) ++{ ++ struct sge *s = &adapter->sge; ++ struct fw_caps_config_cmd caps_cmd; ++ u32 v; ++ int i, ret; ++ ++ /* ++ * Reset device if necessary ++ */ ++ if (reset) { ++ ret = t4_fw_reset(adapter, adapter->mbox, ++ PIORSTMODE | PIORST); ++ if (ret < 0) ++ goto bye; ++ } ++ ++ /* ++ * Get device capabilities and select which we'll be using. ++ */ ++ memset(&caps_cmd, 0, sizeof(caps_cmd)); ++ caps_cmd.op_to_write = htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) | ++ FW_CMD_REQUEST | FW_CMD_READ); ++ caps_cmd.retval_len16 = htonl(FW_LEN16(caps_cmd)); ++ ret = t4_wr_mbox(adapter, adapter->mbox, &caps_cmd, sizeof(caps_cmd), ++ &caps_cmd); ++ if (ret < 0) ++ goto bye; ++ ++#ifndef CONFIG_CHELSIO_T4_OFFLOAD ++ /* ++ * If we're a pure NIC driver then disable all offloading facilities. ++ * This will allow the firmware to optimize aspects of the hardware ++ * configuration which will result in improved performance. ++ */ ++ caps_cmd.ofldcaps = 0; ++ caps_cmd.iscsicaps = 0; ++ caps_cmd.rdmacaps = 0; ++ caps_cmd.fcoecaps = 0; ++#endif ++ ++ if (caps_cmd.niccaps & htons(FW_CAPS_CONFIG_NIC_VM)) { ++ if (!vf_acls) ++ caps_cmd.niccaps ^= htons(FW_CAPS_CONFIG_NIC_VM); ++ else ++ caps_cmd.niccaps = htons(FW_CAPS_CONFIG_NIC_VM); ++ } else if (vf_acls) { ++ dev_err(adapter->pdev_dev, "virtualization ACLs not supported"); ++ goto bye; ++ } ++ caps_cmd.op_to_write = htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) | ++ FW_CMD_REQUEST | FW_CMD_WRITE); ++ ret = t4_wr_mbox(adapter, adapter->mbox, &caps_cmd, sizeof(caps_cmd), ++ NULL); ++ if (ret < 0) ++ goto bye; ++ ++ /* ++ * Tweak configuration based on system architecture, module ++ * parameters, etc. ++ */ ++ ret = adap_init0_tweaks(adapter); ++ if (ret < 0) ++ goto bye; ++ ++ /* ++ * Select RSS Global Mode we want to use. We use "Basic Virtual" ++ * mode which maps each Virtual Interface to its own section of ++ * the RSS Table and we turn on all map and hash enables ... ++ */ ++ adapter->flags |= RSS_TNLALLLOOKUP; ++ ret = t4_config_glbl_rss(adapter, adapter->mbox, ++ FW_RSS_GLB_CONFIG_CMD_MODE_BASICVIRTUAL, ++ FW_RSS_GLB_CONFIG_CMD_TNLMAPEN | ++ FW_RSS_GLB_CONFIG_CMD_HASHTOEPLITZ | ++ ((adapter->flags & RSS_TNLALLLOOKUP) ? ++ FW_RSS_GLB_CONFIG_CMD_TNLALLLKP : 0)); ++ if (ret < 0) ++ goto bye; ++ ++ /* ++ * Set up our own fundamental resource provisioning ... ++ */ ++ ret = t4_cfg_pfvf(adapter, adapter->mbox, adapter->fn, 0, ++ PFRES_NEQ, PFRES_NETHCTRL, ++ PFRES_NIQFLINT, PFRES_NIQ, ++ PFRES_TC, PFRES_NVI, ++ FW_PFVF_CMD_CMASK_MASK, ++ pfvfres_pmask(adapter, adapter->fn, 0), ++ PFRES_NEXACTF, ++ PFRES_R_CAPS, PFRES_WX_CAPS); ++ if (ret < 0) ++ goto bye; ++ ++ /* ++ * Perform low level SGE initialization. We need to do this before we ++ * send the firmware the INITIALIZE command because that will cause ++ * any other PF Drivers which are waiting for the Master ++ * Initialization to proceed forward. ++ */ ++ for (i = 0; i < SGE_NTIMERS - 1; i++) ++ s->timer_val[i] = min(intr_holdoff[i], MAX_SGE_TIMERVAL); ++ s->timer_val[SGE_NTIMERS - 1] = MAX_SGE_TIMERVAL; ++ s->counter_val[0] = 1; ++ for (i = 1; i < SGE_NCOUNTERS; i++) ++ s->counter_val[i] = min(intr_cnt[i - 1], ++ THRESHOLD_0_GET(THRESHOLD_0_MASK)); ++ t4_sge_init(adapter); ++ ++#ifdef CONFIG_PCI_IOV ++ /* ++ * Provision resource limits for Virtual Functions. We currently ++ * grant them all the same static resource limits except for the Port ++ * Access Rights Mask which we're assigning based on the PF. All of ++ * the static provisioning stuff for both the PF and VF really needs ++ * to be managed in a persistent manner for each device which the ++ * firmware controls. ++ */ ++ { ++ int pf, vf; ++ ++ for (pf = 0; pf < ARRAY_SIZE(num_vf); pf++) { ++ if (num_vf[pf] <= 0) ++ continue; ++ ++ /* VF numbering starts at 1! */ ++ for (vf = 1; vf <= num_vf[pf]; vf++) { ++ ret = t4_cfg_pfvf(adapter, adapter->mbox, ++ pf, vf, ++ VFRES_NEQ, VFRES_NETHCTRL, ++ VFRES_NIQFLINT, VFRES_NIQ, ++ VFRES_TC, VFRES_NVI, ++ FW_PFVF_CMD_CMASK_GET( ++ FW_PFVF_CMD_CMASK_MASK), ++ pfvfres_pmask( ++ adapter, pf, vf), ++ VFRES_NEXACTF, ++ VFRES_R_CAPS, VFRES_WX_CAPS); ++ if (ret < 0) ++ dev_warn(adapter->pdev_dev, ++ "failed to "\ ++ "provision pf/vf=%d/%d; " ++ "err=%d\n", pf, vf, ret); ++ } ++ } ++ } ++#endif ++ ++ /* ++ * Set up the default filter mode. Later we'll want to implement this ++ * via a firmware command, etc. ... This needs to be done before the ++ * firmare initialization command ... If the selected set of fields ++ * isn't equal to the default value, we'll need to make sure that the ++ * field selections will fit in the 36-bit budget. ++ */ ++ if (tp_vlan_pri_map != TP_VLAN_PRI_MAP_DEFAULT) { ++ int i, bits = 0; ++ ++ for (i = TP_VLAN_PRI_MAP_FIRST; i <= TP_VLAN_PRI_MAP_LAST; i++) ++ switch (tp_vlan_pri_map & (1 << i)) { ++ case 0: ++ /* compressed filter field not enabled */ ++ break; ++ case FCOE_MASK: ++ bits += 1; ++ break; ++ case PORT_MASK: ++ bits += 3; ++ break; ++ case VNIC_ID_MASK: ++ bits += 17; ++ break; ++ case VLAN_MASK: ++ bits += 17; ++ break; ++ case TOS_MASK: ++ bits += 8; ++ break; ++ case PROTOCOL_MASK: ++ bits += 8; ++ break; ++ case ETHERTYPE_MASK: ++ bits += 16; ++ break; ++ case MACMATCH_MASK: ++ bits += 9; ++ break; ++ case MPSHITTYPE_MASK: ++ bits += 3; ++ break; ++ case FRAGMENTATION_MASK: ++ bits += 1; ++ break; ++ } ++ ++ if (bits > 36) { ++ dev_err(adapter->pdev_dev, ++ "tp_vlan_pri_map=%#x needs %d bits > 36;"\ ++ " using %#x\n", tp_vlan_pri_map, bits, ++ TP_VLAN_PRI_MAP_DEFAULT); ++ tp_vlan_pri_map = TP_VLAN_PRI_MAP_DEFAULT; ++ } ++ } ++ v = tp_vlan_pri_map; ++ t4_write_indirect(adapter, TP_PIO_ADDR, TP_PIO_DATA, ++ &v, 1, TP_VLAN_PRI_MAP); ++ ++ /* ++ * We need Five Tuple Lookup mode to be set in TP_GLOBAL_CONFIG order ++ * to support any of the compressed filter fields above. Newer ++ * versions of the firmware do this automatically but it doesn't hurt ++ * to set it here. Meanwhile, we do _not_ need to set Lookup Every ++ * Packet in TP_INGRESS_CONFIG to support matching non-TCP packets ++ * since the firmware automatically turns this on and off when we have ++ * a non-zero number of filters active (since it does have a ++ * performance impact). ++ */ ++ if (tp_vlan_pri_map) ++ t4_set_reg_field(adapter, TP_GLOBAL_CONFIG, ++ FIVETUPLELOOKUP_MASK, ++ FIVETUPLELOOKUP_MASK); ++ ++ /* ++ * Tweak some settings. ++ */ ++ t4_write_reg(adapter, TP_SHIFT_CNT, SYNSHIFTMAX(6) | ++ RXTSHIFTMAXR1(4) | RXTSHIFTMAXR2(15) | ++ PERSHIFTBACKOFFMAX(8) | PERSHIFTMAX(8) | ++ KEEPALIVEMAXR1(4) | KEEPALIVEMAXR2(9)); ++ ++ /* ++ * Get basic stuff going by issuing the Firmware Initialize command. ++ * Note that this _must_ be after all PFVF commands ... ++ */ ++ ret = t4_fw_initialize(adapter, adapter->mbox); ++ if (ret < 0) ++ goto bye; ++ ++ /* ++ * Return successfully! ++ */ ++ dev_info(adapter->pdev_dev, "Successfully configured using built-in "\ ++ "driver parameters\n"); ++ return 0; ++ ++ /* ++ * Something bad happened. Return the error ... ++ */ ++bye: ++ return ret; ++} ++ ++/* + * Phase 0 of initialization: contact FW, obtain config, perform basic init. + */ + static int adap_init0(struct adapter *adap) +@@ -3474,7 +3781,9 @@ static int adap_init0(struct adapter *adap) + goto bye; + + /* +- * Find out what ports are available to us. ++ * Find out what ports are available to us. Note that we need to do ++ * this before calling adap_init0_no_config() since it needs nports ++ * and portvec ... + */ + v = + FW_PARAMS_MNEM(FW_PARAMS_MNEM_DEV) | +@@ -3500,35 +3809,52 @@ static int adap_init0(struct adapter *adap) + } else { + dev_info(adap->pdev_dev, "Coming up as MASTER: "\ + "Initializing adapter\n"); +- /* +- * Find out whether we're dealing with a version of +- * the firmware which has configuration file support. +- */ +- params[0] = (FW_PARAMS_MNEM(FW_PARAMS_MNEM_DEV) | +- FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DEV_CF)); +- ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 1, +- params, val); + + /* + * If the firmware doesn't support Configuration + * Files warn user and exit, + */ + if (ret < 0) +- dev_warn(adap->pdev_dev, "Firmware doesn't support "\ ++ dev_warn(adap->pdev_dev, "Firmware doesn't support " + "configuration file.\n"); ++ if (force_old_init) ++ ret = adap_init0_no_config(adap, reset); + else { + /* +- * The firmware provides us with a memory +- * buffer where we can load a Configuration +- * File from the host if we want to override +- * the Configuration File in flash. ++ * Find out whether we're dealing with a version of ++ * the firmware which has configuration file support. + */ ++ params[0] = (FW_PARAMS_MNEM(FW_PARAMS_MNEM_DEV) | ++ FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DEV_CF)); ++ ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 1, ++ params, val); + +- ret = adap_init0_config(adap, reset); +- if (ret == -ENOENT) { +- dev_info(adap->pdev_dev, +- "No Configuration File present " +- "on adapter.\n"); ++ /* ++ * If the firmware doesn't support Configuration ++ * Files, use the old Driver-based, hard-wired ++ * initialization. Otherwise, try using the ++ * Configuration File support and fall back to the ++ * Driver-based initialization if there's no ++ * Configuration File found. ++ */ ++ if (ret < 0) ++ ret = adap_init0_no_config(adap, reset); ++ else { ++ /* ++ * The firmware provides us with a memory ++ * buffer where we can load a Configuration ++ * File from the host if we want to override ++ * the Configuration File in flash. ++ */ ++ ++ ret = adap_init0_config(adap, reset); ++ if (ret == -ENOENT) { ++ dev_info(adap->pdev_dev, ++ "No Configuration File present " ++ "on adapter. Using hard-wired " ++ "configuration parameters.\n"); ++ ret = adap_init0_no_config(adap, reset); ++ } + } + } + if (ret < 0) { +@@ -3601,14 +3927,14 @@ static int adap_init0(struct adapter *adap) + */ + memset(&caps_cmd, 0, sizeof(caps_cmd)); + caps_cmd.op_to_write = htonl(V_FW_CMD_OP(FW_CAPS_CONFIG_CMD) | +- F_FW_CMD_REQUEST | F_FW_CMD_READ); +- caps_cmd.cfvalid_to_len16 = htonl(FW_LEN16(caps_cmd)); ++ FW_CMD_REQUEST | FW_CMD_READ); ++ caps_cmd.retval_len16 = htonl(FW_LEN16(caps_cmd)); + ret = t4_wr_mbox(adap, adap->mbox, &caps_cmd, sizeof(caps_cmd), + &caps_cmd); + if (ret < 0) + goto bye; + +- if (caps_cmd.toecaps) { ++ if (caps_cmd.ofldcaps) { + /* query offload-related parameters */ + params[0] = FW_PARAM_DEV(NTID); + params[1] = FW_PARAM_PFVF(SERVER_START); +diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +index 419432d..61f002d 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c ++++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +@@ -120,6 +120,28 @@ static void t4_read_indirect(struct adapter *adap, unsigned int addr_reg, + } + } + ++/** ++ * t4_write_indirect - write indirectly addressed registers ++ * @adap: the adapter ++ * @addr_reg: register holding the indirect addresses ++ * @data_reg: register holding the value for the indirect registers ++ * @vals: values to write ++ * @nregs: how many indirect registers to write ++ * @start_idx: address of first indirect register to write ++ * ++ * Writes a sequential block of registers that are accessed indirectly ++ * through an address/data register pair. ++ */ ++void t4_write_indirect(struct adapter *adap, unsigned int addr_reg, ++ unsigned int data_reg, const u32 *vals, ++ unsigned int nregs, unsigned int start_idx) ++{ ++ while (nregs--) { ++ t4_write_reg(adap, addr_reg, start_idx++); ++ t4_write_reg(adap, data_reg, *vals++); ++ } ++} ++ + /* + * Get the reply to a mailbox command and store it in @rpl in big-endian order. + */ +diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +index 779b23f..732c6da 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h ++++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +@@ -491,6 +491,13 @@ + #define VLANEXTENABLE_MASK 0x0000f000U + #define VLANEXTENABLE_SHIFT 12 + ++#define TP_GLOBAL_CONFIG 0x7d08 ++#define FIVETUPLELOOKUP_SHIFT 17 ++#define FIVETUPLELOOKUP_MASK 0x00060000U ++#define FIVETUPLELOOKUP(x) ((x) << FIVETUPLELOOKUP_SHIFT) ++#define FIVETUPLELOOKUP_GET(x) (((x) & FIVETUPLELOOKUP_MASK) >> \ ++ FIVETUPLELOOKUP_SHIFT) ++ + #define TP_PARA_REG2 0x7d68 + #define MAXRXDATA_MASK 0xffff0000U + #define MAXRXDATA_SHIFT 16 +@@ -506,6 +513,41 @@ + (((x) & DELAYEDACKRESOLUTION_MASK) >> DELAYEDACKRESOLUTION_SHIFT) + + #define TP_SHIFT_CNT 0x7dc0 ++#define SYNSHIFTMAX_SHIFT 24 ++#define SYNSHIFTMAX_MASK 0xff000000U ++#define SYNSHIFTMAX(x) ((x) << SYNSHIFTMAX_SHIFT) ++#define SYNSHIFTMAX_GET(x) (((x) & SYNSHIFTMAX_MASK) >> \ ++ SYNSHIFTMAX_SHIFT) ++#define RXTSHIFTMAXR1_SHIFT 20 ++#define RXTSHIFTMAXR1_MASK 0x00f00000U ++#define RXTSHIFTMAXR1(x) ((x) << RXTSHIFTMAXR1_SHIFT) ++#define RXTSHIFTMAXR1_GET(x) (((x) & RXTSHIFTMAXR1_MASK) >> \ ++ RXTSHIFTMAXR1_SHIFT) ++#define RXTSHIFTMAXR2_SHIFT 16 ++#define RXTSHIFTMAXR2_MASK 0x000f0000U ++#define RXTSHIFTMAXR2(x) ((x) << RXTSHIFTMAXR2_SHIFT) ++#define RXTSHIFTMAXR2_GET(x) (((x) & RXTSHIFTMAXR2_MASK) >> \ ++ RXTSHIFTMAXR2_SHIFT) ++#define PERSHIFTBACKOFFMAX_SHIFT 12 ++#define PERSHIFTBACKOFFMAX_MASK 0x0000f000U ++#define PERSHIFTBACKOFFMAX(x) ((x) << PERSHIFTBACKOFFMAX_SHIFT) ++#define PERSHIFTBACKOFFMAX_GET(x) (((x) & PERSHIFTBACKOFFMAX_MASK) >> \ ++ PERSHIFTBACKOFFMAX_SHIFT) ++#define PERSHIFTMAX_SHIFT 8 ++#define PERSHIFTMAX_MASK 0x00000f00U ++#define PERSHIFTMAX(x) ((x) << PERSHIFTMAX_SHIFT) ++#define PERSHIFTMAX_GET(x) (((x) & PERSHIFTMAX_MASK) >> \ ++ PERSHIFTMAX_SHIFT) ++#define KEEPALIVEMAXR1_SHIFT 4 ++#define KEEPALIVEMAXR1_MASK 0x000000f0U ++#define KEEPALIVEMAXR1(x) ((x) << KEEPALIVEMAXR1_SHIFT) ++#define KEEPALIVEMAXR1_GET(x) (((x) & KEEPALIVEMAXR1_MASK) >> \ ++ KEEPALIVEMAXR1_SHIFT) ++#define KEEPALIVEMAXR2_SHIFT 0 ++#define KEEPALIVEMAXR2_MASK 0x0000000fU ++#define KEEPALIVEMAXR2(x) ((x) << KEEPALIVEMAXR2_SHIFT) ++#define KEEPALIVEMAXR2_GET(x) (((x) & KEEPALIVEMAXR2_MASK) >> \ ++ KEEPALIVEMAXR2_SHIFT) + + #define TP_CCTRL_TABLE 0x7ddc + #define TP_MTU_TABLE 0x7de4 +@@ -539,6 +581,20 @@ + #define TP_INT_CAUSE 0x7e74 + #define FLMTXFLSTEMPTY 0x40000000U + ++#define TP_VLAN_PRI_MAP 0x140 ++#define FRAGMENTATION_SHIFT 9 ++#define FRAGMENTATION_MASK 0x00000200U ++#define MPSHITTYPE_MASK 0x00000100U ++#define MACMATCH_MASK 0x00000080U ++#define ETHERTYPE_MASK 0x00000040U ++#define PROTOCOL_MASK 0x00000020U ++#define TOS_MASK 0x00000010U ++#define VLAN_MASK 0x00000008U ++#define VNIC_ID_MASK 0x00000004U ++#define PORT_MASK 0x00000002U ++#define FCOE_SHIFT 0 ++#define FCOE_MASK 0x00000001U ++ + #define TP_INGRESS_CONFIG 0x141 + #define VNIC 0x00000800U + #define CSUM_HAS_PSEUDO_HDR 0x00000400U +diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +index 3f85019..b1d5561 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h ++++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +@@ -79,6 +79,8 @@ struct fw_wr_hdr { + #define FW_WR_FLOWID(x) ((x) << 8) + #define FW_WR_LEN16(x) ((x) << 0) + ++#define HW_TPL_FR_MT_PR_IV_P_FC 0X32B ++ + struct fw_ulptx_wr { + __be32 op_to_compl; + __be32 flowid_len16; +-- +1.7.1 + diff --git a/linux-next-cherry-picks/0007-cxgb4-Inform-caller-if-driver-didn-t-upgrade-firmwar.patch b/linux-next-cherry-picks/0007-cxgb4-Inform-caller-if-driver-didn-t-upgrade-firmwar.patch new file mode 100644 index 0000000..1c6ba3c --- /dev/null +++ b/linux-next-cherry-picks/0007-cxgb4-Inform-caller-if-driver-didn-t-upgrade-firmwar.patch @@ -0,0 +1,39 @@ +From 1648a22b16ab05bd99dd8e2f33ca80bbcea60031 Mon Sep 17 00:00:00 2001 +From: Vipul Pandya +Date: Wed, 26 Sep 2012 02:39:41 +0000 +Subject: [PATCH 5/6] cxgb4: Inform caller if driver didn't upgrade firmware + +If a card had already been initialized, on reloading cxgb4 driver firmware +required an upgrade but the upgrade did not happen. In that case a mailbox +timeout would occur during T4 configuration file stuff. The fix is to let the +caller know the firmware was not upgraded so a reset would be issued before +starting the T4 config stuff. + +Signed-off-by: Jay Hernandez +Signed-off-by: Vipul Pandya +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 6 ++++++ + 1 files changed, 6 insertions(+), 0 deletions(-) + +diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +index b9cd08d..a3f866d 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c ++++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +@@ -933,7 +933,13 @@ static int upgrade_fw(struct adapter *adap) + if (!ret) + dev_info(dev, "firmware upgraded to version %pI4 from " + FW_FNAME "\n", &hdr->fw_ver); ++ } else { ++ /* ++ * Tell our caller that we didn't upgrade the firmware. ++ */ ++ ret = -EINVAL; + } ++ + out: release_firmware(fw); + return ret; + } +-- +1.7.1 + diff --git a/linux-next-cherry-picks/0008-cxgb4-Don-t-attempt-to-upgrade-T4-firmware-when-cxgb.patch b/linux-next-cherry-picks/0008-cxgb4-Don-t-attempt-to-upgrade-T4-firmware-when-cxgb.patch new file mode 100644 index 0000000..8f5803a --- /dev/null +++ b/linux-next-cherry-picks/0008-cxgb4-Don-t-attempt-to-upgrade-T4-firmware-when-cxgb.patch @@ -0,0 +1,323 @@ +From 26f7cbc0a5a42d8cc0c7725d10317089a8215f97 Mon Sep 17 00:00:00 2001 +From: Vipul Pandya +Date: Wed, 26 Sep 2012 02:39:42 +0000 +Subject: [PATCH 6/6] cxgb4: Don't attempt to upgrade T4 firmware when cxgb4 will end up as a slave + +This patch adds a new common code routine to upgrade an adapter's +firmware. This routine handles all of the complexities of working with the +the existing adapter firmware in order to quiesce the adapter and uP, etc. +For an automatic upgrade it will send a HELLO command to check if cxgb4 +want/can upgrade firmware, i.e. if cxgb4 is MASTER and has newer firmware +that it wants to load and call the new common code routine t4_fw_upgrade. +Note that it should not issue a RESET command after a successful firmware +upgrade. + +Signed-off-by: Jay Hernandez +Signed-off-by: Vipul Pandya +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 4 + + drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 14 ++- + drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 171 +++++++++++++++++++++++ + drivers/net/ethernet/chelsio/cxgb4/t4_regs.h | 3 + + drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h | 19 +++- + 5 files changed, 206 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +index 6827ce3..745a1f5 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h ++++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +@@ -726,6 +726,10 @@ int t4_fw_hello(struct adapter *adap, unsigned int mbox, unsigned int evt_mbox, + int t4_fw_bye(struct adapter *adap, unsigned int mbox); + int t4_early_init(struct adapter *adap, unsigned int mbox); + int t4_fw_reset(struct adapter *adap, unsigned int mbox, int reset); ++int t4_fw_halt(struct adapter *adap, unsigned int mbox, int force); ++int t4_fw_restart(struct adapter *adap, unsigned int mbox, int reset); ++int t4_fw_upgrade(struct adapter *adap, unsigned int mbox, ++ const u8 *fw_data, unsigned int size, int force); + int t4_fw_config_file(struct adapter *adap, unsigned int mbox, + unsigned int mtype, unsigned int maddr, + u32 *finiver, u32 *finicsum, u32 *cfcsum); +diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +index a3f866d..94b7846 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c ++++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +@@ -929,10 +929,18 @@ static int upgrade_fw(struct adapter *adap) + */ + if (FW_HDR_FW_VER_MAJOR_GET(adap->params.fw_vers) != FW_VERSION_MAJOR || + vers > adap->params.fw_vers) { +- ret = -t4_load_fw(adap, fw->data, fw->size); ++ dev_info(dev, "upgrading firmware ...\n"); ++ ret = t4_fw_upgrade(adap, adap->mbox, fw->data, fw->size, ++ /*force=*/false); + if (!ret) +- dev_info(dev, "firmware upgraded to version %pI4 from " +- FW_FNAME "\n", &hdr->fw_ver); ++ dev_info(dev, "firmware successfully upgraded to " ++ FW_FNAME " (%d.%d.%d.%d)\n", ++ FW_HDR_FW_VER_MAJOR_GET(vers), ++ FW_HDR_FW_VER_MINOR_GET(vers), ++ FW_HDR_FW_VER_MICRO_GET(vers), ++ FW_HDR_FW_VER_BUILD_GET(vers)); ++ else ++ dev_err(dev, "firmware upgrade failed! err=%d\n", -ret); + } else { + /* + * Tell our caller that we didn't upgrade the firmware. +diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +index 61f002d..ab732b3 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c ++++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +@@ -2537,6 +2537,177 @@ int t4_fw_reset(struct adapter *adap, unsigned int mbox, int reset) + } + + /** ++ * t4_fw_halt - issue a reset/halt to FW and put uP into RESET ++ * @adap: the adapter ++ * @mbox: mailbox to use for the FW RESET command (if desired) ++ * @force: force uP into RESET even if FW RESET command fails ++ * ++ * Issues a RESET command to firmware (if desired) with a HALT indication ++ * and then puts the microprocessor into RESET state. The RESET command ++ * will only be issued if a legitimate mailbox is provided (mbox <= ++ * FW_PCIE_FW_MASTER_MASK). ++ * ++ * This is generally used in order for the host to safely manipulate the ++ * adapter without fear of conflicting with whatever the firmware might ++ * be doing. The only way out of this state is to RESTART the firmware ++ * ... ++ */ ++int t4_fw_halt(struct adapter *adap, unsigned int mbox, int force) ++{ ++ int ret = 0; ++ ++ /* ++ * If a legitimate mailbox is provided, issue a RESET command ++ * with a HALT indication. ++ */ ++ if (mbox <= FW_PCIE_FW_MASTER_MASK) { ++ struct fw_reset_cmd c; ++ ++ memset(&c, 0, sizeof(c)); ++ INIT_CMD(c, RESET, WRITE); ++ c.val = htonl(PIORST | PIORSTMODE); ++ c.halt_pkd = htonl(FW_RESET_CMD_HALT(1U)); ++ ret = t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); ++ } ++ ++ /* ++ * Normally we won't complete the operation if the firmware RESET ++ * command fails but if our caller insists we'll go ahead and put the ++ * uP into RESET. This can be useful if the firmware is hung or even ++ * missing ... We'll have to take the risk of putting the uP into ++ * RESET without the cooperation of firmware in that case. ++ * ++ * We also force the firmware's HALT flag to be on in case we bypassed ++ * the firmware RESET command above or we're dealing with old firmware ++ * which doesn't have the HALT capability. This will serve as a flag ++ * for the incoming firmware to know that it's coming out of a HALT ++ * rather than a RESET ... if it's new enough to understand that ... ++ */ ++ if (ret == 0 || force) { ++ t4_set_reg_field(adap, CIM_BOOT_CFG, UPCRST, UPCRST); ++ t4_set_reg_field(adap, PCIE_FW, FW_PCIE_FW_HALT, ++ FW_PCIE_FW_HALT); ++ } ++ ++ /* ++ * And we always return the result of the firmware RESET command ++ * even when we force the uP into RESET ... ++ */ ++ return ret; ++} ++ ++/** ++ * t4_fw_restart - restart the firmware by taking the uP out of RESET ++ * @adap: the adapter ++ * @reset: if we want to do a RESET to restart things ++ * ++ * Restart firmware previously halted by t4_fw_halt(). On successful ++ * return the previous PF Master remains as the new PF Master and there ++ * is no need to issue a new HELLO command, etc. ++ * ++ * We do this in two ways: ++ * ++ * 1. If we're dealing with newer firmware we'll simply want to take ++ * the chip's microprocessor out of RESET. This will cause the ++ * firmware to start up from its start vector. And then we'll loop ++ * until the firmware indicates it's started again (PCIE_FW.HALT ++ * reset to 0) or we timeout. ++ * ++ * 2. If we're dealing with older firmware then we'll need to RESET ++ * the chip since older firmware won't recognize the PCIE_FW.HALT ++ * flag and automatically RESET itself on startup. ++ */ ++int t4_fw_restart(struct adapter *adap, unsigned int mbox, int reset) ++{ ++ if (reset) { ++ /* ++ * Since we're directing the RESET instead of the firmware ++ * doing it automatically, we need to clear the PCIE_FW.HALT ++ * bit. ++ */ ++ t4_set_reg_field(adap, PCIE_FW, FW_PCIE_FW_HALT, 0); ++ ++ /* ++ * If we've been given a valid mailbox, first try to get the ++ * firmware to do the RESET. If that works, great and we can ++ * return success. Otherwise, if we haven't been given a ++ * valid mailbox or the RESET command failed, fall back to ++ * hitting the chip with a hammer. ++ */ ++ if (mbox <= FW_PCIE_FW_MASTER_MASK) { ++ t4_set_reg_field(adap, CIM_BOOT_CFG, UPCRST, 0); ++ msleep(100); ++ if (t4_fw_reset(adap, mbox, ++ PIORST | PIORSTMODE) == 0) ++ return 0; ++ } ++ ++ t4_write_reg(adap, PL_RST, PIORST | PIORSTMODE); ++ msleep(2000); ++ } else { ++ int ms; ++ ++ t4_set_reg_field(adap, CIM_BOOT_CFG, UPCRST, 0); ++ for (ms = 0; ms < FW_CMD_MAX_TIMEOUT; ) { ++ if (!(t4_read_reg(adap, PCIE_FW) & FW_PCIE_FW_HALT)) ++ return 0; ++ msleep(100); ++ ms += 100; ++ } ++ return -ETIMEDOUT; ++ } ++ return 0; ++} ++ ++/** ++ * t4_fw_upgrade - perform all of the steps necessary to upgrade FW ++ * @adap: the adapter ++ * @mbox: mailbox to use for the FW RESET command (if desired) ++ * @fw_data: the firmware image to write ++ * @size: image size ++ * @force: force upgrade even if firmware doesn't cooperate ++ * ++ * Perform all of the steps necessary for upgrading an adapter's ++ * firmware image. Normally this requires the cooperation of the ++ * existing firmware in order to halt all existing activities ++ * but if an invalid mailbox token is passed in we skip that step ++ * (though we'll still put the adapter microprocessor into RESET in ++ * that case). ++ * ++ * On successful return the new firmware will have been loaded and ++ * the adapter will have been fully RESET losing all previous setup ++ * state. On unsuccessful return the adapter may be completely hosed ... ++ * positive errno indicates that the adapter is ~probably~ intact, a ++ * negative errno indicates that things are looking bad ... ++ */ ++int t4_fw_upgrade(struct adapter *adap, unsigned int mbox, ++ const u8 *fw_data, unsigned int size, int force) ++{ ++ const struct fw_hdr *fw_hdr = (const struct fw_hdr *)fw_data; ++ int reset, ret; ++ ++ ret = t4_fw_halt(adap, mbox, force); ++ if (ret < 0 && !force) ++ return ret; ++ ++ ret = t4_load_fw(adap, fw_data, size); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * Older versions of the firmware don't understand the new ++ * PCIE_FW.HALT flag and so won't know to perform a RESET when they ++ * restart. So for newly loaded older firmware we'll have to do the ++ * RESET for it so it starts up on a clean slate. We can tell if ++ * the newly loaded firmware will handle this right by checking ++ * its header flags to see if it advertises the capability. ++ */ ++ reset = ((ntohl(fw_hdr->flags) & FW_HDR_FLAGS_RESET_HALT) == 0); ++ return t4_fw_restart(adap, mbox, reset); ++} ++ ++ ++/** + * t4_fw_config_file - setup an adapter via a Configuration File + * @adap: the adapter + * @mbox: mailbox to use for the FW command +diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +index 732c6da..a1a8b57 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h ++++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +@@ -321,6 +321,8 @@ + #define WINDOW(x) ((x) << WINDOW_SHIFT) + #define PCIE_MEM_ACCESS_OFFSET 0x306c + ++#define PCIE_FW 0x30b8 ++ + #define PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS 0x5908 + #define RNPP 0x80000000U + #define RPCP 0x20000000U +@@ -419,6 +421,7 @@ + + #define CIM_BOOT_CFG 0x7b00 + #define BOOTADDR_MASK 0xffffff00U ++#define UPCRST 0x1U + + #define CIM_PF_MAILBOX_DATA 0x240 + #define CIM_PF_MAILBOX_CTRL 0x280 +diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +index b1d5561..a636463 100644 +--- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h ++++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +@@ -317,9 +317,15 @@ struct fw_reset_cmd { + __be32 op_to_write; + __be32 retval_len16; + __be32 val; +- __be32 r3; ++ __be32 halt_pkd; + }; + ++#define FW_RESET_CMD_HALT_SHIFT 31 ++#define FW_RESET_CMD_HALT_MASK 0x1 ++#define FW_RESET_CMD_HALT(x) ((x) << FW_RESET_CMD_HALT_SHIFT) ++#define FW_RESET_CMD_HALT_GET(x) \ ++ (((x) >> FW_RESET_CMD_HALT_SHIFT) & FW_RESET_CMD_HALT_MASK) ++ + enum fw_hellow_cmd { + fw_hello_cmd_stage_os = 0x0 + }; +@@ -1648,6 +1654,7 @@ struct fw_debug_cmd { + + #define FW_PCIE_FW_ERR (1U << 31) + #define FW_PCIE_FW_INIT (1U << 30) ++#define FW_PCIE_FW_HALT (1U << 29) + #define FW_PCIE_FW_MASTER_VLD (1U << 15) + #define FW_PCIE_FW_MASTER_MASK 0x7 + #define FW_PCIE_FW_MASTER_SHIFT 12 +@@ -1669,7 +1676,11 @@ struct fw_hdr { + u8 intfver_iscsi; + u8 intfver_fcoe; + u8 reserved2; +- __be32 reserved3[27]; ++ __u32 reserved3; ++ __u32 reserved4; ++ __u32 reserved5; ++ __be32 flags; ++ __be32 reserved6[23]; + }; + + #define FW_HDR_FW_VER_MAJOR_GET(x) (((x) >> 24) & 0xff) +@@ -1677,4 +1688,8 @@ struct fw_hdr { + #define FW_HDR_FW_VER_MICRO_GET(x) (((x) >> 8) & 0xff) + #define FW_HDR_FW_VER_BUILD_GET(x) (((x) >> 0) & 0xff) + ++enum fw_hdr_flags { ++ FW_HDR_FLAGS_RESET_HALT = 0x00000001, ++}; ++ + #endif /* _T4FW_INTERFACE_H_ */ +-- +1.7.1 + -- 2.41.0