summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-kernel/linux
diff options
context:
space:
mode:
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-add-DTS-for-Intel-platforms.patch473
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0002-Enable-pass-through-on-GPIOE1-and-GPIOE3-free.patch121
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0003-Enable-GPIOE0-and-GPIOE2-pass-through-by-default.patch70
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-128MB-flashmap-for-PFR.patch30
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0006-Allow-monitoring-of-power-control-input-GPIOs.patch80
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-aspeed-pwm-tacho-change-default-fan-speed.patch28
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Report-link-statistics-for-the-NCSI-channel.patch54
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0014-arm-dts-aspeed-g5-add-espi.patch56
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0015-New-flash-map-for-intel.patch117
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0016-Add-ASPEED-SGPIO-driver.patch759
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0017-SGPIO-DT-and-pinctrl-fixup.patch202
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0018-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch5611
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0019-Add-I2C-IPMB-support.patch425
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0020-misc-aspeed-add-lpc-mbox-driver.patch475
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch619
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch667
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0026-Add-support-for-new-PECI-commands.patch724
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch95
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0030-Add-dump-debug-code-into-I2C-drivers.patch145
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0031-Add-high-speed-baud-rate-support-for-UART.patch135
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch556
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-aspeed-Swap-the-mac-nodes-numbering.patch85
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch246
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch514
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch488
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0042-Add-bus-timeout-ms-and-retries-device-tree-propertie.patch105
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0043-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-BT.patch140
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0044-misc-Add-clock-control-logic-into-Aspeed-LPC-SNOOP-d.patch125
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0045-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-KC.patch235
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0047-misc-Block-error-printing-on-probe-defer-case-in-Asp.patch43
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0049-Suppress-excessive-HID-gadget-error-logs.patch52
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0051-Add-AST2500-JTAG-device.patch35
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0052-drivers-jtag-Add-JTAG-core-driver.patch920
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0053-Add-Aspeed-SoC-24xx-and-25xx-families-JTAG.patch1294
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0054-Documentation-jtag-Add-bindings-for-Aspeed-SoC.patch108
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0055-Documentation-jtag-Add-ABI-documentation.patch310
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0056-Documentation-jtag-Add-JTAG-core-driver-ioctl-number.patch57
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0057-drivers-jtag-Add-JTAG-core-driver-Maintainers.patch50
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0060-i2c-aspeed-fix-master-pending-state-handling.patch135
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0061-i2c-aspeed-add-buffer-mode-transfer-support.patch1040
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0062-i2c-aspeed-add-DMA-mode-transfer-support.patch442
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0063-i2c-aspeed-add-general-call-support.patch180
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0064-set-idle-disconnect-to-true-in-all-cases.patch34
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0068-i2c-aspeed-add-H-W-timeout-support.patch191
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0069-i2c-aspeed-add-SLAVE_ADDR_RECEIVED_PENDING-interrupt.patch50
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0070-gpio-aspeed-temporary-fix-for-gpiochip-range-setting.patch44
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0072-pmbus-add-fault-and-beep-attributes.patch88
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0073-Add-IO-statistics-to-USB-Mass-storage-gadget.patch155
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0074-media-aspeed-refine-HSYNC-VSYNC-polarity-setting-log.patch93
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0075-Refine-initialization-flow-in-I2C-driver.patch64
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0076-media-aspeed-clear-garbage-interrupts.patch74
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/intel.cfg74
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend62
53 files changed, 18975 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-add-DTS-for-Intel-platforms.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-add-DTS-for-Intel-platforms.patch
new file mode 100644
index 000000000..d185171f6
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-add-DTS-for-Intel-platforms.patch
@@ -0,0 +1,473 @@
+From 965806acbf8eb3f38367958594f0651e1893ddb8 Mon Sep 17 00:00:00 2001
+From: Yuan Li <yuan.li@linux.intel.com>
+Date: Tue, 19 Sep 2017 15:55:39 +0800
+Subject: [PATCH] arm: dts: add DTS for Intel platforms
+
+Add the DTS file for Intel systems.
+
+Signed-off-by: Yuan Li <yuan.li@linux.intel.com>
+Signed-off-by: Yong Li <yong.b.li@linux.intel.com>
+Signed-off-by: James Feist <james.feist@linux.intel.com>
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
+Signed-off-by: Zhu, Yunge <yunge.zhu@linux.intel.com>
+Signed-off-by: Qiang XU <qiang.xu@linux.intel.com>
+Signed-off-by: Chen Yugang <yugang.chen@linux.intel.com>
+---
+ arch/arm/boot/dts/aspeed-bmc-intel-purley.dts | 444 ++++++++++++++++++++++++++
+ 1 file changed, 444 insertions(+)
+ create mode 100644 arch/arm/boot/dts/aspeed-bmc-intel-purley.dts
+
+diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts b/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts
+new file mode 100644
+index 0000000..b901d98
+--- /dev/null
++++ b/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts
+@@ -0,0 +1,444 @@
++/dts-v1/;
++
++#include "aspeed-g5.dtsi"
++#include <dt-bindings/gpio/aspeed-gpio.h>
++#include <dt-bindings/i2c/i2c.h>
++
++/ {
++ model = "Purley BMC";
++ compatible = "intel,purley-bmc", "aspeed,ast2500";
++
++ aliases {
++ serial4 = &uart5;
++ };
++
++ chosen {
++ stdout-path = &uart5;
++ bootargs = "console=ttyS4,115200 earlyprintk";
++ };
++
++ memory@80000000 {
++ reg = <0x80000000 0x20000000>;
++ };
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++
++ vga_memory: framebuffer@7f000000 {
++ no-map;
++ reg = <0x7f000000 0x01000000>;
++ };
++
++ 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;
++ };
++
++ ramoops@9eff0000{
++ compatible = "ramoops";
++ reg = <0x9eff0000 0x10000>;
++ record-size = <0x2000>;
++ console-size = <0x2000>;
++ };
++ };
++
++ vga-shared-memory {
++ compatible = "aspeed,ast2500-vga-sharedmem";
++ reg = <0x9ff00000 0x100000>;
++ };
++
++ 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 11>,
++ <&adc 12>, <&adc 13>, <&adc 14>, <&adc 15>;
++ };
++
++ leds {
++ compatible = "gpio-leds";
++
++ identify {
++ default-state = "off";
++ gpios = <&gpio ASPEED_GPIO(S, 6) GPIO_ACTIVE_LOW>;
++ };
++
++ status_amber {
++ default-state = "off";
++ gpios = <&gpio ASPEED_GPIO(S, 5) GPIO_ACTIVE_LOW>;
++ };
++
++ status_green {
++ default-state = "keep";
++ gpios = <&gpio ASPEED_GPIO(S, 4) GPIO_ACTIVE_LOW>;
++ };
++ };
++
++ beeper {
++ compatible = "pwm-beeper";
++ pwms = <&timer 5 1000000 0>;
++ };
++};
++
++&fmc {
++ status = "okay";
++ flash@0 {
++ status = "okay";
++ m25p,fast-read;
++#include "openbmc-flash-layout-intel-64MB.dtsi"
++ };
++};
++
++&espi {
++ status = "okay";
++};
++
++&jtag {
++ status = "okay";
++};
++
++&peci0 {
++ status = "okay";
++ gpios = <&gpio ASPEED_GPIO(F, 6) 0>;
++};
++
++&syscon {
++ uart-clock-high-speed;
++ status = "okay";
++};
++
++&adc {
++ status = "okay";
++};
++
++&gpio {
++ status = "okay";
++ gpio-line-names =
++ /*A0-A7*/ "","","","","","","","",
++ /*B0-B7*/ "FM_BMC_BOARD_SKU_ID0_N","FM_BMC_BOARD_SKU_ID1_N","FM_BMC_BOARD_SKU_ID2_N","FM_BMC_BOARD_SKU_ID3_N","FM_BMC_BOARD_SKU_ID4_N","","","",
++ /*C0-C7*/ "","","","","","","","",
++ /*D0-D7*/ "","","","","","","","",
++ /*E0-E7*/ "RESET_BUTTON","RESET_OUT","POWER_BUTTON","POWER_OUT","","DEBUG_EN_N","","",
++ /*F0-F7*/ "NMI_OUT","","","","CPU_ERR0","CPU_ERR1","PLTRST_N","PRDY_N",
++ /*G0-G7*/ "CPU_ERR2","CPU_CATERR","PCH_BMC_THERMTRIP","","","FM_BMC_BOARD_SKU_ID5_N","","",
++ /*H0-H7*/ "","","","","","","","",
++ /*I0-I7*/ "","","","","","","","",
++ /*J0-J7*/ "","","","","","","","",
++ /*K0-K7*/ "","","","","","","","",
++ /*L0-L7*/ "","","","","","","","",
++ /*M0-M7*/ "","","","","","","","",
++ /*N0-N7*/ "","","","","","","","",
++ /*O0-O7*/ "","","","","","","","",
++ /*P0-P7*/ "","","","","","","","",
++ /*Q0-Q7*/ "","","","","","","","PWR_DEBUG_N",
++ /*R0-R7*/ "","XDP_PRST_N","","","","","","",
++ /*S0-S7*/ "","SYSPWROK","RSMRST_N","","","","","",
++ /*T0-T7*/ "","","","","","","","",
++ /*U0-U7*/ "","","","","","","","",
++ /*V0-V7*/ "","","","","","","","",
++ /*W0-W7*/ "","","","","","","","",
++ /*X0-X7*/ "","","","","","","","",
++ /*Y0-Y7*/ "SIO_S3","SIO_S5","","SIO_ONCONTROL","","","","",
++ /*Z0-Z7*/ "","SIO_POWER_GOOD","","","","","","",
++ /*AA0-AA7*/ "P3VBAT_BRIDGE_EN","","","","PREQ_N","TCK_MUX_SEL","SMI","POST_COMPLETE",
++ /*AB0-AB7*/ "","NMI_BUTTON","ID_BUTTON","PS_PWROK","","","","",
++ /*AC0-AC7*/ "","","","","","","","";
++};
++
++&sgpio {
++ status = "okay";
++ gpio-line-names =
++ /* SGPIO output lines */
++ /*OA0-OA7*/ "","","","","","","","",
++ /*OB0-OB7*/ "LED_CPU1_CH1_DIMM1_FAULT","LED_CPU1_CH1_DIMM2_FAULT","LED_CPU1_CH2_DIMM1_FAULT","LED_CPU1_CH2_DIMM2_FAULT","LED_CPU1_CH3_DIMM1_FAULT","LED_CPU1_CH3_DIMM2_FAULT","LED_CPU1_CH4_DIMM1_FAULT","LED_CPU1_CH4_DIMM2_FAULT",
++ /*OC0-OC7*/ "LED_CPU1_CH5_DIMM1_FAULT","LED_CPU1_CH5_DIMM2_FAULT","LED_CPU1_CH6_DIMM1_FAULT","LED_CPU1_CH6_DIMM2_FAULT","LED_FAN1_FAULT","LED_FAN2_FAULT","LED_FAN3_FAULT","LED_FAN4_FAULT",
++ /*OD0-OD7*/ "LED_FAN5_FAULT","LED_FAN6_FAULT","LED_FAN7_FAULT","LED_FAN8_FAULT","LED_CPU2_CH1_DIMM1_FAULT","LED_CPU1_CH1_DIMM2_FAULT","LED_CPU2_CH2_DIMM1_FAULT","LED_CPU2_CH2_DIMM2_FAULT",
++ /*OE0-OE7*/ "LED_CPU2_CH3_DIMM1_FAULT","LED_CPU2_CH3_DIMM2_FAULT","LED_CPU2_CH4_DIMM1_FAULT","LED_CPU2_CH4_DIMM2_FAULT","LED_CPU2_CH5_DIMM1_FAULT","LED_CPU2_CH5_DIMM2_FAULT","LED_CPU2_CH6_DIMM1_FAULT","LED_CPU2_CH6_DIMM2_FAULT",
++ /*OF0-OF7*/ "LED_CPU3_CH1_DIMM1_FAULT","LED_CPU3_CH1_DIMM2_FAULT","LED_CPU3_CH2_DIMM1_FAULT","LED_CPU3_CH2_DIMM2_FAULT","LED_CPU3_CH3_DIMM1_FAULT","LED_CPU3_CH3_DIMM2_FAULT","LED_CPU3_CH4_DIMM1_FAULT","LED_CPU3_CH4_DIMM2_FAULT",
++ /*OG0-OG7*/ "LED_CPU3_CH5_DIMM1_FAULT","LED_CPU3_CH5_DIMM2_FAULT","LED_CPU3_CH6_DIMM1_FAULT","LED_CPU3_CH6_DIMM2_FAULT","LED_CPU4_CH1_DIMM1_FAULT","LED_CPU4_CH1_DIMM2_FAULT","LED_CPU4_CH2_DIMM1_FAULT","LED_CPU4_CH2_DIMM2_FAULT",
++ /*OH0-OH7*/ "LED_CPU4_CH3_DIMM1_FAULT","LED_CPU4_CH3_DIMM2_FAULT","LED_CPU4_CH4_DIMM1_FAULT","LED_CPU4_CH4_DIMM2_FAULT","LED_CPU4_CH5_DIMM1_FAULT","LED_CPU4_CH5_DIMM2_FAULT","LED_CPU4_CH6_DIMM1_FAULT","LED_CPU4_CH6_DIMM2_FAULT",
++ /*OI0-OI7*/ "","","","","","","","",
++ /*OJ0-OJ7*/ "","","","","","","","",
++ /*DUMMY*/ "","","","","","","","",
++ /*DUMMY*/ "","","","","","","","",
++
++ /* SGPIO input lines */
++ /*IA0-IA7*/ "CPU1_PRESENCE","CPU1_THERMTRIP","CPU1_VRHOT","CPU1_FIVR_FAULT","CPU1_MEM_ABCD_VRHOT","CPU1_MEM_EFGH_VRHOT","","",
++ /*IB0-IB7*/ "","","CPU2_PRESENCE","CPU2_THERMTRIP","CPU2_VRHOT","CPU2_FIVR_FAULT","CPU2_MEM_ABCD_VRHOT","CPU2_MEM_EFGH_VRHOT",
++ /*IC0-IC7*/ "","","","","","","","",
++ /*ID0-ID7*/ "","","","","","","","",
++ /*IE0-IE7*/ "","","","","","","","",
++ /*IF0-IF7*/ "","","","","","","","",
++ /*IG0-IG7*/ "","","","","","","","",
++ /*IH0-IH7*/ "","","","","","","","",
++ /*II0-II7*/ "","","","","","","","",
++ /*IJ0-IJ7*/ "","","","","","","","";
++};
++
++&kcs3 {
++ kcs_addr = <0xCA2>;
++ status = "okay";
++};
++
++&kcs4 {
++ kcs_addr = <0xCA4>;
++ status = "okay";
++};
++
++&lpc_sio {
++ status = "okay";
++};
++
++&lpc_snoop {
++ snoop-ports = <0x80>;
++ status = "okay";
++};
++
++&mbox {
++ status = "okay";
++};
++
++/**
++ * SAFS through SPI1 is available only on Wilson Point.
++ * These pins are used as fan presence checking gpios in WFP
++ * so commenting it out for now.
++ * &spi1 {
++ * status = "okay";
++ *
++ * flash@0 {
++ * m25p,fast-read;
++ * status = "okay";
++ * };
++ *};
++ */
++
++&uart1 {
++ status = "okay";
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_txd1_default
++ &pinctrl_rxd1_default
++ &pinctrl_nrts1_default
++ &pinctrl_ndtr1_default
++ &pinctrl_ndsr1_default
++ &pinctrl_ncts1_default
++ &pinctrl_ndcd1_default
++ &pinctrl_nri1_default>;
++};
++
++&uart2 {
++ 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";
++};
++
++&uart4 {
++ status = "okay";
++ pinctrl-names = "default";
++ pinctrl-0 = <>;
++};
++
++&uart5 {
++ status = "okay";
++};
++
++&mac1 {
++ status = "okay";
++
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_rgmii2_default &pinctrl_mdio2_default>;
++};
++
++&mac0 {
++ status = "okay";
++ use-ncsi;
++
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_rmii1_default>;
++};
++
++&i2c0 {
++ multi-master;
++ general-call;
++ aspeed,dma-buf-size = <4095>;
++ aspeed,hw-timeout-ms = <300>;
++ status = "okay";
++};
++
++&i2c1 {
++ multi-master;
++ aspeed,dma-buf-size = <4095>;
++ aspeed,hw-timeout-ms = <300>;
++ status = "okay";
++};
++
++&i2c2 {
++ multi-master;
++ aspeed,dma-buf-size = <4095>;
++ aspeed,hw-timeout-ms = <300>;
++ status = "okay";
++};
++
++&i2c3 {
++ multi-master;
++ aspeed,dma-buf-size = <4095>;
++ aspeed,hw-timeout-ms = <300>;
++ status = "okay";
++};
++
++&i2c4 {
++ multi-master;
++ aspeed,dma-buf-size = <4095>;
++ aspeed,hw-timeout-ms = <300>;
++ status = "okay";
++
++ hsbp0@10 {
++ compatible = "slave-mqueue";
++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>;
++ };
++};
++
++&i2c5 {
++ bus-frequency = <1000000>;
++ multi-master;
++ aspeed,dma-buf-size = <4095>;
++ aspeed,hw-timeout-ms = <300>;
++ status = "okay";
++
++ smlink0@10 {
++ compatible = "slave-mqueue";
++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>;
++ };
++};
++
++&i2c6 {
++ multi-master;
++ aspeed,dma-buf-size = <4095>;
++ aspeed,hw-timeout-ms = <300>;
++ status = "okay";
++};
++
++&i2c7 {
++ multi-master;
++ #retries = <3>;
++ aspeed,dma-buf-size = <4095>;
++ aspeed,hw-timeout-ms = <300>;
++ status = "okay";
++};
++
++&i2c9 {
++ multi-master;
++ aspeed,dma-buf-size = <4095>;
++ aspeed,hw-timeout-ms = <300>;
++ status = "okay";
++};
++
++&i2c11 {
++ multi-master;
++ aspeed,dma-buf-size = <4095>;
++ aspeed,hw-timeout-ms = <300>;
++ status = "okay";
++};
++
++&i2c13 {
++ multi-master;
++ aspeed,dma-buf-size = <4095>;
++ aspeed,hw-timeout-ms = <300>;
++ status = "okay";
++};
++
++&gfx {
++ status = "okay";
++ memory-region = <&gfx_memory>;
++};
++
++&vuart {
++ status = "okay";
++};
++
++&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 &pinctrl_pwm7_default>;
++
++ fan@0 {
++ reg = <0x00>;
++ aspeed,fan-tach-ch = /bits/ 8 <0x00 0x01>;
++ };
++ fan@1 {
++ reg = <0x01>;
++ aspeed,fan-tach-ch = /bits/ 8 <0x02 0x03>;
++ };
++ fan@2 {
++ reg = <0x02>;
++ aspeed,fan-tach-ch = /bits/ 8 <0x04 0x05>;
++ };
++ fan@3 {
++ reg = <0x03>;
++ aspeed,fan-tach-ch = /bits/ 8 <0x06 0x07>;
++ };
++ fan@4 {
++ reg = <0x04>;
++ aspeed,fan-tach-ch = /bits/ 8 <0x08 0x09>;
++ };
++ fan@5 {
++ reg = <0x05>;
++ aspeed,fan-tach-ch = /bits/ 8 <0x0A 0x0B>;
++ };
++ fan@6 {
++ reg = <0x06>;
++ aspeed,fan-tach-ch = /bits/ 8 <0x0C 0x0D>;
++ };
++ fan@7 {
++ reg = <0x07>;
++ aspeed,fan-tach-ch = /bits/ 8 <0x0E 0x0F>;
++ };
++
++};
++
++&timer {
++/*
++ * Available settings:
++ * fttmr010,pwm-outputs = <5>, <6>, <7>, <8>;
++ * pinctrl-0 = <&pinctrl_timer5_default &pinctrl_timer6_default
++ * &pinctrl_timer7_default &pinctrl_timer8_default>;
++ */
++ fttmr010,pwm-outputs = <5>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_timer5_default>;
++ #pwm-cells = <3>;
++ status = "okay";
++};
++
++&video {
++ status = "okay";
++ memory-region = <&video_engine_memory>;
++};
++
++&vhub {
++ status = "okay";
++};
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0002-Enable-pass-through-on-GPIOE1-and-GPIOE3-free.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0002-Enable-pass-through-on-GPIOE1-and-GPIOE3-free.patch
new file mode 100644
index 000000000..ecee21f1c
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0002-Enable-pass-through-on-GPIOE1-and-GPIOE3-free.patch
@@ -0,0 +1,121 @@
+From ae2bcda6000d7ec278ea78d1eda6e8aacbe5a741 Mon Sep 17 00:00:00 2001
+From: "Jason M. Bills" <jason.m.bills@linux.intel.com>
+Date: Fri, 3 May 2019 16:12:39 -0700
+Subject: [PATCH] Enable pass-through on GPIOE1 and GPIOE3 free
+
+This change adds a gpio_disable_free() implementation that checks
+if the GPIO being freed is GPIOE1 (33) or GPIOE3 (35) and will
+re-enable the pass-through mux.
+
+Tested:
+Requested GPIOs 33 and 35 and used devmem to check that pass-through
+was disabled. Then freed them and checked that pass-through was
+enabled again.
+
+Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
+---
+ drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c | 1 +
+ drivers/pinctrl/aspeed/pinctrl-aspeed.c | 60 ++++++++++++++++++++++++++++++
+ drivers/pinctrl/aspeed/pinctrl-aspeed.h | 3 ++
+ 3 files changed, 64 insertions(+)
+
+diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
+index d8a804b9f958..5e7f53fab76e 100644
+--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
+@@ -2805,6 +2805,7 @@ static const struct pinmux_ops aspeed_g5_pinmux_ops = {
+ .get_function_groups = aspeed_pinmux_get_fn_groups,
+ .set_mux = aspeed_pinmux_set_mux,
+ .gpio_request_enable = aspeed_gpio_request_enable,
++ .gpio_disable_free = aspeed_gpio_disable_free,
+ .strict = true,
+ };
+
+diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.c b/drivers/pinctrl/aspeed/pinctrl-aspeed.c
+index 54933665b5f8..aa7d56e99824 100644
+--- a/drivers/pinctrl/aspeed/pinctrl-aspeed.c
++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.c
+@@ -356,6 +356,66 @@ int aspeed_gpio_request_enable(struct pinctrl_dev *pctldev,
+ return aspeed_sig_expr_enable(&pdata->pinmux, expr);
+ }
+
++void aspeed_gpio_disable_free(struct pinctrl_dev *pctldev,
++ struct pinctrl_gpio_range *range,
++ unsigned int offset)
++{
++ const struct aspeed_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev);
++ const struct aspeed_pin_desc *pdesc = pdata->pins[offset].drv_data;
++ const struct aspeed_sig_expr ***prios, **funcs, *expr;
++ int ret;
++
++ /*
++ * If we're freeing GPIOE1 (33) or GPIOE3 (35) then re-enable the
++ * pass-through mux setting; otherwise, do nothing.
++ */
++ if (offset != 33 && offset != 35)
++ return;
++
++ dev_dbg(pctldev->dev,
++ "Freeing pass-through pin %s (%d). Re-enabling pass-through.\n",
++ pdesc->name, offset);
++
++ if (!pdesc)
++ return;
++
++ prios = pdesc->prios;
++
++ if (!prios)
++ return;
++
++ /* Disable any functions of higher priority than GPIO just in case */
++ while ((funcs = *prios)) {
++ if (aspeed_gpio_in_exprs(funcs))
++ break;
++
++ ret = aspeed_disable_sig(&pdata->pinmux, funcs);
++ if (ret)
++ return;
++
++ prios++;
++ }
++
++ if (!funcs) {
++ char *signals = get_defined_signals(pdesc);
++
++ pr_warn("No GPIO signal type found on pin %s (%d). Found: %s\n",
++ pdesc->name, offset, signals);
++ kfree(signals);
++
++ return;
++ }
++
++ /*
++ * Pass-through should be one priority higher than the GPIO function,
++ * so decrement our prios and enable that function
++ */
++ prios--;
++ funcs = *prios;
++ expr = *funcs;
++ aspeed_sig_expr_enable(&pdata->pinmux, expr);
++}
++
+ int aspeed_pinctrl_probe(struct platform_device *pdev,
+ struct pinctrl_desc *pdesc,
+ struct aspeed_pinctrl_data *pdata)
+diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.h b/drivers/pinctrl/aspeed/pinctrl-aspeed.h
+index a5d83986f32e..c1104341e202 100644
+--- a/drivers/pinctrl/aspeed/pinctrl-aspeed.h
++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.h
+@@ -67,6 +67,9 @@ int aspeed_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int function,
+ int aspeed_gpio_request_enable(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned int offset);
++void aspeed_gpio_disable_free(struct pinctrl_dev *pctldev,
++ struct pinctrl_gpio_range *range,
++ unsigned int offset);
+ int aspeed_pinctrl_probe(struct platform_device *pdev,
+ struct pinctrl_desc *pdesc,
+ struct aspeed_pinctrl_data *pdata);
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0003-Enable-GPIOE0-and-GPIOE2-pass-through-by-default.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0003-Enable-GPIOE0-and-GPIOE2-pass-through-by-default.patch
new file mode 100644
index 000000000..a5bd4d08e
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0003-Enable-GPIOE0-and-GPIOE2-pass-through-by-default.patch
@@ -0,0 +1,70 @@
+From fa75e700680ea87ab5aa9722bde1667c572f2fe8 Mon Sep 17 00:00:00 2001
+From: "Jason M. Bills" <jason.m.bills@linux.intel.com>
+Date: Mon, 6 May 2019 14:18:27 -0700
+Subject: [PATCH] Enable GPIOE0 and GPIOE2 pass-through by default
+
+This change sets the gpio DT pinctrl default configuration to
+enable GPIOE0 and GPIOE2 pass-through. Since this causes
+pinctrl_get_select_default() to be called automatically for
+the gpio driver to claim the GPIO pins in those groups, we
+also need to call pinctrl_put() to release claim on the
+pass-through GPIOs so they can be requested at runtime.
+
+Tested:
+Disabled pass-through in uboot and confirmed that after booting
+Linux, pass-through is enabled and 'cat /sys/kernel/debug/pinctrl/
+1e6e2000.syscon\:pinctrl-aspeed-g5-pinctrl/pinmux-pins' shows that
+the pass-through GPIOs are UNCLAIMED.
+
+Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
+---
+ arch/arm/boot/dts/aspeed-bmc-intel-purley.dts | 4 ++++
+ drivers/gpio/gpio-aspeed.c | 10 ++++++++++
+ 2 files changed, 14 insertions(+)
+
+diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts b/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts
+index 040b345f55e1..bc6bb41a8e68 100644
+--- a/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts
++++ b/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts
+@@ -117,6 +117,10 @@
+
+ &gpio {
+ status = "okay";
++ /* Enable GPIOE0 and GPIOE2 pass-through by default */
++ pinctrl-names = "pass-through";
++ pinctrl-0 = <&pinctrl_gpie0_default
++ &pinctrl_gpie2_default>;
+ gpio-line-names =
+ /*A0-A7*/ "","","","","","","","",
+ /*B0-B7*/ "","","","","","","","",
+diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
+index 09e53c5f3b0a..ac33f8134fe6 100644
+--- a/drivers/gpio/gpio-aspeed.c
++++ b/drivers/gpio/gpio-aspeed.c
+@@ -1140,6 +1140,7 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
+ {
+ const struct of_device_id *gpio_id;
+ struct aspeed_gpio *gpio;
++ struct pinctrl *pinctrl;
+ int rc, i, banks, err;
+ u32 ngpio;
+
+@@ -1190,6 +1191,15 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
+ return -ENOMEM;
+
+ /*
++ * Select the pass-through pinctrl config to enable the pass-through
++ * mux for GPIOs E0 and E2. Then call pinctrl_put() to release claim
++ * of the GPIO pins, so they can be requested at runtime.
++ */
++ pinctrl = pinctrl_get_select(&pdev->dev, "pass-through");
++ if (pinctrl)
++ pinctrl_put(pinctrl);
++
++ /*
+ * Populate it with initial values read from the HW and switch
+ * all command sources to the ARM by default
+ */
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-128MB-flashmap-for-PFR.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-128MB-flashmap-for-PFR.patch
new file mode 100644
index 000000000..14b7154bf
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-128MB-flashmap-for-PFR.patch
@@ -0,0 +1,30 @@
+From 9a71adc7aecbfdf066ba54c763c2ecd8fb09d3cd Mon Sep 17 00:00:00 2001
+From: Vikram Bodireddy <vikram.bodireddy@intel.com>
+Date: Wed, 6 Feb 2019 15:59:34 +0530
+Subject: [PATCH] Selecting 128MB for PFR
+
+PFR platforms requires 128MB flash mapping.
+This will override the existing 64MB flash map
+and loads 128MB flash map.
+
+Signed-off-by: Vikram Bodireddy <vikram.bodireddy@intel.com>
+---
+ arch/arm/boot/dts/aspeed-bmc-intel-purley.dts | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts b/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts
+index 4815104459f1..df02bb1aaf36 100644
+--- a/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts
++++ b/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts
+@@ -89,7 +89,7 @@
+ flash@0 {
+ status = "okay";
+ m25p,fast-read;
+-#include "openbmc-flash-layout-intel-64MB.dtsi"
++#include "openbmc-flash-layout-intel-128MB.dtsi"
+ };
+ };
+
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0006-Allow-monitoring-of-power-control-input-GPIOs.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0006-Allow-monitoring-of-power-control-input-GPIOs.patch
new file mode 100644
index 000000000..c84746359
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0006-Allow-monitoring-of-power-control-input-GPIOs.patch
@@ -0,0 +1,80 @@
+From e9d15bf9fdec1cd17c2ed335566b7d463d63fbdb Mon Sep 17 00:00:00 2001
+From: "Jason M. Bills" <jason.m.bills@linux.intel.com>
+Date: Fri, 24 May 2019 12:42:59 -0700
+Subject: [PATCH] Allow monitoring of power control input GPIOs
+
+The pass-through input GPIOs cannot be monitored because when
+requested, pass-through is disabled which causes a change on the
+pass-through output.
+
+The SIO GPIOs cannot be monitored because when requested, the
+request is rejected based on the value of the ACPI strap.
+
+This change removes the register check condition from the pass-
+through and desired SIO GPIOs so they can be requsted and
+monitored from power control.
+
+Tested:
+For pass-through, I used gpioset to hold a request on the input
+GPIOs and confirmed that pass-through remained enabled.
+
+For SIO, I used gpioget to confirm that I can successfully request
+and read the GPIO value.
+
+Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
+---
+ drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
+index 5e7f53fab76e..b08b5325edb1 100644
+--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
+@@ -279,7 +279,7 @@ FUNC_GROUP_DECL(SD2, F19, E21, F20, D20, D21, E20, G18, C21);
+
+ #define B20 32
+ SIG_EXPR_LIST_DECL_SINGLE(B20, NCTS3, NCTS3, SIG_DESC_SET(SCU80, 16));
+-SIG_EXPR_DECL_SINGLE(GPIE0IN, GPIE0, GPIE0_DESC);
++SIG_EXPR_DECL_SINGLE(GPIE0IN, GPIE0);
+ SIG_EXPR_DECL_SINGLE(GPIE0IN, GPIE, GPIE_DESC);
+ SIG_EXPR_LIST_DECL_DUAL(B20, GPIE0IN, GPIE0, GPIE);
+ PIN_DECL_2(B20, GPIOE0, NCTS3, GPIE0IN);
+@@ -299,7 +299,7 @@ FUNC_GROUP_DECL(GPIE0, B20, C20);
+
+ #define F18 34
+ SIG_EXPR_LIST_DECL_SINGLE(F18, NDSR3, NDSR3, SIG_DESC_SET(SCU80, 18));
+-SIG_EXPR_DECL_SINGLE(GPIE2IN, GPIE2, GPIE2_DESC);
++SIG_EXPR_DECL_SINGLE(GPIE2IN, GPIE2);
+ SIG_EXPR_DECL_SINGLE(GPIE2IN, GPIE, GPIE_DESC);
+ SIG_EXPR_LIST_DECL_DUAL(F18, GPIE2IN, GPIE2, GPIE);
+ PIN_DECL_2(F18, GPIOE2, NDSR3, GPIE2IN);
+@@ -1412,7 +1412,7 @@ FUNC_GROUP_DECL(ADC15, H4);
+
+ #define R22 192
+ SIG_EXPR_DECL_SINGLE(SIOS3, SIOS3, SIG_DESC_SET(SCUA4, 8));
+-SIG_EXPR_DECL_SINGLE(SIOS3, ACPI, ACPI_DESC);
++SIG_EXPR_DECL_SINGLE(SIOS3, ACPI);
+ SIG_EXPR_LIST_DECL_DUAL(R22, SIOS3, SIOS3, ACPI);
+ SIG_EXPR_LIST_DECL_SINGLE(R22, DASHR22, DASHR22, SIG_DESC_SET(SCU94, 10));
+ PIN_DECL_2(R22, GPIOY0, SIOS3, DASHR22);
+@@ -1420,7 +1420,7 @@ FUNC_GROUP_DECL(SIOS3, R22);
+
+ #define R21 193
+ SIG_EXPR_DECL_SINGLE(SIOS5, SIOS5, SIG_DESC_SET(SCUA4, 9));
+-SIG_EXPR_DECL_SINGLE(SIOS5, ACPI, ACPI_DESC);
++SIG_EXPR_DECL_SINGLE(SIOS5, ACPI);
+ SIG_EXPR_LIST_DECL_DUAL(R21, SIOS5, SIOS5, ACPI);
+ SIG_EXPR_LIST_DECL_SINGLE(R21, DASHR21, DASHR21, SIG_DESC_SET(SCU94, 10));
+ PIN_DECL_2(R21, GPIOY1, SIOS5, DASHR21);
+@@ -1436,7 +1436,7 @@ FUNC_GROUP_DECL(SIOPWREQ, P22);
+
+ #define P21 195
+ SIG_EXPR_DECL_SINGLE(SIOONCTRL, SIOONCTRL, SIG_DESC_SET(SCUA4, 11));
+-SIG_EXPR_DECL_SINGLE(SIOONCTRL, ACPI, ACPI_DESC);
++SIG_EXPR_DECL_SINGLE(SIOONCTRL, ACPI);
+ SIG_EXPR_LIST_DECL_DUAL(P21, SIOONCTRL, SIOONCTRL, ACPI);
+ SIG_EXPR_LIST_DECL_SINGLE(P21, DASHP21, DASHP21, SIG_DESC_SET(SCU94, 11));
+ PIN_DECL_2(P21, GPIOY3, SIOONCTRL, DASHP21);
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-aspeed-pwm-tacho-change-default-fan-speed.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-aspeed-pwm-tacho-change-default-fan-speed.patch
new file mode 100644
index 000000000..476a07043
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-aspeed-pwm-tacho-change-default-fan-speed.patch
@@ -0,0 +1,28 @@
+From b7e8941cf3b1c1c42330207600c91fb23781a77b Mon Sep 17 00:00:00 2001
+From: James Feist <james.feist@linux.intel.com>
+Date: Tue, 2 Jul 2019 10:14:59 -0700
+Subject: [PATCH] aspeed-pwm-tacho: change default fan speed
+
+Change it from max to 58%
+
+Signed-off-by: James Feist <james.feist@linux.intel.com>
+---
+ drivers/hwmon/aspeed-pwm-tacho.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c
+index 40c489be62ea..ae5771f881b5 100644
+--- a/drivers/hwmon/aspeed-pwm-tacho.c
++++ b/drivers/hwmon/aspeed-pwm-tacho.c
+@@ -160,7 +160,7 @@
+ */
+ #define M_TACH_MODE 0x02 /* 10b */
+ #define M_TACH_UNIT 0x0210
+-#define INIT_FAN_CTRL 0xFF
++#define INIT_FAN_CTRL 150 /* 58% */
+
+ /* How long we sleep in us while waiting for an RPM result. */
+ #define ASPEED_RPM_STATUS_SLEEP_USEC 500
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Report-link-statistics-for-the-NCSI-channel.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Report-link-statistics-for-the-NCSI-channel.patch
new file mode 100644
index 000000000..643deb659
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Report-link-statistics-for-the-NCSI-channel.patch
@@ -0,0 +1,54 @@
+From f3300099b6638df5829e75b1fbfbb6e7ebc8b2b9 Mon Sep 17 00:00:00 2001
+From: Johnathan Mantey <johnathanx.mantey@intel.com>
+Date: Thu, 1 Aug 2019 11:29:41 -0700
+Subject: [PATCH] Report link statistics for the NCSI channel
+
+The ftgmac driver does not report the link statistics for the NCSI
+channel used for the shared NICs attached to the BMC. Report a fixed
+value for the NSCI interface.
+
+Change-Id: Idb65ca1ce07f06a883417ee44df30ea2c8483107
+Signed-off-by: Johnathan Mantey <johnathanx.mantey@intel.com>
+---
+ drivers/net/ethernet/faraday/ftgmac100.c | 22 +++++++++++++++++++++-
+ 1 file changed, 21 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
+index 9b7af94a40bb..4cd679233795 100644
+--- a/drivers/net/ethernet/faraday/ftgmac100.c
++++ b/drivers/net/ethernet/faraday/ftgmac100.c
+@@ -1216,10 +1216,30 @@ static int ftgmac100_set_pauseparam(struct net_device *netdev,
+ return 0;
+ }
+
++int ftgmac100_ethtool_get_link_ksettings(struct net_device *netdev,
++ struct ethtool_link_ksettings *cmd)
++{
++ struct phy_device *phydev = netdev->phydev;
++ struct ftgmac100 *priv = netdev_priv(netdev);
++ int retval = 0;
++
++ if (phydev) {
++ phy_ethtool_ksettings_get(phydev, cmd);
++ } else if (priv->use_ncsi) {
++ cmd->base.speed = SPEED_100;
++ cmd->base.duplex = DUPLEX_FULL;
++ cmd->base.autoneg = 0;
++ } else {
++ retval = -ENODEV;
++ }
++
++ return retval;
++}
++
+ static const struct ethtool_ops ftgmac100_ethtool_ops = {
+ .get_drvinfo = ftgmac100_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+- .get_link_ksettings = phy_ethtool_get_link_ksettings,
++ .get_link_ksettings = ftgmac100_ethtool_get_link_ksettings,
+ .set_link_ksettings = phy_ethtool_set_link_ksettings,
+ .nway_reset = phy_ethtool_nway_reset,
+ .get_ringparam = ftgmac100_get_ringparam,
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0014-arm-dts-aspeed-g5-add-espi.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0014-arm-dts-aspeed-g5-add-espi.patch
new file mode 100644
index 000000000..094fc8396
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0014-arm-dts-aspeed-g5-add-espi.patch
@@ -0,0 +1,56 @@
+From b70fe24abeef901b3ba8e32b5e5d8aaf35ec061d Mon Sep 17 00:00:00 2001
+From: Juston Li <juston.li@intel.com>
+Date: Mon, 27 Mar 2017 11:16:00 -0700
+Subject: [PATCH] arm: dts: aspeed-g5: add espi
+
+Signed-off-by: Juston Li <juston.li@intel.com>
+---
+ arch/arm/boot/dts/aspeed-g5.dtsi | 18 +++++++++++++++++-
+ 1 file changed, 17 insertions(+), 1 deletion(-)
+
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index 00f05bd3375d..271f3c96456a 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -311,7 +311,7 @@
+ #gpio-cells = <2>;
+ gpio-controller;
+ compatible = "aspeed,ast2500-gpio";
+- reg = <0x1e780000 0x1000>;
++ reg = <0x1e780000 0x0200>;
+ interrupts = <20>;
+ gpio-ranges = <&pinctrl 0 0 232>;
+ clocks = <&syscon ASPEED_CLK_APB>;
+@@ -319,6 +319,15 @@
+ #interrupt-cells = <2>;
+ };
+
++ sgpio: sgpio@1e780200 {
++ #gpio-cells = <2>;
++ gpio-controller;
++ compatible = "aspeed,ast2500-sgpio";
++ reg = <0x1e780200 0x0100>;
++ interrupts = <40>;
++ interrupt-controller;
++ };
++
+ rtc: rtc@1e781000 {
+ compatible = "aspeed,ast2500-rtc";
+ reg = <0x1e781000 0x18>;
+@@ -394,6 +403,13 @@
+ status = "disabled";
+ };
+
++ espi: espi@1e6ee000 {
++ compatible = "aspeed,ast2500-espi-slave";
++ reg = <0x1e6ee000 0x100>;
++ interrupts = <23>;
++ status = "disabled";
++ };
++
+ lpc: lpc@1e789000 {
+ compatible = "aspeed,ast2500-lpc", "simple-mfd";
+ reg = <0x1e789000 0x1000>;
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0015-New-flash-map-for-intel.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0015-New-flash-map-for-intel.patch
new file mode 100644
index 000000000..695491d28
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0015-New-flash-map-for-intel.patch
@@ -0,0 +1,117 @@
+From f57d473a30f208754457bdb63512c307f7499ac8 Mon Sep 17 00:00:00 2001
+From: Vernon Mauery <vernon.mauery@intel.com>
+Date: Mon, 4 Jun 2018 13:45:42 -0700
+Subject: [PATCH] New flash map for Intel
+
+Signed-off-by: Vernon Mauery <vernon.mauery@intel.com>
+Signed-off-by: Vikram Bodireddy <vikram.bodireddy@intel.com>
+---
+ .../boot/dts/openbmc-flash-layout-intel-128MB.dtsi | 50 ++++++++++++++++++++++
+ .../boot/dts/openbmc-flash-layout-intel-64MB.dtsi | 38 ++++++++++++++++
+ 2 files changed, 88 insertions(+)
+ create mode 100644 arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi
+ create mode 100644 arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi
+
+diff --git a/arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi b/arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi
+new file mode 100644
+index 000000000000..0d3794423aed
+--- /dev/null
++++ b/arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi
+@@ -0,0 +1,50 @@
++// SPDX-License-Identifier: GPL-2.0+
++// 128MB flash layout: PFR (active + tmp1/tmp2 + extra)
++// image with common RW partition
++
++partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ u-boot@0 {
++ reg = <0x0 0x80000>;
++ label = "u-boot";
++ };
++
++ pfm@80000 {
++ reg = <0x80000 0x20000>;
++ label = "pfm";
++ };
++
++ u-boot-env@a0000 {
++ reg = <0xa0000 0x20000>;
++ label = "u-boot-env";
++ };
++
++ sofs@c0000 {
++ reg = <0xc0000 0x200000>;
++ label = "sofs";
++ };
++
++ rwfs@2c0000 {
++ reg = <0x2c0000 0x840000>;
++ label = "rwfs";
++ };
++
++ fit-image-a@b00000 {
++ reg = <0xb00000 0x1f00000>;
++ label = "image-a";
++ };
++
++ rc-image@2a00000 {
++ reg = <0x2a00000 0x2000000>;
++ label = "rc-image";
++ };
++
++ image-staging@4a00000 {
++ reg = <0x4a00000 0x3600000>;
++ label = "image-stg";
++ };
++
++};
+diff --git a/arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi b/arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi
+new file mode 100644
+index 000000000000..092708f5021f
+--- /dev/null
++++ b/arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi
+@@ -0,0 +1,38 @@
++// SPDX-License-Identifier: GPL-2.0+
++// 64MB flash layout: redundant image with common RW partition
++
++partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ u-boot@0 {
++ reg = <0x0 0x80000>;
++ label = "u-boot";
++ };
++
++ fit-image-a@80000 {
++ reg = <0x80000 0x1b80000>;
++ label = "image-a";
++ };
++
++ sofs@1c00000 {
++ reg = <0x1c00000 0x200000>;
++ label = "sofs";
++ };
++
++ rwfs@1e00000 {
++ reg = <0x1e00000 0x600000>;
++ label = "rwfs";
++ };
++
++ u-boot-env@2400000 {
++ reg = <0x2400000 0x20000>;
++ label = "u-boot-env";
++ };
++
++ fit-image-b@2480000 {
++ reg = <0x2480000 0x1b80000>;
++ label = "image-b";
++ };
++};
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0016-Add-ASPEED-SGPIO-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0016-Add-ASPEED-SGPIO-driver.patch
new file mode 100644
index 000000000..07bdf60af
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0016-Add-ASPEED-SGPIO-driver.patch
@@ -0,0 +1,759 @@
+From ab104c6067683a3a251e2814991474243b7e1cb8 Mon Sep 17 00:00:00 2001
+From: "Feist, James" <james.feist@intel.com>
+Date: Tue, 4 Jun 2019 14:00:39 -0700
+Subject: [PATCH] gpio: aspeed: add ASPEED SGPIO driver
+
+Add SGPIO driver support for Aspeed SoCs.
+
+Signed-off-by: James Feist <james.feist@linux.intel.com>
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/gpio/Kconfig | 8 +
+ drivers/gpio/Makefile | 1 +
+ drivers/gpio/sgpio-aspeed.c | 703 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 712 insertions(+)
+ create mode 100644 drivers/gpio/sgpio-aspeed.c
+
+diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
+index bb13c266c329..4061686d8651 100644
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -120,6 +120,14 @@ config GPIO_ASPEED
+ help
+ Say Y here to support Aspeed AST2400 and AST2500 GPIO controllers.
+
++config SGPIO_ASPEED
++ tristate "ASPEED SGPIO support"
++ depends on (ARCH_ASPEED || COMPILE_TEST) && OF_GPIO
++ select GPIO_GENERIC
++ select GPIOLIB_IRQCHIP
++ help
++ Say Y here to support ASPEED SGPIO functionality.
++
+ config GPIO_ATH79
+ tristate "Atheros AR71XX/AR724X/AR913X GPIO support"
+ default y if ATH79
+diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
+index a4e91175c708..bebbd8205c11 100644
+--- a/drivers/gpio/Makefile
++++ b/drivers/gpio/Makefile
+@@ -32,6 +32,7 @@ obj-$(CONFIG_GPIO_AMD_FCH) += gpio-amd-fch.o
+ obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o
+ obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o
+ obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o
++obj-$(CONFIG_SGPIO_ASPEED) += sgpio-aspeed.o
+ obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o
+ obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
+ obj-$(CONFIG_GPIO_BD70528) += gpio-bd70528.o
+diff --git a/drivers/gpio/sgpio-aspeed.c b/drivers/gpio/sgpio-aspeed.c
+new file mode 100644
+index 000000000000..b6e9ccee774d
+--- /dev/null
++++ b/drivers/gpio/sgpio-aspeed.c
+@@ -0,0 +1,703 @@
++// SPDX-License-Identifier: GPL-2.0+
++// Copyright (c) 2019 Intel Corporation
++
++#include <linux/bitfield.h>
++#include <linux/clk.h>
++#include <linux/gpio/driver.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/spinlock.h>
++
++#define ASPEED_SGPIO_CTRL 0x54
++#define ASPEED_SGPIO_CLK_DIV_MASK GENMASK(31, 16)
++#define ASPEED_SGPIO_CLK_DIV_MIN 1
++#define ASPEED_SGPIO_CLK_DIV_MAX 65535
++#define ASPEED_SGPIO_PINBYTES_MASK GENMASK(9, 6)
++#define ASPEED_SGPIO_PINBYTES_MIN 1
++#define ASPEED_SGPIO_PINBYTES_MAX 10
++#define ASPEED_SGPIO_ENABLE BIT(0)
++
++#define ASPEED_SGPIO_BUS_FREQ_DEFAULT 1000000
++
++struct aspeed_bank_props {
++ unsigned int bank;
++ u32 input;
++ u32 output;
++};
++
++struct aspeed_sgpio_config {
++ unsigned int nr_pgpios;
++ unsigned int nr_gpios;
++ const struct aspeed_bank_props *props;
++};
++
++struct aspeed_sgpio {
++ struct gpio_chip chip;
++ struct irq_chip irqc;
++ spinlock_t lock;
++ void __iomem *base;
++ int irq;
++ const struct aspeed_sgpio_config *config;
++};
++
++struct aspeed_sgpio_bank {
++ uint16_t val_reg;
++ uint16_t rdata_reg;
++ uint16_t tolerance_reg;
++ uint16_t irq_regs;
++ bool support_irq;
++ const char names[4][3];
++};
++
++/*
++ * Note: The "val" register returns the input value sampled on the line.
++ * Or, it can be used for writing a value on the line.
++ *
++ * The "rdata" register returns the content of the write latch and thus
++ * can be used to read back what was last written reliably.
++ */
++
++static const struct aspeed_sgpio_bank aspeed_sgpio_banks[] = {
++ {
++ .val_reg = 0x0000,
++ .rdata_reg = 0x0070,
++ .tolerance_reg = 0x0018,
++ .irq_regs = 0x0004,
++ .support_irq = false,
++ .names = { "OA", "OB", "OC", "OD" },
++ },
++ {
++ .val_reg = 0x001C,
++ .rdata_reg = 0x0074,
++ .tolerance_reg = 0x0034,
++ .irq_regs = 0x0020,
++ .support_irq = false,
++ .names = { "OE", "OF", "OG", "OH" },
++ },
++ {
++ .val_reg = 0x0038,
++ .rdata_reg = 0x0078,
++ .tolerance_reg = 0x0050,
++ .irq_regs = 0x003C,
++ .support_irq = false,
++ .names = { "OI", "OJ" },
++ },
++ {
++ .val_reg = 0x0000,
++ .rdata_reg = 0x0070,
++ .tolerance_reg = 0x0018,
++ .irq_regs = 0x0004,
++ .support_irq = true,
++ .names = { "IA", "IB", "IC", "ID" },
++ },
++ {
++ .val_reg = 0x001C,
++ .rdata_reg = 0x0074,
++ .tolerance_reg = 0x0034,
++ .irq_regs = 0x0020,
++ .support_irq = true,
++ .names = { "IE", "IF", "IG", "IH" },
++ },
++ {
++ .val_reg = 0x0038,
++ .rdata_reg = 0x0078,
++ .tolerance_reg = 0x0050,
++ .irq_regs = 0x003C,
++ .support_irq = true,
++ .names = { "II", "IJ" },
++ },
++};
++
++enum aspeed_sgpio_reg {
++ reg_val,
++ reg_rdata,
++ reg_irq_enable,
++ reg_irq_type0,
++ reg_irq_type1,
++ reg_irq_type2,
++ reg_irq_status,
++ reg_tolerance,
++};
++
++#define GPIO_IRQ_ENABLE 0x00
++#define GPIO_IRQ_TYPE0 0x04
++#define GPIO_IRQ_TYPE1 0x08
++#define GPIO_IRQ_TYPE2 0x0c
++#define GPIO_IRQ_STATUS 0x10
++
++/* This will be resolved at compile time */
++static inline void __iomem *bank_reg(struct aspeed_sgpio *gpio,
++ const struct aspeed_sgpio_bank *bank,
++ const enum aspeed_sgpio_reg reg)
++{
++ switch (reg) {
++ case reg_val:
++ return gpio->base + bank->val_reg;
++ case reg_rdata:
++ return gpio->base + bank->rdata_reg;
++ case reg_irq_enable:
++ return gpio->base + bank->irq_regs + GPIO_IRQ_ENABLE;
++ case reg_irq_type0:
++ return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE0;
++ case reg_irq_type1:
++ return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE1;
++ case reg_irq_type2:
++ return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE2;
++ case reg_irq_status:
++ return gpio->base + bank->irq_regs + GPIO_IRQ_STATUS;
++ case reg_tolerance:
++ return gpio->base + bank->tolerance_reg;
++ default:
++ WARN_ON(1);
++ }
++
++ return NULL;
++}
++
++#define GPIO_BANK(x) ((x) >> 5)
++#define GPIO_OFFSET(x) ((x) & 0x1f)
++#define GPIO_BIT(x) BIT(GPIO_OFFSET(x))
++
++static const struct aspeed_sgpio_bank *to_bank(unsigned int offset)
++{
++ unsigned int bank = GPIO_BANK(offset);
++
++ WARN_ON(bank >= ARRAY_SIZE(aspeed_sgpio_banks));
++ return &aspeed_sgpio_banks[bank];
++}
++
++static inline bool is_bank_props_sentinel(const struct aspeed_bank_props *props)
++{
++ return !(props->input || props->output);
++}
++
++static inline const struct aspeed_bank_props *find_bank_props(
++ struct aspeed_sgpio *gpio, unsigned int offset)
++{
++ const struct aspeed_bank_props *props = gpio->config->props;
++
++ while (!is_bank_props_sentinel(props)) {
++ if (props->bank == GPIO_BANK(offset))
++ return props;
++ props++;
++ }
++
++ return NULL;
++}
++
++static inline bool have_input(struct aspeed_sgpio *gpio, unsigned int offset)
++{
++ const struct aspeed_bank_props *props = find_bank_props(gpio, offset);
++
++ return !props || (props->input & GPIO_BIT(offset));
++}
++
++static inline bool have_output(struct aspeed_sgpio *gpio, unsigned int offset)
++{
++ const struct aspeed_bank_props *props = find_bank_props(gpio, offset);
++
++ return !props || (props->output & GPIO_BIT(offset));
++}
++
++static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset)
++{
++ struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
++ const struct aspeed_sgpio_bank *bank = to_bank(offset);
++ enum aspeed_sgpio_reg reg;
++
++ if (have_output(gpio, offset))
++ reg = reg_rdata;
++ else
++ reg = reg_val;
++
++ return !!(ioread32(bank_reg(gpio, bank, reg)) & GPIO_BIT(offset));
++}
++
++static void aspeed_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val)
++{
++ const struct aspeed_sgpio_bank *bank = to_bank(offset);
++ struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
++ unsigned long flags;
++ u32 reg;
++
++ if (!have_output(gpio, offset))
++ return;
++
++ spin_lock_irqsave(&gpio->lock, flags);
++
++ reg = ioread32(bank_reg(gpio, bank, reg_rdata));
++
++ if (val)
++ reg |= GPIO_BIT(offset);
++ else
++ reg &= ~GPIO_BIT(offset);
++
++ iowrite32(reg, bank_reg(gpio, bank, reg_val));
++
++ spin_unlock_irqrestore(&gpio->lock, flags);
++}
++
++static int aspeed_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset)
++{
++ struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
++
++ if (!have_input(gpio, offset))
++ return -ENOTSUPP;
++
++ return 0;
++}
++
++static int aspeed_sgpio_dir_out(struct gpio_chip *gc, unsigned int offset,
++ int val)
++{
++ struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
++
++ if (!have_output(gpio, offset))
++ return -ENOTSUPP;
++
++ aspeed_sgpio_set(gc, offset, val);
++
++ return 0;
++}
++
++static int aspeed_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset)
++{
++ struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
++
++ if (have_output(gpio, offset))
++ return 0;
++ else if (have_input(gpio, offset))
++ return 1;
++
++ return -ENOTSUPP;
++}
++
++static inline int
++irqd_to_aspeed_sgpio_data(struct irq_data *d, struct aspeed_sgpio **gpio,
++ const struct aspeed_sgpio_bank **bank,
++ u32 *bit, int *offset)
++{
++ struct aspeed_sgpio *internal;
++
++ *offset = irqd_to_hwirq(d);
++
++ internal = irq_data_get_irq_chip_data(d);
++
++ *gpio = internal;
++ *bank = to_bank(*offset);
++ *bit = GPIO_BIT(*offset);
++
++ return 0;
++}
++
++static void aspeed_sgpio_irq_ack(struct irq_data *d)
++{
++ const struct aspeed_sgpio_bank *bank;
++ struct aspeed_sgpio *gpio;
++ void __iomem *status_addr;
++ unsigned long flags;
++ int rc, offset;
++ u32 bit;
++
++ rc = irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
++ if (rc)
++ return;
++
++ status_addr = bank_reg(gpio, bank, reg_irq_status);
++
++ spin_lock_irqsave(&gpio->lock, flags);
++
++ iowrite32(bit, status_addr);
++
++ spin_unlock_irqrestore(&gpio->lock, flags);
++}
++
++static void aspeed_sgpio_irq_set_mask(struct irq_data *d, bool set)
++{
++ const struct aspeed_sgpio_bank *bank;
++ struct aspeed_sgpio *gpio;
++ unsigned long flags;
++ u32 reg, bit;
++ void __iomem *addr;
++ int rc, offset;
++
++ rc = irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
++ if (rc)
++ return;
++
++ if (!bank->support_irq)
++ return;
++
++ addr = bank_reg(gpio, bank, reg_irq_enable);
++
++ spin_lock_irqsave(&gpio->lock, flags);
++
++ reg = ioread32(addr);
++ if (set)
++ reg |= bit;
++ else
++ reg &= ~bit;
++
++ iowrite32(reg, addr);
++
++ spin_unlock_irqrestore(&gpio->lock, flags);
++}
++
++static void aspeed_sgpio_irq_mask(struct irq_data *d)
++{
++ aspeed_sgpio_irq_set_mask(d, false);
++}
++
++static void aspeed_sgpio_irq_unmask(struct irq_data *d)
++{
++ aspeed_sgpio_irq_set_mask(d, true);
++}
++
++static int aspeed_sgpio_set_type(struct irq_data *d, unsigned int type)
++{
++ u32 type0 = 0;
++ u32 type1 = 0;
++ u32 type2 = 0;
++ u32 bit, reg;
++ const struct aspeed_sgpio_bank *bank;
++ irq_flow_handler_t handler;
++ struct aspeed_sgpio *gpio;
++ unsigned long flags;
++ void __iomem *addr;
++ int rc, offset;
++
++ rc = irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
++ if (rc)
++ return -EINVAL;
++
++ if (!bank->support_irq)
++ return -ENOTSUPP;
++
++ switch (type & IRQ_TYPE_SENSE_MASK) {
++ case IRQ_TYPE_EDGE_BOTH:
++ type2 |= bit;
++ /* fall through */
++ case IRQ_TYPE_EDGE_RISING:
++ type0 |= bit;
++ /* fall through */
++ case IRQ_TYPE_EDGE_FALLING:
++ handler = handle_edge_irq;
++ break;
++ case IRQ_TYPE_LEVEL_HIGH:
++ type0 |= bit;
++ /* fall through */
++ case IRQ_TYPE_LEVEL_LOW:
++ type1 |= bit;
++ handler = handle_level_irq;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ spin_lock_irqsave(&gpio->lock, flags);
++
++ addr = bank_reg(gpio, bank, reg_irq_type0);
++ reg = ioread32(addr);
++ reg = (reg & ~bit) | type0;
++ iowrite32(reg, addr);
++
++ addr = bank_reg(gpio, bank, reg_irq_type1);
++ reg = ioread32(addr);
++ reg = (reg & ~bit) | type1;
++ iowrite32(reg, addr);
++
++ addr = bank_reg(gpio, bank, reg_irq_type2);
++ reg = ioread32(addr);
++ reg = (reg & ~bit) | type2;
++ iowrite32(reg, addr);
++
++ spin_unlock_irqrestore(&gpio->lock, flags);
++
++ irq_set_handler_locked(d, handler);
++
++ return 0;
++}
++
++static void aspeed_sgpio_irq_handler(struct irq_desc *desc)
++{
++ struct gpio_chip *gc = irq_desc_get_handler_data(desc);
++ struct aspeed_sgpio *data = gpiochip_get_data(gc);
++ struct irq_chip *ic = irq_desc_get_chip(desc);
++ unsigned int i, p, girq;
++ unsigned long reg;
++
++ chained_irq_enter(ic, desc);
++
++ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
++ const struct aspeed_sgpio_bank *bank = &aspeed_sgpio_banks[i];
++
++ if (!bank->support_irq)
++ continue;
++
++ reg = ioread32(bank_reg(data, bank, reg_irq_status));
++
++ for_each_set_bit(p, &reg, 32) {
++ girq = irq_find_mapping(gc->irq.domain, i * 32 + p);
++ generic_handle_irq(girq);
++ }
++ }
++
++ chained_irq_exit(ic, desc);
++}
++
++static void aspeed_sgpio_init_irq_valid_mask(struct gpio_chip *gc,
++ unsigned long *valid_mask,
++ unsigned int ngpios)
++{
++ struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
++ const struct aspeed_bank_props *props = gpio->config->props;
++
++ while (!is_bank_props_sentinel(props)) {
++ unsigned int offset;
++ const unsigned long int input = props->input;
++
++ /* Pretty crummy approach, but similar to GPIO core */
++ for_each_clear_bit(offset, &input, 32) {
++ unsigned int i = props->bank * 32 + offset;
++
++ if (i >= gpio->chip.ngpio)
++ break;
++
++ clear_bit(i, valid_mask);
++ }
++
++ props++;
++ }
++}
++
++static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio,
++ struct platform_device *pdev)
++{
++ const struct aspeed_sgpio_bank *bank;
++ struct gpio_irq_chip *girq;
++ int rc, i;
++
++ /* Initialize IRQ and tolerant settings */
++ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
++ bank = &aspeed_sgpio_banks[i];
++
++ /* Value will be reset by WDT reset */
++ iowrite32(0x00000000, bank_reg(gpio, bank, reg_tolerance));
++
++ if (!bank->support_irq)
++ continue;
++
++ /* disable irq enable bits */
++ iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_enable));
++ /* clear status bits */
++ iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_status));
++ /* set rising or level-high irq */
++ iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_type0));
++ /* trigger type is level */
++ iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_type1));
++ /* single trigger mode */
++ iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type2));
++ }
++
++ rc = platform_get_irq(pdev, 0);
++ if (rc < 0)
++ return rc;
++
++ gpio->irq = rc;
++ girq = &gpio->chip.irq;
++ girq->chip = &gpio->irqc;
++ girq->chip->name = dev_name(&pdev->dev);
++ girq->chip->irq_ack = aspeed_sgpio_irq_ack;
++ girq->chip->irq_mask = aspeed_sgpio_irq_mask;
++ girq->chip->irq_unmask = aspeed_sgpio_irq_unmask;
++ girq->chip->irq_set_type = aspeed_sgpio_set_type;
++ girq->parent_handler = aspeed_sgpio_irq_handler;
++ girq->num_parents = 1;
++ girq->parents = devm_kcalloc(&pdev->dev, 1,
++ sizeof(*girq->parents),
++ GFP_KERNEL);
++ if (!girq->parents)
++ return -ENOMEM;
++ girq->parents[0] = gpio->irq;
++ girq->default_type = IRQ_TYPE_NONE;
++ girq->handler = handle_bad_irq;
++ girq->init_valid_mask = aspeed_sgpio_init_irq_valid_mask;
++
++ return 0;
++}
++
++static int aspeed_sgpio_reset_tolerance(struct gpio_chip *chip,
++ unsigned int offset, bool enable)
++{
++ struct aspeed_sgpio *gpio = gpiochip_get_data(chip);
++ unsigned long flags;
++ void __iomem *treg;
++ u32 val;
++
++ treg = bank_reg(gpio, to_bank(offset), reg_tolerance);
++
++ spin_lock_irqsave(&gpio->lock, flags);
++
++ val = readl(treg);
++
++ if (enable)
++ val |= GPIO_BIT(offset);
++ else
++ val &= ~GPIO_BIT(offset);
++
++ writel(val, treg);
++
++ spin_unlock_irqrestore(&gpio->lock, flags);
++
++ return 0;
++}
++
++static int aspeed_sgpio_set_config(struct gpio_chip *chip, unsigned int offset,
++ unsigned long config)
++{
++ unsigned long param = pinconf_to_config_param(config);
++ u32 arg = pinconf_to_config_argument(config);
++
++ if (param == PIN_CONFIG_PERSIST_STATE)
++ return aspeed_sgpio_reset_tolerance(chip, offset, arg);
++
++ return -ENOTSUPP;
++}
++
++/*
++ * Any banks not specified in a struct aspeed_bank_props array are assumed to
++ * have the properties:
++ *
++ * { .input = 0xffffffff, .output = 0xffffffff }
++ */
++
++static const struct aspeed_bank_props ast_sgpio_bank_props[] = {
++ /* input output */
++ { 0, 0x00000000, 0xffffffff }, /* OA/OB/OC/OD */
++ { 1, 0x00000000, 0xffffffff }, /* OE/OF/OG/OH */
++ { 2, 0x00000000, 0x0000ffff }, /* OI/OJ */
++ { 3, 0xffffffff, 0x00000000 }, /* IA/IB/IC/ID */
++ { 4, 0xffffffff, 0x00000000 }, /* IE/IF/IG/IH */
++ { 5, 0x0000ffff, 0x00000000 }, /* II/IJ */
++ { }
++};
++
++/*
++ * This H/W has 80 bidirectional lines so this driver provides total 160 lines
++ * for 80 outputs and 80 inputs. To simplify bank register manipulation, it
++ * uses 96 lines per each input and output set so total 192 lines it has.
++ */
++static const struct aspeed_sgpio_config ast2400_config =
++ { .nr_pgpios = 224, .nr_gpios = 192, .props = ast_sgpio_bank_props };
++
++static const struct aspeed_sgpio_config ast2500_config =
++ { .nr_pgpios = 232, .nr_gpios = 192, .props = ast_sgpio_bank_props };
++
++static const struct of_device_id aspeed_sgpio_of_table[] = {
++ { .compatible = "aspeed,ast2400-sgpio", .data = &ast2400_config },
++ { .compatible = "aspeed,ast2500-sgpio", .data = &ast2500_config },
++ { }
++};
++MODULE_DEVICE_TABLE(of, aspeed_sgpio_of_table);
++
++static int __init aspeed_sgpio_probe(struct platform_device *pdev)
++{
++ const struct of_device_id *gpio_id;
++ u32 sgpio_freq, clk_div, nb_gpios;
++ struct aspeed_sgpio *gpio;
++ unsigned long src_freq;
++ struct clk *clk;
++ int rc;
++
++ gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
++ if (!gpio)
++ return -ENOMEM;
++
++ gpio->base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(gpio->base))
++ return PTR_ERR(gpio->base);
++
++ spin_lock_init(&gpio->lock);
++
++ gpio_id = of_match_node(aspeed_sgpio_of_table, pdev->dev.of_node);
++ if (!gpio_id)
++ return -EINVAL;
++
++ gpio->config = gpio_id->data;
++
++ rc = device_property_read_u32(&pdev->dev, "bus-frequency", &sgpio_freq);
++ if (rc < 0) {
++ dev_warn(&pdev->dev, "Could not read bus-frequency property. Use default.\n");
++ sgpio_freq = ASPEED_SGPIO_BUS_FREQ_DEFAULT;
++ }
++
++ clk = devm_clk_get(&pdev->dev, NULL);
++ if (IS_ERR(clk)) {
++ rc = PTR_ERR(clk);
++ if (rc != -EPROBE_DEFER)
++ dev_err(&pdev->dev, "Failed to get clk source.\n");
++ return rc;
++ }
++
++ /*
++ * There is a limitation that SGPIO clock division has to be larger or
++ * equal to 1. And a read back value of clock division is 1-bit left
++ * shifted from the actual value.
++ *
++ * GPIO254[31:16] - Serial GPIO clock division:
++ * Serial GPIO clock period = period of PCLK * 2 * (GPIO254[31:16] + 1)
++ *
++ * SGPIO master controller updates every data input when SGPMLD is low.
++ * For an example, SGPIO clock is 1MHz and number of SGPIO is 80. Each
++ * SGPIO will be updated every 80us.
++ */
++ src_freq = clk_get_rate(clk);
++ clk_div = src_freq / (2 * sgpio_freq) - 1;
++ if (clk_div < ASPEED_SGPIO_CLK_DIV_MIN)
++ clk_div = ASPEED_SGPIO_CLK_DIV_MIN;
++ else if (clk_div > ASPEED_SGPIO_CLK_DIV_MAX)
++ clk_div = ASPEED_SGPIO_CLK_DIV_MAX;
++
++ nb_gpios = gpio->config->nr_gpios / 16;
++ if (nb_gpios < ASPEED_SGPIO_PINBYTES_MIN)
++ nb_gpios = ASPEED_SGPIO_PINBYTES_MIN;
++ else if (nb_gpios > ASPEED_SGPIO_PINBYTES_MAX)
++ nb_gpios = ASPEED_SGPIO_PINBYTES_MAX;
++
++ iowrite32(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, clk_div) |
++ FIELD_PREP(ASPEED_SGPIO_PINBYTES_MASK, nb_gpios) |
++ ASPEED_SGPIO_ENABLE,
++ gpio->base + ASPEED_SGPIO_CTRL);
++
++ gpio->chip.parent = &pdev->dev;
++ gpio->chip.ngpio = gpio->config->nr_gpios;
++
++ gpio->chip.direction_input = aspeed_sgpio_dir_in;
++ gpio->chip.direction_output = aspeed_sgpio_dir_out;
++ gpio->chip.get_direction = aspeed_sgpio_get_direction;
++ gpio->chip.get = aspeed_sgpio_get;
++ gpio->chip.set = aspeed_sgpio_set;
++ gpio->chip.set_config = aspeed_sgpio_set_config;
++ gpio->chip.label = dev_name(&pdev->dev);
++ gpio->chip.base = -1;
++
++ rc = aspeed_sgpio_setup_irqs(gpio, pdev);
++ if (rc < 0)
++ return rc;
++
++ return devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
++}
++
++static struct platform_driver aspeed_sgpio_driver = {
++ .driver = {
++ .name = KBUILD_MODNAME,
++ .of_match_table = aspeed_sgpio_of_table,
++ },
++};
++
++module_platform_driver_probe(aspeed_sgpio_driver, aspeed_sgpio_probe);
++
++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
++MODULE_DESCRIPTION("Aspeed SGPIO Master Driver");
++MODULE_LICENSE("GPL v2");
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0017-SGPIO-DT-and-pinctrl-fixup.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0017-SGPIO-DT-and-pinctrl-fixup.patch
new file mode 100644
index 000000000..6bfcb43c4
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0017-SGPIO-DT-and-pinctrl-fixup.patch
@@ -0,0 +1,202 @@
+From 4c5ab7c103b693096ae719abd16bc80b81043beb Mon Sep 17 00:00:00 2001
+From: Vernon Mauery <vernon.mauery@intel.com>
+Date: Wed, 16 May 2018 10:03:14 -0700
+Subject: [PATCH] SGPIO DT and pinctrl fixup
+
+This commit fixes DT and pinctrl for SGPIO use.
+
+Signed-off-by: Vernon Mauery <vernon.mauery@intel.com>
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ arch/arm/boot/dts/aspeed-g4.dtsi | 56 +++++++++++-------------------
+ arch/arm/boot/dts/aspeed-g5.dtsi | 5 +++
+ drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c | 48 ++++++++++++-------------
+ 3 files changed, 49 insertions(+), 60 deletions(-)
+
+diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
+index cc78564b2f8d..ee86b41af291 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -255,6 +255,20 @@
+ #interrupt-cells = <2>;
+ };
+
++ sgpio: sgpio@1e780200 {
++ #gpio-cells = <2>;
++ gpio-controller;
++ compatible = "aspeed,ast2400-sgpio";
++ reg = <0x1e780200 0x0100>;
++ interrupts = <40>;
++ interrupt-controller;
++ clocks = <&syscon ASPEED_CLK_APB>;
++ bus-frequency = <1000000>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_sgpm_default>;
++ status = "disabled";
++ };
++
+ timer: timer@1e782000 {
+ /* This timer is a Faraday FTTMR010 derivative */
+ compatible = "aspeed,ast2400-timer";
+@@ -1228,44 +1242,14 @@
+ groups = "SD2";
+ };
+
+- pinctrl_sgpmck_default: sgpmck_default {
+- function = "SGPMCK";
+- groups = "SGPMCK";
+- };
+-
+- pinctrl_sgpmi_default: sgpmi_default {
+- function = "SGPMI";
+- groups = "SGPMI";
+- };
+-
+- pinctrl_sgpmld_default: sgpmld_default {
+- function = "SGPMLD";
+- groups = "SGPMLD";
+- };
+-
+- pinctrl_sgpmo_default: sgpmo_default {
+- function = "SGPMO";
+- groups = "SGPMO";
+- };
+-
+- pinctrl_sgpsck_default: sgpsck_default {
+- function = "SGPSCK";
+- groups = "SGPSCK";
+- };
+-
+- pinctrl_sgpsi0_default: sgpsi0_default {
+- function = "SGPSI0";
+- groups = "SGPSI0";
+- };
+-
+- pinctrl_sgpsi1_default: sgpsi1_default {
+- function = "SGPSI1";
+- groups = "SGPSI1";
++ pinctrl_sgpm_default: sgpm_default {
++ function = "SGPM";
++ groups = "SGPM";
+ };
+
+- pinctrl_sgpsld_default: sgpsld_default {
+- function = "SGPSLD";
+- groups = "SGPSLD";
++ pinctrl_sgps_default: sgps_default {
++ function = "SGPS";
++ groups = "SGPS";
+ };
+
+ pinctrl_sioonctrl_default: sioonctrl_default {
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index 271f3c96456a..128e0b5bbae2 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -326,6 +326,11 @@
+ reg = <0x1e780200 0x0100>;
+ interrupts = <40>;
+ interrupt-controller;
++ clocks = <&syscon ASPEED_CLK_APB>;
++ bus-frequency = <1000000>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_sgpm_default>;
++ status = "disabled";
+ };
+
+ rtc: rtc@1e781000 {
+diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c
+index 95ea593fa29d..70284c5f9ad9 100644
+--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c
++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c
+@@ -430,16 +430,22 @@ SSSF_PIN_DECL(E16, GPIOF6, TXD4, SIG_DESC_SET(SCU80, 30));
+ SSSF_PIN_DECL(C17, GPIOF7, RXD4, SIG_DESC_SET(SCU80, 31));
+
+ #define A14 48
+-SSSF_PIN_DECL(A14, GPIOG0, SGPSCK, SIG_DESC_SET(SCU84, 0));
++SIG_EXPR_LIST_DECL_SINGLE(A14, SGPSCK, SGPS, SIG_DESC_SET(SCU84, 0));
++PIN_DECL_1(A14, GPIOG0, SGPSCK);
+
+ #define E13 49
+-SSSF_PIN_DECL(E13, GPIOG1, SGPSLD, SIG_DESC_SET(SCU84, 1));
++SIG_EXPR_LIST_DECL_SINGLE(E13, SGPSLD, SGPS, SIG_DESC_SET(SCU84, 1));
++PIN_DECL_1(E13, GPIOG1, SGPSLD);
+
+ #define D13 50
+-SSSF_PIN_DECL(D13, GPIOG2, SGPSI0, SIG_DESC_SET(SCU84, 2));
++SIG_EXPR_LIST_DECL_SINGLE(D13, SGPSIO, SGPS, SIG_DESC_SET(SCU84, 2));
++PIN_DECL_1(D13, GPIOG2, SGPSIO);
+
+ #define C13 51
+-SSSF_PIN_DECL(C13, GPIOG3, SGPSI1, SIG_DESC_SET(SCU84, 3));
++SIG_EXPR_LIST_DECL_SINGLE(C13, SGPSI1, SGPS, SIG_DESC_SET(SCU84, 3));
++PIN_DECL_1(C13, GPIOG3, SGPSI1);
++
++FUNC_GROUP_DECL(SGPS, A14, E13, D13, C13);
+
+ #define B13 52
+ SIG_EXPR_LIST_DECL_SINGLE(B13, OSCCLK, OSCCLK, SIG_DESC_SET(SCU2C, 1));
+@@ -613,16 +619,22 @@ FUNC_GROUP_DECL(SPI1PASSTHRU, C22, G18, D19, C20, B22, G19, C18, E20);
+ FUNC_GROUP_DECL(VGABIOS_ROM, B22, G19, C18, E20);
+
+ #define J5 72
+-SSSF_PIN_DECL(J5, GPIOJ0, SGPMCK, SIG_DESC_SET(SCU84, 8));
++SIG_EXPR_LIST_DECL_SINGLE(J5, SGPMCK, SGPM, SIG_DESC_SET(SCU84, 8));
++PIN_DECL_1(J5, GPIOJ0, SGPMCK);
+
+ #define J4 73
+-SSSF_PIN_DECL(J4, GPIOJ1, SGPMLD, SIG_DESC_SET(SCU84, 9));
++SIG_EXPR_LIST_DECL_SINGLE(J4, SGPMLD, SGPM, SIG_DESC_SET(SCU84, 9));
++PIN_DECL_1(J4, GPIOJ1, SGPMLD);
+
+ #define K5 74
+-SSSF_PIN_DECL(K5, GPIOJ2, SGPMO, SIG_DESC_SET(SCU84, 10));
++SIG_EXPR_LIST_DECL_SINGLE(K5, SGPMO, SGPM, SIG_DESC_SET(SCU84, 10));
++PIN_DECL_1(K5, GPIOJ2, SGPMO);
+
+ #define J3 75
+-SSSF_PIN_DECL(J3, GPIOJ3, SGPMI, SIG_DESC_SET(SCU84, 11));
++SIG_EXPR_LIST_DECL_SINGLE(J3, SGPMI, SGPM, SIG_DESC_SET(SCU84, 11));
++PIN_DECL_1(J3, GPIOJ3, SGPMI);
++
++FUNC_GROUP_DECL(SGPM, J5, J4, K5, J3);
+
+ #define T4 76
+ SSSF_PIN_DECL(T4, GPIOJ4, VGAHS, SIG_DESC_SET(SCU84, 12));
+@@ -2234,14 +2246,8 @@ static const struct aspeed_pin_group aspeed_g4_groups[] = {
+ ASPEED_PINCTRL_GROUP(SALT4),
+ ASPEED_PINCTRL_GROUP(SD1),
+ ASPEED_PINCTRL_GROUP(SD2),
+- ASPEED_PINCTRL_GROUP(SGPMCK),
+- ASPEED_PINCTRL_GROUP(SGPMI),
+- ASPEED_PINCTRL_GROUP(SGPMLD),
+- ASPEED_PINCTRL_GROUP(SGPMO),
+- ASPEED_PINCTRL_GROUP(SGPSCK),
+- ASPEED_PINCTRL_GROUP(SGPSI0),
+- ASPEED_PINCTRL_GROUP(SGPSI1),
+- ASPEED_PINCTRL_GROUP(SGPSLD),
++ ASPEED_PINCTRL_GROUP(SGPM),
++ ASPEED_PINCTRL_GROUP(SGPS),
+ ASPEED_PINCTRL_GROUP(SIOONCTRL),
+ ASPEED_PINCTRL_GROUP(SIOPBI),
+ ASPEED_PINCTRL_GROUP(SIOPBO),
+@@ -2389,14 +2395,8 @@ static const struct aspeed_pin_function aspeed_g4_functions[] = {
+ ASPEED_PINCTRL_FUNC(SALT4),
+ ASPEED_PINCTRL_FUNC(SD1),
+ ASPEED_PINCTRL_FUNC(SD2),
+- ASPEED_PINCTRL_FUNC(SGPMCK),
+- ASPEED_PINCTRL_FUNC(SGPMI),
+- ASPEED_PINCTRL_FUNC(SGPMLD),
+- ASPEED_PINCTRL_FUNC(SGPMO),
+- ASPEED_PINCTRL_FUNC(SGPSCK),
+- ASPEED_PINCTRL_FUNC(SGPSI0),
+- ASPEED_PINCTRL_FUNC(SGPSI1),
+- ASPEED_PINCTRL_FUNC(SGPSLD),
++ ASPEED_PINCTRL_FUNC(SGPM),
++ ASPEED_PINCTRL_FUNC(SGPS),
+ ASPEED_PINCTRL_FUNC(SIOONCTRL),
+ ASPEED_PINCTRL_FUNC(SIOPBI),
+ ASPEED_PINCTRL_FUNC(SIOPBO),
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0018-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0018-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch
new file mode 100644
index 000000000..77e413125
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0018-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch
@@ -0,0 +1,5611 @@
+From edeea958f026102ce28c8b760f7a96b9ffd7f65a Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Mon, 7 Jan 2019 09:56:10 -0800
+Subject: [PATCH] Update PECI drivers to sync with linux upstreaming version
+
+Upstreaming is in holding. It's for adding DTS sensor with PECI
+subsystem code update.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
+---
+ Documentation/hwmon/peci-cputemp | 34 +-
+ drivers/hwmon/Kconfig | 4 +-
+ drivers/hwmon/peci-cputemp.c | 171 +++---
+ drivers/hwmon/peci-dimmtemp.c | 184 +++++--
+ drivers/hwmon/peci-hwmon.h | 9 +-
+ drivers/mfd/Kconfig | 5 +-
+ drivers/mfd/intel-peci-client.c | 51 +-
+ drivers/peci/Kconfig | 46 +-
+ drivers/peci/Makefile | 7 +-
+ drivers/peci/busses/Kconfig | 32 ++
+ drivers/peci/busses/Makefile | 7 +
+ drivers/peci/busses/peci-aspeed.c | 492 +++++++++++++++++
+ drivers/peci/busses/peci-npcm.c | 410 +++++++++++++++
+ drivers/peci/peci-aspeed.c | 505 ------------------
+ drivers/peci/peci-core.c | 959 +++++++++++++++++++---------------
+ drivers/peci/peci-dev.c | 346 ++++++++++++
+ drivers/peci/peci-npcm.c | 410 ---------------
+ include/linux/mfd/intel-peci-client.h | 31 +-
+ include/linux/peci.h | 30 +-
+ include/uapi/linux/peci-ioctl.h | 416 +++++++++------
+ 20 files changed, 2446 insertions(+), 1703 deletions(-)
+ create mode 100644 drivers/peci/busses/Kconfig
+ create mode 100644 drivers/peci/busses/Makefile
+ create mode 100644 drivers/peci/busses/peci-aspeed.c
+ create mode 100644 drivers/peci/busses/peci-npcm.c
+ delete mode 100644 drivers/peci/peci-aspeed.c
+ create mode 100644 drivers/peci/peci-dev.c
+ delete mode 100644 drivers/peci/peci-npcm.c
+
+diff --git a/Documentation/hwmon/peci-cputemp b/Documentation/hwmon/peci-cputemp
+index 821a925..a3a3e46 100644
+--- a/Documentation/hwmon/peci-cputemp
++++ b/Documentation/hwmon/peci-cputemp
+@@ -51,28 +51,38 @@ temp1_crit Provides shutdown temperature of the CPU package which
+ 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
++temp2_label "DTS"
++temp2_input Provides current DTS temperature of the CPU package.
++temp2_max Provides thermal control temperature of the CPU package
++ which is also known as Tcontrol.
++temp2_crit Provides shutdown temperature of the CPU package which
++ is also known as the maximum processor junction
++ temperature, Tjmax or Tprochot.
++temp2_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of
++ the CPU package.
++
++temp3_label "Tcontrol"
++temp3_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
++temp3_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
++temp4_label "Tthrottle"
++temp4_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
++temp5_label "Tjmax"
++temp5_input Provides the maximum junction temperature, Tjmax of the
+ CPU package.
+
+-temp[5-*]_label Provides string "Core X", where X is resolved core
++temp[6-*]_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
++temp[6-*]_input Provides current temperature of each core.
++temp[6-*]_max Provides thermal control temperature of the core.
++temp[6-*]_crit Provides shutdown temperature of the core.
++temp[6-*]_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of
+ the core.
+diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
+index c0623fa..7399c3c 100644
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -1333,7 +1333,7 @@ config SENSORS_PECI_CPUTEMP
+ 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
++ This driver can also be built as a module. If so, the module
+ will be called peci-cputemp.
+
+ config SENSORS_PECI_DIMMTEMP
+@@ -1347,7 +1347,7 @@ config SENSORS_PECI_DIMMTEMP
+ 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
++ This driver can also be built as a module. If so, the module
+ will be called peci-dimmtemp.
+
+ source "drivers/hwmon/pmbus/Kconfig"
+diff --git a/drivers/hwmon/peci-cputemp.c b/drivers/hwmon/peci-cputemp.c
+index 11880c8..d0d68e8 100644
+--- a/drivers/hwmon/peci-cputemp.c
++++ b/drivers/hwmon/peci-cputemp.c
+@@ -1,5 +1,5 @@
+ // SPDX-License-Identifier: GPL-2.0
+-// Copyright (c) 2018 Intel Corporation
++// Copyright (c) 2018-2019 Intel Corporation
+
+ #include <linux/hwmon.h>
+ #include <linux/jiffies.h>
+@@ -9,18 +9,13 @@
+ #include <linux/platform_device.h>
+ #include "peci-hwmon.h"
+
+-#define DEFAULT_CHANNEL_NUMS 4
++#define DEFAULT_CHANNEL_NUMS 5
+ #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 dts;
+ struct temp_data tcontrol;
+ struct temp_data tthrottle;
+ struct temp_data tjmax;
+@@ -43,6 +38,7 @@ struct peci_cputemp {
+
+ enum cputemp_channels {
+ channel_die,
++ channel_dts,
+ channel_tcontrol,
+ channel_tthrottle,
+ channel_tjmax,
+@@ -54,6 +50,10 @@ static const u32 config_table[DEFAULT_CHANNEL_NUMS + 1] = {
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+ HWMON_T_CRIT_HYST,
+
++ /* DTS margin */
++ 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,
+
+@@ -70,6 +70,7 @@ static const u32 config_table[DEFAULT_CHANNEL_NUMS + 1] = {
+
+ static const char *cputemp_label[CPUTEMP_CHANNEL_NUMS] = {
+ "Die",
++ "DTS",
+ "Tcontrol",
+ "Tthrottle",
+ "Tjmax",
+@@ -92,19 +93,20 @@ static int get_temp_targets(struct peci_cputemp *priv)
+ s32 tthrottle_offset;
+ s32 tcontrol_margin;
+ u8 pkg_cfg[4];
+- int rc;
++ int ret;
+
+- /**
++ /*
+ * 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;
++ ret = peci_client_read_package_config(priv->mgr,
++ PECI_MBX_INDEX_TEMP_TARGET, 0,
++ pkg_cfg);
++ if (ret)
++ return ret;
+
+ priv->temp.tjmax.value = pkg_cfg[2] * 1000;
+
+@@ -123,17 +125,16 @@ static int get_temp_targets(struct peci_cputemp *priv)
+ static int get_die_temp(struct peci_cputemp *priv)
+ {
+ struct peci_get_temp_msg msg;
+- int rc;
++ int ret;
+
+ 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;
++ ret = peci_command(priv->mgr->client->adapter, PECI_CMD_GET_TEMP, &msg);
++ if (ret)
++ return ret;
+
+ /* Note that the tjmax should be available before calling it */
+ priv->temp.die.value = priv->temp.tjmax.value +
+@@ -144,24 +145,64 @@ static int get_die_temp(struct peci_cputemp *priv)
+ return 0;
+ }
+
++static int get_dts(struct peci_cputemp *priv)
++{
++ s32 dts_margin;
++ u8 pkg_cfg[4];
++ int ret;
++
++ if (!peci_temp_need_update(&priv->temp.dts))
++ return 0;
++
++ ret = peci_client_read_package_config(priv->mgr,
++ PECI_MBX_INDEX_DTS_MARGIN, 0,
++ pkg_cfg);
++
++ if (ret)
++ return ret;
++
++ dts_margin = (pkg_cfg[1] << 8) | pkg_cfg[0];
++
++ /**
++ * Processors return a value of 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 (dts_margin >= 0x8000 && dts_margin <= 0x81ff)
++ return -EIO;
++
++ dts_margin = ten_dot_six_to_millidegree(dts_margin);
++
++ /* Note that the tcontrol should be available before calling it */
++ priv->temp.dts.value = priv->temp.tcontrol.value - dts_margin;
++
++ peci_temp_mark_updated(&priv->temp.dts);
++
++ return 0;
++}
++
+ static int get_core_temp(struct peci_cputemp *priv, int core_index)
+ {
+ s32 core_dts_margin;
+ u8 pkg_cfg[4];
+- int rc;
++ int ret;
+
+ 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;
++ ret = peci_client_read_package_config(priv->mgr,
++ PECI_MBX_INDEX_PER_CORE_DTS_TEMP,
++ core_index, pkg_cfg);
++ if (ret)
++ return ret;
+
+ 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:
+@@ -192,6 +233,7 @@ static int cputemp_read_string(struct device *dev,
+ return -EOPNOTSUPP;
+
+ *str = cputemp_label[channel];
++
+ return 0;
+ }
+
+@@ -200,26 +242,33 @@ static int cputemp_read(struct device *dev,
+ u32 attr, int channel, long *val)
+ {
+ struct peci_cputemp *priv = dev_get_drvdata(dev);
+- int rc, core_index;
++ int ret, core_index;
+
+ if (channel >= CPUTEMP_CHANNEL_NUMS ||
+ !(priv->temp_config[channel] & BIT(attr)))
+ return -EOPNOTSUPP;
+
+- rc = get_temp_targets(priv);
+- if (rc)
+- return rc;
++ ret = get_temp_targets(priv);
++ if (ret)
++ return ret;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ switch (channel) {
+ case channel_die:
+- rc = get_die_temp(priv);
+- if (rc)
++ ret = get_die_temp(priv);
++ if (ret)
+ break;
+
+ *val = priv->temp.die.value;
+ break;
++ case channel_dts:
++ ret = get_dts(priv);
++ if (ret)
++ break;
++
++ *val = priv->temp.dts.value;
++ break;
+ case channel_tcontrol:
+ *val = priv->temp.tcontrol.value;
+ break;
+@@ -231,8 +280,8 @@ static int cputemp_read(struct device *dev,
+ break;
+ default:
+ core_index = channel - DEFAULT_CHANNEL_NUMS;
+- rc = get_core_temp(priv, core_index);
+- if (rc)
++ ret = get_core_temp(priv, core_index);
++ if (ret)
+ break;
+
+ *val = priv->temp.core[core_index].value;
+@@ -249,11 +298,11 @@ static int cputemp_read(struct device *dev,
+ *val = priv->temp.tjmax.value - priv->temp.tcontrol.value;
+ break;
+ default:
+- rc = -EOPNOTSUPP;
++ ret = -EOPNOTSUPP;
+ break;
+ }
+
+- return rc;
++ return ret;
+ }
+
+ static umode_t cputemp_is_visible(const void *data,
+@@ -262,11 +311,11 @@ static umode_t cputemp_is_visible(const void *data,
+ {
+ 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;
++ if ((priv->temp_config[channel] & BIT(attr)) &&
++ (channel < DEFAULT_CHANNEL_NUMS ||
++ (channel >= DEFAULT_CHANNEL_NUMS &&
++ (priv->core_mask & BIT(channel - DEFAULT_CHANNEL_NUMS)))))
++ return 0444;
+
+ return 0;
+ }
+@@ -280,40 +329,43 @@ static const struct hwmon_ops cputemp_ops = {
+ static int check_resolved_cores(struct peci_cputemp *priv)
+ {
+ struct peci_rd_pci_cfg_local_msg msg;
+- int rc;
++ int ret;
+
+ /* 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.bus = 1;
++ msg.device = 30;
++ msg.function = 3;
++ msg.reg = 0xb4;
+ msg.rx_len = 4;
+
+- rc = peci_command(priv->mgr->client->adapter,
+- PECI_CMD_RD_PCI_CFG_LOCAL, &msg);
+- if (rc)
+- return rc;
++ ret = peci_command(priv->mgr->client->adapter,
++ PECI_CMD_RD_PCI_CFG_LOCAL, &msg);
++ if (msg.cc != PECI_DEV_CC_SUCCESS)
++ ret = -EAGAIN;
++ if (ret)
++ return ret;
+
+ 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;
++ int ret, i;
+
+- rc = check_resolved_cores(priv);
+- if (rc)
+- return rc;
++ ret = check_resolved_cores(priv);
++ if (ret)
++ return ret;
+
+ for (i = 0; i < priv->gen_info->core_max; i++)
+ if (priv->core_mask & BIT(i))
+- while (i + DEFAULT_CHANNEL_NUMS >= priv->config_idx)
++ while (priv->config_idx <= i + DEFAULT_CHANNEL_NUMS)
+ priv->temp_config[priv->config_idx++] =
+ config_table[channel_core];
+
+@@ -326,7 +378,7 @@ static int peci_cputemp_probe(struct platform_device *pdev)
+ struct device *dev = &pdev->dev;
+ struct peci_cputemp *priv;
+ struct device *hwmon_dev;
+- int rc;
++ int ret;
+
+ if ((mgr->client->adapter->cmd_mask &
+ (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
+@@ -346,12 +398,13 @@ static int peci_cputemp_probe(struct platform_device *pdev)
+ 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_dts];
+ 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)
++ ret = create_core_temp_info(priv);
++ if (ret)
+ dev_dbg(dev, "Skipped creating core temp info\n");
+
+ priv->chip.ops = &cputemp_ops;
+@@ -385,7 +438,7 @@ 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", },
++ .driver = { .name = KBUILD_MODNAME, },
+ };
+ module_platform_driver(peci_cputemp_driver);
+
+diff --git a/drivers/hwmon/peci-dimmtemp.c b/drivers/hwmon/peci-dimmtemp.c
+index 86a45a9..a404b6e 100644
+--- a/drivers/hwmon/peci-dimmtemp.c
++++ b/drivers/hwmon/peci-dimmtemp.c
+@@ -1,5 +1,5 @@
+ // SPDX-License-Identifier: GPL-2.0
+-// Copyright (c) 2018 Intel Corporation
++// Copyright (c) 2018-2019 Intel Corporation
+
+ #include <linux/hwmon.h>
+ #include <linux/jiffies.h>
+@@ -21,6 +21,8 @@ struct peci_dimmtemp {
+ struct workqueue_struct *work_queue;
+ struct delayed_work work_handler;
+ struct temp_data temp[DIMM_NUMS_MAX];
++ long temp_max[DIMM_NUMS_MAX];
++ long temp_crit[DIMM_NUMS_MAX];
+ u32 dimm_mask;
+ int retry_count;
+ u32 temp_config[DIMM_NUMS_MAX + 1];
+@@ -44,20 +46,106 @@ 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;
++ struct peci_rd_pci_cfg_local_msg rp_msg;
+ u8 cfg_data[4];
+- int rc;
++ int ret;
+
+ 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;
++ ret = peci_client_read_package_config(priv->mgr,
++ PECI_MBX_INDEX_DDR_DIMM_TEMP,
++ chan_rank, cfg_data);
++ if (ret)
++ return ret;
+
+ priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
+
++ switch (priv->gen_info->model) {
++ case INTEL_FAM6_SKYLAKE_X:
++ rp_msg.addr = priv->mgr->client->addr;
++ rp_msg.bus = 2;
++ /*
++ * Device 10, Function 2: IMC 0 channel 0 -> rank 0
++ * Device 10, Function 6: IMC 0 channel 1 -> rank 1
++ * Device 11, Function 2: IMC 0 channel 2 -> rank 2
++ * Device 12, Function 2: IMC 1 channel 0 -> rank 3
++ * Device 12, Function 6: IMC 1 channel 1 -> rank 4
++ * Device 13, Function 2: IMC 1 channel 2 -> rank 5
++ */
++ rp_msg.device = 10 + chan_rank / 3 * 2 +
++ (chan_rank % 3 == 2 ? 1 : 0);
++ rp_msg.function = chan_rank % 3 == 1 ? 6 : 2;
++ rp_msg.reg = 0x120 + dimm_order * 4;
++ rp_msg.rx_len = 4;
++
++ ret = peci_command(priv->mgr->client->adapter,
++ PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
++ if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
++ ret = -EAGAIN;
++ if (ret)
++ return ret;
++
++ priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
++ priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
++ break;
++ case INTEL_FAM6_SKYLAKE_XD:
++ rp_msg.addr = priv->mgr->client->addr;
++ rp_msg.bus = 2;
++ /*
++ * Device 10, Function 2: IMC 0 channel 0 -> rank 0
++ * Device 10, Function 6: IMC 0 channel 1 -> rank 1
++ * Device 12, Function 2: IMC 1 channel 0 -> rank 2
++ * Device 12, Function 6: IMC 1 channel 1 -> rank 3
++ */
++ rp_msg.device = 10 + chan_rank / 2 * 2;
++ rp_msg.function = chan_rank % 2 ? 6 : 2;
++ rp_msg.reg = 0x120 + dimm_order * 4;
++ rp_msg.rx_len = 4;
++
++ ret = peci_command(priv->mgr->client->adapter,
++ PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
++ if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
++ ret = -EAGAIN;
++ if (ret)
++ return ret;
++
++ priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
++ priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
++ break;
++ case INTEL_FAM6_HASWELL_X:
++ case INTEL_FAM6_BROADWELL_X:
++ rp_msg.addr = priv->mgr->client->addr;
++ rp_msg.bus = 1;
++ /*
++ * Device 20, Function 0: IMC 0 channel 0 -> rank 0
++ * Device 20, Function 1: IMC 0 channel 1 -> rank 1
++ * Device 21, Function 0: IMC 0 channel 2 -> rank 2
++ * Device 21, Function 1: IMC 0 channel 3 -> rank 3
++ * Device 23, Function 0: IMC 1 channel 0 -> rank 4
++ * Device 23, Function 1: IMC 1 channel 1 -> rank 5
++ * Device 24, Function 0: IMC 1 channel 2 -> rank 6
++ * Device 24, Function 1: IMC 1 channel 3 -> rank 7
++ */
++ rp_msg.device = 20 + chan_rank / 2 + chan_rank / 4;
++ rp_msg.function = chan_rank % 2;
++ rp_msg.reg = 0x120 + dimm_order * 4;
++ rp_msg.rx_len = 4;
++
++ ret = peci_command(priv->mgr->client->adapter,
++ PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
++ if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
++ ret = -EAGAIN;
++ if (ret)
++ return ret;
++
++ priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
++ priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
++ break;
++ default:
++ return -EOPNOTSUPP;
++ }
++
+ peci_temp_mark_updated(&priv->temp[dimm_no]);
+
+ return 0;
+@@ -77,6 +165,7 @@ static int dimmtemp_read_string(struct device *dev,
+ chan_rank = channel / dimm_idx_max;
+ dimm_idx = channel % dimm_idx_max;
+ *str = dimmtemp_label[chan_rank][dimm_idx];
++
+ return 0;
+ }
+
+@@ -84,17 +173,28 @@ 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;
++ int ret;
++
++ ret = get_dimm_temp(priv, channel);
++ if (ret)
++ return ret;
++
++ switch (attr) {
++ case hwmon_temp_input:
++ *val = priv->temp[channel].value;
++ break;
++ case hwmon_temp_max:
++ *val = priv->temp_max[channel];
++ break;
++ case hwmon_temp_crit:
++ *val = priv->temp_crit[channel];
++ break;
++ default:
++ ret = -EOPNOTSUPP;
++ break;
++ }
+
+- *val = priv->temp[channel].value;
+- return 0;
++ return ret;
+ }
+
+ static umode_t dimmtemp_is_visible(const void *data,
+@@ -120,16 +220,16 @@ 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;
++ int chan_rank, dimm_idx, ret;
+ 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) {
++ ret = peci_client_read_package_config(priv->mgr,
++ PECI_MBX_INDEX_DDR_DIMM_TEMP,
++ chan_rank, cfg_data);
++ if (ret) {
+ priv->dimm_mask = 0;
+- return rc;
++ return ret;
+ }
+
+ for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++)
+@@ -143,17 +243,18 @@ static int check_populated_dimms(struct peci_dimmtemp *priv)
+ 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;
++ int ret, i, config_idx, channels;
+ struct device *hwmon_dev;
+
+- rc = check_populated_dimms(priv);
+- if (rc) {
+- if (rc == -EAGAIN) {
++ ret = check_populated_dimms(priv);
++ if (ret) {
++ if (ret == -EAGAIN) {
+ if (priv->retry_count < DIMM_MASK_CHECK_RETRY_MAX) {
+ queue_delayed_work(priv->work_queue,
+ &priv->work_handler,
+@@ -164,11 +265,11 @@ static int create_dimm_temp_info(struct peci_dimmtemp *priv)
+ } else {
+ dev_err(priv->dev,
+ "Timeout DIMM temp info creation\n");
+- rc = -ETIMEDOUT;
++ ret = -ETIMEDOUT;
+ }
+ }
+
+- return rc;
++ return ret;
+ }
+
+ channels = priv->gen_info->chan_rank_max *
+@@ -177,7 +278,8 @@ static int create_dimm_temp_info(struct peci_dimmtemp *priv)
+ if (priv->dimm_mask & BIT(i))
+ while (i >= config_idx)
+ priv->temp_config[config_idx++] =
+- HWMON_T_LABEL | HWMON_T_INPUT;
++ HWMON_T_LABEL | HWMON_T_INPUT |
++ HWMON_T_MAX | HWMON_T_CRIT;
+
+ priv->chip.ops = &dimmtemp_ops;
+ priv->chip.info = priv->info;
+@@ -192,12 +294,12 @@ static int create_dimm_temp_info(struct peci_dimmtemp *priv)
+ priv,
+ &priv->chip,
+ NULL);
+- rc = PTR_ERR_OR_ZERO(hwmon_dev);
+- if (!rc)
++ ret = PTR_ERR_OR_ZERO(hwmon_dev);
++ if (!ret)
+ dev_dbg(priv->dev, "%s: sensor '%s'\n",
+ dev_name(hwmon_dev), priv->name);
+
+- return rc;
++ return ret;
+ }
+
+ static void create_dimm_temp_info_delayed(struct work_struct *work)
+@@ -205,10 +307,10 @@ 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;
++ int ret;
+
+- rc = create_dimm_temp_info(priv);
+- if (rc && rc != -EAGAIN)
++ ret = create_dimm_temp_info(priv);
++ if (ret && ret != -EAGAIN)
+ dev_dbg(priv->dev, "Failed to create DIMM temp info\n");
+ }
+
+@@ -217,7 +319,7 @@ 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;
++ int ret;
+
+ if ((mgr->client->adapter->cmd_mask &
+ (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
+@@ -242,8 +344,8 @@ static int peci_dimmtemp_probe(struct platform_device *pdev)
+
+ INIT_DELAYED_WORK(&priv->work_handler, create_dimm_temp_info_delayed);
+
+- rc = create_dimm_temp_info(priv);
+- if (rc && rc != -EAGAIN) {
++ ret = create_dimm_temp_info(priv);
++ if (ret && ret != -EAGAIN) {
+ dev_err(dev, "Failed to create DIMM temp info\n");
+ goto err_free_wq;
+ }
+@@ -252,7 +354,7 @@ static int peci_dimmtemp_probe(struct platform_device *pdev)
+
+ err_free_wq:
+ destroy_workqueue(priv->work_queue);
+- return rc;
++ return ret;
+ }
+
+ static int peci_dimmtemp_remove(struct platform_device *pdev)
+@@ -275,7 +377,7 @@ static struct platform_driver peci_dimmtemp_driver = {
+ .probe = peci_dimmtemp_probe,
+ .remove = peci_dimmtemp_remove,
+ .id_table = peci_dimmtemp_ids,
+- .driver = { .name = "peci-dimmtemp", },
++ .driver = { .name = KBUILD_MODNAME, },
+ };
+ module_platform_driver(peci_dimmtemp_driver);
+
+diff --git a/drivers/hwmon/peci-hwmon.h b/drivers/hwmon/peci-hwmon.h
+index 6ca1855..ce6b470 100644
+--- a/drivers/hwmon/peci-hwmon.h
++++ b/drivers/hwmon/peci-hwmon.h
+@@ -1,5 +1,5 @@
+ /* SPDX-License-Identifier: GPL-2.0 */
+-/* Copyright (c) 2018 Intel Corporation */
++/* Copyright (c) 2018-2019 Intel Corporation */
+
+ #ifndef __PECI_HWMON_H
+ #define __PECI_HWMON_H
+@@ -29,11 +29,8 @@ struct temp_data {
+ */
+ 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;
++ return !temp->valid ||
++ time_after(jiffies, temp->last_updated + UPDATE_INTERVAL);
+ }
+
+ /**
+diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
+index 5d89546..46f52a3 100644
+--- a/drivers/mfd/Kconfig
++++ b/drivers/mfd/Kconfig
+@@ -630,7 +630,7 @@ config MFD_INTEL_MSIC
+ devices used in Intel Medfield platforms.
+
+ config MFD_INTEL_PECI_CLIENT
+- bool "Intel PECI client"
++ tristate "Intel PECI client"
+ depends on (PECI || COMPILE_TEST)
+ select MFD_CORE
+ help
+@@ -643,6 +643,9 @@ config MFD_INTEL_PECI_CLIENT
+ Additional drivers must be enabled in order to use the functionality
+ of the device.
+
++ This driver can also be built as a module. If so, the module
++ will be called intel-peci-client.
++
+ config MFD_IPAQ_MICRO
+ bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
+ depends on SA1100_H3100 || SA1100_H3600
+diff --git a/drivers/mfd/intel-peci-client.c b/drivers/mfd/intel-peci-client.c
+index d53e4f1..18bf0af 100644
+--- a/drivers/mfd/intel-peci-client.c
++++ b/drivers/mfd/intel-peci-client.c
+@@ -1,12 +1,12 @@
+ // SPDX-License-Identifier: GPL-2.0
+-// Copyright (c) 2018 Intel Corporation
++// Copyright (c) 2018-2019 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>
++#include <linux/peci.h>
+
+ #define CPU_ID_MODEL_MASK GENMASK(7, 4)
+ #define CPU_ID_FAMILY_MASK GENMASK(11, 8)
+@@ -18,12 +18,6 @@
+ #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", },
+@@ -31,38 +25,45 @@ static struct mfd_cell peci_functions[] = {
+ };
+
+ static const struct cpu_gen_info cpu_gen_info_table[] = {
+- [CPU_GEN_HSX] = {
++ { /* Haswell Xeon */
+ .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] = {
++ { /* Broadwell Xeon */
+ .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] = {
++ { /* Skylake Xeon */
+ .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 },
++ { /* Skylake Xeon D */
++ .family = 6, /* Family code */
++ .model = INTEL_FAM6_SKYLAKE_XD,
++ .core_max = CORE_MAX_ON_SKXD,
++ .chan_rank_max = CHAN_RANK_MAX_ON_SKXD,
++ .dimm_idx_max = DIMM_IDX_MAX_ON_SKXD },
+ };
+
+ static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv)
+ {
++ struct device *dev = &priv->client->dev;
+ u32 cpu_id;
+ u16 family;
+ u8 model;
+- int rc;
++ int ret;
+ int i;
+
+- rc = peci_get_cpu_id(priv->client->adapter, priv->client->addr,
+- &cpu_id);
+- if (rc)
+- return rc;
++ ret = peci_get_cpu_id(priv->client->adapter, priv->client->addr,
++ &cpu_id);
++ if (ret)
++ return ret;
+
+ family = FIELD_PREP(LOWER_BYTE_MASK,
+ FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id)) |
+@@ -83,11 +84,11 @@ static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv)
+ }
+
+ if (!priv->gen_info) {
+- dev_err(priv->dev, "Can't support this CPU: 0x%x\n", cpu_id);
+- rc = -ENODEV;
++ dev_err(dev, "Can't support this CPU: 0x%x\n", cpu_id);
++ ret = -ENODEV;
+ }
+
+- return rc;
++ return ret;
+ }
+
+ static int peci_client_probe(struct peci_client *client)
+@@ -103,31 +104,29 @@ static int peci_client_probe(struct peci_client *client)
+
+ 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,
++ ret = devm_mfd_add_devices(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);
++ dev_err(dev, "Failed to register child devices: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+ }
+
+-#ifdef CONFIG_OF
++#if IS_ENABLED(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
++#endif /* CONFIG_OF */
+
+ static const struct peci_device_id peci_client_ids[] = {
+ { .name = "peci-client" },
+@@ -139,7 +138,7 @@ static struct peci_driver peci_client_driver = {
+ .probe = peci_client_probe,
+ .id_table = peci_client_ids,
+ .driver = {
+- .name = "peci-client",
++ .name = KBUILD_MODNAME,
+ .of_match_table = of_match_ptr(peci_client_of_table),
+ },
+ };
+diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig
+index 7293108..9752fee 100644
+--- a/drivers/peci/Kconfig
++++ b/drivers/peci/Kconfig
+@@ -2,10 +2,12 @@
+ # Platform Environment Control Interface (PECI) subsystem configuration
+ #
+
++menu "PECI support"
++
+ config PECI
+- bool "PECI support"
+- select RT_MUTEXES
++ tristate "PECI support"
+ select CRC8
++ default n
+ help
+ The Platform Environment Control Interface (PECI) is a one-wire bus
+ interface that provides a communication channel from Intel processors
+@@ -14,37 +16,23 @@ config PECI
+ 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-core.
+
+- This support is also available as a module. If so, the module
+- will be called peci-aspeed.
++if PECI
+
+-config PECI_NPCM
+- tristate "Nuvoton NPCM PECI support"
+- select REGMAP_MMIO
+- depends on OF
+- depends on ARCH_NPCM || COMPILE_TEST
++config PECI_CHARDEV
++ tristate "PECI device interface"
+ help
+- Say Y here if you want support for the Platform Environment Control
+- Interface (PECI) bus adapter driver on the Nuvoton NPCM SoCs.
++ Say Y here to use peci-* device files, usually found in the /dev
++ directory on your system. They make it possible to have user-space
++ programs use the PECI bus.
+
+ This support is also available as a module. If so, the module
+- will be called peci-npcm.
+-endmenu
++ will be called peci-dev.
++
++source "drivers/peci/busses/Kconfig"
+
+ endif # PECI
++
++endmenu
+diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile
+index 3326da5..da8b0a3 100644
+--- a/drivers/peci/Makefile
++++ b/drivers/peci/Makefile
+@@ -1,10 +1,11 @@
++# SPDX-License-Identifier: GPL-2.0
+ #
+-# Makefile for the PECI core and bus drivers.
++# Makefile for the PECI core drivers.
+ #
+
+ # Core functionality
+ obj-$(CONFIG_PECI) += peci-core.o
++obj-$(CONFIG_PECI_CHARDEV) += peci-dev.o
+
+ # Hardware specific bus drivers
+-obj-$(CONFIG_PECI_ASPEED) += peci-aspeed.o
+-obj-$(CONFIG_PECI_NPCM) += peci-npcm.o
++obj-y += busses/
+diff --git a/drivers/peci/busses/Kconfig b/drivers/peci/busses/Kconfig
+new file mode 100644
+index 0000000..bfacafb
+--- /dev/null
++++ b/drivers/peci/busses/Kconfig
+@@ -0,0 +1,32 @@
++#
++# PECI hardware bus configuration
++#
++
++menu "PECI Hardware Bus support"
++
++config PECI_ASPEED
++ tristate "ASPEED PECI support"
++ depends on ARCH_ASPEED || COMPILE_TEST
++ depends on OF
++ depends on PECI
++ 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
++ depends on PECI
++ 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
+diff --git a/drivers/peci/busses/Makefile b/drivers/peci/busses/Makefile
+new file mode 100644
+index 0000000..aa8ce3a
+--- /dev/null
++++ b/drivers/peci/busses/Makefile
+@@ -0,0 +1,7 @@
++# SPDX-License-Identifier: GPL-2.0
++#
++# Makefile for the PECI hardware bus drivers.
++#
++
++obj-$(CONFIG_PECI_ASPEED) += peci-aspeed.o
++obj-$(CONFIG_PECI_NPCM) += peci-npcm.o
+diff --git a/drivers/peci/busses/peci-aspeed.c b/drivers/peci/busses/peci-aspeed.c
+new file mode 100644
+index 0000000..851b71e3
+--- /dev/null
++++ b/drivers/peci/busses/peci-aspeed.c
+@@ -0,0 +1,492 @@
++// SPDX-License-Identifier: GPL-2.0
++// Copyright (C) 2012-2017 ASPEED Technology Inc.
++// Copyright (c) 2018-2019 Intel Corporation
++
++#include <linux/bitfield.h>
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/jiffies.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/peci.h>
++#include <linux/platform_device.h>
++#include <linux/reset.h>
++
++/* ASPEED PECI Registers */
++/* Control Register */
++#define ASPEED_PECI_CTRL 0x00
++#define ASPEED_PECI_CTRL_SAMPLING_MASK GENMASK(19, 16)
++#define ASPEED_PECI_CTRL_READ_MODE_MASK GENMASK(13, 12)
++#define ASPEED_PECI_CTRL_READ_MODE_COUNT BIT(12)
++#define ASPEED_PECI_CTRL_READ_MODE_DBG BIT(13)
++#define ASPEED_PECI_CTRL_CLK_SOURCE_MASK BIT(11)
++#define ASPEED_PECI_CTRL_CLK_DIV_MASK GENMASK(10, 8)
++#define ASPEED_PECI_CTRL_INVERT_OUT BIT(7)
++#define ASPEED_PECI_CTRL_INVERT_IN BIT(6)
++#define ASPEED_PECI_CTRL_BUS_CONTENT_EN BIT(5)
++#define ASPEED_PECI_CTRL_PECI_EN BIT(4)
++#define ASPEED_PECI_CTRL_PECI_CLK_EN BIT(0)
++
++/* Timing Negotiation Register */
++#define ASPEED_PECI_TIMING_NEGOTIATION 0x04
++#define ASPEED_PECI_TIMING_MESSAGE_MASK GENMASK(15, 8)
++#define ASPEED_PECI_TIMING_ADDRESS_MASK GENMASK(7, 0)
++
++/* Command Register */
++#define ASPEED_PECI_CMD 0x08
++#define ASPEED_PECI_CMD_PIN_MON BIT(31)
++#define ASPEED_PECI_CMD_STS_MASK GENMASK(27, 24)
++#define ASPEED_PECI_CMD_IDLE_MASK (ASPEED_PECI_CMD_STS_MASK | \
++ ASPEED_PECI_CMD_PIN_MON)
++#define ASPEED_PECI_CMD_FIRE BIT(0)
++
++/* Read/Write Length Register */
++#define ASPEED_PECI_RW_LENGTH 0x0c
++#define ASPEED_PECI_AW_FCS_EN BIT(31)
++#define ASPEED_PECI_READ_LEN_MASK GENMASK(23, 16)
++#define ASPEED_PECI_WRITE_LEN_MASK GENMASK(15, 8)
++#define ASPEED_PECI_TAGET_ADDR_MASK GENMASK(7, 0)
++
++/* Expected FCS Data Register */
++#define ASPEED_PECI_EXP_FCS 0x10
++#define ASPEED_PECI_EXP_READ_FCS_MASK GENMASK(23, 16)
++#define ASPEED_PECI_EXP_AW_FCS_AUTO_MASK GENMASK(15, 8)
++#define ASPEED_PECI_EXP_WRITE_FCS_MASK GENMASK(7, 0)
++
++/* Captured FCS Data Register */
++#define ASPEED_PECI_CAP_FCS 0x14
++#define ASPEED_PECI_CAP_READ_FCS_MASK GENMASK(23, 16)
++#define ASPEED_PECI_CAP_WRITE_FCS_MASK GENMASK(7, 0)
++
++/* Interrupt Register */
++#define ASPEED_PECI_INT_CTRL 0x18
++#define ASPEED_PECI_TIMING_NEGO_SEL_MASK GENMASK(31, 30)
++#define ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO 0
++#define ASPEED_PECI_2ND_BIT_OF_ADDR_NEGO 1
++#define ASPEED_PECI_MESSAGE_NEGO 2
++#define ASPEED_PECI_INT_MASK GENMASK(4, 0)
++#define ASPEED_PECI_INT_BUS_TIMEOUT BIT(4)
++#define ASPEED_PECI_INT_BUS_CONNECT BIT(3)
++#define ASPEED_PECI_INT_W_FCS_BAD BIT(2)
++#define ASPEED_PECI_INT_W_FCS_ABORT BIT(1)
++#define ASPEED_PECI_INT_CMD_DONE BIT(0)
++
++/* Interrupt Status Register */
++#define ASPEED_PECI_INT_STS 0x1c
++#define ASPEED_PECI_INT_TIMING_RESULT_MASK GENMASK(29, 16)
++ /* bits[4..0]: Same bit fields in the 'Interrupt Register' */
++
++/* Rx/Tx Data Buffer Registers */
++#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
++#define ASPEED_PECI_DATA_BUF_SIZE_MAX 32
++
++/* Timing Negotiation */
++#define ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT 8
++#define ASPEED_PECI_RD_SAMPLING_POINT_MAX 15
++#define ASPEED_PECI_CLK_DIV_DEFAULT 0
++#define ASPEED_PECI_CLK_DIV_MAX 7
++#define ASPEED_PECI_MSG_TIMING_DEFAULT 1
++#define ASPEED_PECI_MSG_TIMING_MAX 255
++#define ASPEED_PECI_ADDR_TIMING_DEFAULT 1
++#define ASPEED_PECI_ADDR_TIMING_MAX 255
++
++/* Timeout */
++#define ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC 50000
++#define ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC 10000
++#define ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT 1000
++#define ASPEED_PECI_CMD_TIMEOUT_MS_MAX 60000
++
++struct aspeed_peci {
++ struct peci_adapter *adapter;
++ struct device *dev;
++ void __iomem *base;
++ 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_check_idle(struct aspeed_peci *priv)
++{
++ ulong timeout = jiffies + usecs_to_jiffies(ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC);
++ u32 cmd_sts;
++
++ for (;;) {
++ cmd_sts = readl(priv->base + ASPEED_PECI_CMD);
++ if (!(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK))
++ break;
++ if (time_after(jiffies, timeout)) {
++ cmd_sts = readl(priv->base + ASPEED_PECI_CMD);
++ break;
++ }
++ usleep_range((ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC >> 2) + 1,
++ ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC);
++ }
++
++ return !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK) ? 0 : -ETIMEDOUT;
++}
++
++static int aspeed_peci_xfer(struct peci_adapter *adapter,
++ struct peci_xfer_msg *msg)
++{
++ struct aspeed_peci *priv = peci_get_adapdata(adapter);
++ long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms);
++ u32 peci_head, peci_state, rx_data = 0;
++ ulong flags;
++ int i, ret;
++ uint reg;
++
++ if (msg->tx_len > ASPEED_PECI_DATA_BUF_SIZE_MAX ||
++ msg->rx_len > ASPEED_PECI_DATA_BUF_SIZE_MAX)
++ return -EINVAL;
++
++ /* Check command sts and bus idle state */
++ ret = aspeed_peci_check_idle(priv);
++ if (ret)
++ return ret; /* -ETIMEDOUT */
++
++ spin_lock_irqsave(&priv->lock, flags);
++ reinit_completion(&priv->xfer_complete);
++
++ peci_head = FIELD_PREP(ASPEED_PECI_TAGET_ADDR_MASK, msg->addr) |
++ FIELD_PREP(ASPEED_PECI_WRITE_LEN_MASK, msg->tx_len) |
++ FIELD_PREP(ASPEED_PECI_READ_LEN_MASK, msg->rx_len);
++
++ writel(peci_head, priv->base + ASPEED_PECI_RW_LENGTH);
++
++ for (i = 0; i < msg->tx_len; i += 4) {
++ reg = i < 16 ? ASPEED_PECI_W_DATA0 + i % 16 :
++ ASPEED_PECI_W_DATA4 + i % 16;
++ writel(le32_to_cpup((__le32 *)&msg->tx_buf[i]),
++ priv->base + reg);
++ }
++
++ 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;
++ writel(ASPEED_PECI_CMD_FIRE, priv->base + ASPEED_PECI_CMD);
++ 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);
++ peci_state = readl(priv->base + ASPEED_PECI_CMD);
++ dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n",
++ FIELD_GET(ASPEED_PECI_CMD_STS_MASK, peci_state));
++
++ writel(0, priv->base + ASPEED_PECI_CMD);
++
++ if (err <= 0 || priv->status != ASPEED_PECI_INT_CMD_DONE) {
++ if (err < 0) { /* -ERESTARTSYS */
++ ret = (int)err;
++ goto err_irqrestore;
++ } else if (err == 0) {
++ dev_dbg(priv->dev, "Timeout waiting for a response!\n");
++ ret = -ETIMEDOUT;
++ goto err_irqrestore;
++ }
++
++ dev_dbg(priv->dev, "No valid response!\n");
++ ret = -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;
++ rx_data = readl(priv->base + reg);
++ }
++
++ 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);
++
++ peci_state = readl(priv->base + ASPEED_PECI_CMD);
++ dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n",
++ FIELD_GET(ASPEED_PECI_CMD_STS_MASK, peci_state));
++ dev_dbg(priv->dev, "------------------------\n");
++
++err_irqrestore:
++ spin_unlock_irqrestore(&priv->lock, flags);
++ return ret;
++}
++
++static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg)
++{
++ struct aspeed_peci *priv = arg;
++ u32 status;
++
++ spin_lock(&priv->lock);
++ status = readl(priv->base + ASPEED_PECI_INT_STS);
++ writel(status, priv->base + ASPEED_PECI_INT_STS);
++ priv->status |= (status & ASPEED_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 & ASPEED_PECI_INT_BUS_TIMEOUT) {
++ dev_dbg(priv->dev, "ASPEED_PECI_INT_BUS_TIMEOUT\n");
++ }
++
++ if (status & ASPEED_PECI_INT_BUS_CONNECT) {
++ dev_dbg(priv->dev, "ASPEED_PECI_INT_BUS_CONNECT\n");
++ }
++
++ if (status & ASPEED_PECI_INT_W_FCS_BAD) {
++ dev_dbg(priv->dev, "ASPEED_PECI_INT_W_FCS_BAD\n");
++ }
++
++ if (status & ASPEED_PECI_INT_W_FCS_ABORT) {
++ dev_dbg(priv->dev, "ASPEED_PECI_INT_W_FCS_ABORT\n");
++ }
++
++ /*
++ * All commands should be ended up with a ASPEED_PECI_INT_CMD_DONE bit
++ * set even in an error case.
++ */
++ if (status & ASPEED_PECI_INT_CMD_DONE) {
++ dev_dbg(priv->dev, "ASPEED_PECI_INT_CMD_DONE\n");
++ complete(&priv->xfer_complete);
++ }
++
++ 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 = device_property_read_u32(priv->dev, "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 < ASPEED_PECI_CLK_DIV_MAX))
++ clk_div_val++;
++
++ ret = device_property_read_u32(priv->dev, "msg-timing", &msg_timing);
++ if (ret || msg_timing > ASPEED_PECI_MSG_TIMING_MAX) {
++ if (!ret)
++ dev_warn(priv->dev,
++ "Invalid msg-timing : %u, Use default : %u\n",
++ msg_timing, ASPEED_PECI_MSG_TIMING_DEFAULT);
++ msg_timing = ASPEED_PECI_MSG_TIMING_DEFAULT;
++ }
++
++ ret = device_property_read_u32(priv->dev, "addr-timing", &addr_timing);
++ if (ret || addr_timing > ASPEED_PECI_ADDR_TIMING_MAX) {
++ if (!ret)
++ dev_warn(priv->dev,
++ "Invalid addr-timing : %u, Use default : %u\n",
++ addr_timing, ASPEED_PECI_ADDR_TIMING_DEFAULT);
++ addr_timing = ASPEED_PECI_ADDR_TIMING_DEFAULT;
++ }
++
++ ret = device_property_read_u32(priv->dev, "rd-sampling-point",
++ &rd_sampling_point);
++ if (ret || rd_sampling_point > ASPEED_PECI_RD_SAMPLING_POINT_MAX) {
++ if (!ret)
++ dev_warn(priv->dev,
++ "Invalid rd-sampling-point : %u. Use default : %u\n",
++ rd_sampling_point,
++ ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT);
++ rd_sampling_point = ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT;
++ }
++
++ ret = device_property_read_u32(priv->dev, "cmd-timeout-ms",
++ &priv->cmd_timeout_ms);
++ if (ret || priv->cmd_timeout_ms > ASPEED_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,
++ ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT);
++ priv->cmd_timeout_ms = ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT;
++ }
++
++ writel(FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK,
++ ASPEED_PECI_CLK_DIV_DEFAULT) |
++ ASPEED_PECI_CTRL_PECI_CLK_EN, priv->base + ASPEED_PECI_CTRL);
++
++ /*
++ * Timing negotiation period setting.
++ * The unit of the programmed value is 4 times of PECI clock period.
++ */
++ writel(FIELD_PREP(ASPEED_PECI_TIMING_MESSAGE_MASK, msg_timing) |
++ FIELD_PREP(ASPEED_PECI_TIMING_ADDRESS_MASK, addr_timing),
++ priv->base + ASPEED_PECI_TIMING_NEGOTIATION);
++
++ /* Clear interrupts */
++ writel(readl(priv->base + ASPEED_PECI_INT_STS) | ASPEED_PECI_INT_MASK,
++ priv->base + ASPEED_PECI_INT_STS);
++
++ /* Set timing negotiation mode and enable interrupts */
++ writel(FIELD_PREP(ASPEED_PECI_TIMING_NEGO_SEL_MASK,
++ ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO) |
++ ASPEED_PECI_INT_MASK, priv->base + ASPEED_PECI_INT_CTRL);
++
++ /* Read sampling point and clock speed setting */
++ writel(FIELD_PREP(ASPEED_PECI_CTRL_SAMPLING_MASK, rd_sampling_point) |
++ FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK, clk_div_val) |
++ ASPEED_PECI_CTRL_PECI_EN | ASPEED_PECI_CTRL_PECI_CLK_EN,
++ priv->base + ASPEED_PECI_CTRL);
++
++ return 0;
++}
++
++static int aspeed_peci_probe(struct platform_device *pdev)
++{
++ struct peci_adapter *adapter;
++ struct aspeed_peci *priv;
++ 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);
++
++ priv->base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(priv->base)) {
++ ret = PTR_ERR(priv->base);
++ 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->adapter->use_dma = false;
++
++ priv->rst = devm_reset_control_get(&pdev->dev, NULL);
++ if (IS_ERR(priv->rst)) {
++ dev_err(&pdev->dev,
++ "missing or invalid reset controller entry\n");
++ 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 = KBUILD_MODNAME,
++ .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/busses/peci-npcm.c b/drivers/peci/busses/peci-npcm.c
+new file mode 100644
+index 0000000..f632365
+--- /dev/null
++++ b/drivers/peci/busses/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/peci/peci-aspeed.c b/drivers/peci/peci-aspeed.c
+deleted file mode 100644
+index 51cb256..0000000
+--- a/drivers/peci/peci-aspeed.c
++++ /dev/null
+@@ -1,505 +0,0 @@
+-// 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
+index 6f24146..2a6be04 100644
+--- a/drivers/peci/peci-core.c
++++ b/drivers/peci/peci-core.c
+@@ -1,38 +1,31 @@
+ // SPDX-License-Identifier: GPL-2.0
+-// Copyright (c) 2018 Intel Corporation
++// Copyright (c) 2018-2019 Intel Corporation
+
+ #include <linux/bitfield.h>
+ #include <linux/crc8.h>
+ #include <linux/delay.h>
+-#include <linux/fs.h>
++#include <linux/mm.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/sched/task_stack.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 */
++/* CRC8 table for Assured 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 *peci_get_adapter(int nr)
+ {
+ struct peci_adapter *adapter;
+
+@@ -48,10 +41,12 @@ static struct peci_adapter *peci_get_adapter(int nr)
+
+ out_unlock:
+ mutex_unlock(&core_lock);
++
+ return adapter;
+ }
++EXPORT_SYMBOL_GPL(peci_get_adapter);
+
+-static void peci_put_adapter(struct peci_adapter *adapter)
++void peci_put_adapter(struct peci_adapter *adapter)
+ {
+ if (!adapter)
+ return;
+@@ -59,6 +54,7 @@ static void peci_put_adapter(struct peci_adapter *adapter)
+ put_device(&adapter->dev);
+ module_put(adapter->owner);
+ }
++EXPORT_SYMBOL_GPL(peci_put_adapter);
+
+ static ssize_t name_show(struct device *dev,
+ struct device_attribute *attr,
+@@ -84,10 +80,11 @@ static struct attribute *peci_device_attrs[] = {
+ };
+ ATTRIBUTE_GROUPS(peci_device);
+
+-static struct device_type peci_client_type = {
++struct device_type peci_client_type = {
+ .groups = peci_device_groups,
+ .release = peci_client_dev_release,
+ };
++EXPORT_SYMBOL_GPL(peci_client_type);
+
+ /**
+ * peci_verify_client - return parameter as peci_client, or NULL
+@@ -103,19 +100,120 @@ struct peci_client *peci_verify_client(struct device *dev)
+ }
+ EXPORT_SYMBOL_GPL(peci_verify_client);
+
+-static u8 peci_aw_fcs(u8 *data, int len)
++/**
++ * peci_get_xfer_msg() - get a DMA safe peci_xfer_msg for the given tx and rx
++ * length
++ * @tx_len: the length of tx_buf. May be 0 if tx_buf isn't needed.
++ * @rx_len: the length of rx_buf. May be 0 if rx_buf isn't needed.
++ *
++ * Return: NULL if a DMA safe buffer was not obtained.
++ * Or a valid pointer to be used with DMA. After use, release it by
++ * calling peci_put_xfer_msg().
++ *
++ * This function must only be called from process context!
++ */
++struct peci_xfer_msg *peci_get_xfer_msg(u8 tx_len, u8 rx_len)
++{
++ struct peci_xfer_msg *msg;
++ u8 *tx_buf, *rx_buf;
++
++ if (tx_len) {
++ tx_buf = kzalloc(tx_len, GFP_KERNEL);
++ if (!tx_buf)
++ return NULL;
++ } else {
++ tx_buf = NULL;
++ }
++
++ if (rx_len) {
++ rx_buf = kzalloc(rx_len, GFP_KERNEL);
++ if (!rx_buf)
++ goto err_free_tx_buf;
++ } else {
++ rx_buf = NULL;
++ }
++
++ msg = kzalloc(sizeof(struct peci_xfer_msg), GFP_KERNEL);
++ if (!msg)
++ goto err_free_tx_rx_buf;
++
++ msg->tx_len = tx_len;
++ msg->tx_buf = tx_buf;
++ msg->rx_len = rx_len;
++ msg->rx_buf = rx_buf;
++
++ return msg;
++
++err_free_tx_rx_buf:
++ kfree(rx_buf);
++err_free_tx_buf:
++ kfree(tx_buf);
++
++ return NULL;
++}
++EXPORT_SYMBOL_GPL(peci_get_xfer_msg);
++
++/**
++ * peci_put_xfer_msg - release a DMA safe peci_xfer_msg
++ * @msg: the message obtained from peci_get_xfer_msg(). May be NULL.
++ */
++void peci_put_xfer_msg(struct peci_xfer_msg *msg)
++{
++ if (!msg)
++ return;
++
++ kfree(msg->rx_buf);
++ kfree(msg->tx_buf);
++ kfree(msg);
++}
++EXPORT_SYMBOL_GPL(peci_put_xfer_msg);
++
++/* Calculate an Assured Write Frame Check Sequence byte */
++static int peci_aw_fcs(struct peci_xfer_msg *msg, int len, u8 *aw_fcs)
+ {
+- return crc8(peci_crc8_table, data, (size_t)len, 0);
++ u8 *tmp_buf;
++
++ /* Allocate a temporary buffer to use a contiguous byte array */
++ tmp_buf = kmalloc(len, GFP_KERNEL);
++ if (!tmp_buf)
++ return -ENOMEM;
++
++ tmp_buf[0] = msg->addr;
++ tmp_buf[1] = msg->tx_len;
++ tmp_buf[2] = msg->rx_len;
++ memcpy(&tmp_buf[3], msg->tx_buf, len - 3);
++
++ *aw_fcs = crc8(peci_crc8_table, tmp_buf, (size_t)len, 0);
++
++ kfree(tmp_buf);
++
++ return 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;
++ ulong timeout = jiffies;
++ u8 aw_fcs;
++ int ret;
++
++ /*
++ * In case if adapter uses DMA, check at here whether tx and rx buffers
++ * are DMA capable or not.
++ */
++ if (IS_ENABLED(CONFIG_HAS_DMA) && adapter->use_dma) {
++ if (is_vmalloc_addr(msg->tx_buf) ||
++ is_vmalloc_addr(msg->rx_buf)) {
++ WARN_ONCE(1, "xfer msg is not dma capable\n");
++ return -EAGAIN;
++ } else if (object_is_on_stack(msg->tx_buf) ||
++ object_is_on_stack(msg->rx_buf)) {
++ WARN_ONCE(1, "xfer msg is on stack\n");
++ return -EAGAIN;
++ }
++ }
+
+- /**
++ /*
+ * 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
+@@ -125,55 +223,51 @@ static int __peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg,
+ */
+
+ if (do_retry)
+- start = ktime_get();
++ timeout += msecs_to_jiffies(PECI_DEV_RETRY_TIME_MS);
+
+- do {
+- rc = adapter->xfer(adapter, msg);
++ for (;;) {
++ ret = adapter->xfer(adapter, msg);
+
+- if (!do_retry || rc)
+- break;
+-
+- if (msg->rx_buf[0] == DEV_PECI_CC_SUCCESS)
++ if (!do_retry || ret || !msg->rx_buf)
+ 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;
++ if ((msg->rx_buf[0] & PECI_DEV_CC_RETRY_CHECK_MASK) !=
++ PECI_DEV_CC_NEED_RETRY)
+ break;
+- }
+
+ /* Set the retry bit to indicate a retry attempt */
+- msg->tx_buf[1] |= DEV_PECI_RETRY_BIT;
++ msg->tx_buf[1] |= PECI_DEV_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);
++ if (has_aw_fcs) {
++ ret = peci_aw_fcs(msg, 2 + msg->tx_len, &aw_fcs);
++ if (ret)
++ break;
+
+- /**
++ msg->tx_buf[msg->tx_len - 1] = 0x80 ^ aw_fcs;
++ }
++
++ /*
+ * 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) {
++ if (time_after(jiffies, timeout)) {
+ dev_dbg(&adapter->dev, "Timeout retrying xfer!\n");
+- rc = -ETIMEDOUT;
++ ret = -ETIMEDOUT;
+ break;
+ }
+
+- usleep_range((DEV_PECI_RETRY_INTERVAL_USEC >> 2) + 1,
+- DEV_PECI_RETRY_INTERVAL_USEC);
+- } while (true);
++ usleep_range((PECI_DEV_RETRY_INTERVAL_USEC >> 2) + 1,
++ PECI_DEV_RETRY_INTERVAL_USEC);
++ }
+
+- if (rc)
+- dev_dbg(&adapter->dev, "xfer error, rc: %d\n", rc);
++ if (ret)
++ dev_dbg(&adapter->dev, "xfer error: %d\n", ret);
+
+- return rc;
++ return ret;
+ }
+
+ static int peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg)
+@@ -190,34 +284,37 @@ static int peci_xfer_with_retries(struct peci_adapter *adapter,
+
+ static int peci_scan_cmd_mask(struct peci_adapter *adapter)
+ {
+- struct peci_xfer_msg msg;
++ struct peci_xfer_msg *msg;
+ u8 revision;
+- int rc = 0;
++ int ret;
+ 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;
++ msg = peci_get_xfer_msg(PECI_GET_DIB_WR_LEN, PECI_GET_DIB_RD_LEN);
++ if (!msg)
++ return -ENOMEM;
++
++ msg->addr = PECI_BASE_ADDR;
++ msg->tx_buf[0] = PECI_GET_DIB_CMD;
+
+- rc = peci_xfer(adapter, &msg);
+- if (rc)
+- return rc;
++ ret = peci_xfer(adapter, msg);
++ if (ret)
++ return ret;
+
+- dib = le64_to_cpup((__le64 *)msg.rx_buf);
++ 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;
++ ret = -EIO;
++ goto out;
+ }
+
+- /**
+- * Setting up the supporting commands based on minor revision number.
++ /*
++ * Setting up the supporting commands based on revision number.
+ * See PECI Spec Table 3-1.
+ */
+ revision = FIELD_GET(REVISION_NUM_MASK, dib);
+@@ -243,10 +340,14 @@ static int peci_scan_cmd_mask(struct peci_adapter *adapter)
+ adapter->cmd_mask |= BIT(PECI_CMD_GET_DIB);
+ adapter->cmd_mask |= BIT(PECI_CMD_PING);
+
+- return rc;
++out:
++ peci_put_xfer_msg(msg);
++
++ return ret;
+ }
+
+-static int peci_cmd_support(struct peci_adapter *adapter, enum peci_cmd cmd)
++static int peci_check_cmd_support(struct peci_adapter *adapter,
++ enum peci_cmd cmd)
+ {
+ if (!(adapter->cmd_mask & BIT(PECI_CMD_PING)) &&
+ peci_scan_cmd_mask(adapter) < 0) {
+@@ -262,70 +363,130 @@ static int peci_cmd_support(struct peci_adapter *adapter, enum peci_cmd cmd)
+ return 0;
+ }
+
+-static int peci_ioctl_xfer(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_xfer(struct peci_adapter *adapter, void *vmsg)
+ {
+ struct peci_xfer_msg *msg = vmsg;
++ u8 aw_fcs;
++ int ret;
++
++ if (!msg->tx_len) {
++ ret = peci_xfer(adapter, msg);
++ } else {
++ switch (msg->tx_buf[0]) {
++ case PECI_RDPKGCFG_CMD:
++ case PECI_RDIAMSR_CMD:
++ case PECI_RDPCICFG_CMD:
++ case PECI_RDPCICFGLOCAL_CMD:
++ ret = peci_xfer_with_retries(adapter, msg, false);
++ break;
++ case PECI_WRPKGCFG_CMD:
++ case PECI_WRIAMSR_CMD:
++ case PECI_WRPCICFG_CMD:
++ case PECI_WRPCICFGLOCAL_CMD:
++ /* Check if the AW FCS byte is already provided */
++ ret = peci_aw_fcs(msg, 2 + msg->tx_len, &aw_fcs);
++ if (ret)
++ break;
++
++ if (msg->tx_buf[msg->tx_len - 1] != (0x80 ^ aw_fcs)) {
++ /* Add an Assured Write Frame Check Sequence byte */
++ /* Increment the tx_len to include the new byte */
++ msg->tx_len++;
++ ret = peci_aw_fcs(msg, 2 + msg->tx_len,
++ &aw_fcs);
++ if (ret)
++ break;
++
++ msg->tx_buf[msg->tx_len - 1] = 0x80 ^ aw_fcs;
++ }
++
++ ret = peci_xfer_with_retries(adapter, msg, true);
++ break;
++ case PECI_GET_DIB_CMD:
++ case PECI_GET_TEMP_CMD:
++ default:
++ ret = peci_xfer(adapter, msg);
++ break;
++ }
++ }
+
+- return peci_xfer(adapter, msg);
++ return ret;
+ }
+
+-static int peci_ioctl_ping(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_ping(struct peci_adapter *adapter, void *vmsg)
+ {
+ struct peci_ping_msg *umsg = vmsg;
+- struct peci_xfer_msg msg;
++ struct peci_xfer_msg *msg;
++ int ret;
++
++ msg = peci_get_xfer_msg(0, 0);
++ if (!msg)
++ return -ENOMEM;
+
+- msg.addr = umsg->addr;
+- msg.tx_len = 0;
+- msg.rx_len = 0;
++ msg->addr = umsg->addr;
+
+- return peci_xfer(adapter, &msg);
++ ret = peci_xfer(adapter, msg);
++
++ peci_put_xfer_msg(msg);
++
++ return ret;
+ }
+
+-static int peci_ioctl_get_dib(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_get_dib(struct peci_adapter *adapter, void *vmsg)
+ {
+ struct peci_get_dib_msg *umsg = vmsg;
+- struct peci_xfer_msg msg;
+- int rc;
++ struct peci_xfer_msg *msg;
++ int ret;
+
+- 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;
++ msg = peci_get_xfer_msg(PECI_GET_DIB_WR_LEN, PECI_GET_DIB_RD_LEN);
++ if (!msg)
++ return -ENOMEM;
+
+- rc = peci_xfer(adapter, &msg);
+- if (rc)
+- return rc;
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_GET_DIB_CMD;
+
+- umsg->dib = le64_to_cpup((__le64 *)msg.rx_buf);
++ ret = peci_xfer(adapter, msg);
++ if (ret)
++ goto out;
+
+- return 0;
++ umsg->dib = le64_to_cpup((__le64 *)msg->rx_buf);
++
++out:
++ peci_put_xfer_msg(msg);
++
++ return ret;
+ }
+
+-static int peci_ioctl_get_temp(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_get_temp(struct peci_adapter *adapter, void *vmsg)
+ {
+ struct peci_get_temp_msg *umsg = vmsg;
+- struct peci_xfer_msg msg;
+- int rc;
++ struct peci_xfer_msg *msg;
++ int ret;
+
+- 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;
++ msg = peci_get_xfer_msg(PECI_GET_TEMP_WR_LEN, PECI_GET_TEMP_RD_LEN);
++ if (!msg)
++ return -ENOMEM;
+
+- rc = peci_xfer(adapter, &msg);
+- if (rc)
+- return rc;
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_GET_TEMP_CMD;
+
+- umsg->temp_raw = le16_to_cpup((__le16 *)msg.rx_buf);
++ ret = peci_xfer(adapter, msg);
++ if (ret)
++ goto out;
+
+- return 0;
++ umsg->temp_raw = le16_to_cpup((__le16 *)msg->rx_buf);
++
++out:
++ peci_put_xfer_msg(msg);
++
++ return ret;
+ }
+
+-static int peci_ioctl_rd_pkg_cfg(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_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;
++ struct peci_xfer_msg *msg;
++ int ret;
+
+ /* 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) {
+@@ -334,29 +495,35 @@ static int peci_ioctl_rd_pkg_cfg(struct peci_adapter *adapter, void *vmsg)
+ 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 */
++ msg = peci_get_xfer_msg(PECI_RDPKGCFG_WRITE_LEN,
++ PECI_RDPKGCFG_READ_LEN_BASE + umsg->rx_len);
++ if (!msg)
++ return -ENOMEM;
++
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_RDPKGCFG_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 */
++
++ ret = peci_xfer_with_retries(adapter, msg, false);
++ if (!ret)
++ memcpy(umsg->pkg_config, &msg->rx_buf[1], umsg->rx_len);
+
+- rc = peci_xfer_with_retries(adapter, &msg, false);
+- if (!rc)
+- memcpy(umsg->pkg_config, &msg.rx_buf[1], umsg->rx_len);
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
+
+- return rc;
++ return ret;
+ }
+
+-static int peci_ioctl_wr_pkg_cfg(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_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;
++ struct peci_xfer_msg *msg;
++ int ret, i;
++ u8 aw_fcs;
+
+ /* Per the PECI spec, the write length must be a dword */
+ if (umsg->tx_len != 4) {
+@@ -365,86 +532,116 @@ static int peci_ioctl_wr_pkg_cfg(struct peci_adapter *adapter, void *vmsg)
+ 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 */
++ msg = peci_get_xfer_msg(PECI_WRPKGCFG_WRITE_LEN_BASE + umsg->tx_len,
++ PECI_WRPKGCFG_READ_LEN);
++ if (!msg)
++ return -ENOMEM;
++
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_WRPKGCFG_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 */
++ 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));
++ msg->tx_buf[5 + i] = (u8)(umsg->value >> (i << 3));
++
++ /* Add an Assured Write Frame Check Sequence byte */
++ ret = peci_aw_fcs(msg, 8 + umsg->tx_len, &aw_fcs);
++ if (ret)
++ goto out;
++
++ msg->tx_buf[5 + i] = 0x80 ^ aw_fcs;
+
+- /* Add an Assure Write Frame Check Sequence byte */
+- msg.tx_buf[5 + i] = 0x80 ^
+- peci_aw_fcs((u8 *)&msg, 8 + umsg->tx_len);
++ ret = peci_xfer_with_retries(adapter, msg, true);
+
+- rc = peci_xfer_with_retries(adapter, &msg, true);
++out:
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
+
+- return rc;
++ return ret;
+ }
+
+-static int peci_ioctl_rd_ia_msr(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_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;
++ struct peci_xfer_msg *msg;
++ int ret;
++
++ msg = peci_get_xfer_msg(PECI_RDIAMSR_WRITE_LEN, PECI_RDIAMSR_READ_LEN);
++ if (!msg)
++ return -ENOMEM;
++
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_RDIAMSR_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);
++
++ ret = peci_xfer_with_retries(adapter, msg, false);
++ if (!ret)
++ memcpy(&umsg->value, &msg->rx_buf[1], sizeof(uint64_t));
++
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
++
++ return ret;
+ }
+
+-static int peci_ioctl_rd_pci_cfg(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_wr_ia_msr(struct peci_adapter *adapter, void *vmsg)
++{
++ return -ENOSYS; /* Not implemented yet */
++}
++
++static int peci_cmd_rd_pci_cfg(struct peci_adapter *adapter, void *vmsg)
+ {
+ struct peci_rd_pci_cfg_msg *umsg = vmsg;
+- struct peci_xfer_msg msg;
++ struct peci_xfer_msg *msg;
+ u32 address;
+- int rc = 0;
++ int ret;
++
++ msg = peci_get_xfer_msg(PECI_RDPCICFG_WRITE_LEN,
++ PECI_RDPCICFG_READ_LEN);
++ if (!msg)
++ return -ENOMEM;
+
+ 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 */
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_RDPCICFG_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 */
++ 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 */
++
++ ret = peci_xfer_with_retries(adapter, msg, false);
++ if (!ret)
++ memcpy(umsg->pci_config, &msg->rx_buf[1], 4);
++
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
+
+- rc = peci_xfer_with_retries(adapter, &msg, false);
+- if (!rc)
+- memcpy(umsg->pci_config, &msg.rx_buf[1], 4);
++ return ret;
++}
+
+- return rc;
++static int peci_cmd_wr_pci_cfg(struct peci_adapter *adapter, void *vmsg)
++{
++ return -ENOSYS; /* Not implemented yet */
+ }
+
+-static int peci_ioctl_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
+ {
+ struct peci_rd_pci_cfg_local_msg *umsg = vmsg;
+- struct peci_xfer_msg msg;
++ struct peci_xfer_msg *msg;
+ u32 address;
+- int rc = 0;
++ int ret;
+
+ /* 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) {
+@@ -453,34 +650,42 @@ static int peci_ioctl_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
+ return -EINVAL;
+ }
+
++ msg = peci_get_xfer_msg(PECI_RDPCICFGLOCAL_WRITE_LEN,
++ PECI_RDPCICFGLOCAL_READ_LEN_BASE +
++ umsg->rx_len);
++ if (!msg)
++ return -ENOMEM;
++
+ 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 */
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_RDPCICFGLOCAL_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 */
++
++ ret = peci_xfer_with_retries(adapter, msg, false);
++ if (!ret)
++ memcpy(umsg->pci_config, &msg->rx_buf[1], umsg->rx_len);
+
+- rc = peci_xfer_with_retries(adapter, &msg, false);
+- if (!rc)
+- memcpy(umsg->pci_config, &msg.rx_buf[1], umsg->rx_len);
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
+
+- return rc;
++ return ret;
+ }
+
+-static int peci_ioctl_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_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;
++ struct peci_xfer_msg *msg;
+ u32 address;
++ int ret, i;
++ u8 aw_fcs;
+
+ /* 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) {
+@@ -489,47 +694,57 @@ static int peci_ioctl_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
+ return -EINVAL;
+ }
+
++ msg = peci_get_xfer_msg(PECI_WRPCICFGLOCAL_WRITE_LEN_BASE +
++ umsg->tx_len, PECI_WRPCICFGLOCAL_READ_LEN);
++ if (!msg)
++ return -ENOMEM;
++
+ 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 */
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_WRPCICFGLOCAL_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));
++ msg->tx_buf[5 + i] = (u8)(umsg->value >> (i << 3));
++
++ /* Add an Assured Write Frame Check Sequence byte */
++ ret = peci_aw_fcs(msg, 8 + umsg->tx_len, &aw_fcs);
++ if (ret)
++ goto out;
+
+- /* Add an Assure Write Frame Check Sequence byte */
+- msg.tx_buf[5 + i] = 0x80 ^
+- peci_aw_fcs((u8 *)&msg, 8 + umsg->tx_len);
++ msg->tx_buf[5 + i] = 0x80 ^ aw_fcs;
+
+- rc = peci_xfer_with_retries(adapter, &msg, true);
++ ret = peci_xfer_with_retries(adapter, msg, true);
+
+- return rc;
++out:
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
++
++ return ret;
+ }
+
+-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,
++typedef int (*peci_cmd_fn_type)(struct peci_adapter *, void *);
++
++static const peci_cmd_fn_type peci_cmd_fn[PECI_CMD_MAX] = {
++ peci_cmd_xfer,
++ 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,
+ };
+
+ /**
+@@ -545,109 +760,28 @@ static const peci_ioctl_fn_type peci_ioctl_fn[PECI_CMD_MAX] = {
+ */
+ int peci_command(struct peci_adapter *adapter, enum peci_cmd cmd, void *vmsg)
+ {
+- int rc = 0;
++ int ret;
+
+ if (cmd >= PECI_CMD_MAX || cmd < PECI_CMD_XFER)
+- return -EINVAL;
++ return -ENOTTY;
+
+ dev_dbg(&adapter->dev, "%s, cmd=0x%02x\n", __func__, cmd);
+
+- if (!peci_ioctl_fn[cmd])
++ if (!peci_cmd_fn[cmd])
+ return -EINVAL;
+
+- rt_mutex_lock(&adapter->bus_lock);
++ mutex_lock(&adapter->bus_lock);
+
+- rc = peci_cmd_support(adapter, cmd);
+- if (!rc)
+- rc = peci_ioctl_fn[cmd](adapter, vmsg);
++ ret = peci_check_cmd_support(adapter, cmd);
++ if (!ret)
++ ret = peci_cmd_fn[cmd](adapter, vmsg);
+
+- rt_mutex_unlock(&adapter->bus_lock);
++ mutex_unlock(&adapter->bus_lock);
+
+- return rc;
++ return ret;
+ }
+ 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;
+@@ -666,9 +800,9 @@ peci_of_match_device(const struct of_device_id *matches,
+ return NULL;
+
+ return of_match_device(matches, &client->dev);
+-#else
++#else /* CONFIG_OF */
+ return NULL;
+-#endif
++#endif /* CONFIG_OF */
+ }
+
+ static const struct peci_device_id *
+@@ -737,6 +871,7 @@ static int peci_device_probe(struct device *dev)
+
+ err_detach_pm_domain:
+ dev_pm_domain_detach(&client->dev, true);
++
+ return status;
+ }
+
+@@ -775,13 +910,14 @@ static void peci_device_shutdown(struct device *dev)
+ driver->shutdown(client);
+ }
+
+-static struct bus_type peci_bus_type = {
++struct bus_type peci_bus_type = {
+ .name = "peci",
+ .match = peci_device_match,
+ .probe = peci_device_probe,
+ .remove = peci_device_remove,
+ .shutdown = peci_device_shutdown,
+ };
++EXPORT_SYMBOL_GPL(peci_bus_type);
+
+ static int peci_check_addr_validity(u8 addr)
+ {
+@@ -814,18 +950,22 @@ static int peci_check_client_busy(struct device *dev, void *client_new_p)
+ int peci_get_cpu_id(struct peci_adapter *adapter, u8 addr, u32 *cpu_id)
+ {
+ struct peci_rd_pkg_cfg_msg msg;
+- int rc;
++ int ret;
+
+ msg.addr = addr;
+- msg.index = MBX_INDEX_CPU_ID;
+- msg.param = PKG_ID_CPU_ID;
++ msg.index = PECI_MBX_INDEX_CPU_ID;
++ msg.param = PECI_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);
++ ret = peci_command(adapter, PECI_CMD_RD_PKG_CFG, &msg);
++ if (msg.cc != PECI_DEV_CC_SUCCESS)
++ ret = -EAGAIN;
++ if (ret)
++ return ret;
++
++ *cpu_id = le32_to_cpup((__le32 *)msg.pkg_config);
+
+- return rc;
++ return 0;
+ }
+ EXPORT_SYMBOL_GPL(peci_get_cpu_id);
+
+@@ -833,7 +973,7 @@ static struct peci_client *peci_new_device(struct peci_adapter *adapter,
+ struct peci_board_info const *info)
+ {
+ struct peci_client *client;
+- int rc;
++ int ret;
+
+ /* Increase reference count for the adapter assigned */
+ if (!peci_get_adapter(adapter->nr))
+@@ -847,46 +987,49 @@ static struct peci_client *peci_new_device(struct peci_adapter *adapter,
+ client->addr = info->addr;
+ strlcpy(client->name, info->type, sizeof(client->name));
+
+- rc = peci_check_addr_validity(client->addr);
+- if (rc) {
++ ret = peci_check_addr_validity(client->addr);
++ if (ret) {
+ 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)
++ ret = peci_detect(adapter, client->addr);
++ if (ret)
+ goto err_free_client;
+
+- rc = device_for_each_child(&adapter->dev, client,
+- peci_check_client_busy);
+- if (rc)
++ ret = device_for_each_child(&adapter->dev, client,
++ peci_check_client_busy);
++ if (ret)
+ 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;
++ client->dev.of_node = of_node_get(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;
++ ret = device_register(&client->dev);
++ if (ret)
++ goto err_put_of_node;
+
+ dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
+ client->name, dev_name(&client->dev));
+
+ return client;
+
++err_put_of_node:
++ of_node_put(info->of_node);
+ err_free_client:
+ dev_err(&adapter->dev,
+ "Failed to register peci client %s at 0x%02x (%d)\n",
+- client->name, client->addr, rc);
++ client->name, client->addr, ret);
+ err_free_client_silent:
+ kfree(client);
+ err_put_adapter:
+ peci_put_adapter(adapter);
++
+ return NULL;
+ }
+
+@@ -895,8 +1038,10 @@ static void peci_unregister_device(struct peci_client *client)
+ if (!client)
+ return;
+
+- if (client->dev.of_node)
++ if (client->dev.of_node) {
+ of_node_clear_flag(client->dev.of_node, OF_POPULATED);
++ of_node_put(client->dev.of_node);
++ }
+
+ device_unregister(&client->dev);
+ }
+@@ -916,7 +1061,7 @@ static void peci_adapter_dev_release(struct device *dev)
+
+ dev_dbg(dev, "%s: %s\n", __func__, adapter->name);
+ mutex_destroy(&adapter->userspace_clients_lock);
+- rt_mutex_destroy(&adapter->bus_lock);
++ mutex_destroy(&adapter->bus_lock);
+ kfree(adapter);
+ }
+
+@@ -928,7 +1073,8 @@ static ssize_t peci_sysfs_new_device(struct device *dev,
+ struct peci_board_info info = {};
+ struct peci_client *client;
+ char *blank, end;
+- int rc;
++ short addr;
++ int ret;
+
+ /* Parse device type */
+ blank = strchr(buf, ' ');
+@@ -943,16 +1089,17 @@ static ssize_t peci_sysfs_new_device(struct device *dev,
+ memcpy(info.type, buf, blank - buf);
+
+ /* Parse remaining parameters, reject extra parameters */
+- rc = sscanf(++blank, "%hi%c", &info.addr, &end);
+- if (rc < 1) {
++ ret = sscanf(++blank, "%hi%c", &addr, &end);
++ if (ret < 1) {
+ dev_err(dev, "%s: Can't parse client address\n", "new_device");
+ return -EINVAL;
+ }
+- if (rc > 1 && end != '\n') {
++ if (ret > 1 && end != '\n') {
+ dev_err(dev, "%s: Extra parameters\n", "new_device");
+ return -EINVAL;
+ }
+
++ info.addr = (u8)addr;
+ client = peci_new_device(adapter, &info);
+ if (!client)
+ return -EINVAL;
+@@ -961,8 +1108,8 @@ static ssize_t peci_sysfs_new_device(struct device *dev,
+ 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);
++ dev_dbg(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device",
++ info.type, info.addr);
+
+ return count;
+ }
+@@ -975,9 +1122,9 @@ static ssize_t peci_sysfs_delete_device(struct device *dev,
+ 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;
++ short addr;
++ int ret;
+
+ /* Parse device type */
+ blank = strchr(buf, ' ');
+@@ -992,41 +1139,41 @@ static ssize_t peci_sysfs_delete_device(struct device *dev,
+ memcpy(info.type, buf, blank - buf);
+
+ /* Parse remaining parameters, reject extra parameters */
+- rc = sscanf(++blank, "%hi%c", &info.addr, &end);
+- if (rc < 1) {
++ ret = sscanf(++blank, "%hi%c", &addr, &end);
++ if (ret < 1) {
+ dev_err(dev, "%s: Can't parse client address\n",
+ "delete_device");
+ return -EINVAL;
+ }
+- if (rc > 1 && end != '\n') {
++ if (ret > 1 && end != '\n') {
+ dev_err(dev, "%s: Extra parameters\n", "delete_device");
+ return -EINVAL;
+ }
+
++ info.addr = (u8)addr;
++
+ /* Make sure the device was added through sysfs */
+- rc = -ENOENT;
++ ret = -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);
++ dev_dbg(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;
++ ret = count;
+ break;
+ }
+ }
+ mutex_unlock(&adapter->userspace_clients_lock);
+
+- if (rc < 0)
+- dev_err(dev, "%s: Can't find device in list\n",
++ if (ret < 0)
++ dev_dbg(dev, "%s: Can't find device in list\n",
+ "delete_device");
+
+- return rc;
++ return ret;
+ }
+ static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, 0200, NULL,
+ peci_sysfs_delete_device);
+@@ -1039,10 +1186,11 @@ static struct attribute *peci_adapter_attrs[] = {
+ };
+ ATTRIBUTE_GROUPS(peci_adapter);
+
+-static struct device_type peci_adapter_type = {
++struct device_type peci_adapter_type = {
+ .groups = peci_adapter_groups,
+ .release = peci_adapter_dev_release,
+ };
++EXPORT_SYMBOL_GPL(peci_adapter_type);
+
+ /**
+ * peci_verify_adapter - return parameter as peci_adapter, or NULL
+@@ -1063,32 +1211,26 @@ 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;
++ struct peci_client *client;
++ u32 addr;
++ int ret;
+
+ 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)) {
++ ret = of_property_read_u32(node, "reg", &addr);
++ if (ret) {
+ dev_err(&adapter->dev, "invalid reg on %pOF\n", node);
+- return ERR_PTR(-EINVAL);
++ return ERR_PTR(ret);
+ }
+
+- info.addr = be32_to_cpup(addr_be);
+- info.of_node = of_node_get(node);
++ info.addr = addr;
++ info.of_node = node;
+
+- result = peci_new_device(adapter, &info);
+- if (!result)
+- result = ERR_PTR(-EINVAL);
++ client = peci_new_device(adapter, &info);
++ if (!client)
++ client = ERR_PTR(-EINVAL);
+
+- of_node_put(node);
+- return result;
++ return client;
+ }
+
+ static void peci_of_register_devices(struct peci_adapter *adapter)
+@@ -1119,7 +1261,7 @@ static void peci_of_register_devices(struct peci_adapter *adapter)
+
+ of_node_put(bus);
+ }
+-#else
++#else /* CONFIG_OF */
+ static void peci_of_register_devices(struct peci_adapter *adapter) { }
+ #endif /* CONFIG_OF */
+
+@@ -1163,9 +1305,7 @@ static struct peci_adapter *peci_of_find_adapter(struct device_node *node)
+ return adapter;
+ }
+
+-static int peci_of_notify(struct notifier_block *nb,
+- unsigned long action,
+- void *arg)
++static int peci_of_notify(struct notifier_block *nb, ulong action, void *arg)
+ {
+ struct of_reconfig_data *rd = arg;
+ struct peci_adapter *adapter;
+@@ -1216,7 +1356,7 @@ static int peci_of_notify(struct notifier_block *nb,
+ static struct notifier_block peci_of_notifier = {
+ .notifier_call = peci_of_notify,
+ };
+-#else
++#else /* CONFIG_OF_DYNAMIC */
+ extern struct notifier_block peci_of_notifier;
+ #endif /* CONFIG_OF_DYNAMIC */
+
+@@ -1240,7 +1380,7 @@ extern struct notifier_block peci_of_notifier;
+ *
+ * Return: the peci_adapter structure on success, else NULL.
+ */
+-struct peci_adapter *peci_alloc_adapter(struct device *dev, unsigned int size)
++struct peci_adapter *peci_alloc_adapter(struct device *dev, uint size)
+ {
+ struct peci_adapter *adapter;
+
+@@ -1263,7 +1403,7 @@ EXPORT_SYMBOL_GPL(peci_alloc_adapter);
+
+ static int peci_register_adapter(struct peci_adapter *adapter)
+ {
+- int rc = -EINVAL;
++ int ret = -EINVAL;
+
+ /* Can't register until after driver model init */
+ if (WARN_ON(!is_registered))
+@@ -1275,27 +1415,17 @@ static int peci_register_adapter(struct peci_adapter *adapter)
+ if (WARN(!adapter->xfer, "peci adapter has no xfer function\n"))
+ goto err_free_idr;
+
+- rt_mutex_init(&adapter->bus_lock);
++ 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) {
++ ret = device_add(&adapter->dev);
++ if (ret) {
+ pr_err("adapter '%s': can't add device (%d)\n",
+- adapter->name, rc);
+- goto err_del_cdev;
++ adapter->name, ret);
++ goto err_free_idr;
+ }
+
+ dev_dbg(&adapter->dev, "adapter [%s] registered\n", adapter->name);
+@@ -1309,13 +1439,11 @@ static int peci_register_adapter(struct peci_adapter *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;
++ return ret;
+ }
+
+ static int peci_add_numbered_adapter(struct peci_adapter *adapter)
+@@ -1354,12 +1482,10 @@ 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);
+- }
++ 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);
+@@ -1411,7 +1537,7 @@ void peci_del_adapter(struct peci_adapter *adapter)
+ }
+ mutex_unlock(&adapter->userspace_clients_lock);
+
+- /**
++ /*
+ * Detach any active clients. This can't fail, thus we do not
+ * check the returned value.
+ */
+@@ -1420,13 +1546,8 @@ void peci_del_adapter(struct peci_adapter *adapter)
+ /* 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 */
+@@ -1436,6 +1557,18 @@ void peci_del_adapter(struct peci_adapter *adapter)
+ }
+ EXPORT_SYMBOL_GPL(peci_del_adapter);
+
++int peci_for_each_dev(void *data, int (*fn)(struct device *, void *))
++{
++ int ret;
++
++ mutex_lock(&core_lock);
++ ret = bus_for_each_dev(&peci_bus_type, NULL, data, fn);
++ mutex_unlock(&core_lock);
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(peci_for_each_dev);
++
+ /**
+ * peci_register_driver - register a PECI driver
+ * @owner: owner module of the driver being registered
+@@ -1446,7 +1579,7 @@ EXPORT_SYMBOL_GPL(peci_del_adapter);
+ */
+ int peci_register_driver(struct module *owner, struct peci_driver *driver)
+ {
+- int rc;
++ int ret;
+
+ /* Can't register until after driver model init */
+ if (WARN_ON(!is_registered))
+@@ -1456,13 +1589,13 @@ int peci_register_driver(struct module *owner, struct peci_driver *driver)
+ 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;
++ ret = driver_register(&driver->driver);
++ if (ret)
++ return ret;
+
+ pr_debug("driver [%s] registered\n", driver->driver.name);
+
+@@ -1492,13 +1625,6 @@ static int __init peci_init(void)
+ 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))
+@@ -1514,11 +1640,10 @@ 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);
++subsys_initcall(peci_init);
+ module_exit(peci_exit);
+
+ MODULE_AUTHOR("Jason M Biils <jason.m.bills@linux.intel.com>");
+diff --git a/drivers/peci/peci-dev.c b/drivers/peci/peci-dev.c
+new file mode 100644
+index 0000000..ac9cba0
+--- /dev/null
++++ b/drivers/peci/peci-dev.c
+@@ -0,0 +1,346 @@
++// SPDX-License-Identifier: GPL-2.0
++// Copyright (c) 2018-2019 Intel Corporation
++
++#include <linux/cdev.h>
++#include <linux/fs.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/notifier.h>
++#include <linux/peci.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++
++/*
++ * A peci_dev represents an peci_adapter ... an PECI or SMBus master, not a
++ * slave (peci_client) with which messages will be exchanged. It's coupled
++ * with a character special file which is accessed by user mode drivers.
++ *
++ * The list of peci_dev structures is parallel to the peci_adapter lists
++ * maintained by the driver model, and is updated using bus notifications.
++ */
++struct peci_dev {
++ struct list_head list;
++ struct peci_adapter *adapter;
++ struct device *dev;
++ struct cdev cdev;
++};
++
++#define PECI_MINORS MINORMASK
++
++static dev_t peci_devt;
++static LIST_HEAD(peci_dev_list);
++static DEFINE_SPINLOCK(peci_dev_list_lock);
++
++static struct peci_dev *peci_dev_get_by_minor(uint index)
++{
++ struct peci_dev *peci_dev;
++
++ spin_lock(&peci_dev_list_lock);
++ list_for_each_entry(peci_dev, &peci_dev_list, list) {
++ if (peci_dev->adapter->nr == index)
++ goto found;
++ }
++ peci_dev = NULL;
++found:
++ spin_unlock(&peci_dev_list_lock);
++
++ return peci_dev;
++}
++
++static struct peci_dev *peci_dev_alloc(struct peci_adapter *adapter)
++{
++ struct peci_dev *peci_dev;
++
++ if (adapter->nr >= PECI_MINORS) {
++ printk(KERN_ERR "peci-dev: Out of device minors (%d)\n",
++ adapter->nr);
++ return ERR_PTR(-ENODEV);
++ }
++
++ peci_dev = kzalloc(sizeof(*peci_dev), GFP_KERNEL);
++ if (!peci_dev)
++ return ERR_PTR(-ENOMEM);
++ peci_dev->adapter = adapter;
++
++ spin_lock(&peci_dev_list_lock);
++ list_add_tail(&peci_dev->list, &peci_dev_list);
++ spin_unlock(&peci_dev_list_lock);
++
++ return peci_dev;
++}
++
++static void peci_dev_put(struct peci_dev *peci_dev)
++{
++ spin_lock(&peci_dev_list_lock);
++ list_del(&peci_dev->list);
++ spin_unlock(&peci_dev_list_lock);
++ kfree(peci_dev);
++}
++
++static ssize_t name_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct peci_dev *peci_dev = peci_dev_get_by_minor(MINOR(dev->devt));
++
++ if (!peci_dev)
++ return -ENODEV;
++
++ return sprintf(buf, "%s\n", peci_dev->adapter->name);
++}
++static DEVICE_ATTR_RO(name);
++
++static struct attribute *peci_dev_attrs[] = {
++ &dev_attr_name.attr,
++ NULL,
++};
++ATTRIBUTE_GROUPS(peci_dev);
++
++static long peci_dev_ioctl(struct file *file, uint iocmd, ulong arg)
++{
++ struct peci_dev *peci_dev = file->private_data;
++ void __user *umsg = (void __user *)arg;
++ struct peci_xfer_msg *xmsg = NULL;
++ struct peci_xfer_msg uxmsg;
++ enum peci_cmd cmd;
++ u8 *msg = NULL;
++ uint msg_len;
++ int ret;
++
++ cmd = _IOC_NR(iocmd);
++ msg_len = _IOC_SIZE(iocmd);
++
++ switch (cmd) {
++ case PECI_CMD_XFER:
++ if (msg_len != sizeof(struct peci_xfer_msg)) {
++ ret = -EFAULT;
++ break;
++ }
++
++ if (copy_from_user(&uxmsg, umsg, msg_len)) {
++ ret = -EFAULT;
++ break;
++ }
++
++ xmsg = peci_get_xfer_msg(uxmsg.tx_len, uxmsg.rx_len);
++ if (IS_ERR(xmsg)) {
++ ret = PTR_ERR(xmsg);
++ break;
++ }
++
++ if (uxmsg.tx_len &&
++ copy_from_user(xmsg->tx_buf, uxmsg.tx_buf, uxmsg.tx_len)) {
++ ret = -EFAULT;
++ break;
++ }
++
++ xmsg->addr = uxmsg.addr;
++ xmsg->tx_len = uxmsg.tx_len;
++ xmsg->rx_len = uxmsg.rx_len;
++
++ ret = peci_command(peci_dev->adapter, cmd, xmsg);
++ if (!ret && xmsg->rx_len &&
++ copy_to_user(uxmsg.rx_buf, xmsg->rx_buf, xmsg->rx_len))
++ ret = -EFAULT;
++
++ break;
++
++ default:
++ msg = memdup_user(umsg, msg_len);
++ if (IS_ERR(msg)) {
++ ret = PTR_ERR(msg);
++ break;
++ }
++
++ ret = peci_command(peci_dev->adapter, cmd, msg);
++ if ((!ret || ret == -ETIMEDOUT) &&
++ copy_to_user(umsg, msg, msg_len))
++ ret = -EFAULT;
++
++ break;
++ }
++
++ peci_put_xfer_msg(xmsg);
++ kfree(msg);
++
++ return (long)ret;
++}
++
++static int peci_dev_open(struct inode *inode, struct file *file)
++{
++ struct peci_adapter *adapter;
++ struct peci_dev *peci_dev;
++
++ peci_dev = peci_dev_get_by_minor(iminor(inode));
++ if (!peci_dev)
++ return -ENODEV;
++
++ adapter = peci_get_adapter(peci_dev->adapter->nr);
++ if (!adapter)
++ return -ENODEV;
++
++ file->private_data = peci_dev;
++
++ return 0;
++}
++
++static int peci_dev_release(struct inode *inode, struct file *file)
++{
++ struct peci_dev *peci_dev = file->private_data;
++
++ peci_put_adapter(peci_dev->adapter);
++ file->private_data = NULL;
++
++ return 0;
++}
++
++static const struct file_operations peci_dev_fops = {
++ .owner = THIS_MODULE,
++ .unlocked_ioctl = peci_dev_ioctl,
++ .open = peci_dev_open,
++ .release = peci_dev_release,
++ .llseek = no_llseek,
++};
++
++static struct class *peci_dev_class;
++
++static int peci_dev_attach_adapter(struct device *dev, void *dummy)
++{
++ struct peci_adapter *adapter;
++ struct peci_dev *peci_dev;
++ dev_t devt;
++ int ret;
++
++ if (dev->type != &peci_adapter_type)
++ return 0;
++
++ adapter = to_peci_adapter(dev);
++ peci_dev = peci_dev_alloc(adapter);
++ if (IS_ERR(peci_dev))
++ return PTR_ERR(peci_dev);
++
++ cdev_init(&peci_dev->cdev, &peci_dev_fops);
++ peci_dev->cdev.owner = THIS_MODULE;
++ devt = MKDEV(MAJOR(peci_devt), adapter->nr);
++
++ ret = cdev_add(&peci_dev->cdev, devt, 1);
++ if (ret)
++ goto err_put_dev;
++
++ /* register this peci device with the driver core */
++ peci_dev->dev = device_create(peci_dev_class, &adapter->dev, devt, NULL,
++ "peci-%d", adapter->nr);
++ if (IS_ERR(peci_dev->dev)) {
++ ret = PTR_ERR(peci_dev->dev);
++ goto err_del_cdev;
++ }
++
++ pr_info("peci-dev: adapter [%s] registered as minor %d\n",
++ adapter->name, adapter->nr);
++
++ return 0;
++
++err_del_cdev:
++ cdev_del(&peci_dev->cdev);
++err_put_dev:
++ peci_dev_put(peci_dev);
++
++ return ret;
++}
++
++static int peci_dev_detach_adapter(struct device *dev, void *dummy)
++{
++ struct peci_adapter *adapter;
++ struct peci_dev *peci_dev;
++ dev_t devt;
++
++ if (dev->type != &peci_adapter_type)
++ return 0;
++
++ adapter = to_peci_adapter(dev);
++ peci_dev = peci_dev_get_by_minor(adapter->nr);
++ if (!peci_dev)
++ return 0;
++
++ cdev_del(&peci_dev->cdev);
++ devt = peci_dev->dev->devt;
++ peci_dev_put(peci_dev);
++ device_destroy(peci_dev_class, devt);
++
++ pr_info("peci-dev: adapter [%s] unregistered\n", adapter->name);
++
++ return 0;
++}
++
++static int peci_dev_notifier_call(struct notifier_block *nb, ulong action,
++ void *data)
++{
++ struct device *dev = data;
++
++ switch (action) {
++ case BUS_NOTIFY_ADD_DEVICE:
++ return peci_dev_attach_adapter(dev, NULL);
++ case BUS_NOTIFY_DEL_DEVICE:
++ return peci_dev_detach_adapter(dev, NULL);
++ }
++
++ return 0;
++}
++
++static struct notifier_block peci_dev_notifier = {
++ .notifier_call = peci_dev_notifier_call,
++};
++
++static int __init peci_dev_init(void)
++{
++ int ret;
++
++ printk(KERN_INFO "peci /dev entries driver\n");
++
++ ret = alloc_chrdev_region(&peci_devt, 0, PECI_MINORS, "peci");
++ if (ret < 0) {
++ pr_err("peci: Failed to allocate chr dev region!\n");
++ bus_unregister(&peci_bus_type);
++ goto err;
++ }
++
++ peci_dev_class = class_create(THIS_MODULE, "peci-dev");
++ if (IS_ERR(peci_dev_class)) {
++ ret = PTR_ERR(peci_dev_class);
++ goto err_unreg_chrdev;
++ }
++ peci_dev_class->dev_groups = peci_dev_groups;
++
++ /* Keep track of adapters which will be added or removed later */
++ ret = bus_register_notifier(&peci_bus_type, &peci_dev_notifier);
++ if (ret)
++ goto err_destroy_class;
++
++ /* Bind to already existing adapters right away */
++ peci_for_each_dev(NULL, peci_dev_attach_adapter);
++
++ return 0;
++
++err_destroy_class:
++ class_destroy(peci_dev_class);
++err_unreg_chrdev:
++ unregister_chrdev_region(peci_devt, PECI_MINORS);
++err:
++ printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
++
++ return ret;
++}
++
++static void __exit peci_dev_exit(void)
++{
++ bus_unregister_notifier(&peci_bus_type, &peci_dev_notifier);
++ peci_for_each_dev(NULL, peci_dev_detach_adapter);
++ class_destroy(peci_dev_class);
++ unregister_chrdev_region(peci_devt, PECI_MINORS);
++}
++
++module_init(peci_dev_init);
++module_exit(peci_dev_exit);
++
++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
++MODULE_DESCRIPTION("PECI /dev entries driver");
++MODULE_LICENSE("GPL v2");
+diff --git a/drivers/peci/peci-npcm.c b/drivers/peci/peci-npcm.c
+deleted file mode 100644
+index f632365..0000000
+--- a/drivers/peci/peci-npcm.c
++++ /dev/null
+@@ -1,410 +0,0 @@
+-// 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/include/linux/mfd/intel-peci-client.h b/include/linux/mfd/intel-peci-client.h
+index 8f6d823..9854303 100644
+--- a/include/linux/mfd/intel-peci-client.h
++++ b/include/linux/mfd/intel-peci-client.h
+@@ -1,5 +1,5 @@
+ /* SPDX-License-Identifier: GPL-2.0 */
+-/* Copyright (c) 2018 Intel Corporation */
++/* Copyright (c) 2018-2019 Intel Corporation */
+
+ #ifndef __LINUX_MFD_INTEL_PECI_CLIENT_H
+ #define __LINUX_MFD_INTEL_PECI_CLIENT_H
+@@ -9,14 +9,15 @@
+ #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
++#define INTEL_FAM6_HASWELL_X 0x3F
++#define INTEL_FAM6_BROADWELL_X 0x4F
++#define INTEL_FAM6_SKYLAKE_X 0x55
++#define INTEL_FAM6_SKYLAKE_XD 0x56
+ #endif
+
+ #define CORE_MAX_ON_HSX 18 /* Max number of cores on Haswell */
+@@ -31,6 +32,10 @@
+ #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_MAX_ON_SKXD 16 /* Max number of cores on Skylake D */
++#define CHAN_RANK_MAX_ON_SKXD 2 /* Max number of channel ranks on Skylake D */
++#define DIMM_IDX_MAX_ON_SKXD 2 /* Max DIMM index per channel on Skylake D */
++
+ #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
+@@ -58,7 +63,6 @@ struct cpu_gen_info {
+ /**
+ * 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
+ *
+@@ -67,7 +71,6 @@ struct cpu_gen_info {
+ */
+ struct peci_client_manager {
+ struct peci_client *client;
+- struct device *dev;
+ char name[PECI_NAME_SIZE];
+ const struct cpu_gen_info *gen_info;
+ };
+@@ -93,18 +96,22 @@ 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;
++ int ret;
+
+ 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);
++ ret = peci_command(priv->client->adapter, PECI_CMD_RD_PKG_CFG, &msg);
++ if (msg.cc != PECI_DEV_CC_SUCCESS)
++ ret = -EAGAIN;
++ if (ret)
++ return ret;
++
++ memcpy(data, msg.pkg_config, 4);
+
+- return rc;
++ return 0;
+ }
+
+ #endif /* __LINUX_MFD_INTEL_PECI_CLIENT_H */
+diff --git a/include/linux/peci.h b/include/linux/peci.h
+index d0e47d4..6fc424d 100644
+--- a/include/linux/peci.h
++++ b/include/linux/peci.h
+@@ -1,19 +1,18 @@
+ /* SPDX-License-Identifier: GPL-2.0 */
+-/* Copyright (c) 2018 Intel Corporation */
++/* Copyright (c) 2018-2019 Intel Corporation */
+
+ #ifndef __LINUX_PECI_H
+ #define __LINUX_PECI_H
+
+-#include <linux/cdev.h>
+ #include <linux/device.h>
++#include <linux/mutex.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 */
++ u8 addr; /* CPU client address */
+ struct device_node *of_node;
+ };
+
+@@ -22,29 +21,29 @@ struct peci_board_info {
+ * @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
++ * @use_dma: flag for indicating that adapter uses DMA
+ *
+ * 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 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;
++ u32 cmd_mask;
++ bool use_dma;
+ };
+
+ static inline struct peci_adapter *to_peci_adapter(void *d)
+@@ -87,8 +86,8 @@ static inline struct peci_client *to_peci_client(void *d)
+ }
+
+ struct peci_device_id {
+- char name[PECI_NAME_SIZE];
+- unsigned long driver_data; /* Data private to the driver */
++ char name[PECI_NAME_SIZE];
++ ulong driver_data; /* Data private to the driver */
+ };
+
+ /**
+@@ -129,13 +128,22 @@ static inline struct peci_driver *to_peci_driver(void *d)
+ /* use a define to avoid include chaining to get THIS_MODULE */
+ #define peci_add_driver(driver) peci_register_driver(THIS_MODULE, driver)
+
++extern struct bus_type peci_bus_type;
++extern struct device_type peci_adapter_type;
++extern struct device_type peci_client_type;
++
+ 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);
++struct peci_adapter *peci_alloc_adapter(struct device *dev, uint size);
++struct peci_adapter *peci_get_adapter(int nr);
++void peci_put_adapter(struct peci_adapter *adapter);
+ 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_for_each_dev(void *data, int (*fn)(struct device *, void *));
++struct peci_xfer_msg *peci_get_xfer_msg(u8 tx_len, u8 rx_len);
++void peci_put_xfer_msg(struct peci_xfer_msg *msg);
+ 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);
+
+diff --git a/include/uapi/linux/peci-ioctl.h b/include/uapi/linux/peci-ioctl.h
+index a6dae71..253fb42 100644
+--- a/include/uapi/linux/peci-ioctl.h
++++ b/include/uapi/linux/peci-ioctl.h
+@@ -1,5 +1,5 @@
+ /* SPDX-License-Identifier: GPL-2.0 */
+-/* Copyright (c) 2018 Intel Corporation */
++/* Copyright (c) 2018-2019 Intel Corporation */
+
+ #ifndef __PECI_IOCTL_H
+ #define __PECI_IOCTL_H
+@@ -7,136 +7,35 @@
+ #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
++/* The PECI client's default address of 0x30 */
++#define PECI_BASE_ADDR 0x30
++
++/* Max number of CPU clients */
++#define PECI_OFFSET_MAX 8
++
++/* PECI read/write data buffer size max */
++#define PECI_BUFFER_SIZE 255
+
+ /* 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
++#define PECI_DEV_CC_SUCCESS 0x40
++#define PECI_DEV_CC_NEED_RETRY 0x80
++#define PECI_DEV_CC_OUT_OF_RESOURCE 0x81
++#define PECI_DEV_CC_UNAVAIL_RESOURCE 0x82
++#define PECI_DEV_CC_INVALID_REQ 0x90
++#define PECI_DEV_CC_MCA_ERROR 0x91
++#define PECI_DEV_CC_CATASTROPHIC_MCA_ERROR 0x93
++#define PECI_DEV_CC_FATAL_MCA_DETECTED 0x94
++#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB 0x98
++#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB_IERR 0x9B
++#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB_MCA 0x9C
+
+ /* Completion Code mask to check retry needs */
+-#define DEV_PECI_CC_RETRY_CHECK_MASK 0xf0
+-#define DEV_PECI_CC_NEED_RETRY 0x80
++#define PECI_DEV_CC_RETRY_CHECK_MASK 0xf0
+
+ /* 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
++#define PECI_DEV_RETRY_TIME_MS 250
++#define PECI_DEV_RETRY_INTERVAL_USEC 10000
++#define PECI_DEV_RETRY_BIT 0x01
+
+ /**
+ * enum peci_cmd - PECI client commands
+@@ -186,11 +85,12 @@ enum peci_cmd {
+ * 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];
++ __u8 addr;
++ __u8 tx_len;
++ __u8 rx_len;
++ __u8 padding;
++ __u8 *tx_buf;
++ __u8 *rx_buf;
+ } __attribute__((__packed__));
+
+ /**
+@@ -202,7 +102,8 @@ struct peci_xfer_msg {
+ * powered-off, etc.
+ */
+ struct peci_ping_msg {
+- __u8 addr;
++ __u8 addr;
++ __u8 padding[3];
+ } __attribute__((__packed__));
+
+ /**
+@@ -216,8 +117,13 @@ struct peci_ping_msg {
+ * command.
+ */
+ struct peci_get_dib_msg {
+- __u8 addr;
+- __u64 dib;
++#define PECI_GET_DIB_WR_LEN 1
++#define PECI_GET_DIB_RD_LEN 8
++#define PECI_GET_DIB_CMD 0xf7
++
++ __u8 addr;
++ __u8 padding[3];
++ __u64 dib;
+ } __attribute__((__packed__));
+
+ /**
+@@ -232,8 +138,13 @@ struct peci_get_dib_msg {
+ * below the maximum processor junction temperature.
+ */
+ struct peci_get_temp_msg {
+- __u8 addr;
+- __s16 temp_raw;
++#define PECI_GET_TEMP_WR_LEN 1
++#define PECI_GET_TEMP_RD_LEN 2
++#define PECI_GET_TEMP_CMD 0x01
++
++ __u8 addr;
++ __u8 padding;
++ __s16 temp_raw;
+ } __attribute__((__packed__));
+
+ /**
+@@ -242,6 +153,7 @@ struct peci_get_temp_msg {
+ * @index: encoding index for the requested service
+ * @param: specific data being requested
+ * @rx_len: number of data to be read in bytes
++ * @cc: completion code
+ * @pkg_config: package config data to be read
+ *
+ * The RdPkgConfig() command provides read access to the Package Configuration
+@@ -251,11 +163,73 @@ struct peci_get_temp_msg {
+ * DIMM temperatures and so on.
+ */
+ struct peci_rd_pkg_cfg_msg {
+- __u8 addr;
+- __u8 index;
+- __u16 param;
+- __u8 rx_len;
+- __u8 pkg_config[4];
++#define PECI_RDPKGCFG_WRITE_LEN 5
++#define PECI_RDPKGCFG_READ_LEN_BASE 1
++#define PECI_RDPKGCFG_CMD 0xa1
++
++ __u8 addr;
++ __u8 index;
++#define PECI_MBX_INDEX_CPU_ID 0 /* Package Identifier Read */
++#define PECI_MBX_INDEX_VR_DEBUG 1 /* VR Debug */
++#define PECI_MBX_INDEX_PKG_TEMP_READ 2 /* Package Temperature Read */
++#define PECI_MBX_INDEX_ENERGY_COUNTER 3 /* Energy counter */
++#define PECI_MBX_INDEX_ENERGY_STATUS 4 /* DDR Energy Status */
++#define PECI_MBX_INDEX_WAKE_MODE_BIT 5 /* "Wake on PECI" Mode bit */
++#define PECI_MBX_INDEX_EPI 6 /* Efficient Performance Indication */
++#define PECI_MBX_INDEX_PKG_RAPL_PERF 8 /* Pkg RAPL Performance Status Read */
++#define PECI_MBX_INDEX_PER_CORE_DTS_TEMP 9 /* Per Core DTS Temperature Read */
++#define PECI_MBX_INDEX_DTS_MARGIN 10 /* DTS thermal margin */
++#define PECI_MBX_INDEX_SKT_PWR_THRTL_DUR 11 /* Socket Power Throttled Duration */
++#define PECI_MBX_INDEX_CFG_TDP_CONTROL 12 /* TDP Config Control */
++#define PECI_MBX_INDEX_CFG_TDP_LEVELS 13 /* TDP Config Levels */
++#define PECI_MBX_INDEX_DDR_DIMM_TEMP 14 /* DDR DIMM Temperature */
++#define PECI_MBX_INDEX_CFG_ICCMAX 15 /* Configurable ICCMAX */
++#define PECI_MBX_INDEX_TEMP_TARGET 16 /* Temperature Target Read */
++#define PECI_MBX_INDEX_CURR_CFG_LIMIT 17 /* Current Config Limit */
++#define PECI_MBX_INDEX_DIMM_TEMP_READ 20 /* Package Thermal Status Read */
++#define PECI_MBX_INDEX_DRAM_IMC_TMP_READ 22 /* DRAM IMC Temperature Read */
++#define PECI_MBX_INDEX_DDR_CH_THERM_STAT 23 /* DDR Channel Thermal Status */
++#define PECI_MBX_INDEX_PKG_POWER_LIMIT1 26 /* Package Power Limit1 */
++#define PECI_MBX_INDEX_PKG_POWER_LIMIT2 27 /* Package Power Limit2 */
++#define PECI_MBX_INDEX_TDP 28 /* Thermal design power minimum */
++#define PECI_MBX_INDEX_TDP_HIGH 29 /* Thermal design power maximum */
++#define PECI_MBX_INDEX_TDP_UNITS 30 /* Units for power/energy registers */
++#define PECI_MBX_INDEX_RUN_TIME 31 /* Accumulated Run Time */
++#define PECI_MBX_INDEX_CONSTRAINED_TIME 32 /* Thermally Constrained Time Read */
++#define PECI_MBX_INDEX_TURBO_RATIO 33 /* Turbo Activation Ratio */
++#define PECI_MBX_INDEX_DDR_RAPL_PL1 34 /* DDR RAPL PL1 */
++#define PECI_MBX_INDEX_DDR_PWR_INFO_HIGH 35 /* DRAM Power Info Read (high) */
++#define PECI_MBX_INDEX_DDR_PWR_INFO_LOW 36 /* DRAM Power Info Read (low) */
++#define PECI_MBX_INDEX_DDR_RAPL_PL2 37 /* DDR RAPL PL2 */
++#define PECI_MBX_INDEX_DDR_RAPL_STATUS 38 /* DDR RAPL Performance Status */
++#define PECI_MBX_INDEX_DDR_HOT_ABSOLUTE 43 /* DDR Hottest Dimm Absolute Temp */
++#define PECI_MBX_INDEX_DDR_HOT_RELATIVE 44 /* DDR Hottest Dimm Relative Temp */
++#define PECI_MBX_INDEX_DDR_THROTTLE_TIME 45 /* DDR Throttle Time */
++#define PECI_MBX_INDEX_DDR_THERM_STATUS 46 /* DDR Thermal Status */
++#define PECI_MBX_INDEX_TIME_AVG_TEMP 47 /* Package time-averaged temperature */
++#define PECI_MBX_INDEX_TURBO_RATIO_LIMIT 49 /* Turbo Ratio Limit Read */
++#define PECI_MBX_INDEX_HWP_AUTO_OOB 53 /* HWP Autonomous Out-of-band */
++#define PECI_MBX_INDEX_DDR_WARM_BUDGET 55 /* DDR Warm Power Budget */
++#define PECI_MBX_INDEX_DDR_HOT_BUDGET 56 /* DDR Hot Power Budget */
++#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM3 57 /* Package/Psys Power Limit3 */
++#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM1 58 /* Package/Psys Power Limit1 */
++#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM2 59 /* Package/Psys Power Limit2 */
++#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM4 60 /* Package/Psys Power Limit4 */
++#define PECI_MBX_INDEX_PERF_LIMIT_REASON 65 /* Performance Limit Reasons */
++
++ __u16 param;
++/* When index is PECI_MBX_INDEX_CPU_ID */
++#define PECI_PKG_ID_CPU_ID 0x0000 /* CPUID Info */
++#define PECI_PKG_ID_PLATFORM_ID 0x0001 /* Platform ID */
++#define PECI_PKG_ID_UNCORE_ID 0x0002 /* Uncore Device ID */
++#define PECI_PKG_ID_MAX_THREAD_ID 0x0003 /* Max Thread ID */
++#define PECI_PKG_ID_MICROCODE_REV 0x0004 /* CPU Microcode Update Revision */
++#define PECI_PKG_ID_MACHINE_CHECK_STATUS 0x0005 /* Machine Check Status */
++
++ __u8 rx_len;
++ __u8 cc;
++ __u8 padding[2];
++ __u8 pkg_config[4];
+ } __attribute__((__packed__));
+
+ /**
+@@ -264,6 +238,7 @@ struct peci_rd_pkg_cfg_msg {
+ * @index: encoding index for the requested service
+ * @param: specific data being requested
+ * @tx_len: number of data to be written in bytes
++ * @cc: completion code
+ * @value: package config data to be written
+ *
+ * The WrPkgConfig() command provides write access to the Package Configuration
+@@ -272,11 +247,20 @@ struct peci_rd_pkg_cfg_msg {
+ * 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;
++#define PECI_WRPKGCFG_WRITE_LEN_BASE 6
++#define PECI_WRPKGCFG_READ_LEN 1
++#define PECI_WRPKGCFG_CMD 0xa5
++
++ __u8 addr;
++ __u8 index;
++#define PECI_MBX_INDEX_DIMM_AMBIENT 19
++#define PECI_MBX_INDEX_DIMM_TEMP 24
++
++ __u16 param;
++ __u8 tx_len;
++ __u8 cc;
++ __u8 padding[2];
++ __u32 value;
+ } __attribute__((__packed__));
+
+ /**
+@@ -284,16 +268,47 @@ struct peci_wr_pkg_cfg_msg {
+ * @addr: address of the client
+ * @thread_id: ID of the specific logical processor
+ * @address: address of MSR to read from
++ * @cc: completion code
+ * @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;
++#define PECI_RDIAMSR_WRITE_LEN 5
++#define PECI_RDIAMSR_READ_LEN 9
++#define PECI_RDIAMSR_CMD 0xb1
++
++ __u8 addr;
++ __u8 thread_id;
++ __u16 address;
++ __u8 cc;
++ __u8 padding[3];
++ __u64 value;
++} __attribute__((__packed__));
++
++/**
++ * struct peci_wr_ia_msr_msg - WrIAMSR command
++ * @addr: address of the client
++ * @thread_id: ID of the specific logical processor
++ * @address: address of MSR to write to
++ * @tx_len: number of data to be written in bytes
++ * @cc: completion code
++ * @value: data to be written
++ *
++ * The WrIAMSR() PECI command provides write access to Model Specific Registers
++ * (MSRs) defined in the processor's Intel Architecture (IA).
++ */
++struct peci_wr_ia_msr_msg {
++#define PECI_WRIAMSR_CMD 0xb5
++
++ __u8 addr;
++ __u8 thread_id;
++ __u16 address;
++ __u8 tx_len;
++ __u8 cc;
++ __u8 padding[2];
++ __u64 value;
+ } __attribute__((__packed__));
+
+ /**
+@@ -303,6 +318,7 @@ struct peci_rd_ia_msr_msg {
+ * @device: PCI device number
+ * @function: specific function to read from
+ * @reg: specific register to read from
++ * @cc: completion code
+ * @pci_config: config data to be read
+ *
+ * The RdPCIConfig() command provides sideband read access to the PCI
+@@ -310,12 +326,56 @@ struct peci_rd_ia_msr_msg {
+ * processor.
+ */
+ struct peci_rd_pci_cfg_msg {
+- __u8 addr;
+- __u8 bus;
+- __u8 device;
+- __u8 function;
+- __u16 reg;
+- __u8 pci_config[4];
++#define PECI_RDPCICFG_WRITE_LEN 6
++#define PECI_RDPCICFG_READ_LEN 5
++#define PECI_RDPCICFG_READ_LEN_MAX 24
++#define PECI_RDPCICFG_CMD 0x61
++
++ __u8 addr;
++ __u8 bus;
++#define PECI_PCI_BUS0_CPU0 0x00
++#define PECI_PCI_BUS0_CPU1 0x80
++#define PECI_PCI_CPUBUSNO_BUS 0x00
++#define PECI_PCI_CPUBUSNO_DEV 0x08
++#define PECI_PCI_CPUBUSNO_FUNC 0x02
++#define PECI_PCI_CPUBUSNO 0xcc
++#define PECI_PCI_CPUBUSNO_1 0xd0
++#define PECI_PCI_CPUBUSNO_VALID 0xd4
++
++ __u8 device;
++ __u8 function;
++ __u16 reg;
++ __u8 cc;
++ __u8 padding[1];
++ __u8 pci_config[4];
++} __attribute__((__packed__));
++
++/**
++ * struct peci_wr_pci_cfg_msg - WrPCIConfig command
++ * @addr: address of the client
++ * @bus: PCI bus number
++ * @device: PCI device number
++ * @function: specific function to write to
++ * @reg: specific register to write to
++ * @tx_len: number of data to be written in bytes
++ * @cc: completion code
++ * @pci_config: config data to be written
++ *
++ * The RdPCIConfig() command provides sideband write access to the PCI
++ * configuration space maintained in downstream devices external to the
++ * processor.
++ */
++struct peci_wr_pci_cfg_msg {
++#define PECI_WRPCICFG_CMD 0x65
++
++ __u8 addr;
++ __u8 bus;
++ __u8 device;
++ __u8 function;
++ __u16 reg;
++ __u8 tx_len;
++ __u8 cc;
++ __u8 pci_config[4];
+ } __attribute__((__packed__));
+
+ /**
+@@ -326,6 +386,7 @@ struct peci_rd_pci_cfg_msg {
+ * @function: specific function to read from
+ * @reg: specific register to read from
+ * @rx_len: number of data to be read in bytes
++ * @cc: completion code
+ * @pci_config: config data to be read
+ *
+ * The RdPCIConfigLocal() command provides sideband read access to the PCI
+@@ -333,13 +394,18 @@ struct peci_rd_pci_cfg_msg {
+ * 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];
++#define PECI_RDPCICFGLOCAL_WRITE_LEN 5
++#define PECI_RDPCICFGLOCAL_READ_LEN_BASE 1
++#define PECI_RDPCICFGLOCAL_CMD 0xe1
++
++ __u8 addr;
++ __u8 bus;
++ __u8 device;
++ __u8 function;
++ __u16 reg;
++ __u8 rx_len;
++ __u8 cc;
++ __u8 pci_config[4];
+ } __attribute__((__packed__));
+
+ /**
+@@ -350,6 +416,7 @@ struct peci_rd_pci_cfg_local_msg {
+ * @function: specific function to read from
+ * @reg: specific register to read from
+ * @tx_len: number of data to be written in bytes
++ * @cc: completion code
+ * @value: config data to be written
+ *
+ * The WrPCIConfigLocal() command provides sideband write access to the PCI
+@@ -357,13 +424,18 @@ struct peci_rd_pci_cfg_local_msg {
+ * 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;
++#define PECI_WRPCICFGLOCAL_WRITE_LEN_BASE 6
++#define PECI_WRPCICFGLOCAL_READ_LEN 1
++#define PECI_WRPCICFGLOCAL_CMD 0xe5
++
++ __u8 addr;
++ __u8 bus;
++ __u8 device;
++ __u8 function;
++ __u16 reg;
++ __u8 tx_len;
++ __u8 cc;
++ __u32 value;
+ } __attribute__((__packed__));
+
+ #define PECI_IOC_BASE 0xb7
+@@ -389,9 +461,15 @@ struct peci_wr_pci_cfg_local_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_WR_IA_MSR \
++ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_IA_MSR, struct peci_wr_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_WR_PCI_CFG \
++ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_PCI_CFG, struct peci_wr_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)
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0019-Add-I2C-IPMB-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0019-Add-I2C-IPMB-support.patch
new file mode 100644
index 000000000..675125322
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0019-Add-I2C-IPMB-support.patch
@@ -0,0 +1,425 @@
+From f588865f8180a6370ac639bdfc186ffc5b926246 Mon Sep 17 00:00:00 2001
+From: Haiyue Wang <haiyue.wang@linux.intel.com>
+Date: Tue, 13 Feb 2018 14:28:12 +0800
+Subject: [PATCH] i2c: slave-mqueue: add mqueue driver to receive ipmi message
+
+Some protocols over I2C are designed for bi-directional transferring
+messages by using I2C Master Write protocol. Like the MCTP (Management
+Component Transport Protocol) and IPMB (Intelligent Platform Management
+Bus), they both require that the userspace can receive messages from
+I2C dirvers under slave mode.
+
+This new slave mqueue backend is used to receive and queue messages, it
+will exposes these messages to userspace by sysfs bin file.
+
+Signed-off-by: Haiyue Wang <haiyue.wang@linux.intel.com>
+---
+ Documentation/i2c/slave-mqueue-backend.rst | 124 +++++++++++++++++
+ drivers/i2c/Kconfig | 23 +++
+ drivers/i2c/Makefile | 1 +
+ drivers/i2c/i2c-slave-mqueue.c | 217 +++++++++++++++++++++++++++++
+ 4 files changed, 365 insertions(+)
+ create mode 100644 Documentation/i2c/slave-mqueue-backend.rst
+ create mode 100644 drivers/i2c/i2c-slave-mqueue.c
+
+diff --git a/Documentation/i2c/slave-mqueue-backend.rst b/Documentation/i2c/slave-mqueue-backend.rst
+new file mode 100644
+index 000000000000..2d0d06d8df9d
+--- /dev/null
++++ b/Documentation/i2c/slave-mqueue-backend.rst
+@@ -0,0 +1,124 @@
++.. SPDX-License-Identifier: GPL-2.0
++
++=====================================
++Linux I2C slave message queue backend
++=====================================
++
++:Author: Haiyue Wang <haiyue.wang@linux.intel.com>
++
++Some protocols over I2C/SMBus are designed for bi-directional transferring
++messages by using I2C Master Write protocol. This requires that both sides
++of the communication have slave addresses.
++
++Like MCTP (Management Component Transport Protocol) and IPMB (Intelligent
++Platform Management Bus), they both require that the userspace can receive
++messages from i2c dirvers under slave mode.
++
++This I2C slave mqueue (message queue) backend is used to receive and queue
++messages from the remote i2c intelligent device; and it will add the target
++slave address (with R/W# bit is always 0) into the message at the first byte,
++so that userspace can use this byte to dispatch the messages into different
++handling modules. Also, like IPMB, the address byte is in its message format,
++it needs it to do checksum.
++
++For messages are time related, so this backend will flush the oldest message
++to queue the newest one.
++
++Link
++----
++`Intelligent Platform Management Bus
++Communications Protocol Specification
++<https://www.intel.com/content/dam/www/public/us/en/documents/product-briefs/ipmp-spec-v1.0.pdf>`_
++
++`Management Component Transport Protocol (MCTP)
++SMBus/I2C Transport Binding Specification
++<https://www.dmtf.org/sites/default/files/standards/documents/DSP0237_1.1.0.pdf>`_
++
++How to use
++----------
++For example, the I2C5 bus has slave address 0x10, the below command will create
++the related message queue interface:
++
++ echo slave-mqueue 0x1010 > /sys/bus/i2c/devices/i2c-5/new_device
++
++Then you can dump the messages like this:
++
++ hexdump -C /sys/bus/i2c/devices/5-1010/slave-mqueue
++
++Code Example
++------------
++*Note: call 'lseek' before 'read', this is a requirement from kernfs' design.*
++
++::
++
++ #include <sys/types.h>
++ #include <sys/stat.h>
++ #include <unistd.h>
++ #include <poll.h>
++ #include <time.h>
++ #include <fcntl.h>
++ #include <stdio.h>
++
++ int main(int argc, char *argv[])
++ {
++ int i, r;
++ struct pollfd pfd;
++ struct timespec ts;
++ unsigned char data[256];
++
++ pfd.fd = open(argv[1], O_RDONLY | O_NONBLOCK);
++ if (pfd.fd < 0)
++ return -1;
++
++ pfd.events = POLLPRI;
++
++ while (1) {
++ r = poll(&pfd, 1, 5000);
++
++ if (r < 0)
++ break;
++
++ if (r == 0 || !(pfd.revents & POLLPRI))
++ continue;
++
++ lseek(pfd.fd, 0, SEEK_SET);
++ r = read(pfd.fd, data, sizeof(data));
++ if (r <= 0)
++ continue;
++
++ clock_gettime(CLOCK_MONOTONIC, &ts);
++ printf("[%ld.%.9ld] :", ts.tv_sec, ts.tv_nsec);
++ for (i = 0; i < r; i++)
++ printf(" %02x", data[i]);
++ printf("\n");
++ }
++
++ close(pfd.fd);
++
++ return 0;
++ }
++
++Result
++------
++*./a.out "/sys/bus/i2c/devices/5-1010/slave-mqueue"*
++
++::
++
++ [10183.232500449] : 20 18 c8 2c 78 01 5b
++ [10183.479358348] : 20 18 c8 2c 78 01 5b
++ [10183.726556812] : 20 18 c8 2c 78 01 5b
++ [10183.972605863] : 20 18 c8 2c 78 01 5b
++ [10184.220124772] : 20 18 c8 2c 78 01 5b
++ [10184.467764166] : 20 18 c8 2c 78 01 5b
++ [10193.233421784] : 20 18 c8 2c 7c 01 57
++ [10193.480273460] : 20 18 c8 2c 7c 01 57
++ [10193.726788733] : 20 18 c8 2c 7c 01 57
++ [10193.972781945] : 20 18 c8 2c 7c 01 57
++ [10194.220487360] : 20 18 c8 2c 7c 01 57
++ [10194.468089259] : 20 18 c8 2c 7c 01 57
++ [10203.233433099] : 20 18 c8 2c 80 01 53
++ [10203.481058715] : 20 18 c8 2c 80 01 53
++ [10203.727610472] : 20 18 c8 2c 80 01 53
++ [10203.974044856] : 20 18 c8 2c 80 01 53
++ [10204.220734634] : 20 18 c8 2c 80 01 53
++ [10204.468461664] : 20 18 c8 2c 80 01 53
+diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
+index abedd55a1264..49a2379876c6 100644
+--- a/drivers/i2c/Kconfig
++++ b/drivers/i2c/Kconfig
+@@ -119,6 +119,29 @@ if I2C_SLAVE
+ config I2C_SLAVE_EEPROM
+ tristate "I2C eeprom slave driver"
+
++config I2C_SLAVE_MQUEUE_MESSAGE_SIZE
++ int "The message size of I2C mqueue slave"
++ default 120
++
++config I2C_SLAVE_MQUEUE_QUEUE_SIZE
++ int "The queue size of I2C mqueue slave"
++ default 32
++ help
++ This number MUST be power of 2.
++
++config I2C_SLAVE_MQUEUE
++ tristate "I2C mqueue (message queue) slave driver"
++ help
++ Some protocols over I2C are designed for bi-directional transferring
++ messages by using I2C Master Write protocol. This driver is used to
++ receive and queue messages from the remote I2C device.
++
++ Userspace can get the messages by reading sysfs file that this driver
++ exposes.
++
++ This support is also available as a module. If so, the module will be
++ called i2c-slave-mqueue.
++
+ endif
+
+ config I2C_DEBUG_CORE
+diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
+index bed6ba63c983..9a31bc75a446 100644
+--- a/drivers/i2c/Makefile
++++ b/drivers/i2c/Makefile
+@@ -16,5 +16,6 @@ obj-$(CONFIG_I2C_MUX) += i2c-mux.o
+ obj-y += algos/ busses/ muxes/
+ obj-$(CONFIG_I2C_STUB) += i2c-stub.o
+ obj-$(CONFIG_I2C_SLAVE_EEPROM) += i2c-slave-eeprom.o
++obj-$(CONFIG_I2C_SLAVE_MQUEUE) += i2c-slave-mqueue.o
+
+ ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG
+diff --git a/drivers/i2c/i2c-slave-mqueue.c b/drivers/i2c/i2c-slave-mqueue.c
+new file mode 100644
+index 000000000000..6014bca0ff2a
+--- /dev/null
++++ b/drivers/i2c/i2c-slave-mqueue.c
+@@ -0,0 +1,217 @@
++// SPDX-License-Identifier: GPL-2.0
++// Copyright (c) 2017 - 2018, Intel Corporation.
++
++#include <linux/i2c.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/sysfs.h>
++
++#define MQ_MSGBUF_SIZE CONFIG_I2C_SLAVE_MQUEUE_MESSAGE_SIZE
++#define MQ_QUEUE_SIZE CONFIG_I2C_SLAVE_MQUEUE_QUEUE_SIZE
++#define MQ_QUEUE_NEXT(x) (((x) + 1) & (MQ_QUEUE_SIZE - 1))
++
++struct mq_msg {
++ int len;
++ u8 *buf;
++};
++
++struct mq_queue {
++ struct bin_attribute bin;
++ struct kernfs_node *kn;
++
++ spinlock_t lock; /* spinlock for queue index handling */
++ int in;
++ int out;
++
++ struct mq_msg *curr;
++ int truncated; /* drop current if truncated */
++ struct mq_msg *queue;
++};
++
++static int i2c_slave_mqueue_callback(struct i2c_client *client,
++ enum i2c_slave_event event, u8 *val)
++{
++ struct mq_queue *mq = i2c_get_clientdata(client);
++ struct mq_msg *msg = mq->curr;
++ int ret = 0;
++
++ switch (event) {
++ case I2C_SLAVE_WRITE_REQUESTED:
++ mq->truncated = 0;
++
++ msg->len = 1;
++ msg->buf[0] = client->addr << 1;
++ break;
++
++ case I2C_SLAVE_WRITE_RECEIVED:
++ if (msg->len < MQ_MSGBUF_SIZE) {
++ msg->buf[msg->len++] = *val;
++ } else {
++ dev_err(&client->dev, "message is truncated!\n");
++ mq->truncated = 1;
++ ret = -EINVAL;
++ }
++ break;
++
++ case I2C_SLAVE_STOP:
++ if (unlikely(mq->truncated || msg->len < 2))
++ break;
++
++ spin_lock(&mq->lock);
++ mq->in = MQ_QUEUE_NEXT(mq->in);
++ mq->curr = &mq->queue[mq->in];
++ mq->curr->len = 0;
++
++ /* Flush the oldest message */
++ if (mq->out == mq->in)
++ mq->out = MQ_QUEUE_NEXT(mq->out);
++ spin_unlock(&mq->lock);
++
++ kernfs_notify(mq->kn);
++ break;
++
++ default:
++ *val = 0xFF;
++ break;
++ }
++
++ return ret;
++}
++
++static ssize_t i2c_slave_mqueue_bin_read(struct file *filp,
++ struct kobject *kobj,
++ struct bin_attribute *attr,
++ char *buf, loff_t pos, size_t count)
++{
++ struct mq_queue *mq;
++ struct mq_msg *msg;
++ unsigned long flags;
++ bool more = false;
++ ssize_t ret = 0;
++
++ mq = dev_get_drvdata(container_of(kobj, struct device, kobj));
++
++ spin_lock_irqsave(&mq->lock, flags);
++ if (mq->out != mq->in) {
++ msg = &mq->queue[mq->out];
++
++ if (msg->len <= count) {
++ ret = msg->len;
++ memcpy(buf, msg->buf, ret);
++ } else {
++ ret = -EOVERFLOW; /* Drop this HUGE one. */
++ }
++
++ mq->out = MQ_QUEUE_NEXT(mq->out);
++ if (mq->out != mq->in)
++ more = true;
++ }
++ spin_unlock_irqrestore(&mq->lock, flags);
++
++ if (more)
++ kernfs_notify(mq->kn);
++
++ return ret;
++}
++
++static int i2c_slave_mqueue_probe(struct i2c_client *client,
++ const struct i2c_device_id *id)
++{
++ struct device *dev = &client->dev;
++ struct mq_queue *mq;
++ int ret, i;
++ void *buf;
++
++ mq = devm_kzalloc(dev, sizeof(*mq), GFP_KERNEL);
++ if (!mq)
++ return -ENOMEM;
++
++ BUILD_BUG_ON(!is_power_of_2(MQ_QUEUE_SIZE));
++
++ buf = devm_kmalloc_array(dev, MQ_QUEUE_SIZE, MQ_MSGBUF_SIZE,
++ GFP_KERNEL);
++ if (!buf)
++ return -ENOMEM;
++
++ mq->queue = devm_kzalloc(dev, sizeof(*mq->queue) * MQ_QUEUE_SIZE,
++ GFP_KERNEL);
++ if (!buf)
++ return -ENOMEM;
++
++ for (i = 0; i < MQ_QUEUE_SIZE; i++)
++ mq->queue[i].buf = buf + i * MQ_MSGBUF_SIZE;
++
++ i2c_set_clientdata(client, mq);
++
++ spin_lock_init(&mq->lock);
++ mq->curr = &mq->queue[0];
++
++ sysfs_bin_attr_init(&mq->bin);
++ mq->bin.attr.name = "slave-mqueue";
++ mq->bin.attr.mode = 0400;
++ mq->bin.read = i2c_slave_mqueue_bin_read;
++ mq->bin.size = MQ_MSGBUF_SIZE * MQ_QUEUE_SIZE;
++
++ ret = sysfs_create_bin_file(&dev->kobj, &mq->bin);
++ if (ret)
++ return ret;
++
++ mq->kn = kernfs_find_and_get(dev->kobj.sd, mq->bin.attr.name);
++ if (!mq->kn) {
++ sysfs_remove_bin_file(&dev->kobj, &mq->bin);
++ return -EFAULT;
++ }
++
++ ret = i2c_slave_register(client, i2c_slave_mqueue_callback);
++ if (ret) {
++ kernfs_put(mq->kn);
++ sysfs_remove_bin_file(&dev->kobj, &mq->bin);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int i2c_slave_mqueue_remove(struct i2c_client *client)
++{
++ struct mq_queue *mq = i2c_get_clientdata(client);
++
++ i2c_slave_unregister(client);
++
++ kernfs_put(mq->kn);
++ sysfs_remove_bin_file(&client->dev.kobj, &mq->bin);
++
++ return 0;
++}
++
++static const struct i2c_device_id i2c_slave_mqueue_id[] = {
++ { "slave-mqueue", 0 },
++ { }
++};
++MODULE_DEVICE_TABLE(i2c, i2c_slave_mqueue_id);
++
++#if IS_ENABLED(CONFIG_OF)
++static const struct of_device_id i2c_slave_mqueue_of_match[] = {
++ { .compatible = "slave-mqueue", .data = (void *)0 },
++ { },
++};
++MODULE_DEVICE_TABLE(of, i2c_slave_mqueue_of_match);
++#endif
++
++static struct i2c_driver i2c_slave_mqueue_driver = {
++ .driver = {
++ .name = "i2c-slave-mqueue",
++ .of_match_table = of_match_ptr(i2c_slave_mqueue_of_match),
++ },
++ .probe = i2c_slave_mqueue_probe,
++ .remove = i2c_slave_mqueue_remove,
++ .id_table = i2c_slave_mqueue_id,
++};
++module_i2c_driver(i2c_slave_mqueue_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
++MODULE_DESCRIPTION("I2C slave mode for receiving and queuing messages");
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0020-misc-aspeed-add-lpc-mbox-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0020-misc-aspeed-add-lpc-mbox-driver.patch
new file mode 100644
index 000000000..a444d39b3
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0020-misc-aspeed-add-lpc-mbox-driver.patch
@@ -0,0 +1,475 @@
+From c1567ac196f176b19b53b6c4e7949809fd01e334 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Wed, 10 Jul 2019 16:19:33 -0700
+Subject: [PATCH] misc: aspeed: add lpc mbox driver
+
+This commit adds back the lpc mbox driver which was removed from
+the openbmc linux dev-5.2 tree.
+
+This driver should be rewritten later.
+
+Signed-off-by: Cyril Bur <cyrilbur@gmail.com>"
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ arch/arm/boot/dts/aspeed-g4.dtsi | 9 +
+ arch/arm/boot/dts/aspeed-g5.dtsi | 9 +
+ drivers/soc/aspeed/Kconfig | 7 +
+ drivers/soc/aspeed/Makefile | 1 +
+ drivers/soc/aspeed/aspeed-lpc-mbox.c | 376 +++++++++++++++++++++++++++++++++++
+ 5 files changed, 402 insertions(+)
+ create mode 100644 drivers/soc/aspeed/aspeed-lpc-mbox.c
+
+diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
+index ee86b41af291..b7b6e8aa3a12 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -394,6 +394,15 @@
+ sio_regs: regs {
+ compatible = "aspeed,bmc-misc";
+ };
++
++ mbox: mbox@180 {
++ compatible = "aspeed,ast2400-mbox";
++ reg = <0x180 0x5c>;
++ interrupts = <46>;
++ #mbox-cells = <1>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
++ status = "disabled";
++ };
+ };
+ };
+
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index 128e0b5bbae2..12a81155f1ab 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -503,6 +503,15 @@
+ sio_regs: regs {
+ compatible = "aspeed,bmc-misc";
+ };
++
++ mbox: mbox@180 {
++ compatible = "aspeed,ast2500-mbox";
++ reg = <0x180 0x5c>;
++ interrupts = <46>;
++ #mbox-cells = <1>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
++ status = "disabled";
++ };
+ };
+ };
+
+diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig
+index 78dd74c49ddb..a4be8e566bc7 100644
+--- a/drivers/soc/aspeed/Kconfig
++++ b/drivers/soc/aspeed/Kconfig
+@@ -21,6 +21,13 @@ config ASPEED_LPC_CTRL
+ ioctl()s, the driver also provides a read/write interface to a BMC ram
+ region where the host LPC read/write region can be buffered.
+
++config ASPEED_LPC_MBOX
++ tristate "Aspeed LPC Mailbox Controller"
++ depends on SOC_ASPEED && REGMAP && MFD_SYSCON
++ ---help---
++ Expose the ASPEED LPC MBOX registers found on Aspeed SOCs (AST2400
++ and AST2500) to userspace.
++
+ config ASPEED_LPC_SNOOP
+ tristate "Aspeed ast2500 HOST LPC snoop support"
+ depends on SOC_ASPEED && REGMAP && MFD_SYSCON
+diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile
+index e631b23d519b..f3ff29b874ed 100644
+--- a/drivers/soc/aspeed/Makefile
++++ b/drivers/soc/aspeed/Makefile
+@@ -1,5 +1,6 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ obj-$(CONFIG_ASPEED_BMC_MISC) += aspeed-bmc-misc.o
+ obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
++obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o
+ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
+ obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o
+diff --git a/drivers/soc/aspeed/aspeed-lpc-mbox.c b/drivers/soc/aspeed/aspeed-lpc-mbox.c
+new file mode 100644
+index 000000000000..795107206022
+--- /dev/null
++++ b/drivers/soc/aspeed/aspeed-lpc-mbox.c
+@@ -0,0 +1,376 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++// Copyright 2017 IBM Corporation
++// TODO: Rewrite this driver
++
++#include <linux/clk.h>
++#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 MBX_USE_INTERRUPT 0
++
++#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;
++ struct clk *clk;
++ unsigned int base;
++ int irq;
++ 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)
++{
++#if MBX_USE_INTERRUPT
++ struct aspeed_mbox *mbox = file_mbox(file);
++#endif
++
++ if (atomic_inc_return(&aspeed_mbox_open_count) == 1) {
++#if MBX_USE_INTERRUPT
++ /*
++ * 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);
++#endif
++ 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 MBX_USE_INTERRUPT
++ 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;
++ }
++#endif
++
++ 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--;
++ }
++
++#if MBX_USE_INTERRUPT
++ /* 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);
++#endif
++ 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, 0xff, ASPEED_MBOX_STATUS_0);
++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STATUS_1);
++ aspeed_mbox_outb(mbox, ASPEED_MBOX_CTRL_RECV | ASPEED_MBOX_CTRL_MASK | 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 MBX_USE_INTERRUPT
++ if (aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL) & ASPEED_MBOX_CTRL_RECV)
++#endif
++ 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 MBX_USE_INTERRUPT
++ int i;
++
++// if (!(aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL) & ASPEED_MBOX_CTRL_RECV))
++// return IRQ_NONE;
++
++ printk(KERN_ERR "BMC_CTRL: 0x%02x\n",
++ aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL));
++ printk(KERN_ERR "STATUS_0: 0x%02x\n",
++ aspeed_mbox_inb(mbox, ASPEED_MBOX_STATUS_0));
++ printk(KERN_ERR "STATUS_1: 0x%02x\n",
++ aspeed_mbox_inb(mbox, ASPEED_MBOX_STATUS_1));
++ for (i = 0; i < ASPEED_MBOX_NUM_REGS; i++) {
++ printk(KERN_ERR "DATA_%d: 0x%02x\n", i,
++ aspeed_mbox_inb(mbox, ASPEED_MBOX_DATA_0 + (i * 4)));
++ }
++#endif
++
++ /* Clear interrupt status */
++ 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);
++
++ 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;
++
++ mbox->irq = platform_get_irq(pdev, 0);
++ if (!mbox->irq)
++ return -ENODEV;
++
++ rc = devm_request_irq(dev, mbox->irq, aspeed_mbox_irq,
++ IRQF_SHARED, DEVICE_NAME, mbox);
++ if (rc < 0) {
++ dev_err(dev, "Unable to request IRQ %d\n", mbox->irq);
++ return rc;
++ }
++
++ /* Disable all register based interrupts. */
++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_INTERRUPT_0); /* regs 0 - 7 */
++ aspeed_mbox_outb(mbox, 0xff, 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->clk = devm_clk_get(dev, NULL);
++ if (IS_ERR(mbox->clk)) {
++ rc = PTR_ERR(mbox->clk);
++ if (rc != -EPROBE_DEFER)
++ dev_err(dev, "couldn't get clock\n");
++ return rc;
++ }
++ rc = clk_prepare_enable(mbox->clk);
++ if (rc) {
++ dev_err(dev, "couldn't enable clock\n");
++ return rc;
++ }
++
++ 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");
++ goto err;
++ }
++
++ rc = aspeed_mbox_config_irq(mbox, pdev);
++ if (rc) {
++ dev_err(dev, "Failed to configure IRQ\n");
++ misc_deregister(&mbox->miscdev);
++ goto err;
++ }
++
++ dev_info(&pdev->dev, "LPC mbox registered, irq %d\n", mbox->irq);
++
++ return 0;
++
++err:
++ clk_disable_unprepare(mbox->clk);
++
++ return rc;
++}
++
++static int aspeed_mbox_remove(struct platform_device *pdev)
++{
++ struct aspeed_mbox *mbox = dev_get_drvdata(&pdev->dev);
++
++ misc_deregister(&mbox->miscdev);
++ clk_disable_unprepare(mbox->clk);
++
++ return 0;
++}
++
++static const struct of_device_id aspeed_mbox_match[] = {
++ { .compatible = "aspeed,ast2400-mbox" },
++ { .compatible = "aspeed,ast2500-mbox" },
++ { },
++};
++MODULE_DEVICE_TABLE(of, aspeed_mbox_match);
++
++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_LICENSE("GPL");
++MODULE_AUTHOR("Cyril Bur <cyrilbur@gmail.com>");
++MODULE_DESCRIPTION("Aspeed mailbox device driver");
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch
new file mode 100644
index 000000000..24eca1bb9
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch
@@ -0,0 +1,619 @@
+From 450b6d6e58ca9954fd4b675da8b6bb25d21c020f Mon Sep 17 00:00:00 2001
+From: Yong Li <yong.b.li@intel.com>
+Date: Mon, 13 Nov 2017 16:29:44 +0800
+Subject: [PATCH] Aspeed LPC SIO driver
+
+Add lpc sio device driver for AST2500/2400
+
+Signed-off-by: Yong Li <yong.b.li@intel.com>
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ .../bindings/soc/aspeed/aspeed-lpc-sio.txt | 17 +
+ arch/arm/boot/dts/aspeed-g4.dtsi | 7 +
+ arch/arm/boot/dts/aspeed-g5.dtsi | 7 +
+ drivers/soc/aspeed/Kconfig | 7 +
+ drivers/soc/aspeed/Makefile | 1 +
+ drivers/soc/aspeed/aspeed-lpc-sio.c | 450 +++++++++++++++++++++
+ include/uapi/linux/aspeed-lpc-sio.h | 44 ++
+ 7 files changed, 533 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/soc/aspeed/aspeed-lpc-sio.txt
+ create mode 100644 drivers/soc/aspeed/aspeed-lpc-sio.c
+ create mode 100644 include/uapi/linux/aspeed-lpc-sio.h
+
+diff --git a/Documentation/devicetree/bindings/soc/aspeed/aspeed-lpc-sio.txt b/Documentation/devicetree/bindings/soc/aspeed/aspeed-lpc-sio.txt
+new file mode 100644
+index 000000000000..c74ea3a4e5ac
+--- /dev/null
++++ b/Documentation/devicetree/bindings/soc/aspeed/aspeed-lpc-sio.txt
+@@ -0,0 +1,17 @@
++* Aspeed LPC SIO driver.
++
++Required properties:
++- compatible : Should be one of:
++ "aspeed,ast2400-lpc-sio"
++ "aspeed,ast2500-lpc-sio"
++- reg : Should contain lpc-sio registers location and length
++- clocks: contains a phandle to the syscon node describing the clocks.
++ There should then be one cell representing the clock to use.
++
++Example:
++lpc_sio: lpc-sio@100 {
++ compatible = "aspeed,ast2500-lpc-sio";
++ reg = <0x100 0x20>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
++ status = "disabled";
++};
+diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
+index b7b6e8aa3a12..71563972d2fe 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -395,6 +395,13 @@
+ compatible = "aspeed,bmc-misc";
+ };
+
++ lpc_sio: lpc-sio@100 {
++ compatible = "aspeed,ast2400-lpc-sio";
++ reg = <0x100 0x20>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
++ status = "disabled";
++ };
++
+ mbox: mbox@180 {
+ compatible = "aspeed,ast2400-mbox";
+ reg = <0x180 0x5c>;
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index 12a81155f1ab..88f75736fe48 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -504,6 +504,13 @@
+ compatible = "aspeed,bmc-misc";
+ };
+
++ lpc_sio: lpc-sio@100 {
++ compatible = "aspeed,ast2500-lpc-sio";
++ reg = <0x100 0x20>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
++ status = "disabled";
++ };
++
+ mbox: mbox@180 {
+ compatible = "aspeed,ast2500-mbox";
+ reg = <0x180 0x5c>;
+diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig
+index a4be8e566bc7..285c19042c65 100644
+--- a/drivers/soc/aspeed/Kconfig
++++ b/drivers/soc/aspeed/Kconfig
+@@ -28,6 +28,13 @@ config ASPEED_LPC_MBOX
+ Expose the ASPEED LPC MBOX registers found on Aspeed SOCs (AST2400
+ and AST2500) to userspace.
+
++config ASPEED_LPC_SIO
++ tristate "Aspeed ast2400/2500 HOST LPC SIO support"
++ depends on SOC_ASPEED && REGMAP && MFD_SYSCON
++ help
++ Provides a driver to control the LPC SIO interface on ASPEED platform
++ through ioctl()s.
++
+ config ASPEED_LPC_SNOOP
+ tristate "Aspeed ast2500 HOST LPC snoop support"
+ depends on SOC_ASPEED && REGMAP && MFD_SYSCON
+diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile
+index f3ff29b874ed..2e547cc47e62 100644
+--- a/drivers/soc/aspeed/Makefile
++++ b/drivers/soc/aspeed/Makefile
+@@ -2,5 +2,6 @@
+ obj-$(CONFIG_ASPEED_BMC_MISC) += aspeed-bmc-misc.o
+ obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
+ obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o
++obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.o
+ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
+ obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o
+diff --git a/drivers/soc/aspeed/aspeed-lpc-sio.c b/drivers/soc/aspeed/aspeed-lpc-sio.c
+new file mode 100644
+index 000000000000..c717a3182320
+--- /dev/null
++++ b/drivers/soc/aspeed/aspeed-lpc-sio.c
+@@ -0,0 +1,450 @@
++// SPDX-License-Identifier: GPL-2.0
++// Copyright (C) 2012-2017 ASPEED Technology Inc.
++// Copyright (c) 2017-2019 Intel Corporation
++
++#include <linux/clk.h>
++#include <linux/mfd/syscon.h>
++#include <linux/miscdevice.h>
++#include <linux/mm.h>
++#include <linux/module.h>
++#include <linux/of_address.h>
++#include <linux/platform_device.h>
++#include <linux/poll.h>
++#include <linux/regmap.h>
++
++#include <linux/aspeed-lpc-sio.h>
++
++#define SOC_NAME "aspeed"
++#define DEVICE_NAME "lpc-sio"
++
++#define AST_LPC_SWCR0300 0x0
++#define LPC_PWRGD_STS (1 << 30)
++#define LPC_PWRGD_RISING_EVT_STS (1 << 29)
++#define LPC_PWRGD_FALLING_EVT_STS (1 << 28)
++#define LPC_PWRBTN_STS (1 << 27)
++#define LPC_PWRBTN_RISING_EVT_STS (1 << 26)
++#define LPC_PWRBTN_FALLING_EVT_STS (1 << 25)
++#define LPC_S5N_STS (1 << 21)
++#define LPC_S5N_RISING_EVT_STS (1 << 20)
++#define LPC_S5N_FALLING_EVT_STS (1 << 19)
++#define LPC_S3N_STS (1 << 18)
++#define LPC_S3N_RISING_EVT_STS (1 << 17)
++#define LPC_S3N_FALLING_EVT_STS (1 << 16)
++#define LPC_PWBTO_RAW_STS (1 << 15)
++#define LPC_LAST_ONCTL_STS (1 << 14)
++#define LPC_WAS_PFAIL_STS (1 << 13)
++#define LPC_POWER_UP_FAIL_STS (1 << 12) /* Crowbar */
++#define LPC_PWRBTN_OVERRIDE_STS (1 << 11)
++
++#define AST_LPC_SWCR0704 0x4
++
++#define AST_LPC_SWCR0B08 0x8
++#define LPC_PWREQ_OUTPUT_LEVEL (1 << 25)
++#define LPC_PWBTO_OUTPUT_LEVEL (1 << 24)
++#define LPC_ONCTL_STS (1 << 15)
++#define LPC_ONCTL_GPIO_LEVEL (1 << 14)
++#define LPC_ONCTL_EN_GPIO_OUTPUT (1 << 13)
++#define LPC_ONCTL_EN_GPIO_MODE (1 << 12)
++
++#define AST_LPC_SWCR0F0C 0xC
++#define AST_LPC_SWCR1310 0x10
++#define AST_LPC_SWCR1714 0x14
++#define AST_LPC_SWCR1B18 0x18
++#define AST_LPC_SWCR1F1C 0x1C
++#define AST_LPC_ACPIE3E0 0x20
++#define AST_LPC_ACPIC1C0 0x24
++#define AST_LPC_ACPIB3B0 0x28
++#define AST_LPC_ACPIB7B4 0x2C
++
++struct aspeed_lpc_sio {
++ struct miscdevice miscdev;
++ struct regmap *regmap;
++ struct clk *clk;
++ struct semaphore lock;
++ unsigned int reg_base;
++};
++
++static struct aspeed_lpc_sio *file_aspeed_lpc_sio(struct file *file)
++{
++ return container_of(file->private_data, struct aspeed_lpc_sio,
++ miscdev);
++}
++
++static int aspeed_lpc_sio_open(struct inode *inode, struct file *filp)
++{
++ return 0;
++}
++
++#define LPC_SLP3N5N_EVENT_STATUS (\
++ LPC_S5N_RISING_EVT_STS | \
++ LPC_S5N_FALLING_EVT_STS | \
++ LPC_S3N_RISING_EVT_STS | \
++ LPC_S3N_FALLING_EVT_STS)
++/*************************************
++ * SLPS3n SLPS5n State
++ * ---------------------------------
++ * 1 1 S12
++ * 0 1 S3I
++ * x 0 S45
++ *************************************
++ */
++
++static long sio_get_acpi_state(struct aspeed_lpc_sio *lpc_sio,
++ struct sio_ioctl_data *sio_data)
++{
++ u32 reg;
++ u32 val;
++ int rc;
++
++ reg = lpc_sio->reg_base + AST_LPC_SWCR0300;
++ rc = regmap_read(lpc_sio->regmap, reg, &val);
++ if (rc) {
++ dev_err(lpc_sio->miscdev.parent,
++ "regmap_read() failed with %d(reg:0x%x)\n", rc, reg);
++ return rc;
++ }
++
++ /* update the ACPI state event status */
++ if (sio_data->param != 0) {
++ if (val & LPC_SLP3N5N_EVENT_STATUS) {
++ sio_data->param = 1;
++ rc = regmap_write(lpc_sio->regmap, reg,
++ LPC_SLP3N5N_EVENT_STATUS);
++ if (rc) {
++ dev_err(lpc_sio->miscdev.parent,
++ "regmap_write() failed with %d(reg:0x%x)\n",
++ rc, reg);
++ return rc;
++ }
++ } else {
++ sio_data->param = 0;
++ }
++ }
++
++ if ((val & LPC_S3N_STS) && (val & LPC_S5N_STS))
++ sio_data->data = ACPI_STATE_S12;
++ else if ((val & LPC_S3N_STS) == 0 && (val & LPC_S5N_STS))
++ sio_data->data = ACPI_STATE_S3I;
++ else
++ sio_data->data = ACPI_STATE_S45;
++
++ return 0;
++}
++
++#define LPC_PWRGD_EVENT_STATUS ( \
++ LPC_PWRGD_RISING_EVT_STS | \
++ LPC_PWRGD_FALLING_EVT_STS)
++
++static long sio_get_pwrgd_status(struct aspeed_lpc_sio *lpc_sio,
++ struct sio_ioctl_data *sio_data)
++{
++ u32 reg;
++ u32 val;
++ int rc;
++
++ reg = lpc_sio->reg_base + AST_LPC_SWCR0300;
++ rc = regmap_read(lpc_sio->regmap, reg, &val);
++ if (rc) {
++ dev_err(lpc_sio->miscdev.parent,
++ "regmap_read() failed with %d(reg:0x%x)\n", rc, reg);
++ return rc;
++ }
++
++ /* update the PWRGD event status */
++ if (sio_data->param != 0) {
++ if (val & LPC_PWRGD_EVENT_STATUS) {
++ sio_data->param = 1;
++ rc = regmap_write(lpc_sio->regmap, reg,
++ LPC_PWRGD_EVENT_STATUS);
++ if (rc) {
++ dev_err(lpc_sio->miscdev.parent,
++ "regmap_write() failed with %d(reg:0x%x)\n",
++ rc, reg);
++ return rc;
++ }
++ } else {
++ sio_data->param = 0;
++ }
++ }
++
++ sio_data->data = (val & LPC_PWRGD_STS) != 0 ? 1 : 0;
++
++ return 0;
++}
++
++static long sio_get_onctl_status(struct aspeed_lpc_sio *lpc_sio,
++ struct sio_ioctl_data *sio_data)
++{
++ u32 reg;
++ u32 val;
++ int rc;
++
++ reg = lpc_sio->reg_base + AST_LPC_SWCR0B08;
++ rc = regmap_read(lpc_sio->regmap, reg, &val);
++ if (rc) {
++ dev_err(lpc_sio->miscdev.parent,
++ "regmap_read() failed with %d(reg:0x%x)\n", rc, reg);
++ return rc;
++ }
++
++ sio_data->data = (val & LPC_ONCTL_STS) != 0 ? 1 : 0;
++
++ return 0;
++}
++
++static long sio_set_onctl_gpio(struct aspeed_lpc_sio *lpc_sio,
++ struct sio_ioctl_data *sio_data)
++{
++ u32 reg;
++ u32 val;
++ int rc;
++
++ reg = lpc_sio->reg_base + AST_LPC_SWCR0B08;
++ rc = regmap_read(lpc_sio->regmap, reg, &val);
++ if (rc) {
++ dev_err(lpc_sio->miscdev.parent,
++ "regmap_read() failed with %d(reg:0x%x)\n", rc, reg);
++ return rc;
++ }
++
++ /* Enable ONCTL GPIO mode */
++ if (sio_data->param != 0) {
++ val |= LPC_ONCTL_EN_GPIO_MODE;
++ val |= LPC_ONCTL_EN_GPIO_OUTPUT;
++
++ if (sio_data->data != 0)
++ val |= LPC_ONCTL_GPIO_LEVEL;
++ else
++ val &= ~LPC_ONCTL_GPIO_LEVEL;
++
++ rc = regmap_write(lpc_sio->regmap, reg, val);
++ if (rc) {
++ dev_err(lpc_sio->miscdev.parent,
++ "regmap_write() failed with %d(reg:0x%x)\n", rc, reg);
++ return rc;
++ }
++ } else {
++ val &= ~LPC_ONCTL_EN_GPIO_MODE;
++ rc = regmap_write(lpc_sio->regmap, reg, val);
++ if (rc) {
++ dev_err(lpc_sio->miscdev.parent,
++ "regmap_write() failed with %d(reg:0x%x)\n", rc, reg);
++ return rc;
++ }
++ }
++
++ return 0;
++}
++
++static long sio_get_pwrbtn_override(struct aspeed_lpc_sio *lpc_sio,
++ struct sio_ioctl_data *sio_data)
++{
++ u32 reg;
++ u32 val;
++ int rc;
++
++ reg = lpc_sio->reg_base + AST_LPC_SWCR0300;
++ rc = regmap_read(lpc_sio->regmap, reg, &val);
++ if (rc) {
++ dev_err(lpc_sio->miscdev.parent,
++ "regmap_read() failed with %d(reg:0x%x)\n", rc, reg);
++ return rc;
++ }
++
++ /* clear the PWRBTN OVERRIDE status */
++ if (sio_data->param != 0) {
++ if (val & LPC_PWRBTN_OVERRIDE_STS) {
++ rc = regmap_write(lpc_sio->regmap, reg,
++ LPC_PWRBTN_OVERRIDE_STS);
++ if (rc) {
++ dev_err(lpc_sio->miscdev.parent,
++ "regmap_write() failed with %d(reg:0x%x)\n",
++ rc, reg);
++ return rc;
++ }
++ }
++ }
++
++ sio_data->data = (val & LPC_PWRBTN_OVERRIDE_STS) != 0 ? 1 : 0;
++
++ return 0;
++}
++
++static long sio_get_pfail_status(struct aspeed_lpc_sio *lpc_sio,
++ struct sio_ioctl_data *sio_data)
++{
++ u32 reg;
++ u32 val;
++ int rc;
++
++ reg = lpc_sio->reg_base + AST_LPC_SWCR0300;
++ rc = regmap_read(lpc_sio->regmap, reg, &val);
++ if (rc) {
++ dev_err(lpc_sio->miscdev.parent,
++ "regmap_read() failed with %d(reg:0x%x)\n", rc, reg);
++ return rc;
++ }
++
++ /* [ASPEED]: SWCR_03_00[13] (Was_pfail: default 1) is used to identify
++ * this current booting is from AC loss (not DC loss) if FW cleans this
++ * bit after booting successfully every time.
++ **********************************************************************/
++ if (val & LPC_WAS_PFAIL_STS) {
++ rc = regmap_write(lpc_sio->regmap, reg, 0); /* W0C */
++ if (rc) {
++ dev_err(lpc_sio->miscdev.parent,
++ "regmap_write() failed with %d(reg:0x%x)\n", rc, reg);
++ return rc;
++ }
++ sio_data->data = 1;
++ } else {
++ sio_data->data = 0;
++ }
++
++ return 0;
++}
++
++typedef long (*sio_cmd_fn) (struct aspeed_lpc_sio *sio_dev,
++ struct sio_ioctl_data *sio_data);
++static sio_cmd_fn sio_cmd_handle[SIO_MAX_CMD] = {
++ [SIO_GET_ACPI_STATE] = sio_get_acpi_state,
++ [SIO_GET_PWRGD_STATUS] = sio_get_pwrgd_status,
++ [SIO_GET_ONCTL_STATUS] = sio_get_onctl_status,
++ [SIO_SET_ONCTL_GPIO] = sio_set_onctl_gpio,
++ [SIO_GET_PWRBTN_OVERRIDE] = sio_get_pwrbtn_override,
++ [SIO_GET_PFAIL_STATUS] = sio_get_pfail_status,
++};
++
++static long aspeed_lpc_sio_ioctl(struct file *file, unsigned int cmd,
++ unsigned long param)
++{
++ struct aspeed_lpc_sio *lpc_sio = file_aspeed_lpc_sio(file);
++ long ret;
++ sio_cmd_fn cmd_fn;
++ struct sio_ioctl_data sio_data;
++
++
++ if (copy_from_user(&sio_data, (void __user *)param, sizeof(sio_data)))
++ return -EFAULT;
++
++ if (cmd != SIO_IOC_COMMAND || sio_data.sio_cmd >= SIO_MAX_CMD)
++ return -EINVAL;
++
++ cmd_fn = sio_cmd_handle[sio_data.sio_cmd];
++ if (cmd_fn == NULL)
++ return -EINVAL;
++
++ if (down_interruptible(&lpc_sio->lock) != 0)
++ return -ERESTARTSYS;
++
++ ret = cmd_fn(lpc_sio, &sio_data);
++ if (ret == 0) {
++ if (copy_to_user((void __user *)param, &sio_data,
++ sizeof(sio_data)))
++ ret = -EFAULT;
++ }
++
++ up(&lpc_sio->lock);
++
++ return ret;
++}
++
++static const struct file_operations aspeed_lpc_sio_fops = {
++ .owner = THIS_MODULE,
++ .open = aspeed_lpc_sio_open,
++ .unlocked_ioctl = aspeed_lpc_sio_ioctl,
++};
++
++static int aspeed_lpc_sio_probe(struct platform_device *pdev)
++{
++ struct aspeed_lpc_sio *lpc_sio;
++ struct device *dev;
++ int rc;
++
++ dev = &pdev->dev;
++
++ lpc_sio = devm_kzalloc(dev, sizeof(*lpc_sio), GFP_KERNEL);
++ if (!lpc_sio)
++ return -ENOMEM;
++
++ dev_set_drvdata(&pdev->dev, lpc_sio);
++
++ rc = of_property_read_u32(dev->of_node, "reg", &lpc_sio->reg_base);
++ if (rc) {
++ dev_err(dev, "Couldn't read reg device-tree property\n");
++ return rc;
++ }
++
++ lpc_sio->regmap = syscon_node_to_regmap(
++ pdev->dev.parent->of_node);
++ if (IS_ERR(lpc_sio->regmap)) {
++ dev_err(dev, "Couldn't get regmap\n");
++ return -ENODEV;
++ }
++
++ sema_init(&lpc_sio->lock, 1);
++
++ lpc_sio->clk = devm_clk_get(dev, NULL);
++ if (IS_ERR(lpc_sio->clk)) {
++ rc = PTR_ERR(lpc_sio->clk);
++ if (rc != -EPROBE_DEFER)
++ dev_err(dev, "couldn't get clock\n");
++ return rc;
++ }
++ rc = clk_prepare_enable(lpc_sio->clk);
++ if (rc) {
++ dev_err(dev, "couldn't enable clock\n");
++ return rc;
++ }
++
++ lpc_sio->miscdev.minor = MISC_DYNAMIC_MINOR;
++ lpc_sio->miscdev.name = DEVICE_NAME;
++ lpc_sio->miscdev.fops = &aspeed_lpc_sio_fops;
++ lpc_sio->miscdev.parent = dev;
++ rc = misc_register(&lpc_sio->miscdev);
++ if (rc) {
++ dev_err(dev, "Unable to register device\n");
++ goto err;
++ }
++
++ dev_info(dev, "Loaded at %pap (0x%08x)\n", &lpc_sio->regmap,
++ lpc_sio->reg_base);
++
++ return 0;
++
++err:
++ clk_disable_unprepare(lpc_sio->clk);
++
++ return rc;
++}
++
++static int aspeed_lpc_sio_remove(struct platform_device *pdev)
++{
++ struct aspeed_lpc_sio *lpc_sio = dev_get_drvdata(&pdev->dev);
++
++ misc_deregister(&lpc_sio->miscdev);
++ clk_disable_unprepare(lpc_sio->clk);
++
++ return 0;
++}
++
++static const struct of_device_id aspeed_lpc_sio_match[] = {
++ { .compatible = "aspeed,ast2500-lpc-sio" },
++ { },
++};
++MODULE_DEVICE_TABLE(of, aspeed_lpc_sio_match);
++
++static struct platform_driver aspeed_lpc_sio_driver = {
++ .driver = {
++ .name = SOC_NAME "-" DEVICE_NAME,
++ .of_match_table = aspeed_lpc_sio_match,
++ },
++ .probe = aspeed_lpc_sio_probe,
++ .remove = aspeed_lpc_sio_remove,
++};
++module_platform_driver(aspeed_lpc_sio_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
++MODULE_AUTHOR("Yong Li <yong.blli@linux.intel.com>");
++MODULE_DESCRIPTION("ASPEED AST LPC SIO device driver");
+diff --git a/include/uapi/linux/aspeed-lpc-sio.h b/include/uapi/linux/aspeed-lpc-sio.h
+new file mode 100644
+index 000000000000..5dc1efd4a426
+--- /dev/null
++++ b/include/uapi/linux/aspeed-lpc-sio.h
+@@ -0,0 +1,44 @@
++/*
++ * Copyright (C) 2012-2020 ASPEED Technology Inc.
++ * Copyright (c) 2017 Intel 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.
++ *
++ */
++
++#ifndef _UAPI_LINUX_ASPEED_LPC_SIO_H
++#define _UAPI_LINUX_ASPEED_LPC_SIO_H
++
++#include <linux/ioctl.h>
++
++enum ACPI_SLP_STATE {
++ ACPI_STATE_S12 = 1,
++ ACPI_STATE_S3I,
++ ACPI_STATE_S45
++};
++
++/* SWC & ACPI for SuperIO IOCTL */
++enum SIO_CMD {
++ SIO_GET_ACPI_STATE = 0,
++ SIO_GET_PWRGD_STATUS,
++ SIO_GET_ONCTL_STATUS,
++ SIO_SET_ONCTL_GPIO,
++ SIO_GET_PWRBTN_OVERRIDE,
++ SIO_GET_PFAIL_STATUS, /* Start from AC Loss */
++
++ SIO_MAX_CMD
++};
++
++struct sio_ioctl_data {
++ unsigned short sio_cmd;
++ unsigned short param;
++ unsigned int data;
++};
++
++#define SIO_IOC_BASE 'P'
++#define SIO_IOC_COMMAND _IOWR(SIO_IOC_BASE, 1, struct sio_ioctl_data)
++
++#endif /* _UAPI_LINUX_ASPEED_LPC_SIO_H */
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch
new file mode 100644
index 000000000..07283f54d
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch
@@ -0,0 +1,667 @@
+From 6e55e28db5eed85b7717aa4fc92c064f11429f6d Mon Sep 17 00:00:00 2001
+From: Haiyue Wang <haiyue.wang@linux.intel.com>
+Date: Sat, 24 Feb 2018 11:12:32 +0800
+Subject: [PATCH] eSPI: add ASPEED AST2500 eSPI driver to boot a host with PCH
+ runs on eSPI
+
+When PCH works under eSPI mode, the PMC (Power Management Controller) in
+PCH is waiting for SUS_ACK from BMC after it alerts SUS_WARN. It is in
+dead loop if no SUS_ACK assert. This is the basic requirement for the BMC
+works as eSPI slave.
+
+Also for the host power on / off actions, from BMC side, the following VW
+(Virtual Wire) messages are done in firmware:
+1. SLAVE_BOOT_LOAD_DONE / SLAVE_BOOT_LOAD_STATUS
+2. SUS_ACK
+3. OOB_RESET_ACK
+4. HOST_RESET_ACK
+
+Also, it provides monitoring interface of PLTRST_N signal through
+/dev/espi-pltrstn
+
+Signed-off-by: Haiyue Wang <haiyue.wang@linux.intel.com>
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Signed-off-by: James Feist <james.feist@linux.intel.com>
+---
+ .../devicetree/bindings/misc/aspeed,espi-slave.txt | 19 +
+ Documentation/misc-devices/espi-slave.rst | 118 ++++++
+ arch/arm/boot/dts/aspeed-g5.dtsi | 4 +
+ drivers/misc/Kconfig | 8 +
+ drivers/misc/Makefile | 1 +
+ drivers/misc/aspeed-espi-slave.c | 420 +++++++++++++++++++++
+ 6 files changed, 570 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt
+ create mode 100644 Documentation/misc-devices/espi-slave.rst
+ create mode 100644 drivers/misc/aspeed-espi-slave.c
+
+diff --git a/Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt b/Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt
+new file mode 100644
+index 000000000000..8660e2ffbb89
+--- /dev/null
++++ b/Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt
+@@ -0,0 +1,19 @@
++ASPEED eSPI Slave Controller
++
++Required properties:
++ - compatible: must be one of:
++ - "aspeed,ast2500-espi-slave"
++
++ - reg: physical base address of the controller and length of memory mapped
++ region
++
++ - interrupts: interrupt generated by the controller
++
++Example:
++
++ espi: espi@1e6ee000 {
++ compatible = "aspeed,ast2500-espi-slave";
++ reg = <0x1e6ee000 0x100>;
++ interrupts = <23>;
++ status = "disabled";
++};
+diff --git a/Documentation/misc-devices/espi-slave.rst b/Documentation/misc-devices/espi-slave.rst
+new file mode 100644
+index 000000000000..887a69a7130a
+--- /dev/null
++++ b/Documentation/misc-devices/espi-slave.rst
+@@ -0,0 +1,118 @@
++.. SPDX-License-Identifier: GPL-2.0
++
++==========
++eSPI Slave
++==========
++
++:Author: Haiyue Wang <haiyue.wang@linux.intel.com>
++
++The PCH (**eSPI master**) provides the eSPI to support connection of a
++BMC (**eSPI slave**) to the platform.
++
++The LPC and eSPI interfaces are mutually exclusive. Both use the same
++pins, but on power-up, a HW strap determines if the eSPI or the LPC bus
++is operational. Once selected, it’s not possible to change to the other
++interface.
++
++``eSPI Channels and Supported Transactions``
++ +------+---------------------+----------------------+--------------------+
++ | CH # | Channel | Posted Cycles | Non-Posted Cycles |
++ +======+=====================+======================+====================+
++ | 0 | Peripheral | Memory Write, | Memory Read, |
++ | | | Completions | I/O Read/Write |
++ +------+---------------------+----------------------+--------------------+
++ | 1 | Virtual Wire | Virtual Wire GET/PUT | N/A |
++ +------+---------------------+----------------------+--------------------+
++ | 2 | Out-of-Band Message | SMBus Packet GET/PUT | N/A |
++ +------+---------------------+----------------------+--------------------+
++ | 3 | Flash Access | N/A | Flash Read, Write, |
++ | | | | Erase |
++ +------+---------------------+----------------------+--------------------+
++ | N/A | General | Register Accesses | N/A |
++ +------+---------------------+----------------------+--------------------+
++
++Virtual Wire Channel (Channel 1) Overview
++-----------------------------------------
++
++The Virtual Wire channel uses a standard message format to communicate
++several types of signals between the components on the platform::
++
++ - Sideband and GPIO Pins: System events and other dedicated signals
++ between the PCH and eSPI slave. These signals are tunneled between the
++ two components over eSPI.
++
++ - Serial IRQ Interrupts: Interrupts are tunneled from the eSPI slave to
++ the PCH. Both edge and triggered interrupts are supported.
++
++When PCH runs on eSPI mode, from BMC side, the following VW messages are
++done in firmware::
++
++ 1. SLAVE_BOOT_LOAD_DONE / SLAVE_BOOT_LOAD_STATUS
++ 2. SUS_ACK
++ 3. OOB_RESET_ACK
++ 4. HOST_RESET_ACK
++
++``eSPI Virtual Wires (VW)``
++ +----------------------+---------+---------------------------------------+
++ |Virtual Wire |PCH Pin |Comments |
++ | |Direction| |
++ +======================+=========+=======================================+
++ |SUS_WARN# |Output |PCH pin is a GPIO when eSPI is enabled.|
++ | | |eSPI controller receives as VW message.|
++ +----------------------+---------+---------------------------------------+
++ |SUS_ACK# |Input |PCH pin is a GPIO when eSPI is enabled.|
++ | | |eSPI controller receives as VW message.|
++ +----------------------+---------+---------------------------------------+
++ |SLAVE_BOOT_LOAD_DONE |Input |Sent when the BMC has completed its |
++ | | |boot process as an indication to |
++ | | |eSPI-MC to continue with the G3 to S0 |
++ | | |exit. |
++ | | |The eSPI Master waits for the assertion|
++ | | |of this virtual wire before proceeding |
++ | | |with the SLP_S5# deassertion. |
++ | | |The intent is that it is never changed |
++ | | |except on a G3 exit - it is reset on a |
++ | | |G3 entry. |
++ +----------------------+---------+---------------------------------------+
++ |SLAVE_BOOT_LOAD_STATUS|Input |Sent upon completion of the Slave Boot |
++ | | |Load from the attached flash. A stat of|
++ | | |1 indicates that the boot code load was|
++ | | |successful and that the integrity of |
++ | | |the image is intact. |
++ +----------------------+---------+---------------------------------------+
++ |HOST_RESET_WARN |Output |Sent from the MC just before the Host |
++ | | |is about to enter reset. Upon receiving|
++ | | |, the BMC must flush and quiesce its |
++ | | |upstream Peripheral Channel request |
++ | | |queues and assert HOST_RESET_ACK VWire.|
++ | | |The MC subsequently completes any |
++ | | |outstanding posted transactions or |
++ | | |completions and then disables the |
++ | | |Peripheral Channel via a write to |
++ | | |the Slave's Configuration Register. |
++ +----------------------+---------+---------------------------------------+
++ |HOST_RESET_ACK |Input |ACK for the HOST_RESET_WARN message |
++ +----------------------+---------+---------------------------------------+
++ |OOB_RESET_WARN |Output |Sent from the MC just before the OOB |
++ | | |processor is about to enter reset. Upon|
++ | | |receiving, the BMC must flush and |
++ | | |quiesce its OOB Channel upstream |
++ | | |request queues and assert OOB_RESET_ACK|
++ | | |VWire. The-MC subsequently completes |
++ | | |any outstanding posted transactions or |
++ | | |completions and then disables the OOB |
++ | | |Channel via a write to the Slave's |
++ | | |Configuration Register. |
++ +----------------------+---------+---------------------------------------+
++ |OOB_RESET_ACK |Input |ACK for OOB_RESET_WARN message |
++ +----------------------+---------+---------------------------------------+
++
++`Intel C620 Series Chipset Platform Controller Hub
++<https://www.intel.com/content/www/us/en/chipsets/c620-series-chipset-datasheet.html>`_
++
++ -- 17. Enhanced Serial Peripheral Interface
++
++
++`Enhanced Serial Peripheral Interface (eSPI)
++- Interface Base Specification (for Client and Server Platforms)
++<https://www.intel.com/content/dam/support/us/en/documents/software/chipset-software/327432-004_espi_base_specification_rev1.0.pdf>`_
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index 88f75736fe48..26671cc4dbd5 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -317,6 +317,7 @@
+ clocks = <&syscon ASPEED_CLK_APB>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
++ status = "disabled";
+ };
+
+ sgpio: sgpio@1e780200 {
+@@ -413,6 +414,9 @@
+ reg = <0x1e6ee000 0x100>;
+ interrupts = <23>;
+ status = "disabled";
++ clocks = <&syscon ASPEED_CLK_GATE_ESPICLK>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_espi_default>;
+ };
+
+ lpc: lpc@1e789000 {
+diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
+index d681b7201f8c..50814caba1d3 100644
+--- a/drivers/misc/Kconfig
++++ b/drivers/misc/Kconfig
+@@ -455,6 +455,14 @@ config VEXPRESS_SYSCFG
+ bus. System Configuration interface is one of the possible means
+ of generating transactions on this bus.
+
++config ASPEED_ESPI_SLAVE
++ depends on ARCH_ASPEED || COMPILE_TEST
++ depends on REGMAP_MMIO
++ tristate "Aspeed ast2500 eSPI slave device driver"
++ ---help---
++ Control Aspeed ast2500 eSPI slave controller to handle event
++ which needs the firmware's processing.
++
+ config PCI_ENDPOINT_TEST
+ depends on PCI
+ select CRC32
+diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
+index fdd404120ed8..f168e6713440 100644
+--- a/drivers/misc/Makefile
++++ b/drivers/misc/Makefile
+@@ -53,6 +53,7 @@ obj-$(CONFIG_GENWQE) += genwqe/
+ obj-$(CONFIG_ECHO) += echo/
+ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
+ obj-$(CONFIG_CXL_BASE) += cxl/
++obj-$(CONFIG_ASPEED_ESPI_SLAVE) += aspeed-espi-slave.o
+ obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o
+ obj-$(CONFIG_OCXL) += ocxl/
+ obj-y += cardreader/
+diff --git a/drivers/misc/aspeed-espi-slave.c b/drivers/misc/aspeed-espi-slave.c
+new file mode 100644
+index 000000000000..b0fc01692d3a
+--- /dev/null
++++ b/drivers/misc/aspeed-espi-slave.c
+@@ -0,0 +1,420 @@
++// SPDX-License-Identifier: GPL-2.0
++// Copyright (c) 2015-2019, Intel Corporation.
++
++#include <linux/clk.h>
++#include <linux/fs.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/miscdevice.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/sched/signal.h>
++#include <linux/spinlock.h>
++#include <linux/uaccess.h>
++
++#define ASPEED_ESPI_CTRL 0x00
++#define ASPEED_ESPI_CTRL_SW_RESET GENMASK(31, 24)
++#define ASPEED_ESPI_CTRL_OOB_CHRDY BIT(4)
++#define ASPEED_ESPI_INT_STS 0x08
++#define ASPEED_ESPI_HW_RESET BIT(31)
++#define ASPEED_ESPI_VW_SYSEVT1 BIT(22)
++#define ASPEED_ESPI_VW_SYSEVT BIT(8)
++#define ASPEED_ESPI_INT_EN 0x0C
++#define ASPEED_ESPI_DATA_PORT 0x28
++#define ASPEED_ESPI_SYSEVT_INT_EN 0x94
++#define ASPEED_ESPI_SYSEVT 0x98
++#define ASPEED_ESPI_SYSEVT_HOST_RST_ACK BIT(27)
++#define ASPEED_ESPI_SYSEVT_SLAVE_BOOT_STATUS BIT(23)
++#define ASPEED_ESPI_SYSEVT_SLAVE_BOOT_DONE BIT(20)
++#define ASPEED_ESPI_SYSEVT_OOB_RST_ACK BIT(16)
++#define ASPEED_ESPI_SYSEVT_INT_T0 0x110
++#define ASPEED_ESPI_SYSEVT_INT_T1 0x114
++#define ASPEED_ESPI_SYSEVT_INT_T2 0x118
++#define ASPEED_ESPI_SYSEVT_INT_STS 0x11C
++#define ASPEED_ESPI_SYSEVT_HOST_RST_WARN BIT(8)
++#define ASPEED_ESPI_SYSEVT_OOB_RST_WARN BIT(6)
++#define ASPEED_ESPI_SYSEVT_PLTRSTN BIT(5)
++#define ASPEED_ESPI_SYSEVT1_INT_EN 0x100
++#define ASPEED_ESPI_SYSEVT1 0x104
++#define ASPEED_ESPI_SYSEVT1_SUS_ACK BIT(20)
++#define ASPEED_ESPI_SYSEVT1_INT_T0 0x120
++#define ASPEED_ESPI_SYSEVT1_INT_T1 0x124
++#define ASPEED_ESPI_SYSEVT1_INT_T2 0x128
++#define ASPEED_ESPI_SYSEVT1_INT_STS 0x12C
++#define ASPEED_ESPI_SYSEVT1_SUS_WARN BIT(0)
++
++#define ASPEED_ESPI_INT_MASK \
++ (ASPEED_ESPI_HW_RESET | \
++ ASPEED_ESPI_VW_SYSEVT1 | \
++ ASPEED_ESPI_VW_SYSEVT)
++
++/*
++ * Setup Interrupt Type / Enable of System Event from Master
++ * T2 T1 T0
++ * 1) HOST_RST_WARN : Dual Edge 1 0 0
++ * 2) OOB_RST_WARN : Dual Edge 1 0 0
++ * 3) PLTRSTN : Dual Edge 1 0 0
++ */
++#define ASPEED_ESPI_SYSEVT_INT_T0_MASK 0
++#define ASPEED_ESPI_SYSEVT_INT_T1_MASK 0
++#define ASPEED_ESPI_SYSEVT_INT_T2_MASK \
++ (ASPEED_ESPI_SYSEVT_HOST_RST_WARN | \
++ ASPEED_ESPI_SYSEVT_OOB_RST_WARN | \
++ ASPEED_ESPI_SYSEVT_PLTRSTN)
++#define ASPEED_ESPI_SYSEVT_INT_MASK \
++ (ASPEED_ESPI_SYSEVT_INT_T0_MASK | \
++ ASPEED_ESPI_SYSEVT_INT_T1_MASK | \
++ ASPEED_ESPI_SYSEVT_INT_T2_MASK)
++
++/*
++ * Setup Interrupt Type / Enable of System Event 1 from Master
++ * T2 T1 T0
++ * 1) SUS_WARN : Rising Edge 0 0 1
++ */
++#define ASPEED_ESPI_SYSEVT1_INT_T0_MASK ASPEED_ESPI_SYSEVT1_SUS_WARN
++#define ASPEED_ESPI_SYSEVT1_INT_T1_MASK 0
++#define ASPEED_ESPI_SYSEVT1_INT_T2_MASK 0
++#define ASPEED_ESPI_SYSEVT1_INT_MASK \
++ (ASPEED_ESPI_SYSEVT1_INT_T0_MASK | \
++ ASPEED_ESPI_SYSEVT1_INT_T1_MASK | \
++ ASPEED_ESPI_SYSEVT1_INT_T2_MASK)
++
++struct aspeed_espi {
++ struct regmap *map;
++ struct clk *clk;
++ struct device *dev;
++ int irq;
++
++ /* for PLTRST_N signal monitoring interface */
++ struct miscdevice pltrstn_miscdev;
++ spinlock_t pltrstn_lock; /* for PLTRST_N signal sampling */
++ wait_queue_head_t pltrstn_waitq;
++ char pltrstn;
++};
++
++static void aspeed_espi_sys_event(struct aspeed_espi *priv)
++{
++ u32 sts, evt;
++
++ regmap_read(priv->map, ASPEED_ESPI_SYSEVT_INT_STS, &sts);
++ regmap_read(priv->map, ASPEED_ESPI_SYSEVT, &evt);
++
++ dev_dbg(priv->dev, "sys: sts = %08x, evt = %08x\n", sts, evt);
++
++ if (!(evt & ASPEED_ESPI_SYSEVT_SLAVE_BOOT_STATUS)) {
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT,
++ evt | ASPEED_ESPI_SYSEVT_SLAVE_BOOT_STATUS |
++ ASPEED_ESPI_SYSEVT_SLAVE_BOOT_DONE);
++ dev_dbg(priv->dev, "Setting espi slave boot done\n");
++ }
++ if (sts & ASPEED_ESPI_SYSEVT_HOST_RST_WARN &&
++ evt & ASPEED_ESPI_SYSEVT_HOST_RST_WARN) {
++ regmap_write_bits(priv->map, ASPEED_ESPI_SYSEVT,
++ ASPEED_ESPI_SYSEVT_HOST_RST_ACK,
++ ASPEED_ESPI_SYSEVT_HOST_RST_ACK);
++ dev_dbg(priv->dev, "SYSEVT_HOST_RST_WARN: acked\n");
++ }
++ if (sts & ASPEED_ESPI_SYSEVT_OOB_RST_WARN &&
++ evt & ASPEED_ESPI_SYSEVT_OOB_RST_WARN) {
++ regmap_write_bits(priv->map, ASPEED_ESPI_SYSEVT,
++ ASPEED_ESPI_SYSEVT_OOB_RST_ACK,
++ ASPEED_ESPI_SYSEVT_OOB_RST_ACK);
++ dev_dbg(priv->dev, "SYSEVT_OOB_RST_WARN: acked\n");
++ }
++ if (sts & ASPEED_ESPI_SYSEVT_PLTRSTN || priv->pltrstn == 'U') {
++ priv->pltrstn = (evt & ASPEED_ESPI_SYSEVT_PLTRSTN) ? '1' : '0';
++ wake_up_interruptible(&priv->pltrstn_waitq);
++ dev_dbg(priv->dev, "SYSEVT_PLTRSTN: %c\n", priv->pltrstn);
++ }
++
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT_INT_STS, sts);
++}
++
++static void aspeed_espi_sys_event1(struct aspeed_espi *priv)
++{
++ u32 sts, evt;
++
++ regmap_read(priv->map, ASPEED_ESPI_SYSEVT1_INT_STS, &sts);
++ regmap_read(priv->map, ASPEED_ESPI_SYSEVT1, &evt);
++
++ dev_dbg(priv->dev, "sys event1: sts = %08x, evt = %08x\n", sts, evt);
++
++ if (sts & ASPEED_ESPI_SYSEVT1_SUS_WARN &&
++ evt & ASPEED_ESPI_SYSEVT1_SUS_WARN) {
++ regmap_write_bits(priv->map, ASPEED_ESPI_SYSEVT1,
++ ASPEED_ESPI_SYSEVT1_SUS_ACK,
++ ASPEED_ESPI_SYSEVT1_SUS_ACK);
++ dev_dbg(priv->dev, "SYSEVT1_SUS_WARN: acked\n");
++ }
++
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT1_INT_STS, sts);
++}
++
++static void aspeed_espi_boot_ack(struct aspeed_espi *priv)
++{
++ u32 evt;
++
++ regmap_read(priv->map, ASPEED_ESPI_SYSEVT, &evt);
++ if (!(evt & ASPEED_ESPI_SYSEVT_SLAVE_BOOT_STATUS)) {
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT,
++ evt | ASPEED_ESPI_SYSEVT_SLAVE_BOOT_STATUS |
++ ASPEED_ESPI_SYSEVT_SLAVE_BOOT_DONE);
++ dev_dbg(priv->dev, "Setting espi slave boot done\n");
++ }
++
++ regmap_read(priv->map, ASPEED_ESPI_SYSEVT1, &evt);
++ if (evt & ASPEED_ESPI_SYSEVT1_SUS_WARN &&
++ !(evt & ASPEED_ESPI_SYSEVT1_SUS_ACK)) {
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT1,
++ evt | ASPEED_ESPI_SYSEVT1_SUS_ACK);
++ dev_dbg(priv->dev, "Boot SYSEVT1_SUS_WARN: acked\n");
++ }
++}
++
++static irqreturn_t aspeed_espi_irq(int irq, void *arg)
++{
++ struct aspeed_espi *priv = arg;
++ u32 sts, sts_handled = 0;
++
++ regmap_read(priv->map, ASPEED_ESPI_INT_STS, &sts);
++
++ dev_dbg(priv->dev, "INT_STS: 0x%08x\n", sts);
++
++ if (sts & ASPEED_ESPI_VW_SYSEVT) {
++ aspeed_espi_sys_event(priv);
++ sts_handled |= ASPEED_ESPI_VW_SYSEVT;
++ }
++
++ if (sts & ASPEED_ESPI_VW_SYSEVT1) {
++ aspeed_espi_sys_event1(priv);
++ sts_handled |= ASPEED_ESPI_VW_SYSEVT1;
++ }
++
++ if (sts & ASPEED_ESPI_HW_RESET) {
++ regmap_write_bits(priv->map, ASPEED_ESPI_CTRL,
++ ASPEED_ESPI_CTRL_SW_RESET, 0);
++ regmap_write_bits(priv->map, ASPEED_ESPI_CTRL,
++ ASPEED_ESPI_CTRL_SW_RESET,
++ ASPEED_ESPI_CTRL_SW_RESET);
++ aspeed_espi_boot_ack(priv);
++ sts_handled |= ASPEED_ESPI_HW_RESET;
++ }
++
++ regmap_write(priv->map, ASPEED_ESPI_INT_STS, sts);
++
++ return sts != sts_handled ? IRQ_NONE : IRQ_HANDLED;
++}
++
++static void aspeed_espi_config_irq(struct aspeed_espi *priv)
++{
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT_INT_T0,
++ ASPEED_ESPI_SYSEVT_INT_T0_MASK);
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT_INT_T1,
++ ASPEED_ESPI_SYSEVT_INT_T1_MASK);
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT_INT_T2,
++ ASPEED_ESPI_SYSEVT_INT_T2_MASK);
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT_INT_EN,
++ ASPEED_ESPI_SYSEVT_INT_MASK);
++
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT1_INT_T0,
++ ASPEED_ESPI_SYSEVT1_INT_T0_MASK);
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT1_INT_T1,
++ ASPEED_ESPI_SYSEVT1_INT_T1_MASK);
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT1_INT_T2,
++ ASPEED_ESPI_SYSEVT1_INT_T2_MASK);
++ regmap_write(priv->map, ASPEED_ESPI_SYSEVT1_INT_EN,
++ ASPEED_ESPI_SYSEVT1_INT_MASK);
++
++ regmap_write(priv->map, ASPEED_ESPI_INT_EN, ASPEED_ESPI_INT_MASK);
++
++ aspeed_espi_boot_ack(priv);
++}
++
++static inline struct aspeed_espi *to_aspeed_espi(struct file *filp)
++{
++ return container_of(filp->private_data, struct aspeed_espi,
++ pltrstn_miscdev);
++}
++
++static int aspeed_espi_pltrstn_open(struct inode *inode, struct file *filp)
++{
++ if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
++ return -EACCES;
++
++ return 0;
++}
++
++static ssize_t aspeed_espi_pltrstn_read(struct file *filp, char __user *buf,
++ size_t count, loff_t *offset)
++{
++ struct aspeed_espi *priv = to_aspeed_espi(filp);
++ DECLARE_WAITQUEUE(wait, current);
++ unsigned long flags;
++ char old_sample;
++ int ret = 0;
++
++ spin_lock_irqsave(&priv->pltrstn_lock, flags);
++
++ add_wait_queue(&priv->pltrstn_waitq, &wait);
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ old_sample = priv->pltrstn;
++
++ do {
++ char new_sample = priv->pltrstn;
++
++ if (filp->f_flags & O_NONBLOCK || old_sample != new_sample) {
++ ret = put_user(new_sample, (unsigned long __user *)buf);
++ if (!ret)
++ ret = sizeof(new_sample);
++ } else if (signal_pending(current)) {
++ ret = -ERESTARTSYS;
++ }
++
++ if (!ret) {
++ spin_unlock_irqrestore(&priv->pltrstn_lock, flags);
++ schedule();
++ spin_lock_irqsave(&priv->pltrstn_lock, flags);
++ }
++ } while (!ret);
++
++ remove_wait_queue(&priv->pltrstn_waitq, &wait);
++ set_current_state(TASK_RUNNING);
++
++ spin_unlock_irqrestore(&priv->pltrstn_lock, flags);
++
++ return ret;
++}
++
++static const struct file_operations aspeed_espi_pltrstn_fops = {
++ .owner = THIS_MODULE,
++ .open = aspeed_espi_pltrstn_open,
++ .read = aspeed_espi_pltrstn_read,
++};
++
++static const struct regmap_config aspeed_espi_regmap_cfg = {
++ .reg_bits = 32,
++ .reg_stride = 4,
++ .val_bits = 32,
++ .max_register = ASPEED_ESPI_SYSEVT1_INT_STS,
++};
++
++static int aspeed_espi_probe(struct platform_device *pdev)
++{
++ struct aspeed_espi *priv;
++ struct resource *res;
++ void __iomem *regs;
++ u32 ctrl;
++ int ret;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ regs = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(regs))
++ return PTR_ERR(regs);
++
++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ dev_set_drvdata(&pdev->dev, priv);
++ priv->dev = &pdev->dev;
++
++ priv->map = devm_regmap_init_mmio(&pdev->dev, regs,
++ &aspeed_espi_regmap_cfg);
++ if (IS_ERR(priv->map))
++ return PTR_ERR(priv->map);
++
++ spin_lock_init(&priv->pltrstn_lock);
++ init_waitqueue_head(&priv->pltrstn_waitq);
++ priv->pltrstn = 'U'; /* means it's not reported yet from master */
++
++ priv->irq = platform_get_irq(pdev, 0);
++ if (priv->irq < 0)
++ return priv->irq;
++
++ aspeed_espi_config_irq(priv);
++
++ ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_espi_irq, 0,
++ "aspeed-espi-irq", priv);
++ if (ret)
++ return ret;
++
++ priv->clk = devm_clk_get(&pdev->dev, NULL);
++ if (IS_ERR(priv->clk)) {
++ ret = PTR_ERR(priv->clk);
++ if (ret != -EPROBE_DEFER)
++ dev_err(&pdev->dev, "couldn't get clock\n");
++ return ret;
++ }
++ ret = clk_prepare_enable(priv->clk);
++ if (ret) {
++ dev_err(&pdev->dev, "couldn't enable clock\n");
++ return ret;
++ }
++
++ /*
++ * 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->map, ASPEED_ESPI_CTRL, &ctrl);
++ if (ret) {
++ dev_err(&pdev->dev, "failed to read ctrl register\n");
++ goto err_clk_disable_out;
++ }
++ regmap_write(priv->map, ASPEED_ESPI_CTRL,
++ ctrl | ASPEED_ESPI_CTRL_OOB_CHRDY);
++
++ priv->pltrstn_miscdev.minor = MISC_DYNAMIC_MINOR;
++ priv->pltrstn_miscdev.name = "espi-pltrstn";
++ priv->pltrstn_miscdev.fops = &aspeed_espi_pltrstn_fops;
++ priv->pltrstn_miscdev.parent = &pdev->dev;
++
++ ret = misc_register(&priv->pltrstn_miscdev);
++ if (ret) {
++ dev_err(&pdev->dev, "Unable to register device\n");
++ goto err_clk_disable_out;
++ }
++
++ dev_info(&pdev->dev, "eSPI registered, irq %d\n", priv->irq);
++
++ return 0;
++
++err_clk_disable_out:
++ clk_disable_unprepare(priv->clk);
++
++ return ret;
++}
++
++static int aspeed_espi_remove(struct platform_device *pdev)
++{
++ struct aspeed_espi *priv = dev_get_drvdata(&pdev->dev);
++
++ misc_deregister(&priv->pltrstn_miscdev);
++ clk_disable_unprepare(priv->clk);
++
++ return 0;
++}
++
++static const struct of_device_id of_espi_match_table[] = {
++ { .compatible = "aspeed,ast2500-espi-slave" },
++ { }
++};
++MODULE_DEVICE_TABLE(of, of_espi_match_table);
++
++static struct platform_driver aspeed_espi_driver = {
++ .driver = {
++ .name = KBUILD_MODNAME,
++ .of_match_table = of_match_ptr(of_espi_match_table),
++ },
++ .probe = aspeed_espi_probe,
++ .remove = aspeed_espi_remove,
++};
++module_platform_driver(aspeed_espi_driver);
++
++MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
++MODULE_DESCRIPTION("Aspeed eSPI driver");
++MODULE_LICENSE("GPL v2");
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0026-Add-support-for-new-PECI-commands.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0026-Add-support-for-new-PECI-commands.patch
new file mode 100644
index 000000000..4dc14d3b1
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0026-Add-support-for-new-PECI-commands.patch
@@ -0,0 +1,724 @@
+From 5f43a95bd032279440196a1c9802e1dec5d24a65 Mon Sep 17 00:00:00 2001
+From: "Jason M. Bills" <jason.m.bills@intel.com>
+Date: Wed, 4 Apr 2018 13:52:39 -0700
+Subject: [PATCH] Add support for new PECI commands
+
+Signed-off-by: Jason M. Bills <jason.m.bills@intel.com>
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Signed-off-by: Zhu, Yunge <yunge.zhu@linux.intel.com>
+---
+ drivers/peci/peci-core.c | 430 ++++++++++++++++++++++++++++++++++++++++
+ include/uapi/linux/peci-ioctl.h | 179 +++++++++++++++++
+ 2 files changed, 609 insertions(+)
+
+diff --git a/drivers/peci/peci-core.c b/drivers/peci/peci-core.c
+index 2a6be04..43a86a0 100644
+--- a/drivers/peci/peci-core.c
++++ b/drivers/peci/peci-core.c
+@@ -318,6 +318,13 @@ static int peci_scan_cmd_mask(struct peci_adapter *adapter)
+ * See PECI Spec Table 3-1.
+ */
+ revision = FIELD_GET(REVISION_NUM_MASK, dib);
++ if (revision >= 0x40) { /* Rev. 4.0 */
++ adapter->cmd_mask |= BIT(PECI_CMD_RD_IA_MSREX);
++ adapter->cmd_mask |= BIT(PECI_CMD_RD_END_PT_CFG);
++ adapter->cmd_mask |= BIT(PECI_CMD_WR_END_PT_CFG);
++ adapter->cmd_mask |= BIT(PECI_CMD_CRASHDUMP_DISC);
++ adapter->cmd_mask |= BIT(PECI_CMD_CRASHDUMP_GET_FRAME);
++ }
+ if (revision >= 0x36) /* Rev. 3.6 */
+ adapter->cmd_mask |= BIT(PECI_CMD_WR_IA_MSR);
+ if (revision >= 0x35) /* Rev. 3.5 */
+@@ -375,14 +382,18 @@ static int peci_cmd_xfer(struct peci_adapter *adapter, void *vmsg)
+ switch (msg->tx_buf[0]) {
+ case PECI_RDPKGCFG_CMD:
+ case PECI_RDIAMSR_CMD:
++ case PECI_RDIAMSREX_CMD:
+ case PECI_RDPCICFG_CMD:
+ case PECI_RDPCICFGLOCAL_CMD:
++ case PECI_RDENDPTCFG_CMD:
++ case PECI_CRASHDUMP_CMD:
+ ret = peci_xfer_with_retries(adapter, msg, false);
+ break;
+ case PECI_WRPKGCFG_CMD:
+ case PECI_WRIAMSR_CMD:
+ case PECI_WRPCICFG_CMD:
+ case PECI_WRPCICFGLOCAL_CMD:
++ case PECI_WRENDPTCFG_CMD:
+ /* Check if the AW FCS byte is already provided */
+ ret = peci_aw_fcs(msg, 2 + msg->tx_len, &aw_fcs);
+ if (ret)
+@@ -590,6 +601,34 @@ static int peci_cmd_rd_ia_msr(struct peci_adapter *adapter, void *vmsg)
+ return ret;
+ }
+
++static int peci_cmd_rd_ia_msrex(struct peci_adapter *adapter, void *vmsg)
++{
++ struct peci_rd_ia_msrex_msg *umsg = vmsg;
++ struct peci_xfer_msg *msg;
++ int ret;
++
++ msg = peci_get_xfer_msg(PECI_RDIAMSREX_WRITE_LEN, PECI_RDIAMSREX_READ_LEN);
++ if (!msg)
++ return -ENOMEM;
++
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_RDIAMSREX_CMD;
++ msg->tx_buf[1] = 0;
++ msg->tx_buf[2] = (u8)umsg->thread_id;
++ msg->tx_buf[3] = (u8)(umsg->thread_id >> 8);
++ msg->tx_buf[4] = (u8)umsg->address;
++ msg->tx_buf[5] = (u8)(umsg->address >> 8);
++
++ ret = peci_xfer_with_retries(adapter, msg, false);
++ if (!ret)
++ memcpy(&umsg->value, &msg->rx_buf[1], sizeof(uint64_t));
++
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
++
++ return ret;
++}
++
+ static int peci_cmd_wr_ia_msr(struct peci_adapter *adapter, void *vmsg)
+ {
+ return -ENOSYS; /* Not implemented yet */
+@@ -730,6 +769,392 @@ static int peci_cmd_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
+ return ret;
+ }
+
++static int peci_cmd_rd_end_pt_cfg(struct peci_adapter *adapter, void *vmsg)
++{
++ struct peci_rd_end_pt_cfg_msg *umsg = vmsg;
++ struct peci_xfer_msg *msg = NULL;
++ u32 address;
++ u8 tx_size;
++ int ret;
++
++ switch (umsg->msg_type) {
++ case PECI_ENDPTCFG_TYPE_LOCAL_PCI:
++ case PECI_ENDPTCFG_TYPE_PCI:
++ /*
++ * 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 = peci_get_xfer_msg(PECI_RDENDPTCFG_PCI_WRITE_LEN,
++ PECI_RDENDPTCFG_READ_LEN_BASE +
++ umsg->rx_len);
++ if (!msg)
++ return -ENOMEM;
++
++ address = umsg->params.pci_cfg.reg; /* [11:0] - Register */
++ address |= (u32)umsg->params.pci_cfg.function
++ << 12; /* [14:12] - Function */
++ address |= (u32)umsg->params.pci_cfg.device
++ << 15; /* [19:15] - Device */
++ address |= (u32)umsg->params.pci_cfg.bus
++ << 20; /* [27:20] - Bus */
++ /* [31:28] - Reserved */
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_RDENDPTCFG_CMD;
++ msg->tx_buf[1] = 0x00; /* request byte for Host ID|Retry bit */
++ msg->tx_buf[2] = umsg->msg_type; /* Message Type */
++ msg->tx_buf[3] = 0x00; /* Endpoint ID */
++ msg->tx_buf[4] = 0x00; /* Reserved */
++ msg->tx_buf[5] = 0x00; /* Reserved */
++ msg->tx_buf[6] = PECI_ENDPTCFG_ADDR_TYPE_PCI; /* Addr Type */
++ msg->tx_buf[7] = umsg->params.pci_cfg.seg; /* PCI Segment */
++ msg->tx_buf[8] = (u8)address; /* LSB - PCI Config Address */
++ msg->tx_buf[9] = (u8)(address >> 8); /* PCI Config Address */
++ msg->tx_buf[10] = (u8)(address >> 16); /* PCI Config Address */
++ msg->tx_buf[11] =
++ (u8)(address >> 24); /* MSB - PCI Config Address */
++ break;
++
++ case PECI_ENDPTCFG_TYPE_MMIO:
++ /*
++ * Per the PECI spec, the read length must be a byte, word,
++ * dword, or qword
++ */
++ if (umsg->rx_len != 1 && umsg->rx_len != 2 &&
++ umsg->rx_len != 4 && umsg->rx_len != 8) {
++ dev_dbg(&adapter->dev,
++ "Invalid read length, rx_len: %d\n",
++ umsg->rx_len);
++ return -EINVAL;
++ }
++ /*
++ * Per the PECI spec, the address type must specify either DWORD
++ * or QWORD
++ */
++ if (umsg->params.mmio.addr_type !=
++ PECI_ENDPTCFG_ADDR_TYPE_MMIO_D &&
++ umsg->params.mmio.addr_type !=
++ PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q) {
++ dev_dbg(&adapter->dev,
++ "Invalid address type, addr_type: %d\n",
++ umsg->params.mmio.addr_type);
++ return -EINVAL;
++ }
++
++ if (umsg->params.mmio.addr_type ==
++ PECI_ENDPTCFG_ADDR_TYPE_MMIO_D)
++ tx_size = PECI_RDENDPTCFG_MMIO_D_WRITE_LEN;
++ else
++ tx_size = PECI_RDENDPTCFG_MMIO_Q_WRITE_LEN;
++ msg = peci_get_xfer_msg(tx_size,
++ PECI_RDENDPTCFG_READ_LEN_BASE +
++ umsg->rx_len);
++ if (!msg)
++ return -ENOMEM;
++
++ address = umsg->params.mmio.function; /* [2:0] - Function */
++ address |= (u32)umsg->params.mmio.device
++ << 3; /* [7:3] - Device */
++
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_RDENDPTCFG_CMD;
++ msg->tx_buf[1] = 0x00; /* request byte for Host ID|Retry bit */
++ msg->tx_buf[2] = umsg->msg_type; /* Message Type */
++ msg->tx_buf[3] = 0x00; /* Endpoint ID */
++ msg->tx_buf[4] = 0x00; /* Reserved */
++ msg->tx_buf[5] = umsg->params.mmio.bar; /* BAR # */
++ msg->tx_buf[6] = umsg->params.mmio.addr_type; /* Address Type */
++ msg->tx_buf[7] = umsg->params.mmio.seg; /* PCI Segment */
++ msg->tx_buf[8] = (u8)address; /* Function/Device */
++ msg->tx_buf[9] = umsg->params.mmio.bus; /* PCI Bus */
++ msg->tx_buf[10] = (u8)umsg->params.mmio
++ .offset; /* LSB - Register Offset */
++ msg->tx_buf[11] = (u8)(umsg->params.mmio.offset
++ >> 8); /* Register Offset */
++ msg->tx_buf[12] = (u8)(umsg->params.mmio.offset
++ >> 16); /* Register Offset */
++ msg->tx_buf[13] = (u8)(umsg->params.mmio.offset
++ >> 24); /* MSB - DWORD Register Offset */
++ if (umsg->params.mmio.addr_type ==
++ PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q) {
++ msg->tx_buf[14] = (u8)(umsg->params.mmio.offset
++ >> 32); /* Register Offset */
++ msg->tx_buf[15] = (u8)(umsg->params.mmio.offset
++ >> 40); /* Register Offset */
++ msg->tx_buf[16] = (u8)(umsg->params.mmio.offset
++ >> 48); /* Register Offset */
++ msg->tx_buf[17] =
++ (u8)(umsg->params.mmio.offset
++ >> 56); /* MSB - QWORD Register Offset */
++ }
++ break;
++
++ default:
++ return -EINVAL;
++ }
++
++ ret = peci_xfer_with_retries(adapter, msg, false);
++ if (!ret)
++ memcpy(umsg->data, &msg->rx_buf[1], umsg->rx_len);
++
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
++
++ return ret;
++}
++
++static int peci_cmd_wr_end_pt_cfg(struct peci_adapter *adapter, void *vmsg)
++{
++ struct peci_wr_end_pt_cfg_msg *umsg = vmsg;
++ struct peci_xfer_msg *msg = NULL;
++ u8 tx_size, aw_fcs;
++ int ret, i, idx;
++ u32 address;
++
++ switch (umsg->msg_type) {
++ case PECI_ENDPTCFG_TYPE_LOCAL_PCI:
++ case PECI_ENDPTCFG_TYPE_PCI:
++ /*
++ * 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;
++ }
++
++ msg = peci_get_xfer_msg(PECI_WRENDPTCFG_PCI_WRITE_LEN_BASE +
++ umsg->tx_len, PECI_WRENDPTCFG_READ_LEN);
++ if (!msg)
++ return -ENOMEM;
++
++ address = umsg->params.pci_cfg.reg; /* [11:0] - Register */
++ address |= (u32)umsg->params.pci_cfg.function
++ << 12; /* [14:12] - Function */
++ address |= (u32)umsg->params.pci_cfg.device
++ << 15; /* [19:15] - Device */
++ address |= (u32)umsg->params.pci_cfg.bus
++ << 20; /* [27:20] - Bus */
++ /* [31:28] - Reserved */
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_WRENDPTCFG_CMD;
++ msg->tx_buf[1] = 0x00; /* request byte for Host ID|Retry bit */
++ msg->tx_buf[2] = umsg->msg_type; /* Message Type */
++ msg->tx_buf[3] = 0x00; /* Endpoint ID */
++ msg->tx_buf[4] = 0x00; /* Reserved */
++ msg->tx_buf[5] = 0x00; /* Reserved */
++ msg->tx_buf[6] = PECI_ENDPTCFG_ADDR_TYPE_PCI; /* Addr Type */
++ msg->tx_buf[7] = umsg->params.pci_cfg.seg; /* PCI Segment */
++ msg->tx_buf[8] = (u8)address; /* LSB - PCI Config Address */
++ msg->tx_buf[9] = (u8)(address >> 8); /* PCI Config Address */
++ msg->tx_buf[10] = (u8)(address >> 16); /* PCI Config Address */
++ msg->tx_buf[11] =
++ (u8)(address >> 24); /* MSB - PCI Config Address */
++ for (i = 0; i < umsg->tx_len; i++)
++ msg->tx_buf[12 + i] = (u8)(umsg->value >> (i << 3));
++
++ /* Add an Assured Write Frame Check Sequence byte */
++ ret = peci_aw_fcs(msg, 15 + umsg->tx_len, &aw_fcs);
++ if (ret)
++ goto out;
++
++ msg->tx_buf[12 + i] = 0x80 ^ aw_fcs;
++ break;
++
++ case PECI_ENDPTCFG_TYPE_MMIO:
++ /*
++ * Per the PECI spec, the write length must be a byte, word,
++ * dword, or qword
++ */
++ if (umsg->tx_len != 1 && umsg->tx_len != 2 &&
++ umsg->tx_len != 4 && umsg->tx_len != 8) {
++ dev_dbg(&adapter->dev,
++ "Invalid write length, tx_len: %d\n",
++ umsg->tx_len);
++ return -EINVAL;
++ }
++ /*
++ * Per the PECI spec, the address type must specify either DWORD
++ * or QWORD
++ */
++ if (umsg->params.mmio.addr_type !=
++ PECI_ENDPTCFG_ADDR_TYPE_MMIO_D &&
++ umsg->params.mmio.addr_type !=
++ PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q) {
++ dev_dbg(&adapter->dev,
++ "Invalid address type, addr_type: %d\n",
++ umsg->params.mmio.addr_type);
++ return -EINVAL;
++ }
++
++ if (umsg->params.mmio.addr_type ==
++ PECI_ENDPTCFG_ADDR_TYPE_MMIO_D)
++ tx_size = PECI_WRENDPTCFG_MMIO_D_WRITE_LEN_BASE +
++ umsg->tx_len;
++ else
++ tx_size = PECI_WRENDPTCFG_MMIO_Q_WRITE_LEN_BASE +
++ umsg->tx_len;
++ msg = peci_get_xfer_msg(tx_size, PECI_WRENDPTCFG_READ_LEN);
++ if (!msg)
++ return -ENOMEM;
++
++ address = umsg->params.mmio.function; /* [2:0] - Function */
++ address |= (u32)umsg->params.mmio.device
++ << 3; /* [7:3] - Device */
++
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_WRENDPTCFG_CMD;
++ msg->tx_buf[1] = 0x00; /* request byte for Host ID|Retry bit */
++ msg->tx_buf[2] = umsg->msg_type; /* Message Type */
++ msg->tx_buf[3] = 0x00; /* Endpoint ID */
++ msg->tx_buf[4] = 0x00; /* Reserved */
++ msg->tx_buf[5] = umsg->params.mmio.bar; /* BAR # */
++ msg->tx_buf[6] = umsg->params.mmio.addr_type; /* Address Type */
++ msg->tx_buf[7] = umsg->params.mmio.seg; /* PCI Segment */
++ msg->tx_buf[8] = (u8)address; /* Function/Device */
++ msg->tx_buf[9] = umsg->params.mmio.bus; /* PCI Bus */
++ msg->tx_buf[10] = (u8)umsg->params.mmio
++ .offset; /* LSB - Register Offset */
++ msg->tx_buf[11] = (u8)(umsg->params.mmio.offset
++ >> 8); /* Register Offset */
++ msg->tx_buf[12] = (u8)(umsg->params.mmio.offset
++ >> 16); /* Register Offset */
++ msg->tx_buf[13] = (u8)(umsg->params.mmio.offset
++ >> 24); /* MSB - DWORD Register Offset */
++ if (umsg->params.mmio.addr_type ==
++ PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q) {
++ msg->tx_len = PECI_WRENDPTCFG_MMIO_Q_WRITE_LEN_BASE;
++ msg->tx_buf[14] = (u8)(umsg->params.mmio.offset
++ >> 32); /* Register Offset */
++ msg->tx_buf[15] = (u8)(umsg->params.mmio.offset
++ >> 40); /* Register Offset */
++ msg->tx_buf[16] = (u8)(umsg->params.mmio.offset
++ >> 48); /* Register Offset */
++ msg->tx_buf[17] =
++ (u8)(umsg->params.mmio.offset
++ >> 56); /* MSB - QWORD Register Offset */
++ idx = 18;
++ } else {
++ idx = 14;
++ }
++ for (i = 0; i < umsg->tx_len; i++)
++ msg->tx_buf[idx + i] = (u8)(umsg->value >> (i << 3));
++
++ /* Add an Assured Write Frame Check Sequence byte */
++ ret = peci_aw_fcs(msg, idx + 3 + umsg->tx_len, &aw_fcs);
++ if (ret)
++ goto out;
++
++ msg->tx_buf[idx + i] = 0x80 ^ aw_fcs;
++ break;
++
++ default:
++ return -EINVAL;
++ }
++
++ ret = peci_xfer_with_retries(adapter, msg, false);
++
++out:
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
++
++ return ret;
++}
++
++static int peci_cmd_crashdump_disc(struct peci_adapter *adapter, void *vmsg)
++{
++ struct peci_crashdump_disc_msg *umsg = vmsg;
++ struct peci_xfer_msg *msg;
++ int ret;
++
++ /* Per the EDS, the read length must be a byte, word, or qword */
++ if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 8) {
++ dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n",
++ umsg->rx_len);
++ return -EINVAL;
++ }
++
++ msg = peci_get_xfer_msg(PECI_CRASHDUMP_DISC_WRITE_LEN,
++ PECI_CRASHDUMP_DISC_READ_LEN_BASE +
++ umsg->rx_len);
++ if (!msg)
++ return -ENOMEM;
++
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_CRASHDUMP_CMD;
++ msg->tx_buf[1] = 0x00; /* request byte for Host ID | Retry bit */
++ /* Host ID is 0 for PECI 3.0 */
++ msg->tx_buf[2] = PECI_CRASHDUMP_DISC_VERSION;
++ msg->tx_buf[3] = PECI_CRASHDUMP_DISC_OPCODE;
++ msg->tx_buf[4] = umsg->subopcode;
++ msg->tx_buf[5] = umsg->param0;
++ msg->tx_buf[6] = (u8)umsg->param1;
++ msg->tx_buf[7] = (u8)(umsg->param1 >> 8);
++ msg->tx_buf[8] = umsg->param2;
++
++ ret = peci_xfer_with_retries(adapter, msg, false);
++ if (!ret)
++ memcpy(umsg->data, &msg->rx_buf[1], umsg->rx_len);
++
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
++
++ return ret;
++}
++
++static int peci_cmd_crashdump_get_frame(struct peci_adapter *adapter,
++ void *vmsg)
++{
++ struct peci_crashdump_get_frame_msg *umsg = vmsg;
++ struct peci_xfer_msg *msg;
++ int ret;
++
++ /* Per the EDS, the read length must be a qword or dqword */
++ if (umsg->rx_len != 8 && umsg->rx_len != 16) {
++ dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n",
++ umsg->rx_len);
++ return -EINVAL;
++ }
++
++ msg = peci_get_xfer_msg(PECI_CRASHDUMP_GET_FRAME_WRITE_LEN,
++ PECI_CRASHDUMP_GET_FRAME_READ_LEN_BASE +
++ umsg->rx_len);
++ if (!msg)
++ return -ENOMEM;
++
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_CRASHDUMP_CMD;
++ msg->tx_buf[1] = 0x00; /* request byte for Host ID | Retry bit */
++ /* Host ID is 0 for PECI 3.0 */
++ msg->tx_buf[2] = PECI_CRASHDUMP_GET_FRAME_VERSION;
++ msg->tx_buf[3] = PECI_CRASHDUMP_GET_FRAME_OPCODE;
++ msg->tx_buf[4] = (u8)umsg->param0;
++ msg->tx_buf[5] = (u8)(umsg->param0 >> 8);
++ msg->tx_buf[6] = (u8)umsg->param1;
++ msg->tx_buf[7] = (u8)(umsg->param1 >> 8);
++ msg->tx_buf[8] = (u8)umsg->param2;
++ msg->tx_buf[8] = (u8)(umsg->param2 >> 8);
++
++ ret = peci_xfer_with_retries(adapter, msg, false);
++ if (!ret)
++ memcpy(umsg->data, &msg->rx_buf[1], umsg->rx_len);
++
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
++
++ return ret;
++}
++
+ typedef int (*peci_cmd_fn_type)(struct peci_adapter *, void *);
+
+ static const peci_cmd_fn_type peci_cmd_fn[PECI_CMD_MAX] = {
+@@ -741,10 +1166,15 @@ static const peci_cmd_fn_type peci_cmd_fn[PECI_CMD_MAX] = {
+ peci_cmd_wr_pkg_cfg,
+ peci_cmd_rd_ia_msr,
+ peci_cmd_wr_ia_msr,
++ peci_cmd_rd_ia_msrex,
+ peci_cmd_rd_pci_cfg,
+ peci_cmd_wr_pci_cfg,
+ peci_cmd_rd_pci_cfg_local,
+ peci_cmd_wr_pci_cfg_local,
++ peci_cmd_rd_end_pt_cfg,
++ peci_cmd_wr_end_pt_cfg,
++ peci_cmd_crashdump_disc,
++ peci_cmd_crashdump_get_frame,
+ };
+
+ /**
+diff --git a/include/uapi/linux/peci-ioctl.h b/include/uapi/linux/peci-ioctl.h
+index 253fb42..1158254 100644
+--- a/include/uapi/linux/peci-ioctl.h
++++ b/include/uapi/linux/peci-ioctl.h
+@@ -47,6 +47,7 @@
+ * @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_IA_MSREX: read 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
+@@ -67,10 +68,15 @@ enum peci_cmd {
+ PECI_CMD_WR_PKG_CFG,
+ PECI_CMD_RD_IA_MSR,
+ PECI_CMD_WR_IA_MSR,
++ PECI_CMD_RD_IA_MSREX,
+ PECI_CMD_RD_PCI_CFG,
+ PECI_CMD_WR_PCI_CFG,
+ PECI_CMD_RD_PCI_CFG_LOCAL,
+ PECI_CMD_WR_PCI_CFG_LOCAL,
++ PECI_CMD_RD_END_PT_CFG,
++ PECI_CMD_WR_END_PT_CFG,
++ PECI_CMD_CRASHDUMP_DISC,
++ PECI_CMD_CRASHDUMP_GET_FRAME,
+ PECI_CMD_MAX
+ };
+
+@@ -312,6 +318,34 @@ struct peci_wr_ia_msr_msg {
+ } __attribute__((__packed__));
+
+ /**
++ * struct peci_rd_ia_msrex_msg - RdIAMSREX command
++ * @addr: address of the client
++ * @thread_id: ID of the specific logical processor
++ * @address: address of MSR to read from
++ * @cc: completion code
++ * @value: data to be read
++ *
++ * The RdIAMSREX() PECI command provides read access to Model Specific Registers
++ * (MSRs) defined in the processor's Intel Architecture (IA).
++ * The differences between RdIAMSREX() and RdIAMSR() are that:
++ * (1)RdIAMSR() can only read MC registers, RdIAMSREX() can read all MSRs
++ * (2)thread_id of RdIAMSR() is u8, thread_id of RdIAMSREX() is u16
++ */
++struct peci_rd_ia_msrex_msg {
++#define PECI_RDIAMSREX_WRITE_LEN 6
++#define PECI_RDIAMSREX_READ_LEN 9
++#define PECI_RDIAMSREX_CMD 0xd1
++
++ __u8 addr;
++ __u8 padding0;
++ __u16 thread_id;
++ __u16 address;
++ __u8 cc;
++ __u8 padding1;
++ __u64 value;
++} __attribute__((__packed__));
++
++/**
+ * struct peci_rd_pci_cfg_msg - RdPCIConfig command
+ * @addr: address of the client
+ * @bus: PCI bus number
+@@ -438,6 +472,132 @@ struct peci_wr_pci_cfg_local_msg {
+ __u32 value;
+ } __attribute__((__packed__));
+
++struct peci_rd_end_pt_cfg_msg {
++#define PECI_RDENDPTCFG_PCI_WRITE_LEN 12
++#define PECI_RDENDPTCFG_MMIO_D_WRITE_LEN 14
++#define PECI_RDENDPTCFG_MMIO_Q_WRITE_LEN 18
++#define PECI_RDENDPTCFG_READ_LEN_BASE 1
++#define PECI_RDENDPTCFG_CMD 0xc1
++
++ __u8 addr;
++ __u8 msg_type;
++#define PECI_ENDPTCFG_TYPE_LOCAL_PCI 0x03
++#define PECI_ENDPTCFG_TYPE_PCI 0x04
++#define PECI_ENDPTCFG_TYPE_MMIO 0x05
++
++ union {
++ struct {
++ __u8 seg;
++ __u8 bus;
++ __u8 device;
++ __u8 function;
++ __u16 reg;
++ } pci_cfg;
++ struct {
++ __u8 seg;
++ __u8 bus;
++ __u8 device;
++ __u8 function;
++ __u8 bar;
++ __u8 addr_type;
++#define PECI_ENDPTCFG_ADDR_TYPE_PCI 0x04
++#define PECI_ENDPTCFG_ADDR_TYPE_MMIO_D 0x05
++#define PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q 0x06
++
++ __u64 offset;
++ } mmio;
++ } params;
++ __u8 rx_len;
++ __u8 cc;
++ __u8 padding[2];
++ __u8 data[8];
++} __attribute__((__packed__));
++
++struct peci_wr_end_pt_cfg_msg {
++#define PECI_WRENDPTCFG_PCI_WRITE_LEN_BASE 13
++#define PECI_WRENDPTCFG_MMIO_D_WRITE_LEN_BASE 15
++#define PECI_WRENDPTCFG_MMIO_Q_WRITE_LEN_BASE 19
++#define PECI_WRENDPTCFG_READ_LEN 1
++#define PECI_WRENDPTCFG_CMD 0xc5
++
++ __u8 addr;
++ __u8 msg_type;
++ /* See msg_type in struct peci_rd_end_pt_cfg_msg */
++
++ union {
++ struct {
++ __u8 seg;
++ __u8 bus;
++ __u8 device;
++ __u8 function;
++ __u16 reg;
++ } pci_cfg;
++ struct {
++ __u8 seg;
++ __u8 bus;
++ __u8 device;
++ __u8 function;
++ __u8 bar;
++ __u8 addr_type;
++ /* See addr_type in struct peci_rd_end_pt_cfg_msg */
++
++ __u64 offset;
++ } mmio;
++ } params;
++ __u8 tx_len;
++ __u8 cc;
++ __u8 padding[2];
++ __u64 value;
++} __attribute__((__packed__));
++
++/* Crashdump Agent */
++#define PECI_CRASHDUMP_CORE 0x00
++#define PECI_CRASHDUMP_TOR 0x01
++
++/* Crashdump Agent Param */
++#define PECI_CRASHDUMP_PAYLOAD_SIZE 0x00
++
++/* Crashdump Agent Data Param */
++#define PECI_CRASHDUMP_AGENT_ID 0x00
++#define PECI_CRASHDUMP_AGENT_PARAM 0x01
++
++struct peci_crashdump_disc_msg {
++ __u8 addr;
++ __u8 subopcode;
++#define PECI_CRASHDUMP_ENABLED 0x00
++#define PECI_CRASHDUMP_NUM_AGENTS 0x01
++#define PECI_CRASHDUMP_AGENT_DATA 0x02
++
++ __u8 cc;
++ __u8 param0;
++ __u16 param1;
++ __u8 param2;
++ __u8 rx_len;
++ __u8 data[8];
++} __attribute__((__packed__));
++
++struct peci_crashdump_get_frame_msg {
++#define PECI_CRASHDUMP_DISC_WRITE_LEN 9
++#define PECI_CRASHDUMP_DISC_READ_LEN_BASE 1
++#define PECI_CRASHDUMP_DISC_VERSION 0
++#define PECI_CRASHDUMP_DISC_OPCODE 1
++#define PECI_CRASHDUMP_GET_FRAME_WRITE_LEN 10
++#define PECI_CRASHDUMP_GET_FRAME_READ_LEN_BASE 1
++#define PECI_CRASHDUMP_GET_FRAME_VERSION 0
++#define PECI_CRASHDUMP_GET_FRAME_OPCODE 3
++#define PECI_CRASHDUMP_CMD 0x71
++
++ __u8 addr;
++ __u8 padding0;
++ __u16 param0;
++ __u16 param1;
++ __u16 param2;
++ __u8 rx_len;
++ __u8 cc;
++ __u8 padding1[2];
++ __u8 data[16];
++} __attribute__((__packed__));
++
+ #define PECI_IOC_BASE 0xb7
+
+ #define PECI_IOC_XFER \
+@@ -464,6 +624,9 @@ struct peci_wr_pci_cfg_local_msg {
+ #define PECI_IOC_WR_IA_MSR \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_IA_MSR, struct peci_wr_ia_msr_msg)
+
++#define PECI_IOC_RD_IA_MSREX \
++ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_IA_MSREX, struct peci_rd_ia_msrex_msg)
++
+ #define PECI_IOC_RD_PCI_CFG \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_PCI_CFG, struct peci_rd_pci_cfg_msg)
+
+@@ -478,4 +641,20 @@ struct peci_wr_pci_cfg_local_msg {
+ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_PCI_CFG_LOCAL, \
+ struct peci_wr_pci_cfg_local_msg)
+
++#define PECI_IOC_RD_END_PT_CFG \
++ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_END_PT_CFG, \
++ struct peci_rd_end_pt_cfg_msg)
++
++#define PECI_IOC_WR_END_PT_CFG \
++ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_END_PT_CFG, \
++ struct peci_wr_end_pt_cfg_msg)
++
++#define PECI_IOC_CRASHDUMP_DISC \
++ _IOWR(PECI_IOC_BASE, PECI_CMD_CRASHDUMP_DISC, \
++ struct peci_crashdump_disc_msg)
++
++#define PECI_IOC_CRASHDUMP_GET_FRAME \
++ _IOWR(PECI_IOC_BASE, PECI_CMD_CRASHDUMP_GET_FRAME, \
++ struct peci_crashdump_get_frame_msg)
++
+ #endif /* __PECI_IOCTL_H */
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch
new file mode 100644
index 000000000..28bc8d36c
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch
@@ -0,0 +1,95 @@
+From a4413fe3ba94906243b632e0b9e0e9a91620dd22 Mon Sep 17 00:00:00 2001
+From: "Corona, Ernesto" <ernesto.corona@intel.com>
+Date: Fri, 1 Mar 2019 11:46:09 -0700
+Subject: [PATCH] Update AST2500d JTAG driver. Step 1
+
+Update AST2500d JTAG driver. Remove Legacy driver but keep headers.
+
+Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com>
+---
+ include/uapi/linux/jtag_drv.h | 73 +++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 73 insertions(+)
+ create mode 100644 include/uapi/linux/jtag_drv.h
+
+diff --git a/include/uapi/linux/jtag_drv.h b/include/uapi/linux/jtag_drv.h
+new file mode 100644
+index 000000000000..4df638f8fa43
+--- /dev/null
++++ b/include/uapi/linux/jtag_drv.h
+@@ -0,0 +1,73 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/* Copyright (C) 2012-2017 ASPEED Technology Inc. */
++/* Copyright (c) 2018 Intel Corporation */
++
++#ifndef __JTAG_DRV_H__
++#define __JTAG_DRV_H__
++
++enum xfer_mode {
++ HW_MODE = 0,
++ SW_MODE
++} xfer_mode;
++
++struct tck_bitbang {
++ __u8 tms;
++ __u8 tdi;
++ __u8 tdo;
++} __attribute__((__packed__));
++
++struct scan_xfer {
++ __u8 mode;
++ __u32 tap_state;
++ __u32 length;
++ __u8 *tdi;
++ __u32 tdi_bytes;
++ __u8 *tdo;
++ __u32 tdo_bytes;
++ __u32 end_tap_state;
++} __attribute__((__packed__));
++
++struct set_tck_param {
++ __u8 mode;
++ __u32 tck;
++} __attribute__((__packed__));
++
++struct get_tck_param {
++ __u8 mode;
++ __u32 tck;
++} __attribute__((__packed__));
++
++struct tap_state_param {
++ __u8 mode;
++ __u32 from_state;
++ __u32 to_state;
++} __attribute__((__packed__));
++
++enum jtag_states {
++ jtag_tlr,
++ jtag_rti,
++ jtag_sel_dr,
++ jtag_cap_dr,
++ jtag_shf_dr,
++ jtag_ex1_dr,
++ jtag_pau_dr,
++ jtag_ex2_dr,
++ jtag_upd_dr,
++ jtag_sel_ir,
++ jtag_cap_ir,
++ jtag_shf_ir,
++ jtag_ex1_ir,
++ jtag_pau_ir,
++ jtag_ex2_ir,
++ jtag_upd_ir
++} jtag_states;
++
++#define JTAGIOC_BASE 'T'
++
++#define AST_JTAG_SET_TCK _IOW(JTAGIOC_BASE, 3, struct set_tck_param)
++#define AST_JTAG_GET_TCK _IOR(JTAGIOC_BASE, 4, struct get_tck_param)
++#define AST_JTAG_BITBANG _IOWR(JTAGIOC_BASE, 5, struct tck_bitbang)
++#define AST_JTAG_SET_TAPSTATE _IOW(JTAGIOC_BASE, 6, struct tap_state_param)
++#define AST_JTAG_READWRITESCAN _IOWR(JTAGIOC_BASE, 7, struct scan_xfer)
++
++#endif
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0030-Add-dump-debug-code-into-I2C-drivers.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0030-Add-dump-debug-code-into-I2C-drivers.patch
new file mode 100644
index 000000000..bef1d0ae8
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0030-Add-dump-debug-code-into-I2C-drivers.patch
@@ -0,0 +1,145 @@
+From 8b9bca54ec03fb80834eb8d15dd599293af6d971 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Fri, 29 Jun 2018 11:00:02 -0700
+Subject: [PATCH] Add dump debug code into I2C drivers
+
+This commit enables dump debug of master and slave I2C drivers.
+This is only for downstream debug purpose so it shouldn't go to
+the upstream.
+
+Usage (in case of bus 5 for an example):
+echo 5 > /sys/module/i2c_aspeed/parameters/dump_debug_bus_id
+echo 1 > /sys/module/i2c_aspeed/parameters/dump_debug
+echo 5 > /sys/module/i2c_slave_mqueue/parameters/dump_debug_bus_id
+echo 1 > /sys/module/i2c_slave_mqueue/parameters/dump_debug
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/i2c/busses/i2c-aspeed.c | 26 ++++++++++++++++++++++++++
+ drivers/i2c/i2c-slave-mqueue.c | 24 ++++++++++++++++++++++++
+ 2 files changed, 50 insertions(+)
+
+diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
+index c2a6e5a27314..e1719b1f2020 100644
+--- a/drivers/i2c/busses/i2c-aspeed.c
++++ b/drivers/i2c/busses/i2c-aspeed.c
+@@ -163,6 +163,21 @@ struct aspeed_i2c_bus {
+ #endif /* CONFIG_I2C_SLAVE */
+ };
+
++static bool dump_debug __read_mostly;
++static int dump_debug_bus_id __read_mostly;
++
++#define I2C_HEX_DUMP(bus, addr, flags, buf, len) \
++ do { \
++ if (dump_debug && (bus)->adap.nr == dump_debug_bus_id) { \
++ char dump_info[100] = {0,}; \
++ snprintf(dump_info, sizeof(dump_info), \
++ "bus_id:%d, addr:0x%02x, flags:0x%02x: ", \
++ (bus)->adap.nr, addr, flags); \
++ print_hex_dump(KERN_ERR, dump_info, DUMP_PREFIX_NONE, \
++ 16, 1, buf, len, true); \
++ } \
++ } while (0)
++
+ static int aspeed_i2c_reset(struct aspeed_i2c_bus *bus);
+
+ static int aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus)
+@@ -652,6 +667,7 @@ 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 i;
+
+ spin_lock_irqsave(&bus->lock, flags);
+ bus->cmd_err = 0;
+@@ -694,6 +710,11 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
+ return -ETIMEDOUT;
+ }
+
++ for (i = 0; i < num; i++) {
++ I2C_HEX_DUMP(bus, msgs[i].addr, msgs[i].flags,
++ msgs[i].buf, msgs[i].len);
++ }
++
+ return bus->master_xfer_result;
+ }
+
+@@ -1065,6 +1086,11 @@ static struct platform_driver aspeed_i2c_bus_driver = {
+ };
+ module_platform_driver(aspeed_i2c_bus_driver);
+
++module_param_named(dump_debug, dump_debug, bool, 0644);
++MODULE_PARM_DESC(dump_debug, "debug flag for dump printing");
++module_param_named(dump_debug_bus_id, dump_debug_bus_id, int, 0644);
++MODULE_PARM_DESC(dump_debug_bus_id, "bus id for dump debug printing");
++
+ MODULE_AUTHOR("Brendan Higgins <brendanhiggins@google.com>");
+ MODULE_DESCRIPTION("Aspeed I2C Bus Driver");
+ MODULE_LICENSE("GPL v2");
+diff --git a/drivers/i2c/i2c-slave-mqueue.c b/drivers/i2c/i2c-slave-mqueue.c
+index 6014bca0ff2a..2c7a6038409c 100644
+--- a/drivers/i2c/i2c-slave-mqueue.c
++++ b/drivers/i2c/i2c-slave-mqueue.c
+@@ -21,6 +21,7 @@ struct mq_msg {
+ struct mq_queue {
+ struct bin_attribute bin;
+ struct kernfs_node *kn;
++ struct i2c_client *client;
+
+ spinlock_t lock; /* spinlock for queue index handling */
+ int in;
+@@ -31,6 +32,21 @@ struct mq_queue {
+ struct mq_msg *queue;
+ };
+
++static bool dump_debug __read_mostly;
++static int dump_debug_bus_id __read_mostly;
++
++#define I2C_HEX_DUMP(client, buf, len) \
++ do { \
++ if (dump_debug && \
++ (client)->adapter->nr == dump_debug_bus_id) { \
++ char dump_info[100] = {0,}; \
++ snprintf(dump_info, sizeof(dump_info), \
++ "bus_id:%d: ", (client)->adapter->nr); \
++ print_hex_dump(KERN_ERR, dump_info, DUMP_PREFIX_NONE, \
++ 16, 1, buf, len, true); \
++ } \
++ } while (0)
++
+ static int i2c_slave_mqueue_callback(struct i2c_client *client,
+ enum i2c_slave_event event, u8 *val)
+ {
+@@ -101,6 +117,7 @@ static ssize_t i2c_slave_mqueue_bin_read(struct file *filp,
+ if (msg->len <= count) {
+ ret = msg->len;
+ memcpy(buf, msg->buf, ret);
++ I2C_HEX_DUMP(mq->client, buf, ret);
+ } else {
+ ret = -EOVERFLOW; /* Drop this HUGE one. */
+ }
+@@ -131,6 +148,8 @@ static int i2c_slave_mqueue_probe(struct i2c_client *client,
+
+ BUILD_BUG_ON(!is_power_of_2(MQ_QUEUE_SIZE));
+
++ mq->client = client;
++
+ buf = devm_kmalloc_array(dev, MQ_QUEUE_SIZE, MQ_MSGBUF_SIZE,
+ GFP_KERNEL);
+ if (!buf)
+@@ -212,6 +231,11 @@ static struct i2c_driver i2c_slave_mqueue_driver = {
+ };
+ module_i2c_driver(i2c_slave_mqueue_driver);
+
++module_param_named(dump_debug, dump_debug, bool, 0644);
++MODULE_PARM_DESC(dump_debug, "debug flag for dump printing");
++module_param_named(dump_debug_bus_id, dump_debug_bus_id, int, 0644);
++MODULE_PARM_DESC(dump_debug_bus_id, "bus id for dump debug printing");
++
+ MODULE_LICENSE("GPL v2");
+ MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
+ MODULE_DESCRIPTION("I2C slave mode for receiving and queuing messages");
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0031-Add-high-speed-baud-rate-support-for-UART.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0031-Add-high-speed-baud-rate-support-for-UART.patch
new file mode 100644
index 000000000..931483954
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0031-Add-high-speed-baud-rate-support-for-UART.patch
@@ -0,0 +1,135 @@
+From d80fcbb3e9d95a7e926598290012eea88a7c474d Mon Sep 17 00:00:00 2001
+From: Yong Li <yong.b.li@linux.intel.com>
+Date: Wed, 2 Jan 2019 15:06:43 +0800
+Subject: [PATCH] Add high speed baud rate support for UART
+
+In order to support high speed baud rate(921600 bps),
+the default UART clock(24MHz) needs to be switched
+to 192MHz(from USB2.0 port1 PHY).
+
+Create a new 192M Hz clock and assign it to uart,
+based on uart clock source configuration in SCU4C.
+
+bootloader(u-boot) will set SCU4C based on the environment configuration
+
+Signed-off-by: Yong Li <yong.b.li@linux.intel.com>
+---
+ drivers/clk/clk-aspeed.c | 44 +++++++++++++++++++++++++++-----
+ include/dt-bindings/clock/aspeed-clock.h | 2 ++
+ 2 files changed, 39 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c
+index 9bd5155598d6..24d56a724969 100644
+--- a/drivers/clk/clk-aspeed.c
++++ b/drivers/clk/clk-aspeed.c
+@@ -14,7 +14,9 @@
+
+ #include "clk-aspeed.h"
+
+-#define ASPEED_NUM_CLKS 38
++#define ASPEED_NUM_CLKS ASPEED_CLK_MAX
++#define UART_HIGH_SPEED_CLK 192000000
++#define UART_LOW_SPEED_CLK 24000000
+
+ #define ASPEED_RESET2_OFFSET 32
+
+@@ -29,6 +31,12 @@
+ #define ASPEED_MISC_CTRL 0x2c
+ #define UART_DIV13_EN BIT(12)
+ #define ASPEED_MAC_CLK_DLY 0x48
++#define ASPEED_MISC2_CTRL 0x4c
++#define UART1_HS_CLK_EN BIT(24)
++#define UART2_HS_CLK_EN BIT(25)
++#define UART3_HS_CLK_EN BIT(26)
++#define UART4_HS_CLK_EN BIT(27)
++#define UART5_HS_CLK_EN BIT(28)
+ #define ASPEED_STRAP 0x70
+ #define CLKIN_25MHZ_EN BIT(23)
+ #define AST2400_CLK_SOURCE_SEL BIT(18)
+@@ -386,7 +394,7 @@ static int aspeed_clk_probe(struct platform_device *pdev)
+ struct aspeed_reset *ar;
+ struct regmap *map;
+ struct clk_hw *hw;
+- u32 val, rate;
++ u32 val, rate, rate_hi;
+ int i, ret;
+
+ map = syscon_node_to_regmap(dev->of_node);
+@@ -420,16 +428,25 @@ static int aspeed_clk_probe(struct platform_device *pdev)
+
+ /* UART clock div13 setting */
+ regmap_read(map, ASPEED_MISC_CTRL, &val);
+- if (val & UART_DIV13_EN)
+- rate = 24000000 / 13;
+- else
+- rate = 24000000;
++ if (val & UART_DIV13_EN) {
++ rate = UART_LOW_SPEED_CLK / 13;
++ rate_hi = UART_HIGH_SPEED_CLK / 13;
++ } else {
++ rate = UART_LOW_SPEED_CLK;
++ rate_hi = UART_HIGH_SPEED_CLK;
++ }
+ /* TODO: Find the parent data for the uart clock */
+ hw = clk_hw_register_fixed_rate(dev, "uart", NULL, 0, rate);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+ aspeed_clk_data->hws[ASPEED_CLK_UART] = hw;
+
++ hw = clk_hw_register_fixed_rate(dev, "uart-hs", "usb-port1-gate", 0,
++ rate_hi);
++ if (IS_ERR(hw))
++ return PTR_ERR(hw);
++ aspeed_clk_data->hws[ASPEED_CLK_UART_HS] = hw;
++
+ /*
+ * Memory controller (M-PLL) PLL. This clock is configured by the
+ * bootloader, and is exposed to Linux as a read-only clock rate.
+@@ -539,9 +556,22 @@ static int aspeed_clk_probe(struct platform_device *pdev)
+ * UART[1..5] clock source mux
+ */
+
++ /* Get the uart clock source configuration from SCU4C*/
++ regmap_read(map, ASPEED_MISC2_CTRL, &val);
+ for (i = 0; i < ARRAY_SIZE(aspeed_gates); i++) {
+ const struct aspeed_gate_data *gd = &aspeed_gates[i];
+ u32 gate_flags;
++ char *parent_name;
++
++ /* For uart, needs to adjust the clock based on SCU4C value */
++ if ((i == ASPEED_CLK_GATE_UART1CLK && (val & UART1_HS_CLK_EN)) ||
++ (i == ASPEED_CLK_GATE_UART2CLK && (val & UART2_HS_CLK_EN)) ||
++ (i == ASPEED_CLK_GATE_UART5CLK && (val & UART5_HS_CLK_EN)) ||
++ (i == ASPEED_CLK_GATE_UART3CLK && (val & UART3_HS_CLK_EN)) ||
++ (i == ASPEED_CLK_GATE_UART4CLK && (val & UART4_HS_CLK_EN)))
++ parent_name = "uart-hs";
++ else
++ parent_name = gd->parent_name;
+
+ /* Special case: the USB port 1 clock (bit 14) is always
+ * working the opposite way from the other ones.
+@@ -549,7 +579,7 @@ static int aspeed_clk_probe(struct platform_device *pdev)
+ gate_flags = (gd->clock_idx == 14) ? 0 : CLK_GATE_SET_TO_DISABLE;
+ hw = aspeed_clk_hw_register_gate(dev,
+ gd->name,
+- gd->parent_name,
++ parent_name,
+ gd->flags,
+ map,
+ gd->clock_idx,
+diff --git a/include/dt-bindings/clock/aspeed-clock.h b/include/dt-bindings/clock/aspeed-clock.h
+index 64e245fb113f..df2f9fdfe5c1 100644
+--- a/include/dt-bindings/clock/aspeed-clock.h
++++ b/include/dt-bindings/clock/aspeed-clock.h
+@@ -41,6 +41,8 @@
+ #define ASPEED_CLK_24M 35
+ #define ASPEED_CLK_GATE_MAC1RCLK 36
+ #define ASPEED_CLK_GATE_MAC2RCLK 37
++#define ASPEED_CLK_UART_HS 38
++#define ASPEED_CLK_MAX 39
+
+ #define ASPEED_RESET_XDMA 0
+ #define ASPEED_RESET_MCTP 1
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch
new file mode 100644
index 000000000..82e90c059
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch
@@ -0,0 +1,556 @@
+From af686df07d23080834332b63fe37ee28b630ca2f Mon Sep 17 00:00:00 2001
+From: Oskar Senft <osk@google.com>
+Date: Wed, 8 Aug 2018 10:15:05 -0400
+Subject: [PATCH] misc: aspeed: Add Aspeed UART routing control driver.
+
+This driver adds sysfs files that allow the BMC userspace to configure
+how UARTs and physical serial I/O ports are routed.
+
+Tested: Checked correct behavior (both read & write) on TYAN S7106
+board by manually changing routing settings and confirming that bits
+flow as expected. Tested for UART1 and UART3 as this board doesn't have
+the other UARTs wired up in a testable way.
+
+Signed-off-by: Oskar Senft <osk@google.com>
+Signed-off-by: Yong Li <yong.b.li@linux.intel.com>
+---
+ .../ABI/stable/sysfs-driver-aspeed-uart-routing | 14 +
+ Documentation/misc-devices/aspeed-uart-routing.txt | 49 +++
+ arch/arm/boot/dts/aspeed-bmc-intel-purley.dts | 4 +
+ arch/arm/boot/dts/aspeed-g5.dtsi | 6 +
+ drivers/misc/Kconfig | 6 +
+ drivers/misc/Makefile | 1 +
+ drivers/misc/aspeed-uart-routing.c | 383 +++++++++++++++++++++
+ 7 files changed, 463 insertions(+)
+ create mode 100644 Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing
+ create mode 100644 Documentation/misc-devices/aspeed-uart-routing.txt
+ create mode 100644 drivers/misc/aspeed-uart-routing.c
+
+diff --git a/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing b/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing
+new file mode 100644
+index 000000000000..5068737d9c12
+--- /dev/null
++++ b/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing
+@@ -0,0 +1,14 @@
++What: /sys/bus/platform/drivers/aspeed-uart-routing/*/io*
++Date: August 2018
++Contact: Oskar Senft <osk@google.com>
++Description: Configures the input source for the specific physical
++ serial I/O port.
++Users: OpenBMC. Proposed changes should be mailed to
++ openbmc@lists.ozlabs.org
++
++What: /sys/bus/platform/drivers/aspeed-uart-routing/*/uart*
++Date: August 2018
++Contact: Oskar Senft <osk@google.com>
++Description: Configures the input source for the specific UART.
++Users: OpenBMC. Proposed changes should be mailed to
++ openbmc@lists.ozlabs.org
+diff --git a/Documentation/misc-devices/aspeed-uart-routing.txt b/Documentation/misc-devices/aspeed-uart-routing.txt
+new file mode 100644
+index 000000000000..afaf17cb7eda
+--- /dev/null
++++ b/Documentation/misc-devices/aspeed-uart-routing.txt
+@@ -0,0 +1,49 @@
++Kernel driver aspeed-uart-routing
++=================================
++
++Supported chips:
++ASPEED AST2500
++
++Author:
++Google LLC
++
++Description
++-----------
++
++The Aspeed AST2500 allows to dynamically route the inputs for the built-in
++UARTS and physical serial I/O ports.
++
++This allows, for example, to connect the output of UART to another UART.
++This can be used to enable host<->BMC communication via UARTs, e.g. to allow
++access to the host's serial console.
++
++This driver is for the BMC side. The sysfs files allow the BMC userspace
++which owns the system configuration policy, to configure how UARTs and
++physical serial I/O ports are routed.
++
++The driver provides the following files in sysfs:
++uart1 Configure the input signal to UART1.
++uart2 Configure the input signal to UART2.
++uart3 Configure the input signal to UART3.
++uart4 Configure the input signal to UART4.
++uart5 Configure the input signal to UART5.
++io1 Configure the input signal to physical serial port 1.
++io2 Configure the input signal to physical serial port 2.
++io3 Configure the input signal to physical serial port 3.
++io4 Configure the input signal to physical serial port 4.
++io5 Configure the input signal to physical serial port 5.
++
++When read, each file shows the list of available options with the currently
++selected option marked by square brackets "[]". The list of available options
++depends on the selected file.
++
++Example:
++$ cat /sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1
++[io1] io2 io3 io4 uart2 uart3 uart4 io6
++
++In this case, UART1 gets its input signal from IO1 (physical serial port 1).
++
++$ echo -n "uart3" \
++ >/sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1
++$ cat /sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1
++io1 io2 io3 io4 uart2 [uart3] uart4 io6
+diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts b/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts
+index 0aa2ac82cae4..403f29a74281 100644
+--- a/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts
++++ b/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts
+@@ -260,6 +260,10 @@
+ status = "okay";
+ };
+
++&uart_routing {
++ status = "okay";
++};
++
+ &mac1 {
+ status = "okay";
+
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index 26671cc4dbd5..8288002e4f02 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -524,6 +524,12 @@
+ status = "disabled";
+ };
+ };
++
++ uart_routing: uart_routing@9c {
++ compatible = "aspeed,ast2500-uart-routing";
++ reg = <0x9c 0x4>;
++ status = "disabled";
++ };
+ };
+
+ peci: bus@1e78b000 {
+diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
+index 50814caba1d3..439f3b0de702 100644
+--- a/drivers/misc/Kconfig
++++ b/drivers/misc/Kconfig
+@@ -463,6 +463,12 @@ config ASPEED_ESPI_SLAVE
+ Control Aspeed ast2500 eSPI slave controller to handle event
+ which needs the firmware's processing.
+
++config ASPEED_UART_ROUTING
++ tristate "Aspeed ast2500 UART routing control"
++ help
++ If you want to configure UART routing on Aspeed BMC platforms, enable
++ this option.
++
+ config PCI_ENDPOINT_TEST
+ depends on PCI
+ select CRC32
+diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
+index f168e6713440..87958cb74d00 100644
+--- a/drivers/misc/Makefile
++++ b/drivers/misc/Makefile
+@@ -54,6 +54,7 @@ obj-$(CONFIG_ECHO) += echo/
+ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
+ obj-$(CONFIG_CXL_BASE) += cxl/
+ obj-$(CONFIG_ASPEED_ESPI_SLAVE) += aspeed-espi-slave.o
++obj-$(CONFIG_ASPEED_UART_ROUTING) += aspeed-uart-routing.o
+ obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o
+ obj-$(CONFIG_OCXL) += ocxl/
+ obj-y += cardreader/
+diff --git a/drivers/misc/aspeed-uart-routing.c b/drivers/misc/aspeed-uart-routing.c
+new file mode 100644
+index 000000000000..21ef5d98c317
+--- /dev/null
++++ b/drivers/misc/aspeed-uart-routing.c
+@@ -0,0 +1,383 @@
++/*
++ * UART Routing driver for Aspeed AST2500
++ *
++ * Copyright (c) 2018 Google LLC
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++#include <linux/device.h>
++#include <linux/module.h>
++#include <linux/of_address.h>
++#include <linux/of_platform.h>
++
++/* The Aspeed AST2500 allows to dynamically route the inputs for the built-in
++ * UARTS and physical serial I/O ports.
++ *
++ * This allows, for example, to connect the output of UART to another UART.
++ * This can be used to enable host<->BMC communication via UARTs, e.g. to allow
++ * access to the host's serial console.
++ *
++ * This driver is for the BMC side. The sysfs files allow the BMC userspace
++ * which owns the system configuration policy, to configure how UARTs and
++ * physical serial I/O ports are routed.
++ */
++
++#define ASPEED_HICRA_IO1 "io1"
++#define ASPEED_HICRA_IO2 "io2"
++#define ASPEED_HICRA_IO3 "io3"
++#define ASPEED_HICRA_IO4 "io4"
++#define ASPEED_HICRA_IO5 "io5"
++#define ASPEED_HICRA_IO6 "io6"
++#define ASPEED_HICRA_UART1 "uart1"
++#define ASPEED_HICRA_UART2 "uart2"
++#define ASPEED_HICRA_UART3 "uart3"
++#define ASPEED_HICRA_UART4 "uart4"
++#define ASPEED_HICRA_UART5 "uart5"
++
++struct aspeed_uart_routing {
++ struct device *dev;
++ void __iomem *regs;
++ spinlock_t lock;
++};
++
++struct aspeed_uart_routing_selector {
++ struct device_attribute dev_attr;
++ int shift;
++ int mask;
++ const char * const options[];
++};
++
++#define to_routing_selector(_dev_attr) \
++ container_of(_dev_attr, struct aspeed_uart_routing_selector, dev_attr)
++
++
++static ssize_t aspeed_uart_routing_show(struct device *dev,
++ struct device_attribute *attr,
++ char *buf);
++
++static ssize_t aspeed_uart_routing_store(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t count);
++
++#define ROUTING_ATTR(_name) { \
++ .attr = {.name = _name, \
++ .mode = VERIFY_OCTAL_PERMISSIONS(S_IWUSR | S_IRUGO) }, \
++ .show = aspeed_uart_routing_show, \
++ .store = aspeed_uart_routing_store, \
++}
++
++static struct aspeed_uart_routing_selector uart5_sel = {
++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART5),
++ .shift = 28,
++ .mask = 0xf,
++ .options = {
++ ASPEED_HICRA_IO5, // 0
++ ASPEED_HICRA_IO1, // 1
++ ASPEED_HICRA_IO2, // 2
++ ASPEED_HICRA_IO3, // 3
++ ASPEED_HICRA_IO4, // 4
++ ASPEED_HICRA_UART1, // 5
++ ASPEED_HICRA_UART2, // 6
++ ASPEED_HICRA_UART3, // 7
++ ASPEED_HICRA_UART4, // 8
++ ASPEED_HICRA_IO6, // 9
++ NULL, // NULL termination
++ },
++};
++
++static struct aspeed_uart_routing_selector uart4_sel = {
++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART4),
++ .shift = 25,
++ .mask = 0x7,
++ .options = {
++ ASPEED_HICRA_IO4, // 0
++ ASPEED_HICRA_IO1, // 1
++ ASPEED_HICRA_IO2, // 2
++ ASPEED_HICRA_IO3, // 3
++ ASPEED_HICRA_UART1, // 4
++ ASPEED_HICRA_UART2, // 5
++ ASPEED_HICRA_UART3, // 6
++ ASPEED_HICRA_IO6, // 7
++ NULL, // NULL termination
++ },
++};
++
++static struct aspeed_uart_routing_selector uart3_sel = {
++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART3),
++ .shift = 22,
++ .mask = 0x7,
++ .options = {
++ ASPEED_HICRA_IO3, // 0
++ ASPEED_HICRA_IO4, // 1
++ ASPEED_HICRA_IO1, // 2
++ ASPEED_HICRA_IO2, // 3
++ ASPEED_HICRA_UART4, // 4
++ ASPEED_HICRA_UART1, // 5
++ ASPEED_HICRA_UART2, // 6
++ ASPEED_HICRA_IO6, // 7
++ NULL, // NULL termination
++ },
++};
++
++static struct aspeed_uart_routing_selector uart2_sel = {
++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART2),
++ .shift = 19,
++ .mask = 0x7,
++ .options = {
++ ASPEED_HICRA_IO2, // 0
++ ASPEED_HICRA_IO3, // 1
++ ASPEED_HICRA_IO4, // 2
++ ASPEED_HICRA_IO1, // 3
++ ASPEED_HICRA_UART3, // 4
++ ASPEED_HICRA_UART4, // 5
++ ASPEED_HICRA_UART1, // 6
++ ASPEED_HICRA_IO6, // 7
++ NULL, // NULL termination
++ },
++};
++
++static struct aspeed_uart_routing_selector uart1_sel = {
++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART1),
++ .shift = 16,
++ .mask = 0x7,
++ .options = {
++ ASPEED_HICRA_IO1, // 0
++ ASPEED_HICRA_IO2, // 1
++ ASPEED_HICRA_IO3, // 2
++ ASPEED_HICRA_IO4, // 3
++ ASPEED_HICRA_UART2, // 4
++ ASPEED_HICRA_UART3, // 5
++ ASPEED_HICRA_UART4, // 6
++ ASPEED_HICRA_IO6, // 7
++ NULL, // NULL termination
++ },
++};
++
++static struct aspeed_uart_routing_selector io5_sel = {
++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO5),
++ .shift = 12,
++ .mask = 0x7,
++ .options = {
++ ASPEED_HICRA_UART5, // 0
++ ASPEED_HICRA_UART1, // 1
++ ASPEED_HICRA_UART2, // 2
++ ASPEED_HICRA_UART3, // 3
++ ASPEED_HICRA_UART4, // 4
++ ASPEED_HICRA_IO1, // 5
++ ASPEED_HICRA_IO3, // 6
++ ASPEED_HICRA_IO6, // 7
++ NULL, // NULL termination
++ },
++};
++
++static struct aspeed_uart_routing_selector io4_sel = {
++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO4),
++ .shift = 9,
++ .mask = 0x7,
++ .options = {
++ ASPEED_HICRA_UART4, // 0
++ ASPEED_HICRA_UART5, // 1
++ ASPEED_HICRA_UART1, // 2
++ ASPEED_HICRA_UART2, // 3
++ ASPEED_HICRA_UART3, // 4
++ ASPEED_HICRA_IO1, // 5
++ ASPEED_HICRA_IO2, // 6
++ ASPEED_HICRA_IO6, // 7
++ NULL, // NULL termination
++ },
++};
++
++static struct aspeed_uart_routing_selector io3_sel = {
++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO3),
++ .shift = 6,
++ .mask = 0x7,
++ .options = {
++ ASPEED_HICRA_UART3, // 0
++ ASPEED_HICRA_UART4, // 1
++ ASPEED_HICRA_UART5, // 2
++ ASPEED_HICRA_UART1, // 3
++ ASPEED_HICRA_UART2, // 4
++ ASPEED_HICRA_IO1, // 5
++ ASPEED_HICRA_IO2, // 6
++ ASPEED_HICRA_IO6, // 7
++ NULL, // NULL termination
++ },
++};
++
++static struct aspeed_uart_routing_selector io2_sel = {
++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO2),
++ .shift = 3,
++ .mask = 0x7,
++ .options = {
++ ASPEED_HICRA_UART2, // 0
++ ASPEED_HICRA_UART3, // 1
++ ASPEED_HICRA_UART4, // 2
++ ASPEED_HICRA_UART5, // 3
++ ASPEED_HICRA_UART1, // 4
++ ASPEED_HICRA_IO3, // 5
++ ASPEED_HICRA_IO4, // 6
++ ASPEED_HICRA_IO6, // 7
++ NULL, // NULL termination
++ },
++};
++
++static struct aspeed_uart_routing_selector io1_sel = {
++ .dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO1),
++ .shift = 0,
++ .mask = 0x7,
++ .options = {
++ ASPEED_HICRA_UART1, // 0
++ ASPEED_HICRA_UART2, // 1
++ ASPEED_HICRA_UART3, // 2
++ ASPEED_HICRA_UART4, // 3
++ ASPEED_HICRA_UART5, // 4
++ ASPEED_HICRA_IO3, // 5
++ ASPEED_HICRA_IO4, // 6
++ ASPEED_HICRA_IO6, // 7
++ NULL, // NULL termination
++ },
++};
++
++
++static struct attribute *aspeed_uart_routing_attrs[] = {
++ &uart1_sel.dev_attr.attr,
++ &uart2_sel.dev_attr.attr,
++ &uart3_sel.dev_attr.attr,
++ &uart4_sel.dev_attr.attr,
++ &uart5_sel.dev_attr.attr,
++ &io1_sel.dev_attr.attr,
++ &io2_sel.dev_attr.attr,
++ &io3_sel.dev_attr.attr,
++ &io4_sel.dev_attr.attr,
++ &io5_sel.dev_attr.attr,
++ NULL,
++};
++
++static const struct attribute_group aspeed_uart_routing_attr_group = {
++ .attrs = aspeed_uart_routing_attrs,
++};
++
++static ssize_t aspeed_uart_routing_show(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ struct aspeed_uart_routing *uart_routing = dev_get_drvdata(dev);
++ struct aspeed_uart_routing_selector *sel = to_routing_selector(attr);
++ int val, pos, len;
++
++ val = (readl(uart_routing->regs) >> sel->shift) & sel->mask;
++
++ len = 0;
++ for (pos = 0; sel->options[pos] != NULL; ++pos) {
++ if (pos == val) {
++ len += snprintf(buf + len, PAGE_SIZE - 1 - len,
++ "[%s] ", sel->options[pos]);
++ } else {
++ len += snprintf(buf + len, PAGE_SIZE - 1 - len,
++ "%s ", sel->options[pos]);
++ }
++ }
++
++ if (val >= pos) {
++ len += snprintf(buf + len, PAGE_SIZE - 1 - len,
++ "[unknown(%d)]", val);
++ }
++
++ len += snprintf(buf + len, PAGE_SIZE - 1 - len, "\n");
++
++ return len;
++}
++
++static ssize_t aspeed_uart_routing_store(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct aspeed_uart_routing *uart_routing = dev_get_drvdata(dev);
++ struct aspeed_uart_routing_selector *sel = to_routing_selector(attr);
++ int val;
++ u32 reg;
++
++ val = match_string(sel->options, -1, buf);
++ if (val < 0) {
++ dev_err(dev, "invalid value \"%s\"\n", buf);
++ return -EINVAL;
++ }
++
++ spin_lock(&uart_routing->lock);
++ reg = readl(uart_routing->regs);
++ // Zero out existing value in specified bits.
++ reg &= ~(sel->mask << sel->shift);
++ // Set new value in specified bits.
++ reg |= (val & sel->mask) << sel->shift;
++ writel(reg, uart_routing->regs);
++ spin_unlock(&uart_routing->lock);
++
++ return count;
++}
++
++static int aspeed_uart_routing_probe(struct platform_device *pdev)
++{
++ struct aspeed_uart_routing *uart_routing;
++ struct resource *res;
++ int rc;
++
++ uart_routing = devm_kzalloc(&pdev->dev,
++ sizeof(*uart_routing),
++ GFP_KERNEL);
++ if (!uart_routing)
++ return -ENOMEM;
++
++ spin_lock_init(&uart_routing->lock);
++ uart_routing->dev = &pdev->dev;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ uart_routing->regs = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(uart_routing->regs))
++ return PTR_ERR(uart_routing->regs);
++
++ rc = sysfs_create_group(&uart_routing->dev->kobj,
++ &aspeed_uart_routing_attr_group);
++ if (rc < 0)
++ return rc;
++
++ platform_set_drvdata(pdev, uart_routing);
++
++ return 0;
++}
++
++static int aspeed_uart_routing_remove(struct platform_device *pdev)
++{
++ struct aspeed_uart_routing *uart_routing = platform_get_drvdata(pdev);
++
++ sysfs_remove_group(&uart_routing->dev->kobj,
++ &aspeed_uart_routing_attr_group);
++
++ return 0;
++}
++
++static const struct of_device_id aspeed_uart_routing_table[] = {
++ { .compatible = "aspeed,ast2500-uart-routing" },
++ { },
++};
++
++static struct platform_driver aspeed_uart_routing_driver = {
++ .driver = {
++ .name = "aspeed-uart-routing",
++ .of_match_table = aspeed_uart_routing_table,
++ },
++ .probe = aspeed_uart_routing_probe,
++ .remove = aspeed_uart_routing_remove,
++};
++
++module_platform_driver(aspeed_uart_routing_driver);
++
++MODULE_AUTHOR("Oskar Senft <osk@google.com>");
++MODULE_LICENSE("GPL v2");
++MODULE_DESCRIPTION("Driver to configure Aspeed UART routing");
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-aspeed-Swap-the-mac-nodes-numbering.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-aspeed-Swap-the-mac-nodes-numbering.patch
new file mode 100644
index 000000000..08f406ac1
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-aspeed-Swap-the-mac-nodes-numbering.patch
@@ -0,0 +1,85 @@
+From 89112c3971a540302834e5e987a1dec236bd752d Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Wed, 3 Oct 2018 10:17:58 -0700
+Subject: [PATCH] arm: dts: aspeed: Swap the mac nodes numbering
+
+This patch swaps the numbering of mac0 and mac1 to make a dedicated
+nic get assigned the first ethernet device number.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ arch/arm/boot/dts/aspeed-g4.dtsi | 16 ++++++++--------
+ arch/arm/boot/dts/aspeed-g5.dtsi | 16 ++++++++--------
+ 2 files changed, 16 insertions(+), 16 deletions(-)
+
+diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
+index 71563972d2fe..78251541a109 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -121,14 +121,6 @@
+ reg = <0x1e6c2000 0x80>;
+ };
+
+- mac0: ethernet@1e660000 {
+- compatible = "aspeed,ast2400-mac", "faraday,ftgmac100";
+- reg = <0x1e660000 0x180>;
+- interrupts = <2>;
+- clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>;
+- status = "disabled";
+- };
+-
+ mac1: ethernet@1e680000 {
+ compatible = "aspeed,ast2400-mac", "faraday,ftgmac100";
+ reg = <0x1e680000 0x180>;
+@@ -137,6 +129,14 @@
+ status = "disabled";
+ };
+
++ mac0: ethernet@1e660000 {
++ compatible = "aspeed,ast2400-mac", "faraday,ftgmac100";
++ reg = <0x1e660000 0x180>;
++ interrupts = <2>;
++ clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>;
++ status = "disabled";
++ };
++
+ ehci0: usb@1e6a1000 {
+ compatible = "aspeed,ast2400-ehci", "generic-ehci";
+ reg = <0x1e6a1000 0x100>;
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index 8288002e4f02..6d2c4494ce04 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -149,14 +149,6 @@
+ reg = <0x1e6c2000 0x80>;
+ };
+
+- mac0: ethernet@1e660000 {
+- compatible = "aspeed,ast2500-mac", "faraday,ftgmac100";
+- reg = <0x1e660000 0x180>;
+- interrupts = <2>;
+- clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>;
+- status = "disabled";
+- };
+-
+ mac1: ethernet@1e680000 {
+ compatible = "aspeed,ast2500-mac", "faraday,ftgmac100";
+ reg = <0x1e680000 0x180>;
+@@ -165,6 +157,14 @@
+ status = "disabled";
+ };
+
++ mac0: ethernet@1e660000 {
++ compatible = "aspeed,ast2500-mac", "faraday,ftgmac100";
++ reg = <0x1e660000 0x180>;
++ interrupts = <2>;
++ clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>;
++ status = "disabled";
++ };
++
+ ehci0: usb@1e6a1000 {
+ compatible = "aspeed,ast2500-ehci", "generic-ehci";
+ reg = <0x1e6a1000 0x100>;
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch
new file mode 100644
index 000000000..798038a8e
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch
@@ -0,0 +1,246 @@
+From 5ccf8e3e397edf195aa65e91af6e9ea16ed88882 Mon Sep 17 00:00:00 2001
+From: Cheng C Yang <cheng.c.yang@intel.com>
+Date: Fri, 9 Nov 2018 10:24:37 +0800
+Subject: [PATCH] Implement a memory driver share memory
+
+Implement a memory driver for BMC to access VGA share memory.
+The driver is used by MDRV2. In MDRV2 BIOS will send whole
+SMBIOS table to VGA memory and BMC can get the table from VGA
+memory through this driver.
+
+Signed-off-by: Cheng C Yang <cheng.c.yang@intel.com>
+---
+ .../bindings/soc/aspeed/aspeed-vga-sharedmem.txt | 20 +++
+ drivers/soc/aspeed/Kconfig | 9 ++
+ drivers/soc/aspeed/Makefile | 1 +
+ drivers/soc/aspeed/aspeed-vga-sharedmem.c | 163 +++++++++++++++++++++
+ 4 files changed, 193 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/soc/aspeed/aspeed-vga-sharedmem.txt
+ create mode 100644 drivers/soc/aspeed/aspeed-vga-sharedmem.c
+
+diff --git a/Documentation/devicetree/bindings/soc/aspeed/aspeed-vga-sharedmem.txt b/Documentation/devicetree/bindings/soc/aspeed/aspeed-vga-sharedmem.txt
+new file mode 100644
+index 000000000000..03f57c53e844
+--- /dev/null
++++ b/Documentation/devicetree/bindings/soc/aspeed/aspeed-vga-sharedmem.txt
+@@ -0,0 +1,20 @@
++* Aspeed VGA shared memory driver
++
++Aspeed VGA shared memory driver allow user to read data from AST2500
++VGA memory. This driver is required by ManagedDataRegionlV2
++specification. In the spec, BIOS will transfer whole SMBIOS table to
++VGA memroy and BMC get the table from VGA memory. 0penBMC project do
++not allow to use /dev/mem for security concerns. To get the data in
++VGA shared memory in user space, implement this driver only allowed
++user to mmap limited memory area.
++
++Required properties:
++- compatible: "aspeed,ast2500-vga-sharedmem"
++ - aspeed,ast2500-vga-sharedmem: Aspeed AST2500 family
++- reg: Should contain VGA shared memory start address and length
++
++Example:
++vga-shared-memory {
++ compatible = "aspeed,ast2500-vga-sharedmem";
++ reg = <0x9ff00000 0x100000>;
++};
+diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig
+index 285c19042c65..59f25d16d6e7 100644
+--- a/drivers/soc/aspeed/Kconfig
++++ b/drivers/soc/aspeed/Kconfig
+@@ -51,4 +51,13 @@ config ASPEED_P2A_CTRL
+ ioctl()s, the driver also provides an interface for userspace mappings to
+ a pre-defined region.
+
++config ASPEED_VGA_SHAREDMEM
++ tristate "Aspeed VGA Shared memory"
++ depends on SOC_ASPEED
++ help
++ To access VGA shared memory on Aspeed BMC, enable this option.
++ This driver used by ManagedDataRegionlV2 specification. In the
++ specification, BIOS will transfer whole SMBIOS table to VGA memory,
++ and BMC can get the table from VGA memory through this driver.
++
+ endmenu
+diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile
+index 2e547cc47e62..ae4ef10914be 100644
+--- a/drivers/soc/aspeed/Makefile
++++ b/drivers/soc/aspeed/Makefile
+@@ -5,3 +5,4 @@ obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o
+ obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.o
+ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
+ obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o
++obj-$(CONFIG_ASPEED_VGA_SHAREDMEM) += aspeed-vga-sharedmem.o
+\ No newline at end of file
+diff --git a/drivers/soc/aspeed/aspeed-vga-sharedmem.c b/drivers/soc/aspeed/aspeed-vga-sharedmem.c
+new file mode 100644
+index 000000000000..cd1f5431378c
+--- /dev/null
++++ b/drivers/soc/aspeed/aspeed-vga-sharedmem.c
+@@ -0,0 +1,163 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) 2018 Intel Corporation
++ * VGA Shared Memory driver for Aspeed AST2500
++ */
++
++#include <linux/kernel.h>
++#include <linux/miscdevice.h>
++#include <linux/mm.h>
++#include <linux/module.h>
++#include <linux/of_platform.h>
++
++#define SHAREDMEM_NAME "vgasharedmem"
++
++struct aspeed_vga_sharedmem {
++ struct miscdevice miscdev;
++ unsigned int addr;
++ unsigned int size;
++ bool mmap_enable;
++};
++
++static struct aspeed_vga_sharedmem *file_sharemem(struct file *file)
++{
++ return container_of(file->private_data,
++ struct aspeed_vga_sharedmem, miscdev);
++}
++
++static int vga_open(struct inode *inode, struct file *file)
++{
++ struct aspeed_vga_sharedmem *vga_sharedmem = file_sharemem(file);
++
++ if (!capable(CAP_SYS_ADMIN))
++ return -EPERM;
++
++ if (!vga_sharedmem->mmap_enable)
++ return -EPERM;
++
++ return 0;
++}
++
++static int vga_mmap(struct file *file, struct vm_area_struct *vma)
++{
++ struct aspeed_vga_sharedmem *vga_sharedmem = file_sharemem(file);
++
++ if (!capable(CAP_SYS_ADMIN))
++ return -EPERM;
++
++ vma->vm_flags = (vma->vm_flags & (~VM_WRITE));
++ remap_pfn_range(vma, vma->vm_start, vga_sharedmem->addr >> PAGE_SHIFT,
++ vga_sharedmem->size, vma->vm_page_prot);
++ return 0;
++}
++
++static ssize_t enable_mmap_show(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ struct aspeed_vga_sharedmem *vga_sharedmem = dev_get_drvdata(dev);
++
++ return sprintf(buf, "%u\n", vga_sharedmem->mmap_enable);
++}
++
++static ssize_t enable_mmap_store(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct aspeed_vga_sharedmem *vga_sharedmem =
++ dev_get_drvdata(dev);
++ bool val;
++
++ if (kstrtobool(buf, &val))
++ return -EINVAL;
++
++ vga_sharedmem->mmap_enable = val;
++
++ return count;
++}
++static DEVICE_ATTR_RW(enable_mmap);
++
++static struct attribute *sharedmem_attrs[] = {
++ &dev_attr_enable_mmap.attr,
++ NULL
++};
++
++static const struct attribute_group sharedmem_attr_group = {
++ .attrs = sharedmem_attrs,
++};
++
++static const struct attribute_group *sharedmem_attr_groups[] = {
++ &sharedmem_attr_group,
++ NULL
++};
++
++static const struct file_operations vga_sharedmem_fops = {
++ .owner = THIS_MODULE,
++ .open = vga_open,
++ .mmap = vga_mmap,
++};
++
++static struct miscdevice vga_sharedmem_miscdev = {
++ .minor = MISC_DYNAMIC_MINOR,
++ .name = SHAREDMEM_NAME,
++ .fops = &vga_sharedmem_fops,
++ .groups = sharedmem_attr_groups,
++};
++
++static int vga_sharedmem_probe(struct platform_device *pdev)
++{
++ struct aspeed_vga_sharedmem *vga_sharedmem;
++ struct device *dev = &pdev->dev;
++ struct resource *rc;
++
++ vga_sharedmem = devm_kzalloc(dev, sizeof(*vga_sharedmem), GFP_KERNEL);
++ if (!vga_sharedmem)
++ return -ENOMEM;
++
++ dev_set_drvdata(&pdev->dev, vga_sharedmem);
++
++ rc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (!rc) {
++ dev_err(dev, "Couldn't read size device-tree property\n");
++ return -ENXIO;
++ }
++
++ vga_sharedmem->addr = rc->start;
++ vga_sharedmem->size = resource_size(rc);
++ vga_sharedmem->mmap_enable = true;
++
++ vga_sharedmem->miscdev = vga_sharedmem_miscdev;
++
++ return misc_register(&vga_sharedmem->miscdev);
++}
++
++static int vga_sharedmem_remove(struct platform_device *pdev)
++{
++ struct aspeed_vga_sharedmem *vga_sharedmem =
++ dev_get_drvdata(&pdev->dev);
++
++ misc_deregister(&vga_sharedmem->miscdev);
++
++ return 0;
++}
++
++static const struct of_device_id vga_sharedmem_match[] = {
++ { .compatible = "aspeed,ast2500-vga-sharedmem", },
++ { }
++};
++MODULE_DEVICE_TABLE(of, vga_sharedmem_match);
++
++static struct platform_driver vga_sharedmem_driver = {
++ .driver = {
++ .name = "VGA-SHAREDMEM",
++ .of_match_table = vga_sharedmem_match,
++ },
++ .probe = vga_sharedmem_probe,
++ .remove = vga_sharedmem_remove,
++};
++
++module_platform_driver(vga_sharedmem_driver);
++
++MODULE_AUTHOR("Yang Cheng <cheng.c.yang@intel.com>");
++MODULE_DESCRIPTION("Shared VGA memory");
++MODULE_LICENSE("GPL v2");
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch
new file mode 100644
index 000000000..6d8ec4883
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch
@@ -0,0 +1,514 @@
+From e2df269568c6c0c8c8edbca73118c2dbdaea75bd Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Mon, 11 Feb 2019 17:02:35 -0800
+Subject: [PATCH] Add Aspeed PWM driver which uses FTTMR010 timer IP
+
+This commit adds Aspeed PWM driver which uses timer pulse output
+feature in Aspeed SoCs. The timer IP is derived from Faraday
+Technologies FTTMR010 IP but has some customized register
+structure changes only for Aspeed SoCs.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ arch/arm/boot/dts/aspeed-g5.dtsi | 2 +-
+ drivers/pwm/Kconfig | 9 +
+ drivers/pwm/Makefile | 1 +
+ drivers/pwm/pwm-fttmr010.c | 441 +++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 452 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/pwm/pwm-fttmr010.c
+
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index 6d2c4494ce04..653e03a0fa4c 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -342,7 +342,7 @@
+
+ timer: timer@1e782000 {
+ /* This timer is a Faraday FTTMR010 derivative */
+- compatible = "aspeed,ast2400-timer";
++ compatible = "aspeed,ast2500-timer";
+ reg = <0x1e782000 0x90>;
+ interrupts = <16 17 18 35 36 37 38 39>;
+ clocks = <&syscon ASPEED_CLK_APB>;
+diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
+index a7e57516959e..3388f837fcf9 100644
+--- a/drivers/pwm/Kconfig
++++ b/drivers/pwm/Kconfig
+@@ -171,6 +171,15 @@ config PWM_FSL_FTM
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-fsl-ftm.
+
++config PWM_FTTMR010
++ tristate "Faraday Technology FTTMR010 timer PWM support"
++ help
++ Generic PWM framework driver for Faraday Technology FTTMR010 Timer
++ PWM output
++
++ To compile this driver as a module, choose M here: the module
++ will be called pwm-fttmr010
++
+ config PWM_HIBVT
+ tristate "HiSilicon BVT PWM support"
+ depends on ARCH_HISI || COMPILE_TEST
+diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
+index 76b555b51887..19ecfd82d8c5 100644
+--- a/drivers/pwm/Makefile
++++ b/drivers/pwm/Makefile
+@@ -15,6 +15,7 @@ obj-$(CONFIG_PWM_CRC) += pwm-crc.o
+ obj-$(CONFIG_PWM_CROS_EC) += pwm-cros-ec.o
+ obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o
+ obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o
++obj-$(CONFIG_PWM_FTTMR010) += pwm-fttmr010.o
+ obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o
+ obj-$(CONFIG_PWM_IMG) += pwm-img.o
+ obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o
+diff --git a/drivers/pwm/pwm-fttmr010.c b/drivers/pwm/pwm-fttmr010.c
+new file mode 100644
+index 000000000000..4c929a25e27c
+--- /dev/null
++++ b/drivers/pwm/pwm-fttmr010.c
+@@ -0,0 +1,441 @@
++// SPDX-License-Identifier: GPL-2.0
++// Copyright (c) 2019 Intel Corporation
++
++#include <linux/clk.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pwm.h>
++
++#define TIMER_CR 0x30
++
++#define TIMER5_ASPEED_COUNT 0x50
++#define TIMER5_ASPEED_LOAD 0x54
++#define TIMER5_ASPEED_MATCH1 0x58
++#define TIMER5_ASPEED_MATCH2 0x5c
++#define TIMER6_ASPEED_COUNT 0x60
++#define TIMER6_ASPEED_LOAD 0x64
++#define TIMER6_ASPEED_MATCH1 0x68
++#define TIMER6_ASPEED_MATCH2 0x6c
++#define TIMER7_ASPEED_COUNT 0x70
++#define TIMER7_ASPEED_LOAD 0x74
++#define TIMER7_ASPEED_MATCH1 0x78
++#define TIMER7_ASPEED_MATCH2 0x7c
++#define TIMER8_ASPEED_COUNT 0x80
++#define TIMER8_ASPEED_LOAD 0x84
++#define TIMER8_ASPEED_MATCH1 0x88
++#define TIMER8_ASPEED_MATCH2 0x8c
++
++#define TIMER_5_CR_ASPEED_ENABLE BIT(16)
++#define TIMER_5_CR_ASPEED_CLOCK BIT(17)
++#define TIMER_5_CR_ASPEED_INT BIT(18)
++#define TIMER_5_CR_ASPEED_PULSE_OUT BIT(19)
++#define TIMER_6_CR_ASPEED_ENABLE BIT(20)
++#define TIMER_6_CR_ASPEED_CLOCK BIT(21)
++#define TIMER_6_CR_ASPEED_INT BIT(22)
++#define TIMER_6_CR_ASPEED_PULSE_OUT BIT(23)
++#define TIMER_7_CR_ASPEED_ENABLE BIT(24)
++#define TIMER_7_CR_ASPEED_CLOCK BIT(25)
++#define TIMER_7_CR_ASPEED_INT BIT(26)
++#define TIMER_7_CR_ASPEED_PULSE_OUT BIT(27)
++#define TIMER_8_CR_ASPEED_ENABLE BIT(28)
++#define TIMER_8_CR_ASPEED_CLOCK BIT(29)
++#define TIMER_8_CR_ASPEED_INT BIT(30)
++#define TIMER_8_CR_ASPEED_PULSE_OUT BIT(31)
++
++/**
++ * struct pwm_fttmr010_variant - variant data depends on SoC
++ * @bits: timer counter resolution
++ * @chan_min: lowest timer channel which has pwm pulse output
++ * @chan_max: highest timer channel which has pwm pulse output
++ * @output_mask: pwm pulse output mask which is defined in device tree
++ */
++struct pwm_fttmr010_variant {
++ u8 bits;
++ u8 chan_min;
++ u8 chan_max;
++ u8 output_mask;
++};
++
++/**
++ * struct pwm_fttmr010_chan - private data of FTTMR010 PWM channel
++ * @period_ns: current period in nanoseconds programmed to the hardware
++ * @duty_ns: current duty time in nanoseconds programmed to the hardware
++ */
++struct pwm_fttmr010_chan {
++ u32 period_ns;
++ u32 duty_ns;
++};
++
++/**
++ * struct pwm_fttmr010 - private data of FTTMR010 PWM
++ * @chip: generic PWM chip
++ * @variant: local copy of hardware variant data
++ * @disabled_mask: disabled status for all channels - one bit per channel
++ * @base: base address of mapped PWM registers
++ * @clk: clock used to drive the timers
++ */
++struct pwm_fttmr010 {
++ struct pwm_chip chip;
++ struct pwm_fttmr010_variant variant;
++ u8 disabled_mask;
++ void __iomem *base;
++ struct clk *clk;
++ u32 clk_tick_ns;
++};
++
++static inline
++struct pwm_fttmr010 *to_pwm_fttmr010(struct pwm_chip *chip)
++{
++ return container_of(chip, struct pwm_fttmr010, chip);
++}
++
++static int pwm_fttmr010_request(struct pwm_chip *chip, struct pwm_device *pwm)
++{
++ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip);
++ struct pwm_fttmr010_chan *chan;
++
++ if (!(priv->variant.output_mask & BIT(pwm->hwpwm))) {
++ dev_warn(chip->dev,
++ "tried to request PWM channel %d without output\n",
++ pwm->hwpwm);
++ return -EINVAL;
++ }
++
++ chan = devm_kzalloc(chip->dev, sizeof(*chan), GFP_KERNEL);
++ if (!chan)
++ return -ENOMEM;
++
++ pwm_set_chip_data(pwm, chan);
++
++ return 0;
++}
++
++static void pwm_fttmr010_free(struct pwm_chip *chip, struct pwm_device *pwm)
++{
++ devm_kfree(chip->dev, pwm_get_chip_data(pwm));
++ pwm_set_chip_data(pwm, NULL);
++}
++
++static int pwm_fttmr010_enable(struct pwm_chip *chip, struct pwm_device *pwm)
++{
++ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip);
++ u32 cr;
++
++ cr = readl(priv->base + TIMER_CR);
++
++ switch (pwm->hwpwm) {
++ case 5:
++ cr |= (TIMER_5_CR_ASPEED_ENABLE | TIMER_5_CR_ASPEED_PULSE_OUT);
++ break;
++ case 6:
++ cr |= (TIMER_6_CR_ASPEED_ENABLE | TIMER_6_CR_ASPEED_PULSE_OUT);
++ break;
++ case 7:
++ cr |= (TIMER_7_CR_ASPEED_ENABLE | TIMER_7_CR_ASPEED_PULSE_OUT);
++ break;
++ case 8:
++ cr |= (TIMER_8_CR_ASPEED_ENABLE | TIMER_8_CR_ASPEED_PULSE_OUT);
++ break;
++ default:
++ return -ERANGE;
++ }
++
++ writel(cr, priv->base + TIMER_CR);
++ priv->disabled_mask &= ~BIT(pwm->hwpwm);
++
++ return 0;
++}
++
++static void pwm_fttmr010_disable(struct pwm_chip *chip, struct pwm_device *pwm)
++{
++ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip);
++ u32 cr;
++
++ cr = readl(priv->base + TIMER_CR);
++
++ switch (pwm->hwpwm) {
++ case 5:
++ cr &= ~(TIMER_5_CR_ASPEED_ENABLE | TIMER_5_CR_ASPEED_PULSE_OUT);
++ break;
++ case 6:
++ cr &= ~(TIMER_6_CR_ASPEED_ENABLE | TIMER_6_CR_ASPEED_PULSE_OUT);
++ break;
++ case 7:
++ cr &= ~(TIMER_7_CR_ASPEED_ENABLE | TIMER_7_CR_ASPEED_PULSE_OUT);
++ break;
++ case 8:
++ cr &= ~(TIMER_8_CR_ASPEED_ENABLE | TIMER_8_CR_ASPEED_PULSE_OUT);
++ break;
++ default:
++ return;
++ }
++
++ writel(cr, priv->base + TIMER_CR);
++ priv->disabled_mask |= BIT(pwm->hwpwm);
++}
++
++static int pwm_fttmr010_config(struct pwm_chip *chip, struct pwm_device *pwm,
++ int duty_ns, int period_ns)
++{
++ u32 tload, tmatch, creg_offset, lreg_offset, mreg_offset;
++ struct pwm_fttmr010_chan *chan = pwm_get_chip_data(pwm);
++ struct pwm_fttmr010 *priv = to_pwm_fttmr010(chip);
++
++ /*
++ * We currently avoid using 64bit arithmetic by using the
++ * fact that anything faster than 1Hz is easily representable
++ * by 32bits.
++ */
++ if (period_ns > NSEC_PER_SEC)
++ return -ERANGE;
++
++ /* No need to update */
++ if (chan->period_ns == period_ns || chan->duty_ns == duty_ns)
++ return 0;
++
++ tload = period_ns / priv->clk_tick_ns;
++
++ /* Period is too short */
++ if (tload <= 1)
++ return -ERANGE;
++
++ tmatch = duty_ns / priv->clk_tick_ns;
++
++ /* 0% duty is not available */
++ if (!tmatch)
++ ++tmatch;
++
++ tmatch = tload - tmatch;
++
++ /* Decrement to get tick numbers, instead of tick counts */
++ --tload;
++ --tmatch;
++
++ if (tload == 0 || tmatch == 0)
++ return -ERANGE;
++
++ dev_dbg(priv->chip.dev, "clk_tick_ns:%u, tload:%u, tmatch:%u\n",
++ priv->clk_tick_ns, tload, tmatch);
++
++ switch (pwm->hwpwm) {
++ case 5:
++ creg_offset = TIMER5_ASPEED_COUNT;
++ lreg_offset = TIMER5_ASPEED_LOAD;
++ mreg_offset = TIMER5_ASPEED_MATCH1;
++ break;
++ case 6:
++ creg_offset = TIMER6_ASPEED_COUNT;
++ lreg_offset = TIMER6_ASPEED_LOAD;
++ mreg_offset = TIMER6_ASPEED_MATCH1;
++ break;
++ case 7:
++ creg_offset = TIMER7_ASPEED_COUNT;
++ lreg_offset = TIMER7_ASPEED_LOAD;
++ mreg_offset = TIMER7_ASPEED_MATCH1;
++ break;
++ case 8:
++ creg_offset = TIMER8_ASPEED_COUNT;
++ lreg_offset = TIMER8_ASPEED_LOAD;
++ mreg_offset = TIMER8_ASPEED_MATCH1;
++ break;
++ default:
++ return -ERANGE;
++ }
++
++ writel(tload, priv->base + creg_offset);
++ writel(tload, priv->base + lreg_offset);
++ writel(tmatch, priv->base + mreg_offset);
++
++ chan->period_ns = period_ns;
++ chan->duty_ns = duty_ns;
++
++ return 0;
++}
++
++static const struct pwm_ops pwm_fttmr010_ops = {
++ .request = pwm_fttmr010_request,
++ .free = pwm_fttmr010_free,
++ .enable = pwm_fttmr010_enable,
++ .disable = pwm_fttmr010_disable,
++ .config = pwm_fttmr010_config,
++ .owner = THIS_MODULE,
++};
++
++#ifdef CONFIG_OF
++static const struct pwm_fttmr010_variant aspeed_variant = {
++ .bits = 32,
++ .chan_min = 5,
++ .chan_max = 8,
++};
++
++static const struct of_device_id pwm_fttmr010_matches[] = {
++ { .compatible = "aspeed,ast2400-timer", .data = &aspeed_variant },
++ { .compatible = "aspeed,ast2500-timer", .data = &aspeed_variant },
++ { },
++};
++MODULE_DEVICE_TABLE(of, pwm_fttmr010_matches);
++
++static int pwm_fttmr010_parse_dt(struct pwm_fttmr010 *priv)
++{
++ struct device_node *np = priv->chip.dev->of_node;
++ const struct of_device_id *match;
++ struct property *prop;
++ const __be32 *cur;
++ u32 val;
++
++ match = of_match_node(pwm_fttmr010_matches, np);
++ if (!match)
++ return -ENODEV;
++
++ memcpy(&priv->variant, match->data, sizeof(priv->variant));
++
++ of_property_for_each_u32(np, "fttmr010,pwm-outputs", prop, cur, val) {
++ if (val < priv->variant.chan_min ||
++ val > priv->variant.chan_max) {
++ dev_err(priv->chip.dev,
++ "invalid channel index in fttmr010,pwm-outputs property\n");
++ continue;
++ }
++ priv->variant.output_mask |= BIT(val);
++ }
++
++ return 0;
++}
++#else
++static int pwm_fttmr010_parse_dt(struct pwm_fttmr010 *priv)
++{
++ return -ENODEV;
++}
++#endif
++
++static int pwm_fttmr010_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct pwm_fttmr010 *priv;
++ struct resource *res;
++ ulong clk_rate;
++ int ret;
++
++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ priv->chip.dev = &pdev->dev;
++ priv->chip.ops = &pwm_fttmr010_ops;
++ priv->chip.base = -1;
++
++ if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
++ ret = pwm_fttmr010_parse_dt(priv);
++ if (ret)
++ return ret;
++
++ priv->chip.of_xlate = of_pwm_xlate_with_flags;
++ priv->chip.of_pwm_n_cells = 3;
++ } else {
++ if (!pdev->dev.platform_data) {
++ dev_err(&pdev->dev, "no platform data specified\n");
++ return -EINVAL;
++ }
++
++ memcpy(&priv->variant, pdev->dev.platform_data,
++ sizeof(priv->variant));
++ }
++
++ priv->chip.npwm = priv->variant.chan_max + 1;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ priv->base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(priv->base))
++ return PTR_ERR(priv->base);
++
++ priv->clk = devm_clk_get(&pdev->dev, "PCLK");
++ if (IS_ERR(priv->clk)) {
++ dev_err(dev, "failed to get timer base clk\n");
++ return PTR_ERR(priv->clk);
++ }
++
++ ret = clk_prepare_enable(priv->clk);
++ if (ret < 0) {
++ dev_err(dev, "failed to enable base clock\n");
++ return ret;
++ }
++
++ clk_rate = clk_get_rate(priv->clk);
++ priv->clk_tick_ns = NSEC_PER_SEC / clk_rate;
++
++ platform_set_drvdata(pdev, priv);
++
++ ret = pwmchip_add(&priv->chip);
++ if (ret < 0) {
++ dev_err(dev, "failed to register PWM chip\n");
++ clk_disable_unprepare(priv->clk);
++ return ret;
++ }
++
++ dev_dbg(dev, "clk at %lu\n", clk_rate);
++
++ return 0;
++}
++
++static int pwm_fttmr010_remove(struct platform_device *pdev)
++{
++ struct pwm_fttmr010 *priv = platform_get_drvdata(pdev);
++ int ret;
++
++ ret = pwmchip_remove(&priv->chip);
++ if (ret < 0)
++ return ret;
++
++ clk_disable_unprepare(priv->clk);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM_SLEEP
++static int pwm_fttmr010_resume(struct device *dev)
++{
++ struct pwm_fttmr010 *priv = dev_get_drvdata(dev);
++ struct pwm_chip *chip = &priv->chip;
++ unsigned int i;
++
++ for (i = chip->variant.chan_min; i < chip->variant.chan_max; i++) {
++ struct pwm_device *pwm = &chip->pwms[i];
++ struct pwm_fttmr010_chan *chan = pwm_get_chip_data(pwm);
++
++ if (!chan)
++ continue;
++
++ if (chan->period_ns) {
++ pwm_fttmr010_config(chip, pwm, chan->duty_ns,
++ chan->period_ns);
++ }
++
++ if (priv->disabled_mask & BIT(i))
++ pwm_fttmr010_disable(chip, pwm);
++ else
++ pwm_fttmr010_enable(chip, pwm);
++ }
++
++ return 0;
++}
++#endif
++
++static SIMPLE_DEV_PM_OPS(pwm_fttmr010_pm_ops, NULL, pwm_fttmr010_resume);
++
++static struct platform_driver pwm_fttmr010_driver = {
++ .driver = {
++ .name = "fttmr010-timer-pwm",
++ .pm = &pwm_fttmr010_pm_ops,
++ .of_match_table = of_match_ptr(pwm_fttmr010_matches),
++ },
++ .probe = pwm_fttmr010_probe,
++ .remove = pwm_fttmr010_remove,
++};
++module_platform_driver(pwm_fttmr010_driver);
++
++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
++MODULE_DESCRIPTION("FTTMR010 PWM Driver for timer pulse outputs");
++MODULE_LICENSE("GPL v2");
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch
new file mode 100644
index 000000000..ce77494f4
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch
@@ -0,0 +1,488 @@
+From be693c1c21979c067623434aa653f85a83c8eac7 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Fri, 15 Feb 2019 16:05:09 -0800
+Subject: [PATCH] i2c: Add mux hold/unhold msg types
+
+This commit adds mux hold/unhold message types to support extended
+mux control for IPMB and MCTP devices. A hold or an unhold message
+can be added at the end of I2C message stream wrapped by
+repeated-start, also can be used as a single message independantly.
+
+This mux hold/unhold message will be delivered throughout all mux
+levels in the path. Means that if it goes to multi-level mux path,
+all muxes will be held/unheld by this message.
+
+1. Hold message
+ struct i2c_msg msg;
+ uint16_t timeout = 5000; // timeout in ms. 5 secs in this example.
+
+ msg.addr = 0x0; // any value can be used. addr will be ignored in this packet.
+ msg.flags = I2C_M_HOLD; // set this flag to indicate it's a hold message.
+ msg.len = sizeof(uint16_t); // timeout value will be delivered using two bytes buffer.
+ msg.buf = (uint8_t *)&timeout; // set timeout value.
+
+2. Unhold message
+ struct i2c_msg msg;
+ uint16_t timeout = 0; // set 0 for an unhold message.
+
+ msg.addr = 0x0; // any value can be used. addr will be ignored in this packet.
+ msg.flags = I2C_M_HOLD; // set this flag to indicate it's an unhold message.
+ msg.len = sizeof(uint16_t); // timeout value will be delivered using two bytes buffer.
+ msg.buf = (uint8_t *)&timeout; // set timeout value.
+
+ This unhold message can be delivered to a mux adapter even when
+ a bus is locked so that any holding state can be unheld
+ immediately by invoking this unhold message.
+
+This patch would not be welcomed from upstream so it should be kept
+in downstream only.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/i2c/i2c-core-base.c | 70 +++++++++++++++++++++++++--
+ drivers/i2c/i2c-core-smbus.c | 22 +++++++--
+ drivers/i2c/i2c-mux.c | 109 +++++++++++++++++++++++++++++++++++++++----
+ include/linux/i2c-mux.h | 3 ++
+ include/linux/i2c.h | 25 ++++++++++
+ include/uapi/linux/i2c.h | 1 +
+ 6 files changed, 214 insertions(+), 16 deletions(-)
+
+diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
+index 9c440fa6a3dd..53ff27cae5d3 100644
+--- a/drivers/i2c/i2c-core-base.c
++++ b/drivers/i2c/i2c-core-base.c
+@@ -1299,6 +1299,25 @@ int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr)
+ }
+ EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify);
+
++static void i2c_adapter_hold(struct i2c_adapter *adapter, unsigned long timeout)
++{
++ mutex_lock(&adapter->hold_lock);
++ mod_timer(&adapter->hold_timer, jiffies + timeout);
++}
++
++static void i2c_adapter_unhold(struct i2c_adapter *adapter)
++{
++ del_timer_sync(&adapter->hold_timer);
++ mutex_unlock(&adapter->hold_lock);
++}
++
++static void i2c_adapter_hold_timer_callback(struct timer_list *t)
++{
++ struct i2c_adapter *adapter = from_timer(adapter, t, hold_timer);
++
++ i2c_adapter_unhold(adapter);
++}
++
+ static int i2c_register_adapter(struct i2c_adapter *adap)
+ {
+ int res = -EINVAL;
+@@ -1381,6 +1400,9 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
+ bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
+ mutex_unlock(&core_lock);
+
++ mutex_init(&adap->hold_lock);
++ timer_setup(&adap->hold_timer, i2c_adapter_hold_timer_callback, 0);
++
+ return 0;
+
+ out_reg:
+@@ -1601,6 +1623,8 @@ void i2c_del_adapter(struct i2c_adapter *adap)
+ idr_remove(&i2c_adapter_idr, adap->nr);
+ mutex_unlock(&core_lock);
+
++ i2c_adapter_unhold(adap);
++
+ /* Clear the device structure in case this adapter is ever going to be
+ added again */
+ memset(&adap->dev, 0, sizeof(adap->dev));
+@@ -1950,7 +1974,9 @@ static int i2c_check_for_quirks(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ */
+ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+ {
++ enum i2c_hold_msg_type hold_msg = I2C_HOLD_MSG_NONE;
+ unsigned long orig_jiffies;
++ unsigned long timeout;
+ int ret, try;
+
+ if (WARN_ON(!msgs || num < 1))
+@@ -1963,6 +1989,25 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+ if (adap->quirks && i2c_check_for_quirks(adap, msgs, num))
+ return -EOPNOTSUPP;
+
++ /* Do not deliver a mux hold msg to root bus adapter */
++ if (!i2c_parent_is_i2c_adapter(adap)) {
++ hold_msg = i2c_check_hold_msg(msgs[num - 1].flags,
++ msgs[num - 1].len,
++ (u16 *)msgs[num - 1].buf);
++ if (hold_msg == I2C_HOLD_MSG_SET) {
++ timeout = msecs_to_jiffies(*(u16 *)msgs[num - 1].buf);
++ i2c_adapter_hold(adap, timeout);
++
++ if (--num == 0)
++ return 0;
++ } else if (hold_msg == I2C_HOLD_MSG_RESET) {
++ i2c_adapter_unhold(adap);
++ return 0;
++ } else if (hold_msg == I2C_HOLD_MSG_NONE) {
++ mutex_lock(&adap->hold_lock);
++ }
++ }
++
+ /*
+ * i2c_trace_msg_key gets enabled when tracepoint i2c_transfer gets
+ * enabled. This is an efficient way of keeping the for-loop from
+@@ -1999,6 +2044,9 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+ trace_i2c_result(adap, num, ret);
+ }
+
++ if (!i2c_parent_is_i2c_adapter(adap) && hold_msg == I2C_HOLD_MSG_NONE)
++ mutex_unlock(&adap->hold_lock);
++
+ return ret;
+ }
+ EXPORT_SYMBOL(__i2c_transfer);
+@@ -2017,6 +2065,7 @@ EXPORT_SYMBOL(__i2c_transfer);
+ */
+ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+ {
++ bool do_bus_lock = true;
+ int ret;
+
+ if (!adap->algo->master_xfer) {
+@@ -2040,12 +2089,25 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+ * one (discarding status on the second message) or errno
+ * (discarding status on the first one).
+ */
+- ret = __i2c_lock_bus_helper(adap);
+- if (ret)
+- return ret;
++ /*
++ * Do not lock a bus for delivering an unhold msg to a mux
++ * adpater. This is just for a single length unhold msg case.
++ */
++ if (num == 1 && i2c_parent_is_i2c_adapter(adap) &&
++ i2c_check_hold_msg(msgs[0].flags, msgs[0].len,
++ (u16 *)msgs[0].buf) ==
++ I2C_HOLD_MSG_RESET)
++ do_bus_lock = false;
++
++ if (do_bus_lock) {
++ ret = __i2c_lock_bus_helper(adap);
++ if (ret)
++ return ret;
++ }
+
+ ret = __i2c_transfer(adap, msgs, num);
+- i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
++ if (do_bus_lock)
++ i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
+
+ return ret;
+ }
+diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c
+index 3ac426a8ab5a..f7bf95101e34 100644
+--- a/drivers/i2c/i2c-core-smbus.c
++++ b/drivers/i2c/i2c-core-smbus.c
+@@ -526,15 +526,29 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int protocol, union i2c_smbus_data *data)
+ {
++ bool do_bus_lock = true;
+ s32 res;
+
+- res = __i2c_lock_bus_helper(adapter);
+- if (res)
+- return res;
++ /*
++ * Do not lock a bus for delivering an unhold msg to a mux adpater.
++ * This is just for a single length unhold msg case.
++ */
++ if (i2c_parent_is_i2c_adapter(adapter) &&
++ i2c_check_hold_msg(flags,
++ protocol == I2C_SMBUS_WORD_DATA ? 2 : 0,
++ &data->word) == I2C_HOLD_MSG_RESET)
++ do_bus_lock = false;
++
++ if (do_bus_lock) {
++ res = __i2c_lock_bus_helper(adapter);
++ if (res)
++ return res;
++ }
+
+ res = __i2c_smbus_xfer(adapter, addr, flags, read_write,
+ command, protocol, data);
+- i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT);
++ if (do_bus_lock)
++ i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT);
+
+ return res;
+ }
+diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
+index 774507b54b57..c6e433238b22 100644
+--- a/drivers/i2c/i2c-mux.c
++++ b/drivers/i2c/i2c-mux.c
+@@ -27,6 +27,7 @@
+ #include <linux/of.h>
+ #include <linux/slab.h>
+ #include <linux/sysfs.h>
++#include <linux/timer.h>
+
+ /* multiplexer per channel data */
+ struct i2c_mux_priv {
+@@ -36,21 +37,57 @@ struct i2c_mux_priv {
+ u32 chan_id;
+ };
+
++static void i2c_mux_hold(struct i2c_mux_core *muxc, unsigned long timeout)
++{
++ mutex_lock(&muxc->hold_lock);
++ mod_timer(&muxc->hold_timer, jiffies + timeout);
++}
++
++static void i2c_mux_unhold(struct i2c_mux_core *muxc)
++{
++ del_timer_sync(&muxc->hold_timer);
++ mutex_unlock(&muxc->hold_lock);
++}
++
++static void i2c_mux_hold_timer_callback(struct timer_list *t)
++{
++ struct i2c_mux_core *muxc = from_timer(muxc, t, hold_timer);
++
++ i2c_mux_unhold(muxc);
++}
++
+ static int __i2c_mux_master_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msgs[], int num)
+ {
+ struct i2c_mux_priv *priv = adap->algo_data;
+ struct i2c_mux_core *muxc = priv->muxc;
+ struct i2c_adapter *parent = muxc->parent;
++ enum i2c_hold_msg_type hold_msg;
++ unsigned long timeout;
+ int ret;
+
+ /* Switch to the right mux port and perform the transfer. */
+
++ hold_msg = i2c_check_hold_msg(msgs[num - 1].flags,
++ msgs[num - 1].len,
++ (u16 *)msgs[num - 1].buf);
++ if (hold_msg == I2C_HOLD_MSG_SET) {
++ timeout = msecs_to_jiffies(*(u16 *)msgs[num - 1].buf);
++ i2c_mux_hold(muxc, timeout);
++ } else if (hold_msg == I2C_HOLD_MSG_NONE) {
++ mutex_lock(&muxc->hold_lock);
++ }
+ ret = muxc->select(muxc, priv->chan_id);
+ if (ret >= 0)
+ ret = __i2c_transfer(parent, msgs, num);
+- if (muxc->deselect)
+- muxc->deselect(muxc, priv->chan_id);
++ if (hold_msg != I2C_HOLD_MSG_SET) {
++ if (muxc->deselect)
++ muxc->deselect(muxc, priv->chan_id);
++ if (hold_msg == I2C_HOLD_MSG_RESET)
++ i2c_mux_unhold(muxc);
++ else
++ mutex_unlock(&muxc->hold_lock);
++ }
+
+ return ret;
+ }
+@@ -61,15 +98,32 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap,
+ struct i2c_mux_priv *priv = adap->algo_data;
+ struct i2c_mux_core *muxc = priv->muxc;
+ struct i2c_adapter *parent = muxc->parent;
++ enum i2c_hold_msg_type hold_msg;
++ unsigned long timeout;
+ int ret;
+
+ /* Switch to the right mux port and perform the transfer. */
+
++ hold_msg = i2c_check_hold_msg(msgs[num - 1].flags,
++ msgs[num - 1].len,
++ (u16 *)msgs[num - 1].buf);
++ if (hold_msg == I2C_HOLD_MSG_SET) {
++ timeout = msecs_to_jiffies(*(u16 *)msgs[num - 1].buf);
++ i2c_mux_hold(muxc, timeout);
++ } else if (hold_msg == I2C_HOLD_MSG_NONE) {
++ mutex_lock(&muxc->hold_lock);
++ }
+ ret = muxc->select(muxc, priv->chan_id);
+ if (ret >= 0)
+ ret = i2c_transfer(parent, msgs, num);
+- if (muxc->deselect)
+- muxc->deselect(muxc, priv->chan_id);
++ if (hold_msg != I2C_HOLD_MSG_SET) {
++ if (muxc->deselect)
++ muxc->deselect(muxc, priv->chan_id);
++ if (hold_msg == I2C_HOLD_MSG_RESET)
++ i2c_mux_unhold(muxc);
++ else
++ mutex_unlock(&muxc->hold_lock);
++ }
+
+ return ret;
+ }
+@@ -82,16 +136,33 @@ static int __i2c_mux_smbus_xfer(struct i2c_adapter *adap,
+ struct i2c_mux_priv *priv = adap->algo_data;
+ struct i2c_mux_core *muxc = priv->muxc;
+ struct i2c_adapter *parent = muxc->parent;
++ enum i2c_hold_msg_type hold_msg;
++ unsigned long timeout;
+ int ret;
+
+ /* Select the right mux port and perform the transfer. */
+
++ hold_msg = i2c_check_hold_msg(flags,
++ size == I2C_SMBUS_WORD_DATA ? 2 : 0,
++ &data->word);
++ if (hold_msg == I2C_HOLD_MSG_SET) {
++ timeout = msecs_to_jiffies(data->word);
++ i2c_mux_hold(muxc, timeout);
++ } else if (hold_msg == I2C_HOLD_MSG_NONE) {
++ mutex_lock(&muxc->hold_lock);
++ }
+ ret = muxc->select(muxc, priv->chan_id);
+ if (ret >= 0)
+ ret = __i2c_smbus_xfer(parent, addr, flags,
+ read_write, command, size, data);
+- if (muxc->deselect)
+- muxc->deselect(muxc, priv->chan_id);
++ if (hold_msg != I2C_HOLD_MSG_SET) {
++ if (muxc->deselect)
++ muxc->deselect(muxc, priv->chan_id);
++ if (hold_msg == I2C_HOLD_MSG_RESET)
++ i2c_mux_unhold(muxc);
++ else
++ mutex_unlock(&muxc->hold_lock);
++ }
+
+ return ret;
+ }
+@@ -104,16 +175,33 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap,
+ struct i2c_mux_priv *priv = adap->algo_data;
+ struct i2c_mux_core *muxc = priv->muxc;
+ struct i2c_adapter *parent = muxc->parent;
++ enum i2c_hold_msg_type hold_msg;
++ unsigned long timeout;
+ int ret;
+
+ /* Select the right mux port and perform the transfer. */
+
++ hold_msg = i2c_check_hold_msg(flags,
++ size == I2C_SMBUS_WORD_DATA ? 2 : 0,
++ &data->word);
++ if (hold_msg == I2C_HOLD_MSG_SET) {
++ timeout = msecs_to_jiffies(data->word);
++ i2c_mux_hold(muxc, timeout);
++ } else if (hold_msg == I2C_HOLD_MSG_NONE) {
++ mutex_lock(&muxc->hold_lock);
++ }
+ ret = muxc->select(muxc, priv->chan_id);
+ if (ret >= 0)
+ ret = i2c_smbus_xfer(parent, addr, flags,
+ read_write, command, size, data);
+- if (muxc->deselect)
+- muxc->deselect(muxc, priv->chan_id);
++ if (hold_msg != I2C_HOLD_MSG_SET) {
++ if (muxc->deselect)
++ muxc->deselect(muxc, priv->chan_id);
++ if (hold_msg == I2C_HOLD_MSG_RESET)
++ i2c_mux_unhold(muxc);
++ else
++ mutex_unlock(&muxc->hold_lock);
++ }
+
+ return ret;
+ }
+@@ -263,6 +351,9 @@ struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent,
+ muxc->deselect = deselect;
+ muxc->max_adapters = max_adapters;
+
++ mutex_init(&muxc->hold_lock);
++ timer_setup(&muxc->hold_timer, i2c_mux_hold_timer_callback, 0);
++
+ return muxc;
+ }
+ EXPORT_SYMBOL_GPL(i2c_mux_alloc);
+@@ -441,6 +532,8 @@ void i2c_mux_del_adapters(struct i2c_mux_core *muxc)
+ {
+ char symlink_name[20];
+
++ i2c_mux_unhold(muxc);
++
+ while (muxc->num_adapters) {
+ struct i2c_adapter *adap = muxc->adapter[--muxc->num_adapters];
+ struct i2c_mux_priv *priv = adap->algo_data;
+diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h
+index c5a977320f82..47f8763d6ed2 100644
+--- a/include/linux/i2c-mux.h
++++ b/include/linux/i2c-mux.h
+@@ -27,6 +27,9 @@ struct i2c_mux_core {
+ int (*select)(struct i2c_mux_core *, u32 chan_id);
+ int (*deselect)(struct i2c_mux_core *, u32 chan_id);
+
++ struct mutex hold_lock; /* mutex for channel holding */
++ struct timer_list hold_timer;
++
+ int num_adapters;
+ int max_adapters;
+ struct i2c_adapter *adapter[0];
+diff --git a/include/linux/i2c.h b/include/linux/i2c.h
+index fa5552c2307b..92c795ce9081 100644
+--- a/include/linux/i2c.h
++++ b/include/linux/i2c.h
+@@ -711,6 +711,13 @@ struct i2c_adapter {
+ const struct i2c_adapter_quirks *quirks;
+
+ struct irq_domain *host_notify_domain;
++
++ /*
++ * These will be used by root adpaters only. For muxes, each mux core
++ * has these individually.
++ */
++ struct mutex hold_lock; /* mutex for bus holding */
++ struct timer_list hold_timer;
+ };
+ #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
+
+@@ -1005,4 +1012,22 @@ static inline struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle ha
+ }
+ #endif /* CONFIG_ACPI */
+
++enum i2c_hold_msg_type {
++ I2C_HOLD_MSG_NONE,
++ I2C_HOLD_MSG_SET,
++ I2C_HOLD_MSG_RESET
++};
++
++static inline enum i2c_hold_msg_type i2c_check_hold_msg(u16 flags, u16 len, u16 *buf)
++{
++ if (flags & I2C_M_HOLD && len == sizeof(u16)) {
++ if (*buf)
++ return I2C_HOLD_MSG_SET;
++
++ return I2C_HOLD_MSG_RESET;
++ }
++
++ return I2C_HOLD_MSG_NONE;
++}
++
+ #endif /* _LINUX_I2C_H */
+diff --git a/include/uapi/linux/i2c.h b/include/uapi/linux/i2c.h
+index f71a1751cacf..a1db9b17ed36 100644
+--- a/include/uapi/linux/i2c.h
++++ b/include/uapi/linux/i2c.h
+@@ -72,6 +72,7 @@ struct i2c_msg {
+ #define I2C_M_RD 0x0001 /* read data, from slave to master */
+ /* I2C_M_RD is guaranteed to be 0x0001! */
+ #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
++#define I2C_M_HOLD 0x0100 /* for holding a mux path */
+ #define I2C_M_DMA_SAFE 0x0200 /* the buffer of this message is DMA safe */
+ /* makes only sense in kernelspace */
+ /* userspace buffers are copied anyway */
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0042-Add-bus-timeout-ms-and-retries-device-tree-propertie.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0042-Add-bus-timeout-ms-and-retries-device-tree-propertie.patch
new file mode 100644
index 000000000..0a9bccf23
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0042-Add-bus-timeout-ms-and-retries-device-tree-propertie.patch
@@ -0,0 +1,105 @@
+From 04af6987c904225fdd4657e7b87874edd11c4e0b Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Thu, 7 Mar 2019 15:17:40 -0800
+Subject: [PATCH] Add bus-timeout-ms and #retries device tree properties
+
+BMC uses I2C bus 7 as a PMBus channel to communicate with PSUs,
+also ME uses this bus as SMLink to control PSUs so this bus is
+managed by multi-masters. In this use case, some arbitration errors
+are expected so we need to add retry logic. And PMBus subsystem
+uses I2C bus in kernel internally so retry logic should be
+supported in kernel level.
+
+To support the use case, this commit adds 'bus-timeout-ms' and
+'#retries' device tree properties to set the bus specific
+parameters at kernel boot time without using any additional ioctls
+from user space.
+
+This patch would not be accepted by I2C maintainer in linux
+upstream because he doesn't like adding these legacy properties
+into device tree, so keep it only in downstream.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ Documentation/devicetree/bindings/i2c/i2c-aspeed.txt | 3 +++
+ Documentation/devicetree/bindings/i2c/i2c.txt | 6 ++++++
+ drivers/i2c/busses/i2c-aspeed.c | 1 -
+ drivers/i2c/i2c-core-base.c | 12 ++++++++++--
+ 4 files changed, 19 insertions(+), 3 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
+index 8fbd8633a387..7da7e813b2b0 100644
+--- a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
++++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
+@@ -16,6 +16,9 @@ Optional Properties:
+ - bus-frequency : frequency of the bus clock in Hz defaults to 100 kHz when not
+ specified
+ - multi-master : states that there is another master active on this bus.
++- bus-timeout-ms: bus timeout in milliseconds defaults to 1 second when not
++ specified.
++- #retries : Number of retries for master transfer.
+
+ Example:
+
+diff --git a/Documentation/devicetree/bindings/i2c/i2c.txt b/Documentation/devicetree/bindings/i2c/i2c.txt
+index 44efafdfd7f5..e382931cf3d6 100644
+--- a/Documentation/devicetree/bindings/i2c/i2c.txt
++++ b/Documentation/devicetree/bindings/i2c/i2c.txt
+@@ -80,6 +80,12 @@ wants to support one of the below features, it should adapt the bindings below.
+ Names of map programmable addresses.
+ It can contain any map needing another address than default one.
+
++- bus-timeout-ms
++ Bus timeout in milliseconds.
++
++- #retries
++ Number of retries for master transfer.
++
+ Binding may contain optional "interrupts" property, describing interrupts
+ used by the device. I2C core will assign "irq" interrupt (or the very first
+ interrupt if not using interrupt names) as primary interrupt for the slave.
+diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
+index e1719b1f2020..58bdbe472721 100644
+--- a/drivers/i2c/busses/i2c-aspeed.c
++++ b/drivers/i2c/busses/i2c-aspeed.c
+@@ -1018,7 +1018,6 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
+ spin_lock_init(&bus->lock);
+ init_completion(&bus->cmd_complete);
+ bus->adap.owner = THIS_MODULE;
+- bus->adap.retries = 0;
+ 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/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
+index 302d2d0c87d0..825e2d85d5a7 100644
+--- a/drivers/i2c/i2c-core-base.c
++++ b/drivers/i2c/i2c-core-base.c
+@@ -1320,6 +1320,7 @@ static void i2c_adapter_hold_timer_callback(struct timer_list *t)
+
+ static int i2c_register_adapter(struct i2c_adapter *adap)
+ {
++ u32 bus_timeout_ms = 0;
+ int res = -EINVAL;
+
+ /* Can't register until after driver model init */
+@@ -1347,8 +1348,15 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
+ INIT_LIST_HEAD(&adap->userspace_clients);
+
+ /* Set default timeout to 1 second if not already set */
+- if (adap->timeout == 0)
+- adap->timeout = HZ;
++ if (adap->timeout == 0) {
++ device_property_read_u32(&adap->dev, "bus-timeout-ms",
++ &bus_timeout_ms);
++ adap->timeout = bus_timeout_ms ?
++ msecs_to_jiffies(bus_timeout_ms) : HZ;
++ }
++
++ /* Set retries count if it has the property setting */
++ device_property_read_u32(&adap->dev, "#retries", &adap->retries);
+
+ /* register soft irqs for Host Notify */
+ res = i2c_setup_host_notify_irq_domain(adap);
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0043-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-BT.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0043-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-BT.patch
new file mode 100644
index 000000000..139d06df0
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0043-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-BT.patch
@@ -0,0 +1,140 @@
+From 4d90c5ba05ee3e8a4bf5e4c1a5fdcf2664b1800b Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Wed, 13 Mar 2019 15:04:16 -0700
+Subject: [PATCH] char: ipmi: Add clock control logic into Aspeed LPC BT driver
+
+If LPC BT driver is registered ahead of lpc-ctrl module, LPC BT
+block will be enabled without heart beating of LCLK until lpc-ctrl
+enables the LCLK. This issue causes improper handling on host
+interrupts when the host sends interrupt in that time frame. Then
+kernel eventually forcibly disables the interrupt with dumping
+stack and printing a 'nobody cared this irq' message out.
+
+To prevent this issue, all LPC sub-nodes should enable LCLK
+individually so this patch adds clock control logic into the LPC
+BT driver.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ .../bindings/ipmi/aspeed,ast2400-ibt-bmc.txt | 3 +++
+ arch/arm/boot/dts/aspeed-g4.dtsi | 1 +
+ arch/arm/boot/dts/aspeed-g5.dtsi | 1 +
+ drivers/char/ipmi/bt-bmc.c | 24 +++++++++++++++++++++-
+ 4 files changed, 28 insertions(+), 1 deletion(-)
+
+diff --git a/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-ibt-bmc.txt b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-ibt-bmc.txt
+index 028268fd99ee..d13887d60f19 100644
+--- a/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-ibt-bmc.txt
++++ b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-ibt-bmc.txt
+@@ -10,6 +10,8 @@ Required properties:
+ "aspeed,ast2400-ibt-bmc"
+ "aspeed,ast2500-ibt-bmc"
+ - reg: physical address and size of the registers
++- clocks: contains a phandle to the syscon node describing the clocks.
++ There should then be one cell representing the clock to use.
+
+ Optional properties:
+
+@@ -22,4 +24,5 @@ Example:
+ compatible = "aspeed,ast2400-ibt-bmc";
+ reg = <0x1e789140 0x18>;
+ interrupts = <8>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ };
+diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
+index 78251541a109..b3b6720fb6fb 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -387,6 +387,7 @@
+ ibt: ibt@c0 {
+ compatible = "aspeed,ast2400-ibt-bmc";
+ reg = <0xc0 0x18>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ interrupts = <8>;
+ status = "disabled";
+ };
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index 653e03a0fa4c..49f792eafdd1 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -500,6 +500,7 @@
+ ibt: ibt@c0 {
+ compatible = "aspeed,ast2500-ibt-bmc";
+ reg = <0xc0 0x18>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ interrupts = <8>;
+ status = "disabled";
+ };
+diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c
+index 40b9927c072c..a4ec9d1743d7 100644
+--- a/drivers/char/ipmi/bt-bmc.c
++++ b/drivers/char/ipmi/bt-bmc.c
+@@ -5,6 +5,7 @@
+
+ #include <linux/atomic.h>
+ #include <linux/bt-bmc.h>
++#include <linux/clk.h>
+ #include <linux/errno.h>
+ #include <linux/interrupt.h>
+ #include <linux/io.h>
+@@ -60,6 +61,7 @@ struct bt_bmc {
+ struct device dev;
+ struct miscdevice miscdev;
+ struct regmap *map;
++ struct clk *clk;
+ int offset;
+ int irq;
+ wait_queue_head_t queue;
+@@ -467,6 +469,19 @@ static int bt_bmc_probe(struct platform_device *pdev)
+ mutex_init(&bt_bmc->mutex);
+ init_waitqueue_head(&bt_bmc->queue);
+
++ bt_bmc->clk = devm_clk_get(dev, NULL);
++ if (IS_ERR(bt_bmc->clk)) {
++ rc = PTR_ERR(bt_bmc->clk);
++ if (rc != -EPROBE_DEFER)
++ dev_err(dev, "couldn't get clock\n");
++ return rc;
++ }
++ rc = clk_prepare_enable(bt_bmc->clk);
++ if (rc) {
++ dev_err(dev, "couldn't enable clock\n");
++ return rc;
++ }
++
+ bt_bmc->miscdev.minor = MISC_DYNAMIC_MINOR,
+ bt_bmc->miscdev.name = DEVICE_NAME,
+ bt_bmc->miscdev.fops = &bt_bmc_fops,
+@@ -474,7 +489,7 @@ static int bt_bmc_probe(struct platform_device *pdev)
+ rc = misc_register(&bt_bmc->miscdev);
+ if (rc) {
+ dev_err(dev, "Unable to register misc device\n");
+- return rc;
++ goto err;
+ }
+
+ bt_bmc_config_irq(bt_bmc, pdev);
+@@ -498,6 +513,11 @@ static int bt_bmc_probe(struct platform_device *pdev)
+ clr_b_busy(bt_bmc);
+
+ return 0;
++
++err:
++ clk_disable_unprepare(bt_bmc->clk);
++
++ return rc;
+ }
+
+ static int bt_bmc_remove(struct platform_device *pdev)
+@@ -507,6 +527,8 @@ static int bt_bmc_remove(struct platform_device *pdev)
+ misc_deregister(&bt_bmc->miscdev);
+ if (!bt_bmc->irq)
+ del_timer_sync(&bt_bmc->poll_timer);
++ clk_disable_unprepare(bt_bmc->clk);
++
+ return 0;
+ }
+
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0044-misc-Add-clock-control-logic-into-Aspeed-LPC-SNOOP-d.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0044-misc-Add-clock-control-logic-into-Aspeed-LPC-SNOOP-d.patch
new file mode 100644
index 000000000..cd20e77ac
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0044-misc-Add-clock-control-logic-into-Aspeed-LPC-SNOOP-d.patch
@@ -0,0 +1,125 @@
+From f2e7fb51e4832a0da2fdb8fb267471b54581312b Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Wed, 13 Mar 2019 15:27:48 -0700
+Subject: [PATCH] misc: Add clock control logic into Aspeed LPC SNOOP driver
+
+If LPC SNOOP driver is registered ahead of lpc-ctrl module, LPC
+SNOOP block will be enabled without heart beating of LCLK until
+lpc-ctrl enables the LCLK. This issue causes improper handling on
+host interrupts when the host sends interrupt in that time frame.
+Then kernel eventually forcibly disables the interrupt with
+dumping stack and printing a 'nobody cared this irq' message out.
+
+To prevent this issue, all LPC sub-nodes should enable LCLK
+individually so this patch adds clock control logic into the LPC
+SNOOP driver.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ arch/arm/boot/dts/aspeed-g4.dtsi | 1 +
+ arch/arm/boot/dts/aspeed-g5.dtsi | 1 +
+ drivers/soc/aspeed/aspeed-lpc-snoop.c | 30 +++++++++++++++++++++++++++---
+ 3 files changed, 29 insertions(+), 3 deletions(-)
+
+diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
+index b3b6720fb6fb..58c5148194a3 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -370,6 +370,7 @@
+ compatible = "aspeed,ast2400-lpc-snoop";
+ reg = <0x0 0x80>;
+ interrupts = <8>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ status = "disabled";
+ };
+
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index 49f792eafdd1..955789d8c736 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -483,6 +483,7 @@
+ compatible = "aspeed,ast2500-lpc-snoop";
+ reg = <0x0 0x80>;
+ interrupts = <8>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ status = "disabled";
+ };
+
+diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c
+index 48f7ac238861..96ea52db25be 100644
+--- a/drivers/soc/aspeed/aspeed-lpc-snoop.c
++++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c
+@@ -11,6 +11,7 @@
+ */
+
+ #include <linux/bitops.h>
++#include <linux/clk.h>
+ #include <linux/interrupt.h>
+ #include <linux/fs.h>
+ #include <linux/kfifo.h>
+@@ -67,6 +68,7 @@ struct aspeed_lpc_snoop_channel {
+ struct aspeed_lpc_snoop {
+ struct regmap *regmap;
+ int irq;
++ struct clk *clk;
+ struct aspeed_lpc_snoop_channel chan[NUM_SNOOP_CHANNELS];
+ };
+
+@@ -282,22 +284,42 @@ static int aspeed_lpc_snoop_probe(struct platform_device *pdev)
+ return -ENODEV;
+ }
+
++ lpc_snoop->clk = devm_clk_get(dev, NULL);
++ if (IS_ERR(lpc_snoop->clk)) {
++ rc = PTR_ERR(lpc_snoop->clk);
++ if (rc != -EPROBE_DEFER)
++ dev_err(dev, "couldn't get clock\n");
++ return rc;
++ }
++ rc = clk_prepare_enable(lpc_snoop->clk);
++ if (rc) {
++ dev_err(dev, "couldn't enable clock\n");
++ return rc;
++ }
++
+ rc = aspeed_lpc_snoop_config_irq(lpc_snoop, pdev);
+ if (rc)
+- return rc;
++ goto err;
+
+ rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 0, port);
+ if (rc)
+- return rc;
++ goto err;
+
+ /* Configuration of 2nd snoop channel port is optional */
+ if (of_property_read_u32_index(dev->of_node, "snoop-ports",
+ 1, &port) == 0) {
+ rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 1, port);
+- if (rc)
++ if (rc) {
+ aspeed_lpc_disable_snoop(lpc_snoop, 0);
++ goto err;
++ }
+ }
+
++ return 0;
++
++err:
++ clk_disable_unprepare(lpc_snoop->clk);
++
+ return rc;
+ }
+
+@@ -309,6 +331,8 @@ static int aspeed_lpc_snoop_remove(struct platform_device *pdev)
+ aspeed_lpc_disable_snoop(lpc_snoop, 0);
+ aspeed_lpc_disable_snoop(lpc_snoop, 1);
+
++ clk_disable_unprepare(lpc_snoop->clk);
++
+ return 0;
+ }
+
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0045-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-KC.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0045-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-KC.patch
new file mode 100644
index 000000000..cfff0a842
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0045-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-KC.patch
@@ -0,0 +1,235 @@
+From 9a6eafbba9f5d972065f65431093ec74968cae39 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Wed, 13 Mar 2019 15:36:34 -0700
+Subject: [PATCH] char: ipmi: Add clock control logic into Aspeed LPC KCS
+ driver
+
+If LPC KCS driver is registered ahead of lpc-ctrl module, LPC KCS
+block will be enabled without heart beating of LCLK until lpc-ctrl
+enables the LCLK. This issue causes improper handling on host
+interrupts when the host sends interrupt in that time frame. Then
+kernel eventually forcibly disables the interrupt with dumping
+stack and printing a 'nobody cared this irq' message out.
+
+To prevent this issue, all LPC sub-nodes should enable LCLK
+individually so this patch adds clock control logic into the LPC
+KCS driver.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ .../devicetree/bindings/ipmi/aspeed-kcs-bmc.txt | 3 ++
+ arch/arm/boot/dts/aspeed-g4.dtsi | 35 ++++++++++++++++++++
+ arch/arm/boot/dts/aspeed-g5.dtsi | 6 +++-
+ drivers/char/ipmi/kcs_bmc_aspeed.c | 37 ++++++++++++++++++----
+ 4 files changed, 73 insertions(+), 8 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt b/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt
+index d98a9bf45d6c..3453eb0bf8f2 100644
+--- a/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt
++++ b/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt
+@@ -9,6 +9,8 @@ Required properties:
+ "aspeed,ast2400-kcs-bmc"
+ "aspeed,ast2500-kcs-bmc"
+ - interrupts : interrupt generated by the controller
++- clocks: contains a phandle to the syscon node describing the clocks.
++ There should then be one cell representing the clock to use.
+ - kcs_chan : The LPC channel number in the controller
+ - kcs_addr : The host CPU IO map address
+
+@@ -19,6 +21,7 @@ Example:
+ compatible = "aspeed,ast2500-kcs-bmc";
+ reg = <0x0 0x80>;
+ interrupts = <8>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ kcs_chan = <3>;
+ kcs_addr = <0xCA2>;
+ status = "okay";
+diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
+index 58c5148194a3..14e5dc260a3b 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -348,6 +348,33 @@
+ lpc_bmc: lpc-bmc@0 {
+ compatible = "aspeed,ast2400-lpc-bmc";
+ reg = <0x0 0x80>;
++ reg-io-width = <4>;
++
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges = <0x0 0x0 0x80>;
++
++ kcs1: kcs1@0 {
++ compatible = "aspeed,ast2400-kcs-bmc";
++ interrupts = <8>;
++ kcs_chan = <1>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
++ status = "disabled";
++ };
++ kcs2: kcs2@0 {
++ compatible = "aspeed,ast2400-kcs-bmc";
++ interrupts = <8>;
++ kcs_chan = <2>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
++ status = "disabled";
++ };
++ kcs3: kcs3@0 {
++ compatible = "aspeed,ast2400-kcs-bmc";
++ interrupts = <8>;
++ kcs_chan = <3>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
++ status = "disabled";
++ };
+ };
+
+ lpc_host: lpc-host@80 {
+@@ -359,6 +386,14 @@
+ #size-cells = <1>;
+ ranges = <0x0 0x80 0x1e0>;
+
++ kcs4: kcs4@0 {
++ compatible = "aspeed,ast2400-kcs-bmc";
++ interrupts = <8>;
++ kcs_chan = <4>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
++ status = "disabled";
++ };
++
+ lpc_ctrl: lpc-ctrl@0 {
+ compatible = "aspeed,ast2400-lpc-ctrl";
+ reg = <0x0 0x80>;
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index 955789d8c736..19739183c1c8 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -135,7 +135,7 @@
+ };
+
+ vic: interrupt-controller@1e6c0080 {
+- compatible = "aspeed,ast2400-vic";
++ compatible = "aspeed,ast2500-vic";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ valid-sources = <0xfefff7ff 0x0807ffff>;
+@@ -440,18 +440,21 @@
+ compatible = "aspeed,ast2500-kcs-bmc";
+ interrupts = <8>;
+ kcs_chan = <1>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ status = "disabled";
+ };
+ kcs2: kcs2@0 {
+ compatible = "aspeed,ast2500-kcs-bmc";
+ interrupts = <8>;
+ kcs_chan = <2>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ status = "disabled";
+ };
+ kcs3: kcs3@0 {
+ compatible = "aspeed,ast2500-kcs-bmc";
+ interrupts = <8>;
+ kcs_chan = <3>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ status = "disabled";
+ };
+ };
+@@ -469,6 +472,7 @@
+ compatible = "aspeed,ast2500-kcs-bmc";
+ interrupts = <8>;
+ kcs_chan = <4>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ status = "disabled";
+ };
+
+diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
+index 3c955946e647..bd1912dc5a21 100644
+--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
++++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
+@@ -1,11 +1,10 @@
+ // SPDX-License-Identifier: GPL-2.0
+-/*
+- * Copyright (c) 2015-2018, Intel Corporation.
+- */
++// Copyright (c) 2015-2019, Intel Corporation.
+
+ #define pr_fmt(fmt) "aspeed-kcs-bmc: " fmt
+
+ #include <linux/atomic.h>
++#include <linux/clk.h>
+ #include <linux/errno.h>
+ #include <linux/interrupt.h>
+ #include <linux/io.h>
+@@ -63,6 +62,7 @@
+
+ struct aspeed_kcs_bmc {
+ struct regmap *map;
++ struct clk *clk;
+ };
+
+
+@@ -264,36 +264,59 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
+ return -ENODEV;
+ }
+
++ priv->clk = devm_clk_get(dev, NULL);
++ if (IS_ERR(priv->clk)) {
++ rc = PTR_ERR(priv->clk);
++ if (rc != -EPROBE_DEFER)
++ dev_err(dev, "couldn't get clock\n");
++ return rc;
++ }
++ rc = clk_prepare_enable(priv->clk);
++ if (rc) {
++ dev_err(dev, "couldn't enable clock\n");
++ return rc;
++ }
++
+ kcs_bmc->ioreg = ast_kcs_bmc_ioregs[chan - 1];
+ kcs_bmc->io_inputb = aspeed_kcs_inb;
+ kcs_bmc->io_outputb = aspeed_kcs_outb;
+
+ dev_set_drvdata(dev, kcs_bmc);
+
+- aspeed_kcs_set_address(kcs_bmc, addr);
+- aspeed_kcs_enable_channel(kcs_bmc, true);
+ rc = aspeed_kcs_config_irq(kcs_bmc, pdev);
+ if (rc)
+- return rc;
++ goto err;
+
+ rc = misc_register(&kcs_bmc->miscdev);
+ if (rc) {
+ dev_err(dev, "Unable to register device\n");
+- return rc;
++ goto err;
+ }
+
++ aspeed_kcs_set_address(kcs_bmc, addr);
++ aspeed_kcs_enable_channel(kcs_bmc, true);
++
+ pr_info("channel=%u addr=0x%x idr=0x%x odr=0x%x str=0x%x\n",
+ chan, addr,
+ kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr, kcs_bmc->ioreg.str);
+
+ return 0;
++
++err:
++ aspeed_kcs_enable_channel(kcs_bmc, false);
++ clk_disable_unprepare(priv->clk);
++
++ return rc;
+ }
+
+ static int aspeed_kcs_remove(struct platform_device *pdev)
+ {
+ struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev);
++ struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
+
+ misc_deregister(&kcs_bmc->miscdev);
++ aspeed_kcs_enable_channel(kcs_bmc, false);
++ clk_disable_unprepare(priv->clk);
+
+ return 0;
+ }
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0047-misc-Block-error-printing-on-probe-defer-case-in-Asp.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0047-misc-Block-error-printing-on-probe-defer-case-in-Asp.patch
new file mode 100644
index 000000000..c3d1f4e8d
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0047-misc-Block-error-printing-on-probe-defer-case-in-Asp.patch
@@ -0,0 +1,43 @@
+From 108b39883e73f822b8f03e0d3fe3818b85b29b41 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Wed, 13 Mar 2019 15:57:08 -0700
+Subject: [PATCH] misc: Block error printing on probe defer case in Aspeed LPC
+ ctrl
+
+This commit adds a checking code when it gets -EPROBE_DEFER while
+getting a clock resource. In this case it doesn't need to print
+out an error message because the probing will be re-visited.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/soc/aspeed/aspeed-lpc-ctrl.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/soc/aspeed/aspeed-lpc-ctrl.c b/drivers/soc/aspeed/aspeed-lpc-ctrl.c
+index 01ed21e8bfee..ae08419834a3 100644
+--- a/drivers/soc/aspeed/aspeed-lpc-ctrl.c
++++ b/drivers/soc/aspeed/aspeed-lpc-ctrl.c
+@@ -252,8 +252,10 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev)
+
+ lpc_ctrl->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(lpc_ctrl->clk)) {
+- dev_err(dev, "couldn't get clock\n");
+- return PTR_ERR(lpc_ctrl->clk);
++ rc = PTR_ERR(lpc_ctrl->clk);
++ if (rc != -EPROBE_DEFER)
++ dev_err(dev, "couldn't get clock\n");
++ return rc;
+ }
+ rc = clk_prepare_enable(lpc_ctrl->clk);
+ if (rc) {
+@@ -275,6 +277,7 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev)
+
+ err:
+ clk_disable_unprepare(lpc_ctrl->clk);
++
+ return rc;
+ }
+
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0049-Suppress-excessive-HID-gadget-error-logs.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0049-Suppress-excessive-HID-gadget-error-logs.patch
new file mode 100644
index 000000000..4a87f2d76
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0049-Suppress-excessive-HID-gadget-error-logs.patch
@@ -0,0 +1,52 @@
+From 5b9ec5081492b461710cb82e7ecc93fd3af8ad34 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Mon, 18 Mar 2019 14:06:36 -0700
+Subject: [PATCH] Suppress excessive HID gadget error logs
+
+HID events can be sent even when the host disconnects the HID
+device according to the current graphic mode. For an example, if
+KVM mouse events are sent when the host is in text mode, queueing
+of end point messages will be dropped with this message:
+
+configfs-gadget gadget: usb_ep_queue error on int endpoint -108
+
+This case is very usual case in BMC since BMC can control power
+status of the host, so this commit suppress the error printing outs
+with making HID gadget driver drop events quietly in the case.
+
+This should be a downstream only customization. Do not upstream it.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/usb/gadget/function/f_hid.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
+index f3816a5c861e..c96c0f6f1df0 100644
+--- a/drivers/usb/gadget/function/f_hid.c
++++ b/drivers/usb/gadget/function/f_hid.c
+@@ -320,7 +320,7 @@ static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req)
+ struct f_hidg *hidg = (struct f_hidg *)ep->driver_data;
+ unsigned long flags;
+
+- if (req->status != 0) {
++ if (req->status != 0 && req->status != -ESHUTDOWN) {
+ ERROR(hidg->func.config->cdev,
+ "End Point Request ERROR: %d\n", req->status);
+ }
+@@ -395,8 +395,10 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
+
+ status = usb_ep_queue(hidg->in_ep, req, GFP_ATOMIC);
+ if (status < 0) {
+- ERROR(hidg->func.config->cdev,
+- "usb_ep_queue error on int endpoint %zd\n", status);
++ if (status != -ESHUTDOWN)
++ ERROR(hidg->func.config->cdev,
++ "usb_ep_queue error on int endpoint %zd\n",
++ status);
+ goto release_write_pending;
+ } else {
+ status = count;
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0051-Add-AST2500-JTAG-device.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0051-Add-AST2500-JTAG-device.patch
new file mode 100644
index 000000000..02bb6527f
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0051-Add-AST2500-JTAG-device.patch
@@ -0,0 +1,35 @@
+From ce35414258a8541a8b81a4a8a929bcf9cdface97 Mon Sep 17 00:00:00 2001
+From: "Hunt, Bryan" <bryan.hunt@intel.com>
+Date: Mon, 6 May 2019 10:02:14 -0700
+Subject: [PATCH] Add AST2500d JTAG driver
+
+Adding aspeed jtag device
+
+Signed-off-by: Hunt, Bryan <bryan.hunt@intel.com>
+---
+ arch/arm/boot/dts/aspeed-g5.dtsi | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index 19739183c1c8..3d615708a0cd 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -419,6 +419,15 @@
+ pinctrl-0 = <&pinctrl_espi_default>;
+ };
+
++ jtag: jtag@1e6e4000 {
++ compatible = "aspeed,ast2500-jtag";
++ reg = <0x1e6e4000 0x1c>;
++ clocks = <&syscon ASPEED_CLK_APB>;
++ resets = <&syscon ASPEED_RESET_JTAG_MASTER>;
++ interrupts = <43>;
++ status = "disabled";
++ };
++
+ lpc: lpc@1e789000 {
+ compatible = "aspeed,ast2500-lpc", "simple-mfd";
+ reg = <0x1e789000 0x1000>;
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0052-drivers-jtag-Add-JTAG-core-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0052-drivers-jtag-Add-JTAG-core-driver.patch
new file mode 100644
index 000000000..4162046e7
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0052-drivers-jtag-Add-JTAG-core-driver.patch
@@ -0,0 +1,920 @@
+From 45dd8ca9bb83b688aa0d0b5472fd0b1ed9fcf29a Mon Sep 17 00:00:00 2001
+From: "Corona, Ernesto" <ernesto.corona@intel.com>
+Date: Fri, 7 Jun 2019 07:37:39 -0800
+Subject: [PATCH v29 1/6] drivers: jtag: Add JTAG core driver
+
+JTAG class driver provide infrastructure to support hardware/software
+JTAG platform drivers. It provide user layer API interface for flashing
+and debugging external devices which equipped with JTAG interface
+using standard transactions.
+
+Driver exposes set of IOCTL to user space for:
+- XFER:
+ SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
+ SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
+- GIOCSTATUS read the current TAPC state of the JTAG controller
+- SIOCSTATE Forces the JTAG TAPC to go into a particular state.
+- SIOCFREQ/GIOCFREQ for setting and reading JTAG frequency.
+- IOCBITBANG for low level control of JTAG signals.
+
+Driver core provides set of internal APIs for allocation and
+registration:
+- jtag_register;
+- jtag_unregister;
+- jtag_alloc;
+- jtag_free;
+
+Platform driver on registration with jtag-core creates the next
+entry in dev folder:
+/dev/jtagX
+
+Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
+Signed-off-by: Jiri Pirko <jiri@mellanox.com>
+Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com>
+Acked-by: Philippe Ombredanne <pombredanne@nexb.com>
+Cc: Vadim Pasternak <vadimp@mellanox.com>
+Cc: Masahiro Yamada <yamada.masahiro@socionext.com>
+Cc: Paul Burton <paul.burton@mips.com>
+Cc: Thomas Gleixner <tglx@linutronix.de>
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Cc: Boris Brezillon <bbrezillon@kernel.org>
+Cc: Randy Dunlap <rdunlap@infradead.org>
+Cc: Johan Hovold <johan@kernel.org>
+Cc: Jens Axboe <axboe@kernel.dk>
+Cc: Joel Stanley <joel@jms.id.au>
+Cc: Palmer Dabbelt <palmer@sifive.com>
+Cc: Kees Cook <keescook@chromium.org>
+Cc: Steven A Filary <steven.a.filary@intel.com>
+Cc: Bryan Hunt <bryan.hunt@intel.com>
+---
+v28->v29
+Comments pointed by Steven Filary <steven.a.filary@intel.com>
+- Expand bitbang function to accept multiples bitbang operations within a
+ single JTAG_IOCBITBANG call. It will receive a buffer with TDI and TMS
+ values and it is expected that driver fills TDO fields with its
+ corresponding output value for every transaction.
+- Always setup JTAG controller to master mode but disable JTAG output when
+ the driver is not in use to allow other HW to own the JTAG bus. Remove SCU
+ register accesses. This register controls the JTAG controller mode
+ (master/slave).
+- Fix static analysis issues
+- Add support for multichain. Set tap state and xfer operations now include
+ two tap state arguments: current state and end state.
+
+v27->v28
+Comments pointed by Steven Filary <steven.a.filary@intel.com>
+- Replace JTAG_IOCRUNTEST with JTAG_SIOCSTATE adding support for all TAPC
+ end states in SW mode using a lookup table to navigate across states.
+- Add support for simultaneous READ/WRITE transfers(JTAG_READ_WRITE_XFER).
+- Support for switching JTAG controller mode between slave and master
+ mode.
+- Setup JTAG controller mode to master only when the driver is opened,
+ letting
+ other HW to own the JTAG bus when it isn't in use.
+- Include JTAG bit bang IOCTL for low level JTAG control usage
+ (JTAG_IOCBITBANG).
+
+v24->v25
+Comments pointed by Greg KH <gregkh@linuxfoundation.org>
+- set values to enums in jtag.h
+
+v23->v24
+Notifications from kbuild test robot <lkp@intel.com>
+- Add include types.h header to jtag.h
+- remove unecessary jtag_release
+
+v22->v23
+Comments pointed by Greg KH <gregkh@linuxfoundation.org>
+- remove restriction of allocated JTAG devs-
+- add validation fo idle values
+- remove unnecessary blank line
+- change retcode for xfer
+- remove unecessary jtag_release callback
+- remove unecessary defined fron jtag.h
+- align in one line define JTAG_IOCRUNTEST
+
+v21->v22
+Comments pointed by Andy Shevchenko <andy.shevchenko@gmail.com>
+- Fix 0x0f -> 0x0F in ioctl-number.txt
+- Add description to #define MAX_JTAG_NAME_LEN
+- Remove unnecessary entry *dev from struct jtag
+- Remove redundant parens
+- Described mandatory callbacks and removed unnecessary
+- Set JTAG_MAX_XFER_DATA_LEN to power of 2
+- rework driver alloc/register to devm_ variant
+- increasing line length up to 84 in order to improve readability.
+
+Comments pointed by Randy Dunlap <rdunlap@infradead.org>
+- fix spell in ABI doccumentation
+
+v20->v21
+ Comments pointed by Randy Dunlap <rdunlap@infradead.org>
+ - Fix JTAG dirver help in Kconfig
+
+v19->v20
+Comments pointed by Randy Dunlap <rdunlap@infradead.org>
+- Fix JTAG dirver help in Kconfig
+
+Notifications from kbuild test robot <lkp@intel.com>
+- fix incompatible type casts
+
+v18->v19
+Comments pointed by Julia Cartwright <juliac@eso.teric.us>
+- Fix memory leak on jtag_alloc exit
+
+v17->v18
+Comments pointed by Julia Cartwright <juliac@eso.teric.us>
+- Change to return -EOPNOTSUPP in case of error in JTAG_GIOCFREQ
+- Add ops callbacks check to jtag_alloc
+- Add err check for copy_to_user
+- Move the kfree() above the if (err) in JTAG_IOCXFER
+- remove unnecessary check for error after put_user
+- add padding to struct jtag_xfer
+
+v16->v17
+Comments pointed by Julia Cartwright <juliac@eso.teric.us>
+- Fix memory allocation on jtag alloc
+- Move out unnecessary form lock on jtag open
+- Rework jtag register behavior
+
+v15->v16
+Comments pointed by Florian Fainelli <f.fainelli@gmail.com>
+- move check jtag->ops->* in ioctl before get_user()
+- change error type -EINVAL --> -EBUSY on open already opened jtag
+- remove unnecessary ARCH_DMA_MINALIGN flag from kzalloc
+- remove define ARCH_DMA_MINALIGN
+
+v14->v15
+v13->v14
+Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
+- Change style of head block comment from /**/ to //
+
+v12->v13
+Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
+- Change jtag.c licence type to
+ SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+ and reorder line with license in description
+
+v11->v12
+Comments pointed by Greg KH <gregkh@linuxfoundation.org>
+- Change jtag.h licence type to
+ SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+ and reorder line with license in description
+
+Comments pointed by Chip Bilbrey <chip@bilbrey.org>
+- Remove Apeed reference from uapi jtag.h header
+- Remove access mode from xfer and idle transactions
+- Add new ioctl JTAG_SIOCMODE for set hw mode
+- Add single open per device blocking
+
+v10->v11
+Notifications from kbuild test robot <lkp@intel.com>
+- Add include types.h header to jtag.h
+- fix incompatible type of xfer callback
+- remove rdundant class defination
+- Fix return order in case of xfer error
+
+V9->v10
+Comments pointed by Greg KH <gregkh@linuxfoundation.org>
+- remove unnecessary alignment for pirv data
+- move jtag_copy_to_user and jtag_copy_from_user code just to ioctl
+- move int jtag_run_test_idle_op and jtag_xfer_op code
+ just to ioctl
+- change return error codes to more applicable
+- add missing error checks
+- fix error check order in ioctl
+- remove unnecessary blank lines
+- add param validation to ioctl
+- remove compat_ioctl
+- remove only one open per JTAG port blocking.
+ User will care about this.
+- Fix idr memory leak on jtag_exit
+- change cdev device type to misc
+
+V8->v9
+Comments pointed by Arnd Bergmann <arnd@arndb.de>
+- use get_user() instead of __get_user().
+- change jtag->open type from int to atomic_t
+- remove spinlock on jtg_open
+- remove mutex on jtag_register
+- add unregister_chrdev_region on jtag_init err
+- add unregister_chrdev_region on jtag_exit
+- remove unnecessary pointer casts
+- add *data parameter to xfer function prototype
+
+v7->v8
+Comments pointed by Moritz Fischer <moritz.fischer@ettus.com>
+- Fix misspelling s/friver/driver
+
+v6->v7
+Notifications from kbuild test robot <lkp@intel.com>
+- Remove include asm/types.h from jtag.h
+- Add include <linux/types.h> to jtag.c
+
+v5->v6
+v4->v5
+
+v3->v4
+Comments pointed by Arnd Bergmann <arnd@arndb.de>
+- change transaction pointer tdio type to __u64
+- change internal status type from enum to __u32
+- reorder jtag_xfer members to avoid the implied padding
+- add __packed attribute to jtag_xfer and jtag_run_test_idle
+
+v2->v3
+Notifications from kbuild test robot <lkp@intel.com>
+- Change include path to <linux/types.h> in jtag.h
+
+v1->v2
+Comments pointed by Greg KH <gregkh@linuxfoundation.org>
+- Change license type from GPLv2/BSD to GPLv2
+- Change type of variables which crossed user/kernel to __type
+- Remove "default n" from Kconfig
+
+Comments pointed by Andrew Lunn <andrew@lunn.ch>
+- Change list_add_tail in jtag_unregister to list_del
+
+Comments pointed by Neil Armstrong <narmstrong@baylibre.com>
+- Add SPDX-License-Identifier instead of license text
+
+Comments pointed by Arnd Bergmann <arnd@arndb.de>
+- Change __copy_to_user to memdup_user
+- Change __put_user to put_user
+- Change type of variables to __type for compatible 32 and 64-bit systems
+- Add check for maximum xfer data size
+- Change lookup data mechanism to get jtag data from inode
+- Add .compat_ioctl to file ops
+- Add mem alignment for jtag priv data
+
+Comments pointed by Tobias Klauser <tklauser@distanz.ch>
+- Change function names to avoid match with variable types
+- Fix description for jtag_ru_test_idle in uapi jtag.h
+- Fix misprints IDEL/IDLE, trough/through
+---
+ drivers/Kconfig | 1 +
+ drivers/Makefile | 1 +
+ drivers/jtag/Kconfig | 17 +++
+ drivers/jtag/Makefile | 1 +
+ drivers/jtag/jtag.c | 321 ++++++++++++++++++++++++++++++++++++++++++++++
+ include/linux/jtag.h | 47 +++++++
+ include/uapi/linux/jtag.h | 214 +++++++++++++++++++++++++++++++
+ 7 files changed, 602 insertions(+)
+ create mode 100644 drivers/jtag/Kconfig
+ create mode 100644 drivers/jtag/Makefile
+ create mode 100644 drivers/jtag/jtag.c
+ create mode 100644 include/linux/jtag.h
+ create mode 100644 include/uapi/linux/jtag.h
+
+diff --git a/drivers/Kconfig b/drivers/Kconfig
+index 92f4a9bb83f1..7403af7ffa85 100644
+--- a/drivers/Kconfig
++++ b/drivers/Kconfig
+@@ -232,4 +232,5 @@ source "drivers/counter/Kconfig"
+
+ source "drivers/peci/Kconfig"
+
++source "drivers/jtag/Kconfig"
+ endmenu
+diff --git a/drivers/Makefile b/drivers/Makefile
+index 47cad1b9f992..cd240910c56e 100644
+--- a/drivers/Makefile
++++ b/drivers/Makefile
+@@ -188,3 +188,4 @@ obj-$(CONFIG_GNSS) += gnss/
+ obj-$(CONFIG_INTERCONNECT) += interconnect/
+ obj-$(CONFIG_COUNTER) += counter/
+ obj-$(CONFIG_PECI) += peci/
++obj-$(CONFIG_JTAG_ASPEED) += jtag/
+diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig
+new file mode 100644
+index 000000000000..47771fcd3c5b
+--- /dev/null
++++ b/drivers/jtag/Kconfig
+@@ -0,0 +1,17 @@
++menuconfig JTAG
++ tristate "JTAG support"
++ help
++ This provides basic core functionality support for JTAG class devices.
++ Hardware that is equipped with a JTAG microcontroller can be
++ supported by using this driver's interfaces.
++ This driver exposes a set of IOCTLs to the user space for
++ the following commands:
++ SDR: Performs an IEEE 1149.1 Data Register scan
++ SIR: Performs an IEEE 1149.1 Instruction Register scan.
++ RUNTEST: Forces the IEEE 1149.1 bus to a run state for a specified
++ number of clocks or a specified time period.
++
++ If you want this support, you should say Y here.
++
++ To compile this driver as a module, choose M here: the module will
++ be called jtag.
+diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile
+new file mode 100644
+index 000000000000..af374939a9e6
+--- /dev/null
++++ b/drivers/jtag/Makefile
+@@ -0,0 +1 @@
++obj-$(CONFIG_JTAG) += jtag.o
+diff --git a/drivers/jtag/jtag.c b/drivers/jtag/jtag.c
+new file mode 100644
+index 000000000000..39a4d88a9c21
+--- /dev/null
++++ b/drivers/jtag/jtag.c
+@@ -0,0 +1,321 @@
++// SPDX-License-Identifier: GPL-2.0-only
++// Copyright (c) 2018 Mellanox Technologies. All rights reserved.
++// Copyright (c) 2018 Oleksandr Shamray <oleksandrs@mellanox.com>
++// Copyright (c) 2019 Intel Corporation
++
++#include <linux/cdev.h>
++#include <linux/device.h>
++#include <linux/jtag.h>
++#include <linux/kernel.h>
++#include <linux/list.h>
++#include <linux/miscdevice.h>
++#include <linux/module.h>
++#include <linux/rtnetlink.h>
++#include <linux/spinlock.h>
++#include <linux/types.h>
++#include <uapi/linux/jtag.h>
++
++struct jtag {
++ struct miscdevice miscdev;
++ const struct jtag_ops *ops;
++ int id;
++ unsigned long priv[0];
++};
++
++static DEFINE_IDA(jtag_ida);
++
++void *jtag_priv(struct jtag *jtag)
++{
++ return jtag->priv;
++}
++EXPORT_SYMBOL_GPL(jtag_priv);
++
++static long jtag_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
++{
++ struct jtag *jtag = file->private_data;
++ struct jtag_tap_state tapstate;
++ struct jtag_xfer xfer;
++ struct bitbang_packet bitbang;
++ struct tck_bitbang *bitbang_data;
++ struct jtag_mode mode;
++ u8 *xfer_data;
++ u32 data_size;
++ u32 value;
++ int err;
++
++ if (!arg)
++ return -EINVAL;
++
++ switch (cmd) {
++ case JTAG_GIOCFREQ:
++ if (!jtag->ops->freq_get)
++ return -EOPNOTSUPP;
++
++ err = jtag->ops->freq_get(jtag, &value);
++ if (err)
++ break;
++
++ if (put_user(value, (__u32 __user *)arg))
++ err = -EFAULT;
++ break;
++
++ case JTAG_SIOCFREQ:
++ if (!jtag->ops->freq_set)
++ return -EOPNOTSUPP;
++
++ if (get_user(value, (__u32 __user *)arg))
++ return -EFAULT;
++ if (value == 0)
++ return -EINVAL;
++
++ err = jtag->ops->freq_set(jtag, value);
++ break;
++
++ case JTAG_SIOCSTATE:
++ if (copy_from_user(&tapstate, (const void __user *)arg,
++ sizeof(struct jtag_tap_state)))
++ return -EFAULT;
++
++ if (tapstate.from > JTAG_STATE_CURRENT)
++ return -EINVAL;
++
++ if (tapstate.endstate > JTAG_STATE_CURRENT)
++ return -EINVAL;
++
++ if (tapstate.reset > JTAG_FORCE_RESET)
++ return -EINVAL;
++
++ err = jtag->ops->status_set(jtag, &tapstate);
++ break;
++
++ case JTAG_IOCXFER:
++ if (copy_from_user(&xfer, (const void __user *)arg,
++ sizeof(struct jtag_xfer)))
++ return -EFAULT;
++
++ if (xfer.length >= JTAG_MAX_XFER_DATA_LEN)
++ return -EINVAL;
++
++ if (xfer.type > JTAG_SDR_XFER)
++ return -EINVAL;
++
++ if (xfer.direction > JTAG_READ_WRITE_XFER)
++ return -EINVAL;
++
++ if (xfer.from > JTAG_STATE_CURRENT)
++ return -EINVAL;
++
++ if (xfer.endstate > JTAG_STATE_CURRENT)
++ return -EINVAL;
++
++ data_size = DIV_ROUND_UP(xfer.length, BITS_PER_BYTE);
++ xfer_data = memdup_user(u64_to_user_ptr(xfer.tdio), data_size);
++ if (IS_ERR(xfer_data))
++ return -EFAULT;
++
++ err = jtag->ops->xfer(jtag, &xfer, xfer_data);
++ if (err) {
++ kfree(xfer_data);
++ return err;
++ }
++
++ err = copy_to_user(u64_to_user_ptr(xfer.tdio),
++ (void *)xfer_data, data_size);
++ kfree(xfer_data);
++ if (err)
++ return -EFAULT;
++
++ if (copy_to_user((void __user *)arg, (void *)&xfer,
++ sizeof(struct jtag_xfer)))
++ return -EFAULT;
++ break;
++
++ case JTAG_GIOCSTATUS:
++ err = jtag->ops->status_get(jtag, &value);
++ if (err)
++ break;
++
++ err = put_user(value, (__u32 __user *)arg);
++ break;
++ case JTAG_IOCBITBANG:
++ if (copy_from_user(&bitbang, (const void __user *)arg,
++ sizeof(struct bitbang_packet)))
++ return -EFAULT;
++
++ if (bitbang.length >= JTAG_MAX_XFER_DATA_LEN)
++ return -EINVAL;
++
++ data_size = bitbang.length * sizeof(struct tck_bitbang);
++ bitbang_data = memdup_user((void __user *)bitbang.data,
++ data_size);
++ if (IS_ERR(bitbang_data))
++ return -EFAULT;
++
++ err = jtag->ops->bitbang(jtag, &bitbang, bitbang_data);
++ if (err) {
++ kfree(bitbang_data);
++ return err;
++ }
++ err = copy_to_user((void __user *)bitbang.data,
++ (void *)bitbang_data, data_size);
++ kfree(bitbang_data);
++ if (err)
++ return -EFAULT;
++ break;
++ case JTAG_SIOCMODE:
++ if (!jtag->ops->mode_set)
++ return -EOPNOTSUPP;
++
++ if (copy_from_user(&mode, (const void __user *)arg,
++ sizeof(struct jtag_mode)))
++ return -EFAULT;
++
++ err = jtag->ops->mode_set(jtag, &mode);
++ break;
++
++ default:
++ return -EINVAL;
++ }
++ return err;
++}
++
++static int jtag_open(struct inode *inode, struct file *file)
++{
++ struct jtag *jtag = container_of(file->private_data,
++ struct jtag,
++ miscdev);
++
++ file->private_data = jtag;
++ if (jtag->ops->enable(jtag))
++ return -EBUSY;
++ return nonseekable_open(inode, file);
++}
++
++static int jtag_release(struct inode *inode, struct file *file)
++{
++ struct jtag *jtag = file->private_data;
++
++ if (jtag->ops->disable(jtag))
++ return -EBUSY;
++ return 0;
++}
++
++static const struct file_operations jtag_fops = {
++ .owner = THIS_MODULE,
++ .open = jtag_open,
++ .llseek = noop_llseek,
++ .unlocked_ioctl = jtag_ioctl,
++ .release = jtag_release,
++};
++
++struct jtag *jtag_alloc(struct device *host, size_t priv_size,
++ const struct jtag_ops *ops)
++{
++ struct jtag *jtag;
++
++ if (!host)
++ return NULL;
++
++ if (!ops)
++ return NULL;
++
++ if (!ops->status_set || !ops->status_get || !ops->xfer)
++ return NULL;
++
++ jtag = kzalloc(sizeof(*jtag) + priv_size, GFP_KERNEL);
++ if (!jtag)
++ return NULL;
++
++ jtag->ops = ops;
++ jtag->miscdev.parent = host;
++
++ return jtag;
++}
++EXPORT_SYMBOL_GPL(jtag_alloc);
++
++void jtag_free(struct jtag *jtag)
++{
++ kfree(jtag);
++}
++EXPORT_SYMBOL_GPL(jtag_free);
++
++static int jtag_register(struct jtag *jtag)
++{
++ struct device *dev = jtag->miscdev.parent;
++ int err;
++ int id;
++
++ if (!dev)
++ return -ENODEV;
++
++ id = ida_simple_get(&jtag_ida, 0, 0, GFP_KERNEL);
++ if (id < 0)
++ return id;
++
++ jtag->id = id;
++
++ jtag->miscdev.fops = &jtag_fops;
++ jtag->miscdev.minor = MISC_DYNAMIC_MINOR;
++ jtag->miscdev.name = kasprintf(GFP_KERNEL, "jtag%d", id);
++ if (!jtag->miscdev.name) {
++ err = -ENOMEM;
++ goto err_jtag_alloc;
++ }
++
++ err = misc_register(&jtag->miscdev);
++ if (err) {
++ dev_err(jtag->miscdev.parent, "Unable to register device\n");
++ goto err_jtag_name;
++ }
++ return 0;
++
++err_jtag_name:
++ kfree(jtag->miscdev.name);
++err_jtag_alloc:
++ ida_simple_remove(&jtag_ida, id);
++ return err;
++}
++
++static void jtag_unregister(struct jtag *jtag)
++{
++ misc_deregister(&jtag->miscdev);
++ kfree(jtag->miscdev.name);
++ ida_simple_remove(&jtag_ida, jtag->id);
++}
++
++static void devm_jtag_unregister(struct device *dev, void *res)
++{
++ jtag_unregister(*(struct jtag **)res);
++}
++
++int devm_jtag_register(struct device *dev, struct jtag *jtag)
++{
++ struct jtag **ptr;
++ int ret;
++
++ ptr = devres_alloc(devm_jtag_unregister, sizeof(struct jtag *),
++ GFP_KERNEL);
++ if (!ptr)
++ return -ENOMEM;
++
++ ret = jtag_register(jtag);
++ if (!ret) {
++ *ptr = jtag;
++ devres_add(dev, ptr);
++ } else {
++ devres_free(ptr);
++ }
++ return ret;
++}
++EXPORT_SYMBOL_GPL(devm_jtag_register);
++
++static void __exit jtag_exit(void)
++{
++ ida_destroy(&jtag_ida);
++}
++
++module_exit(jtag_exit);
++
++MODULE_AUTHOR("Oleksandr Shamray <oleksandrs@mellanox.com>");
++MODULE_DESCRIPTION("Generic jtag support");
++MODULE_LICENSE("GPL v2");
+diff --git a/include/linux/jtag.h b/include/linux/jtag.h
+new file mode 100644
+index 000000000000..fab12dc4fc5e
+--- /dev/null
++++ b/include/linux/jtag.h
+@@ -0,0 +1,47 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/* Copyright (c) 2018 Mellanox Technologies. All rights reserved. */
++/* Copyright (c) 2018 Oleksandr Shamray <oleksandrs@mellanox.com> */
++/* Copyright (c) 2019 Intel Corporation */
++
++#ifndef __LINUX_JTAG_H
++#define __LINUX_JTAG_H
++
++#include <linux/types.h>
++#include <uapi/linux/jtag.h>
++
++#define JTAG_MAX_XFER_DATA_LEN 65535
++
++struct jtag;
++/**
++ * struct jtag_ops - callbacks for JTAG control functions:
++ *
++ * @freq_get: get frequency function. Filled by dev driver
++ * @freq_set: set frequency function. Filled by dev driver
++ * @status_get: get JTAG TAPC state function. Mandatory, Filled by dev driver
++ * @status_set: set JTAG TAPC state function. Mandatory, Filled by dev driver
++ * @xfer: send JTAG xfer function. Mandatory func. Filled by dev driver
++ * @mode_set: set specific work mode for JTAG. Filled by dev driver
++ * @bitbang: set low level bitbang operations. Filled by dev driver
++ * @enable: enables JTAG interface in master mode. Filled by dev driver
++ * @disable: disables JTAG interface master mode. Filled by dev driver
++ */
++struct jtag_ops {
++ int (*freq_get)(struct jtag *jtag, u32 *freq);
++ int (*freq_set)(struct jtag *jtag, u32 freq);
++ int (*status_get)(struct jtag *jtag, u32 *state);
++ int (*status_set)(struct jtag *jtag, struct jtag_tap_state *endst);
++ int (*xfer)(struct jtag *jtag, struct jtag_xfer *xfer, u8 *xfer_data);
++ int (*mode_set)(struct jtag *jtag, struct jtag_mode *jtag_mode);
++ int (*bitbang)(struct jtag *jtag, struct bitbang_packet *bitbang,
++ struct tck_bitbang *bitbang_data);
++ int (*enable)(struct jtag *jtag);
++ int (*disable)(struct jtag *jtag);
++};
++
++void *jtag_priv(struct jtag *jtag);
++int devm_jtag_register(struct device *dev, struct jtag *jtag);
++struct jtag *jtag_alloc(struct device *host, size_t priv_size,
++ const struct jtag_ops *ops);
++void jtag_free(struct jtag *jtag);
++
++#endif /* __LINUX_JTAG_H */
+diff --git a/include/uapi/linux/jtag.h b/include/uapi/linux/jtag.h
+new file mode 100644
+index 000000000000..315e59577a17
+--- /dev/null
++++ b/include/uapi/linux/jtag.h
+@@ -0,0 +1,214 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/* Copyright (c) 2018 Mellanox Technologies. All rights reserved. */
++/* Copyright (c) 2018 Oleksandr Shamray <oleksandrs@mellanox.com> */
++/* Copyright (c) 2019 Intel Corporation */
++
++#ifndef __UAPI_LINUX_JTAG_H
++#define __UAPI_LINUX_JTAG_H
++
++/*
++ * JTAG_XFER_MODE: JTAG transfer mode. Used to set JTAG controller transfer mode
++ * This is bitmask for feature param in jtag_mode for ioctl JTAG_SIOCMODE
++ */
++#define JTAG_XFER_MODE 0
++/*
++ * JTAG_CONTROL_MODE: JTAG controller mode. Used to set JTAG controller mode
++ * This is bitmask for feature param in jtag_mode for ioctl JTAG_SIOCMODE
++ */
++#define JTAG_CONTROL_MODE 1
++/*
++ * JTAG_MASTER_OUTPUT_DISABLE: JTAG master mode output disable, it is used to
++ * enable other devices to own the JTAG bus.
++ * This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE
++ */
++#define JTAG_MASTER_OUTPUT_DISABLE 0
++/*
++ * JTAG_MASTER_MODE: JTAG master mode. Used to set JTAG controller master mode
++ * This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE
++ */
++#define JTAG_MASTER_MODE 1
++/*
++ * JTAG_XFER_HW_MODE: JTAG hardware mode. Used to set HW drived or bitbang
++ * mode. This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE
++ */
++#define JTAG_XFER_HW_MODE 1
++/*
++ * JTAG_XFER_SW_MODE: JTAG software mode. Used to set SW drived or bitbang
++ * mode. This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE
++ */
++#define JTAG_XFER_SW_MODE 0
++
++/**
++ * enum jtag_tapstate:
++ *
++ * @JTAG_STATE_TLRESET: JTAG state machine Test Logic Reset state
++ * @JTAG_STATE_IDLE: JTAG state machine IDLE state
++ * @JTAG_STATE_SELECTDR: JTAG state machine SELECT_DR state
++ * @JTAG_STATE_CAPTUREDR: JTAG state machine CAPTURE_DR state
++ * @JTAG_STATE_SHIFTDR: JTAG state machine SHIFT_DR state
++ * @JTAG_STATE_EXIT1DR: JTAG state machine EXIT-1 DR state
++ * @JTAG_STATE_PAUSEDR: JTAG state machine PAUSE_DR state
++ * @JTAG_STATE_EXIT2DR: JTAG state machine EXIT-2 DR state
++ * @JTAG_STATE_UPDATEDR: JTAG state machine UPDATE DR state
++ * @JTAG_STATE_SELECTIR: JTAG state machine SELECT_IR state
++ * @JTAG_STATE_CAPTUREIR: JTAG state machine CAPTURE_IR state
++ * @JTAG_STATE_SHIFTIR: JTAG state machine SHIFT_IR state
++ * @JTAG_STATE_EXIT1IR: JTAG state machine EXIT-1 IR state
++ * @JTAG_STATE_PAUSEIR: JTAG state machine PAUSE_IR state
++ * @JTAG_STATE_EXIT2IR: JTAG state machine EXIT-2 IR state
++ * @JTAG_STATE_UPDATEIR: JTAG state machine UPDATE IR state
++ * @JTAG_STATE_CURRENT: JTAG current state, saved by driver
++ */
++enum jtag_tapstate {
++ JTAG_STATE_TLRESET,
++ JTAG_STATE_IDLE,
++ JTAG_STATE_SELECTDR,
++ JTAG_STATE_CAPTUREDR,
++ JTAG_STATE_SHIFTDR,
++ JTAG_STATE_EXIT1DR,
++ JTAG_STATE_PAUSEDR,
++ JTAG_STATE_EXIT2DR,
++ JTAG_STATE_UPDATEDR,
++ JTAG_STATE_SELECTIR,
++ JTAG_STATE_CAPTUREIR,
++ JTAG_STATE_SHIFTIR,
++ JTAG_STATE_EXIT1IR,
++ JTAG_STATE_PAUSEIR,
++ JTAG_STATE_EXIT2IR,
++ JTAG_STATE_UPDATEIR,
++ JTAG_STATE_CURRENT
++};
++
++/**
++ * enum jtag_reset:
++ *
++ * @JTAG_NO_RESET: JTAG run TAP from current state
++ * @JTAG_FORCE_RESET: JTAG force TAP to reset state
++ */
++enum jtag_reset {
++ JTAG_NO_RESET = 0,
++ JTAG_FORCE_RESET = 1,
++};
++
++/**
++ * enum jtag_xfer_type:
++ *
++ * @JTAG_SIR_XFER: SIR transfer
++ * @JTAG_SDR_XFER: SDR transfer
++ */
++enum jtag_xfer_type {
++ JTAG_SIR_XFER = 0,
++ JTAG_SDR_XFER = 1,
++};
++
++/**
++ * enum jtag_xfer_direction:
++ *
++ * @JTAG_READ_XFER: read transfer
++ * @JTAG_WRITE_XFER: write transfer
++ * @JTAG_READ_WRITE_XFER: read & write transfer
++ */
++enum jtag_xfer_direction {
++ JTAG_READ_XFER = 1,
++ JTAG_WRITE_XFER = 2,
++ JTAG_READ_WRITE_XFER = 3,
++};
++
++/**
++ * struct jtag_tap_state - forces JTAG state machine to go into a TAPC
++ * state
++ *
++ * @reset: 0 - run IDLE/PAUSE from current state
++ * 1 - go through TEST_LOGIC/RESET state before IDLE/PAUSE
++ * @end: completion flag
++ * @tck: clock counter
++ *
++ * Structure provide interface to JTAG device for JTAG set state execution.
++ */
++struct jtag_tap_state {
++ __u8 reset;
++ __u8 from;
++ __u8 endstate;
++ __u8 tck;
++};
++
++/**
++ * struct jtag_xfer - jtag xfer:
++ *
++ * @type: transfer type
++ * @direction: xfer direction
++ * @from: xfer current state
++ * @endstate: xfer end state
++ * @padding: xfer padding
++ * @length: xfer bits length
++ * @tdio : xfer data array
++ *
++ * Structure provide interface to JTAG device for JTAG SDR/SIR xfer execution.
++ */
++struct jtag_xfer {
++ __u8 type;
++ __u8 direction;
++ __u8 from;
++ __u8 endstate;
++ __u8 padding;
++ __u32 length;
++ __u64 tdio;
++};
++
++/**
++ * struct bitbang_packet - jtag bitbang array packet:
++ *
++ * @data: JTAG Bitbang struct array pointer(input/output)
++ * @length: array size (input)
++ *
++ * Structure provide interface to JTAG device for JTAG bitbang bundle execution
++ */
++struct bitbang_packet {
++ struct tck_bitbang *data;
++ __u32 length;
++} __attribute__((__packed__));
++
++/**
++ * struct jtag_bitbang - jtag bitbang:
++ *
++ * @tms: JTAG TMS
++ * @tdi: JTAG TDI (input)
++ * @tdo: JTAG TDO (output)
++ *
++ * Structure provide interface to JTAG device for JTAG bitbang execution.
++ */
++struct tck_bitbang {
++ __u8 tms;
++ __u8 tdi;
++ __u8 tdo;
++} __attribute__((__packed__));
++
++/**
++ * struct jtag_mode - jtag mode:
++ *
++ * @feature: 0 - JTAG feature setting selector for JTAG controller HW/SW
++ * 1 - JTAG feature setting selector for controller bus master
++ * mode output (enable / disable).
++ * @mode: (0 - SW / 1 - HW) for JTAG_XFER_MODE feature(0)
++ * (0 - output disable / 1 - output enable) for JTAG_CONTROL_MODE
++ * feature(1)
++ *
++ * Structure provide configuration modes to JTAG device.
++ */
++struct jtag_mode {
++ __u32 feature;
++ __u32 mode;
++};
++
++/* ioctl interface */
++#define __JTAG_IOCTL_MAGIC 0xb2
++
++#define JTAG_SIOCSTATE _IOW(__JTAG_IOCTL_MAGIC, 0, struct jtag_tap_state)
++#define JTAG_SIOCFREQ _IOW(__JTAG_IOCTL_MAGIC, 1, unsigned int)
++#define JTAG_GIOCFREQ _IOR(__JTAG_IOCTL_MAGIC, 2, unsigned int)
++#define JTAG_IOCXFER _IOWR(__JTAG_IOCTL_MAGIC, 3, struct jtag_xfer)
++#define JTAG_GIOCSTATUS _IOWR(__JTAG_IOCTL_MAGIC, 4, enum jtag_tapstate)
++#define JTAG_SIOCMODE _IOW(__JTAG_IOCTL_MAGIC, 5, unsigned int)
++#define JTAG_IOCBITBANG _IOW(__JTAG_IOCTL_MAGIC, 6, unsigned int)
++
++#endif /* __UAPI_LINUX_JTAG_H */
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0053-Add-Aspeed-SoC-24xx-and-25xx-families-JTAG.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0053-Add-Aspeed-SoC-24xx-and-25xx-families-JTAG.patch
new file mode 100644
index 000000000..94722d6c4
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0053-Add-Aspeed-SoC-24xx-and-25xx-families-JTAG.patch
@@ -0,0 +1,1294 @@
+From 817a43d1b1e197e7eff43492599469bbc23bf0fd Mon Sep 17 00:00:00 2001
+From: "Corona, Ernesto" <ernesto.corona@intel.com>
+Date: Mon, 3 Jun 2019 08:22:09 -0800
+Subject: [PATCH v29 2/6] Add Aspeed SoC 24xx and 25xx families JTAG master
+ driver
+
+Driver adds support of Aspeed 2500/2400 series SOC JTAG master controller.
+
+Driver implements the following jtag ops:
+- freq_get;
+- freq_set;
+- status_get;
+- status_set
+- xfer;
+- mode_set;
+- bitbang;
+- enable;
+- disable;
+
+It has been tested on Mellanox system with BMC equipped with
+Aspeed 2520 SoC for programming CPLD devices.
+
+It has also been tested on Intel system using Aspeed 25xx SoC
+for JTAG communication.
+
+Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
+Signed-off-by: Jiri Pirko <jiri@mellanox.com>
+Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com>
+Acked-by: Arnd Bergmann <arnd@arndb.de>
+Acked-by: Philippe Ombredanne <pombredanne@nexb.com>
+Acked-by: Joel Stanley <joel@jms.id.au>
+Cc: Vadim Pasternak <vadimp@mellanox.com>
+Cc: Andrew Jeffery <andrew@aj.id.au>
+Cc: Steven A Filary <steven.a.filary@intel.com>
+Cc: Bryan Hunt <bryan.hunt@intel.com>
+---
+v28->v29
+Comments pointed by Steven Filary <steven.a.filary@intel.com>
+- Expand bitbang function to accept multiples bitbang operations within a
+ single JTAG_IOCBITBANG call. It will receive a buffer with TDI and TMS
+ values and it is expected that driver fills TDO fields with its
+ corresponding output value for every transaction.
+- Always setup JTAG controller to master mode but disable JTAG output when
+ the driver is not in use to allow other HW to own the JTAG bus. Remove SCU
+ register accesses. This register controls the JTAG controller mode
+ (master/slave).
+- Encansulate dev_dgb message into DEBUG_JTAG macros to improve driver's JTAG
+ performace.
+- Add support for multichain. Set tap state and xfer operations now include
+ two tap state arguments: current state and end state.
+
+v27->v28
+Comments pointed by Steven Filary <steven.a.filary@intel.com>
+- Replace JTAG_IOCRUNTEST with JTAG_SIOCSTATE adding support for all TAPC
+ end states in SW mode using a lookup table to navigate across states.
+- Add support for simultaneous READ/WRITE transfers(JTAG_READ_WRITE_XFER).
+- Support for switching JTAG controller mode between slave and master
+ mode.
+- Setup JTAG controller mode to master only when the driver is opened,
+ letting other HW to own the JTAG bus when it isn't in use.
+- Include JTAG bit bang IOCTL for low level JTAG control usage
+ (JTAG_IOCBITBANG).
+- Add debug traces.
+- Add support for register polling (default) due it is 3 times faster than
+ interrupt mode. Define USE_INTERRUPTS macro to enable interrupt usage.
+- Remove unnecessary delays for aspeed_jtag_status_set function. It makes
+ SW mode 4 times faster.
+- Clean data buffer on aspeed_jtag_xfer_sw before tdo writes to avoid data
+ output corruption for read operations in SW mode.
+- Correct register settings for HW mode transfer operations.
+- Propagate ret codes all the way from low level functions up to
+ JTAG_IOCXFER call.
+- Support for partitioned transfers. Single JTAG transfer through
+ multiples JTAG_IOCXFER calls. Now end transmission(scan_end) also
+ evaluates transfer end state.
+
+v26->v27
+Changes made by Oleksandr Shamray <oleksandrs@mellamnox.com>
+- change aspeed_jtag_sw_delay to udelay function in bit-bang operation
+
+v25->v26
+v24->v25
+Comments pointed by Greg KH <gregkh@linuxfoundation.org>
+- reduced debug printouts
+
+v23->v24
+v22->v23
+v21->v22
+Comments pointed by Andy Shevchenko <andy.shevchenko@gmail.com>
+- rearrange ASPEED register defines
+- simplified JTAG divider calculation formula
+- change delay function in bit-bang operation
+- add helper functions for TAP states switching
+- remove unnecessary comments
+- remove redundant debug messages
+- make dines for repetative register bit sets
+- fixed indentation
+- change checks from negative to positive
+- add error check for clk_prepare_enable
+- rework driver alloc/register to devm_ variant
+- Increasing line length up to 85 in order to improve readability
+
+v20->v21
+v19->v20
+Notifications from kbuild test robot <lkp@intel.com>
+- add static declaration to 'aspeed_jtag_init' and
+ 'aspeed_jtag_deinit' functions
+
+v18->v19
+v17->v18
+v16->v17
+v15->v16
+Comments pointed by Joel Stanley <joel.stan@gmail.com>
+- Add reset_control on Jtag init/deinit
+
+v14->v15
+Comments pointed by Joel Stanley <joel.stan@gmail.com>
+- Add ARCH_ASPEED || COMPILE_TEST to Kconfig
+- remove unused offset variable
+- remove "aspeed_jtag" from dev_err and dev_dbg messages
+- change clk_prepare_enable initialisation order
+
+v13->v14
+Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
+- Change style of head block comment from /**/ to //
+
+v12->v13
+Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
+- Change jtag-aspeed.c licence type to
+ SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+ and reorder line with license- add reset descriptions in bndings file
+ in description
+Comments pointed by Kun Yi <kunyi@google.com>
+- Changed capability check for aspeed,ast2400-jtag/ast200-jtag
+
+v11->v12
+Comments pointed by Chip Bilbrey <chip@bilbrey.org>
+- Remove access mode from xfer and idle transactions
+- Add new ioctl JTAG_SIOCMODE for set hw mode
+
+v10->v11
+v9->v10
+V8->v9
+Comments pointed by Arnd Bergmann <arnd@arndb.de>
+- add *data parameter to xfer function prototype
+
+v7->v8
+Comments pointed by Joel Stanley <joel.stan@gmail.com>
+- aspeed_jtag_init replace goto to return;
+- change input variables type from __u32 to u32
+ in functios freq_get, freq_set, status_get
+- change sm_ variables type from char to u8
+- in jatg_init add disable clocks on error case
+- remove release_mem_region on error case
+- remove devm_free_irq on jtag_deinit
+- Fix misspelling Disabe/Disable
+- Change compatible string to ast2400 and ast2000
+
+v6->v7
+Notifications from kbuild test robot <lkp@intel.com>
+- Add include <linux/types.h> to jtag-asapeed.c
+
+v5->v6
+v4->v5
+Comments pointed by Arnd Bergmann <arnd@arndb.de>
+- Added HAS_IOMEM dependence in Kconfig to avoid
+ "undefined reference to `devm_ioremap_resource'" error,
+ because in some arch this not supported
+
+v3->v4
+Comments pointed by Arnd Bergmann <arnd@arndb.de>
+- change transaction pointer tdio type to __u64
+- change internal status type from enum to __u32
+
+v2->v3
+
+v1->v2
+Comments pointed by Greg KH <gregkh@linuxfoundation.org>
+- change license type from GPLv2/BSD to GPLv2
+
+Comments pointed by Neil Armstrong <narmstrong@baylibre.com>
+- Add clk_prepare_enable/clk_disable_unprepare in clock init/deinit
+- Change .compatible to soc-specific compatible names
+ aspeed,aspeed4000-jtag/aspeed5000-jtag
+- Added dt-bindings
+
+Comments pointed by Arnd Bergmann <arnd@arndb.de>
+- Reorder functions and removed the forward declarations
+- Add static const qualifier to state machine states transitions
+- Change .compatible to soc-specific compatible names
+ aspeed,aspeed4000-jtag/aspeed5000-jtag
+- Add dt-bindings
+
+Comments pointed by Randy Dunlap <rdunlap@infradead.org>
+- Change module name jtag-aspeed in description in Kconfig
+
+Comments pointed by kbuild test robot <lkp@intel.com>
+- Remove invalid include <asm/mach-types.h>
+- add resource_size instead of calculation
+---
+ drivers/jtag/Kconfig | 14 +
+ drivers/jtag/Makefile | 1 +
+ drivers/jtag/jtag-aspeed.c | 1050 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 1065 insertions(+)
+ create mode 100644 drivers/jtag/jtag-aspeed.c
+
+diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig
+index 47771fc..0cc163f 100644
+--- a/drivers/jtag/Kconfig
++++ b/drivers/jtag/Kconfig
+@@ -15,3 +15,17 @@ menuconfig JTAG
+
+ To compile this driver as a module, choose M here: the module will
+ be called jtag.
++
++menuconfig JTAG_ASPEED
++ tristate "Aspeed SoC JTAG controller support"
++ depends on JTAG && HAS_IOMEM
++ depends on ARCH_ASPEED || COMPILE_TEST
++ help
++ This provides a support for Aspeed JTAG device, equipped on
++ Aspeed SoC 24xx and 25xx families. Drivers allows programming
++ of hardware devices, connected to SoC through the JTAG interface.
++
++ If you want this support, you should say Y here.
++
++ To compile this driver as a module, choose M here: the module will
++ be called jtag-aspeed.
+diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile
+index af37493..04a855e 100644
+--- a/drivers/jtag/Makefile
++++ b/drivers/jtag/Makefile
+@@ -1 +1,2 @@
+ obj-$(CONFIG_JTAG) += jtag.o
++obj-$(CONFIG_JTAG_ASPEED) += jtag-aspeed.o
+diff --git a/drivers/jtag/jtag-aspeed.c b/drivers/jtag/jtag-aspeed.c
+new file mode 100644
+index 0000000..1d41a66
+--- /dev/null
++++ b/drivers/jtag/jtag-aspeed.c
+@@ -0,0 +1,1050 @@
++// SPDX-License-Identifier: GPL-2.0
++// Copyright (c) 2018 Mellanox Technologies. All rights reserved.
++// Copyright (c) 2018 Oleksandr Shamray <oleksandrs@mellanox.com>
++// Copyright (c) 2019 Intel Corporation
++
++#include <linux/clk.h>
++#include <linux/device.h>
++#include <linux/interrupt.h>
++#include <linux/jtag.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of_address.h>
++#include <linux/platform_device.h>
++#include <linux/reset.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/delay.h>
++#include <uapi/linux/jtag.h>
++
++#define ASPEED_SCU_RESET_JTAG BIT(22)
++
++#define ASPEED_JTAG_DATA 0x00
++#define ASPEED_JTAG_INST 0x04
++#define ASPEED_JTAG_CTRL 0x08
++#define ASPEED_JTAG_ISR 0x0C
++#define ASPEED_JTAG_SW 0x10
++#define ASPEED_JTAG_TCK 0x14
++#define ASPEED_JTAG_EC 0x18
++
++#define ASPEED_JTAG_DATA_MSB 0x01
++#define ASPEED_JTAG_DATA_CHUNK_SIZE 0x20
++
++/* ASPEED_JTAG_CTRL: Engine Control */
++#define ASPEED_JTAG_CTL_ENG_EN BIT(31)
++#define ASPEED_JTAG_CTL_ENG_OUT_EN BIT(30)
++#define ASPEED_JTAG_CTL_FORCE_TMS BIT(29)
++#define ASPEED_JTAG_CTL_IR_UPDATE BIT(26)
++#define ASPEED_JTAG_CTL_INST_LEN(x) ((x) << 20)
++#define ASPEED_JTAG_CTL_LASPEED_INST BIT(17)
++#define ASPEED_JTAG_CTL_INST_EN BIT(16)
++#define ASPEED_JTAG_CTL_DR_UPDATE BIT(10)
++#define ASPEED_JTAG_CTL_DATA_LEN(x) ((x) << 4)
++#define ASPEED_JTAG_CTL_LASPEED_DATA BIT(1)
++#define ASPEED_JTAG_CTL_DATA_EN BIT(0)
++
++/* ASPEED_JTAG_ISR : Interrupt status and enable */
++#define ASPEED_JTAG_ISR_INST_PAUSE BIT(19)
++#define ASPEED_JTAG_ISR_INST_COMPLETE BIT(18)
++#define ASPEED_JTAG_ISR_DATA_PAUSE BIT(17)
++#define ASPEED_JTAG_ISR_DATA_COMPLETE BIT(16)
++#define ASPEED_JTAG_ISR_INST_PAUSE_EN BIT(3)
++#define ASPEED_JTAG_ISR_INST_COMPLETE_EN BIT(2)
++#define ASPEED_JTAG_ISR_DATA_PAUSE_EN BIT(1)
++#define ASPEED_JTAG_ISR_DATA_COMPLETE_EN BIT(0)
++#define ASPEED_JTAG_ISR_INT_EN_MASK GENMASK(3, 0)
++#define ASPEED_JTAG_ISR_INT_MASK GENMASK(19, 16)
++
++/* ASPEED_JTAG_SW : Software Mode and Status */
++#define ASPEED_JTAG_SW_MODE_EN BIT(19)
++#define ASPEED_JTAG_SW_MODE_TCK BIT(18)
++#define ASPEED_JTAG_SW_MODE_TMS BIT(17)
++#define ASPEED_JTAG_SW_MODE_TDIO BIT(16)
++
++/* ASPEED_JTAG_TCK : TCK Control */
++#define ASPEED_JTAG_TCK_DIVISOR_MASK GENMASK(10, 0)
++#define ASPEED_JTAG_TCK_GET_DIV(x) ((x) & ASPEED_JTAG_TCK_DIVISOR_MASK)
++
++/* ASPEED_JTAG_EC : Controller set for go to IDLE */
++#define ASPEED_JTAG_EC_GO_IDLE BIT(0)
++
++#define ASPEED_JTAG_IOUT_LEN(len) \
++ (ASPEED_JTAG_CTL_ENG_EN | \
++ ASPEED_JTAG_CTL_ENG_OUT_EN | \
++ ASPEED_JTAG_CTL_INST_LEN(len))
++
++#define ASPEED_JTAG_DOUT_LEN(len) \
++ (ASPEED_JTAG_CTL_ENG_EN | \
++ ASPEED_JTAG_CTL_ENG_OUT_EN | \
++ ASPEED_JTAG_CTL_DATA_LEN(len))
++
++#define ASPEED_JTAG_SW_TDIO (ASPEED_JTAG_SW_MODE_EN | ASPEED_JTAG_SW_MODE_TDIO)
++
++#define ASPEED_JTAG_GET_TDI(direction, byte) \
++ (((direction) & JTAG_WRITE_XFER) ? byte : UINT_MAX)
++
++#define ASPEED_JTAG_TCK_WAIT 10
++#define ASPEED_JTAG_RESET_CNTR 10
++#define WAIT_ITERATIONS 75
++
++/*#define USE_INTERRUPTS*/
++/*#define DEBUG_JTAG*/
++
++static const char * const regnames[] = {
++ [ASPEED_JTAG_DATA] = "ASPEED_JTAG_DATA",
++ [ASPEED_JTAG_INST] = "ASPEED_JTAG_INST",
++ [ASPEED_JTAG_CTRL] = "ASPEED_JTAG_CTRL",
++ [ASPEED_JTAG_ISR] = "ASPEED_JTAG_ISR",
++ [ASPEED_JTAG_SW] = "ASPEED_JTAG_SW",
++ [ASPEED_JTAG_TCK] = "ASPEED_JTAG_TCK",
++ [ASPEED_JTAG_EC] = "ASPEED_JTAG_EC",
++};
++
++#define ASPEED_JTAG_NAME "jtag-aspeed"
++
++struct aspeed_jtag {
++ void __iomem *reg_base;
++ struct device *dev;
++ struct clk *pclk;
++ enum jtag_tapstate status;
++ int irq;
++ struct reset_control *rst;
++ u32 flag;
++ wait_queue_head_t jtag_wq;
++ u32 mode;
++};
++
++/*
++ * This structure represents a TMS cycle, as expressed in a set of bits and a
++ * count of bits (note: there are no start->end state transitions that require
++ * more than 1 byte of TMS cycles)
++ */
++struct tms_cycle {
++ unsigned char tmsbits;
++ unsigned char count;
++};
++
++/*
++ * This is the complete set TMS cycles for going from any TAP state to any
++ * other TAP state, following a "shortest path" rule.
++ */
++static const struct tms_cycle _tms_cycle_lookup[][16] = {
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* TLR */{{0x00, 0}, {0x00, 1}, {0x02, 2}, {0x02, 3}, {0x02, 4}, {0x0a, 4},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x0a, 5}, {0x2a, 6}, {0x1a, 5}, {0x06, 3}, {0x06, 4}, {0x06, 5},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x16, 5}, {0x16, 6}, {0x56, 7}, {0x36, 6} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* RTI */{{0x07, 3}, {0x00, 0}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x05, 4}, {0x15, 5}, {0x0d, 4}, {0x03, 2}, {0x03, 3}, {0x03, 4},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* SelDR*/{{0x03, 2}, {0x03, 3}, {0x00, 0}, {0x00, 1}, {0x00, 2}, {0x02, 2},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x02, 3}, {0x0a, 4}, {0x06, 3}, {0x01, 1}, {0x01, 2}, {0x01, 3},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x05, 3}, {0x05, 4}, {0x15, 5}, {0x0d, 4} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* CapDR*/{{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x00, 0}, {0x00, 1}, {0x01, 1},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x01, 2}, {0x05, 3}, {0x03, 2}, {0x0f, 4}, {0x0f, 5}, {0x0f, 6},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, {0x6f, 7} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* SDR */{{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x00, 0}, {0x01, 1},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x01, 2}, {0x05, 3}, {0x03, 2}, {0x0f, 4}, {0x0f, 5}, {0x0f, 6},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, {0x6f, 7} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* Ex1DR*/{{0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x02, 3}, {0x00, 0},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x00, 1}, {0x02, 2}, {0x01, 1}, {0x07, 3}, {0x07, 4}, {0x07, 5},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* PDR */{{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x01, 2}, {0x05, 3},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x00, 0}, {0x01, 1}, {0x03, 2}, {0x0f, 4}, {0x0f, 5}, {0x0f, 6},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, {0x6f, 7} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* Ex2DR*/{{0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x00, 1}, {0x02, 2},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x02, 3}, {0x00, 0}, {0x01, 1}, {0x07, 3}, {0x07, 4}, {0x07, 5},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* UpdDR*/{{0x07, 3}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x05, 4}, {0x15, 5}, {0x00, 0}, {0x03, 2}, {0x03, 3}, {0x03, 4},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* SelIR*/{{0x01, 1}, {0x01, 2}, {0x05, 3}, {0x05, 4}, {0x05, 5}, {0x15, 5},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x15, 6}, {0x55, 7}, {0x35, 6}, {0x00, 0}, {0x00, 1}, {0x00, 2},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x02, 2}, {0x02, 3}, {0x0a, 4}, {0x06, 3} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* CapIR*/{{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, {0x00, 0}, {0x00, 1},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* SIR */{{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, {0x0f, 5}, {0x00, 0},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* Ex1IR*/{{0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x0b, 5}, {0x2b, 6}, {0x1b, 5}, {0x07, 3}, {0x07, 4}, {0x02, 3},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x00, 0}, {0x00, 1}, {0x02, 2}, {0x01, 1} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* PIR */{{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, {0x0f, 5}, {0x01, 2},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x05, 3}, {0x00, 0}, {0x01, 1}, {0x03, 2} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* Ex2IR*/{{0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x0b, 5}, {0x2b, 6}, {0x1b, 5}, {0x07, 3}, {0x07, 4}, {0x00, 1},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x02, 2}, {0x02, 3}, {0x00, 0}, {0x01, 1} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* UpdIR*/{{0x07, 3}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x05, 4}, {0x15, 5}, {0x0d, 4}, {0x03, 2}, {0x03, 3}, {0x03, 4},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x00, 0} },
++};
++
++#ifdef DEBUG_JTAG
++static char *end_status_str[] = {
++ "tlr", "idle", "selDR", "capDR", "sDR", "ex1DR", "pDR", "ex2DR",
++ "updDR", "selIR", "capIR", "sIR", "ex1IR", "pIR", "ex2IR", "updIR"
++};
++#endif
++
++static u32 aspeed_jtag_read(struct aspeed_jtag *aspeed_jtag, u32 reg)
++{
++ u32 val = readl(aspeed_jtag->reg_base + reg);
++
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev, "read:%s val = 0x%08x\n", regnames[reg], val);
++#endif
++ return val;
++}
++
++static void
++aspeed_jtag_write(struct aspeed_jtag *aspeed_jtag, u32 val, u32 reg)
++{
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev, "write:%s val = 0x%08x\n",
++ regnames[reg], val);
++#endif
++ writel(val, aspeed_jtag->reg_base + reg);
++}
++
++static int aspeed_jtag_freq_set(struct jtag *jtag, u32 freq)
++{
++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
++ unsigned long apb_frq;
++ u32 tck_val;
++ u16 div;
++
++ apb_frq = clk_get_rate(aspeed_jtag->pclk);
++ if (!apb_frq)
++ return -ENOTSUPP;
++
++ div = (apb_frq - 1) / freq;
++ tck_val = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_TCK);
++ aspeed_jtag_write(aspeed_jtag,
++ (tck_val & ~ASPEED_JTAG_TCK_DIVISOR_MASK) | div,
++ ASPEED_JTAG_TCK);
++ return 0;
++}
++
++static int aspeed_jtag_freq_get(struct jtag *jtag, u32 *frq)
++{
++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
++ u32 pclk;
++ u32 tck;
++
++ pclk = clk_get_rate(aspeed_jtag->pclk);
++ tck = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_TCK);
++ *frq = pclk / (ASPEED_JTAG_TCK_GET_DIV(tck) + 1);
++
++ return 0;
++}
++
++static inline void aspeed_jtag_output_disable(struct aspeed_jtag *aspeed_jtag)
++{
++ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_CTRL);
++}
++
++static inline void aspeed_jtag_master(struct aspeed_jtag *aspeed_jtag)
++{
++ aspeed_jtag_write(aspeed_jtag, (ASPEED_JTAG_CTL_ENG_EN |
++ ASPEED_JTAG_CTL_ENG_OUT_EN),
++ ASPEED_JTAG_CTRL);
++
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
++ ASPEED_JTAG_SW_MODE_TDIO,
++ ASPEED_JTAG_SW);
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_ISR_INST_PAUSE |
++ ASPEED_JTAG_ISR_INST_COMPLETE |
++ ASPEED_JTAG_ISR_DATA_PAUSE |
++ ASPEED_JTAG_ISR_DATA_COMPLETE |
++ ASPEED_JTAG_ISR_INST_PAUSE_EN |
++ ASPEED_JTAG_ISR_INST_COMPLETE_EN |
++ ASPEED_JTAG_ISR_DATA_PAUSE_EN |
++ ASPEED_JTAG_ISR_DATA_COMPLETE_EN,
++ ASPEED_JTAG_ISR); /* Enable Interrupt */
++}
++
++static int aspeed_jtag_mode_set(struct jtag *jtag, struct jtag_mode *jtag_mode)
++{
++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
++
++ switch (jtag_mode->feature) {
++ case JTAG_XFER_MODE:
++ aspeed_jtag->mode = jtag_mode->mode;
++ break;
++ case JTAG_CONTROL_MODE:
++ if (jtag_mode->mode == JTAG_MASTER_OUTPUT_DISABLE)
++ aspeed_jtag_output_disable(aspeed_jtag);
++ else if (jtag_mode->mode == JTAG_MASTER_MODE)
++ aspeed_jtag_master(aspeed_jtag);
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static char aspeed_jtag_tck_cycle(struct aspeed_jtag *aspeed_jtag,
++ u8 tms, u8 tdi)
++{
++ char tdo = 0;
++
++ /* TCK = 0 */
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
++ (tms * ASPEED_JTAG_SW_MODE_TMS) |
++ (tdi * ASPEED_JTAG_SW_MODE_TDIO), ASPEED_JTAG_SW);
++
++ aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_SW);
++
++ /* TCK = 1 */
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
++ ASPEED_JTAG_SW_MODE_TCK |
++ (tms * ASPEED_JTAG_SW_MODE_TMS) |
++ (tdi * ASPEED_JTAG_SW_MODE_TDIO), ASPEED_JTAG_SW);
++
++ if (aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_SW) &
++ ASPEED_JTAG_SW_MODE_TDIO)
++ tdo = 1;
++
++ return tdo;
++}
++
++static int aspeed_jtag_bitbang(struct jtag *jtag,
++ struct bitbang_packet *bitbang,
++ struct tck_bitbang *bitbang_data)
++{
++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
++ int i = 0;
++
++ for (i = 0; i < bitbang->length; i++) {
++ bitbang_data[i].tdo =
++ aspeed_jtag_tck_cycle(aspeed_jtag, bitbang_data[i].tms,
++ bitbang_data[i].tdi);
++ }
++ return 0;
++}
++
++static int aspeed_jtag_wait_instruction_pause(struct aspeed_jtag *aspeed_jtag)
++{
++ int res = 0;
++#ifdef USE_INTERRUPTS
++ res = wait_event_interruptible(aspeed_jtag->jtag_wq,
++ aspeed_jtag->flag &
++ ASPEED_JTAG_ISR_INST_PAUSE);
++ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_INST_PAUSE;
++#else
++ u32 status = 0;
++ u32 iterations = 0;
++
++ while ((status & ASPEED_JTAG_ISR_INST_PAUSE) == 0) {
++ status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR);
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev, "%s = 0x%08x\n", __func__, status);
++#endif
++ iterations++;
++ if (iterations > WAIT_ITERATIONS) {
++ dev_err(aspeed_jtag->dev,
++ "aspeed_jtag driver timed out waiting for instruction pause complete\n");
++ res = -EFAULT;
++ break;
++ }
++ if ((status & ASPEED_JTAG_ISR_DATA_COMPLETE) == 0) {
++ if (iterations % 25 == 0)
++ usleep_range(1, 5);
++ else
++ udelay(1);
++ }
++ }
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_ISR_INST_PAUSE |
++ (status & 0xf),
++ ASPEED_JTAG_ISR);
++#endif
++ return res;
++}
++
++static int
++aspeed_jtag_wait_instruction_complete(struct aspeed_jtag *aspeed_jtag)
++{
++ int res = 0;
++#ifdef USE_INTERRUPTS
++ res = wait_event_interruptible(aspeed_jtag->jtag_wq,
++ aspeed_jtag->flag &
++ ASPEED_JTAG_ISR_INST_COMPLETE);
++ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_INST_COMPLETE;
++#else
++ u32 status = 0;
++ u32 iterations = 0;
++
++ while ((status & ASPEED_JTAG_ISR_INST_COMPLETE) == 0) {
++ status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR);
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev, "%s = 0x%08x\n", __func__, status);
++#endif
++ iterations++;
++ if (iterations > WAIT_ITERATIONS) {
++ dev_err(aspeed_jtag->dev,
++ "aspeed_jtag driver timed out waiting for instruction complete\n");
++ res = -EFAULT;
++ break;
++ }
++ if ((status & ASPEED_JTAG_ISR_DATA_COMPLETE) == 0) {
++ if (iterations % 25 == 0)
++ usleep_range(1, 5);
++ else
++ udelay(1);
++ }
++ }
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_ISR_INST_COMPLETE |
++ (status & 0xf),
++ ASPEED_JTAG_ISR);
++#endif
++ return res;
++}
++
++static int
++aspeed_jtag_wait_data_pause_complete(struct aspeed_jtag *aspeed_jtag)
++{
++ int res = 0;
++#ifdef USE_INTERRUPTS
++ res = wait_event_interruptible(aspeed_jtag->jtag_wq,
++ aspeed_jtag->flag &
++ ASPEED_JTAG_ISR_DATA_PAUSE);
++ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_DATA_PAUSE;
++#else
++ u32 status = 0;
++ u32 iterations = 0;
++
++ while ((status & ASPEED_JTAG_ISR_DATA_PAUSE) == 0) {
++ status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR);
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev, "%s = 0x%08x\n", __func__, status);
++#endif
++ iterations++;
++ if (iterations > WAIT_ITERATIONS) {
++ dev_err(aspeed_jtag->dev,
++ "aspeed_jtag driver timed out waiting for data pause complete\n");
++ res = -EFAULT;
++ break;
++ }
++ if ((status & ASPEED_JTAG_ISR_DATA_COMPLETE) == 0) {
++ if (iterations % 25 == 0)
++ usleep_range(1, 5);
++ else
++ udelay(1);
++ }
++ }
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_ISR_DATA_PAUSE |
++ (status & 0xf), ASPEED_JTAG_ISR);
++#endif
++ return res;
++}
++
++static int aspeed_jtag_wait_data_complete(struct aspeed_jtag *aspeed_jtag)
++{
++ int res = 0;
++#ifdef USE_INTERRUPTS
++ res = wait_event_interruptible(aspeed_jtag->jtag_wq,
++ aspeed_jtag->flag &
++ ASPEED_JTAG_ISR_DATA_COMPLETE);
++ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_DATA_COMPLETE;
++#else
++ u32 status = 0;
++ u32 iterations = 0;
++
++ while ((status & ASPEED_JTAG_ISR_DATA_COMPLETE) == 0) {
++ status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR);
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev, "%s = 0x%08x\n", __func__, status);
++#endif
++ iterations++;
++ if (iterations > WAIT_ITERATIONS) {
++ dev_err(aspeed_jtag->dev,
++ "ast_jtag driver timed out waiting for data complete\n");
++ res = -EFAULT;
++ break;
++ }
++ if ((status & ASPEED_JTAG_ISR_DATA_COMPLETE) == 0) {
++ if (iterations % 25 == 0)
++ usleep_range(1, 5);
++ else
++ udelay(1);
++ }
++ }
++ aspeed_jtag_write(aspeed_jtag,
++ ASPEED_JTAG_ISR_DATA_COMPLETE | (status & 0xf),
++ ASPEED_JTAG_ISR);
++#endif
++ return res;
++}
++
++static void aspeed_jtag_set_tap_state(struct aspeed_jtag *aspeed_jtag,
++ enum jtag_tapstate from_state,
++ enum jtag_tapstate end_state)
++{
++ int i = 0;
++ enum jtag_tapstate from, to;
++
++ from = from_state;
++ to = end_state;
++
++ if (from == JTAG_STATE_CURRENT)
++ from = aspeed_jtag->status;
++
++ for (i = 0; i < _tms_cycle_lookup[from][to].count; i++)
++ aspeed_jtag_tck_cycle(aspeed_jtag,
++ ((_tms_cycle_lookup[from][to].tmsbits >> i) & 0x1), 0);
++ aspeed_jtag->status = end_state;
++}
++
++static void aspeed_jtag_set_tap_state_sw(struct aspeed_jtag *aspeed_jtag,
++ struct jtag_tap_state *tapstate)
++{
++ /* SW mode from curent tap state -> to end_state */
++ if (tapstate->reset) {
++ int i = 0;
++
++ for (i = 0; i < ASPEED_JTAG_RESET_CNTR; i++)
++ aspeed_jtag_tck_cycle(aspeed_jtag, 1, 0);
++ aspeed_jtag->status = JTAG_STATE_TLRESET;
++ }
++
++ aspeed_jtag_set_tap_state(aspeed_jtag, tapstate->from,
++ tapstate->endstate);
++}
++
++static int aspeed_jtag_status_set(struct jtag *jtag,
++ struct jtag_tap_state *tapstate)
++{
++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
++
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev, "Set TAP state: %s\n",
++ end_status_str[tapstate->endstate]);
++#endif
++
++ if (!(aspeed_jtag->mode & JTAG_XFER_HW_MODE)) {
++ aspeed_jtag_set_tap_state_sw(aspeed_jtag, tapstate);
++ return 0;
++ }
++
++ /* x TMS high + 1 TMS low */
++ if (tapstate->reset) {
++ /* Disable sw mode */
++ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_SW);
++ mdelay(1);
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_CTL_ENG_EN |
++ ASPEED_JTAG_CTL_ENG_OUT_EN |
++ ASPEED_JTAG_CTL_FORCE_TMS, ASPEED_JTAG_CTRL);
++ mdelay(1);
++ aspeed_jtag_write(aspeed_jtag,
++ ASPEED_JTAG_SW_TDIO, ASPEED_JTAG_SW);
++ aspeed_jtag->status = JTAG_STATE_TLRESET;
++ }
++
++ return 0;
++}
++
++static void aspeed_jtag_xfer_sw(struct aspeed_jtag *aspeed_jtag,
++ struct jtag_xfer *xfer, u32 *data)
++{
++ unsigned long remain_xfer = xfer->length;
++ unsigned long shift_bits = 0;
++ unsigned long index = 0;
++ unsigned long tdi;
++ char tdo;
++
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev, "SW JTAG SHIFT %s, length = %d\n",
++ (xfer->type == JTAG_SIR_XFER) ? "IR" : "DR", xfer->length);
++#endif
++
++ if (xfer->type == JTAG_SIR_XFER)
++ aspeed_jtag_set_tap_state(aspeed_jtag, xfer->from,
++ JTAG_STATE_SHIFTIR);
++ else
++ aspeed_jtag_set_tap_state(aspeed_jtag, xfer->from,
++ JTAG_STATE_SHIFTDR);
++
++ tdi = ASPEED_JTAG_GET_TDI(xfer->direction, data[index]);
++ data[index] = 0;
++ while (remain_xfer > 1) {
++ tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 0,
++ tdi & ASPEED_JTAG_DATA_MSB);
++ data[index] |= tdo << (shift_bits %
++ ASPEED_JTAG_DATA_CHUNK_SIZE);
++ tdi >>= 1;
++ shift_bits++;
++ remain_xfer--;
++
++ if (shift_bits % ASPEED_JTAG_DATA_CHUNK_SIZE == 0) {
++ tdo = 0;
++ index++;
++ tdi = ASPEED_JTAG_GET_TDI(xfer->direction, data[index]);
++ data[index] = 0;
++ }
++ }
++
++ if ((xfer->endstate == (xfer->type == JTAG_SIR_XFER ?
++ JTAG_STATE_SHIFTIR : JTAG_STATE_SHIFTDR))) {
++ /* Stay in Shift IR/DR*/
++ tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 0,
++ tdi & ASPEED_JTAG_DATA_MSB);
++ data[index] |= tdo << (shift_bits %
++ ASPEED_JTAG_DATA_CHUNK_SIZE);
++ } else {
++ /* Goto end state */
++ tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 1,
++ tdi & ASPEED_JTAG_DATA_MSB);
++ data[index] |= tdo << (shift_bits %
++ ASPEED_JTAG_DATA_CHUNK_SIZE);
++ aspeed_jtag->status = (xfer->type == JTAG_SIR_XFER) ?
++ JTAG_STATE_EXIT1IR : JTAG_STATE_EXIT1DR;
++ aspeed_jtag_set_tap_state(aspeed_jtag, aspeed_jtag->status,
++ xfer->endstate);
++ }
++}
++
++static int aspeed_jtag_xfer_push_data(struct aspeed_jtag *aspeed_jtag,
++ enum jtag_xfer_type type, u32 bits_len)
++{
++ int res = 0;
++
++ if (type == JTAG_SIR_XFER) {
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_IOUT_LEN(bits_len),
++ ASPEED_JTAG_CTRL);
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_IOUT_LEN(bits_len) |
++ ASPEED_JTAG_CTL_INST_EN, ASPEED_JTAG_CTRL);
++ res = aspeed_jtag_wait_instruction_pause(aspeed_jtag);
++ } else {
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_DOUT_LEN(bits_len),
++ ASPEED_JTAG_CTRL);
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_DOUT_LEN(bits_len) |
++ ASPEED_JTAG_CTL_DATA_EN, ASPEED_JTAG_CTRL);
++ res = aspeed_jtag_wait_data_pause_complete(aspeed_jtag);
++ }
++ return res;
++}
++
++static int aspeed_jtag_xfer_push_data_last(struct aspeed_jtag *aspeed_jtag,
++ enum jtag_xfer_type type,
++ u32 shift_bits)
++{
++ int res = 0;
++
++ if (type == JTAG_SIR_XFER) {
++ aspeed_jtag_write(aspeed_jtag,
++ ASPEED_JTAG_IOUT_LEN(shift_bits) |
++ ASPEED_JTAG_CTL_LASPEED_INST,
++ ASPEED_JTAG_CTRL);
++ aspeed_jtag_write(aspeed_jtag,
++ ASPEED_JTAG_IOUT_LEN(shift_bits) |
++ ASPEED_JTAG_CTL_LASPEED_INST |
++ ASPEED_JTAG_CTL_INST_EN,
++ ASPEED_JTAG_CTRL);
++ res = aspeed_jtag_wait_instruction_complete(aspeed_jtag);
++ } else {
++ aspeed_jtag_write(aspeed_jtag,
++ ASPEED_JTAG_DOUT_LEN(shift_bits) |
++ ASPEED_JTAG_CTL_LASPEED_DATA,
++ ASPEED_JTAG_CTRL);
++ aspeed_jtag_write(aspeed_jtag,
++ ASPEED_JTAG_DOUT_LEN(shift_bits) |
++ ASPEED_JTAG_CTL_LASPEED_DATA |
++ ASPEED_JTAG_CTL_DATA_EN,
++ ASPEED_JTAG_CTRL);
++ res = aspeed_jtag_wait_data_complete(aspeed_jtag);
++ }
++ return res;
++}
++
++static int aspeed_jtag_xfer_hw(struct aspeed_jtag *aspeed_jtag,
++ struct jtag_xfer *xfer, u32 *data)
++{
++ unsigned long remain_xfer = xfer->length;
++ unsigned long index = 0;
++ char shift_bits;
++ u32 data_reg;
++ u32 scan_end;
++
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev, "HW JTAG SHIFT %s, length = %d\n",
++ (xfer->type == JTAG_SIR_XFER) ? "IR" : "DR", xfer->length);
++#endif
++ data_reg = xfer->type == JTAG_SIR_XFER ?
++ ASPEED_JTAG_INST : ASPEED_JTAG_DATA;
++ if (xfer->endstate == JTAG_STATE_SHIFTIR ||
++ xfer->endstate == JTAG_STATE_SHIFTDR ||
++ xfer->endstate == JTAG_STATE_PAUSEIR ||
++ xfer->endstate == JTAG_STATE_PAUSEDR) {
++ scan_end = 0;
++ } else {
++ scan_end = 1;
++ }
++
++ while (remain_xfer) {
++ if (xfer->direction & JTAG_WRITE_XFER)
++ aspeed_jtag_write(aspeed_jtag, data[index], data_reg);
++ else
++ aspeed_jtag_write(aspeed_jtag, 0, data_reg);
++
++ if (remain_xfer > ASPEED_JTAG_DATA_CHUNK_SIZE) {
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev,
++ "Chunk len=%d chunk_size=%d remain_xfer=%lu\n",
++ xfer->length, ASPEED_JTAG_DATA_CHUNK_SIZE,
++ remain_xfer);
++#endif
++ shift_bits = ASPEED_JTAG_DATA_CHUNK_SIZE;
++
++ /*
++ * Transmit bytes that were not equals to column length
++ * and after the transfer go to Pause IR/DR.
++ */
++ if (aspeed_jtag_xfer_push_data(aspeed_jtag, xfer->type,
++ shift_bits) != 0) {
++ return -EFAULT;
++ }
++ } else {
++ /*
++ * Read bytes equals to column length
++ */
++ shift_bits = remain_xfer;
++ if (scan_end) {
++ /*
++ * If this data is the end of the transmission
++ * send remaining bits and go to endstate
++ */
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev,
++ "Last len=%d chunk_size=%d remain_xfer=%lu\n",
++ xfer->length,
++ ASPEED_JTAG_DATA_CHUNK_SIZE,
++ remain_xfer);
++#endif
++ if (aspeed_jtag_xfer_push_data_last(
++ aspeed_jtag,
++ xfer->type,
++ shift_bits) != 0) {
++ return -EFAULT;
++ }
++ } else {
++ /*
++ * If transmission is waiting for additional
++ * data send remaining bits and then go to
++ * Pause IR/DR.
++ */
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev,
++ "Tail len=%d chunk_size=%d remain_xfer=%lu\n",
++ xfer->length,
++ ASPEED_JTAG_DATA_CHUNK_SIZE,
++ remain_xfer);
++#endif
++ if (aspeed_jtag_xfer_push_data(aspeed_jtag,
++ xfer->type,
++ shift_bits)
++ != 0) {
++ return -EFAULT;
++ }
++ }
++ }
++
++ if (xfer->direction & JTAG_READ_XFER) {
++ if (shift_bits < ASPEED_JTAG_DATA_CHUNK_SIZE) {
++ data[index] = aspeed_jtag_read(aspeed_jtag,
++ data_reg);
++
++ data[index] >>= ASPEED_JTAG_DATA_CHUNK_SIZE -
++ shift_bits;
++ } else {
++ data[index] = aspeed_jtag_read(aspeed_jtag,
++ data_reg);
++ }
++ }
++
++ remain_xfer = remain_xfer - shift_bits;
++ index++;
++ }
++ return 0;
++}
++
++static int aspeed_jtag_xfer(struct jtag *jtag, struct jtag_xfer *xfer,
++ u8 *xfer_data)
++{
++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
++
++ if (!(aspeed_jtag->mode & JTAG_XFER_HW_MODE)) {
++ /* SW mode */
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_TDIO,
++ ASPEED_JTAG_SW);
++
++ aspeed_jtag_xfer_sw(aspeed_jtag, xfer, (u32 *)xfer_data);
++ } else {
++ /* HW mode */
++ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_SW);
++ if (aspeed_jtag_xfer_hw(aspeed_jtag, xfer,
++ (u32 *)xfer_data) != 0)
++ return -EFAULT;
++ }
++
++ aspeed_jtag->status = xfer->endstate;
++ return 0;
++}
++
++static int aspeed_jtag_status_get(struct jtag *jtag, u32 *status)
++{
++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
++
++ *status = aspeed_jtag->status;
++ return 0;
++}
++
++#ifdef USE_INTERRUPTS
++static irqreturn_t aspeed_jtag_interrupt(s32 this_irq, void *dev_id)
++{
++ struct aspeed_jtag *aspeed_jtag = dev_id;
++ irqreturn_t ret = IRQ_HANDLED;
++ u32 status;
++
++ status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR);
++
++ if (status & ASPEED_JTAG_ISR_INT_MASK) {
++ aspeed_jtag_write(aspeed_jtag,
++ (status & ASPEED_JTAG_ISR_INT_MASK)
++ | (status & ASPEED_JTAG_ISR_INT_EN_MASK),
++ ASPEED_JTAG_ISR);
++ aspeed_jtag->flag |= status & ASPEED_JTAG_ISR_INT_MASK;
++ }
++
++ if (aspeed_jtag->flag) {
++ wake_up_interruptible(&aspeed_jtag->jtag_wq);
++ ret = IRQ_HANDLED;
++ } else {
++ dev_err(aspeed_jtag->dev, "irq status:%x\n",
++ status);
++ ret = IRQ_NONE;
++ }
++ return ret;
++}
++#endif
++
++static int aspeed_jtag_enable(struct jtag *jtag)
++{
++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
++
++ aspeed_jtag_master(aspeed_jtag);
++ return 0;
++}
++
++static int aspeed_jtag_disable(struct jtag *jtag)
++{
++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
++
++ aspeed_jtag_output_disable(aspeed_jtag);
++ return 0;
++}
++
++static int aspeed_jtag_init(struct platform_device *pdev,
++ struct aspeed_jtag *aspeed_jtag)
++{
++ struct resource *res;
++#ifdef USE_INTERRUPTS
++ int err;
++#endif
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ aspeed_jtag->reg_base = devm_ioremap_resource(aspeed_jtag->dev, res);
++ if (IS_ERR(aspeed_jtag->reg_base))
++ return -ENOMEM;
++
++ aspeed_jtag->pclk = devm_clk_get(aspeed_jtag->dev, NULL);
++ if (IS_ERR(aspeed_jtag->pclk)) {
++ dev_err(aspeed_jtag->dev, "devm_clk_get failed\n");
++ return PTR_ERR(aspeed_jtag->pclk);
++ }
++
++#ifdef USE_INTERRUPTS
++ aspeed_jtag->irq = platform_get_irq(pdev, 0);
++ if (aspeed_jtag->irq < 0) {
++ dev_err(aspeed_jtag->dev, "no irq specified\n");
++ return -ENOENT;
++ }
++#endif
++
++ if (clk_prepare_enable(aspeed_jtag->pclk)) {
++ dev_err(aspeed_jtag->dev, "no irq specified\n");
++ return -ENOENT;
++ }
++
++ aspeed_jtag->rst = devm_reset_control_get_shared(&pdev->dev, NULL);
++ if (IS_ERR(aspeed_jtag->rst)) {
++ dev_err(aspeed_jtag->dev,
++ "missing or invalid reset controller device tree entry");
++ return PTR_ERR(aspeed_jtag->rst);
++ }
++ reset_control_deassert(aspeed_jtag->rst);
++
++#ifdef USE_INTERRUPTS
++ err = devm_request_irq(aspeed_jtag->dev, aspeed_jtag->irq,
++ aspeed_jtag_interrupt, 0,
++ "aspeed-jtag", aspeed_jtag);
++ if (err) {
++ dev_err(aspeed_jtag->dev, "unable to get IRQ");
++ clk_disable_unprepare(aspeed_jtag->pclk);
++ return err;
++ }
++#endif
++
++ aspeed_jtag_output_disable(aspeed_jtag);
++
++ aspeed_jtag->flag = 0;
++ aspeed_jtag->mode = 0;
++ init_waitqueue_head(&aspeed_jtag->jtag_wq);
++ return 0;
++}
++
++static int aspeed_jtag_deinit(struct platform_device *pdev,
++ struct aspeed_jtag *aspeed_jtag)
++{
++ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_ISR);
++ /* Disable clock */
++ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_CTRL);
++ reset_control_assert(aspeed_jtag->rst);
++ clk_disable_unprepare(aspeed_jtag->pclk);
++ return 0;
++}
++
++static const struct jtag_ops aspeed_jtag_ops = {
++ .freq_get = aspeed_jtag_freq_get,
++ .freq_set = aspeed_jtag_freq_set,
++ .status_get = aspeed_jtag_status_get,
++ .status_set = aspeed_jtag_status_set,
++ .xfer = aspeed_jtag_xfer,
++ .mode_set = aspeed_jtag_mode_set,
++ .bitbang = aspeed_jtag_bitbang,
++ .enable = aspeed_jtag_enable,
++ .disable = aspeed_jtag_disable
++};
++
++static int aspeed_jtag_probe(struct platform_device *pdev)
++{
++ struct aspeed_jtag *aspeed_jtag;
++ struct jtag *jtag;
++ int err;
++
++ jtag = jtag_alloc(&pdev->dev, sizeof(*aspeed_jtag), &aspeed_jtag_ops);
++ if (!jtag)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, jtag);
++ aspeed_jtag = jtag_priv(jtag);
++ aspeed_jtag->dev = &pdev->dev;
++
++ /* Initialize device*/
++ err = aspeed_jtag_init(pdev, aspeed_jtag);
++ if (err)
++ goto err_jtag_init;
++
++ /* Initialize JTAG core structure*/
++ err = devm_jtag_register(aspeed_jtag->dev, jtag);
++ if (err)
++ goto err_jtag_register;
++
++ return 0;
++
++err_jtag_register:
++ aspeed_jtag_deinit(pdev, aspeed_jtag);
++err_jtag_init:
++ jtag_free(jtag);
++ return err;
++}
++
++static int aspeed_jtag_remove(struct platform_device *pdev)
++{
++ struct jtag *jtag = platform_get_drvdata(pdev);
++
++ aspeed_jtag_deinit(pdev, jtag_priv(jtag));
++ return 0;
++}
++
++static const struct of_device_id aspeed_jtag_of_match[] = {
++ { .compatible = "aspeed,ast2400-jtag", },
++ { .compatible = "aspeed,ast2500-jtag", },
++ {}
++};
++
++static struct platform_driver aspeed_jtag_driver = {
++ .probe = aspeed_jtag_probe,
++ .remove = aspeed_jtag_remove,
++ .driver = {
++ .name = ASPEED_JTAG_NAME,
++ .of_match_table = aspeed_jtag_of_match,
++ },
++};
++module_platform_driver(aspeed_jtag_driver);
++
++MODULE_AUTHOR("Oleksandr Shamray <oleksandrs@mellanox.com>");
++MODULE_DESCRIPTION("ASPEED JTAG driver");
++MODULE_LICENSE("GPL v2");
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0054-Documentation-jtag-Add-bindings-for-Aspeed-SoC.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0054-Documentation-jtag-Add-bindings-for-Aspeed-SoC.patch
new file mode 100644
index 000000000..f17bdcd68
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0054-Documentation-jtag-Add-bindings-for-Aspeed-SoC.patch
@@ -0,0 +1,108 @@
+From 2a22feac440070b7feaf0a6fe7e7e555d57ca19b Mon Sep 17 00:00:00 2001
+From: "Corona, Ernesto" <ernesto.corona@intel.com>
+Date: Wed, 10 Mar 2019 11:45:04 -0800
+Subject: [PATCH v29 3/6] Documentation: jtag: Add bindings for Aspeed SoC
+ 24xx and 25xx families JTAG master driver
+
+It has been tested on Mellanox system with BMC equipped with
+Aspeed 2520 SoC for programming CPLD devices.
+
+It also has been tested on Intel systems with BMC equipped with
+Aspeed 25x0 SoC for JTAG board communication.
+
+Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
+Signed-off-by: Jiri Pirko <jiri@mellanox.com>
+Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com>
+Acked-by: Rob Herring <robh@kernel.org>
+Cc: Mark Rutland <mark.rutland@arm.com>
+Cc: Joel Stanley <joel@jms.id.au>
+Cc: Andrew Jeffery <andrew@aj.id.au>
+Cc: Steven A Filary <steven.a.filary@intel.com>
+Cc: Bryan Hunt <bryan.hunt@intel.com>
+---
+v28->v29
+v27->v28
+v26->v27
+v25->v26
+v24->v25
+v23->v24
+v22->v23
+v21->v22
+v20->v21
+v19->v20
+v18->v19
+
+v17->v18
+v16->v17
+v15->v16
+Comments pointed by Joel Stanley <joel.stan@gmail.com>
+- change clocks = <&clk_apb> to proper clocks = <&syscon ASPEED_CLK_APB>
+- add reset descriptions in bindings file
+
+v14->v15
+v13->v14
+v12->v13
+v11->v12
+v10->v11
+v9->v10
+v8->v9
+v7->v8
+Comments pointed by pointed by Joel Stanley <joel.stan@gmail.com>
+- Change compatible string to ast2400 and ast2000
+
+V6->v7
+Comments pointed by Tobias Klauser <tklauser@distanz.ch>
+ - Fix spell "Doccumentation" -> "Documentation"
+
+v5->v6
+Comments pointed by Tobias Klauser <tklauser@distanz.ch>
+- Small nit: s/documentation/Documentation/
+
+v4->v5
+
+V3->v4
+Comments pointed by Rob Herring <robh@kernel.org>
+- delete unnecessary "status" and "reg-shift" descriptions in
+ bindings file
+
+v2->v3
+Comments pointed by Rob Herring <robh@kernel.org>
+- split Aspeed jtag driver and binding to separate patches
+- delete unnecessary "status" and "reg-shift" descriptions in
+ bindings file
+---
+ .../devicetree/bindings/jtag/aspeed-jtag.txt | 22 ++++++++++++++++++++++
+ 1 file changed, 22 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
+
+diff --git a/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt b/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
+new file mode 100644
+index 0000000..7c36eb6
+--- /dev/null
++++ b/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
+@@ -0,0 +1,22 @@
++Aspeed JTAG driver for ast2400 and ast2500 SoC
++
++Required properties:
++- compatible: Should be one of
++ - "aspeed,ast2400-jtag"
++ - "aspeed,ast2500-jtag"
++- reg contains the offset and length of the JTAG memory
++ region
++- clocks root clock of bus, should reference the APB
++ clock in the second cell
++- resets phandle to reset controller with the reset number in
++ the second cell
++- interrupts should contain JTAG controller interrupt
++
++Example:
++jtag: jtag@1e6e4000 {
++ compatible = "aspeed,ast2500-jtag";
++ reg = <0x1e6e4000 0x1c>;
++ clocks = <&syscon ASPEED_CLK_APB>;
++ resets = <&syscon ASPEED_RESET_JTAG_MASTER>;
++ interrupts = <43>;
++};
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0055-Documentation-jtag-Add-ABI-documentation.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0055-Documentation-jtag-Add-ABI-documentation.patch
new file mode 100644
index 000000000..1e4142035
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0055-Documentation-jtag-Add-ABI-documentation.patch
@@ -0,0 +1,310 @@
+From d4c55bd4eeb6d9290025fa353d1787f18bca6ade Mon Sep 17 00:00:00 2001
+From: "Corona, Ernesto" <ernesto.corona@intel.com>
+Date: Sun, 10 Mar 2019 11:47:40 -0800
+Subject: [PATCH v29 4/6] Documentation: jtag: Add ABI documentation
+
+Added document that describe the ABI for JTAG class driver
+
+Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
+Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com>
+Acked-by: Arnd Bergmann <arnd@arndb.de>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Jiri Pirko <jiri@mellanox.com>
+Cc: Vadim Pasternak <vadimp@mellanox.com>
+Cc: Steven A Filary <steven.a.filary@intel.com>
+Cc: Bryan Hunt <bryan.hunt@intel.com>
+---
+v28->v29
+Comments pointed by Steven Filary <steven.a.filary@intel.com>
+- Expand bitbang function to accept multiples bitbang operations within a
+ single JTAG_IOCBITBANG call. It will receive a buffer with TDI and TMS
+ values and it is expected that driver fills TDO fields with its
+ corresponding output value for every transaction.
+
+v27->v28
+Comments pointed by Steven Filary <steven.a.filary@intel.com>
+- Replace JTAG_IOCRUNTEST with JTAG_SIOCSTATE adding support for all TAPC
+ end states in SW mode using a lookup table to navigate across states.
+- Add support for simultaneous READ/WRITE transfers(JTAG_READ_WRITE_XFER).
+- Support for switching JTAG controller mode between slave and master
+ mode.
+- Setup JTAG controller mode to master only when the driver is opened,
+ letting other HW to own the JTAG bus when it isn't in use.
+- Include JTAG bit bang IOCTL for low level JTAG control usage
+ (JTAG_IOCBITBANG).
+
+v26->v27
+v25->v26
+Comments pointed by Randy Dunlap <rdunlap@infradead.org>
+- fix spell in ABI documentation
+
+v24->v25
+Comments pointed by Greg KH <gregkh@linuxfoundation.org>
+- Fixed documentation according to new open() behavior
+
+v23->v24
+v22->v23
+Comments pointed by Randy Dunlap <rdunlap@infradead.org>
+- fix spell in ABI doccumentation
+
+v21->v22
+Comments pointed by Randy Dunlap <rdunlap@infradead.org>
+- fix spell in ABI doccumentation
+
+v20->v21
+Comments pointed by Randy Dunlap <rdunlap@infradead.org>
+- Fix JTAG dirver help in Kconfig
+
+v19->v20
+Comments pointed by Randy Dunlap <rdunlap@infradead.org>
+- Fix JTAG doccumentation
+
+v18->v19
+Pavel Machek <pavel@ucw.cz>
+- Added JTAG doccumentation to Documentation/jtag
+
+v17->v18
+v16->v17
+v15->v16
+v14->v15
+v13->v14
+v12->v13
+v11->v12 Tobias Klauser <tklauser@distanz.ch>
+Comments pointed by
+- rename /Documentation/ABI/testing/jatg-dev -> jtag-dev
+- Typo: s/interfase/interface
+v10->v11
+v9->v10
+Fixes added by Oleksandr:
+- change jtag-cdev to jtag-dev in documentation
+- update KernelVersion and Date in jtag-dev documentation;
+v8->v9
+v7->v8
+v6->v7
+Comments pointed by Pavel Machek <pavel@ucw.cz>
+- Added jtag-cdev documentation to Documentation/ABI/testing folder
+---
+ Documentation/ABI/testing/jtag-dev | 23 ++++++
+ Documentation/jtag/overview | 27 +++++++
+ Documentation/jtag/transactions | 145 +++++++++++++++++++++++++++++++++++++
+ 3 files changed, 195 insertions(+)
+ create mode 100644 Documentation/ABI/testing/jtag-dev
+ create mode 100644 Documentation/jtag/overview
+ create mode 100644 Documentation/jtag/transactions
+
+diff --git a/Documentation/ABI/testing/jtag-dev b/Documentation/ABI/testing/jtag-dev
+new file mode 100644
+index 000000000000..423baab18761
+--- /dev/null
++++ b/Documentation/ABI/testing/jtag-dev
+@@ -0,0 +1,23 @@
++What: /dev/jtag[0-9]+
++Date: July 2018
++KernelVersion: 4.20
++Contact: oleksandrs@mellanox.com
++Description:
++ The misc device files /dev/jtag* are the interface
++ between JTAG master interface and userspace.
++
++ The ioctl(2)-based ABI is defined and documented in
++ [include/uapi]<linux/jtag.h>.
++
++ The following file operations are supported:
++
++ open(2)
++ Opens and allocates file descriptor.
++
++ ioctl(2)
++ Initiate various actions.
++ See the inline documentation in [include/uapi]<linux/jtag.h>
++ for descriptions of all ioctls.
++
++Users:
++ userspace tools which wants to access to JTAG bus
+diff --git a/Documentation/jtag/overview b/Documentation/jtag/overview
+new file mode 100644
+index 000000000000..6a5ec335e313
+--- /dev/null
++++ b/Documentation/jtag/overview
+@@ -0,0 +1,27 @@
++Linux kernel JTAG support
++=========================
++
++JTAG is an industry standard for verifying hardware. JTAG provides access to
++many logic signals of a complex integrated circuit, including the device pins.
++
++A JTAG interface is a special interface added to a chip.
++Depending on the version of JTAG, two, four, or five pins are added.
++
++The connector pins are:
++ TDI (Test Data In)
++ TDO (Test Data Out)
++ TCK (Test Clock)
++ TMS (Test Mode Select)
++ TRST (Test Reset) optional
++
++JTAG interface is designed to have two parts - basic core driver and
++hardware specific driver. The basic driver introduces a general interface
++which is not dependent of specific hardware. It provides communication
++between user space and hardware specific driver.
++Each JTAG device is represented as a char device from (jtag0, jtag1, ...).
++Access to a JTAG device is performed through IOCTL calls.
++
++Call flow example:
++User: open -> /dev/jatgX -> JTAG core driver -> JTAG hardware specific driver
++User: ioctl -> /dev/jtagX -> JTAG core driver -> JTAG hardware specific driver
++User: close -> /dev/jatgX -> JTAG core driver -> JTAG hardware specific driver
+diff --git a/Documentation/jtag/transactions b/Documentation/jtag/transactions
+new file mode 100644
+index 000000000000..f5d4a1ded6cf
+--- /dev/null
++++ b/Documentation/jtag/transactions
+@@ -0,0 +1,145 @@
++The JTAG API
++=============
++
++JTAG master devices can be accessed through a character misc-device.
++Each JTAG master interface can be accessed by using /dev/jtagN.
++
++JTAG system calls set:
++- SIR (Scan Instruction Register, IEEE 1149.1 Instruction Register scan);
++- SDR (Scan Data Register, IEEE 1149.1 Data Register scan);
++- RUNTEST (Forces the IEEE 1149.1 bus to a run state for a specified
++number of clocks.
++
++open(), close()
++-------
++open() opens JTAG device.
++
++Open/Close device:
++- jtag_fd = open("/dev/jtag0", O_RDWR);
++- close(jtag_fd);
++
++ioctl()
++-------
++All access operations to JTAG devices are performed through ioctl interface.
++The IOCTL interface supports these requests:
++ JTAG_SIOCSTATE - Force JTAG state machine to go into a TAPC state
++ JTAG_SIOCFREQ - Set JTAG TCK frequency
++ JTAG_GIOCFREQ - Get JTAG TCK frequency
++ JTAG_IOCXFER - send/receive JTAG data Xfer
++ JTAG_GIOCSTATUS - get current JTAG TAP state
++ JTAG_SIOCMODE - set JTAG mode flags.
++ JTAG_IOCBITBANG - JTAG bitbang low level control.
++
++JTAG_SIOCFREQ, JTAG_GIOCFREQ
++------
++Set/Get JTAG clock speed:
++
++ unsigned int jtag_fd;
++ ioctl(jtag_fd, JTAG_SIOCFREQ, &frq);
++ ioctl(jtag_fd, JTAG_GIOCFREQ, &frq);
++
++JTAG_SIOCSTATE
++------
++Force JTAG state machine to go into a TAPC state
++
++struct jtag_tap_state {
++ __u8 reset;
++ __u8 from;
++ __u8 endstate;
++ __u8 tck;
++};
++
++reset:
++ JTAG_NO_RESET - go through selected endstate from current state
++ JTAG_FORCE_RESET - go through TEST_LOGIC/RESET state before selected endstate
++endstate: completion flag
++tck: clock counter
++
++Example:
++ struct jtag_tap_state tap_state;
++
++ tap_state.from = JTAG_STATE_TLRESET;
++ tap_state.endstate = JTAG_STATE_IDLE;
++ tap_state.reset = 0;
++ tap_state.tck = data_p->tck;
++ usleep(25 * 1000);
++ ioctl(jtag_fd, JTAG_SIOCSTATE, &tap_state);
++
++JTAG_GIOCSTATUS
++------
++Get JTAG TAPC machine state
++
++ unsigned int jtag_fd;
++ jtag_tapstate tapstate;
++ ioctl(jtag_fd, JTAG_GIOCSTATUS, &tapstate);
++
++JTAG_IOCXFER
++------
++Send SDR/SIR transaction
++
++struct jtag_xfer {
++ __u8 type;
++ __u8 direction;
++ __u8 from;
++ __u8 endstate;
++ __u8 padding;
++ __u32 length;
++ __u64 tdio;
++};
++
++type: transfer type - JTAG_SIR_XFER/JTAG_SDR_XFER
++direction: xfer direction - JTAG_READ_XFER/JTAG_WRITE_XFER/JTAG_READ_WRITE_XFER
++length: xfer data length in bits
++tdio : xfer data array
++from: xfer from state can be current JTAG tap state saved by the driver
++ JTAG_STATE_CURRENT or in a multichain environment any state listed in
++ jtag_tapstate struct saved by your multichain controller software.
++endstate: xfer end state after transaction finish
++ can be: any state listed in jtag_tapstate struct
++
++Example:
++ struct jtag_xfer xfer;
++ static char buf[64];
++ static unsigned int buf_len = 0;
++ [...]
++ xfer.type = JTAG_SDR_XFER;
++ xfer.tdio = (__u64)buf;
++ xfer.length = buf_len;
++ xfer.from = JTAG_STATE_TLRESET;
++ xfer.endstate = JTAG_STATE_IDLE;
++
++ if (is_read)
++ xfer.direction = JTAG_READ_XFER;
++ else if (is_write)
++ xfer.direction = JTAG_WRITE_XFER;
++ else
++ xfer.direction = JTAG_READ_WRITE_XFER;
++
++ ioctl(jtag_fd, JTAG_IOCXFER, &xfer);
++
++JTAG_SIOCMODE
++------
++If hardware driver can support different running modes you can change it.
++
++Example:
++ struct jtag_mode mode;
++ mode.feature = JTAG_XFER_MODE;
++ mode.mode = JTAG_XFER_HW_MODE;
++ ioctl(jtag_fd, JTAG_SIOCMODE, &mode);
++
++JTAG_IOCBITBANG
++------
++JTAG Bitbang low level operation.
++
++Example:
++ struct bitbang_packet bitbang;
++ struct tck_bitbang bitbang_data[2];
++ bitbang_data[0].tms = 0;
++ bitbang_data[0].tdi = 1;
++ bitbang_data[1].tms = 0;
++ bitbang_data[1].tdi = 1;
++ bitbang.data = bitbang_data;
++ bitbang.length = 2;
++ ioctl(jtag_fd, JTAG_IOCBITBANG, &bitbang);
++ tdo0 = bitbang_data[0].tdo;
++ tdo1 = bitbang_data[1].tdo;
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0056-Documentation-jtag-Add-JTAG-core-driver-ioctl-number.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0056-Documentation-jtag-Add-JTAG-core-driver-ioctl-number.patch
new file mode 100644
index 000000000..3efe2c5f3
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0056-Documentation-jtag-Add-JTAG-core-driver-ioctl-number.patch
@@ -0,0 +1,57 @@
+From d5efb0ec2b28bc1074472ab4eaa937dcbe490f6a Mon Sep 17 00:00:00 2001
+From: "Corona, Ernesto" <ernesto.corona@intel.com>
+Date: Sun, 10 Mar 2019 11:48:18 -0800
+Subject: [PATCH] Documentation jtag: Add JTAG core driver ioctl number
+
+JTAG class driver provide infrastructure to support hardware/software
+JTAG platform drivers. It provide user layer API interface for flashing
+and debugging external devices which equipped with JTAG interface
+using standard transactions.
+
+Driver exposes set of IOCTL to user space for:
+- XFER:
+ SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
+ SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
+- GIOCSTATUS read the current TAPC state of the JTAG controller
+- SIOCSTATE Forces the JTAG TAPC to go into a particular state.
+- SIOCFREQ/GIOCFREQ for setting and reading JTAG frequency.
+- IOCBITBANG for low level control of JTAG signals.
+
+Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
+Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com>
+Acked-by: Philippe Ombredanne <pombredanne@nexb.com>
+Cc: Jiri Pirko <jiri@mellanox.com>
+Cc: Vadim Pasternak <vadimp@mellanox.com>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
+Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+Cc: Kishon Vijay Abraham I <kishon@ti.com>
+Cc: Darrick J. Wong <darrick.wong@oracle.com>
+Cc: Bryant G. Ly <bryantly@linux.vnet.ibm.com>
+Cc: Eric Sandeen <sandeen@redhat.com>
+Cc: Randy Dunlap <rdunlap@infradead.org>
+Cc: Tomohiro Kusumi <kusumi.tomohiro@gmail.com>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Cc: Steven A Filary <steven.a.filary@intel.com>
+Cc: Bryan Hunt <bryan.hunt@intel.com>
+---
+ Documentation/ioctl/ioctl-number.rst | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/Documentation/ioctl/ioctl-number.rst b/Documentation/ioctl/ioctl-number.rst
+index 7f8dcae7a230..4d25966d44e5 100644
+--- a/Documentation/ioctl/ioctl-number.rst
++++ b/Documentation/ioctl/ioctl-number.rst
+@@ -332,6 +332,8 @@ Code Seq# Include File Comments
+ <mailto:vgo@ratio.de>
+ 0xB1 00-1F PPPoX
+ <mailto:mostrows@styx.uwaterloo.ca>
++0xB2 00-0F linux/jtag.h JTAG driver
++ <mailto:oleksandrs@mellanox.com>
+ 0xB3 00 linux/mmc/ioctl.h
+ 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>
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0057-drivers-jtag-Add-JTAG-core-driver-Maintainers.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0057-drivers-jtag-Add-JTAG-core-driver-Maintainers.patch
new file mode 100644
index 000000000..b5f5a93a0
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0057-drivers-jtag-Add-JTAG-core-driver-Maintainers.patch
@@ -0,0 +1,50 @@
+From 01fc94b1193f4e97d498e2bcb05dfe21b991b01d Mon Sep 17 00:00:00 2001
+From: "Corona, Ernesto" <ernesto.corona@intel.com>
+Date: Sun, 10 Mar 2019 11:49:37 -0800
+Subject: [PATCH v29 6/6] drivers: jtag: Add JTAG core driver Maintainers
+
+JTAG class driver provide infrastructure to support hardware/software
+JTAG platform drivers. It provide user layer API interface for flashing
+and debugging external devices which equipped with JTAG interface
+using standard transactions.
+
+Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
+Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com>
+Acked-by: Arnd Bergmann <arnd@arndb.de>
+Cc: Jiri Pirko <jiri@mellanox.com>
+Cc: Vadim Pasternak <vadimp@mellanox.com>
+Cc: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc: David S. Miller <davem@davemloft.net>
+Cc: Nicolas Ferre <nicolas.ferre@microchip.com>
+Cc: Steven A Filary <steven.a.filary@intel.com>
+Cc: Bryan Hunt <bryan.hunt@intel.com>
+---
+ MAINTAINERS | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index f5c5eaa69f2f..92b0932c4b9f 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -8709,6 +8709,17 @@ L: linux-serial@vger.kernel.org
+ S: Orphan
+ F: drivers/tty/serial/jsm/
+
++JTAG SUBSYSTEM
++M: Oleksandr Shamray <oleksandrs@mellanox.com>
++M: Vadim Pasternak <vadimp@mellanox.com>
++M Ernesto Corona <ernesto.corona@intel.com>
++S: Maintained
++F: include/linux/jtag.h
++F: include/uapi/linux/jtag.h
++F: drivers/jtag/
++F: Documentation/devicetree/bindings/jtag/
++F: Documentation/ABI/testing/jtag-dev
++
+ K10TEMP HARDWARE MONITORING DRIVER
+ M: Clemens Ladisch <clemens@ladisch.de>
+ L: linux-hwmon@vger.kernel.org
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0060-i2c-aspeed-fix-master-pending-state-handling.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0060-i2c-aspeed-fix-master-pending-state-handling.patch
new file mode 100644
index 000000000..d38c089af
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0060-i2c-aspeed-fix-master-pending-state-handling.patch
@@ -0,0 +1,135 @@
+From ca5e5e784ada4da11caebf6ba6852e1ff8a13bf7 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Tue, 11 Jun 2019 14:59:53 -0700
+Subject: [PATCH] i2c: aspeed: fix master pending state handling
+
+In case of master pending state, it should not trigger a master
+command, otherwise data could be corrupted because this H/W shares
+the same data buffer for slave and master operations. It also means
+that H/W command queue handling is unreliable because of the buffer
+sharing issue. To fix this issue, it clears command queue if a
+master command is queued in pending state to use S/W solution
+instead of H/W command queue handling. Also, it refines restarting
+mechanism of the pending master command.
+
+Fixes: 2e57b7cebb98 ("i2c: aspeed: Add multi-master use case support")
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/i2c/busses/i2c-aspeed.c | 54 ++++++++++++++++++++++++++---------------
+ 1 file changed, 34 insertions(+), 20 deletions(-)
+
+diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
+index 58bdbe472721..7becfcd67142 100644
+--- a/drivers/i2c/busses/i2c-aspeed.c
++++ b/drivers/i2c/busses/i2c-aspeed.c
+@@ -108,6 +108,12 @@
+ #define ASPEED_I2CD_S_TX_CMD BIT(2)
+ #define ASPEED_I2CD_M_TX_CMD BIT(1)
+ #define ASPEED_I2CD_M_START_CMD BIT(0)
++#define ASPEED_I2CD_MASTER_CMDS_MASK \
++ (ASPEED_I2CD_M_STOP_CMD | \
++ ASPEED_I2CD_M_S_RX_CMD_LAST | \
++ ASPEED_I2CD_M_RX_CMD | \
++ ASPEED_I2CD_M_TX_CMD | \
++ ASPEED_I2CD_M_START_CMD)
+
+ /* 0x18 : I2CD Slave Device Address Register */
+ #define ASPEED_I2CD_DEV_ADDR_MASK GENMASK(6, 0)
+@@ -351,18 +357,19 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus)
+ struct i2c_msg *msg = &bus->msgs[bus->msgs_index];
+ 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)
++ if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) {
+ bus->master_state = ASPEED_I2C_MASTER_PENDING;
++ return;
++ }
+ #endif /* CONFIG_I2C_SLAVE */
+
++ bus->master_state = ASPEED_I2C_MASTER_START;
+ bus->buf_index = 0;
+
+ if (msg->flags & I2C_M_RD) {
+@@ -437,20 +444,6 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ }
+ }
+
+-#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)
+@@ -477,11 +470,15 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ #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.
++ * master command, clear the queued master command and change
++ * its state to 'pending'. To simplify handling of pending
++ * cases, it uses S/W solution instead of H/W command queue
++ * handling.
+ */
+ if (unlikely(irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH)) {
++ writel(readl(bus->base + ASPEED_I2C_CMD_REG) &
++ ~ASPEED_I2CD_MASTER_CMDS_MASK,
++ bus->base + ASPEED_I2C_CMD_REG);
+ bus->master_state = ASPEED_I2C_MASTER_PENDING;
+ dev_dbg(bus->dev,
+ "master goes pending due to a slave start\n");
+@@ -644,6 +641,14 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
+ irq_handled |= aspeed_i2c_master_irq(bus,
+ irq_remaining);
+ }
++
++ /*
++ * Start a pending master command at here if a slave operation is
++ * completed.
++ */
++ if (bus->master_state == ASPEED_I2C_MASTER_PENDING &&
++ bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE)
++ aspeed_i2c_do_start(bus);
+ #else
+ irq_handled = aspeed_i2c_master_irq(bus, irq_remaining);
+ #endif /* CONFIG_I2C_SLAVE */
+@@ -707,6 +712,15 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
+ ASPEED_I2CD_BUS_BUSY_STS))
+ aspeed_i2c_recover_bus(bus);
+
++ /*
++ * If timed out and the state is still pending, drop the pending
++ * master command.
++ */
++ spin_lock_irqsave(&bus->lock, flags);
++ if (bus->master_state == ASPEED_I2C_MASTER_PENDING)
++ bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
++ spin_unlock_irqrestore(&bus->lock, flags);
++
+ return -ETIMEDOUT;
+ }
+
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0061-i2c-aspeed-add-buffer-mode-transfer-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0061-i2c-aspeed-add-buffer-mode-transfer-support.patch
new file mode 100644
index 000000000..8e91b5ced
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0061-i2c-aspeed-add-buffer-mode-transfer-support.patch
@@ -0,0 +1,1040 @@
+From 0bc5efede7c99da41fc0cbadfb1644b428ead9d3 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Tue, 11 Jun 2019 15:07:08 -0700
+Subject: [PATCH] i2c: aspeed: add buffer mode transfer support
+
+Byte mode currently this driver uses makes lots of interrupt call
+which isn't good for performance and it makes the driver very
+timing sensitive. To improve performance of the driver, this commit
+adds buffer mode transfer support which uses I2C SRAM buffer
+instead of using a single byte buffer.
+
+AST2400:
+It has 2 KBytes (256 Bytes x 8 pages) of I2C SRAM buffer pool from
+0x1e78a800 to 0x1e78afff that can be used for all busses with
+buffer pool manipulation. To simplify implementation for supporting
+both AST2400 and AST2500, it assigns each 128 Bytes per bus without
+using buffer pool manipulation so total 1792 Bytes of I2C SRAM
+buffer will be used.
+
+AST2500:
+It has 16 Bytes of individual I2C SRAM buffer per each bus and its
+range is from 0x1e78a200 to 0x1e78a2df, so it doesn't have 'buffer
+page selection' bit field in the Function control register, and
+neither 'base address pointer' bit field in the Pool buffer control
+register it has. To simplify implementation for supporting both
+AST2400 and AST2500, it writes zeros on those register bit fields
+but it's okay because it does nothing in AST2500.
+
+It provides buffer based master and slave data transfer.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ .../devicetree/bindings/i2c/i2c-aspeed.txt | 40 ++-
+ arch/arm/boot/dts/aspeed-g4.dtsi | 47 ++--
+ arch/arm/boot/dts/aspeed-g5.dtsi | 47 ++--
+ arch/arm/boot/dts/aspeed-g6.dtsi | 34 +--
+ drivers/i2c/busses/i2c-aspeed.c | 294 ++++++++++++++++++---
+ 5 files changed, 366 insertions(+), 96 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
+index 7da7e813b2b0..0ff3539cee95 100644
+--- a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
++++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
+@@ -3,7 +3,10 @@ Device tree configuration for the I2C busses on the AST24XX and AST25XX SoCs.
+ Required Properties:
+ - #address-cells : should be 1
+ - #size-cells : should be 0
+-- reg : address offset and range of bus
++- reg : Address offset and range of bus registers.
++ An additional SRAM buffer address offset and range is
++ optional in case of enabling I2C dedicated SRAM for
++ buffer mode transfer support.
+ - compatible : should be "aspeed,ast2400-i2c-bus"
+ or "aspeed,ast2500-i2c-bus"
+ - clocks : root clock of bus, should reference the APB
+@@ -28,12 +31,21 @@ i2c {
+ #size-cells = <1>;
+ ranges = <0 0x1e78a000 0x1000>;
+
+- i2c_ic: interrupt-controller@0 {
+- #interrupt-cells = <1>;
+- compatible = "aspeed,ast2400-i2c-ic";
++ i2c_gr: i2c-global-regs@0 {
++ compatible = "aspeed,ast2500-i2c-gr", "syscon";
+ reg = <0x0 0x40>;
+- interrupts = <12>;
+- interrupt-controller;
++
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges = <0x0 0x0 0x40>;
++
++ i2c_ic: interrupt-controller@0 {
++ #interrupt-cells = <1>;
++ compatible = "aspeed,ast2500-i2c-ic";
++ reg = <0x0 0x4>;
++ interrupts = <12>;
++ interrupt-controller;
++ };
+ };
+
+ i2c0: i2c-bus@40 {
+@@ -41,11 +53,25 @@ i2c {
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+ reg = <0x40 0x40>;
+- compatible = "aspeed,ast2400-i2c-bus";
++ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+ bus-frequency = <100000>;
+ interrupts = <0>;
+ interrupt-parent = <&i2c_ic>;
+ };
++
++ /* buffer mode transfer enabled */
++ i2c1: i2c-bus@80 {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ #interrupt-cells = <1>;
++ reg = <0x80 0x40>, <0x210 0x10>;
++ compatible = "aspeed,ast2500-i2c-bus";
++ clocks = <&syscon ASPEED_CLK_APB>;
++ resets = <&syscon ASPEED_RESET_I2C>;
++ bus-frequency = <100000>;
++ interrupts = <1>;
++ interrupt-parent = <&i2c_ic>;
++ };
+ };
+diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
+index 14e5dc260a3b..68f40a89c91f 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -519,12 +519,21 @@
+ };
+
+ &i2c {
+- i2c_ic: interrupt-controller@0 {
+- #interrupt-cells = <1>;
+- compatible = "aspeed,ast2400-i2c-ic";
++ i2c_gr: i2c-global-regs@0 {
++ compatible = "aspeed,ast2400-i2c-gr", "syscon";
+ reg = <0x0 0x40>;
+- interrupts = <12>;
+- interrupt-controller;
++
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges = <0x0 0x0 0x40>;
++
++ i2c_ic: interrupt-controller@0 {
++ #interrupt-cells = <1>;
++ compatible = "aspeed,ast2400-i2c-ic";
++ reg = <0x0 0x4>;
++ interrupts = <12>;
++ interrupt-controller;
++ };
+ };
+
+ i2c0: i2c-bus@40 {
+@@ -532,7 +541,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x40 0x40>;
++ reg = <0x40 0x40>, <0x800 0x80>;
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -548,7 +557,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x80 0x40>;
++ reg = <0x80 0x40>, <0x880 0x80>;
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -564,7 +573,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0xc0 0x40>;
++ reg = <0xc0 0x40>, <0x900 0x80>;
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -581,7 +590,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x100 0x40>;
++ reg = <0x100 0x40>, <0x980 0x80>;
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -598,7 +607,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x140 0x40>;
++ reg = <0x140 0x40>, <0xa00 0x80>;
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -615,7 +624,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x180 0x40>;
++ reg = <0x180 0x40>, <0xa80 0x80>;
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -632,7 +641,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x1c0 0x40>;
++ reg = <0x1c0 0x40>, <0xb00 0x80>;
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -649,7 +658,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x300 0x40>;
++ reg = <0x300 0x40>, <0xb80 0x80>;
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -666,7 +675,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x340 0x40>;
++ reg = <0x340 0x40>, <0xc00 0x80>;
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -683,7 +692,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x380 0x40>;
++ reg = <0x380 0x40>, <0xc80 0x80>;
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -700,7 +709,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x3c0 0x40>;
++ reg = <0x3c0 0x40>, <0xd00 0x80>;
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -717,7 +726,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x400 0x40>;
++ reg = <0x400 0x40>, <0xd80 0x80>;
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -734,7 +743,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x440 0x40>;
++ reg = <0x440 0x40>, <0xe00 0x80>;
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -751,7 +760,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x480 0x40>;
++ reg = <0x480 0x40>, <0xe80 0x80>;
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index 3d615708a0cd..fdc669ebfb84 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -616,12 +616,21 @@
+ };
+
+ &i2c {
+- i2c_ic: interrupt-controller@0 {
+- #interrupt-cells = <1>;
+- compatible = "aspeed,ast2500-i2c-ic";
++ i2c_gr: i2c-global-regs@0 {
++ compatible = "aspeed,ast2500-i2c-gr", "syscon";
+ reg = <0x0 0x40>;
+- interrupts = <12>;
+- interrupt-controller;
++
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges = <0x0 0x0 0x40>;
++
++ i2c_ic: interrupt-controller@0 {
++ #interrupt-cells = <1>;
++ compatible = "aspeed,ast2500-i2c-ic";
++ reg = <0x0 0x4>;
++ interrupts = <12>;
++ interrupt-controller;
++ };
+ };
+
+ i2c0: i2c-bus@40 {
+@@ -629,7 +638,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x40 0x40>;
++ reg = <0x40 0x40>, <0x200 0x10>;
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -645,7 +654,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x80 0x40>;
++ reg = <0x80 0x40>, <0x210 0x10>;
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -661,7 +670,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0xc0 0x40>;
++ reg = <0xc0 0x40>, <0x220 0x10>;
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -678,7 +687,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x100 0x40>;
++ reg = <0x100 0x40>, <0x230 0x10>;
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -695,7 +704,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x140 0x40>;
++ reg = <0x140 0x40>, <0x240 0x10>;
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -712,7 +721,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x180 0x40>;
++ reg = <0x180 0x40>, <0x250 0x10>;
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -729,7 +738,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x1c0 0x40>;
++ reg = <0x1c0 0x40>, <0x260 0x10>;
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -746,7 +755,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x300 0x40>;
++ reg = <0x300 0x40>, <0x270 0x10>;
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -763,7 +772,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x340 0x40>;
++ reg = <0x340 0x40>, <0x280 0x10>;
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -780,7 +789,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x380 0x40>;
++ reg = <0x380 0x40>, <0x290 0x10>;
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -797,7 +806,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x3c0 0x40>;
++ reg = <0x3c0 0x40>, <0x2a0 0x10>;
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -814,7 +823,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x400 0x40>;
++ reg = <0x400 0x40>, <0x2b0 0x10>;
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -831,7 +840,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x440 0x40>;
++ reg = <0x440 0x40>, <0x2c0 0x10>;
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -848,7 +857,7 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x480 0x40>;
++ reg = <0x480 0x40>, <0x2d0 0x10>;
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
+index 2ad90a906266..2dd89efee37c 100644
+--- a/arch/arm/boot/dts/aspeed-g6.dtsi
++++ b/arch/arm/boot/dts/aspeed-g6.dtsi
+@@ -530,11 +530,11 @@
+ #include "aspeed-g6-pinctrl.dtsi"
+
+ &i2c {
+- i2c0: i2c-bus@40 {
++ i2c0: i2c-bus@80 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+- reg = <0x80 0x80>;
++ reg = <0x80 0x80>, <0xc00 0x20>;
+ compatible = "aspeed,ast2600-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB1>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -549,7 +549,7 @@
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+- reg = <0x100 0x80>;
++ reg = <0x100 0x80>, <0xc20 0x20>;
+ compatible = "aspeed,ast2600-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB1>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -564,7 +564,7 @@
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+- reg = <0x180 0x80>;
++ reg = <0x180 0x80>, <0xc40 0x20>;
+ compatible = "aspeed,ast2600-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB1>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -579,7 +579,7 @@
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+- reg = <0x200 0x80>;
++ reg = <0x200 0x80>, <0xc60 0x20>;
+ compatible = "aspeed,ast2600-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB1>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -594,7 +594,7 @@
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+- reg = <0x280 0x80>;
++ reg = <0x280 0x80>, <0xc80 0x20>;
+ compatible = "aspeed,ast2600-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB1>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -609,7 +609,7 @@
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+- reg = <0x300 0x80>;
++ reg = <0x300 0x80>, <0xca0 0x20>;
+ compatible = "aspeed,ast2600-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB1>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -624,7 +624,7 @@
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+- reg = <0x380 0x80>;
++ reg = <0x380 0x80>, <0xcc0 0x20>;
+ compatible = "aspeed,ast2600-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB1>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -639,7 +639,7 @@
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+- reg = <0x400 0x80>;
++ reg = <0x400 0x80>, <0xce0 0x20>;
+ compatible = "aspeed,ast2600-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB1>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -654,7 +654,7 @@
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+- reg = <0x480 0x80>;
++ reg = <0x480 0x80>, <0xd00 0x20>;
+ compatible = "aspeed,ast2600-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB1>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -669,7 +669,7 @@
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+- reg = <0x500 0x80>;
++ reg = <0x500 0x80>, <0xd20 0x20>;
+ compatible = "aspeed,ast2600-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB1>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -684,7 +684,7 @@
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+- reg = <0x580 0x80>;
++ reg = <0x580 0x80>, <0xd40 0x20>;
+ compatible = "aspeed,ast2600-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB1>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -699,7 +699,7 @@
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+- reg = <0x600 0x80>;
++ reg = <0x600 0x80>, <0xd60 0x20>;
+ compatible = "aspeed,ast2600-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB1>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -714,7 +714,7 @@
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+- reg = <0x680 0x80>;
++ reg = <0x680 0x80>, <0xd80 0x20>;
+ compatible = "aspeed,ast2600-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB1>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -729,7 +729,7 @@
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+- reg = <0x700 0x80>;
++ reg = <0x700 0x80>, <0xda0 0x20>;
+ compatible = "aspeed,ast2600-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB1>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -744,7 +744,7 @@
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+- reg = <0x780 0x80>;
++ reg = <0x780 0x80>, <0xdc0 0x20>;
+ compatible = "aspeed,ast2600-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB1>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -759,7 +759,7 @@
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+- reg = <0x800 0x80>;
++ reg = <0x800 0x80>, <0xde0 0x20>;
+ compatible = "aspeed,ast2600-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB1>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
+index b9f425739940..3831466912b4 100644
+--- a/drivers/i2c/busses/i2c-aspeed.c
++++ b/drivers/i2c/busses/i2c-aspeed.c
+@@ -7,6 +7,7 @@
+ * Copyright 2017 Google, Inc.
+ */
+
++#include <linux/bitfield.h>
+ #include <linux/clk.h>
+ #include <linux/completion.h>
+ #include <linux/err.h>
+@@ -19,15 +20,24 @@
+ #include <linux/irqchip/chained_irq.h>
+ #include <linux/irqdomain.h>
+ #include <linux/kernel.h>
++#include <linux/mfd/syscon.h>
+ #include <linux/module.h>
+ #include <linux/of_address.h>
+ #include <linux/of_irq.h>
+ #include <linux/of_platform.h>
+ #include <linux/platform_device.h>
++#include <linux/regmap.h>
+ #include <linux/reset.h>
+ #include <linux/slab.h>
+
+-/* I2C Register */
++/* I2C Global Registers */
++/* 0x00 : I2CG Interrupt Status Register */
++/* 0x08 : I2CG Interrupt Target Assignment */
++/* 0x0c : I2CG Global Control Register (AST2500) */
++#define ASPEED_I2CG_GLOBAL_CTRL_REG 0x0c
++#define ASPEED_I2CG_SRAM_BUFFER_EN BIT(0)
++
++/* I2C Bus Registers */
+ #define ASPEED_I2C_FUN_CTRL_REG 0x00
+ #define ASPEED_I2C_AC_TIMING_REG1 0x04
+ #define ASPEED_I2C_AC_TIMING_REG2 0x08
+@@ -35,14 +45,12 @@
+ #define ASPEED_I2C_INTR_STS_REG 0x10
+ #define ASPEED_I2C_CMD_REG 0x14
+ #define ASPEED_I2C_DEV_ADDR_REG 0x18
++#define ASPEED_I2C_BUF_CTRL_REG 0x1c
+ #define ASPEED_I2C_BYTE_BUF_REG 0x20
+
+-/* Global Register Definition */
+-/* 0x00 : I2C Interrupt Status Register */
+-/* 0x08 : I2C Interrupt Target Assignment */
+-
+ /* Device Register Definition */
+ /* 0x00 : I2CD Function Control Register */
++#define ASPEED_I2CD_BUFFER_PAGE_SEL_MASK GENMASK(22, 20)
+ #define ASPEED_I2CD_MULTI_MASTER_DIS BIT(15)
+ #define ASPEED_I2CD_SDA_DRIVE_1T_EN BIT(8)
+ #define ASPEED_I2CD_M_SDA_DRIVE_1T_EN BIT(7)
+@@ -102,6 +110,8 @@
+ #define ASPEED_I2CD_BUS_RECOVER_CMD BIT(11)
+
+ /* Command Bit */
++#define ASPEED_I2CD_RX_BUFF_ENABLE BIT(7)
++#define ASPEED_I2CD_TX_BUFF_ENABLE BIT(6)
+ #define ASPEED_I2CD_M_STOP_CMD BIT(5)
+ #define ASPEED_I2CD_M_S_RX_CMD_LAST BIT(4)
+ #define ASPEED_I2CD_M_RX_CMD BIT(3)
+@@ -112,6 +122,13 @@
+ /* 0x18 : I2CD Slave Device Address Register */
+ #define ASPEED_I2CD_DEV_ADDR_MASK GENMASK(6, 0)
+
++/* 0x1c : I2CD Buffer Control Register */
++/* Use 8-bits or 6-bits wide bit fileds to support both AST2400 and AST2500 */
++#define ASPEED_I2CD_BUF_RX_COUNT_MASK GENMASK(31, 24)
++#define ASPEED_I2CD_BUF_RX_SIZE_MASK GENMASK(23, 16)
++#define ASPEED_I2CD_BUF_TX_COUNT_MASK GENMASK(15, 8)
++#define ASPEED_I2CD_BUF_OFFSET_MASK GENMASK(5, 0)
++
+ enum aspeed_i2c_master_state {
+ ASPEED_I2C_MASTER_INACTIVE,
+ ASPEED_I2C_MASTER_PENDING,
+@@ -157,6 +174,11 @@ struct aspeed_i2c_bus {
+ int master_xfer_result;
+ /* Multi-master */
+ bool multi_master;
++ /* Buffer mode */
++ void __iomem *buf_base;
++ size_t buf_size;
++ u8 buf_offset;
++ u8 buf_page;
+ #if IS_ENABLED(CONFIG_I2C_SLAVE)
+ struct i2c_client *slave;
+ enum aspeed_i2c_slave_state slave_state;
+@@ -253,6 +275,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ {
+ u32 command, irq_handled = 0;
+ struct i2c_client *slave = bus->slave;
++ int i, len;
+ u8 value;
+
+ if (!slave)
+@@ -275,7 +298,12 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+
+ /* Slave was sent something. */
+ if (irq_status & ASPEED_I2CD_INTR_RX_DONE) {
+- value = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8;
++ if (bus->buf_base &&
++ bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED &&
++ !(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP))
++ value = readb(bus->buf_base);
++ else
++ value = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8;
+ /* Handle address frame. */
+ if (bus->slave_state == ASPEED_I2C_SLAVE_START) {
+ if (value & 0x1)
+@@ -290,6 +318,20 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+
+ /* Slave was asked to stop. */
+ if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) {
++ if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED &&
++ irq_status & ASPEED_I2CD_INTR_RX_DONE) {
++ if (bus->buf_base) {
++ len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK,
++ readl(bus->base +
++ ASPEED_I2C_BUF_CTRL_REG));
++ for (i = 0; i < len; i++) {
++ value = readb(bus->buf_base + i);
++ i2c_slave_event(slave,
++ I2C_SLAVE_WRITE_RECEIVED,
++ &value);
++ }
++ }
++ }
+ irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP;
+ bus->slave_state = ASPEED_I2C_SLAVE_STOP;
+ }
+@@ -322,9 +364,36 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ case ASPEED_I2C_SLAVE_WRITE_REQUESTED:
+ bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED;
+ i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
++ if (bus->buf_base) {
++ writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK,
++ bus->buf_size - 1) |
++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK,
++ bus->buf_offset),
++ bus->base + ASPEED_I2C_BUF_CTRL_REG);
++ writel(ASPEED_I2CD_RX_BUFF_ENABLE,
++ bus->base + ASPEED_I2C_CMD_REG);
++ }
+ break;
+ case ASPEED_I2C_SLAVE_WRITE_RECEIVED:
+ i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value);
++ if (bus->buf_base) {
++ len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK,
++ readl(bus->base +
++ ASPEED_I2C_BUF_CTRL_REG));
++ for (i = 1; i < len; i++) {
++ value = readb(bus->buf_base + i);
++ i2c_slave_event(slave,
++ I2C_SLAVE_WRITE_RECEIVED,
++ &value);
++ }
++ writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK,
++ bus->buf_size - 1) |
++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK,
++ bus->buf_offset),
++ bus->base + ASPEED_I2C_BUF_CTRL_REG);
++ writel(ASPEED_I2CD_RX_BUFF_ENABLE,
++ bus->base + ASPEED_I2C_CMD_REG);
++ }
+ break;
+ case ASPEED_I2C_SLAVE_STOP:
+ i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
+@@ -350,6 +419,8 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus)
+ u32 command = ASPEED_I2CD_M_START_CMD | ASPEED_I2CD_M_TX_CMD;
+ struct i2c_msg *msg = &bus->msgs[bus->msgs_index];
+ u8 slave_addr = i2c_8bit_addr_from_msg(msg);
++ u8 wbuf[4];
++ int len;
+
+ #if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /*
+@@ -368,12 +439,66 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus)
+
+ if (msg->flags & I2C_M_RD) {
+ command |= ASPEED_I2CD_M_RX_CMD;
+- /* Need to let the hardware know to NACK after RX. */
+- if (msg->len == 1 && !(msg->flags & I2C_M_RECV_LEN))
+- command |= ASPEED_I2CD_M_S_RX_CMD_LAST;
++
++ if (bus->buf_base && !(msg->flags & I2C_M_RECV_LEN)) {
++ command |= ASPEED_I2CD_RX_BUFF_ENABLE;
++
++ if (msg->len > bus->buf_size) {
++ len = bus->buf_size;
++ } else {
++ len = msg->len;
++ command |= ASPEED_I2CD_M_S_RX_CMD_LAST;
++ }
++
++ writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK,
++ len - 1) |
++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK,
++ bus->buf_offset),
++ bus->base + ASPEED_I2C_BUF_CTRL_REG);
++ } else {
++ /* Need to let the hardware know to NACK after RX. */
++ if (msg->len == 1 && !(msg->flags & I2C_M_RECV_LEN))
++ command |= ASPEED_I2CD_M_S_RX_CMD_LAST;
++ }
++ } else {
++ if (bus->buf_base) {
++ int i;
++
++ command |= ASPEED_I2CD_TX_BUFF_ENABLE;
++
++ if (msg->len + 1 > bus->buf_size)
++ len = bus->buf_size;
++ else
++ len = msg->len + 1;
++
++ /*
++ * Yeah, it looks clumsy but byte writings on a remapped
++ * I2C SRAM cause corruptions so use this way to make
++ * dword writings.
++ */
++ wbuf[0] = slave_addr;
++ for (i = 1; i < len; i++) {
++ wbuf[i % 4] = msg->buf[i - 1];
++ if (i % 4 == 3)
++ writel(*(u32 *)wbuf,
++ bus->buf_base + i - 3);
++ }
++ if (--i % 4 != 3)
++ writel(*(u32 *)wbuf,
++ bus->buf_base + i - (i % 4));
++
++ bus->buf_index = len - 1;
++
++ writel(FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK,
++ len - 1) |
++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK,
++ bus->buf_offset),
++ bus->base + ASPEED_I2C_BUF_CTRL_REG);
++ }
+ }
+
+- writel(slave_addr, bus->base + ASPEED_I2C_BYTE_BUF_REG);
++ if (!(command & ASPEED_I2CD_TX_BUFF_ENABLE))
++ writel(slave_addr, bus->base + ASPEED_I2C_BYTE_BUF_REG);
+ writel(command, bus->base + ASPEED_I2C_CMD_REG);
+ }
+
+@@ -413,7 +538,7 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ u32 irq_handled = 0, command = 0;
+ struct i2c_msg *msg;
+ u8 recv_byte;
+- int ret;
++ int ret, len;
+
+ if (irq_status & ASPEED_I2CD_INTR_BUS_RECOVER_DONE) {
+ bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
+@@ -526,11 +651,43 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ /* fall through */
+ case ASPEED_I2C_MASTER_TX_FIRST:
+ if (bus->buf_index < msg->len) {
++ command = ASPEED_I2CD_M_TX_CMD;
++
++ if (bus->buf_base) {
++ u8 wbuf[4];
++ int i;
++
++ command |= ASPEED_I2CD_TX_BUFF_ENABLE;
++
++ if (msg->len - bus->buf_index > bus->buf_size)
++ len = bus->buf_size;
++ else
++ len = msg->len - bus->buf_index;
++
++ for (i = 0; i < len; i++) {
++ wbuf[i % 4] = msg->buf[bus->buf_index
++ + i];
++ if (i % 4 == 3)
++ writel(*(u32 *)wbuf,
++ bus->buf_base + i - 3);
++ }
++ if (--i % 4 != 3)
++ writel(*(u32 *)wbuf,
++ bus->buf_base + i - (i % 4));
++
++ bus->buf_index += len;
++
++ writel(FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK,
++ len - 1) |
++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK,
++ bus->buf_offset),
++ bus->base + ASPEED_I2C_BUF_CTRL_REG);
++ } else {
++ writel(msg->buf[bus->buf_index++],
++ bus->base + ASPEED_I2C_BYTE_BUF_REG);
++ }
++ writel(command, bus->base + ASPEED_I2C_CMD_REG);
+ bus->master_state = ASPEED_I2C_MASTER_TX;
+- writel(msg->buf[bus->buf_index++],
+- bus->base + ASPEED_I2C_BYTE_BUF_REG);
+- writel(ASPEED_I2CD_M_TX_CMD,
+- bus->base + ASPEED_I2C_CMD_REG);
+ } else {
+ aspeed_i2c_next_msg_or_stop(bus);
+ }
+@@ -547,25 +704,56 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ }
+ irq_handled |= ASPEED_I2CD_INTR_RX_DONE;
+
+- recv_byte = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8;
+- msg->buf[bus->buf_index++] = recv_byte;
+-
+- if (msg->flags & I2C_M_RECV_LEN) {
+- if (unlikely(recv_byte > I2C_SMBUS_BLOCK_MAX)) {
+- bus->cmd_err = -EPROTO;
+- aspeed_i2c_do_stop(bus);
+- goto out_no_complete;
++ if (bus->buf_base && !(msg->flags & I2C_M_RECV_LEN)) {
++ len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK,
++ readl(bus->base +
++ ASPEED_I2C_BUF_CTRL_REG));
++ memcpy_fromio(msg->buf + bus->buf_index,
++ bus->buf_base, len);
++ bus->buf_index += len;
++ } else {
++ recv_byte = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG)
++ >> 8;
++ msg->buf[bus->buf_index++] = recv_byte;
++
++ if (msg->flags & I2C_M_RECV_LEN) {
++ if (unlikely(recv_byte > I2C_SMBUS_BLOCK_MAX)) {
++ bus->cmd_err = -EPROTO;
++ aspeed_i2c_do_stop(bus);
++ goto out_no_complete;
++ }
++ msg->len = recv_byte +
++ ((msg->flags & I2C_CLIENT_PEC) ?
++ 2 : 1);
++ msg->flags &= ~I2C_M_RECV_LEN;
+ }
+- msg->len = recv_byte +
+- ((msg->flags & I2C_CLIENT_PEC) ? 2 : 1);
+- msg->flags &= ~I2C_M_RECV_LEN;
+ }
+
+ if (bus->buf_index < msg->len) {
+- bus->master_state = ASPEED_I2C_MASTER_RX;
+ command = ASPEED_I2CD_M_RX_CMD;
+- if (bus->buf_index + 1 == msg->len)
+- command |= ASPEED_I2CD_M_S_RX_CMD_LAST;
++ bus->master_state = ASPEED_I2C_MASTER_RX;
++ if (bus->buf_base) {
++ command |= ASPEED_I2CD_RX_BUFF_ENABLE;
++
++ if (msg->len - bus->buf_index >
++ bus->buf_size) {
++ len = bus->buf_size;
++ } else {
++ len = msg->len - bus->buf_index;
++ command |= ASPEED_I2CD_M_S_RX_CMD_LAST;
++ }
++
++ writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK,
++ len - 1) |
++ FIELD_PREP(ASPEED_I2CD_BUF_TX_COUNT_MASK,
++ 0) |
++ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK,
++ bus->buf_offset),
++ bus->base + ASPEED_I2C_BUF_CTRL_REG);
++ } else {
++ if (bus->buf_index + 1 == msg->len)
++ command |= ASPEED_I2CD_M_S_RX_CMD_LAST;
++ }
+ writel(command, bus->base + ASPEED_I2C_CMD_REG);
+ } else {
+ aspeed_i2c_next_msg_or_stop(bus);
+@@ -911,6 +1099,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus,
+ if (ret < 0)
+ return ret;
+
++ fun_ctrl_reg |= FIELD_PREP(ASPEED_I2CD_BUFFER_PAGE_SEL_MASK,
++ bus->buf_page);
++
+ if (of_property_read_bool(pdev->dev.of_node, "multi-master"))
+ bus->multi_master = true;
+ else
+@@ -972,16 +1163,15 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
+ {
+ const struct of_device_id *match;
+ struct aspeed_i2c_bus *bus;
++ bool sram_enabled = true;
+ struct clk *parent_clk;
+- struct resource *res;
+ int irq, ret;
+
+ bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ return -ENOMEM;
+
+- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+- bus->base = devm_ioremap_resource(&pdev->dev, res);
++ bus->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(bus->base))
+ return PTR_ERR(bus->base);
+
+@@ -1015,6 +1205,42 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
+ bus->get_clk_reg_val = (u32 (*)(struct device *, u32))
+ match->data;
+
++ /* Enable I2C SRAM in case of AST2500 */
++ if (of_device_is_compatible(pdev->dev.of_node,
++ "aspeed,ast2500-i2c-bus")) {
++ struct regmap *gr_regmap = syscon_regmap_lookup_by_compatible(
++ "aspeed,ast2500-i2c-gr");
++ if (IS_ERR(gr_regmap))
++ ret = PTR_ERR(gr_regmap);
++ else
++ ret = regmap_update_bits(gr_regmap,
++ ASPEED_I2CG_GLOBAL_CTRL_REG,
++ ASPEED_I2CG_SRAM_BUFFER_EN,
++ ASPEED_I2CG_SRAM_BUFFER_EN);
++
++ if (ret)
++ sram_enabled = false;
++ }
++
++ if (sram_enabled) {
++ struct resource *res = platform_get_resource(pdev,
++ IORESOURCE_MEM, 1);
++
++ if (res)
++ bus->buf_base = devm_ioremap_resource(&pdev->dev, res);
++
++ if (!IS_ERR_OR_NULL(bus->buf_base) && resource_size(res) >= 2) {
++ bus->buf_size = resource_size(res);
++ if (of_device_is_compatible(pdev->dev.of_node,
++ "aspeed,ast2400-i2c-bus")) {
++ bus->buf_page = ((res->start >> 8) &
++ GENMASK(3, 0)) - 8;
++ bus->buf_offset = (res->start >> 2) &
++ ASPEED_I2CD_BUF_OFFSET_MASK;
++ }
++ }
++ }
++
+ /* Initialize the I2C adapter */
+ spin_lock_init(&bus->lock);
+ init_completion(&bus->cmd_complete);
+@@ -1050,8 +1276,8 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
+
+ platform_set_drvdata(pdev, bus);
+
+- dev_info(bus->dev, "i2c bus %d registered, irq %d\n",
+- bus->adap.nr, irq);
++ dev_info(bus->dev, "i2c bus %d registered (%s mode), irq %d\n",
++ bus->adap.nr, bus->buf_base ? "buffer" : "byte", irq);
+
+ return 0;
+ }
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0062-i2c-aspeed-add-DMA-mode-transfer-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0062-i2c-aspeed-add-DMA-mode-transfer-support.patch
new file mode 100644
index 000000000..f3021d410
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0062-i2c-aspeed-add-DMA-mode-transfer-support.patch
@@ -0,0 +1,442 @@
+From 09aece99e18a0fd0612c865394424afa74050171 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Tue, 18 Jun 2019 08:47:50 -0700
+Subject: [PATCH] i2c: aspeed: add DMA mode transfer support
+
+This commit adds DMA mode transfer support.
+
+Only AST2500 supports DMA mode under some limitations:
+I2C is sharing the DMA H/W with UHCI host controller and MCTP
+controller. Since those controllers operate with DMA mode only, I2C
+has to use buffer mode or byte mode instead if one of those
+controllers is enabled. Also make sure that if SD/eMMC or Port80
+snoop uses DMA mode instead of PIO or FIFO respectively, I2C can't
+use DMA mode.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ .../devicetree/bindings/i2c/i2c-aspeed.txt | 25 +++
+ drivers/i2c/busses/i2c-aspeed.c | 231 +++++++++++++++++++--
+ 2 files changed, 241 insertions(+), 15 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
+index 0ff3539cee95..d3f4a39f7ba6 100644
+--- a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
++++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
+@@ -22,6 +22,16 @@ Optional Properties:
+ - bus-timeout-ms: bus timeout in milliseconds defaults to 1 second when not
+ specified.
+ - #retries : Number of retries for master transfer.
++- aspeed,dma-buf-size : size of DMA buffer (from 2 to 4095 in case of AST2500)
++ Only AST2500 supports DMA mode under some limitations:
++ I2C is sharing the DMA H/W with UHCI host controller
++ and MCTP controller. Since those controllers operate
++ with DMA mode only, I2C has to use buffer mode or byte
++ mode instead if one of those controllers is enabled.
++ Also make sure that if SD/eMMC or Port80 snoop uses
++ DMA mode instead of PIO or FIFO respectively, I2C
++ can't use DMA mode. IF both DMA and buffer modes are
++ enabled, DMA mode will be selected.
+
+ Example:
+
+@@ -74,4 +84,19 @@ i2c {
+ interrupts = <1>;
+ interrupt-parent = <&i2c_ic>;
+ };
++
++ /* DMA mode transfer enabled */
++ i2c2: i2c-bus@c0 {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ #interrupt-cells = <1>;
++ reg = <0xc0 0x40>;
++ aspeed,dma-buf-size = <4095>;
++ compatible = "aspeed,ast2500-i2c-bus";
++ clocks = <&syscon ASPEED_CLK_APB>;
++ resets = <&syscon ASPEED_RESET_I2C>;
++ bus-frequency = <100000>;
++ interrupts = <2>;
++ interrupt-parent = <&i2c_ic>;
++ };
+ };
+diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
+index e37f0764d184..4567ec3498dc 100644
+--- a/drivers/i2c/busses/i2c-aspeed.c
++++ b/drivers/i2c/busses/i2c-aspeed.c
+@@ -10,6 +10,8 @@
+ #include <linux/bitfield.h>
+ #include <linux/clk.h>
+ #include <linux/completion.h>
++#include <linux/dma-mapping.h>
++#include <linux/dmapool.h>
+ #include <linux/err.h>
+ #include <linux/errno.h>
+ #include <linux/i2c.h>
+@@ -47,6 +49,8 @@
+ #define ASPEED_I2C_DEV_ADDR_REG 0x18
+ #define ASPEED_I2C_BUF_CTRL_REG 0x1c
+ #define ASPEED_I2C_BYTE_BUF_REG 0x20
++#define ASPEED_I2C_DMA_ADDR_REG 0x24
++#define ASPEED_I2C_DMA_LEN_REG 0x28
+
+ /* Device Register Definition */
+ /* 0x00 : I2CD Function Control Register */
+@@ -110,6 +114,8 @@
+ #define ASPEED_I2CD_BUS_RECOVER_CMD BIT(11)
+
+ /* Command Bit */
++#define ASPEED_I2CD_RX_DMA_ENABLE BIT(9)
++#define ASPEED_I2CD_TX_DMA_ENABLE BIT(8)
+ #define ASPEED_I2CD_RX_BUFF_ENABLE BIT(7)
+ #define ASPEED_I2CD_TX_BUFF_ENABLE BIT(6)
+ #define ASPEED_I2CD_M_STOP_CMD BIT(5)
+@@ -129,6 +135,14 @@
+ #define ASPEED_I2CD_BUF_TX_COUNT_MASK GENMASK(15, 8)
+ #define ASPEED_I2CD_BUF_OFFSET_MASK GENMASK(5, 0)
+
++/* 0x24 : I2CD DMA Mode Buffer Address Register */
++#define ASPEED_I2CD_DMA_ADDR_MASK GENMASK(31, 2)
++#define ASPEED_I2CD_DMA_ALIGN 4
++
++/* 0x28 : I2CD DMA Transfer Length Register */
++#define ASPEED_I2CD_DMA_LEN_SHIFT 0
++#define ASPEED_I2CD_DMA_LEN_MASK GENMASK(11, 0)
++
+ enum aspeed_i2c_master_state {
+ ASPEED_I2C_MASTER_INACTIVE,
+ ASPEED_I2C_MASTER_PENDING,
+@@ -179,6 +193,12 @@ struct aspeed_i2c_bus {
+ size_t buf_size;
+ u8 buf_offset;
+ u8 buf_page;
++ /* DMA mode */
++ struct dma_pool *dma_pool;
++ dma_addr_t dma_handle;
++ u8 *dma_buf;
++ size_t dma_buf_size;
++ size_t dma_len;
+ #if IS_ENABLED(CONFIG_I2C_SLAVE)
+ struct i2c_client *slave;
+ enum aspeed_i2c_slave_state slave_state;
+@@ -298,9 +318,13 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+
+ /* Slave was sent something. */
+ if (irq_status & ASPEED_I2CD_INTR_RX_DONE) {
+- if (bus->buf_base &&
++ if (bus->dma_buf &&
+ bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED &&
+ !(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP))
++ value = bus->dma_buf[0];
++ else if (bus->buf_base &&
++ bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED &&
++ !(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP))
+ value = readb(bus->buf_base);
+ else
+ value = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8;
+@@ -320,7 +344,18 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) {
+ if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED &&
+ irq_status & ASPEED_I2CD_INTR_RX_DONE) {
+- if (bus->buf_base) {
++ if (bus->dma_buf) {
++ len = bus->dma_buf_size -
++ FIELD_GET(ASPEED_I2CD_DMA_LEN_MASK,
++ readl(bus->base +
++ ASPEED_I2C_DMA_LEN_REG));
++ for (i = 0; i < len; i++) {
++ value = bus->dma_buf[i];
++ i2c_slave_event(slave,
++ I2C_SLAVE_WRITE_RECEIVED,
++ &value);
++ }
++ } else if (bus->buf_base) {
+ len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK,
+ readl(bus->base +
+ ASPEED_I2C_BUF_CTRL_REG));
+@@ -364,7 +399,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ case ASPEED_I2C_SLAVE_WRITE_REQUESTED:
+ bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED;
+ i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
+- if (bus->buf_base) {
++ if (bus->dma_buf) {
++ writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK,
++ bus->base + ASPEED_I2C_DMA_ADDR_REG);
++ writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK,
++ bus->dma_buf_size),
++ bus->base + ASPEED_I2C_DMA_LEN_REG);
++ writel(ASPEED_I2CD_RX_DMA_ENABLE,
++ bus->base + ASPEED_I2C_CMD_REG);
++ } else if (bus->buf_base) {
+ writel(FIELD_PREP(ASPEED_I2CD_BUF_RX_SIZE_MASK,
+ bus->buf_size - 1) |
+ FIELD_PREP(ASPEED_I2CD_BUF_OFFSET_MASK,
+@@ -376,7 +419,25 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ break;
+ case ASPEED_I2C_SLAVE_WRITE_RECEIVED:
+ i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value);
+- if (bus->buf_base) {
++ if (bus->dma_buf) {
++ len = bus->dma_buf_size -
++ FIELD_GET(ASPEED_I2CD_DMA_LEN_MASK,
++ readl(bus->base +
++ ASPEED_I2C_DMA_LEN_REG));
++ for (i = 1; i < len; i++) {
++ value = bus->dma_buf[i];
++ i2c_slave_event(slave,
++ I2C_SLAVE_WRITE_RECEIVED,
++ &value);
++ }
++ writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK,
++ bus->base + ASPEED_I2C_DMA_ADDR_REG);
++ writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK,
++ bus->dma_buf_size),
++ bus->base + ASPEED_I2C_DMA_LEN_REG);
++ writel(ASPEED_I2CD_RX_DMA_ENABLE,
++ bus->base + ASPEED_I2C_CMD_REG);
++ } else if (bus->buf_base) {
+ len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK,
+ readl(bus->base +
+ ASPEED_I2C_BUF_CTRL_REG));
+@@ -440,7 +501,23 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus)
+ if (msg->flags & I2C_M_RD) {
+ command |= ASPEED_I2CD_M_RX_CMD;
+
+- if (bus->buf_base && !(msg->flags & I2C_M_RECV_LEN)) {
++ if (bus->dma_buf && !(msg->flags & I2C_M_RECV_LEN)) {
++ command |= ASPEED_I2CD_RX_DMA_ENABLE;
++
++ if (msg->len > bus->dma_buf_size) {
++ len = bus->dma_buf_size;
++ } else {
++ len = msg->len;
++ command |= ASPEED_I2CD_M_S_RX_CMD_LAST;
++ }
++
++ writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK,
++ bus->base + ASPEED_I2C_DMA_ADDR_REG);
++ writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK,
++ len),
++ bus->base + ASPEED_I2C_DMA_LEN_REG);
++ bus->dma_len = len;
++ } else if (bus->buf_base && !(msg->flags & I2C_M_RECV_LEN)) {
+ command |= ASPEED_I2CD_RX_BUFF_ENABLE;
+
+ if (msg->len > bus->buf_size) {
+@@ -461,7 +538,26 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus)
+ command |= ASPEED_I2CD_M_S_RX_CMD_LAST;
+ }
+ } else {
+- if (bus->buf_base) {
++ if (bus->dma_buf) {
++ command |= ASPEED_I2CD_TX_DMA_ENABLE;
++
++ if (msg->len + 1 > bus->dma_buf_size)
++ len = bus->dma_buf_size;
++ else
++ len = msg->len + 1;
++
++ bus->dma_buf[0] = slave_addr;
++ memcpy(bus->dma_buf + 1, msg->buf, len);
++
++ bus->buf_index = len - 1;
++
++ writel(bus->dma_handle & ASPEED_I2CD_DMA_ADDR_MASK,
++ bus->base + ASPEED_I2C_DMA_ADDR_REG);
++ writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK,
++ len),
++ bus->base + ASPEED_I2C_DMA_LEN_REG);
++ bus->dma_len = len;
++ } else if (bus->buf_base) {
+ int i;
+
+ command |= ASPEED_I2CD_TX_BUFF_ENABLE;
+@@ -497,7 +593,8 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus)
+ }
+ }
+
+- if (!(command & ASPEED_I2CD_TX_BUFF_ENABLE))
++ if (!(command & (ASPEED_I2CD_TX_BUFF_ENABLE |
++ ASPEED_I2CD_TX_DMA_ENABLE)))
+ writel(slave_addr, bus->base + ASPEED_I2C_BYTE_BUF_REG);
+ writel(command, bus->base + ASPEED_I2C_CMD_REG);
+ }
+@@ -653,7 +750,28 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ if (bus->buf_index < msg->len) {
+ command = ASPEED_I2CD_M_TX_CMD;
+
+- if (bus->buf_base) {
++ if (bus->dma_buf) {
++ command |= ASPEED_I2CD_TX_DMA_ENABLE;
++
++ if (msg->len - bus->buf_index >
++ bus->dma_buf_size)
++ len = bus->dma_buf_size;
++ else
++ len = msg->len - bus->buf_index;
++
++ memcpy(bus->dma_buf, msg->buf + bus->buf_index,
++ len);
++
++ bus->buf_index += len;
++
++ writel(bus->dma_handle &
++ ASPEED_I2CD_DMA_ADDR_MASK,
++ bus->base + ASPEED_I2C_DMA_ADDR_REG);
++ writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK,
++ len),
++ bus->base + ASPEED_I2C_DMA_LEN_REG);
++ bus->dma_len = len;
++ } else if (bus->buf_base) {
+ u8 wbuf[4];
+ int i;
+
+@@ -704,7 +822,15 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ }
+ irq_handled |= ASPEED_I2CD_INTR_RX_DONE;
+
+- if (bus->buf_base && !(msg->flags & I2C_M_RECV_LEN)) {
++ if (bus->dma_buf && !(msg->flags & I2C_M_RECV_LEN)) {
++ len = bus->dma_len -
++ FIELD_GET(ASPEED_I2CD_DMA_LEN_MASK,
++ readl(bus->base +
++ ASPEED_I2C_DMA_LEN_REG));
++
++ memcpy(msg->buf + bus->buf_index, bus->dma_buf, len);
++ bus->buf_index += len;
++ } else if (bus->buf_base && !(msg->flags & I2C_M_RECV_LEN)) {
+ len = FIELD_GET(ASPEED_I2CD_BUF_RX_COUNT_MASK,
+ readl(bus->base +
+ ASPEED_I2C_BUF_CTRL_REG));
+@@ -732,7 +858,25 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ if (bus->buf_index < msg->len) {
+ command = ASPEED_I2CD_M_RX_CMD;
+ bus->master_state = ASPEED_I2C_MASTER_RX;
+- if (bus->buf_base) {
++ if (bus->dma_buf) {
++ command |= ASPEED_I2CD_RX_DMA_ENABLE;
++
++ if (msg->len - bus->buf_index >
++ bus->dma_buf_size) {
++ len = bus->dma_buf_size;
++ } else {
++ len = msg->len - bus->buf_index;
++ command |= ASPEED_I2CD_M_S_RX_CMD_LAST;
++ }
++
++ writel(bus->dma_handle &
++ ASPEED_I2CD_DMA_ADDR_MASK,
++ bus->base + ASPEED_I2C_DMA_ADDR_REG);
++ writel(FIELD_PREP(ASPEED_I2CD_DMA_LEN_MASK,
++ len),
++ bus->base + ASPEED_I2C_DMA_LEN_REG);
++ bus->dma_len = len;
++ } else if (bus->buf_base) {
+ command |= ASPEED_I2CD_RX_BUFF_ENABLE;
+
+ if (msg->len - bus->buf_index >
+@@ -1222,7 +1366,51 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
+ sram_enabled = false;
+ }
+
+- if (sram_enabled) {
++ /*
++ * Only AST2500 supports DMA mode under some limitations:
++ * I2C is sharing the DMA H/W with UHCI host controller and MCTP
++ * controller. Since those controllers operate with DMA mode only, I2C
++ * has to use buffer mode or byte mode instead if one of those
++ * controllers is enabled. Also make sure that if SD/eMMC or Port80
++ * snoop uses DMA mode instead of PIO or FIFO respectively, I2C can't
++ * use DMA mode.
++ */
++ if (sram_enabled && !IS_ENABLED(CONFIG_USB_UHCI_ASPEED) &&
++ of_device_is_compatible(pdev->dev.of_node,
++ "aspeed,ast2500-i2c-bus")) {
++ u32 dma_len_max = ASPEED_I2CD_DMA_LEN_MASK >>
++ ASPEED_I2CD_DMA_LEN_SHIFT;
++
++ ret = device_property_read_u32(&pdev->dev,
++ "aspeed,dma-buf-size",
++ &bus->dma_buf_size);
++ if (!ret && bus->dma_buf_size > dma_len_max)
++ bus->dma_buf_size = dma_len_max;
++ }
++
++ if (bus->dma_buf_size) {
++ if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
++ dev_warn(&pdev->dev, "No suitable DMA available\n");
++ } else {
++ bus->dma_pool = dma_pool_create("i2c-aspeed",
++ &pdev->dev,
++ bus->dma_buf_size,
++ ASPEED_I2CD_DMA_ALIGN,
++ 0);
++ if (bus->dma_pool)
++ bus->dma_buf = dma_pool_alloc(bus->dma_pool,
++ GFP_KERNEL,
++ &bus->dma_handle);
++
++ if (!bus->dma_buf) {
++ dev_warn(&pdev->dev,
++ "Cannot allocate DMA buffer\n");
++ dma_pool_destroy(bus->dma_pool);
++ }
++ }
++ }
++
++ if (!bus->dma_buf && sram_enabled) {
+ struct resource *res = platform_get_resource(pdev,
+ IORESOURCE_MEM, 1);
+
+@@ -1262,24 +1450,33 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
+ */
+ ret = aspeed_i2c_init(bus, pdev);
+ if (ret < 0)
+- return ret;
++ goto out_free_dma_buf;
+
+ irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+ ret = devm_request_irq(&pdev->dev, irq, aspeed_i2c_bus_irq,
+ 0, dev_name(&pdev->dev), bus);
+ if (ret < 0)
+- return ret;
++ goto out_free_dma_buf;
+
+ ret = i2c_add_adapter(&bus->adap);
+ if (ret < 0)
+- return ret;
++ goto out_free_dma_buf;
+
+ platform_set_drvdata(pdev, bus);
+
+ dev_info(bus->dev, "i2c bus %d registered (%s mode), irq %d\n",
+- bus->adap.nr, bus->buf_base ? "buffer" : "byte", irq);
++ bus->adap.nr, bus->dma_buf ? "dma" :
++ bus->buf_base ? "buffer" : "byte",
++ irq);
+
+ return 0;
++
++out_free_dma_buf:
++ if (bus->dma_buf)
++ dma_pool_free(bus->dma_pool, bus->dma_buf, bus->dma_handle);
++ dma_pool_destroy(bus->dma_pool);
++
++ return ret;
+ }
+
+ static int aspeed_i2c_remove_bus(struct platform_device *pdev)
+@@ -1297,6 +1494,10 @@ static int aspeed_i2c_remove_bus(struct platform_device *pdev)
+
+ reset_control_assert(bus->rst);
+
++ if (bus->dma_buf)
++ dma_pool_free(bus->dma_pool, bus->dma_buf, bus->dma_handle);
++ dma_pool_destroy(bus->dma_pool);
++
+ i2c_del_adapter(&bus->adap);
+
+ return 0;
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0063-i2c-aspeed-add-general-call-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0063-i2c-aspeed-add-general-call-support.patch
new file mode 100644
index 000000000..381197a64
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0063-i2c-aspeed-add-general-call-support.patch
@@ -0,0 +1,180 @@
+From f9f2e586985f90197b30208599bd37a9fd7a7f63 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Wed, 1 May 2019 13:27:34 -0700
+Subject: [PATCH] i2c: aspeed: add general call support
+
+This commit adds general call support into Aspeed I2C driver.
+This is downstream only customization so it should not go into
+upstream.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ .../devicetree/bindings/i2c/i2c-aspeed.txt | 1 +
+ drivers/i2c/busses/i2c-aspeed.c | 39 ++++++++++++++++++++++
+ drivers/i2c/i2c-slave-mqueue.c | 4 ++-
+ include/linux/i2c.h | 1 +
+ 4 files changed, 44 insertions(+), 1 deletion(-)
+
+diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
+index d3f4a39f7ba6..c1ee99398517 100644
+--- a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
++++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
+@@ -19,6 +19,7 @@ Optional Properties:
+ - bus-frequency : frequency of the bus clock in Hz defaults to 100 kHz when not
+ specified
+ - multi-master : states that there is another master active on this bus.
++- general-call : enables general call receiving.
+ - bus-timeout-ms: bus timeout in milliseconds defaults to 1 second when not
+ specified.
+ - #retries : Number of retries for master transfer.
+diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
+index 4567ec3498dc..3e72068f6a2b 100644
+--- a/drivers/i2c/busses/i2c-aspeed.c
++++ b/drivers/i2c/busses/i2c-aspeed.c
+@@ -59,6 +59,7 @@
+ #define ASPEED_I2CD_SDA_DRIVE_1T_EN BIT(8)
+ #define ASPEED_I2CD_M_SDA_DRIVE_1T_EN BIT(7)
+ #define ASPEED_I2CD_M_HIGH_SPEED_EN BIT(6)
++#define ASPEED_I2CD_GCALL_EN BIT(2)
+ #define ASPEED_I2CD_SLAVE_EN BIT(1)
+ #define ASPEED_I2CD_MASTER_EN BIT(0)
+
+@@ -83,6 +84,7 @@
+ */
+ #define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT BIT(14)
+ #define ASPEED_I2CD_INTR_BUS_RECOVER_DONE BIT(13)
++#define ASPEED_I2CD_INTR_GCALL_ADDR BIT(8)
+ #define ASPEED_I2CD_INTR_SLAVE_MATCH BIT(7)
+ #define ASPEED_I2CD_INTR_SCL_TIMEOUT BIT(6)
+ #define ASPEED_I2CD_INTR_ABNORMAL BIT(5)
+@@ -161,6 +163,8 @@ enum aspeed_i2c_slave_state {
+ ASPEED_I2C_SLAVE_READ_PROCESSED,
+ ASPEED_I2C_SLAVE_WRITE_REQUESTED,
+ ASPEED_I2C_SLAVE_WRITE_RECEIVED,
++ ASPEED_I2C_SLAVE_GCALL_START,
++ ASPEED_I2C_SLAVE_GCALL_REQUESTED,
+ ASPEED_I2C_SLAVE_STOP,
+ };
+
+@@ -202,6 +206,8 @@ struct aspeed_i2c_bus {
+ #if IS_ENABLED(CONFIG_I2C_SLAVE)
+ struct i2c_client *slave;
+ enum aspeed_i2c_slave_state slave_state;
++ /* General call */
++ bool general_call;
+ #endif /* CONFIG_I2C_SLAVE */
+ };
+
+@@ -309,6 +315,12 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ bus->slave_state = ASPEED_I2C_SLAVE_START;
+ }
+
++ /* General call was requested, restart state machine. */
++ if (irq_status & ASPEED_I2CD_INTR_GCALL_ADDR) {
++ irq_handled |= ASPEED_I2CD_INTR_GCALL_ADDR;
++ bus->slave_state = ASPEED_I2C_SLAVE_GCALL_START;
++ }
++
+ /* Slave is not currently active, irq was for someone else. */
+ if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE)
+ return irq_handled;
+@@ -336,6 +348,21 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ else
+ bus->slave_state =
+ ASPEED_I2C_SLAVE_WRITE_REQUESTED;
++ } else if (bus->slave_state == ASPEED_I2C_SLAVE_GCALL_START) {
++ /*
++ * I2C spec defines the second byte meaning like below.
++ * 0x06 : Reset and write programmable part of slave
++ * address by hardware.
++ * 0x04 : Write programmable part of slave address by
++ * hardware.
++ * 0x00 : No allowed.
++ *
++ * But in OpenBMC, we are going to use this
++ * 'General call' feature for IPMB message broadcasting
++ * so it delivers all data as is without any specific
++ * handling of the second byte.
++ */
++ bus->slave_state = ASPEED_I2C_SLAVE_GCALL_REQUESTED;
+ }
+ irq_handled |= ASPEED_I2CD_INTR_RX_DONE;
+ }
+@@ -456,11 +483,16 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ bus->base + ASPEED_I2C_CMD_REG);
+ }
+ break;
++ case ASPEED_I2C_SLAVE_GCALL_REQUESTED:
++ bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED;
++ i2c_slave_event(slave, I2C_SLAVE_GCALL_REQUESTED, &value);
++ 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:
++ case ASPEED_I2C_SLAVE_GCALL_START:
+ /* Slave was just started. Waiting for the next event. */;
+ break;
+ default:
+@@ -1071,6 +1103,8 @@ static void __aspeed_i2c_reg_slave(struct aspeed_i2c_bus *bus, u16 slave_addr)
+ /* Turn on slave mode. */
+ func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG);
+ func_ctrl_reg_val |= ASPEED_I2CD_SLAVE_EN;
++ if (bus->general_call)
++ func_ctrl_reg_val |= ASPEED_I2CD_GCALL_EN;
+ writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG);
+ }
+
+@@ -1109,6 +1143,8 @@ static int aspeed_i2c_unreg_slave(struct i2c_client *client)
+ /* Turn off slave mode. */
+ func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG);
+ func_ctrl_reg_val &= ~ASPEED_I2CD_SLAVE_EN;
++ if (bus->general_call)
++ func_ctrl_reg_val &= ~ASPEED_I2CD_GCALL_EN;
+ writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG);
+
+ bus->slave = NULL;
+@@ -1256,6 +1292,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus,
+ bus->base + ASPEED_I2C_FUN_CTRL_REG);
+
+ #if IS_ENABLED(CONFIG_I2C_SLAVE)
++ if (of_property_read_bool(pdev->dev.of_node, "general-call"))
++ bus->general_call = true;
++
+ /* If slave has already been registered, re-enable it. */
+ if (bus->slave)
+ __aspeed_i2c_reg_slave(bus, bus->slave->addr);
+diff --git a/drivers/i2c/i2c-slave-mqueue.c b/drivers/i2c/i2c-slave-mqueue.c
+index 2c7a6038409c..1d4db584b393 100644
+--- a/drivers/i2c/i2c-slave-mqueue.c
++++ b/drivers/i2c/i2c-slave-mqueue.c
+@@ -56,10 +56,12 @@ static int i2c_slave_mqueue_callback(struct i2c_client *client,
+
+ switch (event) {
+ case I2C_SLAVE_WRITE_REQUESTED:
++ case I2C_SLAVE_GCALL_REQUESTED:
+ mq->truncated = 0;
+
+ msg->len = 1;
+- msg->buf[0] = client->addr << 1;
++ msg->buf[0] = event == I2C_SLAVE_GCALL_REQUESTED ?
++ 0 : client->addr << 1;
+ break;
+
+ case I2C_SLAVE_WRITE_RECEIVED:
+diff --git a/include/linux/i2c.h b/include/linux/i2c.h
+index 92c795ce9081..1e5c74888160 100644
+--- a/include/linux/i2c.h
++++ b/include/linux/i2c.h
+@@ -365,6 +365,7 @@ enum i2c_slave_event {
+ I2C_SLAVE_WRITE_REQUESTED,
+ I2C_SLAVE_READ_PROCESSED,
+ I2C_SLAVE_WRITE_RECEIVED,
++ I2C_SLAVE_GCALL_REQUESTED,
+ I2C_SLAVE_STOP,
+ };
+
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0064-set-idle-disconnect-to-true-in-all-cases.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0064-set-idle-disconnect-to-true-in-all-cases.patch
new file mode 100644
index 000000000..925880eff
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0064-set-idle-disconnect-to-true-in-all-cases.patch
@@ -0,0 +1,34 @@
+From 7854a5e094ac49bebf9b2bfdd44db2f8cdd37543 Mon Sep 17 00:00:00 2001
+From: James Feist <james.feist@linux.intel.com>
+Date: Fri, 31 May 2019 15:05:13 -0700
+Subject: [PATCH] set idle-disconnect to true in all cases
+
+From sysfs this parameter can't be set. We want the
+muxes to clean themselves up if possible. Set this to
+true.
+
+Signed-off-by: James Feist <james.feist@linux.intel.com>
+---
+ drivers/i2c/muxes/i2c-mux-pca954x.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
+index 923aa3a5a3dc..084c10951890 100644
+--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
++++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
+@@ -474,8 +474,12 @@ static int pca954x_probe(struct i2c_client *client,
+ data->last_chan = 0; /* force the first selection */
+ data->idle_state = MUX_IDLE_AS_IS;
+
++#if 1 /* Forcibly set the self-disconnect flag */
++ idle_disconnect_dt = true;
++#else
+ idle_disconnect_dt = np &&
+ of_property_read_bool(np, "i2c-mux-idle-disconnect");
++#endif
+ if (idle_disconnect_dt)
+ data->idle_state = MUX_IDLE_DISCONNECT;
+
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0068-i2c-aspeed-add-H-W-timeout-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0068-i2c-aspeed-add-H-W-timeout-support.patch
new file mode 100644
index 000000000..ba564e695
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0068-i2c-aspeed-add-H-W-timeout-support.patch
@@ -0,0 +1,191 @@
+From 25a38287274f9c39eb8355d51ba06203efdb07aa Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Thu, 11 Jul 2019 13:53:34 -0700
+Subject: [PATCH] i2c: aspeed: add H/W timeout support
+
+This commit adds I2C H/W timeout support.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/i2c/busses/i2c-aspeed.c | 82 ++++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 76 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
+index 127bc69952ca..542b0f4017eb 100644
+--- a/drivers/i2c/busses/i2c-aspeed.c
++++ b/drivers/i2c/busses/i2c-aspeed.c
+@@ -55,6 +55,7 @@
+ /* Device Register Definition */
+ /* 0x00 : I2CD Function Control Register */
+ #define ASPEED_I2CD_BUFFER_PAGE_SEL_MASK GENMASK(22, 20)
++#define ASPEED_I2CD_BUS_AUTO_RECOVERY_EN BIT(17)
+ #define ASPEED_I2CD_MULTI_MASTER_DIS BIT(15)
+ #define ASPEED_I2CD_SDA_DRIVE_1T_EN BIT(8)
+ #define ASPEED_I2CD_M_SDA_DRIVE_1T_EN BIT(7)
+@@ -71,10 +72,14 @@
+ #define ASPEED_I2CD_TIME_SCL_HIGH_MASK GENMASK(19, 16)
+ #define ASPEED_I2CD_TIME_SCL_LOW_SHIFT 12
+ #define ASPEED_I2CD_TIME_SCL_LOW_MASK GENMASK(15, 12)
++#define ASPEED_I2CD_TIME_TIMEOUT_BASE_DIVISOR_SHIFT 8
++#define ASPEED_I2CD_TIME_TIMEOUT_BASE_DIVISOR_MASK GENMASK(9, 8)
+ #define ASPEED_I2CD_TIME_BASE_DIVISOR_MASK GENMASK(3, 0)
+ #define ASPEED_I2CD_TIME_SCL_REG_MAX GENMASK(3, 0)
++
+ /* 0x08 : I2CD Clock and AC Timing Control Register #2 */
+-#define ASPEED_NO_TIMEOUT_CTRL 0
++#define ASPEED_I2CD_TIMEOUT_CYCLES_SHIFT 0
++#define ASPEED_I2CD_TIMEOUT_CYCLES_MASK GENMASK(4, 0)
+
+ /* 0x0c : I2CD Interrupt Control Register &
+ * 0x10 : I2CD Interrupt Status Register
+@@ -82,6 +87,7 @@
+ * These share bit definitions, so use the same values for the enable &
+ * status bits.
+ */
++#define ASPEED_I2CD_INTR_SLAVE_INACTIVE_TIMEOUT BIT(15)
+ #define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT BIT(14)
+ #define ASPEED_I2CD_INTR_BUS_RECOVER_DONE BIT(13)
+ #define ASPEED_I2CD_INTR_GCALL_ADDR BIT(8)
+@@ -98,8 +104,11 @@
+ ASPEED_I2CD_INTR_SCL_TIMEOUT | \
+ ASPEED_I2CD_INTR_ABNORMAL | \
+ ASPEED_I2CD_INTR_ARBIT_LOSS)
++#define ASPEED_I2CD_INTR_SLAVE_ERRORS \
++ ASPEED_I2CD_INTR_SLAVE_INACTIVE_TIMEOUT
+ #define ASPEED_I2CD_INTR_ALL \
+- (ASPEED_I2CD_INTR_SDA_DL_TIMEOUT | \
++ (ASPEED_I2CD_INTR_SLAVE_INACTIVE_TIMEOUT | \
++ ASPEED_I2CD_INTR_SDA_DL_TIMEOUT | \
+ ASPEED_I2CD_INTR_BUS_RECOVER_DONE | \
+ ASPEED_I2CD_INTR_SCL_TIMEOUT | \
+ ASPEED_I2CD_INTR_ABNORMAL | \
+@@ -180,6 +189,7 @@ struct aspeed_i2c_bus {
+ u32 divisor);
+ unsigned long parent_clk_frequency;
+ u32 bus_frequency;
++ u32 hw_timeout_ms;
+ /* Transaction state. */
+ enum aspeed_i2c_master_state master_state;
+ struct i2c_msg *msgs;
+@@ -297,6 +307,14 @@ static int aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus)
+ }
+
+ #if IS_ENABLED(CONFIG_I2C_SLAVE)
++static int aspeed_i2c_check_slave_error(u32 irq_status)
++{
++ if (irq_status & ASPEED_I2CD_INTR_SLAVE_INACTIVE_TIMEOUT)
++ return -EIO;
++
++ return 0;
++}
++
+ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ {
+ u32 command, irq_handled = 0;
+@@ -307,6 +325,14 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ if (!slave)
+ return 0;
+
++ if (aspeed_i2c_check_slave_error(irq_status)) {
++ dev_dbg(bus->dev, "received slave error interrupt: 0x%08x\n",
++ irq_status);
++ irq_handled |= (irq_status & ASPEED_I2CD_INTR_SLAVE_ERRORS);
++ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
++ return irq_handled;
++ }
++
+ command = readl(bus->base + ASPEED_I2C_CMD_REG);
+
+ /* Slave was requested, restart state machine. */
+@@ -649,7 +675,7 @@ static void aspeed_i2c_next_msg_or_stop(struct aspeed_i2c_bus *bus)
+ }
+ }
+
+-static int aspeed_i2c_is_irq_error(u32 irq_status)
++static int aspeed_i2c_check_master_error(u32 irq_status)
+ {
+ if (irq_status & ASPEED_I2CD_INTR_ARBIT_LOSS)
+ return -EAGAIN;
+@@ -680,9 +706,9 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ * should clear the command queue effectively taking us back to the
+ * INACTIVE state.
+ */
+- ret = aspeed_i2c_is_irq_error(irq_status);
++ ret = aspeed_i2c_check_master_error(irq_status);
+ if (ret) {
+- dev_dbg(bus->dev, "received error interrupt: 0x%08x\n",
++ dev_dbg(bus->dev, "received master error interrupt: 0x%08x\n",
+ irq_status);
+ irq_handled |= (irq_status & ASPEED_I2CD_INTR_MASTER_ERRORS);
+ if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) {
+@@ -1251,6 +1277,7 @@ static u32 aspeed_i2c_25xx_get_clk_reg_val(struct device *dev, u32 divisor)
+ /* precondition: bus.lock has been acquired. */
+ static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus)
+ {
++ u32 timeout_base_divisor, timeout_tick_us, timeout_cycles;
+ u32 divisor, clk_reg_val;
+
+ divisor = DIV_ROUND_UP(bus->parent_clk_frequency, bus->bus_frequency);
+@@ -1259,8 +1286,46 @@ static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus)
+ ASPEED_I2CD_TIME_THDSTA_MASK |
+ ASPEED_I2CD_TIME_TACST_MASK);
+ clk_reg_val |= bus->get_clk_reg_val(bus->dev, divisor);
++
++ if (bus->hw_timeout_ms) {
++ u8 div_max = ASPEED_I2CD_TIME_TIMEOUT_BASE_DIVISOR_MASK >>
++ ASPEED_I2CD_TIME_TIMEOUT_BASE_DIVISOR_SHIFT;
++ u8 cycles_max = ASPEED_I2CD_TIMEOUT_CYCLES_MASK >>
++ ASPEED_I2CD_TIMEOUT_CYCLES_SHIFT;
++
++ timeout_base_divisor = 0;
++
++ do {
++ timeout_tick_us = 1000 * (16384 <<
++ (timeout_base_divisor << 1)) /
++ (bus->parent_clk_frequency / 1000);
++
++ if (timeout_base_divisor == div_max ||
++ timeout_tick_us * ASPEED_I2CD_TIMEOUT_CYCLES_MASK >=
++ bus->hw_timeout_ms * 1000)
++ break;
++ } while (timeout_base_divisor++ < div_max);
++
++ if (timeout_tick_us) {
++ timeout_cycles = DIV_ROUND_UP(bus->hw_timeout_ms * 1000,
++ timeout_tick_us);
++ if (timeout_cycles == 0)
++ timeout_cycles = 1;
++ else if (timeout_cycles > cycles_max)
++ timeout_cycles = cycles_max;
++ } else {
++ timeout_cycles = 0;
++ }
++ } else {
++ timeout_base_divisor = 0;
++ timeout_cycles = 0;
++ }
++
++ clk_reg_val |= FIELD_PREP(ASPEED_I2CD_TIME_TIMEOUT_BASE_DIVISOR_MASK,
++ timeout_base_divisor);
++
+ writel(clk_reg_val, bus->base + ASPEED_I2C_AC_TIMING_REG1);
+- writel(ASPEED_NO_TIMEOUT_CTRL, bus->base + ASPEED_I2C_AC_TIMING_REG2);
++ writel(timeout_cycles, bus->base + ASPEED_I2C_AC_TIMING_REG2);
+
+ return 0;
+ }
+@@ -1275,6 +1340,11 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus,
+ /* Disable everything. */
+ writel(0, bus->base + ASPEED_I2C_FUN_CTRL_REG);
+
++ device_property_read_u32(&pdev->dev, "aspeed,hw-timeout-ms",
++ &bus->hw_timeout_ms);
++ if (bus->hw_timeout_ms)
++ fun_ctrl_reg |= ASPEED_I2CD_BUS_AUTO_RECOVERY_EN;
++
+ ret = aspeed_i2c_init_clk(bus);
+ if (ret < 0)
+ return ret;
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0069-i2c-aspeed-add-SLAVE_ADDR_RECEIVED_PENDING-interrupt.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0069-i2c-aspeed-add-SLAVE_ADDR_RECEIVED_PENDING-interrupt.patch
new file mode 100644
index 000000000..8fc35243c
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0069-i2c-aspeed-add-SLAVE_ADDR_RECEIVED_PENDING-interrupt.patch
@@ -0,0 +1,50 @@
+From 6ffb52e1f1d80fd3116fccef045bcdc78d2d361c Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Thu, 11 Jul 2019 14:04:39 -0700
+Subject: [PATCH] i2c: aspeed: add SLAVE_ADDR_RECEIVED_PENDING interrupt
+ handling
+
+If a peer master sends messages too quickly before it processes
+previous slave DMA data handling, this indicator will be set. It's
+just a indicator and driver can't recover this case so just ignore
+it.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/i2c/busses/i2c-aspeed.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
+index bcc354d11e29..0070366e9d6d 100644
+--- a/drivers/i2c/busses/i2c-aspeed.c
++++ b/drivers/i2c/busses/i2c-aspeed.c
+@@ -87,6 +87,7 @@
+ * These share bit definitions, so use the same values for the enable &
+ * status bits.
+ */
++#define ASPEED_I2CD_INTR_SLAVE_ADDR_RECEIVED_PENDING BIT(30)
+ #define ASPEED_I2CD_INTR_SLAVE_INACTIVE_TIMEOUT BIT(15)
+ #define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT BIT(14)
+ #define ASPEED_I2CD_INTR_BUS_RECOVER_DONE BIT(13)
+@@ -354,6 +355,18 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n",
+ irq_status, command);
+
++ /*
++ * If a peer master sends messages too quickly before it processes
++ * previous slave DMA data handling, this indicator will be set. It's
++ * just a indicator and driver can't recover this case so just ignore
++ * it.
++ */
++ if (unlikely(irq_status &
++ ASPEED_I2CD_INTR_SLAVE_ADDR_RECEIVED_PENDING)) {
++ dev_dbg(bus->dev, "A slave addr match interrupt is pending.\n");
++ irq_handled |= ASPEED_I2CD_INTR_SLAVE_ADDR_RECEIVED_PENDING;
++ }
++
+ /* Slave was sent something. */
+ if (irq_status & ASPEED_I2CD_INTR_RX_DONE) {
+ if (bus->dma_buf &&
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0070-gpio-aspeed-temporary-fix-for-gpiochip-range-setting.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0070-gpio-aspeed-temporary-fix-for-gpiochip-range-setting.patch
new file mode 100644
index 000000000..bcee8bc6c
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0070-gpio-aspeed-temporary-fix-for-gpiochip-range-setting.patch
@@ -0,0 +1,44 @@
+From 89e1d083726d4d56703a6787f4707d61a2c0efd1 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Fri, 19 Jul 2019 12:54:38 -0700
+Subject: [PATCH] gpio: aspeed: temporary fix for gpiochip range setting
+
+Since we are still using fixed indices for gpio line numbers for sysfs
+interface, this commit set the gpiochip range as fixed temporariliy
+til we replace all index based gpio uses with name based uses.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/gpio/gpio-aspeed.c | 2 +-
+ drivers/gpio/sgpio-aspeed.c | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
+index ac33f8134fe6..4f1a40b3a73f 100644
+--- a/drivers/gpio/gpio-aspeed.c
++++ b/drivers/gpio/gpio-aspeed.c
+@@ -1181,7 +1181,7 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
+ gpio->chip.set = aspeed_gpio_set;
+ gpio->chip.set_config = aspeed_gpio_set_config;
+ gpio->chip.label = dev_name(&pdev->dev);
+- gpio->chip.base = -1;
++ gpio->chip.base = 0;
+
+ /* Allocate a cache of the output registers */
+ banks = DIV_ROUND_UP(gpio->chip.ngpio, 32);
+diff --git a/drivers/gpio/sgpio-aspeed.c b/drivers/gpio/sgpio-aspeed.c
+index d2dbfce531a4..792ef0d70ecf 100644
+--- a/drivers/gpio/sgpio-aspeed.c
++++ b/drivers/gpio/sgpio-aspeed.c
+@@ -678,7 +678,7 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev)
+ gpio->chip.set = aspeed_sgpio_set;
+ gpio->chip.set_config = aspeed_sgpio_set_config;
+ gpio->chip.label = dev_name(&pdev->dev);
+- gpio->chip.base = -1;
++ gpio->chip.base = gpio->config->nr_pgpios;
+
+ rc = aspeed_sgpio_setup_irqs(gpio, pdev);
+ if (rc < 0)
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0072-pmbus-add-fault-and-beep-attributes.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0072-pmbus-add-fault-and-beep-attributes.patch
new file mode 100644
index 000000000..40a9272e0
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0072-pmbus-add-fault-and-beep-attributes.patch
@@ -0,0 +1,88 @@
+From e360a6c2a3f15bfc8900c7262c56f9bcd5e0f16e Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Thu, 8 Aug 2019 10:38:00 -0700
+Subject: [PATCH] pmbus: add 'fault' and 'beep' attributes
+
+This commit adds two more attirbutes to reflect MFR_SPECIFIC bit in
+the STATUS_WORD and 'Unit Off For Insufficient Input Voltage' bit in
+the STATUS_INPUT into 'fault' and 'beep' attributes respectively.
+
+The attributes will be enumerated as 'inX_fault' and 'inX_beep' in
+a 'vin' group.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/hwmon/pmbus/pmbus.h | 1 +
+ drivers/hwmon/pmbus/pmbus_core.c | 30 ++++++++++++++++++++++++++++++
+ 2 files changed, 31 insertions(+)
+
+diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
+index d198af3a92b6..09e6fe01c304 100644
+--- a/drivers/hwmon/pmbus/pmbus.h
++++ b/drivers/hwmon/pmbus/pmbus.h
+@@ -303,6 +303,7 @@ enum pmbus_fan_mode { percent = 0, rpm };
+ #define PB_PIN_OP_WARNING BIT(0)
+ #define PB_IIN_OC_WARNING BIT(1)
+ #define PB_IIN_OC_FAULT BIT(2)
++#define PB_UNIT_OFF_FOR_INSUF_VIN BIT(3)
+
+ /*
+ * STATUS_TEMPERATURE
+diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
+index 898d7378f4f8..85295a45c3ba 100644
+--- a/drivers/hwmon/pmbus/pmbus_core.c
++++ b/drivers/hwmon/pmbus/pmbus_core.c
+@@ -1163,6 +1163,8 @@ struct pmbus_limit_attr {
+ struct pmbus_sensor_attr {
+ u16 reg; /* sensor register */
+ u16 gbit; /* generic status bit */
++ u16 gfbit; /* generic fault status bit */
++ u16 sbbit; /* beep status bit */
+ u8 nlimit; /* # of limit registers */
+ enum pmbus_sensor_classes class;/* sensor class */
+ const char *label; /* sensor label */
+@@ -1264,6 +1266,32 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client,
+ return ret;
+ }
+ }
++ /*
++ * Add fault attribute if there is a generic fault bit, and if
++ * the generic status register (word or byte, depending on which global
++ * bit is set) for this page is accessible.
++ */
++ if (attr->gfbit) {
++ upper = !!(attr->gfbit & 0xff00); /* need to check STATUS_WORD */
++ if ((!upper || (upper && data->has_status_word)) &&
++ pmbus_check_status_register(client, page)) {
++ ret = pmbus_add_boolean(data, name, "fault", index,
++ NULL, NULL,
++ PB_STATUS_BASE + page,
++ attr->gfbit);
++ if (ret)
++ return ret;
++ }
++ }
++ /* Add beep attribute if there is a beep status bit. */
++ if (attr->sbbit) {
++ ret = pmbus_add_boolean(data, name, "beep", index,
++ NULL, NULL,
++ attr->sbase + page,
++ attr->sbbit);
++ if (ret)
++ return ret;
++ }
+ return 0;
+ }
+
+@@ -1435,6 +1463,8 @@ static const struct pmbus_sensor_attr voltage_attributes[] = {
+ .gbit = PB_STATUS_VIN_UV,
+ .limit = vin_limit_attrs,
+ .nlimit = ARRAY_SIZE(vin_limit_attrs),
++ .gfbit = PB_STATUS_WORD_MFR,
++ .sbbit = PB_UNIT_OFF_FOR_INSUF_VIN,
+ }, {
+ .reg = PMBUS_VIRT_READ_VMON,
+ .class = PSC_VOLTAGE_IN,
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0073-Add-IO-statistics-to-USB-Mass-storage-gadget.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0073-Add-IO-statistics-to-USB-Mass-storage-gadget.patch
new file mode 100644
index 000000000..41969349e
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0073-Add-IO-statistics-to-USB-Mass-storage-gadget.patch
@@ -0,0 +1,155 @@
+From 5c82e0b33f2a373d5e19569635f108cfa096f53e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Adrian=20Ambro=C5=BCewicz?= <adrian.ambrozewicz@intel.com>
+Date: Mon, 29 Jul 2019 10:19:00 +0200
+Subject: [PATCH] Add IO stats to USB Mass Storage gadget
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Introduces new attribute to Mass Storage Gadget ConfigFS : stats.
+It's read-only attribute which contains statistics of read/write operations
+based on LUN transaction counters (IO number and bytes transferred).
+
+Goal is to provide a way to observe whether simulated device is actually
+used by host. Statistics on hosted file / nbd level are not always viable
+due to page cache having severe impact on actual IO statistics.
+This attribute should provide information about host IO on USB Gadget as
+close to endpoint as possible.
+
+Attribute is tied completely to configFS implementation and it's lifecycle
+is managed by Kernel and user. Driver implements a handler which populates
+output buffer on read.
+
+Tests performed:
+- mounted USB Mass Storage gadget, new attribute showed up in gadget tree
+- attribute was monitored for changes during IO performed on host machine
+- removed device, attribute (along with other device attributes) was gone
+
+Signed-off-by: Adrian Ambrożewicz <adrian.ambrozewicz@intel.com>
+---
+ drivers/usb/gadget/function/f_mass_storage.c | 12 ++++++++++++
+ drivers/usb/gadget/function/storage_common.c | 9 +++++++++
+ drivers/usb/gadget/function/storage_common.h | 29 ++++++++++++++++++++++++++++
+ 3 files changed, 50 insertions(+)
+
+diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
+index 7c96c4665178..ecc3c68a7882 100644
+--- a/drivers/usb/gadget/function/f_mass_storage.c
++++ b/drivers/usb/gadget/function/f_mass_storage.c
+@@ -710,6 +710,8 @@ static int do_read(struct fsg_common *common)
+ amount_left -= nread;
+ common->residue -= nread;
+
++ fsg_stats_rd_attempt(&curlun->stats, nread);
++
+ /*
+ * Except at the end of the transfer, nread will be
+ * equal to the buffer size, which is divisible by the
+@@ -907,6 +909,8 @@ static int do_write(struct fsg_common *common)
+ amount_left_to_write -= nwritten;
+ common->residue -= nwritten;
+
++ fsg_stats_wr_attempt(&curlun->stats, nwritten);
++
+ /* If an error occurred, report it and its position */
+ if (nwritten < amount) {
+ curlun->sense_data = SS_WRITE_ERROR;
+@@ -3122,6 +3126,13 @@ static ssize_t fsg_lun_opts_inquiry_string_store(struct config_item *item,
+
+ CONFIGFS_ATTR(fsg_lun_opts_, inquiry_string);
+
++static ssize_t fsg_lun_opts_stats_show(struct config_item *item, char *page)
++{
++ return fsg_show_stats(to_fsg_lun_opts(item)->lun, page);
++}
++
++CONFIGFS_ATTR_RO(fsg_lun_opts_, stats);
++
+ static struct configfs_attribute *fsg_lun_attrs[] = {
+ &fsg_lun_opts_attr_file,
+ &fsg_lun_opts_attr_ro,
+@@ -3129,6 +3140,7 @@ static struct configfs_attribute *fsg_lun_attrs[] = {
+ &fsg_lun_opts_attr_cdrom,
+ &fsg_lun_opts_attr_nofua,
+ &fsg_lun_opts_attr_inquiry_string,
++ &fsg_lun_opts_attr_stats,
+ NULL,
+ };
+
+diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c
+index f7e6c42558eb..2325b97961df 100644
+--- a/drivers/usb/gadget/function/storage_common.c
++++ b/drivers/usb/gadget/function/storage_common.c
+@@ -371,6 +371,15 @@ ssize_t fsg_show_inquiry_string(struct fsg_lun *curlun, char *buf)
+ }
+ EXPORT_SYMBOL_GPL(fsg_show_inquiry_string);
+
++ssize_t fsg_show_stats(struct fsg_lun *curlun, char *buf)
++{
++ return sprintf(buf, "read cnt: %u\n" "read sum: %llu\n"
++ "write cnt: %u\n" "write sum: %llu\n",
++ curlun->stats.read.count, curlun->stats.read.bytes,
++ curlun->stats.write.count, curlun->stats.write.bytes);
++}
++EXPORT_SYMBOL_GPL(fsg_show_stats);
++
+ /*
+ * The caller must hold fsg->filesem for reading when calling this function.
+ */
+diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h
+index e5e3a2553aaa..447021ba821a 100644
+--- a/drivers/usb/gadget/function/storage_common.h
++++ b/drivers/usb/gadget/function/storage_common.h
+@@ -95,6 +95,32 @@ do { \
+ */
+ #define INQUIRY_STRING_LEN ((size_t) (8 + 16 + 4 + 1))
+
++struct fsg_stats_cnt {
++ u64 bytes;
++ u32 count;
++};
++
++struct fsg_stats {
++ struct fsg_stats_cnt read;
++ struct fsg_stats_cnt write;
++};
++
++static inline void fsg_stats_update(struct fsg_stats_cnt *cnt, u64 diff)
++{
++ cnt->count++;
++ cnt->bytes += diff;
++}
++
++static inline void fsg_stats_wr_attempt(struct fsg_stats *stats, u64 b_written)
++{
++ fsg_stats_update(&stats->write, b_written);
++}
++
++static inline void fsg_stats_rd_attempt(struct fsg_stats *stats, u64 b_read)
++{
++ fsg_stats_update(&stats->read, b_read);
++}
++
+ struct fsg_lun {
+ struct file *filp;
+ loff_t file_length;
+@@ -120,6 +146,8 @@ struct fsg_lun {
+ const char *name; /* "lun.name" */
+ const char **name_pfx; /* "function.name" */
+ char inquiry_string[INQUIRY_STRING_LEN];
++
++ struct fsg_stats stats;
+ };
+
+ static inline bool fsg_lun_is_open(struct fsg_lun *curlun)
+@@ -213,6 +241,7 @@ ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+ ssize_t fsg_show_inquiry_string(struct fsg_lun *curlun, char *buf);
+ ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf);
+ ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf);
++ssize_t fsg_show_stats(struct fsg_lun *curlun, char *buf);
+ ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+ const char *buf, size_t count);
+ ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count);
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0074-media-aspeed-refine-HSYNC-VSYNC-polarity-setting-log.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0074-media-aspeed-refine-HSYNC-VSYNC-polarity-setting-log.patch
new file mode 100644
index 000000000..4118e366c
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0074-media-aspeed-refine-HSYNC-VSYNC-polarity-setting-log.patch
@@ -0,0 +1,93 @@
+From 1032b062669b7ee041d2f5a9f4729953655efe61 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Wed, 4 Sep 2019 14:52:40 -0700
+Subject: [PATCH] media: aspeed: refine HSYNC/VSYNC polarity setting logic
+
+Sometimes it detects weird resolutions such as 1024x287 when the
+actual resolution is 1280x768. To resolve this issue, this commit
+refines HSYNC/VSYNC polarity setting code for mode detection by
+clearing the bits as normal polarity at the beginning of the first
+mode detection like datasheet suggested, and refines polarity
+setting logic so that the bits can be set or cleared properly.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/media/platform/aspeed-video.c | 45 ++++++++++++++++++-----------------
+ 1 file changed, 23 insertions(+), 22 deletions(-)
+
+diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c
+index 4ef37cfc8446..455c6af81236 100644
+--- a/drivers/media/platform/aspeed-video.c
++++ b/drivers/media/platform/aspeed-video.c
+@@ -614,7 +614,7 @@ static void aspeed_video_check_and_set_polarity(struct aspeed_video *video)
+ int i;
+ int hsync_counter = 0;
+ int vsync_counter = 0;
+- u32 sts;
++ u32 sts, ctrl;
+
+ for (i = 0; i < NUM_POLARITY_CHECKS; ++i) {
+ sts = aspeed_video_read(video, VE_MODE_DETECT_STATUS);
+@@ -629,30 +629,29 @@ static void aspeed_video_check_and_set_polarity(struct aspeed_video *video)
+ hsync_counter++;
+ }
+
+- if (hsync_counter < 0 || vsync_counter < 0) {
+- u32 ctrl = 0;
++ ctrl = aspeed_video_read(video, VE_CTRL);
+
+- if (hsync_counter < 0) {
+- ctrl = VE_CTRL_HSYNC_POL;
+- video->detected_timings.polarities &=
+- ~V4L2_DV_HSYNC_POS_POL;
+- } else {
+- video->detected_timings.polarities |=
+- V4L2_DV_HSYNC_POS_POL;
+- }
+-
+- if (vsync_counter < 0) {
+- ctrl = VE_CTRL_VSYNC_POL;
+- video->detected_timings.polarities &=
+- ~V4L2_DV_VSYNC_POS_POL;
+- } else {
+- video->detected_timings.polarities |=
+- V4L2_DV_VSYNC_POS_POL;
+- }
++ if (hsync_counter < 0) {
++ ctrl |= VE_CTRL_HSYNC_POL;
++ video->detected_timings.polarities &=
++ ~V4L2_DV_HSYNC_POS_POL;
++ } else {
++ ctrl &= ~VE_CTRL_HSYNC_POL;
++ video->detected_timings.polarities |=
++ V4L2_DV_HSYNC_POS_POL;
++ }
+
+- if (ctrl)
+- aspeed_video_update(video, VE_CTRL, 0, ctrl);
++ if (vsync_counter < 0) {
++ ctrl |= VE_CTRL_VSYNC_POL;
++ video->detected_timings.polarities &=
++ ~V4L2_DV_VSYNC_POS_POL;
++ } else {
++ ctrl &= ~VE_CTRL_VSYNC_POL;
++ video->detected_timings.polarities |=
++ V4L2_DV_VSYNC_POS_POL;
+ }
++
++ aspeed_video_write(video, VE_CTRL, ctrl);
+ }
+
+ static bool aspeed_video_alloc_buf(struct aspeed_video *video,
+@@ -741,6 +740,8 @@ static void aspeed_video_get_resolution(struct aspeed_video *video)
+ }
+
+ set_bit(VIDEO_RES_DETECT, &video->flags);
++ aspeed_video_update(video, VE_CTRL,
++ VE_CTRL_VSYNC_POL | VE_CTRL_HSYNC_POL, 0);
+ aspeed_video_enable_mode_detect(video);
+
+ rc = wait_event_interruptible_timeout(video->wait,
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0075-Refine-initialization-flow-in-I2C-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0075-Refine-initialization-flow-in-I2C-driver.patch
new file mode 100644
index 000000000..363f25368
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0075-Refine-initialization-flow-in-I2C-driver.patch
@@ -0,0 +1,64 @@
+From a98e86429ce520cab3505c76ce02703837ef79b9 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Mon, 23 Sep 2019 13:48:49 -0700
+Subject: [PATCH] Refine initialization flow in I2C driver
+
+Since we enabled I2C busses in u-boot, we need to disable the I2C
+bus and clear all garbage interrupts when kernel probes the bus.
+This commit refines the initialization flow by adding a bus reset
+at the beginning of probe function and by moving bus init function
+after interrupt handling setup.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/i2c/busses/i2c-aspeed.c | 20 +++++++++-----------
+ 1 file changed, 9 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
+index 0070366e9d6d..ab771a57a252 100644
+--- a/drivers/i2c/busses/i2c-aspeed.c
++++ b/drivers/i2c/busses/i2c-aspeed.c
+@@ -1441,6 +1441,11 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
+ if (IS_ERR(bus->base))
+ return PTR_ERR(bus->base);
+
++ /* Disable bus and clean up any left over interrupt state. */
++ writel(0, bus->base + ASPEED_I2C_FUN_CTRL_REG);
++ writel(0, bus->base + ASPEED_I2C_INTR_CTRL_REG);
++ writel(0xffffffff, bus->base + ASPEED_I2C_INTR_STS_REG);
++
+ parent_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(parent_clk))
+ return PTR_ERR(parent_clk);
+@@ -1563,17 +1568,6 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
+
+ bus->dev = &pdev->dev;
+
+- /* Clean up any left over interrupt state. */
+- writel(0, bus->base + ASPEED_I2C_INTR_CTRL_REG);
+- writel(0xffffffff, bus->base + ASPEED_I2C_INTR_STS_REG);
+- /*
+- * bus.lock does not need to be held because the interrupt handler has
+- * not been enabled yet.
+- */
+- ret = aspeed_i2c_init(bus, pdev);
+- if (ret < 0)
+- goto out_free_dma_buf;
+-
+ irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+ ret = devm_request_irq(&pdev->dev, irq, aspeed_i2c_bus_irq,
+ 0, dev_name(&pdev->dev), bus);
+@@ -1586,6 +1580,10 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
+
+ platform_set_drvdata(pdev, bus);
+
++ ret = aspeed_i2c_init(bus, pdev);
++ if (ret < 0)
++ goto out_free_dma_buf;
++
+ dev_info(bus->dev, "i2c bus %d registered (%s mode), irq %d\n",
+ bus->adap.nr, bus->dma_buf ? "dma" :
+ bus->buf_base ? "buffer" : "byte",
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0076-media-aspeed-clear-garbage-interrupts.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0076-media-aspeed-clear-garbage-interrupts.patch
new file mode 100644
index 000000000..0cf9913fe
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0076-media-aspeed-clear-garbage-interrupts.patch
@@ -0,0 +1,74 @@
+From 5f89fa4b6468771b5de6e73454bf0ea546249b7b Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Thu, 26 Sep 2019 12:15:23 -0700
+Subject: [PATCH] media: aspeed: clear garbage interrupts
+
+CAPTURE_COMPLETE and FRAME_COMPLETE interrupts come even when these
+are disabled in the VE_INTERRUPT_CTRL register and eventually this
+behavior causes disabling irq itself like below:
+
+[10055.108784] irq 23: nobody cared (try booting with the "irqpoll" option)
+[10055.115525] CPU: 0 PID: 331 Comm: swampd Tainted: G W 5.3.0-4fde000-dirty-d683e2e #1
+[10055.124565] Hardware name: Generic DT based system
+[10055.129355] Backtrace:
+[10055.131854] [<80107d7c>] (dump_backtrace) from [<80107fb0>] (show_stack+0x20/0x24)
+[10055.139431] r7:00000017 r6:00000001 r5:00000000 r4:9d51dc00
+[10055.145120] [<80107f90>] (show_stack) from [<8074bf50>] (dump_stack+0x20/0x28)
+[10055.152361] [<8074bf30>] (dump_stack) from [<80150ffc>] (__report_bad_irq+0x40/0xc0)
+[10055.160109] [<80150fbc>] (__report_bad_irq) from [<80150f2c>] (note_interrupt+0x23c/0x294)
+[10055.168374] r9:015b6e60 r8:00000000 r7:00000017 r6:00000001 r5:00000000 r4:9d51dc00
+[10055.176136] [<80150cf0>] (note_interrupt) from [<8014df1c>] (handle_irq_event_percpu+0x88/0x98)
+[10055.184835] r10:7eff7910 r9:015b6e60 r8:00000000 r7:9d417600 r6:00000001 r5:00000002
+[10055.192657] r4:9d51dc00 r3:00000000
+[10055.196248] [<8014de94>] (handle_irq_event_percpu) from [<8014df64>] (handle_irq_event+0x38/0x4c)
+[10055.205113] r5:80b56d50 r4:9d51dc00
+[10055.208697] [<8014df2c>] (handle_irq_event) from [<80151f1c>] (handle_level_irq+0xbc/0x12c)
+[10055.217037] r5:80b56d50 r4:9d51dc00
+[10055.220623] [<80151e60>] (handle_level_irq) from [<8014d4b8>] (generic_handle_irq+0x30/0x44)
+[10055.229052] r5:80b56d50 r4:00000017
+[10055.232648] [<8014d488>] (generic_handle_irq) from [<8014d524>] (__handle_domain_irq+0x58/0xb4)
+[10055.241356] [<8014d4cc>] (__handle_domain_irq) from [<801021e4>] (avic_handle_irq+0x68/0x70)
+[10055.249797] r9:015b6e60 r8:00c5387d r7:00c5387d r6:ffffffff r5:9dd33fb0 r4:9d402380
+[10055.257539] [<8010217c>] (avic_handle_irq) from [<80101e34>] (__irq_usr+0x54/0x80)
+[10055.265105] Exception stack(0x9dd33fb0 to 0x9dd33ff8)
+[10055.270152] 3fa0: 015d0530 00000000 00000000 015d0538
+[10055.278328] 3fc0: 015d0530 015b6e60 00000000 00000000 0052c5d0 015b6e60 7eff7910 7eff7918
+[10055.286496] 3fe0: 76ce5614 7eff7908 0050e2f4 76a3a08c 20000010 ffffffff
+[10055.293104] r5:20000010 r4:76a3a08c
+[10055.296673] handlers:
+[10055.298967] [<79f218a5>] irq_default_primary_handler threaded [<1de88514>] aspeed_video_irq
+[10055.307344] Disabling IRQ #23
+
+To fix this issue, this commit makes the interrupt handler clear
+these garbage interrupts. This driver enables and uses only
+COMP_COMPLETE interrupt.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/media/platform/aspeed-video.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c
+index 455c6af81236..0473f3141329 100644
+--- a/drivers/media/platform/aspeed-video.c
++++ b/drivers/media/platform/aspeed-video.c
+@@ -606,6 +606,16 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg)
+ aspeed_video_start_frame(video);
+ }
+
++ /*
++ * CAPTURE_COMPLETE and FRAME_COMPLETE interrupts come even when these
++ * are disabled in the VE_INTERRUPT_CTRL register so clear them to
++ * prevent unnecessary interrupt calls.
++ */
++ if (sts & VE_INTERRUPT_CAPTURE_COMPLETE)
++ sts &= ~VE_INTERRUPT_CAPTURE_COMPLETE;
++ if (sts & VE_INTERRUPT_FRAME_COMPLETE)
++ sts &= ~VE_INTERRUPT_FRAME_COMPLETE;
++
+ return sts ? IRQ_NONE : IRQ_HANDLED;
+ }
+
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/intel.cfg b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/intel.cfg
new file mode 100644
index 000000000..2a4e87d80
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/intel.cfg
@@ -0,0 +1,74 @@
+CONFIG_BLK_DEV_RAM=y
+CONFIG_HWMON=y
+CONFIG_SENSORS_ASPEED=y
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+CONFIG_IIO=y
+CONFIG_SENSORS_IIO_HWMON=y
+CONFIG_ASPEED_ADC=y
+CONFIG_SGPIO_ASPEED=y
+CONFIG_CRC8=y
+CONFIG_PECI=y
+CONFIG_PECI_CHARDEV=y
+CONFIG_PECI_ASPEED=y
+CONFIG_SENSORS_PECI_CPUTEMP=y
+CONFIG_SENSORS_PECI_DIMMTEMP=y
+CONFIG_CONFIGFS_FS=y
+CONFIG_BLK_DEV_RAM_SIZE=49152
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x01
+CONFIG_MAGIC_SYSRQ_SERIAL=y
+CONFIG_ASPEED_ESPI_SLAVE=y
+CONFIG_ASPEED_KCS_IPMI_BMC=y
+CONFIG_I2C_SLAVE=y
+CONFIG_I2C_SLAVE_MQUEUE=y
+CONFIG_I2C_SLAVE_MQUEUE_MESSAGE_SIZE=256
+CONFIG_I2C_SLAVE_MQUEUE_QUEUE_SIZE=32
+CONFIG_ASPEED_BT_IPMI_BMC=n
+CONFIG_ASPEED_LPC_CTRL=n
+CONFIG_ASPEED_LPC_MBOX=y
+CONFIG_ASPEED_LPC_SIO=y
+CONFIG_JTAG=y
+CONFIG_JTAG_ASPEED=y
+CONFIG_FRAME_VECTOR=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_V4L2=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_VIDEO_ASPEED=y
+CONFIG_VIDEOBUF2_CORE=y
+CONFIG_VIDEOBUF2_V4L2=y
+CONFIG_VIDEOBUF2_MEMOPS=y
+CONFIG_VIDEOBUF2_DMA_CONTIG=y
+CONFIG_MEDIA_SUBDRV_AUTOSELECT=y
+CONFIG_USB_GADGET_VBUS_DRAW=2
+CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2
+CONFIG_USB_LIBCOMPOSITE=y
+CONFIG_USB_F_HID=y
+CONFIG_USB_GADGET=y
+CONFIG_U_SERIAL_CONSOLE=y
+CONFIG_USB_ASPEED_VHUB=y
+CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_MASS_STORAGE=y
+CONFIG_USB_CONFIGFS_F_FS=y
+CONFIG_USB_CONFIGFS_F_HID=y
+CONFIG_ASPEED_UART_ROUTING=y
+CONFIG_ASPEED_VGA_SHAREDMEM=y
+CONFIG_PWM=y
+CONFIG_PWM_FTTMR010=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_PWM_BEEPER=y
+CONFIG_VFAT_FS=y
+CONFIG_NLS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_15=y
+CONFIG_NLS_UTF8=y
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_CIFS=y
+CONFIG_CIFS_XATTR=y
+CONFIG_PSTORE=y
+CONFIG_PSTORE_ZLIB_COMPRESS=y
+CONFIG_PSTORE_RAM=y
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend
new file mode 100644
index 000000000..a901ce9db
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend
@@ -0,0 +1,62 @@
+FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
+
+do_compile_prepend(){
+ # device tree compiler flags
+ export DTC_FLAGS=-@
+}
+
+SRC_URI += " \
+ file://intel.cfg \
+ file://0001-arm-dts-add-DTS-for-Intel-platforms.patch \
+ file://0002-Enable-pass-through-on-GPIOE1-and-GPIOE3-free.patch \
+ file://0003-Enable-GPIOE0-and-GPIOE2-pass-through-by-default.patch \
+ file://0006-Allow-monitoring-of-power-control-input-GPIOs.patch \
+ file://0007-aspeed-pwm-tacho-change-default-fan-speed.patch \
+ file://0008-Report-link-statistics-for-the-NCSI-channel.patch \
+ file://0014-arm-dts-aspeed-g5-add-espi.patch \
+ file://0015-New-flash-map-for-intel.patch \
+ file://0016-Add-ASPEED-SGPIO-driver.patch \
+ file://0017-SGPIO-DT-and-pinctrl-fixup.patch \
+ file://0018-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch \
+ file://0019-Add-I2C-IPMB-support.patch \
+ file://0020-misc-aspeed-add-lpc-mbox-driver.patch \
+ file://0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch \
+ file://0022-Add-AST2500-eSPI-driver.patch \
+ file://0026-Add-support-for-new-PECI-commands.patch \
+ file://0028-Add-AST2500-JTAG-driver.patch \
+ file://0030-Add-dump-debug-code-into-I2C-drivers.patch \
+ file://0031-Add-high-speed-baud-rate-support-for-UART.patch \
+ file://0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch \
+ file://0034-arm-dts-aspeed-Swap-the-mac-nodes-numbering.patch \
+ file://0035-Implement-a-memory-driver-share-memory.patch \
+ file://0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch \
+ file://0040-i2c-Add-mux-hold-unhold-msg-types.patch \
+ file://0042-Add-bus-timeout-ms-and-retries-device-tree-propertie.patch \
+ file://0043-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-BT.patch \
+ file://0044-misc-Add-clock-control-logic-into-Aspeed-LPC-SNOOP-d.patch \
+ file://0045-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-KC.patch \
+ file://0047-misc-Block-error-printing-on-probe-defer-case-in-Asp.patch \
+ file://0049-Suppress-excessive-HID-gadget-error-logs.patch \
+ file://0051-Add-AST2500-JTAG-device.patch \
+ file://0052-drivers-jtag-Add-JTAG-core-driver.patch \
+ file://0053-Add-Aspeed-SoC-24xx-and-25xx-families-JTAG.patch \
+ file://0054-Documentation-jtag-Add-bindings-for-Aspeed-SoC.patch \
+ file://0055-Documentation-jtag-Add-ABI-documentation.patch \
+ file://0056-Documentation-jtag-Add-JTAG-core-driver-ioctl-number.patch \
+ file://0057-drivers-jtag-Add-JTAG-core-driver-Maintainers.patch \
+ file://0060-i2c-aspeed-fix-master-pending-state-handling.patch \
+ file://0061-i2c-aspeed-add-buffer-mode-transfer-support.patch \
+ file://0062-i2c-aspeed-add-DMA-mode-transfer-support.patch \
+ file://0063-i2c-aspeed-add-general-call-support.patch \
+ file://0064-set-idle-disconnect-to-true-in-all-cases.patch \
+ file://0068-i2c-aspeed-add-H-W-timeout-support.patch \
+ file://0069-i2c-aspeed-add-SLAVE_ADDR_RECEIVED_PENDING-interrupt.patch \
+ file://0070-gpio-aspeed-temporary-fix-for-gpiochip-range-setting.patch \
+ file://0072-pmbus-add-fault-and-beep-attributes.patch \
+ file://0073-Add-IO-statistics-to-USB-Mass-storage-gadget.patch \
+ file://0074-media-aspeed-refine-HSYNC-VSYNC-polarity-setting-log.patch \
+ file://0075-Refine-initialization-flow-in-I2C-driver.patch \
+ file://0076-media-aspeed-clear-garbage-interrupts.patch \
+ "
+
+SRC_URI += "${@bb.utils.contains('IMAGE_FSTYPES', 'intel-pfr', 'file://0005-128MB-flashmap-for-PFR.patch', '', d)}"