Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F164652858
D58435.id182905.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
18 KB
Referenced Files
None
Subscribers
None
D58435.id182905.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D58435: aq(4): PHY thermal-shutdown handling and correctness fixes
Attached
Detach File
Event Timeline
Log In to Comment