diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-06 20:38:14 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-06 20:38:14 +0400 |
commit | ae045e2455429c418a418a3376301a9e5753a0a8 (patch) | |
tree | b445bdeecd3f38aa0d0a29c9585cee49e4ccb0f1 /drivers/net/ethernet/chelsio | |
parent | f4f142ed4ef835709c7e6d12eaca10d190bcebed (diff) | |
parent | d247b6ab3ce6dd43665780865ec5fa145d9ab6bd (diff) | |
download | linux-ae045e2455429c418a418a3376301a9e5753a0a8.tar.xz |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller:
"Highlights:
1) Steady transitioning of the BPF instructure to a generic spot so
all kernel subsystems can make use of it, from Alexei Starovoitov.
2) SFC driver supports busy polling, from Alexandre Rames.
3) Take advantage of hash table in UDP multicast delivery, from David
Held.
4) Lighten locking, in particular by getting rid of the LRU lists, in
inet frag handling. From Florian Westphal.
5) Add support for various RFC6458 control messages in SCTP, from
Geir Ola Vaagland.
6) Allow to filter bridge forwarding database dumps by device, from
Jamal Hadi Salim.
7) virtio-net also now supports busy polling, from Jason Wang.
8) Some low level optimization tweaks in pktgen from Jesper Dangaard
Brouer.
9) Add support for ipv6 address generation modes, so that userland
can have some input into the process. From Jiri Pirko.
10) Consolidate common TCP connection request code in ipv4 and ipv6,
from Octavian Purdila.
11) New ARP packet logger in netfilter, from Pablo Neira Ayuso.
12) Generic resizable RCU hash table, with intial users in netlink and
nftables. From Thomas Graf.
13) Maintain a name assignment type so that userspace can see where a
network device name came from (enumerated by kernel, assigned
explicitly by userspace, etc.) From Tom Gundersen.
14) Automatic flow label generation on transmit in ipv6, from Tom
Herbert.
15) New packet timestamping facilities from Willem de Bruijn, meant to
assist in measuring latencies going into/out-of the packet
scheduler, latency from TCP data transmission to ACK, etc"
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1536 commits)
cxgb4 : Disable recursive mailbox commands when enabling vi
net: reduce USB network driver config options.
tg3: Modify tg3_tso_bug() to handle multiple TX rings
amd-xgbe: Perform phy connect/disconnect at dev open/stop
amd-xgbe: Use dma_set_mask_and_coherent to set DMA mask
net: sun4i-emac: fix memory leak on bad packet
sctp: fix possible seqlock seadlock in sctp_packet_transmit()
Revert "net: phy: Set the driver when registering an MDIO bus device"
cxgb4vf: Turn off SGE RX/TX Callback Timers and interrupts in PCI shutdown routine
team: Simplify return path of team_newlink
bridge: Update outdated comment on promiscuous mode
net-timestamp: ACK timestamp for bytestreams
net-timestamp: TCP timestamping
net-timestamp: SCHED timestamp on entering packet scheduler
net-timestamp: add key to disambiguate concurrent datagrams
net-timestamp: move timestamp flags out of sk_flags
net-timestamp: extend SCM_TIMESTAMPING ancillary data struct
cxgb4i : Move stray CPL definitions to cxgb4 driver
tcp: reduce spurious retransmits due to transient SACK reneging
qlcnic: Initialize dcbnl_ops before register_netdev
...
Diffstat (limited to 'drivers/net/ethernet/chelsio')
-rw-r--r-- | drivers/net/ethernet/chelsio/Kconfig | 11 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 37 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c | 971 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.h | 141 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 487 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h | 14 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/l2t.c | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/l2t.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/sge.c | 37 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 364 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/t4_hw.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/t4_msg.h | 7 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/t4_regs.h | 17 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h | 99 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c | 38 |
16 files changed, 1960 insertions, 270 deletions
diff --git a/drivers/net/ethernet/chelsio/Kconfig b/drivers/net/ethernet/chelsio/Kconfig index 570222c33410..c3ce9df0041a 100644 --- a/drivers/net/ethernet/chelsio/Kconfig +++ b/drivers/net/ethernet/chelsio/Kconfig @@ -86,6 +86,17 @@ config CHELSIO_T4 To compile this driver as a module choose M here; the module will be called cxgb4. +config CHELSIO_T4_DCB + bool "Data Center Bridging (DCB) Support for Chelsio T4/T5 cards" + default n + depends on CHELSIO_T4 && DCB + ---help--- + Enable DCB support through rtNetlink interface. + Say Y here if you want to enable Data Center Bridging (DCB) support + in the driver. + + If unsure, say N. + config CHELSIO_T4VF tristate "Chelsio Communications T4/T5 Virtual Function Ethernet support" depends on PCI diff --git a/drivers/net/ethernet/chelsio/cxgb4/Makefile b/drivers/net/ethernet/chelsio/cxgb4/Makefile index 498667487f52..1df65c915b99 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/Makefile +++ b/drivers/net/ethernet/chelsio/cxgb4/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_CHELSIO_T4) += cxgb4.o cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o +cxgb4-$(CONFIG_CHELSIO_T4_DCB) += cxgb4_dcb.o diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index f503dce4ab17..c9b922cc3e67 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -1,7 +1,7 @@ /* * This file is part of the Chelsio T4 Ethernet driver for Linux. * - * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved. + * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -85,7 +85,8 @@ enum { MEMWIN1_BASE_T5 = 0x52000, MEMWIN2_APERTURE = 65536, MEMWIN2_BASE = 0x30000, - MEMWIN2_BASE_T5 = 0x54000, + MEMWIN2_APERTURE_T5 = 131072, + MEMWIN2_BASE_T5 = 0x60000, }; enum dev_master { @@ -309,6 +310,9 @@ struct adapter_params { unsigned int ofldq_wr_cred; bool ulptx_memwrite_dsgl; /* use of T5 DSGL allowed */ + + unsigned int max_ordird_qp; /* Max read depth per RDMA QP */ + unsigned int max_ird_adapter; /* Max read depth per adapter */ }; #include "t4fw_api.h" @@ -373,6 +377,8 @@ enum { struct adapter; struct sge_rspq; +#include "cxgb4_dcb.h" + struct port_info { struct adapter *adapter; u16 viid; @@ -389,6 +395,9 @@ struct port_info { u8 rss_mode; struct link_config link_cfg; u16 *rss; +#ifdef CONFIG_CHELSIO_T4_DCB + struct port_dcb_info dcb; /* Data Center Bridging support */ +#endif }; struct dentry; @@ -603,6 +612,7 @@ struct l2t_data; struct adapter { void __iomem *regs; void __iomem *bar2; + u32 t4_bar0; struct pci_dev *pdev; struct device *pdev_dev; unsigned int mbox; @@ -647,6 +657,7 @@ struct adapter { struct dentry *debugfs_root; spinlock_t stats_lock; + spinlock_t win0_lock ____cacheline_aligned_in_smp; }; /* Defined bit width of user definable filter tuples @@ -855,6 +866,7 @@ void t4_os_link_changed(struct adapter *adap, int port_id, int link_stat); void *t4_alloc_mem(size_t size); void t4_free_sge_resources(struct adapter *adap); +void t4_free_ofld_rxqs(struct adapter *adap, int n, struct sge_ofld_rxq *q); irq_handler_t t4_intr_handler(struct adapter *adap); netdev_tx_t t4_eth_xmit(struct sk_buff *skb, struct net_device *dev); int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, @@ -941,6 +953,7 @@ void t4_write_indirect(struct adapter *adap, unsigned int addr_reg, void t4_read_indirect(struct adapter *adap, unsigned int addr_reg, unsigned int data_reg, u32 *vals, unsigned int nregs, unsigned int start_idx); +void t4_hw_pci_read_cfg4(struct adapter *adapter, int reg, u32 *val); struct fw_filter_wr; @@ -952,8 +965,17 @@ 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); + +#define T4_MEMORY_WRITE 0 +#define T4_MEMORY_READ 1 +int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr, u32 len, + __be32 *buf, int dir); +static inline int t4_memory_write(struct adapter *adap, int mtype, u32 addr, + u32 len, __be32 *buf) +{ + return t4_memory_rw(adap, 0, mtype, addr, len, buf, 0); +} + 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); @@ -1007,6 +1029,10 @@ int t4_query_params(struct adapter *adap, unsigned int mbox, unsigned int pf, int t4_set_params(struct adapter *adap, unsigned int mbox, unsigned int pf, unsigned int vf, unsigned int nparams, const u32 *params, const u32 *val); +int t4_set_params_nosleep(struct adapter *adap, unsigned int mbox, + unsigned int pf, unsigned int vf, + unsigned int nparams, const u32 *params, + const u32 *val); int t4_cfg_pfvf(struct adapter *adap, unsigned int mbox, unsigned int pf, unsigned int vf, unsigned int txq, unsigned int txq_eth_ctrl, unsigned int rxqi, unsigned int rxq, unsigned int tc, @@ -1025,6 +1051,8 @@ int t4_change_mac(struct adapter *adap, unsigned int mbox, unsigned int viid, int idx, const u8 *addr, bool persist, bool add_smt); int t4_set_addr_hash(struct adapter *adap, unsigned int mbox, unsigned int viid, bool ucast, u64 vec, bool sleep_ok); +int t4_enable_vi_params(struct adapter *adap, unsigned int mbox, + unsigned int viid, bool rx_en, bool tx_en, bool dcb_en); int t4_enable_vi(struct adapter *adap, unsigned int mbox, unsigned int viid, bool rx_en, bool tx_en); int t4_identify_port(struct adapter *adap, unsigned int mbox, unsigned int viid, @@ -1045,7 +1073,6 @@ int t4_ofld_eq_free(struct adapter *adap, unsigned int mbox, unsigned int pf, int t4_handle_fw_rpl(struct adapter *adap, const __be64 *rpl); void t4_db_full(struct adapter *adapter); void t4_db_dropped(struct adapter *adapter); -int t4_mem_win_read_len(struct adapter *adap, u32 addr, __be32 *data, int len); int t4_fwaddrspace_write(struct adapter *adap, unsigned int mbox, u32 addr, u32 val); void t4_sge_decode_idma_state(struct adapter *adapter, int state); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c new file mode 100644 index 000000000000..0d3a9df5be36 --- /dev/null +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c @@ -0,0 +1,971 @@ +/* + * Copyright (C) 2013-2014 Chelsio Communications. All rights reserved. + * + * Written by Anish Bhatt (anish@chelsio.com) + * Casey Leedom (leedom@chelsio.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + */ + +#include "cxgb4.h" + +/* Initialize a port's Data Center Bridging state. Typically used after a + * Link Down event. + */ +void cxgb4_dcb_state_init(struct net_device *dev) +{ + struct port_info *pi = netdev2pinfo(dev); + struct port_dcb_info *dcb = &pi->dcb; + + memset(dcb, 0, sizeof(struct port_dcb_info)); + dcb->state = CXGB4_DCB_STATE_START; +} + +/* Finite State machine for Data Center Bridging. + */ +void cxgb4_dcb_state_fsm(struct net_device *dev, + enum cxgb4_dcb_state_input input) +{ + struct port_info *pi = netdev2pinfo(dev); + struct port_dcb_info *dcb = &pi->dcb; + struct adapter *adap = pi->adapter; + + switch (input) { + case CXGB4_DCB_INPUT_FW_DISABLED: { + /* Firmware tells us it's not doing DCB */ + switch (dcb->state) { + case CXGB4_DCB_STATE_START: { + /* we're going to use Host DCB */ + dcb->state = CXGB4_DCB_STATE_HOST; + dcb->supported = CXGB4_DCBX_HOST_SUPPORT; + dcb->enabled = 1; + break; + } + + case CXGB4_DCB_STATE_HOST: { + /* we're alreaady in Host DCB mode */ + break; + } + + default: + goto bad_state_transition; + } + break; + } + + case CXGB4_DCB_INPUT_FW_ENABLED: { + /* Firmware tells us that it is doing DCB */ + switch (dcb->state) { + case CXGB4_DCB_STATE_START: { + /* we're going to use Firmware DCB */ + dcb->state = CXGB4_DCB_STATE_FW_INCOMPLETE; + dcb->supported = CXGB4_DCBX_FW_SUPPORT; + break; + } + + case CXGB4_DCB_STATE_FW_INCOMPLETE: + case CXGB4_DCB_STATE_FW_ALLSYNCED: { + /* we're alreaady in firmware DCB mode */ + break; + } + + default: + goto bad_state_transition; + } + break; + } + + case CXGB4_DCB_INPUT_FW_INCOMPLETE: { + /* Firmware tells us that its DCB state is incomplete */ + switch (dcb->state) { + case CXGB4_DCB_STATE_FW_INCOMPLETE: { + /* we're already incomplete */ + break; + } + + case CXGB4_DCB_STATE_FW_ALLSYNCED: { + /* We were successfully running with firmware DCB but + * now it's telling us that it's in an "incomplete + * state. We need to reset back to a ground state + * of incomplete. + */ + cxgb4_dcb_state_init(dev); + dcb->state = CXGB4_DCB_STATE_FW_INCOMPLETE; + dcb->supported = CXGB4_DCBX_FW_SUPPORT; + linkwatch_fire_event(dev); + break; + } + + default: + goto bad_state_transition; + } + break; + } + + case CXGB4_DCB_INPUT_FW_ALLSYNCED: { + /* Firmware tells us that its DCB state is complete */ + switch (dcb->state) { + case CXGB4_DCB_STATE_FW_INCOMPLETE: { + dcb->state = CXGB4_DCB_STATE_FW_ALLSYNCED; + dcb->enabled = 1; + linkwatch_fire_event(dev); + break; + } + + case CXGB4_DCB_STATE_FW_ALLSYNCED: { + /* we're already all sync'ed */ + break; + } + + default: + goto bad_state_transition; + } + break; + } + + default: + goto bad_state_input; + } + return; + +bad_state_input: + dev_err(adap->pdev_dev, "cxgb4_dcb_state_fsm: illegal input symbol %d\n", + input); + return; + +bad_state_transition: + dev_err(adap->pdev_dev, "cxgb4_dcb_state_fsm: bad state transition, state = %d, input = %d\n", + dcb->state, input); +} + +/* Handle a DCB/DCBX update message from the firmware. + */ +void cxgb4_dcb_handle_fw_update(struct adapter *adap, + const struct fw_port_cmd *pcmd) +{ + const union fw_port_dcb *fwdcb = &pcmd->u.dcb; + int port = FW_PORT_CMD_PORTID_GET(be32_to_cpu(pcmd->op_to_portid)); + struct net_device *dev = adap->port[port]; + struct port_info *pi = netdev_priv(dev); + struct port_dcb_info *dcb = &pi->dcb; + int dcb_type = pcmd->u.dcb.pgid.type; + + /* Handle Firmware DCB Control messages separately since they drive + * our state machine. + */ + if (dcb_type == FW_PORT_DCB_TYPE_CONTROL) { + enum cxgb4_dcb_state_input input = + ((pcmd->u.dcb.control.all_syncd_pkd & + FW_PORT_CMD_ALL_SYNCD) + ? CXGB4_DCB_STATE_FW_ALLSYNCED + : CXGB4_DCB_STATE_FW_INCOMPLETE); + + cxgb4_dcb_state_fsm(dev, input); + return; + } + + /* It's weird, and almost certainly an error, to get Firmware DCB + * messages when we either haven't been told whether we're going to be + * doing Host or Firmware DCB; and even worse when we've been told + * that we're doing Host DCB! + */ + if (dcb->state == CXGB4_DCB_STATE_START || + dcb->state == CXGB4_DCB_STATE_HOST) { + dev_err(adap->pdev_dev, "Receiving Firmware DCB messages in State %d\n", + dcb->state); + return; + } + + /* Now handle the general Firmware DCB update messages ... + */ + switch (dcb_type) { + case FW_PORT_DCB_TYPE_PGID: + dcb->pgid = be32_to_cpu(fwdcb->pgid.pgid); + dcb->msgs |= CXGB4_DCB_FW_PGID; + break; + + case FW_PORT_DCB_TYPE_PGRATE: + dcb->pg_num_tcs_supported = fwdcb->pgrate.num_tcs_supported; + memcpy(dcb->pgrate, &fwdcb->pgrate.pgrate, + sizeof(dcb->pgrate)); + dcb->msgs |= CXGB4_DCB_FW_PGRATE; + break; + + case FW_PORT_DCB_TYPE_PRIORATE: + memcpy(dcb->priorate, &fwdcb->priorate.strict_priorate, + sizeof(dcb->priorate)); + dcb->msgs |= CXGB4_DCB_FW_PRIORATE; + break; + + case FW_PORT_DCB_TYPE_PFC: + dcb->pfcen = fwdcb->pfc.pfcen; + dcb->pfc_num_tcs_supported = fwdcb->pfc.max_pfc_tcs; + dcb->msgs |= CXGB4_DCB_FW_PFC; + break; + + case FW_PORT_DCB_TYPE_APP_ID: { + const struct fw_port_app_priority *fwap = &fwdcb->app_priority; + int idx = fwap->idx; + struct app_priority *ap = &dcb->app_priority[idx]; + + struct dcb_app app = { + .selector = fwap->sel_field, + .protocol = be16_to_cpu(fwap->protocolid), + .priority = fwap->user_prio_map, + }; + int err; + + err = dcb_setapp(dev, &app); + if (err) + dev_err(adap->pdev_dev, + "Failed DCB Set Application Priority: sel=%d, prot=%d, prio=%d, err=%d\n", + app.selector, app.protocol, app.priority, -err); + + ap->user_prio_map = fwap->user_prio_map; + ap->sel_field = fwap->sel_field; + ap->protocolid = be16_to_cpu(fwap->protocolid); + dcb->msgs |= CXGB4_DCB_FW_APP_ID; + break; + } + + default: + dev_err(adap->pdev_dev, "Unknown DCB update type received %x\n", + dcb_type); + break; + } +} + +/* Data Center Bridging netlink operations. + */ + + +/* Get current DCB enabled/disabled state. + */ +static u8 cxgb4_getstate(struct net_device *dev) +{ + struct port_info *pi = netdev2pinfo(dev); + + return pi->dcb.enabled; +} + +/* Set DCB enabled/disabled. + */ +static u8 cxgb4_setstate(struct net_device *dev, u8 enabled) +{ + struct port_info *pi = netdev2pinfo(dev); + + /* Firmware doesn't provide any mechanism to control the DCB state. + */ + if (enabled != (pi->dcb.state == CXGB4_DCB_STATE_FW_ALLSYNCED)) + return 1; + + return 0; +} + +static void cxgb4_getpgtccfg(struct net_device *dev, int tc, + u8 *prio_type, u8 *pgid, u8 *bw_per, + u8 *up_tc_map, int local) +{ + struct fw_port_cmd pcmd; + struct port_info *pi = netdev2pinfo(dev); + struct adapter *adap = pi->adapter; + int err; + + *prio_type = *pgid = *bw_per = *up_tc_map = 0; + + if (local) + INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); + else + INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); + + pcmd.u.dcb.pgid.type = FW_PORT_DCB_TYPE_PGID; + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + if (err != FW_PORT_DCB_CFG_SUCCESS) { + dev_err(adap->pdev_dev, "DCB read PGID failed with %d\n", -err); + return; + } + *pgid = (be32_to_cpu(pcmd.u.dcb.pgid.pgid) >> (tc * 4)) & 0xf; + + INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); + pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + if (err != FW_PORT_DCB_CFG_SUCCESS) { + dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n", + -err); + return; + } + + *bw_per = pcmd.u.dcb.pgrate.pgrate[*pgid]; + *up_tc_map = (1 << tc); + + /* prio_type is link strict */ + *prio_type = 0x2; +} + +static void cxgb4_getpgtccfg_tx(struct net_device *dev, int tc, + u8 *prio_type, u8 *pgid, u8 *bw_per, + u8 *up_tc_map) +{ + return cxgb4_getpgtccfg(dev, tc, prio_type, pgid, bw_per, up_tc_map, 1); +} + + +static void cxgb4_getpgtccfg_rx(struct net_device *dev, int tc, + u8 *prio_type, u8 *pgid, u8 *bw_per, + u8 *up_tc_map) +{ + return cxgb4_getpgtccfg(dev, tc, prio_type, pgid, bw_per, up_tc_map, 0); +} + +static void cxgb4_setpgtccfg_tx(struct net_device *dev, int tc, + u8 prio_type, u8 pgid, u8 bw_per, + u8 up_tc_map) +{ + struct fw_port_cmd pcmd; + struct port_info *pi = netdev2pinfo(dev); + struct adapter *adap = pi->adapter; + u32 _pgid; + int err; + + if (pgid == DCB_ATTR_VALUE_UNDEFINED) + return; + if (bw_per == DCB_ATTR_VALUE_UNDEFINED) + return; + + INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); + pcmd.u.dcb.pgid.type = FW_PORT_DCB_TYPE_PGID; + + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + if (err != FW_PORT_DCB_CFG_SUCCESS) { + dev_err(adap->pdev_dev, "DCB read PGID failed with %d\n", -err); + return; + } + + _pgid = be32_to_cpu(pcmd.u.dcb.pgid.pgid); + _pgid &= ~(0xF << (tc * 4)); + _pgid |= pgid << (tc * 4); + pcmd.u.dcb.pgid.pgid = cpu_to_be32(_pgid); + + INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id); + + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + if (err != FW_PORT_DCB_CFG_SUCCESS) { + dev_err(adap->pdev_dev, "DCB write PGID failed with %d\n", + -err); + return; + } + + memset(&pcmd, 0, sizeof(struct fw_port_cmd)); + + INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); + pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; + + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + if (err != FW_PORT_DCB_CFG_SUCCESS) { + dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n", + -err); + return; + } + + pcmd.u.dcb.pgrate.pgrate[pgid] = bw_per; + + INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id); + if (pi->dcb.state == CXGB4_DCB_STATE_HOST) + pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY); + + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + if (err != FW_PORT_DCB_CFG_SUCCESS) + dev_err(adap->pdev_dev, "DCB write PGRATE failed with %d\n", + -err); +} + +static void cxgb4_getpgbwgcfg(struct net_device *dev, int pgid, u8 *bw_per, + int local) +{ + struct fw_port_cmd pcmd; + struct port_info *pi = netdev2pinfo(dev); + struct adapter *adap = pi->adapter; + int err; + + if (local) + INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); + else + INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); + + pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + if (err != FW_PORT_DCB_CFG_SUCCESS) { + dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n", + -err); + } else { + *bw_per = pcmd.u.dcb.pgrate.pgrate[pgid]; + } +} + +static void cxgb4_getpgbwgcfg_tx(struct net_device *dev, int pgid, u8 *bw_per) +{ + return cxgb4_getpgbwgcfg(dev, pgid, bw_per, 1); +} + +static void cxgb4_getpgbwgcfg_rx(struct net_device *dev, int pgid, u8 *bw_per) +{ + return cxgb4_getpgbwgcfg(dev, pgid, bw_per, 0); +} + +static void cxgb4_setpgbwgcfg_tx(struct net_device *dev, int pgid, + u8 bw_per) +{ + struct fw_port_cmd pcmd; + struct port_info *pi = netdev2pinfo(dev); + struct adapter *adap = pi->adapter; + int err; + + INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); + pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; + + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + if (err != FW_PORT_DCB_CFG_SUCCESS) { + dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n", + -err); + return; + } + + pcmd.u.dcb.pgrate.pgrate[pgid] = bw_per; + + INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id); + if (pi->dcb.state == CXGB4_DCB_STATE_HOST) + pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY); + + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + + if (err != FW_PORT_DCB_CFG_SUCCESS) + dev_err(adap->pdev_dev, "DCB write PGRATE failed with %d\n", + -err); +} + +/* Return whether the specified Traffic Class Priority has Priority Pause + * Frames enabled. + */ +static void cxgb4_getpfccfg(struct net_device *dev, int priority, u8 *pfccfg) +{ + struct port_info *pi = netdev2pinfo(dev); + struct port_dcb_info *dcb = &pi->dcb; + + if (dcb->state != CXGB4_DCB_STATE_FW_ALLSYNCED || + priority >= CXGB4_MAX_PRIORITY) + *pfccfg = 0; + else + *pfccfg = (pi->dcb.pfcen >> priority) & 1; +} + +/* Enable/disable Priority Pause Frames for the specified Traffic Class + * Priority. + */ +static void cxgb4_setpfccfg(struct net_device *dev, int priority, u8 pfccfg) +{ + struct fw_port_cmd pcmd; + struct port_info *pi = netdev2pinfo(dev); + struct adapter *adap = pi->adapter; + int err; + + if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED || + priority >= CXGB4_MAX_PRIORITY) + return; + + INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id); + if (pi->dcb.state == CXGB4_DCB_STATE_HOST) + pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY); + + pcmd.u.dcb.pfc.type = FW_PORT_DCB_TYPE_PFC; + pcmd.u.dcb.pfc.pfcen = pi->dcb.pfcen; + + if (pfccfg) + pcmd.u.dcb.pfc.pfcen |= (1 << priority); + else + pcmd.u.dcb.pfc.pfcen &= (~(1 << priority)); + + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + if (err != FW_PORT_DCB_CFG_SUCCESS) { + dev_err(adap->pdev_dev, "DCB PFC write failed with %d\n", -err); + return; + } + + pi->dcb.pfcen = pcmd.u.dcb.pfc.pfcen; +} + +static u8 cxgb4_setall(struct net_device *dev) +{ + return 0; +} + +/* Return DCB capabilities. + */ +static u8 cxgb4_getcap(struct net_device *dev, int cap_id, u8 *caps) +{ + struct port_info *pi = netdev2pinfo(dev); + + switch (cap_id) { + case DCB_CAP_ATTR_PG: + case DCB_CAP_ATTR_PFC: + *caps = true; + break; + + case DCB_CAP_ATTR_PG_TCS: + /* 8 priorities for PG represented by bitmap */ + *caps = 0x80; + break; + + case DCB_CAP_ATTR_PFC_TCS: + /* 8 priorities for PFC represented by bitmap */ + *caps = 0x80; + break; + + case DCB_CAP_ATTR_GSP: + *caps = true; + break; + + case DCB_CAP_ATTR_UP2TC: + case DCB_CAP_ATTR_BCN: + *caps = false; + break; + + case DCB_CAP_ATTR_DCBX: + *caps = pi->dcb.supported; + break; + + default: + *caps = false; + } + + return 0; +} + +/* Return the number of Traffic Classes for the indicated Traffic Class ID. + */ +static int cxgb4_getnumtcs(struct net_device *dev, int tcs_id, u8 *num) +{ + struct port_info *pi = netdev2pinfo(dev); + + switch (tcs_id) { + case DCB_NUMTCS_ATTR_PG: + if (pi->dcb.msgs & CXGB4_DCB_FW_PGRATE) + *num = pi->dcb.pg_num_tcs_supported; + else + *num = 0x8; + break; + + case DCB_NUMTCS_ATTR_PFC: + *num = 0x8; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* Set the number of Traffic Classes supported for the indicated Traffic Class + * ID. + */ +static int cxgb4_setnumtcs(struct net_device *dev, int tcs_id, u8 num) +{ + /* Setting the number of Traffic Classes isn't supported. + */ + return -ENOSYS; +} + +/* Return whether Priority Flow Control is enabled. */ +static u8 cxgb4_getpfcstate(struct net_device *dev) +{ + struct port_info *pi = netdev2pinfo(dev); + + if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED) + return false; + + return pi->dcb.pfcen != 0; +} + +/* Enable/disable Priority Flow Control. */ +static void cxgb4_setpfcstate(struct net_device *dev, u8 state) +{ + /* We can't enable/disable Priority Flow Control but we also can't + * return an error ... + */ +} + +/* Return the Application User Priority Map associated with the specified + * Application ID. + */ +static int __cxgb4_getapp(struct net_device *dev, u8 app_idtype, u16 app_id, + int peer) +{ + struct port_info *pi = netdev2pinfo(dev); + struct adapter *adap = pi->adapter; + int i; + + if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED) + return 0; + + for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) { + struct fw_port_cmd pcmd; + int err; + + if (peer) + INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); + else + INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); + + pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID; + pcmd.u.dcb.app_priority.idx = i; + + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + if (err != FW_PORT_DCB_CFG_SUCCESS) { + dev_err(adap->pdev_dev, "DCB APP read failed with %d\n", + -err); + return err; + } + if (be16_to_cpu(pcmd.u.dcb.app_priority.protocolid) == app_id) + return pcmd.u.dcb.app_priority.user_prio_map; + + /* exhausted app list */ + if (!pcmd.u.dcb.app_priority.protocolid) + break; + } + + return -EEXIST; +} + +/* Return the Application User Priority Map associated with the specified + * Application ID. + */ +static int cxgb4_getapp(struct net_device *dev, u8 app_idtype, u16 app_id) +{ + return __cxgb4_getapp(dev, app_idtype, app_id, 0); +} + +/* Write a new Application User Priority Map for the specified Application ID + */ +static int cxgb4_setapp(struct net_device *dev, u8 app_idtype, u16 app_id, + u8 app_prio) +{ + struct fw_port_cmd pcmd; + struct port_info *pi = netdev2pinfo(dev); + struct adapter *adap = pi->adapter; + int i, err; + + + if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED) + return -EINVAL; + + /* DCB info gets thrown away on link up */ + if (!netif_carrier_ok(dev)) + return -ENOLINK; + + if (app_idtype != DCB_APP_IDTYPE_ETHTYPE && + app_idtype != DCB_APP_IDTYPE_PORTNUM) + return -EINVAL; + + for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) { + INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); + pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID; + pcmd.u.dcb.app_priority.idx = i; + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + + if (err != FW_PORT_DCB_CFG_SUCCESS) { + dev_err(adap->pdev_dev, "DCB app table read failed with %d\n", + -err); + return err; + } + if (be16_to_cpu(pcmd.u.dcb.app_priority.protocolid) == app_id) { + /* overwrite existing app table */ + pcmd.u.dcb.app_priority.protocolid = 0; + break; + } + /* find first empty slot */ + if (!pcmd.u.dcb.app_priority.protocolid) + break; + } + + if (i == CXGB4_MAX_DCBX_APP_SUPPORTED) { + /* no empty slots available */ + dev_err(adap->pdev_dev, "DCB app table full\n"); + return -EBUSY; + } + + /* write out new app table entry */ + INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id); + if (pi->dcb.state == CXGB4_DCB_STATE_HOST) + pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY); + + pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID; + pcmd.u.dcb.app_priority.protocolid = cpu_to_be16(app_id); + pcmd.u.dcb.app_priority.sel_field = app_idtype; + pcmd.u.dcb.app_priority.user_prio_map = app_prio; + pcmd.u.dcb.app_priority.idx = i; + + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + if (err != FW_PORT_DCB_CFG_SUCCESS) { + dev_err(adap->pdev_dev, "DCB app table write failed with %d\n", + -err); + return err; + } + + return 0; +} + +/* Return whether IEEE Data Center Bridging has been negotiated. + */ +static inline int cxgb4_ieee_negotiation_complete(struct net_device *dev) +{ + struct port_info *pi = netdev2pinfo(dev); + struct port_dcb_info *dcb = &pi->dcb; + + return (dcb->state == CXGB4_DCB_STATE_FW_ALLSYNCED && + (dcb->supported & DCB_CAP_DCBX_VER_IEEE)); +} + +/* Fill in the Application User Priority Map associated with the + * specified Application. + */ +static int cxgb4_ieee_getapp(struct net_device *dev, struct dcb_app *app) +{ + int prio; + + if (!cxgb4_ieee_negotiation_complete(dev)) + return -EINVAL; + if (!(app->selector && app->protocol)) + return -EINVAL; + + prio = dcb_getapp(dev, app); + if (prio == 0) { + /* If app doesn't exist in dcb_app table, try firmware + * directly. + */ + prio = __cxgb4_getapp(dev, app->selector, app->protocol, 0); + } + + app->priority = prio; + return 0; +} + +/* Write a new Application User Priority Map for the specified App id. */ +static int cxgb4_ieee_setapp(struct net_device *dev, struct dcb_app *app) +{ + if (!cxgb4_ieee_negotiation_complete(dev)) + return -EINVAL; + if (!(app->selector && app->protocol && app->priority)) + return -EINVAL; + + cxgb4_setapp(dev, app->selector, app->protocol, app->priority); + return dcb_setapp(dev, app); +} + +/* Return our DCBX parameters. + */ +static u8 cxgb4_getdcbx(struct net_device *dev) +{ + struct port_info *pi = netdev2pinfo(dev); + + /* This is already set by cxgb4_set_dcb_caps, so just return it */ + return pi->dcb.supported; +} + +/* Set our DCBX parameters. + */ +static u8 cxgb4_setdcbx(struct net_device *dev, u8 dcb_request) +{ + struct port_info *pi = netdev2pinfo(dev); + + /* Filter out requests which exceed our capabilities. + */ + if ((dcb_request & (CXGB4_DCBX_FW_SUPPORT | CXGB4_DCBX_HOST_SUPPORT)) + != dcb_request) + return 1; + + /* Can't set DCBX capabilities if DCBX isn't enabled. */ + if (!pi->dcb.state) + return 1; + + /* There's currently no mechanism to allow for the firmware DCBX + * negotiation to be changed from the Host Driver. If the caller + * requests exactly the same parameters that we already have then + * we'll allow them to be successfully "set" ... + */ + if (dcb_request != pi->dcb.supported) + return 1; + + pi->dcb.supported = dcb_request; + return 0; +} + +static int cxgb4_getpeer_app(struct net_device *dev, + struct dcb_peer_app_info *info, u16 *app_count) +{ + struct fw_port_cmd pcmd; + struct port_info *pi = netdev2pinfo(dev); + struct adapter *adap = pi->adapter; + int i, err = 0; + + if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED) + return 1; + + info->willing = 0; + info->error = 0; + + *app_count = 0; + for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) { + INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); + pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID; + pcmd.u.dcb.app_priority.idx = *app_count; + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + + if (err != FW_PORT_DCB_CFG_SUCCESS) { + dev_err(adap->pdev_dev, "DCB app table read failed with %d\n", + -err); + return err; + } + + /* find first empty slot */ + if (!pcmd.u.dcb.app_priority.protocolid) + break; + } + *app_count = i; + return err; +} + +static int cxgb4_getpeerapp_tbl(struct net_device *dev, struct dcb_app *table) +{ + struct fw_port_cmd pcmd; + struct port_info *pi = netdev2pinfo(dev); + struct adapter *adap = pi->adapter; + int i, err = 0; + + if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED) + return 1; + + for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) { + INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); + pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID; + pcmd.u.dcb.app_priority.idx = i; + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + + if (err != FW_PORT_DCB_CFG_SUCCESS) { + dev_err(adap->pdev_dev, "DCB app table read failed with %d\n", + -err); + return err; + } + + /* find first empty slot */ + if (!pcmd.u.dcb.app_priority.protocolid) + break; + + table[i].selector = pcmd.u.dcb.app_priority.sel_field; + table[i].protocol = + be16_to_cpu(pcmd.u.dcb.app_priority.protocolid); + table[i].priority = pcmd.u.dcb.app_priority.user_prio_map; + } + return err; +} + +/* Return Priority Group information. + */ +static int cxgb4_cee_peer_getpg(struct net_device *dev, struct cee_pg *pg) +{ + struct fw_port_cmd pcmd; + struct port_info *pi = netdev2pinfo(dev); + struct adapter *adap = pi->adapter; + u32 pgid; + int i, err; + + /* We're always "willing" -- the Switch Fabric always dictates the + * DCBX parameters to us. + */ + pg->willing = true; + + INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); + pcmd.u.dcb.pgid.type = FW_PORT_DCB_TYPE_PGID; + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + if (err != FW_PORT_DCB_CFG_SUCCESS) { + dev_err(adap->pdev_dev, "DCB read PGID failed with %d\n", -err); + return err; + } + pgid = be32_to_cpu(pcmd.u.dcb.pgid.pgid); + + for (i = 0; i < CXGB4_MAX_PRIORITY; i++) + pg->prio_pg[i] = (pgid >> (i * 4)) & 0xF; + + INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); + pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + if (err != FW_PORT_DCB_CFG_SUCCESS) { + dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n", + -err); + return err; + } + + for (i = 0; i < CXGB4_MAX_PRIORITY; i++) + pg->pg_bw[i] = pcmd.u.dcb.pgrate.pgrate[i]; + + return 0; +} + +/* Return Priority Flow Control information. + */ +static int cxgb4_cee_peer_getpfc(struct net_device *dev, struct cee_pfc *pfc) +{ + struct port_info *pi = netdev2pinfo(dev); + + cxgb4_getnumtcs(dev, DCB_NUMTCS_ATTR_PFC, &(pfc->tcs_supported)); + pfc->pfc_en = pi->dcb.pfcen; + + return 0; +} + +const struct dcbnl_rtnl_ops cxgb4_dcb_ops = { + .ieee_getapp = cxgb4_ieee_getapp, + .ieee_setapp = cxgb4_ieee_setapp, + + /* CEE std */ + .getstate = cxgb4_getstate, + .setstate = cxgb4_setstate, + .getpgtccfgtx = cxgb4_getpgtccfg_tx, + .getpgbwgcfgtx = cxgb4_getpgbwgcfg_tx, + .getpgtccfgrx = cxgb4_getpgtccfg_rx, + .getpgbwgcfgrx = cxgb4_getpgbwgcfg_rx, + .setpgtccfgtx = cxgb4_setpgtccfg_tx, + .setpgbwgcfgtx = cxgb4_setpgbwgcfg_tx, + .setpfccfg = cxgb4_setpfccfg, + .getpfccfg = cxgb4_getpfccfg, + .setall = cxgb4_setall, + .getcap = cxgb4_getcap, + .getnumtcs = cxgb4_getnumtcs, + .setnumtcs = cxgb4_setnumtcs, + .getpfcstate = cxgb4_getpfcstate, + .setpfcstate = cxgb4_setpfcstate, + .getapp = cxgb4_getapp, + .setapp = cxgb4_setapp, + + /* DCBX configuration */ + .getdcbx = cxgb4_getdcbx, + .setdcbx = cxgb4_setdcbx, + + /* peer apps */ + .peer_getappinfo = cxgb4_getpeer_app, + .peer_getapptable = cxgb4_getpeerapp_tbl, + + /* CEE peer */ + .cee_peer_getpg = cxgb4_cee_peer_getpg, + .cee_peer_getpfc = cxgb4_cee_peer_getpfc, +}; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.h new file mode 100644 index 000000000000..1ec1d834e257 --- /dev/null +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2013-2014 Chelsio Communications. All rights reserved. + * + * Written by Anish Bhatt (anish@chelsio.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + */ + +#ifndef __CXGB4_DCB_H +#define __CXGB4_DCB_H + +#include <linux/netdevice.h> +#include <linux/dcbnl.h> +#include <net/dcbnl.h> + +#ifdef CONFIG_CHELSIO_T4_DCB + +#define CXGB4_DCBX_FW_SUPPORT \ + (DCB_CAP_DCBX_VER_CEE | \ + DCB_CAP_DCBX_VER_IEEE | \ + DCB_CAP_DCBX_LLD_MANAGED) +#define CXGB4_DCBX_HOST_SUPPORT \ + (DCB_CAP_DCBX_VER_CEE | \ + DCB_CAP_DCBX_VER_IEEE | \ + DCB_CAP_DCBX_HOST) + +#define CXGB4_MAX_PRIORITY CXGB4_MAX_DCBX_APP_SUPPORTED +#define CXGB4_MAX_TCS CXGB4_MAX_DCBX_APP_SUPPORTED + +#define INIT_PORT_DCB_CMD(__pcmd, __port, __op, __action) \ + do { \ + memset(&(__pcmd), 0, sizeof(__pcmd)); \ + (__pcmd).op_to_portid = \ + cpu_to_be32(FW_CMD_OP(FW_PORT_CMD) | \ + FW_CMD_REQUEST | \ + FW_CMD_##__op | \ + FW_PORT_CMD_PORTID(__port)); \ + (__pcmd).action_to_len16 = \ + cpu_to_be32(FW_PORT_CMD_ACTION(__action) | \ + FW_LEN16(pcmd)); \ + } while (0) + +#define INIT_PORT_DCB_READ_PEER_CMD(__pcmd, __port) \ + INIT_PORT_DCB_CMD(__pcmd, __port, READ, FW_PORT_ACTION_DCB_READ_RECV) + +#define INIT_PORT_DCB_READ_LOCAL_CMD(__pcmd, __port) \ + INIT_PORT_DCB_CMD(__pcmd, __port, READ, FW_PORT_ACTION_DCB_READ_TRANS) + +#define INIT_PORT_DCB_READ_SYNC_CMD(__pcmd, __port) \ + INIT_PORT_DCB_CMD(__pcmd, __port, READ, FW_PORT_ACTION_DCB_READ_DET) + +#define INIT_PORT_DCB_WRITE_CMD(__pcmd, __port) \ + INIT_PORT_DCB_CMD(__pcmd, __port, EXEC, FW_PORT_ACTION_L2_DCB_CFG) + +/* States we can be in for a port's Data Center Bridging. + */ +enum cxgb4_dcb_state { + CXGB4_DCB_STATE_START, /* initial unknown state */ + CXGB4_DCB_STATE_HOST, /* we're using Host DCB (if at all) */ + CXGB4_DCB_STATE_FW_INCOMPLETE, /* using firmware DCB, incomplete */ + CXGB4_DCB_STATE_FW_ALLSYNCED, /* using firmware DCB, all sync'ed */ +}; + +/* Data Center Bridging state input for the Finite State Machine. + */ +enum cxgb4_dcb_state_input { + /* Input from the firmware. + */ + CXGB4_DCB_INPUT_FW_DISABLED, /* firmware DCB disabled */ + CXGB4_DCB_INPUT_FW_ENABLED, /* firmware DCB enabled */ + CXGB4_DCB_INPUT_FW_INCOMPLETE, /* firmware reports incomplete DCB */ + CXGB4_DCB_INPUT_FW_ALLSYNCED, /* firmware reports all sync'ed */ + +}; + +/* Firmware DCB messages that we've received so far ... + */ +enum cxgb4_dcb_fw_msgs { + CXGB4_DCB_FW_PGID = 0x01, + CXGB4_DCB_FW_PGRATE = 0x02, + CXGB4_DCB_FW_PRIORATE = 0x04, + CXGB4_DCB_FW_PFC = 0x08, + CXGB4_DCB_FW_APP_ID = 0x10, +}; + +#define CXGB4_MAX_DCBX_APP_SUPPORTED 8 + +/* Data Center Bridging support; + */ +struct port_dcb_info { + enum cxgb4_dcb_state state; /* DCB State Machine */ + enum cxgb4_dcb_fw_msgs msgs; /* DCB Firmware messages received */ + unsigned int supported; /* OS DCB capabilities supported */ + bool enabled; /* OS Enabled state */ + + /* Cached copies of DCB information sent by the firmware (in Host + * Native Endian format). + */ + u32 pgid; /* Priority Group[0..7] */ + u8 pfcen; /* Priority Flow Control[0..7] */ + u8 pg_num_tcs_supported; /* max PG Traffic Classes */ + u8 pfc_num_tcs_supported; /* max PFC Traffic Classes */ + u8 pgrate[8]; /* Priority Group Rate[0..7] */ + u8 priorate[8]; /* Priority Rate[0..7] */ + struct app_priority { /* Application Information */ + u8 user_prio_map; /* Priority Map bitfield */ + u8 sel_field; /* Protocol ID interpretation */ + u16 protocolid; /* Protocol ID */ + } app_priority[CXGB4_MAX_DCBX_APP_SUPPORTED]; +}; + +void cxgb4_dcb_state_init(struct net_device *); +void cxgb4_dcb_state_fsm(struct net_device *, enum cxgb4_dcb_state_input); +void cxgb4_dcb_handle_fw_update(struct adapter *, const struct fw_port_cmd *); +void cxgb4_dcb_set_caps(struct adapter *, const struct fw_port_cmd *); +extern const struct dcbnl_rtnl_ops cxgb4_dcb_ops; + +#define CXGB4_DCB_ENABLED true + +#else /* !CONFIG_CHELSIO_T4_DCB */ + +static inline void cxgb4_dcb_state_init(struct net_device *dev) +{ +} + +#define CXGB4_DCB_ENABLED false + +#endif /* !CONFIG_CHELSIO_T4_DCB */ + +#endif /* __CXGB4_DCB_H */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index a83271cf17c3..4247356c16ff 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -1,7 +1,7 @@ /* * This file is part of the Chelsio T4 Ethernet driver for Linux. * - * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved. + * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -67,6 +67,7 @@ #include "t4_regs.h" #include "t4_msg.h" #include "t4fw_api.h" +#include "cxgb4_dcb.h" #include "l2t.h" #include <../drivers/net/bonding/bonding.h> @@ -223,6 +224,17 @@ static DEFINE_PCI_DEVICE_TABLE(cxgb4_pci_tbl) = { CH_DEVICE(0x4008, -1), CH_DEVICE(0x4009, -1), CH_DEVICE(0x400a, -1), + CH_DEVICE(0x400d, -1), + CH_DEVICE(0x400e, -1), + CH_DEVICE(0x4080, -1), + CH_DEVICE(0x4081, -1), + CH_DEVICE(0x4082, -1), + CH_DEVICE(0x4083, -1), + CH_DEVICE(0x4084, -1), + CH_DEVICE(0x4085, -1), + CH_DEVICE(0x4086, -1), + CH_DEVICE(0x4087, -1), + CH_DEVICE(0x4088, -1), CH_DEVICE(0x4401, 4), CH_DEVICE(0x4402, 4), CH_DEVICE(0x4403, 4), @@ -235,6 +247,15 @@ static DEFINE_PCI_DEVICE_TABLE(cxgb4_pci_tbl) = { CH_DEVICE(0x440a, 4), CH_DEVICE(0x440d, 4), CH_DEVICE(0x440e, 4), + CH_DEVICE(0x4480, 4), + CH_DEVICE(0x4481, 4), + CH_DEVICE(0x4482, 4), + CH_DEVICE(0x4483, 4), + CH_DEVICE(0x4484, 4), + CH_DEVICE(0x4485, 4), + CH_DEVICE(0x4486, 4), + CH_DEVICE(0x4487, 4), + CH_DEVICE(0x4488, 4), CH_DEVICE(0x5001, 4), CH_DEVICE(0x5002, 4), CH_DEVICE(0x5003, 4), @@ -391,6 +412,17 @@ module_param_array(num_vf, uint, NULL, 0644); MODULE_PARM_DESC(num_vf, "number of VFs for each of PFs 0-3"); #endif +/* TX Queue select used to determine what algorithm to use for selecting TX + * queue. Select between the kernel provided function (select_queue=0) or user + * cxgb_select_queue function (select_queue=1) + * + * Default: select_queue=0 + */ +static int select_queue; +module_param(select_queue, int, 0644); +MODULE_PARM_DESC(select_queue, + "Select between kernel provided method of selecting or driver method of selecting TX queue. Default is kernel method."); + /* * 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 @@ -458,6 +490,42 @@ static void link_report(struct net_device *dev) } } +#ifdef CONFIG_CHELSIO_T4_DCB +/* Set up/tear down Data Center Bridging Priority mapping for a net device. */ +static void dcb_tx_queue_prio_enable(struct net_device *dev, int enable) +{ + struct port_info *pi = netdev_priv(dev); + struct adapter *adap = pi->adapter; + struct sge_eth_txq *txq = &adap->sge.ethtxq[pi->first_qset]; + int i; + + /* We use a simple mapping of Port TX Queue Index to DCB + * Priority when we're enabling DCB. + */ + for (i = 0; i < pi->nqsets; i++, txq++) { + u32 name, value; + int err; + + name = (FW_PARAMS_MNEM(FW_PARAMS_MNEM_DMAQ) | + FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DMAQ_EQ_DCBPRIO_ETH) | + FW_PARAMS_PARAM_YZ(txq->q.cntxt_id)); + value = enable ? i : 0xffffffff; + + /* Since we can be called while atomic (from "interrupt + * level") we need to issue the Set Parameters Commannd + * without sleeping (timeout < 0). + */ + err = t4_set_params_nosleep(adap, adap->mbox, adap->fn, 0, 1, + &name, &value); + + if (err) + dev_err(adap->pdev_dev, + "Can't %s DCB Priority on port %d, TX Queue %d: err=%d\n", + enable ? "set" : "unset", pi->port_id, i, -err); + } +} +#endif /* CONFIG_CHELSIO_T4_DCB */ + void t4_os_link_changed(struct adapter *adapter, int port_id, int link_stat) { struct net_device *dev = adapter->port[port_id]; @@ -466,8 +534,13 @@ void t4_os_link_changed(struct adapter *adapter, int port_id, int link_stat) if (netif_running(dev) && link_stat != netif_carrier_ok(dev)) { if (link_stat) netif_carrier_on(dev); - else + else { +#ifdef CONFIG_CHELSIO_T4_DCB + cxgb4_dcb_state_init(dev); + dcb_tx_queue_prio_enable(dev, false); +#endif /* CONFIG_CHELSIO_T4_DCB */ netif_carrier_off(dev); + } link_report(dev); } @@ -600,11 +673,49 @@ static int link_start(struct net_device *dev) if (ret == 0) ret = t4_link_start(pi->adapter, mb, pi->tx_chan, &pi->link_cfg); - if (ret == 0) - ret = t4_enable_vi(pi->adapter, mb, pi->viid, true, true); + if (ret == 0) { + local_bh_disable(); + ret = t4_enable_vi_params(pi->adapter, mb, pi->viid, true, + true, CXGB4_DCB_ENABLED); + local_bh_enable(); + } + return ret; } +int cxgb4_dcb_enabled(const struct net_device *dev) +{ +#ifdef CONFIG_CHELSIO_T4_DCB + struct port_info *pi = netdev_priv(dev); + + return pi->dcb.state == CXGB4_DCB_STATE_FW_ALLSYNCED; +#else + return 0; +#endif +} +EXPORT_SYMBOL(cxgb4_dcb_enabled); + +#ifdef CONFIG_CHELSIO_T4_DCB +/* Handle a Data Center Bridging update message from the firmware. */ +static void dcb_rpl(struct adapter *adap, const struct fw_port_cmd *pcmd) +{ + int port = FW_PORT_CMD_PORTID_GET(ntohl(pcmd->op_to_portid)); + struct net_device *dev = adap->port[port]; + int old_dcb_enabled = cxgb4_dcb_enabled(dev); + int new_dcb_enabled; + + cxgb4_dcb_handle_fw_update(adap, pcmd); + new_dcb_enabled = cxgb4_dcb_enabled(dev); + + /* If the DCB has become enabled or disabled on the port then we're + * going to need to set up/tear down DCB Priority parameters for the + * TX Queues associated with the port. + */ + if (new_dcb_enabled != old_dcb_enabled) + dcb_tx_queue_prio_enable(dev, new_dcb_enabled); +} +#endif /* CONFIG_CHELSIO_T4_DCB */ + /* Clear a filter and release any of its resources that we own. This also * clears the filter's "pending" status. */ @@ -709,8 +820,32 @@ static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp, } else if (opcode == CPL_FW6_MSG || opcode == CPL_FW4_MSG) { const struct cpl_fw6_msg *p = (void *)rsp; - if (p->type == 0) - t4_handle_fw_rpl(q->adap, p->data); +#ifdef CONFIG_CHELSIO_T4_DCB + const struct fw_port_cmd *pcmd = (const void *)p->data; + unsigned int cmd = FW_CMD_OP_GET(ntohl(pcmd->op_to_portid)); + unsigned int action = + FW_PORT_CMD_ACTION_GET(ntohl(pcmd->action_to_len16)); + + if (cmd == FW_PORT_CMD && + action == FW_PORT_ACTION_GET_PORT_INFO) { + int port = FW_PORT_CMD_PORTID_GET( + be32_to_cpu(pcmd->op_to_portid)); + struct net_device *dev = q->adap->port[port]; + int state_input = ((pcmd->u.info.dcbxdis_pkd & + FW_PORT_CMD_DCBXDIS) + ? CXGB4_DCB_INPUT_FW_DISABLED + : CXGB4_DCB_INPUT_FW_ENABLED); + + cxgb4_dcb_state_fsm(dev, state_input); + } + + if (cmd == FW_PORT_CMD && + action == FW_PORT_ACTION_L2_DCB_CFG) + dcb_rpl(q->adap, pcmd); + else +#endif + if (p->type == 0) + t4_handle_fw_rpl(q->adap, p->data); } else if (opcode == CPL_L2T_WRITE_RPL) { const struct cpl_l2t_write_rpl *p = (void *)rsp; @@ -1290,6 +1425,48 @@ static int del_filter_wr(struct adapter *adapter, int fidx) return 0; } +static u16 cxgb_select_queue(struct net_device *dev, struct sk_buff *skb, + void *accel_priv, select_queue_fallback_t fallback) +{ + int txq; + +#ifdef CONFIG_CHELSIO_T4_DCB + /* If a Data Center Bridging has been successfully negotiated on this + * link then we'll use the skb's priority to map it to a TX Queue. + * The skb's priority is determined via the VLAN Tag Priority Code + * Point field. + */ + if (cxgb4_dcb_enabled(dev)) { + u16 vlan_tci; + int err; + + err = vlan_get_tag(skb, &vlan_tci); + if (unlikely(err)) { + if (net_ratelimit()) + netdev_warn(dev, + "TX Packet without VLAN Tag on DCB Link\n"); + txq = 0; + } else { + txq = (vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; + } + return txq; + } +#endif /* CONFIG_CHELSIO_T4_DCB */ + + if (select_queue) { + txq = (skb_rx_queue_recorded(skb) + ? skb_get_rx_queue(skb) + : smp_processor_id()); + + while (unlikely(txq >= dev->real_num_tx_queues)) + txq -= dev->real_num_tx_queues; + + return txq; + } + + return fallback(dev, skb) % dev->real_num_tx_queues; +} + static inline int is_offload(const struct adapter *adap) { return adap->params.offload; @@ -2912,6 +3089,8 @@ static ssize_t mem_read(struct file *file, char __user *buf, size_t count, loff_t avail = file_inode(file)->i_size; unsigned int mem = (uintptr_t)file->private_data & 3; struct adapter *adap = file->private_data - mem; + __be32 *data; + int ret; if (pos < 0) return -EINVAL; @@ -2920,29 +3099,24 @@ static ssize_t mem_read(struct file *file, char __user *buf, size_t count, if (count > avail - pos) count = avail - pos; - while (count) { - size_t len; - int ret, ofst; - __be32 data[16]; + data = t4_alloc_mem(count); + if (!data) + return -ENOMEM; - if ((mem == MEM_MC) || (mem == MEM_MC1)) - ret = t4_mc_read(adap, mem % MEM_MC, pos, data, NULL); - else - ret = t4_edc_read(adap, mem, pos, data, NULL); - if (ret) - return ret; + spin_lock(&adap->win0_lock); + ret = t4_memory_rw(adap, 0, mem, pos, count, data, T4_MEMORY_READ); + spin_unlock(&adap->win0_lock); + if (ret) { + t4_free_mem(data); + return ret; + } + ret = copy_to_user(buf, data, count); - ofst = pos % sizeof(data); - len = min(count, sizeof(data) - ofst); - if (copy_to_user(buf, (u8 *)data + ofst, len)) - return -EFAULT; + t4_free_mem(data); + if (ret) + return -EFAULT; - buf += len; - pos += len; - count -= len; - } - count = pos - *ppos; - *ppos = pos; + *ppos = pos + count; return count; } @@ -3274,8 +3448,8 @@ static int tid_init(struct tid_info *t) return 0; } -static int cxgb4_clip_get(const struct net_device *dev, - const struct in6_addr *lip) +int cxgb4_clip_get(const struct net_device *dev, + const struct in6_addr *lip) { struct adapter *adap; struct fw_clip_cmd c; @@ -3289,9 +3463,10 @@ static int cxgb4_clip_get(const struct net_device *dev, c.ip_lo = *(__be64 *)(lip->s6_addr + 8); return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false); } +EXPORT_SYMBOL(cxgb4_clip_get); -static int cxgb4_clip_release(const struct net_device *dev, - const struct in6_addr *lip) +int cxgb4_clip_release(const struct net_device *dev, + const struct in6_addr *lip) { struct adapter *adap; struct fw_clip_cmd c; @@ -3305,6 +3480,7 @@ static int cxgb4_clip_release(const struct net_device *dev, c.ip_lo = *(__be64 *)(lip->s6_addr + 8); return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false); } +EXPORT_SYMBOL(cxgb4_clip_release); /** * cxgb4_create_server - create an IP server @@ -3603,7 +3779,11 @@ static int read_eq_indices(struct adapter *adap, u16 qid, u16 *pidx, u16 *cidx) __be64 indices; int ret; - ret = t4_mem_win_read_len(adap, addr, (__be32 *)&indices, 8); + spin_lock(&adap->win0_lock); + ret = t4_memory_rw(adap, 0, MEM_EDC0, addr, + sizeof(indices), (__be32 *)&indices, + T4_MEMORY_READ); + spin_unlock(&adap->win0_lock); if (!ret) { *cidx = (be64_to_cpu(indices) >> 25) & 0xffff; *pidx = (be64_to_cpu(indices) >> 9) & 0xffff; @@ -3657,6 +3837,85 @@ void cxgb4_enable_db_coalescing(struct net_device *dev) } EXPORT_SYMBOL(cxgb4_enable_db_coalescing); +int cxgb4_read_tpte(struct net_device *dev, u32 stag, __be32 *tpte) +{ + struct adapter *adap; + u32 offset, memtype, memaddr; + u32 edc0_size, edc1_size, mc0_size, mc1_size; + u32 edc0_end, edc1_end, mc0_end, mc1_end; + int ret; + + adap = netdev2adap(dev); + + offset = ((stag >> 8) * 32) + adap->vres.stag.start; + + /* Figure out where the offset lands in the Memory Type/Address scheme. + * This code assumes that the memory is laid out starting at offset 0 + * with no breaks as: EDC0, EDC1, MC0, MC1. All cards have both EDC0 + * and EDC1. Some cards will have neither MC0 nor MC1, most cards have + * MC0, and some have both MC0 and MC1. + */ + edc0_size = EDRAM_SIZE_GET(t4_read_reg(adap, MA_EDRAM0_BAR)) << 20; + edc1_size = EDRAM_SIZE_GET(t4_read_reg(adap, MA_EDRAM1_BAR)) << 20; + mc0_size = EXT_MEM_SIZE_GET(t4_read_reg(adap, MA_EXT_MEMORY_BAR)) << 20; + + edc0_end = edc0_size; + edc1_end = edc0_end + edc1_size; + mc0_end = edc1_end + mc0_size; + + if (offset < edc0_end) { + memtype = MEM_EDC0; + memaddr = offset; + } else if (offset < edc1_end) { + memtype = MEM_EDC1; + memaddr = offset - edc0_end; + } else { + if (offset < mc0_end) { + memtype = MEM_MC0; + memaddr = offset - edc1_end; + } else if (is_t4(adap->params.chip)) { + /* T4 only has a single memory channel */ + goto err; + } else { + mc1_size = EXT_MEM_SIZE_GET( + t4_read_reg(adap, + MA_EXT_MEMORY1_BAR)) << 20; + mc1_end = mc0_end + mc1_size; + if (offset < mc1_end) { + memtype = MEM_MC1; + memaddr = offset - mc0_end; + } else { + /* offset beyond the end of any memory */ + goto err; + } + } + } + + spin_lock(&adap->win0_lock); + ret = t4_memory_rw(adap, 0, memtype, memaddr, 32, tpte, T4_MEMORY_READ); + spin_unlock(&adap->win0_lock); + return ret; + +err: + dev_err(adap->pdev_dev, "stag %#x, offset %#x out of range\n", + stag, offset); + return -EINVAL; +} +EXPORT_SYMBOL(cxgb4_read_tpte); + +u64 cxgb4_read_sge_timestamp(struct net_device *dev) +{ + u32 hi, lo; + struct adapter *adap; + + adap = netdev2adap(dev); + lo = t4_read_reg(adap, SGE_TIMESTAMP_LO); + hi = GET_TSVAL(t4_read_reg(adap, SGE_TIMESTAMP_HI)); + + return ((u64)hi << 32) | (u64)lo; +} +EXPORT_SYMBOL(cxgb4_read_sge_timestamp); + static struct pci_driver cxgb4_driver; static void check_neigh_update(struct neighbour *neigh) @@ -3899,6 +4158,7 @@ static void uld_attach(struct adapter *adap, unsigned int uld) unsigned short i; lli.pdev = adap->pdev; + lli.pf = adap->fn; lli.l2t = adap->l2t; lli.tids = &adap->tids; lli.ports = adap->port; @@ -3919,6 +4179,7 @@ static void uld_attach(struct adapter *adap, unsigned int uld) lli.wr_cred = adap->params.ofldq_wr_cred; lli.adapter_type = adap->params.chip; lli.iscsi_iolen = MAXRXDATA_GET(t4_read_reg(adap, TP_PARA_REG2)); + lli.cclk_ps = 1000000000 / adap->params.vpd.cclk; lli.udb_density = 1 << QUEUESPERPAGEPF0_GET( t4_read_reg(adap, SGE_EGRESS_QUEUES_PER_PAGE_PF) >> (adap->fn * 4)); @@ -3933,8 +4194,12 @@ static void uld_attach(struct adapter *adap, unsigned int uld) lli.db_reg = adap->regs + MYPF_REG(SGE_PF_KDOORBELL); lli.fw_vers = adap->params.fw_vers; lli.dbfifo_int_thresh = dbfifo_int_thresh; + lli.sge_ingpadboundary = adap->sge.fl_align; + lli.sge_egrstatuspagesize = adap->sge.stat_len; lli.sge_pktshift = adap->sge.pktshift; lli.enable_fw_ofld_conn = adap->flags & FW_OFLD_CONN; + lli.max_ordird_qp = adap->params.max_ordird_qp; + lli.max_ird_adapter = adap->params.max_ird_adapter; lli.ulptx_memwrite_dsgl = adap->params.ulptx_memwrite_dsgl; handle = ulds[uld].add(&lli); @@ -4598,6 +4863,7 @@ static const struct net_device_ops cxgb4_netdev_ops = { .ndo_open = cxgb_open, .ndo_stop = cxgb_close, .ndo_start_xmit = t4_eth_xmit, + .ndo_select_queue = cxgb_select_queue, .ndo_get_stats64 = cxgb_get_stats, .ndo_set_rx_mode = cxgb_set_rxmode, .ndo_set_mac_address = cxgb_set_mac_addr, @@ -4617,20 +4883,75 @@ void t4_fatal_err(struct adapter *adap) dev_alert(adap->pdev_dev, "encountered fatal error, adapter stopped\n"); } +/* Return the specified PCI-E Configuration Space register from our Physical + * Function. We try first via a Firmware LDST Command since we prefer to let + * the firmware own all of these registers, but if that fails we go for it + * directly ourselves. + */ +static u32 t4_read_pcie_cfg4(struct adapter *adap, int reg) +{ + struct fw_ldst_cmd ldst_cmd; + u32 val; + int ret; + + /* Construct and send the Firmware LDST Command to retrieve the + * specified PCI-E Configuration Space register. + */ + memset(&ldst_cmd, 0, sizeof(ldst_cmd)); + ldst_cmd.op_to_addrspace = + htonl(FW_CMD_OP(FW_LDST_CMD) | + FW_CMD_REQUEST | + FW_CMD_READ | + FW_LDST_CMD_ADDRSPACE(FW_LDST_ADDRSPC_FUNC_PCIE)); + ldst_cmd.cycles_to_len16 = htonl(FW_LEN16(ldst_cmd)); + ldst_cmd.u.pcie.select_naccess = FW_LDST_CMD_NACCESS(1); + ldst_cmd.u.pcie.ctrl_to_fn = + (FW_LDST_CMD_LC | FW_LDST_CMD_FN(adap->fn)); + ldst_cmd.u.pcie.r = reg; + ret = t4_wr_mbox(adap, adap->mbox, &ldst_cmd, sizeof(ldst_cmd), + &ldst_cmd); + + /* If the LDST Command suucceeded, exctract the returned register + * value. Otherwise read it directly ourself. + */ + if (ret == 0) + val = ntohl(ldst_cmd.u.pcie.data[0]); + else + t4_hw_pci_read_cfg4(adap, reg, &val); + + return val; +} + static void setup_memwin(struct adapter *adap) { - u32 bar0, mem_win0_base, mem_win1_base, mem_win2_base; + u32 mem_win0_base, mem_win1_base, mem_win2_base, mem_win2_aperture; - bar0 = pci_resource_start(adap->pdev, 0); /* truncation intentional */ if (is_t4(adap->params.chip)) { + u32 bar0; + + /* Truncation intentional: we only read the bottom 32-bits of + * the 64-bit BAR0/BAR1 ... We use the hardware backdoor + * mechanism to read BAR0 instead of using + * pci_resource_start() because we could be operating from + * within a Virtual Machine which is trapping our accesses to + * our Configuration Space and we need to set up the PCI-E + * Memory Window decoders with the actual addresses which will + * be coming across the PCI-E link. + */ + bar0 = t4_read_pcie_cfg4(adap, PCI_BASE_ADDRESS_0); + bar0 &= PCI_BASE_ADDRESS_MEM_MASK; + adap->t4_bar0 = bar0; + mem_win0_base = bar0 + MEMWIN0_BASE; mem_win1_base = bar0 + MEMWIN1_BASE; mem_win2_base = bar0 + MEMWIN2_BASE; + mem_win2_aperture = MEMWIN2_APERTURE; } else { /* For T5, only relative offset inside the PCIe BAR is passed */ mem_win0_base = MEMWIN0_BASE; - mem_win1_base = MEMWIN1_BASE_T5; + mem_win1_base = MEMWIN1_BASE; mem_win2_base = MEMWIN2_BASE_T5; + mem_win2_aperture = MEMWIN2_APERTURE_T5; } t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 0), mem_win0_base | BIR(0) | @@ -4640,16 +4961,19 @@ static void setup_memwin(struct adapter *adap) WINDOW(ilog2(MEMWIN1_APERTURE) - 10)); t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 2), mem_win2_base | BIR(0) | - WINDOW(ilog2(MEMWIN2_APERTURE) - 10)); + WINDOW(ilog2(mem_win2_aperture) - 10)); + t4_read_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 2)); } static void setup_memwin_rdma(struct adapter *adap) { if (adap->vres.ocq.size) { - unsigned int start, sz_kb; + u32 start; + unsigned int sz_kb; - start = pci_resource_start(adap->pdev, 2) + - OCQ_WIN_OFFSET(adap->pdev, &adap->vres); + start = t4_read_pcie_cfg4(adap, PCI_BASE_ADDRESS_2); + start &= PCI_BASE_ADDRESS_MEM_MASK; + start += OCQ_WIN_OFFSET(adap->pdev, &adap->vres); sz_kb = roundup_pow_of_two(adap->vres.ocq.size) >> 10; t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 3), @@ -4862,7 +5186,7 @@ static int adap_init0_config(struct adapter *adapter, int reset) adapter->fn, 0, 1, params, val); if (ret == 0) { /* - * For t4_memory_write() below addresses and + * For t4_memory_rw() 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 @@ -4878,8 +5202,9 @@ static int adap_init0_config(struct adapter *adapter, int reset) 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); + spin_lock(&adapter->win0_lock); + ret = t4_memory_rw(adapter, 0, mtype, maddr, + size, data, T4_MEMORY_WRITE); if (ret == 0 && resid != 0) { union { __be32 word; @@ -4890,10 +5215,12 @@ static int adap_init0_config(struct adapter *adapter, int reset) 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); + ret = t4_memory_rw(adapter, 0, mtype, + maddr + size, + 4, &last.word, + T4_MEMORY_WRITE); } + spin_unlock(&adapter->win0_lock); } } @@ -5637,6 +5964,22 @@ static int adap_init0(struct adapter *adap) adap->vres.cq.size = val[3] - val[2] + 1; adap->vres.ocq.start = val[4]; adap->vres.ocq.size = val[5] - val[4] + 1; + + params[0] = FW_PARAM_DEV(MAXORDIRD_QP); + params[1] = FW_PARAM_DEV(MAXIRD_ADAPTER); + ret = t4_query_params(adap, 0, 0, 0, 2, params, val); + if (ret < 0) { + adap->params.max_ordird_qp = 8; + adap->params.max_ird_adapter = 32 * adap->tids.ntids; + ret = 0; + } else { + adap->params.max_ordird_qp = val[0]; + adap->params.max_ird_adapter = val[1]; + } + dev_info(adap->pdev_dev, + "max_ordird_qp %d max_ird_adapter %d\n", + adap->params.max_ordird_qp, + adap->params.max_ird_adapter); } if (caps_cmd.iscsicaps) { params[0] = FW_PARAM_PFVF(ISCSI_START); @@ -5838,12 +6181,33 @@ static inline void init_rspq(struct adapter *adap, struct sge_rspq *q, static void cfg_queues(struct adapter *adap) { struct sge *s = &adap->sge; - int i, q10g = 0, n10g = 0, qidx = 0; + int i, n10g = 0, qidx = 0; +#ifndef CONFIG_CHELSIO_T4_DCB + int q10g = 0; +#endif int ciq_size; for_each_port(adap, i) n10g += is_x_10g_port(&adap2pinfo(adap, i)->link_cfg); +#ifdef CONFIG_CHELSIO_T4_DCB + /* For Data Center Bridging support we need to be able to support up + * to 8 Traffic Priorities; each of which will be assigned to its + * own TX Queue in order to prevent Head-Of-Line Blocking. + */ + if (adap->params.nports * 8 > MAX_ETH_QSETS) { + dev_err(adap->pdev_dev, "MAX_ETH_QSETS=%d < %d!\n", + MAX_ETH_QSETS, adap->params.nports * 8); + BUG_ON(1); + } + for_each_port(adap, i) { + struct port_info *pi = adap2pinfo(adap, i); + + pi->first_qset = qidx; + pi->nqsets = 8; + qidx += pi->nqsets; + } +#else /* !CONFIG_CHELSIO_T4_DCB */ /* * We default to 1 queue per non-10G port and up to # of cores queues * per 10G port. @@ -5860,6 +6224,7 @@ static void cfg_queues(struct adapter *adap) pi->nqsets = is_x_10g_port(&pi->link_cfg) ? q10g : 1; qidx += pi->nqsets; } +#endif /* !CONFIG_CHELSIO_T4_DCB */ s->ethqsets = qidx; s->max_ethqsets = qidx; /* MSI-X may lower it later */ @@ -5978,8 +6343,14 @@ static int enable_msix(struct adapter *adap) /* need nchan for each possible ULD */ ofld_need = 3 * nchan; } +#ifdef CONFIG_CHELSIO_T4_DCB + /* For Data Center Bridging we need 8 Ethernet TX Priority Queues for + * each port. + */ + need = 8 * adap->params.nports + EXTRA_VECS + ofld_need; +#else need = adap->params.nports + EXTRA_VECS + ofld_need; - +#endif want = pci_enable_msix_range(adap->pdev, entries, need, want); if (want < 0) return want; @@ -6111,13 +6482,6 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return err; } - /* We control everything through one PF */ - func = PCI_FUNC(pdev->devfn); - if (func != ent->driver_data) { - pci_save_state(pdev); /* to restore SR-IOV later */ - goto sriov; - } - err = pci_enable_device(pdev); if (err) { dev_err(&pdev->dev, "cannot enable PCI device\n"); @@ -6161,6 +6525,15 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_free_adapter; } + /* We control everything through one PF */ + func = SOURCEPF_GET(readl(adapter->regs + PL_WHOAMI)); + if ((pdev->device == 0xa000 && func != 0) || + func != ent->driver_data) { + pci_save_state(pdev); /* to restore SR-IOV later */ + err = 0; + goto out_unmap_bar0; + } + adapter->pdev = pdev; adapter->pdev_dev = &pdev->dev; adapter->mbox = func; @@ -6242,6 +6615,10 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->priv_flags |= IFF_UNICAST_FLT; netdev->netdev_ops = &cxgb4_netdev_ops; +#ifdef CONFIG_CHELSIO_T4_DCB + netdev->dcbnl_ops = &cxgb4_dcb_ops; + cxgb4_dcb_state_init(netdev); +#endif netdev->ethtool_ops = &cxgb_ethtool_ops; } @@ -6320,7 +6697,6 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (is_offload(adapter)) attach_ulds(adapter); -sriov: #ifdef CONFIG_PCI_IOV if (func < ARRAY_SIZE(num_vf) && num_vf[func] > 0) if (pci_enable_sriov(pdev, num_vf[func]) == 0) @@ -6366,8 +6742,7 @@ static void remove_one(struct pci_dev *pdev) if (adapter->port[i]->reg_state == NETREG_REGISTERED) unregister_netdev(adapter->port[i]); - if (adapter->debugfs_root) - debugfs_remove_recursive(adapter->debugfs_root); + debugfs_remove_recursive(adapter->debugfs_root); /* If we allocated filters, free up state associated with any * valid filters ... diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h index 55e9daf7f9d4..1366ba620c87 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h @@ -1,7 +1,7 @@ /* * This file is part of the Chelsio T4 Ethernet driver for Linux. * - * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved. + * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -172,6 +172,10 @@ int cxgb4_create_server_filter(const struct net_device *dev, unsigned int stid, unsigned char port, unsigned char mask); int cxgb4_remove_server_filter(const struct net_device *dev, unsigned int stid, unsigned int queue, bool ipv6); +int cxgb4_clip_get(const struct net_device *dev, const struct in6_addr *lip); +int cxgb4_clip_release(const struct net_device *dev, + const struct in6_addr *lip); + static inline void set_wr_txq(struct sk_buff *skb, int prio, int queue) { skb_set_queue_mapping(skb, (queue << 1) | prio); @@ -243,6 +247,7 @@ struct cxgb4_lld_info { unsigned char fw_api_ver; /* FW API version */ unsigned int fw_vers; /* FW version */ unsigned int iscsi_iolen; /* iSCSI max I/O length */ + unsigned int cclk_ps; /* Core clock period in psec */ unsigned short udb_density; /* # of user DB/page */ unsigned short ucq_density; /* # of user CQs/page */ unsigned short filt_mode; /* filter optional components */ @@ -251,10 +256,15 @@ struct cxgb4_lld_info { void __iomem *gts_reg; /* address of GTS register */ void __iomem *db_reg; /* address of kernel doorbell */ int dbfifo_int_thresh; /* doorbell fifo int threshold */ + unsigned int sge_ingpadboundary; /* SGE ingress padding boundary */ + unsigned int sge_egrstatuspagesize; /* SGE egress status page size */ unsigned int sge_pktshift; /* Padding between CPL and */ /* packet data */ + unsigned int pf; /* Physical Function we're using */ bool enable_fw_ofld_conn; /* Enable connection through fw */ /* WR */ + unsigned int max_ordird_qp; /* Max ORD/IRD depth per RDMA QP */ + unsigned int max_ird_adapter; /* Max IRD memory per adapter */ bool ulptx_memwrite_dsgl; /* use of T5 DSGL allowed */ }; @@ -291,5 +301,7 @@ int cxgb4_sync_txq_pidx(struct net_device *dev, u16 qid, u16 pidx, u16 size); int cxgb4_flush_eq_cache(struct net_device *dev); void cxgb4_disable_db_coalescing(struct net_device *dev); void cxgb4_enable_db_coalescing(struct net_device *dev); +int cxgb4_read_tpte(struct net_device *dev, u32 stag, __be32 *tpte); +u64 cxgb4_read_sge_timestamp(struct net_device *dev); #endif /* !__CXGB4_OFLD_H */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.c b/drivers/net/ethernet/chelsio/cxgb4/l2t.c index 8a96572fdde0..96041397ee15 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/l2t.c +++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.c @@ -1,7 +1,7 @@ /* * This file is part of the Chelsio T4 Ethernet driver for Linux. * - * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved. + * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.h b/drivers/net/ethernet/chelsio/cxgb4/l2t.h index 85eb5c71358d..a30126ce90cb 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/l2t.h +++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.h @@ -1,7 +1,7 @@ /* * This file is part of the Chelsio T4 Ethernet driver for Linux. * - * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved. + * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index dd4355d248e4..b0bba32d69d5 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -1,7 +1,7 @@ /* * This file is part of the Chelsio T4 Ethernet driver for Linux. * - * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved. + * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -2480,6 +2480,22 @@ static void free_rspq_fl(struct adapter *adap, struct sge_rspq *rq, } /** + * t4_free_ofld_rxqs - free a block of consecutive Rx queues + * @adap: the adapter + * @n: number of queues + * @q: pointer to first queue + * + * Release the resources of a consecutive block of offload Rx queues. + */ +void t4_free_ofld_rxqs(struct adapter *adap, int n, struct sge_ofld_rxq *q) +{ + for ( ; n; n--, q++) + if (q->rspq.desc) + free_rspq_fl(adap, &q->rspq, + q->fl.size ? &q->fl : NULL); +} + +/** * t4_free_sge_resources - free SGE resources * @adap: the adapter * @@ -2490,12 +2506,12 @@ void t4_free_sge_resources(struct adapter *adap) int i; struct sge_eth_rxq *eq = adap->sge.ethrxq; struct sge_eth_txq *etq = adap->sge.ethtxq; - struct sge_ofld_rxq *oq = adap->sge.ofldrxq; /* clean up Ethernet Tx/Rx queues */ for (i = 0; i < adap->sge.ethqsets; i++, eq++, etq++) { if (eq->rspq.desc) - free_rspq_fl(adap, &eq->rspq, &eq->fl); + free_rspq_fl(adap, &eq->rspq, + eq->fl.size ? &eq->fl : NULL); if (etq->q.desc) { t4_eth_eq_free(adap, adap->fn, adap->fn, 0, etq->q.cntxt_id); @@ -2506,18 +2522,9 @@ void t4_free_sge_resources(struct adapter *adap) } /* clean up RDMA and iSCSI Rx queues */ - for (i = 0; i < adap->sge.ofldqsets; i++, oq++) { - if (oq->rspq.desc) - free_rspq_fl(adap, &oq->rspq, &oq->fl); - } - for (i = 0, oq = adap->sge.rdmarxq; i < adap->sge.rdmaqs; i++, oq++) { - if (oq->rspq.desc) - free_rspq_fl(adap, &oq->rspq, &oq->fl); - } - for (i = 0, oq = adap->sge.rdmaciq; i < adap->sge.rdmaciqs; i++, oq++) { - if (oq->rspq.desc) - free_rspq_fl(adap, &oq->rspq, &oq->fl); - } + t4_free_ofld_rxqs(adap, adap->sge.ofldqsets, adap->sge.ofldrxq); + t4_free_ofld_rxqs(adap, adap->sge.rdmaqs, adap->sge.rdmarxq); + t4_free_ofld_rxqs(adap, adap->sge.rdmaciqs, adap->sge.rdmaciq); /* clean up offload Tx queues */ for (i = 0; i < ARRAY_SIZE(adap->sge.ofldtxq); i++) { diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 931478e7bd28..a853133d8db8 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -1,7 +1,7 @@ /* * This file is part of the Chelsio T4 Ethernet driver for Linux. * - * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved. + * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -144,6 +144,30 @@ void t4_write_indirect(struct adapter *adap, unsigned int addr_reg, } /* + * Read a 32-bit PCI Configuration Space register via the PCI-E backdoor + * mechanism. This guarantees that we get the real value even if we're + * operating within a Virtual Machine and the Hypervisor is trapping our + * Configuration Space accesses. + */ +void t4_hw_pci_read_cfg4(struct adapter *adap, int reg, u32 *val) +{ + u32 req = ENABLE | FUNCTION(adap->fn) | reg; + + if (is_t4(adap->params.chip)) + req |= F_LOCALCFG; + + t4_write_reg(adap, PCIE_CFG_SPACE_REQ, req); + *val = t4_read_reg(adap, PCIE_CFG_SPACE_DATA); + + /* Reset ENABLE to 0 so reads of PCIE_CFG_SPACE_DATA won't cause a + * Configuration Space read. (None of the other fields matter when + * ENABLE is 0 so a simple register write is easier than a + * read-modify-write via t4_set_reg_field().) + */ + t4_write_reg(adap, PCIE_CFG_SPACE_REQ, 0); +} + +/* * Get the reply to a mailbox command and store it in @rpl in big-endian order. */ static void get_mbox_rpl(struct adapter *adap, __be64 *rpl, int nflit, @@ -389,78 +413,41 @@ 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; - u32 win_pf = is_t4(adap->params.chip) ? 0 : V_PFNUM(adap->fn); - - /* - * 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)) | win_pf); - 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++ = (__force __be32) t4_read_reg(adap, - (MEMWIN0_BASE + i)); - else - t4_write_reg(adap, (MEMWIN0_BASE + i), - (__force u32) *data++); - } - - return 0; -} - /** * t4_memory_rw - read/write EDC 0, EDC 1 or MC via PCIE memory window * @adap: the adapter + * @win: PCI-E Memory Window to use * @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 + * @dir: direction of transfer T4_MEMORY_READ (1) or T4_MEMORY_WRITE (0) * * 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. + * firmware memory address and host buffer must be aligned on 32-bit + * boudaries; the length may be arbitrary. 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 + * caller's 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) +int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr, + u32 len, __be32 *buf, int dir) { - u32 pos, start, end, offset, memoffset; - u32 edc_size, mc_size; - int ret = 0; - __be32 *data; + u32 pos, offset, resid, memoffset; + u32 edc_size, mc_size, win_pf, mem_reg, mem_aperture, mem_base; - /* - * Argument sanity checks ... + /* Argument sanity checks ... */ - if ((addr & 0x3) || (len & 0x3)) + if (addr & 0x3) return -EINVAL; - data = vmalloc(MEMWIN0_APERTURE); - if (!data) - return -ENOMEM; + /* It's convenient to be able to handle lengths which aren't a + * multiple of 32-bits because we often end up transferring files to + * the firmware. So we'll handle that by normalizing the length here + * and then handling any residual transfer at the end. + */ + resid = len & 0x3; + len -= resid; /* Offset into the region of memory which is being accessed * MEM_EDC0 = 0 @@ -481,66 +468,98 @@ static int t4_memory_rw(struct adapter *adap, int mtype, u32 addr, u32 len, /* 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. + /* Each PCI-E Memory Window is programmed with a window size -- or + * "aperture" -- which controls the granularity of its mapping onto + * adapter memory. We need to grab that aperture in order to know + * how to use the specified window. The window is also programmed + * with the base address of the Memory Window in BAR0's address + * space. For T4 this is an absolute PCI-E Bus Address. For T5 + * the address is relative to BAR0. */ - start = addr & ~(MEMWIN0_APERTURE-1); - end = (addr + len + MEMWIN0_APERTURE-1) & ~(MEMWIN0_APERTURE-1); - offset = (addr - start)/sizeof(__be32); + mem_reg = t4_read_reg(adap, + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, + win)); + mem_aperture = 1 << (GET_WINDOW(mem_reg) + 10); + mem_base = GET_PCIEOFST(mem_reg) << 10; + if (is_t4(adap->params.chip)) + mem_base -= adap->t4_bar0; + win_pf = is_t4(adap->params.chip) ? 0 : V_PFNUM(adap->fn); - for (pos = start; pos < end; pos += MEMWIN0_APERTURE, offset = 0) { + /* Calculate our initial PCI-E Memory Window Position and Offset into + * that Window. + */ + pos = addr & ~(mem_aperture-1); + offset = addr - pos; - /* - * If we're writing, copy the data from the caller's memory - * buffer + /* Set up initial PCI-E Memory Window to cover the start of our + * transfer. (Read it back to ensure that changes propagate before we + * attempt to use the new value.) + */ + t4_write_reg(adap, + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win), + pos | win_pf); + t4_read_reg(adap, + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win)); + + /* Transfer data to/from the adapter as long as there's an integral + * number of 32-bit transfers to complete. + */ + while (len > 0) { + if (dir == T4_MEMORY_READ) + *buf++ = (__force __be32) t4_read_reg(adap, + mem_base + offset); + else + t4_write_reg(adap, mem_base + offset, + (__force u32) *buf++); + offset += sizeof(__be32); + len -= sizeof(__be32); + + /* If we've reached the end of our current window aperture, + * move the PCI-E Memory Window on to the next. Note that + * doing this here after "len" may be 0 allows us to set up + * the PCI-E Memory Window for a possible final residual + * transfer below ... */ - 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) - break; - } - while (offset < (MEMWIN0_APERTURE/sizeof(__be32)) && - len > 0) { - data[offset++] = *buf++; - len -= sizeof(__be32); - } + if (offset == mem_aperture) { + pos += mem_aperture; + offset = 0; + t4_write_reg(adap, + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, + win), pos | win_pf); + t4_read_reg(adap, + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, + win)); } - - /* - * Transfer a block of memory and bail if there's an error. - */ - ret = t4_mem_win_rw(adap, pos, data, dir); - if (ret) - break; - - /* - * 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); - } } - vfree(data); - return ret; -} + /* If the original transfer had a length which wasn't a multiple of + * 32-bits, now's where we need to finish off the transfer of the + * residual amount. The PCI-E Memory Window has already been moved + * above (if necessary) to cover this final transfer. + */ + if (resid) { + union { + __be32 word; + char byte[4]; + } last; + unsigned char *bp; + int i; + + if (dir == T4_MEMORY_READ) { + last.word = (__force __be32) t4_read_reg(adap, + mem_base + offset); + for (bp = (unsigned char *)buf, i = resid; i < 4; i++) + bp[i] = last.byte[i]; + } else { + last.word = *buf; + for (i = resid; i < 4; i++) + last.byte[i] = 0; + t4_write_reg(adap, mem_base + offset, + (__force u32) last.word); + } + } -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); + return 0; } #define EEPROM_STAT_ADDR 0x7bfc @@ -1700,16 +1719,24 @@ static void mps_intr_handler(struct adapter *adapter) */ static void mem_intr_handler(struct adapter *adapter, int idx) { - static const char name[3][5] = { "EDC0", "EDC1", "MC" }; + static const char name[4][7] = { "EDC0", "EDC1", "MC/MC0", "MC1" }; unsigned int addr, cnt_addr, v; if (idx <= MEM_EDC1) { addr = EDC_REG(EDC_INT_CAUSE, idx); cnt_addr = EDC_REG(EDC_ECC_STATUS, idx); + } else if (idx == MEM_MC) { + if (is_t4(adapter->params.chip)) { + addr = MC_INT_CAUSE; + cnt_addr = MC_ECC_STATUS; + } else { + addr = MC_P_INT_CAUSE; + cnt_addr = MC_P_ECC_STATUS; + } } else { - addr = MC_INT_CAUSE; - cnt_addr = MC_ECC_STATUS; + addr = MC_REG(MC_P_INT_CAUSE, 1); + cnt_addr = MC_REG(MC_P_ECC_STATUS, 1); } v = t4_read_reg(adapter, addr) & MEM_INT_MASK; @@ -1873,6 +1900,8 @@ int t4_slow_intr_handler(struct adapter *adapter) pcie_intr_handler(adapter); if (cause & MC) mem_intr_handler(adapter, MEM_MC); + if (!is_t4(adapter->params.chip) && (cause & MC1)) + mem_intr_handler(adapter, MEM_MC1); if (cause & EDC0) mem_intr_handler(adapter, MEM_EDC0); if (cause & EDC1) @@ -2505,39 +2534,6 @@ int t4_fwaddrspace_write(struct adapter *adap, unsigned int mbox, } /** - * t4_mem_win_read_len - read memory through PCIE memory window - * @adap: the adapter - * @addr: address of first byte requested aligned on 32b. - * @data: len bytes to hold the data read - * @len: amount of data to read from window. Must be <= - * MEMWIN0_APERATURE after adjusting for 16B for T4 and - * 128B for T5 alignment requirements of the the memory window. - * - * Read len bytes of data from MC starting at @addr. - */ -int t4_mem_win_read_len(struct adapter *adap, u32 addr, __be32 *data, int len) -{ - int i, off; - u32 win_pf = is_t4(adap->params.chip) ? 0 : V_PFNUM(adap->fn); - - /* Align on a 2KB boundary. - */ - off = addr & MEMWIN0_APERTURE; - if ((addr & 3) || (len + off) > MEMWIN0_APERTURE) - return -EINVAL; - - t4_write_reg(adap, PCIE_MEM_ACCESS_OFFSET, - (addr & ~MEMWIN0_APERTURE) | win_pf); - t4_read_reg(adap, PCIE_MEM_ACCESS_OFFSET); - - for (i = 0; i < len; i += 4) - *data++ = (__force __be32) t4_read_reg(adap, - (MEMWIN0_BASE + off + i)); - - return 0; -} - -/** * t4_mdio_rd - read a PHY register through MDIO * @adap: the adapter * @mbox: mailbox to use for the FW command @@ -3175,6 +3171,46 @@ int t4_query_params(struct adapter *adap, unsigned int mbox, unsigned int pf, } /** + * t4_set_params_nosleep - sets FW or device parameters + * @adap: the adapter + * @mbox: mailbox to use for the FW command + * @pf: the PF + * @vf: the VF + * @nparams: the number of parameters + * @params: the parameter names + * @val: the parameter values + * + * Does not ever sleep + * Sets the value of FW or device parameters. Up to 7 parameters can be + * specified at once. + */ +int t4_set_params_nosleep(struct adapter *adap, unsigned int mbox, + unsigned int pf, unsigned int vf, + unsigned int nparams, const u32 *params, + const u32 *val) +{ + struct fw_params_cmd c; + __be32 *p = &c.param[0].mnem; + + if (nparams > 7) + return -EINVAL; + + memset(&c, 0, sizeof(c)); + c.op_to_vfn = cpu_to_be32(FW_CMD_OP(FW_PARAMS_CMD) | + FW_CMD_REQUEST | FW_CMD_WRITE | + FW_PARAMS_CMD_PFN(pf) | + FW_PARAMS_CMD_VFN(vf)); + c.retval_len16 = cpu_to_be32(FW_LEN16(c)); + + while (nparams--) { + *p++ = cpu_to_be32(*params++); + *p++ = cpu_to_be32(*val++); + } + + return t4_wr_mbox_ns(adap, mbox, &c, sizeof(c), NULL); +} + +/** * t4_set_params - sets FW or device parameters * @adap: the adapter * @mbox: mailbox to use for the FW command @@ -3499,6 +3535,33 @@ int t4_set_addr_hash(struct adapter *adap, unsigned int mbox, unsigned int viid, } /** + * t4_enable_vi_params - enable/disable a virtual interface + * @adap: the adapter + * @mbox: mailbox to use for the FW command + * @viid: the VI id + * @rx_en: 1=enable Rx, 0=disable Rx + * @tx_en: 1=enable Tx, 0=disable Tx + * @dcb_en: 1=enable delivery of Data Center Bridging messages. + * + * Enables/disables a virtual interface. Note that setting DCB Enable + * only makes sense when enabling a Virtual Interface ... + */ +int t4_enable_vi_params(struct adapter *adap, unsigned int mbox, + unsigned int viid, bool rx_en, bool tx_en, bool dcb_en) +{ + struct fw_vi_enable_cmd c; + + memset(&c, 0, sizeof(c)); + c.op_to_viid = htonl(FW_CMD_OP(FW_VI_ENABLE_CMD) | FW_CMD_REQUEST | + FW_CMD_EXEC | FW_VI_ENABLE_CMD_VIID(viid)); + + c.ien_to_len16 = htonl(FW_VI_ENABLE_CMD_IEN(rx_en) | + FW_VI_ENABLE_CMD_EEN(tx_en) | FW_LEN16(c) | + FW_VI_ENABLE_CMD_DCB_INFO(dcb_en)); + return t4_wr_mbox_ns(adap, mbox, &c, sizeof(c), NULL); +} + +/** * t4_enable_vi - enable/disable a virtual interface * @adap: the adapter * @mbox: mailbox to use for the FW command @@ -3511,14 +3574,7 @@ int t4_set_addr_hash(struct adapter *adap, unsigned int mbox, unsigned int viid, int t4_enable_vi(struct adapter *adap, unsigned int mbox, unsigned int viid, bool rx_en, bool tx_en) { - struct fw_vi_enable_cmd c; - - memset(&c, 0, sizeof(c)); - c.op_to_viid = htonl(FW_CMD_OP(FW_VI_ENABLE_CMD) | FW_CMD_REQUEST | - FW_CMD_EXEC | FW_VI_ENABLE_CMD_VIID(viid)); - c.ien_to_len16 = htonl(FW_VI_ENABLE_CMD_IEN(rx_en) | - FW_VI_ENABLE_CMD_EEN(tx_en) | FW_LEN16(c)); - return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); + return t4_enable_vi_params(adap, mbox, viid, rx_en, tx_en, 0); } /** diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h index 71b799b5b0f4..35e3d8e32881 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h @@ -1,7 +1,7 @@ /* * This file is part of the Chelsio T4 Ethernet driver for Linux. * - * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved. + * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h index 973eb11aa98a..52e08103f221 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h @@ -1,7 +1,7 @@ /* * This file is part of the Chelsio T4 Ethernet driver for Linux. * - * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved. + * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -75,6 +75,7 @@ enum { CPL_RX_DATA_DDP = 0x42, CPL_PASS_ACCEPT_REQ = 0x44, CPL_TRACE_PKT_T5 = 0x48, + CPL_RX_ISCSI_DDP = 0x49, CPL_RDMA_READ_REQ = 0x60, @@ -86,6 +87,7 @@ enum { CPL_SGE_EGR_UPDATE = 0xA5, CPL_TRACE_PKT = 0xB0, + CPL_ISCSI_DATA = 0xB2, CPL_FW4_MSG = 0xC0, CPL_FW4_PLD = 0xC1, @@ -270,12 +272,15 @@ struct cpl_pass_accept_rpl { #define RX_COALESCE_VALID(x) ((x) << 11) #define RX_COALESCE(x) ((x) << 12) #define PACE(x) ((x) << 16) +#define RX_FC_VALID ((1U) << 19) +#define RX_FC_DISABLE ((1U) << 20) #define TX_QUEUE(x) ((x) << 23) #define RX_CHANNEL(x) ((x) << 26) #define CCTRL_ECN(x) ((x) << 27) #define WND_SCALE_EN(x) ((x) << 28) #define TSTAMPS_EN(x) ((x) << 29) #define SACK_EN(x) ((x) << 30) +#define T5_OPT_2_VALID ((1U) << 31) __be64 opt0; }; diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index 225ad8a5722d..e3146e83df20 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -1,7 +1,7 @@ /* * This file is part of the Chelsio T4 Ethernet driver for Linux. * - * Copyright (c) 2010 Chelsio Communications, Inc. All rights reserved. + * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -251,6 +251,12 @@ #define V_NOCOALESCE(x) ((x) << S_NOCOALESCE) #define F_NOCOALESCE V_NOCOALESCE(1U) +#define SGE_TIMESTAMP_LO 0x1098 +#define SGE_TIMESTAMP_HI 0x109c +#define S_TSVAL 0 +#define M_TSVAL 0xfffffffU +#define GET_TSVAL(x) (((x) >> S_TSVAL) & M_TSVAL) + #define SGE_TIMER_VALUE_0_AND_1 0x10b8 #define TIMERVALUE0_MASK 0xffff0000U #define TIMERVALUE0_SHIFT 16 @@ -387,6 +393,8 @@ #define MSTGRPPERR 0x00000001U #define PCIE_NONFAT_ERR 0x3010 +#define PCIE_CFG_SPACE_REQ 0x3060 +#define PCIE_CFG_SPACE_DATA 0x3064 #define PCIE_MEM_ACCESS_BASE_WIN 0x3068 #define S_PCIEOFST 10 #define M_PCIEOFST 0x3fffffU @@ -398,7 +406,11 @@ #define WINDOW_MASK 0x000000ffU #define WINDOW_SHIFT 0 #define WINDOW(x) ((x) << WINDOW_SHIFT) +#define GET_WINDOW(x) (((x) >> WINDOW_SHIFT) & WINDOW_MASK) #define PCIE_MEM_ACCESS_OFFSET 0x306c +#define ENABLE (1U << 30) +#define FUNCTION(x) ((x) << 12) +#define F_LOCALCFG (1U << 28) #define S_PFNUM 0 #define V_PFNUM(x) ((x) << S_PFNUM) @@ -436,11 +448,13 @@ #define TDUE 0x00010000U #define MC_INT_CAUSE 0x7518 +#define MC_P_INT_CAUSE 0x41318 #define ECC_UE_INT_CAUSE 0x00000004U #define ECC_CE_INT_CAUSE 0x00000002U #define PERR_INT_CAUSE 0x00000001U #define MC_ECC_STATUS 0x751c +#define MC_P_ECC_STATUS 0x4131c #define ECC_CECNT_MASK 0xffff0000U #define ECC_CECNT_SHIFT 16 #define ECC_CECNT(x) ((x) << ECC_CECNT_SHIFT) @@ -1089,6 +1103,7 @@ #define I2CM 0x00000002U #define CIM 0x00000001U +#define MC1 0x31 #define PL_INT_ENABLE 0x19410 #define PL_INT_MAP0 0x19414 #define PL_RST 0x19428 diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h index 9cc973fbcf26..ff709e3b3e7e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h @@ -1,7 +1,7 @@ /* * This file is part of the Chelsio T4 Ethernet driver for Linux. * - * Copyright (c) 2009-2010 Chelsio Communications, Inc. All rights reserved. + * Copyright (c) 2009-2014 Chelsio Communications, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -46,9 +46,11 @@ enum fw_retval { FW_EFAULT = 14, /* bad address; fw bad */ FW_EBUSY = 16, /* resource busy */ FW_EEXIST = 17, /* file exists */ + FW_ENODEV = 19, /* no such device */ FW_EINVAL = 22, /* invalid argument */ FW_ENOSPC = 28, /* no space left on device */ FW_ENOSYS = 38, /* functionality not implemented */ + FW_ENODATA = 61, /* no data available */ FW_EPROTO = 71, /* protocol error */ FW_EADDRINUSE = 98, /* address already in use */ FW_EADDRNOTAVAIL = 99, /* cannot assigned requested address */ @@ -932,6 +934,8 @@ enum fw_params_param_dev { FW_PARAMS_PARAM_DEV_FWREV = 0x0B, FW_PARAMS_PARAM_DEV_TPREV = 0x0C, FW_PARAMS_PARAM_DEV_CF = 0x0D, + FW_PARAMS_PARAM_DEV_MAXORDIRD_QP = 0x13, /* max supported QP IRD/ORD */ + FW_PARAMS_PARAM_DEV_MAXIRD_ADAPTER = 0x14, /* max supported adap IRD */ FW_PARAMS_PARAM_DEV_ULPTX_MEMWRITE_DSGL = 0x17, }; @@ -989,6 +993,7 @@ enum fw_params_param_dmaq { FW_PARAMS_PARAM_DMAQ_EQ_CMPLIQID_MNGT = 0x10, FW_PARAMS_PARAM_DMAQ_EQ_CMPLIQID_CTRL = 0x11, FW_PARAMS_PARAM_DMAQ_EQ_SCHEDCLASS_ETH = 0x12, + FW_PARAMS_PARAM_DMAQ_EQ_DCBPRIO_ETH = 0x13, }; #define FW_PARAMS_MNEM(x) ((x) << 24) @@ -1422,6 +1427,7 @@ struct fw_vi_enable_cmd { #define FW_VI_ENABLE_CMD_VIID(x) ((x) << 0) #define FW_VI_ENABLE_CMD_IEN(x) ((x) << 31) #define FW_VI_ENABLE_CMD_EEN(x) ((x) << 30) +#define FW_VI_ENABLE_CMD_DCB_INFO(x) ((x) << 28) #define FW_VI_ENABLE_CMD_LED (1U << 29) /* VI VF stats offset definitions */ @@ -1594,6 +1600,9 @@ enum fw_port_action { FW_PORT_ACTION_GET_PORT_INFO = 0x0003, FW_PORT_ACTION_L2_PPP_CFG = 0x0004, FW_PORT_ACTION_L2_DCB_CFG = 0x0005, + FW_PORT_ACTION_DCB_READ_TRANS = 0x0006, + FW_PORT_ACTION_DCB_READ_RECV = 0x0007, + FW_PORT_ACTION_DCB_READ_DET = 0x0008, FW_PORT_ACTION_LOW_PWR_TO_NORMAL = 0x0010, FW_PORT_ACTION_L1_LOW_PWR_EN = 0x0011, FW_PORT_ACTION_L2_WOL_MODE_EN = 0x0012, @@ -1637,6 +1646,14 @@ enum fw_port_dcb_type { FW_PORT_DCB_TYPE_PRIORATE = 0x02, FW_PORT_DCB_TYPE_PFC = 0x03, FW_PORT_DCB_TYPE_APP_ID = 0x04, + FW_PORT_DCB_TYPE_CONTROL = 0x05, +}; + +enum fw_port_dcb_feature_state { + FW_PORT_DCB_FEATURE_STATE_PENDING = 0x0, + FW_PORT_DCB_FEATURE_STATE_SUCCESS = 0x1, + FW_PORT_DCB_FEATURE_STATE_ERROR = 0x2, + FW_PORT_DCB_FEATURE_STATE_TIMEOUT = 0x3, }; struct fw_port_cmd { @@ -1648,9 +1665,11 @@ struct fw_port_cmd { __be32 r; } l1cfg; struct fw_port_l2cfg { - __be16 ctlbf_to_ivlan0; + __u8 ctlbf; + __u8 ovlan3_to_ivlan0; __be16 ivlantype; - __be32 txipg_pkd; + __be16 txipg_force_pinfo; + __be16 mtu; __be16 ovlan0mask; __be16 ovlan0type; __be16 ovlan1mask; @@ -1666,24 +1685,60 @@ struct fw_port_cmd { __be16 acap; __be16 mtu; __u8 cbllen; - __u8 r9; - __be32 r10; - __be64 r11; + __u8 auxlinfo; + __u8 dcbxdis_pkd; + __u8 r8_lo[3]; + __be64 r9; } info; - struct fw_port_ppp { - __be32 pppen_to_ncsich; - __be32 r11; - } ppp; - struct fw_port_dcb { - __be16 cfg; - u8 up_map; - u8 sf_cfgrc; - __be16 prot_ix; - u8 pe7_to_pe0; - u8 numTCPFCs; - __be32 pgid0_to_pgid7; - __be32 numTCs_oui; - u8 pgpc[8]; + struct fw_port_diags { + __u8 diagop; + __u8 r[3]; + __be32 diagval; + } diags; + union fw_port_dcb { + struct fw_port_dcb_pgid { + __u8 type; + __u8 apply_pkd; + __u8 r10_lo[2]; + __be32 pgid; + __be64 r11; + } pgid; + struct fw_port_dcb_pgrate { + __u8 type; + __u8 apply_pkd; + __u8 r10_lo[5]; + __u8 num_tcs_supported; + __u8 pgrate[8]; + } pgrate; + struct fw_port_dcb_priorate { + __u8 type; + __u8 apply_pkd; + __u8 r10_lo[6]; + __u8 strict_priorate[8]; + } priorate; + struct fw_port_dcb_pfc { + __u8 type; + __u8 pfcen; + __u8 r10[5]; + __u8 max_pfc_tcs; + __be64 r11; + } pfc; + struct fw_port_app_priority { + __u8 type; + __u8 r10[2]; + __u8 idx; + __u8 user_prio_map; + __u8 sel_field; + __be16 protocolid; + __be64 r12; + } app_priority; + struct fw_port_dcb_control { + __u8 type; + __u8 all_syncd_pkd; + __be16 pfc_state_to_app_state; + __be32 r11; + __be64 r12; + } control; } dcb; } u; }; @@ -1720,6 +1775,10 @@ struct fw_port_cmd { #define FW_PORT_CMD_MODTYPE_MASK 0x1f #define FW_PORT_CMD_MODTYPE_GET(x) (((x) >> 0) & FW_PORT_CMD_MODTYPE_MASK) +#define FW_PORT_CMD_DCBXDIS (1U << 7) +#define FW_PORT_CMD_APPLY (1U << 7) +#define FW_PORT_CMD_ALL_SYNCD (1U << 7) + #define FW_PORT_CMD_PPPEN(x) ((x) << 31) #define FW_PORT_CMD_TPSRC(x) ((x) << 28) #define FW_PORT_CMD_NCSISRC(x) ((x) << 24) diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index ff1cdd1788b5..d8d28e82ade1 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -2876,24 +2876,24 @@ static void cxgb4vf_pci_shutdown(struct pci_dev *pdev) if (!adapter) return; - /* - * Disable all Virtual Interfaces. This will shut down the + /* Disable all Virtual Interfaces. This will shut down the * delivery of all ingress packets into the chip for these * Virtual Interfaces. */ - for_each_port(adapter, pidx) { - struct net_device *netdev; - struct port_info *pi; - - if (!test_bit(pidx, &adapter->registered_device_map)) - continue; - - netdev = adapter->port[pidx]; - if (!netdev) - continue; + for_each_port(adapter, pidx) + if (test_bit(pidx, &adapter->registered_device_map)) + unregister_netdev(adapter->port[pidx]); - pi = netdev_priv(netdev); - t4vf_enable_vi(adapter, pi->viid, false, false); + /* Free up all Queues which will prevent further DMA and + * Interrupts allowing various internal pathways to drain. + */ + t4vf_sge_stop(adapter); + if (adapter->flags & USING_MSIX) { + pci_disable_msix(adapter->pdev); + adapter->flags &= ~USING_MSIX; + } else if (adapter->flags & USING_MSI) { + pci_disable_msi(adapter->pdev); + adapter->flags &= ~USING_MSI; } /* @@ -2901,6 +2901,7 @@ static void cxgb4vf_pci_shutdown(struct pci_dev *pdev) * Interrupts allowing various internal pathways to drain. */ t4vf_free_sge_resources(adapter); + pci_set_drvdata(pdev, NULL); } /* @@ -2924,6 +2925,15 @@ static DEFINE_PCI_DEVICE_TABLE(cxgb4vf_pci_tbl) = { CH_DEVICE(0x480a, 0), /* T404-bt */ CH_DEVICE(0x480d, 0), /* T480-cr */ CH_DEVICE(0x480e, 0), /* T440-lp-cr */ + CH_DEVICE(0x4880, 0), + CH_DEVICE(0x4880, 1), + CH_DEVICE(0x4880, 2), + CH_DEVICE(0x4880, 3), + CH_DEVICE(0x4880, 4), + CH_DEVICE(0x4880, 5), + CH_DEVICE(0x4880, 6), + CH_DEVICE(0x4880, 7), + CH_DEVICE(0x4880, 8), CH_DEVICE(0x5800, 0), /* T580-dbg */ CH_DEVICE(0x5801, 0), /* T520-cr */ CH_DEVICE(0x5802, 0), /* T522-cr */ |