AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/29 00:47 flow patch-triage
1m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch adds a new driver for the Linear Technology LTC4266 Power Sourcing Equipment (PSE) controller over I2C (drivers/net/pse-pd/ltc4266.c) along with its Device Tree bindings. This driver requires physical LTC4266 hardware on an I2C bus with an assigned interrupt and devicetree description, which is not present or emulated in standard virtualized environments (QEMU/GCE on amd64). As such, the code is structurally unreachable for fuzzing.",
  "WorthFuzzing": false
}

1/1 2026/09/29 00:47 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 3cdeae96b3e0ba727e8ac5c36046dfc4901af5e3\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Tue Sep 29 00:47:24 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/Documentation/devicetree/bindings/net/pse-pd/lltc,ltc4266.yaml b/Documentation/devicetree/bindings/net/pse-pd/lltc,ltc4266.yaml\nnew file mode 100644\nindex 0000000000000..e11c9d601eee9\n--- /dev/null\n+++ b/Documentation/devicetree/bindings/net/pse-pd/lltc,ltc4266.yaml\n@@ -0,0 +1,180 @@\n+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)\n+%YAML 1.2\n+---\n+$id: http://devicetree.org/schemas/net/pse-pd/lltc,ltc4266.yaml#\n+$schema: http://devicetree.org/meta-schemas/core.yaml#\n+\n+title: Linear Technology LTC4266 Power Sourcing Equipment controller\n+\n+maintainers:\n+  - Kyle Swenson \u003ckyle.swenson@est.tech\u003e\n+\n+allOf:\n+  - $ref: pse-controller.yaml#\n+\n+properties:\n+  compatible:\n+    enum:\n+      - lltc,ltc4266\n+\n+  reg:\n+    maxItems: 1\n+\n+  interrupts:\n+    maxItems: 1\n+\n+  channels:\n+    type: object\n+    additionalProperties: false\n+    description:\n+      Defines the 4 physical delivery channels on the controller that can be\n+      referenced by PSE PIs through their \"pairsets\" property. The actual port\n+      matrix mapping is created when PSE PIs reference these channels in their\n+      pairsets.\n+\n+    properties:\n+      '#address-cells':\n+        const: 1\n+\n+      '#size-cells':\n+        const: 0\n+\n+    patternProperties:\n+      '^channel@[0-3]$':\n+        type: object\n+        additionalProperties: false\n+\n+        properties:\n+          reg:\n+            maxItems: 1\n+\n+          sense-resistor-micro-ohms:\n+            description: Sense resistor connected to the channel's MOSFET, used\n+              for current measurement and for overcurrent detection. The I_CUT\n+              and I_LIM register encodings depend on which value is fitted, so\n+              a wrong value programs the wrong thresholds.\n+            enum: [250000, 500000]\n+\n+        required:\n+          - reg\n+          - sense-resistor-micro-ohms\n+\n+    required:\n+      - '#address-cells'\n+      - '#size-cells'\n+\n+  pse-pis:\n+    type: object\n+    additionalProperties: false\n+\n+    properties:\n+      '#address-cells':\n+        const: 1\n+\n+      '#size-cells':\n+        const: 0\n+\n+    patternProperties:\n+      '^pse-pi@[0-3]$':\n+        type: object\n+        additionalProperties: true\n+        properties:\n+          pairsets:\n+            description: The LTC4266 delivers power to a PI over a single\n+              pairset, driven by one of the controller's four channels. There\n+              is no 4-pair mode spreading a PI over two channels, so exactly\n+              one channel phandle is expected.\n+            maxItems: 1\n+          pairset-names:\n+            maxItems: 1\n+\n+required:\n+  - compatible\n+  - reg\n+  - interrupts\n+  - channels\n+  - pse-pis\n+\n+unevaluatedProperties: false\n+\n+examples:\n+  - |\n+    #include \u003cdt-bindings/interrupt-controller/irq.h\u003e\n+\n+    i2c {\n+      #address-cells = \u003c1\u003e;\n+      #size-cells = \u003c0\u003e;\n+\n+      ethernet-pse@2f {\n+        compatible = \"lltc,ltc4266\";\n+        reg = \u003c0x2f\u003e;\n+        interrupts = \u003c8 IRQ_TYPE_LEVEL_LOW\u003e;\n+        interrupt-parent = \u003c\u0026gpio\u003e;\n+\n+        channels {\n+          #address-cells = \u003c1\u003e;\n+          #size-cells = \u003c0\u003e;\n+\n+          phys0: channel@0 {\n+            reg = \u003c0\u003e;\n+            sense-resistor-micro-ohms = \u003c500000\u003e;\n+          };\n+\n+          phys1: channel@1 {\n+            reg = \u003c1\u003e;\n+            sense-resistor-micro-ohms = \u003c500000\u003e;\n+          };\n+\n+          phys2: channel@2 {\n+            reg = \u003c2\u003e;\n+            sense-resistor-micro-ohms = \u003c500000\u003e;\n+          };\n+\n+          phys3: channel@3 {\n+            reg = \u003c3\u003e;\n+            sense-resistor-micro-ohms = \u003c500000\u003e;\n+          };\n+        };\n+\n+        pse-pis {\n+          #address-cells = \u003c1\u003e;\n+          #size-cells = \u003c0\u003e;\n+\n+          pse_pi0: pse-pi@0 {\n+            reg = \u003c0\u003e;\n+            #pse-cells = \u003c0\u003e;\n+            pairset-names = \"alternative-a\";\n+            pairsets = \u003c\u0026phys0\u003e;\n+            polarity-supported = \"MDI\";\n+            vpwr-supply = \u003c\u0026vreg_pse\u003e;\n+          };\n+\n+          pse_pi1: pse-pi@1 {\n+            reg = \u003c1\u003e;\n+            #pse-cells = \u003c0\u003e;\n+            pairset-names = \"alternative-a\";\n+            pairsets = \u003c\u0026phys1\u003e;\n+            polarity-supported = \"MDI\";\n+            vpwr-supply = \u003c\u0026vreg_pse\u003e;\n+          };\n+\n+          pse_pi2: pse-pi@2 {\n+            reg = \u003c2\u003e;\n+            #pse-cells = \u003c0\u003e;\n+            pairset-names = \"alternative-a\";\n+            pairsets = \u003c\u0026phys2\u003e;\n+            polarity-supported = \"MDI\";\n+            vpwr-supply = \u003c\u0026vreg_pse\u003e;\n+          };\n+\n+          pse_pi3: pse-pi@3 {\n+            reg = \u003c3\u003e;\n+            #pse-cells = \u003c0\u003e;\n+            pairset-names = \"alternative-a\";\n+            pairsets = \u003c\u0026phys3\u003e;\n+            polarity-supported = \"MDI\";\n+            vpwr-supply = \u003c\u0026vreg_pse\u003e;\n+          };\n+        };\n+      };\n+    };\ndiff --git a/MAINTAINERS b/MAINTAINERS\nindex 6de1ff058db64..3f7226d049839 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -15533,6 +15533,13 @@ W:\thttps://ez.analog.com/linux-software-drivers\n F:\tDocumentation/devicetree/bindings/leds/adi,ltc3220.yaml\n F:\tdrivers/leds/leds-ltc3220.c\n \n+LTC4266 PSE CONTROLLER DRIVER\n+M:\tKyle Swenson \u003ckyle.swenson@est.tech\u003e\n+L:\tnetdev@vger.kernel.org\n+S:\tMaintained\n+F:\tDocumentation/devicetree/bindings/net/pse-pd/lltc,ltc4266.yaml\n+F:\tdrivers/net/pse-pd/ltc4266.c\n+\n LTC4282 HARDWARE MONITOR DRIVER\n M:\tNuno Sa \u003cnuno.sa@analog.com\u003e\n L:\tlinux-hwmon@vger.kernel.org\ndiff --git a/drivers/net/pse-pd/Kconfig b/drivers/net/pse-pd/Kconfig\nindex a0f2ae668c67c..844c1b237df7b 100644\n--- a/drivers/net/pse-pd/Kconfig\n+++ b/drivers/net/pse-pd/Kconfig\n@@ -48,6 +48,17 @@ config PSE_REGULATOR\n \t  Sourcing Equipment without automatic classification support. For\n \t  example for basic implementation of PoDL (802.3bu) specification.\n \n+config PSE_LTC4266\n+\ttristate \"LTC4266 PSE controller\"\n+\tdepends on I2C\n+\tselect REGMAP_I2C\n+\thelp\n+\t  This module provides support for LTC4266 regulator based Ethernet\n+\t  Power Sourcing Equipment.\n+\n+\t  To compile this driver as a module, choose M here: the\n+\t  module will be called ltc4266.\n+\n config PSE_PD692X0\n \ttristate \"PD692X0 PSE controller\"\n \tdepends on I2C\ndiff --git a/drivers/net/pse-pd/Makefile b/drivers/net/pse-pd/Makefile\nindex 9cca5900fe342..c3fe4efcf1de4 100644\n--- a/drivers/net/pse-pd/Makefile\n+++ b/drivers/net/pse-pd/Makefile\n@@ -3,6 +3,7 @@\n \n obj-$(CONFIG_PSE_CONTROLLER) += pse_core.o\n \n+obj-$(CONFIG_PSE_LTC4266) += ltc4266.o\n obj-$(CONFIG_PSE_REALTEK_MCU) += realtek-pse-mcu-core.o\n obj-$(CONFIG_PSE_REALTEK_MCU_I2C) += realtek-pse-mcu-i2c.o\n obj-$(CONFIG_PSE_REALTEK_MCU_UART) += realtek-pse-mcu-uart.o\ndiff --git a/drivers/net/pse-pd/ltc4266.c b/drivers/net/pse-pd/ltc4266.c\nnew file mode 100644\nindex 0000000000000..37dea467811a4\n--- /dev/null\n+++ b/drivers/net/pse-pd/ltc4266.c\n@@ -0,0 +1,1386 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+/*\n+ * Driver for Linear LTC4266 PoE PSE Controller\n+ *\n+ * Original work:\n+ *    Copyright 2019 Cradlepoint Technology, Inc.\n+ *    Cradlepoint Technology, Inc.  \u003csource@cradlepoint.com\u003e\n+ *\n+ * Re-written in 2026:\n+ *    Copyright 2026 Ericsson Software Technology\n+ *    Kyle Swenson \u003ckyle.swenson@est.tech\u003e\n+ *\n+ */\n+\n+#include \u003clinux/bitfield.h\u003e\n+#include \u003clinux/bits.h\u003e\n+#include \u003clinux/delay.h\u003e\n+#include \u003clinux/device.h\u003e\n+#include \u003clinux/errno.h\u003e\n+#include \u003clinux/ethtool.h\u003e\n+#include \u003clinux/i2c.h\u003e\n+#include \u003clinux/interrupt.h\u003e\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/math.h\u003e\n+#include \u003clinux/module.h\u003e\n+#include \u003clinux/of.h\u003e\n+#include \u003clinux/pse-pd/pse.h\u003e\n+#include \u003clinux/regmap.h\u003e\n+#include \u003clinux/regulator/consumer.h\u003e\n+#include \u003clinux/slab.h\u003e\n+\n+#define LTC4266_MAX_PORTS\t\t\t4\n+\n+/* The minimum and maximum here depend on the resolution of the I_CUT field,\n+ * which is 18.75mA.  To get 1000mW with a 50V port voltage, we need 20mA; but\n+ * with 18.75 mA steps we end up with a current limit of 37.5 mA resulting in\n+ * an actual power limit of 1875mW.\n+ */\n+#define LTC4266_PW_LIMIT_MAX\t\t\t30000\n+#define LTC4266_PW_LIMIT_MIN\t\t\t1000\n+\n+/* Nominal PI voltage reported when the port is not delivering power and no\n+ * \"vpwr-supply\" is described in the device tree: V_Port_PSE min for a Type 2\n+ * PSE. IEEE 802.3-2022 Table 33-11 item 1 specifies the POWER_ON state output\n+ * voltage as 50.0 V to 57.0 V for a Type 2 PSE, 44.0 V to 57.0 V for Type 1.\n+ */\n+#define LTC4266_VPORT_NOMINAL_UV\t\t50000000\n+\n+/* Register definitions */\n+#define LTC4266_REG_INTSTAT\t\t\t0x00\n+#define LTC4266_REG_INTMASK\t\t\t0x01\n+#define LTC4266_REG_PWREVN_COR\t\t\t0x03\n+#define LTC4266_REG_DETEVN_COR\t\t\t0x05\n+#define LTC4266_REG_FLTEVN_COR\t\t\t0x07\n+#define LTC4266_REG_TSEVN_COR\t\t\t0x09\n+#define LTC4266_REG_SUPEVN_COR\t\t\t0x0B\n+#define LTC4266_REG_STAT(_p)\t\t\t(0x0C + (_p))\n+#define LTC4266_REG_STATPWR\t\t\t0x10\n+#define LTC4266_REG_OPMD\t\t\t0x12\n+#define LTC4266_REG_DISENA\t\t\t0x13 /* Disconnect detect enable */\n+#define LTC4266_REG_MCONF\t\t\t0x17\n+#define LTC4266_REG_DETPB\t\t\t0x18 /*PB means \"push button\" */\n+#define LTC4266_REG_PWRPB\t\t\t0x19\n+#define LTC4266_REG_RSTPB\t\t\t0x1A\n+#define LTC4266_REG_ID\t\t\t\t0x1B\n+#define LTC4266_REG_TLIM12\t\t\t0x1E\n+#define LTC4266_REG_TLIM34\t\t\t0x1F\n+#define LTC4266_REG_IPLSB(_p)\t\t\t(0x30 | ((_p) \u003c\u003c 2))\n+#define LTC4266_REG_VPLSB(_p)\t\t\t(LTC4266_REG_IPLSB(_p) + 2)\n+#define LTC4266_REG_HPEN\t\t\t0x44\n+#define LTC4266_REG_HPMD(_p)\t\t\t(0x46 + (5 * (_p)))\n+#define LTC4266_REG_ICUT_HP(_p)\t\t\t(LTC4266_REG_HPMD(_p) + 1)\n+#define LTC4266_REG_ILIM(_p)\t\t\t(LTC4266_REG_HPMD(_p) + 2)\n+\n+/* Register field definitions */\n+\n+/* LTC4266_REG_INTSTAT and LTC4266_REG_INTMASK */\n+#define LTC4266_INT_TSTART\t\t\tBIT(6)\n+#define LTC4266_INT_TCUT\t\t\tBIT(5)\n+#define LTC4266_INT_CLASS\t\t\tBIT(4)\n+#define LTC4266_INT_DETECT\t\t\tBIT(3)\n+#define LTC4266_INT_DIS\t\t\t\tBIT(2)\n+#define LTC4266_INT_PWRGD\t\t\tBIT(1)\n+\n+/* Per-port event bits within the CoR event registers. Every per-port event\n+ * register splits its 8 bits into a per-port low nibble and a per-port high\n+ * nibble:\n+ * pwrevn (03h): LO = power on/off change, HI = power good change\n+ * detevn (05h): LO = detection complete, HI = classification complete\n+ * fltevn (07h): LO = tCUT overcurrent,   HI = tDIS DC disconnect\n+ * tsevn  (09h): LO = tSTART overcurrent, HI = tLIM current-limit timeout\n+ */\n+#define LTC4266_EVN_HI(_p)\t\t\tBIT((_p) + 4)\t/* ports 0-3 */\n+#define LTC4266_EVN_LO(_p)\t\t\tBIT(_p)\t\t/* ports 0-3 */\n+\n+/* statp\u003cn\u003e (0Ch-0Fh) detection result, and the \"Signature Good\" value */\n+#define LTC4266_PORT_CLASS(_stat)\t\tFIELD_GET(GENMASK(6, 4), (_stat))\n+#define LTC4266_PORT_DETECT(_stat)\t\tFIELD_GET(GENMASK(2, 0), (_stat))\n+#define LTC4266_DETECT_GOOD\t\t\t0x4\n+\n+/* LTC4266_REG_STATPWR */\n+#define LTC4266_STATPWR_PG(_p)\t\t\tBIT((_p) + 4)\n+#define LTC4266_STATPWR_PE(_p)\t\t\tBIT(_p)\n+\n+/* LTC4266_REG_OPMD\n+ * There are three other operation modes possible this\n+ * driver doesn't support and so aren't defined.  The one supported mode,\n+ * OPMD_SEMI, means that a port will continuously detect and classify devices,\n+ * but will not power the device until instructed to do so.\n+ */\n+#define LTC4266_OPMD_SEMI\t\t\t2\n+#define LTC4266_TWO_BIT_WORD_OFFSET(_v, _p)\t((_v) \u003c\u003c ((_p) * 2))\n+#define LTC4266_TWO_BIT_WORD_MASK(_p)\t\tLTC4266_TWO_BIT_WORD_OFFSET(0x03, (_p))\n+\n+/* LTC4266_REG_MCONF */\n+#define LTC4266_MCONF_INTERRUPT_ENABLE\t\tBIT(7)\n+/* Only report a detect event when the result changes, not every cycle */\n+#define LTC4266_MCONF_DETCHG\t\t\tBIT(6)\n+\n+/* LTC4266_REG_DETPB */\n+#define LTC4266_DETPB_CLASS_ENABLE(_p)\t\tBIT((_p) + 4)\n+#define LTC4266_DETPB_DETECT_ENABLE(_p)\t\tBIT(_p)\n+\n+/* LTC4266_REG_RSTPB */\n+#define LTC4266_RSTPB_INTCLR\t\t\tBIT(7)\n+#define LTC4266_RSTPB_PINCLR\t\t\tBIT(6)\n+#define LTC4266_RSTPB_RSTALL\t\t\tBIT(4)\n+#define LTC4266_RSTPB_RSTPORTS\t\t\tGENMASK(3, 0)\n+\n+/* LTC4266_REG_ID */\n+#define LTC4266_ID\t\t\t\t0x64\n+\n+/* LTC4266_REG_TLIM* */\n+#define LTC4266_TLIM_VALUE\t\t\t0x01\n+\n+/* Current-sense scaling, in nA per LSB, dependent on the sense resistor. */\n+#define LTC4266_IP_NA_PER_LSB_RSENSE_025\t122070\t/* 122.07 uA/LSB */\n+#define LTC4266_IP_NA_PER_LSB_RSENSE_050\t61035\t/* 61.035 uA/LSB */\n+\n+/* Voltage-sense scaling: 5.835 mV == 5835 uV per LSB. */\n+#define LTC4266_VP_UV_PER_LSB\t\t\t5835\n+\n+/* LTC4266_REG_HPEN, enable \"High Power\" mode (Type 2, Class 4) */\n+#define LTC4266_HPEN(_p)\t\t\tBIT(_p)\n+\n+/* LTC4266_REG_HPMD */\n+#define LTC4266_HPMD_PONGEN\t\t\t0x01\n+\n+/* LTC4266_REG_ICUT_HP.\n+ * Set if the sense resistor specified in DT is 0.25 Ohm to have accurately\n+ * scaled ICUT thresholds.\n+ */\n+#define LTC4266_ICUT_RSENSE_025_OHM\t\tBIT(7)\n+\n+/* To keep the ICUT resolution at a constant 18.75 mA, for the 0.25 Ohm sense\n+ * case we should also set this ICUT_RANGE\n+ */\n+#define LTC4266_ICUT_RANGE\t\t\tBIT(6)\n+\n+/* I_CUT is programmed in a 6-bit field; each step is 18.75 mA (18750 uA). */\n+#define LTC4266_ICUT_STEP_UA\t\t\t18750\n+#define LTC4266_ICUT_MASK\t\t\tGENMASK(5, 0)\n+\n+/* Cap I_CUT at the suggested value for a Type 2 PD at 638mA */\n+#define LTC4266_ICUT_MAX_MA\t\t\t638\n+#define LTC4266_ICUT_MAX_STEPS\t\t\t34\n+\n+/* Recommended lim\u003cn\u003e settings from datasheet Table 5.\n+ *\n+ *\tI_LIM (mA)\tRSENSE = 0.5 Ohm\tRSENSE = 0.25 Ohm\n+ *\t425 (Type 1)\t0x00\t\t\t0x80\n+ *\t850 (Type 2)\t0x40\t\t\t0xC0\n+ */\n+#define LTC4266_ILIM_TYPE1_RSENSE_050\t\t0x00\n+#define LTC4266_ILIM_TYPE1_RSENSE_025\t\t0x80\n+#define LTC4266_ILIM_TYPE2_RSENSE_050\t\t0x40\n+#define LTC4266_ILIM_TYPE2_RSENSE_025\t\t0xC0\n+\n+enum {\n+\tLTC4266_READ_CURRENT = 0,\n+\tLTC4266_READ_VOLTAGE = 2\n+};\n+\n+/* Map LTC4266 Classification result to PD class.  Note for a PD that has a\n+ * valid detect signature, but doesn't produce a classification signature is\n+ * still a valid PD.  The LTC4266 indicates this with 0x06 in the statp\u003cn\u003e\n+ * register and calls it \"Class 0\".  This is a different state than when\n+ * statp\u003cn\u003e indicates 0, which means classification isn't complete.  This maps\n+ * the result to either an errno or classification value suitable for use up\n+ * the stack.\n+ */\n+static const int ltc4266_class_map[] = {\n+\t-EAGAIN, /* Classification is incomplete */\n+\t1,\n+\t2,\n+\t3,\n+\t4,\n+\t-EINVAL,\n+\t0,\n+\t-ERANGE\n+};\n+\n+/* Map a PD Class to I_CUT thresholds from the LTC4266 datasheet Table 2 */\n+static const int ltc4266_class_to_icut[] = {\n+\t375,\n+\t112,\n+\t206,\n+\t375,\n+\t638\n+};\n+\n+/* Per-class power budget at the PSE PI in mW, indexed by class (0-4).\n+ * Classes 0-3 are the P_Class values from Table 33-7 in the IEEE802.3\n+ * standard, which Note 1 defines as the minimum power a PSE must supply at the\n+ * PI for that class (the PD input power maxima are in Table 33-18). Class 4 is\n+ * P_Type, which Table 33-11 item 12 defines as I_Cable x V_Port_PSE min:\n+ * 0.600 A x 50.0 V for a Type 2 PSE, with I_Cable from Table 33-1.\n+ *\n+ * Class 0 and Class 3 have the same value because a device without a classification\n+ * signature (Class 0) has to be assumed to consume up to the maximum power for\n+ * a Type 1 PD (Class 3).\n+ */\n+static const int ltc4266_class_pw[] = {\n+\t15400,\t/* Class 0 */\n+\t4000,\t/* Class 1 */\n+\t7000,\t/* Class 2 */\n+\t15400,\t/* Class 3 */\n+\t30000,\t/* Class 4 (Type 2) */\n+};\n+\n+enum sense_resistor {\n+\tLTC4266_RSENSE_500, /* Rsense 0.5 Ohm */\n+\tLTC4266_RSENSE_250 /* Rsense 0.25 Ohm */\n+};\n+\n+struct ltc4266;\n+\n+/**\n+ * struct ltc4266_port - per-PSE-PI context\n+ *\n+ * @ltc4266: the controller owning this port\n+ * @chan: index of the LTC4266 delivery channel backing this PI, established\n+ *\t  from the PI pairset phandle by ltc4266_map_pis().  All register\n+ *\t  addressing uses this member.\n+ * @rsense: sense resistor on @chan, used to scale current readings and\n+ *\t    to pick the I_CUT and I_LIM encodings.\n+ * @pw_limit: Admin-configured power limit in mW, always valid: it defaults to\n+ *\t      LTC4266_PW_LIMIT_MAX, the most this controller can deliver, until\n+ *\t      pi_set_pw_limit() lowers it.\n+ * @vpwr_uv: nominal PI voltage in uV, read once from the PI's \"vpwr-supply\" at\n+ *\t     probe, or LTC4266_VPORT_NOMINAL_UV when the PI does not describe\n+ *\t     one. Reported while the port is not delivering power, when the\n+ *\t     controller has nothing to measure.\n+ */\n+struct ltc4266_port {\n+\tstruct ltc4266 *ltc4266;\n+\tu8 chan;\n+\tenum sense_resistor rsense;\n+\tint pw_limit;\n+\tint vpwr_uv;\n+};\n+\n+/**\n+ * struct ltc4266 - LTC4266 controller context\n+ *\n+ * @client: the I2C client\n+ * @regmap: register map of @client\n+ * @ports: table of PSE PI contexts, indexed by PSE PI id. A NULL entry is a PI\n+ *\t   that is not described in the device tree and therefore has no\n+ *\t   channel mapped.\n+ * @dev: the underlying device\n+ * @np: device node of @dev\n+ * @pcdev: the PSE controller registered with the PSE core\n+ */\n+struct ltc4266 {\n+\tstruct i2c_client *client;\n+\tstruct regmap *regmap;\n+\tstruct ltc4266_port *ports[LTC4266_MAX_PORTS];\n+\tstruct device *dev;\n+\tstruct device_node *np;\n+\tstruct pse_controller_dev pcdev;\n+};\n+\n+static struct ltc4266_port *ltc4266_pi_port(struct pse_controller_dev *pcdev,\n+\t\t\t\t\t    int id)\n+{\n+\tstruct ltc4266 *ltc4266 = container_of(pcdev, struct ltc4266, pcdev);\n+\n+\treturn ltc4266-\u003eports[id];\n+}\n+\n+static int ltc4266_read_iv(struct ltc4266_port *port, u8 iv)\n+{\n+\tstruct ltc4266 *ltc4266 = port-\u003eltc4266;\n+\tunsigned int lsb, msb;\n+\tunsigned int statpwr;\n+\tint lsb_reg;\n+\tint result;\n+\tu64 ivbits;\n+\n+\tresult = regmap_read(ltc4266-\u003eregmap, LTC4266_REG_STATPWR, \u0026statpwr);\n+\tif (result \u003c 0)\n+\t\treturn result;\n+\n+\t/* LTC4266 IV readings are only meaningful while the port is delivering\n+\t * power. When the PG (power good) bit is not set, the port is\n+\t * delivering nothing, so report 0 rather than an error: returning an\n+\t * errno here would abort pse_ethtool_get_status() and fail the whole\n+\t * \"ethtool --show-pse\" query for an otherwise perfectly readable port.\n+\t * Callers that must not report 0 (ltc4266_port_voltage_uv()) substitute\n+\t * a nominal value.\n+\t */\n+\tif (!(statpwr \u0026 LTC4266_STATPWR_PG(port-\u003echan)))\n+\t\treturn 0;\n+\n+\t/* Since iv is either 0 (to read current) or 2 to (read voltage), we\n+\t * can get the LSB reg by adding\n+\t */\n+\tlsb_reg = LTC4266_REG_IPLSB(port-\u003echan) + iv;\n+\n+\tresult = regmap_read(ltc4266-\u003eregmap, lsb_reg, \u0026lsb);\n+\tif (result \u003c 0)\n+\t\treturn result;\n+\n+\tresult = regmap_read(ltc4266-\u003eregmap, lsb_reg + 1, \u0026msb);\n+\tif (result \u003c 0)\n+\t\treturn result;\n+\n+\tivbits = (msb \u003c\u003c 8) | lsb;\n+\n+\tif (iv == LTC4266_READ_CURRENT)\n+\t\tif (port-\u003ersense == LTC4266_RSENSE_250)\n+\t\t\tresult = DIV_ROUND_CLOSEST_ULL(ivbits * LTC4266_IP_NA_PER_LSB_RSENSE_025,\n+\t\t\t\t\t\t       1000);\n+\t\telse\n+\t\t\tresult = DIV_ROUND_CLOSEST_ULL(ivbits * LTC4266_IP_NA_PER_LSB_RSENSE_050,\n+\t\t\t\t\t\t       1000);\n+\telse\n+\t\tresult = ivbits * LTC4266_VP_UV_PER_LSB;\n+\n+\treturn result;\n+}\n+\n+/* Voltage at the PI in uV, if the PI is currently powering a device;\n+ * otherwise, return the nominal voltage\n+ */\n+static int ltc4266_port_voltage_uv(struct ltc4266_port *port)\n+{\n+\tint uv = ltc4266_read_iv(port, LTC4266_READ_VOLTAGE);\n+\n+\tif (uv \u003c 0)\n+\t\treturn uv;\n+\n+\treturn uv ? uv : port-\u003evpwr_uv;\n+}\n+\n+/**\n+ * ltc4266_port_set_ilim - Set the active current limit (ILIM) for a port\n+ * @port: the port to configure\n+ * @class: the detected PD class (0-4)\n+ *\n+ * Given the PD class, configure the active current limit for a particular\n+ * channel.  The LTC4266 will actively enforce this current limit using the\n+ * sense resistor for the channel. The values written are from the LTC4266\n+ * Datasheet, Table 5, and correspond to 425mA for Type I PDs, and 850mA for\n+ * Type 2 PDs (per the datasheet requirement for IEEE compliance).\n+ *\n+ * IEEE Std 802.3-2022, Table 33-11 specifies ILIM parameter ranges:\n+ * - For Type 1 PSE operation (PD Classes 0-3):\n+ * The minimum ILIM is 0.400A. This driver uses 425mA. This value fits\n+ * within typical Type 1 ILIM specifications (e.g., 0.400A min to\n+ * around 0.440A-0.500A max for the programmed steady-state limit).\n+ *\n+ * - For Type 2 PSE operation (typically PD Class 4):\n+ * The minimum ILIM is 1.14 * ICable (or ~1.05 * IPort_max from other\n+ * interpretations, e.g., ~0.630A to ~0.684A). This driver uses 850mA.\n+ * This value meets the minimum requirement and is a supported operational\n+ * current limit for high power modes in the LTC4266.\n+ *\n+ * The overall PSE current output must not exceed the time-dependent PSE\n+ * upperbound template, IPSEUT(t), described in IEEE Std 802.3-2022,\n+ * Equation (33-6). The programmed ILIM values (425mA/850mA) serve as the\n+ * long-term current limit (Ilimmin segment of IPSEUT(t)) and are well\n+ * within the higher short-term current allowances of that template (e.g., 1.75A).\n+ *\n+ * Returns: 0 on success or a negative errno.\n+ */\n+static int ltc4266_port_set_ilim(struct ltc4266_port *port, int class)\n+{\n+\tbool rsense_250 = port-\u003ersense == LTC4266_RSENSE_250;\n+\tu8 ilim;\n+\n+\tif (class \u003e 4 || class \u003c 0)\n+\t\treturn -EINVAL;\n+\n+\tif (class \u003c 4)\n+\t\tilim = rsense_250 ? LTC4266_ILIM_TYPE1_RSENSE_025 :\n+\t\t\t\t    LTC4266_ILIM_TYPE1_RSENSE_050;\n+\telse\n+\t\tilim = rsense_250 ? LTC4266_ILIM_TYPE2_RSENSE_025 :\n+\t\t\t\t    LTC4266_ILIM_TYPE2_RSENSE_050;\n+\n+\treturn regmap_write(port-\u003eltc4266-\u003eregmap,\n+\t\t\t    LTC4266_REG_ILIM(port-\u003echan), ilim);\n+}\n+\n+static int ltc4266_port_set_icut(struct ltc4266_port *port, int icut)\n+{\n+\tu8 val;\n+\n+\tif (icut \u003e LTC4266_ICUT_MAX_MA)\n+\t\treturn -ERANGE;\n+\n+\tval = min(DIV_ROUND_UP(icut * 1000, LTC4266_ICUT_STEP_UA),\n+\t\t  LTC4266_ICUT_MAX_STEPS) \u0026 LTC4266_ICUT_MASK;\n+\n+\tif (port-\u003ersense == LTC4266_RSENSE_250)\n+\t\tval |= LTC4266_ICUT_RSENSE_025_OHM | LTC4266_ICUT_RANGE;\n+\n+\treturn regmap_write(port-\u003eltc4266-\u003eregmap,\n+\t\t\t    LTC4266_REG_ICUT_HP(port-\u003echan), val);\n+}\n+\n+/**\n+ * ltc4266_pw_limit_to_icut - Convert an admin power limit to an I_CUT threshold\n+ * @port: the port to convert for\n+ * @max_mw: the power limit in mW\n+ * @class: the detected PD class (0-4)\n+ *\n+ * The LTC4266 only enforces a current threshold, so a power limit has to be\n+ * divided by the port voltage.\n+ *\n+ * Lowering I_CUT below the class threshold is legitimate: IEEE 802.3-2022\n+ * 33.2.7.10 defines P_Class as either the class power or \"PSE allocated power\n+ * ... added to the channel power loss\", and an administrative limit is that\n+ * allocated power.\n+ *\n+ * Raising it above the class threshold is not. Table 33-11 item 7 bounds I_CUT\n+ * at I_LIM, and ltc4266_port_set_ilim() picks I_LIM from the class: 425 mA for\n+ * Type 1, 850 mA for Type 2. An administrative limit of LTC4266_PW_LIMIT_MAX\n+ * asks for 600 mA at the nominal port voltage, which would exceed I_LIM on a\n+ * Type 1 class. Capping at the class threshold from datasheet Table 2 keeps\n+ * I_CUT under I_LIM for every class; the LTC4266_ICUT_MAX_MA check in\n+ * ltc4266_port_set_icut() only covers Type 2.\n+ *\n+ * Return: the threshold in mA, or a negative errno if the voltage read failed.\n+ */\n+static int ltc4266_pw_limit_to_icut(struct ltc4266_port *port, int max_mw,\n+\t\t\t\t    int class)\n+{\n+\tint uv = ltc4266_port_voltage_uv(port);\n+\tint icut_mA;\n+\n+\tif (uv \u003c 0)\n+\t\treturn uv;\n+\n+\t/* 30000 mW x 1000000 overflows an int */\n+\ticut_mA = DIV_ROUND_UP_ULL((u64)max_mw * 1000000, uv);\n+\n+\treturn min(ltc4266_class_to_icut[class], icut_mA);\n+}\n+\n+static int ltc4266_port_delivering(struct ltc4266_port *port)\n+{\n+\tunsigned int result;\n+\tint ret;\n+\n+\tret = regmap_read(port-\u003eltc4266-\u003eregmap, LTC4266_REG_STATPWR, \u0026result);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\treturn !!((result \u0026 LTC4266_STATPWR_PG(port-\u003echan)) \u0026\u0026\n+\t\t  (result \u0026 LTC4266_STATPWR_PE(port-\u003echan)));\n+}\n+\n+static int ltc4266_port_init(struct ltc4266_port *port)\n+{\n+\tstruct ltc4266 *ltc4266 = port-\u003eltc4266;\n+\tu8 chan = port-\u003echan;\n+\tu8 tlim_shift;\n+\tu8 tlim_mask;\n+\tu8 tlim_reg;\n+\tint ret;\n+\n+\t/* Reset the port */\n+\tret = regmap_write(ltc4266-\u003eregmap, LTC4266_REG_RSTPB, BIT(chan));\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\t/* Set Semi-auto mode */\n+\tret = regmap_update_bits(port-\u003eltc4266-\u003eregmap, LTC4266_REG_OPMD,\n+\t\t\t\t LTC4266_TWO_BIT_WORD_MASK(port-\u003echan),\n+\t\t\t\t LTC4266_TWO_BIT_WORD_OFFSET(LTC4266_OPMD_SEMI,\n+\t\t\t\t\t\t\t     port-\u003echan));\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\t/* Enable high power mode on the port (for Type 2 PD support) */\n+\tret = regmap_update_bits(ltc4266-\u003eregmap, LTC4266_REG_HPEN,\n+\t\t\t\t LTC4266_HPEN(chan), LTC4266_HPEN(chan));\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\t/* Enable 2-event classification (IEEE 802.3-2022, Clause 33), which the\n+\t * datasheet refers to as \"Ping-Pong\" classification.\n+\t */\n+\tret = regmap_update_bits(ltc4266-\u003eregmap, LTC4266_REG_HPMD(chan),\n+\t\t\t\t LTC4266_HPMD_PONGEN, LTC4266_HPMD_PONGEN);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\tif (chan \u003c= 1)\n+\t\ttlim_reg = LTC4266_REG_TLIM12;\n+\telse\n+\t\ttlim_reg = LTC4266_REG_TLIM34;\n+\n+\t/* Each tlim register packs two ports: the even port in the low nibble\n+\t * and the odd port in the high nibble. Shift both the value and the\n+\t * mask into the correct nibble.\n+\t */\n+\tif (chan \u0026 BIT(0))\n+\t\ttlim_shift = 4;\n+\telse\n+\t\ttlim_shift = 0;\n+\n+\ttlim_mask = GENMASK(3, 0) \u003c\u003c tlim_shift;\n+\n+\tret = regmap_update_bits(ltc4266-\u003eregmap, tlim_reg,\n+\t\t\t\t tlim_mask, LTC4266_TLIM_VALUE \u003c\u003c tlim_shift);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\t/* Enable disconnect detect. */\n+\tret = regmap_update_bits(ltc4266-\u003eregmap, LTC4266_REG_DISENA,\n+\t\t\t\t BIT(chan), BIT(chan));\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\t/* Enable detection (low nibble), classification (high nibble) on the port */\n+\tret = regmap_write(ltc4266-\u003eregmap, LTC4266_REG_DETPB,\n+\t\t\t   LTC4266_DETPB_CLASS_ENABLE(chan) |\n+\t\t\t   LTC4266_DETPB_DETECT_ENABLE(chan));\n+\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\tdev_dbg(ltc4266-\u003edev, \"Channel %d has been initialized\\n\", chan);\n+\treturn 0;\n+}\n+\n+/* Read the port's classification result and return a class 0-4 or an error if\n+ * the result isn't valid.\n+ */\n+static int ltc4266_port_get_class(struct ltc4266_port *port)\n+{\n+\tstruct ltc4266 *ltc4266 = port-\u003eltc4266;\n+\tunsigned int val;\n+\tint ret;\n+\n+\tret = regmap_read(ltc4266-\u003eregmap, LTC4266_REG_STAT(port-\u003echan), \u0026val);\n+\tif (ret \u003c 0) {\n+\t\tdev_warn(ltc4266-\u003edev, \"Failed to read status register, err=%d\\n\", ret);\n+\t\treturn ret;\n+\t}\n+\n+\t/* Can't have a valid classification result if we've not yet had a good\n+\t * detection result.\n+\t */\n+\tif (LTC4266_PORT_DETECT(val) != LTC4266_DETECT_GOOD)\n+\t\treturn -EINVAL;\n+\n+\tret = ltc4266_class_map[LTC4266_PORT_CLASS(val)];\n+\treturn ret;\n+}\n+\n+/* Maximum power the classified PD is allowed. It does not depend on the port\n+ * being powered, but it does depend on the port having a PD attached and being\n+ * enabled to the point of running classification and detection cycles.\n+ */\n+static int ltc4266_port_max_pw(struct ltc4266_port *port)\n+{\n+\tint class = ltc4266_port_get_class(port);\n+\n+\tif (class \u003c 0)\n+\t\treturn class;\n+\n+\treturn ltc4266_class_pw[class];\n+}\n+\n+static int ltc4266_pi_get_pw_status(struct pse_controller_dev *pcdev, int id,\n+\t\t\t\t    struct pse_pw_status *pw_status)\n+{\n+\tstruct ltc4266_port *port = ltc4266_pi_port(pcdev, id);\n+\tint ret;\n+\n+\tret = ltc4266_port_delivering(port);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\tif (ret)\n+\t\tpw_status-\u003ec33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING;\n+\telse\n+\t\tpw_status-\u003ec33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING;\n+\n+\treturn 0;\n+}\n+\n+/* With the static budget evaluation strategy the PSE core calls this only\n+ * after a PD has been classified and the power budget has been allocated.\n+ * Program the current limit for the classified PD, reduced if an admin power\n+ * limit asks for less than the class is entitled to, and apply power.\n+ */\n+static int ltc4266_pi_enable(struct pse_controller_dev *pcdev, int id)\n+{\n+\tstruct ltc4266_port *port = ltc4266_pi_port(pcdev, id);\n+\tint class, icut, ret;\n+\n+\tclass = ltc4266_port_get_class(port);\n+\tif (class \u003c 0)\n+\t\treturn class;\n+\n+\tret = ltc4266_port_set_ilim(port, class);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\t/* Derive the threshold from the admin limit against the class in front of\n+\t * us now, since a different PD may have been plugged in since the limit\n+\t * was set.\n+\t */\n+\ticut = ltc4266_pw_limit_to_icut(port, port-\u003epw_limit, class);\n+\tif (icut \u003c 0)\n+\t\treturn icut;\n+\n+\tret = ltc4266_port_set_icut(port, icut);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\t/* Apply power to the port. */\n+\treturn regmap_write(port-\u003eltc4266-\u003eregmap, LTC4266_REG_PWRPB,\n+\t\t\t    BIT(port-\u003echan));\n+}\n+\n+static int ltc4266_pi_disable(struct pse_controller_dev *pcdev, int id)\n+{\n+\tstruct ltc4266_port *port = ltc4266_pi_port(pcdev, id);\n+\n+\t/* Resetting the port (RSTPB, issued at the start of ltc4266_port_init)\n+\t * removes power, disables detection and classification, and clears the\n+\t * port's status register. Re-init the port so that detection and\n+\t * classification can happen again.\n+\t */\n+\treturn ltc4266_port_init(port);\n+}\n+\n+static int ltc4266_pi_get_voltage(struct pse_controller_dev *pcdev, int id)\n+{\n+\treturn ltc4266_port_voltage_uv(ltc4266_pi_port(pcdev, id));\n+}\n+\n+static int ltc4266_pi_get_admin_state(struct pse_controller_dev *pcdev, int id,\n+\t\t\t\t      struct pse_admin_state *admin_state)\n+{\n+\tstruct ltc4266_port *port = ltc4266_pi_port(pcdev, id);\n+\tunsigned int val;\n+\tint ret;\n+\n+\t/* The PSE core uses this as the hardware admin state: whether power has\n+\t * been commanded on, not whether it has finished coming up.\n+\t * pse_pi_is_hw_enabled() decides from it which software-enabled PIs\n+\t * still need power delivery attempted, so report the power-enable\n+\t * nibble (peN), which the controller sets as soon as it applies power to\n+\t * the port. The power-good nibble only sets once OUT has pulled down to\n+\t * VEE, up to a tSTART later, and a port reported as not enabled for that\n+\t * whole ramp is one the core will allocate power domain budget for a\n+\t * second time. Actual delivery is reported by pi_get_pw_status().\n+\t */\n+\tret = regmap_read(port-\u003eltc4266-\u003eregmap, LTC4266_REG_STATPWR, \u0026val);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\tif (val \u0026 LTC4266_STATPWR_PE(port-\u003echan))\n+\t\tadmin_state-\u003ec33_admin_state =\n+\t\t\tETHTOOL_C33_PSE_ADMIN_STATE_ENABLED;\n+\telse\n+\t\tadmin_state-\u003ec33_admin_state =\n+\t\t\tETHTOOL_C33_PSE_ADMIN_STATE_DISABLED;\n+\n+\treturn 0;\n+}\n+\n+static int ltc4266_pi_get_pw_class(struct pse_controller_dev *pcdev, int id)\n+{\n+\tint ret = ltc4266_port_get_class(ltc4266_pi_port(pcdev, id));\n+\n+\t/* ltc4266_port_get_class will return either the class, or an errno.\n+\t * Returning an errno from this function will mean the ethtool command\n+\t * will abort with the error and not emit later useful information.\n+\t * Additionally, returning 0 to indicate no classification signature\n+\t * will cause the ethtool output to omit the \"Power Class\" attribute\n+\t * entirely. Since the \"Power Class\" is expected to be returned here,\n+\t * and class 0 and class 3 are equivalent in terms of power allocation,\n+\t * we'll return 3.\n+\t */\n+\n+\tif (ret == 0)\n+\t\tret = 3;\n+\tif (ret \u003c 0)\n+\t\tret = 0;\n+\treturn ret;\n+}\n+\n+/* Get the power requested by the PD before enabling the port: its\n+ * classification power.\n+ */\n+static int ltc4266_pi_get_pw_req(struct pse_controller_dev *pcdev, int id)\n+{\n+\treturn ltc4266_port_max_pw(ltc4266_pi_port(pcdev, id));\n+}\n+\n+static int ltc4266_pi_get_actual_pw(struct pse_controller_dev *pcdev, int id)\n+{\n+\tstruct ltc4266_port *port = ltc4266_pi_port(pcdev, id);\n+\tint uA, uV;\n+\n+\tuA = ltc4266_read_iv(port, LTC4266_READ_CURRENT);\n+\tif (uA \u003c 0)\n+\t\treturn uA;\n+\n+\tuV = ltc4266_read_iv(port, LTC4266_READ_VOLTAGE);\n+\tif (uV \u003c 0)\n+\t\treturn uV;\n+\n+\t/* mW = uA * uV / 1000000000 */\n+\treturn DIV_ROUND_CLOSEST_ULL((u64)uA * uV, 1000000000);\n+}\n+\n+/* The range of administrative power limits this controller supports. It does\n+ * not depend on the detected class: the class only bounds the I_CUT threshold\n+ * that ltc4266_pw_limit_to_icut() programs.\n+ */\n+static int ltc4266_pi_get_pw_limit_ranges(struct pse_controller_dev *pcdev, int id,\n+\t\t\t\t\t  struct pse_pw_limit_ranges *pw_limit_ranges)\n+{\n+\tstruct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges;\n+\n+\tc33_pw_limit_ranges = kzalloc_obj(*c33_pw_limit_ranges);\n+\tif (!c33_pw_limit_ranges)\n+\t\treturn -ENOMEM;\n+\n+\tc33_pw_limit_ranges[0].min = LTC4266_PW_LIMIT_MIN;\n+\tc33_pw_limit_ranges[0].max = LTC4266_PW_LIMIT_MAX;\n+\n+\tpw_limit_ranges-\u003ec33_pw_limit_ranges = c33_pw_limit_ranges;\n+\n+\t/* Return the number of ranges */\n+\treturn 1;\n+}\n+\n+/* Store the administrative power limit. The limit is independent of any PD in\n+ * front of us, so accept it whether or not the port has classified anything;\n+ * program I_CUT immediately when it has, so a change on a live port takes\n+ * effect, and leave it to ltc4266_pi_enable() otherwise.\n+ */\n+static int ltc4266_pi_set_pw_limit(struct pse_controller_dev *pcdev,\n+\t\t\t\t   int id, int max_mw)\n+{\n+\tstruct ltc4266_port *port = ltc4266_pi_port(pcdev, id);\n+\tint class;\n+\tint icut;\n+\tint ret;\n+\n+\tif (max_mw \u003c LTC4266_PW_LIMIT_MIN || max_mw \u003e LTC4266_PW_LIMIT_MAX) {\n+\t\tdev_err(port-\u003eltc4266-\u003edev, \"power limit %d is out of range [%d, %d]\\n\",\n+\t\t\tmax_mw, LTC4266_PW_LIMIT_MIN, LTC4266_PW_LIMIT_MAX);\n+\t\treturn -ERANGE;\n+\t}\n+\n+\tclass = ltc4266_port_get_class(port);\n+\tif (class \u003e= 0) {\n+\t\ticut = ltc4266_pw_limit_to_icut(port, max_mw, class);\n+\t\tif (icut \u003c 0)\n+\t\t\treturn icut;\n+\n+\t\tret = ltc4266_port_set_icut(port, icut);\n+\t\tif (ret \u003c 0)\n+\t\t\treturn ret;\n+\t}\n+\n+\tport-\u003epw_limit = max_mw;\n+\n+\treturn 0;\n+}\n+\n+static int ltc4266_pi_get_pw_limit(struct pse_controller_dev *pcdev, int id)\n+{\n+\treturn ltc4266_pi_port(pcdev, id)-\u003epw_limit;\n+}\n+\n+/* Description of one delivery channel parsed out of the \"channels\" node. Used\n+ * locally only during ltc4266_setup_pi_matrix()\n+ */\n+struct ltc4266_chan_desc {\n+\tstruct device_node *np;\n+\tenum sense_resistor rsense;\n+};\n+\n+static int ltc4266_get_of_channels(struct ltc4266 *ltc4266,\n+\t\t\t\t   struct ltc4266_chan_desc *chans)\n+{\n+\tstruct device_node *channels_node __free(device_node) =\n+\t\tof_get_child_by_name(ltc4266-\u003enp, \"channels\");\n+\tu32 chan_id, sense;\n+\tint ret;\n+\n+\tif (!channels_node)\n+\t\treturn dev_err_probe(ltc4266-\u003edev, -EINVAL,\n+\t\t\t\t     \"missing \\\"channels\\\" node\\n\");\n+\n+\tfor_each_child_of_node_scoped(channels_node, chan_node) {\n+\t\tif (!of_node_name_eq(chan_node, \"channel\"))\n+\t\t\tcontinue;\n+\n+\t\tret = of_property_read_u32(chan_node, \"reg\", \u0026chan_id);\n+\t\tif (ret)\n+\t\t\treturn dev_err_probe(ltc4266-\u003edev, ret,\n+\t\t\t\t\t     \"missing reg property in node %pOF\\n\",\n+\t\t\t\t\t     chan_node);\n+\n+\t\tif (chan_id \u003e= LTC4266_MAX_PORTS)\n+\t\t\treturn dev_err_probe(ltc4266-\u003edev, -EINVAL,\n+\t\t\t\t\t     \"channel id %u is out of range in node %pOF\\n\",\n+\t\t\t\t\t     chan_id, chan_node);\n+\n+\t\tif (chans[chan_id].np)\n+\t\t\treturn dev_err_probe(ltc4266-\u003edev, -EINVAL,\n+\t\t\t\t\t     \"channel id %u is already used, please check the reg property in node %pOF\\n\",\n+\t\t\t\t\t     chan_id, chan_node);\n+\n+\t\tret = of_property_read_u32(chan_node, \"sense-resistor-micro-ohms\", \u0026sense);\n+\t\tif (ret)\n+\t\t\treturn dev_err_probe(ltc4266-\u003edev, ret,\n+\t\t\t\t\t     \"missing sense-resistor-micro-ohms property in node %pOF\\n\",\n+\t\t\t\t\t     chan_node);\n+\n+\t\tif (sense == 250000)\n+\t\t\tchans[chan_id].rsense = LTC4266_RSENSE_250;\n+\t\telse if (sense == 500000)\n+\t\t\tchans[chan_id].rsense = LTC4266_RSENSE_500;\n+\t\telse\n+\t\t\treturn dev_err_probe(ltc4266-\u003edev, -EINVAL,\n+\t\t\t\t\t     \"invalid sense resistor value %u in node %pOF\\n\",\n+\t\t\t\t\t     sense, chan_node);\n+\n+\t\tchans[chan_id].np = of_node_get(chan_node);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/* Find the channel a PI pairset phandle points at. */\n+static int ltc4266_match_channel(const struct pse_pi_pairset *pairset,\n+\t\t\t\t struct ltc4266_chan_desc *chans)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i \u003c LTC4266_MAX_PORTS; i++)\n+\t\tif (pairset-\u003enp == chans[i].np)\n+\t\t\treturn i;\n+\n+\treturn -ENODEV;\n+}\n+\n+/*\n+ * Nominal voltage of the rail feeding a PSE PI, taken from its \"vpwr-supply\".\n+ * This is intended to work around the limitation on the LTC4266 where the port\n+ * voltage is not valid until _after_ the port is powered.  Since the PSE core\n+ * expects to read the port voltage when the port is not powered we need\n+ * something non-zero to return to the core.\n+ *\n+ * In the event the regulator isn't present, or if the regulator doesn't have a\n+ * nominal voltage available, we'll fall back and use 50V, which is the minimum\n+ * allowed for a Type 2 PSE.\n+ *\n+ * Finally, we read this regulator voltage here instead of during\n+ * ltc4266_pi_get_voltage to avoid deadlock.\n+ *\n+ * Return: the nominal voltage in uV, or LTC4266_VPORT_NOMINAL_UV if the PI\n+ * describes no supply, its supply has not registered yet, or its voltage\n+ * cannot be determined.\n+ */\n+static int ltc4266_pi_nominal_uv(struct ltc4266 *ltc4266, struct device_node *np)\n+{\n+\tstruct regulator *vpwr;\n+\tint uv;\n+\n+\tvpwr = of_regulator_get_optional(ltc4266-\u003edev, np, \"vpwr\");\n+\tif (IS_ERR(vpwr)) {\n+\t\t/* -ENODEV means the PI describes no vpwr-supply at all, which\n+\t\t * is the case the fallback exists for. -EPROBE_DEFER means the\n+\t\t * rail _is_ described but has not registered yet, so the\n+\t\t * fallback is wrong for it. Asking for a probe retry is not an\n+\t\t * option from here: we run from setup_pi_matrix(), and\n+\t\t * pse_controller_register() unwinds neither its notification\n+\t\t * fifo nor its pse_pi array when that fails, so every retry\n+\t\t * would leak. Warn instead so the assumed voltage is visible.\n+\t\t */\n+\t\tif (PTR_ERR(vpwr) == -EPROBE_DEFER)\n+\t\t\tdev_warn(ltc4266-\u003edev,\n+\t\t\t\t \"%pOF: vpwr-supply is not registered yet, assuming %d uV\\n\",\n+\t\t\t\t np, LTC4266_VPORT_NOMINAL_UV);\n+\n+\t\treturn LTC4266_VPORT_NOMINAL_UV;\n+\t}\n+\n+\tuv = regulator_get_voltage(vpwr);\n+\tregulator_put(vpwr);\n+\n+\tif (uv \u003c= 0)\n+\t\treturn LTC4266_VPORT_NOMINAL_UV;\n+\n+\treturn uv;\n+}\n+\n+/* Build a port context for every PSE PI described in the device tree, bound to\n+ * the channel its pairset references. A PI with no node is left NULL; the PSE\n+ * core does not register a regulator for it and so never calls back for it.\n+ */\n+static int ltc4266_map_pis(struct ltc4266 *ltc4266,\n+\t\t\t   struct ltc4266_chan_desc *chans)\n+{\n+\tstruct pse_controller_dev *pcdev = \u0026ltc4266-\u003epcdev;\n+\tstruct ltc4266_port *port;\n+\tint i, j, chan, uv;\n+\n+\tfor (i = 0; i \u003c LTC4266_MAX_PORTS; i++) {\n+\t\tstruct pse_pi *pi = \u0026pcdev-\u003epi[i];\n+\n+\t\tif (!pi-\u003enp)\n+\t\t\tcontinue;\n+\n+\t\tif (!pi-\u003epairset[0].np)\n+\t\t\treturn dev_err_probe(ltc4266-\u003edev, -EINVAL,\n+\t\t\t\t\t     \"%pOF has no pairsets\\n\", pi-\u003enp);\n+\n+\t\t/* The LTC4266 delivers over a single pairset per channel, so\n+\t\t * there is no 4-pair mode to spread a PI over two channels.\n+\t\t */\n+\t\tif (pi-\u003epairset[1].np)\n+\t\t\treturn dev_err_probe(ltc4266-\u003edev, -EOPNOTSUPP,\n+\t\t\t\t\t     \"%pOF: 4-pair PSE PIs are not supported\\n\",\n+\t\t\t\t\t     pi-\u003enp);\n+\n+\t\tchan = ltc4266_match_channel(\u0026pi-\u003epairset[0], chans);\n+\t\tif (chan \u003c 0)\n+\t\t\treturn dev_err_probe(ltc4266-\u003edev, chan,\n+\t\t\t\t\t     \"%pOF: pairset %pOF is not a channel of this controller\\n\",\n+\t\t\t\t\t     pi-\u003enp, pi-\u003epairset[0].np);\n+\n+\t\tfor (j = 0; j \u003c i; j++)\n+\t\t\tif (ltc4266-\u003eports[j] \u0026\u0026 ltc4266-\u003eports[j]-\u003echan == chan)\n+\t\t\t\treturn dev_err_probe(ltc4266-\u003edev, -EINVAL,\n+\t\t\t\t\t\t     \"%pOF: channel %d is already used by %pOF\\n\",\n+\t\t\t\t\t\t     pi-\u003enp, chan, pcdev-\u003epi[j].np);\n+\n+\t\tuv = ltc4266_pi_nominal_uv(ltc4266, pi-\u003enp);\n+\n+\t\tport = devm_kzalloc(ltc4266-\u003edev, sizeof(*port), GFP_KERNEL);\n+\t\tif (!port)\n+\t\t\treturn -ENOMEM;\n+\n+\t\tport-\u003eltc4266 = ltc4266;\n+\t\tport-\u003echan = chan;\n+\t\tport-\u003ersense = chans[chan].rsense;\n+\t\tport-\u003evpwr_uv = uv;\n+\t\tport-\u003epw_limit = LTC4266_PW_LIMIT_MAX;\n+\t\tltc4266-\u003eports[i] = port;\n+\n+\t\tdev_dbg(ltc4266-\u003edev, \"PI %d is backed by channel %d, nominal %d uV\\n\",\n+\t\t\ti, chan, uv);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int ltc4266_setup_pi_matrix(struct pse_controller_dev *pcdev)\n+{\n+\tstruct ltc4266 *ltc4266 = container_of(pcdev, struct ltc4266, pcdev);\n+\tstruct ltc4266_chan_desc chans[LTC4266_MAX_PORTS] = { };\n+\tint i, ret;\n+\n+\tif (pcdev-\u003eno_of_pse_pi)\n+\t\treturn dev_err_probe(ltc4266-\u003edev, -EINVAL,\n+\t\t\t\t     \"a \\\"pse-pis\\\" node is required\\n\");\n+\n+\tret = ltc4266_get_of_channels(ltc4266, chans);\n+\tif (!ret)\n+\t\tret = ltc4266_map_pis(ltc4266, chans);\n+\n+\tfor (i = 0; i \u003c LTC4266_MAX_PORTS; i++)\n+\t\tof_node_put(chans[i].np);\n+\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tfor (i = 0; i \u003c LTC4266_MAX_PORTS; i++) {\n+\t\tif (!ltc4266-\u003eports[i])\n+\t\t\tcontinue;\n+\n+\t\tret = ltc4266_port_init(ltc4266-\u003eports[i]);\n+\t\tif (ret \u003c 0)\n+\t\t\treturn dev_err_probe(ltc4266-\u003edev, ret,\n+\t\t\t\t\t     \"Failed to initialize PI %d\\n\", i);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static const struct pse_controller_ops ltc4266_ops = {\n+\t.setup_pi_matrix = ltc4266_setup_pi_matrix,\n+\t.pi_get_admin_state = ltc4266_pi_get_admin_state,\n+\t.pi_get_pw_status = ltc4266_pi_get_pw_status,\n+\t.pi_get_pw_class = ltc4266_pi_get_pw_class,\n+\t.pi_get_actual_pw = ltc4266_pi_get_actual_pw,\n+\t.pi_enable = ltc4266_pi_enable,\n+\t.pi_disable = ltc4266_pi_disable,\n+\t.pi_get_voltage = ltc4266_pi_get_voltage,\n+\t.pi_get_pw_limit = ltc4266_pi_get_pw_limit,\n+\t.pi_set_pw_limit = ltc4266_pi_set_pw_limit,\n+\t.pi_get_pw_limit_ranges = ltc4266_pi_get_pw_limit_ranges,\n+\t.pi_get_pw_req = ltc4266_pi_get_pw_req,\n+};\n+\n+#define LTC4266_INTERRUPT_SOURCES\t(LTC4266_INT_TSTART | LTC4266_INT_TCUT | \\\n+\t\t\t\t\t LTC4266_INT_CLASS | LTC4266_INT_DETECT | \\\n+\t\t\t\t\t LTC4266_INT_DIS | LTC4266_INT_PWRGD)\n+\n+static int ltc4266_enable_interrupts(struct ltc4266 *ltc4266)\n+{\n+\t/* Unmask interrupts */\n+\treturn regmap_write(ltc4266-\u003eregmap, LTC4266_REG_INTMASK,\n+\t\t     LTC4266_INTERRUPT_SOURCES);\n+}\n+\n+static int ltc4266_disable_interrupts(struct ltc4266 *ltc4266)\n+{\n+\tint ret;\n+\n+\tret = regmap_write(ltc4266-\u003eregmap, LTC4266_REG_INTMASK, 0x00);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\t/* Reset the (SMBus Alert) interrupt pin */\n+\treturn regmap_write(ltc4266-\u003eregmap, LTC4266_REG_RSTPB, LTC4266_RSTPB_PINCLR);\n+}\n+\n+static int ltc4266_map_event(int irq, struct pse_controller_dev *pcdev,\n+\t\t\t     unsigned long *notifs, unsigned long *notifs_mask)\n+{\n+\tstruct ltc4266 *ltc4266 = container_of(pcdev, struct ltc4266, pcdev);\n+\tunsigned int detevn = 0, fltevn = 0, tsevn = 0, pwrevn = 0;\n+\tunsigned int statpwr = 0;\n+\tunsigned int intstat;\n+\tint ret;\n+\tint i;\n+\n+\tret = ltc4266_disable_interrupts(ltc4266);\n+\tif (ret \u003c 0)\n+\t\tgoto err;\n+\n+\tret = regmap_read(ltc4266-\u003eregmap, LTC4266_REG_INTSTAT, \u0026intstat);\n+\tif (ret \u003c 0)\n+\t\tgoto err;\n+\n+\tif (!intstat)\n+\t\tgoto done;\n+\n+\tif (intstat \u0026 LTC4266_INT_PWRGD) {\n+\t\tret = regmap_read(ltc4266-\u003eregmap, LTC4266_REG_PWREVN_COR, \u0026pwrevn);\n+\t\tif (ret \u003c 0) {\n+\t\t\tdev_err(\u0026ltc4266-\u003eclient-\u003edev, \"Failed to read pwrevn, err=%d\\n\", ret);\n+\t\t\tgoto err;\n+\t\t}\n+\n+\t\tret = regmap_read(ltc4266-\u003eregmap, LTC4266_REG_STATPWR, \u0026statpwr);\n+\t\tif (ret \u003c 0) {\n+\t\t\tdev_err(\u0026ltc4266-\u003eclient-\u003edev, \"Failed to read statpwr, err=%d\\n\", ret);\n+\t\t\tgoto err;\n+\t\t}\n+\t}\n+\n+\tif (intstat \u0026 (LTC4266_INT_DIS | LTC4266_INT_TCUT)) {\n+\t\tret = regmap_read(ltc4266-\u003eregmap, LTC4266_REG_FLTEVN_COR, \u0026fltevn);\n+\t\tif (ret \u003c 0) {\n+\t\t\tdev_err(\u0026ltc4266-\u003eclient-\u003edev, \"Failed to read fltevn err=%d\\n\", ret);\n+\t\t\tgoto err;\n+\t\t}\n+\t}\n+\n+\tif (intstat \u0026 (LTC4266_INT_TSTART | LTC4266_INT_TCUT)) {\n+\t\tret = regmap_read(ltc4266-\u003eregmap, LTC4266_REG_TSEVN_COR, \u0026tsevn);\n+\t\tif (ret \u003c 0) {\n+\t\t\tdev_err(\u0026ltc4266-\u003eclient-\u003edev, \"Failed to read tsevn, err=%d\\n\", ret);\n+\t\t\tgoto err;\n+\t\t}\n+\t}\n+\n+\tif (intstat \u0026 (LTC4266_INT_CLASS | LTC4266_INT_DETECT)) {\n+\t\tret = regmap_read(ltc4266-\u003eregmap, LTC4266_REG_DETEVN_COR, \u0026detevn);\n+\t\tif (ret \u003c 0) {\n+\t\t\tdev_err(\u0026ltc4266-\u003eclient-\u003edev, \"Failed to read detevn, err=%d\\n\", ret);\n+\t\t\tgoto err;\n+\t\t}\n+\t}\n+\n+\t/* The event registers are indexed by delivery channel while notifs[] is\n+\t * indexed by PSE PI id, so walk the PIs and use each one's channel to\n+\t * select the event bits.\n+\t */\n+\tfor (i = 0; i \u003c LTC4266_MAX_PORTS; i++) {\n+\t\tstruct ltc4266_port *port = ltc4266-\u003eports[i];\n+\t\tunsigned int fault;\n+\t\tbool pg_lost;\n+\t\tu8 chan;\n+\n+\t\tif (!port)\n+\t\t\tcontinue;\n+\n+\t\tchan = port-\u003echan;\n+\n+\t\tfault = (tsevn | fltevn) \u0026\n+\t\t\t(LTC4266_EVN_HI(chan) | LTC4266_EVN_LO(chan));\n+\n+\t\t/* The controller also drops a port without reporting any\n+\t\t * per-port fault event, for instance on an over-temperature\n+\t\t * shutdown or when it finds a failed external MOSFET. Both of\n+\t\t * those clear the port's detection and classification enables,\n+\t\t * so the port stays dark until it is re-initialised. A power\n+\t\t * good change that leaves statpwr[pg] clear catches them, and\n+\t\t * ignores the rising edge of a port that has just been powered\n+\t\t * on.\n+\t\t */\n+\t\tpg_lost = (pwrevn \u0026 LTC4266_EVN_HI(chan)) \u0026\u0026\n+\t\t\t  !(statpwr \u0026 LTC4266_STATPWR_PG(chan));\n+\n+\t\tif (fault || pg_lost) {\n+\t\t\t/* The port has gone down; if we see this, then we don't\n+\t\t\t * care if any of the remaining events are set.  If the\n+\t\t\t * device did disconnect briefly, it'll redetect and\n+\t\t\t * reclassify accordingly\n+\t\t\t */\n+\t\t\tnotifs[i] |= ETHTOOL_C33_PSE_EVENT_DISCONNECTION;\n+\t\t\t*notifs_mask |= BIT(i);\n+\n+\t\t\t/* Report over-current if the port went down for any\n+\t\t\t * overcurrent reason: tSTART (tsevn low nibble, startup\n+\t\t\t * inrush), tLIM (tsevn high nibble, current-limit\n+\t\t\t * timeout) or tCUT (fltevn low nibble, I_CUT timeout).\n+\t\t\t */\n+\t\t\tif ((tsevn \u0026 (LTC4266_EVN_LO(chan) | LTC4266_EVN_HI(chan))) ||\n+\t\t\t    (fltevn \u0026 LTC4266_EVN_LO(chan)))\n+\t\t\t\tnotifs[i] |= ETHTOOL_PSE_EVENT_OVER_CURRENT;\n+\n+\t\t\tdev_dbg(\u0026ltc4266-\u003eclient-\u003edev,\n+\t\t\t\t\"tsevn=0x%02X fltevn=0x%02X pwrevn=0x%02X statpwr=0x%02X\\n\",\n+\t\t\t\ttsevn, fltevn, pwrevn, statpwr);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (detevn \u0026 LTC4266_EVN_LO(chan)) {\n+\t\t\t/* Read the detect result, and if it isn't detect good,\n+\t\t\t * call it a disconnect\n+\t\t\t */\n+\t\t\tunsigned int detval;\n+\n+\t\t\tret = regmap_read(ltc4266-\u003eregmap, LTC4266_REG_STAT(chan), \u0026detval);\n+\t\t\tif (ret \u003c 0) {\n+\t\t\t\tdev_warn(ltc4266-\u003edev, \"Failed to read status register, err=%d\\n\",\n+\t\t\t\t\t ret);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tif (LTC4266_PORT_DETECT(detval) != LTC4266_DETECT_GOOD) {\n+\t\t\t\tnotifs[i] |= ETHTOOL_C33_PSE_EVENT_DISCONNECTION;\n+\t\t\t\t*notifs_mask |= BIT(i);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (detevn \u0026 LTC4266_EVN_HI(chan)) {\n+\t\t\tint class = ltc4266_port_get_class(port);\n+\n+\t\t\tif (class \u003e= 0) {\n+\t\t\t\tnotifs[i] |= ETHTOOL_C33_PSE_EVENT_CLASSIFICATION;\n+\t\t\t\t*notifs_mask |= BIT(i);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+done:\n+\treturn ltc4266_enable_interrupts(ltc4266);\n+\n+err:\n+\t/* (Attempt to) clear any remaining event registers that we might've\n+\t * missed in the event a previous read has failed.\n+\t */\n+\tret = regmap_write(ltc4266-\u003eregmap, LTC4266_REG_RSTPB, LTC4266_RSTPB_INTCLR);\n+\tif (ret)\n+\t\tdev_warn(\u0026ltc4266-\u003eclient-\u003edev, \"Failed to clear pending interrupts, err=%d\\n\",\n+\t\t\t ret);\n+\n+\treturn ltc4266_enable_interrupts(ltc4266);\n+}\n+\n+static const struct regmap_config ltc4266_regmap_config = {\n+\t.reg_bits = 8,\n+\t.val_bits = 8,\n+\t.max_register = 0x5F,\n+};\n+\n+static void ltc4266_teardown(void *data)\n+{\n+\tstruct ltc4266 *ltc4266 = data;\n+\n+\tltc4266_disable_interrupts(ltc4266);\n+\n+\t/* Prevent the chip from asserting interrupts */\n+\tregmap_update_bits(ltc4266-\u003eregmap, LTC4266_REG_MCONF,\n+\t\t\t   LTC4266_MCONF_INTERRUPT_ENABLE, 0);\n+\n+\t/* Reset all the ports and do not re-init: a port reset removes power and\n+\t * clears the port's detection and classification enables, leaving it in\n+\t * semi-auto mode, which never powers a port without a host request.\n+\t */\n+\tregmap_write(ltc4266-\u003eregmap, LTC4266_REG_RSTPB, LTC4266_RSTPB_RSTPORTS);\n+}\n+\n+static int ltc4266_probe(struct i2c_client *client)\n+{\n+\tstruct ltc4266 *ltc4266;\n+\tstruct regmap *regmap;\n+\tunsigned int id_reg;\n+\tint ret;\n+\n+\tstruct pse_irq_desc irq_desc = {\n+\t\t.name = \"ltc4266-irq\",\n+\t\t.map_event = ltc4266_map_event,\n+\t};\n+\n+\t/* We need IRQ for static power budgeting and if don't have it, fail\n+\t * probe early\n+\t */\n+\tif (!client-\u003eirq)\n+\t\treturn dev_err_probe(\u0026client-\u003edev, -EINVAL,\n+\t\t\t\t     \"Interrupt is required for power budget management\\n\");\n+\n+\tregmap = devm_regmap_init_i2c(client, \u0026ltc4266_regmap_config);\n+\tif (IS_ERR(regmap))\n+\t\treturn dev_err_probe(\u0026client-\u003edev, PTR_ERR(regmap),\n+\t\t\t\t     \"Failed to allocate regmap\\n\");\n+\n+\t/* Confirm we are talking to an LTC4266: the id register (0x1B) should\n+\t * read back its documented reset value of 0x64.\n+\t */\n+\tret = regmap_read(regmap, LTC4266_REG_ID, \u0026id_reg);\n+\tif (ret \u003c 0)\n+\t\treturn dev_err_probe(\u0026client-\u003edev, ret, \"Failed to read ID register\\n\");\n+\n+\tif (id_reg != LTC4266_ID)\n+\t\treturn dev_err_probe(\u0026client-\u003edev, -ENODEV,\n+\t\t\t\t     \"Expected an ID of 0x64, saw 0x%02X\\n\", id_reg);\n+\n+\t/* Reset the chip */\n+\tret = regmap_write(regmap, LTC4266_REG_RSTPB, LTC4266_RSTPB_INTCLR | LTC4266_RSTPB_RSTALL);\n+\tif (ret \u003c 0)\n+\t\treturn dev_err_probe(\u0026client-\u003edev, ret, \"Failed to reset\\n\");\n+\n+\t/* LTC4266 requires approximately 10 ms after reset to be stable; if it\n+\t * isn't, then there is typically an undervoltage lockout/something pretty bad\n+\t * going on. We give it 50 ms here so we don't need to poll the chip and use I2C bandwidth\n+\t */\n+\tmsleep(50);\n+\n+\t/* Let's make sure the chip came out of reset (if not, the chip is probably\n+\t * either (no longer?) present, in thermal shutdown, or watchdogged....either\n+\t * way, there's nothing we can do in software to fix it)\n+\t */\n+\tret = regmap_read(regmap, LTC4266_REG_ID, \u0026id_reg);\n+\tif (ret \u003c 0)\n+\t\treturn dev_err_probe(\u0026client-\u003edev, ret,\n+\t\t\t\t     \"Failed to re-read ID register after reset\\n\");\n+\n+\tif (id_reg != LTC4266_ID)\n+\t\treturn dev_err_probe(\u0026client-\u003edev, -ENODEV,\n+\t\t\t\t     \"Failed to re-read device ID after reset 0x%02X\\n\",\n+\t\t\t\t     id_reg);\n+\n+\tltc4266 = devm_kzalloc(\u0026client-\u003edev, sizeof(struct ltc4266), GFP_KERNEL);\n+\tif (!ltc4266)\n+\t\treturn -ENOMEM;\n+\n+\tltc4266-\u003eclient = client;\n+\tltc4266-\u003eregmap = regmap;\n+\tltc4266-\u003enp = client-\u003edev.of_node;\n+\tltc4266-\u003edev = \u0026client-\u003edev;\n+\n+\t/* After reset, the LTC4266 will interrupt with a (single) supply fault.\n+\t * Clear it here and discard the result\n+\t */\n+\tregmap_read(ltc4266-\u003eregmap, LTC4266_REG_SUPEVN_COR, \u0026id_reg);\n+\n+\tret = ltc4266_disable_interrupts(ltc4266);\n+\tif (ret)\n+\t\treturn dev_err_probe(\u0026client-\u003edev, ret,\n+\t\t\t\t     \"Failed to disable interrupts\\n\");\n+\n+\t/* Registered before the controller and the IRQ so devres ordering runs\n+\t * it after free_irq() and pse_controller_unregister(), and so a failed\n+\t * probe cannot leave ports running detection with no driver bound.\n+\t */\n+\tret = devm_add_action_or_reset(\u0026client-\u003edev, ltc4266_teardown, ltc4266);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tltc4266-\u003epcdev.owner = THIS_MODULE;\n+\tltc4266-\u003epcdev.ops = \u0026ltc4266_ops;\n+\tltc4266-\u003epcdev.dev = \u0026client-\u003edev;\n+\tltc4266-\u003epcdev.types = ETHTOOL_PSE_C33;\n+\tltc4266-\u003epcdev.nr_lines = LTC4266_MAX_PORTS;\n+\tltc4266-\u003epcdev.supp_budget_eval_strategies = PSE_BUDGET_EVAL_STRAT_STATIC;\n+\n+\tret = devm_pse_controller_register(ltc4266-\u003edev, \u0026ltc4266-\u003epcdev);\n+\tif (ret)\n+\t\treturn dev_err_probe(\u0026client-\u003edev, ret,\n+\t\t\t\t     \"Failed to register PSE controller\\n\");\n+\n+\t/* Enable the interrupt pin, and only report detect events on\n+\t * change (detchg) so idle ports continuously re-running\n+\t * detection in semi-auto mode don't flood the host with a\n+\t * detect event every cycle.\n+\t */\n+\tret = regmap_update_bits(ltc4266-\u003eregmap, LTC4266_REG_MCONF,\n+\t\t\t\t LTC4266_MCONF_INTERRUPT_ENABLE | LTC4266_MCONF_DETCHG,\n+\t\t\t\t LTC4266_MCONF_INTERRUPT_ENABLE | LTC4266_MCONF_DETCHG);\n+\tif (ret)\n+\t\treturn dev_err_probe(\u0026client-\u003edev, ret,\n+\t\t\t\t     \"Failed to configure interrupts\\n\");\n+\n+\tret = devm_pse_irq_helper(\u0026ltc4266-\u003epcdev, client-\u003eirq,\n+\t\t\t\t  0, \u0026irq_desc);\n+\tif (ret)\n+\t\treturn dev_err_probe(\u0026client-\u003edev, ret,\n+\t\t\t\t     \"Failed to register PSE IRQ\\n\");\n+\n+\t/* Unmask the chip interrupt sources now the handler is ready. */\n+\tret = ltc4266_enable_interrupts(ltc4266);\n+\tif (ret)\n+\t\treturn dev_err_probe(\u0026client-\u003edev, ret,\n+\t\t\t\t     \"Failed to enable interrupts\\n\");\n+\n+\treturn 0;\n+}\n+\n+static const struct i2c_device_id ltc4266_id[] = {\n+\t{.name = \"ltc4266\"},\n+\t{ }\n+};\n+MODULE_DEVICE_TABLE(i2c, ltc4266_id);\n+\n+static const struct of_device_id ltc4266_of_match[] = {\n+\t{ .compatible = \"lltc,ltc4266\" },\n+\t{ }\n+};\n+MODULE_DEVICE_TABLE(of, ltc4266_of_match);\n+\n+static struct i2c_driver ltc4266_driver = {\n+\t.driver\t\t= {\n+\t\t.name\t= \"ltc4266\",\n+\t\t.of_match_table = ltc4266_of_match,\n+\t},\n+\t.probe\t\t= ltc4266_probe,\n+\t.id_table\t= ltc4266_id,\n+};\n+module_i2c_driver(ltc4266_driver);\n+\n+MODULE_AUTHOR(\"Kyle Swenson \u003ckyle.swenson@est.tech\u003e\");\n+MODULE_DESCRIPTION(\"LTC4266 PoE PSE Controller Driver\");\n+MODULE_LICENSE(\"GPL\");\n"
}

2/1 2026/09/29 00:47 agent patch-evaluator
1m Model:
core

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch adds a new driver for the Linear Technology LTC4266 Power Sourcing Equipment (PSE) controller over I2C (drivers/net/pse-pd/ltc4266.c) along with its Device Tree bindings. This driver requires physical LTC4266 hardware on an I2C bus with an assigned interrupt and devicetree description, which is not present or emulated in standard virtualized environments (QEMU/GCE on amd64). As such, the code is structurally unreachable for fuzzing.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 3cdeae96b3e0ba727e8ac5c36046dfc4901af5e3
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Sep 29 00:47:24 2026 +0000

    syz-cluster: applied patch under review

diff --git a/Documentation/devicetree/bindings/net/pse-pd/lltc,ltc4266.yaml b/Documentation/devicetree/bindings/net/pse-pd/lltc,ltc4266.yaml
new file mode 100644
index 0000000000000..e11c9d601eee9
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/pse-pd/lltc,ltc4266.yaml
@@ -0,0 +1,180 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/pse-pd/lltc,ltc4266.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Linear Technology LTC4266 Power Sourcing Equipment controller
+
+maintainers:
+  - Kyle Swenson <kyle.swenson@est.tech>
+
+allOf:
+  - $ref: pse-controller.yaml#
+
+properties:
+  compatible:
+    enum:
+      - lltc,ltc4266
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  channels:
+    type: object
+    additionalProperties: false
+    description:
+      Defines the 4 physical delivery channels on the controller that can be
+      referenced by PSE PIs through their "pairsets" property. The actual port
+      matrix mapping is created when PSE PIs reference these channels in their
+      pairsets.
+
+    properties:
+      '#address-cells':
+        const: 1
+
+      '#size-cells':
+        const: 0
+
+    patternProperties:
+      '^channel@[0-3]$':
+        type: object
+        additionalProperties: false
+
+        properties:
+          reg:
+            maxItems: 1
+
+          sense-resistor-micro-ohms:
+            description: Sense resistor connected to the channel's MOSFET, used
+              for current measurement and for overcurrent detection. The I_CUT
+              and I_LIM register encodings depend on which value is fitted, so
+              a wrong value programs the wrong thresholds.
+            enum: [250000, 500000]
+
+        required:
+          - reg
+          - sense-resistor-micro-ohms
+
+    required:
+      - '#address-cells'
+      - '#size-cells'
+
+  pse-pis:
+    type: object
+    additionalProperties: false
+
+    properties:
+      '#address-cells':
+        const: 1
+
+      '#size-cells':
+        const: 0
+
+    patternProperties:
+      '^pse-pi@[0-3]$':
+        type: object
+        additionalProperties: true
+        properties:
+          pairsets:
+            description: The LTC4266 delivers power to a PI over a single
+              pairset, driven by one of the controller's four channels. There
+              is no 4-pair mode spreading a PI over two channels, so exactly
+              one channel phandle is expected.
+            maxItems: 1
+          pairset-names:
+            maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - channels
+  - pse-pis
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      ethernet-pse@2f {
+        compatible = "lltc,ltc4266";
+        reg = <0x2f>;
+        interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
+        interrupt-parent = <&gpio>;
+
+        channels {
+          #address-cells = <1>;
+          #size-cells = <0>;
+
+          phys0: channel@0 {
+            reg = <0>;
+            sense-resistor-micro-ohms = <500000>;
+          };
+
+          phys1: channel@1 {
+            reg = <1>;
+            sense-resistor-micro-ohms = <500000>;
+          };
+
+          phys2: channel@2 {
+            reg = <2>;
+            sense-resistor-micro-ohms = <500000>;
+          };
+
+          phys3: channel@3 {
+            reg = <3>;
+            sense-resistor-micro-ohms = <500000>;
+          };
+        };
+
+        pse-pis {
+          #address-cells = <1>;
+          #size-cells = <0>;
+
+          pse_pi0: pse-pi@0 {
+            reg = <0>;
+            #pse-cells = <0>;
+            pairset-names = "alternative-a";
+            pairsets = <&phys0>;
+            polarity-supported = "MDI";
+            vpwr-supply = <&vreg_pse>;
+          };
+
+          pse_pi1: pse-pi@1 {
+            reg = <1>;
+            #pse-cells = <0>;
+            pairset-names = "alternative-a";
+            pairsets = <&phys1>;
+            polarity-supported = "MDI";
+            vpwr-supply = <&vreg_pse>;
+          };
+
+          pse_pi2: pse-pi@2 {
+            reg = <2>;
+            #pse-cells = <0>;
+            pairset-names = "alternative-a";
+            pairsets = <&phys2>;
+            polarity-supported = "MDI";
+            vpwr-supply = <&vreg_pse>;
+          };
+
+          pse_pi3: pse-pi@3 {
+            reg = <3>;
+            #pse-cells = <0>;
+            pairset-names = "alternative-a";
+            pairsets = <&phys3>;
+            polarity-supported = "MDI";
+            vpwr-supply = <&vreg_pse>;
+          };
+        };
+      };
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index 6de1ff058db64..3f7226d049839 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15533,6 +15533,13 @@ W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/devicetree/bindings/leds/adi,ltc3220.yaml
 F:	drivers/leds/leds-ltc3220.c
 
+LTC4266 PSE CONTROLLER DRIVER
+M:	Kyle Swenson <kyle.swenson@est.tech>
+L:	netdev@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/net/pse-pd/lltc,ltc4266.yaml
+F:	drivers/net/pse-pd/ltc4266.c
+
 LTC4282 HARDWARE MONITOR DRIVER
 M:	Nuno Sa <nuno.sa@analog.com>
 L:	linux-hwmon@vger.kernel.org
diff --git a/drivers/net/pse-pd/Kconfig b/drivers/net/pse-pd/Kconfig
index a0f2ae668c67c..844c1b237df7b 100644
--- a/drivers/net/pse-pd/Kconfig
+++ b/drivers/net/pse-pd/Kconfig
@@ -48,6 +48,17 @@ config PSE_REGULATOR
 	  Sourcing Equipment without automatic classification support. For
 	  example for basic implementation of PoDL (802.3bu) specification.
 
+config PSE_LTC4266
+	tristate "LTC4266 PSE controller"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  This module provides support for LTC4266 regulator based Ethernet
+	  Power Sourcing Equipment.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ltc4266.
+
 config PSE_PD692X0
 	tristate "PD692X0 PSE controller"
 	depends on I2C
diff --git a/drivers/net/pse-pd/Makefile b/drivers/net/pse-pd/Makefile
index 9cca5900fe342..c3fe4efcf1de4 100644
--- a/drivers/net/pse-pd/Makefile
+++ b/drivers/net/pse-pd/Makefile
@@ -3,6 +3,7 @@
 
 obj-$(CONFIG_PSE_CONTROLLER) += pse_core.o
 
+obj-$(CONFIG_PSE_LTC4266) += ltc4266.o
 obj-$(CONFIG_PSE_REALTEK_MCU) += realtek-pse-mcu-core.o
 obj-$(CONFIG_PSE_REALTEK_MCU_I2C) += realtek-pse-mcu-i2c.o
 obj-$(CONFIG_PSE_REALTEK_MCU_UART) += realtek-pse-mcu-uart.o
diff --git a/drivers/net/pse-pd/ltc4266.c b/drivers/net/pse-pd/ltc4266.c
new file mode 100644
index 0000000000000..37dea467811a4
--- /dev/null
+++ b/drivers/net/pse-pd/ltc4266.c
@@ -0,0 +1,1386 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Linear LTC4266 PoE PSE Controller
+ *
+ * Original work:
+ *    Copyright 2019 Cradlepoint Technology, Inc.
+ *    Cradlepoint Technology, Inc.  <source@cradlepoint.com>
+ *
+ * Re-written in 2026:
+ *    Copyright 2026 Ericsson Software Technology
+ *    Kyle Swenson <kyle.swenson@est.tech>
+ *
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/ethtool.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pse-pd/pse.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#define LTC4266_MAX_PORTS			4
+
+/* The minimum and maximum here depend on the resolution of the I_CUT field,
+ * which is 18.75mA.  To get 1000mW with a 50V port voltage, we need 20mA; but
+ * with 18.75 mA steps we end up with a current limit of 37.5 mA resulting in
+ * an actual power limit of 1875mW.
+ */
+#define LTC4266_PW_LIMIT_MAX			30000
+#define LTC4266_PW_LIMIT_MIN			1000
+
+/* Nominal PI voltage reported when the port is not delivering power and no
+ * "vpwr-supply" is described in the device tree: V_Port_PSE min for a Type 2
+ * PSE. IEEE 802.3-2022 Table 33-11 item 1 specifies the POWER_ON state output
+ * voltage as 50.0 V to 57.0 V for a Type 2 PSE, 44.0 V to 57.0 V for Type 1.
+ */
+#define LTC4266_VPORT_NOMINAL_UV		50000000
+
+/* Register definitions */
+#define LTC4266_REG_INTSTAT			0x00
+#define LTC4266_REG_INTMASK			0x01
+#define LTC4266_REG_PWREVN_COR			0x03
+#define LTC4266_REG_DETEVN_COR			0x05
+#define LTC4266_REG_FLTEVN_COR			0x07
+#define LTC4266_REG_TSEVN_COR			0x09
+#define LTC4266_REG_SUPEVN_COR			0x0B
+#define LTC4266_REG_STAT(_p)			(0x0C + (_p))
+#define LTC4266_REG_STATPWR			0x10
+#define LTC4266_REG_OPMD			0x12
+#define LTC4266_REG_DISENA			0x13 /* Disconnect detect enable */
+#define LTC4266_REG_MCONF			0x17
+#define LTC4266_REG_DETPB			0x18 /*PB means "push button" */
+#define LTC4266_REG_PWRPB			0x19
+#define LTC4266_REG_RSTPB			0x1A
+#define LTC4266_REG_ID				0x1B
+#define LTC4266_REG_TLIM12			0x1E
+#define LTC4266_REG_TLIM34			0x1F
+#define LTC4266_REG_IPLSB(_p)			(0x30 | ((_p) << 2))
+#define LTC4266_REG_VPLSB(_p)			(LTC4266_REG_IPLSB(_p) + 2)
+#define LTC4266_REG_HPEN			0x44
+#define LTC4266_REG_HPMD(_p)			(0x46 + (5 * (_p)))
+#define LTC4266_REG_ICUT_HP(_p)			(LTC4266_REG_HPMD(_p) + 1)
+#define LTC4266_REG_ILIM(_p)			(LTC4266_REG_HPMD(_p) + 2)
+
+/* Register field definitions */
+
+/* LTC4266_REG_INTSTAT and LTC4266_REG_INTMASK */
+#define LTC4266_INT_TSTART			BIT(6)
+#define LTC4266_INT_TCUT			BIT(5)
+#define LTC4266_INT_CLASS			BIT(4)
+#define LTC4266_INT_DETECT			BIT(3)
+#define LTC4266_INT_DIS				BIT(2)
+#define LTC4266_INT_PWRGD			BIT(1)
+
+/* Per-port event bits within the CoR event registers. Every per-port event
+ * register splits its 8 bits into a per-port low nibble and a per-port high
+ * nibble:
+ * pwrevn (03h): LO = power on/off change, HI = power good change
+ * detevn (05h): LO = detection complete, HI = classification complete
+ * fltevn (07h): LO = tCUT overcurrent,   HI = tDIS DC disconnect
+ * tsevn  (09h): LO = tSTART overcurrent, HI = tLIM current-limit timeout
+ */
+#define LTC4266_EVN_HI(_p)			BIT((_p) + 4)	/* ports 0-3 */
+#define LTC4266_EVN_LO(_p)			BIT(_p)		/* ports 0-3 */
+
+/* statp<n> (0Ch-0Fh) detection result, and the "Signature Good" value */
+#define LTC4266_PORT_CLASS(_stat)		FIELD_GET(GENMASK(6, 4), (_stat))
+#define LTC4266_PORT_DETECT(_stat)		FIELD_GET(GENMASK(2, 0), (_stat))
+#define LTC4266_DETECT_GOOD			0x4
+
+/* LTC4266_REG_STATPWR */
+#define LTC4266_STATPWR_PG(_p)			BIT((_p) + 4)
+#define LTC4266_STATPWR_PE(_p)			BIT(_p)
+
+/* LTC4266_REG_OPMD
+ * There are three other operation modes possible this
+ * driver doesn't support and so aren't defined.  The one supported mode,
+ * OPMD_SEMI, means that a port will continuously detect and classify devices,
+ * but will not power the device until instructed to do so.
+ */
+#define LTC4266_OPMD_SEMI			2
+#define LTC4266_TWO_BIT_WORD_OFFSET(_v, _p)	((_v) << ((_p) * 2))
+#define LTC4266_TWO_BIT_WORD_MASK(_p)		LTC4266_TWO_BIT_WORD_OFFSET(0x03, (_p))
+
+/* LTC4266_REG_MCONF */
+#define LTC4266_MCONF_INTERRUPT_ENABLE		BIT(7)
+/* Only report a detect event when the result changes, not every cycle */
+#define LTC4266_MCONF_DETCHG			BIT(6)
+
+/* LTC4266_REG_DETPB */
+#define LTC4266_DETPB_CLASS_ENABLE(_p)		BIT((_p) + 4)
+#define LTC4266_DETPB_DETECT_ENABLE(_p)		BIT(_p)
+
+/* LTC4266_REG_RSTPB */
+#define LTC4266_RSTPB_INTCLR			BIT(7)
+#define LTC4266_RSTPB_PINCLR			BIT(6)
+#define LTC4266_RSTPB_RSTALL			BIT(4)
+#define LTC4266_RSTPB_RSTPORTS			GENMASK(3, 0)
+
+/* LTC4266_REG_ID */
+#define LTC4266_ID				0x64
+
+/* LTC4266_REG_TLIM* */
+#define LTC4266_TLIM_VALUE			0x01
+
+/* Current-sense scaling, in nA per LSB, dependent on the sense resistor. */
+#define LTC4266_IP_NA_PER_LSB_RSENSE_025	122070	/* 122.07 uA/LSB */
+#define LTC4266_IP_NA_PER_LSB_RSENSE_050	61035	/* 61.035 uA/LSB */
+
+/* Voltage-sense scaling: 5.835 mV == 5835 uV per LSB. */
+#define LTC4266_VP_UV_PER_LSB			5835
+
+/* LTC4266_REG_HPEN, enable "High Power" mode (Type 2, Class 4) */
+#define LTC4266_HPEN(_p)			BIT(_p)
+
+/* LTC4266_REG_HPMD */
+#define LTC4266_HPMD_PONGEN			0x01
+
+/* LTC4266_REG_ICUT_HP.
+ * Set if the sense resistor specified in DT is 0.25 Ohm to have accurately
+ * scaled ICUT thresholds.
+ */
+#define LTC4266_ICUT_RSENSE_025_OHM		BIT(7)
+
+/* To keep the ICUT resolution at a constant 18.75 mA, for the 0.25 Ohm sense
+ * case we should also set this ICUT_RANGE
+ */
+#define LTC4266_ICUT_RANGE			BIT(6)
+
+/* I_CUT is programmed in a 6-bit field; each step is 18.75 mA (18750 uA). */
+#define LTC4266_ICUT_STEP_UA			18750
+#define LTC4266_ICUT_MASK			GENMASK(5, 0)
+
+/* Cap I_CUT at the suggested value for a Type 2 PD at 638mA */
+#define LTC4266_ICUT_MAX_MA			638
+#define LTC4266_ICUT_MAX_STEPS			34
+
+/* Recommended lim<n> settings from datasheet Table 5.
+ *
+ *	I_LIM (mA)	RSENSE = 0.5 Ohm	RSENSE = 0.25 Ohm
+ *	425 (Type 1)	0x00			0x80
+ *	850 (Type 2)	0x40			0xC0
+ */
+#define LTC4266_ILIM_TYPE1_RSENSE_050		0x00
+#define LTC4266_ILIM_TYPE1_RSENSE_025		0x80
+#define LTC4266_ILIM_TYPE2_RSENSE_050		0x40
+#define LTC4266_ILIM_TYPE2_RSENSE_025		0xC0
+
+enum {
+	LTC4266_READ_CURRENT = 0,
+	LTC4266_READ_VOLTAGE = 2
+};
+
+/* Map LTC4266 Classification result to PD class.  Note for a PD that has a
+ * valid detect signature, but doesn't produce a classification signature is
+ * still a valid PD.  The LTC4266 indicates this with 0x06 in the statp<n>
+ * register and calls it "Class 0".  This is a different state than when
+ * statp<n> indicates 0, which means classification isn't complete.  This maps
+ * the result to either an errno or classification value suitable for use up
+ * the stack.
+ */
+static const int ltc4266_class_map[] = {
+	-EAGAIN, /* Classification is incomplete */
+	1,
+	2,
+	3,
+	4,
+	-EINVAL,
+	0,
+	-ERANGE
+};
+
+/* Map a PD Class to I_CUT thresholds from the LTC4266 datasheet Table 2 */
+static const int ltc4266_class_to_icut[] = {
+	375,
+	112,
+	206,
+	375,
+	638
+};
+
+/* Per-class power budget at the PSE PI in mW, indexed by class (0-4).
+ * Classes 0-3 are the P_Class values from Table 33-7 in the IEEE802.3
+ * standard, which Note 1 defines as the minimum power a PSE must supply at the
+ * PI for that class (the PD input power maxima are in Table 33-18). Class 4 is
+ * P_Type, which Table 33-11 item 12 defines as I_Cable x V_Port_PSE min:
+ * 0.600 A x 50.0 V for a Type 2 PSE, with I_Cable from Table 33-1.
+ *
+ * Class 0 and Class 3 have the same value because a device without a classification
+ * signature (Class 0) has to be assumed to consume up to the maximum power for
+ * a Type 1 PD (Class 3).
+ */
+static const int ltc4266_class_pw[] = {
+	15400,	/* Class 0 */
+	4000,	/* Class 1 */
+	7000,	/* Class 2 */
+	15400,	/* Class 3 */
+	30000,	/* Class 4 (Type 2) */
+};
+
+enum sense_resistor {
+	LTC4266_RSENSE_500, /* Rsense 0.5 Ohm */
+	LTC4266_RSENSE_250 /* Rsense 0.25 Ohm */
+};
+
+struct ltc4266;
+
+/**
+ * struct ltc4266_port - per-PSE-PI context
+ *
+ * @ltc4266: the controller owning this port
+ * @chan: index of the LTC4266 delivery channel backing this PI, established
+ *	  from the PI pairset phandle by ltc4266_map_pis().  All register
+ *	  addressing uses this member.
+ * @rsense: sense resistor on @chan, used to scale current readings and
+ *	    to pick the I_CUT and I_LIM encodings.
+ * @pw_limit: Admin-configured power limit in mW, always valid: it defaults to
+ *	      LTC4266_PW_LIMIT_MAX, the most this controller can deliver, until
+ *	      pi_set_pw_limit() lowers it.
+ * @vpwr_uv: nominal PI voltage in uV, read once from the PI's "vpwr-supply" at
+ *	     probe, or LTC4266_VPORT_NOMINAL_UV when the PI does not describe
+ *	     one. Reported while the port is not delivering power, when the
+ *	     controller has nothing to measure.
+ */
+struct ltc4266_port {
+	struct ltc4266 *ltc4266;
+	u8 chan;
+	enum sense_resistor rsense;
+	int pw_limit;
+	int vpwr_uv;
+};
+
+/**
+ * struct ltc4266 - LTC4266 controller context
+ *
+ * @client: the I2C client
+ * @regmap: register map of @client
+ * @ports: table of PSE PI contexts, indexed by PSE PI id. A NULL entry is a PI
+ *	   that is not described in the device tree and therefore has no
+ *	   channel mapped.
+ * @dev: the underlying device
+ * @np: device node of @dev
+ * @pcdev: the PSE controller registered with the PSE core
+ */
+struct ltc4266 {
+	struct i2c_client *client;
+	struct regmap *regmap;
+	struct ltc4266_port *ports[LTC4266_MAX_PORTS];
+	struct device *dev;
+	struct device_node *np;
+	struct pse_controller_dev pcdev;
+};
+
+static struct ltc4266_port *ltc4266_pi_port(struct pse_controller_dev *pcdev,
+					    int id)
+{
+	struct ltc4266 *ltc4266 = container_of(pcdev, struct ltc4266, pcdev);
+
+	return ltc4266->ports[id];
+}
+
+static int ltc4266_read_iv(struct ltc4266_port *port, u8 iv)
+{
+	struct ltc4266 *ltc4266 = port->ltc4266;
+	unsigned int lsb, msb;
+	unsigned int statpwr;
+	int lsb_reg;
+	int result;
+	u64 ivbits;
+
+	result = regmap_read(ltc4266->regmap, LTC4266_REG_STATPWR, &statpwr);
+	if (result < 0)
+		return result;
+
+	/* LTC4266 IV readings are only meaningful while the port is delivering
+	 * power. When the PG (power good) bit is not set, the port is
+	 * delivering nothing, so report 0 rather than an error: returning an
+	 * errno here would abort pse_ethtool_get_status() and fail the whole
+	 * "ethtool --show-pse" query for an otherwise perfectly readable port.
+	 * Callers that must not report 0 (ltc4266_port_voltage_uv()) substitute
+	 * a nominal value.
+	 */
+	if (!(statpwr & LTC4266_STATPWR_PG(port->chan)))
+		return 0;
+
+	/* Since iv is either 0 (to read current) or 2 to (read voltage), we
+	 * can get the LSB reg by adding
+	 */
+	lsb_reg = LTC4266_REG_IPLSB(port->chan) + iv;
+
+	result = regmap_read(ltc4266->regmap, lsb_reg, &lsb);
+	if (result < 0)
+		return result;
+
+	result = regmap_read(ltc4266->regmap, lsb_reg + 1, &msb);
+	if (result < 0)
+		return result;
+
+	ivbits = (msb << 8) | lsb;
+
+	if (iv == LTC4266_READ_CURRENT)
+		if (port->rsense == LTC4266_RSENSE_250)
+			result = DIV_ROUND_CLOSEST_ULL(ivbits * LTC4266_IP_NA_PER_LSB_RSENSE_025,
+						       1000);
+		else
+			result = DIV_ROUND_CLOSEST_ULL(ivbits * LTC4266_IP_NA_PER_LSB_RSENSE_050,
+						       1000);
+	else
+		result = ivbits * LTC4266_VP_UV_PER_LSB;
+
+	return result;
+}
+
+/* Voltage at the PI in uV, if the PI is currently powering a device;
+ * otherwise, return the nominal voltage
+ */
+static int ltc4266_port_voltage_uv(struct ltc4266_port *port)
+{
+	int uv = ltc4266_read_iv(port, LTC4266_READ_VOLTAGE);
+
+	if (uv < 0)
+		return uv;
+
+	return uv ? uv : port->vpwr_uv;
+}
+
+/**
+ * ltc4266_port_set_ilim - Set the active current limit (ILIM) for a port
+ * @port: the port to configure
+ * @class: the detected PD class (0-4)
+ *
+ * Given the PD class, configure the active current limit for a particular
+ * channel.  The LTC4266 will actively enforce this current limit using the
+ * sense resistor for the channel. The values written are from the LTC4266
+ * Datasheet, Table 5, and correspond to 425mA for Type I PDs, and 850mA for
+ * Type 2 PDs (per the datasheet requirement for IEEE compliance).
+ *
+ * IEEE Std 802.3-2022, Table 33-11 specifies ILIM parameter ranges:
+ * - For Type 1 PSE operation (PD Classes 0-3):
+ * The minimum ILIM is 0.400A. This driver uses 425mA. This value fits
+ * within typical Type 1 ILIM specifications (e.g., 0.400A min to
+ * around 0.440A-0.500A max for the programmed steady-state limit).
+ *
+ * - For Type 2 PSE operation (typically PD Class 4):
+ * The minimum ILIM is 1.14 * ICable (or ~1.05 * IPort_max from other
+ * interpretations, e.g., ~0.630A to ~0.684A). This driver uses 850mA.
+ * This value meets the minimum requirement and is a supported operational
+ * current limit for high power modes in the LTC4266.
+ *
+ * The overall PSE current output must not exceed the time-dependent PSE
+ * upperbound template, IPSEUT(t), described in IEEE Std 802.3-2022,
+ * Equation (33-6). The programmed ILIM values (425mA/850mA) serve as the
+ * long-term current limit (Ilimmin segment of IPSEUT(t)) and are well
+ * within the higher short-term current allowances of that template (e.g., 1.75A).
+ *
+ * Returns: 0 on success or a negative errno.
+ */
+static int ltc4266_port_set_ilim(struct ltc4266_port *port, int class)
+{
+	bool rsense_250 = port->rsense == LTC4266_RSENSE_250;
+	u8 ilim;
+
+	if (class > 4 || class < 0)
+		return -EINVAL;
+
+	if (class < 4)
+		ilim = rsense_250 ? LTC4266_ILIM_TYPE1_RSENSE_025 :
+				    LTC4266_ILIM_TYPE1_RSENSE_050;
+	else
+		ilim = rsense_250 ? LTC4266_ILIM_TYPE2_RSENSE_025 :
+				    LTC4266_ILIM_TYPE2_RSENSE_050;
+
+	return regmap_write(port->ltc4266->regmap,
+			    LTC4266_REG_ILIM(port->chan), ilim);
+}
+
+static int ltc4266_port_set_icut(struct ltc4266_port *port, int icut)
+{
+	u8 val;
+
+	if (icut > LTC4266_ICUT_MAX_MA)
+		return -ERANGE;
+
+	val = min(DIV_ROUND_UP(icut * 1000, LTC4266_ICUT_STEP_UA),
+		  LTC4266_ICUT_MAX_STEPS) & LTC4266_ICUT_MASK;
+
+	if (port->rsense == LTC4266_RSENSE_250)
+		val |= LTC4266_ICUT_RSENSE_025_OHM | LTC4266_ICUT_RANGE;
+
+	return regmap_write(port->ltc4266->regmap,
+			    LTC4266_REG_ICUT_HP(port->chan), val);
+}
+
+/**
+ * ltc4266_pw_limit_to_icut - Convert an admin power limit to an I_CUT threshold
+ * @port: the port to convert for
+ * @max_mw: the power limit in mW
+ * @class: the detected PD class (0-4)
+ *
+ * The LTC4266 only enforces a current threshold, so a power limit has to be
+ * divided by the port voltage.
+ *
+ * Lowering I_CUT below the class threshold is legitimate: IEEE 802.3-2022
+ * 33.2.7.10 defines P_Class as either the class power or "PSE allocated power
+ * ... added to the channel power loss", and an administrative limit is that
+ * allocated power.
+ *
+ * Raising it above the class threshold is not. Table 33-11 item 7 bounds I_CUT
+ * at I_LIM, and ltc4266_port_set_ilim() picks I_LIM from the class: 425 mA for
+ * Type 1, 850 mA for Type 2. An administrative limit of LTC4266_PW_LIMIT_MAX
+ * asks for 600 mA at the nominal port voltage, which would exceed I_LIM on a
+ * Type 1 class. Capping at the class threshold from datasheet Table 2 keeps
+ * I_CUT under I_LIM for every class; the LTC4266_ICUT_MAX_MA check in
+ * ltc4266_port_set_icut() only covers Type 2.
+ *
+ * Return: the threshold in mA, or a negative errno if the voltage read failed.
+ */
+static int ltc4266_pw_limit_to_icut(struct ltc4266_port *port, int max_mw,
+				    int class)
+{
+	int uv = ltc4266_port_voltage_uv(port);
+	int icut_mA;
+
+	if (uv < 0)
+		return uv;
+
+	/* 30000 mW x 1000000 overflows an int */
+	icut_mA = DIV_ROUND_UP_ULL((u64)max_mw * 1000000, uv);
+
+	return min(ltc4266_class_to_icut[class], icut_mA);
+}
+
+static int ltc4266_port_delivering(struct ltc4266_port *port)
+{
+	unsigned int result;
+	int ret;
+
+	ret = regmap_read(port->ltc4266->regmap, LTC4266_REG_STATPWR, &result);
+	if (ret < 0)
+		return ret;
+
+	return !!((result & LTC4266_STATPWR_PG(port->chan)) &&
+		  (result & LTC4266_STATPWR_PE(port->chan)));
+}
+
+static int ltc4266_port_init(struct ltc4266_port *port)
+{
+	struct ltc4266 *ltc4266 = port->ltc4266;
+	u8 chan = port->chan;
+	u8 tlim_shift;
+	u8 tlim_mask;
+	u8 tlim_reg;
+	int ret;
+
+	/* Reset the port */
+	ret = regmap_write(ltc4266->regmap, LTC4266_REG_RSTPB, BIT(chan));
+	if (ret < 0)
+		return ret;
+
+	/* Set Semi-auto mode */
+	ret = regmap_update_bits(port->ltc4266->regmap, LTC4266_REG_OPMD,
+				 LTC4266_TWO_BIT_WORD_MASK(port->chan),
+				 LTC4266_TWO_BIT_WORD_OFFSET(LTC4266_OPMD_SEMI,
+							     port->chan));
+	if (ret < 0)
+		return ret;
+
+	/* Enable high power mode on the port (for Type 2 PD support) */
+	ret = regmap_update_bits(ltc4266->regmap, LTC4266_REG_HPEN,
+				 LTC4266_HPEN(chan), LTC4266_HPEN(chan));
+	if (ret < 0)
+		return ret;
+
+	/* Enable 2-event classification (IEEE 802.3-2022, Clause 33), which the
+	 * datasheet refers to as "Ping-Pong" classification.
+	 */
+	ret = regmap_update_bits(ltc4266->regmap, LTC4266_REG_HPMD(chan),
+				 LTC4266_HPMD_PONGEN, LTC4266_HPMD_PONGEN);
+	if (ret < 0)
+		return ret;
+
+	if (chan <= 1)
+		tlim_reg = LTC4266_REG_TLIM12;
+	else
+		tlim_reg = LTC4266_REG_TLIM34;
+
+	/* Each tlim register packs two ports: the even port in the low nibble
+	 * and the odd port in the high nibble. Shift both the value and the
+	 * mask into the correct nibble.
+	 */
+	if (chan & BIT(0))
+		tlim_shift = 4;
+	else
+		tlim_shift = 0;
+
+	tlim_mask = GENMASK(3, 0) << tlim_shift;
+
+	ret = regmap_update_bits(ltc4266->regmap, tlim_reg,
+				 tlim_mask, LTC4266_TLIM_VALUE << tlim_shift);
+	if (ret < 0)
+		return ret;
+
+	/* Enable disconnect detect. */
+	ret = regmap_update_bits(ltc4266->regmap, LTC4266_REG_DISENA,
+				 BIT(chan), BIT(chan));
+	if (ret < 0)
+		return ret;
+
+	/* Enable detection (low nibble), classification (high nibble) on the port */
+	ret = regmap_write(ltc4266->regmap, LTC4266_REG_DETPB,
+			   LTC4266_DETPB_CLASS_ENABLE(chan) |
+			   LTC4266_DETPB_DETECT_ENABLE(chan));
+
+	if (ret < 0)
+		return ret;
+
+	dev_dbg(ltc4266->dev, "Channel %d has been initialized\n", chan);
+	return 0;
+}
+
+/* Read the port's classification result and return a class 0-4 or an error if
+ * the result isn't valid.
+ */
+static int ltc4266_port_get_class(struct ltc4266_port *port)
+{
+	struct ltc4266 *ltc4266 = port->ltc4266;
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(ltc4266->regmap, LTC4266_REG_STAT(port->chan), &val);
+	if (ret < 0) {
+		dev_warn(ltc4266->dev, "Failed to read status register, err=%d\n", ret);
+		return ret;
+	}
+
+	/* Can't have a valid classification result if we've not yet had a good
+	 * detection result.
+	 */
+	if (LTC4266_PORT_DETECT(val) != LTC4266_DETECT_GOOD)
+		return -EINVAL;
+
+	ret = ltc4266_class_map[LTC4266_PORT_CLASS(val)];
+	return ret;
+}
+
+/* Maximum power the classified PD is allowed. It does not depend on the port
+ * being powered, but it does depend on the port having a PD attached and being
+ * enabled to the point of running classification and detection cycles.
+ */
+static int ltc4266_port_max_pw(struct ltc4266_port *port)
+{
+	int class = ltc4266_port_get_class(port);
+
+	if (class < 0)
+		return class;
+
+	return ltc4266_class_pw[class];
+}
+
+static int ltc4266_pi_get_pw_status(struct pse_controller_dev *pcdev, int id,
+				    struct pse_pw_status *pw_status)
+{
+	struct ltc4266_port *port = ltc4266_pi_port(pcdev, id);
+	int ret;
+
+	ret = ltc4266_port_delivering(port);
+	if (ret < 0)
+		return ret;
+
+	if (ret)
+		pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING;
+	else
+		pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING;
+
+	return 0;
+}
+
+/* With the static budget evaluation strategy the PSE core calls this only
+ * after a PD has been classified and the power budget has been allocated.
+ * Program the current limit for the classified PD, reduced if an admin power
+ * limit asks for less than the class is entitled to, and apply power.
+ */
+static int ltc4266_pi_enable(struct pse_controller_dev *pcdev, int id)
+{
+	struct ltc4266_port *port = ltc4266_pi_port(pcdev, id);
+	int class, icut, ret;
+
+	class = ltc4266_port_get_class(port);
+	if (class < 0)
+		return class;
+
+	ret = ltc4266_port_set_ilim(port, class);
+	if (ret < 0)
+		return ret;
+
+	/* Derive the threshold from the admin limit against the class in front of
+	 * us now, since a different PD may have been plugged in since the limit
+	 * was set.
+	 */
+	icut = ltc4266_pw_limit_to_icut(port, port->pw_limit, class);
+	if (icut < 0)
+		return icut;
+
+	ret = ltc4266_port_set_icut(port, icut);
+	if (ret < 0)
+		return ret;
+
+	/* Apply power to the port. */
+	return regmap_write(port->ltc4266->regmap, LTC4266_REG_PWRPB,
+			    BIT(port->chan));
+}
+
+static int ltc4266_pi_disable(struct pse_controller_dev *pcdev, int id)
+{
+	struct ltc4266_port *port = ltc4266_pi_port(pcdev, id);
+
+	/* Resetting the port (RSTPB, issued at the start of ltc4266_port_init)
+	 * removes power, disables detection and classification, and clears the
+	 * port's status register. Re-init the port so that detection and
+	 * classification can happen again.
+	 */
+	return ltc4266_port_init(port);
+}
+
+static int ltc4266_pi_get_voltage(struct pse_controller_dev *pcdev, int id)
+{
+	return ltc4266_port_voltage_uv(ltc4266_pi_port(pcdev, id));
+}
+
+static int ltc4266_pi_get_admin_state(struct pse_controller_dev *pcdev, int id,
+				      struct pse_admin_state *admin_state)
+{
+	struct ltc4266_port *port = ltc4266_pi_port(pcdev, id);
+	unsigned int val;
+	int ret;
+
+	/* The PSE core uses this as the hardware admin state: whether power has
+	 * been commanded on, not whether it has finished coming up.
+	 * pse_pi_is_hw_enabled() decides from it which software-enabled PIs
+	 * still need power delivery attempted, so report the power-enable
+	 * nibble (peN), which the controller sets as soon as it applies power to
+	 * the port. The power-good nibble only sets once OUT has pulled down to
+	 * VEE, up to a tSTART later, and a port reported as not enabled for that
+	 * whole ramp is one the core will allocate power domain budget for a
+	 * second time. Actual delivery is reported by pi_get_pw_status().
+	 */
+	ret = regmap_read(port->ltc4266->regmap, LTC4266_REG_STATPWR, &val);
+	if (ret < 0)
+		return ret;
+
+	if (val & LTC4266_STATPWR_PE(port->chan))
+		admin_state->c33_admin_state =
+			ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED;
+	else
+		admin_state->c33_admin_state =
+			ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED;
+
+	return 0;
+}
+
+static int ltc4266_pi_get_pw_class(struct pse_controller_dev *pcdev, int id)
+{
+	int ret = ltc4266_port_get_class(ltc4266_pi_port(pcdev, id));
+
+	/* ltc4266_port_get_class will return either the class, or an errno.
+	 * Returning an errno from this function will mean the ethtool command
+	 * will abort with the error and not emit later useful information.
+	 * Additionally, returning 0 to indicate no classification signature
+	 * will cause the ethtool output to omit the "Power Class" attribute
+	 * entirely. Since the "Power Class" is expected to be returned here,
+	 * and class 0 and class 3 are equivalent in terms of power allocation,
+	 * we'll return 3.
+	 */
+
+	if (ret == 0)
+		ret = 3;
+	if (ret < 0)
+		ret = 0;
+	return ret;
+}
+
+/* Get the power requested by the PD before enabling the port: its
+ * classification power.
+ */
+static int ltc4266_pi_get_pw_req(struct pse_controller_dev *pcdev, int id)
+{
+	return ltc4266_port_max_pw(ltc4266_pi_port(pcdev, id));
+}
+
+static int ltc4266_pi_get_actual_pw(struct pse_controller_dev *pcdev, int id)
+{
+	struct ltc4266_port *port = ltc4266_pi_port(pcdev, id);
+	int uA, uV;
+
+	uA = ltc4266_read_iv(port, LTC4266_READ_CURRENT);
+	if (uA < 0)
+		return uA;
+
+	uV = ltc4266_read_iv(port, LTC4266_READ_VOLTAGE);
+	if (uV < 0)
+		return uV;
+
+	/* mW = uA * uV / 1000000000 */
+	return DIV_ROUND_CLOSEST_ULL((u64)uA * uV, 1000000000);
+}
+
+/* The range of administrative power limits this controller supports. It does
+ * not depend on the detected class: the class only bounds the I_CUT threshold
+ * that ltc4266_pw_limit_to_icut() programs.
+ */
+static int ltc4266_pi_get_pw_limit_ranges(struct pse_controller_dev *pcdev, int id,
+					  struct pse_pw_limit_ranges *pw_limit_ranges)
+{
+	struct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges;
+
+	c33_pw_limit_ranges = kzalloc_obj(*c33_pw_limit_ranges);
+	if (!c33_pw_limit_ranges)
+		return -ENOMEM;
+
+	c33_pw_limit_ranges[0].min = LTC4266_PW_LIMIT_MIN;
+	c33_pw_limit_ranges[0].max = LTC4266_PW_LIMIT_MAX;
+
+	pw_limit_ranges->c33_pw_limit_ranges = c33_pw_limit_ranges;
+
+	/* Return the number of ranges */
+	return 1;
+}
+
+/* Store the administrative power limit. The limit is independent of any PD in
+ * front of us, so accept it whether or not the port has classified anything;
+ * program I_CUT immediately when it has, so a change on a live port takes
+ * effect, and leave it to ltc4266_pi_enable() otherwise.
+ */
+static int ltc4266_pi_set_pw_limit(struct pse_controller_dev *pcdev,
+				   int id, int max_mw)
+{
+	struct ltc4266_port *port = ltc4266_pi_port(pcdev, id);
+	int class;
+	int icut;
+	int ret;
+
+	if (max_mw < LTC4266_PW_LIMIT_MIN || max_mw > LTC4266_PW_LIMIT_MAX) {
+		dev_err(port->ltc4266->dev, "power limit %d is out of range [%d, %d]\n",
+			max_mw, LTC4266_PW_LIMIT_MIN, LTC4266_PW_LIMIT_MAX);
+		return -ERANGE;
+	}
+
+	class = ltc4266_port_get_class(port);
+	if (class >= 0) {
+		icut = ltc4266_pw_limit_to_icut(port, max_mw, class);
+		if (icut < 0)
+			return icut;
+
+		ret = ltc4266_port_set_icut(port, icut);
+		if (ret < 0)
+			return ret;
+	}
+
+	port->pw_limit = max_mw;
+
+	return 0;
+}
+
+static int ltc4266_pi_get_pw_limit(struct pse_controller_dev *pcdev, int id)
+{
+	return ltc4266_pi_port(pcdev, id)->pw_limit;
+}
+
+/* Description of one delivery channel parsed out of the "channels" node. Used
+ * locally only during ltc4266_setup_pi_matrix()
+ */
+struct ltc4266_chan_desc {
+	struct device_node *np;
+	enum sense_resistor rsense;
+};
+
+static int ltc4266_get_of_channels(struct ltc4266 *ltc4266,
+				   struct ltc4266_chan_desc *chans)
+{
+	struct device_node *channels_node __free(device_node) =
+		of_get_child_by_name(ltc4266->np, "channels");
+	u32 chan_id, sense;
+	int ret;
+
+	if (!channels_node)
+		return dev_err_probe(ltc4266->dev, -EINVAL,
+				     "missing \"channels\" node\n");
+
+	for_each_child_of_node_scoped(channels_node, chan_node) {
+		if (!of_node_name_eq(chan_node, "channel"))
+			continue;
+
+		ret = of_property_read_u32(chan_node, "reg", &chan_id);
+		if (ret)
+			return dev_err_probe(ltc4266->dev, ret,
+					     "missing reg property in node %pOF\n",
+					     chan_node);
+
+		if (chan_id >= LTC4266_MAX_PORTS)
+			return dev_err_probe(ltc4266->dev, -EINVAL,
+					     "channel id %u is out of range in node %pOF\n",
+					     chan_id, chan_node);
+
+		if (chans[chan_id].np)
+			return dev_err_probe(ltc4266->dev, -EINVAL,
+					     "channel id %u is already used, please check the reg property in node %pOF\n",
+					     chan_id, chan_node);
+
+		ret = of_property_read_u32(chan_node, "sense-resistor-micro-ohms", &sense);
+		if (ret)
+			return dev_err_probe(ltc4266->dev, ret,
+					     "missing sense-resistor-micro-ohms property in node %pOF\n",
+					     chan_node);
+
+		if (sense == 250000)
+			chans[chan_id].rsense = LTC4266_RSENSE_250;
+		else if (sense == 500000)
+			chans[chan_id].rsense = LTC4266_RSENSE_500;
+		else
+			return dev_err_probe(ltc4266->dev, -EINVAL,
+					     "invalid sense resistor value %u in node %pOF\n",
+					     sense, chan_node);
+
+		chans[chan_id].np = of_node_get(chan_node);
+	}
+
+	return 0;
+}
+
+/* Find the channel a PI pairset phandle points at. */
+static int ltc4266_match_channel(const struct pse_pi_pairset *pairset,
+				 struct ltc4266_chan_desc *chans)
+{
+	int i;
+
+	for (i = 0; i < LTC4266_MAX_PORTS; i++)
+		if (pairset->np == chans[i].np)
+			return i;
+
+	return -ENODEV;
+}
+
+/*
+ * Nominal voltage of the rail feeding a PSE PI, taken from its "vpwr-supply".
+ * This is intended to work around the limitation on the LTC4266 where the port
+ * voltage is not valid until _after_ the port is powered.  Since the PSE core
+ * expects to read the port voltage when the port is not powered we need
+ * something non-zero to return to the core.
+ *
+ * In the event the regulator isn't present, or if the regulator doesn't have a
+ * nominal voltage available, we'll fall back and use 50V, which is the minimum
+ * allowed for a Type 2 PSE.
+ *
+ * Finally, we read this regulator voltage here instead of during
+ * ltc4266_pi_get_voltage to avoid deadlock.
+ *
+ * Return: the nominal voltage in uV, or LTC4266_VPORT_NOMINAL_UV if the PI
+ * describes no supply, its supply has not registered yet, or its voltage
+ * cannot be determined.
+ */
+static int ltc4266_pi_nominal_uv(struct ltc4266 *ltc4266, struct device_node *np)
+{
+	struct regulator *vpwr;
+	int uv;
+
+	vpwr = of_regulator_get_optional(ltc4266->dev, np, "vpwr");
+	if (IS_ERR(vpwr)) {
+		/* -ENODEV means the PI describes no vpwr-supply at all, which
+		 * is the case the fallback exists for. -EPROBE_DEFER means the
+		 * rail _is_ described but has not registered yet, so the
+		 * fallback is wrong for it. Asking for a probe retry is not an
+		 * option from here: we run from setup_pi_matrix(), and
+		 * pse_controller_register() unwinds neither its notification
+		 * fifo nor its pse_pi array when that fails, so every retry
+		 * would leak. Warn instead so the assumed voltage is visible.
+		 */
+		if (PTR_ERR(vpwr) == -EPROBE_DEFER)
+			dev_warn(ltc4266->dev,
+				 "%pOF: vpwr-supply is not registered yet, assuming %d uV\n",
+				 np, LTC4266_VPORT_NOMINAL_UV);
+
+		return LTC4266_VPORT_NOMINAL_UV;
+	}
+
+	uv = regulator_get_voltage(vpwr);
+	regulator_put(vpwr);
+
+	if (uv <= 0)
+		return LTC4266_VPORT_NOMINAL_UV;
+
+	return uv;
+}
+
+/* Build a port context for every PSE PI described in the device tree, bound to
+ * the channel its pairset references. A PI with no node is left NULL; the PSE
+ * core does not register a regulator for it and so never calls back for it.
+ */
+static int ltc4266_map_pis(struct ltc4266 *ltc4266,
+			   struct ltc4266_chan_desc *chans)
+{
+	struct pse_controller_dev *pcdev = &ltc4266->pcdev;
+	struct ltc4266_port *port;
+	int i, j, chan, uv;
+
+	for (i = 0; i < LTC4266_MAX_PORTS; i++) {
+		struct pse_pi *pi = &pcdev->pi[i];
+
+		if (!pi->np)
+			continue;
+
+		if (!pi->pairset[0].np)
+			return dev_err_probe(ltc4266->dev, -EINVAL,
+					     "%pOF has no pairsets\n", pi->np);
+
+		/* The LTC4266 delivers over a single pairset per channel, so
+		 * there is no 4-pair mode to spread a PI over two channels.
+		 */
+		if (pi->pairset[1].np)
+			return dev_err_probe(ltc4266->dev, -EOPNOTSUPP,
+					     "%pOF: 4-pair PSE PIs are not supported\n",
+					     pi->np);
+
+		chan = ltc4266_match_channel(&pi->pairset[0], chans);
+		if (chan < 0)
+			return dev_err_probe(ltc4266->dev, chan,
+					     "%pOF: pairset %pOF is not a channel of this controller\n",
+					     pi->np, pi->pairset[0].np);
+
+		for (j = 0; j < i; j++)
+			if (ltc4266->ports[j] && ltc4266->ports[j]->chan == chan)
+				return dev_err_probe(ltc4266->dev, -EINVAL,
+						     "%pOF: channel %d is already used by %pOF\n",
+						     pi->np, chan, pcdev->pi[j].np);
+
+		uv = ltc4266_pi_nominal_uv(ltc4266, pi->np);
+
+		port = devm_kzalloc(ltc4266->dev, sizeof(*port), GFP_KERNEL);
+		if (!port)
+			return -ENOMEM;
+
+		port->ltc4266 = ltc4266;
+		port->chan = chan;
+		port->rsense = chans[chan].rsense;
+		port->vpwr_uv = uv;
+		port->pw_limit = LTC4266_PW_LIMIT_MAX;
+		ltc4266->ports[i] = port;
+
+		dev_dbg(ltc4266->dev, "PI %d is backed by channel %d, nominal %d uV\n",
+			i, chan, uv);
+	}
+
+	return 0;
+}
+
+static int ltc4266_setup_pi_matrix(struct pse_controller_dev *pcdev)
+{
+	struct ltc4266 *ltc4266 = container_of(pcdev, struct ltc4266, pcdev);
+	struct ltc4266_chan_desc chans[LTC4266_MAX_PORTS] = { };
+	int i, ret;
+
+	if (pcdev->no_of_pse_pi)
+		return dev_err_probe(ltc4266->dev, -EINVAL,
+				     "a \"pse-pis\" node is required\n");
+
+	ret = ltc4266_get_of_channels(ltc4266, chans);
+	if (!ret)
+		ret = ltc4266_map_pis(ltc4266, chans);
+
+	for (i = 0; i < LTC4266_MAX_PORTS; i++)
+		of_node_put(chans[i].np);
+
+	if (ret)
+		return ret;
+
+	for (i = 0; i < LTC4266_MAX_PORTS; i++) {
+		if (!ltc4266->ports[i])
+			continue;
+
+		ret = ltc4266_port_init(ltc4266->ports[i]);
+		if (ret < 0)
+			return dev_err_probe(ltc4266->dev, ret,
+					     "Failed to initialize PI %d\n", i);
+	}
+
+	return 0;
+}
+
+static const struct pse_controller_ops ltc4266_ops = {
+	.setup_pi_matrix = ltc4266_setup_pi_matrix,
+	.pi_get_admin_state = ltc4266_pi_get_admin_state,
+	.pi_get_pw_status = ltc4266_pi_get_pw_status,
+	.pi_get_pw_class = ltc4266_pi_get_pw_class,
+	.pi_get_actual_pw = ltc4266_pi_get_actual_pw,
+	.pi_enable = ltc4266_pi_enable,
+	.pi_disable = ltc4266_pi_disable,
+	.pi_get_voltage = ltc4266_pi_get_voltage,
+	.pi_get_pw_limit = ltc4266_pi_get_pw_limit,
+	.pi_set_pw_limit = ltc4266_pi_set_pw_limit,
+	.pi_get_pw_limit_ranges = ltc4266_pi_get_pw_limit_ranges,
+	.pi_get_pw_req = ltc4266_pi_get_pw_req,
+};
+
+#define LTC4266_INTERRUPT_SOURCES	(LTC4266_INT_TSTART | LTC4266_INT_TCUT | \
+					 LTC4266_INT_CLASS | LTC4266_INT_DETECT | \
+					 LTC4266_INT_DIS | LTC4266_INT_PWRGD)
+
+static int ltc4266_enable_interrupts(struct ltc4266 *ltc4266)
+{
+	/* Unmask interrupts */
+	return regmap_write(ltc4266->regmap, LTC4266_REG_INTMASK,
+		     LTC4266_INTERRUPT_SOURCES);
+}
+
+static int ltc4266_disable_interrupts(struct ltc4266 *ltc4266)
+{
+	int ret;
+
+	ret = regmap_write(ltc4266->regmap, LTC4266_REG_INTMASK, 0x00);
+	if (ret < 0)
+		return ret;
+
+	/* Reset the (SMBus Alert) interrupt pin */
+	return regmap_write(ltc4266->regmap, LTC4266_REG_RSTPB, LTC4266_RSTPB_PINCLR);
+}
+
+static int ltc4266_map_event(int irq, struct pse_controller_dev *pcdev,
+			     unsigned long *notifs, unsigned long *notifs_mask)
+{
+	struct ltc4266 *ltc4266 = container_of(pcdev, struct ltc4266, pcdev);
+	unsigned int detevn = 0, fltevn = 0, tsevn = 0, pwrevn = 0;
+	unsigned int statpwr = 0;
+	unsigned int intstat;
+	int ret;
+	int i;
+
+	ret = ltc4266_disable_interrupts(ltc4266);
+	if (ret < 0)
+		goto err;
+
+	ret = regmap_read(ltc4266->regmap, LTC4266_REG_INTSTAT, &intstat);
+	if (ret < 0)
+		goto err;
+
+	if (!intstat)
+		goto done;
+
+	if (intstat & LTC4266_INT_PWRGD) {
+		ret = regmap_read(ltc4266->regmap, LTC4266_REG_PWREVN_COR, &pwrevn);
+		if (ret < 0) {
+			dev_err(&ltc4266->client->dev, "Failed to read pwrevn, err=%d\n", ret);
+			goto err;
+		}
+
+		ret = regmap_read(ltc4266->regmap, LTC4266_REG_STATPWR, &statpwr);
+		if (ret < 0) {
+			dev_err(&ltc4266->client->dev, "Failed to read statpwr, err=%d\n", ret);
+			goto err;
+		}
+	}
+
+	if (intstat & (LTC4266_INT_DIS | LTC4266_INT_TCUT)) {
+		ret = regmap_read(ltc4266->regmap, LTC4266_REG_FLTEVN_COR, &fltevn);
+		if (ret < 0) {
+			dev_err(&ltc4266->client->dev, "Failed to read fltevn err=%d\n", ret);
+			goto err;
+		}
+	}
+
+	if (intstat & (LTC4266_INT_TSTART | LTC4266_INT_TCUT)) {
+		ret = regmap_read(ltc4266->regmap, LTC4266_REG_TSEVN_COR, &tsevn);
+		if (ret < 0) {
+			dev_err(&ltc4266->client->dev, "Failed to read tsevn, err=%d\n", ret);
+			goto err;
+		}
+	}
+
+	if (intstat & (LTC4266_INT_CLASS | LTC4266_INT_DETECT)) {
+		ret = regmap_read(ltc4266->regmap, LTC4266_REG_DETEVN_COR, &detevn);
+		if (ret < 0) {
+			dev_err(&ltc4266->client->dev, "Failed to read detevn, err=%d\n", ret);
+			goto err;
+		}
+	}
+
+	/* The event registers are indexed by delivery channel while notifs[] is
+	 * indexed by PSE PI id, so walk the PIs and use each one's channel to
+	 * select the event bits.
+	 */
+	for (i = 0; i < LTC4266_MAX_PORTS; i++) {
+		struct ltc4266_port *port = ltc4266->ports[i];
+		unsigned int fault;
+		bool pg_lost;
+		u8 chan;
+
+		if (!port)
+			continue;
+
+		chan = port->chan;
+
+		fault = (tsevn | fltevn) &
+			(LTC4266_EVN_HI(chan) | LTC4266_EVN_LO(chan));
+
+		/* The controller also drops a port without reporting any
+		 * per-port fault event, for instance on an over-temperature
+		 * shutdown or when it finds a failed external MOSFET. Both of
+		 * those clear the port's detection and classification enables,
+		 * so the port stays dark until it is re-initialised. A power
+		 * good change that leaves statpwr[pg] clear catches them, and
+		 * ignores the rising edge of a port that has just been powered
+		 * on.
+		 */
+		pg_lost = (pwrevn & LTC4266_EVN_HI(chan)) &&
+			  !(statpwr & LTC4266_STATPWR_PG(chan));
+
+		if (fault || pg_lost) {
+			/* The port has gone down; if we see this, then we don't
+			 * care if any of the remaining events are set.  If the
+			 * device did disconnect briefly, it'll redetect and
+			 * reclassify accordingly
+			 */
+			notifs[i] |= ETHTOOL_C33_PSE_EVENT_DISCONNECTION;
+			*notifs_mask |= BIT(i);
+
+			/* Report over-current if the port went down for any
+			 * overcurrent reason: tSTART (tsevn low nibble, startup
+			 * inrush), tLIM (tsevn high nibble, current-limit
+			 * timeout) or tCUT (fltevn low nibble, I_CUT timeout).
+			 */
+			if ((tsevn & (LTC4266_EVN_LO(chan) | LTC4266_EVN_HI(chan))) ||
+			    (fltevn & LTC4266_EVN_LO(chan)))
+				notifs[i] |= ETHTOOL_PSE_EVENT_OVER_CURRENT;
+
+			dev_dbg(&ltc4266->client->dev,
+				"tsevn=0x%02X fltevn=0x%02X pwrevn=0x%02X statpwr=0x%02X\n",
+				tsevn, fltevn, pwrevn, statpwr);
+			continue;
+		}
+		if (detevn & LTC4266_EVN_LO(chan)) {
+			/* Read the detect result, and if it isn't detect good,
+			 * call it a disconnect
+			 */
+			unsigned int detval;
+
+			ret = regmap_read(ltc4266->regmap, LTC4266_REG_STAT(chan), &detval);
+			if (ret < 0) {
+				dev_warn(ltc4266->dev, "Failed to read status register, err=%d\n",
+					 ret);
+				continue;
+			}
+
+			if (LTC4266_PORT_DETECT(detval) != LTC4266_DETECT_GOOD) {
+				notifs[i] |= ETHTOOL_C33_PSE_EVENT_DISCONNECTION;
+				*notifs_mask |= BIT(i);
+				continue;
+			}
+		}
+
+		if (detevn & LTC4266_EVN_HI(chan)) {
+			int class = ltc4266_port_get_class(port);
+
+			if (class >= 0) {
+				notifs[i] |= ETHTOOL_C33_PSE_EVENT_CLASSIFICATION;
+				*notifs_mask |= BIT(i);
+			}
+		}
+	}
+
+done:
+	return ltc4266_enable_interrupts(ltc4266);
+
+err:
+	/* (Attempt to) clear any remaining event registers that we might've
+	 * missed in the event a previous read has failed.
+	 */
+	ret = regmap_write(ltc4266->regmap, LTC4266_REG_RSTPB, LTC4266_RSTPB_INTCLR);
+	if (ret)
+		dev_warn(&ltc4266->client->dev, "Failed to clear pending interrupts, err=%d\n",
+			 ret);
+
+	return ltc4266_enable_interrupts(ltc4266);
+}
+
+static const struct regmap_config ltc4266_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0x5F,
+};
+
+static void ltc4266_teardown(void *data)
+{
+	struct ltc4266 *ltc4266 = data;
+
+	ltc4266_disable_interrupts(ltc4266);
+
+	/* Prevent the chip from asserting interrupts */
+	regmap_update_bits(ltc4266->regmap, LTC4266_REG_MCONF,
+			   LTC4266_MCONF_INTERRUPT_ENABLE, 0);
+
+	/* Reset all the ports and do not re-init: a port reset removes power and
+	 * clears the port's detection and classification enables, leaving it in
+	 * semi-auto mode, which never powers a port without a host request.
+	 */
+	regmap_write(ltc4266->regmap, LTC4266_REG_RSTPB, LTC4266_RSTPB_RSTPORTS);
+}
+
+static int ltc4266_probe(struct i2c_client *client)
+{
+	struct ltc4266 *ltc4266;
+	struct regmap *regmap;
+	unsigned int id_reg;
+	int ret;
+
+	struct pse_irq_desc irq_desc = {
+		.name = "ltc4266-irq",
+		.map_event = ltc4266_map_event,
+	};
+
+	/* We need IRQ for static power budgeting and if don't have it, fail
+	 * probe early
+	 */
+	if (!client->irq)
+		return dev_err_probe(&client->dev, -EINVAL,
+				     "Interrupt is required for power budget management\n");
+
+	regmap = devm_regmap_init_i2c(client, &ltc4266_regmap_config);
+	if (IS_ERR(regmap))
+		return dev_err_probe(&client->dev, PTR_ERR(regmap),
+				     "Failed to allocate regmap\n");
+
+	/* Confirm we are talking to an LTC4266: the id register (0x1B) should
+	 * read back its documented reset value of 0x64.
+	 */
+	ret = regmap_read(regmap, LTC4266_REG_ID, &id_reg);
+	if (ret < 0)
+		return dev_err_probe(&client->dev, ret, "Failed to read ID register\n");
+
+	if (id_reg != LTC4266_ID)
+		return dev_err_probe(&client->dev, -ENODEV,
+				     "Expected an ID of 0x64, saw 0x%02X\n", id_reg);
+
+	/* Reset the chip */
+	ret = regmap_write(regmap, LTC4266_REG_RSTPB, LTC4266_RSTPB_INTCLR | LTC4266_RSTPB_RSTALL);
+	if (ret < 0)
+		return dev_err_probe(&client->dev, ret, "Failed to reset\n");
+
+	/* LTC4266 requires approximately 10 ms after reset to be stable; if it
+	 * isn't, then there is typically an undervoltage lockout/something pretty bad
+	 * going on. We give it 50 ms here so we don't need to poll the chip and use I2C bandwidth
+	 */
+	msleep(50);
+
+	/* Let's make sure the chip came out of reset (if not, the chip is probably
+	 * either (no longer?) present, in thermal shutdown, or watchdogged....either
+	 * way, there's nothing we can do in software to fix it)
+	 */
+	ret = regmap_read(regmap, LTC4266_REG_ID, &id_reg);
+	if (ret < 0)
+		return dev_err_probe(&client->dev, ret,
+				     "Failed to re-read ID register after reset\n");
+
+	if (id_reg != LTC4266_ID)
+		return dev_err_probe(&client->dev, -ENODEV,
+				     "Failed to re-read device ID after reset 0x%02X\n",
+				     id_reg);
+
+	ltc4266 = devm_kzalloc(&client->dev, sizeof(struct ltc4266), GFP_KERNEL);
+	if (!ltc4266)
+		return -ENOMEM;
+
+	ltc4266->client = client;
+	ltc4266->regmap = regmap;
+	ltc4266->np = client->dev.of_node;
+	ltc4266->dev = &client->dev;
+
+	/* After reset, the LTC4266 will interrupt with a (single) supply fault.
+	 * Clear it here and discard the result
+	 */
+	regmap_read(ltc4266->regmap, LTC4266_REG_SUPEVN_COR, &id_reg);
+
+	ret = ltc4266_disable_interrupts(ltc4266);
+	if (ret)
+		return dev_err_probe(&client->dev, ret,
+				     "Failed to disable interrupts\n");
+
+	/* Registered before the controller and the IRQ so devres ordering runs
+	 * it after free_irq() and pse_controller_unregister(), and so a failed
+	 * probe cannot leave ports running detection with no driver bound.
+	 */
+	ret = devm_add_action_or_reset(&client->dev, ltc4266_teardown, ltc4266);
+	if (ret)
+		return ret;
+
+	ltc4266->pcdev.owner = THIS_MODULE;
+	ltc4266->pcdev.ops = &ltc4266_ops;
+	ltc4266->pcdev.dev = &client->dev;
+	ltc4266->pcdev.types = ETHTOOL_PSE_C33;
+	ltc4266->pcdev.nr_lines = LTC4266_MAX_PORTS;
+	ltc4266->pcdev.supp_budget_eval_strategies = PSE_BUDGET_EVAL_STRAT_STATIC;
+
+	ret = devm_pse_controller_register(ltc4266->dev, &ltc4266->pcdev);
+	if (ret)
+		return dev_err_probe(&client->dev, ret,
+				     "Failed to register PSE controller\n");
+
+	/* Enable the interrupt pin, and only report detect events on
+	 * change (detchg) so idle ports continuously re-running
+	 * detection in semi-auto mode don't flood the host with a
+	 * detect event every cycle.
+	 */
+	ret = regmap_update_bits(ltc4266->regmap, LTC4266_REG_MCONF,
+				 LTC4266_MCONF_INTERRUPT_ENABLE | LTC4266_MCONF_DETCHG,
+				 LTC4266_MCONF_INTERRUPT_ENABLE | LTC4266_MCONF_DETCHG);
+	if (ret)
+		return dev_err_probe(&client->dev, ret,
+				     "Failed to configure interrupts\n");
+
+	ret = devm_pse_irq_helper(&ltc4266->pcdev, client->irq,
+				  0, &irq_desc);
+	if (ret)
+		return dev_err_probe(&client->dev, ret,
+				     "Failed to register PSE IRQ\n");
+
+	/* Unmask the chip interrupt sources now the handler is ready. */
+	ret = ltc4266_enable_interrupts(ltc4266);
+	if (ret)
+		return dev_err_probe(&client->dev, ret,
+				     "Failed to enable interrupts\n");
+
+	return 0;
+}
+
+static const struct i2c_device_id ltc4266_id[] = {
+	{.name = "ltc4266"},
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ltc4266_id);
+
+static const struct of_device_id ltc4266_of_match[] = {
+	{ .compatible = "lltc,ltc4266" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ltc4266_of_match);
+
+static struct i2c_driver ltc4266_driver = {
+	.driver		= {
+		.name	= "ltc4266",
+		.of_match_table = ltc4266_of_match,
+	},
+	.probe		= ltc4266_probe,
+	.id_table	= ltc4266_id,
+};
+module_i2c_driver(ltc4266_driver);
+
+MODULE_AUTHOR("Kyle Swenson <kyle.swenson@est.tech>");
+MODULE_DESCRIPTION("LTC4266 PoE PSE Controller Driver");
+MODULE_LICENSE("GPL");

3/2 2026/09/29 00:47 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 24557
							output: 28
							thoughts: 797
						

Thoughts:
**Evaluating the Syzkaller Fuzzing Potential of This Patch**

Okay, so I need to decide if this commit warrants fuzzing with syzkaller. First, let's break down the patch. It touches several files: `Documentation/devicetree/bindings/net/pse-pd/lltc,ltc4266.yaml`, `MAINTAINERS`, `drivers/net/pse-pd/Kconfig`, `drivers/net/pse-pd/Makefile`, and `drivers/net/pse-pd/ltc4266.c`. It's pretty clear that `ltc4266.c` is the core driver here. Ah, right, "Driver for Linear LTC4266 PoE PSE Controller." It's an I2C device driver, using `module_i2c_driver(ltc4266_driver)` with the `lltc,ltc4266` compatible string.

Now, the important part: is this driver *reachable* in a typical syzkaller environment? Let's look at the `ltc4266_probe` function. I see the usual checks: `!client->irq` leads to an error, then a read of the `LTC4266_REG_ID` register, verifying the ID.

My triage philosophy immediately kicks in: *reachability*. Syzkaller is all about finding bugs in code that *can* execute in GCE or QEMU using emulated devices. The core question is: can this I2C driver *be* instantiated and reached? Hardware that's structurally unreachable is a no-go. I immediately think of vendor-specific PCIe switches, and SmartNICs. But wait, this is an I2C device!

Can I2C devices be instantiated from userspace or emulated? Ah, the `/sys/bus/i2c/devices/i2c-*/new_device` trick. Can I create an I2C client that way? The critical question: Does an I2C adapter *exist* in QEMU/syzkaller in the first place? And if I *do* instantiate a device via `new_device`, can it actually communicate?

There's the clincher in `ltc4266_probe`: `if (!client->irq)`. Does the `new_device` interface supply `client->irq`? Probably not. I need to check how `new_device` actually works in `drivers/i2c/i2c-core-sysfs.c` or similar to confirm. This is where the answer lies. This is the lynchpin of the whole evaluation. Is this device *structurally* reachable in our fuzzing environment? If not, then it is NOT suitable for fuzzing.



4/2 2026/09/29 00:47 tool grepper
0m Args:
{
  "Expression": "new_device",
  "PathPrefix": "drivers/i2c"
}

Results:
{
  "Output": "drivers/i2c/i2c-core-acpi.c=522=struct notifier_block i2c_acpi_notifier = {\n--\ndrivers/i2c/i2c-core-acpi.c-526-/**\ndrivers/i2c/i2c-core-acpi.c:527: * i2c_acpi_new_device_by_fwnode - Create i2c-client for the Nth I2cSerialBus resource\ndrivers/i2c/i2c-core-acpi.c-528- * @fwnode:  fwnode with the ACPI resources to get the client from\n--\ndrivers/i2c/i2c-core-acpi.c-543- */\ndrivers/i2c/i2c-core-acpi.c:544:struct i2c_client *i2c_acpi_new_device_by_fwnode(struct fwnode_handle *fwnode,\ndrivers/i2c/i2c-core-acpi.c-545-\t\t\t\t\t\t int index,\n--\ndrivers/i2c/i2c-core-acpi.c-578-}\ndrivers/i2c/i2c-core-acpi.c:579:EXPORT_SYMBOL_GPL(i2c_acpi_new_device_by_fwnode);\ndrivers/i2c/i2c-core-acpi.c-580-\n--\ndrivers/i2c/i2c-core-base.c=1264=static ssize_t\ndrivers/i2c/i2c-core-base.c:1265:new_device_store(struct device *dev, struct device_attribute *attr,\ndrivers/i2c/i2c-core-base.c-1266-\t\t const char *buf, size_t count)\n--\ndrivers/i2c/i2c-core-base.c-1277-\tif (!blank) {\ndrivers/i2c/i2c-core-base.c:1278:\t\tdev_err(dev, \"%s: Missing parameters\\n\", \"new_device\");\ndrivers/i2c/i2c-core-base.c-1279-\t\treturn -EINVAL;\n--\ndrivers/i2c/i2c-core-base.c-1281-\tif (blank - buf \u003e I2C_NAME_SIZE - 1) {\ndrivers/i2c/i2c-core-base.c:1282:\t\tdev_err(dev, \"%s: Invalid device name\\n\", \"new_device\");\ndrivers/i2c/i2c-core-base.c-1283-\t\treturn -EINVAL;\n--\ndrivers/i2c/i2c-core-base.c-1289-\tif (res \u003c 1) {\ndrivers/i2c/i2c-core-base.c:1290:\t\tdev_err(dev, \"%s: Can't parse I2C address\\n\", \"new_device\");\ndrivers/i2c/i2c-core-base.c-1291-\t\treturn -EINVAL;\n--\ndrivers/i2c/i2c-core-base.c-1293-\tif (res \u003e 1  \u0026\u0026 end != '\\n') {\ndrivers/i2c/i2c-core-base.c:1294:\t\tdev_err(dev, \"%s: Extra parameters\\n\", \"new_device\");\ndrivers/i2c/i2c-core-base.c-1295-\t\treturn -EINVAL;\n--\ndrivers/i2c/i2c-core-base.c-1315-\tmutex_unlock(\u0026adap-\u003euserspace_clients_lock);\ndrivers/i2c/i2c-core-base.c:1316:\tdev_info(dev, \"%s: Instantiated device %s at 0x%02hx\\n\", \"new_device\",\ndrivers/i2c/i2c-core-base.c-1317-\t\t info.type, info.addr);\n--\ndrivers/i2c/i2c-core-base.c-1320-}\ndrivers/i2c/i2c-core-base.c:1321:static DEVICE_ATTR_WO(new_device);\ndrivers/i2c/i2c-core-base.c-1322-\n--\ndrivers/i2c/i2c-core-base.c-1325- * they got it wrong. This interface can only be used to delete devices\ndrivers/i2c/i2c-core-base.c:1326: * instantiated by i2c_sysfs_new_device above. This guarantees that we\ndrivers/i2c/i2c-core-base.c-1327- * don't delete devices to which some kernel code still has references.\n--\ndrivers/i2c/i2c-core-base.c=1379=static struct attribute *i2c_adapter_attrs[] = {\ndrivers/i2c/i2c-core-base.c-1380-\t\u0026dev_attr_name.attr,\ndrivers/i2c/i2c-core-base.c:1381:\t\u0026dev_attr_new_device.attr,\ndrivers/i2c/i2c-core-base.c-1382-\t\u0026dev_attr_delete_device.attr,\n--\ndrivers/i2c/i2c-core-base.c=1804=void i2c_del_adapter(struct i2c_adapter *adap)\n--\ndrivers/i2c/i2c-core-base.c-1822-\t */\ndrivers/i2c/i2c-core-base.c:1823:\tdevice_remove_file(\u0026adap-\u003edev, \u0026dev_attr_new_device);\ndrivers/i2c/i2c-core-base.c-1824-\n"
}

5/2 2026/09/29 00:47 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 27480
							output: 44
							thoughts: 29
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**Diving into the I2C Core Driver**

Alright, time to get my hands dirty. I'm thinking I need to jump right into the `drivers/i2c/i2c-core-base.c` file. Specifically, I want to zero in on the `new_device_store` function. I have a hunch, based on the current issue/investigation, that this is the pivotal function for understanding device registration and potentially where my problem is manifesting. I'm expecting to find some crucial logic for handling new I2C device instantiation, addressing things like probe calls, resource allocation, and perhaps even some device attribute setting that might be interfering. I'll need to carefully dissect the code flow to understand precisely how a new device gets its foothold within the I2C subsystem, and specifically, to understand if a known edge-case could be triggered, resulting in the current reported problem.



6/2 2026/09/29 00:47 tool read-file
0m Args:
{
  "File": "drivers/i2c/i2c-core-base.c",
  "FirstLine": 1265,
  "LineCount": 60
}

Results:
{
  "Contents": "1265:\tnew_device_store(struct device *dev, struct device_attribute *attr,\n1266:\t\t\t const char *buf, size_t count)\n1267:\t{\n1268:\t\tstruct i2c_adapter *adap = to_i2c_adapter(dev);\n1269:\t\tstruct i2c_board_info info;\n1270:\t\tstruct i2c_client *client;\n1271:\t\tchar *blank, end;\n1272:\t\tint res;\n1273:\t\n1274:\t\tmemset(\u0026info, 0, sizeof(struct i2c_board_info));\n1275:\t\n1276:\t\tblank = strchr(buf, ' ');\n1277:\t\tif (!blank) {\n1278:\t\t\tdev_err(dev, \"%s: Missing parameters\\n\", \"new_device\");\n1279:\t\t\treturn -EINVAL;\n1280:\t\t}\n1281:\t\tif (blank - buf \u003e I2C_NAME_SIZE - 1) {\n1282:\t\t\tdev_err(dev, \"%s: Invalid device name\\n\", \"new_device\");\n1283:\t\t\treturn -EINVAL;\n1284:\t\t}\n1285:\t\tmemcpy(info.type, buf, blank - buf);\n1286:\t\n1287:\t\t/* Parse remaining parameters, reject extra parameters */\n1288:\t\tres = sscanf(++blank, \"%hi%c\", \u0026info.addr, \u0026end);\n1289:\t\tif (res \u003c 1) {\n1290:\t\t\tdev_err(dev, \"%s: Can't parse I2C address\\n\", \"new_device\");\n1291:\t\t\treturn -EINVAL;\n1292:\t\t}\n1293:\t\tif (res \u003e 1  \u0026\u0026 end != '\\n') {\n1294:\t\t\tdev_err(dev, \"%s: Extra parameters\\n\", \"new_device\");\n1295:\t\t\treturn -EINVAL;\n1296:\t\t}\n1297:\t\n1298:\t\tif ((info.addr \u0026 I2C_ADDR_OFFSET_TEN_BIT) == I2C_ADDR_OFFSET_TEN_BIT) {\n1299:\t\t\tinfo.addr \u0026= ~I2C_ADDR_OFFSET_TEN_BIT;\n1300:\t\t\tinfo.flags |= I2C_CLIENT_TEN;\n1301:\t\t}\n1302:\t\n1303:\t\tif (info.addr \u0026 I2C_ADDR_OFFSET_SLAVE) {\n1304:\t\t\tinfo.addr \u0026= ~I2C_ADDR_OFFSET_SLAVE;\n1305:\t\t\tinfo.flags |= I2C_CLIENT_SLAVE;\n1306:\t\t}\n1307:\t\n1308:\t\tclient = i2c_new_client_device(adap, \u0026info);\n1309:\t\tif (IS_ERR(client))\n1310:\t\t\treturn PTR_ERR(client);\n1311:\t\n1312:\t\t/* Keep track of the added device */\n1313:\t\tmutex_lock(\u0026adap-\u003euserspace_clients_lock);\n1314:\t\tlist_add_tail(\u0026client-\u003edetected, \u0026adap-\u003euserspace_clients);\n1315:\t\tmutex_unlock(\u0026adap-\u003euserspace_clients_lock);\n1316:\t\tdev_info(dev, \"%s: Instantiated device %s at 0x%02hx\\n\", \"new_device\",\n1317:\t\t\t info.type, info.addr);\n1318:\t\n1319:\t\treturn count;\n1320:\t}\n1321:\tstatic DEVICE_ATTR_WO(new_device);\n1322:\t\n1323:\t/*\n1324:\t * And of course let the users delete the devices they instantiated, if\n"
}

7/2 2026/09/29 00:47 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 28753
							output: 25
							thoughts: 904
						

Thoughts:
**Analyzing the Feasibility of Fuzzing the LTC4266 Driver**

Okay, let's break this down. First, I'm looking at `new_device_store`. I see that `info.irq` is being initialized to zero. Then, in the `ltc4266_probe` function, there's a check: if `client->irq` is zero, the probe fails immediately with `-EINVAL`. So, even if the device were instantiated, it would fail right away, regardless, because the interrupt line is set to 0. Plus, even *beyond* that, there are physical hardware interactions via I2C using `regmap_read`, which requires a *physical* LTC4266 responding with a specific ID.

Now, the crucial question is: is this even relevant to the target architecture, which is amd64? This means checking for Device Tree presence. On amd64 with QEMU, there's no Device Tree emulation with an `lltc,ltc4266` node or even any sort of LTC4266 emulation. Nope, QEMU and GCE definitely don't emulate it. This is a PoE PSE controller, a specific piece of hardware. I can confirm this by checking the bindings (`Documentation/devicetree/bindings/net/pse-pd/lltc,ltc4266.yaml`) and the driver itself (`drivers/net/pse-pd/ltc4266.c`).

Now, let's refer to the provided instructions. According to section 2 (negative criteria), I should only return WorthFuzzing=false if all modified code falls strictly into one or more of several categories, one of which includes "Structurally unreachable hardware." This specifically calls out drivers requiring physical ASIC/PCIe cards that are not emulated in standard QEMU – which sounds exactly like this scenario.

Therefore, let's consider the following: is this driver, specifically `pse-pd` or `ltc4266`, accessible via any *virtual* buses? That's the key. I need to understand how the `drivers/net/pse-pd/` directory works and what drivers are contained there. It's a "Power Sourcing Equipment - Powered Device" or PoE.



8/2 2026/09/29 00:47 tool codesearch-dir-index
0m Args:
{
  "Dir": "drivers/net/pse-pd"
}

Results:
{
  "Files": [
    "ltc4266.c",
    "pd692x0.c",
    "pse_core.c",
    "pse_regulator.c",
    "realtek-pse-mcu-core.c",
    "realtek-pse-mcu-i2c.c",
    "realtek-pse-mcu-uart.c",
    "realtek-pse-mcu.h",
    "si3474.c",
    "tps23881.c"
  ],
  "Subdirs": null
}

9/2 2026/09/29 00:47 llm patch-evaluator
1m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 30306
							output: 146
							thoughts: 682
						

Thoughts:
**Unreachable Hardware Driver: LTC4266 PoE PSE Controller**

Okay, let's break this down. My initial thought was to check if this patch touches `pse_core.c` or other existing drivers. No, it doesn't. This is a *new* driver, specifically `ltc4266.c`, and it's for a Linear Technology LTC4266 PoE PSE controller that communicates over I2C. The patch includes all the necessary components: device tree bindings, Kconfig, Makefile entries, and the driver code itself.

The key thing is that this driver is *hardware-dependent*. It's designed to interface with a *physical* LTC4266 chip connected to an I2C bus. Looking at the code, in `ltc4266_probe`, it immediately checks for an IRQ (`client->irq`) and then tries to read a register (`LTC4266_REG_ID`) to verify the chip's ID (0x64). It also resets the chip via I2C.

Now, here's the kicker: this hardware is not emulated in standard environments like QEMU or GCE. There's no virtual LTC4266 to respond on the I2C bus. Even if someone tried to instantiate it manually via sysfs, it would fail instantly because `client->irq` would be zero, and there wouldn't be a chip with the correct ID responding on the bus. All other conditions would also fail if there is no physical hardware.

So, in essence, `drivers/net/pse-pd/ltc4266.c` is structurally unreachable in those environments. It needs specific physical hardware and Device Tree configuration that isn't present in any typical fuzzing setup. I'm going to set-results accordingly.



10/2 2026/09/29 00:48 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch adds a new driver for the Linear Technology LTC4266 Power Sourcing Equipment (PSE) controller over I2C (drivers/net/pse-pd/ltc4266.c) along with its Device Tree bindings. This driver requires physical LTC4266 hardware on an I2C bus with an assigned interrupt and devicetree description, which is not present or emulated in standard virtualized environments (QEMU/GCE on amd64). As such, the code is structurally unreachable for fuzzing.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch adds a new driver for the Linear Technology LTC4266 Power Sourcing Equipment (PSE) controller over I2C (drivers/net/pse-pd/ltc4266.c) along with its Device Tree bindings. This driver requires physical LTC4266 hardware on an I2C bus with an assigned interrupt and devicetree description, which is not present or emulated in standard virtualized environments (QEMU/GCE on amd64). As such, the code is structurally unreachable for fuzzing.",
  "WorthFuzzing": false
}

11/1 2026/09/29 00:48 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)