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/0005-arm-dts-aspeed-g5-add-espi.patch56
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-New-flash-map-for-intel.patch120
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch473
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0009-SGPIO-DT-and-pinctrl-fixup.patch240
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch5395
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0019-Add-I2C-IPMB-support.patch426
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch620
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch597
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0026-Add-support-for-new-PECI-commands.patch392
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch93
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0030-Add-dump-debug-code-into-I2C-drivers.patch142
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0031-Add-high-speed-baud-rate-support-for-UART.patch132
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch556
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-aspeed-Swap-the-mac-nodes-numbering.patch85
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch252
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch510
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch511
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0041-Enable-passthrough-based-gpio-character-device.patch287
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0042-Add-bus-timeout-ms-and-retries-device-tree-propertie.patch105
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0043-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-BT.patch140
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0044-misc-Add-clock-control-logic-into-Aspeed-LPC-SNOOP-d.patch125
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0045-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-KC.patch235
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0046-misc-Add-clock-control-logic-into-Aspeed-LPC-MBOX-dr.patch166
-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/0048-ARM-dts-aspeed-Set-default-status-of-LPC-BT-as-disab.patch40
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0049-Suppress-excessive-HID-gadget-error-logs.patch43
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0051-Add-AST2500-JTAG-device.patch35
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0052-drivers-jtag-Add-JTAG-core-driver.patch920
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0053-Add-Aspeed-SoC-24xx-and-25xx-families-JTAG.patch1294
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0054-Documentation-jtag-Add-bindings-for-Aspeed-SoC.patch108
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0055-Documentation-jtag-Add-ABI-documentation.patch309
-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/0058-i2c-aspeed-add-general-call-support.patch182
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0065-i2c-aspeed-fix-master-pending-state-handling.patch55
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0066-i2c-aspeed-add-buffer-mode-transfer-support.patch770
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/intel.cfg1
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend46
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers/0001-Enable-passthrough-based-gpio-character-device.patch26
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers_%.bbappend5
40 files changed, 15642 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-arm-dts-aspeed-g5-add-espi.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-arm-dts-aspeed-g5-add-espi.patch
new file mode 100644
index 000000000..08498cd01
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-arm-dts-aspeed-g5-add-espi.patch
@@ -0,0 +1,56 @@
+From 536b09695117440ed428ff27023cd9167fcf4dfe Mon Sep 17 00:00:00 2001
+From: Juston Li <juston.li@intel.com>
+Date: Mon, 27 Mar 2017 11:16:00 -0700
+Subject: [PATCH] arm: dts: aspeed-g5: add espi
+
+Signed-off-by: Juston Li <juston.li@intel.com>
+---
+ arch/arm/boot/dts/aspeed-g5.dtsi | 18 +++++++++++++++++-
+ 1 file changed, 17 insertions(+), 1 deletion(-)
+
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index a79e01ffe9d4..0c74adf739d2 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -261,7 +261,7 @@
+ #gpio-cells = <2>;
+ gpio-controller;
+ compatible = "aspeed,ast2500-gpio";
+- reg = <0x1e780000 0x1000>;
++ reg = <0x1e780000 0x0200>;
+ interrupts = <20>;
+ gpio-ranges = <&pinctrl 0 0 220>;
+ clocks = <&syscon ASPEED_CLK_APB>;
+@@ -269,6 +269,15 @@
+ #interrupt-cells = <2>;
+ };
+
++ sgpio: sgpio@1e780200 {
++ #gpio-cells = <2>;
++ gpio-controller;
++ compatible = "aspeed,ast2500-sgpio";
++ reg = <0x1e780200 0x0100>;
++ interrupts = <40>;
++ interrupt-controller;
++ };
++
+ rtc: rtc@1e781000 {
+ compatible = "aspeed,ast2500-rtc";
+ reg = <0x1e781000 0x18>;
+@@ -344,6 +353,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/0007-New-flash-map-for-intel.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-New-flash-map-for-intel.patch
new file mode 100644
index 000000000..11663c503
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-New-flash-map-for-intel.patch
@@ -0,0 +1,120 @@
+From 3eabb52efdecfc0da896476ac5567060a6b3788a 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 | 52 ++++++++++++++++++++++
+ .../boot/dts/openbmc-flash-layout-intel-64MB.dtsi | 39 ++++++++++++++++
+ 2 files changed, 91 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..23426acc30c7
+--- /dev/null
++++ b/arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi
+@@ -0,0 +1,52 @@
++// 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 0xe40000>;
++ label = "rwfs";
++ };
++
++ fit-image-a@1100000 {
++ reg = <0x1100000 0x2500000>;
++ label = "image-a";
++ };
++
++ rc-image@3600000 {
++ reg = <0x3600000 0x2500000>;
++ label = "rc-image";
++ };
++
++ image-staging@5b00000 {
++ reg = <0x5b00000 0x2500000>;
++ 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..6ae8e57087e2
+--- /dev/null
++++ b/arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi
+@@ -0,0 +1,39 @@
++// 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/0008-Add-ASPEED-SGPIO-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch
new file mode 100644
index 000000000..beb5087f5
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch
@@ -0,0 +1,473 @@
+From 58adbd18074fbf8005d5d7a5ec116c326252f606 Mon Sep 17 00:00:00 2001
+From: "Feist, James" <james.feist@intel.com>
+Date: Mon, 5 Jun 2017 11:13:52 -0700
+Subject: [PATCH] Add ASPEED SGPIO driver.
+
+Port aspeed sgpio driver to OBMC Kernel and
+enable it on Purley config. Based off AST sdk 4.0.
+
+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 | 416 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 425 insertions(+)
+ create mode 100644 drivers/gpio/sgpio-aspeed.c
+
+diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
+index b5a2845347ec..e3ce2b68a1fc 100644
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -124,6 +124,14 @@ config GPIO_ASPEED
+ help
+ Say Y here to support Aspeed AST2400 and AST2500 GPIO controllers.
+
++config SGPIO_ASPEED
++ bool "ASPEED SGPIO support"
++ depends on ARCH_ASPEED
++ 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 37628f8dbf70..069155f1db9e 100644
+--- a/drivers/gpio/Makefile
++++ b/drivers/gpio/Makefile
+@@ -32,6 +32,7 @@ obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o
+ obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o
+ obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o
+ obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o
++obj-$(CONFIG_SGPIO_ASPEED) += sgpio-aspeed.o
+ obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o
+ obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
+ obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
+diff --git a/drivers/gpio/sgpio-aspeed.c b/drivers/gpio/sgpio-aspeed.c
+new file mode 100644
+index 000000000000..9c4add74602a
+--- /dev/null
++++ b/drivers/gpio/sgpio-aspeed.c
+@@ -0,0 +1,416 @@
++// SPDX-License-Identifier: GPL-2.0
++// Copyright (C) 2012-2017 ASPEED Technology Inc.
++// Copyright (c) 2018 Intel Corporation
++
++#include <asm/mach/irq.h>
++#include <linux/bitfield.h>
++#include <linux/gpio/driver.h>
++#include <linux/init.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of_gpio.h>
++#include <linux/platform_device.h>
++
++#ifdef ARCH_NR_GPIOS
++#undef ARCH_NR_GPIOS
++#endif
++
++// TODO: move this to aspeed_sgpio_of_table
++#if defined(CONFIG_MACH_ASPEED_G5)
++#define GPIO_PORT_NUM 29
++#elif defined(CONFIG_MACH_ASPEED_G4)
++#define GPIO_PORT_NUM 28
++#endif
++
++// TODO: fix defines
++#define GPIOS_PER_PORT 8
++#define ARCH_NR_GPIOS (GPIOS_PER_PORT * GPIO_PORT_NUM)
++#define ASPEED_SGPIO_CTRL 0x54
++#define SGPIO_CHAIN_CHIP_BASE ARCH_NR_GPIOS
++#define SGPIO_GROUP_NUMS 10
++
++#define ASPEED_VIC_NUMS 64
++#define ASPEED_FIQ_NUMS 64
++#define ARCH_NR_I2C 14
++
++#define IRQ_I2C_CHAIN_START (ASPEED_VIC_NUMS + ASPEED_FIQ_NUMS)
++#define IRQ_GPIO_CHAIN_START (IRQ_I2C_CHAIN_START + ARCH_NR_I2C)
++#define IRQ_SGPIO_CHAIN_START (IRQ_GPIO_CHAIN_START + ARCH_NR_GPIOS)
++
++#define ASPEED_SGPIO_PINS_MASK GENMASK(9, 6)
++#define ASPEED_SGPIO_CLK_DIV_MASK GENMASK(31, 16)
++#define ASPEED_SGPIO_ENABLE BIT(0)
++
++struct aspeed_sgpio {
++ struct device *dev;
++ int mirq; /* master irq */
++ void __iomem *base;
++// TODO: make below members as a struct member
++ uint index;
++ uint data_offset;
++ uint data_read_offset;
++ uint int_en_offset;
++ uint int_type_offset;
++ uint int_sts_offset;
++ uint rst_tol_offset;
++ struct gpio_chip chip;
++};
++
++uint aspeed_sgpio_to_irq(uint gpio)
++{
++ return (gpio + IRQ_SGPIO_CHAIN_START);
++}
++EXPORT_SYMBOL(aspeed_sgpio_to_irq);
++
++uint aspeed_irq_to_sgpio(uint irq)
++{
++ return (irq - IRQ_SGPIO_CHAIN_START);
++}
++EXPORT_SYMBOL(aspeed_irq_to_sgpio);
++
++static int aspeed_sgpio_get(struct gpio_chip *chip, unsigned offset)
++{
++ struct aspeed_sgpio *priv = gpiochip_get_data(chip);
++ uint bit_nr = priv->index * GPIOS_PER_PORT + offset;
++ unsigned long flags;
++ u32 v;
++
++ local_irq_save(flags);
++
++ v = readl(priv->base + priv->data_offset);
++ v &= BIT(bit_nr);
++
++ if (v)
++ v = 1;
++ else
++ v = 0;
++
++ local_irq_restore(flags);
++
++ dev_dbg(priv->dev, "%s, %s[%d]: %d\n", __func__, chip->label,
++ offset, v);
++
++ return v;
++}
++
++static void aspeed_sgpio_set(struct gpio_chip *chip, unsigned offset, int val)
++{
++ struct aspeed_sgpio *priv = gpiochip_get_data(chip);
++ uint bit_nr = priv->index * GPIOS_PER_PORT + offset;
++ unsigned long flags;
++ u32 v;
++
++ local_irq_save(flags);
++
++ v = readl(priv->base + priv->data_read_offset);
++
++ if (val)
++ v |= BIT(bit_nr);
++ else
++ v &= ~BIT(bit_nr);
++
++ writel(v, priv->base + priv->data_offset);
++
++ dev_dbg(priv->dev, "%s, %s[%d]: %d\n", __func__, chip->label,
++ offset, val);
++
++ local_irq_restore(flags);
++}
++
++#define SGPIO_BANK(name, index_no, data, read_data, int_en, int_type, \
++ int_sts, rst_tol, chip_base_idx) { \
++ .index = index_no, \
++ .data_offset = data, \
++ .data_read_offset = read_data, \
++ .int_en_offset = int_en, \
++ .int_type_offset = int_type, \
++ .int_sts_offset = int_sts, \
++ .rst_tol_offset = rst_tol, \
++ .chip = { \
++ .label = name, \
++ .get = aspeed_sgpio_get, \
++ .set = aspeed_sgpio_set, \
++ .base = SGPIO_CHAIN_CHIP_BASE + chip_base_idx * 8, \
++ .ngpio = GPIOS_PER_PORT, \
++ }, \
++}
++
++// TODO: use a single priv after changing it as an array member of the priv
++static struct aspeed_sgpio aspeed_sgpio_gp[] = {
++ SGPIO_BANK("SGPIOA", 0, 0x000, 0x070, 0x004, 0x008, 0x014, 0x018, 0),
++ SGPIO_BANK("SGPIOB", 1, 0x000, 0x070, 0x004, 0x008, 0x014, 0x018, 1),
++ SGPIO_BANK("SGPIOC", 2, 0x000, 0x070, 0x004, 0x008, 0x014, 0x018, 2),
++ SGPIO_BANK("SGPIOD", 3, 0x000, 0x070, 0x004, 0x008, 0x014, 0x018, 3),
++ SGPIO_BANK("SGPIOE", 0, 0x01C, 0x074, 0x020, 0x024, 0x030, 0x034, 4),
++ SGPIO_BANK("SGPIOF", 1, 0x01C, 0x074, 0x020, 0x024, 0x030, 0x034, 5),
++ SGPIO_BANK("SGPIOG", 2, 0x01C, 0x074, 0x020, 0x024, 0x030, 0x034, 6),
++ SGPIO_BANK("SGPIOH", 3, 0x01C, 0x074, 0x020, 0x024, 0x030, 0x034, 7),
++ SGPIO_BANK("SGPIOI", 0, 0x038, 0x078, 0x03C, 0x040, 0x04C, 0x050, 8),
++ SGPIO_BANK("SGPIOJ", 1, 0x038, 0x078, 0x03C, 0x040, 0x04C, 0x050, 9),
++};
++
++/**
++ * We need to unmask the GPIO bank interrupt as soon as possible to avoid
++ * missing GPIO interrupts for other lines in the bank. Then we need to
++ * mask-read-clear-unmask the triggered GPIO lines in the bank to avoid missing
++ * nested interrupts for a GPIO line. If we wait to unmask individual GPIO lines
++ * in the bank after the line's interrupt handler has been run, we may miss some
++ * nested interrupts.
++ */
++static void aspeed_sgpio_irq_handler(struct irq_desc *desc)
++{
++ struct irq_chip *chip = irq_desc_get_chip(desc);
++ struct aspeed_sgpio *priv = irq_desc_get_chip_data(desc);
++ u32 isr;
++ int i;
++
++ chained_irq_enter(chip, desc);
++
++ isr = readl(priv->base + priv->int_sts_offset);
++ isr = (isr >> (GPIOS_PER_PORT * priv->index)) & 0xff;
++
++ dev_dbg(priv->dev, "[%s] isr %x \n", priv->chip.label, isr);
++
++ if (isr != 0) {
++ for (i = 0; i < GPIOS_PER_PORT; i++) {
++ if (BIT(i) & isr)
++ generic_handle_irq(i * GPIOS_PER_PORT +
++ i + IRQ_SGPIO_CHAIN_START);
++ }
++ }
++
++ chained_irq_exit(chip, desc);
++}
++
++static void aspeed_sgpio_ack_irq(struct irq_data *d)
++{
++ struct aspeed_sgpio *priv = irq_get_chip_data(d->irq);
++ uint sgpio_irq = (d->irq - IRQ_SGPIO_CHAIN_START) % GPIOS_PER_PORT;
++ uint bit_nr = priv->index * GPIOS_PER_PORT + sgpio_irq;
++
++ dev_dbg(priv->dev, "irq %d: %s [%s] pin %d\n", d->irq, __func__,
++ priv->chip.label, sgpio_irq);
++
++ writel(BIT(bit_nr), priv->base + priv->int_sts_offset);
++}
++
++static void aspeed_sgpio_mask_irq(struct irq_data *d)
++{
++ struct aspeed_sgpio *priv = irq_get_chip_data(d->irq);
++ uint sgpio_irq = (d->irq - IRQ_SGPIO_CHAIN_START) % GPIOS_PER_PORT;
++ uint bit_nr = priv->index * GPIOS_PER_PORT + sgpio_irq;
++
++ /* Disable IRQ */
++ writel(readl(priv->base + priv->int_en_offset) & ~BIT(bit_nr),
++ priv->base + priv->int_en_offset);
++
++ dev_dbg(priv->dev, "irq %d: %s [%s] pin %d\n ", d->irq, __func__,
++ priv->chip.label, sgpio_irq);
++}
++
++static void aspeed_sgpio_unmask_irq(struct irq_data *d)
++{
++ struct aspeed_sgpio *priv = irq_data_get_irq_chip_data(d);
++ u32 sgpio_irq = (d->irq - IRQ_SGPIO_CHAIN_START) % GPIOS_PER_PORT;
++ uint bit_nr = priv->index * GPIOS_PER_PORT + sgpio_irq;
++
++ /* Enable IRQ */
++ writel(BIT(bit_nr), priv->base + priv->int_sts_offset);
++ writel(readl(priv->base + priv->int_en_offset) | BIT(bit_nr),
++ priv->base + priv->int_en_offset);
++
++ dev_dbg(priv->dev, "irq %d: %s [%s] pin %d\n", d->irq, __func__,
++ priv->chip.label, sgpio_irq);
++}
++
++static int aspeed_sgpio_irq_type(struct irq_data *d, unsigned int type)
++{
++ unsigned int irq = d->irq;
++ struct aspeed_sgpio *priv = irq_get_chip_data(irq);
++ u32 sgpio_irq = (irq - IRQ_SGPIO_CHAIN_START) % GPIOS_PER_PORT;
++ u32 type0, type1, type2;
++
++ dev_dbg(priv->dev, "irq: %d, sgpio_irq: %d , irq_type: 0x%x\n",
++ irq, sgpio_irq, type);
++
++ if (type & ~IRQ_TYPE_SENSE_MASK)
++ return -EINVAL;
++
++ type0 = readl(priv->base + priv->int_type_offset);
++ type1 = readl(priv->base + priv->int_type_offset + 0x04);
++ type2 = readl(priv->base + priv->int_type_offset + 0x08);
++
++ switch (type) {
++ /* Edge rising type */
++ case IRQ_TYPE_EDGE_RISING:
++ type0 |= BIT(sgpio_irq);
++ type1 &= ~BIT(sgpio_irq);
++ type2 &= ~BIT(sgpio_irq);
++ break;
++ /* Edge falling type */
++ case IRQ_TYPE_EDGE_FALLING:
++ type2 |= BIT(sgpio_irq);
++ break;
++ case IRQ_TYPE_EDGE_BOTH:
++ type0 &= ~BIT(sgpio_irq);
++ type1 |= BIT(sgpio_irq);
++ type2 &= ~BIT(sgpio_irq);
++ break;
++ case IRQ_TYPE_LEVEL_HIGH:
++ type0 |= BIT(sgpio_irq);
++ type1 |= BIT(sgpio_irq);
++ type2 &= ~BIT(sgpio_irq);
++ break;
++ case IRQ_TYPE_LEVEL_LOW:
++ type0 &= ~BIT(sgpio_irq);
++ type1 |= BIT(sgpio_irq);
++ type2 &= ~BIT(sgpio_irq);
++ break;
++ default:
++ dev_dbg(priv->dev, "not supported trigger type: %d", type);
++ return -EINVAL;
++ break;
++ }
++
++ writel(type0, priv->base + priv->int_type_offset);
++ writel(type1, priv->base + priv->int_type_offset + 0x04);
++ writel(type2, priv->base + priv->int_type_offset + 0x08);
++
++ return 0;
++}
++
++static struct irq_chip aspeed_sgpio_irq_chip = {
++ .name = "aspeed-sgpio",
++ .irq_ack = aspeed_sgpio_ack_irq,
++ .irq_mask = aspeed_sgpio_mask_irq,
++ .irq_unmask = aspeed_sgpio_unmask_irq,
++ .irq_set_type = aspeed_sgpio_irq_type,
++};
++
++static int aspeed_sgpio_config(struct aspeed_sgpio *priv)
++{
++ /**
++ * There is a limitation that SGPIO clock division has to be larger or
++ * equal to 1. And the value of clock division read back is left shift
++ * 1 bit from 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 example, SGPIO clock is 1MHz and number of SGPIO is 80 then each
++ * SGPIO will be updated in every 80us.
++ */
++ writel(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, 10) |
++ FIELD_PREP(ASPEED_SGPIO_PINS_MASK, SGPIO_GROUP_NUMS) |
++ ASPEED_SGPIO_ENABLE,
++ priv->base + ASPEED_SGPIO_CTRL);
++ dev_dbg(priv->dev, "sgpio config reg: 0x%08X\n",
++ readl(priv->base + ASPEED_SGPIO_CTRL));
++
++ return 0;
++}
++
++static int aspeed_sgpio_probe(struct platform_device *pdev)
++{
++ int i, j;
++ uint irq;
++ struct resource *res;
++ struct aspeed_sgpio *priv;
++ void __iomem *base;
++ int mirq;
++
++ // aspeed_scu_multi_func_sgpio(); done via pinctl
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++
++ base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(base))
++ return PTR_ERR(base);
++
++ mirq = platform_get_irq(pdev, 0);
++ if (!mirq)
++ return -ENODEV;
++
++ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_gp); i++) {
++ // TODO: use heap allocation and use a single priv
++ priv = &aspeed_sgpio_gp[i];
++ priv->dev = &pdev->dev;
++ priv->base = base;
++ priv->mirq = mirq;
++ dev_set_drvdata(&pdev->dev, priv);
++
++ dev_dbg(priv->dev, "add gpio_chip [%s]: %d\n", priv->chip.label,
++ i);
++
++ devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv);
++
++ /* Disable Interrupt & Clear Status & Set Level-High Trigger */
++ writel(0x00000000, priv->base + priv->int_en_offset);
++ writel(0xffffffff, priv->base + priv->int_sts_offset);
++ writel(0xffffffff, priv->base + priv->int_type_offset);
++ writel(0xffffffff, priv->base + priv->int_type_offset + 0x04);
++ writel(0x00000000, priv->base + priv->int_type_offset + 0x08);
++
++ // TODO: no this many chip registration is needed. fix it.
++ for (j = 0; j < GPIOS_PER_PORT; j++) {
++ irq = i * GPIOS_PER_PORT + j + IRQ_SGPIO_CHAIN_START;
++ dev_dbg(priv->dev, "inst chip data %d\n", irq);
++ irq_set_chip_data(irq, priv);
++ irq_set_chip_and_handler(irq, &aspeed_sgpio_irq_chip,
++ handle_level_irq);
++ irq_clear_status_flags(irq, IRQ_NOREQUEST);
++ }
++ }
++
++ irq_set_chained_handler(priv->mirq, aspeed_sgpio_irq_handler);
++
++ aspeed_sgpio_config(priv);
++
++ dev_info(&pdev->dev, "sgpio controller registered, irq %d\n",
++ priv->mirq);
++
++ return 0;
++}
++
++static int aspeed_sgpio_remove(struct platform_device *pdev)
++{
++ struct aspeed_sgpio *priv =
++ &aspeed_sgpio_gp[ARRAY_SIZE(aspeed_sgpio_gp) - 1];
++
++ irq_set_chained_handler(priv->mirq, NULL);
++
++ return 0;
++}
++
++static const struct of_device_id aspeed_sgpio_of_table[] = {
++ { .compatible = "aspeed,ast2400-sgpio", },
++ { .compatible = "aspeed,ast2500-sgpio", },
++ { },
++};
++MODULE_DEVICE_TABLE(of, aspeed_sgpio_of_table);
++
++static struct platform_driver aspeed_sgpio_driver = {
++ .probe = aspeed_sgpio_probe,
++ .remove = aspeed_sgpio_remove,
++ .driver = {
++ .name = "sgpio-aspeed",
++ .of_match_table = of_match_ptr(aspeed_sgpio_of_table),
++ },
++};
++
++static int __init aspeed_sgpio_init(void)
++{
++ return platform_driver_register(&aspeed_sgpio_driver);
++}
++subsys_initcall(aspeed_sgpio_init);
++
++static void __exit aspeed_sgpio_exit(void)
++{
++ platform_driver_unregister(&aspeed_sgpio_driver);
++}
++module_exit(aspeed_sgpio_exit);
++
++MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
++MODULE_DESCRIPTION("ASPEED SGPIO driver");
++MODULE_LICENSE("GPL v2");
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0009-SGPIO-DT-and-pinctrl-fixup.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0009-SGPIO-DT-and-pinctrl-fixup.patch
new file mode 100644
index 000000000..1c5d9ab53
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0009-SGPIO-DT-and-pinctrl-fixup.patch
@@ -0,0 +1,240 @@
+From 2f895fe17cd72124b2a04af306f9349e5da90a6c 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 | 54 ++++++++++--------------------
+ arch/arm/boot/dts/aspeed-g5.dtsi | 8 +++++
+ drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c | 48 +++++++++++++-------------
+ drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c | 4 +++
+ 4 files changed, 54 insertions(+), 60 deletions(-)
+
+diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
+index 3990aed25ee6..19f721118b52 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -203,6 +203,18 @@
+ #interrupt-cells = <2>;
+ };
+
++ sgpio: sgpio@1e780200 {
++ #gpio-cells = <2>;
++ gpio-controller;
++ compatible = "aspeed,ast2400-sgpio";
++ reg = <0x1e780200 0x0100>;
++ interrupts = <40>;
++ interrupt-controller;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_sgpm_default>;
++ status = "disabled";
++ };
++
+ timer: timer@1e782000 {
+ /* This timer is a Faraday FTTMR010 derivative */
+ compatible = "aspeed,ast2400-timer";
+@@ -1183,44 +1195,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 0c74adf739d2..d4c99b82f7bd 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -276,6 +276,9 @@
+ reg = <0x1e780200 0x0100>;
+ interrupts = <40>;
+ interrupt-controller;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_sgpm_default>;
++ status = "disabled";
+ };
+
+ rtc: rtc@1e781000 {
+@@ -1388,6 +1391,11 @@
+ groups = "SDA2";
+ };
+
++ pinctrl_sgpm_default: sgpm_default {
++ function = "SGPM";
++ groups = "SGPM";
++ };
++
+ pinctrl_sgps1_default: sgps1_default {
+ function = "SGPS1";
+ groups = "SGPS1";
+diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c
+index 05b153034517..353af05b8602 100644
+--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c
++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c
+@@ -401,16 +401,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(SGPSCK, SGPS, SIG_DESC_SET(SCU84, 0));
++SS_PIN_DECL(A14, GPIOG0, SGPSCK);
+
+ #define E13 49
+-SSSF_PIN_DECL(E13, GPIOG1, SGPSLD, SIG_DESC_SET(SCU84, 1));
++SIG_EXPR_LIST_DECL_SINGLE(SGPSLD, SGPS, SIG_DESC_SET(SCU84, 1));
++SS_PIN_DECL(E13, GPIOG1, SGPSLD);
+
+ #define D13 50
+-SSSF_PIN_DECL(D13, GPIOG2, SGPSI0, SIG_DESC_SET(SCU84, 2));
++SIG_EXPR_LIST_DECL_SINGLE(SGPSIO, SGPS, SIG_DESC_SET(SCU84, 2));
++SS_PIN_DECL(D13, GPIOG2, SGPSIO);
+
+ #define C13 51
+-SSSF_PIN_DECL(C13, GPIOG3, SGPSI1, SIG_DESC_SET(SCU84, 3));
++SIG_EXPR_LIST_DECL_SINGLE(SGPSI1, SGPS, SIG_DESC_SET(SCU84, 3));
++SS_PIN_DECL(C13, GPIOG3, SGPSI1);
++
++FUNC_GROUP_DECL(SGPS, A14, E13, D13, C13);
+
+ #define B13 52
+ SIG_EXPR_LIST_DECL_SINGLE(OSCCLK, OSCCLK, SIG_DESC_SET(SCU2C, 1));
+@@ -576,16 +582,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(SGPMCK, SGPM, SIG_DESC_SET(SCU84, 8));
++SS_PIN_DECL(J5, GPIOJ0, SGPMCK);
+
+ #define J4 73
+-SSSF_PIN_DECL(J4, GPIOJ1, SGPMLD, SIG_DESC_SET(SCU84, 9));
++SIG_EXPR_LIST_DECL_SINGLE(SGPMLD, SGPM, SIG_DESC_SET(SCU84, 9));
++SS_PIN_DECL(J4, GPIOJ1, SGPMLD);
+
+ #define K5 74
+-SSSF_PIN_DECL(K5, GPIOJ2, SGPMO, SIG_DESC_SET(SCU84, 10));
++SIG_EXPR_LIST_DECL_SINGLE(SGPMO, SGPM, SIG_DESC_SET(SCU84, 10));
++SS_PIN_DECL(K5, GPIOJ2, SGPMO);
+
+ #define J3 75
+-SSSF_PIN_DECL(J3, GPIOJ3, SGPMI, SIG_DESC_SET(SCU84, 11));
++SIG_EXPR_LIST_DECL_SINGLE(SGPMI, SGPM, SIG_DESC_SET(SCU84, 11));
++SS_PIN_DECL(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));
+@@ -2083,14 +2095,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),
+@@ -2238,14 +2244,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),
+diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
+index 4230e1038a88..13f749e35001 100644
+--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c
+@@ -577,6 +577,8 @@ SS_PIN_DECL(N3, GPIOJ2, SGPMO);
+ SIG_EXPR_LIST_DECL_SINGLE(SGPMI, SGPM, SIG_DESC_SET(SCU84, 11));
+ SS_PIN_DECL(N4, GPIOJ3, SGPMI);
+
++FUNC_GROUP_DECL(SGPM, R2, L2, N3, N4);
++
+ #define N5 76
+ SIG_EXPR_LIST_DECL_SINGLE(VGAHS, VGAHS, SIG_DESC_SET(SCU84, 12));
+ SIG_EXPR_LIST_DECL_SINGLE(DASHN5, DASHN5, SIG_DESC_SET(SCU94, 8));
+@@ -2127,6 +2129,7 @@ static const struct aspeed_pin_group aspeed_g5_groups[] = {
+ ASPEED_PINCTRL_GROUP(SD2),
+ ASPEED_PINCTRL_GROUP(SDA1),
+ ASPEED_PINCTRL_GROUP(SDA2),
++ ASPEED_PINCTRL_GROUP(SGPM),
+ ASPEED_PINCTRL_GROUP(SGPS1),
+ ASPEED_PINCTRL_GROUP(SGPS2),
+ ASPEED_PINCTRL_GROUP(SIOONCTRL),
+@@ -2296,6 +2299,7 @@ static const struct aspeed_pin_function aspeed_g5_functions[] = {
+ ASPEED_PINCTRL_FUNC(SD2),
+ ASPEED_PINCTRL_FUNC(SDA1),
+ ASPEED_PINCTRL_FUNC(SDA2),
++ ASPEED_PINCTRL_FUNC(SGPM),
+ ASPEED_PINCTRL_FUNC(SGPS1),
+ ASPEED_PINCTRL_FUNC(SGPS2),
+ ASPEED_PINCTRL_FUNC(SIOONCTRL),
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch
new file mode 100644
index 000000000..d2ecdfd72
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch
@@ -0,0 +1,5395 @@
+From 9c27803dd432c7a9fc57dd3e16f0fd724919575e Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Mon, 7 Jan 2019 09:56:10 -0800
+Subject: [PATCH] Update PECI drivers to sync with linux upstreaming version
+
+Upstreaming is in holding. It's for adding DTS sensor with PECI
+subsystem code update.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ Documentation/hwmon/peci-cputemp | 34 +-
+ drivers/hwmon/Kconfig | 4 +-
+ drivers/hwmon/peci-cputemp.c | 162 ++++--
+ drivers/hwmon/peci-dimmtemp.c | 69 +--
+ drivers/hwmon/peci-hwmon.h | 9 +-
+ drivers/mfd/Kconfig | 5 +-
+ drivers/mfd/intel-peci-client.c | 49 +-
+ drivers/peci/Kconfig | 46 +-
+ drivers/peci/Makefile | 7 +-
+ drivers/peci/busses/Kconfig | 32 ++
+ drivers/peci/busses/Makefile | 7 +
+ drivers/peci/busses/peci-aspeed.c | 494 +++++++++++++++++++
+ drivers/peci/busses/peci-npcm.c | 410 +++++++++++++++
+ drivers/peci/peci-aspeed.c | 505 -------------------
+ drivers/peci/peci-core.c | 905 +++++++++++++++++++---------------
+ drivers/peci/peci-dev.c | 346 +++++++++++++
+ drivers/peci/peci-npcm.c | 410 ---------------
+ include/linux/mfd/intel-peci-client.h | 32 +-
+ include/linux/peci.h | 30 +-
+ include/uapi/linux/peci-ioctl.h | 416 +++++++++-------
+ 20 files changed, 2296 insertions(+), 1676 deletions(-)
+ create mode 100644 drivers/peci/busses/Kconfig
+ create mode 100644 drivers/peci/busses/Makefile
+ create mode 100644 drivers/peci/busses/peci-aspeed.c
+ create mode 100644 drivers/peci/busses/peci-npcm.c
+ delete mode 100644 drivers/peci/peci-aspeed.c
+ create mode 100644 drivers/peci/peci-dev.c
+ delete mode 100644 drivers/peci/peci-npcm.c
+
+diff --git a/Documentation/hwmon/peci-cputemp b/Documentation/hwmon/peci-cputemp
+index 821a9258f2e6..a3a3e465c888 100644
+--- a/Documentation/hwmon/peci-cputemp
++++ b/Documentation/hwmon/peci-cputemp
+@@ -51,28 +51,38 @@ temp1_crit Provides shutdown temperature of the CPU package which
+ temp1_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of
+ the CPU package.
+
+-temp2_label "Tcontrol"
+-temp2_input Provides current Tcontrol temperature of the CPU
++temp2_label "DTS"
++temp2_input Provides current DTS temperature of the CPU package.
++temp2_max Provides thermal control temperature of the CPU package
++ which is also known as Tcontrol.
++temp2_crit Provides shutdown temperature of the CPU package which
++ is also known as the maximum processor junction
++ temperature, Tjmax or Tprochot.
++temp2_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of
++ the CPU package.
++
++temp3_label "Tcontrol"
++temp3_input Provides current Tcontrol temperature of the CPU
+ package which is also known as Fan Temperature target.
+ Indicates the relative value from thermal monitor trip
+ temperature at which fans should be engaged.
+-temp2_crit Provides Tcontrol critical value of the CPU package
++temp3_crit Provides Tcontrol critical value of the CPU package
+ which is same to Tjmax.
+
+-temp3_label "Tthrottle"
+-temp3_input Provides current Tthrottle temperature of the CPU
++temp4_label "Tthrottle"
++temp4_input Provides current Tthrottle temperature of the CPU
+ package. Used for throttling temperature. If this value
+ is allowed and lower than Tjmax - the throttle will
+ occur and reported at lower than Tjmax.
+
+-temp4_label "Tjmax"
+-temp4_input Provides the maximum junction temperature, Tjmax of the
++temp5_label "Tjmax"
++temp5_input Provides the maximum junction temperature, Tjmax of the
+ CPU package.
+
+-temp[5-*]_label Provides string "Core X", where X is resolved core
++temp[6-*]_label Provides string "Core X", where X is resolved core
+ number.
+-temp[5-*]_input Provides current temperature of each core.
+-temp[5-*]_max Provides thermal control temperature of the core.
+-temp[5-*]_crit Provides shutdown temperature of the core.
+-temp[5-*]_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of
++temp[6-*]_input Provides current temperature of each core.
++temp[6-*]_max Provides thermal control temperature of the core.
++temp[6-*]_crit Provides shutdown temperature of the core.
++temp[6-*]_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of
+ the core.
+diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
+index 18cd3b17f660..0bd06a938526 100644
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -1321,7 +1321,7 @@ config SENSORS_PECI_CPUTEMP
+ the PECI Client Command Suite via the processor PECI client.
+ Check Documentation/hwmon/peci-cputemp for details.
+
+- This driver can also be built as a module. If so, the module
++ This driver can also be built as a module. If so, the module
+ will be called peci-cputemp.
+
+ config SENSORS_PECI_DIMMTEMP
+@@ -1335,7 +1335,7 @@ config SENSORS_PECI_DIMMTEMP
+ Suite via the processor PECI client.
+ Check Documentation/hwmon/peci-dimmtemp for details.
+
+- This driver can also be built as a module. If so, the module
++ This driver can also be built as a module. If so, the module
+ will be called peci-dimmtemp.
+
+ source "drivers/hwmon/pmbus/Kconfig"
+diff --git a/drivers/hwmon/peci-cputemp.c b/drivers/hwmon/peci-cputemp.c
+index 11880c86a854..3cb2db2fdf0a 100644
+--- a/drivers/hwmon/peci-cputemp.c
++++ b/drivers/hwmon/peci-cputemp.c
+@@ -1,5 +1,5 @@
+ // SPDX-License-Identifier: GPL-2.0
+-// Copyright (c) 2018 Intel Corporation
++// Copyright (c) 2018-2019 Intel Corporation
+
+ #include <linux/hwmon.h>
+ #include <linux/jiffies.h>
+@@ -9,7 +9,7 @@
+ #include <linux/platform_device.h>
+ #include "peci-hwmon.h"
+
+-#define DEFAULT_CHANNEL_NUMS 4
++#define DEFAULT_CHANNEL_NUMS 5
+ #define CORETEMP_CHANNEL_NUMS CORE_NUMS_MAX
+ #define CPUTEMP_CHANNEL_NUMS (DEFAULT_CHANNEL_NUMS + CORETEMP_CHANNEL_NUMS)
+
+@@ -21,6 +21,7 @@
+
+ struct temp_group {
+ struct temp_data die;
++ struct temp_data dts;
+ struct temp_data tcontrol;
+ struct temp_data tthrottle;
+ struct temp_data tjmax;
+@@ -43,6 +44,7 @@ struct peci_cputemp {
+
+ enum cputemp_channels {
+ channel_die,
++ channel_dts,
+ channel_tcontrol,
+ channel_tthrottle,
+ channel_tjmax,
+@@ -54,6 +56,10 @@ static const u32 config_table[DEFAULT_CHANNEL_NUMS + 1] = {
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+ HWMON_T_CRIT_HYST,
+
++ /* DTS margin */
++ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
++ HWMON_T_CRIT_HYST,
++
+ /* Tcontrol temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT,
+
+@@ -70,6 +76,7 @@ static const u32 config_table[DEFAULT_CHANNEL_NUMS + 1] = {
+
+ static const char *cputemp_label[CPUTEMP_CHANNEL_NUMS] = {
+ "Die",
++ "DTS",
+ "Tcontrol",
+ "Tthrottle",
+ "Tjmax",
+@@ -92,19 +99,20 @@ static int get_temp_targets(struct peci_cputemp *priv)
+ s32 tthrottle_offset;
+ s32 tcontrol_margin;
+ u8 pkg_cfg[4];
+- int rc;
++ int ret;
+
+- /**
++ /*
+ * Just use only the tcontrol marker to determine if target values need
+ * update.
+ */
+ if (!peci_temp_need_update(&priv->temp.tcontrol))
+ return 0;
+
+- rc = peci_client_read_package_config(priv->mgr,
+- MBX_INDEX_TEMP_TARGET, 0, pkg_cfg);
+- if (rc)
+- return rc;
++ ret = peci_client_read_package_config(priv->mgr,
++ PECI_MBX_INDEX_TEMP_TARGET, 0,
++ pkg_cfg);
++ if (ret)
++ return ret;
+
+ priv->temp.tjmax.value = pkg_cfg[2] * 1000;
+
+@@ -123,17 +131,16 @@ static int get_temp_targets(struct peci_cputemp *priv)
+ static int get_die_temp(struct peci_cputemp *priv)
+ {
+ struct peci_get_temp_msg msg;
+- int rc;
++ int ret;
+
+ if (!peci_temp_need_update(&priv->temp.die))
+ return 0;
+
+ msg.addr = priv->mgr->client->addr;
+
+- rc = peci_command(priv->mgr->client->adapter, PECI_CMD_GET_TEMP,
+- &msg);
+- if (rc)
+- return rc;
++ ret = peci_command(priv->mgr->client->adapter, PECI_CMD_GET_TEMP, &msg);
++ if (ret)
++ return ret;
+
+ /* Note that the tjmax should be available before calling it */
+ priv->temp.die.value = priv->temp.tjmax.value +
+@@ -144,24 +151,70 @@ static int get_die_temp(struct peci_cputemp *priv)
+ return 0;
+ }
+
++static int get_dts(struct peci_cputemp *priv)
++{
++ struct peci_rd_pkg_cfg_msg msg;
++ s32 dts_margin;
++ int ret;
++
++ if (!peci_temp_need_update(&priv->temp.dts))
++ return 0;
++
++ msg.addr = priv->mgr->client->addr;
++ msg.index = PECI_MBX_INDEX_DTS_MARGIN;
++ msg.param = 0;
++ msg.rx_len = 4;
++
++ ret = peci_command(priv->mgr->client->adapter, PECI_CMD_RD_PKG_CFG,
++ &msg);
++ if (ret)
++ return ret;
++
++ if (msg.cc != PECI_DEV_CC_SUCCESS)
++ return -EAGAIN;
++
++ dts_margin = (msg.pkg_config[1] << 8) | msg.pkg_config[0];
++
++ /**
++ * Processors return a value of DTS reading in 10.6 format
++ * (10 bits signed decimal, 6 bits fractional).
++ * Error codes:
++ * 0x8000: General sensor error
++ * 0x8001: Reserved
++ * 0x8002: Underflow on reading value
++ * 0x8003-0x81ff: Reserved
++ */
++ if (dts_margin >= 0x8000 && dts_margin <= 0x81ff)
++ return -EIO;
++
++ dts_margin = ten_dot_six_to_millidegree(dts_margin);
++
++ /* Note that the tcontrol should be available before calling it */
++ priv->temp.dts.value = priv->temp.tcontrol.value - dts_margin;
++
++ peci_temp_mark_updated(&priv->temp.dts);
++
++ return 0;
++}
++
+ static int get_core_temp(struct peci_cputemp *priv, int core_index)
+ {
+ s32 core_dts_margin;
+ u8 pkg_cfg[4];
+- int rc;
++ int ret;
+
+ if (!peci_temp_need_update(&priv->temp.core[core_index]))
+ return 0;
+
+- rc = peci_client_read_package_config(priv->mgr,
+- MBX_INDEX_PER_CORE_DTS_TEMP,
+- core_index, pkg_cfg);
+- if (rc)
+- return rc;
++ ret = peci_client_read_package_config(priv->mgr,
++ PECI_MBX_INDEX_PER_CORE_DTS_TEMP,
++ core_index, pkg_cfg);
++ if (ret)
++ return ret;
+
+ core_dts_margin = le16_to_cpup((__le16 *)pkg_cfg);
+
+- /**
++ /*
+ * Processors return a value of the core DTS reading in 10.6 format
+ * (10 bits signed decimal, 6 bits fractional).
+ * Error codes:
+@@ -192,6 +245,7 @@ static int cputemp_read_string(struct device *dev,
+ return -EOPNOTSUPP;
+
+ *str = cputemp_label[channel];
++
+ return 0;
+ }
+
+@@ -200,26 +254,33 @@ static int cputemp_read(struct device *dev,
+ u32 attr, int channel, long *val)
+ {
+ struct peci_cputemp *priv = dev_get_drvdata(dev);
+- int rc, core_index;
++ int ret, core_index;
+
+ if (channel >= CPUTEMP_CHANNEL_NUMS ||
+ !(priv->temp_config[channel] & BIT(attr)))
+ return -EOPNOTSUPP;
+
+- rc = get_temp_targets(priv);
+- if (rc)
+- return rc;
++ ret = get_temp_targets(priv);
++ if (ret)
++ return ret;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ switch (channel) {
+ case channel_die:
+- rc = get_die_temp(priv);
+- if (rc)
++ ret = get_die_temp(priv);
++ if (ret)
+ break;
+
+ *val = priv->temp.die.value;
+ break;
++ case channel_dts:
++ ret = get_dts(priv);
++ if (ret)
++ break;
++
++ *val = priv->temp.dts.value;
++ break;
+ case channel_tcontrol:
+ *val = priv->temp.tcontrol.value;
+ break;
+@@ -231,8 +292,8 @@ static int cputemp_read(struct device *dev,
+ break;
+ default:
+ core_index = channel - DEFAULT_CHANNEL_NUMS;
+- rc = get_core_temp(priv, core_index);
+- if (rc)
++ ret = get_core_temp(priv, core_index);
++ if (ret)
+ break;
+
+ *val = priv->temp.core[core_index].value;
+@@ -249,11 +310,11 @@ static int cputemp_read(struct device *dev,
+ *val = priv->temp.tjmax.value - priv->temp.tcontrol.value;
+ break;
+ default:
+- rc = -EOPNOTSUPP;
++ ret = -EOPNOTSUPP;
+ break;
+ }
+
+- return rc;
++ return ret;
+ }
+
+ static umode_t cputemp_is_visible(const void *data,
+@@ -262,11 +323,11 @@ static umode_t cputemp_is_visible(const void *data,
+ {
+ const struct peci_cputemp *priv = data;
+
+- if (priv->temp_config[channel] & BIT(attr))
+- if (channel < DEFAULT_CHANNEL_NUMS ||
+- (channel >= DEFAULT_CHANNEL_NUMS &&
+- (priv->core_mask & BIT(channel - DEFAULT_CHANNEL_NUMS))))
+- return 0444;
++ if ((priv->temp_config[channel] & BIT(attr)) &&
++ (channel < DEFAULT_CHANNEL_NUMS ||
++ (channel >= DEFAULT_CHANNEL_NUMS &&
++ (priv->core_mask & BIT(channel - DEFAULT_CHANNEL_NUMS)))))
++ return 0444;
+
+ return 0;
+ }
+@@ -280,7 +341,7 @@ static const struct hwmon_ops cputemp_ops = {
+ static int check_resolved_cores(struct peci_cputemp *priv)
+ {
+ struct peci_rd_pci_cfg_local_msg msg;
+- int rc;
++ int ret;
+
+ /* Get the RESOLVED_CORES register value */
+ msg.addr = priv->mgr->client->addr;
+@@ -290,30 +351,34 @@ static int check_resolved_cores(struct peci_cputemp *priv)
+ msg.reg = REG_RESOLVED_CORES_OFFSET;
+ msg.rx_len = 4;
+
+- rc = peci_command(priv->mgr->client->adapter,
+- PECI_CMD_RD_PCI_CFG_LOCAL, &msg);
+- if (rc)
+- return rc;
++ ret = peci_command(priv->mgr->client->adapter,
++ PECI_CMD_RD_PCI_CFG_LOCAL, &msg);
++ if (ret)
++ return ret;
++
++ if (msg.cc != PECI_DEV_CC_SUCCESS)
++ return -EAGAIN;
+
+ priv->core_mask = le32_to_cpup((__le32 *)msg.pci_config);
+ if (!priv->core_mask)
+ return -EAGAIN;
+
+ dev_dbg(priv->dev, "Scanned resolved cores: 0x%x\n", priv->core_mask);
++
+ return 0;
+ }
+
+ static int create_core_temp_info(struct peci_cputemp *priv)
+ {
+- int rc, i;
++ int ret, i;
+
+- rc = check_resolved_cores(priv);
+- if (rc)
+- return rc;
++ ret = check_resolved_cores(priv);
++ if (ret)
++ return ret;
+
+ for (i = 0; i < priv->gen_info->core_max; i++)
+ if (priv->core_mask & BIT(i))
+- while (i + DEFAULT_CHANNEL_NUMS >= priv->config_idx)
++ while (priv->config_idx <= i + DEFAULT_CHANNEL_NUMS)
+ priv->temp_config[priv->config_idx++] =
+ config_table[channel_core];
+
+@@ -326,7 +391,7 @@ static int peci_cputemp_probe(struct platform_device *pdev)
+ struct device *dev = &pdev->dev;
+ struct peci_cputemp *priv;
+ struct device *hwmon_dev;
+- int rc;
++ int ret;
+
+ if ((mgr->client->adapter->cmd_mask &
+ (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
+@@ -346,12 +411,13 @@ static int peci_cputemp_probe(struct platform_device *pdev)
+ mgr->client->addr - PECI_BASE_ADDR);
+
+ priv->temp_config[priv->config_idx++] = config_table[channel_die];
++ priv->temp_config[priv->config_idx++] = config_table[channel_dts];
+ priv->temp_config[priv->config_idx++] = config_table[channel_tcontrol];
+ priv->temp_config[priv->config_idx++] = config_table[channel_tthrottle];
+ priv->temp_config[priv->config_idx++] = config_table[channel_tjmax];
+
+- rc = create_core_temp_info(priv);
+- if (rc)
++ ret = create_core_temp_info(priv);
++ if (ret)
+ dev_dbg(dev, "Skipped creating core temp info\n");
+
+ priv->chip.ops = &cputemp_ops;
+diff --git a/drivers/hwmon/peci-dimmtemp.c b/drivers/hwmon/peci-dimmtemp.c
+index 86a45a90805b..e088366fd138 100644
+--- a/drivers/hwmon/peci-dimmtemp.c
++++ b/drivers/hwmon/peci-dimmtemp.c
+@@ -1,5 +1,5 @@
+ // SPDX-License-Identifier: GPL-2.0
+-// Copyright (c) 2018 Intel Corporation
++// Copyright (c) 2018-2019 Intel Corporation
+
+ #include <linux/hwmon.h>
+ #include <linux/jiffies.h>
+@@ -45,16 +45,16 @@ static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no)
+ int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
+ int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
+ u8 cfg_data[4];
+- int rc;
++ int ret;
+
+ if (!peci_temp_need_update(&priv->temp[dimm_no]))
+ return 0;
+
+- rc = peci_client_read_package_config(priv->mgr,
+- MBX_INDEX_DDR_DIMM_TEMP,
+- chan_rank, cfg_data);
+- if (rc)
+- return rc;
++ ret = peci_client_read_package_config(priv->mgr,
++ PECI_MBX_INDEX_DDR_DIMM_TEMP,
++ chan_rank, cfg_data);
++ if (ret)
++ return ret;
+
+ priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
+
+@@ -77,6 +77,7 @@ static int dimmtemp_read_string(struct device *dev,
+ chan_rank = channel / dimm_idx_max;
+ dimm_idx = channel % dimm_idx_max;
+ *str = dimmtemp_label[chan_rank][dimm_idx];
++
+ return 0;
+ }
+
+@@ -84,16 +85,17 @@ static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+ {
+ struct peci_dimmtemp *priv = dev_get_drvdata(dev);
+- int rc;
++ int ret;
+
+ if (attr != hwmon_temp_input)
+ return -EOPNOTSUPP;
+
+- rc = get_dimm_temp(priv, channel);
+- if (rc)
+- return rc;
++ ret = get_dimm_temp(priv, channel);
++ if (ret)
++ return ret;
+
+ *val = priv->temp[channel].value;
++
+ return 0;
+ }
+
+@@ -120,16 +122,16 @@ static int check_populated_dimms(struct peci_dimmtemp *priv)
+ {
+ u32 chan_rank_max = priv->gen_info->chan_rank_max;
+ u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
+- int chan_rank, dimm_idx, rc;
++ int chan_rank, dimm_idx, ret;
+ u8 cfg_data[4];
+
+ for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) {
+- rc = peci_client_read_package_config(priv->mgr,
+- MBX_INDEX_DDR_DIMM_TEMP,
+- chan_rank, cfg_data);
+- if (rc) {
++ ret = peci_client_read_package_config(priv->mgr,
++ PECI_MBX_INDEX_DDR_DIMM_TEMP,
++ chan_rank, cfg_data);
++ if (ret) {
+ priv->dimm_mask = 0;
+- return rc;
++ return ret;
+ }
+
+ for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++)
+@@ -143,17 +145,18 @@ static int check_populated_dimms(struct peci_dimmtemp *priv)
+ return -EAGAIN;
+
+ dev_dbg(priv->dev, "Scanned populated DIMMs: 0x%x\n", priv->dimm_mask);
++
+ return 0;
+ }
+
+ static int create_dimm_temp_info(struct peci_dimmtemp *priv)
+ {
+- int rc, i, config_idx, channels;
++ int ret, i, config_idx, channels;
+ struct device *hwmon_dev;
+
+- rc = check_populated_dimms(priv);
+- if (rc) {
+- if (rc == -EAGAIN) {
++ ret = check_populated_dimms(priv);
++ if (ret) {
++ if (ret == -EAGAIN) {
+ if (priv->retry_count < DIMM_MASK_CHECK_RETRY_MAX) {
+ queue_delayed_work(priv->work_queue,
+ &priv->work_handler,
+@@ -164,11 +167,11 @@ static int create_dimm_temp_info(struct peci_dimmtemp *priv)
+ } else {
+ dev_err(priv->dev,
+ "Timeout DIMM temp info creation\n");
+- rc = -ETIMEDOUT;
++ ret = -ETIMEDOUT;
+ }
+ }
+
+- return rc;
++ return ret;
+ }
+
+ channels = priv->gen_info->chan_rank_max *
+@@ -192,12 +195,12 @@ static int create_dimm_temp_info(struct peci_dimmtemp *priv)
+ priv,
+ &priv->chip,
+ NULL);
+- rc = PTR_ERR_OR_ZERO(hwmon_dev);
+- if (!rc)
++ ret = PTR_ERR_OR_ZERO(hwmon_dev);
++ if (!ret)
+ dev_dbg(priv->dev, "%s: sensor '%s'\n",
+ dev_name(hwmon_dev), priv->name);
+
+- return rc;
++ return ret;
+ }
+
+ static void create_dimm_temp_info_delayed(struct work_struct *work)
+@@ -205,10 +208,10 @@ static void create_dimm_temp_info_delayed(struct work_struct *work)
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct peci_dimmtemp *priv = container_of(dwork, struct peci_dimmtemp,
+ work_handler);
+- int rc;
++ int ret;
+
+- rc = create_dimm_temp_info(priv);
+- if (rc && rc != -EAGAIN)
++ ret = create_dimm_temp_info(priv);
++ if (ret && ret != -EAGAIN)
+ dev_dbg(priv->dev, "Failed to create DIMM temp info\n");
+ }
+
+@@ -217,7 +220,7 @@ static int peci_dimmtemp_probe(struct platform_device *pdev)
+ struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct peci_dimmtemp *priv;
+- int rc;
++ int ret;
+
+ if ((mgr->client->adapter->cmd_mask &
+ (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
+@@ -242,8 +245,8 @@ static int peci_dimmtemp_probe(struct platform_device *pdev)
+
+ INIT_DELAYED_WORK(&priv->work_handler, create_dimm_temp_info_delayed);
+
+- rc = create_dimm_temp_info(priv);
+- if (rc && rc != -EAGAIN) {
++ ret = create_dimm_temp_info(priv);
++ if (ret && ret != -EAGAIN) {
+ dev_err(dev, "Failed to create DIMM temp info\n");
+ goto err_free_wq;
+ }
+@@ -252,7 +255,7 @@ static int peci_dimmtemp_probe(struct platform_device *pdev)
+
+ err_free_wq:
+ destroy_workqueue(priv->work_queue);
+- return rc;
++ return ret;
+ }
+
+ static int peci_dimmtemp_remove(struct platform_device *pdev)
+diff --git a/drivers/hwmon/peci-hwmon.h b/drivers/hwmon/peci-hwmon.h
+index 6ca1855a86bb..ce6b470eae63 100644
+--- a/drivers/hwmon/peci-hwmon.h
++++ b/drivers/hwmon/peci-hwmon.h
+@@ -1,5 +1,5 @@
+ /* SPDX-License-Identifier: GPL-2.0 */
+-/* Copyright (c) 2018 Intel Corporation */
++/* Copyright (c) 2018-2019 Intel Corporation */
+
+ #ifndef __PECI_HWMON_H
+ #define __PECI_HWMON_H
+@@ -29,11 +29,8 @@ struct temp_data {
+ */
+ static inline bool peci_temp_need_update(struct temp_data *temp)
+ {
+- if (temp->valid &&
+- time_before(jiffies, temp->last_updated + UPDATE_INTERVAL))
+- return false;
+-
+- return true;
++ return !temp->valid ||
++ time_after(jiffies, temp->last_updated + UPDATE_INTERVAL);
+ }
+
+ /**
+diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
+index 9af5730ad7ba..28087e9cd4da 100644
+--- a/drivers/mfd/Kconfig
++++ b/drivers/mfd/Kconfig
+@@ -606,7 +606,7 @@ config MFD_INTEL_MSIC
+ devices used in Intel Medfield platforms.
+
+ config MFD_INTEL_PECI_CLIENT
+- bool "Intel PECI client"
++ tristate "Intel PECI client"
+ depends on (PECI || COMPILE_TEST)
+ select MFD_CORE
+ help
+@@ -619,6 +619,9 @@ config MFD_INTEL_PECI_CLIENT
+ Additional drivers must be enabled in order to use the functionality
+ of the device.
+
++ This driver can also be built as a module. If so, the module
++ will be called intel-peci-client.
++
+ config MFD_IPAQ_MICRO
+ bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
+ depends on SA1100_H3100 || SA1100_H3600
+diff --git a/drivers/mfd/intel-peci-client.c b/drivers/mfd/intel-peci-client.c
+index d53e4f1078ac..466085fd43b9 100644
+--- a/drivers/mfd/intel-peci-client.c
++++ b/drivers/mfd/intel-peci-client.c
+@@ -1,12 +1,12 @@
+ // SPDX-License-Identifier: GPL-2.0
+-// Copyright (c) 2018 Intel Corporation
++// Copyright (c) 2018-2019 Intel Corporation
+
+ #include <linux/bitfield.h>
+ #include <linux/mfd/core.h>
+ #include <linux/mfd/intel-peci-client.h>
+ #include <linux/module.h>
+-#include <linux/peci.h>
+ #include <linux/of_device.h>
++#include <linux/peci.h>
+
+ #define CPU_ID_MODEL_MASK GENMASK(7, 4)
+ #define CPU_ID_FAMILY_MASK GENMASK(11, 8)
+@@ -18,12 +18,6 @@
+ #define LOWER_BYTE_MASK GENMASK(7, 0)
+ #define UPPER_BYTE_MASK GENMASK(16, 8)
+
+-enum cpu_gens {
+- CPU_GEN_HSX = 0, /* Haswell Xeon */
+- CPU_GEN_BRX, /* Broadwell Xeon */
+- CPU_GEN_SKX, /* Skylake Xeon */
+-};
+-
+ static struct mfd_cell peci_functions[] = {
+ { .name = "peci-cputemp", },
+ { .name = "peci-dimmtemp", },
+@@ -31,19 +25,25 @@ static struct mfd_cell peci_functions[] = {
+ };
+
+ static const struct cpu_gen_info cpu_gen_info_table[] = {
+- [CPU_GEN_HSX] = {
++ { /* Haswell Xeon */
+ .family = 6, /* Family code */
+ .model = INTEL_FAM6_HASWELL_X,
+ .core_max = CORE_MAX_ON_HSX,
+ .chan_rank_max = CHAN_RANK_MAX_ON_HSX,
+ .dimm_idx_max = DIMM_IDX_MAX_ON_HSX },
+- [CPU_GEN_BRX] = {
++ { /* Broadwell Xeon */
+ .family = 6, /* Family code */
+ .model = INTEL_FAM6_BROADWELL_X,
+ .core_max = CORE_MAX_ON_BDX,
+ .chan_rank_max = CHAN_RANK_MAX_ON_BDX,
+ .dimm_idx_max = DIMM_IDX_MAX_ON_BDX },
+- [CPU_GEN_SKX] = {
++ { /* Broadwell Xeon D */
++ .family = 6, /* Family code */
++ .model = INTEL_FAM6_BROADWELL_XEON_D,
++ .core_max = CORE_MAX_ON_XD,
++ .chan_rank_max = CHAN_RANK_MAX_ON_XD,
++ .dimm_idx_max = DIMM_IDX_MAX_ON_XD },
++ { /* Skylake Xeon */
+ .family = 6, /* Family code */
+ .model = INTEL_FAM6_SKYLAKE_X,
+ .core_max = CORE_MAX_ON_SKX,
+@@ -53,16 +53,17 @@ static const struct cpu_gen_info cpu_gen_info_table[] = {
+
+ static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv)
+ {
++ struct device *dev = &priv->client->dev;
+ u32 cpu_id;
+ u16 family;
+ u8 model;
+- int rc;
++ int ret;
+ int i;
+
+- rc = peci_get_cpu_id(priv->client->adapter, priv->client->addr,
+- &cpu_id);
+- if (rc)
+- return rc;
++ ret = peci_get_cpu_id(priv->client->adapter, priv->client->addr,
++ &cpu_id);
++ if (ret)
++ return ret;
+
+ family = FIELD_PREP(LOWER_BYTE_MASK,
+ FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id)) |
+@@ -83,11 +84,11 @@ static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv)
+ }
+
+ if (!priv->gen_info) {
+- dev_err(priv->dev, "Can't support this CPU: 0x%x\n", cpu_id);
+- rc = -ENODEV;
++ dev_err(dev, "Can't support this CPU: 0x%x\n", cpu_id);
++ ret = -ENODEV;
+ }
+
+- return rc;
++ return ret;
+ }
+
+ static int peci_client_probe(struct peci_client *client)
+@@ -103,31 +104,29 @@ static int peci_client_probe(struct peci_client *client)
+
+ dev_set_drvdata(dev, priv);
+ priv->client = client;
+- priv->dev = dev;
+ cpu_no = client->addr - PECI_BASE_ADDR;
+
+ ret = peci_client_get_cpu_gen_info(priv);
+ if (ret)
+ return ret;
+
+- ret = devm_mfd_add_devices(priv->dev, cpu_no, peci_functions,
++ ret = devm_mfd_add_devices(dev, cpu_no, peci_functions,
+ ARRAY_SIZE(peci_functions), NULL, 0, NULL);
+ if (ret < 0) {
+- dev_err(priv->dev, "Failed to register child devices: %d\n",
+- ret);
++ dev_err(dev, "Failed to register child devices: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+ }
+
+-#ifdef CONFIG_OF
++#if IS_ENABLED(CONFIG_OF)
+ static const struct of_device_id peci_client_of_table[] = {
+ { .compatible = "intel,peci-client" },
+ { }
+ };
+ MODULE_DEVICE_TABLE(of, peci_client_of_table);
+-#endif
++#endif /* CONFIG_OF */
+
+ static const struct peci_device_id peci_client_ids[] = {
+ { .name = "peci-client" },
+diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig
+index 7293108fb543..9752feee2454 100644
+--- a/drivers/peci/Kconfig
++++ b/drivers/peci/Kconfig
+@@ -2,10 +2,12 @@
+ # Platform Environment Control Interface (PECI) subsystem configuration
+ #
+
++menu "PECI support"
++
+ config PECI
+- bool "PECI support"
+- select RT_MUTEXES
++ tristate "PECI support"
+ select CRC8
++ default n
+ help
+ The Platform Environment Control Interface (PECI) is a one-wire bus
+ interface that provides a communication channel from Intel processors
+@@ -14,37 +16,23 @@ config PECI
+ If you want PECI support, you should say Y here and also to the
+ specific driver for your bus adapter(s) below.
+
+-if PECI
+-
+-#
+-# PECI hardware bus configuration
+-#
+-
+-menu "PECI Hardware Bus support"
+-
+-config PECI_ASPEED
+- tristate "ASPEED PECI support"
+- select REGMAP_MMIO
+- depends on OF
+- depends on ARCH_ASPEED || COMPILE_TEST
+- help
+- Say Y here if you want support for the Platform Environment Control
+- Interface (PECI) bus adapter driver on the ASPEED SoCs.
++ This support is also available as a module. If so, the module
++ will be called peci-core.
+
+- This support is also available as a module. If so, the module
+- will be called peci-aspeed.
++if PECI
+
+-config PECI_NPCM
+- tristate "Nuvoton NPCM PECI support"
+- select REGMAP_MMIO
+- depends on OF
+- depends on ARCH_NPCM || COMPILE_TEST
++config PECI_CHARDEV
++ tristate "PECI device interface"
+ help
+- Say Y here if you want support for the Platform Environment Control
+- Interface (PECI) bus adapter driver on the Nuvoton NPCM SoCs.
++ Say Y here to use peci-* device files, usually found in the /dev
++ directory on your system. They make it possible to have user-space
++ programs use the PECI bus.
+
+ This support is also available as a module. If so, the module
+- will be called peci-npcm.
+-endmenu
++ will be called peci-dev.
++
++source "drivers/peci/busses/Kconfig"
+
+ endif # PECI
++
++endmenu
+diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile
+index 3326da54a21a..da8b0a33fa42 100644
+--- a/drivers/peci/Makefile
++++ b/drivers/peci/Makefile
+@@ -1,10 +1,11 @@
++# SPDX-License-Identifier: GPL-2.0
+ #
+-# Makefile for the PECI core and bus drivers.
++# Makefile for the PECI core drivers.
+ #
+
+ # Core functionality
+ obj-$(CONFIG_PECI) += peci-core.o
++obj-$(CONFIG_PECI_CHARDEV) += peci-dev.o
+
+ # Hardware specific bus drivers
+-obj-$(CONFIG_PECI_ASPEED) += peci-aspeed.o
+-obj-$(CONFIG_PECI_NPCM) += peci-npcm.o
++obj-y += busses/
+diff --git a/drivers/peci/busses/Kconfig b/drivers/peci/busses/Kconfig
+new file mode 100644
+index 000000000000..bfacafb7a7ba
+--- /dev/null
++++ b/drivers/peci/busses/Kconfig
+@@ -0,0 +1,32 @@
++#
++# PECI hardware bus configuration
++#
++
++menu "PECI Hardware Bus support"
++
++config PECI_ASPEED
++ tristate "ASPEED PECI support"
++ depends on ARCH_ASPEED || COMPILE_TEST
++ depends on OF
++ depends on PECI
++ help
++ Say Y here if you want support for the Platform Environment Control
++ Interface (PECI) bus adapter driver on the ASPEED SoCs.
++
++ This support is also available as a module. If so, the module
++ will be called peci-aspeed.
++
++config PECI_NPCM
++ tristate "Nuvoton NPCM PECI support"
++ select REGMAP_MMIO
++ depends on OF
++ depends on ARCH_NPCM || COMPILE_TEST
++ depends on PECI
++ help
++ Say Y here if you want support for the Platform Environment Control
++ Interface (PECI) bus adapter driver on the Nuvoton NPCM SoCs.
++
++ This support is also available as a module. If so, the module
++ will be called peci-npcm.
++
++endmenu
+diff --git a/drivers/peci/busses/Makefile b/drivers/peci/busses/Makefile
+new file mode 100644
+index 000000000000..aa8ce3ae5947
+--- /dev/null
++++ b/drivers/peci/busses/Makefile
+@@ -0,0 +1,7 @@
++# SPDX-License-Identifier: GPL-2.0
++#
++# Makefile for the PECI hardware bus drivers.
++#
++
++obj-$(CONFIG_PECI_ASPEED) += peci-aspeed.o
++obj-$(CONFIG_PECI_NPCM) += peci-npcm.o
+diff --git a/drivers/peci/busses/peci-aspeed.c b/drivers/peci/busses/peci-aspeed.c
+new file mode 100644
+index 000000000000..8a0dd40730cc
+--- /dev/null
++++ b/drivers/peci/busses/peci-aspeed.c
+@@ -0,0 +1,494 @@
++// SPDX-License-Identifier: GPL-2.0
++// Copyright (C) 2012-2017 ASPEED Technology Inc.
++// Copyright (c) 2018-2019 Intel Corporation
++
++#include <linux/bitfield.h>
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/jiffies.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/peci.h>
++#include <linux/platform_device.h>
++#include <linux/reset.h>
++
++/* ASPEED PECI Registers */
++/* Control Register */
++#define ASPEED_PECI_CTRL 0x00
++#define ASPEED_PECI_CTRL_SAMPLING_MASK GENMASK(19, 16)
++#define ASPEED_PECI_CTRL_READ_MODE_MASK GENMASK(13, 12)
++#define ASPEED_PECI_CTRL_READ_MODE_COUNT BIT(12)
++#define ASPEED_PECI_CTRL_READ_MODE_DBG BIT(13)
++#define ASPEED_PECI_CTRL_CLK_SOURCE_MASK BIT(11)
++#define ASPEED_PECI_CTRL_CLK_DIV_MASK GENMASK(10, 8)
++#define ASPEED_PECI_CTRL_INVERT_OUT BIT(7)
++#define ASPEED_PECI_CTRL_INVERT_IN BIT(6)
++#define ASPEED_PECI_CTRL_BUS_CONTENT_EN BIT(5)
++#define ASPEED_PECI_CTRL_PECI_EN BIT(4)
++#define ASPEED_PECI_CTRL_PECI_CLK_EN BIT(0)
++
++/* Timing Negotiation Register */
++#define ASPEED_PECI_TIMING_NEGOTIATION 0x04
++#define ASPEED_PECI_TIMING_MESSAGE_MASK GENMASK(15, 8)
++#define ASPEED_PECI_TIMING_ADDRESS_MASK GENMASK(7, 0)
++
++/* Command Register */
++#define ASPEED_PECI_CMD 0x08
++#define ASPEED_PECI_CMD_PIN_MON BIT(31)
++#define ASPEED_PECI_CMD_STS_MASK GENMASK(27, 24)
++#define ASPEED_PECI_CMD_IDLE_MASK (ASPEED_PECI_CMD_STS_MASK | \
++ ASPEED_PECI_CMD_PIN_MON)
++#define ASPEED_PECI_CMD_FIRE BIT(0)
++
++/* Read/Write Length Register */
++#define ASPEED_PECI_RW_LENGTH 0x0c
++#define ASPEED_PECI_AW_FCS_EN BIT(31)
++#define ASPEED_PECI_READ_LEN_MASK GENMASK(23, 16)
++#define ASPEED_PECI_WRITE_LEN_MASK GENMASK(15, 8)
++#define ASPEED_PECI_TAGET_ADDR_MASK GENMASK(7, 0)
++
++/* Expected FCS Data Register */
++#define ASPEED_PECI_EXP_FCS 0x10
++#define ASPEED_PECI_EXP_READ_FCS_MASK GENMASK(23, 16)
++#define ASPEED_PECI_EXP_AW_FCS_AUTO_MASK GENMASK(15, 8)
++#define ASPEED_PECI_EXP_WRITE_FCS_MASK GENMASK(7, 0)
++
++/* Captured FCS Data Register */
++#define ASPEED_PECI_CAP_FCS 0x14
++#define ASPEED_PECI_CAP_READ_FCS_MASK GENMASK(23, 16)
++#define ASPEED_PECI_CAP_WRITE_FCS_MASK GENMASK(7, 0)
++
++/* Interrupt Register */
++#define ASPEED_PECI_INT_CTRL 0x18
++#define ASPEED_PECI_TIMING_NEGO_SEL_MASK GENMASK(31, 30)
++#define ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO 0
++#define ASPEED_PECI_2ND_BIT_OF_ADDR_NEGO 1
++#define ASPEED_PECI_MESSAGE_NEGO 2
++#define ASPEED_PECI_INT_MASK GENMASK(4, 0)
++#define ASPEED_PECI_INT_BUS_TIMEOUT BIT(4)
++#define ASPEED_PECI_INT_BUS_CONNECT BIT(3)
++#define ASPEED_PECI_INT_W_FCS_BAD BIT(2)
++#define ASPEED_PECI_INT_W_FCS_ABORT BIT(1)
++#define ASPEED_PECI_INT_CMD_DONE BIT(0)
++
++/* Interrupt Status Register */
++#define ASPEED_PECI_INT_STS 0x1c
++#define ASPEED_PECI_INT_TIMING_RESULT_MASK GENMASK(29, 16)
++ /* bits[4..0]: Same bit fields in the 'Interrupt Register' */
++
++/* Rx/Tx Data Buffer Registers */
++#define ASPEED_PECI_W_DATA0 0x20
++#define ASPEED_PECI_W_DATA1 0x24
++#define ASPEED_PECI_W_DATA2 0x28
++#define ASPEED_PECI_W_DATA3 0x2c
++#define ASPEED_PECI_R_DATA0 0x30
++#define ASPEED_PECI_R_DATA1 0x34
++#define ASPEED_PECI_R_DATA2 0x38
++#define ASPEED_PECI_R_DATA3 0x3c
++#define ASPEED_PECI_W_DATA4 0x40
++#define ASPEED_PECI_W_DATA5 0x44
++#define ASPEED_PECI_W_DATA6 0x48
++#define ASPEED_PECI_W_DATA7 0x4c
++#define ASPEED_PECI_R_DATA4 0x50
++#define ASPEED_PECI_R_DATA5 0x54
++#define ASPEED_PECI_R_DATA6 0x58
++#define ASPEED_PECI_R_DATA7 0x5c
++#define ASPEED_PECI_DATA_BUF_SIZE_MAX 32
++
++/* Timing Negotiation */
++#define ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT 8
++#define ASPEED_PECI_RD_SAMPLING_POINT_MAX 15
++#define ASPEED_PECI_CLK_DIV_DEFAULT 0
++#define ASPEED_PECI_CLK_DIV_MAX 7
++#define ASPEED_PECI_MSG_TIMING_DEFAULT 1
++#define ASPEED_PECI_MSG_TIMING_MAX 255
++#define ASPEED_PECI_ADDR_TIMING_DEFAULT 1
++#define ASPEED_PECI_ADDR_TIMING_MAX 255
++
++/* Timeout */
++#define ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC 50000
++#define ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC 10000
++#define ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT 1000
++#define ASPEED_PECI_CMD_TIMEOUT_MS_MAX 60000
++
++struct aspeed_peci {
++ struct peci_adapter *adapter;
++ struct device *dev;
++ void __iomem *base;
++ struct clk *clk;
++ struct reset_control *rst;
++ int irq;
++ spinlock_t lock; /* to sync completion status handling */
++ struct completion xfer_complete;
++ u32 status;
++ u32 cmd_timeout_ms;
++};
++
++static int aspeed_peci_check_idle(struct aspeed_peci *priv)
++{
++ ulong timeout = jiffies + usecs_to_jiffies(ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC);
++ u32 cmd_sts;
++
++ for (;;) {
++ cmd_sts = readl(priv->base + ASPEED_PECI_CMD);
++ if (!(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK))
++ break;
++ if (time_after(jiffies, timeout)) {
++ cmd_sts = readl(priv->base + ASPEED_PECI_CMD);
++ break;
++ }
++ usleep_range((ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC >> 2) + 1,
++ ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC);
++ }
++
++ return !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK) ? 0 : -ETIMEDOUT;
++}
++
++static int aspeed_peci_xfer(struct peci_adapter *adapter,
++ struct peci_xfer_msg *msg)
++{
++ struct aspeed_peci *priv = peci_get_adapdata(adapter);
++ long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms);
++ u32 peci_head, peci_state, rx_data = 0;
++ ulong flags;
++ int i, ret;
++ uint reg;
++
++ if (msg->tx_len > ASPEED_PECI_DATA_BUF_SIZE_MAX ||
++ msg->rx_len > ASPEED_PECI_DATA_BUF_SIZE_MAX)
++ return -EINVAL;
++
++ /* Check command sts and bus idle state */
++ ret = aspeed_peci_check_idle(priv);
++ if (ret)
++ return ret; /* -ETIMEDOUT */
++
++ spin_lock_irqsave(&priv->lock, flags);
++ reinit_completion(&priv->xfer_complete);
++
++ peci_head = FIELD_PREP(ASPEED_PECI_TAGET_ADDR_MASK, msg->addr) |
++ FIELD_PREP(ASPEED_PECI_WRITE_LEN_MASK, msg->tx_len) |
++ FIELD_PREP(ASPEED_PECI_READ_LEN_MASK, msg->rx_len);
++
++ writel(peci_head, priv->base + ASPEED_PECI_RW_LENGTH);
++
++ for (i = 0; i < msg->tx_len; i += 4) {
++ reg = i < 16 ? ASPEED_PECI_W_DATA0 + i % 16 :
++ ASPEED_PECI_W_DATA4 + i % 16;
++ writel(le32_to_cpup((__le32 *)&msg->tx_buf[i]),
++ priv->base + reg);
++ }
++
++ dev_dbg(priv->dev, "HEAD : 0x%08x\n", peci_head);
++ print_hex_dump_debug("TX : ", DUMP_PREFIX_NONE, 16, 1,
++ msg->tx_buf, msg->tx_len, true);
++
++ priv->status = 0;
++ writel(ASPEED_PECI_CMD_FIRE, priv->base + ASPEED_PECI_CMD);
++ spin_unlock_irqrestore(&priv->lock, flags);
++
++ err = wait_for_completion_interruptible_timeout(&priv->xfer_complete,
++ timeout);
++
++ spin_lock_irqsave(&priv->lock, flags);
++ dev_dbg(priv->dev, "INT_STS : 0x%08x\n", priv->status);
++ peci_state = readl(priv->base + ASPEED_PECI_CMD);
++ dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n",
++ FIELD_GET(ASPEED_PECI_CMD_STS_MASK, peci_state));
++
++ writel(0, priv->base + ASPEED_PECI_CMD);
++
++ if (err <= 0 || priv->status != ASPEED_PECI_INT_CMD_DONE) {
++ if (err < 0) { /* -ERESTARTSYS */
++ ret = (int)err;
++ goto err_irqrestore;
++ } else if (err == 0) {
++ dev_dbg(priv->dev, "Timeout waiting for a response!\n");
++ ret = -ETIMEDOUT;
++ goto err_irqrestore;
++ }
++
++ dev_dbg(priv->dev, "No valid response!\n");
++ ret = -EIO;
++ goto err_irqrestore;
++ }
++
++ /*
++ * Note that rx_len and rx_buf size can be an odd number.
++ * Byte handling is more efficient.
++ */
++ for (i = 0; i < msg->rx_len; i++) {
++ u8 byte_offset = i % 4;
++
++ if (byte_offset == 0) {
++ reg = i < 16 ? ASPEED_PECI_R_DATA0 + i % 16 :
++ ASPEED_PECI_R_DATA4 + i % 16;
++ rx_data = readl(priv->base + reg);
++ }
++
++ msg->rx_buf[i] = (u8)(rx_data >> (byte_offset << 3));
++ }
++
++ print_hex_dump_debug("RX : ", DUMP_PREFIX_NONE, 16, 1,
++ msg->rx_buf, msg->rx_len, true);
++
++ peci_state = readl(priv->base + ASPEED_PECI_CMD);
++ dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n",
++ FIELD_GET(ASPEED_PECI_CMD_STS_MASK, peci_state));
++ dev_dbg(priv->dev, "------------------------\n");
++
++err_irqrestore:
++ spin_unlock_irqrestore(&priv->lock, flags);
++ return ret;
++}
++
++static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg)
++{
++ struct aspeed_peci *priv = arg;
++ u32 status;
++
++ spin_lock(&priv->lock);
++ status = readl(priv->base + ASPEED_PECI_INT_STS);
++ writel(status, priv->base + ASPEED_PECI_INT_STS);
++ priv->status |= (status & ASPEED_PECI_INT_MASK);
++
++ /*
++ * In most cases, interrupt bits will be set one by one but also note
++ * that multiple interrupt bits could be set at the same time.
++ */
++ if (status & ASPEED_PECI_INT_BUS_TIMEOUT) {
++ dev_dbg(priv->dev, "ASPEED_PECI_INT_BUS_TIMEOUT\n");
++ }
++
++ if (status & ASPEED_PECI_INT_BUS_CONNECT) {
++ dev_dbg(priv->dev, "ASPEED_PECI_INT_BUS_CONNECT\n");
++ }
++
++ if (status & ASPEED_PECI_INT_W_FCS_BAD) {
++ dev_dbg(priv->dev, "ASPEED_PECI_INT_W_FCS_BAD\n");
++ }
++
++ if (status & ASPEED_PECI_INT_W_FCS_ABORT) {
++ dev_dbg(priv->dev, "ASPEED_PECI_INT_W_FCS_ABORT\n");
++ }
++
++ /*
++ * All commands should be ended up with a ASPEED_PECI_INT_CMD_DONE bit
++ * set even in an error case.
++ */
++ if (status & ASPEED_PECI_INT_CMD_DONE) {
++ dev_dbg(priv->dev, "ASPEED_PECI_INT_CMD_DONE\n");
++ complete(&priv->xfer_complete);
++ }
++
++ spin_unlock(&priv->lock);
++ return IRQ_HANDLED;
++}
++
++static int aspeed_peci_init_ctrl(struct aspeed_peci *priv)
++{
++ u32 msg_timing, addr_timing, rd_sampling_point;
++ u32 clk_freq, clk_divisor, clk_div_val = 0;
++ int ret;
++
++ priv->clk = devm_clk_get(priv->dev, NULL);
++ if (IS_ERR(priv->clk)) {
++ dev_err(priv->dev, "Failed to get clk source.\n");
++ return PTR_ERR(priv->clk);
++ }
++
++ ret = clk_prepare_enable(priv->clk);
++ if (ret) {
++ dev_err(priv->dev, "Failed to enable clock.\n");
++ return ret;
++ }
++
++ ret = device_property_read_u32(priv->dev, "clock-frequency", &clk_freq);
++ if (ret) {
++ dev_err(priv->dev,
++ "Could not read clock-frequency property.\n");
++ clk_disable_unprepare(priv->clk);
++ return ret;
++ }
++
++ clk_divisor = clk_get_rate(priv->clk) / clk_freq;
++
++ while ((clk_divisor >> 1) && (clk_div_val < ASPEED_PECI_CLK_DIV_MAX))
++ clk_div_val++;
++
++ ret = device_property_read_u32(priv->dev, "msg-timing", &msg_timing);
++ if (ret || msg_timing > ASPEED_PECI_MSG_TIMING_MAX) {
++ if (!ret)
++ dev_warn(priv->dev,
++ "Invalid msg-timing : %u, Use default : %u\n",
++ msg_timing, ASPEED_PECI_MSG_TIMING_DEFAULT);
++ msg_timing = ASPEED_PECI_MSG_TIMING_DEFAULT;
++ }
++
++ ret = device_property_read_u32(priv->dev, "addr-timing", &addr_timing);
++ if (ret || addr_timing > ASPEED_PECI_ADDR_TIMING_MAX) {
++ if (!ret)
++ dev_warn(priv->dev,
++ "Invalid addr-timing : %u, Use default : %u\n",
++ addr_timing, ASPEED_PECI_ADDR_TIMING_DEFAULT);
++ addr_timing = ASPEED_PECI_ADDR_TIMING_DEFAULT;
++ }
++
++ ret = device_property_read_u32(priv->dev, "rd-sampling-point",
++ &rd_sampling_point);
++ if (ret || rd_sampling_point > ASPEED_PECI_RD_SAMPLING_POINT_MAX) {
++ if (!ret)
++ dev_warn(priv->dev,
++ "Invalid rd-sampling-point : %u. Use default : %u\n",
++ rd_sampling_point,
++ ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT);
++ rd_sampling_point = ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT;
++ }
++
++ ret = device_property_read_u32(priv->dev, "cmd-timeout-ms",
++ &priv->cmd_timeout_ms);
++ if (ret || priv->cmd_timeout_ms > ASPEED_PECI_CMD_TIMEOUT_MS_MAX ||
++ priv->cmd_timeout_ms == 0) {
++ if (!ret)
++ dev_warn(priv->dev,
++ "Invalid cmd-timeout-ms : %u. Use default : %u\n",
++ priv->cmd_timeout_ms,
++ ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT);
++ priv->cmd_timeout_ms = ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT;
++ }
++
++ writel(FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK,
++ ASPEED_PECI_CLK_DIV_DEFAULT) |
++ ASPEED_PECI_CTRL_PECI_CLK_EN, priv->base + ASPEED_PECI_CTRL);
++
++ /*
++ * Timing negotiation period setting.
++ * The unit of the programmed value is 4 times of PECI clock period.
++ */
++ writel(FIELD_PREP(ASPEED_PECI_TIMING_MESSAGE_MASK, msg_timing) |
++ FIELD_PREP(ASPEED_PECI_TIMING_ADDRESS_MASK, addr_timing),
++ priv->base + ASPEED_PECI_TIMING_NEGOTIATION);
++
++ /* Clear interrupts */
++ writel(readl(priv->base + ASPEED_PECI_INT_STS) | ASPEED_PECI_INT_MASK,
++ priv->base + ASPEED_PECI_INT_STS);
++
++ /* Set timing negotiation mode and enable interrupts */
++ writel(FIELD_PREP(ASPEED_PECI_TIMING_NEGO_SEL_MASK,
++ ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO) |
++ ASPEED_PECI_INT_MASK, priv->base + ASPEED_PECI_INT_CTRL);
++
++ /* Read sampling point and clock speed setting */
++ writel(FIELD_PREP(ASPEED_PECI_CTRL_SAMPLING_MASK, rd_sampling_point) |
++ FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK, clk_div_val) |
++ ASPEED_PECI_CTRL_PECI_EN | ASPEED_PECI_CTRL_PECI_CLK_EN,
++ priv->base + ASPEED_PECI_CTRL);
++
++ return 0;
++}
++
++static int aspeed_peci_probe(struct platform_device *pdev)
++{
++ struct peci_adapter *adapter;
++ struct aspeed_peci *priv;
++ struct resource *res;
++ int ret;
++
++ adapter = peci_alloc_adapter(&pdev->dev, sizeof(*priv));
++ if (!adapter)
++ return -ENOMEM;
++
++ priv = peci_get_adapdata(adapter);
++ priv->adapter = adapter;
++ priv->dev = &pdev->dev;
++ dev_set_drvdata(&pdev->dev, priv);
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ priv->base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(priv->base)) {
++ ret = PTR_ERR(priv->base);
++ goto err_put_adapter_dev;
++ }
++
++ priv->irq = platform_get_irq(pdev, 0);
++ if (!priv->irq) {
++ ret = -ENODEV;
++ goto err_put_adapter_dev;
++ }
++
++ ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_peci_irq_handler,
++ 0, "peci-aspeed-irq", priv);
++ if (ret)
++ goto err_put_adapter_dev;
++
++ init_completion(&priv->xfer_complete);
++ spin_lock_init(&priv->lock);
++
++ priv->adapter->owner = THIS_MODULE;
++ priv->adapter->dev.of_node = of_node_get(dev_of_node(priv->dev));
++ strlcpy(priv->adapter->name, pdev->name, sizeof(priv->adapter->name));
++ priv->adapter->xfer = aspeed_peci_xfer;
++ priv->adapter->use_dma = false;
++
++ priv->rst = devm_reset_control_get(&pdev->dev, NULL);
++ if (IS_ERR(priv->rst)) {
++ dev_err(&pdev->dev,
++ "missing or invalid reset controller entry");
++ ret = PTR_ERR(priv->rst);
++ goto err_put_adapter_dev;
++ }
++ reset_control_deassert(priv->rst);
++
++ ret = aspeed_peci_init_ctrl(priv);
++ if (ret)
++ goto err_put_adapter_dev;
++
++ ret = peci_add_adapter(priv->adapter);
++ if (ret)
++ goto err_put_adapter_dev;
++
++ dev_info(&pdev->dev, "peci bus %d registered, irq %d\n",
++ priv->adapter->nr, priv->irq);
++
++ return 0;
++
++err_put_adapter_dev:
++ put_device(&adapter->dev);
++ return ret;
++}
++
++static int aspeed_peci_remove(struct platform_device *pdev)
++{
++ struct aspeed_peci *priv = dev_get_drvdata(&pdev->dev);
++
++ clk_disable_unprepare(priv->clk);
++ reset_control_assert(priv->rst);
++ peci_del_adapter(priv->adapter);
++ of_node_put(priv->adapter->dev.of_node);
++
++ return 0;
++}
++
++static const struct of_device_id aspeed_peci_of_table[] = {
++ { .compatible = "aspeed,ast2400-peci", },
++ { .compatible = "aspeed,ast2500-peci", },
++ { }
++};
++MODULE_DEVICE_TABLE(of, aspeed_peci_of_table);
++
++static struct platform_driver aspeed_peci_driver = {
++ .probe = aspeed_peci_probe,
++ .remove = aspeed_peci_remove,
++ .driver = {
++ .name = "peci-aspeed",
++ .of_match_table = of_match_ptr(aspeed_peci_of_table),
++ },
++};
++module_platform_driver(aspeed_peci_driver);
++
++MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
++MODULE_DESCRIPTION("ASPEED PECI driver");
++MODULE_LICENSE("GPL v2");
+diff --git a/drivers/peci/busses/peci-npcm.c b/drivers/peci/busses/peci-npcm.c
+new file mode 100644
+index 000000000000..f632365b1416
+--- /dev/null
++++ b/drivers/peci/busses/peci-npcm.c
+@@ -0,0 +1,410 @@
++// SPDX-License-Identifier: GPL-2.0
++// Copyright (c) 2019 Nuvoton Technology corporation.
++
++#include <linux/bitfield.h>
++#include <linux/clk.h>
++#include <linux/interrupt.h>
++#include <linux/jiffies.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/peci.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/mfd/syscon.h>
++#include <linux/reset.h>
++
++/* NPCM7xx GCR module */
++#define NPCM7XX_INTCR3_OFFSET 0x9C
++#define NPCM7XX_INTCR3_PECIVSEL BIT(19)
++
++/* NPCM PECI Registers */
++#define NPCM_PECI_CTL_STS 0x00
++#define NPCM_PECI_RD_LENGTH 0x04
++#define NPCM_PECI_ADDR 0x08
++#define NPCM_PECI_CMD 0x0C
++#define NPCM_PECI_CTL2 0x10
++#define NPCM_PECI_WR_LENGTH 0x1C
++#define NPCM_PECI_PDDR 0x2C
++#define NPCM_PECI_DAT_INOUT(n) (0x100 + ((n) * 4))
++
++#define NPCM_PECI_MAX_REG 0x200
++
++/* NPCM_PECI_CTL_STS - 0x00 : Control Register */
++#define NPCM_PECI_CTRL_DONE_INT_EN BIT(6)
++#define NPCM_PECI_CTRL_ABRT_ERR BIT(4)
++#define NPCM_PECI_CTRL_CRC_ERR BIT(3)
++#define NPCM_PECI_CTRL_DONE BIT(1)
++#define NPCM_PECI_CTRL_START_BUSY BIT(0)
++
++/* NPCM_PECI_RD_LENGTH - 0x04 : Command Register */
++#define NPCM_PECI_RD_LEN_MASK GENMASK(6, 0)
++
++/* NPCM_PECI_CMD - 0x10 : Command Register */
++#define NPCM_PECI_CTL2_MASK GENMASK(7, 6)
++
++/* NPCM_PECI_WR_LENGTH - 0x1C : Command Register */
++#define NPCM_PECI_WR_LEN_MASK GENMASK(6, 0)
++
++/* NPCM_PECI_PDDR - 0x2C : Command Register */
++#define NPCM_PECI_PDDR_MASK GENMASK(4, 0)
++
++#define NPCM_PECI_INT_MASK (NPCM_PECI_CTRL_ABRT_ERR | \
++ NPCM_PECI_CTRL_CRC_ERR | \
++ NPCM_PECI_CTRL_DONE)
++
++#define NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC 50000
++#define NPCM_PECI_IDLE_CHECK_INTERVAL_USEC 10000
++#define NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT 1000
++#define NPCM_PECI_CMD_TIMEOUT_MS_MAX 60000
++#define NPCM_PECI_HOST_NEG_BIT_RATE_MAX 31
++#define NPCM_PECI_HOST_NEG_BIT_RATE_MIN 7
++#define NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT 15
++#define NPCM_PECI_PULL_DOWN_DEFAULT 0
++#define NPCM_PECI_PULL_DOWN_MAX 2
++
++struct npcm_peci {
++ u32 cmd_timeout_ms;
++ u32 host_bit_rate;
++ struct completion xfer_complete;
++ struct regmap *gcr_regmap;
++ struct peci_adapter *adapter;
++ struct regmap *regmap;
++ u32 status;
++ spinlock_t lock; /* to sync completion status handling */
++ struct device *dev;
++ struct clk *clk;
++ int irq;
++};
++
++static int npcm_peci_xfer_native(struct npcm_peci *priv,
++ struct peci_xfer_msg *msg)
++{
++ long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms);
++ unsigned long flags;
++ unsigned int msg_rd;
++ u32 cmd_sts;
++ int i, rc;
++
++ /* Check command sts and bus idle state */
++ rc = regmap_read_poll_timeout(priv->regmap, NPCM_PECI_CTL_STS, cmd_sts,
++ !(cmd_sts & NPCM_PECI_CTRL_START_BUSY),
++ NPCM_PECI_IDLE_CHECK_INTERVAL_USEC,
++ NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC);
++ if (rc)
++ return rc; /* -ETIMEDOUT */
++
++ spin_lock_irqsave(&priv->lock, flags);
++ reinit_completion(&priv->xfer_complete);
++
++ regmap_write(priv->regmap, NPCM_PECI_ADDR, msg->addr);
++ regmap_write(priv->regmap, NPCM_PECI_RD_LENGTH,
++ NPCM_PECI_WR_LEN_MASK & msg->rx_len);
++ regmap_write(priv->regmap, NPCM_PECI_WR_LENGTH,
++ NPCM_PECI_WR_LEN_MASK & msg->tx_len);
++
++ if (msg->tx_len) {
++ regmap_write(priv->regmap, NPCM_PECI_CMD, msg->tx_buf[0]);
++
++ for (i = 0; i < (msg->tx_len - 1); i++)
++ regmap_write(priv->regmap, NPCM_PECI_DAT_INOUT(i),
++ msg->tx_buf[i + 1]);
++ }
++
++ priv->status = 0;
++ regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS,
++ NPCM_PECI_CTRL_START_BUSY,
++ NPCM_PECI_CTRL_START_BUSY);
++
++ spin_unlock_irqrestore(&priv->lock, flags);
++
++ err = wait_for_completion_interruptible_timeout(&priv->xfer_complete,
++ timeout);
++
++ spin_lock_irqsave(&priv->lock, flags);
++
++ regmap_write(priv->regmap, NPCM_PECI_CMD, 0);
++
++ if (err <= 0 || priv->status != NPCM_PECI_CTRL_DONE) {
++ if (err < 0) { /* -ERESTARTSYS */
++ rc = (int)err;
++ goto err_irqrestore;
++ } else if (err == 0) {
++ dev_dbg(priv->dev, "Timeout waiting for a response!\n");
++ rc = -ETIMEDOUT;
++ goto err_irqrestore;
++ }
++
++ dev_dbg(priv->dev, "No valid response!\n");
++ rc = -EIO;
++ goto err_irqrestore;
++ }
++
++ for (i = 0; i < msg->rx_len; i++) {
++ regmap_read(priv->regmap, NPCM_PECI_DAT_INOUT(i), &msg_rd);
++ msg->rx_buf[i] = (u8)msg_rd;
++ }
++
++err_irqrestore:
++ spin_unlock_irqrestore(&priv->lock, flags);
++ return rc;
++}
++
++static irqreturn_t npcm_peci_irq_handler(int irq, void *arg)
++{
++ struct npcm_peci *priv = arg;
++ u32 status_ack = 0;
++ u32 status;
++
++ spin_lock(&priv->lock);
++ regmap_read(priv->regmap, NPCM_PECI_CTL_STS, &status);
++ priv->status |= (status & NPCM_PECI_INT_MASK);
++
++ if (status & NPCM_PECI_CTRL_CRC_ERR) {
++ dev_dbg(priv->dev, "PECI_INT_W_FCS_BAD\n");
++ status_ack |= NPCM_PECI_CTRL_CRC_ERR;
++ }
++
++ if (status & NPCM_PECI_CTRL_ABRT_ERR) {
++ dev_dbg(priv->dev, "NPCM_PECI_CTRL_ABRT_ERR\n");
++ status_ack |= NPCM_PECI_CTRL_ABRT_ERR;
++ }
++
++ /*
++ * All commands should be ended up with a NPCM_PECI_CTRL_DONE
++ * bit set even in an error case.
++ */
++ if (status & NPCM_PECI_CTRL_DONE) {
++ dev_dbg(priv->dev, "NPCM_PECI_CTRL_DONE\n");
++ status_ack |= NPCM_PECI_CTRL_DONE;
++ complete(&priv->xfer_complete);
++ }
++
++ regmap_write_bits(priv->regmap, NPCM_PECI_CTL_STS,
++ NPCM_PECI_INT_MASK, status_ack);
++
++ spin_unlock(&priv->lock);
++ return IRQ_HANDLED;
++}
++
++static int npcm_peci_init_ctrl(struct npcm_peci *priv)
++{
++ u32 cmd_sts, host_neg_bit_rate = 0, pull_down = 0;
++ int ret;
++ bool volt;
++
++ priv->clk = devm_clk_get(priv->dev, NULL);
++ if (IS_ERR(priv->clk)) {
++ dev_err(priv->dev, "Failed to get clk source.\n");
++ return PTR_ERR(priv->clk);
++ }
++
++ ret = clk_prepare_enable(priv->clk);
++ if (ret) {
++ dev_err(priv->dev, "Failed to enable clock.\n");
++ return ret;
++ }
++
++ ret = of_property_read_u32(priv->dev->of_node, "cmd-timeout-ms",
++ &priv->cmd_timeout_ms);
++ if (ret || priv->cmd_timeout_ms > NPCM_PECI_CMD_TIMEOUT_MS_MAX ||
++ priv->cmd_timeout_ms == 0) {
++ if (ret)
++ dev_warn(priv->dev,
++ "cmd-timeout-ms not found, use default : %u\n",
++ NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT);
++ else
++ dev_warn(priv->dev,
++ "Invalid cmd-timeout-ms : %u. Use default : %u\n",
++ priv->cmd_timeout_ms,
++ NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT);
++
++ priv->cmd_timeout_ms = NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT;
++ }
++
++ if (of_device_is_compatible(priv->dev->of_node,
++ "nuvoton,npcm750-peci")) {
++ priv->gcr_regmap = syscon_regmap_lookup_by_compatible
++ ("nuvoton,npcm750-gcr");
++ if (!IS_ERR(priv->gcr_regmap)) {
++ volt = of_property_read_bool(priv->dev->of_node,
++ "high-volt-range");
++ if (volt)
++ regmap_update_bits(priv->gcr_regmap,
++ NPCM7XX_INTCR3_OFFSET,
++ NPCM7XX_INTCR3_PECIVSEL,
++ NPCM7XX_INTCR3_PECIVSEL);
++ else
++ regmap_update_bits(priv->gcr_regmap,
++ NPCM7XX_INTCR3_OFFSET,
++ NPCM7XX_INTCR3_PECIVSEL, 0);
++ }
++ }
++
++ ret = of_property_read_u32(priv->dev->of_node, "pull-down",
++ &pull_down);
++ if (ret || pull_down > NPCM_PECI_PULL_DOWN_MAX) {
++ if (ret)
++ dev_warn(priv->dev,
++ "pull-down not found, use default : %u\n",
++ NPCM_PECI_PULL_DOWN_DEFAULT);
++ else
++ dev_warn(priv->dev,
++ "Invalid pull-down : %u. Use default : %u\n",
++ pull_down,
++ NPCM_PECI_PULL_DOWN_DEFAULT);
++ pull_down = NPCM_PECI_PULL_DOWN_DEFAULT;
++ }
++
++ regmap_update_bits(priv->regmap, NPCM_PECI_CTL2, NPCM_PECI_CTL2_MASK,
++ pull_down << 6);
++
++ ret = of_property_read_u32(priv->dev->of_node, "host-neg-bit-rate",
++ &host_neg_bit_rate);
++ if (ret || host_neg_bit_rate > NPCM_PECI_HOST_NEG_BIT_RATE_MAX ||
++ host_neg_bit_rate < NPCM_PECI_HOST_NEG_BIT_RATE_MIN) {
++ if (ret)
++ dev_warn(priv->dev,
++ "host-neg-bit-rate not found, use default : %u\n",
++ NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT);
++ else
++ dev_warn(priv->dev,
++ "Invalid host-neg-bit-rate : %u. Use default : %u\n",
++ host_neg_bit_rate,
++ NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT);
++ host_neg_bit_rate = NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT;
++ }
++
++ regmap_update_bits(priv->regmap, NPCM_PECI_PDDR, NPCM_PECI_PDDR_MASK,
++ host_neg_bit_rate);
++
++ priv->host_bit_rate = clk_get_rate(priv->clk) /
++ (4 * (host_neg_bit_rate + 1));
++
++ ret = regmap_read_poll_timeout(priv->regmap, NPCM_PECI_CTL_STS, cmd_sts,
++ !(cmd_sts & NPCM_PECI_CTRL_START_BUSY),
++ NPCM_PECI_IDLE_CHECK_INTERVAL_USEC,
++ NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC);
++ if (ret)
++ return ret; /* -ETIMEDOUT */
++
++ /* PECI interrupt enable */
++ regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS,
++ NPCM_PECI_CTRL_DONE_INT_EN,
++ NPCM_PECI_CTRL_DONE_INT_EN);
++
++ return 0;
++}
++
++static const struct regmap_config npcm_peci_regmap_config = {
++ .reg_bits = 8,
++ .val_bits = 8,
++ .max_register = NPCM_PECI_MAX_REG,
++ .fast_io = true,
++};
++
++static int npcm_peci_xfer(struct peci_adapter *adapter,
++ struct peci_xfer_msg *msg)
++{
++ struct npcm_peci *priv = peci_get_adapdata(adapter);
++
++ return npcm_peci_xfer_native(priv, msg);
++}
++
++static int npcm_peci_probe(struct platform_device *pdev)
++{
++ struct peci_adapter *adapter;
++ struct npcm_peci *priv;
++ struct resource *res;
++ void __iomem *base;
++ int ret;
++
++ adapter = peci_alloc_adapter(&pdev->dev, sizeof(*priv));
++ if (!adapter)
++ return -ENOMEM;
++
++ priv = peci_get_adapdata(adapter);
++ priv->adapter = adapter;
++ priv->dev = &pdev->dev;
++ dev_set_drvdata(&pdev->dev, priv);
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(base)) {
++ ret = PTR_ERR(base);
++ goto err_put_adapter_dev;
++ }
++
++ priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
++ &npcm_peci_regmap_config);
++ if (IS_ERR(priv->regmap)) {
++ ret = PTR_ERR(priv->regmap);
++ goto err_put_adapter_dev;
++ }
++
++ priv->irq = platform_get_irq(pdev, 0);
++ if (!priv->irq) {
++ ret = -ENODEV;
++ goto err_put_adapter_dev;
++ }
++
++ ret = devm_request_irq(&pdev->dev, priv->irq, npcm_peci_irq_handler,
++ 0, "peci-npcm-irq", priv);
++ if (ret)
++ goto err_put_adapter_dev;
++
++ init_completion(&priv->xfer_complete);
++ spin_lock_init(&priv->lock);
++
++ priv->adapter->owner = THIS_MODULE;
++ priv->adapter->dev.of_node = of_node_get(dev_of_node(priv->dev));
++ strlcpy(priv->adapter->name, pdev->name, sizeof(priv->adapter->name));
++ priv->adapter->xfer = npcm_peci_xfer;
++
++ ret = npcm_peci_init_ctrl(priv);
++ if (ret)
++ goto err_put_adapter_dev;
++
++ ret = peci_add_adapter(priv->adapter);
++ if (ret)
++ goto err_put_adapter_dev;
++
++ dev_info(&pdev->dev, "peci bus %d registered, host negotiation bit rate %dHz",
++ priv->adapter->nr, priv->host_bit_rate);
++
++ return 0;
++
++err_put_adapter_dev:
++ put_device(&adapter->dev);
++ return ret;
++}
++
++static int npcm_peci_remove(struct platform_device *pdev)
++{
++ struct npcm_peci *priv = dev_get_drvdata(&pdev->dev);
++
++ clk_disable_unprepare(priv->clk);
++ peci_del_adapter(priv->adapter);
++ of_node_put(priv->adapter->dev.of_node);
++
++ return 0;
++}
++
++static const struct of_device_id npcm_peci_of_table[] = {
++ { .compatible = "nuvoton,npcm750-peci", },
++ { }
++};
++MODULE_DEVICE_TABLE(of, npcm_peci_of_table);
++
++static struct platform_driver npcm_peci_driver = {
++ .probe = npcm_peci_probe,
++ .remove = npcm_peci_remove,
++ .driver = {
++ .name = "peci-npcm",
++ .of_match_table = of_match_ptr(npcm_peci_of_table),
++ },
++};
++module_platform_driver(npcm_peci_driver);
++
++MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
++MODULE_DESCRIPTION("NPCM Platform Environment Control Interface (PECI) driver");
++MODULE_LICENSE("GPL v2");
+diff --git a/drivers/peci/peci-aspeed.c b/drivers/peci/peci-aspeed.c
+deleted file mode 100644
+index 51cb2563ceb6..000000000000
+--- a/drivers/peci/peci-aspeed.c
++++ /dev/null
+@@ -1,505 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-// Copyright (C) 2012-2017 ASPEED Technology Inc.
+-// Copyright (c) 2018 Intel Corporation
+-
+-#include <linux/bitfield.h>
+-#include <linux/clk.h>
+-#include <linux/interrupt.h>
+-#include <linux/jiffies.h>
+-#include <linux/module.h>
+-#include <linux/of.h>
+-#include <linux/peci.h>
+-#include <linux/platform_device.h>
+-#include <linux/regmap.h>
+-#include <linux/reset.h>
+-
+-/* ASPEED PECI Registers */
+-#define ASPEED_PECI_CTRL 0x00
+-#define ASPEED_PECI_TIMING 0x04
+-#define ASPEED_PECI_CMD 0x08
+-#define ASPEED_PECI_CMD_CTRL 0x0c
+-#define ASPEED_PECI_EXP_FCS 0x10
+-#define ASPEED_PECI_CAP_FCS 0x14
+-#define ASPEED_PECI_INT_CTRL 0x18
+-#define ASPEED_PECI_INT_STS 0x1c
+-#define ASPEED_PECI_W_DATA0 0x20
+-#define ASPEED_PECI_W_DATA1 0x24
+-#define ASPEED_PECI_W_DATA2 0x28
+-#define ASPEED_PECI_W_DATA3 0x2c
+-#define ASPEED_PECI_R_DATA0 0x30
+-#define ASPEED_PECI_R_DATA1 0x34
+-#define ASPEED_PECI_R_DATA2 0x38
+-#define ASPEED_PECI_R_DATA3 0x3c
+-#define ASPEED_PECI_W_DATA4 0x40
+-#define ASPEED_PECI_W_DATA5 0x44
+-#define ASPEED_PECI_W_DATA6 0x48
+-#define ASPEED_PECI_W_DATA7 0x4c
+-#define ASPEED_PECI_R_DATA4 0x50
+-#define ASPEED_PECI_R_DATA5 0x54
+-#define ASPEED_PECI_R_DATA6 0x58
+-#define ASPEED_PECI_R_DATA7 0x5c
+-
+-/* ASPEED_PECI_CTRL - 0x00 : Control Register */
+-#define PECI_CTRL_SAMPLING_MASK GENMASK(19, 16)
+-#define PECI_CTRL_READ_MODE_MASK GENMASK(13, 12)
+-#define PECI_CTRL_READ_MODE_COUNT BIT(12)
+-#define PECI_CTRL_READ_MODE_DBG BIT(13)
+-#define PECI_CTRL_CLK_SOURCE_MASK BIT(11)
+-#define PECI_CTRL_CLK_DIV_MASK GENMASK(10, 8)
+-#define PECI_CTRL_INVERT_OUT BIT(7)
+-#define PECI_CTRL_INVERT_IN BIT(6)
+-#define PECI_CTRL_BUS_CONTENT_EN BIT(5)
+-#define PECI_CTRL_PECI_EN BIT(4)
+-#define PECI_CTRL_PECI_CLK_EN BIT(0)
+-
+-/* ASPEED_PECI_TIMING - 0x04 : Timing Negotiation Register */
+-#define PECI_TIMING_MESSAGE_MASK GENMASK(15, 8)
+-#define PECI_TIMING_ADDRESS_MASK GENMASK(7, 0)
+-
+-/* ASPEED_PECI_CMD - 0x08 : Command Register */
+-#define PECI_CMD_PIN_MON BIT(31)
+-#define PECI_CMD_STS_MASK GENMASK(27, 24)
+-#define PECI_CMD_IDLE_MASK (PECI_CMD_STS_MASK | PECI_CMD_PIN_MON)
+-#define PECI_CMD_FIRE BIT(0)
+-
+-/* ASPEED_PECI_LEN - 0x0C : Read/Write Length Register */
+-#define PECI_AW_FCS_EN BIT(31)
+-#define PECI_READ_LEN_MASK GENMASK(23, 16)
+-#define PECI_WRITE_LEN_MASK GENMASK(15, 8)
+-#define PECI_TAGET_ADDR_MASK GENMASK(7, 0)
+-
+-/* ASPEED_PECI_EXP_FCS - 0x10 : Expected FCS Data Register */
+-#define PECI_EXPECT_READ_FCS_MASK GENMASK(23, 16)
+-#define PECI_EXPECT_AW_FCS_AUTO_MASK GENMASK(15, 8)
+-#define PECI_EXPECT_WRITE_FCS_MASK GENMASK(7, 0)
+-
+-/* ASPEED_PECI_CAP_FCS - 0x14 : Captured FCS Data Register */
+-#define PECI_CAPTURE_READ_FCS_MASK GENMASK(23, 16)
+-#define PECI_CAPTURE_WRITE_FCS_MASK GENMASK(7, 0)
+-
+-/* ASPEED_PECI_INT_CTRL/STS - 0x18/0x1c : Interrupt Register */
+-#define PECI_INT_TIMING_RESULT_MASK GENMASK(31, 30)
+-#define PECI_INT_TIMEOUT BIT(4)
+-#define PECI_INT_CONNECT BIT(3)
+-#define PECI_INT_W_FCS_BAD BIT(2)
+-#define PECI_INT_W_FCS_ABORT BIT(1)
+-#define PECI_INT_CMD_DONE BIT(0)
+-
+-#define PECI_INT_MASK (PECI_INT_TIMEOUT | PECI_INT_CONNECT | \
+- PECI_INT_W_FCS_BAD | PECI_INT_W_FCS_ABORT | \
+- PECI_INT_CMD_DONE)
+-
+-#define PECI_IDLE_CHECK_TIMEOUT_USEC 50000
+-#define PECI_IDLE_CHECK_INTERVAL_USEC 10000
+-
+-#define PECI_RD_SAMPLING_POINT_DEFAULT 8
+-#define PECI_RD_SAMPLING_POINT_MAX 15
+-#define PECI_CLK_DIV_DEFAULT 0
+-#define PECI_CLK_DIV_MAX 7
+-#define PECI_MSG_TIMING_DEFAULT 1
+-#define PECI_MSG_TIMING_MAX 255
+-#define PECI_ADDR_TIMING_DEFAULT 1
+-#define PECI_ADDR_TIMING_MAX 255
+-#define PECI_CMD_TIMEOUT_MS_DEFAULT 1000
+-#define PECI_CMD_TIMEOUT_MS_MAX 60000
+-
+-struct aspeed_peci {
+- struct peci_adapter *adapter;
+- struct device *dev;
+- struct regmap *regmap;
+- struct clk *clk;
+- struct reset_control *rst;
+- int irq;
+- spinlock_t lock; /* to sync completion status handling */
+- struct completion xfer_complete;
+- u32 status;
+- u32 cmd_timeout_ms;
+-};
+-
+-static int aspeed_peci_xfer_native(struct aspeed_peci *priv,
+- struct peci_xfer_msg *msg)
+-{
+- long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms);
+- u32 peci_head, peci_state, rx_data, cmd_sts;
+- unsigned long flags;
+- int i, rc;
+- uint reg;
+-
+- /* Check command sts and bus idle state */
+- rc = regmap_read_poll_timeout(priv->regmap, ASPEED_PECI_CMD, cmd_sts,
+- !(cmd_sts & PECI_CMD_IDLE_MASK),
+- PECI_IDLE_CHECK_INTERVAL_USEC,
+- PECI_IDLE_CHECK_TIMEOUT_USEC);
+- if (rc)
+- return rc; /* -ETIMEDOUT */
+-
+- spin_lock_irqsave(&priv->lock, flags);
+- reinit_completion(&priv->xfer_complete);
+-
+- peci_head = FIELD_PREP(PECI_TAGET_ADDR_MASK, msg->addr) |
+- FIELD_PREP(PECI_WRITE_LEN_MASK, msg->tx_len) |
+- FIELD_PREP(PECI_READ_LEN_MASK, msg->rx_len);
+-
+- regmap_write(priv->regmap, ASPEED_PECI_CMD_CTRL, peci_head);
+-
+- for (i = 0; i < msg->tx_len; i += 4) {
+- reg = i < 16 ? ASPEED_PECI_W_DATA0 + i % 16 :
+- ASPEED_PECI_W_DATA4 + i % 16;
+- regmap_write(priv->regmap, reg,
+- le32_to_cpup((__le32 *)&msg->tx_buf[i]));
+- }
+-
+- dev_dbg(priv->dev, "HEAD : 0x%08x\n", peci_head);
+- print_hex_dump_debug("TX : ", DUMP_PREFIX_NONE, 16, 1,
+- msg->tx_buf, msg->tx_len, true);
+-
+- priv->status = 0;
+- regmap_write(priv->regmap, ASPEED_PECI_CMD, PECI_CMD_FIRE);
+- spin_unlock_irqrestore(&priv->lock, flags);
+-
+- err = wait_for_completion_interruptible_timeout(&priv->xfer_complete,
+- timeout);
+-
+- spin_lock_irqsave(&priv->lock, flags);
+- dev_dbg(priv->dev, "INT_STS : 0x%08x\n", priv->status);
+- regmap_read(priv->regmap, ASPEED_PECI_CMD, &peci_state);
+- dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n",
+- FIELD_GET(PECI_CMD_STS_MASK, peci_state));
+-
+- regmap_write(priv->regmap, ASPEED_PECI_CMD, 0);
+-
+- if (err <= 0 || priv->status != PECI_INT_CMD_DONE) {
+- if (err < 0) { /* -ERESTARTSYS */
+- rc = (int)err;
+- goto err_irqrestore;
+- } else if (err == 0) {
+- dev_dbg(priv->dev, "Timeout waiting for a response!\n");
+- rc = -ETIMEDOUT;
+- goto err_irqrestore;
+- }
+-
+- dev_dbg(priv->dev, "No valid response!\n");
+- rc = -EIO;
+- goto err_irqrestore;
+- }
+-
+- /**
+- * Note that rx_len and rx_buf size can be an odd number.
+- * Byte handling is more efficient.
+- */
+- for (i = 0; i < msg->rx_len; i++) {
+- u8 byte_offset = i % 4;
+-
+- if (byte_offset == 0) {
+- reg = i < 16 ? ASPEED_PECI_R_DATA0 + i % 16 :
+- ASPEED_PECI_R_DATA4 + i % 16;
+- regmap_read(priv->regmap, reg, &rx_data);
+- }
+-
+- msg->rx_buf[i] = (u8)(rx_data >> (byte_offset << 3));
+- }
+-
+- print_hex_dump_debug("RX : ", DUMP_PREFIX_NONE, 16, 1,
+- msg->rx_buf, msg->rx_len, true);
+-
+- regmap_read(priv->regmap, ASPEED_PECI_CMD, &peci_state);
+- dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n",
+- FIELD_GET(PECI_CMD_STS_MASK, peci_state));
+- dev_dbg(priv->dev, "------------------------\n");
+-
+-err_irqrestore:
+- spin_unlock_irqrestore(&priv->lock, flags);
+- return rc;
+-}
+-
+-static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg)
+-{
+- struct aspeed_peci *priv = arg;
+- u32 status_ack = 0;
+- u32 status;
+-
+- spin_lock(&priv->lock);
+- regmap_read(priv->regmap, ASPEED_PECI_INT_STS, &status);
+- priv->status |= (status & PECI_INT_MASK);
+-
+- /**
+- * In most cases, interrupt bits will be set one by one but also note
+- * that multiple interrupt bits could be set at the same time.
+- */
+- if (status & PECI_INT_TIMEOUT) {
+- dev_dbg(priv->dev, "PECI_INT_TIMEOUT\n");
+- status_ack |= PECI_INT_TIMEOUT;
+- }
+-
+- if (status & PECI_INT_CONNECT) {
+- dev_dbg(priv->dev, "PECI_INT_CONNECT\n");
+- status_ack |= PECI_INT_CONNECT;
+- }
+-
+- if (status & PECI_INT_W_FCS_BAD) {
+- dev_dbg(priv->dev, "PECI_INT_W_FCS_BAD\n");
+- status_ack |= PECI_INT_W_FCS_BAD;
+- }
+-
+- if (status & PECI_INT_W_FCS_ABORT) {
+- dev_dbg(priv->dev, "PECI_INT_W_FCS_ABORT\n");
+- status_ack |= PECI_INT_W_FCS_ABORT;
+- }
+-
+- /**
+- * All commands should be ended up with a PECI_INT_CMD_DONE bit set
+- * even in an error case.
+- */
+- if (status & PECI_INT_CMD_DONE) {
+- dev_dbg(priv->dev, "PECI_INT_CMD_DONE\n");
+- status_ack |= PECI_INT_CMD_DONE;
+- complete(&priv->xfer_complete);
+- }
+-
+- regmap_write(priv->regmap, ASPEED_PECI_INT_STS, status_ack);
+- spin_unlock(&priv->lock);
+- return IRQ_HANDLED;
+-}
+-
+-static int aspeed_peci_init_ctrl(struct aspeed_peci *priv)
+-{
+- u32 msg_timing, addr_timing, rd_sampling_point;
+- u32 clk_freq, clk_divisor, clk_div_val = 0;
+- int ret;
+-
+- priv->clk = devm_clk_get(priv->dev, NULL);
+- if (IS_ERR(priv->clk)) {
+- dev_err(priv->dev, "Failed to get clk source.\n");
+- return PTR_ERR(priv->clk);
+- }
+-
+- ret = clk_prepare_enable(priv->clk);
+- if (ret) {
+- dev_err(priv->dev, "Failed to enable clock.\n");
+- return ret;
+- }
+-
+- ret = of_property_read_u32(priv->dev->of_node, "clock-frequency",
+- &clk_freq);
+- if (ret) {
+- dev_err(priv->dev,
+- "Could not read clock-frequency property.\n");
+- clk_disable_unprepare(priv->clk);
+- return ret;
+- }
+-
+- clk_divisor = clk_get_rate(priv->clk) / clk_freq;
+-
+- while ((clk_divisor >> 1) && (clk_div_val < PECI_CLK_DIV_MAX))
+- clk_div_val++;
+-
+- ret = of_property_read_u32(priv->dev->of_node, "msg-timing",
+- &msg_timing);
+- if (ret || msg_timing > PECI_MSG_TIMING_MAX) {
+- if (!ret)
+- dev_warn(priv->dev,
+- "Invalid msg-timing : %u, Use default : %u\n",
+- msg_timing, PECI_MSG_TIMING_DEFAULT);
+- msg_timing = PECI_MSG_TIMING_DEFAULT;
+- }
+-
+- ret = of_property_read_u32(priv->dev->of_node, "addr-timing",
+- &addr_timing);
+- if (ret || addr_timing > PECI_ADDR_TIMING_MAX) {
+- if (!ret)
+- dev_warn(priv->dev,
+- "Invalid addr-timing : %u, Use default : %u\n",
+- addr_timing, PECI_ADDR_TIMING_DEFAULT);
+- addr_timing = PECI_ADDR_TIMING_DEFAULT;
+- }
+-
+- ret = of_property_read_u32(priv->dev->of_node, "rd-sampling-point",
+- &rd_sampling_point);
+- if (ret || rd_sampling_point > PECI_RD_SAMPLING_POINT_MAX) {
+- if (!ret)
+- dev_warn(priv->dev,
+- "Invalid rd-sampling-point : %u. Use default : %u\n",
+- rd_sampling_point,
+- PECI_RD_SAMPLING_POINT_DEFAULT);
+- rd_sampling_point = PECI_RD_SAMPLING_POINT_DEFAULT;
+- }
+-
+- ret = of_property_read_u32(priv->dev->of_node, "cmd-timeout-ms",
+- &priv->cmd_timeout_ms);
+- if (ret || priv->cmd_timeout_ms > PECI_CMD_TIMEOUT_MS_MAX ||
+- priv->cmd_timeout_ms == 0) {
+- if (!ret)
+- dev_warn(priv->dev,
+- "Invalid cmd-timeout-ms : %u. Use default : %u\n",
+- priv->cmd_timeout_ms,
+- PECI_CMD_TIMEOUT_MS_DEFAULT);
+- priv->cmd_timeout_ms = PECI_CMD_TIMEOUT_MS_DEFAULT;
+- }
+-
+- regmap_write(priv->regmap, ASPEED_PECI_CTRL,
+- FIELD_PREP(PECI_CTRL_CLK_DIV_MASK, PECI_CLK_DIV_DEFAULT) |
+- PECI_CTRL_PECI_CLK_EN);
+-
+- /**
+- * Timing negotiation period setting.
+- * The unit of the programmed value is 4 times of PECI clock period.
+- */
+- regmap_write(priv->regmap, ASPEED_PECI_TIMING,
+- FIELD_PREP(PECI_TIMING_MESSAGE_MASK, msg_timing) |
+- FIELD_PREP(PECI_TIMING_ADDRESS_MASK, addr_timing));
+-
+- /* Clear interrupts */
+- regmap_write(priv->regmap, ASPEED_PECI_INT_STS, PECI_INT_MASK);
+-
+- /* Enable interrupts */
+- regmap_write(priv->regmap, ASPEED_PECI_INT_CTRL, PECI_INT_MASK);
+-
+- /* Read sampling point and clock speed setting */
+- regmap_write(priv->regmap, ASPEED_PECI_CTRL,
+- FIELD_PREP(PECI_CTRL_SAMPLING_MASK, rd_sampling_point) |
+- FIELD_PREP(PECI_CTRL_CLK_DIV_MASK, clk_div_val) |
+- PECI_CTRL_PECI_EN | PECI_CTRL_PECI_CLK_EN);
+-
+- return 0;
+-}
+-
+-static const struct regmap_config aspeed_peci_regmap_config = {
+- .reg_bits = 32,
+- .val_bits = 32,
+- .reg_stride = 4,
+- .max_register = ASPEED_PECI_R_DATA7,
+- .val_format_endian = REGMAP_ENDIAN_LITTLE,
+- .fast_io = true,
+-};
+-
+-static int aspeed_peci_xfer(struct peci_adapter *adapter,
+- struct peci_xfer_msg *msg)
+-{
+- struct aspeed_peci *priv = peci_get_adapdata(adapter);
+-
+- return aspeed_peci_xfer_native(priv, msg);
+-}
+-
+-static int aspeed_peci_probe(struct platform_device *pdev)
+-{
+- struct peci_adapter *adapter;
+- struct aspeed_peci *priv;
+- struct resource *res;
+- void __iomem *base;
+- u32 cmd_sts;
+- int ret;
+-
+- adapter = peci_alloc_adapter(&pdev->dev, sizeof(*priv));
+- if (!adapter)
+- return -ENOMEM;
+-
+- priv = peci_get_adapdata(adapter);
+- priv->adapter = adapter;
+- priv->dev = &pdev->dev;
+- dev_set_drvdata(&pdev->dev, priv);
+-
+- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+- base = devm_ioremap_resource(&pdev->dev, res);
+- if (IS_ERR(base)) {
+- ret = PTR_ERR(base);
+- goto err_put_adapter_dev;
+- }
+-
+- priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+- &aspeed_peci_regmap_config);
+- if (IS_ERR(priv->regmap)) {
+- ret = PTR_ERR(priv->regmap);
+- goto err_put_adapter_dev;
+- }
+-
+- /**
+- * We check that the regmap works on this very first access,
+- * but as this is an MMIO-backed regmap, subsequent regmap
+- * access is not going to fail and we skip error checks from
+- * this point.
+- */
+- ret = regmap_read(priv->regmap, ASPEED_PECI_CMD, &cmd_sts);
+- if (ret) {
+- ret = -EIO;
+- goto err_put_adapter_dev;
+- }
+-
+- priv->irq = platform_get_irq(pdev, 0);
+- if (!priv->irq) {
+- ret = -ENODEV;
+- goto err_put_adapter_dev;
+- }
+-
+- ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_peci_irq_handler,
+- 0, "peci-aspeed-irq", priv);
+- if (ret)
+- goto err_put_adapter_dev;
+-
+- init_completion(&priv->xfer_complete);
+- spin_lock_init(&priv->lock);
+-
+- priv->adapter->owner = THIS_MODULE;
+- priv->adapter->dev.of_node = of_node_get(dev_of_node(priv->dev));
+- strlcpy(priv->adapter->name, pdev->name, sizeof(priv->adapter->name));
+- priv->adapter->xfer = aspeed_peci_xfer;
+-
+- priv->rst = devm_reset_control_get(&pdev->dev, NULL);
+- if (IS_ERR(priv->rst)) {
+- dev_err(&pdev->dev,
+- "missing or invalid reset controller entry");
+- ret = PTR_ERR(priv->rst);
+- goto err_put_adapter_dev;
+- }
+- reset_control_deassert(priv->rst);
+-
+- ret = aspeed_peci_init_ctrl(priv);
+- if (ret)
+- goto err_put_adapter_dev;
+-
+- ret = peci_add_adapter(priv->adapter);
+- if (ret)
+- goto err_put_adapter_dev;
+-
+- dev_info(&pdev->dev, "peci bus %d registered, irq %d\n",
+- priv->adapter->nr, priv->irq);
+-
+- return 0;
+-
+-err_put_adapter_dev:
+- put_device(&adapter->dev);
+- return ret;
+-}
+-
+-static int aspeed_peci_remove(struct platform_device *pdev)
+-{
+- struct aspeed_peci *priv = dev_get_drvdata(&pdev->dev);
+-
+- clk_disable_unprepare(priv->clk);
+- reset_control_assert(priv->rst);
+- peci_del_adapter(priv->adapter);
+- of_node_put(priv->adapter->dev.of_node);
+-
+- return 0;
+-}
+-
+-static const struct of_device_id aspeed_peci_of_table[] = {
+- { .compatible = "aspeed,ast2400-peci", },
+- { .compatible = "aspeed,ast2500-peci", },
+- { }
+-};
+-MODULE_DEVICE_TABLE(of, aspeed_peci_of_table);
+-
+-static struct platform_driver aspeed_peci_driver = {
+- .probe = aspeed_peci_probe,
+- .remove = aspeed_peci_remove,
+- .driver = {
+- .name = "peci-aspeed",
+- .of_match_table = of_match_ptr(aspeed_peci_of_table),
+- },
+-};
+-module_platform_driver(aspeed_peci_driver);
+-
+-MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+-MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+-MODULE_DESCRIPTION("ASPEED PECI driver");
+-MODULE_LICENSE("GPL v2");
+diff --git a/drivers/peci/peci-core.c b/drivers/peci/peci-core.c
+index 6f241469ec7e..d1f0df8b139a 100644
+--- a/drivers/peci/peci-core.c
++++ b/drivers/peci/peci-core.c
+@@ -1,38 +1,31 @@
+ // SPDX-License-Identifier: GPL-2.0
+-// Copyright (c) 2018 Intel Corporation
++// Copyright (c) 2018-2019 Intel Corporation
+
+ #include <linux/bitfield.h>
+ #include <linux/crc8.h>
+ #include <linux/delay.h>
+-#include <linux/fs.h>
++#include <linux/mm.h>
+ #include <linux/module.h>
+ #include <linux/of_device.h>
+ #include <linux/peci.h>
+ #include <linux/pm_domain.h>
+ #include <linux/pm_runtime.h>
++#include <linux/sched/task_stack.h>
+ #include <linux/slab.h>
+-#include <linux/uaccess.h>
+
+ /* Mask for getting minor revision number from DIB */
+ #define REVISION_NUM_MASK GENMASK(15, 8)
+
+-/* CRC8 table for Assure Write Frame Check */
++/* CRC8 table for Assured Write Frame Check */
+ #define PECI_CRC8_POLYNOMIAL 0x07
+ DECLARE_CRC8_TABLE(peci_crc8_table);
+
+-static struct device_type peci_adapter_type;
+-static struct device_type peci_client_type;
+-
+-/* Max number of peci cdev */
+-#define PECI_CDEV_MAX 16
+-
+-static dev_t peci_devt;
+ static bool is_registered;
+
+ static DEFINE_MUTEX(core_lock);
+ static DEFINE_IDR(peci_adapter_idr);
+
+-static struct peci_adapter *peci_get_adapter(int nr)
++struct peci_adapter *peci_get_adapter(int nr)
+ {
+ struct peci_adapter *adapter;
+
+@@ -48,10 +41,12 @@ static struct peci_adapter *peci_get_adapter(int nr)
+
+ out_unlock:
+ mutex_unlock(&core_lock);
++
+ return adapter;
+ }
++EXPORT_SYMBOL_GPL(peci_get_adapter);
+
+-static void peci_put_adapter(struct peci_adapter *adapter)
++void peci_put_adapter(struct peci_adapter *adapter)
+ {
+ if (!adapter)
+ return;
+@@ -59,6 +54,7 @@ static void peci_put_adapter(struct peci_adapter *adapter)
+ put_device(&adapter->dev);
+ module_put(adapter->owner);
+ }
++EXPORT_SYMBOL_GPL(peci_put_adapter);
+
+ static ssize_t name_show(struct device *dev,
+ struct device_attribute *attr,
+@@ -84,10 +80,11 @@ static struct attribute *peci_device_attrs[] = {
+ };
+ ATTRIBUTE_GROUPS(peci_device);
+
+-static struct device_type peci_client_type = {
++struct device_type peci_client_type = {
+ .groups = peci_device_groups,
+ .release = peci_client_dev_release,
+ };
++EXPORT_SYMBOL_GPL(peci_client_type);
+
+ /**
+ * peci_verify_client - return parameter as peci_client, or NULL
+@@ -103,19 +100,120 @@ struct peci_client *peci_verify_client(struct device *dev)
+ }
+ EXPORT_SYMBOL_GPL(peci_verify_client);
+
+-static u8 peci_aw_fcs(u8 *data, int len)
++/**
++ * peci_get_xfer_msg() - get a DMA safe peci_xfer_msg for the given tx and rx
++ * length
++ * @tx_len: the length of tx_buf. May be 0 if tx_buf isn't needed.
++ * @rx_len: the length of rx_buf. May be 0 if rx_buf isn't needed.
++ *
++ * Return: NULL if a DMA safe buffer was not obtained.
++ * Or a valid pointer to be used with DMA. After use, release it by
++ * calling peci_put_xfer_msg().
++ *
++ * This function must only be called from process context!
++ */
++struct peci_xfer_msg *peci_get_xfer_msg(u8 tx_len, u8 rx_len)
+ {
+- return crc8(peci_crc8_table, data, (size_t)len, 0);
++ struct peci_xfer_msg *msg;
++ u8 *tx_buf, *rx_buf;
++
++ if (tx_len) {
++ tx_buf = kzalloc(tx_len, GFP_KERNEL);
++ if (!tx_buf)
++ return NULL;
++ } else {
++ tx_buf = NULL;
++ }
++
++ if (rx_len) {
++ rx_buf = kzalloc(rx_len, GFP_KERNEL);
++ if (!rx_buf)
++ goto err_free_tx_buf;
++ } else {
++ rx_buf = NULL;
++ }
++
++ msg = kzalloc(sizeof(struct peci_xfer_msg), GFP_KERNEL);
++ if (!msg)
++ goto err_free_tx_rx_buf;
++
++ msg->tx_len = tx_len;
++ msg->tx_buf = tx_buf;
++ msg->rx_len = rx_len;
++ msg->rx_buf = rx_buf;
++
++ return msg;
++
++err_free_tx_rx_buf:
++ kfree(rx_buf);
++err_free_tx_buf:
++ kfree(tx_buf);
++
++ return NULL;
++}
++EXPORT_SYMBOL_GPL(peci_get_xfer_msg);
++
++/**
++ * peci_put_xfer_msg - release a DMA safe peci_xfer_msg
++ * @msg: the message obtained from peci_get_xfer_msg(). May be NULL.
++ */
++void peci_put_xfer_msg(struct peci_xfer_msg *msg)
++{
++ if (!msg)
++ return;
++
++ kfree(msg->rx_buf);
++ kfree(msg->tx_buf);
++ kfree(msg);
++}
++EXPORT_SYMBOL_GPL(peci_put_xfer_msg);
++
++/* Calculate an Assured Write Frame Check Sequence byte */
++static int peci_aw_fcs(struct peci_xfer_msg *msg, int len, u8 *aw_fcs)
++{
++ u8 *tmp_buf;
++
++ /* Allocate a temporary buffer to use a contiguous byte array */
++ tmp_buf = kmalloc(len, GFP_KERNEL);
++ if (!tmp_buf)
++ return -ENOMEM;
++
++ tmp_buf[0] = msg->addr;
++ tmp_buf[1] = msg->tx_len;
++ tmp_buf[2] = msg->rx_len;
++ memcpy(&tmp_buf[3], msg->tx_buf, len - 3);
++
++ *aw_fcs = crc8(peci_crc8_table, tmp_buf, (size_t)len, 0);
++
++ kfree(tmp_buf);
++
++ return 0;
+ }
+
+ static int __peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg,
+ bool do_retry, bool has_aw_fcs)
+ {
+- ktime_t start, end;
+- s64 elapsed_ms;
+- int rc = 0;
++ ulong timeout = jiffies;
++ u8 aw_fcs;
++ int ret;
++
++ /*
++ * In case if adapter uses DMA, check at here whether tx and rx buffers
++ * are DMA capable or not.
++ */
++ if (IS_ENABLED(CONFIG_HAS_DMA) && adapter->use_dma) {
++ if (is_vmalloc_addr(msg->tx_buf) ||
++ is_vmalloc_addr(msg->rx_buf)) {
++ WARN_ONCE(1, "xfer msg is not dma capable\n");
++ return -EAGAIN;
++ } else if (object_is_on_stack(msg->tx_buf) ||
++ object_is_on_stack(msg->rx_buf)) {
++ WARN_ONCE(1, "xfer msg is on stack\n");
++ return -EAGAIN;
++ }
++ }
+
+- /**
++ /*
+ * For some commands, the PECI originator may need to retry a command if
+ * the processor PECI client responds with a 0x8x completion code. In
+ * each instance, the processor PECI client may have started the
+@@ -125,55 +223,51 @@ static int __peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg,
+ */
+
+ if (do_retry)
+- start = ktime_get();
++ timeout += msecs_to_jiffies(PECI_DEV_RETRY_TIME_MS);
+
+- do {
+- rc = adapter->xfer(adapter, msg);
++ for (;;) {
++ ret = adapter->xfer(adapter, msg);
+
+- if (!do_retry || rc)
+- break;
+-
+- if (msg->rx_buf[0] == DEV_PECI_CC_SUCCESS)
++ if (!do_retry || ret || !msg->rx_buf)
+ break;
+
+ /* Retry is needed when completion code is 0x8x */
+- if ((msg->rx_buf[0] & DEV_PECI_CC_RETRY_CHECK_MASK) !=
+- DEV_PECI_CC_NEED_RETRY) {
+- rc = -EIO;
++ if ((msg->rx_buf[0] & PECI_DEV_CC_RETRY_CHECK_MASK) !=
++ PECI_DEV_CC_NEED_RETRY)
+ break;
+- }
+
+ /* Set the retry bit to indicate a retry attempt */
+- msg->tx_buf[1] |= DEV_PECI_RETRY_BIT;
++ msg->tx_buf[1] |= PECI_DEV_RETRY_BIT;
+
+ /* Recalculate the AW FCS if it has one */
+- if (has_aw_fcs)
+- msg->tx_buf[msg->tx_len - 1] = 0x80 ^
+- peci_aw_fcs((u8 *)msg,
+- 2 + msg->tx_len);
++ if (has_aw_fcs) {
++ ret = peci_aw_fcs(msg, 2 + msg->tx_len, &aw_fcs);
++ if (ret)
++ break;
++
++ msg->tx_buf[msg->tx_len - 1] = 0x80 ^ aw_fcs;
++ }
+
+- /**
++ /*
+ * Retry for at least 250ms before returning an error.
+ * Retry interval guideline:
+ * No minimum < Retry Interval < No maximum
+ * (recommend 10ms)
+ */
+- end = ktime_get();
+- elapsed_ms = ktime_to_ms(ktime_sub(end, start));
+- if (elapsed_ms >= DEV_PECI_RETRY_TIME_MS) {
++ if (time_after(jiffies, timeout)) {
+ dev_dbg(&adapter->dev, "Timeout retrying xfer!\n");
+- rc = -ETIMEDOUT;
++ ret = -ETIMEDOUT;
+ break;
+ }
+
+- usleep_range((DEV_PECI_RETRY_INTERVAL_USEC >> 2) + 1,
+- DEV_PECI_RETRY_INTERVAL_USEC);
+- } while (true);
++ usleep_range((PECI_DEV_RETRY_INTERVAL_USEC >> 2) + 1,
++ PECI_DEV_RETRY_INTERVAL_USEC);
++ }
+
+- if (rc)
+- dev_dbg(&adapter->dev, "xfer error, rc: %d\n", rc);
++ if (ret)
++ dev_dbg(&adapter->dev, "xfer error: %d\n", ret);
+
+- return rc;
++ return ret;
+ }
+
+ static int peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg)
+@@ -190,34 +284,37 @@ static int peci_xfer_with_retries(struct peci_adapter *adapter,
+
+ static int peci_scan_cmd_mask(struct peci_adapter *adapter)
+ {
+- struct peci_xfer_msg msg;
++ struct peci_xfer_msg *msg;
+ u8 revision;
+- int rc = 0;
++ int ret;
+ u64 dib;
+
+ /* Update command mask just once */
+ if (adapter->cmd_mask & BIT(PECI_CMD_XFER))
+ return 0;
+
+- msg.addr = PECI_BASE_ADDR;
+- msg.tx_len = GET_DIB_WR_LEN;
+- msg.rx_len = GET_DIB_RD_LEN;
+- msg.tx_buf[0] = GET_DIB_PECI_CMD;
++ msg = peci_get_xfer_msg(PECI_GET_DIB_WR_LEN, PECI_GET_DIB_RD_LEN);
++ if (!msg)
++ return -ENOMEM;
++
++ msg->addr = PECI_BASE_ADDR;
++ msg->tx_buf[0] = PECI_GET_DIB_CMD;
+
+- rc = peci_xfer(adapter, &msg);
+- if (rc)
+- return rc;
++ ret = peci_xfer(adapter, msg);
++ if (ret)
++ return ret;
+
+- dib = le64_to_cpup((__le64 *)msg.rx_buf);
++ dib = le64_to_cpup((__le64 *)msg->rx_buf);
+
+ /* Check special case for Get DIB command */
+ if (dib == 0) {
+ dev_dbg(&adapter->dev, "DIB read as 0\n");
+- return -EIO;
++ ret = -EIO;
++ goto out;
+ }
+
+- /**
+- * Setting up the supporting commands based on minor revision number.
++ /*
++ * Setting up the supporting commands based on revision number.
+ * See PECI Spec Table 3-1.
+ */
+ revision = FIELD_GET(REVISION_NUM_MASK, dib);
+@@ -243,10 +340,14 @@ static int peci_scan_cmd_mask(struct peci_adapter *adapter)
+ adapter->cmd_mask |= BIT(PECI_CMD_GET_DIB);
+ adapter->cmd_mask |= BIT(PECI_CMD_PING);
+
+- return rc;
++out:
++ peci_put_xfer_msg(msg);
++
++ return ret;
+ }
+
+-static int peci_cmd_support(struct peci_adapter *adapter, enum peci_cmd cmd)
++static int peci_check_cmd_support(struct peci_adapter *adapter,
++ enum peci_cmd cmd)
+ {
+ if (!(adapter->cmd_mask & BIT(PECI_CMD_PING)) &&
+ peci_scan_cmd_mask(adapter) < 0) {
+@@ -262,70 +363,87 @@ static int peci_cmd_support(struct peci_adapter *adapter, enum peci_cmd cmd)
+ return 0;
+ }
+
+-static int peci_ioctl_xfer(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_xfer(struct peci_adapter *adapter, void *vmsg)
+ {
+ struct peci_xfer_msg *msg = vmsg;
+
+ return peci_xfer(adapter, msg);
+ }
+
+-static int peci_ioctl_ping(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_ping(struct peci_adapter *adapter, void *vmsg)
+ {
+ struct peci_ping_msg *umsg = vmsg;
+- struct peci_xfer_msg msg;
++ struct peci_xfer_msg *msg;
++ int ret;
++
++ msg = peci_get_xfer_msg(0, 0);
++ if (!msg)
++ return -ENOMEM;
++
++ msg->addr = umsg->addr;
+
+- msg.addr = umsg->addr;
+- msg.tx_len = 0;
+- msg.rx_len = 0;
++ ret = peci_xfer(adapter, msg);
+
+- return peci_xfer(adapter, &msg);
++ peci_put_xfer_msg(msg);
++
++ return ret;
+ }
+
+-static int peci_ioctl_get_dib(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_get_dib(struct peci_adapter *adapter, void *vmsg)
+ {
+ struct peci_get_dib_msg *umsg = vmsg;
+- struct peci_xfer_msg msg;
+- int rc;
++ struct peci_xfer_msg *msg;
++ int ret;
+
+- msg.addr = umsg->addr;
+- msg.tx_len = GET_DIB_WR_LEN;
+- msg.rx_len = GET_DIB_RD_LEN;
+- msg.tx_buf[0] = GET_DIB_PECI_CMD;
++ msg = peci_get_xfer_msg(PECI_GET_DIB_WR_LEN, PECI_GET_DIB_RD_LEN);
++ if (!msg)
++ return -ENOMEM;
+
+- rc = peci_xfer(adapter, &msg);
+- if (rc)
+- return rc;
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_GET_DIB_CMD;
+
+- umsg->dib = le64_to_cpup((__le64 *)msg.rx_buf);
++ ret = peci_xfer(adapter, msg);
++ if (ret)
++ goto out;
+
+- return 0;
++ umsg->dib = le64_to_cpup((__le64 *)msg->rx_buf);
++
++out:
++ peci_put_xfer_msg(msg);
++
++ return ret;
+ }
+
+-static int peci_ioctl_get_temp(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_get_temp(struct peci_adapter *adapter, void *vmsg)
+ {
+ struct peci_get_temp_msg *umsg = vmsg;
+- struct peci_xfer_msg msg;
+- int rc;
++ struct peci_xfer_msg *msg;
++ int ret;
+
+- msg.addr = umsg->addr;
+- msg.tx_len = GET_TEMP_WR_LEN;
+- msg.rx_len = GET_TEMP_RD_LEN;
+- msg.tx_buf[0] = GET_TEMP_PECI_CMD;
++ msg = peci_get_xfer_msg(PECI_GET_TEMP_WR_LEN, PECI_GET_TEMP_RD_LEN);
++ if (!msg)
++ return -ENOMEM;
+
+- rc = peci_xfer(adapter, &msg);
+- if (rc)
+- return rc;
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_GET_TEMP_CMD;
+
+- umsg->temp_raw = le16_to_cpup((__le16 *)msg.rx_buf);
++ ret = peci_xfer(adapter, msg);
++ if (ret)
++ goto out;
+
+- return 0;
++ umsg->temp_raw = le16_to_cpup((__le16 *)msg->rx_buf);
++
++out:
++ peci_put_xfer_msg(msg);
++
++ return ret;
+ }
+
+-static int peci_ioctl_rd_pkg_cfg(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_rd_pkg_cfg(struct peci_adapter *adapter, void *vmsg)
+ {
+ struct peci_rd_pkg_cfg_msg *umsg = vmsg;
+- struct peci_xfer_msg msg;
+- int rc = 0;
++ struct peci_xfer_msg *msg;
++ int ret;
+
+ /* Per the PECI spec, the read length must be a byte, word, or dword */
+ if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 4) {
+@@ -334,29 +452,35 @@ static int peci_ioctl_rd_pkg_cfg(struct peci_adapter *adapter, void *vmsg)
+ return -EINVAL;
+ }
+
+- msg.addr = umsg->addr;
+- msg.tx_len = RDPKGCFG_WRITE_LEN;
+- /* read lengths of 1 and 2 result in an error, so only use 4 for now */
+- msg.rx_len = RDPKGCFG_READ_LEN_BASE + umsg->rx_len;
+- msg.tx_buf[0] = RDPKGCFG_PECI_CMD;
+- msg.tx_buf[1] = 0; /* request byte for Host ID | Retry bit */
+- /* Host ID is 0 for PECI 3.0 */
+- msg.tx_buf[2] = umsg->index; /* RdPkgConfig index */
+- msg.tx_buf[3] = (u8)umsg->param; /* LSB - Config parameter */
+- msg.tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */
++ msg = peci_get_xfer_msg(PECI_RDPKGCFG_WRITE_LEN,
++ PECI_RDPKGCFG_READ_LEN_BASE + umsg->rx_len);
++ if (!msg)
++ return -ENOMEM;
++
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_RDPKGCFG_CMD;
++ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */
++ /* Host ID is 0 for PECI 3.0 */
++ msg->tx_buf[2] = umsg->index; /* RdPkgConfig index */
++ msg->tx_buf[3] = (u8)umsg->param; /* LSB - Config parameter */
++ msg->tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */
+
+- rc = peci_xfer_with_retries(adapter, &msg, false);
+- if (!rc)
+- memcpy(umsg->pkg_config, &msg.rx_buf[1], umsg->rx_len);
++ ret = peci_xfer_with_retries(adapter, msg, false);
++ if (!ret)
++ memcpy(umsg->pkg_config, &msg->rx_buf[1], umsg->rx_len);
+
+- return rc;
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
++
++ return ret;
+ }
+
+-static int peci_ioctl_wr_pkg_cfg(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_wr_pkg_cfg(struct peci_adapter *adapter, void *vmsg)
+ {
+ struct peci_wr_pkg_cfg_msg *umsg = vmsg;
+- struct peci_xfer_msg msg;
+- int rc = 0, i;
++ struct peci_xfer_msg *msg;
++ int ret, i;
++ u8 aw_fcs;
+
+ /* Per the PECI spec, the write length must be a dword */
+ if (umsg->tx_len != 4) {
+@@ -365,86 +489,116 @@ static int peci_ioctl_wr_pkg_cfg(struct peci_adapter *adapter, void *vmsg)
+ return -EINVAL;
+ }
+
+- msg.addr = umsg->addr;
+- msg.tx_len = WRPKGCFG_WRITE_LEN_BASE + umsg->tx_len;
+- /* read lengths of 1 and 2 result in an error, so only use 4 for now */
+- msg.rx_len = WRPKGCFG_READ_LEN;
+- msg.tx_buf[0] = WRPKGCFG_PECI_CMD;
+- msg.tx_buf[1] = 0; /* request byte for Host ID | Retry bit */
++ msg = peci_get_xfer_msg(PECI_WRPKGCFG_WRITE_LEN_BASE + umsg->tx_len,
++ PECI_WRPKGCFG_READ_LEN);
++ if (!msg)
++ return -ENOMEM;
++
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_WRPKGCFG_CMD;
++ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+- msg.tx_buf[2] = umsg->index; /* RdPkgConfig index */
+- msg.tx_buf[3] = (u8)umsg->param; /* LSB - Config parameter */
+- msg.tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */
++ msg->tx_buf[2] = umsg->index; /* RdPkgConfig index */
++ msg->tx_buf[3] = (u8)umsg->param; /* LSB - Config parameter */
++ msg->tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */
+ for (i = 0; i < umsg->tx_len; i++)
+- msg.tx_buf[5 + i] = (u8)(umsg->value >> (i << 3));
++ msg->tx_buf[5 + i] = (u8)(umsg->value >> (i << 3));
++
++ /* Add an Assured Write Frame Check Sequence byte */
++ ret = peci_aw_fcs(msg, 8 + umsg->tx_len, &aw_fcs);
++ if (ret)
++ goto out;
+
+- /* Add an Assure Write Frame Check Sequence byte */
+- msg.tx_buf[5 + i] = 0x80 ^
+- peci_aw_fcs((u8 *)&msg, 8 + umsg->tx_len);
++ msg->tx_buf[5 + i] = 0x80 ^ aw_fcs;
+
+- rc = peci_xfer_with_retries(adapter, &msg, true);
++ ret = peci_xfer_with_retries(adapter, msg, true);
+
+- return rc;
++out:
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
++
++ return ret;
+ }
+
+-static int peci_ioctl_rd_ia_msr(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_rd_ia_msr(struct peci_adapter *adapter, void *vmsg)
+ {
+ struct peci_rd_ia_msr_msg *umsg = vmsg;
+- struct peci_xfer_msg msg;
+- int rc = 0;
+-
+- msg.addr = umsg->addr;
+- msg.tx_len = RDIAMSR_WRITE_LEN;
+- msg.rx_len = RDIAMSR_READ_LEN;
+- msg.tx_buf[0] = RDIAMSR_PECI_CMD;
+- msg.tx_buf[1] = 0;
+- msg.tx_buf[2] = umsg->thread_id;
+- msg.tx_buf[3] = (u8)umsg->address;
+- msg.tx_buf[4] = (u8)(umsg->address >> 8);
+-
+- rc = peci_xfer_with_retries(adapter, &msg, false);
+- if (!rc)
+- memcpy(&umsg->value, &msg.rx_buf[1], sizeof(uint64_t));
+-
+- return rc;
++ struct peci_xfer_msg *msg;
++ int ret;
++
++ msg = peci_get_xfer_msg(PECI_RDIAMSR_WRITE_LEN, PECI_RDIAMSR_READ_LEN);
++ if (!msg)
++ return -ENOMEM;
++
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_RDIAMSR_CMD;
++ msg->tx_buf[1] = 0;
++ msg->tx_buf[2] = umsg->thread_id;
++ msg->tx_buf[3] = (u8)umsg->address;
++ msg->tx_buf[4] = (u8)(umsg->address >> 8);
++
++ ret = peci_xfer_with_retries(adapter, msg, false);
++ if (!ret)
++ memcpy(&umsg->value, &msg->rx_buf[1], sizeof(uint64_t));
++
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
++
++ return ret;
+ }
+
+-static int peci_ioctl_rd_pci_cfg(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_wr_ia_msr(struct peci_adapter *adapter, void *vmsg)
++{
++ return -ENOSYS; /* Not implemented yet */
++}
++
++static int peci_cmd_rd_pci_cfg(struct peci_adapter *adapter, void *vmsg)
+ {
+ struct peci_rd_pci_cfg_msg *umsg = vmsg;
+- struct peci_xfer_msg msg;
++ struct peci_xfer_msg *msg;
+ u32 address;
+- int rc = 0;
++ int ret;
++
++ msg = peci_get_xfer_msg(PECI_RDPCICFG_WRITE_LEN,
++ PECI_RDPCICFG_READ_LEN);
++ if (!msg)
++ return -ENOMEM;
+
+ address = umsg->reg; /* [11:0] - Register */
+ address |= (u32)umsg->function << 12; /* [14:12] - Function */
+ address |= (u32)umsg->device << 15; /* [19:15] - Device */
+ address |= (u32)umsg->bus << 20; /* [27:20] - Bus */
+ /* [31:28] - Reserved */
+- msg.addr = umsg->addr;
+- msg.tx_len = RDPCICFG_WRITE_LEN;
+- msg.rx_len = RDPCICFG_READ_LEN;
+- msg.tx_buf[0] = RDPCICFG_PECI_CMD;
+- msg.tx_buf[1] = 0; /* request byte for Host ID | Retry bit */
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_RDPCICFG_CMD;
++ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+- msg.tx_buf[2] = (u8)address; /* LSB - PCI Config Address */
+- msg.tx_buf[3] = (u8)(address >> 8); /* PCI Config Address */
+- msg.tx_buf[4] = (u8)(address >> 16); /* PCI Config Address */
+- msg.tx_buf[5] = (u8)(address >> 24); /* MSB - PCI Config Address */
++ msg->tx_buf[2] = (u8)address; /* LSB - PCI Config Address */
++ msg->tx_buf[3] = (u8)(address >> 8); /* PCI Config Address */
++ msg->tx_buf[4] = (u8)(address >> 16); /* PCI Config Address */
++ msg->tx_buf[5] = (u8)(address >> 24); /* MSB - PCI Config Address */
++
++ ret = peci_xfer_with_retries(adapter, msg, false);
++ if (!ret)
++ memcpy(umsg->pci_config, &msg->rx_buf[1], 4);
+
+- rc = peci_xfer_with_retries(adapter, &msg, false);
+- if (!rc)
+- memcpy(umsg->pci_config, &msg.rx_buf[1], 4);
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
+
+- return rc;
++ return ret;
+ }
+
+-static int peci_ioctl_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_wr_pci_cfg(struct peci_adapter *adapter, void *vmsg)
++{
++ return -ENOSYS; /* Not implemented yet */
++}
++
++static int peci_cmd_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
+ {
+ struct peci_rd_pci_cfg_local_msg *umsg = vmsg;
+- struct peci_xfer_msg msg;
++ struct peci_xfer_msg *msg;
+ u32 address;
+- int rc = 0;
++ int ret;
+
+ /* Per the PECI spec, the read length must be a byte, word, or dword */
+ if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 4) {
+@@ -453,34 +607,42 @@ static int peci_ioctl_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
+ return -EINVAL;
+ }
+
++ msg = peci_get_xfer_msg(PECI_RDPCICFGLOCAL_WRITE_LEN,
++ PECI_RDPCICFGLOCAL_READ_LEN_BASE +
++ umsg->rx_len);
++ if (!msg)
++ return -ENOMEM;
++
+ address = umsg->reg; /* [11:0] - Register */
+ address |= (u32)umsg->function << 12; /* [14:12] - Function */
+ address |= (u32)umsg->device << 15; /* [19:15] - Device */
+ address |= (u32)umsg->bus << 20; /* [23:20] - Bus */
+
+- msg.addr = umsg->addr;
+- msg.tx_len = RDPCICFGLOCAL_WRITE_LEN;
+- msg.rx_len = RDPCICFGLOCAL_READ_LEN_BASE + umsg->rx_len;
+- msg.tx_buf[0] = RDPCICFGLOCAL_PECI_CMD;
+- msg.tx_buf[1] = 0; /* request byte for Host ID | Retry bit */
+- /* Host ID is 0 for PECI 3.0 */
+- msg.tx_buf[2] = (u8)address; /* LSB - PCI Configuration Address */
+- msg.tx_buf[3] = (u8)(address >> 8); /* PCI Configuration Address */
+- msg.tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_RDPCICFGLOCAL_CMD;
++ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */
++ /* Host ID is 0 for PECI 3.0 */
++ msg->tx_buf[2] = (u8)address; /* LSB - PCI Configuration Address */
++ msg->tx_buf[3] = (u8)(address >> 8); /* PCI Configuration Address */
++ msg->tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */
++
++ ret = peci_xfer_with_retries(adapter, msg, false);
++ if (!ret)
++ memcpy(umsg->pci_config, &msg->rx_buf[1], umsg->rx_len);
+
+- rc = peci_xfer_with_retries(adapter, &msg, false);
+- if (!rc)
+- memcpy(umsg->pci_config, &msg.rx_buf[1], umsg->rx_len);
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
+
+- return rc;
++ return ret;
+ }
+
+-static int peci_ioctl_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
++static int peci_cmd_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
+ {
+ struct peci_wr_pci_cfg_local_msg *umsg = vmsg;
+- struct peci_xfer_msg msg;
+- int rc = 0, i;
++ struct peci_xfer_msg *msg;
+ u32 address;
++ int ret, i;
++ u8 aw_fcs;
+
+ /* Per the PECI spec, the write length must be a byte, word, or dword */
+ if (umsg->tx_len != 1 && umsg->tx_len != 2 && umsg->tx_len != 4) {
+@@ -489,47 +651,57 @@ static int peci_ioctl_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
+ return -EINVAL;
+ }
+
++ msg = peci_get_xfer_msg(PECI_WRPCICFGLOCAL_WRITE_LEN_BASE +
++ umsg->tx_len, PECI_WRPCICFGLOCAL_READ_LEN);
++ if (!msg)
++ return -ENOMEM;
++
+ address = umsg->reg; /* [11:0] - Register */
+ address |= (u32)umsg->function << 12; /* [14:12] - Function */
+ address |= (u32)umsg->device << 15; /* [19:15] - Device */
+ address |= (u32)umsg->bus << 20; /* [23:20] - Bus */
+
+- msg.addr = umsg->addr;
+- msg.tx_len = WRPCICFGLOCAL_WRITE_LEN_BASE + umsg->tx_len;
+- msg.rx_len = WRPCICFGLOCAL_READ_LEN;
+- msg.tx_buf[0] = WRPCICFGLOCAL_PECI_CMD;
+- msg.tx_buf[1] = 0; /* request byte for Host ID | Retry bit */
+- /* Host ID is 0 for PECI 3.0 */
+- msg.tx_buf[2] = (u8)address; /* LSB - PCI Configuration Address */
+- msg.tx_buf[3] = (u8)(address >> 8); /* PCI Configuration Address */
+- msg.tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_WRPCICFGLOCAL_CMD;
++ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */
++ /* Host ID is 0 for PECI 3.0 */
++ msg->tx_buf[2] = (u8)address; /* LSB - PCI Configuration Address */
++ msg->tx_buf[3] = (u8)(address >> 8); /* PCI Configuration Address */
++ msg->tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */
+ for (i = 0; i < umsg->tx_len; i++)
+- msg.tx_buf[5 + i] = (u8)(umsg->value >> (i << 3));
++ msg->tx_buf[5 + i] = (u8)(umsg->value >> (i << 3));
++
++ /* Add an Assured Write Frame Check Sequence byte */
++ ret = peci_aw_fcs(msg, 8 + umsg->tx_len, &aw_fcs);
++ if (ret)
++ goto out;
++
++ msg->tx_buf[5 + i] = 0x80 ^ aw_fcs;
+
+- /* Add an Assure Write Frame Check Sequence byte */
+- msg.tx_buf[5 + i] = 0x80 ^
+- peci_aw_fcs((u8 *)&msg, 8 + umsg->tx_len);
++ ret = peci_xfer_with_retries(adapter, msg, true);
+
+- rc = peci_xfer_with_retries(adapter, &msg, true);
++out:
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
+
+- return rc;
++ return ret;
+ }
+
+-typedef int (*peci_ioctl_fn_type)(struct peci_adapter *, void *);
+-
+-static const peci_ioctl_fn_type peci_ioctl_fn[PECI_CMD_MAX] = {
+- peci_ioctl_xfer,
+- peci_ioctl_ping,
+- peci_ioctl_get_dib,
+- peci_ioctl_get_temp,
+- peci_ioctl_rd_pkg_cfg,
+- peci_ioctl_wr_pkg_cfg,
+- peci_ioctl_rd_ia_msr,
+- NULL, /* Reserved */
+- peci_ioctl_rd_pci_cfg,
+- NULL, /* Reserved */
+- peci_ioctl_rd_pci_cfg_local,
+- peci_ioctl_wr_pci_cfg_local,
++typedef int (*peci_cmd_fn_type)(struct peci_adapter *, void *);
++
++static const peci_cmd_fn_type peci_cmd_fn[PECI_CMD_MAX] = {
++ peci_cmd_xfer,
++ peci_cmd_ping,
++ peci_cmd_get_dib,
++ peci_cmd_get_temp,
++ peci_cmd_rd_pkg_cfg,
++ peci_cmd_wr_pkg_cfg,
++ peci_cmd_rd_ia_msr,
++ peci_cmd_wr_ia_msr,
++ peci_cmd_rd_pci_cfg,
++ peci_cmd_wr_pci_cfg,
++ peci_cmd_rd_pci_cfg_local,
++ peci_cmd_wr_pci_cfg_local,
+ };
+
+ /**
+@@ -545,109 +717,28 @@ static const peci_ioctl_fn_type peci_ioctl_fn[PECI_CMD_MAX] = {
+ */
+ int peci_command(struct peci_adapter *adapter, enum peci_cmd cmd, void *vmsg)
+ {
+- int rc = 0;
++ int ret;
+
+ if (cmd >= PECI_CMD_MAX || cmd < PECI_CMD_XFER)
+- return -EINVAL;
++ return -ENOTTY;
+
+ dev_dbg(&adapter->dev, "%s, cmd=0x%02x\n", __func__, cmd);
+
+- if (!peci_ioctl_fn[cmd])
++ if (!peci_cmd_fn[cmd])
+ return -EINVAL;
+
+- rt_mutex_lock(&adapter->bus_lock);
++ mutex_lock(&adapter->bus_lock);
+
+- rc = peci_cmd_support(adapter, cmd);
+- if (!rc)
+- rc = peci_ioctl_fn[cmd](adapter, vmsg);
++ ret = peci_check_cmd_support(adapter, cmd);
++ if (!ret)
++ ret = peci_cmd_fn[cmd](adapter, vmsg);
+
+- rt_mutex_unlock(&adapter->bus_lock);
++ mutex_unlock(&adapter->bus_lock);
+
+- return rc;
++ return ret;
+ }
+ EXPORT_SYMBOL_GPL(peci_command);
+
+-static long peci_ioctl(struct file *file, unsigned int iocmd, unsigned long arg)
+-{
+- struct peci_adapter *adapter = file->private_data;
+- void __user *argp = (void __user *)arg;
+- unsigned int msg_len;
+- enum peci_cmd cmd;
+- int rc = 0;
+- u8 *msg;
+-
+- if (!capable(CAP_SYS_ADMIN))
+- return -EPERM;
+-
+- dev_dbg(&adapter->dev, "ioctl, cmd=0x%x, arg=0x%lx\n", iocmd, arg);
+-
+- switch (iocmd) {
+- case PECI_IOC_XFER:
+- case PECI_IOC_PING:
+- case PECI_IOC_GET_DIB:
+- case PECI_IOC_GET_TEMP:
+- case PECI_IOC_RD_PKG_CFG:
+- case PECI_IOC_WR_PKG_CFG:
+- case PECI_IOC_RD_IA_MSR:
+- case PECI_IOC_RD_PCI_CFG:
+- case PECI_IOC_RD_PCI_CFG_LOCAL:
+- case PECI_IOC_WR_PCI_CFG_LOCAL:
+- cmd = _IOC_NR(iocmd);
+- msg_len = _IOC_SIZE(iocmd);
+- break;
+-
+- default:
+- dev_dbg(&adapter->dev, "Invalid ioctl cmd : 0x%x\n", iocmd);
+- return -ENOTTY;
+- }
+-
+- if (!access_ok(argp, msg_len))
+- return -EFAULT;
+-
+- msg = memdup_user(argp, msg_len);
+- if (IS_ERR(msg))
+- return PTR_ERR(msg);
+-
+- rc = peci_command(adapter, cmd, msg);
+-
+- if (!rc && copy_to_user(argp, msg, msg_len))
+- rc = -EFAULT;
+-
+- kfree(msg);
+- return (long)rc;
+-}
+-
+-static int peci_open(struct inode *inode, struct file *file)
+-{
+- unsigned int minor = iminor(inode);
+- struct peci_adapter *adapter;
+-
+- adapter = peci_get_adapter(minor);
+- if (!adapter)
+- return -ENODEV;
+-
+- file->private_data = adapter;
+-
+- return 0;
+-}
+-
+-static int peci_release(struct inode *inode, struct file *file)
+-{
+- struct peci_adapter *adapter = file->private_data;
+-
+- peci_put_adapter(adapter);
+- file->private_data = NULL;
+-
+- return 0;
+-}
+-
+-static const struct file_operations peci_fops = {
+- .owner = THIS_MODULE,
+- .unlocked_ioctl = peci_ioctl,
+- .open = peci_open,
+- .release = peci_release,
+-};
+-
+ static int peci_detect(struct peci_adapter *adapter, u8 addr)
+ {
+ struct peci_ping_msg msg;
+@@ -666,9 +757,9 @@ peci_of_match_device(const struct of_device_id *matches,
+ return NULL;
+
+ return of_match_device(matches, &client->dev);
+-#else
++#else /* CONFIG_OF */
+ return NULL;
+-#endif
++#endif /* CONFIG_OF */
+ }
+
+ static const struct peci_device_id *
+@@ -737,6 +828,7 @@ static int peci_device_probe(struct device *dev)
+
+ err_detach_pm_domain:
+ dev_pm_domain_detach(&client->dev, true);
++
+ return status;
+ }
+
+@@ -775,13 +867,14 @@ static void peci_device_shutdown(struct device *dev)
+ driver->shutdown(client);
+ }
+
+-static struct bus_type peci_bus_type = {
++struct bus_type peci_bus_type = {
+ .name = "peci",
+ .match = peci_device_match,
+ .probe = peci_device_probe,
+ .remove = peci_device_remove,
+ .shutdown = peci_device_shutdown,
+ };
++EXPORT_SYMBOL_GPL(peci_bus_type);
+
+ static int peci_check_addr_validity(u8 addr)
+ {
+@@ -814,18 +907,23 @@ static int peci_check_client_busy(struct device *dev, void *client_new_p)
+ int peci_get_cpu_id(struct peci_adapter *adapter, u8 addr, u32 *cpu_id)
+ {
+ struct peci_rd_pkg_cfg_msg msg;
+- int rc;
++ int ret;
+
+ msg.addr = addr;
+- msg.index = MBX_INDEX_CPU_ID;
+- msg.param = PKG_ID_CPU_ID;
++ msg.index = PECI_MBX_INDEX_CPU_ID;
++ msg.param = PECI_PKG_ID_CPU_ID;
+ msg.rx_len = 4;
+
+- rc = peci_command(adapter, PECI_CMD_RD_PKG_CFG, &msg);
+- if (!rc)
+- *cpu_id = le32_to_cpup((__le32 *)msg.pkg_config);
++ ret = peci_command(adapter, PECI_CMD_RD_PKG_CFG, &msg);
++ if (ret)
++ return ret;
++
++ if (msg.cc != PECI_DEV_CC_SUCCESS)
++ return -EAGAIN;
++
++ *cpu_id = le32_to_cpup((__le32 *)msg.pkg_config);
+
+- return rc;
++ return 0;
+ }
+ EXPORT_SYMBOL_GPL(peci_get_cpu_id);
+
+@@ -833,7 +931,7 @@ static struct peci_client *peci_new_device(struct peci_adapter *adapter,
+ struct peci_board_info const *info)
+ {
+ struct peci_client *client;
+- int rc;
++ int ret;
+
+ /* Increase reference count for the adapter assigned */
+ if (!peci_get_adapter(adapter->nr))
+@@ -847,46 +945,49 @@ static struct peci_client *peci_new_device(struct peci_adapter *adapter,
+ client->addr = info->addr;
+ strlcpy(client->name, info->type, sizeof(client->name));
+
+- rc = peci_check_addr_validity(client->addr);
+- if (rc) {
++ ret = peci_check_addr_validity(client->addr);
++ if (ret) {
+ dev_err(&adapter->dev, "Invalid PECI CPU address 0x%02hx\n",
+ client->addr);
+ goto err_free_client_silent;
+ }
+
+ /* Check online status of client */
+- rc = peci_detect(adapter, client->addr);
+- if (rc)
++ ret = peci_detect(adapter, client->addr);
++ if (ret)
+ goto err_free_client;
+
+- rc = device_for_each_child(&adapter->dev, client,
+- peci_check_client_busy);
+- if (rc)
++ ret = device_for_each_child(&adapter->dev, client,
++ peci_check_client_busy);
++ if (ret)
+ goto err_free_client;
+
+ client->dev.parent = &client->adapter->dev;
+ client->dev.bus = &peci_bus_type;
+ client->dev.type = &peci_client_type;
+- client->dev.of_node = info->of_node;
++ client->dev.of_node = of_node_get(info->of_node);
+ dev_set_name(&client->dev, "%d-%02x", adapter->nr, client->addr);
+
+- rc = device_register(&client->dev);
+- if (rc)
+- goto err_free_client;
++ ret = device_register(&client->dev);
++ if (ret)
++ goto err_put_of_node;
+
+ dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
+ client->name, dev_name(&client->dev));
+
+ return client;
+
++err_put_of_node:
++ of_node_put(info->of_node);
+ err_free_client:
+ dev_err(&adapter->dev,
+ "Failed to register peci client %s at 0x%02x (%d)\n",
+- client->name, client->addr, rc);
++ client->name, client->addr, ret);
+ err_free_client_silent:
+ kfree(client);
+ err_put_adapter:
+ peci_put_adapter(adapter);
++
+ return NULL;
+ }
+
+@@ -895,8 +996,10 @@ static void peci_unregister_device(struct peci_client *client)
+ if (!client)
+ return;
+
+- if (client->dev.of_node)
++ if (client->dev.of_node) {
+ of_node_clear_flag(client->dev.of_node, OF_POPULATED);
++ of_node_put(client->dev.of_node);
++ }
+
+ device_unregister(&client->dev);
+ }
+@@ -916,7 +1019,7 @@ static void peci_adapter_dev_release(struct device *dev)
+
+ dev_dbg(dev, "%s: %s\n", __func__, adapter->name);
+ mutex_destroy(&adapter->userspace_clients_lock);
+- rt_mutex_destroy(&adapter->bus_lock);
++ mutex_destroy(&adapter->bus_lock);
+ kfree(adapter);
+ }
+
+@@ -928,7 +1031,8 @@ static ssize_t peci_sysfs_new_device(struct device *dev,
+ struct peci_board_info info = {};
+ struct peci_client *client;
+ char *blank, end;
+- int rc;
++ short addr;
++ int ret;
+
+ /* Parse device type */
+ blank = strchr(buf, ' ');
+@@ -943,16 +1047,17 @@ static ssize_t peci_sysfs_new_device(struct device *dev,
+ memcpy(info.type, buf, blank - buf);
+
+ /* Parse remaining parameters, reject extra parameters */
+- rc = sscanf(++blank, "%hi%c", &info.addr, &end);
+- if (rc < 1) {
++ ret = sscanf(++blank, "%hi%c", &addr, &end);
++ if (ret < 1) {
+ dev_err(dev, "%s: Can't parse client address\n", "new_device");
+ return -EINVAL;
+ }
+- if (rc > 1 && end != '\n') {
++ if (ret > 1 && end != '\n') {
+ dev_err(dev, "%s: Extra parameters\n", "new_device");
+ return -EINVAL;
+ }
+
++ info.addr = (u8)addr;
+ client = peci_new_device(adapter, &info);
+ if (!client)
+ return -EINVAL;
+@@ -961,8 +1066,8 @@ static ssize_t peci_sysfs_new_device(struct device *dev,
+ mutex_lock(&adapter->userspace_clients_lock);
+ list_add_tail(&client->detected, &adapter->userspace_clients);
+ mutex_unlock(&adapter->userspace_clients_lock);
+- dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device",
+- info.type, info.addr);
++ dev_dbg(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device",
++ info.type, info.addr);
+
+ return count;
+ }
+@@ -975,9 +1080,9 @@ static ssize_t peci_sysfs_delete_device(struct device *dev,
+ struct peci_adapter *adapter = to_peci_adapter(dev);
+ struct peci_client *client, *next;
+ struct peci_board_info info = {};
+- struct peci_driver *driver;
+ char *blank, end;
+- int rc;
++ short addr;
++ int ret;
+
+ /* Parse device type */
+ blank = strchr(buf, ' ');
+@@ -992,41 +1097,41 @@ static ssize_t peci_sysfs_delete_device(struct device *dev,
+ memcpy(info.type, buf, blank - buf);
+
+ /* Parse remaining parameters, reject extra parameters */
+- rc = sscanf(++blank, "%hi%c", &info.addr, &end);
+- if (rc < 1) {
++ ret = sscanf(++blank, "%hi%c", &addr, &end);
++ if (ret < 1) {
+ dev_err(dev, "%s: Can't parse client address\n",
+ "delete_device");
+ return -EINVAL;
+ }
+- if (rc > 1 && end != '\n') {
++ if (ret > 1 && end != '\n') {
+ dev_err(dev, "%s: Extra parameters\n", "delete_device");
+ return -EINVAL;
+ }
+
++ info.addr = (u8)addr;
++
+ /* Make sure the device was added through sysfs */
+- rc = -ENOENT;
++ ret = -ENOENT;
+ mutex_lock(&adapter->userspace_clients_lock);
+ list_for_each_entry_safe(client, next, &adapter->userspace_clients,
+ detected) {
+- driver = to_peci_driver(client->dev.driver);
+-
+ if (client->addr == info.addr &&
+ !strncmp(client->name, info.type, PECI_NAME_SIZE)) {
+- dev_info(dev, "%s: Deleting device %s at 0x%02hx\n",
+- "delete_device", client->name, client->addr);
++ dev_dbg(dev, "%s: Deleting device %s at 0x%02hx\n",
++ "delete_device", client->name, client->addr);
+ list_del(&client->detected);
+ peci_unregister_device(client);
+- rc = count;
++ ret = count;
+ break;
+ }
+ }
+ mutex_unlock(&adapter->userspace_clients_lock);
+
+- if (rc < 0)
+- dev_err(dev, "%s: Can't find device in list\n",
++ if (ret < 0)
++ dev_dbg(dev, "%s: Can't find device in list\n",
+ "delete_device");
+
+- return rc;
++ return ret;
+ }
+ static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, 0200, NULL,
+ peci_sysfs_delete_device);
+@@ -1039,10 +1144,11 @@ static struct attribute *peci_adapter_attrs[] = {
+ };
+ ATTRIBUTE_GROUPS(peci_adapter);
+
+-static struct device_type peci_adapter_type = {
++struct device_type peci_adapter_type = {
+ .groups = peci_adapter_groups,
+ .release = peci_adapter_dev_release,
+ };
++EXPORT_SYMBOL_GPL(peci_adapter_type);
+
+ /**
+ * peci_verify_adapter - return parameter as peci_adapter, or NULL
+@@ -1063,32 +1169,26 @@ static struct peci_client *peci_of_register_device(struct peci_adapter *adapter,
+ struct device_node *node)
+ {
+ struct peci_board_info info = {};
+- struct peci_client *result;
+- const __be32 *addr_be;
+- int len;
++ struct peci_client *client;
++ u32 addr;
++ int ret;
+
+ dev_dbg(&adapter->dev, "register %pOF\n", node);
+
+- if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
+- dev_err(&adapter->dev, "modalias failure on %pOF\n", node);
+- return ERR_PTR(-EINVAL);
+- }
+-
+- addr_be = of_get_property(node, "reg", &len);
+- if (!addr_be || len < sizeof(*addr_be)) {
++ ret = of_property_read_u32(node, "reg", &addr);
++ if (ret) {
+ dev_err(&adapter->dev, "invalid reg on %pOF\n", node);
+- return ERR_PTR(-EINVAL);
++ return ERR_PTR(ret);
+ }
+
+- info.addr = be32_to_cpup(addr_be);
+- info.of_node = of_node_get(node);
++ info.addr = addr;
++ info.of_node = node;
+
+- result = peci_new_device(adapter, &info);
+- if (!result)
+- result = ERR_PTR(-EINVAL);
++ client = peci_new_device(adapter, &info);
++ if (!client)
++ client = ERR_PTR(-EINVAL);
+
+- of_node_put(node);
+- return result;
++ return client;
+ }
+
+ static void peci_of_register_devices(struct peci_adapter *adapter)
+@@ -1119,7 +1219,7 @@ static void peci_of_register_devices(struct peci_adapter *adapter)
+
+ of_node_put(bus);
+ }
+-#else
++#else /* CONFIG_OF */
+ static void peci_of_register_devices(struct peci_adapter *adapter) { }
+ #endif /* CONFIG_OF */
+
+@@ -1163,9 +1263,7 @@ static struct peci_adapter *peci_of_find_adapter(struct device_node *node)
+ return adapter;
+ }
+
+-static int peci_of_notify(struct notifier_block *nb,
+- unsigned long action,
+- void *arg)
++static int peci_of_notify(struct notifier_block *nb, ulong action, void *arg)
+ {
+ struct of_reconfig_data *rd = arg;
+ struct peci_adapter *adapter;
+@@ -1216,7 +1314,7 @@ static int peci_of_notify(struct notifier_block *nb,
+ static struct notifier_block peci_of_notifier = {
+ .notifier_call = peci_of_notify,
+ };
+-#else
++#else /* CONFIG_OF_DYNAMIC */
+ extern struct notifier_block peci_of_notifier;
+ #endif /* CONFIG_OF_DYNAMIC */
+
+@@ -1240,7 +1338,7 @@ extern struct notifier_block peci_of_notifier;
+ *
+ * Return: the peci_adapter structure on success, else NULL.
+ */
+-struct peci_adapter *peci_alloc_adapter(struct device *dev, unsigned int size)
++struct peci_adapter *peci_alloc_adapter(struct device *dev, uint size)
+ {
+ struct peci_adapter *adapter;
+
+@@ -1263,7 +1361,7 @@ EXPORT_SYMBOL_GPL(peci_alloc_adapter);
+
+ static int peci_register_adapter(struct peci_adapter *adapter)
+ {
+- int rc = -EINVAL;
++ int ret = -EINVAL;
+
+ /* Can't register until after driver model init */
+ if (WARN_ON(!is_registered))
+@@ -1275,27 +1373,17 @@ static int peci_register_adapter(struct peci_adapter *adapter)
+ if (WARN(!adapter->xfer, "peci adapter has no xfer function\n"))
+ goto err_free_idr;
+
+- rt_mutex_init(&adapter->bus_lock);
++ mutex_init(&adapter->bus_lock);
+ mutex_init(&adapter->userspace_clients_lock);
+ INIT_LIST_HEAD(&adapter->userspace_clients);
+
+ dev_set_name(&adapter->dev, "peci-%d", adapter->nr);
+
+- /* cdev */
+- cdev_init(&adapter->cdev, &peci_fops);
+- adapter->cdev.owner = THIS_MODULE;
+- adapter->dev.devt = MKDEV(MAJOR(peci_devt), adapter->nr);
+- rc = cdev_add(&adapter->cdev, adapter->dev.devt, 1);
+- if (rc) {
+- pr_err("adapter '%s': can't add cdev (%d)\n",
+- adapter->name, rc);
+- goto err_free_idr;
+- }
+- rc = device_add(&adapter->dev);
+- if (rc) {
++ ret = device_add(&adapter->dev);
++ if (ret) {
+ pr_err("adapter '%s': can't add device (%d)\n",
+- adapter->name, rc);
+- goto err_del_cdev;
++ adapter->name, ret);
++ goto err_free_idr;
+ }
+
+ dev_dbg(&adapter->dev, "adapter [%s] registered\n", adapter->name);
+@@ -1309,13 +1397,11 @@ static int peci_register_adapter(struct peci_adapter *adapter)
+
+ return 0;
+
+-err_del_cdev:
+- cdev_del(&adapter->cdev);
+ err_free_idr:
+ mutex_lock(&core_lock);
+ idr_remove(&peci_adapter_idr, adapter->nr);
+ mutex_unlock(&core_lock);
+- return rc;
++ return ret;
+ }
+
+ static int peci_add_numbered_adapter(struct peci_adapter *adapter)
+@@ -1411,7 +1497,7 @@ void peci_del_adapter(struct peci_adapter *adapter)
+ }
+ mutex_unlock(&adapter->userspace_clients_lock);
+
+- /**
++ /*
+ * Detach any active clients. This can't fail, thus we do not
+ * check the returned value.
+ */
+@@ -1420,13 +1506,8 @@ void peci_del_adapter(struct peci_adapter *adapter)
+ /* device name is gone after device_unregister */
+ dev_dbg(&adapter->dev, "adapter [%s] unregistered\n", adapter->name);
+
+- /* free cdev */
+- cdev_del(&adapter->cdev);
+-
+ pm_runtime_disable(&adapter->dev);
+-
+ nr = adapter->nr;
+-
+ device_unregister(&adapter->dev);
+
+ /* free bus id */
+@@ -1436,6 +1517,18 @@ void peci_del_adapter(struct peci_adapter *adapter)
+ }
+ EXPORT_SYMBOL_GPL(peci_del_adapter);
+
++int peci_for_each_dev(void *data, int (*fn)(struct device *, void *))
++{
++ int ret;
++
++ mutex_lock(&core_lock);
++ ret = bus_for_each_dev(&peci_bus_type, NULL, data, fn);
++ mutex_unlock(&core_lock);
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(peci_for_each_dev);
++
+ /**
+ * peci_register_driver - register a PECI driver
+ * @owner: owner module of the driver being registered
+@@ -1446,7 +1539,7 @@ EXPORT_SYMBOL_GPL(peci_del_adapter);
+ */
+ int peci_register_driver(struct module *owner, struct peci_driver *driver)
+ {
+- int rc;
++ int ret;
+
+ /* Can't register until after driver model init */
+ if (WARN_ON(!is_registered))
+@@ -1456,13 +1549,13 @@ int peci_register_driver(struct module *owner, struct peci_driver *driver)
+ driver->driver.owner = owner;
+ driver->driver.bus = &peci_bus_type;
+
+- /**
++ /*
+ * When registration returns, the driver core
+ * will have called probe() for all matching-but-unbound devices.
+ */
+- rc = driver_register(&driver->driver);
+- if (rc)
+- return rc;
++ ret = driver_register(&driver->driver);
++ if (ret)
++ return ret;
+
+ pr_debug("driver [%s] registered\n", driver->driver.name);
+
+@@ -1492,13 +1585,6 @@ static int __init peci_init(void)
+ return ret;
+ }
+
+- ret = alloc_chrdev_region(&peci_devt, 0, PECI_CDEV_MAX, "peci");
+- if (ret < 0) {
+- pr_err("peci: Failed to allocate chr dev region!\n");
+- bus_unregister(&peci_bus_type);
+- return ret;
+- }
+-
+ crc8_populate_msb(peci_crc8_table, PECI_CRC8_POLYNOMIAL);
+
+ if (IS_ENABLED(CONFIG_OF_DYNAMIC))
+@@ -1514,11 +1600,10 @@ static void __exit peci_exit(void)
+ if (IS_ENABLED(CONFIG_OF_DYNAMIC))
+ WARN_ON(of_reconfig_notifier_unregister(&peci_of_notifier));
+
+- unregister_chrdev_region(peci_devt, PECI_CDEV_MAX);
+ bus_unregister(&peci_bus_type);
+ }
+
+-postcore_initcall(peci_init);
++subsys_initcall(peci_init);
+ module_exit(peci_exit);
+
+ MODULE_AUTHOR("Jason M Biils <jason.m.bills@linux.intel.com>");
+diff --git a/drivers/peci/peci-dev.c b/drivers/peci/peci-dev.c
+new file mode 100644
+index 000000000000..ac9cba0fb429
+--- /dev/null
++++ b/drivers/peci/peci-dev.c
+@@ -0,0 +1,346 @@
++// SPDX-License-Identifier: GPL-2.0
++// Copyright (c) 2018-2019 Intel Corporation
++
++#include <linux/cdev.h>
++#include <linux/fs.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/notifier.h>
++#include <linux/peci.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++
++/*
++ * A peci_dev represents an peci_adapter ... an PECI or SMBus master, not a
++ * slave (peci_client) with which messages will be exchanged. It's coupled
++ * with a character special file which is accessed by user mode drivers.
++ *
++ * The list of peci_dev structures is parallel to the peci_adapter lists
++ * maintained by the driver model, and is updated using bus notifications.
++ */
++struct peci_dev {
++ struct list_head list;
++ struct peci_adapter *adapter;
++ struct device *dev;
++ struct cdev cdev;
++};
++
++#define PECI_MINORS MINORMASK
++
++static dev_t peci_devt;
++static LIST_HEAD(peci_dev_list);
++static DEFINE_SPINLOCK(peci_dev_list_lock);
++
++static struct peci_dev *peci_dev_get_by_minor(uint index)
++{
++ struct peci_dev *peci_dev;
++
++ spin_lock(&peci_dev_list_lock);
++ list_for_each_entry(peci_dev, &peci_dev_list, list) {
++ if (peci_dev->adapter->nr == index)
++ goto found;
++ }
++ peci_dev = NULL;
++found:
++ spin_unlock(&peci_dev_list_lock);
++
++ return peci_dev;
++}
++
++static struct peci_dev *peci_dev_alloc(struct peci_adapter *adapter)
++{
++ struct peci_dev *peci_dev;
++
++ if (adapter->nr >= PECI_MINORS) {
++ printk(KERN_ERR "peci-dev: Out of device minors (%d)\n",
++ adapter->nr);
++ return ERR_PTR(-ENODEV);
++ }
++
++ peci_dev = kzalloc(sizeof(*peci_dev), GFP_KERNEL);
++ if (!peci_dev)
++ return ERR_PTR(-ENOMEM);
++ peci_dev->adapter = adapter;
++
++ spin_lock(&peci_dev_list_lock);
++ list_add_tail(&peci_dev->list, &peci_dev_list);
++ spin_unlock(&peci_dev_list_lock);
++
++ return peci_dev;
++}
++
++static void peci_dev_put(struct peci_dev *peci_dev)
++{
++ spin_lock(&peci_dev_list_lock);
++ list_del(&peci_dev->list);
++ spin_unlock(&peci_dev_list_lock);
++ kfree(peci_dev);
++}
++
++static ssize_t name_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct peci_dev *peci_dev = peci_dev_get_by_minor(MINOR(dev->devt));
++
++ if (!peci_dev)
++ return -ENODEV;
++
++ return sprintf(buf, "%s\n", peci_dev->adapter->name);
++}
++static DEVICE_ATTR_RO(name);
++
++static struct attribute *peci_dev_attrs[] = {
++ &dev_attr_name.attr,
++ NULL,
++};
++ATTRIBUTE_GROUPS(peci_dev);
++
++static long peci_dev_ioctl(struct file *file, uint iocmd, ulong arg)
++{
++ struct peci_dev *peci_dev = file->private_data;
++ void __user *umsg = (void __user *)arg;
++ struct peci_xfer_msg *xmsg = NULL;
++ struct peci_xfer_msg uxmsg;
++ enum peci_cmd cmd;
++ u8 *msg = NULL;
++ uint msg_len;
++ int ret;
++
++ cmd = _IOC_NR(iocmd);
++ msg_len = _IOC_SIZE(iocmd);
++
++ switch (cmd) {
++ case PECI_CMD_XFER:
++ if (msg_len != sizeof(struct peci_xfer_msg)) {
++ ret = -EFAULT;
++ break;
++ }
++
++ if (copy_from_user(&uxmsg, umsg, msg_len)) {
++ ret = -EFAULT;
++ break;
++ }
++
++ xmsg = peci_get_xfer_msg(uxmsg.tx_len, uxmsg.rx_len);
++ if (IS_ERR(xmsg)) {
++ ret = PTR_ERR(xmsg);
++ break;
++ }
++
++ if (uxmsg.tx_len &&
++ copy_from_user(xmsg->tx_buf, uxmsg.tx_buf, uxmsg.tx_len)) {
++ ret = -EFAULT;
++ break;
++ }
++
++ xmsg->addr = uxmsg.addr;
++ xmsg->tx_len = uxmsg.tx_len;
++ xmsg->rx_len = uxmsg.rx_len;
++
++ ret = peci_command(peci_dev->adapter, cmd, xmsg);
++ if (!ret && xmsg->rx_len &&
++ copy_to_user(uxmsg.rx_buf, xmsg->rx_buf, xmsg->rx_len))
++ ret = -EFAULT;
++
++ break;
++
++ default:
++ msg = memdup_user(umsg, msg_len);
++ if (IS_ERR(msg)) {
++ ret = PTR_ERR(msg);
++ break;
++ }
++
++ ret = peci_command(peci_dev->adapter, cmd, msg);
++ if ((!ret || ret == -ETIMEDOUT) &&
++ copy_to_user(umsg, msg, msg_len))
++ ret = -EFAULT;
++
++ break;
++ }
++
++ peci_put_xfer_msg(xmsg);
++ kfree(msg);
++
++ return (long)ret;
++}
++
++static int peci_dev_open(struct inode *inode, struct file *file)
++{
++ struct peci_adapter *adapter;
++ struct peci_dev *peci_dev;
++
++ peci_dev = peci_dev_get_by_minor(iminor(inode));
++ if (!peci_dev)
++ return -ENODEV;
++
++ adapter = peci_get_adapter(peci_dev->adapter->nr);
++ if (!adapter)
++ return -ENODEV;
++
++ file->private_data = peci_dev;
++
++ return 0;
++}
++
++static int peci_dev_release(struct inode *inode, struct file *file)
++{
++ struct peci_dev *peci_dev = file->private_data;
++
++ peci_put_adapter(peci_dev->adapter);
++ file->private_data = NULL;
++
++ return 0;
++}
++
++static const struct file_operations peci_dev_fops = {
++ .owner = THIS_MODULE,
++ .unlocked_ioctl = peci_dev_ioctl,
++ .open = peci_dev_open,
++ .release = peci_dev_release,
++ .llseek = no_llseek,
++};
++
++static struct class *peci_dev_class;
++
++static int peci_dev_attach_adapter(struct device *dev, void *dummy)
++{
++ struct peci_adapter *adapter;
++ struct peci_dev *peci_dev;
++ dev_t devt;
++ int ret;
++
++ if (dev->type != &peci_adapter_type)
++ return 0;
++
++ adapter = to_peci_adapter(dev);
++ peci_dev = peci_dev_alloc(adapter);
++ if (IS_ERR(peci_dev))
++ return PTR_ERR(peci_dev);
++
++ cdev_init(&peci_dev->cdev, &peci_dev_fops);
++ peci_dev->cdev.owner = THIS_MODULE;
++ devt = MKDEV(MAJOR(peci_devt), adapter->nr);
++
++ ret = cdev_add(&peci_dev->cdev, devt, 1);
++ if (ret)
++ goto err_put_dev;
++
++ /* register this peci device with the driver core */
++ peci_dev->dev = device_create(peci_dev_class, &adapter->dev, devt, NULL,
++ "peci-%d", adapter->nr);
++ if (IS_ERR(peci_dev->dev)) {
++ ret = PTR_ERR(peci_dev->dev);
++ goto err_del_cdev;
++ }
++
++ pr_info("peci-dev: adapter [%s] registered as minor %d\n",
++ adapter->name, adapter->nr);
++
++ return 0;
++
++err_del_cdev:
++ cdev_del(&peci_dev->cdev);
++err_put_dev:
++ peci_dev_put(peci_dev);
++
++ return ret;
++}
++
++static int peci_dev_detach_adapter(struct device *dev, void *dummy)
++{
++ struct peci_adapter *adapter;
++ struct peci_dev *peci_dev;
++ dev_t devt;
++
++ if (dev->type != &peci_adapter_type)
++ return 0;
++
++ adapter = to_peci_adapter(dev);
++ peci_dev = peci_dev_get_by_minor(adapter->nr);
++ if (!peci_dev)
++ return 0;
++
++ cdev_del(&peci_dev->cdev);
++ devt = peci_dev->dev->devt;
++ peci_dev_put(peci_dev);
++ device_destroy(peci_dev_class, devt);
++
++ pr_info("peci-dev: adapter [%s] unregistered\n", adapter->name);
++
++ return 0;
++}
++
++static int peci_dev_notifier_call(struct notifier_block *nb, ulong action,
++ void *data)
++{
++ struct device *dev = data;
++
++ switch (action) {
++ case BUS_NOTIFY_ADD_DEVICE:
++ return peci_dev_attach_adapter(dev, NULL);
++ case BUS_NOTIFY_DEL_DEVICE:
++ return peci_dev_detach_adapter(dev, NULL);
++ }
++
++ return 0;
++}
++
++static struct notifier_block peci_dev_notifier = {
++ .notifier_call = peci_dev_notifier_call,
++};
++
++static int __init peci_dev_init(void)
++{
++ int ret;
++
++ printk(KERN_INFO "peci /dev entries driver\n");
++
++ ret = alloc_chrdev_region(&peci_devt, 0, PECI_MINORS, "peci");
++ if (ret < 0) {
++ pr_err("peci: Failed to allocate chr dev region!\n");
++ bus_unregister(&peci_bus_type);
++ goto err;
++ }
++
++ peci_dev_class = class_create(THIS_MODULE, "peci-dev");
++ if (IS_ERR(peci_dev_class)) {
++ ret = PTR_ERR(peci_dev_class);
++ goto err_unreg_chrdev;
++ }
++ peci_dev_class->dev_groups = peci_dev_groups;
++
++ /* Keep track of adapters which will be added or removed later */
++ ret = bus_register_notifier(&peci_bus_type, &peci_dev_notifier);
++ if (ret)
++ goto err_destroy_class;
++
++ /* Bind to already existing adapters right away */
++ peci_for_each_dev(NULL, peci_dev_attach_adapter);
++
++ return 0;
++
++err_destroy_class:
++ class_destroy(peci_dev_class);
++err_unreg_chrdev:
++ unregister_chrdev_region(peci_devt, PECI_MINORS);
++err:
++ printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
++
++ return ret;
++}
++
++static void __exit peci_dev_exit(void)
++{
++ bus_unregister_notifier(&peci_bus_type, &peci_dev_notifier);
++ peci_for_each_dev(NULL, peci_dev_detach_adapter);
++ class_destroy(peci_dev_class);
++ unregister_chrdev_region(peci_devt, PECI_MINORS);
++}
++
++module_init(peci_dev_init);
++module_exit(peci_dev_exit);
++
++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
++MODULE_DESCRIPTION("PECI /dev entries driver");
++MODULE_LICENSE("GPL v2");
+diff --git a/drivers/peci/peci-npcm.c b/drivers/peci/peci-npcm.c
+deleted file mode 100644
+index f632365b1416..000000000000
+--- a/drivers/peci/peci-npcm.c
++++ /dev/null
+@@ -1,410 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-// Copyright (c) 2019 Nuvoton Technology corporation.
+-
+-#include <linux/bitfield.h>
+-#include <linux/clk.h>
+-#include <linux/interrupt.h>
+-#include <linux/jiffies.h>
+-#include <linux/module.h>
+-#include <linux/of.h>
+-#include <linux/peci.h>
+-#include <linux/platform_device.h>
+-#include <linux/regmap.h>
+-#include <linux/mfd/syscon.h>
+-#include <linux/reset.h>
+-
+-/* NPCM7xx GCR module */
+-#define NPCM7XX_INTCR3_OFFSET 0x9C
+-#define NPCM7XX_INTCR3_PECIVSEL BIT(19)
+-
+-/* NPCM PECI Registers */
+-#define NPCM_PECI_CTL_STS 0x00
+-#define NPCM_PECI_RD_LENGTH 0x04
+-#define NPCM_PECI_ADDR 0x08
+-#define NPCM_PECI_CMD 0x0C
+-#define NPCM_PECI_CTL2 0x10
+-#define NPCM_PECI_WR_LENGTH 0x1C
+-#define NPCM_PECI_PDDR 0x2C
+-#define NPCM_PECI_DAT_INOUT(n) (0x100 + ((n) * 4))
+-
+-#define NPCM_PECI_MAX_REG 0x200
+-
+-/* NPCM_PECI_CTL_STS - 0x00 : Control Register */
+-#define NPCM_PECI_CTRL_DONE_INT_EN BIT(6)
+-#define NPCM_PECI_CTRL_ABRT_ERR BIT(4)
+-#define NPCM_PECI_CTRL_CRC_ERR BIT(3)
+-#define NPCM_PECI_CTRL_DONE BIT(1)
+-#define NPCM_PECI_CTRL_START_BUSY BIT(0)
+-
+-/* NPCM_PECI_RD_LENGTH - 0x04 : Command Register */
+-#define NPCM_PECI_RD_LEN_MASK GENMASK(6, 0)
+-
+-/* NPCM_PECI_CMD - 0x10 : Command Register */
+-#define NPCM_PECI_CTL2_MASK GENMASK(7, 6)
+-
+-/* NPCM_PECI_WR_LENGTH - 0x1C : Command Register */
+-#define NPCM_PECI_WR_LEN_MASK GENMASK(6, 0)
+-
+-/* NPCM_PECI_PDDR - 0x2C : Command Register */
+-#define NPCM_PECI_PDDR_MASK GENMASK(4, 0)
+-
+-#define NPCM_PECI_INT_MASK (NPCM_PECI_CTRL_ABRT_ERR | \
+- NPCM_PECI_CTRL_CRC_ERR | \
+- NPCM_PECI_CTRL_DONE)
+-
+-#define NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC 50000
+-#define NPCM_PECI_IDLE_CHECK_INTERVAL_USEC 10000
+-#define NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT 1000
+-#define NPCM_PECI_CMD_TIMEOUT_MS_MAX 60000
+-#define NPCM_PECI_HOST_NEG_BIT_RATE_MAX 31
+-#define NPCM_PECI_HOST_NEG_BIT_RATE_MIN 7
+-#define NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT 15
+-#define NPCM_PECI_PULL_DOWN_DEFAULT 0
+-#define NPCM_PECI_PULL_DOWN_MAX 2
+-
+-struct npcm_peci {
+- u32 cmd_timeout_ms;
+- u32 host_bit_rate;
+- struct completion xfer_complete;
+- struct regmap *gcr_regmap;
+- struct peci_adapter *adapter;
+- struct regmap *regmap;
+- u32 status;
+- spinlock_t lock; /* to sync completion status handling */
+- struct device *dev;
+- struct clk *clk;
+- int irq;
+-};
+-
+-static int npcm_peci_xfer_native(struct npcm_peci *priv,
+- struct peci_xfer_msg *msg)
+-{
+- long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms);
+- unsigned long flags;
+- unsigned int msg_rd;
+- u32 cmd_sts;
+- int i, rc;
+-
+- /* Check command sts and bus idle state */
+- rc = regmap_read_poll_timeout(priv->regmap, NPCM_PECI_CTL_STS, cmd_sts,
+- !(cmd_sts & NPCM_PECI_CTRL_START_BUSY),
+- NPCM_PECI_IDLE_CHECK_INTERVAL_USEC,
+- NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC);
+- if (rc)
+- return rc; /* -ETIMEDOUT */
+-
+- spin_lock_irqsave(&priv->lock, flags);
+- reinit_completion(&priv->xfer_complete);
+-
+- regmap_write(priv->regmap, NPCM_PECI_ADDR, msg->addr);
+- regmap_write(priv->regmap, NPCM_PECI_RD_LENGTH,
+- NPCM_PECI_WR_LEN_MASK & msg->rx_len);
+- regmap_write(priv->regmap, NPCM_PECI_WR_LENGTH,
+- NPCM_PECI_WR_LEN_MASK & msg->tx_len);
+-
+- if (msg->tx_len) {
+- regmap_write(priv->regmap, NPCM_PECI_CMD, msg->tx_buf[0]);
+-
+- for (i = 0; i < (msg->tx_len - 1); i++)
+- regmap_write(priv->regmap, NPCM_PECI_DAT_INOUT(i),
+- msg->tx_buf[i + 1]);
+- }
+-
+- priv->status = 0;
+- regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS,
+- NPCM_PECI_CTRL_START_BUSY,
+- NPCM_PECI_CTRL_START_BUSY);
+-
+- spin_unlock_irqrestore(&priv->lock, flags);
+-
+- err = wait_for_completion_interruptible_timeout(&priv->xfer_complete,
+- timeout);
+-
+- spin_lock_irqsave(&priv->lock, flags);
+-
+- regmap_write(priv->regmap, NPCM_PECI_CMD, 0);
+-
+- if (err <= 0 || priv->status != NPCM_PECI_CTRL_DONE) {
+- if (err < 0) { /* -ERESTARTSYS */
+- rc = (int)err;
+- goto err_irqrestore;
+- } else if (err == 0) {
+- dev_dbg(priv->dev, "Timeout waiting for a response!\n");
+- rc = -ETIMEDOUT;
+- goto err_irqrestore;
+- }
+-
+- dev_dbg(priv->dev, "No valid response!\n");
+- rc = -EIO;
+- goto err_irqrestore;
+- }
+-
+- for (i = 0; i < msg->rx_len; i++) {
+- regmap_read(priv->regmap, NPCM_PECI_DAT_INOUT(i), &msg_rd);
+- msg->rx_buf[i] = (u8)msg_rd;
+- }
+-
+-err_irqrestore:
+- spin_unlock_irqrestore(&priv->lock, flags);
+- return rc;
+-}
+-
+-static irqreturn_t npcm_peci_irq_handler(int irq, void *arg)
+-{
+- struct npcm_peci *priv = arg;
+- u32 status_ack = 0;
+- u32 status;
+-
+- spin_lock(&priv->lock);
+- regmap_read(priv->regmap, NPCM_PECI_CTL_STS, &status);
+- priv->status |= (status & NPCM_PECI_INT_MASK);
+-
+- if (status & NPCM_PECI_CTRL_CRC_ERR) {
+- dev_dbg(priv->dev, "PECI_INT_W_FCS_BAD\n");
+- status_ack |= NPCM_PECI_CTRL_CRC_ERR;
+- }
+-
+- if (status & NPCM_PECI_CTRL_ABRT_ERR) {
+- dev_dbg(priv->dev, "NPCM_PECI_CTRL_ABRT_ERR\n");
+- status_ack |= NPCM_PECI_CTRL_ABRT_ERR;
+- }
+-
+- /*
+- * All commands should be ended up with a NPCM_PECI_CTRL_DONE
+- * bit set even in an error case.
+- */
+- if (status & NPCM_PECI_CTRL_DONE) {
+- dev_dbg(priv->dev, "NPCM_PECI_CTRL_DONE\n");
+- status_ack |= NPCM_PECI_CTRL_DONE;
+- complete(&priv->xfer_complete);
+- }
+-
+- regmap_write_bits(priv->regmap, NPCM_PECI_CTL_STS,
+- NPCM_PECI_INT_MASK, status_ack);
+-
+- spin_unlock(&priv->lock);
+- return IRQ_HANDLED;
+-}
+-
+-static int npcm_peci_init_ctrl(struct npcm_peci *priv)
+-{
+- u32 cmd_sts, host_neg_bit_rate = 0, pull_down = 0;
+- int ret;
+- bool volt;
+-
+- priv->clk = devm_clk_get(priv->dev, NULL);
+- if (IS_ERR(priv->clk)) {
+- dev_err(priv->dev, "Failed to get clk source.\n");
+- return PTR_ERR(priv->clk);
+- }
+-
+- ret = clk_prepare_enable(priv->clk);
+- if (ret) {
+- dev_err(priv->dev, "Failed to enable clock.\n");
+- return ret;
+- }
+-
+- ret = of_property_read_u32(priv->dev->of_node, "cmd-timeout-ms",
+- &priv->cmd_timeout_ms);
+- if (ret || priv->cmd_timeout_ms > NPCM_PECI_CMD_TIMEOUT_MS_MAX ||
+- priv->cmd_timeout_ms == 0) {
+- if (ret)
+- dev_warn(priv->dev,
+- "cmd-timeout-ms not found, use default : %u\n",
+- NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT);
+- else
+- dev_warn(priv->dev,
+- "Invalid cmd-timeout-ms : %u. Use default : %u\n",
+- priv->cmd_timeout_ms,
+- NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT);
+-
+- priv->cmd_timeout_ms = NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT;
+- }
+-
+- if (of_device_is_compatible(priv->dev->of_node,
+- "nuvoton,npcm750-peci")) {
+- priv->gcr_regmap = syscon_regmap_lookup_by_compatible
+- ("nuvoton,npcm750-gcr");
+- if (!IS_ERR(priv->gcr_regmap)) {
+- volt = of_property_read_bool(priv->dev->of_node,
+- "high-volt-range");
+- if (volt)
+- regmap_update_bits(priv->gcr_regmap,
+- NPCM7XX_INTCR3_OFFSET,
+- NPCM7XX_INTCR3_PECIVSEL,
+- NPCM7XX_INTCR3_PECIVSEL);
+- else
+- regmap_update_bits(priv->gcr_regmap,
+- NPCM7XX_INTCR3_OFFSET,
+- NPCM7XX_INTCR3_PECIVSEL, 0);
+- }
+- }
+-
+- ret = of_property_read_u32(priv->dev->of_node, "pull-down",
+- &pull_down);
+- if (ret || pull_down > NPCM_PECI_PULL_DOWN_MAX) {
+- if (ret)
+- dev_warn(priv->dev,
+- "pull-down not found, use default : %u\n",
+- NPCM_PECI_PULL_DOWN_DEFAULT);
+- else
+- dev_warn(priv->dev,
+- "Invalid pull-down : %u. Use default : %u\n",
+- pull_down,
+- NPCM_PECI_PULL_DOWN_DEFAULT);
+- pull_down = NPCM_PECI_PULL_DOWN_DEFAULT;
+- }
+-
+- regmap_update_bits(priv->regmap, NPCM_PECI_CTL2, NPCM_PECI_CTL2_MASK,
+- pull_down << 6);
+-
+- ret = of_property_read_u32(priv->dev->of_node, "host-neg-bit-rate",
+- &host_neg_bit_rate);
+- if (ret || host_neg_bit_rate > NPCM_PECI_HOST_NEG_BIT_RATE_MAX ||
+- host_neg_bit_rate < NPCM_PECI_HOST_NEG_BIT_RATE_MIN) {
+- if (ret)
+- dev_warn(priv->dev,
+- "host-neg-bit-rate not found, use default : %u\n",
+- NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT);
+- else
+- dev_warn(priv->dev,
+- "Invalid host-neg-bit-rate : %u. Use default : %u\n",
+- host_neg_bit_rate,
+- NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT);
+- host_neg_bit_rate = NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT;
+- }
+-
+- regmap_update_bits(priv->regmap, NPCM_PECI_PDDR, NPCM_PECI_PDDR_MASK,
+- host_neg_bit_rate);
+-
+- priv->host_bit_rate = clk_get_rate(priv->clk) /
+- (4 * (host_neg_bit_rate + 1));
+-
+- ret = regmap_read_poll_timeout(priv->regmap, NPCM_PECI_CTL_STS, cmd_sts,
+- !(cmd_sts & NPCM_PECI_CTRL_START_BUSY),
+- NPCM_PECI_IDLE_CHECK_INTERVAL_USEC,
+- NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC);
+- if (ret)
+- return ret; /* -ETIMEDOUT */
+-
+- /* PECI interrupt enable */
+- regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS,
+- NPCM_PECI_CTRL_DONE_INT_EN,
+- NPCM_PECI_CTRL_DONE_INT_EN);
+-
+- return 0;
+-}
+-
+-static const struct regmap_config npcm_peci_regmap_config = {
+- .reg_bits = 8,
+- .val_bits = 8,
+- .max_register = NPCM_PECI_MAX_REG,
+- .fast_io = true,
+-};
+-
+-static int npcm_peci_xfer(struct peci_adapter *adapter,
+- struct peci_xfer_msg *msg)
+-{
+- struct npcm_peci *priv = peci_get_adapdata(adapter);
+-
+- return npcm_peci_xfer_native(priv, msg);
+-}
+-
+-static int npcm_peci_probe(struct platform_device *pdev)
+-{
+- struct peci_adapter *adapter;
+- struct npcm_peci *priv;
+- struct resource *res;
+- void __iomem *base;
+- int ret;
+-
+- adapter = peci_alloc_adapter(&pdev->dev, sizeof(*priv));
+- if (!adapter)
+- return -ENOMEM;
+-
+- priv = peci_get_adapdata(adapter);
+- priv->adapter = adapter;
+- priv->dev = &pdev->dev;
+- dev_set_drvdata(&pdev->dev, priv);
+-
+- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+- base = devm_ioremap_resource(&pdev->dev, res);
+- if (IS_ERR(base)) {
+- ret = PTR_ERR(base);
+- goto err_put_adapter_dev;
+- }
+-
+- priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+- &npcm_peci_regmap_config);
+- if (IS_ERR(priv->regmap)) {
+- ret = PTR_ERR(priv->regmap);
+- goto err_put_adapter_dev;
+- }
+-
+- priv->irq = platform_get_irq(pdev, 0);
+- if (!priv->irq) {
+- ret = -ENODEV;
+- goto err_put_adapter_dev;
+- }
+-
+- ret = devm_request_irq(&pdev->dev, priv->irq, npcm_peci_irq_handler,
+- 0, "peci-npcm-irq", priv);
+- if (ret)
+- goto err_put_adapter_dev;
+-
+- init_completion(&priv->xfer_complete);
+- spin_lock_init(&priv->lock);
+-
+- priv->adapter->owner = THIS_MODULE;
+- priv->adapter->dev.of_node = of_node_get(dev_of_node(priv->dev));
+- strlcpy(priv->adapter->name, pdev->name, sizeof(priv->adapter->name));
+- priv->adapter->xfer = npcm_peci_xfer;
+-
+- ret = npcm_peci_init_ctrl(priv);
+- if (ret)
+- goto err_put_adapter_dev;
+-
+- ret = peci_add_adapter(priv->adapter);
+- if (ret)
+- goto err_put_adapter_dev;
+-
+- dev_info(&pdev->dev, "peci bus %d registered, host negotiation bit rate %dHz",
+- priv->adapter->nr, priv->host_bit_rate);
+-
+- return 0;
+-
+-err_put_adapter_dev:
+- put_device(&adapter->dev);
+- return ret;
+-}
+-
+-static int npcm_peci_remove(struct platform_device *pdev)
+-{
+- struct npcm_peci *priv = dev_get_drvdata(&pdev->dev);
+-
+- clk_disable_unprepare(priv->clk);
+- peci_del_adapter(priv->adapter);
+- of_node_put(priv->adapter->dev.of_node);
+-
+- return 0;
+-}
+-
+-static const struct of_device_id npcm_peci_of_table[] = {
+- { .compatible = "nuvoton,npcm750-peci", },
+- { }
+-};
+-MODULE_DEVICE_TABLE(of, npcm_peci_of_table);
+-
+-static struct platform_driver npcm_peci_driver = {
+- .probe = npcm_peci_probe,
+- .remove = npcm_peci_remove,
+- .driver = {
+- .name = "peci-npcm",
+- .of_match_table = of_match_ptr(npcm_peci_of_table),
+- },
+-};
+-module_platform_driver(npcm_peci_driver);
+-
+-MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+-MODULE_DESCRIPTION("NPCM Platform Environment Control Interface (PECI) driver");
+-MODULE_LICENSE("GPL v2");
+diff --git a/include/linux/mfd/intel-peci-client.h b/include/linux/mfd/intel-peci-client.h
+index 8f6d823a59cd..7b62a02e46ee 100644
+--- a/include/linux/mfd/intel-peci-client.h
++++ b/include/linux/mfd/intel-peci-client.h
+@@ -1,5 +1,5 @@
+ /* SPDX-License-Identifier: GPL-2.0 */
+-/* Copyright (c) 2018 Intel Corporation */
++/* Copyright (c) 2018-2019 Intel Corporation */
+
+ #ifndef __LINUX_MFD_INTEL_PECI_CLIENT_H
+ #define __LINUX_MFD_INTEL_PECI_CLIENT_H
+@@ -9,14 +9,15 @@
+ #if IS_ENABLED(CONFIG_X86)
+ #include <asm/intel-family.h>
+ #else
+-/**
++/*
+ * Architectures other than x86 cannot include the header file so define these
+ * at here. These are needed for detecting type of client x86 CPUs behind a PECI
+ * connection.
+ */
+-#define INTEL_FAM6_HASWELL_X 0x3F
+-#define INTEL_FAM6_BROADWELL_X 0x4F
+-#define INTEL_FAM6_SKYLAKE_X 0x55
++#define INTEL_FAM6_HASWELL_X 0x3F
++#define INTEL_FAM6_BROADWELL_X 0x4F
++#define INTEL_FAM6_BROADWELL_XEON_D 0x56
++#define INTEL_FAM6_SKYLAKE_X 0x55
+ #endif
+
+ #define CORE_MAX_ON_HSX 18 /* Max number of cores on Haswell */
+@@ -27,6 +28,10 @@
+ #define CHAN_RANK_MAX_ON_BDX 4 /* Max number of channel ranks on Broadwell */
+ #define DIMM_IDX_MAX_ON_BDX 3 /* Max DIMM index per channel on Broadwell */
+
++#define CORE_MAX_ON_XD 16 /* Max number of cores on Xeon D */
++#define CHAN_RANK_MAX_ON_XD 2 /* Max number of channel ranks on Xeon D */
++#define DIMM_IDX_MAX_ON_XD 2 /* Max DIMM index per channel on Xeon D */
++
+ #define CORE_MAX_ON_SKX 28 /* Max number of cores on Skylake */
+ #define CHAN_RANK_MAX_ON_SKX 6 /* Max number of channel ranks on Skylake */
+ #define DIMM_IDX_MAX_ON_SKX 2 /* Max DIMM index per channel on Skylake */
+@@ -58,7 +63,6 @@ struct cpu_gen_info {
+ /**
+ * struct peci_client_manager - PECI client manager information
+ * @client; pointer to the PECI client
+- * @dev: pointer to the struct device
+ * @name: PECI client manager name
+ * @gen_info: CPU generation info of the detected CPU
+ *
+@@ -67,7 +71,6 @@ struct cpu_gen_info {
+ */
+ struct peci_client_manager {
+ struct peci_client *client;
+- struct device *dev;
+ char name[PECI_NAME_SIZE];
+ const struct cpu_gen_info *gen_info;
+ };
+@@ -93,18 +96,23 @@ peci_client_read_package_config(struct peci_client_manager *priv,
+ u8 index, u16 param, u8 *data)
+ {
+ struct peci_rd_pkg_cfg_msg msg;
+- int rc;
++ int ret;
+
+ msg.addr = priv->client->addr;
+ msg.index = index;
+ msg.param = param;
+ msg.rx_len = 4;
+
+- rc = peci_command(priv->client->adapter, PECI_CMD_RD_PKG_CFG, &msg);
+- if (!rc)
+- memcpy(data, msg.pkg_config, 4);
++ ret = peci_command(priv->client->adapter, PECI_CMD_RD_PKG_CFG, &msg);
++ if (ret)
++ return ret;
++
++ if (msg.cc != PECI_DEV_CC_SUCCESS)
++ return -EAGAIN;
++
++ memcpy(data, msg.pkg_config, 4);
+
+- return rc;
++ return 0;
+ }
+
+ #endif /* __LINUX_MFD_INTEL_PECI_CLIENT_H */
+diff --git a/include/linux/peci.h b/include/linux/peci.h
+index d0e47d45d1d0..6fc424dc2a73 100644
+--- a/include/linux/peci.h
++++ b/include/linux/peci.h
+@@ -1,19 +1,18 @@
+ /* SPDX-License-Identifier: GPL-2.0 */
+-/* Copyright (c) 2018 Intel Corporation */
++/* Copyright (c) 2018-2019 Intel Corporation */
+
+ #ifndef __LINUX_PECI_H
+ #define __LINUX_PECI_H
+
+-#include <linux/cdev.h>
+ #include <linux/device.h>
++#include <linux/mutex.h>
+ #include <linux/peci-ioctl.h>
+-#include <linux/rtmutex.h>
+
+ #define PECI_NAME_SIZE 32
+
+ struct peci_board_info {
+ char type[PECI_NAME_SIZE];
+- unsigned short addr; /* CPU client address */
++ u8 addr; /* CPU client address */
+ struct device_node *of_node;
+ };
+
+@@ -22,29 +21,29 @@ struct peci_board_info {
+ * @owner: owner module of the PECI adpater
+ * @bus_lock: mutex for exclusion of multiple callers
+ * @dev: device interface to this driver
+- * @cdev: character device object to create character device
+ * @nr: the bus number to map
+ * @name: name of the adapter
+ * @userspace_clients_lock: mutex for exclusion of clients handling
+ * @userspace_clients: list of registered clients
+ * @xfer: low-level transfer function pointer of the adapter
+ * @cmd_mask: mask for supportable PECI commands
++ * @use_dma: flag for indicating that adapter uses DMA
+ *
+ * Each PECI adapter can communicate with one or more PECI client children.
+ * These make a small bus, sharing a single wired PECI connection.
+ */
+ struct peci_adapter {
+ struct module *owner;
+- struct rt_mutex bus_lock;
++ struct mutex bus_lock;
+ struct device dev;
+- struct cdev cdev;
+ int nr;
+ char name[PECI_NAME_SIZE];
+ struct mutex userspace_clients_lock; /* clients list mutex */
+ struct list_head userspace_clients;
+ int (*xfer)(struct peci_adapter *adapter,
+ struct peci_xfer_msg *msg);
+- uint cmd_mask;
++ u32 cmd_mask;
++ bool use_dma;
+ };
+
+ static inline struct peci_adapter *to_peci_adapter(void *d)
+@@ -87,8 +86,8 @@ static inline struct peci_client *to_peci_client(void *d)
+ }
+
+ struct peci_device_id {
+- char name[PECI_NAME_SIZE];
+- unsigned long driver_data; /* Data private to the driver */
++ char name[PECI_NAME_SIZE];
++ ulong driver_data; /* Data private to the driver */
+ };
+
+ /**
+@@ -129,13 +128,22 @@ static inline struct peci_driver *to_peci_driver(void *d)
+ /* use a define to avoid include chaining to get THIS_MODULE */
+ #define peci_add_driver(driver) peci_register_driver(THIS_MODULE, driver)
+
++extern struct bus_type peci_bus_type;
++extern struct device_type peci_adapter_type;
++extern struct device_type peci_client_type;
++
+ int peci_register_driver(struct module *owner, struct peci_driver *drv);
+ void peci_del_driver(struct peci_driver *driver);
+ struct peci_client *peci_verify_client(struct device *dev);
+-struct peci_adapter *peci_alloc_adapter(struct device *dev, unsigned int size);
++struct peci_adapter *peci_alloc_adapter(struct device *dev, uint size);
++struct peci_adapter *peci_get_adapter(int nr);
++void peci_put_adapter(struct peci_adapter *adapter);
+ int peci_add_adapter(struct peci_adapter *adapter);
+ void peci_del_adapter(struct peci_adapter *adapter);
+ struct peci_adapter *peci_verify_adapter(struct device *dev);
++int peci_for_each_dev(void *data, int (*fn)(struct device *, void *));
++struct peci_xfer_msg *peci_get_xfer_msg(u8 tx_len, u8 rx_len);
++void peci_put_xfer_msg(struct peci_xfer_msg *msg);
+ int peci_command(struct peci_adapter *adpater, enum peci_cmd cmd, void *vmsg);
+ int peci_get_cpu_id(struct peci_adapter *adapter, u8 addr, u32 *cpu_id);
+
+diff --git a/include/uapi/linux/peci-ioctl.h b/include/uapi/linux/peci-ioctl.h
+index a6dae71cbff5..253fb42e38b7 100644
+--- a/include/uapi/linux/peci-ioctl.h
++++ b/include/uapi/linux/peci-ioctl.h
+@@ -1,5 +1,5 @@
+ /* SPDX-License-Identifier: GPL-2.0 */
+-/* Copyright (c) 2018 Intel Corporation */
++/* Copyright (c) 2018-2019 Intel Corporation */
+
+ #ifndef __PECI_IOCTL_H
+ #define __PECI_IOCTL_H
+@@ -7,136 +7,35 @@
+ #include <linux/ioctl.h>
+ #include <linux/types.h>
+
+-/* Base Address of 48d */
+-#define PECI_BASE_ADDR 0x30 /* The PECI client's default address of 0x30 */
+-#define PECI_OFFSET_MAX 8 /* Max numver of CPU clients */
+-
+-/* PCI Access */
+-#define MAX_PCI_READ_LEN 24 /* Number of bytes of the PCI Space read */
+-
+-#define PCI_BUS0_CPU0 0x00
+-#define PCI_BUS0_CPU1 0x80
+-#define PCI_CPUBUSNO_BUS 0x00
+-#define PCI_CPUBUSNO_DEV 0x08
+-#define PCI_CPUBUSNO_FUNC 0x02
+-#define PCI_CPUBUSNO 0xcc
+-#define PCI_CPUBUSNO_1 0xd0
+-#define PCI_CPUBUSNO_VALID 0xd4
+-
+-/* Package Identifier Read Parameter Value */
+-#define PKG_ID_CPU_ID 0x0000 /* CPUID Info */
+-#define PKG_ID_PLATFORM_ID 0x0001 /* Platform ID */
+-#define PKG_ID_UNCORE_ID 0x0002 /* Uncore Device ID */
+-#define PKG_ID_MAX_THREAD_ID 0x0003 /* Max Thread ID */
+-#define PKG_ID_MICROCODE_REV 0x0004 /* CPU Microcode Update Revision */
+-#define PKG_ID_MACHINE_CHECK_STATUS 0x0005 /* Machine Check Status */
+-
+-/* RdPkgConfig Index */
+-#define MBX_INDEX_CPU_ID 0 /* Package Identifier Read */
+-#define MBX_INDEX_VR_DEBUG 1 /* VR Debug */
+-#define MBX_INDEX_PKG_TEMP_READ 2 /* Package Temperature Read */
+-#define MBX_INDEX_ENERGY_COUNTER 3 /* Energy counter */
+-#define MBX_INDEX_ENERGY_STATUS 4 /* DDR Energy Status */
+-#define MBX_INDEX_WAKE_MODE_BIT 5 /* "Wake on PECI" Mode bit */
+-#define MBX_INDEX_EPI 6 /* Efficient Performance Indication */
+-#define MBX_INDEX_PKG_RAPL_PERF 8 /* Pkg RAPL Performance Status Read */
+-#define MBX_INDEX_PER_CORE_DTS_TEMP 9 /* Per Core DTS Temperature Read */
+-#define MBX_INDEX_DTS_MARGIN 10 /* DTS thermal margin */
+-#define MBX_INDEX_SKT_PWR_THRTL_DUR 11 /* Socket Power Throttled Duration */
+-#define MBX_INDEX_CFG_TDP_CONTROL 12 /* TDP Config Control */
+-#define MBX_INDEX_CFG_TDP_LEVELS 13 /* TDP Config Levels */
+-#define MBX_INDEX_DDR_DIMM_TEMP 14 /* DDR DIMM Temperature */
+-#define MBX_INDEX_CFG_ICCMAX 15 /* Configurable ICCMAX */
+-#define MBX_INDEX_TEMP_TARGET 16 /* Temperature Target Read */
+-#define MBX_INDEX_CURR_CFG_LIMIT 17 /* Current Config Limit */
+-#define MBX_INDEX_DIMM_TEMP_READ 20 /* Package Thermal Status Read */
+-#define MBX_INDEX_DRAM_IMC_TMP_READ 22 /* DRAM IMC Temperature Read */
+-#define MBX_INDEX_DDR_CH_THERM_STAT 23 /* DDR Channel Thermal Status */
+-#define MBX_INDEX_PKG_POWER_LIMIT1 26 /* Package Power Limit1 */
+-#define MBX_INDEX_PKG_POWER_LIMIT2 27 /* Package Power Limit2 */
+-#define MBX_INDEX_TDP 28 /* Thermal design power minimum */
+-#define MBX_INDEX_TDP_HIGH 29 /* Thermal design power maximum */
+-#define MBX_INDEX_TDP_UNITS 30 /* Units for power/energy registers */
+-#define MBX_INDEX_RUN_TIME 31 /* Accumulated Run Time */
+-#define MBX_INDEX_CONSTRAINED_TIME 32 /* Thermally Constrained Time Read */
+-#define MBX_INDEX_TURBO_RATIO 33 /* Turbo Activation Ratio */
+-#define MBX_INDEX_DDR_RAPL_PL1 34 /* DDR RAPL PL1 */
+-#define MBX_INDEX_DDR_PWR_INFO_HIGH 35 /* DRAM Power Info Read (high) */
+-#define MBX_INDEX_DDR_PWR_INFO_LOW 36 /* DRAM Power Info Read (low) */
+-#define MBX_INDEX_DDR_RAPL_PL2 37 /* DDR RAPL PL2 */
+-#define MBX_INDEX_DDR_RAPL_STATUS 38 /* DDR RAPL Performance Status */
+-#define MBX_INDEX_DDR_HOT_ABSOLUTE 43 /* DDR Hottest Dimm Absolute Temp */
+-#define MBX_INDEX_DDR_HOT_RELATIVE 44 /* DDR Hottest Dimm Relative Temp */
+-#define MBX_INDEX_DDR_THROTTLE_TIME 45 /* DDR Throttle Time */
+-#define MBX_INDEX_DDR_THERM_STATUS 46 /* DDR Thermal Status */
+-#define MBX_INDEX_TIME_AVG_TEMP 47 /* Package time-averaged temperature */
+-#define MBX_INDEX_TURBO_RATIO_LIMIT 49 /* Turbo Ratio Limit Read */
+-#define MBX_INDEX_HWP_AUTO_OOB 53 /* HWP Autonomous Out-of-band */
+-#define MBX_INDEX_DDR_WARM_BUDGET 55 /* DDR Warm Power Budget */
+-#define MBX_INDEX_DDR_HOT_BUDGET 56 /* DDR Hot Power Budget */
+-#define MBX_INDEX_PKG_PSYS_PWR_LIM3 57 /* Package/Psys Power Limit3 */
+-#define MBX_INDEX_PKG_PSYS_PWR_LIM1 58 /* Package/Psys Power Limit1 */
+-#define MBX_INDEX_PKG_PSYS_PWR_LIM2 59 /* Package/Psys Power Limit2 */
+-#define MBX_INDEX_PKG_PSYS_PWR_LIM4 60 /* Package/Psys Power Limit4 */
+-#define MBX_INDEX_PERF_LIMIT_REASON 65 /* Performance Limit Reasons */
+-
+-/* WrPkgConfig Index */
+-#define MBX_INDEX_DIMM_AMBIENT 19
+-#define MBX_INDEX_DIMM_TEMP 24
++/* The PECI client's default address of 0x30 */
++#define PECI_BASE_ADDR 0x30
++
++/* Max number of CPU clients */
++#define PECI_OFFSET_MAX 8
++
++/* PECI read/write data buffer size max */
++#define PECI_BUFFER_SIZE 255
+
+ /* Device Specific Completion Code (CC) Definition */
+-#define DEV_PECI_CC_SUCCESS 0x40
+-#define DEV_PECI_CC_TIMEOUT 0x80
+-#define DEV_PECI_CC_OUT_OF_RESOURCE 0x81
+-#define DEV_PECI_CC_UNAVAIL_RESOURCE 0x82
+-#define DEV_PECI_CC_INVALID_REQ 0x90
++#define PECI_DEV_CC_SUCCESS 0x40
++#define PECI_DEV_CC_NEED_RETRY 0x80
++#define PECI_DEV_CC_OUT_OF_RESOURCE 0x81
++#define PECI_DEV_CC_UNAVAIL_RESOURCE 0x82
++#define PECI_DEV_CC_INVALID_REQ 0x90
++#define PECI_DEV_CC_MCA_ERROR 0x91
++#define PECI_DEV_CC_CATASTROPHIC_MCA_ERROR 0x93
++#define PECI_DEV_CC_FATAL_MCA_DETECTED 0x94
++#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB 0x98
++#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB_IERR 0x9B
++#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB_MCA 0x9C
+
+ /* Completion Code mask to check retry needs */
+-#define DEV_PECI_CC_RETRY_CHECK_MASK 0xf0
+-#define DEV_PECI_CC_NEED_RETRY 0x80
++#define PECI_DEV_CC_RETRY_CHECK_MASK 0xf0
+
+ /* Skylake EDS says to retry for 250ms */
+-#define DEV_PECI_RETRY_TIME_MS 250
+-#define DEV_PECI_RETRY_INTERVAL_USEC 10000
+-#define DEV_PECI_RETRY_BIT 0x01
+-
+-#define GET_TEMP_WR_LEN 1
+-#define GET_TEMP_RD_LEN 2
+-#define GET_TEMP_PECI_CMD 0x01
+-
+-#define GET_DIB_WR_LEN 1
+-#define GET_DIB_RD_LEN 8
+-#define GET_DIB_PECI_CMD 0xf7
+-
+-#define RDPKGCFG_WRITE_LEN 5
+-#define RDPKGCFG_READ_LEN_BASE 1
+-#define RDPKGCFG_PECI_CMD 0xa1
+-
+-#define WRPKGCFG_WRITE_LEN_BASE 6
+-#define WRPKGCFG_READ_LEN 1
+-#define WRPKGCFG_PECI_CMD 0xa5
+-
+-#define RDIAMSR_WRITE_LEN 5
+-#define RDIAMSR_READ_LEN 9
+-#define RDIAMSR_PECI_CMD 0xb1
+-
+-#define WRIAMSR_PECI_CMD 0xb5
+-
+-#define RDPCICFG_WRITE_LEN 6
+-#define RDPCICFG_READ_LEN 5
+-#define RDPCICFG_PECI_CMD 0x61
+-
+-#define WRPCICFG_PECI_CMD 0x65
+-
+-#define RDPCICFGLOCAL_WRITE_LEN 5
+-#define RDPCICFGLOCAL_READ_LEN_BASE 1
+-#define RDPCICFGLOCAL_PECI_CMD 0xe1
+-
+-#define WRPCICFGLOCAL_WRITE_LEN_BASE 6
+-#define WRPCICFGLOCAL_READ_LEN 1
+-#define WRPCICFGLOCAL_PECI_CMD 0xe5
+-
+-#define PECI_BUFFER_SIZE 32
++#define PECI_DEV_RETRY_TIME_MS 250
++#define PECI_DEV_RETRY_INTERVAL_USEC 10000
++#define PECI_DEV_RETRY_BIT 0x01
+
+ /**
+ * enum peci_cmd - PECI client commands
+@@ -186,11 +85,12 @@ enum peci_cmd {
+ * raw PECI transfer
+ */
+ struct peci_xfer_msg {
+- __u8 addr;
+- __u8 tx_len;
+- __u8 rx_len;
+- __u8 tx_buf[PECI_BUFFER_SIZE];
+- __u8 rx_buf[PECI_BUFFER_SIZE];
++ __u8 addr;
++ __u8 tx_len;
++ __u8 rx_len;
++ __u8 padding;
++ __u8 *tx_buf;
++ __u8 *rx_buf;
+ } __attribute__((__packed__));
+
+ /**
+@@ -202,7 +102,8 @@ struct peci_xfer_msg {
+ * powered-off, etc.
+ */
+ struct peci_ping_msg {
+- __u8 addr;
++ __u8 addr;
++ __u8 padding[3];
+ } __attribute__((__packed__));
+
+ /**
+@@ -216,8 +117,13 @@ struct peci_ping_msg {
+ * command.
+ */
+ struct peci_get_dib_msg {
+- __u8 addr;
+- __u64 dib;
++#define PECI_GET_DIB_WR_LEN 1
++#define PECI_GET_DIB_RD_LEN 8
++#define PECI_GET_DIB_CMD 0xf7
++
++ __u8 addr;
++ __u8 padding[3];
++ __u64 dib;
+ } __attribute__((__packed__));
+
+ /**
+@@ -232,8 +138,13 @@ struct peci_get_dib_msg {
+ * below the maximum processor junction temperature.
+ */
+ struct peci_get_temp_msg {
+- __u8 addr;
+- __s16 temp_raw;
++#define PECI_GET_TEMP_WR_LEN 1
++#define PECI_GET_TEMP_RD_LEN 2
++#define PECI_GET_TEMP_CMD 0x01
++
++ __u8 addr;
++ __u8 padding;
++ __s16 temp_raw;
+ } __attribute__((__packed__));
+
+ /**
+@@ -242,6 +153,7 @@ struct peci_get_temp_msg {
+ * @index: encoding index for the requested service
+ * @param: specific data being requested
+ * @rx_len: number of data to be read in bytes
++ * @cc: completion code
+ * @pkg_config: package config data to be read
+ *
+ * The RdPkgConfig() command provides read access to the Package Configuration
+@@ -251,11 +163,73 @@ struct peci_get_temp_msg {
+ * DIMM temperatures and so on.
+ */
+ struct peci_rd_pkg_cfg_msg {
+- __u8 addr;
+- __u8 index;
+- __u16 param;
+- __u8 rx_len;
+- __u8 pkg_config[4];
++#define PECI_RDPKGCFG_WRITE_LEN 5
++#define PECI_RDPKGCFG_READ_LEN_BASE 1
++#define PECI_RDPKGCFG_CMD 0xa1
++
++ __u8 addr;
++ __u8 index;
++#define PECI_MBX_INDEX_CPU_ID 0 /* Package Identifier Read */
++#define PECI_MBX_INDEX_VR_DEBUG 1 /* VR Debug */
++#define PECI_MBX_INDEX_PKG_TEMP_READ 2 /* Package Temperature Read */
++#define PECI_MBX_INDEX_ENERGY_COUNTER 3 /* Energy counter */
++#define PECI_MBX_INDEX_ENERGY_STATUS 4 /* DDR Energy Status */
++#define PECI_MBX_INDEX_WAKE_MODE_BIT 5 /* "Wake on PECI" Mode bit */
++#define PECI_MBX_INDEX_EPI 6 /* Efficient Performance Indication */
++#define PECI_MBX_INDEX_PKG_RAPL_PERF 8 /* Pkg RAPL Performance Status Read */
++#define PECI_MBX_INDEX_PER_CORE_DTS_TEMP 9 /* Per Core DTS Temperature Read */
++#define PECI_MBX_INDEX_DTS_MARGIN 10 /* DTS thermal margin */
++#define PECI_MBX_INDEX_SKT_PWR_THRTL_DUR 11 /* Socket Power Throttled Duration */
++#define PECI_MBX_INDEX_CFG_TDP_CONTROL 12 /* TDP Config Control */
++#define PECI_MBX_INDEX_CFG_TDP_LEVELS 13 /* TDP Config Levels */
++#define PECI_MBX_INDEX_DDR_DIMM_TEMP 14 /* DDR DIMM Temperature */
++#define PECI_MBX_INDEX_CFG_ICCMAX 15 /* Configurable ICCMAX */
++#define PECI_MBX_INDEX_TEMP_TARGET 16 /* Temperature Target Read */
++#define PECI_MBX_INDEX_CURR_CFG_LIMIT 17 /* Current Config Limit */
++#define PECI_MBX_INDEX_DIMM_TEMP_READ 20 /* Package Thermal Status Read */
++#define PECI_MBX_INDEX_DRAM_IMC_TMP_READ 22 /* DRAM IMC Temperature Read */
++#define PECI_MBX_INDEX_DDR_CH_THERM_STAT 23 /* DDR Channel Thermal Status */
++#define PECI_MBX_INDEX_PKG_POWER_LIMIT1 26 /* Package Power Limit1 */
++#define PECI_MBX_INDEX_PKG_POWER_LIMIT2 27 /* Package Power Limit2 */
++#define PECI_MBX_INDEX_TDP 28 /* Thermal design power minimum */
++#define PECI_MBX_INDEX_TDP_HIGH 29 /* Thermal design power maximum */
++#define PECI_MBX_INDEX_TDP_UNITS 30 /* Units for power/energy registers */
++#define PECI_MBX_INDEX_RUN_TIME 31 /* Accumulated Run Time */
++#define PECI_MBX_INDEX_CONSTRAINED_TIME 32 /* Thermally Constrained Time Read */
++#define PECI_MBX_INDEX_TURBO_RATIO 33 /* Turbo Activation Ratio */
++#define PECI_MBX_INDEX_DDR_RAPL_PL1 34 /* DDR RAPL PL1 */
++#define PECI_MBX_INDEX_DDR_PWR_INFO_HIGH 35 /* DRAM Power Info Read (high) */
++#define PECI_MBX_INDEX_DDR_PWR_INFO_LOW 36 /* DRAM Power Info Read (low) */
++#define PECI_MBX_INDEX_DDR_RAPL_PL2 37 /* DDR RAPL PL2 */
++#define PECI_MBX_INDEX_DDR_RAPL_STATUS 38 /* DDR RAPL Performance Status */
++#define PECI_MBX_INDEX_DDR_HOT_ABSOLUTE 43 /* DDR Hottest Dimm Absolute Temp */
++#define PECI_MBX_INDEX_DDR_HOT_RELATIVE 44 /* DDR Hottest Dimm Relative Temp */
++#define PECI_MBX_INDEX_DDR_THROTTLE_TIME 45 /* DDR Throttle Time */
++#define PECI_MBX_INDEX_DDR_THERM_STATUS 46 /* DDR Thermal Status */
++#define PECI_MBX_INDEX_TIME_AVG_TEMP 47 /* Package time-averaged temperature */
++#define PECI_MBX_INDEX_TURBO_RATIO_LIMIT 49 /* Turbo Ratio Limit Read */
++#define PECI_MBX_INDEX_HWP_AUTO_OOB 53 /* HWP Autonomous Out-of-band */
++#define PECI_MBX_INDEX_DDR_WARM_BUDGET 55 /* DDR Warm Power Budget */
++#define PECI_MBX_INDEX_DDR_HOT_BUDGET 56 /* DDR Hot Power Budget */
++#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM3 57 /* Package/Psys Power Limit3 */
++#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM1 58 /* Package/Psys Power Limit1 */
++#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM2 59 /* Package/Psys Power Limit2 */
++#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM4 60 /* Package/Psys Power Limit4 */
++#define PECI_MBX_INDEX_PERF_LIMIT_REASON 65 /* Performance Limit Reasons */
++
++ __u16 param;
++/* When index is PECI_MBX_INDEX_CPU_ID */
++#define PECI_PKG_ID_CPU_ID 0x0000 /* CPUID Info */
++#define PECI_PKG_ID_PLATFORM_ID 0x0001 /* Platform ID */
++#define PECI_PKG_ID_UNCORE_ID 0x0002 /* Uncore Device ID */
++#define PECI_PKG_ID_MAX_THREAD_ID 0x0003 /* Max Thread ID */
++#define PECI_PKG_ID_MICROCODE_REV 0x0004 /* CPU Microcode Update Revision */
++#define PECI_PKG_ID_MACHINE_CHECK_STATUS 0x0005 /* Machine Check Status */
++
++ __u8 rx_len;
++ __u8 cc;
++ __u8 padding[2];
++ __u8 pkg_config[4];
+ } __attribute__((__packed__));
+
+ /**
+@@ -264,6 +238,7 @@ struct peci_rd_pkg_cfg_msg {
+ * @index: encoding index for the requested service
+ * @param: specific data being requested
+ * @tx_len: number of data to be written in bytes
++ * @cc: completion code
+ * @value: package config data to be written
+ *
+ * The WrPkgConfig() command provides write access to the Package Configuration
+@@ -272,11 +247,20 @@ struct peci_rd_pkg_cfg_msg {
+ * may include power limiting, thermal averaging constant programming and so on.
+ */
+ struct peci_wr_pkg_cfg_msg {
+- __u8 addr;
+- __u8 index;
+- __u16 param;
+- __u8 tx_len;
+- __u32 value;
++#define PECI_WRPKGCFG_WRITE_LEN_BASE 6
++#define PECI_WRPKGCFG_READ_LEN 1
++#define PECI_WRPKGCFG_CMD 0xa5
++
++ __u8 addr;
++ __u8 index;
++#define PECI_MBX_INDEX_DIMM_AMBIENT 19
++#define PECI_MBX_INDEX_DIMM_TEMP 24
++
++ __u16 param;
++ __u8 tx_len;
++ __u8 cc;
++ __u8 padding[2];
++ __u32 value;
+ } __attribute__((__packed__));
+
+ /**
+@@ -284,16 +268,47 @@ struct peci_wr_pkg_cfg_msg {
+ * @addr: address of the client
+ * @thread_id: ID of the specific logical processor
+ * @address: address of MSR to read from
++ * @cc: completion code
+ * @value: data to be read
+ *
+ * The RdIAMSR() PECI command provides read access to Model Specific Registers
+ * (MSRs) defined in the processor's Intel Architecture (IA).
+ */
+ struct peci_rd_ia_msr_msg {
+- __u8 addr;
+- __u8 thread_id;
+- __u16 address;
+- __u64 value;
++#define PECI_RDIAMSR_WRITE_LEN 5
++#define PECI_RDIAMSR_READ_LEN 9
++#define PECI_RDIAMSR_CMD 0xb1
++
++ __u8 addr;
++ __u8 thread_id;
++ __u16 address;
++ __u8 cc;
++ __u8 padding[3];
++ __u64 value;
++} __attribute__((__packed__));
++
++/**
++ * struct peci_wr_ia_msr_msg - WrIAMSR command
++ * @addr: address of the client
++ * @thread_id: ID of the specific logical processor
++ * @address: address of MSR to write to
++ * @tx_len: number of data to be written in bytes
++ * @cc: completion code
++ * @value: data to be written
++ *
++ * The WrIAMSR() PECI command provides write access to Model Specific Registers
++ * (MSRs) defined in the processor's Intel Architecture (IA).
++ */
++struct peci_wr_ia_msr_msg {
++#define PECI_WRIAMSR_CMD 0xb5
++
++ __u8 addr;
++ __u8 thread_id;
++ __u16 address;
++ __u8 tx_len;
++ __u8 cc;
++ __u8 padding[2];
++ __u64 value;
+ } __attribute__((__packed__));
+
+ /**
+@@ -303,6 +318,7 @@ struct peci_rd_ia_msr_msg {
+ * @device: PCI device number
+ * @function: specific function to read from
+ * @reg: specific register to read from
++ * @cc: completion code
+ * @pci_config: config data to be read
+ *
+ * The RdPCIConfig() command provides sideband read access to the PCI
+@@ -310,12 +326,56 @@ struct peci_rd_ia_msr_msg {
+ * processor.
+ */
+ struct peci_rd_pci_cfg_msg {
+- __u8 addr;
+- __u8 bus;
+- __u8 device;
+- __u8 function;
+- __u16 reg;
+- __u8 pci_config[4];
++#define PECI_RDPCICFG_WRITE_LEN 6
++#define PECI_RDPCICFG_READ_LEN 5
++#define PECI_RDPCICFG_READ_LEN_MAX 24
++#define PECI_RDPCICFG_CMD 0x61
++
++ __u8 addr;
++ __u8 bus;
++#define PECI_PCI_BUS0_CPU0 0x00
++#define PECI_PCI_BUS0_CPU1 0x80
++#define PECI_PCI_CPUBUSNO_BUS 0x00
++#define PECI_PCI_CPUBUSNO_DEV 0x08
++#define PECI_PCI_CPUBUSNO_FUNC 0x02
++#define PECI_PCI_CPUBUSNO 0xcc
++#define PECI_PCI_CPUBUSNO_1 0xd0
++#define PECI_PCI_CPUBUSNO_VALID 0xd4
++
++ __u8 device;
++ __u8 function;
++ __u16 reg;
++ __u8 cc;
++ __u8 padding[1];
++ __u8 pci_config[4];
++} __attribute__((__packed__));
++
++/**
++ * struct peci_wr_pci_cfg_msg - WrPCIConfig command
++ * @addr: address of the client
++ * @bus: PCI bus number
++ * @device: PCI device number
++ * @function: specific function to write to
++ * @reg: specific register to write to
++ * @tx_len: number of data to be written in bytes
++ * @cc: completion code
++ * @pci_config: config data to be written
++ *
++ * The RdPCIConfig() command provides sideband write access to the PCI
++ * configuration space maintained in downstream devices external to the
++ * processor.
++ */
++struct peci_wr_pci_cfg_msg {
++#define PECI_WRPCICFG_CMD 0x65
++
++ __u8 addr;
++ __u8 bus;
++ __u8 device;
++ __u8 function;
++ __u16 reg;
++ __u8 tx_len;
++ __u8 cc;
++ __u8 pci_config[4];
+ } __attribute__((__packed__));
+
+ /**
+@@ -326,6 +386,7 @@ struct peci_rd_pci_cfg_msg {
+ * @function: specific function to read from
+ * @reg: specific register to read from
+ * @rx_len: number of data to be read in bytes
++ * @cc: completion code
+ * @pci_config: config data to be read
+ *
+ * The RdPCIConfigLocal() command provides sideband read access to the PCI
+@@ -333,13 +394,18 @@ struct peci_rd_pci_cfg_msg {
+ * processor IIO and uncore registers within the PCI configuration space.
+ */
+ struct peci_rd_pci_cfg_local_msg {
+- __u8 addr;
+- __u8 bus;
+- __u8 device;
+- __u8 function;
+- __u16 reg;
+- __u8 rx_len;
+- __u8 pci_config[4];
++#define PECI_RDPCICFGLOCAL_WRITE_LEN 5
++#define PECI_RDPCICFGLOCAL_READ_LEN_BASE 1
++#define PECI_RDPCICFGLOCAL_CMD 0xe1
++
++ __u8 addr;
++ __u8 bus;
++ __u8 device;
++ __u8 function;
++ __u16 reg;
++ __u8 rx_len;
++ __u8 cc;
++ __u8 pci_config[4];
+ } __attribute__((__packed__));
+
+ /**
+@@ -350,6 +416,7 @@ struct peci_rd_pci_cfg_local_msg {
+ * @function: specific function to read from
+ * @reg: specific register to read from
+ * @tx_len: number of data to be written in bytes
++ * @cc: completion code
+ * @value: config data to be written
+ *
+ * The WrPCIConfigLocal() command provides sideband write access to the PCI
+@@ -357,13 +424,18 @@ struct peci_rd_pci_cfg_local_msg {
+ * access this space even before BIOS enumeration of the system buses.
+ */
+ struct peci_wr_pci_cfg_local_msg {
+- __u8 addr;
+- __u8 bus;
+- __u8 device;
+- __u8 function;
+- __u16 reg;
+- __u8 tx_len;
+- __u32 value;
++#define PECI_WRPCICFGLOCAL_WRITE_LEN_BASE 6
++#define PECI_WRPCICFGLOCAL_READ_LEN 1
++#define PECI_WRPCICFGLOCAL_CMD 0xe5
++
++ __u8 addr;
++ __u8 bus;
++ __u8 device;
++ __u8 function;
++ __u16 reg;
++ __u8 tx_len;
++ __u8 cc;
++ __u32 value;
+ } __attribute__((__packed__));
+
+ #define PECI_IOC_BASE 0xb7
+@@ -389,9 +461,15 @@ struct peci_wr_pci_cfg_local_msg {
+ #define PECI_IOC_RD_IA_MSR \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_IA_MSR, struct peci_rd_ia_msr_msg)
+
++#define PECI_IOC_WR_IA_MSR \
++ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_IA_MSR, struct peci_wr_ia_msr_msg)
++
+ #define PECI_IOC_RD_PCI_CFG \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_PCI_CFG, struct peci_rd_pci_cfg_msg)
+
++#define PECI_IOC_WR_PCI_CFG \
++ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_PCI_CFG, struct peci_wr_pci_cfg_msg)
++
+ #define PECI_IOC_RD_PCI_CFG_LOCAL \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_PCI_CFG_LOCAL, \
+ struct peci_rd_pci_cfg_local_msg)
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0019-Add-I2C-IPMB-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0019-Add-I2C-IPMB-support.patch
new file mode 100644
index 000000000..391d6f816
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0019-Add-I2C-IPMB-support.patch
@@ -0,0 +1,426 @@
+From 59e2471d9bf64fa7d539520ef66cf5f33c0b0e55 Mon Sep 17 00:00:00 2001
+From: Haiyue Wang <haiyue.wang@linux.intel.com>
+Date: Tue, 13 Feb 2018 14:28:12 +0800
+Subject: [PATCH] i2c: slave-mqueue: add mqueue driver to receive ipmi message
+
+Some protocols over I2C are designed for bi-directional transferring
+messages by using I2C Master Write protocol. Like the MCTP (Management
+Component Transport Protocol) and IPMB (Intelligent Platform Management
+Bus), they both require that the userspace can receive messages from
+I2C dirvers under slave mode.
+
+This new slave mqueue backend is used to receive and queue messages, it
+will exposes these messages to userspace by sysfs bin file.
+
+Signed-off-by: Haiyue Wang <haiyue.wang@linux.intel.com>
+---
+ Documentation/i2c/slave-mqueue-backend.rst | 125 +++++++++++++++++
+ drivers/i2c/Kconfig | 23 +++
+ drivers/i2c/Makefile | 1 +
+ drivers/i2c/i2c-slave-mqueue.c | 217 +++++++++++++++++++++++++++++
+ 4 files changed, 366 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..3966cf0ab8da
+--- /dev/null
++++ b/Documentation/i2c/slave-mqueue-backend.rst
+@@ -0,0 +1,125 @@
++.. 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 efc3354d60ae..04fb851f2c82 100644
+--- a/drivers/i2c/Kconfig
++++ b/drivers/i2c/Kconfig
+@@ -118,6 +118,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/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..d1745ce5f
--- /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,620 @@
+From 7d5cd323d3b05a00f8b8a6eb38a5a1ec7925660a 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>
+---
+ .../devicetree/bindings/misc/aspeed-sio.txt | 18 +
+ arch/arm/boot/dts/aspeed-g4.dtsi | 7 +
+ arch/arm/boot/dts/aspeed-g5.dtsi | 7 +
+ drivers/misc/Kconfig | 9 +
+ drivers/misc/Makefile | 1 +
+ drivers/misc/aspeed-lpc-sio.c | 450 +++++++++++++++++++++
+ include/uapi/linux/aspeed-lpc-sio.h | 44 ++
+ 7 files changed, 536 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/misc/aspeed-sio.txt
+ create mode 100644 drivers/misc/aspeed-lpc-sio.c
+ create mode 100644 include/uapi/linux/aspeed-lpc-sio.h
+
+diff --git a/Documentation/devicetree/bindings/misc/aspeed-sio.txt b/Documentation/devicetree/bindings/misc/aspeed-sio.txt
+new file mode 100644
+index 000000000000..3530c2b02f5c
+--- /dev/null
++++ b/Documentation/devicetree/bindings/misc/aspeed-sio.txt
+@@ -0,0 +1,18 @@
++* 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 e8bcfc90bf7c..a87fd5ee1c84 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -340,6 +340,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 e5c0ba0f87c8..a568699c28f4 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -451,6 +451,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/misc/Kconfig b/drivers/misc/Kconfig
+index 00d1c547ece7..3ffb18f915e8 100644
+--- a/drivers/misc/Kconfig
++++ b/drivers/misc/Kconfig
+@@ -493,6 +493,15 @@ 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_SIO
++ depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
++ tristate "Aspeed ast2400/2500 HOST LPC SIO support"
++ 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 (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
+diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
+index 768278b059c3..de2d5c6d186c 100644
+--- a/drivers/misc/Makefile
++++ b/drivers/misc/Makefile
+@@ -53,6 +53,7 @@ obj-$(CONFIG_GENWQE) += genwqe/
+ obj-$(CONFIG_ECHO) += echo/
+ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
+ obj-$(CONFIG_CXL_BASE) += cxl/
++obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.o
+ obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
+ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
+ obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o
+--- /dev/null
++++ b/drivers/misc/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..303bff0b1
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch
@@ -0,0 +1,597 @@
+From 3437db37b2f39a69505338546d9f846338de6c88 Mon Sep 17 00:00:00 2001
+From: Haiyue Wang <haiyue.wang@linux.intel.com>
+Date: Sat, 24 Feb 2018 11:12:32 +0800
+Subject: [PATCH] eSPI: add ASPEED AST2500 eSPI driver to boot a host with PCH
+ runs on eSPI
+
+When PCH works under eSPI mode, the PMC (Power Management Controller) in
+PCH is waiting for SUS_ACK from BMC after it alerts SUS_WARN. It is in
+dead loop if no SUS_ACK assert. This is the basic requirement for the BMC
+works as eSPI slave.
+
+Also for the host power on / off actions, from BMC side, the following VW
+(Virtual Wire) messages are done in firmware:
+1. SLAVE_BOOT_LOAD_DONE / SLAVE_BOOT_LOAD_STATUS
+2. SUS_ACK
+3. OOB_RESET_ACK
+4. HOST_RESET_ACK
+
+Signed-off-by: Haiyue Wang <haiyue.wang@linux.intel.com>
+---
+ .../devicetree/bindings/misc/aspeed,espi-slave.txt | 20 ++
+ Documentation/misc-devices/espi-slave.rst | 119 +++++++
+ arch/arm/boot/dts/aspeed-g5.dtsi | 4 +
+ drivers/misc/Kconfig | 8 +
+ drivers/misc/Makefile | 1 +
+ drivers/misc/aspeed-espi-slave.c | 353 +++++++++++++++++++++
+ 6 files changed, 505 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..4f5d47ecc882
+--- /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"
++
++ - 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..185acd71bd26
+--- /dev/null
++++ b/Documentation/misc-devices/espi-slave.rst
+@@ -0,0 +1,119 @@
++.. 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 da9e903808bc..01d27e845982 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -267,6 +267,7 @@
+ clocks = <&syscon ASPEED_CLK_APB>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
++ status = "disabled";
+ };
+
+ sgpio: sgpio@1e780200 {
+@@ -361,6 +362,9 @@
+ reg = <0x1e6ee000 0x100>;
+ interrupts = <23>;
+ status = "disabled";
++ clocks = <&syscon ASPEED_CLK_GATE_ESPICLK>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&pinctrl_espi_default>;
+ };
+
+ lpc: lpc@1e789000 {
+diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
+index d4ed3777462a..8b1fcf741411 100644
+--- a/drivers/misc/Kconfig
++++ b/drivers/misc/Kconfig
+@@ -485,6 +485,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 ASPEED_LPC_CTRL
+ depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
+ tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control"
+diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
+index 7b018962cad3..89b051f82391 100644
+--- a/drivers/misc/Makefile
++++ b/drivers/misc/Makefile
+@@ -54,6 +54,7 @@ obj-$(CONFIG_ECHO) += echo/
+ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
+ obj-$(CONFIG_CXL_BASE) += cxl/
+ obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.o
++obj-$(CONFIG_ASPEED_ESPI_SLAVE) += aspeed-espi-slave.o
+ obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
+ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
+ obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o
+diff --git a/drivers/misc/aspeed-espi-slave.c b/drivers/misc/aspeed-espi-slave.c
+new file mode 100644
+index 000000000000..36ae867ca6f9
+--- /dev/null
++++ b/drivers/misc/aspeed-espi-slave.c
+@@ -0,0 +1,353 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2012-2015, ASPEED Technology Inc.
++ * Copyright (c) 2015-2018, Intel Corporation.
++ */
++
++#include <linux/atomic.h>
++#include <linux/clk.h>
++#include <linux/errno.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/poll.h>
++#include <linux/regmap.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/timer.h>
++
++#define DEVICE_NAME "aspeed-espi-slave"
++
++#define ESPI_CTRL 0x00
++#define ESPI_CTRL_SW_RESET GENMASK(31, 24)
++#define ESPI_CTRL_OOB_CHRDY BIT(4)
++#define ESPI_ISR 0x08
++#define ESPI_ISR_HW_RESET BIT(31)
++#define ESPI_ISR_VW_SYS_EVT1 BIT(22)
++#define ESPI_ISR_VW_SYS_EVT BIT(8)
++#define ESPI_IER 0x0C
++#define ESPI_DATA_PORT 0x28
++#define ESPI_DATA_PORT_ASPEED 0xa8
++#define ESPI_SYS_IER 0x94
++#define ESPI_SYS_EVENT 0x98
++#define ESPI_SYS_INT_T0 0x110
++#define ESPI_SYS_INT_T1 0x114
++#define ESPI_SYS_INT_T2 0x118
++#define ESPI_SYS_ISR 0x11C
++#define ESPI_SYSEVT_HOST_RST_ACK BIT(27)
++#define ESPI_SYSEVT_SLAVE_BOOT_STATUS BIT(23)
++#define ESPI_SYSEVT_SLAVE_BOOT_DONE BIT(20)
++#define ESPI_SYSEVT_OOB_RST_ACK BIT(16)
++#define ESPI_SYSEVT_HOST_RST_WARN BIT(8)
++#define ESPI_SYSEVT_OOB_RST_WARN BIT(6)
++#define ESPI_SYSEVT_PLT_RST_N BIT(5)
++#define ESPI_SYS1_IER 0x100
++#define ESPI_SYS1_EVENT 0x104
++#define ESPI_SYS1_INT_T0 0x120
++#define ESPI_SYS1_INT_T1 0x124
++#define ESPI_SYS1_INT_T2 0x128
++#define ESPI_SYS1_ISR 0x12C
++#define ESPI_SYSEVT1_SUS_ACK BIT(20)
++#define ESPI_SYSEVT1_SUS_WARN BIT(0)
++
++struct aspeed_espi_slave_data {
++ struct regmap *map;
++ struct clk *clk;
++};
++
++static void aspeed_espi_slave_sys_event(struct platform_device *pdev)
++{
++ struct aspeed_espi_slave_data *priv = platform_get_drvdata(pdev);
++ struct device *dev = &pdev->dev;
++ u32 sts, evt;
++
++ if (regmap_read(priv->map, ESPI_SYS_ISR, &sts) != 0 ||
++ regmap_read(priv->map, ESPI_SYS_EVENT, &evt) != 0) {
++ dev_err(dev, "regmap_read failed\n");
++ return;
++ }
++
++ dev_dbg(dev, "sys: sts = %08x, evt = %08x\n", sts, evt);
++
++ if ((evt & ESPI_SYSEVT_SLAVE_BOOT_STATUS) == 0) {
++ dev_info(dev, "Setting espi slave boot done\n");
++ regmap_write(priv->map, ESPI_SYS_EVENT,
++ evt | ESPI_SYSEVT_SLAVE_BOOT_STATUS |
++ ESPI_SYSEVT_SLAVE_BOOT_DONE);
++ }
++#if 0
++ if (sts & ESPI_SYSEVT_HOST_RST_WARN) {
++ dev_info(dev, "ESPI_SYSEVT_HOST_RST_WARN; %s ack\n",
++ (evt & ESPI_SYSEVT_HOST_RST_WARN ? "send" : "clr"));
++ regmap_write_bits(priv->map, ESPI_SYS_EVENT,
++ ESPI_SYSEVT_HOST_RST_ACK,
++ evt & ESPI_SYSEVT_HOST_RST_WARN ?
++ ESPI_SYSEVT_HOST_RST_ACK : 0);
++ }
++ if (sts & ESPI_SYSEVT_OOB_RST_WARN) {
++ dev_info(dev, "ESPI_SYSEVT_OOB_RST_WARN; %s ack\n",
++ (evt & ESPI_SYSEVT_OOB_RST_WARN ? "send" : "clr"));
++ regmap_write_bits(priv->map, ESPI_SYS_EVENT,
++ ESPI_SYSEVT_OOB_RST_ACK,
++ evt & ESPI_SYSEVT_OOB_RST_WARN ?
++ ESPI_SYSEVT_OOB_RST_ACK : 0);
++ }
++#else
++ if (sts & ESPI_SYSEVT_HOST_RST_WARN) {
++ if (evt & ESPI_SYSEVT_HOST_RST_WARN) {
++ dev_info(dev, "ESPI_SYSEVT_HOST_RST_WARN; send ack\n");
++ regmap_write_bits(priv->map, ESPI_SYS_EVENT,
++ ESPI_SYSEVT_HOST_RST_ACK, ESPI_SYSEVT_HOST_RST_ACK);
++ }
++ }
++ if (sts & ESPI_SYSEVT_OOB_RST_WARN) {
++ if (evt & ESPI_SYSEVT_OOB_RST_WARN) {
++ dev_info(dev, "ESPI_SYSEVT_OOB_RST_WARN; send ack\n");
++ regmap_write_bits(priv->map, ESPI_SYS_EVENT,
++ ESPI_SYSEVT_OOB_RST_ACK, ESPI_SYSEVT_OOB_RST_ACK);
++ }
++ }
++#endif
++ regmap_write(priv->map, ESPI_SYS_ISR, sts);
++}
++
++static void aspeed_espi_slave_sys1_event(struct platform_device *pdev)
++{
++ struct aspeed_espi_slave_data *priv = platform_get_drvdata(pdev);
++ struct device *dev = &pdev->dev;
++ u32 sts, evt;
++
++ if (regmap_read(priv->map, ESPI_SYS1_ISR, &sts) != 0 ||
++ regmap_read(priv->map, ESPI_SYS1_EVENT, &evt) != 0) {
++ dev_err(dev, "regmap_read failed\n");
++ return;
++ }
++ dev_dbg(dev, "sys1: sts = %08x, evt = %08x\n", sts, evt);
++
++#if 0
++ if (sts & ESPI_SYSEVT1_SUS_WARN) {
++ dev_info(dev, "ESPI_SYSEVT1_SUS_WARN; %s ack\n",
++ (evt & ESPI_SYSEVT1_SUS_WARN ? "send" : "clr"));
++ regmap_write_bits(priv->map, ESPI_SYS1_EVENT,
++ ESPI_SYSEVT1_SUS_ACK,
++ evt & ESPI_SYSEVT1_SUS_WARN ?
++ ESPI_SYSEVT1_SUS_ACK : 0);
++ }
++#else
++ if (sts & ESPI_SYSEVT1_SUS_WARN) {
++ if (evt & ESPI_SYSEVT1_SUS_WARN) {
++ dev_info(dev, "ESPI_SYSEVT_OOB_RST_WARN; send ack\n");
++ regmap_write_bits(priv->map, ESPI_SYS1_EVENT,
++ ESPI_SYSEVT1_SUS_ACK, ESPI_SYSEVT1_SUS_ACK);
++ }
++ }
++#endif
++ regmap_write(priv->map, ESPI_SYS1_ISR, sts);
++}
++
++static void aspeed_espi_slave_boot_ack(struct platform_device *pdev)
++{
++ struct aspeed_espi_slave_data *priv = platform_get_drvdata(pdev);
++ struct device *dev = &pdev->dev;
++ u32 evt;
++
++ if (regmap_read(priv->map, ESPI_SYS_EVENT, &evt) == 0 &&
++ (evt & ESPI_SYSEVT_SLAVE_BOOT_STATUS) == 0) {
++ dev_info(dev, "Setting espi slave boot done\n");
++ regmap_write(priv->map, ESPI_SYS_EVENT,
++ evt | ESPI_SYSEVT_SLAVE_BOOT_STATUS |
++ ESPI_SYSEVT_SLAVE_BOOT_DONE);
++ }
++
++ if (regmap_read(priv->map, ESPI_SYS1_EVENT, &evt) == 0 &&
++ (evt & ESPI_SYSEVT1_SUS_WARN) != 0 &&
++ (evt & ESPI_SYSEVT1_SUS_ACK) == 0) {
++ dev_info(dev, "Boot SUS WARN set; send ack\n");
++ regmap_write(priv->map, ESPI_SYS1_EVENT,
++ evt | ESPI_SYSEVT1_SUS_ACK);
++ }
++}
++
++static irqreturn_t aspeed_espi_slave_irq(int irq, void *arg)
++{
++ struct platform_device *pdev = arg;
++ struct aspeed_espi_slave_data *priv = platform_get_drvdata(pdev);
++ struct device *dev = &pdev->dev;
++ u32 sts;
++
++ if (regmap_read(priv->map, ESPI_ISR, &sts) != 0) {
++ dev_err(dev, "regmap_read failed\n");
++ return IRQ_NONE;
++ }
++
++ dev_dbg(dev, "ESPI_ISR: %08x\n", sts);
++
++ if (sts & ESPI_ISR_VW_SYS_EVT)
++ aspeed_espi_slave_sys_event(pdev);
++
++ if (sts & ESPI_ISR_VW_SYS_EVT1)
++ aspeed_espi_slave_sys1_event(pdev);
++
++ /*
++ if (sts & ESPI_ISR_HW_RESET) {
++ regmap_write_bits(priv->map, ESPI_CTRL,
++ ESPI_CTRL_SW_RESET, 0);
++ regmap_write_bits(priv->map, ESPI_CTRL,
++ ESPI_CTRL_SW_RESET, ESPI_CTRL_SW_RESET);
++
++ aspeed_espi_slave_boot_ack(pdev);
++ }
++ */
++
++ regmap_write(priv->map, ESPI_ISR, sts);
++
++ return IRQ_HANDLED;
++}
++
++/* 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). PLTRST_N : Dual Edge 1 0 0
++ */
++#define ESPI_SYS_INT_T0_SET 0x00000000
++#define ESPI_SYS_INT_T1_SET 0x00000000
++#define ESPI_SYS_INT_T2_SET \
++(ESPI_SYSEVT_HOST_RST_WARN | ESPI_SYSEVT_OOB_RST_WARN | ESPI_SYSEVT_PLT_RST_N)
++#define ESPI_SYS_INT_SET \
++(ESPI_SYSEVT_HOST_RST_WARN | ESPI_SYSEVT_OOB_RST_WARN | ESPI_SYSEVT_PLT_RST_N)
++
++/* Setup Interrupt Type/Enable of System Event 1 from Master
++ * T2 T1 T0
++ * 1). SUS_WARN : Rising Edge 0 0 1
++ */
++#define ESPI_SYS1_INT_T0_SET ESPI_SYSEVT1_SUS_WARN
++#define ESPI_SYS1_INT_T1_SET 0x00000000
++#define ESPI_SYS1_INT_T2_SET 0x00000000
++#define ESPI_SYS1_INT_SET ESPI_SYSEVT1_SUS_WARN
++
++static int aspeed_espi_slave_config_irq(struct platform_device *pdev)
++{
++ struct aspeed_espi_slave_data *priv = platform_get_drvdata(pdev);
++ struct device *dev = &pdev->dev;
++ int irq;
++ int rc;
++
++ irq = platform_get_irq(pdev, 0);
++ if (irq < 0)
++ return irq;
++
++ regmap_write_bits(priv->map, ESPI_CTRL, ESPI_CTRL_OOB_CHRDY,
++ ESPI_CTRL_OOB_CHRDY);
++
++ regmap_write(priv->map, ESPI_SYS_INT_T0, ESPI_SYS_INT_T0_SET);
++ regmap_write(priv->map, ESPI_SYS_INT_T1, ESPI_SYS_INT_T1_SET);
++ regmap_write(priv->map, ESPI_SYS_INT_T2, ESPI_SYS_INT_T2_SET);
++ regmap_write(priv->map, ESPI_SYS_IER, ESPI_SYS_INT_SET);
++
++ regmap_write(priv->map, ESPI_SYS1_INT_T0, ESPI_SYS1_INT_T0_SET);
++ regmap_write(priv->map, ESPI_SYS1_INT_T1, ESPI_SYS1_INT_T1_SET);
++ regmap_write(priv->map, ESPI_SYS1_INT_T2, ESPI_SYS1_INT_T2_SET);
++ regmap_write(priv->map, ESPI_SYS1_IER, ESPI_SYS1_INT_SET);
++
++ regmap_write(priv->map, ESPI_IER, 0xFFFFFFFF);
++
++ aspeed_espi_slave_boot_ack(pdev);
++
++ rc = devm_request_irq(dev, irq, aspeed_espi_slave_irq, IRQF_SHARED,
++ dev_name(dev), pdev);
++ if (rc < 0)
++ return rc;
++
++ return 0;
++}
++
++static const struct regmap_config espi_slave_regmap_cfg = {
++ .reg_bits = 32,
++ .reg_stride = 4,
++ .val_bits = 32,
++ .max_register = ESPI_SYS1_ISR,
++};
++
++static int aspeed_espi_slave_probe(struct platform_device *pdev)
++{
++ struct aspeed_espi_slave_data *priv;
++ struct device *dev = &pdev->dev;
++ struct resource *res;
++ void __iomem *regs;
++ int rc;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ 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->map = devm_regmap_init_mmio(dev, regs, &espi_slave_regmap_cfg);
++ if (IS_ERR(priv->map))
++ return PTR_ERR(priv->map);
++
++ priv->clk = devm_clk_get(dev, NULL);
++ if (IS_ERR(priv->clk)) {
++ dev_err(dev, "couldn't get clock\n");
++ return PTR_ERR(priv->clk);
++ }
++ rc = clk_prepare_enable(priv->clk);
++ if (rc) {
++ dev_err(dev, "couldn't enable clock\n");
++ return rc;
++ }
++
++ dev_set_name(dev, DEVICE_NAME);
++
++ platform_set_drvdata(pdev, priv);
++
++ rc = aspeed_espi_slave_config_irq(pdev);
++ if (rc) {
++ platform_set_drvdata(pdev, NULL);
++ goto err;
++ }
++
++ dev_info(dev, "aspeed,ast2500-espi-slave probe complete\n");
++ return 0;
++
++err:
++ clk_disable_unprepare(priv->clk);
++ return rc;
++}
++
++
++static int aspeed_espi_slave_remove(struct platform_device *pdev)
++{
++ struct aspeed_espi_slave_data *priv = platform_get_drvdata(pdev);
++
++ clk_disable_unprepare(priv->clk);
++
++ return 0;
++}
++
++static const struct of_device_id of_espi_slave_match_table[] = {
++ { .compatible = "aspeed,ast2500-espi-slave" },
++ { }
++};
++MODULE_DEVICE_TABLE(of, of_espi_slave_match_table);
++
++static struct platform_driver aspeed_espi_slave_driver = {
++ .driver = {
++ .name = DEVICE_NAME,
++ .of_match_table = of_match_ptr(of_espi_slave_match_table),
++ },
++ .probe = aspeed_espi_slave_probe,
++ .remove = aspeed_espi_slave_remove,
++};
++module_platform_driver(aspeed_espi_slave_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
++MODULE_DESCRIPTION("Linux device interface to the eSPI slave");
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0026-Add-support-for-new-PECI-commands.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0026-Add-support-for-new-PECI-commands.patch
new file mode 100644
index 000000000..1532a5642
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0026-Add-support-for-new-PECI-commands.patch
@@ -0,0 +1,392 @@
+From d1d221aedb9b366e3477f253386b1cf829c56193 Mon Sep 17 00:00:00 2001
+From: "Jason M. Bills" <jason.m.bills@intel.com>
+Date: Wed, 4 Apr 2018 13:52:39 -0700
+Subject: [PATCH] Add support for new PECI commands
+
+Signed-off-by: Jason M. Bills <jason.m.bills@intel.com>
+---
+ drivers/peci/peci-core.c | 226 ++++++++++++++++++++++++++++++++++++++++
+ include/uapi/linux/peci-ioctl.h | 104 ++++++++++++++++++
+ 2 files changed, 330 insertions(+)
+
+diff --git a/drivers/peci/peci-core.c b/drivers/peci/peci-core.c
+index e282f9ef383b..9af11accc1d4 100644
+--- a/drivers/peci/peci-core.c
++++ b/drivers/peci/peci-core.c
+@@ -345,6 +345,9 @@ static int peci_scan_cmd_mask(struct peci_adapter *adapter)
+ adapter->cmd_mask |= BIT(PECI_CMD_GET_TEMP);
+ adapter->cmd_mask |= BIT(PECI_CMD_GET_DIB);
+ adapter->cmd_mask |= BIT(PECI_CMD_PING);
++ adapter->cmd_mask |= BIT(PECI_CMD_RD_END_PT_CFG);
++ adapter->cmd_mask |= BIT(PECI_CMD_CRASHDUMP_DISC);
++ adapter->cmd_mask |= BIT(PECI_CMD_CRASHDUMP_GET_FRAME);
+
+ out:
+ peci_put_xfer_msg(msg);
+@@ -693,6 +696,226 @@ static int peci_cmd_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
+ return ret;
+ }
+
++static int peci_cmd_rd_end_pt_cfg(struct peci_adapter *adapter, void *vmsg)
++{
++ struct peci_rd_end_pt_cfg_msg *umsg = vmsg;
++ struct peci_xfer_msg *msg = NULL;
++ u32 address;
++ int ret;
++
++ switch (umsg->msg_type) {
++ case PECI_RDENDPTCFG_TYPE_LOCAL_PCI:
++ case PECI_RDENDPTCFG_TYPE_PCI:
++ /*
++ * Per the PECI spec, the read length must be a byte, word,
++ * or dword
++ */
++ if (umsg->rx_len != 1 && umsg->rx_len != 2 &&
++ umsg->rx_len != 4) {
++ dev_dbg(&adapter->dev,
++ "Invalid read length, rx_len: %d\n",
++ umsg->rx_len);
++ return -EINVAL;
++ }
++
++ msg = peci_get_xfer_msg(PECI_RDENDPTCFG_PCI_WRITE_LEN,
++ PECI_RDENDPTCFG_READ_LEN_BASE +
++ umsg->rx_len);
++ if (!msg)
++ return -ENOMEM;
++
++ address = umsg->params.pci_cfg.reg; /* [11:0] - Register */
++ address |= (u32)umsg->params.pci_cfg.function
++ << 12; /* [14:12] - Function */
++ address |= (u32)umsg->params.pci_cfg.device
++ << 15; /* [19:15] - Device */
++ address |= (u32)umsg->params.pci_cfg.bus
++ << 20; /* [27:20] - Bus */
++ /* [31:28] - Reserved */
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_RDENDPTCFG_CMD;
++ msg->tx_buf[1] = 0x00; /* request byte for Host ID|Retry bit */
++ msg->tx_buf[2] = umsg->msg_type; /* Message Type */
++ msg->tx_buf[3] = 0x00; /* Endpoint ID */
++ msg->tx_buf[4] = 0x00; /* Reserved */
++ msg->tx_buf[5] = 0x00; /* Reserved */
++ msg->tx_buf[6] = PECI_RDENDPTCFG_ADDR_TYPE_PCI; /* Addr Type */
++ msg->tx_buf[7] = umsg->params.pci_cfg.seg; /* PCI Segment */
++ msg->tx_buf[8] = (u8)address; /* LSB - PCI Config Address */
++ msg->tx_buf[9] = (u8)(address >> 8); /* PCI Config Address */
++ msg->tx_buf[10] = (u8)(address >> 16); /* PCI Config Address */
++ msg->tx_buf[11] =
++ (u8)(address >> 24); /* MSB - PCI Config Address */
++ break;
++
++ case PECI_RDENDPTCFG_TYPE_MMIO:
++ /*
++ * Per the PECI spec, the read length must be a byte, word,
++ * dword, or qword
++ */
++ if (umsg->rx_len != 1 && umsg->rx_len != 2 &&
++ umsg->rx_len != 4 && umsg->rx_len != 8) {
++ dev_dbg(&adapter->dev,
++ "Invalid read length, rx_len: %d\n",
++ umsg->rx_len);
++ return -EINVAL;
++ }
++ /*
++ * Per the PECI spec, the address type must specify either DWORD
++ * or QWORD
++ */
++ if (umsg->params.mmio.addr_type !=
++ PECI_RDENDPTCFG_ADDR_TYPE_MMIO_D &&
++ umsg->params.mmio.addr_type !=
++ PECI_RDENDPTCFG_ADDR_TYPE_MMIO_Q) {
++ dev_dbg(&adapter->dev,
++ "Invalid address type, addr_type: %d\n",
++ umsg->params.mmio.addr_type);
++ return -EINVAL;
++ }
++
++ msg = peci_get_xfer_msg(PECI_RDENDPTCFG_MMIO_D_WRITE_LEN,
++ PECI_RDENDPTCFG_READ_LEN_BASE +
++ umsg->rx_len);
++ if (!msg)
++ return -ENOMEM;
++
++ address = umsg->params.mmio.function; /* [2:0] - Function */
++ address |= (u32)umsg->params.mmio.device
++ << 3; /* [7:3] - Device */
++
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_RDENDPTCFG_CMD;
++ msg->tx_buf[1] = 0x00; /* request byte for Host ID|Retry bit */
++ msg->tx_buf[2] = umsg->msg_type; /* Message Type */
++ msg->tx_buf[3] = 0x00; /* Endpoint ID */
++ msg->tx_buf[4] = 0x00; /* Reserved */
++ msg->tx_buf[5] = umsg->params.mmio.bar; /* BAR # */
++ msg->tx_buf[6] = umsg->params.mmio.addr_type; /* Address Type */
++ msg->tx_buf[7] = umsg->params.mmio.seg; /* PCI Segment */
++ msg->tx_buf[8] = (u8)address; /* Function/Device */
++ msg->tx_buf[9] = umsg->params.mmio.bus; /* PCI Bus */
++ msg->tx_buf[10] = (u8)umsg->params.mmio
++ .offset; /* LSB - Register Offset */
++ msg->tx_buf[11] = (u8)(umsg->params.mmio.offset
++ >> 8); /* Register Offset */
++ msg->tx_buf[12] = (u8)(umsg->params.mmio.offset
++ >> 16); /* Register Offset */
++ msg->tx_buf[13] = (u8)(umsg->params.mmio.offset
++ >> 24); /* MSB - DWORD Register Offset */
++ if (umsg->params.mmio.addr_type ==
++ PECI_RDENDPTCFG_ADDR_TYPE_MMIO_Q) {
++ msg->tx_len = PECI_RDENDPTCFG_MMIO_Q_WRITE_LEN;
++ msg->tx_buf[14] = (u8)(umsg->params.mmio.offset
++ >> 32); /* Register Offset */
++ msg->tx_buf[15] = (u8)(umsg->params.mmio.offset
++ >> 40); /* Register Offset */
++ msg->tx_buf[16] = (u8)(umsg->params.mmio.offset
++ >> 48); /* Register Offset */
++ msg->tx_buf[17] =
++ (u8)(umsg->params.mmio.offset
++ >> 56); /* MSB - QWORD Register Offset */
++ }
++ break;
++
++ default:
++ return -EINVAL;
++ }
++
++ ret = peci_xfer_with_retries(adapter, msg, false);
++ if (!ret)
++ memcpy(umsg->data, &msg->rx_buf[1], umsg->rx_len);
++
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
++
++ return ret;
++}
++
++static int peci_cmd_crashdump_disc(struct peci_adapter *adapter, void *vmsg)
++{
++ struct peci_crashdump_disc_msg *umsg = vmsg;
++ struct peci_xfer_msg *msg;
++ int ret;
++
++ /* Per the EDS, the read length must be a byte, word, or qword */
++ if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 8) {
++ dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n",
++ umsg->rx_len);
++ return -EINVAL;
++ }
++
++ msg = peci_get_xfer_msg(PECI_CRASHDUMP_DISC_WRITE_LEN,
++ PECI_CRASHDUMP_DISC_READ_LEN_BASE +
++ umsg->rx_len);
++ if (!msg)
++ return -ENOMEM;
++
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_CRASHDUMP_CMD;
++ msg->tx_buf[1] = 0x00; /* request byte for Host ID | Retry bit */
++ /* Host ID is 0 for PECI 3.0 */
++ msg->tx_buf[2] = PECI_CRASHDUMP_DISC_VERSION;
++ msg->tx_buf[3] = PECI_CRASHDUMP_DISC_OPCODE;
++ msg->tx_buf[4] = umsg->subopcode;
++ msg->tx_buf[5] = umsg->param0;
++ msg->tx_buf[6] = (u8)umsg->param1;
++ msg->tx_buf[7] = (u8)(umsg->param1 >> 8);
++ msg->tx_buf[8] = umsg->param2;
++
++ ret = peci_xfer_with_retries(adapter, msg, false);
++ if (!ret)
++ memcpy(umsg->data, &msg->rx_buf[1], umsg->rx_len);
++
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
++
++ return ret;
++}
++
++static int peci_cmd_crashdump_get_frame(struct peci_adapter *adapter,
++ void *vmsg)
++{
++ struct peci_crashdump_get_frame_msg *umsg = vmsg;
++ struct peci_xfer_msg *msg;
++ int ret;
++
++ /* Per the EDS, the read length must be a qword or dqword */
++ if (umsg->rx_len != 8 && umsg->rx_len != 16) {
++ dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n",
++ umsg->rx_len);
++ return -EINVAL;
++ }
++
++ msg = peci_get_xfer_msg(PECI_CRASHDUMP_GET_FRAME_WRITE_LEN,
++ PECI_CRASHDUMP_GET_FRAME_READ_LEN_BASE +
++ umsg->rx_len);
++ if (!msg)
++ return -ENOMEM;
++
++ msg->addr = umsg->addr;
++ msg->tx_buf[0] = PECI_CRASHDUMP_CMD;
++ msg->tx_buf[1] = 0x00; /* request byte for Host ID | Retry bit */
++ /* Host ID is 0 for PECI 3.0 */
++ msg->tx_buf[2] = PECI_CRASHDUMP_GET_FRAME_VERSION;
++ msg->tx_buf[3] = PECI_CRASHDUMP_GET_FRAME_OPCODE;
++ msg->tx_buf[4] = (u8)umsg->param0;
++ msg->tx_buf[5] = (u8)(umsg->param0 >> 8);
++ msg->tx_buf[6] = (u8)umsg->param1;
++ msg->tx_buf[7] = (u8)(umsg->param1 >> 8);
++ msg->tx_buf[8] = (u8)umsg->param2;
++ msg->tx_buf[8] = (u8)(umsg->param2 >> 8);
++
++ ret = peci_xfer_with_retries(adapter, msg, false);
++ if (!ret)
++ memcpy(umsg->data, &msg->rx_buf[1], umsg->rx_len);
++
++ umsg->cc = msg->rx_buf[0];
++ peci_put_xfer_msg(msg);
++
++ return ret;
++}
++
+ typedef int (*peci_cmd_fn_type)(struct peci_adapter *, void *);
+
+ static const peci_cmd_fn_type peci_cmd_fn[PECI_CMD_MAX] = {
+@@ -708,6 +931,9 @@ static const peci_cmd_fn_type peci_cmd_fn[PECI_CMD_MAX] = {
+ peci_cmd_wr_pci_cfg,
+ peci_cmd_rd_pci_cfg_local,
+ peci_cmd_wr_pci_cfg_local,
++ peci_cmd_rd_end_pt_cfg,
++ peci_cmd_crashdump_disc,
++ peci_cmd_crashdump_get_frame,
+ };
+
+ /**
+diff --git a/include/uapi/linux/peci-ioctl.h b/include/uapi/linux/peci-ioctl.h
+index 4c28abe2c17a..e67b0735f606 100644
+--- a/include/uapi/linux/peci-ioctl.h
++++ b/include/uapi/linux/peci-ioctl.h
+@@ -72,6 +72,9 @@ enum peci_cmd {
+ PECI_CMD_WR_PCI_CFG,
+ PECI_CMD_RD_PCI_CFG_LOCAL,
+ PECI_CMD_WR_PCI_CFG_LOCAL,
++ PECI_CMD_RD_END_PT_CFG,
++ PECI_CMD_CRASHDUMP_DISC,
++ PECI_CMD_CRASHDUMP_GET_FRAME,
+ PECI_CMD_MAX
+ };
+
+@@ -439,6 +442,95 @@ struct peci_wr_pci_cfg_local_msg {
+ __u32 value;
+ } __attribute__((__packed__));
+
++struct peci_rd_end_pt_cfg_msg {
++#define PECI_RDENDPTCFG_PCI_WRITE_LEN 0x0C
++#define PECI_RDENDPTCFG_MMIO_D_WRITE_LEN 0x0E
++#define PECI_RDENDPTCFG_MMIO_Q_WRITE_LEN 0x12
++#define PECI_RDENDPTCFG_READ_LEN_BASE 1
++#define PECI_RDENDPTCFG_CMD 0xC1
++
++ __u8 addr;
++ __u8 msg_type;
++#define PECI_RDENDPTCFG_TYPE_LOCAL_PCI 0x03
++#define PECI_RDENDPTCFG_TYPE_PCI 0x04
++#define PECI_RDENDPTCFG_TYPE_MMIO 0x05
++
++ union {
++ struct {
++ __u8 seg;
++ __u8 bus;
++ __u8 device;
++ __u8 function;
++ __u16 reg;
++ } pci_cfg;
++ struct {
++ __u8 seg;
++ __u8 bus;
++ __u8 device;
++ __u8 function;
++ __u8 bar;
++ __u8 addr_type;
++#define PECI_RDENDPTCFG_ADDR_TYPE_PCI 0x04
++#define PECI_RDENDPTCFG_ADDR_TYPE_MMIO_D 0x05
++#define PECI_RDENDPTCFG_ADDR_TYPE_MMIO_Q 0x06
++
++ __u64 offset;
++ } mmio;
++ } params;
++ __u8 rx_len;
++ __u8 cc;
++ __u8 padding[2];
++ __u8 data[8];
++} __attribute__((__packed__));
++
++/* Crashdump Agent */
++#define PECI_CRASHDUMP_CORE 0x00
++#define PECI_CRASHDUMP_TOR 0x01
++
++/* Crashdump Agent Param */
++#define PECI_CRASHDUMP_PAYLOAD_SIZE 0x00
++
++/* Crashdump Agent Data Param */
++#define PECI_CRASHDUMP_AGENT_ID 0x00
++#define PECI_CRASHDUMP_AGENT_PARAM 0x01
++
++struct peci_crashdump_disc_msg {
++ __u8 addr;
++ __u8 subopcode;
++#define PECI_CRASHDUMP_ENABLED 0x00
++#define PECI_CRASHDUMP_NUM_AGENTS 0x01
++#define PECI_CRASHDUMP_AGENT_DATA 0x02
++
++ __u8 cc;
++ __u8 param0;
++ __u16 param1;
++ __u8 param2;
++ __u8 rx_len;
++ __u8 data[8];
++} __attribute__((__packed__));
++
++struct peci_crashdump_get_frame_msg {
++#define PECI_CRASHDUMP_DISC_WRITE_LEN 9
++#define PECI_CRASHDUMP_DISC_READ_LEN_BASE 1
++#define PECI_CRASHDUMP_DISC_VERSION 0
++#define PECI_CRASHDUMP_DISC_OPCODE 1
++#define PECI_CRASHDUMP_GET_FRAME_WRITE_LEN 10
++#define PECI_CRASHDUMP_GET_FRAME_READ_LEN_BASE 1
++#define PECI_CRASHDUMP_GET_FRAME_VERSION 0
++#define PECI_CRASHDUMP_GET_FRAME_OPCODE 3
++#define PECI_CRASHDUMP_CMD 0x71
++
++ __u8 addr;
++ __u8 padding0;
++ __u16 param0;
++ __u16 param1;
++ __u16 param2;
++ __u8 rx_len;
++ __u8 cc;
++ __u8 padding1[2];
++ __u8 data[16];
++} __attribute__((__packed__));
++
+ #define PECI_IOC_BASE 0xb7
+
+ #define PECI_IOC_XFER \
+@@ -479,4 +571,16 @@ struct peci_wr_pci_cfg_local_msg {
+ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_PCI_CFG_LOCAL, \
+ struct peci_wr_pci_cfg_local_msg)
+
++#define PECI_IOC_RD_END_PT_CFG \
++ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_END_PT_CFG, \
++ struct peci_rd_end_pt_cfg_msg)
++
++#define PECI_IOC_CRASHDUMP_DISC \
++ _IOWR(PECI_IOC_BASE, PECI_CMD_CRASHDUMP_DISC, \
++ struct peci_crashdump_disc_msg)
++
++#define PECI_IOC_CRASHDUMP_GET_FRAME \
++ _IOWR(PECI_IOC_BASE, PECI_CMD_CRASHDUMP_GET_FRAME, \
++ struct peci_crashdump_get_frame_msg)
++
+ #endif /* __PECI_IOCTL_H */
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch
new file mode 100644
index 000000000..56dca7345
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch
@@ -0,0 +1,93 @@
+From 43470f186979483ba6c1e6374c7ea3a129622862 Mon Sep 17 00:00:00 2001
+From: "Corona, Ernesto" <ernesto.corona@intel.com>
+Date: Mon, 1 Mar 2019 11:46:09 -0700
+Subject: [PATCH] Update AST2500d JTAG driver. Step 1
+
+Update AST2500d JTAG driver. Remove Legacy driver but keep headers.
+
+Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com>
+---
+ 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..e984623ba
--- /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,142 @@
+From f4c13be7b95899d6eca3fa7ae38224bc9c7d7902 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 | 25 +++++++++++++++++++++++++
+ drivers/i2c/i2c-slave-mqueue.c | 22 ++++++++++++++++++++++
+ 2 files changed, 47 insertions(+)
+
+diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
+index 6c8b38fd6e64..77dbd37b7b51 100644
+--- a/drivers/i2c/busses/i2c-aspeed.c
++++ b/drivers/i2c/busses/i2c-aspeed.c
+@@ -166,6 +166,19 @@ 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) \
++ if (dump_debug && bus->adap.nr == dump_debug_bus_id) { \
++ char dump_info[100] = {0,}; \
++ snprintf(dump_info, sizeof(dump_info), \
++ "%s (bus_id:%d, addr:0x%02x, flags:0x%02x): ", \
++ __func__, bus->adap.nr, addr, flags); \
++ print_hex_dump(KERN_ERR, dump_info, DUMP_PREFIX_NONE, 16, 1, \
++ buf, len, true); \
++ }
++
+ static int aspeed_i2c_reset(struct aspeed_i2c_bus *bus);
+
+ static int aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus)
+@@ -655,6 +668,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;
+@@ -697,6 +711,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;
+ }
+
+@@ -1064,6 +1083,12 @@ static struct platform_driver aspeed_i2c_bus_driver = {
+ };
+ module_platform_driver(aspeed_i2c_bus_driver);
+
++module_param_named(dump_debug, dump_debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(dump_debug, "debug flag for dump printing");
++module_param_named(dump_debug_bus_id, dump_debug_bus_id, int,
++ S_IRUGO | S_IWUSR);
++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..4548088e1922 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,18 @@ 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) \
++ if (dump_debug && client->adapter->nr == dump_debug_bus_id) { \
++ char dump_info[100] = {0,}; \
++ snprintf(dump_info, sizeof(dump_info), \
++ "%s (bus_id:%d): ", __func__, client->adapter->nr); \
++ print_hex_dump(KERN_ERR, dump_info, DUMP_PREFIX_NONE, 16, 1, \
++ buf, len, true); \
++ }
++
+ static int i2c_slave_mqueue_callback(struct i2c_client *client,
+ enum i2c_slave_event event, u8 *val)
+ {
+@@ -101,6 +114,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 +145,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 +228,12 @@ static struct i2c_driver i2c_slave_mqueue_driver = {
+ };
+ module_i2c_driver(i2c_slave_mqueue_driver);
+
++module_param_named(dump_debug, dump_debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(dump_debug, "debug flag for dump printing");
++module_param_named(dump_debug_bus_id, dump_debug_bus_id, int,
++ S_IRUGO | S_IWUSR);
++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..8c9d2dce0
--- /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,132 @@
+From 7baa65c9bf638265874838401e27a7b6179559ff 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 | 41 +++++++++++++++++++++++++++-----
+ include/dt-bindings/clock/aspeed-clock.h | 2 ++
+ 2 files changed, 37 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c
+index 5961367..3bbb4fb 100644
+--- a/drivers/clk/clk-aspeed.c
++++ b/drivers/clk/clk-aspeed.c
+@@ -14,7 +14,9 @@
+
+ #include <dt-bindings/clock/aspeed-clock.h>
+
+-#define ASPEED_NUM_CLKS 36
++#define ASPEED_NUM_CLKS ASPEED_CLK_MAX
++#define UART_HIGH_SPEED_CLK 192000000
++#define UART_LOW_SPEED_CLK 24000000
+
+ #define ASPEED_RESET2_OFFSET 32
+
+@@ -28,6 +30,12 @@
+ #define AST2400_HPLL_BYPASS_EN BIT(17)
+ #define ASPEED_MISC_CTRL 0x2c
+ #define UART_DIV13_EN BIT(12)
++#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)
+@@ -425,7 +433,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, uart_clock_div;
+ int i, ret;
+
+ map = syscon_node_to_regmap(dev->of_node);
+@@ -460,15 +468,23 @@ 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;
++ uart_clock_div = 13;
+ else
+- rate = 24000000;
++ uart_clock_div = 1;
++
+ /* TODO: Find the parent data for the uart clock */
+- hw = clk_hw_register_fixed_rate(dev, "uart", NULL, 0, rate);
++ hw = clk_hw_register_fixed_rate(dev, "uart", NULL, 0,
++ UART_LOW_SPEED_CLK / uart_clock_div);
+ 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,
++ UART_HIGH_SPEED_CLK / uart_clock_div);
++ 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.
+@@ -534,9 +550,22 @@ static int aspeed_clk_probe(struct platform_device *pdev)
+ * Video Engine (ECLK) mux and clock divider
+ */
+
++ /* 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.
+@@ -544,7 +573,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 f437386..3358795 100644
+--- a/include/dt-bindings/clock/aspeed-clock.h
++++ b/include/dt-bindings/clock/aspeed-clock.h
+@@ -39,6 +39,8 @@
+ #define ASPEED_CLK_BCLK 33
+ #define ASPEED_CLK_MPLL 34
+ #define ASPEED_CLK_24M 35
++#define ASPEED_CLK_UART_HS 36
++#define ASPEED_CLK_MAX 37
+
+ #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..e015f2fd9
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch
@@ -0,0 +1,556 @@
+From 37b192b278d5ea5da62b2fcff4fce7cf372e4fe6 Mon Sep 17 00:00:00 2001
+From: Oskar Senft <osk@google.com>
+Date: Wed, 8 Aug 2018 10:15:05 -0400
+Subject: [PATCH] misc: aspeed: Add Aspeed UART routing control driver.
+
+This driver adds sysfs files that allow the BMC userspace to configure
+how UARTs and physical serial I/O ports are routed.
+
+Tested: Checked correct behavior (both read & write) on TYAN S7106
+board by manually changing routing settings and confirming that bits
+flow as expected. Tested for UART1 and UART3 as this board doesn't have
+the other UARTs wired up in a testable way.
+
+Signed-off-by: Oskar Senft <osk@google.com>
+Signed-off-by: Yong Li <yong.b.li@linux.intel.com>
+---
+ .../ABI/stable/sysfs-driver-aspeed-uart-routing | 14 +
+ Documentation/misc-devices/aspeed-uart-routing.txt | 49 +++
+ arch/arm/boot/dts/aspeed-bmc-intel-purley.dts | 4 +
+ arch/arm/boot/dts/aspeed-g5.dtsi | 6 +
+ drivers/misc/Kconfig | 6 +
+ drivers/misc/Makefile | 1 +
+ drivers/misc/aspeed-uart-routing.c | 383 +++++++++++++++++++++
+ 7 files changed, 463 insertions(+)
+ create mode 100644 Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing
+ create mode 100644 Documentation/misc-devices/aspeed-uart-routing.txt
+ create mode 100644 drivers/misc/aspeed-uart-routing.c
+
+diff --git a/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing b/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing
+new file mode 100644
+index 000000000000..5068737d9c12
+--- /dev/null
++++ b/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing
+@@ -0,0 +1,14 @@
++What: /sys/bus/platform/drivers/aspeed-uart-routing/*/io*
++Date: August 2018
++Contact: Oskar Senft <osk@google.com>
++Description: Configures the input source for the specific physical
++ serial I/O port.
++Users: OpenBMC. Proposed changes should be mailed to
++ openbmc@lists.ozlabs.org
++
++What: /sys/bus/platform/drivers/aspeed-uart-routing/*/uart*
++Date: August 2018
++Contact: Oskar Senft <osk@google.com>
++Description: Configures the input source for the specific UART.
++Users: OpenBMC. Proposed changes should be mailed to
++ openbmc@lists.ozlabs.org
+diff --git a/Documentation/misc-devices/aspeed-uart-routing.txt b/Documentation/misc-devices/aspeed-uart-routing.txt
+new file mode 100644
+index 000000000000..afaf17cb7eda
+--- /dev/null
++++ b/Documentation/misc-devices/aspeed-uart-routing.txt
+@@ -0,0 +1,49 @@
++Kernel driver aspeed-uart-routing
++=================================
++
++Supported chips:
++ASPEED AST2500
++
++Author:
++Google LLC
++
++Description
++-----------
++
++The Aspeed AST2500 allows to dynamically route the inputs for the built-in
++UARTS and physical serial I/O ports.
++
++This allows, for example, to connect the output of UART to another UART.
++This can be used to enable host<->BMC communication via UARTs, e.g. to allow
++access to the host's serial console.
++
++This driver is for the BMC side. The sysfs files allow the BMC userspace
++which owns the system configuration policy, to configure how UARTs and
++physical serial I/O ports are routed.
++
++The driver provides the following files in sysfs:
++uart1 Configure the input signal to UART1.
++uart2 Configure the input signal to UART2.
++uart3 Configure the input signal to UART3.
++uart4 Configure the input signal to UART4.
++uart5 Configure the input signal to UART5.
++io1 Configure the input signal to physical serial port 1.
++io2 Configure the input signal to physical serial port 2.
++io3 Configure the input signal to physical serial port 3.
++io4 Configure the input signal to physical serial port 4.
++io5 Configure the input signal to physical serial port 5.
++
++When read, each file shows the list of available options with the currently
++selected option marked by square brackets "[]". The list of available options
++depends on the selected file.
++
++Example:
++$ cat /sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1
++[io1] io2 io3 io4 uart2 uart3 uart4 io6
++
++In this case, UART1 gets its input signal from IO1 (physical serial port 1).
++
++$ echo -n "uart3" \
++ >/sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1
++$ cat /sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1
++io1 io2 io3 io4 uart2 [uart3] uart4 io6
+diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts b/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts
+index 8aba46cdce46..d184fdf6dda6 100644
+--- a/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts
++++ b/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts
+@@ -227,6 +227,10 @@
+ status = "okay";
+ };
+
++&uart_routing {
++ status = "okay";
++};
++
+ &mac1 {
+ status = "okay";
+
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index adde826ac1d9..5606ac1d96d5 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -479,6 +479,12 @@
+ status = "disabled";
+ };
+ };
++
++ uart_routing: uart_routing@9c {
++ compatible = "aspeed,ast2500-uart-routing";
++ reg = <0x9c 0x4>;
++ status = "disabled";
++ };
+ };
+
+ peci: bus@1e78b000 {
+diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
+index 8b1fcf741411..60f203c04b9b 100644
+--- a/drivers/misc/Kconfig
++++ b/drivers/misc/Kconfig
+@@ -560,6 +560,12 @@ config NPCM7XX_PCI_MBOX
+ Expose the NPCM750/730/715/705 PCI MBOX registers found on
+ Nuvoton SOCs to userspace.
+
++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.
++
+ source "drivers/misc/c2port/Kconfig"
+ source "drivers/misc/eeprom/Kconfig"
+ source "drivers/misc/cb710/Kconfig"
+diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
+index 89b051f82391..8f70b888a9ca 100644
+--- a/drivers/misc/Makefile
++++ b/drivers/misc/Makefile
+@@ -56,6 +56,7 @@ obj-$(CONFIG_CXL_BASE) += cxl/
+ obj-$(CONFIG_ASPEED_ESPI_SLAVE) += aspeed-espi-slave.o
+ obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
+ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
++obj-$(CONFIG_ASPEED_UART_ROUTING) += aspeed-uart-routing.o
+ obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o
+ obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.o
+ obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o
+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..b819be69b
--- /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 9c509b9450f641c169ee3aeb60e398c43810dcb2 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 22eab8a952ed..004bbb08dd4a 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -101,14 +101,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>;
+@@ -117,6 +109,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 92843cc1a8f4..30a7f349feeb 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -142,14 +142,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>;
+@@ -158,6 +150,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..bb6465023
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch
@@ -0,0 +1,252 @@
+From dae410353f8681b58907c61eb2eb056513d86f6d 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>
+---
+ .../devicetree/bindings/misc/vga-shared-memory.txt | 20 +++
+ drivers/misc/Kconfig | 10 ++
+ drivers/misc/Makefile | 1 +
+ drivers/misc/aspeed-vga-sharedmem.c | 164 +++++++++++++++++++++
+ 4 files changed, 195 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/misc/vga-shared-memory.txt
+ create mode 100644 drivers/misc/aspeed-vga-sharedmem.c
+
+diff --git a/Documentation/devicetree/bindings/misc/vga-shared-memory.txt b/Documentation/devicetree/bindings/misc/vga-shared-memory.txt
+new file mode 100644
+index 000000000000..03f57c53e844
+--- /dev/null
++++ b/Documentation/devicetree/bindings/misc/vga-shared-memory.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/misc/Kconfig b/drivers/misc/Kconfig
+index 60f203c04b9b..2d4c6ba87e70 100644
+--- a/drivers/misc/Kconfig
++++ b/drivers/misc/Kconfig
+@@ -566,6 +566,16 @@ config ASPEED_UART_ROUTING
+ If you want to configure UART routing on Aspeed BMC platforms, enable
+ this option.
+
++config ASPEED_VGA_SHAREDMEM
++ tristate "Aspeed VGA Shared memory"
++ depends on (ARCH_ASPEED || COMPILE_TEST)
++ 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.
++
+ source "drivers/misc/c2port/Kconfig"
+ source "drivers/misc/eeprom/Kconfig"
+ source "drivers/misc/cb710/Kconfig"
+diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
+index 8f70b888a9ca..30ee065491ef 100644
+--- a/drivers/misc/Makefile
++++ b/drivers/misc/Makefile
+@@ -55,6 +55,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpres
+ obj-$(CONFIG_CXL_BASE) += cxl/
+ obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.o
+ obj-$(CONFIG_ASPEED_ESPI_SLAVE) += aspeed-espi-slave.o
++obj-$(CONFIG_ASPEED_VGA_SHAREDMEM) += aspeed-vga-sharedmem.o
+ obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
+ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
+ obj-$(CONFIG_ASPEED_UART_ROUTING) += aspeed-uart-routing.o
+diff --git a/drivers/misc/aspeed-vga-sharedmem.c b/drivers/misc/aspeed-vga-sharedmem.c
+new file mode 100644
+index 000000000000..76f60cd67d3a
+--- /dev/null
++++ b/drivers/misc/aspeed-vga-sharedmem.c
+@@ -0,0 +1,164 @@
++// 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)
++{
++ if (!capable(CAP_SYS_ADMIN))
++ return -EPERM;
++
++ struct aspeed_vga_sharedmem *vga_sharedmem = file_sharemem(file);
++
++ 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;
++ u32 reg[2];
++ 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..3e8f86666
--- /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,510 @@
+From ef2e1d9d2e8c97daf806f4da74738a84de054116 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 | 437 +++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 448 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 8a7c4257b917..c24197232385 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -300,7 +300,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 a8f47df0655a..92a8fbebe2d9 100644
+--- a/drivers/pwm/Kconfig
++++ b/drivers/pwm/Kconfig
+@@ -170,6 +170,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 9c676a0dadf5..13b7b20ad5ab 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_IMX) += pwm-imx.o
+diff --git a/drivers/pwm/pwm-fttmr010.c b/drivers/pwm/pwm-fttmr010.c
+new file mode 100644
+index 000000000000..32e508c962dc
+--- /dev/null
++++ b/drivers/pwm/pwm-fttmr010.c
+@@ -0,0 +1,437 @@
++// 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);
++ ulong flags;
++ 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;
++ }
++
++ 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);
++ ulong flags;
++ 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;
++ }
++
++ 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;
++ }
++
++ writel(tload, priv->base + creg_offset);
++ writel(tload, priv->base + lreg_offset);
++ writel(tmatch, priv->base + mreg_offset);
++
++ chan->period_ns = period_ns;
++ chan->duty_ns = duty_ns;
++
++ return 0;
++}
++
++static const struct pwm_ops pwm_fttmr010_ops = {
++ .request = pwm_fttmr010_request,
++ .free = pwm_fttmr010_free,
++ .enable = pwm_fttmr010_enable,
++ .disable = pwm_fttmr010_disable,
++ .config = pwm_fttmr010_config,
++ .owner = THIS_MODULE,
++};
++
++#ifdef CONFIG_OF
++static const struct pwm_fttmr010_variant aspeed_variant = {
++ .bits = 32,
++ .chan_min = 5,
++ .chan_max = 8,
++};
++
++static const struct of_device_id pwm_fttmr010_matches[] = {
++ { .compatible = "aspeed,ast2400-timer", .data = &aspeed_variant },
++ { .compatible = "aspeed,ast2500-timer", .data = &aspeed_variant },
++ { },
++};
++MODULE_DEVICE_TABLE(of, pwm_fttmr010_matches);
++
++static int pwm_fttmr010_parse_dt(struct pwm_fttmr010 *priv)
++{
++ struct device_node *np = priv->chip.dev->of_node;
++ const struct of_device_id *match;
++ struct property *prop;
++ const __be32 *cur;
++ u32 val;
++
++ match = of_match_node(pwm_fttmr010_matches, np);
++ if (!match)
++ return -ENODEV;
++
++ memcpy(&priv->variant, match->data, sizeof(priv->variant));
++
++ of_property_for_each_u32(np, "fttmr010,pwm-outputs", prop, cur, val) {
++ if (val < priv->variant.chan_min ||
++ val > priv->variant.chan_max) {
++ dev_err(priv->chip.dev,
++ "invalid channel index in fttmr010,pwm-outputs property\n");
++ continue;
++ }
++ priv->variant.output_mask |= BIT(val);
++ }
++
++ return 0;
++}
++#else
++static int pwm_fttmr010_parse_dt(struct pwm_fttmr010 *priv)
++{
++ return -ENODEV;
++}
++#endif
++
++static int pwm_fttmr010_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct pwm_fttmr010 *priv;
++ struct resource *res;
++ ulong clk_rate;
++ int ret;
++
++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ priv->chip.dev = &pdev->dev;
++ priv->chip.ops = &pwm_fttmr010_ops;
++ priv->chip.base = -1;
++
++ if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
++ ret = pwm_fttmr010_parse_dt(priv);
++ if (ret)
++ return ret;
++
++ priv->chip.of_xlate = of_pwm_xlate_with_flags;
++ priv->chip.of_pwm_n_cells = 3;
++ } else {
++ if (!pdev->dev.platform_data) {
++ dev_err(&pdev->dev, "no platform data specified\n");
++ return -EINVAL;
++ }
++
++ memcpy(&priv->variant, pdev->dev.platform_data,
++ sizeof(priv->variant));
++ }
++
++ priv->chip.npwm = priv->variant.chan_max + 1;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ priv->base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(priv->base))
++ return PTR_ERR(priv->base);
++
++ priv->clk = devm_clk_get(&pdev->dev, "PCLK");
++ if (IS_ERR(priv->clk)) {
++ dev_err(dev, "failed to get timer base clk\n");
++ return PTR_ERR(priv->clk);
++ }
++
++ ret = clk_prepare_enable(priv->clk);
++ if (ret < 0) {
++ dev_err(dev, "failed to enable base clock\n");
++ return ret;
++ }
++
++ clk_rate = clk_get_rate(priv->clk);
++ priv->clk_tick_ns = NSEC_PER_SEC / clk_rate;
++
++ platform_set_drvdata(pdev, priv);
++
++ ret = pwmchip_add(&priv->chip);
++ if (ret < 0) {
++ dev_err(dev, "failed to register PWM chip\n");
++ clk_disable_unprepare(priv->clk);
++ return ret;
++ }
++
++ dev_dbg(dev, "clk at %lu\n", clk_rate);
++
++ return 0;
++}
++
++static int pwm_fttmr010_remove(struct platform_device *pdev)
++{
++ struct pwm_fttmr010 *priv = platform_get_drvdata(pdev);
++ int ret;
++
++ ret = pwmchip_remove(&priv->chip);
++ if (ret < 0)
++ return ret;
++
++ clk_disable_unprepare(priv->clk);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM_SLEEP
++static int pwm_fttmr010_resume(struct device *dev)
++{
++ struct pwm_fttmr010 *priv = dev_get_drvdata(dev);
++ struct pwm_chip *chip = &priv->chip;
++ unsigned int i;
++
++ for (i = chip->variant.chan_min; i < chip->variant.chan_max; i++) {
++ struct pwm_device *pwm = &chip->pwms[i];
++ struct pwm_fttmr010_chan *chan = pwm_get_chip_data(pwm);
++
++ if (!chan)
++ continue;
++
++ if (chan->period_ns) {
++ pwm_fttmr010_config(chip, pwm, chan->duty_ns,
++ chan->period_ns);
++ }
++
++ if (priv->disabled_mask & BIT(i))
++ pwm_fttmr010_disable(chip, pwm);
++ else
++ pwm_fttmr010_enable(chip, pwm);
++ }
++
++ return 0;
++}
++#endif
++
++static SIMPLE_DEV_PM_OPS(pwm_fttmr010_pm_ops, NULL, pwm_fttmr010_resume);
++
++static struct platform_driver pwm_fttmr010_driver = {
++ .driver = {
++ .name = "fttmr010-timer-pwm",
++ .pm = &pwm_fttmr010_pm_ops,
++ .of_match_table = of_match_ptr(pwm_fttmr010_matches),
++ },
++ .probe = pwm_fttmr010_probe,
++ .remove = pwm_fttmr010_remove,
++};
++module_platform_driver(pwm_fttmr010_driver);
++
++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
++MODULE_DESCRIPTION("FTTMR010 PWM Driver for timer pulse outputs");
++MODULE_LICENSE("GPL v2");
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch
new file mode 100644
index 000000000..1b86e9c04
--- /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,511 @@
+From 80ea6461d77e5b415d9f83fa2f4708fc21eab09b 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 | 99 ++++++++++++++++++++++++++++++++++-----
+ drivers/i2c/i2c-core-smbus.c | 17 ++++++-
+ 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, 233 insertions(+), 21 deletions(-)
+
+diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
+index 28460f6a60cc..009b0507768e 100644
+--- a/drivers/i2c/i2c-core-base.c
++++ b/drivers/i2c/i2c-core-base.c
+@@ -1210,6 +1210,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;
+@@ -1291,6 +1310,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:
+@@ -1511,6 +1533,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));
+@@ -1860,7 +1884,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;
+ unsigned long orig_jiffies;
++ unsigned long timeout;
+ int ret, try;
+
+ if (WARN_ON(!msgs || num < 1))
+@@ -1869,6 +1895,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
+@@ -1901,6 +1946,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);
+@@ -1919,6 +1967,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) {
+@@ -1942,19 +1991,47 @@ 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).
+ */
+- if (in_atomic() || irqs_disabled()) {
+- ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT);
+- if (!ret)
+- /* I2C activity is ongoing. */
+- return -EAGAIN;
+- } else {
+- i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
+- }
+
+- ret = __i2c_transfer(adap, msgs, num);
+- i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
++ if (adap->algo->master_xfer) {
++#ifdef DEBUG
++ for (ret = 0; ret < num; ret++) {
++ dev_dbg(&adap->dev,
++ "master_xfer[%d] %c, addr=0x%02x, len=%d%s\n",
++ ret, (msgs[ret].flags & I2C_M_RD) ? 'R' : 'W',
++ msgs[ret].addr, msgs[ret].len,
++ (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
++ }
++#endif
++ /*
++ * 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) {
++ if (in_atomic() || irqs_disabled()) {
++ ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT);
++ if (!ret)
++ /* I2C activity is ongoing. */
++ return -EAGAIN;
++ } else {
++ i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
++ }
++ }
+
+- return ret;
++ ret = __i2c_transfer(adap, msgs, num);
++ if (do_bus_lock)
++ i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
++
++ return ret;
++ } else {
++ dev_dbg(&adap->dev, "I2C level transfers not supported\n");
++ return -EOPNOTSUPP;
++ }
+ }
+ EXPORT_SYMBOL(i2c_transfer);
+
+diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c
+index 9cd66cabb84f..64c58911bf21 100644
+--- a/drivers/i2c/i2c-core-smbus.c
++++ b/drivers/i2c/i2c-core-smbus.c
+@@ -528,12 +528,25 @@ 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;
+
+- i2c_lock_bus(adapter, I2C_LOCK_SEGMENT);
++ /*
++ * 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)
++ i2c_lock_bus(adapter, I2C_LOCK_SEGMENT);
+ 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 f330690b4125..4d8909a0f90a 100644
+--- a/drivers/i2c/i2c-mux.c
++++ b/drivers/i2c/i2c-mux.c
+@@ -26,6 +26,7 @@
+ #include <linux/module.h>
+ #include <linux/of.h>
+ #include <linux/slab.h>
++#include <linux/timer.h>
+
+ /* multiplexer per channel data */
+ struct i2c_mux_priv {
+@@ -35,21 +36,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;
+ }
+@@ -60,15 +97,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;
+ }
+@@ -81,16 +135,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;
+ }
+@@ -103,16 +174,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);
+@@ -435,6 +526,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 bd74d5706f3b..bc6f778eaf9d 100644
+--- a/include/linux/i2c-mux.h
++++ b/include/linux/i2c-mux.h
+@@ -41,6 +41,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 65b4eaed1d96..eadde70c0d4a 100644
+--- a/include/linux/i2c.h
++++ b/include/linux/i2c.h
+@@ -692,6 +692,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)
+
+@@ -949,4 +956,22 @@ static inline struct i2c_client *i2c_acpi_new_device(struct device *dev,
+ }
+ #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/0041-Enable-passthrough-based-gpio-character-device.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0041-Enable-passthrough-based-gpio-character-device.patch
new file mode 100644
index 000000000..7a4b090ec
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0041-Enable-passthrough-based-gpio-character-device.patch
@@ -0,0 +1,287 @@
+From 554bc7a7c7aa6e0c0ec49a24063102e17954d06c Mon Sep 17 00:00:00 2001
+From: Kuiying Wang <kuiying.wang@intel.com>
+Date: Thu, 31 Jan 2019 17:47:39 +0800
+Subject: [PATCH] Enable passthrough based gpio character device.
+
+Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
+---
+ drivers/gpio/gpio-aspeed.c | 47 ++++++++++++++++++++++++++++--
+ drivers/gpio/gpiolib.c | 51 +++++++++++++++++++++++++++++++--
+ drivers/gpio/gpiolib.h | 1 +
+ include/linux/gpio/consumer.h | 9 ++++++
+ include/linux/pinctrl/pinconf-generic.h | 2 ++
+ include/uapi/linux/gpio.h | 1 +
+ 6 files changed, 106 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
+index 854bce4fb9e7..5f1bce3a9274 100644
+--- a/drivers/gpio/gpio-aspeed.c
++++ b/drivers/gpio/gpio-aspeed.c
+@@ -17,9 +17,11 @@
+ #include <linux/init.h>
+ #include <linux/io.h>
+ #include <linux/kernel.h>
++#include <linux/mfd/syscon.h>
+ #include <linux/module.h>
+ #include <linux/pinctrl/consumer.h>
+ #include <linux/platform_device.h>
++#include <linux/regmap.h>
+ #include <linux/spinlock.h>
+ #include <linux/string.h>
+
+@@ -58,6 +60,7 @@ struct aspeed_gpio {
+ struct gpio_chip chip;
+ spinlock_t lock;
+ void __iomem *base;
++ struct regmap *scu;
+ int irq;
+ const struct aspeed_gpio_config *config;
+
+@@ -91,6 +94,13 @@ struct aspeed_gpio_bank {
+ * and thus can be used to read back what was last written
+ * reliably.
+ */
++#define SCU8C 0x8C /* Multi-function Pin Control #4 */
++#define PASS_THROUGH1 32
++#define PASS_THROUGH2 34
++#define PASS_THROUGH2_MASK 0x2000
++#define PASS_THROUGH1_MASK 0x1000
++#define PASS_THROUGH2_ON 0x2000
++#define PASS_THROUGH1_ON 0x1000
+
+ static const int debounce_timers[4] = { 0x00, 0x50, 0x54, 0x58 };
+
+@@ -988,12 +998,38 @@ static int set_debounce(struct gpio_chip *chip, unsigned int offset,
+ return disable_debounce(chip, offset);
+ }
+
++static int aspeed_gpio_pass_through(struct gpio_chip *chip, unsigned int offset,
++ unsigned long param)
++{
++ struct aspeed_gpio *gpio = gpiochip_get_data(chip);
++ u32 value;
++
++ if (!gpio->scu)
++ return -ENOTSUPP;
++ if (param == PIN_CONFIG_PASS_THROUGH_ENABLE){
++ if (offset == PASS_THROUGH2){
++ regmap_update_bits(gpio->scu, SCU8C, PASS_THROUGH2_MASK, PASS_THROUGH2_ON);
++ } else if (offset == PASS_THROUGH1){
++ regmap_update_bits(gpio->scu, SCU8C, PASS_THROUGH1_MASK, PASS_THROUGH1_ON);
++ }
++ } else if (param == PIN_CONFIG_PASS_THROUGH_DISABLE){
++ if (offset == PASS_THROUGH2){
++ regmap_update_bits(gpio->scu, SCU8C, PASS_THROUGH2_MASK, ~(PASS_THROUGH2_ON));
++ } else if (offset == PASS_THROUGH1){
++ regmap_update_bits(gpio->scu, SCU8C, PASS_THROUGH1_MASK, ~(PASS_THROUGH1_ON));
++ }
++ } else {
++ return -ENOTSUPP;
++ }
++
++ return 0;
++}
++
+ static int aspeed_gpio_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_INPUT_DEBOUNCE)
+ return set_debounce(chip, offset, arg);
+ else if (param == PIN_CONFIG_BIAS_DISABLE ||
+@@ -1006,6 +1042,9 @@ static int aspeed_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
+ return -ENOTSUPP;
+ else if (param == PIN_CONFIG_PERSIST_STATE)
+ return aspeed_gpio_reset_tolerance(chip, offset, arg);
++ else if (param == PIN_CONFIG_PASS_THROUGH_ENABLE ||
++ param == PIN_CONFIG_PASS_THROUGH_DISABLE)
++ return aspeed_gpio_pass_through(chip, offset, param);
+
+ return -ENOTSUPP;
+ }
+@@ -1167,7 +1206,11 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
+ gpio->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(gpio->base))
+ return PTR_ERR(gpio->base);
+-
++ gpio->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2500-scu");
++ if (IS_ERR(gpio->scu)) {
++ dev_err(&pdev->dev, "Failed to find SCU regmap\n");
++ gpio->scu = NULL;
++ }
+ spin_lock_init(&gpio->lock);
+
+ gpio_id = of_match_node(aspeed_gpio_of_table, pdev->dev.of_node);
+diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
+index d1adfdf50fb3..4f9fdd25c6d7 100644
+--- a/drivers/gpio/gpiolib.c
++++ b/drivers/gpio/gpiolib.c
+@@ -428,6 +428,7 @@ struct linehandle_state {
+ GPIOHANDLE_REQUEST_OUTPUT | \
+ GPIOHANDLE_REQUEST_ACTIVE_LOW | \
+ GPIOHANDLE_REQUEST_OPEN_DRAIN | \
++ GPIOHANDLE_REQUEST_PASS_THROUGH | \
+ GPIOHANDLE_REQUEST_OPEN_SOURCE)
+
+ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
+@@ -530,7 +531,6 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
+ return -EINVAL;
+
+ lflags = handlereq.flags;
+-
+ /* Return an error if an unknown flag is set */
+ if (lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS)
+ return -EINVAL;
+@@ -590,6 +590,8 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
+ set_bit(FLAG_OPEN_DRAIN, &desc->flags);
+ if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)
+ set_bit(FLAG_OPEN_SOURCE, &desc->flags);
++ if (lflags & GPIOHANDLE_REQUEST_PASS_THROUGH)
++ set_bit(FLAG_PASS_THROUGH, &desc->flags);
+
+ ret = gpiod_set_transitory(desc, false);
+ if (ret < 0)
+@@ -609,6 +611,11 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
+ ret = gpiod_direction_input(desc);
+ if (ret)
+ goto out_free_descs;
++ } else if (lflags & GPIOHANDLE_REQUEST_PASS_THROUGH) {
++ int val = !!handlereq.default_values[i];
++ ret = gpiod_direction_pass_through(desc, val);
++ if (ret)
++ goto out_free_descs;
+ }
+ dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
+ offset);
+@@ -1027,7 +1034,6 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+ struct gpio_device *gdev = filp->private_data;
+ struct gpio_chip *chip = gdev->chip;
+ void __user *ip = (void __user *)arg;
+-
+ /* We fail any subsequent ioctl():s when the chip is gone */
+ if (!chip)
+ return -ENODEV;
+@@ -1035,7 +1041,6 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+ /* Fill in the struct and pass to userspace */
+ if (cmd == GPIO_GET_CHIPINFO_IOCTL) {
+ struct gpiochip_info chipinfo;
+-
+ memset(&chipinfo, 0, sizeof(chipinfo));
+
+ strncpy(chipinfo.name, dev_name(&gdev->dev),
+@@ -2709,6 +2714,46 @@ int gpiod_direction_output(struct gpio_desc *desc, int value)
+ EXPORT_SYMBOL_GPL(gpiod_direction_output);
+
+ /**
++ * gpiod_direction_pass_through - set the GPIO direction to pass-through
++ * @desc: GPIO to set to pass-through
++ *
++ * Set the direction of the passed GPIO to passthrough.
++ *
++ * Return 0 in case of success, else an error code.
++ */
++int gpiod_direction_pass_through(struct gpio_desc *desc, int val)
++{
++ struct gpio_chip *gc;
++
++ VALIDATE_DESC(desc);
++ /* GPIOs used for IRQs shall not be set as pass-through */
++ if (test_bit(FLAG_USED_AS_IRQ, &desc->flags)) {
++ gpiod_err(desc,
++ "%s: tried to set a GPIO tied to an IRQ as pass-through\n",
++ __func__);
++ return -EIO;
++ }
++ gc = desc->gdev->chip;
++ val = !!val;
++ if (test_bit(FLAG_PASS_THROUGH, &desc->flags)) {
++ if (val)
++ gpio_set_config(gc, gpio_chip_hwgpio(desc),
++ PIN_CONFIG_PASS_THROUGH_ENABLE);
++ else
++ gpio_set_config(gc, gpio_chip_hwgpio(desc),
++ PIN_CONFIG_PASS_THROUGH_DISABLE);
++ } else {
++ gpiod_err(desc,
++ "%s: desc->flags is not set to FLAG_PASS_THROUGH\n",
++ __func__);
++ return -EIO;
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(gpiod_direction_pass_through);
++
++/**
+ * gpiod_set_debounce - sets @debounce time for a GPIO
+ * @desc: descriptor of the GPIO for which to set debounce time
+ * @debounce: debounce time in microseconds
+diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
+index bc57f0dc5953..a821a04fc04b 100644
+--- a/drivers/gpio/gpiolib.h
++++ b/drivers/gpio/gpiolib.h
+@@ -212,6 +212,7 @@ struct gpio_desc {
+ #define FLAG_IS_OUT 1
+ #define FLAG_EXPORT 2 /* protected by sysfs_lock */
+ #define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */
++#define FLAG_PASS_THROUGH 4 /*Gpio is passthrough type*/
+ #define FLAG_ACTIVE_LOW 6 /* value has active low */
+ #define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */
+ #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */
+diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
+index 9ddcf50a3c59..f9775be5a46a 100644
+--- a/include/linux/gpio/consumer.h
++++ b/include/linux/gpio/consumer.h
+@@ -110,6 +110,7 @@ void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs);
+ int gpiod_get_direction(struct gpio_desc *desc);
+ int gpiod_direction_input(struct gpio_desc *desc);
+ int gpiod_direction_output(struct gpio_desc *desc, int value);
++int gpiod_direction_pass_through(struct gpio_desc *desc, int val);
+ int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
+
+ /* Value get/set from non-sleeping context */
+@@ -348,6 +349,14 @@ static inline int gpiod_direction_output(struct gpio_desc *desc, int value)
+ WARN_ON(1);
+ return -ENOSYS;
+ }
++
++static inline int gpiod_direction_pass_through(struct gpio_desc *desc, int val)
++{
++ /* GPIO can never have been requested */
++ WARN_ON(1);
++ return -ENOSYS;
++}
++
+ static inline int gpiod_direction_output_raw(struct gpio_desc *desc, int value)
+ {
+ /* GPIO can never have been requested */
+diff --git a/include/linux/pinctrl/pinconf-generic.h b/include/linux/pinctrl/pinconf-generic.h
+index 6c0680641108..59f0cbabb685 100644
+--- a/include/linux/pinctrl/pinconf-generic.h
++++ b/include/linux/pinctrl/pinconf-generic.h
+@@ -124,6 +124,8 @@ enum pin_config_param {
+ PIN_CONFIG_SLEW_RATE,
+ PIN_CONFIG_SKEW_DELAY,
+ PIN_CONFIG_PERSIST_STATE,
++ PIN_CONFIG_PASS_THROUGH_ENABLE,
++ PIN_CONFIG_PASS_THROUGH_DISABLE,
+ PIN_CONFIG_END = 0x7F,
+ PIN_CONFIG_MAX = 0xFF,
+ };
+diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h
+index 4ebfe0ac6c5b..99864572b7d9 100644
+--- a/include/uapi/linux/gpio.h
++++ b/include/uapi/linux/gpio.h
+@@ -62,6 +62,7 @@ struct gpioline_info {
+ #define GPIOHANDLE_REQUEST_ACTIVE_LOW (1UL << 2)
+ #define GPIOHANDLE_REQUEST_OPEN_DRAIN (1UL << 3)
+ #define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4)
++#define GPIOHANDLE_REQUEST_PASS_THROUGH (1UL << 5)
+
+ /**
+ * struct gpiohandle_request - Information about a GPIO handle request
+--
+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..3588d62d9
--- /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 6515a2134f90f33dbbea8ede5de598d17bb00c12 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 506d867b43d9..84237c5d0aca 100644
+--- a/drivers/i2c/busses/i2c-aspeed.c
++++ b/drivers/i2c/busses/i2c-aspeed.c
+@@ -1012,7 +1012,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 009b0507768e..386aa2dad908 100644
+--- a/drivers/i2c/i2c-core-base.c
++++ b/drivers/i2c/i2c-core-base.c
+@@ -1231,6 +1231,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 */
+@@ -1257,8 +1258,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..f04824c0e
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0043-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-BT.patch
@@ -0,0 +1,140 @@
+From d82aacea62f2cc3f5c4f6654bd8920255edf24fd Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Wed, 13 Mar 2019 15:04:16 -0700
+Subject: [PATCH] char: ipmi: Add clock control logic into Aspeed LPC BT driver
+
+If LPC BT driver is registered ahead of lpc-ctrl module, LPC BT
+block will be enabled without heart beating of LCLK until lpc-ctrl
+enables the LCLK. This issue causes improper handling on host
+interrupts when the host sends interrupt in that time frame. Then
+kernel eventually forcibly disables the interrupt with dumping
+stack and printing a 'nobody cared this irq' message out.
+
+To prevent this issue, all LPC sub-nodes should enable LCLK
+individually so this patch adds clock control logic into the LPC
+BT driver.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ .../bindings/ipmi/aspeed,ast2400-ibt-bmc.txt | 3 +++
+ arch/arm/boot/dts/aspeed-g4.dtsi | 1 +
+ arch/arm/boot/dts/aspeed-g5.dtsi | 1 +
+ drivers/char/ipmi/bt-bmc.c | 24 +++++++++++++++++++++-
+ 4 files changed, 28 insertions(+), 1 deletion(-)
+
+diff --git a/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-ibt-bmc.txt b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-ibt-bmc.txt
+index 028268fd99ee..d13887d60f19 100644
+--- a/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-ibt-bmc.txt
++++ b/Documentation/devicetree/bindings/ipmi/aspeed,ast2400-ibt-bmc.txt
+@@ -10,6 +10,8 @@ Required properties:
+ "aspeed,ast2400-ibt-bmc"
+ "aspeed,ast2500-ibt-bmc"
+ - reg: physical address and size of the registers
++- clocks: contains a phandle to the syscon node describing the clocks.
++ There should then be one cell representing the clock to use.
+
+ Optional properties:
+
+@@ -22,4 +24,5 @@ Example:
+ compatible = "aspeed,ast2400-ibt-bmc";
+ reg = <0x1e789140 0x18>;
+ interrupts = <8>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ };
+diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
+index 6e6f50a0fbab..3a7e31f3de07 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -333,6 +333,7 @@
+ ibt: ibt@c0 {
+ compatible = "aspeed,ast2400-ibt-bmc";
+ reg = <0xc0 0x18>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ interrupts = <8>;
+ };
+
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index 4cd4a8258e42..a6720bc952b0 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -468,6 +468,7 @@
+ ibt: ibt@c0 {
+ compatible = "aspeed,ast2500-ibt-bmc";
+ reg = <0xc0 0x18>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ interrupts = <8>;
+ };
+
+diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c
+index 40b9927c072c..a4ec9d1743d7 100644
+--- a/drivers/char/ipmi/bt-bmc.c
++++ b/drivers/char/ipmi/bt-bmc.c
+@@ -5,6 +5,7 @@
+
+ #include <linux/atomic.h>
+ #include <linux/bt-bmc.h>
++#include <linux/clk.h>
+ #include <linux/errno.h>
+ #include <linux/interrupt.h>
+ #include <linux/io.h>
+@@ -60,6 +61,7 @@ struct bt_bmc {
+ struct device dev;
+ struct miscdevice miscdev;
+ struct regmap *map;
++ struct clk *clk;
+ int offset;
+ int irq;
+ wait_queue_head_t queue;
+@@ -467,6 +469,19 @@ static int bt_bmc_probe(struct platform_device *pdev)
+ mutex_init(&bt_bmc->mutex);
+ init_waitqueue_head(&bt_bmc->queue);
+
++ bt_bmc->clk = devm_clk_get(dev, NULL);
++ if (IS_ERR(bt_bmc->clk)) {
++ rc = PTR_ERR(bt_bmc->clk);
++ if (rc != -EPROBE_DEFER)
++ dev_err(dev, "couldn't get clock\n");
++ return rc;
++ }
++ rc = clk_prepare_enable(bt_bmc->clk);
++ if (rc) {
++ dev_err(dev, "couldn't enable clock\n");
++ return rc;
++ }
++
+ bt_bmc->miscdev.minor = MISC_DYNAMIC_MINOR,
+ bt_bmc->miscdev.name = DEVICE_NAME,
+ bt_bmc->miscdev.fops = &bt_bmc_fops,
+@@ -474,7 +489,7 @@ static int bt_bmc_probe(struct platform_device *pdev)
+ rc = misc_register(&bt_bmc->miscdev);
+ if (rc) {
+ dev_err(dev, "Unable to register misc device\n");
+- return rc;
++ goto err;
+ }
+
+ bt_bmc_config_irq(bt_bmc, pdev);
+@@ -498,6 +513,11 @@ static int bt_bmc_probe(struct platform_device *pdev)
+ clr_b_busy(bt_bmc);
+
+ return 0;
++
++err:
++ clk_disable_unprepare(bt_bmc->clk);
++
++ return rc;
+ }
+
+ static int bt_bmc_remove(struct platform_device *pdev)
+@@ -507,6 +527,8 @@ static int bt_bmc_remove(struct platform_device *pdev)
+ misc_deregister(&bt_bmc->miscdev);
+ if (!bt_bmc->irq)
+ del_timer_sync(&bt_bmc->poll_timer);
++ clk_disable_unprepare(bt_bmc->clk);
++
+ return 0;
+ }
+
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0044-misc-Add-clock-control-logic-into-Aspeed-LPC-SNOOP-d.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0044-misc-Add-clock-control-logic-into-Aspeed-LPC-SNOOP-d.patch
new file mode 100644
index 000000000..0559ef5be
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0044-misc-Add-clock-control-logic-into-Aspeed-LPC-SNOOP-d.patch
@@ -0,0 +1,125 @@
+From 1ebca05f5cb04162e124e59cac701291f23d9091 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Wed, 13 Mar 2019 15:27:48 -0700
+Subject: [PATCH] misc: Add clock control logic into Aspeed LPC SNOOP driver
+
+If LPC SNOOP driver is registered ahead of lpc-ctrl module, LPC
+SNOOP block will be enabled without heart beating of LCLK until
+lpc-ctrl enables the LCLK. This issue causes improper handling on
+host interrupts when the host sends interrupt in that time frame.
+Then kernel eventually forcibly disables the interrupt with
+dumping stack and printing a 'nobody cared this irq' message out.
+
+To prevent this issue, all LPC sub-nodes should enable LCLK
+individually so this patch adds clock control logic into the LPC
+SNOOP driver.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ arch/arm/boot/dts/aspeed-g4.dtsi | 1 +
+ arch/arm/boot/dts/aspeed-g5.dtsi | 1 +
+ drivers/misc/aspeed-lpc-snoop.c | 30 +++++++++++++++++++++++++++---
+ 3 files changed, 29 insertions(+), 3 deletions(-)
+
+diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
+index 3a7e31f3de07..bedfb77c0158 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -316,6 +316,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 a6720bc952b0..a26e8b3c09bf 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -451,6 +451,7 @@
+ compatible = "aspeed,ast2500-lpc-snoop";
+ reg = <0x0 0x80>;
+ interrupts = <8>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ status = "disabled";
+ };
+
+diff --git a/drivers/misc/aspeed-lpc-snoop.c b/drivers/misc/aspeed-lpc-snoop.c
+index 2feb4347d67f..39a0471f0b8f 100644
+--- a/drivers/misc/aspeed-lpc-snoop.c
++++ b/drivers/misc/aspeed-lpc-snoop.c
+@@ -15,6 +15,7 @@
+ */
+
+ #include <linux/bitops.h>
++#include <linux/clk.h>
+ #include <linux/interrupt.h>
+ #include <linux/fs.h>
+ #include <linux/kfifo.h>
+@@ -71,6 +72,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];
+ };
+
+@@ -286,22 +288,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;
+ }
+
+@@ -313,6 +335,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..d9b6d05f9
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0045-char-ipmi-Add-clock-control-logic-into-Aspeed-LPC-KC.patch
@@ -0,0 +1,235 @@
+From 1326920183042bb91583eb56dabd29ec921f8f65 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Wed, 13 Mar 2019 15:36:34 -0700
+Subject: [PATCH] char: ipmi: Add clock control logic into Aspeed LPC KCS
+ driver
+
+If LPC KCS driver is registered ahead of lpc-ctrl module, LPC KCS
+block will be enabled without heart beating of LCLK until lpc-ctrl
+enables the LCLK. This issue causes improper handling on host
+interrupts when the host sends interrupt in that time frame. Then
+kernel eventually forcibly disables the interrupt with dumping
+stack and printing a 'nobody cared this irq' message out.
+
+To prevent this issue, all LPC sub-nodes should enable LCLK
+individually so this patch adds clock control logic into the LPC
+KCS driver.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ .../devicetree/bindings/ipmi/aspeed-kcs-bmc.txt | 3 ++
+ arch/arm/boot/dts/aspeed-g4.dtsi | 35 ++++++++++++++++++++
+ arch/arm/boot/dts/aspeed-g5.dtsi | 6 +++-
+ drivers/char/ipmi/kcs_bmc_aspeed.c | 37 ++++++++++++++++++----
+ 4 files changed, 73 insertions(+), 8 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt b/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt
+index d98a9bf45d6c..3453eb0bf8f2 100644
+--- a/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt
++++ b/Documentation/devicetree/bindings/ipmi/aspeed-kcs-bmc.txt
+@@ -9,6 +9,8 @@ Required properties:
+ "aspeed,ast2400-kcs-bmc"
+ "aspeed,ast2500-kcs-bmc"
+ - interrupts : interrupt generated by the controller
++- clocks: contains a phandle to the syscon node describing the clocks.
++ There should then be one cell representing the clock to use.
+ - kcs_chan : The LPC channel number in the controller
+ - kcs_addr : The host CPU IO map address
+
+@@ -19,6 +21,7 @@ Example:
+ compatible = "aspeed,ast2500-kcs-bmc";
+ reg = <0x0 0x80>;
+ interrupts = <8>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ kcs_chan = <3>;
+ kcs_addr = <0xCA2>;
+ status = "okay";
+diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
+index bedfb77c0158..a5072ed1f823 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -294,6 +294,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 {
+@@ -305,6 +332,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 a26e8b3c09bf..6a2f161e7548 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -128,7 +128,7 @@
+ };
+
+ vic: interrupt-controller@1e6c0080 {
+- compatible = "aspeed,ast2400-vic";
++ compatible = "aspeed,ast2500-vic";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ valid-sources = <0xfefff7ff 0x0807ffff>;
+@@ -408,18 +408,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";
+ };
+ };
+@@ -437,6 +440,7 @@
+ compatible = "aspeed,ast2500-kcs-bmc";
+ interrupts = <8>;
+ kcs_chan = <4>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ status = "disabled";
+ };
+
+diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c
+index 3c955946e647..bd1912dc5a21 100644
+--- a/drivers/char/ipmi/kcs_bmc_aspeed.c
++++ b/drivers/char/ipmi/kcs_bmc_aspeed.c
+@@ -1,11 +1,10 @@
+ // SPDX-License-Identifier: GPL-2.0
+-/*
+- * Copyright (c) 2015-2018, Intel Corporation.
+- */
++// Copyright (c) 2015-2019, Intel Corporation.
+
+ #define pr_fmt(fmt) "aspeed-kcs-bmc: " fmt
+
+ #include <linux/atomic.h>
++#include <linux/clk.h>
+ #include <linux/errno.h>
+ #include <linux/interrupt.h>
+ #include <linux/io.h>
+@@ -63,6 +62,7 @@
+
+ struct aspeed_kcs_bmc {
+ struct regmap *map;
++ struct clk *clk;
+ };
+
+
+@@ -264,36 +264,59 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
+ return -ENODEV;
+ }
+
++ priv->clk = devm_clk_get(dev, NULL);
++ if (IS_ERR(priv->clk)) {
++ rc = PTR_ERR(priv->clk);
++ if (rc != -EPROBE_DEFER)
++ dev_err(dev, "couldn't get clock\n");
++ return rc;
++ }
++ rc = clk_prepare_enable(priv->clk);
++ if (rc) {
++ dev_err(dev, "couldn't enable clock\n");
++ return rc;
++ }
++
+ kcs_bmc->ioreg = ast_kcs_bmc_ioregs[chan - 1];
+ kcs_bmc->io_inputb = aspeed_kcs_inb;
+ kcs_bmc->io_outputb = aspeed_kcs_outb;
+
+ dev_set_drvdata(dev, kcs_bmc);
+
+- aspeed_kcs_set_address(kcs_bmc, addr);
+- aspeed_kcs_enable_channel(kcs_bmc, true);
+ rc = aspeed_kcs_config_irq(kcs_bmc, pdev);
+ if (rc)
+- return rc;
++ goto err;
+
+ rc = misc_register(&kcs_bmc->miscdev);
+ if (rc) {
+ dev_err(dev, "Unable to register device\n");
+- return rc;
++ goto err;
+ }
+
++ aspeed_kcs_set_address(kcs_bmc, addr);
++ aspeed_kcs_enable_channel(kcs_bmc, true);
++
+ pr_info("channel=%u addr=0x%x idr=0x%x odr=0x%x str=0x%x\n",
+ chan, addr,
+ kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr, kcs_bmc->ioreg.str);
+
+ return 0;
++
++err:
++ aspeed_kcs_enable_channel(kcs_bmc, false);
++ clk_disable_unprepare(priv->clk);
++
++ return rc;
+ }
+
+ static int aspeed_kcs_remove(struct platform_device *pdev)
+ {
+ struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev);
++ struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
+
+ misc_deregister(&kcs_bmc->miscdev);
++ aspeed_kcs_enable_channel(kcs_bmc, false);
++ clk_disable_unprepare(priv->clk);
+
+ return 0;
+ }
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0046-misc-Add-clock-control-logic-into-Aspeed-LPC-MBOX-dr.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0046-misc-Add-clock-control-logic-into-Aspeed-LPC-MBOX-dr.patch
new file mode 100644
index 000000000..220283e24
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0046-misc-Add-clock-control-logic-into-Aspeed-LPC-MBOX-dr.patch
@@ -0,0 +1,166 @@
+From db310b43e5b444a4e2854f3d69d002c2f0d0605c Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Wed, 13 Mar 2019 15:53:24 -0700
+Subject: [PATCH] misc: Add clock control logic into Aspeed LPC MBOX driver
+
+If LPC MBOX driver is registered ahead of lpc-ctrl module, LPC
+MBOX 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
+MBOX driver.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ arch/arm/boot/dts/aspeed-g4.dtsi | 1 +
+ arch/arm/boot/dts/aspeed-g5.dtsi | 1 +
+ drivers/misc/aspeed-lpc-mbox.c | 42 +++++++++++++++++++++++++++++++---------
+ 3 files changed, 35 insertions(+), 9 deletions(-)
+
+diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
+index a5072ed1f823..729245b74c13 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -389,6 +389,7 @@
+ 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 6a2f161e7548..df9d63a94264 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -493,6 +493,7 @@
+ reg = <0x180 0x5c>;
+ interrupts = <46>;
+ #mbox-cells = <1>;
++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ status = "disabled";
+ };
+ };
+diff --git a/drivers/misc/aspeed-lpc-mbox.c b/drivers/misc/aspeed-lpc-mbox.c
+index 0933e0553953..f105d27786ac 100644
+--- a/drivers/misc/aspeed-lpc-mbox.c
++++ b/drivers/misc/aspeed-lpc-mbox.c
+@@ -7,6 +7,7 @@
+ * 2 of the License, or (at your option) any later version.
+ */
+
++#include <linux/clk.h>
+ #include <linux/interrupt.h>
+ #include <linux/mfd/syscon.h>
+ #include <linux/miscdevice.h>
+@@ -37,7 +38,9 @@
+ 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;
+ };
+@@ -237,16 +240,16 @@ static int aspeed_mbox_config_irq(struct aspeed_mbox *mbox,
+ struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+- int rc, irq;
++ int rc;
+
+- irq = irq_of_parse_and_map(dev->of_node, 0);
+- if (!irq)
++ mbox->irq = platform_get_irq(pdev, 0);
++ if (!mbox->irq)
+ return -ENODEV;
+
+- rc = devm_request_irq(dev, irq, aspeed_mbox_irq,
+- IRQF_SHARED, DEVICE_NAME, mbox);
++ 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", irq);
++ dev_err(dev, "Unable to request IRQ %d\n", mbox->irq);
+ return rc;
+ }
+
+@@ -301,6 +304,19 @@ static int aspeed_mbox_probe(struct platform_device *pdev)
+ 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;
+@@ -308,17 +324,24 @@ static int aspeed_mbox_probe(struct platform_device *pdev)
+ rc = misc_register(&mbox->miscdev);
+ if (rc) {
+ dev_err(dev, "Unable to register device\n");
+- return rc;
++ goto err;
+ }
+
+ rc = aspeed_mbox_config_irq(mbox, pdev);
+ if (rc) {
+ dev_err(dev, "Failed to configure IRQ\n");
+ misc_deregister(&mbox->miscdev);
+- return rc;
++ 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)
+@@ -326,6 +349,7 @@ 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;
+ }
+@@ -335,6 +359,7 @@ static const struct of_device_id aspeed_mbox_match[] = {
+ { .compatible = "aspeed,ast2500-mbox" },
+ { },
+ };
++MODULE_DEVICE_TABLE(of, aspeed_mbox_match);
+
+ static struct platform_driver aspeed_mbox_driver = {
+ .driver = {
+@@ -347,7 +372,6 @@ static struct platform_driver aspeed_mbox_driver = {
+
+ module_platform_driver(aspeed_mbox_driver);
+
+-MODULE_DEVICE_TABLE(of, aspeed_mbox_match);
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Cyril Bur <cyrilbur@gmail.com>");
+ MODULE_DESCRIPTION("Aspeed mailbox device driver");
+--
+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..40c26e31b
--- /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 4762687044ec864719ca14d8efa3dccdc3807e70 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/misc/aspeed-lpc-ctrl.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/misc/aspeed-lpc-ctrl.c b/drivers/misc/aspeed-lpc-ctrl.c
+index a024f8042259..c0818c7b0ffb 100644
+--- a/drivers/misc/aspeed-lpc-ctrl.c
++++ b/drivers/misc/aspeed-lpc-ctrl.c
+@@ -239,8 +239,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) {
+@@ -264,6 +266,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/0048-ARM-dts-aspeed-Set-default-status-of-LPC-BT-as-disab.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0048-ARM-dts-aspeed-Set-default-status-of-LPC-BT-as-disab.patch
new file mode 100644
index 000000000..efbea1be8
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0048-ARM-dts-aspeed-Set-default-status-of-LPC-BT-as-disab.patch
@@ -0,0 +1,40 @@
+From abf63c03805bf7df31133b720e165eab759ea702 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Thu, 14 Mar 2019 13:11:49 -0700
+Subject: [PATCH] ARM: dts: aspeed: Set default status of LPC BT as 'disabled'
+
+LPC BT is not widely used so set its default status as 'disabled'.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ arch/arm/boot/dts/aspeed-g4.dtsi | 1 +
+ arch/arm/boot/dts/aspeed-g5.dtsi | 1 +
+ 2 files changed, 2 insertions(+)
+
+diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
+index 729245b74c13..d4e1e29c6ed2 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -371,6 +371,7 @@
+ reg = <0xc0 0x18>;
+ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ interrupts = <8>;
++ status = "disabled";
+ };
+
+ sio_regs: regs {
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index df9d63a94264..a3850644b10e 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -475,6 +475,7 @@
+ reg = <0xc0 0x18>;
+ clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+ interrupts = <8>;
++ status = "disabled";
+ };
+
+ sio_regs: regs {
+--
+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..d0f98b9c1
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0049-Suppress-excessive-HID-gadget-error-logs.patch
@@ -0,0 +1,43 @@
+From 7dd0a7c62e5885bb726ef2bd5007e79a50932c38 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 | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
+index f3816a5c861e..3a94584a9dbc 100644
+--- a/drivers/usb/gadget/function/f_hid.c
++++ b/drivers/usb/gadget/function/f_hid.c
+@@ -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..d66facdfa
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0051-Add-AST2500-JTAG-device.patch
@@ -0,0 +1,35 @@
+From a2e0020ef6e03abde6819fb7fc328dcf23d0bd71 Mon Sep 17 00:00:00 2001
+From: "Hunt, Bryan" <bryan.hunt@intel.com>
+Date: Mon, 6 May 2019 10:02:14 -0700
+Subject: [PATCH] Add AST2500d JTAG driver
+
+Adding aspeed jtag device
+
+Signed-off-by: Hunt, Bryan <bryan.hunt@intel.com>
+---
+ arch/arm/boot/dts/aspeed-g5.dtsi | 9 +
+ 1 files changed, 15 insertions(+)
+
+diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
+index bf7ae631a458..2c9f287759ce 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -366,6 +366,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..6c9d46d9d
--- /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 0b8b93851bb79e70e91159f310afd4b56084977f 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 4f9f990..0102bae 100644
+--- a/drivers/Kconfig
++++ b/drivers/Kconfig
+@@ -228,4 +228,5 @@ source "drivers/siox/Kconfig"
+
+ source "drivers/slimbus/Kconfig"
+
++source "drivers/jtag/Kconfig"
+ endmenu
+diff --git a/drivers/Makefile b/drivers/Makefile
+index e1ce029..6d756c2 100644
+--- a/drivers/Makefile
++++ b/drivers/Makefile
+@@ -186,3 +186,4 @@ obj-$(CONFIG_MULTIPLEXER) += mux/
+ obj-$(CONFIG_SIOX) += siox/
+ obj-$(CONFIG_GNSS) += gnss/
+ obj-$(CONFIG_PECI) += peci/
++obj-$(CONFIG_JTAG) += jtag/
+diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig
+new file mode 100644
+index 0000000..47771fc
+--- /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 0000000..af37493
+--- /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 0000000..47503a1
+--- /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 0000000..4153c90
+--- /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 0000000..3f9e195
+--- /dev/null
++++ b/include/uapi/linux/jtag.h
+@@ -0,0 +1,214 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/* Copyright (c) 2018 Mellanox Technologies. All rights reserved. */
++/* Copyright (c) 2018 Oleksandr Shamray <oleksandrs@mellanox.com> */
++/* Copyright (c) 2019 Intel Corporation */
++
++#ifndef __UAPI_LINUX_JTAG_H
++#define __UAPI_LINUX_JTAG_H
++
++/*
++ * JTAG_XFER_MODE: JTAG transfer mode. Used to set JTAG controller transfer mode
++ * This is bitmask for feature param in jtag_mode for ioctl JTAG_SIOCMODE
++ */
++#define JTAG_XFER_MODE 0
++/*
++ * JTAG_CONTROL_MODE: JTAG controller mode. Used to set JTAG controller mode
++ * This is bitmask for feature param in jtag_mode for ioctl JTAG_SIOCMODE
++ */
++#define JTAG_CONTROL_MODE 1
++/*
++ * JTAG_MASTER_OUTPUT_DISABLE: JTAG master mode output disable, it is used to
++ * enable other devices to own the JTAG bus.
++ * This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE
++ */
++#define JTAG_MASTER_OUTPUT_DISABLE 0
++/*
++ * JTAG_MASTER_MODE: JTAG master mode. Used to set JTAG controller master mode
++ * This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE
++ */
++#define JTAG_MASTER_MODE 1
++/*
++ * JTAG_XFER_HW_MODE: JTAG hardware mode. Used to set HW drived or bitbang
++ * mode. This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE
++ */
++#define JTAG_XFER_HW_MODE 1
++/*
++ * JTAG_XFER_SW_MODE: JTAG software mode. Used to set SW drived or bitbang
++ * mode. This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE
++ */
++#define JTAG_XFER_SW_MODE 0
++
++/**
++ * enum jtag_tapstate:
++ *
++ * @JTAG_STATE_TLRESET: JTAG state machine Test Logic Reset state
++ * @JTAG_STATE_IDLE: JTAG state machine IDLE state
++ * @JTAG_STATE_SELECTDR: JTAG state machine SELECT_DR state
++ * @JTAG_STATE_CAPTUREDR: JTAG state machine CAPTURE_DR state
++ * @JTAG_STATE_SHIFTDR: JTAG state machine SHIFT_DR state
++ * @JTAG_STATE_EXIT1DR: JTAG state machine EXIT-1 DR state
++ * @JTAG_STATE_PAUSEDR: JTAG state machine PAUSE_DR state
++ * @JTAG_STATE_EXIT2DR: JTAG state machine EXIT-2 DR state
++ * @JTAG_STATE_UPDATEDR: JTAG state machine UPDATE DR state
++ * @JTAG_STATE_SELECTIR: JTAG state machine SELECT_IR state
++ * @JTAG_STATE_CAPTUREIR: JTAG state machine CAPTURE_IR state
++ * @JTAG_STATE_SHIFTIR: JTAG state machine SHIFT_IR state
++ * @JTAG_STATE_EXIT1IR: JTAG state machine EXIT-1 IR state
++ * @JTAG_STATE_PAUSEIR: JTAG state machine PAUSE_IR state
++ * @JTAG_STATE_EXIT2IR: JTAG state machine EXIT-2 IR state
++ * @JTAG_STATE_UPDATEIR: JTAG state machine UPDATE IR state
++ * @JTAG_STATE_CURRENT: JTAG current state, saved by driver
++ */
++enum jtag_tapstate {
++ JTAG_STATE_TLRESET,
++ JTAG_STATE_IDLE,
++ JTAG_STATE_SELECTDR,
++ JTAG_STATE_CAPTUREDR,
++ JTAG_STATE_SHIFTDR,
++ JTAG_STATE_EXIT1DR,
++ JTAG_STATE_PAUSEDR,
++ JTAG_STATE_EXIT2DR,
++ JTAG_STATE_UPDATEDR,
++ JTAG_STATE_SELECTIR,
++ JTAG_STATE_CAPTUREIR,
++ JTAG_STATE_SHIFTIR,
++ JTAG_STATE_EXIT1IR,
++ JTAG_STATE_PAUSEIR,
++ JTAG_STATE_EXIT2IR,
++ JTAG_STATE_UPDATEIR,
++ JTAG_STATE_CURRENT
++};
++
++/**
++ * enum jtag_reset:
++ *
++ * @JTAG_NO_RESET: JTAG run TAP from current state
++ * @JTAG_FORCE_RESET: JTAG force TAP to reset state
++ */
++enum jtag_reset {
++ JTAG_NO_RESET = 0,
++ JTAG_FORCE_RESET = 1,
++};
++
++/**
++ * enum jtag_xfer_type:
++ *
++ * @JTAG_SIR_XFER: SIR transfer
++ * @JTAG_SDR_XFER: SDR transfer
++ */
++enum jtag_xfer_type {
++ JTAG_SIR_XFER = 0,
++ JTAG_SDR_XFER = 1,
++};
++
++/**
++ * enum jtag_xfer_direction:
++ *
++ * @JTAG_READ_XFER: read transfer
++ * @JTAG_WRITE_XFER: write transfer
++ * @JTAG_READ_WRITE_XFER: read & write transfer
++ */
++enum jtag_xfer_direction {
++ JTAG_READ_XFER = 1,
++ JTAG_WRITE_XFER = 2,
++ JTAG_READ_WRITE_XFER = 3,
++};
++
++/**
++ * struct jtag_tap_state - forces JTAG state machine to go into a TAPC
++ * state
++ *
++ * @reset: 0 - run IDLE/PAUSE from current state
++ * 1 - go through TEST_LOGIC/RESET state before IDLE/PAUSE
++ * @end: completion flag
++ * @tck: clock counter
++ *
++ * Structure provide interface to JTAG device for JTAG set state execution.
++ */
++struct jtag_tap_state {
++ __u8 reset;
++ __u8 from;
++ __u8 endstate;
++ __u8 tck;
++};
++
++/**
++ * struct jtag_xfer - jtag xfer:
++ *
++ * @type: transfer type
++ * @direction: xfer direction
++ * @from: xfer current state
++ * @endstate: xfer end state
++ * @padding: xfer padding
++ * @length: xfer bits length
++ * @tdio : xfer data array
++ *
++ * Structure provide interface to JTAG device for JTAG SDR/SIR xfer execution.
++ */
++struct jtag_xfer {
++ __u8 type;
++ __u8 direction;
++ __u8 from;
++ __u8 endstate;
++ __u8 padding;
++ __u32 length;
++ __u64 tdio;
++};
++
++/**
++ * struct bitbang_packet - jtag bitbang array packet:
++ *
++ * @data: JTAG Bitbang struct array pointer(input/output)
++ * @length: array size (input)
++ *
++ * Structure provide interface to JTAG device for JTAG bitbang bundle execution
++ */
++struct bitbang_packet {
++ struct tck_bitbang *data;
++ __u32 length;
++} __attribute__((__packed__));
++
++/**
++ * struct jtag_bitbang - jtag bitbang:
++ *
++ * @tms: JTAG TMS
++ * @tdi: JTAG TDI (input)
++ * @tdo: JTAG TDO (output)
++ *
++ * Structure provide interface to JTAG device for JTAG bitbang execution.
++ */
++struct tck_bitbang {
++ __u8 tms;
++ __u8 tdi;
++ __u8 tdo;
++} __attribute__((__packed__));
++
++/**
++ * struct jtag_mode - jtag mode:
++ *
++ * @feature: 0 - JTAG feature setting selector for JTAG controller HW/SW
++ * 1 - JTAG feature setting selector for controller bus master
++ * mode output (enable / disable).
++ * @mode: (0 - SW / 1 - HW) for JTAG_XFER_MODE feature(0)
++ * (0 - output disable / 1 - output enable) for JTAG_CONTROL_MODE
++ * feature(1)
++ *
++ * Structure provide configuration modes to JTAG device.
++ */
++struct jtag_mode {
++ __u32 feature;
++ __u32 mode;
++};
++
++/* ioctl interface */
++#define __JTAG_IOCTL_MAGIC 0xb2
++
++#define JTAG_SIOCSTATE _IOW(__JTAG_IOCTL_MAGIC, 0, struct jtag_tap_state)
++#define JTAG_SIOCFREQ _IOW(__JTAG_IOCTL_MAGIC, 1, unsigned int)
++#define JTAG_GIOCFREQ _IOR(__JTAG_IOCTL_MAGIC, 2, unsigned int)
++#define JTAG_IOCXFER _IOWR(__JTAG_IOCTL_MAGIC, 3, struct jtag_xfer)
++#define JTAG_GIOCSTATUS _IOWR(__JTAG_IOCTL_MAGIC, 4, enum jtag_tapstate)
++#define JTAG_SIOCMODE _IOW(__JTAG_IOCTL_MAGIC, 5, unsigned int)
++#define JTAG_IOCBITBANG _IOW(__JTAG_IOCTL_MAGIC, 6, unsigned int)
++
++#endif /* __UAPI_LINUX_JTAG_H */
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0053-Add-Aspeed-SoC-24xx-and-25xx-families-JTAG.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0053-Add-Aspeed-SoC-24xx-and-25xx-families-JTAG.patch
new file mode 100644
index 000000000..94722d6c4
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0053-Add-Aspeed-SoC-24xx-and-25xx-families-JTAG.patch
@@ -0,0 +1,1294 @@
+From 817a43d1b1e197e7eff43492599469bbc23bf0fd Mon Sep 17 00:00:00 2001
+From: "Corona, Ernesto" <ernesto.corona@intel.com>
+Date: Mon, 3 Jun 2019 08:22:09 -0800
+Subject: [PATCH v29 2/6] Add Aspeed SoC 24xx and 25xx families JTAG master
+ driver
+
+Driver adds support of Aspeed 2500/2400 series SOC JTAG master controller.
+
+Driver implements the following jtag ops:
+- freq_get;
+- freq_set;
+- status_get;
+- status_set
+- xfer;
+- mode_set;
+- bitbang;
+- enable;
+- disable;
+
+It has been tested on Mellanox system with BMC equipped with
+Aspeed 2520 SoC for programming CPLD devices.
+
+It has also been tested on Intel system using Aspeed 25xx SoC
+for JTAG communication.
+
+Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
+Signed-off-by: Jiri Pirko <jiri@mellanox.com>
+Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com>
+Acked-by: Arnd Bergmann <arnd@arndb.de>
+Acked-by: Philippe Ombredanne <pombredanne@nexb.com>
+Acked-by: Joel Stanley <joel@jms.id.au>
+Cc: Vadim Pasternak <vadimp@mellanox.com>
+Cc: Andrew Jeffery <andrew@aj.id.au>
+Cc: Steven A Filary <steven.a.filary@intel.com>
+Cc: Bryan Hunt <bryan.hunt@intel.com>
+---
+v28->v29
+Comments pointed by Steven Filary <steven.a.filary@intel.com>
+- Expand bitbang function to accept multiples bitbang operations within a
+ single JTAG_IOCBITBANG call. It will receive a buffer with TDI and TMS
+ values and it is expected that driver fills TDO fields with its
+ corresponding output value for every transaction.
+- Always setup JTAG controller to master mode but disable JTAG output when
+ the driver is not in use to allow other HW to own the JTAG bus. Remove SCU
+ register accesses. This register controls the JTAG controller mode
+ (master/slave).
+- Encansulate dev_dgb message into DEBUG_JTAG macros to improve driver's JTAG
+ performace.
+- Add support for multichain. Set tap state and xfer operations now include
+ two tap state arguments: current state and end state.
+
+v27->v28
+Comments pointed by Steven Filary <steven.a.filary@intel.com>
+- Replace JTAG_IOCRUNTEST with JTAG_SIOCSTATE adding support for all TAPC
+ end states in SW mode using a lookup table to navigate across states.
+- Add support for simultaneous READ/WRITE transfers(JTAG_READ_WRITE_XFER).
+- Support for switching JTAG controller mode between slave and master
+ mode.
+- Setup JTAG controller mode to master only when the driver is opened,
+ letting other HW to own the JTAG bus when it isn't in use.
+- Include JTAG bit bang IOCTL for low level JTAG control usage
+ (JTAG_IOCBITBANG).
+- Add debug traces.
+- Add support for register polling (default) due it is 3 times faster than
+ interrupt mode. Define USE_INTERRUPTS macro to enable interrupt usage.
+- Remove unnecessary delays for aspeed_jtag_status_set function. It makes
+ SW mode 4 times faster.
+- Clean data buffer on aspeed_jtag_xfer_sw before tdo writes to avoid data
+ output corruption for read operations in SW mode.
+- Correct register settings for HW mode transfer operations.
+- Propagate ret codes all the way from low level functions up to
+ JTAG_IOCXFER call.
+- Support for partitioned transfers. Single JTAG transfer through
+ multiples JTAG_IOCXFER calls. Now end transmission(scan_end) also
+ evaluates transfer end state.
+
+v26->v27
+Changes made by Oleksandr Shamray <oleksandrs@mellamnox.com>
+- change aspeed_jtag_sw_delay to udelay function in bit-bang operation
+
+v25->v26
+v24->v25
+Comments pointed by Greg KH <gregkh@linuxfoundation.org>
+- reduced debug printouts
+
+v23->v24
+v22->v23
+v21->v22
+Comments pointed by Andy Shevchenko <andy.shevchenko@gmail.com>
+- rearrange ASPEED register defines
+- simplified JTAG divider calculation formula
+- change delay function in bit-bang operation
+- add helper functions for TAP states switching
+- remove unnecessary comments
+- remove redundant debug messages
+- make dines for repetative register bit sets
+- fixed indentation
+- change checks from negative to positive
+- add error check for clk_prepare_enable
+- rework driver alloc/register to devm_ variant
+- Increasing line length up to 85 in order to improve readability
+
+v20->v21
+v19->v20
+Notifications from kbuild test robot <lkp@intel.com>
+- add static declaration to 'aspeed_jtag_init' and
+ 'aspeed_jtag_deinit' functions
+
+v18->v19
+v17->v18
+v16->v17
+v15->v16
+Comments pointed by Joel Stanley <joel.stan@gmail.com>
+- Add reset_control on Jtag init/deinit
+
+v14->v15
+Comments pointed by Joel Stanley <joel.stan@gmail.com>
+- Add ARCH_ASPEED || COMPILE_TEST to Kconfig
+- remove unused offset variable
+- remove "aspeed_jtag" from dev_err and dev_dbg messages
+- change clk_prepare_enable initialisation order
+
+v13->v14
+Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
+- Change style of head block comment from /**/ to //
+
+v12->v13
+Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
+- Change jtag-aspeed.c licence type to
+ SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+ and reorder line with license- add reset descriptions in bndings file
+ in description
+Comments pointed by Kun Yi <kunyi@google.com>
+- Changed capability check for aspeed,ast2400-jtag/ast200-jtag
+
+v11->v12
+Comments pointed by Chip Bilbrey <chip@bilbrey.org>
+- Remove access mode from xfer and idle transactions
+- Add new ioctl JTAG_SIOCMODE for set hw mode
+
+v10->v11
+v9->v10
+V8->v9
+Comments pointed by Arnd Bergmann <arnd@arndb.de>
+- add *data parameter to xfer function prototype
+
+v7->v8
+Comments pointed by Joel Stanley <joel.stan@gmail.com>
+- aspeed_jtag_init replace goto to return;
+- change input variables type from __u32 to u32
+ in functios freq_get, freq_set, status_get
+- change sm_ variables type from char to u8
+- in jatg_init add disable clocks on error case
+- remove release_mem_region on error case
+- remove devm_free_irq on jtag_deinit
+- Fix misspelling Disabe/Disable
+- Change compatible string to ast2400 and ast2000
+
+v6->v7
+Notifications from kbuild test robot <lkp@intel.com>
+- Add include <linux/types.h> to jtag-asapeed.c
+
+v5->v6
+v4->v5
+Comments pointed by Arnd Bergmann <arnd@arndb.de>
+- Added HAS_IOMEM dependence in Kconfig to avoid
+ "undefined reference to `devm_ioremap_resource'" error,
+ because in some arch this not supported
+
+v3->v4
+Comments pointed by Arnd Bergmann <arnd@arndb.de>
+- change transaction pointer tdio type to __u64
+- change internal status type from enum to __u32
+
+v2->v3
+
+v1->v2
+Comments pointed by Greg KH <gregkh@linuxfoundation.org>
+- change license type from GPLv2/BSD to GPLv2
+
+Comments pointed by Neil Armstrong <narmstrong@baylibre.com>
+- Add clk_prepare_enable/clk_disable_unprepare in clock init/deinit
+- Change .compatible to soc-specific compatible names
+ aspeed,aspeed4000-jtag/aspeed5000-jtag
+- Added dt-bindings
+
+Comments pointed by Arnd Bergmann <arnd@arndb.de>
+- Reorder functions and removed the forward declarations
+- Add static const qualifier to state machine states transitions
+- Change .compatible to soc-specific compatible names
+ aspeed,aspeed4000-jtag/aspeed5000-jtag
+- Add dt-bindings
+
+Comments pointed by Randy Dunlap <rdunlap@infradead.org>
+- Change module name jtag-aspeed in description in Kconfig
+
+Comments pointed by kbuild test robot <lkp@intel.com>
+- Remove invalid include <asm/mach-types.h>
+- add resource_size instead of calculation
+---
+ drivers/jtag/Kconfig | 14 +
+ drivers/jtag/Makefile | 1 +
+ drivers/jtag/jtag-aspeed.c | 1050 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 1065 insertions(+)
+ create mode 100644 drivers/jtag/jtag-aspeed.c
+
+diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig
+index 47771fc..0cc163f 100644
+--- a/drivers/jtag/Kconfig
++++ b/drivers/jtag/Kconfig
+@@ -15,3 +15,17 @@ menuconfig JTAG
+
+ To compile this driver as a module, choose M here: the module will
+ be called jtag.
++
++menuconfig JTAG_ASPEED
++ tristate "Aspeed SoC JTAG controller support"
++ depends on JTAG && HAS_IOMEM
++ depends on ARCH_ASPEED || COMPILE_TEST
++ help
++ This provides a support for Aspeed JTAG device, equipped on
++ Aspeed SoC 24xx and 25xx families. Drivers allows programming
++ of hardware devices, connected to SoC through the JTAG interface.
++
++ If you want this support, you should say Y here.
++
++ To compile this driver as a module, choose M here: the module will
++ be called jtag-aspeed.
+diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile
+index af37493..04a855e 100644
+--- a/drivers/jtag/Makefile
++++ b/drivers/jtag/Makefile
+@@ -1 +1,2 @@
+ obj-$(CONFIG_JTAG) += jtag.o
++obj-$(CONFIG_JTAG_ASPEED) += jtag-aspeed.o
+diff --git a/drivers/jtag/jtag-aspeed.c b/drivers/jtag/jtag-aspeed.c
+new file mode 100644
+index 0000000..1d41a66
+--- /dev/null
++++ b/drivers/jtag/jtag-aspeed.c
+@@ -0,0 +1,1050 @@
++// SPDX-License-Identifier: GPL-2.0
++// Copyright (c) 2018 Mellanox Technologies. All rights reserved.
++// Copyright (c) 2018 Oleksandr Shamray <oleksandrs@mellanox.com>
++// Copyright (c) 2019 Intel Corporation
++
++#include <linux/clk.h>
++#include <linux/device.h>
++#include <linux/interrupt.h>
++#include <linux/jtag.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of_address.h>
++#include <linux/platform_device.h>
++#include <linux/reset.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/delay.h>
++#include <uapi/linux/jtag.h>
++
++#define ASPEED_SCU_RESET_JTAG BIT(22)
++
++#define ASPEED_JTAG_DATA 0x00
++#define ASPEED_JTAG_INST 0x04
++#define ASPEED_JTAG_CTRL 0x08
++#define ASPEED_JTAG_ISR 0x0C
++#define ASPEED_JTAG_SW 0x10
++#define ASPEED_JTAG_TCK 0x14
++#define ASPEED_JTAG_EC 0x18
++
++#define ASPEED_JTAG_DATA_MSB 0x01
++#define ASPEED_JTAG_DATA_CHUNK_SIZE 0x20
++
++/* ASPEED_JTAG_CTRL: Engine Control */
++#define ASPEED_JTAG_CTL_ENG_EN BIT(31)
++#define ASPEED_JTAG_CTL_ENG_OUT_EN BIT(30)
++#define ASPEED_JTAG_CTL_FORCE_TMS BIT(29)
++#define ASPEED_JTAG_CTL_IR_UPDATE BIT(26)
++#define ASPEED_JTAG_CTL_INST_LEN(x) ((x) << 20)
++#define ASPEED_JTAG_CTL_LASPEED_INST BIT(17)
++#define ASPEED_JTAG_CTL_INST_EN BIT(16)
++#define ASPEED_JTAG_CTL_DR_UPDATE BIT(10)
++#define ASPEED_JTAG_CTL_DATA_LEN(x) ((x) << 4)
++#define ASPEED_JTAG_CTL_LASPEED_DATA BIT(1)
++#define ASPEED_JTAG_CTL_DATA_EN BIT(0)
++
++/* ASPEED_JTAG_ISR : Interrupt status and enable */
++#define ASPEED_JTAG_ISR_INST_PAUSE BIT(19)
++#define ASPEED_JTAG_ISR_INST_COMPLETE BIT(18)
++#define ASPEED_JTAG_ISR_DATA_PAUSE BIT(17)
++#define ASPEED_JTAG_ISR_DATA_COMPLETE BIT(16)
++#define ASPEED_JTAG_ISR_INST_PAUSE_EN BIT(3)
++#define ASPEED_JTAG_ISR_INST_COMPLETE_EN BIT(2)
++#define ASPEED_JTAG_ISR_DATA_PAUSE_EN BIT(1)
++#define ASPEED_JTAG_ISR_DATA_COMPLETE_EN BIT(0)
++#define ASPEED_JTAG_ISR_INT_EN_MASK GENMASK(3, 0)
++#define ASPEED_JTAG_ISR_INT_MASK GENMASK(19, 16)
++
++/* ASPEED_JTAG_SW : Software Mode and Status */
++#define ASPEED_JTAG_SW_MODE_EN BIT(19)
++#define ASPEED_JTAG_SW_MODE_TCK BIT(18)
++#define ASPEED_JTAG_SW_MODE_TMS BIT(17)
++#define ASPEED_JTAG_SW_MODE_TDIO BIT(16)
++
++/* ASPEED_JTAG_TCK : TCK Control */
++#define ASPEED_JTAG_TCK_DIVISOR_MASK GENMASK(10, 0)
++#define ASPEED_JTAG_TCK_GET_DIV(x) ((x) & ASPEED_JTAG_TCK_DIVISOR_MASK)
++
++/* ASPEED_JTAG_EC : Controller set for go to IDLE */
++#define ASPEED_JTAG_EC_GO_IDLE BIT(0)
++
++#define ASPEED_JTAG_IOUT_LEN(len) \
++ (ASPEED_JTAG_CTL_ENG_EN | \
++ ASPEED_JTAG_CTL_ENG_OUT_EN | \
++ ASPEED_JTAG_CTL_INST_LEN(len))
++
++#define ASPEED_JTAG_DOUT_LEN(len) \
++ (ASPEED_JTAG_CTL_ENG_EN | \
++ ASPEED_JTAG_CTL_ENG_OUT_EN | \
++ ASPEED_JTAG_CTL_DATA_LEN(len))
++
++#define ASPEED_JTAG_SW_TDIO (ASPEED_JTAG_SW_MODE_EN | ASPEED_JTAG_SW_MODE_TDIO)
++
++#define ASPEED_JTAG_GET_TDI(direction, byte) \
++ (((direction) & JTAG_WRITE_XFER) ? byte : UINT_MAX)
++
++#define ASPEED_JTAG_TCK_WAIT 10
++#define ASPEED_JTAG_RESET_CNTR 10
++#define WAIT_ITERATIONS 75
++
++/*#define USE_INTERRUPTS*/
++/*#define DEBUG_JTAG*/
++
++static const char * const regnames[] = {
++ [ASPEED_JTAG_DATA] = "ASPEED_JTAG_DATA",
++ [ASPEED_JTAG_INST] = "ASPEED_JTAG_INST",
++ [ASPEED_JTAG_CTRL] = "ASPEED_JTAG_CTRL",
++ [ASPEED_JTAG_ISR] = "ASPEED_JTAG_ISR",
++ [ASPEED_JTAG_SW] = "ASPEED_JTAG_SW",
++ [ASPEED_JTAG_TCK] = "ASPEED_JTAG_TCK",
++ [ASPEED_JTAG_EC] = "ASPEED_JTAG_EC",
++};
++
++#define ASPEED_JTAG_NAME "jtag-aspeed"
++
++struct aspeed_jtag {
++ void __iomem *reg_base;
++ struct device *dev;
++ struct clk *pclk;
++ enum jtag_tapstate status;
++ int irq;
++ struct reset_control *rst;
++ u32 flag;
++ wait_queue_head_t jtag_wq;
++ u32 mode;
++};
++
++/*
++ * This structure represents a TMS cycle, as expressed in a set of bits and a
++ * count of bits (note: there are no start->end state transitions that require
++ * more than 1 byte of TMS cycles)
++ */
++struct tms_cycle {
++ unsigned char tmsbits;
++ unsigned char count;
++};
++
++/*
++ * This is the complete set TMS cycles for going from any TAP state to any
++ * other TAP state, following a "shortest path" rule.
++ */
++static const struct tms_cycle _tms_cycle_lookup[][16] = {
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* TLR */{{0x00, 0}, {0x00, 1}, {0x02, 2}, {0x02, 3}, {0x02, 4}, {0x0a, 4},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x0a, 5}, {0x2a, 6}, {0x1a, 5}, {0x06, 3}, {0x06, 4}, {0x06, 5},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x16, 5}, {0x16, 6}, {0x56, 7}, {0x36, 6} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* RTI */{{0x07, 3}, {0x00, 0}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x05, 4}, {0x15, 5}, {0x0d, 4}, {0x03, 2}, {0x03, 3}, {0x03, 4},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* SelDR*/{{0x03, 2}, {0x03, 3}, {0x00, 0}, {0x00, 1}, {0x00, 2}, {0x02, 2},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x02, 3}, {0x0a, 4}, {0x06, 3}, {0x01, 1}, {0x01, 2}, {0x01, 3},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x05, 3}, {0x05, 4}, {0x15, 5}, {0x0d, 4} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* CapDR*/{{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x00, 0}, {0x00, 1}, {0x01, 1},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x01, 2}, {0x05, 3}, {0x03, 2}, {0x0f, 4}, {0x0f, 5}, {0x0f, 6},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, {0x6f, 7} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* SDR */{{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x00, 0}, {0x01, 1},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x01, 2}, {0x05, 3}, {0x03, 2}, {0x0f, 4}, {0x0f, 5}, {0x0f, 6},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, {0x6f, 7} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* Ex1DR*/{{0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x02, 3}, {0x00, 0},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x00, 1}, {0x02, 2}, {0x01, 1}, {0x07, 3}, {0x07, 4}, {0x07, 5},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* PDR */{{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x01, 2}, {0x05, 3},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x00, 0}, {0x01, 1}, {0x03, 2}, {0x0f, 4}, {0x0f, 5}, {0x0f, 6},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, {0x6f, 7} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* Ex2DR*/{{0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x00, 1}, {0x02, 2},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x02, 3}, {0x00, 0}, {0x01, 1}, {0x07, 3}, {0x07, 4}, {0x07, 5},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* UpdDR*/{{0x07, 3}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x05, 4}, {0x15, 5}, {0x00, 0}, {0x03, 2}, {0x03, 3}, {0x03, 4},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* SelIR*/{{0x01, 1}, {0x01, 2}, {0x05, 3}, {0x05, 4}, {0x05, 5}, {0x15, 5},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x15, 6}, {0x55, 7}, {0x35, 6}, {0x00, 0}, {0x00, 1}, {0x00, 2},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x02, 2}, {0x02, 3}, {0x0a, 4}, {0x06, 3} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* CapIR*/{{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, {0x00, 0}, {0x00, 1},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* SIR */{{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, {0x0f, 5}, {0x00, 0},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* Ex1IR*/{{0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x0b, 5}, {0x2b, 6}, {0x1b, 5}, {0x07, 3}, {0x07, 4}, {0x02, 3},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x00, 0}, {0x00, 1}, {0x02, 2}, {0x01, 1} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* PIR */{{0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, {0x0f, 5}, {0x01, 2},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x05, 3}, {0x00, 0}, {0x01, 1}, {0x03, 2} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* Ex2IR*/{{0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x0b, 5}, {0x2b, 6}, {0x1b, 5}, {0x07, 3}, {0x07, 4}, {0x00, 1},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x02, 2}, {0x02, 3}, {0x00, 0}, {0x01, 1} },
++
++/* TLR RTI SelDR CapDR SDR Ex1DR*/
++/* UpdIR*/{{0x07, 3}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3},
++/* PDR Ex2DR UpdDR SelIR CapIR SIR*/
++ {0x05, 4}, {0x15, 5}, {0x0d, 4}, {0x03, 2}, {0x03, 3}, {0x03, 4},
++/* Ex1IR PIR Ex2IR UpdIR*/
++ {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x00, 0} },
++};
++
++#ifdef DEBUG_JTAG
++static char *end_status_str[] = {
++ "tlr", "idle", "selDR", "capDR", "sDR", "ex1DR", "pDR", "ex2DR",
++ "updDR", "selIR", "capIR", "sIR", "ex1IR", "pIR", "ex2IR", "updIR"
++};
++#endif
++
++static u32 aspeed_jtag_read(struct aspeed_jtag *aspeed_jtag, u32 reg)
++{
++ u32 val = readl(aspeed_jtag->reg_base + reg);
++
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev, "read:%s val = 0x%08x\n", regnames[reg], val);
++#endif
++ return val;
++}
++
++static void
++aspeed_jtag_write(struct aspeed_jtag *aspeed_jtag, u32 val, u32 reg)
++{
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev, "write:%s val = 0x%08x\n",
++ regnames[reg], val);
++#endif
++ writel(val, aspeed_jtag->reg_base + reg);
++}
++
++static int aspeed_jtag_freq_set(struct jtag *jtag, u32 freq)
++{
++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
++ unsigned long apb_frq;
++ u32 tck_val;
++ u16 div;
++
++ apb_frq = clk_get_rate(aspeed_jtag->pclk);
++ if (!apb_frq)
++ return -ENOTSUPP;
++
++ div = (apb_frq - 1) / freq;
++ tck_val = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_TCK);
++ aspeed_jtag_write(aspeed_jtag,
++ (tck_val & ~ASPEED_JTAG_TCK_DIVISOR_MASK) | div,
++ ASPEED_JTAG_TCK);
++ return 0;
++}
++
++static int aspeed_jtag_freq_get(struct jtag *jtag, u32 *frq)
++{
++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
++ u32 pclk;
++ u32 tck;
++
++ pclk = clk_get_rate(aspeed_jtag->pclk);
++ tck = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_TCK);
++ *frq = pclk / (ASPEED_JTAG_TCK_GET_DIV(tck) + 1);
++
++ return 0;
++}
++
++static inline void aspeed_jtag_output_disable(struct aspeed_jtag *aspeed_jtag)
++{
++ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_CTRL);
++}
++
++static inline void aspeed_jtag_master(struct aspeed_jtag *aspeed_jtag)
++{
++ aspeed_jtag_write(aspeed_jtag, (ASPEED_JTAG_CTL_ENG_EN |
++ ASPEED_JTAG_CTL_ENG_OUT_EN),
++ ASPEED_JTAG_CTRL);
++
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
++ ASPEED_JTAG_SW_MODE_TDIO,
++ ASPEED_JTAG_SW);
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_ISR_INST_PAUSE |
++ ASPEED_JTAG_ISR_INST_COMPLETE |
++ ASPEED_JTAG_ISR_DATA_PAUSE |
++ ASPEED_JTAG_ISR_DATA_COMPLETE |
++ ASPEED_JTAG_ISR_INST_PAUSE_EN |
++ ASPEED_JTAG_ISR_INST_COMPLETE_EN |
++ ASPEED_JTAG_ISR_DATA_PAUSE_EN |
++ ASPEED_JTAG_ISR_DATA_COMPLETE_EN,
++ ASPEED_JTAG_ISR); /* Enable Interrupt */
++}
++
++static int aspeed_jtag_mode_set(struct jtag *jtag, struct jtag_mode *jtag_mode)
++{
++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
++
++ switch (jtag_mode->feature) {
++ case JTAG_XFER_MODE:
++ aspeed_jtag->mode = jtag_mode->mode;
++ break;
++ case JTAG_CONTROL_MODE:
++ if (jtag_mode->mode == JTAG_MASTER_OUTPUT_DISABLE)
++ aspeed_jtag_output_disable(aspeed_jtag);
++ else if (jtag_mode->mode == JTAG_MASTER_MODE)
++ aspeed_jtag_master(aspeed_jtag);
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static char aspeed_jtag_tck_cycle(struct aspeed_jtag *aspeed_jtag,
++ u8 tms, u8 tdi)
++{
++ char tdo = 0;
++
++ /* TCK = 0 */
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
++ (tms * ASPEED_JTAG_SW_MODE_TMS) |
++ (tdi * ASPEED_JTAG_SW_MODE_TDIO), ASPEED_JTAG_SW);
++
++ aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_SW);
++
++ /* TCK = 1 */
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
++ ASPEED_JTAG_SW_MODE_TCK |
++ (tms * ASPEED_JTAG_SW_MODE_TMS) |
++ (tdi * ASPEED_JTAG_SW_MODE_TDIO), ASPEED_JTAG_SW);
++
++ if (aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_SW) &
++ ASPEED_JTAG_SW_MODE_TDIO)
++ tdo = 1;
++
++ return tdo;
++}
++
++static int aspeed_jtag_bitbang(struct jtag *jtag,
++ struct bitbang_packet *bitbang,
++ struct tck_bitbang *bitbang_data)
++{
++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
++ int i = 0;
++
++ for (i = 0; i < bitbang->length; i++) {
++ bitbang_data[i].tdo =
++ aspeed_jtag_tck_cycle(aspeed_jtag, bitbang_data[i].tms,
++ bitbang_data[i].tdi);
++ }
++ return 0;
++}
++
++static int aspeed_jtag_wait_instruction_pause(struct aspeed_jtag *aspeed_jtag)
++{
++ int res = 0;
++#ifdef USE_INTERRUPTS
++ res = wait_event_interruptible(aspeed_jtag->jtag_wq,
++ aspeed_jtag->flag &
++ ASPEED_JTAG_ISR_INST_PAUSE);
++ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_INST_PAUSE;
++#else
++ u32 status = 0;
++ u32 iterations = 0;
++
++ while ((status & ASPEED_JTAG_ISR_INST_PAUSE) == 0) {
++ status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR);
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev, "%s = 0x%08x\n", __func__, status);
++#endif
++ iterations++;
++ if (iterations > WAIT_ITERATIONS) {
++ dev_err(aspeed_jtag->dev,
++ "aspeed_jtag driver timed out waiting for instruction pause complete\n");
++ res = -EFAULT;
++ break;
++ }
++ if ((status & ASPEED_JTAG_ISR_DATA_COMPLETE) == 0) {
++ if (iterations % 25 == 0)
++ usleep_range(1, 5);
++ else
++ udelay(1);
++ }
++ }
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_ISR_INST_PAUSE |
++ (status & 0xf),
++ ASPEED_JTAG_ISR);
++#endif
++ return res;
++}
++
++static int
++aspeed_jtag_wait_instruction_complete(struct aspeed_jtag *aspeed_jtag)
++{
++ int res = 0;
++#ifdef USE_INTERRUPTS
++ res = wait_event_interruptible(aspeed_jtag->jtag_wq,
++ aspeed_jtag->flag &
++ ASPEED_JTAG_ISR_INST_COMPLETE);
++ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_INST_COMPLETE;
++#else
++ u32 status = 0;
++ u32 iterations = 0;
++
++ while ((status & ASPEED_JTAG_ISR_INST_COMPLETE) == 0) {
++ status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR);
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev, "%s = 0x%08x\n", __func__, status);
++#endif
++ iterations++;
++ if (iterations > WAIT_ITERATIONS) {
++ dev_err(aspeed_jtag->dev,
++ "aspeed_jtag driver timed out waiting for instruction complete\n");
++ res = -EFAULT;
++ break;
++ }
++ if ((status & ASPEED_JTAG_ISR_DATA_COMPLETE) == 0) {
++ if (iterations % 25 == 0)
++ usleep_range(1, 5);
++ else
++ udelay(1);
++ }
++ }
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_ISR_INST_COMPLETE |
++ (status & 0xf),
++ ASPEED_JTAG_ISR);
++#endif
++ return res;
++}
++
++static int
++aspeed_jtag_wait_data_pause_complete(struct aspeed_jtag *aspeed_jtag)
++{
++ int res = 0;
++#ifdef USE_INTERRUPTS
++ res = wait_event_interruptible(aspeed_jtag->jtag_wq,
++ aspeed_jtag->flag &
++ ASPEED_JTAG_ISR_DATA_PAUSE);
++ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_DATA_PAUSE;
++#else
++ u32 status = 0;
++ u32 iterations = 0;
++
++ while ((status & ASPEED_JTAG_ISR_DATA_PAUSE) == 0) {
++ status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR);
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev, "%s = 0x%08x\n", __func__, status);
++#endif
++ iterations++;
++ if (iterations > WAIT_ITERATIONS) {
++ dev_err(aspeed_jtag->dev,
++ "aspeed_jtag driver timed out waiting for data pause complete\n");
++ res = -EFAULT;
++ break;
++ }
++ if ((status & ASPEED_JTAG_ISR_DATA_COMPLETE) == 0) {
++ if (iterations % 25 == 0)
++ usleep_range(1, 5);
++ else
++ udelay(1);
++ }
++ }
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_ISR_DATA_PAUSE |
++ (status & 0xf), ASPEED_JTAG_ISR);
++#endif
++ return res;
++}
++
++static int aspeed_jtag_wait_data_complete(struct aspeed_jtag *aspeed_jtag)
++{
++ int res = 0;
++#ifdef USE_INTERRUPTS
++ res = wait_event_interruptible(aspeed_jtag->jtag_wq,
++ aspeed_jtag->flag &
++ ASPEED_JTAG_ISR_DATA_COMPLETE);
++ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_DATA_COMPLETE;
++#else
++ u32 status = 0;
++ u32 iterations = 0;
++
++ while ((status & ASPEED_JTAG_ISR_DATA_COMPLETE) == 0) {
++ status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR);
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev, "%s = 0x%08x\n", __func__, status);
++#endif
++ iterations++;
++ if (iterations > WAIT_ITERATIONS) {
++ dev_err(aspeed_jtag->dev,
++ "ast_jtag driver timed out waiting for data complete\n");
++ res = -EFAULT;
++ break;
++ }
++ if ((status & ASPEED_JTAG_ISR_DATA_COMPLETE) == 0) {
++ if (iterations % 25 == 0)
++ usleep_range(1, 5);
++ else
++ udelay(1);
++ }
++ }
++ aspeed_jtag_write(aspeed_jtag,
++ ASPEED_JTAG_ISR_DATA_COMPLETE | (status & 0xf),
++ ASPEED_JTAG_ISR);
++#endif
++ return res;
++}
++
++static void aspeed_jtag_set_tap_state(struct aspeed_jtag *aspeed_jtag,
++ enum jtag_tapstate from_state,
++ enum jtag_tapstate end_state)
++{
++ int i = 0;
++ enum jtag_tapstate from, to;
++
++ from = from_state;
++ to = end_state;
++
++ if (from == JTAG_STATE_CURRENT)
++ from = aspeed_jtag->status;
++
++ for (i = 0; i < _tms_cycle_lookup[from][to].count; i++)
++ aspeed_jtag_tck_cycle(aspeed_jtag,
++ ((_tms_cycle_lookup[from][to].tmsbits >> i) & 0x1), 0);
++ aspeed_jtag->status = end_state;
++}
++
++static void aspeed_jtag_set_tap_state_sw(struct aspeed_jtag *aspeed_jtag,
++ struct jtag_tap_state *tapstate)
++{
++ /* SW mode from curent tap state -> to end_state */
++ if (tapstate->reset) {
++ int i = 0;
++
++ for (i = 0; i < ASPEED_JTAG_RESET_CNTR; i++)
++ aspeed_jtag_tck_cycle(aspeed_jtag, 1, 0);
++ aspeed_jtag->status = JTAG_STATE_TLRESET;
++ }
++
++ aspeed_jtag_set_tap_state(aspeed_jtag, tapstate->from,
++ tapstate->endstate);
++}
++
++static int aspeed_jtag_status_set(struct jtag *jtag,
++ struct jtag_tap_state *tapstate)
++{
++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
++
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev, "Set TAP state: %s\n",
++ end_status_str[tapstate->endstate]);
++#endif
++
++ if (!(aspeed_jtag->mode & JTAG_XFER_HW_MODE)) {
++ aspeed_jtag_set_tap_state_sw(aspeed_jtag, tapstate);
++ return 0;
++ }
++
++ /* x TMS high + 1 TMS low */
++ if (tapstate->reset) {
++ /* Disable sw mode */
++ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_SW);
++ mdelay(1);
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_CTL_ENG_EN |
++ ASPEED_JTAG_CTL_ENG_OUT_EN |
++ ASPEED_JTAG_CTL_FORCE_TMS, ASPEED_JTAG_CTRL);
++ mdelay(1);
++ aspeed_jtag_write(aspeed_jtag,
++ ASPEED_JTAG_SW_TDIO, ASPEED_JTAG_SW);
++ aspeed_jtag->status = JTAG_STATE_TLRESET;
++ }
++
++ return 0;
++}
++
++static void aspeed_jtag_xfer_sw(struct aspeed_jtag *aspeed_jtag,
++ struct jtag_xfer *xfer, u32 *data)
++{
++ unsigned long remain_xfer = xfer->length;
++ unsigned long shift_bits = 0;
++ unsigned long index = 0;
++ unsigned long tdi;
++ char tdo;
++
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev, "SW JTAG SHIFT %s, length = %d\n",
++ (xfer->type == JTAG_SIR_XFER) ? "IR" : "DR", xfer->length);
++#endif
++
++ if (xfer->type == JTAG_SIR_XFER)
++ aspeed_jtag_set_tap_state(aspeed_jtag, xfer->from,
++ JTAG_STATE_SHIFTIR);
++ else
++ aspeed_jtag_set_tap_state(aspeed_jtag, xfer->from,
++ JTAG_STATE_SHIFTDR);
++
++ tdi = ASPEED_JTAG_GET_TDI(xfer->direction, data[index]);
++ data[index] = 0;
++ while (remain_xfer > 1) {
++ tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 0,
++ tdi & ASPEED_JTAG_DATA_MSB);
++ data[index] |= tdo << (shift_bits %
++ ASPEED_JTAG_DATA_CHUNK_SIZE);
++ tdi >>= 1;
++ shift_bits++;
++ remain_xfer--;
++
++ if (shift_bits % ASPEED_JTAG_DATA_CHUNK_SIZE == 0) {
++ tdo = 0;
++ index++;
++ tdi = ASPEED_JTAG_GET_TDI(xfer->direction, data[index]);
++ data[index] = 0;
++ }
++ }
++
++ if ((xfer->endstate == (xfer->type == JTAG_SIR_XFER ?
++ JTAG_STATE_SHIFTIR : JTAG_STATE_SHIFTDR))) {
++ /* Stay in Shift IR/DR*/
++ tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 0,
++ tdi & ASPEED_JTAG_DATA_MSB);
++ data[index] |= tdo << (shift_bits %
++ ASPEED_JTAG_DATA_CHUNK_SIZE);
++ } else {
++ /* Goto end state */
++ tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 1,
++ tdi & ASPEED_JTAG_DATA_MSB);
++ data[index] |= tdo << (shift_bits %
++ ASPEED_JTAG_DATA_CHUNK_SIZE);
++ aspeed_jtag->status = (xfer->type == JTAG_SIR_XFER) ?
++ JTAG_STATE_EXIT1IR : JTAG_STATE_EXIT1DR;
++ aspeed_jtag_set_tap_state(aspeed_jtag, aspeed_jtag->status,
++ xfer->endstate);
++ }
++}
++
++static int aspeed_jtag_xfer_push_data(struct aspeed_jtag *aspeed_jtag,
++ enum jtag_xfer_type type, u32 bits_len)
++{
++ int res = 0;
++
++ if (type == JTAG_SIR_XFER) {
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_IOUT_LEN(bits_len),
++ ASPEED_JTAG_CTRL);
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_IOUT_LEN(bits_len) |
++ ASPEED_JTAG_CTL_INST_EN, ASPEED_JTAG_CTRL);
++ res = aspeed_jtag_wait_instruction_pause(aspeed_jtag);
++ } else {
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_DOUT_LEN(bits_len),
++ ASPEED_JTAG_CTRL);
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_DOUT_LEN(bits_len) |
++ ASPEED_JTAG_CTL_DATA_EN, ASPEED_JTAG_CTRL);
++ res = aspeed_jtag_wait_data_pause_complete(aspeed_jtag);
++ }
++ return res;
++}
++
++static int aspeed_jtag_xfer_push_data_last(struct aspeed_jtag *aspeed_jtag,
++ enum jtag_xfer_type type,
++ u32 shift_bits)
++{
++ int res = 0;
++
++ if (type == JTAG_SIR_XFER) {
++ aspeed_jtag_write(aspeed_jtag,
++ ASPEED_JTAG_IOUT_LEN(shift_bits) |
++ ASPEED_JTAG_CTL_LASPEED_INST,
++ ASPEED_JTAG_CTRL);
++ aspeed_jtag_write(aspeed_jtag,
++ ASPEED_JTAG_IOUT_LEN(shift_bits) |
++ ASPEED_JTAG_CTL_LASPEED_INST |
++ ASPEED_JTAG_CTL_INST_EN,
++ ASPEED_JTAG_CTRL);
++ res = aspeed_jtag_wait_instruction_complete(aspeed_jtag);
++ } else {
++ aspeed_jtag_write(aspeed_jtag,
++ ASPEED_JTAG_DOUT_LEN(shift_bits) |
++ ASPEED_JTAG_CTL_LASPEED_DATA,
++ ASPEED_JTAG_CTRL);
++ aspeed_jtag_write(aspeed_jtag,
++ ASPEED_JTAG_DOUT_LEN(shift_bits) |
++ ASPEED_JTAG_CTL_LASPEED_DATA |
++ ASPEED_JTAG_CTL_DATA_EN,
++ ASPEED_JTAG_CTRL);
++ res = aspeed_jtag_wait_data_complete(aspeed_jtag);
++ }
++ return res;
++}
++
++static int aspeed_jtag_xfer_hw(struct aspeed_jtag *aspeed_jtag,
++ struct jtag_xfer *xfer, u32 *data)
++{
++ unsigned long remain_xfer = xfer->length;
++ unsigned long index = 0;
++ char shift_bits;
++ u32 data_reg;
++ u32 scan_end;
++
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev, "HW JTAG SHIFT %s, length = %d\n",
++ (xfer->type == JTAG_SIR_XFER) ? "IR" : "DR", xfer->length);
++#endif
++ data_reg = xfer->type == JTAG_SIR_XFER ?
++ ASPEED_JTAG_INST : ASPEED_JTAG_DATA;
++ if (xfer->endstate == JTAG_STATE_SHIFTIR ||
++ xfer->endstate == JTAG_STATE_SHIFTDR ||
++ xfer->endstate == JTAG_STATE_PAUSEIR ||
++ xfer->endstate == JTAG_STATE_PAUSEDR) {
++ scan_end = 0;
++ } else {
++ scan_end = 1;
++ }
++
++ while (remain_xfer) {
++ if (xfer->direction & JTAG_WRITE_XFER)
++ aspeed_jtag_write(aspeed_jtag, data[index], data_reg);
++ else
++ aspeed_jtag_write(aspeed_jtag, 0, data_reg);
++
++ if (remain_xfer > ASPEED_JTAG_DATA_CHUNK_SIZE) {
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev,
++ "Chunk len=%d chunk_size=%d remain_xfer=%lu\n",
++ xfer->length, ASPEED_JTAG_DATA_CHUNK_SIZE,
++ remain_xfer);
++#endif
++ shift_bits = ASPEED_JTAG_DATA_CHUNK_SIZE;
++
++ /*
++ * Transmit bytes that were not equals to column length
++ * and after the transfer go to Pause IR/DR.
++ */
++ if (aspeed_jtag_xfer_push_data(aspeed_jtag, xfer->type,
++ shift_bits) != 0) {
++ return -EFAULT;
++ }
++ } else {
++ /*
++ * Read bytes equals to column length
++ */
++ shift_bits = remain_xfer;
++ if (scan_end) {
++ /*
++ * If this data is the end of the transmission
++ * send remaining bits and go to endstate
++ */
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev,
++ "Last len=%d chunk_size=%d remain_xfer=%lu\n",
++ xfer->length,
++ ASPEED_JTAG_DATA_CHUNK_SIZE,
++ remain_xfer);
++#endif
++ if (aspeed_jtag_xfer_push_data_last(
++ aspeed_jtag,
++ xfer->type,
++ shift_bits) != 0) {
++ return -EFAULT;
++ }
++ } else {
++ /*
++ * If transmission is waiting for additional
++ * data send remaining bits and then go to
++ * Pause IR/DR.
++ */
++#ifdef DEBUG_JTAG
++ dev_dbg(aspeed_jtag->dev,
++ "Tail len=%d chunk_size=%d remain_xfer=%lu\n",
++ xfer->length,
++ ASPEED_JTAG_DATA_CHUNK_SIZE,
++ remain_xfer);
++#endif
++ if (aspeed_jtag_xfer_push_data(aspeed_jtag,
++ xfer->type,
++ shift_bits)
++ != 0) {
++ return -EFAULT;
++ }
++ }
++ }
++
++ if (xfer->direction & JTAG_READ_XFER) {
++ if (shift_bits < ASPEED_JTAG_DATA_CHUNK_SIZE) {
++ data[index] = aspeed_jtag_read(aspeed_jtag,
++ data_reg);
++
++ data[index] >>= ASPEED_JTAG_DATA_CHUNK_SIZE -
++ shift_bits;
++ } else {
++ data[index] = aspeed_jtag_read(aspeed_jtag,
++ data_reg);
++ }
++ }
++
++ remain_xfer = remain_xfer - shift_bits;
++ index++;
++ }
++ return 0;
++}
++
++static int aspeed_jtag_xfer(struct jtag *jtag, struct jtag_xfer *xfer,
++ u8 *xfer_data)
++{
++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
++
++ if (!(aspeed_jtag->mode & JTAG_XFER_HW_MODE)) {
++ /* SW mode */
++ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_TDIO,
++ ASPEED_JTAG_SW);
++
++ aspeed_jtag_xfer_sw(aspeed_jtag, xfer, (u32 *)xfer_data);
++ } else {
++ /* HW mode */
++ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_SW);
++ if (aspeed_jtag_xfer_hw(aspeed_jtag, xfer,
++ (u32 *)xfer_data) != 0)
++ return -EFAULT;
++ }
++
++ aspeed_jtag->status = xfer->endstate;
++ return 0;
++}
++
++static int aspeed_jtag_status_get(struct jtag *jtag, u32 *status)
++{
++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
++
++ *status = aspeed_jtag->status;
++ return 0;
++}
++
++#ifdef USE_INTERRUPTS
++static irqreturn_t aspeed_jtag_interrupt(s32 this_irq, void *dev_id)
++{
++ struct aspeed_jtag *aspeed_jtag = dev_id;
++ irqreturn_t ret = IRQ_HANDLED;
++ u32 status;
++
++ status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR);
++
++ if (status & ASPEED_JTAG_ISR_INT_MASK) {
++ aspeed_jtag_write(aspeed_jtag,
++ (status & ASPEED_JTAG_ISR_INT_MASK)
++ | (status & ASPEED_JTAG_ISR_INT_EN_MASK),
++ ASPEED_JTAG_ISR);
++ aspeed_jtag->flag |= status & ASPEED_JTAG_ISR_INT_MASK;
++ }
++
++ if (aspeed_jtag->flag) {
++ wake_up_interruptible(&aspeed_jtag->jtag_wq);
++ ret = IRQ_HANDLED;
++ } else {
++ dev_err(aspeed_jtag->dev, "irq status:%x\n",
++ status);
++ ret = IRQ_NONE;
++ }
++ return ret;
++}
++#endif
++
++static int aspeed_jtag_enable(struct jtag *jtag)
++{
++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
++
++ aspeed_jtag_master(aspeed_jtag);
++ return 0;
++}
++
++static int aspeed_jtag_disable(struct jtag *jtag)
++{
++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
++
++ aspeed_jtag_output_disable(aspeed_jtag);
++ return 0;
++}
++
++static int aspeed_jtag_init(struct platform_device *pdev,
++ struct aspeed_jtag *aspeed_jtag)
++{
++ struct resource *res;
++#ifdef USE_INTERRUPTS
++ int err;
++#endif
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ aspeed_jtag->reg_base = devm_ioremap_resource(aspeed_jtag->dev, res);
++ if (IS_ERR(aspeed_jtag->reg_base))
++ return -ENOMEM;
++
++ aspeed_jtag->pclk = devm_clk_get(aspeed_jtag->dev, NULL);
++ if (IS_ERR(aspeed_jtag->pclk)) {
++ dev_err(aspeed_jtag->dev, "devm_clk_get failed\n");
++ return PTR_ERR(aspeed_jtag->pclk);
++ }
++
++#ifdef USE_INTERRUPTS
++ aspeed_jtag->irq = platform_get_irq(pdev, 0);
++ if (aspeed_jtag->irq < 0) {
++ dev_err(aspeed_jtag->dev, "no irq specified\n");
++ return -ENOENT;
++ }
++#endif
++
++ if (clk_prepare_enable(aspeed_jtag->pclk)) {
++ dev_err(aspeed_jtag->dev, "no irq specified\n");
++ return -ENOENT;
++ }
++
++ aspeed_jtag->rst = devm_reset_control_get_shared(&pdev->dev, NULL);
++ if (IS_ERR(aspeed_jtag->rst)) {
++ dev_err(aspeed_jtag->dev,
++ "missing or invalid reset controller device tree entry");
++ return PTR_ERR(aspeed_jtag->rst);
++ }
++ reset_control_deassert(aspeed_jtag->rst);
++
++#ifdef USE_INTERRUPTS
++ err = devm_request_irq(aspeed_jtag->dev, aspeed_jtag->irq,
++ aspeed_jtag_interrupt, 0,
++ "aspeed-jtag", aspeed_jtag);
++ if (err) {
++ dev_err(aspeed_jtag->dev, "unable to get IRQ");
++ clk_disable_unprepare(aspeed_jtag->pclk);
++ return err;
++ }
++#endif
++
++ aspeed_jtag_output_disable(aspeed_jtag);
++
++ aspeed_jtag->flag = 0;
++ aspeed_jtag->mode = 0;
++ init_waitqueue_head(&aspeed_jtag->jtag_wq);
++ return 0;
++}
++
++static int aspeed_jtag_deinit(struct platform_device *pdev,
++ struct aspeed_jtag *aspeed_jtag)
++{
++ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_ISR);
++ /* Disable clock */
++ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_CTRL);
++ reset_control_assert(aspeed_jtag->rst);
++ clk_disable_unprepare(aspeed_jtag->pclk);
++ return 0;
++}
++
++static const struct jtag_ops aspeed_jtag_ops = {
++ .freq_get = aspeed_jtag_freq_get,
++ .freq_set = aspeed_jtag_freq_set,
++ .status_get = aspeed_jtag_status_get,
++ .status_set = aspeed_jtag_status_set,
++ .xfer = aspeed_jtag_xfer,
++ .mode_set = aspeed_jtag_mode_set,
++ .bitbang = aspeed_jtag_bitbang,
++ .enable = aspeed_jtag_enable,
++ .disable = aspeed_jtag_disable
++};
++
++static int aspeed_jtag_probe(struct platform_device *pdev)
++{
++ struct aspeed_jtag *aspeed_jtag;
++ struct jtag *jtag;
++ int err;
++
++ jtag = jtag_alloc(&pdev->dev, sizeof(*aspeed_jtag), &aspeed_jtag_ops);
++ if (!jtag)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, jtag);
++ aspeed_jtag = jtag_priv(jtag);
++ aspeed_jtag->dev = &pdev->dev;
++
++ /* Initialize device*/
++ err = aspeed_jtag_init(pdev, aspeed_jtag);
++ if (err)
++ goto err_jtag_init;
++
++ /* Initialize JTAG core structure*/
++ err = devm_jtag_register(aspeed_jtag->dev, jtag);
++ if (err)
++ goto err_jtag_register;
++
++ return 0;
++
++err_jtag_register:
++ aspeed_jtag_deinit(pdev, aspeed_jtag);
++err_jtag_init:
++ jtag_free(jtag);
++ return err;
++}
++
++static int aspeed_jtag_remove(struct platform_device *pdev)
++{
++ struct jtag *jtag = platform_get_drvdata(pdev);
++
++ aspeed_jtag_deinit(pdev, jtag_priv(jtag));
++ return 0;
++}
++
++static const struct of_device_id aspeed_jtag_of_match[] = {
++ { .compatible = "aspeed,ast2400-jtag", },
++ { .compatible = "aspeed,ast2500-jtag", },
++ {}
++};
++
++static struct platform_driver aspeed_jtag_driver = {
++ .probe = aspeed_jtag_probe,
++ .remove = aspeed_jtag_remove,
++ .driver = {
++ .name = ASPEED_JTAG_NAME,
++ .of_match_table = aspeed_jtag_of_match,
++ },
++};
++module_platform_driver(aspeed_jtag_driver);
++
++MODULE_AUTHOR("Oleksandr Shamray <oleksandrs@mellanox.com>");
++MODULE_DESCRIPTION("ASPEED JTAG driver");
++MODULE_LICENSE("GPL v2");
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0054-Documentation-jtag-Add-bindings-for-Aspeed-SoC.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0054-Documentation-jtag-Add-bindings-for-Aspeed-SoC.patch
new file mode 100644
index 000000000..f17bdcd68
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0054-Documentation-jtag-Add-bindings-for-Aspeed-SoC.patch
@@ -0,0 +1,108 @@
+From 2a22feac440070b7feaf0a6fe7e7e555d57ca19b Mon Sep 17 00:00:00 2001
+From: "Corona, Ernesto" <ernesto.corona@intel.com>
+Date: Wed, 10 Mar 2019 11:45:04 -0800
+Subject: [PATCH v29 3/6] Documentation: jtag: Add bindings for Aspeed SoC
+ 24xx and 25xx families JTAG master driver
+
+It has been tested on Mellanox system with BMC equipped with
+Aspeed 2520 SoC for programming CPLD devices.
+
+It also has been tested on Intel systems with BMC equipped with
+Aspeed 25x0 SoC for JTAG board communication.
+
+Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
+Signed-off-by: Jiri Pirko <jiri@mellanox.com>
+Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com>
+Acked-by: Rob Herring <robh@kernel.org>
+Cc: Mark Rutland <mark.rutland@arm.com>
+Cc: Joel Stanley <joel@jms.id.au>
+Cc: Andrew Jeffery <andrew@aj.id.au>
+Cc: Steven A Filary <steven.a.filary@intel.com>
+Cc: Bryan Hunt <bryan.hunt@intel.com>
+---
+v28->v29
+v27->v28
+v26->v27
+v25->v26
+v24->v25
+v23->v24
+v22->v23
+v21->v22
+v20->v21
+v19->v20
+v18->v19
+
+v17->v18
+v16->v17
+v15->v16
+Comments pointed by Joel Stanley <joel.stan@gmail.com>
+- change clocks = <&clk_apb> to proper clocks = <&syscon ASPEED_CLK_APB>
+- add reset descriptions in bindings file
+
+v14->v15
+v13->v14
+v12->v13
+v11->v12
+v10->v11
+v9->v10
+v8->v9
+v7->v8
+Comments pointed by pointed by Joel Stanley <joel.stan@gmail.com>
+- Change compatible string to ast2400 and ast2000
+
+V6->v7
+Comments pointed by Tobias Klauser <tklauser@distanz.ch>
+ - Fix spell "Doccumentation" -> "Documentation"
+
+v5->v6
+Comments pointed by Tobias Klauser <tklauser@distanz.ch>
+- Small nit: s/documentation/Documentation/
+
+v4->v5
+
+V3->v4
+Comments pointed by Rob Herring <robh@kernel.org>
+- delete unnecessary "status" and "reg-shift" descriptions in
+ bindings file
+
+v2->v3
+Comments pointed by Rob Herring <robh@kernel.org>
+- split Aspeed jtag driver and binding to separate patches
+- delete unnecessary "status" and "reg-shift" descriptions in
+ bindings file
+---
+ .../devicetree/bindings/jtag/aspeed-jtag.txt | 22 ++++++++++++++++++++++
+ 1 file changed, 22 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
+
+diff --git a/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt b/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
+new file mode 100644
+index 0000000..7c36eb6
+--- /dev/null
++++ b/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
+@@ -0,0 +1,22 @@
++Aspeed JTAG driver for ast2400 and ast2500 SoC
++
++Required properties:
++- compatible: Should be one of
++ - "aspeed,ast2400-jtag"
++ - "aspeed,ast2500-jtag"
++- reg contains the offset and length of the JTAG memory
++ region
++- clocks root clock of bus, should reference the APB
++ clock in the second cell
++- resets phandle to reset controller with the reset number in
++ the second cell
++- interrupts should contain JTAG controller interrupt
++
++Example:
++jtag: jtag@1e6e4000 {
++ compatible = "aspeed,ast2500-jtag";
++ reg = <0x1e6e4000 0x1c>;
++ clocks = <&syscon ASPEED_CLK_APB>;
++ resets = <&syscon ASPEED_RESET_JTAG_MASTER>;
++ interrupts = <43>;
++};
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0055-Documentation-jtag-Add-ABI-documentation.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0055-Documentation-jtag-Add-ABI-documentation.patch
new file mode 100644
index 000000000..b91311d94
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0055-Documentation-jtag-Add-ABI-documentation.patch
@@ -0,0 +1,309 @@
+From c2d57900820475b50affd171f4dc423a278887ae Mon Sep 17 00:00:00 2001
+From: "Corona, Ernesto" <ernesto.corona@intel.com>
+Date: Wed, 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 0000000..423baab
+--- /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 0000000..6a5ec33
+--- /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 0000000..76fd0b1
+--- /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..a7dccc4b6
--- /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 ba0c35ae070cffcb384fc76e23a38e00142b128d Mon Sep 17 00:00:00 2001
+From: "Corona, Ernesto" <ernesto.corona@intel.com>
+Date: Wed, 10 Mar 2019 11:48:18 -0800
+Subject: [PATCH v29 5/6] 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.txt | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
+index c955814..f732118 100644
+--- a/Documentation/ioctl/ioctl-number.txt
++++ b/Documentation/ioctl/ioctl-number.txt
+@@ -323,6 +323,8 @@ Code Seq#(hex) Include File Comments
+ 0xB0 all RATIO devices in development:
+ <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..8ab4615f0
--- /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 0a563429bc3c34a951db92600681e799b606a01f Mon Sep 17 00:00:00 2001
+From: "Corona, Ernesto" <ernesto.corona@intel.com>
+Date: Wed, 10 Mar 2019 11:49:37 -0800
+Subject: [PATCH v29 6/6] drivers: jtag: Add JTAG core driver Maintainers
+
+JTAG class driver provide infrastructure to support hardware/software
+JTAG platform drivers. It provide user layer API interface for flashing
+and debugging external devices which equipped with JTAG interface
+using standard transactions.
+
+Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
+Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com>
+Acked-by: Arnd Bergmann <arnd@arndb.de>
+Cc: Jiri Pirko <jiri@mellanox.com>
+Cc: Vadim Pasternak <vadimp@mellanox.com>
+Cc: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc: David S. Miller <davem@davemloft.net>
+Cc: Nicolas Ferre <nicolas.ferre@microchip.com>
+Cc: Steven A Filary <steven.a.filary@intel.com>
+Cc: Bryan Hunt <bryan.hunt@intel.com>
+---
+ MAINTAINERS | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index dce5c09..eb710a6 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -8173,6 +8173,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/0058-i2c-aspeed-add-general-call-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0058-i2c-aspeed-add-general-call-support.patch
new file mode 100644
index 000000000..de8bf2355
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0058-i2c-aspeed-add-general-call-support.patch
@@ -0,0 +1,182 @@
+From 551b5192b1074679ca9411cdedb9137d38f7de3d 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 7da7e813b2b0..724ee9f35c10 100644
+--- a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
++++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
+@@ -16,6 +16,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 0060193e1aa4..f96160e01a69 100644
+--- a/drivers/i2c/busses/i2c-aspeed.c
++++ b/drivers/i2c/busses/i2c-aspeed.c
+@@ -50,6 +50,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)
+
+@@ -74,6 +75,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)
+@@ -133,6 +135,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,
+ };
+
+@@ -163,6 +167,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 */
+ };
+
+@@ -267,6 +273,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;
+@@ -285,6 +297,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;
+ }
+@@ -324,6 +351,10 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED;
+ i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
+ 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_WRITE_RECEIVED:
+ i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value);
+ break;
+@@ -332,6 +363,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ 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:
+@@ -739,6 +771,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);
+ }
+
+@@ -777,6 +811,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;
+@@ -921,6 +957,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 4548088e1922..a608846cb1db 100644
+--- a/drivers/i2c/i2c-slave-mqueue.c
++++ b/drivers/i2c/i2c-slave-mqueue.c
+@@ -53,10 +53,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 e1c6b78bdaf1..03ffb70d75f2 100644
+--- a/include/linux/i2c.h
++++ b/include/linux/i2c.h
+@@ -371,6 +371,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/0065-i2c-aspeed-fix-master-pending-state-handling.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0065-i2c-aspeed-fix-master-pending-state-handling.patch
new file mode 100644
index 000000000..3a86d5b25
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0065-i2c-aspeed-fix-master-pending-state-handling.patch
@@ -0,0 +1,55 @@
+From 50221ac92816333efcf961c5f22f8b9ffdccb31b Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Tue, 11 Jun 2019 14:59:53 -0700
+Subject: [PATCH] i2c: aspeed: fix master pending state handling
+
+In case of a master pending state, it should not trigger the master
+command because this H/W is sharing the same byte buffer for slave
+and master operation, so this commit fixes the issue with making
+the master command triggering happens when the state goes to active
+state.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ drivers/i2c/busses/i2c-aspeed.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
+index f96160e01a69..64bc68eaa88f 100644
+--- a/drivers/i2c/busses/i2c-aspeed.c
++++ b/drivers/i2c/busses/i2c-aspeed.c
+@@ -384,18 +384,19 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus)
+ struct i2c_msg *msg = &bus->msgs[bus->msgs_index];
+ u8 slave_addr = i2c_8bit_addr_from_msg(msg);
+
+- bus->master_state = ASPEED_I2C_MASTER_START;
+-
+ #if IS_ENABLED(CONFIG_I2C_SLAVE)
+ /*
+ * If it's requested in the middle of a slave session, set the master
+ * state to 'pending' then H/W will continue handling this master
+ * command when the bus comes back to the idle state.
+ */
+- if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE)
++ if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) {
+ bus->master_state = ASPEED_I2C_MASTER_PENDING;
++ return;
++ }
+ #endif /* CONFIG_I2C_SLAVE */
+
++ bus->master_state = ASPEED_I2C_MASTER_START;
+ bus->buf_index = 0;
+
+ if (msg->flags & I2C_M_RD) {
+@@ -480,7 +481,7 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
+ if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE)
+ goto out_no_complete;
+
+- bus->master_state = ASPEED_I2C_MASTER_START;
++ aspeed_i2c_do_start(bus);
+ }
+ #endif /* CONFIG_I2C_SLAVE */
+
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0066-i2c-aspeed-add-buffer-mode-transfer-support.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0066-i2c-aspeed-add-buffer-mode-transfer-support.patch
new file mode 100644
index 000000000..9480daeff
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0066-i2c-aspeed-add-buffer-mode-transfer-support.patch
@@ -0,0 +1,770 @@
+From efb710a6b3a39f28b988af717eefc1b72c4c43bd 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 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>
+---
+ arch/arm/boot/dts/aspeed-g4.dtsi | 42 ++++--
+ arch/arm/boot/dts/aspeed-g5.dtsi | 42 ++++--
+ drivers/i2c/busses/i2c-aspeed.c | 262 ++++++++++++++++++++++++++++++++----
+ drivers/irqchip/irq-aspeed-i2c-ic.c | 8 ++
+ 4 files changed, 301 insertions(+), 53 deletions(-)
+
+diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
+index 47a5029f5bdb..c1c125add9fa 100644
+--- a/arch/arm/boot/dts/aspeed-g4.dtsi
++++ b/arch/arm/boot/dts/aspeed-g4.dtsi
+@@ -482,7 +482,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x40 0x40>;
++ reg = <0x40 0x40>, <0x800 0x80>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -498,7 +499,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x80 0x40>;
++ reg = <0x80 0x40>, <0x880 0x80>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -514,7 +516,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0xc0 0x40>;
++ reg = <0xc0 0x40>, <0x900 0x80>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -531,7 +534,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x100 0x40>;
++ reg = <0x100 0x40>, <0x980 0x80>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -548,7 +552,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x140 0x40>;
++ reg = <0x140 0x40>, <0xa00 0x80>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -565,7 +570,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x180 0x40>;
++ reg = <0x180 0x40>, <0xa80 0x80>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -582,7 +588,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x1c0 0x40>;
++ reg = <0x1c0 0x40>, <0xb00 0x80>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -599,7 +606,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x300 0x40>;
++ reg = <0x300 0x40>, <0xb80 0x80>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -616,7 +624,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x340 0x40>;
++ reg = <0x340 0x40>, <0xc00 0x80>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -633,7 +642,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x380 0x40>;
++ reg = <0x380 0x40>, <0xc80 0x80>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -650,7 +660,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x3c0 0x40>;
++ reg = <0x3c0 0x40>, <0xd00 0x80>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -667,7 +678,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x400 0x40>;
++ reg = <0x400 0x40>, <0xd80 0x80>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -684,7 +696,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x440 0x40>;
++ reg = <0x440 0x40>, <0xe00 0x80>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2400-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -701,7 +714,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x480 0x40>;
++ reg = <0x480 0x40>, <0xe80 0x80>;
++ reg-names = "bus-regs", "buf";
+ 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 9d5ed9499b1f..662249bc15f9 100644
+--- a/arch/arm/boot/dts/aspeed-g5.dtsi
++++ b/arch/arm/boot/dts/aspeed-g5.dtsi
+@@ -599,7 +599,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x40 0x40>;
++ reg = <0x40 0x40>, <0x200 0x10>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -615,7 +616,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x80 0x40>;
++ reg = <0x80 0x40>, <0x210 0x10>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -631,7 +633,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0xc0 0x40>;
++ reg = <0xc0 0x40>, <0x220 0x10>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -648,7 +651,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x100 0x40>;
++ reg = <0x100 0x40>, <0x230 0x10>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -665,7 +669,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x140 0x40>;
++ reg = <0x140 0x40>, <0x240 0x10>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -682,7 +687,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x180 0x40>;
++ reg = <0x180 0x40>, <0x250 0x10>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -699,7 +705,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x1c0 0x40>;
++ reg = <0x1c0 0x40>, <0x260 0x10>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -716,7 +723,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x300 0x40>;
++ reg = <0x300 0x40>, <0x270 0x10>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -733,7 +741,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x340 0x40>;
++ reg = <0x340 0x40>, <0x280 0x10>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -750,7 +759,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x380 0x40>;
++ reg = <0x380 0x40>, <0x290 0x10>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -767,7 +777,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x3c0 0x40>;
++ reg = <0x3c0 0x40>, <0x2a0 0x10>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -784,7 +795,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x400 0x40>;
++ reg = <0x400 0x40>, <0x2b0 0x10>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -801,7 +813,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x440 0x40>;
++ reg = <0x440 0x40>, <0x2c0 0x10>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+@@ -818,7 +831,8 @@
+ #size-cells = <0>;
+ #interrupt-cells = <1>;
+
+- reg = <0x480 0x40>;
++ reg = <0x480 0x40>, <0x2d0 0x10>;
++ reg-names = "bus-regs", "buf";
+ compatible = "aspeed,ast2500-i2c-bus";
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_I2C>;
+diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
+index 64bc68eaa88f..b21a4c87853e 100644
+--- a/drivers/i2c/busses/i2c-aspeed.c
++++ b/drivers/i2c/busses/i2c-aspeed.c
+@@ -10,6 +10,7 @@
+ * published by the Free Software Foundation.
+ */
+
++#include <linux/bitfield.h>
+ #include <linux/clk.h>
+ #include <linux/completion.h>
+ #include <linux/err.h>
+@@ -38,6 +39,7 @@
+ #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 */
+@@ -46,6 +48,7 @@
+
+ /* 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)
+@@ -107,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)
+@@ -117,6 +122,13 @@
+ /* 0x18 : I2CD Slave Device Address Register */
+ #define ASPEED_I2CD_DEV_ADDR_MASK GENMASK(6, 0)
+
++/* 0x1c : I2CD Buffer Control Register */
++/* Use 8-bits or 6-bits wide bit fileds to support both AST2400 and AST2500 */
++#define ASPEED_I2CD_BUF_RX_COUNT_MASK GENMASK(31, 24)
++#define ASPEED_I2CD_BUF_RX_SIZE_MASK GENMASK(23, 16)
++#define ASPEED_I2CD_BUF_TX_COUNT_MASK GENMASK(15, 8)
++#define ASPEED_I2CD_BUF_OFFSET_MASK GENMASK(5, 0)
++
+ enum aspeed_i2c_master_state {
+ ASPEED_I2C_MASTER_INACTIVE,
+ ASPEED_I2C_MASTER_PENDING,
+@@ -164,6 +176,11 @@ struct aspeed_i2c_bus {
+ int master_xfer_result;
+ /* Multi-master */
+ bool multi_master;
++ /* Buffer/DMA 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;
+@@ -260,6 +277,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)
+@@ -288,7 +306,11 @@ 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)
++ 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)
+@@ -318,6 +340,18 @@ 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->buf_base &&
++ bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED) {
++ 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;
+ }
+@@ -350,6 +384,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) {
++ 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_GCALL_REQUESTED:
+ bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED;
+@@ -357,6 +400,24 @@ 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) {
++ 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);
+@@ -383,6 +444,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)
+ /*
+@@ -401,12 +464,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_TX_BUFF_ENABLE |
++ ASPEED_I2CD_RX_BUFF_ENABLE;
++
++ wbuf[0] = slave_addr;
++ writel(*(u32 *)wbuf, bus->buf_base);
++
++ 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_TX_COUNT_MASK, 0) |
++ 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;
++
++ 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);
+ }
+
+@@ -441,12 +558,22 @@ static int aspeed_i2c_is_irq_error(u32 irq_status)
+ return 0;
+ }
+
++static inline int aspeed_i2c_is_tx_error(struct aspeed_i2c_bus *bus)
++{
++ if (FIELD_GET(ASPEED_I2CD_BUF_TX_COUNT_MASK,
++ readl(bus->base + ASPEED_I2C_BUF_CTRL_REG)) !=
++ bus->buf_size - 1)
++ return -EIO;
++
++ return 0;
++}
++
+ 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;
+@@ -559,11 +686,46 @@ 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) {
++ if (bus->buf_base) {
++ u8 wbuf[4];
++ int i;
++
++ if (unlikely(aspeed_i2c_is_tx_error(bus)))
++ goto error_and_stop;
++
++ 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);
++ writel(ASPEED_I2CD_TX_BUFF_ENABLE |
++ ASPEED_I2CD_M_TX_CMD,
++ bus->base + ASPEED_I2C_CMD_REG);
++ } else {
++ 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);
++ }
+ 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);
+ }
+@@ -580,25 +742,57 @@ 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);
+@@ -948,6 +1142,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
+@@ -1010,17 +1207,32 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
+ struct aspeed_i2c_bus *bus;
+ struct clk *parent_clk;
+ struct resource *res;
++ void __iomem *gc_reg;
+ int irq, ret;
+
+ bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ return -ENOMEM;
+
+- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bus-regs");
+ bus->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(bus->base))
+ return PTR_ERR(bus->base);
+
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "buf");
++ bus->buf_base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(bus->buf_base) || resource_size(res) < 2) {
++ bus->buf_base = NULL;
++ } else {
++ 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;
++ }
++ }
++
+ parent_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(parent_clk))
+ return PTR_ERR(parent_clk);
+diff --git a/drivers/irqchip/irq-aspeed-i2c-ic.c b/drivers/irqchip/irq-aspeed-i2c-ic.c
+index f20200af0992..99985b22a9fa 100644
+--- a/drivers/irqchip/irq-aspeed-i2c-ic.c
++++ b/drivers/irqchip/irq-aspeed-i2c-ic.c
+@@ -18,6 +18,9 @@
+ #include <linux/of_irq.h>
+ #include <linux/io.h>
+
++/* I2C Global Control Register (AST2500) */
++#define ASPEED_I2C_GLOBAL_CTRL_REG 0xc
++#define ASPEED_I2C_SRAM_BUFFER_EN BIT(0)
+
+ #define ASPEED_I2C_IC_NUM_BUS 14
+
+@@ -100,6 +103,11 @@ static int __init aspeed_i2c_ic_of_init(struct device_node *node,
+ irq_set_chained_handler_and_data(i2c_ic->parent_irq,
+ aspeed_i2c_ic_irq_handler, i2c_ic);
+
++ /* Enable I2C SRAM buffer in case of AST2500 */
++ if (of_device_is_compatible(node, "aspeed,ast2500-i2c-ic"))
++ writel(ASPEED_I2C_SRAM_BUFFER_EN,
++ i2c_ic->base + ASPEED_I2C_GLOBAL_CTRL_REG);
++
+ pr_info("i2c controller registered, irq %d\n", i2c_ic->parent_irq);
+
+ return 0;
+--
+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..41530dd6e
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/intel.cfg
@@ -0,0 +1 @@
+CONFIG_BLK_DEV_RAM=y
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend
new file mode 100644
index 000000000..ed5be3455
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend
@@ -0,0 +1,46 @@
+FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
+
+do_compile_prepend(){
+ # device tree compiler flags
+ export DTC_FLAGS=-@
+}
+
+SRC_URI += " \
+ file://intel.cfg \
+ file://0005-arm-dts-aspeed-g5-add-espi.patch \
+ file://0007-New-flash-map-for-intel.patch \
+ file://0008-Add-ASPEED-SGPIO-driver.patch \
+ file://0009-SGPIO-DT-and-pinctrl-fixup.patch \
+ file://0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch \
+ file://0019-Add-I2C-IPMB-support.patch \
+ file://0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch \
+ file://0022-Add-AST2500-eSPI-driver.patch \
+ file://0026-Add-support-for-new-PECI-commands.patch \
+ file://0028-Add-AST2500-JTAG-driver.patch \
+ file://0030-Add-dump-debug-code-into-I2C-drivers.patch \
+ file://0031-Add-high-speed-baud-rate-support-for-UART.patch \
+ file://0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch \
+ file://0034-arm-dts-aspeed-Swap-the-mac-nodes-numbering.patch \
+ file://0035-Implement-a-memory-driver-share-memory.patch \
+ file://0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch \
+ file://0040-i2c-Add-mux-hold-unhold-msg-types.patch \
+ file://0041-Enable-passthrough-based-gpio-character-device.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://0046-misc-Add-clock-control-logic-into-Aspeed-LPC-MBOX-dr.patch \
+ file://0047-misc-Block-error-printing-on-probe-defer-case-in-Asp.patch \
+ file://0048-ARM-dts-aspeed-Set-default-status-of-LPC-BT-as-disab.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://0058-i2c-aspeed-add-general-call-support.patch \
+ file://0065-i2c-aspeed-fix-master-pending-state-handling.patch \
+ file://0066-i2c-aspeed-add-buffer-mode-transfer-support.patch \
+ "
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers/0001-Enable-passthrough-based-gpio-character-device.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers/0001-Enable-passthrough-based-gpio-character-device.patch
new file mode 100644
index 000000000..15e845ebc
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers/0001-Enable-passthrough-based-gpio-character-device.patch
@@ -0,0 +1,26 @@
+From 61a5796d151337fd537d1aeb3fb072dcdf96abbe Mon Sep 17 00:00:00 2001
+From: Kuiying Wang <kuiying.wang@intel.com>
+Date: Sun, 3 Feb 2019 16:08:11 +0800
+Subject: [PATCH] Enable passthrough based gpio character device.
+
+Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
+---
+ include/uapi/linux/gpio.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h
+index 1bf6e6df..8883ca90 100644
+--- a/include/uapi/linux/gpio.h
++++ b/include/uapi/linux/gpio.h
+@@ -62,7 +62,7 @@ struct gpioline_info {
+ #define GPIOHANDLE_REQUEST_ACTIVE_LOW (1UL << 2)
+ #define GPIOHANDLE_REQUEST_OPEN_DRAIN (1UL << 3)
+ #define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4)
+-
++#define GPIOHANDLE_REQUEST_PASS_THROUGH (1UL << 5)
+ /**
+ * struct gpiohandle_request - Information about a GPIO handle request
+ * @lineoffsets: an array desired lines, specified by offset index for the
+--
+2.16.2
+
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers_%.bbappend b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers_%.bbappend
new file mode 100644
index 000000000..703af71df
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-libc-headers_%.bbappend
@@ -0,0 +1,5 @@
+FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
+
+SRC_URI += " \
+ file://0001-Enable-passthrough-based-gpio-character-device.patch \
+ "