Page MenuHomeFreeBSD

D59593.diff
No OneTemporary

D59593.diff

diff --git a/share/man/man4/aq.4 b/share/man/man4/aq.4
--- a/share/man/man4/aq.4
+++ b/share/man/man4/aq.4
@@ -3,7 +3,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.Dd August 15, 2026
+.Dd September 11, 2026
.Dt AQ 4
.Os
.Sh NAME
@@ -159,6 +159,24 @@
also reports the approximate distance to the fault in metres.
.El
.Pp
+The following variable controls the PHY thermal shutdown.
+It is present only on adapters whose firmware reports a PHY temperature
+and lets the driver arm the shutdown, and it may also be set as a
+.Xr loader.conf 5
+tunable:
+.Bl -tag -width indent
+.It Va thermal_shutdown.enabled
+Arm the PHY's autonomous thermal shutdown.
+Armed, the PHY drops the link when its die passes the threshold the
+board provisions; the driver reports the trip, waits for the PHY to
+cool, and brings the link back.
+The default, 1, arms the shutdown on every adapter that supports it,
+whether or not its firmware ships it armed; 0 disarms it on those same
+adapters.
+The driver applies the setting from its periodic status poll, normally
+within a second, and again after any reset of the adapter.
+.El
+.Pp
The following variables tune the driver's own tracing:
.Bl -tag -width indent
.It Va debug
@@ -176,4 +194,5 @@
.Xr if_bridge 4 ,
.Xr lagg 4 ,
.Xr miibus 4 ,
+.Xr loader.conf 5 ,
.Xr ifconfig 8
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
@@ -46,6 +46,7 @@
uint32_t *down);
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);
+static int aq2_fw_thermal_arm(struct aq_hw *hw, bool enable);
static int aq2_fw_get_phy_hot_warning(struct aq_hw *hw, bool *hot);
static int aq2_fw_cable_diag(struct aq_hw *hw, struct aq_hw_cable_diag *cd);
@@ -556,6 +557,38 @@
return (0);
}
+/* interface-in thermal_shutdown.enable; the F/W boots with it set. */
+static int
+aq2_fw_thermal_arm(struct aq_hw *hw, bool enable)
+{
+ uint32_t v, want;
+ int err, limit_mc;
+
+ /* No threshold means an unpopulated word or a departed adapter. */
+ err = aq2_fw_get_thermal_limit(hw, &limit_mc);
+ if (err != 0)
+ return (err);
+
+ mtx_lock(&hw->fw_mtx);
+ v = AQ_READ_REG(hw, AQ2_FW_INTERFACE_IN_THERMAL_SHUTDOWN_REG);
+ if (enable)
+ want = v | AQ2_FW_INTERFACE_IN_THERMAL_SHUTDOWN_ENABLE;
+ else
+ want = v & ~AQ2_FW_INTERFACE_IN_THERMAL_SHUTDOWN_ENABLE;
+ if (want != v) {
+ AQ_WRITE_REG(hw, AQ2_FW_INTERFACE_IN_THERMAL_SHUTDOWN_REG,
+ want);
+ err = aq2_fw_wait_shared_ack(hw);
+ /* Unacked: put the word back so the next poll retries. */
+ if (err != 0)
+ AQ_WRITE_REG(hw,
+ AQ2_FW_INTERFACE_IN_THERMAL_SHUTDOWN_REG, v);
+ }
+ mtx_unlock(&hw->fw_mtx);
+
+ return (err);
+}
+
/* interface-in thermal_shutdown.shutdown_temperature, whole degC. */
static int
aq2_fw_get_thermal_limit(struct aq_hw *hw, int *limit_mc)
@@ -737,7 +770,7 @@
.get_phy_hot_warning = aq2_fw_get_phy_hot_warning,
.cable_diag = aq2_fw_cable_diag,
.phy_reset = NULL, /* A2 clears thermal shutdown on its own reset */
- .thermal_arm = NULL, /* A2 firmware ships thermal shutdown armed */
+ .thermal_arm = aq2_fw_thermal_arm,
.get_thermal_limit = aq2_fw_get_thermal_limit,
.led_control = NULL,
/* Unimplemented: an A2 fibre part reports no SFP module diagnostics. */
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
@@ -142,6 +142,8 @@
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 */
+ bool thermal_shutdown_enabled; /* arm the shutdown */
+ int thermal_arm_err; /* last result returned */
bool init_failed; /* aq_hw_init() left the hw down */
int init_retries;
bool reset_pending; /* a re-init is already queued */
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
@@ -89,8 +89,8 @@
/* 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);
+ /* Arms or disarms the firmware autonomous thermal shutdown. */
+ int (*thermal_arm)(struct aq_hw* hw, bool enable);
/* Reports the PHY high-temperature shutdown threshold (millidegrees C). */
int (*get_thermal_limit)(struct aq_hw* hw, int* limit_mc);
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
@@ -756,11 +756,11 @@
return (err);
}
-/* Arm autonomous thermal shutdown (1E.C478.A), cleared by any PHY reset. */
+/* Arm/disarm thermal shutdown, cleared by any PHY reset. */
static int
-aq_fw2x_thermal_arm(struct aq_hw* hw)
+aq_fw2x_thermal_arm(struct aq_hw* hw, bool enable)
{
- uint16_t ctrl;
+ uint16_t ctrl, want;
int err;
if ((hw->fw_caps & FW2X_CAP_TEMPERATURE) == 0)
@@ -770,11 +770,21 @@
aq_fw2x_phy_id_probe(hw);
err = aq_fw2x_phy_read(hw, AQ_PHY_MMD_GLOBAL, AQ_PHY_THERMAL_CTRL_REG,
&ctrl);
- if (err == 0 && ctrl == 0xffff)
+ if (err != 0)
+ goto out;
+ /* All ones is no PHY answering, not a control word. */
+ if (ctrl == 0xffff) {
err = ENXIO;
- if (err == 0 && (ctrl & AQ_PHY_THERMAL_SD_EN) == 0)
+ goto out;
+ }
+ if (enable)
+ want = ctrl | AQ_PHY_THERMAL_SD_EN;
+ else
+ want = ctrl & ~AQ_PHY_THERMAL_SD_EN;
+ if (want != ctrl)
err = aq_fw2x_phy_write(hw, AQ_PHY_MMD_GLOBAL,
- AQ_PHY_THERMAL_CTRL_REG, ctrl | AQ_PHY_THERMAL_SD_EN);
+ AQ_PHY_THERMAL_CTRL_REG, want);
+out:
mtx_unlock(&hw->fw_mtx);
return (err);
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
@@ -178,6 +178,35 @@
device_printf(aq_dev->dev, "PHY over-temperature warning\n");
}
+/* reconcile thermal arm with knob value */
+static void
+aq_thermal_apply(struct aq_dev *aq_dev)
+{
+ struct aq_hw *hw = &aq_dev->hw;
+ bool enable;
+ int err;
+
+ /* A queued re-init would undo the write; the poll after it applies. */
+ if (hw->fw_ops->thermal_arm == NULL || aq_dev->reset_pending)
+ return;
+
+ enable = aq_dev->thermal_shutdown_enabled;
+ err = hw->fw_ops->thermal_arm(hw, enable);
+ /* No sensor to arm against; the knob is absent on such an adapter. */
+ if (err == ENOTSUP)
+ return;
+ /* Print only on a change, so a lasting failure is reported once. */
+ if (err == aq_dev->thermal_arm_err)
+ return;
+ aq_dev->thermal_arm_err = err;
+ if (err != 0)
+ device_printf(aq_dev->dev, "could not %s PHY thermal shutdown, "
+ "error %d\n", enable ? "arm" : "disarm", err);
+ else
+ device_printf(aq_dev->dev, "PHY thermal shutdown %s\n",
+ enable ? "armed" : "disarmed");
+}
+
/* 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)
@@ -192,6 +221,7 @@
switch (aq_dev->thermal_state) {
case AQ_THERMAL_NORMAL:
+ aq_thermal_apply(aq_dev);
aq_thermal_report_hot(aq_dev);
if (aq_dev->linkup)
return;
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
@@ -394,6 +394,7 @@
hw->fc.fc_rx = 1;
hw->fc.fc_tx = 1;
softc->linkup = 0U;
+ softc->thermal_shutdown_enabled = true;
/* Set here, not in aq_if_init(): a recovery re-init must not reset it. */
softc->thermal_retry_ticks = ticks;
@@ -799,14 +800,6 @@
softc->init_failed = false;
softc->init_retries = 0;
- /* aq_hw_init reloads the PHY, resetting the thermal-shutdown arming. */
- if (hw->fw_ops->thermal_arm != NULL) {
- err = hw->fw_ops->thermal_arm(hw);
- if (err != 0 && err != ENOTSUP)
- device_printf(softc->dev,
- "could not arm PHY thermal shutdown\n");
- }
-
aq_if_media_status(ctx, &ifmr);
aq_update_vlan_filters(softc);
@@ -1715,7 +1708,7 @@
struct sysctl_oid *tree = device_get_sysctl_tree(dev);
struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree);
struct aq_stats *stats = &softc->curr_stats;
- struct sysctl_oid *stat_node, *queue_node;
+ struct sysctl_oid *stat_node, *queue_node, *thermal_node;
struct sysctl_oid_list *stat_list, *queue_list;
uint32_t link_up, link_down;
int temp_mc;
@@ -1737,10 +1730,21 @@
/* ENOTSUP means no sensor; other errors may just be a cold PHY. */
if (softc->hw.fw_ops != NULL && softc->hw.fw_ops->get_temp != NULL &&
- softc->hw.fw_ops->get_temp(&softc->hw, &temp_mc) != ENOTSUP)
+ softc->hw.fw_ops->get_temp(&softc->hw, &temp_mc) != ENOTSUP) {
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "temperature",
CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, softc, 0,
aq_sysctl_temperature, "IK", "PHY temperature");
+ /* Without the sensor there is no shutdown to arm. */
+ if (softc->hw.fw_ops->thermal_arm != NULL) {
+ thermal_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO,
+ "thermal_shutdown", CTLFLAG_RD | CTLFLAG_MPSAFE,
+ NULL, "PHY thermal shutdown");
+ SYSCTL_ADD_BOOL(ctx, SYSCTL_CHILDREN(thermal_node),
+ OID_AUTO, "enabled", CTLFLAG_RWTUN,
+ &softc->thermal_shutdown_enabled, 0,
+ "Arm the PHY's autonomous thermal shutdown");
+ }
+ }
/* A cage has no twisted pairs to reflect off, whatever the F/W says. */
if (softc->media_type == AQ_MEDIA_TYPE_TP &&

File Metadata

Mime Type
text/plain
Expires
Sat, Sep 12, 8:57 PM (17 h, 49 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38818697
Default Alt Text
D59593.diff (9 KB)

Event Timeline