Page MenuHomeFreeBSD

D58435.id182905.diff
No OneTemporary

D58435.id182905.diff

diff --git a/sys/dev/aq/aq2_fw.c b/sys/dev/aq/aq2_fw.c
--- a/sys/dev/aq/aq2_fw.c
+++ b/sys/dev/aq/aq2_fw.c
@@ -42,6 +42,7 @@
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);
static int aq2_fw_get_temp(struct aq_hw *hw, int *temp_mc);
+static int aq2_fw_get_thermal_limit(struct aq_hw *hw, int *limit_mc);
/* Coherent OUT-window read, bracketed by the transaction id. */
static int
@@ -490,7 +491,7 @@
return (ENXIO);
}
- /* F/W reports whole degrees Celsius. */
+ /* F/W reports whole degrees Celsius, signed. */
*temp_mc = (int)(int8_t)((raw &
AQ2_FW_INTERFACE_OUT_PHY_TEMPERATURE) >>
AQ2_FW_INTERFACE_OUT_PHY_TEMPERATURE_S) * 1000;
@@ -498,6 +499,50 @@
return (0);
}
+/* interface-in thermal_shutdown.shutdown_temperature, whole degC. */
+static int
+aq2_fw_get_thermal_limit(struct aq_hw *hw, int *limit_mc)
+{
+ uint32_t v;
+ int temp_c;
+
+ v = AQ_READ_REG(hw, AQ2_FW_INTERFACE_IN_THERMAL_SHUTDOWN_REG);
+ temp_c = (v & AQ2_FW_INTERFACE_IN_THERMAL_SHUTDOWN_TEMP) >>
+ AQ2_FW_INTERFACE_IN_THERMAL_SHUTDOWN_TEMP_S;
+ if (temp_c == 0 || temp_c == 0xff)
+ return (ENXIO);
+ *limit_mc = temp_c * 1000;
+
+ return (0);
+}
+
+static int
+aq2_fw_get_phy_fault(struct aq_hw *hw, uint16_t *fault)
+{
+ uint32_t health, code;
+ int err;
+
+ err = aq2_fw_interface_buffer_read(hw,
+ AQ2_FW_INTERFACE_OUT_PHY_HEALTH_MONITOR_REG, &health,
+ sizeof(health));
+ if (err != 0)
+ return (err);
+
+ if ((health & AQ2_FW_INTERFACE_OUT_PHY_HEALTH_MONITOR_FAULT) == 0) {
+ *fault = 0;
+ return (0);
+ }
+
+ err = aq2_fw_interface_buffer_read(hw,
+ AQ2_FW_INTERFACE_OUT_PHY_FAULT_CODE_REG, &code, sizeof(code));
+ if (err != 0)
+ return (err);
+
+ *fault = (uint16_t)(code & AQ2_FW_INTERFACE_OUT_PHY_FAULT_CODE);
+
+ return (0);
+}
+
const struct aq_firmware_ops aq2_fw_ops = {
.reset = aq2_fw_reset,
.set_mode = aq2_fw_set_mode,
@@ -505,5 +550,9 @@
.get_mac_addr = aq2_fw_get_mac_addr,
.get_stats = aq2_fw_get_stats,
.get_temp = aq2_fw_get_temp,
+ .get_phy_fault = aq2_fw_get_phy_fault,
+ .phy_reset = NULL, /* A2 clears thermal shutdown on its own reset */
+ .thermal_arm = NULL, /* A2 firmware ships thermal shutdown armed */
+ .get_thermal_limit = aq2_fw_get_thermal_limit,
.led_control = NULL,
};
diff --git a/sys/dev/aq/aq2_hw.h b/sys/dev/aq/aq2_hw.h
--- a/sys/dev/aq/aq2_hw.h
+++ b/sys/dev/aq/aq2_hw.h
@@ -194,6 +194,11 @@
#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_THERMAL_SHUTDOWN_REG 0x12020
+#define AQ2_FW_INTERFACE_IN_THERMAL_SHUTDOWN_ENABLE (1 << 0)
+#define AQ2_FW_INTERFACE_IN_THERMAL_SHUTDOWN_TEMP 0x0000ff00 /* whole degC */
+#define AQ2_FW_INTERFACE_IN_THERMAL_SHUTDOWN_TEMP_S 8
+
#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
@@ -231,9 +236,13 @@
#define AQ2_FW_INTERFACE_OUT_PHY_HEALTH_MONITOR_REG 0x13620
#define AQ2_FW_INTERFACE_OUT_PHY_HEALTH_MONITOR_READY 0x00000001
+#define AQ2_FW_INTERFACE_OUT_PHY_HEALTH_MONITOR_FAULT 0x00000002
#define AQ2_FW_INTERFACE_OUT_PHY_TEMPERATURE 0x0000ff00
#define AQ2_FW_INTERFACE_OUT_PHY_TEMPERATURE_S 8
+#define AQ2_FW_INTERFACE_OUT_PHY_FAULT_CODE_REG 0x13624
+#define AQ2_FW_INTERFACE_OUT_PHY_FAULT_CODE 0x0000ffff
+
#define AQ2_FW_INTERFACE_OUT_STATS_REG 0x13700
#define AQ2_FW_INTERFACE_OUT_LINK_STATUS_REG 0x13014
@@ -260,6 +269,9 @@
#define AQ2_RPF_ACT_ART_REQ_MASK_REG(i) (0x14004 + (i) * 0x10)
#define AQ2_RPF_ACT_ART_REQ_ACTION_REG(i) (0x14008 + (i) * 0x10)
+/* Action-resolver table depth: 16 enable sections of 8 rows. */
+#define AQ2_ART_TABLE_SIZE 128
+
/*
* L2 unicast filter MSW register (shared with Atlantic 1). Atlantic 2 sets
* the TAG field so unicast frames are classified into the resolver table.
diff --git a/sys/dev/aq/aq_device.h b/sys/dev/aq/aq_device.h
--- a/sys/dev/aq/aq_device.h
+++ b/sys/dev/aq/aq_device.h
@@ -139,6 +139,16 @@
uint32_t tx_rings_count;
uint32_t rx_rings_count;
bool linkup;
+ uint16_t phy_fault_last; /* last fault code reported */
+ enum aq_thermal_state {
+ AQ_THERMAL_NORMAL = 0, /* no thermal shutdown pending */
+ AQ_THERMAL_COOLING, /* shut down; waiting to cool */
+ AQ_THERMAL_SETTLING, /* PHY reset; waiting to re-init */
+ } thermal_state;
+ int thermal_settle;
+ int thermal_retry_ticks; /* earliest tick to retry at */
+ int thermal_temp_mc; /* temp at the last shutdown/cool */
+ int thermal_recover_mc; /* recover once cooled to here */
int media_active;
struct aq_hw_stats last_stats;
diff --git a/sys/dev/aq/aq_fw.h b/sys/dev/aq/aq_fw.h
--- a/sys/dev/aq/aq_fw.h
+++ b/sys/dev/aq/aq_fw.h
@@ -71,9 +71,24 @@
/* Reports millidegrees Celsius. */
int (*get_temp)(struct aq_hw* hw, int* temp_mc);
+ /* Reports the PHY global fault code; zero means no fault. */
+ int (*get_phy_fault)(struct aq_hw* hw, uint16_t* fault);
+
+ /* Resets the PHY (clears a latched thermal shutdown). */
+ int (*phy_reset)(struct aq_hw* hw);
+
+ /* Arms the firmware autonomous thermal shutdown (A2 ships armed). */
+ int (*thermal_arm)(struct aq_hw* hw);
+
+ /* Reports the PHY high-temperature shutdown threshold (millidegrees C). */
+ int (*get_thermal_limit)(struct aq_hw* hw, int* limit_mc);
+
int (*led_control)(struct aq_hw* hw, uint32_t mode);
};
+/* PHY global fault codes, register 1E.C850. */
+#define AQ_PHY_FAULT_THERMAL_SHUTDOWN 0x8007
+
/* 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;
diff --git a/sys/dev/aq/aq_fw2x.c b/sys/dev/aq/aq_fw2x.c
--- a/sys/dev/aq/aq_fw2x.c
+++ b/sys/dev/aq/aq_fw2x.c
@@ -88,7 +88,7 @@
CAPS_HI_TEMPERATURE,
CAPS_HI_DOWNSHIFT,
CAPS_HI_PTP_AVB_EN,
- CAPS_HI_MEDIA_DETECT,
+ CAPS_HI_THERMAL_SHUTDOWN,
CAPS_HI_LINK_DROP,
CAPS_HI_SLEEP_PROXY,
CAPS_HI_WOL,
@@ -499,6 +499,167 @@
return (0);
}
+static int
+aq_fw2x_get_phy_fault(struct aq_hw* hw, uint16_t* fault)
+{
+ uint32_t raw;
+ int err;
+
+ mtx_lock(&hw->fw_mtx);
+ err = aq_hw_fw_downld_dwords(hw, hw->mbox_addr +
+ offsetof(struct aq_fw2x_mailbox, phy_h_bit), &raw, 1);
+ mtx_unlock(&hw->fw_mtx);
+
+ if (err != 0)
+ return (err);
+
+ *fault = (uint16_t)(raw >> 16);
+
+ return (0);
+}
+
+/* PHY MDIO access: MMD register read/write via the MAC's MDIO controller. */
+#define AQ_MDIO_IFACE(n) (0x280 + (((n) - 1) * 4))
+#define AQ_MDIO_BUSY 0x80000000u /* iface2 bit 31 */
+#define AQ_MDIO_EXECUTE 0x00008000u /* iface2 bit 15 */
+#define AQ_MDIO_OP_S 12 /* iface2 bits 13:12 */
+#define AQ_MDIO_OP_ADDR 3
+#define AQ_MDIO_OP_READ 1
+#define AQ_MDIO_OP_WRITE 2
+#define AQ_MDIO_PHYADDR_MSK 0x3ffu
+#define AQ_FW_SM_MDIO 0 /* cpu semaphore index */
+
+#define AQ_PHY_ID_MAX 32 /* MDIO port addresses to scan */
+#define AQ_MDIO_MMD_PMAPMD 0x01 /* PMA/PMD MMD */
+#define AQ_PHY_ID2_REG 0x0003 /* PMA/PMD Device Identifier 2 */
+
+#define AQ_PHY_MMD_GLOBAL 0x1e
+#define AQ_PHY_RESET_REG 0x2681 /* 1E.2681.0 = PHY hard reset */
+#define AQ_PHY_RESET 0x0001
+#define AQ_PHY_THERMAL_CTRL_REG 0xc478 /* 1E.C478 thermal control */
+#define AQ_PHY_THERMAL_SD_EN 0x0400 /* .A thermalShutdownEnable */
+
+static uint16_t
+aq_fw2x_mdio_op(struct aq_hw* hw, uint16_t mmd, uint16_t addr, int write,
+ uint16_t data)
+{
+ uint32_t pa = (((uint32_t)hw->phy_id & 0x1f) << 5) | (mmd & 0x1f);
+
+ AQ_WRITE_REG(hw, AQ_MDIO_IFACE(4), addr);
+ AQ_WRITE_REG(hw, AQ_MDIO_IFACE(2),
+ AQ_MDIO_EXECUTE | (AQ_MDIO_OP_ADDR << AQ_MDIO_OP_S) | pa);
+ AQ_HW_WAIT_FOR((AQ_READ_REG(hw, AQ_MDIO_IFACE(2)) & AQ_MDIO_BUSY) == 0,
+ 10, 10000);
+ if (write) {
+ AQ_WRITE_REG(hw, AQ_MDIO_IFACE(3), data);
+ AQ_WRITE_REG(hw, AQ_MDIO_IFACE(2),
+ AQ_MDIO_EXECUTE | (AQ_MDIO_OP_WRITE << AQ_MDIO_OP_S) | pa);
+ } else {
+ AQ_WRITE_REG(hw, AQ_MDIO_IFACE(2),
+ AQ_MDIO_EXECUTE | (AQ_MDIO_OP_READ << AQ_MDIO_OP_S) | pa);
+ }
+ AQ_HW_WAIT_FOR((AQ_READ_REG(hw, AQ_MDIO_IFACE(2)) & AQ_MDIO_BUSY) == 0,
+ 10, 10000);
+ return ((uint16_t)AQ_READ_REG(hw, AQ_MDIO_IFACE(5)));
+}
+
+/* MDIO is serialized against the F/W by cpu semaphore 0. */
+static void
+aq_fw2x_phy_write(struct aq_hw* hw, uint16_t mmd, uint16_t addr, uint16_t data)
+{
+ if (AQ_HW_WAIT_FOR(reg_glb_cpu_sem_get(hw, AQ_FW_SM_MDIO) == 1U,
+ 10, 10000) != 0)
+ return;
+ aq_fw2x_mdio_op(hw, mmd, addr, 1, data);
+ reg_glb_cpu_sem_set(hw, 1U, AQ_FW_SM_MDIO);
+}
+
+static int
+aq_fw2x_phy_read(struct aq_hw* hw, uint16_t mmd, uint16_t addr, uint16_t* val)
+{
+ if (AQ_HW_WAIT_FOR(reg_glb_cpu_sem_get(hw, AQ_FW_SM_MDIO) == 1U,
+ 10, 10000) != 0)
+ return (ETIMEDOUT);
+ *val = aq_fw2x_mdio_op(hw, mmd, addr, 0, 0);
+ reg_glb_cpu_sem_set(hw, 1U, AQ_FW_SM_MDIO);
+
+ return (0);
+}
+
+/* Discover the PHY's MDIO port address; it is strap-selectable, not fixed at 0. */
+static bool
+aq_fw2x_init_phy_id(struct aq_hw* hw)
+{
+ uint16_t val;
+ uint8_t id;
+
+ for (id = 0; id < AQ_PHY_ID_MAX; id++) {
+ hw->phy_id = id;
+ if (aq_fw2x_phy_read(hw, AQ_MDIO_MMD_PMAPMD, AQ_PHY_ID2_REG,
+ &val) == 0 && val != 0xffff)
+ return (true);
+ }
+ hw->phy_id = 0;
+ return (false);
+}
+
+/* Called with fw_mtx held; the port address is needed by every MDIO caller. */
+static void
+aq_fw2x_phy_id_probe(struct aq_hw* hw)
+{
+ if (!hw->phy_id_valid && aq_fw2x_init_phy_id(hw))
+ hw->phy_id_valid = true;
+}
+
+/* PHY hard reset (1E.2681.0): clears a latched thermal shutdown a MAC reset cannot. */
+static int
+aq_fw2x_phy_reset(struct aq_hw* hw)
+{
+ mtx_lock(&hw->fw_mtx);
+ aq_fw2x_phy_id_probe(hw);
+ aq_fw2x_phy_write(hw, AQ_PHY_MMD_GLOBAL, AQ_PHY_RESET_REG, AQ_PHY_RESET);
+ mtx_unlock(&hw->fw_mtx);
+
+ return (0);
+}
+
+/* Arm autonomous thermal shutdown (1E.C478.A), cleared by any PHY reset. */
+static int
+aq_fw2x_thermal_arm(struct aq_hw* hw)
+{
+ uint16_t ctrl;
+
+ mtx_lock(&hw->fw_mtx);
+ aq_fw2x_phy_id_probe(hw);
+ if (aq_fw2x_phy_read(hw, AQ_PHY_MMD_GLOBAL, AQ_PHY_THERMAL_CTRL_REG,
+ &ctrl) == 0 && ctrl != 0xffff && (ctrl & AQ_PHY_THERMAL_SD_EN) == 0)
+ aq_fw2x_phy_write(hw, AQ_PHY_MMD_GLOBAL,
+ AQ_PHY_THERMAL_CTRL_REG, ctrl | AQ_PHY_THERMAL_SD_EN);
+ mtx_unlock(&hw->fw_mtx);
+
+ return (0);
+}
+
+/* 1E.C421 high-temp shutdown threshold, degrees C in Q8.8 fixed point. */
+#define AQ_PHY_THERMAL_HIGH_REG 0xc421
+static int
+aq_fw2x_get_thermal_limit(struct aq_hw* hw, int* limit_mc)
+{
+ uint16_t raw;
+ int err;
+
+ mtx_lock(&hw->fw_mtx);
+ aq_fw2x_phy_id_probe(hw);
+ err = aq_fw2x_phy_read(hw, AQ_PHY_MMD_GLOBAL, AQ_PHY_THERMAL_HIGH_REG,
+ &raw);
+ mtx_unlock(&hw->fw_mtx);
+ if (err != 0 || raw == 0 || raw == 0xffff)
+ return (ENXIO);
+ *limit_mc = (int)(int16_t)raw * 1000 / 256;
+
+ return (0);
+}
+
static int
aq_fw2x_led_control(struct aq_hw* hw, uint32_t onoff)
{
@@ -526,6 +687,10 @@
.get_mac_addr = aq_fw2x_get_mac_addr,
.get_stats = aq_fw2x_get_stats,
.get_temp = aq_fw2x_get_temp,
+ .get_phy_fault = aq_fw2x_get_phy_fault,
+ .phy_reset = aq_fw2x_phy_reset,
+ .thermal_arm = aq_fw2x_thermal_arm,
+ .get_thermal_limit = aq_fw2x_get_thermal_limit,
.led_control = aq_fw2x_led_control,
};
diff --git a/sys/dev/aq/aq_hw.h b/sys/dev/aq/aq_hw.h
--- a/sys/dev/aq/aq_hw.h
+++ b/sys/dev/aq/aq_hw.h
@@ -195,6 +195,10 @@
uint32_t chip_features;
uint64_t fw_caps;
+ /* Atlantic 1: MDIO port address of the PHY, discovered once. */
+ uint8_t phy_id;
+ bool phy_id_valid;
+
bool lro_enabled;
uint32_t mbox_addr;
diff --git a/sys/dev/aq/aq_hw.c b/sys/dev/aq/aq_hw.c
--- a/sys/dev/aq/aq_hw.c
+++ b/sys/dev/aq/aq_hw.c
@@ -598,13 +598,20 @@
aq2_art_filter_set(struct aq_hw *hw, uint32_t idx, uint32_t tag,
uint32_t mask, uint32_t action)
{
+ idx += hw->art_filter_base_index;
+ if (idx >= AQ2_ART_TABLE_SIZE) {
+ device_printf(hw->dev,
+ "ART index %u out of range (firmware base %u)\n", idx,
+ hw->art_filter_base_index);
+ return (EINVAL);
+ }
+
if (AQ_HW_WAIT_FOR(reg_glb_cpu_sem_get(hw, AQ2_ART_SEM_INDEX) == 1U,
10U, 1000U) != 0) {
device_printf(hw->dev, "ART semaphore timeout, idx %u\n", idx);
return (EBUSY);
}
- idx += hw->art_filter_base_index;
AQ_WRITE_REG(hw, AQ2_RPF_ACT_ART_REQ_TAG_REG(idx), tag);
AQ_WRITE_REG(hw, AQ2_RPF_ACT_ART_REQ_MASK_REG(idx), mask);
AQ_WRITE_REG(hw, AQ2_RPF_ACT_ART_REQ_ACTION_REG(idx), action);
@@ -739,6 +746,10 @@
err = EINVAL;
goto err_exit;
}
+ if (index >= AQ_HW_MAC_MAX) {
+ err = EINVAL;
+ goto err_exit;
+ }
h = (mac_addr[0] << 8) | (mac_addr[1]);
l = (mac_addr[2] << 24) | (mac_addr[3] << 16) | (mac_addr[4] << 8) |
mac_addr[5];
diff --git a/sys/dev/aq/aq_irq.c b/sys/dev/aq/aq_irq.c
--- a/sys/dev/aq/aq_irq.c
+++ b/sys/dev/aq/aq_irq.c
@@ -51,6 +51,7 @@
#include "aq_dbg.h"
#include "aq_hw.h"
#include "aq_hw_llh.h"
+#include "aq_fw.h"
int
aq_update_hw_stats(struct aq_dev *aq_dev)
@@ -63,9 +64,7 @@
return (0);
#define AQ_SDELTA(_N_) do { \
- int32_t _d = (int32_t)(stats._N_ - aq_dev->last_stats._N_); \
- if (_d > 0) \
- aq_dev->curr_stats._N_ += _d; \
+ aq_dev->curr_stats._N_ += stats._N_ - aq_dev->last_stats._N_; \
} while (0)
if (aq_dev->linkup) {
AQ_SDELTA(uprc);
@@ -115,6 +114,100 @@
}
+#define AQ_THERMAL_HYSTERESIS_MC 18000 /* recover this far below the limit */
+#define AQ_THERMAL_RECOVER_MC 90000 /* fallback when the limit is unreadable */
+#define AQ_THERMAL_SETTLE_POLLS 5 /* ~5 s for the PHY reset to settle */
+#define AQ_THERMAL_RETRY_SECS 60 /* minimum spacing between recoveries */
+
+/* Temperature here is post-trip; the PHY is already dropping to low power. */
+static void
+aq_thermal_report_shutdown(struct aq_dev *aq_dev)
+{
+ struct aq_hw *hw = &aq_dev->hw;
+ int temp_mc, limit_mc;
+ bool have_temp, have_limit;
+
+ have_temp = hw->fw_ops->get_temp(hw, &temp_mc) == 0;
+ have_limit = hw->fw_ops->get_thermal_limit(hw, &limit_mc) == 0;
+ if (have_temp)
+ aq_dev->thermal_temp_mc = temp_mc;
+ /* The F/W also exposes a cold_temperature hysteresis point. */
+ aq_dev->thermal_recover_mc = have_limit ?
+ limit_mc - AQ_THERMAL_HYSTERESIS_MC : AQ_THERMAL_RECOVER_MC;
+
+ if (have_temp && have_limit)
+ device_printf(aq_dev->dev, "PHY thermal shutdown; "
+ "limit %d C, temp %d C; holding link down until it cools\n",
+ limit_mc / 1000, temp_mc / 1000);
+ else if (have_temp)
+ device_printf(aq_dev->dev, "PHY thermal shutdown; "
+ "temp %d C; holding link down until it cools\n",
+ temp_mc / 1000);
+ else
+ device_printf(aq_dev->dev, "PHY thermal shutdown; "
+ "holding link down until it cools\n");
+}
+
+/* Recover after cooldown: A1 needs a PHY reset then re-init, A2 re-inits alone. */
+static void
+aq_thermal_poll(struct aq_dev *aq_dev)
+{
+ struct aq_hw *hw = &aq_dev->hw;
+ uint16_t fault;
+ int temp_mc;
+
+ switch (aq_dev->thermal_state) {
+ case AQ_THERMAL_NORMAL:
+ if (aq_dev->linkup)
+ return;
+ /* The F/W raises the fault a poll after it drops the link. */
+ if (hw->fw_ops->get_phy_fault(hw, &fault) != 0 || fault == 0)
+ return;
+ /* Report each code once; do not mask a later shutdown. */
+ if (fault == aq_dev->phy_fault_last)
+ return;
+ aq_dev->phy_fault_last = fault;
+ if (fault != AQ_PHY_FAULT_THERMAL_SHUTDOWN) {
+ device_printf(aq_dev->dev,
+ "PHY fault 0x%04x\n", fault);
+ return;
+ }
+ aq_thermal_report_shutdown(aq_dev);
+ aq_dev->thermal_state = AQ_THERMAL_COOLING;
+ return;
+
+ case AQ_THERMAL_COOLING:
+ if (hw->fw_ops->get_temp(hw, &temp_mc) != 0 ||
+ temp_mc > aq_dev->thermal_recover_mc)
+ return;
+ aq_dev->thermal_temp_mc = temp_mc;
+ if (hw->fw_ops->phy_reset != NULL) {
+ hw->fw_ops->phy_reset(hw);
+ aq_dev->thermal_settle = 0;
+ aq_dev->thermal_state = AQ_THERMAL_SETTLING;
+ return;
+ }
+ /* No PHY reset needed (A2): re-init below restores the link. */
+ break;
+
+ case AQ_THERMAL_SETTLING:
+ if (++aq_dev->thermal_settle < AQ_THERMAL_SETTLE_POLLS)
+ return;
+ break;
+ }
+
+ /* Space attempts out: recovery costs a re-init and a renegotiation. */
+ if ((int)(ticks - aq_dev->thermal_retry_ticks) < 0)
+ return;
+ aq_dev->thermal_retry_ticks = ticks + AQ_THERMAL_RETRY_SECS * hz;
+
+ device_printf(aq_dev->dev, "PHY cooled to %d C; restoring "
+ "link\n", aq_dev->thermal_temp_mc / 1000);
+ aq_dev->thermal_state = AQ_THERMAL_NORMAL;
+ iflib_request_reset(aq_dev->ctx);
+ iflib_admin_intr_deferred(aq_dev->ctx);
+}
+
void
aq_if_update_admin_status(if_ctx_t ctx)
{
@@ -129,6 +222,7 @@
device_printf(aq_dev->dev, "atlantic: link UP: speed=%d\n", link_speed);
aq_dev->linkup = 1;
+ aq_dev->phy_fault_last = 0;
/* turn on/off RX Pause in RPB */
rpb_rx_xoff_en_per_tc_set(hw, fc_neg.fc_rx, 0);
@@ -151,6 +245,9 @@
aq_mediastatus_update(aq_dev, link_speed, &fc_neg);
}
+ if (hw->fw_ops->get_phy_fault != NULL)
+ aq_thermal_poll(aq_dev);
+
aq_update_hw_stats(aq_dev);
}
diff --git a/sys/dev/aq/aq_main.c b/sys/dev/aq/aq_main.c
--- a/sys/dev/aq/aq_main.c
+++ b/sys/dev/aq/aq_main.c
@@ -338,6 +338,8 @@
softc = iflib_get_softc(ctx);
rc = 0;
+ sysctl_ctx_init(&softc->aq_sysctl_ctx);
+
softc->ctx = ctx;
softc->dev = iflib_get_dev(ctx);
softc->media = iflib_get_media(ctx);
@@ -374,6 +376,8 @@
hw->fc.fc_rx = 1;
hw->fc.fc_tx = 1;
softc->linkup = 0U;
+ /* Set here, not in aq_if_init(): a recovery re-init must not reset it. */
+ softc->thermal_retry_ticks = ticks;
softc->dbg_level = AQ_DBG_LEVEL_DEFAULT;
softc->dbg_categories = AQ_DBG_CATEGORIES_DEFAULT;
@@ -745,6 +749,8 @@
atomic_store_rel_long(&hw->flags, 0);
+ softc->phy_fault_last = 0;
+ softc->thermal_state = AQ_THERMAL_NORMAL;
hw->tx_rings_count = softc->tx_rings_count;
err = aq_hw_init(&softc->hw, softc->hw.mac_addr, softc->msix,
@@ -755,6 +761,10 @@
return;
}
+ /* aq_hw_init reloads the PHY, resetting the thermal-shutdown arming. */
+ if (hw->fw_ops->thermal_arm != NULL)
+ hw->fw_ops->thermal_arm(hw);
+
aq_if_media_status(ctx, &ifmr);
aq_update_vlan_filters(softc);
@@ -864,7 +874,7 @@
struct aq_hw *hw = &softc->hw;
uint8_t *mac_addr = NULL;
- if (count == AQ_HW_MAC_MAX)
+ if (count >= AQ_HW_MAC_MAX - 1)
return (0);
mac_addr = LLADDR(dl);
@@ -1425,8 +1435,6 @@
#define QUEUE_NAME_LEN 32
char namebuf[QUEUE_NAME_LEN];
- /* Own these oids so aq_if_detach can drain and free them in order. */
- sysctl_ctx_init(ctx);
/* RSS configuration */
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "print_rss_config",
CTLTYPE_STRING | CTLFLAG_RD, softc, 0,

File Metadata

Mime Type
text/plain
Expires
Mon, Aug 3, 11:15 PM (3 h, 28 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35912505
Default Alt Text
D58435.id182905.diff (18 KB)

Event Timeline