* Re: [PATCH v2 0/2] ADRF5702/ADRF5703 Digital Attenuator support
From: Jonathan Cameron @ 2026-03-26 20:12 UTC (permalink / raw)
To: Rodrigo Alencar via B4 Relay
Cc: rodrigo.alencar, linux-iio, devicetree, linux-kernel,
Lars-Peter Clausen, Michael Hennerich, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley
In-Reply-To: <20260326-adrf570x-iio-support-v2-0-b622a17335d0@analog.com>
On Thu, 26 Mar 2026 18:32:15 +0000
Rodrigo Alencar via B4 Relay <devnull+rodrigo.alencar.analog.com@kernel.org> wrote:
> This is a small patch series that adds support for two extra attenuators.
> Basically, new chip info and device table entries are added into the
> AD8366 IIO driver.
>
> Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
> ---
> Changes in v2:
> - Add more context to the dt-bindings commit message.
> - Link to v1: https://lore.kernel.org/r/20260325-adrf570x-iio-support-v1-0-9a2685eb2e55@analog.com
Given I want to get this a tiny bit of exposure in next (tomorrow hopefully)
prior to sending a pull request on the weekend I've applied this now.
Given how near the top of the tree this is and short window I may well
apply any tags from dt-maintainers etc after applying.
(as background I'm travelling next week so scooping stuff up now)
Thanks,
Jonathan
>
> ---
> Rodrigo Alencar (2):
> dt-bindings: iio: amplifiers: ad8366: add adrf5702/3 support
> iio: amplifiers: ad8366: add support for adrf5702/3
>
> .../bindings/iio/amplifiers/adi,ad8366.yaml | 4 ++++
> drivers/iio/amplifiers/Kconfig | 2 ++
> drivers/iio/amplifiers/ad8366.c | 22 ++++++++++++++++++++++
> 3 files changed, 28 insertions(+)
> ---
> base-commit: af980a79bfed43c4a0be12cca786be46f1a0c5e8
> change-id: 20260325-adrf570x-iio-support-e199418fba40
>
> Best regards,
^ permalink raw reply
* Re: [PATCH v3 net-next 02/14] dt-bindings: net: dsa: add NETC switch
From: Frank Li @ 2026-03-26 20:06 UTC (permalink / raw)
To: Wei Fang
Cc: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, robh, krzk+dt, conor+dt,
f.fainelli, chleroy, horms, linux, andrew, netdev, linux-kernel,
devicetree, linuxppc-dev, linux-arm-kernel, imx
In-Reply-To: <20260326062917.3552334-3-wei.fang@nxp.com>
On Thu, Mar 26, 2026 at 02:29:05PM +0800, Wei Fang wrote:
> Add bindings for NETC switch. This switch is a PCIe function of NETC IP,
> it supports advanced QoS with 8 traffic classes and 4 drop resilience
> levels, and a full range of TSN standards capabilities. The switch CPU
Nit: double space before TSN.
> port connects to an internal ENETC port, which is also a PCIe function
> of NETC IP. So these two ports use a light-weight "pseudo MAC" instead
> of a back-to-back MAC, because the "pseudo MAC" provides the delineation
> between switch and ENETC, this translates to lower power (less logic and
what's means "this translates", do you means
"this help reduce power and latency."
> memory) and lower delay (as there is no serialization delay across this
> link).
>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
> ---
> .../bindings/net/dsa/nxp,netc-switch.yaml | 130 ++++++++++++++++++
> 1 file changed, 130 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/net/dsa/nxp,netc-switch.yaml
>
> diff --git a/Documentation/devicetree/bindings/net/dsa/nxp,netc-switch.yaml b/Documentation/devicetree/bindings/net/dsa/nxp,netc-switch.yaml
> new file mode 100644
> index 000000000000..e34a4e3504c3
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/dsa/nxp,netc-switch.yaml
> @@ -0,0 +1,130 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/net/dsa/nxp,netc-switch.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: NETC Switch family
> +
> +description:
Nit use ">" for multi paragraph.
others look good
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> + The NETC presents itself as a multi-function PCIe Root Complex Integrated
> + Endpoint (RCiEP) and provides full 802.1Q Ethernet switch functionality,
> + advanced QoS with 8 traffic classes and 4 drop resilience levels, and a
> + full range of TSN standards capabilities.
> +
> + The CPU port of the switch connects to an internal ENETC. The switch and
> + the internal ENETC are fully integrated into the NETC IP, a back-to-back
> + MAC is not required. Instead, a light-weight "pseudo MAC" provides the
> + delineation between the switch and ENETC. This translates to lower power
> + (less logic and memory) and lower delay (as there is no serialization
> + delay across this link).
> +
> +maintainers:
> + - Wei Fang <wei.fang@nxp.com>
> +
> +properties:
> + compatible:
> + enum:
> + - pci1131,eef2
> +
> + reg:
> + maxItems: 1
> +
> + dsa,member:
> + description:
> + The property indicates DSA cluster and switch index. For NETC switch,
> + the valid range of the switch index is 1 ~ 7, the index is reflected
> + in the switch tag as an indication of the switch ID where the frame
> + originated. The value 0 is reserved for ENETC VEPA switch, whose ID
> + is hardwired to zero.
> +
> +$ref: dsa.yaml#
> +
> +patternProperties:
> + "^(ethernet-)?ports$":
> + type: object
> + additionalProperties: true
> + patternProperties:
> + "^(ethernet-)?port@[0-9a-f]$":
> + type: object
> +
> + $ref: dsa-port.yaml#
> +
> + properties:
> + clocks:
> + items:
> + - description: MAC transmit/receive reference clock.
> +
> + clock-names:
> + items:
> + - const: ref
> +
> + mdio:
> + $ref: /schemas/net/mdio.yaml#
> + unevaluatedProperties: false
> + description:
> + Optional child node for switch port, otherwise use NETC EMDIO.
> +
> + unevaluatedProperties: false
> +
> +required:
> + - compatible
> + - reg
> + - dsa,member
> +
> +allOf:
> + - $ref: /schemas/pci/pci-device.yaml
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + pcie {
> + #address-cells = <3>;
> + #size-cells = <2>;
> +
> + ethernet-switch@0,2 {
> + compatible = "pci1131,eef2";
> + reg = <0x200 0 0 0 0>;
> + dsa,member = <0 1>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_switch>;
> +
> + ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + port@0 {
> + reg = <0>;
> + phy-handle = <ðphy0>;
> + phy-mode = "mii";
> + };
> +
> + port@1 {
> + reg = <1>;
> + phy-handle = <ðphy1>;
> + phy-mode = "mii";
> + };
> +
> + port@2 {
> + reg = <2>;
> + clocks = <&scmi_clk 103>;
> + clock-names = "ref";
> + phy-handle = <ðphy2>;
> + phy-mode = "rgmii-id";
> + };
> +
> + port@3 {
> + reg = <3>;
> + ethernet = <&enetc3>;
> + phy-mode = "internal";
> +
> + fixed-link {
> + speed = <2500>;
> + full-duplex;
> + pause;
> + };
> + };
> + };
> + };
> + };
> --
> 2.34.1
>
^ permalink raw reply
* [PATCH v4 2/2] hwmon: Add support for TI INA4230 power monitor
From: Alexey Charkov @ 2026-03-26 19:59 UTC (permalink / raw)
To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-hwmon, devicetree, linux-kernel, Alexey Charkov
In-Reply-To: <20260326-ina4230-v4-0-c1e312c09de7@flipper.net>
Add a driver for the TI INA4230, a 4-channel power monitor with I2C
interface.
The driver supports voltage, current, power and energy measurements, but
skips the alert functionality in this initial implementation.
Signed-off-by: Alexey Charkov <alchark@flipper.net>
---
MAINTAINERS | 1 +
drivers/hwmon/Kconfig | 11 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/ina4230.c | 1032 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1045 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 3204e1b8753e..7cbf662d7d29 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12541,6 +12541,7 @@ M: Alexey Charkov <alchark@flipper.net>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/hwmon/ti,ina4230.yaml
+F: drivers/hwmon/ina4230.c
INDEX OF FURTHER KERNEL DOCUMENTATION
M: Carlos Bilbao <carlos.bilbao@kernel.org>
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 9d49cfd4ef3d..4649f00f24ca 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -2296,6 +2296,17 @@ config SENSORS_INA3221
This driver can also be built as a module. If so, the module
will be called ina3221.
+config SENSORS_INA4230
+ tristate "Texas Instruments INA4230 Quad Current/Voltage Monitor"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for the TI INA4230 Quad
+ Current/Voltage Monitor.
+
+ This driver can also be built as a module. If so, the module
+ will be called ina4230.
+
config SENSORS_SPD5118
tristate "SPD5118 Compliant Temperature Sensors"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 556e86d277b1..3d83eba94bec 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -104,6 +104,7 @@ obj-$(CONFIG_SENSORS_INA209) += ina209.o
obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o
obj-$(CONFIG_SENSORS_INA238) += ina238.o
obj-$(CONFIG_SENSORS_INA3221) += ina3221.o
+obj-$(CONFIG_SENSORS_INA4230) += ina4230.o
obj-$(CONFIG_SENSORS_INTEL_M10_BMC_HWMON) += intel-m10-bmc-hwmon.o
obj-$(CONFIG_SENSORS_ISL28022) += isl28022.o
obj-$(CONFIG_SENSORS_IT87) += it87.o
diff --git a/drivers/hwmon/ina4230.c b/drivers/hwmon/ina4230.c
new file mode 100644
index 000000000000..b5233c004089
--- /dev/null
+++ b/drivers/hwmon/ina4230.c
@@ -0,0 +1,1032 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * INA4230 Quad Current/Voltage Monitor
+ *
+ * Based on INA3221 driver by Texas Instruments Incorporated - https://www.ti.com/
+ * Adapted for INA4230 by Alexey Charkov <alchark@flipper.net>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/byteorder/generic.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/math.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/util_macros.h>
+
+#define INA4230_DRIVER_NAME "ina4230"
+
+#define INA4230_SHUNT_VOLTAGE_CH1 0x00
+#define INA4230_BUS_VOLTAGE_CH1 0x01
+#define INA4230_CURRENT_CH1 0x02
+#define INA4230_POWER_CH1 0x03
+#define INA4230_ENERGY_CH1 0x04
+#define INA4230_CALIBRATION_CH1 0x05
+#define INA4230_ALERT_LIMIT1 0x06
+#define INA4230_ALERT_CONFIG1 0x07
+#define INA4230_SHUNT_VOLTAGE_CH2 0x08
+#define INA4230_BUS_VOLTAGE_CH2 0x09
+#define INA4230_CURRENT_CH2 0x0A
+#define INA4230_POWER_CH2 0x0B
+#define INA4230_ENERGY_CH2 0x0C
+#define INA4230_CALIBRATION_CH2 0x0D
+#define INA4230_ALERT_LIMIT2 0x0E
+#define INA4230_ALERT_CONFIG2 0x0F
+#define INA4230_SHUNT_VOLTAGE_CH3 0x10
+#define INA4230_BUS_VOLTAGE_CH3 0x11
+#define INA4230_CURRENT_CH3 0x12
+#define INA4230_POWER_CH3 0x13
+#define INA4230_ENERGY_CH3 0x14
+#define INA4230_CALIBRATION_CH3 0x15
+#define INA4230_ALERT_LIMIT3 0x16
+#define INA4230_ALERT_CONFIG3 0x17
+#define INA4230_SHUNT_VOLTAGE_CH4 0x18
+#define INA4230_BUS_VOLTAGE_CH4 0x19
+#define INA4230_CURRENT_CH4 0x1A
+#define INA4230_POWER_CH4 0x1B
+#define INA4230_ENERGY_CH4 0x1C
+#define INA4230_CALIBRATION_CH4 0x1D
+#define INA4230_ALERT_LIMIT4 0x1E
+#define INA4230_ALERT_CONFIG4 0x1F
+#define INA4230_CONFIG1 0x20
+#define INA4230_CONFIG2 0x21
+#define INA4230_FLAGS 0x22
+#define INA4230_MANUFACTURER_ID 0x7E
+
+#define INA4230_CALIBRATION_MASK GENMASK(14, 0)
+
+#define INA4230_ALERT_CHANNEL_MASK GENMASK(4, 3)
+#define INA4230_ALERT_MASK GENMASK(2, 0)
+/* Shunt voltage over limit */
+#define INA4230_ALERT_MASK_SOL 0x1
+/* Shunt voltage under limit */
+#define INA4230_ALERT_MASK_SUL 0x2
+/* Bus voltage over limit */
+#define INA4230_ALERT_MASK_BOL 0x3
+/* Bus voltage under limit */
+#define INA4230_ALERT_MASK_BUL 0x4
+/* Power over limit */
+#define INA4230_ALERT_MASK_POL 0x5
+
+#define INA4230_CONFIG1_ACTIVE_CHANNEL_MASK GENMASK(15, 12)
+#define INA4230_CONFIG1_AVG_MASK GENMASK(11, 9)
+#define INA4230_CONFIG1_VBUSCT_MASK GENMASK(8, 6)
+#define INA4230_CONFIG1_VSHCT_MASK GENMASK(5, 3)
+#define INA4230_CONFIG1_MODE_MASK GENMASK(2, 0)
+#define INA4230_MODE_POWERDOWN 0
+#define INA4230_MODE_SHUNT_SINGLE 1
+#define INA4230_MODE_BUS_SINGLE 2
+#define INA4230_MODE_BUS_SHUNT_SINGLE 3
+#define INA4230_MODE_POWERDOWN1 4
+#define INA4230_MODE_SHUNT_CONTINUOUS 5
+#define INA4230_MODE_BUS_CONTINUOUS 6
+#define INA4230_MODE_BUS_SHUNT_CONTINUOUS 7
+
+#define INA4230_CONFIG2_RST BIT(15)
+#define INA4230_CONFIG2_ACC_RST_MASK GENMASK(11, 8)
+#define INA4230_CONFIG2_CNVR_MASK BIT(7)
+#define INA4230_CONFIG2_ENOF_MASK BIT(6)
+#define INA4230_CONFIG2_ALERT_LATCH BIT(5)
+#define INA4230_CONFIG2_ALERT_POL BIT(4)
+#define INA4230_CONFIG2_RANGE_MASK GENMASK(3, 0)
+#define INA4230_CONFIG2_RANGE_CH(x) \
+ FIELD_PREP(INA4230_CONFIG2_RANGE_MASK, BIT((x)))
+
+#define INA4230_FLAGS_LIMIT4_ALERT BIT(15)
+#define INA4230_FLAGS_LIMIT3_ALERT BIT(14)
+#define INA4230_FLAGS_LIMIT2_ALERT BIT(13)
+#define INA4230_FLAGS_LIMIT1_ALERT BIT(12)
+#define INA4230_FLAGS_ENERGY_OVERFLOW_CH4 BIT(11)
+#define INA4230_FLAGS_ENERGY_OVERFLOW_CH3 BIT(10)
+#define INA4230_FLAGS_ENERGY_OVERFLOW_CH2 BIT(9)
+#define INA4230_FLAGS_ENERGY_OVERFLOW_CH1 BIT(8)
+#define INA4230_FLAGS_CVRF BIT(7)
+#define INA4230_FLAGS_MATH_OVERFLOW BIT(6)
+
+#define INA4230_RSHUNT_DEFAULT 10000
+#define INA4230_CONFIG_DEFAULT \
+ (FIELD_PREP(INA4230_CONFIG1_ACTIVE_CHANNEL_MASK, 0xF) | \
+ FIELD_PREP(INA4230_CONFIG1_AVG_MASK, 0x1) | \
+ FIELD_PREP(INA4230_CONFIG1_VBUSCT_MASK, 0x4) | \
+ FIELD_PREP(INA4230_CONFIG1_VSHCT_MASK, 0x4) | \
+ FIELD_PREP(INA4230_CONFIG1_MODE_MASK, 0x7))
+#define INA4230_CONFIG_CHx_EN(x) \
+ FIELD_PREP(INA4230_CONFIG1_ACTIVE_CHANNEL_MASK, BIT((x)))
+
+enum ina4230_fields {
+ /* Alert configuration settings: channel masks */
+ F_ALERT1_CH, F_ALERT2_CH, F_ALERT3_CH, F_ALERT4_CH,
+ /* Alert configuration settings: alert masks */
+ F_ALERT1_TYPE, F_ALERT2_TYPE, F_ALERT3_TYPE, F_ALERT4_TYPE,
+ /* Configuration registers */
+ F_CH_EN, F_AVG, F_VBUSCT, F_VSHCT, F_MODE,
+ F_RST, F_ACC_RST, F_CNV_ALERT, F_ENOF, F_ALERT_LATCH, F_ALERT_POL, F_RANGE,
+ /* Status flags */
+ F_LIMIT1_ALERT, F_LIMIT2_ALERT, F_LIMIT3_ALERT, F_LIMIT4_ALERT,
+ F_ENERGY_OVERFLOW_CH1, F_ENERGY_OVERFLOW_CH2, F_ENERGY_OVERFLOW_CH3, F_ENERGY_OVERFLOW_CH4,
+ F_CVRF, F_MATH_OVERFLOW,
+ /* sentinel */
+ F_MAX_FIELDS
+};
+
+static const struct reg_field ina4230_reg_fields[] = {
+ [F_ALERT1_CH] = REG_FIELD(INA4230_ALERT_CONFIG1, 3, 4),
+ [F_ALERT2_CH] = REG_FIELD(INA4230_ALERT_CONFIG2, 3, 4),
+ [F_ALERT3_CH] = REG_FIELD(INA4230_ALERT_CONFIG3, 3, 4),
+ [F_ALERT4_CH] = REG_FIELD(INA4230_ALERT_CONFIG4, 3, 4),
+
+ [F_ALERT1_TYPE] = REG_FIELD(INA4230_ALERT_CONFIG1, 0, 2),
+ [F_ALERT2_TYPE] = REG_FIELD(INA4230_ALERT_CONFIG2, 0, 2),
+ [F_ALERT3_TYPE] = REG_FIELD(INA4230_ALERT_CONFIG3, 0, 2),
+ [F_ALERT4_TYPE] = REG_FIELD(INA4230_ALERT_CONFIG4, 0, 2),
+
+ [F_CH_EN] = REG_FIELD(INA4230_CONFIG1, 12, 15),
+ [F_AVG] = REG_FIELD(INA4230_CONFIG1, 9, 11),
+ [F_VBUSCT] = REG_FIELD(INA4230_CONFIG1, 6, 8),
+ [F_VSHCT] = REG_FIELD(INA4230_CONFIG1, 3, 5),
+ [F_MODE] = REG_FIELD(INA4230_CONFIG1, 0, 2),
+ [F_RST] = REG_FIELD(INA4230_CONFIG2, 15, 15),
+ [F_ACC_RST] = REG_FIELD(INA4230_CONFIG2, 8, 11),
+ [F_CNV_ALERT] = REG_FIELD(INA4230_CONFIG2, 7, 7),
+ [F_ENOF] = REG_FIELD(INA4230_CONFIG2, 6, 6),
+ [F_ALERT_LATCH] = REG_FIELD(INA4230_CONFIG2, 5, 5),
+ [F_ALERT_POL] = REG_FIELD(INA4230_CONFIG2, 4, 4),
+ [F_RANGE] = REG_FIELD(INA4230_CONFIG2, 0, 3),
+
+ [F_LIMIT1_ALERT] = REG_FIELD(INA4230_FLAGS, 12, 12),
+ [F_LIMIT2_ALERT] = REG_FIELD(INA4230_FLAGS, 13, 13),
+ [F_LIMIT3_ALERT] = REG_FIELD(INA4230_FLAGS, 14, 14),
+ [F_LIMIT4_ALERT] = REG_FIELD(INA4230_FLAGS, 15, 15),
+ [F_ENERGY_OVERFLOW_CH1] = REG_FIELD(INA4230_FLAGS, 8, 8),
+ [F_ENERGY_OVERFLOW_CH2] = REG_FIELD(INA4230_FLAGS, 9, 9),
+ [F_ENERGY_OVERFLOW_CH3] = REG_FIELD(INA4230_FLAGS, 10, 10),
+ [F_ENERGY_OVERFLOW_CH4] = REG_FIELD(INA4230_FLAGS, 11, 11),
+ [F_CVRF] = REG_FIELD(INA4230_FLAGS, 7, 7),
+ [F_MATH_OVERFLOW] = REG_FIELD(INA4230_FLAGS, 6, 6),
+};
+
+enum ina4230_channels {
+ INA4230_CHANNEL1,
+ INA4230_CHANNEL2,
+ INA4230_CHANNEL3,
+ INA4230_CHANNEL4,
+ INA4230_NUM_CHANNELS
+};
+
+/**
+ * struct ina4230_input - channel input source specific information
+ * @label: label of channel input source
+ * @shunt_resistor: shunt resistor value of channel input source
+ * @shunt_gain: gain of shunt voltage for current calculation
+ * @max_expected_current: maximum expected current in micro-Ampere for ADC
+ * calibration
+ * @current_lsb_uA: current LSB in micro-Amperes
+ * @disconnected: connection status of channel input source
+ */
+struct ina4230_input {
+ const char *label;
+ int shunt_resistor;
+ int shunt_gain;
+ int max_expected_current;
+ int current_lsb_uA;
+ bool disconnected;
+};
+
+/**
+ * struct ina4230_data - device specific information
+ * @pm_dev: Device pointer for pm runtime
+ * @regmap: Register map of the device
+ * @fields: Register fields of the device
+ * @inputs: Array of channel input source specific structures
+ * @reg_config1: cached value of CONFIG1 register
+ * @reg_config2: cached value of CONFIG2 register
+ * @alert_active_high: flag indicating alert polarity is active high
+ */
+struct ina4230_data {
+ struct device *pm_dev;
+ struct regmap *regmap;
+ struct regmap_field *fields[F_MAX_FIELDS];
+ struct ina4230_input inputs[INA4230_NUM_CHANNELS];
+ unsigned int reg_config1;
+ unsigned int reg_config2;
+ bool alert_active_high;
+};
+
+static inline bool ina4230_is_enabled(struct ina4230_data *ina, int channel)
+{
+ return pm_runtime_active(ina->pm_dev) &&
+ !ina->inputs[channel].disconnected &&
+ ina->reg_config1 & INA4230_CONFIG_CHx_EN(channel);
+}
+
+/* Lookup table for Bus and Shunt conversion times in usec */
+static const u16 ina4230_conv_time[] = {
+ 140, 204, 332, 588, 1100, 2116, 4156, 8244,
+};
+
+/* Lookup table for number of samples used in averaging mode */
+static const int ina4230_avg_samples[] = {
+ 1, 4, 16, 64, 128, 256, 512, 1024,
+};
+
+/* Converting update_interval in msec to conversion time in usec */
+static inline u32 ina4230_interval_ms_to_conv_time(u16 config, int interval)
+{
+ u32 channels = hweight16(config & INA4230_CONFIG1_ACTIVE_CHANNEL_MASK);
+ u32 samples_idx = FIELD_GET(INA4230_CONFIG1_AVG_MASK, config);
+ u32 samples = ina4230_avg_samples[samples_idx];
+
+ /* Bisect the result to Bus and Shunt conversion times */
+ return DIV_ROUND_CLOSEST(interval * 1000 / 2, channels * samples);
+}
+
+/* Converting CONFIG register value to update_interval in usec */
+static inline u32 ina4230_reg_to_interval_us(u16 config)
+{
+ u32 channels = hweight16(config & INA4230_CONFIG1_ACTIVE_CHANNEL_MASK);
+ u32 vbus_ct_idx = FIELD_GET(INA4230_CONFIG1_VBUSCT_MASK, config);
+ u32 vsh_ct_idx = FIELD_GET(INA4230_CONFIG1_VSHCT_MASK, config);
+ u32 vbus_ct = ina4230_conv_time[vbus_ct_idx];
+ u32 vsh_ct = ina4230_conv_time[vsh_ct_idx];
+
+ /* Calculate total conversion time */
+ return channels * (vbus_ct + vsh_ct);
+}
+
+static const u8 ina4230_calibration_reg[] = {
+ INA4230_CALIBRATION_CH1,
+ INA4230_CALIBRATION_CH2,
+ INA4230_CALIBRATION_CH3,
+ INA4230_CALIBRATION_CH4,
+};
+
+static int ina4230_set_calibration(struct ina4230_data *ina, int channel)
+{
+ struct ina4230_input *input = &ina->inputs[channel];
+ u8 reg = ina4230_calibration_reg[channel];
+ int shunt_range_uV, ret;
+ u32 calibration;
+ u64 n, d;
+
+ shunt_range_uV = mult_frac(input->max_expected_current,
+ input->shunt_resistor,
+ 1000000);
+ input->shunt_gain = shunt_range_uV > 20480 ? 1 : 4;
+ ina->reg_config2 &= ~INA4230_CONFIG2_RANGE_CH(channel);
+ if (input->shunt_gain == 4)
+ ina->reg_config2 |= INA4230_CONFIG2_RANGE_CH(channel);
+
+ ret = regmap_write(ina->regmap, INA4230_CONFIG2, ina->reg_config2);
+ if (ret)
+ return ret;
+
+ input->current_lsb_uA = DIV_ROUND_UP(input->max_expected_current, 32768);
+ n = 5120000000ULL;
+ d = (u64)input->current_lsb_uA * input->shunt_resistor * input->shunt_gain;
+ /* Ensure rounding to the closest integer */
+ n += d / 2;
+ n = div64_u64(n, d);
+ if (n > INA4230_CALIBRATION_MASK) {
+ dev_err(ina->pm_dev,
+ "Shunt %duOhm too low for expected current %duA, cannot calibrate channel %d\n",
+ input->shunt_resistor, input->max_expected_current, channel + 1);
+ return -ERANGE;
+ }
+
+ calibration = n & INA4230_CALIBRATION_MASK;
+
+ return regmap_write(ina->regmap, reg, calibration);
+}
+
+static const u8 ina4230_in_reg[] = {
+ INA4230_BUS_VOLTAGE_CH1,
+ INA4230_BUS_VOLTAGE_CH2,
+ INA4230_BUS_VOLTAGE_CH3,
+ INA4230_BUS_VOLTAGE_CH4,
+ INA4230_SHUNT_VOLTAGE_CH1,
+ INA4230_SHUNT_VOLTAGE_CH2,
+ INA4230_SHUNT_VOLTAGE_CH3,
+ INA4230_SHUNT_VOLTAGE_CH4,
+};
+
+static const u8 ina4230_curr_reg[][INA4230_NUM_CHANNELS] = {
+ [hwmon_curr_input] = { INA4230_CURRENT_CH1, INA4230_CURRENT_CH2,
+ INA4230_CURRENT_CH3, INA4230_CURRENT_CH4 },
+};
+
+static const u8 ina4230_power_reg[] = {
+ INA4230_POWER_CH1, INA4230_POWER_CH2, INA4230_POWER_CH3, INA4230_POWER_CH4
+};
+
+static const u8 ina4230_energy_reg[] = {
+ INA4230_ENERGY_CH1, INA4230_ENERGY_CH2,
+ INA4230_ENERGY_CH3, INA4230_ENERGY_CH4
+};
+
+static int ina4230_read_chip(struct device *dev, u32 attr, long *val)
+{
+ struct ina4230_data *ina = dev_get_drvdata(dev);
+ int regval;
+
+ switch (attr) {
+ case hwmon_chip_samples:
+ regval = FIELD_GET(INA4230_CONFIG1_AVG_MASK, ina->reg_config1);
+ *val = ina4230_avg_samples[regval];
+ return 0;
+ case hwmon_chip_update_interval:
+ /* Return in msec */
+ *val = ina4230_reg_to_interval_us(ina->reg_config1);
+ *val = DIV_ROUND_CLOSEST(*val, 1000);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ina4230_read_in(struct device *dev, u32 attr, int channel, long *val)
+{
+ const bool is_shunt = channel > INA4230_CHANNEL4;
+ struct ina4230_data *ina = dev_get_drvdata(dev);
+ u8 reg = ina4230_in_reg[channel];
+ int regval, ret;
+
+ /*
+ * Translate shunt channel index to sensor channel index
+ */
+ channel %= INA4230_NUM_CHANNELS;
+
+ switch (attr) {
+ case hwmon_in_input:
+ if (!ina4230_is_enabled(ina, channel))
+ return -ENODATA;
+
+ ret = regmap_read(ina->regmap, reg, ®val);
+ if (ret)
+ return ret;
+
+ /*
+ * Scale of shunt voltage (uV): LSB is 2.5uV or 625nV
+ * depending on gain setting
+ * Scale of bus voltage (mV): LSB is 1.6mV
+ */
+ if (is_shunt)
+ *val = mult_frac((long)(int16_t)regval,
+ 2500 / ina->inputs[channel].shunt_gain,
+ 1000000);
+ else
+ *val = mult_frac((long)(int16_t)regval,
+ 1600,
+ 1000);
+ return 0;
+ case hwmon_in_enable:
+ *val = ina4230_is_enabled(ina, channel);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ina4230_read_power(struct device *dev, u32 attr, int channel, long *val)
+{
+ struct ina4230_data *ina = dev_get_drvdata(dev);
+ u8 reg = ina4230_power_reg[channel];
+ int regval, ret;
+
+ switch (attr) {
+ case hwmon_power_input:
+ if (!ina4230_is_enabled(ina, channel))
+ return -ENODATA;
+
+ ret = regmap_read(ina->regmap, reg, ®val);
+ if (ret)
+ return ret;
+
+ *val = (int16_t)regval *
+ (long)ina->inputs[channel].current_lsb_uA * 32;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ina4230_read_energy(struct device *dev, u32 attr, int channel, long *val)
+{
+ struct ina4230_data *ina = dev_get_drvdata(dev);
+ u8 reg = ina4230_energy_reg[channel];
+ int ret;
+ __be32 regval;
+
+ switch (attr) {
+ case hwmon_energy_input:
+ if (!ina4230_is_enabled(ina, channel))
+ return -ENODATA;
+
+ ret = regmap_noinc_read(ina->regmap, reg, ®val, sizeof(regval));
+ if (ret)
+ return ret;
+
+ *val = be32_to_cpu(regval) *
+ (long)ina->inputs[channel].current_lsb_uA * 32;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ina4230_read_curr(struct device *dev, u32 attr,
+ int channel, long *val)
+{
+ struct ina4230_data *ina = dev_get_drvdata(dev);
+ u8 reg = ina4230_curr_reg[attr][channel];
+ int regval, ret;
+
+ switch (attr) {
+ case hwmon_curr_input:
+ if (!ina4230_is_enabled(ina, channel))
+ return -ENODATA;
+
+ ret = regmap_read(ina->regmap, reg, ®val);
+ if (ret)
+ return ret;
+
+ *val = (int16_t)regval *
+ (long)ina->inputs[channel].current_lsb_uA / 1000;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ina4230_write_chip(struct device *dev, u32 attr, long val)
+{
+ struct ina4230_data *ina = dev_get_drvdata(dev);
+ int idx;
+ u32 tmp;
+
+ switch (attr) {
+ case hwmon_chip_samples:
+ idx = find_closest(val, ina4230_avg_samples,
+ ARRAY_SIZE(ina4230_avg_samples));
+
+ FIELD_MODIFY(INA4230_CONFIG1_AVG_MASK, &ina->reg_config1, idx);
+ return regmap_write(ina->regmap, INA4230_CONFIG1, ina->reg_config1);
+ case hwmon_chip_update_interval:
+ tmp = ina4230_interval_ms_to_conv_time(ina->reg_config1, val);
+ idx = find_closest(tmp, ina4230_conv_time,
+ ARRAY_SIZE(ina4230_conv_time));
+
+ FIELD_MODIFY(INA4230_CONFIG1_VBUSCT_MASK, &ina->reg_config1, idx);
+ FIELD_MODIFY(INA4230_CONFIG1_VSHCT_MASK, &ina->reg_config1, idx);
+ return regmap_write(ina->regmap, INA4230_CONFIG1, ina->reg_config1);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ina4230_write_enable(struct device *dev, int channel, bool enable)
+{
+ struct ina4230_data *ina = dev_get_drvdata(dev);
+ u16 config, mask = INA4230_CONFIG_CHx_EN(channel);
+ u16 config_old = ina->reg_config1 & mask;
+ u32 tmp;
+ int ret;
+
+ config = enable ? mask : 0;
+
+ /* Bypass if enable status is not being changed */
+ if (config_old == config)
+ return 0;
+
+ /* For enabling routine, increase refcount and resume() at first */
+ if (enable) {
+ ret = pm_runtime_resume_and_get(ina->pm_dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get PM runtime\n");
+ return ret;
+ }
+ }
+
+ /* Enable or disable the channel */
+ tmp = (ina->reg_config1 & ~mask) | (config & mask);
+ ret = regmap_write(ina->regmap, INA4230_CONFIG1, tmp);
+ if (ret)
+ goto fail;
+
+ /* Cache the latest config register value */
+ ina->reg_config1 = tmp;
+
+ /* For disabling routine, decrease refcount or suspend() at last */
+ if (!enable)
+ pm_runtime_put_sync(ina->pm_dev);
+
+ return 0;
+
+fail:
+ if (enable) {
+ dev_err(dev, "Failed to enable channel %d: error %d\n",
+ channel, ret);
+ pm_runtime_put_sync(ina->pm_dev);
+ }
+
+ return ret;
+}
+
+static int ina4230_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ int ret;
+
+ switch (type) {
+ case hwmon_chip:
+ ret = ina4230_read_chip(dev, attr, val);
+ break;
+ case hwmon_in:
+ /* 0-align channel ID */
+ ret = ina4230_read_in(dev, attr, channel - 1, val);
+ break;
+ case hwmon_curr:
+ ret = ina4230_read_curr(dev, attr, channel, val);
+ break;
+ case hwmon_power:
+ ret = ina4230_read_power(dev, attr, channel, val);
+ break;
+ case hwmon_energy:
+ ret = ina4230_read_energy(dev, attr, channel, val);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ return ret;
+}
+
+static int ina4230_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ int ret;
+
+ switch (type) {
+ case hwmon_chip:
+ ret = ina4230_write_chip(dev, attr, val);
+ break;
+ case hwmon_in:
+ /* 0-align channel ID */
+ ret = ina4230_write_enable(dev, channel - 1, val);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ return ret;
+}
+
+static int ina4230_read_string(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ struct ina4230_data *ina = dev_get_drvdata(dev);
+ int index = channel - 1;
+
+ *str = ina->inputs[index].label;
+
+ return 0;
+}
+
+static umode_t ina4230_is_visible(const void *drvdata,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct ina4230_data *ina = drvdata;
+ const struct ina4230_input *input = NULL;
+
+ switch (type) {
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_samples:
+ case hwmon_chip_update_interval:
+ return 0644;
+ default:
+ return 0;
+ }
+ case hwmon_in:
+ /* Ignore in0_ */
+ if (channel == 0)
+ return 0;
+
+ switch (attr) {
+ case hwmon_in_label:
+ if (channel - 1 <= INA4230_CHANNEL4)
+ input = &ina->inputs[channel - 1];
+ /* Hide label node if label is not provided */
+ return (input && input->label) ? 0444 : 0;
+ case hwmon_in_input:
+ return 0444;
+ case hwmon_in_enable:
+ return 0644;
+ default:
+ return 0;
+ }
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_input:
+ return 0444;
+ default:
+ return 0;
+ }
+ case hwmon_power:
+ switch (attr) {
+ case hwmon_power_input:
+ return 0444;
+ default:
+ return 0;
+ }
+ case hwmon_energy:
+ switch (attr) {
+ case hwmon_energy_input:
+ return 0444;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
+static const struct hwmon_channel_info * const ina4230_info[] = {
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_SAMPLES,
+ HWMON_C_UPDATE_INTERVAL),
+ HWMON_CHANNEL_INFO(in,
+ /* 0: dummy, skipped in is_visible */
+ HWMON_I_INPUT,
+ /* 1-4: input voltage Channels */
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ /* 5-8: shunt voltage Channels */
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT),
+ HWMON_CHANNEL_INFO(curr,
+ /* 1-4: current channels*/
+ HWMON_C_INPUT,
+ HWMON_C_INPUT,
+ HWMON_C_INPUT,
+ HWMON_C_INPUT),
+ HWMON_CHANNEL_INFO(power,
+ /* 1-4: power channels*/
+ HWMON_P_INPUT,
+ HWMON_P_INPUT,
+ HWMON_P_INPUT,
+ HWMON_P_INPUT),
+ HWMON_CHANNEL_INFO(energy,
+ /* 1-4: energy channels*/
+ HWMON_E_INPUT,
+ HWMON_E_INPUT,
+ HWMON_E_INPUT,
+ HWMON_E_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops ina4230_hwmon_ops = {
+ .is_visible = ina4230_is_visible,
+ .read_string = ina4230_read_string,
+ .read = ina4230_read,
+ .write = ina4230_write,
+};
+
+static const struct hwmon_chip_info ina4230_chip_info = {
+ .ops = &ina4230_hwmon_ops,
+ .info = ina4230_info,
+};
+
+/* Extra attribute groups */
+static ssize_t ina4230_shunt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
+ struct ina4230_data *ina = dev_get_drvdata(dev);
+ unsigned int channel = sd_attr->index;
+ struct ina4230_input *input = &ina->inputs[channel];
+
+ return sysfs_emit(buf, "%d\n", input->shunt_resistor);
+}
+
+static ssize_t ina4230_shunt_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
+ struct ina4230_data *ina = dev_get_drvdata(dev);
+ unsigned int channel = sd_attr->index;
+ struct ina4230_input *input = &ina->inputs[channel];
+ int val;
+ int ret;
+
+ ret = kstrtoint(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ val = clamp_val(val, 1, INT_MAX);
+
+ input->shunt_resistor = val;
+ ret = ina4230_set_calibration(ina, channel);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+/* shunt resistance */
+static SENSOR_DEVICE_ATTR_RW(shunt1_resistor, ina4230_shunt, INA4230_CHANNEL1);
+static SENSOR_DEVICE_ATTR_RW(shunt2_resistor, ina4230_shunt, INA4230_CHANNEL2);
+static SENSOR_DEVICE_ATTR_RW(shunt3_resistor, ina4230_shunt, INA4230_CHANNEL3);
+static SENSOR_DEVICE_ATTR_RW(shunt4_resistor, ina4230_shunt, INA4230_CHANNEL4);
+
+static struct attribute *ina4230_attrs[] = {
+ &sensor_dev_attr_shunt1_resistor.dev_attr.attr,
+ &sensor_dev_attr_shunt2_resistor.dev_attr.attr,
+ &sensor_dev_attr_shunt3_resistor.dev_attr.attr,
+ &sensor_dev_attr_shunt4_resistor.dev_attr.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(ina4230);
+
+static const struct regmap_range ina4230_vol_ranges[] = {
+ regmap_reg_range(INA4230_SHUNT_VOLTAGE_CH1, INA4230_ENERGY_CH1),
+ regmap_reg_range(INA4230_SHUNT_VOLTAGE_CH2, INA4230_ENERGY_CH2),
+ regmap_reg_range(INA4230_SHUNT_VOLTAGE_CH3, INA4230_ENERGY_CH3),
+ regmap_reg_range(INA4230_SHUNT_VOLTAGE_CH4, INA4230_ENERGY_CH4),
+ regmap_reg_range(INA4230_FLAGS, INA4230_FLAGS),
+};
+
+static const struct regmap_access_table ina4230_volatile_table = {
+ .yes_ranges = ina4230_vol_ranges,
+ .n_yes_ranges = ARRAY_SIZE(ina4230_vol_ranges),
+};
+
+static const struct regmap_config ina4230_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .cache_type = REGCACHE_MAPLE,
+ .volatile_table = &ina4230_volatile_table,
+};
+
+static int ina4230_probe_child_from_dt(struct device *dev,
+ struct device_node *child,
+ struct ina4230_data *ina)
+{
+ struct ina4230_input *input;
+ u32 val;
+ int ret;
+
+ ret = of_property_read_u32(child, "reg", &val);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "missing reg property of %pOFn\n", child);
+ else if (val > INA4230_CHANNEL4)
+ return dev_err_probe(dev, -EINVAL,
+ "invalid reg %d of %pOFn\n", val, child);
+
+ input = &ina->inputs[val];
+
+ /* Log the disconnected channel input */
+ if (!of_device_is_available(child)) {
+ input->disconnected = true;
+ return 0;
+ }
+
+ /* Save the connected input label if available */
+ of_property_read_string(child, "label", &input->label);
+
+ /* Overwrite default shunt resistor value optionally */
+ if (!of_property_read_u32(child, "shunt-resistor-micro-ohms", &val)) {
+ if (val < 1 || val > INT_MAX)
+ return dev_err_probe(dev, -EINVAL,
+ "invalid shunt resistor value %u of %pOFn\n",
+ val, child);
+
+ input->shunt_resistor = val;
+ }
+
+ /* Save the expected maxcurrent */
+ if (!of_property_read_u32(child, "ti,maximum-expected-current-microamp", &val)) {
+ if (val < 32768 || val > INT_MAX)
+ return dev_err_probe(dev, -EINVAL,
+ "invalid max current value %u of %pOFn\n",
+ val, child);
+
+ input->max_expected_current = val;
+ }
+
+ return 0;
+}
+
+static int ina4230_probe_from_dt(struct device *dev, struct ina4230_data *ina)
+{
+ const struct device_node *np = dev->of_node;
+ int ret;
+
+ /* Compatible with non-DT platforms */
+ if (!np)
+ return 0;
+
+ ina->alert_active_high = of_property_read_bool(np, "ti,alert-polarity-active-high");
+
+ for_each_child_of_node_scoped(np, child) {
+ ret = ina4230_probe_child_from_dt(dev, child, ina);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_regulator_get_enable_optional(dev, "vs");
+ if (ret && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "Failed to get regulator\n");
+
+ return 0;
+}
+
+static int ina4230_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct ina4230_data *ina;
+ struct device *hwmon_dev;
+ int i, ret;
+
+ ina = devm_kzalloc(dev, sizeof(*ina), GFP_KERNEL);
+ if (!ina)
+ return -ENOMEM;
+
+ ina->regmap = devm_regmap_init_i2c(client, &ina4230_regmap_config);
+ if (IS_ERR(ina->regmap))
+ return PTR_ERR(ina->regmap);
+
+ ret = devm_regmap_field_bulk_alloc(dev, ina->regmap, ina->fields,
+ ina4230_reg_fields,
+ ARRAY_SIZE(ina4230_reg_fields));
+ if (ret)
+ return ret;
+
+ for (i = 0; i < INA4230_NUM_CHANNELS; i++) {
+ ina->inputs[i].shunt_resistor = INA4230_RSHUNT_DEFAULT;
+ /* Default for 1mA LSB current measurements */
+ ina->inputs[i].max_expected_current = 32768000;
+ }
+
+ ret = ina4230_probe_from_dt(dev, ina);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Unable to probe from device tree\n");
+
+ /* The driver will be reset, so use reset value */
+ ina->reg_config1 = INA4230_CONFIG_DEFAULT;
+ ina->reg_config2 = 0;
+
+ if (ina->alert_active_high)
+ FIELD_MODIFY(INA4230_CONFIG2_ALERT_POL, &ina->reg_config2, 1);
+
+ /* Disable channels if their inputs are disconnected */
+ for (i = 0; i < INA4230_NUM_CHANNELS; i++) {
+ if (ina->inputs[i].disconnected)
+ ina->reg_config1 &= ~INA4230_CONFIG_CHx_EN(i);
+ }
+
+ ina->pm_dev = dev;
+ dev_set_drvdata(dev, ina);
+
+ /* Enable PM runtime -- status is suspended by default */
+ pm_runtime_enable(ina->pm_dev);
+
+ /* Initialize (resume) the device */
+ for (i = 0; i < INA4230_NUM_CHANNELS; i++) {
+ if (ina->inputs[i].disconnected)
+ continue;
+
+ /* Match the refcount with number of enabled channels */
+ ret = pm_runtime_get_sync(ina->pm_dev);
+ if (ret < 0)
+ goto fail;
+ }
+
+ /* Set calibration values after device resume/reset */
+ for (i = 0; i < INA4230_NUM_CHANNELS; i++) {
+ if (!ina->inputs[i].disconnected) {
+ ret = ina4230_set_calibration(ina, i);
+ if (ret)
+ goto fail;
+ }
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, ina,
+ &ina4230_chip_info,
+ ina4230_groups);
+ if (IS_ERR(hwmon_dev)) {
+ ret = dev_err_probe(dev, PTR_ERR(hwmon_dev),
+ "Unable to register hwmon device\n");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ pm_runtime_disable(ina->pm_dev);
+ pm_runtime_set_suspended(ina->pm_dev);
+ /* pm_runtime_put_noidle() for connected channels to balance get_sync */
+ for (i = 0; i < INA4230_NUM_CHANNELS; i++) {
+ if (!ina->inputs[i].disconnected)
+ pm_runtime_put_noidle(ina->pm_dev);
+ }
+
+ return ret;
+}
+
+static void ina4230_remove(struct i2c_client *client)
+{
+ struct ina4230_data *ina = dev_get_drvdata(&client->dev);
+ int i;
+
+ pm_runtime_disable(ina->pm_dev);
+ pm_runtime_set_suspended(ina->pm_dev);
+
+ /* pm_runtime_put_noidle() for connected channels to balance get_sync */
+ for (i = 0; i < INA4230_NUM_CHANNELS; i++) {
+ if (!ina->inputs[i].disconnected)
+ pm_runtime_put_noidle(ina->pm_dev);
+ }
+}
+
+static int ina4230_suspend(struct device *dev)
+{
+ struct ina4230_data *ina = dev_get_drvdata(dev);
+ int ret;
+
+ /* Save config register value and enable cache-only */
+ ret = regmap_read(ina->regmap, INA4230_CONFIG1, &ina->reg_config1);
+ if (ret)
+ return ret;
+
+ regcache_cache_only(ina->regmap, true);
+ regcache_mark_dirty(ina->regmap);
+
+ return 0;
+}
+
+static int ina4230_resume(struct device *dev)
+{
+ struct ina4230_data *ina = dev_get_drvdata(dev);
+ int ret;
+
+ regcache_cache_only(ina->regmap, false);
+
+ /* Software reset the chip */
+ ret = regmap_field_write(ina->fields[F_RST], true);
+ if (ret) {
+ dev_err(dev, "Unable to reset device\n");
+ return ret;
+ }
+
+ /* Restore cached register values to hardware */
+ ret = regcache_sync(ina->regmap);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(ina4230_pm, ina4230_suspend, ina4230_resume,
+ NULL);
+
+static const struct of_device_id ina4230_of_match_table[] = {
+ { .compatible = "ti,ina4230", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ina4230_of_match_table);
+
+static const struct i2c_device_id ina4230_ids[] = {
+ { "ina4230" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, ina4230_ids);
+
+static struct i2c_driver ina4230_i2c_driver = {
+ .probe = ina4230_probe,
+ .remove = ina4230_remove,
+ .driver = {
+ .name = INA4230_DRIVER_NAME,
+ .of_match_table = ina4230_of_match_table,
+ .pm = pm_ptr(&ina4230_pm),
+ },
+ .id_table = ina4230_ids,
+};
+module_i2c_driver(ina4230_i2c_driver);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@flipper.net>");
+MODULE_DESCRIPTION("Texas Instruments INA4230 HWMon Driver");
+MODULE_LICENSE("GPL");
--
2.52.0
^ permalink raw reply related
* [PATCH v4 1/2] dt-bindings: hwmon: Add TI INA4230 4-channel I2C power monitor
From: Alexey Charkov @ 2026-03-26 19:59 UTC (permalink / raw)
To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-hwmon, devicetree, linux-kernel, Alexey Charkov,
Krzysztof Kozlowski
In-Reply-To: <20260326-ina4230-v4-0-c1e312c09de7@flipper.net>
Add TI INA4230, which is a 48V 4-channel 16-bit I2C-based
current/voltage/power/energy monitor with alert function.
Link: https://www.ti.com/product/INA4230
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Alexey Charkov <alchark@flipper.net>
---
.../devicetree/bindings/hwmon/ti,ina4230.yaml | 134 +++++++++++++++++++++
MAINTAINERS | 6 +
2 files changed, 140 insertions(+)
diff --git a/Documentation/devicetree/bindings/hwmon/ti,ina4230.yaml b/Documentation/devicetree/bindings/hwmon/ti,ina4230.yaml
new file mode 100644
index 000000000000..bed45c413206
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/ti,ina4230.yaml
@@ -0,0 +1,134 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/ti,ina4230.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments INA4230 quad-channel power monitors
+
+maintainers:
+ - Alexey Charkov <alchark@flipper.net>
+
+description: |
+ The INA4230 is a 48V quad-channel 16-bit current, voltage, power and energy
+ monitor with an I2C interface.
+
+ Datasheet:
+ https://www.ti.com/product/INA4230
+
+properties:
+ compatible:
+ enum:
+ - ti,ina4230
+
+ reg:
+ maxItems: 1
+
+ "#address-cells":
+ description: Required only if a child node is present.
+ const: 1
+
+ "#size-cells":
+ description: Required only if a child node is present.
+ const: 0
+
+ vs-supply:
+ description: phandle to the regulator that provides the VS supply typically
+ in range from 1.7 V to 5.5 V.
+
+ ti,alert-polarity-active-high:
+ description: Alert pin is asserted based on the value of Alert polarity Bit
+ of the CONFIG2 register. Default value is 0, for which the alert pin
+ toggles from high to low during faults. When this property is set, the
+ corresponding register bit is set to 1, and the alert pin toggles from
+ low to high during faults.
+ $ref: /schemas/types.yaml#/definitions/flag
+
+patternProperties:
+ "^input@[0-3]$":
+ description: The node contains optional child nodes for four channels.
+ Each child node describes the information of input source. Input channels
+ default to enabled in the chip. Unless channels are explicitly disabled
+ in device-tree, input channels will be enabled.
+ type: object
+ additionalProperties: false
+ properties:
+ reg:
+ description: Must be 0, 1, 2 or 3, corresponding to the IN1, IN2, IN3
+ or IN4 ports of the INA4230, respectively.
+ enum: [ 0, 1, 2, 3 ]
+
+ label:
+ description: name of the input source
+
+ shunt-resistor-micro-ohms:
+ description: shunt resistor value in micro-Ohm
+
+ ti,maximum-expected-current-microamp:
+ description: |
+ This value indicates the maximum current in microamps that you can
+ expect to measure with ina4230 in your circuit.
+
+ This value will be used to calculate the Current_LSB to maximize the
+ available precision while ensuring your expected maximum current fits
+ within the chip's ADC range. It will also enable built-in shunt gain
+ to increase ADC granularity by a factor of 4 if the provided maximum
+ current / shunt resistance combination does not produce more than
+ 20.48 mV drop at the shunt.
+ minimum: 32768
+ maximum: 2147483647
+ default: 32768000
+
+ required:
+ - reg
+
+required:
+ - compatible
+ - reg
+
+allOf:
+ - $ref: hwmon-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ power-sensor@44 {
+ compatible = "ti,ina4230";
+ reg = <0x44>;
+ vs-supply = <&vdd_3v0>;
+ ti,alert-polarity-active-high;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ input@0 {
+ reg = <0x0>;
+ /*
+ * Input channels are enabled by default in the device and so
+ * to disable, must be explicitly disabled in device-tree.
+ */
+ status = "disabled";
+ };
+
+ input@1 {
+ reg = <0x1>;
+ shunt-resistor-micro-ohms = <50000>;
+ ti,maximum-expected-current-microamp = <300000>;
+ };
+
+ input@2 {
+ reg = <0x2>;
+ label = "VDD_5V";
+ shunt-resistor-micro-ohms = <10000>;
+ ti,maximum-expected-current-microamp = <5000000>;
+ };
+
+ input@3 {
+ reg = <0x3>;
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 9fbb619c6f42..3204e1b8753e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12536,6 +12536,12 @@ S: Maintained
F: Documentation/hwmon/ina233.rst
F: drivers/hwmon/pmbus/ina233.c
+INA4230 HWMON DRIVER
+M: Alexey Charkov <alchark@flipper.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/hwmon/ti,ina4230.yaml
+
INDEX OF FURTHER KERNEL DOCUMENTATION
M: Carlos Bilbao <carlos.bilbao@kernel.org>
S: Maintained
--
2.52.0
^ permalink raw reply related
* [PATCH v4 0/2] Add support for Texas Instruments INA4230 power monitor
From: Alexey Charkov @ 2026-03-26 19:58 UTC (permalink / raw)
To: Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-hwmon, devicetree, linux-kernel, Alexey Charkov,
Krzysztof Kozlowski
TI INA4230 is a 4-channel power monitor with I2C interface, similar in
operation to INA3221 (3-channel) and INA219 (single-channel) but with
a different register layout, different alerting mechanism and slightly
different support for directly reading calculated current/power/energy
values (pre-multiplied by the device itself and needing only to be scaled
by the driver depending on its selected LSB unit values).
In this initial implementation, the driver supports reading voltage,
current, power and energy values, but does not yet support alerts, which
can be added separately if needed. Also the overflows during hardware
calculations are not yet handled, nor is the support for the device's
internal 32-bit energy counter reset.
An example device tree using this binding and driver is available at [1]
(not currently upstreamed, as the device in question is in engineering
phase and not yet publicly available)
[1] https://github.com/flipperdevices/flipper-linux-kernel/blob/flipper-devel/arch/arm64/boot/dts/rockchip/rk3576-flipper-one-rev-f0b0c1.dts
Signed-off-by: Alexey Charkov <alchark@flipper.net>
---
Changes in v4:
- Aligned the maximum value of ti,maximum-expected-current-microamp property
in the binding with the one expected by the driver (Guenter Roeck)
"2147A ought to be enough for anybody (c)"
- Actually requested the optional vs-supply regulator in the driver (Guenter Roeck)
- Program the ALERT_POL bit according to the value of ti,alert-polarity-active-high
even though the alerts themselves are not yet implemented (Guenter Roeck)
- Added a check for manually disabled channels in the is_enabled() function to
avoid reading invalid data from them (Guenter Roeck)
- Dropped support for the single-shot mode as its operation is not clearly
documented in the datasheet and there is no pressing need to support it (Guenter Roeck)
- NB: AI feedback regarding regmap_noinc_read() producing incorrect byte order on LE
hosts is incorrect, as its implementation does a byte-wise read and doesn't care
about the regmap value width or endianness flags, so it produces a 4-byte output
buffer in the same byte order as the device returns, which is BE in this case
- NB: AI feedback regarding fail-path pm_runtime_put_noidle() potentially being
unbalanced if the probe loop failed early is technically correct but practically
irrelevant, as the driver will simply fail to load, and the usage count won't
decrease beyond zero anyway. The alternatives are cumbersome for no real benefit
- Link to v3: https://lore.kernel.org/r/20260310-ina4230-v3-0-06ab3a77c570@flipper.net
Changes in v3:
- Updated the description of the ti,maximum-expected-current-microamp property
in the binding to clarify how it is used, and drop the irrelevant mention of
the PMbus (Guenter Roeck)
- Use div64_u64() instead of do_div() for the final division in the calibration value
calculation to avoid overflows in the denominator (Guenter Roeck)
- Avoid overflow while scaling the voltage values on 32-bit platforms (Guenter Roeck)
- Use regmap_noinc_read() instead of regmap_raw_read() for reading the energy values
to ensure that the regmap / bus driver don't wander off to adjacent registers
during the read operation (on INA4230 the whole 32 bits should be read from
the same register offset) (Guenter Roeck)
- Remove redundant call to ina4230_set_calibration() in the current read path,
as the calibration value is already set when enabling the channel and restored
across PM changes via regcache_sync() (Guenter Roeck)
- Add missing write_enable() function to make hwmon_in_enable writes work as
advertised in is_visible() (Guenter Roeck)
- Add a check for disabled channels before calling pm_runtime_put_noidle() on them
to avoid refcount underflow due to imbalanced get_sync/put_noidle calls (Guenter Roeck)
- Dropped unused include of linux/debugfs.h
- Add missing return checks on regmap_write() calls
- uO -> uOhm in the error message to avoid confusion
- Move probe-time calibration after enabling runtime PM to avoid it being reverted
by the PM sync
- Link to v2: https://lore.kernel.org/r/20260302-ina4230-v2-0-55b49d19d2ab@flipper.net
Changes in v2:
- Replace u64/u64 division with do_div() (kernel test robot)
- Add an example with ti,maximum-expected-current-microamp property in
bindings (Krzysztof Kozlowski)
- Include the newly added binding in MAINTAINERS file (Krzysztof Kozlowski)
- Use dev_err_probe() where appropriate in the driver (Krzysztof Kozlowski)
- Switch to devm_regmap_field_bulk_alloc() instead of an open-coded loop
- Add a bounds check for the calculated calibration value,
and a corresponding error message
- Link to v1: https://lore.kernel.org/r/20260225-ina4230-v1-0-92b1de981d46@flipper.net
---
Alexey Charkov (2):
dt-bindings: hwmon: Add TI INA4230 4-channel I2C power monitor
hwmon: Add support for TI INA4230 power monitor
.../devicetree/bindings/hwmon/ti,ina4230.yaml | 134 +++
MAINTAINERS | 7 +
drivers/hwmon/Kconfig | 11 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/ina4230.c | 1032 ++++++++++++++++++++
5 files changed, 1185 insertions(+)
---
base-commit: 66ba480978ce390e631e870b740a3406e3eb6b01
change-id: 20260219-ina4230-74a02409153d
Best regards,
--
Alexey Charkov <alchark@flipper.net>
^ permalink raw reply
* Re: [PATCH v3 0/4] iio: adc: meson-saradc: add support for Meson S4
From: Jonathan Cameron @ 2026-03-26 19:57 UTC (permalink / raw)
To: Nick Xie
Cc: neil.armstrong, khilman, martin.blumenstingl, jbrunet, dlechner,
andy, krzk+dt, robh, conor+dt, linux-iio, linux-amlogic,
linux-arm-kernel, devicetree, linux-kernel
In-Reply-To: <20260325070618.81955-1-nick@khadas.com>
On Wed, 25 Mar 2026 15:06:14 +0800
Nick Xie <nick@khadas.com> wrote:
> This series adds support for the SARADC IP block found in the Amlogic
> Meson S4 (S905Y4) SoC and enables it for the Khadas VIM1S board to
> support the onboard ADC 'Function' key.
>
> There are no known hardware differences between the SARADC on the S4
> and the previous G12A generation. The S4 bindings utilize a fallback
> to G12A, but a dedicated driver match entry is included to ensure the
> correct part name is exposed to the userspace ABI.
>
Applied patches 1 and 2 to the togreg branch of iio.git.
> Changes in v3:
> - Patch 1: Updated the commit message to explicitly explain why the
> comment regarding the generic fallback was added.
> - Patch 2: Retained the driver patch and updated the commit message to
> clarify that the dedicated match entry is kept specifically for
> userspace ABI naming purposes.
> - Link to v2: https://lore.kernel.org/all/20260323013408.429701-1-nick@khadas.com/
>
> Changes in v2:
> - Addressed feedback from Krzysztof Kozlowski:
> - Updated dt-bindings commit message to explain the hardware compatibility.
> - Separated the S4 compatible string in the YAML bindings to ensure it
> correctly falls back to the specific "amlogic,meson-g12a-saradc" rather
> than the generic "amlogic,meson-saradc".
> - Updated the S4 SoC dtsi to use the correct G12A fallback.
> - Added Martin's Reviewed-by tags where appropriate.
> - Link to v1: https://lore.kernel.org/all/20260228065840.702651-1-nick@khadas.com/
>
>
> Nick Xie (4):
> dt-bindings: iio: adc: amlogic,meson-saradc: add S4 compatible
> iio: adc: meson-saradc: add support for Meson S4
> arm64: dts: amlogic: meson-s4: add internal SARADC controller
> arm64: dts: amlogic: meson-s4-s905y4-khadas-vim1s: add Function key
> support
>
> .../iio/adc/amlogic,meson-saradc.yaml | 4 ++++
> .../amlogic/meson-s4-s905y4-khadas-vim1s.dts | 19 +++++++++++++++++++
> arch/arm64/boot/dts/amlogic/meson-s4.dtsi | 14 ++++++++++++++
> drivers/iio/adc/meson_saradc.c | 8 ++++++++
> 4 files changed, 45 insertions(+)
>
^ permalink raw reply
* Re: [PATCH v4 3/7] pinctrl: extract pinctrl_generic_to_map() from pinctrl_generic_pins_function_dt_node_to_map()
From: Frank Li @ 2026-03-26 19:47 UTC (permalink / raw)
To: Conor Dooley
Cc: Peter Rosin, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Rafał Miłecki, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, linux-kernel, linux-gpio,
devicetree, imx, linux-arm-kernel, Haibo Chen
In-Reply-To: <20260326-poncho-expanse-d30a9eded8e2@spud>
On Thu, Mar 26, 2026 at 06:55:01PM +0000, Conor Dooley wrote:
> On Thu, Mar 26, 2026 at 06:52:12PM +0000, Conor Dooley wrote:
> > On Wed, Mar 25, 2026 at 07:04:12PM -0400, Frank Li wrote:
> >
> > > diff --git a/drivers/pinctrl/pinctrl-generic.c b/drivers/pinctrl/pinctrl-generic.c
> > > index efb39c6a670331775855efdc8566102b5c6202ef..20a216ae63e91b69985ea4cfcd0b57103c6ca950 100644
> > > --- a/drivers/pinctrl/pinctrl-generic.c
> > > +++ b/drivers/pinctrl/pinctrl-generic.c
> > > @@ -17,29 +17,18 @@
> > > #include "pinctrl-utils.h"
> > > #include "pinmux.h"
> > >
> > > -static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *pctldev,
> >
> > > +int
> > > +pinctrl_generic_to_map(struct pinctrl_dev *pctldev, struct device_node *parent,
> >
> > Can you drop this stylistic change please? The
>
> Whoops, cut myself off. To be clear, what I am asking for is to keep the
> "int" etc on the same line as the function name. This function is new,
> but you did it for the existing function too and the comparison is here.
>
> >
> > > + struct device_node *np, struct pinctrl_map **maps,
> > > + unsigned int *num_maps, unsigned int *num_reserved_maps,
> > > + const char **group_names, unsigned int ngroups,
> > > + const char **functions, unsigned int *pins)
> > > {
> > > struct device *dev = pctldev->dev;
> > > - const char **functions;
> > > + int npins, ret, reserve = 1;
> > > + unsigned int num_configs;
> > > const char *group_name;
> > > unsigned long *configs;
> > > - unsigned int num_configs, pin, *pins;
> > > - int npins, ret, reserve = 1;
> > > -
> > > - npins = of_property_count_u32_elems(np, "pins");
> > > -
> > > - if (npins < 1) {
> > > - dev_err(dev, "invalid pinctrl group %pOFn.%pOFn %d\n",
> > > - parent, np, npins);
> > > - return npins;
> > > - }
> > >
> > > group_name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn.%pOFn", parent, np);
> > > if (!group_name)
> > > @@ -51,22 +40,6 @@ static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *p
> > > if (!pins)
> > > return -ENOMEM;
> >
> > This looks suspect. You've left the pins allocation behind:
> > pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL);
> > if (!pins)
> > return -ENOMEM;
> > but pinctrl_generic_pins_function_dt_subnode_to_map() has already
> > populated this array before calling the function.
what's means?
pinctrl_generic_pins_function_dt_subnode_to_map()
{
pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL);
...
pinctrl_generic_to_map();
}
pinctrl_generic_pins_function_dt_subnode_to_map() have not use this array.
Frank
> >
> > Also, this should probably be
> > Suggested-by: Conor Dooley <conor.dooley@microchip.com>
> >
> > Cheers,
> > Conor.
> >
> > >
> > > - functions = devm_kcalloc(dev, npins, sizeof(*functions), GFP_KERNEL);
> > > - if (!functions)
> > > - return -ENOMEM;
> > > -
> > > - for (int i = 0; i < npins; i++) {
> > > - ret = of_property_read_u32_index(np, "pins", i, &pin);
> > > - if (ret)
> > > - return ret;
> > > -
> > > - pins[i] = pin;
> > > -
> > > - ret = of_property_read_string(np, "function", &functions[i]);
> > > - if (ret)
> > > - return ret;
> > > - }
> > > -
> > > ret = pinctrl_utils_reserve_map(pctldev, maps, num_reserved_maps, num_maps, reserve);
> > > if (ret)
> > > return ret;
> > > @@ -103,6 +76,54 @@ static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *p
> > > return 0;
> > > };
> > >
> > > +static int
> > > +pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *pctldev,
> > > + struct device_node *parent,
> > > + struct device_node *np,
> > > + struct pinctrl_map **maps,
> > > + unsigned int *num_maps,
> > > + unsigned int *num_reserved_maps,
> > > + const char **group_names,
> > > + unsigned int ngroups)
> > > +{
> > > + struct device *dev = pctldev->dev;
> > > + unsigned int pin, *pins;
> > > + const char **functions;
> > > + int npins, ret;
> > > +
> > > + npins = of_property_count_u32_elems(np, "pins");
> > > +
> > > + if (npins < 1) {
> > > + dev_err(dev, "invalid pinctrl group %pOFn.%pOFn %d\n",
> > > + parent, np, npins);
> > > + return npins;
> > > + }
> > > +
> > > + pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL);
> > > + if (!pins)
> > > + return -ENOMEM;
> > > +
> > > + functions = devm_kcalloc(dev, npins, sizeof(*functions), GFP_KERNEL);
> > > + if (!functions)
> > > + return -ENOMEM;
> > > +
> > > + for (int i = 0; i < npins; i++) {
> > > + ret = of_property_read_u32_index(np, "pins", i, &pin);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + pins[i] = pin;
> > > +
> > > + ret = of_property_read_string(np, "function", &functions[i]);
> > > + if (ret)
> > > + return ret;
> > > + }
> > > +
> > > + return pinctrl_generic_to_map(pctldev, parent, np, maps, num_maps,
> > > + num_reserved_maps, group_names, ngroups,
> > > + functions, pins);
> > > +}
> > > +
> > > /*
> > > * For platforms that do not define groups or functions in the driver, but
> > > * instead use the devicetree to describe them. This function will, unlike
> > >
> > > --
> > > 2.43.0
> > >
>
>
^ permalink raw reply
* Re: [PATCH v3 1/2] media: dt-bindings: rockchip,rk3568-mipi-csi2: add rk3588 compatible
From: Rob Herring @ 2026-03-26 19:42 UTC (permalink / raw)
To: Michael Riesch
Cc: Mauro Carvalho Chehab, Sakari Ailus, Laurent Pinchart, Frank Li,
Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner, Kever Yang,
Collabora Kernel Team, linux-media, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
In-Reply-To: <703bcf13-ab45-4e9a-b80c-80911d85d819@collabora.com>
On Wed, Mar 25, 2026 at 4:34 PM Michael Riesch
<michael.riesch@collabora.com> wrote:
>
> Hi Rob,
>
> On 3/25/26 22:06, Rob Herring wrote:
> > On Wed, Mar 25, 2026 at 11:25:34AM +0100, Michael Riesch wrote:
> >> The RK3588 MIPI CSI-2 receivers are compatible to the ones found in
> >> the RK3568.
> >> Introduce a list of compatible variants and add the RK3588 variant to
> >> it.
> >>
> >> Acked-by: Rob Herring (Arm) <robh@kernel.org>
>
> First of all, apologies for applying your Acked-by tag. I figured
> resolving the merged conflict was trivial and impossible to screw up, but...
No worries. I would have kept it too.
> >> Signed-off-by: Michael Riesch <michael.riesch@collabora.com>
> >> ---
> >> .../devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml | 10 +++++++---
> >> 1 file changed, 7 insertions(+), 3 deletions(-)
> >>
> >> diff --git a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml
> >> index 4ac4a3b6f406..3d3b3cd78884 100644
> >> --- a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml
> >> +++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml
> >> @@ -16,9 +16,13 @@ description:
> >>
> >> properties:
> >> compatible:
> >> - enum:
> >> - - fsl,imx93-mipi-csi2
> >> - - rockchip,rk3568-mipi-csi2
> >> + oneOf:
> >> + - const: fsl,imx93-mipi-csi2
> >> + - const: rockchip,rk3568-mipi-csi2
> >
> > These 2 should be a single enum as they were before.
>
> ... hm. Well.
>
> First, do you mean
>
> properties:
> compatible:
> oneOf:
> - enum:
> - fsl,imx93-mipi-csi2
> - rockchip,rk3568-mipi-csi2
> - items:
> - enum:
> - rockchip,rk3588-mipi-csi2
> - const: rockchip,rk3568-mipi-csi2
> ?
Yes.
> If so, what is the practical difference?
First, then you aren't changing what's already there. For validation,
there is no difference other than failures with 'oneOf' give poor
error messages. It wouldn't be much better, just one less oneOf entry.
Rob
^ permalink raw reply
* [PATCH 0/6] phy: realtek: usb2: support for RTL9607C USB2 PHY
From: Rustam Adilov @ 2026-03-26 19:34 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Stanley Chang, linux-phy, devicetree, linux-kernel
Cc: Rustam Adilov
This patch series for Realtek USB2 PHY driver adds support for RTL9607C
USB2 PHY.
RTL9607C is a big endian MIPS CPU which is quite far from RTD series SoCs
supported by realtek usb2 phy driver, but the phy initilization is found
to be very indentical in most areas.
Most of the code was based on the Realtek's usb driver from the GPL tarball
in [1] and adjusted to fit into the realtek usb2 phy driver code format.
The patch series was split into smaller patches that add/change something
in the driver that are not exactly related to RTL9607C and that also
helps for easier review. That also means, patch 5 depends on all the prior
patches that come before it.
USB2 PHY on RTL9607C is primarly used for its internal OHCI/EHCI controllers.
[1] - https://github.com/jameywine/GPL-for-GP3000/blob/main/linux-5.10.x/arch/mips/rtl9607c/usb.c
Rustam Adilov (6):
phy: realtek: usb2: introduce vstatus/new_reg_req variables to driver
data
phy: realtek: usb2: introduce read and write functions to driver data
dt-bindings: phy: realtek,usb2phy.yaml: extend for resets and RTL9607C
support
phy: realtek: usb2: introduce reset controller struct
phy: realtek: usb2: add support for RTL9607C USB2 PHY
phy: realtek: usb2: Make configs available for MACH_REALTEK_RTL
.../bindings/phy/realtek,usb2phy.yaml | 23 ++-
drivers/phy/realtek/Kconfig | 2 +-
drivers/phy/realtek/phy-rtk-usb2.c | 189 ++++++++++++++++--
3 files changed, 194 insertions(+), 20 deletions(-)
--
2.53.0
^ permalink raw reply
* Re: [PATCH v4 2/3] ath10k: Add device-tree quirk to skip host cap QMI requests
From: Paul Sajna @ 2026-03-26 19:39 UTC (permalink / raw)
To: david, Johannes Berg, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jeff Johnson, Bjorn Andersson, Konrad Dybcio
Cc: Amit Pundir, linux-wireless, devicetree, ath10k, linux-kernel,
linux-arm-msm, phone-devel, David Heidelberg
In-Reply-To: <20260325-skip-host-cam-qmi-req-v4-2-bc08538487aa@ixit.cz>
March 25, 2026 at 5:57 PM, "David Heidelberg via B4 Relay" <devnull+david.ixit.cz@kernel.org mailto:devnull+david.ixit.cz@kernel.org?to=%22David%20Heidelberg%20via%20B4%20Relay%22%20%3Cdevnull%2Bdavid.ixit.cz%40kernel.org%3E > wrote:
>
> From: Amit Pundir <amit.pundir@linaro.org>
>
> Some firmware versions do not support the host capability QMI request.
> Since this request occurs before firmware-N.bin and board-M.bin are
> loaded, the quirk cannot be expressed in the firmware itself.
>
> The root cause is unclear, but there appears to be a generation of
> firmware that lacks host capability support.
>
> Without this quirk, ath10k_qmi_host_cap_send_sync() returns
> QMI_ERR_MALFORMED_MSG_V01 before loading the firmware. This error is not
> fatal - Wi-Fi services still come up successfully if the request is simply
> skipped.
>
> Add a device-tree quirk to skip the host capability QMI request on devices
> whose firmware does not support it.
>
> For example, firmware build
> "QC_IMAGE_VERSION_STRING=WLAN.HL.2.0.c3-00257-QCAHLSWMTPLZ-1"
> on Xiaomi Poco F1 phone requires this quirk.
>
> Suggested-by: Bjorn Andersson <andersson@kernel.org>
> Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
> Signed-off-by: David Heidelberg <david@ixit.cz>
> ---
> drivers/net/wireless/ath/ath10k/qmi.c | 13 ++++++++++---
> drivers/net/wireless/ath/ath10k/snoc.c | 3 +++
> drivers/net/wireless/ath/ath10k/snoc.h | 1 +
> 3 files changed, 14 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c
> index eebd78e7ff6bc..e7f90fd9e9b83 100644
> --- a/drivers/net/wireless/ath/ath10k/qmi.c
> +++ b/drivers/net/wireless/ath/ath10k/qmi.c
> @@ -808,6 +808,7 @@ ath10k_qmi_ind_register_send_sync_msg(struct ath10k_qmi *qmi)
> static void ath10k_qmi_event_server_arrive(struct ath10k_qmi *qmi)
> {
> struct ath10k *ar = qmi->ar;
> + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
> int ret;
>
> ret = ath10k_qmi_ind_register_send_sync_msg(qmi);
> @@ -819,9 +820,15 @@ static void ath10k_qmi_event_server_arrive(struct ath10k_qmi *qmi)
> return;
> }
>
> - ret = ath10k_qmi_host_cap_send_sync(qmi);
> - if (ret)
> - return;
> + /*
> + * Skip the host capability request for the firmware versions which
> + * do not support this feature.
> + */
> + if (!test_bit(ATH10K_SNOC_FLAG_SKIP_HOST_CAP_QUIRK, &ar_snoc->flags)) {
> + ret = ath10k_qmi_host_cap_send_sync(qmi);
> + if (ret)
> + return;
> + }
>
> ret = ath10k_qmi_msa_mem_info_send_sync_msg(qmi);
> if (ret)
> diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c
> index f72f236fb9eb3..3106502275781 100644
> --- a/drivers/net/wireless/ath/ath10k/snoc.c
> +++ b/drivers/net/wireless/ath/ath10k/snoc.c
> @@ -1362,6 +1362,9 @@ static void ath10k_snoc_quirks_init(struct ath10k *ar)
>
> if (of_property_read_bool(dev->of_node, "qcom,snoc-host-cap-8bit-quirk"))
> set_bit(ATH10K_SNOC_FLAG_8BIT_HOST_CAP_QUIRK, &ar_snoc->flags);
> +
> + if (of_property_read_bool(dev->of_node, "qcom,snoc-host-cap-skip-quirk"))
> + set_bit(ATH10K_SNOC_FLAG_SKIP_HOST_CAP_QUIRK, &ar_snoc->flags);
> }
>
> int ath10k_snoc_fw_indication(struct ath10k *ar, u64 type)
> diff --git a/drivers/net/wireless/ath/ath10k/snoc.h b/drivers/net/wireless/ath/ath10k/snoc.h
> index 1ecae34687c21..46574fd8f84ee 100644
> --- a/drivers/net/wireless/ath/ath10k/snoc.h
> +++ b/drivers/net/wireless/ath/ath10k/snoc.h
> @@ -51,6 +51,7 @@ enum ath10k_snoc_flags {
> ATH10K_SNOC_FLAG_MODEM_STOPPED,
> ATH10K_SNOC_FLAG_RECOVERY,
> ATH10K_SNOC_FLAG_8BIT_HOST_CAP_QUIRK,
> + ATH10K_SNOC_FLAG_SKIP_HOST_CAP_QUIRK,
> };
>
> struct clk_bulk_data;
>
> --
> 2.53.0
>
Tested-by: Paul Sajna <sajattack@postmarketos.org>
^ permalink raw reply
* Re: [PATCH v4 1/2] dt-bindings: embedded-controller: Add synology,microp device
From: Rob Herring @ 2026-03-26 19:36 UTC (permalink / raw)
To: Markus Probst
Cc: Krzysztof Kozlowski, Krzysztof Kozlowski, Conor Dooley,
Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Greg Kroah-Hartman, devicetree, linux-kernel,
rust-for-linux
In-Reply-To: <3281ba870fff981a1c37989000fdc6a8c448a304.camel@posteo.de>
On Thu, Mar 26, 2026 at 8:02 AM Markus Probst <markus.probst@posteo.de> wrote:
>
> On Wed, 2026-03-25 at 17:07 -0500, Rob Herring wrote:
> > On Sat, Mar 21, 2026 at 01:02:22PM +0000, Markus Probst wrote:
> > > On Sat, 2026-03-21 at 13:32 +0100, Krzysztof Kozlowski wrote:
> > > > On 21/03/2026 13:17, Markus Probst wrote:
> > > > > On Sat, 2026-03-21 at 11:21 +0100, Krzysztof Kozlowski wrote:
> > > > > > On Fri, Mar 20, 2026 at 11:09:53PM +0100, Markus Probst wrote:
> > > > > > > +
> > > > > > > +examples:
> > > > > > > + - |
> > > > > > > + #include <dt-bindings/leds/common.h>
> > > > > > > +
> > > > > > > + embedded-controller {
> > > > > > > + compatible = "synology,microp";
> > > > > > > +
> > > > > > > + power-led {
> > > > > > > + color = <LED_COLOR_ID_BLUE>;
> > > > > > > + function = LED_FUNCTION_POWER;
> > > > > > > + };
> > > > > > > +
> > > > > > > + status-led {
> > > > > > > + color = <LED_COLOR_ID_MULTI>;
> > > > > > > + function = LED_FUNCTION_STATUS;
> > > > > > > + };
> > > > > >
> > > > > > Where are other leds? Binding mentions 4.
> > > > > >
> > > > > Status and Power leds exist on every Synology NAS model I am aware of.
> > > > > But there are models which have additionally a usb or alert led. The
> > > > > device nodes for those leds should only be present, if they exist
> > > > > physically on the device.
> > > >
> > > > Then help me to understand - are these different models?
> > > Yes, even with different CPU architectures.
> > > How much the "microp" device differs is not clear, but the
> > > communication protocol is the same.
> > > >
> > > > EC is not a generic purpose component and is tightly coupled with the
> > > > actual board it is being present on. Unless exactly same board is used
> > > > in different models (unlikely) then the compatible defines the LEDs and
> > > > they are not needed in DT.
> > > So for instance "synology,ds923p-microp", "synology,ds723p-microp" etc.
> > > ?
> > >
> > > I can do that, but that would be many.
> >
> > How many is many?
> Estimated 300.
Okay, that's a lot and probably safe to say there are not 300
variations of the EC.
> As a side note: I only have 1 model I can test the driver with.
> >
> > > Having it generic seems more flexible.
> >
> > Is there firmware for these ECs? If so is it the same or different
> > firmware for each device? If the former or the functionality is really
> > trivial, then I'd be more comfortable with 1 or a few compatibles.
> The firmware is not public and the exact differences between them isn't
> documented. The communication protocol is the same though.
>
> >
> > Generic means you'll need to add quirk properties when there is some
> > difference the OS needs to handle which we'll reject. So stuck with one
> > compatible and no way to distinguish different ECs is anything but
> > flexible.
> Describing the physical leds that are present on the NAS device are not
> quirk properties, at least in my definition.
That's not what I mean. I mean things like this other device needs
some different timing for power-on/reset or delays between accesses or
some LED control is inverted or some protocol difference... Could be
about anything. The key thing is you have specific enough information
(compatible) to start with that you can handle any issue that comes up
*without* changing the DT.
As you said, you only have 1 device. Make the binding specific to that
1 device. If the next one that comes along can reuse the binding as
it, then great. Nothing to do. If it can't, then it gets its own new
compatible. Strictly speaking we would add a new compatible for each
device, but it's a judgement call that there aren't going to be
differences to handle. In this case, there likely aren't 300 versions
of h/w, the functionality is simple enough, and the functionality is
entirely optional (just a guess). But that's all really your argument
to make.
Rob
^ permalink raw reply
* [PATCH 6/6] phy: realtek: usb2: Make configs available for MACH_REALTEK_RTL
From: Rustam Adilov @ 2026-03-26 19:34 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Stanley Chang, linux-phy, devicetree, linux-kernel
Cc: Rustam Adilov
In-Reply-To: <20260326193419.48419-1-adilov@disroot.org>
Add the MACH_REALTEK_RTL to the if statement to make the config
options available for Realtek RTL SoCs as well.
Signed-off-by: Rustam Adilov <adilov@disroot.org>
---
drivers/phy/realtek/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/phy/realtek/Kconfig b/drivers/phy/realtek/Kconfig
index 75ac7e7c31ae..f9eadffacd18 100644
--- a/drivers/phy/realtek/Kconfig
+++ b/drivers/phy/realtek/Kconfig
@@ -3,7 +3,7 @@
# Phy drivers for Realtek platforms
#
-if ARCH_REALTEK || COMPILE_TEST
+if ARCH_REALTEK || MACH_REALTEK_RTL || COMPILE_TEST
config PHY_RTK_RTD_USB2PHY
tristate "Realtek RTD USB2 PHY Transceiver Driver"
--
2.53.0
^ permalink raw reply related
* [PATCH 5/6] phy: realtek: usb2: add support for RTL9607C USB2 PHY
From: Rustam Adilov @ 2026-03-26 19:34 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Stanley Chang, linux-phy, devicetree, linux-kernel
Cc: Rustam Adilov, Michael Zavertkin
In-Reply-To: <20260326193419.48419-1-adilov@disroot.org>
Add support for the usb2 phy of RTL9607C series based SoCs.
Add the macros and phy config struct for rtl9607.
RTL9607C requires to clear a "force host disconnect" bit in the
specific register (which is at an offset from reg_wrap_vstatus)
before proceeding with phy parameter writes.
Add the bool variable to the driver data struct and hide this whole
procedure under the if statement that checks this new variable.
Co-developed-by: Michael Zavertkin <misha.zavertkin@mail.ru>
Signed-off-by: Michael Zavertkin <misha.zavertkin@mail.ru>
Signed-off-by: Rustam Adilov <adilov@disroot.org>
---
drivers/phy/realtek/phy-rtk-usb2.c | 57 ++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
diff --git a/drivers/phy/realtek/phy-rtk-usb2.c b/drivers/phy/realtek/phy-rtk-usb2.c
index 070cba1e0e0a..bf22d12681dc 100644
--- a/drivers/phy/realtek/phy-rtk-usb2.c
+++ b/drivers/phy/realtek/phy-rtk-usb2.c
@@ -26,6 +26,12 @@
#define PHY_VCTRL_SHIFT 8
#define PHY_REG_DATA_MASK 0xff
+#define PHY_9607_VSTS_BUSY BIT(17)
+#define PHY_9607_NEW_REG_REQ BIT(13)
+
+#define PHY_9607_FORCE_DISCONNECT_REG 0x10
+#define PHY_9607_FORCE_DISCONNECT_BIT BIT(5)
+
#define GET_LOW_NIBBLE(addr) ((addr) & 0x0f)
#define GET_HIGH_NIBBLE(addr) (((addr) & 0xf0) >> 4)
@@ -109,6 +115,7 @@ struct phy_cfg {
u32 (*read)(void __iomem *reg);
void (*write)(u32 val, void __iomem *reg);
+ bool force_host_disconnect;
};
struct phy_parameter {
@@ -614,6 +621,16 @@ static int do_rtk_phy_init(struct rtk_phy *rtk_phy, int index)
goto do_toggle;
}
+ if (phy_cfg->force_host_disconnect) {
+ /* disable force-host-disconnect */
+ u32 temp = readl(phy_reg->reg_wrap_vstatus + PHY_9607_FORCE_DISCONNECT_REG);
+
+ temp &= ~PHY_9607_FORCE_DISCONNECT_BIT;
+ writel(temp, phy_reg->reg_wrap_vstatus + PHY_9607_FORCE_DISCONNECT_REG);
+
+ mdelay(10);
+ }
+
/* Set page 0 */
phy_data_page = phy_cfg->page0;
rtk_phy_set_page(phy_reg, 0);
@@ -1141,6 +1158,7 @@ static const struct phy_cfg rtd1295_phy_cfg = {
.new_reg_req = PHY_NEW_REG_REQ,
.read = phy_read,
.write = phy_write,
+ .force_host_disconnect = false,
};
static const struct phy_cfg rtd1395_phy_cfg = {
@@ -1170,6 +1188,7 @@ static const struct phy_cfg rtd1395_phy_cfg = {
.new_reg_req = PHY_NEW_REG_REQ,
.read = phy_read,
.write = phy_write,
+ .force_host_disconnect = false,
};
static const struct phy_cfg rtd1395_phy_cfg_2port = {
@@ -1199,6 +1218,7 @@ static const struct phy_cfg rtd1395_phy_cfg_2port = {
.new_reg_req = PHY_NEW_REG_REQ,
.read = phy_read,
.write = phy_write,
+ .force_host_disconnect = false,
};
static const struct phy_cfg rtd1619_phy_cfg = {
@@ -1226,6 +1246,7 @@ static const struct phy_cfg rtd1619_phy_cfg = {
.new_reg_req = PHY_NEW_REG_REQ,
.read = phy_read,
.write = phy_write,
+ .force_host_disconnect = false,
};
static const struct phy_cfg rtd1319_phy_cfg = {
@@ -1257,6 +1278,7 @@ static const struct phy_cfg rtd1319_phy_cfg = {
.new_reg_req = PHY_NEW_REG_REQ,
.read = phy_read,
.write = phy_write,
+ .force_host_disconnect = false,
};
static const struct phy_cfg rtd1312c_phy_cfg = {
@@ -1287,6 +1309,7 @@ static const struct phy_cfg rtd1312c_phy_cfg = {
.new_reg_req = PHY_NEW_REG_REQ,
.read = phy_read,
.write = phy_write,
+ .force_host_disconnect = false,
};
static const struct phy_cfg rtd1619b_phy_cfg = {
@@ -1317,6 +1340,7 @@ static const struct phy_cfg rtd1619b_phy_cfg = {
.new_reg_req = PHY_NEW_REG_REQ,
.read = phy_read,
.write = phy_write,
+ .force_host_disconnect = false,
};
static const struct phy_cfg rtd1319d_phy_cfg = {
@@ -1347,6 +1371,7 @@ static const struct phy_cfg rtd1319d_phy_cfg = {
.new_reg_req = PHY_NEW_REG_REQ,
.read = phy_read,
.write = phy_write,
+ .force_host_disconnect = false,
};
static const struct phy_cfg rtd1315e_phy_cfg = {
@@ -1378,6 +1403,37 @@ static const struct phy_cfg rtd1315e_phy_cfg = {
.new_reg_req = PHY_NEW_REG_REQ,
.read = phy_read,
.write = phy_write,
+ .force_host_disconnect = false,
+};
+
+static const struct phy_cfg rtl9607_phy_cfg = {
+ .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+ .page0 = { [0] = {0xe0, 0x95},
+ [4] = {0xe4, 0x6a},
+ [12] = {0xf3, 0x31}, },
+ .page1_size = MAX_USB_PHY_PAGE1_DATA_SIZE,
+ .page1 = { [0] = {0xe0, 0x26}, },
+ .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE,
+ .page2 = { [7] = {0xe7, 0x33}, },
+ .num_phy = 1,
+ .check_efuse = false,
+ .check_efuse_version = CHECK_EFUSE_V2,
+ .efuse_dc_driving_rate = EFUS_USB_DC_CAL_RATE,
+ .dc_driving_mask = 0x1f,
+ .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+ .dc_disconnect_mask = 0xf,
+ .usb_dc_disconnect_at_page0 = true,
+ .do_toggle = true,
+ .do_toggle_driving = false,
+ .driving_updated_for_dev_dis = 0x8,
+ .use_default_parameter = false,
+ .is_double_sensitivity_mode = true,
+ .vstatus_offset = 0xc,
+ .vstatus_busy = PHY_9607_VSTS_BUSY,
+ .new_reg_req = PHY_9607_NEW_REG_REQ,
+ .read = phy_read_le,
+ .write = phy_write_le,
+ .force_host_disconnect = true,
};
static const struct of_device_id usbphy_rtk_dt_match[] = {
@@ -1390,6 +1446,7 @@ static const struct of_device_id usbphy_rtk_dt_match[] = {
{ .compatible = "realtek,rtd1395-usb2phy-2port", .data = &rtd1395_phy_cfg_2port },
{ .compatible = "realtek,rtd1619-usb2phy", .data = &rtd1619_phy_cfg },
{ .compatible = "realtek,rtd1619b-usb2phy", .data = &rtd1619b_phy_cfg },
+ { .compatible = "realtek,rtl9607-usb2phy", .data = &rtl9607_phy_cfg },
{},
};
MODULE_DEVICE_TABLE(of, usbphy_rtk_dt_match);
--
2.53.0
^ permalink raw reply related
* [PATCH 4/6] phy: realtek: usb2: introduce reset controller struct
From: Rustam Adilov @ 2026-03-26 19:34 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Stanley Chang, linux-phy, devicetree, linux-kernel
Cc: Rustam Adilov, Michael Zavertkin
In-Reply-To: <20260326193419.48419-1-adilov@disroot.org>
In RTL9607C, there is so called "IP Enable Controller" which resemble
reset controller with reset lines and is used for various things like
USB, PCIE, GMAC and such.
Introduce the reset_control struct to this driver to handle deasserting
usb2 phy reset line.
Make use of the function devm_reset_control_array_get_optional_exclusive()
function to get the reset controller and since existing RTD SoCs don't
specify the resets we can have a cleaner code.
Co-developed-by: Michael Zavertkin <misha.zavertkin@mail.ru>
Signed-off-by: Michael Zavertkin <misha.zavertkin@mail.ru>
Signed-off-by: Rustam Adilov <adilov@disroot.org>
---
drivers/phy/realtek/phy-rtk-usb2.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/phy/realtek/phy-rtk-usb2.c b/drivers/phy/realtek/phy-rtk-usb2.c
index e65b8525b88b..070cba1e0e0a 100644
--- a/drivers/phy/realtek/phy-rtk-usb2.c
+++ b/drivers/phy/realtek/phy-rtk-usb2.c
@@ -17,6 +17,7 @@
#include <linux/sys_soc.h>
#include <linux/mfd/syscon.h>
#include <linux/phy/phy.h>
+#include <linux/reset.h>
#include <linux/usb.h>
/* GUSB2PHYACCn register */
@@ -130,6 +131,7 @@ struct rtk_phy {
struct phy_cfg *phy_cfg;
int num_phy;
struct phy_parameter *phy_parameter;
+ struct reset_control *phy_rst;
struct dentry *debug_dir;
};
@@ -602,6 +604,10 @@ static int do_rtk_phy_init(struct rtk_phy *rtk_phy, int index)
phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index];
phy_reg = &phy_parameter->phy_reg;
+ reset_control_deassert(rtk_phy->phy_rst);
+
+ mdelay(5);
+
if (phy_cfg->use_default_parameter) {
dev_dbg(rtk_phy->dev, "%s phy#%d use default parameter\n",
__func__, index);
@@ -1069,6 +1075,12 @@ static int rtk_usb2phy_probe(struct platform_device *pdev)
rtk_phy->num_phy = phy_cfg->num_phy;
+ rtk_phy->phy_rst = devm_reset_control_array_get_optional_exclusive(dev);
+ if (IS_ERR(rtk_phy->phy_rst)) {
+ dev_err(dev, "usb2 phy resets are not working\n");
+ return PTR_ERR(rtk_phy->phy_rst);
+ }
+
ret = parse_phy_data(rtk_phy);
if (ret)
goto err;
--
2.53.0
^ permalink raw reply related
* [PATCH 3/6] dt-bindings: phy: realtek,usb2phy.yaml: extend for resets and RTL9607C support
From: Rustam Adilov @ 2026-03-26 19:34 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Stanley Chang, linux-phy, devicetree, linux-kernel
Cc: Rustam Adilov
In-Reply-To: <20260326193419.48419-1-adilov@disroot.org>
Add the "realtek,rtl9607-usb2phy" compatible for USB2 PHY on the RTL9607C
SoC series.
Add a resets property to properties to describe the usb2phy reset line.
In RTL9607C, USB2 PHY reset line is from "IP Enable controller" which is
multipurpose and handle activating various SoC peripherals.
It is unclear whether RTD SoCs have something similar to that but make it
available to them anyway.
RTL9607C requires the "resets" to be specified so add the corresponding
if check for the "realtek,rtl9607-usb2phy" compatible.
Signed-off-by: Rustam Adilov <adilov@disroot.org>
---
.../bindings/phy/realtek,usb2phy.yaml | 23 ++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/phy/realtek,usb2phy.yaml b/Documentation/devicetree/bindings/phy/realtek,usb2phy.yaml
index 9911ada39ee7..cdc7b72e5f60 100644
--- a/Documentation/devicetree/bindings/phy/realtek,usb2phy.yaml
+++ b/Documentation/devicetree/bindings/phy/realtek,usb2phy.yaml
@@ -11,10 +11,12 @@ maintainers:
- Stanley Chang <stanley_chang@realtek.com>
description: |
- Realtek USB 2.0 PHY support the digital home center (DHC) RTD series SoCs.
+ Realtek USB 2.0 PHY support the digital home center (DHC) RTD and
+ RTL9607C series SoCs.
The USB 2.0 PHY driver is designed to support the XHCI controller. The SoCs
support multiple XHCI controllers. One PHY device node maps to one XHCI
controller.
+ This driver also supports the OCHI and EHCI controllers.
RTD1295/RTD1619 SoCs USB
The USB architecture includes three XHCI controllers.
@@ -57,6 +59,12 @@ description: |
XHCI controller#1 -- usb2phy -- phy#0
XHCI controller#2 -- usb2phy -- phy#0
+ RTL9607C SoCs USB
+ The USB architecture includes OHCI and EHCI controllers.
+ Both of them map to one USB2.0 PHY.
+ OHCI controller#0 -- usb2phy -- phy#0
+ EHCI controller#0 -- usb2phy -- phy#0
+
properties:
compatible:
enum:
@@ -69,6 +77,7 @@ properties:
- realtek,rtd1395-usb2phy-2port
- realtek,rtd1619-usb2phy
- realtek,rtd1619b-usb2phy
+ - realtek,rtl9607-usb2phy
reg:
items:
@@ -130,6 +139,9 @@ properties:
minimum: -8
maximum: 8
+ resets:
+ maxItems: 1
+
required:
- compatible
- reg
@@ -157,6 +169,15 @@ allOf:
then:
properties:
realtek,driving-level-compensate: false
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - realtek,rtl9607-usb2phy
+ then:
+ required:
+ - resets
additionalProperties: false
--
2.53.0
^ permalink raw reply related
* [PATCH 2/6] phy: realtek: usb2: introduce read and write functions to driver data
From: Rustam Adilov @ 2026-03-26 19:34 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Stanley Chang, linux-phy, devicetree, linux-kernel
Cc: Rustam Adilov, Michael Zavertkin
In-Reply-To: <20260326193419.48419-1-adilov@disroot.org>
RTL9607C is a big endian SoC but has little endian USB host controller and
thus, reads and writes to the reg_gusb2phyacc0 should go through
le32_to_cpu and cpu_to_le32 functions respectively. This doesn't apply to
vstatus register though.
To handle this situation, introduce read and write functions to the driver
data and create 2 variations of reads and write function with le32 function
in it and without.
Adjust all instances of utmi_wait_register function to now include the read
function as one of its arguments.
Assign the existing phy configuration for RTD SoCs to the default phy_read
and phy_write functions.
Co-developed-by: Michael Zavertkin <misha.zavertkin@mail.ru>
Signed-off-by: Michael Zavertkin <misha.zavertkin@mail.ru>
Signed-off-by: Rustam Adilov <adilov@disroot.org>
---
drivers/phy/realtek/phy-rtk-usb2.c | 73 ++++++++++++++++++++++++------
1 file changed, 60 insertions(+), 13 deletions(-)
diff --git a/drivers/phy/realtek/phy-rtk-usb2.c b/drivers/phy/realtek/phy-rtk-usb2.c
index f5d2f0c3376a..e65b8525b88b 100644
--- a/drivers/phy/realtek/phy-rtk-usb2.c
+++ b/drivers/phy/realtek/phy-rtk-usb2.c
@@ -67,6 +67,9 @@ struct phy_reg {
int vstatus_offset;
int vstatus_busy;
int new_reg_req;
+
+ u32 (*read)(void __iomem *reg);
+ void (*write)(u32 val, void __iomem *reg);
};
struct phy_data {
@@ -102,6 +105,9 @@ struct phy_cfg {
int vstatus_offset;
int vstatus_busy;
int new_reg_req;
+
+ u32 (*read)(void __iomem *reg);
+ void (*write)(u32 val, void __iomem *reg);
};
struct phy_parameter {
@@ -128,6 +134,26 @@ struct rtk_phy {
struct dentry *debug_dir;
};
+static inline u32 phy_read(void __iomem *reg)
+{
+ return readl(reg);
+}
+
+static inline u32 phy_read_le(void __iomem *reg)
+{
+ return le32_to_cpu(readl(reg));
+}
+
+static inline void phy_write(u32 val, void __iomem *reg)
+{
+ writel(val, reg);
+}
+
+static inline void phy_write_le(u32 val, void __iomem *reg)
+{
+ writel(cpu_to_le32(val), reg);
+}
+
/* mapping 0xE0 to 0 ... 0xE7 to 7, 0xF0 to 8 ,,, 0xF7 to 15 */
static inline int page_addr_to_array_index(u8 addr)
{
@@ -144,12 +170,13 @@ static inline u8 array_index_to_page_addr(int index)
#define PHY_IO_TIMEOUT_USEC (50000)
#define PHY_IO_DELAY_US (100)
-static inline int utmi_wait_register(void __iomem *reg, u32 mask, u32 result)
+static inline int utmi_wait_register(u32 (*read)(void __iomem *reg), void __iomem *reg, u32 mask,
+ u32 result)
{
int ret;
unsigned int val;
- ret = read_poll_timeout(readl, val, ((val & mask) == result),
+ ret = read_poll_timeout(read, val, ((val & mask) == result),
PHY_IO_DELAY_US, PHY_IO_TIMEOUT_USEC, false, reg);
if (ret) {
pr_err("%s can't program USB phy\n", __func__);
@@ -168,25 +195,25 @@ static char rtk_phy_read(struct phy_reg *phy_reg, char addr)
addr -= OFFEST_PHY_READ;
/* polling until VBusy == 0 */
- ret = utmi_wait_register(reg_gusb2phyacc0, phy_reg->vstatus_busy, 0);
+ ret = utmi_wait_register(phy_reg->read, reg_gusb2phyacc0, phy_reg->vstatus_busy, 0);
if (ret)
return (char)ret;
/* VCtrl = low nibble of addr, and set PHY_NEW_REG_REQ */
val = phy_reg->new_reg_req | (GET_LOW_NIBBLE(addr) << PHY_VCTRL_SHIFT);
- writel(val, reg_gusb2phyacc0);
- ret = utmi_wait_register(reg_gusb2phyacc0, phy_reg->vstatus_busy, 0);
+ phy_reg->write(val, reg_gusb2phyacc0);
+ ret = utmi_wait_register(phy_reg->read, reg_gusb2phyacc0, phy_reg->vstatus_busy, 0);
if (ret)
return (char)ret;
/* VCtrl = high nibble of addr, and set PHY_NEW_REG_REQ */
val = phy_reg->new_reg_req | (GET_HIGH_NIBBLE(addr) << PHY_VCTRL_SHIFT);
- writel(val, reg_gusb2phyacc0);
- ret = utmi_wait_register(reg_gusb2phyacc0, phy_reg->vstatus_busy, 0);
+ phy_reg->write(val, reg_gusb2phyacc0);
+ ret = utmi_wait_register(phy_reg->read, reg_gusb2phyacc0, phy_reg->vstatus_busy, 0);
if (ret)
return (char)ret;
- val = readl(reg_gusb2phyacc0);
+ val = phy_reg->read(reg_gusb2phyacc0);
return (char)(val & PHY_REG_DATA_MASK);
}
@@ -202,23 +229,23 @@ static int rtk_phy_write(struct phy_reg *phy_reg, char addr, char data)
/* write data to VStatusOut2 (data output to phy) */
writel((u32)data << shift_bits, reg_wrap_vstatus + phy_reg->vstatus_offset);
- ret = utmi_wait_register(reg_gusb2phyacc0, phy_reg->vstatus_busy, 0);
+ ret = utmi_wait_register(phy_reg->read, reg_gusb2phyacc0, phy_reg->vstatus_busy, 0);
if (ret)
return ret;
/* VCtrl = low nibble of addr, set PHY_NEW_REG_REQ */
val = phy_reg->new_reg_req | (GET_LOW_NIBBLE(addr) << PHY_VCTRL_SHIFT);
- writel(val, reg_gusb2phyacc0);
- ret = utmi_wait_register(reg_gusb2phyacc0, phy_reg->vstatus_busy, 0);
+ phy_reg->write(val, reg_gusb2phyacc0);
+ ret = utmi_wait_register(phy_reg->read, reg_gusb2phyacc0, phy_reg->vstatus_busy, 0);
if (ret)
return ret;
/* VCtrl = high nibble of addr, set PHY_NEW_REG_REQ */
val = phy_reg->new_reg_req | (GET_HIGH_NIBBLE(addr) << PHY_VCTRL_SHIFT);
- writel(val, reg_gusb2phyacc0);
- ret = utmi_wait_register(reg_gusb2phyacc0, phy_reg->vstatus_busy, 0);
+ phy_reg->write(val, reg_gusb2phyacc0);
+ ret = utmi_wait_register(phy_reg->read, reg_gusb2phyacc0, phy_reg->vstatus_busy, 0);
if (ret)
return ret;
@@ -984,6 +1011,8 @@ static int parse_phy_data(struct rtk_phy *rtk_phy)
phy_parameter->phy_reg.vstatus_offset = phy_cfg->vstatus_offset;
phy_parameter->phy_reg.vstatus_busy = phy_cfg->vstatus_busy;
phy_parameter->phy_reg.new_reg_req = phy_cfg->new_reg_req;
+ phy_parameter->phy_reg.read = phy_cfg->read;
+ phy_parameter->phy_reg.write = phy_cfg->write;
if (of_property_read_bool(np, "realtek,inverse-hstx-sync-clock"))
phy_parameter->inverse_hstx_sync_clock = true;
@@ -1098,6 +1127,8 @@ static const struct phy_cfg rtd1295_phy_cfg = {
.vstatus_offset = 0,
.vstatus_busy = PHY_VSTS_BUSY,
.new_reg_req = PHY_NEW_REG_REQ,
+ .read = phy_read,
+ .write = phy_write,
};
static const struct phy_cfg rtd1395_phy_cfg = {
@@ -1125,6 +1156,8 @@ static const struct phy_cfg rtd1395_phy_cfg = {
.vstatus_offset = 0,
.vstatus_busy = PHY_VSTS_BUSY,
.new_reg_req = PHY_NEW_REG_REQ,
+ .read = phy_read,
+ .write = phy_write,
};
static const struct phy_cfg rtd1395_phy_cfg_2port = {
@@ -1152,6 +1185,8 @@ static const struct phy_cfg rtd1395_phy_cfg_2port = {
.vstatus_offset = 0,
.vstatus_busy = PHY_VSTS_BUSY,
.new_reg_req = PHY_NEW_REG_REQ,
+ .read = phy_read,
+ .write = phy_write,
};
static const struct phy_cfg rtd1619_phy_cfg = {
@@ -1177,6 +1212,8 @@ static const struct phy_cfg rtd1619_phy_cfg = {
.vstatus_offset = 0,
.vstatus_busy = PHY_VSTS_BUSY,
.new_reg_req = PHY_NEW_REG_REQ,
+ .read = phy_read,
+ .write = phy_write,
};
static const struct phy_cfg rtd1319_phy_cfg = {
@@ -1206,6 +1243,8 @@ static const struct phy_cfg rtd1319_phy_cfg = {
.vstatus_offset = 0,
.vstatus_busy = PHY_VSTS_BUSY,
.new_reg_req = PHY_NEW_REG_REQ,
+ .read = phy_read,
+ .write = phy_write,
};
static const struct phy_cfg rtd1312c_phy_cfg = {
@@ -1234,6 +1273,8 @@ static const struct phy_cfg rtd1312c_phy_cfg = {
.vstatus_offset = 0,
.vstatus_busy = PHY_VSTS_BUSY,
.new_reg_req = PHY_NEW_REG_REQ,
+ .read = phy_read,
+ .write = phy_write,
};
static const struct phy_cfg rtd1619b_phy_cfg = {
@@ -1262,6 +1303,8 @@ static const struct phy_cfg rtd1619b_phy_cfg = {
.vstatus_offset = 0,
.vstatus_busy = PHY_VSTS_BUSY,
.new_reg_req = PHY_NEW_REG_REQ,
+ .read = phy_read,
+ .write = phy_write,
};
static const struct phy_cfg rtd1319d_phy_cfg = {
@@ -1290,6 +1333,8 @@ static const struct phy_cfg rtd1319d_phy_cfg = {
.vstatus_offset = 0,
.vstatus_busy = PHY_VSTS_BUSY,
.new_reg_req = PHY_NEW_REG_REQ,
+ .read = phy_read,
+ .write = phy_write,
};
static const struct phy_cfg rtd1315e_phy_cfg = {
@@ -1319,6 +1364,8 @@ static const struct phy_cfg rtd1315e_phy_cfg = {
.vstatus_offset = 0,
.vstatus_busy = PHY_VSTS_BUSY,
.new_reg_req = PHY_NEW_REG_REQ,
+ .read = phy_read,
+ .write = phy_write,
};
static const struct of_device_id usbphy_rtk_dt_match[] = {
--
2.53.0
^ permalink raw reply related
* [PATCH 1/6] phy: realtek: usb2: introduce vstatus/new_reg_req variables to driver data
From: Rustam Adilov @ 2026-03-26 19:34 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Stanley Chang, linux-phy, devicetree, linux-kernel
Cc: Rustam Adilov, Michael Zavertkin
In-Reply-To: <20260326193419.48419-1-adilov@disroot.org>
In RTL9607C SoC, the vstatus register is located at a certain offset from
the base and so introduce the vstatus_offset to handle it.
Busy bit of the vstatus and new_reg_req bit are also different and so
introduce these variables to the driver data as well.
Add these variables to the pre-existing phy cfg structs for RTD SoCs and
assign them the default values.
Co-developed-by: Michael Zavertkin <misha.zavertkin@mail.ru>
Signed-off-by: Michael Zavertkin <misha.zavertkin@mail.ru>
Signed-off-by: Rustam Adilov <adilov@disroot.org>
---
drivers/phy/realtek/phy-rtk-usb2.c | 59 ++++++++++++++++++++++++------
1 file changed, 48 insertions(+), 11 deletions(-)
diff --git a/drivers/phy/realtek/phy-rtk-usb2.c b/drivers/phy/realtek/phy-rtk-usb2.c
index 248550ef98ca..f5d2f0c3376a 100644
--- a/drivers/phy/realtek/phy-rtk-usb2.c
+++ b/drivers/phy/realtek/phy-rtk-usb2.c
@@ -64,6 +64,9 @@ struct phy_reg {
void __iomem *reg_wrap_vstatus;
void __iomem *reg_gusb2phyacc0;
int vstatus_index;
+ int vstatus_offset;
+ int vstatus_busy;
+ int new_reg_req;
};
struct phy_data {
@@ -96,6 +99,9 @@ struct phy_cfg {
bool do_toggle_driving;
bool use_default_parameter;
bool is_double_sensitivity_mode;
+ int vstatus_offset;
+ int vstatus_busy;
+ int new_reg_req;
};
struct phy_parameter {
@@ -162,21 +168,21 @@ static char rtk_phy_read(struct phy_reg *phy_reg, char addr)
addr -= OFFEST_PHY_READ;
/* polling until VBusy == 0 */
- ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+ ret = utmi_wait_register(reg_gusb2phyacc0, phy_reg->vstatus_busy, 0);
if (ret)
return (char)ret;
/* VCtrl = low nibble of addr, and set PHY_NEW_REG_REQ */
- val = PHY_NEW_REG_REQ | (GET_LOW_NIBBLE(addr) << PHY_VCTRL_SHIFT);
+ val = phy_reg->new_reg_req | (GET_LOW_NIBBLE(addr) << PHY_VCTRL_SHIFT);
writel(val, reg_gusb2phyacc0);
- ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+ ret = utmi_wait_register(reg_gusb2phyacc0, phy_reg->vstatus_busy, 0);
if (ret)
return (char)ret;
/* VCtrl = high nibble of addr, and set PHY_NEW_REG_REQ */
- val = PHY_NEW_REG_REQ | (GET_HIGH_NIBBLE(addr) << PHY_VCTRL_SHIFT);
+ val = phy_reg->new_reg_req | (GET_HIGH_NIBBLE(addr) << PHY_VCTRL_SHIFT);
writel(val, reg_gusb2phyacc0);
- ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+ ret = utmi_wait_register(reg_gusb2phyacc0, phy_reg->vstatus_busy, 0);
if (ret)
return (char)ret;
@@ -194,25 +200,25 @@ static int rtk_phy_write(struct phy_reg *phy_reg, char addr, char data)
int ret = 0;
/* write data to VStatusOut2 (data output to phy) */
- writel((u32)data << shift_bits, reg_wrap_vstatus);
+ writel((u32)data << shift_bits, reg_wrap_vstatus + phy_reg->vstatus_offset);
- ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+ ret = utmi_wait_register(reg_gusb2phyacc0, phy_reg->vstatus_busy, 0);
if (ret)
return ret;
/* VCtrl = low nibble of addr, set PHY_NEW_REG_REQ */
- val = PHY_NEW_REG_REQ | (GET_LOW_NIBBLE(addr) << PHY_VCTRL_SHIFT);
+ val = phy_reg->new_reg_req | (GET_LOW_NIBBLE(addr) << PHY_VCTRL_SHIFT);
writel(val, reg_gusb2phyacc0);
- ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+ ret = utmi_wait_register(reg_gusb2phyacc0, phy_reg->vstatus_busy, 0);
if (ret)
return ret;
/* VCtrl = high nibble of addr, set PHY_NEW_REG_REQ */
- val = PHY_NEW_REG_REQ | (GET_HIGH_NIBBLE(addr) << PHY_VCTRL_SHIFT);
+ val = phy_reg->new_reg_req | (GET_HIGH_NIBBLE(addr) << PHY_VCTRL_SHIFT);
writel(val, reg_gusb2phyacc0);
- ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+ ret = utmi_wait_register(reg_gusb2phyacc0, phy_reg->vstatus_busy, 0);
if (ret)
return ret;
@@ -957,6 +963,7 @@ static int get_phy_data_by_efuse(struct rtk_phy *rtk_phy,
static int parse_phy_data(struct rtk_phy *rtk_phy)
{
+ struct phy_cfg *phy_cfg = rtk_phy->phy_cfg;
struct device *dev = rtk_phy->dev;
struct device_node *np = dev->of_node;
struct phy_parameter *phy_parameter;
@@ -974,6 +981,9 @@ static int parse_phy_data(struct rtk_phy *rtk_phy)
phy_parameter->phy_reg.reg_wrap_vstatus = of_iomap(np, 0);
phy_parameter->phy_reg.reg_gusb2phyacc0 = of_iomap(np, 1) + index;
phy_parameter->phy_reg.vstatus_index = index;
+ phy_parameter->phy_reg.vstatus_offset = phy_cfg->vstatus_offset;
+ phy_parameter->phy_reg.vstatus_busy = phy_cfg->vstatus_busy;
+ phy_parameter->phy_reg.new_reg_req = phy_cfg->new_reg_req;
if (of_property_read_bool(np, "realtek,inverse-hstx-sync-clock"))
phy_parameter->inverse_hstx_sync_clock = true;
@@ -1085,6 +1095,9 @@ static const struct phy_cfg rtd1295_phy_cfg = {
.driving_updated_for_dev_dis = 0xf,
.use_default_parameter = false,
.is_double_sensitivity_mode = false,
+ .vstatus_offset = 0,
+ .vstatus_busy = PHY_VSTS_BUSY,
+ .new_reg_req = PHY_NEW_REG_REQ,
};
static const struct phy_cfg rtd1395_phy_cfg = {
@@ -1109,6 +1122,9 @@ static const struct phy_cfg rtd1395_phy_cfg = {
.driving_updated_for_dev_dis = 0xf,
.use_default_parameter = false,
.is_double_sensitivity_mode = false,
+ .vstatus_offset = 0,
+ .vstatus_busy = PHY_VSTS_BUSY,
+ .new_reg_req = PHY_NEW_REG_REQ,
};
static const struct phy_cfg rtd1395_phy_cfg_2port = {
@@ -1133,6 +1149,9 @@ static const struct phy_cfg rtd1395_phy_cfg_2port = {
.driving_updated_for_dev_dis = 0xf,
.use_default_parameter = false,
.is_double_sensitivity_mode = false,
+ .vstatus_offset = 0,
+ .vstatus_busy = PHY_VSTS_BUSY,
+ .new_reg_req = PHY_NEW_REG_REQ,
};
static const struct phy_cfg rtd1619_phy_cfg = {
@@ -1155,6 +1174,9 @@ static const struct phy_cfg rtd1619_phy_cfg = {
.driving_updated_for_dev_dis = 0xf,
.use_default_parameter = false,
.is_double_sensitivity_mode = false,
+ .vstatus_offset = 0,
+ .vstatus_busy = PHY_VSTS_BUSY,
+ .new_reg_req = PHY_NEW_REG_REQ,
};
static const struct phy_cfg rtd1319_phy_cfg = {
@@ -1181,6 +1203,9 @@ static const struct phy_cfg rtd1319_phy_cfg = {
.driving_updated_for_dev_dis = 0xf,
.use_default_parameter = false,
.is_double_sensitivity_mode = true,
+ .vstatus_offset = 0,
+ .vstatus_busy = PHY_VSTS_BUSY,
+ .new_reg_req = PHY_NEW_REG_REQ,
};
static const struct phy_cfg rtd1312c_phy_cfg = {
@@ -1206,6 +1231,9 @@ static const struct phy_cfg rtd1312c_phy_cfg = {
.driving_updated_for_dev_dis = 0xf,
.use_default_parameter = false,
.is_double_sensitivity_mode = true,
+ .vstatus_offset = 0,
+ .vstatus_busy = PHY_VSTS_BUSY,
+ .new_reg_req = PHY_NEW_REG_REQ,
};
static const struct phy_cfg rtd1619b_phy_cfg = {
@@ -1231,6 +1259,9 @@ static const struct phy_cfg rtd1619b_phy_cfg = {
.driving_updated_for_dev_dis = 0x8,
.use_default_parameter = false,
.is_double_sensitivity_mode = true,
+ .vstatus_offset = 0,
+ .vstatus_busy = PHY_VSTS_BUSY,
+ .new_reg_req = PHY_NEW_REG_REQ,
};
static const struct phy_cfg rtd1319d_phy_cfg = {
@@ -1256,6 +1287,9 @@ static const struct phy_cfg rtd1319d_phy_cfg = {
.driving_updated_for_dev_dis = 0x8,
.use_default_parameter = false,
.is_double_sensitivity_mode = true,
+ .vstatus_offset = 0,
+ .vstatus_busy = PHY_VSTS_BUSY,
+ .new_reg_req = PHY_NEW_REG_REQ,
};
static const struct phy_cfg rtd1315e_phy_cfg = {
@@ -1282,6 +1316,9 @@ static const struct phy_cfg rtd1315e_phy_cfg = {
.driving_updated_for_dev_dis = 0x8,
.use_default_parameter = false,
.is_double_sensitivity_mode = true,
+ .vstatus_offset = 0,
+ .vstatus_busy = PHY_VSTS_BUSY,
+ .new_reg_req = PHY_NEW_REG_REQ,
};
static const struct of_device_id usbphy_rtk_dt_match[] = {
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v3 0/7] Refactor reserved memory regions handling code
From: Rob Herring @ 2026-03-26 19:17 UTC (permalink / raw)
To: Marek Szyprowski
Cc: linux-kernel, devicetree, linux-mm, iommu, Krzysztof Kozlowski,
Oreoluwa Babatunde, Saravana Kannan, Andrew Morton, Robin Murphy
In-Reply-To: <fe9359e0-1c9a-4113-82c3-0275c932a58a@samsung.com>
On Thu, Mar 26, 2026 at 4:58 AM Marek Szyprowski
<m.szyprowski@samsung.com> wrote:
>
> Hi Rob
>
> On 25.03.2026 15:16, Rob Herring wrote:
> > On Wed, Mar 25, 2026 at 10:00:16AM +0100, Marek Szyprowski wrote:
> >> The reserved memory regions handling code was reworked to handle
> >> unlimited so called "static" memory nodes in commit 00c9a452a235 ("of:
> >> reserved_mem: Add code to dynamically allocate reserved_mem array").
> >>
> >> The side effect of this rework was a set of bugs fixed later by commits
> >> 0fd17e598333 ("of: reserved_mem: Allow reserved_mem framework detect
> >> "cma=" kernel param") and 2c223f7239f3 ("of: reserved_mem: Restructure
> >> call site for dma_contiguous_early_fixup()"). As a result, the code in
> >> drivers/of/of_reserved_mem.c became a mix of generic code and CMA
> >> specific fixups.
> >>
> >> In this patchset I try to untangle this spaghetti and perform some code
> >> cleanup. I hope nothing breaks this time.
> >>
> >> Best regards
> >> Marek Szyprowski, PhD
> >> Samsung R&D Institute Poland
> >>
> >>
> >> Changelog:
> >>
> >> v3:
> >> - fixed more issues pointed by Sashiko in
> >> https://protect2.fireeye.com/v1/url?k=c0975fdb-a11c4aed-c096d494-74fe485cbff1-5d0a8d4ece172e7b&q=1&e=abd3a151-11c5-430e-a6ac-b04cc3b34ab7&u=https%3A%2F%2Fsashiko.dev%2F%23%2Fpatchset%2F20260323100901.4079171-1-m.szyprowski%40samsung.com
> >> (restored use of _OF_DECLARE macro, extended some comments and commit
> >> descriptions, the remaining items I consider not relevant)
> >>
> >> v2: https://lore.kernel.org/all/20260323100901.4079171-1-m.szyprowski@samsung.com/
> >> - added missing ops assignment removal in tegra210-emc-table and swiotlb
> >> drivers
> >> - fixed issues pointed by kernel test robot and Sashiko: removed typos,
> >> improved comments
> >> - fixed incorrect node passed to fdt_validate_reserved_mem_node() in
> >> fdt_scan_reserved_mem_reg_nodes()
> >>
> >> v1: https://lore.kernel.org/all/20260313150802.1121442-1-m.szyprowski@samsung.com/
> >> - initial version
> >>
> >>
> >> Patch summary:
> >>
> >> Marek Szyprowski (7):
> >> of: reserved_mem: remove fdt node from the structure
> >> of: reserved_mem: use -ENODEV instead of -ENOENT
> >> of: reserved_mem: switch to ops based OF_DECLARE()
> >> of: reserved_mem: replace CMA quirks by generic methods
> >> of: reserved_mem: rearrange code a bit
> >> of: reserved_mem: clarify fdt_scan_reserved_mem*() functions
> >> of: reserved_mem: rework fdt_init_reserved_mem_node()
> > I've applied the series, thanks!
>
> I forgot to mention this earlier. There are other pending changes
> tokernel/dma/contiguous.c like
> https://lore.kernel.org/all/20260303-dma-buf-heaps-as-modules-v3-0-24344812c707@kernel.org/
> which I would like to merge to -next. This conflicts with this patchset.
> Could You provide a stable branch with those changes to let me resolve
> conflicts in kernel/dma/contiguous.c on top of it?
Here you go:
The following changes since commit 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f:
Linux 7.0-rc1 (2026-02-22 13:18:59 -0800)
are available in the Git repository at:
ssh://git@gitolite.kernel.org/pub/scm/linux/kernel/git/robh/linux.git
dt-reserved-mem-cleanups
for you to fetch changes up to 34e0e2a8ea9e9e4f4dceb33072103dffaa1366b3:
of: reserved_mem: rework fdt_init_reserved_mem_node() (2026-03-26
14:12:02 -0500)
----------------------------------------------------------------
Marek Szyprowski (7):
of: reserved_mem: remove fdt node from the structure
of: reserved_mem: use -ENODEV instead of -ENOENT
of: reserved_mem: switch to ops based OF_DECLARE()
of: reserved_mem: replace CMA quirks by generic methods
of: reserved_mem: rearrange code a bit
of: reserved_mem: clarify fdt_scan_reserved_mem*() functions
of: reserved_mem: rework fdt_init_reserved_mem_node()
drivers/memory/tegra/tegra210-emc-table.c | 19 +++++-----
drivers/of/fdt.c | 2 +-
drivers/of/of_private.h | 2 +-
drivers/of/of_reserved_mem.c | 320
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------------------------------------------------
include/linux/cma.h | 10 ------
include/linux/dma-map-ops.h | 3 --
include/linux/of_reserved_mem.h | 16 +++++----
kernel/dma/coherent.c | 19 +++++-----
kernel/dma/contiguous.c | 86
+++++++++++++++++++++++++++++++--------------
kernel/dma/swiotlb.c | 19 +++++-----
10 files changed, 285 insertions(+), 211 deletions(-)
^ permalink raw reply
* Re: [PATCH 2/2] iio: adc: qcom-pm8xxx-xoadc: add support for reading channel labels
From: Andy Shevchenko @ 2026-03-26 19:04 UTC (permalink / raw)
To: Antony Kurniawan Soemardi
Cc: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, linux-arm-msm, devicetree, linux-kernel,
linux-iio, phone-devel
In-Reply-To: <0b85a229-219f-4457-8fe4-bd4b3545684f@smankusors.com>
On Thu, Mar 26, 2026 at 12:00:52PM +0000, Antony Kurniawan Soemardi wrote:
> On 3/26/2026 5:18 PM, Andy Shevchenko wrote:
...
> >> Tested-on: Sony Xperia SP (PM8921)
> >
> > Interesting, never saw this tag before.
>
> Oh, I just realized I misremember Tested-by tag as Tested-on... Let me
> know if it's not acceptable.
You can just put it in a free text:
"The change has been tested on ..."
...
> >> + if (!ch) {
> >> + dev_err(adc->dev, "no such channel %lu\n", chan->address);
> >> + return -EINVAL;
> >> + }
> >
> > Isn't it a dead code? Also poisoning dmesg with this recurrent message is
> > not good idea to begin with (the user space will have a door to flood it,
> > which might be considered as an assistance to hackers to clear immediate
> > logs after a successful attack).
>
> Good point about the successful attack hint! I was copying the existing
> code from pm8xxx_read_raw. Do you think those checks are unnecessary for
> pm8xxx_read_raw as well?
Yes, I think they are not as the returned code should be enough to identify
the problem. (For no such channel I would rather see -ENOENT, but we can't
simply replace that in the existing code as it's part of ABI.)
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH v4 3/7] pinctrl: extract pinctrl_generic_to_map() from pinctrl_generic_pins_function_dt_node_to_map()
From: Conor Dooley @ 2026-03-26 18:55 UTC (permalink / raw)
To: Frank Li
Cc: Peter Rosin, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Rafał Miłecki, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, linux-kernel, linux-gpio,
devicetree, imx, linux-arm-kernel, Haibo Chen
In-Reply-To: <20260326-concur-eel-3e0b3d91e00a@spud>
[-- Attachment #1: Type: text/plain, Size: 4792 bytes --]
On Thu, Mar 26, 2026 at 06:52:12PM +0000, Conor Dooley wrote:
> On Wed, Mar 25, 2026 at 07:04:12PM -0400, Frank Li wrote:
>
> > diff --git a/drivers/pinctrl/pinctrl-generic.c b/drivers/pinctrl/pinctrl-generic.c
> > index efb39c6a670331775855efdc8566102b5c6202ef..20a216ae63e91b69985ea4cfcd0b57103c6ca950 100644
> > --- a/drivers/pinctrl/pinctrl-generic.c
> > +++ b/drivers/pinctrl/pinctrl-generic.c
> > @@ -17,29 +17,18 @@
> > #include "pinctrl-utils.h"
> > #include "pinmux.h"
> >
> > -static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *pctldev,
>
> > +int
> > +pinctrl_generic_to_map(struct pinctrl_dev *pctldev, struct device_node *parent,
>
> Can you drop this stylistic change please? The
Whoops, cut myself off. To be clear, what I am asking for is to keep the
"int" etc on the same line as the function name. This function is new,
but you did it for the existing function too and the comparison is here.
>
> > + struct device_node *np, struct pinctrl_map **maps,
> > + unsigned int *num_maps, unsigned int *num_reserved_maps,
> > + const char **group_names, unsigned int ngroups,
> > + const char **functions, unsigned int *pins)
> > {
> > struct device *dev = pctldev->dev;
> > - const char **functions;
> > + int npins, ret, reserve = 1;
> > + unsigned int num_configs;
> > const char *group_name;
> > unsigned long *configs;
> > - unsigned int num_configs, pin, *pins;
> > - int npins, ret, reserve = 1;
> > -
> > - npins = of_property_count_u32_elems(np, "pins");
> > -
> > - if (npins < 1) {
> > - dev_err(dev, "invalid pinctrl group %pOFn.%pOFn %d\n",
> > - parent, np, npins);
> > - return npins;
> > - }
> >
> > group_name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn.%pOFn", parent, np);
> > if (!group_name)
> > @@ -51,22 +40,6 @@ static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *p
> > if (!pins)
> > return -ENOMEM;
>
> This looks suspect. You've left the pins allocation behind:
> pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL);
> if (!pins)
> return -ENOMEM;
> but pinctrl_generic_pins_function_dt_subnode_to_map() has already
> populated this array before calling the function.
>
> Also, this should probably be
> Suggested-by: Conor Dooley <conor.dooley@microchip.com>
>
> Cheers,
> Conor.
>
> >
> > - functions = devm_kcalloc(dev, npins, sizeof(*functions), GFP_KERNEL);
> > - if (!functions)
> > - return -ENOMEM;
> > -
> > - for (int i = 0; i < npins; i++) {
> > - ret = of_property_read_u32_index(np, "pins", i, &pin);
> > - if (ret)
> > - return ret;
> > -
> > - pins[i] = pin;
> > -
> > - ret = of_property_read_string(np, "function", &functions[i]);
> > - if (ret)
> > - return ret;
> > - }
> > -
> > ret = pinctrl_utils_reserve_map(pctldev, maps, num_reserved_maps, num_maps, reserve);
> > if (ret)
> > return ret;
> > @@ -103,6 +76,54 @@ static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *p
> > return 0;
> > };
> >
> > +static int
> > +pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *pctldev,
> > + struct device_node *parent,
> > + struct device_node *np,
> > + struct pinctrl_map **maps,
> > + unsigned int *num_maps,
> > + unsigned int *num_reserved_maps,
> > + const char **group_names,
> > + unsigned int ngroups)
> > +{
> > + struct device *dev = pctldev->dev;
> > + unsigned int pin, *pins;
> > + const char **functions;
> > + int npins, ret;
> > +
> > + npins = of_property_count_u32_elems(np, "pins");
> > +
> > + if (npins < 1) {
> > + dev_err(dev, "invalid pinctrl group %pOFn.%pOFn %d\n",
> > + parent, np, npins);
> > + return npins;
> > + }
> > +
> > + pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL);
> > + if (!pins)
> > + return -ENOMEM;
> > +
> > + functions = devm_kcalloc(dev, npins, sizeof(*functions), GFP_KERNEL);
> > + if (!functions)
> > + return -ENOMEM;
> > +
> > + for (int i = 0; i < npins; i++) {
> > + ret = of_property_read_u32_index(np, "pins", i, &pin);
> > + if (ret)
> > + return ret;
> > +
> > + pins[i] = pin;
> > +
> > + ret = of_property_read_string(np, "function", &functions[i]);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + return pinctrl_generic_to_map(pctldev, parent, np, maps, num_maps,
> > + num_reserved_maps, group_names, ngroups,
> > + functions, pins);
> > +}
> > +
> > /*
> > * For platforms that do not define groups or functions in the driver, but
> > * instead use the devicetree to describe them. This function will, unlike
> >
> > --
> > 2.43.0
> >
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v4 3/7] pinctrl: extract pinctrl_generic_to_map() from pinctrl_generic_pins_function_dt_node_to_map()
From: Conor Dooley @ 2026-03-26 18:52 UTC (permalink / raw)
To: Frank Li
Cc: Peter Rosin, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Rafał Miłecki, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, linux-kernel, linux-gpio,
devicetree, imx, linux-arm-kernel, Haibo Chen
In-Reply-To: <20260325-pinctrl-mux-v4-3-043c2c82e623@nxp.com>
[-- Attachment #1: Type: text/plain, Size: 4227 bytes --]
On Wed, Mar 25, 2026 at 07:04:12PM -0400, Frank Li wrote:
> diff --git a/drivers/pinctrl/pinctrl-generic.c b/drivers/pinctrl/pinctrl-generic.c
> index efb39c6a670331775855efdc8566102b5c6202ef..20a216ae63e91b69985ea4cfcd0b57103c6ca950 100644
> --- a/drivers/pinctrl/pinctrl-generic.c
> +++ b/drivers/pinctrl/pinctrl-generic.c
> @@ -17,29 +17,18 @@
> #include "pinctrl-utils.h"
> #include "pinmux.h"
>
> -static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *pctldev,
> +int
> +pinctrl_generic_to_map(struct pinctrl_dev *pctldev, struct device_node *parent,
Can you drop this stylistic change please? The
> + struct device_node *np, struct pinctrl_map **maps,
> + unsigned int *num_maps, unsigned int *num_reserved_maps,
> + const char **group_names, unsigned int ngroups,
> + const char **functions, unsigned int *pins)
> {
> struct device *dev = pctldev->dev;
> - const char **functions;
> + int npins, ret, reserve = 1;
> + unsigned int num_configs;
> const char *group_name;
> unsigned long *configs;
> - unsigned int num_configs, pin, *pins;
> - int npins, ret, reserve = 1;
> -
> - npins = of_property_count_u32_elems(np, "pins");
> -
> - if (npins < 1) {
> - dev_err(dev, "invalid pinctrl group %pOFn.%pOFn %d\n",
> - parent, np, npins);
> - return npins;
> - }
>
> group_name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn.%pOFn", parent, np);
> if (!group_name)
> @@ -51,22 +40,6 @@ static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *p
> if (!pins)
> return -ENOMEM;
This looks suspect. You've left the pins allocation behind:
pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL);
if (!pins)
return -ENOMEM;
but pinctrl_generic_pins_function_dt_subnode_to_map() has already
populated this array before calling the function.
Also, this should probably be
Suggested-by: Conor Dooley <conor.dooley@microchip.com>
Cheers,
Conor.
>
> - functions = devm_kcalloc(dev, npins, sizeof(*functions), GFP_KERNEL);
> - if (!functions)
> - return -ENOMEM;
> -
> - for (int i = 0; i < npins; i++) {
> - ret = of_property_read_u32_index(np, "pins", i, &pin);
> - if (ret)
> - return ret;
> -
> - pins[i] = pin;
> -
> - ret = of_property_read_string(np, "function", &functions[i]);
> - if (ret)
> - return ret;
> - }
> -
> ret = pinctrl_utils_reserve_map(pctldev, maps, num_reserved_maps, num_maps, reserve);
> if (ret)
> return ret;
> @@ -103,6 +76,54 @@ static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *p
> return 0;
> };
>
> +static int
> +pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_dev *pctldev,
> + struct device_node *parent,
> + struct device_node *np,
> + struct pinctrl_map **maps,
> + unsigned int *num_maps,
> + unsigned int *num_reserved_maps,
> + const char **group_names,
> + unsigned int ngroups)
> +{
> + struct device *dev = pctldev->dev;
> + unsigned int pin, *pins;
> + const char **functions;
> + int npins, ret;
> +
> + npins = of_property_count_u32_elems(np, "pins");
> +
> + if (npins < 1) {
> + dev_err(dev, "invalid pinctrl group %pOFn.%pOFn %d\n",
> + parent, np, npins);
> + return npins;
> + }
> +
> + pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL);
> + if (!pins)
> + return -ENOMEM;
> +
> + functions = devm_kcalloc(dev, npins, sizeof(*functions), GFP_KERNEL);
> + if (!functions)
> + return -ENOMEM;
> +
> + for (int i = 0; i < npins; i++) {
> + ret = of_property_read_u32_index(np, "pins", i, &pin);
> + if (ret)
> + return ret;
> +
> + pins[i] = pin;
> +
> + ret = of_property_read_string(np, "function", &functions[i]);
> + if (ret)
> + return ret;
> + }
> +
> + return pinctrl_generic_to_map(pctldev, parent, np, maps, num_maps,
> + num_reserved_maps, group_names, ngroups,
> + functions, pins);
> +}
> +
> /*
> * For platforms that do not define groups or functions in the driver, but
> * instead use the devicetree to describe them. This function will, unlike
>
> --
> 2.43.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v2 1/1] dt-bindings: connector: Add role‑switch provider phandle
From: Elson Serrao @ 2026-03-26 18:50 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Heikki Krogerus,
Bjorn Andersson, Konrad Dybcio, Wesley Cheng, devicetree,
linux-kernel, linux-arm-msm
In-Reply-To: <CAO9ioeW7O+arR2VdAZboty_cAdtYm--ppMx9RT3nTQpJbTGccg@mail.gmail.com>
On 3/25/2026 6:57 PM, Dmitry Baryshkov wrote:
> On Thu, 26 Mar 2026 at 03:49, Elson Serrao
> <elson.serrao@oss.qualcomm.com> wrote:
>>
[...]
>>>
>>> Previously we didn't have such an issue for the usb-role-switch,
>>> because there always have been a direct link between the USB connector
>>> (be it gpio-usb-b-connector or usb-c-connector) and the USB controller
>>> (implementing usb-role-switch). As with the EUD this is no longer a
>>> case, my suggestion would be to follow prior art and let EUD receive,
>>> interpret and resend usb-role-switch events.
>>>
>>
>> In this topology, the EUD hardware spans more than one independent
>> role-switch relationship, as a single EUD node is the direct neighbor of
>> multiple connectors. This introduces additional considerations around
>> role-switch discovery.
>>
>> One practical consideration if the EUD registers multiple role-switch
>> instances is that fwnode_usb_role_switch_get() ( which relies on
>> class_find_device_by_fwnode API), assumes a unique firmware node per
>> role-switch instance. If multiple role-switch instances are registered
>> against the same firmware node (the EUD fwnode), the lookup will return
>> only the first registered instance, making it difficult for a connector to
>> reliably bind to its intended role-switch provider.
>>
>> Supporting multiple role-switch instances in this model would therefore
>> require extending the lookup mechanism to allow additional disambiguation
>> (for example, associating role-switch instances with connector context).
>>
>> I want to make sure I clearly understand the intended modeling and whether
>> these USB role-switch framework implications are considered acceptable.
>
> As far as I can see, you can register two usb-role-switches, one per
> the EUD path. then the connector will still be able to discover
> correct switch by following the chain from the connector. On the other
> hand, the EUD driver can use fwnode_usb_role_switch_get() passing the
> path's fwnode and find the next role-switch connected to the each of
> the EUD ports / paths.
>
My earlier questions were primarily around a flattened ports representation.
I agree that modeling each EUD path as a distinct child node with its own
firmware node addresses those concerns cleanly.
For context, the existing EUD binding [1] models a single controller ↔
connector relationship using a flat top-level ports block. An earlier
attempt [2] to reinterpret that top-level structure to represent
multiple paths ran into DT ABI concerns, as it changed the meaning of
existing bindings.
Based on your example, my understanding is that the intended direction is
to keep the existing top-level `ports` semantics unchanged for backward
compatibility, and model multi-path hardware using explicit child nodes,
each representing one controller ↔ connector relationship and registering
a separate usb-role-switch instance.
Please let me know if this matches the intended direction.
For the purposes of the usb‑role‑switch discussion, the other feedback in
[2] around PHY handling is orthogonal and will be addressed in a follow‑up
revision.
Thanks,
Elson
[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/Documentation/devicetree/bindings/soc/qcom/qcom,eud.yaml?h=v7.0-rc5
[2] https://lore.kernel.org/all/20260126233830.2193816-2-elson.serrao@oss.qualcomm.com/
> Here I am assuming that EUD device structured in a way like:
>
> eud {
> compatible = "qcom,eud";
>
> path@0 {
> ports {
> port@0 {
> endpoint {
> remote-endpoint = <&usb_con_0_hs>;
> };
> };
> port@1 {
> endpoint {
> remote-endpoint = <&usb0_hs>;
> };
> };
> };
> };
>
> path@1 {
> ports {
> port@0 {
> endpoint {
> remote-endpoint = <&usb_con_1_hs>;
> };
> };
> port@1 {
> endpoint {
> remote-endpoint = <&usb1_hs>;
> };
> };
> };
> };
>
> };
>
^ permalink raw reply
* Re: [PATCH 5/7] arm64: dts: qcom: sm6125-xiaomi-ginkgo: Add IR transmitter
From: Biswapriyo Nath @ 2026-03-26 18:41 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Biswapriyo Nath, Bjorn Andersson, Konrad Dybcio, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Lee Jones, Pavel Machek,
Sean Young, Michael Turquette, Stephen Boyd, Martin Botka,
linux-arm-msm, devicetree, linux-kernel, linux-leds, linux-clk,
~postmarketos/upstreaming, phone-devel
In-Reply-To: <dd71e35d-5dd1-4341-bcdb-d082dba41d3a@oss.qualcomm.com>
On Thu, 26 Mar 2026 10:29:49 +0100 Konrad Dybcio <konrad.dybcio@oss.qualcomm.com> wrote:
> On 3/25/26 7:07 PM, Biswapriyo Nath wrote:
> > The IR transmitting LED is connected to SPI8 controller.
> >
> > Signed-off-by: Biswapriyo Nath <nathbappai@gmail.com>
> > ---
>
> [...]
>
> > +&spi8 {
> > + status = "okay";
> > +
> > + irled@1 {
> > + compatible = "ir-spi-led";
> > + reg = <1>;
> > +
> > + duty-cycle = /bits/ 8 <30>;
> > + spi-max-frequency = <1000000>;
>
> I see the binding allows a power-supply handle - do you need one?
>
> Konrad
>
I have tested the IR transmitter with my TV and set-top box both.
power-supply is not required and not mentioned in Android devicetree.
^ permalink raw reply
* Re: [PATCH v6 2/3] dt-bindings: arm: Add Samsung Galaxy Book4 Edge
From: Maxim Storetvedt @ 2026-03-26 18:40 UTC (permalink / raw)
To: Krzysztof Kozlowski, andersson, robh, krzk+dt, conor+dt
Cc: marcus, marijn.suijten, linux-arm-msm, devicetree, linux-kernel,
abel.vesa, abel.vesa, johan, konradybcio, kirill
In-Reply-To: <ad4880c3-bec8-43f0-a79a-52f3d09f3f10@kernel.org>
On 3/26/26 12:44, Krzysztof Kozlowski wrote:
> On 22/03/2026 17:03, Maxim Storetvedt wrote:
>> From: Marcus Glocker <marcus@nazgul.ch>
>>
>> Add the Samsung Galaxy Book4 Edge compatibility binding.
>>
>> Signed-off-by: Marcus Glocker <marcus@nazgul.ch>
>> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
>
> Incomplete DCO. Please read submitting patches about certification you
> have to make.
>
> Do not attach (thread) your patchsets to some other threads (unrelated
> or older versions). This buries them deep in the mailbox and might
> interfere with applying entire sets. See also:
> https://elixir.bootlin.com/linux/v6.16-rc2/source/Documentation/process/submitting-patches.rst#L830
>
> Please organize the patch documenting the compatible (DT bindings)
> before the patch using that compatible.
> See also:
> https://elixir.bootlin.com/linux/v6.14-rc6/source/Documentation/devicetree/bindings/submitting-patches.rst#L46
>
> Best regards,
> Krzysztof
Duly noted. There's already plenty of useful info/feedback added here,
so appreciate the responses despite the entangled thread. To be
continued in next (and hopefully correctly threaded) patch revision.
Cheers,
-Max
^ permalink raw reply
* [PATCH v2] dt-bindings: sound: Convert pcm3060 to DT schema
From: Padmashree S S @ 2026-03-26 18:37 UTC (permalink / raw)
To: k.marinushkin, lgirdwood, broonie
Cc: robh, conor+dt, krzk+dt, devicetree, linux-sound, linux-kernel,
Padmashree S S
Note:
* This patch is part of the GSoC2026 application process for device tree bindings conversions
* https://github.com/LinuxFoundationGSoC/ProjectIdeas/wiki/GSoC-2026-Device-Tree-Bindings
Signed-off-by: Padmashree S S <padmashreess2006@gmail.com>
---
.../devicetree/bindings/sound/pcm3060.txt | 23 ----------
.../devicetree/bindings/sound/pcm3060.yaml | 45 +++++++++++++++++++
2 files changed, 45 insertions(+), 23 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/sound/pcm3060.txt
create mode 100644 Documentation/devicetree/bindings/sound/pcm3060.yaml
diff --git a/Documentation/devicetree/bindings/sound/pcm3060.txt b/Documentation/devicetree/bindings/sound/pcm3060.txt
deleted file mode 100644
index 97de66932d44..000000000000
--- a/Documentation/devicetree/bindings/sound/pcm3060.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-PCM3060 audio CODEC
-
-This driver supports both I2C and SPI.
-
-Required properties:
-
-- compatible: "ti,pcm3060"
-
-- reg : the I2C address of the device for I2C, the chip select
- number for SPI.
-
-Optional properties:
-
-- ti,out-single-ended: "true" if output is single-ended;
- "false" or not specified if output is differential.
-
-Examples:
-
- pcm3060: pcm3060@46 {
- compatible = "ti,pcm3060";
- reg = <0x46>;
- ti,out-single-ended = "true";
- };
diff --git a/Documentation/devicetree/bindings/sound/pcm3060.yaml b/Documentation/devicetree/bindings/sound/pcm3060.yaml
new file mode 100644
index 000000000000..ceb6f044b196
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/pcm3060.yaml
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/pcm3060.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: PCM3060 audio CODEC
+
+maintainers:
+ - Kirill Marinushkin <k.marinushkin@gmail.com>
+
+properties:
+ compatible:
+ const: ti,pcm3060
+
+ reg:
+ maxItems: 1
+ description: |
+ The I2C address of the device
+ or SPI chip select number.
+
+ ti,out-single-ended:
+ type: boolean
+ description: |
+ If present, the output is single-ended.
+ If absent, the output is differential.
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pcm3060: audio-codec@46 {
+ compatible = "ti,pcm3060";
+ reg = <0x46>;
+ ti,out-single-ended;
+ };
+ };
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox