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-ast2500-platforms.patch500
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-add-DTS-for-Intel-ast2600-platforms.patch531
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-base-aspeed-g6-dtsi-fixups.patch212
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0002-Add-Aspeed-fmc-spi-driver.patch645
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0002-Enable-pass-through-on-GPIOE1-and-GPIOE3-free.patch173
-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.patch123
-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.patch31
-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.patch760
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0017-SGPIO-DT-and-pinctrl-fixup.patch227
-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.patch700
-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.patch559
-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.patch154
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0044-misc-Add-clock-control-logic-into-Aspeed-LPC-SNOOP-d.patch139
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0045-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-KC.patch273
-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.patch1293
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0054-Documentation-jtag-Add-bindings-for-Aspeed-SoC.patch107
-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/0061-i2c-aspeed-add-buffer-mode-transfer-support.patch1035
-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-arm-ast2600-add-pwm_tacho-driver-from-aspeed.patch1107
-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/0077-soc-aspeed-Add-read-only-property-support.patch47
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0078-Fix-NCSI-driver-issue-caused-by-host-shutdown.patch70
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0080-i2c-aspeed-filter-garbage-interrupts-out.patch56
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0082-ARM-dts-aspeed-g6-add-USB-virtual-hub-fixup.patch53
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0083-usb-gadget-aspeed-add-ast2600-compatible-string.patch32
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0084-ARM-dts-aspeed-g6-add-GFX-node.patch35
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0085-drm-add-AST2600-GFX-support.patch105
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0086-ADC-linux-driver-for-AST2600.patch271
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0086-ARM-dts-aspeed-g6-add-video-node.patch36
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0087-media-aspeed-add-aspeed-ast2600-video-engine-compati.patch63
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0088-clk-ast2600-enable-ESPICLK-always.patch30
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0089-ast2600-enable-high-speed-uart-in-kernel.patch97
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/intel.cfg87
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend74
66 files changed, 16138 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-add-DTS-for-Intel-ast2500-platforms.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-add-DTS-for-Intel-ast2500-platforms.patch
new file mode 100644
index 000000000..baf9dba9a
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-add-DTS-for-Intel-ast2500-platforms.patch
@@ -0,0 +1,500 @@
+From 89dec433cd2fcfcb8690b315bb4e787d53de2296 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 ast2500 platforms
+
+Add the DTS file for Intel ast2500-based 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>
+Signed-off-by: Zhikui Ren <zhikui.ren@intel.com>
+---
+ arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts | 471 +++++++++++++++++++++++++
+ 1 file changed, 471 insertions(+)
+ create mode 100644 arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts
+
+diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts b/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts
+new file mode 100644
+index 0000000..6ded94d
+--- /dev/null
++++ b/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts
+@@ -0,0 +1,470 @@
++/dts-v1/;
++
++#include "aspeed-g5.dtsi"
++#include <dt-bindings/gpio/aspeed-gpio.h>
++#include <dt-bindings/i2c/i2c.h>
++
++/ {
++ model = "Intel AST2500 BMC";
++ compatible = "intel,ast2500-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 = "on";
++ 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";
++
++ misc_control {
++ compatible = "aspeed,bmc-misc";
++ uart_port_debug {
++ offset = <0x2c>;
++ bit-mask = <0x1>;
++ bit-shift = <10>;
++ read-only;
++ };
++ p2a-bridge {
++ offset = <0x180>;
++ bit-mask = <0x1>;
++ bit-shift = <1>;
++ read-only;
++ };
++ boot-2nd-flash {
++ offset = <0x70>;
++ bit-mask = <0x1>;
++ bit-shift = <17>;
++ read-only;
++ };
++ };
++};
++
++&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*/ "FM_SYS_FAN0_PRSNT_D_N","FM_SYS_FAN1_PRSNT_D_N","FM_SYS_FAN2_PRSNT_D_N","FM_SYS_FAN3_PRSNT_D_N","FM_SYS_FAN4_PRSNT_D_N","FM_SYS_FAN5_PRSNT_D_N","","",
++ /*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*/ "REMOTE_DEBUG_ENABLE","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";
++};
++
++&uart_routing {
++ 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/0001-arm-dts-add-DTS-for-Intel-ast2600-platforms.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-add-DTS-for-Intel-ast2600-platforms.patch
new file mode 100644
index 000000000..34490822e
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-add-DTS-for-Intel-ast2600-platforms.patch
@@ -0,0 +1,531 @@
+From 57363b496c6eb832b0c3407ee997fdee09f4007f Mon Sep 17 00:00:00 2001
+From: Vernon Mauery <vernon.mauery@linux.intel.com>
+Date: Tue, 19 Sep 2017 15:55:39 +0800
+Subject: [PATCH] arm: dts: add DTS for Intel ast2600 platforms
+
+Add the DTS file for Intel ast2600-based systems.
+
+Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+Signed-off-by: Chen Yugang <yugang.chen@linux.intel.com>
+---
+ arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts | 507 +++++++++++++++++++++++++
+ 1 file changed, 507 insertions(+)
+ create mode 100644 arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts
+
+diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts b/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts
+new file mode 100644
+index 000000000000..1ad46e8bc69b
+--- /dev/null
++++ b/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts
+@@ -0,0 +1,507 @@
++// SPDX-License-Identifier: GPL-2.0+
++/dts-v1/;
++
++#include "aspeed-g6.dtsi"
++#include <dt-bindings/gpio/aspeed-gpio.h>
++#include <dt-bindings/i2c/i2c.h>
++
++/ {
++ model = "AST2600 EVB";
++ compatible = "aspeed,ast2600";
++
++ chosen {
++ stdout-path = &uart5;
++ bootargs = "console=tty0 console=ttyS4,115200n8 root=/dev/ram rw init=/linuxrc";
++ };
++
++ memory@80000000 {
++ device_type = "memory";
++ reg = <0x80000000 0x40000000>;
++ };
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++
++ 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;
++ };
++ };
++
++ reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ video_memory: video {
++ size = <0x04000000>;
++ alignment = <0x01000000>;
++ compatible = "shared-dma-pool";
++ no-map;
++ };
++ };
++
++ iio-hwmon {
++ compatible = "iio-hwmon";
++ io-channels = <&adc0 0>, <&adc0 1>, <&adc0 2>, <&adc0 3>,
++ <&adc0 4>, <&adc0 5>, <&adc0 6>, <&adc0 7>,
++ <&adc1 8>, <&adc1 9>, <&adc1 10>, <&adc1 11>,
++ <&adc1 12>, <&adc1 13>, <&adc1 14>, <&adc1 15>;
++ };
++
++ leds {
++ compatible = "gpio-leds";
++
++ identify {
++ default-state = "off";
++ gpios = <&gpio1 ASPEED_GPIO(B, 7) GPIO_ACTIVE_LOW>;
++ };
++
++ status_amber {
++ default-state = "off";
++ gpios = <&gpio1 ASPEED_GPIO(G, 3) GPIO_ACTIVE_LOW>;
++ };
++
++ status_green {
++ default-state = "keep";
++ gpios = <&gpio1 ASPEED_GPIO(G, 2) GPIO_ACTIVE_LOW>;
++ };
++ };
++/*
++ beeper {
++ compatible = "pwm-beeper";
++ pwms = <&timer 7 1000000 0>;
++ }; */
++};
++
++&fmc {
++ status = "okay";
++ flash: m25p80@0 {
++ compatible = "m25p80", "jedec,spi-nor";
++ reg = <0x0>;
++ #address-cells = <1>;
++ #size-cells = <1>;
++ spi-max-frequency = <10000000>;
++ m25p,fast-read;
++#include "openbmc-flash-layout-intel-64MB.dtsi"
++ };
++};
++
++&espi {
++ status = "disabled"; /* FIXME: Use H/W handshaking */
++};
++
++&peci0 {
++ status = "okay";
++ gpios = <&gpio0 ASPEED_GPIO(F, 6) 0>;
++};
++
++&syscon {
++ uart-clock-high-speed;
++ status = "okay";
++};
++
++#if 0
++ GPIO Alias: (runtime alias -> schematic name)
++ ID_BUTTON -> FP_ID_BTN_N
++ CPU_CATERR -> FM_PLT_BMC_THERMTRIP_N
++ PCH_BMC_THERMTRIP -> FM_PLT_BMC_THERMTRIP_N
++ RESET_BUTTON -> FP_BMC_RST_BTN_N
++ RESET_OUT -> RST_BMC_RSTBTN_OUT_R_N
++ POWER_BUTTON -> FP_BMC_PWR_BTN_R_N
++ POWER_OUT -> FM_BMC_PWR_BTN_N
++ PREQ_N -> DBP_ASD_BMC_PREQ_R_N
++ POST_COMPLETE -> FM_BIOS_POST_CMPLT_BMC_N
++ CPU_ERR0 -> FM_CPU_ERR0_LVT3_N
++ CPU_ERR1 -> FM_CPU_ERR1_LVT3_N
++ CPU_ERR2 -> FM_CPU_ERR2_LVT3_N
++ DEBUG_EN_N -> FM_JTAG_TCK_MUX_SEL_R
++ NMI_OUT -> IRQ_BMC_CPU_NMI_R
++ PLTRST_N -> RST_PLTRST_BMC_N
++ PRDY_N -> DBP_ASD_BMC_PRDY_R_N
++ PWR_DEBUG_N ->
++ XDP_PRST_N ->
++ SYSPWROK ->
++ RSMRST_N ->
++ SIO_S3 -> FM_SLPS3_R_N
++ SIO_S5 -> FM_SLPS4_R_N
++ SIO_ONCONTROL -> FM_BMC_ONCTL_R_N
++ SIO_POWER_GOOD -> PWRGD_CPU0_LVC3_R
++ PS_PWROK -> PWRGD_BMC_PS_PWROK_R
++ P3VBAT_BRIDGE_EN ->
++ TCK_MUX_SEL ->
++ SMI -> IRQ_SMI_ACTIVE_BMC_N
++ NMI_BUTTON -> FP_NMI_BTN_N
++#endif
++&gpio0 {
++ status = "okay";
++ /* Enable GPIOP0 and GPIOP2 pass-through by default */
++ /* pinctrl-names = "pass-through";
++ pinctrl-0 = <&pinctrl_thru0_default
++ &pinctrl_thru1_default>; */
++ gpio-line-names =
++ /*A0-A7*/ "","","","","SMB_CPU_PIROM_SCL","SMB_CPU_PIROM_SDA","SMB_IPMB_STBY_LVC3_R_SCL","SMB_IPMB_STBY_LVC3_R_SDA",
++ /*B0-B7*/ "FM_1200VA_OC","NMI_OUT","IRQ_SMB3_M2_ALERT_N","","RGMII_BMC_RMM4_LVC3_R_MDC","RGMII_BMC_RMM4_LVC3_R_MDIO","FM_BMC_BMCINIT_R","FP_ID_LED_N",
++ /*C0-C7*/ "FM_FORCE_BMC_UPDATE_N","RST_RGMII_PHYRST_N","FM_TPM_EN_PULSE","FM_BMC_CRASHLOG_TRIG_N","IRQ_BMC_PCH_NMI_R","FM_CPU1_DISABLE_COD_N","","",
++ /*D0-D7*/ "CPU_ERR0","CPU_ERR1","CPU_ERR2","PRDY_N","FM_SPD_SWITCH_CTRL_N","","","",
++ /*E0-E7*/ "FM_SKT1_FAULT_LED","FM_SKT0_FAULT_LED","CLK_50M_CKMNG_BMCB","FM_BMC_BOARD_REV_ID2_N","","","","",
++ /*F0-F7*/ "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","FM_BMC_BOARD_SKU_ID5_N","ID_BUTTON","PS_PWROK",
++ /*G0-G7*/ "FM_SMB_BMC_NVME_LVC3_ALERT_N","RST_BMC_I2C_M2_R_N","FP_LED_STATUS_GREEN_N","FP_LED_STATUS_AMBER_N","FM_BMC_BOARD_REV_ID0_N","FM_BMC_BOARD_REV_ID1_N","FM_BMC_CPU_FBRK_OUT_R_N","DBP_PRESENT_IN_R2_N",
++ /*H0-H7*/ "SGPIO_BMC_CLK_R","SGPIO_BMC_LD_R","SGPIO_BMC_DOUT_R","SGPIO_BMC_DIN","PLTRST_N","CPU_CATERR","PCH_BMC_THERMTRIP","",
++ /*I0-I7*/ "JTAG_ASD_NTRST_R_N","JTAG_ASD_TDI_R","JTAG_ASD_TCK_R","JTAG_ASD_TMS_R","JTAG_ASD_TDO","FM_BMC_PWRBTN_OUT_R_N","FM_BMC_PWR_BTN_N","",
++ /*J0-J7*/ "SMB_CHASSENSOR_STBY_LVC3_SCL","SMB_CHASSENSOR_STBY_LVC3_SDA","","","","","","",
++ /*K0-K7*/ "SMB_HSBP_STBY_LVC3_R_SCL","SMB_HSBP_STBY_LVC3_R_SDA","SMB_SMLINK0_STBY_LVC3_R2_SCL","SMB_SMLINK0_STBY_LVC3_R2_SDA","SMB_TEMPSENSOR_STBY_LVC3_R_SCL","SMB_TEMPSENSOR_STBY_LVC3_R_SDA","SMB_PMBUS_SML1_STBY_LVC3_R_SCL","SMB_PMBUS_SML1_STBY_LVC3_R_SDA",
++ /*L0-L7*/ "SMB_PCIE_STBY_LVC3_R_SCL","SMB_PCIE_STBY_LVC3_R_SDA","SMB_HOST_STBY_BMC_LVC3_R_SCL","SMB_HOST_STBY_BMC_LVC3_R_SDA","PREQ_N","DEBUG_EN_N","V_BMC_GFX_HSYNC_R","V_BMC_GFX_VSYNC_R",
++ /*M0-M7*/ "SPA_CTS_N","SPA_DCD_N","SPA_DSR_N","PU_SPA_RI_N","SPA_DTR_N","SPA_RTS_N","SPA_SOUT","SPA_SIN",
++ /*N0-N7*/ "SPB_CTS_N","SPB_DCD_N","SPB_DSR_N","PU_SPB_RI_N","SPB_DTR_N","SPB_RTS_N","SPB_SOUT","SPB_SIN",
++ /*O0-O7*/ "FAN_BMC_PWM0","FAN_BMC_PWM1","FAN_BMC_PWM2","FAN_BMC_PWM3","FAN_BMC_PWM4","FAN_BMC_PWM5","NMI_BUTTON","SPEAKER_BMC_R",
++ /*P0-P7*/ "RESET_BUTTON","RESET_OUT","POWER_BUTTON","POWER_OUT","FAN_BMC_PWM6","FAN_BMC_PWM7","FAN_BMC_PWM8","FAN_BMC_PWM9",
++ /*Q0-Q7*/ "FAN_BMC_TACH0","FAN_BMC_TACH1","FAN_BMC_TACH2","FAN_BMC_TACH3","FAN_BMC_TACH4","FAN_BMC_TACH5","FAN_BMC_TACH6","FAN_BMC_TACH7",
++ /*R0-R7*/ "FAN_BMC_TACH8","FAN_BMC_TACH9","","","","","","",
++ /*S0-S7*/ "RST_BMC_PCIE_MUX_N","FM_BMC_EUP_LOT6_N","","","","A_P3V_BAT_SCALED_EN","REMOTE_DEBUG_ENABLE","FM_PCHHOT_N",
++ /*T0-T7*/ "A_P12V_PSU_SCALED","A_P12V_AUX_SCALED","A_P3V3_SCALED","A_P5V_SCALED","A_PVNN_PCH_AUX_SENSOR","A_P1V05_PCH_AUX_SENSOR","A_P1V8_AUX_SENSOR","A_P3V_BAT_SCALED",
++ /*U0-U7*/ "A_PVCCIN_CPU0_SENSOR","A_PVCCIN_CPU1_SENSOR","A_PVCCINFAON_CPU0_SENSOR","A_PVCCINFAON_CPU1_SENSOR","A_PVCCFA_EHV_FIVRA_CPU0_SENSOR","A_PVCCFA_EHV_FIVRA_CPU1_SENSOR","A_PVCCD_HV_CPU0_SENSOR","A_PVCCD_HV_CPU1_SENSOR",
++ /*V0-V7*/ "SIO_S3","SIO_S5","TP_BMC_SIO_PWREQ_N","SIO_ONCONTROL","SIO_POWER_GOOD","LED_BMC_HB_LED_N","FM_BMC_SUSACK_N","",
++ /*W0-W7*/ "LPC_LAD0_ESPI_R_IO0","LPC_LAD1_ESPI_R_IO1","LPC_LAD2_ESPI_R_IO2","LPC_LAD3_ESPI_R_IO3","CLK_24M_66M_LPC0_ESPI_BMC","LPC_LFRAME_N_ESPI_CS0_BMC_N","IRQ_LPC_SERIRQ_ESPI_ALERT_N","RST_LPC_LRST_ESPI_RST_BMC_R_N",
++ /*X0-X7*/ "","SMI","POST_COMPLETE","","","","","",
++ /*Y0-Y7*/ "","IRQ_SML0_ALERT_BMC_R2_N","","IRQ_SML1_PMBUS_BMC_ALERT_N","SPI_BMC_BOOT_R_IO2","SPI_BMC_BOOT_R_IO3","PU_SPI_BMC_BOOT_ABR","PU_SPI_BMC_BOOT_WP_N",
++ /*Z0-Z7*/ "PWRGD_P3V3_RISER1","PWRGD_P3V3_RISER2","","HW_STRAP_5","HW_STRAP_6","HW_STRAP_7","HW_STRAP_2","HW_STRAP_3";
++};
++
++&gpio1 {
++ status = "disabled";
++ gpio-line-names = /* GPIO18 A-E */
++ /*A0-A7*/ "","","RST_EMMC_BMC_R_N","FM_SYS_FAN6_PRSNT_D_N","FM_SYS_FAN0_PRSNT_D_N","FM_SYS_FAN1_PRSNT_D_N","FM_SYS_FAN2_PRSNT_D_N","FM_SYS_FAN3_PRSNT_D_N",
++ /*B0-B7*/ "FM_SYS_FAN4_PRSNT_D_N","FM_SYS_FAN5_PRSNT_D_N","","FM_SYS_FAN7_PRSNT_D_N","RGMII_BMC_RMM4_TX_R_CLK","RGMII_BMC_RMM4_TX_R_CTRL","RGMII_BMC_RMM4_R_TXD0","RGMII_BMC_RMM4_R_TXD1",
++ /*C0-C7*/ "RGMII_BMC_RMM4_R_TXD2","RGMII_BMC_RMM4_R_TXD3","RGMII_BMC_RMM4_RX_CLK","RGMII_BMC_RMM4_RX_CTRL","RGMII_BMC_RMM4_RXD0","RGMII_BMC_RMM4_RXD1","RGMII_BMC_RMM4_RXD2","RGMII_BMC_RMM4_RXD3",
++ /*D0-D7*/ "EMMC_BMC_R_CLK","EMMC_BMC_R_CMD","EMMC_BMC_R_DATA0","EMMC_BMC_R_DATA1","EMMC_BMC_R_DATA2","EMMC_BMC_R_DATA3","EMMC_BMC_CD_N","EMMC_BMC_WP_N",
++ /*E0-E3*/ "EMMC_BMC_R_DATA4","EMMC_BMC_R_DATA5","EMMC_BMC_R_DATA6","EMMC_BMC_R_DATA7";
++};
++
++&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 */
++ /* Some lines have been renamed from the net names:
++ CPU1_PRESENCE -> FM_CPU0_SKTOCC_LVT3_N
++ CPU1_THERMTRIP -> H_CPU0_THERMTRIP_LVC1_N
++ CPU1_VRHOT -> IRQ_CPU0_VRHOT_N
++ CPU1_FIVR_FAULT -> H_CPU0_MON_FAIL_PLD_LVC1_N
++ CPU1_MEM_ABCD_VRHOT -> ??
++ CPU1_MEM_EFGH_VRHOT -> ??
++ CPU2_PRESENCE -> FM_CPU1_SKTOCC_LVT3_N
++ CPU2_THERMTRIP -> H_CPU1_THERMTRIP_LVC1_N
++ CPU2_VRHOT -> IRQ_CPU1_VRHOT_N
++ CPU2_FIVR_FAULT -> H_CPU1_MON_FAIL_PLD_LVC1_N
++ CPU2_MEM_ABCD_VRHOT -> ??
++ CPU2_MEM_EFGH_VRHOT -> ??
++
++ /*IA0-IA7*/ "CPU1_PRESENCE","CPU1_THERMTRIP","CPU1_VRHOT","CPU1_FIVR_FAULT","IRQ_CPU0_MEM_VRHOT_N","H_CPU0_MEMHOT_OUT_LVC1_N","FM_CPU0_PROC_ID0","FM_CPU0_PROC_ID1",
++ /*IB0-IB7*/ "WCPU_MISMATCH","IRQ_PSYS_CRIT_N","CPU2_PRESENCE","CPU2_THERMTRIP","CPU2_VRHOT","CPU2_FIVR_FAULT","IRQ_CPU1_MEM_VRHOT_N","H_CPU1_MEMHOT_OUT_LVC1_N",
++ /*IC0-IC7*/ "FM_CPU1_PROC_ID0","FM_CPU1_PROC_ID1","","","","","","",
++ /*ID0-ID7*/ "","","","","","","","",
++ /*IE0-IE7*/ "","","","","","","","",
++ /*IF0-IF7*/ "FPGA_REV_TEST_0","FPGA_REV_TEST_1","FPGA_REV_TEST_2","FPGA_REV_TEST_3","FPGA_REV_TEST_4","FPGA_REV_TEST_5","FPGA_REV_TEST_6","FPGA_REV_TEST_7",
++ /*IG0-IG7*/ "FPGA_REV_0","FPGA_REV_1","FPGA_REV_2","FPGA_REV_3","FPGA_REV_4","FPGA_REV_5","FPGA_REV_6","FPGA_REV_7",
++ /*IH0-IH7*/ "","WMEMX_PWR_FLT","WCPUX_MEM_PWR_FLT","PWRGD_P3V3_FF","WPSU_PWR_FLT","","","WPCH_PWR_FLT",
++ /*II0-II7*/ "FM_CPU0_PKGID0","FM_CPU0_PKGID1","FM_CPU0_PKGID2","H_CPU0_MEMTRIP_LVC1_N","FM_CPU1_PKGID0","FM_CPU1_PKGID1","FM_CPU1_PKGID2","H_CPU1_MEMTRIP_LVC1_N",
++ /*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";
++};
++
++&mdio1 {
++ status = "okay";
++
++ ethphy1: ethernet-phy@0 {
++ compatible = "ethernet-phy-ieee802.3-c22";
++ reg = <0>;
++ };
++};
++
++&mac1 {
++ status = "okay";
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_rgmii2_default>;
++ clocks = <&syscon ASPEED_CLK_GATE_MAC2CLK>,
++ <&syscon ASPEED_CLK_MAC2RCLK>;
++ clock-names = "MACCLK", "RCLK";
++ phy-mode = "rgmii";
++ phy-handle = <&ethphy1>;
++};
++
++&mdio2 {
++ status = "okay";
++
++ ethphy2: ethernet-phy@1 {
++ compatible = "ethernet-phy-ieee802.3-c22";
++ reg = <0>;
++ };
++};
++
++&adc0 {
++ status = "okay";
++};
++
++&adc1 {
++ status = "okay";
++};
++
++&uart1 {
++ status = "okay";
++ // Workaround for A0
++ compatible = "snps,dw-apb-uart";
++ 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";
++ // Workaround for A0
++ compatible = "snps,dw-apb-uart";
++ 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";
++ // Workaround for A0
++ compatible = "snps,dw-apb-uart";
++ pinctrl-0 = <>;
++};
++
++&uart4 {
++ status = "okay";
++ // Workaround for A0
++ compatible = "snps,dw-apb-uart";
++ pinctrl-0 = <>;
++};
++
++&uart5 {
++ status = "okay";
++ // Workaround for A0
++ compatible = "snps,dw-apb-uart";
++};
++
++&uart_routing {
++ status = "okay";
++};
++
++&emmc_controller{
++ status = "okay";
++};
++
++&emmc {
++ non-removable;
++ bus-width = <4>;
++ max-frequency = <52000000>;
++};
++
++&i2c0 {
++ /* SMB_CHASSENSOR_STBY_LVC3 */
++ multi-master;
++ status = "okay";
++};
++
++&i2c4 {
++ /* SMB_HSBP_STBY_LVC3_R */
++ multi-master;
++ status = "okay";
++
++ hsbp0@10 {
++ compatible = "slave-mqueue";
++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>;
++ };
++};
++
++&i2c5 {
++ /* SMB_SMLINK0_STBY_LVC3_R2 */
++ bus-frequency = <1000000>;
++ multi-master;
++ status = "okay";
++
++ smlink0@10 {
++ compatible = "slave-mqueue";
++ reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>;
++ };
++};
++
++&i2c6 {
++ /* SMB_TEMPSENSOR_STBY_LVC3_R */
++ multi-master;
++ status = "okay";
++};
++
++&i2c7 {
++ /* SMB_PMBUS_SML1_STBY_LVC3_R */
++ multi-master;
++ #retries = <3>;
++
++ status = "okay";
++};
++
++&i2c8 {
++ /* SMB_PCIE_STBY_LVC3_R */
++ multi-master;
++ status = "okay";
++};
++
++&i2c9 {
++ /* SMB_HOST_STBY_BMC_LVC3_R */
++ multi-master;
++ status = "okay";
++};
++
++&i2c12 {
++ /* SMB_CPU_PIROM */
++ multi-master;
++ status = "okay";
++};
++
++&i2c13 {
++ /* SMB_IPMB_STBY_LVC3_R */
++ multi-master;
++ status = "okay";
++};
++
++&i3c0 {
++ /* I3C_SPD_DDRABCD_CPU0_BMC ; FIXME: i3c driver hangs kernel on probe...*/
++ status = "disabled";
++};
++
++&i3c1 {
++ /* I3C_SPD_DDREFGH_CPU0_BMC */
++ status = "disabled";
++};
++
++&i3c2 {
++ /* I3C_SPD_DDRABCD_CPU1_BMC */
++ status = "disabled";
++};
++
++&i3c3 {
++ /* I3C_SPD_DDREFGH_CPU1_BMC */
++ status = "disabled";
++};
++
++&gfx {
++ status = "okay";
++ memory-region = <&gfx_memory>;
++};
++
++&pwm_tacho {
++ status = "disabled"; /* FIXME: disabled because of bug in driver */
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_pwm0_default &pinctrl_pwm1_default
++ &pinctrl_pwm2_default &pinctrl_pwm3_default
++ &pinctrl_pwm4_default &pinctrl_pwm5_default
++ &pinctrl_pwm12g1_default &pinctrl_pwm13g1_default
++ &pinctrl_pwm14g1_default &pinctrl_pwm15g1_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 <0x18 0x19>;
++ };
++ fan@7 {
++ reg = <0x07>;
++ aspeed,fan-tach-ch = /bits/ 8 <0x1A 0x1B>;
++ };
++ fan@8 {
++ reg = <0x08>;
++ aspeed,fan-tach-ch = /bits/ 8 <0x1C 0x1D>;
++ };
++ fan@9 {
++ reg = <0x09>;
++ aspeed,fan-tach-ch = /bits/ 8 <0x1E 0x1F>;
++ };
++};
++
++&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/0001-arm-dts-base-aspeed-g6-dtsi-fixups.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-base-aspeed-g6-dtsi-fixups.patch
new file mode 100644
index 000000000..d75229a15
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0001-arm-dts-base-aspeed-g6-dtsi-fixups.patch
@@ -0,0 +1,212 @@
+From 5e5758fe5929766a9b4677b86f9343a777526fe8 Mon Sep 17 00:00:00 2001
+From: Vernon Mauery <vernon.mauery@linux.intel.com>
+Date: Thu, 12 Sep 2019 15:55:39 +0800
+Subject: [PATCH] arm: dts: base aspeed g6 dtsi fixups
+
+Additions to the base g6 dtsi file for Aspeed ast2600 systems.
+This mostly includes entries for the drivers that are not upstream.
+
+Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
+---
+ arch/arm/boot/dts/aspeed-g6.dtsi | 129 +++++++++++++++++++++++++++++-
+ include/dt-bindings/clock/ast2600-clock.h | 8 ++
+ 2 files changed, 135 insertions(+), 2 deletions(-)
+
+diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
+index 2af9efa1faa1..54add29d8217 100644
+--- a/arch/arm/boot/dts/aspeed-g6.dtsi
++++ b/arch/arm/boot/dts/aspeed-g6.dtsi
+@@ -28,6 +28,12 @@
+ i2c13 = &i2c13;
+ i2c14 = &i2c14;
+ i2c15 = &i2c15;
++ i3c0 = &i3c0;
++ i3c1 = &i3c1;
++ i3c2 = &i3c2;
++ i3c3 = &i3c3;
++ i3c4 = &i3c4;
++ i3c5 = &i3c5;
+ serial0 = &uart1;
+ serial1 = &uart2;
+ serial2 = &uart3;
+@@ -297,11 +303,21 @@
+ quality = <100>;
+ };
+
++ adc: adc@1e6e9000 {
++ compatible = "aspeed,ast2500-adc";
++ reg = <0x1e6e9000 0x100>;
++ clocks = <&syscon ASPEED_CLK_APB2>;
++ interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
++ resets = <&syscon ASPEED_RESET_ADC>;
++ #io-channel-cells = <1>;
++ status = "disabled";
++ };
++
+ gpio0: gpio@1e780000 {
+ #gpio-cells = <2>;
+ gpio-controller;
+ compatible = "aspeed,ast2600-gpio";
+- reg = <0x1e780000 0x800>;
++ reg = <0x1e780000 0x200>;
+ interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-ranges = <&pinctrl 0 0 208>;
+ ngpios = <208>;
+@@ -314,7 +330,7 @@
+ #gpio-cells = <2>;
+ gpio-controller;
+ compatible = "aspeed,ast2600-gpio";
+- reg = <0x1e780800 0x800>;
++ reg = <0x1e780800 0x200>;
+ interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-ranges = <&pinctrl 0 208 36>;
+ ngpios = <36>;
+@@ -398,6 +414,13 @@
+ ranges = <0x0 0x1e78b000 0x100>;
+ };
+
++ i3c: bus@1e7a0000 {
++ compatible = "simple-bus";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges = <0 0x1e7a0000 0x8000>;
++ };
++
+ lpc: lpc@1e789000 {
+ compatible = "aspeed,ast2600-lpc", "simple-mfd";
+ reg = <0x1e789000 0x1000>;
+@@ -486,6 +509,20 @@
+ sio_regs: regs {
+ 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,ast2600-mbox";
++ reg = <0x180 0x5c>;
++ interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>;
++ #mbox-cells = <1>;
++ };
+ };
+ };
+
+@@ -871,3 +908,91 @@
+ status = "disabled";
+ };
+ };
++
++&i3c {
++ i3c0: i3c0@2000 {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ #interrupt-cells = <1>;
++ reg = <0x2000 0x1000>;
++ compatible = "snps,dw-i3c-master-1.00a";
++ clocks = <&syscon ASPEED_CLK_APB2>;
++ resets = <&syscon ASPEED_RESET_I3C0>;
++ bus-frequency = <100000>;
++ interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ i3c1: i3c1@3000 {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ #interrupt-cells = <1>;
++ reg = <0x3000 0x1000>;
++ compatible = "snps,dw-i3c-master-1.00a";
++ clocks = <&syscon ASPEED_CLK_APB2>;
++ resets = <&syscon ASPEED_RESET_I3C1>;
++ bus-frequency = <100000>;
++ interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ i3c2: i3c2@4000 {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ #interrupt-cells = <1>;
++ reg = <0x4000 0x1000>;
++ compatible = "snps,dw-i3c-master-1.00a";
++ clocks = <&syscon ASPEED_CLK_APB2>;
++ resets = <&syscon ASPEED_RESET_I3C2>;
++ bus-frequency = <100000>;
++ interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_i3c3_default>;
++ status = "disabled";
++ };
++
++ i3c3: i3c3@5000 {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ #interrupt-cells = <1>;
++ reg = <0x5000 0x1000>;
++ compatible = "snps,dw-i3c-master-1.00a";
++ clocks = <&syscon ASPEED_CLK_APB2>;
++ resets = <&syscon ASPEED_RESET_I3C3>;
++ bus-frequency = <100000>;
++ interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_i3c4_default>;
++ status = "disabled";
++ };
++
++ i3c4: i3c4@6000 {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ #interrupt-cells = <1>;
++ reg = <0x6000 0x1000>;
++ compatible = "snps,dw-i3c-master-1.00a";
++ clocks = <&syscon ASPEED_CLK_APB2>;
++ resets = <&syscon ASPEED_RESET_I3C4>;
++ bus-frequency = <100000>;
++ interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_i3c5_default>;
++ status = "disabled";
++ };
++
++ i3c5: i3c5@7000 {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ #interrupt-cells = <1>;
++ reg = <0x7000 0x1000>;
++ compatible = "snps,dw-i3c-master-1.00a";
++ clocks = <&syscon ASPEED_CLK_APB2>;
++ resets = <&syscon ASPEED_RESET_I3C5>;
++ bus-frequency = <100000>;
++ interrupts = <GIC_SPI 107 IRQ_TYPE_LEVEL_HIGH>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_i3c6_default>;
++ status = "disabled";
++ };
++};
+diff --git a/include/dt-bindings/clock/ast2600-clock.h b/include/dt-bindings/clock/ast2600-clock.h
+index 62b9520a00fd..3d90582a813f 100644
+--- a/include/dt-bindings/clock/ast2600-clock.h
++++ b/include/dt-bindings/clock/ast2600-clock.h
+@@ -91,6 +91,14 @@
+ /* Only list resets here that are not part of a gate */
+ #define ASPEED_RESET_ADC 55
+ #define ASPEED_RESET_JTAG_MASTER2 54
++#define ASPEED_RESET_I3C7 47
++#define ASPEED_RESET_I3C6 46
++#define ASPEED_RESET_I3C5 45
++#define ASPEED_RESET_I3C4 44
++#define ASPEED_RESET_I3C3 43
++#define ASPEED_RESET_I3C2 42
++#define ASPEED_RESET_I3C1 41
++#define ASPEED_RESET_I3C0 40
+ #define ASPEED_RESET_I3C_DMA 39
+ #define ASPEED_RESET_PWM 37
+ #define ASPEED_RESET_PECI 36
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0002-Add-Aspeed-fmc-spi-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0002-Add-Aspeed-fmc-spi-driver.patch
new file mode 100644
index 000000000..08e350c15
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0002-Add-Aspeed-fmc-spi-driver.patch
@@ -0,0 +1,645 @@
+From 1365492683b47f63d470d0666fee258a5c7ca3c3 Mon Sep 17 00:00:00 2001
+From: Vernon Mauery <vernon.mauery@intel.com>
+Date: Thu, 12 Sep 2019 15:26:08 -0700
+Subject: [PATCH 04/52] Add Aspeed fmc-spi driver
+
+Add the Aspeed fmc-spi driver from the Apeed SDK v5.02
+
+Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
+---
+ arch/arm/boot/dts/aspeed-g6.dtsi | 43 ++-
+ drivers/spi/Kconfig | 6 +
+ drivers/spi/Makefile | 1 +
+ drivers/spi/fmc_spi.c | 530 +++++++++++++++++++++++++++++++
+ 4 files changed, 579 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/spi/fmc_spi.c
+
+diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
+index 42f644ac8111..1aab48fbf49e 100644
+--- a/arch/arm/boot/dts/aspeed-g6.dtsi
++++ b/arch/arm/boot/dts/aspeed-g6.dtsi
+@@ -90,7 +90,7 @@
+ <0x40464000 0x2000>,
+ <0x40466000 0x2000>;
+ };
+-
++#if 1
+ fmc: spi@1e620000 {
+ reg = < 0x1e620000 0xc4
+ 0x20000000 0x10000000 >;
+@@ -169,6 +169,47 @@
+ status = "disabled";
+ };
+ };
++#else
++ spi0: spi@1e620000 {
++ /* reg : cs0 : cs1 : cs2 */
++ reg = <0x1e620000 0x100
++ 0x20000000 0x40
++ 0x28000000 0x40>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ compatible = "aspeed,fmc-spi";
++ clocks = <&syscon ASPEED_CLK_AHB>;
++ status = "disable";
++ number_of_chip_select = /bits/ 16 <2>;
++ interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ spi1: spi1@1e630000 {
++ /* reg : cs0 : cs1 */
++ reg = <0x1e630000 0x100
++ 0x30000000 0x20
++ 0x32000000 0x20>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ compatible = "aspeed,fmc-spi";
++ clocks = <&syscon ASPEED_CLK_AHB>;
++ status = "disable";
++ number_of_chip_select = /bits/ 16 <2>;
++ };
++
++ spi2: spi2@1e631000 {
++ /* reg : cs0 : cs1 */
++ reg = <0x1e631000 0x100
++ 0x38000000 0x20
++ 0x3A000000 0x20>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ compatible = "aspeed,fmc-spi";
++ clocks = <&syscon ASPEED_CLK_AHB>;
++ status = "disable";
++ number_of_chip_select = /bits/ 16 <2>;
++ };
++#endif
+
+ mdio0: mdio@1e650000 {
+ compatible = "aspeed,ast2600-mdio";
+diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
+index 6ee514fd0920..9f32c31ffa3c 100644
+--- a/drivers/spi/Kconfig
++++ b/drivers/spi/Kconfig
+@@ -57,6 +57,12 @@ config SPI_MEM
+
+ comment "SPI Master Controller Drivers"
+
++config SPI_FMC
++ tristate "Aspeed FMC SPI Controller"
++ depends on ARCH_ASPEED
++ help
++ This selects a driver for the AST FMC SPI Controller
++
+ config SPI_ALTERA
+ tristate "Altera SPI Controller"
+ help
+diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
+index adbebee93a75..224b9b71e29c 100644
+--- a/drivers/spi/Makefile
++++ b/drivers/spi/Makefile
+@@ -13,6 +13,7 @@ obj-$(CONFIG_SPI_SPIDEV) += spidev.o
+ obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
+
+ # SPI master controller drivers (bus)
++obj-$(CONFIG_SPI_FMC) += fmc_spi.o
+ obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
+ obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o
+ obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
+diff --git a/drivers/spi/fmc_spi.c b/drivers/spi/fmc_spi.c
+new file mode 100644
+index 000000000000..f21f7a00496e
+--- /dev/null
++++ b/drivers/spi/fmc_spi.c
+@@ -0,0 +1,530 @@
++/*
++ * fmc_spi.c - FMC SPI driver for the Aspeed SoC
++ *
++ * Copyright (C) ASPEED Technology Inc.
++ * Ryan Chen <ryan_chen@aspeedtech.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ *
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/clk.h>
++#include <linux/sched.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/ioport.h>
++#include <linux/platform_device.h>
++#include <linux/err.h>
++#include <linux/errno.h>
++#include <linux/wait.h>
++#include <linux/delay.h>
++#include <linux/spi/spi.h>
++#include <linux/spi/flash.h>
++
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++#include <linux/of_platform.h>
++
++/******************************************************************************/
++/* AST_SPI_CONFIG 0x00 : SPI00 CE Type Setting Register */
++#define AST_G5_SPI_CONF_CE1_WEN (0x1 << 17)
++#define AST_G5_SPI_CONF_CE0_WEN (0x1 << 16)
++
++#define SPI_CONF_CE0_WEN (0x1)
++
++/* Register offsets */
++#define FMC_SPI_CONFIG 0x00
++#define FMC_SPI_CTRL 0x04
++#define FMC_SPI_DMA_STS 0x08
++
++#define FMC_SPI_CE0_CTRL 0x10
++#define FMC_SPI_CE1_CTRL 0x14
++
++#define AST_SPI_DMA_CTRL 0x80
++#define AST_SPI_DMA_FLASH_BASE 0x84
++#define AST_SPI_DMA_DRAM_BASE 0x88
++#define AST_SPI_DMA_LENGTH 0x8c
++
++/* AST_FMC_CONFIG 0x00 : FMC00 CE Type Setting Register */
++#define FMC_CONF_LAGACY_DIS (0x1 << 31)
++#define FMC_CONF_CE1_WEN (0x1 << 17)
++#define FMC_CONF_CE0_WEN (0x1 << 16)
++#define FMC_CONF_CE1_SPI (0x2 << 2)
++#define FMC_CONF_CE0_SPI (0x2)
++
++/* FMC_SPI_CTRL : 0x04 : FMC04 CE Control Register */
++#define FMC_CTRL_CE1_4BYTE_MODE (0x1 << 1)
++#define FMC_CTRL_CE0_4BYTE_MODE (0x1)
++
++/* FMC_SPI_DMA_STS : 0x08 : FMC08 Interrupt Control and Status Register */
++#define FMC_STS_DMA_READY 0x0800
++#define FMC_STS_DMA_CLEAR 0x0800
++
++/* FMC_CE0_CTRL for SPI 0x10, 0x14, 0x18, 0x1c, 0x20 */
++#define SPI_IO_MODE_MASK (3 << 28)
++#define SPI_SINGLE_BIT (0 << 28)
++#define SPI_DUAL_MODE (0x2 << 28)
++#define SPI_DUAL_IO_MODE (0x3 << 28)
++#define SPI_QUAD_MODE (0x4 << 28)
++#define SPI_QUAD_IO_MODE (0x5 << 28)
++
++#define SPI_CE_WIDTH(x) (x << 24)
++#define SPI_CMD_DATA_MASK (0xff << 16)
++#define SPI_CMD_DATA(x) (x << 16)
++#define SPI_DUMMY_CMD (1 << 15)
++#define SPI_DUMMY_HIGH (1 << 14)
++//#define SPI_CLK_DIV (1 << 13) ?? TODO ask....
++//#define SPI_ADDR_CYCLE (1 << 13) ?? TODO ask....
++#define SPI_CMD_MERGE_DIS (1 << 12)
++#define SPI_CLK_DIV(x) (x << 8)
++#define SPI_CLK_DIV_MASK (0xf << 8)
++
++#define SPI_DUMMY_LOW_MASK (0x3 << 6)
++#define SPI_DUMMY_LOW(x) ((x) << 6)
++#define SPI_LSB_FIRST_CTRL (1 << 5)
++#define SPI_CPOL_1 (1 << 4)
++#define SPI_DUAL_DATA (1 << 3)
++#define SPI_CE_INACTIVE (1 << 2)
++#define SPI_CMD_MODE_MASK (0x3)
++#define SPI_CMD_NORMAL_READ_MODE 0
++#define SPI_CMD_READ_CMD_MODE 1
++#define SPI_CMD_WRITE_CMD_MODE 2
++#define SPI_CMD_USER_MODE 3
++
++/* AST_SPI_DMA_CTRL 0x80 */
++#define FMC_DMA_ENABLE (0x1)
++
++/******************************************************************************/
++struct fmc_spi_host {
++ void __iomem *base;
++ void __iomem *ctrl_reg;
++ u32 buff[5];
++ struct spi_master *master;
++ struct spi_device *spi_dev;
++ struct device *dev;
++ u32 ahb_clk;
++ spinlock_t lock;
++};
++
++static u32 ast_spi_calculate_divisor(struct fmc_spi_host *host,
++ u32 max_speed_hz)
++{
++ // [0] ->15 : HCLK , HCLK/16
++ u8 SPI_DIV[16] = {
++ 16, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 0
++ };
++ u32 i, spi_cdvr = 0;
++
++ for (i = 1; i < 17; i++) {
++ if (max_speed_hz >= (host->ahb_clk / i)) {
++ spi_cdvr = SPI_DIV[i - 1];
++ break;
++ }
++ }
++
++ // printk("hclk is %d, divisor is %d, target :%d , cal speed %d\n", host->ahb_clk, spi_cdvr, spi->max_speed_hz, hclk/i);
++ return spi_cdvr;
++}
++
++/* the spi->mode bits understood by this driver: */
++#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH)
++
++static int fmc_spi_setup(struct spi_device *spi)
++{
++ struct fmc_spi_host *host =
++ (struct fmc_spi_host *)spi_master_get_devdata(spi->master);
++ unsigned int bits = spi->bits_per_word;
++ u32 fmc_config = 0;
++ u32 spi_ctrl = 0;
++ u32 divisor;
++
++ // dev_dbg(host->dev, "fmc_spi_setup() cs: %d, spi->mode %d \n", spi->chip_select, spi->mode);
++ // printk("fmc_spi_setup() cs: %d, spi->mode %d spi->max_speed_hz %d , spi->bits_per_word %d \n", spi->chip_select, spi->mode, spi->max_speed_hz, spi->bits_per_word);
++
++ switch (spi->chip_select) {
++ case 0:
++ fmc_config |= FMC_CONF_CE0_WEN | FMC_CONF_CE0_SPI;
++ host->ctrl_reg = host->base + FMC_SPI_CE0_CTRL;
++ break;
++ case 1:
++ fmc_config |= FMC_CONF_CE1_WEN | FMC_CONF_CE1_SPI;
++ host->ctrl_reg = host->base + FMC_SPI_CE0_CTRL;
++ break;
++ default:
++ dev_dbg(&spi->dev,
++ "setup: invalid chipselect %u (%u defined)\n",
++ spi->chip_select, spi->master->num_chipselect);
++ return -EINVAL;
++ break;
++ }
++ writel(fmc_config, host->base);
++
++ if (bits == 0)
++ bits = 8;
++
++ if (bits < 8 || bits > 16) {
++ dev_dbg(&spi->dev,
++ "setup: invalid bits_per_word %u (8 to 16)\n", bits);
++ return -EINVAL;
++ }
++
++ if (spi->mode & ~MODEBITS) {
++ dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n",
++ spi->mode & ~MODEBITS);
++ return -EINVAL;
++ }
++
++ /* see notes above re chipselect */
++ if ((spi->chip_select == 0) && (spi->mode & SPI_CS_HIGH)) {
++ dev_dbg(&spi->dev, "setup: can't be active-high\n");
++ return -EINVAL;
++ }
++
++ /*
++ * Pre-new_1 chips start out at half the peripheral
++ * bus speed.
++ */
++
++ if (spi->max_speed_hz) {
++ /* Set the SPI slaves select and characteristic control register */
++ divisor = ast_spi_calculate_divisor(host, spi->max_speed_hz);
++ } else {
++ /* speed zero means "as slow as possible" */
++ divisor = 15;
++ }
++
++ spi_ctrl &= ~SPI_CLK_DIV_MASK;
++ // printk("set div %x \n",divisor);
++ //TODO MASK first
++ spi_ctrl |= SPI_CLK_DIV(divisor);
++
++ /* only support mode 0 (CPOL=0, CPHA=0) and cannot support mode 1 ~ mode 3 */
++
++#if 0
++ if (SPI_CPHA & spi->mode)
++ cpha = SPI_CPHA_1;
++ else
++ cpha = SPI_CPHA_0;
++#endif
++
++ // if (SPI_CPOL & spi->mode)
++ // spi_ctrl |= SPI_CPOL_1;
++ // else
++ // spi_ctrl &= ~SPI_CPOL_1;
++
++ //ISSUE : ast spi ctrl couldn't use mode 3, so fix mode 0
++ spi_ctrl &= ~SPI_CPOL_1;
++
++ if (SPI_LSB_FIRST & spi->mode)
++ spi_ctrl |= SPI_LSB_FIRST_CTRL;
++ else
++ spi_ctrl &= ~SPI_LSB_FIRST_CTRL;
++
++ /* Configure SPI controller */
++ writel(spi_ctrl, host->ctrl_reg);
++
++ // printk("ctrl %x, ", spi_ctrl);
++ return 0;
++}
++
++static int fmc_spi_transfer(struct spi_device *spi, struct spi_message *msg)
++{
++ struct fmc_spi_host *host =
++ (struct fmc_spi_host *)spi_master_get_devdata(spi->master);
++ struct spi_transfer *xfer;
++ const u8 *tx_buf;
++ u8 *rx_buf;
++ unsigned long flags;
++
++ int i = 0, j = 0;
++
++ // dev_dbg(host->dev, "xfer %s \n", dev_name(&spi->dev));
++ // printk("xfer spi->chip_select %d \n", spi->chip_select);
++
++ host->spi_dev = spi;
++ spin_lock_irqsave(&host->lock, flags);
++
++ writel(readl(host->ctrl_reg) | SPI_CMD_USER_MODE, host->ctrl_reg);
++ msg->actual_length = 0;
++ msg->status = 0;
++
++ list_for_each_entry (xfer, &msg->transfers, transfer_list) {
++ dev_dbg(host->dev,
++ "xfer[%d] %p: width %d, len %u, tx %p/%08x, rx %p/%08x\n",
++ j, xfer, xfer->bits_per_word, xfer->len, xfer->tx_buf,
++ xfer->tx_dma, xfer->rx_buf, xfer->rx_dma);
++
++ tx_buf = xfer->tx_buf;
++ rx_buf = xfer->rx_buf;
++
++ if (tx_buf != 0) {
++#if 0
++ printk("tx : ");
++ if(xfer->len > 10) {
++ for(i=0;i<10;i++)
++ printk("%x ",tx_buf[i]);
++ } else {
++ for(i=0;i<xfer->len;i++)
++ printk("%x ",tx_buf[i]);
++ }
++ printk("\n");
++#endif
++ for (i = 0; i < xfer->len; i++) {
++ writeb(tx_buf[i],
++ (void *)host->buff
++ [host->spi_dev->chip_select]);
++ }
++ }
++ //Issue need clarify
++ udelay(1);
++ if (rx_buf != 0) {
++ for (i = 0; i < xfer->len; i++) {
++ rx_buf[i] = readb(
++ (void *)host->buff
++ [host->spi_dev->chip_select]);
++ }
++#if 0
++ printk("rx : ");
++ if(xfer->len > 10) {
++ for(i=0;i<10;i++)
++ printk(" %x",rx_buf[i]);
++ } else {
++ for(i=0;i<xfer->len;i++)
++ printk(" %x",rx_buf[i]);
++ }
++ printk("\n");
++#endif
++ }
++ dev_dbg(host->dev, "old msg->actual_length %d , +len %d \n",
++ msg->actual_length, xfer->len);
++ msg->actual_length += xfer->len;
++ dev_dbg(host->dev, "new msg->actual_length %d \n",
++ msg->actual_length);
++ // j++;
++ }
++
++ // writel( SPI_CE_INACTIVE | readl(host->spi_data->ctrl_reg),host->spi_data->ctrl_reg);
++ writel(readl(host->ctrl_reg) & ~SPI_CMD_USER_MODE, host->ctrl_reg);
++ msg->status = 0;
++
++ msg->complete(msg->context);
++
++ // spin_unlock(&host->lock);
++ spin_unlock_irqrestore(&host->lock, flags);
++
++ return 0;
++}
++
++static void fmc_spi_cleanup(struct spi_device *spi)
++{
++ struct fmc_spi_host *host = spi_master_get_devdata(spi->master);
++ unsigned long flags;
++ dev_dbg(host->dev, "fmc_spi_cleanup() \n");
++
++ spin_lock_irqsave(&host->lock, flags);
++ // if (host->stay == spi) {
++ // host->stay = NULL;
++ // cs_deactivate(host, spi);
++ // }
++ spin_unlock_irqrestore(&host->lock, flags);
++}
++
++#if 0
++static int fmc_spi_flash_read(struct spi_device *spi,
++ struct spi_flash_read_message *msg)
++{
++// struct fmc_spi_host *host = spi_master_get_devdata(spi->master);
++ int ret = 0;
++
++// printk("read msg->from %x, msg->len %x , msg->buf %x , msg->addr_width %d , msg->dummy_bytes %x , msg->read_opcode %x \n", msg->from, msg->len, msg->buf, msg->addr_width, msg->dummy_bytes, msg->read_opcode);
++
++// memcpy_fromio(msg->buf, b53spi->mmio_base + msg->from, msg->len);
++ msg->retlen = msg->len;
++
++ return ret;
++}
++#endif
++
++static int fmc_spi_probe(struct platform_device *pdev)
++{
++ struct resource *res;
++ struct fmc_spi_host *host;
++ struct spi_master *master;
++ struct clk *clk;
++ int cs_num = 0;
++ int err = 0;
++
++ dev_dbg(&pdev->dev, "fmc_spi_probe() \n");
++
++ master = spi_alloc_master(&pdev->dev, sizeof(struct fmc_spi_host));
++ if (NULL == master) {
++ dev_err(&pdev->dev, "No memory for spi_master\n");
++ err = -ENOMEM;
++ goto err_nomem;
++ }
++
++ /* the spi->mode bits understood by this driver: */
++ master->mode_bits =
++ SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_RX_DUAL | SPI_TX_DUAL;
++ master->bits_per_word_mask = SPI_BPW_MASK(8);
++
++ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_RX_DUAL;
++ // master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 16);
++ master->dev.of_node = pdev->dev.of_node;
++ master->bus_num = pdev->id;
++ // master->num_chipselect = master->dev.of_node ? 0 : 4;
++ platform_set_drvdata(pdev, master);
++
++ host = spi_master_get_devdata(master);
++ memset(host, 0, sizeof(struct fmc_spi_host));
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (!res) {
++ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM 0\n");
++ err = -ENXIO;
++ goto err_no_io_res;
++ }
++
++ host->base = devm_ioremap_resource(&pdev->dev, res);
++ if (!host->base) {
++ dev_err(&pdev->dev, "cannot remap register\n");
++ err = -EIO;
++ goto err_no_io_res;
++ }
++
++ clk = devm_clk_get(&pdev->dev, NULL);
++ if (IS_ERR(clk)) {
++ dev_err(&pdev->dev, "no clock defined\n");
++ return -ENODEV;
++ }
++ host->ahb_clk = clk_get_rate(clk);
++
++ dev_dbg(&pdev->dev, "remap phy %x, virt %x \n", (u32)res->start,
++ (u32)host->base);
++
++ host->master = spi_master_get(master);
++
++ if (of_property_read_u16(pdev->dev.of_node, "number_of_chip_select",
++ &host->master->num_chipselect))
++ goto err_register;
++
++ for (cs_num = 0; cs_num < host->master->num_chipselect; cs_num++) {
++ res = platform_get_resource(pdev, IORESOURCE_MEM, cs_num + 1);
++ if (!res) {
++ dev_err(&pdev->dev, "cannot get IORESOURCE_IO 0\n");
++ return -ENXIO;
++ }
++
++ host->buff[cs_num] =
++ (u32)devm_ioremap_resource(&pdev->dev, res);
++ if (!host->buff[cs_num]) {
++ dev_err(&pdev->dev, "cannot remap buffer \n");
++ err = -EIO;
++ goto err_no_io_res;
++ }
++
++ dev_dbg(&pdev->dev, "remap io phy %x, virt %x \n",
++ (u32)res->start, (u32)host->buff[cs_num]);
++ }
++
++ host->master->bus_num = pdev->id;
++ host->dev = &pdev->dev;
++
++ /* Setup the state for bitbang driver */
++ host->master->setup = fmc_spi_setup;
++ host->master->transfer = fmc_spi_transfer;
++ host->master->cleanup = fmc_spi_cleanup;
++ // host->master->spi_flash_read = fmc_spi_flash_read;
++
++ platform_set_drvdata(pdev, host);
++
++ /* Register our spi controller */
++ err = devm_spi_register_master(&pdev->dev, host->master);
++ if (err) {
++ dev_err(&pdev->dev, "failed to register SPI master\n");
++ goto err_register;
++ }
++
++ dev_dbg(&pdev->dev, "fmc_spi : driver load \n");
++
++ return 0;
++
++err_register:
++ spi_master_put(host->master);
++ iounmap(host->base);
++ for (cs_num = 0; cs_num < host->master->num_chipselect; cs_num++) {
++ iounmap((void *)host->buff[cs_num]);
++ }
++
++err_no_io_res:
++ kfree(master);
++ kfree(host);
++
++err_nomem:
++ return err;
++}
++
++static int fmc_spi_remove(struct platform_device *pdev)
++{
++ struct resource *res0;
++ struct fmc_spi_host *host = platform_get_drvdata(pdev);
++
++ dev_dbg(host->dev, "fmc_spi_remove()\n");
++
++ if (!host)
++ return -1;
++
++ res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ release_mem_region(res0->start, res0->end - res0->start + 1);
++ iounmap(host->base);
++ iounmap(host->buff);
++
++ platform_set_drvdata(pdev, NULL);
++ spi_unregister_master(host->master);
++ spi_master_put(host->master);
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int fmc_spi_suspend(struct platform_device *pdev, pm_message_t msg)
++{
++ return 0;
++}
++
++static int fmc_spi_resume(struct platform_device *pdev)
++{
++ return 0;
++}
++#else
++#define fmc_spi_suspend NULL
++#define fmc_spi_resume NULL
++#endif
++
++static const struct of_device_id fmc_spi_of_match[] = {
++ { .compatible = "aspeed,fmc-spi" },
++ {},
++};
++
++static struct platform_driver fmc_spi_driver = {
++ .probe = fmc_spi_probe,
++ .remove = fmc_spi_remove,
++#ifdef CONFIG_PM
++ .suspend = fmc_spi_suspend,
++ .resume = fmc_spi_resume,
++#endif
++ .driver = {
++ .name = KBUILD_MODNAME,
++ .of_match_table = fmc_spi_of_match,
++ },
++};
++
++module_platform_driver(fmc_spi_driver);
++
++MODULE_DESCRIPTION("FMC SPI Driver");
++MODULE_AUTHOR("Ryan Chen");
++MODULE_LICENSE("GPL");
+--
+2.17.1
+
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..6123b8701
--- /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,173 @@
+From 12ef9a4189cd44212a5a5bd2e1c6fce0756ace9f 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 | 17 ++++++++++
+ drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c | 17 ++++++++++
+ drivers/pinctrl/aspeed/pinctrl-aspeed.c | 53 ++++++++++++++++++++++++++++++
+ drivers/pinctrl/aspeed/pinctrl-aspeed.h | 3 ++
+ 4 files changed, 90 insertions(+)
+
+diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
+index 0cab4c2576e2..c5406e2da320 100644
+--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
+@@ -2780,6 +2780,22 @@ static int aspeed_g5_sig_expr_set(struct aspeed_pinmux_data *ctx,
+ return 0;
+ }
+
++#define GPIOE1 33
++#define GPIOE3 35
++static void aspeed_g5_gpio_disable_free(struct pinctrl_dev *pctldev,
++ struct pinctrl_gpio_range *range,
++ unsigned int offset)
++{
++ /*
++ * If we're freeing GPIOE1 (33) or GPIOE3 (35) then re-enable the
++ * pass-through mux setting; otherwise, do nothing.
++ */
++ if (offset != GPIOE1 && offset != GPIOE3)
++ return;
++
++ aspeed_gpio_disable_free(pctldev, range, offset);
++}
++
+ static const struct aspeed_pin_config_map aspeed_g5_pin_config_map[] = {
+ { PIN_CONFIG_BIAS_PULL_DOWN, 0, 1, BIT_MASK(0)},
+ { PIN_CONFIG_BIAS_PULL_DOWN, -1, 0, BIT_MASK(0)},
+@@ -2815,6 +2831,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_g5_gpio_disable_free,
+ .strict = true,
+ };
+
+diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c
+index fb96e8d2e6c8..bcd8c5656265 100644
+--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c
++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c
+@@ -2655,6 +2655,22 @@ static int aspeed_g6_sig_expr_set(struct aspeed_pinmux_data *ctx,
+ return 0;
+ }
+
++#define GPIOP1 121
++#define GPIOP3 123
++static void aspeed_g6_gpio_disable_free(struct pinctrl_dev *pctldev,
++ struct pinctrl_gpio_range *range,
++ unsigned int offset)
++{
++ /*
++ * If we're freeing GPIOP1 (121) or GPIOP3 (123) then re-enable the
++ * pass-through mux setting; otherwise, do nothing.
++ */
++ if (offset != GPIOP1 && offset != GPIOP3)
++ return;
++
++ aspeed_gpio_disable_free(pctldev, range, offset);
++}
++
+ static const struct aspeed_pin_config_map aspeed_g6_pin_config_map[] = {
+ { PIN_CONFIG_BIAS_PULL_DOWN, 0, 1, BIT_MASK(0)},
+ { PIN_CONFIG_BIAS_PULL_DOWN, -1, 0, BIT_MASK(0)},
+@@ -2695,6 +2711,7 @@ static const struct pinmux_ops aspeed_g6_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_g6_gpio_disable_free,
+ .strict = true,
+ };
+
+diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.c b/drivers/pinctrl/aspeed/pinctrl-aspeed.c
+index 53f3f8aec695..ca53f3743d16 100644
+--- a/drivers/pinctrl/aspeed/pinctrl-aspeed.c
++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.c
+@@ -375,6 +375,59 @@ int aspeed_gpio_request_enable(struct pinctrl_dev *pctldev,
+ return 0;
+ }
+
++void aspeed_gpio_disable_free(struct pinctrl_dev *pctldev,
++ struct pinctrl_gpio_range *range,
++ unsigned int offset)
++{
++ 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 (!pdesc)
++ return;
++
++ dev_dbg(pctldev->dev,
++ "Freeing pass-through pin %s (%d). Re-enabling pass-through.\n",
++ pdesc->name, offset);
++
++ 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 4dcde3bc29c8..bd497c20a15f 100644
+--- a/drivers/pinctrl/aspeed/pinctrl-aspeed.h
++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.h
+@@ -101,6 +101,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..1ee1c68de
--- /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 b34fdedc21c98db698150d5014e12927a017b07f 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-ast2500.dts | 4 ++++
+ drivers/gpio/gpio-aspeed.c | 10 ++++++++++
+ 2 files changed, 14 insertions(+)
+
+diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts b/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts
+index 72bb1b3..ea1d9cd3 100644
+--- a/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts
++++ b/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts
+@@ -134,6 +134,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*/ "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","","","",
+diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
+index 09e53c5..6165b44 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 marked as pass-through. 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 (!IS_ERR(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..ca54df9ee
--- /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-ast2500.dts | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts b/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts
+index 4815104459f1..df02bb1aaf36 100644
+--- a/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts
++++ b/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.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..b9c14c1ac
--- /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,123 @@
+From 5f984831a3f451e299ecaf42d65dee36ff0ffeee 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 +++++-----
+ drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c | 10 +++++-----
+ 2 files changed, 10 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
+index c5406e2da320..fde74c23ca7f 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);
+diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c
+index bcd8c5656265..c18ceb52c289 100644
+--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c
++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c
+@@ -762,7 +762,7 @@ SSSF_PIN_DECL(AC23, GPIOO7, PWM7, SIG_DESC_SET(SCU41C, 23));
+
+ #define AB22 120
+ SIG_EXPR_LIST_DECL_SEMG(AB22, PWM8, PWM8G1, PWM8, SIG_DESC_SET(SCU41C, 24));
+-SIG_EXPR_LIST_DECL_SESG(AB22, THRUIN0, THRU0, SIG_DESC_SET(SCU4BC, 24));
++SIG_EXPR_LIST_DECL_SESG(AB22, THRUIN0, THRU0);
+ PIN_DECL_2(AB22, GPIOP0, PWM8, THRUIN0);
+ GROUP_DECL(PWM8G1, AB22);
+ FUNC_DECL_2(PWM8, PWM8G0, PWM8G1);
+@@ -779,7 +779,7 @@ FUNC_DECL_2(PWM9, PWM9G0, PWM9G1);
+
+ #define AA23 122
+ SIG_EXPR_LIST_DECL_SEMG(AA23, PWM10, PWM10G1, PWM10, SIG_DESC_SET(SCU41C, 26));
+-SIG_EXPR_LIST_DECL_SESG(AA23, THRUIN1, THRU1, SIG_DESC_SET(SCU4BC, 26));
++SIG_EXPR_LIST_DECL_SESG(AA23, THRUIN1, THRU1);
+ PIN_DECL_2(AA23, GPIOP2, PWM10, THRUIN1);
+ GROUP_DECL(PWM10G1, AA23);
+ FUNC_DECL_2(PWM10, PWM10G0, PWM10G1);
+@@ -1070,16 +1070,16 @@ FUNC_GROUP_DECL(GPIU7, AC17);
+ FUNC_GROUP_DECL(ADC15, AC17);
+
+ #define AB15 168
+-SSSF_PIN_DECL(AB15, GPIOV0, SIOS3, SIG_DESC_SET(SCU434, 8));
++SSSF_PIN_DECL(AB15, GPIOV0, SIOS3);
+
+ #define AF14 169
+-SSSF_PIN_DECL(AF14, GPIOV1, SIOS5, SIG_DESC_SET(SCU434, 9));
++SSSF_PIN_DECL(AF14, GPIOV1, SIOS5);
+
+ #define AD14 170
+ SSSF_PIN_DECL(AD14, GPIOV2, SIOPWREQ, SIG_DESC_SET(SCU434, 10));
+
+ #define AC15 171
+-SSSF_PIN_DECL(AC15, GPIOV3, SIOONCTRL, SIG_DESC_SET(SCU434, 11));
++SSSF_PIN_DECL(AC15, GPIOV3, SIOONCTRL);
+
+ #define AE15 172
+ SSSF_PIN_DECL(AE15, GPIOV4, SIOPWRGD, SIG_DESC_SET(SCU434, 12));
+--
+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..7e38110af
--- /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 b08fba62b18ecb04d3e7dafac6bd819dd9b90d35 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 2e5fba354412..b1eb3abe759b 100644
+--- a/drivers/net/ethernet/faraday/ftgmac100.c
++++ b/drivers/net/ethernet/faraday/ftgmac100.c
+@@ -1218,10 +1218,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..993beb9ec
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0014-arm-dts-aspeed-g5-add-espi.patch
@@ -0,0 +1,31 @@
+From ea37c85db4d2a593d4f3392e21ec2cc108b111e5 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 | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index 6580b232771e..452ec323534f 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -408,6 +408,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..b9de9f5a9
--- /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..950272e1b
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0016-Add-ASPEED-SGPIO-driver.patch
@@ -0,0 +1,760 @@
+From a49e262cca260d15dc245fdd1870c89068042063 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] 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 | 704 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 713 insertions(+)
+ create mode 100644 drivers/gpio/sgpio-aspeed.c
+
+diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
+index 7138290cdd36..0235b20a95f6 100644
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -128,6 +128,14 @@ config GPIO_ASPEED_SGPIO
+ help
+ Say Y here to support Aspeed AST2500 SGPIO functionality.
+
++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 e4599f90f702..0e80c8cd5ae7 100644
+--- a/drivers/gpio/Makefile
++++ b/drivers/gpio/Makefile
+@@ -33,6 +33,7 @@ obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o
+ obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o
+ obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o
+ obj-$(CONFIG_GPIO_ASPEED_SGPIO) += gpio-aspeed-sgpio.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..5028e9144a75
+--- /dev/null
++++ b/drivers/gpio/sgpio-aspeed.c
+@@ -0,0 +1,704 @@
++// 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 },
++ { .compatible = "aspeed,ast2600-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..50c31f625
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0017-SGPIO-DT-and-pinctrl-fixup.patch
@@ -0,0 +1,227 @@
+From 89b5ebd26fc44b4bf820aa507bc88eec028ba218 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 | 3 +-
+ arch/arm/boot/dts/aspeed-g6.dtsi | 15 ++++++++
+ drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c | 48 ++++++++++++-------------
+ 4 files changed, 60 insertions(+), 62 deletions(-)
+
+diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
+index b875c0785833..e9fd66ab3099 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -254,6 +254,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";
+@@ -1227,44 +1241,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 452ec323534f..20b2eb8052b7 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -326,8 +326,7 @@
+ reg = <0x1e780200 0x0100>;
+ clocks = <&syscon ASPEED_CLK_APB>;
+ interrupt-controller;
+- ngpios = <8>;
+- bus-frequency = <12000000>;
++ bus-frequency = <1000000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_sgpm_default>;
+ status = "disabled";
+diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
+index 54add29d8217..459070693aba 100644
+--- a/arch/arm/boot/dts/aspeed-g6.dtsi
++++ b/arch/arm/boot/dts/aspeed-g6.dtsi
+@@ -339,6 +339,21 @@
+ #interrupt-cells = <2>;
+ };
+
++ sgpio: sgpio@1e780500 {
++ #gpio-cells = <2>;
++ gpio-controller;
++ compatible = "aspeed,ast2600-sgpio";
++ reg = <0x1e780500 0x0100>;
++ #interrupt-cells = <2>;
++ interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-controller;
++ bus-frequency = <1000000>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_sgpm1_default>;
++ clocks = <&syscon ASPEED_CLK_APB1>;
++ status = "disabled";
++ };
++
+ rtc: rtc@1e781000 {
+ compatible = "aspeed,ast2600-rtc";
+ reg = <0x1e781000 0x18>;
+diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c
+index bfed0e274643..10dadfd0e6e1 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/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..128b11ecf
--- /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] Add I2C IPMB support
+
+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..bd468e29d
--- /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 5d411ed0d66d3d00232519ed7d4ab6fac45e7c6e 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 e9fd66ab3099..f3edda4ae477 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -393,6 +393,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 20b2eb8052b7..bd6d1461e4bd 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -502,6 +502,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..d66e84beb
--- /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 c111aac36e2f4fa1149662c85883407315ba76a6 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 f3edda4ae477..a70bee24d058 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -394,6 +394,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 bd6d1461e4bd..c15be82c3a9d 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -503,6 +503,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..490104cda
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch
@@ -0,0 +1,700 @@
+From 1d0c60f1aa8b7b25d8d0d6f6f6443d307d543600 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] Add AST2500 eSPI driver
+
+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>
+Signed-off-by: Vernon Mauery <vernon.mauery@intel.com>
+---
+ .../devicetree/bindings/misc/aspeed,espi-slave.txt | 20 +
+ Documentation/misc-devices/espi-slave.rst | 118 ++++++
+ arch/arm/boot/dts/aspeed-g5.dtsi | 4 +
+ arch/arm/boot/dts/aspeed-g6.dtsi | 12 +
+ drivers/misc/Kconfig | 8 +
+ drivers/misc/Makefile | 1 +
+ drivers/misc/aspeed-espi-slave.c | 421 +++++++++++++++++++++
+ 7 files changed, 584 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..f72d9ae32f3e
+--- /dev/null
++++ b/Documentation/devicetree/bindings/misc/aspeed,espi-slave.txt
+@@ -0,0 +1,20 @@
++ASPEED eSPI Slave Controller
++
++Required properties:
++ - compatible: must be one of:
++ - "aspeed,ast2500-espi-slave"
++ - "aspeed,ast2600-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 c15be82c3a9d..bd2037e52a94 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -316,6 +316,7 @@
+ clocks = <&syscon ASPEED_CLK_APB>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
++ status = "disabled";
+ };
+
+ sgpio: sgpio@1e780200 {
+@@ -412,6 +413,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/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
+index 459070693aba..e4c1ab3d274e 100644
+--- a/arch/arm/boot/dts/aspeed-g6.dtsi
++++ b/arch/arm/boot/dts/aspeed-g6.dtsi
+@@ -3,6 +3,7 @@
+
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/ast2600-clock.h>
++#include <dt-bindings/gpio/aspeed-gpio.h>
+
+ / {
+ model = "Aspeed BMC";
+@@ -651,6 +652,17 @@
+ status = "disabled";
+ };
+
++ espi: espi@1e6ee000 {
++ compatible = "aspeed,ast2600-espi-slave";
++ reg = <0x1e6ee000 0x200>;
++ interrupts-extended = <&gic GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>,
++ <&gpio0 ASPEED_GPIO(W, 7) IRQ_TYPE_EDGE_FALLING>;
++ status = "disabled";
++ clocks = <&syscon ASPEED_CLK_GATE_ESPICLK>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_espi_default>;
++ };
++
+ i2c: bus@1e78a000 {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
+index 06a2b753cc7c..0f9b5a356c93 100644
+--- a/drivers/misc/Kconfig
++++ b/drivers/misc/Kconfig
+@@ -433,6 +433,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 b9e6d4c3e906..53864687e8fd 100644
+--- a/drivers/misc/Makefile
++++ b/drivers/misc/Makefile
+@@ -51,6 +51,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..d70332d1fef3
+--- /dev/null
++++ b/drivers/misc/aspeed-espi-slave.c
+@@ -0,0 +1,421 @@
++// 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" },
++ { .compatible = "aspeed,ast2600-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/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..45dec5b20
--- /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] Add AST2500 JTAG driver
+
+Update AST2500 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..c9844e1bd
--- /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 a57f401058e452f9abb6f3233e7f2776af1e6b8d 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 a7be6f24450b..c113ffa8d5df 100644
+--- a/drivers/i2c/busses/i2c-aspeed.c
++++ b/drivers/i2c/busses/i2c-aspeed.c
+@@ -169,6 +169,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)
+@@ -657,6 +672,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;
+@@ -708,6 +724,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;
+ }
+
+@@ -1079,6 +1100,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..5240a05f5
--- /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 0bc792097e73c07bc324e2c9b0172fb27b51a087 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 411ff5fb2c07..5e1c87bc8a99 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 9ff4f6e4558c..41d531dd0b48 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_MAC1RCLK 36
+ #define ASPEED_CLK_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..2ba9e5002
--- /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,559 @@
+From 09aa5d5af94823f100fb515d45b0a04fc8d7ee4f 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>
+Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
+---
+ .../ABI/stable/sysfs-driver-aspeed-uart-routing | 14 +
+ Documentation/misc-devices/aspeed-uart-routing.txt | 49 +++
+ arch/arm/boot/dts/aspeed-g5.dtsi | 6 +
+ arch/arm/boot/dts/aspeed-g6.dtsi | 6 +
+ drivers/misc/Kconfig | 6 +
+ drivers/misc/Makefile | 1 +
+ drivers/misc/aspeed-uart-routing.c | 383 +++++++++++++++++++++
+ 7 files changed, 465 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..cf1c2a466875
+--- /dev/null
++++ b/Documentation/misc-devices/aspeed-uart-routing.txt
+@@ -0,0 +1,49 @@
++Kernel driver aspeed-uart-routing
++=================================
++
++Supported chips:
++ASPEED AST2500/AST2600
++
++Author:
++Google LLC
++
++Description
++-----------
++
++The Aspeed AST2500/AST2600 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-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index bd2037e52a94..797013debaa7 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -523,6 +523,12 @@
+ status = "disabled";
+ };
+ };
++
++ uart_routing: uart_routing@9c {
++ compatible = "aspeed,ast2500-uart-routing";
++ reg = <0x9c 0x4>;
++ status = "disabled";
++ };
+ };
+
+ peci: bus@1e78b000 {
+diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
+index e4c1ab3d274e..8cc978058f16 100644
+--- a/arch/arm/boot/dts/aspeed-g6.dtsi
++++ b/arch/arm/boot/dts/aspeed-g6.dtsi
+@@ -291,6 +291,12 @@
+ compatible = "aspeed,ast2600-pinctrl";
+ };
+
++ uart_routing: uart_routing@9c {
++ compatible = "aspeed,ast2500-uart-routing";
++ reg = <0x9c 0x4>;
++ status = "disabled";
++ };
++
+ smp-memram@180 {
+ compatible = "aspeed,ast2600-smpmem";
+ reg = <0x180 0x40>;
+diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
+index 0f9b5a356c93..8af817694648 100644
+--- a/drivers/misc/Kconfig
++++ b/drivers/misc/Kconfig
+@@ -441,6 +441,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 53864687e8fd..a4372208f4de 100644
+--- a/drivers/misc/Makefile
++++ b/drivers/misc/Makefile
+@@ -52,6 +52,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..2f0fd3a31
--- /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 65c8090f2b418892aee9f239729fa417bd508a00 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 a70bee24d058..054d97229626 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -120,14 +120,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>;
+@@ -136,6 +128,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 797013debaa7..27ed188296a1 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -148,14 +148,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>;
+@@ -164,6 +156,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..0fdd40d77
--- /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 b20b1ce81f4451d9e906b84e7edcc22d4e70e7df 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 27ed188296a1..45202bc3d60c 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -341,7 +341,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 e3a2518503ed..21def8cd5af6 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 26326adf71d7..3a9e9840b5ae 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..283ded6906f1
+--- /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 = priv->variant.chan_min; i < priv->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..198beaa93
--- /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 ee9cee93b96b791f52295d7763985fdb10903e2b 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 5f6a4985f2bc..d1cd7afe4068 100644
+--- a/drivers/i2c/i2c-core-base.c
++++ b/drivers/i2c/i2c-core-base.c
+@@ -1297,6 +1297,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;
+@@ -1379,6 +1398,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:
+@@ -1599,6 +1621,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));
+@@ -1948,7 +1972,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))
+@@ -1961,6 +1987,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
+@@ -1997,6 +2042,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);
+@@ -2015,6 +2063,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) {
+@@ -2038,12 +2087,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 1361637c369d..b4055d133338 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..5b9d4ef8b
--- /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 37e7896bf297edef4f2877998ca7a5c086015591 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 c113ffa8d5df..7becfcd67142 100644
+--- a/drivers/i2c/busses/i2c-aspeed.c
++++ b/drivers/i2c/busses/i2c-aspeed.c
+@@ -1032,7 +1032,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 d1cd7afe4068..1db991220fae 100644
+--- a/drivers/i2c/i2c-core-base.c
++++ b/drivers/i2c/i2c-core-base.c
+@@ -1318,6 +1318,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 */
+@@ -1345,8 +1346,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..6cc7d1138
--- /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,154 @@
+From 301c1aaba9e59eb593406d878451e3ea4fe355bb 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>
+Signed-off-by: Vernon Mauery <vernon.mauery@linux.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 +
+ arch/arm/boot/dts/aspeed-g6.dtsi | 1 +
+ drivers/char/ipmi/bt-bmc.c | 24 +++++++++++++++++++++-
+ 5 files changed, 29 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 054d97229626..db962ab435af 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -386,6 +386,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 45202bc3d60c..c4724ec27041 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -499,6 +499,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/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
+index 8cc978058f16..a557a7e6fe8d 100644
+--- a/arch/arm/boot/dts/aspeed-g6.dtsi
++++ b/arch/arm/boot/dts/aspeed-g6.dtsi
+@@ -524,6 +524,7 @@
+ ibt: ibt@c0 {
+ compatible = "aspeed,ast2600-ibt-bmc";
+ reg = <0xc0 0x18>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ interrupts = <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c
+index 0e600449931b..87bf3ff28542 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..b3ace73f6
--- /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,139 @@
+From 404f73827714e324a21250b1a855fdc10fcfaf51 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>
+Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
+---
+ arch/arm/boot/dts/aspeed-g4.dtsi | 1 +
+ arch/arm/boot/dts/aspeed-g5.dtsi | 1 +
+ arch/arm/boot/dts/aspeed-g6.dtsi | 1 +
+ drivers/soc/aspeed/aspeed-lpc-snoop.c | 30 +++++++++++++++++++++++++++---
+ 4 files changed, 30 insertions(+), 3 deletions(-)
+
+diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
+index db962ab435af..24a20384b5e8 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -369,6 +369,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 c4724ec27041..18d2a465c0ed 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -482,6 +482,7 @@
+ compatible = "aspeed,ast2500-lpc-snoop";
+ reg = <0x0 0x80>;
+ interrupts = <8>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ status = "disabled";
+ };
+
+diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
+index a557a7e6fe8d..4035d7bd647e 100644
+--- a/arch/arm/boot/dts/aspeed-g6.dtsi
++++ b/arch/arm/boot/dts/aspeed-g6.dtsi
+@@ -507,6 +507,7 @@
+ compatible = "aspeed,ast2600-lpc-snoop";
+ reg = <0x0 0x80>;
+ interrupts = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>;
++ 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 c7b4ac066b40..ed272677fc82 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..1c0903d71
--- /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,273 @@
+From cd62ca008a771bd6b7aeb06526c37d8435f86648 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>
+Signed-off-by: Vernon Mauery <vernon.mauery@linux.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 +++-
+ arch/arm/boot/dts/aspeed-g6.dtsi | 6 ++++
+ drivers/char/ipmi/kcs_bmc_aspeed.c | 37 ++++++++++++++++++----
+ 5 files changed, 79 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 24a20384b5e8..fc6f4e009db7 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -347,6 +347,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 {
+@@ -358,6 +385,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 18d2a465c0ed..751a8f0316d6 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -134,7 +134,7 @@
+ };
+
+ vic: interrupt-controller@1e6c0080 {
+- compatible = "aspeed,ast2400-vic";
++ compatible = "aspeed,ast2500-vic";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ valid-sources = <0xfefff7ff 0x0807ffff>;
+@@ -439,18 +439,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";
+ };
+ };
+@@ -468,6 +471,7 @@
+ compatible = "aspeed,ast2500-kcs-bmc";
+ interrupts = <8>;
+ kcs_chan = <4>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ status = "disabled";
+ };
+
+diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
+index 4035d7bd647e..0e35c4598df5 100644
+--- a/arch/arm/boot/dts/aspeed-g6.dtsi
++++ b/arch/arm/boot/dts/aspeed-g6.dtsi
+@@ -463,18 +463,23 @@
+ kcs1: kcs1@0 {
+ compatible = "aspeed,ast2600-kcs-bmc";
+ interrupts = <GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ kcs_chan = <1>;
+ status = "disabled";
+ };
++
+ kcs2: kcs2@0 {
+ compatible = "aspeed,ast2600-kcs-bmc";
+ interrupts = <GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ kcs_chan = <2>;
+ status = "disabled";
+ };
++
+ kcs3: kcs3@0 {
+ compatible = "aspeed,ast2600-kcs-bmc";
+ interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ kcs_chan = <3>;
+ status = "disabled";
+ };
+@@ -492,6 +497,7 @@
+ kcs4: kcs4@0 {
+ compatible = "aspeed,ast2600-kcs-bmc";
+ interrupts = <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ kcs_chan = <4>;
+ status = "disabled";
+ };
+diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
+index a0a8bb89c9b3..94cfb879f520 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..8e9c793ba
--- /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 5473931df3348b7284c16fac3e7d336c9d0c4294 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 f4ac14c40518..432128b8db87 100644
+--- a/drivers/soc/aspeed/aspeed-lpc-ctrl.c
++++ b/drivers/soc/aspeed/aspeed-lpc-ctrl.c
+@@ -265,8 +265,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) {
+@@ -288,6 +290,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..c3c1eb509
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0051-Add-AST2500-JTAG-device.patch
@@ -0,0 +1,35 @@
+From fe860284fb5e062b430dc7b882144f69cb605353 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 AST2500 JTAG device
+
+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 751a8f0316d6..eb1f9c9d9cca 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -418,6 +418,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..484c576b9
--- /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 ea6fa8fd6e36c776d560b4f69d1ede4a3bbe5f6b 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 4322efa37732..e5e4fe21b5d9 100644
+--- a/drivers/Kconfig
++++ b/drivers/Kconfig
+@@ -230,4 +230,5 @@ source "drivers/counter/Kconfig"
+
+ source "drivers/peci/Kconfig"
+
++source "drivers/jtag/Kconfig"
+ endmenu
+diff --git a/drivers/Makefile b/drivers/Makefile
+index 82f78cfedf69..297047d4ed9b 100644
+--- a/drivers/Makefile
++++ b/drivers/Makefile
+@@ -187,3 +187,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..1ffaf7646
--- /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,1293 @@
+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] Add Aspeed SoC 24xx and 25xx families JTAG
+
+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..12073da02
--- /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,107 @@
+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] Documentation: jtag: Add bindings for Aspeed SoC
+
+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..99cc318ab
--- /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 8618c2cd920d10681859bacf8af56c71f168b5a2 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 de23fc0f0fce..339d198c5adf 100644
+--- a/Documentation/ioctl/ioctl-number.rst
++++ b/Documentation/ioctl/ioctl-number.rst
+@@ -333,6 +333,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..9ff92d8ec
--- /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 fcf8f7fd3cb4e0b08cb1540f9ff9e5988cd285d1 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] 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 3bbdae50eb7e..34ab3522801c 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -8819,6 +8819,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/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..24032087b
--- /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,1035 @@
+From fcee7b9515140486ad8c58beedf88cf12cd09b8b 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 | 32 +--
+ drivers/i2c/busses/i2c-aspeed.c | 294 ++++++++++++++++++---
+ 5 files changed, 365 insertions(+), 95 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 fc6f4e009db7..0b30c8546e64 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -518,12 +518,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 {
+@@ -531,7 +540,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>;
+@@ -547,7 +556,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>;
+@@ -563,7 +572,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>;
+@@ -580,7 +589,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>;
+@@ -597,7 +606,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>;
+@@ -614,7 +623,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>;
+@@ -631,7 +640,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>;
+@@ -648,7 +657,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>;
+@@ -665,7 +674,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>;
+@@ -682,7 +691,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>;
+@@ -699,7 +708,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>;
+@@ -716,7 +725,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>;
+@@ -733,7 +742,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>;
+@@ -750,7 +759,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 eb1f9c9d9cca..51593a0a8a23 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -615,12 +615,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 {
+@@ -628,7 +637,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>;
+@@ -644,7 +653,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>;
+@@ -660,7 +669,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>;
+@@ -677,7 +686,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>;
+@@ -694,7 +703,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>;
+@@ -711,7 +720,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>;
+@@ -728,7 +737,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>;
+@@ -745,7 +754,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>;
+@@ -762,7 +771,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>;
+@@ -779,7 +788,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>;
+@@ -796,7 +805,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>;
+@@ -813,7 +822,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>;
+@@ -830,7 +839,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>;
+@@ -847,7 +856,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 0e35c4598df5..eeace4b7b725 100644
+--- a/arch/arm/boot/dts/aspeed-g6.dtsi
++++ b/arch/arm/boot/dts/aspeed-g6.dtsi
+@@ -713,7 +713,7 @@
+ #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_APB2>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -728,7 +728,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_APB2>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -743,7 +743,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_APB2>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -758,7 +758,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_APB2>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -773,7 +773,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_APB2>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -788,7 +788,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_APB2>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -803,7 +803,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_APB2>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -818,7 +818,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_APB2>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -833,7 +833,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_APB2>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -848,7 +848,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_APB2>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -863,7 +863,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_APB2>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -878,7 +878,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_APB2>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -893,7 +893,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_APB2>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -908,7 +908,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_APB2>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -923,7 +923,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_APB2>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -938,7 +938,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_APB2>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
+index 7becfcd67142..1b338492c68a 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)
+@@ -118,6 +128,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,
+@@ -163,6 +180,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;
+@@ -259,6 +281,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)
+@@ -281,7 +304,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)
+@@ -296,6 +324,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;
+ }
+@@ -328,9 +370,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);
+@@ -356,6 +425,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)
+ /*
+@@ -374,12 +445,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);
+ }
+
+@@ -419,7 +544,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;
+@@ -522,11 +647,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);
+ }
+@@ -543,25 +700,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);
+@@ -924,6 +1112,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
+@@ -985,16 +1176,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);
+
+@@ -1028,6 +1218,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);
+@@ -1063,8 +1289,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..a66e759e7
--- /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 0d237f4b5111aa192a1ae9aaee6e6779761906bc 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 1b338492c68a..8dc6723bfaaf 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)
+@@ -135,6 +141,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,
+@@ -185,6 +199,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;
+@@ -304,9 +324,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;
+@@ -326,7 +350,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));
+@@ -370,7 +405,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,
+@@ -382,7 +425,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));
+@@ -446,7 +507,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) {
+@@ -467,7 +544,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;
+@@ -503,7 +599,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);
+ }
+@@ -649,7 +746,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;
+
+@@ -700,7 +818,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));
+@@ -728,7 +854,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 >
+@@ -1235,7 +1379,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);
+
+@@ -1275,24 +1463,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)
+@@ -1310,6 +1507,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..2f51c3fc9
--- /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 3f73215941667176ba05f358f4ee08816299bd32 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 8dc6723bfaaf..891b2b5c4b7a 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)
+@@ -167,6 +169,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,
+ };
+
+@@ -208,6 +212,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 */
+ };
+
+@@ -315,6 +321,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;
+@@ -342,6 +354,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;
+ }
+@@ -462,11 +489,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:
+@@ -1084,6 +1116,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);
+ }
+
+@@ -1122,6 +1156,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;
+@@ -1269,6 +1305,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 b4055d133338..52369ea150b4 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..a5f4fa216
--- /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 3e359be7e8a96b068479d01aa671ff642d291bca 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 891b2b5c4b7a..71ebf3bbf38b 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 | \
+@@ -186,6 +195,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;
+@@ -303,6 +313,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;
+@@ -313,6 +331,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. */
+@@ -655,7 +681,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;
+@@ -686,9 +712,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) {
+@@ -1264,6 +1290,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);
+@@ -1272,8 +1299,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;
+ }
+@@ -1288,6 +1353,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..a304de019
--- /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 78ad5c6291df33b7cba0e6be9a2b6ed6e7bbd571 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 71ebf3bbf38b..a15f54f64b50 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)
+@@ -360,6 +361,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..5c3e35016
--- /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 0bda96c5fe3064c71f190bc484e2ce59c51d5585 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 39c50c53dae3..05945470bd89 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 5028e9144a75..850539da43f5 100644
+--- a/drivers/gpio/sgpio-aspeed.c
++++ b/drivers/gpio/sgpio-aspeed.c
+@@ -681,7 +681,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..1056b3beb
--- /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 statistics 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..009fccacf
--- /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 83c1fcb7dacb59d22b41356e3b7009ff2387d448 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 a15f54f64b50..62b803e15ce2 100644
+--- a/drivers/i2c/busses/i2c-aspeed.c
++++ b/drivers/i2c/busses/i2c-aspeed.c
+@@ -1454,6 +1454,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);
+@@ -1576,17 +1581,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);
+@@ -1599,6 +1593,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-arm-ast2600-add-pwm_tacho-driver-from-aspeed.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0076-arm-ast2600-add-pwm_tacho-driver-from-aspeed.patch
new file mode 100644
index 000000000..9515a1a89
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0076-arm-ast2600-add-pwm_tacho-driver-from-aspeed.patch
@@ -0,0 +1,1107 @@
+From a17a084c4889dcc7cb43ef5f93032997cfcee4f0 Mon Sep 17 00:00:00 2001
+From: Vernon Mauery <vernon.mauery@intel.com>
+Date: Fri, 27 Sep 2019 13:09:48 -0700
+Subject: [PATCH] arm: ast2600: add pwm_tacho driver from aspeed
+
+Add the pwm_tacho driver from Aspeed to get pwm working until an
+upstream PWM/Tacho driver is available. This was copied from the v5.02
+BSP from Aspeed.
+
+Signed-off-by: Vernon Mauery <vernon.mauery@intel.com>
+---
+ arch/arm/boot/dts/aspeed-g6.dtsi | 10 +
+ drivers/hwmon/Kconfig | 11 +
+ drivers/hwmon/Makefile | 1 +
+ drivers/hwmon/aspeed-g6-pwm-tacho.c | 1025 +++++++++++++++++++++++++++++++++++
+ 4 files changed, 1047 insertions(+)
+ create mode 100644 drivers/hwmon/aspeed-g6-pwm-tacho.c
+
+diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
+index eeace4b7b725..33fcd89db6b8 100644
+--- a/arch/arm/boot/dts/aspeed-g6.dtsi
++++ b/arch/arm/boot/dts/aspeed-g6.dtsi
+@@ -278,6 +278,16 @@
+ #size-cells = <1>;
+ ranges;
+
++ pwm_tacho: pwm-tacho-controller@1e610000 {
++ compatible = "aspeed,ast2600-pwm-tacho";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ reg = <0x1e610000 0x100>;
++ clocks = <&syscon ASPEED_CLK_AHB>;
++ resets = <&syscon ASPEED_RESET_PWM>;
++ status = "disabled";
++ };
++
+ syscon: syscon@1e6e2000 {
+ compatible = "aspeed,ast2600-scu", "syscon", "simple-mfd";
+ reg = <0x1e6e2000 0x1000>;
+diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
+index e244a7901392..8312b3798b82 100644
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -371,6 +371,17 @@ config SENSORS_ASPEED
+ This driver can also be built as a module. If so, the module
+ will be called aspeed_pwm_tacho.
+
++config SENSORS_ASPEED_G6
++ tristate "ASPEED AST2600 PWM and Fan tach driver"
++ depends on THERMAL || THERMAL=n
++ select REGMAP
++ help
++ This driver provides support for ASPEED AST2600 PWM
++ and Fan Tacho controllers.
++
++ This driver can also be built as a module. If so, the module
++ will be called aspeed_g6_pwm_tacho.
++
+ config SENSORS_ATXP1
+ tristate "Attansic ATXP1 VID controller"
+ depends on I2C
+diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
+index 1c7ab361adc7..e74ea925fb56 100644
+--- a/drivers/hwmon/Makefile
++++ b/drivers/hwmon/Makefile
+@@ -50,6 +50,7 @@ obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o
+ obj-$(CONFIG_SENSORS_AS370) += as370-hwmon.o
+ obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o
+ obj-$(CONFIG_SENSORS_ASPEED) += aspeed-pwm-tacho.o
++obj-$(CONFIG_SENSORS_ASPEED_G6) += aspeed-g6-pwm-tacho.o
+ obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
+ obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
+ obj-$(CONFIG_SENSORS_DA9052_ADC)+= da9052-hwmon.o
+diff --git a/drivers/hwmon/aspeed-g6-pwm-tacho.c b/drivers/hwmon/aspeed-g6-pwm-tacho.c
+new file mode 100644
+index 000000000000..1894f6ad5edb
+--- /dev/null
++++ b/drivers/hwmon/aspeed-g6-pwm-tacho.c
+@@ -0,0 +1,1025 @@
++/*
++ * Copyright (C) ASPEED Technology Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 or later as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/clk.h>
++#include <linux/errno.h>
++#include <linux/gpio/consumer.h>
++#include <linux/delay.h>
++#include <linux/hwmon.h>
++#include <linux/hwmon-sysfs.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of_platform.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/sysfs.h>
++#include <linux/reset.h>
++#include <linux/regmap.h>
++#include <linux/thermal.h>
++
++#define ASPEED_PWM_CTRL 0x00 //PWM0 General Register
++#define ASPEED_PWM_CTRL_CH(x) ((x * 0x10) + 0x00)
++#define PWM_LOAD_AS_WDT BIT(19) //load selection as WDT
++#define PWM_DUTY_LOAD_AS_WDT_EN BIT(18) //enable PWM duty load as WDT
++#define PWM_DUTY_SYNC_DIS BIT(17) //disable PWM duty sync
++#define PWM_CLK_ENABLE BIT(16) //enable PWM clock
++#define PWM_LEVEL_OUTPUT BIT(15) //output PWM level
++#define PWM_INVERSE BIT(14) //inverse PWM pin
++#define PWM_OPEN_DRAIN_EN BIT(13) //enable open-drain
++#define PWM_PIN_EN BIT(12) //enable PWM pin
++#define PWM_CLK_DIV_H_MASK (0xf << 8) //PWM clock division H bit [3:0]
++#define PWM_CLK_DIV_L_MASK (0xff) //PWM clock division H bit [3:0]
++
++/*
++\xregmid {11:8 }{RW}{PWM clock division H bit [3:0]}{
++ 0: divide 1 \n
++ 1: divide 2 \n
++ 2: divide 4 \n
++ 3: divide 8 \n
++ ... \n
++ F: divide 32768}
++\xregmid {7 :0 }{RW}{PWM clock division L bit [7:0]}{
++ 00: divide 1 \n
++ 01: divide 2 \n
++ 02: divide 3 \n
++ 03: divide 4 \n
++ ... \n
++ FF: divide 256}
++*/
++
++#define ASPEED_PWM_DUTY_CYCLE 0x04 //PWM0 Duty Cycle Register
++#define ASPEED_PWM_DUTY_CYCLE_CH(x) ((x * 0x10) + 0x04)
++#define PWM_LOOP_BIT_MASK (0xf << 24) //loop bit [7:0]
++#define PWM_PERIOD_BIT (24) //pwm period bit [7:0]
++#define PWM_PERIOD_BIT_MASK (0xff << 24) //pwm period bit [7:0]
++#define PWM_RISING_FALLING_AS_WDT_BIT (16)
++#define PWM_RISING_FALLING_AS_WDT_MASK (0xff << 16) //pwm rising/falling point bit [7:0] as WDT
++#define PWM_RISING_FALLING_MASK (0xffff)
++#define PWM_RISING_FALLING_BIT (8) //pwm falling point bit [7:0]
++#define PWM_RISING_RISING_BIT (0) //pwm rising point bit [7:0]
++
++#define ASPEED_TACHO_CTRL 0x08 //TACH0 General Register
++#define ASPEED_TACHO_CTRL_CH(x) ((x * 0x10) + 0x08)
++#define TACHO_IER BIT(31) //enable tacho interrupt
++#define TACHO_INVERS_LIMIT BIT(30) //inverse tacho limit comparison
++#define TACHO_LOOPBACK BIT(29) //tacho loopback
++#define TACHO_ENABLE BIT(28) //{enable tacho}
++#define TACHO_DEBOUNCE_BIT (26) //{tacho de-bounce}
++#define TACHO_DEBOUNCE_MASK (0x3 << 26) //{tacho de-bounce}
++#define TECHIO_EDGE_MASK (0x3 << 24) //tacho edge}
++#define TECHIO_EDGE_BIT (24) //tacho edge}
++#define TACHO_CLK_DIV_T_MASK (0xf << 20)
++#define TACHO_CLK_DIV_BIT (20)
++#define TACHO_THRESHOLD_MASK (0xfffff) //tacho threshold bit
++/*
++\xregmid {23:20}{RW}{tacho clock division T bit [3:0]}{
++ 0: divide 1 \n
++ 1: divide 4 \n
++ 2: divide 16 \n
++ 3: divide 64 \n
++ ... \n
++ B: divide 4194304 \n
++ others: reserved}
++\xregmidb{19 :0 }{RW}{tacho threshold bit [19:0]}
++*/
++
++#define ASPEED_TACHO_STS 0x0C //TACH0 Status Register
++#define ASPEED_TACHO_STS_CH(x) ((x * 0x10) + 0x0C)
++#define TACHO_ISR BIT(31) //interrupt status and clear
++#define PWM_OUT BIT(25) //{pwm_out}
++#define PWM_OEN BIT(24) //{pwm_oeN}
++#define TACHO_DEB_INPUT BIT(23) //tacho deB input
++#define TACHO_RAW_INPUT BIT(22) //tacho raw input}
++#define TACHO_VALUE_UPDATE BIT(21) //tacho value updated since the last read
++#define TACHO_FULL_MEASUREMENT BIT(20) //{tacho full measurement}
++#define TACHO_VALUE_MASK 0xfffff //tacho value bit [19:0]}
++
++#define MAX_CDEV_NAME_LEN 16
++
++struct aspeed_pwm_channel_params {
++ int load_wdt_rising_falling_pt;
++ int load_wdt_selection; //0: rising , 1: falling
++ int load_wdt_enable;
++ int duty_sync_enable;
++ int invert_pin;
++ u8 divide_h;
++ u8 divide_l;
++ u8 period;
++ u8 rising;
++ u8 falling;
++};
++
++static struct aspeed_pwm_channel_params default_pwm_params[] = {
++ [0] = {
++ .load_wdt_rising_falling_pt = 0x10,
++ .load_wdt_selection = 0,
++ .load_wdt_enable = 1,
++ .duty_sync_enable = 0,
++ .invert_pin = 0,
++ .divide_h = 0x5,
++ .divide_l = 0x6,
++ .period = 0x13, //5% ~~
++ .rising = 0x00,
++ .falling = 0x0a,
++ },
++ [1] = {
++ .load_wdt_rising_falling_pt = 0x10,
++ .load_wdt_selection = 0,
++ .load_wdt_enable = 0,
++ .duty_sync_enable = 0,
++ .invert_pin = 0,
++ .divide_h = 0x5,
++ .divide_l = 0x6,
++ .period = 0x13, //5% ~~
++ .rising = 0x00,
++ .falling = 0x0a,
++ },
++ [2] = {
++ .load_wdt_rising_falling_pt = 0x10,
++ .load_wdt_selection = 0,
++ .load_wdt_enable = 0,
++ .duty_sync_enable = 0,
++ .invert_pin = 0,
++ .divide_h = 0x5,
++ .divide_l = 0x6,
++ .period = 0x13, //5% ~~
++ .rising = 0x00,
++ .falling = 0x0a,
++ },
++ [3] = {
++ .load_wdt_rising_falling_pt = 0x10,
++ .load_wdt_selection = 0,
++ .load_wdt_enable = 0,
++ .duty_sync_enable = 0,
++ .invert_pin = 0,
++ .divide_h = 0x5,
++ .divide_l = 0x6,
++ .period = 0x13, //5% ~~
++ .rising = 0x00,
++ .falling = 0x0a,
++ },
++ [4] = {
++ .load_wdt_rising_falling_pt = 0x10,
++ .load_wdt_selection = 0,
++ .load_wdt_enable = 0,
++ .duty_sync_enable = 0,
++ .invert_pin = 0,
++ .divide_h = 0x5,
++ .divide_l = 0x6,
++ .period = 0x13, //5% ~~
++ .rising = 0x00,
++ .falling = 0x0a,
++ },
++ [5] = {
++ .load_wdt_rising_falling_pt = 0x10,
++ .load_wdt_selection = 0,
++ .load_wdt_enable = 0,
++ .duty_sync_enable = 0,
++ .invert_pin = 0,
++ .divide_h = 0x5,
++ .divide_l = 0x6,
++ .period = 0x13, //5% ~~
++ .rising = 0x00,
++ .falling = 0x0a,
++ },
++ [6] = {
++ .load_wdt_rising_falling_pt = 0x10,
++ .load_wdt_selection = 0,
++ .load_wdt_enable = 0,
++ .duty_sync_enable = 0,
++ .invert_pin = 0,
++ .divide_h = 0x5,
++ .divide_l = 0x6,
++ .period = 0x13, //5% ~~
++ .rising = 0x00,
++ .falling = 0x0a,
++ },
++ [7] = {
++ .load_wdt_rising_falling_pt = 0x10,
++ .load_wdt_selection = 0,
++ .load_wdt_enable = 0,
++ .duty_sync_enable = 0,
++ .invert_pin = 0,
++ .divide_h = 0x5,
++ .divide_l = 0x6,
++ .period = 0x13, //5% ~~
++ .rising = 0x00,
++ .falling = 0x0a,
++ },
++ [8] = {
++ .load_wdt_rising_falling_pt = 0x10,
++ .load_wdt_selection = 0,
++ .load_wdt_enable = 0,
++ .duty_sync_enable = 0,
++ .invert_pin = 0,
++ .divide_h = 0x5,
++ .divide_l = 0x6,
++ .period = 0x13, //5% ~~
++ .rising = 0x00,
++ .falling = 0x0a,
++ },
++ [9] = {
++ .load_wdt_rising_falling_pt = 0x10,
++ .load_wdt_selection = 0,
++ .load_wdt_enable = 0,
++ .duty_sync_enable = 0,
++ .invert_pin = 0,
++ .divide_h = 0x5,
++ .divide_l = 0x6,
++ .period = 0x13, //5% ~~
++ .rising = 0x00,
++ .falling = 0x0a,
++ },
++ [10] = {
++ .load_wdt_rising_falling_pt = 0x10,
++ .load_wdt_selection = 0,
++ .load_wdt_enable = 0,
++ .duty_sync_enable = 0,
++ .invert_pin = 0,
++ .divide_h = 0x5,
++ .divide_l = 0x6,
++ .period = 0x13, //5% ~~
++ .rising = 0x00,
++ .falling = 0x0a,
++ },
++ [11] = {
++ .load_wdt_rising_falling_pt = 0x10,
++ .load_wdt_selection = 0,
++ .load_wdt_enable = 0,
++ .duty_sync_enable = 0,
++ .invert_pin = 0,
++ .divide_h = 0x5,
++ .divide_l = 0x6,
++ .period = 0x13, //5% ~~
++ .rising = 0x00,
++ .falling = 0x0a,
++ },
++ [12] = {
++ .load_wdt_rising_falling_pt = 0x10,
++ .load_wdt_selection = 0,
++ .load_wdt_enable = 0,
++ .duty_sync_enable = 0,
++ .invert_pin = 0,
++ .divide_h = 0x5,
++ .divide_l = 0x6,
++ .period = 0x13, //5% ~~
++ .rising = 0x00,
++ .falling = 0x0a,
++ },
++ [13] = {
++ .load_wdt_rising_falling_pt = 0x10,
++ .load_wdt_selection = 0,
++ .load_wdt_enable = 0,
++ .duty_sync_enable = 0,
++ .invert_pin = 0,
++ .divide_h = 0x5,
++ .divide_l = 0x6,
++ .period = 0x13, //5% ~~
++ .rising = 0x00,
++ .falling = 0x0a,
++ },
++ [14] = {
++ .load_wdt_rising_falling_pt = 0x10,
++ .load_wdt_selection = 0,
++ .load_wdt_enable = 0,
++ .duty_sync_enable = 0,
++ .invert_pin = 0,
++ .divide_h = 0x5,
++ .divide_l = 0x6,
++ .period = 0x13, //5% ~~
++ .rising = 0x00,
++ .falling = 0x0a,
++ },
++ [15] = {
++ .load_wdt_rising_falling_pt = 0x10,
++ .load_wdt_selection = 0,
++ .load_wdt_enable = 0,
++ .duty_sync_enable = 0,
++ .invert_pin = 0,
++ .divide_h = 0x5,
++ .divide_l = 0x6,
++ .period = 0x13, //5% ~~
++ .rising = 0x00,
++ .falling = 0x0a,
++ },
++};
++
++/*
++ * 5:4 fan tach edge mode selection bit:
++ * 00: falling
++ * 01: rising
++ * 10: both
++ * 11: reserved.
++ */
++
++struct aspeed_tacho_channel_params {
++ int limited_inverse;
++ u16 threshold;
++ u8 tacho_edge;
++ u8 tacho_debounce;
++ u8 divide;
++};
++
++
++static struct aspeed_tacho_channel_params default_tacho_params[] = {
++ [0] = {
++ .limited_inverse = 0,
++ .threshold = 0,
++ .tacho_edge = 0,
++ .tacho_debounce = 0,
++ .divide = 8,
++ },
++ [1] = {
++ .limited_inverse = 0,
++ .threshold = 0,
++ .tacho_edge = 0,
++ .tacho_debounce = 0,
++ .divide = 8,
++ },
++ [2] = {
++ .limited_inverse = 0,
++ .threshold = 0,
++ .tacho_edge = 0,
++ .tacho_debounce = 0,
++ .divide = 8,
++ },
++ [3] = {
++ .limited_inverse = 0,
++ .threshold = 0,
++ .tacho_edge = 0,
++ .tacho_debounce = 0,
++ .divide = 8,
++ },
++ [4] = {
++ .limited_inverse = 0,
++ .threshold = 0,
++ .tacho_edge = 0,
++ .tacho_debounce = 0,
++ .divide = 8,
++ },
++ [5] = {
++ .limited_inverse = 0,
++ .threshold = 0,
++ .tacho_edge = 0,
++ .tacho_debounce = 0,
++ .divide = 8,
++ },
++ [6] = {
++ .limited_inverse = 0,
++ .threshold = 0,
++ .tacho_edge = 0,
++ .tacho_debounce = 0,
++ .divide = 8,
++ },
++ [7] = {
++ .limited_inverse = 0,
++ .threshold = 0,
++ .tacho_edge = 0,
++ .tacho_debounce = 0,
++ .divide = 8,
++ },
++ [8] = {
++ .limited_inverse = 0,
++ .threshold = 0,
++ .tacho_edge = 0,
++ .tacho_debounce = 0,
++ .divide = 8,
++ },
++ [9] = {
++ .limited_inverse = 0,
++ .threshold = 0,
++ .tacho_edge = 0,
++ .tacho_debounce = 0,
++ .divide = 8,
++ },
++ [10] = {
++ .limited_inverse = 0,
++ .threshold = 0,
++ .tacho_edge = 0,
++ .tacho_debounce = 0,
++ .divide = 8,
++ },
++ [11] = {
++ .limited_inverse = 0,
++ .threshold = 0,
++ .tacho_edge = 0,
++ .tacho_debounce = 0,
++ .divide = 8,
++ },
++ [12] = {
++ .limited_inverse = 0,
++ .threshold = 0,
++ .tacho_edge = 0,
++ .tacho_debounce = 0,
++ .divide = 8,
++ },
++ [13] = {
++ .limited_inverse = 0,
++ .threshold = 0,
++ .tacho_edge = 0,
++ .tacho_debounce = 0,
++ .divide = 8,
++ },
++ [14] = {
++ .limited_inverse = 0,
++ .threshold = 0,
++ .tacho_edge = 0,
++ .tacho_debounce = 0,
++ .divide = 8,
++ },
++ [15] = {
++ .limited_inverse = 0,
++ .threshold = 0,
++ .tacho_edge = 0,
++ .tacho_debounce = 0,
++ .divide = 8,
++ },
++};
++
++struct aspeed_pwm_tachometer_data {
++ struct regmap *regmap;
++ unsigned long clk_freq;
++ struct reset_control *reset;
++ bool pwm_present[16];
++ bool fan_tach_present[16];
++ struct aspeed_pwm_channel_params *pwm_channel;
++ struct aspeed_tacho_channel_params *tacho_channel;
++ struct aspeed_cooling_device *cdev[8];
++ const struct attribute_group *groups[3];
++};
++
++struct aspeed_cooling_device {
++ char name[16];
++ struct aspeed_pwm_tachometer_data *priv;
++ struct thermal_cooling_device *tcdev;
++ int pwm_channel;
++ u8 *cooling_levels;
++ u8 max_state;
++ u8 cur_state;
++};
++
++static int regmap_aspeed_pwm_tachometer_reg_write(void *context, unsigned int reg,
++ unsigned int val)
++{
++ void __iomem *regs = (void __iomem *)context;
++
++ writel(val, regs + reg);
++ return 0;
++}
++
++static int regmap_aspeed_pwm_tachometer_reg_read(void *context, unsigned int reg,
++ unsigned int *val)
++{
++ void __iomem *regs = (void __iomem *)context;
++
++ *val = readl(regs + reg);
++ return 0;
++}
++
++static const struct regmap_config aspeed_pwm_tachometer_regmap_config = {
++ .reg_bits = 32,
++ .val_bits = 32,
++ .reg_stride = 4,
++ .max_register = 0x100,
++ .reg_write = regmap_aspeed_pwm_tachometer_reg_write,
++ .reg_read = regmap_aspeed_pwm_tachometer_reg_read,
++ .fast_io = true,
++};
++
++static void aspeed_set_pwm_channel_enable(struct regmap *regmap, u8 pwm_channel,
++ bool enable)
++{
++ regmap_update_bits(regmap, ASPEED_PWM_CTRL_CH(pwm_channel), (PWM_CLK_ENABLE | PWM_PIN_EN), enable ? (PWM_CLK_ENABLE | PWM_PIN_EN) : 0);
++}
++
++static void aspeed_set_fan_tach_ch_enable(struct aspeed_pwm_tachometer_data *priv, u8 fan_tach_ch,
++ bool enable)
++{
++ u32 i = 0, j;
++ u32 divide_val = 0;
++ u32 reg_value = 0;
++
++ if(enable) {
++ //4 ^ n
++ //check pwm clk and to change tacho devide 25KZ
++ for(i = 0; i < 12; i++) {
++ divide_val = 1;
++ for (j = 1; j <= i; j++)
++ divide_val *= 4;
++// printk("i : %d , priv->clk_freq/divide_val %d ",i, priv->clk_freq/divide_val);
++ if((priv->clk_freq/divide_val) < 250000)
++ break;
++ }
++ i--;
++ divide_val = ((1 << i) * (1 << i));
++// printk("tacho divide_val %d , i %x max tacho clk %d \n", divide_val, i, priv->clk_freq / divide_val);
++ priv->tacho_channel[fan_tach_ch].divide = i;
++
++ reg_value = TACHO_ENABLE |
++ (priv->tacho_channel[fan_tach_ch].tacho_edge << TECHIO_EDGE_BIT) |
++ (priv->tacho_channel[fan_tach_ch].divide << TACHO_CLK_DIV_BIT) |
++ (priv->tacho_channel[fan_tach_ch].tacho_debounce << TACHO_DEBOUNCE_BIT);
++
++ if(priv->tacho_channel[fan_tach_ch].limited_inverse)
++ reg_value |= TACHO_INVERS_LIMIT;
++
++ if(priv->tacho_channel[fan_tach_ch].threshold)
++ reg_value |= (TACHO_IER | priv->tacho_channel[fan_tach_ch].threshold);
++
++ regmap_write(priv->regmap, ASPEED_TACHO_CTRL_CH(fan_tach_ch), reg_value);
++ } else
++ regmap_update_bits(priv->regmap, ASPEED_TACHO_CTRL_CH(fan_tach_ch), TACHO_ENABLE, 0);
++}
++
++static void aspeed_set_pwm_channel_fan_ctrl(struct aspeed_pwm_tachometer_data *priv,
++ u8 index, u8 fan_ctrl)
++{
++ u32 duty_value, ctrl_value;
++
++ if (fan_ctrl == 0) {
++ aspeed_set_pwm_channel_enable(priv->regmap, index, false);
++ } else {
++ duty_value = (priv->pwm_channel[index].period << PWM_PERIOD_BIT) |
++ (0 << PWM_RISING_RISING_BIT) | (fan_ctrl << PWM_RISING_FALLING_BIT);
++
++ ctrl_value = (priv->pwm_channel[index].divide_h << 8) | priv->pwm_channel[index].divide_l;
++
++ if (priv->pwm_channel[index].load_wdt_enable) {
++ ctrl_value |= PWM_DUTY_LOAD_AS_WDT_EN;
++ if(priv->pwm_channel[index].load_wdt_selection) {
++ ctrl_value |= PWM_LOAD_AS_WDT;
++ duty_value |= (priv->pwm_channel[index].load_wdt_rising_falling_pt << PWM_RISING_FALLING_AS_WDT_BIT);
++ } else {
++ duty_value |= (priv->pwm_channel[index].load_wdt_rising_falling_pt << PWM_RISING_FALLING_AS_WDT_BIT);
++ }
++ }
++
++ regmap_write(priv->regmap, ASPEED_PWM_DUTY_CYCLE_CH(index), duty_value);
++
++ regmap_write(priv->regmap, ASPEED_PWM_CTRL_CH(index), ctrl_value);
++// printk("pwm clk is %d \n", priv->clk_freq / (priv->pwm_channel[index].period + 1));
++ aspeed_set_pwm_channel_enable(priv->regmap, index, true);
++ }
++}
++
++#define BOTH_EDGES 0x02 /* 10b */
++
++static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tachometer_data *priv,
++ u8 fan_tach_ch)
++{
++ u32 raw_data, tach_div, clk_source, val;
++ u8 mode, both;
++ int i, retries = 3;
++
++ for(i = 0; i < retries; i++) {
++ regmap_read(priv->regmap, ASPEED_TACHO_STS_CH(fan_tach_ch), &val);
++ if (TACHO_FULL_MEASUREMENT & val)
++ break;
++ }
++
++ raw_data = val & TACHO_VALUE_MASK;
++ if(raw_data == 0xfffff)
++ return 0;
++
++ tach_div = priv->tacho_channel[fan_tach_ch].divide;
++ /*
++ * We need the mode to determine if the raw_data is double (from
++ * counting both edges).
++ */
++ mode = priv->tacho_channel[fan_tach_ch].tacho_edge;
++ both = (mode & BOTH_EDGES) ? 1 : 0;
++// printk("clk %ld, raw_data %x , tach_div %x both %x \n", priv->clk_freq, raw_data, tach_div, both);
++
++ tach_div = (tach_div * 2) * (0x1 << both);
++ clk_source = priv->clk_freq;
++
++ if (raw_data == 0)
++ return 0;
++
++ return (clk_source * 60) / (2 * raw_data * tach_div);
++
++}
++
++static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
++ int index = sensor_attr->index;
++ int ret;
++ struct aspeed_pwm_tachometer_data *priv = dev_get_drvdata(dev);
++ long fan_ctrl;
++
++ ret = kstrtol(buf, 10, &fan_ctrl);
++ if (ret != 0)
++ return ret;
++
++ if (fan_ctrl < 0 || fan_ctrl > priv->pwm_channel[index].period)
++ return -EINVAL;
++
++ if (priv->pwm_channel[index].falling == fan_ctrl)
++ return count;
++
++ priv->pwm_channel[index].falling = fan_ctrl;
++ aspeed_set_pwm_channel_fan_ctrl(priv, index, fan_ctrl);
++
++ return count;
++}
++
++static ssize_t show_pwm(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
++ int index = sensor_attr->index;
++ struct aspeed_pwm_tachometer_data *priv = dev_get_drvdata(dev);
++
++ return sprintf(buf, "%u\n", priv->pwm_channel[index].falling);
++}
++
++static ssize_t show_rpm(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
++ int index = sensor_attr->index;
++ int rpm;
++ struct aspeed_pwm_tachometer_data *priv = dev_get_drvdata(dev);
++
++ rpm = aspeed_get_fan_tach_ch_rpm(priv, index);
++ if (rpm < 0)
++ return rpm;
++
++ return sprintf(buf, "%d\n", rpm);
++}
++
++static umode_t pwm_is_visible(struct kobject *kobj,
++ struct attribute *a, int index)
++{
++ struct device *dev = container_of(kobj, struct device, kobj);
++ struct aspeed_pwm_tachometer_data *priv = dev_get_drvdata(dev);
++
++ if (!priv->pwm_present[index])
++ return 0;
++ return a->mode;
++}
++
++static umode_t fan_dev_is_visible(struct kobject *kobj,
++ struct attribute *a, int index)
++{
++ struct device *dev = container_of(kobj, struct device, kobj);
++ struct aspeed_pwm_tachometer_data *priv = dev_get_drvdata(dev);
++
++ if (!priv->fan_tach_present[index])
++ return 0;
++ return a->mode;
++}
++
++static SENSOR_DEVICE_ATTR(pwm0, 0644,
++ show_pwm, set_pwm, 0);
++static SENSOR_DEVICE_ATTR(pwm1, 0644,
++ show_pwm, set_pwm, 1);
++static SENSOR_DEVICE_ATTR(pwm2, 0644,
++ show_pwm, set_pwm, 2);
++static SENSOR_DEVICE_ATTR(pwm3, 0644,
++ show_pwm, set_pwm, 3);
++static SENSOR_DEVICE_ATTR(pwm4, 0644,
++ show_pwm, set_pwm, 4);
++static SENSOR_DEVICE_ATTR(pwm5, 0644,
++ show_pwm, set_pwm, 5);
++static SENSOR_DEVICE_ATTR(pwm6, 0644,
++ show_pwm, set_pwm, 6);
++static SENSOR_DEVICE_ATTR(pwm7, 0644,
++ show_pwm, set_pwm, 7);
++static SENSOR_DEVICE_ATTR(pwm8, 0644,
++ show_pwm, set_pwm, 8);
++static SENSOR_DEVICE_ATTR(pwm9, 0644,
++ show_pwm, set_pwm, 9);
++static SENSOR_DEVICE_ATTR(pwm10, 0644,
++ show_pwm, set_pwm, 10);
++static SENSOR_DEVICE_ATTR(pwm11, 0644,
++ show_pwm, set_pwm, 11);
++static SENSOR_DEVICE_ATTR(pwm12, 0644,
++ show_pwm, set_pwm, 12);
++static SENSOR_DEVICE_ATTR(pwm13, 0644,
++ show_pwm, set_pwm, 13);
++static SENSOR_DEVICE_ATTR(pwm14, 0644,
++ show_pwm, set_pwm, 14);
++static SENSOR_DEVICE_ATTR(pwm15, 0644,
++ show_pwm, set_pwm, 15);
++static struct attribute *pwm_dev_attrs[] = {
++ &sensor_dev_attr_pwm0.dev_attr.attr,
++ &sensor_dev_attr_pwm1.dev_attr.attr,
++ &sensor_dev_attr_pwm2.dev_attr.attr,
++ &sensor_dev_attr_pwm3.dev_attr.attr,
++ &sensor_dev_attr_pwm4.dev_attr.attr,
++ &sensor_dev_attr_pwm5.dev_attr.attr,
++ &sensor_dev_attr_pwm6.dev_attr.attr,
++ &sensor_dev_attr_pwm7.dev_attr.attr,
++ &sensor_dev_attr_pwm8.dev_attr.attr,
++ &sensor_dev_attr_pwm9.dev_attr.attr,
++ &sensor_dev_attr_pwm10.dev_attr.attr,
++ &sensor_dev_attr_pwm11.dev_attr.attr,
++ &sensor_dev_attr_pwm12.dev_attr.attr,
++ &sensor_dev_attr_pwm13.dev_attr.attr,
++ &sensor_dev_attr_pwm14.dev_attr.attr,
++ &sensor_dev_attr_pwm15.dev_attr.attr,
++ NULL,
++};
++
++static const struct attribute_group pwm_dev_group = {
++ .attrs = pwm_dev_attrs,
++ .is_visible = pwm_is_visible,
++};
++
++static SENSOR_DEVICE_ATTR(fan0_input, 0444,
++ show_rpm, NULL, 0);
++static SENSOR_DEVICE_ATTR(fan1_input, 0444,
++ show_rpm, NULL, 1);
++static SENSOR_DEVICE_ATTR(fan2_input, 0444,
++ show_rpm, NULL, 2);
++static SENSOR_DEVICE_ATTR(fan3_input, 0444,
++ show_rpm, NULL, 3);
++static SENSOR_DEVICE_ATTR(fan4_input, 0444,
++ show_rpm, NULL, 4);
++static SENSOR_DEVICE_ATTR(fan5_input, 0444,
++ show_rpm, NULL, 5);
++static SENSOR_DEVICE_ATTR(fan6_input, 0444,
++ show_rpm, NULL, 6);
++static SENSOR_DEVICE_ATTR(fan7_input, 0444,
++ show_rpm, NULL, 7);
++static SENSOR_DEVICE_ATTR(fan8_input, 0444,
++ show_rpm, NULL, 8);
++static SENSOR_DEVICE_ATTR(fan9_input, 0444,
++ show_rpm, NULL, 9);
++static SENSOR_DEVICE_ATTR(fan10_input, 0444,
++ show_rpm, NULL, 10);
++static SENSOR_DEVICE_ATTR(fan11_input, 0444,
++ show_rpm, NULL, 11);
++static SENSOR_DEVICE_ATTR(fan12_input, 0444,
++ show_rpm, NULL, 12);
++static SENSOR_DEVICE_ATTR(fan13_input, 0444,
++ show_rpm, NULL, 13);
++static SENSOR_DEVICE_ATTR(fan14_input, 0444,
++ show_rpm, NULL, 14);
++static SENSOR_DEVICE_ATTR(fan15_input, 0444,
++ show_rpm, NULL, 15);
++static struct attribute *fan_dev_attrs[] = {
++ &sensor_dev_attr_fan0_input.dev_attr.attr,
++ &sensor_dev_attr_fan1_input.dev_attr.attr,
++ &sensor_dev_attr_fan2_input.dev_attr.attr,
++ &sensor_dev_attr_fan3_input.dev_attr.attr,
++ &sensor_dev_attr_fan4_input.dev_attr.attr,
++ &sensor_dev_attr_fan5_input.dev_attr.attr,
++ &sensor_dev_attr_fan6_input.dev_attr.attr,
++ &sensor_dev_attr_fan7_input.dev_attr.attr,
++ &sensor_dev_attr_fan8_input.dev_attr.attr,
++ &sensor_dev_attr_fan9_input.dev_attr.attr,
++ &sensor_dev_attr_fan10_input.dev_attr.attr,
++ &sensor_dev_attr_fan11_input.dev_attr.attr,
++ &sensor_dev_attr_fan12_input.dev_attr.attr,
++ &sensor_dev_attr_fan13_input.dev_attr.attr,
++ &sensor_dev_attr_fan14_input.dev_attr.attr,
++ &sensor_dev_attr_fan15_input.dev_attr.attr,
++ NULL
++};
++
++static const struct attribute_group fan_dev_group = {
++ .attrs = fan_dev_attrs,
++ .is_visible = fan_dev_is_visible,
++};
++
++static void aspeed_create_pwm_channel(struct aspeed_pwm_tachometer_data *priv,
++ u8 pwm_channel)
++{
++ priv->pwm_present[pwm_channel] = true;
++
++ //use default
++ aspeed_set_pwm_channel_fan_ctrl(priv, pwm_channel, priv->pwm_channel[pwm_channel].falling);
++}
++
++static void aspeed_create_fan_tach_channel(struct aspeed_pwm_tachometer_data *priv,
++ u8 *fan_tach_ch,
++ int count)
++{
++ u8 val, index;
++
++ for (val = 0; val < count; val++) {
++ index = fan_tach_ch[val];
++ priv->fan_tach_present[index] = true;
++ aspeed_set_fan_tach_ch_enable(priv, index, true);
++ }
++}
++
++static int
++aspeed_pwm_cz_get_max_state(struct thermal_cooling_device *tcdev,
++ unsigned long *state)
++{
++ struct aspeed_cooling_device *cdev = tcdev->devdata;
++
++ *state = cdev->max_state;
++
++ return 0;
++}
++
++static int
++aspeed_pwm_cz_get_cur_state(struct thermal_cooling_device *tcdev,
++ unsigned long *state)
++{
++ struct aspeed_cooling_device *cdev = tcdev->devdata;
++
++ *state = cdev->cur_state;
++
++ return 0;
++}
++
++static int
++aspeed_pwm_cz_set_cur_state(struct thermal_cooling_device *tcdev,
++ unsigned long state)
++{
++ struct aspeed_cooling_device *cdev = tcdev->devdata;
++
++ if (state > cdev->max_state)
++ return -EINVAL;
++
++ cdev->cur_state = state;
++ cdev->priv->pwm_channel[cdev->pwm_channel].falling =
++ cdev->cooling_levels[cdev->cur_state];
++ aspeed_set_pwm_channel_fan_ctrl(cdev->priv, cdev->pwm_channel,
++ cdev->cooling_levels[cdev->cur_state]);
++
++ return 0;
++}
++
++static const struct thermal_cooling_device_ops aspeed_pwm_cool_ops = {
++ .get_max_state = aspeed_pwm_cz_get_max_state,
++ .get_cur_state = aspeed_pwm_cz_get_cur_state,
++ .set_cur_state = aspeed_pwm_cz_set_cur_state,
++};
++
++static int aspeed_create_pwm_cooling(struct device *dev,
++ struct device_node *child,
++ struct aspeed_pwm_tachometer_data *priv,
++ u32 pwm_channel, u8 num_levels)
++{
++ int ret;
++ struct aspeed_cooling_device *cdev;
++
++ cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL);
++ if (!cdev)
++ return -ENOMEM;
++
++ cdev->cooling_levels = devm_kzalloc(dev, num_levels, GFP_KERNEL);
++ if (!cdev->cooling_levels)
++ return -ENOMEM;
++
++ cdev->max_state = num_levels - 1;
++ ret = of_property_read_u8_array(child, "cooling-levels",
++ cdev->cooling_levels,
++ num_levels);
++ if (ret) {
++ dev_err(dev, "Property 'cooling-levels' cannot be read.\n");
++ return ret;
++ }
++ snprintf(cdev->name, MAX_CDEV_NAME_LEN, "%s%d", child->name, pwm_channel);
++
++ cdev->tcdev = thermal_of_cooling_device_register(child,
++ cdev->name,
++ cdev,
++ &aspeed_pwm_cool_ops);
++ if (IS_ERR(cdev->tcdev))
++ return PTR_ERR(cdev->tcdev);
++
++ cdev->priv = priv;
++ cdev->pwm_channel = pwm_channel;
++
++ priv->cdev[pwm_channel] = cdev;
++
++ return 0;
++}
++
++static int aspeed_pwm_create_fan(struct device *dev,
++ struct device_node *child,
++ struct aspeed_pwm_tachometer_data *priv)
++{
++ u8 *fan_tach_ch;
++ u32 pwm_channel;
++ int ret, count;
++
++ ret = of_property_read_u32(child, "reg", &pwm_channel);
++ if (ret)
++ return ret;
++
++ aspeed_create_pwm_channel(priv, (u8)pwm_channel);
++
++ ret = of_property_count_u8_elems(child, "cooling-levels");
++ if (ret > 0) {
++ ret = aspeed_create_pwm_cooling(dev, child, priv, pwm_channel,
++ ret);
++ if (ret)
++ return ret;
++ }
++
++ count = of_property_count_u8_elems(child, "aspeed,fan-tach-ch");
++ if (count < 1)
++ return -EINVAL;
++
++ fan_tach_ch = devm_kzalloc(dev, sizeof(*fan_tach_ch) * count,
++ GFP_KERNEL);
++ if (!fan_tach_ch)
++ return -ENOMEM;
++ ret = of_property_read_u8_array(child, "aspeed,fan-tach-ch",
++ fan_tach_ch, count);
++ if (ret)
++ return ret;
++
++ aspeed_create_fan_tach_channel(priv, fan_tach_ch, count);
++
++ return 0;
++}
++
++static int aspeed_pwm_tachometer_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct device_node *np, *child;
++ struct aspeed_pwm_tachometer_data *priv;
++ void __iomem *regs;
++ struct resource *res;
++ struct device *hwmon;
++ struct clk *clk;
++ int ret;
++
++ np = dev->of_node;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (!res)
++ return -ENOENT;
++ regs = devm_ioremap_resource(dev, res);
++ if (IS_ERR(regs))
++ return PTR_ERR(regs);
++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ priv->pwm_channel = default_pwm_params;
++ priv->tacho_channel = default_tacho_params;
++ priv->regmap = devm_regmap_init(dev, NULL, (__force void *)regs,
++ &aspeed_pwm_tachometer_regmap_config);
++ if (IS_ERR(priv->regmap))
++ return PTR_ERR(priv->regmap);
++
++ clk = devm_clk_get(dev, NULL);
++ if (IS_ERR(clk))
++ return -ENODEV;
++ priv->clk_freq = clk_get_rate(clk);
++
++ priv->reset = devm_reset_control_get(&pdev->dev, NULL);
++ if (IS_ERR(priv->reset)) {
++ dev_err(&pdev->dev, "can't get aspeed_pwm_tacho reset\n");
++ return PTR_ERR(priv->reset);
++ }
++
++ //scu init
++ reset_control_assert(priv->reset);
++ reset_control_deassert(priv->reset);
++
++ for_each_child_of_node(np, child) {
++ ret = aspeed_pwm_create_fan(dev, child, priv);
++ if (ret) {
++ of_node_put(child);
++ return ret;
++ }
++ }
++
++ priv->groups[0] = &pwm_dev_group;
++ priv->groups[1] = &fan_dev_group;
++ priv->groups[2] = NULL;
++ hwmon = devm_hwmon_device_register_with_groups(dev,
++ "aspeed_g6_pwm_tacho",
++ priv, priv->groups);
++
++ return PTR_ERR_OR_ZERO(hwmon);
++}
++
++static const struct of_device_id of_pwm_tachometer_match_table[] = {
++ { .compatible = "aspeed,ast2600-pwm-tacho", },
++ {},
++};
++MODULE_DEVICE_TABLE(of, of_pwm_tachometer_match_table);
++
++static struct platform_driver aspeed_pwm_tachometer_driver = {
++ .probe = aspeed_pwm_tachometer_probe,
++ .driver = {
++ .name = "aspeed_g6_pwm_tacho",
++ .of_match_table = of_pwm_tachometer_match_table,
++ },
++};
++
++module_platform_driver(aspeed_pwm_tachometer_driver);
++
++MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
++MODULE_DESCRIPTION("ASPEED PWM and Fan Tachometer device driver");
++MODULE_LICENSE("GPL");
+--
+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/0077-soc-aspeed-Add-read-only-property-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0077-soc-aspeed-Add-read-only-property-support.patch
new file mode 100644
index 000000000..7ea00a4a2
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0077-soc-aspeed-Add-read-only-property-support.patch
@@ -0,0 +1,47 @@
+From f2d62c90f2ea05b1abf9a1887502122eefb5906d Mon Sep 17 00:00:00 2001
+From: Yong Li <yong.b.li@linux.intel.com>
+Date: Wed, 16 Oct 2019 15:11:06 +0800
+Subject: [PATCH] soc: aspeed: Add read-only property support
+
+Add a read-only property, to support export register bits as read-only.
+
+Signed-off-by: Yong Li <yong.b.li@linux.intel.com>
+---
+ drivers/soc/aspeed/aspeed-bmc-misc.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/drivers/soc/aspeed/aspeed-bmc-misc.c b/drivers/soc/aspeed/aspeed-bmc-misc.c
+index 314007b..bf88376 100644
+--- a/drivers/soc/aspeed/aspeed-bmc-misc.c
++++ b/drivers/soc/aspeed/aspeed-bmc-misc.c
+@@ -15,6 +15,7 @@ struct aspeed_bmc_ctrl {
+ u32 offset;
+ u32 mask;
+ u32 shift;
++ bool read_only;
+ struct regmap *map;
+ struct kobj_attribute attr;
+ };
+@@ -55,6 +56,8 @@ static int aspeed_bmc_misc_parse_dt_child(struct device_node *child,
+ if (rc < 0)
+ return rc;
+
++ ctrl->read_only = of_property_read_bool(child, "read-only");
++
+ ctrl->mask <<= ctrl->shift;
+
+ return 0;
+@@ -116,6 +119,10 @@ static ssize_t aspeed_bmc_misc_store(struct kobject *kobj,
+ return rc;
+
+ ctrl = container_of(attr, struct aspeed_bmc_ctrl, attr);
++
++ if (ctrl->read_only)
++ return -EROFS;
++
+ val <<= ctrl->shift;
+ rc = regmap_update_bits(ctrl->map, ctrl->offset, ctrl->mask, val);
+
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0078-Fix-NCSI-driver-issue-caused-by-host-shutdown.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0078-Fix-NCSI-driver-issue-caused-by-host-shutdown.patch
new file mode 100644
index 000000000..09a957860
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0078-Fix-NCSI-driver-issue-caused-by-host-shutdown.patch
@@ -0,0 +1,70 @@
+From 3eaf814c51f85aaad0d181d27ff686ce35b31d6a Mon Sep 17 00:00:00 2001
+From: Kuiying Wang <kuiying.wang@intel.com>
+Date: Tue, 29 Oct 2019 11:28:29 +0800
+Subject: [PATCH] Fix NCSI driver issue caused by host shutdown due to
+ overheated.
+
+NCSI device cannot be recovered when host shutdown due to overheated.
+
+Tested:
+Heat host till shutdown due to overheated and then
+run the ipmi command like power status
+
+Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
+---
+ net/ncsi/ncsi-manage.c | 18 +++++++++++++-----
+ 1 file changed, 13 insertions(+), 5 deletions(-)
+
+diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
+index 70fe02697544..4e16a0015f26 100644
+--- a/net/ncsi/ncsi-manage.c
++++ b/net/ncsi/ncsi-manage.c
+@@ -132,18 +132,15 @@ static void ncsi_channel_monitor(struct timer_list *t)
+ netdev_err(ndp->ndev.dev, "NCSI Channel %d timed out!\n",
+ nc->id);
+ ncsi_report_link(ndp, true);
+- ndp->flags |= NCSI_DEV_RESHUFFLE;
+
+ ncsi_stop_channel_monitor(nc);
+
+- ncm = &nc->modes[NCSI_MODE_LINK];
+ spin_lock_irqsave(&nc->lock, flags);
+- nc->state = NCSI_CHANNEL_INVISIBLE;
+- ncm->data[2] &= ~0x1;
++ nc->state = NCSI_CHANNEL_INACTIVE;
+ spin_unlock_irqrestore(&nc->lock, flags);
+
+ spin_lock_irqsave(&ndp->lock, flags);
+- nc->state = NCSI_CHANNEL_ACTIVE;
++ ndp->flags |= NCSI_DEV_RESHUFFLE | NCSI_DEV_RESET;
+ list_add_tail_rcu(&nc->link, &ndp->channel_queue);
+ spin_unlock_irqrestore(&ndp->lock, flags);
+ ncsi_process_next_channel(ndp);
+@@ -424,6 +421,7 @@ static void ncsi_request_timeout(struct timer_list *t)
+ {
+ struct ncsi_request *nr = from_timer(nr, t, timer);
+ struct ncsi_dev_priv *ndp = nr->ndp;
++ struct ncsi_dev *nd = &ndp->ndev;
+ struct ncsi_cmd_pkt *cmd;
+ struct ncsi_package *np;
+ struct ncsi_channel *nc;
+@@ -438,6 +436,16 @@ static void ncsi_request_timeout(struct timer_list *t)
+ spin_unlock_irqrestore(&ndp->lock, flags);
+ return;
+ }
++ if (nd->state == ncsi_dev_state_suspend ||
++ nd->state == ncsi_dev_state_suspend_select ||
++ nd->state == ncsi_dev_state_suspend_gls ||
++ nd->state == ncsi_dev_state_suspend_dcnt ||
++ nd->state == ncsi_dev_state_suspend_dc ||
++ nd->state == ncsi_dev_state_suspend_deselect ||
++ nd->state == ncsi_dev_state_suspend_done) {
++ ndp->flags |= NCSI_DEV_RESET;
++ nd->state = ncsi_dev_state_suspend_done;
++ }
+ spin_unlock_irqrestore(&ndp->lock, flags);
+
+ if (nr->flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0080-i2c-aspeed-filter-garbage-interrupts-out.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0080-i2c-aspeed-filter-garbage-interrupts-out.patch
new file mode 100644
index 000000000..89e7b894f
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0080-i2c-aspeed-filter-garbage-interrupts-out.patch
@@ -0,0 +1,56 @@
+From a90b8f7dfcfcab2d40d1a575e3948ef3a34f643f Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Fri, 8 Nov 2019 15:57:27 -0800
+Subject: [PATCH] i2c: aspeed: filter garbage interrupts out
+
+AST2600 makes a garbage interrupt which is decribed as 'reserved'
+in datasheet so filter them out.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/i2c/busses/i2c-aspeed.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
+index 62b803e15ce2..c24cecdcfe89 100644
+--- a/drivers/i2c/busses/i2c-aspeed.c
++++ b/drivers/i2c/busses/i2c-aspeed.c
+@@ -87,7 +87,11 @@
+ * These share bit definitions, so use the same values for the enable &
+ * status bits.
+ */
++#if defined(CONFIG_MACH_ASPEED_G6)
++#define ASPEED_I2CD_INTR_SLAVE_ADDR_RECEIVED_PENDING BIT(29)
++#else
+ #define ASPEED_I2CD_INTR_SLAVE_ADDR_RECEIVED_PENDING BIT(30)
++#endif
+ #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)
+@@ -118,6 +122,11 @@
+ ASPEED_I2CD_INTR_RX_DONE | \
+ ASPEED_I2CD_INTR_TX_NAK | \
+ ASPEED_I2CD_INTR_TX_ACK)
++#define ASPEED_I2CD_INTR_STATUS_MASK \
++ (ASPEED_I2CD_INTR_SLAVE_ADDR_RECEIVED_PENDING | \
++ ASPEED_I2CD_INTR_GCALL_ADDR | \
++ ASPEED_I2CD_INTR_SLAVE_MATCH | \
++ ASPEED_I2CD_INTR_ALL)
+
+ /* 0x14 : I2CD Command/Status Register */
+ #define ASPEED_I2CD_SCL_LINE_STS BIT(18)
+@@ -1021,6 +1030,11 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
+ /* Ack all interrupts except for Rx done */
+ writel(irq_received & ~ASPEED_I2CD_INTR_RX_DONE,
+ bus->base + ASPEED_I2C_INTR_STS_REG);
++ /*
++ * AST2600 makes a garbage interrupt which is decribed as 'reserved'
++ * in datasheet so filter them out.
++ */
++ irq_received &= ASPEED_I2CD_INTR_STATUS_MASK;
+ irq_remaining = irq_received;
+
+ #if IS_ENABLED(CONFIG_I2C_SLAVE)
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0082-ARM-dts-aspeed-g6-add-USB-virtual-hub-fixup.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0082-ARM-dts-aspeed-g6-add-USB-virtual-hub-fixup.patch
new file mode 100644
index 000000000..f950c9262
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0082-ARM-dts-aspeed-g6-add-USB-virtual-hub-fixup.patch
@@ -0,0 +1,53 @@
+From e0e33dfafe30f6dfb175caaf6be99aa9cbfe295a Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Wed, 20 Nov 2019 13:06:58 -0800
+Subject: [PATCH] ARM: dts: aspeed-g6: add USB virtual hub fixup
+
+This commit adds dt and pinctrl fixup for USB virtual hub.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ arch/arm/boot/dts/aspeed-g6-pinctrl.dtsi | 5 +++++
+ arch/arm/boot/dts/aspeed-g6.dtsi | 10 ++++++++++
+ 2 files changed, 15 insertions(+)
+
+diff --git a/arch/arm/boot/dts/aspeed-g6-pinctrl.dtsi b/arch/arm/boot/dts/aspeed-g6-pinctrl.dtsi
+index 045ce66ca876..6ea66aaf9dd0 100644
+--- a/arch/arm/boot/dts/aspeed-g6-pinctrl.dtsi
++++ b/arch/arm/boot/dts/aspeed-g6-pinctrl.dtsi
+@@ -1112,6 +1112,11 @@
+ groups = "UART9";
+ };
+
++ pinctrl_usb2adp_default: usb2adp_default {
++ function = "USB2ADP";
++ groups = "USBA";
++ };
++
+ pinctrl_vb_default: vb_default {
+ function = "VB";
+ groups = "VB";
+diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
+index 33fcd89db6b8..b880e8e8f999 100644
+--- a/arch/arm/boot/dts/aspeed-g6.dtsi
++++ b/arch/arm/boot/dts/aspeed-g6.dtsi
+@@ -272,6 +272,16 @@
+ status = "disabled";
+ };
+
++ vhub: usb-vhub@1e6a0000 {
++ compatible = "aspeed,ast2600-usb-vhub";
++ reg = <0x1e6a0000 0x300>;
++ interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&syscon ASPEED_CLK_GATE_USBPORT1CLK>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_usb2adp_default>;
++ status = "disabled";
++ };
++
+ apb {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0083-usb-gadget-aspeed-add-ast2600-compatible-string.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0083-usb-gadget-aspeed-add-ast2600-compatible-string.patch
new file mode 100644
index 000000000..3f6f3ce91
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0083-usb-gadget-aspeed-add-ast2600-compatible-string.patch
@@ -0,0 +1,32 @@
+From eedb53957b507bda2b9f6025149d2052e0598f76 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Wed, 20 Nov 2019 12:49:46 -0800
+Subject: [PATCH] usb: gadget: aspeed: add ast2600 compatible string
+
+This commit adds "aspeed,ast2600-usb-vhub" compatible string to
+use it for AST2600 USB virtual hub driver. AST2600 support total 7
+downstream device ports so this driver should be modified later to
+support the additional ports.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/usb/gadget/udc/aspeed-vhub/core.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c
+index 90b134d..905e1cf 100644
+--- a/drivers/usb/gadget/udc/aspeed-vhub/core.c
++++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c
+@@ -407,6 +407,9 @@ static const struct of_device_id ast_vhub_dt_ids[] = {
+ {
+ .compatible = "aspeed,ast2500-usb-vhub",
+ },
++ {
++ .compatible = "aspeed,ast2600-usb-vhub",
++ },
+ { }
+ };
+ MODULE_DEVICE_TABLE(of, ast_vhub_dt_ids);
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0084-ARM-dts-aspeed-g6-add-GFX-node.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0084-ARM-dts-aspeed-g6-add-GFX-node.patch
new file mode 100644
index 000000000..f578d0f04
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0084-ARM-dts-aspeed-g6-add-GFX-node.patch
@@ -0,0 +1,35 @@
+From 523bbaaf9f9d064c9cf1f627d64dd7ba169d175d Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Wed, 20 Nov 2019 15:01:06 -0800
+Subject: [PATCH] ARM: dts: aspeed-g6: add GFX node
+
+This commit adds GFX node for AST2600 SoC.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ arch/arm/boot/dts/aspeed-g6.dtsi | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
+index b880e8e8f999..3e2153416e11 100644
+--- a/arch/arm/boot/dts/aspeed-g6.dtsi
++++ b/arch/arm/boot/dts/aspeed-g6.dtsi
+@@ -330,6 +330,15 @@
+ quality = <100>;
+ };
+
++ gfx: display@1e6e6000 {
++ compatible = "aspeed,ast2600-gfx", "syscon";
++ reg = <0x1e6e6000 0x1000>;
++ reg-io-width = <4>;
++ clocks = <&syscon ASPEED_CLK_GATE_D1CLK>;
++ status = "disabled";
++ interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
+ adc: adc@1e6e9000 {
+ compatible = "aspeed,ast2500-adc";
+ reg = <0x1e6e9000 0x100>;
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0085-drm-add-AST2600-GFX-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0085-drm-add-AST2600-GFX-support.patch
new file mode 100644
index 000000000..259720e87
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0085-drm-add-AST2600-GFX-support.patch
@@ -0,0 +1,105 @@
+From 195cd60ff2fe38b18ebdea90245b908548f4e985 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Wed, 20 Nov 2019 14:58:24 -0800
+Subject: [PATCH] drm: add AST2600 GFX support
+
+This commit adds support for AST2600 GFX.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/gpu/drm/aspeed/aspeed_gfx.h | 4 ++++
+ drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c | 6 +++---
+ drivers/gpu/drm/aspeed/aspeed_gfx_drv.c | 18 +++++++++++-------
+ 3 files changed, 18 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx.h b/drivers/gpu/drm/aspeed/aspeed_gfx.h
+index a10358bb61ec..eebd72eb1220 100644
+--- a/drivers/gpu/drm/aspeed/aspeed_gfx.h
++++ b/drivers/gpu/drm/aspeed/aspeed_gfx.h
+@@ -13,11 +13,15 @@ struct aspeed_gfx {
+ struct drm_simple_display_pipe pipe;
+ struct drm_connector connector;
+ struct drm_fbdev_cma *fbdev;
++ u32 scu_misc_offset;
+ };
+
+ int aspeed_gfx_create_pipe(struct drm_device *drm);
+ int aspeed_gfx_create_output(struct drm_device *drm);
+
++#define SCU_MISC_AST2500 0x2c /* SCU Misc of AST2500 */
++#define SCU_MISC_AST2600 0xc0 /* SCU Misc1 of AST2600 */
++
+ #define CRT_CTRL1 0x60 /* CRT Control I */
+ #define CRT_CTRL2 0x64 /* CRT Control II */
+ #define CRT_STATUS 0x68 /* CRT Status */
+diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c
+index 2184b8be6fd4..86359a0fe05f 100644
+--- a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c
++++ b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c
+@@ -59,8 +59,8 @@ static void aspeed_gfx_enable_controller(struct aspeed_gfx *priv)
+ u32 ctrl1 = readl(priv->base + CRT_CTRL1);
+ u32 ctrl2 = readl(priv->base + CRT_CTRL2);
+
+- /* SCU2C: set DAC source for display output to Graphics CRT (GFX) */
+- regmap_update_bits(priv->scu, 0x2c, BIT(16), BIT(16));
++ /* Set DAC source for display output to Graphics CRT (GFX) */
++ regmap_update_bits(priv->scu, priv->scu_misc_offset, BIT(16), BIT(16));
+
+ writel(ctrl1 | CRT_CTRL_EN, priv->base + CRT_CTRL1);
+ writel(ctrl2 | CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2);
+@@ -74,7 +74,7 @@ static void aspeed_gfx_disable_controller(struct aspeed_gfx *priv)
+ writel(ctrl1 & ~CRT_CTRL_EN, priv->base + CRT_CTRL1);
+ writel(ctrl2 & ~CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2);
+
+- regmap_update_bits(priv->scu, 0x2c, BIT(16), 0);
++ regmap_update_bits(priv->scu, priv->scu_misc_offset, BIT(16), 0);
+ }
+
+ static void aspeed_gfx_crtc_mode_set_nofb(struct aspeed_gfx *priv)
+diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c
+index ada2f6aca906..92bc9b475e0f 100644
+--- a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c
++++ b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c
+@@ -112,8 +112,14 @@ static int aspeed_gfx_load(struct drm_device *drm)
+
+ priv->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2500-scu");
+ if (IS_ERR(priv->scu)) {
+- dev_err(&pdev->dev, "failed to find SCU regmap\n");
+- return PTR_ERR(priv->scu);
++ priv->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2600-scu");
++ if (IS_ERR(priv->scu)) {
++ dev_err(&pdev->dev, "failed to find SCU regmap\n");
++ return PTR_ERR(priv->scu);
++ }
++ priv->scu_misc_offset = SCU_MISC_AST2600;
++ } else {
++ priv->scu_misc_offset = SCU_MISC_AST2500;
+ }
+
+ ret = of_reserved_mem_device_init(drm->dev);
+@@ -130,12 +136,9 @@ static int aspeed_gfx_load(struct drm_device *drm)
+ }
+
+ priv->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+- if (IS_ERR(priv->rst)) {
+- dev_err(&pdev->dev,
+- "missing or invalid reset controller device tree entry");
+- return PTR_ERR(priv->rst);
++ if (!IS_ERR_OR_NULL(priv->rst)) {
++ reset_control_deassert(priv->rst);
+ }
+- reset_control_deassert(priv->rst);
+
+ priv->clk = devm_clk_get(drm->dev, NULL);
+ if (IS_ERR(priv->clk)) {
+@@ -211,6 +214,7 @@ static struct drm_driver aspeed_gfx_driver = {
+
+ static const struct of_device_id aspeed_gfx_match[] = {
+ { .compatible = "aspeed,ast2500-gfx" },
++ { .compatible = "aspeed,ast2600-gfx" },
+ { }
+ };
+
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0086-ADC-linux-driver-for-AST2600.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0086-ADC-linux-driver-for-AST2600.patch
new file mode 100644
index 000000000..4dde9fb06
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0086-ADC-linux-driver-for-AST2600.patch
@@ -0,0 +1,271 @@
+From 278db13a36f128b84f76fc0f0f13860b5bf62588 Mon Sep 17 00:00:00 2001
+From: Chen Yugang <yugang.chen@linux.intel.com>
+Date: Tue, 3 Dec 2019 13:41:37 +0800
+Subject: [PATCH] ADC linux driver for AST2600
+
+Tested:
+it's tested with DC input.
+
+Signed-off-by: Chen Yugang <yugang.chen@linux.intel.com>
+---
+ arch/arm/boot/dts/aspeed-g6.dtsi | 14 +++++-
+ drivers/iio/adc/aspeed_adc.c | 99 +++++++++++++++++++++++++++++++++++-----
+ 2 files changed, 99 insertions(+), 14 deletions(-)
+
+diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
+index 3e2153416e11..5acc1085526d 100644
+--- a/arch/arm/boot/dts/aspeed-g6.dtsi
++++ b/arch/arm/boot/dts/aspeed-g6.dtsi
+@@ -339,12 +339,22 @@
+ interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+- adc: adc@1e6e9000 {
+- compatible = "aspeed,ast2500-adc";
++ adc0: adc@1e6e9000 {
++ compatible = "aspeed,ast2600-adc";
+ reg = <0x1e6e9000 0x100>;
+ clocks = <&syscon ASPEED_CLK_APB2>;
++ resets = <&syscon ASPEED_RESET_ADC>;
+ interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
++ #io-channel-cells = <1>;
++ status = "disabled";
++ };
++
++ adc1: adc@1e6e9100 {
++ compatible = "aspeed,ast2600-adc";
++ reg = <0x1e6e9100 0x100>;
++ clocks = <&syscon ASPEED_CLK_APB2>;
+ resets = <&syscon ASPEED_RESET_ADC>;
++ interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
+ #io-channel-cells = <1>;
+ status = "disabled";
+ };
+diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c
+index d3fc39df535d..1dd5a97a16bc 100644
+--- a/drivers/iio/adc/aspeed_adc.c
++++ b/drivers/iio/adc/aspeed_adc.c
+@@ -1,8 +1,12 @@
+-// SPDX-License-Identifier: GPL-2.0-only
+ /*
+- * Aspeed AST2400/2500 ADC
++ * Aspeed AST2400/2500/2600 ADC
+ *
+ * Copyright (C) 2017 Google, Inc.
++ * Copyright (C) ASPEED Technology Inc.
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms and conditions of the GNU General Public License,
++ * version 2, as published by the Free Software Foundation.
++ *
+ */
+
+ #include <linux/clk.h>
+@@ -30,6 +34,14 @@
+ #define ASPEED_REG_CLOCK_CONTROL 0x0C
+ #define ASPEED_REG_MAX 0xC0
+
++/* ast2600 */
++#define REF_VLOTAGE_2500mV 0
++#define REF_VLOTAGE_1200mV GENMASK(6, 6)
++#define REF_VLOTAGE_1550mV GENMASK(7, 7)
++#define REF_VLOTAGE_900mV GENMASK(7, 6)
++
++#define ASPEED_AUTOPENSATING BIT(5)
++
+ #define ASPEED_OPERATION_MODE_POWER_DOWN (0x0 << 1)
+ #define ASPEED_OPERATION_MODE_STANDBY (0x1 << 1)
+ #define ASPEED_OPERATION_MODE_NORMAL (0x7 << 1)
+@@ -45,8 +57,10 @@ struct aspeed_adc_model_data {
+ const char *model_name;
+ unsigned int min_sampling_rate; // Hz
+ unsigned int max_sampling_rate; // Hz
+- unsigned int vref_voltage; // mV
++ u32 vref_voltage; // mV
+ bool wait_init_sequence;
++ struct iio_chan_spec const *channels;
++ int num_channels;
+ };
+
+ struct aspeed_adc_data {
+@@ -56,6 +70,7 @@ struct aspeed_adc_data {
+ struct clk_hw *clk_prescaler;
+ struct clk_hw *clk_scaler;
+ struct reset_control *rst;
++ int cv;
+ };
+
+ #define ASPEED_CHAN(_idx, _data_reg_addr) { \
+@@ -87,6 +102,17 @@ static const struct iio_chan_spec aspeed_adc_iio_channels[] = {
+ ASPEED_CHAN(15, 0x2E),
+ };
+
++static const struct iio_chan_spec ast2600_adc_iio_channels[] = {
++ ASPEED_CHAN(0, 0x10),
++ ASPEED_CHAN(1, 0x12),
++ ASPEED_CHAN(2, 0x14),
++ ASPEED_CHAN(3, 0x16),
++ ASPEED_CHAN(4, 0x18),
++ ASPEED_CHAN(5, 0x1A),
++ ASPEED_CHAN(6, 0x1C),
++ ASPEED_CHAN(7, 0x1E),
++};
++
+ static int aspeed_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+@@ -175,7 +201,10 @@ static int aspeed_adc_probe(struct platform_device *pdev)
+ const struct aspeed_adc_model_data *model_data;
+ struct resource *res;
+ const char *clk_parent_name;
++ char prescaler_clk_name[32];
++ char scaler_clk_name[32];
+ int ret;
++ u32 eng_ctrl = 0;
+ u32 adc_engine_control_reg_val;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
+@@ -194,19 +223,21 @@ static int aspeed_adc_probe(struct platform_device *pdev)
+ spin_lock_init(&data->clk_lock);
+ clk_parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0);
+
++ snprintf(prescaler_clk_name, sizeof(prescaler_clk_name), "prescaler-%s", pdev->name);
+ data->clk_prescaler = clk_hw_register_divider(
+- &pdev->dev, "prescaler", clk_parent_name, 0,
++ &pdev->dev, prescaler_clk_name, clk_parent_name, 0,
+ data->base + ASPEED_REG_CLOCK_CONTROL,
+ 17, 15, 0, &data->clk_lock);
+ if (IS_ERR(data->clk_prescaler))
+ return PTR_ERR(data->clk_prescaler);
+
++ snprintf(scaler_clk_name, sizeof(scaler_clk_name), "scaler-%s", pdev->name);
+ /*
+ * Register ADC clock scaler downstream from the prescaler. Allow rate
+ * setting to adjust the prescaler as well.
+ */
+ data->clk_scaler = clk_hw_register_divider(
+- &pdev->dev, "scaler", "prescaler",
++ &pdev->dev, scaler_clk_name, prescaler_clk_name,
+ CLK_SET_RATE_PARENT,
+ data->base + ASPEED_REG_CLOCK_CONTROL,
+ 0, 10, 0, &data->clk_lock);
+@@ -215,7 +246,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
+ goto scaler_error;
+ }
+
+- data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
++ data->rst = devm_reset_control_get_shared(&pdev->dev, NULL);
+ if (IS_ERR(data->rst)) {
+ dev_err(&pdev->dev,
+ "invalid or missing reset controller device tree entry");
+@@ -225,11 +256,26 @@ static int aspeed_adc_probe(struct platform_device *pdev)
+ reset_control_deassert(data->rst);
+
+ model_data = of_device_get_match_data(&pdev->dev);
++ if (!of_property_read_u32(pdev->dev.of_node, "ref_voltage", (u32 *)&model_data->vref_voltage)) {
++ if (model_data->vref_voltage == 2500)
++ eng_ctrl = REF_VLOTAGE_2500mV;
++ else if (model_data->vref_voltage == 1200)
++ eng_ctrl = REF_VLOTAGE_1200mV;
++ else if ((model_data->vref_voltage >= 1550) && (model_data->vref_voltage <= 2700))
++ eng_ctrl = REF_VLOTAGE_1550mV;
++ else if ((model_data->vref_voltage >= 900) && (model_data->vref_voltage <= 1650))
++ eng_ctrl = REF_VLOTAGE_900mV;
++ else {
++ printk("error ref voltage %d \n", model_data->vref_voltage);
++ eng_ctrl = 0;
++ }
++ } else
++ eng_ctrl = 0;
+
+ if (model_data->wait_init_sequence) {
+ /* Enable engine in normal mode. */
+- writel(ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE,
+- data->base + ASPEED_REG_ENGINE_CONTROL);
++ eng_ctrl |= ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE;
++ writel(eng_ctrl, data->base + ASPEED_REG_ENGINE_CONTROL);
+
+ /* Wait for initial sequence complete. */
+ ret = readl_poll_timeout(data->base + ASPEED_REG_ENGINE_CONTROL,
+@@ -242,12 +288,26 @@ static int aspeed_adc_probe(struct platform_device *pdev)
+ goto poll_timeout_error;
+ }
+
++ /* do compensating calculation use ch 0 */
++ writel(eng_ctrl | ASPEED_OPERATION_MODE_NORMAL |
++ ASPEED_ENGINE_ENABLE | ASPEED_AUTOPENSATING, data->base + ASPEED_REG_ENGINE_CONTROL);
++
++ writel(eng_ctrl | ASPEED_OPERATION_MODE_NORMAL | BIT(16) |
++ ASPEED_ENGINE_ENABLE | ASPEED_AUTOPENSATING, data->base + ASPEED_REG_ENGINE_CONTROL);
++ mdelay(1);
++
++ data->cv = 0x200 - (readl(data->base + 0x10) & GENMASK(9, 0));
++
++ writel(eng_ctrl | ASPEED_OPERATION_MODE_NORMAL |
++ ASPEED_ENGINE_ENABLE | ASPEED_AUTOPENSATING, data->base + ASPEED_REG_ENGINE_CONTROL);
++ printk(KERN_INFO "aspeed_adc: cv %d \n", data->cv);
++
+ /* Start all channels in normal mode. */
+ ret = clk_prepare_enable(data->clk_scaler->clk);
+ if (ret)
+ goto clk_enable_error;
+
+- adc_engine_control_reg_val = GENMASK(31, 16) |
++ adc_engine_control_reg_val = eng_ctrl | GENMASK(31, 16) |
+ ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE;
+ writel(adc_engine_control_reg_val,
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+@@ -257,8 +317,8 @@ static int aspeed_adc_probe(struct platform_device *pdev)
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &aspeed_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+- indio_dev->channels = aspeed_adc_iio_channels;
+- indio_dev->num_channels = ARRAY_SIZE(aspeed_adc_iio_channels);
++ indio_dev->channels = model_data->channels;
++ indio_dev->num_channels = model_data->num_channels;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+@@ -301,6 +361,8 @@ static const struct aspeed_adc_model_data ast2400_model_data = {
+ .vref_voltage = 2500, // mV
+ .min_sampling_rate = 10000,
+ .max_sampling_rate = 500000,
++ .channels = aspeed_adc_iio_channels,
++ .num_channels = 16,
+ };
+
+ static const struct aspeed_adc_model_data ast2500_model_data = {
+@@ -309,11 +371,24 @@ static const struct aspeed_adc_model_data ast2500_model_data = {
+ .min_sampling_rate = 1,
+ .max_sampling_rate = 1000000,
+ .wait_init_sequence = true,
++ .channels = aspeed_adc_iio_channels,
++ .num_channels = 16,
++};
++
++static const struct aspeed_adc_model_data ast2600_model_data = {
++ .model_name = "ast2500-adc",
++ .vref_voltage = 1800, /* mV --> can be 1.2v or 2.5 or ext 1.55~2.7v, 0.9v ~1.65v */
++ .min_sampling_rate = 1,
++ .max_sampling_rate = 1000000,
++ .wait_init_sequence = true,
++ .channels = ast2600_adc_iio_channels,
++ .num_channels = 8,
+ };
+
+ static const struct of_device_id aspeed_adc_matches[] = {
+ { .compatible = "aspeed,ast2400-adc", .data = &ast2400_model_data },
+ { .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data },
++ { .compatible = "aspeed,ast2600-adc", .data = &ast2600_model_data },
+ {},
+ };
+ MODULE_DEVICE_TABLE(of, aspeed_adc_matches);
+@@ -330,5 +405,5 @@ static struct platform_driver aspeed_adc_driver = {
+ module_platform_driver(aspeed_adc_driver);
+
+ MODULE_AUTHOR("Rick Altherr <raltherr@google.com>");
+-MODULE_DESCRIPTION("Aspeed AST2400/2500 ADC Driver");
++MODULE_DESCRIPTION("Aspeed AST2400/2500/2600 ADC Driver");
+ MODULE_LICENSE("GPL");
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0086-ARM-dts-aspeed-g6-add-video-node.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0086-ARM-dts-aspeed-g6-add-video-node.patch
new file mode 100644
index 000000000..221800720
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0086-ARM-dts-aspeed-g6-add-video-node.patch
@@ -0,0 +1,36 @@
+From 90c72bbe5f1ac2c8d62736c44492bd8bbc4fcab0 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Tue, 26 Nov 2019 16:37:36 -0800
+Subject: [PATCH] ARM: dts: aspeed-g6: add video node
+
+This commit adds video node.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ arch/arm/boot/dts/aspeed-g6.dtsi | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
+index 5acc1085526d..b0283e03e9ff 100644
+--- a/arch/arm/boot/dts/aspeed-g6.dtsi
++++ b/arch/arm/boot/dts/aspeed-g6.dtsi
+@@ -359,6 +359,16 @@
+ status = "disabled";
+ };
+
++ video: video@1e700000 {
++ compatible = "aspeed,ast2600-video-engine";
++ reg = <0x1e700000 0x1000>;
++ clocks = <&syscon ASPEED_CLK_GATE_VCLK>,
++ <&syscon ASPEED_CLK_GATE_ECLK>;
++ clock-names = "vclk", "eclk";
++ interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
+ gpio0: gpio@1e780000 {
+ #gpio-cells = <2>;
+ gpio-controller;
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0087-media-aspeed-add-aspeed-ast2600-video-engine-compati.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0087-media-aspeed-add-aspeed-ast2600-video-engine-compati.patch
new file mode 100644
index 000000000..1d8b722de
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0087-media-aspeed-add-aspeed-ast2600-video-engine-compati.patch
@@ -0,0 +1,63 @@
+From 1765cb18bacff9d1f48b833f44da198a09f7d5cb Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Tue, 26 Nov 2019 16:39:16 -0800
+Subject: [PATCH] media: aspeed: add aspeed,ast2600-video-engine compatible
+ string
+
+This commit adds "aspeed,ast2600-video-engine" compatible string
+to support AST2600 video engine H/W. Also. it adds G6 specific
+register handling flow.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/media/platform/aspeed-video.c | 13 ++++++++++---
+ 1 file changed, 10 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c
+index db45502774b1..fe70ad408375 100644
+--- a/drivers/media/platform/aspeed-video.c
++++ b/drivers/media/platform/aspeed-video.c
+@@ -72,8 +72,8 @@
+ #define VE_SEQ_CTRL_CAP_BUSY BIT(16)
+ #define VE_SEQ_CTRL_COMP_BUSY BIT(18)
+
+-#ifdef CONFIG_MACH_ASPEED_G5
+-#define VE_SEQ_CTRL_JPEG_MODE BIT(13) /* AST2500 */
++#if defined(CONFIG_MACH_ASPEED_G5) || defined(CONFIG_MACH_ASPEED_G6)
++#define VE_SEQ_CTRL_JPEG_MODE BIT(13) /* AST2500/2600 */
+ #else
+ #define VE_SEQ_CTRL_JPEG_MODE BIT(8) /* AST2400 */
+ #endif /* CONFIG_MACH_ASPEED_G5 */
+@@ -135,6 +135,12 @@
+
+ #define VE_OFFSET_COMP_STREAM 0x078
+
++#ifdef CONFIG_MACH_ASPEED_G6
++#define VE_JPEG_COMP_SIZE_READ_BACK 0x084 /* AST2600 */
++#else
++#define VE_JPEG_COMP_SIZE_READ_BACK VE_OFFSET_COMP_STREAM
++#endif
++
+ #define VE_SRC_LR_EDGE_DET 0x090
+ #define VE_SRC_LR_EDGE_DET_LEFT GENMASK(11, 0)
+ #define VE_SRC_LR_EDGE_DET_NO_V BIT(12)
+@@ -572,7 +578,7 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg)
+ if (sts & VE_INTERRUPT_COMP_COMPLETE) {
+ struct aspeed_video_buffer *buf;
+ u32 frame_size = aspeed_video_read(video,
+- VE_OFFSET_COMP_STREAM);
++ VE_JPEG_COMP_SIZE_READ_BACK);
+
+ spin_lock(&video->lock);
+ clear_bit(VIDEO_FRAME_INPRG, &video->flags);
+@@ -1718,6 +1724,7 @@ static int aspeed_video_remove(struct platform_device *pdev)
+ static const struct of_device_id aspeed_video_of_match[] = {
+ { .compatible = "aspeed,ast2400-video-engine" },
+ { .compatible = "aspeed,ast2500-video-engine" },
++ { .compatible = "aspeed,ast2600-video-engine" },
+ {}
+ };
+ MODULE_DEVICE_TABLE(of, aspeed_video_of_match);
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0088-clk-ast2600-enable-ESPICLK-always.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0088-clk-ast2600-enable-ESPICLK-always.patch
new file mode 100644
index 000000000..34df0882b
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0088-clk-ast2600-enable-ESPICLK-always.patch
@@ -0,0 +1,30 @@
+From 37efef00064a228c3e723b0eece22d72f2632705 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Fri, 6 Dec 2019 13:51:37 -0800
+Subject: [PATCH] clk: ast2600: enable ESPICLK always
+
+To support continous eSPI H/W handshaking, this patch enables
+ESPICLK always so that there discontinuity of eSPI handshaking while
+boot BMC.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/clk/clk-ast2600.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c
+index 8201d65..21967bc 100644
+--- a/drivers/clk/clk-ast2600.c
++++ b/drivers/clk/clk-ast2600.c
+@@ -87,7 +87,7 @@ static const struct aspeed_gate_data aspeed_g6_gates[] = {
+ [ASPEED_CLK_GATE_EMMCCLK] = { 27, 16, "emmcclk-gate", NULL, 0 }, /* For card clk */
+ /* Reserved 28/29/30 */
+ [ASPEED_CLK_GATE_LCLK] = { 32, 32, "lclk-gate", NULL, 0 }, /* LPC */
+- [ASPEED_CLK_GATE_ESPICLK] = { 33, -1, "espiclk-gate", NULL, 0 }, /* eSPI */
++ [ASPEED_CLK_GATE_ESPICLK] = { 33, -1, "espiclk-gate", NULL, CLK_IS_CRITICAL }, /* eSPI */
+ [ASPEED_CLK_GATE_REF1CLK] = { 34, -1, "ref1clk-gate", "clkin", CLK_IS_CRITICAL },
+ /* Reserved 35 */
+ [ASPEED_CLK_GATE_SDCLK] = { 36, 56, "sdclk-gate", NULL, 0 }, /* SDIO/SD */
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0089-ast2600-enable-high-speed-uart-in-kernel.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0089-ast2600-enable-high-speed-uart-in-kernel.patch
new file mode 100644
index 000000000..962d42e6f
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0089-ast2600-enable-high-speed-uart-in-kernel.patch
@@ -0,0 +1,97 @@
+From 054391014d04e5b97ae62e1bf5e6aed005f3c67a Mon Sep 17 00:00:00 2001
+From: Kuiying Wang <kuiying.wang@intel.com>
+Date: Fri, 13 Dec 2019 16:15:16 +0800
+Subject: [PATCH] ast2600: enable high speed uart in kernel.
+
+Tested:
+1. Config baud rate to 921600 in BIOS setup page
+2. BMC could change env variable "hostserialcfg" to 1.
+3. BMC is force to reboot and SPA baud rate is changed to 921600 successfully.
+4. It is same for back to 115200.
+
+Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
+---
+ drivers/clk/clk-ast2600.c | 26 +++++++++++++++++++-------
+ include/dt-bindings/clock/ast2600-clock.h | 2 ++
+ 2 files changed, 21 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c
+index c7236e39ef85..af908b2dbeb6 100644
+--- a/drivers/clk/clk-ast2600.c
++++ b/drivers/clk/clk-ast2600.c
+@@ -15,7 +15,7 @@
+
+ #include "clk-aspeed.h"
+
+-#define ASPEED_G6_NUM_CLKS 71
++#define ASPEED_G6_NUM_CLKS ASPEED_CLK_MAX
+
+ #define ASPEED_G6_SILICON_REV 0x004
+
+@@ -43,6 +43,9 @@
+ #define ASPEED_MAC12_CLK_DLY 0x340
+ #define ASPEED_MAC34_CLK_DLY 0x350
+
++#define ASPEED_G6_GEN_UART_REF 0x338
++#define UART_192MHZ_R_N_VALUE 0x3c38e
++
+ /* Globally visible clocks */
+ static DEFINE_SPINLOCK(aspeed_g6_clk_lock);
+
+@@ -76,7 +79,7 @@ static const struct aspeed_gate_data aspeed_g6_gates[] = {
+ /* Reserved 11/12 */
+ [ASPEED_CLK_GATE_YCLK] = { 13, 4, "yclk-gate", NULL, 0 }, /* HAC */
+ [ASPEED_CLK_GATE_USBPORT1CLK] = { 14, 14, "usb-port1-gate", NULL, 0 }, /* USB2 hub/USB2 host port 1/USB1.1 dev */
+- [ASPEED_CLK_GATE_UART5CLK] = { 15, -1, "uart5clk-gate", "uart", 0 }, /* UART5 */
++ [ASPEED_CLK_GATE_UART5CLK] = { 15, -1, "uart5clk-gate", "uart5", 0 }, /* UART5 */
+ /* Reserved 16/19 */
+ [ASPEED_CLK_GATE_MAC1CLK] = { 20, 11, "mac1clk-gate", "mac12", 0 }, /* MAC1 */
+ [ASPEED_CLK_GATE_MAC2CLK] = { 21, 12, "mac2clk-gate", "mac12", 0 }, /* MAC2 */
+@@ -437,17 +440,26 @@ static int aspeed_g6_clk_probe(struct platform_device *pdev)
+ return ret;
+ }
+
+- /* UART clock div13 setting */
+- regmap_read(map, ASPEED_G6_MISC_CTRL, &val);
+- if (val & UART_DIV13_EN)
+- rate = 24000000 / 13;
++ /* UART clock setting */
++ regmap_read(map, ASPEED_G6_GEN_UART_REF, &val);
++ if (val == UART_192MHZ_R_N_VALUE){
++ rate = 192000000 / 13;
++ dev_err(dev, "192Mhz baud rate 921600\n");
++ }
+ else
+- rate = 24000000;
++ rate = 24000000 / 13;
+ hw = clk_hw_register_fixed_rate(dev, "uart", NULL, 0, rate);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+ aspeed_g6_clk_data->hws[ASPEED_CLK_UART] = hw;
+
++ /* UART5 clock setting */
++ rate = 24000000 / 13;
++ hw = clk_hw_register_fixed_rate(dev, "uart5", NULL, 0, rate);
++ if (IS_ERR(hw))
++ return PTR_ERR(hw);
++ aspeed_g6_clk_data->hws[ASPEED_CLK_UART5] = hw;
++
+ /* UART6~13 clock div13 setting */
+ regmap_read(map, 0x80, &val);
+ if (val & BIT(31))
+diff --git a/include/dt-bindings/clock/ast2600-clock.h b/include/dt-bindings/clock/ast2600-clock.h
+index 3d90582a813f..4952f7683be7 100644
+--- a/include/dt-bindings/clock/ast2600-clock.h
++++ b/include/dt-bindings/clock/ast2600-clock.h
+@@ -87,6 +87,8 @@
+ #define ASPEED_CLK_MAC2RCLK 68
+ #define ASPEED_CLK_MAC3RCLK 69
+ #define ASPEED_CLK_MAC4RCLK 70
++#define ASPEED_CLK_UART5 71
++#define ASPEED_CLK_MAX 72
+
+ /* Only list resets here that are not part of a gate */
+ #define ASPEED_RESET_ADC 55
+--
+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..714f40bfd
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/intel.cfg
@@ -0,0 +1,87 @@
+CONFIG_BLK_DEV_RAM=y
+CONFIG_HWMON=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_MASS_STORAGE=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
+CONFIG_FSI=n
+CONFIG_FSI_MASTER_HUB=n
+CONFIG_FSI_MASTER_ASPEED=n
+CONFIG_FSI_SCOM=n
+CONFIG_FSI_SBEFIFO=n
+CONFIG_FSI_OCC=n
+CONFIG_ASPEED_P2A_CTRL=n
+CONFIG_USB=n
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=n
+CONFIG_USB_DYNAMIC_MINORS=n
+CONFIG_USB_EHCI_HCD=n
+CONFIG_USB_EHCI_ROOT_HUB_TT=n
+CONFIG_USB_EHCI_HCD_PLATFORM=n
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..1898e95f0
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend
@@ -0,0 +1,74 @@
+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-ast2500-platforms.patch \
+ file://0001-arm-dts-add-DTS-for-Intel-ast2600-platforms.patch \
+ file://0001-arm-dts-base-aspeed-g6-dtsi-fixups.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://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://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://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 \
+ file://0076-arm-ast2600-add-pwm_tacho-driver-from-aspeed.patch \
+ file://0077-soc-aspeed-Add-read-only-property-support.patch \
+ file://0078-Fix-NCSI-driver-issue-caused-by-host-shutdown.patch \
+ file://0080-i2c-aspeed-filter-garbage-interrupts-out.patch \
+ file://0082-ARM-dts-aspeed-g6-add-USB-virtual-hub-fixup.patch \
+ file://0083-usb-gadget-aspeed-add-ast2600-compatible-string.patch \
+ file://0084-ARM-dts-aspeed-g6-add-GFX-node.patch \
+ file://0085-drm-add-AST2600-GFX-support.patch \
+ file://0086-ADC-linux-driver-for-AST2600.patch \
+ file://0086-ARM-dts-aspeed-g6-add-video-node.patch \
+ file://0087-media-aspeed-add-aspeed-ast2600-video-engine-compati.patch \
+ file://0088-clk-ast2600-enable-ESPICLK-always.patch \
+ file://0089-ast2600-enable-high-speed-uart-in-kernel.patch \
+ "
+
+SRC_URI += "${@bb.utils.contains('IMAGE_FSTYPES', 'intel-pfr', 'file://0005-128MB-flashmap-for-PFR.patch', '', d)}"