mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
524 lines
14 KiB
524 lines
14 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* Copyright(c) 2013 - 2018 Intel Corporation. */ |
|
|
|
#include "fm10k_common.h" |
|
|
|
/** |
|
* fm10k_get_bus_info_generic - Generic set PCI bus info |
|
* @hw: pointer to hardware structure |
|
* |
|
* Gets the PCI bus info (speed, width, type) then calls helper function to |
|
* store this data within the fm10k_hw structure. |
|
**/ |
|
s32 fm10k_get_bus_info_generic(struct fm10k_hw *hw) |
|
{ |
|
u16 link_cap, link_status, device_cap, device_control; |
|
|
|
/* Get the maximum link width and speed from PCIe config space */ |
|
link_cap = fm10k_read_pci_cfg_word(hw, FM10K_PCIE_LINK_CAP); |
|
|
|
switch (link_cap & FM10K_PCIE_LINK_WIDTH) { |
|
case FM10K_PCIE_LINK_WIDTH_1: |
|
hw->bus_caps.width = fm10k_bus_width_pcie_x1; |
|
break; |
|
case FM10K_PCIE_LINK_WIDTH_2: |
|
hw->bus_caps.width = fm10k_bus_width_pcie_x2; |
|
break; |
|
case FM10K_PCIE_LINK_WIDTH_4: |
|
hw->bus_caps.width = fm10k_bus_width_pcie_x4; |
|
break; |
|
case FM10K_PCIE_LINK_WIDTH_8: |
|
hw->bus_caps.width = fm10k_bus_width_pcie_x8; |
|
break; |
|
default: |
|
hw->bus_caps.width = fm10k_bus_width_unknown; |
|
break; |
|
} |
|
|
|
switch (link_cap & FM10K_PCIE_LINK_SPEED) { |
|
case FM10K_PCIE_LINK_SPEED_2500: |
|
hw->bus_caps.speed = fm10k_bus_speed_2500; |
|
break; |
|
case FM10K_PCIE_LINK_SPEED_5000: |
|
hw->bus_caps.speed = fm10k_bus_speed_5000; |
|
break; |
|
case FM10K_PCIE_LINK_SPEED_8000: |
|
hw->bus_caps.speed = fm10k_bus_speed_8000; |
|
break; |
|
default: |
|
hw->bus_caps.speed = fm10k_bus_speed_unknown; |
|
break; |
|
} |
|
|
|
/* Get the PCIe maximum payload size for the PCIe function */ |
|
device_cap = fm10k_read_pci_cfg_word(hw, FM10K_PCIE_DEV_CAP); |
|
|
|
switch (device_cap & FM10K_PCIE_DEV_CAP_PAYLOAD) { |
|
case FM10K_PCIE_DEV_CAP_PAYLOAD_128: |
|
hw->bus_caps.payload = fm10k_bus_payload_128; |
|
break; |
|
case FM10K_PCIE_DEV_CAP_PAYLOAD_256: |
|
hw->bus_caps.payload = fm10k_bus_payload_256; |
|
break; |
|
case FM10K_PCIE_DEV_CAP_PAYLOAD_512: |
|
hw->bus_caps.payload = fm10k_bus_payload_512; |
|
break; |
|
default: |
|
hw->bus_caps.payload = fm10k_bus_payload_unknown; |
|
break; |
|
} |
|
|
|
/* Get the negotiated link width and speed from PCIe config space */ |
|
link_status = fm10k_read_pci_cfg_word(hw, FM10K_PCIE_LINK_STATUS); |
|
|
|
switch (link_status & FM10K_PCIE_LINK_WIDTH) { |
|
case FM10K_PCIE_LINK_WIDTH_1: |
|
hw->bus.width = fm10k_bus_width_pcie_x1; |
|
break; |
|
case FM10K_PCIE_LINK_WIDTH_2: |
|
hw->bus.width = fm10k_bus_width_pcie_x2; |
|
break; |
|
case FM10K_PCIE_LINK_WIDTH_4: |
|
hw->bus.width = fm10k_bus_width_pcie_x4; |
|
break; |
|
case FM10K_PCIE_LINK_WIDTH_8: |
|
hw->bus.width = fm10k_bus_width_pcie_x8; |
|
break; |
|
default: |
|
hw->bus.width = fm10k_bus_width_unknown; |
|
break; |
|
} |
|
|
|
switch (link_status & FM10K_PCIE_LINK_SPEED) { |
|
case FM10K_PCIE_LINK_SPEED_2500: |
|
hw->bus.speed = fm10k_bus_speed_2500; |
|
break; |
|
case FM10K_PCIE_LINK_SPEED_5000: |
|
hw->bus.speed = fm10k_bus_speed_5000; |
|
break; |
|
case FM10K_PCIE_LINK_SPEED_8000: |
|
hw->bus.speed = fm10k_bus_speed_8000; |
|
break; |
|
default: |
|
hw->bus.speed = fm10k_bus_speed_unknown; |
|
break; |
|
} |
|
|
|
/* Get the negotiated PCIe maximum payload size for the PCIe function */ |
|
device_control = fm10k_read_pci_cfg_word(hw, FM10K_PCIE_DEV_CTRL); |
|
|
|
switch (device_control & FM10K_PCIE_DEV_CTRL_PAYLOAD) { |
|
case FM10K_PCIE_DEV_CTRL_PAYLOAD_128: |
|
hw->bus.payload = fm10k_bus_payload_128; |
|
break; |
|
case FM10K_PCIE_DEV_CTRL_PAYLOAD_256: |
|
hw->bus.payload = fm10k_bus_payload_256; |
|
break; |
|
case FM10K_PCIE_DEV_CTRL_PAYLOAD_512: |
|
hw->bus.payload = fm10k_bus_payload_512; |
|
break; |
|
default: |
|
hw->bus.payload = fm10k_bus_payload_unknown; |
|
break; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static u16 fm10k_get_pcie_msix_count_generic(struct fm10k_hw *hw) |
|
{ |
|
u16 msix_count; |
|
|
|
/* read in value from MSI-X capability register */ |
|
msix_count = fm10k_read_pci_cfg_word(hw, FM10K_PCI_MSIX_MSG_CTRL); |
|
msix_count &= FM10K_PCI_MSIX_MSG_CTRL_TBL_SZ_MASK; |
|
|
|
/* MSI-X count is zero-based in HW */ |
|
msix_count++; |
|
|
|
if (msix_count > FM10K_MAX_MSIX_VECTORS) |
|
msix_count = FM10K_MAX_MSIX_VECTORS; |
|
|
|
return msix_count; |
|
} |
|
|
|
/** |
|
* fm10k_get_invariants_generic - Inits constant values |
|
* @hw: pointer to the hardware structure |
|
* |
|
* Initialize the common invariants for the device. |
|
**/ |
|
s32 fm10k_get_invariants_generic(struct fm10k_hw *hw) |
|
{ |
|
struct fm10k_mac_info *mac = &hw->mac; |
|
|
|
/* initialize GLORT state to avoid any false hits */ |
|
mac->dglort_map = FM10K_DGLORTMAP_NONE; |
|
|
|
/* record maximum number of MSI-X vectors */ |
|
mac->max_msix_vectors = fm10k_get_pcie_msix_count_generic(hw); |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* fm10k_start_hw_generic - Prepare hardware for Tx/Rx |
|
* @hw: pointer to hardware structure |
|
* |
|
* This function sets the Tx ready flag to indicate that the Tx path has |
|
* been initialized. |
|
**/ |
|
s32 fm10k_start_hw_generic(struct fm10k_hw *hw) |
|
{ |
|
/* set flag indicating we are beginning Tx */ |
|
hw->mac.tx_ready = true; |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* fm10k_disable_queues_generic - Stop Tx/Rx queues |
|
* @hw: pointer to hardware structure |
|
* @q_cnt: number of queues to be disabled |
|
* |
|
**/ |
|
s32 fm10k_disable_queues_generic(struct fm10k_hw *hw, u16 q_cnt) |
|
{ |
|
u32 reg; |
|
u16 i, time; |
|
|
|
/* clear tx_ready to prevent any false hits for reset */ |
|
hw->mac.tx_ready = false; |
|
|
|
if (FM10K_REMOVED(hw->hw_addr)) |
|
return 0; |
|
|
|
/* clear the enable bit for all rings */ |
|
for (i = 0; i < q_cnt; i++) { |
|
reg = fm10k_read_reg(hw, FM10K_TXDCTL(i)); |
|
fm10k_write_reg(hw, FM10K_TXDCTL(i), |
|
reg & ~FM10K_TXDCTL_ENABLE); |
|
reg = fm10k_read_reg(hw, FM10K_RXQCTL(i)); |
|
fm10k_write_reg(hw, FM10K_RXQCTL(i), |
|
reg & ~FM10K_RXQCTL_ENABLE); |
|
} |
|
|
|
fm10k_write_flush(hw); |
|
udelay(1); |
|
|
|
/* loop through all queues to verify that they are all disabled */ |
|
for (i = 0, time = FM10K_QUEUE_DISABLE_TIMEOUT; time;) { |
|
/* if we are at end of rings all rings are disabled */ |
|
if (i == q_cnt) |
|
return 0; |
|
|
|
/* if queue enables cleared, then move to next ring pair */ |
|
reg = fm10k_read_reg(hw, FM10K_TXDCTL(i)); |
|
if (!~reg || !(reg & FM10K_TXDCTL_ENABLE)) { |
|
reg = fm10k_read_reg(hw, FM10K_RXQCTL(i)); |
|
if (!~reg || !(reg & FM10K_RXQCTL_ENABLE)) { |
|
i++; |
|
continue; |
|
} |
|
} |
|
|
|
/* decrement time and wait 1 usec */ |
|
time--; |
|
if (time) |
|
udelay(1); |
|
} |
|
|
|
return FM10K_ERR_REQUESTS_PENDING; |
|
} |
|
|
|
/** |
|
* fm10k_stop_hw_generic - Stop Tx/Rx units |
|
* @hw: pointer to hardware structure |
|
* |
|
**/ |
|
s32 fm10k_stop_hw_generic(struct fm10k_hw *hw) |
|
{ |
|
return fm10k_disable_queues_generic(hw, hw->mac.max_queues); |
|
} |
|
|
|
/** |
|
* fm10k_read_hw_stats_32b - Reads value of 32-bit registers |
|
* @hw: pointer to the hardware structure |
|
* @addr: address of register containing a 32-bit value |
|
* @stat: pointer to structure holding hw stat information |
|
* |
|
* Function reads the content of the register and returns the delta |
|
* between the base and the current value. |
|
* **/ |
|
u32 fm10k_read_hw_stats_32b(struct fm10k_hw *hw, u32 addr, |
|
struct fm10k_hw_stat *stat) |
|
{ |
|
u32 delta = fm10k_read_reg(hw, addr) - stat->base_l; |
|
|
|
if (FM10K_REMOVED(hw->hw_addr)) |
|
stat->base_h = 0; |
|
|
|
return delta; |
|
} |
|
|
|
/** |
|
* fm10k_read_hw_stats_48b - Reads value of 48-bit registers |
|
* @hw: pointer to the hardware structure |
|
* @addr: address of register containing the lower 32-bit value |
|
* @stat: pointer to structure holding hw stat information |
|
* |
|
* Function reads the content of 2 registers, combined to represent a 48-bit |
|
* statistical value. Extra processing is required to handle overflowing. |
|
* Finally, a delta value is returned representing the difference between the |
|
* values stored in registers and values stored in the statistic counters. |
|
* **/ |
|
static u64 fm10k_read_hw_stats_48b(struct fm10k_hw *hw, u32 addr, |
|
struct fm10k_hw_stat *stat) |
|
{ |
|
u32 count_l; |
|
u32 count_h; |
|
u32 count_tmp; |
|
u64 delta; |
|
|
|
count_h = fm10k_read_reg(hw, addr + 1); |
|
|
|
/* Check for overflow */ |
|
do { |
|
count_tmp = count_h; |
|
count_l = fm10k_read_reg(hw, addr); |
|
count_h = fm10k_read_reg(hw, addr + 1); |
|
} while (count_h != count_tmp); |
|
|
|
delta = ((u64)(count_h - stat->base_h) << 32) + count_l; |
|
delta -= stat->base_l; |
|
|
|
return delta & FM10K_48_BIT_MASK; |
|
} |
|
|
|
/** |
|
* fm10k_update_hw_base_48b - Updates 48-bit statistic base value |
|
* @stat: pointer to the hardware statistic structure |
|
* @delta: value to be updated into the hardware statistic structure |
|
* |
|
* Function receives a value and determines if an update is required based on |
|
* a delta calculation. Only the base value will be updated. |
|
**/ |
|
static void fm10k_update_hw_base_48b(struct fm10k_hw_stat *stat, u64 delta) |
|
{ |
|
if (!delta) |
|
return; |
|
|
|
/* update lower 32 bits */ |
|
delta += stat->base_l; |
|
stat->base_l = (u32)delta; |
|
|
|
/* update upper 32 bits */ |
|
stat->base_h += (u32)(delta >> 32); |
|
} |
|
|
|
/** |
|
* fm10k_update_hw_stats_tx_q - Updates TX queue statistics counters |
|
* @hw: pointer to the hardware structure |
|
* @q: pointer to the ring of hardware statistics queue |
|
* @idx: index pointing to the start of the ring iteration |
|
* |
|
* Function updates the TX queue statistics counters that are related to the |
|
* hardware. |
|
**/ |
|
static void fm10k_update_hw_stats_tx_q(struct fm10k_hw *hw, |
|
struct fm10k_hw_stats_q *q, |
|
u32 idx) |
|
{ |
|
u32 id_tx, id_tx_prev, tx_packets; |
|
u64 tx_bytes = 0; |
|
|
|
/* Retrieve TX Owner Data */ |
|
id_tx = fm10k_read_reg(hw, FM10K_TXQCTL(idx)); |
|
|
|
/* Process TX Ring */ |
|
do { |
|
tx_packets = fm10k_read_hw_stats_32b(hw, FM10K_QPTC(idx), |
|
&q->tx_packets); |
|
|
|
if (tx_packets) |
|
tx_bytes = fm10k_read_hw_stats_48b(hw, |
|
FM10K_QBTC_L(idx), |
|
&q->tx_bytes); |
|
|
|
/* Re-Check Owner Data */ |
|
id_tx_prev = id_tx; |
|
id_tx = fm10k_read_reg(hw, FM10K_TXQCTL(idx)); |
|
} while ((id_tx ^ id_tx_prev) & FM10K_TXQCTL_ID_MASK); |
|
|
|
/* drop non-ID bits and set VALID ID bit */ |
|
id_tx &= FM10K_TXQCTL_ID_MASK; |
|
id_tx |= FM10K_STAT_VALID; |
|
|
|
/* update packet counts */ |
|
if (q->tx_stats_idx == id_tx) { |
|
q->tx_packets.count += tx_packets; |
|
q->tx_bytes.count += tx_bytes; |
|
} |
|
|
|
/* update bases and record ID */ |
|
fm10k_update_hw_base_32b(&q->tx_packets, tx_packets); |
|
fm10k_update_hw_base_48b(&q->tx_bytes, tx_bytes); |
|
|
|
q->tx_stats_idx = id_tx; |
|
} |
|
|
|
/** |
|
* fm10k_update_hw_stats_rx_q - Updates RX queue statistics counters |
|
* @hw: pointer to the hardware structure |
|
* @q: pointer to the ring of hardware statistics queue |
|
* @idx: index pointing to the start of the ring iteration |
|
* |
|
* Function updates the RX queue statistics counters that are related to the |
|
* hardware. |
|
**/ |
|
static void fm10k_update_hw_stats_rx_q(struct fm10k_hw *hw, |
|
struct fm10k_hw_stats_q *q, |
|
u32 idx) |
|
{ |
|
u32 id_rx, id_rx_prev, rx_packets, rx_drops; |
|
u64 rx_bytes = 0; |
|
|
|
/* Retrieve RX Owner Data */ |
|
id_rx = fm10k_read_reg(hw, FM10K_RXQCTL(idx)); |
|
|
|
/* Process RX Ring */ |
|
do { |
|
rx_drops = fm10k_read_hw_stats_32b(hw, FM10K_QPRDC(idx), |
|
&q->rx_drops); |
|
|
|
rx_packets = fm10k_read_hw_stats_32b(hw, FM10K_QPRC(idx), |
|
&q->rx_packets); |
|
|
|
if (rx_packets) |
|
rx_bytes = fm10k_read_hw_stats_48b(hw, |
|
FM10K_QBRC_L(idx), |
|
&q->rx_bytes); |
|
|
|
/* Re-Check Owner Data */ |
|
id_rx_prev = id_rx; |
|
id_rx = fm10k_read_reg(hw, FM10K_RXQCTL(idx)); |
|
} while ((id_rx ^ id_rx_prev) & FM10K_RXQCTL_ID_MASK); |
|
|
|
/* drop non-ID bits and set VALID ID bit */ |
|
id_rx &= FM10K_RXQCTL_ID_MASK; |
|
id_rx |= FM10K_STAT_VALID; |
|
|
|
/* update packet counts */ |
|
if (q->rx_stats_idx == id_rx) { |
|
q->rx_drops.count += rx_drops; |
|
q->rx_packets.count += rx_packets; |
|
q->rx_bytes.count += rx_bytes; |
|
} |
|
|
|
/* update bases and record ID */ |
|
fm10k_update_hw_base_32b(&q->rx_drops, rx_drops); |
|
fm10k_update_hw_base_32b(&q->rx_packets, rx_packets); |
|
fm10k_update_hw_base_48b(&q->rx_bytes, rx_bytes); |
|
|
|
q->rx_stats_idx = id_rx; |
|
} |
|
|
|
/** |
|
* fm10k_update_hw_stats_q - Updates queue statistics counters |
|
* @hw: pointer to the hardware structure |
|
* @q: pointer to the ring of hardware statistics queue |
|
* @idx: index pointing to the start of the ring iteration |
|
* @count: number of queues to iterate over |
|
* |
|
* Function updates the queue statistics counters that are related to the |
|
* hardware. |
|
**/ |
|
void fm10k_update_hw_stats_q(struct fm10k_hw *hw, struct fm10k_hw_stats_q *q, |
|
u32 idx, u32 count) |
|
{ |
|
u32 i; |
|
|
|
for (i = 0; i < count; i++, idx++, q++) { |
|
fm10k_update_hw_stats_tx_q(hw, q, idx); |
|
fm10k_update_hw_stats_rx_q(hw, q, idx); |
|
} |
|
} |
|
|
|
/** |
|
* fm10k_unbind_hw_stats_q - Unbind the queue counters from their queues |
|
* @q: pointer to the ring of hardware statistics queue |
|
* @idx: index pointing to the start of the ring iteration |
|
* @count: number of queues to iterate over |
|
* |
|
* Function invalidates the index values for the queues so any updates that |
|
* may have happened are ignored and the base for the queue stats is reset. |
|
**/ |
|
void fm10k_unbind_hw_stats_q(struct fm10k_hw_stats_q *q, u32 idx, u32 count) |
|
{ |
|
u32 i; |
|
|
|
for (i = 0; i < count; i++, idx++, q++) { |
|
q->rx_stats_idx = 0; |
|
q->tx_stats_idx = 0; |
|
} |
|
} |
|
|
|
/** |
|
* fm10k_get_host_state_generic - Returns the state of the host |
|
* @hw: pointer to hardware structure |
|
* @host_ready: pointer to boolean value that will record host state |
|
* |
|
* This function will check the health of the mailbox and Tx queue 0 |
|
* in order to determine if we should report that the link is up or not. |
|
**/ |
|
s32 fm10k_get_host_state_generic(struct fm10k_hw *hw, bool *host_ready) |
|
{ |
|
struct fm10k_mbx_info *mbx = &hw->mbx; |
|
struct fm10k_mac_info *mac = &hw->mac; |
|
s32 ret_val = 0; |
|
u32 txdctl = fm10k_read_reg(hw, FM10K_TXDCTL(0)); |
|
|
|
/* process upstream mailbox in case interrupts were disabled */ |
|
mbx->ops.process(hw, mbx); |
|
|
|
/* If Tx is no longer enabled link should come down */ |
|
if (!(~txdctl) || !(txdctl & FM10K_TXDCTL_ENABLE)) |
|
mac->get_host_state = true; |
|
|
|
/* exit if not checking for link, or link cannot be changed */ |
|
if (!mac->get_host_state || !(~txdctl)) |
|
goto out; |
|
|
|
/* if we somehow dropped the Tx enable we should reset */ |
|
if (mac->tx_ready && !(txdctl & FM10K_TXDCTL_ENABLE)) { |
|
ret_val = FM10K_ERR_RESET_REQUESTED; |
|
goto out; |
|
} |
|
|
|
/* if Mailbox timed out we should request reset */ |
|
if (!mbx->timeout) { |
|
ret_val = FM10K_ERR_RESET_REQUESTED; |
|
goto out; |
|
} |
|
|
|
/* verify Mailbox is still open */ |
|
if (mbx->state != FM10K_STATE_OPEN) |
|
goto out; |
|
|
|
/* interface cannot receive traffic without logical ports */ |
|
if (mac->dglort_map == FM10K_DGLORTMAP_NONE) { |
|
if (mac->ops.request_lport_map) |
|
ret_val = mac->ops.request_lport_map(hw); |
|
|
|
goto out; |
|
} |
|
|
|
/* if we passed all the tests above then the switch is ready and we no |
|
* longer need to check for link |
|
*/ |
|
mac->get_host_state = false; |
|
|
|
out: |
|
*host_ready = !mac->get_host_state; |
|
return ret_val; |
|
}
|
|
|