Merge tag 'v5.0.14' into dev-5.0

This is the 5.0.14 stable release

Signed-off-by: Joel Stanley <joel@jms.id.au>
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 858b6c0..b82566d 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -2475,6 +2475,9 @@
 			deep    - Suspend-To-RAM or equivalent (if supported)
 			See Documentation/admin-guide/pm/sleep-states.rst.
 
+	mem.devmem=	Activate the /dev/mem device
+			Format: <bool>  (1/Y/y=enable, 0/N/n=disable)
+
 	meye.*=		[HW] Set MotionEye Camera parameters
 			See Documentation/media/v4l-drivers/meye.rst.
 
diff --git a/Documentation/devicetree/bindings/bmc/npcm7xx-lpc-bpc.txt b/Documentation/devicetree/bindings/bmc/npcm7xx-lpc-bpc.txt
new file mode 100644
index 0000000..0832c9c
--- /dev/null
+++ b/Documentation/devicetree/bindings/bmc/npcm7xx-lpc-bpc.txt
@@ -0,0 +1,26 @@
+Nuvoton NPCM7xx LPC BPC interface
+
+Nuvoton BMC NPCM7xx BIOS Post Code (BPC) monitoring two
+configurable I/O addresses written by the host on the
+Low Pin Count (LPC) bus, the capure data stored in 128-word FIFO.
+
+NPCM7xx BPC supports capture double words, when using capture
+double word only I/O address 1 is monitored.
+
+Required properties for lpc_bpc node
+- compatible	: "nuvoton,npcm750-lpc-bpc" for Poleg NPCM7XX.
+- reg 			: specifies physical base address and size of the registers.
+- interrupts	: contain the LPC BPC with flags for falling edge.
+- monitor-ports : contain monitor I/O addresses, at least one monitor I/O
+				  address required
+
+Optional property for lpc_bpc node
+- bpc-en-dwcapture : enable capture double words support.
+
+Example:
+	lpc_bpc: lpc-bpc@f0007040 {
+		compatible = "nuvoton,npcm7xx-lpc-bpc";
+		reg = <0xf0007040 0x14>;
+		monitor-ports = <0x80>;
+		interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+	};
diff --git a/Documentation/devicetree/bindings/bmc/npcm7xx-pci-mbox.txt b/Documentation/devicetree/bindings/bmc/npcm7xx-pci-mbox.txt
new file mode 100644
index 0000000..e5585f3
--- /dev/null
+++ b/Documentation/devicetree/bindings/bmc/npcm7xx-pci-mbox.txt
@@ -0,0 +1,19 @@
+Nuvoton NPCM7xx PCI mail box interface
+
+Nuvoton BMC NPCM7xx PCI mail box, The mailbox is a high-bandwidth
+communication module between the BMC CPU and host CPU.
+
+Required properties for lpc_bpc node
+- compatible	: "nuvoton,npcm750-pci-mbox" for Poleg NPCM7XX.
+- reg 			: specifies two address space
+					1. physical base address and size of the registers.
+					2. physical base address and size of the dual-ported RAM.
+- interrupts	: contain the PCI mail box interrupt with flags for falling edge.
+
+Example:
+	pcimbox: pcimbox@f0848000 {
+		compatible = "nuvoton,npcm750-pci-mbox", "simple-mfd", "syscon";
+		reg = <0xf084C000 0x8
+			0xf0848000 0x3F00>;
+		interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+	};
diff --git a/Documentation/devicetree/bindings/gpu/aspeed-gfx.txt b/Documentation/devicetree/bindings/gpu/aspeed-gfx.txt
new file mode 100644
index 0000000..a740333
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpu/aspeed-gfx.txt
@@ -0,0 +1,41 @@
+Device tree configuration for the GFX display deivce on the AST2500 SoCs.
+
+Required properties:
+  - compatible
+    * Must be one of the following:
+      + aspeed,ast2500-gfx
+      + aspeed,ast2400-gfx
+    * In addition, the ASPEED pinctrl bindings require the 'syscon' property to
+      be present
+
+  - reg: Physical base address and length of the GFX registers
+
+  - interrupts: interrupt number for the GFX device
+
+  - clocks: clock number used to generate the pixel clock
+
+  - resets: reset line that must be released to use the GFX device
+
+  - memory-region:
+    Phandle to a memory region to allocate from, as defined in
+    Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
+
+
+Example:
+
+gfx: display@1e6e6000 {
+	compatible = "aspeed,ast2500-gfx", "syscon";
+	reg = <0x1e6e6000 0x1000>;
+	reg-io-width = <4>;
+	clocks = <&syscon ASPEED_CLK_GATE_D1CLK>;
+	resets = <&syscon ASPEED_RESET_CRT1>;
+	interrupts = <0x19>;
+	memory-region = <&gfx_memory>;
+};
+
+gfx_memory: framebuffer {
+	size = <0x01000000>;
+	alignment = <0x01000000>;
+	compatible = "shared-dma-pool";
+	reusable;
+};
diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/max31785.txt b/Documentation/devicetree/bindings/hwmon/pmbus/max31785.txt
new file mode 100644
index 0000000..af9578e
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/pmbus/max31785.txt
@@ -0,0 +1,158 @@
+Bindings for the Maxim MAX31785 Intelligent Fan Controller
+==========================================================
+
+Reference:
+
+https://datasheets.maximintegrated.com/en/ds/MAX31785.pdf
+
+Required properties:
+- compatible     : One of "maxim,max31785" or "maxim,max31785a"
+- reg            : I2C address, one of 0x52, 0x53, 0x54, 0x55.
+- #address-cells : Must be 1
+- #size-cells    : Must be 0
+- #thermal-sensor-cells  : Should be 1. The device supports:
+                           - One internal sensor
+                           - Four external I2C digital sensors
+                           - Six external thermal diodes
+
+Optional properties:
+- use-stored-presence    : Do not treat the devicetree description as canon for
+                           fan presence (the 'installed' bit of FAN_CONFIG_*).
+                           Instead, rely on the on the default value store of
+                           the device to populate it.
+
+Capabilities are configured through subnodes of the controller's node.
+
+Fans
+----
+
+Only fans with subnodes present will be considered as installed. If
+use-stored-presence is present in the parent node, then only fans that are both
+defined in the devicetree and have their installed bit set are considered
+installed.
+
+Required subnode properties:
+- compatible             : Must be "pmbus-fan"
+- reg                    : The PMBus page the properties apply to.
+- #cooling-cells         : Should be 2. See the thermal bindings at [1].
+- maxim,fan-rotor-input  : The type of rotor measurement provided to the
+                           controller. Must be either "tach" for tachometer
+                           pulses or "lock" for a locked-rotor signal.
+- maxim,fan-lock-polarity: Required iff maxim,fan-rotor-input is "lock". Valid
+                           values are "low" for active low, "high" for active
+                           high.
+
+Optional subnode properties:
+- fan-mode               : "rpm" or "pwm". Default value is "pwm".
+- tach-pulses            : Tachometer pulses per revolution. Valid values are
+                           1, 2, 3 or 4. The default is 1.
+- cooling-min-level      : Smallest cooling state accepted. See [1].
+- cooling-max-level      : Largest cooling state accepted. See [1].
+- maxim,fan-no-fault-ramp: Do not ramp the fan to 100% PWM duty on detecting a
+                           fan fault
+- maxim,fan-startup      : The number of rotations required before taking
+                           emergency action for an unresponsive fan and driving
+                           it with 100% or 0% PWM duty, depending on the state
+                           of maxim,fan-no-fault-ramp. Valid values are 0
+                           (automatic spin-up disabled), 2, 4, or 8. Default
+                           value is 0.
+- maxim,fan-health       : Enable automated fan health check
+- maxim,fan-ramp         : Configures how fast the device ramps the PWM duty
+                           cycle from one value to another. Valid values are 0
+                           to 7 inclusive, with values 0 - 2 configuring a
+                           1000ms update rate and 1 - 3% duty respective duty
+                           increase, and 3 - 7 a 200ms update rate with a 1 -
+                           5% respective duty increase. Default value is 0.
+- maxim,fan-no-watchdog  : Do not ramp fan to 100% PWM duty on failure to
+                           update desired fan rate inside 10s. This implies
+                           maxim,tmp-no-fault-ramp
+- maxim,tmp-no-fault-ramp: Do not ramp fan to 100% PWM duty on temperature
+                           sensor fault detection. This implies
+                           maxim,fan-no-watchdog
+- maxim,tmp-hysteresis   : The temperature hysteresis used to determine
+                           transitions to lower fan speed bands in the
+                           temperature/fan rate lookup table. Valid values are
+                           2, 4, 6 or 8 (degrees celcius). Default value is 2.
+- maxim,fan-dual-tach    : Enable dual tachometer functionality
+- maxim,fan-pwm-freq     : The PWM frequency. Valid values are 30, 50, 100, 150
+                           and 25000 (Hz). Default value is 30Hz.
+- maxim,fan-lookup-table : A 16-element cell array of alternating temperature
+                           and rate values representing the look up table. The
+                           rate units are set through the fan-mode property.
+- maxim,fan-fault-pin-mon: Ramp fans to 100% PWM duty when the FAULT pin is
+                           asserted
+
+Temperature
+-----------
+
+Required subnode properties:
+- compatible    : Must be "pmbus-temperature"
+- reg           : The PMBus page the properties apply to.
+
+Optional subnode properties:
+- maxim,tmp-offset      : Valid values are 0 - 30 (degrees celcius) inclusive.
+                          Default value is 0.
+- maxim,tmp-fans        : An array of phandles to fans controlled by the
+                          current temperature sensor.
+
+[1] Documentation/devicetree/bindings/thermal/thermal.txt
+
+Example:
+	fan-max31785: max31785@52 {
+		reg = <0x52>;
+		compatible = "maxim,max31785";
+                #address-cells = <1>;
+                #size-cells = <0>;
+                #thermal-sensor-cells = <1>;
+
+                fan@0 {
+                        compatible = "pmbus-fan";
+                        reg = <0>;
+                        mode = "rpm";
+                        tach-pulses = <1>;
+
+                        #cooling-cells = <2>;
+                        cooling-min-level = <0>;
+                        cooling-max-level = <9>;
+
+                        maxim,fan-rotor-input = "tach";
+                        maxim,fan-dual-tach;
+                };
+
+                /*
+                 * Hardware controlled fan: Fan speed is controlled by a
+                 * temperature sensor feeding values into the lookup table. The
+                 * fan association is done in the temperature sensor node. One
+                 * sensor can drive multiple fans.
+                 */
+                cpu_fan: fan@1 {
+                        compatible = "pmbus-fan";
+                        reg = <1>;
+                        mode = "rpm";
+                        tach-pulses = <1>;
+
+                        #cooling-cells = <2>;
+
+                        maxim,fan-rotor-input = "tach";
+                        maxim,tmp-hysteresis = <2>;
+                        maxim,fan-lookup-table = <
+                        /*  Temperature    RPM  */
+                                 0        1000
+                                10        2000
+                                20        3000
+                                30        4000
+                                40        5000
+                                50        6000
+                                60        7000
+                                70        8000
+                        >;
+                };
+
+                cpu_temp: sensor@6 {
+                        compatible = "pmbus-temperature";
+                        reg = <6>;
+
+                        maxim,tmp-offset = <0>;
+                        maxim,tmp-fans = <&cpu_fan>;
+                };
+	};
diff --git a/Documentation/devicetree/bindings/i2c/i2c-npcm7xx.txt b/Documentation/devicetree/bindings/i2c/i2c-npcm7xx.txt
new file mode 100644
index 0000000..0ecae95
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-npcm7xx.txt
@@ -0,0 +1,29 @@
+Nuvoton NPCM7XX I2C bus
+
+The NPCM750x includes sixteen I2C bus controllers. All Controllers support
+both master and slave mode. Each controller has two 16 byte HW FIFO for TX and
+RX.
+
+Required properties:
+- compatible      : must be "nuvoton,npcm750-i2c"
+- reg             : Offset and length of the register set for the device.
+- interrupts      : Contain the I2C interrupt with flags for falling edge.
+- clocks          : phandle of I2C reference clock.
+
+Optional:
+- bus-frequency   : Contain the I2C bus frequency,
+			the default I2C bus frequency is 100000.
+- pinctrl-0       : must be <&smbX_pins>, X is module number
+			(on NPCM7XX it's 0 to 15)
+- pinctrl-names   : should be set to "default"
+Example:
+
+	i2c0: i2c@80000 {
+		compatible = "nuvoton,npcm750-i2c";
+		reg = <0x80000 0x1000>;
+		clocks = <&clk NPCM7XX_CLK_APB2>;
+		bus-frequency = <100000>;
+		interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&smb0_pins>;
+	};
diff --git a/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt b/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt
new file mode 100644
index 0000000..1b8132c
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt
@@ -0,0 +1,35 @@
+Nuvoton NPCM Analog to Digital Converter (ADC)
+
+The NPCM ADC is a 10-bit converter for eight channel inputs.
+
+Required properties:
+- compatible: "nuvoton,npcm750-adc" for the NPCM7XX BMC.
+- reg: specifies physical base address and size of the registers.
+- interrupts: Contain the ADC interrupt with flags for falling edge.
+
+Optional properties:
+- clocks: phandle of ADC reference clock, in case the clock is not
+		  added the ADC will use the default ADC sample rate.
+- vref-supply: The regulator supply ADC reference voltage, in case the
+			   vref-supply is not added the ADC will use internal voltage
+			   reference.
+
+Required Node in the NPCM7xx BMC:
+An additional register is present in the NPCM7xx SOC which is
+assumed to be in the same device tree, with and marked as
+compatible with "nuvoton,npcm750-rst".
+
+Example:
+
+adc: adc@f000c000 {
+	compatible = "nuvoton,npcm750-adc";
+	reg = <0xf000c000 0x8>;
+	interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
+	clocks = <&clk NPCM7XX_CLK_ADC>;
+};
+
+rst: rst@f0801000 {
+	compatible = "nuvoton,npcm750-rst", "syscon",
+	"simple-mfd";
+	reg = <0xf0801000 0x6C>;
+};
diff --git a/Documentation/devicetree/bindings/media/aspeed-video.txt b/Documentation/devicetree/bindings/media/aspeed-video.txt
index 78b464a..346c2d3 100644
--- a/Documentation/devicetree/bindings/media/aspeed-video.txt
+++ b/Documentation/devicetree/bindings/media/aspeed-video.txt
@@ -14,6 +14,11 @@
 			the VE
  - interrupts:		the interrupt associated with the VE on this platform
 
+Optional properties:
+ - memory-region:
+	phandle to a memory region to allocate from, as defined in
+	Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
+
 Example:
 
 video-engine@1e700000 {
@@ -23,4 +28,5 @@
     clock-names = "vclk", "eclk";
     resets = <&syscon ASPEED_RESET_VIDEO>;
     interrupts = <7>;
+    memory-region = <&video_engine_memory>
 };
diff --git a/Documentation/devicetree/bindings/mfd/intel-peci-client.txt b/Documentation/devicetree/bindings/mfd/intel-peci-client.txt
new file mode 100644
index 0000000..5d1d5d0
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/intel-peci-client.txt
@@ -0,0 +1,34 @@
+* Intel PECI client bindings
+
+PECI (Platform Environment Control Interface) is a one-wire bus interface that
+provides a communication channel from PECI clients in Intel processors and
+chipset components to external monitoring or control devices. PECI is designed
+to support the following sideband functions:
+
+- Processor and DRAM thermal management
+- Platform Manageability
+- Processor Interface Tuning and Diagnostics
+- Failure Analysis
+
+Required properties:
+- compatible : Should be "intel,peci-client".
+- reg        : Should contain address of a client CPU. According to the PECI
+	       specification, client addresses start from 0x30.
+
+Example:
+	peci-bus@0 {
+		compatible = "vendor,soc-peci";
+		reg = <0x0 0x1000>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		peci-client@30 {
+			compatible = "intel,peci-client";
+			reg = <0x30>;
+		};
+
+		peci-client@31 {
+			compatible = "intel,peci-client";
+			reg = <0x31>;
+		};
+	};
diff --git a/Documentation/devicetree/bindings/misc/aspeed-p2a-ctrl.txt b/Documentation/devicetree/bindings/misc/aspeed-p2a-ctrl.txt
new file mode 100644
index 0000000..854bd67
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/aspeed-p2a-ctrl.txt
@@ -0,0 +1,47 @@
+======================================================================
+Device tree bindings for Aspeed AST2400/AST2500 PCI-to-AHB Bridge Control Driver
+======================================================================
+
+The bridge is available on platforms with the VGA enabled on the Aspeed device.
+In this case, the host has access to a 64KiB window into all of the BMC's
+memory.  The BMC can disable this bridge.  If the bridge is enabled, the host
+has read access to all the regions of memory, however the host only has read
+and write access depending on a register controlled by the BMC.
+
+Required properties:
+===================
+
+ - compatible: must be one of:
+	- "aspeed,ast2400-p2a-ctrl"
+	- "aspeed,ast2500-p2a-ctrl"
+
+Optional properties:
+===================
+
+- memory-region: A phandle to a reserved_memory region to be used for the PCI
+		to AHB mapping
+
+The p2a-control node should be the child of a syscon node with the required
+property:
+
+- compatible : Should be one of the following:
+		"aspeed,ast2400-scu", "syscon", "simple-mfd"
+		"aspeed,g4-scu", "syscon", "simple-mfd"
+		"aspeed,ast2500-scu", "syscon", "simple-mfd"
+		"aspeed,g5-scu", "syscon", "simple-mfd"
+
+Example
+===================
+
+g4 Example
+----------
+
+syscon: scu@1e6e2000 {
+	compatible = "aspeed,ast2400-scu", "syscon", "simple-mfd";
+	reg = <0x1e6e2000 0x1a8>;
+
+	p2a: p2a-control {
+		compatible = "aspeed,ast2400-p2a-ctrl";
+		memory-region = <&reserved_memory>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/mtd/npcm-fiu.txt b/Documentation/devicetree/bindings/mtd/npcm-fiu.txt
new file mode 100644
index 0000000..9746cb5
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/npcm-fiu.txt
@@ -0,0 +1,64 @@
+* Nuvoton FLASH Interface Unit (FIU) SPI Controller
+
+NPCM FIU supports single, dual and quad communication interface.
+
+The NPCM7XX supports three FIU modules,
+FIU0 and FIUx supports two chip selects,
+FIU3 support four chip select.
+
+Required properties:
+  - compatible : "nuvoton,npcm750-fiu" for the NPCM7XX BMC
+  - #address-cells : should be 1.
+  - #size-cells : should be 0.
+  - reg : the first contains the register location and length,
+          the second contains the memory mapping address and length
+  - reg-names: Should contain the reg names "control" and "memory"
+  - clocks : phandle of F reference clock.
+
+Required properties in case the pins can be muxed:
+  - pinctrl-names : a pinctrl state named "default" must be defined.
+  - pinctrl-0 : phandle referencing pin configuration of the device.
+
+Optional property:
+  - spix-mode: enable spix-mode for an expansion bus to an ASIC or CPLD.
+
+The SPI device must be a child of the FIU node and must have a
+compatible property as specified in bindings/mtd/jedec,spi-nor.txt
+
+Required property:
+- reg: chip select number.
+
+Optional property:
+- spi-rx-bus-width: see ../spi/spi-bus.txt for the description.
+
+Aliases:
+- All the FIU controller nodes should be represented in the aliases node using
+  the following format 'fiu{n}' where n is a unique number for the alias.
+  In the NPCM7XX BMC:
+  		fiu0 represent fiu 0 controller
+  		fiu1 represent fiu 3 controller
+  		fiu2 represent fiu x controller
+
+Example:
+fiu3: fiu@c00000000 {
+	compatible = "nuvoton,npcm750-fiu";
+	#address-cells = <1>;
+	#size-cells = <0>;
+	reg = <0xfb000000 0x1000>, <0x80000000 0x10000000>;
+	reg-names = "control", "memory";
+	clocks = <&clk NPCM7XX_CLK_AHB>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi3_pins>;
+	spi-nor@0 {
+		compatible = "jedec,spi-nor";
+		spi-rx-bus-width = <2>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0>;
+		partition@0 {
+			label = "flash_data";
+			reg = <0x0 0x800000>;
+		};
+	};
+};
+
diff --git a/Documentation/devicetree/bindings/net/nuvoton,npcm7xx-emc.txt b/Documentation/devicetree/bindings/net/nuvoton,npcm7xx-emc.txt
new file mode 100644
index 0000000..4227597
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/nuvoton,npcm7xx-emc.txt
@@ -0,0 +1,36 @@
+Nuvoton NPCM7XX 10/100 Ethernet MAC Controller (EMC)
+
+The NPCM750x provides two identical Ethernet MAC Controllers
+for WAN/LAN applications
+
+Required properties:
+- device_type     : Should be "network"
+- compatible      : "nuvoton,npcm750-emc" for Poleg NPCM750.
+- reg             : Offset and length of the register set for the device.
+- interrupts      : Contain the emc interrupts with flags for falling edge.
+                    first interrupt dedicated to Txirq
+                    second interrupt dedicated to Rxirq
+- phy-mode        : Should be "rmii" (see ethernet.txt in the same directory)
+- clocks          : phandle of emc reference clock.
+- use-ncsi        : Use the NC-SI stack instead of an MDIO PHY
+
+Example:
+
+emc0: eth@f0825000 {
+	device_type = "network";
+	compatible = "nuvoton,npcm750-emc";
+	reg = <0xf0825000 0x1000>;
+	interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>,
+	             <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
+	phy-mode = "rmii";
+	clocks = <&clk NPCM7XX_CLK_AHB>;
+
+	#use-ncsi; /* add this to support ncsi */
+
+	clock-names = "clk_emc";
+	pinctrl-names = "default";
+	pinctrl-0 = <&r1_pins
+	             &r1err_pins
+	             &r1md_pins>;
+	status = "okay";
+};
\ No newline at end of file
diff --git a/Documentation/devicetree/bindings/peci/peci-aspeed.txt b/Documentation/devicetree/bindings/peci/peci-aspeed.txt
new file mode 100644
index 0000000..cdca73a
--- /dev/null
+++ b/Documentation/devicetree/bindings/peci/peci-aspeed.txt
@@ -0,0 +1,55 @@
+Device tree configuration for PECI buses on the AST24XX and AST25XX SoCs.
+
+Required properties:
+- compatible        : Should be one of:
+			"aspeed,ast2400-peci"
+			"aspeed,ast2500-peci"
+- reg               : Should contain PECI controller registers location and
+		      length.
+- #address-cells    : Should be <1> required to define a client address.
+- #size-cells       : Should be <0> required to define a client address.
+- interrupts        : Should contain PECI controller interrupt.
+- clocks            : Should contain clock source for PECI controller. Should
+		      reference the external oscillator clock in the second
+		      cell.
+- resets            : Should contain phandle to reset controller with the reset
+		      number in the second cell.
+- clock-frequency   : Should contain the operation frequency of PECI controller
+		      in units of Hz.
+		      187500 ~ 24000000
+
+Optional properties:
+- msg-timing        : Message timing negotiation period. This value will
+		      determine the period of message timing negotiation to be
+		      issued by PECI controller. The unit of the programmed
+		      value is four times of PECI clock period.
+		      0 ~ 255 (default: 1)
+- addr-timing       : Address timing negotiation period. This value will
+		      determine the period of address timing negotiation to be
+		      issued by PECI controller. The unit of the programmed
+		      value is four times of PECI clock period.
+		      0 ~ 255 (default: 1)
+- rd-sampling-point : Read sampling point selection. The whole period of a bit
+		      time will be divided into 16 time frames. This value will
+		      determine the time frame in which the controller will
+		      sample PECI signal for data read back. Usually in the
+		      middle of a bit time is the best.
+		      0 ~ 15 (default: 8)
+- cmd-timeout-ms    : Command timeout in units of ms.
+		      1 ~ 60000 (default: 1000)
+
+Example:
+	peci0: peci-bus@0 {
+		compatible = "aspeed,ast2500-peci";
+		reg = <0x0 0x60>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <15>;
+		clocks = <&syscon ASPEED_CLK_GATE_REFCLK>;
+		resets = <&syscon ASPEED_RESET_PECI>;
+		clock-frequency = <24000000>;
+		msg-timing = <1>;
+		addr-timing = <1>;
+		rd-sampling-point = <8>;
+		cmd-timeout-ms = <1000>;
+	};
diff --git a/Documentation/devicetree/bindings/peci/peci-npcm.txt b/Documentation/devicetree/bindings/peci/peci-npcm.txt
new file mode 100644
index 0000000..ea5bead
--- /dev/null
+++ b/Documentation/devicetree/bindings/peci/peci-npcm.txt
@@ -0,0 +1,38 @@
+Nuvoton NPCM Platform Environment Control Interface (PECI) bus
+
+Required properties:
+- compatible        : "nuvoton,npcm750-peci" for the NPCM7XX BMC.
+- reg               : specifies PECI physical base address and size of the registers.
+- #address-cells    : Should be <1> required to define a client address.
+- #size-cells       : Should be <0> required to define a client address.
+- interrupts        : Contain the PECI interrupt with flags for falling edge.
+- clocks            : phandle of PECI reference clock.
+
+Optional properties:
+- cmd-timeout-ms    : Command timeout in units of ms.
+                      1 ~ 60000 (default: 1000)
+- pull-down         : Defines the PECI I/O internal pull down operation.
+		      0 - pull down always enable (default)
+		      1 - pull down only during transactions.
+		      2 - pull down always disable.
+- host-neg-bit-rate : Define host negotiation bit rate divider.
+                      the host negotiation bit rate calculate with formula:
+                      clock frequency[Hz] / [4 x {host-neg-bit-rate + 1}]
+		      7 ~ 31 (default: 15)
+- high-volt-range   : Adapts PECI I/O interface to voltage range, the property
+                      is a boolean parameter define as follow:
+		      false - PECI I/O interface voltage range of 0.8-1.06V. (default)
+		      true - PECI I/O interface voltage range of 0.95-1.26V.
+
+Example:
+	peci: peci-bus@100000 {
+		compatible = "nuvoton,npcm750-peci";
+		reg = <0x100000 0x200>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk NPCM7XX_CLK_APB3>;
+		cmd-timeout-ms = <1000>;
+		pull-down = <0>;
+		host-neg-bit-rate = <15>;
+	};
diff --git a/Documentation/devicetree/bindings/peci/peci.txt b/Documentation/devicetree/bindings/peci/peci.txt
new file mode 100644
index 0000000..71f26c7
--- /dev/null
+++ b/Documentation/devicetree/bindings/peci/peci.txt
@@ -0,0 +1,43 @@
+Generic device tree configuration for PECI adapters
+===================================================
+
+Required properties:
+- #address-cells : Should be <1>. Read more about client addresses below.
+- #size-cells    : Should be <0>. Read more about client addresses below.
+
+The cells properties above define that an address of CPU clients of a PECI bus
+are described by a single value.
+
+Example:
+	peci0: peci-bus@0 {
+		compatible = "vendor,soc-peci";
+		reg = <0x0 0x1000>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+	};
+
+Generic device tree configuration for PECI clients
+==================================================
+
+Required properties:
+- compatible : Should contain name of PECI client.
+- reg        : Should contain address of a client CPU. According to the PECI
+	       specification, client addresses start from 0x30.
+
+Example:
+	peci-bus@0 {
+		compatible = "vendor,soc-peci";
+		reg = <0x0 0x1000>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		peci-client@30 {
+			compatible = "intel,peci-client";
+			reg = <0x30>;
+		};
+
+		peci-client@31 {
+			compatible = "intel,peci-client";
+			reg = <0x31>;
+		};
+	};
diff --git a/Documentation/devicetree/bindings/rtc/rtc-aspeed.txt b/Documentation/devicetree/bindings/rtc/rtc-aspeed.txt
new file mode 100644
index 0000000..d31a4d2
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/rtc-aspeed.txt
@@ -0,0 +1,18 @@
+ASPEED BMC RTC
+==============
+
+Required properties:
+ - compatible: should be one of the following
+   * aspeed,ast2400-rtc for the ast2400
+   * aspeed,ast2500-rtc for the ast2500
+
+ - reg: physical base address of the controller and length of memory mapped
+   region
+
+Example:
+
+   rtc@1e781000 {
+           compatible = "aspeed,ast2400-rtc";
+           reg = <0x1e781000 0x18>;
+           status = "disabled";
+   };
diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml
index cc64ec6..eae6103 100644
--- a/Documentation/devicetree/bindings/trivial-devices.yaml
+++ b/Documentation/devicetree/bindings/trivial-devices.yaml
@@ -92,6 +92,8 @@
           - fsl,sgtl5000
             # G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire Interface
           - gmt,g751
+            # Infineon IR38064 Voltage Regulator
+          - infineon,ir38064
             # Infineon SLB9635 (Soft-) I2C TPM (old protocol, max 100khz)
           - infineon,slb9635tt
             # Infineon SLB9645 I2C TPM (new protocol, max 400khz)
@@ -102,6 +104,8 @@
           - isil,isl29028
             # Intersil ISL29030 Ambient Light and Proximity Sensor
           - isil,isl29030
+            # Intersil ISL68137 Digital Output Configurable PWM Controller
+          - isil,isl68137
             # 5 Bit Programmable, Pulse-Width Modulator
           - maxim,ds1050
             # Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs
diff --git a/Documentation/hwmon/isl68137 b/Documentation/hwmon/isl68137
new file mode 100644
index 0000000..92e5c5f
--- /dev/null
+++ b/Documentation/hwmon/isl68137
@@ -0,0 +1,72 @@
+Kernel driver isl68137
+======================
+
+Supported chips:
+  * Intersil ISL68137
+    Prefix: 'isl68137'
+    Addresses scanned: -
+    Datasheet: Publicly available at the Intersil website
+      https://www.intersil.com/content/dam/Intersil/documents/isl6/isl68137.pdf
+
+Authors:
+        Maxim Sloyko <maxims@google.com>
+        Robert Lippert <rlippert@google.com>
+        Patrick Venture <venture@google.com>
+
+Description
+-----------
+
+Intersil ISL68137 is a digital output 7-phase configurable PWM
+controller with an AVSBus interface.
+
+Usage Notes
+-----------
+
+This driver does not probe for PMBus devices. You will have to instantiate
+devices explicitly.
+
+The ISL68137 AVS operation mode must be enabled/disabled at runtime.
+
+Beyond the normal sysfs pmbus attributes, the driver exposes a control attribute.
+
+Additional Sysfs attributes
+---------------------------
+
+avs(0|1)_enable		Controls the AVS state of each rail.
+
+curr1_label		"iin"
+curr1_input		Measured input current
+curr1_crit		Critical maximum current
+curr1_crit_alarm	Current critical high alarm
+
+curr[2-3]_label		"iout[1-2]"
+curr[2-3]_input		Measured output current
+curr[2-3]_crit		Critical maximum current
+curr[2-3]_crit_alarm	Current critical high alarm
+
+in1_label		"vin"
+in1_input		Measured input voltage
+in1_lcrit		Critical minimum input voltage
+in1_lcrit_alarm		Input voltage critical low alarm
+in1_crit		Critical maximum input voltage
+in1_crit_alarm		Input voltage critical high alarm
+
+in[2-3]_label		"vout[1-2]"
+in[2-3]_input		Measured output voltage
+in[2-3]_lcrit		Critical minimum output voltage
+in[2-3]_lcrit_alarm	Output voltage critical low alarm
+in[2-3]_crit		Critical maximum output voltage
+in[2-3]_crit_alarm	Output voltage critical high alarm
+
+power1_label		"pin"
+power1_input		Measured input power
+power1_alarm		Input power high alarm
+
+power[2-3]_label	"pout[1-2]"
+power[2-3]_input	Measured output power
+
+temp[1-3]_input		Measured temperature
+temp[1-3]_crit		Critical high temperature
+temp[1-3]_crit_alarm	Chip temperature critical high alarm
+temp[1-3]_max		Maximum temperature
+temp[1-3]_max_alarm	Chip temperature high alarm
diff --git a/Documentation/hwmon/peci-cputemp b/Documentation/hwmon/peci-cputemp
new file mode 100644
index 0000000..821a925
--- /dev/null
+++ b/Documentation/hwmon/peci-cputemp
@@ -0,0 +1,78 @@
+Kernel driver peci-cputemp
+==========================
+
+Supported chips:
+	One of Intel server CPUs listed below which is connected to a PECI bus.
+		* Intel Xeon E5/E7 v3 server processors
+			Intel Xeon E5-14xx v3 family
+			Intel Xeon E5-24xx v3 family
+			Intel Xeon E5-16xx v3 family
+			Intel Xeon E5-26xx v3 family
+			Intel Xeon E5-46xx v3 family
+			Intel Xeon E7-48xx v3 family
+			Intel Xeon E7-88xx v3 family
+		* Intel Xeon E5/E7 v4 server processors
+			Intel Xeon E5-16xx v4 family
+			Intel Xeon E5-26xx v4 family
+			Intel Xeon E5-46xx v4 family
+			Intel Xeon E7-48xx v4 family
+			Intel Xeon E7-88xx v4 family
+		* Intel Xeon Scalable server processors
+			Intel Xeon Bronze family
+			Intel Xeon Silver family
+			Intel Xeon Gold family
+			Intel Xeon Platinum family
+	Addresses scanned: PECI client address 0x30 - 0x37
+	Datasheet: Available from http://www.intel.com/design/literature.htm
+
+Author:
+	Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+Description
+-----------
+
+This driver implements a generic PECI hwmon feature which provides Digital
+Thermal Sensor (DTS) thermal readings of the CPU package and CPU cores that are
+accessible using the PECI Client Command Suite via the processor PECI client.
+
+All temperature values are given in millidegree Celsius and will be measurable
+only when the target CPU is powered on.
+
+sysfs attributes
+----------------
+
+temp1_label		"Die"
+temp1_input		Provides current die temperature of the CPU package.
+temp1_max		Provides thermal control temperature of the CPU package
+			which is also known as Tcontrol.
+temp1_crit		Provides shutdown temperature of the CPU package which
+			is also known as the maximum processor junction
+			temperature, Tjmax or Tprochot.
+temp1_crit_hyst		Provides the hysteresis value from Tcontrol to Tjmax of
+			the CPU package.
+
+temp2_label		"Tcontrol"
+temp2_input		Provides current Tcontrol temperature of the CPU
+			package which is also known as Fan Temperature target.
+			Indicates the relative value from thermal monitor trip
+			temperature at which fans should be engaged.
+temp2_crit		Provides Tcontrol critical value of the CPU package
+			which is same to Tjmax.
+
+temp3_label		"Tthrottle"
+temp3_input		Provides current Tthrottle temperature of the CPU
+			package. Used for throttling temperature. If this value
+			is allowed and lower than Tjmax - the throttle will
+			occur and reported at lower than Tjmax.
+
+temp4_label		"Tjmax"
+temp4_input		Provides the maximum junction temperature, Tjmax of the
+			CPU package.
+
+temp[5-*]_label		Provides string "Core X", where X is resolved core
+			number.
+temp[5-*]_input		Provides current temperature of each core.
+temp[5-*]_max		Provides thermal control temperature of the core.
+temp[5-*]_crit		Provides shutdown temperature of the core.
+temp[5-*]_crit_hyst	Provides the hysteresis value from Tcontrol to Tjmax of
+			the core.
diff --git a/Documentation/hwmon/peci-dimmtemp b/Documentation/hwmon/peci-dimmtemp
new file mode 100644
index 0000000..c54f252
--- /dev/null
+++ b/Documentation/hwmon/peci-dimmtemp
@@ -0,0 +1,50 @@
+Kernel driver peci-dimmtemp
+===========================
+
+Supported chips:
+	One of Intel server CPUs listed below which is connected to a PECI bus.
+		* Intel Xeon E5/E7 v3 server processors
+			Intel Xeon E5-14xx v3 family
+			Intel Xeon E5-24xx v3 family
+			Intel Xeon E5-16xx v3 family
+			Intel Xeon E5-26xx v3 family
+			Intel Xeon E5-46xx v3 family
+			Intel Xeon E7-48xx v3 family
+			Intel Xeon E7-88xx v3 family
+		* Intel Xeon E5/E7 v4 server processors
+			Intel Xeon E5-16xx v4 family
+			Intel Xeon E5-26xx v4 family
+			Intel Xeon E5-46xx v4 family
+			Intel Xeon E7-48xx v4 family
+			Intel Xeon E7-88xx v4 family
+		* Intel Xeon Scalable server processors
+			Intel Xeon Bronze family
+			Intel Xeon Silver family
+			Intel Xeon Gold family
+			Intel Xeon Platinum family
+	Addresses scanned: PECI client address 0x30 - 0x37
+	Datasheet: Available from http://www.intel.com/design/literature.htm
+
+Author:
+	Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+Description
+-----------
+
+This driver implements a generic PECI hwmon feature which provides Digital
+Thermal Sensor (DTS) thermal readings of DIMM components that are accessible
+using the PECI Client Command Suite via the processor PECI client.
+
+All temperature values are given in millidegree Celsius and will be measurable
+only when the target CPU is powered on.
+
+sysfs attributes
+----------------
+
+temp[N]_label		Provides string "DIMM CI", where C is DIMM channel and
+			I is DIMM index of the populated DIMM.
+temp[N]_input		Provides current temperature of the populated DIMM.
+
+Note:
+	DIMM temperature attributes will appear when the client CPU's BIOS
+	completes memory training and testing.
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index c955814..aec1215 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -327,6 +327,8 @@
 0xB4	00-0F	linux/gpio.h		<mailto:linux-gpio@vger.kernel.org>
 0xB5	00-0F	uapi/linux/rpmsg.h	<mailto:linux-remoteproc@vger.kernel.org>
 0xB6	all	linux/fpga-dfl.h
+0xB7	00-0F	uapi/linux/peci-ioctl.h	PECI subsystem
+					<mailto:jae.hyun.yoo@linux.intel.com>
 0xC0	00-0F	linux/usb/iowarrior.h
 0xCA	00-0F	uapi/misc/cxl.h
 0xCA	10-2F	uapi/misc/ocxl.h
diff --git a/MAINTAINERS b/MAINTAINERS
index dce5c09..997e27a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1371,6 +1371,14 @@
 F:	arch/arm/boot/dts/aspeed-*
 N:	aspeed
 
+ARM/ASPEED PECI DRIVER
+M:	Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+M:	Jason M Biils <jason.m.bills@linux.intel.com>
+L:	linux-aspeed@lists.ozlabs.org (moderated for non-subscribers)
+S:	Maintained
+F:	Documentation/devicetree/bindings/peci/peci-aspeed.txt
+F:	drivers/peci/peci-aspeed.c
+
 ARM/CALXEDA HIGHBANK ARCHITECTURE
 M:	Rob Herring <robh@kernel.org>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
@@ -11904,6 +11912,20 @@
 S:	Maintained
 F:	drivers/platform/x86/peaq-wmi.c
 
+PECI SUBSYSTEM
+M:	Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+M:	Jason M Biils <jason.m.bills@linux.intel.com>
+L:	openbmc@lists.ozlabs.org (moderated for non-subscribers)
+S:	Maintained
+F:	Documentation/devicetree/bindings/peci/
+F:	drivers/mfd/intel-peci-client.c
+F:	drivers/peci/
+F:	drivers/hwmon/peci-*.c
+F:	drivers/hwmon/peci-hwmon.h
+F:	include/linux/mfd/intel-peci-client.h
+F:	include/linux/peci.h
+F:	include/uapi/linux/peci-ioctl.h
+
 PER-CPU MEMORY ALLOCATOR
 M:	Dennis Zhou <dennis@kernel.org>
 M:	Tejun Heo <tj@kernel.org>
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index bd40148..c01a7b1 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -1241,6 +1241,7 @@
 	aspeed-bmc-facebook-cmm.dtb \
 	aspeed-bmc-facebook-tiogapass.dtb \
 	aspeed-bmc-intel-s2600wf.dtb \
+	aspeed-bmc-lenovo-hr630.dtb \
 	aspeed-bmc-opp-lanyang.dtb \
 	aspeed-bmc-opp-palmetto.dtb \
 	aspeed-bmc-opp-romulus.dtb \
diff --git a/arch/arm/boot/dts/aspeed-ast2500-evb.dts b/arch/arm/boot/dts/aspeed-ast2500-evb.dts
index 2375449..5dbb33c1 100644
--- a/arch/arm/boot/dts/aspeed-ast2500-evb.dts
+++ b/arch/arm/boot/dts/aspeed-ast2500-evb.dts
@@ -13,12 +13,26 @@
 
 	chosen {
 		stdout-path = &uart5;
-		bootargs = "console=ttyS4,115200 earlyprintk";
+		bootargs = "console=tty0 console=ttyS4,115200 earlyprintk";
 	};
 
 	memory@80000000 {
+		device_type = "memory";
 		reg = <0x80000000 0x20000000>;
 	};
+
+	reserved-memory {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		gfx_memory: framebuffer {
+			size = <0x01000000>;
+			alignment = <0x01000000>;
+			compatible = "shared-dma-pool";
+			reusable;
+		};
+	};
 };
 
 &fmc {
@@ -27,6 +41,8 @@
 		status = "okay";
 		m25p,fast-read;
 		label = "bmc";
+		spi-max-frequency = <50000000>;
+#include "openbmc-flash-layout.dtsi"
 	};
 };
 
@@ -36,6 +52,7 @@
 		status = "okay";
 		m25p,fast-read;
 		label = "pnor";
+		spi-max-frequency = <100000000>;
 	};
 };
 
@@ -97,3 +114,8 @@
 &uhci {
 	status = "okay";
 };
+
+&gfx {
+     status = "okay";
+     memory-region = <&gfx_memory>;
+};
diff --git a/arch/arm/boot/dts/aspeed-bmc-facebook-tiogapass.dts b/arch/arm/boot/dts/aspeed-bmc-facebook-tiogapass.dts
index f8e7b71..73e58a8 100644
--- a/arch/arm/boot/dts/aspeed-bmc-facebook-tiogapass.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-facebook-tiogapass.dts
@@ -21,6 +21,17 @@
 	memory@80000000 {
 		reg = <0x80000000 0x20000000>;
 	};
+
+	iio-hwmon {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 0>, <&adc 1>, <&adc 2>, <&adc 3>,
+					<&adc 4>, <&adc 5>, <&adc 6>;
+	};
+
+	iio-hwmon-battery {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 7>;
+	};
 };
 
 &fmc {
@@ -56,6 +67,18 @@
 	status = "okay";
 };
 
+&kcs2 {
+	// BMC KCS channel 2
+	status = "okay";
+	kcs_addr = <0xca8>;
+};
+
+&kcs3 {
+	// BMC KCS channel 3
+	status = "okay";
+	kcs_addr = <0xca2>;
+};
+
 &mac0 {
 	status = "okay";
 
@@ -64,6 +87,10 @@
 	use-ncsi;
 };
 
+&adc {
+	status = "okay";
+};
+
 &i2c0 {
 	status = "okay";
 	//Airmax Conn B, CPU0 PIROM, CPU1 PIROM
@@ -122,6 +149,10 @@
 
 &i2c8 {
 	status = "okay";
+	tmp421@1f {
+		compatible = "ti,tmp421";
+		reg = <0x1f>;
+	};
 	//Mezz Sensor SMBus
 };
 
@@ -140,7 +171,7 @@
 	};
 
 	fan@1 {
-		reg = <0x00>;
-		aspeed,fan-tach-ch = /bits/ 8 <0x01>;
+		reg = <0x01>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x02>;
 	};
 };
diff --git a/arch/arm/boot/dts/aspeed-bmc-lenovo-hr630.dts b/arch/arm/boot/dts/aspeed-bmc-lenovo-hr630.dts
new file mode 100644
index 0000000..d3695a3
--- /dev/null
+++ b/arch/arm/boot/dts/aspeed-bmc-lenovo-hr630.dts
@@ -0,0 +1,566 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Device Tree file for Lenovo Hr630 platform
+ *
+ * Copyright (C) 2019-present Lenovo
+ */
+
+/dts-v1/;
+
+#include "aspeed-g5.dtsi"
+#include <dt-bindings/gpio/aspeed-gpio.h>
+
+/ {
+	model = "HR630 BMC";
+	compatible = "lenovo,hr630-bmc", "aspeed,ast2500";
+
+	aliases {
+		i2c14 = &i2c_rbp;
+		i2c15 = &i2c_fbp1;
+		i2c16 = &i2c_fbp2;
+		i2c17 = &i2c_fbp3;
+		i2c18 = &i2c_riser2;
+		i2c19 = &i2c_pcie4;
+		i2c20 = &i2c_riser1;
+		i2c21 = &i2c_ocp;
+	};
+
+	chosen {
+		stdout-path = &uart5;
+		bootargs = "console=tty0 console=ttyS4,115200 earlyprintk";
+	};
+
+	memory@80000000 {
+		device_type = "memory";
+		reg = <0x80000000 0x20000000>;
+	};
+
+	reserved-memory {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		flash_memory: region@98000000 {
+			no-map;
+			reg = <0x98000000 0x00100000>; /* 1M */
+		};
+
+		gfx_memory: framebuffer {
+			size = <0x01000000>;
+			alignment = <0x01000000>;
+			compatible = "shared-dma-pool";
+			reusable;
+		};
+	};
+
+	leds {
+		compatible = "gpio-leds";
+
+		heartbeat {
+			gpios = <&gpio ASPEED_GPIO(J, 1) GPIO_ACTIVE_LOW>;
+		};
+
+		fault {
+			gpios = <&gpio ASPEED_GPIO(J, 0) GPIO_ACTIVE_LOW>;
+		};
+	};
+
+	iio-hwmon {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 0>, <&adc 1>, <&adc 2>, <&adc 3>,
+		<&adc 4>, <&adc 5>, <&adc 6>, <&adc 7>,
+		<&adc 8>, <&adc 9>, <&adc 10>,
+		<&adc 12>, <&adc 13>, <&adc 14>;
+	};
+
+};
+
+&fmc {
+	status = "okay";
+	flash@0 {
+		status = "okay";
+		m25p,fast-read;
+		label = "bmc";
+		spi-max-frequency = <50000000>;
+#include "openbmc-flash-layout.dtsi"
+	};
+};
+
+&lpc_ctrl {
+	status = "okay";
+	memory-region = <&flash_memory>;
+	flash = <&spi1>;
+};
+
+&uart1 {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_txd1_default
+			&pinctrl_rxd1_default>;
+};
+
+&uart2 {
+	/* Rear RS-232 connector */
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_txd2_default
+			&pinctrl_rxd2_default
+			&pinctrl_nrts2_default
+			&pinctrl_ndtr2_default
+			&pinctrl_ndsr2_default
+			&pinctrl_ncts2_default
+			&pinctrl_ndcd2_default
+			&pinctrl_nri2_default>;
+};
+
+&uart3 {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_txd3_default
+			&pinctrl_rxd3_default>;
+};
+
+&uart5 {
+	status = "okay";
+};
+
+&ibt {
+	status = "okay";
+};
+
+&mac0 {
+	status = "okay";
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_rmii1_default>;
+	use-ncsi;
+};
+
+&mac1 {
+	status = "okay";
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_rgmii2_default &pinctrl_mdio2_default>;
+};
+
+&adc {
+	status = "okay";
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_adc0_default
+			&pinctrl_adc1_default
+			&pinctrl_adc2_default
+			&pinctrl_adc3_default
+			&pinctrl_adc4_default
+			&pinctrl_adc5_default
+			&pinctrl_adc6_default
+			&pinctrl_adc7_default
+			&pinctrl_adc8_default
+			&pinctrl_adc9_default
+			&pinctrl_adc10_default
+			&pinctrl_adc12_default
+			&pinctrl_adc13_default
+			&pinctrl_adc14_default>;
+};
+
+&i2c0 {
+	status = "okay";
+	/* temp1 inlet */
+	tmp75@4e {
+		compatible = "national,lm75";
+		reg = <0x4e>;
+	};
+};
+
+&i2c1 {
+	status = "okay";
+	/* temp2 outlet */
+	tmp75@4d {
+		compatible = "national,lm75";
+		reg = <0x4d>;
+	};
+};
+
+&i2c2 {
+	status = "okay";
+};
+
+&i2c3 {
+	status = "okay";
+};
+
+&i2c4 {
+	status = "okay";
+};
+
+&i2c5 {
+	status = "okay";
+};
+
+&i2c6 {
+	status = "okay";
+	/*	Slot 0,
+	 *	Slot 1,
+	 *	Slot 2,
+	 *	Slot 3
+	 */
+
+	i2c-switch@70 {
+		compatible = "nxp,pca9545";
+		reg = <0x70>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		i2c-mux-idle-disconnect;	/* may use mux@70 next. */
+
+		i2c_rbp: i2c@0 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0>;
+		};
+
+		i2c_fbp1: i2c@1 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <1>;
+		};
+
+		i2c_fbp2: i2c@2 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <2>;
+		};
+
+		i2c_fbp3: i2c@3 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <3>;
+		};
+	};
+};
+
+&i2c7 {
+	status = "okay";
+
+	/*	Slot 0,
+	 *	Slot 1,
+	 *	Slot 2,
+	 *	Slot 3
+	 */
+	i2c-switch@76 {
+		compatible = "nxp,pca9546";
+		reg = <0x76>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		i2c-mux-idle-disconnect;  /* may use mux@76 next. */
+
+		i2c_riser2: i2c@0 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0>;
+		};
+
+		i2c_pcie4: i2c@1 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <1>;
+		};
+
+		i2c_riser1: i2c@2 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <2>;
+		};
+
+		i2c_ocp: i2c@3 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <3>;
+		};
+	};
+};
+
+&i2c8 {
+	status = "okay";
+
+	eeprom@57 {
+		compatible = "atmel,24c256";
+		reg = <0x57>;
+		pagesize = <16>;
+	};
+};
+
+&i2c9 {
+	status = "okay";
+};
+
+&i2c10 {
+	status = "okay";
+};
+
+&i2c11 {
+	status = "okay";
+};
+
+&i2c12 {
+	status = "okay";
+};
+
+&ehci1 {
+	status = "okay";
+};
+
+&uhci {
+	status = "okay";
+};
+
+&gfx {
+	status = "okay";
+	memory-region = <&gfx_memory>;
+};
+
+&pwm_tacho {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_pwm0_default
+	&pinctrl_pwm1_default
+	&pinctrl_pwm2_default
+	&pinctrl_pwm3_default
+	&pinctrl_pwm4_default
+	&pinctrl_pwm5_default
+	&pinctrl_pwm6_default>;
+
+	fan@0 {
+		reg = <0x00>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x00>;
+	};
+
+	fan@1 {
+		reg = <0x00>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x01>;
+	};
+
+	fan@2 {
+		reg = <0x01>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x02>;
+	};
+
+	fan@3 {
+		reg = <0x01>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x03>;
+	};
+
+	fan@4 {
+		reg = <0x02>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x04>;
+	};
+
+	fan@5 {
+		reg = <0x02>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x05>;
+	};
+
+	fan@6 {
+		reg = <0x03>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x06>;
+	};
+
+	fan@7 {
+		reg = <0x03>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x07>;
+	};
+
+	fan@8 {
+		reg = <0x04>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x08>;
+	};
+
+	fan@9 {
+		reg = <0x04>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x09>;
+	};
+
+	fan@10 {
+		reg = <0x05>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x0a>;
+	};
+
+	fan@11 {
+		reg = <0x05>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x0b>;
+	};
+
+	fan@12 {
+		reg = <0x06>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x0c>;
+	};
+
+	fan@13 {
+		reg = <0x06>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x0d>;
+	};
+};
+
+&gpio {
+
+	pin_gpio_b5 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(B, 5) GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "IRQ_BMC_PCH_SMI_LPC_N";
+	};
+
+	pin_gpio_f0 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(F, 0) GPIO_ACTIVE_HIGH>;
+		output-low;
+		line-name = "IRQ_BMC_PCH_NMI_R";
+	};
+
+	pin_gpio_f3 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(F, 3) GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "I2C_BUS0_RST_OUT_N";
+	};
+
+	pin_gpio_f4 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(F, 4) GPIO_ACTIVE_HIGH>;
+		output-low;
+		line-name = "FM_SKT0_FAULT_LED";
+	};
+
+	pin_gpio_f5 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(F, 5) GPIO_ACTIVE_HIGH>;
+		output-low;
+		line-name = "FM_SKT1_FAULT_LED";
+	};
+
+	pin_gpio_g4 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(G, 4) GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "FAN_PWR_CTL_N";
+	};
+
+	pin_gpio_g7 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(G, 7) GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "RST_BMC_PCIE_I2CMUX_N";
+	};
+
+	pin_gpio_h2 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(H, 2) GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "PSU1_FFS_N_R";
+	};
+
+	pin_gpio_h3 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(H, 3) GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "PSU2_FFS_N_R";
+	};
+
+	pin_gpio_i3 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(I, 3) GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "BMC_INTRUDED_COVER";
+	};
+
+	pin_gpio_j2 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(J, 2) GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "BMC_BIOS_UPDATE_N";
+	};
+
+	pin_gpio_j3 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(J, 3) GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "RST_BMC_HDD_I2CMUX_N";
+	};
+
+	pin_gpio_s2 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(S, 2) GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "BMC_VGA_SW";
+	};
+
+	pin_gpio_s4 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(S, 4) GPIO_ACTIVE_HIGH>;
+		output;
+		line-name = "VBAT_EN_N";
+	};
+
+	pin_gpio_s6 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(S, 6) GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "PU_BMC_GPIOS6";
+	};
+
+	pin_gpio_y0 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(Y, 0) GPIO_ACTIVE_HIGH>;
+		output-low;
+		line-name = "BMC_NCSI_MUX_CTL_S0";
+	};
+
+	pin_gpio_y1 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(Y, 1) GPIO_ACTIVE_HIGH>;
+		output-low;
+		line-name = "BMC_NCSI_MUX_CTL_S1";
+	};
+
+	pin_gpio_z0 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(Z, 0) GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "I2C_RISER2_INT_N";
+	};
+
+	pin_gpio_z2 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(Z, 2) GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "I2C_RISER2_RESET_N";
+	};
+
+	pin_gpio_z3 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(Z, 3) GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "FM_BMC_PCH_SCI_LPC_N";
+	};
+
+	pin_gpio_z7 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(Z, 7) GPIO_ACTIVE_HIGH>;
+		output-low;
+		line-name = "BMC_POST_CMPLT_N";
+	};
+
+	pin_gpio_aa0 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(AA, 0) GPIO_ACTIVE_HIGH>;
+		output-low;
+		line-name = "HOST_BMC_USB_SEL";
+	};
+
+	pin_gpio_aa5 {
+		gpio-hog;
+		gpios = <ASPEED_GPIO(AA, 5) GPIO_ACTIVE_HIGH>;
+		output-high;
+		line-name = "I2C_BUS1_RST_OUT_N";
+	};
+
+};
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-lanyang.dts b/arch/arm/boot/dts/aspeed-bmc-opp-lanyang.dts
index 024e52a..fcc8d19 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-lanyang.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-lanyang.dts
@@ -169,6 +169,10 @@
 	snoop-ports = <0x80>;
 };
 
+&mbox {
+	status = "okay";
+};
+
 &uart5 {
 	status = "okay";
 };
@@ -322,3 +326,5 @@
 &adc {
 	status = "okay";
 };
+
+#include "ibm-power9-dual.dtsi"
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
index 9aa1d44..2f2cb0e 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
@@ -32,9 +32,9 @@
 			no-map;
 		};
 
-		flash_memory: region@98000000 {
+		flash_memory: region@5c000000 {
 			no-map;
-			reg = <0x98000000 0x01000000>; /* 16MB */
+			reg = <0x5C000000 0x02000000>; /* 32MB */
 		};
 	};
 
@@ -87,6 +87,7 @@
 		status = "okay";
 		m25p,fast-read;
 		label = "bmc";
+		spi-max-frequency = <50000000>;
 #include "openbmc-flash-layout.dtsi"
 	};
 };
@@ -99,6 +100,7 @@
 	flash@0 {
 		status = "okay";
 		m25p,fast-read;
+		spi-max-frequency = <50000000>;
 		label = "pnor";
 	};
 };
@@ -169,6 +171,11 @@
 
 &i2c3 {
 	status = "okay";
+
+	occ-hwmon@50 {
+		compatible = "ibm,p8-occ-hwmon";
+		reg = <0x50>;
+	};
 };
 
 &i2c4 {
@@ -195,6 +202,10 @@
 	status = "okay";
 };
 
+&mbox {
+	status = "okay";
+};
+
 &lpc_ctrl {
 	status = "okay";
 	memory-region = <&flash_memory>;
@@ -342,3 +353,25 @@
 		line-name = "BMC_TPM_INT_N";
 	};
 };
+
+&fsi {
+	cfam@0,0 {
+		reg = <0 0>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		chip-id = <0>;
+
+		scom@1000 {
+			compatible = "ibm,fsi2pib";
+			reg = <0x1000 0x400>;
+		};
+
+		fsi_hub0: hub@3400 {
+			compatible = "ibm,fsi-master-hub";
+			reg = <0x3400 0x400>;
+			#address-cells = <2>;
+			#size-cells = <0>;
+			no-scan-on-init;
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-romulus.dts b/arch/arm/boot/dts/aspeed-bmc-opp-romulus.dts
index 76fe994..8c47c60 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-romulus.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-romulus.dts
@@ -35,6 +35,20 @@
 			reg = <0x9ef00000 0x00100000>;
 			no-map;
 		};
+
+		gfx_memory: framebuffer {
+			size = <0x01000000>;
+			alignment = <0x01000000>;
+			compatible = "shared-dma-pool";
+			reusable;
+		};
+
+		video_engine_memory: jpegbuffer {
+			size = <0x02000000>;	/* 32M */
+			alignment = <0x01000000>;
+			compatible = "shared-dma-pool";
+			reusable;
+		};
 	};
 
 	leds {
@@ -98,6 +112,7 @@
 		status = "okay";
 		m25p,fast-read;
 		label = "bmc";
+		spi-max-frequency = <50000000>;
 #include "openbmc-flash-layout.dtsi"
 	};
 };
@@ -111,6 +126,7 @@
 		status = "okay";
 		m25p,fast-read;
 		label = "pnor";
+		spi-max-frequency = <100000000>;
 	};
 };
 
@@ -120,6 +136,10 @@
 	flash = <&spi1>;
 };
 
+&mbox {
+       status = "okay";
+};
+
 &uart1 {
 	/* Rear RS-232 connector */
 	status = "okay";
@@ -296,3 +316,15 @@
 &adc {
 	status = "okay";
 };
+
+&gfx {
+     status = "okay";
+     memory-region = <&gfx_memory>;
+};
+
+&video {
+	status = "okay";
+	memory-region = <&video_engine_memory>;
+};
+
+#include "ibm-power9-dual.dtsi"
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts b/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
index ad54117..e03de4e 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-witherspoon.dts
@@ -26,6 +26,20 @@
 			no-map;
 			reg = <0x98000000 0x04000000>; /* 64M */
 		};
+
+		gfx_memory: framebuffer {
+			size = <0x01000000>;
+			alignment = <0x01000000>;
+			compatible = "shared-dma-pool";
+			reusable;
+		};
+
+		video_engine_memory: jpegbuffer {
+			size = <0x02000000>;	/* 32MM */
+			alignment = <0x01000000>;
+			compatible = "shared-dma-pool";
+			reusable;
+		};
 	};
 
 	gpio-keys {
@@ -186,13 +200,48 @@
 		status = "okay";
 		label = "bmc";
 		m25p,fast-read;
-#include "openbmc-flash-layout.dtsi"
+		spi-max-frequency = <50000000>;
+		partitions {
+			#address-cells = < 1 >;
+			#size-cells = < 1 >;
+			compatible = "fixed-partitions";
+			u-boot@0 {
+				reg = < 0 0x60000 >;
+				label = "u-boot";
+			};
+			u-boot-env@60000 {
+				reg = < 0x60000 0x20000 >;
+				label = "u-boot-env";
+			};
+			obmc-ubi@80000 {
+				reg = < 0x80000 0x1F80000 >;
+				label = "obmc-ubi";
+			};
+		};
 	};
 
 	flash@1 {
 		status = "okay";
-		label = "alt";
+		label = "alt-bmc";
 		m25p,fast-read;
+		spi-max-frequency = <50000000>;
+		partitions {
+			#address-cells = < 1 >;
+			#size-cells = < 1 >;
+			compatible = "fixed-partitions";
+			u-boot@0 {
+				reg = < 0 0x60000 >;
+				label = "alt-u-boot";
+			};
+			u-boot-env@60000 {
+				reg = < 0x60000 0x20000 >;
+				label = "alt-u-boot-env";
+			};
+			obmc-ubi@80000 {
+				reg = < 0x80000 0x1F80000 >;
+				label = "alt-obmc-ubi";
+			};
+		};
 	};
 };
 
@@ -205,6 +254,7 @@
 		status = "okay";
 		label = "pnor";
 		m25p,fast-read;
+		spi-max-frequency = <100000000>;
 	};
 };
 
@@ -239,6 +289,10 @@
 	flash = <&spi1>;
 };
 
+&mbox {
+	status = "okay";
+};
+
 &mac0 {
 	status = "okay";
 	pinctrl-names = "default";
@@ -269,6 +323,58 @@
 		reg = <0x52>;
 		#address-cells = <1>;
 		#size-cells = <0>;
+
+		fan@0 {
+			compatible = "pmbus-fan";
+			reg = <0>;
+			tach-pulses = <2>;
+			maxim,fan-rotor-input = "tach";
+			maxim,fan-pwm-freq = <25000>;
+			maxim,fan-dual-tach;
+			maxim,fan-no-watchdog;
+			maxim,fan-no-fault-ramp;
+			maxim,fan-ramp = <2>;
+			maxim,fan-fault-pin-mon;
+		};
+
+		fan@1 {
+			compatible = "pmbus-fan";
+			reg = <1>;
+			tach-pulses = <2>;
+			maxim,fan-rotor-input = "tach";
+			maxim,fan-pwm-freq = <25000>;
+			maxim,fan-dual-tach;
+			maxim,fan-no-watchdog;
+			maxim,fan-no-fault-ramp;
+			maxim,fan-ramp = <2>;
+			maxim,fan-fault-pin-mon;
+		};
+
+		fan@2 {
+			compatible = "pmbus-fan";
+			reg = <2>;
+			tach-pulses = <2>;
+			maxim,fan-rotor-input = "tach";
+			maxim,fan-pwm-freq = <25000>;
+			maxim,fan-dual-tach;
+			maxim,fan-no-watchdog;
+			maxim,fan-no-fault-ramp;
+			maxim,fan-ramp = <2>;
+			maxim,fan-fault-pin-mon;
+		};
+
+		fan@3 {
+			compatible = "pmbus-fan";
+			reg = <3>;
+			tach-pulses = <2>;
+			maxim,fan-rotor-input = "tach";
+			maxim,fan-pwm-freq = <25000>;
+			maxim,fan-dual-tach;
+			maxim,fan-no-watchdog;
+			maxim,fan-no-fault-ramp;
+			maxim,fan-ramp = <2>;
+			maxim,fan-fault-pin-mon;
+		};
 	};
 
 	dps: dps310@76 {
@@ -565,6 +671,7 @@
 
 &gfx {
 	status = "okay";
+	memory-region = <&gfx_memory>;
 };
 
 &pinctrl {
@@ -592,3 +699,14 @@
 &adc {
 	status = "okay";
 };
+
+&vhub {
+	status = "okay";
+};
+
+&video {
+	status = "okay";
+	memory-region = <&video_engine_memory>;
+};
+
+#include "ibm-power9-dual.dtsi"
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts b/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts
index 2c5aa90..a6626d4 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-zaius.dts
@@ -7,6 +7,14 @@
 	model = "Zaius BMC";
 	compatible = "ingrasys,zaius-bmc", "aspeed,ast2500";
 
+	aliases {
+		i2c15 = &i2cpcie0;
+		i2c16 = &i2cpcie1;
+		i2c17 = &i2cpcie2;
+		i2c19 = &i2cpcie3;
+		i2c20 = &i2cpcie4;
+	};
+
 	chosen {
 		stdout-path = &uart5;
 		bootargs = "console=ttyS4,115200 earlyprintk";
@@ -122,6 +130,7 @@
 		status = "okay";
 		label = "bmc";
 		m25p,fast-read;
+		spi-max-frequency = <50000000>;
 #include "openbmc-flash-layout.dtsi"
 	};
 };
@@ -135,6 +144,7 @@
 		status = "okay";
 		label = "pnor";
 		m25p,fast-read;
+		spi-max-frequency = <100000000>;
 	};
 };
 
@@ -170,6 +180,9 @@
 	snoop-ports = <0x80>;
 };
 
+&mbox {
+       status = "okay";
+};
 
 &uart5 {
 	status = "okay";
@@ -223,6 +236,27 @@
 		reg = <0x71>;
 		#address-cells = <1>;
 		#size-cells = <0>;
+
+		i2cpcie0: i2c@0 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0>;
+		};
+		i2cpcie1: i2c@1 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <1>;
+		};
+		i2cpcie2: i2c@2 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <2>;
+		};
+		i2ctpm: i2c@3 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <3>;
+		};
 	};
 
 	/* MUX1 PCA9546A @71h
@@ -253,6 +287,17 @@
 		reg = <0x71>;
 		#address-cells = <1>;
 		#size-cells = <0>;
+
+		i2cpcie3: i2c@0 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0>;
+		};
+		i2cpcie4: i2c@1 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <1>;
+		};
 	};
 
 	/* MUX1 PCA9546A @71h
@@ -296,33 +341,98 @@
 				reg = <0x54>;
 			};
 		};
+
+	};
+
+	vrm@64 {
+		compatible = "isil,isl68137";
+		reg = <0x64>;
+	};
+
+	vrm@40 {
+		compatible = "isil,isl68137";
+		reg = <0x40>;
+	};
+
+	vrm@60 {
+		compatible = "isil,isl68137";
+		reg = <0x60>;
+	};
+
+	vrm@43 {
+		compatible = "infineon,ir38064";
+		reg = <0x43>;
+	};
+
+	vrm@41 {
+		compatible = "isil,isl68137";
+		reg = <0x41>;
 	};
 
 	/* Master selector PCA9541A @70h (other master: CPU0)
 	 *   LM5066I PMBUS @10h
 	 */
 
-	/* 12V Quarter Brick DC/DC Converter Q54SJ12050 @61h */
-	power-brick@61 {
+	/*
+	 * Brick will be one of these types/addresses.  Depending
+	 * on the board SKU only one is actually present and will successfully
+	 * instantiate while the others will fail the probe operation.
+	 * These are the PVT (and presumably beyond) addresses:
+	 *    12V Quarter Brick DC/DC Converter Q54SJ12050 @6Ah
+	 *    12V Quarter Brick DC/DC Converter Q54SH12050 @30h
+	 */
+	power-brick@6a {
 		compatible = "delta,dps800";
-		reg = <0x61>;
+		reg = <0x6a>;
+	};
+	power-brick@30 {
+		compatible = "delta,dps800";
+		reg = <0x30>;
 	};
 
 	/* CPU0 VR ISL68137 0.7V, 0.96V PMBUS @64h */
 	/* CPU0 VR ISL68137 1.2V CH03 PMBUS @40h */
 	/* CPU0 VR ISL68137 0.8V PMBUS @60h */
-	/* CPU0 VR 1.0V IR38064 I2C @11h, PMBUS @41h */
+	/* CPU0 VR 1.0V IR38064 I2C @11h, PMBUS @43h */
 	/* CPU0 VR ISL68137 1.2V CH47 PMBUS @41h */
+	/* Master selector PCA9541A @70h (other master: CPU0)
+	 *   LM5066I PMBUS @10h
+	 */
 };
 
 &i2c8 {
 	status = "okay";
 
-	/* CPU1 VR ISL68137 0.7V, 0.96V PMBUS @65h */
-	/* CPU1 VR ISL68137 1.2V CH03 PMBUS @44h */
-	/* CPU1 VR ISL68137 0.8V PMBUS @61h */
+	vrm@64 {
+		compatible = "isil,isl68137";
+		reg = <0x64>;
+	};
+
+	vrm@40 {
+		compatible = "isil,isl68137";
+		reg = <0x40>;
+	};
+
+	vrm@41 {
+		compatible = "isil,isl68137";
+		reg = <0x41>;
+	};
+
+	vrm@42 {
+		compatible = "infineon,ir38064";
+		reg = <0x42>;
+	};
+
+	vrm@60 {
+		compatible = "isil,isl68137";
+		reg = <0x60>;
+	};
+
+	/* CPU1 VR ISL68137 0.7V, 0.96V PMBUS @64h */
+	/* CPU1 VR ISL68137 1.2V CH03 PMBUS @40h */
+	/* CPU1 VR ISL68137 1.2V CH47 PMBUS @41h */
 	/* CPU1 VR 1.0V IR38064 I2C @12h, PMBUS @42h */
-	/* CPU0 VR ISL68137 1.2V CH47 PMBUS @45h */
+	/* CPU1 VR ISL68137 0.8V PMBUS @60h */
 };
 
 
@@ -435,3 +545,5 @@
 &ibt {
 	status = "okay";
 };
+
+#include "ibm-power9-dual.dtsi"
diff --git a/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts b/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
index 385c0f4b..080d7c8 100644
--- a/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
@@ -112,6 +112,11 @@
 			&pinctrl_ddcclk_default &pinctrl_ddcdat_default>;
 };
 
+&p2a {
+	status = "okay";
+	memory-region = <&vga_memory>;
+};
+
 &ibt {
 	status = "okay";
 };
diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
index 69f6b9d..6011692 100644
--- a/arch/arm/boot/dts/aspeed-g4.dtsi
+++ b/arch/arm/boot/dts/aspeed-g4.dtsi
@@ -29,6 +29,7 @@
 		serial3 = &uart4;
 		serial4 = &uart5;
 		serial5 = &vuart;
+		peci0 = &peci0;
 	};
 
 	cpus {
@@ -53,7 +54,7 @@
 		#size-cells = <1>;
 		ranges;
 
-		fmc: flash-controller@1e620000 {
+		fmc: spi@1e620000 {
 			reg = < 0x1e620000 0x94
 				0x20000000 0x10000000 >;
 			#address-cells = <1>;
@@ -65,11 +66,12 @@
 			flash@0 {
 				reg = < 0 >;
 				compatible = "jedec,spi-nor";
+				spi-max-frequency = <50000000>;
 				status = "disabled";
 			};
 		};
 
-		spi: flash-controller@1e630000 {
+		spi: spi@1e630000 {
 			reg = < 0x1e630000 0x18
 				0x30000000 0x10000000 >;
 			#address-cells = <1>;
@@ -80,6 +82,7 @@
 			flash@0 {
 				reg = < 0 >;
 				compatible = "jedec,spi-nor";
+				spi-max-frequency = <50000000>;
 				status = "disabled";
 			};
 		};
@@ -165,6 +168,10 @@
 					compatible = "aspeed,g4-pinctrl";
 				};
 
+				p2a: p2a-control {
+					compatible = "aspeed,ast2400-p2a-ctrl";
+					status = "disabled";
+				};
 			};
 
 			rng: hwrng@1e6e2078 {
@@ -197,6 +204,7 @@
 				gpio-ranges = <&pinctrl 0 0 220>;
 				clocks = <&syscon ASPEED_CLK_APB>;
 				interrupt-controller;
+				#interrupt-cells = <2>;
 			};
 
 			timer: timer@1e782000 {
@@ -208,6 +216,12 @@
 				clock-names = "PCLK";
 			};
 
+			rtc: rtc@1e781000 {
+				compatible = "aspeed,ast2400-rtc";
+				reg = <0x1e781000 0x18>;
+				status = "disabled";
+			};
+
 			uart1: serial@1e783000 {
 				compatible = "ns16550a";
 				reg = <0x1e783000 0x20>;
@@ -312,11 +326,29 @@
 						compatible = "aspeed,ast2400-ibt-bmc";
 						reg = <0xc0 0x18>;
 						interrupts = <8>;
+					};
+
+					sio_regs: regs {
+						compatible = "aspeed,bmc-misc";
+					};
+
+					mbox: mbox@180 {
+						compatible = "aspeed,ast2400-mbox";
+						reg = <0x180 0x5c>;
+						interrupts = <46>;
+						#mbox-cells = <1>;
 						status = "disabled";
 					};
 				};
 			};
 
+			peci: bus@1e78b000 {
+				compatible = "simple-bus";
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0x0 0x1e78b000 0x60>;
+			};
+
 			uart2: serial@1e78d000 {
 				compatible = "ns16550a";
 				reg = <0x1e78d000 0x20>;
@@ -360,6 +392,24 @@
 	};
 };
 
+&peci {
+	peci0: peci-bus@0 {
+		compatible = "aspeed,ast2400-peci";
+		reg = <0x0 0x60>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <15>;
+		clocks = <&syscon ASPEED_CLK_GATE_REFCLK>;
+		resets = <&syscon ASPEED_RESET_PECI>;
+		clock-frequency = <24000000>;
+		msg-timing = <1>;
+		addr-timing = <1>;
+		rd-sampling-point = <8>;
+		cmd-timeout-ms = <1000>;
+		status = "disabled";
+	};
+};
+
 &i2c {
 	i2c_ic: interrupt-controller@0 {
 		#interrupt-cells = <1>;
@@ -1357,3 +1407,86 @@
 		groups = "WDTRST2";
 	};
 };
+
+&sio_regs {
+	sio_2b {
+		offset = <0xf0>;
+		bit-mask = <0xff>;
+		bit-shift = <24>;
+	};
+	sio_2a {
+		offset = <0xf0>;
+		bit-mask = <0xff>;
+		bit-shift = <16>;
+	};
+	sio_29 {
+		offset = <0xf0>;
+		bit-mask = <0xff>;
+		bit-shift = <8>;
+	};
+	sio_28 {
+		offset = <0xf0>;
+		bit-mask = <0xff>;
+		bit-shift = <0>;
+	};
+	sio_2f {
+		offset = <0xf4>;
+		bit-mask = <0xff>;
+		bit-shift = <24>;
+	};
+	sio_2e {
+		offset = <0xf4>;
+		bit-mask = <0xff>;
+		bit-shift = <16>;
+	};
+	sio_2d {
+		offset = <0xf4>;
+		bit-mask = <0xff>;
+		bit-shift = <8>;
+	};
+	sio_2c {
+		offset = <0xf4>;
+		bit-mask = <0xff>;
+		bit-shift = <0>;
+	};
+	sio_23 {
+		offset = <0xf8>;
+		bit-mask = <0xff>;
+		bit-shift = <24>;
+	};
+	sio_22 {
+		offset = <0xf8>;
+		bit-mask = <0xff>;
+		bit-shift = <16>;
+	};
+	sio_21 {
+		offset = <0xf8>;
+		bit-mask = <0xff>;
+		bit-shift = <8>;
+	};
+	sio_20 {
+		offset = <0xf8>;
+		bit-mask = <0xff>;
+		bit-shift = <0>;
+	};
+	sio_27 {
+		offset = <0xfc>;
+		bit-mask = <0xff>;
+		bit-shift = <24>;
+	};
+	sio_26 {
+		offset = <0xfc>;
+		bit-mask = <0xff>;
+		bit-shift = <16>;
+	};
+	sio_25 {
+		offset = <0xfc>;
+		bit-mask = <0xff>;
+		bit-shift = <8>;
+	};
+	sio_24 {
+		offset = <0xfc>;
+		bit-mask = <0xff>;
+		bit-shift = <0>;
+	};
+};
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index d107459..6747465 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -29,6 +29,7 @@
 		serial3 = &uart4;
 		serial4 = &uart5;
 		serial5 = &vuart;
+		peci0 = &peci0;
 	};
 
 	cpus {
@@ -53,7 +54,7 @@
 		#size-cells = <1>;
 		ranges;
 
-		fmc: flash-controller@1e620000 {
+		fmc: spi@1e620000 {
 			reg = < 0x1e620000 0xc4
 				0x20000000 0x10000000 >;
 			#address-cells = <1>;
@@ -65,21 +66,24 @@
 			flash@0 {
 				reg = < 0 >;
 				compatible = "jedec,spi-nor";
+				spi-max-frequency = <50000000>;
 				status = "disabled";
 			};
 			flash@1 {
 				reg = < 1 >;
 				compatible = "jedec,spi-nor";
+				spi-max-frequency = <50000000>;
 				status = "disabled";
 			};
 			flash@2 {
 				reg = < 2 >;
 				compatible = "jedec,spi-nor";
+				spi-max-frequency = <50000000>;
 				status = "disabled";
 			};
 		};
 
-		spi1: flash-controller@1e630000 {
+		spi1: spi@1e630000 {
 			reg = < 0x1e630000 0xc4
 				0x30000000 0x08000000 >;
 			#address-cells = <1>;
@@ -90,16 +94,18 @@
 			flash@0 {
 				reg = < 0 >;
 				compatible = "jedec,spi-nor";
+				spi-max-frequency = <50000000>;
 				status = "disabled";
 			};
 			flash@1 {
 				reg = < 1 >;
 				compatible = "jedec,spi-nor";
+				spi-max-frequency = <50000000>;
 				status = "disabled";
 			};
 		};
 
-		spi2: flash-controller@1e631000 {
+		spi2: spi@1e631000 {
 			reg = < 0x1e631000 0xc4
 				0x38000000 0x08000000 >;
 			#address-cells = <1>;
@@ -110,11 +116,13 @@
 			flash@0 {
 				reg = < 0 >;
 				compatible = "jedec,spi-nor";
+				spi-max-frequency = <50000000>;
 				status = "disabled";
 			};
 			flash@1 {
 				reg = < 1 >;
 				compatible = "jedec,spi-nor";
+				spi-max-frequency = <50000000>;
 				status = "disabled";
 			};
 		};
@@ -212,6 +220,15 @@
 					aspeed,external-nodes = <&gfx &lhc>;
 
 				};
+
+				vga_scratch: scratch {
+					compatible = "aspeed,bmc-misc";
+				};
+
+				p2a: p2a-control {
+					compatible = "aspeed,ast2500-p2a-ctrl";
+					status = "disabled";
+				};
 			};
 
 			rng: hwrng@1e6e2078 {
@@ -225,6 +242,10 @@
 				compatible = "aspeed,ast2500-gfx", "syscon";
 				reg = <0x1e6e6000 0x1000>;
 				reg-io-width = <4>;
+				clocks = <&syscon ASPEED_CLK_GATE_D1CLK>;
+				resets = <&syscon ASPEED_RESET_CRT1>;
+				status = "disabled";
+				interrupts = <0x19>;
 			};
 
 			adc: adc@1e6e9000 {
@@ -236,6 +257,16 @@
 				status = "disabled";
 			};
 
+			video: video@1e700000 {
+				compatible = "aspeed,ast2500-video-engine";
+				reg = <0x1e700000 0x1000>;
+				clocks = <&syscon ASPEED_CLK_GATE_VCLK>,
+					 <&syscon ASPEED_CLK_GATE_ECLK>;
+				clock-names = "vclk", "eclk";
+				interrupts = <7>;
+				status = "disabled";
+			};
+
 			sram: sram@1e720000 {
 				compatible = "mmio-sram";
 				reg = <0x1e720000 0x9000>;	// 36K
@@ -250,6 +281,13 @@
 				gpio-ranges = <&pinctrl 0 0 220>;
 				clocks = <&syscon ASPEED_CLK_APB>;
 				interrupt-controller;
+				#interrupt-cells = <2>;
+			};
+
+			rtc: rtc@1e781000 {
+				compatible = "aspeed,ast2500-rtc";
+				reg = <0x1e781000 0x18>;
+				status = "disabled";
 			};
 
 			timer: timer@1e782000 {
@@ -330,8 +368,32 @@
 				ranges = <0x0 0x1e789000 0x1000>;
 
 				lpc_bmc: lpc-bmc@0 {
-					compatible = "aspeed,ast2500-lpc-bmc";
+					compatible = "aspeed,ast2500-lpc-bmc", "simple-mfd", "syscon";
 					reg = <0x0 0x80>;
+					reg-io-width = <4>;
+
+					#address-cells = <1>;
+					#size-cells = <1>;
+					ranges = <0x0 0x0 0x80>;
+
+					kcs1: kcs1@0 {
+						compatible = "aspeed,ast2500-kcs-bmc";
+						interrupts = <8>;
+						kcs_chan = <1>;
+						status = "disabled";
+					};
+					kcs2: kcs2@0 {
+						compatible = "aspeed,ast2500-kcs-bmc";
+						interrupts = <8>;
+						kcs_chan = <2>;
+						status = "disabled";
+					};
+					kcs3: kcs3@0 {
+						compatible = "aspeed,ast2500-kcs-bmc";
+						interrupts = <8>;
+						kcs_chan = <3>;
+						status = "disabled";
+					};
 				};
 
 				lpc_host: lpc-host@80 {
@@ -343,6 +405,13 @@
 					#size-cells = <1>;
 					ranges = <0x0 0x80 0x1e0>;
 
+					kcs4: kcs4@0 {
+						compatible = "aspeed,ast2500-kcs-bmc";
+						interrupts = <8>;
+						kcs_chan = <4>;
+						status = "disabled";
+					};
+
 					lpc_ctrl: lpc-ctrl@0 {
 						compatible = "aspeed,ast2500-lpc-ctrl";
 						reg = <0x0 0x80>;
@@ -372,11 +441,29 @@
 						compatible = "aspeed,ast2500-ibt-bmc";
 						reg = <0xc0 0x18>;
 						interrupts = <8>;
+					};
+
+					sio_regs: regs {
+						compatible = "aspeed,bmc-misc";
+					};
+
+					mbox: mbox@180 {
+						compatible = "aspeed,ast2500-mbox";
+						reg = <0x180 0x5c>;
+						interrupts = <46>;
+						#mbox-cells = <1>;
 						status = "disabled";
 					};
 				};
 			};
 
+			peci: bus@1e78b000 {
+				compatible = "simple-bus";
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0x0 0x1e78b000 0x60>;
+			};
+
 			uart2: serial@1e78d000 {
 				compatible = "ns16550a";
 				reg = <0x1e78d000 0x20>;
@@ -420,6 +507,24 @@
 	};
 };
 
+&peci {
+	peci0: peci-bus@0 {
+		compatible = "aspeed,ast2500-peci";
+		reg = <0x0 0x60>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <15>;
+		clocks = <&syscon ASPEED_CLK_GATE_REFCLK>;
+		resets = <&syscon ASPEED_RESET_PECI>;
+		clock-frequency = <24000000>;
+		msg-timing = <1>;
+		addr-timing = <1>;
+		rd-sampling-point = <8>;
+		cmd-timeout-ms = <1000>;
+		status = "disabled";
+	};
+};
+
 &i2c {
 	i2c_ic: interrupt-controller@0 {
 		#interrupt-cells = <1>;
@@ -1492,3 +1597,134 @@
 		groups = "WDTRST2";
 	};
 };
+
+&vga_scratch {
+	dac_mux {
+		offset = <0x2c>;
+		bit-mask = <0x3>;
+		bit-shift = <16>;
+	};
+	vga0 {
+		offset = <0x50>;
+		bit-mask = <0xffffffff>;
+		bit-shift = <0>;
+	};
+	vga1 {
+		offset = <0x54>;
+		bit-mask = <0xffffffff>;
+		bit-shift = <0>;
+	};
+	vga2 {
+		offset = <0x58>;
+		bit-mask = <0xffffffff>;
+		bit-shift = <0>;
+	};
+	vga3 {
+		offset = <0x5c>;
+		bit-mask = <0xffffffff>;
+		bit-shift = <0>;
+	};
+	vga4 {
+		offset = <0x60>;
+		bit-mask = <0xffffffff>;
+		bit-shift = <0>;
+	};
+	vga5 {
+		offset = <0x64>;
+		bit-mask = <0xffffffff>;
+		bit-shift = <0>;
+	};
+	vga6 {
+		offset = <0x68>;
+		bit-mask = <0xffffffff>;
+		bit-shift = <0>;
+	};
+	vga7 {
+		offset = <0x6c>;
+		bit-mask = <0xffffffff>;
+		bit-shift = <0>;
+	};
+};
+
+&sio_regs {
+	sio_2b {
+		offset = <0xf0>;
+		bit-mask = <0xff>;
+		bit-shift = <24>;
+	};
+	sio_2a {
+		offset = <0xf0>;
+		bit-mask = <0xff>;
+		bit-shift = <16>;
+	};
+	sio_29 {
+		offset = <0xf0>;
+		bit-mask = <0xff>;
+		bit-shift = <8>;
+	};
+	sio_28 {
+		offset = <0xf0>;
+		bit-mask = <0xff>;
+		bit-shift = <0>;
+	};
+	sio_2f {
+		offset = <0xf4>;
+		bit-mask = <0xff>;
+		bit-shift = <24>;
+	};
+	sio_2e {
+		offset = <0xf4>;
+		bit-mask = <0xff>;
+		bit-shift = <16>;
+	};
+	sio_2d {
+		offset = <0xf4>;
+		bit-mask = <0xff>;
+		bit-shift = <8>;
+	};
+	sio_2c {
+		offset = <0xf4>;
+		bit-mask = <0xff>;
+		bit-shift = <0>;
+	};
+	sio_23 {
+		offset = <0xf8>;
+		bit-mask = <0xff>;
+		bit-shift = <24>;
+	};
+	sio_22 {
+		offset = <0xf8>;
+		bit-mask = <0xff>;
+		bit-shift = <16>;
+	};
+	sio_21 {
+		offset = <0xf8>;
+		bit-mask = <0xff>;
+		bit-shift = <8>;
+	};
+	sio_20 {
+		offset = <0xf8>;
+		bit-mask = <0xff>;
+		bit-shift = <0>;
+	};
+	sio_27 {
+		offset = <0xfc>;
+		bit-mask = <0xff>;
+		bit-shift = <24>;
+	};
+	sio_26 {
+		offset = <0xfc>;
+		bit-mask = <0xff>;
+		bit-shift = <16>;
+	};
+	sio_25 {
+		offset = <0xfc>;
+		bit-mask = <0xff>;
+		bit-shift = <8>;
+	};
+	sio_24 {
+		offset = <0xfc>;
+		bit-mask = <0xff>;
+		bit-shift = <0>;
+	};
+};
diff --git a/arch/arm/boot/dts/ibm-power9-dual.dtsi b/arch/arm/boot/dts/ibm-power9-dual.dtsi
new file mode 100644
index 0000000..2abc42e
--- /dev/null
+++ b/arch/arm/boot/dts/ibm-power9-dual.dtsi
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2018 IBM Corp
+
+&fsi {
+	cfam@0,0 {
+		reg = <0 0>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		chip-id = <0>;
+
+		scom@1000 {
+			compatible = "ibm,fsi2pib";
+			reg = <0x1000 0x400>;
+		};
+
+		i2c@1800 {
+			compatible = "ibm,fsi-i2c-master";
+			reg = <0x1800 0x400>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			cfam0_i2c0: i2c-bus@0 {
+				reg = <0>;
+			};
+
+			cfam0_i2c1: i2c-bus@1 {
+				reg = <1>;
+			};
+
+			cfam0_i2c2: i2c-bus@2 {
+				reg = <2>;
+			};
+
+			cfam0_i2c3: i2c-bus@3 {
+				reg = <3>;
+			};
+
+			cfam0_i2c4: i2c-bus@4 {
+				reg = <4>;
+			};
+
+			cfam0_i2c5: i2c-bus@5 {
+				reg = <5>;
+			};
+
+			cfam0_i2c6: i2c-bus@6 {
+				reg = <6>;
+			};
+
+			cfam0_i2c7: i2c-bus@7 {
+				reg = <7>;
+			};
+
+			cfam0_i2c8: i2c-bus@8 {
+				reg = <8>;
+			};
+
+			cfam0_i2c9: i2c-bus@9 {
+				reg = <9>;
+			};
+
+			cfam0_i2c10: i2c-bus@a {
+				reg = <10>;
+			};
+
+			cfam0_i2c11: i2c-bus@b {
+				reg = <11>;
+			};
+
+			cfam0_i2c12: i2c-bus@c {
+				reg = <12>;
+			};
+
+			cfam0_i2c13: i2c-bus@d {
+				reg = <13>;
+			};
+
+			cfam0_i2c14: i2c-bus@e {
+				reg = <14>;
+			};
+		};
+
+		sbefifo@2400 {
+			compatible = "ibm,p9-sbefifo";
+			reg = <0x2400 0x400>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			fsi_occ0: occ {
+				compatible = "ibm,p9-occ";
+			};
+		};
+
+		fsi_hub0: hub@3400 {
+			compatible = "fsi-master-hub";
+			reg = <0x3400 0x400>;
+			#address-cells = <2>;
+			#size-cells = <0>;
+
+			no-scan-on-init;
+		};
+	};
+};
+
+&fsi_hub0 {
+	cfam@1,0 {
+		reg = <1 0>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		chip-id = <1>;
+
+		scom@1000 {
+			compatible = "ibm,fsi2pib";
+			reg = <0x1000 0x400>;
+		};
+
+		i2c@1800 {
+			compatible = "ibm,fsi-i2c-master";
+			reg = <0x1800 0x400>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			cfam1_i2c0: i2c-bus@0 {
+				reg = <0>;
+			};
+
+			cfam1_i2c1: i2c-bus@1 {
+				reg = <1>;
+			};
+
+			cfam1_i2c2: i2c-bus@2 {
+				reg = <2>;
+			};
+
+			cfam1_i2c3: i2c-bus@3 {
+				reg = <3>;
+			};
+
+			cfam1_i2c4: i2c-bus@4 {
+				reg = <4>;
+			};
+
+			cfam1_i2c5: i2c-bus@5 {
+				reg = <5>;
+			};
+
+			cfam1_i2c6: i2c-bus@6 {
+				reg = <6>;
+			};
+
+			cfam1_i2c7: i2c-bus@7 {
+				reg = <7>;
+			};
+
+			cfam1_i2c8: i2c-bus@8 {
+				reg = <8>;
+			};
+
+			cfam1_i2c9: i2c-bus@9 {
+				reg = <9>;
+			};
+
+			cfam1_i2c10: i2c-bus@a {
+				reg = <10>;
+			};
+
+			cfam1_i2c11: i2c-bus@b {
+				reg = <11>;
+			};
+
+			cfam1_i2c12: i2c-bus@c {
+				reg = <12>;
+			};
+
+			cfam1_i2c13: i2c-bus@d {
+				reg = <13>;
+			};
+
+			cfam1_i2c14: i2c-bus@e {
+				reg = <14>;
+			};
+		};
+
+		sbefifo@2400 {
+			compatible = "ibm,p9-sbefifo";
+			reg = <0x2400 0x400>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			fsi_occ1: occ {
+				compatible = "ibm,p9-occ";
+			};
+		};
+
+		fsi_hub1: hub@3400 {
+			compatible = "fsi-master-hub";
+			reg = <0x3400 0x400>;
+			#address-cells = <2>;
+			#size-cells = <0>;
+
+			no-scan-on-init;
+		};
+	};
+};
+
+/* Legacy OCC numbering (to get rid of when userspace is fixed) */
+&fsi_occ0 {
+	reg = <1>;
+};
+
+&fsi_occ1 {
+	reg = <2>;
+};
+
+/ {
+	aliases {
+		i2c100 = &cfam0_i2c0;
+		i2c101 = &cfam0_i2c1;
+		i2c102 = &cfam0_i2c2;
+		i2c103 = &cfam0_i2c3;
+		i2c104 = &cfam0_i2c4;
+		i2c105 = &cfam0_i2c5;
+		i2c106 = &cfam0_i2c6;
+		i2c107 = &cfam0_i2c7;
+		i2c108 = &cfam0_i2c8;
+		i2c109 = &cfam0_i2c9;
+		i2c110 = &cfam0_i2c10;
+		i2c111 = &cfam0_i2c11;
+		i2c112 = &cfam0_i2c12;
+		i2c113 = &cfam0_i2c13;
+		i2c114 = &cfam0_i2c14;
+		i2c200 = &cfam1_i2c0;
+		i2c201 = &cfam1_i2c1;
+		i2c202 = &cfam1_i2c2;
+		i2c203 = &cfam1_i2c3;
+		i2c204 = &cfam1_i2c4;
+		i2c205 = &cfam1_i2c5;
+		i2c206 = &cfam1_i2c6;
+		i2c207 = &cfam1_i2c7;
+		i2c208 = &cfam1_i2c8;
+		i2c209 = &cfam1_i2c9;
+		i2c210 = &cfam1_i2c10;
+		i2c211 = &cfam1_i2c11;
+		i2c212 = &cfam1_i2c12;
+		i2c213 = &cfam1_i2c13;
+		i2c214 = &cfam1_i2c14;
+	};
+};
diff --git a/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi b/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi
index d2d0761..da02aa7 100644
--- a/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi
+++ b/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi
@@ -3,14 +3,21 @@
 // Copyright 2018 Google, Inc.
 
 #include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/clock/nuvoton,npcm7xx-clock.h>
+#include <dt-bindings/gpio/gpio.h>
 
 / {
 	#address-cells = <1>;
 	#size-cells = <1>;
 	interrupt-parent = <&gic>;
 
+	memory {
+		device_type = "memory";
+		reg = <0 0>;
+	};
+
 	/* external reference clock */
-	clk_refclk: clk_refclk {
+	clk_refclk: clk-refclk {
 		compatible = "fixed-clock";
 		#clock-cells = <0>;
 		clock-frequency = <25000000>;
@@ -18,7 +25,7 @@
 	};
 
 	/* external reference clock for cpu. float in normal operation */
-	clk_sysbypck: clk_sysbypck {
+	clk_sysbypck: clk-sysbypck {
 		compatible = "fixed-clock";
 		#clock-cells = <0>;
 		clock-frequency = <800000000>;
@@ -26,7 +33,7 @@
 	};
 
 	/* external reference clock for MC. float in normal operation */
-	clk_mcbypck: clk_mcbypck {
+	clk_mcbypck: clk-mcbypck {
 		compatible = "fixed-clock";
 		#clock-cells = <0>;
 		clock-frequency = <800000000>;
@@ -34,7 +41,7 @@
 	};
 
 	 /* external clock signal rg1refck, supplied by the phy */
-	clk_rg1refck: clk_rg1refck {
+	clk_rg1refck: clk-rg1refck {
 		compatible = "fixed-clock";
 		#clock-cells = <0>;
 		clock-frequency = <125000000>;
@@ -42,14 +49,14 @@
 	};
 
 	 /* external clock signal rg2refck, supplied by the phy */
-	clk_rg2refck: clk_rg2refck {
+	clk_rg2refck: clk-rg2refck {
 		compatible = "fixed-clock";
 		#clock-cells = <0>;
 		clock-frequency = <125000000>;
 		clock-output-names = "clk_rg2refck";
 	};
 
-	clk_xin: clk_xin {
+	clk_xin: clk-xin {
 		compatible = "fixed-clock";
 		#clock-cells = <0>;
 		clock-frequency = <50000000>;
@@ -69,6 +76,12 @@
 			reg = <0x800000 0x1000>;
 		};
 
+		rst: rst@f0801000 {
+			compatible = "nuvoton,npcm750-rst", "syscon",
+			"simple-mfd";
+			reg = <0x801000 0x6C>;
+		};
+
 		scu: scu@3fe000 {
 			compatible = "arm,cortex-a9-scu";
 			reg = <0x3fe000 0x1000>;
@@ -80,7 +93,7 @@
 			interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
 			cache-unified;
 			cache-level = <2>;
-			clocks = <&clk 10>;
+			clocks = <&clk NPCM7XX_CLK_AXI>;
 			arm,shared-override;
 		};
 
@@ -109,6 +122,170 @@
 			clocks = <&clk_refclk>, <&clk_sysbypck>, <&clk_mcbypck>;
 		};
 
+		gmac0: eth@f0802000 {
+			device_type = "network";
+			compatible = "snps,dwmac";
+			reg = <0xf0802000 0x2000>;
+			interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "macirq";
+			ethernet = <0>;
+			clocks	= <&clk_rg1refck>, <&clk NPCM7XX_CLK_AHB>;
+			clock-names = "stmmaceth", "clk_gmac";
+			pinctrl-names = "default";
+			pinctrl-0 = <&rg1_pins
+					&rg1mdio_pins>;
+			status = "disabled";
+		};
+
+		emc0: eth@f0825000 {
+			device_type = "network";
+			compatible = "nuvoton,npcm750-emc";
+			reg = <0xf0825000 0x1000>;
+			interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>,
+					<GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&clk NPCM7XX_CLK_AHB>;
+			clock-names = "clk_emc";
+			pinctrl-names = "default";
+			pinctrl-0 = <&r1_pins
+					&r1err_pins
+					&r1md_pins>;
+			status = "disabled";
+		};
+
+		ehci1:usb@f0806000 {
+			compatible = "nuvoton,npcm750-ehci";
+			reg = <0xf0806000 0x1000>;
+			interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+		};
+
+		ohci1: ohci@f0807000 {
+			compatible = "nuvoton,npcm750-ohci";
+			reg = <0xf0807000 0x1000>;
+			interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+		};
+
+		sdhci0: sdhci@f0842000 {
+			compatible = "nuvoton,npcm750-sdhci";
+			status = "disabled";
+			reg = <0xf0842000 0x200>;
+			interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
+			clocks =  <&clk NPCM7XX_CLK_AHB>;      /*, <&clk_xin>;*/
+			clock-names = "clk_mmc";               /* ,"clk_xin"; */
+			pinctrl-names = "default";
+			pinctrl-0 = <&mmc8_pins
+				     &mmc_pins>;
+		};
+
+		sdhci1: sdhci@f0840000 {
+			compatible = "nuvoton,npcm750-sdhci";
+			status = "disabled";
+			reg = <0xf0840000 0x200>;
+			interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>;
+			clocks =  <&clk NPCM7XX_CLK_AHB>;      /*, <&clk_xin>;*/
+			clock-names = "clk_sdhc";              /* ,"clk_xin"; */
+			pinctrl-names = "default";
+			pinctrl-0 = <&sd1_pins>;
+		};
+
+		aes:aes@f0858000 {
+			compatible = "nuvoton,npcm750-aes";
+			reg = <0xf0858000 0x1000>;
+			status = "disabled";
+			clocks = <&clk NPCM7XX_CLK_AHB>;
+			clock-names = "clk_ahb";
+		};
+
+		sha:sha@f085a000 {
+			compatible = "nuvoton,npcm750-sha";
+			reg = <0xf085a000 0x1000>;
+			status = "disabled";
+			clocks = <&clk NPCM7XX_CLK_AHB>;
+			clock-names = "clk_ahb";
+		};
+
+		copr: copr@0 {
+			compatible = "nuvoton,npcm750-copr";
+			interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&clk NPCM7XX_CLK_AHB>;
+			clock-names = "clk_ahb";
+		};
+
+		vdma: vdma@e0800000 {
+			compatible = "nuvoton,npcm750-vdm";
+			reg = <0xe0800000 0x1000
+				   0xf0822000 0x1000>;
+			interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
+		};
+
+		fiu0: fiu@fb000000 {
+			compatible = "nuvoton,npcm750-fiu";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0xfb000000 0x1000>, <0x80000000 0x10000000>;
+			reg-names = "control", "memory";
+			clocks = <&clk NPCM7XX_CLK_AHB>;
+			clock-names = "clk_ahb";
+			status = "disabled";
+		};
+
+		fiu3: fiu@c0000000 {
+			compatible = "nuvoton,npcm750-fiu";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0xc0000000 0x1000>, <0xA0000000 0x20000000>;
+			reg-names = "control", "memory";
+			clocks = <&clk NPCM7XX_CLK_AHB>;
+			clock-names = "clk_ahb";
+			pinctrl-names = "default";
+			pinctrl-0 = <&spi3_pins>;
+			status = "disabled";
+		};
+
+		fiux: fiu@fb001000 {
+			compatible = "nuvoton,npcm750-fiu";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0xfb001000 0x1000>, <0xf8000000 0x2000000>;
+			reg-names = "control", "memory";
+			clocks = <&clk NPCM7XX_CLK_AHB>;
+			clock-names = "clk_ahb";
+			status = "disabled";
+		};
+
+		dvc: dvc@f0808000 {
+			compatible = "nuvoton,npcm750-dvc";
+			reg = <0xf0808000 0x1000>;
+			interrupts = <0 23 4>;
+		};
+
+		vcd: vcd@f0810000 {
+			compatible = "nuvoton,npcm750-vcd";
+			reg = <0xf0810000 0x10000>;
+			mem-addr = <0x3e200000>;
+			mem-size = <0x600000>;
+			interrupts = <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+		};
+
+		ece: ece@f0820000 {
+			compatible = "nuvoton,npcm750-ece";
+			reg = <0xf0820000 0x2000>;
+			mem-addr = <0x3e800000>;
+			mem-size = <0x600000>;
+			interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+		};
+
+		pcimbox: pcimbox@f0848000 {
+			compatible = "nuvoton,npcm750-pci-mbox",
+					"simple-mfd", "syscon";
+			reg = <0xf084C000 0x8
+				0xf0848000 0x3F00>;
+			interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+		};
+
 		apb {
 			#address-cells = <1>;
 			#size-cells = <1>;
@@ -116,11 +293,99 @@
 			interrupt-parent = <&gic>;
 			ranges = <0x0 0xf0000000 0x00300000>;
 
+			lpc_kcs: lpc_kcs@7000 {
+				compatible = "nuvoton,npcm750-lpc-kcs",
+						"simple-mfd", "syscon";
+				reg = <0x7000 0x40>;
+				reg-io-width = <1>;
+
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0x0 0x7000 0x40>;
+
+				kcs1: kcs1@0 {
+					compatible = "nuvoton,npcm750-kcs-bmc";
+					reg = <0x0 0x40>;
+					interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+					kcs_chan = <1>;
+					status = "disabled";
+				};
+
+				kcs2: kcs2@0 {
+					compatible = "nuvoton,npcm750-kcs-bmc";
+					reg = <0x0 0x40>;
+					interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+					kcs_chan = <2>;
+					status = "disabled";
+				};
+
+				kcs3: kcs3@0 {
+					compatible = "nuvoton,npcm750-kcs-bmc";
+					reg = <0x0 0x40>;
+					interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+					kcs_chan = <3>;
+					status = "disabled";
+				};
+			};
+
+			lpc_host: lpc_host@7000 {
+				compatible = "nuvoton,npcm750-lpc-host",
+						"simple-mfd", "syscon";
+				reg = <0x7000 0x60>;
+
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0x0 0x7000 0x60>;
+
+				lpc_bpc: lpc_bpc@40 {
+					compatible = "nuvoton,npcm750-lpc-bpc";
+					reg = <0x40 0x20>;
+					interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+					status = "disabled";
+				};
+			};
+
+			peci: peci-bus@100000 {
+				compatible = "nuvoton,npcm750-peci";
+				reg = <0x100000 0x200>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+				clocks = <&clk NPCM7XX_CLK_APB3>;
+				status = "disabled";
+			};
+
+			spi0: spi@200000 {
+				compatible = "nuvoton,npcm750-pspi";
+				reg = <0x200000 0x1000>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&pspi1_pins>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+				clocks = <&clk NPCM7XX_CLK_APB5>;
+				clock-names = "clk_apb5";
+				status = "disabled";
+			};
+
+			spi1: spi@201000 {
+				compatible = "nuvoton,npcm750-pspi";
+				reg = <0x201000 0x1000>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&pspi2_pins>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
+				clocks = <&clk NPCM7XX_CLK_APB5>;
+				clock-names = "clk_apb5";
+				status = "disabled";
+			};
+
 			timer0: timer@8000 {
 				compatible = "nuvoton,npcm750-timer";
 				interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
-				reg = <0x8000 0x50>;
-				clocks = <&clk 5>;
+				reg = <0x8000 0x1C>;
+				clocks = <&clk NPCM7XX_CLK_TIMER>;
 			};
 
 			watchdog0: watchdog@801C {
@@ -128,7 +393,7 @@
 				interrupts = <GIC_SPI 47 IRQ_TYPE_LEVEL_HIGH>;
 				reg = <0x801C 0x4>;
 				status = "disabled";
-				clocks = <&clk 5>;
+				clocks = <&clk NPCM7XX_CLK_TIMER>;
 			};
 
 			watchdog1: watchdog@901C {
@@ -136,7 +401,7 @@
 				interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
 				reg = <0x901C 0x4>;
 				status = "disabled";
-				clocks = <&clk 5>;
+				clocks = <&clk NPCM7XX_CLK_TIMER>;
 			};
 
 			watchdog2: watchdog@a01C {
@@ -144,13 +409,13 @@
 				interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>;
 				reg = <0xa01C 0x4>;
 				status = "disabled";
-				clocks = <&clk 5>;
+				clocks = <&clk NPCM7XX_CLK_TIMER>;
 			};
 
 			serial0: serial@1000 {
 				compatible = "nuvoton,npcm750-uart";
 				reg = <0x1000 0x1000>;
-				clocks = <&clk 6>;
+				clocks = <&clk NPCM7XX_CLK_UART>;
 				interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
 				reg-shift = <2>;
 				status = "disabled";
@@ -159,7 +424,7 @@
 			serial1: serial@2000 {
 				compatible = "nuvoton,npcm750-uart";
 				reg = <0x2000 0x1000>;
-				clocks = <&clk 6>;
+				clocks = <&clk NPCM7XX_CLK_UART>;
 				interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
 				reg-shift = <2>;
 				status = "disabled";
@@ -168,7 +433,7 @@
 			serial2: serial@3000 {
 				compatible = "nuvoton,npcm750-uart";
 				reg = <0x3000 0x1000>;
-				clocks = <&clk 6>;
+				clocks = <&clk NPCM7XX_CLK_UART>;
 				interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
 				reg-shift = <2>;
 				status = "disabled";
@@ -177,11 +442,818 @@
 			serial3: serial@4000 {
 				compatible = "nuvoton,npcm750-uart";
 				reg = <0x4000 0x1000>;
-				clocks = <&clk 6>;
+				clocks = <&clk NPCM7XX_CLK_UART>;
 				interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
 				reg-shift = <2>;
 				status = "disabled";
 			};
+
+			rng: rng@b000 {
+				compatible = "nuvoton,npcm750-rng";
+				reg = <0xb000 0x1000>;
+				clocks = <&clk NPCM7XX_CLK_APB1>;
+				clock-names = "clk_apb1";
+				status = "disabled";
+			};
+
+			adc: adc@c000 {
+				compatible = "nuvoton,npcm750-adc";
+				reg = <0xc000 0x8>;
+				interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
+				clocks = <&clk NPCM7XX_CLK_ADC>;
+				status = "disabled";
+			};
+
+			otp:otp@189000 {
+				compatible = "nuvoton,npcm750-otp";
+				reg = <0x189000 0x1000
+					   0x18a000 0x1000>;
+				status = "disabled";
+				clocks = <&clk NPCM7XX_CLK_APB4>;
+				clock-names = "clk_apb4";
+			};
+
+			pwm_fan:pwm-fan-controller@103000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "nuvoton,npcm750-pwm-fan";
+				reg = <0x103000 0x2000>,
+					<0x180000 0x8000>;
+				reg-names = "pwm", "fan";
+				clocks = <&clk NPCM7XX_CLK_APB3>,
+					<&clk NPCM7XX_CLK_APB4>;
+				clock-names = "pwm","fan";
+				interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>,
+						<GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>,
+						<GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>,
+						<GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>,
+						<GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>,
+						<GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>,
+						<GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>,
+						<GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&pwm0_pins &pwm1_pins
+						&pwm2_pins &pwm3_pins
+						&pwm4_pins &pwm5_pins
+						&pwm6_pins &pwm7_pins
+						&fanin0_pins &fanin1_pins
+						&fanin2_pins &fanin3_pins
+						&fanin4_pins &fanin5_pins
+						&fanin6_pins &fanin7_pins
+						&fanin8_pins &fanin9_pins
+						&fanin10_pins &fanin11_pins
+						&fanin12_pins &fanin13_pins
+						&fanin14_pins &fanin15_pins>;
+				status = "disabled";
+			};
+
+			i2c0: i2c@80000 {
+				reg = <0x80000 0x1000>;
+				compatible = "nuvoton,npcm750-i2c";
+				clocks = <&clk NPCM7XX_CLK_APB2>;
+				bus-frequency = <100000>;
+				interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&smb0_pins>;
+				status = "disabled";
+			};
+
+			i2c1: i2c@81000 {
+				reg = <0x81000 0x1000>;
+				compatible = "nuvoton,npcm750-i2c";
+				clocks = <&clk NPCM7XX_CLK_APB2>;
+				bus-frequency = <100000>;
+				interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&smb1_pins>;
+				status = "disabled";
+			};
+
+			i2c2: i2c@82000 {
+				reg = <0x82000 0x1000>;
+				compatible = "nuvoton,npcm750-i2c";
+				clocks = <&clk NPCM7XX_CLK_APB2>;
+				bus-frequency = <100000>;
+				interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&smb2_pins>;
+				status = "disabled";
+			};
+
+			i2c3: i2c@83000 {
+				reg = <0x83000 0x1000>;
+				compatible = "nuvoton,npcm750-i2c";
+				clocks = <&clk NPCM7XX_CLK_APB2>;
+				bus-frequency = <100000>;
+				interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&smb3_pins>;
+				status = "disabled";
+			};
+
+			i2c4: i2c@84000 {
+				reg = <0x84000 0x1000>;
+				compatible = "nuvoton,npcm750-i2c";
+				clocks = <&clk NPCM7XX_CLK_APB2>;
+				bus-frequency = <100000>;
+				interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&smb4_pins>;
+				status = "disabled";
+			};
+
+			i2c5: i2c@85000 {
+				reg = <0x85000 0x1000>;
+				compatible = "nuvoton,npcm750-i2c";
+				clocks = <&clk NPCM7XX_CLK_APB2>;
+				bus-frequency = <100000>;
+				interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&smb5_pins>;
+				status = "disabled";
+			};
+
+			i2c6: i2c@86000 {
+				reg = <0x86000 0x1000>;
+				compatible = "nuvoton,npcm750-i2c";
+				clocks = <&clk NPCM7XX_CLK_APB2>;
+				bus-frequency = <100000>;
+				interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&smb6_pins>;
+				status = "disabled";
+			};
+
+			i2c7: i2c@87000 {
+				reg = <0x87000 0x1000>;
+				compatible = "nuvoton,npcm750-i2c";
+				clocks = <&clk NPCM7XX_CLK_APB2>;
+				bus-frequency = <100000>;
+				interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&smb7_pins>;
+				status = "disabled";
+			};
+
+			i2c8: i2c@88000 {
+				reg = <0x88000 0x1000>;
+				compatible = "nuvoton,npcm750-i2c";
+				clocks = <&clk NPCM7XX_CLK_APB2>;
+				bus-frequency = <100000>;
+				interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&smb8_pins>;
+				status = "disabled";
+			};
+
+			i2c9: i2c@89000 {
+				reg = <0x89000 0x1000>;
+				compatible = "nuvoton,npcm750-i2c";
+				clocks = <&clk NPCM7XX_CLK_APB2>;
+				bus-frequency = <100000>;
+				interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&smb9_pins>;
+				status = "disabled";
+			};
+
+			i2c10: i2c@8a000 {
+				reg = <0x8a000 0x1000>;
+				compatible = "nuvoton,npcm750-i2c";
+				clocks = <&clk NPCM7XX_CLK_APB2>;
+				bus-frequency = <100000>;
+				interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&smb10_pins>;
+				status = "disabled";
+			};
+
+			i2c11: i2c@8b000 {
+				reg = <0x8b000 0x1000>;
+				compatible = "nuvoton,npcm750-i2c";
+				clocks = <&clk NPCM7XX_CLK_APB2>;
+				bus-frequency = <100000>;
+				interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&smb11_pins>;
+				status = "disabled";
+			};
+
+			i2c12: i2c@8c000 {
+				reg = <0x8c000 0x1000>;
+				compatible = "nuvoton,npcm750-i2c";
+				clocks = <&clk NPCM7XX_CLK_APB2>;
+				bus-frequency = <100000>;
+				interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&smb12_pins>;
+				status = "disabled";
+			};
+
+			i2c13: i2c@8d000 {
+				reg = <0x8d000 0x1000>;
+				compatible = "nuvoton,npcm750-i2c";
+				clocks = <&clk NPCM7XX_CLK_APB2>;
+				bus-frequency = <100000>;
+				interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&smb13_pins>;
+				status = "disabled";
+			};
+
+			i2c14: i2c@8e000 {
+				reg = <0x8e000 0x1000>;
+				compatible = "nuvoton,npcm750-i2c";
+				clocks = <&clk NPCM7XX_CLK_APB2>;
+				bus-frequency = <100000>;
+				interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&smb14_pins>;
+				status = "disabled";
+			};
+
+			i2c15: i2c@8f000 {
+				reg = <0x8f000 0x1000>;
+				compatible = "nuvoton,npcm750-i2c";
+				clocks = <&clk NPCM7XX_CLK_APB2>;
+				bus-frequency = <100000>;
+				interrupts = <GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&smb15_pins>;
+				status = "disabled";
+			};
+
+			gfxi: gfxi@f000e000 {
+				compatible = "nuvoton,npcm750-gfxi", "syscon",
+						"simple-mfd";
+				reg = <0xf000e000 0x100>;
+			};
+
+		};
+	};
+
+	pinctrl: pinctrl@f0800000 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "nuvoton,npcm750-pinctrl", "syscon", "simple-mfd";
+		ranges = <0 0xf0010000 0x8000>;
+		status = "okay";
+		gpio0: gpio@f0010000 {
+			gpio-controller;
+			#gpio-cells = <2>;
+			reg = <0x0 0x80>;
+			interrupts = <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>;
+			gpio-ranges = <&pinctrl 0 0 32>;
+		};
+		gpio1: gpio@f0011000 {
+			gpio-controller;
+			#gpio-cells = <2>;
+			reg = <0x1000 0x80>;
+			interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
+			gpio-ranges = <&pinctrl 0 32 32>;
+		};
+		gpio2: gpio@f0012000 {
+			gpio-controller;
+			#gpio-cells = <2>;
+			reg = <0x2000 0x80>;
+			interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
+			gpio-ranges = <&pinctrl 0 64 32>;
+		};
+		gpio3: gpio@f0013000 {
+			gpio-controller;
+			#gpio-cells = <2>;
+			reg = <0x3000 0x80>;
+			interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
+			gpio-ranges = <&pinctrl 0 96 32>;
+		};
+		gpio4: gpio@f0014000 {
+			gpio-controller;
+			#gpio-cells = <2>;
+			reg = <0x4000 0x80>;
+			interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
+			gpio-ranges = <&pinctrl 0 128 32>;
+		};
+		gpio5: gpio@f0015000 {
+			gpio-controller;
+			#gpio-cells = <2>;
+			reg = <0x5000 0x80>;
+			interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
+			gpio-ranges = <&pinctrl 0 160 32>;
+		};
+		gpio6: gpio@f0016000 {
+			gpio-controller;
+			#gpio-cells = <2>;
+			reg = <0x6000 0x80>;
+			interrupts = <GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>;
+			gpio-ranges = <&pinctrl 0 192 32>;
+		};
+		gpio7: gpio@f0017000 {
+			gpio-controller;
+			#gpio-cells = <2>;
+			reg = <0x7000 0x80>;
+			interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
+			gpio-ranges = <&pinctrl 0 224 32>;
+		};
+
+		iox1_pins: iox1-pins {
+			groups = "iox1";
+			function = "iox1";
+		};
+		iox2_pins: iox2-pins {
+			groups = "iox2";
+			function = "iox2";
+		};
+		smb1d_pins: smb1d-pins {
+			groups = "smb1d";
+			function = "smb1d";
+		};
+		smb2d_pins: smb2d-pins {
+			groups = "smb2d";
+			function = "smb2d";
+		};
+		lkgpo1_pins: lkgpo1-pins {
+			groups = "lkgpo1";
+			function = "lkgpo1";
+		};
+		lkgpo2_pins: lkgpo2-pins {
+			groups = "lkgpo2";
+			function = "lkgpo2";
+		};
+		ioxh_pins: ioxh-pins {
+			groups = "ioxh";
+			function = "ioxh";
+		};
+		gspi_pins: gspi-pins {
+			groups = "gspi";
+			function = "gspi";
+		};
+		smb5b_pins: smb5b-pins {
+			groups = "smb5b";
+			function = "smb5b";
+		};
+		smb5c_pins: smb5c-pins {
+			groups = "smb5c";
+			function = "smb5c";
+		};
+		lkgpo0_pins: lkgpo0-pins {
+			groups = "lkgpo0";
+			function = "lkgpo0";
+		};
+		pspi2_pins: pspi2-pins {
+			groups = "pspi2";
+			function = "pspi2";
+		};
+		smb4den_pins: smb4den-pins {
+			groups = "smb4den";
+			function = "smb4den";
+		};
+		smb4b_pins: smb4b-pins {
+			groups = "smb4b";
+			function = "smb4b";
+		};
+		smb4c_pins: smb4c-pins {
+			groups = "smb4c";
+			function = "smb4c";
+		};
+		smb15_pins: smb15-pins {
+			groups = "smb15";
+			function = "smb15";
+		};
+		smb4d_pins: smb4d-pins {
+			groups = "smb4d";
+			function = "smb4d";
+		};
+		smb14_pins: smb14-pins {
+			groups = "smb14";
+			function = "smb14";
+		};
+		smb5_pins: smb5-pins {
+			groups = "smb5";
+			function = "smb5";
+		};
+		smb4_pins: smb4-pins {
+			groups = "smb4";
+			function = "smb4";
+		};
+		smb3_pins: smb3-pins {
+			groups = "smb3";
+			function = "smb3";
+		};
+		spi0cs1_pins: spi0cs1-pins {
+			groups = "spi0cs1";
+			function = "spi0cs1";
+		};
+		spi0cs2_pins: spi0cs2-pins {
+			groups = "spi0cs2";
+			function = "spi0cs2";
+		};
+		spi0cs3_pins: spi0cs3-pins {
+			groups = "spi0cs3";
+			function = "spi0cs3";
+		};
+		smb3c_pins: smb3c-pins {
+			groups = "smb3c";
+			function = "smb3c";
+		};
+		smb3b_pins: smb3b-pins {
+			groups = "smb3b";
+			function = "smb3b";
+		};
+		bmcuart0a_pins: bmcuart0a-pins {
+			groups = "bmcuart0a";
+			function = "bmcuart0a";
+		};
+		uart1_pins: uart1-pins {
+			groups = "uart1";
+			function = "uart1";
+		};
+		jtag2_pins: jtag2-pins {
+			groups = "jtag2";
+			function = "jtag2";
+		};
+		bmcuart1_pins: bmcuart1-pins {
+			groups = "bmcuart1";
+			function = "bmcuart1";
+		};
+		uart2_pins: uart2-pins {
+			groups = "uart2";
+			function = "uart2";
+		};
+		bmcuart0b_pins: bmcuart0b-pins {
+			groups = "bmcuart0b";
+			function = "bmcuart0b";
+		};
+		r1err_pins: r1err-pins {
+			groups = "r1err";
+			function = "r1err";
+		};
+		r1md_pins: r1md-pins {
+			groups = "r1md";
+			function = "r1md";
+		};
+		smb3d_pins: smb3d-pins {
+			groups = "smb3d";
+			function = "smb3d";
+		};
+		fanin0_pins: fanin0-pins {
+			groups = "fanin0";
+			function = "fanin0";
+		};
+		fanin1_pins: fanin1-pins {
+			groups = "fanin1";
+			function = "fanin1";
+		};
+		fanin2_pins: fanin2-pins {
+			groups = "fanin2";
+			function = "fanin2";
+		};
+		fanin3_pins: fanin3-pins {
+			groups = "fanin3";
+			function = "fanin3";
+		};
+		fanin4_pins: fanin4-pins {
+			groups = "fanin4";
+			function = "fanin4";
+		};
+		fanin5_pins: fanin5-pins {
+			groups = "fanin5";
+			function = "fanin5";
+		};
+		fanin6_pins: fanin6-pins {
+			groups = "fanin6";
+			function = "fanin6";
+		};
+		fanin7_pins: fanin7-pins {
+			groups = "fanin7";
+			function = "fanin7";
+		};
+		fanin8_pins: fanin8-pins {
+			groups = "fanin8";
+			function = "fanin8";
+		};
+		fanin9_pins: fanin9-pins {
+			groups = "fanin9";
+			function = "fanin9";
+		};
+		fanin10_pins: fanin10-pins {
+			groups = "fanin10";
+			function = "fanin10";
+		};
+		fanin11_pins: fanin11-pins {
+			groups = "fanin11";
+			function = "fanin11";
+		};
+		fanin12_pins: fanin12-pins {
+			groups = "fanin12";
+			function = "fanin12";
+		};
+		fanin13_pins: fanin13-pins {
+			groups = "fanin13";
+			function = "fanin13";
+		};
+		fanin14_pins: fanin14-pins {
+			groups = "fanin14";
+			function = "fanin14";
+		};
+		fanin15_pins: fanin15-pins {
+			groups = "fanin15";
+			function = "fanin15";
+		};
+		pwm0_pins: pwm0-pins {
+			groups = "pwm0";
+			function = "pwm0";
+		};
+		pwm1_pins: pwm1-pins {
+			groups = "pwm1";
+			function = "pwm1";
+		};
+		pwm2_pins: pwm2-pins {
+			groups = "pwm2";
+			function = "pwm2";
+		};
+		pwm3_pins: pwm3-pins {
+			groups = "pwm3";
+			function = "pwm3";
+		};
+		r2_pins: r2-pins {
+			groups = "r2";
+			function = "r2";
+		};
+		r2err_pins: r2err-pins {
+			groups = "r2err";
+			function = "r2err";
+		};
+		r2md_pins: r2md-pins {
+			groups = "r2md";
+			function = "r2md";
+		};
+		ga20kbc_pins: ga20kbc-pins {
+			groups = "ga20kbc";
+			function = "ga20kbc";
+		};
+		smb5d_pins: smb5d-pins {
+			groups = "smb5d";
+			function = "smb5d";
+		};
+		lpc_pins: lpc-pins {
+			groups = "lpc";
+			function = "lpc";
+		};
+		espi_pins: espi-pins {
+			groups = "espi";
+			function = "espi";
+		};
+		rg1_pins: rg1-pins {
+			groups = "rg1";
+			function = "rg1";
+		};
+		rg1mdio_pins: rg1mdio-pins {
+			groups = "rg1mdio";
+			function = "rg1mdio";
+		};
+		rg2_pins: rg2-pins {
+			groups = "rg2";
+			function = "rg2";
+		};
+		ddr_pins: ddr-pins {
+			groups = "ddr";
+			function = "ddr";
+		};
+		smb0_pins: smb0-pins {
+			groups = "smb0";
+			function = "smb0";
+		};
+		smb1_pins: smb1-pins {
+			groups = "smb1";
+			function = "smb1";
+		};
+		smb2_pins: smb2-pins {
+			groups = "smb2";
+			function = "smb2";
+		};
+		smb2c_pins: smb2c-pins {
+			groups = "smb2c";
+			function = "smb2c";
+		};
+		smb2b_pins: smb2b-pins {
+			groups = "smb2b";
+			function = "smb2b";
+		};
+		smb1c_pins: smb1c-pins {
+			groups = "smb1c";
+			function = "smb1c";
+		};
+		smb1b_pins: smb1b-pins {
+			groups = "smb1b";
+			function = "smb1b";
+		};
+		smb8_pins: smb8-pins {
+			groups = "smb8";
+			function = "smb8";
+		};
+		smb9_pins: smb9-pins {
+			groups = "smb9";
+			function = "smb9";
+		};
+		smb10_pins: smb10-pins {
+			groups = "smb10";
+			function = "smb10";
+		};
+		smb11_pins: smb11-pins {
+			groups = "smb11";
+			function = "smb11";
+		};
+		sd1_pins: sd1-pins {
+			groups = "sd1";
+			function = "sd1";
+		};
+		sd1pwr_pins: sd1pwr-pins {
+			groups = "sd1pwr";
+			function = "sd1pwr";
+		};
+		pwm4_pins: pwm4-pins {
+			groups = "pwm4";
+			function = "pwm4";
+		};
+		pwm5_pins: pwm5-pins {
+			groups = "pwm5";
+			function = "pwm5";
+		};
+		pwm6_pins: pwm6-pins {
+			groups = "pwm6";
+			function = "pwm6";
+		};
+		pwm7_pins: pwm7-pins {
+			groups = "pwm7";
+			function = "pwm7";
+		};
+		mmc8_pins: mmc8-pins {
+			groups = "mmc8";
+			function = "mmc8";
+		};
+		mmc_pins: mmc-pins {
+			groups = "mmc";
+			function = "mmc";
+		};
+		mmcwp_pins: mmcwp-pins {
+			groups = "mmcwp";
+			function = "mmcwp";
+		};
+		mmccd_pins: mmccd-pins {
+			groups = "mmccd";
+			function = "mmccd";
+		};
+		mmcrst_pins: mmcrst-pins {
+			groups = "mmcrst";
+			function = "mmcrst";
+		};
+		clkout_pins: clkout-pins {
+			groups = "clkout";
+			function = "clkout";
+		};
+		serirq_pins: serirq-pins {
+			groups = "serirq";
+			function = "serirq";
+		};
+		lpcclk_pins: lpcclk-pins {
+			groups = "lpcclk";
+			function = "lpcclk";
+		};
+		scipme_pins: scipme-pins {
+			groups = "scipme";
+			function = "scipme";
+		};
+		sci_pins: sci-pins {
+			groups = "sci";
+			function = "sci";
+		};
+		smb6_pins: smb6-pins {
+			groups = "smb6";
+			function = "smb6";
+		};
+		smb7_pins: smb7-pins {
+			groups = "smb7";
+			function = "smb7";
+		};
+		pspi1_pins: pspi1-pins {
+			groups = "pspi1";
+			function = "pspi1";
+		};
+		faninx_pins: faninx-pins {
+			groups = "faninx";
+			function = "faninx";
+		};
+		r1_pins: r1-pins {
+			groups = "r1";
+			function = "r1";
+		};
+		spi3_pins: spi3-pins {
+			groups = "spi3";
+			function = "spi3";
+		};
+		spi3cs1_pins: spi3cs1-pins {
+			groups = "spi3cs1";
+			function = "spi3cs1";
+		};
+		spi3quad_pins: spi3quad-pins {
+			groups = "spi3quad";
+			function = "spi3quad";
+		};
+		spi3cs2_pins: spi3cs2-pins {
+			groups = "spi3cs2";
+			function = "spi3cs2";
+		};
+		spi3cs3_pins: spi3cs3-pins {
+			groups = "spi3cs3";
+			function = "spi3cs3";
+		};
+		nprd_smi_pins: nprd-smi-pins {
+			groups = "nprd_smi";
+			function = "nprd_smi";
+		};
+		smb0b_pins: smb0b-pins {
+			groups = "smb0b";
+			function = "smb0b";
+		};
+		smb0c_pins: smb0c-pins {
+			groups = "smb0c";
+			function = "smb0c";
+		};
+		smb0den_pins: smb0den-pins {
+			groups = "smb0den";
+			function = "smb0den";
+		};
+		smb0d_pins: smb0d-pins {
+			groups = "smb0d";
+			function = "smb0d";
+		};
+		ddc_pins: ddc-pins {
+			groups = "ddc";
+			function = "ddc";
+		};
+		rg2mdio_pins: rg2mdio-pins {
+			groups = "rg2mdio";
+			function = "rg2mdio";
+		};
+		wdog1_pins: wdog1-pins {
+			groups = "wdog1";
+			function = "wdog1";
+		};
+		wdog2_pins: wdog2-pins {
+			groups = "wdog2";
+			function = "wdog2";
+		};
+		smb12_pins: smb12-pins {
+			groups = "smb12";
+			function = "smb12";
+		};
+		smb13_pins: smb13-pins {
+			groups = "smb13";
+			function = "smb13";
+		};
+		spix_pins: spix-pins {
+			groups = "spix";
+			function = "spix";
+		};
+		spixcs1_pins: spixcs1-pins {
+			groups = "spixcs1";
+			function = "spixcs1";
+		};
+		clkreq_pins: clkreq-pins {
+			groups = "clkreq";
+			function = "clkreq";
+		};
+		hgpio0_pins: hgpio0-pins {
+			groups = "hgpio0";
+			function = "hgpio0";
+		};
+		hgpio1_pins: hgpio1-pins {
+			groups = "hgpio1";
+			function = "hgpio1";
+		};
+		hgpio2_pins: hgpio2-pins {
+			groups = "hgpio2";
+			function = "hgpio2";
+		};
+		hgpio3_pins: hgpio3-pins {
+			groups = "hgpio3";
+			function = "hgpio3";
+		};
+		hgpio4_pins: hgpio4-pins {
+			groups = "hgpio4";
+			function = "hgpio4";
+		};
+		hgpio5_pins: hgpio5-pins {
+			groups = "hgpio5";
+			function = "hgpio5";
+		};
+		hgpio6_pins: hgpio6-pins {
+			groups = "hgpio6";
+			function = "hgpio6";
+		};
+		hgpio7_pins: hgpio7-pins {
+			groups = "hgpio7";
+			function = "hgpio7";
 		};
 	};
 };
diff --git a/arch/arm/boot/dts/nuvoton-npcm750-evb.dts b/arch/arm/boot/dts/nuvoton-npcm750-evb.dts
index 15f744f..7728f7b 100644
--- a/arch/arm/boot/dts/nuvoton-npcm750-evb.dts
+++ b/arch/arm/boot/dts/nuvoton-npcm750-evb.dts
@@ -9,6 +9,50 @@
 	model = "Nuvoton npcm750 Development Board (Device Tree)";
 	compatible = "nuvoton,npcm750";
 
+	aliases {
+		ethernet0 = &emc0;
+		ethernet1 = &emc1;
+		ethernet2 = &gmac0;
+		ethernet3 = &gmac1;
+		serial0 = &serial0;
+		serial1 = &serial1;
+		serial2 = &serial2;
+		serial3 = &serial3;
+		udc0 = &udc0;
+		udc1 = &udc1;
+		udc2 = &udc2;
+		udc3 = &udc3;
+		udc4 = &udc4;
+		udc5 = &udc5;
+		udc6 = &udc6;
+		udc7 = &udc7;
+		udc8 = &udc8;
+		udc9 = &udc9;
+		emmc0 = &sdhci0;
+		emmc1 = &sdhci1;
+		i2c0 = &i2c0;
+		i2c1 = &i2c1;
+		i2c2 = &i2c2;
+		i2c3 = &i2c3;
+		i2c4 = &i2c4;
+		i2c5 = &i2c5;
+		i2c6 = &i2c6;
+		i2c7 = &i2c7;
+		i2c8 = &i2c8;
+		i2c9 = &i2c9;
+		i2c10 = &i2c10;
+		i2c11 = &i2c11;
+		i2c12 = &i2c12;
+		i2c13 = &i2c13;
+		i2c14 = &i2c14;
+		i2c15 = &i2c15;
+		spi0 = &spi0;
+		spi1 = &spi1;
+		fiu0 = &fiu0;
+		fiu1 = &fiu3;
+		fiu2 = &fiux;
+	};
+
 	chosen {
 		stdout-path = &serial3;
 	};
@@ -16,24 +60,553 @@
 	memory {
 		reg = <0 0x40000000>;
 	};
+
+	regulators {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		reg_vref1_2: regulator@0 {
+			compatible = "regulator-fixed";
+			reg = <0>;
+			regulator-name = "vref_1_2v";
+			regulator-min-microvolt = <1200000>;
+			regulator-max-microvolt = <1200000>;
+		};
+		reg_vref3_3: regulator@1 {
+			compatible = "regulator-fixed";
+			reg = <0>;
+			regulator-name = "vref_3_3v";
+			regulator-min-microvolt = <3300000>;
+			regulator-max-microvolt = <3300000>;
+		};
+	};
+
+	ahb {
+		gmac0: eth@f0802000 {
+			phy-mode = "rgmii-id";
+			status = "okay";
+		};
+
+		gmac1: eth@f0804000 {
+			phy-mode = "rgmii-id";
+			status = "okay";
+		};
+
+		emc0: eth@f0825000 {
+			phy-mode = "rmii";
+			#use-ncsi; /* add this to support ncsi */
+			status = "okay";
+		};
+
+		emc1: eth@f0826000 {
+			phy-mode = "rmii";
+			#use-ncsi; /* add this to support ncsi */
+			status = "okay";
+		};
+
+		ehci1: usb@f0806000 {
+			status = "okay";
+		};
+
+		ohci1: ohci@f0807000 {
+			status = "okay";
+		};
+
+		udc0:udc@f0830000 {
+			status = "okay";
+		};
+
+		udc1:udc@f0831000 {
+			status = "okay";
+		};
+
+		udc2:udc@f0832000 {
+			status = "okay";
+		};
+
+		udc3:udc@f0833000 {
+			status = "okay";
+		};
+
+		udc4:udc@f0834000 {
+			status = "okay";
+		};
+
+		udc5:udc@f0835000 {
+			status = "okay";
+		};
+
+		udc6:udc@f0836000 {
+			status = "okay";
+		};
+
+		udc7:udc@f0837000 {
+			status = "okay";
+		};
+
+		udc8:udc@f0838000 {
+			status = "okay";
+		};
+
+		udc9:udc@f0839000 {
+			status = "okay";
+		};
+
+		aes:aes@f0858000 {
+			status = "okay";
+		};
+
+		sha:sha@f085a000 {
+			status = "okay";
+		};
+
+		fiu0: fiu@fb000000 {
+			status = "okay";
+			spi-nor@0 {
+				compatible = "jedec,spi-nor";
+				#address-cells = <1>;
+				#size-cells = <1>;
+				spi-rx-bus-width = <2>;
+				reg = <0>;
+				partitions@80000000 {
+					compatible = "fixed-partitions";
+					#address-cells = <1>;
+					#size-cells = <1>;
+					bbuboot1@0 {
+						label = "bb-uboot-1";
+						reg = <0x0000000 0x80000>;
+						read-only;
+						};
+					bbuboot2@80000 {
+						label = "bb-uboot-2";
+						reg = <0x0080000 0x80000>;
+						read-only;
+						};
+					envparam@100000 {
+						label = "env-param";
+						reg = <0x0100000 0x40000>;
+						read-only;
+						};
+					spare@140000 {
+						label = "spare";
+						reg = <0x0140000 0xC0000>;
+						};
+					kernel@200000 {
+						label = "kernel";
+						reg = <0x0200000 0x400000>;
+						};
+					rootfs@600000 {
+						label = "rootfs";
+						reg = <0x0600000 0x700000>;
+						};
+					spare1@D00000 {
+						label = "spare1";
+						reg = <0x0D00000 0x200000>;
+						};
+					spare2@0F00000 {
+						label = "spare2";
+						reg = <0x0F00000 0x200000>;
+						};
+					spare3@1100000 {
+						label = "spare3";
+						reg = <0x1100000 0x200000>;
+						};
+					spare4@1300000 {
+						label = "spare4";
+						reg = <0x1300000 0x0>;
+					};
+				};
+			};
+		};
+
+		fiu3: fiu@c0000000 {
+			pinctrl-0 = <&spi3_pins>, <&spi3quad_pins>;
+			status = "okay";
+			spi-nor@0 {
+				compatible = "jedec,spi-nor";
+				#address-cells = <1>;
+				#size-cells = <1>;
+				spi-rx-bus-width = <2>;
+				reg = <0>;
+				partitions@A0000000 {
+					compatible = "fixed-partitions";
+					#address-cells = <1>;
+					#size-cells = <1>;
+					system1@0 {
+						label = "spi3-system1";
+						reg = <0x0 0x0>;
+					};
+				};
+			};
+		};
+
+		fiux: fiu@fb001000 {
+			spix-mode;
+		};
+
+		sdhci0: sdhci@f0842000 {
+			status = "okay";
+		};
+
+		sdhci1: sdhci@f0840000 {
+			status = "okay";
+		};
+
+		pcimbox: pcimbox@f0848000 {
+			status = "okay";
+		};
+
+		vcd: vcd@f0810000 {
+			status = "okay";
+		};
+
+		ece: ece@f0820000 {
+			status = "okay";
+		};
+
+		apb {
+
+			watchdog1: watchdog@901C {
+				status = "okay";
+			};
+
+			rng: rng@b000 {
+				status = "okay";
+			};
+
+			serial0: serial@1000 {
+				status = "okay";
+			};
+
+			serial1: serial@2000 {
+				status = "okay";
+			};
+
+			serial2: serial@3000 {
+				status = "okay";
+			};
+
+			serial3: serial@4000 {
+				status = "okay";
+			};
+
+			adc: adc@c000 {
+				/* enable external vref */
+				/*vref-supply = <&reg_vref1_2>;*/
+				status = "okay";
+			};
+
+			otp:otp@189000 {
+				status = "okay";
+			};
+
+			lpc_kcs: lpc_kcs@7000 {
+				kcs1: kcs1@0 {
+					status = "okay";
+				};
+
+				kcs2: kcs2@0 {
+					status = "okay";
+				};
+
+				kcs3: kcs3@0 {
+					status = "okay";
+				};
+			};
+
+			lpc_host: lpc_host@7000 {
+				lpc_bpc: lpc_bpc@40 {
+					monitor-ports = <0x80>;
+					status = "okay";
+				};
+			};
+
+			/* lm75 on SVB */
+			i2c0: i2c@80000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				bus-frequency = <100000>;
+				status = "okay";
+
+				lm75@48 {
+					compatible = "lm75";
+					reg = <0x48>;
+					status = "okay";
+				};
+			};
+
+			/* lm75 on EB */
+			i2c1: i2c@81000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				bus-frequency = <100000>;
+				status = "okay";
+
+				lm75@48 {
+					compatible = "lm75";
+					reg = <0x48>;
+					status = "okay";
+				};
+			};
+
+			/* tmp100 on EB */
+			i2c2: i2c@82000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				bus-frequency = <100000>;
+				status = "okay";
+
+				tmp100@48 {
+					compatible = "tmp100";
+					reg = <0x48>;
+					status = "okay";
+				};
+			};
+
+			/* tmp100 on SVB */
+			i2c6: i2c@86000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				bus-frequency = <100000>;
+				status = "okay";
+
+				tmp100@48 {
+					compatible = "tmp100";
+					reg = <0x48>;
+					status = "okay";
+				};
+			};
+			i2c3: i2c@83000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				bus-frequency = <100000>;
+				status = "okay";
+			};
+
+			i2c4: i2c@84000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				bus-frequency = <100000>;
+				status = "disabled";
+			};
+
+			i2c5: i2c@85000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				bus-frequency = <100000>;
+				status = "okay";
+			};
+
+			i2c7: i2c@87000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				bus-frequency = <100000>;
+				status = "okay";
+			};
+
+			i2c8: i2c@88000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				bus-frequency = <100000>;
+				status = "okay";
+			};
+
+			i2c9: i2c@89000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				bus-frequency = <100000>;
+				status = "okay";
+			};
+
+			i2c10: i2c@8a000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				bus-frequency = <100000>;
+				status = "okay";
+			};
+
+			i2c11: i2c@8b000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				bus-frequency = <100000>;
+				status = "okay";
+			};
+
+			i2c14: i2c@8e000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				bus-frequency = <100000>;
+				status = "okay";
+			};
+
+			i2c15: i2c@8f000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				bus-frequency = <100000>;
+				/* SVB conflict with pspi2 cs gpio20o_pins */
+				status = "disabled";
+			};
+
+			pwm_fan:pwm-fan-controller@103000 {
+				status = "okay";
+				fan@0 {
+					reg = <0x00>;
+					fan-tach-ch = /bits/ 8 <0x00 0x01>;
+					cooling-levels = <127 255>;
+				};
+				fan@1 {
+					reg = <0x01>;
+					fan-tach-ch = /bits/ 8 <0x02 0x03>;
+					cooling-levels = /bits/ 8 <127 255>;
+				};
+				fan@2 {
+					reg = <0x02>;
+					fan-tach-ch = /bits/ 8 <0x04 0x05>;
+					cooling-levels = /bits/ 8 <127 255>;
+				};
+				fan@3 {
+					reg = <0x03>;
+					fan-tach-ch = /bits/ 8 <0x06 0x07>;
+					cooling-levels = /bits/ 8 <127 255>;
+				};
+				fan@4 {
+					reg = <0x04>;
+					fan-tach-ch = /bits/ 8 <0x08 0x09>;
+					cooling-levels = /bits/ 8 <127 255>;
+				};
+				fan@5 {
+					reg = <0x05>;
+					fan-tach-ch = /bits/ 8 <0x0A 0x0B>;
+					cooling-levels = /bits/ 8 <127 255>;
+				};
+				fan@6 {
+					reg = <0x06>;
+					fan-tach-ch = /bits/ 8 <0x0C 0x0D>;
+					cooling-levels = /bits/ 8 <127 255>;
+				};
+				fan@7 {
+					reg = <0x07>;
+					fan-tach-ch = /bits/ 8 <0x0E 0x0F>;
+					cooling-levels = /bits/ 8 <127 255>;
+				};
+			};
+
+			peci: peci-bus@100000 {
+				cmd-timeout-ms = <1000>;
+				pull-down = <0>;
+				host-neg-bit-rate = <15>;
+				status = "okay";
+				intel-peci-dimmtemp@30 {
+					compatible = "intel,peci-client";
+					reg = <0x30>;
+					status = "okay";
+				};
+			};
+
+			spi0: spi@200000 {
+				cs-gpios = <&gpio6 11 GPIO_ACTIVE_LOW>;
+				status = "okay";
+				Flash@0 {
+					compatible = "winbond,w25q128",
+					"jedec,spi-nor";
+					reg = <0x0>;
+					#address-cells = <1>;
+					#size-cells = <1>;
+					spi-max-frequency = <5000000>;
+					partition@0 {
+						label = "spi_spare1";
+						reg = <0x0000000 0x800000>;
+					};
+					partition@1 {
+						label = "spi_spare2";
+						reg = <0x800000 0x0>;
+					};
+				};
+			};
+
+			spi1: spi@201000 {
+				cs-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>;
+				status = "okay";
+				Flash@0 {
+					compatible = "winbond,w25q128fw",
+					"jedec,spi-nor";
+					reg = <0x0>;
+					#address-cells = <1>;
+					#size-cells = <1>;
+					spi-max-frequency = <5000000>;
+					partition@0 {
+						label = "spi_spare1";
+						reg = <0x0000000 0x800000>;
+					};
+					partition@1 {
+						label = "spi_spare2";
+						reg = <0x800000 0x0>;
+					};
+				};
+			};
+		};
+	};
+
+	pinctrl: pinctrl@f0800000 {
+		pinctrl-names = "default";
+		pinctrl-0 = <	&iox1_pins
+				&gpio8_pins
+				&gpio9o_pins
+				&gpio10_pins
+				&gpio11o_pins
+				&gpio16_pins
+				&gpio24o_pins
+				&gpio25ol_pins
+				&gpio32o_pins
+				&jtag2_pins
+				&gpio61o_pins
+				&gpio62o_pins
+				&gpio63o_pins
+				&gpio64o_pins      /* SVB pspi1 enable */
+				&gpio80_pins
+				&gpio81_pins
+				&gpio82_pins
+				&gpio83_pins
+				&lpc_pins
+				&gpio132_pins
+				&gpio133_pins
+				&gpio134_pins
+				&gpio135_pins
+				&gpio144_pins
+				&gpio145_pins
+				&gpio146_pins
+				&gpio147_pins
+				&gpio160_pins
+				&gpio162_pins
+				&gpio168_pins
+				&gpio169_pins
+				&gpio170_pins
+				&gpio187o_pins
+				&gpio190_pins
+				&gpio191o_pins
+				&gpio192o_pins
+				&gpio197ol_pins
+				&ddc_pins
+				&gpio218_pins
+				&gpio219ol_pins
+				&gpio220ol_pins
+				&gpio221o_pins
+				&gpio222_pins
+				&gpio223ol_pins
+				&spix_pins
+				&gpio228ol_pins
+				&gpio231o_pins
+				&gpio255_pins>;
+	};
 };
 
-&watchdog1 {
-	status = "okay";
-};
+&gcr {
+	serial_port_mux: mux-controller {
+		compatible = "mmio-mux";
+		#mux-control-cells = <1>;
 
-&serial0 {
-	status = "okay";
-};
-
-&serial1 {
-	status = "okay";
-};
-
-&serial2 {
-	status = "okay";
-};
-
-&serial3 {
-	status = "okay";
+		mux-reg-masks = <0x38 0x07>;
+		idle-states = <2>; /* Serial port mode 3 (takeover) */
+	};
 };
diff --git a/arch/arm/boot/dts/nuvoton-npcm750-gpio.dtsi b/arch/arm/boot/dts/nuvoton-npcm750-gpio.dtsi
new file mode 100644
index 0000000..a912910
--- /dev/null
+++ b/arch/arm/boot/dts/nuvoton-npcm750-gpio.dtsi
@@ -0,0 +1,2021 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 Nuvoton Technology tomer.maimon@nuvoton.com
+
+/ {
+	pinctrl: pinctrl@f0800000 {
+		gpio0o_pins: gpio0o-pins {
+			pins = "GPIO0/IOX1DI";
+			bias-disable;
+			output-high;
+		};
+		gpio1_pins: gpio1-pins {
+			pins = "GPIO1/IOX1LD";
+			bias-disable;
+			input-enable;
+		};
+		gpio2_pins: gpio2-pins {
+			pins = "GPIO2/IOX1CK";
+			bias-disable;
+			input-enable;
+		};
+		gpio2o_pins: gpio2o-pins {
+			pins = "GPIO2/IOX1CK";
+			bias-disable;
+			output_high;
+		};
+		gpio3_pins: gpio3-pins {
+			pins = "GPIO3/IOX1D0";
+			bias-disable;
+			input-enable;
+		};
+		gpio3o_pins: gpio3o-pins {
+			pins = "GPIO3/IOX1D0";
+			bias-disable;
+			output-high;
+		};
+		gpio4_pins: gpio4-pins {
+			pins = "GPIO4/IOX2DI/SMB1DSDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio5_pins: gpio5-pins {
+			pins = "GPIO5/IOX2LD/SMB1DSCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio6_pins: gpio6-pins {
+			pins = "GPIO6/IOX2CK/SMB2DSDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio6o_pins: gpio6o-pins {
+			pins = "GPIO6/IOX2CK/SMB2DSDA";
+			bias-disable;
+			output-high;
+		};
+		gpio6ol_pins: gpio6ol-pins {
+			pins = "GPIO6/IOX2CK/SMB2DSDA";
+			bias-disable;
+			output-low;
+		};
+		gpio7_pins: gpio7-pins {
+			pins = "GPIO7/IOX2D0/SMB2DSCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio7o_pins: gpio7o-pins {
+			pins = "GPIO7/IOX2D0/SMB2DSCL";
+			bias-disable;
+			output-high;
+		};
+		gpio7ol_pins: gpio7ol-pins {
+			pins = "GPIO7/IOX2D0/SMB2DSCL";
+			bias-disable;
+			output-low;
+		};
+		gpio8_pins: gpio8-pins {
+			pins = "GPIO8/LKGPO1";
+			bias-disable;
+			input-enable;
+		};
+		gpio8ol_pins: gpio8ol-pins {
+			pins = "GPIO8/LKGPO1";
+			bias-disable;
+			output-low;
+		};
+		gpio9_pins: gpio9-pins {
+			pins = "GPIO9/LKGPO2";
+			bias-disable;
+			input-enable;
+		};
+		gpio9o_pins: gpio9o-pins {
+			pins = "GPIO9/LKGPO2";
+			bias-disable;
+			output-high;
+		};
+		gpio9ol_pins: gpio9ol-pins {
+			pins = "GPIO9/LKGPO2";
+			bias-disable;
+			output-low;
+		};
+		gpio10_pins: gpio10-pins {
+			pins = "GPIO10/IOXHLD";
+			bias-disable;
+			input-enable;
+		};
+		gpio10ol_pins: gpio10ol-pins {
+			pins = "GPIO10/IOXHLD";
+			bias-disable;
+			output-low;
+		};
+		gpio11_pins: gpio11-pins {
+			pins = "GPIO11/IOXHCK";
+			bias-disable;
+			input-enable;
+		};
+		gpio11o_pins: gpio11o-pins {
+			pins = "GPIO11/IOXHCK";
+			bias-disable;
+			output-high;
+		};
+		gpio11ol_pins: gpio11ol-pins {
+			pins = "GPIO11/IOXHCK";
+			bias-disable;
+			output-low;
+		};
+		gpio12_pins: gpio12-pins {
+			pins = "GPIO12/GSPICK/SMB5BSCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio12o_pins: gpio12o-pins {
+			pins = "GPIO12/GSPICK/SMB5BSCL";
+			bias-disable;
+			output-high;
+		};
+		gpio12ol_pins: gpio12ol-pins {
+			pins = "GPIO12/GSPICK/SMB5BSCL";
+			bias-disable;
+			output-low;
+		};
+		gpio13_pins: gpio13-pins {
+			pins = "GPIO13/GSPIDO/SMB5BSDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio13ol_pins: gpio13ol-pins {
+			pins = "GPIO13/GSPIDO/SMB5BSDA";
+			bias-disable;
+			output-low;
+		};
+		gpio14_pins: gpio14-pins {
+			pins = "GPIO14/GSPIDI/SMB5CSCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio14ol_pins: gpio14ol-pins {
+			pins = "GPIO14/GSPIDI/SMB5CSCL";
+			bias-disable;
+			output-low;
+		};
+		gpio15_pins: gpio15-pins {
+			pins = "GPIO15/GSPICS/SMB5CSDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio15o_pins: gpio15o-pins {
+			pins = "GPIO15/GSPICS/SMB5CSDA";
+			bias-disable;
+			output-high;
+		};
+		gpio16_pins: gpio16-pins {
+			pins = "GPIO16/LKGPO0";
+			bias-disable;
+			input-enable;
+		};
+		gpio16o_pins: gpio16o-pins {
+			pins = "GPIO16/LKGPO0";
+			bias-disable;
+			output-high;
+		};
+		gpio16ol_pins: gpio16ol-pins {
+			pins = "GPIO16/LKGPO0";
+			bias-disable;
+			output-low;
+		};
+		gpio17_pins: gpio17-pins {
+			pins = "GPIO17/PSPI2DI/SMB4DEN";
+			bias-disable;
+			input-enable;
+		};
+		gpio17o_pins: gpio17o-pins {
+			pins = "GPIO17/PSPI2DI/SMB4DEN";
+			bias-disable;
+			output-high;
+		};
+		gpio17ol_pins: gpio17ol-pins {
+			pins = "GPIO17/PSPI2DI/SMB4DEN";
+			bias-disable;
+			output-low;
+		};
+		gpio18_pins: gpio18-pins {
+			pins = "GPIO18/PSPI2D0/SMB4BSDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio18ol_pins: gpio18ol-pins {
+			pins = "GPIO18/PSPI2D0/SMB4BSDA";
+			bias-disable;
+			output-low;
+		};
+		gpio19_pins: gpio19-pins {
+			pins = "GPIO19/PSPI2CK/SMB4BSCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio19ol_pins: gpio19ol-pins {
+			pins = "GPIO19/PSPI2CK/SMB4BSCL";
+			bias-disable;
+			output-low;
+		};
+		gpio20_pins: gpio20-pins {
+			pins = "GPIO20/SMB4CSDA/SMB15SDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio20o_pins: gpio20o-pins {
+			pins = "GPIO20/SMB4CSDA/SMB15SDA";
+			bias-disable;
+			output-high;
+		};
+		gpio20ol_pins: gpio20ol-pins {
+			pins = "GPIO20/SMB4CSDA/SMB15SDA";
+			bias-disable;
+			output-low;
+		};
+		gpio21_pins: gpio21-pins {
+			pins = "GPIO21/SMB4CSCL/SMB15SCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio21ol_pins: gpio21ol-pins {
+			pins = "GPIO21/SMB4CSCL/SMB15SCL";
+			bias-disable;
+			output-low;
+		};
+		gpio22_pins: gpio22-pins {
+			pins = "GPIO22/SMB4DSDA/SMB14SDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio22ol_pins: gpio22ol-pins {
+			pins = "GPIO22/SMB4DSDA/SMB14SDA";
+			bias-disable;
+			output-low;
+		};
+		gpio23_pins: gpio23-pins {
+			pins = "GPIO23/SMB4DSCL/SMB14SCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio23ol_pins: gpio23ol-pins {
+			pins = "GPIO23/SMB4DSCL/SMB14SCL";
+			bias-disable;
+			output-low;
+		};
+		gpio24_pins: gpio24-pins {
+			pins = "GPIO24/IOXHDO";
+			bias-disable;
+			input-enable;
+		};
+		gpio24o_pins: gpio24o-pins {
+			pins = "GPIO24/IOXHDO";
+			bias-disable;
+			output-high;
+		};
+		gpio24ol_pins: gpio24ol-pins {
+			pins = "GPIO24/IOXHDO";
+			bias-disable;
+			output-low;
+		};
+		gpio25_pins: gpio25-pins {
+			pins = "GPIO25/IOXHDI";
+			bias-disable;
+			input-enable;
+		};
+		gpio25o_pins: gpio25o-pins {
+			pins = "GPIO25/IOXHDI";
+			bias-disable;
+			output-high;
+		};
+		gpio25ol_pins: gpio25ol-pins {
+			pins = "GPIO25/IOXHDI";
+			bias-disable;
+			output-low;
+		};
+		gpio26_pins: gpio26-pins {
+			pins = "GPIO26/SMB5SDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio27_pins: gpio27-pins {
+			pins = "GPIO27/SMB5SCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio32_pins: gpio32-pins {
+			pins = "GPIO32/nSPI0CS1";
+			bias-disable;
+			input-enable;
+		};
+		gpio32o_pins: gpio32o-pins {
+			pins = "GPIO32/nSPI0CS1";
+			bias-disable;
+			output-high;
+		};
+		gpio32ol_pins: gpio32ol-pins {
+			pins = "GPIO32/nSPI0CS1";
+			bias-disable;
+			output-low;
+		};
+		gpio37_pins: gpio37-pins {
+			pins = "GPIO37/SMB3CSDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio37o_pins: gpio37o-pins {
+			pins = "GPIO37/SMB3CSDA";
+			bias-disable;
+			output-high;
+		};
+		gpio37ol_pins: gpio37ol-pins {
+			pins = "GPIO37/SMB3CSDA";
+			bias-disable;
+			output-low;
+		};
+		gpio38_pins: gpio38-pins {
+			pins = "GPIO38/SMB3CSCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio38o_pins: gpio38o-pins {
+			pins = "GPIO38/SMB3CSCL";
+			bias-disable;
+			output-high;
+		};
+		gpio38ol_pins: gpio38ol-pins {
+			pins = "GPIO38/SMB3CSCL";
+			bias-disable;
+			output-low;
+		};
+		gpio39_pins: gpio39-pins {
+			pins = "GPIO39/SMB3BSDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio39o_pins: gpio39o-pins {
+			pins = "GPIO39/SMB3BSDA";
+			bias-disable;
+			output-high;
+		};
+		gpio39ol_pins: gpio39ol-pins {
+			pins = "GPIO39/SMB3BSDA";
+			bias-disable;
+			output-low;
+		};
+		gpio40_pins: gpio40-pins {
+			pins = "GPIO40/SMB3BSCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio40o_pins: gpio40o-pins {
+			pins = "GPIO40/SMB3BSCL";
+			bias-disable;
+			output-high;
+		};
+		gpio40ol_pins: gpio40ol-pins {
+			pins = "GPIO40/SMB3BSCL";
+			bias-disable;
+			output-low;
+		};
+		gpio41_pins: gpio41-pins {
+			pins = "GPIO41/BSPRXD";
+			input-enable;
+		};
+		gpio42_pins: gpio42-pins {
+			pins = "GPO42/BSPTXD/STRAP11";
+			bias-disable;
+			input-enable;
+		};
+		gpio43_pins: gpio43-pins {
+			pins = "GPIO43/RXD1/JTMS2/BU1RXD";
+			bias-disable;
+			input-enable;
+		};
+		gpio44_pins: gpio44-pins {
+			pins = "GPIO44/nCTS1/JTDI2/BU1CTS";
+			bias-disable;
+			input-enable;
+		};
+		gpio45_pins: gpio45-pins {
+			pins = "GPIO45/nDCD1/JTDO2";
+			bias-disable;
+			input-enable;
+		};
+		gpio46_pins: gpio46-pins {
+			pins = "GPIO46/nDSR1/JTCK2";
+			bias-disable;
+			input-enable;
+		};
+		gpio47_pins: gpio47-pins {
+			pins = "GPIO47/nRI1/JCP_RDY2";
+			bias-disable;
+			input-enable;
+		};
+		gpio48_pins: gpio48-pins {
+			pins = "GPIO48/TXD2/BSPTXD";
+			bias-disable;
+			input-enable;
+		};
+		gpio49_pins: gpio49-pins {
+			pins = "GPIO49/RXD2/BSPRXD";
+			bias-disable;
+			input-enable;
+		};
+		gpio50_pins: gpio50-pins {
+			pins = "GPIO50/nCTS2";
+			bias-disable;
+			input-enable;
+		};
+		gpio50ol_pins: gpio50ol-pins {
+			pins = "GPIO50/nCTS2";
+			bias-disable;
+			output-low;
+		};
+		gpio51_pins: gpio51-pins {
+			pins = "GPO51/nRTS2/STRAP2";
+			bias-disable;
+			input-enable;
+		};
+		gpio51o_pins: gpio51o-pins {
+			pins = "GPO51/nRTS2/STRAP2";
+			bias-disable;
+			output-high;
+		};
+		gpio52_pins: gpio52-pins {
+			pins = "GPIO52/nDCD2";
+			bias-disable;
+			input-enable;
+		};
+		gpio52ol_pins: gpio52ol-pins {
+			pins = "GPIO52/nDCD2";
+			bias-disable;
+			output-low;
+		};
+		gpio53_pins: gpio53-pins {
+			pins = "GPO53/nDTR2_BOUT2/STRAP1";
+			bias-disable;
+			input-enable;
+		};
+		gpio53o_pins: gpio53o-pins {
+			pins = "GPO53/nDTR2_BOUT2/STRAP1";
+			bias-disable;
+			output-high;
+		};
+		gpio54_pins: gpio54-pins {
+			pins = "GPIO54/nDSR2";
+			bias-disable;
+			input-enable;
+		};
+		gpio54ol_pins: gpio54ol-pins {
+			pins = "GPIO54/nDSR2";
+			bias-disable;
+			output-low;
+		};
+		gpio55_pins: gpio55-pins {
+			pins = "GPIO55/nRI2";
+			bias-disable;
+			input-enable;
+		};
+		gpio55ol_pins: gpio55ol-pins {
+			pins = "GPIO55/nRI2";
+			bias-disable;
+			output-low;
+		};
+		gpio56_pins: gpio56-pins {
+			pins = "GPIO56/R1RXERR";
+			bias-disable;
+			input-enable;
+		};
+		gpio57_pins: gpio57-pins {
+			pins = "GPIO57/R1MDC";
+			bias-disable;
+			input-enable;
+		};
+		gpio57ol_pins: gpio57ol-pins {
+			pins = "GPIO57/R1MDC";
+			bias-disable;
+			output-low;
+		};
+		gpio58_pins: gpio58-pins {
+			pins = "GPIO58/R1MDIO";
+			bias-disable;
+			input-enable;
+		};
+		gpio58ol_pins: gpio58ol-pins {
+			pins = "GPIO58/R1MDIO";
+			bias-disable;
+			output-low;
+		};
+		gpio59_pins: gpio59-pins {
+			pins = "GPIO59/SMB3DSDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio59o_pins: gpio59o-pins {
+			pins = "GPIO59/SMB3DSDA";
+			bias-disable;
+			output-high;
+		};
+		gpio59ol_pins: gpio59ol-pins {
+			pins = "GPIO59/SMB3DSDA";
+			bias-disable;
+			output-low;
+		};
+		gpio60_pins: gpio60-pins {
+			pins = "GPIO60/SMB3DSCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio60o_pins: gpio60o-pins {
+			pins = "GPIO60/SMB3DSCL";
+			bias-disable;
+			output-high;
+		};
+		gpio60ol_pins: gpio60ol-pins {
+			pins = "GPIO60/SMB3DSCL";
+			bias-disable;
+			output-low;
+		};
+		gpio61_pins: gpio61-pins {
+			pins = "GPO61/nDTR1_BOUT1/STRAP6";
+			bias-disable;
+			input-enable;
+		};
+		gpio61o_pins: gpio61o-pins {
+			pins = "GPO61/nDTR1_BOUT1/STRAP6";
+			bias-disable;
+			output-high;
+		};
+		gpio62_pins: gpio62-pins {
+			pins = "GPO62/nRTST1/STRAP5";
+			bias-disable;
+			input-enable;
+		};
+		gpio62o_pins: gpio62o-pins {
+			pins = "GPO62/nRTST1/STRAP5";
+			bias-disable;
+			output-high;
+		};
+		gpio63_pins: gpio63-pins {
+			pins = "GPO63/TXD1/STRAP4";
+			bias-disable;
+			input-enable;
+		};
+		gpio63o_pins: gpio63o-pins {
+			pins = "GPO63/TXD1/STRAP4";
+			bias-disable;
+			output-high;
+		};
+		gpio64_pins: gpio64-pins {
+			pins = "GPIO64/FANIN0";
+			bias-disable;
+			input-enable;
+		};
+		gpio64o_pins: gpio64o-pins {
+			pins = "GPIO64/FANIN0";
+			bias-disable;
+			output-high;
+		};
+		gpio65_pins: gpio65-pins {
+			pins = "GPIO65/FANIN1";
+			bias-disable;
+			input-enable;
+		};
+		gpio66_pins: gpio66-pins {
+			pins = "GPIO66/FANIN2";
+			bias-disable;
+			input-enable;
+		};
+		gpio67_pins: gpio67-pins {
+			pins = "GPIO67/FANIN3";
+			bias-disable;
+			input-enable;
+		};
+		gpio68_pins: gpio68-pins {
+			pins = "GPIO68/FANIN4";
+			bias-disable;
+			input-enable;
+		};
+		gpio69_pins: gpio69-pins {
+			pins = "GPIO69/FANIN5";
+			bias-disable;
+			input-enable;
+		};
+		gpio69ol_pins: gpio69ol-pins {
+			pins = "GPIO69/FANIN5";
+			bias-disable;
+			output-low;
+		};
+		gpio70_pins: gpio70-pins {
+			pins = "GPIO70/FANIN6";
+			bias-disable;
+			input-enable;
+		};
+		gpio71_pins: gpio71-pins {
+			pins = "GPIO71/FANIN7";
+			bias-disable;
+			input-enable;
+		};
+		gpio72_pins: gpio72-pins {
+			pins = "GPIO72/FANIN8";
+			bias-disable;
+			input-enable;
+		};
+		gpio72ol_pins: gpio72ol-pins {
+			pins = "GPIO72/FANIN8";
+			bias-disable;
+			output-low;
+		};
+		gpio73_pins: gpio73-pins {
+			pins = "GPIO73/FANIN9";
+			bias-disable;
+			input-enable;
+		};
+		gpio73ol_pins: gpio73ol-pins {
+			pins = "GPIO73/FANIN9";
+			bias-disable;
+			output-low;
+		};
+		gpio74_pins: gpio74-pins {
+			pins = "GPIO74/FANIN10";
+			bias-disable;
+			input-enable;
+		};
+		gpio74ol_pins: gpio74ol-pins {
+			pins = "GPIO74/FANIN10";
+			bias-disable;
+			output-low;
+		};
+		gpio75_pins: gpio75-pins {
+			pins = "GPIO75/FANIN11";
+			bias-disable;
+			input-enable;
+		};
+		gpio75ol_pins: gpio75ol-pins {
+			pins = "GPIO75/FANIN11";
+			bias-disable;
+			output-low;
+		};
+		gpio76_pins: gpio76-pins {
+			pins = "GPIO76/FANIN12";
+			bias-disable;
+			input-enable;
+		};
+		gpio76ol_pins: gpio76ol-pins {
+			pins = "GPIO76/FANIN12";
+			bias-disable;
+			output-low;
+		};
+		gpio77_pins: gpio77-pins {
+			pins = "GPIO77/FANIN13";
+			bias-disable;
+			input-enable;
+		};
+		gpio77ol_pins: gpio77ol-pins {
+			pins = "GPIO77/FANIN13";
+			bias-disable;
+			output-low;
+		};
+		gpio78_pins: gpio78-pins {
+			pins = "GPIO78/FANIN14";
+			bias-disable;
+			input-enable;
+		};
+		gpio78ol_pins: gpio78ol-pins {
+			pins = "GPIO78/FANIN14";
+			bias-disable;
+			output-low;
+		};
+		gpio79_pins: gpio79-pins {
+			pins = "GPIO79/FANIN15";
+			bias-disable;
+			input-enable;
+		};
+		gpio79ol_pins: gpio79ol-pins {
+			pins = "GPIO79/FANIN15";
+			bias-disable;
+			output-low;
+		};
+		gpio80_pins: gpio80-pins {
+			pins = "GPIO80/PWM0";
+			bias-disable;
+			input-enable;
+		};
+		gpio81_pins: gpio81-pins {
+			pins = "GPIO81/PWM1";
+			bias-disable;
+			input-enable;
+		};
+		gpio82_pins: gpio82-pins {
+			pins = "GPIO82/PWM2";
+			bias-disable;
+			input-enable;
+		};
+		gpio83_pins: gpio83-pins {
+			pins = "GPIO83/PWM3";
+			bias-disable;
+			input-enable;
+		};
+		gpio84_pins: gpio84-pins {
+			pins = "GPIO84/R2TXD0";
+			bias-disable;
+			input-enable;
+		};
+		gpio84o_pins: gpio84ol-pins {
+			pins = "GPIO84/R2TXD0";
+			bias-disable;
+			output-high;
+		};
+		gpio85_pins: gpio85-pins {
+			pins = "GPIO85/R2TXD1";
+			bias-disable;
+			input-enable;
+		};
+		gpio85o_pins: gpio85o-pins {
+			pins = "GPIO85/R2TXD1";
+			bias-disable;
+			output-high;
+		};
+		gpio86_pins: gpio86-pins {
+			pins = "GPIO86/R2TXEN";
+			bias-disable;
+			input-enable;
+		};
+		gpio86o_pins: gpio86o-pins {
+			pins = "GPIO86/R2TXEN";
+			bias-disable;
+			output-high;
+		};
+		gpio87_pins: gpio87-pins {
+			pins = "GPIO87/R2RXD0";
+			bias-disable;
+			input-enable;
+		};
+		gpio87o_pins: gpio87o-pins {
+			pins = "GPIO87/R2RXD0";
+			bias-disable;
+			output-high;
+		};
+		gpio88_pins: gpio88-pins {
+			pins = "GPIO88/R2RXD1";
+			bias-disable;
+			input-enable;
+		};
+		gpio88ol_pins: gpio88ol-pins {
+			pins = "GPIO88/R2RXD1";
+			bias-disable;
+			output-low;
+		};
+		gpio89_pins: gpio89-pins {
+			pins = "GPIO89/R2CRSDV";
+			bias-disable;
+			input-enable;
+		};
+		gpio89ol_pins: gpio89ol-pins {
+			pins = "GPIO89/R2CRSDV";
+			bias-disable;
+			output-low;
+		};
+		gpio90_pins: gpio90-pins {
+			pins = "GPIO90/R2RXERR";
+			bias-disable;
+			input-enable;
+		};
+		gpio90o_pins: gpio90o0-pins {
+			pins = "GPIO90/R2RXERR";
+			bias-disable;
+			output-high;
+		};
+		gpio90ol_pins: gpio90ol-pins {
+			pins = "GPIO90/R2RXERR";
+			bias-disable;
+			output-low;
+		};
+		gpio91_pins: gpio91-pins {
+			pins = "GPIO91/R2MDC";
+			bias-disable;
+			input-enable;
+		};
+		gpio91o_pins: gpio91o-pins {
+			pins = "GPIO91/R2MDC";
+			bias-disable;
+			output-high;
+		};
+		gpio91ol_pins: gpio91ol-pins {
+			pins = "GPIO91/R2MDC";
+			bias-disable;
+			output-low;
+		};
+		gpio92_pins: gpio92-pins {
+			pins = "GPIO92/R2MDIO";
+			bias-disable;
+			input-enable;
+		};
+		gpio92o_pins: gpio92o-pins {
+			pins = "GPIO92/R2MDIO";
+			bias-disable;
+			output-high;
+		};
+		gpio92ol_pins: gpio92ol-pins {
+			pins = "GPIO92/R2MDIO";
+			bias-disable;
+			output-low;
+		};
+		gpio93_pins: gpio93-pins {
+			pins = "GPIO93/GA20/SMB5DSCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio93ol_pins: gpio93ol-pins {
+			pins = "GPIO93/GA20/SMB5DSCL";
+			bias-disable;
+			output-low;
+		};
+		gpio94_pins: gpio94-pins {
+			pins = "GPIO94/nKBRST/SMB5DSDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio94o_pins: gpio94o-pins {
+			pins = "GPIO94/nKBRST/SMB5DSDA";
+			bias-disable;
+			output-high;
+		};
+		gpio95_pins: gpio95-pins {
+			pins = "GPIO95/nLRESET/nESPIRST";
+			bias-disable;
+			input-enable;
+		};
+		gpio96_pins: gpio96-pins {
+			pins = "GPIO96/RG1TXD0";
+			bias-disable;
+			input-enable;
+		};
+		gpio96ol_pins: gpio96ol-pins {
+			pins = "GPIO96/RG1TXD0";
+			bias-disable;
+			output-low;
+		};
+		gpio97_pins: gpio97-pins {
+			pins = "GPIO97/RG1TXD1";
+			bias-disable;
+			input-enable;
+		};
+		gpio97ol_pins: gpio97ol-pins {
+			pins = "GPIO97/RG1TXD1";
+			bias-disable;
+			output-low;
+		};
+		gpio98_pins: gpio98-pins {
+			pins = "GPIO98/RG1TXD2";
+			bias-disable;
+			input-enable;
+		};
+		gpio98ol_pins: gpio98ol-pins {
+			pins = "GPIO98/RG1TXD2";
+			bias-disable;
+			output-low;
+		};
+		gpio99_pins: gpio99-pins {
+			pins = "GPIO99/RG1TXD3";
+			bias-disable;
+			input-enable;
+		};
+		gpio99ol_pins: gpio99ol-pins {
+			pins = "GPIO99/RG1TXD3";
+			bias-disable;
+			output-low;
+		};
+		gpio100_pins: gpio100-pins {
+			pins = "GPIO100/RG1TXC";
+			bias-disable;
+			input-enable;
+		};
+		gpio100ol_pins: gpio100ol-pins {
+			pins = "GPIO100/RG1TXC";
+			bias-disable;
+			output-low;
+		};
+		gpio101_pins: gpio101-pins {
+			pins = "GPIO101/RG1TXCTL";
+			bias-disable;
+			input-enable;
+		};
+		gpio101ol_pins: gpio101ol-pins {
+			pins = "GPIO101/RG1TXCTL";
+			bias-disable;
+			output-low;
+		};
+		gpio102_pins: gpio102-pins {
+			pins = "GPIO102/RG1RXD0";
+			bias-disable;
+			input-enable;
+		};
+		gpio102ol_pins: gpio102ol-pins {
+			pins = "GPIO102/RG1RXD0";
+			bias-disable;
+			output-low;
+		};
+		gpio103_pins: gpio103-pins {
+			pins = "GPIO103/RG1RXD1";
+			bias-disable;
+			input-enable;
+		};
+		gpio103ol_pins: gpio103ol-pins {
+			pins = "GPIO103/RG1RXD1";
+			bias-disable;
+			output-low;
+		};
+		gpio104_pins: gpio104-pins {
+			pins = "GPIO104/RG1RXD2";
+			bias-disable;
+			input-enable;
+		};
+		gpio104ol_pins: gpio104ol-pins {
+			pins = "GPIO104/RG1RXD2";
+			bias-disable;
+			output-low;
+		};
+		gpio105_pins: gpio105-pins {
+			pins = "GPIO105/RG1RXD3";
+			bias-disable;
+			input-enable;
+		};
+		gpio105ol_pins: gpio105ol-pins {
+			pins = "GPIO105/RG1RXD3";
+			bias-disable;
+			output-low;
+		};
+		gpio106_pins: gpio106-pins {
+			pins = "GPIO106/RG1RXC";
+			bias-disable;
+			input-enable;
+		};
+		gpio106ol_pins: gpio106ol-pins {
+			pins = "GPIO106/RG1RXC";
+			bias-disable;
+			output-low;
+		};
+		gpio107_pins: gpio107-pins {
+			pins = "GPIO107/RG1RXCTL";
+			bias-disable;
+			input-enable;
+		};
+		gpio107ol_pins: gpio107ol-pins {
+			pins = "GPIO107/RG1RXCTL";
+			bias-disable;
+			output-low;
+		};
+		gpio108_pins: gpio108-pins {
+			pins = "GPIO108/RG1MDC";
+			bias-disable;
+			input-enable;
+		};
+		gpio108ol_pins: gpio108ol-pins {
+			pins = "GPIO108/RG1MDC";
+			bias-disable;
+			output-low;
+		};
+		gpio109_pins: gpio109-pins {
+			pins = "GPIO109/RG1MDIO";
+			bias-disable;
+			input-enable;
+		};
+		gpio109ol_pins: gpio109ol-pins {
+			pins = "GPIO109/RG1MDIO";
+			bias-disable;
+			output-low;
+		};
+		gpio110_pins: gpio110-pins {
+			pins = "GPIO110/RG2TXD0/DDRV0";
+			bias-disable;
+			input-enable;
+		};
+		gpio110ol_pins: gpio110ol-pins {
+			pins = "GPIO110/RG2TXD0/DDRV0";
+			bias-disable;
+			output-low;
+		};
+		gpio111_pins: gpio111-pins {
+			pins = "GPIO111/RG2TXD1/DDRV1";
+			bias-disable;
+			input-enable;
+		};
+		gpio111ol_pins: gpio111ol-pins {
+			pins = "GPIO111/RG2TXD1/DDRV1";
+			bias-disable;
+			output-low;
+		};
+		gpio112_pins: gpio112-pins {
+			pins = "GPIO112/RG2TXD2/DDRV2";
+			bias-disable;
+			input-enable;
+		};
+		gpio112ol_pins: gpio112ol-pins {
+			pins = "GPIO112/RG2TXD2/DDRV2";
+			bias-disable;
+			output-low;
+		};
+		gpio113_pins: gpio113-pins {
+			pins = "GPIO113/RG2TXD3/DDRV3";
+			bias-disable;
+			input-enable;
+		};
+		gpio113ol_pins: gpio113ol-pins {
+			pins = "GPIO113/RG2TXD3/DDRV3";
+			bias-disable;
+			output-low;
+		};
+		gpio118_pins: gpio118-pins {
+			pins = "GPIO118/SMB2SCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio119_pins: gpio119-pins {
+			pins = "GPIO119/SMB2SDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio120_pins: gpio120-pins {
+			pins = "GPIO120/SMB2CSDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio121_pins: gpio121-pins {
+			pins = "GPIO121/SMB2CSCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio122_pins: gpio122-pins {
+			pins = "GPIO122/SMB2BSDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio123_pins: gpio123-pins {
+			pins = "GPIO123/SMB2BSCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio123_pins: gpio123-pins {
+			pins = "GPIO123/SMB2BSCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio124_pins: gpio124-pins {
+			pins = "GPIO124/SMB1CSDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio125_pins: gpio125-pins {
+			pins = "GPIO125/SMB1CSCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio126_pins: gpio126-pins {
+			pins = "GPIO126/SMB1BSDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio127_pins: gpio127-pins {
+			pins = "GPIO127/SMB1BSCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio128o_pins: gpio128o-pins {
+			pins = "GPIO128/SMB8SCL";
+			bias-disable;
+			output-high;
+		};
+		gpio130_pins: gpio130-pins {
+			pins = "GPIO130/SMB9SCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio131_pins: gpio131-pins {
+			pins = "GPIO131/SMB9SDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio132_pins: gpio132-pins {
+			pins = "GPIO132/SMB10SCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio133_pins: gpio133-pins {
+			pins = "GPIO133/SMB10SDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio134_pins: gpio134-pins {
+			pins = "GPIO134/SMB11SCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio135_pins: gpio135-pins {
+			pins = "GPIO135/SMB11SDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio136_pins: gpio136-pins {
+			pins = "GPIO136/SD1DT0";
+			bias-disable;
+			input-enable;
+		};
+		gpio136o_pins: gpio136o-pins {
+			pins = "GPIO136/SD1DT0";
+			bias-disable;
+			output-high;
+		};
+		gpio137_pins: gpio137-pins {
+			pins = "GPIO137/SD1DT1";
+			bias-disable;
+			input-enable;
+		};
+		gpio137o_pins: gpio137o-pins {
+			pins = "GPIO137/SD1DT1";
+			bias-disable;
+			output-high;
+		};
+		gpio138_pins: gpio138-pins {
+			pins = "GPIO138/SD1DT2";
+			bias-disable;
+			input-enable;
+		};
+		gpio138o_pins: gpio138o-pins {
+			pins = "GPIO138/SD1DT2";
+			bias-disable;
+			output-high;
+		};
+		gpio139_pins: gpio139-pins {
+			pins = "GPIO139/SD1DT3";
+			bias-disable;
+			input-enable;
+		};
+		gpio139o_pins: gpio139o-pins {
+			pins = "GPIO139/SD1DT3";
+			bias-disable;
+			output-high;
+		};
+		gpio140_pins: gpio140-pins {
+			pins = "GPIO140/SD1CLK";
+			bias-disable;
+			input-enable;
+		};
+		gpio140o_pins: gpio140o-pins {
+			pins = "GPIO140/SD1CLK";
+			bias-disable;
+			output-high;
+		};
+		gpio141_pins: gpio141-pins {
+			pins = "GPIO141/SD1WP";
+			bias-disable;
+			input-enable;
+		};
+		gpio141o_pins: gpio141o-pins {
+			pins = "GPIO141/SD1WP";
+			bias-disable;
+			output-high;
+		};
+		gpio142_pins: gpio142-pins {
+			pins = "GPIO142/SD1CMD";
+			bias-disable;
+			input-enable;
+		};
+		gpio142o_pins: gpio142o-pins {
+			pins = "GPIO142/SD1CMD";
+			bias-disable;
+			output-high;
+		};
+		gpio143_pins: gpio143-pins {
+			pins = "GPIO143/SD1CD/SD1PWR";
+			bias-disable;
+			input-enable;
+		};
+		gpio143o_pins: gpio143o-pins {
+			pins = "GPIO143/SD1CD/SD1PWR";
+			bias-disable;
+			output-high;
+		};
+		gpio143ol_pins: gpio143ol-pins {
+			pins = "GPIO143/SD1CD/SD1PWR";
+			bias-disable;
+			output-low;
+		};
+		gpio144_pins: gpio144-pins {
+			pins = "GPIO144/PWM4";
+			bias-disable;
+			input-enable;
+		};
+		gpio145_pins: gpio145-pins {
+			pins = "GPIO145/PWM5";
+			bias-disable;
+			input-enable;
+		};
+		gpio146_pins: gpio146-pins {
+			pins = "GPIO146/PWM6";
+			bias-disable;
+			input-enable;
+		};
+		gpio147_pins: gpio147-pins {
+			pins = "GPIO147/PWM7";
+			bias-disable;
+			input-enable;
+		};
+		gpio148_pins: gpio148-pins {
+			pins = "GPIO148/MMCDT4";
+			bias-disable;
+			input-enable;
+		};
+		gpio148o_pins: gpio148o-pins {
+			pins = "GPIO148/MMCDT4";
+			bias-disable;
+			output-high;
+		};
+		gpio148ol_pins: gpio148ol_pins {
+			pins = "GPIO148/MMCDT4";
+			bias-disable;
+			output-low;
+		};
+		gpio149_pins: gpio149-pins {
+			pins = "GPIO149/MMCDT5";
+			bias-disable;
+			input-enable;
+		};
+		gpio149o_pins: gpio149o-pins {
+			pins = "GPIO149/MMCDT5";
+			bias-disable;
+			output-high;
+		};
+		gpio149ol_pins: gpio149ol-pins {
+			pins = "GPIO149/MMCDT5";
+			bias-disable;
+			output-low;
+		};
+		gpio150_pins: gpio150-pins {
+			pins = "GPIO150/MMCDT6";
+			bias-disable;
+			input-enable;
+		};
+		gpio150o_pins: gpio150o-pins {
+			pins = "GPIO150/MMCDT6";
+			bias-disable;
+			output-high;
+		};
+		gpio150ol_pins: gpio150ol-pins {
+			pins = "GPIO150/MMCDT6";
+			bias-disable;
+			output-low;
+		};
+		gpio151_pins: gpio151-pins {
+			pins = "GPIO151/MMCDT7";
+			bias-disable;
+			input-enable;
+		};
+		gpio151o_pins: gpio151o-pins {
+			pins = "GPIO151/MMCDT7";
+			bias-disable;
+			output-high;
+		};
+		gpio151ol_pins: gpio151ol-pins {
+			pins = "GPIO151/MMCDT7";
+			bias-disable;
+			output-low;
+		};
+		gpio152_pins: gpio152-pins {
+			pins = "GPIO152/MMCCLK";
+			bias-disable;
+			input-enable;
+		};
+		gpio152o_pins: gpio152o-pins {
+			pins = "GPIO152/MMCCLK";
+			bias-disable;
+			output-high;
+		};
+		gpio152ol_pins: gpio152ol-pins {
+			pins = "GPIO152/MMCCLK";
+			bias-disable;
+			output-low;
+		};
+		gpio153_pins: gpio153-pins {
+			pins = "GPIO153/MMCWP";
+			bias-disable;
+			input-enable;
+		};
+		gpio153ol_pins: gpio153ol-pins {
+			pins = "GPIO153/MMCWP";
+			bias-disable;
+			output-low;
+		};
+		gpio154_pins: gpio154-pins {
+			pins = "GPIO154/MMCCMD";
+			bias-disable;
+			input-enable;
+		};
+		gpio154ol_pins: gpio154ol-pins {
+			pins = "GPIO154/MMCCMD";
+			bias-disable;
+			output-low;
+		};
+		gpio155_pins: gpio155-pins {
+			pins = "GPIO155/nMMCCD/nMMCRST";
+			bias-disable;
+			input-enable;
+		};
+		gpio155ol_pins: gpio155ol-pins {
+			pins = "GPIO155/nMMCCD/nMMCRST";
+			bias-disable;
+			output-low;
+		};
+		gpio156_pins: gpio156-pins {
+			pins = "GPIO156/MMCDT0";
+			bias-disable;
+			input-enable;
+		};
+		gpio156ol_pins: gpio156ol-pins {
+			pins = "GPIO156/MMCDT0";
+			bias-disable;
+			output-low;
+		};
+		gpio157_pins: gpio157-pins {
+			pins = "GPIO157/MMCDT1";
+			bias-disable;
+			input-enable;
+		};
+		gpio157o_pins: gpio157o-pins {
+			pins = "GPIO157/MMCDT1";
+			bias-disable;
+			output-high;
+		};
+		gpio157ol_pins: gpio157ol-pins {
+			pins = "GPIO157/MMCDT1";
+			bias-disable;
+			output-low;
+		};
+		gpio158_pins: gpio158-pins {
+			pins = "GPIO158/MMCDT2";
+			bias-disable;
+			input-enable;
+		};
+		gpio158o_pins: gpio158o-pins {
+			pins = "GPIO158/MMCDT2";
+			bias-disable;
+			output-high;
+		};
+		gpio158ol_pins: gpio158ol-pins {
+			pins = "GPIO158/MMCDT2";
+			bias-disable;
+			output-low;
+		};
+		gpio159_pins: gpio159-pins {
+			pins = "GPIO159/MMCDT3";
+			bias-disable;
+			input-enable;
+		};
+		gpio159o_pins: gpio159o-pins {
+			pins = "GPIO159/MMCDT3";
+			bias-disable;
+			output-high;
+		};
+		gpio159ol_pins: gpio159ol-pins {
+			pins = "GPIO159/MMCDT3";
+			bias-disable;
+			output-low;
+		};
+		gpio160_pins: gpio160-pins {
+			pins = "GPIO160/CLKOUT/RNGOSCOUT";
+			bias-disable;
+			input-enable;
+		};
+		gpio160o_pins: gpio160o-pins {
+			pins = "GPIO160/CLKOUT/RNGOSCOUT";
+			bias-disable;
+			output-high;
+		};
+		gpio160ol_pins: gpio160ol-pins {
+			pins = "GPIO160/CLKOUT/RNGOSCOUT";
+			bias-disable;
+			output-low;
+		};
+		gpio161_pins: gpio161-pins {
+			pins = "GPIO161/nLFRAME/nESPICS";
+			bias-disable;
+			input-enable;
+		};
+		gpio162_pins: gpio162-pins {
+			pins = "GPIO162/SERIRQ";
+			bias-disable;
+			input-enable;
+		};
+		gpio163_pins: gpio163-pins {
+			pins = "GPIO163/LCLK/ESPICLK";
+			bias-disable;
+			input-enable;
+		};
+		gpio164_pins: gpio164-pins {
+			pins = "GPIO164/LAD0/ESPI_IO0";
+			bias-disable;
+			input-enable;
+		};
+		gpio165_pins: gpio165-pins {
+			pins = "GPIO165/LAD1/ESPI_IO1";
+			bias-disable;
+			input-enable;
+		};
+		gpio166_pins: gpio166-pins {
+			pins = "GPIO166/LAD2/ESPI_IO2";
+			bias-disable;
+			input-enable;
+		};
+		gpio167_pins: gpio167-pins {
+			pins = "GPIO167/LAD3/ESPI_IO3";
+			bias-disable;
+			input-enable;
+		};
+		gpio168_pins: gpio168-pins {
+			pins = "GPIO168/nCLKRUN/nESPIALERT";
+			bias-disable;
+			input-enable;
+		};
+		gpio168ol_pins: gpio168ol-pins {
+			pins = "GPIO168/nCLKRUN/nESPIALERT";
+			bias-disable;
+			output-low;
+		};
+		gpio169_pins: gpio169-pins {
+			pins = "GPIO169/nSCIPME";
+			bias-disable;
+			input-enable;
+		};
+		gpio169o_pins: gpio169o-pins {
+			pins = "GPIO169/nSCIPME";
+			bias-disable;
+			output-high;
+		};
+		gpio169ol_pins: gpio169ol-pins {
+			pins = "GPIO169/nSCIPME";
+			bias-disable;
+			output-low;
+		};
+		gpio170_pins: gpio170-pins {
+			pins = "GPIO170/nSMI";
+			bias-disable;
+			input-enable;
+		};
+		gpio170ol_pins: gpio170ol-pins {
+			pins = "GPIO170/nSMI";
+			bias-disable;
+			output-low;
+		};
+		gpio173o_pins: gpio173o-pins {
+			pins = "GPIO173/SMB7SCL";
+			bias-disable;
+			output-high;
+		};
+		gpio173ol_pins: gpio173ol-pins {
+			pins = "GPIO173/SMB7SCL";
+			bias-disable;
+			output-low;
+		};
+		gpio174_pins: gpio174-pins {
+			pins = "GPIO174/SMB7SDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio175_pins: gpio175-pins {
+			pins = "GPIO175/PSPI1CK/FANIN19";
+			bias-disable;
+			input-enable;
+		};
+		gpio175o_pins: gpio175o-pins {
+			pins = "GPIO175/PSPI1CK/FANIN19";
+			bias-disable;
+			output-high;
+		};
+		gpio175ol_pins: gpio175ol-pins {
+			pins = "GPIO175/PSPI1CK/FANIN19";
+			bias-disable;
+			output-low;
+		};
+		gpio176_pins: gpio176-pins {
+			pins = "GPIO176/PSPI1DO/FANIN18";
+			bias-disable;
+			input-enable;
+		};
+		gpio176o_pins: gpio176o-pins {
+			pins = "GPIO176/PSPI1DO/FANIN18";
+			bias-disable;
+			output-high;
+		};
+		gpio176ol_pins: gpio176ol-pins {
+			pins = "GPIO176/PSPI1DO/FANIN18";
+			bias-disable;
+			output-low;
+		};
+		gpio177_pins: gpio177-pins {
+			pins = "GPIO177/PSPI1DI/FANIN17";
+			bias-disable;
+			input-enable;
+		};
+		gpio177o_pins: gpio177o-pins {
+			pins = "GPIO177/PSPI1DI/FANIN17";
+			bias-disable;
+			output-high;
+		};
+		gpio177ol_pins: gpio177ol-pins {
+			pins = "GPIO177/PSPI1DI/FANIN17";
+			bias-disable;
+			output-low;
+		};
+		gpio187_pins: gpio187-pins {
+			pins = "GPIO187/nSPI3CS1";
+			bias-disable;
+			input-enable;
+		};
+		gpio187o_pins: gpio187o-pins {
+			pins = "GPIO187/nSPI3CS1";
+			bias-disable;
+			output-high;
+		};
+		gpio187ol_pins: gpio187ol-pins {
+			pins = "GPIO187/nSPI3CS1";
+			bias-disable;
+			output-low;
+		};
+		gpio188_pins: gpio188-pins {
+			pins = "GPIO188/SPI3D2/nSPI3CS2";
+			bias-disable;
+			input-enable;
+		};
+		gpio188o_pins: gpio188o-pins {
+			pins = "GPIO188/SPI3D2/nSPI3CS2";
+			bias-disable;
+			output-high;
+		};
+		gpio189o_pins: gpio189o-pins {
+			pins = "GPIO189/SPI3D3/nSPI3CS3";
+			bias-disable;
+			output-high;
+		};
+		gpio190_pins: gpio190-pins {
+			pins = "GPIO190/nPRD_SMI";
+			bias-disable;
+			input-enable;
+		};
+		gpio190o_pins: gpio190o-pins {
+			pins = "GPIO190/nPRD_SMI";
+			bias-disable;
+			output-high;
+		};
+		gpio190ol_pins: gpio190ol-pins {
+			pins = "GPIO190/nPRD_SMI";
+			bias-disable;
+			output-low;
+		};
+		gpio191o_pins: gpio191o-pins {
+			pins = "GPIO191";
+			bias-disable;
+			output-high;
+		};
+		gpio191ol_pins: gpio191ol-pins {
+			pins = "GPIO191";
+			bias-disable;
+			output-low;
+		};
+		gpio192_pins: gpio192-pins {
+			pins = "GPIO192";
+			bias-disable;
+			input-enable;
+		};
+		gpio192o_pins: gpio192o-pins {
+			pins = "GPIO192";
+			bias-disable;
+			output-high;
+		};
+		gpio192ol_pins: gpio192ol-pins {
+			pins = "GPIO192";
+			bias-disable;
+			output-low;
+		};
+		gpio194_pins: gpio194-pins {
+			pins = "GPIO194/SMB0BSCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio194o_pins: gpio194o-pins {
+			pins = "GPIO194/SMB0BSCL";
+			bias-disable;
+			output-high;
+		};
+		gpio195_pins: gpio195-pins {
+			pins = "GPIO195/SMB0BSDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio196_pins: gpio196-pins {
+			pins = "GPIO196/SMB0CSCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio197_pins: gpio197-pins {
+			pins = "GPIO197/SMB0DEN";
+			bias-disable;
+			input-enable;
+		};
+		gpio197o_pins: gpio197o-pins {
+			pins = "GPIO197/SMB0DEN";
+			bias-disable;
+			output-high;
+		};
+		gpio197ol_pins: gpio197ol-pins {
+			pins = "GPIO197/SMB0DEN";
+			bias-disable;
+			output-low;
+		};
+		gpio198o_pins: gpio198o-pins {
+			pins = "GPIO198/SMB0DSDA";
+			bias-disable;
+			output-high;
+		};
+		gpio198ol_pins: gpio198ol-pins {
+			pins = "GPIO198/SMB0DSDA";
+			bias-disable;
+			output-low;
+		};
+		gpio199_pins: gpio199-pins {
+			pins = "GPIO199/SMB0DSCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio200_pins: gpio200-pins {
+			pins = "GPIO200/R2CK";
+			input-enable;
+			bias-disable;
+		};
+		gpio200ol_pins: gpio200ol-pins {
+			pins = "GPIO200/R2CK";
+			bias-disable;
+			output-low;
+		};
+		gpio201ol_pins: gpio201ol-pins {
+			pins = "GPIO200/R2CK";
+			bias-disable;
+			output-low;
+		};
+		gpio202_pins: gpio202-pins {
+			pins = "GPIO202/SMB0CSDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio203_pins: gpio203-pins {
+			pins = "GPIO203/FANIN16";
+			bias-disable;
+			input-enable;
+		};
+		gpio203o_pins: gpio203o-pins {
+			pins = "GPIO203/FANIN16";
+			bias-disable;
+			output-high;
+		};
+		gpio203ol_pins: gpio203ol-pins {
+			pins = "GPIO203/FANIN16";
+			bias-disable;
+			output-low;
+		};
+		gpio204_pins: gpio204-pins {
+			pins = "GPIO204/DDC2SCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio204o_pins: gpio204o-pins {
+			pins = "GPIO204/DDC2SCL";
+			bias-disable;
+			output-high;
+		};
+		gpio204ol_pins: gpio204ol-pins {
+			pins = "GPIO204/DDC2SCL";
+			bias-disable;
+			output-low;
+		};
+		gpio205_pins: gpio205-pins {
+			pins = "GPIO205/DDC2SDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio205o_pins: gpio205o-pins {
+			pins = "GPIO205/DDC2SDA";
+			bias-disable;
+			output-high;
+		};
+		gpio205ol_pins: gpio205ol-pins {
+			pins = "GPIO205/DDC2SDA";
+			bias-disable;
+			output-low;
+		};
+		gpio206_pins: gpio206-pins {
+			pins = "GPIO206/HSYNC2";
+			bias-disable;
+			input-enable;
+		};
+		gpio206o_pins: gpio206o-pins {
+			pins = "GPIO206/HSYNC2";
+			bias-disable;
+			output-high;
+		};
+		gpio206ol_pins: gpio206ol-pins {
+			pins = "GPIO206/HSYNC2";
+			bias-disable;
+			output-low;
+		};
+		gpio207_pins: gpio207-pins {
+			pins = "GPIO207/VSYNC2";
+			bias-disable;
+			input-enable;
+		};
+		gpio207o_pins: gpio207o-pins {
+			pins = "GPIO207/VSYNC2";
+			bias-disable;
+			output-high;
+		};
+		gpio207ol_pins: gpio207ol-pins {
+			pins = "GPIO207/VSYNC2";
+			bias-disable;
+			output-low;
+		};
+		gpio208_pins: gpio208-pins {
+			pins = "GPIO208/RG2TXC/DVCK";
+			bias-disable;
+			input-enable;
+		};
+		gpio208o_pins: gpio208o-pins {
+			pins = "GPIO208/RG2TXC/DVCK";
+			bias-disable;
+			output-high;
+		};
+		gpio208ol_pins: gpio208ol-pins {
+			pins = "GPIO208/RG2TXC/DVCK";
+			bias-disable;
+			output-low;
+		};
+		gpio209_pins: gpio209-pins {
+			pins = "GPIO209/RG2TXCTL/DDRV4";
+			bias-disable;
+			input-enable;
+		};
+		gpio209ol_pins: gpio209ol-pins {
+			pins = "GPIO209/RG2TXCTL/DDRV4";
+			bias-disable;
+			output-low;
+		};
+		gpio210_pins: gpio210-pins {
+			pins = "GPIO210/RG2RXD0/DDRV5";
+			bias-disable;
+			input-enable;
+		};
+		gpio210o_pins: gpio210o-pins {
+			pins = "GPIO210/RG2RXD0/DDRV5";
+			bias-disable;
+			output-high;
+		};
+		gpio210ol_pins: gpio210ol-pins {
+			pins = "GPIO210/RG2RXD0/DDRV5";
+			bias-disable;
+			output-low;
+		};
+		gpio211_pins: gpio211-pins {
+			pins = "GPIO211/RG2RXD1/DDRV6";
+			bias-disable;
+			input-enable;
+		};
+		gpio211o_pins: gpio211o-pins {
+			pins = "GPIO211/RG2RXD1/DDRV6";
+			bias-disable;
+			output-high;
+		};
+		gpio211ol_pins: gpio211ol-pins {
+			pins = "GPIO211/RG2RXD1/DDRV6";
+			bias-disable;
+			output-low;
+		};
+		gpio212_pins: gpio212-pins {
+			pins = "GPIO212/RG2RXD2/DDRV7";
+			bias-disable;
+			input-enable;
+		};
+		gpio212o_pins: gpio212o-pins {
+			pins = "GPIO212/RG2RXD2/DDRV7";
+			bias-disable;
+			output-high;
+		};
+		gpio212ol_pins: gpio212ol-pins {
+			pins = "GPIO212/RG2RXD2/DDRV7";
+			bias-disable;
+			output-low;
+		};
+		gpio213_pins: gpio213-pins {
+			pins = "GPIO213/RG2RXD3/DDRV8";
+			bias-disable;
+			input-enable;
+		};
+		gpio213o_pins: gpio213o-pins {
+			pins = "GPIO213/RG2RXD3/DDRV8";
+			bias-disable;
+			output-high;
+		};
+		gpio213ol_pins: gpio213ol-pins {
+			pins = "GPIO213/RG2RXD3/DDRV8";
+			bias-disable;
+			output-low;
+		};
+		gpio214_pins: gpio214-pins {
+			pins = "GPIO214/RG2RXC/DDRV9";
+			bias-disable;
+			input-enable;
+		};
+		gpio214ol_pins: gpio214ol-pins {
+			pins = "GPIO214/RG2RXC/DDRV9";
+			bias-disable;
+			output-low;
+		};
+		gpio215_pins: gpio215-pins {
+			pins = "GPIO215/RG2RXCTL/DDRV10";
+			bias-disable;
+			input-enable;
+		};
+		gpio215ol_pins: gpio215ol-pins {
+			pins = "GPIO215/RG2RXCTL/DDRV10";
+			bias-disable;
+			output-low;
+		};
+		gpio216_pins: gpio216-pins {
+			pins = "GPIO216/RG2MDC/DDRV11";
+			bias-disable;
+			input-enable;
+		};
+		gpio216ol_pins: gpio216ol-pins {
+			pins = "GPIO216/RG2MDC/DDRV11";
+			bias-disable;
+			output-low;
+		};
+		gpio217_pins: gpio217-pins {
+			pins = "GPIO217/RG2MDIO/DVHSYNC";
+			bias-disable;
+			input-enable;
+		};
+		gpio217ol_pins: gpio217ol-pins {
+			pins = "GPIO217/RG2MDIO/DVHSYNC";
+			bias-disable;
+			output-low;
+		};
+		gpio218_pins: gpio218-pins {
+			pins = "GPIO218/nWDO1";
+			bias-disable;
+			input-enable;
+		};
+		gpio218ol_pins: gpio218ol-pins {
+			pins = "GPIO218/nWDO1";
+			bias-disable;
+			output-low;
+		};
+		gpio219_pins: gpio219-pins {
+			pins = "GPIO219/nWDO2";
+			bias-disable;
+			input-enable;
+		};
+		gpio219ol_pins: gpio219ol-pins {
+			pins = "GPIO219/nWDO2";
+			bias-disable;
+			output-low;
+		};
+		gpio220ol_pins: gpio220ol-pins {
+			pins = "GPIO220/SMB12SCL";
+			bias-disable;
+			output-low;
+		};
+		gpio221o_pins: gpio221o-pins {
+			pins = "GPIO221/SMB12SDA";
+			bias-disable;
+			output-high;
+		};
+		gpio222_pins: gpio222-pins {
+			pins = "GPIO222/SMB13SCL";
+			bias-disable;
+			input-enable;
+		};
+		gpio222o_pins: gpio222o-pins {
+			pins = "GPIO222/SMB13SCL";
+			bias-disable;
+			output-high;
+		};
+		gpio223_pins: gpio223-pins {
+			pins = "GPIO223/SMB13SDA";
+			bias-disable;
+			input-enable;
+		};
+		gpio223ol_pins: gpio223ol-pins {
+			pins = "GPIO223/SMB13SDA";
+			bias-disable;
+			output-low;
+		};
+		gpio224_pins: gpio224-pins {
+			pins = "GPIO224/SPIXCK";
+			bias-disable;
+			input-enable;
+		};
+		gpio224o_pins: gpio224o-pins {
+			pins = "GPIO224/SPIXCK";
+			bias-disable;
+			output-high;
+		};
+		gpio224ol_pins: gpio224ol-pins {
+			pins = "GPIO224/SPIXCK";
+			bias-disable;
+			output-low;
+		};
+		gpio225_pins: gpio225-pins {
+			pins = "GPO225/SPIXD0/STRAP12";
+			bias-disable;
+			input-enable;
+		};
+		gpio225o_pins: gpio225o-pins {
+			pins = "GPO225/SPIXD0/STRAP12";
+			bias-disable;
+			output-high;
+		};
+		gpio226_pins: gpio226-pins {
+			pins = "GPO226/SPIXD1/STRAP13";
+			bias-disable;
+			input-enable;
+		};
+		gpio226o_pins: gpio226o-pins {
+			pins = "GPO226/SPIXD1/STRAP13";
+			bias-disable;
+			output-high;
+		};
+		gpio227_pins: gpio227-pins {
+			pins = "GPIO227/nSPIXCS0";
+			bias-disable;
+			input-enable;
+		};
+		gpio227o_pins: gpio227o-pins {
+			pins = "GPIO227/nSPIXCS0";
+			bias-disable;
+			output-high;
+		};
+		gpio227ol_pins: gpio227ol-pins {
+			pins = "GPIO227/nSPIXCS0";
+			bias-disable;
+			output-low;
+		};
+		gpio228_pins: gpio228-pins {
+			pins = "GPIO228/nSPIXCS1";
+			bias-disable;
+			input-enable;
+		};
+		gpio228ol_pins: gpio228ol-pins {
+			pins = "GPIO228/nSPIXCS1";
+			bias-disable;
+			output-low;
+		};
+		gpio229_pins: gpio229-pins {
+			pins = "GPO229/SPIXD2/STRAP3";
+			bias-disable;
+			input-enable;
+		};
+		gpio229o_pins: gpio229o-pins {
+			pins = "GPO229/SPIXD2/STRAP3";
+			bias-disable;
+			output-high;
+		};
+		gpio230_pins: gpio230-pins {
+			pins = "GPIO230/SPIXD3";
+			bias-disable;
+			input-enable;
+		};
+		gpio230o_pins: gpio230o-pins {
+			pins = "GPIO230/SPIXD3";
+			bias-disable;
+			output-high;
+		};
+		gpio230ol_pins: gpio230ol-pins {
+			pins = "GPIO230/SPIXD3";
+			bias-disable;
+			output-low;
+		};
+		gpio231_pins: gpio231-pins {
+			pins = "GPIO231/nCLKREQ";
+			bias-disable;
+			input-enable;
+		};
+		gpio231o_pins: gpio231o-pins {
+			pins = "GPIO231/nCLKREQ";
+			bias-disable;
+			output-high;
+		};
+		gpio255_pins: gpio255-pins {
+			pins = "GPI255/DACOSEL";
+			bias-disable;
+			input-enable;
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/nuvoton-npcm750.dtsi b/arch/arm/boot/dts/nuvoton-npcm750.dtsi
index 6ac3405..14b3d5b 100644
--- a/arch/arm/boot/dts/nuvoton-npcm750.dtsi
+++ b/arch/arm/boot/dts/nuvoton-npcm750.dtsi
@@ -3,6 +3,7 @@
 // Copyright 2018 Google, Inc.
 
 #include "nuvoton-common-npcm7xx.dtsi"
+#include "nuvoton-npcm750-gpio.dtsi"
 
 / {
 	#address-cells = <1>;
@@ -17,7 +18,7 @@
 		cpu@0 {
 			device_type = "cpu";
 			compatible = "arm,cortex-a9";
-			clocks = <&clk 0>;
+			clocks = <&clk NPCM7XX_CLK_CPU>;
 			clock-names = "clk_cpu";
 			reg = <0>;
 			next-level-cache = <&l2>;
@@ -26,19 +27,152 @@
 		cpu@1 {
 			device_type = "cpu";
 			compatible = "arm,cortex-a9";
-			clocks = <&clk 0>;
+			clocks = <&clk NPCM7XX_CLK_CPU>;
 			clock-names = "clk_cpu";
 			reg = <1>;
 			next-level-cache = <&l2>;
 		};
 	};
+
 	soc {
 		timer@3fe600 {
 			compatible = "arm,cortex-a9-twd-timer";
 			reg = <0x3fe600 0x20>;
 			interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(2) |
 						  IRQ_TYPE_LEVEL_HIGH)>;
-			clocks = <&clk 5>;
+			clocks = <&clk NPCM7XX_CLK_AHB>;
+		};
+	};
+
+	ahb {
+		gmac1: eth@f0804000 {
+			device_type = "network";
+			compatible = "snps,dwmac";
+			reg = <0xf0804000 0x2000>;
+			interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "macirq";
+			ethernet = <1>;
+			clocks	= <&clk_rg2refck>, <&clk NPCM7XX_CLK_AHB>;
+			clock-names = "stmmaceth", "clk_gmac";
+			pinctrl-names = "default";
+			pinctrl-0 = <&rg2_pins
+					&rg2mdio_pins>;
+			status = "disabled";
+		};
+
+		emc1: eth@f0826000 {
+			device_type = "network";
+			compatible = "nuvoton,npcm750-emc";
+			reg = <0xf0826000 0x1000>;
+			interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>,
+					<GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&clk NPCM7XX_CLK_AHB>;
+			clock-names = "clk_emc";
+			pinctrl-names = "default";
+			pinctrl-0 = <&r2_pins
+					&r2err_pins
+					&r2md_pins>;
+			status = "disabled";
+		};
+
+		udc0:udc@f0830000 {
+			compatible = "nuvoton,npcm750-udc";
+			reg = <0xf0830000 0x1000
+			       0xfffd0000 0x800>;
+			interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+			clocks = <&clk NPCM7XX_CLK_SU>;
+			clock-names = "clk_usb_bridge";
+		};
+
+		udc1:udc@f0831000 {
+			compatible = "nuvoton,npcm750-udc";
+			reg = <0xf0831000 0x1000
+			       0xfffd0800 0x800>;
+			interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+			clocks = <&clk NPCM7XX_CLK_SU>;
+			clock-names = "clk_usb_bridge";
+		};
+
+		udc2:udc@f0832000 {
+			compatible = "nuvoton,npcm750-udc";
+			reg = <0xf0832000 0x1000
+			       0xfffd1000 0x800>;
+			interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+			clocks = <&clk NPCM7XX_CLK_SU>;
+			clock-names = "clk_usb_bridge";
+		};
+
+		udc3:udc@f0833000 {
+			compatible = "nuvoton,npcm750-udc";
+			reg = <0xf0833000 0x1000
+			       0xfffd1800 0x800>;
+			interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+			clocks = <&clk NPCM7XX_CLK_SU>;
+			clock-names = "clk_usb_bridge";
+		};
+
+		udc4:udc@f0834000 {
+			compatible = "nuvoton,npcm750-udc";
+			reg = <0xf0834000 0x1000
+			       0xfffd2000 0x800>;
+			interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+			clocks = <&clk NPCM7XX_CLK_SU>;
+			clock-names = "clk_usb_bridge";
+		};
+
+		udc5:udc@f0835000 {
+			compatible = "nuvoton,npcm750-udc";
+			reg = <0xf0835000 0x1000
+			       0xfffd2800 0x800>;
+			interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+			clocks = <&clk NPCM7XX_CLK_SU>;
+			clock-names = "clk_usb_bridge";
+		};
+
+		udc6:udc@f0836000 {
+			compatible = "nuvoton,npcm750-udc";
+			reg = <0xf0836000 0x1000
+			       0xfffd3000 0x800>;
+			interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+			clocks = <&clk NPCM7XX_CLK_SU>;
+			clock-names = "clk_usb_bridge";
+		};
+
+		udc7:udc@f0837000 {
+			compatible = "nuvoton,npcm750-udc";
+			reg = <0xf0837000 0x1000
+			       0xfffd3800 0x800>;
+			interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+			clocks = <&clk NPCM7XX_CLK_SU>;
+			clock-names = "clk_usb_bridge";
+		};
+
+		udc8:udc@f0838000 {
+			compatible = "nuvoton,npcm750-udc";
+			reg = <0xf0838000 0x1000
+			       0xfffd4000 0x800>;
+			interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+			clocks = <&clk NPCM7XX_CLK_SU>;
+			clock-names = "clk_usb_bridge";
+		};
+
+		udc9:udc@f0839000 {
+			compatible = "nuvoton,npcm750-udc";
+			reg = <0xf0839000 0x1000
+			       0xfffd4800 0x800>;
+			interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+			clocks = <&clk NPCM7XX_CLK_SU>;
+			clock-names = "clk_usb_bridge";
 		};
 	};
 };
diff --git a/arch/arm/configs/aspeed_g4_defconfig b/arch/arm/configs/aspeed_g4_defconfig
index 1446262..b3d9a95 100644
--- a/arch/arm/configs/aspeed_g4_defconfig
+++ b/arch/arm/configs/aspeed_g4_defconfig
@@ -3,6 +3,8 @@
 CONFIG_SYSVIPC=y
 CONFIG_NO_HZ_IDLE=y
 CONFIG_HIGH_RES_TIMERS=y
+CONFIG_PSI=y
+CONFIG_PSI_DEFAULT_DISABLED=y
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_BUF_SHIFT=16
@@ -20,30 +22,27 @@
 # CONFIG_COMPAT_BRK is not set
 CONFIG_SLAB=y
 CONFIG_SLAB_FREELIST_RANDOM=y
-CONFIG_JUMP_LABEL=y
-CONFIG_STRICT_KERNEL_RWX=y
-CONFIG_GCC_PLUGINS=y
-# CONFIG_LBDAF is not set
-# CONFIG_BLK_DEV_BSG is not set
-# CONFIG_BLK_DEBUG_FS is not set
-# CONFIG_IOSCHED_DEADLINE is not set
-# CONFIG_MQ_IOSCHED_DEADLINE is not set
-# CONFIG_MQ_IOSCHED_KYBER is not set
 # CONFIG_ARCH_MULTI_V7 is not set
 CONFIG_ARCH_ASPEED=y
 CONFIG_MACH_ASPEED_G4=y
 CONFIG_VMSPLIT_2G=y
 CONFIG_AEABI=y
-# CONFIG_COMPACTION is not set
 CONFIG_UACCESS_WITH_MEMCPY=y
 CONFIG_SECCOMP=y
 # CONFIG_ATAGS is not set
 CONFIG_ZBOOT_ROM_TEXT=0x0
 CONFIG_ZBOOT_ROM_BSS=0x0
-CONFIG_ARM_APPENDED_DTB=y
-CONFIG_ARM_ATAG_DTB_COMPAT=y
 CONFIG_KEXEC=y
+CONFIG_FIRMWARE_MEMMAP=y
+CONFIG_JUMP_LABEL=y
+CONFIG_STRICT_KERNEL_RWX=y
+# CONFIG_LBDAF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEBUG_FS is not set
+# CONFIG_MQ_IOSCHED_DEADLINE is not set
+# CONFIG_MQ_IOSCHED_KYBER is not set
 # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_COMPACTION is not set
 CONFIG_NET=y
 CONFIG_PACKET=y
 CONFIG_PACKET_DIAG=y
@@ -51,19 +50,26 @@
 CONFIG_UNIX_DIAG=y
 CONFIG_INET=y
 CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
 CONFIG_SYN_COOKIES=y
 # CONFIG_INET_XFRM_MODE_TRANSPORT is not set
 # CONFIG_INET_XFRM_MODE_TUNNEL is not set
 # CONFIG_INET_XFRM_MODE_BEET is not set
 # CONFIG_INET_DIAG is not set
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
 # CONFIG_INET6_XFRM_MODE_TRANSPORT is not set
 # CONFIG_INET6_XFRM_MODE_TUNNEL is not set
 # CONFIG_INET6_XFRM_MODE_BEET is not set
+CONFIG_IPV6_MULTIPLE_TABLES=y
 CONFIG_NETFILTER=y
 # CONFIG_NETFILTER_ADVANCED is not set
 CONFIG_VLAN_8021Q=y
 CONFIG_NET_NCSI=y
-CONFIG_BPF_STREAM_PARSER=y
 # CONFIG_WIRELESS is not set
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 CONFIG_DEVTMPFS=y
@@ -79,8 +85,10 @@
 CONFIG_MTD_UBI_FASTMAP=y
 CONFIG_MTD_UBI_BLOCK=y
 CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_NBD=y
 CONFIG_ASPEED_LPC_CTRL=y
 CONFIG_ASPEED_LPC_SNOOP=y
+CONFIG_ASPEED_LPC_MBOX=y
 CONFIG_EEPROM_AT24=y
 CONFIG_NETDEVICES=y
 CONFIG_NETCONSOLE=y
@@ -102,6 +110,7 @@
 # CONFIG_NET_VENDOR_MARVELL is not set
 # CONFIG_NET_VENDOR_MELLANOX is not set
 # CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
 # CONFIG_NET_VENDOR_MICROSEMI is not set
 # CONFIG_NET_VENDOR_NATSEMI is not set
 # CONFIG_NET_VENDOR_NETRONOME is not set
@@ -159,6 +168,10 @@
 CONFIG_SENSORS_IIO_HWMON=y
 CONFIG_SENSORS_LM75=y
 CONFIG_SENSORS_NCT7904=y
+CONFIG_SENSORS_OCC_P8_I2C=y
+CONFIG_SENSORS_OCC_P9_SBE=y
+CONFIG_SENSORS_PECI_CPUTEMP=y
+CONFIG_SENSORS_PECI_DIMMTEMP=y
 CONFIG_PMBUS=y
 CONFIG_SENSORS_ADM1275=y
 CONFIG_SENSORS_IBM_CFFPS=y
@@ -171,6 +184,7 @@
 CONFIG_SENSORS_W83773G=y
 CONFIG_WATCHDOG_SYSFS=y
 CONFIG_DRM=y
+CONFIG_DRM_ASPEED_GFX=y
 CONFIG_USB=y
 CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
 CONFIG_USB_DYNAMIC_MINORS=y
@@ -208,6 +222,7 @@
 CONFIG_RTC_DRV_DS1307=y
 CONFIG_RTC_DRV_PCF8523=y
 CONFIG_RTC_DRV_RV8803=y
+CONFIG_RTC_DRV_ASPEED=y
 # CONFIG_VIRTIO_MENU is not set
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_IIO=y
@@ -220,7 +235,9 @@
 CONFIG_FSI_MASTER_AST_CF=y
 CONFIG_FSI_SCOM=y
 CONFIG_FSI_SBEFIFO=y
-CONFIG_FIRMWARE_MEMMAP=y
+CONFIG_FSI_OCC=y
+CONFIG_PECI=y
+CONFIG_PECI_ASPEED=y
 CONFIG_FANOTIFY=y
 CONFIG_OVERLAY_FS=y
 CONFIG_TMPFS=y
@@ -233,6 +250,17 @@
 CONFIG_SQUASHFS_XZ=y
 CONFIG_SQUASHFS_ZSTD=y
 # CONFIG_NETWORK_FILESYSTEMS is not set
+CONFIG_HARDENED_USERCOPY=y
+CONFIG_FORTIFY_SOURCE=y
+# CONFIG_CRYPTO_ECHAINIV is not set
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_USER_API_HASH=y
+# CONFIG_CRYPTO_HW is not set
+# CONFIG_XZ_DEC_X86 is not set
+# CONFIG_XZ_DEC_POWERPC is not set
+# CONFIG_XZ_DEC_IA64 is not set
+# CONFIG_XZ_DEC_SPARC is not set
 CONFIG_PRINTK_TIME=y
 CONFIG_DYNAMIC_DEBUG=y
 CONFIG_DEBUG_INFO=y
@@ -252,14 +280,3 @@
 # CONFIG_RUNTIME_TESTING_MENU is not set
 CONFIG_DEBUG_WX=y
 CONFIG_DEBUG_USER=y
-CONFIG_HARDENED_USERCOPY=y
-CONFIG_FORTIFY_SOURCE=y
-# CONFIG_CRYPTO_ECHAINIV is not set
-CONFIG_CRYPTO_HMAC=y
-CONFIG_CRYPTO_SHA256=y
-CONFIG_CRYPTO_USER_API_HASH=y
-# CONFIG_CRYPTO_HW is not set
-# CONFIG_XZ_DEC_X86 is not set
-# CONFIG_XZ_DEC_POWERPC is not set
-# CONFIG_XZ_DEC_IA64 is not set
-# CONFIG_XZ_DEC_SPARC is not set
diff --git a/arch/arm/configs/aspeed_g5_defconfig b/arch/arm/configs/aspeed_g5_defconfig
index 02fa3a4..1849cbc 100644
--- a/arch/arm/configs/aspeed_g5_defconfig
+++ b/arch/arm/configs/aspeed_g5_defconfig
@@ -3,6 +3,8 @@
 CONFIG_SYSVIPC=y
 CONFIG_NO_HZ_IDLE=y
 CONFIG_HIGH_RES_TIMERS=y
+CONFIG_PSI=y
+CONFIG_PSI_DEFAULT_DISABLED=y
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_BUF_SHIFT=16
@@ -20,30 +22,27 @@
 # CONFIG_COMPAT_BRK is not set
 CONFIG_SLAB=y
 CONFIG_SLAB_FREELIST_RANDOM=y
-CONFIG_JUMP_LABEL=y
-CONFIG_STRICT_KERNEL_RWX=y
-CONFIG_GCC_PLUGINS=y
-# CONFIG_LBDAF is not set
-# CONFIG_BLK_DEV_BSG is not set
-# CONFIG_BLK_DEBUG_FS is not set
-# CONFIG_IOSCHED_DEADLINE is not set
-# CONFIG_MQ_IOSCHED_DEADLINE is not set
-# CONFIG_MQ_IOSCHED_KYBER is not set
 CONFIG_ARCH_MULTI_V6=y
-# CONFIG_ARCH_MULTI_V7 is not set
 CONFIG_ARCH_ASPEED=y
 CONFIG_MACH_ASPEED_G5=y
 # CONFIG_CACHE_L2X0 is not set
 CONFIG_VMSPLIT_2G=y
-# CONFIG_COMPACTION is not set
 CONFIG_UACCESS_WITH_MEMCPY=y
 CONFIG_SECCOMP=y
 # CONFIG_ATAGS is not set
 CONFIG_ZBOOT_ROM_TEXT=0x0
 CONFIG_ZBOOT_ROM_BSS=0x0
 CONFIG_KEXEC=y
-# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
 # CONFIG_SUSPEND is not set
+CONFIG_FIRMWARE_MEMMAP=y
+CONFIG_JUMP_LABEL=y
+# CONFIG_LBDAF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEBUG_FS is not set
+# CONFIG_MQ_IOSCHED_DEADLINE is not set
+# CONFIG_MQ_IOSCHED_KYBER is not set
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_COMPACTION is not set
 CONFIG_NET=y
 CONFIG_PACKET=y
 CONFIG_PACKET_DIAG=y
@@ -51,19 +50,26 @@
 CONFIG_UNIX_DIAG=y
 CONFIG_INET=y
 CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
 CONFIG_SYN_COOKIES=y
 # CONFIG_INET_XFRM_MODE_TRANSPORT is not set
 # CONFIG_INET_XFRM_MODE_TUNNEL is not set
 # CONFIG_INET_XFRM_MODE_BEET is not set
 # CONFIG_INET_DIAG is not set
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
 # CONFIG_INET6_XFRM_MODE_TRANSPORT is not set
 # CONFIG_INET6_XFRM_MODE_TUNNEL is not set
 # CONFIG_INET6_XFRM_MODE_BEET is not set
+CONFIG_IPV6_MULTIPLE_TABLES=y
 CONFIG_NETFILTER=y
 # CONFIG_NETFILTER_ADVANCED is not set
 CONFIG_VLAN_8021Q=y
 CONFIG_NET_NCSI=y
-CONFIG_BPF_STREAM_PARSER=y
 # CONFIG_WIRELESS is not set
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 CONFIG_DEVTMPFS=y
@@ -79,8 +85,10 @@
 CONFIG_MTD_UBI_FASTMAP=y
 CONFIG_MTD_UBI_BLOCK=y
 CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_NBD=y
 CONFIG_ASPEED_LPC_CTRL=y
 CONFIG_ASPEED_LPC_SNOOP=y
+CONFIG_ASPEED_LPC_MBOX=y
 CONFIG_EEPROM_AT24=y
 CONFIG_NETDEVICES=y
 CONFIG_NETCONSOLE=y
@@ -102,6 +110,7 @@
 # CONFIG_NET_VENDOR_MARVELL is not set
 # CONFIG_NET_VENDOR_MELLANOX is not set
 # CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
 # CONFIG_NET_VENDOR_MICROSEMI is not set
 # CONFIG_NET_VENDOR_NATSEMI is not set
 # CONFIG_NET_VENDOR_NETRONOME is not set
@@ -159,6 +168,10 @@
 CONFIG_SENSORS_IIO_HWMON=y
 CONFIG_SENSORS_LM75=y
 CONFIG_SENSORS_NCT7904=y
+CONFIG_SENSORS_OCC_P8_I2C=y
+CONFIG_SENSORS_OCC_P9_SBE=y
+CONFIG_SENSORS_PECI_CPUTEMP=y
+CONFIG_SENSORS_PECI_DIMMTEMP=y
 CONFIG_PMBUS=y
 CONFIG_SENSORS_ADM1275=y
 CONFIG_SENSORS_IBM_CFFPS=y
@@ -170,7 +183,12 @@
 CONFIG_SENSORS_TMP421=y
 CONFIG_SENSORS_W83773G=y
 CONFIG_WATCHDOG_SYSFS=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_VIDEO_ASPEED=y
 CONFIG_DRM=y
+CONFIG_DRM_ASPEED_GFX=y
 CONFIG_USB=y
 CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
 CONFIG_USB_DYNAMIC_MINORS=y
@@ -208,19 +226,23 @@
 CONFIG_RTC_DRV_DS1307=y
 CONFIG_RTC_DRV_PCF8523=y
 CONFIG_RTC_DRV_RV8803=y
+CONFIG_RTC_DRV_ASPEED=y
 # CONFIG_VIRTIO_MENU is not set
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_IIO=y
 CONFIG_ASPEED_ADC=y
 CONFIG_MAX1363=y
 CONFIG_BMP280=y
+CONFIG_DPS310=y
 CONFIG_FSI=y
 CONFIG_FSI_MASTER_GPIO=y
 CONFIG_FSI_MASTER_HUB=y
 CONFIG_FSI_MASTER_AST_CF=y
 CONFIG_FSI_SCOM=y
 CONFIG_FSI_SBEFIFO=y
-CONFIG_FIRMWARE_MEMMAP=y
+CONFIG_FSI_OCC=y
+CONFIG_PECI=y
+CONFIG_PECI_ASPEED=y
 CONFIG_FANOTIFY=y
 CONFIG_OVERLAY_FS=y
 CONFIG_TMPFS=y
@@ -233,6 +255,17 @@
 CONFIG_SQUASHFS_XZ=y
 CONFIG_SQUASHFS_ZSTD=y
 # CONFIG_NETWORK_FILESYSTEMS is not set
+CONFIG_HARDENED_USERCOPY=y
+CONFIG_FORTIFY_SOURCE=y
+# CONFIG_CRYPTO_ECHAINIV is not set
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_USER_API_HASH=y
+# CONFIG_CRYPTO_HW is not set
+# CONFIG_XZ_DEC_X86 is not set
+# CONFIG_XZ_DEC_POWERPC is not set
+# CONFIG_XZ_DEC_IA64 is not set
+# CONFIG_XZ_DEC_SPARC is not set
 CONFIG_PRINTK_TIME=y
 CONFIG_DYNAMIC_DEBUG=y
 CONFIG_DEBUG_INFO=y
@@ -252,14 +285,3 @@
 # CONFIG_RUNTIME_TESTING_MENU is not set
 CONFIG_DEBUG_WX=y
 CONFIG_DEBUG_USER=y
-CONFIG_HARDENED_USERCOPY=y
-CONFIG_FORTIFY_SOURCE=y
-# CONFIG_CRYPTO_ECHAINIV is not set
-CONFIG_CRYPTO_HMAC=y
-CONFIG_CRYPTO_SHA256=y
-CONFIG_CRYPTO_USER_API_HASH=y
-# CONFIG_CRYPTO_HW is not set
-# CONFIG_XZ_DEC_X86 is not set
-# CONFIG_XZ_DEC_POWERPC is not set
-# CONFIG_XZ_DEC_IA64 is not set
-# CONFIG_XZ_DEC_SPARC is not set
diff --git a/arch/arm/configs/npcm7xx_defconfig b/arch/arm/configs/npcm7xx_defconfig
new file mode 100644
index 0000000..62aaffd
--- /dev/null
+++ b/arch/arm/configs/npcm7xx_defconfig
@@ -0,0 +1,127 @@
+CONFIG_KERNEL_XZ=y
+CONFIG_SYSVIPC=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_LOG_BUF_SHIFT=21
+CONFIG_CGROUPS=y
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_EMBEDDED=y
+CONFIG_SLAB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_DEFAULT_DEADLINE=y
+CONFIG_ARCH_NPCM=y
+CONFIG_ARCH_NPCM7XX=y
+CONFIG_SMP=y
+CONFIG_VMSPLIT_3G_OPT=y
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_BINFMT_MISC=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_MTD=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_RAM=y
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_SPI_NPCM_FIU=y
+CONFIG_OF_OVERLAY=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_NBD=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=1
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_NPCM7XX_LPC_BPC=y
+CONFIG_NPCM7XX_PCI_MBOX=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_NETDEVICES=y
+CONFIG_NPCM7XX_EMC_ETH=y
+CONFIG_STMMAC_ETH=y
+CONFIG_BROADCOM_PHY=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_NPCM750_OTP=y
+CONFIG_NPCM750_OTP_WRITE_ENABLE=y
+CONFIG_NPCM7XX_KCS_IPMI_BMC=y
+CONFIG_HW_RANDOM=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_NPCM7XX=y
+CONFIG_I2C_SLAVE_EEPROM=y
+CONFIG_SPI=y
+CONFIG_SPI_NPCM_PSPI=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_GENERIC_PLATFORM=y
+CONFIG_SENSORS_LM75=y
+CONFIG_SENSORS_NPCM7XX=y
+CONFIG_SENSORS_TMP102=y
+CONFIG_WATCHDOG=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_NPCM750_VCD=y
+CONFIG_NPCM750_ECE=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_CHIPIDEA=y
+CONFIG_USB_CHIPIDEA_UDC=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_NPCMX50_USB2=y
+CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_F_HID=y
+CONFIG_USB_MASS_STORAGE=m
+CONFIG_USB_EDM_KBD_MOUSE=m
+CONFIG_MMC=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_NPCM750=y
+CONFIG_IIO=y
+CONFIG_NPCM_ADC=y
+CONFIG_IIO_MUX=y
+CONFIG_MUX_MMIO=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_ROMFS_FS=y
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_CIFS=y
+CONFIG_CIFS_XATTR=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_INFO_REDUCED=y
+CONFIG_READABLE_ASM=y
+CONFIG_DEBUG_SECTION_MISMATCH=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_FUNCTION_TRACER=y
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_LZO=y
+CONFIG_CRYPTO_USER_API_SKCIPHER=y
+CONFIG_CRYPTO_DEV_NPCMX50=y
+CONFIG_ARM_CRYPTO=y
+CONFIG_PECI=y
+CONFIG_PECI_NPCM=y
+CONFIG_MFD_INTEL_PECI_CLIENT=y
+CONFIG_SENSORS_PECI_CPUTEMP=y
+CONFIG_SENSORS_PECI_DIMMTEMP=y
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 4f9f990..bbb6643 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -228,4 +228,6 @@
 
 source "drivers/slimbus/Kconfig"
 
+source "drivers/peci/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index e1ce029d..9ec44c0 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -186,3 +186,4 @@
 obj-$(CONFIG_UNISYS_VISORBUS)	+= visorbus/
 obj-$(CONFIG_SIOX)		+= siox/
 obj-$(CONFIG_GNSS)		+= gnss/
+obj-$(CONFIG_PECI)		+= peci/
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 51c77f0..79afe2e 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -16,6 +16,15 @@
 	  memory.
 	  When in doubt, say "Y".
 
+config DEVMEM_BOOTPARAM
+	bool "mem.devmem boot parameter"
+	depends on DEVMEM
+	default n
+	help
+	  This option adds a 'mem.devmem' kernel parameter which activates
+	  the /dev/mem device when enabled.
+	  When in doubt, say "N".
+
 config DEVKMEM
 	bool "/dev/kmem virtual device support"
 	# On arm64, VMALLOC_START < PAGE_OFFSET, which confuses kmem read/write
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index b08dc50..591366b 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -10,6 +10,7 @@
  */
 
 #include <linux/mm.h>
+#include <linux/moduleparam.h>
 #include <linux/miscdevice.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
@@ -36,6 +37,7 @@
 # include <linux/efi.h>
 #endif
 
+#define DEVMEM_MINOR	1
 #define DEVPORT_MINOR	4
 
 static inline unsigned long size_inside_page(unsigned long start,
@@ -913,6 +915,12 @@ static char *mem_devnode(struct device *dev, umode_t *mode)
 	return NULL;
 }
 
+#ifdef CONFIG_DEVMEM_BOOTPARAM
+static bool devmem;
+module_param(devmem, bool, 0444);
+MODULE_PARM_DESC(devmem, "kernel parameter to activate /dev/mem");
+#endif
+
 static struct class *mem_class;
 
 static int __init chr_dev_init(void)
@@ -931,6 +939,10 @@ static int __init chr_dev_init(void)
 		if (!devlist[minor].name)
 			continue;
 
+#ifdef CONFIG_DEVMEM_BOOTPARAM
+		if (minor == DEVMEM_MINOR && !devmem)
+			continue;
+#endif
 		/*
 		 * Create /dev/port?
 		 */
diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c
index 5961367..42b4df6 100644
--- a/drivers/clk/clk-aspeed.c
+++ b/drivers/clk/clk-aspeed.c
@@ -87,10 +87,10 @@ struct aspeed_clk_gate {
 /* TODO: ask Aspeed about the actual parent data */
 static const struct aspeed_gate_data aspeed_gates[] = {
 	/*				 clk rst   name			parent	flags */
-	[ASPEED_CLK_GATE_ECLK] =	{  0, -1, "eclk-gate",		"eclk",	0 }, /* Video Engine */
+	[ASPEED_CLK_GATE_ECLK] =	{  0,  6, "eclk-gate",		"eclk",	0 }, /* Video Engine */
 	[ASPEED_CLK_GATE_GCLK] =	{  1,  7, "gclk-gate",		NULL,	0 }, /* 2D engine */
 	[ASPEED_CLK_GATE_MCLK] =	{  2, -1, "mclk-gate",		"mpll",	CLK_IS_CRITICAL }, /* SDRAM */
-	[ASPEED_CLK_GATE_VCLK] =	{  3,  6, "vclk-gate",		NULL,	0 }, /* Video Capture */
+	[ASPEED_CLK_GATE_VCLK] =	{  3, -1, "vclk-gate",		NULL,	0 }, /* Video Capture */
 	[ASPEED_CLK_GATE_BCLK] =	{  4,  8, "bclk-gate",		"bclk",	CLK_IS_CRITICAL }, /* PCIe/PCI */
 	[ASPEED_CLK_GATE_DCLK] =	{  5, -1, "dclk-gate",		NULL,	CLK_IS_CRITICAL }, /* DAC */
 	[ASPEED_CLK_GATE_REFCLK] =	{  6, -1, "refclk-gate",	"clkin", CLK_IS_CRITICAL },
@@ -113,6 +113,24 @@ static const struct aspeed_gate_data aspeed_gates[] = {
 	[ASPEED_CLK_GATE_LHCCLK] =	{ 28, -1, "lhclk-gate",		"lhclk", 0 }, /* LPC master/LPC+ */
 };
 
+static const char * const eclk_parent_names[] = {
+	"mpll",
+	"hpll",
+	"dpll",
+};
+
+static const struct clk_div_table ast2500_eclk_div_table[] = {
+	{ 0x0, 2 },
+	{ 0x1, 2 },
+	{ 0x2, 3 },
+	{ 0x3, 4 },
+	{ 0x4, 5 },
+	{ 0x5, 6 },
+	{ 0x6, 7 },
+	{ 0x7, 8 },
+	{ 0 }
+};
+
 static const struct clk_div_table ast2500_mac_div_table[] = {
 	{ 0x0, 4 }, /* Yep, really. Aspeed confirmed this is correct */
 	{ 0x1, 4 },
@@ -192,18 +210,21 @@ static struct clk_hw *aspeed_ast2500_calc_pll(const char *name, u32 val)
 
 struct aspeed_clk_soc_data {
 	const struct clk_div_table *div_table;
+	const struct clk_div_table *eclk_div_table;
 	const struct clk_div_table *mac_div_table;
 	struct clk_hw *(*calc_pll)(const char *name, u32 val);
 };
 
 static const struct aspeed_clk_soc_data ast2500_data = {
 	.div_table = ast2500_div_table,
+	.eclk_div_table = ast2500_eclk_div_table,
 	.mac_div_table = ast2500_mac_div_table,
 	.calc_pll = aspeed_ast2500_calc_pll,
 };
 
 static const struct aspeed_clk_soc_data ast2400_data = {
 	.div_table = ast2400_div_table,
+	.eclk_div_table = ast2400_div_table,
 	.mac_div_table = ast2400_div_table,
 	.calc_pll = aspeed_ast2400_calc_pll,
 };
@@ -522,6 +543,22 @@ static int aspeed_clk_probe(struct platform_device *pdev)
 		return PTR_ERR(hw);
 	aspeed_clk_data->hws[ASPEED_CLK_24M] = hw;
 
+	hw = clk_hw_register_mux(dev, "eclk-mux", eclk_parent_names,
+				 ARRAY_SIZE(eclk_parent_names), 0,
+				 scu_base + ASPEED_CLK_SELECTION, 2, 0x3, 0,
+				 &aspeed_clk_lock);
+	if (IS_ERR(hw))
+		return PTR_ERR(hw);
+	aspeed_clk_data->hws[ASPEED_CLK_ECLK_MUX] = hw;
+
+	hw = clk_hw_register_divider_table(dev, "eclk", "eclk-mux", 0,
+					   scu_base + ASPEED_CLK_SELECTION, 28,
+					   3, 0, soc_data->eclk_div_table,
+					   &aspeed_clk_lock);
+	if (IS_ERR(hw))
+		return PTR_ERR(hw);
+	aspeed_clk_data->hws[ASPEED_CLK_ECLK] = hw;
+
 	/*
 	 * TODO: There are a number of clocks that not included in this driver
 	 * as more information is required:
@@ -531,7 +568,6 @@ static int aspeed_clk_probe(struct platform_device *pdev)
 	 *   RGMII
 	 *   RMII
 	 *   UART[1..5] clock source mux
-	 *   Video Engine (ECLK) mux and clock divider
 	 */
 
 	for (i = 0; i < ARRAY_SIZE(aspeed_gates); i++) {
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 4385f00..0add640 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -333,6 +333,8 @@
 
 source "drivers/gpu/drm/xen/Kconfig"
 
+source "drivers/gpu/drm/aspeed/Kconfig"
+
 # Keep legacy drivers last
 
 menuconfig DRM_LEGACY
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index ce8d1d3..8e2f12a 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -109,3 +109,4 @@
 obj-$(CONFIG_DRM_PL111) += pl111/
 obj-$(CONFIG_DRM_TVE200) += tve200/
 obj-$(CONFIG_DRM_XEN) += xen/
+obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/
diff --git a/drivers/gpu/drm/aspeed/Kconfig b/drivers/gpu/drm/aspeed/Kconfig
new file mode 100644
index 0000000..42b74d1
--- /dev/null
+++ b/drivers/gpu/drm/aspeed/Kconfig
@@ -0,0 +1,14 @@
+config DRM_ASPEED_GFX
+	tristate "ASPEED BMC Display Controller"
+	depends on DRM && OF
+	select DRM_KMS_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_PANEL
+	select DMA_CMA
+	select CMA
+	select MFD_SYSCON
+	help
+	  Chose this option if you have an ASPEED AST2500 SOC Display
+	  Controller (aka GFX).
+
+	  If M is selected this module will be called aspeed_gfx.
diff --git a/drivers/gpu/drm/aspeed/Makefile b/drivers/gpu/drm/aspeed/Makefile
new file mode 100644
index 0000000..6e194cd
--- /dev/null
+++ b/drivers/gpu/drm/aspeed/Makefile
@@ -0,0 +1,3 @@
+aspeed_gfx-y := aspeed_gfx_drv.o aspeed_gfx_crtc.o aspeed_gfx_out.o
+
+obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed_gfx.o
diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx.h b/drivers/gpu/drm/aspeed/aspeed_gfx.h
new file mode 100644
index 0000000..b7a986e
--- /dev/null
+++ b/drivers/gpu/drm/aspeed/aspeed_gfx.h
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2018 IBM Corporation
+
+#include <drm/drm_device.h>
+#include <drm/drm_simple_kms_helper.h>
+
+struct aspeed_gfx {
+	void __iomem			*base;
+	struct clk			*clk;
+	struct reset_control		*rst;
+	struct regmap			*scu;
+
+	struct drm_simple_display_pipe	pipe;
+	struct drm_connector		connector;
+	struct drm_fbdev_cma		*fbdev;
+};
+
+int aspeed_gfx_create_pipe(struct drm_device *drm);
+int aspeed_gfx_create_output(struct drm_device *drm);
+
+#define CRT_CTRL1		0x60 /* CRT Control I */
+#define CRT_CTRL2		0x64 /* CRT Control II */
+#define CRT_STATUS		0x68 /* CRT Status */
+#define CRT_MISC		0x6c /* CRT Misc Setting */
+#define CRT_HORIZ0		0x70 /* CRT Horizontal Total & Display Enable End */
+#define CRT_HORIZ1		0x74 /* CRT Horizontal Retrace Start & End */
+#define CRT_VERT0		0x78 /* CRT Vertical Total & Display Enable End */
+#define CRT_VERT1		0x7C /* CRT Vertical Retrace Start & End */
+#define CRT_ADDR		0x80 /* CRT Display Starting Address */
+#define CRT_OFFSET		0x84 /* CRT Display Offset & Terminal Count */
+#define CRT_THROD		0x88 /* CRT Threshold */
+#define CRT_XSCALE		0x8C /* CRT Scaling-Up Factor */
+#define CRT_CURSOR0		0x90 /* CRT Hardware Cursor X & Y Offset */
+#define CRT_CURSOR1		0x94 /* CRT Hardware Cursor X & Y Position */
+#define CRT_CURSOR2		0x98 /* CRT Hardware Cursor Pattern Address */
+#define CRT_9C			0x9C
+#define CRT_OSD_H		0xA0 /* CRT OSD Horizontal Start/End */
+#define CRT_OSD_V		0xA4 /* CRT OSD Vertical Start/End */
+#define CRT_OSD_ADDR		0xA8 /* CRT OSD Pattern Address */
+#define CRT_OSD_DISP		0xAC /* CRT OSD Offset */
+#define CRT_OSD_THRESH		0xB0 /* CRT OSD Threshold & Alpha */
+#define CRT_B4			0xB4
+#define CRT_STS_V		0xB8 /* CRT Status V */
+#define CRT_SCRATCH		0xBC /* Scratchpad */
+#define CRT_BB0_ADDR		0xD0 /* CRT Display BB0 Starting Address */
+#define CRT_BB1_ADDR		0xD4 /* CRT Display BB1 Starting Address */
+#define CRT_BB_COUNT		0xD8 /* CRT Display BB Terminal Count */
+#define OSD_COLOR1		0xE0 /* OSD Color Palette Index 1 & 0 */
+#define OSD_COLOR2		0xE4 /* OSD Color Palette Index 3 & 2 */
+#define OSD_COLOR3		0xE8 /* OSD Color Palette Index 5 & 4 */
+#define OSD_COLOR4		0xEC /* OSD Color Palette Index 7 & 6 */
+#define OSD_COLOR5		0xF0 /* OSD Color Palette Index 9 & 8 */
+#define OSD_COLOR6		0xF4 /* OSD Color Palette Index 11 & 10 */
+#define OSD_COLOR7		0xF8 /* OSD Color Palette Index 13 & 12 */
+#define OSD_COLOR8		0xFC /* OSD Color Palette Index 15 & 14 */
+
+/* CTRL1 */
+#define CRT_CTRL_EN			BIT(0)
+#define CRT_CTRL_HW_CURSOR_EN		BIT(1)
+#define CRT_CTRL_OSD_EN			BIT(2)
+#define CRT_CTRL_INTERLACED		BIT(3)
+#define CRT_CTRL_COLOR_RGB565		(0 << 7)
+#define CRT_CTRL_COLOR_YUV444		(1 << 7)
+#define CRT_CTRL_COLOR_XRGB8888		(2 << 7)
+#define CRT_CTRL_COLOR_RGB888		(3 << 7)
+#define CRT_CTRL_COLOR_YUV444_2RGB	(5 << 7)
+#define CRT_CTRL_COLOR_YUV422		(7 << 7)
+#define CRT_CTRL_COLOR_MASK		GENMASK(9, 7)
+#define CRT_CTRL_HSYNC_NEGATIVE		BIT(16)
+#define CRT_CTRL_VSYNC_NEGATIVE		BIT(17)
+#define CRT_CTRL_VERTICAL_INTR_EN	BIT(30)
+#define CRT_CTRL_VERTICAL_INTR_STS	BIT(31)
+
+/* CTRL2 */
+#define CRT_CTRL_DAC_EN			BIT(0)
+#define CRT_CTRL_VBLANK_LINE(x)		(((x) << 20) & CRT_CTRL_VBLANK_LINE_MASK)
+#define CRT_CTRL_VBLANK_LINE_MASK	GENMASK(20, 31)
+
+/* CRT_HORIZ0 */
+#define CRT_H_TOTAL(x)			(x)
+#define CRT_H_DE(x)			((x) << 16)
+
+/* CRT_HORIZ1 */
+#define CRT_H_RS_START(x)		(x)
+#define CRT_H_RS_END(x)			((x) << 16)
+
+/* CRT_VIRT0 */
+#define CRT_V_TOTAL(x)			(x)
+#define CRT_V_DE(x)			((x) << 16)
+
+/* CRT_VIRT1 */
+#define CRT_V_RS_START(x)		(x)
+#define CRT_V_RS_END(x)			((x) << 16)
+
+/* CRT_OFFSET */
+#define CRT_DISP_OFFSET(x)		(x)
+#define CRT_TERM_COUNT(x)		((x) << 16)
+
+/* CRT_THROD */
+#define CRT_THROD_LOW(x)		(x)
+#define CRT_THROD_HIGH(x)		((x) << 8)
+
+/* Default Threshold Seting */
+#define G5_CRT_THROD_VAL	(CRT_THROD_LOW(0x24) | CRT_THROD_HIGH(0x3C))
diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c
new file mode 100644
index 0000000..15db9e4
--- /dev/null
+++ b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2018 IBM Corporation
+
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_device.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/drm_vblank.h>
+
+#include "aspeed_gfx.h"
+
+static struct aspeed_gfx *
+drm_pipe_to_aspeed_gfx(struct drm_simple_display_pipe *pipe)
+{
+	return container_of(pipe, struct aspeed_gfx, pipe);
+}
+
+static int aspeed_gfx_set_pixel_fmt(struct aspeed_gfx *priv, u32 *bpp)
+{
+	struct drm_crtc *crtc = &priv->pipe.crtc;
+	struct drm_device *drm = crtc->dev;
+	const u32 format = crtc->primary->state->fb->format->format;
+	u32 ctrl1;
+
+	ctrl1 = readl(priv->base + CRT_CTRL1);
+	ctrl1 &= ~CRT_CTRL_COLOR_MASK;
+
+	switch (format) {
+	case DRM_FORMAT_RGB565:
+		dev_dbg(drm->dev, "Setting up RGB565 mode\n");
+		ctrl1 |= CRT_CTRL_COLOR_RGB565;
+		*bpp = 16;
+		break;
+	case DRM_FORMAT_XRGB8888:
+		dev_dbg(drm->dev, "Setting up XRGB8888 mode\n");
+		ctrl1 |= CRT_CTRL_COLOR_XRGB8888;
+		*bpp = 32;
+		break;
+	default:
+		dev_err(drm->dev, "Unhandled pixel format %08x\n", format);
+		return -EINVAL;
+	}
+
+	writel(ctrl1, priv->base + CRT_CTRL1);
+
+	return 0;
+}
+
+static void aspeed_gfx_enable_controller(struct aspeed_gfx *priv)
+{
+	u32 ctrl1 = readl(priv->base + CRT_CTRL1);
+	u32 ctrl2 = readl(priv->base + CRT_CTRL2);
+
+	/* SCU2C: set DAC source for display output to Graphics CRT (GFX) */
+	regmap_update_bits(priv->scu, 0x2c, BIT(16), BIT(16));
+
+	writel(ctrl1 | CRT_CTRL_EN, priv->base + CRT_CTRL1);
+	writel(ctrl2 | CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2);
+}
+
+static void aspeed_gfx_disable_controller(struct aspeed_gfx *priv)
+{
+	u32 ctrl1 = readl(priv->base + CRT_CTRL1);
+	u32 ctrl2 = readl(priv->base + CRT_CTRL2);
+
+	writel(ctrl1 & ~CRT_CTRL_EN, priv->base + CRT_CTRL1);
+	writel(ctrl2 & ~CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2);
+
+	regmap_update_bits(priv->scu, 0x2c, BIT(16), 0);
+}
+
+static void aspeed_gfx_crtc_mode_set_nofb(struct aspeed_gfx *priv)
+{
+	struct drm_display_mode *m = &priv->pipe.crtc.state->adjusted_mode;
+	u32 ctrl1, d_offset, t_count, bpp;
+	int err;
+
+	err = aspeed_gfx_set_pixel_fmt(priv, &bpp);
+	if (err)
+		return;
+
+#if 0
+	/* TODO: we have only been able to test with the 40MHz USB clock. The
+	 * clock is fixed, so we cannot adjust it here. */
+	clk_set_rate(priv->pixel_clk, m->crtc_clock * 1000);
+#endif
+
+	ctrl1 = readl(priv->base + CRT_CTRL1);
+	ctrl1 &= ~(CRT_CTRL_INTERLACED |
+			CRT_CTRL_HSYNC_NEGATIVE |
+			CRT_CTRL_VSYNC_NEGATIVE);
+
+	if (m->flags & DRM_MODE_FLAG_INTERLACE)
+		ctrl1 |= CRT_CTRL_INTERLACED;
+
+	if (!(m->flags & DRM_MODE_FLAG_PHSYNC))
+		ctrl1 |= CRT_CTRL_HSYNC_NEGATIVE;
+
+	if (!(m->flags & DRM_MODE_FLAG_PVSYNC))
+		ctrl1 |= CRT_CTRL_VSYNC_NEGATIVE;
+
+	writel(ctrl1, priv->base + CRT_CTRL1);
+
+	/* Horizontal timing */
+	writel(CRT_H_TOTAL(m->htotal - 1) | CRT_H_DE(m->hdisplay - 1),
+			priv->base + CRT_HORIZ0);
+	writel(CRT_H_RS_START(m->hsync_start - 1) | CRT_H_RS_END(m->hsync_end),
+			priv->base + CRT_HORIZ1);
+
+
+	/* Vertical timing */
+	writel(CRT_V_TOTAL(m->vtotal - 1) | CRT_V_DE(m->vdisplay - 1),
+			priv->base + CRT_VERT0);
+	writel(CRT_V_RS_START(m->vsync_start) | CRT_V_RS_END(m->vsync_end),
+			priv->base + CRT_VERT1);
+
+	/*
+	 * Display Offset: address difference between consecutive scan lines
+	 * Terminal Count: memory size of one scan line
+	 */
+	d_offset = m->hdisplay * bpp / 8;
+	t_count = (m->hdisplay * bpp + 127) / 128;
+	writel(CRT_DISP_OFFSET(d_offset) | CRT_TERM_COUNT(t_count),
+			priv->base + CRT_OFFSET);
+
+	/*
+	 * Threshold: FIFO thresholds of refill and stop (16 byte chunks
+	 * per line, rounded up)
+	 */
+	writel(G5_CRT_THROD_VAL, priv->base + CRT_THROD);
+}
+
+static void aspeed_gfx_pipe_enable(struct drm_simple_display_pipe *pipe,
+			      struct drm_crtc_state *crtc_state,
+			      struct drm_plane_state *plane_state)
+{
+	struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
+	struct drm_crtc *crtc = &pipe->crtc;
+
+	aspeed_gfx_crtc_mode_set_nofb(priv);
+	aspeed_gfx_enable_controller(priv);
+	drm_crtc_vblank_on(crtc);
+}
+
+static void aspeed_gfx_pipe_disable(struct drm_simple_display_pipe *pipe)
+{
+	struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
+	struct drm_crtc *crtc = &pipe->crtc;
+
+	drm_crtc_vblank_off(crtc);
+	aspeed_gfx_disable_controller(priv);
+}
+
+static void aspeed_gfx_pipe_update(struct drm_simple_display_pipe *pipe,
+				   struct drm_plane_state *plane_state)
+{
+	struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
+	struct drm_crtc *crtc = &pipe->crtc;
+	struct drm_framebuffer *fb = pipe->plane.state->fb;
+	struct drm_pending_vblank_event *event;
+	struct drm_gem_cma_object *gem;
+
+	spin_lock_irq(&crtc->dev->event_lock);
+	event = crtc->state->event;
+	if (event) {
+		crtc->state->event = NULL;
+
+		if (drm_crtc_vblank_get(crtc) == 0)
+			drm_crtc_arm_vblank_event(crtc, event);
+		else
+			drm_crtc_send_vblank_event(crtc, event);
+	}
+	spin_unlock_irq(&crtc->dev->event_lock);
+
+	if (!fb)
+		return;
+
+	gem = drm_fb_cma_get_gem_obj(fb, 0);
+	if (!gem)
+		return;
+	writel(gem->paddr, priv->base + CRT_ADDR);
+}
+
+static int aspeed_gfx_enable_vblank(struct drm_simple_display_pipe *pipe)
+{
+	struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
+	u32 reg = readl(priv->base + CRT_CTRL1);
+
+	/* Clear pending VBLANK IRQ */
+	writel(reg | CRT_CTRL_VERTICAL_INTR_STS, priv->base + CRT_CTRL1);
+
+	reg |= CRT_CTRL_VERTICAL_INTR_EN;
+	writel(reg, priv->base + CRT_CTRL1);
+
+	return 0;
+}
+
+static void aspeed_gfx_disable_vblank(struct drm_simple_display_pipe *pipe)
+{
+	struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
+	u32 reg = readl(priv->base + CRT_CTRL1);
+
+	reg &= ~CRT_CTRL_VERTICAL_INTR_EN;
+	writel(reg, priv->base + CRT_CTRL1);
+
+	/* Clear pending VBLANK IRQ */
+	writel(reg | CRT_CTRL_VERTICAL_INTR_STS, priv->base + CRT_CTRL1);
+}
+
+static struct drm_simple_display_pipe_funcs aspeed_gfx_funcs = {
+	.enable		= aspeed_gfx_pipe_enable,
+	.disable	= aspeed_gfx_pipe_disable,
+	.update		= aspeed_gfx_pipe_update,
+	.prepare_fb	= drm_gem_fb_simple_display_pipe_prepare_fb,
+	.enable_vblank	= aspeed_gfx_enable_vblank,
+	.disable_vblank	= aspeed_gfx_disable_vblank,
+};
+
+static const uint32_t aspeed_gfx_formats[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_RGB565,
+};
+
+int aspeed_gfx_create_pipe(struct drm_device *drm)
+{
+	struct aspeed_gfx *priv = drm->dev_private;
+
+	return drm_simple_display_pipe_init(drm, &priv->pipe, &aspeed_gfx_funcs,
+					    aspeed_gfx_formats,
+					    ARRAY_SIZE(aspeed_gfx_formats),
+					    NULL,
+					    &priv->connector);
+}
diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c
new file mode 100644
index 0000000..713a397
--- /dev/null
+++ b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2018 IBM Corporation
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/irq.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_device.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_drv.h>
+
+#include "aspeed_gfx.h"
+
+/**
+ * DOC: ASPEED GFX Driver
+ *
+ * This driver is for the ASPEED BMC SoC's 'GFX' display hardware, also called
+ * the 'SOC Display Controller' in the datasheet. This driver runs on the ARM
+ * based BMC systems, unlike the ast driver which runs on a host CPU and is for
+ * a PCIe graphics device.
+ *
+ * The AST2500 supports a total of 3 output paths:
+ *
+ *   1. VGA output, the output target can choose either or both to the DAC
+ *   or DVO interface.
+ *
+ *   2. Graphics CRT output, the output target can choose either or both to
+ *   the DAC or DVO interface.
+ *
+ *   3. Video input from DVO, the video input can be used for video engine
+ *   capture or DAC display output.
+ *
+ * Output options are selected in SCU2C.
+ *
+ * The "VGA mode" device is the PCI attached controller. The "Graphics CRT"
+ * is the ARM's internal display controller.
+ *
+ * The driver only supports a simple configuration consisting of a 40MHz
+ * pixel clock, fixed by hardware limitations, and the VGA output path.
+ *
+ * The driver was written with the 'AST2500 Software Programming Guide' v17,
+ * which is available under NDA from ASPEED.
+ */
+
+static const struct drm_mode_config_funcs aspeed_gfx_mode_config_funcs = {
+	.fb_create		= drm_gem_fb_create,
+	.atomic_check		= drm_atomic_helper_check,
+	.atomic_commit		= drm_atomic_helper_commit,
+};
+
+static void aspeed_gfx_setup_mode_config(struct drm_device *drm)
+{
+	drm_mode_config_init(drm);
+
+	drm->mode_config.min_width = 0;
+	drm->mode_config.min_height = 0;
+	drm->mode_config.max_width = 800;
+	drm->mode_config.max_height = 600;
+	drm->mode_config.funcs = &aspeed_gfx_mode_config_funcs;
+}
+
+static irqreturn_t aspeed_gfx_irq_handler(int irq, void *data)
+{
+	struct drm_device *drm = data;
+	struct aspeed_gfx *priv = drm->dev_private;
+	u32 reg;
+
+	reg = readl(priv->base + CRT_CTRL1);
+
+	if (reg & CRT_CTRL_VERTICAL_INTR_STS) {
+		drm_crtc_handle_vblank(&priv->pipe.crtc);
+		writel(reg, priv->base + CRT_CTRL1);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+
+
+static int aspeed_gfx_load(struct drm_device *drm)
+{
+	struct platform_device *pdev = to_platform_device(drm->dev);
+	struct aspeed_gfx *priv;
+	struct resource *res;
+	int ret;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	drm->dev_private = priv;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->base = devm_ioremap_resource(drm->dev, res);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	priv->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2500-scu");
+	if (IS_ERR(priv->scu)) {
+		dev_err(&pdev->dev, "failed to find SCU regmap\n");
+		return PTR_ERR(priv->scu);
+	}
+
+	ret = of_reserved_mem_device_init(drm->dev);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"failed to initialize reserved mem: %d\n", ret);
+		return ret;
+	}
+
+	ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32));
+	if (ret) {
+		dev_err(&pdev->dev, "failed to set DMA mask: %d\n", ret);
+		return ret;
+	}
+
+	priv->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+	if (IS_ERR(priv->rst)) {
+		dev_err(&pdev->dev,
+			"missing or invalid reset controller device tree entry");
+		return PTR_ERR(priv->rst);
+	}
+	reset_control_deassert(priv->rst);
+
+	priv->clk = devm_clk_get(drm->dev, NULL);
+	if (IS_ERR(priv->clk)) {
+		dev_err(&pdev->dev,
+			"missing or invalid clk device tree entry");
+		return PTR_ERR(priv->clk);
+	}
+	clk_prepare_enable(priv->clk);
+
+	/* Sanitize control registers */
+	writel(0, priv->base + CRT_CTRL1);
+	writel(0, priv->base + CRT_CTRL2);
+
+	aspeed_gfx_setup_mode_config(drm);
+
+	ret = drm_vblank_init(drm, 1);
+	if (ret < 0) {
+		dev_err(drm->dev, "Failed to initialise vblank\n");
+		return ret;
+	}
+
+	ret = aspeed_gfx_create_output(drm);
+	if (ret < 0) {
+		dev_err(drm->dev, "Failed to create outputs\n");
+		return ret;
+	}
+
+	ret = aspeed_gfx_create_pipe(drm);
+	if (ret < 0) {
+		dev_err(drm->dev, "Cannot setup simple display pipe\n");
+		return ret;
+	}
+
+	ret = devm_request_irq(drm->dev, platform_get_irq(pdev, 0),
+			       aspeed_gfx_irq_handler, 0, "aspeed gfx", drm);
+	if (ret < 0) {
+		dev_err(drm->dev, "Failed to install IRQ handler\n");
+		return ret;
+	}
+
+	drm_mode_config_reset(drm);
+
+	drm_fbdev_generic_setup(drm, 32);
+
+	return 0;
+}
+
+static void aspeed_gfx_unload(struct drm_device *drm)
+{
+	drm_kms_helper_poll_fini(drm);
+	drm_mode_config_cleanup(drm);
+
+	drm->dev_private = NULL;
+}
+
+DEFINE_DRM_GEM_CMA_FOPS(fops);
+
+static struct drm_driver aspeed_gfx_driver = {
+	.driver_features        = DRIVER_GEM | DRIVER_MODESET |
+				DRIVER_PRIME | DRIVER_ATOMIC,
+	.gem_create_object	= drm_cma_gem_create_object_default_funcs,
+	.dumb_create		= drm_gem_cma_dumb_create,
+	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
+	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+	.gem_prime_mmap		= drm_gem_prime_mmap,
+	.fops = &fops,
+	.name = "aspeed-gfx-drm",
+	.desc = "ASPEED GFX DRM",
+	.date = "20180319",
+	.major = 1,
+	.minor = 0,
+};
+
+static const struct of_device_id aspeed_gfx_match[] = {
+	{ .compatible = "aspeed,ast2500-gfx" },
+	{ }
+};
+
+static int aspeed_gfx_probe(struct platform_device *pdev)
+{
+	struct drm_device *drm;
+	int ret;
+
+	drm = drm_dev_alloc(&aspeed_gfx_driver, &pdev->dev);
+	if (IS_ERR(drm))
+		return PTR_ERR(drm);
+
+	ret = aspeed_gfx_load(drm);
+	if (ret)
+		goto err_free;
+
+	ret = drm_dev_register(drm, 0);
+	if (ret)
+		goto err_unload;
+
+	return 0;
+
+err_unload:
+	aspeed_gfx_unload(drm);
+err_free:
+	drm_dev_put(drm);
+
+	return ret;
+}
+
+static int aspeed_gfx_remove(struct platform_device *pdev)
+{
+	struct drm_device *drm = platform_get_drvdata(pdev);
+
+	drm_dev_unregister(drm);
+	aspeed_gfx_unload(drm);
+	drm_dev_put(drm);
+
+	return 0;
+}
+
+static struct platform_driver aspeed_gfx_platform_driver = {
+	.probe		= aspeed_gfx_probe,
+	.remove		= aspeed_gfx_remove,
+	.driver = {
+		.name = "aspeed_gfx",
+		.of_match_table = aspeed_gfx_match,
+	},
+};
+
+module_platform_driver(aspeed_gfx_platform_driver);
+
+MODULE_AUTHOR("Joel Stanley <joel@jms.id.au>");
+MODULE_DESCRIPTION("ASPEED BMC DRM/KMS driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_out.c b/drivers/gpu/drm/aspeed/aspeed_gfx_out.c
new file mode 100644
index 0000000..2f31151
--- /dev/null
+++ b/drivers/gpu/drm/aspeed/aspeed_gfx_out.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2018 IBM Corporation
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "aspeed_gfx.h"
+
+static int aspeed_gfx_get_modes(struct drm_connector *connector)
+{
+	return drm_add_modes_noedid(connector, 800, 600);
+}
+
+static const struct
+drm_connector_helper_funcs aspeed_gfx_connector_helper_funcs = {
+	.get_modes = aspeed_gfx_get_modes,
+};
+
+static const struct drm_connector_funcs aspeed_gfx_connector_funcs = {
+	.fill_modes		= drm_helper_probe_single_connector_modes,
+	.destroy		= drm_connector_cleanup,
+	.reset			= drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state	= drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
+};
+
+int aspeed_gfx_create_output(struct drm_device *drm)
+{
+	struct aspeed_gfx *priv = drm->dev_private;
+	int ret;
+
+	priv->connector.dpms = DRM_MODE_DPMS_OFF;
+	priv->connector.polled = 0;
+	drm_connector_helper_add(&priv->connector,
+				 &aspeed_gfx_connector_helper_funcs);
+	ret = drm_connector_init(drm, &priv->connector,
+				 &aspeed_gfx_connector_funcs,
+				 DRM_MODE_CONNECTOR_Unknown);
+	return ret;
+}
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index d0f1dfe..18cd3b1 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1310,6 +1310,34 @@
 	  These devices are hard to detect and rarely found on mainstream
 	  hardware. If unsure, say N.
 
+config SENSORS_PECI_CPUTEMP
+	tristate "PECI CPU temperature monitoring support"
+	depends on PECI
+	select MFD_INTEL_PECI_CLIENT
+	help
+	  If you say yes here you get support for the generic Intel PECI
+	  cputemp driver which provides Digital Thermal Sensor (DTS) thermal
+	  readings of the CPU package and CPU cores that are accessible using
+	  the PECI Client Command Suite via the processor PECI client.
+	  Check Documentation/hwmon/peci-cputemp for details.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called peci-cputemp.
+
+config SENSORS_PECI_DIMMTEMP
+	tristate "PECI DIMM temperature monitoring support"
+	depends on PECI
+	select MFD_INTEL_PECI_CLIENT
+	help
+	  If you say yes here you get support for the generic Intel PECI hwmon
+	  driver which provides Digital Thermal Sensor (DTS) thermal readings of
+	  DIMM components that are accessible using the PECI Client Command
+	  Suite via the processor PECI client.
+	  Check Documentation/hwmon/peci-dimmtemp for details.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called peci-dimmtemp.
+
 source "drivers/hwmon/pmbus/Kconfig"
 
 config SENSORS_PWM_FAN
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index f5c7b44..c2c8bb5 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -141,6 +141,8 @@
 obj-$(CONFIG_SENSORS_PC87360)	+= pc87360.o
 obj-$(CONFIG_SENSORS_PC87427)	+= pc87427.o
 obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
+obj-$(CONFIG_SENSORS_PECI_CPUTEMP)	+= peci-cputemp.o
+obj-$(CONFIG_SENSORS_PECI_DIMMTEMP)	+= peci-dimmtemp.o
 obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
 obj-$(CONFIG_SENSORS_PWM_FAN)	+= pwm-fan.o
 obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)	+= raspberrypi-hwmon.o
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index c888f4a..e6d3fb5 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -138,6 +138,7 @@ static int occ_poll(struct occ *occ)
 	/* mutex should already be locked if necessary */
 	rc = occ->send_cmd(occ, cmd);
 	if (rc) {
+		occ->last_error = rc;
 		if (occ->error_count++ > OCC_ERROR_COUNT_THRESHOLD)
 			occ->error = rc;
 
@@ -146,6 +147,7 @@ static int occ_poll(struct occ *occ)
 
 	/* clear error since communication was successful */
 	occ->error_count = 0;
+	occ->last_error = 0;
 	occ->error = 0;
 
 	/* check for safe state */
@@ -207,6 +209,8 @@ int occ_update_response(struct occ *occ)
 	if (time_after(jiffies, occ->last_update + OCC_UPDATE_FREQUENCY)) {
 		rc = occ_poll(occ);
 		occ->last_update = jiffies;
+	} else {
+		rc = occ->last_error;
 	}
 
 	mutex_unlock(&occ->lock);
@@ -234,6 +238,12 @@ static ssize_t occ_show_temp_1(struct device *dev,
 		val = get_unaligned_be16(&temp->sensor_id);
 		break;
 	case 1:
+		/*
+		 * If a sensor reading has expired and couldn't be refreshed,
+		 * OCC returns 0xFFFF for that sensor.
+		 */
+		if (temp->value == 0xFFFF)
+			return -EREMOTEIO;
 		val = get_unaligned_be16(&temp->value) * 1000;
 		break;
 	default:
diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
index 7c44df3..c676e48 100644
--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -105,7 +105,8 @@ struct occ {
 	struct attribute_group group;
 	const struct attribute_group *groups[2];
 
-	int error;                      /* latest transfer error */
+	int error;                      /* final transfer error after retry */
+	int last_error;			/* latest transfer error */
 	unsigned int error_count;       /* number of xfr errors observed */
 	unsigned long last_safe;        /* time OCC entered "safe" state */
 
diff --git a/drivers/hwmon/occ/sysfs.c b/drivers/hwmon/occ/sysfs.c
index 743b26ec..1b599f4 100644
--- a/drivers/hwmon/occ/sysfs.c
+++ b/drivers/hwmon/occ/sysfs.c
@@ -51,16 +51,16 @@ static ssize_t occ_sysfs_show(struct device *dev,
 		val = !!(header->status & OCC_STAT_ACTIVE);
 		break;
 	case 2:
-		val = !!(header->status & OCC_EXT_STAT_DVFS_OT);
+		val = !!(header->ext_status & OCC_EXT_STAT_DVFS_OT);
 		break;
 	case 3:
-		val = !!(header->status & OCC_EXT_STAT_DVFS_POWER);
+		val = !!(header->ext_status & OCC_EXT_STAT_DVFS_POWER);
 		break;
 	case 4:
-		val = !!(header->status & OCC_EXT_STAT_MEM_THROTTLE);
+		val = !!(header->ext_status & OCC_EXT_STAT_MEM_THROTTLE);
 		break;
 	case 5:
-		val = !!(header->status & OCC_EXT_STAT_QUICK_DROP);
+		val = !!(header->ext_status & OCC_EXT_STAT_QUICK_DROP);
 		break;
 	case 6:
 		val = header->occ_state;
@@ -71,9 +71,6 @@ static ssize_t occ_sysfs_show(struct device *dev,
 		else
 			val = 1;
 		break;
-	case 8:
-		val = occ->error;
-		break;
 	default:
 		return -EINVAL;
 	}
@@ -81,6 +78,16 @@ static ssize_t occ_sysfs_show(struct device *dev,
 	return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);
 }
 
+static ssize_t occ_error_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	struct occ *occ = dev_get_drvdata(dev);
+
+	occ_update_response(occ);
+
+	return snprintf(buf, PAGE_SIZE - 1, "%d\n", occ->error);
+}
+
 static SENSOR_DEVICE_ATTR(occ_master, 0444, occ_sysfs_show, NULL, 0);
 static SENSOR_DEVICE_ATTR(occ_active, 0444, occ_sysfs_show, NULL, 1);
 static SENSOR_DEVICE_ATTR(occ_dvfs_overtemp, 0444, occ_sysfs_show, NULL, 2);
@@ -89,7 +96,7 @@ static SENSOR_DEVICE_ATTR(occ_mem_throttle, 0444, occ_sysfs_show, NULL, 4);
 static SENSOR_DEVICE_ATTR(occ_quick_pwr_drop, 0444, occ_sysfs_show, NULL, 5);
 static SENSOR_DEVICE_ATTR(occ_state, 0444, occ_sysfs_show, NULL, 6);
 static SENSOR_DEVICE_ATTR(occs_present, 0444, occ_sysfs_show, NULL, 7);
-static SENSOR_DEVICE_ATTR(occ_error, 0444, occ_sysfs_show, NULL, 8);
+static DEVICE_ATTR_RO(occ_error);
 
 static struct attribute *occ_attributes[] = {
 	&sensor_dev_attr_occ_master.dev_attr.attr,
@@ -100,7 +107,7 @@ static struct attribute *occ_attributes[] = {
 	&sensor_dev_attr_occ_quick_pwr_drop.dev_attr.attr,
 	&sensor_dev_attr_occ_state.dev_attr.attr,
 	&sensor_dev_attr_occs_present.dev_attr.attr,
-	&sensor_dev_attr_occ_error.dev_attr.attr,
+	&dev_attr_occ_error.attr,
 	NULL
 };
 
@@ -164,7 +171,7 @@ void occ_sysfs_poll_done(struct occ *occ)
 	}
 
 	if (occ->error && occ->error != occ->prev_error) {
-		name = sensor_dev_attr_occ_error.dev_attr.attr.name;
+		name = dev_attr_occ_error.attr.name;
 		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
 	}
 
diff --git a/drivers/hwmon/peci-cputemp.c b/drivers/hwmon/peci-cputemp.c
new file mode 100644
index 0000000..11880c8
--- /dev/null
+++ b/drivers/hwmon/peci-cputemp.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 Intel Corporation
+
+#include <linux/hwmon.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/intel-peci-client.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include "peci-hwmon.h"
+
+#define DEFAULT_CHANNEL_NUMS   4
+#define CORETEMP_CHANNEL_NUMS  CORE_NUMS_MAX
+#define CPUTEMP_CHANNEL_NUMS   (DEFAULT_CHANNEL_NUMS + CORETEMP_CHANNEL_NUMS)
+
+/* The RESOLVED_CORES register in PCU of a client CPU */
+#define REG_RESOLVED_CORES_BUS       1
+#define REG_RESOLVED_CORES_DEVICE    30
+#define REG_RESOLVED_CORES_FUNCTION  3
+#define REG_RESOLVED_CORES_OFFSET    0xB4
+
+struct temp_group {
+	struct temp_data die;
+	struct temp_data tcontrol;
+	struct temp_data tthrottle;
+	struct temp_data tjmax;
+	struct temp_data core[CORETEMP_CHANNEL_NUMS];
+};
+
+struct peci_cputemp {
+	struct peci_client_manager *mgr;
+	struct device *dev;
+	char name[PECI_NAME_SIZE];
+	const struct cpu_gen_info *gen_info;
+	struct temp_group temp;
+	u32 core_mask;
+	u32 temp_config[CPUTEMP_CHANNEL_NUMS + 1];
+	uint config_idx;
+	struct hwmon_channel_info temp_info;
+	const struct hwmon_channel_info *info[2];
+	struct hwmon_chip_info chip;
+};
+
+enum cputemp_channels {
+	channel_die,
+	channel_tcontrol,
+	channel_tthrottle,
+	channel_tjmax,
+	channel_core,
+};
+
+static const u32 config_table[DEFAULT_CHANNEL_NUMS + 1] = {
+	/* Die temperature */
+	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+	HWMON_T_CRIT_HYST,
+
+	/* Tcontrol temperature */
+	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT,
+
+	/* Tthrottle temperature */
+	HWMON_T_LABEL | HWMON_T_INPUT,
+
+	/* Tjmax temperature */
+	HWMON_T_LABEL | HWMON_T_INPUT,
+
+	/* Core temperature - for all core channels */
+	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+	HWMON_T_CRIT_HYST,
+};
+
+static const char *cputemp_label[CPUTEMP_CHANNEL_NUMS] = {
+	"Die",
+	"Tcontrol",
+	"Tthrottle",
+	"Tjmax",
+	"Core 0", "Core 1", "Core 2", "Core 3",
+	"Core 4", "Core 5", "Core 6", "Core 7",
+	"Core 8", "Core 9", "Core 10", "Core 11",
+	"Core 12", "Core 13", "Core 14", "Core 15",
+	"Core 16", "Core 17", "Core 18", "Core 19",
+	"Core 20", "Core 21", "Core 22", "Core 23",
+	"Core 24", "Core 25", "Core 26", "Core 27",
+};
+
+static s32 ten_dot_six_to_millidegree(s32 val)
+{
+	return ((val ^ 0x8000) - 0x8000) * 1000 / 64;
+}
+
+static int get_temp_targets(struct peci_cputemp *priv)
+{
+	s32 tthrottle_offset;
+	s32 tcontrol_margin;
+	u8  pkg_cfg[4];
+	int rc;
+
+	/**
+	 * Just use only the tcontrol marker to determine if target values need
+	 * update.
+	 */
+	if (!peci_temp_need_update(&priv->temp.tcontrol))
+		return 0;
+
+	rc = peci_client_read_package_config(priv->mgr,
+					     MBX_INDEX_TEMP_TARGET, 0, pkg_cfg);
+	if (rc)
+		return rc;
+
+	priv->temp.tjmax.value = pkg_cfg[2] * 1000;
+
+	tcontrol_margin = pkg_cfg[1];
+	tcontrol_margin = ((tcontrol_margin ^ 0x80) - 0x80) * 1000;
+	priv->temp.tcontrol.value = priv->temp.tjmax.value - tcontrol_margin;
+
+	tthrottle_offset = (pkg_cfg[3] & 0x2f) * 1000;
+	priv->temp.tthrottle.value = priv->temp.tjmax.value - tthrottle_offset;
+
+	peci_temp_mark_updated(&priv->temp.tcontrol);
+
+	return 0;
+}
+
+static int get_die_temp(struct peci_cputemp *priv)
+{
+	struct peci_get_temp_msg msg;
+	int rc;
+
+	if (!peci_temp_need_update(&priv->temp.die))
+		return 0;
+
+	msg.addr = priv->mgr->client->addr;
+
+	rc = peci_command(priv->mgr->client->adapter, PECI_CMD_GET_TEMP,
+			  &msg);
+	if (rc)
+		return rc;
+
+	/* Note that the tjmax should be available before calling it */
+	priv->temp.die.value = priv->temp.tjmax.value +
+			       (msg.temp_raw * 1000 / 64);
+
+	peci_temp_mark_updated(&priv->temp.die);
+
+	return 0;
+}
+
+static int get_core_temp(struct peci_cputemp *priv, int core_index)
+{
+	s32 core_dts_margin;
+	u8  pkg_cfg[4];
+	int rc;
+
+	if (!peci_temp_need_update(&priv->temp.core[core_index]))
+		return 0;
+
+	rc = peci_client_read_package_config(priv->mgr,
+					     MBX_INDEX_PER_CORE_DTS_TEMP,
+					     core_index, pkg_cfg);
+	if (rc)
+		return rc;
+
+	core_dts_margin = le16_to_cpup((__le16 *)pkg_cfg);
+
+	/**
+	 * Processors return a value of the core DTS reading in 10.6 format
+	 * (10 bits signed decimal, 6 bits fractional).
+	 * Error codes:
+	 *   0x8000: General sensor error
+	 *   0x8001: Reserved
+	 *   0x8002: Underflow on reading value
+	 *   0x8003-0x81ff: Reserved
+	 */
+	if (core_dts_margin >= 0x8000 && core_dts_margin <= 0x81ff)
+		return -EIO;
+
+	core_dts_margin = ten_dot_six_to_millidegree(core_dts_margin);
+
+	/* Note that the tjmax should be available before calling it */
+	priv->temp.core[core_index].value = priv->temp.tjmax.value +
+					    core_dts_margin;
+
+	peci_temp_mark_updated(&priv->temp.core[core_index]);
+
+	return 0;
+}
+
+static int cputemp_read_string(struct device *dev,
+			       enum hwmon_sensor_types type,
+			       u32 attr, int channel, const char **str)
+{
+	if (attr != hwmon_temp_label)
+		return -EOPNOTSUPP;
+
+	*str = cputemp_label[channel];
+	return 0;
+}
+
+static int cputemp_read(struct device *dev,
+			enum hwmon_sensor_types type,
+			u32 attr, int channel, long *val)
+{
+	struct peci_cputemp *priv = dev_get_drvdata(dev);
+	int rc, core_index;
+
+	if (channel >= CPUTEMP_CHANNEL_NUMS ||
+	    !(priv->temp_config[channel] & BIT(attr)))
+		return -EOPNOTSUPP;
+
+	rc = get_temp_targets(priv);
+	if (rc)
+		return rc;
+
+	switch (attr) {
+	case hwmon_temp_input:
+		switch (channel) {
+		case channel_die:
+			rc = get_die_temp(priv);
+			if (rc)
+				break;
+
+			*val = priv->temp.die.value;
+			break;
+		case channel_tcontrol:
+			*val = priv->temp.tcontrol.value;
+			break;
+		case channel_tthrottle:
+			*val = priv->temp.tthrottle.value;
+			break;
+		case channel_tjmax:
+			*val = priv->temp.tjmax.value;
+			break;
+		default:
+			core_index = channel - DEFAULT_CHANNEL_NUMS;
+			rc = get_core_temp(priv, core_index);
+			if (rc)
+				break;
+
+			*val = priv->temp.core[core_index].value;
+			break;
+		}
+		break;
+	case hwmon_temp_max:
+		*val = priv->temp.tcontrol.value;
+		break;
+	case hwmon_temp_crit:
+		*val = priv->temp.tjmax.value;
+		break;
+	case hwmon_temp_crit_hyst:
+		*val = priv->temp.tjmax.value - priv->temp.tcontrol.value;
+		break;
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
+}
+
+static umode_t cputemp_is_visible(const void *data,
+				  enum hwmon_sensor_types type,
+				  u32 attr, int channel)
+{
+	const struct peci_cputemp *priv = data;
+
+	if (priv->temp_config[channel] & BIT(attr))
+		if (channel < DEFAULT_CHANNEL_NUMS ||
+		    (channel >= DEFAULT_CHANNEL_NUMS &&
+		     (priv->core_mask & BIT(channel - DEFAULT_CHANNEL_NUMS))))
+			return 0444;
+
+	return 0;
+}
+
+static const struct hwmon_ops cputemp_ops = {
+	.is_visible = cputemp_is_visible,
+	.read_string = cputemp_read_string,
+	.read = cputemp_read,
+};
+
+static int check_resolved_cores(struct peci_cputemp *priv)
+{
+	struct peci_rd_pci_cfg_local_msg msg;
+	int rc;
+
+	/* Get the RESOLVED_CORES register value */
+	msg.addr = priv->mgr->client->addr;
+	msg.bus = REG_RESOLVED_CORES_BUS;
+	msg.device = REG_RESOLVED_CORES_DEVICE;
+	msg.function = REG_RESOLVED_CORES_FUNCTION;
+	msg.reg = REG_RESOLVED_CORES_OFFSET;
+	msg.rx_len = 4;
+
+	rc = peci_command(priv->mgr->client->adapter,
+			  PECI_CMD_RD_PCI_CFG_LOCAL, &msg);
+	if (rc)
+		return rc;
+
+	priv->core_mask = le32_to_cpup((__le32 *)msg.pci_config);
+	if (!priv->core_mask)
+		return -EAGAIN;
+
+	dev_dbg(priv->dev, "Scanned resolved cores: 0x%x\n", priv->core_mask);
+	return 0;
+}
+
+static int create_core_temp_info(struct peci_cputemp *priv)
+{
+	int rc, i;
+
+	rc = check_resolved_cores(priv);
+	if (rc)
+		return rc;
+
+	for (i = 0; i < priv->gen_info->core_max; i++)
+		if (priv->core_mask & BIT(i))
+			while (i + DEFAULT_CHANNEL_NUMS >= priv->config_idx)
+				priv->temp_config[priv->config_idx++] =
+					config_table[channel_core];
+
+	return 0;
+}
+
+static int peci_cputemp_probe(struct platform_device *pdev)
+{
+	struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent);
+	struct device *dev = &pdev->dev;
+	struct peci_cputemp *priv;
+	struct device *hwmon_dev;
+	int rc;
+
+	if ((mgr->client->adapter->cmd_mask &
+	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
+	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG)))
+		return -ENODEV;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, priv);
+	priv->mgr = mgr;
+	priv->dev = dev;
+	priv->gen_info = mgr->gen_info;
+
+	snprintf(priv->name, PECI_NAME_SIZE, "peci_cputemp.cpu%d",
+		 mgr->client->addr - PECI_BASE_ADDR);
+
+	priv->temp_config[priv->config_idx++] = config_table[channel_die];
+	priv->temp_config[priv->config_idx++] = config_table[channel_tcontrol];
+	priv->temp_config[priv->config_idx++] = config_table[channel_tthrottle];
+	priv->temp_config[priv->config_idx++] = config_table[channel_tjmax];
+
+	rc = create_core_temp_info(priv);
+	if (rc)
+		dev_dbg(dev, "Skipped creating core temp info\n");
+
+	priv->chip.ops = &cputemp_ops;
+	priv->chip.info = priv->info;
+
+	priv->info[0] = &priv->temp_info;
+
+	priv->temp_info.type = hwmon_temp;
+	priv->temp_info.config = priv->temp_config;
+
+	hwmon_dev = devm_hwmon_device_register_with_info(priv->dev,
+							 priv->name,
+							 priv,
+							 &priv->chip,
+							 NULL);
+
+	if (IS_ERR(hwmon_dev))
+		return PTR_ERR(hwmon_dev);
+
+	dev_dbg(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), priv->name);
+
+	return 0;
+}
+
+static const struct platform_device_id peci_cputemp_ids[] = {
+	{ .name = "peci-cputemp", .driver_data = 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(platform, peci_cputemp_ids);
+
+static struct platform_driver peci_cputemp_driver = {
+	.probe    = peci_cputemp_probe,
+	.id_table = peci_cputemp_ids,
+	.driver   = { .name = "peci-cputemp", },
+};
+module_platform_driver(peci_cputemp_driver);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI cputemp driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/peci-dimmtemp.c b/drivers/hwmon/peci-dimmtemp.c
new file mode 100644
index 0000000..86a45a9
--- /dev/null
+++ b/drivers/hwmon/peci-dimmtemp.c
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 Intel Corporation
+
+#include <linux/hwmon.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/intel-peci-client.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include "peci-hwmon.h"
+
+#define DIMM_MASK_CHECK_DELAY_JIFFIES  msecs_to_jiffies(5000)
+#define DIMM_MASK_CHECK_RETRY_MAX      60 /* 60 x 5 secs = 5 minutes */
+
+struct peci_dimmtemp {
+	struct peci_client_manager *mgr;
+	struct device *dev;
+	char name[PECI_NAME_SIZE];
+	const struct cpu_gen_info *gen_info;
+	struct workqueue_struct *work_queue;
+	struct delayed_work work_handler;
+	struct temp_data temp[DIMM_NUMS_MAX];
+	u32 dimm_mask;
+	int retry_count;
+	u32 temp_config[DIMM_NUMS_MAX + 1];
+	struct hwmon_channel_info temp_info;
+	const struct hwmon_channel_info *info[2];
+	struct hwmon_chip_info chip;
+};
+
+static const char *dimmtemp_label[CHAN_RANK_MAX][DIMM_IDX_MAX] = {
+	{ "DIMM A1", "DIMM A2", "DIMM A3" },
+	{ "DIMM B1", "DIMM B2", "DIMM B3" },
+	{ "DIMM C1", "DIMM C2", "DIMM C3" },
+	{ "DIMM D1", "DIMM D2", "DIMM D3" },
+	{ "DIMM E1", "DIMM E2", "DIMM E3" },
+	{ "DIMM F1", "DIMM F2", "DIMM F3" },
+	{ "DIMM G1", "DIMM G2", "DIMM G3" },
+	{ "DIMM H1", "DIMM H2", "DIMM H3" },
+};
+
+static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no)
+{
+	int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
+	int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
+	u8  cfg_data[4];
+	int rc;
+
+	if (!peci_temp_need_update(&priv->temp[dimm_no]))
+		return 0;
+
+	rc = peci_client_read_package_config(priv->mgr,
+					     MBX_INDEX_DDR_DIMM_TEMP,
+					     chan_rank, cfg_data);
+	if (rc)
+		return rc;
+
+	priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
+
+	peci_temp_mark_updated(&priv->temp[dimm_no]);
+
+	return 0;
+}
+
+static int dimmtemp_read_string(struct device *dev,
+				enum hwmon_sensor_types type,
+				u32 attr, int channel, const char **str)
+{
+	struct peci_dimmtemp *priv = dev_get_drvdata(dev);
+	u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
+	int chan_rank, dimm_idx;
+
+	if (attr != hwmon_temp_label)
+		return -EOPNOTSUPP;
+
+	chan_rank = channel / dimm_idx_max;
+	dimm_idx = channel % dimm_idx_max;
+	*str = dimmtemp_label[chan_rank][dimm_idx];
+	return 0;
+}
+
+static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types type,
+			 u32 attr, int channel, long *val)
+{
+	struct peci_dimmtemp *priv = dev_get_drvdata(dev);
+	int rc;
+
+	if (attr != hwmon_temp_input)
+		return -EOPNOTSUPP;
+
+	rc = get_dimm_temp(priv, channel);
+	if (rc)
+		return rc;
+
+	*val = priv->temp[channel].value;
+	return 0;
+}
+
+static umode_t dimmtemp_is_visible(const void *data,
+				   enum hwmon_sensor_types type,
+				   u32 attr, int channel)
+{
+	const struct peci_dimmtemp *priv = data;
+
+	if (priv->temp_config[channel] & BIT(attr) &&
+	    priv->dimm_mask & BIT(channel))
+		return 0444;
+
+	return 0;
+}
+
+static const struct hwmon_ops dimmtemp_ops = {
+	.is_visible = dimmtemp_is_visible,
+	.read_string = dimmtemp_read_string,
+	.read = dimmtemp_read,
+};
+
+static int check_populated_dimms(struct peci_dimmtemp *priv)
+{
+	u32 chan_rank_max = priv->gen_info->chan_rank_max;
+	u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
+	int chan_rank, dimm_idx, rc;
+	u8  cfg_data[4];
+
+	for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) {
+		rc = peci_client_read_package_config(priv->mgr,
+						     MBX_INDEX_DDR_DIMM_TEMP,
+						     chan_rank, cfg_data);
+		if (rc) {
+			priv->dimm_mask = 0;
+			return rc;
+		}
+
+		for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++)
+			if (cfg_data[dimm_idx])
+				priv->dimm_mask |= BIT(chan_rank *
+						       dimm_idx_max +
+						       dimm_idx);
+	}
+
+	if (!priv->dimm_mask)
+		return -EAGAIN;
+
+	dev_dbg(priv->dev, "Scanned populated DIMMs: 0x%x\n", priv->dimm_mask);
+	return 0;
+}
+
+static int create_dimm_temp_info(struct peci_dimmtemp *priv)
+{
+	int rc, i, config_idx, channels;
+	struct device *hwmon_dev;
+
+	rc = check_populated_dimms(priv);
+	if (rc) {
+		if (rc == -EAGAIN) {
+			if (priv->retry_count < DIMM_MASK_CHECK_RETRY_MAX) {
+				queue_delayed_work(priv->work_queue,
+						   &priv->work_handler,
+						 DIMM_MASK_CHECK_DELAY_JIFFIES);
+				priv->retry_count++;
+				dev_dbg(priv->dev,
+					"Deferred DIMM temp info creation\n");
+			} else {
+				dev_err(priv->dev,
+					"Timeout DIMM temp info creation\n");
+				rc = -ETIMEDOUT;
+			}
+		}
+
+		return rc;
+	}
+
+	channels = priv->gen_info->chan_rank_max *
+		   priv->gen_info->dimm_idx_max;
+	for (i = 0, config_idx = 0; i < channels; i++)
+		if (priv->dimm_mask & BIT(i))
+			while (i >= config_idx)
+				priv->temp_config[config_idx++] =
+					HWMON_T_LABEL | HWMON_T_INPUT;
+
+	priv->chip.ops = &dimmtemp_ops;
+	priv->chip.info = priv->info;
+
+	priv->info[0] = &priv->temp_info;
+
+	priv->temp_info.type = hwmon_temp;
+	priv->temp_info.config = priv->temp_config;
+
+	hwmon_dev = devm_hwmon_device_register_with_info(priv->dev,
+							 priv->name,
+							 priv,
+							 &priv->chip,
+							 NULL);
+	rc = PTR_ERR_OR_ZERO(hwmon_dev);
+	if (!rc)
+		dev_dbg(priv->dev, "%s: sensor '%s'\n",
+			dev_name(hwmon_dev), priv->name);
+
+	return rc;
+}
+
+static void create_dimm_temp_info_delayed(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct peci_dimmtemp *priv = container_of(dwork, struct peci_dimmtemp,
+						  work_handler);
+	int rc;
+
+	rc = create_dimm_temp_info(priv);
+	if (rc && rc != -EAGAIN)
+		dev_dbg(priv->dev, "Failed to create DIMM temp info\n");
+}
+
+static int peci_dimmtemp_probe(struct platform_device *pdev)
+{
+	struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent);
+	struct device *dev = &pdev->dev;
+	struct peci_dimmtemp *priv;
+	int rc;
+
+	if ((mgr->client->adapter->cmd_mask &
+	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
+	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG)))
+		return -ENODEV;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, priv);
+	priv->mgr = mgr;
+	priv->dev = dev;
+	priv->gen_info = mgr->gen_info;
+
+	snprintf(priv->name, PECI_NAME_SIZE, "peci_dimmtemp.cpu%d",
+		 priv->mgr->client->addr - PECI_BASE_ADDR);
+
+	priv->work_queue = alloc_ordered_workqueue(priv->name, 0);
+	if (!priv->work_queue)
+		return -ENOMEM;
+
+	INIT_DELAYED_WORK(&priv->work_handler, create_dimm_temp_info_delayed);
+
+	rc = create_dimm_temp_info(priv);
+	if (rc && rc != -EAGAIN) {
+		dev_err(dev, "Failed to create DIMM temp info\n");
+		goto err_free_wq;
+	}
+
+	return 0;
+
+err_free_wq:
+	destroy_workqueue(priv->work_queue);
+	return rc;
+}
+
+static int peci_dimmtemp_remove(struct platform_device *pdev)
+{
+	struct peci_dimmtemp *priv = dev_get_drvdata(&pdev->dev);
+
+	cancel_delayed_work_sync(&priv->work_handler);
+	destroy_workqueue(priv->work_queue);
+
+	return 0;
+}
+
+static const struct platform_device_id peci_dimmtemp_ids[] = {
+	{ .name = "peci-dimmtemp", .driver_data = 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(platform, peci_dimmtemp_ids);
+
+static struct platform_driver peci_dimmtemp_driver = {
+	.probe    = peci_dimmtemp_probe,
+	.remove   = peci_dimmtemp_remove,
+	.id_table = peci_dimmtemp_ids,
+	.driver   = { .name = "peci-dimmtemp", },
+};
+module_platform_driver(peci_dimmtemp_driver);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI dimmtemp driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/peci-hwmon.h b/drivers/hwmon/peci-hwmon.h
new file mode 100644
index 0000000..6ca1855
--- /dev/null
+++ b/drivers/hwmon/peci-hwmon.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018 Intel Corporation */
+
+#ifndef __PECI_HWMON_H
+#define __PECI_HWMON_H
+
+#include <linux/peci.h>
+
+#define TEMP_TYPE_PECI   6 /* Sensor type 6: Intel PECI */
+#define UPDATE_INTERVAL  HZ
+
+/**
+ * struct temp_data - PECI temperature information
+ * @valid: flag to indicate the temperature value is valid
+ * @value: temperature value in millidegree Celsius
+ * @last_updated: time of the last update in jiffies
+ */
+struct temp_data {
+	uint  valid;
+	s32   value;
+	ulong last_updated;
+};
+
+/**
+ * peci_temp_need_update - check whether temperature update is needed or not
+ * @temp: pointer to temperature data struct
+ *
+ * Return: true if update is needed, false if not.
+ */
+static inline bool peci_temp_need_update(struct temp_data *temp)
+{
+	if (temp->valid &&
+	    time_before(jiffies, temp->last_updated + UPDATE_INTERVAL))
+		return false;
+
+	return true;
+}
+
+/**
+ * peci_temp_mark_updated - mark the temperature is updated
+ * @temp: pointer to temperature data struct
+ */
+static inline void peci_temp_mark_updated(struct temp_data *temp)
+{
+	temp->valid = 1;
+	temp->last_updated = jiffies;
+}
+
+#endif /* __PECI_HWMON_H */
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 629cb45..965efe1 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -54,6 +54,24 @@
 	  This driver can also be built as a module. If so, the module will
 	  be called ir35521.
 
+config SENSORS_ISL68137
+	tristate "Intersil ISL68137"
+	help
+	  If you say yes here you get hardware monitoring support for Intersil
+	  ISL68137.
+
+	  This driver can also be built as a module. If so, the module will
+	  be called isl68137.
+
+config SENSORS_IR38064
+	tristate "Infineon IR38064"
+	help
+	  If you say yes here you get hardware monitoring support for Infineon
+	  IR38064.
+
+	  This driver can also be built as a module. If so, the module will
+	  be called ir38064.
+
 config SENSORS_LM25066
 	tristate "National Semiconductor LM25066 and compatibles"
 	help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index ea0e395..8ee236b 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -8,6 +8,8 @@
 obj-$(CONFIG_SENSORS_ADM1275)	+= adm1275.o
 obj-$(CONFIG_SENSORS_IBM_CFFPS)	+= ibm-cffps.o
 obj-$(CONFIG_SENSORS_IR35221)	+= ir35221.o
+obj-$(CONFIG_SENSORS_ISL68137)	+= isl68137.o
+obj-$(CONFIG_SENSORS_IR38064)	+= ir38064.o
 obj-$(CONFIG_SENSORS_LM25066)	+= lm25066.o
 obj-$(CONFIG_SENSORS_LTC2978)	+= ltc2978.o
 obj-$(CONFIG_SENSORS_LTC3815)	+= ltc3815.o
diff --git a/drivers/hwmon/pmbus/ir38064.c b/drivers/hwmon/pmbus/ir38064.c
new file mode 100644
index 0000000..4b957eb
--- /dev/null
+++ b/drivers/hwmon/pmbus/ir38064.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Hardware monitoring driver for Infineon IR38064
+ *
+ * Copyright (c) 2017 Google Inc
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "pmbus.h"
+
+static struct pmbus_driver_info ir38064_info = {
+	.pages = 1,
+	.format[PSC_VOLTAGE_IN] = linear,
+	.format[PSC_VOLTAGE_OUT] = direct,
+	.format[PSC_CURRENT_OUT] = linear,
+	.format[PSC_POWER] = linear,
+	.format[PSC_TEMPERATURE] = linear,
+	.m[PSC_VOLTAGE_OUT] = 256,
+	.b[PSC_VOLTAGE_OUT] = 0,
+	.R[PSC_VOLTAGE_OUT] = 0,
+	.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
+	    | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP
+	    | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+	    | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
+	    | PMBUS_HAVE_POUT,
+};
+
+static int ir38064_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	return pmbus_do_probe(client, id, &ir38064_info);
+}
+
+static const struct i2c_device_id ir38064_id[] = {
+	{"ir38064", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, ir38064_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ir38064_driver = {
+	.driver = {
+		   .name = "ir38064",
+		   },
+	.probe = ir38064_probe,
+	.remove = pmbus_do_remove,
+	.id_table = ir38064_id,
+};
+
+module_i2c_driver(ir38064_driver);
+
+MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>");
+MODULE_DESCRIPTION("PMBus driver for Infineon IR38064");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/pmbus/isl68137.c b/drivers/hwmon/pmbus/isl68137.c
new file mode 100644
index 0000000..515596c
--- /dev/null
+++ b/drivers/hwmon/pmbus/isl68137.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Hardware monitoring driver for Intersil ISL68137
+ *
+ * Copyright (c) 2017 Google Inc
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include "pmbus.h"
+
+#define ISL68137_VOUT_AVS	0x30
+
+static ssize_t isl68137_avs_enable_show_page(struct i2c_client *client,
+					     int page,
+					     char *buf)
+{
+	int val = pmbus_read_byte_data(client, page, PMBUS_OPERATION);
+
+	return sprintf(buf, "%d\n",
+		       (val & ISL68137_VOUT_AVS) == ISL68137_VOUT_AVS ? 1 : 0);
+}
+
+static ssize_t isl68137_avs_enable_store_page(struct i2c_client *client,
+					      int page,
+					      const char *buf, size_t count)
+{
+	int rc, op_val;
+	bool result;
+
+	rc = kstrtobool(buf, &result);
+	if (rc)
+		return rc;
+
+	op_val = result ? ISL68137_VOUT_AVS : 0;
+
+	/*
+	 * Writes to VOUT setpoint over AVSBus will persist after the VRM is
+	 * switched to PMBus control. Switching back to AVSBus control
+	 * restores this persisted setpoint rather than re-initializing to
+	 * PMBus VOUT_COMMAND. Writing VOUT_COMMAND first over PMBus before
+	 * enabling AVS control is the workaround.
+	 */
+	if (op_val == ISL68137_VOUT_AVS) {
+		rc = pmbus_read_word_data(client, page, PMBUS_VOUT_COMMAND);
+		if (rc < 0)
+			return rc;
+
+		rc = pmbus_write_word_data(client, page, PMBUS_VOUT_COMMAND,
+					   rc);
+		if (rc < 0)
+			return rc;
+	}
+
+	rc = pmbus_update_byte_data(client, page, PMBUS_OPERATION,
+				    ISL68137_VOUT_AVS, op_val);
+
+	return (rc < 0) ? rc : count;
+}
+
+static ssize_t isl68137_avs_enable_show(struct device *dev,
+					struct device_attribute *devattr,
+					char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev->parent);
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+	return isl68137_avs_enable_show_page(client, attr->index, buf);
+}
+
+static ssize_t isl68137_avs_enable_store(struct device *dev,
+				struct device_attribute *devattr,
+				const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev->parent);
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+	return isl68137_avs_enable_store_page(client, attr->index, buf, count);
+}
+
+static SENSOR_DEVICE_ATTR_RW(avs0_enable, isl68137_avs_enable, 0);
+static SENSOR_DEVICE_ATTR_RW(avs1_enable, isl68137_avs_enable, 1);
+
+static struct attribute *enable_attrs[] = {
+	&sensor_dev_attr_avs0_enable.dev_attr.attr,
+	&sensor_dev_attr_avs1_enable.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group enable_group = {
+	.attrs = enable_attrs,
+};
+
+static const struct attribute_group *attribute_groups[] = {
+	&enable_group,
+	NULL,
+};
+
+static struct pmbus_driver_info isl68137_info = {
+	.pages = 2,
+	.format[PSC_VOLTAGE_IN] = direct,
+	.format[PSC_VOLTAGE_OUT] = direct,
+	.format[PSC_CURRENT_IN] = direct,
+	.format[PSC_CURRENT_OUT] = direct,
+	.format[PSC_POWER] = direct,
+	.format[PSC_TEMPERATURE] = direct,
+	.m[PSC_VOLTAGE_IN] = 1,
+	.b[PSC_VOLTAGE_IN] = 0,
+	.R[PSC_VOLTAGE_IN] = 3,
+	.m[PSC_VOLTAGE_OUT] = 1,
+	.b[PSC_VOLTAGE_OUT] = 0,
+	.R[PSC_VOLTAGE_OUT] = 3,
+	.m[PSC_CURRENT_IN] = 1,
+	.b[PSC_CURRENT_IN] = 0,
+	.R[PSC_CURRENT_IN] = 2,
+	.m[PSC_CURRENT_OUT] = 1,
+	.b[PSC_CURRENT_OUT] = 0,
+	.R[PSC_CURRENT_OUT] = 1,
+	.m[PSC_POWER] = 1,
+	.b[PSC_POWER] = 0,
+	.R[PSC_POWER] = 0,
+	.m[PSC_TEMPERATURE] = 1,
+	.b[PSC_TEMPERATURE] = 0,
+	.R[PSC_TEMPERATURE] = 0,
+	.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN
+	    | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2
+	    | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP
+	    | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+	    | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT,
+	.func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+	    | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT,
+	.groups = attribute_groups,
+};
+
+static int isl68137_probe(struct i2c_client *client,
+			  const struct i2c_device_id *id)
+{
+	return pmbus_do_probe(client, id, &isl68137_info);
+}
+
+static const struct i2c_device_id isl68137_id[] = {
+	{"isl68137", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, isl68137_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver isl68137_driver = {
+	.driver = {
+		   .name = "isl68137",
+		   },
+	.probe = isl68137_probe,
+	.remove = pmbus_do_remove,
+	.id_table = isl68137_id,
+};
+
+module_i2c_driver(isl68137_driver);
+
+MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>");
+MODULE_DESCRIPTION("PMBus driver for Intersil ISL68137");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c
index c9dc879..393641d0 100644
--- a/drivers/hwmon/pmbus/max31785.c
+++ b/drivers/hwmon/pmbus/max31785.c
@@ -16,40 +16,126 @@
 
 enum max31785_regs {
 	MFR_REVISION		= 0x9b,
+	MFR_FAULT_RESPONSE	= 0xd9,
+	MFR_TEMP_SENSOR_CONFIG	= 0xf0,
 	MFR_FAN_CONFIG		= 0xf1,
+	MFR_FAN_FAULT_LIMIT	= 0xf5,
 };
 
 #define MAX31785			0x3030
 #define MAX31785A			0x3040
 
 #define MFR_FAN_CONFIG_DUAL_TACH	BIT(12)
+#define MFR_FAN_CONFIG_TSFO		BIT(9)
+#define MFR_FAN_CONFIG_TACHO		BIT(8)
+#define MFR_FAN_CONFIG_HEALTH		BIT(4)
+#define MFR_FAN_CONFIG_ROTOR_HI_LO	BIT(3)
+#define MFR_FAN_CONFIG_ROTOR		BIT(2)
+
+#define MFR_FAULT_RESPONSE_MONITOR	BIT(0)
 
 #define MAX31785_NR_PAGES		23
 #define MAX31785_NR_FAN_PAGES		6
 
+/*
+ * MAX31785 dragons ahead
+ *
+ * We see weird issues where some transfers fail. There doesn't appear to be
+ * any pattern to the problem, so below we wrap all the read/write calls with a
+ * retry. The device provides no indication of this besides NACK'ing master
+ * Txs; no bits are set in STATUS_BYTE to suggest anything has gone wrong.
+ */
+
+#define max31785_retry(_func, ...) ({					\
+	/* All relevant functions return int, sue me */			\
+	int _ret = _func(__VA_ARGS__);					\
+	if (_ret == -EIO)						\
+		_ret = _func(__VA_ARGS__);				\
+	_ret;								\
+})
+
+static int max31785_i2c_smbus_read_byte_data(struct i2c_client *client,
+					      int command)
+{
+	return max31785_retry(i2c_smbus_read_byte_data, client, command);
+}
+
+
+static int max31785_i2c_smbus_write_byte_data(struct i2c_client *client,
+					      int command, u16 data)
+{
+	return max31785_retry(i2c_smbus_write_byte_data, client, command, data);
+}
+
+static int max31785_i2c_smbus_read_word_data(struct i2c_client *client,
+					     int command)
+{
+	return max31785_retry(i2c_smbus_read_word_data, client, command);
+}
+
+static int max31785_i2c_smbus_write_word_data(struct i2c_client *client,
+					      int command, u16 data)
+{
+	return max31785_retry(i2c_smbus_write_word_data, client, command, data);
+}
+
+static int max31785_pmbus_write_byte(struct i2c_client *client, int page,
+				     u8 value)
+{
+	return max31785_retry(pmbus_write_byte, client, page, value);
+}
+
+static int max31785_pmbus_read_byte_data(struct i2c_client *client, int page,
+					  int command)
+{
+	return max31785_retry(pmbus_read_byte_data, client, page, command);
+}
+
+static int max31785_pmbus_write_byte_data(struct i2c_client *client, int page,
+					  int command, u16 data)
+{
+	return max31785_retry(pmbus_write_byte_data, client, page, command,
+			      data);
+}
+
+static int max31785_pmbus_read_word_data(struct i2c_client *client, int page,
+					  int command)
+{
+	return max31785_retry(pmbus_read_word_data, client, page, command);
+}
+
+static int max31785_pmbus_write_word_data(struct i2c_client *client, int page,
+					  int command, u16 data)
+{
+	return max31785_retry(pmbus_write_word_data, client, page, command,
+			      data);
+}
+
 static int max31785_read_byte_data(struct i2c_client *client, int page,
 				   int reg)
 {
-	if (page < MAX31785_NR_PAGES)
-		return -ENODATA;
-
 	switch (reg) {
 	case PMBUS_VOUT_MODE:
-		return -ENOTSUPP;
+		if (page >= MAX31785_NR_PAGES)
+			return -ENOTSUPP;
+		break;
 	case PMBUS_FAN_CONFIG_12:
-		return pmbus_read_byte_data(client, page - MAX31785_NR_PAGES,
-					    reg);
+		if (page >= MAX31785_NR_PAGES)
+			return max31785_pmbus_read_byte_data(client,
+					page - MAX31785_NR_PAGES,
+					reg);
+		break;
 	}
 
-	return -ENODATA;
+	return max31785_pmbus_read_byte_data(client, page, reg);
 }
 
 static int max31785_write_byte(struct i2c_client *client, int page, u8 value)
 {
-	if (page < MAX31785_NR_PAGES)
-		return -ENODATA;
+	if (page >= MAX31785_NR_PAGES)
+		return -ENOTSUPP;
 
-	return -ENOTSUPP;
+	return max31785_pmbus_write_byte(client, page, value);
 }
 
 static int max31785_read_long_data(struct i2c_client *client, int page,
@@ -110,11 +196,13 @@ static int max31785_get_pwm_mode(struct i2c_client *client, int page)
 	int config;
 	int command;
 
-	config = pmbus_read_byte_data(client, page, PMBUS_FAN_CONFIG_12);
+	config = max31785_pmbus_read_byte_data(client, page,
+					       PMBUS_FAN_CONFIG_12);
 	if (config < 0)
 		return config;
 
-	command = pmbus_read_word_data(client, page, PMBUS_FAN_COMMAND_1);
+	command = max31785_pmbus_read_word_data(client, page,
+						PMBUS_FAN_COMMAND_1);
 	if (command < 0)
 		return command;
 
@@ -138,15 +226,14 @@ static int max31785_read_word_data(struct i2c_client *client, int page,
 	switch (reg) {
 	case PMBUS_READ_FAN_SPEED_1:
 		if (page < MAX31785_NR_PAGES)
-			return -ENODATA;
+			return max31785_pmbus_read_word_data(client, page, reg);
 
 		rv = max31785_read_long_data(client, page - MAX31785_NR_PAGES,
 					     reg, &val);
 		if (rv < 0)
 			return rv;
 
-		rv = (val >> 16) & 0xffff;
-		break;
+		return (val >> 16) & 0xffff;
 	case PMBUS_FAN_COMMAND_1:
 		/*
 		 * PMBUS_FAN_COMMAND_x is probed to judge whether or not to
@@ -154,20 +241,28 @@ static int max31785_read_word_data(struct i2c_client *client, int page,
 		 *
 		 * Don't expose fan_target attribute for virtual pages.
 		 */
-		rv = (page >= MAX31785_NR_PAGES) ? -ENOTSUPP : -ENODATA;
+		if (page >= MAX31785_NR_PAGES)
+			return -ENOTSUPP;
 		break;
+	case PMBUS_VIRT_FAN_TARGET_1:
+		if (page >= MAX31785_NR_PAGES)
+			return -ENOTSUPP;
+
+		return -ENODATA;
 	case PMBUS_VIRT_PWM_1:
-		rv = max31785_get_pwm(client, page);
-		break;
+		return max31785_get_pwm(client, page);
 	case PMBUS_VIRT_PWM_ENABLE_1:
-		rv = max31785_get_pwm_mode(client, page);
-		break;
+		return max31785_get_pwm_mode(client, page);
 	default:
-		rv = -ENODATA;
+		if (page >= MAX31785_NR_PAGES)
+			return -ENXIO;
 		break;
 	}
 
-	return rv;
+	if (reg >= PMBUS_VIRT_BASE)
+		return -ENXIO;
+
+	return max31785_pmbus_read_word_data(client, page, reg);
 }
 
 static inline u32 max31785_scale_pwm(u32 sensor_val)
@@ -191,6 +286,31 @@ static inline u32 max31785_scale_pwm(u32 sensor_val)
 	return (sensor_val * 100) / 255;
 }
 
+static int max31785_update_fan(struct i2c_client *client, int page,
+			       u8 config, u8 mask, u16 command)
+{
+	int from, rv;
+	u8 to;
+
+	from = max31785_pmbus_read_byte_data(client, page, PMBUS_FAN_CONFIG_12);
+	if (from < 0)
+		return from;
+
+	to = (from & ~mask) | (config & mask);
+
+	if (to != from) {
+		rv = max31785_pmbus_write_byte_data(client, page,
+						    PMBUS_FAN_CONFIG_12, to);
+		if (rv < 0)
+			return rv;
+	}
+
+	rv = max31785_pmbus_write_word_data(client, page, PMBUS_FAN_COMMAND_1,
+					    command);
+
+	return rv;
+}
+
 static int max31785_pwm_enable(struct i2c_client *client, int page,
 				    u16 word)
 {
@@ -220,15 +340,18 @@ static int max31785_pwm_enable(struct i2c_client *client, int page,
 		return -EINVAL;
 	}
 
-	return pmbus_update_fan(client, page, 0, config, PB_FAN_1_RPM, rate);
+	return max31785_update_fan(client, page, config, PB_FAN_1_RPM, rate);
 }
 
 static int max31785_write_word_data(struct i2c_client *client, int page,
 				    int reg, u16 word)
 {
 	switch (reg) {
+	case PMBUS_VIRT_FAN_TARGET_1:
+		return max31785_update_fan(client, page, PB_FAN_1_RPM,
+					   PB_FAN_1_RPM, word);
 	case PMBUS_VIRT_PWM_1:
-		return pmbus_update_fan(client, page, 0, 0, PB_FAN_1_RPM,
+		return max31785_update_fan(client, page, 0, PB_FAN_1_RPM,
 					max31785_scale_pwm(word));
 	case PMBUS_VIRT_PWM_ENABLE_1:
 		return max31785_pwm_enable(client, page, word);
@@ -236,7 +359,279 @@ static int max31785_write_word_data(struct i2c_client *client, int page,
 		break;
 	}
 
-	return -ENODATA;
+	if (reg < PMBUS_VIRT_BASE)
+		return max31785_pmbus_write_word_data(client, page, reg, word);
+
+	return -ENXIO;
+}
+
+/*
+ * Returns negative error codes if an unrecoverable problem is detected, 0 if a
+ * recoverable problem is detected, or a positive value on success.
+ */
+static int max31785_of_fan_config(struct i2c_client *client,
+				  struct pmbus_driver_info *info,
+				  struct device_node *child)
+{
+	int mfr_cfg = 0, mfr_fault_resp = 0, pb_cfg;
+	struct device *dev = &client->dev;
+	char *lock_polarity = NULL;
+	const char *sval;
+	u32 page;
+	u32 uval;
+	int ret;
+
+	if (!of_device_is_compatible(child, "pmbus-fan"))
+		return 0;
+
+	ret = of_property_read_u32(child, "reg", &page);
+	if (ret < 0) {
+		dev_err(&client->dev, "Missing valid reg property\n");
+		return ret;
+	}
+
+	if (!(info->func[page] & PMBUS_HAVE_FAN12)) {
+		dev_err(dev, "Page %d does not have fan capabilities\n", page);
+		return -ENXIO;
+	}
+
+	ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
+	if (ret < 0)
+		return ret;
+
+	pb_cfg = max31785_i2c_smbus_read_byte_data(client, PMBUS_FAN_CONFIG_12);
+	if (pb_cfg < 0)
+		return pb_cfg;
+
+	if (of_property_read_bool(child->parent, "use-stored-presence")) {
+		if (!(pb_cfg & PB_FAN_1_INSTALLED))
+			dev_info(dev, "Fan %d is configured but not installed\n",
+				 page);
+	} else {
+		pb_cfg |= PB_FAN_1_INSTALLED;
+	}
+
+	ret = of_property_read_string(child, "maxim,fan-rotor-input", &sval);
+	if (ret < 0) {
+		dev_err(dev, "Missing valid maxim,fan-rotor-input property for fan %d\n",
+				page);
+		return ret;
+	}
+
+	if (strcmp("tach", sval) && strcmp("lock", sval)) {
+		dev_err(dev, "maxim,fan-rotor-input has invalid value for fan %d: %s\n",
+				page, sval);
+		return -EINVAL;
+	} else if (!strcmp("lock", sval)) {
+		mfr_cfg |= MFR_FAN_CONFIG_ROTOR;
+
+		ret = max31785_i2c_smbus_write_word_data(client,
+							 MFR_FAN_FAULT_LIMIT,
+							 1);
+		if (ret < 0)
+			return ret;
+
+		ret = of_property_read_string(child, "maxim,fan-lock-polarity",
+					      &sval);
+		if (ret < 0) {
+			dev_err(dev, "Missing valid maxim,fan-lock-polarity property for fan %d\n",
+					page);
+			return ret;
+		}
+
+		if (strcmp("low", sval) && strcmp("high", sval)) {
+			dev_err(dev, "maxim,fan-lock-polarity has invalid value for fan %d: %s\n",
+					page, lock_polarity);
+			return -EINVAL;
+		} else if (!strcmp("high", sval))
+			mfr_cfg |= MFR_FAN_CONFIG_ROTOR_HI_LO;
+	}
+
+	if (!of_property_read_string(child, "fan-mode", &sval)) {
+		if (!strcmp("rpm", sval))
+			pb_cfg |= PB_FAN_1_RPM;
+		else if (!strcmp("pwm", sval))
+			pb_cfg &= ~PB_FAN_1_RPM;
+		else {
+			dev_err(dev, "fan-mode has invalid value for fan %d: %s\n",
+					page, sval);
+			return -EINVAL;
+		}
+	}
+
+	ret = of_property_read_u32(child, "tach-pulses", &uval);
+	if (ret < 0) {
+		pb_cfg &= ~PB_FAN_1_PULSE_MASK;
+	} else if (uval && (uval - 1) < 4) {
+		pb_cfg = ((pb_cfg & ~PB_FAN_1_PULSE_MASK) | ((uval - 1) << 4));
+	} else {
+		dev_err(dev, "tach-pulses has invalid value for fan %d: %u\n",
+				page, uval);
+		return -EINVAL;
+	}
+
+	if (of_property_read_bool(child, "maxim,fan-health"))
+		mfr_cfg |= MFR_FAN_CONFIG_HEALTH;
+
+	if (of_property_read_bool(child, "maxim,fan-no-watchdog") ||
+		of_property_read_bool(child, "maxim,tmp-no-fault-ramp"))
+		mfr_cfg |= MFR_FAN_CONFIG_TSFO;
+
+	if (of_property_read_bool(child, "maxim,fan-dual-tach"))
+		mfr_cfg |= MFR_FAN_CONFIG_DUAL_TACH;
+
+	if (of_property_read_bool(child, "maxim,fan-no-fault-ramp"))
+		mfr_cfg |= MFR_FAN_CONFIG_TACHO;
+
+	if (!of_property_read_u32(child, "maxim,fan-startup", &uval)) {
+		uval /= 2;
+		if (uval < 5) {
+			mfr_cfg |= uval;
+		} else {
+			dev_err(dev, "maxim,fan-startup has invalid value for fan %d: %u\n",
+					page, uval);
+			return -EINVAL;
+		}
+	}
+
+	if (!of_property_read_u32(child, "maxim,fan-ramp", &uval)) {
+		if (uval < 8) {
+			mfr_cfg |= uval << 5;
+		} else {
+			dev_err(dev, "maxim,fan-ramp has invalid value for fan %d: %u\n",
+					page, uval);
+			return -EINVAL;
+		}
+	}
+
+	if (!of_property_read_u32(child, "maxim,tmp-hysteresis", &uval)) {
+		uval /= 2;
+		uval -= 1;
+		if (uval < 4) {
+			mfr_cfg |= uval << 10;
+		} else {
+			dev_err(dev, "maxim,tmp-hysteresis has invalid value for fan %d, %u\n",
+					page, uval);
+			return -EINVAL;
+		}
+	}
+
+	if (!of_property_read_u32(child, "maxim,fan-pwm-freq", &uval)) {
+		u16 val;
+
+		if (uval == 30) {
+			val = 0;
+		} else if (uval == 50) {
+			val = 1;
+		} else if (uval == 100) {
+			val = 2;
+		} else if (uval == 150) {
+			val = 3;
+		} else if (uval == 25000) {
+			val = 7;
+		} else {
+			dev_err(dev, "maxim,fan-pwm-freq has invalid value for fan %d: %u\n",
+					page, uval);
+			return -EINVAL;
+		}
+
+		mfr_cfg |= val << 13;
+	}
+
+	if (of_property_read_bool(child, "maxim,fan-fault-pin-mon"))
+		mfr_fault_resp |= MFR_FAULT_RESPONSE_MONITOR;
+
+	ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_FAN_CONFIG_12,
+					pb_cfg & ~PB_FAN_1_INSTALLED);
+	if (ret < 0)
+		return ret;
+
+	ret = max31785_i2c_smbus_write_word_data(client, MFR_FAN_CONFIG,
+						 mfr_cfg);
+	if (ret < 0)
+		return ret;
+
+	ret = max31785_i2c_smbus_write_byte_data(client, MFR_FAULT_RESPONSE,
+						 mfr_fault_resp);
+	if (ret < 0)
+		return ret;
+
+	ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_FAN_CONFIG_12,
+						 pb_cfg);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Fans are on pages 0 - 5. If the page property of a fan node is
+	 * greater than 5 we will have errored in checks above out above.
+	 * Therefore we don't need to cope with values up to 31, and the int
+	 * return type is enough.
+	 *
+	 * The bit mask return value is used to populate a bitfield of fans
+	 * who are both configured in the devicetree _and_ reported as
+	 * installed by the hardware. Any fans that are not configured in the
+	 * devicetree but are reported as installed by the hardware will have
+	 * their hardware configuration updated to unset the installed bit.
+	 */
+	return BIT(page);
+}
+
+static int max31785_of_tmp_config(struct i2c_client *client,
+				  struct pmbus_driver_info *info,
+				  struct device_node *child)
+{
+	struct device *dev = &client->dev;
+	struct device_node *np;
+	u16 mfr_tmp_cfg = 0;
+	u32 page;
+	u32 uval;
+	int ret;
+	int i;
+
+	if (!of_device_is_compatible(child, "pmbus-temperature"))
+		return 0;
+
+	ret = of_property_read_u32(child, "reg", &page);
+	if (ret < 0) {
+		dev_err(&client->dev, "Missing valid reg property\n");
+		return ret;
+	}
+
+	if (!(info->func[page] & PMBUS_HAVE_TEMP)) {
+		dev_err(dev, "Page %d does not have temp capabilities\n", page);
+		return -ENXIO;
+	}
+
+	ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
+	if (ret < 0)
+		return ret;
+
+	if (!of_property_read_u32(child, "maxim,tmp-offset", &uval)) {
+		if (uval < 32)
+			mfr_tmp_cfg |= uval << 10;
+	}
+
+	i = 0;
+	while ((np = of_parse_phandle(child, "maxim,tmp-fans", i))) {
+		if (of_property_read_u32(np, "reg", &uval)) {
+			dev_err(&client->dev, "Failed to read fan reg property for phandle index %d\n",
+					i);
+		} else {
+			if (uval < 6)
+				mfr_tmp_cfg |= BIT(uval);
+			else
+				dev_warn(&client->dev, "Invalid fan page: %d\n",
+						uval);
+		}
+		i++;
+	}
+
+	ret = max31785_i2c_smbus_write_word_data(client, MFR_TEMP_SENSOR_CONFIG,
+					mfr_tmp_cfg);
+	if (ret < 0)
+		return ret;
+
+	return 0;
 }
 
 #define MAX31785_FAN_FUNCS \
@@ -310,11 +705,11 @@ static int max31785_configure_dual_tach(struct i2c_client *client,
 	int i;
 
 	for (i = 0; i < MAX31785_NR_FAN_PAGES; i++) {
-		ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);
+		ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);
 		if (ret < 0)
 			return ret;
 
-		ret = i2c_smbus_read_word_data(client, MFR_FAN_CONFIG);
+		ret = max31785_i2c_smbus_read_word_data(client, MFR_FAN_CONFIG);
 		if (ret < 0)
 			return ret;
 
@@ -334,9 +729,12 @@ static int max31785_probe(struct i2c_client *client,
 			  const struct i2c_device_id *id)
 {
 	struct device *dev = &client->dev;
+	struct device_node *child;
 	struct pmbus_driver_info *info;
 	bool dual_tach = false;
+	u32 fans;
 	s64 ret;
+	int i;
 
 	if (!i2c_check_functionality(client->adapter,
 				     I2C_FUNC_SMBUS_BYTE_DATA |
@@ -349,7 +747,7 @@ static int max31785_probe(struct i2c_client *client,
 
 	*info = max31785_info;
 
-	ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 255);
+	ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_PAGE, 255);
 	if (ret < 0)
 		return ret;
 
@@ -366,6 +764,49 @@ static int max31785_probe(struct i2c_client *client,
 		return -ENODEV;
 	}
 
+	fans = 0;
+	for_each_child_of_node(dev->of_node, child) {
+		ret = max31785_of_fan_config(client, info, child);
+		if (ret < 0) {
+			of_node_put(child);
+			return ret;
+		}
+
+		if (ret)
+			fans |= ret;
+
+		ret = max31785_of_tmp_config(client, info, child);
+		if (ret < 0) {
+			of_node_put(child);
+			return ret;
+		}
+	}
+
+	for (i = 0; i < MAX31785_NR_PAGES; i++) {
+		bool have_fan = !!(info->func[i] & PMBUS_HAVE_FAN12);
+		bool fan_configured = !!(fans & BIT(i));
+
+		if (!have_fan || fan_configured)
+			continue;
+
+		ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_PAGE,
+							 i);
+		if (ret < 0)
+			return ret;
+
+		ret = max31785_i2c_smbus_read_byte_data(client,
+							PMBUS_FAN_CONFIG_12);
+		if (ret < 0)
+			return ret;
+
+		ret &= ~PB_FAN_1_INSTALLED;
+		ret = max31785_i2c_smbus_write_word_data(client,
+							 PMBUS_FAN_CONFIG_12,
+							 ret);
+		if (ret < 0)
+			return ret;
+	}
+
 	if (dual_tach) {
 		ret = max31785_configure_dual_tach(client, info);
 		if (ret < 0)
diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index 1d24397..fb267ec 100644
--- a/drivers/hwmon/pmbus/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -417,6 +417,9 @@ struct pmbus_driver_info {
 	/* Regulator functionality, if supported by this chip driver. */
 	int num_regulators;
 	const struct regulator_desc *reg_desc;
+
+	/* custom attributes */
+	const struct attribute_group **groups;
 };
 
 /* Regulator ops */
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 2e2b585..6e3320b 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -103,7 +103,7 @@ struct pmbus_data {
 	int max_attributes;
 	int num_attributes;
 	struct attribute_group group;
-	const struct attribute_group *groups[2];
+	const struct attribute_group **groups;
 	struct dentry *debugfs;		/* debugfs device directory */
 
 	struct pmbus_sensor *sensors;
@@ -168,9 +168,17 @@ int pmbus_set_page(struct i2c_client *client, int page)
 		return 0;
 
 	if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL)) {
+		dev_dbg(&client->dev, "Want page %u, %u cached\n", page,
+			data->currpage);
+
 		rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
-		if (rv < 0)
-			return rv;
+		if (rv) {
+			rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE,
+						       page);
+			dev_dbg(&client->dev,
+				"Failed to set page %u, performed one-shot retry %s: %d\n",
+				page, rv ? "and failed" : "with success", rv);
+		}
 
 		rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
 		if (rv < 0)
@@ -446,15 +454,15 @@ static int pmbus_get_fan_rate(struct i2c_client *client, int page, int id,
 		return s->data;
 	}
 
-	config = pmbus_read_byte_data(client, page,
-				      pmbus_fan_config_registers[id]);
+	config = _pmbus_read_byte_data(client, page,
+				       pmbus_fan_config_registers[id]);
 	if (config < 0)
 		return config;
 
 	have_rpm = !!(config & pmbus_fan_rpm_mask[id]);
 	if (want_rpm == have_rpm)
-		return pmbus_read_word_data(client, page,
-					    pmbus_fan_command_registers[id]);
+		return _pmbus_read_word_data(client, page,
+					     pmbus_fan_command_registers[id]);
 
 	/* Can't sensibly map between RPM and PWM, just return zero */
 	return 0;
@@ -2305,6 +2313,7 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
 	struct device *dev = &client->dev;
 	const struct pmbus_platform_data *pdata = dev_get_platdata(dev);
 	struct pmbus_data *data;
+	size_t groups_num = 0;
 	int ret;
 
 	if (!info)
@@ -2319,6 +2328,15 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
 	if (!data)
 		return -ENOMEM;
 
+	if (info->groups)
+		while (info->groups[groups_num])
+			groups_num++;
+
+	data->groups = devm_kcalloc(dev, groups_num + 2, sizeof(void *),
+				    GFP_KERNEL);
+	if (!data->groups)
+		return -ENOMEM;
+
 	i2c_set_clientdata(client, data);
 	mutex_init(&data->update_lock);
 	data->dev = dev;
@@ -2346,6 +2364,7 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
 	}
 
 	data->groups[0] = &data->group;
+	memcpy(data->groups + 1, info->groups, sizeof(void *) * groups_num);
 	data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name,
 							    data, data->groups);
 	if (IS_ERR(data->hwmon_dev)) {
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index f8979ab..72341ff 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -750,6 +750,18 @@
 	  I2C interface from ST-Ericsson's Nomadik and Ux500 architectures,
 	  as well as the STA2X11 PCIe I/O HUB.
 
+config I2C_NPCM7XX
+	tristate "Nuvoton I2C Controller"
+	depends on ARCH_NPCM7XX
+	select I2C_SLAVE
+	select CRC8
+	help
+	  If you say yes to this option, support will be included for the
+	  Nuvoton I2C controller.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-npcm7xx.
+
 config I2C_OCORES
 	tristate "OpenCores I2C Controller"
 	help
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 5f0cb69..cc6bcae 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -75,6 +75,7 @@
 obj-$(CONFIG_I2C_MV64XXX)	+= i2c-mv64xxx.o
 obj-$(CONFIG_I2C_MXS)		+= i2c-mxs.o
 obj-$(CONFIG_I2C_NOMADIK)	+= i2c-nomadik.o
+obj-$(CONFIG_I2C_NPCM7XX)	+= i2c-npcm7xx.o
 obj-$(CONFIG_I2C_OCORES)	+= i2c-ocores.o
 obj-$(CONFIG_I2C_OMAP)		+= i2c-omap.o
 obj-$(CONFIG_I2C_OWL)		+= i2c-owl.o
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
index 8dc9161..6c8b38f 100644
--- a/drivers/i2c/busses/i2c-aspeed.c
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -117,6 +117,7 @@
 
 enum aspeed_i2c_master_state {
 	ASPEED_I2C_MASTER_INACTIVE,
+	ASPEED_I2C_MASTER_PENDING,
 	ASPEED_I2C_MASTER_START,
 	ASPEED_I2C_MASTER_TX_FIRST,
 	ASPEED_I2C_MASTER_TX,
@@ -126,12 +127,13 @@ enum aspeed_i2c_master_state {
 };
 
 enum aspeed_i2c_slave_state {
-	ASPEED_I2C_SLAVE_STOP,
+	ASPEED_I2C_SLAVE_INACTIVE,
 	ASPEED_I2C_SLAVE_START,
 	ASPEED_I2C_SLAVE_READ_REQUESTED,
 	ASPEED_I2C_SLAVE_READ_PROCESSED,
 	ASPEED_I2C_SLAVE_WRITE_REQUESTED,
 	ASPEED_I2C_SLAVE_WRITE_RECEIVED,
+	ASPEED_I2C_SLAVE_STOP,
 };
 
 struct aspeed_i2c_bus {
@@ -156,6 +158,8 @@ struct aspeed_i2c_bus {
 	int				cmd_err;
 	/* Protected only by i2c_lock_bus */
 	int				master_xfer_result;
+	/* Multi-master */
+	bool				multi_master;
 #if IS_ENABLED(CONFIG_I2C_SLAVE)
 	struct i2c_client		*slave;
 	enum aspeed_i2c_slave_state	slave_state;
@@ -251,7 +255,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
 	}
 
 	/* Slave is not currently active, irq was for someone else. */
-	if (bus->slave_state == ASPEED_I2C_SLAVE_STOP)
+	if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE)
 		return irq_handled;
 
 	dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n",
@@ -277,16 +281,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
 		irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP;
 		bus->slave_state = ASPEED_I2C_SLAVE_STOP;
 	}
-	if (irq_status & ASPEED_I2CD_INTR_TX_NAK) {
+	if (irq_status & ASPEED_I2CD_INTR_TX_NAK &&
+	    bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) {
 		irq_handled |= ASPEED_I2CD_INTR_TX_NAK;
 		bus->slave_state = ASPEED_I2C_SLAVE_STOP;
 	}
-	if (irq_status & ASPEED_I2CD_INTR_TX_ACK)
-		irq_handled |= ASPEED_I2CD_INTR_TX_ACK;
 
 	switch (bus->slave_state) {
 	case ASPEED_I2C_SLAVE_READ_REQUESTED:
-		if (irq_status & ASPEED_I2CD_INTR_TX_ACK)
+		if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_ACK))
 			dev_err(bus->dev, "Unexpected ACK on read request.\n");
 		bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED;
 		i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value);
@@ -294,9 +297,12 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
 		writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG);
 		break;
 	case ASPEED_I2C_SLAVE_READ_PROCESSED:
-		if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK))
+		if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) {
 			dev_err(bus->dev,
 				"Expected ACK after processed read.\n");
+			break;
+		}
+		irq_handled |= ASPEED_I2CD_INTR_TX_ACK;
 		i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value);
 		writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG);
 		writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG);
@@ -310,10 +316,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
 		break;
 	case ASPEED_I2C_SLAVE_STOP:
 		i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
+		bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
+		break;
+	case ASPEED_I2C_SLAVE_START:
+		/* Slave was just started. Waiting for the next event. */;
 		break;
 	default:
-		dev_err(bus->dev, "unhandled slave_state: %d\n",
+		dev_err(bus->dev, "unknown slave_state: %d\n",
 			bus->slave_state);
+		bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
 		break;
 	}
 
@@ -329,6 +340,17 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus)
 	u8 slave_addr = i2c_8bit_addr_from_msg(msg);
 
 	bus->master_state = ASPEED_I2C_MASTER_START;
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	/*
+	 * If it's requested in the middle of a slave session, set the master
+	 * state to 'pending' then H/W will continue handling this master
+	 * command when the bus comes back to the idle state.
+	 */
+	if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE)
+		bus->master_state = ASPEED_I2C_MASTER_PENDING;
+#endif /* CONFIG_I2C_SLAVE */
+
 	bus->buf_index = 0;
 
 	if (msg->flags & I2C_M_RD) {
@@ -384,10 +406,6 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
 		bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
 		irq_handled |= ASPEED_I2CD_INTR_BUS_RECOVER_DONE;
 		goto out_complete;
-	} else {
-		/* Master is not currently active, irq was for someone else. */
-		if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE)
-			goto out_no_complete;
 	}
 
 	/*
@@ -399,12 +417,33 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
 	if (ret) {
 		dev_dbg(bus->dev, "received error interrupt: 0x%08x\n",
 			irq_status);
-		bus->cmd_err = ret;
-		bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
 		irq_handled |= (irq_status & ASPEED_I2CD_INTR_MASTER_ERRORS);
-		goto out_complete;
+		if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) {
+			bus->cmd_err = ret;
+			bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
+			goto out_complete;
+		}
 	}
 
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	/*
+	 * A pending master command will be started by H/W when the bus comes
+	 * back to idle state after completing a slave operation so change the
+	 * master state from 'pending' to 'start' at here if slave is inactive.
+	 */
+	if (bus->master_state == ASPEED_I2C_MASTER_PENDING) {
+		if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE)
+			goto out_no_complete;
+
+		bus->master_state = ASPEED_I2C_MASTER_START;
+	}
+#endif /* CONFIG_I2C_SLAVE */
+
+	/* Master is not currently active, irq was for someone else. */
+	if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE ||
+	    bus->master_state == ASPEED_I2C_MASTER_PENDING)
+		goto out_no_complete;
+
 	/* We are in an invalid state; reset bus to a known state. */
 	if (!bus->msgs) {
 		dev_err(bus->dev, "bus in unknown state. irq_status: 0x%x\n",
@@ -423,6 +462,20 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
 	 * then update the state and handle the new state below.
 	 */
 	if (bus->master_state == ASPEED_I2C_MASTER_START) {
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+		/*
+		 * If a peer master starts a xfer immediately after it queues a
+		 * master command, change its state to 'pending' then H/W will
+		 * continue the queued master xfer just after completing the
+		 * slave mode session.
+		 */
+		if (unlikely(irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH)) {
+			bus->master_state = ASPEED_I2C_MASTER_PENDING;
+			dev_dbg(bus->dev,
+				"master goes pending due to a slave start\n");
+			goto out_no_complete;
+		}
+#endif /* CONFIG_I2C_SLAVE */
 		if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) {
 			if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_NAK))) {
 				bus->cmd_err = -ENXIO;
@@ -566,7 +619,8 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
 	 * interrupt bits. Each case needs to be handled using corresponding
 	 * handlers depending on the current state.
 	 */
-	if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) {
+	if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE &&
+	    bus->master_state != ASPEED_I2C_MASTER_PENDING) {
 		irq_handled = aspeed_i2c_master_irq(bus, irq_remaining);
 		irq_remaining &= ~irq_handled;
 		if (irq_remaining)
@@ -601,15 +655,16 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
 {
 	struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap);
 	unsigned long time_left, flags;
-	int ret = 0;
 
 	spin_lock_irqsave(&bus->lock, flags);
 	bus->cmd_err = 0;
 
-	/* If bus is busy, attempt recovery. We assume a single master
-	 * environment.
-	 */
-	if (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS) {
+	/* If bus is busy in a single master environment, attempt recovery. */
+	if (!bus->multi_master &&
+	    (readl(bus->base + ASPEED_I2C_CMD_REG) &
+	     ASPEED_I2CD_BUS_BUSY_STS)) {
+		int ret;
+
 		spin_unlock_irqrestore(&bus->lock, flags);
 		ret = aspeed_i2c_recover_bus(bus);
 		if (ret)
@@ -629,10 +684,20 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
 	time_left = wait_for_completion_timeout(&bus->cmd_complete,
 						bus->adap.timeout);
 
-	if (time_left == 0)
+	if (time_left == 0) {
+		/*
+		 * If timed out and bus is still busy in a multi master
+		 * environment, attempt recovery at here.
+		 */
+		if (bus->multi_master &&
+		    (readl(bus->base + ASPEED_I2C_CMD_REG) &
+		     ASPEED_I2CD_BUS_BUSY_STS))
+			aspeed_i2c_recover_bus(bus);
+
 		return -ETIMEDOUT;
-	else
-		return bus->master_xfer_result;
+	}
+
+	return bus->master_xfer_result;
 }
 
 static u32 aspeed_i2c_functionality(struct i2c_adapter *adap)
@@ -672,7 +737,7 @@ static int aspeed_i2c_reg_slave(struct i2c_client *client)
 	__aspeed_i2c_reg_slave(bus, client->addr);
 
 	bus->slave = client;
-	bus->slave_state = ASPEED_I2C_SLAVE_STOP;
+	bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
 	spin_unlock_irqrestore(&bus->lock, flags);
 
 	return 0;
@@ -827,7 +892,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus,
 	if (ret < 0)
 		return ret;
 
-	if (!of_property_read_bool(pdev->dev.of_node, "multi-master"))
+	if (of_property_read_bool(pdev->dev.of_node, "multi-master"))
+		bus->multi_master = true;
+	else
 		fun_ctrl_reg |= ASPEED_I2CD_MULTI_MASTER_DIS;
 
 	/* Enable Master Mode */
@@ -930,7 +997,6 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
 	init_completion(&bus->cmd_complete);
 	bus->adap.owner = THIS_MODULE;
 	bus->adap.retries = 0;
-	bus->adap.timeout = 5 * HZ;
 	bus->adap.algo = &aspeed_i2c_algo;
 	bus->adap.dev.parent = &pdev->dev;
 	bus->adap.dev.of_node = pdev->dev.of_node;
diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c
new file mode 100644
index 0000000..6d177d3
--- /dev/null
+++ b/drivers/i2c/busses/i2c-npcm7xx.c
@@ -0,0 +1,2017 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Nuvoton NPCM7xx SMB Controller driver
+ *
+ * Copyright (C) 2018 Nuvoton Technologies tali.perry@nuvoton.com
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clk/nuvoton.h>
+#include <linux/crc8.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define I2C_VERSION "0.0.3"
+
+enum smb_mode {
+	SMB_SLAVE = 1,
+	SMB_MASTER
+};
+
+/*
+ * External SMB Interface driver xfer indication values, which indicate status
+ * of the bus.
+ */
+enum smb_state_ind {
+	SMB_NO_STATUS_IND = 0,
+	SMB_SLAVE_RCV_IND = 1,
+	SMB_SLAVE_XMIT_IND = 2,
+	SMB_SLAVE_XMIT_MISSING_DATA_IND = 3,
+	SMB_SLAVE_RESTART_IND = 4,
+	SMB_SLAVE_DONE_IND = 5,
+	SMB_MASTER_DONE_IND = 6,
+	SMB_NO_DATA_IND = 7,
+	SMB_NACK_IND = 8,
+	SMB_BUS_ERR_IND = 9,
+	SMB_WAKE_UP_IND = 10,
+	SMB_MASTER_PEC_ERR_IND = 11,
+	SMB_BLOCK_BYTES_ERR_IND = 12,
+	SMB_SLAVE_PEC_ERR_IND = 13,
+	SMB_SLAVE_RCV_MISSING_DATA_IND = 14,
+};
+
+// SMBus Operation type values
+enum smb_oper {
+	SMB_NO_OPER = 0,
+	SMB_WRITE_OPER = 1,
+	SMB_READ_OPER = 2
+};
+
+// SMBus Bank (FIFO mode)
+enum smb_bank {
+	SMB_BANK_0 = 0,
+	SMB_BANK_1 = 1
+};
+
+// Internal SMB states values (for the SMB module state machine).
+enum smb_state {
+	SMB_DISABLE = 0,
+	SMB_IDLE,
+	SMB_MASTER_START,
+	SMB_SLAVE_MATCH,
+	SMB_OPER_STARTED,
+	SMB_REPEATED_START,
+	SMB_STOP_PENDING
+};
+
+// Module supports setting multiple own slave addresses:
+enum smb_addr {
+	SMB_SLAVE_ADDR1 = 0,
+	SMB_SLAVE_ADDR2,
+	SMB_SLAVE_ADDR3,
+	SMB_SLAVE_ADDR4,
+	SMB_SLAVE_ADDR5,
+	SMB_SLAVE_ADDR6,
+	SMB_SLAVE_ADDR7,
+	SMB_SLAVE_ADDR8,
+	SMB_SLAVE_ADDR9,
+	SMB_SLAVE_ADDR10,
+	SMB_GC_ADDR,
+	SMB_ARP_ADDR
+};
+
+// global regs
+static struct regmap *gcr_regmap;
+static struct regmap *clk_regmap;
+
+#define NPCM_I2CSEGCTL  0xE4
+#define NPCM_SECCNT	0x68
+#define NPCM_CNTR25M	0x6C
+#define I2CSEGCTL_VAL	0x0333F000
+
+// Common regs
+#define NPCM_SMBSDA			0x000
+#define NPCM_SMBST			0x002
+#define NPCM_SMBCST			0x004
+#define NPCM_SMBCTL1			0x006
+#define NPCM_SMBADDR1			0x008
+#define NPCM_SMBCTL2			0x00A
+#define NPCM_SMBADDR2			0x00C
+#define NPCM_SMBCTL3			0x00E
+#define NPCM_SMBCST2			0x018
+#define NPCM_SMBCST3			0x019
+#define SMB_VER				0x01F
+
+// BANK 0 regs
+#define NPCM_SMBADDR3			0x010
+#define NPCM_SMBADDR7			0x011
+#define NPCM_SMBADDR4			0x012
+#define NPCM_SMBADDR8			0x013
+#define NPCM_SMBADDR5			0x014
+#define NPCM_SMBADDR9			0x015
+#define NPCM_SMBADDR6			0x016
+#define NPCM_SMBADDR10			0x017
+
+// SMBADDR array: because the addr regs are sprincled all over the address space
+const int  NPCM_SMBADDR[10] = {NPCM_SMBADDR1, NPCM_SMBADDR2, NPCM_SMBADDR3,
+			       NPCM_SMBADDR4, NPCM_SMBADDR5, NPCM_SMBADDR6,
+			       NPCM_SMBADDR7, NPCM_SMBADDR8, NPCM_SMBADDR9,
+			       NPCM_SMBADDR10};
+
+#define NPCM_SMBCTL4			0x01A
+#define NPCM_SMBCTL5			0x01B
+#define NPCM_SMBSCLLT			0x01C // SCL Low Time
+#define NPCM_SMBFIF_CTL			0x01D // FIFO Control
+#define NPCM_SMBSCLHT			0x01E // SCL High Time
+
+// BANK 1 regs
+#define NPCM_SMBFIF_CTS			0x010 // FIFO Control
+#define NPCM_SMBTXF_CTL			0x012 // Tx-FIFO Control
+#define NPCM_SMBT_OUT			0x014 // Bus T.O.
+#define NPCM_SMBPEC			0x016 // PEC Data
+#define NPCM_SMBTXF_STS			0x01A // Tx-FIFO Status
+#define NPCM_SMBRXF_STS			0x01C // Rx-FIFO Status
+#define NPCM_SMBRXF_CTL			0x01E // Rx-FIFO Control
+
+// NPCM_SMBST reg fields
+#define NPCM_SMBST_XMIT			BIT(0)
+#define NPCM_SMBST_MASTER		BIT(1)
+#define NPCM_SMBST_NMATCH		BIT(2)
+#define NPCM_SMBST_STASTR		BIT(3)
+#define NPCM_SMBST_NEGACK		BIT(4)
+#define NPCM_SMBST_BER			BIT(5)
+#define NPCM_SMBST_SDAST		BIT(6)
+#define NPCM_SMBST_SLVSTP		BIT(7)
+
+// NPCM_SMBCST reg fields
+#define NPCM_SMBCST_BUSY		BIT(0)
+#define NPCM_SMBCST_BB			BIT(1)
+#define NPCM_SMBCST_MATCH		BIT(2)
+#define NPCM_SMBCST_GCMATCH		BIT(3)
+#define NPCM_SMBCST_TSDA		BIT(4)
+#define NPCM_SMBCST_TGSCL		BIT(5)
+#define NPCM_SMBCST_MATCHAF		BIT(6)
+#define NPCM_SMBCST_ARPMATCH		BIT(7)
+
+// NPCM_SMBCTL1 reg fields
+#define NPCM_SMBCTL1_START		BIT(0)
+#define NPCM_SMBCTL1_STOP		BIT(1)
+#define NPCM_SMBCTL1_INTEN		BIT(2)
+#define NPCM_SMBCTL1_EOBINTE		BIT(3)
+#define NPCM_SMBCTL1_ACK		BIT(4)
+#define NPCM_SMBCTL1_GCMEN		BIT(5)
+#define NPCM_SMBCTL1_NMINTE		BIT(6)
+#define NPCM_SMBCTL1_STASTRE		BIT(7)
+
+// RW1S fields (inside a RW reg):
+#define NPCM_SMBCTL1_RWS_FIELDS	  (NPCM_SMBCTL1_START | NPCM_SMBCTL1_STOP | \
+				   NPCM_SMBCTL1_ACK)
+// NPCM_SMBADDR reg fields
+#define NPCM_SMBADDR_ADDR		GENMASK(6, 0)
+#define NPCM_SMBADDR_SAEN		BIT(7)
+
+// NPCM_SMBCTL2 reg fields
+#define SMBCTL2_ENABLE			BIT(0)
+#define SMBCTL2_SCLFRQ6_0		GENMASK(7, 1)
+
+// NPCM_SMBCTL3 reg fields
+#define SMBCTL3_SCLFRQ8_7		GENMASK(1, 0)
+#define SMBCTL3_ARPMEN			BIT(2)
+#define SMBCTL3_IDL_START		BIT(3)
+#define SMBCTL3_400K_MODE		BIT(4)
+#define SMBCTL3_BNK_SEL			BIT(5)
+#define SMBCTL3_SDA_LVL			BIT(6)
+#define SMBCTL3_SCL_LVL			BIT(7)
+
+// NPCM_SMBCST2 reg fields
+#define NPCM_SMBCST2_MATCHA1F		BIT(0)
+#define NPCM_SMBCST2_MATCHA2F		BIT(1)
+#define NPCM_SMBCST2_MATCHA3F		BIT(2)
+#define NPCM_SMBCST2_MATCHA4F		BIT(3)
+#define NPCM_SMBCST2_MATCHA5F		BIT(4)
+#define NPCM_SMBCST2_MATCHA6F		BIT(5)
+#define NPCM_SMBCST2_MATCHA7F		BIT(5)
+#define NPCM_SMBCST2_INTSTS		BIT(7)
+
+// NPCM_SMBCST3 reg fields
+#define NPCM_SMBCST3_MATCHA8F		BIT(0)
+#define NPCM_SMBCST3_MATCHA9F		BIT(1)
+#define NPCM_SMBCST3_MATCHA10F		BIT(2)
+#define NPCM_SMBCST3_EO_BUSY		BIT(7)
+
+// NPCM_SMBCTL4 reg fields
+#define SMBCTL4_HLDT			GENMASK(5, 0)
+#define SMBCTL4_LVL_WE			BIT(7)
+
+// NPCM_SMBCTL5 reg fields
+#define SMBCTL5_DBNCT			GENMASK(3, 0)
+
+// NPCM_SMBFIF_CTS reg fields
+#define NPCM_SMBFIF_CTS_RXF_TXE		BIT(1)
+#define NPCM_SMBFIF_CTS_RFTE_IE		BIT(3)
+#define NPCM_SMBFIF_CTS_CLR_FIFO	BIT(6)
+#define NPCM_SMBFIF_CTS_SLVRSTR		BIT(7)
+
+// NPCM_SMBTXF_CTL reg fields
+#ifdef SMB_CAPABILITY_32B_FIFO
+#define NPCM_SMBTXF_CTL_TX_THR		GENMASK(5, 0)
+#else
+#define NPCM_SMBTXF_CTL_TX_THR		GENMASK(4, 0)
+#endif
+#define NPCM_SMBTXF_CTL_THR_TXIE	BIT(6)
+
+// NPCM_SMBT_OUT reg fields
+#define NPCM_SMBT_OUT_TO_CKDIV		GENMASK(5, 0)
+#define NPCM_SMBT_OUT_T_OUTIE		BIT(6)
+#define NPCM_SMBT_OUT_T_OUTST		BIT(7)
+
+// NPCM_SMBTXF_STS reg fields
+#ifdef SMB_CAPABILITY_32B_FIFO
+#define NPCM_SMBTXF_STS_TX_BYTES	GENMASK(5, 0)
+#else
+#define NPCM_SMBTXF_STS_TX_BYTES	GENMASK(4, 0)
+#endif
+#define NPCM_SMBTXF_STS_TX_THST		BIT(6)
+
+// NPCM_SMBRXF_STS reg fields
+#ifdef SMB_CAPABILITY_32B_FIFO
+#define NPCM_SMBRXF_STS_RX_BYTES	GENMASK(5, 0)
+#else
+#define NPCM_SMBRXF_STS_RX_BYTES	GENMASK(4, 0)
+#endif
+#define NPCM_SMBRXF_STS_RX_THST		BIT(6)
+
+// NPCM_SMBFIF_CTL reg fields
+#define NPCM_SMBFIF_CTL_FIFO_EN		BIT(4)
+
+// NPCM_SMBRXF_CTL reg fields
+// Note: on the next HW version of this module, this HW is about to switch to
+//	 32 bytes FIFO. This size will be set using a config.
+//	 on current version 16 bytes FIFO is set using a define
+#ifdef SMB_CAPABILITY_32B_FIFO
+#define NPCM_SMBRXF_CTL_RX_THR		GENMASK(5, 0)
+#define NPCM_SMBRXF_CTL_THR_RXIE	BIT(6)
+#define NPCM_SMBRXF_CTL_LAST_PEC	BIT(7)
+#define SMBUS_FIFO_SIZE			32
+#else
+#define NPCM_SMBRXF_CTL_RX_THR		GENMASK(4, 0)
+#define NPCM_SMBRXF_CTL_LAST_PEC	BIT(5)
+#define NPCM_SMBRXF_CTL_THR_RXIE	BIT(6)
+#define SMBUS_FIFO_SIZE			16
+#endif
+
+// SMB_VER reg fields
+#define SMB_VER_VERSION			GENMASK(6, 0)
+#define SMB_VER_FIFO_EN			BIT(7)
+
+// stall/stuck timeout
+const unsigned int DEFAULT_STALL_COUNT =	25;
+
+// Data abort timeout
+const unsigned int ABORT_TIMEOUT =	 1000;
+
+// SMBus spec. values in KHZ
+const unsigned int SMBUS_FREQ_MIN = 10;
+const unsigned int SMBUS_FREQ_MAX = 1000;
+const unsigned int SMBUS_FREQ_100KHZ = 100;
+const unsigned int SMBUS_FREQ_400KHZ = 400;
+const unsigned int SMBUS_FREQ_1MHZ = 1000;
+
+// SCLFRQ min/max field values
+const unsigned int SCLFRQ_MIN = 10;
+const unsigned int SCLFRQ_MAX = 511;
+
+// SCLFRQ field position
+#define SCLFRQ_0_TO_6		GENMASK(6, 0)
+#define SCLFRQ_7_TO_8		GENMASK(8, 7)
+
+// SMB Maximum Retry Trials (on Bus Arbitration Loss)
+const unsigned int SMB_RETRY_MAX_COUNT = 2;
+const unsigned int SMB_NUM_OF_ADDR = 10;
+
+// for logging:
+#define NPCM_I2C_EVENT_START	BIT(0)
+#define NPCM_I2C_EVENT_STOP	BIT(1)
+#define NPCM_I2C_EVENT_ABORT	BIT(2)
+#define NPCM_I2C_EVENT_WRITE	BIT(3)
+#define NPCM_I2C_EVENT_READ	BIT(4)
+#define NPCM_I2C_EVENT_BER	BIT(5)
+#define NPCM_I2C_EVENT_NACK	BIT(6)
+#define NPCM_I2C_EVENT_TO	BIT(7)
+#define NPCM_I2C_EVENT_EOB	BIT(8)
+
+#define NPCM_I2C_EVENT_LOG(event)   (bus->event_log |= event)
+
+#define SMB_RECOVERY_SUPPORT
+
+// slave mode: if end device reads more data than available, ask issuer or
+// request for more data:
+#define SMB_WRAP_AROUND_BUFFER
+
+// Status of one SMBus module
+struct npcm_i2c {
+	struct i2c_adapter	adap;
+	struct device		*dev;
+	unsigned char __iomem	*reg;
+	spinlock_t		lock;   /* IRQ synchronization */
+	struct completion	cmd_complete;
+	int			irq;
+	int			cmd_err;
+	struct i2c_msg		*msgs;
+	int			msgs_num;
+	int			num;
+	u32			apb_clk;
+	enum smb_state		state;
+	enum smb_oper		operation;
+	enum smb_mode		master_or_slave;
+	enum smb_state_ind	stop_ind;
+	u8			dest_addr;
+	u8			*rd_buf;
+	u16			rd_size;
+	u16			rd_ind;
+	u8			*wr_buf;
+	u16			wr_size;
+	u16			wr_ind;
+	bool			fifo_use;
+	u8			threshold_fifo;
+
+	// PEC bit mask per slave address.
+	//		1: use PEC for this address,
+	//		0: do not use PEC for this address
+	u16			PEC_mask;
+	bool			PEC_use;
+	u8			crc_data;
+	bool			read_block_use;
+	u8			retry_count;
+	u8			int_cnt;
+	u32			event_log;
+	u32			clk_period_us;
+	u32			int_time_stamp[2];
+};
+
+static inline void _npcm7xx_get_time_stamp(u32 *time_quad0, u32 *time_quad1)
+{
+	u32 seconds, seconds_last;
+	u32 ref_clock;
+
+	regmap_read(clk_regmap, NPCM_SECCNT, &seconds_last);
+
+	do {
+		regmap_read(clk_regmap, NPCM_SECCNT, &seconds);
+		regmap_read(clk_regmap, NPCM_CNTR25M, &ref_clock);
+		regmap_read(clk_regmap, NPCM_SECCNT, &seconds_last);
+	} while (seconds_last != seconds);
+
+	*time_quad0 = ref_clock;
+	*time_quad1 = seconds;
+}
+
+#define EXT_CLOCK_FREQUENCY_MHZ 25
+#define CNTR25M_ACCURECY	EXT_CLOCK_FREQUENCY_MHZ  // minimum accurecy
+
+// Function:	 _npcm7xx_delay_relative
+// Parameters:
+//		 us_delay -  number of microseconds to delay since t0_time.
+//				  if zero: no delay.
+//
+//		t0_time	      - start time , to measure time from.
+// get a time stamp, delay us_delay from it. If us_delay has already passed
+// since the time stamp , then no delay is executed. returns the time elapsed
+// since t0_time
+
+static inline u32 _npcm7xx_delay_relative(u32 us_delay, u32 t0_time0,
+					  u32 t0_time1)
+{
+	u32 t1_time_0, t1_time_1;
+	u32 time_elapsed;
+	u32 minimum_delay = (us_delay * EXT_CLOCK_FREQUENCY_MHZ)
+		+ CNTR25M_ACCURECY;
+
+	// this is equivalent to microSec/0.64 + minimal tic length.
+	do {
+		_npcm7xx_get_time_stamp(&t1_time_0, &t1_time_1);
+		time_elapsed = ((EXT_CLOCK_FREQUENCY_MHZ * 1000000) *
+				(t1_time_1 - t0_time1)) +
+				(t1_time_0 - t0_time0);
+	} while (time_elapsed < minimum_delay);
+
+	// return elapsed time
+	return (u32)(time_elapsed / EXT_CLOCK_FREQUENCY_MHZ);
+}
+
+static inline void npcm_smb_select_bank(struct npcm_i2c *bus,
+					enum smb_bank bank)
+{
+	if (bus->fifo_use)
+		iowrite8((ioread8(bus->reg + NPCM_SMBCTL3) & ~SMBCTL3_BNK_SEL) |
+			 FIELD_PREP(SMBCTL3_BNK_SEL, bank),
+			 bus->reg + NPCM_SMBCTL3);
+}
+
+DECLARE_CRC8_TABLE(npcm7xx_crc8);
+
+static u8 npcm_smb_calc_crc8(u8 crc_data, u8 data)
+{
+	crc_data = crc8(npcm7xx_crc8, &data, 1, crc_data);
+	return crc_data;
+}
+
+static void npcm_smb_calc_PEC(struct npcm_i2c *bus, u8 data)
+{
+	if (bus->PEC_use)
+		bus->crc_data = npcm_smb_calc_crc8(bus->crc_data, data);
+}
+
+static inline void npcm_smb_wr_byte(struct npcm_i2c *bus, u8 data)
+{
+	iowrite8(data, bus->reg + NPCM_SMBSDA);
+	npcm_smb_calc_PEC(bus, data);
+}
+
+static inline void npcm_smb_rd_byte(struct npcm_i2c *bus, u8 *data)
+{
+	*data = ioread8(bus->reg + NPCM_SMBSDA);
+	npcm_smb_calc_PEC(bus, *data);
+}
+
+static inline u8 npcm_smb_get_PEC(struct npcm_i2c *bus)
+{
+	if (bus->PEC_use)
+		return bus->crc_data;
+	else
+		return 0;
+}
+
+static inline void npcm_smb_write_PEC(struct npcm_i2c *bus)
+{
+	if (bus->PEC_use) {
+		// get PAC value and write to the bus:
+		npcm_smb_wr_byte(bus, npcm_smb_get_PEC(bus));
+	}
+}
+
+//
+//  NPCM7XX SMB module allows writing to SCL and SDA pins directly
+//  without the need to change muxing of pins.
+//  This feature will be used for recovery sequences i.e.
+//
+static void npcm_smb_set_SCL(struct i2c_adapter *_adap, int level)
+{
+#ifdef SMB_CAPABILITY_FORCE_SCL_SDA
+	unsigned long flags;
+	struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
+
+	// Select Bank 0 to access NPCM_SMBCTL4
+	spin_lock_irqsave(&bus->lock, flags);
+	npcm_smb_select_bank(bus, SMB_BANK_0);
+
+	// Set SCL_LVL, SDA_LVL bits as Read/Write (R/W)
+	iowrite8(ioread8(bus->reg + NPCM_SMBCTL4) | SMBCTL4_LVL_WE,
+		 bus->reg + NPCM_SMBCTL4);
+
+	// Set level
+	iowrite8((ioread8(bus->reg + NPCM_SMBCTL3)
+		& ~SMBCTL3_SCL_LVL) | FIELD_PREP(SMBCTL3_SCL_LVL,
+		level), bus->reg + NPCM_SMBCTL3);
+
+	// Set SCL_LVL, SDA_LVL bits as Read Only (RO)
+	iowrite8(ioread8(bus->reg + NPCM_SMBCTL4)
+		 & ~SMBCTL4_LVL_WE, bus->reg + NPCM_SMBCTL4);
+
+	// Return to Bank 1
+	npcm_smb_select_bank(bus, SMB_BANK_1);
+	spin_unlock_irqrestore(&bus->lock, flags);
+#endif
+}
+
+static int npcm_smb_get_SCL(struct i2c_adapter *_adap)
+{
+	unsigned long flags;
+	unsigned int ret = 0;
+	struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
+
+	// Select Bank 0 to access NPCM_SMBCTL4
+	spin_lock_irqsave(&bus->lock, flags);
+	npcm_smb_select_bank(bus, SMB_BANK_0);
+
+	// Get SCL level
+	ret = FIELD_GET(SMBCTL3_SCL_LVL,  ioread8(bus->reg + NPCM_SMBCTL3));
+
+	// Return to Bank 1
+	npcm_smb_select_bank(bus, SMB_BANK_1);
+	spin_unlock_irqrestore(&bus->lock, flags);
+	return ret;
+}
+
+static int npcm_smb_get_SDA(struct i2c_adapter *_adap)
+{
+	unsigned long flags;
+	unsigned int ret = 0;
+	struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
+
+	// Select Bank 0 to access NPCM_SMBCTL4
+	spin_lock_irqsave(&bus->lock, flags);
+	npcm_smb_select_bank(bus, SMB_BANK_0);
+
+	// Get SDA level
+	ret = FIELD_GET(SMBCTL3_SDA_LVL,  ioread8(bus->reg + NPCM_SMBCTL3));
+
+	// Return to Bank 1
+	npcm_smb_select_bank(bus, SMB_BANK_1);
+	spin_unlock_irqrestore(&bus->lock, flags);
+	return ret;
+}
+
+static inline u16 npcm_smb_get_index(struct npcm_i2c *bus)
+{
+	u16 index = 0;
+
+	if (bus->operation == SMB_READ_OPER)
+		index = bus->rd_ind;
+	else if (bus->operation == SMB_WRITE_OPER)
+		index = bus->wr_ind;
+
+	return index;
+}
+
+// quick protocol:
+static inline bool npcm_smb_is_quick(struct npcm_i2c *bus)
+{
+	if (bus->wr_size == 0 && bus->rd_size == 0)
+		return true;
+	return false;
+}
+
+static void npcm_smb_disable(struct npcm_i2c *bus)
+{
+	int i;
+
+	// select bank 0 for SMB addresses
+	npcm_smb_select_bank(bus, SMB_BANK_0);
+
+	// Slave Addresses Removal
+	for (i = SMB_SLAVE_ADDR1; i < SMB_NUM_OF_ADDR; i++)
+		iowrite8(0, bus->reg + NPCM_SMBADDR[i]);
+
+	// select bank 0 for SMB addresses
+	npcm_smb_select_bank(bus, SMB_BANK_1);
+
+	// Disable module.
+	iowrite8(ioread8(bus->reg + NPCM_SMBCTL2) & ~SMBCTL2_ENABLE,
+		 bus->reg + NPCM_SMBCTL2);
+
+	// Set module disable
+	bus->state = SMB_DISABLE;
+}
+
+static void npcm_smb_enable(struct npcm_i2c *bus)
+{
+	iowrite8((ioread8(bus->reg + NPCM_SMBCTL2) | SMBCTL2_ENABLE),
+		 bus->reg + NPCM_SMBCTL2);
+}
+
+// enable\disable end of busy (EOB) interrupt
+static inline void npcm_smb_eob_int(struct npcm_i2c *bus, bool enable)
+{
+	if (enable) {
+		iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) |
+			 NPCM_SMBCTL1_EOBINTE)  & ~NPCM_SMBCTL1_RWS_FIELDS,
+			 bus->reg + NPCM_SMBCTL1);
+	} else {
+		iowrite8(ioread8(bus->reg + NPCM_SMBCTL1) &
+			 ~NPCM_SMBCTL1_EOBINTE & ~NPCM_SMBCTL1_RWS_FIELDS,
+			 bus->reg + NPCM_SMBCTL1);
+
+		// Clear EO_BUSY pending bit:
+		iowrite8(ioread8(bus->reg + NPCM_SMBCST3) |
+			 NPCM_SMBCST3_EO_BUSY, bus->reg + NPCM_SMBCST3);
+	}
+}
+
+static inline bool npcm_smb_tx_fifo_full(struct npcm_i2c *bus)
+{
+	// check if TX FIFO full:
+	return (bool)FIELD_GET(NPCM_SMBTXF_STS_TX_THST,
+			       ioread8(bus->reg + NPCM_SMBTXF_STS));
+}
+
+static inline bool npcm_smb_rx_fifo_full(struct npcm_i2c *bus)
+{
+	// check if RX FIFO full:
+	return (bool)FIELD_GET(NPCM_SMBRXF_STS_RX_THST,
+			       ioread8(bus->reg + NPCM_SMBRXF_STS));
+}
+
+static inline void npcm_smb_clear_tx_fifo(struct npcm_i2c *bus)
+{
+	// clear TX FIFO:
+	iowrite8(ioread8(bus->reg + NPCM_SMBTXF_STS) |
+		 NPCM_SMBTXF_STS_TX_THST,
+		 bus->reg + NPCM_SMBTXF_STS);
+}
+
+static inline void npcm_smb_clear_rx_fifo(struct npcm_i2c *bus)
+{
+	// clear RX FIFO:
+	iowrite8(ioread8(bus->reg + NPCM_SMBRXF_STS) |
+			 NPCM_SMBRXF_STS_RX_THST,
+			 bus->reg + NPCM_SMBRXF_STS);
+}
+
+static void npcm_smb_int_enable(struct npcm_i2c *bus, bool enable)
+{
+	if (enable)
+		iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) |
+			 NPCM_SMBCTL1_INTEN) & ~NPCM_SMBCTL1_RWS_FIELDS,
+			 bus->reg + NPCM_SMBCTL1);
+	else
+		iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) &
+			 ~NPCM_SMBCTL1_INTEN) & ~NPCM_SMBCTL1_RWS_FIELDS,
+			 bus->reg + NPCM_SMBCTL1);
+}
+
+static inline void npcm_smb_master_start(struct npcm_i2c *bus)
+{
+	NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_START);
+
+	iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) | NPCM_SMBCTL1_START) &
+		 ~(NPCM_SMBCTL1_STOP | NPCM_SMBCTL1_ACK),
+		 bus->reg + NPCM_SMBCTL1);
+}
+
+static inline void npcm_smb_master_stop(struct npcm_i2c *bus)
+{
+	NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_STOP);
+
+	// override HW issue: SMBus may fail to supply stop condition in Master
+	// Write operation.
+	// Need to delay at least 5 us from the last int, before issueing a stop
+	_npcm7xx_delay_relative(5, bus->int_time_stamp[0],
+				bus->int_time_stamp[1]);
+
+	iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) | NPCM_SMBCTL1_STOP) &
+		 ~(NPCM_SMBCTL1_START | NPCM_SMBCTL1_ACK),
+		 bus->reg + NPCM_SMBCTL1);
+
+	if (bus->fifo_use) {
+		npcm_smb_select_bank(bus, SMB_BANK_1);
+
+		if (bus->operation == SMB_READ_OPER)
+			npcm_smb_clear_rx_fifo(bus);
+		else
+			npcm_smb_clear_tx_fifo(bus);
+
+		iowrite8(ioread8(bus->reg + NPCM_SMBFIF_CTS) |
+				 NPCM_SMBFIF_CTS_SLVRSTR |
+				 NPCM_SMBFIF_CTS_RXF_TXE,
+				 bus->reg + NPCM_SMBFIF_CTS);
+
+		iowrite8(0, bus->reg + NPCM_SMBTXF_CTL);
+	}
+}
+
+static inline void npcm_smb_abort_data(struct npcm_i2c *bus)
+{
+	unsigned int timeout = ABORT_TIMEOUT;
+	u8 data;
+
+	NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_ABORT);
+	// Generate a STOP condition
+	npcm_smb_master_stop(bus);
+	npcm_smb_rd_byte(bus, &data);
+
+	// Clear NEGACK, STASTR and BER bits
+	iowrite8(NPCM_SMBST_STASTR | NPCM_SMBST_NEGACK |
+		NPCM_SMBST_BER, bus->reg + NPCM_SMBST);
+
+	// Wait till STOP condition is generated
+	while (FIELD_GET(NPCM_SMBCTL1_STOP, ioread8(bus->reg + NPCM_SMBCTL1))) {
+		timeout--;
+		if (!FIELD_GET(NPCM_SMBCTL1_STOP,
+			       ioread8(bus->reg + NPCM_SMBCTL1)))
+			break;
+		if (timeout <= 1) {
+			dev_err(bus->dev, "%s, abort timeout!\n", __func__);
+			break;
+		}
+	}
+}
+
+static inline void npcm_smb_stall_after_start(struct npcm_i2c *bus, bool stall)
+{
+	if (stall)
+		iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) |
+			 NPCM_SMBCTL1_STASTRE)  & ~NPCM_SMBCTL1_RWS_FIELDS,
+			 bus->reg + NPCM_SMBCTL1);
+	else
+		iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) &
+			 ~NPCM_SMBCTL1_STASTRE)  & ~NPCM_SMBCTL1_RWS_FIELDS,
+			 bus->reg + NPCM_SMBCTL1);
+}
+
+static inline void npcm_smb_nack(struct npcm_i2c *bus)
+{
+	if (bus->rd_ind < (bus->rd_size - 1))
+		dev_info(bus->dev,
+			 "\tNACK err bus%d, SA=0x%x, rd(%d\%d), op=%d st=%d\n",
+			 bus->num, bus->dest_addr, bus->rd_ind, bus->rd_size,
+			 bus->operation, bus->state);
+	iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) | NPCM_SMBCTL1_ACK) &
+		 ~(NPCM_SMBCTL1_STOP | NPCM_SMBCTL1_START),
+		 bus->reg + NPCM_SMBCTL1);
+}
+
+static void npcm_smb_reset(struct npcm_i2c *bus)
+{
+	// Save NPCM_SMBCTL1 relevant bits. It is being cleared when the
+	// module is disabled
+	u8 smbctl1 = ioread8(bus->reg + NPCM_SMBCTL1) & (NPCM_SMBCTL1_GCMEN
+						      | NPCM_SMBCTL1_INTEN
+						      | NPCM_SMBCTL1_NMINTE);
+
+	// Disable the SMB module
+	iowrite8((ioread8(bus->reg + NPCM_SMBCTL2) & ~SMBCTL2_ENABLE),
+		 bus->reg + NPCM_SMBCTL2);
+
+	// Enable the SMB module
+	npcm_smb_enable(bus);
+
+	// Restore NPCM_SMBCTL1 status
+	iowrite8(smbctl1 & ~NPCM_SMBCTL1_RWS_FIELDS, bus->reg + NPCM_SMBCTL1);
+
+	// Reset driver status
+	bus->state = SMB_IDLE;
+	//
+	// Configure FIFO disabled mode so slave will not use fifo
+	//  (master will set it on if supported)
+	iowrite8(ioread8(bus->reg + NPCM_SMBFIF_CTL) &
+		~NPCM_SMBFIF_CTL_FIFO_EN,
+		bus->reg + NPCM_SMBFIF_CTL);
+	bus->fifo_use = false;
+}
+
+static inline bool npcm_smb_is_master(struct npcm_i2c *bus)
+{
+	return (bool)FIELD_GET(NPCM_SMBST_MASTER,
+			       ioread8(bus->reg + NPCM_SMBST));
+}
+
+static int npcm_smb_master_abort(struct npcm_i2c *bus)
+{
+	int ret = -(EIO);
+
+	// Only current master is allowed to issue Stop Condition
+	if (npcm_smb_is_master(bus)) {
+		npcm_smb_abort_data(bus);
+		ret = 0;
+	}
+
+	npcm_smb_reset(bus);
+
+	return ret;
+}
+
+static void npcm_smb_callback(struct npcm_i2c *bus,
+			      enum smb_state_ind op_status, u16 info)
+{
+	struct i2c_msg *msgs = bus->msgs;
+	int msgs_num = bus->msgs_num;
+
+	switch (op_status) {
+	case SMB_MASTER_DONE_IND:
+	// Master transaction finished and all transmit bytes were sent
+	// info: number of bytes actually received after the Master
+	//	receive operation (if Master didn't issue receive it
+	//	should be 0)
+	// Notify that not all data was received on Master or Slave
+	// info:
+	//	on receive: number of actual bytes received
+	//	when PEC is used even if 'info' is the expected number
+	//	of bytes, it means that PEC error occurred.
+	{
+		if (msgs[0].flags & I2C_M_RD)
+			msgs[0].len = info;
+		else if (msgs_num == 2 && msgs[1].flags & I2C_M_RD)
+			msgs[1].len = info;
+
+		bus->cmd_err = 0;
+		complete(&bus->cmd_complete);
+	}
+	break;
+
+	case SMB_NO_DATA_IND:
+	// Notify that not all data was received on Master or Slave
+	// info:
+	//on receive: number of actual bytes received
+	//	when PEC is used even if 'info' is the expected number
+	//	of bytes,it means that PEC error occurred.
+	{
+		if (msgs[0].flags & I2C_M_RD)
+			msgs[0].len = info;
+		else if (msgs_num == 2 && msgs[1].flags & I2C_M_RD)
+			msgs[1].len = info;
+
+		bus->cmd_err = -EFAULT;
+		complete(&bus->cmd_complete);
+	}
+	break;
+	case SMB_NACK_IND:
+		// MASTER transmit got a NAK before transmitting all bytes
+		// info: number of transmitted bytes
+		bus->cmd_err = -EAGAIN;
+		complete(&bus->cmd_complete);
+
+		break;
+	case SMB_BUS_ERR_IND:
+		// Bus error
+		// info: has no meaning
+		bus->cmd_err = -EIO;
+		complete(&bus->cmd_complete);
+		break;
+	case SMB_WAKE_UP_IND:
+		// SMBus wake up
+		// info: has no meaning
+		break;
+	default:
+		break;
+	}
+}
+
+static u32 npcm_smb_get_fifo_fullness(struct npcm_i2c *bus)
+{
+	if (bus->operation == SMB_WRITE_OPER)
+		return FIELD_GET(NPCM_SMBTXF_STS_TX_BYTES,
+				 ioread8(bus->reg + NPCM_SMBTXF_STS));
+	else if (bus->operation == SMB_READ_OPER)
+		return FIELD_GET(NPCM_SMBRXF_STS_RX_BYTES,
+				 ioread8(bus->reg + NPCM_SMBRXF_STS));
+	return 0;
+}
+
+static void npcm_smb_write_to_fifo(struct npcm_i2c *bus, u16 max_bytes_to_send)
+{
+	// Fill the FIFO, while the FIFO is not full and there are more bytes to
+	// write
+	while ((max_bytes_to_send--) && (SMBUS_FIFO_SIZE -
+					 npcm_smb_get_fifo_fullness(bus))) {
+		// write the data
+		if (bus->wr_ind < bus->wr_size) {
+			if (bus->PEC_use &&
+			    (bus->wr_ind + 1 == bus->wr_size) &&
+			    (bus->rd_size == 0 ||
+			     bus->master_or_slave == SMB_SLAVE)) {
+				// Master send PEC in write protocol, Slave send
+				// PEC in read protocol.
+				npcm_smb_write_PEC(bus);
+				bus->wr_ind++;
+			} else {
+				npcm_smb_wr_byte(bus,
+						 bus->wr_buf[bus->wr_ind++]);
+			}
+		} else {
+#ifdef SMB_WRAP_AROUND_BUFFER
+			// We're out of bytes. Ask the higher level for
+			// more bytes. Let it know that driver
+			// used all its' bytes
+
+			npcm_smb_clear_tx_fifo(bus);
+
+			// Reset state for the remaining bytes transaction
+			bus->state = SMB_SLAVE_MATCH;
+
+			// Notify upper layer of transaction completion
+			npcm_smb_callback(bus, SMB_SLAVE_XMIT_MISSING_DATA_IND,
+					  bus->wr_ind);
+
+			iowrite8(NPCM_SMBST_SDAST, bus->reg + NPCM_SMBST);
+#else
+			npcm_smb_wr_byte(bus, 0xFF);
+#endif
+		}
+	}
+}
+
+// configure the FIFO before using it. If nread is -1 RX FIFO will not be
+// configured. same for	nwrite
+static void npcm_smb_set_fifo(struct npcm_i2c *bus, int nread, int nwrite)
+{
+	if (!bus->fifo_use)
+		return;
+	npcm_smb_select_bank(bus, SMB_BANK_1);
+	npcm_smb_clear_tx_fifo(bus);
+	npcm_smb_clear_rx_fifo(bus);
+
+	// configure RX FIFO
+	if (nread > 0) {
+		// clear LAST bit:
+		iowrite8(ioread8(bus->reg + NPCM_SMBRXF_CTL) &
+					(~NPCM_SMBRXF_CTL_LAST_PEC),
+					bus->reg + NPCM_SMBRXF_CTL);
+
+		if (nread > SMBUS_FIFO_SIZE)
+			iowrite8((ioread8(bus->reg + NPCM_SMBRXF_CTL) &
+				~NPCM_SMBRXF_CTL_RX_THR)
+				| FIELD_PREP(NPCM_SMBRXF_CTL_RX_THR,
+				SMBUS_FIFO_SIZE), bus->reg + NPCM_SMBRXF_CTL);
+		else
+			iowrite8((ioread8(bus->reg + NPCM_SMBRXF_CTL) &
+					  ~NPCM_SMBRXF_CTL_RX_THR) |
+					  FIELD_PREP(NPCM_SMBRXF_CTL_RX_THR,
+						     (u8)(nread)),
+				 bus->reg + NPCM_SMBRXF_CTL);
+
+		if (nread <= SMBUS_FIFO_SIZE && !bus->read_block_use)
+			iowrite8(ioread8(bus->reg + NPCM_SMBRXF_CTL) |
+				 NPCM_SMBRXF_CTL_LAST_PEC,
+				 bus->reg + NPCM_SMBRXF_CTL);
+	}
+
+	// configure TX FIFO
+	if (nwrite > 0) {
+		if (nwrite > SMBUS_FIFO_SIZE)
+			// data to send is more then FIFO size.
+			// Configure the FIFO int to be mid of FIFO.
+			iowrite8(NPCM_SMBTXF_CTL_THR_TXIE |
+				(SMBUS_FIFO_SIZE / 2),
+				bus->reg + NPCM_SMBTXF_CTL);
+		else if (nwrite > (SMBUS_FIFO_SIZE / 2) &&
+			 bus->wr_ind != 0)
+			// wr_ind != 0 means that this is not the first
+			// write. since int is in the mid of FIFO, only
+			// half of the fifo is empty.
+			// Continue to configure the FIFO int to be mid
+			// of FIFO.
+			iowrite8(NPCM_SMBTXF_CTL_THR_TXIE |
+				 (SMBUS_FIFO_SIZE / 2),
+				 bus->reg + NPCM_SMBTXF_CTL);
+		else
+			// This is the either first write (wr_ind = 0)
+			// and data to send is less or equal to FIFO
+			// size.
+			// Or this is the last write and data to send
+			// is less or equal half FIFO size.
+			// In both cases disable the FIFO threshold int.
+			// The next int will happen after the FIFO will
+			// get empty.
+			iowrite8(0, bus->reg + NPCM_SMBTXF_CTL);
+		npcm_smb_clear_tx_fifo(bus);
+	}
+}
+
+static void npcm_smb_read_from_fifo(struct npcm_i2c *bus, u8 bytes_in_fifo)
+{
+	while (bytes_in_fifo--) {
+		// Keep read data
+		u8 data = ioread8(bus->reg + NPCM_SMBSDA);
+
+		npcm_smb_calc_PEC(bus, data);
+		if (bus->rd_ind < bus->rd_size) {
+			bus->rd_buf[bus->rd_ind++] = data;
+			if (bus->rd_ind == 1 && bus->read_block_use)
+				// First byte indicates length in block protocol
+				bus->rd_size = data;
+		}
+	}
+}
+
+static void npcm_smb_master_fifo_read(struct npcm_i2c *bus)
+{
+	u16 rcount;
+	u8 fifo_bytes;
+	enum smb_state_ind ind = SMB_MASTER_DONE_IND;
+
+	rcount = bus->rd_size - bus->rd_ind;
+
+	// In order not to change the RX_TRH during transaction (we found that
+	// this might be problematic if it takes too much time to read the FIFO)
+	//  we read the data in the following way. If the number of bytes to
+	// read == FIFO Size + C (where C < FIFO Size)then first read C bytes
+	// and in the next int we read rest of the data.
+	if (rcount < (2 * SMBUS_FIFO_SIZE) && rcount > SMBUS_FIFO_SIZE)
+		fifo_bytes = (u8)(rcount - SMBUS_FIFO_SIZE);
+	else
+		fifo_bytes = npcm_smb_get_fifo_fullness(bus);
+
+	if (rcount - fifo_bytes == 0) {
+		// last byte is about to be read - end of transaction.
+		// Stop should be set before reading last byte.
+		npcm_smb_eob_int(bus, true);
+		npcm_smb_master_stop(bus);
+		npcm_smb_read_from_fifo(bus, fifo_bytes);
+
+		if (npcm_smb_get_PEC(bus) != 0)
+			ind = SMB_MASTER_PEC_ERR_IND;
+		bus->state = SMB_STOP_PENDING;
+		bus->stop_ind = ind;
+
+	} else {
+		npcm_smb_read_from_fifo(bus, fifo_bytes);
+		rcount = bus->rd_size - bus->rd_ind;
+		npcm_smb_set_fifo(bus, rcount, -1);
+	}
+}
+
+static void npcm_smb_int_master_handler_write(struct npcm_i2c *bus)
+{
+	u16 wcount;
+
+	NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_WRITE);
+	if (bus->fifo_use)
+		npcm_smb_clear_tx_fifo(bus);
+
+	// Master write operation - last byte handling
+	if (bus->wr_ind == bus->wr_size) {
+		if (bus->fifo_use && npcm_smb_get_fifo_fullness(bus) > 0)
+	// No more bytes to send (to add to the FIFO), however the FIFO is not
+	// empty yet. It is still in the middle of tx. Currently there's nothing
+	// to do except for waiting to the end of the tx.
+	// We will get an int when the FIFO will get empty.
+			return;
+
+		if (bus->rd_size == 0) {
+			// all bytes have been written, in a pure wr operation
+			npcm_smb_eob_int(bus, true);
+
+			// Issue a STOP condition on the bus
+			npcm_smb_master_stop(bus);
+			// Clear SDA Status bit (by writing dummy byte)
+			npcm_smb_wr_byte(bus, 0xFF);
+
+			bus->state = SMB_STOP_PENDING;
+			bus->stop_ind = SMB_MASTER_DONE_IND;
+		} else {
+			// last write-byte written on previous int - need to
+			// restart & send slave address
+			if (bus->PEC_use && !bus->read_block_use &&
+			    !npcm_smb_is_quick(bus))
+			    // PEC is used but the protocol is not block read
+			    // then we add extra bytes for PEC support
+				bus->rd_size += 1;
+
+			if (bus->fifo_use) {
+				if (bus->rd_size == 1 || bus->read_block_use) {
+					// SMBus Block read transaction.
+					iowrite8(0, bus->reg + NPCM_SMBTXF_CTL);
+					iowrite8(1, bus->reg + NPCM_SMBRXF_CTL);
+				}
+			}
+
+			npcm_smb_set_fifo(bus, bus->rd_size, -1);
+
+			// Generate (Repeated) Start upon next write to SDA
+			npcm_smb_master_start(bus);
+
+			if (bus->rd_size == 1)
+
+	// Receiving one byte only - stall after successful completion of send
+	// address byte. If we NACK here, and slave doesn't ACK the address, we
+	// might unintentionally NACK the next multi-byte read
+
+				npcm_smb_stall_after_start(bus, true);
+
+			// send the slave address in read direction
+			npcm_smb_wr_byte(bus, bus->dest_addr | 0x1);
+
+			// Next int will occur on read
+			bus->operation = SMB_READ_OPER;
+		}
+	} else {
+		if (bus->PEC_use && !npcm_smb_is_quick(bus))
+			// extra bytes for PEC support
+			bus->wr_size += 1;
+
+		// write next byte not last byte and not slave address
+		if (!bus->fifo_use || bus->wr_size == 1) {
+			if (bus->PEC_use && bus->rd_size == 0 &&
+			    (bus->wr_ind + 1 == bus->wr_size)) {
+				// Master write protocol to send PEC byte.
+				npcm_smb_write_PEC(bus);
+				bus->wr_ind++;
+			} else {
+				npcm_smb_wr_byte(bus,
+						 bus->wr_buf[bus->wr_ind++]);
+			}
+		} else { // FIFO is used
+			wcount = bus->wr_size - bus->wr_ind;
+			npcm_smb_set_fifo(bus, -1, wcount);
+			npcm_smb_write_to_fifo(bus, wcount);
+		}
+	}
+}
+
+static void npcm_smb_int_master_handler_read(struct npcm_i2c *bus)
+{
+	u16 block_zero_bytes;
+	u32 fifo_bytes;
+
+	// Master read operation (pure read or following a write operation).
+	NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_READ);
+
+	// Initialize number of bytes to include only the first byte (presents
+	// a case where number of bytes to read is zero); add PEC if applicable
+	block_zero_bytes = 1;
+	if (bus->PEC_use)
+		block_zero_bytes++;
+
+	fifo_bytes = FIELD_GET(NPCM_SMBRXF_CTL_RX_THR,
+			       ioread8(bus->reg + NPCM_SMBRXF_CTL));
+
+	// Perform master read, distinguishing between last byte and the rest of
+	// the bytes. The last byte should be read when the clock is stopped
+	if ((bus->rd_ind < (bus->rd_size - 1)) ||  bus->fifo_use) {
+		u8 data;
+
+		// byte to be read is not the last one
+		// Check if byte-before-last is about to be read
+		if ((bus->rd_ind == (bus->rd_size - 2)) &&
+		    !bus->fifo_use){
+			// Set nack before reading byte-before-last, so that
+			// nack will be generated after receive of last byte
+			npcm_smb_nack(bus);
+
+			if (!FIELD_GET(NPCM_SMBST_SDAST,
+				       ioread8(bus->reg + NPCM_SMBST))) {
+				// No data available - reset state for new xfer
+				bus->state = SMB_IDLE;
+
+				// Notify upper layer of rx completion
+				npcm_smb_callback(bus, SMB_NO_DATA_IND,
+						  bus->rd_ind);
+			}
+		} else if (bus->rd_ind == 0) { //first byte handling:
+			// in block protocol first byte is the size
+			if (bus->read_block_use) {
+				npcm_smb_rd_byte(bus, &data);
+
+				// First byte indicates length in block protocol
+				bus->rd_buf[bus->rd_ind++] = data;
+				bus->rd_size = data + 1;
+
+				if (bus->PEC_use) {
+					bus->rd_size += 1;
+					data += 1;
+				}
+
+				if (bus->fifo_use) {
+					iowrite8(NPCM_SMBFIF_CTS_RXF_TXE |
+						 ioread8(bus->reg +
+							 NPCM_SMBFIF_CTS),
+						 bus->reg + NPCM_SMBFIF_CTS);
+
+					// first byte in block protocol
+					// is zero -> not supported. read at
+					// least one byte
+					if (data == 0)
+						data = 1;
+				}
+				npcm_smb_set_fifo(bus, bus->rd_size, -1);
+			} else {
+				if (!bus->fifo_use) {
+					npcm_smb_rd_byte(bus, &data);
+					bus->rd_buf[bus->rd_ind++] = data;
+				} else {
+					npcm_smb_clear_tx_fifo(bus);
+					npcm_smb_master_fifo_read(bus);
+				}
+			}
+
+		} else {
+			if (bus->fifo_use) {
+				if (bus->rd_size == block_zero_bytes &&
+				    bus->read_block_use) {
+					npcm_smb_eob_int(bus, true);
+					npcm_smb_master_stop(bus);
+					npcm_smb_read_from_fifo(bus,
+								fifo_bytes);
+					bus->state = SMB_STOP_PENDING;
+					bus->stop_ind = SMB_BLOCK_BYTES_ERR_IND;
+
+				} else {
+					npcm_smb_master_fifo_read(bus);
+				}
+			} else {
+				npcm_smb_rd_byte(bus, &data);
+				bus->rd_buf[bus->rd_ind++] = data;
+			}
+		}
+	} else {
+		// last byte is about to be read - end of transaction.
+		// Stop should be set before reading last byte.
+		u8 data;
+		enum smb_state_ind ind = SMB_MASTER_DONE_IND;
+
+		npcm_smb_eob_int(bus, true);
+
+		npcm_smb_master_stop(bus);
+
+		npcm_smb_rd_byte(bus, &data);
+
+		if (bus->rd_size == block_zero_bytes && bus->read_block_use) {
+			ind = SMB_BLOCK_BYTES_ERR_IND;
+		} else {
+			bus->rd_buf[bus->rd_ind++] = data;
+			if (npcm_smb_get_PEC(bus) != 0)
+				ind = SMB_MASTER_PEC_ERR_IND;
+		}
+
+		bus->state = SMB_STOP_PENDING;
+		bus->stop_ind = ind;
+	}
+}
+
+static void npcm_smb_int_master_handler(struct npcm_i2c *bus)
+{
+	// A negative acknowledge has occurred
+	if (FIELD_GET(NPCM_SMBST_NEGACK, ioread8(bus->reg + NPCM_SMBST))) {
+		NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_NACK);
+		if (bus->fifo_use) {
+			// if there are still untransmitted bytes in TX FIFO
+			// reduce them from wr_ind
+
+			if (bus->operation == SMB_WRITE_OPER)
+				bus->wr_ind -= npcm_smb_get_fifo_fullness(bus);
+			// clear the FIFO
+			iowrite8(NPCM_SMBFIF_CTS_CLR_FIFO,
+				 bus->reg + NPCM_SMBFIF_CTS);
+		}
+
+		// In master write operation, NACK is a problem
+		// number of bytes sent to master less than required
+		npcm_smb_master_abort(bus);
+		bus->state = SMB_IDLE;
+
+		// In Master mode, NEGACK should be cleared only after
+		// generating STOP.
+		// In such case, the bus is released from stall only after the
+		// software clears NEGACK bit.
+		// Then a Stop condition is sent.
+		iowrite8(NPCM_SMBST_NEGACK, bus->reg + NPCM_SMBST);
+		npcm_smb_callback(bus, SMB_NACK_IND, bus->wr_ind);
+		return;
+	}
+
+	// Master mode: a Bus Error has been identified
+	if (FIELD_GET(NPCM_SMBST_BER, ioread8(bus->reg + NPCM_SMBST))) {
+		// Check whether bus arbitration or Start or Stop during data
+		// xfer bus arbitration problem should not result in recovery
+		if (npcm_smb_is_master(bus)) {
+			// Only current master is allowed to issue stop
+			npcm_smb_master_abort(bus);
+		} else {
+			// Bus arbitration loss
+			if (bus->retry_count-- > 0) {
+				// Perform a retry (generate a start condition)
+				// as soon as the SMBus is free
+				iowrite8(NPCM_SMBST_BER, bus->reg + NPCM_SMBST);
+				npcm_smb_master_start(bus);
+				return;
+			}
+		}
+		iowrite8(NPCM_SMBST_BER, bus->reg + NPCM_SMBST);
+		bus->state = SMB_IDLE;
+		npcm_smb_callback(bus, SMB_BUS_ERR_IND,
+				  npcm_smb_get_index(bus));
+		return;
+	}
+
+	// A Master End of Busy (meaning Stop Condition happened)
+	// End of Busy int is on and End of Busy is set
+	if ((FIELD_GET(NPCM_SMBCTL1_EOBINTE,
+		       ioread8(bus->reg + NPCM_SMBCTL1)) == 1) &&
+	    (FIELD_GET(NPCM_SMBCST3_EO_BUSY,
+		       ioread8(bus->reg + NPCM_SMBCST3)))) {
+		NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_EOB);
+		npcm_smb_eob_int(bus, false);
+		bus->state = SMB_IDLE;
+		if (npcm_smb_is_quick(bus))
+			npcm_smb_callback(bus, bus->stop_ind, 0);
+		else
+			npcm_smb_callback(bus, bus->stop_ind, bus->rd_ind);
+		return;
+	}
+
+	// Address sent and requested stall occurred (Master mode)
+	if (FIELD_GET(NPCM_SMBST_STASTR, ioread8(bus->reg + NPCM_SMBST))) {
+		// Check for Quick Command SMBus protocol
+		if (npcm_smb_is_quick(bus)) {
+			npcm_smb_eob_int(bus, true);
+			npcm_smb_master_stop(bus);
+
+			// Update status
+			bus->state = SMB_STOP_PENDING;
+			bus->stop_ind = SMB_MASTER_DONE_IND;
+
+		} else if (bus->rd_size == 1) {
+			// Receiving one byte only - set NACK after ensuring
+			// slave ACKed the address byte
+			npcm_smb_nack(bus);
+		}
+
+		// Reset stall-after-address-byte
+		npcm_smb_stall_after_start(bus, false);
+
+		// Clear stall only after setting STOP
+		iowrite8(NPCM_SMBST_STASTR, bus->reg + NPCM_SMBST);
+
+		return;
+	}
+
+	// SDA status is set - transmit or receive, master
+	if (FIELD_GET(NPCM_SMBST_SDAST, ioread8(bus->reg + NPCM_SMBST)) ||
+	    (bus->fifo_use &&
+	    (npcm_smb_tx_fifo_full(bus) || npcm_smb_rx_fifo_full(bus)))) {
+		// Status Bit is cleared by writing to or reading from SDA
+		// (depending on current direction)
+		switch (bus->state) {
+		// Handle unsuccessful bus mastership
+		case SMB_IDLE:
+			npcm_smb_master_abort(bus);
+			return;
+
+		case SMB_MASTER_START:
+			if (npcm_smb_is_master(bus)) {
+				u8 addr_byte = bus->dest_addr;
+
+				bus->crc_data = 0;
+				if (npcm_smb_is_quick(bus)) {
+					// Need to stall after successful
+					// completion of sending address byte
+					npcm_smb_stall_after_start(bus, true);
+				} else if (bus->wr_size == 0) {
+					// Set direction to Read
+					addr_byte |= (u8)0x1;
+					bus->operation = SMB_READ_OPER;
+				} else {
+					bus->operation = SMB_WRITE_OPER;
+				}
+
+	// Receiving one byte only - stall after successful completion of
+	// sending address byte. If we NACK here, and slave doesn't ACK the
+	// address, we might unintentionally NACK the next multi-byte read
+				if (bus->wr_size == 0 && bus->rd_size == 1)
+					npcm_smb_stall_after_start(bus, true);
+
+				// Write the address to the bus
+				bus->state = SMB_OPER_STARTED;
+				npcm_smb_wr_byte(bus, addr_byte);
+			} else {
+				dev_err(bus->dev,
+					"SDA, bus%d is not master, wr %d 0x%x...\n",
+					bus->num, bus->wr_size,
+					bus->wr_buf[0]);
+			}
+			break;
+
+		// SDA status is set - transmit or receive: Handle master mode
+		case SMB_OPER_STARTED:
+			if (bus->operation == SMB_WRITE_OPER)
+				npcm_smb_int_master_handler_write(bus);
+			else if (bus->operation == SMB_READ_OPER)
+				npcm_smb_int_master_handler_read(bus);
+			else
+				pr_err("I2C%d: unknown operation\n", bus->num);
+			break;
+		default:
+			dev_err(bus->dev, "i2c%d master sda err on state machine\n",
+				bus->num);
+		}
+	}
+}
+
+static int npcm_smb_recovery(struct i2c_adapter *_adap)
+{
+	u8   iter = 27;	  // Allow one byte to be sent by the Slave
+	u16  timeout;
+	bool done = false;
+	struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
+
+	dev_info(bus->dev, "recovery bus%d\n", bus->num);
+
+	might_sleep();
+
+	// Disable int
+	npcm_smb_int_enable(bus, false);
+
+	// Check If the SDA line is active (low)
+	if (FIELD_GET(NPCM_SMBCST_TSDA, ioread8(bus->reg + NPCM_SMBCST)) == 0) {
+		// Repeat the following sequence until SDA is released
+		do {
+			// Issue a single SCL cycle
+			iowrite8(NPCM_SMBCST_TGSCL, bus->reg + NPCM_SMBCST);
+			timeout = ABORT_TIMEOUT;
+			while (timeout != 0 &&
+			       FIELD_GET(NPCM_SMBCST_TGSCL,
+					 ioread8(bus->reg + NPCM_SMBCST) == 0))
+				timeout--;
+
+			// If SDA line is inactive (high), stop
+			if (FIELD_GET(NPCM_SMBCST_TSDA,
+				      ioread8(bus->reg + NPCM_SMBCST)) == 1)
+				done = true;
+		} while ((!done) && (--iter != 0));
+
+		// If SDA line is released (high)
+		if (done) {
+			// Clear BB (BUS BUSY) bit
+			iowrite8(NPCM_SMBCST_BB, bus->reg + NPCM_SMBCST);
+
+			// Generate a START, to synchronize Master and Slave
+			npcm_smb_master_start(bus);
+
+			// Wait until START condition is sent, or timeout
+			timeout = ABORT_TIMEOUT;
+			while (timeout != 0 && !npcm_smb_is_master(bus))
+				timeout--;
+
+			// If START condition was sent
+			if (timeout > 0) {
+				// Send an address byte
+				npcm_smb_wr_byte(bus, bus->dest_addr);
+
+				// Generate a STOP condition
+				npcm_smb_master_stop(bus);
+			}
+			return 0;
+		}
+	}
+
+	// check if success:
+	if (npcm_smb_get_SCL(_adap) == 1 && npcm_smb_get_SDA(_adap) == 1)
+		goto npcm_smb_recovery_done;
+
+	// hold clock low for 35ms: 25 and some spair:
+	npcm_smb_set_SCL(_adap, 0);
+	usleep_range(35000, 40000);
+	npcm_smb_set_SCL(_adap, 1);
+	usleep_range(1000, 2000);
+
+	// check if success:
+	if (npcm_smb_get_SCL(_adap) == 1 && npcm_smb_get_SDA(_adap) == 1)
+		goto npcm_smb_recovery_done;
+
+	return 0;
+
+npcm_smb_recovery_done:
+
+	npcm_smb_int_enable(bus, true);
+	return -(ENOTRECOVERABLE);
+}
+
+static bool npcm_smb_init_clk(struct npcm_i2c *bus, enum smb_mode mode,
+			      u32 bus_freq)
+{
+	u32  k1 = 0;
+	u32  k2 = 0;
+	u8   dbnct = 0;
+	u32  sclfrq = 0;
+	u8   hldt = 7;
+	bool fast_mode = false;
+	u32  src_clk_freq; // in KHz
+
+	src_clk_freq = bus->apb_clk / 1000;
+
+	if (bus_freq <= SMBUS_FREQ_100KHZ) {
+		sclfrq = src_clk_freq / (bus_freq * 4);
+
+		if (sclfrq < SCLFRQ_MIN || sclfrq > SCLFRQ_MAX)
+			return false;
+
+		if (src_clk_freq >= 40000)
+			hldt = 17;
+		else if (src_clk_freq >= 12500)
+			hldt = 15;
+		else
+			hldt = 7;
+	}
+
+	else if (bus_freq == SMBUS_FREQ_400KHZ) {
+		sclfrq = 0;
+		fast_mode = true;
+
+		if ((mode == SMB_MASTER && src_clk_freq < 7500) ||
+		    (mode == SMB_SLAVE && src_clk_freq < 10000))
+		  // 400KHZ cannot be supported for master core clock < 7.5 MHZ
+		  // or slave core clock < 10 MHZ
+			return false;
+
+		// Master or Slave with frequency > 25 MHZ
+		if (mode == SMB_MASTER || src_clk_freq > 25000) {
+			hldt = (u8)__KERNEL_DIV_ROUND_UP(src_clk_freq * 300,
+							 1000000) + 7;
+			if (mode == SMB_MASTER) {
+				k1 = __KERNEL_DIV_ROUND_UP(src_clk_freq * 1600,
+							   1000000);
+				k2 = __KERNEL_DIV_ROUND_UP(src_clk_freq * 900,
+							   1000000);
+				k1 = round_up(k1, 2);
+				k2 = round_up(k2 + 1, 2);
+				if (k1 < SCLFRQ_MIN || k1 > SCLFRQ_MAX ||
+				    k2 < SCLFRQ_MIN || k2 > SCLFRQ_MAX)
+					return false;
+			}
+		} else { // Slave with frequency 10-25 MHZ
+			hldt = 7;
+			dbnct = 2;
+		}
+	}
+
+	else if (bus_freq == SMBUS_FREQ_1MHZ) {
+		sclfrq = 0;
+		fast_mode = true;
+
+		if ((mode == SMB_MASTER && src_clk_freq < 15000) ||
+		    (mode == SMB_SLAVE	&& src_clk_freq < 24000))
+		// 1MHZ cannot be supported for master core clock < 15 MHZ
+		// or slave core clock < 24 MHZ
+			return false;
+
+		if (mode == SMB_MASTER) {
+			k1 = round_up((__KERNEL_DIV_ROUND_UP(src_clk_freq * 620,
+							     1000000)), 2);
+			k2 = round_up((__KERNEL_DIV_ROUND_UP(src_clk_freq * 380,
+							     1000000) + 1), 2);
+			if (k1 < SCLFRQ_MIN || k1 > SCLFRQ_MAX ||
+			    k2 < SCLFRQ_MIN || k2 > SCLFRQ_MAX) {
+				return false;
+			}
+		}
+
+		// Master or Slave with frequency > 40 MHZ
+		if (mode == SMB_MASTER || src_clk_freq > 40000) {
+			// Set HLDT:
+			// SDA hold time:  (HLDT-7) * T(CLK) >= 120
+			// HLDT = 120/T(CLK) + 7 = 120 * FREQ(CLK) + 7
+			hldt = (u8)__KERNEL_DIV_ROUND_UP(src_clk_freq * 120,
+							 1000000) + 7;
+
+		// Slave with frequency 24-40 MHZ
+		} else {
+			hldt = 7;
+			dbnct = 2;
+		}
+	}
+
+	// Frequency larger than 1 MHZ
+	else
+		return false;
+
+	// After clock parameters calculation update the reg
+	iowrite8((ioread8(bus->reg + NPCM_SMBCTL2)
+		& ~SMBCTL2_SCLFRQ6_0) | FIELD_PREP(SMBCTL2_SCLFRQ6_0,
+		sclfrq & 0x7F), bus->reg + NPCM_SMBCTL2);
+
+	iowrite8((ioread8(bus->reg + NPCM_SMBCTL3) & ~SMBCTL3_SCLFRQ8_7) |
+		 FIELD_PREP(SMBCTL3_SCLFRQ8_7, (sclfrq >> 7) & 0x3),
+		 bus->reg + NPCM_SMBCTL3);
+
+	iowrite8((ioread8(bus->reg + NPCM_SMBCTL3) & ~SMBCTL3_400K_MODE) |
+		 FIELD_PREP(SMBCTL3_400K_MODE, fast_mode),
+		 bus->reg + NPCM_SMBCTL3);
+
+	// Select Bank 0 to access NPCM_SMBCTL4/NPCM_SMBCTL5
+	npcm_smb_select_bank(bus, SMB_BANK_0);
+
+	if (bus_freq >= SMBUS_FREQ_400KHZ) {
+		// k1 and k2 are relevant for master mode only
+		if (mode == SMB_MASTER) {
+			// Set SCL Low/High Time:
+			// k1 = 2 * SCLLT7-0 -> Low Time  = k1 / 2
+			// k2 = 2 * SCLLT7-0 -> High Time = k2 / 2
+			iowrite8((u8)k1 / 2, bus->reg + NPCM_SMBSCLLT);
+			iowrite8((u8)k2 / 2, bus->reg + NPCM_SMBSCLHT);
+		} else { // DBNCT is relevant for slave mode only
+			iowrite8((ioread8(bus->reg + NPCM_SMBCTL5) &
+				 ~SMBCTL5_DBNCT) |
+				 FIELD_PREP(SMBCTL5_DBNCT, dbnct),
+				 bus->reg + NPCM_SMBCTL5);
+		}
+	}
+
+	iowrite8((ioread8(bus->reg + NPCM_SMBCTL4) & ~SMBCTL4_HLDT)
+		 | FIELD_PREP(SMBCTL4_HLDT, hldt), bus->reg + NPCM_SMBCTL4);
+
+	// Return to Bank 1, and stay there by default:
+	npcm_smb_select_bank(bus, SMB_BANK_1);
+
+	dev_dbg(bus->dev, "k1 = %d k2 = %d dbnct = %d sclfrq = %d hldt = %d src_clk_freq %d fast_mode %d\n",
+		k1, k2, dbnct, sclfrq, hldt, src_clk_freq, fast_mode);
+
+	return true;
+}
+
+static bool npcm_smb_init_module(struct npcm_i2c *bus, enum smb_mode mode,
+				 u32 bus_freq)
+{
+	// Check whether module already enabled or frequency is out of bounds
+	if ((bus->state != SMB_DISABLE && bus->state != SMB_IDLE) ||
+	    bus_freq < SMBUS_FREQ_MIN || bus_freq > SMBUS_FREQ_MAX)
+		return false;
+	// Configure FIFO disabled mode so slave will not use fifo
+	// (maste will set it on if supported)
+	bus->threshold_fifo = SMBUS_FIFO_SIZE;
+	iowrite8(ioread8(bus->reg + NPCM_SMBFIF_CTL) & ~NPCM_SMBFIF_CTL_FIFO_EN,
+		 bus->reg + NPCM_SMBFIF_CTL);
+
+	bus->fifo_use = false;
+
+	// Configure SMB module clock frequency
+	if (!npcm_smb_init_clk(bus, mode, bus_freq)) {
+		pr_err("npcm_smb_init_clk failed\n");
+		return false;
+	}
+	npcm_smb_disable(bus);
+
+	// Enable module (before configuring CTL1)
+	npcm_smb_enable(bus);
+	bus->state = SMB_IDLE;
+
+	// Enable SMB int and New Address Match int source
+	iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) | NPCM_SMBCTL1_NMINTE) &
+		 ~NPCM_SMBCTL1_RWS_FIELDS,
+		 bus->reg + NPCM_SMBCTL1);
+
+	npcm_smb_int_enable(bus, true);
+	return true;
+}
+
+static int __npcm_i2c_init(struct npcm_i2c *bus, struct platform_device *pdev)
+{
+	u32 clk_freq;
+	int ret;
+
+	// Initialize the internal data structures
+	bus->state = SMB_DISABLE;
+	bus->master_or_slave = SMB_SLAVE;
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+				   "bus-frequency", &clk_freq);
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+			"Could not read bus-frequency property\n");
+		clk_freq = 100000;
+	}
+	ret = npcm_smb_init_module(bus, SMB_MASTER, clk_freq / 1000);
+	if (!ret) {
+		dev_err(&pdev->dev,
+			"npcm_smb_init_module() failed\n");
+		return -1;
+	}
+
+	crc8_populate_lsb(npcm7xx_crc8, 0x07);
+	crc8_populate_msb(npcm7xx_crc8, 0x07);
+	return 0;
+}
+
+static irqreturn_t npcm_i2c_bus_irq(int irq, void *dev_id)
+{
+	struct npcm_i2c *bus = dev_id;
+
+	bus->int_cnt++;
+	_npcm7xx_get_time_stamp(&bus->int_time_stamp[0],
+				&bus->int_time_stamp[1]);
+	if (bus->master_or_slave == SMB_MASTER)	{
+		npcm_smb_int_master_handler(bus);
+		return IRQ_HANDLED;
+	}
+
+	dev_err(bus->dev, "int unknown on bus%d\n", bus->num);
+	return IRQ_NONE;
+}
+
+static bool npcm_smb_master_start_xmit(struct npcm_i2c *bus,
+				       u8 slave_addr, u16 nwrite, u16 nread,
+				       u8 *write_data, u8 *read_data,
+				       bool use_PEC)
+{
+	//
+	// Allow only if bus is not busy
+	//
+	if (bus->state != SMB_IDLE) {
+		dev_info(bus->dev, "\tbus%d->state != SMB_IDLE\n", bus->num);
+		return false;
+	}
+
+	// Configure FIFO mode :
+	if (FIELD_GET(SMB_VER_FIFO_EN, ioread8(bus->reg + SMB_VER))) {
+		bus->fifo_use = true;
+		iowrite8(ioread8(bus->reg + NPCM_SMBFIF_CTL) |
+			 NPCM_SMBFIF_CTL_FIFO_EN, bus->reg + NPCM_SMBFIF_CTL);
+	} else {
+		bus->fifo_use = false;
+	}
+
+	// Update driver state
+	bus->master_or_slave = SMB_MASTER;
+	bus->state = SMB_MASTER_START;
+	if (nwrite > 0)
+		bus->operation = SMB_WRITE_OPER;
+	else
+		bus->operation = SMB_READ_OPER;
+
+	if (npcm_smb_is_quick(bus))
+		bus->operation = SMB_WRITE_OPER; // send the address with W bit.
+
+	bus->dest_addr = (u8)(slave_addr << 1);// Translate 7bit to 8bit format
+	bus->wr_buf = write_data;
+	bus->wr_size = nwrite;
+	bus->wr_ind = 0;
+	bus->rd_buf = read_data;
+	bus->rd_size = nread;
+	bus->rd_ind = 0;
+	bus->PEC_use = use_PEC;
+	bus->retry_count = SMB_RETRY_MAX_COUNT;
+
+	// clear BER just in case it is set due to a previous transaction
+	iowrite8(NPCM_SMBST_BER, bus->reg + NPCM_SMBST);
+
+	// Initiate SMBus master transaction
+	// Generate a Start condition on the SMBus
+	if (bus->fifo_use) {
+		// select bank 1 for FIFO regs
+		npcm_smb_select_bank(bus, SMB_BANK_1);
+
+		// clear FIFO and relevant status bits.
+		iowrite8(ioread8(bus->reg + NPCM_SMBFIF_CTS) |
+			 NPCM_SMBFIF_CTS_SLVRSTR |
+			 NPCM_SMBFIF_CTS_CLR_FIFO |
+			 NPCM_SMBFIF_CTS_RXF_TXE, bus->reg + NPCM_SMBFIF_CTS);
+
+		if (bus->operation == SMB_READ_OPER) {
+			//This is a read only operation. Configure the FIFO
+			//threshold according to the needed # of bytes to read.
+			npcm_smb_set_fifo(bus, nread, -1);
+		} else if (bus->operation == SMB_WRITE_OPER) {
+			npcm_smb_set_fifo(bus, -1, nwrite);
+		}
+	}
+
+	bus->int_cnt = 0;
+	bus->event_log = 0;
+	npcm_smb_master_start(bus);
+
+	return true;
+}
+
+static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+				int num)
+{
+	struct npcm_i2c *bus = adap->algo_data;
+	struct i2c_msg *msg0, *msg1;
+	unsigned long time_left, flags;
+	u16 nwrite, nread;
+	u8 *write_data, *read_data;
+	u8 slave_addr;
+	int ret = 0;
+
+	spin_lock_irqsave(&bus->lock, flags);
+	bus->cmd_err = -EPERM;
+	bus->int_cnt = 0;
+	bus->stop_ind = SMB_NO_STATUS_IND;
+	bus->read_block_use = false;
+
+	iowrite8(0xFF, bus->reg + NPCM_SMBST);
+
+	if (num > 2 || num < 1) {
+		pr_err("I2C command not supported, num of msgs = %d\n", num);
+		spin_unlock_irqrestore(&bus->lock, flags);
+		return -EINVAL;
+	}
+
+	msg0 = &msgs[0];
+	slave_addr = msg0->addr;
+	if (msg0->flags & I2C_M_RD) { // read
+		if (num == 2) {
+			pr_err(" num = 2 but first msg is rd instead of wr\n");
+			spin_unlock_irqrestore(&bus->lock, flags);
+			return -EINVAL;
+		}
+		nwrite = 0;
+		write_data = NULL;
+		if (msg0->flags & I2C_M_RECV_LEN) {
+			nread = 1;
+			bus->read_block_use = true;
+
+		} else {
+			nread = msg0->len;
+		}
+		read_data = msg0->buf;
+
+	} else { // write
+		nwrite = msg0->len;
+		write_data = msg0->buf;
+		nread = 0;
+		read_data = NULL;
+		if (num == 2) {
+			msg1 = &msgs[1];
+			if (slave_addr != msg1->addr) {
+				pr_err("SA==%02x but msg1->addr == %02x\n",
+				       slave_addr, msg1->addr);
+				spin_unlock_irqrestore(&bus->lock, flags);
+				return -EINVAL;
+			}
+			if ((msg1->flags & I2C_M_RD) == 0) {
+				pr_err("num = 2 but both msg are write.\n");
+				spin_unlock_irqrestore(&bus->lock, flags);
+				return -EINVAL;
+			}
+			if (msg1->flags & I2C_M_RECV_LEN) {
+				nread = 1;
+				bus->read_block_use = true;
+			} else {
+				nread = msg1->len;
+				bus->read_block_use = false;
+			}
+
+			read_data = msg1->buf;
+		}
+	}
+
+	bus->msgs = msgs;
+	bus->msgs_num = num;
+
+	if (nwrite >= 32 * 1024 ||  nread >= 32 * 1024) {
+		pr_err("i2c%d buffer too big\n", bus->num);
+		return -EINVAL;
+	}
+
+	reinit_completion(&bus->cmd_complete);
+
+	if (npcm_smb_master_start_xmit(bus, slave_addr, nwrite, nread,
+				       write_data, read_data, 0) == false)
+		ret = -(EBUSY);
+
+	if (ret != -(EBUSY)) {
+		time_left = wait_for_completion_timeout(&bus->cmd_complete,
+							bus->adap.timeout);
+
+		if (time_left == 0 && bus->cmd_err == -EPERM) {
+			npcm_smb_master_abort(bus);
+			ret = -ETIMEDOUT;
+		} else {
+			ret = bus->cmd_err;
+		}
+	}
+
+	bus->msgs = NULL;
+	bus->msgs_num = 0;
+	spin_unlock_irqrestore(&bus->lock, flags);
+
+	// If nothing went wrong, return number of messages xferred.
+	if (ret >= 0)
+		return num;
+	else
+		return ret;
+}
+
+static u32 npcm_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static const struct i2c_algorithm npcm_i2c_algo = {
+	.master_xfer = npcm_i2c_master_xfer,
+	.functionality = npcm_i2c_functionality,
+};
+
+static struct i2c_bus_recovery_info npcm_i2c_recovery = {
+	.recover_bus = npcm_smb_recovery,
+	.get_scl = npcm_smb_get_SCL,
+	.set_scl = npcm_smb_set_SCL,
+	.get_sda = npcm_smb_get_SDA,
+};
+
+static int  npcm_i2c_probe_bus(struct platform_device *pdev)
+{
+	struct npcm_i2c *bus;
+	struct resource *res;
+	struct clk *i2c_clk;
+	int ret;
+	int num;
+
+	bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
+	if (!bus)
+		return -ENOMEM;
+
+#ifdef CONFIG_OF
+	num = of_alias_get_id(pdev->dev.of_node, "i2c");
+	bus->num = num;
+	i2c_clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(i2c_clk)) {
+		pr_err(" I2C probe failed: can't read clk.\n");
+		return	-EPROBE_DEFER;
+	}
+	bus->apb_clk = clk_get_rate(i2c_clk);
+	dev_dbg(bus->dev, "I2C APB clock is %d\n", bus->apb_clk);
+#endif //  CONFIG_OF
+
+	gcr_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr");
+	if (IS_ERR(gcr_regmap)) {
+		pr_err("%s: failed to find nuvoton,npcm750-gcr\n", __func__);
+		return IS_ERR(gcr_regmap);
+	}
+	regmap_write(gcr_regmap, NPCM_I2CSEGCTL, I2CSEGCTL_VAL);
+	dev_dbg(bus->dev, "I2C%d: gcr mapped\n", bus->num);
+
+	clk_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-clk");
+	if (IS_ERR(clk_regmap)) {
+		pr_err("%s: failed to find nuvoton,npcm750-clk\n", __func__);
+		return IS_ERR(clk_regmap);
+	}
+	dev_dbg(bus->dev, "I2C%d: clk mapped\n", bus->num);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dev_dbg(bus->dev, "resource: %pR\n", res);
+	bus->reg = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR((bus)->reg))
+		return PTR_ERR((bus)->reg);
+	dev_dbg(bus->dev, "base = %p\n", bus->reg);
+
+	// Initialize the I2C adapter
+	spin_lock_init(&bus->lock);
+	init_completion(&bus->cmd_complete);
+	bus->adap.owner = THIS_MODULE;
+	bus->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+	bus->adap.retries = 0;
+	bus->adap.timeout = 500 * HZ / 1000;
+	bus->adap.algo = &npcm_i2c_algo;
+	bus->adap.algo_data = bus;
+	bus->adap.dev.parent = &pdev->dev;
+	bus->adap.dev.of_node = pdev->dev.of_node;
+	bus->adap.bus_recovery_info = &npcm_i2c_recovery;
+
+	snprintf(bus->adap.name, sizeof(bus->adap.name), "Nuvoton i2c");
+
+	bus->dev = &pdev->dev;
+
+	ret = __npcm_i2c_init(bus, pdev);
+	if (ret < 0)
+		return ret;
+
+	bus->irq = platform_get_irq(pdev, 0);
+	if (bus->irq < 0) {
+		pr_err("I2C platform_get_irq error.");
+		return -ENODEV;
+	}
+	dev_dbg(bus->dev, "irq = %d\n", bus->irq);
+
+	ret = request_irq(bus->irq, npcm_i2c_bus_irq, 0,
+			  dev_name(&pdev->dev), (void *)bus);
+	if (ret) {
+		dev_err(&pdev->dev, "I2C%d: request_irq fail\n", bus->num);
+		return ret;
+	}
+
+	ret = i2c_add_adapter(&bus->adap);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "I2C%d: i2c_add_adapter fail\n", bus->num);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, bus);
+	pr_info("i2c bus %d registered\n", bus->adap.nr);
+
+	return 0;
+}
+
+static int  npcm_i2c_remove_bus(struct platform_device *pdev)
+{
+	unsigned long lock_flags;
+	struct npcm_i2c *bus = platform_get_drvdata(pdev);
+
+	spin_lock_irqsave(&bus->lock, lock_flags);
+	npcm_smb_disable(bus);
+	spin_unlock_irqrestore(&bus->lock, lock_flags);
+	i2c_del_adapter(&bus->adap);
+
+	return 0;
+}
+
+static const struct of_device_id npcm_i2c_bus_of_table[] = {
+	{ .compatible = "nuvoton,npcm750-i2c", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, npcm_i2c_bus_of_table);
+
+static struct platform_driver npcm_i2c_bus_driver = {
+	.probe = npcm_i2c_probe_bus,
+	.remove = npcm_i2c_remove_bus,
+	.driver = {
+		.name = "nuvoton-i2c",
+		.of_match_table = npcm_i2c_bus_of_table,
+	}
+};
+module_platform_driver(npcm_i2c_bus_driver);
+
+MODULE_AUTHOR("Avi Fishman <avi.fishman@gmail.com>");
+MODULE_AUTHOR("Tali Perry <tali.perry@nuvoton.com>");
+MODULE_DESCRIPTION("Nuvoton I2C Bus Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(I2C_VERSION);
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 7a3ca4e..2d1e70a 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -576,6 +576,16 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called nau7802.
 
+config NPCM_ADC
+	tristate "Nuvoton NPCM ADC driver"
+	depends on ARCH_NPCM || COMPILE_TEST
+	depends on HAS_IOMEM
+	help
+	  Say yes here to build support for Nuvoton NPCM ADC.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called npcm_adc.
+
 config PALMAS_GPADC
 	tristate "TI Palmas General Purpose ADC"
 	depends on MFD_PALMAS
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 07df37f..3337eb1 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -55,6 +55,7 @@
 obj-$(CONFIG_MESON_SARADC) += meson_saradc.o
 obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
 obj-$(CONFIG_NAU7802) += nau7802.o
+obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
 obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
 obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o
 obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c
new file mode 100644
index 0000000..1cc377c
--- /dev/null
+++ b/drivers/iio/adc/npcm_adc.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Nuvoton Technology corporation.
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/io.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+
+struct npcm_adc {
+	bool int_status;
+	u32 adc_sample_hz;
+	struct device *dev;
+	void __iomem *regs;
+	struct clk *adc_clk;
+	wait_queue_head_t wq;
+	struct regulator *vref;
+	struct regmap *rst_regmap;
+};
+
+/* NPCM7xx reset module */
+#define NPCM7XX_IPSRST1_OFFSET		0x020
+#define NPCM7XX_IPSRST1_ADC_RST		BIT(27)
+
+/* ADC registers */
+#define NPCM_ADCCON	 0x00
+#define NPCM_ADCDATA	 0x04
+
+/* ADCCON Register Bits */
+#define NPCM_ADCCON_ADC_INT_EN		BIT(21)
+#define NPCM_ADCCON_REFSEL		BIT(19)
+#define NPCM_ADCCON_ADC_INT_ST		BIT(18)
+#define NPCM_ADCCON_ADC_EN		BIT(17)
+#define NPCM_ADCCON_ADC_RST		BIT(16)
+#define NPCM_ADCCON_ADC_CONV		BIT(13)
+
+#define NPCM_ADCCON_CH_MASK		GENMASK(27, 24)
+#define NPCM_ADCCON_CH(x)		((x) << 24)
+#define NPCM_ADCCON_DIV_SHIFT		1
+#define NPCM_ADCCON_DIV_MASK		GENMASK(8, 1)
+#define NPCM_ADC_DATA_MASK(x)		((x) & GENMASK(9, 0))
+
+#define NPCM_ADC_ENABLE		(NPCM_ADCCON_ADC_EN | NPCM_ADCCON_ADC_INT_EN)
+
+/* ADC General Definition */
+#define NPCM_RESOLUTION_BITS		10
+#define NPCM_INT_VREF_MV		2000
+
+#define NPCM_ADC_CHAN(ch) {					\
+	.type = IIO_VOLTAGE,					\
+	.indexed = 1,						\
+	.channel = ch,						\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |	\
+				BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
+}
+
+static const struct iio_chan_spec npcm_adc_iio_channels[] = {
+	NPCM_ADC_CHAN(0),
+	NPCM_ADC_CHAN(1),
+	NPCM_ADC_CHAN(2),
+	NPCM_ADC_CHAN(3),
+	NPCM_ADC_CHAN(4),
+	NPCM_ADC_CHAN(5),
+	NPCM_ADC_CHAN(6),
+	NPCM_ADC_CHAN(7),
+};
+
+static irqreturn_t npcm_adc_isr(int irq, void *data)
+{
+	u32 regtemp;
+	struct iio_dev *indio_dev = data;
+	struct npcm_adc *info = iio_priv(indio_dev);
+
+	regtemp = ioread32(info->regs + NPCM_ADCCON);
+	if (regtemp & NPCM_ADCCON_ADC_INT_ST) {
+		iowrite32(regtemp, info->regs + NPCM_ADCCON);
+		wake_up_interruptible(&info->wq);
+		info->int_status = true;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int npcm_adc_read(struct npcm_adc *info, int *val, u8 channel)
+{
+	int ret;
+	u32 regtemp;
+
+	/* Select ADC channel */
+	regtemp = ioread32(info->regs + NPCM_ADCCON);
+	regtemp &= ~NPCM_ADCCON_CH_MASK;
+	info->int_status = false;
+	iowrite32(regtemp | NPCM_ADCCON_CH(channel) |
+		  NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON);
+
+	ret = wait_event_interruptible_timeout(info->wq, info->int_status,
+					       msecs_to_jiffies(10));
+	if (ret == 0) {
+		regtemp = ioread32(info->regs + NPCM_ADCCON);
+		if ((regtemp & NPCM_ADCCON_ADC_CONV) && info->rst_regmap) {
+			/* if conversion failed - reset ADC module */
+			regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET,
+				     NPCM7XX_IPSRST1_ADC_RST);
+			msleep(100);
+			regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET,
+				     0x0);
+			msleep(100);
+
+			/* Enable ADC and start conversion module */
+			iowrite32(NPCM_ADC_ENABLE | NPCM_ADCCON_ADC_CONV,
+				  info->regs + NPCM_ADCCON);
+			dev_err(info->dev, "RESET ADC Complete\n");
+		}
+		return -ETIMEDOUT;
+	}
+	if (ret < 0)
+		return ret;
+
+	*val = NPCM_ADC_DATA_MASK(ioread32(info->regs + NPCM_ADCDATA));
+
+	return 0;
+}
+
+static int npcm_adc_read_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan, int *val,
+			     int *val2, long mask)
+{
+	int ret;
+	int vref_uv;
+	struct npcm_adc *info = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&indio_dev->mlock);
+		ret = npcm_adc_read(info, val, chan->channel);
+		mutex_unlock(&indio_dev->mlock);
+		if (ret) {
+			dev_err(info->dev, "NPCM ADC read failed\n");
+			return ret;
+		}
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		if (info->vref) {
+			vref_uv = regulator_get_voltage(info->vref);
+			*val = vref_uv / 1000;
+		} else {
+			*val = NPCM_INT_VREF_MV;
+		}
+		*val2 = NPCM_RESOLUTION_BITS;
+		return IIO_VAL_FRACTIONAL_LOG2;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*val = info->adc_sample_hz;
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct iio_info npcm_adc_iio_info = {
+	.read_raw = &npcm_adc_read_raw,
+};
+
+static const struct of_device_id npcm_adc_match[] = {
+	{ .compatible = "nuvoton,npcm750-adc", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, npcm_adc_match);
+
+static int npcm_adc_probe(struct platform_device *pdev)
+{
+	int ret;
+	int irq;
+	u32 div;
+	u32 reg_con;
+	struct resource *res;
+	struct npcm_adc *info;
+	struct iio_dev *indio_dev;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = pdev->dev.of_node;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+	if (!indio_dev)
+		return -ENOMEM;
+	info = iio_priv(indio_dev);
+
+	info->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	info->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(info->regs))
+		return PTR_ERR(info->regs);
+
+	info->adc_clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(info->adc_clk)) {
+		dev_warn(&pdev->dev, "ADC clock failed: can't read clk\n");
+		return PTR_ERR(info->adc_clk);
+	}
+
+	/* calculate ADC clock sample rate */
+	reg_con = ioread32(info->regs + NPCM_ADCCON);
+	div = reg_con & NPCM_ADCCON_DIV_MASK;
+	div = div >> NPCM_ADCCON_DIV_SHIFT;
+	info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2);
+
+	if (of_device_is_compatible(np, "nuvoton,npcm750-adc")) {
+		info->rst_regmap = syscon_regmap_lookup_by_compatible
+			("nuvoton,npcm750-rst");
+		if (IS_ERR(info->rst_regmap)) {
+			dev_err(&pdev->dev, "Failed to find nuvoton,npcm750-rst\n");
+			ret = PTR_ERR(info->rst_regmap);
+			goto err_disable_clk;
+		}
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq <= 0) {
+		dev_err(dev, "failed getting interrupt resource\n");
+		ret = -EINVAL;
+		goto err_disable_clk;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq, npcm_adc_isr, 0,
+			       "NPCM_ADC", indio_dev);
+	if (ret < 0) {
+		dev_err(dev, "failed requesting interrupt\n");
+		goto err_disable_clk;
+	}
+
+	reg_con = ioread32(info->regs + NPCM_ADCCON);
+	info->vref = devm_regulator_get_optional(&pdev->dev, "vref");
+	if (!IS_ERR(info->vref)) {
+		ret = regulator_enable(info->vref);
+		if (ret) {
+			dev_err(&pdev->dev, "Can't enable ADC reference voltage\n");
+			goto err_disable_clk;
+		}
+
+		iowrite32(reg_con & ~NPCM_ADCCON_REFSEL,
+			  info->regs + NPCM_ADCCON);
+	} else {
+		/*
+		 * Any error which is not ENODEV indicates the regulator
+		 * has been specified and so is a failure case.
+		 */
+		if (PTR_ERR(info->vref) != -ENODEV) {
+			ret = PTR_ERR(info->vref);
+			goto err_disable_clk;
+		}
+
+		/* Use internal reference */
+		iowrite32(reg_con | NPCM_ADCCON_REFSEL,
+			  info->regs + NPCM_ADCCON);
+	}
+
+	init_waitqueue_head(&info->wq);
+
+	reg_con = ioread32(info->regs + NPCM_ADCCON);
+	reg_con |= NPCM_ADC_ENABLE;
+
+	/* Enable the ADC Module */
+	iowrite32(reg_con, info->regs + NPCM_ADCCON);
+
+	/* Start ADC conversion */
+	iowrite32(reg_con | NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON);
+
+	platform_set_drvdata(pdev, indio_dev);
+	indio_dev->name = dev_name(&pdev->dev);
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->info = &npcm_adc_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = npcm_adc_iio_channels;
+	indio_dev->num_channels = ARRAY_SIZE(npcm_adc_iio_channels);
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Couldn't register the device.\n");
+		goto err_iio_register;
+	}
+
+	pr_info("NPCM ADC driver probed\n");
+
+	return 0;
+
+err_iio_register:
+	iowrite32(reg_con & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
+	if (!IS_ERR(info->vref))
+		regulator_disable(info->vref);
+err_disable_clk:
+	clk_disable_unprepare(info->adc_clk);
+
+	return ret;
+}
+
+static int npcm_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct npcm_adc *info = iio_priv(indio_dev);
+	u32 regtemp;
+
+	regtemp = ioread32(info->regs + NPCM_ADCCON);
+
+	iio_device_unregister(indio_dev);
+	iowrite32(regtemp & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
+	if (!IS_ERR(info->vref))
+		regulator_disable(info->vref);
+	clk_disable_unprepare(info->adc_clk);
+
+	return 0;
+}
+
+static struct platform_driver npcm_adc_driver = {
+	.probe		= npcm_adc_probe,
+	.remove		= npcm_adc_remove,
+	.driver		= {
+		.name	= "npcm_adc",
+		.of_match_table = npcm_adc_match,
+	},
+};
+
+module_platform_driver(npcm_adc_driver);
+
+MODULE_DESCRIPTION("Nuvoton NPCM ADC Driver");
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig
index eaa7cfc..e879a01 100644
--- a/drivers/iio/pressure/Kconfig
+++ b/drivers/iio/pressure/Kconfig
@@ -52,6 +52,17 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called cros_ec_baro.
 
+config DPS310
+       tristate "Infineon DPS310 pressure and temperature sensor"
+       depends on I2C
+       select REGMAP_I2C
+       help
+	 Support for the Infineon DPS310 digital barometric pressure sensor.
+	 This driver measures temperature only.
+
+	 This driver can also be built as a module.  If so, the module will be
+	 called dps310.
+
 config HID_SENSOR_PRESS
 	depends on HID_SENSOR_HUB
 	select IIO_BUFFER
diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile
index c2058d7..d8f5ace 100644
--- a/drivers/iio/pressure/Makefile
+++ b/drivers/iio/pressure/Makefile
@@ -9,6 +9,7 @@
 bmp280-objs := bmp280-core.o bmp280-regmap.o
 obj-$(CONFIG_BMP280_I2C) += bmp280-i2c.o
 obj-$(CONFIG_BMP280_SPI) += bmp280-spi.o
+obj-$(CONFIG_DPS310) += dps310.o
 obj-$(CONFIG_IIO_CROS_EC_BARO) += cros_ec_baro.o
 obj-$(CONFIG_HID_SENSOR_PRESS)   += hid-sensor-press.o
 obj-$(CONFIG_HP03) += hp03.o
diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c
new file mode 100644
index 0000000..beb8b11
--- /dev/null
+++ b/drivers/iio/pressure/dps310.c
@@ -0,0 +1,470 @@
+/*
+ * Copyright 2017 IBM Corporation
+ *
+ * Joel Stanley <joel@jms.id.au>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * The DPS310 is a barometric pressure and temperature sensor.
+ * Currently only reading a single temperature is supported by
+ * this driver.
+ *
+ * https://www.infineon.com/dgdl/?fileId=5546d462576f34750157750826c42242
+ *
+ * Temperature calculation:
+ *   c0 * 0.5 + c1 * T_raw / kT °C
+ *
+ * TODO:
+ *  - Pressure sensor readings
+ *  - Optionally support the FIFO
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define DPS310_PRS_B0		0x00
+#define DPS310_PRS_B1		0x01
+#define DPS310_PRS_B2		0x02
+#define DPS310_TMP_B0		0x03
+#define DPS310_TMP_B1		0x04
+#define DPS310_TMP_B2		0x05
+#define DPS310_PRS_CFG		0x06
+#define DPS310_TMP_CFG		0x07
+#define  DPS310_TMP_RATE_BITS	GENMASK(6, 4)
+#define  DPS310_TMP_PRC_BITS	GENMASK(3, 0)
+#define  DPS310_TMP_EXT		BIT(7)
+#define DPS310_MEAS_CFG		0x08
+#define  DPS310_MEAS_CTRL_BITS	GENMASK(2, 0)
+#define   DPS310_PRESSURE_EN	BIT(0)
+#define   DPS310_TEMP_EN	BIT(1)
+#define   DPS310_BACKGROUND	BIT(2)
+#define  DPS310_PRS_RDY		BIT(4)
+#define  DPS310_TMP_RDY		BIT(5)
+#define  DPS310_SENSOR_RDY	BIT(6)
+#define  DPS310_COEF_RDY	BIT(7)
+#define DPS310_CFG_REG		0x09
+#define  DPS310_INT_HL		BIT(7)
+#define  DPS310_TMP_SHIFT_EN	BIT(3)
+#define  DPS310_PRS_SHIFT_EN	BIT(4)
+#define  DPS310_FIFO_EN		BIT(5)
+#define  DPS310_SPI_EN		BIT(6)
+#define DPS310_RESET		0x0c
+#define  DPS310_RESET_MAGIC	(BIT(0) | BIT(3))
+#define DPS310_COEF_BASE	0x10
+
+#define DPS310_PRS_BASE		DPS310_PRS_B0
+#define DPS310_TMP_BASE		DPS310_TMP_B0
+
+#define DPS310_TMP_RATE(_n)	ilog2(_n)
+#define DPS310_TMP_PRC(_n)	ilog2(_n)
+
+#define MCELSIUS_PER_CELSIUS	1000
+
+const int scale_factor[] = {
+	 524288,
+	1572864,
+	3670016,
+	7864320,
+	 253952,
+	 516096,
+	1040384,
+	2088960,
+};
+
+struct dps310_data {
+	struct i2c_client *client;
+	struct regmap *regmap;
+
+	s32 c0, c1;
+	s32 temp_raw;
+};
+
+static const struct iio_chan_spec dps310_channels[] = {
+	{
+		.type = IIO_TEMP,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_OFFSET) |
+			BIT(IIO_CHAN_INFO_SCALE) |
+			BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
+			BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+			BIT(IIO_CHAN_INFO_RAW),
+	},
+};
+
+/* To be called after checking the TMP_RDY bit in MEAS_CFG */
+static int dps310_get_temp_coef(struct dps310_data *data)
+{
+	struct regmap *regmap = data->regmap;
+	uint8_t coef[3] = {0};
+	int r;
+	u32 c0, c1;
+
+	/*
+	 * Read temperature calibration coefficients c0 and c1 from the
+	 * COEF register. The numbers are 12-bit 2's compliment numbers
+	 */
+	r = regmap_bulk_read(regmap, DPS310_COEF_BASE, coef, 3);
+	if (r < 0)
+		return r;
+
+	c0 = (coef[0] << 4) | (coef[1] >> 4);
+	data->c0 = sign_extend32(c0, 11);
+
+	c1 = ((coef[1] & GENMASK(3, 0)) << 8) | coef[2];
+	data->c1 = sign_extend32(c1, 11);
+
+	return 0;
+}
+
+static int dps310_get_temp_precision(struct dps310_data *data)
+{
+	int val, r;
+
+	r = regmap_read(data->regmap, DPS310_TMP_CFG, &val);
+	if (r < 0)
+		return r;
+
+	/*
+	 * Scale factor is bottom 4 bits of the register, but 1111 is
+	 * reserved so just grab bottom three
+	 */
+	return BIT(val & GENMASK(2, 0));
+}
+
+static int dps310_set_temp_precision(struct dps310_data *data, int val)
+{
+	int ret;
+	u8 shift_en;
+
+	if (val < 0 || val > 128)
+		return -EINVAL;
+
+	shift_en = val >= 16 ? DPS310_TMP_SHIFT_EN : 0;
+	ret = regmap_write_bits(data->regmap, DPS310_CFG_REG,
+			DPS310_TMP_SHIFT_EN,
+			shift_en);
+	if (ret)
+		return ret;
+
+	return regmap_update_bits(data->regmap, DPS310_TMP_CFG,
+			DPS310_TMP_PRC_BITS, DPS310_TMP_PRC(val));
+}
+
+static int dps310_set_temp_samp_freq(struct dps310_data *data, int freq)
+{
+	uint8_t val;
+
+	if (freq < 0 || freq > 128)
+		return -EINVAL;
+
+	val = DPS310_TMP_RATE(freq) << 4;
+
+	return regmap_update_bits(data->regmap, DPS310_TMP_CFG,
+			DPS310_TMP_RATE_BITS, val);
+}
+
+static int dps310_get_temp_samp_freq(struct dps310_data *data)
+{
+	int val, r;
+
+	r = regmap_read(data->regmap, DPS310_TMP_CFG, &val);
+	if (r < 0)
+		return r;
+
+	return BIT((val & DPS310_TMP_RATE_BITS) >> 4);
+}
+
+static int dps310_get_temp_k(struct dps310_data *data)
+{
+	return scale_factor[DPS310_TMP_PRC(dps310_get_temp_precision(data))];
+}
+
+static int dps310_read_temp(struct dps310_data *data)
+{
+	struct device *dev = &data->client->dev;
+	struct regmap *regmap = data->regmap;
+	uint8_t val[3] = {0};
+	int r, ready;
+	int T_raw;
+
+	r = regmap_read(regmap, DPS310_MEAS_CFG, &ready);
+	if (r < 0)
+		return r;
+	if (!(ready & DPS310_TMP_RDY)) {
+		dev_dbg(dev, "temperature not ready\n");
+		return -EAGAIN;
+	}
+
+	r = regmap_bulk_read(regmap, DPS310_TMP_BASE, val, 3);
+	if (r < 0)
+		return r;
+
+	T_raw = (val[0] << 16) | (val[1] << 8) | val[2];
+	data->temp_raw = sign_extend32(T_raw, 23);
+
+	return 0;
+}
+
+static bool dps310_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case DPS310_PRS_CFG:
+	case DPS310_TMP_CFG:
+	case DPS310_MEAS_CFG:
+	case DPS310_CFG_REG:
+	case DPS310_RESET:
+	case 0x0e:
+	case 0x0f:
+	case 0x62:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool dps310_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case DPS310_PRS_B0:
+	case DPS310_PRS_B1:
+	case DPS310_PRS_B2:
+	case DPS310_TMP_B0:
+	case DPS310_TMP_B1:
+	case DPS310_TMP_B2:
+	case DPS310_MEAS_CFG:
+	case 0x32:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static int dps310_write_raw(struct iio_dev *iio,
+			    struct iio_chan_spec const *chan, int val,
+			    int val2, long mask)
+{
+	struct dps310_data *data = iio_priv(iio);
+
+	if (chan->type != IIO_TEMP)
+		return -EINVAL;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		return dps310_set_temp_samp_freq(data, val);
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+		return dps310_set_temp_precision(data, val);
+	default:
+		return -EINVAL;
+	}
+
+	return -EINVAL;
+}
+
+static int dps310_read_raw(struct iio_dev *iio,
+			   struct iio_chan_spec const *chan,
+			   int *val, int *val2, long mask)
+{
+	struct dps310_data *data = iio_priv(iio);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*val = dps310_get_temp_samp_freq(data);
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_RAW:
+		ret = dps310_read_temp(data);
+		if (ret)
+			return ret;
+
+		*val = data->temp_raw * data->c1;
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_OFFSET:
+		*val = (data->c0 >> 1) * dps310_get_temp_k(data);
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SCALE:
+		*val = 1000; /* milliCelsius per Celsius */
+		*val2 = dps310_get_temp_k(data);
+		return IIO_VAL_FRACTIONAL;
+
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+		*val = dps310_get_temp_precision(data);
+		return IIO_VAL_INT;
+
+	default:
+		return -EINVAL;
+	}
+
+	return -EINVAL;
+}
+
+static const struct regmap_config dps310_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.writeable_reg = dps310_is_writeable_reg,
+	.volatile_reg = dps310_is_volatile_reg,
+	.cache_type = REGCACHE_RBTREE,
+	.max_register = 0x62,
+};
+
+static const struct iio_info dps310_info = {
+	.read_raw = dps310_read_raw,
+	.write_raw = dps310_write_raw,
+};
+
+/*
+ * Some verions of chip will read temperatures in the ~60C range when
+ * its acutally ~20C. This is the manufacturer recommended workaround
+ * to correct the issue.
+ */
+static int dps310_temp_workaround(struct dps310_data *data)
+{
+	int r, reg;
+
+	r = regmap_read(data->regmap, 0x32, &reg);
+	if (r < 0)
+		return r;
+
+	/* If bit 1 is set then the device is okay, and the workaround does not
+	 * need to be applied */
+	if (reg & BIT(1))
+		return 0;
+
+	r = regmap_write(data->regmap, 0x0e, 0xA5);
+	if (r < 0)
+		return r;
+
+	r = regmap_write(data->regmap, 0x0f, 0x96);
+	if (r < 0)
+		return r;
+
+	r = regmap_write(data->regmap, 0x62, 0x02);
+	if (r < 0)
+		return r;
+
+	r = regmap_write(data->regmap, 0x0e, 0x00);
+	if (r < 0)
+		return r;
+
+	r = regmap_write(data->regmap, 0x0f, 0x00);
+
+	return r;
+}
+
+static int dps310_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct dps310_data *data;
+	struct iio_dev *iio;
+	int r, ready;
+
+	iio = devm_iio_device_alloc(&client->dev,  sizeof(*data));
+	if (!iio)
+		return -ENOMEM;
+
+	data = iio_priv(iio);
+	data->client = client;
+
+	iio->dev.parent = &client->dev;
+	iio->name = id->name;
+	iio->channels = dps310_channels;
+	iio->num_channels = ARRAY_SIZE(dps310_channels);
+	iio->info = &dps310_info;
+	iio->modes = INDIO_DIRECT_MODE;
+
+	data->regmap = devm_regmap_init_i2c(client, &dps310_regmap_config);
+	if (IS_ERR(data->regmap))
+		return PTR_ERR(data->regmap);
+
+	/*
+	 * Set up external (MEMS) temperature sensor in single sample, one
+	 * measurement per second mode
+	 */
+	r = regmap_write(data->regmap, DPS310_TMP_CFG,
+			DPS310_TMP_EXT | DPS310_TMP_RATE(1) | DPS310_TMP_PRC(1));
+	if (r < 0)
+		return r;
+
+	/* Temp shift is disabled when PRC <= 8 */
+	r = regmap_write_bits(data->regmap, DPS310_CFG_REG,
+			DPS310_TMP_SHIFT_EN, 0);
+	if (r < 0)
+		return r;
+
+	/* Turn on temperature measurement in the background */
+	r = regmap_write_bits(data->regmap, DPS310_MEAS_CFG,
+			DPS310_MEAS_CTRL_BITS,
+			DPS310_TEMP_EN | DPS310_BACKGROUND);
+	if (r < 0)
+		return r;
+
+	/*
+	 * Calibration coefficients required for reporting temperature.
+	 * They are available 40ms after the device has started
+	 */
+	r = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready,
+			ready & DPS310_COEF_RDY,
+			10 * 1000,
+			40 * 1000);
+	if (r < 0)
+		return r;
+
+	r = dps310_get_temp_coef(data);
+	if (r < 0)
+		return r;
+
+	r = dps310_temp_workaround(data);
+	if (r < 0)
+		return r;
+
+	r = devm_iio_device_register(&client->dev, iio);
+	if (r)
+		return r;
+
+	i2c_set_clientdata(client, iio);
+
+	dev_info(&client->dev, "%s: sensor '%s'\n", dev_name(&iio->dev),
+			client->name);
+
+	return 0;
+}
+
+static int dps310_remove(struct i2c_client *client)
+{
+	struct dps310_data *data = i2c_get_clientdata(client);
+
+	return regmap_write(data->regmap, DPS310_RESET, DPS310_RESET_MAGIC);
+}
+
+static const struct i2c_device_id dps310_id[] = {
+	{ "dps310", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, dps310_id);
+
+static const unsigned short normal_i2c[] = {
+	0x77, 0x76, I2C_CLIENT_END
+};
+
+static struct i2c_driver dps310_driver = {
+	.driver = {
+		.name = "dps310",
+	},
+	.probe = dps310_probe,
+	.remove = dps310_remove,
+	.address_list = normal_i2c,
+	.id_table = dps310_id,
+};
+module_i2c_driver(dps310_driver);
+
+MODULE_AUTHOR("Joel Stanley <joel@jms.id.au>");
+MODULE_DESCRIPTION("Infineon DPS310 pressure and temperature sensor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c
index dfec813..1bb863b 100644
--- a/drivers/media/platform/aspeed-video.c
+++ b/drivers/media/platform/aspeed-video.c
@@ -14,7 +14,6 @@
 #include <linux/of_irq.h>
 #include <linux/of_reserved_mem.h>
 #include <linux/platform_device.h>
-#include <linux/reset.h>
 #include <linux/sched.h>
 #include <linux/spinlock.h>
 #include <linux/string.h>
@@ -188,6 +187,7 @@ enum {
 	VIDEO_STREAMING,
 	VIDEO_FRAME_INPRG,
 	VIDEO_STOPPED,
+	VIDEO_CLOCKS_ON,
 };
 
 struct aspeed_video_addr {
@@ -208,7 +208,6 @@ struct aspeed_video {
 	void __iomem *base;
 	struct clk *eclk;
 	struct clk *vclk;
-	struct reset_control *rst;
 
 	struct device *dev;
 	struct v4l2_ctrl_handler ctrl_handler;
@@ -442,7 +441,7 @@ static int aspeed_video_start_frame(struct aspeed_video *video)
 
 	if (!(seq_ctrl & VE_SEQ_CTRL_COMP_BUSY) ||
 	    !(seq_ctrl & VE_SEQ_CTRL_CAP_BUSY)) {
-		dev_err(video->dev, "Engine busy; don't start frame\n");
+		dev_dbg(video->dev, "Engine busy; don't start frame\n");
 		return -EBUSY;
 	}
 
@@ -464,8 +463,7 @@ static int aspeed_video_start_frame(struct aspeed_video *video)
 	aspeed_video_write(video, VE_COMP_ADDR, addr);
 
 	aspeed_video_update(video, VE_INTERRUPT_CTRL, 0,
-			    VE_INTERRUPT_COMP_COMPLETE |
-			    VE_INTERRUPT_CAPTURE_COMPLETE);
+			    VE_INTERRUPT_COMP_COMPLETE);
 
 	aspeed_video_update(video, VE_SEQ_CTRL, 0,
 			    VE_SEQ_CTRL_TRIG_CAPTURE | VE_SEQ_CTRL_TRIG_COMP);
@@ -483,32 +481,32 @@ static void aspeed_video_enable_mode_detect(struct aspeed_video *video)
 	aspeed_video_update(video, VE_SEQ_CTRL, 0, VE_SEQ_CTRL_TRIG_MODE_DET);
 }
 
-static void aspeed_video_reset(struct aspeed_video *video)
-{
-	/* Reset the engine */
-	reset_control_assert(video->rst);
-
-	/* Don't usleep here; function may be called in interrupt context */
-	udelay(100);
-	reset_control_deassert(video->rst);
-}
-
 static void aspeed_video_off(struct aspeed_video *video)
 {
-	aspeed_video_reset(video);
+	if (!test_bit(VIDEO_CLOCKS_ON, &video->flags))
+		return;
+
+	/* Disable interrupts */
+	aspeed_video_write(video, VE_INTERRUPT_CTRL, 0);
+	aspeed_video_write(video, VE_INTERRUPT_STATUS, 0xffffffff);
 
 	/* Turn off the relevant clocks */
-	clk_disable_unprepare(video->vclk);
-	clk_disable_unprepare(video->eclk);
+	clk_disable(video->vclk);
+	clk_disable(video->eclk);
+
+	clear_bit(VIDEO_CLOCKS_ON, &video->flags);
 }
 
 static void aspeed_video_on(struct aspeed_video *video)
 {
-	/* Turn on the relevant clocks */
-	clk_prepare_enable(video->eclk);
-	clk_prepare_enable(video->vclk);
+	if (test_bit(VIDEO_CLOCKS_ON, &video->flags))
+		return;
 
-	aspeed_video_reset(video);
+	/* Turn on the relevant clocks */
+	clk_enable(video->eclk);
+	clk_enable(video->vclk);
+
+	set_bit(VIDEO_CLOCKS_ON, &video->flags);
 }
 
 static void aspeed_video_bufs_done(struct aspeed_video *video,
@@ -526,12 +524,14 @@ static void aspeed_video_bufs_done(struct aspeed_video *video,
 
 static void aspeed_video_irq_res_change(struct aspeed_video *video)
 {
+	spin_lock(&video->lock);
 	dev_dbg(video->dev, "Resolution changed; resetting\n");
 
 	set_bit(VIDEO_RES_CHANGE, &video->flags);
 	clear_bit(VIDEO_FRAME_INPRG, &video->flags);
 
 	aspeed_video_off(video);
+	spin_unlock(&video->lock);
 	aspeed_video_bufs_done(video, VB2_BUF_STATE_ERROR);
 
 	schedule_delayed_work(&video->res_work, RESOLUTION_CHANGE_DELAY);
@@ -557,7 +557,7 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg)
 					    VE_INTERRUPT_MODE_DETECT, 0);
 			aspeed_video_write(video, VE_INTERRUPT_STATUS,
 					   VE_INTERRUPT_MODE_DETECT);
-
+			sts &= ~VE_INTERRUPT_MODE_DETECT;
 			set_bit(VIDEO_MODE_DETECT_DONE, &video->flags);
 			wake_up_interruptible_all(&video->wait);
 		} else {
@@ -570,8 +570,7 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg)
 		}
 	}
 
-	if ((sts & VE_INTERRUPT_COMP_COMPLETE) &&
-	    (sts & VE_INTERRUPT_CAPTURE_COMPLETE)) {
+	if (sts & VE_INTERRUPT_COMP_COMPLETE) {
 		struct aspeed_video_buffer *buf;
 		u32 frame_size = aspeed_video_read(video,
 						   VE_OFFSET_COMP_STREAM);
@@ -600,17 +599,15 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg)
 				    VE_SEQ_CTRL_FORCE_IDLE |
 				    VE_SEQ_CTRL_TRIG_COMP, 0);
 		aspeed_video_update(video, VE_INTERRUPT_CTRL,
-				    VE_INTERRUPT_COMP_COMPLETE |
-				    VE_INTERRUPT_CAPTURE_COMPLETE, 0);
+				    VE_INTERRUPT_COMP_COMPLETE, 0);
 		aspeed_video_write(video, VE_INTERRUPT_STATUS,
-				   VE_INTERRUPT_COMP_COMPLETE |
-				   VE_INTERRUPT_CAPTURE_COMPLETE);
-
+				   VE_INTERRUPT_COMP_COMPLETE);
+		sts &= ~VE_INTERRUPT_COMP_COMPLETE;
 		if (test_bit(VIDEO_STREAMING, &video->flags) && buf)
 			aspeed_video_start_frame(video);
 	}
 
-	return IRQ_HANDLED;
+	return sts ? IRQ_NONE : IRQ_HANDLED;
 }
 
 static void aspeed_video_check_and_set_polarity(struct aspeed_video *video)
@@ -771,7 +768,7 @@ static void aspeed_video_get_resolution(struct aspeed_video *video)
 						      res_check(video),
 						      MODE_DETECT_TIMEOUT);
 		if (!rc) {
-			dev_err(video->dev, "Timed out; first mode detect\n");
+			dev_dbg(video->dev, "Timed out; first mode detect\n");
 			clear_bit(VIDEO_RES_DETECT, &video->flags);
 			return;
 		}
@@ -789,7 +786,7 @@ static void aspeed_video_get_resolution(struct aspeed_video *video)
 						      MODE_DETECT_TIMEOUT);
 		clear_bit(VIDEO_RES_DETECT, &video->flags);
 		if (!rc) {
-			dev_err(video->dev, "Timed out; second mode detect\n");
+			dev_dbg(video->dev, "Timed out; second mode detect\n");
 			return;
 		}
 
@@ -823,7 +820,7 @@ static void aspeed_video_get_resolution(struct aspeed_video *video)
 	} while (invalid_resolution && (tries++ < INVALID_RESOLUTION_RETRIES));
 
 	if (invalid_resolution) {
-		dev_err(video->dev, "Invalid resolution detected\n");
+		dev_dbg(video->dev, "Invalid resolution detected\n");
 		return;
 	}
 
@@ -951,9 +948,13 @@ static void aspeed_video_init_regs(struct aspeed_video *video)
 
 static void aspeed_video_start(struct aspeed_video *video)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&video->lock, flags);
 	aspeed_video_on(video);
 
 	aspeed_video_init_regs(video);
+	spin_unlock_irqrestore(&video->lock, flags);
 
 	/* Resolution set to 640x480 if no signal found */
 	aspeed_video_get_resolution(video);
@@ -969,6 +970,9 @@ static void aspeed_video_start(struct aspeed_video *video)
 
 static void aspeed_video_stop(struct aspeed_video *video)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&video->lock, flags);
 	set_bit(VIDEO_STOPPED, &video->flags);
 	cancel_delayed_work_sync(&video->res_work);
 
@@ -982,6 +986,7 @@ static void aspeed_video_stop(struct aspeed_video *video)
 
 	video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL;
 	video->flags = 0;
+	spin_unlock_irqrestore(&video->lock, flags);
 }
 
 static int aspeed_video_querycap(struct file *file, void *fh,
@@ -1319,16 +1324,21 @@ static void aspeed_video_resolution_work(struct work_struct *work)
 	struct delayed_work *dwork = to_delayed_work(work);
 	struct aspeed_video *video = container_of(dwork, struct aspeed_video,
 						  res_work);
-	u32 input_status = video->v4l2_input_status;
+	unsigned long flags;
+	u32 input_status;
 
+	spin_lock_irqsave(&video->lock, flags);
+	input_status = video->v4l2_input_status;
 	aspeed_video_on(video);
 
 	/* Exit early in case no clients remain */
-	if (test_bit(VIDEO_STOPPED, &video->flags))
+	if (test_bit(VIDEO_STOPPED, &video->flags)) {
+		spin_unlock_irqrestore(&video->lock, flags);
 		goto done;
+	}
 
 	aspeed_video_init_regs(video);
-
+	spin_unlock_irqrestore(&video->lock, flags);
 	aspeed_video_get_resolution(video);
 
 	if (video->detected_timings.width != video->active_timings.width ||
@@ -1458,13 +1468,15 @@ static void aspeed_video_stop_streaming(struct vb2_queue *q)
 				!test_bit(VIDEO_FRAME_INPRG, &video->flags),
 				STOP_TIMEOUT);
 	if (!rc) {
-		dev_err(video->dev, "Timed out when stopping streaming\n");
+		dev_dbg(video->dev, "Timed out when stopping streaming\n");
 
 		/*
 		 * Need to force stop any DMA and try and get HW into a good
 		 * state for future calls to start streaming again.
 		 */
-		aspeed_video_reset(video);
+		aspeed_video_off(video);
+		aspeed_video_on(video);
+
 		aspeed_video_init_regs(video);
 
 		aspeed_video_get_resolution(video);
@@ -1600,8 +1612,8 @@ static int aspeed_video_init(struct aspeed_video *video)
 		return -ENODEV;
 	}
 
-	rc = devm_request_irq(dev, irq, aspeed_video_irq, IRQF_SHARED,
-			      DEVICE_NAME, video);
+	rc = devm_request_threaded_irq(dev, irq, NULL, aspeed_video_irq,
+				       IRQF_ONESHOT, DEVICE_NAME, video);
 	if (rc < 0) {
 		dev_err(dev, "Unable to request IRQ %d\n", irq);
 		return rc;
@@ -1613,41 +1625,46 @@ static int aspeed_video_init(struct aspeed_video *video)
 		return PTR_ERR(video->eclk);
 	}
 
+	rc = clk_prepare(video->eclk);
+	if (rc)
+		return rc;
+
 	video->vclk = devm_clk_get(dev, "vclk");
 	if (IS_ERR(video->vclk)) {
 		dev_err(dev, "Unable to get VCLK\n");
-		return PTR_ERR(video->vclk);
+		rc = PTR_ERR(video->vclk);
+		goto err_unprepare_eclk;
 	}
 
-	video->rst = devm_reset_control_get_exclusive(dev, NULL);
-	if (IS_ERR(video->rst)) {
-		dev_err(dev, "Unable to get VE reset\n");
-		return PTR_ERR(video->rst);
-	}
+	rc = clk_prepare(video->vclk);
+	if (rc)
+		goto err_unprepare_eclk;
 
-	rc = of_reserved_mem_device_init(dev);
-	if (rc) {
-		dev_err(dev, "Unable to reserve memory\n");
-		return rc;
-	}
+	of_reserved_mem_device_init(dev);
 
 	rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
 	if (rc) {
 		dev_err(dev, "Failed to set DMA mask\n");
-		of_reserved_mem_device_release(dev);
-		return rc;
+		goto err_release_reserved_mem;
 	}
 
 	if (!aspeed_video_alloc_buf(video, &video->jpeg,
 				    VE_JPEG_HEADER_SIZE)) {
 		dev_err(dev, "Failed to allocate DMA for JPEG header\n");
-		of_reserved_mem_device_release(dev);
-		return rc;
+		goto err_release_reserved_mem;
 	}
 
 	aspeed_video_init_jpeg_table(video->jpeg.virt, video->yuv420);
 
 	return 0;
+
+err_release_reserved_mem:
+	of_reserved_mem_device_release(dev);
+	clk_unprepare(video->vclk);
+err_unprepare_eclk:
+	clk_unprepare(video->eclk);
+
+	return rc;
 }
 
 static int aspeed_video_probe(struct platform_device *pdev)
@@ -1661,6 +1678,7 @@ static int aspeed_video_probe(struct platform_device *pdev)
 
 	video->frame_rate = 30;
 	video->dev = &pdev->dev;
+	spin_lock_init(&video->lock);
 	mutex_init(&video->video_lock);
 	init_waitqueue_head(&video->wait);
 	INIT_DELAYED_WORK(&video->res_work, aspeed_video_resolution_work);
@@ -1690,6 +1708,11 @@ static int aspeed_video_remove(struct platform_device *pdev)
 	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
 	struct aspeed_video *video = to_aspeed_video(v4l2_dev);
 
+	aspeed_video_off(video);
+
+	clk_unprepare(video->vclk);
+	clk_unprepare(video->eclk);
+
 	video_unregister_device(&video->vdev);
 
 	vb2_queue_release(&video->queue);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 76f9909..9af5730 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -605,6 +605,20 @@
 	  Passage) chip. This chip embeds audio, battery, GPIO, etc.
 	  devices used in Intel Medfield platforms.
 
+config MFD_INTEL_PECI_CLIENT
+	bool "Intel PECI client"
+	depends on (PECI || COMPILE_TEST)
+	select MFD_CORE
+	help
+	  If you say yes to this option, support will be included for the
+	  Intel PECI (Platform Environment Control Interface) client. PECI is a
+	  one-wire bus interface that provides a communication channel from PECI
+	  clients in Intel processors and chipset components to external
+	  monitoring or control devices.
+
+	  Additional drivers must be enabled in order to use the functionality
+	  of the device.
+
 config MFD_IPAQ_MICRO
 	bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
 	depends on SA1100_H3100 || SA1100_H3600
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 12980a4..b8c1da8 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -204,6 +204,7 @@
 obj-$(CONFIG_MFD_INTEL_LPSS_PCI)	+= intel-lpss-pci.o
 obj-$(CONFIG_MFD_INTEL_LPSS_ACPI)	+= intel-lpss-acpi.o
 obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
+obj-$(CONFIG_MFD_INTEL_PECI_CLIENT)	+= intel-peci-client.o
 obj-$(CONFIG_MFD_PALMAS)	+= palmas.o
 obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o
 obj-$(CONFIG_MFD_RC5T583)	+= rc5t583.o rc5t583-irq.o
diff --git a/drivers/mfd/intel-peci-client.c b/drivers/mfd/intel-peci-client.c
new file mode 100644
index 0000000..d53e4f1
--- /dev/null
+++ b/drivers/mfd/intel-peci-client.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 Intel Corporation
+
+#include <linux/bitfield.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/intel-peci-client.h>
+#include <linux/module.h>
+#include <linux/peci.h>
+#include <linux/of_device.h>
+
+#define CPU_ID_MODEL_MASK      GENMASK(7, 4)
+#define CPU_ID_FAMILY_MASK     GENMASK(11, 8)
+#define CPU_ID_EXT_MODEL_MASK  GENMASK(19, 16)
+#define CPU_ID_EXT_FAMILY_MASK GENMASK(27, 20)
+
+#define LOWER_NIBBLE_MASK      GENMASK(3, 0)
+#define UPPER_NIBBLE_MASK      GENMASK(7, 4)
+#define LOWER_BYTE_MASK        GENMASK(7, 0)
+#define UPPER_BYTE_MASK        GENMASK(16, 8)
+
+enum cpu_gens {
+	CPU_GEN_HSX = 0, /* Haswell Xeon */
+	CPU_GEN_BRX,     /* Broadwell Xeon */
+	CPU_GEN_SKX,     /* Skylake Xeon */
+};
+
+static struct mfd_cell peci_functions[] = {
+	{ .name = "peci-cputemp", },
+	{ .name = "peci-dimmtemp", },
+	/* TODO: Add additional PECI sideband functions into here */
+};
+
+static const struct cpu_gen_info cpu_gen_info_table[] = {
+	[CPU_GEN_HSX] = {
+		.family        = 6, /* Family code */
+		.model         = INTEL_FAM6_HASWELL_X,
+		.core_max      = CORE_MAX_ON_HSX,
+		.chan_rank_max = CHAN_RANK_MAX_ON_HSX,
+		.dimm_idx_max  = DIMM_IDX_MAX_ON_HSX },
+	[CPU_GEN_BRX] = {
+		.family        = 6, /* Family code */
+		.model         = INTEL_FAM6_BROADWELL_X,
+		.core_max      = CORE_MAX_ON_BDX,
+		.chan_rank_max = CHAN_RANK_MAX_ON_BDX,
+		.dimm_idx_max  = DIMM_IDX_MAX_ON_BDX },
+	[CPU_GEN_SKX] = {
+		.family        = 6, /* Family code */
+		.model         = INTEL_FAM6_SKYLAKE_X,
+		.core_max      = CORE_MAX_ON_SKX,
+		.chan_rank_max = CHAN_RANK_MAX_ON_SKX,
+		.dimm_idx_max  = DIMM_IDX_MAX_ON_SKX },
+};
+
+static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv)
+{
+	u32 cpu_id;
+	u16 family;
+	u8 model;
+	int rc;
+	int i;
+
+	rc = peci_get_cpu_id(priv->client->adapter, priv->client->addr,
+			     &cpu_id);
+	if (rc)
+		return rc;
+
+	family = FIELD_PREP(LOWER_BYTE_MASK,
+			    FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id)) |
+		 FIELD_PREP(UPPER_BYTE_MASK,
+			    FIELD_GET(CPU_ID_EXT_FAMILY_MASK, cpu_id));
+	model = FIELD_PREP(LOWER_NIBBLE_MASK,
+			   FIELD_GET(CPU_ID_MODEL_MASK, cpu_id)) |
+		FIELD_PREP(UPPER_NIBBLE_MASK,
+			   FIELD_GET(CPU_ID_EXT_MODEL_MASK, cpu_id));
+
+	for (i = 0; i < ARRAY_SIZE(cpu_gen_info_table); i++) {
+		const struct cpu_gen_info *cpu_info = &cpu_gen_info_table[i];
+
+		if (family == cpu_info->family && model == cpu_info->model) {
+			priv->gen_info = cpu_info;
+			break;
+		}
+	}
+
+	if (!priv->gen_info) {
+		dev_err(priv->dev, "Can't support this CPU: 0x%x\n", cpu_id);
+		rc = -ENODEV;
+	}
+
+	return rc;
+}
+
+static int peci_client_probe(struct peci_client *client)
+{
+	struct device *dev = &client->dev;
+	struct peci_client_manager *priv;
+	uint cpu_no;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, priv);
+	priv->client = client;
+	priv->dev = dev;
+	cpu_no = client->addr - PECI_BASE_ADDR;
+
+	ret = peci_client_get_cpu_gen_info(priv);
+	if (ret)
+		return ret;
+
+	ret = devm_mfd_add_devices(priv->dev, cpu_no, peci_functions,
+				   ARRAY_SIZE(peci_functions), NULL, 0, NULL);
+	if (ret < 0) {
+		dev_err(priv->dev, "Failed to register child devices: %d\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id peci_client_of_table[] = {
+	{ .compatible = "intel,peci-client" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, peci_client_of_table);
+#endif
+
+static const struct peci_device_id peci_client_ids[] = {
+	{ .name = "peci-client" },
+	{ }
+};
+MODULE_DEVICE_TABLE(peci, peci_client_ids);
+
+static struct peci_driver peci_client_driver = {
+	.probe    = peci_client_probe,
+	.id_table = peci_client_ids,
+	.driver   = {
+		.name           = "peci-client",
+		.of_match_table = of_match_ptr(peci_client_of_table),
+	},
+};
+module_peci_driver(peci_client_driver);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI client driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index f417b06..31361d7 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -485,6 +485,14 @@
 	  bus. System Configuration interface is one of the possible means
 	  of generating transactions on this bus.
 
+config ASPEED_P2A_CTRL
+	depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
+	tristate "Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC bridge control"
+	help
+	  Control Aspeed ast2400/2500 HOST P2A VGA MMIO to BMC mappings through
+	  ioctl()s, the driver also provides an interface for userspace mappings to
+	  a pre-defined region.
+
 config ASPEED_LPC_CTRL
 	depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
 	tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control"
@@ -501,6 +509,13 @@
 	  allows the BMC to listen on and save the data written by
 	  the host to an arbitrary LPC I/O port.
 
+config ASPEED_LPC_MBOX
+	tristate "Aspeed LPC Mailbox Controller"
+	depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
+	---help---
+	  Expose the ASPEED LPC MBOX registers found on Aspeed SOCs (AST2400
+	  and AST2500) to userspace.
+
 config PCI_ENDPOINT_TEST
 	depends on PCI
 	select CRC32
@@ -521,6 +536,21 @@
 	  a paravirtualized device provided by QEMU; it lets a virtual machine
 	  (guest) communicate panic events to the host.
 
+config NPCM7XX_LPC_BPC
+	tristate "NPCM7xx LPC BIOS Post Code support"
+	depends on (ARCH_NPCM7XX || COMPILE_TEST)
+	help
+	  Provides a NPCM7xx driver to control the LPC BIOS Post Code
+	  interface which allows the BMC to monitoring and save
+	  the data written by the host to an arbitrary LPC I/O port.
+
+config NPCM7XX_PCI_MBOX
+	tristate "NPCM7xx PCI Mailbox Controller"
+	depends on (ARCH_NPCM7XX || COMPILE_TEST) && REGMAP && MFD_SYSCON
+	help
+	  Expose the NPCM750/730/715/705 PCI MBOX registers found on
+	  Nuvoton SOCs to userspace.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index e39ccbb..d0c4958 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -55,7 +55,11 @@
 obj-$(CONFIG_CXL_BASE)		+= cxl/
 obj-$(CONFIG_ASPEED_LPC_CTRL)	+= aspeed-lpc-ctrl.o
 obj-$(CONFIG_ASPEED_LPC_SNOOP)	+= aspeed-lpc-snoop.o
+obj-$(CONFIG_ASPEED_LPC_MBOX)	+= aspeed-lpc-mbox.o
+obj-$(CONFIG_ASPEED_P2A_CTRL)	+= aspeed-p2a-ctrl.o
 obj-$(CONFIG_PCI_ENDPOINT_TEST)	+= pci_endpoint_test.o
 obj-$(CONFIG_OCXL)		+= ocxl/
 obj-y				+= cardreader/
 obj-$(CONFIG_PVPANIC)   	+= pvpanic.o
+obj-$(CONFIG_NPCM7XX_LPC_BPC)	+= npcm7xx-lpc-bpc.o
+obj-$(CONFIG_NPCM7XX_PCI_MBOX)	+= npcm7xx-pci-mbox.o
diff --git a/drivers/misc/aspeed-lpc-ctrl.c b/drivers/misc/aspeed-lpc-ctrl.c
index a024f80..332210e 100644
--- a/drivers/misc/aspeed-lpc-ctrl.c
+++ b/drivers/misc/aspeed-lpc-ctrl.c
@@ -68,6 +68,7 @@ static long aspeed_lpc_ctrl_ioctl(struct file *file, unsigned int cmd,
 		unsigned long param)
 {
 	struct aspeed_lpc_ctrl *lpc_ctrl = file_aspeed_lpc_ctrl(file);
+	struct device *dev = file->private_data;
 	void __user *p = (void __user *)param;
 	struct aspeed_lpc_ctrl_mapping map;
 	u32 addr;
@@ -90,6 +91,12 @@ static long aspeed_lpc_ctrl_ioctl(struct file *file, unsigned int cmd,
 		if (map.window_id != 0)
 			return -EINVAL;
 
+		/* If memory-region is not described in device tree */
+		if (!lpc_ctrl->mem_size) {
+			dev_err(dev, "Didn't find reserved memory\n");
+			return -EINVAL;
+		}
+
 		map.size = lpc_ctrl->mem_size;
 
 		return copy_to_user(p, &map, sizeof(map)) ? -EFAULT : 0;
@@ -126,9 +133,18 @@ static long aspeed_lpc_ctrl_ioctl(struct file *file, unsigned int cmd,
 			return -EINVAL;
 
 		if (map.window_type == ASPEED_LPC_CTRL_WINDOW_FLASH) {
+			if (!lpc_ctrl->pnor_size) {
+				dev_err(dev, "Didn't find host pnor flash\n");
+				return -EINVAL;
+			}
 			addr = lpc_ctrl->pnor_base;
 			size = lpc_ctrl->pnor_size;
 		} else if (map.window_type == ASPEED_LPC_CTRL_WINDOW_MEMORY) {
+			/* If memory-region is not described in device tree */
+			if (!lpc_ctrl->mem_size) {
+				dev_err(dev, "Didn't find reserved memory\n");
+				return -EINVAL;
+			}
 			addr = lpc_ctrl->mem_base;
 			size = lpc_ctrl->mem_size;
 		} else {
@@ -196,17 +212,17 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev)
 	if (!lpc_ctrl)
 		return -ENOMEM;
 
+	/* If flash is described in device tree then store */
 	node = of_parse_phandle(dev->of_node, "flash", 0);
 	if (!node) {
-		dev_err(dev, "Didn't find host pnor flash node\n");
-		return -ENODEV;
-	}
-
-	rc = of_address_to_resource(node, 1, &resm);
-	of_node_put(node);
-	if (rc) {
-		dev_err(dev, "Couldn't address to resource for flash\n");
-		return rc;
+		dev_dbg(dev, "Didn't find host pnor flash node\n");
+	} else {
+		rc = of_address_to_resource(node, 1, &resm);
+		of_node_put(node);
+		if (rc) {
+			dev_err(dev, "Couldn't address to resource for flash\n");
+			return rc;
+		}
 	}
 
 	lpc_ctrl->pnor_size = resource_size(&resm);
@@ -214,22 +230,22 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev)
 
 	dev_set_drvdata(&pdev->dev, lpc_ctrl);
 
+	/* If memory-region is described in device tree then store */
 	node = of_parse_phandle(dev->of_node, "memory-region", 0);
 	if (!node) {
-		dev_err(dev, "Didn't find reserved memory\n");
-		return -EINVAL;
-	}
+		dev_dbg(dev, "Didn't find reserved memory\n");
+	} else {
+		rc = of_address_to_resource(node, 0, &resm);
+		of_node_put(node);
+		if (rc) {
+			dev_err(dev, "Couldn't address to resource for reserved memory\n");
+			return -ENOMEM;
+		}
 
-	rc = of_address_to_resource(node, 0, &resm);
-	of_node_put(node);
-	if (rc) {
-		dev_err(dev, "Couldn't address to resource for reserved memory\n");
-		return -ENOMEM;
+		lpc_ctrl->mem_size = resource_size(&resm);
+		lpc_ctrl->mem_base = resm.start;
 	}
 
-	lpc_ctrl->mem_size = resource_size(&resm);
-	lpc_ctrl->mem_base = resm.start;
-
 	lpc_ctrl->regmap = syscon_node_to_regmap(
 			pdev->dev.parent->of_node);
 	if (IS_ERR(lpc_ctrl->regmap)) {
@@ -258,8 +274,6 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev)
 		goto err;
 	}
 
-	dev_info(dev, "Loaded at %pr\n", &resm);
-
 	return 0;
 
 err:
diff --git a/drivers/misc/aspeed-lpc-mbox.c b/drivers/misc/aspeed-lpc-mbox.c
new file mode 100644
index 0000000..bab86e5
--- /dev/null
+++ b/drivers/misc/aspeed-lpc-mbox.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2017 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define DEVICE_NAME	"aspeed-mbox"
+
+#define ASPEED_MBOX_NUM_REGS 16
+
+#define ASPEED_MBOX_DATA_0 0x00
+#define ASPEED_MBOX_STATUS_0 0x40
+#define ASPEED_MBOX_STATUS_1 0x44
+#define ASPEED_MBOX_BMC_CTRL 0x48
+#define   ASPEED_MBOX_CTRL_RECV BIT(7)
+#define   ASPEED_MBOX_CTRL_MASK BIT(1)
+#define   ASPEED_MBOX_CTRL_SEND BIT(0)
+#define ASPEED_MBOX_HOST_CTRL 0x4c
+#define ASPEED_MBOX_INTERRUPT_0 0x50
+#define ASPEED_MBOX_INTERRUPT_1 0x54
+
+struct aspeed_mbox {
+	struct miscdevice	miscdev;
+	struct regmap		*regmap;
+	unsigned int		base;
+	wait_queue_head_t	queue;
+	struct mutex		mutex;
+};
+
+static atomic_t aspeed_mbox_open_count = ATOMIC_INIT(0);
+
+static u8 aspeed_mbox_inb(struct aspeed_mbox *mbox, int reg)
+{
+	/*
+	 * The mbox registers are actually only one byte but are addressed
+	 * four bytes apart. The other three bytes are marked 'reserved',
+	 * they *should* be zero but lets not rely on it.
+	 * I am going to rely on the fact we can casually read/write to them...
+	 */
+	unsigned int val = 0xff; /* If regmap throws an error return 0xff */
+	int rc = regmap_read(mbox->regmap, mbox->base + reg, &val);
+
+	if (rc)
+		dev_err(mbox->miscdev.parent, "regmap_read() failed with "
+				"%d (reg: 0x%08x)\n", rc, reg);
+
+	return val & 0xff;
+}
+
+static void aspeed_mbox_outb(struct aspeed_mbox *mbox, u8 data, int reg)
+{
+	int rc = regmap_write(mbox->regmap, mbox->base + reg, data);
+
+	if (rc)
+		dev_err(mbox->miscdev.parent, "regmap_write() failed with "
+				"%d (data: %u reg: 0x%08x)\n", rc, data, reg);
+}
+
+static struct aspeed_mbox *file_mbox(struct file *file)
+{
+	return container_of(file->private_data, struct aspeed_mbox, miscdev);
+}
+
+static int aspeed_mbox_open(struct inode *inode, struct file *file)
+{
+	struct aspeed_mbox *mbox = file_mbox(file);
+
+	if (atomic_inc_return(&aspeed_mbox_open_count) == 1) {
+		/*
+		 * Clear the interrupt status bit if it was left on and unmask
+		 * interrupts.
+		 * ASPEED_MBOX_CTRL_RECV bit is W1C, this also unmasks in 1 step
+		 */
+		aspeed_mbox_outb(mbox, ASPEED_MBOX_CTRL_RECV, ASPEED_MBOX_BMC_CTRL);
+		return 0;
+	}
+
+	atomic_dec(&aspeed_mbox_open_count);
+	return -EBUSY;
+}
+
+static ssize_t aspeed_mbox_read(struct file *file, char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct aspeed_mbox *mbox = file_mbox(file);
+	char __user *p = buf;
+	ssize_t ret;
+	int i;
+
+	if (!access_ok(buf, count))
+		return -EFAULT;
+
+	if (count + *ppos > ASPEED_MBOX_NUM_REGS)
+		return -EINVAL;
+
+	if (file->f_flags & O_NONBLOCK) {
+		if (!(aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL) &
+				ASPEED_MBOX_CTRL_RECV))
+			return -EAGAIN;
+	} else if (wait_event_interruptible(mbox->queue,
+				aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL) &
+				ASPEED_MBOX_CTRL_RECV)) {
+		return -ERESTARTSYS;
+	}
+
+	mutex_lock(&mbox->mutex);
+
+	for (i = *ppos; count > 0 && i < ASPEED_MBOX_NUM_REGS; i++) {
+		uint8_t reg = aspeed_mbox_inb(mbox, ASPEED_MBOX_DATA_0 + (i * 4));
+
+		ret = __put_user(reg, p);
+		if (ret)
+			goto out_unlock;
+
+		p++;
+		count--;
+	}
+
+	/* ASPEED_MBOX_CTRL_RECV bit is write to clear, this also unmasks in 1 step */
+	aspeed_mbox_outb(mbox, ASPEED_MBOX_CTRL_RECV, ASPEED_MBOX_BMC_CTRL);
+	ret = p - buf;
+
+out_unlock:
+	mutex_unlock(&mbox->mutex);
+	return ret;
+}
+
+static ssize_t aspeed_mbox_write(struct file *file, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct aspeed_mbox *mbox = file_mbox(file);
+	const char __user *p = buf;
+	ssize_t ret;
+	char c;
+	int i;
+
+	if (!access_ok(buf, count))
+		return -EFAULT;
+
+	if (count + *ppos > ASPEED_MBOX_NUM_REGS)
+		return -EINVAL;
+
+	mutex_lock(&mbox->mutex);
+
+	for (i = *ppos; count > 0 && i < ASPEED_MBOX_NUM_REGS; i++) {
+		ret = __get_user(c, p);
+		if (ret)
+			goto out_unlock;
+
+		aspeed_mbox_outb(mbox, c, ASPEED_MBOX_DATA_0 + (i * 4));
+		p++;
+		count--;
+	}
+
+	aspeed_mbox_outb(mbox, ASPEED_MBOX_CTRL_SEND, ASPEED_MBOX_BMC_CTRL);
+	ret = p - buf;
+
+out_unlock:
+	mutex_unlock(&mbox->mutex);
+	return ret;
+}
+
+static unsigned int aspeed_mbox_poll(struct file *file, poll_table *wait)
+{
+	struct aspeed_mbox *mbox = file_mbox(file);
+	unsigned int mask = 0;
+
+	poll_wait(file, &mbox->queue, wait);
+
+	if (aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL) & ASPEED_MBOX_CTRL_RECV)
+		mask |= POLLIN;
+
+	return mask;
+}
+
+static int aspeed_mbox_release(struct inode *inode, struct file *file)
+{
+	atomic_dec(&aspeed_mbox_open_count);
+	return 0;
+}
+
+static const struct file_operations aspeed_mbox_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_seek_end_llseek,
+	.read		= aspeed_mbox_read,
+	.write		= aspeed_mbox_write,
+	.open		= aspeed_mbox_open,
+	.release	= aspeed_mbox_release,
+	.poll		= aspeed_mbox_poll,
+};
+
+static irqreturn_t aspeed_mbox_irq(int irq, void *arg)
+{
+	struct aspeed_mbox *mbox = arg;
+
+	if (!(aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL) & ASPEED_MBOX_CTRL_RECV))
+		return IRQ_NONE;
+
+	/*
+	 * Leave the status bit set so that we know the data is for us,
+	 * clear it once it has been read.
+	 */
+
+	/* Mask it off, we'll clear it when we the data gets read */
+	aspeed_mbox_outb(mbox, ASPEED_MBOX_CTRL_MASK, ASPEED_MBOX_BMC_CTRL);
+
+	wake_up(&mbox->queue);
+	return IRQ_HANDLED;
+}
+
+static int aspeed_mbox_config_irq(struct aspeed_mbox *mbox,
+		struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	int rc, irq;
+
+	irq = irq_of_parse_and_map(dev->of_node, 0);
+	if (!irq)
+		return -ENODEV;
+
+	rc = devm_request_irq(dev, irq, aspeed_mbox_irq,
+			IRQF_SHARED, DEVICE_NAME, mbox);
+	if (rc < 0) {
+		dev_err(dev, "Unable to request IRQ %d\n", irq);
+		return rc;
+	}
+
+	/*
+	 * Disable all register based interrupts.
+	 */
+	aspeed_mbox_outb(mbox, 0x00, ASPEED_MBOX_INTERRUPT_0); /* regs 0 - 7 */
+	aspeed_mbox_outb(mbox, 0x00, ASPEED_MBOX_INTERRUPT_1); /* regs 8 - 15 */
+
+	/* These registers are write one to clear. Clear them. */
+	aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STATUS_0);
+	aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STATUS_1);
+
+	aspeed_mbox_outb(mbox, ASPEED_MBOX_CTRL_RECV, ASPEED_MBOX_BMC_CTRL);
+	return 0;
+}
+
+static int aspeed_mbox_probe(struct platform_device *pdev)
+{
+	struct aspeed_mbox *mbox;
+	struct device *dev;
+	int rc;
+
+	dev = &pdev->dev;
+
+	mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
+	if (!mbox)
+		return -ENOMEM;
+
+	dev_set_drvdata(&pdev->dev, mbox);
+
+	rc = of_property_read_u32(dev->of_node, "reg", &mbox->base);
+	if (rc) {
+		dev_err(dev, "Couldn't read reg device-tree property\n");
+		return rc;
+	}
+
+	mbox->regmap = syscon_node_to_regmap(
+			pdev->dev.parent->of_node);
+	if (IS_ERR(mbox->regmap)) {
+		dev_err(dev, "Couldn't get regmap\n");
+		return -ENODEV;
+	}
+
+	mutex_init(&mbox->mutex);
+	init_waitqueue_head(&mbox->queue);
+
+	mbox->miscdev.minor = MISC_DYNAMIC_MINOR;
+	mbox->miscdev.name = DEVICE_NAME;
+	mbox->miscdev.fops = &aspeed_mbox_fops;
+	mbox->miscdev.parent = dev;
+	rc = misc_register(&mbox->miscdev);
+	if (rc) {
+		dev_err(dev, "Unable to register device\n");
+		return rc;
+	}
+
+	rc = aspeed_mbox_config_irq(mbox, pdev);
+	if (rc) {
+		dev_err(dev, "Failed to configure IRQ\n");
+		misc_deregister(&mbox->miscdev);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int aspeed_mbox_remove(struct platform_device *pdev)
+{
+	struct aspeed_mbox *mbox = dev_get_drvdata(&pdev->dev);
+
+	misc_deregister(&mbox->miscdev);
+
+	return 0;
+}
+
+static const struct of_device_id aspeed_mbox_match[] = {
+	{ .compatible = "aspeed,ast2400-mbox" },
+	{ .compatible = "aspeed,ast2500-mbox" },
+	{ },
+};
+
+static struct platform_driver aspeed_mbox_driver = {
+	.driver = {
+		.name		= DEVICE_NAME,
+		.of_match_table = aspeed_mbox_match,
+	},
+	.probe = aspeed_mbox_probe,
+	.remove = aspeed_mbox_remove,
+};
+
+module_platform_driver(aspeed_mbox_driver);
+
+MODULE_DEVICE_TABLE(of, aspeed_mbox_match);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Cyril Bur <cyrilbur@gmail.com>");
+MODULE_DESCRIPTION("Aspeed mailbox device driver");
diff --git a/drivers/misc/aspeed-p2a-ctrl.c b/drivers/misc/aspeed-p2a-ctrl.c
new file mode 100644
index 0000000..b60fbea
--- /dev/null
+++ b/drivers/misc/aspeed-p2a-ctrl.c
@@ -0,0 +1,444 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2019 Google Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Provides a simple driver to control the ASPEED P2A interface which allows
+ * the host to read and write to various regions of the BMC's memory.
+ */
+
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <linux/aspeed-p2a-ctrl.h>
+
+#define DEVICE_NAME	"aspeed-p2a-ctrl"
+
+/* SCU2C is a Misc. Control Register. */
+#define SCU2C 0x2c
+/* SCU180 is the PCIe Configuration Setting Control Register. */
+#define SCU180 0x180
+/* Bit 1 controls the P2A bridge, while bit 0 controls the entire VGA device
+ * on the PCI bus.
+ */
+#define SCU180_ENP2A BIT(1)
+
+/* The ast2400/2500 both have six ranges. */
+#define P2A_REGION_COUNT 6
+
+struct region {
+	u64 min;
+	u64 max;
+	u32 bit;
+};
+
+struct aspeed_p2a_model_data {
+	/* min, max, bit */
+	struct region regions[P2A_REGION_COUNT];
+};
+
+struct aspeed_p2a_ctrl {
+	struct miscdevice miscdev;
+	struct regmap *regmap;
+
+	const struct aspeed_p2a_model_data *config;
+
+	/* Access to these needs to be locked, held via probe, mapping ioctl,
+	 * and release, remove.
+	 */
+	struct mutex tracking;
+	u32 readers;
+	u32 readerwriters[P2A_REGION_COUNT];
+
+	phys_addr_t mem_base;
+	resource_size_t mem_size;
+};
+
+struct aspeed_p2a_user {
+	struct file *file;
+	struct aspeed_p2a_ctrl *parent;
+
+	/* The entire memory space is opened for reading once the bridge is
+	 * enabled, therefore this needs only to be tracked once per user.
+	 * If any user has it open for read, the bridge must stay enabled.
+	 */
+	u32 read;
+
+	/* Each entry of the array corresponds to a P2A Region.  If the user
+	 * opens for read or readwrite, the reference goes up here.  On
+	 * release, this array is walked and references adjusted accordingly.
+	 */
+	u32 readwrite[P2A_REGION_COUNT];
+};
+
+static void aspeed_p2a_enable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
+{
+	regmap_update_bits(p2a_ctrl->regmap,
+		SCU180, SCU180_ENP2A, SCU180_ENP2A);
+}
+
+static void aspeed_p2a_disable_bridge(struct aspeed_p2a_ctrl *p2a_ctrl)
+{
+	regmap_update_bits(p2a_ctrl->regmap, SCU180, SCU180_ENP2A, 0);
+}
+
+static int aspeed_p2a_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	unsigned long vsize;
+	pgprot_t prot;
+	struct aspeed_p2a_user *priv = file->private_data;
+	struct aspeed_p2a_ctrl *ctrl = priv->parent;
+
+	if (ctrl->mem_base == 0 && ctrl->mem_size == 0)
+		return -EINVAL;
+
+	vsize = vma->vm_end - vma->vm_start;
+	prot = vma->vm_page_prot;
+
+	if (vma->vm_pgoff + vsize > ctrl->mem_base + ctrl->mem_size)
+		return -EINVAL;
+
+	/* ast2400/2500 AHB accesses are not cache coherent */
+	prot = pgprot_noncached(prot);
+
+	if (remap_pfn_range(vma, vma->vm_start,
+		(ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff,
+		vsize, prot))
+		return -EAGAIN;
+
+	return 0;
+}
+
+static bool aspeed_p2a_region_acquire(struct aspeed_p2a_user *priv,
+		struct aspeed_p2a_ctrl *ctrl,
+		struct aspeed_p2a_ctrl_mapping *map)
+{
+	int i;
+	u64 base, end;
+	bool matched = false;
+
+	base = map->addr;
+	end = map->addr + (map->length - 1);
+
+	/* If the value is a legal u32, it will find a match. */
+	for (i = 0; i < P2A_REGION_COUNT; i++) {
+		const struct region *curr = &ctrl->config->regions[i];
+
+		/* If the top of this region is lower than your base, skip it.
+		 */
+		if (curr->max < base)
+			continue;
+
+		/* If the bottom of this region is higher than your end, bail.
+		 */
+		if (curr->min > end)
+			break;
+
+		/* Lock this and update it, therefore it someone else is
+		 * closing their file out, this'll preserve the increment.
+		 */
+		mutex_lock(&ctrl->tracking);
+		ctrl->readerwriters[i] += 1;
+		mutex_unlock(&ctrl->tracking);
+
+		/* Track with the user, so when they close their file, we can
+		 * decrement properly.
+		 */
+		priv->readwrite[i] += 1;
+
+		/* Enable the region as read-write. */
+		regmap_update_bits(ctrl->regmap, SCU2C, curr->bit, 0);
+		matched = true;
+	}
+
+	return matched;
+}
+
+static long aspeed_p2a_ioctl(struct file *file, unsigned int cmd,
+		unsigned long data)
+{
+	struct aspeed_p2a_user *priv = file->private_data;
+	struct aspeed_p2a_ctrl *ctrl = priv->parent;
+	void __user *arg = (void __user *)data;
+	struct aspeed_p2a_ctrl_mapping map;
+
+	if (copy_from_user(&map, arg, sizeof(map)))
+		return -EFAULT;
+
+	switch (cmd) {
+	case ASPEED_P2A_CTRL_IOCTL_SET_WINDOW:
+		/* If they want a region to be read-only, since the entire
+		 * region is read-only once enabled, we just need to track this
+		 * user wants to read from the bridge, and if it's not enabled.
+		 * Enable it.
+		 */
+		if (map.flags == ASPEED_P2A_CTRL_READ_ONLY) {
+			mutex_lock(&ctrl->tracking);
+			ctrl->readers += 1;
+			mutex_unlock(&ctrl->tracking);
+
+			/* Track with the user, so when they close their file,
+			 * we can decrement properly.
+			 */
+			priv->read += 1;
+		} else if (map.flags == ASPEED_P2A_CTRL_READWRITE) {
+			/* If we don't acquire any region return error. */
+			if (!aspeed_p2a_region_acquire(priv, ctrl, &map)) {
+				return -EINVAL;
+			}
+		} else {
+			/* Invalid map flags. */
+			return -EINVAL;
+		}
+
+		aspeed_p2a_enable_bridge(ctrl);
+		return 0;
+	case ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG:
+		/* This is a request for the memory-region and corresponding
+		 * length that is used by the driver for mmap.
+		 */
+
+		map.flags = 0;
+		map.addr = ctrl->mem_base;
+		map.length = ctrl->mem_size;
+
+		return copy_to_user(arg, &map, sizeof(map)) ? -EFAULT : 0;
+	}
+
+	return -EINVAL;
+}
+
+
+/*
+ * When a user opens this file, we create a structure to track their mappings.
+ *
+ * A user can map a region as read-only (bridge enabled), or read-write (bit
+ * flipped, and bridge enabled).  Either way, this tracking is used, s.t. when
+ * they release the device references are handled.
+ *
+ * The bridge is not enabled until a user calls an ioctl to map a region,
+ * simply opening the device does not enable it.
+ */
+static int aspeed_p2a_open(struct inode *inode, struct file *file)
+{
+	struct aspeed_p2a_user *priv;
+
+	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->file = file;
+	priv->read = 0;
+	memset(priv->readwrite, 0, sizeof(priv->readwrite));
+
+	/* The file's private_data is initialized to the p2a_ctrl. */
+	priv->parent = file->private_data;
+
+	/* Set the file's private_data to the user's data. */
+	file->private_data = priv;
+
+	return 0;
+}
+
+/*
+ * This will close the users mappings.  It will go through what they had opened
+ * for readwrite, and decrement those counts.  If at the end, this is the last
+ * user, it'll close the bridge.
+ */
+static int aspeed_p2a_release(struct inode *inode, struct file *file)
+{
+	int i;
+	u32 bits = 0;
+	bool open_regions = false;
+	struct aspeed_p2a_user *priv = file->private_data;
+
+	/* Lock others from changing these values until everything is updated
+	 * in one pass.
+	 */
+	mutex_lock(&priv->parent->tracking);
+
+	priv->parent->readers -= priv->read;
+
+	for (i = 0; i < P2A_REGION_COUNT; i++) {
+		priv->parent->readerwriters[i] -= priv->readwrite[i];
+
+		if (priv->parent->readerwriters[i] > 0)
+			open_regions = true;
+		else
+			bits |= priv->parent->config->regions[i].bit;
+	}
+
+	/* Setting a bit to 1 disables the region, so let's just OR with the
+	 * above to disable any.
+	 */
+
+	/* Note, if another user is trying to ioctl, they can't grab tracking,
+	 * and therefore can't grab either register mutex.
+	 * If another user is trying to close, they can't grab tracking either.
+	 */
+	regmap_update_bits(priv->parent->regmap, SCU2C, bits, bits);
+
+	/* If parent->readers is zero and open windows is 0, disable the
+	 * bridge.
+	 */
+	if (!open_regions && priv->parent->readers == 0)
+		aspeed_p2a_disable_bridge(priv->parent);
+
+	mutex_unlock(&priv->parent->tracking);
+
+	kfree(priv);
+
+	return 0;
+}
+
+static const struct file_operations aspeed_p2a_ctrl_fops = {
+	.owner = THIS_MODULE,
+	.mmap = aspeed_p2a_mmap,
+	.unlocked_ioctl = aspeed_p2a_ioctl,
+	.open = aspeed_p2a_open,
+	.release = aspeed_p2a_release,
+};
+
+/* The regions are controlled by SCU2C */
+static void aspeed_p2a_disable_all(struct aspeed_p2a_ctrl *p2a_ctrl)
+{
+	int i;
+	u32 value = 0;
+
+	for (i = 0; i < P2A_REGION_COUNT; i++)
+		value |= p2a_ctrl->config->regions[i].bit;
+
+	regmap_update_bits(p2a_ctrl->regmap, SCU2C, value, value);
+
+	/* Disable the bridge. */
+	aspeed_p2a_disable_bridge(p2a_ctrl);
+}
+
+static int aspeed_p2a_ctrl_probe(struct platform_device *pdev)
+{
+	struct aspeed_p2a_ctrl *misc_ctrl;
+	struct device *dev;
+	struct resource resm;
+	struct device_node *node;
+	int rc = 0;
+
+	dev = &pdev->dev;
+
+	misc_ctrl = devm_kzalloc(dev, sizeof(*misc_ctrl), GFP_KERNEL);
+	if (!misc_ctrl)
+		return -ENOMEM;
+
+	mutex_init(&misc_ctrl->tracking);
+
+	/* optional. */
+	node = of_parse_phandle(dev->of_node, "memory-region", 0);
+	if (node) {
+		rc = of_address_to_resource(node, 0, &resm);
+		of_node_put(node);
+		if (rc) {
+			dev_err(dev, "Couldn't address to resource for reserved memory\n");
+			return -ENODEV;
+		}
+
+		misc_ctrl->mem_size = resource_size(&resm);
+		misc_ctrl->mem_base = resm.start;
+	}
+
+	misc_ctrl->regmap = syscon_node_to_regmap(pdev->dev.parent->of_node);
+	if (IS_ERR(misc_ctrl->regmap)) {
+		dev_err(dev, "Couldn't get regmap\n");
+		return -ENODEV;
+	}
+
+	misc_ctrl->config = of_device_get_match_data(dev);
+
+	dev_set_drvdata(&pdev->dev, misc_ctrl);
+
+	aspeed_p2a_disable_all(misc_ctrl);
+
+	misc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
+	misc_ctrl->miscdev.name = DEVICE_NAME;
+	misc_ctrl->miscdev.fops = &aspeed_p2a_ctrl_fops;
+	misc_ctrl->miscdev.parent = dev;
+
+	rc = misc_register(&misc_ctrl->miscdev);
+	if (rc)
+		dev_err(dev, "Unable to register device\n");
+
+	return rc;
+}
+
+static int aspeed_p2a_ctrl_remove(struct platform_device *pdev)
+{
+	struct aspeed_p2a_ctrl *p2a_ctrl = dev_get_drvdata(&pdev->dev);
+
+	misc_deregister(&p2a_ctrl->miscdev);
+
+	return 0;
+}
+
+#define SCU2C_DRAM	BIT(25)
+#define SCU2C_SPI	BIT(24)
+#define SCU2C_SOC	BIT(23)
+#define SCU2C_FLASH	BIT(22)
+
+static const struct aspeed_p2a_model_data ast2400_model_data = {
+	.regions = {
+		{0x00000000, 0x17FFFFFF, SCU2C_FLASH},
+		{0x18000000, 0x1FFFFFFF, SCU2C_SOC},
+		{0x20000000, 0x2FFFFFFF, SCU2C_FLASH},
+		{0x30000000, 0x3FFFFFFF, SCU2C_SPI},
+		{0x40000000, 0x5FFFFFFF, SCU2C_DRAM},
+		{0x60000000, 0xFFFFFFFF, SCU2C_SOC},
+	}
+};
+
+static const struct aspeed_p2a_model_data ast2500_model_data = {
+	.regions = {
+		{0x00000000, 0x0FFFFFFF, SCU2C_FLASH},
+		{0x10000000, 0x1FFFFFFF, SCU2C_SOC},
+		{0x20000000, 0x3FFFFFFF, SCU2C_FLASH},
+		{0x40000000, 0x5FFFFFFF, SCU2C_SOC},
+		{0x60000000, 0x7FFFFFFF, SCU2C_SPI},
+		{0x80000000, 0xFFFFFFFF, SCU2C_DRAM},
+	}
+};
+
+static const struct of_device_id aspeed_p2a_ctrl_match[] = {
+	{ .compatible = "aspeed,ast2400-p2a-ctrl",
+	  .data = &ast2400_model_data },
+	{ .compatible = "aspeed,ast2500-p2a-ctrl",
+	  .data = &ast2500_model_data },
+	{ },
+};
+
+static struct platform_driver aspeed_p2a_ctrl_driver = {
+	.driver = {
+		.name		= DEVICE_NAME,
+		.of_match_table = aspeed_p2a_ctrl_match,
+	},
+	.probe = aspeed_p2a_ctrl_probe,
+	.remove = aspeed_p2a_ctrl_remove,
+};
+
+module_platform_driver(aspeed_p2a_ctrl_driver);
+
+MODULE_DEVICE_TABLE(of, aspeed_p2a_ctrl_match);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick Venture <venture@google.com>");
+MODULE_DESCRIPTION("Control for aspeed 2400/2500 P2A VGA HOST to BMC mappings");
diff --git a/drivers/misc/npcm7xx-lpc-bpc.c b/drivers/misc/npcm7xx-lpc-bpc.c
new file mode 100644
index 0000000..e014e07
--- /dev/null
+++ b/drivers/misc/npcm7xx-lpc-bpc.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2014-2018 Nuvoton Technology corporation.
+
+#include <linux/fs.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
+
+#define DEVICE_NAME	"npcm7xx-lpc-bpc"
+
+#define NUM_BPC_CHANNELS		2
+#define DW_PAD_SIZE			3
+
+/* BIOS POST Code FIFO Registers */
+#define NPCM7XX_BPCFA2L_REG	0x2 //BIOS POST Code FIFO Address 2 LSB
+#define NPCM7XX_BPCFA2M_REG	0x4 //BIOS POST Code FIFO Address 2 MSB
+#define NPCM7XX_BPCFEN_REG	0x6 //BIOS POST Code FIFO Enable
+#define NPCM7XX_BPCFSTAT_REG	0x8 //BIOS POST Code FIFO Status
+#define NPCM7XX_BPCFDATA_REG	0xA //BIOS POST Code FIFO Data
+#define NPCM7XX_BPCFMSTAT_REG	0xC //BIOS POST Code FIFO Miscellaneous Status
+#define NPCM7XX_BPCFA1L_REG	0x10 //BIOS POST Code FIFO Address 1 LSB
+#define NPCM7XX_BPCFA1M_REG	0x12 //BIOS POST Code FIFO Address 1 MSB
+
+/*BIOS regiser data*/
+#define FIFO_IOADDR1_ENABLE	0x80
+#define FIFO_IOADDR2_ENABLE	0x40
+
+/* BPC interface package and structure definition */
+#define BPC_KFIFO_SIZE		0x400
+
+/*BPC regiser data*/
+#define FIFO_DATA_VALID		0x80
+#define FIFO_OVERFLOW		0x20
+#define FIFO_READY_INT_ENABLE	0x8
+#define FIFO_DWCAPTURE		0x4
+#define FIFO_ADDR_DECODE	0x1
+
+/*Host Reset*/
+#define HOST_RESET_INT_ENABLE	0x10
+#define HOST_RESET_CHANGED	0x40
+
+struct npcm7xx_bpc_channel {
+	struct npcm7xx_bpc	*data;
+	struct kfifo		fifo;
+	wait_queue_head_t	wq;
+	bool			host_reset;
+	struct miscdevice	miscdev;
+};
+
+struct npcm7xx_bpc {
+	void __iomem			*base;
+	int				irq;
+	bool				en_dwcap;
+	struct npcm7xx_bpc_channel	ch[NUM_BPC_CHANNELS];
+};
+
+static struct npcm7xx_bpc_channel *npcm7xx_file_to_ch(struct file *file)
+{
+	return container_of(file->private_data, struct npcm7xx_bpc_channel,
+			    miscdev);
+}
+
+static ssize_t npcm7xx_bpc_read(struct file *file, char __user *buffer,
+				size_t count, loff_t *ppos)
+{
+	struct npcm7xx_bpc_channel *chan = npcm7xx_file_to_ch(file);
+	struct npcm7xx_bpc *lpc_bpc = chan->data;
+	unsigned int copied;
+	int ret = 0;
+	int cond_size = 1;
+
+	if (lpc_bpc->en_dwcap)
+		cond_size = 3;
+
+	if (kfifo_len(&chan->fifo) < cond_size) {
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		ret = wait_event_interruptible
+			(chan->wq, kfifo_len(&chan->fifo) > cond_size);
+		if (ret == -ERESTARTSYS)
+			return -EINTR;
+	}
+
+	ret = kfifo_to_user(&chan->fifo, buffer, count, &copied);
+
+	return ret ? ret : copied;
+}
+
+static __poll_t npcm7xx_bpc_poll(struct file *file,
+				 struct poll_table_struct *pt)
+{
+	struct npcm7xx_bpc_channel *chan = npcm7xx_file_to_ch(file);
+	__poll_t mask = 0;
+
+	poll_wait(file, &chan->wq, pt);
+	if (!kfifo_is_empty(&chan->fifo))
+		mask |= POLLIN;
+
+	if (chan->host_reset) {
+		mask |= POLLHUP;
+		chan->host_reset = false;
+	}
+
+	return mask;
+}
+
+static const struct file_operations npcm7xx_bpc_fops = {
+	.owner		= THIS_MODULE,
+	.read		= npcm7xx_bpc_read,
+	.poll		= npcm7xx_bpc_poll,
+	.llseek		= noop_llseek,
+};
+
+static irqreturn_t npcm7xx_bpc_irq(int irq, void *arg)
+{
+	struct npcm7xx_bpc *lpc_bpc = arg;
+	u8 fifo_st;
+	u8 host_st;
+	u8 addr_index = 0;
+	u8 Data;
+	u8 padzero[3] = {0};
+	u8 last_addr_bit = 0;
+	bool isr_flag = false;
+
+	fifo_st = ioread8(lpc_bpc->base + NPCM7XX_BPCFSTAT_REG);
+	while (FIFO_DATA_VALID & fifo_st) {
+		 /* If dwcapture enabled only channel 0 (FIFO 0) used */
+		if (!lpc_bpc->en_dwcap)
+			addr_index = fifo_st & FIFO_ADDR_DECODE;
+		else
+			last_addr_bit = fifo_st & FIFO_ADDR_DECODE;
+
+		/*Read data from FIFO to clear interrupt*/
+		Data = ioread8(lpc_bpc->base + NPCM7XX_BPCFDATA_REG);
+		if (kfifo_is_full(&lpc_bpc->ch[addr_index].fifo))
+			kfifo_skip(&lpc_bpc->ch[addr_index].fifo);
+		kfifo_put(&lpc_bpc->ch[addr_index].fifo, Data);
+		if (fifo_st & FIFO_OVERFLOW)
+			pr_info("BIOS Post Codes FIFO Overflow!!!\n");
+
+		fifo_st = ioread8(lpc_bpc->base + NPCM7XX_BPCFSTAT_REG);
+		if (lpc_bpc->en_dwcap && last_addr_bit) {
+			if ((fifo_st & FIFO_ADDR_DECODE) ||
+			    ((FIFO_DATA_VALID & fifo_st) == 0)) {
+				while (kfifo_avail(&lpc_bpc->ch[addr_index].fifo) < DW_PAD_SIZE)
+					kfifo_skip(&lpc_bpc->ch[addr_index].fifo);
+				kfifo_in(&lpc_bpc->ch[addr_index].fifo,
+					 padzero, DW_PAD_SIZE);
+			}
+		}
+		isr_flag = true;
+	}
+
+	host_st = ioread8(lpc_bpc->base + NPCM7XX_BPCFMSTAT_REG);
+	if (host_st & HOST_RESET_CHANGED) {
+		iowrite8(HOST_RESET_CHANGED,
+			 lpc_bpc->base + NPCM7XX_BPCFMSTAT_REG);
+		lpc_bpc->ch[addr_index].host_reset = true;
+		isr_flag = true;
+	}
+
+	if (isr_flag) {
+		wake_up_interruptible(&lpc_bpc->ch[addr_index].wq);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static int npcm7xx_bpc_config_irq(struct npcm7xx_bpc *lpc_bpc,
+				  struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	int rc;
+
+	lpc_bpc->irq = platform_get_irq(pdev, 0);
+	if (lpc_bpc->irq < 0) {
+		dev_err(dev, "get IRQ failed\n");
+		return lpc_bpc->irq;
+	}
+
+	rc = devm_request_irq(dev, lpc_bpc->irq,
+			      npcm7xx_bpc_irq, IRQF_SHARED,
+			      DEVICE_NAME, lpc_bpc);
+	if (rc < 0) {
+		dev_warn(dev, "Unable to request IRQ %d\n", lpc_bpc->irq);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int npcm7xx_enable_bpc(struct npcm7xx_bpc *lpc_bpc, struct device *dev,
+			      int channel, u16 lpc_port)
+{
+	int rc;
+	u8 addr_en, reg_en;
+
+	init_waitqueue_head(&lpc_bpc->ch[channel].wq);
+
+	rc = kfifo_alloc(&lpc_bpc->ch[channel].fifo,
+			 BPC_KFIFO_SIZE, GFP_KERNEL);
+	if (rc)
+		return rc;
+
+	lpc_bpc->ch[channel].miscdev.minor = MISC_DYNAMIC_MINOR;
+	lpc_bpc->ch[channel].miscdev.name =
+		devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel);
+	lpc_bpc->ch[channel].miscdev.fops = &npcm7xx_bpc_fops;
+	lpc_bpc->ch[channel].miscdev.parent = dev;
+	rc = misc_register(&lpc_bpc->ch[channel].miscdev);
+	if (rc)
+		return rc;
+
+	lpc_bpc->ch[channel].data = lpc_bpc;
+	lpc_bpc->ch[channel].host_reset = false;
+
+	/* Enable LPC snoop channel at requested port */
+	switch (channel) {
+	case 0:
+		addr_en = FIFO_IOADDR1_ENABLE;
+		iowrite8((u8)lpc_port & 0xFF,
+			 lpc_bpc->base + NPCM7XX_BPCFA1L_REG);
+		iowrite8((u8)(lpc_port >> 8),
+			 lpc_bpc->base + NPCM7XX_BPCFA1M_REG);
+		break;
+	case 1:
+		addr_en = FIFO_IOADDR2_ENABLE;
+		iowrite8((u8)lpc_port & 0xFF,
+			 lpc_bpc->base + NPCM7XX_BPCFA2L_REG);
+		iowrite8((u8)(lpc_port >> 8),
+			 lpc_bpc->base + NPCM7XX_BPCFA2M_REG);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (lpc_bpc->en_dwcap)
+		addr_en = FIFO_DWCAPTURE;
+
+	/*
+	 * Enable FIFO Ready Interrupt, FIFO Capture of I/O addr,
+	 * and Host Reset
+	 */
+	reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+	iowrite8(reg_en | addr_en | FIFO_READY_INT_ENABLE |
+		 HOST_RESET_INT_ENABLE, lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+
+	return 0;
+}
+
+static void npcm7xx_disable_bpc(struct npcm7xx_bpc *lpc_bpc, int channel)
+{
+	u8 reg_en;
+
+	switch (channel) {
+	case 0:
+		reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+		if (lpc_bpc->en_dwcap)
+			iowrite8(reg_en & ~FIFO_DWCAPTURE,
+				 lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+		else
+			iowrite8(reg_en & ~FIFO_IOADDR1_ENABLE,
+				 lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+		break;
+	case 1:
+		reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+		iowrite8(reg_en & ~FIFO_IOADDR2_ENABLE,
+			 lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+		break;
+	default:
+		return;
+	}
+
+	if (!(reg_en & (FIFO_IOADDR1_ENABLE | FIFO_IOADDR2_ENABLE)))
+		iowrite8(reg_en &
+			 ~(FIFO_READY_INT_ENABLE | HOST_RESET_INT_ENABLE),
+			 lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+
+	kfifo_free(&lpc_bpc->ch[channel].fifo);
+	misc_deregister(&lpc_bpc->ch[channel].miscdev);
+}
+
+static int npcm7xx_bpc_probe(struct platform_device *pdev)
+{
+	struct npcm7xx_bpc *lpc_bpc;
+	struct resource *res;
+	struct device *dev;
+	u32 port;
+	int rc;
+
+	dev = &pdev->dev;
+
+	lpc_bpc = devm_kzalloc(dev, sizeof(*lpc_bpc), GFP_KERNEL);
+	if (!lpc_bpc)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "BIOS post code reg resource not found\n");
+		return -ENODEV;
+	}
+
+	dev_dbg(dev, "BIOS post code base resource is %pR\n", res);
+	lpc_bpc->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(lpc_bpc->base))
+		return PTR_ERR(lpc_bpc->base);
+
+	dev_set_drvdata(&pdev->dev, lpc_bpc);
+
+	rc = of_property_read_u32_index(dev->of_node, "monitor-ports", 0,
+					&port);
+	if (rc) {
+		dev_err(dev, "no monitor ports configured\n");
+		return -ENODEV;
+	}
+
+	lpc_bpc->en_dwcap =
+		of_property_read_bool(dev->of_node, "bpc-en-dwcapture");
+
+	rc = npcm7xx_bpc_config_irq(lpc_bpc, pdev);
+	if (rc)
+		return rc;
+
+	rc = npcm7xx_enable_bpc(lpc_bpc, dev, 0, port);
+	if (rc) {
+		dev_err(dev, "Enable BIOS post code I/O port 0 failed\n");
+		return rc;
+	}
+
+	/*
+	 * Configuration of second BPC channel port is optional
+	 * Double-Word Capture ignoring address 2
+	 */
+	if (!lpc_bpc->en_dwcap) {
+		if (of_property_read_u32_index(dev->of_node, "monitor-ports",
+					       1, &port) == 0) {
+			rc = npcm7xx_enable_bpc(lpc_bpc, dev, 1, port);
+			if (rc) {
+				dev_err(dev, "Enable BIOS post code I/O port 1 failed, disable I/O port 0\n");
+				npcm7xx_disable_bpc(lpc_bpc, 0);
+				return rc;
+			}
+		}
+	}
+
+	pr_info("npcm7xx BIOS post code probe\n");
+
+	return rc;
+}
+
+static int npcm7xx_bpc_remove(struct platform_device *pdev)
+{
+	struct npcm7xx_bpc *lpc_bpc = dev_get_drvdata(&pdev->dev);
+	u8 reg_en;
+
+	reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+
+	if (reg_en & FIFO_IOADDR1_ENABLE)
+		npcm7xx_disable_bpc(lpc_bpc, 0);
+	if (reg_en & FIFO_IOADDR2_ENABLE)
+		npcm7xx_disable_bpc(lpc_bpc, 1);
+
+	return 0;
+}
+
+static const struct of_device_id npcm7xx_bpc_match[] = {
+	{ .compatible = "nuvoton,npcm750-lpc-bpc" },
+	{ },
+};
+
+static struct platform_driver npcm7xx_bpc_driver = {
+	.driver = {
+		.name		= DEVICE_NAME,
+		.of_match_table = npcm7xx_bpc_match,
+	},
+	.probe = npcm7xx_bpc_probe,
+	.remove = npcm7xx_bpc_remove,
+};
+
+module_platform_driver(npcm7xx_bpc_driver);
+
+MODULE_DEVICE_TABLE(of, npcm7xx_bpc_match);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_DESCRIPTION("Linux driver to control NPCM7XX LPC BIOS post code monitoring");
diff --git a/drivers/misc/npcm7xx-pci-mbox.c b/drivers/misc/npcm7xx-pci-mbox.c
new file mode 100644
index 0000000..1a80661
--- /dev/null
+++ b/drivers/misc/npcm7xx-pci-mbox.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2014-2018 Nuvoton Technology corporation.
+
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define DEVICE_NAME	"npcm7xx-pci-mbox"
+
+#define NPCM7XX_MBOX_BMBXSTAT	0x0
+#define NPCM7XX_MBOX_BMBXCTL	0x4
+#define NPCM7XX_MBOX_BMBXCMD	0x8
+
+#define NPCM7XX_MBOX_CIF_0	BIT(0)
+#define NPCM7XX_MBOX_CIE_0	BIT(0)
+#define NPCM7XX_MBOX_HIF_0	BIT(0)
+
+#define NPCM7XX_MBOX_ALL_CIF	GENMASK(7, 0)
+#define NPCM7XX_MBOX_ALL_CIE	GENMASK(7, 0)
+#define NPCM7XX_MBOX_ALL_HIF	GENMASK(7, 0)
+
+struct npcm7xx_mbox {
+	struct miscdevice	miscdev;
+	struct regmap		*regmap;
+	void __iomem		*memory;
+	wait_queue_head_t	queue;
+	spinlock_t		lock;	/* mbox access mutex */
+	bool			cif0;
+	u32			max_buf_size;
+};
+
+static atomic_t npcm7xx_mbox_open_count = ATOMIC_INIT(0);
+
+static struct npcm7xx_mbox *file_mbox(struct file *file)
+{
+	return container_of(file->private_data, struct npcm7xx_mbox, miscdev);
+}
+
+static int npcm7xx_mbox_open(struct inode *inode, struct file *file)
+{
+	struct npcm7xx_mbox *mbox = file_mbox(file);
+
+	if (atomic_inc_return(&npcm7xx_mbox_open_count) == 1) {
+		/* enable mailbox interrupt */
+		regmap_update_bits(mbox->regmap, NPCM7XX_MBOX_BMBXCTL,
+				   NPCM7XX_MBOX_ALL_CIE, NPCM7XX_MBOX_CIE_0);
+		return 0;
+	}
+
+	atomic_dec(&npcm7xx_mbox_open_count);
+	return -EBUSY;
+}
+
+static ssize_t npcm7xx_mbox_read(struct file *file, char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	struct npcm7xx_mbox *mbox = file_mbox(file);
+	unsigned long flags;
+
+	if (!access_ok(buf, count))
+		return -EFAULT;
+
+	if ((*ppos + count) > mbox->max_buf_size)
+		return -EINVAL;
+
+	if (file->f_flags & O_NONBLOCK) {
+		if (!mbox->cif0)
+			return -EAGAIN;
+	} else if (wait_event_interruptible(mbox->queue, mbox->cif0)) {
+		return -ERESTARTSYS;
+	}
+
+	spin_lock_irqsave(&mbox->lock, flags);
+
+	if (copy_to_user((void __user *)buf,
+			 (const void *)(mbox->memory + *ppos), count)) {
+		spin_unlock_irqrestore(&mbox->lock, flags);
+		return -EFAULT;
+	}
+
+	mbox->cif0 = false;
+	spin_unlock_irqrestore(&mbox->lock, flags);
+	return count;
+}
+
+static ssize_t npcm7xx_mbox_write(struct file *file, const char __user *buf,
+				  size_t count, loff_t *ppos)
+{
+	struct npcm7xx_mbox *mbox = file_mbox(file);
+	unsigned long flags;
+
+	if (!access_ok(buf, count))
+		return -EFAULT;
+
+	if ((*ppos + count) > mbox->max_buf_size)
+		return -EINVAL;
+
+	spin_lock_irqsave(&mbox->lock, flags);
+
+	if (copy_from_user((void *)(mbox->memory + *ppos),
+			   (void __user *)buf, count)) {
+		spin_unlock_irqrestore(&mbox->lock, flags);
+		return -EFAULT;
+	}
+
+	regmap_update_bits(mbox->regmap, NPCM7XX_MBOX_BMBXCMD,
+			   NPCM7XX_MBOX_ALL_HIF, NPCM7XX_MBOX_HIF_0);
+
+	spin_unlock_irqrestore(&mbox->lock, flags);
+	return count;
+}
+
+static unsigned int npcm7xx_mbox_poll(struct file *file, poll_table *wait)
+{
+	struct npcm7xx_mbox *mbox = file_mbox(file);
+	unsigned int mask = 0;
+
+	poll_wait(file, &mbox->queue, wait);
+	if (mbox->cif0)
+		mask |= POLLIN;
+
+	return mask;
+}
+
+static int npcm7xx_mbox_release(struct inode *inode, struct file *file)
+{
+	atomic_dec(&npcm7xx_mbox_open_count);
+	return 0;
+}
+
+static const struct file_operations npcm7xx_mbox_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_seek_end_llseek,
+	.read		= npcm7xx_mbox_read,
+	.write		= npcm7xx_mbox_write,
+	.open		= npcm7xx_mbox_open,
+	.release	= npcm7xx_mbox_release,
+	.poll		= npcm7xx_mbox_poll,
+};
+
+static irqreturn_t npcm7xx_mbox_irq(int irq, void *arg)
+{
+	struct npcm7xx_mbox *mbox = arg;
+	u32 val;
+
+	regmap_read(mbox->regmap, NPCM7XX_MBOX_BMBXSTAT, &val);
+	if ((val & NPCM7XX_MBOX_CIF_0) != NPCM7XX_MBOX_CIF_0)
+		return IRQ_NONE;
+
+	/*
+	 * Leave the status bit set so that we know the data is for us,
+	 * clear it once it has been read.
+	 */
+	mbox->cif0 = true;
+
+	/* Mask it off, we'll clear it when we the data gets read */
+	regmap_write_bits(mbox->regmap, NPCM7XX_MBOX_BMBXSTAT,
+			  NPCM7XX_MBOX_ALL_CIF, NPCM7XX_MBOX_CIF_0);
+
+	wake_up(&mbox->queue);
+
+	return IRQ_HANDLED;
+}
+
+static int npcm7xx_mbox_config_irq(struct npcm7xx_mbox *mbox,
+				   struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	int rc, irq;
+	u32 val;
+
+	/* Disable all register based interrupts */
+	regmap_update_bits(mbox->regmap, NPCM7XX_MBOX_BMBXCTL,
+			   NPCM7XX_MBOX_ALL_CIE, 0);
+/*
+ * These registers are write one to clear. Clear them.
+ * Per spec, cleared bits should not be re-cleared.
+ * Need to read and clear needed bits only, instead of blindly clearing all.
+ */
+	regmap_read(mbox->regmap, NPCM7XX_MBOX_BMBXSTAT, &val);
+	val &= NPCM7XX_MBOX_ALL_CIF;
+
+	/* If any bit is set, write back to clear */
+	if (val)
+		regmap_write_bits(mbox->regmap, NPCM7XX_MBOX_BMBXSTAT,
+				  NPCM7XX_MBOX_ALL_CIF, val);
+
+	irq = irq_of_parse_and_map(dev->of_node, 0);
+	if (!irq)
+		return -ENODEV;
+
+	rc = devm_request_irq(dev, irq, npcm7xx_mbox_irq, 0, DEVICE_NAME, mbox);
+	if (rc < 0) {
+		dev_err(dev, "Unable to request IRQ %d\n", irq);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int npcm7xx_mbox_probe(struct platform_device *pdev)
+{
+	struct npcm7xx_mbox *mbox;
+	struct device *dev;
+	struct resource *res;
+	int rc;
+
+	dev = &pdev->dev;
+
+	mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
+	if (!mbox)
+		return -ENOMEM;
+
+	dev_set_drvdata(&pdev->dev, mbox);
+
+	mbox->regmap = syscon_node_to_regmap(dev->of_node);
+	if (IS_ERR(mbox->regmap)) {
+		dev_err(dev, "Couldn't get regmap\n");
+		return -ENODEV;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	mbox->memory = devm_ioremap_resource(dev, res);
+	if (IS_ERR(mbox->memory))
+		return PTR_ERR(mbox->memory);
+	mbox->max_buf_size = resource_size(res);
+
+	spin_lock_init(&mbox->lock);
+	init_waitqueue_head(&mbox->queue);
+
+	mbox->miscdev.minor = MISC_DYNAMIC_MINOR;
+	mbox->miscdev.name = DEVICE_NAME;
+	mbox->miscdev.fops = &npcm7xx_mbox_fops;
+	mbox->miscdev.parent = dev;
+	mbox->cif0 = false;
+	rc = misc_register(&mbox->miscdev);
+	if (rc) {
+		dev_err(dev, "Unable to register device\n");
+		return rc;
+	}
+
+	rc = npcm7xx_mbox_config_irq(mbox, pdev);
+	if (rc) {
+		dev_err(dev, "Failed to configure IRQ\n");
+		misc_deregister(&mbox->miscdev);
+		return rc;
+	}
+
+	pr_info("NPCM7xx PCI Mailbox probed\n");
+
+	return 0;
+}
+
+static int npcm7xx_mbox_remove(struct platform_device *pdev)
+{
+	struct npcm7xx_mbox *mbox = dev_get_drvdata(&pdev->dev);
+
+	misc_deregister(&mbox->miscdev);
+
+	return 0;
+}
+
+static const struct of_device_id npcm7xx_mbox_match[] = {
+	{ .compatible = "nuvoton,npcm750-pci-mbox" },
+	{ },
+};
+
+static struct platform_driver npcm7xx_mbox_driver = {
+	.driver = {
+		.name		= DEVICE_NAME,
+		.of_match_table = npcm7xx_mbox_match,
+	},
+	.probe = npcm7xx_mbox_probe,
+	.remove = npcm7xx_mbox_remove,
+};
+
+module_platform_driver(npcm7xx_mbox_driver);
+
+MODULE_DEVICE_TABLE(of, npcm7xx_mbox_match);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_DESCRIPTION("NPCM7XX mailbox device driver");
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
index 44fe801..b53f825 100644
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -66,6 +66,14 @@
 	help
 	  This enables support for hisilicon SPI-NOR flash controller.
 
+config SPI_NPCM_FIU
+	tristate "NPCM FLASH Interface unit(FIU) controller "
+	depends on ARCH_NPCM || COMPILE_TEST
+	help
+	  This enables support for the FLASH Interface unit(FIU) controller.
+	  This driver does not support generic SPI. The implementation only
+	  supports SPI NOR.
+
 config SPI_NXP_SPIFI
 	tristate "NXP SPI Flash Interface (SPIFI)"
 	depends on OF && (ARCH_LPC18XX || COMPILE_TEST)
diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile
index a552efd..875619e 100644
--- a/drivers/mtd/spi-nor/Makefile
+++ b/drivers/mtd/spi-nor/Makefile
@@ -5,6 +5,7 @@
 obj-$(CONFIG_SPI_FSL_QUADSPI)	+= fsl-quadspi.o
 obj-$(CONFIG_SPI_HISI_SFC)	+= hisi-sfc.o
 obj-$(CONFIG_MTD_MT81xx_NOR)    += mtk-quadspi.o
+obj-$(CONFIG_SPI_NPCM_FIU)	+= npcm-fiu.o
 obj-$(CONFIG_SPI_NXP_SPIFI)	+= nxp-spifi.o
 obj-$(CONFIG_SPI_INTEL_SPI)	+= intel-spi.o
 obj-$(CONFIG_SPI_INTEL_SPI_PCI)	+= intel-spi-pci.o
diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
index 95e5446..aefa795 100644
--- a/drivers/mtd/spi-nor/aspeed-smc.c
+++ b/drivers/mtd/spi-nor/aspeed-smc.c
@@ -10,6 +10,7 @@
  */
 
 #include <linux/bug.h>
+#include <linux/clk.h>
 #include <linux/device.h>
 #include <linux/io.h>
 #include <linux/module.h>
@@ -20,6 +21,7 @@
 #include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/sizes.h>
+#include <linux/slab.h>
 #include <linux/sysfs.h>
 
 #define DEVICE_NAME	"aspeed-smc"
@@ -41,12 +43,16 @@ struct aspeed_smc_info {
 	bool hastype;		/* flash type field exists in config reg */
 	u8 we0;			/* shift for write enable bit for CE0 */
 	u8 ctl0;		/* offset in regs of ctl for CE0 */
+	u8 timing;		/* offset in regs of timing */
 
 	void (*set_4b)(struct aspeed_smc_chip *chip);
+	int (*optimize_read)(struct aspeed_smc_chip *chip, u32 max_freq);
 };
 
 static void aspeed_smc_chip_set_4b_spi_2400(struct aspeed_smc_chip *chip);
 static void aspeed_smc_chip_set_4b(struct aspeed_smc_chip *chip);
+static int aspeed_smc_optimize_read(struct aspeed_smc_chip *chip,
+				     u32 max_freq);
 
 static const struct aspeed_smc_info fmc_2400_info = {
 	.maxsize = 64 * 1024 * 1024,
@@ -54,7 +60,9 @@ static const struct aspeed_smc_info fmc_2400_info = {
 	.hastype = true,
 	.we0 = 16,
 	.ctl0 = 0x10,
+	.timing = 0x94,
 	.set_4b = aspeed_smc_chip_set_4b,
+	.optimize_read = aspeed_smc_optimize_read,
 };
 
 static const struct aspeed_smc_info spi_2400_info = {
@@ -63,7 +71,9 @@ static const struct aspeed_smc_info spi_2400_info = {
 	.hastype = false,
 	.we0 = 0,
 	.ctl0 = 0x04,
+	.timing = 0x14,
 	.set_4b = aspeed_smc_chip_set_4b_spi_2400,
+	.optimize_read = aspeed_smc_optimize_read,
 };
 
 static const struct aspeed_smc_info fmc_2500_info = {
@@ -72,7 +82,9 @@ static const struct aspeed_smc_info fmc_2500_info = {
 	.hastype = true,
 	.we0 = 16,
 	.ctl0 = 0x10,
+	.timing = 0x94,
 	.set_4b = aspeed_smc_chip_set_4b,
+	.optimize_read = aspeed_smc_optimize_read,
 };
 
 static const struct aspeed_smc_info spi_2500_info = {
@@ -81,7 +93,9 @@ static const struct aspeed_smc_info spi_2500_info = {
 	.hastype = false,
 	.we0 = 16,
 	.ctl0 = 0x10,
+	.timing = 0x94,
 	.set_4b = aspeed_smc_chip_set_4b,
+	.optimize_read = aspeed_smc_optimize_read,
 };
 
 enum aspeed_smc_ctl_reg_value {
@@ -102,6 +116,7 @@ struct aspeed_smc_chip {
 	u32 ctl_val[smc_max];			/* control settings */
 	enum aspeed_smc_flash_type type;	/* what type of flash */
 	struct spi_nor nor;
+	u32 clk_rate;
 };
 
 struct aspeed_smc_controller {
@@ -113,9 +128,13 @@ struct aspeed_smc_controller {
 	void __iomem *ahb_base;			/* per-chip windows resource */
 	u32 ahb_window_size;			/* full mapping window size */
 
+	unsigned long	clk_frequency;
+
 	struct aspeed_smc_chip *chips[0];	/* pointers to attached chips */
 };
 
+#define ASPEED_SPI_DEFAULT_FREQ		50000000
+
 /*
  * SPI Flash Configuration Register (AST2500 SPI)
  *     or
@@ -202,6 +221,12 @@ struct aspeed_smc_controller {
 	((controller)->regs + SEGMENT_ADDR_REG0 + (cs) * 4)
 
 /*
+ * Switch to turn off read optimisation if needed
+ */
+static bool optimize_read = true;
+module_param(optimize_read, bool, 0644);
+
+/*
  * In user mode all data bytes read or written to the chip decode address
  * range are transferred to or from the SPI bus. The range is treated as a
  * fifo of arbitratry 1, 2, or 4 byte width but each write has to be aligned
@@ -373,18 +398,49 @@ static void aspeed_smc_send_cmd_addr(struct spi_nor *nor, u8 cmd, u32 addr)
 	}
 }
 
+static int aspeed_smc_get_io_mode(struct aspeed_smc_chip *chip)
+{
+	switch (chip->nor.read_proto) {
+	case SNOR_PROTO_1_1_1:
+		return 0;
+	case SNOR_PROTO_1_1_2:
+		return CONTROL_IO_DUAL_DATA;
+	case SNOR_PROTO_1_2_2:
+		return CONTROL_IO_DUAL_ADDR_DATA;
+	default:
+		dev_err(chip->nor.dev, "unsupported SPI read mode\n");
+		return -EINVAL;
+	}
+}
+
+static void aspeed_smc_set_io_mode(struct aspeed_smc_chip *chip, u32 io_mode)
+{
+	u32 ctl;
+
+	if (io_mode > 0) {
+		ctl = readl(chip->ctl) & ~CONTROL_IO_MODE_MASK;
+		ctl |= io_mode;
+		writel(ctl, chip->ctl);
+	}
+}
+
 static ssize_t aspeed_smc_read_user(struct spi_nor *nor, loff_t from,
 				    size_t len, u_char *read_buf)
 {
 	struct aspeed_smc_chip *chip = nor->priv;
 	int i;
 	u8 dummy = 0xFF;
+	int io_mode = aspeed_smc_get_io_mode(chip);
 
 	aspeed_smc_start_user(nor);
 	aspeed_smc_send_cmd_addr(nor, nor->read_opcode, from);
 	for (i = 0; i < chip->nor.read_dummy / 8; i++)
 		aspeed_smc_write_to_ahb(chip->ahb_base, &dummy, sizeof(dummy));
 
+	/* Set IO mode only for data */
+	if (io_mode == CONTROL_IO_DUAL_DATA)
+		aspeed_smc_set_io_mode(chip, io_mode);
+
 	aspeed_smc_read_from_ahb(read_buf, chip->ahb_base, len);
 	aspeed_smc_stop_user(nor);
 	return len;
@@ -402,6 +458,31 @@ static ssize_t aspeed_smc_write_user(struct spi_nor *nor, loff_t to,
 	return len;
 }
 
+static ssize_t aspeed_smc_read(struct spi_nor *nor, loff_t from, size_t len,
+			       u_char *read_buf)
+{
+	struct aspeed_smc_chip *chip = nor->priv;
+
+	/*
+	 * The AHB window configured for the chip is too small for the
+	 * read offset. Use the "User mode" of the controller to
+	 * perform the read.
+	 */
+	if (from >= chip->ahb_window_size) {
+		aspeed_smc_read_user(nor, from, len, read_buf);
+		goto out;
+	}
+
+	/*
+	 * Use the "Command mode" to do a direct read from the AHB
+	 * window configured for the chip. This should be the default.
+	 */
+	memcpy_fromio(read_buf, chip->ahb_base + from, len);
+
+out:
+	return len;
+}
+
 static int aspeed_smc_unregister(struct aspeed_smc_controller *controller)
 {
 	struct aspeed_smc_chip *chip;
@@ -706,10 +787,199 @@ static int aspeed_smc_chip_setup_init(struct aspeed_smc_chip *chip,
 	return 0;
 }
 
+
+#define CALIBRATE_BUF_SIZE 16384
+
+static bool aspeed_smc_check_reads(struct aspeed_smc_chip *chip,
+				  const u8 *golden_buf, u8 *test_buf)
+{
+	int i;
+
+	for (i = 0; i < 10; i++) {
+		aspeed_smc_read_from_ahb(test_buf, chip->ahb_base,
+					 CALIBRATE_BUF_SIZE);
+		if (memcmp(test_buf, golden_buf, CALIBRATE_BUF_SIZE) != 0)
+			return false;
+	}
+	return true;
+}
+
+static int aspeed_smc_calibrate_reads(struct aspeed_smc_chip *chip, u32 hdiv,
+				      const u8 *golden_buf, u8 *test_buf)
+{
+	struct aspeed_smc_controller *controller = chip->controller;
+	const struct aspeed_smc_info *info = controller->info;
+	int i;
+	int good_pass = -1, pass_count = 0;
+	u32 shift = (hdiv - 1) << 2;
+	u32 mask = ~(0xfu << shift);
+	u32 fread_timing_val = 0;
+
+#define FREAD_TPASS(i)	(((i) / 2) | (((i) & 1) ? 0 : 8))
+
+	/* Try HCLK delay 0..5, each one with/without delay and look for a
+	 * good pair.
+	 */
+	for (i = 0; i < 12; i++) {
+		bool pass;
+
+		fread_timing_val &= mask;
+		fread_timing_val |= FREAD_TPASS(i) << shift;
+
+		writel(fread_timing_val, controller->regs + info->timing);
+		pass = aspeed_smc_check_reads(chip, golden_buf, test_buf);
+		dev_dbg(chip->nor.dev,
+			"  * [%08x] %d HCLK delay, %dns DI delay : %s",
+			fread_timing_val, i/2, (i & 1) ? 0 : 4,
+			pass ? "PASS" : "FAIL");
+		if (pass) {
+			pass_count++;
+			if (pass_count == 3) {
+				good_pass = i - 1;
+				break;
+			}
+		} else
+			pass_count = 0;
+	}
+
+	/* No good setting for this frequency */
+	if (good_pass < 0)
+		return -1;
+
+	/* We have at least one pass of margin, let's use first pass */
+	fread_timing_val &= mask;
+	fread_timing_val |= FREAD_TPASS(good_pass) << shift;
+	writel(fread_timing_val, controller->regs + info->timing);
+	dev_dbg(chip->nor.dev, " * -> good is pass %d [0x%08x]",
+		good_pass, fread_timing_val);
+	return 0;
+}
+
+static bool aspeed_smc_check_calib_data(const u8 *test_buf, u32 size)
+{
+	const u32 *tb32 = (const u32 *) test_buf;
+	u32 i, cnt = 0;
+
+	/* We check if we have enough words that are neither all 0
+	 * nor all 1's so the calibration can be considered valid.
+	 *
+	 * I use an arbitrary threshold for now of 64
+	 */
+	size >>= 2;
+	for (i = 0; i < size; i++) {
+		if (tb32[i] != 0 && tb32[i] != 0xffffffff)
+			cnt++;
+	}
+	return cnt >= 64;
+}
+
+static const uint32_t aspeed_smc_hclk_divs[] = {
+	0xf, /* HCLK */
+	0x7, /* HCLK/2 */
+	0xe, /* HCLK/3 */
+	0x6, /* HCLK/4 */
+	0xd, /* HCLK/5 */
+};
+#define ASPEED_SMC_HCLK_DIV(i) (aspeed_smc_hclk_divs[(i) - 1] << 8)
+
+static u32 aspeed_smc_default_read(struct aspeed_smc_chip *chip)
+{
+	/*
+	 * Keep the 4Byte address mode on the AST2400 SPI controller.
+	 * Other controllers set the 4Byte mode in the CE Control
+	 * Register
+	 */
+	u32 ctl_mask = chip->controller->info == &spi_2400_info ?
+		 CONTROL_IO_ADDRESS_4B : 0;
+	u8 cmd = chip->nor.flags & SNOR_F_4B_OPCODES ? SPINOR_OP_READ_4B :
+		SPINOR_OP_READ;
+
+	/*
+	 * Use the "read command" mode to customize the opcode. In
+	 * normal command mode, the value is necessarily READ (0x3) on
+	 * the AST2400/2500 SoCs.
+	 */
+	return (chip->ctl_val[smc_read] & ctl_mask) |
+		(0x00 << 28) | /* Single bit */
+		(0x00 << 24) | /* CE# max */
+		(cmd  << 16) | /* use read mode to support 4B opcode */
+		(0x00 <<  8) | /* HCLK/16 */
+		(0x00 <<  6) | /* no dummy cycle */
+		(0x01);        /* read mode */
+}
+
+static int aspeed_smc_optimize_read(struct aspeed_smc_chip *chip,
+				     u32 max_freq)
+{
+	u8 *golden_buf, *test_buf;
+	int i, rc, best_div = -1;
+	u32 save_read_val = chip->ctl_val[smc_read];
+	u32 ahb_freq = chip->controller->clk_frequency;
+
+	dev_dbg(chip->nor.dev, "AHB frequency: %d MHz", ahb_freq / 1000000);
+
+	test_buf = kmalloc(CALIBRATE_BUF_SIZE * 2, GFP_KERNEL);
+	golden_buf = test_buf + CALIBRATE_BUF_SIZE;
+
+	/* We start with the dumbest setting (keep 4Byte bit) and read
+	 * some data
+	 */
+	chip->ctl_val[smc_read] = aspeed_smc_default_read(chip);
+
+	writel(chip->ctl_val[smc_read], chip->ctl);
+
+	aspeed_smc_read_from_ahb(golden_buf, chip->ahb_base,
+				 CALIBRATE_BUF_SIZE);
+
+	/* Establish our read mode with freq field set to 0 (HCLK/16) */
+	chip->ctl_val[smc_read] = save_read_val & 0xfffff0ff;
+
+	/* Check if calibration data is suitable */
+	if (!aspeed_smc_check_calib_data(golden_buf, CALIBRATE_BUF_SIZE)) {
+		dev_info(chip->nor.dev,
+			 "Calibration area too uniform, using low speed");
+		writel(chip->ctl_val[smc_read], chip->ctl);
+		kfree(test_buf);
+		return 0;
+	}
+
+	/* Now we iterate the HCLK dividers until we find our breaking point */
+	for (i = ARRAY_SIZE(aspeed_smc_hclk_divs); i > 0; i--) {
+		u32 tv, freq;
+
+		/* Compare timing to max */
+		freq = ahb_freq / i;
+		if (freq >= max_freq)
+			continue;
+
+		/* Set the timing */
+		tv = chip->ctl_val[smc_read] | ASPEED_SMC_HCLK_DIV(i);
+		writel(tv, chip->ctl);
+		dev_dbg(chip->nor.dev, "Trying HCLK/%d...", i);
+		rc = aspeed_smc_calibrate_reads(chip, i, golden_buf, test_buf);
+		if (rc == 0)
+			best_div = i;
+	}
+	kfree(test_buf);
+
+	/* Nothing found ? */
+	if (best_div < 0)
+		dev_warn(chip->nor.dev, "No good frequency, using dumb slow");
+	else {
+		dev_dbg(chip->nor.dev, "Found good read timings at HCLK/%d",
+			best_div);
+		chip->ctl_val[smc_read] |= ASPEED_SMC_HCLK_DIV(best_div);
+	}
+
+	writel(chip->ctl_val[smc_read], chip->ctl);
+	return 0;
+}
+
 static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
 {
 	struct aspeed_smc_controller *controller = chip->controller;
 	const struct aspeed_smc_info *info = controller->info;
+	int io_mode;
 	u32 cmd;
 
 	if (chip->nor.addr_width == 4 && info->set_4b)
@@ -732,21 +1002,24 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
 	 * TODO: Adjust clocks if fast read is supported and interpret
 	 * SPI-NOR flags to adjust controller settings.
 	 */
-	if (chip->nor.read_proto == SNOR_PROTO_1_1_1) {
-		if (chip->nor.read_dummy == 0)
-			cmd = CONTROL_COMMAND_MODE_NORMAL;
-		else
-			cmd = CONTROL_COMMAND_MODE_FREAD;
-	} else {
-		dev_err(chip->nor.dev, "unsupported SPI read mode\n");
-		return -EINVAL;
-	}
+	io_mode = aspeed_smc_get_io_mode(chip);
+	if (io_mode < 0)
+		return io_mode;
 
-	chip->ctl_val[smc_read] |= cmd |
+	if (chip->nor.read_dummy == 0)
+		cmd = CONTROL_COMMAND_MODE_NORMAL;
+	else
+		cmd = CONTROL_COMMAND_MODE_FREAD;
+
+	chip->ctl_val[smc_read] |= cmd | io_mode |
+		chip->nor.read_opcode << CONTROL_COMMAND_SHIFT |
 		CONTROL_IO_DUMMY_SET(chip->nor.read_dummy / 8);
 
-	dev_dbg(controller->dev, "base control register: %08x\n",
+	dev_info(controller->dev, "read control register: %08x\n",
 		chip->ctl_val[smc_read]);
+
+	if (optimize_read && info->optimize_read)
+		info->optimize_read(chip, chip->clk_rate);
 	return 0;
 }
 
@@ -756,6 +1029,7 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
 	const struct spi_nor_hwcaps hwcaps = {
 		.mask = SNOR_HWCAPS_READ |
 			SNOR_HWCAPS_READ_FAST |
+			SNOR_HWCAPS_READ_1_1_2 |
 			SNOR_HWCAPS_PP,
 	};
 	const struct aspeed_smc_info *info = controller->info;
@@ -799,6 +1073,13 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
 			break;
 		}
 
+		if (of_property_read_u32(child, "spi-max-frequency",
+					 &chip->clk_rate)) {
+			chip->clk_rate = ASPEED_SPI_DEFAULT_FREQ;
+		}
+		dev_info(dev, "Using %d MHz SPI frequency\n",
+			 chip->clk_rate / 1000000);
+
 		chip->controller = controller;
 		chip->ctl = controller->regs + info->ctl0 + cs * 4;
 		chip->cs = cs;
@@ -809,7 +1090,7 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
 		nor->dev = dev;
 		nor->priv = chip;
 		spi_nor_set_flash_node(nor, child);
-		nor->read = aspeed_smc_read_user;
+		nor->read = aspeed_smc_read;
 		nor->write = aspeed_smc_write_user;
 		nor->read_reg = aspeed_smc_read_reg;
 		nor->write_reg = aspeed_smc_write_reg;
@@ -853,6 +1134,7 @@ static int aspeed_smc_probe(struct platform_device *pdev)
 	struct aspeed_smc_controller *controller;
 	const struct of_device_id *match;
 	const struct aspeed_smc_info *info;
+	struct clk *clk;
 	struct resource *res;
 	int ret;
 
@@ -884,6 +1166,12 @@ static int aspeed_smc_probe(struct platform_device *pdev)
 
 	controller->ahb_window_size = resource_size(res);
 
+	clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+	controller->clk_frequency = clk_get_rate(clk);
+	devm_clk_put(&pdev->dev, clk);
+
 	ret = aspeed_smc_setup_flash(controller, np, res);
 	if (ret)
 		dev_err(dev, "Aspeed SMC probe failed %d\n", ret);
diff --git a/drivers/mtd/spi-nor/npcm-fiu.c b/drivers/mtd/spi-nor/npcm-fiu.c
new file mode 100644
index 0000000..0007b83
--- /dev/null
+++ b/drivers/mtd/spi-nor/npcm-fiu.c
@@ -0,0 +1,929 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 Nuvoton Technology corporation.
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/spi-nor.h>
+#include <linux/vmalloc.h>
+#include <linux/regmap.h>
+#include <linux/log2.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of_device.h>
+
+#include <mtd/mtd-abi.h>
+
+/* Flash Interface Unit (FIU) Registers */
+#define NPCM_FIU_DRD_CFG		0x00
+#define NPCM_FIU_DWR_CFG		0x04
+#define NPCM_FIU_UMA_CFG		0x08
+#define NPCM_FIU_UMA_CTS		0x0C
+#define NPCM_FIU_UMA_CMD		0x10
+#define NPCM_FIU_UMA_ADDR		0x14
+#define NPCM_FIU_PRT_CFG		0x18
+#define NPCM_FIU_UMA_DW0		0x20
+#define NPCM_FIU_UMA_DW1		0x24
+#define NPCM_FIU_UMA_DW2		0x28
+#define NPCM_FIU_UMA_DW3		0x2C
+#define NPCM_FIU_UMA_DR0		0x30
+#define NPCM_FIU_UMA_DR1		0x34
+#define NPCM_FIU_UMA_DR2		0x38
+#define NPCM_FIU_UMA_DR3		0x3C
+#define NPCM_FIU_MAX_REG_LIMIT		0x80
+
+/* FIU Direct Read Configuration Register */
+#define NPCM_FIU_DRD_CFG_LCK		BIT(31)
+#define NPCM_FIU_DRD_CFG_R_BURST	GENMASK(25, 24)
+#define NPCM_FIU_DRD_CFG_ADDSIZ		GENMASK(17, 16)
+#define NPCM_FIU_DRD_CFG_DBW		GENMASK(13, 12)
+#define NPCM_FIU_DRD_CFG_ACCTYPE	GENMASK(9, 8)
+#define NPCM_FIU_DRD_CFG_RDCMD		GENMASK(7, 0)
+#define NPCM_FIU_DRD_ADDSIZ_SHIFT	16
+#define NPCM_FIU_DRD_DBW_SHIFT		12
+#define NPCM_FIU_DRD_ACCTYPE_SHIFT	8
+
+/* FIU Direct Write Configuration Register */
+#define NPCM_FIU_DWR_CFG_LCK		BIT(31)
+#define NPCM_FIU_DWR_CFG_W_BURST	GENMASK(25, 24)
+#define NPCM_FIU_DWR_CFG_ADDSIZ		GENMASK(17, 16)
+#define NPCM_FIU_DWR_CFG_ABPCK		GENMASK(11, 10)
+#define NPCM_FIU_DWR_CFG_DBPCK		GENMASK(9, 8)
+#define NPCM_FIU_DWR_CFG_WRCMD		GENMASK(7, 0)
+#define NPCM_FIU_DWR_ADDSIZ_SHIFT	16
+#define NPCM_FIU_DWR_ABPCK_SHIFT	10
+#define NPCM_FIU_DWR_DBPCK_SHIFT	8
+
+/* FIU UMA Configuration Register */
+#define NPCM_FIU_UMA_CFG_LCK		BIT(31)
+#define NPCM_FIU_UMA_CFG_CMMLCK		BIT(30)
+#define NPCM_FIU_UMA_CFG_RDATSIZ	GENMASK(28, 24)
+#define NPCM_FIU_UMA_CFG_DBSIZ		GENMASK(23, 21)
+#define NPCM_FIU_UMA_CFG_WDATSIZ	GENMASK(20, 16)
+#define NPCM_FIU_UMA_CFG_ADDSIZ		GENMASK(13, 11)
+#define NPCM_FIU_UMA_CFG_CMDSIZ		BIT(10)
+#define NPCM_FIU_UMA_CFG_RDBPCK		GENMASK(9, 8)
+#define NPCM_FIU_UMA_CFG_DBPCK		GENMASK(7, 6)
+#define NPCM_FIU_UMA_CFG_WDBPCK		GENMASK(5, 4)
+#define NPCM_FIU_UMA_CFG_ADBPCK		GENMASK(3, 2)
+#define NPCM_FIU_UMA_CFG_CMBPCK		GENMASK(1, 0)
+#define NPCM_FIU_UMA_CFG_ADBPCK_SHIFT	2
+#define NPCM_FIU_UMA_CFG_WDBPCK_SHIFT	4
+#define NPCM_FIU_UMA_CFG_DBPCK_SHIFT	6
+#define NPCM_FIU_UMA_CFG_RDBPCK_SHIFT	8
+#define NPCM_FIU_UMA_CFG_ADDSIZ_SHIFT	11
+#define NPCM_FIU_UMA_CFG_WDATSIZ_SHIFT	16
+#define NPCM_FIU_UMA_CFG_DBSIZ_SHIFT	21
+#define NPCM_FIU_UMA_CFG_RDATSIZ_SHIFT	24
+
+/* FIU UMA Control and Status Register */
+#define NPCM_FIU_UMA_CTS_RDYIE		BIT(25)
+#define NPCM_FIU_UMA_CTS_RDYST		BIT(24)
+#define NPCM_FIU_UMA_CTS_SW_CS		BIT(16)
+#define NPCM_FIU_UMA_CTS_DEV_NUM	GENMASK(9, 8)
+#define NPCM_FIU_UMA_CTS_EXEC_DONE	BIT(0)
+#define NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT	8
+
+/* FIU UMA Command Register */
+#define NPCM_FIU_UMA_CMD_DUM3		GENMASK(31, 24)
+#define NPCM_FIU_UMA_CMD_DUM2		GENMASK(23, 16)
+#define NPCM_FIU_UMA_CMD_DUM1		GENMASK(15, 8)
+#define NPCM_FIU_UMA_CMD_CMD		GENMASK(7, 0)
+
+/* FIU UMA Address Register */
+#define NPCM_FIU_UMA_ADDR_UMA_ADDR	GENMASK(31, 0)
+#define NPCM_FIU_UMA_ADDR_AB3		GENMASK(31, 24)
+#define NPCM_FIU_UMA_ADDR_AB2		GENMASK(23, 16)
+#define NPCM_FIU_UMA_ADDR_AB1		GENMASK(15, 8)
+#define NPCM_FIU_UMA_ADDR_AB0		GENMASK(7, 0)
+
+/* FIU UMA Write Data Bytes 0-3 Register */
+#define NPCM_FIU_UMA_DW0_WB3		GENMASK(31, 24)
+#define NPCM_FIU_UMA_DW0_WB2		GENMASK(23, 16)
+#define NPCM_FIU_UMA_DW0_WB1		GENMASK(15, 8)
+#define NPCM_FIU_UMA_DW0_WB0		GENMASK(7, 0)
+
+/* FIU UMA Write Data Bytes 4-7 Register */
+#define NPCM_FIU_UMA_DW1_WB7		GENMASK(31, 24)
+#define NPCM_FIU_UMA_DW1_WB6		GENMASK(23, 16)
+#define NPCM_FIU_UMA_DW1_WB5		GENMASK(15, 8)
+#define NPCM_FIU_UMA_DW1_WB4		GENMASK(7, 0)
+
+/* FIU UMA Write Data Bytes 8-11 Register */
+#define NPCM_FIU_UMA_DW2_WB11		GENMASK(31, 24)
+#define NPCM_FIU_UMA_DW2_WB10		GENMASK(23, 16)
+#define NPCM_FIU_UMA_DW2_WB9		GENMASK(15, 8)
+#define NPCM_FIU_UMA_DW2_WB8		GENMASK(7, 0)
+
+/* FIU UMA Write Data Bytes 12-15 Register */
+#define NPCM_FIU_UMA_DW3_WB15		GENMASK(31, 24)
+#define NPCM_FIU_UMA_DW3_WB14		GENMASK(23, 16)
+#define NPCM_FIU_UMA_DW3_WB13		GENMASK(15, 8)
+#define NPCM_FIU_UMA_DW3_WB12		GENMASK(7, 0)
+
+/* FIU UMA Read Data Bytes 0-3 Register */
+#define NPCM_FIU_UMA_DR0_RB3		GENMASK(31, 24)
+#define NPCM_FIU_UMA_DR0_RB2		GENMASK(23, 16)
+#define NPCM_FIU_UMA_DR0_RB1		GENMASK(15, 8)
+#define NPCM_FIU_UMA_DR0_RB0		GENMASK(7, 0)
+
+/* FIU UMA Read Data Bytes 4-7 Register */
+#define NPCM_FIU_UMA_DR1_RB15		GENMASK(31, 24)
+#define NPCM_FIU_UMA_DR1_RB14		GENMASK(23, 16)
+#define NPCM_FIU_UMA_DR1_RB13		GENMASK(15, 8)
+#define NPCM_FIU_UMA_DR1_RB12		GENMASK(7, 0)
+
+/* FIU UMA Read Data Bytes 8-11 Register */
+#define NPCM_FIU_UMA_DR2_RB15		GENMASK(31, 24)
+#define NPCM_FIU_UMA_DR2_RB14		GENMASK(23, 16)
+#define NPCM_FIU_UMA_DR2_RB13		GENMASK(15, 8)
+#define NPCM_FIU_UMA_DR2_RB12		GENMASK(7, 0)
+
+/* FIU UMA Read Data Bytes 12-15 Register */
+#define NPCM_FIU_UMA_DR3_RB15		GENMASK(31, 24)
+#define NPCM_FIU_UMA_DR3_RB14		GENMASK(23, 16)
+#define NPCM_FIU_UMA_DR3_RB13		GENMASK(15, 8)
+#define NPCM_FIU_UMA_DR3_RB12		GENMASK(7, 0)
+
+/* FIU Read Mode */
+enum {
+	DRD_SINGLE_WIRE_MODE	= 0,
+	DRD_DUAL_IO_MODE	= 1,
+	DRD_QUAD_IO_MODE	= 2,
+	DRD_SPI_X_MODE		= 3,
+};
+
+enum {
+	DWR_ABPCK_BIT_PER_CLK	= 0,
+	DWR_ABPCK_2_BIT_PER_CLK	= 1,
+	DWR_ABPCK_4_BIT_PER_CLK	= 2,
+};
+
+enum {
+	DWR_DBPCK_BIT_PER_CLK	= 0,
+	DWR_DBPCK_2_BIT_PER_CLK	= 1,
+	DWR_DBPCK_4_BIT_PER_CLK	= 2,
+};
+
+#define NPCM_FIU_DRD_16_BYTE_BURST	0x3000000
+#define NPCM_FIU_DWR_16_BYTE_BURST	0x3000000
+
+#define MAP_SIZE_128MB			0x8000000
+#define MAP_SIZE_16MB			0x1000000
+#define MAP_SIZE_8MB			0x800000
+
+#define NUM_BITS_IN_BYTE		8
+#define FIU_DRD_MAX_DUMMY_NUMBER	3
+#define NPCM_MAX_CHIP_NUM		4
+#define CHUNK_SIZE			16
+#define UMA_MICRO_SEC_TIMEOUT		150
+
+enum {
+	FIU0 = 0,
+	FIU3,
+	FIUX,
+};
+
+struct npcm_fiu_info {
+	char *name;
+	u32 fiu_id;
+	u32 max_map_size;
+	u32 max_cs;
+};
+
+struct fiu_data {
+	const struct npcm_fiu_info *npcm_fiu_data_info;
+	int fiu_max;
+};
+
+static const struct npcm_fiu_info npxm7xx_fiu_info[] = {
+	{.name = "FIU0", .fiu_id = FIU0,
+		.max_map_size = MAP_SIZE_128MB, .max_cs = 2},
+	{.name = "FIU3", .fiu_id = FIU3,
+		.max_map_size = MAP_SIZE_128MB, .max_cs = 4},
+	{.name = "FIUX", .fiu_id = FIUX,
+		.max_map_size = MAP_SIZE_16MB, .max_cs = 2} };
+
+static const struct fiu_data npxm7xx_fiu_data = {
+	.npcm_fiu_data_info = npxm7xx_fiu_info,
+	.fiu_max = 3,
+};
+
+struct npcm_fiu_bus;
+
+struct npcm_chip {
+	void __iomem *flash_region_mapped_ptr;
+	enum spi_nor_protocol direct_rd_proto;
+	struct npcm_fiu_bus *host;
+	struct spi_nor nor;
+	bool direct_read;
+	u32 read_proto;
+	u32 chipselect;
+	u32 clkrate;
+};
+
+struct npcm_fiu_bus {
+	struct npcm_chip *chip[NPCM_MAX_CHIP_NUM];
+	enum spi_nor_protocol direct_rd_proto;
+	const struct npcm_fiu_info *info;
+	struct resource *res_mem;
+	resource_size_t iosize;
+	struct regmap *regmap;
+	void __iomem *regbase;
+	u32 direct_read_proto;
+	struct device *dev;
+	struct mutex lock;	/* controller access mutex */
+	struct clk *clk;
+	bool spix_mode;
+	int id;
+};
+
+static const struct regmap_config npcm_mtd_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = NPCM_FIU_MAX_REG_LIMIT,
+};
+
+static int npcm_fiu_direct_read(struct mtd_info *mtd, loff_t from, size_t len,
+				size_t *retlen, u_char *buf)
+{
+	struct spi_nor *nor = mtd->priv;
+	struct npcm_chip *chip = nor->priv;
+
+	memcpy_fromio(buf, chip->flash_region_mapped_ptr + from, len);
+
+	*retlen = len;
+	return 0;
+}
+
+static int npcm_fiu_direct_write(struct mtd_info *mtd, loff_t to, size_t len,
+				 size_t *retlen, const u_char *buf)
+{
+	struct spi_nor *nor = mtd->priv;
+	struct npcm_chip *chip = nor->priv;
+
+	memcpy_toio(chip->flash_region_mapped_ptr + to, buf, len);
+
+	*retlen = len;
+	return 0;
+}
+
+static int npcm_fiu_uma_read(struct spi_nor *nor, u8 transaction_code,
+			     u32 address, bool is_address_size, u8 *data,
+			     u32 data_size)
+{
+	struct npcm_chip *chip = nor->priv;
+	struct npcm_fiu_bus *host = chip->host;
+	u32 uma_cfg = BIT(10);
+	u32 dummy_bytes;
+	u32 data_reg[4];
+	int ret;
+	u32 val;
+	u32 i;
+
+	regmap_update_bits(host->regmap, NPCM_FIU_UMA_CTS,
+			   NPCM_FIU_UMA_CTS_DEV_NUM,
+			   (chip->chipselect <<
+			    NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT));
+	regmap_update_bits(host->regmap, NPCM_FIU_UMA_CMD,
+			   NPCM_FIU_UMA_CMD_CMD, transaction_code);
+	regmap_write(host->regmap, NPCM_FIU_UMA_ADDR, address);
+
+	if (is_address_size) {
+		uma_cfg |=
+			ilog2(spi_nor_get_protocol_inst_nbits(nor->read_proto));
+		uma_cfg |=
+			ilog2(spi_nor_get_protocol_addr_nbits(nor->read_proto))
+		<< NPCM_FIU_UMA_CFG_ADBPCK_SHIFT;
+		uma_cfg |=
+			ilog2(spi_nor_get_protocol_addr_nbits(nor->read_proto))
+		<< NPCM_FIU_UMA_CFG_DBPCK_SHIFT;
+		uma_cfg |=
+			ilog2(spi_nor_get_protocol_data_nbits(nor->read_proto))
+		<< NPCM_FIU_UMA_CFG_RDBPCK_SHIFT;
+		dummy_bytes =
+			(nor->read_dummy *
+			 spi_nor_get_protocol_addr_nbits(nor->read_proto)) /
+			NUM_BITS_IN_BYTE;
+		uma_cfg |= dummy_bytes << NPCM_FIU_UMA_CFG_DBSIZ_SHIFT;
+		uma_cfg |= (nor->addr_width << NPCM_FIU_UMA_CFG_ADDSIZ_SHIFT);
+	}
+
+	uma_cfg |= data_size << NPCM_FIU_UMA_CFG_RDATSIZ_SHIFT;
+	regmap_write(host->regmap, NPCM_FIU_UMA_CFG, uma_cfg);
+
+	regmap_write_bits(host->regmap, NPCM_FIU_UMA_CTS,
+			  NPCM_FIU_UMA_CTS_EXEC_DONE,
+			  NPCM_FIU_UMA_CTS_EXEC_DONE);
+
+	ret = regmap_read_poll_timeout(host->regmap, NPCM_FIU_UMA_CTS, val,
+				       (!(val & NPCM_FIU_UMA_CTS_EXEC_DONE)), 0,
+				       UMA_MICRO_SEC_TIMEOUT);
+	if (ret)
+		return ret;
+
+	if (data_size) {
+		for (i = 0; i <= data_size / 4; i++)
+			regmap_read(host->regmap, NPCM_FIU_UMA_DR0 + (i * 4),
+				    &data_reg[i]);
+		memcpy(data, data_reg, data_size);
+	}
+
+	return 0;
+}
+
+static int npcm_fiu_uma_write(struct spi_nor *nor, u8 transaction_code,
+			      u32 address, bool is_address_size, u8 *data,
+			      u32 data_size)
+{
+	struct npcm_chip *chip = nor->priv;
+	struct npcm_fiu_bus *host = chip->host;
+	u32 uma_cfg = BIT(10);
+	u32 data_reg[4] = {0};
+	u32 val;
+	u32 i;
+
+	regmap_update_bits(host->regmap, NPCM_FIU_UMA_CTS,
+			   NPCM_FIU_UMA_CTS_DEV_NUM,
+			   (chip->chipselect <<
+			    NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT));
+
+	regmap_update_bits(host->regmap, NPCM_FIU_UMA_CMD,
+			   NPCM_FIU_UMA_CMD_CMD, transaction_code);
+	regmap_write(host->regmap, NPCM_FIU_UMA_ADDR, address);
+
+	if (data_size) {
+		memcpy(data_reg, data, data_size);
+		for (i = 0; i <= data_size / 4; i++)
+			regmap_write(host->regmap, NPCM_FIU_UMA_DW0 + (i * 4),
+				     data_reg[i]);
+	}
+
+	if (is_address_size) {
+		uma_cfg |=
+			ilog2(spi_nor_get_protocol_inst_nbits
+			      (nor->write_proto));
+		uma_cfg |=
+			ilog2(spi_nor_get_protocol_addr_nbits(nor->write_proto))
+		<< NPCM_FIU_UMA_CFG_ADBPCK_SHIFT;
+		uma_cfg |=
+			ilog2(spi_nor_get_protocol_data_nbits(nor->write_proto))
+		<< NPCM_FIU_UMA_CFG_WDBPCK_SHIFT;
+		uma_cfg |= (nor->addr_width << NPCM_FIU_UMA_CFG_ADDSIZ_SHIFT);
+	}
+
+	uma_cfg |= (data_size << NPCM_FIU_UMA_CFG_WDATSIZ_SHIFT);
+	regmap_write(host->regmap, NPCM_FIU_UMA_CFG, uma_cfg);
+
+	regmap_write_bits(host->regmap, NPCM_FIU_UMA_CTS,
+			  NPCM_FIU_UMA_CTS_EXEC_DONE,
+			  NPCM_FIU_UMA_CTS_EXEC_DONE);
+
+	return regmap_read_poll_timeout(host->regmap, NPCM_FIU_UMA_CTS, val,
+				       (!(val & NPCM_FIU_UMA_CTS_EXEC_DONE)), 0,
+					UMA_MICRO_SEC_TIMEOUT);
+}
+
+static int npcm_fiu_manualwrite(struct spi_nor *nor, u8 transaction_code,
+				u32 address, u8 *data, u32 data_size)
+{
+	u32 num_data_chunks;
+	u32 remain_data;
+	u32 idx = 0;
+	int ret;
+
+	struct npcm_chip *chip = nor->priv;
+	struct npcm_fiu_bus *host = chip->host;
+
+	num_data_chunks  = data_size / CHUNK_SIZE;
+	remain_data  = data_size % CHUNK_SIZE;
+
+	regmap_update_bits(host->regmap, NPCM_FIU_UMA_CTS,
+			   NPCM_FIU_UMA_CTS_DEV_NUM,
+			   (chip->chipselect <<
+			    NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT));
+	regmap_update_bits(host->regmap, NPCM_FIU_UMA_CTS,
+			   NPCM_FIU_UMA_CTS_SW_CS, 0);
+
+	ret = npcm_fiu_uma_write(nor, transaction_code, address, true,
+				 NULL, 0);
+	if (ret)
+		return ret;
+
+	/* Starting the data writing loop in multiples of 8 */
+	for (idx = 0; idx < num_data_chunks; ++idx) {
+		ret = npcm_fiu_uma_write(nor, data[0], (u32)NULL, false,
+					 &data[1], CHUNK_SIZE - 1);
+		if (ret)
+			return ret;
+
+		data += CHUNK_SIZE;
+	}
+
+	/* Handling chunk remains */
+	if (remain_data > 0) {
+		ret = npcm_fiu_uma_write(nor, data[0], (u32)NULL, false,
+					 &data[1], remain_data - 1);
+		if (ret)
+			return ret;
+	}
+
+	regmap_update_bits(host->regmap, NPCM_FIU_UMA_CTS,
+			   NPCM_FIU_UMA_CTS_SW_CS, NPCM_FIU_UMA_CTS_SW_CS);
+
+	return 0;
+}
+
+static ssize_t npcm_fiu_write(struct spi_nor *nor, loff_t to,
+			      size_t len, const u_char *write_buf)
+{
+	u32 local_addr = (u32)to;
+	struct mtd_info *mtd;
+	u32 actual_size = 0;
+	u32 cnt = (u32)len;
+	int ret;
+
+	mtd = &nor->mtd;
+
+	if (cnt != 0) {
+		while (cnt) {
+			actual_size = ((((local_addr) / nor->page_size) + 1)
+				       * nor->page_size) - (local_addr);
+			if (actual_size > cnt)
+				actual_size = cnt;
+
+			ret = npcm_fiu_manualwrite(nor, nor->program_opcode,
+						   local_addr,
+						   (u_char *)write_buf,
+						   actual_size);
+			if (ret)
+				return ret;
+
+			write_buf += actual_size;
+			local_addr += actual_size;
+			cnt -= actual_size;
+		}
+	}
+
+	return (len - cnt);
+}
+
+static void npcm_fiu_set_drd(struct spi_nor *nor, struct npcm_fiu_bus *host)
+{
+	regmap_update_bits(host->regmap, NPCM_FIU_DRD_CFG,
+			   NPCM_FIU_DRD_CFG_ACCTYPE,
+			   ilog2(spi_nor_get_protocol_addr_nbits
+				 (nor->read_proto)) <<
+			   NPCM_FIU_DRD_ACCTYPE_SHIFT);
+	regmap_update_bits(host->regmap, NPCM_FIU_DRD_CFG,
+			   NPCM_FIU_DRD_CFG_DBW,
+			   ((nor->read_dummy *
+			     spi_nor_get_protocol_addr_nbits(nor->read_proto))
+			    / NUM_BITS_IN_BYTE) << NPCM_FIU_DRD_DBW_SHIFT);
+	regmap_update_bits(host->regmap, NPCM_FIU_DRD_CFG,
+			   NPCM_FIU_DRD_CFG_RDCMD, nor->read_opcode);
+	regmap_update_bits(host->regmap, NPCM_FIU_DRD_CFG,
+			   NPCM_FIU_DRD_CFG_ADDSIZ,
+			   (nor->addr_width - 3) << NPCM_FIU_DRD_ADDSIZ_SHIFT);
+}
+
+static ssize_t npcm_fiu_read(struct spi_nor *nor, loff_t from, size_t len,
+			     u_char *read_buf)
+{
+	struct npcm_chip *chip = nor->priv;
+	struct npcm_fiu_bus *host = chip->host;
+	int i, readlen, currlen;
+	struct mtd_info *mtd;
+	size_t retlen = 0;
+	u8 *buf_ptr;
+	u32 addr;
+	int ret;
+
+	mtd = &nor->mtd;
+
+	if (chip->direct_read) {
+		regmap_read(host->regmap, NPCM_FIU_DRD_CFG, &addr);
+		if (host->direct_rd_proto != chip->direct_rd_proto) {
+			npcm_fiu_set_drd(nor, host);
+			host->direct_rd_proto = chip->direct_rd_proto;
+		}
+		npcm_fiu_direct_read(mtd, from, len, &retlen, read_buf);
+	} else {
+		i = 0;
+		currlen = (int)len;
+
+		do {
+			addr = ((u32)from + i);
+			if (currlen < 4)
+				readlen = currlen;
+			else
+				readlen = 4;
+
+			buf_ptr = read_buf + i;
+			ret = npcm_fiu_uma_read(nor, nor->read_opcode, addr,
+						true, buf_ptr, readlen);
+			if (ret)
+				return ret;
+
+			i += readlen;
+			currlen -= 4;
+		} while (currlen > 0);
+
+		retlen = i;
+	}
+
+	return retlen;
+}
+
+static int npcm_fiu_erase(struct spi_nor *nor, loff_t offs)
+{
+	return npcm_fiu_uma_write(nor, nor->erase_opcode, (u32)offs, true,
+				  NULL, 0);
+}
+
+static int npcm_fiu_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
+			     int len)
+{
+	return npcm_fiu_uma_read(nor, opcode, 0, false, buf, len);
+}
+
+static int npcm_fiu_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
+			      int len)
+{
+	return npcm_fiu_uma_write(nor, opcode, 0, false, buf, len);
+}
+
+static int npcm_fiu_nor_prep(struct spi_nor *nor, enum spi_nor_ops ops)
+{
+	struct npcm_chip *chip = nor->priv;
+	struct npcm_fiu_bus *host = chip->host;
+
+	mutex_lock(&host->lock);
+
+	return 0;
+}
+
+static void npcm_fiu_nor_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
+{
+	struct npcm_chip *chip = nor->priv;
+	struct npcm_fiu_bus *host = chip->host;
+
+	mutex_unlock(&host->lock);
+}
+
+/* Expansion bus registers as mtd_ram device */
+static int npcm_mtd_ram_register(struct device_node *np,
+				 struct npcm_fiu_bus *host)
+{
+	struct device *dev = host->dev;
+	struct npcm_chip *chip;
+	struct mtd_info *mtd;
+	struct spi_nor *nor;
+	u32 chipselect;
+	u32 rx_dummy = 0;
+	int ret;
+
+	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	ret = of_property_read_u32(np, "reg", &chipselect);
+	if (ret) {
+		dev_err(dev, "There's no reg property for %s\n",
+			dev->of_node->full_name);
+		return ret;
+	}
+
+	of_property_read_u32(np, "npcm,fiu-spix-rx-dummy-num", &rx_dummy);
+	if (rx_dummy > FIU_DRD_MAX_DUMMY_NUMBER) {
+		dev_warn(dev, "npcm,fiu-spix-rx-dummy-num %d not supported\n",
+			 rx_dummy);
+		rx_dummy = 0;
+	}
+
+	chip->host = host;
+	chip->chipselect = chipselect;
+	nor = &chip->nor;
+	nor->dev = dev;
+	nor->priv = chip;
+	mtd = &nor->mtd;
+
+	chip->flash_region_mapped_ptr =
+		devm_ioremap(dev, (host->res_mem->start +
+				   (host->info->max_map_size *
+				    chip->chipselect)), MAP_SIZE_8MB);
+	if (!chip->flash_region_mapped_ptr) {
+		dev_err(dev, "Error mapping memory region!\n");
+		return -ENOMEM;
+	}
+
+	/* Populate mtd_info data structure */
+	*mtd = (struct mtd_info) {
+		.dev		= { .parent = dev },
+		.name		= "exp-bus",
+		.type		= MTD_RAM,
+		.priv		= nor,
+		.size		= MAP_SIZE_8MB,
+		.writesize	= 1,
+		.writebufsize	= 1,
+		.flags		= MTD_CAP_RAM,
+		._read		= npcm_fiu_direct_read,
+		._write		= npcm_fiu_direct_write,
+	};
+
+	/* set read and write direct to configuration to SPI-X mode */
+	regmap_write(host->regmap, NPCM_FIU_DRD_CFG,
+		     NPCM_FIU_DRD_16_BYTE_BURST);
+	regmap_update_bits(host->regmap, NPCM_FIU_DRD_CFG,
+			   NPCM_FIU_DRD_CFG_ACCTYPE,
+			   DRD_SPI_X_MODE << NPCM_FIU_DRD_ACCTYPE_SHIFT);
+	regmap_update_bits(host->regmap, NPCM_FIU_DRD_CFG,
+			   NPCM_FIU_DRD_CFG_DBW,
+			   rx_dummy << NPCM_FIU_DRD_DBW_SHIFT);
+	regmap_write(host->regmap, NPCM_FIU_DWR_CFG,
+		     NPCM_FIU_DWR_16_BYTE_BURST);
+	regmap_update_bits(host->regmap, NPCM_FIU_DWR_CFG,
+			   NPCM_FIU_DWR_CFG_ABPCK,
+			   DWR_ABPCK_4_BIT_PER_CLK << NPCM_FIU_DWR_ABPCK_SHIFT);
+	regmap_update_bits(host->regmap, NPCM_FIU_DWR_CFG,
+			   NPCM_FIU_DWR_CFG_DBPCK,
+			   DWR_DBPCK_4_BIT_PER_CLK << NPCM_FIU_DWR_DBPCK_SHIFT);
+
+	ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
+	if (ret)
+		return ret;
+
+	host->chip[chip->chipselect] = chip;
+
+	return 0;
+}
+
+static void npcm_fiu_enable_direct_rd(struct spi_nor *nor,
+				      struct npcm_fiu_bus *host,
+				      struct npcm_chip *chip)
+{
+	struct device *dev = host->dev;
+	u32 flashsize;
+
+	if (!host->res_mem) {
+		dev_warn(dev, "Reserved memory not defined, direct read disabled\n");
+		return;
+	}
+
+	/* direct read supports only I/O read mode */
+	if (nor->read_proto != SNOR_PROTO_1_1_1 &&
+	    nor->read_proto != SNOR_PROTO_1_2_2 &&
+	    nor->read_proto != SNOR_PROTO_1_4_4) {
+		dev_warn(dev, "Only Read I/O commands supported, direct read disabled\n");
+		return;
+	}
+
+	flashsize = (u32)(nor->mtd.size >> 10) * 1024;
+	if (flashsize == 0 || flashsize > host->info->max_map_size) {
+		dev_warn(dev, "Flash size ecxeed(0x%x) map size(0x%x), direct read disabled\n"
+			 , flashsize, host->info->max_map_size);
+		return;
+	}
+
+	chip->flash_region_mapped_ptr =
+		devm_ioremap(dev, (host->res_mem->start +
+				   (host->info->max_map_size *
+				    chip->chipselect)), flashsize);
+	if (!chip->flash_region_mapped_ptr) {
+		dev_warn(dev, "Error mapping memory region, direct read disabled\n");
+		return;
+	}
+
+	npcm_fiu_set_drd(nor, host);
+
+	host->direct_rd_proto = nor->read_proto;
+	chip->direct_rd_proto = nor->read_proto;
+	chip->direct_read = true;
+}
+
+/* Get spi flash device information and register it as a mtd device. */
+static int npcm_fiu_nor_register(struct device_node *np,
+				 struct npcm_fiu_bus *host)
+{
+	struct device *dev = host->dev;
+	struct npcm_chip *chip;
+	struct mtd_info *mtd;
+	struct spi_nor *nor;
+	u32 chipselect;
+	int ret;
+	u32 val;
+	struct spi_nor_hwcaps hwcaps = {
+		.mask = SNOR_HWCAPS_READ |
+			SNOR_HWCAPS_READ_FAST |
+			SNOR_HWCAPS_PP |
+			SNOR_HWCAPS_PP_1_1_4 |
+			SNOR_HWCAPS_PP_1_4_4 |
+			SNOR_HWCAPS_PP_4_4_4,
+	};
+
+	/* This driver mode supports only NOR flash devices. */
+	if (!of_device_is_compatible(np, "jedec,spi-nor")) {
+		dev_err(dev, "The device is no compatible to jedec,spi-nor\n");
+		return -ENOMEM;
+	}
+
+	ret = of_property_read_u32(np, "reg", &chipselect);
+	if (ret) {
+		dev_err(dev, "There's no reg property for %s\n", np->full_name);
+		return ret;
+	}
+
+	if (chipselect >= host->info->max_cs) {
+		dev_err(dev, "Flash device number exceeds the maximum chipselect number\n");
+		return -ENOMEM;
+	}
+
+	if (!of_property_read_u32(np, "spi-rx-bus-width", &val)) {
+		switch (val) {
+		case 1:
+			break;
+		case 2:
+			hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2
+				| SNOR_HWCAPS_READ_1_2_2
+				| SNOR_HWCAPS_READ_2_2_2;
+			break;
+		case 4:
+			hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4
+				 | SNOR_HWCAPS_READ_1_4_4
+				 | SNOR_HWCAPS_READ_4_4_4;
+				break;
+		default:
+			dev_warn(dev, "spi-rx-bus-width %d not supported\n", val);
+			break;
+		}
+	}
+
+	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->host = host;
+	chip->chipselect = chipselect;
+
+	nor = &chip->nor;
+	mtd = &nor->mtd;
+
+	nor->dev = dev;
+	nor->priv = chip;
+
+	spi_nor_set_flash_node(nor, np);
+
+	nor->prepare = npcm_fiu_nor_prep;
+	nor->unprepare = npcm_fiu_nor_unprep;
+	nor->read_reg = npcm_fiu_read_reg;
+	nor->write_reg = npcm_fiu_write_reg;
+	nor->read = npcm_fiu_read;
+	nor->write = npcm_fiu_write;
+	nor->erase = npcm_fiu_erase;
+
+	ret = spi_nor_scan(nor, NULL, &hwcaps);
+	if (ret)
+		return ret;
+
+	npcm_fiu_enable_direct_rd(nor, host, chip);
+	ret = mtd_device_register(mtd, NULL, 0);
+	if (ret) {
+		dev_err(dev, "MTD NOR device register failed\n");
+		return ret;
+	}
+
+	host->chip[chip->chipselect] = chip;
+	return 0;
+}
+
+static void npcm_fiu_unregister_all(struct npcm_fiu_bus *host)
+{
+	struct npcm_chip *chip;
+	int n;
+
+	for (n = 0; n < host->info->max_cs; n++) {
+		chip = host->chip[n];
+		if (chip)
+			mtd_device_unregister(&chip->nor.mtd);
+	}
+}
+
+static void npcm_fiu_register_all(struct npcm_fiu_bus *host)
+{
+	struct device *dev = host->dev;
+	struct device_node *np;
+	int ret;
+
+	for_each_available_child_of_node(dev->of_node, np) {
+		if (host->spix_mode)
+			ret = npcm_mtd_ram_register(np, host);
+		else
+			ret = npcm_fiu_nor_register(np, host);
+		if (ret)
+			dev_warn(dev, "npcm fiu %s registration failed\n", np->full_name);
+	}
+}
+
+static const struct of_device_id npcm_fiu_dt_ids[] = {
+	{ .compatible = "nuvoton,npcm750-fiu", .data = &npxm7xx_fiu_data  },
+	{ /* sentinel */ }
+};
+
+static int npcm_fiu_probe(struct platform_device *pdev)
+{
+	const struct fiu_data *fiu_data_match;
+	const struct of_device_id *match;
+	struct device *dev = &pdev->dev;
+	struct npcm_fiu_bus *host;
+	struct resource *res;
+
+	host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+	if (!host)
+		return -ENOMEM;
+
+	match = of_match_device(npcm_fiu_dt_ids, dev);
+	if (!match || !match->data) {
+		dev_err(dev, "No compatible OF match\n");
+		return -ENODEV;
+	}
+
+	fiu_data_match = match->data;
+
+	host->id = of_alias_get_id(dev->of_node, "fiu");
+	if (host->id < 0 || host->id >= fiu_data_match->fiu_max) {
+		dev_err(dev, "Invalid platform device id: %d\n", host->id);
+		return -EINVAL;
+	}
+
+	host->info = &fiu_data_match->npcm_fiu_data_info[host->id];
+
+	platform_set_drvdata(pdev, host);
+	host->dev = dev;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control");
+	host->regbase = devm_ioremap_resource(dev, res);
+	if (IS_ERR(host->regbase))
+		return PTR_ERR(host->regbase);
+
+	host->regmap = devm_regmap_init_mmio(dev, host->regbase,
+					     &npcm_mtd_regmap_config);
+	if (IS_ERR(host->regmap)) {
+		dev_err(dev, "Failed to create regmap\n");
+		return PTR_ERR(host->regmap);
+	}
+
+	host->res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						     "memory");
+	host->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(host->clk))
+		return PTR_ERR(host->clk);
+
+	host->spix_mode = of_property_read_bool(dev->of_node, "spix-mode");
+
+	mutex_init(&host->lock);
+	clk_prepare_enable(host->clk);
+
+	npcm_fiu_register_all(host);
+
+	dev_info(dev, "NPCM %s probe succeed\n", host->info->name);
+
+	return 0;
+}
+
+static int npcm_fiu_remove(struct platform_device *pdev)
+{
+	struct npcm_fiu_bus *host = platform_get_drvdata(pdev);
+
+	npcm_fiu_unregister_all(host);
+	mutex_destroy(&host->lock);
+	clk_disable_unprepare(host->clk);
+	return 0;
+}
+
+MODULE_DEVICE_TABLE(of, npcm_fiu_dt_ids);
+
+static struct platform_driver npcm_fiu_driver = {
+	.driver = {
+		.name	= "NPCM-FIU",
+		.bus	= &platform_bus_type,
+		.of_match_table = npcm_fiu_dt_ids,
+	},
+	.probe      = npcm_fiu_probe,
+	.remove	    = npcm_fiu_remove,
+};
+module_platform_driver(npcm_fiu_driver);
+
+MODULE_DESCRIPTION("Nuvoton FLASH Interface Unit SPI Controller Driver");
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 6e13bbd..5a5a476 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -1848,7 +1848,7 @@ static const struct flash_info spi_nor_ids[] = {
 			 .fixups = &mx25l25635_fixups },
 	{ "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) },
 	{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
-	{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+	{ "mx66l51235f", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
 	{ "mx66u51235f", INFO(0xc2253a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
 	{ "mx66l1g45g",  INFO(0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
 	{ "mx66l1g55g",  INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
diff --git a/drivers/net/ethernet/nuvoton/Kconfig b/drivers/net/ethernet/nuvoton/Kconfig
index 71c973f..b129976 100644
--- a/drivers/net/ethernet/nuvoton/Kconfig
+++ b/drivers/net/ethernet/nuvoton/Kconfig
@@ -5,7 +5,7 @@
 config NET_VENDOR_NUVOTON
 	bool "Nuvoton devices"
 	default y
-	depends on ARM && ARCH_W90X900
+	depends on ARM && (ARCH_W90X900 || ARCH_NPCM7XX)
 	---help---
 	  If you have a network (Ethernet) card belonging to this class, say Y.
 
@@ -25,4 +25,19 @@
 	  Say Y here if you want to use built-in Ethernet ports
 	  on w90p910 processor.
 
+config NPCM7XX_EMC_ETH
+	bool "Nuvoton NPCM7XX Ethernet EMC"
+	depends on ARM && ARCH_NPCM7XX
+	select PHYLIB
+	select MII
+	help
+	  Say Y here if you want to use built-in Ethernet MAC
+	  on NPCM750 MCU.
+
+config NPCM7XX_EMC_ETH_DEBUG
+	bool "Nuvoton NPCM7XX Ethernet EMC debug"
+	depends on NPCM7XX_EMC_ETH
+	help
+	  Say Y here if you want debug info via /proc/driver/npcm7xx_emc.x
+
 endif # NET_VENDOR_NUVOTON
diff --git a/drivers/net/ethernet/nuvoton/Makefile b/drivers/net/ethernet/nuvoton/Makefile
index 171aa04..513a606 100644
--- a/drivers/net/ethernet/nuvoton/Makefile
+++ b/drivers/net/ethernet/nuvoton/Makefile
@@ -2,4 +2,6 @@
 # Makefile for the Nuvoton network device drivers.
 #
 
+#Eternet 10/100 EMC
 obj-$(CONFIG_W90P910_ETH) += w90p910_ether.o
+obj-$(CONFIG_NPCM7XX_EMC_ETH) += npcm7xx_emc.o
diff --git a/drivers/net/ethernet/nuvoton/npcm7xx_emc.c b/drivers/net/ethernet/nuvoton/npcm7xx_emc.c
new file mode 100644
index 0000000..efb441d
--- /dev/null
+++ b/drivers/net/ethernet/nuvoton/npcm7xx_emc.c
@@ -0,0 +1,2091 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2014-2019 Nuvoton Technology corporation.
+
+#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/platform_device.h>
+#include <linux/gfp.h>
+#include <linux/kthread.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
+
+#include <linux/clk.h>
+
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/of_device.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include <linux/if_ether.h>
+
+#include <net/ip.h>
+#include <net/ncsi.h>
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *npcm7xx_fs_dir;
+#endif
+
+#define  MFSEL1_OFFSET 0x00C
+#define  MFSEL3_OFFSET 0x064
+#define  INTCR_OFFSET  0x03C
+
+#define  IPSRST1_OFFSET 0x020
+
+#define DRV_MODULE_NAME		"npcm7xx-emc"
+#define DRV_MODULE_VERSION	"3.90"
+
+/* Ethernet MAC Registers */
+#define REG_CAMCMR	0x00
+#define REG_CAMEN	0x04
+#define REG_CAMM_BASE	0x08
+#define REG_CAML_BASE	0x0c
+#define REG_TXDLSA	0x88
+#define REG_RXDLSA	0x8C
+#define REG_MCMDR	0x90
+#define REG_MIID	0x94
+#define REG_MIIDA	0x98
+#define REG_FFTCR	0x9C
+#define REG_TSDR	0xa0
+#define REG_RSDR	0xa4
+#define REG_DMARFC	0xa8
+#define REG_MIEN	0xac
+#define REG_MISTA	0xb0
+#define REG_MGSTA	0xb4
+#define REG_MPCNT	0xb8
+#define REG_MRPC	0xbc
+#define REG_MRPCC	0xc0
+#define REG_MREPC	0xc4
+#define REG_DMARFS	0xc8
+#define REG_CTXDSA	0xcc
+#define REG_CTXBSA	0xd0
+#define REG_CRXDSA	0xd4
+#define REG_CRXBSA	0xd8
+
+/* EMC Diagnostic Registers */
+#define REG_RXFSM      0x200
+#define REG_TXFSM      0x204
+#define REG_FSM0       0x208
+#define REG_FSM1       0x20c
+#define REG_DCR        0x210
+#define REG_DMMIR      0x214
+#define REG_BISTR      0x300
+
+/* mac controller bit */
+#define MCMDR_RXON		BIT(0)
+#define MCMDR_ALP		BIT(1)
+#define MCMDR_ACP		BIT(3)
+#define MCMDR_SPCRC		BIT(5)
+#define MCMDR_TXON		BIT(8)
+#define MCMDR_NDEF		BIT(9)
+#define MCMDR_FDUP		BIT(18)
+#define MCMDR_ENMDC		BIT(19)
+#define MCMDR_OPMOD		BIT(20)
+#define SWR			BIT(24)
+
+/* cam command regiser */
+#define CAMCMR_AUP		BIT(0)
+#define CAMCMR_AMP		BIT(1)
+#define CAMCMR_ABP		BIT(2)
+#define CAMCMR_CCAM		BIT(3)
+#define CAMCMR_ECMP		BIT(4)
+
+/* cam enable regiser */
+#define CAM0EN			BIT(0)
+
+/* mac mii controller bit */
+#define PHYAD			BIT(8)
+#define PHYWR			BIT(16)
+#define PHYBUSY			BIT(17)
+#define PHYPRESP		BIT(18)
+#define MDCON			BIT(19)
+#define CAM_ENTRY_SIZE	 0x08
+
+/* rx and tx status */
+#define TXDS_TXCP		BIT(19)
+#define RXDS_CRCE		BIT(17)
+#define RXDS_PTLE		BIT(19)
+#define RXDS_RXGD		BIT(20)
+#define RXDS_ALIE		BIT(21)
+#define RXDS_RP			BIT(22)
+
+/* mac interrupt status*/
+#define MISTA_RXINTR		BIT(0)
+#define MISTA_CRCE		BIT(1)
+#define MISTA_RXOV		BIT(2)
+#define MISTA_PTLE		BIT(3)
+#define MISTA_RXGD		BIT(4)
+#define MISTA_ALIE		BIT(5)
+#define MISTA_RP		BIT(6)
+#define MISTA_MMP		BIT(7)
+#define MISTA_DFOI		BIT(8)
+#define MISTA_DENI		BIT(9)
+#define MISTA_RDU		BIT(10)
+#define MISTA_RXBERR		BIT(11)
+#define MISTA_CFR		BIT(14)
+#define MISTA_TXINTR		BIT(16)
+#define MISTA_TXEMP		BIT(17)
+#define MISTA_TXCP		BIT(18)
+#define MISTA_EXDEF		BIT(19)
+#define MISTA_NCS		BIT(20)
+#define MISTA_TXABT		BIT(21)
+#define MISTA_LC		BIT(22)
+#define MISTA_TDU		BIT(23)
+#define MISTA_TXBERR		BIT(24)
+
+/* Transmit/Receive Start Demand Register */
+#define ENSTART			BIT(0)
+
+#define ENRXINTR		BIT(0)
+#define ENCRCE			BIT(1)
+#define EMRXOV			BIT(2)
+#define ENPTLE			BIT(3)
+#define ENRXGD			BIT(4)
+#define ENALIE			BIT(5)
+#define ENRP			BIT(6)
+#define ENMMP			BIT(7)
+#define ENDFO			BIT(8)
+#define ENDENI			BIT(9)
+#define ENRDU			BIT(10)
+#define ENRXBERR		BIT(11)
+#define ENCFR			BIT(14)
+#define ENTXINTR		BIT(16)
+#define ENTXEMP			BIT(17)
+#define ENTXCP			BIT(18)
+#define ENTXDEF			BIT(19)
+#define ENNCS			BIT(20)
+#define ENTXABT			BIT(21)
+#define ENLC			BIT(22)
+#define ENTDU			BIT(23)
+#define ENTXBERR		BIT(24)
+
+/* rx and tx owner bit */
+#define RX_OWN_DMA		BIT(31)
+#define TX_OWN_DMA		BIT(31)
+
+/* tx frame desc controller bit */
+#define MACTXINTEN		BIT(2)
+#define CRCMODE			BIT(1)
+#define PADDINGMODE		BIT(0)
+
+/* fftcr controller bit */
+#define RXTHD			(0x03 << 0)
+#define TXTHD			(0x02 << 8)
+#define BLENGTH			(0x02 << 20)
+
+/* global setting for driver */
+#define RX_QUEUE_LEN	128
+#define TX_QUEUE_LEN	64
+#define MAX_RBUFF_SZ	0x600
+#define MAX_TBUFF_SZ	0x600
+#define TX_TIMEOUT	50
+#define DELAY		1000
+#define CAM0		0x0
+#define RX_POLL_SIZE    16
+
+#ifdef CONFIG_VLAN_8021Q
+#define IS_VLAN 1
+#else
+#define IS_VLAN 0
+#endif
+
+#define MAX_PACKET_SIZE           (1514 + (IS_VLAN * 4))
+#define MAX_PACKET_SIZE_W_CRC     (MAX_PACKET_SIZE + 4) /* 1518 */
+
+#define MHZ (1000 * 1000)
+#define MII_TIMEOUT	100
+
+struct plat_npcm7xx_emc_data {
+	char *phy_bus_name;
+	int phy_addr;
+	unsigned char mac_addr[ETH_ALEN];
+};
+
+struct npcm7xx_rxbd {
+	__le32 sl;
+	__le32 buffer;
+	__le32 reserved;
+	__le32 next;
+};
+
+struct npcm7xx_txbd {
+	__le32 mode;   /* Ownership bit and some other bits	*/
+	__le32 buffer; /* Transmit Buffer Starting Address	*/
+	__le32 sl;     /* Transmit Byte Count and status bits	*/
+	__le32 next;   /* Next Tx Descriptor Starting Address	*/
+};
+
+struct  npcm7xx_ether {
+	struct sk_buff *rx_skb[RX_QUEUE_LEN];
+	struct sk_buff *tx_skb[TX_QUEUE_LEN];
+	spinlock_t lock;	/* lock sk */
+	struct npcm7xx_rxbd *rdesc;
+	struct npcm7xx_txbd *tdesc;
+	dma_addr_t rdesc_phys;
+	dma_addr_t tdesc_phys;
+	struct net_device_stats stats;
+	struct platform_device *pdev;
+	struct net_device *ndev;
+	struct resource *res;
+	unsigned int msg_enable;
+	struct mii_bus *mii_bus;
+	struct phy_device *phy_dev;
+	struct napi_struct napi;
+	struct ncsi_dev *ncsidev;
+	bool use_ncsi;
+	void __iomem *reg;
+	int rxirq;
+	int txirq;
+	unsigned int cur_tx;
+	unsigned int cur_rx;
+	unsigned int finish_tx;
+	unsigned int pending_tx;
+	__le32 start_tx_ptr;
+	__le32 start_rx_ptr;
+	unsigned int rx_berr;
+	unsigned int rx_err;
+	unsigned int rdu;
+	unsigned int rxov;
+	__le32 camcmr;
+	unsigned int rx_stuck;
+	int link;
+	int speed;
+	int duplex;
+	int need_reset;
+	char *dump_buf;
+	struct regmap *rst_regmap;
+
+	/* debug counters */
+	unsigned int max_waiting_rx;
+	unsigned int rx_count_pool;
+	unsigned int count_xmit;
+	unsigned int rx_int_count;
+	unsigned int rx_err_count;
+	unsigned int tx_int_count;
+	unsigned int tx_tdu;
+	unsigned int tx_tdu_i;
+	unsigned int tx_cp_i;
+	unsigned int count_finish;
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *dbgfs_dir;
+	struct dentry *dbgfs_status;
+	struct dentry *dbgfs_dma_cap;
+#endif
+};
+
+#if defined CONFIG_NPCM7XX_EMC_ETH_DEBUG || defined CONFIG_DEBUG_FS
+#define REG_PRINT(reg_name) {t = scnprintf(next, size, "%-10s = %08X\n", \
+	#reg_name, readl(ether->reg + reg_name)); size -= t;	next += t; }
+#define DUMP_PRINT(f, x...) {t = scnprintf(next, size, f, ## x); size -= t; \
+	next += t; }
+
+static int npcm7xx_info_dump(char *buf, int count, struct net_device *dev)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	struct npcm7xx_txbd *txbd;
+	struct npcm7xx_rxbd *rxbd;
+	unsigned long flags;
+	unsigned int i, cur, txd_offset, rxd_offset;
+	char *next = buf;
+	unsigned int size = count;
+	int t;
+	int is_locked = spin_is_locked(&ether->lock);
+
+	if (!is_locked)
+		spin_lock_irqsave(&ether->lock, flags);
+
+	/* ------basic driver information ---- */
+	DUMP_PRINT("NPCM7XX EMC %s driver version: %s\n", dev->name,
+		   DRV_MODULE_VERSION);
+
+	REG_PRINT(REG_CAMCMR);
+	REG_PRINT(REG_CAMEN);
+	REG_PRINT(REG_CAMM_BASE);
+	REG_PRINT(REG_CAML_BASE);
+	REG_PRINT(REG_TXDLSA);
+	REG_PRINT(REG_RXDLSA);
+	REG_PRINT(REG_MCMDR);
+	REG_PRINT(REG_MIID);
+	REG_PRINT(REG_MIIDA);
+	REG_PRINT(REG_FFTCR);
+	REG_PRINT(REG_TSDR);
+	REG_PRINT(REG_RSDR);
+	REG_PRINT(REG_DMARFC);
+	REG_PRINT(REG_MIEN);
+	REG_PRINT(REG_MISTA);
+	REG_PRINT(REG_MGSTA);
+	REG_PRINT(REG_MPCNT);
+	writel(0x7FFF, (ether->reg + REG_MPCNT));
+	REG_PRINT(REG_MRPC);
+	REG_PRINT(REG_MRPCC);
+	REG_PRINT(REG_MREPC);
+	REG_PRINT(REG_DMARFS);
+	REG_PRINT(REG_CTXDSA);
+	REG_PRINT(REG_CTXBSA);
+	REG_PRINT(REG_CRXDSA);
+	REG_PRINT(REG_CRXBSA);
+	REG_PRINT(REG_RXFSM);
+	REG_PRINT(REG_TXFSM);
+	REG_PRINT(REG_FSM0);
+	REG_PRINT(REG_FSM1);
+	REG_PRINT(REG_DCR);
+	REG_PRINT(REG_DMMIR);
+	REG_PRINT(REG_BISTR);
+	DUMP_PRINT("\n");
+
+	DUMP_PRINT("netif_queue %s\n\n", netif_queue_stopped(dev) ?
+					"Stopped" : "Running");
+	if (ether->rdesc)
+		DUMP_PRINT("napi is %s\n\n", test_bit(NAPI_STATE_SCHED,
+						      &ether->napi.state) ?
+							"scheduled" :
+							"not scheduled");
+
+	txd_offset = (readl((ether->reg + REG_CTXDSA)) -
+		      readl((ether->reg + REG_TXDLSA))) /
+		sizeof(struct npcm7xx_txbd);
+	DUMP_PRINT("TXD offset    %6d\n", txd_offset);
+	DUMP_PRINT("cur_tx        %6d\n", ether->cur_tx);
+	DUMP_PRINT("finish_tx     %6d\n", ether->finish_tx);
+	DUMP_PRINT("pending_tx    %6d\n", ether->pending_tx);
+	/* debug counters */
+	DUMP_PRINT("tx_tdu        %6d\n", ether->tx_tdu);
+	ether->tx_tdu = 0;
+	DUMP_PRINT("tx_tdu_i      %6d\n", ether->tx_tdu_i);
+	ether->tx_tdu_i = 0;
+	DUMP_PRINT("tx_cp_i       %6d\n", ether->tx_cp_i);
+	 ether->tx_cp_i = 0;
+	DUMP_PRINT("tx_int_count  %6d\n", ether->tx_int_count);
+	ether->tx_int_count = 0;
+	DUMP_PRINT("count_xmit tx %6d\n", ether->count_xmit);
+	ether->count_xmit = 0;
+	DUMP_PRINT("count_finish  %6d\n", ether->count_finish);
+	ether->count_finish = 0;
+	DUMP_PRINT("\n");
+
+	rxd_offset = (readl((ether->reg + REG_CRXDSA)) -
+		      readl((ether->reg + REG_RXDLSA)))
+		/ sizeof(struct npcm7xx_txbd);
+	DUMP_PRINT("RXD offset    %6d\n", rxd_offset);
+	DUMP_PRINT("cur_rx        %6d\n", ether->cur_rx);
+	DUMP_PRINT("rx_err        %6d\n", ether->rx_err);
+	ether->rx_err = 0;
+	DUMP_PRINT("rx_berr       %6d\n", ether->rx_berr);
+	ether->rx_berr = 0;
+	DUMP_PRINT("rx_stuck      %6d\n", ether->rx_stuck);
+	ether->rx_stuck = 0;
+	DUMP_PRINT("rdu           %6d\n", ether->rdu);
+	ether->rdu = 0;
+	DUMP_PRINT("rxov rx       %6d\n", ether->rxov);
+	ether->rxov = 0;
+	/* debug counters */
+	DUMP_PRINT("rx_int_count  %6d\n", ether->rx_int_count);
+	ether->rx_int_count = 0;
+	DUMP_PRINT("rx_err_count  %6d\n", ether->rx_err_count);
+	ether->rx_err_count = 0;
+	DUMP_PRINT("rx_count_pool %6d\n", ether->rx_count_pool);
+	ether->rx_count_pool = 0;
+	DUMP_PRINT("max_waiting_rx %5d\n", ether->max_waiting_rx);
+	ether->max_waiting_rx = 0;
+	DUMP_PRINT("\n");
+	DUMP_PRINT("need_reset    %5d\n", ether->need_reset);
+
+	if (ether->tdesc && ether->rdesc) {
+		cur = ether->finish_tx - 2;
+		for (i = 0; i < 3; i++) {
+			cur = (cur + 1) % TX_QUEUE_LEN;
+			txbd = (ether->tdesc + cur);
+			DUMP_PRINT("finish %3d txbd mode %08X buffer %08X sl %08X next %08X tx_skb %p\n",
+				   cur, txbd->mode, txbd->buffer,
+				   txbd->sl, txbd->next, ether->tx_skb[cur]);
+		}
+		DUMP_PRINT("\n");
+
+		cur = txd_offset - 2;
+		for (i = 0; i < 3; i++) {
+			cur = (cur + 1) % TX_QUEUE_LEN;
+			txbd = (ether->tdesc + cur);
+			DUMP_PRINT("txd_of %3d txbd mode %08X buffer %08X sl %08X next %08X\n",
+				   cur, txbd->mode, txbd->buffer,
+				   txbd->sl, txbd->next);
+		}
+		DUMP_PRINT("\n");
+
+		cur = ether->cur_tx - 63;
+		for (i = 0; i < 64; i++) {
+			cur = (cur + 1) % TX_QUEUE_LEN;
+			txbd = (ether->tdesc + cur);
+			DUMP_PRINT("cur_tx %3d txbd mode %08X buffer %08X sl %08X next %08X\n",
+				   cur, txbd->mode, txbd->buffer,
+				   txbd->sl, txbd->next);
+		}
+		DUMP_PRINT("\n");
+
+		cur = ether->cur_rx - 63;
+		for (i = 0; i < 64; i++) {
+			cur = (cur + 1) % RX_QUEUE_LEN;
+			rxbd = (ether->rdesc + cur);
+			DUMP_PRINT("cur_rx %3d rxbd sl   %08X buffer %08X sl %08X next %08X\n",
+				   cur, rxbd->sl, rxbd->buffer,
+				   rxbd->reserved, rxbd->next);
+		}
+		DUMP_PRINT("\n");
+
+		cur = rxd_offset - 2;
+		for (i = 0; i < 3; i++) {
+			cur = (cur + 1) % RX_QUEUE_LEN;
+			rxbd = (ether->rdesc + cur);
+			DUMP_PRINT("rxd_of %3d rxbd sl %08X buffer %08X sl %08X next %08X\n",
+				   cur, rxbd->sl, rxbd->buffer,
+				   rxbd->reserved, rxbd->next);
+		}
+		DUMP_PRINT("\n");
+	}
+
+	if (!is_locked)
+		spin_unlock_irqrestore(&ether->lock, flags);
+
+	return count - size;
+}
+#endif
+
+#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG
+static void npcm7xx_info_print(struct net_device *dev)
+{
+	char *emc_dump_buf;
+	int count;
+	struct npcm7xx_ether *ether;
+	struct platform_device *pdev;
+	const size_t print_size = 5 * PAGE_SIZE;
+
+	ether = netdev_priv(dev);
+	pdev = ether->pdev;
+
+	emc_dump_buf = kmalloc(print_size, GFP_KERNEL);
+	if (!emc_dump_buf) {
+		dev_err(&pdev->dev, "kmalloc failed\n");
+	} else {
+		char c;
+		char *tmp_buf = emc_dump_buf;
+
+		count = npcm7xx_info_dump(emc_dump_buf, print_size, dev);
+		while (count > 512) {
+			c = tmp_buf[512];
+			tmp_buf[512] = 0;
+			dev_info(&pdev->dev, "%s", tmp_buf);
+			tmp_buf += 512;
+			tmp_buf[0] = c;
+			count -= 512;
+		}
+		dev_info(&pdev->dev, "%s", tmp_buf);
+		kfree(emc_dump_buf);
+	}
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/seq_file.h>
+
+static int npcm7xx_debug_show(struct seq_file *sf, void *v)
+{
+	struct net_device *dev = (struct net_device *)sf->private;
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	const size_t print_size = 5 * PAGE_SIZE;
+
+	if (!ether->dump_buf) {
+		ether->dump_buf = kmalloc(print_size, GFP_KERNEL);
+		if (!ether->dump_buf)
+			return -1;
+		npcm7xx_info_dump(ether->dump_buf, print_size, dev);
+	}
+
+	seq_printf(sf, "%s", ether->dump_buf);
+	if (sf->count < sf->size) {
+		kfree(ether->dump_buf);
+		ether->dump_buf = NULL;
+	}
+
+	return 0;
+}
+
+static int npcm7xx_debug_show_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, npcm7xx_debug_show, inode->i_private);
+}
+
+static const struct file_operations npcm7xx_debug_show_fops = {
+	.open           = npcm7xx_debug_show_open,
+	.read           = seq_read,
+	.llseek         = seq_lseek,
+	.release        = single_release,
+};
+
+static int npcm7xx_debug_reset(struct seq_file *sf, void *v)
+{
+	struct net_device *dev = (struct net_device *)sf->private;
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	unsigned long flags;
+
+	seq_puts(sf, "Ask to reset the module\n");
+	spin_lock_irqsave(&ether->lock, flags);
+	writel(0,  (ether->reg + REG_MIEN));
+	spin_unlock_irqrestore(&ether->lock, flags);
+	ether->need_reset = 1;
+	napi_schedule(&ether->napi);
+
+	return 0;
+}
+
+static int npcm7xx_debug_reset_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, npcm7xx_debug_reset, inode->i_private);
+}
+
+static const struct file_operations npcm7xx_debug_reset_fops = {
+	.owner		= THIS_MODULE,
+	.open           = npcm7xx_debug_reset_open,
+	.read           = seq_read,
+	.llseek         = seq_lseek,
+	.release        = single_release,
+};
+
+static int npcm7xx_debug_fs(struct npcm7xx_ether *ether)
+{
+	/* Create debugfs main directory if it doesn't exist yet */
+	if (!npcm7xx_fs_dir) {
+		npcm7xx_fs_dir = debugfs_create_dir(DRV_MODULE_NAME, NULL);
+
+		if (!npcm7xx_fs_dir || IS_ERR(npcm7xx_fs_dir)) {
+			dev_err(&ether->pdev->dev, "ERROR %s, debugfs create directory failed\n",
+				DRV_MODULE_NAME);
+			return -ENOMEM;
+		}
+	}
+
+	/* Create per netdev entries */
+	ether->dbgfs_dir = debugfs_create_dir(ether->ndev->name,
+					      npcm7xx_fs_dir);
+	if (!ether->dbgfs_dir || IS_ERR(ether->dbgfs_dir)) {
+		dev_err(&ether->pdev->dev, "ERROR failed to create %s directory\n", ether->ndev->name);
+		return -ENOMEM;
+	}
+
+	/* Entry to report DMA RX/TX rings */
+	ether->dbgfs_status =
+		debugfs_create_file("status", 0444,
+				    ether->dbgfs_dir, ether->ndev,
+				    &npcm7xx_debug_show_fops);
+
+	if (!ether->dbgfs_status || IS_ERR(ether->dbgfs_status)) {
+		dev_err(&ether->pdev->dev, "ERROR creating \'status\' debugfs file\n");
+		debugfs_remove_recursive(ether->dbgfs_dir);
+
+		return -ENOMEM;
+	}
+
+	/* Entry to report the DMA HW features */
+	ether->dbgfs_dma_cap = debugfs_create_file("do_reset", 0444,
+						   ether->dbgfs_dir,
+						   ether->ndev,
+						   &npcm7xx_debug_reset_fops);
+
+	if (!ether->dbgfs_dma_cap || IS_ERR(ether->dbgfs_dma_cap)) {
+		dev_err(&ether->pdev->dev, "ERROR creating stmmac \'do_reset\' debugfs file\n");
+		debugfs_remove_recursive(ether->dbgfs_dir);
+
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+#endif
+
+static void npcm7xx_opmode(struct net_device *dev, int speed, int duplex)
+{
+	__le32 val;
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+
+	val = readl((ether->reg + REG_MCMDR));
+	if (speed == 100)
+		val |= MCMDR_OPMOD;
+	else
+		val &= ~MCMDR_OPMOD;
+
+	if (duplex == DUPLEX_FULL)
+		val |= MCMDR_FDUP;
+	else
+		val &= ~MCMDR_FDUP;
+
+	writel(val, (ether->reg + REG_MCMDR));
+}
+
+static void adjust_link(struct net_device *dev)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	struct phy_device *phydev = ether->phy_dev;
+	bool status_change = false;
+	unsigned long flags;
+
+	/* clear GPIO interrupt status whihc indicates PHY statu change? */
+	spin_lock_irqsave(&ether->lock, flags);
+
+	if (phydev->link) {
+		if (ether->speed != phydev->speed ||
+		    ether->duplex != phydev->duplex) {
+			ether->speed = phydev->speed;
+			ether->duplex = phydev->duplex;
+			status_change = true;
+		}
+	} else {
+		ether->speed = 0;
+		ether->duplex = -1;
+	}
+
+	if (phydev->link != ether->link) {
+		ether->link = phydev->link;
+		status_change = true;
+	}
+
+	spin_unlock_irqrestore(&ether->lock, flags);
+
+	if (status_change)
+		npcm7xx_opmode(dev, ether->speed, ether->duplex);
+}
+
+static void npcm7xx_write_cam(struct net_device *dev,
+			      unsigned int x, unsigned char *pval)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	__le32 msw, lsw;
+
+	msw = (pval[0] << 24) | (pval[1] << 16) | (pval[2] << 8) | pval[3];
+
+	lsw = (pval[4] << 24) | (pval[5] << 16);
+
+	writel(lsw, (ether->reg + REG_CAML_BASE) + x * CAM_ENTRY_SIZE);
+	writel(msw, (ether->reg + REG_CAMM_BASE) + x * CAM_ENTRY_SIZE);
+	dev_dbg(&ether->pdev->dev, "REG_CAML_BASE = 0x%08X REG_CAMM_BASE = 0x%08X", lsw, msw);
+}
+
+static struct sk_buff *get_new_skb(struct net_device *dev, u32 i)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	struct sk_buff *skb = dev_alloc_skb(roundup(MAX_PACKET_SIZE_W_CRC, 4));
+
+	if (!skb)
+		return NULL;
+
+	/* Do not unmark the following skb_reserve() Receive Buffer Starting
+	 * Address must be aligned to 4 bytes and the following line
+	 * if unmarked will make it align to 2 and this likely will
+	 * hult the RX and crash the linux skb_reserve(skb, NET_IP_ALIGN);
+	 */
+	skb->dev = dev;
+	(ether->rdesc + i)->buffer =
+		dma_map_single(&dev->dev, skb->data,
+			       roundup(MAX_PACKET_SIZE_W_CRC, 4),
+			       DMA_FROM_DEVICE);
+	ether->rx_skb[i] = skb;
+
+	return skb;
+}
+
+static int npcm7xx_init_desc(struct net_device *dev)
+{
+	struct npcm7xx_ether *ether;
+	struct npcm7xx_txbd  *tdesc;
+	struct npcm7xx_rxbd  *rdesc;
+	struct platform_device *pdev;
+	unsigned int i;
+
+	ether = netdev_priv(dev);
+	pdev = ether->pdev;
+
+	if (!ether->tdesc) {
+		ether->tdesc = (struct npcm7xx_txbd *)
+				dma_alloc_coherent(&pdev->dev,
+						   sizeof(struct npcm7xx_txbd) *
+						   TX_QUEUE_LEN,
+						   &ether->tdesc_phys,
+						   GFP_KERNEL);
+
+		if (!ether->tdesc) {
+			dev_err(&pdev->dev, "Failed to allocate memory for tx desc\n");
+			return -ENOMEM;
+		}
+	}
+
+	if (!ether->rdesc) {
+		ether->rdesc = (struct npcm7xx_rxbd *)
+				dma_alloc_coherent(&pdev->dev,
+						   sizeof(struct npcm7xx_rxbd) *
+						   RX_QUEUE_LEN,
+						   &ether->rdesc_phys,
+						   GFP_KERNEL);
+
+		if (!ether->rdesc) {
+			dev_err(&pdev->dev, "Failed to allocate memory for rx desc\n");
+			dma_free_coherent(&pdev->dev,
+					  sizeof(struct npcm7xx_txbd) *
+					  TX_QUEUE_LEN, ether->tdesc,
+					  ether->tdesc_phys);
+			ether->tdesc = NULL;
+			return -ENOMEM;
+		}
+	}
+
+	for (i = 0; i < TX_QUEUE_LEN; i++) {
+		unsigned int offset;
+
+		tdesc = (ether->tdesc + i);
+
+		if (i == TX_QUEUE_LEN - 1)
+			offset = 0;
+		else
+			offset = sizeof(struct npcm7xx_txbd) * (i + 1);
+
+		tdesc->next = ether->tdesc_phys + offset;
+		tdesc->buffer = (__le32)NULL;
+		tdesc->sl = 0;
+		tdesc->mode = 0;
+	}
+
+	ether->start_tx_ptr = ether->tdesc_phys;
+
+	for (i = 0; i < RX_QUEUE_LEN; i++) {
+		unsigned int offset;
+
+		rdesc = (ether->rdesc + i);
+
+		if (i == RX_QUEUE_LEN - 1)
+			offset = 0;
+		else
+			offset = sizeof(struct npcm7xx_rxbd) * (i + 1);
+
+		rdesc->next = ether->rdesc_phys + offset;
+		rdesc->sl = RX_OWN_DMA;
+
+		if (!get_new_skb(dev, i)) {
+			dev_err(&pdev->dev, "get_new_skb() failed\n");
+
+			for (; i != 0; i--) {
+				dma_unmap_single(&dev->dev, (dma_addr_t)
+						 ((ether->rdesc + i)->buffer),
+						 roundup(MAX_PACKET_SIZE_W_CRC,
+							 4), DMA_FROM_DEVICE);
+				dev_kfree_skb_any(ether->rx_skb[i]);
+				ether->rx_skb[i] = NULL;
+			}
+
+			dma_free_coherent(&pdev->dev,
+					  sizeof(struct npcm7xx_txbd) *
+					  TX_QUEUE_LEN,
+					  ether->tdesc, ether->tdesc_phys);
+			dma_free_coherent(&pdev->dev,
+					  sizeof(struct npcm7xx_rxbd) *
+					  RX_QUEUE_LEN,
+					  ether->rdesc, ether->rdesc_phys);
+
+			return -ENOMEM;
+		}
+	}
+
+	ether->start_rx_ptr = ether->rdesc_phys;
+	wmb();
+	for (i = 0; i < TX_QUEUE_LEN; i++)
+		ether->tx_skb[i] = NULL;
+
+	return 0;
+}
+
+/* This API must call with Tx/Rx stopped */
+static void npcm7xx_free_desc(struct net_device *dev,
+			      bool free_also_descriptors)
+{
+	struct sk_buff *skb;
+	u32 i;
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	struct platform_device *pdev = ether->pdev;
+
+	for (i = 0; i < TX_QUEUE_LEN; i++) {
+		skb = ether->tx_skb[i];
+		if (skb) {
+			dma_unmap_single(&dev->dev, (dma_addr_t)((ether->tdesc +
+								  i)->buffer),
+					 skb->len, DMA_TO_DEVICE);
+			dev_kfree_skb_any(skb);
+			ether->tx_skb[i] = NULL;
+		}
+	}
+
+	for (i = 0; i < RX_QUEUE_LEN; i++) {
+		skb = ether->rx_skb[i];
+		if (skb) {
+			dma_unmap_single(&dev->dev, (dma_addr_t)((ether->rdesc +
+								   i)->buffer),
+					 roundup(MAX_PACKET_SIZE_W_CRC, 4),
+					 DMA_FROM_DEVICE);
+			dev_kfree_skb_any(skb);
+			ether->rx_skb[i] = NULL;
+		}
+	}
+
+	if (free_also_descriptors) {
+		if (ether->tdesc)
+			dma_free_coherent(&pdev->dev,
+					  sizeof(struct npcm7xx_txbd) *
+					  TX_QUEUE_LEN,
+					  ether->tdesc, ether->tdesc_phys);
+		ether->tdesc = NULL;
+
+		if (ether->rdesc)
+			dma_free_coherent(&pdev->dev,
+					  sizeof(struct npcm7xx_rxbd) *
+					  RX_QUEUE_LEN,
+					  ether->rdesc, ether->rdesc_phys);
+		ether->rdesc = NULL;
+	}
+}
+
+static void npcm7xx_set_fifo_threshold(struct net_device *dev)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	__le32 val;
+
+	val = RXTHD | TXTHD | BLENGTH;
+	writel(val, (ether->reg + REG_FFTCR));
+}
+
+static void npcm7xx_return_default_idle(struct net_device *dev)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	__le32 val;
+	__le32 saved_bits;
+
+	val = readl((ether->reg + REG_MCMDR));
+	saved_bits = val & (MCMDR_FDUP | MCMDR_OPMOD);
+	val |= SWR;
+	writel(val, (ether->reg + REG_MCMDR));
+
+	/* During the EMC reset the AHB will read 0 from all registers,
+	 * so in order to see if the reset finished we can't count on
+	 * (ether->reg + REG_MCMDR).SWR to become 0, instead we read another
+	 * register that its reset value is not 0,
+	 * we choose (ether->reg + REG_FFTCR).
+	 */
+	do {
+		val = readl((ether->reg + REG_FFTCR));
+	} while (val == 0);
+
+	/*
+	 * Now we can verify if (ether->reg + REG_MCMDR).SWR became
+	 * 0 (probably it will be 0 on the first read).
+	 */
+	do {
+		val = readl((ether->reg + REG_MCMDR));
+	} while (val & SWR);
+
+	/* restore values */
+	writel(saved_bits, (ether->reg + REG_MCMDR));
+}
+
+static void npcm7xx_enable_mac_interrupt(struct net_device *dev)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	__le32 val;
+
+	val = ENRXINTR |  /* Start of RX interrupts */
+		ENCRCE   |
+		EMRXOV   |
+		(ENPTLE * (!IS_VLAN)) | /* If we don't support VLAN we want interrupt on long packets */
+		ENRXGD   |
+		ENALIE   |
+		ENRP     |
+		ENMMP    |
+		ENDFO    |
+		/*   ENDENI   |  */  /* We don't need interrupt on DMA Early Notification */
+		ENRDU    |    /* We don't need interrupt on Receive Descriptor Unavailable Interrupt */
+		ENRXBERR |
+		/*   ENCFR    |  */
+		ENTXINTR |  /* Start of TX interrupts */
+		ENTXEMP  |
+		ENTXCP   |
+		ENTXDEF  |
+		ENNCS    |
+		ENTXABT  |
+		ENLC     |
+		/* ENTDU    |  */  /* We don't need interrupt on Transmit Descriptor Unavailable at start of operation */
+		ENTXBERR;
+	writel(val, (ether->reg + REG_MIEN));
+}
+
+static void npcm7xx_get_and_clear_int(struct net_device *dev,
+				      __le32 *val, __le32 mask)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+
+	*val = readl((ether->reg + REG_MISTA)) & mask;
+	writel(*val, (ether->reg + REG_MISTA));
+}
+
+static void npcm7xx_set_global_maccmd(struct net_device *dev)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	__le32 val;
+
+	val = readl((ether->reg + REG_MCMDR));
+
+	val |= MCMDR_SPCRC | MCMDR_ENMDC | MCMDR_ACP | MCMDR_NDEF;
+	if (IS_VLAN) {
+		/*
+		 * we set ALP accept long packets since VLAN packets
+		 * are 4 bytes longer than 1518
+		 */
+		val |= MCMDR_ALP;
+		/* limit receive length to 1522 bytes due to VLAN */
+		writel(MAX_PACKET_SIZE_W_CRC, (ether->reg + REG_DMARFC));
+	}
+	writel(val, (ether->reg + REG_MCMDR));
+}
+
+static void npcm7xx_enable_cam(struct net_device *dev)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	__le32 val;
+
+	npcm7xx_write_cam(dev, CAM0, dev->dev_addr);
+
+	val = readl((ether->reg + REG_CAMEN));
+	val |= CAM0EN;
+	writel(val, (ether->reg + REG_CAMEN));
+}
+
+static void npcm7xx_set_curdest(struct net_device *dev)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+
+	writel(ether->start_rx_ptr, (ether->reg + REG_RXDLSA));
+	writel(ether->start_tx_ptr, (ether->reg + REG_TXDLSA));
+}
+
+static void npcm7xx_ether_set_rx_mode(struct net_device *dev)
+{
+	struct npcm7xx_ether *ether;
+	__le32 rx_mode;
+
+	ether = netdev_priv(dev);
+
+	dev_dbg(&ether->pdev->dev, "%s CAMCMR_AUP\n",
+		(dev->flags & IFF_PROMISC) ? "Set" : "Clear");
+	if (dev->flags & IFF_PROMISC)
+		rx_mode = CAMCMR_AUP | CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP;
+	else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev))
+		rx_mode = CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP;
+	else
+		rx_mode = CAMCMR_ECMP | CAMCMR_ABP;
+	writel(rx_mode, (ether->reg + REG_CAMCMR));
+	ether->camcmr = rx_mode;
+}
+
+static void npcm7xx_reset_mac(struct net_device *dev, int need_free)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+
+	netif_tx_lock(dev);
+
+	/* disable RX and TX */
+	writel(readl((ether->reg + REG_MCMDR)) & ~(MCMDR_TXON | MCMDR_RXON),
+	       (ether->reg + REG_MCMDR));
+
+	npcm7xx_return_default_idle(dev);
+	npcm7xx_set_fifo_threshold(dev);
+
+	if (need_free)
+		npcm7xx_free_desc(dev, false);
+
+	npcm7xx_init_desc(dev);
+
+	ether->cur_tx = 0x0;
+	ether->finish_tx = 0x0;
+	ether->pending_tx = 0x0;
+	ether->cur_rx = 0x0;
+	ether->tx_tdu = 0;
+	ether->tx_tdu_i = 0;
+	ether->tx_cp_i = 0;
+
+	npcm7xx_set_curdest(dev);
+	npcm7xx_enable_cam(dev);
+	npcm7xx_ether_set_rx_mode(dev);
+	npcm7xx_enable_mac_interrupt(dev);
+	npcm7xx_set_global_maccmd(dev);
+
+	/* enable RX and TX */
+	writel(readl((ether->reg + REG_MCMDR)) | MCMDR_TXON | MCMDR_RXON,
+	       (ether->reg + REG_MCMDR));
+
+	/* trigger RX */
+	writel(ENSTART, (ether->reg + REG_RSDR));
+
+	ether->need_reset = 0;
+
+	netif_wake_queue(dev);
+	netif_tx_unlock(dev);
+}
+
+static int npcm7xx_mdio_write(struct mii_bus *bus, int phy_id, int regnum,
+			      u16 value)
+{
+	struct npcm7xx_ether *ether = bus->priv;
+	unsigned long timeout = jiffies + msecs_to_jiffies(MII_TIMEOUT * 100);
+
+	writel(value,  (ether->reg + REG_MIID));
+	writel((phy_id << 0x08) | regnum | PHYBUSY | PHYWR,
+	       (ether->reg + REG_MIIDA));
+
+	/* Wait for completion */
+	while (readl((ether->reg + REG_MIIDA)) & PHYBUSY) {
+		if (time_after(jiffies, timeout)) {
+			dev_dbg(&ether->pdev->dev, "mdio read timed out\n ether->reg = 0x%x phy_id=0x%x REG_MIIDA=0x%x\n",
+				(unsigned int)ether->reg, phy_id
+				, readl((ether->reg + REG_MIIDA)));
+			return -ETIMEDOUT;
+		}
+		cpu_relax();
+	}
+
+	return 0;
+}
+
+static int npcm7xx_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
+{
+	struct npcm7xx_ether *ether = bus->priv;
+	unsigned long timeout = jiffies + msecs_to_jiffies(MII_TIMEOUT * 100);
+
+	writel((phy_id << 0x08) | regnum | PHYBUSY,  (ether->reg + REG_MIIDA));
+
+	/* Wait for completion */
+	while (readl((ether->reg + REG_MIIDA)) & PHYBUSY) {
+		if (time_after(jiffies, timeout)) {
+			dev_dbg(&ether->pdev->dev, "mdio read timed out\n ether->reg = 0x%x phy_id=0x%x REG_MIIDA=0x%x\n",
+				(unsigned int)ether->reg, phy_id
+				, readl((ether->reg + REG_MIIDA)));
+			return -ETIMEDOUT;
+		}
+		cpu_relax();
+	}
+
+	return readl((ether->reg + REG_MIID));
+}
+
+static int npcm7xx_mdio_reset(struct mii_bus *bus)
+{
+	/* reset EMAC engine?? */
+	return 0;
+}
+
+static int npcm7xx_set_mac_address(struct net_device *dev, void *addr)
+{
+	struct sockaddr *address = addr;
+
+	if (!is_valid_ether_addr((u8 *)address->sa_data))
+		return -EADDRNOTAVAIL;
+
+	memcpy(dev->dev_addr, address->sa_data, dev->addr_len);
+	npcm7xx_write_cam(dev, CAM0, dev->dev_addr);
+
+	return 0;
+}
+
+static int npcm7xx_ether_close(struct net_device *dev)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+
+	npcm7xx_return_default_idle(dev);
+
+	if (ether->phy_dev)
+		phy_stop(ether->phy_dev);
+	else if (ether->use_ncsi)
+		ncsi_stop_dev(ether->ncsidev);
+
+	msleep(20);
+
+	free_irq(ether->txirq, dev);
+	free_irq(ether->rxirq, dev);
+
+	netif_stop_queue(dev);
+	napi_disable(&ether->napi);
+
+	npcm7xx_free_desc(dev, true);
+
+	kfree(ether->dump_buf);
+	ether->dump_buf = NULL;
+
+	return 0;
+}
+
+static struct net_device_stats *npcm7xx_ether_stats(struct net_device *dev)
+{
+	struct npcm7xx_ether *ether;
+
+	ether = netdev_priv(dev);
+	return &ether->stats;
+}
+
+static int npcm7xx_clean_tx(struct net_device *dev, bool from_xmit)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	struct npcm7xx_txbd *txbd;
+	struct sk_buff *s;
+	dma_addr_t cur_entry, entry;
+	__le32 sl;
+
+	if (ether->pending_tx == 0)
+		return (0);
+
+	cur_entry = readl((ether->reg + REG_CTXDSA));
+
+	/* Release old used buffers */
+	entry = ether->tdesc_phys + sizeof(struct npcm7xx_txbd) *
+		(ether->finish_tx);
+
+	while (entry != cur_entry) {
+		txbd = (ether->tdesc + ether->finish_tx);
+		s = ether->tx_skb[ether->finish_tx];
+		if (!s)
+			break;
+
+		ether->count_finish++;
+
+		dma_unmap_single(&dev->dev, txbd->buffer, s->len,
+				 DMA_TO_DEVICE);
+		consume_skb(s);
+		ether->tx_skb[ether->finish_tx] = NULL;
+
+		if (++ether->finish_tx >= TX_QUEUE_LEN)
+			ether->finish_tx = 0;
+		ether->pending_tx--;
+
+		sl = txbd->sl;
+		if (sl & TXDS_TXCP) {
+			ether->stats.tx_packets++;
+			ether->stats.tx_bytes += (sl & 0xFFFF);
+		} else {
+			ether->stats.tx_errors++;
+		}
+
+		entry = ether->tdesc_phys + sizeof(struct npcm7xx_txbd) *
+			(ether->finish_tx);
+	}
+
+	if (!from_xmit && unlikely(netif_queue_stopped(dev) &&
+				   (TX_QUEUE_LEN - ether->pending_tx) > 1)) {
+		netif_tx_lock(dev);
+		if (netif_queue_stopped(dev) &&
+		    (TX_QUEUE_LEN - ether->pending_tx) > 1) {
+			netif_wake_queue(dev);
+		}
+		netif_tx_unlock(dev);
+	}
+
+	return(0);
+}
+
+static int npcm7xx_ether_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	struct npcm7xx_txbd *txbd;
+	unsigned long flags;
+
+	ether->count_xmit++;
+
+	/* Insert new buffer */
+	txbd = (ether->tdesc + ether->cur_tx);
+	txbd->buffer = dma_map_single(&dev->dev, skb->data, skb->len,
+				      DMA_TO_DEVICE);
+	ether->tx_skb[ether->cur_tx]  = skb;
+	if (skb->len > MAX_PACKET_SIZE)
+		dev_err(&ether->pdev->dev, "skb->len (= %d) > MAX_PACKET_SIZE (= %d)\n",
+			skb->len, MAX_PACKET_SIZE);
+
+	txbd->sl = skb->len > MAX_PACKET_SIZE ? MAX_PACKET_SIZE : skb->len;
+	dma_wmb();
+
+	txbd->mode = TX_OWN_DMA | PADDINGMODE | CRCMODE;
+	wmb();
+
+	/* trigger TX */
+	writel(ENSTART, (ether->reg + REG_TSDR));
+
+	if (++ether->cur_tx >= TX_QUEUE_LEN)
+		ether->cur_tx = 0;
+
+	spin_lock_irqsave(&ether->lock, flags);
+	ether->pending_tx++;
+
+	npcm7xx_clean_tx(dev, true);
+
+	if (ether->pending_tx >= TX_QUEUE_LEN - 1) {
+		__le32 reg_mien;
+		unsigned int index_to_wake = ether->cur_tx +
+			((TX_QUEUE_LEN * 3) / 4);
+
+		if (index_to_wake >= TX_QUEUE_LEN)
+			index_to_wake -= TX_QUEUE_LEN;
+
+		txbd = (ether->tdesc + index_to_wake);
+		txbd->mode = TX_OWN_DMA | PADDINGMODE | CRCMODE | MACTXINTEN;
+		wmb();
+
+		writel(MISTA_TDU, (ether->reg + REG_MISTA));
+		/* Clear TDU interrupt */
+		reg_mien = readl((ether->reg + REG_MIEN));
+
+		if (reg_mien != 0)
+			/* Enable TDU interrupt */
+			writel(reg_mien | ENTDU, (ether->reg + REG_MIEN));
+
+		ether->tx_tdu++;
+		netif_stop_queue(dev);
+	}
+
+	spin_unlock_irqrestore(&ether->lock, flags);
+
+	return 0;
+}
+
+static irqreturn_t npcm7xx_tx_interrupt(int irq, void *dev_id)
+{
+	struct npcm7xx_ether *ether;
+	struct platform_device *pdev;
+	struct net_device *dev;
+	__le32 status;
+	unsigned long flags;
+
+	dev = dev_id;
+	ether = netdev_priv(dev);
+	pdev = ether->pdev;
+
+	npcm7xx_get_and_clear_int(dev, &status, 0xFFFF0000);
+
+	ether->tx_int_count++;
+
+	if (status & MISTA_EXDEF)
+		dev_err(&pdev->dev, "emc defer exceed interrupt status=0x%08X\n"
+			, status);
+	else if (status & MISTA_TXBERR) {
+		dev_err(&pdev->dev, "emc bus error interrupt status=0x%08X\n",
+			status);
+#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG
+		npcm7xx_info_print(dev);
+#endif
+		spin_lock_irqsave(&ether->lock, flags);
+		writel(0, (ether->reg + REG_MIEN)); /* disable any interrupt */
+		spin_unlock_irqrestore(&ether->lock, flags);
+		ether->need_reset = 1;
+	} else if (status & ~(MISTA_TXINTR | MISTA_TXCP | MISTA_TDU))
+		dev_err(&pdev->dev, "emc other error interrupt status=0x%08X\n",
+			status);
+
+    /* if we got MISTA_TXCP | MISTA_TDU remove those interrupt and call napi */
+	if (status & (MISTA_TXCP | MISTA_TDU) &
+	    readl((ether->reg + REG_MIEN))) {
+		__le32 reg_mien;
+
+		spin_lock_irqsave(&ether->lock, flags);
+		reg_mien = readl((ether->reg + REG_MIEN));
+		if (reg_mien & ENTDU)
+			/* Disable TDU interrupt */
+			writel(reg_mien & (~ENTDU), (ether->reg + REG_MIEN));
+
+		spin_unlock_irqrestore(&ether->lock, flags);
+
+		if (status & MISTA_TXCP)
+			ether->tx_cp_i++;
+		if (status & MISTA_TDU)
+			ether->tx_tdu_i++;
+	} else {
+		dev_dbg(&pdev->dev, "status=0x%08X\n", status);
+	}
+
+	napi_schedule(&ether->napi);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t npcm7xx_rx_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = (struct net_device *)dev_id;
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	struct platform_device *pdev = ether->pdev;
+	__le32 status;
+	unsigned long flags;
+	unsigned int any_err = 0;
+	__le32 rxfsm;
+
+	npcm7xx_get_and_clear_int(dev, &status, 0xFFFF);
+	ether->rx_int_count++;
+
+	if (unlikely(status & MISTA_RXBERR)) {
+		ether->rx_berr++;
+		dev_err(&pdev->dev, "emc rx bus error status=0x%08X\n", status);
+#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG
+		npcm7xx_info_print(dev);
+#endif
+		spin_lock_irqsave(&ether->lock, flags);
+		writel(0, (ether->reg + REG_MIEN)); /* disable any interrupt */
+		spin_unlock_irqrestore(&ether->lock, flags);
+		ether->need_reset = 1;
+		napi_schedule(&ether->napi);
+		return IRQ_HANDLED;
+	}
+
+	if (unlikely(status & (MISTA_RXOV | MISTA_RDU))) {
+		/*
+		 * filter out all received packets until we have
+		 * enough available buffer descriptors
+		 */
+		writel(0, (ether->reg + REG_CAMCMR));
+		any_err = 1;
+		if (status & (MISTA_RXOV))
+			ether->rxov++;
+		if (status & (MISTA_RDU))
+			ether->rdu++;
+
+		/*
+		 * workaround Errata 1.36: EMC Hangs on receiving 253-256
+		 * byte packet
+		 */
+		rxfsm = readl((ether->reg + REG_RXFSM));
+
+		if ((rxfsm & 0xFFFFF000) == 0x08044000) {
+			int i;
+
+			for (i = 0; i < 32; i++) {
+				rxfsm = readl((ether->reg + REG_RXFSM));
+				if ((rxfsm & 0xFFFFF000) != 0x08044000)
+					break;
+			}
+			if (i == 32) {
+				ether->rx_stuck++;
+				spin_lock_irqsave(&ether->lock, flags);
+#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG
+				npcm7xx_info_print(dev);
+#endif
+				writel(0,  (ether->reg + REG_MIEN));
+				spin_unlock_irqrestore(&ether->lock, flags);
+				ether->need_reset = 1;
+				    napi_schedule(&ether->napi);
+				dev_err(&pdev->dev, "stuck on REG_RXFSM = 0x%08X status=%08X doing reset!\n", rxfsm, status);
+				return IRQ_HANDLED;
+			}
+		}
+	}
+
+	/* echo MISTA status on unexpected flags although we don't do anithing with them */
+	if (unlikely(status &
+		     (/* MISTA_RXINTR | */ /* Receive - all RX interrupt set this */
+			       MISTA_CRCE   |    /* CRC Error */
+			    /* MISTA_RXOV   | */ /* Receive FIFO Overflow - we alread handled it */
+			       (MISTA_PTLE * !IS_VLAN) | /* Packet Too Long is needed if VLAN is not supported */
+			    /* MISTA_RXGD   | */ /* Receive Good - this is the common good case */
+			       MISTA_ALIE   |    /* Alignment Error */
+			       MISTA_RP     |    /* Runt Packet */
+			       MISTA_MMP    |    /* More Missed Packet */
+			       MISTA_DFOI   |    /* Maximum Frame Length */
+			    /* MISTA_DENI   | */ /* DMA Early Notification - every packet get this */
+			    /* MISTA_RDU    | */ /* Receive Descriptor Unavailable */
+			    /* MISTA_RXBERR | */ /* Receive Bus Error Interrupt - we alread handled it */
+			    /* MISTA_CFR    | */ /* Control Frame Receive - not an error */
+				0))) {
+		dev_dbg(&pdev->dev, "emc rx MISTA status=0x%08X\n", status);
+		any_err = 1;
+		ether->rx_err++;
+	}
+
+	if (!any_err && ((status & MISTA_RXGD) == 0))
+		dev_err(&pdev->dev, "emc rx MISTA status=0x%08X\n", status);
+
+	spin_lock_irqsave(&ether->lock, flags);
+	writel(readl((ether->reg + REG_MIEN)) & ~ENRXGD,
+	       (ether->reg + REG_MIEN));
+	spin_unlock_irqrestore(&ether->lock, flags);
+	napi_schedule(&ether->napi);
+
+	return IRQ_HANDLED;
+}
+
+static int npcm7xx_poll(struct napi_struct *napi, int budget)
+{
+	struct npcm7xx_ether *ether =
+		container_of(napi, struct npcm7xx_ether, napi);
+	struct npcm7xx_rxbd *rxbd;
+	struct net_device *dev = ether->ndev;
+	struct platform_device *pdev = ether->pdev;
+	struct sk_buff *skb, *s;
+	unsigned int length;
+	__le32 status;
+	unsigned long flags;
+	int rx_cnt = 0;
+	int complete = 0;
+	unsigned int rx_offset = (readl((ether->reg + REG_CRXDSA)) -
+				  ether->start_rx_ptr) /
+				sizeof(struct npcm7xx_txbd);
+	unsigned int local_count = (rx_offset >= ether->cur_rx) ?
+		rx_offset - ether->cur_rx : rx_offset +
+		RX_QUEUE_LEN - ether->cur_rx;
+
+	if (local_count > ether->max_waiting_rx)
+		ether->max_waiting_rx = local_count;
+
+	if (local_count > (4 * RX_POLL_SIZE))
+		/*
+		 * we are porbably in a storm of short packets and we don't
+		 * want to get into RDU since short packets in RDU cause
+		 * many RXOV which may cause EMC halt, so we filter out all
+		 * coming packets
+		 */
+		writel(0, (ether->reg + REG_CAMCMR));
+
+	if (local_count <= budget)
+		/* we can restore accepting of packets */
+		writel(ether->camcmr, (ether->reg + REG_CAMCMR));
+
+	spin_lock_irqsave(&ether->lock, flags);
+	npcm7xx_clean_tx(dev, false);
+	spin_unlock_irqrestore(&ether->lock, flags);
+
+	rxbd = (ether->rdesc + ether->cur_rx);
+
+	while (rx_cnt < budget) {
+		status = rxbd->sl;
+		if ((status & RX_OWN_DMA) == RX_OWN_DMA) {
+			complete = 1;
+			break;
+		}
+		/* for debug puposes we save the previous value */
+		rxbd->reserved = status;
+		s = ether->rx_skb[ether->cur_rx];
+		length = status & 0xFFFF;
+
+		/*
+		 * If VLAN is not supporte RXDS_PTLE (packet too long) is also
+		 * an error
+		 */
+		if (likely((status & (RXDS_RXGD | RXDS_CRCE | RXDS_ALIE |
+				      RXDS_RP | (IS_VLAN ? 0 : RXDS_PTLE))) ==
+			   RXDS_RXGD) && likely(length <= MAX_PACKET_SIZE)) {
+			dma_unmap_single(&dev->dev, (dma_addr_t)rxbd->buffer,
+					 roundup(MAX_PACKET_SIZE_W_CRC, 4),
+					 DMA_FROM_DEVICE);
+
+			skb_put(s, length);
+			s->protocol = eth_type_trans(s, dev);
+			netif_receive_skb(s);
+			ether->stats.rx_packets++;
+			ether->stats.rx_bytes += length;
+			rx_cnt++;
+			ether->rx_count_pool++;
+
+			/* now we allocate new skb instead if the used one. */
+			skb = dev_alloc_skb(roundup(MAX_PACKET_SIZE_W_CRC, 4));
+			if (!skb) {
+				dev_err(&pdev->dev, "get skb buffer error\n");
+				ether->stats.rx_dropped++;
+				goto rx_out;
+			}
+
+			/* Do not unmark the following skb_reserve() Receive
+			 * Buffer Starting Address must be aligned
+			 * to 4 bytes and the following line if unmarked
+			 * will make it align to 2 and this likely
+			 * will hult the RX and crash the linux
+			 * skb_reserve(skb, NET_IP_ALIGN);
+			 */
+			skb->dev = dev;
+
+			rxbd->buffer = dma_map_single(&dev->dev, skb->data,
+						      roundup(MAX_PACKET_SIZE_W_CRC, 4),
+						      DMA_FROM_DEVICE);
+			ether->rx_skb[ether->cur_rx] = skb;
+		} else {
+			ether->rx_err_count++;
+			ether->stats.rx_errors++;
+			dev_dbg(&pdev->dev, "rx_errors = %lu status = 0x%08X\n",
+				ether->stats.rx_errors, status);
+
+			if (status & RXDS_RP) {
+				ether->stats.rx_length_errors++;
+				dev_dbg(&pdev->dev, "rx_length_errors = %lu\n",
+					ether->stats.rx_length_errors);
+			} else if (status & RXDS_CRCE) {
+				ether->stats.rx_crc_errors++;
+				dev_dbg(&pdev->dev, "rx_crc_errors = %lu\n",
+					ether->stats.rx_crc_errors);
+			} else if (status & RXDS_ALIE) {
+				ether->stats.rx_frame_errors++;
+				dev_dbg(&pdev->dev, "rx_frame_errors = %lu\n",
+					ether->stats.rx_frame_errors);
+			} else if (((!IS_VLAN) && (status & RXDS_PTLE)) ||
+				   length > MAX_PACKET_SIZE) {
+				ether->stats.rx_length_errors++;
+				dev_dbg(&pdev->dev, "rx_length_errors = %lu\n",
+					ether->stats.rx_length_errors);
+			}
+		}
+
+		wmb();
+		rxbd->sl = RX_OWN_DMA;
+		wmb();
+
+		if (++ether->cur_rx >= RX_QUEUE_LEN)
+			ether->cur_rx = 0;
+
+		rxbd = (ether->rdesc + ether->cur_rx);
+	}
+
+	if (complete) {
+		napi_complete(napi);
+
+		if (ether->need_reset) {
+			dev_dbg(&pdev->dev, "Reset\n");
+			npcm7xx_reset_mac(dev, 1);
+		}
+
+		spin_lock_irqsave(&ether->lock, flags);
+		writel(readl((ether->reg + REG_MIEN)) | ENRXGD,  (ether->reg +
+								  REG_MIEN));
+		spin_unlock_irqrestore(&ether->lock, flags);
+	} else {
+		rx_offset = (readl((ether->reg + REG_CRXDSA)) -
+			     ether->start_rx_ptr) / sizeof(struct npcm7xx_txbd);
+		local_count = (rx_offset >= ether->cur_rx) ? rx_offset -
+			ether->cur_rx : rx_offset + RX_QUEUE_LEN -
+			ether->cur_rx;
+
+		if (local_count > ether->max_waiting_rx)
+			ether->max_waiting_rx = local_count;
+
+		if (local_count > (3 * RX_POLL_SIZE))
+			/*
+			 * we are porbably in a storm of short packets and
+			 * we don't want to get into RDU since short packets in
+			 * RDU cause many RXOV which may cause
+			 * EMC halt, so we filter out all coming packets
+			 */
+			writel(0, (ether->reg + REG_CAMCMR));
+		if (local_count <= RX_POLL_SIZE)
+			/* we can restore accepting of packets */
+			writel(ether->camcmr, (ether->reg + REG_CAMCMR));
+	}
+rx_out:
+
+	/* trigger RX */
+	writel(ENSTART, (ether->reg + REG_RSDR));
+	return rx_cnt;
+}
+
+static int npcm7xx_ether_open(struct net_device *dev)
+{
+	struct npcm7xx_ether *ether;
+	struct platform_device *pdev;
+
+	ether = netdev_priv(dev);
+	pdev = ether->pdev;
+
+	if (ether->use_ncsi) {
+		ether->speed = 100;
+		ether->duplex = DUPLEX_FULL;
+		npcm7xx_opmode(dev, 100, DUPLEX_FULL);
+	}
+	npcm7xx_reset_mac(dev, 0);
+
+	if (request_irq(ether->txirq, npcm7xx_tx_interrupt, 0x0, pdev->name,
+			dev)) {
+		dev_err(&pdev->dev, "register irq tx failed\n");
+		npcm7xx_ether_close(dev);
+		return -EAGAIN;
+	}
+
+	if (request_irq(ether->rxirq, npcm7xx_rx_interrupt, 0x0, pdev->name,
+			dev)) {
+		dev_err(&pdev->dev, "register irq rx failed\n");
+		npcm7xx_ether_close(dev);
+		return -EAGAIN;
+	}
+
+	if (ether->phy_dev)
+		phy_start(ether->phy_dev);
+	else if (ether->use_ncsi)
+		netif_carrier_on(dev);
+
+	netif_start_queue(dev);
+	napi_enable(&ether->napi);
+
+	/* trigger RX */
+	writel(ENSTART, (ether->reg + REG_RSDR));
+
+	/* Start the NCSI device */
+	if (ether->use_ncsi) {
+		int err = ncsi_start_dev(ether->ncsidev);
+
+		if (err) {
+			npcm7xx_ether_close(dev);
+			return err;
+		}
+	}
+
+	dev_info(&pdev->dev, "%s is OPENED\n", dev->name);
+
+	return 0;
+}
+
+static int npcm7xx_ether_ioctl(struct net_device *dev,
+			       struct ifreq *ifr, int cmd)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	struct phy_device *phydev = ether->phy_dev;
+
+	if (!netif_running(dev))
+		return -EINVAL;
+
+	if (!phydev)
+		return -ENODEV;
+
+	return phy_mii_ioctl(phydev, ifr, cmd);
+}
+
+static void npcm7xx_get_drvinfo(struct net_device *dev,
+				struct ethtool_drvinfo *info)
+{
+	strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver));
+	strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version));
+	strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));
+	strlcpy(info->bus_info, "N/A", sizeof(info->bus_info));
+}
+
+static int npcm7xx_get_settings(struct net_device *dev,
+				struct ethtool_link_ksettings *cmd)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	struct phy_device *phydev = ether->phy_dev;
+
+	if (!phydev)
+		return -ENODEV;
+
+	dev_info(&ether->pdev->dev, "\n\nnpcm7xx_get_settings\n");
+	phy_ethtool_ksettings_get(phydev, cmd);
+
+	return 0;
+}
+
+static int npcm7xx_set_settings(struct net_device *dev,
+				const struct ethtool_link_ksettings *cmd)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	struct phy_device *phydev = ether->phy_dev;
+	int ret;
+
+	if (!phydev)
+		return -ENODEV;
+
+	dev_info(&ether->pdev->dev, "\n\nnpcm7xx_set_settings\n");
+	ret =  phy_ethtool_ksettings_set(phydev, cmd);
+
+	return ret;
+}
+
+static u32 npcm7xx_get_msglevel(struct net_device *dev)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+
+	return ether->msg_enable;
+}
+
+static void npcm7xx_set_msglevel(struct net_device *dev, u32 level)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+
+	ether->msg_enable = level;
+}
+
+static const struct ethtool_ops npcm7xx_ether_ethtool_ops = {
+	.get_link_ksettings     = npcm7xx_get_settings,
+	.set_link_ksettings     = npcm7xx_set_settings,
+	.get_drvinfo	= npcm7xx_get_drvinfo,
+	.get_msglevel	= npcm7xx_get_msglevel,
+	.set_msglevel	= npcm7xx_set_msglevel,
+	.get_link	= ethtool_op_get_link,
+};
+
+static const struct net_device_ops npcm7xx_ether_netdev_ops = {
+	.ndo_open		= npcm7xx_ether_open,
+	.ndo_stop		= npcm7xx_ether_close,
+	.ndo_start_xmit		= npcm7xx_ether_start_xmit,
+	.ndo_get_stats		= npcm7xx_ether_stats,
+	.ndo_set_rx_mode	= npcm7xx_ether_set_rx_mode,
+	.ndo_set_mac_address	= npcm7xx_set_mac_address,
+	.ndo_do_ioctl		= npcm7xx_ether_ioctl,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+};
+
+static void get_mac_address(struct net_device *dev)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	struct platform_device *pdev = ether->pdev;
+	struct device_node *np = ether->pdev->dev.of_node;
+	const u8 *mac_address = NULL;
+
+	mac_address = of_get_mac_address(np);
+
+	if (mac_address != 0)
+		ether_addr_copy(dev->dev_addr, mac_address);
+
+	if (is_valid_ether_addr(dev->dev_addr)) {
+		dev_info(&pdev->dev, "%s: device MAC address : %pM\n",
+			 pdev->name, dev->dev_addr);
+	} else {
+		eth_hw_addr_random(dev);
+		dev_info(&pdev->dev, "%s: device MAC address (random generator) %pM\n",
+			 dev->name, dev->dev_addr);
+	}
+}
+
+static int npcm7xx_mii_setup(struct net_device *dev)
+{
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+	struct platform_device *pdev;
+	struct phy_device *phydev = NULL;
+	int i, err = 0;
+
+	pdev = ether->pdev;
+
+	ether->mii_bus = mdiobus_alloc();
+	if (!ether->mii_bus) {
+		err = -ENOMEM;
+		dev_err(&pdev->dev, "mdiobus_alloc() failed\n");
+		goto out0;
+	}
+
+	ether->mii_bus->name = "npcm7xx_rmii";
+	ether->mii_bus->read = &npcm7xx_mdio_read;
+	ether->mii_bus->write = &npcm7xx_mdio_write;
+	ether->mii_bus->reset = &npcm7xx_mdio_reset;
+	snprintf(ether->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
+		 ether->pdev->name, ether->pdev->id);
+	dev_dbg(&pdev->dev, "%s ether->mii_bus->id=%s\n", __func__,
+		ether->mii_bus->id);
+	ether->mii_bus->priv = ether;
+	ether->mii_bus->parent = &ether->pdev->dev;
+
+	for (i = 0; i < PHY_MAX_ADDR; i++)
+		ether->mii_bus->irq[i] = PHY_POLL;
+
+	platform_set_drvdata(ether->pdev, ether->mii_bus);
+
+	/* Enable MDIO Clock */
+	writel(readl((ether->reg + REG_MCMDR)) | MCMDR_ENMDC,
+	       (ether->reg + REG_MCMDR));
+
+	if (mdiobus_register(ether->mii_bus)) {
+		dev_err(&pdev->dev, "mdiobus_register() failed\n");
+		goto out2;
+	}
+
+	phydev = phy_find_first(ether->mii_bus);
+	if (!phydev) {
+		dev_err(&pdev->dev, "phy_find_first() failed\n");
+		goto out3;
+	}
+
+	dev_info(&pdev->dev, " name = %s ETH-Phy-Id = 0x%x\n",
+		 phydev_name(phydev), phydev->phy_id);
+
+	phydev = phy_connect(dev, phydev_name(phydev),
+			     &adjust_link,
+			     PHY_INTERFACE_MODE_RMII);
+
+	dev_info(&pdev->dev, " ETH-Phy-Id = 0x%x name = %s\n",
+		 phydev->phy_id, phydev->drv->name);
+
+	if (IS_ERR(phydev)) {
+		err = PTR_ERR(phydev);
+		dev_err(&pdev->dev, "phy_connect() failed - %d\n", err);
+		goto out3;
+	}
+
+	linkmode_and(phydev->supported, phydev->supported, PHY_BASIC_FEATURES);
+	linkmode_copy(phydev->advertising, phydev->supported);
+	ether->phy_dev = phydev;
+
+	return 0;
+
+out3:
+	mdiobus_unregister(ether->mii_bus);
+out2:
+	kfree(ether->mii_bus->irq);
+	mdiobus_free(ether->mii_bus);
+out0:
+
+	return err;
+}
+
+static const struct of_device_id emc_dt_id[] = {
+	{ .compatible = "nuvoton,npcm750-emc",  },
+	{},
+};
+MODULE_DEVICE_TABLE(of, emc_dt_id);
+
+static void npcm7xx_ncsi_handler(struct ncsi_dev *nd)
+{
+	if (unlikely(nd->state != ncsi_dev_state_functional))
+		return;
+
+	netdev_info(nd->dev, "NCSI interface %s\n",
+		    nd->link_up ? "up" : "down");
+}
+
+static int npcm7xx_ether_probe(struct platform_device *pdev)
+{
+	struct npcm7xx_ether *ether;
+	struct net_device *dev;
+	int error;
+
+	struct clk *emc_clk = NULL;
+	struct device_node *np = pdev->dev.of_node;
+
+	pdev->id = of_alias_get_id(np, "ethernet");
+	if (pdev->id < 0)
+		pdev->id = 0;
+
+	emc_clk = devm_clk_get(&pdev->dev, NULL);
+
+	if (IS_ERR(emc_clk))
+		return PTR_ERR(emc_clk);
+
+	/* Enable Clock */
+	clk_prepare_enable(emc_clk);
+
+	error = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+	if (error)
+		return -ENODEV;
+
+	dev = alloc_etherdev(sizeof(struct npcm7xx_ether));
+	if (!dev)
+		return -ENOMEM;
+
+	ether = netdev_priv(dev);
+
+	ether->rst_regmap =
+		syscon_regmap_lookup_by_compatible("nuvoton,npcm750-rst");
+	if (IS_ERR(ether->rst_regmap)) {
+		dev_err(&pdev->dev, "%s: failed to find nuvoton,npcm750-rst\n", __func__);
+		return IS_ERR(ether->rst_regmap);
+	}
+
+	/* Reset EMC module */
+	if (pdev->id == 0) {
+		regmap_update_bits(ether->rst_regmap, IPSRST1_OFFSET,
+				   (0x1 << 6), (0x1 << 6));
+		regmap_update_bits(ether->rst_regmap, IPSRST1_OFFSET,
+				   (0x1 << 6), 0);
+	}
+	if (pdev->id == 1) {
+		regmap_update_bits(ether->rst_regmap, IPSRST1_OFFSET,
+				   (0x1 << 21), (0x1 << 21));
+		regmap_update_bits(ether->rst_regmap, IPSRST1_OFFSET,
+				   (0x1 << 21), 0);
+	}
+
+	ether->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!ether->res) {
+		dev_err(&pdev->dev, "failed to get I/O memory\n");
+		error = -ENXIO;
+		goto failed_free;
+	}
+
+	if (!request_mem_region(ether->res->start,
+				resource_size(ether->res), pdev->name)) {
+		dev_err(&pdev->dev, "failed to request I/O memory\n");
+		error = -EBUSY;
+		goto failed_free;
+	}
+
+	ether->reg = ioremap(ether->res->start, resource_size(ether->res));
+	dev_dbg(&pdev->dev, "%s ether->reg = 0x%x\n", __func__,
+		(unsigned int)ether->reg);
+
+	if (!ether->reg) {
+		dev_err(&pdev->dev, "failed to remap I/O memory\n");
+		error = -ENXIO;
+		goto failed_free_mem;
+	}
+
+	ether->txirq = platform_get_irq(pdev, 0);
+	if (ether->txirq < 0) {
+		dev_err(&pdev->dev, "failed to get ether tx irq\n");
+		error = -ENXIO;
+		goto failed_free_io;
+	}
+
+	ether->rxirq = platform_get_irq(pdev, 1);
+	if (ether->rxirq < 0) {
+		dev_err(&pdev->dev, "failed to get ether rx irq\n");
+		error = -ENXIO;
+		goto failed_free_io;
+	}
+
+	SET_NETDEV_DEV(dev, &pdev->dev);
+	platform_set_drvdata(pdev, dev);
+	ether->ndev = dev;
+
+	ether->pdev = pdev;
+	ether->msg_enable = NETIF_MSG_LINK;
+
+	dev->netdev_ops = &npcm7xx_ether_netdev_ops;
+	dev->ethtool_ops = &npcm7xx_ether_ethtool_ops;
+
+	dev->tx_queue_len = TX_QUEUE_LEN;
+	dev->dma = 0x0;
+	dev->watchdog_timeo = TX_TIMEOUT;
+
+	get_mac_address(dev);
+
+	ether->cur_tx = 0x0;
+	ether->cur_rx = 0x0;
+	ether->finish_tx = 0x0;
+	ether->pending_tx = 0x0;
+	ether->link = 0;
+	ether->speed = 100;
+	ether->duplex = DUPLEX_FULL;
+	ether->need_reset = 0;
+	ether->dump_buf = NULL;
+	ether->rx_berr = 0;
+	ether->rx_err = 0;
+	ether->rdu = 0;
+	ether->rxov = 0;
+	ether->rx_stuck = 0;
+	/* debug counters */
+	ether->max_waiting_rx = 0;
+	ether->rx_count_pool = 0;
+	ether->count_xmit = 0;
+	ether->rx_int_count = 0;
+	ether->rx_err_count = 0;
+	ether->tx_int_count = 0;
+	ether->count_finish = 0;
+	ether->tx_tdu = 0;
+	ether->tx_tdu_i = 0;
+	ether->tx_cp_i = 0;
+
+	spin_lock_init(&ether->lock);
+
+	netif_napi_add(dev, &ether->napi, npcm7xx_poll, RX_POLL_SIZE);
+
+	if (pdev->dev.of_node &&
+	    of_get_property(pdev->dev.of_node, "use-ncsi", NULL)) {
+		if (!IS_ENABLED(CONFIG_NET_NCSI)) {
+			dev_err(&pdev->dev, "CONFIG_NET_NCSI not enabled\n");
+			error = -ENODEV;
+			goto failed_free_napi;
+		}
+		dev_info(&pdev->dev, "Using NCSI interface\n");
+		ether->use_ncsi = true;
+		ether->ncsidev = ncsi_register_dev(dev, npcm7xx_ncsi_handler);
+		if (!ether->ncsidev) {
+			error = -ENODEV;
+			goto failed_free_napi;
+		}
+	} else {
+		ether->use_ncsi = false;
+	error = npcm7xx_mii_setup(dev);
+	if (error < 0) {
+		dev_err(&pdev->dev, "npcm7xx_mii_setup err\n");
+		goto failed_free_napi;
+		}
+	}
+
+	error = register_netdev(dev);
+	if (error != 0) {
+		dev_err(&pdev->dev, "register_netdev() failed\n");
+		error = -ENODEV;
+		goto failed_free_napi;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	npcm7xx_debug_fs(ether);
+#endif
+
+	return 0;
+
+failed_free_napi:
+	netif_napi_del(&ether->napi);
+	platform_set_drvdata(pdev, NULL);
+failed_free_io:
+	iounmap(ether->reg);
+failed_free_mem:
+	release_mem_region(ether->res->start, resource_size(ether->res));
+failed_free:
+	free_netdev(dev);
+
+	return error;
+}
+
+static int npcm7xx_ether_remove(struct platform_device *pdev)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+	struct npcm7xx_ether *ether = netdev_priv(dev);
+
+#ifdef CONFIG_DEBUG_FS
+	debugfs_remove_recursive(ether->dbgfs_dir);
+#endif
+
+	unregister_netdev(dev);
+
+	free_irq(ether->txirq, dev);
+	free_irq(ether->rxirq, dev);
+
+	if (ether->phy_dev)
+		phy_disconnect(ether->phy_dev);
+
+	mdiobus_unregister(ether->mii_bus);
+	kfree(ether->mii_bus->irq);
+	mdiobus_free(ether->mii_bus);
+
+	platform_set_drvdata(pdev, NULL);
+
+	free_netdev(dev);
+	return 0;
+}
+
+static struct platform_driver npcm7xx_ether_driver = {
+	.probe		= npcm7xx_ether_probe,
+	.remove		= npcm7xx_ether_remove,
+	.driver		= {
+		.name	= DRV_MODULE_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(emc_dt_id),
+	},
+};
+
+module_platform_driver(npcm7xx_ether_driver);
+
+MODULE_AUTHOR("Nuvoton Technology Corp.");
+MODULE_DESCRIPTION("NPCM750 EMC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:npcm750-emc");
+MODULE_VERSION(DRV_MODULE_VERSION);
diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig
new file mode 100644
index 0000000..7293108
--- /dev/null
+++ b/drivers/peci/Kconfig
@@ -0,0 +1,50 @@
+#
+# Platform Environment Control Interface (PECI) subsystem configuration
+#
+
+config PECI
+	bool "PECI support"
+	select RT_MUTEXES
+	select CRC8
+	help
+	  The Platform Environment Control Interface (PECI) is a one-wire bus
+	  interface that provides a communication channel from Intel processors
+	  and chipset components to external monitoring or control devices.
+
+	  If you want PECI support, you should say Y here and also to the
+	  specific driver for your bus adapter(s) below.
+
+if PECI
+
+#
+# PECI hardware bus configuration
+#
+
+menu "PECI Hardware Bus support"
+
+config PECI_ASPEED
+	tristate "ASPEED PECI support"
+	select REGMAP_MMIO
+	depends on OF
+	depends on ARCH_ASPEED || COMPILE_TEST
+	help
+	  Say Y here if you want support for the Platform Environment Control
+	  Interface (PECI) bus adapter driver on the ASPEED SoCs.
+
+	  This support is also available as a module.  If so, the module
+	  will be called peci-aspeed.
+
+config PECI_NPCM
+	tristate "Nuvoton NPCM PECI support"
+	select REGMAP_MMIO
+	depends on OF
+	depends on ARCH_NPCM || COMPILE_TEST
+	help
+	  Say Y here if you want support for the Platform Environment Control
+	  Interface (PECI) bus adapter driver on the Nuvoton NPCM SoCs.
+
+	  This support is also available as a module. If so, the module
+	  will be called peci-npcm.
+endmenu
+
+endif # PECI
diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile
new file mode 100644
index 0000000..3326da5
--- /dev/null
+++ b/drivers/peci/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the PECI core and bus drivers.
+#
+
+# Core functionality
+obj-$(CONFIG_PECI)		+= peci-core.o
+
+# Hardware specific bus drivers
+obj-$(CONFIG_PECI_ASPEED)	+= peci-aspeed.o
+obj-$(CONFIG_PECI_NPCM)		+= peci-npcm.o
diff --git a/drivers/peci/peci-aspeed.c b/drivers/peci/peci-aspeed.c
new file mode 100644
index 0000000..51cb256
--- /dev/null
+++ b/drivers/peci/peci-aspeed.c
@@ -0,0 +1,505 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2012-2017 ASPEED Technology Inc.
+// Copyright (c) 2018 Intel Corporation
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/peci.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+/* ASPEED PECI Registers */
+#define ASPEED_PECI_CTRL     0x00
+#define ASPEED_PECI_TIMING   0x04
+#define ASPEED_PECI_CMD      0x08
+#define ASPEED_PECI_CMD_CTRL 0x0c
+#define ASPEED_PECI_EXP_FCS  0x10
+#define ASPEED_PECI_CAP_FCS  0x14
+#define ASPEED_PECI_INT_CTRL 0x18
+#define ASPEED_PECI_INT_STS  0x1c
+#define ASPEED_PECI_W_DATA0  0x20
+#define ASPEED_PECI_W_DATA1  0x24
+#define ASPEED_PECI_W_DATA2  0x28
+#define ASPEED_PECI_W_DATA3  0x2c
+#define ASPEED_PECI_R_DATA0  0x30
+#define ASPEED_PECI_R_DATA1  0x34
+#define ASPEED_PECI_R_DATA2  0x38
+#define ASPEED_PECI_R_DATA3  0x3c
+#define ASPEED_PECI_W_DATA4  0x40
+#define ASPEED_PECI_W_DATA5  0x44
+#define ASPEED_PECI_W_DATA6  0x48
+#define ASPEED_PECI_W_DATA7  0x4c
+#define ASPEED_PECI_R_DATA4  0x50
+#define ASPEED_PECI_R_DATA5  0x54
+#define ASPEED_PECI_R_DATA6  0x58
+#define ASPEED_PECI_R_DATA7  0x5c
+
+/* ASPEED_PECI_CTRL - 0x00 : Control Register */
+#define PECI_CTRL_SAMPLING_MASK      GENMASK(19, 16)
+#define PECI_CTRL_READ_MODE_MASK     GENMASK(13, 12)
+#define PECI_CTRL_READ_MODE_COUNT    BIT(12)
+#define PECI_CTRL_READ_MODE_DBG      BIT(13)
+#define PECI_CTRL_CLK_SOURCE_MASK    BIT(11)
+#define PECI_CTRL_CLK_DIV_MASK       GENMASK(10, 8)
+#define PECI_CTRL_INVERT_OUT         BIT(7)
+#define PECI_CTRL_INVERT_IN          BIT(6)
+#define PECI_CTRL_BUS_CONTENT_EN     BIT(5)
+#define PECI_CTRL_PECI_EN            BIT(4)
+#define PECI_CTRL_PECI_CLK_EN        BIT(0)
+
+/* ASPEED_PECI_TIMING - 0x04 : Timing Negotiation Register */
+#define PECI_TIMING_MESSAGE_MASK     GENMASK(15, 8)
+#define PECI_TIMING_ADDRESS_MASK     GENMASK(7, 0)
+
+/* ASPEED_PECI_CMD - 0x08 : Command Register */
+#define PECI_CMD_PIN_MON             BIT(31)
+#define PECI_CMD_STS_MASK            GENMASK(27, 24)
+#define PECI_CMD_IDLE_MASK           (PECI_CMD_STS_MASK | PECI_CMD_PIN_MON)
+#define PECI_CMD_FIRE                BIT(0)
+
+/* ASPEED_PECI_LEN - 0x0C : Read/Write Length Register */
+#define PECI_AW_FCS_EN               BIT(31)
+#define PECI_READ_LEN_MASK           GENMASK(23, 16)
+#define PECI_WRITE_LEN_MASK          GENMASK(15, 8)
+#define PECI_TAGET_ADDR_MASK         GENMASK(7, 0)
+
+/* ASPEED_PECI_EXP_FCS - 0x10 : Expected FCS Data Register */
+#define PECI_EXPECT_READ_FCS_MASK    GENMASK(23, 16)
+#define PECI_EXPECT_AW_FCS_AUTO_MASK GENMASK(15, 8)
+#define PECI_EXPECT_WRITE_FCS_MASK   GENMASK(7, 0)
+
+/* ASPEED_PECI_CAP_FCS - 0x14 : Captured FCS Data Register */
+#define PECI_CAPTURE_READ_FCS_MASK   GENMASK(23, 16)
+#define PECI_CAPTURE_WRITE_FCS_MASK  GENMASK(7, 0)
+
+/* ASPEED_PECI_INT_CTRL/STS - 0x18/0x1c : Interrupt Register */
+#define PECI_INT_TIMING_RESULT_MASK  GENMASK(31, 30)
+#define PECI_INT_TIMEOUT             BIT(4)
+#define PECI_INT_CONNECT             BIT(3)
+#define PECI_INT_W_FCS_BAD           BIT(2)
+#define PECI_INT_W_FCS_ABORT         BIT(1)
+#define PECI_INT_CMD_DONE            BIT(0)
+
+#define PECI_INT_MASK  (PECI_INT_TIMEOUT | PECI_INT_CONNECT | \
+			PECI_INT_W_FCS_BAD | PECI_INT_W_FCS_ABORT | \
+			PECI_INT_CMD_DONE)
+
+#define PECI_IDLE_CHECK_TIMEOUT_USEC    50000
+#define PECI_IDLE_CHECK_INTERVAL_USEC   10000
+
+#define PECI_RD_SAMPLING_POINT_DEFAULT  8
+#define PECI_RD_SAMPLING_POINT_MAX      15
+#define PECI_CLK_DIV_DEFAULT            0
+#define PECI_CLK_DIV_MAX                7
+#define PECI_MSG_TIMING_DEFAULT         1
+#define PECI_MSG_TIMING_MAX             255
+#define PECI_ADDR_TIMING_DEFAULT        1
+#define PECI_ADDR_TIMING_MAX            255
+#define PECI_CMD_TIMEOUT_MS_DEFAULT     1000
+#define PECI_CMD_TIMEOUT_MS_MAX         60000
+
+struct aspeed_peci {
+	struct peci_adapter	*adapter;
+	struct device		*dev;
+	struct regmap		*regmap;
+	struct clk		*clk;
+	struct reset_control	*rst;
+	int			irq;
+	spinlock_t		lock; /* to sync completion status handling */
+	struct completion	xfer_complete;
+	u32			status;
+	u32			cmd_timeout_ms;
+};
+
+static int aspeed_peci_xfer_native(struct aspeed_peci *priv,
+				   struct peci_xfer_msg *msg)
+{
+	long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms);
+	u32 peci_head, peci_state, rx_data, cmd_sts;
+	unsigned long flags;
+	int i, rc;
+	uint reg;
+
+	/* Check command sts and bus idle state */
+	rc = regmap_read_poll_timeout(priv->regmap, ASPEED_PECI_CMD, cmd_sts,
+				      !(cmd_sts & PECI_CMD_IDLE_MASK),
+				      PECI_IDLE_CHECK_INTERVAL_USEC,
+				      PECI_IDLE_CHECK_TIMEOUT_USEC);
+	if (rc)
+		return rc; /* -ETIMEDOUT */
+
+	spin_lock_irqsave(&priv->lock, flags);
+	reinit_completion(&priv->xfer_complete);
+
+	peci_head = FIELD_PREP(PECI_TAGET_ADDR_MASK, msg->addr) |
+		    FIELD_PREP(PECI_WRITE_LEN_MASK, msg->tx_len) |
+		    FIELD_PREP(PECI_READ_LEN_MASK, msg->rx_len);
+
+	regmap_write(priv->regmap, ASPEED_PECI_CMD_CTRL, peci_head);
+
+	for (i = 0; i < msg->tx_len; i += 4) {
+		reg = i < 16 ? ASPEED_PECI_W_DATA0 + i % 16 :
+			       ASPEED_PECI_W_DATA4 + i % 16;
+		regmap_write(priv->regmap, reg,
+			     le32_to_cpup((__le32 *)&msg->tx_buf[i]));
+	}
+
+	dev_dbg(priv->dev, "HEAD : 0x%08x\n", peci_head);
+	print_hex_dump_debug("TX : ", DUMP_PREFIX_NONE, 16, 1,
+			     msg->tx_buf, msg->tx_len, true);
+
+	priv->status = 0;
+	regmap_write(priv->regmap, ASPEED_PECI_CMD, PECI_CMD_FIRE);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	err = wait_for_completion_interruptible_timeout(&priv->xfer_complete,
+							timeout);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	dev_dbg(priv->dev, "INT_STS : 0x%08x\n", priv->status);
+	regmap_read(priv->regmap, ASPEED_PECI_CMD, &peci_state);
+	dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n",
+		FIELD_GET(PECI_CMD_STS_MASK, peci_state));
+
+	regmap_write(priv->regmap, ASPEED_PECI_CMD, 0);
+
+	if (err <= 0 || priv->status != PECI_INT_CMD_DONE) {
+		if (err < 0) { /* -ERESTARTSYS */
+			rc = (int)err;
+			goto err_irqrestore;
+		} else if (err == 0) {
+			dev_dbg(priv->dev, "Timeout waiting for a response!\n");
+			rc = -ETIMEDOUT;
+			goto err_irqrestore;
+		}
+
+		dev_dbg(priv->dev, "No valid response!\n");
+		rc = -EIO;
+		goto err_irqrestore;
+	}
+
+	/**
+	 * Note that rx_len and rx_buf size can be an odd number.
+	 * Byte handling is more efficient.
+	 */
+	for (i = 0; i < msg->rx_len; i++) {
+		u8 byte_offset = i % 4;
+
+		if (byte_offset == 0) {
+			reg = i < 16 ? ASPEED_PECI_R_DATA0 + i % 16 :
+				       ASPEED_PECI_R_DATA4 + i % 16;
+			regmap_read(priv->regmap, reg, &rx_data);
+		}
+
+		msg->rx_buf[i] = (u8)(rx_data >> (byte_offset << 3));
+	}
+
+	print_hex_dump_debug("RX : ", DUMP_PREFIX_NONE, 16, 1,
+			     msg->rx_buf, msg->rx_len, true);
+
+	regmap_read(priv->regmap, ASPEED_PECI_CMD, &peci_state);
+	dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n",
+		FIELD_GET(PECI_CMD_STS_MASK, peci_state));
+	dev_dbg(priv->dev, "------------------------\n");
+
+err_irqrestore:
+	spin_unlock_irqrestore(&priv->lock, flags);
+	return rc;
+}
+
+static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg)
+{
+	struct aspeed_peci *priv = arg;
+	u32 status_ack = 0;
+	u32 status;
+
+	spin_lock(&priv->lock);
+	regmap_read(priv->regmap, ASPEED_PECI_INT_STS, &status);
+	priv->status |= (status & PECI_INT_MASK);
+
+	/**
+	 * In most cases, interrupt bits will be set one by one but also note
+	 * that multiple interrupt bits could be set at the same time.
+	 */
+	if (status & PECI_INT_TIMEOUT) {
+		dev_dbg(priv->dev, "PECI_INT_TIMEOUT\n");
+		status_ack |= PECI_INT_TIMEOUT;
+	}
+
+	if (status & PECI_INT_CONNECT) {
+		dev_dbg(priv->dev, "PECI_INT_CONNECT\n");
+		status_ack |= PECI_INT_CONNECT;
+	}
+
+	if (status & PECI_INT_W_FCS_BAD) {
+		dev_dbg(priv->dev, "PECI_INT_W_FCS_BAD\n");
+		status_ack |= PECI_INT_W_FCS_BAD;
+	}
+
+	if (status & PECI_INT_W_FCS_ABORT) {
+		dev_dbg(priv->dev, "PECI_INT_W_FCS_ABORT\n");
+		status_ack |= PECI_INT_W_FCS_ABORT;
+	}
+
+	/**
+	 * All commands should be ended up with a PECI_INT_CMD_DONE bit set
+	 * even in an error case.
+	 */
+	if (status & PECI_INT_CMD_DONE) {
+		dev_dbg(priv->dev, "PECI_INT_CMD_DONE\n");
+		status_ack |= PECI_INT_CMD_DONE;
+		complete(&priv->xfer_complete);
+	}
+
+	regmap_write(priv->regmap, ASPEED_PECI_INT_STS, status_ack);
+	spin_unlock(&priv->lock);
+	return IRQ_HANDLED;
+}
+
+static int aspeed_peci_init_ctrl(struct aspeed_peci *priv)
+{
+	u32 msg_timing, addr_timing, rd_sampling_point;
+	u32 clk_freq, clk_divisor, clk_div_val = 0;
+	int ret;
+
+	priv->clk = devm_clk_get(priv->dev, NULL);
+	if (IS_ERR(priv->clk)) {
+		dev_err(priv->dev, "Failed to get clk source.\n");
+		return PTR_ERR(priv->clk);
+	}
+
+	ret = clk_prepare_enable(priv->clk);
+	if (ret) {
+		dev_err(priv->dev, "Failed to enable clock.\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(priv->dev->of_node, "clock-frequency",
+				   &clk_freq);
+	if (ret) {
+		dev_err(priv->dev,
+			"Could not read clock-frequency property.\n");
+		clk_disable_unprepare(priv->clk);
+		return ret;
+	}
+
+	clk_divisor = clk_get_rate(priv->clk) / clk_freq;
+
+	while ((clk_divisor >> 1) && (clk_div_val < PECI_CLK_DIV_MAX))
+		clk_div_val++;
+
+	ret = of_property_read_u32(priv->dev->of_node, "msg-timing",
+				   &msg_timing);
+	if (ret || msg_timing > PECI_MSG_TIMING_MAX) {
+		if (!ret)
+			dev_warn(priv->dev,
+				 "Invalid msg-timing : %u, Use default : %u\n",
+				 msg_timing, PECI_MSG_TIMING_DEFAULT);
+		msg_timing = PECI_MSG_TIMING_DEFAULT;
+	}
+
+	ret = of_property_read_u32(priv->dev->of_node, "addr-timing",
+				   &addr_timing);
+	if (ret || addr_timing > PECI_ADDR_TIMING_MAX) {
+		if (!ret)
+			dev_warn(priv->dev,
+				 "Invalid addr-timing : %u, Use default : %u\n",
+				 addr_timing, PECI_ADDR_TIMING_DEFAULT);
+		addr_timing = PECI_ADDR_TIMING_DEFAULT;
+	}
+
+	ret = of_property_read_u32(priv->dev->of_node, "rd-sampling-point",
+				   &rd_sampling_point);
+	if (ret || rd_sampling_point > PECI_RD_SAMPLING_POINT_MAX) {
+		if (!ret)
+			dev_warn(priv->dev,
+				 "Invalid rd-sampling-point : %u. Use default : %u\n",
+				 rd_sampling_point,
+				 PECI_RD_SAMPLING_POINT_DEFAULT);
+		rd_sampling_point = PECI_RD_SAMPLING_POINT_DEFAULT;
+	}
+
+	ret = of_property_read_u32(priv->dev->of_node, "cmd-timeout-ms",
+				   &priv->cmd_timeout_ms);
+	if (ret || priv->cmd_timeout_ms > PECI_CMD_TIMEOUT_MS_MAX ||
+	    priv->cmd_timeout_ms == 0) {
+		if (!ret)
+			dev_warn(priv->dev,
+				 "Invalid cmd-timeout-ms : %u. Use default : %u\n",
+				 priv->cmd_timeout_ms,
+				 PECI_CMD_TIMEOUT_MS_DEFAULT);
+		priv->cmd_timeout_ms = PECI_CMD_TIMEOUT_MS_DEFAULT;
+	}
+
+	regmap_write(priv->regmap, ASPEED_PECI_CTRL,
+		     FIELD_PREP(PECI_CTRL_CLK_DIV_MASK, PECI_CLK_DIV_DEFAULT) |
+		     PECI_CTRL_PECI_CLK_EN);
+
+	/**
+	 * Timing negotiation period setting.
+	 * The unit of the programmed value is 4 times of PECI clock period.
+	 */
+	regmap_write(priv->regmap, ASPEED_PECI_TIMING,
+		     FIELD_PREP(PECI_TIMING_MESSAGE_MASK, msg_timing) |
+		     FIELD_PREP(PECI_TIMING_ADDRESS_MASK, addr_timing));
+
+	/* Clear interrupts */
+	regmap_write(priv->regmap, ASPEED_PECI_INT_STS, PECI_INT_MASK);
+
+	/* Enable interrupts */
+	regmap_write(priv->regmap, ASPEED_PECI_INT_CTRL, PECI_INT_MASK);
+
+	/* Read sampling point and clock speed setting */
+	regmap_write(priv->regmap, ASPEED_PECI_CTRL,
+		     FIELD_PREP(PECI_CTRL_SAMPLING_MASK, rd_sampling_point) |
+		     FIELD_PREP(PECI_CTRL_CLK_DIV_MASK, clk_div_val) |
+		     PECI_CTRL_PECI_EN | PECI_CTRL_PECI_CLK_EN);
+
+	return 0;
+}
+
+static const struct regmap_config aspeed_peci_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = ASPEED_PECI_R_DATA7,
+	.val_format_endian = REGMAP_ENDIAN_LITTLE,
+	.fast_io = true,
+};
+
+static int aspeed_peci_xfer(struct peci_adapter *adapter,
+			    struct peci_xfer_msg *msg)
+{
+	struct aspeed_peci *priv = peci_get_adapdata(adapter);
+
+	return aspeed_peci_xfer_native(priv, msg);
+}
+
+static int aspeed_peci_probe(struct platform_device *pdev)
+{
+	struct peci_adapter *adapter;
+	struct aspeed_peci *priv;
+	struct resource *res;
+	void __iomem *base;
+	u32 cmd_sts;
+	int ret;
+
+	adapter = peci_alloc_adapter(&pdev->dev, sizeof(*priv));
+	if (!adapter)
+		return -ENOMEM;
+
+	priv = peci_get_adapdata(adapter);
+	priv->adapter = adapter;
+	priv->dev = &pdev->dev;
+	dev_set_drvdata(&pdev->dev, priv);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base)) {
+		ret = PTR_ERR(base);
+		goto err_put_adapter_dev;
+	}
+
+	priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+					     &aspeed_peci_regmap_config);
+	if (IS_ERR(priv->regmap)) {
+		ret = PTR_ERR(priv->regmap);
+		goto err_put_adapter_dev;
+	}
+
+	/**
+	 * We check that the regmap works on this very first access,
+	 * but as this is an MMIO-backed regmap, subsequent regmap
+	 * access is not going to fail and we skip error checks from
+	 * this point.
+	 */
+	ret = regmap_read(priv->regmap, ASPEED_PECI_CMD, &cmd_sts);
+	if (ret) {
+		ret = -EIO;
+		goto err_put_adapter_dev;
+	}
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (!priv->irq) {
+		ret = -ENODEV;
+		goto err_put_adapter_dev;
+	}
+
+	ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_peci_irq_handler,
+			       0, "peci-aspeed-irq", priv);
+	if (ret)
+		goto err_put_adapter_dev;
+
+	init_completion(&priv->xfer_complete);
+	spin_lock_init(&priv->lock);
+
+	priv->adapter->owner = THIS_MODULE;
+	priv->adapter->dev.of_node = of_node_get(dev_of_node(priv->dev));
+	strlcpy(priv->adapter->name, pdev->name, sizeof(priv->adapter->name));
+	priv->adapter->xfer = aspeed_peci_xfer;
+
+	priv->rst = devm_reset_control_get(&pdev->dev, NULL);
+	if (IS_ERR(priv->rst)) {
+		dev_err(&pdev->dev,
+			"missing or invalid reset controller entry");
+		ret = PTR_ERR(priv->rst);
+		goto err_put_adapter_dev;
+	}
+	reset_control_deassert(priv->rst);
+
+	ret = aspeed_peci_init_ctrl(priv);
+	if (ret)
+		goto err_put_adapter_dev;
+
+	ret = peci_add_adapter(priv->adapter);
+	if (ret)
+		goto err_put_adapter_dev;
+
+	dev_info(&pdev->dev, "peci bus %d registered, irq %d\n",
+		 priv->adapter->nr, priv->irq);
+
+	return 0;
+
+err_put_adapter_dev:
+	put_device(&adapter->dev);
+	return ret;
+}
+
+static int aspeed_peci_remove(struct platform_device *pdev)
+{
+	struct aspeed_peci *priv = dev_get_drvdata(&pdev->dev);
+
+	clk_disable_unprepare(priv->clk);
+	reset_control_assert(priv->rst);
+	peci_del_adapter(priv->adapter);
+	of_node_put(priv->adapter->dev.of_node);
+
+	return 0;
+}
+
+static const struct of_device_id aspeed_peci_of_table[] = {
+	{ .compatible = "aspeed,ast2400-peci", },
+	{ .compatible = "aspeed,ast2500-peci", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, aspeed_peci_of_table);
+
+static struct platform_driver aspeed_peci_driver = {
+	.probe  = aspeed_peci_probe,
+	.remove = aspeed_peci_remove,
+	.driver = {
+		.name           = "peci-aspeed",
+		.of_match_table = of_match_ptr(aspeed_peci_of_table),
+	},
+};
+module_platform_driver(aspeed_peci_driver);
+
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("ASPEED PECI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/peci/peci-core.c b/drivers/peci/peci-core.c
new file mode 100644
index 0000000..6f24146
--- /dev/null
+++ b/drivers/peci/peci-core.c
@@ -0,0 +1,1527 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 Intel Corporation
+
+#include <linux/bitfield.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/peci.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+/* Mask for getting minor revision number from DIB */
+#define REVISION_NUM_MASK  GENMASK(15, 8)
+
+/* CRC8 table for Assure Write Frame Check */
+#define PECI_CRC8_POLYNOMIAL  0x07
+DECLARE_CRC8_TABLE(peci_crc8_table);
+
+static struct device_type peci_adapter_type;
+static struct device_type peci_client_type;
+
+/* Max number of peci cdev */
+#define PECI_CDEV_MAX  16
+
+static dev_t peci_devt;
+static bool is_registered;
+
+static DEFINE_MUTEX(core_lock);
+static DEFINE_IDR(peci_adapter_idr);
+
+static struct peci_adapter *peci_get_adapter(int nr)
+{
+	struct peci_adapter *adapter;
+
+	mutex_lock(&core_lock);
+	adapter = idr_find(&peci_adapter_idr, nr);
+	if (!adapter)
+		goto out_unlock;
+
+	if (try_module_get(adapter->owner))
+		get_device(&adapter->dev);
+	else
+		adapter = NULL;
+
+out_unlock:
+	mutex_unlock(&core_lock);
+	return adapter;
+}
+
+static void peci_put_adapter(struct peci_adapter *adapter)
+{
+	if (!adapter)
+		return;
+
+	put_device(&adapter->dev);
+	module_put(adapter->owner);
+}
+
+static ssize_t name_show(struct device *dev,
+			 struct device_attribute *attr,
+			 char *buf)
+{
+	return sprintf(buf, "%s\n", dev->type == &peci_client_type ?
+		       to_peci_client(dev)->name : to_peci_adapter(dev)->name);
+}
+static DEVICE_ATTR_RO(name);
+
+static void peci_client_dev_release(struct device *dev)
+{
+	struct peci_client *client = to_peci_client(dev);
+
+	dev_dbg(dev, "%s: %s\n", __func__, client->name);
+	peci_put_adapter(client->adapter);
+	kfree(client);
+}
+
+static struct attribute *peci_device_attrs[] = {
+	&dev_attr_name.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(peci_device);
+
+static struct device_type peci_client_type = {
+	.groups		= peci_device_groups,
+	.release	= peci_client_dev_release,
+};
+
+/**
+ * peci_verify_client - return parameter as peci_client, or NULL
+ * @dev: device, probably from some driver model iterator
+ *
+ * Return: pointer to peci_client on success, else NULL.
+ */
+struct peci_client *peci_verify_client(struct device *dev)
+{
+	return (dev->type == &peci_client_type)
+			? to_peci_client(dev)
+			: NULL;
+}
+EXPORT_SYMBOL_GPL(peci_verify_client);
+
+static u8 peci_aw_fcs(u8 *data, int len)
+{
+	return crc8(peci_crc8_table, data, (size_t)len, 0);
+}
+
+static int __peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg,
+		       bool do_retry, bool has_aw_fcs)
+{
+	ktime_t start, end;
+	s64 elapsed_ms;
+	int rc = 0;
+
+	/**
+	 * For some commands, the PECI originator may need to retry a command if
+	 * the processor PECI client responds with a 0x8x completion code. In
+	 * each instance, the processor PECI client may have started the
+	 * operation but not completed it yet. When the 'retry' bit is set, the
+	 * PECI client will ignore a new request if it exactly matches a
+	 * previous valid request.
+	 */
+
+	if (do_retry)
+		start = ktime_get();
+
+	do {
+		rc = adapter->xfer(adapter, msg);
+
+		if (!do_retry || rc)
+			break;
+
+		if (msg->rx_buf[0] == DEV_PECI_CC_SUCCESS)
+			break;
+
+		/* Retry is needed when completion code is 0x8x */
+		if ((msg->rx_buf[0] & DEV_PECI_CC_RETRY_CHECK_MASK) !=
+		    DEV_PECI_CC_NEED_RETRY) {
+			rc = -EIO;
+			break;
+		}
+
+		/* Set the retry bit to indicate a retry attempt */
+		msg->tx_buf[1] |= DEV_PECI_RETRY_BIT;
+
+		/* Recalculate the AW FCS if it has one */
+		if (has_aw_fcs)
+			msg->tx_buf[msg->tx_len - 1] = 0x80 ^
+						peci_aw_fcs((u8 *)msg,
+							    2 + msg->tx_len);
+
+		/**
+		 * Retry for at least 250ms before returning an error.
+		 * Retry interval guideline:
+		 *   No minimum < Retry Interval < No maximum
+		 *                (recommend 10ms)
+		 */
+		end = ktime_get();
+		elapsed_ms = ktime_to_ms(ktime_sub(end, start));
+		if (elapsed_ms >= DEV_PECI_RETRY_TIME_MS) {
+			dev_dbg(&adapter->dev, "Timeout retrying xfer!\n");
+			rc = -ETIMEDOUT;
+			break;
+		}
+
+		usleep_range((DEV_PECI_RETRY_INTERVAL_USEC >> 2) + 1,
+			     DEV_PECI_RETRY_INTERVAL_USEC);
+	} while (true);
+
+	if (rc)
+		dev_dbg(&adapter->dev, "xfer error, rc: %d\n", rc);
+
+	return rc;
+}
+
+static int peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg)
+{
+	return __peci_xfer(adapter, msg, false, false);
+}
+
+static int peci_xfer_with_retries(struct peci_adapter *adapter,
+				  struct peci_xfer_msg *msg,
+				  bool has_aw_fcs)
+{
+	return __peci_xfer(adapter, msg, true, has_aw_fcs);
+}
+
+static int peci_scan_cmd_mask(struct peci_adapter *adapter)
+{
+	struct peci_xfer_msg msg;
+	u8 revision;
+	int rc = 0;
+	u64 dib;
+
+	/* Update command mask just once */
+	if (adapter->cmd_mask & BIT(PECI_CMD_XFER))
+		return 0;
+
+	msg.addr      = PECI_BASE_ADDR;
+	msg.tx_len    = GET_DIB_WR_LEN;
+	msg.rx_len    = GET_DIB_RD_LEN;
+	msg.tx_buf[0] = GET_DIB_PECI_CMD;
+
+	rc = peci_xfer(adapter, &msg);
+	if (rc)
+		return rc;
+
+	dib = le64_to_cpup((__le64 *)msg.rx_buf);
+
+	/* Check special case for Get DIB command */
+	if (dib == 0) {
+		dev_dbg(&adapter->dev, "DIB read as 0\n");
+		return -EIO;
+	}
+
+	/**
+	 * Setting up the supporting commands based on minor revision number.
+	 * See PECI Spec Table 3-1.
+	 */
+	revision = FIELD_GET(REVISION_NUM_MASK, dib);
+	if (revision >= 0x36) /* Rev. 3.6 */
+		adapter->cmd_mask |= BIT(PECI_CMD_WR_IA_MSR);
+	if (revision >= 0x35) /* Rev. 3.5 */
+		adapter->cmd_mask |= BIT(PECI_CMD_WR_PCI_CFG);
+	if (revision >= 0x34) /* Rev. 3.4 */
+		adapter->cmd_mask |= BIT(PECI_CMD_RD_PCI_CFG);
+	if (revision >= 0x33) { /* Rev. 3.3 */
+		adapter->cmd_mask |= BIT(PECI_CMD_RD_PCI_CFG_LOCAL);
+		adapter->cmd_mask |= BIT(PECI_CMD_WR_PCI_CFG_LOCAL);
+	}
+	if (revision >= 0x32) /* Rev. 3.2 */
+		adapter->cmd_mask |= BIT(PECI_CMD_RD_IA_MSR);
+	if (revision >= 0x31) { /* Rev. 3.1 */
+		adapter->cmd_mask |= BIT(PECI_CMD_RD_PKG_CFG);
+		adapter->cmd_mask |= BIT(PECI_CMD_WR_PKG_CFG);
+	}
+
+	adapter->cmd_mask |= BIT(PECI_CMD_XFER);
+	adapter->cmd_mask |= BIT(PECI_CMD_GET_TEMP);
+	adapter->cmd_mask |= BIT(PECI_CMD_GET_DIB);
+	adapter->cmd_mask |= BIT(PECI_CMD_PING);
+
+	return rc;
+}
+
+static int peci_cmd_support(struct peci_adapter *adapter, enum peci_cmd cmd)
+{
+	if (!(adapter->cmd_mask & BIT(PECI_CMD_PING)) &&
+	    peci_scan_cmd_mask(adapter) < 0) {
+		dev_dbg(&adapter->dev, "Failed to scan command mask\n");
+		return -EIO;
+	}
+
+	if (!(adapter->cmd_mask & BIT(cmd))) {
+		dev_dbg(&adapter->dev, "Command %d is not supported\n", cmd);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int peci_ioctl_xfer(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_xfer_msg *msg = vmsg;
+
+	return peci_xfer(adapter, msg);
+}
+
+static int peci_ioctl_ping(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_ping_msg *umsg = vmsg;
+	struct peci_xfer_msg msg;
+
+	msg.addr   = umsg->addr;
+	msg.tx_len = 0;
+	msg.rx_len = 0;
+
+	return peci_xfer(adapter, &msg);
+}
+
+static int peci_ioctl_get_dib(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_get_dib_msg *umsg = vmsg;
+	struct peci_xfer_msg msg;
+	int rc;
+
+	msg.addr      = umsg->addr;
+	msg.tx_len    = GET_DIB_WR_LEN;
+	msg.rx_len    = GET_DIB_RD_LEN;
+	msg.tx_buf[0] = GET_DIB_PECI_CMD;
+
+	rc = peci_xfer(adapter, &msg);
+	if (rc)
+		return rc;
+
+	umsg->dib = le64_to_cpup((__le64 *)msg.rx_buf);
+
+	return 0;
+}
+
+static int peci_ioctl_get_temp(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_get_temp_msg *umsg = vmsg;
+	struct peci_xfer_msg msg;
+	int rc;
+
+	msg.addr      = umsg->addr;
+	msg.tx_len    = GET_TEMP_WR_LEN;
+	msg.rx_len    = GET_TEMP_RD_LEN;
+	msg.tx_buf[0] = GET_TEMP_PECI_CMD;
+
+	rc = peci_xfer(adapter, &msg);
+	if (rc)
+		return rc;
+
+	umsg->temp_raw = le16_to_cpup((__le16 *)msg.rx_buf);
+
+	return 0;
+}
+
+static int peci_ioctl_rd_pkg_cfg(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_rd_pkg_cfg_msg *umsg = vmsg;
+	struct peci_xfer_msg msg;
+	int rc = 0;
+
+	/* Per the PECI spec, the read length must be a byte, word, or dword */
+	if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 4) {
+		dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n",
+			umsg->rx_len);
+		return -EINVAL;
+	}
+
+	msg.addr = umsg->addr;
+	msg.tx_len = RDPKGCFG_WRITE_LEN;
+	/* read lengths of 1 and 2 result in an error, so only use 4 for now */
+	msg.rx_len = RDPKGCFG_READ_LEN_BASE + umsg->rx_len;
+	msg.tx_buf[0] = RDPKGCFG_PECI_CMD;
+	msg.tx_buf[1] = 0;         /* request byte for Host ID | Retry bit */
+				   /* Host ID is 0 for PECI 3.0 */
+	msg.tx_buf[2] = umsg->index;            /* RdPkgConfig index */
+	msg.tx_buf[3] = (u8)umsg->param;        /* LSB - Config parameter */
+	msg.tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */
+
+	rc = peci_xfer_with_retries(adapter, &msg, false);
+	if (!rc)
+		memcpy(umsg->pkg_config, &msg.rx_buf[1], umsg->rx_len);
+
+	return rc;
+}
+
+static int peci_ioctl_wr_pkg_cfg(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_wr_pkg_cfg_msg *umsg = vmsg;
+	struct peci_xfer_msg msg;
+	int rc = 0, i;
+
+	/* Per the PECI spec, the write length must be a dword */
+	if (umsg->tx_len != 4) {
+		dev_dbg(&adapter->dev, "Invalid write length, tx_len: %d\n",
+			umsg->tx_len);
+		return -EINVAL;
+	}
+
+	msg.addr = umsg->addr;
+	msg.tx_len = WRPKGCFG_WRITE_LEN_BASE + umsg->tx_len;
+	/* read lengths of 1 and 2 result in an error, so only use 4 for now */
+	msg.rx_len = WRPKGCFG_READ_LEN;
+	msg.tx_buf[0] = WRPKGCFG_PECI_CMD;
+	msg.tx_buf[1] = 0;         /* request byte for Host ID | Retry bit */
+				   /* Host ID is 0 for PECI 3.0 */
+	msg.tx_buf[2] = umsg->index;            /* RdPkgConfig index */
+	msg.tx_buf[3] = (u8)umsg->param;        /* LSB - Config parameter */
+	msg.tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */
+	for (i = 0; i < umsg->tx_len; i++)
+		msg.tx_buf[5 + i] = (u8)(umsg->value >> (i << 3));
+
+	/* Add an Assure Write Frame Check Sequence byte */
+	msg.tx_buf[5 + i] = 0x80 ^
+			    peci_aw_fcs((u8 *)&msg, 8 + umsg->tx_len);
+
+	rc = peci_xfer_with_retries(adapter, &msg, true);
+
+	return rc;
+}
+
+static int peci_ioctl_rd_ia_msr(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_rd_ia_msr_msg *umsg = vmsg;
+	struct peci_xfer_msg msg;
+	int rc = 0;
+
+	msg.addr = umsg->addr;
+	msg.tx_len = RDIAMSR_WRITE_LEN;
+	msg.rx_len = RDIAMSR_READ_LEN;
+	msg.tx_buf[0] = RDIAMSR_PECI_CMD;
+	msg.tx_buf[1] = 0;
+	msg.tx_buf[2] = umsg->thread_id;
+	msg.tx_buf[3] = (u8)umsg->address;
+	msg.tx_buf[4] = (u8)(umsg->address >> 8);
+
+	rc = peci_xfer_with_retries(adapter, &msg, false);
+	if (!rc)
+		memcpy(&umsg->value, &msg.rx_buf[1], sizeof(uint64_t));
+
+	return rc;
+}
+
+static int peci_ioctl_rd_pci_cfg(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_rd_pci_cfg_msg *umsg = vmsg;
+	struct peci_xfer_msg msg;
+	u32 address;
+	int rc = 0;
+
+	address = umsg->reg;                  /* [11:0]  - Register */
+	address |= (u32)umsg->function << 12; /* [14:12] - Function */
+	address |= (u32)umsg->device << 15;   /* [19:15] - Device   */
+	address |= (u32)umsg->bus << 20;      /* [27:20] - Bus      */
+					      /* [31:28] - Reserved */
+	msg.addr = umsg->addr;
+	msg.tx_len = RDPCICFG_WRITE_LEN;
+	msg.rx_len = RDPCICFG_READ_LEN;
+	msg.tx_buf[0] = RDPCICFG_PECI_CMD;
+	msg.tx_buf[1] = 0;         /* request byte for Host ID | Retry bit */
+				   /* Host ID is 0 for PECI 3.0 */
+	msg.tx_buf[2] = (u8)address;         /* LSB - PCI Config Address */
+	msg.tx_buf[3] = (u8)(address >> 8);  /* PCI Config Address */
+	msg.tx_buf[4] = (u8)(address >> 16); /* PCI Config Address */
+	msg.tx_buf[5] = (u8)(address >> 24); /* MSB - PCI Config Address */
+
+	rc = peci_xfer_with_retries(adapter, &msg, false);
+	if (!rc)
+		memcpy(umsg->pci_config, &msg.rx_buf[1], 4);
+
+	return rc;
+}
+
+static int peci_ioctl_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_rd_pci_cfg_local_msg *umsg = vmsg;
+	struct peci_xfer_msg msg;
+	u32 address;
+	int rc = 0;
+
+	/* Per the PECI spec, the read length must be a byte, word, or dword */
+	if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 4) {
+		dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n",
+			umsg->rx_len);
+		return -EINVAL;
+	}
+
+	address = umsg->reg;                  /* [11:0]  - Register */
+	address |= (u32)umsg->function << 12; /* [14:12] - Function */
+	address |= (u32)umsg->device << 15;   /* [19:15] - Device   */
+	address |= (u32)umsg->bus << 20;      /* [23:20] - Bus      */
+
+	msg.addr = umsg->addr;
+	msg.tx_len = RDPCICFGLOCAL_WRITE_LEN;
+	msg.rx_len = RDPCICFGLOCAL_READ_LEN_BASE + umsg->rx_len;
+	msg.tx_buf[0] = RDPCICFGLOCAL_PECI_CMD;
+	msg.tx_buf[1] = 0;         /* request byte for Host ID | Retry bit */
+				   /* Host ID is 0 for PECI 3.0 */
+	msg.tx_buf[2] = (u8)address;       /* LSB - PCI Configuration Address */
+	msg.tx_buf[3] = (u8)(address >> 8);  /* PCI Configuration Address */
+	msg.tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */
+
+	rc = peci_xfer_with_retries(adapter, &msg, false);
+	if (!rc)
+		memcpy(umsg->pci_config, &msg.rx_buf[1], umsg->rx_len);
+
+	return rc;
+}
+
+static int peci_ioctl_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_wr_pci_cfg_local_msg *umsg = vmsg;
+	struct peci_xfer_msg msg;
+	int rc = 0, i;
+	u32 address;
+
+	/* Per the PECI spec, the write length must be a byte, word, or dword */
+	if (umsg->tx_len != 1 && umsg->tx_len != 2 && umsg->tx_len != 4) {
+		dev_dbg(&adapter->dev, "Invalid write length, tx_len: %d\n",
+			umsg->tx_len);
+		return -EINVAL;
+	}
+
+	address = umsg->reg;                  /* [11:0]  - Register */
+	address |= (u32)umsg->function << 12; /* [14:12] - Function */
+	address |= (u32)umsg->device << 15;   /* [19:15] - Device   */
+	address |= (u32)umsg->bus << 20;      /* [23:20] - Bus      */
+
+	msg.addr = umsg->addr;
+	msg.tx_len = WRPCICFGLOCAL_WRITE_LEN_BASE + umsg->tx_len;
+	msg.rx_len = WRPCICFGLOCAL_READ_LEN;
+	msg.tx_buf[0] = WRPCICFGLOCAL_PECI_CMD;
+	msg.tx_buf[1] = 0;         /* request byte for Host ID | Retry bit */
+				   /* Host ID is 0 for PECI 3.0 */
+	msg.tx_buf[2] = (u8)address;       /* LSB - PCI Configuration Address */
+	msg.tx_buf[3] = (u8)(address >> 8);  /* PCI Configuration Address */
+	msg.tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */
+	for (i = 0; i < umsg->tx_len; i++)
+		msg.tx_buf[5 + i] = (u8)(umsg->value >> (i << 3));
+
+	/* Add an Assure Write Frame Check Sequence byte */
+	msg.tx_buf[5 + i] = 0x80 ^
+			    peci_aw_fcs((u8 *)&msg, 8 + umsg->tx_len);
+
+	rc = peci_xfer_with_retries(adapter, &msg, true);
+
+	return rc;
+}
+
+typedef int (*peci_ioctl_fn_type)(struct peci_adapter *, void *);
+
+static const peci_ioctl_fn_type peci_ioctl_fn[PECI_CMD_MAX] = {
+	peci_ioctl_xfer,
+	peci_ioctl_ping,
+	peci_ioctl_get_dib,
+	peci_ioctl_get_temp,
+	peci_ioctl_rd_pkg_cfg,
+	peci_ioctl_wr_pkg_cfg,
+	peci_ioctl_rd_ia_msr,
+	NULL, /* Reserved */
+	peci_ioctl_rd_pci_cfg,
+	NULL, /* Reserved */
+	peci_ioctl_rd_pci_cfg_local,
+	peci_ioctl_wr_pci_cfg_local,
+};
+
+/**
+ * peci_command - transfer function of a PECI command
+ * @adapter: pointer to peci_adapter
+ * @vmsg: pointer to PECI messages
+ * Context: can sleep
+ *
+ * This performs a transfer of a PECI command using PECI messages parameter
+ * which has various formats on each command.
+ *
+ * Return: zero on success, else a negative error code.
+ */
+int peci_command(struct peci_adapter *adapter, enum peci_cmd cmd, void *vmsg)
+{
+	int rc = 0;
+
+	if (cmd >= PECI_CMD_MAX || cmd < PECI_CMD_XFER)
+		return -EINVAL;
+
+	dev_dbg(&adapter->dev, "%s, cmd=0x%02x\n", __func__, cmd);
+
+	if (!peci_ioctl_fn[cmd])
+		return -EINVAL;
+
+	rt_mutex_lock(&adapter->bus_lock);
+
+	rc = peci_cmd_support(adapter, cmd);
+	if (!rc)
+		rc = peci_ioctl_fn[cmd](adapter, vmsg);
+
+	rt_mutex_unlock(&adapter->bus_lock);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(peci_command);
+
+static long peci_ioctl(struct file *file, unsigned int iocmd, unsigned long arg)
+{
+	struct peci_adapter *adapter = file->private_data;
+	void __user *argp = (void __user *)arg;
+	unsigned int msg_len;
+	enum peci_cmd cmd;
+	int rc = 0;
+	u8 *msg;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	dev_dbg(&adapter->dev, "ioctl, cmd=0x%x, arg=0x%lx\n", iocmd, arg);
+
+	switch (iocmd) {
+	case PECI_IOC_XFER:
+	case PECI_IOC_PING:
+	case PECI_IOC_GET_DIB:
+	case PECI_IOC_GET_TEMP:
+	case PECI_IOC_RD_PKG_CFG:
+	case PECI_IOC_WR_PKG_CFG:
+	case PECI_IOC_RD_IA_MSR:
+	case PECI_IOC_RD_PCI_CFG:
+	case PECI_IOC_RD_PCI_CFG_LOCAL:
+	case PECI_IOC_WR_PCI_CFG_LOCAL:
+		cmd = _IOC_NR(iocmd);
+		msg_len = _IOC_SIZE(iocmd);
+		break;
+
+	default:
+		dev_dbg(&adapter->dev, "Invalid ioctl cmd : 0x%x\n", iocmd);
+		return -ENOTTY;
+	}
+
+	if (!access_ok(argp, msg_len))
+		return -EFAULT;
+
+	msg = memdup_user(argp, msg_len);
+	if (IS_ERR(msg))
+		return PTR_ERR(msg);
+
+	rc = peci_command(adapter, cmd, msg);
+
+	if (!rc && copy_to_user(argp, msg, msg_len))
+		rc = -EFAULT;
+
+	kfree(msg);
+	return (long)rc;
+}
+
+static int peci_open(struct inode *inode, struct file *file)
+{
+	unsigned int minor = iminor(inode);
+	struct peci_adapter *adapter;
+
+	adapter = peci_get_adapter(minor);
+	if (!adapter)
+		return -ENODEV;
+
+	file->private_data = adapter;
+
+	return 0;
+}
+
+static int peci_release(struct inode *inode, struct file *file)
+{
+	struct peci_adapter *adapter = file->private_data;
+
+	peci_put_adapter(adapter);
+	file->private_data = NULL;
+
+	return 0;
+}
+
+static const struct file_operations peci_fops = {
+	.owner          = THIS_MODULE,
+	.unlocked_ioctl = peci_ioctl,
+	.open           = peci_open,
+	.release        = peci_release,
+};
+
+static int peci_detect(struct peci_adapter *adapter, u8 addr)
+{
+	struct peci_ping_msg msg;
+
+	msg.addr = addr;
+
+	return peci_command(adapter, PECI_CMD_PING, &msg);
+}
+
+static const struct of_device_id *
+peci_of_match_device(const struct of_device_id *matches,
+		     struct peci_client *client)
+{
+#if IS_ENABLED(CONFIG_OF)
+	if (!(client && matches))
+		return NULL;
+
+	return of_match_device(matches, &client->dev);
+#else
+	return NULL;
+#endif
+}
+
+static const struct peci_device_id *
+peci_match_id(const struct peci_device_id *id, struct peci_client *client)
+{
+	if (!(id && client))
+		return NULL;
+
+	while (id->name[0]) {
+		if (!strncmp(client->name, id->name, PECI_NAME_SIZE))
+			return id;
+		id++;
+	}
+
+	return NULL;
+}
+
+static int peci_device_match(struct device *dev, struct device_driver *drv)
+{
+	struct peci_client *client = peci_verify_client(dev);
+	struct peci_driver *driver;
+
+	/* Attempt an OF style match */
+	if (peci_of_match_device(drv->of_match_table, client))
+		return 1;
+
+	driver = to_peci_driver(drv);
+
+	/* Finally an ID match */
+	if (peci_match_id(driver->id_table, client))
+		return 1;
+
+	return 0;
+}
+
+static int peci_device_probe(struct device *dev)
+{
+	struct peci_client *client = peci_verify_client(dev);
+	struct peci_driver *driver;
+	int status = -EINVAL;
+
+	if (!client)
+		return 0;
+
+	driver = to_peci_driver(dev->driver);
+
+	if (!driver->id_table &&
+	    !peci_of_match_device(dev->driver->of_match_table, client))
+		return -ENODEV;
+
+	dev_dbg(dev, "%s: name:%s\n", __func__, client->name);
+
+	status = dev_pm_domain_attach(&client->dev, true);
+	if (status == -EPROBE_DEFER)
+		return status;
+
+	if (driver->probe)
+		status = driver->probe(client);
+	else
+		status = -EINVAL;
+
+	if (status)
+		goto err_detach_pm_domain;
+
+	return 0;
+
+err_detach_pm_domain:
+	dev_pm_domain_detach(&client->dev, true);
+	return status;
+}
+
+static int peci_device_remove(struct device *dev)
+{
+	struct peci_client *client = peci_verify_client(dev);
+	struct peci_driver *driver;
+	int status = 0;
+
+	if (!client || !dev->driver)
+		return 0;
+
+	driver = to_peci_driver(dev->driver);
+	if (driver->remove) {
+		dev_dbg(dev, "%s: name:%s\n", __func__, client->name);
+		status = driver->remove(client);
+	}
+
+	dev_pm_domain_detach(&client->dev, true);
+
+	return status;
+}
+
+static void peci_device_shutdown(struct device *dev)
+{
+	struct peci_client *client = peci_verify_client(dev);
+	struct peci_driver *driver;
+
+	if (!client || !dev->driver)
+		return;
+
+	dev_dbg(dev, "%s: name:%s\n", __func__, client->name);
+
+	driver = to_peci_driver(dev->driver);
+	if (driver->shutdown)
+		driver->shutdown(client);
+}
+
+static struct bus_type peci_bus_type = {
+	.name		= "peci",
+	.match		= peci_device_match,
+	.probe		= peci_device_probe,
+	.remove		= peci_device_remove,
+	.shutdown	= peci_device_shutdown,
+};
+
+static int peci_check_addr_validity(u8 addr)
+{
+	if (addr < PECI_BASE_ADDR && addr > PECI_BASE_ADDR + PECI_OFFSET_MAX)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int peci_check_client_busy(struct device *dev, void *client_new_p)
+{
+	struct peci_client *client = peci_verify_client(dev);
+	struct peci_client *client_new = client_new_p;
+
+	if (client && client->addr == client_new->addr)
+		return -EBUSY;
+
+	return 0;
+}
+
+/**
+ * peci_get_cpu_id - read CPU ID from the Package Configuration Space of CPU
+ * @adapter: pointer to peci_adapter
+ * @addr: address of the PECI client CPU
+ * @cpu_id: where the CPU ID will be stored
+ * Context: can sleep
+ *
+ * Return: zero on success, else a negative error code.
+ */
+int peci_get_cpu_id(struct peci_adapter *adapter, u8 addr, u32 *cpu_id)
+{
+	struct peci_rd_pkg_cfg_msg msg;
+	int rc;
+
+	msg.addr = addr;
+	msg.index = MBX_INDEX_CPU_ID;
+	msg.param = PKG_ID_CPU_ID;
+	msg.rx_len = 4;
+
+	rc = peci_command(adapter, PECI_CMD_RD_PKG_CFG, &msg);
+	if (!rc)
+		*cpu_id = le32_to_cpup((__le32 *)msg.pkg_config);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(peci_get_cpu_id);
+
+static struct peci_client *peci_new_device(struct peci_adapter *adapter,
+					   struct peci_board_info const *info)
+{
+	struct peci_client *client;
+	int rc;
+
+	/* Increase reference count for the adapter assigned */
+	if (!peci_get_adapter(adapter->nr))
+		return NULL;
+
+	client = kzalloc(sizeof(*client), GFP_KERNEL);
+	if (!client)
+		goto err_put_adapter;
+
+	client->adapter = adapter;
+	client->addr = info->addr;
+	strlcpy(client->name, info->type, sizeof(client->name));
+
+	rc = peci_check_addr_validity(client->addr);
+	if (rc) {
+		dev_err(&adapter->dev, "Invalid PECI CPU address 0x%02hx\n",
+			client->addr);
+		goto err_free_client_silent;
+	}
+
+	/* Check online status of client */
+	rc = peci_detect(adapter, client->addr);
+	if (rc)
+		goto err_free_client;
+
+	rc = device_for_each_child(&adapter->dev, client,
+				   peci_check_client_busy);
+	if (rc)
+		goto err_free_client;
+
+	client->dev.parent = &client->adapter->dev;
+	client->dev.bus = &peci_bus_type;
+	client->dev.type = &peci_client_type;
+	client->dev.of_node = info->of_node;
+	dev_set_name(&client->dev, "%d-%02x", adapter->nr, client->addr);
+
+	rc = device_register(&client->dev);
+	if (rc)
+		goto err_free_client;
+
+	dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
+		client->name, dev_name(&client->dev));
+
+	return client;
+
+err_free_client:
+	dev_err(&adapter->dev,
+		"Failed to register peci client %s at 0x%02x (%d)\n",
+		client->name, client->addr, rc);
+err_free_client_silent:
+	kfree(client);
+err_put_adapter:
+	peci_put_adapter(adapter);
+	return NULL;
+}
+
+static void peci_unregister_device(struct peci_client *client)
+{
+	if (!client)
+		return;
+
+	if (client->dev.of_node)
+		of_node_clear_flag(client->dev.of_node, OF_POPULATED);
+
+	device_unregister(&client->dev);
+}
+
+static int peci_unregister_client(struct device *dev, void *dummy)
+{
+	struct peci_client *client = peci_verify_client(dev);
+
+	peci_unregister_device(client);
+
+	return 0;
+}
+
+static void peci_adapter_dev_release(struct device *dev)
+{
+	struct peci_adapter *adapter = to_peci_adapter(dev);
+
+	dev_dbg(dev, "%s: %s\n", __func__, adapter->name);
+	mutex_destroy(&adapter->userspace_clients_lock);
+	rt_mutex_destroy(&adapter->bus_lock);
+	kfree(adapter);
+}
+
+static ssize_t peci_sysfs_new_device(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct peci_adapter *adapter = to_peci_adapter(dev);
+	struct peci_board_info info = {};
+	struct peci_client *client;
+	char *blank, end;
+	int rc;
+
+	/* Parse device type */
+	blank = strchr(buf, ' ');
+	if (!blank) {
+		dev_err(dev, "%s: Missing parameters\n", "new_device");
+		return -EINVAL;
+	}
+	if (blank - buf > PECI_NAME_SIZE - 1) {
+		dev_err(dev, "%s: Invalid device type\n", "new_device");
+		return -EINVAL;
+	}
+	memcpy(info.type, buf, blank - buf);
+
+	/* Parse remaining parameters, reject extra parameters */
+	rc = sscanf(++blank, "%hi%c", &info.addr, &end);
+	if (rc < 1) {
+		dev_err(dev, "%s: Can't parse client address\n", "new_device");
+		return -EINVAL;
+	}
+	if (rc > 1  && end != '\n') {
+		dev_err(dev, "%s: Extra parameters\n", "new_device");
+		return -EINVAL;
+	}
+
+	client = peci_new_device(adapter, &info);
+	if (!client)
+		return -EINVAL;
+
+	/* Keep track of the added device */
+	mutex_lock(&adapter->userspace_clients_lock);
+	list_add_tail(&client->detected, &adapter->userspace_clients);
+	mutex_unlock(&adapter->userspace_clients_lock);
+	dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device",
+		 info.type, info.addr);
+
+	return count;
+}
+static DEVICE_ATTR(new_device, 0200, NULL, peci_sysfs_new_device);
+
+static ssize_t peci_sysfs_delete_device(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct peci_adapter *adapter = to_peci_adapter(dev);
+	struct peci_client *client, *next;
+	struct peci_board_info info = {};
+	struct peci_driver *driver;
+	char *blank, end;
+	int rc;
+
+	/* Parse device type */
+	blank = strchr(buf, ' ');
+	if (!blank) {
+		dev_err(dev, "%s: Missing parameters\n", "delete_device");
+		return -EINVAL;
+	}
+	if (blank - buf > PECI_NAME_SIZE - 1) {
+		dev_err(dev, "%s: Invalid device type\n", "delete_device");
+		return -EINVAL;
+	}
+	memcpy(info.type, buf, blank - buf);
+
+	/* Parse remaining parameters, reject extra parameters */
+	rc = sscanf(++blank, "%hi%c", &info.addr, &end);
+	if (rc < 1) {
+		dev_err(dev, "%s: Can't parse client address\n",
+			"delete_device");
+		return -EINVAL;
+	}
+	if (rc > 1  && end != '\n') {
+		dev_err(dev, "%s: Extra parameters\n", "delete_device");
+		return -EINVAL;
+	}
+
+	/* Make sure the device was added through sysfs */
+	rc = -ENOENT;
+	mutex_lock(&adapter->userspace_clients_lock);
+	list_for_each_entry_safe(client, next, &adapter->userspace_clients,
+				 detected) {
+		driver = to_peci_driver(client->dev.driver);
+
+		if (client->addr == info.addr &&
+		    !strncmp(client->name, info.type, PECI_NAME_SIZE)) {
+			dev_info(dev, "%s: Deleting device %s at 0x%02hx\n",
+				 "delete_device", client->name, client->addr);
+			list_del(&client->detected);
+			peci_unregister_device(client);
+			rc = count;
+			break;
+		}
+	}
+	mutex_unlock(&adapter->userspace_clients_lock);
+
+	if (rc < 0)
+		dev_err(dev, "%s: Can't find device in list\n",
+			"delete_device");
+
+	return rc;
+}
+static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, 0200, NULL,
+				  peci_sysfs_delete_device);
+
+static struct attribute *peci_adapter_attrs[] = {
+	&dev_attr_name.attr,
+	&dev_attr_new_device.attr,
+	&dev_attr_delete_device.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(peci_adapter);
+
+static struct device_type peci_adapter_type = {
+	.groups		= peci_adapter_groups,
+	.release	= peci_adapter_dev_release,
+};
+
+/**
+ * peci_verify_adapter - return parameter as peci_adapter, or NULL
+ * @dev: device, probably from some driver model iterator
+ *
+ * Return: pointer to peci_adapter on success, else NULL.
+ */
+struct peci_adapter *peci_verify_adapter(struct device *dev)
+{
+	return (dev->type == &peci_adapter_type)
+			? to_peci_adapter(dev)
+			: NULL;
+}
+EXPORT_SYMBOL_GPL(peci_verify_adapter);
+
+#if IS_ENABLED(CONFIG_OF)
+static struct peci_client *peci_of_register_device(struct peci_adapter *adapter,
+						   struct device_node *node)
+{
+	struct peci_board_info info = {};
+	struct peci_client *result;
+	const __be32 *addr_be;
+	int len;
+
+	dev_dbg(&adapter->dev, "register %pOF\n", node);
+
+	if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
+		dev_err(&adapter->dev, "modalias failure on %pOF\n", node);
+		return ERR_PTR(-EINVAL);
+	}
+
+	addr_be = of_get_property(node, "reg", &len);
+	if (!addr_be || len < sizeof(*addr_be)) {
+		dev_err(&adapter->dev, "invalid reg on %pOF\n", node);
+		return ERR_PTR(-EINVAL);
+	}
+
+	info.addr = be32_to_cpup(addr_be);
+	info.of_node = of_node_get(node);
+
+	result = peci_new_device(adapter, &info);
+	if (!result)
+		result = ERR_PTR(-EINVAL);
+
+	of_node_put(node);
+	return result;
+}
+
+static void peci_of_register_devices(struct peci_adapter *adapter)
+{
+	struct device_node *bus, *node;
+	struct peci_client *client;
+
+	/* Only register child devices if the adapter has a node pointer set */
+	if (!adapter->dev.of_node)
+		return;
+
+	bus = of_get_child_by_name(adapter->dev.of_node, "peci-bus");
+	if (!bus)
+		bus = of_node_get(adapter->dev.of_node);
+
+	for_each_available_child_of_node(bus, node) {
+		if (of_node_test_and_set_flag(node, OF_POPULATED))
+			continue;
+
+		client = peci_of_register_device(adapter, node);
+		if (IS_ERR(client)) {
+			dev_warn(&adapter->dev,
+				 "Failed to create PECI device for %pOF\n",
+				 node);
+			of_node_clear_flag(node, OF_POPULATED);
+		}
+	}
+
+	of_node_put(bus);
+}
+#else
+static void peci_of_register_devices(struct peci_adapter *adapter) { }
+#endif /* CONFIG_OF */
+
+#if IS_ENABLED(CONFIG_OF_DYNAMIC)
+static int peci_of_match_node(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+/* must call put_device() when done with returned peci_client device */
+static struct peci_client *peci_of_find_device(struct device_node *node)
+{
+	struct peci_client *client;
+	struct device *dev;
+
+	dev = bus_find_device(&peci_bus_type, NULL, node, peci_of_match_node);
+	if (!dev)
+		return NULL;
+
+	client = peci_verify_client(dev);
+	if (!client)
+		put_device(dev);
+
+	return client;
+}
+
+/* must call put_device() when done with returned peci_adapter device */
+static struct peci_adapter *peci_of_find_adapter(struct device_node *node)
+{
+	struct peci_adapter *adapter;
+	struct device *dev;
+
+	dev = bus_find_device(&peci_bus_type, NULL, node, peci_of_match_node);
+	if (!dev)
+		return NULL;
+
+	adapter = peci_verify_adapter(dev);
+	if (!adapter)
+		put_device(dev);
+
+	return adapter;
+}
+
+static int peci_of_notify(struct notifier_block *nb,
+			  unsigned long action,
+			  void *arg)
+{
+	struct of_reconfig_data *rd = arg;
+	struct peci_adapter *adapter;
+	struct peci_client *client;
+
+	switch (of_reconfig_get_state_change(action, rd)) {
+	case OF_RECONFIG_CHANGE_ADD:
+		adapter = peci_of_find_adapter(rd->dn->parent);
+		if (!adapter)
+			return NOTIFY_OK;	/* not for us */
+
+		if (of_node_test_and_set_flag(rd->dn, OF_POPULATED)) {
+			put_device(&adapter->dev);
+			return NOTIFY_OK;
+		}
+
+		client = peci_of_register_device(adapter, rd->dn);
+		put_device(&adapter->dev);
+
+		if (IS_ERR(client)) {
+			dev_err(&adapter->dev,
+				"failed to create client for '%pOF'\n", rd->dn);
+			of_node_clear_flag(rd->dn, OF_POPULATED);
+			return notifier_from_errno(PTR_ERR(client));
+		}
+		break;
+	case OF_RECONFIG_CHANGE_REMOVE:
+		/* already depopulated? */
+		if (!of_node_check_flag(rd->dn, OF_POPULATED))
+			return NOTIFY_OK;
+
+		/* find our device by node */
+		client = peci_of_find_device(rd->dn);
+		if (!client)
+			return NOTIFY_OK;	/* no? not meant for us */
+
+		/* unregister takes one ref away */
+		peci_unregister_device(client);
+
+		/* and put the reference of the find */
+		put_device(&client->dev);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block peci_of_notifier = {
+	.notifier_call = peci_of_notify,
+};
+#else
+extern struct notifier_block peci_of_notifier;
+#endif /* CONFIG_OF_DYNAMIC */
+
+/**
+ * peci_alloc_adapter - allocate a PECI adapter
+ * @dev: the adapter, possibly using the platform_bus
+ * @size: how much zeroed driver-private data to allocate; the pointer to this
+ *	memory is in the driver_data field of the returned device,
+ *	accessible with peci_get_adapdata().
+ * Context: can sleep
+ *
+ * This call is used only by PECI adapter drivers, which are the only ones
+ * directly touching chip registers.  It's how they allocate a peci_adapter
+ * structure, prior to calling peci_add_adapter().
+ *
+ * This must be called from context that can sleep.
+ *
+ * The caller is responsible for initializing the adapter's methods before
+ * calling peci_add_adapter(); and (after errors while adding the device)
+ * calling put_device() to prevent a memory leak.
+ *
+ * Return: the peci_adapter structure on success, else NULL.
+ */
+struct peci_adapter *peci_alloc_adapter(struct device *dev, unsigned int size)
+{
+	struct peci_adapter *adapter;
+
+	if (!dev)
+		return NULL;
+
+	adapter = kzalloc(size + sizeof(*adapter), GFP_KERNEL);
+	if (!adapter)
+		return NULL;
+
+	device_initialize(&adapter->dev);
+	adapter->dev.parent = dev;
+	adapter->dev.bus = &peci_bus_type;
+	adapter->dev.type = &peci_adapter_type;
+	peci_set_adapdata(adapter, &adapter[1]);
+
+	return adapter;
+}
+EXPORT_SYMBOL_GPL(peci_alloc_adapter);
+
+static int peci_register_adapter(struct peci_adapter *adapter)
+{
+	int rc = -EINVAL;
+
+	/* Can't register until after driver model init */
+	if (WARN_ON(!is_registered))
+		goto err_free_idr;
+
+	if (WARN(!adapter->name[0], "peci adapter has no name"))
+		goto err_free_idr;
+
+	if (WARN(!adapter->xfer, "peci adapter has no xfer function\n"))
+		goto err_free_idr;
+
+	rt_mutex_init(&adapter->bus_lock);
+	mutex_init(&adapter->userspace_clients_lock);
+	INIT_LIST_HEAD(&adapter->userspace_clients);
+
+	dev_set_name(&adapter->dev, "peci-%d", adapter->nr);
+
+	/* cdev */
+	cdev_init(&adapter->cdev, &peci_fops);
+	adapter->cdev.owner = THIS_MODULE;
+	adapter->dev.devt = MKDEV(MAJOR(peci_devt), adapter->nr);
+	rc = cdev_add(&adapter->cdev, adapter->dev.devt, 1);
+	if (rc) {
+		pr_err("adapter '%s': can't add cdev (%d)\n",
+		       adapter->name, rc);
+		goto err_free_idr;
+	}
+	rc = device_add(&adapter->dev);
+	if (rc) {
+		pr_err("adapter '%s': can't add device (%d)\n",
+		       adapter->name, rc);
+		goto err_del_cdev;
+	}
+
+	dev_dbg(&adapter->dev, "adapter [%s] registered\n", adapter->name);
+
+	pm_runtime_no_callbacks(&adapter->dev);
+	pm_suspend_ignore_children(&adapter->dev, true);
+	pm_runtime_enable(&adapter->dev);
+
+	/* create pre-declared device nodes */
+	peci_of_register_devices(adapter);
+
+	return 0;
+
+err_del_cdev:
+	cdev_del(&adapter->cdev);
+err_free_idr:
+	mutex_lock(&core_lock);
+	idr_remove(&peci_adapter_idr, adapter->nr);
+	mutex_unlock(&core_lock);
+	return rc;
+}
+
+static int peci_add_numbered_adapter(struct peci_adapter *adapter)
+{
+	int id;
+
+	mutex_lock(&core_lock);
+	id = idr_alloc(&peci_adapter_idr, adapter,
+		       adapter->nr, adapter->nr + 1, GFP_KERNEL);
+	mutex_unlock(&core_lock);
+	if (WARN(id < 0, "couldn't get idr"))
+		return id == -ENOSPC ? -EBUSY : id;
+
+	return peci_register_adapter(adapter);
+}
+
+/**
+ * peci_add_adapter - add a PECI adapter
+ * @adapter: initialized adapter, originally from peci_alloc_adapter()
+ * Context: can sleep
+ *
+ * PECI adapters connect to their drivers using some non-PECI bus,
+ * such as the platform bus.  The final stage of probe() in that code
+ * includes calling peci_add_adapter() to hook up to this PECI bus glue.
+ *
+ * This must be called from context that can sleep.
+ *
+ * It returns zero on success, else a negative error code (dropping the
+ * adapter's refcount).  After a successful return, the caller is responsible
+ * for calling peci_del_adapter().
+ *
+ * Return: zero on success, else a negative error code.
+ */
+int peci_add_adapter(struct peci_adapter *adapter)
+{
+	struct device *dev = &adapter->dev;
+	int id;
+
+	if (dev->of_node) {
+		id = of_alias_get_id(dev->of_node, "peci");
+		if (id >= 0) {
+			adapter->nr = id;
+			return peci_add_numbered_adapter(adapter);
+		}
+	}
+
+	mutex_lock(&core_lock);
+	id = idr_alloc(&peci_adapter_idr, adapter, 0, 0, GFP_KERNEL);
+	mutex_unlock(&core_lock);
+	if (WARN(id < 0, "couldn't get idr"))
+		return id;
+
+	adapter->nr = id;
+
+	return peci_register_adapter(adapter);
+}
+EXPORT_SYMBOL_GPL(peci_add_adapter);
+
+/**
+ * peci_del_adapter - delete a PECI adapter
+ * @adapter: the adpater being deleted
+ * Context: can sleep
+ *
+ * This call is used only by PECI adpater drivers, which are the only ones
+ * directly touching chip registers.
+ *
+ * This must be called from context that can sleep.
+ *
+ * Note that this function also drops a reference to the adapter.
+ */
+void peci_del_adapter(struct peci_adapter *adapter)
+{
+	struct peci_client *client, *next;
+	struct peci_adapter *found;
+	int nr;
+
+	/* First make sure that this adapter was ever added */
+	mutex_lock(&core_lock);
+	found = idr_find(&peci_adapter_idr, adapter->nr);
+	mutex_unlock(&core_lock);
+
+	if (found != adapter)
+		return;
+
+	/* Remove devices instantiated from sysfs */
+	mutex_lock(&adapter->userspace_clients_lock);
+	list_for_each_entry_safe(client, next, &adapter->userspace_clients,
+				 detected) {
+		dev_dbg(&adapter->dev, "Removing %s at 0x%x\n", client->name,
+			client->addr);
+		list_del(&client->detected);
+		peci_unregister_device(client);
+	}
+	mutex_unlock(&adapter->userspace_clients_lock);
+
+	/**
+	 * Detach any active clients. This can't fail, thus we do not
+	 * check the returned value.
+	 */
+	device_for_each_child(&adapter->dev, NULL, peci_unregister_client);
+
+	/* device name is gone after device_unregister */
+	dev_dbg(&adapter->dev, "adapter [%s] unregistered\n", adapter->name);
+
+	/* free cdev */
+	cdev_del(&adapter->cdev);
+
+	pm_runtime_disable(&adapter->dev);
+
+	nr = adapter->nr;
+
+	device_unregister(&adapter->dev);
+
+	/* free bus id */
+	mutex_lock(&core_lock);
+	idr_remove(&peci_adapter_idr, nr);
+	mutex_unlock(&core_lock);
+}
+EXPORT_SYMBOL_GPL(peci_del_adapter);
+
+/**
+ * peci_register_driver - register a PECI driver
+ * @owner: owner module of the driver being registered
+ * @driver: the driver being registered
+ * Context: can sleep
+ *
+ * Return: zero on success, else a negative error code.
+ */
+int peci_register_driver(struct module *owner, struct peci_driver *driver)
+{
+	int rc;
+
+	/* Can't register until after driver model init */
+	if (WARN_ON(!is_registered))
+		return -EAGAIN;
+
+	/* add the driver to the list of peci drivers in the driver core */
+	driver->driver.owner = owner;
+	driver->driver.bus = &peci_bus_type;
+
+	/**
+	 * When registration returns, the driver core
+	 * will have called probe() for all matching-but-unbound devices.
+	 */
+	rc = driver_register(&driver->driver);
+	if (rc)
+		return rc;
+
+	pr_debug("driver [%s] registered\n", driver->driver.name);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(peci_register_driver);
+
+/**
+ * peci_del_driver - unregister a PECI driver
+ * @driver: the driver being unregistered
+ * Context: can sleep
+ */
+void peci_del_driver(struct peci_driver *driver)
+{
+	driver_unregister(&driver->driver);
+	pr_debug("driver [%s] unregistered\n", driver->driver.name);
+}
+EXPORT_SYMBOL_GPL(peci_del_driver);
+
+static int __init peci_init(void)
+{
+	int ret;
+
+	ret = bus_register(&peci_bus_type);
+	if (ret < 0) {
+		pr_err("peci: Failed to register PECI bus type!\n");
+		return ret;
+	}
+
+	ret = alloc_chrdev_region(&peci_devt, 0, PECI_CDEV_MAX, "peci");
+	if (ret < 0) {
+		pr_err("peci: Failed to allocate chr dev region!\n");
+		bus_unregister(&peci_bus_type);
+		return ret;
+	}
+
+	crc8_populate_msb(peci_crc8_table, PECI_CRC8_POLYNOMIAL);
+
+	if (IS_ENABLED(CONFIG_OF_DYNAMIC))
+		WARN_ON(of_reconfig_notifier_register(&peci_of_notifier));
+
+	is_registered = true;
+
+	return 0;
+}
+
+static void __exit peci_exit(void)
+{
+	if (IS_ENABLED(CONFIG_OF_DYNAMIC))
+		WARN_ON(of_reconfig_notifier_unregister(&peci_of_notifier));
+
+	unregister_chrdev_region(peci_devt, PECI_CDEV_MAX);
+	bus_unregister(&peci_bus_type);
+}
+
+postcore_initcall(peci_init);
+module_exit(peci_exit);
+
+MODULE_AUTHOR("Jason M Biils <jason.m.bills@linux.intel.com>");
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI bus core module");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/peci/peci-npcm.c b/drivers/peci/peci-npcm.c
new file mode 100644
index 0000000..f632365
--- /dev/null
+++ b/drivers/peci/peci-npcm.c
@@ -0,0 +1,410 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Nuvoton Technology corporation.
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/peci.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/reset.h>
+
+/* NPCM7xx GCR module */
+#define NPCM7XX_INTCR3_OFFSET		0x9C
+#define NPCM7XX_INTCR3_PECIVSEL		BIT(19)
+
+/* NPCM PECI Registers */
+#define NPCM_PECI_CTL_STS	0x00
+#define NPCM_PECI_RD_LENGTH	0x04
+#define NPCM_PECI_ADDR		0x08
+#define NPCM_PECI_CMD		0x0C
+#define NPCM_PECI_CTL2		0x10
+#define NPCM_PECI_WR_LENGTH	0x1C
+#define NPCM_PECI_PDDR		0x2C
+#define NPCM_PECI_DAT_INOUT(n)	(0x100 + ((n) * 4))
+
+#define NPCM_PECI_MAX_REG	0x200
+
+/* NPCM_PECI_CTL_STS - 0x00 : Control Register */
+#define NPCM_PECI_CTRL_DONE_INT_EN	BIT(6)
+#define NPCM_PECI_CTRL_ABRT_ERR		BIT(4)
+#define NPCM_PECI_CTRL_CRC_ERR		BIT(3)
+#define NPCM_PECI_CTRL_DONE		BIT(1)
+#define NPCM_PECI_CTRL_START_BUSY	BIT(0)
+
+/* NPCM_PECI_RD_LENGTH - 0x04 : Command Register */
+#define NPCM_PECI_RD_LEN_MASK		GENMASK(6, 0)
+
+/* NPCM_PECI_CMD - 0x10 : Command Register */
+#define NPCM_PECI_CTL2_MASK		GENMASK(7, 6)
+
+/* NPCM_PECI_WR_LENGTH - 0x1C : Command Register */
+#define NPCM_PECI_WR_LEN_MASK		GENMASK(6, 0)
+
+/* NPCM_PECI_PDDR - 0x2C : Command Register */
+#define NPCM_PECI_PDDR_MASK		GENMASK(4, 0)
+
+#define NPCM_PECI_INT_MASK	(NPCM_PECI_CTRL_ABRT_ERR | \
+				NPCM_PECI_CTRL_CRC_ERR | \
+				NPCM_PECI_CTRL_DONE)
+
+#define NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC	50000
+#define NPCM_PECI_IDLE_CHECK_INTERVAL_USEC	10000
+#define NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT	1000
+#define NPCM_PECI_CMD_TIMEOUT_MS_MAX		60000
+#define NPCM_PECI_HOST_NEG_BIT_RATE_MAX		31
+#define NPCM_PECI_HOST_NEG_BIT_RATE_MIN		7
+#define NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT	15
+#define NPCM_PECI_PULL_DOWN_DEFAULT		0
+#define NPCM_PECI_PULL_DOWN_MAX			2
+
+struct npcm_peci {
+	u32			cmd_timeout_ms;
+	u32			host_bit_rate;
+	struct completion	xfer_complete;
+	struct regmap		*gcr_regmap;
+	struct peci_adapter	*adapter;
+	struct regmap		*regmap;
+	u32			status;
+	spinlock_t		lock; /* to sync completion status handling */
+	struct device		*dev;
+	struct clk		*clk;
+	int			irq;
+};
+
+static int npcm_peci_xfer_native(struct npcm_peci *priv,
+				 struct peci_xfer_msg *msg)
+{
+	long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms);
+	unsigned long flags;
+	unsigned int msg_rd;
+	u32 cmd_sts;
+	int i, rc;
+
+	/* Check command sts and bus idle state */
+	rc = regmap_read_poll_timeout(priv->regmap, NPCM_PECI_CTL_STS, cmd_sts,
+				      !(cmd_sts & NPCM_PECI_CTRL_START_BUSY),
+				      NPCM_PECI_IDLE_CHECK_INTERVAL_USEC,
+				      NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC);
+	if (rc)
+		return rc; /* -ETIMEDOUT */
+
+	spin_lock_irqsave(&priv->lock, flags);
+	reinit_completion(&priv->xfer_complete);
+
+	regmap_write(priv->regmap, NPCM_PECI_ADDR, msg->addr);
+	regmap_write(priv->regmap, NPCM_PECI_RD_LENGTH,
+		     NPCM_PECI_WR_LEN_MASK & msg->rx_len);
+	regmap_write(priv->regmap, NPCM_PECI_WR_LENGTH,
+		     NPCM_PECI_WR_LEN_MASK & msg->tx_len);
+
+	if (msg->tx_len) {
+		regmap_write(priv->regmap, NPCM_PECI_CMD, msg->tx_buf[0]);
+
+		for (i = 0; i < (msg->tx_len - 1); i++)
+			regmap_write(priv->regmap, NPCM_PECI_DAT_INOUT(i),
+				     msg->tx_buf[i + 1]);
+	}
+
+	priv->status = 0;
+	regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS,
+			   NPCM_PECI_CTRL_START_BUSY,
+			   NPCM_PECI_CTRL_START_BUSY);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	err = wait_for_completion_interruptible_timeout(&priv->xfer_complete,
+							timeout);
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	regmap_write(priv->regmap, NPCM_PECI_CMD, 0);
+
+	if (err <= 0 || priv->status  != NPCM_PECI_CTRL_DONE) {
+		if (err < 0) { /* -ERESTARTSYS */
+			rc = (int)err;
+			goto err_irqrestore;
+		} else if (err == 0) {
+			dev_dbg(priv->dev, "Timeout waiting for a response!\n");
+			rc = -ETIMEDOUT;
+			goto err_irqrestore;
+		}
+
+		dev_dbg(priv->dev, "No valid response!\n");
+		rc = -EIO;
+		goto err_irqrestore;
+	}
+
+	for (i = 0; i < msg->rx_len; i++) {
+		regmap_read(priv->regmap, NPCM_PECI_DAT_INOUT(i), &msg_rd);
+		msg->rx_buf[i] = (u8)msg_rd;
+	}
+
+err_irqrestore:
+	spin_unlock_irqrestore(&priv->lock, flags);
+	return rc;
+}
+
+static irqreturn_t npcm_peci_irq_handler(int irq, void *arg)
+{
+	struct npcm_peci *priv = arg;
+	u32 status_ack = 0;
+	u32 status;
+
+	spin_lock(&priv->lock);
+	regmap_read(priv->regmap, NPCM_PECI_CTL_STS, &status);
+	priv->status |= (status & NPCM_PECI_INT_MASK);
+
+	if (status & NPCM_PECI_CTRL_CRC_ERR) {
+		dev_dbg(priv->dev, "PECI_INT_W_FCS_BAD\n");
+		status_ack |= NPCM_PECI_CTRL_CRC_ERR;
+	}
+
+	if (status & NPCM_PECI_CTRL_ABRT_ERR) {
+		dev_dbg(priv->dev, "NPCM_PECI_CTRL_ABRT_ERR\n");
+		status_ack |= NPCM_PECI_CTRL_ABRT_ERR;
+	}
+
+	/*
+	 * All commands should be ended up with a NPCM_PECI_CTRL_DONE
+	 * bit set even in an error case.
+	 */
+	if (status & NPCM_PECI_CTRL_DONE) {
+		dev_dbg(priv->dev, "NPCM_PECI_CTRL_DONE\n");
+		status_ack |= NPCM_PECI_CTRL_DONE;
+		complete(&priv->xfer_complete);
+	}
+
+	regmap_write_bits(priv->regmap, NPCM_PECI_CTL_STS,
+			  NPCM_PECI_INT_MASK, status_ack);
+
+	spin_unlock(&priv->lock);
+	return IRQ_HANDLED;
+}
+
+static int npcm_peci_init_ctrl(struct npcm_peci *priv)
+{
+	u32 cmd_sts, host_neg_bit_rate = 0, pull_down = 0;
+	int ret;
+	bool volt;
+
+	priv->clk = devm_clk_get(priv->dev, NULL);
+	if (IS_ERR(priv->clk)) {
+		dev_err(priv->dev, "Failed to get clk source.\n");
+		return PTR_ERR(priv->clk);
+	}
+
+	ret = clk_prepare_enable(priv->clk);
+	if (ret) {
+		dev_err(priv->dev, "Failed to enable clock.\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(priv->dev->of_node, "cmd-timeout-ms",
+				   &priv->cmd_timeout_ms);
+	if (ret || priv->cmd_timeout_ms > NPCM_PECI_CMD_TIMEOUT_MS_MAX ||
+	    priv->cmd_timeout_ms == 0) {
+		if (ret)
+			dev_warn(priv->dev,
+				 "cmd-timeout-ms not found, use default : %u\n",
+				 NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT);
+		else
+			dev_warn(priv->dev,
+				 "Invalid cmd-timeout-ms : %u. Use default : %u\n",
+				 priv->cmd_timeout_ms,
+				 NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT);
+
+		priv->cmd_timeout_ms = NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT;
+	}
+
+	if (of_device_is_compatible(priv->dev->of_node,
+				    "nuvoton,npcm750-peci")) {
+		priv->gcr_regmap = syscon_regmap_lookup_by_compatible
+			("nuvoton,npcm750-gcr");
+		if (!IS_ERR(priv->gcr_regmap)) {
+			volt = of_property_read_bool(priv->dev->of_node,
+						     "high-volt-range");
+			if (volt)
+				regmap_update_bits(priv->gcr_regmap,
+						   NPCM7XX_INTCR3_OFFSET,
+						   NPCM7XX_INTCR3_PECIVSEL,
+						   NPCM7XX_INTCR3_PECIVSEL);
+			else
+				regmap_update_bits(priv->gcr_regmap,
+						   NPCM7XX_INTCR3_OFFSET,
+						   NPCM7XX_INTCR3_PECIVSEL, 0);
+		}
+	}
+
+	ret = of_property_read_u32(priv->dev->of_node, "pull-down",
+				   &pull_down);
+	if (ret || pull_down > NPCM_PECI_PULL_DOWN_MAX) {
+		if (ret)
+			dev_warn(priv->dev,
+				 "pull-down not found, use default : %u\n",
+				 NPCM_PECI_PULL_DOWN_DEFAULT);
+		else
+			dev_warn(priv->dev,
+				 "Invalid pull-down : %u. Use default : %u\n",
+				 pull_down,
+				 NPCM_PECI_PULL_DOWN_DEFAULT);
+		pull_down = NPCM_PECI_PULL_DOWN_DEFAULT;
+	}
+
+	regmap_update_bits(priv->regmap, NPCM_PECI_CTL2, NPCM_PECI_CTL2_MASK,
+			   pull_down << 6);
+
+	ret = of_property_read_u32(priv->dev->of_node, "host-neg-bit-rate",
+				   &host_neg_bit_rate);
+	if (ret || host_neg_bit_rate > NPCM_PECI_HOST_NEG_BIT_RATE_MAX ||
+	    host_neg_bit_rate < NPCM_PECI_HOST_NEG_BIT_RATE_MIN) {
+		if (ret)
+			dev_warn(priv->dev,
+				 "host-neg-bit-rate not found, use default : %u\n",
+				 NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT);
+		else
+			dev_warn(priv->dev,
+				 "Invalid host-neg-bit-rate : %u. Use default : %u\n",
+				 host_neg_bit_rate,
+				 NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT);
+		host_neg_bit_rate = NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT;
+	}
+
+	regmap_update_bits(priv->regmap, NPCM_PECI_PDDR, NPCM_PECI_PDDR_MASK,
+			   host_neg_bit_rate);
+
+	priv->host_bit_rate = clk_get_rate(priv->clk) /
+		(4 * (host_neg_bit_rate + 1));
+
+	ret = regmap_read_poll_timeout(priv->regmap, NPCM_PECI_CTL_STS, cmd_sts,
+				       !(cmd_sts & NPCM_PECI_CTRL_START_BUSY),
+				       NPCM_PECI_IDLE_CHECK_INTERVAL_USEC,
+				       NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC);
+	if (ret)
+		return ret; /* -ETIMEDOUT */
+
+	/* PECI interrupt enable */
+	regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS,
+			   NPCM_PECI_CTRL_DONE_INT_EN,
+			   NPCM_PECI_CTRL_DONE_INT_EN);
+
+	return 0;
+}
+
+static const struct regmap_config npcm_peci_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = NPCM_PECI_MAX_REG,
+	.fast_io = true,
+};
+
+static int npcm_peci_xfer(struct peci_adapter *adapter,
+			  struct peci_xfer_msg *msg)
+{
+	struct npcm_peci *priv = peci_get_adapdata(adapter);
+
+	return npcm_peci_xfer_native(priv, msg);
+}
+
+static int npcm_peci_probe(struct platform_device *pdev)
+{
+	struct peci_adapter *adapter;
+	struct npcm_peci *priv;
+	struct resource *res;
+	void __iomem *base;
+	int ret;
+
+	adapter = peci_alloc_adapter(&pdev->dev, sizeof(*priv));
+	if (!adapter)
+		return -ENOMEM;
+
+	priv = peci_get_adapdata(adapter);
+	priv->adapter = adapter;
+	priv->dev = &pdev->dev;
+	dev_set_drvdata(&pdev->dev, priv);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base)) {
+		ret = PTR_ERR(base);
+		goto err_put_adapter_dev;
+	}
+
+	priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+					     &npcm_peci_regmap_config);
+	if (IS_ERR(priv->regmap)) {
+		ret = PTR_ERR(priv->regmap);
+		goto err_put_adapter_dev;
+	}
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (!priv->irq) {
+		ret = -ENODEV;
+		goto err_put_adapter_dev;
+	}
+
+	ret = devm_request_irq(&pdev->dev, priv->irq, npcm_peci_irq_handler,
+			       0, "peci-npcm-irq", priv);
+	if (ret)
+		goto err_put_adapter_dev;
+
+	init_completion(&priv->xfer_complete);
+	spin_lock_init(&priv->lock);
+
+	priv->adapter->owner = THIS_MODULE;
+	priv->adapter->dev.of_node = of_node_get(dev_of_node(priv->dev));
+	strlcpy(priv->adapter->name, pdev->name, sizeof(priv->adapter->name));
+	priv->adapter->xfer = npcm_peci_xfer;
+
+	ret = npcm_peci_init_ctrl(priv);
+	if (ret)
+		goto err_put_adapter_dev;
+
+	ret = peci_add_adapter(priv->adapter);
+	if (ret)
+		goto err_put_adapter_dev;
+
+	dev_info(&pdev->dev, "peci bus %d registered, host negotiation bit rate %dHz",
+		 priv->adapter->nr, priv->host_bit_rate);
+
+	return 0;
+
+err_put_adapter_dev:
+	put_device(&adapter->dev);
+	return ret;
+}
+
+static int npcm_peci_remove(struct platform_device *pdev)
+{
+	struct npcm_peci *priv = dev_get_drvdata(&pdev->dev);
+
+	clk_disable_unprepare(priv->clk);
+	peci_del_adapter(priv->adapter);
+	of_node_put(priv->adapter->dev.of_node);
+
+	return 0;
+}
+
+static const struct of_device_id npcm_peci_of_table[] = {
+	{ .compatible = "nuvoton,npcm750-peci", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, npcm_peci_of_table);
+
+static struct platform_driver npcm_peci_driver = {
+	.probe  = npcm_peci_probe,
+	.remove = npcm_peci_remove,
+	.driver = {
+		.name           = "peci-npcm",
+		.of_match_table = of_match_ptr(npcm_peci_of_table),
+	},
+};
+module_platform_driver(npcm_peci_driver);
+
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_DESCRIPTION("NPCM Platform Environment Control Interface (PECI) driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 225b0b8..bc9b9bb 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1790,6 +1790,16 @@
 	  If you say yes here, you get support for the RTD1295 SoC
 	  Real Time Clock.
 
+config RTC_DRV_ASPEED
+	tristate "Aspeed RTC"
+	depends on ARCH_ASPEED || COMPILE_TEST
+	help
+	  If you say yes here you get support for the ASPEED AST2400 and
+	  AST2500 SoC real time clocks.
+
+	  This driver can also be built as a module, if so, the module
+	  will be called "rtc-aspeed".
+
 comment "HID Sensor RTC drivers"
 
 config RTC_DRV_HID_SENSOR_TIME
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index df022d8..4ebad3a 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -33,6 +33,7 @@
 obj-$(CONFIG_RTC_DRV_ARMADA38X)	+= rtc-armada38x.o
 obj-$(CONFIG_RTC_DRV_AS3722)	+= rtc-as3722.o
 obj-$(CONFIG_RTC_DRV_ASM9260)	+= rtc-asm9260.o
+obj-$(CONFIG_RTC_DRV_ASPEED)	+= rtc-aspeed.o
 obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
 obj-$(CONFIG_RTC_DRV_AT91SAM9)	+= rtc-at91sam9.o
 obj-$(CONFIG_RTC_DRV_AU1XXX)	+= rtc-au1xxx.o
diff --git a/drivers/rtc/rtc-aspeed.c b/drivers/rtc/rtc-aspeed.c
new file mode 100644
index 0000000..dce00e5
--- /dev/null
+++ b/drivers/rtc/rtc-aspeed.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2015 IBM Corp.
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/io.h>
+
+struct aspeed_rtc {
+	struct rtc_device	*rtc_dev;
+	void __iomem		*base;
+	spinlock_t		lock;
+};
+
+#define RTC_TIME	0x00
+#define RTC_YEAR	0x04
+#define RTC_CTRL	0x10
+
+#define RTC_UNLOCK	0x02
+#define RTC_ENABLE	0x01
+
+static int aspeed_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct aspeed_rtc *rtc = dev_get_drvdata(dev);
+	unsigned int cent, year, mon, day, hour, min, sec;
+	unsigned long flags;
+	u32 reg1, reg2;
+
+	spin_lock_irqsave(&rtc->lock, flags);
+
+	do {
+		reg2 = readl(rtc->base + RTC_YEAR);
+		reg1 = readl(rtc->base + RTC_TIME);
+	} while (reg2 != readl(rtc->base + RTC_YEAR));
+
+	day  = (reg1 >> 24) & 0x1f;
+	hour = (reg1 >> 16) & 0x1f;
+	min  = (reg1 >>  8) & 0x3f;
+	sec  = (reg1 >>  0) & 0x3f;
+	cent = (reg2 >> 16) & 0x1f;
+	year = (reg2 >>  8) & 0x7f;
+	/*
+	 * Month is 1-12 in hardware, and 0-11 in struct rtc_time, however we
+	 * are using mktime64 which is 1-12, so no adjustment is necessary
+	 */
+	mon  = (reg2 >>  0) & 0x0f;
+
+	rtc_time64_to_tm(mktime64(cent * 100 + year, mon, day, hour, min, sec),
+			tm);
+
+	spin_unlock_irqrestore(&rtc->lock, flags);
+
+	return 0;
+}
+
+static int aspeed_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct aspeed_rtc *rtc = dev_get_drvdata(dev);
+	unsigned long flags;
+	u32 reg1, reg2, ctrl;
+	int year, cent;
+
+	spin_lock_irqsave(&rtc->lock, flags);
+
+	cent = (tm->tm_year + 1900) / 100;
+	year = tm->tm_year % 100;
+
+	reg1 = (tm->tm_mday << 24) | (tm->tm_hour << 16) | (tm->tm_min << 8) |
+		tm->tm_sec;
+
+	/* Hardware is 1-12, convert to 0-11 */
+	reg2 = ((cent & 0x1f) << 16) | ((year & 0x7f) << 8) |
+		((tm->tm_mon & 0xf) + 1);
+
+	ctrl = readl(rtc->base + RTC_CTRL);
+	writel(ctrl | RTC_UNLOCK, rtc->base + RTC_CTRL);
+
+	writel(reg1, rtc->base + RTC_TIME);
+	writel(reg2, rtc->base + RTC_YEAR);
+
+	writel(ctrl, rtc->base + RTC_CTRL);
+
+	spin_unlock_irqrestore(&rtc->lock, flags);
+
+	return 0;
+}
+
+static const struct rtc_class_ops aspeed_rtc_ops = {
+	.read_time = aspeed_rtc_read_time,
+	.set_time = aspeed_rtc_set_time,
+};
+
+static int aspeed_rtc_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct aspeed_rtc *rtc;
+
+	rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+	if (!rtc)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	rtc->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(rtc->base))
+		return PTR_ERR(rtc->base);
+
+	platform_set_drvdata(pdev, rtc);
+
+	rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
+						&aspeed_rtc_ops, THIS_MODULE);
+
+	if (IS_ERR(rtc->rtc_dev))
+		return PTR_ERR(rtc->rtc_dev);
+
+	spin_lock_init(&rtc->lock);
+
+	/* Enable RTC and clear the unlock bit */
+	writel(RTC_ENABLE, rtc->base + RTC_CTRL);
+
+	return 0;
+}
+
+static const struct of_device_id aspeed_rtc_match[] = {
+	{ .compatible = "aspeed,ast2400-rtc", },
+	{ .compatible = "aspeed,ast2500-rtc", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, aspeed_rtc_match);
+
+static struct platform_driver aspeed_rtc_driver = {
+	.driver = {
+		.name = "aspeed-rtc",
+		.of_match_table = of_match_ptr(aspeed_rtc_match),
+	},
+};
+
+module_platform_driver_probe(aspeed_rtc_driver, aspeed_rtc_probe);
+
+MODULE_DESCRIPTION("Aspeed RTC driver");
+MODULE_AUTHOR("Joel Stanley <joel@jms.id.au>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index c07b4a8..b750a88 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -2,6 +2,7 @@
 
 source "drivers/soc/actions/Kconfig"
 source "drivers/soc/amlogic/Kconfig"
+source "drivers/soc/aspeed/Kconfig"
 source "drivers/soc/atmel/Kconfig"
 source "drivers/soc/bcm/Kconfig"
 source "drivers/soc/fsl/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 90b686e..eb473f4 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -4,6 +4,7 @@
 #
 
 obj-$(CONFIG_ARCH_ACTIONS)	+= actions/
+obj-$(CONFIG_ARCH_ASPEED)       += aspeed/
 obj-$(CONFIG_ARCH_AT91)		+= atmel/
 obj-y				+= bcm/
 obj-$(CONFIG_ARCH_DOVE)		+= dove/
diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig
new file mode 100644
index 0000000..704a4efe
--- /dev/null
+++ b/drivers/soc/aspeed/Kconfig
@@ -0,0 +1,11 @@
+menu "ASPEED SoC drivers"
+
+config ASPEED_BMC_MISC
+	bool "Miscellaneous ASPEED BMC interfaces"
+	depends on ARCH_ASPEED || COMPILE_TEST
+	default ARCH_ASPEED
+	help
+	  Say yes to expose VGA and LPC scratch registers, and other
+	  miscellaneous control interfaces specific to the ASPEED BMC SoCs
+
+endmenu
diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile
new file mode 100644
index 0000000..d1a80f9
--- /dev/null
+++ b/drivers/soc/aspeed/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_ASPEED_BMC_MISC) += aspeed-bmc-misc.o
diff --git a/drivers/soc/aspeed/aspeed-bmc-misc.c b/drivers/soc/aspeed/aspeed-bmc-misc.c
new file mode 100644
index 0000000..314007b
--- /dev/null
+++ b/drivers/soc/aspeed/aspeed-bmc-misc.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2018 IBM Corp.
+
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#define DEVICE_NAME "aspeed-bmc-misc"
+
+struct aspeed_bmc_ctrl {
+	const char *name;
+	u32 offset;
+	u32 mask;
+	u32 shift;
+	struct regmap *map;
+	struct kobj_attribute attr;
+};
+
+struct aspeed_bmc_misc {
+	struct device *dev;
+	struct regmap *map;
+	struct aspeed_bmc_ctrl *ctrls;
+	int nr_ctrls;
+};
+
+static int aspeed_bmc_misc_parse_dt_child(struct device_node *child,
+					  struct aspeed_bmc_ctrl *ctrl)
+{
+	int rc;
+
+	/* Example child:
+	 *
+	 * ilpc2ahb {
+	 *     offset = <0x80>;
+	 *     bit-mask = <0x1>;
+	 *     bit-shift = <6>;
+	 *     label = "foo";
+	 * }
+	 */
+	if (of_property_read_string(child, "label", &ctrl->name))
+		ctrl->name = child->name;
+
+	rc = of_property_read_u32(child, "offset", &ctrl->offset);
+	if (rc < 0)
+		return rc;
+
+	rc = of_property_read_u32(child, "bit-mask", &ctrl->mask);
+	if (rc < 0)
+		return rc;
+
+	rc = of_property_read_u32(child, "bit-shift", &ctrl->shift);
+	if (rc < 0)
+		return rc;
+
+	ctrl->mask <<= ctrl->shift;
+
+	return 0;
+}
+
+static int aspeed_bmc_misc_parse_dt(struct aspeed_bmc_misc *bmc,
+				    struct device_node *parent)
+{
+	struct aspeed_bmc_ctrl *ctrl;
+	struct device_node *child;
+	int rc;
+
+	bmc->nr_ctrls = of_get_child_count(parent);
+	bmc->ctrls = devm_kcalloc(bmc->dev, bmc->nr_ctrls, sizeof(*bmc->ctrls),
+				  GFP_KERNEL);
+	if (!bmc->ctrls)
+		return -ENOMEM;
+
+	ctrl = bmc->ctrls;
+	for_each_child_of_node(parent, child) {
+		rc = aspeed_bmc_misc_parse_dt_child(child, ctrl++);
+		if (rc < 0) {
+			of_node_put(child);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static ssize_t aspeed_bmc_misc_show(struct kobject *kobj,
+				    struct kobj_attribute *attr, char *buf)
+{
+	struct aspeed_bmc_ctrl *ctrl;
+	unsigned int val;
+	int rc;
+
+	ctrl = container_of(attr, struct aspeed_bmc_ctrl, attr);
+	rc = regmap_read(ctrl->map, ctrl->offset, &val);
+	if (rc)
+		return rc;
+
+	val &= ctrl->mask;
+	val >>= ctrl->shift;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t aspeed_bmc_misc_store(struct kobject *kobj,
+				     struct kobj_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct aspeed_bmc_ctrl *ctrl;
+	long val;
+	int rc;
+
+	rc = kstrtol(buf, 0, &val);
+	if (rc)
+		return rc;
+
+	ctrl = container_of(attr, struct aspeed_bmc_ctrl, attr);
+	val <<= ctrl->shift;
+	rc = regmap_update_bits(ctrl->map, ctrl->offset, ctrl->mask, val);
+
+	return rc < 0 ? rc : count;
+}
+
+static int aspeed_bmc_misc_add_sysfs_attr(struct aspeed_bmc_misc *bmc,
+					  struct aspeed_bmc_ctrl *ctrl)
+{
+	ctrl->map = bmc->map;
+
+	sysfs_attr_init(&ctrl->attr.attr);
+	ctrl->attr.attr.name = ctrl->name;
+	ctrl->attr.attr.mode = 0664;
+	ctrl->attr.show = aspeed_bmc_misc_show;
+	ctrl->attr.store = aspeed_bmc_misc_store;
+
+	return sysfs_create_file(&bmc->dev->kobj, &ctrl->attr.attr);
+}
+
+static int aspeed_bmc_misc_populate_sysfs(struct aspeed_bmc_misc *bmc)
+{
+	int rc;
+	int i;
+
+	for (i = 0; i < bmc->nr_ctrls; i++) {
+		rc = aspeed_bmc_misc_add_sysfs_attr(bmc, &bmc->ctrls[i]);
+		if (rc < 0)
+			return rc;
+	}
+
+	return 0;
+}
+
+static int aspeed_bmc_misc_probe(struct platform_device *pdev)
+{
+	struct aspeed_bmc_misc *bmc;
+	int rc;
+
+	bmc = devm_kzalloc(&pdev->dev, sizeof(*bmc), GFP_KERNEL);
+	if (!bmc)
+		return -ENOMEM;
+
+	bmc->dev = &pdev->dev;
+	bmc->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
+	if (IS_ERR(bmc->map))
+		return PTR_ERR(bmc->map);
+
+	rc = aspeed_bmc_misc_parse_dt(bmc, pdev->dev.of_node);
+	if (rc < 0)
+		return rc;
+
+	return aspeed_bmc_misc_populate_sysfs(bmc);
+}
+
+static const struct of_device_id aspeed_bmc_misc_match[] = {
+	{ .compatible = "aspeed,bmc-misc" },
+	{ },
+};
+
+static struct platform_driver aspeed_bmc_misc = {
+	.driver = {
+		.name		= DEVICE_NAME,
+		.of_match_table = aspeed_bmc_misc_match,
+	},
+	.probe = aspeed_bmc_misc_probe,
+};
+
+module_platform_driver(aspeed_bmc_misc);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
diff --git a/include/linux/clk/nuvoton.h b/include/linux/clk/nuvoton.h
new file mode 100644
index 0000000..9a474d6
--- /dev/null
+++ b/include/linux/clk/nuvoton.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2014-2019 Nuvoton Technology corporation. */
+
+#ifndef __LINUX_CLK_NUVOTON_H_
+#define __LINUX_CLK_NUVOTON_H_
+
+void nuvoton_npcm750_clock_init(void);
+
+#endif
diff --git a/include/linux/mfd/intel-peci-client.h b/include/linux/mfd/intel-peci-client.h
new file mode 100644
index 0000000..8f6d823
--- /dev/null
+++ b/include/linux/mfd/intel-peci-client.h
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018 Intel Corporation */
+
+#ifndef __LINUX_MFD_INTEL_PECI_CLIENT_H
+#define __LINUX_MFD_INTEL_PECI_CLIENT_H
+
+#include <linux/peci.h>
+
+#if IS_ENABLED(CONFIG_X86)
+#include <asm/intel-family.h>
+#else
+/**
+ * Architectures other than x86 cannot include the header file so define these
+ * at here. These are needed for detecting type of client x86 CPUs behind a PECI
+ * connection.
+ */
+#define INTEL_FAM6_HASWELL_X   0x3F
+#define INTEL_FAM6_BROADWELL_X 0x4F
+#define INTEL_FAM6_SKYLAKE_X   0x55
+#endif
+
+#define CORE_MAX_ON_HSX        18 /* Max number of cores on Haswell */
+#define CHAN_RANK_MAX_ON_HSX   8  /* Max number of channel ranks on Haswell */
+#define DIMM_IDX_MAX_ON_HSX    3  /* Max DIMM index per channel on Haswell */
+
+#define CORE_MAX_ON_BDX        24 /* Max number of cores on Broadwell */
+#define CHAN_RANK_MAX_ON_BDX   4  /* Max number of channel ranks on Broadwell */
+#define DIMM_IDX_MAX_ON_BDX    3  /* Max DIMM index per channel on Broadwell */
+
+#define CORE_MAX_ON_SKX        28 /* Max number of cores on Skylake */
+#define CHAN_RANK_MAX_ON_SKX   6  /* Max number of channel ranks on Skylake */
+#define DIMM_IDX_MAX_ON_SKX    2  /* Max DIMM index per channel on Skylake */
+
+#define CORE_NUMS_MAX          CORE_MAX_ON_SKX
+#define CHAN_RANK_MAX          CHAN_RANK_MAX_ON_HSX
+#define DIMM_IDX_MAX           DIMM_IDX_MAX_ON_HSX
+#define DIMM_NUMS_MAX          (CHAN_RANK_MAX * DIMM_IDX_MAX)
+
+/**
+ * struct cpu_gen_info - CPU generation specific information
+ * @family: CPU family ID
+ * @model: CPU model
+ * @core_max: max number of cores
+ * @chan_rank_max: max number of channel ranks
+ * @dimm_idx_max: max number of DIMM indices
+ *
+ * CPU generation specific information to identify maximum number of cores and
+ * DIMM slots.
+ */
+struct cpu_gen_info {
+	u16  family;
+	u8   model;
+	uint core_max;
+	uint chan_rank_max;
+	uint dimm_idx_max;
+};
+
+/**
+ * struct peci_client_manager - PECI client manager information
+ * @client; pointer to the PECI client
+ * @dev: pointer to the struct device
+ * @name: PECI client manager name
+ * @gen_info: CPU generation info of the detected CPU
+ *
+ * PECI client manager information for managing PECI sideband functions on a CPU
+ * client.
+ */
+struct peci_client_manager {
+	struct peci_client *client;
+	struct device *dev;
+	char name[PECI_NAME_SIZE];
+	const struct cpu_gen_info *gen_info;
+};
+
+/**
+ * peci_client_read_package_config - read from the Package Configuration Space
+ * @priv: driver private data structure
+ * @index: encoding index for the requested service
+ * @param: parameter to specify the exact data being requested
+ * @data: data buffer to store the result
+ * Context: can sleep
+ *
+ * A generic PECI command that provides read access to the
+ * "Package Configuration Space" that is maintained by the PCU, including
+ * various power and thermal management functions. Typical PCS read services
+ * supported by the processor may include access to temperature data, energy
+ * status, run time information, DIMM temperatures and so on.
+ *
+ * Return: zero on success, else a negative error code.
+ */
+static inline int
+peci_client_read_package_config(struct peci_client_manager *priv,
+				u8 index, u16 param, u8 *data)
+{
+	struct peci_rd_pkg_cfg_msg msg;
+	int rc;
+
+	msg.addr = priv->client->addr;
+	msg.index = index;
+	msg.param = param;
+	msg.rx_len = 4;
+
+	rc = peci_command(priv->client->adapter, PECI_CMD_RD_PKG_CFG, &msg);
+	if (!rc)
+		memcpy(data, msg.pkg_config, 4);
+
+	return rc;
+}
+
+#endif /* __LINUX_MFD_INTEL_PECI_CLIENT_H */
diff --git a/include/linux/peci.h b/include/linux/peci.h
new file mode 100644
index 0000000..d0e47d4
--- /dev/null
+++ b/include/linux/peci.h
@@ -0,0 +1,142 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018 Intel Corporation */
+
+#ifndef __LINUX_PECI_H
+#define __LINUX_PECI_H
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/peci-ioctl.h>
+#include <linux/rtmutex.h>
+
+#define PECI_NAME_SIZE   32
+
+struct peci_board_info {
+	char			type[PECI_NAME_SIZE];
+	unsigned short		addr;	/* CPU client address */
+	struct device_node	*of_node;
+};
+
+/**
+ * struct peci_adapter - represent a PECI adapter
+ * @owner: owner module of the PECI adpater
+ * @bus_lock: mutex for exclusion of multiple callers
+ * @dev: device interface to this driver
+ * @cdev: character device object to create character device
+ * @nr: the bus number to map
+ * @name: name of the adapter
+ * @userspace_clients_lock: mutex for exclusion of clients handling
+ * @userspace_clients: list of registered clients
+ * @xfer: low-level transfer function pointer of the adapter
+ * @cmd_mask: mask for supportable PECI commands
+ *
+ * Each PECI adapter can communicate with one or more PECI client children.
+ * These make a small bus, sharing a single wired PECI connection.
+ */
+struct peci_adapter {
+	struct module		*owner;
+	struct rt_mutex		bus_lock;
+	struct device		dev;
+	struct cdev		cdev;
+	int			nr;
+	char			name[PECI_NAME_SIZE];
+	struct mutex		userspace_clients_lock; /* clients list mutex */
+	struct list_head	userspace_clients;
+	int			(*xfer)(struct peci_adapter *adapter,
+					struct peci_xfer_msg *msg);
+	uint			cmd_mask;
+};
+
+static inline struct peci_adapter *to_peci_adapter(void *d)
+{
+	return container_of(d, struct peci_adapter, dev);
+}
+
+static inline void *peci_get_adapdata(const struct peci_adapter *adapter)
+{
+	return dev_get_drvdata(&adapter->dev);
+}
+
+static inline void peci_set_adapdata(struct peci_adapter *adapter, void *data)
+{
+	dev_set_drvdata(&adapter->dev, data);
+}
+
+/**
+ * struct peci_client - represent a PECI client device
+ * @dev: driver model device node for the client
+ * @adapter: manages the bus segment hosting this PECI device
+ * @addr: address used on the PECI bus connected to the parent adapter
+ * @name: indicates the type of the device
+ * @detected: detected PECI clients list
+ *
+ * A peci_client identifies a single device (i.e. CPU) connected to a peci bus.
+ * The behaviour exposed to Linux is defined by the driver managing the device.
+ */
+struct peci_client {
+	struct device		dev;
+	struct peci_adapter	*adapter;
+	u8			addr;
+	char			name[PECI_NAME_SIZE];
+	struct list_head	detected;
+};
+
+static inline struct peci_client *to_peci_client(void *d)
+{
+	return container_of(d, struct peci_client, dev);
+}
+
+struct peci_device_id {
+	char		name[PECI_NAME_SIZE];
+	unsigned long	driver_data;	/* Data private to the driver */
+};
+
+/**
+ * struct peci_driver - represent a PECI device driver
+ * @probe: callback for device binding
+ * @remove: callback for device unbinding
+ * @shutdown: callback for device shutdown
+ * @driver: device driver model driver
+ * @id_table: list of PECI devices supported by this driver
+ *
+ * The driver.owner field should be set to the module owner of this driver.
+ * The driver.name field should be set to the name of this driver.
+ */
+struct peci_driver {
+	int				(*probe)(struct peci_client *client);
+	int				(*remove)(struct peci_client *client);
+	void				(*shutdown)(struct peci_client *client);
+	struct device_driver		driver;
+	const struct peci_device_id	*id_table;
+};
+
+static inline struct peci_driver *to_peci_driver(void *d)
+{
+	return container_of(d, struct peci_driver, driver);
+}
+
+/**
+ * module_peci_driver - Helper macro for registering a modular PECI driver
+ * @__peci_driver: peci_driver struct
+ *
+ * Helper macro for PECI drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_peci_driver(__peci_driver) \
+	module_driver(__peci_driver, peci_add_driver, peci_del_driver)
+
+/* use a define to avoid include chaining to get THIS_MODULE */
+#define peci_add_driver(driver) peci_register_driver(THIS_MODULE, driver)
+
+int  peci_register_driver(struct module *owner, struct peci_driver *drv);
+void peci_del_driver(struct peci_driver *driver);
+struct peci_client *peci_verify_client(struct device *dev);
+struct peci_adapter *peci_alloc_adapter(struct device *dev, unsigned int size);
+int  peci_add_adapter(struct peci_adapter *adapter);
+void peci_del_adapter(struct peci_adapter *adapter);
+struct peci_adapter *peci_verify_adapter(struct device *dev);
+int  peci_command(struct peci_adapter *adpater, enum peci_cmd cmd, void *vmsg);
+int  peci_get_cpu_id(struct peci_adapter *adapter, u8 addr, u32 *cpu_id);
+
+#endif /* __LINUX_PECI_H */
diff --git a/include/uapi/linux/aspeed-p2a-ctrl.h b/include/uapi/linux/aspeed-p2a-ctrl.h
new file mode 100644
index 0000000..0333555
--- /dev/null
+++ b/include/uapi/linux/aspeed-p2a-ctrl.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Copyright 2019 Google Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Provides a simple driver to control the ASPEED P2A interface which allows
+ * the host to read and write to various regions of the BMC's memory.
+ */
+
+#ifndef _UAPI_LINUX_ASPEED_P2A_CTRL_H
+#define _UAPI_LINUX_ASPEED_P2A_CTRL_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define ASPEED_P2A_CTRL_READ_ONLY 0
+#define ASPEED_P2A_CTRL_READWRITE 1
+
+/*
+ * This driver provides a mechanism for enabling or disabling the read-write
+ * property of specific windows into the ASPEED BMC's memory.
+ *
+ * A user can map a region of the BMC's memory as read-only or read-write, with
+ * the caveat that once any region is mapped, all regions are unlocked for
+ * reading.
+ */
+
+/*
+ * Unlock a region of BMC physical memory for access from the host.
+ *
+ * Also used to read back the optional memory-region configuration for the
+ * driver.
+ */
+struct aspeed_p2a_ctrl_mapping {
+	__u64 addr;
+	__u32 length;
+	__u32 flags;
+};
+
+#define __ASPEED_P2A_CTRL_IOCTL_MAGIC 0xb3
+
+/*
+ * This IOCTL is meant to configure a region or regions of memory given a
+ * starting address and length to be readable by the host, or
+ * readable-writeable.
+ */
+#define ASPEED_P2A_CTRL_IOCTL_SET_WINDOW _IOW(__ASPEED_P2A_CTRL_IOCTL_MAGIC, \
+		0x00, struct aspeed_p2a_ctrl_mapping)
+
+/*
+ * This IOCTL is meant to read back to the user the base address and length of
+ * the memory-region specified to the driver for use with mmap.
+ */
+#define ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG \
+	_IOWR(__ASPEED_P2A_CTRL_IOCTL_MAGIC, \
+		0x01, struct aspeed_p2a_ctrl_mapping)
+
+#endif /* _UAPI_LINUX_ASPEED_P2A_CTRL_H */
diff --git a/include/uapi/linux/peci-ioctl.h b/include/uapi/linux/peci-ioctl.h
new file mode 100644
index 0000000..a6dae71
--- /dev/null
+++ b/include/uapi/linux/peci-ioctl.h
@@ -0,0 +1,403 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018 Intel Corporation */
+
+#ifndef __PECI_IOCTL_H
+#define __PECI_IOCTL_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/* Base Address of 48d */
+#define PECI_BASE_ADDR  0x30  /* The PECI client's default address of 0x30 */
+#define PECI_OFFSET_MAX 8     /* Max numver of CPU clients */
+
+/* PCI Access */
+#define MAX_PCI_READ_LEN 24   /* Number of bytes of the PCI Space read */
+
+#define PCI_BUS0_CPU0      0x00
+#define PCI_BUS0_CPU1      0x80
+#define PCI_CPUBUSNO_BUS   0x00
+#define PCI_CPUBUSNO_DEV   0x08
+#define PCI_CPUBUSNO_FUNC  0x02
+#define PCI_CPUBUSNO       0xcc
+#define PCI_CPUBUSNO_1     0xd0
+#define PCI_CPUBUSNO_VALID 0xd4
+
+/* Package Identifier Read Parameter Value */
+#define PKG_ID_CPU_ID               0x0000  /* CPUID Info */
+#define PKG_ID_PLATFORM_ID          0x0001  /* Platform ID */
+#define PKG_ID_UNCORE_ID            0x0002  /* Uncore Device ID */
+#define PKG_ID_MAX_THREAD_ID        0x0003  /* Max Thread ID */
+#define PKG_ID_MICROCODE_REV        0x0004  /* CPU Microcode Update Revision */
+#define PKG_ID_MACHINE_CHECK_STATUS 0x0005  /* Machine Check Status */
+
+/* RdPkgConfig Index */
+#define MBX_INDEX_CPU_ID            0   /* Package Identifier Read */
+#define MBX_INDEX_VR_DEBUG          1   /* VR Debug */
+#define MBX_INDEX_PKG_TEMP_READ     2   /* Package Temperature Read */
+#define MBX_INDEX_ENERGY_COUNTER    3   /* Energy counter */
+#define MBX_INDEX_ENERGY_STATUS     4   /* DDR Energy Status */
+#define MBX_INDEX_WAKE_MODE_BIT     5   /* "Wake on PECI" Mode bit */
+#define MBX_INDEX_EPI               6   /* Efficient Performance Indication */
+#define MBX_INDEX_PKG_RAPL_PERF     8   /* Pkg RAPL Performance Status Read */
+#define MBX_INDEX_PER_CORE_DTS_TEMP 9   /* Per Core DTS Temperature Read */
+#define MBX_INDEX_DTS_MARGIN        10  /* DTS thermal margin */
+#define MBX_INDEX_SKT_PWR_THRTL_DUR 11  /* Socket Power Throttled Duration */
+#define MBX_INDEX_CFG_TDP_CONTROL   12  /* TDP Config Control */
+#define MBX_INDEX_CFG_TDP_LEVELS    13  /* TDP Config Levels */
+#define MBX_INDEX_DDR_DIMM_TEMP     14  /* DDR DIMM Temperature */
+#define MBX_INDEX_CFG_ICCMAX        15  /* Configurable ICCMAX */
+#define MBX_INDEX_TEMP_TARGET       16  /* Temperature Target Read */
+#define MBX_INDEX_CURR_CFG_LIMIT    17  /* Current Config Limit */
+#define MBX_INDEX_DIMM_TEMP_READ    20  /* Package Thermal Status Read */
+#define MBX_INDEX_DRAM_IMC_TMP_READ 22  /* DRAM IMC Temperature Read */
+#define MBX_INDEX_DDR_CH_THERM_STAT 23  /* DDR Channel Thermal Status */
+#define MBX_INDEX_PKG_POWER_LIMIT1  26  /* Package Power Limit1 */
+#define MBX_INDEX_PKG_POWER_LIMIT2  27  /* Package Power Limit2 */
+#define MBX_INDEX_TDP               28  /* Thermal design power minimum */
+#define MBX_INDEX_TDP_HIGH          29  /* Thermal design power maximum */
+#define MBX_INDEX_TDP_UNITS         30  /* Units for power/energy registers */
+#define MBX_INDEX_RUN_TIME          31  /* Accumulated Run Time */
+#define MBX_INDEX_CONSTRAINED_TIME  32  /* Thermally Constrained Time Read */
+#define MBX_INDEX_TURBO_RATIO       33  /* Turbo Activation Ratio */
+#define MBX_INDEX_DDR_RAPL_PL1      34  /* DDR RAPL PL1 */
+#define MBX_INDEX_DDR_PWR_INFO_HIGH 35  /* DRAM Power Info Read (high) */
+#define MBX_INDEX_DDR_PWR_INFO_LOW  36  /* DRAM Power Info Read (low) */
+#define MBX_INDEX_DDR_RAPL_PL2      37  /* DDR RAPL PL2 */
+#define MBX_INDEX_DDR_RAPL_STATUS   38  /* DDR RAPL Performance Status */
+#define MBX_INDEX_DDR_HOT_ABSOLUTE  43  /* DDR Hottest Dimm Absolute Temp */
+#define MBX_INDEX_DDR_HOT_RELATIVE  44  /* DDR Hottest Dimm Relative Temp */
+#define MBX_INDEX_DDR_THROTTLE_TIME 45  /* DDR Throttle Time */
+#define MBX_INDEX_DDR_THERM_STATUS  46  /* DDR Thermal Status */
+#define MBX_INDEX_TIME_AVG_TEMP     47  /* Package time-averaged temperature */
+#define MBX_INDEX_TURBO_RATIO_LIMIT 49  /* Turbo Ratio Limit Read */
+#define MBX_INDEX_HWP_AUTO_OOB      53  /* HWP Autonomous Out-of-band */
+#define MBX_INDEX_DDR_WARM_BUDGET   55  /* DDR Warm Power Budget */
+#define MBX_INDEX_DDR_HOT_BUDGET    56  /* DDR Hot Power Budget */
+#define MBX_INDEX_PKG_PSYS_PWR_LIM3 57  /* Package/Psys Power Limit3 */
+#define MBX_INDEX_PKG_PSYS_PWR_LIM1 58  /* Package/Psys Power Limit1 */
+#define MBX_INDEX_PKG_PSYS_PWR_LIM2 59  /* Package/Psys Power Limit2 */
+#define MBX_INDEX_PKG_PSYS_PWR_LIM4 60  /* Package/Psys Power Limit4 */
+#define MBX_INDEX_PERF_LIMIT_REASON 65  /* Performance Limit Reasons */
+
+/* WrPkgConfig Index */
+#define MBX_INDEX_DIMM_AMBIENT      19
+#define MBX_INDEX_DIMM_TEMP         24
+
+/* Device Specific Completion Code (CC) Definition */
+#define DEV_PECI_CC_SUCCESS          0x40
+#define DEV_PECI_CC_TIMEOUT          0x80
+#define DEV_PECI_CC_OUT_OF_RESOURCE  0x81
+#define DEV_PECI_CC_UNAVAIL_RESOURCE 0x82
+#define DEV_PECI_CC_INVALID_REQ      0x90
+
+/* Completion Code mask to check retry needs */
+#define DEV_PECI_CC_RETRY_CHECK_MASK 0xf0
+#define DEV_PECI_CC_NEED_RETRY       0x80
+
+/* Skylake EDS says to retry for 250ms */
+#define DEV_PECI_RETRY_TIME_MS       250
+#define DEV_PECI_RETRY_INTERVAL_USEC 10000
+#define DEV_PECI_RETRY_BIT           0x01
+
+#define GET_TEMP_WR_LEN   1
+#define GET_TEMP_RD_LEN   2
+#define GET_TEMP_PECI_CMD 0x01
+
+#define GET_DIB_WR_LEN   1
+#define GET_DIB_RD_LEN   8
+#define GET_DIB_PECI_CMD 0xf7
+
+#define RDPKGCFG_WRITE_LEN     5
+#define RDPKGCFG_READ_LEN_BASE 1
+#define RDPKGCFG_PECI_CMD      0xa1
+
+#define WRPKGCFG_WRITE_LEN_BASE 6
+#define WRPKGCFG_READ_LEN       1
+#define WRPKGCFG_PECI_CMD       0xa5
+
+#define RDIAMSR_WRITE_LEN 5
+#define RDIAMSR_READ_LEN  9
+#define RDIAMSR_PECI_CMD  0xb1
+
+#define WRIAMSR_PECI_CMD  0xb5
+
+#define RDPCICFG_WRITE_LEN 6
+#define RDPCICFG_READ_LEN  5
+#define RDPCICFG_PECI_CMD  0x61
+
+#define WRPCICFG_PECI_CMD  0x65
+
+#define RDPCICFGLOCAL_WRITE_LEN     5
+#define RDPCICFGLOCAL_READ_LEN_BASE 1
+#define RDPCICFGLOCAL_PECI_CMD      0xe1
+
+#define WRPCICFGLOCAL_WRITE_LEN_BASE 6
+#define WRPCICFGLOCAL_READ_LEN       1
+#define WRPCICFGLOCAL_PECI_CMD       0xe5
+
+#define PECI_BUFFER_SIZE 32
+
+/**
+ * enum peci_cmd - PECI client commands
+ * @PECI_CMD_XFER: raw PECI transfer
+ * @PECI_CMD_PING: ping, a required message for all PECI devices
+ * @PECI_CMD_GET_DIB: get DIB (Device Info Byte)
+ * @PECI_CMD_GET_TEMP: get maximum die temperature
+ * @PECI_CMD_RD_PKG_CFG: read access to the PCS (Package Configuration Space)
+ * @PECI_CMD_WR_PKG_CFG: write access to the PCS (Package Configuration Space)
+ * @PECI_CMD_RD_IA_MSR: read access to MSRs (Model Specific Registers)
+ * @PECI_CMD_WR_IA_MSR: write access to MSRs (Model Specific Registers)
+ * @PECI_CMD_RD_PCI_CFG: sideband read access to the PCI configuration space
+ *	maintained in downstream devices external to the processor
+ * @PECI_CMD_WR_PCI_CFG: sideband write access to the PCI configuration space
+ *	maintained in downstream devices external to the processor
+ * @PECI_CMD_RD_PCI_CFG_LOCAL: sideband read access to the PCI configuration
+ *	space that resides within the processor
+ * @PECI_CMD_WR_PCI_CFG_LOCAL: sideband write access to the PCI configuration
+ *	space that resides within the processor
+ *
+ * Available commands depend on client's PECI revision.
+ */
+enum peci_cmd {
+	PECI_CMD_XFER = 0,
+	PECI_CMD_PING,
+	PECI_CMD_GET_DIB,
+	PECI_CMD_GET_TEMP,
+	PECI_CMD_RD_PKG_CFG,
+	PECI_CMD_WR_PKG_CFG,
+	PECI_CMD_RD_IA_MSR,
+	PECI_CMD_WR_IA_MSR,
+	PECI_CMD_RD_PCI_CFG,
+	PECI_CMD_WR_PCI_CFG,
+	PECI_CMD_RD_PCI_CFG_LOCAL,
+	PECI_CMD_WR_PCI_CFG_LOCAL,
+	PECI_CMD_MAX
+};
+
+/**
+ * struct peci_xfer_msg - raw PECI transfer command
+ * @addr; address of the client
+ * @tx_len: number of data to be written in bytes
+ * @rx_len: number of data to be read in bytes
+ * @tx_buf: data to be written, or NULL
+ * @rx_buf: data to be read, or NULL
+ *
+ * raw PECI transfer
+ */
+struct peci_xfer_msg {
+	__u8 addr;
+	__u8 tx_len;
+	__u8 rx_len;
+	__u8 tx_buf[PECI_BUFFER_SIZE];
+	__u8 rx_buf[PECI_BUFFER_SIZE];
+} __attribute__((__packed__));
+
+/**
+ * struct peci_ping_msg - ping command
+ * @addr: address of the client
+ *
+ * Ping() is a required message for all PECI devices. This message is used to
+ * enumerate devices or determine if a device has been removed, been
+ * powered-off, etc.
+ */
+struct peci_ping_msg {
+	__u8 addr;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_get_dib_msg - GetDIB command
+ * @addr: address of the client
+ * @dib: DIB data to be read
+ *
+ * The processor PECI client implementation of GetDIB() includes an 8-byte
+ * response and provides information regarding client revision number and the
+ * number of supported domains. All processor PECI clients support the GetDIB()
+ * command.
+ */
+struct peci_get_dib_msg {
+	__u8  addr;
+	__u64 dib;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_get_temp_msg - GetTemp command
+ * @addr: address of the client
+ * @temp_raw: raw temperature data to be read
+ *
+ * The GetTemp() command is used to retrieve the maximum die temperature from a
+ * target PECI address. The temperature is used by the external thermal
+ * management system to regulate the temperature on the die. The data is
+ * returned as a negative value representing the number of degrees centigrade
+ * below the maximum processor junction temperature.
+ */
+struct peci_get_temp_msg {
+	__u8  addr;
+	__s16 temp_raw;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_rd_pkg_cfg_msg - RdPkgConfig command
+ * @addr: address of the client
+ * @index: encoding index for the requested service
+ * @param: specific data being requested
+ * @rx_len: number of data to be read in bytes
+ * @pkg_config: package config data to be read
+ *
+ * The RdPkgConfig() command provides read access to the Package Configuration
+ * Space (PCS) within the processor, including various power and thermal
+ * management functions. Typical PCS read services supported by the processor
+ * may include access to temperature data, energy status, run time information,
+ * DIMM temperatures and so on.
+ */
+struct peci_rd_pkg_cfg_msg {
+	__u8  addr;
+	__u8  index;
+	__u16 param;
+	__u8  rx_len;
+	__u8  pkg_config[4];
+} __attribute__((__packed__));
+
+/**
+ * struct peci_wr_pkg_cfg_msg - WrPkgConfig command
+ * @addr: address of the client
+ * @index: encoding index for the requested service
+ * @param: specific data being requested
+ * @tx_len: number of data to be written in bytes
+ * @value: package config data to be written
+ *
+ * The WrPkgConfig() command provides write access to the Package Configuration
+ * Space (PCS) within the processor, including various power and thermal
+ * management functions. Typical PCS write services supported by the processor
+ * may include power limiting, thermal averaging constant programming and so on.
+ */
+struct peci_wr_pkg_cfg_msg {
+	__u8  addr;
+	__u8  index;
+	__u16 param;
+	__u8  tx_len;
+	__u32 value;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_rd_ia_msr_msg - RdIAMSR command
+ * @addr: address of the client
+ * @thread_id: ID of the specific logical processor
+ * @address: address of MSR to read from
+ * @value: data to be read
+ *
+ * The RdIAMSR() PECI command provides read access to Model Specific Registers
+ * (MSRs) defined in the processor's Intel Architecture (IA).
+ */
+struct peci_rd_ia_msr_msg {
+	__u8  addr;
+	__u8  thread_id;
+	__u16 address;
+	__u64 value;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_rd_pci_cfg_msg - RdPCIConfig command
+ * @addr: address of the client
+ * @bus: PCI bus number
+ * @device: PCI device number
+ * @function: specific function to read from
+ * @reg: specific register to read from
+ * @pci_config: config data to be read
+ *
+ * The RdPCIConfig() command provides sideband read access to the PCI
+ * configuration space maintained in downstream devices external to the
+ * processor.
+ */
+struct peci_rd_pci_cfg_msg {
+	__u8  addr;
+	__u8  bus;
+	__u8  device;
+	__u8  function;
+	__u16 reg;
+	__u8  pci_config[4];
+} __attribute__((__packed__));
+
+/**
+ * struct peci_rd_pci_cfg_local_msg - RdPCIConfigLocal command
+ * @addr: address of the client
+ * @bus: PCI bus number
+ * @device: PCI device number
+ * @function: specific function to read from
+ * @reg: specific register to read from
+ * @rx_len: number of data to be read in bytes
+ * @pci_config: config data to be read
+ *
+ * The RdPCIConfigLocal() command provides sideband read access to the PCI
+ * configuration space that resides within the processor. This includes all
+ * processor IIO and uncore registers within the PCI configuration space.
+ */
+struct peci_rd_pci_cfg_local_msg {
+	__u8  addr;
+	__u8  bus;
+	__u8  device;
+	__u8  function;
+	__u16 reg;
+	__u8  rx_len;
+	__u8  pci_config[4];
+} __attribute__((__packed__));
+
+/**
+ * struct peci_wr_pci_cfg_local_msg - WrPCIConfigLocal command
+ * @addr: address of the client
+ * @bus: PCI bus number
+ * @device: PCI device number
+ * @function: specific function to read from
+ * @reg: specific register to read from
+ * @tx_len: number of data to be written in bytes
+ * @value: config data to be written
+ *
+ * The WrPCIConfigLocal() command provides sideband write access to the PCI
+ * configuration space that resides within the processor. PECI originators can
+ * access this space even before BIOS enumeration of the system buses.
+ */
+struct peci_wr_pci_cfg_local_msg {
+	__u8  addr;
+	__u8  bus;
+	__u8  device;
+	__u8  function;
+	__u16 reg;
+	__u8  tx_len;
+	__u32 value;
+} __attribute__((__packed__));
+
+#define PECI_IOC_BASE  0xb7
+
+#define PECI_IOC_XFER \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_XFER, struct peci_xfer_msg)
+
+#define PECI_IOC_PING \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_PING, struct peci_ping_msg)
+
+#define PECI_IOC_GET_DIB \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_GET_DIB, struct peci_get_dib_msg)
+
+#define PECI_IOC_GET_TEMP \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_GET_TEMP, struct peci_get_temp_msg)
+
+#define PECI_IOC_RD_PKG_CFG \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_RD_PKG_CFG, struct peci_rd_pkg_cfg_msg)
+
+#define PECI_IOC_WR_PKG_CFG \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_WR_PKG_CFG, struct peci_wr_pkg_cfg_msg)
+
+#define PECI_IOC_RD_IA_MSR \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_RD_IA_MSR, struct peci_rd_ia_msr_msg)
+
+#define PECI_IOC_RD_PCI_CFG \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_RD_PCI_CFG, struct peci_rd_pci_cfg_msg)
+
+#define PECI_IOC_RD_PCI_CFG_LOCAL \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_RD_PCI_CFG_LOCAL, \
+	      struct peci_rd_pci_cfg_local_msg)
+
+#define PECI_IOC_WR_PCI_CFG_LOCAL \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_WR_PCI_CFG_LOCAL, \
+	      struct peci_wr_pci_cfg_local_msg)
+
+#endif /* __PECI_IOCTL_H */