* [PATCH 3/3] arm64: dts: rockchip: Add devicetree for the Graperain G3568 v2
From: Coia Prant @ 2026-06-27 22:57 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner,
Dragan Simic, Jonas Karlman
Cc: devicetree, linux-arm-kernel, linux-rockchip, linux-kernel,
Coia Prant
In-Reply-To: <20260627225755.1710837-2-coiaprant@gmail.com>
The Graperain G3568 v2 is an RK3568-based development board, just like the RK3568-EVB.
It always uses soldered connections between the GR3568CV2 core board and the
RK3568BV2/GR3568BV2 I/O board.
The G3568 board has multiple hardware revisions, and we currently support v2 (I/O board).
Specification:
- SoC: RockChip RK3568 ARM64 (4 cores)
- eMMC: 16-128 GB
- RAM: 2-16 GB
- Power: DC 12V 2A
- Ethernet: 2x RTL8211F RGMII (10/100/1000 Mbps)
- Wireless radio: 802.11b/g/n/ac dual-band
- LED:
Power: AlwaysOn
User: GPIO
- Button:
ESC: SARADC/0 <1100k µV>
MENU: SARADC/0 <1400k µV>
VOL-: SARADC/0 <430k µV>
VOL+: SARADC/0 <50k µV>
Power/Reset: PMIC RK809
- CAN
CAN/1: 4-pin (PH 2.0)
- PWM
PWM/4: Backlight
PWM/5: Backlight
PWM/7: IR Receiver
- UART:
UART/2: Debug TTL - 1500000 8N1 (1.25mm)
UART/3: TTL (PH 2.0)
UART/4: TTL (PH 2.0)
UART/8: AP6356S Bluetooth
UART/9: TTL (PH 2.0)
- I2C:
I2C/0: PMIC RK809
I2C/1: Touchscreen
I2C/4: Camera
I2C/5: RTC@51 PCF8563
- I2S:
I2S/0: HDMI Sound
I2S/1: RK809 Audio Codec
I2S/3: AP6356S Bluetooth Sound
- SDMMC:
SDMMC/0: microSD (TF) slot
SDMMC/2: AP6356S SDIO WiFi card
- Camera: 1x CSI
- Video: HDMI / DSI0 (MIPI/LVDS) / DSI1 (MIPI/EDP)
- Audio: HDMI / MIC / Speaker / SPDIF / 3.5mm Headphones / AP6356S Bluetooth
- USB:
USB 2.0 HOST x2
USB 2.0 OTG x1 (shared with USB 3.0 OTG/HOST)
USB 3.0 HOST x1
USB 3.0 OTG/HOST x1
- SATA: 1x SATA 3.0 with Power/4-pin
- PCIe: 1x PCIe 3.0 x2 (x4 connecter)
Link:
- https://image.chukouplus.com/upload/C_153/product_file/20211022/6daddec9e400458816dd4c57ba807fc3.pdf
- https://blog.gov.cooking/archives/research-graperain-g3568-v2-and-flash.html
Signed-off-by: Coia Prant <coiaprant@gmail.com>
---
arch/arm64/boot/dts/rockchip/Makefile | 1 +
.../rockchip/rk3568-graperain-g3568-v2.dts | 894 ++++++++++++++++++
2 files changed, 895 insertions(+)
create mode 100644 arch/arm64/boot/dts/rockchip/rk3568-graperain-g3568-v2.dts
diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile
index 761d82b4f..6e9d049e8 100644
--- a/arch/arm64/boot/dts/rockchip/Makefile
+++ b/arch/arm64/boot/dts/rockchip/Makefile
@@ -144,6 +144,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-easepi-r1.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-evb1-v10.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-fastrhino-r66s.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-fastrhino-r68s.dtb
+dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-graperain-g3568-v2.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-hinlink-h66k.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-hinlink-h68k.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-lubancat-2.dtb
diff --git a/arch/arm64/boot/dts/rockchip/rk3568-graperain-g3568-v2.dts b/arch/arm64/boot/dts/rockchip/rk3568-graperain-g3568-v2.dts
new file mode 100644
index 000000000..221992d6c
--- /dev/null
+++ b/arch/arm64/boot/dts/rockchip/rk3568-graperain-g3568-v2.dts
@@ -0,0 +1,894 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+
+/dts-v1/;
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/leds/common.h>
+#include <dt-bindings/pinctrl/rockchip.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
+#include "rk3568.dtsi"
+
+/ {
+ model = "Graperain G3568 v2";
+ compatible = "graperain,g3568-v2", "rockchip,rk3568";
+
+ aliases {
+ ethernet0 = &gmac0;
+ ethernet1 = &gmac1;
+ mmc0 = &sdhci;
+ mmc1 = &sdmmc0;
+ mmc2 = &sdmmc2;
+ rtc0 = &rtc0;
+ };
+
+ chosen {
+ stdout-path = "serial2:1500000n8";
+ };
+
+ adc-keys {
+ compatible = "adc-keys";
+ io-channels = <&saradc 0>;
+ io-channel-names = "buttons";
+ keyup-threshold-microvolt = <1800000>;
+ poll-interval = <100>;
+
+ button-esc {
+ label = "esc";
+ linux,code = <KEY_ESC>;
+ press-threshold-microvolt = <1250000>;
+ };
+
+ button-menu {
+ label = "menu";
+ linux,code = <KEY_MENU>;
+ press-threshold-microvolt = <1600000>;
+ };
+
+ button-vol-down {
+ label = "volume down";
+ linux,code = <KEY_VOLUMEDOWN>;
+ press-threshold-microvolt = <600000>;
+ };
+
+ button-vol-up {
+ label = "volume up";
+ linux,code = <KEY_VOLUMEUP>;
+ press-threshold-microvolt = <100000>;
+ };
+ };
+
+ hdmi-con {
+ compatible = "hdmi-connector";
+ type = "a";
+
+ port {
+ hdmi_con_in: endpoint {
+ remote-endpoint = <&hdmi_out_con>;
+ };
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ led_work: led-0 {
+ gpios = <&gpio0 RK_PC0 GPIO_ACTIVE_HIGH>;
+ function = LED_FUNCTION_HEARTBEAT;
+ color = <LED_COLOR_ID_BLUE>;
+ linux,default-trigger = "heartbeat";
+ pinctrl-names = "default";
+ pinctrl-0 = <&led_work_en>;
+ };
+ };
+
+ rk809-sound {
+ compatible = "simple-audio-card";
+ simple-audio-card,format = "i2s";
+ simple-audio-card,name = "Analog RK809";
+ simple-audio-card,mclk-fs = <256>;
+
+ simple-audio-card,cpu {
+ sound-dai = <&i2s1_8ch>;
+ };
+ simple-audio-card,codec {
+ sound-dai = <&rk809>;
+ };
+ };
+
+ pdm_codec: pdm-codec {
+ compatible = "dmic-codec";
+ num-channels = <2>;
+ #sound-dai-cells = <0>;
+ };
+
+ pdm_sound: pdm-sound {
+ compatible = "simple-audio-card";
+ simple-audio-card,name = "microphone";
+
+ simple-audio-card,cpu {
+ sound-dai = <&pdm>;
+ };
+
+ simple-audio-card,codec {
+ sound-dai = <&pdm_codec>;
+ };
+ };
+
+ spdif_dit: spdif-dit {
+ compatible = "linux,spdif-dit";
+ #sound-dai-cells = <0>;
+ };
+
+ spdif_sound: spdif-sound {
+ compatible = "simple-audio-card";
+ simple-audio-card,name = "SPDIF";
+
+ simple-audio-card,cpu {
+ sound-dai = <&spdif>;
+ };
+ simple-audio-card,codec {
+ sound-dai = <&spdif_dit>;
+ };
+ };
+
+ sdio_pwrseq: sdio-pwrseq {
+ compatible = "mmc-pwrseq-simple";
+ clocks = <&rk809 1>;
+ clock-names = "ext_clock";
+ pinctrl-names = "default";
+ pinctrl-0 = <&wifi_enable>;
+ post-power-on-delay-ms = <100>;
+ power-off-delay-us = <300>;
+ reset-gpios = <&gpio3 RK_PD5 GPIO_ACTIVE_LOW>;
+ };
+
+ dc_12v: regulator-dc-12v {
+ compatible = "regulator-fixed";
+ regulator-name = "dc_12v";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <12000000>;
+ regulator-max-microvolt = <12000000>;
+ };
+
+ pcie30_avdd0v9: regulator-pcie30-avdd0v9 {
+ compatible = "regulator-fixed";
+ regulator-name = "pcie30_avdd0v9";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ vin-supply = <&vcc3v3_sys>;
+ };
+
+ pcie30_avdd1v8: regulator-pcie30-avdd1v8 {
+ compatible = "regulator-fixed";
+ regulator-name = "pcie30_avdd1v8";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ vin-supply = <&vcc3v3_sys>;
+ };
+
+ vcc3v3_sys: regulator-vcc3v3-sys {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc3v3_sys";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ vin-supply = <&dc_12v>;
+ };
+
+ vcc3v3_pcie: regulator-vcc3v3-pcie {
+ compatible = "regulator-fixed";
+ enable-active-high;
+ gpios = <&gpio3 RK_PC3 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&vcc3v3_pcie_en_pin>;
+ regulator-name = "vcc3v3_pcie";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ startup-delay-us = <5000>;
+ vin-supply = <&dc_12v>;
+ };
+
+ vcc5v0_sys: regulator-vcc5v0-sys {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc5v0_sys";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ vin-supply = <&dc_12v>;
+ };
+
+ vcc5v0_usb: regulator-vcc5v0-usb {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc5v0_usb";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ vin-supply = <&dc_12v>;
+ };
+
+ vcc5v0_usb_host: regulator-vcc5v0-usb-host {
+ compatible = "regulator-fixed";
+ enable-active-high;
+ gpio = <&gpio0 RK_PA6 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&vcc5v0_usb_host_en>;
+ regulator-name = "vcc5v0_usb_host";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ vin-supply = <&vcc5v0_usb>;
+ };
+
+ vcc5v0_usb_otg: regulator-vcc5v0-usb-otg {
+ compatible = "regulator-fixed";
+ enable-active-high;
+ gpio = <&gpio0 RK_PA5 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&vcc5v0_usb_otg_en>;
+ regulator-name = "vcc5v0_usb_otg";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ vin-supply = <&vcc5v0_usb>;
+ };
+};
+
+&can1 {
+ assigned-clocks = <&cru CLK_CAN1>;
+ assigned-clock-rates = <150000000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&can1m1_pins>;
+ status = "okay";
+};
+
+/* used for usb_host0_xhci */
+&combphy0 {
+ status = "okay";
+};
+
+/* used for usb_host1_xhci */
+&combphy1 {
+ status = "okay";
+};
+
+/* connected to sata2 */
+&combphy2 {
+ status = "okay";
+};
+
+&cpu0 {
+ cpu-supply = <&vdd_cpu>;
+};
+
+&cpu1 {
+ cpu-supply = <&vdd_cpu>;
+};
+
+&cpu2 {
+ cpu-supply = <&vdd_cpu>;
+};
+
+&cpu3 {
+ cpu-supply = <&vdd_cpu>;
+};
+
+&gmac0 {
+ assigned-clocks = <&cru SCLK_GMAC0_RX_TX>, <&cru SCLK_GMAC0>;
+ assigned-clock-parents = <&cru SCLK_GMAC0_RGMII_SPEED>;
+ assigned-clock-rates = <0>, <125000000>;
+ clock_in_out = "output";
+ pinctrl-names = "default";
+ pinctrl-0 = <&gmac0_miim
+ &gmac0_tx_bus2
+ &gmac0_rx_bus2
+ &gmac0_rgmii_clk
+ &gmac0_rgmii_bus>;
+ phy-handle = <&rgmii_phy0>;
+ phy-mode = "rgmii-id";
+ status = "okay";
+};
+
+&gmac1 {
+ assigned-clocks = <&cru SCLK_GMAC1_RX_TX>, <&cru SCLK_GMAC1>;
+ assigned-clock-parents = <&cru SCLK_GMAC1_RGMII_SPEED>;
+ assigned-clock-rates = <0>, <125000000>;
+ clock_in_out = "output";
+ pinctrl-names = "default";
+ pinctrl-0 = <&gmac1m1_miim
+ &gmac1m1_tx_bus2
+ &gmac1m1_rx_bus2
+ &gmac1m1_rgmii_clk
+ &gmac1m1_rgmii_bus>;
+ phy-handle = <&rgmii_phy1>;
+ phy-mode = "rgmii-id";
+ status = "okay";
+};
+
+&gpu {
+ mali-supply = <&vdd_gpu>;
+ status = "okay";
+};
+
+&hdmi {
+ avdd-0v9-supply = <&vdda0v9_image>;
+ avdd-1v8-supply = <&vcca1v8_image>;
+ status = "okay";
+};
+
+&hdmi_in {
+ hdmi_in_vp0: endpoint {
+ remote-endpoint = <&vp0_out_hdmi>;
+ };
+};
+
+&hdmi_out {
+ hdmi_out_con: endpoint {
+ remote-endpoint = <&hdmi_con_in>;
+ };
+};
+
+&hdmi_sound {
+ status = "okay";
+};
+
+&i2c0 {
+ status = "okay";
+
+ vdd_cpu: regulator@1c {
+ compatible = "tcs,tcs4525";
+ reg = <0x1c>;
+ fcs,suspend-voltage-selector = <1>;
+ regulator-name = "vdd_cpu";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1150000>;
+ regulator-ramp-delay = <2300>;
+ vin-supply = <&vcc5v0_sys>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ rk809: pmic@20 {
+ compatible = "rockchip,rk809";
+ reg = <0x20>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <RK_PA3 IRQ_TYPE_LEVEL_LOW>;
+ assigned-clocks = <&cru I2S1_MCLKOUT_TX>;
+ assigned-clock-parents = <&cru CLK_I2S1_8CH_TX>;
+ #clock-cells = <1>;
+ clock-names = "mclk";
+ clocks = <&cru I2S1_MCLKOUT_TX>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pmic_int>, <&i2s1m0_mclk>;
+ system-power-controller;
+ #sound-dai-cells = <0>;
+ vcc1-supply = <&vcc3v3_sys>;
+ vcc2-supply = <&vcc3v3_sys>;
+ vcc3-supply = <&vcc3v3_sys>;
+ vcc4-supply = <&vcc3v3_sys>;
+ vcc5-supply = <&vcc3v3_sys>;
+ vcc6-supply = <&vcc3v3_sys>;
+ vcc7-supply = <&vcc3v3_sys>;
+ vcc8-supply = <&vcc3v3_sys>;
+ vcc9-supply = <&vcc3v3_sys>;
+ wakeup-source;
+
+ regulators {
+ vdd_logic: DCDC_REG1 {
+ regulator-name = "vdd_logic";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-initial-mode = <0x2>;
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-ramp-delay = <6001>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdd_gpu: DCDC_REG2 {
+ regulator-name = "vdd_gpu";
+ regulator-always-on;
+ regulator-initial-mode = <0x2>;
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-ramp-delay = <6001>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcc_ddr: DCDC_REG3 {
+ regulator-name = "vcc_ddr";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-initial-mode = <0x2>;
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ };
+ };
+
+ vdd_npu: DCDC_REG4 {
+ regulator-name = "vdd_npu";
+ regulator-initial-mode = <0x2>;
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-ramp-delay = <6001>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcc_1v8: DCDC_REG5 {
+ regulator-name = "vcc_1v8";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdda0v9_image: LDO_REG1 {
+ regulator-name = "vdda0v9_image";
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdda_0v9: LDO_REG2 {
+ regulator-name = "vdda_0v9";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdda0v9_pmu: LDO_REG3 {
+ regulator-name = "vdda0v9_pmu";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <900000>;
+ };
+ };
+
+ vccio_acodec: LDO_REG4 {
+ regulator-name = "vccio_acodec";
+ regulator-always-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vccio_sd: LDO_REG5 {
+ regulator-name = "vccio_sd";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcc3v3_pmu: LDO_REG6 {
+ regulator-name = "vcc3v3_pmu";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <3300000>;
+ };
+ };
+
+ vcca_1v8: LDO_REG7 {
+ regulator-name = "vcca_1v8";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcca1v8_pmu: LDO_REG8 {
+ regulator-name = "vcca1v8_pmu";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <1800000>;
+ };
+ };
+
+ vcca1v8_image: LDO_REG9 {
+ regulator-name = "vcca1v8_image";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcc_3v3: SWITCH_REG1 {
+ regulator-name = "vcc_3v3";
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcc3v3_sd: SWITCH_REG2 {
+ regulator-name = "vcc3v3_sd";
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+ };
+
+ codec {
+ rockchip,mic-in-differential;
+ };
+ };
+};
+
+&i2c5 {
+ status = "okay";
+
+ rtc0: rtc@51 {
+ compatible = "nxp,pcf8563";
+ reg = <0x51>;
+ #clock-cells = <0>;
+ };
+};
+
+&i2s0_8ch {
+ status = "okay";
+};
+
+&i2s1_8ch {
+ pinctrl-0 = <&i2s1m0_sclktx &i2s1m0_lrcktx &i2s1m0_sdi0 &i2s1m0_sdo0>;
+ rockchip,trcm-sync-tx-only;
+ status = "okay";
+};
+
+/* used for AP6356S Bluetooth Sound */
+&i2s3_2ch {
+ status = "okay";
+};
+
+&mdio0 {
+ rgmii_phy0: ethernet-phy@1 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <1>;
+ reset-assert-us = <20000>;
+ reset-deassert-us = <100000>;
+ reset-gpios = <&gpio2 RK_PD3 GPIO_ACTIVE_LOW>;
+
+ leds {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ led@1 {
+ reg = <1>;
+ color = <LED_COLOR_ID_GREEN>;
+ function = LED_FUNCTION_LAN;
+ default-state = "keep";
+ };
+
+ led@2 {
+ reg = <2>;
+ color = <LED_COLOR_ID_AMBER>;
+ function = LED_FUNCTION_LAN;
+ default-state = "keep";
+ };
+ };
+ };
+};
+
+&mdio1 {
+ rgmii_phy1: ethernet-phy@1 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <1>;
+ reset-assert-us = <20000>;
+ reset-deassert-us = <100000>;
+ reset-gpios = <&gpio2 RK_PD1 GPIO_ACTIVE_LOW>;
+
+ leds {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ led@1 {
+ reg = <1>;
+ color = <LED_COLOR_ID_GREEN>;
+ function = LED_FUNCTION_LAN;
+ default-state = "keep";
+ };
+
+ led@2 {
+ reg = <2>;
+ color = <LED_COLOR_ID_AMBER>;
+ function = LED_FUNCTION_LAN;
+ default-state = "keep";
+ };
+ };
+ };
+};
+
+&pcie30phy {
+ status = "okay";
+};
+
+&pcie3x2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pcie_reset_pin>;
+ reset-gpios = <&gpio2 RK_PD6 GPIO_ACTIVE_HIGH>;
+ vpcie3v3-supply = <&vcc3v3_pcie>;
+ status = "okay";
+};
+
+&pdm {
+ status = "okay";
+};
+
+&pinctrl {
+ leds {
+ led_work_en: led_work_en {
+ rockchip,pins = <0 RK_PC0 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
+ pmic {
+ pmic_int: pmic_int {
+ rockchip,pins = <0 RK_PA3 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ sdio-pwrseq {
+ wifi_enable: wifi-enable {
+ rockchip,pins = <3 RK_PD5 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
+ usb {
+ vcc5v0_usb_host_en: vcc5v0_usb_host_en {
+ rockchip,pins = <0 RK_PA6 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ vcc5v0_usb_otg_en: vcc5v0_usb_otg_en {
+ rockchip,pins = <0 RK_PA5 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
+ pcie {
+ pcie_reset_pin: pcie-reset-pin {
+ rockchip,pins = <2 RK_PD6 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ vcc3v3_pcie_en_pin: vcc3v3-pcie-en-pin {
+ rockchip,pins = <3 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+};
+
+&pmu_io_domains {
+ pmuio1-supply = <&vcc3v3_pmu>;
+ pmuio2-supply = <&vcc3v3_pmu>;
+ vccio1-supply = <&vccio_acodec>;
+ vccio2-supply = <&vcc_1v8>;
+ vccio3-supply = <&vccio_sd>;
+ vccio4-supply = <&vcc_1v8>;
+ vccio5-supply = <&vcc_3v3>;
+ vccio6-supply = <&vcc_1v8>;
+ vccio7-supply = <&vcc_3v3>;
+ status = "okay";
+};
+
+&pwm4 {
+ status = "okay";
+};
+
+&pwm5 {
+ status = "okay";
+};
+
+/* Required remotectl for IR receiver */
+&pwm7 {
+ status = "disabled";
+};
+
+&saradc {
+ vref-supply = <&vcca_1v8>;
+ status = "okay";
+};
+
+&sata2 {
+ status = "okay";
+};
+
+/* used for eMMC */
+&sdhci {
+ bus-width = <8>;
+ max-frequency = <200000000>;
+ mmc-hs200-1_8v;
+ non-removable;
+ pinctrl-names = "default";
+ pinctrl-0 = <&emmc_bus8 &emmc_clk &emmc_cmd &emmc_datastrobe>;
+ status = "okay";
+};
+
+/* used for microSD (TF) Slot */
+&sdmmc0 {
+ bus-width = <4>;
+ cap-sd-highspeed;
+ cd-gpios = <&gpio0 RK_PA4 GPIO_ACTIVE_LOW>;
+ disable-wp;
+ pinctrl-names = "default";
+ pinctrl-0 = <&sdmmc0_bus4 &sdmmc0_clk &sdmmc0_cmd &sdmmc0_det>;
+ sd-uhs-sdr104;
+ vmmc-supply = <&vcc3v3_sd>;
+ vqmmc-supply = <&vccio_sd>;
+ status = "okay";
+};
+
+/* used for AP6356S WiFi */
+&sdmmc2 {
+ bus-width = <4>;
+ cap-sd-highspeed;
+ cap-sdio-irq;
+ keep-power-in-suspend;
+ mmc-pwrseq = <&sdio_pwrseq>;
+ non-removable;
+ pinctrl-names = "default";
+ pinctrl-0 = <&sdmmc2m0_bus4 &sdmmc2m0_cmd &sdmmc2m0_clk>;
+ sd-uhs-sdr104;
+ vmmc-supply = <&vcc3v3_sys>;
+ vqmmc-supply = <&vcc_1v8>;
+ status = "okay";
+};
+
+&spdif {
+ status = "okay";
+};
+
+&tsadc {
+ rockchip,hw-tshut-mode = <1>;
+ rockchip,hw-tshut-polarity = <0>;
+ status = "okay";
+};
+
+/* used for Debug */
+&uart2 {
+ status = "okay";
+};
+
+&uart3 {
+ pinctrl-0 = <&uart3m1_xfer>;
+ status = "okay";
+};
+
+&uart4 {
+ pinctrl-0 = <&uart4m1_xfer>;
+ status = "okay";
+};
+
+/* used for WiFi/BT AP6356S */
+&uart8 {
+ pinctrl-0 = <&uart8m0_xfer &uart8m0_ctsn>;
+ status = "okay";
+};
+
+&uart9 {
+ pinctrl-0 = <&uart9m1_xfer>;
+ status = "okay";
+};
+
+&usb_host0_ehci {
+ status = "okay";
+};
+
+&usb_host0_ohci {
+ status = "okay";
+};
+
+&usb_host0_xhci {
+ extcon = <&usb2phy0>;
+ status = "okay";
+};
+
+&usb_host1_ehci {
+ status = "okay";
+};
+
+&usb_host1_ohci {
+ status = "okay";
+};
+
+&usb_host1_xhci {
+ status = "okay";
+};
+
+&usb2phy0 {
+ status = "okay";
+};
+
+&usb2phy0_host {
+ phy-supply = <&vcc5v0_usb_host>;
+ status = "okay";
+};
+
+&usb2phy0_otg {
+ phy-supply = <&vcc5v0_usb_otg>;
+ status = "okay";
+};
+
+&usb2phy1 {
+ status = "okay";
+};
+
+&usb2phy1_host {
+ phy-supply = <&vcc5v0_usb_host>;
+ status = "okay";
+};
+
+&usb2phy1_otg {
+ phy-supply = <&vcc5v0_usb_host>;
+ status = "okay";
+};
+
+&vop {
+ assigned-clocks = <&cru DCLK_VOP0>, <&cru DCLK_VOP1>;
+ assigned-clock-parents = <&pmucru PLL_HPLL>, <&cru PLL_VPLL>;
+ status = "okay";
+};
+
+&vop_mmu {
+ status = "okay";
+};
+
+&vp0 {
+ vp0_out_hdmi: endpoint@ROCKCHIP_VOP2_EP_HDMI0 {
+ reg = <ROCKCHIP_VOP2_EP_HDMI0>;
+ remote-endpoint = <&hdmi_in_vp0>;
+ };
+};
--
2.47.3
^ permalink raw reply related
* Re: [PATCH v3 2/2] iio: adc: add Axiado SARADC driver
From: David Lechner @ 2026-06-27 23:07 UTC (permalink / raw)
To: Petar Stepanovic, Akhila Kavi, Prasad Bolisetty, Jonathan Cameron,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Harshit Shah
Cc: linux-iio, devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <20260622-axiado-ax3000-ax3005-saradc-v3-2-e57c7c7ae675@axiado.com>
On 6/22/26 2:47 AM, Petar Stepanovic wrote:
> Add support for the SARADC controller found on Axiado AX3000 and
> AX3005 SoCs.
>
...
> +#define AX_SARADC_CH(_index, _id) \
> + { \
> + .type = IIO_VOLTAGE, \
> + .indexed = 1, \
> + .channel = (_index), \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
> + .datasheet_name = (_id), \
This could probably be:
.datasheet_name = "adc" #_index,
and avoid the need for _id.
> + }
> +
> +static const struct iio_chan_spec axiado_saradc_iio_channels[] = {
> + AX_SARADC_CH(0, "adc0"), AX_SARADC_CH(1, "adc1"),
> + AX_SARADC_CH(2, "adc2"), AX_SARADC_CH(3, "adc3"),
> + AX_SARADC_CH(4, "adc4"), AX_SARADC_CH(5, "adc5"),
> + AX_SARADC_CH(6, "adc6"), AX_SARADC_CH(7, "adc7"),
> + AX_SARADC_CH(8, "adc8"), AX_SARADC_CH(9, "adc9"),
> + AX_SARADC_CH(10, "adc10"), AX_SARADC_CH(11, "adc11"),
> + AX_SARADC_CH(12, "adc12"), AX_SARADC_CH(13, "adc13"),
> + AX_SARADC_CH(14, "adc14"), AX_SARADC_CH(15, "adc15"),
Two columns looks a bit odd.
> +};
> +
> +static void axiado_saradc_disable(void *data)
> +{
> + struct axiado_saradc *info = data;
> +
> + writel(AX_SARADC_GLOBAL_CTRL_PD, info->regs + AX_SARADC_GLOBAL_CTRL_REG);
People usual make read and write wrappers or use regmap to avoid having
to write `info->regs + AX_SARADC_GLOBAL_CTRL_REG` so many times.
> +}
^ permalink raw reply
* Re: [PATCH 3/3] arm64: dts: rockchip: Add devicetree for the Graperain G3568 v2
From: sashiko-bot @ 2026-06-27 23:08 UTC (permalink / raw)
To: Coia Prant; +Cc: robh, conor+dt, devicetree
In-Reply-To: <20260627225755.1710837-6-coiaprant@gmail.com>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Medium] Incorrect `press-threshold-microvolt` values in `adc-keys` use boundary midpoints instead of nominal voltages, causing the MENU button to misregister as ESC.
- [Medium] The WiFi SDIO interface specifies `keep-power-in-suspend`, but its I/O voltage supply (`vcc_1v8`) is configured to turn off in suspend, breaking Wake-on-WLAN.
- [Medium] The AP6356S Bluetooth UART is missing hardware flow control properties and the required Bluetooth child node.
- [Low] The deprecated `gpio` property is used instead of `gpios` in fixed regulator nodes.
--
commit 13181564cb78799e1d57eb0995588d8e839f22a5
Author: Coia Prant <coiaprant@gmail.com>
arm64: dts: rockchip: Add devicetree for the Graperain G3568 v2
This patch introduces the device tree for the Graperain G3568 v2, an RK3568
based development board. It configures the hardware peripherals including
ethernet, storage, wireless, and power regulators.
Link: https://image.chukouplus.com/upload/C_153/product_file/20211022/6daddec9e400458816dd4c57ba807fc3.pdf
Link: https://blog.gov.cooking/archives/research-graperain-g3568-v2-and-flash.html
> diff --git a/arch/arm64/boot/dts/rockchip/rk3568-graperain-g3568-v2.dts b/arch/arm64/boot/dts/rockchip/rk3568-graperain-g3568-v2.dts
> new file mode 100644
> index 0000000000000..221992d6c6003
> --- /dev/null
> +++ b/arch/arm64/boot/dts/rockchip/rk3568-graperain-g3568-v2.dts
> @@ -0,0 +1,894 @@
[ ... ]
> + adc-keys {
> + compatible = "adc-keys";
> + io-channels = <&saradc 0>;
> + io-channel-names = "buttons";
> + keyup-threshold-microvolt = <1800000>;
> + poll-interval = <100>;
> +
> + button-esc {
> + label = "esc";
> + linux,code = <KEY_ESC>;
> + press-threshold-microvolt = <1250000>;
> + };
> +
> + button-menu {
> + label = "menu";
> + linux,code = <KEY_MENU>;
> + press-threshold-microvolt = <1600000>;
[Severity: Medium]
Will this cause the menu button to misregister as esc?
The adc-keys driver uses a nearest-neighbor algorithm to match the ADC
reading to the button. Since the commit message indicates the nominal
voltages are 1.1V for ESC and 1.4V for MENU, an ADC reading of 1.4V from
pressing MENU is closer to the ESC threshold of 1.25V (0.15V difference)
than the MENU threshold of 1.6V (0.2V difference).
Could we use the exact expected nominal voltages here instead of the
boundary midpoints?
[ ... ]
> + vcc5v0_usb_host: regulator-vcc5v0-usb-host {
> + compatible = "regulator-fixed";
> + enable-active-high;
> + gpio = <&gpio0 RK_PA6 GPIO_ACTIVE_HIGH>;
[Severity: Low]
Is this using the deprecated gpio property?
The standard regulator-fixed binding expects gpios instead of gpio, which
can cause device tree schema validation warnings.
> + pinctrl-names = "default";
> + pinctrl-0 = <&vcc5v0_usb_host_en>;
> + regulator-name = "vcc5v0_usb_host";
> + regulator-min-microvolt = <5000000>;
> + regulator-max-microvolt = <5000000>;
> + vin-supply = <&vcc5v0_usb>;
> + };
> +
> + vcc5v0_usb_otg: regulator-vcc5v0-usb-otg {
> + compatible = "regulator-fixed";
> + enable-active-high;
> + gpio = <&gpio0 RK_PA5 GPIO_ACTIVE_HIGH>;
[Severity: Low]
Would it be better to use the gpios property here as well to match the
regulator-fixed device tree binding schema?
[ ... ]
> +/* used for AP6356S WiFi */
> +&sdmmc2 {
> + bus-width = <4>;
> + cap-sd-highspeed;
> + cap-sdio-irq;
> + keep-power-in-suspend;
> + mmc-pwrseq = <&sdio_pwrseq>;
> + non-removable;
> + pinctrl-names = "default";
> + pinctrl-0 = <&sdmmc2m0_bus4 &sdmmc2m0_cmd &sdmmc2m0_clk>;
> + sd-uhs-sdr104;
> + vmmc-supply = <&vcc3v3_sys>;
> + vqmmc-supply = <&vcc_1v8>;
[Severity: Medium]
Will this configuration break Wake-on-WLAN and SDIO communication on resume?
This node specifies keep-power-in-suspend, but the vqmmc-supply is set to
vcc_1v8. Looking at the vcc_1v8 regulator definition (DCDC_REG5) earlier in
the file, it is configured with regulator-off-in-suspend.
Cutting power to the SDIO I/O domain during suspend violates the power
sequencing for keep-power-in-suspend and prevents out-of-band wake interrupts
from signaling the host.
[ ... ]
> +/* used for WiFi/BT AP6356S */
> +&uart8 {
> + pinctrl-0 = <&uart8m0_xfer &uart8m0_ctsn>;
> + status = "okay";
> +};
[Severity: Medium]
Does this Bluetooth UART configuration need hardware flow control and a
device child node?
High-speed Bluetooth modules like the AP6356S generally rely on RTS/CTS to
prevent buffer overflows, but the uart-has-rtscts property and the
uart8m0_rtsn pin configuration are missing here.
Additionally, without a bluetooth child node (such as compatible =
"brcm,bcm4345c5"), the kernel's HCI UART driver might not be able to probe
and initialize the module automatically.
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260627225755.1710837-2-coiaprant@gmail.com?part=3
^ permalink raw reply
* [PATCH] arm64: dts: rockchip: add Firefly AIO-3588L board
From: zhao xinchao @ 2026-06-28 0:52 UTC (permalink / raw)
To: heiko@sntech.de
Cc: linux-rockchip@lists.infradead.org, devicetree@vger.kernel.org
From: Chris Xin <chrisxinchao@outlook.com>
Date: Sun, 28 Jun 2026 00:00:00 +0800
Subject: [PATCH] arm64: dts: rockchip: add Firefly AIO-3588L board
Add device tree for the Firefly AIO-3588L board based on the Rockchip
RK3588 SoC. The board features:
- Dual HDMI output via DesignWare HDMI QP
- RTL8852BE WiFi on PCIe (rtw89 driver)
- RTL8211F Ethernet
- Single RK806 PMIC
- LPDDR4 RAM, eMMC storage, USB 3.0
The board DTS includes the common firefly-core-3588j.dtsi which provides
PMIC, eMMC, and other shared configurations for Firefly RK3588 boards.
Signed-off-by: Chris Xin <chrisxinchao@outlook.com>
---
arch/arm64/boot/dts/rockchip/Makefile | 1 +
.../boot/dts/rockchip/rk3588-firefly-aio-3588l.dts | 718 +++++++++++++++
.../boot/dts/rockchip/rk3588-firefly-core-3588j.dtsi | 453 +++++++++
3 files changed, 1172 insertions(+)
--- a/arch/arm64/boot/dts/rockchip/Makefile 2026-06-28 08:08:00.071682235 +0800
+++ --- a/arch/arm64/boot/dts/rockchip/Makefile 2026-06-28 08:08:11.319090249 +0800
@@ -182,6 +182,7 @@
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-coolpi-cm5-evb.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-coolpi-cm5-genbook.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-edgeble-neu6a-io.dtb
+dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-firefly-aio-3588l.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-edgeble-neu6a-wifi.dtbo
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-edgeble-neu6b-io.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb1-v10.dtb
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-firefly-aio-3588l.dts b/arch/arm64/boot/dts/rockchip/rk3588-firefly-aio-3588l.dts
new file mode 100644
index 000000000000..000000000000
--- /dev/null
+++ b/arch/arm64/boot/dts/rockchip/rk3588-firefly-aio-3588l.dts
@@ -0,0 +1,718 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+
+/dts-v1/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/leds/common.h>
+#include <dt-bindings/pinctrl/rockchip.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
+#include <dt-bindings/usb/pd.h>
+#include "rk3588-firefly-core-3588j.dtsi"
+
+/ {
+ model = "Firefly AIO-3588L Board";
+ compatible = "firefly,aio-3588l", "rockchip,rk3588";
+
+ aliases {
+ ethernet0 = &gmac0;
+ mmc1 = &sdmmc;
+ };
+
+ chosen {
+ stdout-path = "serial2:1500000n8";
+ bootargs = "earlycon console=ttyS2,1500000n8 root=/dev/mmcblk0p6 rootfstype=ext4 rootwait rw loglevel=3 video=HDMI-A-1:1920x1080@60 video=HDMI-A-2:1920x1080@60 fbcon=map:0";
+ };
+
+ adc-keys {
+ compatible = "adc-keys";
+ io-channels = <&saradc 1>;
+ io-channel-names = "buttons";
+ keyup-threshold-microvolt = <1800000>;
+ poll-interval = <100>;
+
+ button-recovery {
+ label = "Recovery";
+ linux,code = <KEY_F12>;
+ press-threshold-microvolt = <17000>;
+ };
+ };
+
+ fan: pwm-fan {
+ compatible = "pwm-fan";
+ #cooling-cells = <2>;
+ pwms = <&pwm15 0 50000 0>;
+ cooling-levels = <0 50 100 150 200 255>;
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&led_pins>;
+
+ led-0 {
+ gpios = <&gpio3 RK_PB2 GPIO_ACTIVE_HIGH>;
+ label = ":power";
+ default-state = "on";
+ };
+
+ led-1 {
+ gpios = <&gpio2 RK_PC3 GPIO_ACTIVE_HIGH>;
+ label = ":user";
+ default-state = "off";
+ };
+ };
+
+ /* ---- fixed regulators ---- */
+
+ vcc12v_dcin: regulator-vcc12v-dcin {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc12v_dcin";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <12000000>;
+ regulator-max-microvolt = <12000000>;
+ };
+
+ vcc5v0_sys: regulator-vcc5v0-sys {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc5v0_sys";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ vin-supply = <&vcc12v_dcin>;
+ };
+
+ vcc5v0_usbdcin: regulator-vcc5v0-usbdcin {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc5v0_usbdcin";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ vin-supply = <&vcc12v_dcin>;
+ };
+
+ vcc5v0_usb: regulator-vcc5v0-usb {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc5v0_usb";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ vin-supply = <&vcc5v0_usbdcin>;
+ };
+
+ vcc_1v1_nldo_s3: regulator-vcc-1v1-nldo-s3 {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc_1v1_nldo_s3";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ vin-supply = <&vcc5v0_sys>;
+ };
+
+ vcc3v3_sys: regulator-vcc3v3-sys {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc3v3_sys";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ vin-supply = <&vcc12v_dcin>;
+ };
+
+ vcc_ext_12v_pwr: regulator-vcc-ext-12v-pwr {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc_ext_12v_pwr";
+ regulator-boot-on;
+ regulator-always-on;
+ enable-active-high;
+ gpio = <&gpio0 RK_PC4 GPIO_ACTIVE_HIGH>;
+ };
+
+ vcc3v3_sd: regulator-vcc3v3-sd {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc3v3_sd";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ enable-active-high;
+ gpio = <&gpio4 RK_PB0 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&vcc3v3_sd_en>;
+ };
+
+ vbus5v0_typec0: regulator-vbus5v0-typec0 {
+ compatible = "regulator-fixed";
+ regulator-name = "vbus5v0_typec0";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ gpio = <&gpio4 RK_PA3 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&vbus5v0_typec0_en>;
+ vin-supply = <&vcc5v0_sys>;
+ };
+
+ vcc5v0_usb30_host: regulator-vcc5v0-usb30-host {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc5v0_usb30_host";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ vin-supply = <&vcc5v0_sys>;
+ };
+
+ vcc3v3_pcie20_1l0: regulator-vcc3v3-pcie20-1l0 {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc3v3_pcie20_1l0";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ enable-active-high;
+ gpio = <&gpio0 RK_PB2 GPIO_ACTIVE_HIGH>;
+ startup-delay-us = <5000>;
+ vin-supply = <&vcc3v3_sys>;
+ };
+
+ vcc3v3_sata0: regulator-vcc3v3-sata0 {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc3v3_sata0";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ enable-active-high;
+ gpio = <&gpio4 RK_PC6 GPIO_ACTIVE_HIGH>;
+ startup-delay-us = <5000>;
+ vin-supply = <&vcc12v_dcin>;
+ };
+
+ vcc5v0_usb20_host: regulator-vcc5v0-usb20-host {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc5v0_usb20_host";
+ regulator-boot-on;
+ regulator-always-on;
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ gpio = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&vcc5v0_usb20_host_en>;
+ vin-supply = <&vcc5v0_usb>;
+ };
+
+ vcc5v0_usb20_host1: regulator-vcc5v0-usb20-host1 {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc5v0_usb20_host1";
+ regulator-boot-on;
+ regulator-always-on;
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ enable-active-high;
+ gpio = <&gpio3 RK_PA6 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&vcc5v0_usb20_host1_en>;
+ vin-supply = <&vcc5v0_usb>;
+ };
+
+ /* ---- HDMI connectors ---- */
+
+ hdmi0-con {
+ compatible = "hdmi-connector";
+ type = "a";
+
+ port {
+ hdmi0_con_in: endpoint {
+ remote-endpoint = <&hdmi0_out_con>;
+ };
+ };
+ };
+
+ hdmi1-con {
+ compatible = "hdmi-connector";
+ type = "a";
+
+ port {
+ hdmi1_con_in: endpoint {
+ remote-endpoint = <&hdmi1_out_con>;
+ };
+ };
+ };
+};
+
+/* ---- Ethernet ---- */
+
+&gmac0 {
+ status = "okay";
+ phy-mode = "rgmii-rxid";
+ clock_in_out = "output";
+ snps,reset-gpio = <&gpio3 RK_PC7 GPIO_ACTIVE_LOW>;
+ snps,reset-active-low;
+ snps,reset-delays-us = <0 20000 100000>;
+ tx_delay = <0x39>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&gmac0_miim
+ &gmac0_tx_bus2
+ &gmac0_rx_bus2
+ &gmac0_rgmii_clk
+ &gmac0_rgmii_bus>;
+ phy-handle = <&rgmii_phy0>;
+};
+
+&mdio0 {
+ rgmii_phy0: ethernet-phy@1 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <0x1>;
+ };
+};
+
+/* ---- HDMI display ---- */
+
+&hdmi0 {
+ status = "okay";
+};
+
+&hdmi0_in {
+ hdmi0_in_vp0: endpoint {
+ remote-endpoint = <&vp0_out_hdmi0>;
+ };
+};
+
+&hdmi0_out {
+ hdmi0_out_con: endpoint {
+ remote-endpoint = <&hdmi0_con_in>;
+ };
+};
+
+&hdmi1 {
+ status = "okay";
+};
+
+&hdmi1_in {
+ hdmi1_in_vp1: endpoint {
+ remote-endpoint = <&vp1_out_hdmi1>;
+ };
+};
+
+&hdmi1_out {
+ hdmi1_out_con: endpoint {
+ remote-endpoint = <&hdmi1_con_in>;
+ };
+};
+
+&hdptxphy0 {
+ status = "okay";
+};
+
+&hdptxphy1 {
+ status = "okay";
+};
+
+&vop {
+ status = "okay";
+};
+
+&vop_mmu {
+ status = "okay";
+};
+
+&vp0 {
+ vp0_out_hdmi0: endpoint@ROCKCHIP_VOP2_EP_HDMI0 {
+ reg = <ROCKCHIP_VOP2_EP_HDMI0>;
+ remote-endpoint = <&hdmi0_in_vp0>;
+ };
+};
+
+&vp1 {
+ vp1_out_hdmi1: endpoint@ROCKCHIP_VOP2_EP_HDMI1 {
+ reg = <ROCKCHIP_VOP2_EP_HDMI1>;
+ remote-endpoint = <&hdmi1_in_vp1>;
+ };
+};
+
+/* DP disabled */
+/*
+&vp2 {
+ vp2_out_dp0: endpoint@ROCKCHIP_VOP2_EP_DP0 {
+ reg = <ROCKCHIP_VOP2_EP_DP0>;
+ remote-endpoint = <&dp0_in_vp2>;
+ };
+};
+*/
+
+/* ---- DP (via Type-C) ---- */
+
+/* DP disabled - causes DRM crash on 7.0 */
+/*
+&dp0 {
+ status = "okay";
+};
+
+&dp0_in {
+ dp0_in_vp2: endpoint {
+ remote-endpoint = <&vp2_out_dp0>;
+ };
+};
+*/
+
+/* ---- SD card ---- */
+
+&sdmmc {
+ status = "okay";
+ max-frequency = <150000000>;
+ no-sdio;
+ no-mmc;
+ bus-width = <4>;
+ cap-mmc-highspeed;
+ cap-sd-highspeed;
+ disable-wp;
+ sd-uhs-sdr104;
+ vqmmc-supply = <&vccio_sd_s0>;
+};
+
+/* ---- PCIe WiFi ---- */
+
+&combphy1_ps {
+ status = "okay";
+};
+
+&pcie2x1l0 {
+ status = "okay";
+ reset-gpios = <&gpio4 RK_PA5 GPIO_ACTIVE_HIGH>;
+ vpcie3v3-supply = <&vcc3v3_pcie20_1l0>;
+};
+
+/* ---- SATA (M.2) ---- */
+
+&combphy0_ps {
+ status = "okay";
+};
+
+&sata0 {
+ status = "okay";
+ target-supply = <&vcc3v3_sata0>;
+};
+
+/* ---- USB ---- */
+
+&u2phy0 {
+ status = "okay";
+};
+
+&u2phy0_otg {
+ status = "okay";
+};
+
+&u2phy1 {
+ status = "okay";
+};
+
+&u2phy1_otg {
+ status = "okay";
+ phy-supply = <&vcc5v0_usb30_host>;
+};
+
+&u2phy2 {
+ status = "okay";
+};
+
+&u2phy2_host {
+ status = "okay";
+ phy-supply = <&vcc5v0_usb20_host>;
+};
+
+&u2phy3 {
+ status = "okay";
+};
+
+&u2phy3_host {
+ status = "okay";
+ phy-supply = <&vcc5v0_usb20_host1>;
+};
+
+&usb_host0_ehci {
+ status = "okay";
+};
+
+&usb_host0_ohci {
+ status = "okay";
+};
+
+&usb_host1_ehci {
+ status = "okay";
+};
+
+&usb_host1_ohci {
+ status = "okay";
+};
+
+&usb_host0_xhci {
+ dr_mode = "otg";
+ usb-role-switch;
+ status = "okay";
+
+ port {
+ dwc3_0_role_switch: endpoint {
+ remote-endpoint = <&usbc0_role_sw>;
+ };
+ };
+};
+
+&usb_host1_xhci {
+ dr_mode = "host";
+ status = "okay";
+};
+
+&usb_host2_xhci {
+ dr_mode = "host";
+ status = "okay";
+};
+
+&combphy2_psu {
+ status = "okay";
+};
+
+&usbdp_phy0 {
+ orientation-switch;
+ sbu1-dc-gpios = <&gpio4 RK_PB2 GPIO_ACTIVE_HIGH>;
+ sbu2-dc-gpios = <&gpio4 RK_PB3 GPIO_ACTIVE_HIGH>;
+ status = "okay";
+
+ port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ usbdp_phy0_orientation_switch: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&usbc0_orien_sw>;
+ };
+
+ usbdp_phy0_dp_altmode_mux: endpoint@1 {
+ reg = <1>;
+ remote-endpoint = <&dp_altmode_mux>;
+ };
+ };
+};
+
+&usbdp_phy1 {
+ status = "okay";
+};
+
+/* ---- Type-C FUSB302 + RTC on I2C6 ---- */
+
+&i2c6 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c6m0_xfer>;
+
+ usbc0: usb-typec@22 {
+ compatible = "fcs,fusb302";
+ reg = <0x22>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <RK_PD3 IRQ_TYPE_LEVEL_LOW>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&usbc0_int>;
+ vbus-supply = <&vbus5v0_typec0>;
+ status = "okay";
+
+ usb_con: connector {
+ compatible = "usb-c-connector";
+ label = "USB-C";
+ data-role = "dual";
+ power-role = "dual";
+ try-power-role = "sink";
+ op-sink-microwatt = <1000000>;
+ sink-pdos =
+ <PDO_FIXED(5000, 1000, PDO_FIXED_USB_COMM)>;
+ source-pdos =
+ <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM)>;
+
+ altmodes {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ altmode@0 {
+ reg = <0>;
+ svid = <0xff01>;
+ vdo = <0xffffffff>;
+ };
+ };
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ usbc0_orien_sw: endpoint {
+ remote-endpoint = <&usbdp_phy0_orientation_switch>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ usbc0_role_sw: endpoint {
+ remote-endpoint = <&dwc3_0_role_switch>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+ dp_altmode_mux: endpoint {
+ remote-endpoint = <&usbdp_phy0_dp_altmode_mux>;
+ };
+ };
+ };
+ };
+ };
+
+ hym8563: rtc@51 {
+ compatible = "haoyu,hym8563";
+ reg = <0x51>;
+ #clock-cells = <0>;
+ clock-frequency = <32768>;
+ clock-output-names = "hym8563";
+ pinctrl-names = "default";
+ pinctrl-0 = <&hym8563_int>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <RK_PB0 IRQ_TYPE_LEVEL_LOW>;
+ wakeup-source;
+ };
+};
+
+/* ---- Audio (ES8388) ---- */
+
+&i2c3 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c3m0_xfer>;
+
+ es8388: audio-codec@11 {
+ compatible = "everest,es8388", "everest,es8323";
+ reg = <0x11>;
+ clocks = <&cru I2S0_8CH_MCLKOUT>;
+ clock-names = "mclk";
+ assigned-clocks = <&cru I2S0_8CH_MCLKOUT>;
+ assigned-clock-rates = <12288000>;
+ #sound-dai-cells = <0>;
+ };
+};
+
+&i2s0_8ch {
+ status = "okay";
+ pinctrl-0 = <&i2s0_lrck
+ &i2s0_sclk
+ &i2s0_sdi0
+ &i2s0_sdo0>;
+};
+
+/* ---- UARTs ---- */
+
+&uart3 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart3m1_xfer>;
+};
+
+&uart6 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart6m1_xfer &uart6m1_ctsn &uart6m1_rtsn>;
+};
+
+&uart7 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart7m1_xfer>;
+};
+
+&uart8 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart8m1_xfer>;
+};
+
+/* ---- SPI1 ---- */
+
+&spi1 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi1m2_cs0 &spi1m2_pins>;
+ num-cs = <1>;
+ max-freq = <50000000>;
+
+ spidev: spi@0 {
+ compatible = "rohm,dh2228fv";
+ reg = <0>;
+ spi-max-frequency = <50000000>;
+ };
+};
+
+/* ---- PWM, ADC, Thermal ---- */
+
+&pwm15 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwm15m2_pins>;
+};
+
+&saradc {
+ vref-supply = <&vcc_1v8_s0>;
+ status = "okay";
+};
+
+&tsadc {
+ status = "okay";
+};
+
+/* ---- Pinmux ---- */
+
+&pinctrl {
+ leds {
+ led_pins: led-pins {
+ rockchip,pins = <3 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>,
+ <2 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
+ sd-card {
+ vcc3v3_sd_en: vcc3v3-sd-en {
+ rockchip,pins = <4 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
+ typec0 {
+ vbus5v0_typec0_en: vbus5v0-typec0-en {
+ rockchip,pins = <4 RK_PA3 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ usbc0_int: usbc0-int {
+ rockchip,pins = <0 RK_PD3 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ usb20-host0 {
+ vcc5v0_usb20_host_en: vcc5v0-usb20-host-en {
+ rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
+ usb20-host1 {
+ vcc5v0_usb20_host1_en: vcc5v0-usb20-host1-en {
+ rockchip,pins = <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
+ hym8563 {
+ hym8563_int: hym8563-int {
+ rockchip,pins = <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ pmic {
+ pmic_pins: pmic-pins {
+ rockchip,pins = <0 RK_PA7 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-firefly-core-3588j.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-firefly-core-3588j.dtsi
new file mode 100644
index 000000000000..000000000000
--- /dev/null
+++ b/arch/arm64/boot/dts/rockchip/rk3588-firefly-core-3588j.dtsi
@@ -0,0 +1,453 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/pinctrl/rockchip.h>
+
+#include "rk3588.dtsi"
+
+/ {
+ compatible = "firefly,core-3588j", "rockchip,rk3588";
+
+ aliases {
+ mmc0 = &sdhci;
+ };
+};
+
+&cpu_b0 {
+ cpu-supply = <&vdd_cpu_big0_s0>;
+};
+
+&cpu_b1 {
+ cpu-supply = <&vdd_cpu_big0_s0>;
+};
+
+&cpu_b2 {
+ cpu-supply = <&vdd_cpu_big1_s0>;
+};
+
+&cpu_b3 {
+ cpu-supply = <&vdd_cpu_big1_s0>;
+};
+
+&cpu_l0 {
+ cpu-supply = <&vdd_cpu_lit_s0>;
+};
+
+&cpu_l1 {
+ cpu-supply = <&vdd_cpu_lit_s0>;
+};
+
+&cpu_l2 {
+ cpu-supply = <&vdd_cpu_lit_s0>;
+};
+
+&cpu_l3 {
+ cpu-supply = <&vdd_cpu_lit_s0>;
+};
+
+&i2c0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c0m2_xfer>;
+ status = "okay";
+
+ vdd_cpu_big0_s0: regulator@42 {
+ compatible = "rockchip,rk8602";
+ reg = <0x42>;
+ fcs,suspend-voltage-selector = <1>;
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <550000>;
+ regulator-max-microvolt = <1050000>;
+ regulator-name = "vdd_cpu_big0_s0";
+ regulator-ramp-delay = <2300>;
+ vin-supply = <&vcc5v0_sys>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdd_cpu_big1_s0: regulator@43 {
+ compatible = "rockchip,rk8603", "rockchip,rk8602";
+ reg = <0x43>;
+ fcs,suspend-voltage-selector = <1>;
+ regulator-name = "vdd_cpu_big1_s0";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <550000>;
+ regulator-max-microvolt = <1050000>;
+ regulator-ramp-delay = <2300>;
+ vin-supply = <&vcc5v0_sys>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+};
+
+&i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1m2_xfer>;
+ status = "okay";
+
+ vdd_npu_s0: vdd_npu_mem_s0: regulator@42 {
+ compatible = "rockchip,rk8602";
+ reg = <0x42>;
+ fcs,suspend-voltage-selector = <1>;
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <550000>;
+ regulator-max-microvolt = <950000>;
+ regulator-name = "vdd_npu_s0";
+ regulator-ramp-delay = <2300>;
+ vin-supply = <&vcc5v0_sys>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+};
+
+&gpu {
+ mali-supply = <&vdd_gpu_s0>;
+ sram-supply = <&vdd_gpu_mem_s0>;
+ status = "okay";
+};
+
+&pd_gpu {
+ domain-supply = <&vdd_gpu_s0>;
+};
+
+&sdhci {
+ bus-width = <8>;
+ no-sdio;
+ no-sd;
+ non-removable;
+ mmc-hs400-1_8v;
+ mmc-hs400-enhanced-strobe;
+ status = "okay";
+};
+
+&spi2 {
+ assigned-clocks = <&cru CLK_SPI2>;
+ assigned-clock-rates = <200000000>;
+ num-cs = <1>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi2m2_cs0 &spi2m2_pins>;
+ status = "okay";
+
+ pmic@0 {
+ compatible = "rockchip,rk806";
+ reg = <0x0>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pmic_pins>, <&rk806_dvs1_null>,
+ <&rk806_dvs2_null>, <&rk806_dvs3_null>;
+ spi-max-frequency = <1000000>;
+ system-power-controller;
+
+ vcc1-supply = <&vcc5v0_sys>;
+ vcc2-supply = <&vcc5v0_sys>;
+ vcc3-supply = <&vcc5v0_sys>;
+ vcc4-supply = <&vcc5v0_sys>;
+ vcc5-supply = <&vcc5v0_sys>;
+ vcc6-supply = <&vcc5v0_sys>;
+ vcc7-supply = <&vcc5v0_sys>;
+ vcc8-supply = <&vcc5v0_sys>;
+ vcc9-supply = <&vcc5v0_sys>;
+ vcc10-supply = <&vcc5v0_sys>;
+ vcc11-supply = <&vcc_2v0_pldo_s3>;
+ vcc12-supply = <&vcc5v0_sys>;
+ vcc13-supply = <&vcc_1v1_nldo_s3>;
+ vcc14-supply = <&vcc_1v1_nldo_s3>;
+ vcca-supply = <&vcc5v0_sys>;
+
+ rk806_dvs1_null: dvs1-null-pins {
+ pins = "gpio_pwrctrl1";
+ function = "pin_fun0";
+ };
+
+ rk806_dvs2_null: dvs2-null-pins {
+ pins = "gpio_pwrctrl2";
+ function = "pin_fun0";
+ };
+
+ rk806_dvs3_null: dvs3-null-pins {
+ pins = "gpio_pwrctrl3";
+ function = "pin_fun0";
+ };
+
+ regulators {
+ vdd_gpu_s0: vdd_gpu_mem_s0: dcdc-reg1 {
+ regulator-boot-on;
+ regulator-min-microvolt = <550000>;
+ regulator-max-microvolt = <950000>;
+ regulator-ramp-delay = <12500>;
+ regulator-name = "vdd_gpu_s0";
+ regulator-enable-ramp-delay = <400>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdd_cpu_lit_s0: vdd_cpu_lit_mem_s0: dcdc-reg2 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <550000>;
+ regulator-max-microvolt = <950000>;
+ regulator-ramp-delay = <12500>;
+ regulator-name = "vdd_cpu_lit_s0";
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdd_log_s0: dcdc-reg3 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <675000>;
+ regulator-max-microvolt = <750000>;
+ regulator-ramp-delay = <12500>;
+ regulator-name = "vdd_log_s0";
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ regulator-suspend-microvolt = <750000>;
+ };
+ };
+
+ vdd_vdenc_s0: vdd_vdenc_mem_s0: dcdc-reg4 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <550000>;
+ regulator-max-microvolt = <950000>;
+ regulator-ramp-delay = <12500>;
+ regulator-name = "vdd_vdenc_s0";
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdd_ddr_s0: dcdc-reg5 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <675000>;
+ regulator-max-microvolt = <900000>;
+ regulator-ramp-delay = <12500>;
+ regulator-name = "vdd_ddr_s0";
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ regulator-suspend-microvolt = <850000>;
+ };
+ };
+
+ vdd2_ddr_s3: dcdc-reg6 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-name = "vdd2_ddr_s3";
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ };
+ };
+
+ vcc_2v0_pldo_s3: dcdc-reg7 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <2000000>;
+ regulator-max-microvolt = <2000000>;
+ regulator-name = "vdd_2v0_pldo_s3";
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <2000000>;
+ };
+ };
+
+ vcc_3v3_s3: dcdc-reg8 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc_3v3_s3";
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <3300000>;
+ };
+ };
+
+ vddq_ddr_s0: dcdc-reg9 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-name = "vddq_ddr_s0";
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcc_1v8_s3: dcdc-reg10 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc_1v8_s3";
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <1800000>;
+ };
+ };
+
+ avcc_1v8_s0: pldo-reg1 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "avcc_1v8_s0";
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcc_1v8_s0: pldo-reg2 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc_1v8_s0";
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ regulator-suspend-microvolt = <1800000>;
+ };
+ };
+
+ avdd_1v2_s0: pldo-reg3 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-name = "avdd_1v2_s0";
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ avcc_3v3_s0: pldo-reg4 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "avcc_3v3_s0";
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vccio_sd_s0: pldo-reg5 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vccio_sd_s0";
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ pldo6_s3: pldo-reg6 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "pldo6_s3";
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <1800000>;
+ };
+ };
+
+ vdd_0v75_s3: nldo-reg1 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <750000>;
+ regulator-name = "vdd_0v75_s3";
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <750000>;
+ };
+ };
+
+ avdd_ddr_pll_s0: nldo-reg2 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <850000>;
+ regulator-max-microvolt = <850000>;
+ regulator-name = "avdd_ddr_pll_s0";
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ regulator-suspend-microvolt = <850000>;
+ };
+ };
+
+ avdd_0v75_s0: nldo-reg3 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <750000>;
+ regulator-name = "avdd_0v75_s0";
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ avdd_0v85_s0: nldo-reg4 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <850000>;
+ regulator-max-microvolt = <850000>;
+ regulator-name = "avdd_0v85_s0";
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdd_0v75_s0: nldo-reg5 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <750000>;
+ regulator-name = "vdd_0v75_s0";
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+ };
+ };
+};
+
+/* rk3588 preferred debug out */
+&uart2 {
+ pinctrl-0 = <&uart2m0_xfer>;
+ status = "okay";
+};
---
2.34.1
^ permalink raw reply related
* Re: [PATCH v2 2/3] arm64: dts: qcom: kodiak: move dp data-lanes to SoC dtsi
From: Bjorn Andersson @ 2026-06-28 2:26 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Mahadevan P, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, cros-qcom-dts-watchers, linux-arm-msm, devicetree,
linux-kernel, Mahadevan P
In-Reply-To: <hej6absxu6nsuktv7tsegduyrduv7diq5zx7dt2a4xp3pe6gxl@b2xscorilbvn>
On Fri, Jun 26, 2026 at 11:50:40PM +0300, Dmitry Baryshkov wrote:
> On Wed, Apr 29, 2026 at 12:10:41PM +0530, Mahadevan P wrote:
> > From: Mahadevan P <mahap@qti.qualcomm.com>
> >
> > The connection between the QMP Combo PHY and the DisplayPort controller
> > is fixed in SoC, so move the data-lanes property to kodiak.dtsi and
> > drop the per-board overrides.
> >
> > Also remove the redundant remote-endpoint cross-links and
> > orientation-switch property from qcs6490-rb3gen2 and
> > qcs6490-thundercomm-rubikpi3, which are already defined in kodiak.dtsi.
>
> Separate commit.
>
> >
> > Signed-off-by: Mahadevan P <mahadevan.p@oss.qualcomm.com>
> > ---
> > arch/arm64/boot/dts/qcom/kodiak.dtsi | 1 +
> > arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts | 4 ----
> > arch/arm64/boot/dts/qcom/qcm6490-particle-tachyon.dts | 4 ----
> > arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts | 11 -----------
> > arch/arm64/boot/dts/qcom/qcs6490-thundercomm-minipc-g1iot.dts | 1 -
> > arch/arm64/boot/dts/qcom/qcs6490-thundercomm-rubikpi3.dts | 3 ---
> > arch/arm64/boot/dts/qcom/sc7280-herobrine.dtsi | 1 -
> > 7 files changed, 1 insertion(+), 24 deletions(-)
> >
> > diff --git a/arch/arm64/boot/dts/qcom/kodiak.dtsi b/arch/arm64/boot/dts/qcom/kodiak.dtsi
> > index 96ac3656ab5a..0acc6917d7aa 100644
> > --- a/arch/arm64/boot/dts/qcom/kodiak.dtsi
> > +++ b/arch/arm64/boot/dts/qcom/kodiak.dtsi
> > @@ -5704,6 +5704,7 @@ dp_in: endpoint {
> > port@1 {
> > reg = <1>;
> > mdss_dp_out: endpoint {
> > + data-lanes = <0 1>;
>
> This is not true. The SoC has 4 lanes going from the DP controller to
> the QMP PHY.
>
Does this property really denote the number of lanes and mapping the
internal pipe between DP TX and PHY? Doesn't it tell how the external
mapping looks like?
Regards,
Bjorn
> > remote-endpoint = <&usb_dp_qmpphy_dp_in>;
> > };
> > };
>
> --
> With best wishes
> Dmitry
^ permalink raw reply
* Re: [PATCH v2 3/3] arm64: dts: qcom: kodiak: Set up 4-lane DP
From: Bjorn Andersson @ 2026-06-28 2:28 UTC (permalink / raw)
To: Mahadevan P
Cc: Konrad Dybcio, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
cros-qcom-dts-watchers, linux-arm-msm, devicetree, linux-kernel,
Mahadevan P
In-Reply-To: <20260429-kodiak_v2-v2-3-c3a703cc30eb@oss.qualcomm.com>
On Wed, Apr 29, 2026 at 12:10:42PM +0530, Mahadevan P wrote:
> From: Mahadevan P <mahap@qti.qualcomm.com>
>
> Allow up to 4 lanes for the DisplayPort link from the PHY to the
It's hard to follow your thought process here, as you didn't document
why this change should be made. Start your commit message by describing
the problem that your change is solving.
> controller now the mode-switch events can reach the QMP Combo PHY.
>
> Signed-off-by: Mahadevan P <mahadevan.p@oss.qualcomm.com>
> ---
> arch/arm64/boot/dts/qcom/kodiak.dtsi | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/arm64/boot/dts/qcom/kodiak.dtsi b/arch/arm64/boot/dts/qcom/kodiak.dtsi
> index 0acc6917d7aa..204513a6bd89 100644
> --- a/arch/arm64/boot/dts/qcom/kodiak.dtsi
> +++ b/arch/arm64/boot/dts/qcom/kodiak.dtsi
> @@ -5704,7 +5704,7 @@ dp_in: endpoint {
> port@1 {
> reg = <1>;
> mdss_dp_out: endpoint {
> - data-lanes = <0 1>;
> + data-lanes = <0 1 2 3>;
And as Dmitry pointed out, not all Kodiak-based boards have 4 DP-lanes
wired up.
Regards,
Bjorn
> remote-endpoint = <&usb_dp_qmpphy_dp_in>;
> };
> };
>
> --
> 2.34.1
>
^ permalink raw reply
* Re: [PATCH 2/3] arm64: dts: qcom: glymur: Add Asus Zenbook A14 (UX3407NA)
From: Bjorn Andersson @ 2026-06-28 2:41 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Konrad Dybcio, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-arm-msm, devicetree, linux-kernel, Bjorn Andersson
In-Reply-To: <fcb6579a-4e5e-47ef-b9a5-009dd761e4de@oss.qualcomm.com>
On Tue, Jun 23, 2026 at 09:06:51AM +0200, Konrad Dybcio wrote:
> On 6/23/26 3:31 AM, Bjorn Andersson wrote:
> > From: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
> >
> > UX3407NA is a variant of the Asus Zenbook A14 built on the Qualcomm
> > Glymur platform. It comes with an 18-core X2 Elite SoC, 32GB DDR, and
> > the other typical Glymur platform capabilities.
> >
> > The Asus Zenbook uses &pcie3b for NVMe storage, the screen is WUXGA
> > OLED, it has two USB Type-C ports, one USB Type-A, and one HDMI port.
> >
> > Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
> > ---
>
> [...]
>
> > +/ {
> > + compatible = "asus,zenbook-a14-ux3407na", "qcom,glymur";
> > + model = "ASUS Zenbook A14 (UX3407NA)";
>
> unusual order!
>
While I don't see this case documented in dts-coding-style.rst, this
order seems better...
[..]
> > +&tlmm {
> > + gpio-reserved-ranges = <4 4>, /* EC Secure */
> > + <10 2>, /* OOB UART */
> > + <44 4>, /* TPM */
> > + <90 2>; /* TPM */
>
> Is there no EC reset pin? I wouldn't want others to have to open up
> a laptop after trying to find out what it does..
>
These are the ones needed to allow the thing to boot, I don't know what
the EC reset pin does, but I guess we can add some more for convenience.
[..]
> > +&uart21 {
> > + status = "disabled";
>
> hm?
>
The debug-uart is left enabled in glymur.dtsi. Perhaps we should change
that instead?
Regards,
Bjorn
> Konrad
^ permalink raw reply
* Re: [PATCH] arm64: dts: qcom: glymur: fix QUP serial engine IRQs
From: Bjorn Andersson @ 2026-06-28 2:44 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Pankaj Patil, Taniya Das,
Manaf Meethalavalappu Pallikunhi, Jyothi Kumar Seerapu,
Jishnu Prakash, Maulik Shah, Sibi Sankar, Kamal Wadhwa,
linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <hdq5iicr3sawlkhxmeut4ms4n2x7zocwva44lhnjqjfneqwtwe@3amlnfuuanie>
On Fri, Jun 12, 2026 at 02:04:05AM +0300, Dmitry Baryshkov wrote:
> On Thu, Jun 11, 2026 at 05:22:37PM +0000, Bjorn Andersson wrote:
> > The Geni serial-engine interrupts from QUP wrapper 0 all fall in ESPI
> > INTIDs space. While some of the i2c instances has gotten their
> > interrupt specifiers corrected, even the other functions on the same
> > serial-engines are wrong.
> >
> > Ensure that all the serial engine interrupts for QUP wrapper 0 matches
> > the datasheet.
> >
> > Assisted-by: Codex:GPT-5.5
> > Fixes: 41b6e8db400c ("arm64: dts: qcom: Introduce Glymur base dtsi")
> > Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
> > ---
> > arch/arm64/boot/dts/qcom/glymur.dtsi | 26 +++++++++++++-------------
> > 1 file changed, 13 insertions(+), 13 deletions(-)
>
> What about the SPI / I2C controllers which are a part of qupv3_1?
>
They are well inside the SPI range.
Regards,
Bjorn
>
> --
> With best wishes
> Dmitry
^ permalink raw reply
* Re: [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver
From: Bjorn Andersson @ 2026-06-28 3:07 UTC (permalink / raw)
To: george.moussalem
Cc: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Konrad Dybcio, Mathieu Poirier,
Philipp Zabel, linux-block, linux-kernel, linux-mmc, devicetree,
linux-wireless, ath10k, linux-arm-msm, linux-bluetooth, netdev,
linux-remoteproc
In-Reply-To: <20260625-ipq5018-bluetooth-v1-2-d999be0e04f7@outlook.com>
On Thu, Jun 25, 2026 at 06:10:06PM +0400, George Moussalem via B4 Relay wrote:
> From: George Moussalem <george.moussalem@outlook.com>
>
> Add support to bring up the M0 core of the bluetooth subsystem found in
> the IPQ5018 SoC.
>
> The signed firmware loaded is authenticated by TrustZone. If successful,
> the M0 core boots the firmware and the peripheral is taken out of reset
> using a Secure Channel Manager call to TrustZone.
>
The remoteproc framework deals with life cycle management of
coprocessors, but you don't want that - you want the BT driver to own
the life cycle.
Further, the fact that you split this in "BT" and "remoteproc", results
in you having two representations in DeviceTree and in the device model
for the same hardware.
I know we have examples of this in the kernel already, but they are all
racy...
Please see if you can embed the firmware loading, authentication and
PAS calls directly into the BT driver - to have a single entity managing
the life cycle of your M0 processor.
Regards,
Bjorn
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> ---
> drivers/remoteproc/Kconfig | 12 ++
> drivers/remoteproc/Makefile | 1 +
> drivers/remoteproc/qcom_m0_btss_pil.c | 261 ++++++++++++++++++++++++++++++++++
> 3 files changed, 274 insertions(+)
>
> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> index c521c744e7db..6b52f78f1427 100644
> --- a/drivers/remoteproc/Kconfig
> +++ b/drivers/remoteproc/Kconfig
> @@ -163,6 +163,18 @@ config PRU_REMOTEPROC
> processors on various TI SoCs. It's safe to say N here if you're
> not interested in the PRU or if you are unsure.
>
> +config QCOM_M0_BTSS_PIL
> + tristate "Qualcomm M0 BTSS Peripheral Image Loader"
> + depends on OF && ARCH_QCOM
> + select QCOM_MDT_LOADER
> + select QCOM_RPROC_COMMON
> + select QCOM_SCM
> + help
> + Say y here to support the Secure Peripheral Imager Loader for the
> + Qualcomm Bluetooth Subsystem running on the M0 remote processor found
> + in the IPQ5018 SoC. The M0 core is started and stopped using a
> + Secure Channel Manager call to TrustZone.
> +
> config QCOM_PIL_INFO
> tristate
>
> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
> index 1c7598b8475d..df80faf8d0df 100644
> --- a/drivers/remoteproc/Makefile
> +++ b/drivers/remoteproc/Makefile
> @@ -21,6 +21,7 @@ obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o
> obj-$(CONFIG_KEYSTONE_REMOTEPROC) += keystone_remoteproc.o
> obj-$(CONFIG_MESON_MX_AO_ARC_REMOTEPROC)+= meson_mx_ao_arc.o
> obj-$(CONFIG_PRU_REMOTEPROC) += pru_rproc.o
> +obj-$(CONFIG_QCOM_M0_BTSS_PIL) += qcom_m0_btss_pil.o
> obj-$(CONFIG_QCOM_PIL_INFO) += qcom_pil_info.o
> obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o
> obj-$(CONFIG_QCOM_Q6V5_COMMON) += qcom_q6v5.o
> diff --git a/drivers/remoteproc/qcom_m0_btss_pil.c b/drivers/remoteproc/qcom_m0_btss_pil.c
> new file mode 100644
> index 000000000000..7168e270e4d4
> --- /dev/null
> +++ b/drivers/remoteproc/qcom_m0_btss_pil.c
> @@ -0,0 +1,261 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2026 The Linux Foundation. All rights reserved.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/elf.h>
> +#include <linux/firmware/qcom/qcom_scm.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +#include <linux/soc/qcom/mdt_loader.h>
> +
> +#include "qcom_common.h"
> +
> +#define BTSS_PAS_ID 0xc
> +
> +struct m0_btss {
> + struct device *dev;
> + phys_addr_t mem_phys;
> + phys_addr_t mem_reloc;
> + void __iomem *mem_region;
> + size_t mem_size;
> + struct reset_control *btss_reset;
> +};
> +
> +static int m0_btss_start(struct rproc *rproc)
> +{
> + int ret;
> +
> + if (!qcom_scm_pas_supported(BTSS_PAS_ID)) {
> + dev_err(rproc->dev.parent,
> + "PAS is not available for peripheral: 0x%x\n",
> + BTSS_PAS_ID);
> + return -ENODEV;
> + }
> +
> + ret = qcom_scm_pas_auth_and_reset(BTSS_PAS_ID);
> + if (ret) {
> + dev_err(rproc->dev.parent, "Failed to start rproc: %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int m0_btss_stop(struct rproc *rproc)
> +{
> + int ret;
> +
> + if (rproc->state == RPROC_RUNNING || rproc->state == RPROC_CRASHED) {
> + ret = qcom_scm_pas_shutdown(BTSS_PAS_ID);
> + if (ret) {
> + dev_err(rproc->dev.parent, "Failed to stop rproc: %d\n",
> + ret);
> + return ret;
> + }
> +
> + dev_info(rproc->dev.parent, "Successfully stopped rproc\n");
> + }
> +
> + return 0;
> +}
> +
> +static int m0_btss_load(struct rproc *rproc, const struct firmware *fw)
> +{
> + struct m0_btss *desc = rproc->priv;
> + const struct elf32_phdr *phdrs;
> + const struct firmware *seg_fw;
> + const struct elf32_phdr *phdr;
> + const struct elf32_hdr *ehdr;
> + void __iomem *metadata;
> + size_t metadata_size;
> + int i, ret;
> +
> + ehdr = (const struct elf32_hdr *)fw->data;
> + phdrs = (const struct elf32_phdr *)(ehdr + 1);
> +
> + ret = request_firmware(&fw, rproc->firmware, rproc->dev.parent);
> + if (ret) {
> + dev_err(rproc->dev.parent, "Failed to request firmware: %d\n",
> + ret);
> + return ret;
> + }
> +
> + metadata = qcom_mdt_read_metadata(fw, &metadata_size, rproc->firmware,
> + rproc->dev.parent);
> + if (IS_ERR(metadata)) {
> + ret = PTR_ERR(metadata);
> + dev_err(rproc->dev.parent,
> + "Failed to read firmware metadata: %d\n", ret);
> + goto release_fw;
> + }
> +
> + ret = qcom_scm_pas_init_image(BTSS_PAS_ID, metadata,
> + metadata_size, NULL);
> + if (ret) {
> + dev_err(rproc->dev.parent, "PAS init image failed: %d\n", ret);
> + goto free_metadata;
> + }
> +
> + for (i = 0; i < ehdr->e_phnum; i++) {
> + char *seg_name __free(kfree) = kstrdup(rproc->firmware,
> + GFP_KERNEL);
> + if (!seg_name)
> + return -ENOMEM;
> +
> + phdr = &phdrs[i];
> +
> + /* Only process valid loadable data segments */
> + if (phdr->p_type != PT_LOAD || !phdr->p_memsz)
> + continue;
> +
> + if (phdr->p_vaddr + phdr->p_filesz > desc->mem_size) {
> + dev_err(rproc->dev.parent,
> + "Segment data exceeds the reserved memory area!\n");
> + goto free_metadata;
> + }
> +
> + /* Check if firmware is split across multiple segment files */
> + if (phdr->p_offset > fw->size ||
> + phdr->p_offset + phdr->p_filesz > fw->size) {
> + sprintf(seg_name + strlen(seg_name) - 3, "b%02d", i);
> + ret = request_firmware(&seg_fw, seg_name,
> + rproc->dev.parent);
> + if (ret) {
> + dev_err(rproc->dev.parent,
> + "Could not find split segment binary: %s\n",
> + seg_name);
> + goto free_metadata;
> + }
> +
> + /*
> + * Use the virtual instead of the physical address as
> + * the offset
> + */
> + memcpy_toio(desc->mem_region + phdr->p_vaddr,
> + seg_fw->data, phdr->p_filesz);
> +
> + release_firmware(seg_fw);
> + } else {
> + memcpy_toio(desc->mem_region + phdr->p_vaddr,
> + fw->data + phdr->p_offset, phdr->p_filesz);
> + }
> + }
> +
> + return 0;
> +
> +free_metadata:
> + kfree(metadata);
> +release_fw:
> + release_firmware(fw);
> + return ret;
> +}
> +
> +static const struct rproc_ops m0_btss_ops = {
> + .start = m0_btss_start,
> + .stop = m0_btss_stop,
> + .load = m0_btss_load,
> + .get_boot_addr = rproc_elf_get_boot_addr,
> +};
> +
> +static int m0_btss_alloc_memory_region(struct m0_btss *desc)
> +{
> + struct device *dev = desc->dev;
> + struct resource res;
> + int ret;
> +
> + ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res);
> + if (ret) {
> + dev_err(dev, "unable to acquire memory-region resource\n");
> + return ret;
> + }
> +
> + desc->mem_phys = res.start;
> + desc->mem_reloc = res.start;
> + desc->mem_size = resource_size(&res);
> + desc->mem_region = devm_ioremap(dev, desc->mem_phys, desc->mem_size);
> + if (!desc->mem_region) {
> + dev_err(dev, "unable to map memory region: %pR\n", &res);
> + return -ENOMEM;
> + }
> +
> + return 0;
> +}
> +
> +static int m0_btss_pil_probe(struct platform_device *pdev)
> +{
> + // struct reset_control *btss_reset;
> + struct device *dev = &pdev->dev;
> + const char *fw_name = NULL;
> + struct m0_btss *desc;
> + struct clk *lpo_clk;
> + struct rproc *rproc;
> + int ret;
> +
> + ret = of_property_read_string(dev->of_node, "firmware-name",
> + &fw_name);
> + if (ret < 0)
> + return ret;
> +
> + rproc = devm_rproc_alloc(dev, "m0btss", &m0_btss_ops,
> + fw_name, sizeof(*desc));
> + if (!rproc) {
> + dev_err(dev, "failed to allocate rproc\n");
> + return -ENOMEM;
> + }
> +
> + desc = rproc->priv;
> + desc->dev = dev;
> +
> + ret = m0_btss_alloc_memory_region(desc);
> + if (ret)
> + return ret;
> +
> + lpo_clk = devm_clk_get_enabled(dev, "btss_lpo_clk");
> + if (IS_ERR(lpo_clk))
> + return dev_err_probe(dev, PTR_ERR(lpo_clk),
> + "Failed to get lpo clock\n");
> +
> + desc->btss_reset = devm_reset_control_get(dev, "btss_reset");
> + if (IS_ERR_OR_NULL(desc->btss_reset))
> + return dev_err_probe(dev, PTR_ERR(desc->btss_reset),
> + "unable to acquire btss_reset\n");
> +
> + ret = reset_control_deassert(desc->btss_reset);
> + if (ret)
> + return dev_err_probe(rproc->dev.parent, ret,
> + "Failed to deassert reset\n");
> +
> + rproc->auto_boot = false;
> + ret = devm_rproc_add(dev, rproc);
> + if (ret)
> + return ret;
> +
> + platform_set_drvdata(pdev, rproc);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id m0_btss_of_match[] = {
> + { .compatible = "qcom,ipq5018-btss-pil" },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, m0_btss_of_match);
> +
> +static struct platform_driver m0_btss_pil_driver = {
> + .probe = m0_btss_pil_probe,
> + .driver = {
> + .name = "qcom-m0-btss-pil",
> + .of_match_table = m0_btss_of_match,
> + },
> +};
> +
> +module_platform_driver(m0_btss_pil_driver);
> +
> +MODULE_DESCRIPTION("Qualcomm M0 Bluetooth Subsystem Peripheral Image Loader");
> +MODULE_LICENSE("GPL");
>
> --
> 2.53.0
>
>
^ permalink raw reply
* Re: [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver
From: Bjorn Andersson @ 2026-06-28 3:08 UTC (permalink / raw)
To: Konrad Dybcio
Cc: george.moussalem, Jens Axboe, Ulf Hansson, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Johannes Berg, Jeff Johnson,
Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
Balakrishna Godavarthi, Rocky Liao, Saravana Kannan, Andrew Lunn,
Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Konrad Dybcio,
Mathieu Poirier, Philipp Zabel, linux-block, linux-kernel,
linux-mmc, devicetree, linux-wireless, ath10k, linux-arm-msm,
linux-bluetooth, netdev, linux-remoteproc
In-Reply-To: <38aceb33-b28e-4994-b277-de070b6dae2b@oss.qualcomm.com>
On Fri, Jun 26, 2026 at 01:20:25PM +0200, Konrad Dybcio wrote:
> On 6/25/26 4:10 PM, George Moussalem via B4 Relay wrote:
> > From: George Moussalem <george.moussalem@outlook.com>
> >
> > Add support to bring up the M0 core of the bluetooth subsystem found in
> > the IPQ5018 SoC.
> >
> > The signed firmware loaded is authenticated by TrustZone. If successful,
> > the M0 core boots the firmware and the peripheral is taken out of reset
> > using a Secure Channel Manager call to TrustZone.
> >
> > Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> > ---
>
> Can this not fit inside the existing PAS driver?
>
While the start/stop SCM calls look the same, this doesn't follow any of
the surrounding concepts. So I think this should follow the other
non-remoteproc uses of mdt_loader and scm directly instead.
Regards,
Bjorn
> Konrad
^ permalink raw reply
* [PATCH v2 0/7] iio: adc: Add TI ADS126X ADC family support
From: Kurt Borja @ 2026-06-28 5:36 UTC (permalink / raw)
To: Kurt Borja, Jonathan Cameron, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, David Lechner
Cc: Nuno Sá, Andy Shevchenko, linux-iio, devicetree,
linux-kernel, Jonathan Cameron
Hi all,
This series introduces support for TI ADS1262 and ADS1263 ADCs [1].
These devices are very similar (if not the same), except ADS1263
includes a secondary auxiliary ADC.
I plan to add these features to the main driver soon:
- Automatic calibration
- GPIO controller capabilities
- Channel hot-reloading in buffer mode
- SPI offload support (38400 SPS turns out to be too high for some
systems)
- User triggered, automatic calibration (Datasheet 9.4.9)
Additionally, full support for the (less capable) auxiliary ADC is
introduced by the auxiliary ti-ads1263-adc2 driver included in this
series.
The auxiliary ADC operates almost completely independent of the main
ADC. The only consideration that has to be taken for interoperability is
when reading conversion data in direct mode (Datasheet 9.4.7.1), which
happens only in buffer mode, when multiple channels are enabled.
When reading data in direct mode, all SPI activity is forbidden between
the data-ready signal and the data retrieval. To achieve this a second
mutex called xfer_lock was introduced to block SPI activity on the
device.
This is one of the biggest drivers I've developed, so I hope the code
and the comments are self-explainatory. If not, please let me know so I
can clarify them.
As always, thanks for your reviews and help. Submitting upstream is
always a great learning experience :)
[1] https://www.ti.com/lit/ds/symlink/ads1263.pdf
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
v2:
dt-bindings
-----------
- ADS1262/ADS1263 now use a fallback compatible
("ti,ads1263", "ti,ads1262") instead of two independent compatibles,
as the ADS1263 is a strict superset of the ADS1262.
- Reworked dt-bindings keeping up with the latest discussion around
per-channel IDAC and reference source selection. Reference source
and excitation (IDAC) configuration moved from device-level
properties to per-channel properties:
- ti,pos-refmux/ti,neg-refmux -> per-channel "reference-sources"
- ti,idac{1,2}-pin -> per-channel "excitation-channels"
- ti,idac{1,2}-microamp -> per-channel "excitation-current-nanoamp"
- ti,sbias-polarity -> ti,burn-out-polarity
- ti,sbias-magnitude -> burn-out-current-nanoamp
- ti,sbias-connection removed, maybe user-space should handle that
- Split the single "vref-supply" into "refp-supply" and "refn-supply".
- Renamed "ti,chop-mode" -> "ti,input-chopping" and
"ti,idac-rotation-mode" -> "ti,idac-chopping".
- Dropped "ti,rev-vref-pol"; reference polarity reversal is now handled
automatically by the driver based on refp/refn voltages.
- "#io-channel-cells" is now per-compatible to address the auxiliary
ADC.
- Dropped the "adc" sub-node and the separate ti,ads1263-adc2.yaml
binding.
ti-ads1262
----------
- Split the main driver to make review easier.
- Simplified the series a bit (I'll submit them later):
- Removed runtime PM support.
- Removed gpiochip support.
- Removed channel hot-reloading.
- Removed manual calibration support.
- Reworked ads1262_channel struct to avoid bitfields.
- Reworked firmware parsing and added easy to grep error messages.
- Reference polarity reversal (MODE0 REFREV) is now applied
automatically when refp < refn.
- Reorganized registers and bitfield masks with indentantion. Moved
bitfield values to enums.
- Removed union in DMA aligned rx buffer.
- Reworked the buffer/SPI transfer model.
- The regmap_bus callbacks now copy regs and values into DMA safe
buffers before transfer.
- Now channels in direct mode are enabled with regmap as latency is
not that big of a deal in this mode.
- Added per-channel IDAC and reference source configuration. Scale is
now computed from the per-channel reference source (internal 2.5 V,
external refp/refn, or AVDD-AVSS).
- Removed sensor bias (burn-out current) handling, as we are still
discussing the approach for it.
ti-ads1263-adc2
---------------
- Dropped callbacks in favor of exported (TI_ADS1262 namespace)
functions
- Dropped channel hot-reloading
- The auxiliary device is now instantiated with an explicit device link
and inherits the parent's OF node; scale/reference handling moved to
the parent driver.
v1: https://patch.msgid.link/20260612-ads126x-v1-0-894c788d03ed@gmail.com
---
Kurt Borja (7):
dt-bindings: iio: adc: Add TI ADS126x ADC family
iio: adc: Add ti-ads1262 driver
iio: adc: ti-ads1262: Add channel filter support
iio: adc: ti-ads1262: Add excitation current support
iio: adc: ti-ads1262: Add conversion delay support
iio: adc: ti-ads1262: Add buffer and trigger support
iio: adc: Add ti-ads1263-adc2 driver
.../devicetree/bindings/iio/adc/ti,ads1262.yaml | 309 ++++
MAINTAINERS | 9 +
drivers/iio/adc/Kconfig | 27 +
drivers/iio/adc/Makefile | 2 +
drivers/iio/adc/ti-ads1262.c | 1835 ++++++++++++++++++++
drivers/iio/adc/ti-ads1262.h | 39 +
drivers/iio/adc/ti-ads1263-adc2.c | 379 ++++
7 files changed, 2600 insertions(+)
---
base-commit: 7667a80340e99fd45357d0c90ae05813b01bbfef
change-id: 20251129-ads126x-fb6107505cae
prerequisite-change-id: 20260514-iio-adc-ti-ads122c14-d0b92479334e:v2
prerequisite-patch-id: 8be45fbe0c6037e775f6ef0e028184403241866c
prerequisite-patch-id: e76673f5e60ebd0a47584756cbcd297cb87d74b7
prerequisite-patch-id: 7eb0e6028d5a49d311e373be866ebae8e83a523a
prerequisite-patch-id: d5c4c4c78f42e6c0b4f186f1ab7f51aa6acd3561
prerequisite-patch-id: ee187ef4a7632cab6bc76c0588c8e2002ac26b7b
prerequisite-patch-id: 31ce1bee6d2c97f43df79c44e5386bf88a9a5d98
prerequisite-patch-id: 3ec60d0638598ef8d006c12a3a8dece2d6bdc54b
prerequisite-patch-id: bbfa2e76f94d06b60e3458dab448579c627225ea
--
Thanks,
~ Kurt
^ permalink raw reply
* [PATCH v2 1/7] dt-bindings: iio: adc: Add TI ADS126x ADC family
From: Kurt Borja @ 2026-06-28 5:36 UTC (permalink / raw)
To: Kurt Borja, Jonathan Cameron, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, David Lechner
Cc: Nuno Sá, Andy Shevchenko, linux-iio, devicetree,
linux-kernel, Jonathan Cameron
In-Reply-To: <20260628-ads126x-v2-0-4b1b231325ba@gmail.com>
The ADS1262 and ADS1263 are 32-bit, 38.4-kSPS delta-sigma ADCs with an
integrated PGA, internal reference, excitation and burn-out current
sources for sensor biasing and diagnostics. The ADS1263 adds a second,
24-bit delta-sigma ADC (ADC2) for background measurements.
Each can configure it's own voltage reference source, the two excitation
current sources (IDAC), plus input and excitation channels rotation for
offset and IDAC mismatch cancellation. This lets the device drive and
ratiometrically measure RTDs and other resistive sensors.
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
.../devicetree/bindings/iio/adc/ti,ads1262.yaml | 309 +++++++++++++++++++++
MAINTAINERS | 6 +
2 files changed, 315 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads1262.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads1262.yaml
new file mode 100644
index 0000000000000000..2f4e812ae2af135a
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/ti,ads1262.yaml
@@ -0,0 +1,309 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/ti,ads1262.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TI ADS1262/ADS1263 analog to digital converter
+
+maintainers:
+ - Kurt Borja <kuurtb@gmail.com>
+
+description: |
+ The ADS1262 and ADS1263 are 38.4-kSPS, delta-sigma (ΔΣ) ADCs with an
+ integrated PGA, reference, and internal fault monitors. The ADS1263 integrates
+ an auxiliary, 24-bit, ΔΣ ADC intended for background measurements.
+
+ Datasheets:
+ - ADS126x: https://www.ti.com/lit/ds/symlink/ads1262.pdf
+
+properties:
+ compatible:
+ oneOf:
+ - const: ti,ads1262
+ - items:
+ - const: ti,ads1263
+ - const: ti,ads1262
+
+ reg:
+ maxItems: 1
+
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+ spi-max-frequency:
+ maximum: 8000000
+
+ spi-cpha: true
+
+ interrupts:
+ description: Data ready (DRDY) interrupt line.
+ maxItems: 1
+
+ start-gpios:
+ description: Start conversion control.
+ maxItems: 1
+
+ reset-gpios:
+ maxItems: 1
+
+ dvdd-supply:
+ description: Digital power supply.
+
+ avdd-supply:
+ description: Analog power supply.
+
+ refp-supply:
+ description: External positive voltage reference.
+
+ refn-supply:
+ description: External negative voltage reference.
+
+ ti,vbias:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: Enables the level-shift voltage on the AINCOM pin.
+
+ clocks:
+ maxItems: 1
+
+ '#io-channel-cells':
+ minimum: 1
+ maximum: 2
+
+ '#gpio-cells':
+ const: 2
+
+ gpio-controller: true
+
+patternProperties:
+ "^channel@[0-9]+$":
+ $ref: /schemas/iio/adc/adc.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ reg:
+ maxItems: 1
+
+ diff-channels:
+ description: |
+ Selects the analog input configuration for this channel. The first
+ value is the positive input and the second is the negative input.
+ The following values are available:
+ 0: AIN0 pin
+ 1: AIN1 pin
+ 2: AIN2 pin
+ 3: AIN3 pin
+ 4: AIN4 pin
+ 5: AIN5 pin
+ 6: AIN6 pin
+ 7: AIN7 pin
+ 8: AIN8 pin
+ 9: AIN9 pin
+ 10: AINCOM pin
+ 11: Temperature sensor monitor
+ 12: Analog power supply monitor
+ 13: Digital power supply monitor
+ 14: TDAC test signal
+ 15: Float (open connection)
+ items:
+ minimum: 0
+ maximum: 15
+
+ reference-sources:
+ minItems: 2
+ description:
+ Indicates the reference sources for this channel. The first and second
+ items are the positive and negative sources of the main ADC (ADC1).
+ The third item is the reference source of the secondary ADC (ADC2).
+ items:
+ - enum: [internal, ain0, ain2, ain4, avdd]
+ - enum: [internal, ain1, ain3, ain5, avss]
+ - enum: [internal, ain0-ain1, ain2-ain3, ain4-ain5, avdd-avss]
+
+ excitation-channels:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 2
+ maxItems: 2
+ description: |
+ Selects pins for the IDAC sources from the following options:
+ 0: AIN0
+ 1: AIN1
+ 2: AIN2
+ 3: AIN3
+ 4: AIN4
+ 5: AIN5
+ 6: AIN6
+ 7: AIN7
+ 8: AIN8
+ 9: AIN9
+ 10: AINCOM
+ 11: No Connection
+ The first value corresponds to IDAC1 and the second to IDAC2.
+ items:
+ minimum: 0
+ maximum: 11
+
+ excitation-current-nanoamp:
+ minItems: 2
+ maxItems: 2
+ description:
+ The first value corresponds to IDAC1 and the second to IDAC2.
+ items:
+ enum: [0, 50000, 100000, 250000, 500000, 750000, 1000000, 1500000,
+ 2000000, 2500000, 3000000]
+
+ burn-out-current-nanoamp:
+ description:
+ The ADC incorporates a sensor bias current source that can be used to
+ apply a small test current to diagnose broken sensor leads or problems
+ existing in the sensor.
+ enum: [0, 500, 2000, 10000, 50000, 200000]
+
+ ti,burn-out-resistor:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: |
+ Instead of a fixed current, the sensor bias (burn-out) current source
+ can be pulled using an internal 10 MΩ resistor.
+
+ ti,burn-out-polarity:
+ $ref: /schemas/types.yaml#/definitions/string
+ description:
+ The sensor bias can be configured to either pull-up or pull-down mode.
+ In pull-up mode, the current flows into the positive input and flows
+ out of the negative input. In pull-down mode, the polarities are
+ reversed.
+ enum: [pull-up, pull-down]
+
+ input-chopping:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description:
+ When enabled, the ADC performs two internal conversions to cancel the
+ input offset voltage. The first conversion is taken with normal input
+ polarity. The ADC reverses the internal input polarity for the second
+ conversion. The difference of the two conversions is computed to yield
+ the final corrected result with the offset voltage removed.
+
+ ti,idac-chopping:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description:
+ Automatically swap the IDAC1 and IDAC2 connections of alternate
+ conversions. The ADC averages the alternate conversions to eliminate
+ IDAC mismatch.
+
+ ti,pga-bypass:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: Bypass the Programmable Gain Amplifier (PGA).
+
+ dependencies:
+ excitation-channels: [excitation-current-nanoamp]
+ excitation-current-nanoamp: [excitation-channels]
+ burn-out-current-nanoamp:
+ not:
+ required:
+ - ti,burn-out-resistor
+
+ required:
+ - reg
+
+dependencies:
+ refn-supply: [refp-supply]
+
+required:
+ - compatible
+ - reg
+ - avdd-supply
+ - dvdd-supply
+ - '#address-cells'
+ - '#size-cells'
+
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: ti,ads1263
+ then:
+ properties:
+ '#io-channel-cells':
+ const: 2
+ patternProperties:
+ "^channel@[0-9]+$":
+ properties:
+ reference-sources:
+ minItems: 3
+ else:
+ properties:
+ '#io-channel-cells':
+ const: 1
+ patternProperties:
+ "^channel@[0-9]+$":
+ properties:
+ reference-sources:
+ maxItems: 2
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc@0 {
+ compatible = "ti,ads1262";
+ reg = <0>;
+ spi-max-frequency = <8000000>;
+ spi-cpha;
+ avdd-supply = <&avdd>;
+ dvdd-supply = <&dvdd>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reset-gpios = <&gpio 18 GPIO_ACTIVE_LOW>;
+ interrupts-extended = <&gpio 10 IRQ_TYPE_EDGE_FALLING>;
+
+ channel@0 {
+ reg = <0>;
+ diff-channels = <0x0 0xA>;
+ };
+ };
+ };
+
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc@0 {
+ compatible = "ti,ads1263", "ti,ads1262";
+ reg = <0>;
+ spi-max-frequency = <8000000>;
+ spi-cpha;
+ avdd-supply = <&avdd>;
+ dvdd-supply = <&dvdd>;
+ refp-supply = <&refp>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reset-gpios = <&gpio 18 GPIO_ACTIVE_LOW>;
+ interrupts-extended = <&gpio 10 IRQ_TYPE_EDGE_FALLING>;
+
+ channel@0 {
+ reg = <0>;
+ diff-channels = <0x4 0x5>;
+ reference-sources = "ain2", "ain3", "ain2-ain3";
+ excitation-channels = <0x1 0x6>;
+ excitation-current-nanoamp = <500000 500000>;
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 6c0471487974f145..9b83d294734b574d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -26923,6 +26923,12 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/adc/ti,ads1018.yaml
F: drivers/iio/adc/ti-ads1018.c
+TI ADS1262 ADC DRIVER
+M: Kurt Borja <kuurtb@gmail.com>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/iio/adc/ti,ads1262.yaml
+
TI ADS7924 ADC DRIVER
M: Hugo Villeneuve <hvilleneuve@dimonoff.com>
L: linux-iio@vger.kernel.org
--
2.54.0
^ permalink raw reply related
* [PATCH v2 2/7] iio: adc: Add ti-ads1262 driver
From: Kurt Borja @ 2026-06-28 5:36 UTC (permalink / raw)
To: Kurt Borja, Jonathan Cameron, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, David Lechner
Cc: Nuno Sá, Andy Shevchenko, linux-iio, devicetree,
linux-kernel, Jonathan Cameron
In-Reply-To: <20260628-ads126x-v2-0-4b1b231325ba@gmail.com>
Add the ti-ads1262 driver with initial support for the primary ADC
(ADC1). The ADS1263 auxiliary ADC (ADC2) is handled by a separate driver
and interoperability considerations were taken into account.
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
MAINTAINERS | 1 +
drivers/iio/adc/Kconfig | 13 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/ti-ads1262.c | 1206 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1221 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 9b83d294734b574d..d868b25f2c65bcd9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -26928,6 +26928,7 @@ M: Kurt Borja <kuurtb@gmail.com>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/adc/ti,ads1262.yaml
+F: drivers/iio/adc/ti-ads1262.c
TI ADS7924 ADC DRIVER
M: Hugo Villeneuve <hvilleneuve@dimonoff.com>
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 5845db2011fb5be1..6051092c20b96731 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -1811,6 +1811,19 @@ config TI_ADS124S08
This driver can also be built as a module. If so, the module will be
called ti-ads124s08.
+config TI_ADS1262
+ tristate "Texas Instruments ADS1262"
+ depends on SPI
+ select REGMAP
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ If you say yes here you get support for Texas Instruments ADS1262 and
+ ADS1263 ADC chips.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads1262.
+
config TI_ADS1298
tristate "Texas Instruments ADS1298"
depends on SPI
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 72f4c6b33ca87b3f..4b1f89a2317a35f7 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -155,6 +155,7 @@ obj-$(CONFIG_TI_ADS1100) += ti-ads1100.o
obj-$(CONFIG_TI_ADS1119) += ti-ads1119.o
obj-$(CONFIG_TI_ADS112C14) += ti-ads112c14.o
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
+obj-$(CONFIG_TI_ADS1262) += ti-ads1262.o
obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o
obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o
obj-$(CONFIG_TI_ADS131M02) += ti-ads131m02.o
diff --git a/drivers/iio/adc/ti-ads1262.c b/drivers/iio/adc/ti-ads1262.c
new file mode 100644
index 0000000000000000..6103cf5a2d1624a9
--- /dev/null
+++ b/drivers/iio/adc/ti-ads1262.c
@@ -0,0 +1,1206 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Texas Instruments ADS1262 ADC driver
+ *
+ * Copyright (C) 2025 Kurt Borja <kuurtb@gmail.com>
+ */
+
+#include <linux/array_size.h>
+#include <linux/align.h>
+#include <linux/bitfield.h>
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/compiler_attributes.h>
+#include <linux/compiler_types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/lockdep.h>
+#include <linux/math.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/overflow.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <asm/byteorder.h>
+
+#include <linux/iio/iio.h>
+
+#define ADS1262_OPCODE_NOP 0x00
+#define ADS1262_OPCODE_RESET 0x06
+#define ADS1262_OPCODE_START1 0x08
+#define ADS1262_OPCODE_STOP1 0x0A
+#define ADS1262_OPCODE_START2 0x0C
+#define ADS1262_OPCODE_STOP2 0x0E
+#define ADS1262_OPCODE_RDATA1 0x12
+#define ADS1262_OPCODE_RDATA2 0x14
+#define ADS1262_OPCODE_SYOCAL1 0x16
+#define ADS1262_OPCODE_SYGCAL1 0x17
+#define ADS1262_OPCODE_SFOCAL1 0x19
+#define ADS1262_OPCODE_SYOCAL2 0x1B
+#define ADS1262_OPCODE_SYGCAL2 0x1C
+#define ADS1262_OPCODE_SFOCAL2 0x1E
+#define ADS1262_OPCODE_RREG 0x20
+#define ADS1262_OPCODE_WREG 0x40
+
+#define ADS1262_ID_REG 0x00
+#define ADS1262_DEV_ID_MASK GENMASK(7, 5)
+#define ADS1262_REV_ID_MASK GENMASK(4, 0)
+
+#define ADS1262_POWER_REG 0x01
+#define ADS1262_POWER_RESET_MASK BIT(4)
+#define ADS1262_POWER_VBIAS_MASK BIT(1)
+#define ADS1262_POWER_INTREF_MASK BIT(0)
+
+#define ADS1262_INTERFACE_REG 0x02
+#define ADS1262_INTERFACE_TIMEOUT_MASK BIT(3)
+#define ADS1262_INTERFACE_STATUS_MASK BIT(2)
+#define ADS1262_INTERFACE_CRC_MASK GENMASK(1, 0)
+
+#define ADS1262_MODE0_REG 0x03
+#define ADS1262_MODE0_REFREV_MASK BIT(7)
+#define ADS1262_MODE0_RUNMODE_MASK BIT(6)
+#define ADS1262_MODE0_IDAC_CHOP_MASK BIT(5)
+#define ADS1262_MODE0_INPUT_CHOP_MASK BIT(4)
+#define ADS1262_MODE0_DELAY_MASK GENMASK(3, 0)
+
+#define ADS1262_MODE1_REG 0x04
+#define ADS1262_MODE1_FILTER_MASK GENMASK(7, 5)
+#define ADS1262_MODE1_SBADC_MASK BIT(4)
+#define ADS1262_MODE1_SBPOL_MASK BIT(3)
+#define ADS1262_MODE1_SBMAG_MASK GENMASK(2, 0)
+
+#define ADS1262_MODE2_REG 0x05
+#define ADS1262_MODE2_BYPASS_MASK BIT(7)
+#define ADS1262_MODE2_GAIN_MASK GENMASK(6, 4)
+#define ADS1262_MODE2_DR_MASK GENMASK(3, 0)
+
+#define ADS1262_INPMUX_REG 0x06
+#define ADS1262_INPMUX_MUXP_MASK GENMASK(7, 4)
+#define ADS1262_INPMUX_MUXN_MASK GENMASK(3, 0)
+
+#define ADS1262_OFCAL0_REG 0x07
+#define ADS1262_OFCAL1_REG 0x08
+#define ADS1262_OFCAL2_REG 0x09
+#define ADS1262_FSCAL0_REG 0x0A
+#define ADS1262_FSCAL1_REG 0x0B
+#define ADS1262_FSCAL2_REG 0x0C
+
+#define ADS1262_IDACMUX_REG 0x0D
+#define ADS1262_IDACMUX_MUX2_MASK GENMASK(7, 4)
+#define ADS1262_IDACMUX_MUX1_MASK GENMASK(3, 0)
+
+#define ADS1262_IDACMAG_REG 0x0E
+#define ADS1262_IDACMAG_MAG2_MASK GENMASK(7, 4)
+#define ADS1262_IDACMAG_MAG1_MASK GENMASK(3, 0)
+
+#define ADS1262_REFMUX_REG 0x0F
+#define ADS1262_REFMUX_RMUXP_MASK GENMASK(5, 3)
+#define ADS1262_REFMUX_RMUXN_MASK GENMASK(2, 0)
+
+#define ADS1262_TDACP_REG 0x10
+#define ADS1262_TDACP_OUTP_MASK BIT(7)
+#define ADS1262_TDACP_MAGP_MASK GENMASK(4, 0)
+
+#define ADS1262_TDACN_REG 0x11
+#define ADS1262_TDACN_OUTN_MASK BIT(7)
+#define ADS1262_TDACN_MAGN_MASK GENMASK(4, 0)
+
+#define ADS1262_GPIOCON_REG 0x12
+#define ADS1262_GPIODIR_REG 0x13
+#define ADS1262_GPIODAT_REG 0x14
+
+#define ADS1262_ADC2CFG_REG 0x15
+#define ADS1262_ADC2CFG_DR2_MASK GENMASK(7, 6)
+#define ADS1262_ADC2CFG_REF2_MASK GENMASK(5, 3)
+#define ADS1262_ADC2CFG_GAIN2_MASK GENMASK(2, 0)
+
+#define ADS1262_ADC2MUX_REG 0x16
+#define ADS1262_ADC2MUX_MUXP2_MASK GENMASK(7, 4)
+#define ADS1262_ADC2MUX_MUXN2_MASK GENMASK(3, 0)
+
+#define ADS1262_ADC2OFC0_REG 0x17
+#define ADS1262_ADC2OFC1_REG 0x18
+#define ADS1262_ADC2FSC0_REG 0x19
+#define ADS1262_ADC2FSC1_REG 0x1A
+#define ADS1262_REG_COUNT 0x1B
+
+#define ADS1262_MAX_CHANNEL_COUNT 16
+#define ADS1262_XFER_BUFFER_SZ 11
+
+enum {
+ ADS1262_RUNMODE_CONTINUOUS,
+ ADS1262_RUNMODE_PULSE,
+};
+
+enum {
+ ADS1262_DR_2_5_SPS,
+ ADS1262_DR_5_SPS,
+ ADS1262_DR_10_SPS,
+ ADS1262_DR_16_6_SPS,
+ ADS1262_DR_20_SPS,
+ ADS1262_DR_50_SPS,
+ ADS1262_DR_60_SPS,
+ ADS1262_DR_100_SPS,
+ ADS1262_DR_400_SPS,
+ ADS1262_DR_1200_SPS,
+ ADS1262_DR_2400_SPS,
+ ADS1262_DR_4800_SPS,
+ ADS1262_DR_7200_SPS,
+ ADS1262_DR_14400_SPS,
+ ADS1262_DR_19200_SPS,
+ ADS1262_DR_38400_SPS,
+};
+
+enum {
+ ADS1262_INPMUX_AIN0,
+ ADS1262_INPMUX_AIN1,
+ ADS1262_INPMUX_AIN2,
+ ADS1262_INPMUX_AIN3,
+ ADS1262_INPMUX_AIN4,
+ ADS1262_INPMUX_AIN5,
+ ADS1262_INPMUX_AIN6,
+ ADS1262_INPMUX_AIN7,
+ ADS1262_INPMUX_AIN8,
+ ADS1262_INPMUX_AIN9,
+ ADS1262_INPMUX_AINCOM,
+ ADS1262_INPMUX_TEMP,
+ ADS1262_INPMUX_AVDD,
+ ADS1262_INPMUX_DVDD,
+ ADS1262_INPMUX_TDAC,
+ ADS1262_INPMUX_FLOAT,
+ ADS1262_INPMUX_LAST
+};
+
+enum {
+ ADS1262_REFMUX_INTERNAL,
+ ADS1262_REFMUX_AIN0_AIN1,
+ ADS1262_REFMUX_AIN2_AIN3,
+ ADS1262_REFMUX_AIN4_AIN5,
+ ADS1262_REFMUX_AVDD_AVSS,
+ ADS1262_REFMUX_LAST
+};
+
+struct ads1262_chip_info {
+ const char *name;
+ bool has_aux_adc;
+};
+
+struct ads1262_channel {
+ u8 input[2];
+ u8 gain;
+ u8 data_rate;
+ u8 reference[3];
+ u8 pga_bypass:1;
+ u8 ref_reversal:1;
+ u8 input_chop:1;
+ u8 idac_chop:1;
+};
+
+struct ads1262 {
+ struct spi_device *spi;
+ const struct ads1262_chip_info *info;
+ struct regmap *regmap;
+ struct iio_dev *indio_dev;
+ struct gpio_desc *reset_gpiod;
+ struct gpio_desc *start_gpiod;
+
+ /* Protects channel state */
+ struct mutex chan_lock;
+ unsigned int num_channels;
+ struct ads1262_channel *channels;
+ struct completion drdy;
+
+ int refp_uV;
+ int refn_uV;
+ int avdd_uV;
+
+ /* Protects transfer buffers and concurrent SPI transfers */
+ struct mutex xfer_lock;
+
+ u8 tx[ADS1262_XFER_BUFFER_SZ] __aligned(IIO_DMA_MINALIGN);
+ u8 rx[ADS1262_XFER_BUFFER_SZ] __aligned(IIO_DMA_MINALIGN);
+};
+
+static const int ads1262_data_rate_avail[][2] = {
+ [ADS1262_DR_2_5_SPS] = { 2, 500000 },
+ [ADS1262_DR_5_SPS] = { 5, 0 },
+ [ADS1262_DR_10_SPS] = { 10, 0 },
+ [ADS1262_DR_16_6_SPS] = { 16, 666667 },
+ [ADS1262_DR_20_SPS] = { 20, 0 },
+ [ADS1262_DR_50_SPS] = { 50, 0 },
+ [ADS1262_DR_60_SPS] = { 60, 0 },
+ [ADS1262_DR_100_SPS] = { 100, 0 },
+ [ADS1262_DR_400_SPS] = { 400, 0 },
+ [ADS1262_DR_1200_SPS] = { 1200, 0 },
+ [ADS1262_DR_2400_SPS] = { 2400, 0 },
+ [ADS1262_DR_4800_SPS] = { 4800, 0 },
+ [ADS1262_DR_7200_SPS] = { 7200, 0 },
+ [ADS1262_DR_14400_SPS] = { 14400, 0 },
+ [ADS1262_DR_19200_SPS] = { 19200, 0 },
+ [ADS1262_DR_38400_SPS] = { 38400, 0 },
+};
+
+static const int ads1262_pga_gain_avail[] = {
+ 1, 2, 4, 8, 16, 32
+};
+
+#define ads1262_find_one(_arr, _val) \
+({ \
+ int _i; \
+ for (_i = 0; _i < ARRAY_SIZE(_arr); _i++) { \
+ if ((_val) == _arr[_i]) \
+ break; \
+ } \
+ if (_i == ARRAY_SIZE(_arr)) \
+ _i = -EINVAL; \
+ _i; \
+})
+
+#define ads1262_find_two(_arr, _val, _val2) \
+({ \
+ int _i; \
+ for (_i = 0; _i < ARRAY_SIZE(_arr); _i++) { \
+ if ((_val) == _arr[_i][0] && (_val2) == _arr[_i][1]) \
+ break; \
+ } \
+ if (_i == ARRAY_SIZE(_arr)) \
+ _i = -EINVAL; \
+ _i; \
+})
+
+#define ads1262_find_string(_arr, _str) \
+({ \
+ int _i; \
+ for (_i = 0; _i < ARRAY_SIZE(_arr); _i++) { \
+ if (!strcmp(_arr[_i], _str)) \
+ break; \
+ } \
+ if (_i == ARRAY_SIZE(_arr)) \
+ _i = -EINVAL; \
+ _i; \
+})
+
+static int ads1262_dev_cmd(struct ads1262 *st, u8 opcode)
+{
+ guard(mutex)(&st->xfer_lock);
+
+ return spi_write_then_read(st->spi, &opcode, sizeof(opcode), NULL, 0);
+}
+
+static int ads1262_dev_reset(struct ads1262 *st)
+{
+ int ret;
+
+ ret = ads1262_dev_cmd(st, ADS1262_OPCODE_RESET);
+ if (ret)
+ return ret;
+
+ /* RESET command timing requirement is 1.085 usecs (Table 9-32) */
+ fsleep(2);
+
+ return ret;
+}
+
+static int ads1262_dev_start(struct ads1262 *st)
+{
+ int ret;
+
+ if (st->start_gpiod)
+ ret = gpiod_set_value_cansleep(st->start_gpiod, 1);
+ else
+ ret = ads1262_dev_cmd(st, ADS1262_OPCODE_START1);
+
+ return ret;
+}
+
+static int ads1262_dev_stop(struct ads1262 *st)
+{
+ int ret;
+
+ if (st->start_gpiod)
+ ret = gpiod_set_value_cansleep(st->start_gpiod, 0);
+ else
+ ret = ads1262_dev_cmd(st, ADS1262_OPCODE_STOP1);
+
+ return ret;
+}
+
+static int ads1262_dev_start_one(struct ads1262 *st, u8 runmode)
+{
+ int ret;
+
+ ret = ads1262_dev_start(st);
+ if (ret)
+ return ret;
+
+ if (runmode == ADS1262_RUNMODE_CONTINUOUS)
+ return ads1262_dev_stop(st);
+
+ return 0;
+}
+
+static int ads1262_calculate_scale(struct ads1262 *st, u8 realbits, u8 gain,
+ u8 pos_ref, u8 neg_ref, int *val, int *val2)
+{
+ u64 divd, divr, tmp, rem;
+ int pos_uV, neg_uV;
+
+ switch (pos_ref) {
+ case ADS1262_REFMUX_INTERNAL:
+ /* Internal voltage reference is 2.5 V */
+ pos_uV = 2500000;
+ break;
+ case ADS1262_REFMUX_AIN0_AIN1...ADS1262_REFMUX_AIN4_AIN5:
+ pos_uV = st->refp_uV;
+ break;
+ case ADS1262_REFMUX_AVDD_AVSS:
+ pos_uV = st->avdd_uV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (neg_ref) {
+ case ADS1262_REFMUX_INTERNAL:
+ neg_uV = 0;
+ break;
+ case ADS1262_REFMUX_AIN0_AIN1...ADS1262_REFMUX_AIN4_AIN5:
+ neg_uV = st->refn_uV == -ENODEV ? 0 : st->refn_uV;
+ break;
+ case ADS1262_REFMUX_AVDD_AVSS:
+ neg_uV = 0;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (pos_uV < neg_uV)
+ divd = neg_uV - pos_uV;
+ else
+ divd = pos_uV - neg_uV;
+ divr = BIT_ULL(gain + realbits - 1) * 1000;
+ tmp = div64_u64(divd * NANO, divr);
+
+ *val = div64_u64_rem(tmp, NANO, &rem);
+ *val2 = rem;
+
+ return 0;
+}
+
+static int ads1262_channel_get_scale(struct ads1262 *st,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2)
+{
+ struct ads1262_channel *chan_data = &st->channels[chan->scan_index];
+
+ return ads1262_calculate_scale(st, chan->scan_type.realbits, chan_data->gain,
+ chan_data->reference[0], chan_data->reference[1],
+ val, val2);
+}
+
+static long ads1262_wait_for_conversion(struct ads1262 *st)
+{
+ /*
+ * The first conversion latency is affected by the channel's data rate,
+ * filter, the configurable conversion delay and whether chop mode
+ * and/or IDAC rotation mode are enabled.
+ *
+ * The worst possible latency is calculated by taking the lowest data
+ * rate (2.5 SPS) and the sinc4 filter. This gives a latency of 1600 ms
+ * (Table 9-13). Then add the slowest configurable conversion delay
+ * (9 ms) and multiply by 4 to account for chop and IDAC rotation modes
+ * (Equation 20).
+ *
+ * Final result is 4 * (1600 ms + 9 ms) = 6436.
+ */
+ return wait_for_completion_interruptible_timeout(&st->drdy, msecs_to_jiffies(6436));
+}
+
+static int ads1262_dev_read_by_cmd(struct ads1262 *st, u8 cmd, __be32 *val)
+{
+ guard(mutex)(&st->xfer_lock);
+
+ return spi_write_then_read(st->spi, &cmd, sizeof(cmd), val, sizeof(*val));
+}
+
+static int ads1262_channel_enable(struct ads1262 *st,
+ struct ads1262_channel *chan)
+{
+ u8 mode0, mode2, inpmux, refmux;
+ int ret;
+
+ /* Avoid using guard() here to mitigate AB/BA deadlock warning */
+ mutex_lock(&st->chan_lock);
+ mode0 = FIELD_PREP(ADS1262_MODE0_INPUT_CHOP_MASK, chan->input_chop) |
+ FIELD_PREP(ADS1262_MODE0_IDAC_CHOP_MASK, chan->idac_chop) |
+ FIELD_PREP(ADS1262_MODE0_REFREV_MASK, chan->ref_reversal);
+ mode2 = FIELD_PREP(ADS1262_MODE2_DR_MASK, chan->data_rate) |
+ FIELD_PREP(ADS1262_MODE2_GAIN_MASK, chan->gain) |
+ FIELD_PREP(ADS1262_MODE2_BYPASS_MASK, chan->pga_bypass);
+ inpmux = FIELD_PREP(ADS1262_INPMUX_MUXN_MASK, chan->input[1]) |
+ FIELD_PREP(ADS1262_INPMUX_MUXP_MASK, chan->input[0]);
+ refmux = FIELD_PREP(ADS1262_REFMUX_RMUXN_MASK, chan->reference[1]) |
+ FIELD_PREP(ADS1262_REFMUX_RMUXP_MASK, chan->reference[0]);
+ mutex_unlock(&st->chan_lock);
+
+ ret = regmap_update_bits(st->regmap, ADS1262_MODE0_REG,
+ ADS1262_MODE0_INPUT_CHOP_MASK |
+ ADS1262_MODE0_IDAC_CHOP_MASK |
+ ADS1262_MODE0_REFREV_MASK, mode0);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, ADS1262_MODE2_REG,
+ ADS1262_MODE2_DR_MASK |
+ ADS1262_MODE2_GAIN_MASK |
+ ADS1262_MODE2_BYPASS_MASK, mode2);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, ADS1262_INPMUX_REG,
+ ADS1262_INPMUX_MUXN_MASK |
+ ADS1262_INPMUX_MUXP_MASK, inpmux);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(st->regmap, ADS1262_REFMUX_REG,
+ ADS1262_REFMUX_RMUXN_MASK |
+ ADS1262_REFMUX_RMUXP_MASK, refmux);
+}
+
+static int ads1262_set_runmode(struct ads1262 *st, u8 runmode)
+{
+ return regmap_update_bits(st->regmap, ADS1262_MODE0_REG,
+ ADS1262_MODE0_RUNMODE_MASK,
+ FIELD_PREP(ADS1262_MODE0_RUNMODE_MASK, runmode));
+}
+
+static int ads1262_channel_read(struct ads1262 *st,
+ struct ads1262_channel *chan_data, __be32 *val)
+{
+ u8 runmode;
+ int ret;
+
+ IIO_DEV_ACQUIRE_DIRECT_MODE(st->indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ /*
+ * When a channel has chop mode or IDAC rotation mode, the first
+ * conversion is always withheld so the datasheet suggests using the
+ * CONTINUOUS mode and briefly starting and stopping conversions to
+ * achieve the same effect (Section 9.4.1.2).
+ */
+ if (chan_data->input_chop || chan_data->idac_chop)
+ runmode = ADS1262_RUNMODE_CONTINUOUS;
+ else
+ runmode = ADS1262_RUNMODE_PULSE;
+
+ ret = ads1262_set_runmode(st, runmode);
+ if (ret)
+ return ret;
+
+ ret = ads1262_channel_enable(st, chan_data);
+ if (ret)
+ return ret;
+
+ reinit_completion(&st->drdy);
+
+ ret = ads1262_dev_start_one(st, runmode);
+ if (ret)
+ return ret;
+
+ ads1262_wait_for_conversion(st);
+
+ return ads1262_dev_read_by_cmd(st, ADS1262_OPCODE_RDATA1, val);
+}
+
+static int ads1262_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct ads1262 *st = iio_priv(indio_dev);
+ struct ads1262_channel *chan_data = &st->channels[chan->scan_index];
+ u8 realbits = chan->scan_type.realbits;
+ __be32 raw;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ads1262_channel_read(st, chan_data, &raw);
+ if (ret)
+ return ret;
+ *val = sign_extend32(be32_to_cpu(raw), realbits - 1);
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE: {
+ guard(mutex)(&st->chan_lock);
+
+ ret = ads1262_channel_get_scale(st, chan, val, val2);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT_PLUS_NANO;
+ }
+
+ case IIO_CHAN_INFO_HARDWAREGAIN: {
+ guard(mutex)(&st->chan_lock);
+
+ *val = ads1262_pga_gain_avail[chan_data->gain];
+
+ return IIO_VAL_INT;
+ }
+
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ guard(mutex)(&st->chan_lock);
+
+ *val = ads1262_data_rate_avail[chan_data->data_rate][0];
+ *val2 = ads1262_data_rate_avail[chan_data->data_rate][1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ads1262_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, const int **vals,
+ int *type, int *length, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *vals = (const int *)ads1262_data_rate_avail;
+ *length = ARRAY_SIZE(ads1262_data_rate_avail) * 2;
+ return IIO_AVAIL_LIST;
+
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ *type = IIO_VAL_INT;
+ *vals = ads1262_pga_gain_avail;
+ *length = ARRAY_SIZE(ads1262_pga_gain_avail);
+ return IIO_AVAIL_LIST;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ads1262_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct ads1262 *st = iio_priv(indio_dev);
+ struct ads1262_channel *chan_data;
+ int i;
+
+ chan_data = &st->channels[chan->scan_index];
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ i = ads1262_find_two(ads1262_data_rate_avail, val, val2);
+ if (i < 0)
+ return i;
+
+ guard(mutex)(&st->chan_lock);
+ chan_data->data_rate = i;
+
+ break;
+ }
+
+ case IIO_CHAN_INFO_HARDWAREGAIN: {
+ i = ads1262_find_one(ads1262_pga_gain_avail, val);
+ if (i < 0)
+ return i;
+
+ guard(mutex)(&st->chan_lock);
+ chan_data->gain = i;
+
+ break;
+ }
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int ads1262_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_CONVDELAY:
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+}
+
+static int ads1262_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int writeval, unsigned int *readval)
+{
+ struct ads1262 *st = iio_priv(indio_dev);
+
+ if (readval)
+ return regmap_read_bypassed(st->regmap, reg, readval);
+
+ return regmap_write(st->regmap, reg, writeval);
+}
+
+static const struct iio_info ads1262_iio_info = {
+ .read_raw = ads1262_read_raw,
+ .read_avail = ads1262_read_avail,
+ .write_raw = ads1262_write_raw,
+ .write_raw_get_fmt = ads1262_write_raw_get_fmt,
+ .debugfs_reg_access = ads1262_debugfs_reg_access,
+};
+
+static irqreturn_t ads1262_irq_handler(int irq, void *dev_id)
+{
+ struct ads1262 *st = dev_id;
+
+ complete(&st->drdy);
+
+ return IRQ_HANDLED;
+}
+
+static int ads1262_alloc_channels(struct ads1262 *st,
+ struct iio_chan_spec **channels)
+{
+ struct device *dev = &st->spi->dev;
+ struct ads1262_channel *chan_data;
+ struct iio_chan_spec *chans;
+ unsigned int i, num_channels;
+
+ /* Account for the timestamp channel */
+ num_channels = st->num_channels + 1;
+ chans = devm_kcalloc(dev, num_channels, sizeof(*chans), GFP_KERNEL);
+ if (!chans)
+ return -ENOMEM;
+
+ for (i = 0; i < st->num_channels; i++) {
+ chan_data = &st->channels[i];
+ chans[i] = (struct iio_chan_spec) {
+ .type = IIO_VOLTAGE,
+ .channel = chan_data->input[0],
+ .channel2 = chan_data->input[1],
+ .scan_index = i,
+ .scan_type = {
+ .format = IIO_SCAN_FORMAT_SIGNED_INT,
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_BE,
+ },
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_type_available =
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .indexed = true,
+ .differential = true,
+ };
+ }
+
+ chans[i] = IIO_CHAN_SOFT_TIMESTAMP(i);
+
+ *channels = chans;
+
+ return num_channels;
+}
+
+static int ads1262_dev_configure(struct ads1262 *st)
+{
+ struct device *dev = &st->spi->dev;
+ int ret;
+
+ ret = ads1262_dev_reset(st);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to reset device\n");
+
+ ret = regmap_clear_bits(st->regmap, ADS1262_POWER_REG,
+ ADS1262_POWER_RESET_MASK);
+ if (ret)
+ return ret;
+
+ ret = regmap_clear_bits(st->regmap, ADS1262_INTERFACE_REG,
+ ADS1262_INTERFACE_STATUS_MASK |
+ ADS1262_INTERFACE_CRC_MASK);
+ if (ret)
+ return ret;
+
+ if (device_property_present(dev, "ti,vbias")) {
+ ret = regmap_set_bits(st->regmap, ADS1262_POWER_REG,
+ ADS1262_POWER_VBIAS_MASK);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct regmap_range ads1262_read_write_range[] = {
+ regmap_reg_range(ADS1262_ID_REG, ADS1262_ADC2FSC1_REG),
+};
+
+static const struct regmap_range ads1262_read_only_range[] = {
+ regmap_reg_range(ADS1262_ID_REG, ADS1262_ID_REG),
+};
+
+static const struct regmap_access_table ads1262_wr_table = {
+ .yes_ranges = ads1262_read_write_range,
+ .n_yes_ranges = ARRAY_SIZE(ads1262_read_write_range),
+ .no_ranges = ads1262_read_only_range,
+ .n_no_ranges = ARRAY_SIZE(ads1262_read_only_range),
+};
+
+static const struct regmap_access_table ads1262_rd_table = {
+ .yes_ranges = ads1262_read_write_range,
+ .n_yes_ranges = ARRAY_SIZE(ads1262_read_write_range),
+};
+
+static const struct reg_default ads1262_reg_defaults[] = {
+ { ADS1262_POWER_REG, 0x11 },
+ { ADS1262_INTERFACE_REG, 0x05 },
+ { ADS1262_MODE0_REG, 0x00 },
+ { ADS1262_MODE1_REG, 0x80 },
+ { ADS1262_MODE2_REG, 0x04 },
+ { ADS1262_INPMUX_REG, 0x01 },
+ { ADS1262_OFCAL0_REG, 0x00 },
+ { ADS1262_OFCAL1_REG, 0x00 },
+ { ADS1262_OFCAL2_REG, 0x00 },
+ { ADS1262_FSCAL0_REG, 0x00 },
+ { ADS1262_FSCAL1_REG, 0x00 },
+ { ADS1262_FSCAL2_REG, 0x40 },
+ { ADS1262_IDACMUX_REG, 0xBB },
+ { ADS1262_IDACMAG_REG, 0x00 },
+ { ADS1262_REFMUX_REG, 0x00 },
+ { ADS1262_TDACP_REG, 0x00 },
+ { ADS1262_TDACN_REG, 0x00 },
+ { ADS1262_GPIOCON_REG, 0x00 },
+ { ADS1262_GPIODIR_REG, 0x00 },
+ { ADS1262_GPIODAT_REG, 0x00 },
+ { ADS1262_ADC2CFG_REG, 0x00 },
+ { ADS1262_ADC2MUX_REG, 0x01 },
+ { ADS1262_ADC2OFC0_REG, 0x00 },
+ { ADS1262_ADC2OFC1_REG, 0x00 },
+ { ADS1262_ADC2FSC0_REG, 0x00 },
+ { ADS1262_ADC2FSC1_REG, 0x40 },
+};
+
+static const struct regmap_config ads1262_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .wr_table = &ads1262_wr_table,
+ .rd_table = &ads1262_rd_table,
+ .reg_defaults = ads1262_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ads1262_reg_defaults),
+ .max_register = ADS1262_ADC2FSC1_REG,
+ .read_flag_mask = ADS1262_OPCODE_RREG,
+ .write_flag_mask = ADS1262_OPCODE_WREG,
+ .can_sleep = true,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int ads1262_regmap_read(void *context, const void *reg_buf,
+ size_t reg_size, void *val_buf, size_t val_size)
+{
+ struct ads1262 *st = context;
+ struct spi_transfer xfer = {
+ .tx_buf = st->tx,
+ .rx_buf = st->rx,
+ .len = reg_size + 1 + val_size,
+ };
+ int ret;
+
+ guard(mutex)(&st->xfer_lock);
+
+ memset(st->tx, 0, reg_size + 1 + val_size);
+
+ memcpy(&st->tx[0], reg_buf, 1);
+ st->tx[1] = val_size - 1;
+
+ ret = spi_sync_transfer(st->spi, &xfer, 1);
+ if (ret)
+ return ret;
+
+ memcpy(val_buf, &st->rx[2], val_size);
+
+ return 0;
+}
+
+static int ads1262_regmap_gather_write(void *context, const void *reg_buf,
+ size_t reg_size, const void *val_buf,
+ size_t val_size)
+{
+ struct ads1262 *st = context;
+ struct spi_transfer xfer = {
+ .tx_buf = st->tx,
+ .rx_buf = st->rx,
+ .len = reg_size + 1 + val_size,
+ };
+
+ guard(mutex)(&st->xfer_lock);
+
+ memset(st->tx, 0, reg_size + 1 + val_size);
+
+ memcpy(&st->tx[0], reg_buf, 1);
+ st->tx[1] = val_size - 1;
+ memcpy(&st->tx[2], val_buf, val_size);
+
+ return spi_sync_transfer(st->spi, &xfer, 1);
+}
+
+static int ads1262_regmap_write(void *context, const void *data, size_t count)
+{
+ return ads1262_regmap_gather_write(context, data, 1, data + 1,
+ count - 1);
+}
+
+static const struct regmap_bus ads1262_regmap_bus = {
+ .read = ads1262_regmap_read,
+ .gather_write = ads1262_regmap_gather_write,
+ .write = ads1262_regmap_write,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+ /* The first two bytes of the buffer are reserved for the protocol */
+ .max_raw_read = ADS1262_XFER_BUFFER_SZ - 2,
+ .max_raw_write = ADS1262_XFER_BUFFER_SZ - 2,
+};
+
+static int ads1262_channel_sanity_check(struct ads1262 *st,
+ struct ads1262_channel *chan)
+{
+ struct device *dev = &st->spi->dev;
+ int pos_uV, neg_uV;
+
+ /* Positive reference */
+ switch (chan->reference[0]) {
+ case ADS1262_REFMUX_INTERNAL:
+ /* Internal voltage reference is 2.5 V */
+ pos_uV = 2500000;
+ break;
+ case ADS1262_REFMUX_AIN0_AIN1...ADS1262_REFMUX_AIN4_AIN5:
+ if (st->refp_uV == -ENODEV)
+ return dev_err_probe(dev, -ENODEV, "refp-supply not found\n");
+ pos_uV = st->refp_uV;
+ break;
+ case ADS1262_REFMUX_AVDD_AVSS:
+ pos_uV = st->avdd_uV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Negative reference */
+ switch (chan->reference[1]) {
+ case ADS1262_REFMUX_INTERNAL:
+ neg_uV = 0;
+ break;
+ case ADS1262_REFMUX_AIN0_AIN1...ADS1262_REFMUX_AIN4_AIN5:
+ neg_uV = st->refn_uV == -ENODEV ? 0 : st->refn_uV;
+ break;
+ case ADS1262_REFMUX_AVDD_AVSS:
+ neg_uV = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* If the positive reference is bellow the negative, reverse it */
+ if (pos_uV < neg_uV)
+ chan->ref_reversal = 1;
+
+ /* ADC2 reference */
+ switch (chan->reference[2]) {
+ case ADS1262_REFMUX_AIN0_AIN1...ADS1262_REFMUX_AIN4_AIN5:
+ if (st->refp_uV == -ENODEV)
+ return dev_err_probe(dev, -ENODEV, "refp-supply not found\n");
+ pos_uV = st->refp_uV;
+ neg_uV = st->refn_uV == -ENODEV ? 0 : st->refn_uV;
+
+ /* We can't reverse ADC2 reference */
+ if (pos_uV < neg_uV && st->info->has_aux_adc)
+ return dev_err_probe(dev, -EINVAL,
+ "ADC2 doesn't support negative reference voltage\n");
+
+ break;
+ }
+
+ return 0;
+}
+
+static const char * const ads1262_ref_sources_positive[] = {
+ [ADS1262_REFMUX_INTERNAL] = "internal",
+ [ADS1262_REFMUX_AIN0_AIN1] = "ain0",
+ [ADS1262_REFMUX_AIN2_AIN3] = "ain2",
+ [ADS1262_REFMUX_AIN4_AIN5] = "ain4",
+ [ADS1262_REFMUX_AVDD_AVSS] = "avdd"
+};
+
+static const char * const ads1262_ref_sources_negative[] = {
+ [ADS1262_REFMUX_INTERNAL] = "internal",
+ [ADS1262_REFMUX_AIN0_AIN1] = "ain1",
+ [ADS1262_REFMUX_AIN2_AIN3] = "ain3",
+ [ADS1262_REFMUX_AIN4_AIN5] = "ain5",
+ [ADS1262_REFMUX_AVDD_AVSS] = "avss"
+};
+
+static const char * const ads1262_ref_sources_adc2[] = {
+ [ADS1262_REFMUX_INTERNAL] = "internal",
+ [ADS1262_REFMUX_AIN0_AIN1] = "ain0-ain1",
+ [ADS1262_REFMUX_AIN2_AIN3] = "ain2-ain3",
+ [ADS1262_REFMUX_AIN4_AIN5] = "ain4-ain5",
+ [ADS1262_REFMUX_AVDD_AVSS] = "avdd-avss"
+};
+
+static int ads1262_parse_channel_node(struct ads1262 *st,
+ struct ads1262_channel *chan,
+ struct fwnode_handle *node)
+{
+ struct device *dev = &st->spi->dev;
+ const char *ref_sources[3] = {};
+ u32 pins[2];
+ int ret;
+
+ /* Write non-zero default configuration values */
+ chan->data_rate = ADS1262_DR_20_SPS;
+
+ ret = fwnode_property_read_u32_array(node, "diff-channels", pins, ARRAY_SIZE(pins));
+ if (ret)
+ return dev_err_probe(dev, ret, "%s: Failed to read diff-channels\n",
+ fwnode_get_name(node));
+ if (pins[0] >= ADS1262_INPMUX_LAST || pins[1] >= ADS1262_INPMUX_LAST)
+ return dev_err_probe(dev, -EINVAL, "%s: input channels not in range\n",
+ fwnode_get_name(node));
+ chan->input[0] = pins[0];
+ chan->input[1] = pins[1];
+
+ if (fwnode_property_present(node, "reference-sources")) {
+ ret = fwnode_property_read_string_array(node, "reference-sources", ref_sources,
+ st->info->has_aux_adc ? 3 : 2);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "%s: Failed to read reference-sources\n",
+ fwnode_get_name(node));
+
+ ret = ads1262_find_string(ads1262_ref_sources_positive, ref_sources[0]);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "%s: Invalid positive reference\n",
+ fwnode_get_name(node));
+ chan->reference[0] = ret;
+
+ ret = ads1262_find_string(ads1262_ref_sources_negative, ref_sources[1]);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "%s: Invalid negative reference\n",
+ fwnode_get_name(node));
+ chan->reference[1] = ret;
+
+ if (st->info->has_aux_adc) {
+ ret = ads1262_find_string(ads1262_ref_sources_adc2, ref_sources[2]);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "%s: Invalid ADC2 reference\n",
+ fwnode_get_name(node));
+ chan->reference[2] = ret;
+ }
+ }
+
+ if (fwnode_property_present(node, "ti,pga-bypass"))
+ chan->pga_bypass = 1;
+
+ if (fwnode_property_present(node, "input-chopping"))
+ chan->input_chop = 1;
+
+ if (fwnode_property_present(node, "ti,idac-chopping"))
+ chan->idac_chop = 1;
+
+ return ads1262_channel_sanity_check(st, chan);
+}
+
+static int ads1262_parse_firmware(struct ads1262 *st)
+{
+ struct device *dev = &st->spi->dev;
+ struct clk *clk;
+ u32 reg;
+ int ret;
+
+ /* Set the nominal clock frequency */
+ clk = devm_clk_get_optional_enabled_with_rate(dev, NULL, 7372800);
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk),
+ "Failed to get external clock\n");
+
+ ret = devm_regulator_get_enable(dev, "dvdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get dvdd regulator\n");
+
+ st->avdd_uV = devm_regulator_get_enable_read_voltage(dev, "avdd");
+ if (st->avdd_uV < 0)
+ return dev_err_probe(dev, st->avdd_uV, "Failed to get avdd regulator\n");
+
+ st->refp_uV = devm_regulator_get_enable_read_voltage(dev, "refp");
+ if (st->refp_uV < 0 && st->refp_uV != -ENODEV)
+ return dev_err_probe(dev, st->refp_uV, "Failed to get refp regulator\n");
+
+ st->refn_uV = devm_regulator_get_enable_read_voltage(dev, "refn");
+ if (st->refn_uV < 0 && st->refn_uV != -ENODEV)
+ return dev_err_probe(dev, st->refn_uV, "Failed to get refn regulator\n");
+
+ st->start_gpiod = devm_gpiod_get_optional(dev, "start", GPIOD_OUT_LOW);
+ if (IS_ERR(st->start_gpiod))
+ return dev_err_probe(dev, PTR_ERR(st->start_gpiod),
+ "Failed to get start GPIO\n");
+
+ st->reset_gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(st->reset_gpiod))
+ return dev_err_probe(dev, PTR_ERR(st->reset_gpiod),
+ "Failed to get reset GPIO\n");
+
+ st->num_channels = device_get_named_child_node_count(dev, "channel");
+ if (!st->num_channels)
+ return dev_err_probe(dev, -ENXIO,
+ "No 'channel' nodes configured\n");
+ if (st->num_channels > ADS1262_MAX_CHANNEL_COUNT)
+ return dev_err_probe(dev, -EINVAL, "Too many channels\n");
+
+ st->channels = devm_kcalloc(dev, st->num_channels, sizeof(*st->channels),
+ GFP_KERNEL);
+ if (!st->channels)
+ return -ENOMEM;
+
+ device_for_each_named_child_node_scoped(dev, node, "channel") {
+ ret = fwnode_property_read_u32(node, "reg", ®);
+ if (ret)
+ return dev_err_probe(dev, ret, "%s: Failed to read channel reg\n",
+ fwnode_get_name(node));
+ if (reg >= st->num_channels)
+ return dev_err_probe(dev, -EINVAL, "%s: reg out of range\n",
+ fwnode_get_name(node));
+
+ ret = ads1262_parse_channel_node(st, &st->channels[reg], node);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ads1262_spi_probe(struct spi_device *spi)
+{
+ const struct ads1262_chip_info *info;
+ struct iio_chan_spec *channels;
+ struct device *dev = &spi->dev;
+ struct iio_dev *indio_dev;
+ struct ads1262 *st;
+ int num_channels;
+ int ret;
+
+ info = spi_get_device_match_data(spi);
+ if (!info)
+ return -EINVAL;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+ st->indio_dev = indio_dev;
+ st->info = info;
+ init_completion(&st->drdy);
+ dev_set_drvdata(dev, st);
+
+ ret = devm_mutex_init(dev, &st->chan_lock);
+ if (ret)
+ return ret;
+ ret = devm_mutex_init(dev, &st->xfer_lock);
+ if (ret)
+ return ret;
+
+ ret = ads1262_parse_firmware(st);
+ if (ret)
+ return ret;
+
+ st->regmap = devm_regmap_init(dev, &ads1262_regmap_bus, st,
+ &ads1262_regmap_config);
+ if (IS_ERR(st->regmap))
+ return PTR_ERR(st->regmap);
+
+ ret = ads1262_dev_configure(st);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to configure device\n");
+
+ num_channels = ads1262_alloc_channels(st, &channels);
+ if (num_channels < 0)
+ return num_channels;
+ indio_dev->name = info->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ads1262_iio_info;
+ indio_dev->channels = channels;
+ indio_dev->num_channels = num_channels;
+
+ if (spi->irq > 0) {
+ ret = devm_request_irq(dev, spi->irq, ads1262_irq_handler,
+ IRQF_NO_THREAD, info->name, st);
+ if (ret)
+ return ret;
+ }
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct ads1262_chip_info ads1262_chip_info = {
+ .name = "ads1262",
+ .has_aux_adc = false,
+};
+
+static const struct ads1262_chip_info ads1263_chip_info = {
+ .name = "ads1263",
+ .has_aux_adc = true,
+};
+
+static const struct of_device_id ads1262_of_match[] = {
+ { .compatible = "ti,ads1263", .data = &ads1263_chip_info },
+ { .compatible = "ti,ads1262", .data = &ads1262_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ads1262_of_match);
+
+static const struct spi_device_id ads1262_spi_match[] = {
+ { .name = "ads1263", .driver_data = (kernel_ulong_t)&ads1263_chip_info },
+ { .name = "ads1262", .driver_data = (kernel_ulong_t)&ads1262_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ads1262_spi_match);
+
+static struct spi_driver ads1262_spi_driver = {
+ .driver = {
+ .name = "ads1262",
+ .of_match_table = ads1262_of_match,
+ },
+ .probe = ads1262_spi_probe,
+ .id_table = ads1262_spi_match,
+};
+module_spi_driver(ads1262_spi_driver);
+
+MODULE_DESCRIPTION("Texas Instruments ADS1262 ADC driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kurt Borja <kuurtb@gmail.com>");
--
2.54.0
^ permalink raw reply related
* [PATCH v2 3/7] iio: adc: ti-ads1262: Add channel filter support
From: Kurt Borja @ 2026-06-28 5:36 UTC (permalink / raw)
To: Kurt Borja, Jonathan Cameron, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, David Lechner
Cc: Nuno Sá, Andy Shevchenko, linux-iio, devicetree,
linux-kernel, Jonathan Cameron
In-Reply-To: <20260628-ads126x-v2-0-4b1b231325ba@gmail.com>
Expose per-channel filter configuration through the filter_type
attribute.
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
drivers/iio/adc/ti-ads1262.c | 65 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 64 insertions(+), 1 deletion(-)
diff --git a/drivers/iio/adc/ti-ads1262.c b/drivers/iio/adc/ti-ads1262.c
index 6103cf5a2d1624a9..ece97a0c2b1304ad 100644
--- a/drivers/iio/adc/ti-ads1262.c
+++ b/drivers/iio/adc/ti-ads1262.c
@@ -146,6 +146,14 @@ enum {
ADS1262_RUNMODE_PULSE,
};
+enum {
+ ADS1262_FILTER_SINC1,
+ ADS1262_FILTER_SINC2,
+ ADS1262_FILTER_SINC3,
+ ADS1262_FILTER_SINC4,
+ ADS1262_FILTER_FIR,
+};
+
enum {
ADS1262_DR_2_5_SPS,
ADS1262_DR_5_SPS,
@@ -201,6 +209,7 @@ struct ads1262_chip_info {
struct ads1262_channel {
u8 input[2];
+ u8 filter;
u8 gain;
u8 data_rate;
u8 reference[3];
@@ -441,7 +450,7 @@ static int ads1262_dev_read_by_cmd(struct ads1262 *st, u8 cmd, __be32 *val)
static int ads1262_channel_enable(struct ads1262 *st,
struct ads1262_channel *chan)
{
- u8 mode0, mode2, inpmux, refmux;
+ u8 mode0, mode1, mode2, inpmux, refmux;
int ret;
/* Avoid using guard() here to mitigate AB/BA deadlock warning */
@@ -449,6 +458,7 @@ static int ads1262_channel_enable(struct ads1262 *st,
mode0 = FIELD_PREP(ADS1262_MODE0_INPUT_CHOP_MASK, chan->input_chop) |
FIELD_PREP(ADS1262_MODE0_IDAC_CHOP_MASK, chan->idac_chop) |
FIELD_PREP(ADS1262_MODE0_REFREV_MASK, chan->ref_reversal);
+ mode1 = FIELD_PREP(ADS1262_MODE1_FILTER_MASK, chan->filter);
mode2 = FIELD_PREP(ADS1262_MODE2_DR_MASK, chan->data_rate) |
FIELD_PREP(ADS1262_MODE2_GAIN_MASK, chan->gain) |
FIELD_PREP(ADS1262_MODE2_BYPASS_MASK, chan->pga_bypass);
@@ -465,6 +475,11 @@ static int ads1262_channel_enable(struct ads1262 *st,
if (ret)
return ret;
+ ret = regmap_update_bits(st->regmap, ADS1262_MODE1_REG,
+ ADS1262_MODE1_FILTER_MASK, mode1);
+ if (ret)
+ return ret;
+
ret = regmap_update_bits(st->regmap, ADS1262_MODE2_REG,
ADS1262_MODE2_DR_MASK |
ADS1262_MODE2_GAIN_MASK |
@@ -682,6 +697,52 @@ static irqreturn_t ads1262_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static int ads1262_get_filter_type(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ads1262 *st = iio_priv(indio_dev);
+ struct ads1262_channel *chan_data;
+
+ guard(mutex)(&st->chan_lock);
+
+ chan_data = &st->channels[chan->scan_index];
+ return chan_data->filter;
+}
+
+static int ads1262_set_filter_type(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int val)
+{
+ struct ads1262 *st = iio_priv(indio_dev);
+
+ guard(mutex)(&st->chan_lock);
+ st->channels[chan->scan_index].filter = val;
+
+ return 0;
+}
+
+static const char * const ads1262_filter_type_labels[] = {
+ [ADS1262_FILTER_SINC1] = "sinc1",
+ [ADS1262_FILTER_SINC2] = "sinc2",
+ [ADS1262_FILTER_SINC3] = "sinc3",
+ [ADS1262_FILTER_SINC4] = "sinc4",
+ [ADS1262_FILTER_FIR] = "fir",
+};
+
+static const struct iio_enum ads1262_filter_type_enum = {
+ .items = ads1262_filter_type_labels,
+ .num_items = ARRAY_SIZE(ads1262_filter_type_labels),
+ .get = ads1262_get_filter_type,
+ .set = ads1262_set_filter_type,
+};
+
+static const struct iio_chan_spec_ext_info ads1262_ext_info[] = {
+ IIO_ENUM("filter_type", IIO_SEPARATE, &ads1262_filter_type_enum),
+ IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE,
+ &ads1262_filter_type_enum),
+ { }
+};
+
static int ads1262_alloc_channels(struct ads1262 *st,
struct iio_chan_spec **channels)
{
@@ -718,6 +779,7 @@ static int ads1262_alloc_channels(struct ads1262 *st,
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.indexed = true,
.differential = true,
+ .ext_info = ads1262_ext_info,
};
}
@@ -983,6 +1045,7 @@ static int ads1262_parse_channel_node(struct ads1262 *st,
int ret;
/* Write non-zero default configuration values */
+ chan->filter = ADS1262_FILTER_FIR;
chan->data_rate = ADS1262_DR_20_SPS;
ret = fwnode_property_read_u32_array(node, "diff-channels", pins, ARRAY_SIZE(pins));
--
2.54.0
^ permalink raw reply related
* [PATCH v2 4/7] iio: adc: ti-ads1262: Add excitation current support
From: Kurt Borja @ 2026-06-28 5:36 UTC (permalink / raw)
To: Kurt Borja, Jonathan Cameron, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, David Lechner
Cc: Nuno Sá, Andy Shevchenko, linux-iio, devicetree,
linux-kernel, Jonathan Cameron
In-Reply-To: <20260628-ads126x-v2-0-4b1b231325ba@gmail.com>
Support the two IDAC excitation current sources. Each channel can route
its IDAC1/IDAC2 outputs to a pin via the "excitation-channels" property
and select a magnitude via "excitation-current-nanoamp".
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
drivers/iio/adc/ti-ads1262.c | 78 ++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 76 insertions(+), 2 deletions(-)
diff --git a/drivers/iio/adc/ti-ads1262.c b/drivers/iio/adc/ti-ads1262.c
index ece97a0c2b1304ad..8921eaae537f6b0a 100644
--- a/drivers/iio/adc/ti-ads1262.c
+++ b/drivers/iio/adc/ti-ads1262.c
@@ -193,6 +193,22 @@ enum {
ADS1262_INPMUX_LAST
};
+enum {
+ ADS1262_IDACMUX_AIN0,
+ ADS1262_IDACMUX_AIN1,
+ ADS1262_IDACMUX_AIN2,
+ ADS1262_IDACMUX_AIN3,
+ ADS1262_IDACMUX_AIN4,
+ ADS1262_IDACMUX_AIN5,
+ ADS1262_IDACMUX_AIN6,
+ ADS1262_IDACMUX_AIN7,
+ ADS1262_IDACMUX_AIN8,
+ ADS1262_IDACMUX_AIN9,
+ ADS1262_IDACMUX_AINCOM,
+ ADS1262_IDACMUX_NO_CONN,
+ ADS1262_IDACMUX_LAST
+};
+
enum {
ADS1262_REFMUX_INTERNAL,
ADS1262_REFMUX_AIN0_AIN1,
@@ -213,6 +229,8 @@ struct ads1262_channel {
u8 gain;
u8 data_rate;
u8 reference[3];
+ u8 idac_mux[2];
+ u8 idac_mag[2];
u8 pga_bypass:1;
u8 ref_reversal:1;
u8 input_chop:1;
@@ -450,7 +468,7 @@ static int ads1262_dev_read_by_cmd(struct ads1262 *st, u8 cmd, __be32 *val)
static int ads1262_channel_enable(struct ads1262 *st,
struct ads1262_channel *chan)
{
- u8 mode0, mode1, mode2, inpmux, refmux;
+ u8 mode0, mode1, mode2, inpmux, idacmux, idacmag, refmux;
int ret;
/* Avoid using guard() here to mitigate AB/BA deadlock warning */
@@ -464,6 +482,10 @@ static int ads1262_channel_enable(struct ads1262 *st,
FIELD_PREP(ADS1262_MODE2_BYPASS_MASK, chan->pga_bypass);
inpmux = FIELD_PREP(ADS1262_INPMUX_MUXN_MASK, chan->input[1]) |
FIELD_PREP(ADS1262_INPMUX_MUXP_MASK, chan->input[0]);
+ idacmux = FIELD_PREP(ADS1262_IDACMUX_MUX1_MASK, chan->idac_mux[0]) |
+ FIELD_PREP(ADS1262_IDACMUX_MUX2_MASK, chan->idac_mux[1]);
+ idacmag = FIELD_PREP(ADS1262_IDACMAG_MAG1_MASK, chan->idac_mag[0]) |
+ FIELD_PREP(ADS1262_IDACMAG_MAG2_MASK, chan->idac_mag[1]);
refmux = FIELD_PREP(ADS1262_REFMUX_RMUXN_MASK, chan->reference[1]) |
FIELD_PREP(ADS1262_REFMUX_RMUXP_MASK, chan->reference[0]);
mutex_unlock(&st->chan_lock);
@@ -493,6 +515,18 @@ static int ads1262_channel_enable(struct ads1262 *st,
if (ret)
return ret;
+ ret = regmap_update_bits(st->regmap, ADS1262_IDACMUX_REG,
+ ADS1262_IDACMUX_MUX1_MASK |
+ ADS1262_IDACMUX_MUX2_MASK, idacmux);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, ADS1262_IDACMAG_REG,
+ ADS1262_IDACMAG_MAG1_MASK |
+ ADS1262_IDACMAG_MAG2_MASK, idacmag);
+ if (ret)
+ return ret;
+
return regmap_update_bits(st->regmap, ADS1262_REFMUX_REG,
ADS1262_REFMUX_RMUXN_MASK |
ADS1262_REFMUX_RMUXP_MASK, refmux);
@@ -1040,13 +1074,19 @@ static int ads1262_parse_channel_node(struct ads1262 *st,
struct fwnode_handle *node)
{
struct device *dev = &st->spi->dev;
+ static const u32 idac_nA[] = {
+ 0, 50000, 100000, 250000, 500000, 750000,
+ 1000000, 1500000, 2000000, 2500000, 3000000
+ };
const char *ref_sources[3] = {};
- u32 pins[2];
+ u32 pins[2], mags[2];
int ret;
/* Write non-zero default configuration values */
chan->filter = ADS1262_FILTER_FIR;
chan->data_rate = ADS1262_DR_20_SPS;
+ chan->idac_mux[0] = ADS1262_IDACMUX_NO_CONN;
+ chan->idac_mux[1] = ADS1262_IDACMUX_NO_CONN;
ret = fwnode_property_read_u32_array(node, "diff-channels", pins, ARRAY_SIZE(pins));
if (ret)
@@ -1086,6 +1126,40 @@ static int ads1262_parse_channel_node(struct ads1262 *st,
}
}
+ if (fwnode_property_present(node, "excitation-channels")) {
+ ret = fwnode_property_read_u32_array(node, "excitation-channels",
+ pins, ARRAY_SIZE(pins));
+ if (ret)
+ return dev_err_probe(dev, ret, "%s: Failed to read excitation-channels\n",
+ fwnode_get_name(node));
+ if (pins[0] >= ADS1262_IDACMUX_LAST || pins[1] >= ADS1262_IDACMUX_LAST)
+ return dev_err_probe(dev, -EINVAL, "%s: excitation-channels not in range\n",
+ fwnode_get_name(node));
+ chan->idac_mux[0] = pins[0];
+ chan->idac_mux[1] = pins[1];
+
+ ret = fwnode_property_read_u32_array(node, "excitation-current-nanoamp",
+ mags, ARRAY_SIZE(mags));
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "%s: Failed to read excitation-current-nanoamp\n",
+ fwnode_get_name(node));
+
+ ret = ads1262_find_one(idac_nA, mags[0]);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "%s: Invalid excitation-current-nanoamp\n",
+ fwnode_get_name(node));
+ chan->idac_mag[0] = ret;
+
+ ret = ads1262_find_one(idac_nA, mags[1]);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "%s: Invalid excitation-current-nanoamp\n",
+ fwnode_get_name(node));
+ chan->idac_mag[1] = ret;
+ }
+
if (fwnode_property_present(node, "ti,pga-bypass"))
chan->pga_bypass = 1;
--
2.54.0
^ permalink raw reply related
* [PATCH v2 5/7] iio: adc: ti-ads1262: Add conversion delay support
From: Kurt Borja @ 2026-06-28 5:36 UTC (permalink / raw)
To: Kurt Borja, Jonathan Cameron, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, David Lechner
Cc: Nuno Sá, Andy Shevchenko, linux-iio, devicetree,
linux-kernel, Jonathan Cameron
In-Reply-To: <20260628-ads126x-v2-0-4b1b231325ba@gmail.com>
Expose the programmable conversion start delay as a per-channel
IIO_CHAN_INFO_CONVDELAY attribute.
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
drivers/iio/adc/ti-ads1262.c | 63 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 62 insertions(+), 1 deletion(-)
diff --git a/drivers/iio/adc/ti-ads1262.c b/drivers/iio/adc/ti-ads1262.c
index 8921eaae537f6b0a..4ae22c1b0b4b7d79 100644
--- a/drivers/iio/adc/ti-ads1262.c
+++ b/drivers/iio/adc/ti-ads1262.c
@@ -141,6 +141,21 @@
#define ADS1262_MAX_CHANNEL_COUNT 16
#define ADS1262_XFER_BUFFER_SZ 11
+enum {
+ ADS1262_DELAY_NO_DELAY,
+ ADS1262_DELAY_8700_NS,
+ ADS1262_DELAY_17_US,
+ ADS1262_DELAY_35_US,
+ ADS1262_DELAY_69_US,
+ ADS1262_DELAY_139_US,
+ ADS1262_DELAY_278_US,
+ ADS1262_DELAY_555_US,
+ ADS1262_DELAY_1100_US,
+ ADS1262_DELAY_2200_US,
+ ADS1262_DELAY_4400_US,
+ ADS1262_DELAY_8800_US,
+};
+
enum {
ADS1262_RUNMODE_CONTINUOUS,
ADS1262_RUNMODE_PULSE,
@@ -225,6 +240,7 @@ struct ads1262_chip_info {
struct ads1262_channel {
u8 input[2];
+ u8 delay;
u8 filter;
u8 gain;
u8 data_rate;
@@ -281,6 +297,21 @@ static const int ads1262_data_rate_avail[][2] = {
[ADS1262_DR_38400_SPS] = { 38400, 0 },
};
+static const int ads1262_conv_delay_avail[][2] = {
+ [ADS1262_DELAY_NO_DELAY] = { 0, 0 },
+ [ADS1262_DELAY_8700_NS] = { 0, 8700 },
+ [ADS1262_DELAY_17_US] = { 0, 17000 },
+ [ADS1262_DELAY_35_US] = { 0, 35000 },
+ [ADS1262_DELAY_69_US] = { 0, 69000 },
+ [ADS1262_DELAY_139_US] = { 0, 139000 },
+ [ADS1262_DELAY_278_US] = { 0, 278000 },
+ [ADS1262_DELAY_555_US] = { 0, 555000 },
+ [ADS1262_DELAY_1100_US] = { 0, 1100000 },
+ [ADS1262_DELAY_2200_US] = { 0, 2200000 },
+ [ADS1262_DELAY_4400_US] = { 0, 4400000 },
+ [ADS1262_DELAY_8800_US] = { 0, 8800000 },
+};
+
static const int ads1262_pga_gain_avail[] = {
1, 2, 4, 8, 16, 32
};
@@ -473,7 +504,8 @@ static int ads1262_channel_enable(struct ads1262 *st,
/* Avoid using guard() here to mitigate AB/BA deadlock warning */
mutex_lock(&st->chan_lock);
- mode0 = FIELD_PREP(ADS1262_MODE0_INPUT_CHOP_MASK, chan->input_chop) |
+ mode0 = FIELD_PREP(ADS1262_MODE0_DELAY_MASK, chan->delay) |
+ FIELD_PREP(ADS1262_MODE0_INPUT_CHOP_MASK, chan->input_chop) |
FIELD_PREP(ADS1262_MODE0_IDAC_CHOP_MASK, chan->idac_chop) |
FIELD_PREP(ADS1262_MODE0_REFREV_MASK, chan->ref_reversal);
mode1 = FIELD_PREP(ADS1262_MODE1_FILTER_MASK, chan->filter);
@@ -491,6 +523,7 @@ static int ads1262_channel_enable(struct ads1262 *st,
mutex_unlock(&st->chan_lock);
ret = regmap_update_bits(st->regmap, ADS1262_MODE0_REG,
+ ADS1262_MODE0_DELAY_MASK |
ADS1262_MODE0_INPUT_CHOP_MASK |
ADS1262_MODE0_IDAC_CHOP_MASK |
ADS1262_MODE0_REFREV_MASK, mode0);
@@ -625,6 +658,15 @@ static int ads1262_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT_PLUS_MICRO;
}
+ case IIO_CHAN_INFO_CONVDELAY: {
+ guard(mutex)(&st->chan_lock);
+
+ *val = ads1262_conv_delay_avail[chan_data->delay][0];
+ *val2 = ads1262_conv_delay_avail[chan_data->delay][1];
+
+ return IIO_VAL_INT_PLUS_NANO;
+ }
+
default:
return -EOPNOTSUPP;
}
@@ -647,6 +689,12 @@ static int ads1262_read_avail(struct iio_dev *indio_dev,
*length = ARRAY_SIZE(ads1262_pga_gain_avail);
return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_CONVDELAY:
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *vals = (const int *)ads1262_conv_delay_avail;
+ *length = ARRAY_SIZE(ads1262_conv_delay_avail) * 2;
+ return IIO_AVAIL_LIST;
+
default:
return -EOPNOTSUPP;
}
@@ -685,6 +733,17 @@ static int ads1262_write_raw(struct iio_dev *indio_dev,
break;
}
+ case IIO_CHAN_INFO_CONVDELAY: {
+ i = ads1262_find_two(ads1262_conv_delay_avail, val, val2);
+ if (i < 0)
+ return i;
+
+ guard(mutex)(&st->chan_lock);
+ chan_data->delay = i;
+
+ break;
+ }
+
default:
return -EOPNOTSUPP;
}
@@ -807,8 +866,10 @@ static int ads1262_alloc_channels(struct ads1262 *st,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_HARDWAREGAIN) |
+ BIT(IIO_CHAN_INFO_CONVDELAY) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_shared_by_type_available =
+ BIT(IIO_CHAN_INFO_CONVDELAY) |
BIT(IIO_CHAN_INFO_HARDWAREGAIN) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.indexed = true,
--
2.54.0
^ permalink raw reply related
* [PATCH v2 6/7] iio: adc: ti-ads1262: Add buffer and trigger support
From: Kurt Borja @ 2026-06-28 5:36 UTC (permalink / raw)
To: Kurt Borja, Jonathan Cameron, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, David Lechner
Cc: Nuno Sá, Andy Shevchenko, linux-iio, devicetree,
linux-kernel, Jonathan Cameron
In-Reply-To: <20260628-ads126x-v2-0-4b1b231325ba@gmail.com>
Add triggered buffer support and a data-ready (DRDY) hardware trigger.
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
drivers/iio/adc/ti-ads1262.c | 265 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 265 insertions(+)
diff --git a/drivers/iio/adc/ti-ads1262.c b/drivers/iio/adc/ti-ads1262.c
index 4ae22c1b0b4b7d79..53bc70e0c35a59da 100644
--- a/drivers/iio/adc/ti-ads1262.c
+++ b/drivers/iio/adc/ti-ads1262.c
@@ -38,6 +38,9 @@
#include <asm/byteorder.h>
#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
#define ADS1262_OPCODE_NOP 0x00
#define ADS1262_OPCODE_RESET 0x06
@@ -258,6 +261,7 @@ struct ads1262 {
const struct ads1262_chip_info *info;
struct regmap *regmap;
struct iio_dev *indio_dev;
+ struct iio_trigger *trig;
struct gpio_desc *reset_gpiod;
struct gpio_desc *start_gpiod;
@@ -273,6 +277,11 @@ struct ads1262 {
/* Protects transfer buffers and concurrent SPI transfers */
struct mutex xfer_lock;
+ struct spi_message msg;
+ struct spi_transfer xfer[2];
+
+ IIO_DECLARE_BUFFER_WITH_TS(__be32, scan_buffer,
+ ADS1262_MAX_CHANNEL_COUNT);
u8 tx[ADS1262_XFER_BUFFER_SZ] __aligned(IIO_DMA_MINALIGN);
u8 rx[ADS1262_XFER_BUFFER_SZ] __aligned(IIO_DMA_MINALIGN);
@@ -781,10 +790,250 @@ static const struct iio_info ads1262_iio_info = {
.debugfs_reg_access = ads1262_debugfs_reg_access,
};
+static int ads1262_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct ads1262 *st = iio_priv(indio_dev);
+ unsigned int weight;
+ unsigned long i;
+ int ret;
+
+ weight = bitmap_weight(indio_dev->active_scan_mask,
+ iio_get_masklength(indio_dev));
+ if (weight == 1) {
+ /*
+ * A single channel is read by command (RDATA1), so one transfer
+ * holds the command byte plus the 4 conversion bytes, which end
+ * up at offset 1 of the rx buffer.
+ */
+ st->xfer[0].len = 5;
+ st->xfer[0].tx_buf = st->tx;
+ st->xfer[0].rx_buf = st->rx;
+ st->xfer[0].cs_change = 0;
+ spi_message_init_with_transfers(&st->msg, st->xfer, 1);
+
+ i = find_first_bit(indio_dev->active_scan_mask,
+ iio_get_masklength(indio_dev));
+ ret = ads1262_channel_enable(st, &st->channels[i]);
+ if (ret)
+ return ret;
+ } else {
+ /*
+ * Multiple channels use software sequencing: each transfer
+ * rewrites the per-channel configuration registers while
+ * returning the conversion of the previously enabled channel,
+ * found at offset 0 of the rx buffer. The registers are not
+ * contiguous, so the write is split in two bulk steps.
+ *
+ * First step: write protocol (2 bytes) + MODE0, MODE1, MODE2,
+ * INPMUX (4 registers).
+ */
+ st->xfer[0].len = 6;
+ st->xfer[0].tx_buf = st->tx;
+ st->xfer[0].rx_buf = st->rx;
+ st->xfer[0].cs_change = 1;
+ /*
+ * Second step: write protocol (2 bytes) + IDACMUX, IDACMAG,
+ * REFMUX (3 registers).
+ */
+ st->xfer[1].len = 5;
+ st->xfer[1].tx_buf = st->tx + 6;
+ st->xfer[1].rx_buf = st->rx + 6;
+ spi_message_init_with_transfers(&st->msg, st->xfer, 2);
+
+ regcache_drop_region(st->regmap, ADS1262_MODE0_REG,
+ ADS1262_INPMUX_REG);
+ regcache_drop_region(st->regmap, ADS1262_IDACMUX_REG,
+ ADS1262_REFMUX_REG);
+ }
+
+ ret = ads1262_set_runmode(st, ADS1262_RUNMODE_CONTINUOUS);
+ if (ret)
+ return ret;
+
+ ret = spi_optimize_message(st->spi, &st->msg);
+ if (ret)
+ return ret;
+
+ ret = ads1262_dev_start(st);
+ if (ret) {
+ spi_unoptimize_message(&st->msg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ads1262_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct ads1262 *st = iio_priv(indio_dev);
+
+ ads1262_dev_stop(st);
+ spi_unoptimize_message(&st->msg);
+
+ return 0;
+}
+
+static bool ads1262_validate_scan_mask(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct ads1262 *st = iio_priv(indio_dev);
+ struct device *dev = &st->spi->dev;
+
+ if (iio_trigger_using_own(indio_dev)) {
+ dev_err_once(dev, "The %s trigger only supports one active channel\n",
+ st->trig->name);
+ return iio_validate_scan_mask_onehot(indio_dev, scan_mask);
+ }
+
+ return true;
+}
+
+static const struct iio_buffer_setup_ops ads1262_buffer_ops = {
+ .preenable = ads1262_buffer_preenable,
+ .postdisable = ads1262_buffer_postdisable,
+ .validate_scan_mask = ads1262_validate_scan_mask,
+};
+
+static int ads1262_enable_and_read_last(struct ads1262 *st,
+ const struct ads1262_channel *chan,
+ __be32 *val)
+{
+ int ret;
+
+ lockdep_assert_held(&st->xfer_lock);
+
+ if (chan) {
+ guard(mutex)(&st->chan_lock);
+
+ st->tx[0] = ADS1262_MODE0_REG | ADS1262_OPCODE_WREG;
+ st->tx[1] = ADS1262_INPMUX_REG - ADS1262_MODE0_REG;
+ st->tx[2] = FIELD_PREP(ADS1262_MODE0_DELAY_MASK, chan->delay) |
+ FIELD_PREP(ADS1262_MODE0_INPUT_CHOP_MASK, chan->input_chop) |
+ FIELD_PREP(ADS1262_MODE0_IDAC_CHOP_MASK, chan->idac_chop) |
+ FIELD_PREP(ADS1262_MODE0_RUNMODE_MASK, ADS1262_RUNMODE_CONTINUOUS) |
+ FIELD_PREP(ADS1262_MODE0_REFREV_MASK, chan->ref_reversal);
+ st->tx[3] = FIELD_PREP(ADS1262_MODE1_FILTER_MASK, chan->filter);
+ st->tx[4] = FIELD_PREP(ADS1262_MODE2_DR_MASK, chan->data_rate) |
+ FIELD_PREP(ADS1262_MODE2_GAIN_MASK, chan->gain) |
+ FIELD_PREP(ADS1262_MODE2_BYPASS_MASK, chan->pga_bypass);
+ st->tx[5] = FIELD_PREP(ADS1262_INPMUX_MUXP_MASK, chan->input[0]) |
+ FIELD_PREP(ADS1262_INPMUX_MUXN_MASK, chan->input[1]);
+
+ st->tx[6] = ADS1262_IDACMUX_REG | ADS1262_OPCODE_WREG;
+ st->tx[7] = ADS1262_REFMUX_REG - ADS1262_IDACMUX_REG;
+ st->tx[8] = FIELD_PREP(ADS1262_IDACMUX_MUX1_MASK, chan->idac_mux[0]) |
+ FIELD_PREP(ADS1262_IDACMUX_MUX2_MASK, chan->idac_mux[1]);
+ st->tx[9] = FIELD_PREP(ADS1262_IDACMAG_MAG1_MASK, chan->idac_mag[0]) |
+ FIELD_PREP(ADS1262_IDACMAG_MAG2_MASK, chan->idac_mag[1]);
+ st->tx[10] = FIELD_PREP(ADS1262_REFMUX_RMUXP_MASK, chan->reference[0]) |
+ FIELD_PREP(ADS1262_REFMUX_RMUXN_MASK, chan->reference[1]);
+ } else {
+ memset(st->tx, 0, sizeof(st->tx));
+ }
+
+ ret = spi_sync(st->spi, &st->msg);
+ if (ret)
+ return ret;
+
+ memcpy(val, st->rx, sizeof(*val));
+
+ return 0;
+}
+
+static int ads1262_fill_buffer_mult(struct ads1262 *st)
+{
+ unsigned int chan;
+ __be32 val;
+ int i = -1;
+ int ret;
+
+ /*
+ * This routine enables and reads channels in a full-duplex fashion.
+ *
+ * When a channel is enabled, the previous conversion is clocked out of
+ * the shift data register on the same transfer (Section 9.4.7.1). This
+ * allows for low latency software sequencing but forbids any
+ * communication with the chip in-between or data corruption may occur,
+ * hence the need to take the xfer_lock for the whole operation.
+ */
+ guard(mutex)(&st->xfer_lock);
+
+ iio_for_each_active_channel(st->indio_dev, chan) {
+ ret = ads1262_enable_and_read_last(st, &st->channels[chan], &val);
+ if (ret)
+ return ret;
+
+ reinit_completion(&st->drdy);
+
+ if (i > -1)
+ st->scan_buffer[i] = val;
+ i++;
+
+ ads1262_wait_for_conversion(st);
+ }
+
+ return ads1262_enable_and_read_last(st, NULL, &st->scan_buffer[i]);
+}
+
+static int ads1262_fill_buffer_one(struct ads1262 *st)
+{
+ int ret;
+
+ guard(mutex)(&st->xfer_lock);
+
+ /*
+ * When only one channel is enabled, we can't really avoid SPI activity
+ * from happening when the auxiliary ADC is in use, thus we have to read
+ * from the data-holding register (command mode).
+ */
+ st->tx[0] = ADS1262_OPCODE_RDATA1;
+ ret = spi_sync(st->spi, &st->msg);
+ if (ret)
+ return ret;
+
+ /* In command mode the conversion data is found at offset 1 */
+ memcpy(st->scan_buffer, &st->rx[1], sizeof(*st->scan_buffer));
+
+ return 0;
+}
+
+static irqreturn_t ads1262_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ads1262 *st = iio_priv(indio_dev);
+ s64 ts = pf->timestamp;
+ unsigned int weight;
+ int ret;
+
+ weight = bitmap_weight(indio_dev->active_scan_mask,
+ iio_get_masklength(indio_dev));
+
+ memset(st->scan_buffer, 0, sizeof(st->scan_buffer));
+
+ if (weight == 1)
+ ret = ads1262_fill_buffer_one(st);
+ else
+ ret = ads1262_fill_buffer_mult(st);
+ if (ret)
+ goto out_notify_done;
+
+ iio_push_to_buffers_with_ts(indio_dev, st->scan_buffer,
+ sizeof(st->scan_buffer), ts);
+
+out_notify_done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t ads1262_irq_handler(int irq, void *dev_id)
{
struct ads1262 *st = dev_id;
+ if (iio_buffer_enabled(st->indio_dev))
+ iio_trigger_poll(st->trig);
+
complete(&st->drdy);
return IRQ_HANDLED;
@@ -1355,7 +1604,23 @@ static int ads1262_spi_probe(struct spi_device *spi)
indio_dev->channels = channels;
indio_dev->num_channels = num_channels;
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ads1262_trigger_handler,
+ &ads1262_buffer_ops);
+ if (ret)
+ return ret;
+
if (spi->irq > 0) {
+ st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d-drdy", info->name,
+ iio_device_id(indio_dev));
+ if (!st->trig)
+ return -ENOMEM;
+ iio_trigger_set_drvdata(st->trig, st);
+ ret = devm_iio_trigger_register(dev, st->trig);
+ if (ret)
+ return ret;
+
ret = devm_request_irq(dev, spi->irq, ads1262_irq_handler,
IRQF_NO_THREAD, info->name, st);
if (ret)
--
2.54.0
^ permalink raw reply related
* [PATCH v2 7/7] iio: adc: Add ti-ads1263-adc2 driver
From: Kurt Borja @ 2026-06-28 5:36 UTC (permalink / raw)
To: Kurt Borja, Jonathan Cameron, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, David Lechner
Cc: Nuno Sá, Andy Shevchenko, linux-iio, devicetree,
linux-kernel, Jonathan Cameron
In-Reply-To: <20260628-ads126x-v2-0-4b1b231325ba@gmail.com>
The TI ADS1263 embeds a second 24-bit delta-sigma ADC (ADC2) with its
own input mux, reference, gain and sample-rate selection.
Model ADC2 as a separate IIO device on the auxiliary bus: the ti-ads1262
SPI driver instantiates the auxiliary device and exports a small set of
TI_ADS1262-namespaced helpers for the conversion and register accesses
that must go through the shared bus. ADC2 channels are derived from the
parent's configured channels.
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
MAINTAINERS | 2 +
drivers/iio/adc/Kconfig | 14 ++
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/ti-ads1262.c | 168 ++++++++++++++++-
drivers/iio/adc/ti-ads1262.h | 39 ++++
drivers/iio/adc/ti-ads1263-adc2.c | 379 ++++++++++++++++++++++++++++++++++++++
6 files changed, 602 insertions(+), 1 deletion(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index d868b25f2c65bcd9..342c661f079bcf39 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -26929,6 +26929,8 @@ L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/adc/ti,ads1262.yaml
F: drivers/iio/adc/ti-ads1262.c
+F: drivers/iio/adc/ti-ads1262.h
+F: drivers/iio/adc/ti-ads1263-adc2.c
TI ADS7924 ADC DRIVER
M: Hugo Villeneuve <hvilleneuve@dimonoff.com>
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 6051092c20b96731..ab2e8e45f3b442d6 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -1817,6 +1817,7 @@ config TI_ADS1262
select REGMAP
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
+ select AUXILIARY_BUS
help
If you say yes here you get support for Texas Instruments ADS1262 and
ADS1263 ADC chips.
@@ -1824,6 +1825,19 @@ config TI_ADS1262
This driver can also be built as a module. If so, the module will be
called ti-ads1262.
+config TI_ADS1263_ADC2
+ tristate "Texas Instruments ADS1263 auxiliary ADC (ADC2) driver"
+ depends on TI_ADS1262
+ select AUXILIARY_BUS
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ If you say yes here you get support for Texas Instruments ADS1263
+ auxiliary ADC (ADC2).
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads1263-adc2.
+
config TI_ADS1298
tristate "Texas Instruments ADS1298"
depends on SPI
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 4b1f89a2317a35f7..4215f56f525349a5 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -156,6 +156,7 @@ obj-$(CONFIG_TI_ADS1119) += ti-ads1119.o
obj-$(CONFIG_TI_ADS112C14) += ti-ads112c14.o
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
obj-$(CONFIG_TI_ADS1262) += ti-ads1262.o
+obj-$(CONFIG_TI_ADS1263_ADC2) += ti-ads1263-adc2.o
obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o
obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o
obj-$(CONFIG_TI_ADS131M02) += ti-ads131m02.o
diff --git a/drivers/iio/adc/ti-ads1262.c b/drivers/iio/adc/ti-ads1262.c
index 53bc70e0c35a59da..e3acc2eb9042c40a 100644
--- a/drivers/iio/adc/ti-ads1262.c
+++ b/drivers/iio/adc/ti-ads1262.c
@@ -14,10 +14,10 @@
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/compiler_attributes.h>
-#include <linux/compiler_types.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
+#include <linux/idr.h>
#include <linux/interrupt.h>
#include <linux/lockdep.h>
#include <linux/math.h>
@@ -25,6 +25,7 @@
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
+#include <linux/of.h>
#include <linux/overflow.h>
#include <linux/property.h>
#include <linux/regmap.h>
@@ -42,6 +43,8 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
+#include "ti-ads1262.h"
+
#define ADS1262_OPCODE_NOP 0x00
#define ADS1262_OPCODE_RESET 0x06
#define ADS1262_OPCODE_START1 0x08
@@ -144,6 +147,8 @@
#define ADS1262_MAX_CHANNEL_COUNT 16
#define ADS1262_XFER_BUFFER_SZ 11
+static DEFINE_IDA(ads1262_ida);
+
enum {
ADS1262_DELAY_NO_DELAY,
ADS1262_DELAY_8700_NS,
@@ -1039,6 +1044,161 @@ static irqreturn_t ads1262_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
+int ads1263_adc2_channel_get_scale(struct ads1263_adc2_ctx *ctx, u8 realbits,
+ u8 gain, u8 ref_source, int *val, int *val2)
+{
+ return ads1262_calculate_scale(ctx->chip, realbits, gain, ref_source, ref_source,
+ val, val2);
+}
+EXPORT_SYMBOL_NS_GPL(ads1263_adc2_channel_get_scale, "TI_ADS1262");
+
+int ads1263_adc2_channel_enable(struct ads1263_adc2_ctx *ctx,
+ const struct ads1263_adc2_channel *chan)
+{
+ struct ads1262 *st = ctx->chip;
+ u8 val;
+ int ret;
+
+ guard(mutex)(&ctx->chan_lock);
+
+ val = FIELD_PREP(ADS1262_ADC2CFG_GAIN2_MASK, chan->gain) |
+ FIELD_PREP(ADS1262_ADC2CFG_REF2_MASK, chan->reference) |
+ FIELD_PREP(ADS1262_ADC2CFG_DR2_MASK, chan->data_rate);
+ ret = regmap_update_bits(st->regmap, ADS1262_ADC2CFG_REG,
+ ADS1262_ADC2CFG_GAIN2_MASK |
+ ADS1262_ADC2CFG_REF2_MASK |
+ ADS1262_ADC2CFG_DR2_MASK, val);
+
+ val = FIELD_PREP(ADS1262_ADC2MUX_MUXP2_MASK, chan->input[0]) |
+ FIELD_PREP(ADS1262_ADC2MUX_MUXN2_MASK, chan->input[1]);
+ return regmap_update_bits(st->regmap, ADS1262_ADC2MUX_REG,
+ ADS1262_ADC2MUX_MUXP2_MASK |
+ ADS1262_ADC2MUX_MUXN2_MASK, val);
+}
+EXPORT_SYMBOL_NS_GPL(ads1263_adc2_channel_enable, "TI_ADS1262");
+
+int ads1263_adc2_start(struct ads1263_adc2_ctx *ctx)
+{
+ struct ads1262 *st = ctx->chip;
+
+ return ads1262_dev_cmd(st, ADS1262_OPCODE_START2);
+}
+EXPORT_SYMBOL_NS_GPL(ads1263_adc2_start, "TI_ADS1262");
+
+int ads1263_adc2_stop(struct ads1263_adc2_ctx *ctx)
+{
+ struct ads1262 *st = ctx->chip;
+
+ return ads1262_dev_cmd(st, ADS1262_OPCODE_STOP2);
+}
+EXPORT_SYMBOL_NS_GPL(ads1263_adc2_stop, "TI_ADS1262");
+
+int ads1263_adc2_read(struct ads1263_adc2_ctx *ctx, __be32 *val)
+{
+ struct ads1262 *st = ctx->chip;
+
+ return ads1262_dev_read_by_cmd(st, ADS1262_OPCODE_RDATA2, val);
+}
+EXPORT_SYMBOL_NS_GPL(ads1263_adc2_read, "TI_ADS1262");
+
+static void ads1262_aux_device_destroy(void *data)
+{
+ struct auxiliary_device *adev = data;
+
+ auxiliary_device_delete(adev);
+ auxiliary_device_uninit(adev);
+}
+
+static void ads1262_aux_device_release(struct device *dev)
+{
+ struct auxiliary_device *adev = to_auxiliary_dev(dev);
+ struct ads1263_adc2_ctx *ctx =
+ container_of(adev, struct ads1263_adc2_ctx, adev);
+ struct device_node *node = adev->dev.of_node;
+
+ of_node_put(node);
+ mutex_destroy(&ctx->chan_lock);
+ kfree(ctx->channels);
+ ida_free(&ads1262_ida, adev->id);
+ kfree(ctx);
+}
+
+static int ads1262_aux_device_setup(struct ads1262 *st)
+{
+ struct device *parent = &st->spi->dev;
+ struct ads1263_adc2_channel *chans;
+ struct ads1262_channel *chan_data;
+ struct auxiliary_device *adev;
+ struct ads1263_adc2_ctx *ctx;
+ struct device_link *link;
+ int id, ret;
+
+ ctx = kzalloc_obj(*ctx);
+ if (!ctx)
+ return -ENOMEM;
+
+ id = ida_alloc(&ads1262_ida, GFP_KERNEL);
+ if (id < 0) {
+ ret = id;
+ goto out_free_adc2;
+ }
+
+ chans = kcalloc(st->num_channels, sizeof(*chans), GFP_KERNEL);
+ if (!chans) {
+ ret = -ENOMEM;
+ goto out_free_id;
+ }
+
+ for (unsigned int i = 0; i < st->num_channels; i++) {
+ chan_data = &st->channels[i];
+ chans[i].input[0] = chan_data->input[0];
+ chans[i].input[1] = chan_data->input[1];
+ chans[i].reference = chan_data->reference[2];
+ }
+
+ ctx->chip = st;
+ ctx->num_channels = st->num_channels;
+ ctx->channels = chans;
+ mutex_init(&ctx->chan_lock);
+
+ adev = &ctx->adev;
+ adev->name = "ads1263_adc2";
+ adev->id = id;
+ adev->dev.release = ads1262_aux_device_release;
+ adev->dev.parent = parent;
+ device_set_of_node_from_dev(&adev->dev, parent);
+
+ ret = auxiliary_device_init(adev);
+ if (ret)
+ goto out_free_res;
+
+ link = device_link_add(&adev->dev, parent, DL_FLAG_AUTOPROBE_CONSUMER);
+ if (!link) {
+ auxiliary_device_uninit(adev);
+ return dev_err_probe(parent, -ENXIO,
+ "Failed to add link to auxiliary device\n");
+ }
+
+ ret = auxiliary_device_add(adev);
+ if (ret) {
+ auxiliary_device_uninit(adev);
+ return ret;
+ }
+
+ return devm_add_action_or_reset(parent, ads1262_aux_device_destroy, adev);
+
+out_free_res:
+ of_node_put(adev->dev.of_node);
+ mutex_destroy(&ctx->chan_lock);
+ kfree(chans);
+out_free_id:
+ ida_free(&ads1262_ida, id);
+out_free_adc2:
+ kfree(ctx);
+
+ return ret;
+}
+
static int ads1262_get_filter_type(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
@@ -1627,6 +1787,12 @@ static int ads1262_spi_probe(struct spi_device *spi)
return ret;
}
+ if (info->has_aux_adc) {
+ ret = ads1262_aux_device_setup(st);
+ if (ret)
+ return ret;
+ }
+
return devm_iio_device_register(dev, indio_dev);
}
diff --git a/drivers/iio/adc/ti-ads1262.h b/drivers/iio/adc/ti-ads1262.h
new file mode 100644
index 0000000000000000..7a94ffd1fa983f9f
--- /dev/null
+++ b/drivers/iio/adc/ti-ads1262.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Texas Instruments ADS1262 ADC driver
+ *
+ * Copyright (C) 2025 Kurt Borja <kuurtb@gmail.com>
+ */
+
+#ifndef _ADS1262_H_
+#define _ADS1262_H_
+
+#include <linux/auxiliary_bus.h>
+#include <linux/types.h>
+
+struct ads1263_adc2_channel {
+ u8 gain;
+ u8 reference;
+ u8 data_rate;
+ u8 input[2];
+};
+
+struct ads1263_adc2_ctx {
+ struct auxiliary_device adev;
+ struct ads1262 *chip;
+
+ /* Protects channel state */
+ struct mutex chan_lock;
+ struct ads1263_adc2_channel *channels;
+ unsigned int num_channels;
+};
+
+int ads1263_adc2_channel_get_scale(struct ads1263_adc2_ctx *ctx, u8 realbits,
+ u8 gain, u8 ref_source, int *val, int *val2);
+int ads1263_adc2_channel_enable(struct ads1263_adc2_ctx *ctx,
+ const struct ads1263_adc2_channel *chan);
+int ads1263_adc2_start(struct ads1263_adc2_ctx *ctx);
+int ads1263_adc2_stop(struct ads1263_adc2_ctx *ctx);
+int ads1263_adc2_read(struct ads1263_adc2_ctx *ctx, __be32 *val);
+
+#endif
diff --git a/drivers/iio/adc/ti-ads1263-adc2.c b/drivers/iio/adc/ti-ads1263-adc2.c
new file mode 100644
index 0000000000000000..385531d96de11269
--- /dev/null
+++ b/drivers/iio/adc/ti-ads1263-adc2.c
@@ -0,0 +1,379 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Texas Instruments ADS1263 auxiliary ADC (ADC2) driver
+ *
+ * Copyright (C) 2025 Kurt Borja <kuurtb@gmail.com>
+ */
+
+#include <linux/align.h>
+#include <linux/array_size.h>
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/unaligned.h>
+#include <linux/units.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include "ti-ads1262.h"
+
+/* ADC2CFG REF2 constants */
+#define ADS1263_ADC2_REF2_INTER 0
+#define ADS1263_ADC2_REF2_COUNT 5
+
+struct ads1263_adc2 {
+ struct iio_dev *indio_dev;
+ struct ads1263_adc2_ctx *ctx;
+};
+
+static const int ads1263_adc2_gain_avail[] = {
+ 1, 2, 4, 8, 16, 32, 64, 128
+};
+
+static const int ads1263_adc2_data_rate_avail[] = {
+ 10, 100, 400, 800
+};
+
+static const unsigned long ads1263_adc2_latency_us[] = {
+ 121000, 31200, 8710, 4970
+};
+
+static int ads1263_adc2_channel_read(struct iio_dev *indio_dev,
+ struct ads1263_adc2_channel *chan_data,
+ __be32 *val)
+{
+ struct ads1263_adc2 *st = iio_priv(indio_dev);
+ struct ads1263_adc2_ctx *ctx = st->ctx;
+ int ret;
+
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ ret = ads1263_adc2_channel_enable(ctx, chan_data);
+ if (ret)
+ return ret;
+
+ ret = ads1263_adc2_start(ctx);
+ if (ret)
+ return ret;
+
+ ret = ads1263_adc2_stop(ctx);
+ if (ret)
+ return ret;
+
+ fsleep(ads1263_adc2_latency_us[chan_data->data_rate]);
+
+ return ads1263_adc2_read(ctx, val);
+}
+
+static int ads1263_adc2_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ads1263_adc2 *st = iio_priv(indio_dev);
+ struct ads1263_adc2_ctx *ctx = st->ctx;
+ struct ads1263_adc2_channel *chan_data = &ctx->channels[chan->scan_index];
+ u8 realbits = chan->scan_type.realbits;
+ __be32 raw;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ads1263_adc2_channel_read(indio_dev, chan_data, &raw);
+ if (ret)
+ return ret;
+
+ *val = sign_extend32(get_unaligned_be24(&raw), realbits - 1);
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE: {
+ guard(mutex)(&ctx->chan_lock);
+
+ ret = ads1263_adc2_channel_get_scale(ctx, realbits, chan_data->gain,
+ chan_data->reference, val, val2);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT_PLUS_NANO;
+ }
+
+ case IIO_CHAN_INFO_HARDWAREGAIN: {
+ guard(mutex)(&ctx->chan_lock);
+
+ *val = ads1263_adc2_gain_avail[chan_data->gain];
+
+ return IIO_VAL_INT;
+ }
+
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ guard(mutex)(&ctx->chan_lock);
+
+ *val = ads1263_adc2_data_rate_avail[chan_data->data_rate];
+
+ return IIO_VAL_INT;
+ }
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ads1263_adc2_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type,
+ int *length, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ *type = IIO_VAL_INT;
+ *vals = ads1263_adc2_gain_avail;
+ *length = ARRAY_SIZE(ads1263_adc2_gain_avail);
+ return IIO_AVAIL_LIST;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *type = IIO_VAL_INT;
+ *vals = ads1263_adc2_data_rate_avail;
+ *length = ARRAY_SIZE(ads1263_adc2_data_rate_avail);
+ return IIO_AVAIL_LIST;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ads1263_adc2_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ads1263_adc2 *st = iio_priv(indio_dev);
+ struct ads1263_adc2_ctx *ctx = st->ctx;
+ struct ads1263_adc2_channel *chan_data = &ctx->channels[chan->scan_index];
+ unsigned int i;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN: {
+ for (i = 0; i < ARRAY_SIZE(ads1263_adc2_gain_avail); i++) {
+ if (val == ads1263_adc2_gain_avail[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(ads1263_adc2_gain_avail))
+ return -EINVAL;
+
+ guard(mutex)(&ctx->chan_lock);
+ chan_data->gain = i;
+
+ break;
+ }
+
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ for (i = 0; i < ARRAY_SIZE(ads1263_adc2_data_rate_avail); i++) {
+ if (val == ads1263_adc2_data_rate_avail[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(ads1263_adc2_data_rate_avail))
+ return -EINVAL;
+
+ guard(mutex)(&ctx->chan_lock);
+ chan_data->data_rate = i;
+
+ break;
+ }
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int ads1263_adc2_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_CONVDELAY:
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+}
+
+static const struct iio_info ads1263_adc2_iio_info = {
+ .read_raw = ads1263_adc2_read_raw,
+ .read_avail = ads1263_adc2_read_avail,
+ .write_raw = ads1263_adc2_write_raw,
+ .write_raw_get_fmt = ads1263_adc2_write_raw_get_fmt,
+};
+
+static int ads1263_adc2_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct ads1263_adc2 *st = iio_priv(indio_dev);
+ struct ads1263_adc2_ctx *ctx = st->ctx;
+ unsigned long i;
+ int ret;
+
+ i = find_first_bit(indio_dev->active_scan_mask,
+ iio_get_masklength(indio_dev));
+ ret = ads1263_adc2_channel_enable(ctx, &ctx->channels[i]);
+ if (ret)
+ return ret;
+
+ return ads1263_adc2_start(ctx);
+}
+
+static int ads1263_adc2_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct ads1263_adc2 *st = iio_priv(indio_dev);
+ struct ads1263_adc2_ctx *ctx = st->ctx;
+
+ ads1263_adc2_stop(ctx);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ads1263_adc2_buffer_ops = {
+ .preenable = ads1263_adc2_buffer_preenable,
+ .postdisable = ads1263_adc2_buffer_postdisable,
+ .validate_scan_mask = iio_validate_scan_mask_onehot,
+};
+
+static irqreturn_t ads1263_adc2_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ads1263_adc2 *st = iio_priv(indio_dev);
+ struct ads1263_adc2_ctx *ctx = st->ctx;
+ struct {
+ __be32 conv;
+ aligned_s64 ts;
+ } scan = {};
+ int ret;
+
+ ret = ads1263_adc2_read(ctx, &scan.conv);
+ if (ret)
+ goto out_notify_done;
+
+ iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan),
+ pf->timestamp);
+
+out_notify_done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ads1263_adc2_channels_setup(struct iio_dev *indio_dev)
+{
+ struct ads1263_adc2 *st = iio_priv(indio_dev);
+ struct device *dev = &st->ctx->adev.dev;
+ struct ads1263_adc2_ctx *ctx = st->ctx;
+ struct ads1263_adc2_channel *chan_data;
+ struct iio_chan_spec *chns;
+ unsigned int i;
+
+ /* Account for the timestamp channel */
+ chns = devm_kcalloc(dev, ctx->num_channels + 1, sizeof(*chns),
+ GFP_KERNEL);
+ if (!chns)
+ return -ENOMEM;
+
+ for (i = 0; i < ctx->num_channels; i++) {
+ guard(mutex)(&ctx->chan_lock);
+
+ chan_data = &ctx->channels[i];
+ chns[i] = (struct iio_chan_spec) {
+ .type = IIO_VOLTAGE,
+ .channel = chan_data->input[0],
+ .channel2 = chan_data->input[1],
+ .scan_index = i,
+ .scan_type = {
+ .format = IIO_SCAN_FORMAT_SIGNED_INT,
+ .realbits = 24,
+ .storagebits = 32,
+ .shift = 8,
+ .endianness = IIO_BE,
+ },
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_type_available =
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .indexed = true,
+ .differential = true,
+ };
+ }
+
+ chns[i] = IIO_CHAN_SOFT_TIMESTAMP(i);
+
+ indio_dev->num_channels = ctx->num_channels + 1;
+ indio_dev->channels = chns;
+
+ return 0;
+}
+
+static int ads1263_adc2_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *id)
+{
+ struct ads1263_adc2_ctx *ctx =
+ container_of(auxdev, struct ads1263_adc2_ctx, adev);
+ struct device *dev = &auxdev->dev;
+ struct iio_dev *indio_dev;
+ struct ads1263_adc2 *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->ctx = ctx;
+ st->indio_dev = indio_dev;
+
+ indio_dev->name = "ads1263_adc2";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ads1263_adc2_iio_info;
+ ret = ads1263_adc2_channels_setup(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ads1263_adc2_trigger_handler,
+ &ads1263_adc2_buffer_ops);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct auxiliary_device_id ads1263_adc2_auxiliary_match[] = {
+ { .name = "ti_ads1262.ads1263_adc2" },
+ { }
+};
+MODULE_DEVICE_TABLE(auxiliary, ads1263_adc2_auxiliary_match);
+
+static struct auxiliary_driver ads1263_adc2_driver = {
+ .name = "ads1263_adc2",
+ .probe = ads1263_adc2_probe,
+ .id_table = ads1263_adc2_auxiliary_match,
+};
+module_auxiliary_driver(ads1263_adc2_driver);
+
+MODULE_IMPORT_NS("TI_ADS1262");
+MODULE_DESCRIPTION("Texas Instruments ADS1263 auxiliary ADC (ADC2) driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kurt Borja <kuurtb@gmail.com>");
--
2.54.0
^ permalink raw reply related
* Re: [PATCH] of/address: Drop ISA parts when !CONFIG_ISA
From: Daniel Palmer @ 2026-06-28 5:44 UTC (permalink / raw)
To: robh, saravanak; +Cc: devicetree, linux-kernel
In-Reply-To: <20260626171906.476688-1-daniel@thingy.jp>
Hi Daniel, (Replying to myself..)
On Sat, 27 Jun 2026 at 02:19, Daniel Palmer <daniel@thingy.jp> wrote:
>
> The PCI parts are already wrapped in #ifdef CONFIG_PCI
> so it seems sensible to add #ifdef CONFIG_ISA around the ISA
> parts.
>
> This reduces the code/data size a bit on configs with !CONFIG_ISA.
>
> Signed-off-by: Daniel Palmer <daniel@thingy.jp>
> ---
>
> Sorry for the spam, somehow I botched sending a patch..
>
> I thought about making this RFC as I'm a bit unsure if machines
> that need this ISA stuff actually select CONFIG_ISA or not.
So sashiko worked out what is going on here. A few machines have
things connected via an LPC bus that is derived from ISA and needs the
code to parse those nodes but those machines don't select CONFIG_ISA.
There seem to be ~15 devicetrees in total that actually need it (1 in
x86, 1 in arm64, a few in mips/loongson for loongson machines, and
then a few in ppc).
I made a series that adds CONFIG_OF_ISA to enable this code, selects
it in the places that needs it, and then does the original part of
disabling the code if not needed. I will wait a bit before sending.
The reason to do this is I am using devicetree to boot a machine with
4MB of RAM and a 7MHz CPU. Removing dead code/data like this helps.
Cheers!
^ permalink raw reply
* Re: [PATCH v2 1/3] dt-bindings: watchdog: npcm: add GCR syscon property
From: Tomer Maimon @ 2026-06-28 7:01 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: andrew, wim, linux, robh, krzk+dt, conor+dt, openbmc,
linux-watchdog, linux-doc, devicetree, linux-kernel, avifishman70,
tali.perry1, venture, yuenn, benjaminfair, corbet, skhan, joel
In-Reply-To: <20260623-ochre-spoonbill-of-security-a4bc42@quoll>
Hi Krzysztof,
On Tue, 23 Jun 2026 at 11:05, Krzysztof Kozlowski <krzk@kernel.org> wrote:
>
> On Mon, Jun 22, 2026 at 11:30:44AM +0300, Tomer Maimon wrote:
> > Describe syscon property that handles general control registers (GCR) in
> > Nuvoton BMC NPCM watchdog driver.
>
> Why? Well, you try to answer by saying something about driver, but we do
> not add bindings for drivers. Instead hardware should be the reason.
>
> Anyway, why is this needed now?
It is needed for accessing the reset indication registers. I would
mention it in the commit message.
>
> >
> > Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
> > ---
> > .../devicetree/bindings/watchdog/nuvoton,npcm750-wdt.yaml | 6 ++++++
> > 1 file changed, 6 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/watchdog/nuvoton,npcm750-wdt.yaml b/Documentation/devicetree/bindings/watchdog/nuvoton,npcm750-wdt.yaml
> > index 7aa30f5b5c49..4f00f099b2d2 100644
> > --- a/Documentation/devicetree/bindings/watchdog/nuvoton,npcm750-wdt.yaml
> > +++ b/Documentation/devicetree/bindings/watchdog/nuvoton,npcm750-wdt.yaml
> > @@ -40,6 +40,12 @@ properties:
> > clock-frequency:
> > description: Frequency in Hz of the clock that drives the NPCM timer.
> >
> > + nuvoton,sysgcr:
> > + $ref: /schemas/types.yaml#/definitions/phandle
> > + description:
> > + a phandle to access GCR registers on NPCM750 and NPCM845 watchdog
> > + instances.
>
> Here you write also for what purpose.
>
>
> Best regards,
> Krzysztof
>
Thanks,
Tomer
^ permalink raw reply
* Re: [PATCH v2 3/3] watchdog: npcm: add bootstatus support
From: Tomer Maimon @ 2026-06-28 7:15 UTC (permalink / raw)
To: sashiko-reviews; +Cc: devicetree, robh, conor+dt, linux-watchdog
In-Reply-To: <20260622084246.D01A91F00ADF@smtp.kernel.org>
Hi,
Thanks for the comments.
On Mon, 22 Jun 2026 at 11:42, <sashiko-bot@kernel.org> wrote:
>
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [High] The first watchdog instance on NPCM750 clears the global RESSR register without caching it, causing subsequent watchdog instances to permanently lose their reset status.
> - [High] Non-atomic read-modify-write of the shared INTCR2 register clobbers concurrent modifications and incorrectly handles W1C semantics.
> --
>
> commit dafe1267618ef0158376caf5f3acee9d7fe7b1b7
> Author: Tomer Maimon <tmaimon77@gmail.com>
>
> watchdog: npcm: add bootstatus support
>
> The NPCM750 uses RESSR and the NPCM845 uses INTCR2 to latch reset
> indications. Read those bits during probe and map them into watchdog
> bootstatus flags.
>
> > diff --git a/drivers/watchdog/npcm_wdt.c b/drivers/watchdog/npcm_wdt.c
> > index e62ea054bc618..98660419ec3f0 100644
> > --- a/drivers/watchdog/npcm_wdt.c
> > +++ b/drivers/watchdog/npcm_wdt.c
> [ ... ]
> > @@ -169,6 +250,92 @@ static bool npcm_is_running(struct watchdog_device *wdd)
> > return readl(wdt->reg) & NPCM_WTE;
> > }
> >
> > +static void npcm_get_reset_status(struct npcm_wdt *wdt, struct device *dev,
> > + const struct npcm_wdt_data *data,
> > + resource_size_t start)
> > +{
> [ ... ]
> > + if (of_device_is_compatible(dev->of_node, "nuvoton,npcm845-wdt")) {
> > + ret = regmap_read(gcr_regmap, NPCM_INTCR2_OFFSET, &rstval);
> > + if (ret) {
> > + dev_warn(dev, "Failed to read INTCR2 reset status: %d\n",
> > + ret);
> > + return;
> > + }
> > +
> > + if (rstval & NPCM8XX_RST) {
> > + ret = regmap_write(gcr_regmap, NPCM_INTCR2_OFFSET,
> > + rstval & ~NPCM8XX_RST);
>
> [Severity: High]
> Does this non-atomic read-modify-write risk clobbering concurrent modifications
> to the shared INTCR2 register?
No, the INTCR2 is accessed only by the WD driver, and reads occur only
during the WD probe.
>
> Since INTCR2 is a shared global control register accessed by multiple drivers,
> using regmap_read() followed by regmap_write() drops the regmap lock between
> operations. Any concurrent modifications to INTCR2 by other drivers could be
> overwritten.
>
> Also, if INTCR2 contains Write-1-to-Clear (W1C) status bits, writing
> `rstval & ~NPCM8XX_RST` might incorrectly write 1s to all other active bits
> (clearing them) and 0s to the watchdog bits.
the INTCR2 is not contains Write-1-to-Clear (W1C) status bits.
>
> Could regmap_update_bits() or a W1C-safe write be used here instead?
It is not needed here.
>
> > + if (ret) {
> > + dev_warn(dev,
> > + "Failed to clear INTCR2 reset status: %d\n",
> > + ret);
> > + return;
> > + }
> [ ... ]
> > + }
> > + } else if (of_device_is_compatible(dev->of_node, "nuvoton,npcm750-wdt")) {
> > + ret = regmap_read(gcr_regmap, NPCM_RESSR_OFFSET, &rstval);
> > + if (ret) {
> > + dev_warn(dev, "Failed to read RESSR reset status: %d\n",
> > + ret);
> > + return;
> > + }
> > +
> > + ret = regmap_write(gcr_regmap, NPCM_RESSR_OFFSET, rstval);
>
> [Severity: High]
> Will the first watchdog instance on NPCM750 clear the global RESSR register
> without caching it, causing subsequent instances to lose their reset status?
>
> The NPCM750 SoC has three watchdog instances that share the global RESSR
> register. When the first watchdog probes, it reads RESSR and immediately
> clears it by writing the value back. Because the value is not cached
> (unlike the SCRPAD10 mechanism used for NPCM845), when the remaining
> watchdogs probe, they will read 0 from RESSR and permanently lose their
> boot status indications.
Will be change in V3
>
> > + if (ret) {
> > + dev_warn(dev, "Failed to clear RESSR reset status: %d\n",
> > + ret);
> > + return;
> > + }
> > + }
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260622083046.3189603-1-tmaimon77@gmail.com?part=3
Thanks,
Tomer
^ permalink raw reply
* Re: [PATCH v3 0/4] Introduce HONOR MagicBook Art 14 Snapdragon device tree
From: Konstantin Shabanov @ 2026-06-28 8:20 UTC (permalink / raw)
To: konrad.dybcio
Cc: andersson, conor+dt, devicetree, konradybcio, krzk+dt,
linux-arm-msm, linux-kernel, mail, robh
In-Reply-To: <2aed327e-abf6-401a-a05b-ba3f4a5cd7f3@oss.qualcomm.com>
On Wed, 24 Jun 2026 14:10:10 +0200, Konrad Dybcio wrote:
>> +&iris {
>> + firmware-name = "qcom/x1e80100/HONOR/MRO-XXX/qcvss8380.mbn";
>
> Is that a model name, or a placeholder?
Yes, this is a model name (Intel variant of this laptop has MRA-XXX [1]).
[1]: https://linux-hardware.org/?view=computers&type=notebook&vendor=HONOR&model=MRA-XXX
^ permalink raw reply
* Re: [PATCH 19/19] MAINTAINERS: add Rambus CryptoManager Hub (CMH)
From: Krzysztof Kozlowski @ 2026-06-28 8:41 UTC (permalink / raw)
To: Krishnamoorthy, Saravanakrishnan
Cc: Albert Ou, Ousherovitch, Alex, Conor Dooley, David S. Miller,
Herbert Xu, Jonathan Corbet, Krzysztof Kozlowski, Palmer Dabbelt,
Paul Walmsley, Rob Herring, Shuah Khan, Alexandre Ghiti,
devicetree@vger.kernel.org, Wittenauer, Joel,
linux-api@vger.kernel.org, linux-crypto@vger.kernel.org,
linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-kselftest@vger.kernel.org, linux-riscv@lists.infradead.org,
Shuah Khan, SIPSupport, Nguyen, Thi
In-Reply-To: <SA1PR04MB985196991689AF3F3DCD349BC2EB2@SA1PR04MB9851.namprd04.prod.outlook.com>
On 26/06/2026 19:22, Krishnamoorthy, Saravanakrishnan wrote:
> Hi Krzysztof,
>
> Thanks for the review - all fair, and we'll fix them in v2:
>
> Drop L: sipsupport@rambus.com (keeping only linux-crypto).
> Drop the T: line - we don't maintain a tree; the driver will go through the crypto tree.
>
> Yes, Joel and Thi reviewed and acknowledged with the statement of oversight.
Do not top post, please.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH 3/5] arm64: defconfig: Enable ILI7807S DSI panel driver
From: Krzysztof Kozlowski @ 2026-06-28 8:44 UTC (permalink / raw)
To: Nabige Aala, Bjorn Andersson, Konrad Dybcio, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-arm-msm, devicetree, linux-kernel, arpit.saini, mohit.dsor
In-Reply-To: <20260627-shikra-dt-changes-v1-3-449a402673d0@oss.qualcomm.com>
On 27/06/2026 12:01, Nabige Aala wrote:
> From: Arpit Saini <arpit.saini@oss.qualcomm.com>
>
> Enable the ILI7807S 1080x1920 video-mode DSI panel driver as a module,
> used on the Shikra board.
So that's a v4, no changelog, tags ignored, comments not responded and
also ignored.
You got yourself one NAK, now second:
NAK
Address the comments before you send the next version.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH 5/5] arm64: dts: qcom: Shikra LT9611UXD support
From: Krzysztof Kozlowski @ 2026-06-28 8:45 UTC (permalink / raw)
To: Nabige Aala, Bjorn Andersson, Konrad Dybcio, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-arm-msm, devicetree, linux-kernel, arpit.saini, mohit.dsor
In-Reply-To: <20260627-shikra-dt-changes-v1-5-449a402673d0@oss.qualcomm.com>
On 27/06/2026 12:01, Nabige Aala wrote:
> From: Mohit Dsor <mohit.dsor@oss.qualcomm.com>
>
> Device tree changes to support lt9611uxd hdmi-dsi driver.
>
> Signed-off-by: Mohit Dsor <mohit.dsor@oss.qualcomm.com>
Incomplete DCO
...
> +
> vreg_wcn_3p3: regulator-wcn-3p3 {
> compatible = "regulator-fixed";
> regulator-name = "wcn_3p3";
> @@ -68,6 +98,78 @@ vreg_pmu_ch1: ldo4 {
> };
> };
>
> +&i2c4 {
> + status = "okay";
> +
> + lt9611uxd: lt9611uxd@41 {
No. Nice try. Instead of fixing broken driver (I reported this), you
send broken workaround which is in clear violations of coding style and
DT spec.
That's a poor way to upstream things. Just throwing the code over the wall.
Best regards,
Krzysztof
^ permalink raw reply
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