guowenxue
2024-07-18 b8d02950d8c50611c2784c7a40e0b3003acf8d49
kernel/patches/igkboard-imx8mp/linux-imx-lf-6.1.36-2.1.0.patch
@@ -10,12 +10,13 @@
+dtb-$(CONFIG_ARCH_MXC) += igkboard-imx8mp.dtb
diff --git a/arch/arm64/boot/dts/freescale/igkboard-imx8mp.dts b/arch/arm64/boot/dts/freescale/igkboard-imx8mp.dts
new file mode 100644
index 000000000..cddc94704
index 000000000..016d92023
--- /dev/null
+++ b/arch/arm64/boot/dts/freescale/igkboard-imx8mp.dts
@@ -0,0 +1,475 @@
@@ -0,0 +1,829 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+/*
+ * Device Tree Source for LingYun IGKBoard(IoT Gateway Kits Board) - imx8mp
+ * Copyright 2023 LingYun IoT System Studio.
+ */
+
@@ -31,16 +32,15 @@
+    model = "LingYun IoT Gateway Kits Board based on i.MX8MP";
+    compatible = "lingyun,igkboard-imx8mp", "fsl,imx8mp";
+
+    /* console and bootargs */
+    /* console */
+    chosen {
+        bootargs = "console=ttymxc1,115200 earlycon=ec_imx6q,0x30890000,115200";
+        stdout-path = &uart2;
+    };
+
+    /* MT53D512M32D2DS-053 WT:D, 2GB LPDDR4 */
+    memory@80000000 {
+        device_type = "memory";
+        reg = <0x0 0x80000000 0 0x80000000>;
+        reg = <0x0 0x80000000 0 0x40000000>;
+    };
+
+    leds {
@@ -53,7 +53,77 @@
+            label = "sysled";
+            gpios = <&gpio3 16 GPIO_ACTIVE_HIGH>;
+            default-state = "on";
+            linux,default-trigger = "heartbeat";
+        };
+
+        ledred {
+            label = "redled";
+            gpios = <&gpio3 21 GPIO_ACTIVE_LOW>;
+            default-state = "off";
+        };
+
+        ledgreen {
+            label = "greenled";
+            gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
+            default-state = "off";
+        };
+
+        ledblue {
+            label = "blueled";
+            gpios = <&gpio1 14 GPIO_ACTIVE_LOW>;
+            default-state = "on";
+            linux,default-trigger = "timer";
+        };
+    };
+
+    keys {
+        compatible = "gpio-keys";
+        pinctrl-names = "default";
+        pinctrl-0 = <&pinctrl_keys>;
+        status = "okay";
+
+        key1 {
+            label = "K1";
+            gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
+            linux,code = <BTN_1>;
+        };
+
+        key2 {
+            label = "K2";
+            gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;
+            linux,code = <BTN_2>;
+        };
+
+        key3 {
+            label = "K3";
+            gpios = <&gpio5 26 GPIO_ACTIVE_LOW>;
+            linux,code = <BTN_3>;
+        };
+
+        key4 {
+            label = "K4";
+            gpios = <&gpio5 27 GPIO_ACTIVE_LOW>;
+            linux,code = <BTN_4>;
+        };
+    };
+
+    sound-wm8960 {
+        compatible = "fsl,imx-audio-wm8960";
+        model = "wm8960-audio";
+        audio-cpu = <&sai3>;
+        audio-codec = <&codec>;
+        audio-asrc = <&easrc>;
+        //hp-det-gpio = <&gpio4 29 0>;
+        audio-routing =
+            "Headphone Jack", "HP_L",
+            "Headphone Jack", "HP_R",
+            "Ext Spk", "SPK_LP",
+            "Ext Spk", "SPK_LN",
+            "Ext Spk", "SPK_RP",
+            "Ext Spk", "SPK_RN",
+            "LINPUT1", "Mic Jack",
+            "LINPUT3", "Mic Jack",
+            "Mic Jack", "MICB";
+    };
+};
+
@@ -166,14 +236,13 @@
+    pinctrl-0 = <&pinctrl_eqos>;
+    phy-mode = "rgmii-id";
+    phy-handle = <&ethphy0>;
+    snps,reset-gpios = <&gpio1 6 GPIO_ACTIVE_LOW>;
+    snps,reset-delays-us = <100000 200000 150000>;
+    status = "okay";
+
+    mdio {
+        compatible = "snps,dwmac-mdio";
+        #address-cells = <1>;
+        #size-cells = <0>;
+        clock-frequency = <5000000>;
+
+        ethphy0: ethernet-phy@0 { /* YT8521SH-CA */
+            compatible = "ethernet-phy-ieee802.3-c22";
@@ -183,22 +252,19 @@
+    };
+};
+
+/* Second 1000Mbps Ethernet on ENET1 */
+/* Second 1000Mbps Ethernet on ENET1, test okay */
+&fec {
+    pinctrl-names = "default";
+    pinctrl-0 = <&pinctrl_fec>;
+    phy-mode = "rgmii-id";
+    phy-handle = <&ethphy1>;
+    phy-reset-duration = <200>;
+    phy-reset-post-delay = <150>;
+    phy-reset-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>;
+
+    fsl,magic-packet;
+    status = "okay";
+
+    mdio {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        clock-frequency = <5000000>;
+
+        ethphy1: ethernet-phy@0 { /* YT8521SH-CA */
+            compatible = "ethernet-phy-ieee802.3-c22";
@@ -209,16 +275,212 @@
+};
+
+/*+------------------------+
+  |      Misc Devices      |
+  +------------------------+*/
+
+/* Buzzer */
+&pwm1 {
+    pinctrl-names = "default";
+    pinctrl-0 = <&pinctrl_pwm1>;
+    status = "okay";
+};
+
+&i2c2 {
+    clock-frequency = <100000>;
+    pinctrl-names = "default";
+    pinctrl-0 = <&pinctrl_i2c2>;
+    status = "okay";
+
+    codec: wm8960@1a {
+        compatible = "wlf,wm8960";
+        reg = <0x1a>;
+        clocks = <&audio_blk_ctrl IMX8MP_CLK_AUDIO_BLK_CTRL_SAI3_MCLK1>;
+        clock-names = "mclk";
+        wlf,shared-lrclk;
+    };
+
+    ms1112@4a {
+        compatible = "ms,ms1112";
+        reg = <0x4a>;
+        status = "okay";
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        channel@2 {
+                reg = <2>;
+                ti,gain = <0>;
+                ti,datarate = <3>;
+                ti,mode = <1>;
+        };
+
+        channel@3{
+                reg = <3>;
+                ti,gain = <0>;
+                ti,datarate = <3>;
+                ti,mode = <1>;
+        };
+    };
+
+    rtc1208@6f {
+        compatible = "isil,isl1208";
+        reg = <0x6f>;
+        status = "okay";
+    };
+};
+
+/*+------------------------+
+  |   WM8960 Audio Codec   |
+  +------------------------+*/
+
+&sai3 {
+    pinctrl-names = "default";
+    pinctrl-0 = <&pinctrl_sai3>;
+    assigned-clocks = <&clk IMX8MP_CLK_SAI3>;
+    assigned-clock-parents = <&clk IMX8MP_AUDIO_PLL1_OUT>;
+    assigned-clock-rates = <12288000>;
+    clocks = <&audio_blk_ctrl IMX8MP_CLK_AUDIO_BLK_CTRL_SAI3_IPG>, <&clk IMX8MP_CLK_DUMMY>,
+         <&audio_blk_ctrl IMX8MP_CLK_AUDIO_BLK_CTRL_SAI3_MCLK1>, <&clk IMX8MP_CLK_DUMMY>,
+         <&clk IMX8MP_CLK_DUMMY>;
+    clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3";
+    fsl,sai-mclk-direction-output;
+    status = "okay";
+};
+
+&easrc {
+    fsl,asrc-rate  = <48000>;
+    status = "okay";
+};
+
+&xcvr {
+    #sound-dai-cells = <0>;
+    status = "okay";
+};
+
+&sdma2 {
+    status = "okay";
+};
+
+/*+------------------------+
+  |      HDMI Display      |
+  +------------------------+*/
+
+&irqsteer_hdmi {
+    status = "okay";
+};
+
+&hdmi_blk_ctrl {
+    status = "okay";
+};
+
+&hdmi_pavi {
+    status = "okay";
+};
+
+&hdmi {
+    status = "okay";
+};
+
+&hdmiphy {
+    status = "okay";
+};
+
+&lcdif3 {
+    status = "okay";
+
+    thres-low  = <1 2>;     /* (FIFO * 1 / 2) */
+    thres-high = <3 4>;     /* (FIFO * 3 / 4) */
+};
+
+/*+------------------------+
+  |  CAN/RS485 interface   |
+  +------------------------+*/
+
+/* RS485 */
+&uart3 {
+    pinctrl-names = "default";
+    pinctrl-0 = <&pinctrl_uart3>;
+    status = "okay";
+};
+
+/* CAN */
+&flexcan1 {
+    pinctrl-names = "default";
+    pinctrl-0 = <&pinctrl_flexcan1>;
+    status = "okay";
+};
+
+&flexcan2 {
+    pinctrl-names = "default";
+    pinctrl-0 = <&pinctrl_flexcan2>;
+    status = "okay";
+};
+
+/*+------------------------+
+  |   MikroBUS interface   |
+  +------------------------+*/
+
+/* Same as RPi 40Pin extend interface: #32 */
+&pwm3 {
+    pinctrl-names = "default";
+    pinctrl-0 = <&pinctrl_pwm3>;
+    status = "okay";
+};
+
+/* Same as RPi 40Pin extend interface: #19, #21, #23, #24 */
+&uart1 {
+    pinctrl-names = "default";
+    pinctrl-0 = <&pinctrl_uart1>;
+    assigned-clocks = <&clk IMX8MP_CLK_UART1>;
+    assigned-clock-parents = <&clk IMX8MP_SYS_PLL1_80M>;
+    status = "okay";
+};
+
+/* Same as RPi 40Pin extend interface */
+&ecspi2 {
+    #address-cells = <1>;
+    #size-cells = <0>;
+    fsl,spi-num-chipselects = <1>;
+    pinctrl-names = "default";
+    pinctrl-0 = <&pinctrl_ecspi2>;
+    cs-gpios = <&gpio5 13 GPIO_ACTIVE_LOW>;
+    status = "okay";
+
+    spidev@0 {
+        compatible = "fsl,spidev", "semtech,sx1301";
+        reg = <0>;
+        spi-max-frequency = <2000000>;
+    };
+};
+
+/* Same as RPi 40Pin extend interface: #3, #5 */
+&i2c5 {
+    clock-frequency = <100000>;
+    pinctrl-names = "default";
+    pinctrl-0 = <&pinctrl_i2c5>;
+    status = "okay";
+
+    hdc1080@40 {
+        compatible = "ti,hdc1080";
+        reg = <0x40>;
+        status = "okay";
+    };
+
+    eeprom@50 {
+        compatible = "microchip,24c32", "atmel,24c32";
+        reg = <0x50>;
+        pagesize = <32>;
+        num-addresses = <8>;
+    };
+};
+
+/*+------------------------+
+  |    PCA9450CHN PMIC     |
+  +------------------------+*/
+
+&i2c1 {
+    clock-frequency = <400000>;
+    pinctrl-names = "default", "gpio";
+    pinctrl-names = "default";
+    pinctrl-0 = <&pinctrl_i2c1>;
+    pinctrl-1 = <&pinctrl_i2c1_gpio>;
+    scl-gpios = <&gpio5 14 GPIO_ACTIVE_HIGH>;
+    sda-gpios = <&gpio5 15 GPIO_ACTIVE_HIGH>;
+    status = "okay";
+
+    pmic@25 {
@@ -319,16 +581,62 @@
+
+&iomuxc {
+    pinctrl-names = "default";
+    pinctrl-0 = <&pinctrl_hog>;
+
+    pinctrl_leds: ledsgrp {
+    pinctrl_hog: hoggrp {
+        fsl,pins = <
+            MX8MP_IOMUXC_NAND_READY_B__GPIO3_IO16                       0x140
+            MX8MP_IOMUXC_HDMI_DDC_SCL__HDMIMIX_HDMI_SCL                 0x400001c2
+            MX8MP_IOMUXC_HDMI_DDC_SDA__HDMIMIX_HDMI_SDA                 0x400001c2
+            MX8MP_IOMUXC_HDMI_HPD__HDMIMIX_HDMI_HPD                     0x40000010
+            MX8MP_IOMUXC_HDMI_CEC__HDMIMIX_HDMI_CEC                     0x40000010
+            /*
+             * M.2 pin20 & pin21 need to be set to 11 for 88W9098 to select the
+             * default Reference Clock Frequency
+             */
+            MX8MP_IOMUXC_SD1_DATA7__GPIO2_IO09                          0x1c4
+        >;
+    };
+
+    pinctrl_wdog: wdoggrp {
+        fsl,pins = <
+            MX8MP_IOMUXC_GPIO1_IO02__WDOG1_WDOG_B                       0xc6
+        >;
+    };
+
+    pinctrl_leds: ledsgrp {
+        fsl,pins = <
+            MX8MP_IOMUXC_NAND_READY_B__GPIO3_IO16                       0x140
+            MX8MP_IOMUXC_GPIO1_IO14__GPIO1_IO14                         0x140
+            MX8MP_IOMUXC_GPIO1_IO09__GPIO1_IO09                         0x140
+            MX8MP_IOMUXC_SAI5_RXD0__GPIO3_IO21                          0x140
+        >;
+    };
+
+    pinctrl_keys: keysgrp {
+        fsl,pins = <
+            MX8MP_IOMUXC_ECSPI1_MISO__GPIO5_IO08                        0x140
+            MX8MP_IOMUXC_ECSPI1_SS0__GPIO5_IO09                         0x140
+            MX8MP_IOMUXC_UART3_RXD__GPIO5_IO26                          0x140
+            MX8MP_IOMUXC_UART3_TXD__GPIO5_IO27                          0x140
+        >;
+    };
+
+    pinctrl_pwm1: pwm1grp {
+        fsl,pins = <
+            MX8MP_IOMUXC_GPIO1_IO08__PWM1_OUT                           0x116
+        >;
+    };
+
+    pinctrl_pwm3: pwm3grp {
+        fsl,pins = <
+            MX8MP_IOMUXC_SAI5_RXC__PWM3_OUT                             0x116
+        >;
+    };
+
+    pinctrl_uart1: uart1grp {
+        fsl,pins = <
+            MX8MP_IOMUXC_UART1_RXD__UART1_DCE_RX                        0x140
+            MX8MP_IOMUXC_UART1_TXD__UART1_DCE_TX                        0x140
+        >;
+    };
+
@@ -339,6 +647,36 @@
+        >;
+    };
+
+    pinctrl_uart3: uart3grp {
+        fsl,pins = <
+            MX8MP_IOMUXC_ECSPI1_SCLK__UART3_DCE_RX                      0x82
+            MX8MP_IOMUXC_ECSPI1_MOSI__UART3_DCE_TX                      0x82
+        >;
+    };
+
+    pinctrl_flexcan1: flexcan1grp {
+        fsl,pins = <
+            MX8MP_IOMUXC_SPDIF_TX__CAN1_TX                              0x154
+            MX8MP_IOMUXC_SPDIF_RX__CAN1_RX                              0x154
+        >;
+    };
+
+    pinctrl_flexcan2: flexcan2grp {
+        fsl,pins = <
+            MX8MP_IOMUXC_SAI5_MCLK__CAN2_RX                             0x154
+            MX8MP_IOMUXC_SAI5_RXD3__CAN2_TX                             0x154
+        >;
+    };
+
+    pinctrl_ecspi2: ecspi2grp {
+        fsl,pins = <
+            MX8MP_IOMUXC_ECSPI2_SCLK__ECSPI2_SCLK                       0x82
+            MX8MP_IOMUXC_ECSPI2_MOSI__ECSPI2_MOSI                       0x82
+            MX8MP_IOMUXC_ECSPI2_MISO__ECSPI2_MISO                       0x82
+            MX8MP_IOMUXC_ECSPI2_SS0__GPIO5_IO13                         0x40000
+        >;
+    };
+
+    pinctrl_i2c1: i2c1grp {
+        fsl,pins = <
+            MX8MP_IOMUXC_I2C1_SCL__I2C1_SCL                             0x400001c3
@@ -346,10 +684,28 @@
+        >;
+    };
+
+    pinctrl_i2c1_gpio: i2c1grp-gpio {
+    pinctrl_i2c2: i2c2grp {
+        fsl,pins = <
+            MX8MP_IOMUXC_I2C1_SCL__GPIO5_IO14                           0x1c3
+            MX8MP_IOMUXC_I2C1_SDA__GPIO5_IO15                           0x1c3
+            MX8MP_IOMUXC_I2C2_SCL__I2C2_SCL                             0x400001c2
+            MX8MP_IOMUXC_I2C2_SDA__I2C2_SDA                             0x400001c2
+        >;
+    };
+
+    pinctrl_i2c5: i2c5grp {
+        fsl,pins = <
+            MX8MP_IOMUXC_SD1_CMD__I2C5_SDA                              0x400001c2
+            MX8MP_IOMUXC_SD1_CLK__I2C5_SCL                              0x400001c2
+        >;
+    };
+
+    pinctrl_sai3: sai3grp {
+        fsl,pins = <
+            MX8MP_IOMUXC_SAI3_TXFS__AUDIOMIX_SAI3_TX_SYNC               0xd6
+            MX8MP_IOMUXC_SAI3_TXC__AUDIOMIX_SAI3_TX_BCLK                0xd6
+            MX8MP_IOMUXC_SAI3_RXD__AUDIOMIX_SAI3_RX_DATA00              0xd6
+            MX8MP_IOMUXC_SAI3_TXD__AUDIOMIX_SAI3_TX_DATA00              0xd6
+            MX8MP_IOMUXC_SAI3_MCLK__AUDIOMIX_SAI3_MCLK                  0xd6
+            MX8MP_IOMUXC_SAI3_RXC__GPIO4_IO29                           0xd6
+        >;
+    };
+
@@ -465,7 +821,6 @@
+            MX8MP_IOMUXC_ENET_TD3__ENET_QOS_RGMII_TD3                   0x16
+            MX8MP_IOMUXC_ENET_TX_CTL__ENET_QOS_RGMII_TX_CTL             0x16
+            MX8MP_IOMUXC_ENET_TXC__CCM_ENET_QOS_CLOCK_GENERATE_TX_CLK   0x16
+            MX8MP_IOMUXC_GPIO1_IO06__GPIO1_IO06                         0x22
+        >;
+    };
+
@@ -485,16 +840,15 @@
+            MX8MP_IOMUXC_SAI1_TXD3__ENET1_RGMII_TD3                     0x16
+            MX8MP_IOMUXC_SAI1_TXD4__ENET1_RGMII_TX_CTL                  0x16
+            MX8MP_IOMUXC_SAI1_TXD5__ENET1_RGMII_TXC                     0x16
+            MX8MP_IOMUXC_GPIO1_IO01__GPIO1_IO01                         0x11
+        >;
+    };
+};
diff --git a/arch/arm64/configs/igkboard-imx8mp_defconfig b/arch/arm64/configs/igkboard-imx8mp_defconfig
new file mode 100644
index 000000000..6b6fe01d9
index 000000000..b0f923742
--- /dev/null
+++ b/arch/arm64/configs/igkboard-imx8mp_defconfig
@@ -0,0 +1,1118 @@
@@ -0,0 +1,1103 @@
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_AUDIT=y
@@ -522,10 +876,12 @@
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_PERF=y
+CONFIG_CGROUP_BPF=y
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_EXPERT=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_PROFILING=y
+CONFIG_ARCH_KEEMBAY=y
@@ -656,7 +1012,8 @@
+CONFIG_QRTR_SMD=m
+CONFIG_QRTR_TUN=m
+CONFIG_NET_PKTGEN=m
+CONFIG_CAN=m
+CONFIG_CAN=y
+CONFIG_CAN_ISOTP=y
+CONFIG_BT=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
@@ -681,8 +1038,6 @@
+CONFIG_CFG80211_WEXT=y
+CONFIG_MAC80211=y
+CONFIG_MAC80211_LEDS=y
+CONFIG_NET_9P=y
+CONFIG_NET_9P_VIRTIO=y
+CONFIG_NFC=m
+CONFIG_NFC_NCI=m
+CONFIG_NFC_S3FWRN5_I2C=m
@@ -751,8 +1106,7 @@
+CONFIG_BLK_DEV_NVME=y
+CONFIG_SRAM=y
+CONFIG_PCI_ENDPOINT_TEST=y
+CONFIG_EEPROM_AT24=m
+CONFIG_EEPROM_AT25=m
+CONFIG_EEPROM_AT24=y
+CONFIG_UACCE=m
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=y
@@ -840,7 +1194,7 @@
+CONFIG_REALTEK_PHY=y
+CONFIG_ROCKCHIP_PHY=y
+CONFIG_VITESSE_PHY=y
+CONFIG_CAN_FLEXCAN=m
+CONFIG_CAN_FLEXCAN=y
+CONFIG_MDIO_BITBANG=y
+CONFIG_MDIO_BUS_MUX_MULTIPLEXER=y
+CONFIG_MDIO_BUS_MUX_MMIOREG=y
@@ -954,22 +1308,8 @@
+CONFIG_PINCTRL_IMX8ULP=y
+CONFIG_PINCTRL_IMX93=y
+CONFIG_PINCTRL_S32V234=y
+CONFIG_GPIO_ALTERA=m
+CONFIG_GPIO_DWAPB=y
+CONFIG_GPIO_IMX_RPMSG=y
+CONFIG_GPIO_MB86S7X=y
+CONFIG_GPIO_MPC8XXX=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_MXC=y
+CONFIG_GPIO_PL061=y
+CONFIG_GPIO_WCD934X=m
+CONFIG_GPIO_XGENE=y
+CONFIG_GPIO_MAX732X=y
+CONFIG_GPIO_PCA953X=y
+CONFIG_GPIO_PCA953X_IRQ=y
+CONFIG_GPIO_ADP5585=y
+CONFIG_GPIO_BD9571MWV=m
+CONFIG_GPIO_MAX77620=y
+CONFIG_GPIO_SL28CPLD=m
+CONFIG_POWER_RESET_BRCMSTB=y
+CONFIG_POWER_RESET_XGENE=y
+CONFIG_POWER_RESET_SYSCON=y
@@ -1056,6 +1396,7 @@
+CONFIG_RC_DEVICES=y
+CONFIG_IR_GPIO_CIR=m
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_SUPPORT_FILTER=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_ANALOG_TV_SUPPORT=y
+CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y
@@ -1149,6 +1490,7 @@
+CONFIG_BACKLIGHT_PWM=y
+CONFIG_BACKLIGHT_LP855X=m
+CONFIG_BACKLIGHT_GPIO=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
@@ -1208,6 +1550,19 @@
+CONFIG_SND_SOC_LPASS_VA_MACRO=m
+CONFIG_SND_SIMPLE_CARD=y
+CONFIG_SND_AUDIO_GRAPH_CARD=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_ITE=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_REDRAGON=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_MULTITOUCH=m
+CONFIG_I2C_HID_ACPI=m
+CONFIG_I2C_HID_OF=m
@@ -1322,29 +1677,8 @@
+CONFIG_EDAC_LAYERSCAPE=m
+CONFIG_EDAC_SYNOPSYS=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_DS1307=m
+CONFIG_RTC_DRV_HYM8563=m
+CONFIG_RTC_DRV_MAX77686=y
+CONFIG_RTC_DRV_RK808=m
+CONFIG_RTC_DRV_PCF85363=m
+CONFIG_RTC_DRV_M41T80=m
+CONFIG_RTC_DRV_RX8581=m
+CONFIG_RTC_DRV_RV3028=m
+CONFIG_RTC_DRV_RV8803=m
+CONFIG_RTC_DRV_S5M=y
+CONFIG_RTC_DRV_DS3232=y
+CONFIG_RTC_DRV_PCF2127=m
+CONFIG_RTC_DRV_PCF2131=m
+CONFIG_RTC_DRV_EFI=y
+CONFIG_RTC_DRV_CROS_EC=y
+CONFIG_RTC_DRV_FSL_FTM_ALARM=m
+CONFIG_RTC_DRV_PL031=y
+CONFIG_RTC_DRV_SNVS=y
+CONFIG_RTC_DRV_BBNSM=y
+CONFIG_RTC_DRV_IMX_SC=y
+CONFIG_RTC_DRV_IMX_RPMSG=y
+CONFIG_RTC_DRV_ISL1208=y
+CONFIG_DMADEVICES=y
+CONFIG_BCM_SBA_RAID=m
+CONFIG_FSL_EDMA=y
+CONFIG_FSL_QDMA=m
+CONFIG_FSL_EDMA_V3=y
@@ -1435,23 +1769,20 @@
+CONFIG_IIO_ST_ACCEL_3AXIS=m
+CONFIG_IMX8QXP_ADC=y
+CONFIG_IMX93_ADC=y
+CONFIG_MAX9611=m
+CONFIG_QCOM_SPMI_VADC=m
+CONFIG_QCOM_SPMI_ADC5=m
+CONFIG_IIO_CROS_EC_SENSORS_CORE=m
+CONFIG_IIO_CROS_EC_SENSORS=m
+CONFIG_FXAS21002C=y
+CONFIG_MS1112=y
+CONFIG_BMG160=m
+CONFIG_IIO_ST_GYRO_3AXIS=m
+CONFIG_MAX30100=m
+CONFIG_MAX30102=m
+CONFIG_DHT11=y
+CONFIG_HDC100X=y
+CONFIG_HTS221=y
+CONFIG_FXOS8700_I2C=y
+CONFIG_RPMSG_IIO_PEDOMETER=m
+CONFIG_INV_MPU6050_I2C=m
+CONFIG_IIO_ST_LSM6DSX=y
+CONFIG_IIO_CROS_EC_LIGHT_PROX=m
+CONFIG_SENSORS_ISL29018=y
+CONFIG_VCNL4000=m
+CONFIG_VCNL4035=m
+CONFIG_IIO_ST_MAGN_3AXIS=m
+CONFIG_IIO_CROS_EC_BARO=m
+CONFIG_MPL3115=y
+CONFIG_MS5611=m
+CONFIG_MS5611_I2C=m
@@ -1505,8 +1836,6 @@
+CONFIG_EXT2_FS=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS_POSIX_ACL=y
+CONFIG_FANOTIFY=y
+CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y
+CONFIG_QUOTA=y
@@ -1515,21 +1844,28 @@
+CONFIG_CUSE=m
+CONFIG_OVERLAY_FS=m
+CONFIG_VFAT_FS=y
+CONFIG_EXFAT_FS=y
+CONFIG_NTFS_FS=y
+CONFIG_NTFS_RW=y
+CONFIG_NTFS3_FS=y
+CONFIG_NTFS3_64BIT_CLUSTER=y
+CONFIG_NTFS3_LZX_XPRESS=y
+CONFIG_NTFS3_FS_POSIX_ACL=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_HUGETLBFS=y
+CONFIG_EFIVAR_FS=y
+CONFIG_JFFS2_FS=y
+CONFIG_UBIFS_FS=y
+CONFIG_SQUASHFS=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_ROOT_NFS=y
+CONFIG_9P_FS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_936=y
+CONFIG_NLS_CODEPAGE_950=y
+CONFIG_NLS_CODEPAGE_874=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_UTF8=y
+CONFIG_TRUSTED_KEYS=m
+# CONFIG_TRUSTED_KEYS_TPM is not set
+# CONFIG_TRUSTED_KEYS_TEE is not set
@@ -1557,12 +1893,16 @@
+CONFIG_CRYPTO_CHACHA20POLY1305=m
+CONFIG_CRYPTO_ECHAINIV=y
+CONFIG_CRYPTO_TLS=m
+CONFIG_CRYPTO_BLAKE2B=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_RMD160=m
+CONFIG_CRYPTO_STREEBOG=m
+CONFIG_CRYPTO_VMAC=m
+CONFIG_CRYPTO_WP512=m
+CONFIG_CRYPTO_XCBC=m
+CONFIG_CRYPTO_XXHASH=m
+CONFIG_CRYPTO_LZO=y
+CONFIG_CRYPTO_ZSTD=y
+CONFIG_CRYPTO_ANSI_CPRNG=y
+CONFIG_CRYPTO_USER_API_HASH=m
+CONFIG_CRYPTO_USER_API_SKCIPHER=m
@@ -1595,7 +1935,6 @@
+CONFIG_CRC8=y
+CONFIG_CMA_SIZE_MBYTES=32
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
+CONFIG_DEBUG_INFO_REDUCED=y
+CONFIG_MAGIC_SYSRQ=y
@@ -1613,3 +1952,613 @@
+CONFIG_CORESIGHT_CPU_DEBUG=m
+CONFIG_CORESIGHT_CTI=m
+CONFIG_MEMTEST=y
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 3946eb595..15278c13c 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -1164,6 +1164,18 @@ config TI_ADC161S626
      This driver can also be built as a module. If so, the module will be
      called ti-adc161s626.
+config MS1112
+   tristate "Ruimeng Technology MS1112 ADC"
+   depends on I2C
+   select IIO_BUFFER
+   select IIO_TRIGGERED_BUFFER
+   help
+     If you say yes here you get support for Ruimeng Technology ADS1015
+     ADC chip.
+
+     This driver can also be built as a module. If so, the module will be
+     called ms1112.
+
 config TI_ADS1015
    tristate "Texas Instruments ADS1015 ADC"
    depends on I2C
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 83233c38c..f403164cf 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -104,6 +104,7 @@ obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
 obj-$(CONFIG_TI_ADC108S102) += ti-adc108s102.o
 obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
 obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
+obj-$(CONFIG_MS1112) += ms1112.o
 obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
 obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
 obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o
diff --git a/drivers/iio/adc/ms1112.c b/drivers/iio/adc/ms1112.c
new file mode 100644
index 000000000..cf8ea5c66
--- /dev/null
+++ b/drivers/iio/adc/ms1112.c
@@ -0,0 +1,569 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MS1112 - Ruimeng Technology Analog-to-Digital Converter
+ *
+ * Copyright (c) 2024, LingYun IoT System Studio.
+ *
+ * IIO driver for MS1112 ADC 7-bit I2C slave address: 0x4A
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/of_gpio.h>
+#include <linux/semaphore.h>
+#include <linux/timer.h>
+#include <linux/i2c.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+
+#define MS1112_DRV_NAME            "ms1112"
+
+#define MS1112_CONV_REG            0x00
+#define MS1112_CFG_REG            0x01
+#define MS1112_DEFAULT_CONFIG      0xFC
+
+#define MS1112_CHANNELS            4
+#define MS1112_CFG_DR_SHIFT         2
+#define MS1112_CFG_MOD_SHIFT      4
+#define MS1112_CFG_PGA_SHIFT      0
+#define MS1112_CFG_MUX_SHIFT      5
+
+#define MS1112_CFG_DR_MASK         GENMASK(3, 2)
+#define MS1112_CFG_MOD_MASK         BIT(4)
+#define MS1112_CFG_PGA_MASK         GENMASK(1, 0)
+#define MS1112_CFG_MUX_MASK         GENMASK(6, 5)
+
+#define MS1112_DEFAULT_PGA         0
+#define MS1112_DEFAULT_DATA_RATE   3
+#define MS1112_DEFAULT_CHAN         2
+#define MS1112_DEFAULT_MODE         1
+
+#define MS1112_CONTINUOUS         0
+#define MS1112_SINGLESHOT         1
+
+struct ms1112_chip_data {
+   struct iio_chan_spec const   *channels;
+   int                     num_channels;
+   const struct iio_info      *info;
+   const int               *data_rate;
+   const int               data_rate_len;
+   const int               *scale;
+   const int               scale_len;
+   bool                  has_comparator;
+};
+
+enum ms1112_channels {
+   MS1112_AIN0_AIN1 = 0,
+   MS1112_AIN2,
+   MS1112_AIN0,
+   MS1112_AIN1,
+   MS1112_TIMESTAMP,
+};
+
+static const int ms1112_data_rate[] = {
+   240,60,30,15
+};
+
+static const int ms1112_fullscale_range[] = {
+   2048
+};
+
+static const int ms1112_scale[] = { /* 12bit ADC */
+   2048,11,
+   2048,13,
+   2048,14,
+   2048,15
+};
+
+#define FIT_CHECK(_testbits, _fitbits)            \
+   (                                    \
+      (_fitbits) *                        \
+      !!sizeof(struct {                     \
+      static_assert((_testbits) <= (_fitbits));   \
+      int pad;                           \
+      })                                 \
+   )
+
+#define MS1112_V_CHAN(_chan, _addr, _realbits, _shift, _event_spec, _num_event_specs) { \
+   .type = IIO_VOLTAGE,            \
+   .indexed = 1,                  \
+   .address = _addr,               \
+   .channel = _chan,               \
+   .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |   \
+   BIT(IIO_CHAN_INFO_SCALE) |                  \
+   BIT(IIO_CHAN_INFO_SAMP_FREQ),               \
+   .info_mask_shared_by_all_available =         \
+   BIT(IIO_CHAN_INFO_SCALE) |                  \
+   BIT(IIO_CHAN_INFO_SAMP_FREQ),               \
+   .scan_index = _addr,                     \
+   .scan_type = {                           \
+      .sign = 's',                        \
+      .realbits = (_realbits),               \
+      .storagebits = FIT_CHECK((_realbits) + (_shift), 16),   \
+      .shift = (_shift),                     \
+      .endianness = IIO_CPU,                  \
+   },                                    \
+   .event_spec = (_event_spec),               \
+   .num_event_specs = (_num_event_specs),         \
+   .datasheet_name = "AIN"#_chan,               \
+}
+
+#define MS1112_V_DIFF_CHAN(_chan, _chan2, _addr, _realbits, _shift, _event_spec, _num_event_specs) { \
+   .type = IIO_VOLTAGE,                     \
+   .differential = 1,                        \
+   .indexed = 1,                           \
+   .address = _addr,                        \
+   .channel = _chan,                        \
+   .channel2 = _chan2,                        \
+   .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |   \
+   BIT(IIO_CHAN_INFO_SCALE) |                  \
+   BIT(IIO_CHAN_INFO_SAMP_FREQ),               \
+   .info_mask_shared_by_all_available =         \
+   BIT(IIO_CHAN_INFO_SCALE) |                  \
+   BIT(IIO_CHAN_INFO_SAMP_FREQ),               \
+   .scan_index = _addr,                     \
+   .scan_type = {                           \
+      .sign = 's',                        \
+      .realbits = (_realbits),               \
+      .storagebits = FIT_CHECK((_realbits) + (_shift), 16),   \
+      .shift = (_shift),                     \
+      .endianness = IIO_CPU,                  \
+   },                                    \
+   .event_spec = (_event_spec),               \
+   .num_event_specs = (_num_event_specs),         \
+   .datasheet_name = "AIN"#_chan"-AIN"#_chan2,      \
+}
+
+struct ms1112_channel_data {
+   bool enabled;
+   unsigned int pga;
+   unsigned int data_rate;
+   unsigned int mode;
+};
+
+struct ms1112_thresh_data {
+   int high_thresh;
+   int low_thresh;
+};
+
+struct ms1112_data {
+   struct ms1112_channel_data channel_data[MS1112_CHANNELS];
+   struct ms1112_thresh_data thresh_data[MS1112_CHANNELS];
+   const struct ms1112_chip_data *chip;
+   struct mutex lock;
+   void *private_data;
+   struct i2c_client *client;
+};
+
+/* MS1112 don't use the register address */
+static int ms1112_read_regs(struct ms1112_data *dev, uint8_t reg, void *buf, uint8_t size)
+{
+   int               ret = 0;
+   struct i2c_msg      msg[1];
+   struct i2c_client   *client = dev->client;
+
+   msg[0].addr  = client->addr;
+   msg[0].flags = I2C_M_RD;
+   msg[0].buf    = buf;
+   msg[0].len    = size;
+
+   ret = i2c_transfer(client->adapter, msg, 1);
+   if(ret != 1) {
+      dev_err(&dev->client->dev, "%s() i2c_transfer error, ret=%d\n", __func__, ret);
+      ret = -EREMOTEIO;
+   }
+
+   return ret;
+}
+
+/* MS1112 don't use the register address */
+static s32 ms1112_write_regs(struct ms1112_data *dev, uint8_t reg, uint8_t *data, uint8_t bytes)
+{
+   int               ret = 0;
+   struct i2c_msg      msg;
+   struct i2c_client   *client = dev->client;
+
+   msg.addr  = client->addr;
+   msg.flags = 0;
+   msg.buf   = data;
+   msg.len   = bytes;
+
+   ret = i2c_transfer(client->adapter, &msg, 1);
+   if(ret != 1) {
+      dev_err(&dev->client->dev, "%s() i2c_transfer error, ret=%d\n", __func__, ret);
+      ret = -EREMOTEIO;
+   }
+
+   return ret;
+}
+
+static int ms1112_readdata(struct ms1112_data *dev,unsigned int *val)
+{
+   unsigned char   buf[3];
+   unsigned char   rx_data[3];
+   int            rv = 0;
+
+   rv = ms1112_read_regs(dev, MS1112_CONV_REG, rx_data, 3);
+   if(rv<0) {
+      return rv;
+   }
+
+   buf[0] = rx_data[0];
+   buf[1] = rx_data[1];
+   buf[2] = rx_data[2];
+
+   *val = (buf[0]<<8) | buf[1];
+   return rv;
+}
+
+static int ms1112_get_adc_result(struct ms1112_data *data, int chan, int *val)
+{
+   int         ret = 0;
+   int         pga, dr , mode;
+   uint8_t      mask, cfg;
+
+   if (chan < 0 || chan >= MS1112_CHANNELS)
+      return -EINVAL;
+
+   mode = data->channel_data[chan].mode;
+   pga = data->channel_data[chan].pga;
+   dr = data->channel_data[chan].data_rate;
+
+   mask = MS1112_CFG_MUX_MASK | MS1112_CFG_PGA_MASK |
+      MS1112_CFG_DR_MASK | MS1112_CFG_MOD_MASK | MS1112_SINGLESHOT << 7;
+
+   cfg = chan << MS1112_CFG_MUX_SHIFT | pga << MS1112_CFG_PGA_SHIFT |
+      dr << MS1112_CFG_DR_SHIFT | mode << MS1112_CFG_MOD_SHIFT | MS1112_SINGLESHOT << 7;
+
+   cfg = (cfg & mask);
+
+   ms1112_write_regs(data, MS1112_CFG_REG, &cfg, 1);
+
+   ret = ms1112_readdata(data,val);
+   return ret;
+}
+
+static int ms1112_set_scale(struct ms1112_data *data, struct iio_chan_spec const *chan,
+      int scale, int uscale)
+{
+   int i;
+   int fullscale = div_s64((scale * 1000000LL + uscale) <<
+         (chan->scan_type.realbits - 1), 1000000);
+
+   for (i = 0; i < ARRAY_SIZE(ms1112_fullscale_range); i++) {
+      if (ms1112_fullscale_range[i] == fullscale) {
+         data->channel_data[chan->address].pga = i;
+         return 0;
+      }
+   }
+
+   return -EINVAL;
+}
+
+static int ms1112_set_data_rate(struct ms1112_data *data, int chan, int rate)
+{
+   int i;
+
+   for (i = 0; i < data->chip->data_rate_len; i++) {
+      if (data->chip->data_rate[i] == rate) {
+         data->channel_data[chan].data_rate = i;
+         return 0;
+      }
+   }
+
+   return -EINVAL;
+}
+
+static int ms1112_read_avail(struct iio_dev *indio_dev,
+      struct iio_chan_spec const *chan,
+      const int **vals, int *type, int *length,
+      long mask)
+{
+   struct ms1112_data *data = iio_priv(indio_dev);
+
+   if (chan->type != IIO_VOLTAGE)
+      return -EINVAL;
+
+   switch (mask) {
+      case IIO_CHAN_INFO_SCALE:
+         *type = IIO_VAL_FRACTIONAL_LOG2;
+         *vals =  data->chip->scale;
+         *length = data->chip->scale_len;
+         return IIO_AVAIL_LIST;
+      case IIO_CHAN_INFO_SAMP_FREQ:
+         *type = IIO_VAL_INT;
+         *vals = data->chip->data_rate;
+         *length = data->chip->data_rate_len;
+         return IIO_AVAIL_LIST;
+      default:
+         return -EINVAL;
+   }
+}
+
+static int ms1112_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+   int ret, idx;
+   struct ms1112_data *data = iio_priv(indio_dev);
+
+   mutex_lock(&data->lock);
+   switch (mask) {
+      case IIO_CHAN_INFO_RAW:
+
+         ret = iio_device_claim_direct_mode(indio_dev);
+         if (ret)
+            break;
+
+         ret = ms1112_get_adc_result(data, chan->address, val);
+         if (ret < 0) {
+            goto release_direct;
+         }
+
+         *val = sign_extend32(*val >> chan->scan_type.shift,
+               chan->scan_type.realbits - 1);
+
+         ret = IIO_VAL_INT;
+release_direct:
+         iio_device_release_direct_mode(indio_dev);
+         break;
+
+      case IIO_CHAN_INFO_SCALE:
+         idx = data->channel_data[chan->address].pga;
+         *val = ms1112_fullscale_range[idx];
+         *val2 = chan->scan_type.realbits - 1;
+         ret = IIO_VAL_FRACTIONAL_LOG2;
+         break;
+      case IIO_CHAN_INFO_SAMP_FREQ:
+         idx = data->channel_data[chan->address].data_rate;
+         *val = data->chip->data_rate[idx];
+         ret = IIO_VAL_INT;
+         break;
+      default:
+         ret = -EINVAL;
+         break;
+   }
+   mutex_unlock(&data->lock);
+
+   return ret;
+}
+
+static int ms1112_write_raw(struct iio_dev *indio_dev,
+      struct iio_chan_spec const *chan, int val,
+      int val2, long mask)
+{
+   struct ms1112_data *data = iio_priv(indio_dev);
+   int ret;
+
+   mutex_lock(&data->lock);
+   switch (mask) {
+      case IIO_CHAN_INFO_SCALE:
+         ret = ms1112_set_scale(data, chan, val, val2);
+         break;
+      case IIO_CHAN_INFO_SAMP_FREQ:
+         ret = ms1112_set_data_rate(data, chan->address, val);
+         break;
+      default:
+         ret = -EINVAL;
+         break;
+   }
+   mutex_unlock(&data->lock);
+
+   return ret;
+}
+
+static const struct iio_info ms1112_info = {
+   .read_raw = ms1112_read_raw,
+   .write_raw = ms1112_write_raw,
+   .read_avail = ms1112_read_avail,
+};
+
+
+static const struct iio_chan_spec ms1112_channels[] = {
+   MS1112_V_DIFF_CHAN(0, 1, MS1112_AIN0_AIN1, 16, 0, NULL, 0),
+   MS1112_V_CHAN(2, MS1112_AIN2, 16, 0, NULL, 0),
+   MS1112_V_CHAN(0, MS1112_AIN0, 16, 0, NULL, 0),
+   MS1112_V_CHAN(1, MS1112_AIN1, 16, 0, NULL, 0),
+   IIO_CHAN_SOFT_TIMESTAMP(MS1112_TIMESTAMP),
+};
+
+static int ms1112_client_get_channels_config(struct i2c_client *client)
+{
+   struct iio_dev *indio_dev = i2c_get_clientdata(client);
+   struct ms1112_data *data = iio_priv(indio_dev);
+   struct device *dev = &client->dev;
+   struct fwnode_handle *node;
+   int i = -1;
+
+   device_for_each_child_node(dev, node) {
+      u32 pval;
+      unsigned int channel;
+      unsigned int pga = MS1112_DEFAULT_PGA;
+      unsigned int data_rate = MS1112_DEFAULT_DATA_RATE;
+      unsigned int mode = MS1112_DEFAULT_MODE;
+
+      if (fwnode_property_read_u32(node, "reg", &pval)) {
+         dev_err(dev, "invalid reg on %pfw\n", node);
+         continue;
+      }
+
+      channel = pval;
+      if (channel >= MS1112_CHANNELS) {
+         dev_err(dev, "invalid channel index %d on %pfw\n",
+               channel, node);
+         continue;
+      }
+
+      if (!fwnode_property_read_u32(node, "ti,gain", &pval)) {
+         pga = pval;
+         if (pga > 3 ) {
+            dev_err(dev, "invalid gain on %pfw\n", node);
+            fwnode_handle_put(node);
+            return -EINVAL;
+         }
+      }
+
+      if (!fwnode_property_read_u32(node, "ti,datarate", &pval)) {
+         data_rate = pval;
+         if (data_rate > 3) {
+            dev_err(dev, "invalid data_rate on %pfw\n", node);
+            fwnode_handle_put(node);
+            return -EINVAL;
+         }
+      }
+
+      if (!fwnode_property_read_u32(node, "ti,mode", &pval)) {
+         mode = pval;
+         if (mode > 1) {
+            dev_err(dev, "invalid mode on %pfw\n", node);
+            fwnode_handle_put(node);
+            return -EINVAL;
+         }
+      }
+
+
+      data->channel_data[channel].pga = pga;
+      data->channel_data[channel].data_rate = data_rate;
+      data->channel_data[channel].mode = mode;
+      i++;
+   }
+
+   return i < 0 ? -EINVAL : 0;
+}
+
+static void ms1112_get_channels_config(struct i2c_client *client)
+{
+   unsigned int k;
+
+   struct iio_dev *indio_dev = i2c_get_clientdata(client);
+   struct ms1112_data *data = iio_priv(indio_dev);
+
+   if (!ms1112_client_get_channels_config(client))
+      return;
+
+   /* fallback on default configuration */
+   for (k = 0; k < MS1112_CHANNELS; ++k) {
+      data->channel_data[k].pga = MS1112_DEFAULT_PGA;
+      data->channel_data[k].data_rate = MS1112_DEFAULT_DATA_RATE;
+      data->channel_data[k].mode = MS1112_DEFAULT_MODE;
+   }
+}
+
+static int ms1112_probe(struct i2c_client *client,const struct i2c_device_id *id)
+{
+   struct iio_dev *indio_dev;
+   const struct ms1112_chip_data *chip;
+   struct ms1112_data *data;
+   int ret;
+   int i;
+
+   chip = device_get_match_data(&client->dev);
+   if (!chip)
+      chip = (const struct ms1112_chip_data *)id->driver_data;
+   if (!chip)
+      return dev_err_probe(&client->dev, -EINVAL, "Unknown chip\n");
+
+   indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*indio_dev));
+   if (!indio_dev)
+      return -ENOMEM;
+
+   data = iio_priv(indio_dev);
+   i2c_set_clientdata(client, indio_dev);
+
+   mutex_init(&data->lock);
+
+   indio_dev->name = MS1112_DRV_NAME;
+   indio_dev->info = chip->info;
+   indio_dev->modes = INDIO_DIRECT_MODE;
+   indio_dev->channels = chip->channels;
+   indio_dev->num_channels = chip->num_channels;
+   data->chip = chip;
+   data->client = client;
+
+   for (i = 0; i < MS1112_CHANNELS; i++) {
+      int realbits = indio_dev->channels[i].scan_type.realbits;
+
+      data->thresh_data[i].low_thresh = -1 << (realbits - 1);
+      data->thresh_data[i].high_thresh = (1 << (realbits - 1)) - 1;
+   }
+
+   /* we need to keep this ABI the same as used by hwmon ADS1015 driver */
+   ms1112_get_channels_config(client);
+
+   ret = iio_device_register(indio_dev);
+   if (ret)
+      dev_err(&client->dev, "Failed to register IIO device\n");
+   return ret;
+}
+
+static void ms1112_remove(struct i2c_client *client)
+{
+   struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+   iio_device_unregister(indio_dev);
+
+}
+
+static const struct ms1112_chip_data ms1112_data = {
+   .channels   = ms1112_channels,
+   .num_channels   = ARRAY_SIZE(ms1112_channels),
+   .info      = &ms1112_info,
+   .data_rate   = ms1112_data_rate,
+   .data_rate_len   = ARRAY_SIZE(ms1112_data_rate),
+   .scale      = ms1112_scale,
+   .scale_len   = ARRAY_SIZE(ms1112_scale),
+   .has_comparator = false,
+};
+
+static const struct i2c_device_id ms1112_id[] = {
+   { "ms1112", (kernel_ulong_t)&ms1112_data },
+   {}
+};
+MODULE_DEVICE_TABLE(i2c, ms1112_id);
+
+static const struct of_device_id ms1112_of_match[] = {
+   { .compatible = "ms,ms1112" },
+   { },
+};
+MODULE_DEVICE_TABLE(of, ms1112_of_match);
+
+static struct i2c_driver ms1112_driver = {
+   .driver = {
+      .owner = THIS_MODULE,
+      .name = "ms1112",
+      .of_match_table = ms1112_of_match,
+   },
+   .probe = ms1112_probe,
+   .remove = ms1112_remove,
+   .id_table   = ms1112_id,
+};
+
+module_i2c_driver(ms1112_driver);
+
+MODULE_AUTHOR("Tang Junfeng");
+MODULE_DESCRIPTION("MS1112 IIO ADC Driver");
+MODULE_LICENSE("GPL");