diff options
| author | Nick Price <nick@spun.io> | 2026-07-19 16:48:05 +0000 |
|---|---|---|
| committer | Adrian Chadd <adrian@FreeBSD.org> | 2026-07-19 16:48:05 +0000 |
| commit | dc5c0fc51cf47d6dfc737343e06805db294258e9 (patch) | |
| tree | cf4e41ca3153adf013c6d9963f88e623abbf1178 | |
| parent | f1796e8781ca8a632ffa0861b2e09944d225c479 (diff) | |
aq(4): add Atlantic 2 (AQC113) device support
Add support for the Marvell Atlantic 2 (AQC113/114/115/116) controllers,
a new chip generation that is not register-compatible with the Atlantic 1
parts aq(4) supports today. Adapted from the OpenBSD/NetBSD if_aq driver.
Register and device definitions (aq2_hw.h): the firmware handshake
(MIF_BOOT / MCP_HOST_REQ_INT / MIF_HOST_FINISHED), the 0x12000/0x13000
firmware interface windows, and the action-resolver table (ART) that
replaces Atlantic 1's discrete RX filters, plus the Atlantic 2 PCI device
ids and the aq_is_atlantic2() helper. Reserve a chip-feature bit
(AQ_HW_CHIP_ATLANTIC2) and add the aq_hw fields the firmware fills at boot
(ART base index, statistics interface version A0/B0). The per-VLAN-filter
resolver-tag field comes from the Linux driver; the BSD sources never
write it.
Firmware operations (aq_fwa2.c): Atlantic 2 talks to the management CPU
through the 0x12000/0x13000 register windows plus the boot handshake,
rather than Atlantic 1's mailbox in shared RAM. Implement that as a third
aq_firmware_ops vtable (reset, set_mode, get_mode, get_mac_addr,
get_stats); aq_fwa2_reboot() boots the firmware, selects the A2 ops, and
reads the version and ART base index, failing fast on the
crash-init / boot-failed bits. fwa2_set_mode advertises full duplex only
(the media model exposes no half-duplex types) and writes and acks the
link options before raising ACTIVE mode, so a forced media change does not
begin negotiation with a stale rate mask. enum aq_fw_link_speed gains
aq_fw_10M, which Atlantic 2 supports and Atlantic 1 does not.
Probe and attach: list the device ids with their media types and link
speeds (all copper; AQC113* up to 10G, AQC116C to 1G), populate
hw->device_id, and tag the generation with AQ_HW_CHIP_ATLANTIC2 so
IS_CHIP_FEATURE() recognises it uniformly. Branch firmware bring-up and
reset on the generation: aq_hw_init_ucp() and aq_hw_reset() reboot the MCP
instead of the Atlantic 1 RBL/FLB reset -- without a real datapath reset
every stop/init cycle reprograms the rings on a live, desynced RX DMA
engine and the receive path stays dead. aq_hw_init() programs the
Atlantic 2 launch-time clock ratio in place of the Atlantic 1
MRRS / TX-DMA request-limit clamp. Add an AQ_LINK_10M capability bit
(Atlantic 2 links at 10M, Atlantic 1 cannot), offer 10baseT media, and map
IFM_10_T to aq_fw_10M.
With every supported media type now present, replace the per-speed switch
statements in aq_media.c with a single {link bit, fw rate, IFM_* subtype,
Mbit/s} table -- one source of truth for the supported link speeds.
With this an Atlantic 2 card probes, brings up its firmware, reads its
MAC, and negotiates link; the RX action-resolver datapath comes next.
Reviewed by: adrian
Differential Revision: https://reviews.freebsd.org/D58141
| -rw-r--r-- | sys/conf/files | 1 | ||||
| -rw-r--r-- | sys/dev/aq/aq2_fw.c | 447 | ||||
| -rw-r--r-- | sys/dev/aq/aq2_hw.h | 276 | ||||
| -rw-r--r-- | sys/dev/aq/aq_device.h | 18 | ||||
| -rw-r--r-- | sys/dev/aq/aq_fw.h | 16 | ||||
| -rw-r--r-- | sys/dev/aq/aq_hw.c | 47 | ||||
| -rw-r--r-- | sys/dev/aq/aq_hw.h | 7 | ||||
| -rw-r--r-- | sys/dev/aq/aq_main.c | 51 | ||||
| -rw-r--r-- | sys/dev/aq/aq_media.c | 101 | ||||
| -rw-r--r-- | sys/modules/aq/Makefile | 2 |
10 files changed, 881 insertions, 85 deletions
diff --git a/sys/conf/files b/sys/conf/files index 3b5ba83eda28..fcce58183b9b 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -858,6 +858,7 @@ dev/aq/aq_hw_llh.c optional aq dev/aq/aq_fw.c optional aq dev/aq/aq_fw1x.c optional aq dev/aq/aq_fw2x.c optional aq +dev/aq/aq2_fw.c optional aq dev/aq/aq_dbg.c optional aq # dev/ata/ata_if.m optional ata | atacore diff --git a/sys/dev/aq/aq2_fw.c b/sys/dev/aq/aq2_fw.c new file mode 100644 index 000000000000..76152a9c07bb --- /dev/null +++ b/sys/dev/aq/aq2_fw.c @@ -0,0 +1,447 @@ +/*- + * Atlantic 2 (AQC113/114/115/116) firmware operations for aq(4). + * + * Adapted from the OpenBSD/NetBSD if_aq driver: + * + * Copyright (c) 2021 Jonathan Matthew <jonathan@d14n.org> + * Copyright (c) 2021 Mike Larkin <mlarkin@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/cdefs.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/endian.h> +#include <net/ethernet.h> + +#include "aq_common.h" +#include "aq_hw.h" +#include "aq_hw_llh.h" +#include "aq2_hw.h" +#include "aq_fw.h" + +static int aq2_fw_reset(struct aq_hw *hw); +static int aq2_fw_set_mode(struct aq_hw *hw, enum aq_hw_fw_mpi_state mode, + enum aq_fw_link_speed speed); +static int aq2_fw_get_mode(struct aq_hw *hw, enum aq_hw_fw_mpi_state *mode, + enum aq_fw_link_speed *speed, enum aq_fw_link_fc *fc); +static int aq2_fw_get_mac_addr(struct aq_hw *hw, uint8_t *mac); +static int aq2_fw_get_stats(struct aq_hw *hw, struct aq_hw_stats *stats); + +/* Coherent OUT-window read, bracketed by the transaction id. */ +static int +aq2_fw_interface_buffer_read(struct aq_hw *hw, uint32_t reg0, uint32_t *data0, + uint32_t size0) +{ + uint32_t tid0, tid1, reg, *data, size; + int timo; + + for (timo = 10000; timo > 0; timo--) { + tid0 = AQ_READ_REG(hw, AQ2_FW_INTERFACE_OUT_TRANSACTION_ID_REG); + if (((tid0 & AQ2_FW_INTERFACE_OUT_TRANSACTION_ID_A) >> + AQ2_FW_INTERFACE_OUT_TRANSACTION_ID_A_S) != + ((tid0 & AQ2_FW_INTERFACE_OUT_TRANSACTION_ID_B) >> + AQ2_FW_INTERFACE_OUT_TRANSACTION_ID_B_S)) { + DELAY(10); + continue; + } + + /* size0 is a 4-byte multiple: full register-width reads. */ + for (reg = reg0, data = data0, size = size0; + size >= 4; reg += 4, data++, size -= 4) + *data = AQ_READ_REG(hw, reg); + + tid1 = AQ_READ_REG(hw, AQ2_FW_INTERFACE_OUT_TRANSACTION_ID_REG); + if (tid0 == tid1) + break; + } + if (timo == 0) { + device_printf(hw->dev, "A2 interface buffer read timeout\n"); + return (ETIMEDOUT); + } + return (0); +} + +/* Boot the A2 firmware and select the A2 firmware ops. */ +int +aq2_fw_reboot(struct aq_hw *hw) +{ + uint32_t v, filter_caps[3]; + int timo, err; + const char *iface; + + hw->fw_ops = &aq2_fw_ops; + + AQ_WRITE_REG(hw, AQ2_MCP_HOST_REQ_INT_CLR_REG, 1); + AQ_WRITE_REG(hw, AQ2_MIF_BOOT_REG, 1); /* reboot request */ + for (timo = 20000; timo > 0; timo--) { + v = AQ_READ_REG(hw, AQ2_MIF_BOOT_REG); + if ((v & AQ2_MIF_BOOT_BOOT_STARTED) && v != 0xffffffff) + break; + DELAY(10); + } + if (timo <= 0) { + device_printf(hw->dev, "A2 firmware reboot timeout\n"); + return (ETIMEDOUT); + } + + for (timo = 200000; timo > 0; timo--) { + v = AQ_READ_REG(hw, AQ2_MIF_BOOT_REG); + if (v & AQ2_MIF_BOOT_COMPLETE) + break; + v = AQ_READ_REG(hw, AQ2_MCP_HOST_REQ_INT_REG); + if (v & AQ2_MCP_HOST_REQ_INT_READY) + break; + DELAY(10); + } + if (timo <= 0) { + device_printf(hw->dev, "A2 firmware restart timeout\n"); + return (ETIMEDOUT); + } + + v = AQ_READ_REG(hw, AQ2_MIF_BOOT_REG); + if (v & AQ2_MIF_BOOT_FAILED) { + device_printf(hw->dev, "A2 firmware restart failed\n"); + return (EIO); + } + v = AQ_READ_REG(hw, AQ2_MCP_HOST_REQ_INT_REG); + if (v & AQ2_MCP_HOST_REQ_INT_READY) { + device_printf(hw->dev, "A2 firmware required but not present\n"); + return (ENXIO); + } + + /* Repack into fw_version's major.minor.build layout. */ + err = aq2_fw_interface_buffer_read(hw, + AQ2_FW_INTERFACE_OUT_VERSION_BUNDLE_REG, &v, sizeof(v)); + if (err != 0) + return (err); + hw->fw_version.raw = + (((v & AQ2_FW_INTERFACE_OUT_VERSION_MAJOR) >> + AQ2_FW_INTERFACE_OUT_VERSION_MAJOR_S) << 24) | + (((v & AQ2_FW_INTERFACE_OUT_VERSION_MINOR) >> + AQ2_FW_INTERFACE_OUT_VERSION_MINOR_S) << 16) | + ((v & AQ2_FW_INTERFACE_OUT_VERSION_BUILD) >> + AQ2_FW_INTERFACE_OUT_VERSION_BUILD_S); + + err = aq2_fw_interface_buffer_read(hw, + AQ2_FW_INTERFACE_OUT_VERSION_IFACE_REG, &v, sizeof(v)); + if (err != 0) + return (err); + hw->aq2_iface = v & AQ2_FW_INTERFACE_OUT_VERSION_IFACE_VER; + switch (hw->aq2_iface) { + case AQ2_FW_INTERFACE_OUT_VERSION_IFACE_VER_A0: + iface = "A0"; + break; + case AQ2_FW_INTERFACE_OUT_VERSION_IFACE_VER_B0: + iface = "B0"; + break; + default: + iface = "unknown"; + break; + } + device_printf(hw->dev, "Atlantic 2 %s, firmware %u.%u.%u\n", iface, + hw->fw_version.major_version, hw->fw_version.minor_version, + hw->fw_version.build_number); + + /* Base row added to every action-resolver-table index. */ + err = aq2_fw_interface_buffer_read(hw, + AQ2_FW_INTERFACE_OUT_FILTER_CAPS_REG, filter_caps, + sizeof(filter_caps)); + if (err != 0) + return (err); + hw->art_filter_base_index = ((filter_caps[2] & + AQ2_FW_INTERFACE_OUT_FILTER_CAPS3_RESOLVER_BASE_INDEX) >> + AQ2_FW_INTERFACE_OUT_FILTER_CAPS3_RESOLVER_BASE_INDEX_SHIFT) * 8; + + return (0); +} + +/* Commit an IN-window write; wait for the MCP ack. */ +static int +aq2_fw_wait_shared_ack(struct aq_hw *hw) +{ + AQ_WRITE_REG(hw, AQ2_MIF_HOST_FINISHED_STATUS_WRITE_REG, + AQ2_MIF_HOST_FINISHED_STATUS_ACK); + return (AQ_HW_WAIT_FOR((AQ_READ_REG(hw, + AQ2_MIF_HOST_FINISHED_STATUS_READ_REG) & + AQ2_MIF_HOST_FINISHED_STATUS_ACK) == 0, 100, 1000)); +} + +static int +aq2_fw_reset(struct aq_hw *hw) +{ + uint32_t v; + int err; + + AQ_WRITE_REG_BIT(hw, AQ2_FW_INTERFACE_IN_LINK_CONTROL_REG, + AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE, 0, + AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE_ACTIVE); + + AQ_WRITE_REG(hw, AQ2_FW_INTERFACE_IN_MTU_REG, HW_ATL_B0_MTU_JUMBO); + + v = AQ_READ_REG(hw, AQ2_FW_INTERFACE_IN_REQUEST_POLICY_REG); + v |= AQ2_FW_INTERFACE_IN_REQUEST_POLICY_MCAST_QUEUE_OR_TC; + v &= ~AQ2_FW_INTERFACE_IN_REQUEST_POLICY_MCAST_RX_QUEUE_TC_INDEX; + v |= AQ2_FW_INTERFACE_IN_REQUEST_POLICY_MCAST_ACCEPT; + v |= AQ2_FW_INTERFACE_IN_REQUEST_POLICY_BCAST_QUEUE_OR_TC; + v &= ~AQ2_FW_INTERFACE_IN_REQUEST_POLICY_BCAST_RX_QUEUE_TC_INDEX; + v |= AQ2_FW_INTERFACE_IN_REQUEST_POLICY_BCAST_ACCEPT; + v |= AQ2_FW_INTERFACE_IN_REQUEST_POLICY_PROMISC_QUEUE_OR_TC; + v &= ~AQ2_FW_INTERFACE_IN_REQUEST_POLICY_PROMISC_RX_QUEUE_TX_INDEX; + AQ_WRITE_REG(hw, AQ2_FW_INTERFACE_IN_REQUEST_POLICY_REG, v); + + err = aq2_fw_wait_shared_ack(hw); + if (err != 0) + device_printf(hw->dev, "A2 firmware reset timed out\n"); + return (err); +} + +static int +aq2_fw_get_mac_addr(struct aq_hw *hw, uint8_t *mac) +{ + uint32_t mac_addr[2]; + + mac_addr[0] = AQ_READ_REG(hw, AQ2_FW_INTERFACE_IN_MAC_ADDRESS_REG); + mac_addr[1] = AQ_READ_REG(hw, AQ2_FW_INTERFACE_IN_MAC_ADDRESS_REG + 4); + + if (mac_addr[0] == 0 && mac_addr[1] == 0) { + device_printf(hw->dev, "A2 mac address not found\n"); + return (ENXIO); + } + + mac_addr[0] = htole32(mac_addr[0]); + mac_addr[1] = htole32(mac_addr[1]); + memcpy(mac, (uint8_t *)mac_addr, ETHER_ADDR_LEN); + return (0); +} + +static int +aq2_fw_set_mode(struct aq_hw *hw, enum aq_hw_fw_mpi_state mode, + enum aq_fw_link_speed speed) +{ + uint32_t v; + int err; + + v = AQ_READ_REG(hw, AQ2_FW_INTERFACE_IN_LINK_OPTIONS_REG); + v &= ~(AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_10G | + AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_N5G | + AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_5G | + AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_N2G5 | + AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_2G5 | + AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_1G | + AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_100M | + AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_10M | + AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_1G_HD | + AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_100M_HD | + AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_10M_HD); + v &= ~AQ2_FW_INTERFACE_IN_LINK_OPTIONS_LINK_UP; + + if (mode == MPI_INIT) { + if (speed & aq_fw_10G) + v |= AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_10G; + if (speed & aq_fw_5G) + v |= AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_N5G | + AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_5G; + if (speed & aq_fw_2G5) + v |= AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_N2G5 | + AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_2G5; + if (speed & aq_fw_1G) + v |= AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_1G; + if (speed & aq_fw_100M) + v |= AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_100M; + if (speed & aq_fw_10M) + v |= AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_10M; + + v &= ~(AQ2_FW_INTERFACE_IN_LINK_OPTIONS_PAUSE_TX | + AQ2_FW_INTERFACE_IN_LINK_OPTIONS_PAUSE_RX); + if (hw->fc.fc_tx) + v |= AQ2_FW_INTERFACE_IN_LINK_OPTIONS_PAUSE_TX; + if (hw->fc.fc_rx) + v |= AQ2_FW_INTERFACE_IN_LINK_OPTIONS_PAUSE_RX; + + v |= AQ2_FW_INTERFACE_IN_LINK_OPTIONS_LINK_UP; + } else { + AQ_WRITE_REG_BIT(hw, AQ2_FW_INTERFACE_IN_LINK_CONTROL_REG, + AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE, 0, + AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE_SHUTDOWN); + } + + /* Options acked before ACTIVE so bring-up negotiates the new mask. */ + AQ_WRITE_REG(hw, AQ2_FW_INTERFACE_IN_LINK_OPTIONS_REG, v); + if (mode == MPI_INIT) { + err = aq2_fw_wait_shared_ack(hw); + if (err != 0) + return (err); + AQ_WRITE_REG_BIT(hw, AQ2_FW_INTERFACE_IN_LINK_CONTROL_REG, + AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE, 0, + AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE_ACTIVE); + } + return (aq2_fw_wait_shared_ack(hw)); +} + +static int +aq2_fw_get_mode(struct aq_hw *hw, enum aq_hw_fw_mpi_state *modep, + enum aq_fw_link_speed *speedp, enum aq_fw_link_fc *fcp) +{ + uint32_t v; + enum aq_fw_link_speed speed; + enum aq_fw_link_fc fc = aq_fw_fc_none; + + if (modep != NULL) + *modep = MPI_INIT; + + v = AQ_READ_REG(hw, AQ2_FW_INTERFACE_OUT_LINK_STATUS_REG); + switch ((v & AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE) >> + AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_S) { + case AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_10G: + speed = aq_fw_10G; + break; + case AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_5G: + speed = aq_fw_5G; + break; + case AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_2G5: + speed = aq_fw_2G5; + break; + case AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_1G: + speed = aq_fw_1G; + break; + case AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_100M: + speed = aq_fw_100M; + break; + case AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_10M: + speed = aq_fw_10M; + break; + default: + speed = aq_fw_none; + break; + } + if (speedp != NULL) + *speedp = speed; + + if (v & AQ2_FW_INTERFACE_OUT_LINK_STATUS_PAUSE_TX) + fc |= aq_fw_fc_ENABLE_TX; + if (v & AQ2_FW_INTERFACE_OUT_LINK_STATUS_PAUSE_RX) + fc |= aq_fw_fc_ENABLE_RX; + if (fcp != NULL) + *fcp = fc; + + return (0); +} + +/* A2 firmware stats; layout depends on interface version. */ +struct aq2_fw_statistics_a0 { + uint32_t link_up; + uint32_t link_down; + uint64_t tx_unicast_octets; + uint64_t tx_multicast_octets; + uint64_t tx_broadcast_octets; + uint64_t rx_unicast_octets; + uint64_t rx_multicast_octets; + uint64_t rx_broadcast_octets; + uint32_t tx_unicast_frames; + uint32_t tx_multicast_frames; + uint32_t tx_broadcast_frames; + uint32_t tx_errors; + uint32_t rx_unicast_frames; + uint32_t rx_multicast_frames; + uint32_t rx_broadcast_frames; + uint32_t rx_dropped_frames; + uint32_t rx_errors; + uint32_t tx_good_frames; + uint32_t rx_good_frames; + uint32_t reserved1; + uint32_t main_loop_cycles; + uint32_t reserved2; +}; + +struct aq2_fw_statistics_b0 { + uint64_t rx_good_octets; + uint64_t rx_pause_frames; + uint64_t rx_good_frames; + uint64_t rx_errors; + uint64_t rx_unicast_frames; + uint64_t rx_multicast_frames; + uint64_t rx_broadcast_frames; + uint64_t tx_good_octets; + uint64_t tx_pause_frames; + uint64_t tx_good_frames; + uint64_t tx_errors; + uint64_t tx_unicast_frames; + uint64_t tx_multicast_frames; + uint64_t tx_broadcast_frames; + uint32_t main_loop_cycles; +} __packed; + +union aq2_fw_statistics { + struct aq2_fw_statistics_a0 a0; + struct aq2_fw_statistics_b0 b0; +}; + +static int +aq2_fw_get_stats(struct aq_hw *hw, struct aq_hw_stats *stats) +{ + union aq2_fw_statistics u; + int err; + + memset(stats, 0, sizeof(*stats)); + + err = aq2_fw_interface_buffer_read(hw, AQ2_FW_INTERFACE_OUT_STATS_REG, + (uint32_t *)&u, sizeof(u)); + if (err != 0) + return (err); + + if (hw->aq2_iface == AQ2_FW_INTERFACE_OUT_VERSION_IFACE_VER_A0) { + stats->uprc = u.a0.rx_unicast_frames; + stats->mprc = u.a0.rx_multicast_frames; + stats->bprc = u.a0.rx_broadcast_frames; + stats->erpr = u.a0.rx_errors; + stats->ubrc = u.a0.rx_unicast_octets; + stats->mbrc = u.a0.rx_multicast_octets; + stats->bbrc = u.a0.rx_broadcast_octets; + stats->prc = u.a0.rx_good_frames; + stats->uptc = u.a0.tx_unicast_frames; + stats->mptc = u.a0.tx_multicast_frames; + stats->bptc = u.a0.tx_broadcast_frames; + stats->erpt = u.a0.tx_errors; + stats->ubtc = u.a0.tx_unicast_octets; + stats->mbtc = u.a0.tx_multicast_octets; + stats->bbtc = u.a0.tx_broadcast_octets; + stats->ptc = u.a0.tx_good_frames; + } else if (hw->aq2_iface == AQ2_FW_INTERFACE_OUT_VERSION_IFACE_VER_B0) { + /* B0 reports aggregate octets only; per-cast stays zero. */ + stats->uprc = u.b0.rx_unicast_frames; + stats->mprc = u.b0.rx_multicast_frames; + stats->bprc = u.b0.rx_broadcast_frames; + stats->erpr = u.b0.rx_errors; + stats->prc = u.b0.rx_good_frames; + stats->uptc = u.b0.tx_unicast_frames; + stats->mptc = u.b0.tx_multicast_frames; + stats->bptc = u.b0.tx_broadcast_frames; + stats->erpt = u.b0.tx_errors; + stats->ptc = u.b0.tx_good_frames; + } + + return (0); +} + +const struct aq_firmware_ops aq2_fw_ops = { + .reset = aq2_fw_reset, + .set_mode = aq2_fw_set_mode, + .get_mode = aq2_fw_get_mode, + .get_mac_addr = aq2_fw_get_mac_addr, + .get_stats = aq2_fw_get_stats, + .led_control = NULL, +}; diff --git a/sys/dev/aq/aq2_hw.h b/sys/dev/aq/aq2_hw.h new file mode 100644 index 000000000000..a2c21162c2d9 --- /dev/null +++ b/sys/dev/aq/aq2_hw.h @@ -0,0 +1,276 @@ +/*- + * Atlantic 2 (AQC113/114/115/116) register definitions for aq(4). + * + * Register layout and field definitions adapted from the OpenBSD/NetBSD + * if_aq driver: + * + * Copyright (c) 2021 Jonathan Matthew <jonathan@d14n.org> + * Copyright (c) 2021 Mike Larkin <mlarkin@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef AQ2_HW_H +#define AQ2_HW_H + +/* Atlantic 2 PCI device IDs (Aquantia/Marvell). */ +#define AQ_DEVICE_ID_AQC113 0x04C0 +#define AQ_DEVICE_ID_AQC113C 0x14C0 +#define AQ_DEVICE_ID_AQC113CA 0x34C0 +#define AQ_DEVICE_ID_AQC113CS 0x94C0 +#define AQ_DEVICE_ID_AQC114CS 0x93C0 +#define AQ_DEVICE_ID_AQC115C 0x12C0 +#define AQ_DEVICE_ID_AQC116C 0x11C0 + +static __inline bool +aq_is_atlantic2(uint16_t device_id) +{ + switch (device_id) { + case AQ_DEVICE_ID_AQC113: + case AQ_DEVICE_ID_AQC113C: + case AQ_DEVICE_ID_AQC113CA: + case AQ_DEVICE_ID_AQC113CS: + case AQ_DEVICE_ID_AQC114CS: + case AQ_DEVICE_ID_AQC115C: + case AQ_DEVICE_ID_AQC116C: + return (true); + default: + return (false); + } +} + +/* Atlantic 2 host packet-buffer sizes (Atlantic 1 uses 160/320). */ +#define AQ2_HW_TXBUF_MAX 128 +#define AQ2_HW_RXBUF_MAX 192 + +/* A2 firmware handshake registers (host <-> MCP). */ +#define AQ2_MIF_HOST_FINISHED_STATUS_WRITE_REG 0x0e00 +#define AQ2_MIF_HOST_FINISHED_STATUS_READ_REG 0x0e04 +#define AQ2_MIF_HOST_FINISHED_STATUS_ACK (1 << 0) + +#define AQ2_MCP_HOST_REQ_INT_REG 0x0f00 +#define AQ2_MCP_HOST_REQ_INT_READY (1 << 0) +#define AQ2_MCP_HOST_REQ_INT_SET_REG 0x0f04 +#define AQ2_MCP_HOST_REQ_INT_CLR_REG 0x0f08 + +#define AQ2_MIF_BOOT_REG 0x3040 +#define AQ2_MIF_BOOT_HOST_DATA_LOADED (1 << 16) +#define AQ2_MIF_BOOT_BOOT_STARTED (1 << 24) +#define AQ2_MIF_BOOT_CRASH_INIT (1 << 27) +#define AQ2_MIF_BOOT_BOOT_CODE_FAILED (1 << 28) +#define AQ2_MIF_BOOT_FW_INIT_FAILED (1 << 29) +#define AQ2_MIF_BOOT_FW_INIT_COMP_SUCCESS (1U << 31) +#define AQ2_MIF_BOOT_FAILED \ + (AQ2_MIF_BOOT_CRASH_INIT | AQ2_MIF_BOOT_BOOT_CODE_FAILED | \ + AQ2_MIF_BOOT_FW_INIT_FAILED) +#define AQ2_MIF_BOOT_COMPLETE \ + (AQ2_MIF_BOOT_FAILED | AQ2_MIF_BOOT_FW_INIT_COMP_SUCCESS) + +/* The ART semaphore is global CPU semaphore index 3. */ +#define AQ2_ART_SEM_INDEX 3 + +/* Action Resolver Table (replaces Atlantic 1's discrete RPF filters). */ +#define AQ2_ART_ACTION_ACT_SHIFT 8 +#define AQ2_ART_ACTION_RSS 0x0080 +#define AQ2_ART_ACTION_INDEX_SHIFT 2 +#define AQ2_ART_ACTION_ENABLE 0x0001 +#define AQ2_ART_ACTION(act, rss, idx, en) \ + (((act) << AQ2_ART_ACTION_ACT_SHIFT) | \ + ((rss) ? AQ2_ART_ACTION_RSS : 0) | \ + ((idx) << AQ2_ART_ACTION_INDEX_SHIFT) | \ + ((en) ? AQ2_ART_ACTION_ENABLE : 0)) +#define AQ2_ART_ACTION_DROP AQ2_ART_ACTION(0, 0, 0, 1) +#define AQ2_ART_ACTION_DISABLE AQ2_ART_ACTION(0, 0, 0, 0) +#define AQ2_ART_ACTION_ASSIGN_QUEUE(q) AQ2_ART_ACTION(1, 0, (q), 1) +#define AQ2_ART_ACTION_ASSIGN_TC(tc) AQ2_ART_ACTION(1, 1, (tc), 1) + +#define AQ2_RPF_TAG_PCP_MASK 0xe0000000 +#define AQ2_RPF_TAG_PCP_SHIFT 29 +#define AQ2_RPF_TAG_FLEX_MASK 0x18000000 +#define AQ2_RPF_TAG_UNKNOWN_MASK 0x07000000 +#define AQ2_RPF_TAG_L4_MASK 0x00e00000 +#define AQ2_RPF_TAG_L3_V6_MASK 0x001c0000 +#define AQ2_RPF_TAG_L3_V4_MASK 0x00038000 +#define AQ2_RPF_TAG_UNTAG_MASK 0x00004000 +#define AQ2_RPF_TAG_VLAN_MASK 0x00003c00 +#define AQ2_RPF_TAG_ET_MASK 0x00000380 +#define AQ2_RPF_TAG_ALLMC_MASK 0x00000040 +#define AQ2_RPF_TAG_UC_MASK 0x0000002f + +/* Logical ART row indices (before adding sc_art_filter_base_index). */ +#define AQ2_RPF_INDEX_L2_PROMISC_OFF 0 +#define AQ2_RPF_INDEX_VLAN_PROMISC_OFF 1 +#define AQ2_RPF_INDEX_L3L4_USER 8 +#define AQ2_RPF_INDEX_ET_PCP_USER 24 +#define AQ2_RPF_INDEX_VLAN_USER 40 +#define AQ2_RPF_INDEX_PCP_TO_TC 56 + +#define AQ2_RPF_L2BC_TAG_REG 0x50f0 +#define AQ2_RPF_L2BC_TAG_MASK 0x0000003f + +#define AQ2_RPF_NEW_CTRL_REG 0x5104 +#define AQ2_RPF_NEW_CTRL_ENABLE (1 << 11) + +#define AQ2_RPF_REDIR2_REG 0x54c8 +#define AQ2_RPF_REDIR2_INDEX (1 << 12) +#define AQ2_RPF_REDIR2_HASHTYPE 0x000001FF +#define AQ2_RPF_REDIR2_HASHTYPE_NONE 0 +#define AQ2_RPF_REDIR2_HASHTYPE_ALL 0x000001FF + +#define AQ2_RX_Q_TC_MAP_REG(i) (0x5900 + (i) * 4) +#define AQ2_TX_Q_TC_MAP_REG(i) (0x799c + (i) * 4) + +#define AQ2_RPF_RSS_REDIR_MAX 64 +#define AQ2_RPF_RSS_REDIR_REG(tc, i) \ + (0x6200 + (0x100 * ((tc) >> 2)) + (i) * 4) + +#define AQ2_RPF_REC_TAB_ENABLE_REG 0x6ff0 +#define AQ2_RPF_REC_TAB_ENABLE_MASK 0x0000ffff + +#define AQ2_LAUNCHTIME_CTRL_REG 0x7a1c +#define AQ2_LAUNCHTIME_CTRL_RATIO 0x0000ff00 +#define AQ2_LAUNCHTIME_CTRL_RATIO_SPEED_QUARTER 4 +#define AQ2_LAUNCHTIME_CTRL_RATIO_SPEED_HALF 2 +#define AQ2_LAUNCHTIME_CTRL_RATIO_SPEED_FULL 1 + +#define AQ2_HW_FPGA_VERSION_REG 0x00f4 + +#define AQ2_TX_INTR_MODERATION_CTL_REG(i) (0x7c28 + (i) * 0x40) +#define AQ2_TX_INTR_MODERATION_CTL_EN (1 << 1) +#define AQ2_TX_INTR_MODERATION_CTL_MIN 0x0000ff00 +#define AQ2_TX_INTR_MODERATION_CTL_MAX 0x01ff0000 + +/* Firmware interface IN window (host -> MCP). */ +#define AQ2_FW_INTERFACE_IN_MTU_REG 0x12000 +#define AQ2_FW_INTERFACE_IN_MAC_ADDRESS_REG 0x12008 + +#define AQ2_FW_INTERFACE_IN_LINK_CONTROL_REG 0x12010 +#define AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE 0x0000000f +#define AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE_INVALID 0 +#define AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE_ACTIVE 1 +#define AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE_SLEEP_PROXY 2 +#define AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE_LOWPOWER 3 +#define AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE_SHUTDOWN 4 + +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_REG 0x12018 +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_DOWNSHIFT (1 << 27) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_PAUSE_TX (1 << 25) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_PAUSE_RX (1 << 24) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_EEE_10G (1 << 20) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_EEE_5G (1 << 19) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_EEE_2G5 (1 << 18) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_EEE_1G (1 << 17) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_EEE_100M (1 << 16) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_10G (1 << 15) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_N5G (1 << 14) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_5G (1 << 13) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_N2G5 (1 << 12) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_2G5 (1 << 11) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_1G (1 << 10) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_100M (1 << 9) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_10M (1 << 8) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_1G_HD (1 << 7) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_100M_HD (1 << 6) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_10M_HD (1 << 5) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_LINK_RENEGOTIATE (1 << 1) +#define AQ2_FW_INTERFACE_IN_LINK_OPTIONS_LINK_UP (1 << 0) + +#define AQ2_FW_INTERFACE_IN_REQUEST_POLICY_REG 0x12a58 +#define AQ2_FW_INTERFACE_IN_REQUEST_POLICY_MCAST_QUEUE_OR_TC 0x00800000 +#define AQ2_FW_INTERFACE_IN_REQUEST_POLICY_MCAST_RX_QUEUE_TC_INDEX 0x007c0000 +#define AQ2_FW_INTERFACE_IN_REQUEST_POLICY_MCAST_ACCEPT 0x00010000 +#define AQ2_FW_INTERFACE_IN_REQUEST_POLICY_BCAST_QUEUE_OR_TC 0x00008000 +#define AQ2_FW_INTERFACE_IN_REQUEST_POLICY_BCAST_RX_QUEUE_TC_INDEX 0x00007c00 +#define AQ2_FW_INTERFACE_IN_REQUEST_POLICY_BCAST_ACCEPT 0x00000100 +#define AQ2_FW_INTERFACE_IN_REQUEST_POLICY_PROMISC_QUEUE_OR_TC 0x00000080 +#define AQ2_FW_INTERFACE_IN_REQUEST_POLICY_PROMISC_RX_QUEUE_TX_INDEX 0x0000007c +#define AQ2_FW_INTERFACE_IN_REQUEST_POLICY_PROMISC_MCAST 0x00000002 +#define AQ2_FW_INTERFACE_IN_REQUEST_POLICY_PROMISC_ALL 0x00000001 + +/* Firmware interface OUT window (MCP -> host). */ +#define AQ2_FW_INTERFACE_OUT_TRANSACTION_ID_REG 0x13000 +#define AQ2_FW_INTERFACE_OUT_TRANSACTION_ID_B 0xffff0000 +#define AQ2_FW_INTERFACE_OUT_TRANSACTION_ID_B_S 16 +#define AQ2_FW_INTERFACE_OUT_TRANSACTION_ID_A 0x0000ffff +#define AQ2_FW_INTERFACE_OUT_TRANSACTION_ID_A_S 0 + +#define AQ2_FW_INTERFACE_OUT_VERSION_BUNDLE_REG 0x13004 +#define AQ2_FW_INTERFACE_OUT_VERSION_MAC_REG 0x13008 + +#define AQ2_FW_INTERFACE_OUT_VERSION_PHY_REG 0x1300c +#define AQ2_FW_INTERFACE_OUT_VERSION_BUILD 0xffff0000 +#define AQ2_FW_INTERFACE_OUT_VERSION_BUILD_S 16 +#define AQ2_FW_INTERFACE_OUT_VERSION_MINOR 0x0000ff00 +#define AQ2_FW_INTERFACE_OUT_VERSION_MINOR_S 8 +#define AQ2_FW_INTERFACE_OUT_VERSION_MAJOR 0x000000ff +#define AQ2_FW_INTERFACE_OUT_VERSION_MAJOR_S 0 + +#define AQ2_FW_INTERFACE_OUT_VERSION_IFACE_REG 0x13010 +#define AQ2_FW_INTERFACE_OUT_VERSION_IFACE_VER 0x0000000f +#define AQ2_FW_INTERFACE_OUT_VERSION_IFACE_VER_A0 0 +#define AQ2_FW_INTERFACE_OUT_VERSION_IFACE_VER_B0 1 + +#define AQ2_FW_INTERFACE_OUT_STATS_REG 0x13700 + +#define AQ2_FW_INTERFACE_OUT_LINK_STATUS_REG 0x13014 +#define AQ2_FW_INTERFACE_OUT_LINK_STATUS_DUPLEX (1 << 11) +#define AQ2_FW_INTERFACE_OUT_LINK_STATUS_EEE (1 << 10) +#define AQ2_FW_INTERFACE_OUT_LINK_STATUS_PAUSE_RX (1 << 9) +#define AQ2_FW_INTERFACE_OUT_LINK_STATUS_PAUSE_TX (1 << 8) +#define AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE 0x000000f0 +#define AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_S 4 +#define AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_10G 6 +#define AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_5G 5 +#define AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_2G5 4 +#define AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_1G 3 +#define AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_100M 2 +#define AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_10M 1 +#define AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_INVALID 0 +#define AQ2_FW_INTERFACE_OUT_LINK_STATUS_STATE 0x0000000f + +#define AQ2_FW_INTERFACE_OUT_FILTER_CAPS_REG 0x13774 +#define AQ2_FW_INTERFACE_OUT_FILTER_CAPS3_RESOLVER_BASE_INDEX 0x00ff0000 +#define AQ2_FW_INTERFACE_OUT_FILTER_CAPS3_RESOLVER_BASE_INDEX_SHIFT 16 + +#define AQ2_RPF_ACT_ART_REQ_TAG_REG(i) (0x14000 + (i) * 0x10) +#define AQ2_RPF_ACT_ART_REQ_MASK_REG(i) (0x14004 + (i) * 0x10) +#define AQ2_RPF_ACT_ART_REQ_ACTION_REG(i) (0x14008 + (i) * 0x10) + +/* + * L2 unicast filter MSW register (shared with Atlantic 1). Atlantic 2 sets + * the TAG field so unicast frames are classified into the resolver table. + */ +#define AQ2_RPF_VLAN_FLR_TAG_REG(i) (0x5290 + (i) * 4) +#define AQ2_RPF_VLAN_FLR_TAG 0x0000F000 +#define AQ2_RPF_VLAN_FLR_TAG_SHIFT 12 + +#define RPF_L2UC_MSW_REG(i) (0x5114 + (i) * 8) +#define RPF_L2UC_MSW_TAG 0x03c00000 +#define RPF_L2UC_MSW_TAG_SHIFT 22 + +/* + * Tx packet-scheduler data-TC credit/weight (shared register, but the + * Atlantic 2 fields are wider than Atlantic 1's), and the Tx buffer control + * register whose randomized ring->TC map Atlantic 2 enables. + */ +#define TPS_DATA_TCT_REG(i) (0x7110 + (i) * 4) +#define TPS2_DATA_TCT_CREDIT_MAX 0xffff0000 +#define TPS2_DATA_TCT_CREDIT_MAX_SHIFT 16 +#define TPS2_DATA_TCT_WEIGHT 0x00007fff +#define TPS2_DATA_TCT_WEIGHT_SHIFT 0 + +#define TPB_TX_BUF_REG 0x7900 +#define TPB_TX_BUF_TC_Q_RAND_MAP_EN (1 << 9) +#define TPB_TX_BUF_TC_Q_RAND_MAP_EN_SHIFT 9 + +#endif /* AQ2_HW_H */ diff --git a/sys/dev/aq/aq_device.h b/sys/dev/aq/aq_device.h index 23785c1854c5..acdf897562f0 100644 --- a/sys/dev/aq/aq_device.h +++ b/sys/dev/aq/aq_device.h @@ -44,14 +44,18 @@ enum aq_media_type { }; #define AQ_LINK_UNKNOWN 0x00000000 -#define AQ_LINK_100M 0x00000001 -#define AQ_LINK_1G 0x00000002 -#define AQ_LINK_2G5 0x00000004 -#define AQ_LINK_5G 0x00000008 -#define AQ_LINK_10G 0x00000010 +#define AQ_LINK_10M 0x00000001 +#define AQ_LINK_100M 0x00000002 +#define AQ_LINK_1G 0x00000004 +#define AQ_LINK_2G5 0x00000008 +#define AQ_LINK_5G 0x00000010 +#define AQ_LINK_10G 0x00000020 -#define AQ_LINK_ALL (AQ_LINK_100M | AQ_LINK_1G | AQ_LINK_2G5 | AQ_LINK_5G | \ - AQ_LINK_10G ) +#define AQ_LINK_ALL (AQ_LINK_10M | AQ_LINK_100M | AQ_LINK_1G | \ + AQ_LINK_2G5 | AQ_LINK_5G | AQ_LINK_10G) + +/* Atlantic 1 has no 10BASE-T PHY. */ +#define AQ_LINK_ALL_ATLANTIC1 (AQ_LINK_ALL & ~AQ_LINK_10M) struct aq_stats { uint64_t prc; diff --git a/sys/dev/aq/aq_fw.h b/sys/dev/aq/aq_fw.h index be6661db7d57..7bb35504eab6 100644 --- a/sys/dev/aq/aq_fw.h +++ b/sys/dev/aq/aq_fw.h @@ -39,11 +39,12 @@ struct aq_hw; enum aq_fw_link_speed { aq_fw_none = 0, - aq_fw_100M = (1 << 0), - aq_fw_1G = (1 << 1), - aq_fw_2G5 = (1 << 2), - aq_fw_5G = (1 << 3), - aq_fw_10G = (1 << 4), + aq_fw_10M = (1 << 0), /* Atlantic 2 only */ + aq_fw_100M = (1 << 1), + aq_fw_1G = (1 << 2), + aq_fw_2G5 = (1 << 3), + aq_fw_5G = (1 << 4), + aq_fw_10G = (1 << 5), }; enum aq_fw_link_fc @@ -55,7 +56,7 @@ enum aq_fw_link_fc }; #define aq_fw_speed_auto \ - (aq_fw_100M | aq_fw_1G | aq_fw_2G5 | aq_fw_5G | aq_fw_10G) + (aq_fw_10M | aq_fw_100M | aq_fw_1G | aq_fw_2G5 | aq_fw_5G | aq_fw_10G) struct aq_firmware_ops { @@ -70,10 +71,13 @@ struct aq_firmware_ops int (*led_control)(struct aq_hw* hw, uint32_t mode); }; +/* aq_fw1x/aq_fw2x: Atlantic 1 firmware ABIs; aq2_fw: Atlantic 2 (AQC11x). */ extern const struct aq_firmware_ops aq_fw1x_ops; extern const struct aq_firmware_ops aq_fw2x_ops; +extern const struct aq_firmware_ops aq2_fw_ops; int aq_fw_reset(struct aq_hw* hw); int aq_fw_ops_init(struct aq_hw* hw); +int aq2_fw_reboot(struct aq_hw* hw); #endif // AQ_FW_H diff --git a/sys/dev/aq/aq_hw.c b/sys/dev/aq/aq_hw.c index 2c147ee61a4e..bbcc79805a4e 100644 --- a/sys/dev/aq/aq_hw.c +++ b/sys/dev/aq/aq_hw.c @@ -39,6 +39,7 @@ #include <machine/cpu.h> #include "aq_hw.h" +#include "aq2_hw.h" #include "aq_dbg.h" #include "aq_hw_llh.h" #include "aq_fw.h" @@ -151,6 +152,10 @@ aq_hw_init_ucp(struct aq_hw *hw) hw->fw_version.raw = 0; + /* Atlantic 2 uses a different reset/firmware handshake. */ + if (IS_CHIP_FEATURE(hw, ATLANTIC2)) + return (aq2_fw_reboot(hw)); + err = aq_fw_reset(hw); if (err != 0) { device_printf(hw->dev, "aq_hw_init_ucp(): F/W reset failed, err %d\n", err); @@ -280,6 +285,9 @@ aq_hw_get_link_state(struct aq_hw *hw, uint32_t *link_speed, struct aq_hw_fc_inf case aq_fw_100M: *link_speed = 100U; break; + case aq_fw_10M: + *link_speed = 10U; + break; default: *link_speed = 0U; break; @@ -361,7 +369,11 @@ aq_hw_reset(struct aq_hw *hw) AQ_DBG_ENTER(); - err = aq_fw_reset(hw); + /* A2 resets by rebooting the MCP; A1 uses the RBL/FLB reset. */ + if (IS_CHIP_FEATURE(hw, ATLANTIC2)) + err = aq2_fw_reboot(hw); + else + err = aq_fw_reset(hw); if (err != 0) goto err_exit; @@ -634,15 +646,32 @@ aq_hw_init(struct aq_hw *hw, uint8_t *mac_addr, uint8_t adm_irq, bool msix) AQ_DBG_ENTER(); - /* Force limit MRRS on RDM/TDM to 2K */ - val = AQ_READ_REG(hw, AQ_HW_PCI_REG_CONTROL_6_ADR); - AQ_WRITE_REG(hw, AQ_HW_PCI_REG_CONTROL_6_ADR, (val & ~0x707) | 0x404); + /* Atlantic 1 only: clamp MRRS and TX DMA total request limit. */ + if (!IS_CHIP_FEATURE(hw, ATLANTIC2)) { + /* Force limit MRRS on RDM/TDM to 2K */ + val = AQ_READ_REG(hw, AQ_HW_PCI_REG_CONTROL_6_ADR); + AQ_WRITE_REG(hw, AQ_HW_PCI_REG_CONTROL_6_ADR, + (val & ~0x707) | 0x404); - /* TX DMA total request limit. B0 hardware is not capable to - * handle more than (8K-MRRS) incoming DMA data. - * Value 24 in 256byte units - */ - AQ_WRITE_REG(hw, AQ_HW_TX_DMA_TOTAL_REQ_LIMIT_ADR, 24); + /* TX DMA total request limit. B0 hardware is not capable to + * handle more than (8K-MRRS) incoming DMA data. + * Value 24 in 256byte units + */ + AQ_WRITE_REG(hw, AQ_HW_TX_DMA_TOTAL_REQ_LIMIT_ADR, 24); + } else { + /* Atlantic 2: launch-time clock ratio per FPGA version. */ + uint32_t fpgaver = AQ_READ_REG(hw, AQ2_HW_FPGA_VERSION_REG); + uint32_t ratio; + + if (fpgaver < 0x01000000U) + ratio = AQ2_LAUNCHTIME_CTRL_RATIO_SPEED_FULL; + else if (fpgaver >= 0x01008502U) + ratio = AQ2_LAUNCHTIME_CTRL_RATIO_SPEED_HALF; + else + ratio = AQ2_LAUNCHTIME_CTRL_RATIO_SPEED_QUARTER; + AQ_WRITE_REG_BIT(hw, AQ2_LAUNCHTIME_CTRL_REG, + AQ2_LAUNCHTIME_CTRL_RATIO, 8, ratio); + } err = aq_hw_init_tx_path(hw); if (err != 0) diff --git a/sys/dev/aq/aq_hw.h b/sys/dev/aq/aq_hw.h index 22e10ba72c54..eb46641ef35d 100644 --- a/sys/dev/aq/aq_hw.h +++ b/sys/dev/aq/aq_hw.h @@ -201,6 +201,12 @@ struct aq_hw { u_long flags; uint32_t tx_rings_count; + + /* Atlantic 2: base row added to every action-resolver-table index. */ + uint32_t art_filter_base_index; + + /* Atlantic 2: firmware statistics interface version (A0/B0). */ + uint32_t aq2_iface; }; #define AQ_HW_MAC 0U @@ -254,6 +260,7 @@ struct aq_hw { #define AQ_HW_CHIP_REVISION_A0 0x01000000U #define AQ_HW_CHIP_REVISION_B0 0x02000000U #define AQ_HW_CHIP_REVISION_B1 0x04000000U +#define AQ_HW_CHIP_ATLANTIC2 0x08000000U #define IS_CHIP_FEATURE(HW, _F_) (AQ_HW_CHIP_##_F_ & \ (HW)->chip_features) diff --git a/sys/dev/aq/aq_main.c b/sys/dev/aq/aq_main.c index 73dc484d1ded..f52407827bc5 100644 --- a/sys/dev/aq/aq_main.c +++ b/sys/dev/aq/aq_main.c @@ -69,6 +69,7 @@ __FBSDID("$FreeBSD$"); #include "aq_device.h" #include "aq_fw.h" #include "aq_hw.h" +#include "aq2_hw.h" #include "aq_hw_llh.h" #include "aq_ring.h" #include "aq_dbg.h" @@ -136,6 +137,22 @@ static pci_vendor_info_t aq_vendor_info_array[] = { PVID(AQUANTIA_VENDOR_ID, AQ_DEVICE_ID_AQC112S, "Aquantia AQtion 2.5Gbit Network Adapter"), + /* Atlantic 2 (Marvell AQtion) */ + PVID(AQUANTIA_VENDOR_ID, AQ_DEVICE_ID_AQC113, + "Marvell AQtion 10Gbit Network Adapter"), + PVID(AQUANTIA_VENDOR_ID, AQ_DEVICE_ID_AQC113C, + "Marvell AQtion 10Gbit Network Adapter"), + PVID(AQUANTIA_VENDOR_ID, AQ_DEVICE_ID_AQC113CA, + "Marvell AQtion 10Gbit Network Adapter"), + PVID(AQUANTIA_VENDOR_ID, AQ_DEVICE_ID_AQC113CS, + "Marvell AQtion 10Gbit Network Adapter"), + PVID(AQUANTIA_VENDOR_ID, AQ_DEVICE_ID_AQC114CS, + "Marvell AQtion 5Gbit Network Adapter"), + PVID(AQUANTIA_VENDOR_ID, AQ_DEVICE_ID_AQC115C, + "Marvell AQtion 2.5Gbit Network Adapter"), + PVID(AQUANTIA_VENDOR_ID, AQ_DEVICE_ID_AQC116C, + "Marvell AQtion 1Gbit Network Adapter"), + PVID_END }; @@ -344,6 +361,9 @@ aq_if_attach_pre(if_ctx_t ctx) softc->hw.hw_tag = softc->mmio_tag; softc->hw.hw_handle = softc->mmio_handle; softc->hw.dev = softc->dev; + softc->hw.device_id = pci_get_device(softc->dev); + if (aq_is_atlantic2(softc->hw.device_id)) + softc->hw.chip_features |= AQ_HW_CHIP_ATLANTIC2; hw = &softc->hw; hw->link_rate = aq_fw_speed_auto; hw->itr = -1; @@ -1166,7 +1186,7 @@ aq_hw_capabilities(struct aq_dev *softc) case AQ_DEVICE_ID_AQC100: case AQ_DEVICE_ID_AQC100S: softc->media_type = AQ_MEDIA_TYPE_FIBRE; - softc->link_speeds = AQ_LINK_ALL & ~AQ_LINK_10G; + softc->link_speeds = AQ_LINK_ALL_ATLANTIC1; break; case AQ_DEVICE_ID_0001: @@ -1174,7 +1194,7 @@ aq_hw_capabilities(struct aq_dev *softc) case AQ_DEVICE_ID_AQC107: case AQ_DEVICE_ID_AQC107S: softc->media_type = AQ_MEDIA_TYPE_TP; - softc->link_speeds = AQ_LINK_ALL; + softc->link_speeds = AQ_LINK_ALL_ATLANTIC1; break; case AQ_DEVICE_ID_D108: @@ -1183,7 +1203,7 @@ aq_hw_capabilities(struct aq_dev *softc) case AQ_DEVICE_ID_AQC111: case AQ_DEVICE_ID_AQC111S: softc->media_type = AQ_MEDIA_TYPE_TP; - softc->link_speeds = AQ_LINK_ALL & ~AQ_LINK_10G; + softc->link_speeds = AQ_LINK_ALL_ATLANTIC1 & ~AQ_LINK_10G; break; case AQ_DEVICE_ID_D109: @@ -1192,9 +1212,34 @@ aq_hw_capabilities(struct aq_dev *softc) case AQ_DEVICE_ID_AQC112: case AQ_DEVICE_ID_AQC112S: softc->media_type = AQ_MEDIA_TYPE_TP; + softc->link_speeds = AQ_LINK_ALL_ATLANTIC1 & + ~(AQ_LINK_10G | AQ_LINK_5G); + break; + + case AQ_DEVICE_ID_AQC113: + case AQ_DEVICE_ID_AQC113C: + case AQ_DEVICE_ID_AQC113CA: + case AQ_DEVICE_ID_AQC113CS: + softc->media_type = AQ_MEDIA_TYPE_TP; + softc->link_speeds = AQ_LINK_ALL; + break; + + case AQ_DEVICE_ID_AQC114CS: + softc->media_type = AQ_MEDIA_TYPE_TP; + softc->link_speeds = AQ_LINK_ALL & ~AQ_LINK_10G; + break; + + case AQ_DEVICE_ID_AQC115C: + softc->media_type = AQ_MEDIA_TYPE_TP; softc->link_speeds = AQ_LINK_ALL & ~(AQ_LINK_10G | AQ_LINK_5G); break; + case AQ_DEVICE_ID_AQC116C: + softc->media_type = AQ_MEDIA_TYPE_TP; + softc->link_speeds = + AQ_LINK_ALL & ~(AQ_LINK_10G | AQ_LINK_5G | AQ_LINK_2G5); + break; + default: return (ENXIO); } diff --git a/sys/dev/aq/aq_media.c b/sys/dev/aq/aq_media.c index 5d974c6b0472..dcb59ce65b2c 100644 --- a/sys/dev/aq/aq_media.c +++ b/sys/dev/aq/aq_media.c @@ -50,13 +50,32 @@ __FBSDID("$FreeBSD$"); #include "aq_fw.h" #include "aq_dbg.h" -#define AQ_HW_SUPPORT_SPEED(softc, s) ((softc)->link_speeds & s) +/* + * Single source of truth for the supported link speeds. + * + * TODO: Split this by media type so the AQC100 SFP+ devices use suitable + * fibre/SFP+ IFM_* subtypes instead of advertising the copper IFM_*_T set. + */ +static const struct aq_media_map { + uint32_t link_bit; /* AQ_LINK_* capability bit */ + enum aq_fw_link_speed fw_rate; /* aq_fw_* rate */ + int ifm; /* IFM_* media subtype */ + uint32_t mbps; /* link speed, Mbit/s */ +} aq_media_types[] = { + { AQ_LINK_10M, aq_fw_10M, IFM_10_T, 10 }, + { AQ_LINK_100M, aq_fw_100M, IFM_100_TX, 100 }, + { AQ_LINK_1G, aq_fw_1G, IFM_1000_T, 1000 }, + { AQ_LINK_2G5, aq_fw_2G5, IFM_2500_T, 2500 }, + { AQ_LINK_5G, aq_fw_5G, IFM_5000_T, 5000 }, + { AQ_LINK_10G, aq_fw_10G, IFM_10G_T, 10000 }, +}; void aq_mediastatus_update(struct aq_dev *aq_dev, uint32_t link_speed, const struct aq_hw_fc_info *fc_neg) { struct aq_hw *hw = &aq_dev->hw; + u_int i; aq_dev->media_active = 0; if (fc_neg->fc_rx) @@ -64,27 +83,11 @@ const struct aq_hw_fc_info *fc_neg) if (fc_neg->fc_tx) aq_dev->media_active |= IFM_ETH_TXPAUSE; - switch(link_speed) { - case 100: - aq_dev->media_active |= IFM_100_TX | IFM_FDX; - break; - case 1000: - aq_dev->media_active |= IFM_1000_T | IFM_FDX; - break; - case 2500: - aq_dev->media_active |= IFM_2500_T | IFM_FDX; - break; - case 5000: - aq_dev->media_active |= IFM_5000_T | IFM_FDX; - break; - case 10000: - aq_dev->media_active |= IFM_10G_T | IFM_FDX; - break; - case 0: - default: - aq_dev->media_active |= IFM_NONE; - break; - } + for (i = 0; i < nitems(aq_media_types); i++) + if (link_speed == aq_media_types[i].mbps) + break; + aq_dev->media_active |= i < nitems(aq_media_types) ? + (aq_media_types[i].ifm | IFM_FDX) : IFM_NONE; if (hw->link_rate == aq_fw_speed_auto) aq_dev->media_active |= IFM_AUTO; @@ -114,6 +117,7 @@ aq_mediachange(if_t ifp) struct ifmedia *ifm = iflib_get_media(aq_dev->ctx); int user_media = IFM_SUBTYPE(ifm->ifm_media); uint64_t media_rate; + u_int i; AQ_DBG_ENTERA("media 0x%x", user_media); @@ -136,34 +140,18 @@ aq_mediachange(if_t ifp) iflib_link_state_change(aq_dev->ctx, LINK_STATE_DOWN, 0); break; - case IFM_100_TX: - hw->link_rate = aq_fw_100M; - media_rate = 100 * 1000; - break; - - case IFM_1000_T: - hw->link_rate = aq_fw_1G; - media_rate = 1000 * 1000; - break; - - case IFM_2500_T: - hw->link_rate = aq_fw_2G5; - media_rate = 2500 * 1000; - break; - - case IFM_5000_T: - hw->link_rate = aq_fw_5G; - media_rate = 5000 * 1000; - break; - - case IFM_10G_T: - hw->link_rate = aq_fw_10G; - media_rate = 10000 * 1000; + default: + for (i = 0; i < nitems(aq_media_types); i++) + if (user_media == aq_media_types[i].ifm) + break; + if (i == nitems(aq_media_types)) { + device_printf(hw->dev, "unknown media: 0x%X\n", + user_media); + return (0); + } + hw->link_rate = aq_media_types[i].fw_rate; + media_rate = (uint64_t)aq_media_types[i].mbps * 1000; break; - - default: // should never happen - device_printf(hw->dev, "unknown media: 0x%X\n", user_media); - return (0); } hw->fc.fc_rx = (ifm->ifm_media & IFM_ETH_RXPAUSE) ? 1 : 0; hw->fc.fc_tx = (ifm->ifm_media & IFM_ETH_TXPAUSE) ? 1 : 0; @@ -197,6 +185,8 @@ aq_add_media_types(struct aq_dev *aq_dev, int media_link_speed) void aq_initmedia(struct aq_dev *aq_dev) { + u_int i; + AQ_DBG_ENTER(); // ifconfig eth0 none @@ -205,16 +195,9 @@ aq_initmedia(struct aq_dev *aq_dev) ifmedia_add(aq_dev->media, IFM_ETHER | IFM_AUTO, 0, NULL); aq_add_media_types(aq_dev, IFM_AUTO); - if (AQ_HW_SUPPORT_SPEED(aq_dev, AQ_LINK_100M)) - aq_add_media_types(aq_dev, IFM_100_TX); - if (AQ_HW_SUPPORT_SPEED(aq_dev, AQ_LINK_1G)) - aq_add_media_types(aq_dev, IFM_1000_T); - if (AQ_HW_SUPPORT_SPEED(aq_dev, AQ_LINK_2G5)) - aq_add_media_types(aq_dev, IFM_2500_T); - if (AQ_HW_SUPPORT_SPEED(aq_dev, AQ_LINK_5G)) - aq_add_media_types(aq_dev, IFM_5000_T); - if (AQ_HW_SUPPORT_SPEED(aq_dev, AQ_LINK_10G)) - aq_add_media_types(aq_dev, IFM_10G_T); + for (i = 0; i < nitems(aq_media_types); i++) + if (aq_dev->link_speeds & aq_media_types[i].link_bit) + aq_add_media_types(aq_dev, aq_media_types[i].ifm); // link is initially autoselect ifmedia_set(aq_dev->media, diff --git a/sys/modules/aq/Makefile b/sys/modules/aq/Makefile index 603bb6770ff4..e03976d28364 100644 --- a/sys/modules/aq/Makefile +++ b/sys/modules/aq/Makefile @@ -3,7 +3,7 @@ KMOD = if_aq SRCS = aq_main.c aq_media.c aq_irq.c SRCS += aq_ring.c aq_hw.c aq_hw_llh.c -SRCS += aq_fw.c aq_fw1x.c aq_fw2x.c aq_dbg.c +SRCS += aq_fw.c aq_fw1x.c aq_fw2x.c aq2_fw.c aq_dbg.c SRCS += device_if.h bus_if.h pci_if.h SRCS += ifdi_if.h opt_inet.h opt_inet6.h opt_platform.h opt_rss.h SRCS += miibus_if.h |
