162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is part of the Chelsio T4 PCI-E SR-IOV Virtual Function Ethernet 362306a36Sopenharmony_ci * driver for Linux. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2009-2010 Chelsio Communications, Inc. All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This software is available to you under a choice of one of two 862306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 962306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 1062306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 1162306a36Sopenharmony_ci * OpenIB.org BSD license below: 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1462306a36Sopenharmony_ci * without modification, are permitted provided that the following 1562306a36Sopenharmony_ci * conditions are met: 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1862306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1962306a36Sopenharmony_ci * disclaimer. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 2262306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2362306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2462306a36Sopenharmony_ci * provided with the distribution. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2762306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2862306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2962306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 3062306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 3162306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 3262306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3362306a36Sopenharmony_ci * SOFTWARE. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include <linux/ethtool.h> 3762306a36Sopenharmony_ci#include <linux/pci.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include "t4vf_common.h" 4062306a36Sopenharmony_ci#include "t4vf_defs.h" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include "../cxgb4/t4_regs.h" 4362306a36Sopenharmony_ci#include "../cxgb4/t4_values.h" 4462306a36Sopenharmony_ci#include "../cxgb4/t4fw_api.h" 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * Wait for the device to become ready (signified by our "who am I" register 4862306a36Sopenharmony_ci * returning a value other than all 1's). Return an error if it doesn't 4962306a36Sopenharmony_ci * become ready ... 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ciint t4vf_wait_dev_ready(struct adapter *adapter) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci const u32 whoami = T4VF_PL_BASE_ADDR + PL_VF_WHOAMI; 5462306a36Sopenharmony_ci const u32 notready1 = 0xffffffff; 5562306a36Sopenharmony_ci const u32 notready2 = 0xeeeeeeee; 5662306a36Sopenharmony_ci u32 val; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci val = t4_read_reg(adapter, whoami); 5962306a36Sopenharmony_ci if (val != notready1 && val != notready2) 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci msleep(500); 6262306a36Sopenharmony_ci val = t4_read_reg(adapter, whoami); 6362306a36Sopenharmony_ci if (val != notready1 && val != notready2) 6462306a36Sopenharmony_ci return 0; 6562306a36Sopenharmony_ci else 6662306a36Sopenharmony_ci return -EIO; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * Get the reply to a mailbox command and store it in @rpl in big-endian order 7162306a36Sopenharmony_ci * (since the firmware data structures are specified in a big-endian layout). 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_cistatic void get_mbox_rpl(struct adapter *adapter, __be64 *rpl, int size, 7462306a36Sopenharmony_ci u32 mbox_data) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci for ( ; size; size -= 8, mbox_data += 8) 7762306a36Sopenharmony_ci *rpl++ = cpu_to_be64(t4_read_reg64(adapter, mbox_data)); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/** 8162306a36Sopenharmony_ci * t4vf_record_mbox - record a Firmware Mailbox Command/Reply in the log 8262306a36Sopenharmony_ci * @adapter: the adapter 8362306a36Sopenharmony_ci * @cmd: the Firmware Mailbox Command or Reply 8462306a36Sopenharmony_ci * @size: command length in bytes 8562306a36Sopenharmony_ci * @access: the time (ms) needed to access the Firmware Mailbox 8662306a36Sopenharmony_ci * @execute: the time (ms) the command spent being executed 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_cistatic void t4vf_record_mbox(struct adapter *adapter, const __be64 *cmd, 8962306a36Sopenharmony_ci int size, int access, int execute) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct mbox_cmd_log *log = adapter->mbox_log; 9262306a36Sopenharmony_ci struct mbox_cmd *entry; 9362306a36Sopenharmony_ci int i; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci entry = mbox_cmd_log_entry(log, log->cursor++); 9662306a36Sopenharmony_ci if (log->cursor == log->size) 9762306a36Sopenharmony_ci log->cursor = 0; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci for (i = 0; i < size / 8; i++) 10062306a36Sopenharmony_ci entry->cmd[i] = be64_to_cpu(cmd[i]); 10162306a36Sopenharmony_ci while (i < MBOX_LEN / 8) 10262306a36Sopenharmony_ci entry->cmd[i++] = 0; 10362306a36Sopenharmony_ci entry->timestamp = jiffies; 10462306a36Sopenharmony_ci entry->seqno = log->seqno++; 10562306a36Sopenharmony_ci entry->access = access; 10662306a36Sopenharmony_ci entry->execute = execute; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/** 11062306a36Sopenharmony_ci * t4vf_wr_mbox_core - send a command to FW through the mailbox 11162306a36Sopenharmony_ci * @adapter: the adapter 11262306a36Sopenharmony_ci * @cmd: the command to write 11362306a36Sopenharmony_ci * @size: command length in bytes 11462306a36Sopenharmony_ci * @rpl: where to optionally store the reply 11562306a36Sopenharmony_ci * @sleep_ok: if true we may sleep while awaiting command completion 11662306a36Sopenharmony_ci * 11762306a36Sopenharmony_ci * Sends the given command to FW through the mailbox and waits for the 11862306a36Sopenharmony_ci * FW to execute the command. If @rpl is not %NULL it is used to store 11962306a36Sopenharmony_ci * the FW's reply to the command. The command and its optional reply 12062306a36Sopenharmony_ci * are of the same length. FW can take up to 500 ms to respond. 12162306a36Sopenharmony_ci * @sleep_ok determines whether we may sleep while awaiting the response. 12262306a36Sopenharmony_ci * If sleeping is allowed we use progressive backoff otherwise we spin. 12362306a36Sopenharmony_ci * 12462306a36Sopenharmony_ci * The return value is 0 on success or a negative errno on failure. A 12562306a36Sopenharmony_ci * failure can happen either because we are not able to execute the 12662306a36Sopenharmony_ci * command or FW executes it but signals an error. In the latter case 12762306a36Sopenharmony_ci * the return value is the error code indicated by FW (negated). 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ciint t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size, 13062306a36Sopenharmony_ci void *rpl, bool sleep_ok) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci static const int delay[] = { 13362306a36Sopenharmony_ci 1, 1, 3, 5, 10, 10, 20, 50, 100 13462306a36Sopenharmony_ci }; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci u16 access = 0, execute = 0; 13762306a36Sopenharmony_ci u32 v, mbox_data; 13862306a36Sopenharmony_ci int i, ms, delay_idx, ret; 13962306a36Sopenharmony_ci const __be64 *p; 14062306a36Sopenharmony_ci u32 mbox_ctl = T4VF_CIM_BASE_ADDR + CIM_VF_EXT_MAILBOX_CTRL; 14162306a36Sopenharmony_ci u32 cmd_op = FW_CMD_OP_G(be32_to_cpu(((struct fw_cmd_hdr *)cmd)->hi)); 14262306a36Sopenharmony_ci __be64 cmd_rpl[MBOX_LEN / 8]; 14362306a36Sopenharmony_ci struct mbox_list entry; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* In T6, mailbox size is changed to 128 bytes to avoid 14662306a36Sopenharmony_ci * invalidating the entire prefetch buffer. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci if (CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5) 14962306a36Sopenharmony_ci mbox_data = T4VF_MBDATA_BASE_ADDR; 15062306a36Sopenharmony_ci else 15162306a36Sopenharmony_ci mbox_data = T6VF_MBDATA_BASE_ADDR; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* 15462306a36Sopenharmony_ci * Commands must be multiples of 16 bytes in length and may not be 15562306a36Sopenharmony_ci * larger than the size of the Mailbox Data register array. 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_ci if ((size % 16) != 0 || 15862306a36Sopenharmony_ci size > NUM_CIM_VF_MAILBOX_DATA_INSTANCES * 4) 15962306a36Sopenharmony_ci return -EINVAL; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Queue ourselves onto the mailbox access list. When our entry is at 16262306a36Sopenharmony_ci * the front of the list, we have rights to access the mailbox. So we 16362306a36Sopenharmony_ci * wait [for a while] till we're at the front [or bail out with an 16462306a36Sopenharmony_ci * EBUSY] ... 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci spin_lock(&adapter->mbox_lock); 16762306a36Sopenharmony_ci list_add_tail(&entry.list, &adapter->mlist.list); 16862306a36Sopenharmony_ci spin_unlock(&adapter->mbox_lock); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci delay_idx = 0; 17162306a36Sopenharmony_ci ms = delay[0]; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci for (i = 0; ; i += ms) { 17462306a36Sopenharmony_ci /* If we've waited too long, return a busy indication. This 17562306a36Sopenharmony_ci * really ought to be based on our initial position in the 17662306a36Sopenharmony_ci * mailbox access list but this is a start. We very rearely 17762306a36Sopenharmony_ci * contend on access to the mailbox ... 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_ci if (i > FW_CMD_MAX_TIMEOUT) { 18062306a36Sopenharmony_ci spin_lock(&adapter->mbox_lock); 18162306a36Sopenharmony_ci list_del(&entry.list); 18262306a36Sopenharmony_ci spin_unlock(&adapter->mbox_lock); 18362306a36Sopenharmony_ci ret = -EBUSY; 18462306a36Sopenharmony_ci t4vf_record_mbox(adapter, cmd, size, access, ret); 18562306a36Sopenharmony_ci return ret; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* If we're at the head, break out and start the mailbox 18962306a36Sopenharmony_ci * protocol. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_ci if (list_first_entry(&adapter->mlist.list, struct mbox_list, 19262306a36Sopenharmony_ci list) == &entry) 19362306a36Sopenharmony_ci break; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* Delay for a bit before checking again ... */ 19662306a36Sopenharmony_ci if (sleep_ok) { 19762306a36Sopenharmony_ci ms = delay[delay_idx]; /* last element may repeat */ 19862306a36Sopenharmony_ci if (delay_idx < ARRAY_SIZE(delay) - 1) 19962306a36Sopenharmony_ci delay_idx++; 20062306a36Sopenharmony_ci msleep(ms); 20162306a36Sopenharmony_ci } else { 20262306a36Sopenharmony_ci mdelay(ms); 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* 20762306a36Sopenharmony_ci * Loop trying to get ownership of the mailbox. Return an error 20862306a36Sopenharmony_ci * if we can't gain ownership. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci v = MBOWNER_G(t4_read_reg(adapter, mbox_ctl)); 21162306a36Sopenharmony_ci for (i = 0; v == MBOX_OWNER_NONE && i < 3; i++) 21262306a36Sopenharmony_ci v = MBOWNER_G(t4_read_reg(adapter, mbox_ctl)); 21362306a36Sopenharmony_ci if (v != MBOX_OWNER_DRV) { 21462306a36Sopenharmony_ci spin_lock(&adapter->mbox_lock); 21562306a36Sopenharmony_ci list_del(&entry.list); 21662306a36Sopenharmony_ci spin_unlock(&adapter->mbox_lock); 21762306a36Sopenharmony_ci ret = (v == MBOX_OWNER_FW) ? -EBUSY : -ETIMEDOUT; 21862306a36Sopenharmony_ci t4vf_record_mbox(adapter, cmd, size, access, ret); 21962306a36Sopenharmony_ci return ret; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* 22362306a36Sopenharmony_ci * Write the command array into the Mailbox Data register array and 22462306a36Sopenharmony_ci * transfer ownership of the mailbox to the firmware. 22562306a36Sopenharmony_ci * 22662306a36Sopenharmony_ci * For the VFs, the Mailbox Data "registers" are actually backed by 22762306a36Sopenharmony_ci * T4's "MA" interface rather than PL Registers (as is the case for 22862306a36Sopenharmony_ci * the PFs). Because these are in different coherency domains, the 22962306a36Sopenharmony_ci * write to the VF's PL-register-backed Mailbox Control can race in 23062306a36Sopenharmony_ci * front of the writes to the MA-backed VF Mailbox Data "registers". 23162306a36Sopenharmony_ci * So we need to do a read-back on at least one byte of the VF Mailbox 23262306a36Sopenharmony_ci * Data registers before doing the write to the VF Mailbox Control 23362306a36Sopenharmony_ci * register. 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_ci if (cmd_op != FW_VI_STATS_CMD) 23662306a36Sopenharmony_ci t4vf_record_mbox(adapter, cmd, size, access, 0); 23762306a36Sopenharmony_ci for (i = 0, p = cmd; i < size; i += 8) 23862306a36Sopenharmony_ci t4_write_reg64(adapter, mbox_data + i, be64_to_cpu(*p++)); 23962306a36Sopenharmony_ci t4_read_reg(adapter, mbox_data); /* flush write */ 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci t4_write_reg(adapter, mbox_ctl, 24262306a36Sopenharmony_ci MBMSGVALID_F | MBOWNER_V(MBOX_OWNER_FW)); 24362306a36Sopenharmony_ci t4_read_reg(adapter, mbox_ctl); /* flush write */ 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* 24662306a36Sopenharmony_ci * Spin waiting for firmware to acknowledge processing our command. 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_ci delay_idx = 0; 24962306a36Sopenharmony_ci ms = delay[0]; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci for (i = 0; i < FW_CMD_MAX_TIMEOUT; i += ms) { 25262306a36Sopenharmony_ci if (sleep_ok) { 25362306a36Sopenharmony_ci ms = delay[delay_idx]; 25462306a36Sopenharmony_ci if (delay_idx < ARRAY_SIZE(delay) - 1) 25562306a36Sopenharmony_ci delay_idx++; 25662306a36Sopenharmony_ci msleep(ms); 25762306a36Sopenharmony_ci } else 25862306a36Sopenharmony_ci mdelay(ms); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* 26162306a36Sopenharmony_ci * If we're the owner, see if this is the reply we wanted. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ci v = t4_read_reg(adapter, mbox_ctl); 26462306a36Sopenharmony_ci if (MBOWNER_G(v) == MBOX_OWNER_DRV) { 26562306a36Sopenharmony_ci /* 26662306a36Sopenharmony_ci * If the Message Valid bit isn't on, revoke ownership 26762306a36Sopenharmony_ci * of the mailbox and continue waiting for our reply. 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_ci if ((v & MBMSGVALID_F) == 0) { 27062306a36Sopenharmony_ci t4_write_reg(adapter, mbox_ctl, 27162306a36Sopenharmony_ci MBOWNER_V(MBOX_OWNER_NONE)); 27262306a36Sopenharmony_ci continue; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* 27662306a36Sopenharmony_ci * We now have our reply. Extract the command return 27762306a36Sopenharmony_ci * value, copy the reply back to our caller's buffer 27862306a36Sopenharmony_ci * (if specified) and revoke ownership of the mailbox. 27962306a36Sopenharmony_ci * We return the (negated) firmware command return 28062306a36Sopenharmony_ci * code (this depends on FW_SUCCESS == 0). 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_ci get_mbox_rpl(adapter, cmd_rpl, size, mbox_data); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* return value in low-order little-endian word */ 28562306a36Sopenharmony_ci v = be64_to_cpu(cmd_rpl[0]); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (rpl) { 28862306a36Sopenharmony_ci /* request bit in high-order BE word */ 28962306a36Sopenharmony_ci WARN_ON((be32_to_cpu(*(const __be32 *)cmd) 29062306a36Sopenharmony_ci & FW_CMD_REQUEST_F) == 0); 29162306a36Sopenharmony_ci memcpy(rpl, cmd_rpl, size); 29262306a36Sopenharmony_ci WARN_ON((be32_to_cpu(*(__be32 *)rpl) 29362306a36Sopenharmony_ci & FW_CMD_REQUEST_F) != 0); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci t4_write_reg(adapter, mbox_ctl, 29662306a36Sopenharmony_ci MBOWNER_V(MBOX_OWNER_NONE)); 29762306a36Sopenharmony_ci execute = i + ms; 29862306a36Sopenharmony_ci if (cmd_op != FW_VI_STATS_CMD) 29962306a36Sopenharmony_ci t4vf_record_mbox(adapter, cmd_rpl, size, access, 30062306a36Sopenharmony_ci execute); 30162306a36Sopenharmony_ci spin_lock(&adapter->mbox_lock); 30262306a36Sopenharmony_ci list_del(&entry.list); 30362306a36Sopenharmony_ci spin_unlock(&adapter->mbox_lock); 30462306a36Sopenharmony_ci return -FW_CMD_RETVAL_G(v); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* We timed out. Return the error ... */ 30962306a36Sopenharmony_ci ret = -ETIMEDOUT; 31062306a36Sopenharmony_ci t4vf_record_mbox(adapter, cmd, size, access, ret); 31162306a36Sopenharmony_ci spin_lock(&adapter->mbox_lock); 31262306a36Sopenharmony_ci list_del(&entry.list); 31362306a36Sopenharmony_ci spin_unlock(&adapter->mbox_lock); 31462306a36Sopenharmony_ci return ret; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci/* In the Physical Function Driver Common Code, the ADVERT_MASK is used to 31862306a36Sopenharmony_ci * mask out bits in the Advertised Port Capabilities which are managed via 31962306a36Sopenharmony_ci * separate controls, like Pause Frames and Forward Error Correction. In the 32062306a36Sopenharmony_ci * Virtual Function Common Code, since we never perform L1 Configuration on 32162306a36Sopenharmony_ci * the Link, the only things we really need to filter out are things which 32262306a36Sopenharmony_ci * we decode and report separately like Speed. 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_ci#define ADVERT_MASK (FW_PORT_CAP32_SPEED_V(FW_PORT_CAP32_SPEED_M) | \ 32562306a36Sopenharmony_ci FW_PORT_CAP32_802_3_PAUSE | \ 32662306a36Sopenharmony_ci FW_PORT_CAP32_802_3_ASM_DIR | \ 32762306a36Sopenharmony_ci FW_PORT_CAP32_FEC_V(FW_PORT_CAP32_FEC_M) | \ 32862306a36Sopenharmony_ci FW_PORT_CAP32_ANEG) 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci/** 33162306a36Sopenharmony_ci * fwcaps16_to_caps32 - convert 16-bit Port Capabilities to 32-bits 33262306a36Sopenharmony_ci * @caps16: a 16-bit Port Capabilities value 33362306a36Sopenharmony_ci * 33462306a36Sopenharmony_ci * Returns the equivalent 32-bit Port Capabilities value. 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_cistatic fw_port_cap32_t fwcaps16_to_caps32(fw_port_cap16_t caps16) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci fw_port_cap32_t caps32 = 0; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci #define CAP16_TO_CAP32(__cap) \ 34162306a36Sopenharmony_ci do { \ 34262306a36Sopenharmony_ci if (caps16 & FW_PORT_CAP_##__cap) \ 34362306a36Sopenharmony_ci caps32 |= FW_PORT_CAP32_##__cap; \ 34462306a36Sopenharmony_ci } while (0) 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci CAP16_TO_CAP32(SPEED_100M); 34762306a36Sopenharmony_ci CAP16_TO_CAP32(SPEED_1G); 34862306a36Sopenharmony_ci CAP16_TO_CAP32(SPEED_25G); 34962306a36Sopenharmony_ci CAP16_TO_CAP32(SPEED_10G); 35062306a36Sopenharmony_ci CAP16_TO_CAP32(SPEED_40G); 35162306a36Sopenharmony_ci CAP16_TO_CAP32(SPEED_100G); 35262306a36Sopenharmony_ci CAP16_TO_CAP32(FC_RX); 35362306a36Sopenharmony_ci CAP16_TO_CAP32(FC_TX); 35462306a36Sopenharmony_ci CAP16_TO_CAP32(ANEG); 35562306a36Sopenharmony_ci CAP16_TO_CAP32(MDIAUTO); 35662306a36Sopenharmony_ci CAP16_TO_CAP32(MDISTRAIGHT); 35762306a36Sopenharmony_ci CAP16_TO_CAP32(FEC_RS); 35862306a36Sopenharmony_ci CAP16_TO_CAP32(FEC_BASER_RS); 35962306a36Sopenharmony_ci CAP16_TO_CAP32(802_3_PAUSE); 36062306a36Sopenharmony_ci CAP16_TO_CAP32(802_3_ASM_DIR); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci #undef CAP16_TO_CAP32 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return caps32; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci/* Translate Firmware Pause specification to Common Code */ 36862306a36Sopenharmony_cistatic inline enum cc_pause fwcap_to_cc_pause(fw_port_cap32_t fw_pause) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci enum cc_pause cc_pause = 0; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (fw_pause & FW_PORT_CAP32_FC_RX) 37362306a36Sopenharmony_ci cc_pause |= PAUSE_RX; 37462306a36Sopenharmony_ci if (fw_pause & FW_PORT_CAP32_FC_TX) 37562306a36Sopenharmony_ci cc_pause |= PAUSE_TX; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return cc_pause; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/* Translate Firmware Forward Error Correction specification to Common Code */ 38162306a36Sopenharmony_cistatic inline enum cc_fec fwcap_to_cc_fec(fw_port_cap32_t fw_fec) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci enum cc_fec cc_fec = 0; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (fw_fec & FW_PORT_CAP32_FEC_RS) 38662306a36Sopenharmony_ci cc_fec |= FEC_RS; 38762306a36Sopenharmony_ci if (fw_fec & FW_PORT_CAP32_FEC_BASER_RS) 38862306a36Sopenharmony_ci cc_fec |= FEC_BASER_RS; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci return cc_fec; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci/* Return the highest speed set in the port capabilities, in Mb/s. */ 39462306a36Sopenharmony_cistatic unsigned int fwcap_to_speed(fw_port_cap32_t caps) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci #define TEST_SPEED_RETURN(__caps_speed, __speed) \ 39762306a36Sopenharmony_ci do { \ 39862306a36Sopenharmony_ci if (caps & FW_PORT_CAP32_SPEED_##__caps_speed) \ 39962306a36Sopenharmony_ci return __speed; \ 40062306a36Sopenharmony_ci } while (0) 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci TEST_SPEED_RETURN(400G, 400000); 40362306a36Sopenharmony_ci TEST_SPEED_RETURN(200G, 200000); 40462306a36Sopenharmony_ci TEST_SPEED_RETURN(100G, 100000); 40562306a36Sopenharmony_ci TEST_SPEED_RETURN(50G, 50000); 40662306a36Sopenharmony_ci TEST_SPEED_RETURN(40G, 40000); 40762306a36Sopenharmony_ci TEST_SPEED_RETURN(25G, 25000); 40862306a36Sopenharmony_ci TEST_SPEED_RETURN(10G, 10000); 40962306a36Sopenharmony_ci TEST_SPEED_RETURN(1G, 1000); 41062306a36Sopenharmony_ci TEST_SPEED_RETURN(100M, 100); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci #undef TEST_SPEED_RETURN 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return 0; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci/** 41862306a36Sopenharmony_ci * fwcap_to_fwspeed - return highest speed in Port Capabilities 41962306a36Sopenharmony_ci * @acaps: advertised Port Capabilities 42062306a36Sopenharmony_ci * 42162306a36Sopenharmony_ci * Get the highest speed for the port from the advertised Port 42262306a36Sopenharmony_ci * Capabilities. It will be either the highest speed from the list of 42362306a36Sopenharmony_ci * speeds or whatever user has set using ethtool. 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_cistatic fw_port_cap32_t fwcap_to_fwspeed(fw_port_cap32_t acaps) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci #define TEST_SPEED_RETURN(__caps_speed) \ 42862306a36Sopenharmony_ci do { \ 42962306a36Sopenharmony_ci if (acaps & FW_PORT_CAP32_SPEED_##__caps_speed) \ 43062306a36Sopenharmony_ci return FW_PORT_CAP32_SPEED_##__caps_speed; \ 43162306a36Sopenharmony_ci } while (0) 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci TEST_SPEED_RETURN(400G); 43462306a36Sopenharmony_ci TEST_SPEED_RETURN(200G); 43562306a36Sopenharmony_ci TEST_SPEED_RETURN(100G); 43662306a36Sopenharmony_ci TEST_SPEED_RETURN(50G); 43762306a36Sopenharmony_ci TEST_SPEED_RETURN(40G); 43862306a36Sopenharmony_ci TEST_SPEED_RETURN(25G); 43962306a36Sopenharmony_ci TEST_SPEED_RETURN(10G); 44062306a36Sopenharmony_ci TEST_SPEED_RETURN(1G); 44162306a36Sopenharmony_ci TEST_SPEED_RETURN(100M); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci #undef TEST_SPEED_RETURN 44462306a36Sopenharmony_ci return 0; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci/* 44862306a36Sopenharmony_ci * init_link_config - initialize a link's SW state 44962306a36Sopenharmony_ci * @lc: structure holding the link state 45062306a36Sopenharmony_ci * @pcaps: link Port Capabilities 45162306a36Sopenharmony_ci * @acaps: link current Advertised Port Capabilities 45262306a36Sopenharmony_ci * 45362306a36Sopenharmony_ci * Initializes the SW state maintained for each link, including the link's 45462306a36Sopenharmony_ci * capabilities and default speed/flow-control/autonegotiation settings. 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_cistatic void init_link_config(struct link_config *lc, 45762306a36Sopenharmony_ci fw_port_cap32_t pcaps, 45862306a36Sopenharmony_ci fw_port_cap32_t acaps) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci lc->pcaps = pcaps; 46162306a36Sopenharmony_ci lc->lpacaps = 0; 46262306a36Sopenharmony_ci lc->speed_caps = 0; 46362306a36Sopenharmony_ci lc->speed = 0; 46462306a36Sopenharmony_ci lc->requested_fc = lc->fc = PAUSE_RX | PAUSE_TX; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* For Forward Error Control, we default to whatever the Firmware 46762306a36Sopenharmony_ci * tells us the Link is currently advertising. 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_ci lc->auto_fec = fwcap_to_cc_fec(acaps); 47062306a36Sopenharmony_ci lc->requested_fec = FEC_AUTO; 47162306a36Sopenharmony_ci lc->fec = lc->auto_fec; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* If the Port is capable of Auto-Negtotiation, initialize it as 47462306a36Sopenharmony_ci * "enabled" and copy over all of the Physical Port Capabilities 47562306a36Sopenharmony_ci * to the Advertised Port Capabilities. Otherwise mark it as 47662306a36Sopenharmony_ci * Auto-Negotiate disabled and select the highest supported speed 47762306a36Sopenharmony_ci * for the link. Note parallel structure in t4_link_l1cfg_core() 47862306a36Sopenharmony_ci * and t4_handle_get_port_info(). 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ci if (lc->pcaps & FW_PORT_CAP32_ANEG) { 48162306a36Sopenharmony_ci lc->acaps = acaps & ADVERT_MASK; 48262306a36Sopenharmony_ci lc->autoneg = AUTONEG_ENABLE; 48362306a36Sopenharmony_ci lc->requested_fc |= PAUSE_AUTONEG; 48462306a36Sopenharmony_ci } else { 48562306a36Sopenharmony_ci lc->acaps = 0; 48662306a36Sopenharmony_ci lc->autoneg = AUTONEG_DISABLE; 48762306a36Sopenharmony_ci lc->speed_caps = fwcap_to_fwspeed(acaps); 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci/** 49262306a36Sopenharmony_ci * t4vf_port_init - initialize port hardware/software state 49362306a36Sopenharmony_ci * @adapter: the adapter 49462306a36Sopenharmony_ci * @pidx: the adapter port index 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_ciint t4vf_port_init(struct adapter *adapter, int pidx) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct port_info *pi = adap2pinfo(adapter, pidx); 49962306a36Sopenharmony_ci unsigned int fw_caps = adapter->params.fw_caps_support; 50062306a36Sopenharmony_ci struct fw_vi_cmd vi_cmd, vi_rpl; 50162306a36Sopenharmony_ci struct fw_port_cmd port_cmd, port_rpl; 50262306a36Sopenharmony_ci enum fw_port_type port_type; 50362306a36Sopenharmony_ci int mdio_addr; 50462306a36Sopenharmony_ci fw_port_cap32_t pcaps, acaps; 50562306a36Sopenharmony_ci int ret; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* If we haven't yet determined whether we're talking to Firmware 50862306a36Sopenharmony_ci * which knows the new 32-bit Port Capabilities, it's time to find 50962306a36Sopenharmony_ci * out now. This will also tell new Firmware to send us Port Status 51062306a36Sopenharmony_ci * Updates using the new 32-bit Port Capabilities version of the 51162306a36Sopenharmony_ci * Port Information message. 51262306a36Sopenharmony_ci */ 51362306a36Sopenharmony_ci if (fw_caps == FW_CAPS_UNKNOWN) { 51462306a36Sopenharmony_ci u32 param, val; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_PFVF) | 51762306a36Sopenharmony_ci FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_PFVF_PORT_CAPS32)); 51862306a36Sopenharmony_ci val = 1; 51962306a36Sopenharmony_ci ret = t4vf_set_params(adapter, 1, ¶m, &val); 52062306a36Sopenharmony_ci fw_caps = (ret == 0 ? FW_CAPS32 : FW_CAPS16); 52162306a36Sopenharmony_ci adapter->params.fw_caps_support = fw_caps; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* 52562306a36Sopenharmony_ci * Execute a VI Read command to get our Virtual Interface information 52662306a36Sopenharmony_ci * like MAC address, etc. 52762306a36Sopenharmony_ci */ 52862306a36Sopenharmony_ci memset(&vi_cmd, 0, sizeof(vi_cmd)); 52962306a36Sopenharmony_ci vi_cmd.op_to_vfn = cpu_to_be32(FW_CMD_OP_V(FW_VI_CMD) | 53062306a36Sopenharmony_ci FW_CMD_REQUEST_F | 53162306a36Sopenharmony_ci FW_CMD_READ_F); 53262306a36Sopenharmony_ci vi_cmd.alloc_to_len16 = cpu_to_be32(FW_LEN16(vi_cmd)); 53362306a36Sopenharmony_ci vi_cmd.type_viid = cpu_to_be16(FW_VI_CMD_VIID_V(pi->viid)); 53462306a36Sopenharmony_ci ret = t4vf_wr_mbox(adapter, &vi_cmd, sizeof(vi_cmd), &vi_rpl); 53562306a36Sopenharmony_ci if (ret != FW_SUCCESS) 53662306a36Sopenharmony_ci return ret; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci BUG_ON(pi->port_id != FW_VI_CMD_PORTID_G(vi_rpl.portid_pkd)); 53962306a36Sopenharmony_ci pi->rss_size = FW_VI_CMD_RSSSIZE_G(be16_to_cpu(vi_rpl.rsssize_pkd)); 54062306a36Sopenharmony_ci t4_os_set_hw_addr(adapter, pidx, vi_rpl.mac); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* 54362306a36Sopenharmony_ci * If we don't have read access to our port information, we're done 54462306a36Sopenharmony_ci * now. Otherwise, execute a PORT Read command to get it ... 54562306a36Sopenharmony_ci */ 54662306a36Sopenharmony_ci if (!(adapter->params.vfres.r_caps & FW_CMD_CAP_PORT)) 54762306a36Sopenharmony_ci return 0; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci memset(&port_cmd, 0, sizeof(port_cmd)); 55062306a36Sopenharmony_ci port_cmd.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | 55162306a36Sopenharmony_ci FW_CMD_REQUEST_F | 55262306a36Sopenharmony_ci FW_CMD_READ_F | 55362306a36Sopenharmony_ci FW_PORT_CMD_PORTID_V(pi->port_id)); 55462306a36Sopenharmony_ci port_cmd.action_to_len16 = cpu_to_be32( 55562306a36Sopenharmony_ci FW_PORT_CMD_ACTION_V(fw_caps == FW_CAPS16 55662306a36Sopenharmony_ci ? FW_PORT_ACTION_GET_PORT_INFO 55762306a36Sopenharmony_ci : FW_PORT_ACTION_GET_PORT_INFO32) | 55862306a36Sopenharmony_ci FW_LEN16(port_cmd)); 55962306a36Sopenharmony_ci ret = t4vf_wr_mbox(adapter, &port_cmd, sizeof(port_cmd), &port_rpl); 56062306a36Sopenharmony_ci if (ret != FW_SUCCESS) 56162306a36Sopenharmony_ci return ret; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* Extract the various fields from the Port Information message. */ 56462306a36Sopenharmony_ci if (fw_caps == FW_CAPS16) { 56562306a36Sopenharmony_ci u32 lstatus = be32_to_cpu(port_rpl.u.info.lstatus_to_modtype); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci port_type = FW_PORT_CMD_PTYPE_G(lstatus); 56862306a36Sopenharmony_ci mdio_addr = ((lstatus & FW_PORT_CMD_MDIOCAP_F) 56962306a36Sopenharmony_ci ? FW_PORT_CMD_MDIOADDR_G(lstatus) 57062306a36Sopenharmony_ci : -1); 57162306a36Sopenharmony_ci pcaps = fwcaps16_to_caps32(be16_to_cpu(port_rpl.u.info.pcap)); 57262306a36Sopenharmony_ci acaps = fwcaps16_to_caps32(be16_to_cpu(port_rpl.u.info.acap)); 57362306a36Sopenharmony_ci } else { 57462306a36Sopenharmony_ci u32 lstatus32 = 57562306a36Sopenharmony_ci be32_to_cpu(port_rpl.u.info32.lstatus32_to_cbllen32); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci port_type = FW_PORT_CMD_PORTTYPE32_G(lstatus32); 57862306a36Sopenharmony_ci mdio_addr = ((lstatus32 & FW_PORT_CMD_MDIOCAP32_F) 57962306a36Sopenharmony_ci ? FW_PORT_CMD_MDIOADDR32_G(lstatus32) 58062306a36Sopenharmony_ci : -1); 58162306a36Sopenharmony_ci pcaps = be32_to_cpu(port_rpl.u.info32.pcaps32); 58262306a36Sopenharmony_ci acaps = be32_to_cpu(port_rpl.u.info32.acaps32); 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci pi->port_type = port_type; 58662306a36Sopenharmony_ci pi->mdio_addr = mdio_addr; 58762306a36Sopenharmony_ci pi->mod_type = FW_PORT_MOD_TYPE_NA; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci init_link_config(&pi->link_cfg, pcaps, acaps); 59062306a36Sopenharmony_ci return 0; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci/** 59462306a36Sopenharmony_ci * t4vf_fw_reset - issue a reset to FW 59562306a36Sopenharmony_ci * @adapter: the adapter 59662306a36Sopenharmony_ci * 59762306a36Sopenharmony_ci * Issues a reset command to FW. For a Physical Function this would 59862306a36Sopenharmony_ci * result in the Firmware resetting all of its state. For a Virtual 59962306a36Sopenharmony_ci * Function this just resets the state associated with the VF. 60062306a36Sopenharmony_ci */ 60162306a36Sopenharmony_ciint t4vf_fw_reset(struct adapter *adapter) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct fw_reset_cmd cmd; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 60662306a36Sopenharmony_ci cmd.op_to_write = cpu_to_be32(FW_CMD_OP_V(FW_RESET_CMD) | 60762306a36Sopenharmony_ci FW_CMD_WRITE_F); 60862306a36Sopenharmony_ci cmd.retval_len16 = cpu_to_be32(FW_LEN16(cmd)); 60962306a36Sopenharmony_ci return t4vf_wr_mbox(adapter, &cmd, sizeof(cmd), NULL); 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci/** 61362306a36Sopenharmony_ci * t4vf_query_params - query FW or device parameters 61462306a36Sopenharmony_ci * @adapter: the adapter 61562306a36Sopenharmony_ci * @nparams: the number of parameters 61662306a36Sopenharmony_ci * @params: the parameter names 61762306a36Sopenharmony_ci * @vals: the parameter values 61862306a36Sopenharmony_ci * 61962306a36Sopenharmony_ci * Reads the values of firmware or device parameters. Up to 7 parameters 62062306a36Sopenharmony_ci * can be queried at once. 62162306a36Sopenharmony_ci */ 62262306a36Sopenharmony_cistatic int t4vf_query_params(struct adapter *adapter, unsigned int nparams, 62362306a36Sopenharmony_ci const u32 *params, u32 *vals) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci int i, ret; 62662306a36Sopenharmony_ci struct fw_params_cmd cmd, rpl; 62762306a36Sopenharmony_ci struct fw_params_param *p; 62862306a36Sopenharmony_ci size_t len16; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (nparams > 7) 63162306a36Sopenharmony_ci return -EINVAL; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 63462306a36Sopenharmony_ci cmd.op_to_vfn = cpu_to_be32(FW_CMD_OP_V(FW_PARAMS_CMD) | 63562306a36Sopenharmony_ci FW_CMD_REQUEST_F | 63662306a36Sopenharmony_ci FW_CMD_READ_F); 63762306a36Sopenharmony_ci len16 = DIV_ROUND_UP(offsetof(struct fw_params_cmd, 63862306a36Sopenharmony_ci param[nparams].mnem), 16); 63962306a36Sopenharmony_ci cmd.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(len16)); 64062306a36Sopenharmony_ci for (i = 0, p = &cmd.param[0]; i < nparams; i++, p++) 64162306a36Sopenharmony_ci p->mnem = htonl(*params++); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci ret = t4vf_wr_mbox(adapter, &cmd, sizeof(cmd), &rpl); 64462306a36Sopenharmony_ci if (ret == 0) 64562306a36Sopenharmony_ci for (i = 0, p = &rpl.param[0]; i < nparams; i++, p++) 64662306a36Sopenharmony_ci *vals++ = be32_to_cpu(p->val); 64762306a36Sopenharmony_ci return ret; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/** 65162306a36Sopenharmony_ci * t4vf_set_params - sets FW or device parameters 65262306a36Sopenharmony_ci * @adapter: the adapter 65362306a36Sopenharmony_ci * @nparams: the number of parameters 65462306a36Sopenharmony_ci * @params: the parameter names 65562306a36Sopenharmony_ci * @vals: the parameter values 65662306a36Sopenharmony_ci * 65762306a36Sopenharmony_ci * Sets the values of firmware or device parameters. Up to 7 parameters 65862306a36Sopenharmony_ci * can be specified at once. 65962306a36Sopenharmony_ci */ 66062306a36Sopenharmony_ciint t4vf_set_params(struct adapter *adapter, unsigned int nparams, 66162306a36Sopenharmony_ci const u32 *params, const u32 *vals) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci int i; 66462306a36Sopenharmony_ci struct fw_params_cmd cmd; 66562306a36Sopenharmony_ci struct fw_params_param *p; 66662306a36Sopenharmony_ci size_t len16; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (nparams > 7) 66962306a36Sopenharmony_ci return -EINVAL; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 67262306a36Sopenharmony_ci cmd.op_to_vfn = cpu_to_be32(FW_CMD_OP_V(FW_PARAMS_CMD) | 67362306a36Sopenharmony_ci FW_CMD_REQUEST_F | 67462306a36Sopenharmony_ci FW_CMD_WRITE_F); 67562306a36Sopenharmony_ci len16 = DIV_ROUND_UP(offsetof(struct fw_params_cmd, 67662306a36Sopenharmony_ci param[nparams]), 16); 67762306a36Sopenharmony_ci cmd.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(len16)); 67862306a36Sopenharmony_ci for (i = 0, p = &cmd.param[0]; i < nparams; i++, p++) { 67962306a36Sopenharmony_ci p->mnem = cpu_to_be32(*params++); 68062306a36Sopenharmony_ci p->val = cpu_to_be32(*vals++); 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci return t4vf_wr_mbox(adapter, &cmd, sizeof(cmd), NULL); 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci/** 68762306a36Sopenharmony_ci * t4vf_fl_pkt_align - return the fl packet alignment 68862306a36Sopenharmony_ci * @adapter: the adapter 68962306a36Sopenharmony_ci * 69062306a36Sopenharmony_ci * T4 has a single field to specify the packing and padding boundary. 69162306a36Sopenharmony_ci * T5 onwards has separate fields for this and hence the alignment for 69262306a36Sopenharmony_ci * next packet offset is maximum of these two. And T6 changes the 69362306a36Sopenharmony_ci * Ingress Padding Boundary Shift, so it's all a mess and it's best 69462306a36Sopenharmony_ci * if we put this in low-level Common Code ... 69562306a36Sopenharmony_ci * 69662306a36Sopenharmony_ci */ 69762306a36Sopenharmony_ciint t4vf_fl_pkt_align(struct adapter *adapter) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci u32 sge_control, sge_control2; 70062306a36Sopenharmony_ci unsigned int ingpadboundary, ingpackboundary, fl_align, ingpad_shift; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci sge_control = adapter->params.sge.sge_control; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* T4 uses a single control field to specify both the PCIe Padding and 70562306a36Sopenharmony_ci * Packing Boundary. T5 introduced the ability to specify these 70662306a36Sopenharmony_ci * separately. The actual Ingress Packet Data alignment boundary 70762306a36Sopenharmony_ci * within Packed Buffer Mode is the maximum of these two 70862306a36Sopenharmony_ci * specifications. (Note that it makes no real practical sense to 70962306a36Sopenharmony_ci * have the Pading Boudary be larger than the Packing Boundary but you 71062306a36Sopenharmony_ci * could set the chip up that way and, in fact, legacy T4 code would 71162306a36Sopenharmony_ci * end doing this because it would initialize the Padding Boundary and 71262306a36Sopenharmony_ci * leave the Packing Boundary initialized to 0 (16 bytes).) 71362306a36Sopenharmony_ci * Padding Boundary values in T6 starts from 8B, 71462306a36Sopenharmony_ci * where as it is 32B for T4 and T5. 71562306a36Sopenharmony_ci */ 71662306a36Sopenharmony_ci if (CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5) 71762306a36Sopenharmony_ci ingpad_shift = INGPADBOUNDARY_SHIFT_X; 71862306a36Sopenharmony_ci else 71962306a36Sopenharmony_ci ingpad_shift = T6_INGPADBOUNDARY_SHIFT_X; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci ingpadboundary = 1 << (INGPADBOUNDARY_G(sge_control) + ingpad_shift); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci fl_align = ingpadboundary; 72462306a36Sopenharmony_ci if (!is_t4(adapter->params.chip)) { 72562306a36Sopenharmony_ci /* T5 has a different interpretation of one of the PCIe Packing 72662306a36Sopenharmony_ci * Boundary values. 72762306a36Sopenharmony_ci */ 72862306a36Sopenharmony_ci sge_control2 = adapter->params.sge.sge_control2; 72962306a36Sopenharmony_ci ingpackboundary = INGPACKBOUNDARY_G(sge_control2); 73062306a36Sopenharmony_ci if (ingpackboundary == INGPACKBOUNDARY_16B_X) 73162306a36Sopenharmony_ci ingpackboundary = 16; 73262306a36Sopenharmony_ci else 73362306a36Sopenharmony_ci ingpackboundary = 1 << (ingpackboundary + 73462306a36Sopenharmony_ci INGPACKBOUNDARY_SHIFT_X); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci fl_align = max(ingpadboundary, ingpackboundary); 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci return fl_align; 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci/** 74262306a36Sopenharmony_ci * t4vf_bar2_sge_qregs - return BAR2 SGE Queue register information 74362306a36Sopenharmony_ci * @adapter: the adapter 74462306a36Sopenharmony_ci * @qid: the Queue ID 74562306a36Sopenharmony_ci * @qtype: the Ingress or Egress type for @qid 74662306a36Sopenharmony_ci * @pbar2_qoffset: BAR2 Queue Offset 74762306a36Sopenharmony_ci * @pbar2_qid: BAR2 Queue ID or 0 for Queue ID inferred SGE Queues 74862306a36Sopenharmony_ci * 74962306a36Sopenharmony_ci * Returns the BAR2 SGE Queue Registers information associated with the 75062306a36Sopenharmony_ci * indicated Absolute Queue ID. These are passed back in return value 75162306a36Sopenharmony_ci * pointers. @qtype should be T4_BAR2_QTYPE_EGRESS for Egress Queue 75262306a36Sopenharmony_ci * and T4_BAR2_QTYPE_INGRESS for Ingress Queues. 75362306a36Sopenharmony_ci * 75462306a36Sopenharmony_ci * This may return an error which indicates that BAR2 SGE Queue 75562306a36Sopenharmony_ci * registers aren't available. If an error is not returned, then the 75662306a36Sopenharmony_ci * following values are returned: 75762306a36Sopenharmony_ci * 75862306a36Sopenharmony_ci * *@pbar2_qoffset: the BAR2 Offset of the @qid Registers 75962306a36Sopenharmony_ci * *@pbar2_qid: the BAR2 SGE Queue ID or 0 of @qid 76062306a36Sopenharmony_ci * 76162306a36Sopenharmony_ci * If the returned BAR2 Queue ID is 0, then BAR2 SGE registers which 76262306a36Sopenharmony_ci * require the "Inferred Queue ID" ability may be used. E.g. the 76362306a36Sopenharmony_ci * Write Combining Doorbell Buffer. If the BAR2 Queue ID is not 0, 76462306a36Sopenharmony_ci * then these "Inferred Queue ID" register may not be used. 76562306a36Sopenharmony_ci */ 76662306a36Sopenharmony_ciint t4vf_bar2_sge_qregs(struct adapter *adapter, 76762306a36Sopenharmony_ci unsigned int qid, 76862306a36Sopenharmony_ci enum t4_bar2_qtype qtype, 76962306a36Sopenharmony_ci u64 *pbar2_qoffset, 77062306a36Sopenharmony_ci unsigned int *pbar2_qid) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci unsigned int page_shift, page_size, qpp_shift, qpp_mask; 77362306a36Sopenharmony_ci u64 bar2_page_offset, bar2_qoffset; 77462306a36Sopenharmony_ci unsigned int bar2_qid, bar2_qid_offset, bar2_qinferred; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci /* T4 doesn't support BAR2 SGE Queue registers. 77762306a36Sopenharmony_ci */ 77862306a36Sopenharmony_ci if (is_t4(adapter->params.chip)) 77962306a36Sopenharmony_ci return -EINVAL; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* Get our SGE Page Size parameters. 78262306a36Sopenharmony_ci */ 78362306a36Sopenharmony_ci page_shift = adapter->params.sge.sge_vf_hps + 10; 78462306a36Sopenharmony_ci page_size = 1 << page_shift; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci /* Get the right Queues per Page parameters for our Queue. 78762306a36Sopenharmony_ci */ 78862306a36Sopenharmony_ci qpp_shift = (qtype == T4_BAR2_QTYPE_EGRESS 78962306a36Sopenharmony_ci ? adapter->params.sge.sge_vf_eq_qpp 79062306a36Sopenharmony_ci : adapter->params.sge.sge_vf_iq_qpp); 79162306a36Sopenharmony_ci qpp_mask = (1 << qpp_shift) - 1; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* Calculate the basics of the BAR2 SGE Queue register area: 79462306a36Sopenharmony_ci * o The BAR2 page the Queue registers will be in. 79562306a36Sopenharmony_ci * o The BAR2 Queue ID. 79662306a36Sopenharmony_ci * o The BAR2 Queue ID Offset into the BAR2 page. 79762306a36Sopenharmony_ci */ 79862306a36Sopenharmony_ci bar2_page_offset = ((u64)(qid >> qpp_shift) << page_shift); 79962306a36Sopenharmony_ci bar2_qid = qid & qpp_mask; 80062306a36Sopenharmony_ci bar2_qid_offset = bar2_qid * SGE_UDB_SIZE; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* If the BAR2 Queue ID Offset is less than the Page Size, then the 80362306a36Sopenharmony_ci * hardware will infer the Absolute Queue ID simply from the writes to 80462306a36Sopenharmony_ci * the BAR2 Queue ID Offset within the BAR2 Page (and we need to use a 80562306a36Sopenharmony_ci * BAR2 Queue ID of 0 for those writes). Otherwise, we'll simply 80662306a36Sopenharmony_ci * write to the first BAR2 SGE Queue Area within the BAR2 Page with 80762306a36Sopenharmony_ci * the BAR2 Queue ID and the hardware will infer the Absolute Queue ID 80862306a36Sopenharmony_ci * from the BAR2 Page and BAR2 Queue ID. 80962306a36Sopenharmony_ci * 81062306a36Sopenharmony_ci * One important censequence of this is that some BAR2 SGE registers 81162306a36Sopenharmony_ci * have a "Queue ID" field and we can write the BAR2 SGE Queue ID 81262306a36Sopenharmony_ci * there. But other registers synthesize the SGE Queue ID purely 81362306a36Sopenharmony_ci * from the writes to the registers -- the Write Combined Doorbell 81462306a36Sopenharmony_ci * Buffer is a good example. These BAR2 SGE Registers are only 81562306a36Sopenharmony_ci * available for those BAR2 SGE Register areas where the SGE Absolute 81662306a36Sopenharmony_ci * Queue ID can be inferred from simple writes. 81762306a36Sopenharmony_ci */ 81862306a36Sopenharmony_ci bar2_qoffset = bar2_page_offset; 81962306a36Sopenharmony_ci bar2_qinferred = (bar2_qid_offset < page_size); 82062306a36Sopenharmony_ci if (bar2_qinferred) { 82162306a36Sopenharmony_ci bar2_qoffset += bar2_qid_offset; 82262306a36Sopenharmony_ci bar2_qid = 0; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci *pbar2_qoffset = bar2_qoffset; 82662306a36Sopenharmony_ci *pbar2_qid = bar2_qid; 82762306a36Sopenharmony_ci return 0; 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ciunsigned int t4vf_get_pf_from_vf(struct adapter *adapter) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci u32 whoami; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci whoami = t4_read_reg(adapter, T4VF_PL_BASE_ADDR + PL_VF_WHOAMI_A); 83562306a36Sopenharmony_ci return (CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5 ? 83662306a36Sopenharmony_ci SOURCEPF_G(whoami) : T6_SOURCEPF_G(whoami)); 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci/** 84062306a36Sopenharmony_ci * t4vf_get_sge_params - retrieve adapter Scatter gather Engine parameters 84162306a36Sopenharmony_ci * @adapter: the adapter 84262306a36Sopenharmony_ci * 84362306a36Sopenharmony_ci * Retrieves various core SGE parameters in the form of hardware SGE 84462306a36Sopenharmony_ci * register values. The caller is responsible for decoding these as 84562306a36Sopenharmony_ci * needed. The SGE parameters are stored in @adapter->params.sge. 84662306a36Sopenharmony_ci */ 84762306a36Sopenharmony_ciint t4vf_get_sge_params(struct adapter *adapter) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci struct sge_params *sge_params = &adapter->params.sge; 85062306a36Sopenharmony_ci u32 params[7], vals[7]; 85162306a36Sopenharmony_ci int v; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci params[0] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | 85462306a36Sopenharmony_ci FW_PARAMS_PARAM_XYZ_V(SGE_CONTROL_A)); 85562306a36Sopenharmony_ci params[1] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | 85662306a36Sopenharmony_ci FW_PARAMS_PARAM_XYZ_V(SGE_HOST_PAGE_SIZE_A)); 85762306a36Sopenharmony_ci params[2] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | 85862306a36Sopenharmony_ci FW_PARAMS_PARAM_XYZ_V(SGE_FL_BUFFER_SIZE0_A)); 85962306a36Sopenharmony_ci params[3] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | 86062306a36Sopenharmony_ci FW_PARAMS_PARAM_XYZ_V(SGE_FL_BUFFER_SIZE1_A)); 86162306a36Sopenharmony_ci params[4] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | 86262306a36Sopenharmony_ci FW_PARAMS_PARAM_XYZ_V(SGE_TIMER_VALUE_0_AND_1_A)); 86362306a36Sopenharmony_ci params[5] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | 86462306a36Sopenharmony_ci FW_PARAMS_PARAM_XYZ_V(SGE_TIMER_VALUE_2_AND_3_A)); 86562306a36Sopenharmony_ci params[6] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | 86662306a36Sopenharmony_ci FW_PARAMS_PARAM_XYZ_V(SGE_TIMER_VALUE_4_AND_5_A)); 86762306a36Sopenharmony_ci v = t4vf_query_params(adapter, 7, params, vals); 86862306a36Sopenharmony_ci if (v) 86962306a36Sopenharmony_ci return v; 87062306a36Sopenharmony_ci sge_params->sge_control = vals[0]; 87162306a36Sopenharmony_ci sge_params->sge_host_page_size = vals[1]; 87262306a36Sopenharmony_ci sge_params->sge_fl_buffer_size[0] = vals[2]; 87362306a36Sopenharmony_ci sge_params->sge_fl_buffer_size[1] = vals[3]; 87462306a36Sopenharmony_ci sge_params->sge_timer_value_0_and_1 = vals[4]; 87562306a36Sopenharmony_ci sge_params->sge_timer_value_2_and_3 = vals[5]; 87662306a36Sopenharmony_ci sge_params->sge_timer_value_4_and_5 = vals[6]; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci /* T4 uses a single control field to specify both the PCIe Padding and 87962306a36Sopenharmony_ci * Packing Boundary. T5 introduced the ability to specify these 88062306a36Sopenharmony_ci * separately with the Padding Boundary in SGE_CONTROL and Packing 88162306a36Sopenharmony_ci * Boundary in SGE_CONTROL2. So for T5 and later we need to grab 88262306a36Sopenharmony_ci * SGE_CONTROL in order to determine how ingress packet data will be 88362306a36Sopenharmony_ci * laid out in Packed Buffer Mode. Unfortunately, older versions of 88462306a36Sopenharmony_ci * the firmware won't let us retrieve SGE_CONTROL2 so if we get a 88562306a36Sopenharmony_ci * failure grabbing it we throw an error since we can't figure out the 88662306a36Sopenharmony_ci * right value. 88762306a36Sopenharmony_ci */ 88862306a36Sopenharmony_ci if (!is_t4(adapter->params.chip)) { 88962306a36Sopenharmony_ci params[0] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | 89062306a36Sopenharmony_ci FW_PARAMS_PARAM_XYZ_V(SGE_CONTROL2_A)); 89162306a36Sopenharmony_ci v = t4vf_query_params(adapter, 1, params, vals); 89262306a36Sopenharmony_ci if (v != FW_SUCCESS) { 89362306a36Sopenharmony_ci dev_err(adapter->pdev_dev, 89462306a36Sopenharmony_ci "Unable to get SGE Control2; " 89562306a36Sopenharmony_ci "probably old firmware.\n"); 89662306a36Sopenharmony_ci return v; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci sge_params->sge_control2 = vals[0]; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci params[0] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | 90262306a36Sopenharmony_ci FW_PARAMS_PARAM_XYZ_V(SGE_INGRESS_RX_THRESHOLD_A)); 90362306a36Sopenharmony_ci params[1] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | 90462306a36Sopenharmony_ci FW_PARAMS_PARAM_XYZ_V(SGE_CONM_CTRL_A)); 90562306a36Sopenharmony_ci v = t4vf_query_params(adapter, 2, params, vals); 90662306a36Sopenharmony_ci if (v) 90762306a36Sopenharmony_ci return v; 90862306a36Sopenharmony_ci sge_params->sge_ingress_rx_threshold = vals[0]; 90962306a36Sopenharmony_ci sge_params->sge_congestion_control = vals[1]; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci /* For T5 and later we want to use the new BAR2 Doorbells. 91262306a36Sopenharmony_ci * Unfortunately, older firmware didn't allow the this register to be 91362306a36Sopenharmony_ci * read. 91462306a36Sopenharmony_ci */ 91562306a36Sopenharmony_ci if (!is_t4(adapter->params.chip)) { 91662306a36Sopenharmony_ci unsigned int pf, s_hps, s_qpp; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci params[0] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | 91962306a36Sopenharmony_ci FW_PARAMS_PARAM_XYZ_V( 92062306a36Sopenharmony_ci SGE_EGRESS_QUEUES_PER_PAGE_VF_A)); 92162306a36Sopenharmony_ci params[1] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | 92262306a36Sopenharmony_ci FW_PARAMS_PARAM_XYZ_V( 92362306a36Sopenharmony_ci SGE_INGRESS_QUEUES_PER_PAGE_VF_A)); 92462306a36Sopenharmony_ci v = t4vf_query_params(adapter, 2, params, vals); 92562306a36Sopenharmony_ci if (v != FW_SUCCESS) { 92662306a36Sopenharmony_ci dev_warn(adapter->pdev_dev, 92762306a36Sopenharmony_ci "Unable to get VF SGE Queues/Page; " 92862306a36Sopenharmony_ci "probably old firmware.\n"); 92962306a36Sopenharmony_ci return v; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci sge_params->sge_egress_queues_per_page = vals[0]; 93262306a36Sopenharmony_ci sge_params->sge_ingress_queues_per_page = vals[1]; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci /* We need the Queues/Page for our VF. This is based on the 93562306a36Sopenharmony_ci * PF from which we're instantiated and is indexed in the 93662306a36Sopenharmony_ci * register we just read. Do it once here so other code in 93762306a36Sopenharmony_ci * the driver can just use it. 93862306a36Sopenharmony_ci */ 93962306a36Sopenharmony_ci pf = t4vf_get_pf_from_vf(adapter); 94062306a36Sopenharmony_ci s_hps = (HOSTPAGESIZEPF0_S + 94162306a36Sopenharmony_ci (HOSTPAGESIZEPF1_S - HOSTPAGESIZEPF0_S) * pf); 94262306a36Sopenharmony_ci sge_params->sge_vf_hps = 94362306a36Sopenharmony_ci ((sge_params->sge_host_page_size >> s_hps) 94462306a36Sopenharmony_ci & HOSTPAGESIZEPF0_M); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci s_qpp = (QUEUESPERPAGEPF0_S + 94762306a36Sopenharmony_ci (QUEUESPERPAGEPF1_S - QUEUESPERPAGEPF0_S) * pf); 94862306a36Sopenharmony_ci sge_params->sge_vf_eq_qpp = 94962306a36Sopenharmony_ci ((sge_params->sge_egress_queues_per_page >> s_qpp) 95062306a36Sopenharmony_ci & QUEUESPERPAGEPF0_M); 95162306a36Sopenharmony_ci sge_params->sge_vf_iq_qpp = 95262306a36Sopenharmony_ci ((sge_params->sge_ingress_queues_per_page >> s_qpp) 95362306a36Sopenharmony_ci & QUEUESPERPAGEPF0_M); 95462306a36Sopenharmony_ci } 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci return 0; 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci/** 96062306a36Sopenharmony_ci * t4vf_get_vpd_params - retrieve device VPD paremeters 96162306a36Sopenharmony_ci * @adapter: the adapter 96262306a36Sopenharmony_ci * 96362306a36Sopenharmony_ci * Retrives various device Vital Product Data parameters. The parameters 96462306a36Sopenharmony_ci * are stored in @adapter->params.vpd. 96562306a36Sopenharmony_ci */ 96662306a36Sopenharmony_ciint t4vf_get_vpd_params(struct adapter *adapter) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci struct vpd_params *vpd_params = &adapter->params.vpd; 96962306a36Sopenharmony_ci u32 params[7], vals[7]; 97062306a36Sopenharmony_ci int v; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci params[0] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) | 97362306a36Sopenharmony_ci FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_CCLK)); 97462306a36Sopenharmony_ci v = t4vf_query_params(adapter, 1, params, vals); 97562306a36Sopenharmony_ci if (v) 97662306a36Sopenharmony_ci return v; 97762306a36Sopenharmony_ci vpd_params->cclk = vals[0]; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci return 0; 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci/** 98362306a36Sopenharmony_ci * t4vf_get_dev_params - retrieve device paremeters 98462306a36Sopenharmony_ci * @adapter: the adapter 98562306a36Sopenharmony_ci * 98662306a36Sopenharmony_ci * Retrives various device parameters. The parameters are stored in 98762306a36Sopenharmony_ci * @adapter->params.dev. 98862306a36Sopenharmony_ci */ 98962306a36Sopenharmony_ciint t4vf_get_dev_params(struct adapter *adapter) 99062306a36Sopenharmony_ci{ 99162306a36Sopenharmony_ci struct dev_params *dev_params = &adapter->params.dev; 99262306a36Sopenharmony_ci u32 params[7], vals[7]; 99362306a36Sopenharmony_ci int v; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci params[0] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) | 99662306a36Sopenharmony_ci FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_FWREV)); 99762306a36Sopenharmony_ci params[1] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) | 99862306a36Sopenharmony_ci FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_TPREV)); 99962306a36Sopenharmony_ci v = t4vf_query_params(adapter, 2, params, vals); 100062306a36Sopenharmony_ci if (v) 100162306a36Sopenharmony_ci return v; 100262306a36Sopenharmony_ci dev_params->fwrev = vals[0]; 100362306a36Sopenharmony_ci dev_params->tprev = vals[1]; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci return 0; 100662306a36Sopenharmony_ci} 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci/** 100962306a36Sopenharmony_ci * t4vf_get_rss_glb_config - retrieve adapter RSS Global Configuration 101062306a36Sopenharmony_ci * @adapter: the adapter 101162306a36Sopenharmony_ci * 101262306a36Sopenharmony_ci * Retrieves global RSS mode and parameters with which we have to live 101362306a36Sopenharmony_ci * and stores them in the @adapter's RSS parameters. 101462306a36Sopenharmony_ci */ 101562306a36Sopenharmony_ciint t4vf_get_rss_glb_config(struct adapter *adapter) 101662306a36Sopenharmony_ci{ 101762306a36Sopenharmony_ci struct rss_params *rss = &adapter->params.rss; 101862306a36Sopenharmony_ci struct fw_rss_glb_config_cmd cmd, rpl; 101962306a36Sopenharmony_ci int v; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci /* 102262306a36Sopenharmony_ci * Execute an RSS Global Configuration read command to retrieve 102362306a36Sopenharmony_ci * our RSS configuration. 102462306a36Sopenharmony_ci */ 102562306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 102662306a36Sopenharmony_ci cmd.op_to_write = cpu_to_be32(FW_CMD_OP_V(FW_RSS_GLB_CONFIG_CMD) | 102762306a36Sopenharmony_ci FW_CMD_REQUEST_F | 102862306a36Sopenharmony_ci FW_CMD_READ_F); 102962306a36Sopenharmony_ci cmd.retval_len16 = cpu_to_be32(FW_LEN16(cmd)); 103062306a36Sopenharmony_ci v = t4vf_wr_mbox(adapter, &cmd, sizeof(cmd), &rpl); 103162306a36Sopenharmony_ci if (v) 103262306a36Sopenharmony_ci return v; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci /* 103562306a36Sopenharmony_ci * Transate the big-endian RSS Global Configuration into our 103662306a36Sopenharmony_ci * cpu-endian format based on the RSS mode. We also do first level 103762306a36Sopenharmony_ci * filtering at this point to weed out modes which don't support 103862306a36Sopenharmony_ci * VF Drivers ... 103962306a36Sopenharmony_ci */ 104062306a36Sopenharmony_ci rss->mode = FW_RSS_GLB_CONFIG_CMD_MODE_G( 104162306a36Sopenharmony_ci be32_to_cpu(rpl.u.manual.mode_pkd)); 104262306a36Sopenharmony_ci switch (rss->mode) { 104362306a36Sopenharmony_ci case FW_RSS_GLB_CONFIG_CMD_MODE_BASICVIRTUAL: { 104462306a36Sopenharmony_ci u32 word = be32_to_cpu( 104562306a36Sopenharmony_ci rpl.u.basicvirtual.synmapen_to_hashtoeplitz); 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci rss->u.basicvirtual.synmapen = 104862306a36Sopenharmony_ci ((word & FW_RSS_GLB_CONFIG_CMD_SYNMAPEN_F) != 0); 104962306a36Sopenharmony_ci rss->u.basicvirtual.syn4tupenipv6 = 105062306a36Sopenharmony_ci ((word & FW_RSS_GLB_CONFIG_CMD_SYN4TUPENIPV6_F) != 0); 105162306a36Sopenharmony_ci rss->u.basicvirtual.syn2tupenipv6 = 105262306a36Sopenharmony_ci ((word & FW_RSS_GLB_CONFIG_CMD_SYN2TUPENIPV6_F) != 0); 105362306a36Sopenharmony_ci rss->u.basicvirtual.syn4tupenipv4 = 105462306a36Sopenharmony_ci ((word & FW_RSS_GLB_CONFIG_CMD_SYN4TUPENIPV4_F) != 0); 105562306a36Sopenharmony_ci rss->u.basicvirtual.syn2tupenipv4 = 105662306a36Sopenharmony_ci ((word & FW_RSS_GLB_CONFIG_CMD_SYN2TUPENIPV4_F) != 0); 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci rss->u.basicvirtual.ofdmapen = 105962306a36Sopenharmony_ci ((word & FW_RSS_GLB_CONFIG_CMD_OFDMAPEN_F) != 0); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci rss->u.basicvirtual.tnlmapen = 106262306a36Sopenharmony_ci ((word & FW_RSS_GLB_CONFIG_CMD_TNLMAPEN_F) != 0); 106362306a36Sopenharmony_ci rss->u.basicvirtual.tnlalllookup = 106462306a36Sopenharmony_ci ((word & FW_RSS_GLB_CONFIG_CMD_TNLALLLKP_F) != 0); 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci rss->u.basicvirtual.hashtoeplitz = 106762306a36Sopenharmony_ci ((word & FW_RSS_GLB_CONFIG_CMD_HASHTOEPLITZ_F) != 0); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci /* we need at least Tunnel Map Enable to be set */ 107062306a36Sopenharmony_ci if (!rss->u.basicvirtual.tnlmapen) 107162306a36Sopenharmony_ci return -EINVAL; 107262306a36Sopenharmony_ci break; 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci default: 107662306a36Sopenharmony_ci /* all unknown/unsupported RSS modes result in an error */ 107762306a36Sopenharmony_ci return -EINVAL; 107862306a36Sopenharmony_ci } 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci return 0; 108162306a36Sopenharmony_ci} 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci/** 108462306a36Sopenharmony_ci * t4vf_get_vfres - retrieve VF resource limits 108562306a36Sopenharmony_ci * @adapter: the adapter 108662306a36Sopenharmony_ci * 108762306a36Sopenharmony_ci * Retrieves configured resource limits and capabilities for a virtual 108862306a36Sopenharmony_ci * function. The results are stored in @adapter->vfres. 108962306a36Sopenharmony_ci */ 109062306a36Sopenharmony_ciint t4vf_get_vfres(struct adapter *adapter) 109162306a36Sopenharmony_ci{ 109262306a36Sopenharmony_ci struct vf_resources *vfres = &adapter->params.vfres; 109362306a36Sopenharmony_ci struct fw_pfvf_cmd cmd, rpl; 109462306a36Sopenharmony_ci int v; 109562306a36Sopenharmony_ci u32 word; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci /* 109862306a36Sopenharmony_ci * Execute PFVF Read command to get VF resource limits; bail out early 109962306a36Sopenharmony_ci * with error on command failure. 110062306a36Sopenharmony_ci */ 110162306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 110262306a36Sopenharmony_ci cmd.op_to_vfn = cpu_to_be32(FW_CMD_OP_V(FW_PFVF_CMD) | 110362306a36Sopenharmony_ci FW_CMD_REQUEST_F | 110462306a36Sopenharmony_ci FW_CMD_READ_F); 110562306a36Sopenharmony_ci cmd.retval_len16 = cpu_to_be32(FW_LEN16(cmd)); 110662306a36Sopenharmony_ci v = t4vf_wr_mbox(adapter, &cmd, sizeof(cmd), &rpl); 110762306a36Sopenharmony_ci if (v) 110862306a36Sopenharmony_ci return v; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci /* 111162306a36Sopenharmony_ci * Extract VF resource limits and return success. 111262306a36Sopenharmony_ci */ 111362306a36Sopenharmony_ci word = be32_to_cpu(rpl.niqflint_niq); 111462306a36Sopenharmony_ci vfres->niqflint = FW_PFVF_CMD_NIQFLINT_G(word); 111562306a36Sopenharmony_ci vfres->niq = FW_PFVF_CMD_NIQ_G(word); 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci word = be32_to_cpu(rpl.type_to_neq); 111862306a36Sopenharmony_ci vfres->neq = FW_PFVF_CMD_NEQ_G(word); 111962306a36Sopenharmony_ci vfres->pmask = FW_PFVF_CMD_PMASK_G(word); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci word = be32_to_cpu(rpl.tc_to_nexactf); 112262306a36Sopenharmony_ci vfres->tc = FW_PFVF_CMD_TC_G(word); 112362306a36Sopenharmony_ci vfres->nvi = FW_PFVF_CMD_NVI_G(word); 112462306a36Sopenharmony_ci vfres->nexactf = FW_PFVF_CMD_NEXACTF_G(word); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci word = be32_to_cpu(rpl.r_caps_to_nethctrl); 112762306a36Sopenharmony_ci vfres->r_caps = FW_PFVF_CMD_R_CAPS_G(word); 112862306a36Sopenharmony_ci vfres->wx_caps = FW_PFVF_CMD_WX_CAPS_G(word); 112962306a36Sopenharmony_ci vfres->nethctrl = FW_PFVF_CMD_NETHCTRL_G(word); 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci return 0; 113262306a36Sopenharmony_ci} 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci/** 113562306a36Sopenharmony_ci * t4vf_read_rss_vi_config - read a VI's RSS configuration 113662306a36Sopenharmony_ci * @adapter: the adapter 113762306a36Sopenharmony_ci * @viid: Virtual Interface ID 113862306a36Sopenharmony_ci * @config: pointer to host-native VI RSS Configuration buffer 113962306a36Sopenharmony_ci * 114062306a36Sopenharmony_ci * Reads the Virtual Interface's RSS configuration information and 114162306a36Sopenharmony_ci * translates it into CPU-native format. 114262306a36Sopenharmony_ci */ 114362306a36Sopenharmony_ciint t4vf_read_rss_vi_config(struct adapter *adapter, unsigned int viid, 114462306a36Sopenharmony_ci union rss_vi_config *config) 114562306a36Sopenharmony_ci{ 114662306a36Sopenharmony_ci struct fw_rss_vi_config_cmd cmd, rpl; 114762306a36Sopenharmony_ci int v; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 115062306a36Sopenharmony_ci cmd.op_to_viid = cpu_to_be32(FW_CMD_OP_V(FW_RSS_VI_CONFIG_CMD) | 115162306a36Sopenharmony_ci FW_CMD_REQUEST_F | 115262306a36Sopenharmony_ci FW_CMD_READ_F | 115362306a36Sopenharmony_ci FW_RSS_VI_CONFIG_CMD_VIID(viid)); 115462306a36Sopenharmony_ci cmd.retval_len16 = cpu_to_be32(FW_LEN16(cmd)); 115562306a36Sopenharmony_ci v = t4vf_wr_mbox(adapter, &cmd, sizeof(cmd), &rpl); 115662306a36Sopenharmony_ci if (v) 115762306a36Sopenharmony_ci return v; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci switch (adapter->params.rss.mode) { 116062306a36Sopenharmony_ci case FW_RSS_GLB_CONFIG_CMD_MODE_BASICVIRTUAL: { 116162306a36Sopenharmony_ci u32 word = be32_to_cpu(rpl.u.basicvirtual.defaultq_to_udpen); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci config->basicvirtual.ip6fourtupen = 116462306a36Sopenharmony_ci ((word & FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN_F) != 0); 116562306a36Sopenharmony_ci config->basicvirtual.ip6twotupen = 116662306a36Sopenharmony_ci ((word & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F) != 0); 116762306a36Sopenharmony_ci config->basicvirtual.ip4fourtupen = 116862306a36Sopenharmony_ci ((word & FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN_F) != 0); 116962306a36Sopenharmony_ci config->basicvirtual.ip4twotupen = 117062306a36Sopenharmony_ci ((word & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F) != 0); 117162306a36Sopenharmony_ci config->basicvirtual.udpen = 117262306a36Sopenharmony_ci ((word & FW_RSS_VI_CONFIG_CMD_UDPEN_F) != 0); 117362306a36Sopenharmony_ci config->basicvirtual.defaultq = 117462306a36Sopenharmony_ci FW_RSS_VI_CONFIG_CMD_DEFAULTQ_G(word); 117562306a36Sopenharmony_ci break; 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci default: 117962306a36Sopenharmony_ci return -EINVAL; 118062306a36Sopenharmony_ci } 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci return 0; 118362306a36Sopenharmony_ci} 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci/** 118662306a36Sopenharmony_ci * t4vf_write_rss_vi_config - write a VI's RSS configuration 118762306a36Sopenharmony_ci * @adapter: the adapter 118862306a36Sopenharmony_ci * @viid: Virtual Interface ID 118962306a36Sopenharmony_ci * @config: pointer to host-native VI RSS Configuration buffer 119062306a36Sopenharmony_ci * 119162306a36Sopenharmony_ci * Write the Virtual Interface's RSS configuration information 119262306a36Sopenharmony_ci * (translating it into firmware-native format before writing). 119362306a36Sopenharmony_ci */ 119462306a36Sopenharmony_ciint t4vf_write_rss_vi_config(struct adapter *adapter, unsigned int viid, 119562306a36Sopenharmony_ci union rss_vi_config *config) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci struct fw_rss_vi_config_cmd cmd, rpl; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 120062306a36Sopenharmony_ci cmd.op_to_viid = cpu_to_be32(FW_CMD_OP_V(FW_RSS_VI_CONFIG_CMD) | 120162306a36Sopenharmony_ci FW_CMD_REQUEST_F | 120262306a36Sopenharmony_ci FW_CMD_WRITE_F | 120362306a36Sopenharmony_ci FW_RSS_VI_CONFIG_CMD_VIID(viid)); 120462306a36Sopenharmony_ci cmd.retval_len16 = cpu_to_be32(FW_LEN16(cmd)); 120562306a36Sopenharmony_ci switch (adapter->params.rss.mode) { 120662306a36Sopenharmony_ci case FW_RSS_GLB_CONFIG_CMD_MODE_BASICVIRTUAL: { 120762306a36Sopenharmony_ci u32 word = 0; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci if (config->basicvirtual.ip6fourtupen) 121062306a36Sopenharmony_ci word |= FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN_F; 121162306a36Sopenharmony_ci if (config->basicvirtual.ip6twotupen) 121262306a36Sopenharmony_ci word |= FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F; 121362306a36Sopenharmony_ci if (config->basicvirtual.ip4fourtupen) 121462306a36Sopenharmony_ci word |= FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN_F; 121562306a36Sopenharmony_ci if (config->basicvirtual.ip4twotupen) 121662306a36Sopenharmony_ci word |= FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F; 121762306a36Sopenharmony_ci if (config->basicvirtual.udpen) 121862306a36Sopenharmony_ci word |= FW_RSS_VI_CONFIG_CMD_UDPEN_F; 121962306a36Sopenharmony_ci word |= FW_RSS_VI_CONFIG_CMD_DEFAULTQ_V( 122062306a36Sopenharmony_ci config->basicvirtual.defaultq); 122162306a36Sopenharmony_ci cmd.u.basicvirtual.defaultq_to_udpen = cpu_to_be32(word); 122262306a36Sopenharmony_ci break; 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci default: 122662306a36Sopenharmony_ci return -EINVAL; 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci return t4vf_wr_mbox(adapter, &cmd, sizeof(cmd), &rpl); 123062306a36Sopenharmony_ci} 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci/** 123362306a36Sopenharmony_ci * t4vf_config_rss_range - configure a portion of the RSS mapping table 123462306a36Sopenharmony_ci * @adapter: the adapter 123562306a36Sopenharmony_ci * @viid: Virtual Interface of RSS Table Slice 123662306a36Sopenharmony_ci * @start: starting entry in the table to write 123762306a36Sopenharmony_ci * @n: how many table entries to write 123862306a36Sopenharmony_ci * @rspq: values for the "Response Queue" (Ingress Queue) lookup table 123962306a36Sopenharmony_ci * @nrspq: number of values in @rspq 124062306a36Sopenharmony_ci * 124162306a36Sopenharmony_ci * Programs the selected part of the VI's RSS mapping table with the 124262306a36Sopenharmony_ci * provided values. If @nrspq < @n the supplied values are used repeatedly 124362306a36Sopenharmony_ci * until the full table range is populated. 124462306a36Sopenharmony_ci * 124562306a36Sopenharmony_ci * The caller must ensure the values in @rspq are in the range 0..1023. 124662306a36Sopenharmony_ci */ 124762306a36Sopenharmony_ciint t4vf_config_rss_range(struct adapter *adapter, unsigned int viid, 124862306a36Sopenharmony_ci int start, int n, const u16 *rspq, int nrspq) 124962306a36Sopenharmony_ci{ 125062306a36Sopenharmony_ci const u16 *rsp = rspq; 125162306a36Sopenharmony_ci const u16 *rsp_end = rspq+nrspq; 125262306a36Sopenharmony_ci struct fw_rss_ind_tbl_cmd cmd; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci /* 125562306a36Sopenharmony_ci * Initialize firmware command template to write the RSS table. 125662306a36Sopenharmony_ci */ 125762306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 125862306a36Sopenharmony_ci cmd.op_to_viid = cpu_to_be32(FW_CMD_OP_V(FW_RSS_IND_TBL_CMD) | 125962306a36Sopenharmony_ci FW_CMD_REQUEST_F | 126062306a36Sopenharmony_ci FW_CMD_WRITE_F | 126162306a36Sopenharmony_ci FW_RSS_IND_TBL_CMD_VIID_V(viid)); 126262306a36Sopenharmony_ci cmd.retval_len16 = cpu_to_be32(FW_LEN16(cmd)); 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci /* 126562306a36Sopenharmony_ci * Each firmware RSS command can accommodate up to 32 RSS Ingress 126662306a36Sopenharmony_ci * Queue Identifiers. These Ingress Queue IDs are packed three to 126762306a36Sopenharmony_ci * a 32-bit word as 10-bit values with the upper remaining 2 bits 126862306a36Sopenharmony_ci * reserved. 126962306a36Sopenharmony_ci */ 127062306a36Sopenharmony_ci while (n > 0) { 127162306a36Sopenharmony_ci __be32 *qp = &cmd.iq0_to_iq2; 127262306a36Sopenharmony_ci int nq = min(n, 32); 127362306a36Sopenharmony_ci int ret; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci /* 127662306a36Sopenharmony_ci * Set up the firmware RSS command header to send the next 127762306a36Sopenharmony_ci * "nq" Ingress Queue IDs to the firmware. 127862306a36Sopenharmony_ci */ 127962306a36Sopenharmony_ci cmd.niqid = cpu_to_be16(nq); 128062306a36Sopenharmony_ci cmd.startidx = cpu_to_be16(start); 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci /* 128362306a36Sopenharmony_ci * "nq" more done for the start of the next loop. 128462306a36Sopenharmony_ci */ 128562306a36Sopenharmony_ci start += nq; 128662306a36Sopenharmony_ci n -= nq; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci /* 128962306a36Sopenharmony_ci * While there are still Ingress Queue IDs to stuff into the 129062306a36Sopenharmony_ci * current firmware RSS command, retrieve them from the 129162306a36Sopenharmony_ci * Ingress Queue ID array and insert them into the command. 129262306a36Sopenharmony_ci */ 129362306a36Sopenharmony_ci while (nq > 0) { 129462306a36Sopenharmony_ci /* 129562306a36Sopenharmony_ci * Grab up to the next 3 Ingress Queue IDs (wrapping 129662306a36Sopenharmony_ci * around the Ingress Queue ID array if necessary) and 129762306a36Sopenharmony_ci * insert them into the firmware RSS command at the 129862306a36Sopenharmony_ci * current 3-tuple position within the commad. 129962306a36Sopenharmony_ci */ 130062306a36Sopenharmony_ci u16 qbuf[3]; 130162306a36Sopenharmony_ci u16 *qbp = qbuf; 130262306a36Sopenharmony_ci int nqbuf = min(3, nq); 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci nq -= nqbuf; 130562306a36Sopenharmony_ci qbuf[0] = qbuf[1] = qbuf[2] = 0; 130662306a36Sopenharmony_ci while (nqbuf) { 130762306a36Sopenharmony_ci nqbuf--; 130862306a36Sopenharmony_ci *qbp++ = *rsp++; 130962306a36Sopenharmony_ci if (rsp >= rsp_end) 131062306a36Sopenharmony_ci rsp = rspq; 131162306a36Sopenharmony_ci } 131262306a36Sopenharmony_ci *qp++ = cpu_to_be32(FW_RSS_IND_TBL_CMD_IQ0_V(qbuf[0]) | 131362306a36Sopenharmony_ci FW_RSS_IND_TBL_CMD_IQ1_V(qbuf[1]) | 131462306a36Sopenharmony_ci FW_RSS_IND_TBL_CMD_IQ2_V(qbuf[2])); 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci /* 131862306a36Sopenharmony_ci * Send this portion of the RRS table update to the firmware; 131962306a36Sopenharmony_ci * bail out on any errors. 132062306a36Sopenharmony_ci */ 132162306a36Sopenharmony_ci ret = t4vf_wr_mbox(adapter, &cmd, sizeof(cmd), NULL); 132262306a36Sopenharmony_ci if (ret) 132362306a36Sopenharmony_ci return ret; 132462306a36Sopenharmony_ci } 132562306a36Sopenharmony_ci return 0; 132662306a36Sopenharmony_ci} 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci/** 132962306a36Sopenharmony_ci * t4vf_alloc_vi - allocate a virtual interface on a port 133062306a36Sopenharmony_ci * @adapter: the adapter 133162306a36Sopenharmony_ci * @port_id: physical port associated with the VI 133262306a36Sopenharmony_ci * 133362306a36Sopenharmony_ci * Allocate a new Virtual Interface and bind it to the indicated 133462306a36Sopenharmony_ci * physical port. Return the new Virtual Interface Identifier on 133562306a36Sopenharmony_ci * success, or a [negative] error number on failure. 133662306a36Sopenharmony_ci */ 133762306a36Sopenharmony_ciint t4vf_alloc_vi(struct adapter *adapter, int port_id) 133862306a36Sopenharmony_ci{ 133962306a36Sopenharmony_ci struct fw_vi_cmd cmd, rpl; 134062306a36Sopenharmony_ci int v; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci /* 134362306a36Sopenharmony_ci * Execute a VI command to allocate Virtual Interface and return its 134462306a36Sopenharmony_ci * VIID. 134562306a36Sopenharmony_ci */ 134662306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 134762306a36Sopenharmony_ci cmd.op_to_vfn = cpu_to_be32(FW_CMD_OP_V(FW_VI_CMD) | 134862306a36Sopenharmony_ci FW_CMD_REQUEST_F | 134962306a36Sopenharmony_ci FW_CMD_WRITE_F | 135062306a36Sopenharmony_ci FW_CMD_EXEC_F); 135162306a36Sopenharmony_ci cmd.alloc_to_len16 = cpu_to_be32(FW_LEN16(cmd) | 135262306a36Sopenharmony_ci FW_VI_CMD_ALLOC_F); 135362306a36Sopenharmony_ci cmd.portid_pkd = FW_VI_CMD_PORTID_V(port_id); 135462306a36Sopenharmony_ci v = t4vf_wr_mbox(adapter, &cmd, sizeof(cmd), &rpl); 135562306a36Sopenharmony_ci if (v) 135662306a36Sopenharmony_ci return v; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci return FW_VI_CMD_VIID_G(be16_to_cpu(rpl.type_viid)); 135962306a36Sopenharmony_ci} 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci/** 136262306a36Sopenharmony_ci * t4vf_free_vi -- free a virtual interface 136362306a36Sopenharmony_ci * @adapter: the adapter 136462306a36Sopenharmony_ci * @viid: the virtual interface identifier 136562306a36Sopenharmony_ci * 136662306a36Sopenharmony_ci * Free a previously allocated Virtual Interface. Return an error on 136762306a36Sopenharmony_ci * failure. 136862306a36Sopenharmony_ci */ 136962306a36Sopenharmony_ciint t4vf_free_vi(struct adapter *adapter, int viid) 137062306a36Sopenharmony_ci{ 137162306a36Sopenharmony_ci struct fw_vi_cmd cmd; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci /* 137462306a36Sopenharmony_ci * Execute a VI command to free the Virtual Interface. 137562306a36Sopenharmony_ci */ 137662306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 137762306a36Sopenharmony_ci cmd.op_to_vfn = cpu_to_be32(FW_CMD_OP_V(FW_VI_CMD) | 137862306a36Sopenharmony_ci FW_CMD_REQUEST_F | 137962306a36Sopenharmony_ci FW_CMD_EXEC_F); 138062306a36Sopenharmony_ci cmd.alloc_to_len16 = cpu_to_be32(FW_LEN16(cmd) | 138162306a36Sopenharmony_ci FW_VI_CMD_FREE_F); 138262306a36Sopenharmony_ci cmd.type_viid = cpu_to_be16(FW_VI_CMD_VIID_V(viid)); 138362306a36Sopenharmony_ci return t4vf_wr_mbox(adapter, &cmd, sizeof(cmd), NULL); 138462306a36Sopenharmony_ci} 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci/** 138762306a36Sopenharmony_ci * t4vf_enable_vi - enable/disable a virtual interface 138862306a36Sopenharmony_ci * @adapter: the adapter 138962306a36Sopenharmony_ci * @viid: the Virtual Interface ID 139062306a36Sopenharmony_ci * @rx_en: 1=enable Rx, 0=disable Rx 139162306a36Sopenharmony_ci * @tx_en: 1=enable Tx, 0=disable Tx 139262306a36Sopenharmony_ci * 139362306a36Sopenharmony_ci * Enables/disables a virtual interface. 139462306a36Sopenharmony_ci */ 139562306a36Sopenharmony_ciint t4vf_enable_vi(struct adapter *adapter, unsigned int viid, 139662306a36Sopenharmony_ci bool rx_en, bool tx_en) 139762306a36Sopenharmony_ci{ 139862306a36Sopenharmony_ci struct fw_vi_enable_cmd cmd; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 140162306a36Sopenharmony_ci cmd.op_to_viid = cpu_to_be32(FW_CMD_OP_V(FW_VI_ENABLE_CMD) | 140262306a36Sopenharmony_ci FW_CMD_REQUEST_F | 140362306a36Sopenharmony_ci FW_CMD_EXEC_F | 140462306a36Sopenharmony_ci FW_VI_ENABLE_CMD_VIID_V(viid)); 140562306a36Sopenharmony_ci cmd.ien_to_len16 = cpu_to_be32(FW_VI_ENABLE_CMD_IEN_V(rx_en) | 140662306a36Sopenharmony_ci FW_VI_ENABLE_CMD_EEN_V(tx_en) | 140762306a36Sopenharmony_ci FW_LEN16(cmd)); 140862306a36Sopenharmony_ci return t4vf_wr_mbox(adapter, &cmd, sizeof(cmd), NULL); 140962306a36Sopenharmony_ci} 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci/** 141262306a36Sopenharmony_ci * t4vf_enable_pi - enable/disable a Port's virtual interface 141362306a36Sopenharmony_ci * @adapter: the adapter 141462306a36Sopenharmony_ci * @pi: the Port Information structure 141562306a36Sopenharmony_ci * @rx_en: 1=enable Rx, 0=disable Rx 141662306a36Sopenharmony_ci * @tx_en: 1=enable Tx, 0=disable Tx 141762306a36Sopenharmony_ci * 141862306a36Sopenharmony_ci * Enables/disables a Port's virtual interface. If the Virtual 141962306a36Sopenharmony_ci * Interface enable/disable operation is successful, we notify the 142062306a36Sopenharmony_ci * OS-specific code of a potential Link Status change via the OS Contract 142162306a36Sopenharmony_ci * API t4vf_os_link_changed(). 142262306a36Sopenharmony_ci */ 142362306a36Sopenharmony_ciint t4vf_enable_pi(struct adapter *adapter, struct port_info *pi, 142462306a36Sopenharmony_ci bool rx_en, bool tx_en) 142562306a36Sopenharmony_ci{ 142662306a36Sopenharmony_ci int ret = t4vf_enable_vi(adapter, pi->viid, rx_en, tx_en); 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci if (ret) 142962306a36Sopenharmony_ci return ret; 143062306a36Sopenharmony_ci t4vf_os_link_changed(adapter, pi->pidx, 143162306a36Sopenharmony_ci rx_en && tx_en && pi->link_cfg.link_ok); 143262306a36Sopenharmony_ci return 0; 143362306a36Sopenharmony_ci} 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci/** 143662306a36Sopenharmony_ci * t4vf_identify_port - identify a VI's port by blinking its LED 143762306a36Sopenharmony_ci * @adapter: the adapter 143862306a36Sopenharmony_ci * @viid: the Virtual Interface ID 143962306a36Sopenharmony_ci * @nblinks: how many times to blink LED at 2.5 Hz 144062306a36Sopenharmony_ci * 144162306a36Sopenharmony_ci * Identifies a VI's port by blinking its LED. 144262306a36Sopenharmony_ci */ 144362306a36Sopenharmony_ciint t4vf_identify_port(struct adapter *adapter, unsigned int viid, 144462306a36Sopenharmony_ci unsigned int nblinks) 144562306a36Sopenharmony_ci{ 144662306a36Sopenharmony_ci struct fw_vi_enable_cmd cmd; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 144962306a36Sopenharmony_ci cmd.op_to_viid = cpu_to_be32(FW_CMD_OP_V(FW_VI_ENABLE_CMD) | 145062306a36Sopenharmony_ci FW_CMD_REQUEST_F | 145162306a36Sopenharmony_ci FW_CMD_EXEC_F | 145262306a36Sopenharmony_ci FW_VI_ENABLE_CMD_VIID_V(viid)); 145362306a36Sopenharmony_ci cmd.ien_to_len16 = cpu_to_be32(FW_VI_ENABLE_CMD_LED_F | 145462306a36Sopenharmony_ci FW_LEN16(cmd)); 145562306a36Sopenharmony_ci cmd.blinkdur = cpu_to_be16(nblinks); 145662306a36Sopenharmony_ci return t4vf_wr_mbox(adapter, &cmd, sizeof(cmd), NULL); 145762306a36Sopenharmony_ci} 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci/** 146062306a36Sopenharmony_ci * t4vf_set_rxmode - set Rx properties of a virtual interface 146162306a36Sopenharmony_ci * @adapter: the adapter 146262306a36Sopenharmony_ci * @viid: the VI id 146362306a36Sopenharmony_ci * @mtu: the new MTU or -1 for no change 146462306a36Sopenharmony_ci * @promisc: 1 to enable promiscuous mode, 0 to disable it, -1 no change 146562306a36Sopenharmony_ci * @all_multi: 1 to enable all-multi mode, 0 to disable it, -1 no change 146662306a36Sopenharmony_ci * @bcast: 1 to enable broadcast Rx, 0 to disable it, -1 no change 146762306a36Sopenharmony_ci * @vlanex: 1 to enable hardware VLAN Tag extraction, 0 to disable it, 146862306a36Sopenharmony_ci * -1 no change 146962306a36Sopenharmony_ci * @sleep_ok: call is allowed to sleep 147062306a36Sopenharmony_ci * 147162306a36Sopenharmony_ci * Sets Rx properties of a virtual interface. 147262306a36Sopenharmony_ci */ 147362306a36Sopenharmony_ciint t4vf_set_rxmode(struct adapter *adapter, unsigned int viid, 147462306a36Sopenharmony_ci int mtu, int promisc, int all_multi, int bcast, int vlanex, 147562306a36Sopenharmony_ci bool sleep_ok) 147662306a36Sopenharmony_ci{ 147762306a36Sopenharmony_ci struct fw_vi_rxmode_cmd cmd; 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci /* convert to FW values */ 148062306a36Sopenharmony_ci if (mtu < 0) 148162306a36Sopenharmony_ci mtu = FW_VI_RXMODE_CMD_MTU_M; 148262306a36Sopenharmony_ci if (promisc < 0) 148362306a36Sopenharmony_ci promisc = FW_VI_RXMODE_CMD_PROMISCEN_M; 148462306a36Sopenharmony_ci if (all_multi < 0) 148562306a36Sopenharmony_ci all_multi = FW_VI_RXMODE_CMD_ALLMULTIEN_M; 148662306a36Sopenharmony_ci if (bcast < 0) 148762306a36Sopenharmony_ci bcast = FW_VI_RXMODE_CMD_BROADCASTEN_M; 148862306a36Sopenharmony_ci if (vlanex < 0) 148962306a36Sopenharmony_ci vlanex = FW_VI_RXMODE_CMD_VLANEXEN_M; 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 149262306a36Sopenharmony_ci cmd.op_to_viid = cpu_to_be32(FW_CMD_OP_V(FW_VI_RXMODE_CMD) | 149362306a36Sopenharmony_ci FW_CMD_REQUEST_F | 149462306a36Sopenharmony_ci FW_CMD_WRITE_F | 149562306a36Sopenharmony_ci FW_VI_RXMODE_CMD_VIID_V(viid)); 149662306a36Sopenharmony_ci cmd.retval_len16 = cpu_to_be32(FW_LEN16(cmd)); 149762306a36Sopenharmony_ci cmd.mtu_to_vlanexen = 149862306a36Sopenharmony_ci cpu_to_be32(FW_VI_RXMODE_CMD_MTU_V(mtu) | 149962306a36Sopenharmony_ci FW_VI_RXMODE_CMD_PROMISCEN_V(promisc) | 150062306a36Sopenharmony_ci FW_VI_RXMODE_CMD_ALLMULTIEN_V(all_multi) | 150162306a36Sopenharmony_ci FW_VI_RXMODE_CMD_BROADCASTEN_V(bcast) | 150262306a36Sopenharmony_ci FW_VI_RXMODE_CMD_VLANEXEN_V(vlanex)); 150362306a36Sopenharmony_ci return t4vf_wr_mbox_core(adapter, &cmd, sizeof(cmd), NULL, sleep_ok); 150462306a36Sopenharmony_ci} 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci/** 150762306a36Sopenharmony_ci * t4vf_alloc_mac_filt - allocates exact-match filters for MAC addresses 150862306a36Sopenharmony_ci * @adapter: the adapter 150962306a36Sopenharmony_ci * @viid: the Virtual Interface Identifier 151062306a36Sopenharmony_ci * @free: if true any existing filters for this VI id are first removed 151162306a36Sopenharmony_ci * @naddr: the number of MAC addresses to allocate filters for (up to 7) 151262306a36Sopenharmony_ci * @addr: the MAC address(es) 151362306a36Sopenharmony_ci * @idx: where to store the index of each allocated filter 151462306a36Sopenharmony_ci * @hash: pointer to hash address filter bitmap 151562306a36Sopenharmony_ci * @sleep_ok: call is allowed to sleep 151662306a36Sopenharmony_ci * 151762306a36Sopenharmony_ci * Allocates an exact-match filter for each of the supplied addresses and 151862306a36Sopenharmony_ci * sets it to the corresponding address. If @idx is not %NULL it should 151962306a36Sopenharmony_ci * have at least @naddr entries, each of which will be set to the index of 152062306a36Sopenharmony_ci * the filter allocated for the corresponding MAC address. If a filter 152162306a36Sopenharmony_ci * could not be allocated for an address its index is set to 0xffff. 152262306a36Sopenharmony_ci * If @hash is not %NULL addresses that fail to allocate an exact filter 152362306a36Sopenharmony_ci * are hashed and update the hash filter bitmap pointed at by @hash. 152462306a36Sopenharmony_ci * 152562306a36Sopenharmony_ci * Returns a negative error number or the number of filters allocated. 152662306a36Sopenharmony_ci */ 152762306a36Sopenharmony_ciint t4vf_alloc_mac_filt(struct adapter *adapter, unsigned int viid, bool free, 152862306a36Sopenharmony_ci unsigned int naddr, const u8 **addr, u16 *idx, 152962306a36Sopenharmony_ci u64 *hash, bool sleep_ok) 153062306a36Sopenharmony_ci{ 153162306a36Sopenharmony_ci int offset, ret = 0; 153262306a36Sopenharmony_ci unsigned nfilters = 0; 153362306a36Sopenharmony_ci unsigned int rem = naddr; 153462306a36Sopenharmony_ci struct fw_vi_mac_cmd cmd, rpl; 153562306a36Sopenharmony_ci unsigned int max_naddr = adapter->params.arch.mps_tcam_size; 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci if (naddr > max_naddr) 153862306a36Sopenharmony_ci return -EINVAL; 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci for (offset = 0; offset < naddr; /**/) { 154162306a36Sopenharmony_ci unsigned int fw_naddr = (rem < ARRAY_SIZE(cmd.u.exact) 154262306a36Sopenharmony_ci ? rem 154362306a36Sopenharmony_ci : ARRAY_SIZE(cmd.u.exact)); 154462306a36Sopenharmony_ci size_t len16 = DIV_ROUND_UP(offsetof(struct fw_vi_mac_cmd, 154562306a36Sopenharmony_ci u.exact[fw_naddr]), 16); 154662306a36Sopenharmony_ci struct fw_vi_mac_exact *p; 154762306a36Sopenharmony_ci int i; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 155062306a36Sopenharmony_ci cmd.op_to_viid = cpu_to_be32(FW_CMD_OP_V(FW_VI_MAC_CMD) | 155162306a36Sopenharmony_ci FW_CMD_REQUEST_F | 155262306a36Sopenharmony_ci FW_CMD_WRITE_F | 155362306a36Sopenharmony_ci (free ? FW_CMD_EXEC_F : 0) | 155462306a36Sopenharmony_ci FW_VI_MAC_CMD_VIID_V(viid)); 155562306a36Sopenharmony_ci cmd.freemacs_to_len16 = 155662306a36Sopenharmony_ci cpu_to_be32(FW_VI_MAC_CMD_FREEMACS_V(free) | 155762306a36Sopenharmony_ci FW_CMD_LEN16_V(len16)); 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci for (i = 0, p = cmd.u.exact; i < fw_naddr; i++, p++) { 156062306a36Sopenharmony_ci p->valid_to_idx = cpu_to_be16( 156162306a36Sopenharmony_ci FW_VI_MAC_CMD_VALID_F | 156262306a36Sopenharmony_ci FW_VI_MAC_CMD_IDX_V(FW_VI_MAC_ADD_MAC)); 156362306a36Sopenharmony_ci memcpy(p->macaddr, addr[offset+i], sizeof(p->macaddr)); 156462306a36Sopenharmony_ci } 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci ret = t4vf_wr_mbox_core(adapter, &cmd, sizeof(cmd), &rpl, 156862306a36Sopenharmony_ci sleep_ok); 156962306a36Sopenharmony_ci if (ret && ret != -ENOMEM) 157062306a36Sopenharmony_ci break; 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci for (i = 0, p = rpl.u.exact; i < fw_naddr; i++, p++) { 157362306a36Sopenharmony_ci u16 index = FW_VI_MAC_CMD_IDX_G( 157462306a36Sopenharmony_ci be16_to_cpu(p->valid_to_idx)); 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci if (idx) 157762306a36Sopenharmony_ci idx[offset+i] = 157862306a36Sopenharmony_ci (index >= max_naddr 157962306a36Sopenharmony_ci ? 0xffff 158062306a36Sopenharmony_ci : index); 158162306a36Sopenharmony_ci if (index < max_naddr) 158262306a36Sopenharmony_ci nfilters++; 158362306a36Sopenharmony_ci else if (hash) 158462306a36Sopenharmony_ci *hash |= (1ULL << hash_mac_addr(addr[offset+i])); 158562306a36Sopenharmony_ci } 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci free = false; 158862306a36Sopenharmony_ci offset += fw_naddr; 158962306a36Sopenharmony_ci rem -= fw_naddr; 159062306a36Sopenharmony_ci } 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci /* 159362306a36Sopenharmony_ci * If there were no errors or we merely ran out of room in our MAC 159462306a36Sopenharmony_ci * address arena, return the number of filters actually written. 159562306a36Sopenharmony_ci */ 159662306a36Sopenharmony_ci if (ret == 0 || ret == -ENOMEM) 159762306a36Sopenharmony_ci ret = nfilters; 159862306a36Sopenharmony_ci return ret; 159962306a36Sopenharmony_ci} 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci/** 160262306a36Sopenharmony_ci * t4vf_free_mac_filt - frees exact-match filters of given MAC addresses 160362306a36Sopenharmony_ci * @adapter: the adapter 160462306a36Sopenharmony_ci * @viid: the VI id 160562306a36Sopenharmony_ci * @naddr: the number of MAC addresses to allocate filters for (up to 7) 160662306a36Sopenharmony_ci * @addr: the MAC address(es) 160762306a36Sopenharmony_ci * @sleep_ok: call is allowed to sleep 160862306a36Sopenharmony_ci * 160962306a36Sopenharmony_ci * Frees the exact-match filter for each of the supplied addresses 161062306a36Sopenharmony_ci * 161162306a36Sopenharmony_ci * Returns a negative error number or the number of filters freed. 161262306a36Sopenharmony_ci */ 161362306a36Sopenharmony_ciint t4vf_free_mac_filt(struct adapter *adapter, unsigned int viid, 161462306a36Sopenharmony_ci unsigned int naddr, const u8 **addr, bool sleep_ok) 161562306a36Sopenharmony_ci{ 161662306a36Sopenharmony_ci int offset, ret = 0; 161762306a36Sopenharmony_ci struct fw_vi_mac_cmd cmd; 161862306a36Sopenharmony_ci unsigned int nfilters = 0; 161962306a36Sopenharmony_ci unsigned int max_naddr = adapter->params.arch.mps_tcam_size; 162062306a36Sopenharmony_ci unsigned int rem = naddr; 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci if (naddr > max_naddr) 162362306a36Sopenharmony_ci return -EINVAL; 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci for (offset = 0; offset < (int)naddr ; /**/) { 162662306a36Sopenharmony_ci unsigned int fw_naddr = (rem < ARRAY_SIZE(cmd.u.exact) ? 162762306a36Sopenharmony_ci rem : ARRAY_SIZE(cmd.u.exact)); 162862306a36Sopenharmony_ci size_t len16 = DIV_ROUND_UP(offsetof(struct fw_vi_mac_cmd, 162962306a36Sopenharmony_ci u.exact[fw_naddr]), 16); 163062306a36Sopenharmony_ci struct fw_vi_mac_exact *p; 163162306a36Sopenharmony_ci int i; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 163462306a36Sopenharmony_ci cmd.op_to_viid = cpu_to_be32(FW_CMD_OP_V(FW_VI_MAC_CMD) | 163562306a36Sopenharmony_ci FW_CMD_REQUEST_F | 163662306a36Sopenharmony_ci FW_CMD_WRITE_F | 163762306a36Sopenharmony_ci FW_CMD_EXEC_V(0) | 163862306a36Sopenharmony_ci FW_VI_MAC_CMD_VIID_V(viid)); 163962306a36Sopenharmony_ci cmd.freemacs_to_len16 = 164062306a36Sopenharmony_ci cpu_to_be32(FW_VI_MAC_CMD_FREEMACS_V(0) | 164162306a36Sopenharmony_ci FW_CMD_LEN16_V(len16)); 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci for (i = 0, p = cmd.u.exact; i < (int)fw_naddr; i++, p++) { 164462306a36Sopenharmony_ci p->valid_to_idx = cpu_to_be16( 164562306a36Sopenharmony_ci FW_VI_MAC_CMD_VALID_F | 164662306a36Sopenharmony_ci FW_VI_MAC_CMD_IDX_V(FW_VI_MAC_MAC_BASED_FREE)); 164762306a36Sopenharmony_ci memcpy(p->macaddr, addr[offset+i], sizeof(p->macaddr)); 164862306a36Sopenharmony_ci } 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci ret = t4vf_wr_mbox_core(adapter, &cmd, sizeof(cmd), &cmd, 165162306a36Sopenharmony_ci sleep_ok); 165262306a36Sopenharmony_ci if (ret) 165362306a36Sopenharmony_ci break; 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci for (i = 0, p = cmd.u.exact; i < fw_naddr; i++, p++) { 165662306a36Sopenharmony_ci u16 index = FW_VI_MAC_CMD_IDX_G( 165762306a36Sopenharmony_ci be16_to_cpu(p->valid_to_idx)); 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci if (index < max_naddr) 166062306a36Sopenharmony_ci nfilters++; 166162306a36Sopenharmony_ci } 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci offset += fw_naddr; 166462306a36Sopenharmony_ci rem -= fw_naddr; 166562306a36Sopenharmony_ci } 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci if (ret == 0) 166862306a36Sopenharmony_ci ret = nfilters; 166962306a36Sopenharmony_ci return ret; 167062306a36Sopenharmony_ci} 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci/** 167362306a36Sopenharmony_ci * t4vf_change_mac - modifies the exact-match filter for a MAC address 167462306a36Sopenharmony_ci * @adapter: the adapter 167562306a36Sopenharmony_ci * @viid: the Virtual Interface ID 167662306a36Sopenharmony_ci * @idx: index of existing filter for old value of MAC address, or -1 167762306a36Sopenharmony_ci * @addr: the new MAC address value 167862306a36Sopenharmony_ci * @persist: if idx < 0, the new MAC allocation should be persistent 167962306a36Sopenharmony_ci * 168062306a36Sopenharmony_ci * Modifies an exact-match filter and sets it to the new MAC address. 168162306a36Sopenharmony_ci * Note that in general it is not possible to modify the value of a given 168262306a36Sopenharmony_ci * filter so the generic way to modify an address filter is to free the 168362306a36Sopenharmony_ci * one being used by the old address value and allocate a new filter for 168462306a36Sopenharmony_ci * the new address value. @idx can be -1 if the address is a new 168562306a36Sopenharmony_ci * addition. 168662306a36Sopenharmony_ci * 168762306a36Sopenharmony_ci * Returns a negative error number or the index of the filter with the new 168862306a36Sopenharmony_ci * MAC value. 168962306a36Sopenharmony_ci */ 169062306a36Sopenharmony_ciint t4vf_change_mac(struct adapter *adapter, unsigned int viid, 169162306a36Sopenharmony_ci int idx, const u8 *addr, bool persist) 169262306a36Sopenharmony_ci{ 169362306a36Sopenharmony_ci int ret; 169462306a36Sopenharmony_ci struct fw_vi_mac_cmd cmd, rpl; 169562306a36Sopenharmony_ci struct fw_vi_mac_exact *p = &cmd.u.exact[0]; 169662306a36Sopenharmony_ci size_t len16 = DIV_ROUND_UP(offsetof(struct fw_vi_mac_cmd, 169762306a36Sopenharmony_ci u.exact[1]), 16); 169862306a36Sopenharmony_ci unsigned int max_mac_addr = adapter->params.arch.mps_tcam_size; 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci /* 170162306a36Sopenharmony_ci * If this is a new allocation, determine whether it should be 170262306a36Sopenharmony_ci * persistent (across a "freemacs" operation) or not. 170362306a36Sopenharmony_ci */ 170462306a36Sopenharmony_ci if (idx < 0) 170562306a36Sopenharmony_ci idx = persist ? FW_VI_MAC_ADD_PERSIST_MAC : FW_VI_MAC_ADD_MAC; 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 170862306a36Sopenharmony_ci cmd.op_to_viid = cpu_to_be32(FW_CMD_OP_V(FW_VI_MAC_CMD) | 170962306a36Sopenharmony_ci FW_CMD_REQUEST_F | 171062306a36Sopenharmony_ci FW_CMD_WRITE_F | 171162306a36Sopenharmony_ci FW_VI_MAC_CMD_VIID_V(viid)); 171262306a36Sopenharmony_ci cmd.freemacs_to_len16 = cpu_to_be32(FW_CMD_LEN16_V(len16)); 171362306a36Sopenharmony_ci p->valid_to_idx = cpu_to_be16(FW_VI_MAC_CMD_VALID_F | 171462306a36Sopenharmony_ci FW_VI_MAC_CMD_IDX_V(idx)); 171562306a36Sopenharmony_ci memcpy(p->macaddr, addr, sizeof(p->macaddr)); 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci ret = t4vf_wr_mbox(adapter, &cmd, sizeof(cmd), &rpl); 171862306a36Sopenharmony_ci if (ret == 0) { 171962306a36Sopenharmony_ci p = &rpl.u.exact[0]; 172062306a36Sopenharmony_ci ret = FW_VI_MAC_CMD_IDX_G(be16_to_cpu(p->valid_to_idx)); 172162306a36Sopenharmony_ci if (ret >= max_mac_addr) 172262306a36Sopenharmony_ci ret = -ENOMEM; 172362306a36Sopenharmony_ci } 172462306a36Sopenharmony_ci return ret; 172562306a36Sopenharmony_ci} 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci/** 172862306a36Sopenharmony_ci * t4vf_set_addr_hash - program the MAC inexact-match hash filter 172962306a36Sopenharmony_ci * @adapter: the adapter 173062306a36Sopenharmony_ci * @viid: the Virtual Interface Identifier 173162306a36Sopenharmony_ci * @ucast: whether the hash filter should also match unicast addresses 173262306a36Sopenharmony_ci * @vec: the value to be written to the hash filter 173362306a36Sopenharmony_ci * @sleep_ok: call is allowed to sleep 173462306a36Sopenharmony_ci * 173562306a36Sopenharmony_ci * Sets the 64-bit inexact-match hash filter for a virtual interface. 173662306a36Sopenharmony_ci */ 173762306a36Sopenharmony_ciint t4vf_set_addr_hash(struct adapter *adapter, unsigned int viid, 173862306a36Sopenharmony_ci bool ucast, u64 vec, bool sleep_ok) 173962306a36Sopenharmony_ci{ 174062306a36Sopenharmony_ci struct fw_vi_mac_cmd cmd; 174162306a36Sopenharmony_ci size_t len16 = DIV_ROUND_UP(offsetof(struct fw_vi_mac_cmd, 174262306a36Sopenharmony_ci u.exact[0]), 16); 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 174562306a36Sopenharmony_ci cmd.op_to_viid = cpu_to_be32(FW_CMD_OP_V(FW_VI_MAC_CMD) | 174662306a36Sopenharmony_ci FW_CMD_REQUEST_F | 174762306a36Sopenharmony_ci FW_CMD_WRITE_F | 174862306a36Sopenharmony_ci FW_VI_ENABLE_CMD_VIID_V(viid)); 174962306a36Sopenharmony_ci cmd.freemacs_to_len16 = cpu_to_be32(FW_VI_MAC_CMD_HASHVECEN_F | 175062306a36Sopenharmony_ci FW_VI_MAC_CMD_HASHUNIEN_V(ucast) | 175162306a36Sopenharmony_ci FW_CMD_LEN16_V(len16)); 175262306a36Sopenharmony_ci cmd.u.hash.hashvec = cpu_to_be64(vec); 175362306a36Sopenharmony_ci return t4vf_wr_mbox_core(adapter, &cmd, sizeof(cmd), NULL, sleep_ok); 175462306a36Sopenharmony_ci} 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci/** 175762306a36Sopenharmony_ci * t4vf_get_port_stats - collect "port" statistics 175862306a36Sopenharmony_ci * @adapter: the adapter 175962306a36Sopenharmony_ci * @pidx: the port index 176062306a36Sopenharmony_ci * @s: the stats structure to fill 176162306a36Sopenharmony_ci * 176262306a36Sopenharmony_ci * Collect statistics for the "port"'s Virtual Interface. 176362306a36Sopenharmony_ci */ 176462306a36Sopenharmony_ciint t4vf_get_port_stats(struct adapter *adapter, int pidx, 176562306a36Sopenharmony_ci struct t4vf_port_stats *s) 176662306a36Sopenharmony_ci{ 176762306a36Sopenharmony_ci struct port_info *pi = adap2pinfo(adapter, pidx); 176862306a36Sopenharmony_ci struct fw_vi_stats_vf fwstats; 176962306a36Sopenharmony_ci unsigned int rem = VI_VF_NUM_STATS; 177062306a36Sopenharmony_ci __be64 *fwsp = (__be64 *)&fwstats; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci /* 177362306a36Sopenharmony_ci * Grab the Virtual Interface statistics a chunk at a time via mailbox 177462306a36Sopenharmony_ci * commands. We could use a Work Request and get all of them at once 177562306a36Sopenharmony_ci * but that's an asynchronous interface which is awkward to use. 177662306a36Sopenharmony_ci */ 177762306a36Sopenharmony_ci while (rem) { 177862306a36Sopenharmony_ci unsigned int ix = VI_VF_NUM_STATS - rem; 177962306a36Sopenharmony_ci unsigned int nstats = min(6U, rem); 178062306a36Sopenharmony_ci struct fw_vi_stats_cmd cmd, rpl; 178162306a36Sopenharmony_ci size_t len = (offsetof(struct fw_vi_stats_cmd, u) + 178262306a36Sopenharmony_ci sizeof(struct fw_vi_stats_ctl)); 178362306a36Sopenharmony_ci size_t len16 = DIV_ROUND_UP(len, 16); 178462306a36Sopenharmony_ci int ret; 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 178762306a36Sopenharmony_ci cmd.op_to_viid = cpu_to_be32(FW_CMD_OP_V(FW_VI_STATS_CMD) | 178862306a36Sopenharmony_ci FW_VI_STATS_CMD_VIID_V(pi->viid) | 178962306a36Sopenharmony_ci FW_CMD_REQUEST_F | 179062306a36Sopenharmony_ci FW_CMD_READ_F); 179162306a36Sopenharmony_ci cmd.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(len16)); 179262306a36Sopenharmony_ci cmd.u.ctl.nstats_ix = 179362306a36Sopenharmony_ci cpu_to_be16(FW_VI_STATS_CMD_IX_V(ix) | 179462306a36Sopenharmony_ci FW_VI_STATS_CMD_NSTATS_V(nstats)); 179562306a36Sopenharmony_ci ret = t4vf_wr_mbox_ns(adapter, &cmd, len, &rpl); 179662306a36Sopenharmony_ci if (ret) 179762306a36Sopenharmony_ci return ret; 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci memcpy(fwsp, &rpl.u.ctl.stat0, sizeof(__be64) * nstats); 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci rem -= nstats; 180262306a36Sopenharmony_ci fwsp += nstats; 180362306a36Sopenharmony_ci } 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci /* 180662306a36Sopenharmony_ci * Translate firmware statistics into host native statistics. 180762306a36Sopenharmony_ci */ 180862306a36Sopenharmony_ci s->tx_bcast_bytes = be64_to_cpu(fwstats.tx_bcast_bytes); 180962306a36Sopenharmony_ci s->tx_bcast_frames = be64_to_cpu(fwstats.tx_bcast_frames); 181062306a36Sopenharmony_ci s->tx_mcast_bytes = be64_to_cpu(fwstats.tx_mcast_bytes); 181162306a36Sopenharmony_ci s->tx_mcast_frames = be64_to_cpu(fwstats.tx_mcast_frames); 181262306a36Sopenharmony_ci s->tx_ucast_bytes = be64_to_cpu(fwstats.tx_ucast_bytes); 181362306a36Sopenharmony_ci s->tx_ucast_frames = be64_to_cpu(fwstats.tx_ucast_frames); 181462306a36Sopenharmony_ci s->tx_drop_frames = be64_to_cpu(fwstats.tx_drop_frames); 181562306a36Sopenharmony_ci s->tx_offload_bytes = be64_to_cpu(fwstats.tx_offload_bytes); 181662306a36Sopenharmony_ci s->tx_offload_frames = be64_to_cpu(fwstats.tx_offload_frames); 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci s->rx_bcast_bytes = be64_to_cpu(fwstats.rx_bcast_bytes); 181962306a36Sopenharmony_ci s->rx_bcast_frames = be64_to_cpu(fwstats.rx_bcast_frames); 182062306a36Sopenharmony_ci s->rx_mcast_bytes = be64_to_cpu(fwstats.rx_mcast_bytes); 182162306a36Sopenharmony_ci s->rx_mcast_frames = be64_to_cpu(fwstats.rx_mcast_frames); 182262306a36Sopenharmony_ci s->rx_ucast_bytes = be64_to_cpu(fwstats.rx_ucast_bytes); 182362306a36Sopenharmony_ci s->rx_ucast_frames = be64_to_cpu(fwstats.rx_ucast_frames); 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci s->rx_err_frames = be64_to_cpu(fwstats.rx_err_frames); 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci return 0; 182862306a36Sopenharmony_ci} 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci/** 183162306a36Sopenharmony_ci * t4vf_iq_free - free an ingress queue and its free lists 183262306a36Sopenharmony_ci * @adapter: the adapter 183362306a36Sopenharmony_ci * @iqtype: the ingress queue type (FW_IQ_TYPE_FL_INT_CAP, etc.) 183462306a36Sopenharmony_ci * @iqid: ingress queue ID 183562306a36Sopenharmony_ci * @fl0id: FL0 queue ID or 0xffff if no attached FL0 183662306a36Sopenharmony_ci * @fl1id: FL1 queue ID or 0xffff if no attached FL1 183762306a36Sopenharmony_ci * 183862306a36Sopenharmony_ci * Frees an ingress queue and its associated free lists, if any. 183962306a36Sopenharmony_ci */ 184062306a36Sopenharmony_ciint t4vf_iq_free(struct adapter *adapter, unsigned int iqtype, 184162306a36Sopenharmony_ci unsigned int iqid, unsigned int fl0id, unsigned int fl1id) 184262306a36Sopenharmony_ci{ 184362306a36Sopenharmony_ci struct fw_iq_cmd cmd; 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 184662306a36Sopenharmony_ci cmd.op_to_vfn = cpu_to_be32(FW_CMD_OP_V(FW_IQ_CMD) | 184762306a36Sopenharmony_ci FW_CMD_REQUEST_F | 184862306a36Sopenharmony_ci FW_CMD_EXEC_F); 184962306a36Sopenharmony_ci cmd.alloc_to_len16 = cpu_to_be32(FW_IQ_CMD_FREE_F | 185062306a36Sopenharmony_ci FW_LEN16(cmd)); 185162306a36Sopenharmony_ci cmd.type_to_iqandstindex = 185262306a36Sopenharmony_ci cpu_to_be32(FW_IQ_CMD_TYPE_V(iqtype)); 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci cmd.iqid = cpu_to_be16(iqid); 185562306a36Sopenharmony_ci cmd.fl0id = cpu_to_be16(fl0id); 185662306a36Sopenharmony_ci cmd.fl1id = cpu_to_be16(fl1id); 185762306a36Sopenharmony_ci return t4vf_wr_mbox(adapter, &cmd, sizeof(cmd), NULL); 185862306a36Sopenharmony_ci} 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci/** 186162306a36Sopenharmony_ci * t4vf_eth_eq_free - free an Ethernet egress queue 186262306a36Sopenharmony_ci * @adapter: the adapter 186362306a36Sopenharmony_ci * @eqid: egress queue ID 186462306a36Sopenharmony_ci * 186562306a36Sopenharmony_ci * Frees an Ethernet egress queue. 186662306a36Sopenharmony_ci */ 186762306a36Sopenharmony_ciint t4vf_eth_eq_free(struct adapter *adapter, unsigned int eqid) 186862306a36Sopenharmony_ci{ 186962306a36Sopenharmony_ci struct fw_eq_eth_cmd cmd; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 187262306a36Sopenharmony_ci cmd.op_to_vfn = cpu_to_be32(FW_CMD_OP_V(FW_EQ_ETH_CMD) | 187362306a36Sopenharmony_ci FW_CMD_REQUEST_F | 187462306a36Sopenharmony_ci FW_CMD_EXEC_F); 187562306a36Sopenharmony_ci cmd.alloc_to_len16 = cpu_to_be32(FW_EQ_ETH_CMD_FREE_F | 187662306a36Sopenharmony_ci FW_LEN16(cmd)); 187762306a36Sopenharmony_ci cmd.eqid_pkd = cpu_to_be32(FW_EQ_ETH_CMD_EQID_V(eqid)); 187862306a36Sopenharmony_ci return t4vf_wr_mbox(adapter, &cmd, sizeof(cmd), NULL); 187962306a36Sopenharmony_ci} 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci/** 188262306a36Sopenharmony_ci * t4vf_link_down_rc_str - return a string for a Link Down Reason Code 188362306a36Sopenharmony_ci * @link_down_rc: Link Down Reason Code 188462306a36Sopenharmony_ci * 188562306a36Sopenharmony_ci * Returns a string representation of the Link Down Reason Code. 188662306a36Sopenharmony_ci */ 188762306a36Sopenharmony_cistatic const char *t4vf_link_down_rc_str(unsigned char link_down_rc) 188862306a36Sopenharmony_ci{ 188962306a36Sopenharmony_ci static const char * const reason[] = { 189062306a36Sopenharmony_ci "Link Down", 189162306a36Sopenharmony_ci "Remote Fault", 189262306a36Sopenharmony_ci "Auto-negotiation Failure", 189362306a36Sopenharmony_ci "Reserved", 189462306a36Sopenharmony_ci "Insufficient Airflow", 189562306a36Sopenharmony_ci "Unable To Determine Reason", 189662306a36Sopenharmony_ci "No RX Signal Detected", 189762306a36Sopenharmony_ci "Reserved", 189862306a36Sopenharmony_ci }; 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci if (link_down_rc >= ARRAY_SIZE(reason)) 190162306a36Sopenharmony_ci return "Bad Reason Code"; 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci return reason[link_down_rc]; 190462306a36Sopenharmony_ci} 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci/** 190762306a36Sopenharmony_ci * t4vf_handle_get_port_info - process a FW reply message 190862306a36Sopenharmony_ci * @pi: the port info 190962306a36Sopenharmony_ci * @cmd: start of the FW message 191062306a36Sopenharmony_ci * 191162306a36Sopenharmony_ci * Processes a GET_PORT_INFO FW reply message. 191262306a36Sopenharmony_ci */ 191362306a36Sopenharmony_cistatic void t4vf_handle_get_port_info(struct port_info *pi, 191462306a36Sopenharmony_ci const struct fw_port_cmd *cmd) 191562306a36Sopenharmony_ci{ 191662306a36Sopenharmony_ci fw_port_cap32_t pcaps, acaps, lpacaps, linkattr; 191762306a36Sopenharmony_ci struct link_config *lc = &pi->link_cfg; 191862306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 191962306a36Sopenharmony_ci unsigned int speed, fc, fec, adv_fc; 192062306a36Sopenharmony_ci enum fw_port_module_type mod_type; 192162306a36Sopenharmony_ci int action, link_ok, linkdnrc; 192262306a36Sopenharmony_ci enum fw_port_type port_type; 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci /* Extract the various fields from the Port Information message. */ 192562306a36Sopenharmony_ci action = FW_PORT_CMD_ACTION_G(be32_to_cpu(cmd->action_to_len16)); 192662306a36Sopenharmony_ci switch (action) { 192762306a36Sopenharmony_ci case FW_PORT_ACTION_GET_PORT_INFO: { 192862306a36Sopenharmony_ci u32 lstatus = be32_to_cpu(cmd->u.info.lstatus_to_modtype); 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci link_ok = (lstatus & FW_PORT_CMD_LSTATUS_F) != 0; 193162306a36Sopenharmony_ci linkdnrc = FW_PORT_CMD_LINKDNRC_G(lstatus); 193262306a36Sopenharmony_ci port_type = FW_PORT_CMD_PTYPE_G(lstatus); 193362306a36Sopenharmony_ci mod_type = FW_PORT_CMD_MODTYPE_G(lstatus); 193462306a36Sopenharmony_ci pcaps = fwcaps16_to_caps32(be16_to_cpu(cmd->u.info.pcap)); 193562306a36Sopenharmony_ci acaps = fwcaps16_to_caps32(be16_to_cpu(cmd->u.info.acap)); 193662306a36Sopenharmony_ci lpacaps = fwcaps16_to_caps32(be16_to_cpu(cmd->u.info.lpacap)); 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci /* Unfortunately the format of the Link Status in the old 193962306a36Sopenharmony_ci * 16-bit Port Information message isn't the same as the 194062306a36Sopenharmony_ci * 16-bit Port Capabilities bitfield used everywhere else ... 194162306a36Sopenharmony_ci */ 194262306a36Sopenharmony_ci linkattr = 0; 194362306a36Sopenharmony_ci if (lstatus & FW_PORT_CMD_RXPAUSE_F) 194462306a36Sopenharmony_ci linkattr |= FW_PORT_CAP32_FC_RX; 194562306a36Sopenharmony_ci if (lstatus & FW_PORT_CMD_TXPAUSE_F) 194662306a36Sopenharmony_ci linkattr |= FW_PORT_CAP32_FC_TX; 194762306a36Sopenharmony_ci if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100M)) 194862306a36Sopenharmony_ci linkattr |= FW_PORT_CAP32_SPEED_100M; 194962306a36Sopenharmony_ci if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_1G)) 195062306a36Sopenharmony_ci linkattr |= FW_PORT_CAP32_SPEED_1G; 195162306a36Sopenharmony_ci if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_10G)) 195262306a36Sopenharmony_ci linkattr |= FW_PORT_CAP32_SPEED_10G; 195362306a36Sopenharmony_ci if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_25G)) 195462306a36Sopenharmony_ci linkattr |= FW_PORT_CAP32_SPEED_25G; 195562306a36Sopenharmony_ci if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_40G)) 195662306a36Sopenharmony_ci linkattr |= FW_PORT_CAP32_SPEED_40G; 195762306a36Sopenharmony_ci if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100G)) 195862306a36Sopenharmony_ci linkattr |= FW_PORT_CAP32_SPEED_100G; 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci break; 196162306a36Sopenharmony_ci } 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci case FW_PORT_ACTION_GET_PORT_INFO32: { 196462306a36Sopenharmony_ci u32 lstatus32; 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci lstatus32 = be32_to_cpu(cmd->u.info32.lstatus32_to_cbllen32); 196762306a36Sopenharmony_ci link_ok = (lstatus32 & FW_PORT_CMD_LSTATUS32_F) != 0; 196862306a36Sopenharmony_ci linkdnrc = FW_PORT_CMD_LINKDNRC32_G(lstatus32); 196962306a36Sopenharmony_ci port_type = FW_PORT_CMD_PORTTYPE32_G(lstatus32); 197062306a36Sopenharmony_ci mod_type = FW_PORT_CMD_MODTYPE32_G(lstatus32); 197162306a36Sopenharmony_ci pcaps = be32_to_cpu(cmd->u.info32.pcaps32); 197262306a36Sopenharmony_ci acaps = be32_to_cpu(cmd->u.info32.acaps32); 197362306a36Sopenharmony_ci lpacaps = be32_to_cpu(cmd->u.info32.lpacaps32); 197462306a36Sopenharmony_ci linkattr = be32_to_cpu(cmd->u.info32.linkattr32); 197562306a36Sopenharmony_ci break; 197662306a36Sopenharmony_ci } 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci default: 197962306a36Sopenharmony_ci dev_err(adapter->pdev_dev, "Handle Port Information: Bad Command/Action %#x\n", 198062306a36Sopenharmony_ci be32_to_cpu(cmd->action_to_len16)); 198162306a36Sopenharmony_ci return; 198262306a36Sopenharmony_ci } 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci fec = fwcap_to_cc_fec(acaps); 198562306a36Sopenharmony_ci adv_fc = fwcap_to_cc_pause(acaps); 198662306a36Sopenharmony_ci fc = fwcap_to_cc_pause(linkattr); 198762306a36Sopenharmony_ci speed = fwcap_to_speed(linkattr); 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci if (mod_type != pi->mod_type) { 199062306a36Sopenharmony_ci /* When a new Transceiver Module is inserted, the Firmware 199162306a36Sopenharmony_ci * will examine any Forward Error Correction parameters 199262306a36Sopenharmony_ci * present in the Transceiver Module i2c EPROM and determine 199362306a36Sopenharmony_ci * the supported and recommended FEC settings from those 199462306a36Sopenharmony_ci * based on IEEE 802.3 standards. We always record the 199562306a36Sopenharmony_ci * IEEE 802.3 recommended "automatic" settings. 199662306a36Sopenharmony_ci */ 199762306a36Sopenharmony_ci lc->auto_fec = fec; 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci /* Some versions of the early T6 Firmware "cheated" when 200062306a36Sopenharmony_ci * handling different Transceiver Modules by changing the 200162306a36Sopenharmony_ci * underlaying Port Type reported to the Host Drivers. As 200262306a36Sopenharmony_ci * such we need to capture whatever Port Type the Firmware 200362306a36Sopenharmony_ci * sends us and record it in case it's different from what we 200462306a36Sopenharmony_ci * were told earlier. Unfortunately, since Firmware is 200562306a36Sopenharmony_ci * forever, we'll need to keep this code here forever, but in 200662306a36Sopenharmony_ci * later T6 Firmware it should just be an assignment of the 200762306a36Sopenharmony_ci * same value already recorded. 200862306a36Sopenharmony_ci */ 200962306a36Sopenharmony_ci pi->port_type = port_type; 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_ci pi->mod_type = mod_type; 201262306a36Sopenharmony_ci t4vf_os_portmod_changed(adapter, pi->pidx); 201362306a36Sopenharmony_ci } 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci if (link_ok != lc->link_ok || speed != lc->speed || 201662306a36Sopenharmony_ci fc != lc->fc || adv_fc != lc->advertised_fc || 201762306a36Sopenharmony_ci fec != lc->fec) { 201862306a36Sopenharmony_ci /* something changed */ 201962306a36Sopenharmony_ci if (!link_ok && lc->link_ok) { 202062306a36Sopenharmony_ci lc->link_down_rc = linkdnrc; 202162306a36Sopenharmony_ci dev_warn_ratelimited(adapter->pdev_dev, 202262306a36Sopenharmony_ci "Port %d link down, reason: %s\n", 202362306a36Sopenharmony_ci pi->port_id, 202462306a36Sopenharmony_ci t4vf_link_down_rc_str(linkdnrc)); 202562306a36Sopenharmony_ci } 202662306a36Sopenharmony_ci lc->link_ok = link_ok; 202762306a36Sopenharmony_ci lc->speed = speed; 202862306a36Sopenharmony_ci lc->advertised_fc = adv_fc; 202962306a36Sopenharmony_ci lc->fc = fc; 203062306a36Sopenharmony_ci lc->fec = fec; 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci lc->pcaps = pcaps; 203362306a36Sopenharmony_ci lc->lpacaps = lpacaps; 203462306a36Sopenharmony_ci lc->acaps = acaps & ADVERT_MASK; 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci /* If we're not physically capable of Auto-Negotiation, note 203762306a36Sopenharmony_ci * this as Auto-Negotiation disabled. Otherwise, we track 203862306a36Sopenharmony_ci * what Auto-Negotiation settings we have. Note parallel 203962306a36Sopenharmony_ci * structure in init_link_config(). 204062306a36Sopenharmony_ci */ 204162306a36Sopenharmony_ci if (!(lc->pcaps & FW_PORT_CAP32_ANEG)) { 204262306a36Sopenharmony_ci lc->autoneg = AUTONEG_DISABLE; 204362306a36Sopenharmony_ci } else if (lc->acaps & FW_PORT_CAP32_ANEG) { 204462306a36Sopenharmony_ci lc->autoneg = AUTONEG_ENABLE; 204562306a36Sopenharmony_ci } else { 204662306a36Sopenharmony_ci /* When Autoneg is disabled, user needs to set 204762306a36Sopenharmony_ci * single speed. 204862306a36Sopenharmony_ci * Similar to cxgb4_ethtool.c: set_link_ksettings 204962306a36Sopenharmony_ci */ 205062306a36Sopenharmony_ci lc->acaps = 0; 205162306a36Sopenharmony_ci lc->speed_caps = fwcap_to_speed(acaps); 205262306a36Sopenharmony_ci lc->autoneg = AUTONEG_DISABLE; 205362306a36Sopenharmony_ci } 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci t4vf_os_link_changed(adapter, pi->pidx, link_ok); 205662306a36Sopenharmony_ci } 205762306a36Sopenharmony_ci} 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci/** 206062306a36Sopenharmony_ci * t4vf_update_port_info - retrieve and update port information if changed 206162306a36Sopenharmony_ci * @pi: the port_info 206262306a36Sopenharmony_ci * 206362306a36Sopenharmony_ci * We issue a Get Port Information Command to the Firmware and, if 206462306a36Sopenharmony_ci * successful, we check to see if anything is different from what we 206562306a36Sopenharmony_ci * last recorded and update things accordingly. 206662306a36Sopenharmony_ci */ 206762306a36Sopenharmony_ciint t4vf_update_port_info(struct port_info *pi) 206862306a36Sopenharmony_ci{ 206962306a36Sopenharmony_ci unsigned int fw_caps = pi->adapter->params.fw_caps_support; 207062306a36Sopenharmony_ci struct fw_port_cmd port_cmd; 207162306a36Sopenharmony_ci int ret; 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci memset(&port_cmd, 0, sizeof(port_cmd)); 207462306a36Sopenharmony_ci port_cmd.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | 207562306a36Sopenharmony_ci FW_CMD_REQUEST_F | FW_CMD_READ_F | 207662306a36Sopenharmony_ci FW_PORT_CMD_PORTID_V(pi->port_id)); 207762306a36Sopenharmony_ci port_cmd.action_to_len16 = cpu_to_be32( 207862306a36Sopenharmony_ci FW_PORT_CMD_ACTION_V(fw_caps == FW_CAPS16 207962306a36Sopenharmony_ci ? FW_PORT_ACTION_GET_PORT_INFO 208062306a36Sopenharmony_ci : FW_PORT_ACTION_GET_PORT_INFO32) | 208162306a36Sopenharmony_ci FW_LEN16(port_cmd)); 208262306a36Sopenharmony_ci ret = t4vf_wr_mbox(pi->adapter, &port_cmd, sizeof(port_cmd), 208362306a36Sopenharmony_ci &port_cmd); 208462306a36Sopenharmony_ci if (ret) 208562306a36Sopenharmony_ci return ret; 208662306a36Sopenharmony_ci t4vf_handle_get_port_info(pi, &port_cmd); 208762306a36Sopenharmony_ci return 0; 208862306a36Sopenharmony_ci} 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci/** 209162306a36Sopenharmony_ci * t4vf_handle_fw_rpl - process a firmware reply message 209262306a36Sopenharmony_ci * @adapter: the adapter 209362306a36Sopenharmony_ci * @rpl: start of the firmware message 209462306a36Sopenharmony_ci * 209562306a36Sopenharmony_ci * Processes a firmware message, such as link state change messages. 209662306a36Sopenharmony_ci */ 209762306a36Sopenharmony_ciint t4vf_handle_fw_rpl(struct adapter *adapter, const __be64 *rpl) 209862306a36Sopenharmony_ci{ 209962306a36Sopenharmony_ci const struct fw_cmd_hdr *cmd_hdr = (const struct fw_cmd_hdr *)rpl; 210062306a36Sopenharmony_ci u8 opcode = FW_CMD_OP_G(be32_to_cpu(cmd_hdr->hi)); 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci switch (opcode) { 210362306a36Sopenharmony_ci case FW_PORT_CMD: { 210462306a36Sopenharmony_ci /* 210562306a36Sopenharmony_ci * Link/module state change message. 210662306a36Sopenharmony_ci */ 210762306a36Sopenharmony_ci const struct fw_port_cmd *port_cmd = 210862306a36Sopenharmony_ci (const struct fw_port_cmd *)rpl; 210962306a36Sopenharmony_ci int action = FW_PORT_CMD_ACTION_G( 211062306a36Sopenharmony_ci be32_to_cpu(port_cmd->action_to_len16)); 211162306a36Sopenharmony_ci int port_id, pidx; 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_ci if (action != FW_PORT_ACTION_GET_PORT_INFO && 211462306a36Sopenharmony_ci action != FW_PORT_ACTION_GET_PORT_INFO32) { 211562306a36Sopenharmony_ci dev_err(adapter->pdev_dev, 211662306a36Sopenharmony_ci "Unknown firmware PORT reply action %x\n", 211762306a36Sopenharmony_ci action); 211862306a36Sopenharmony_ci break; 211962306a36Sopenharmony_ci } 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci port_id = FW_PORT_CMD_PORTID_G( 212262306a36Sopenharmony_ci be32_to_cpu(port_cmd->op_to_portid)); 212362306a36Sopenharmony_ci for_each_port(adapter, pidx) { 212462306a36Sopenharmony_ci struct port_info *pi = adap2pinfo(adapter, pidx); 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci if (pi->port_id != port_id) 212762306a36Sopenharmony_ci continue; 212862306a36Sopenharmony_ci t4vf_handle_get_port_info(pi, port_cmd); 212962306a36Sopenharmony_ci } 213062306a36Sopenharmony_ci break; 213162306a36Sopenharmony_ci } 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci default: 213462306a36Sopenharmony_ci dev_err(adapter->pdev_dev, "Unknown firmware reply %X\n", 213562306a36Sopenharmony_ci opcode); 213662306a36Sopenharmony_ci } 213762306a36Sopenharmony_ci return 0; 213862306a36Sopenharmony_ci} 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ciint t4vf_prep_adapter(struct adapter *adapter) 214162306a36Sopenharmony_ci{ 214262306a36Sopenharmony_ci int err; 214362306a36Sopenharmony_ci unsigned int chipid; 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci /* Wait for the device to become ready before proceeding ... 214662306a36Sopenharmony_ci */ 214762306a36Sopenharmony_ci err = t4vf_wait_dev_ready(adapter); 214862306a36Sopenharmony_ci if (err) 214962306a36Sopenharmony_ci return err; 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ci /* Default port and clock for debugging in case we can't reach 215262306a36Sopenharmony_ci * firmware. 215362306a36Sopenharmony_ci */ 215462306a36Sopenharmony_ci adapter->params.nports = 1; 215562306a36Sopenharmony_ci adapter->params.vfres.pmask = 1; 215662306a36Sopenharmony_ci adapter->params.vpd.cclk = 50000; 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci adapter->params.chip = 0; 215962306a36Sopenharmony_ci switch (CHELSIO_PCI_ID_VER(adapter->pdev->device)) { 216062306a36Sopenharmony_ci case CHELSIO_T4: 216162306a36Sopenharmony_ci adapter->params.chip |= CHELSIO_CHIP_CODE(CHELSIO_T4, 0); 216262306a36Sopenharmony_ci adapter->params.arch.sge_fl_db = DBPRIO_F; 216362306a36Sopenharmony_ci adapter->params.arch.mps_tcam_size = 216462306a36Sopenharmony_ci NUM_MPS_CLS_SRAM_L_INSTANCES; 216562306a36Sopenharmony_ci break; 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci case CHELSIO_T5: 216862306a36Sopenharmony_ci chipid = REV_G(t4_read_reg(adapter, PL_VF_REV_A)); 216962306a36Sopenharmony_ci adapter->params.chip |= CHELSIO_CHIP_CODE(CHELSIO_T5, chipid); 217062306a36Sopenharmony_ci adapter->params.arch.sge_fl_db = DBPRIO_F | DBTYPE_F; 217162306a36Sopenharmony_ci adapter->params.arch.mps_tcam_size = 217262306a36Sopenharmony_ci NUM_MPS_T5_CLS_SRAM_L_INSTANCES; 217362306a36Sopenharmony_ci break; 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_ci case CHELSIO_T6: 217662306a36Sopenharmony_ci chipid = REV_G(t4_read_reg(adapter, PL_VF_REV_A)); 217762306a36Sopenharmony_ci adapter->params.chip |= CHELSIO_CHIP_CODE(CHELSIO_T6, chipid); 217862306a36Sopenharmony_ci adapter->params.arch.sge_fl_db = 0; 217962306a36Sopenharmony_ci adapter->params.arch.mps_tcam_size = 218062306a36Sopenharmony_ci NUM_MPS_T5_CLS_SRAM_L_INSTANCES; 218162306a36Sopenharmony_ci break; 218262306a36Sopenharmony_ci } 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci return 0; 218562306a36Sopenharmony_ci} 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci/** 218862306a36Sopenharmony_ci * t4vf_get_vf_mac_acl - Get the MAC address to be set to 218962306a36Sopenharmony_ci * the VI of this VF. 219062306a36Sopenharmony_ci * @adapter: The adapter 219162306a36Sopenharmony_ci * @port: The port associated with vf 219262306a36Sopenharmony_ci * @naddr: the number of ACL MAC addresses returned in addr 219362306a36Sopenharmony_ci * @addr: Placeholder for MAC addresses 219462306a36Sopenharmony_ci * 219562306a36Sopenharmony_ci * Find the MAC address to be set to the VF's VI. The requested MAC address 219662306a36Sopenharmony_ci * is from the host OS via callback in the PF driver. 219762306a36Sopenharmony_ci */ 219862306a36Sopenharmony_ciint t4vf_get_vf_mac_acl(struct adapter *adapter, unsigned int port, 219962306a36Sopenharmony_ci unsigned int *naddr, u8 *addr) 220062306a36Sopenharmony_ci{ 220162306a36Sopenharmony_ci struct fw_acl_mac_cmd cmd; 220262306a36Sopenharmony_ci int ret; 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 220562306a36Sopenharmony_ci cmd.op_to_vfn = cpu_to_be32(FW_CMD_OP_V(FW_ACL_MAC_CMD) | 220662306a36Sopenharmony_ci FW_CMD_REQUEST_F | 220762306a36Sopenharmony_ci FW_CMD_READ_F); 220862306a36Sopenharmony_ci cmd.en_to_len16 = cpu_to_be32((unsigned int)FW_LEN16(cmd)); 220962306a36Sopenharmony_ci ret = t4vf_wr_mbox(adapter, &cmd, sizeof(cmd), &cmd); 221062306a36Sopenharmony_ci if (ret) 221162306a36Sopenharmony_ci return ret; 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_ci if (cmd.nmac < *naddr) 221462306a36Sopenharmony_ci *naddr = cmd.nmac; 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci switch (port) { 221762306a36Sopenharmony_ci case 3: 221862306a36Sopenharmony_ci memcpy(addr, cmd.macaddr3, sizeof(cmd.macaddr3)); 221962306a36Sopenharmony_ci break; 222062306a36Sopenharmony_ci case 2: 222162306a36Sopenharmony_ci memcpy(addr, cmd.macaddr2, sizeof(cmd.macaddr2)); 222262306a36Sopenharmony_ci break; 222362306a36Sopenharmony_ci case 1: 222462306a36Sopenharmony_ci memcpy(addr, cmd.macaddr1, sizeof(cmd.macaddr1)); 222562306a36Sopenharmony_ci break; 222662306a36Sopenharmony_ci case 0: 222762306a36Sopenharmony_ci memcpy(addr, cmd.macaddr0, sizeof(cmd.macaddr0)); 222862306a36Sopenharmony_ci break; 222962306a36Sopenharmony_ci } 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci return ret; 223262306a36Sopenharmony_ci} 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci/** 223562306a36Sopenharmony_ci * t4vf_get_vf_vlan_acl - Get the VLAN ID to be set to 223662306a36Sopenharmony_ci * the VI of this VF. 223762306a36Sopenharmony_ci * @adapter: The adapter 223862306a36Sopenharmony_ci * 223962306a36Sopenharmony_ci * Find the VLAN ID to be set to the VF's VI. The requested VLAN ID 224062306a36Sopenharmony_ci * is from the host OS via callback in the PF driver. 224162306a36Sopenharmony_ci */ 224262306a36Sopenharmony_ciint t4vf_get_vf_vlan_acl(struct adapter *adapter) 224362306a36Sopenharmony_ci{ 224462306a36Sopenharmony_ci struct fw_acl_vlan_cmd cmd; 224562306a36Sopenharmony_ci int vlan = 0; 224662306a36Sopenharmony_ci int ret = 0; 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci cmd.op_to_vfn = htonl(FW_CMD_OP_V(FW_ACL_VLAN_CMD) | 224962306a36Sopenharmony_ci FW_CMD_REQUEST_F | FW_CMD_READ_F); 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci /* Note: Do not enable the ACL */ 225262306a36Sopenharmony_ci cmd.en_to_len16 = cpu_to_be32((unsigned int)FW_LEN16(cmd)); 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci ret = t4vf_wr_mbox(adapter, &cmd, sizeof(cmd), &cmd); 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ci if (!ret) 225762306a36Sopenharmony_ci vlan = be16_to_cpu(cmd.vlanid[0]); 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci return vlan; 226062306a36Sopenharmony_ci} 2261