From 87a65e63bac789bca0607e0b4ab09d62517b95e7 Mon Sep 17 00:00:00 2001 From: Ed Tanous Date: Wed, 29 May 2019 10:29:58 -0700 Subject: Update to internal Signed-off-by: Ed Tanous --- ...-drivers-to-sync-with-linux-upstreaming-v.patch | 1375 +++++++++++++++++--- ...021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch | 13 +- .../0022-Add-AST2500-eSPI-driver.patch | 4 +- .../0026-Add-support-for-new-PECI-commands.patch | 39 +- .../0028-Add-AST2500-JTAG-driver.patch | 1057 +-------------- ...Improve-driver-to-support-multi-master-us.patch | 291 ----- ...0030-Add-dump-debug-code-into-I2C-drivers.patch | 35 +- ...35-Implement-a-memory-driver-share-memory.patch | 14 +- .../0038-media-aspeed-backport-ikvm-patches.patch | 174 --- ...d-PWM-driver-which-uses-FTTMR010-timer-IP.patch | 177 +-- ...e-passthrough-based-gpio-character-device.patch | 4 +- ...tform-Fix-a-kernel-warning-on-clk-control.patch | 177 --- .../0051-Add-AST2500-JTAG-device.patch | 35 + .../0052-drivers-jtag-Add-JTAG-core-driver.patch | 904 +++++++++++++ ...dd-Aspeed-SoC-24xx-and-25xx-families-JTAG.patch | 1282 ++++++++++++++++++ ...entation-jtag-Add-bindings-for-Aspeed-SoC.patch | 108 ++ ...-Documentation-jtag-Add-ABI-documentation.patch | 303 +++++ ...on-jtag-Add-JTAG-core-driver-ioctl-number.patch | 57 + ...ers-jtag-Add-JTAG-core-driver-Maintainers.patch | 50 + .../0058-i2c-aspeed-add-general-call-support.patch | 182 +++ ...d-remove-source-buffer-allocation-before-.patch | 49 + ...d-use-different-delays-for-triggering-VE-.patch | 60 + ...d-fix-an-incorrect-timeout-checking-in-mo.patch | 30 + ...eed-add-a-workaround-to-fix-a-silicon-bug.patch | 66 + .../recipes-kernel/linux/linux-aspeed_%.bbappend | 14 +- 25 files changed, 4394 insertions(+), 2106 deletions(-) delete mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0029-i2c-aspeed-Improve-driver-to-support-multi-master-us.patch delete mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0038-media-aspeed-backport-ikvm-patches.patch delete mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0050-media-platform-Fix-a-kernel-warning-on-clk-control.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0051-Add-AST2500-JTAG-device.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0052-drivers-jtag-Add-JTAG-core-driver.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0053-Add-Aspeed-SoC-24xx-and-25xx-families-JTAG.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0054-Documentation-jtag-Add-bindings-for-Aspeed-SoC.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0055-Documentation-jtag-Add-ABI-documentation.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0056-Documentation-jtag-Add-JTAG-core-driver-ioctl-number.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0057-drivers-jtag-Add-JTAG-core-driver-Maintainers.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0058-i2c-aspeed-add-general-call-support.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0059-media-aspeed-remove-source-buffer-allocation-before-.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0060-media-aspeed-use-different-delays-for-triggering-VE-.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0061-media-aspeed-fix-an-incorrect-timeout-checking-in-mo.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0062-media-aspeed-add-a-workaround-to-fix-a-silicon-bug.patch (limited to 'meta-openbmc-mods/meta-common/recipes-kernel') 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 index db21250bb..d2ecdfd72 100644 --- 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 @@ -1,4 +1,4 @@ -From 63ccbbe64f7e6560233971b886f6166fc59d20ef Mon Sep 17 00:00:00 2001 +From 9c27803dd432c7a9fc57dd3e16f0fd724919575e Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Mon, 7 Jan 2019 09:56:10 -0800 Subject: [PATCH] Update PECI drivers to sync with linux upstreaming version @@ -10,28 +10,32 @@ Signed-off-by: Jae Hyun Yoo --- Documentation/hwmon/peci-cputemp | 34 +- drivers/hwmon/Kconfig | 4 +- - drivers/hwmon/peci-cputemp.c | 156 ++++-- + 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 | 43 +- - drivers/peci/Kconfig | 35 +- - drivers/peci/Makefile | 6 +- - drivers/peci/busses/Kconfig | 19 + - drivers/peci/busses/Makefile | 6 + + 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 | 889 ++++++++++++++++++---------------- - drivers/peci/peci-dev.c | 340 +++++++++++++ - include/linux/mfd/intel-peci-client.h | 6 +- + 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 | 394 ++++++++------- - 18 files changed, 1805 insertions(+), 1239 deletions(-) + 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 @@ -89,7 +93,7 @@ index 821a9258f2e6..a3a3e465c888 100644 +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 996e80590b5b..93945eb19261 100644 +index 18cd3b17f660..0bd06a938526 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1321,7 +1321,7 @@ config SENSORS_PECI_CPUTEMP @@ -111,7 +115,7 @@ index 996e80590b5b..93945eb19261 100644 source "drivers/hwmon/pmbus/Kconfig" diff --git a/drivers/hwmon/peci-cputemp.c b/drivers/hwmon/peci-cputemp.c -index 11880c86a854..30ba1638e358 100644 +index 11880c86a854..3cb2db2fdf0a 100644 --- a/drivers/hwmon/peci-cputemp.c +++ b/drivers/hwmon/peci-cputemp.c @@ -1,5 +1,5 @@ @@ -214,7 +218,7 @@ index 11880c86a854..30ba1638e358 100644 /* Note that the tjmax should be available before calling it */ priv->temp.die.value = priv->temp.tjmax.value + -@@ -144,24 +151,67 @@ static int get_die_temp(struct peci_cputemp *priv) +@@ -144,24 +151,70 @@ static int get_die_temp(struct peci_cputemp *priv) return 0; } @@ -237,6 +241,9 @@ index 11880c86a854..30ba1638e358 100644 + if (ret) + return ret; + ++ if (msg.cc != PECI_DEV_CC_SUCCESS) ++ return -EAGAIN; ++ + dts_margin = (msg.pkg_config[1] << 8) | msg.pkg_config[0]; + + /** @@ -289,7 +296,7 @@ index 11880c86a854..30ba1638e358 100644 * Processors return a value of the core DTS reading in 10.6 format * (10 bits signed decimal, 6 bits fractional). * Error codes: -@@ -192,6 +242,7 @@ static int cputemp_read_string(struct device *dev, +@@ -192,6 +245,7 @@ static int cputemp_read_string(struct device *dev, return -EOPNOTSUPP; *str = cputemp_label[channel]; @@ -297,7 +304,7 @@ index 11880c86a854..30ba1638e358 100644 return 0; } -@@ -200,26 +251,33 @@ static int cputemp_read(struct device *dev, +@@ -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); @@ -337,7 +344,7 @@ index 11880c86a854..30ba1638e358 100644 case channel_tcontrol: *val = priv->temp.tcontrol.value; break; -@@ -231,8 +289,8 @@ static int cputemp_read(struct device *dev, +@@ -231,8 +292,8 @@ static int cputemp_read(struct device *dev, break; default: core_index = channel - DEFAULT_CHANNEL_NUMS; @@ -348,7 +355,7 @@ index 11880c86a854..30ba1638e358 100644 break; *val = priv->temp.core[core_index].value; -@@ -249,11 +307,11 @@ static int cputemp_read(struct device *dev, +@@ -249,11 +310,11 @@ static int cputemp_read(struct device *dev, *val = priv->temp.tjmax.value - priv->temp.tcontrol.value; break; default: @@ -362,7 +369,7 @@ index 11880c86a854..30ba1638e358 100644 } static umode_t cputemp_is_visible(const void *data, -@@ -262,11 +320,11 @@ 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; @@ -379,7 +386,7 @@ index 11880c86a854..30ba1638e358 100644 return 0; } -@@ -280,7 +338,7 @@ static const struct hwmon_ops cputemp_ops = { +@@ -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; @@ -388,7 +395,7 @@ index 11880c86a854..30ba1638e358 100644 /* Get the RESOLVED_CORES register value */ msg.addr = priv->mgr->client->addr; -@@ -290,30 +348,31 @@ static int check_resolved_cores(struct peci_cputemp *priv) +@@ -290,30 +351,34 @@ static int check_resolved_cores(struct peci_cputemp *priv) msg.reg = REG_RESOLVED_CORES_OFFSET; msg.rx_len = 4; @@ -400,6 +407,9 @@ index 11880c86a854..30ba1638e358 100644 + 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) @@ -429,7 +439,7 @@ index 11880c86a854..30ba1638e358 100644 priv->temp_config[priv->config_idx++] = config_table[channel_core]; -@@ -326,7 +385,7 @@ static int peci_cputemp_probe(struct platform_device *pdev) +@@ -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; @@ -438,7 +448,7 @@ index 11880c86a854..30ba1638e358 100644 if ((mgr->client->adapter->cmd_mask & (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) != -@@ -346,12 +405,13 @@ static int peci_cputemp_probe(struct platform_device *pdev) +@@ -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]; @@ -686,7 +696,7 @@ index 9af5730ad7ba..28087e9cd4da 100644 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..d62442438512 100644 +index d53e4f1078ac..466085fd43b9 100644 --- a/drivers/mfd/intel-peci-client.c +++ b/drivers/mfd/intel-peci-client.c @@ -1,12 +1,12 @@ @@ -717,7 +727,7 @@ index d53e4f1078ac..d62442438512 100644 static struct mfd_cell peci_functions[] = { { .name = "peci-cputemp", }, { .name = "peci-dimmtemp", }, -@@ -31,19 +25,19 @@ static struct mfd_cell peci_functions[] = { +@@ -31,19 +25,25 @@ static struct mfd_cell peci_functions[] = { }; static const struct cpu_gen_info cpu_gen_info_table[] = { @@ -736,11 +746,17 @@ index d53e4f1078ac..d62442438512 100644 .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 +47,17 @@ static const struct cpu_gen_info cpu_gen_info_table[] = { +@@ -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) { @@ -763,7 +779,7 @@ index d53e4f1078ac..d62442438512 100644 family = FIELD_PREP(LOWER_BYTE_MASK, FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id)) | -@@ -83,11 +78,11 @@ static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv) +@@ -83,11 +84,11 @@ static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv) } if (!priv->gen_info) { @@ -778,7 +794,7 @@ index d53e4f1078ac..d62442438512 100644 } static int peci_client_probe(struct peci_client *client) -@@ -103,31 +98,29 @@ 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; @@ -815,7 +831,7 @@ index d53e4f1078ac..d62442438512 100644 static const struct peci_device_id peci_client_ids[] = { { .name = "peci-client" }, diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig -index 9e9845ebcff4..9752feee2454 100644 +index 7293108fb543..9752feee2454 100644 --- a/drivers/peci/Kconfig +++ b/drivers/peci/Kconfig @@ -2,10 +2,12 @@ @@ -833,7 +849,7 @@ index 9e9845ebcff4..9752feee2454 100644 help The Platform Environment Control Interface (PECI) is a one-wire bus interface that provides a communication channel from Intel processors -@@ -14,26 +16,23 @@ config PECI +@@ -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. @@ -842,42 +858,53 @@ index 9e9845ebcff4..9752feee2454 100644 -# -# 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. --menu "PECI Hardware Bus support" +- This support is also available as a module. If so, the module +- will be called peci-aspeed. +if PECI --config PECI_ASPEED -- tristate "ASPEED PECI support" +-config PECI_NPCM +- tristate "Nuvoton NPCM PECI support" - select REGMAP_MMIO - depends on OF -- depends on ARCH_ASPEED || COMPILE_TEST +- 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 ASPEED SoCs. +- 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-aspeed. -+ This support is also available as a module. If so, the module -+ will be called peci-dev. - + 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 886285e69765..da8b0a33fa42 100644 +index 3326da54a21a..da8b0a33fa42 100644 --- a/drivers/peci/Makefile +++ b/drivers/peci/Makefile -@@ -1,9 +1,11 @@ +@@ -1,10 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 # -# Makefile for the PECI core and bus drivers. @@ -890,13 +917,14 @@ index 886285e69765..da8b0a33fa42 100644 # 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..a20d470b4250 +index 000000000000..bfacafb7a7ba --- /dev/null +++ b/drivers/peci/busses/Kconfig -@@ -0,0 +1,19 @@ +@@ -0,0 +1,32 @@ +# +# PECI hardware bus configuration +# @@ -912,22 +940,36 @@ index 000000000000..a20d470b4250 + 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 ++ 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..69e31dfaca19 +index 000000000000..aa8ce3ae5947 --- /dev/null +++ b/drivers/peci/busses/Makefile -@@ -0,0 +1,6 @@ +@@ -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 @@ -1428,6 +1470,422 @@ index 000000000000..8a0dd40730cc +MODULE_AUTHOR("Jae Hyun Yoo "); +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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* 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 "); ++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 @@ -1940,7 +2398,7 @@ index 51cb2563ceb6..000000000000 -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..e2ef013e5002 100644 +index 6f241469ec7e..d1f0df8b139a 100644 --- a/drivers/peci/peci-core.c +++ b/drivers/peci/peci-core.c @@ -1,38 +1,31 @@ @@ -2040,7 +2498,8 @@ index 6f241469ec7e..e2ef013e5002 100644 + * 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; + @@ -2097,8 +2556,7 @@ index 6f241469ec7e..e2ef013e5002 100644 + +/* Calculate an Assured Write Frame Check Sequence byte */ +static int peci_aw_fcs(struct peci_xfer_msg *msg, int len, u8 *aw_fcs) - { -- return crc8(peci_crc8_table, data, (size_t)len, 0); ++{ + u8 *tmp_buf; + + /* Allocate a temporary buffer to use a contiguous byte array */ @@ -2127,8 +2585,7 @@ index 6f241469ec7e..e2ef013e5002 100644 + 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. @@ -2144,12 +2601,13 @@ index 6f241469ec7e..e2ef013e5002 100644 + 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,56 @@ static int __peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg, +@@ -125,55 +223,51 @@ static int __peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg, */ if (do_retry) @@ -2162,11 +2620,10 @@ index 6f241469ec7e..e2ef013e5002 100644 + ret = adapter->xfer(adapter, msg); - if (!do_retry || rc) -+ if (!do_retry || ret) - break; - +- break; +- - if (msg->rx_buf[0] == DEV_PECI_CC_SUCCESS) -+ if (msg->rx_buf[0] == PECI_DEV_CC_SUCCESS) ++ if (!do_retry || ret || !msg->rx_buf) break; /* Retry is needed when completion code is 0x8x */ @@ -2174,10 +2631,9 @@ index 6f241469ec7e..e2ef013e5002 100644 - DEV_PECI_CC_NEED_RETRY) { - rc = -EIO; + if ((msg->rx_buf[0] & PECI_DEV_CC_RETRY_CHECK_MASK) != -+ PECI_DEV_CC_NEED_RETRY) { -+ ret = -EIO; ++ PECI_DEV_CC_NEED_RETRY) break; - } +- } /* Set the retry bit to indicate a retry attempt */ - msg->tx_buf[1] |= DEV_PECI_RETRY_BIT; @@ -2192,11 +2648,11 @@ index 6f241469ec7e..e2ef013e5002 100644 + 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: @@ -2230,7 +2686,7 @@ index 6f241469ec7e..e2ef013e5002 100644 } static int peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg) -@@ -190,34 +289,37 @@ static int peci_xfer_with_retries(struct peci_adapter *adapter, +@@ -190,34 +284,37 @@ static int peci_xfer_with_retries(struct peci_adapter *adapter, static int peci_scan_cmd_mask(struct peci_adapter *adapter) { @@ -2274,13 +2730,14 @@ index 6f241469ec7e..e2ef013e5002 100644 + 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 +345,14 @@ static int peci_scan_cmd_mask(struct peci_adapter *adapter) +@@ -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); @@ -2297,7 +2754,7 @@ index 6f241469ec7e..e2ef013e5002 100644 { if (!(adapter->cmd_mask & BIT(PECI_CMD_PING)) && peci_scan_cmd_mask(adapter) < 0) { -@@ -262,70 +368,87 @@ static int peci_cmd_support(struct peci_adapter *adapter, enum peci_cmd cmd) +@@ -262,70 +363,87 @@ static int peci_cmd_support(struct peci_adapter *adapter, enum peci_cmd cmd) return 0; } @@ -2316,19 +2773,19 @@ index 6f241469ec7e..e2ef013e5002 100644 - 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; -+ msg = peci_get_xfer_msg(0, 0); -+ if (!msg) -+ return -ENOMEM; ++ ret = peci_xfer(adapter, msg); - return peci_xfer(adapter, &msg); -+ msg->addr = umsg->addr; -+ -+ ret = peci_xfer(adapter, msg); -+ + peci_put_xfer_msg(msg); + + return ret; @@ -2419,7 +2876,7 @@ index 6f241469ec7e..e2ef013e5002 100644 /* 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 +457,34 @@ static int peci_ioctl_rd_pkg_cfg(struct peci_adapter *adapter, void *vmsg) +@@ -334,29 +452,35 @@ static int peci_ioctl_rd_pkg_cfg(struct peci_adapter *adapter, void *vmsg) return -EINVAL; } @@ -2445,17 +2902,18 @@ index 6f241469ec7e..e2ef013e5002 100644 + msg->tx_buf[2] = umsg->index; /* RdPkgConfig index */ + msg->tx_buf[3] = (u8)umsg->param; /* LSB - Config parameter */ + msg->tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */ -+ -+ ret = peci_xfer_with_retries(adapter, msg, false); -+ if (!ret) -+ memcpy(umsg->pkg_config, &msg->rx_buf[1], umsg->rx_len); - rc = peci_xfer_with_retries(adapter, &msg, false); - if (!rc) - memcpy(umsg->pkg_config, &msg.rx_buf[1], umsg->rx_len); -+ peci_put_xfer_msg(msg); ++ 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; } @@ -2471,7 +2929,7 @@ index 6f241469ec7e..e2ef013e5002 100644 /* Per the PECI spec, the write length must be a dword */ if (umsg->tx_len != 4) { -@@ -365,86 +493,113 @@ static int peci_ioctl_wr_pkg_cfg(struct peci_adapter *adapter, void *vmsg) +@@ -365,86 +489,116 @@ static int peci_ioctl_wr_pkg_cfg(struct peci_adapter *adapter, void *vmsg) return -EINVAL; } @@ -2504,19 +2962,20 @@ index 6f241469ec7e..e2ef013e5002 100644 + 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); ++ msg->tx_buf[5 + i] = 0x80 ^ aw_fcs; - rc = peci_xfer_with_retries(adapter, &msg, true); -+out: -+ peci_put_xfer_msg(msg); ++ ret = peci_xfer_with_retries(adapter, msg, true); - return rc; ++out: ++ umsg->cc = msg->rx_buf[0]; ++ peci_put_xfer_msg(msg); ++ + return ret; } @@ -2559,17 +3018,18 @@ index 6f241469ec7e..e2ef013e5002 100644 + if (!ret) + memcpy(&umsg->value, &msg->rx_buf[1], sizeof(uint64_t)); + ++ umsg->cc = msg->rx_buf[0]; + peci_put_xfer_msg(msg); + + return ret; -+} -+ -+static int peci_cmd_wr_ia_msr(struct peci_adapter *adapter, void *vmsg) -+{ -+ return -ENOSYS; /* Not implemented yet */ } -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; @@ -2614,6 +3074,7 @@ index 6f241469ec7e..e2ef013e5002 100644 - 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; @@ -2637,7 +3098,7 @@ index 6f241469ec7e..e2ef013e5002 100644 /* 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 +608,41 @@ static int peci_ioctl_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) +@@ -453,34 +607,42 @@ static int peci_ioctl_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) return -EINVAL; } @@ -2676,6 +3137,7 @@ index 6f241469ec7e..e2ef013e5002 100644 - 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; @@ -2695,7 +3157,7 @@ index 6f241469ec7e..e2ef013e5002 100644 /* 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,56 @@ static int peci_ioctl_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) +@@ -489,47 +651,57 @@ static int peci_ioctl_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) return -EINVAL; } @@ -2743,6 +3205,7 @@ index 6f241469ec7e..e2ef013e5002 100644 - rc = peci_xfer_with_retries(adapter, &msg, true); +out: ++ umsg->cc = msg->rx_buf[0]; + peci_put_xfer_msg(msg); - return rc; @@ -2782,7 +3245,7 @@ index 6f241469ec7e..e2ef013e5002 100644 }; /** -@@ -545,109 +716,28 @@ static const peci_ioctl_fn_type peci_ioctl_fn[PECI_CMD_MAX] = { +@@ -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) { @@ -2901,7 +3364,7 @@ index 6f241469ec7e..e2ef013e5002 100644 static int peci_detect(struct peci_adapter *adapter, u8 addr) { struct peci_ping_msg msg; -@@ -666,9 +756,9 @@ peci_of_match_device(const struct of_device_id *matches, +@@ -666,9 +757,9 @@ peci_of_match_device(const struct of_device_id *matches, return NULL; return of_match_device(matches, &client->dev); @@ -2913,7 +3376,7 @@ index 6f241469ec7e..e2ef013e5002 100644 } static const struct peci_device_id * -@@ -737,6 +827,7 @@ static int peci_device_probe(struct device *dev) +@@ -737,6 +828,7 @@ static int peci_device_probe(struct device *dev) err_detach_pm_domain: dev_pm_domain_detach(&client->dev, true); @@ -2921,7 +3384,7 @@ index 6f241469ec7e..e2ef013e5002 100644 return status; } -@@ -775,13 +866,14 @@ static void peci_device_shutdown(struct device *dev) +@@ -775,13 +867,14 @@ static void peci_device_shutdown(struct device *dev) driver->shutdown(client); } @@ -2937,7 +3400,7 @@ index 6f241469ec7e..e2ef013e5002 100644 static int peci_check_addr_validity(u8 addr) { -@@ -814,18 +906,18 @@ static int peci_check_client_busy(struct device *dev, void *client_new_p) +@@ -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; @@ -2953,16 +3416,22 @@ index 6f241469ec7e..e2ef013e5002 100644 - 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) - *cpu_id = le32_to_cpup((__le32 *)msg.pkg_config); ++ 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 ret; ++ return 0; } EXPORT_SYMBOL_GPL(peci_get_cpu_id); -@@ -833,7 +925,7 @@ static struct peci_client *peci_new_device(struct peci_adapter *adapter, +@@ -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; @@ -2971,7 +3440,7 @@ index 6f241469ec7e..e2ef013e5002 100644 /* Increase reference count for the adapter assigned */ if (!peci_get_adapter(adapter->nr)) -@@ -847,46 +939,49 @@ static struct peci_client *peci_new_device(struct peci_adapter *adapter, +@@ -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)); @@ -3033,7 +3502,7 @@ index 6f241469ec7e..e2ef013e5002 100644 return NULL; } -@@ -895,8 +990,10 @@ static void peci_unregister_device(struct peci_client *client) +@@ -895,8 +996,10 @@ static void peci_unregister_device(struct peci_client *client) if (!client) return; @@ -3045,7 +3514,7 @@ index 6f241469ec7e..e2ef013e5002 100644 device_unregister(&client->dev); } -@@ -916,7 +1013,7 @@ static void peci_adapter_dev_release(struct device *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); @@ -3054,7 +3523,7 @@ index 6f241469ec7e..e2ef013e5002 100644 kfree(adapter); } -@@ -928,7 +1025,8 @@ static ssize_t peci_sysfs_new_device(struct device *dev, +@@ -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; @@ -3064,7 +3533,7 @@ index 6f241469ec7e..e2ef013e5002 100644 /* Parse device type */ blank = strchr(buf, ' '); -@@ -943,16 +1041,17 @@ static ssize_t peci_sysfs_new_device(struct device *dev, +@@ -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 */ @@ -3085,7 +3554,7 @@ index 6f241469ec7e..e2ef013e5002 100644 client = peci_new_device(adapter, &info); if (!client) return -EINVAL; -@@ -961,8 +1060,8 @@ static ssize_t peci_sysfs_new_device(struct device *dev, +@@ -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); @@ -3096,7 +3565,7 @@ index 6f241469ec7e..e2ef013e5002 100644 return count; } -@@ -975,9 +1074,9 @@ static ssize_t peci_sysfs_delete_device(struct device *dev, +@@ -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 = {}; @@ -3108,7 +3577,7 @@ index 6f241469ec7e..e2ef013e5002 100644 /* Parse device type */ blank = strchr(buf, ' '); -@@ -992,41 +1091,41 @@ static ssize_t peci_sysfs_delete_device(struct device *dev, +@@ -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 */ @@ -3162,7 +3631,7 @@ index 6f241469ec7e..e2ef013e5002 100644 } static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, 0200, NULL, peci_sysfs_delete_device); -@@ -1039,10 +1138,11 @@ static struct attribute *peci_adapter_attrs[] = { +@@ -1039,10 +1144,11 @@ static struct attribute *peci_adapter_attrs[] = { }; ATTRIBUTE_GROUPS(peci_adapter); @@ -3175,7 +3644,7 @@ index 6f241469ec7e..e2ef013e5002 100644 /** * peci_verify_adapter - return parameter as peci_adapter, or NULL -@@ -1063,32 +1163,26 @@ static struct peci_client *peci_of_register_device(struct peci_adapter *adapter, +@@ -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 = {}; @@ -3220,7 +3689,7 @@ index 6f241469ec7e..e2ef013e5002 100644 } static void peci_of_register_devices(struct peci_adapter *adapter) -@@ -1119,7 +1213,7 @@ 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); } @@ -3229,7 +3698,7 @@ index 6f241469ec7e..e2ef013e5002 100644 static void peci_of_register_devices(struct peci_adapter *adapter) { } #endif /* CONFIG_OF */ -@@ -1163,9 +1257,7 @@ static struct peci_adapter *peci_of_find_adapter(struct device_node *node) +@@ -1163,9 +1263,7 @@ static struct peci_adapter *peci_of_find_adapter(struct device_node *node) return adapter; } @@ -3240,7 +3709,7 @@ index 6f241469ec7e..e2ef013e5002 100644 { struct of_reconfig_data *rd = arg; struct peci_adapter *adapter; -@@ -1216,7 +1308,7 @@ static int peci_of_notify(struct notifier_block *nb, +@@ -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, }; @@ -3249,7 +3718,7 @@ index 6f241469ec7e..e2ef013e5002 100644 extern struct notifier_block peci_of_notifier; #endif /* CONFIG_OF_DYNAMIC */ -@@ -1240,7 +1332,7 @@ extern struct notifier_block peci_of_notifier; +@@ -1240,7 +1338,7 @@ extern struct notifier_block peci_of_notifier; * * Return: the peci_adapter structure on success, else NULL. */ @@ -3258,7 +3727,7 @@ index 6f241469ec7e..e2ef013e5002 100644 { struct peci_adapter *adapter; -@@ -1263,7 +1355,7 @@ EXPORT_SYMBOL_GPL(peci_alloc_adapter); +@@ -1263,7 +1361,7 @@ EXPORT_SYMBOL_GPL(peci_alloc_adapter); static int peci_register_adapter(struct peci_adapter *adapter) { @@ -3267,7 +3736,7 @@ index 6f241469ec7e..e2ef013e5002 100644 /* Can't register until after driver model init */ if (WARN_ON(!is_registered)) -@@ -1275,27 +1367,17 @@ static int peci_register_adapter(struct peci_adapter *adapter) +@@ -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; @@ -3300,7 +3769,7 @@ index 6f241469ec7e..e2ef013e5002 100644 } dev_dbg(&adapter->dev, "adapter [%s] registered\n", adapter->name); -@@ -1309,13 +1391,11 @@ static int peci_register_adapter(struct peci_adapter *adapter) +@@ -1309,13 +1397,11 @@ static int peci_register_adapter(struct peci_adapter *adapter) return 0; @@ -3315,7 +3784,7 @@ index 6f241469ec7e..e2ef013e5002 100644 } static int peci_add_numbered_adapter(struct peci_adapter *adapter) -@@ -1411,7 +1491,7 @@ void peci_del_adapter(struct peci_adapter *adapter) +@@ -1411,7 +1497,7 @@ void peci_del_adapter(struct peci_adapter *adapter) } mutex_unlock(&adapter->userspace_clients_lock); @@ -3324,7 +3793,7 @@ index 6f241469ec7e..e2ef013e5002 100644 * Detach any active clients. This can't fail, thus we do not * check the returned value. */ -@@ -1420,13 +1500,8 @@ void peci_del_adapter(struct peci_adapter *adapter) +@@ -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); @@ -3338,7 +3807,7 @@ index 6f241469ec7e..e2ef013e5002 100644 device_unregister(&adapter->dev); /* free bus id */ -@@ -1436,6 +1511,18 @@ void peci_del_adapter(struct peci_adapter *adapter) +@@ -1436,6 +1517,18 @@ void peci_del_adapter(struct peci_adapter *adapter) } EXPORT_SYMBOL_GPL(peci_del_adapter); @@ -3357,7 +3826,7 @@ index 6f241469ec7e..e2ef013e5002 100644 /** * peci_register_driver - register a PECI driver * @owner: owner module of the driver being registered -@@ -1446,7 +1533,7 @@ EXPORT_SYMBOL_GPL(peci_del_adapter); +@@ -1446,7 +1539,7 @@ EXPORT_SYMBOL_GPL(peci_del_adapter); */ int peci_register_driver(struct module *owner, struct peci_driver *driver) { @@ -3366,7 +3835,7 @@ index 6f241469ec7e..e2ef013e5002 100644 /* Can't register until after driver model init */ if (WARN_ON(!is_registered)) -@@ -1456,13 +1543,13 @@ int peci_register_driver(struct module *owner, struct peci_driver *driver) +@@ -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; @@ -3384,7 +3853,7 @@ index 6f241469ec7e..e2ef013e5002 100644 pr_debug("driver [%s] registered\n", driver->driver.name); -@@ -1492,13 +1579,6 @@ static int __init peci_init(void) +@@ -1492,13 +1585,6 @@ static int __init peci_init(void) return ret; } @@ -3398,7 +3867,7 @@ index 6f241469ec7e..e2ef013e5002 100644 crc8_populate_msb(peci_crc8_table, PECI_CRC8_POLYNOMIAL); if (IS_ENABLED(CONFIG_OF_DYNAMIC)) -@@ -1514,11 +1594,10 @@ static void __exit peci_exit(void) +@@ -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)); @@ -3413,10 +3882,10 @@ index 6f241469ec7e..e2ef013e5002 100644 MODULE_AUTHOR("Jason M Biils "); diff --git a/drivers/peci/peci-dev.c b/drivers/peci/peci-dev.c new file mode 100644 -index 000000000000..5de0683206bc +index 000000000000..ac9cba0fb429 --- /dev/null +++ b/drivers/peci/peci-dev.c -@@ -0,0 +1,340 @@ +@@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018-2019 Intel Corporation + @@ -3517,9 +3986,9 @@ index 000000000000..5de0683206bc +static long peci_dev_ioctl(struct file *file, uint iocmd, ulong arg) +{ + struct peci_dev *peci_dev = file->private_data; -+ struct peci_xfer_msg __user *uxmsg; ++ void __user *umsg = (void __user *)arg; + struct peci_xfer_msg *xmsg = NULL; -+ void __user *umsg; ++ struct peci_xfer_msg uxmsg; + enum peci_cmd cmd; + u8 *msg = NULL; + uint msg_len; @@ -3535,30 +4004,35 @@ index 000000000000..5de0683206bc + break; + } + -+ uxmsg = (struct peci_xfer_msg __user *)arg; -+ xmsg = peci_get_xfer_msg(uxmsg->tx_len, uxmsg->rx_len); ++ 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(uxmsg->tx_buf, xmsg->tx_buf, -+ uxmsg->tx_len)) { ++ 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 && uxmsg->rx_len && -+ copy_to_user(xmsg->rx_buf, uxmsg->rx_buf, -+ uxmsg->rx_len)) ++ if (!ret && xmsg->rx_len && ++ copy_to_user(uxmsg.rx_buf, xmsg->rx_buf, xmsg->rx_len)) + ret = -EFAULT; + + break; + + default: -+ umsg = (void __user *)arg; + msg = memdup_user(umsg, msg_len); + if (IS_ERR(msg)) { + ret = PTR_ERR(msg); @@ -3566,7 +4040,8 @@ index 000000000000..5de0683206bc + } + + ret = peci_command(peci_dev->adapter, cmd, msg); -+ if (!ret && copy_to_user(umsg, msg, msg_len)) ++ if ((!ret || ret == -ETIMEDOUT) && ++ copy_to_user(umsg, msg, msg_len)) + ret = -EFAULT; + + break; @@ -3757,8 +4232,424 @@ index 000000000000..5de0683206bc +MODULE_AUTHOR("Jae Hyun Yoo "); +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 +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-/* 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 "); +-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..1f1b07a9aeab 100644 +index 8f6d823a59cd..7b62a02e46ee 100644 --- a/include/linux/mfd/intel-peci-client.h +++ b/include/linux/mfd/intel-peci-client.h @@ -1,5 +1,5 @@ @@ -3768,7 +4659,7 @@ index 8f6d823a59cd..1f1b07a9aeab 100644 #ifndef __LINUX_MFD_INTEL_PECI_CLIENT_H #define __LINUX_MFD_INTEL_PECI_CLIENT_H -@@ -9,7 +9,7 @@ +@@ -9,14 +9,15 @@ #if IS_ENABLED(CONFIG_X86) #include #else @@ -3777,7 +4668,29 @@ index 8f6d823a59cd..1f1b07a9aeab 100644 * 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. -@@ -58,7 +58,6 @@ struct cpu_gen_info { + */ +-#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 @@ -3785,7 +4698,7 @@ index 8f6d823a59cd..1f1b07a9aeab 100644 * @name: PECI client manager name * @gen_info: CPU generation info of the detected CPU * -@@ -67,7 +66,6 @@ struct cpu_gen_info { +@@ -67,7 +71,6 @@ struct cpu_gen_info { */ struct peci_client_manager { struct peci_client *client; @@ -3793,6 +4706,35 @@ index 8f6d823a59cd..1f1b07a9aeab 100644 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 @@ -3890,7 +4832,7 @@ index d0e47d45d1d0..6fc424dc2a73 100644 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..8467b2fbee1f 100644 +index a6dae71cbff5..253fb42e38b7 100644 --- a/include/uapi/linux/peci-ioctl.h +++ b/include/uapi/linux/peci-ioctl.h @@ -1,5 +1,5 @@ @@ -3900,7 +4842,7 @@ index a6dae71cbff5..8467b2fbee1f 100644 #ifndef __PECI_IOCTL_H #define __PECI_IOCTL_H -@@ -7,136 +7,34 @@ +@@ -7,136 +7,35 @@ #include #include @@ -3981,13 +4923,13 @@ index a6dae71cbff5..8467b2fbee1f 100644 -#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 ++#define PECI_BASE_ADDR 0x30 + +/* Max number of CPU clients */ -+#define PECI_OFFSET_MAX 8 ++#define PECI_OFFSET_MAX 8 + +/* PECI read/write data buffer size max */ -+#define PECI_BUFFER_SIZE 255 ++#define PECI_BUFFER_SIZE 255 /* Device Specific Completion Code (CC) Definition */ -#define DEV_PECI_CC_SUCCESS 0x40 @@ -3995,17 +4937,22 @@ index a6dae71cbff5..8467b2fbee1f 100644 -#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_TIMEOUT 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_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 -+#define PECI_DEV_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 @@ -4037,13 +4984,9 @@ index a6dae71cbff5..8467b2fbee1f 100644 -#define RDPCICFG_WRITE_LEN 6 -#define RDPCICFG_READ_LEN 5 -#define RDPCICFG_PECI_CMD 0x61 -+#define PECI_DEV_RETRY_TIME_MS 250 -+#define PECI_DEV_RETRY_INTERVAL_USEC 10000 -+#define PECI_DEV_RETRY_BIT 0x01 - +- -#define WRPCICFG_PECI_CMD 0x65 -+#define PECI_WRIAMSR_CMD 0xb5 - +- -#define RDPCICFGLOCAL_WRITE_LEN 5 -#define RDPCICFGLOCAL_READ_LEN_BASE 1 -#define RDPCICFGLOCAL_PECI_CMD 0xe1 @@ -4053,11 +4996,13 @@ index a6dae71cbff5..8467b2fbee1f 100644 -#define WRPCICFGLOCAL_PECI_CMD 0xe5 - -#define PECI_BUFFER_SIZE 32 -+#define PECI_WRPCICFG_CMD 0x65 ++#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 +84,12 @@ enum peci_cmd { +@@ -186,11 +85,12 @@ enum peci_cmd { * raw PECI transfer */ struct peci_xfer_msg { @@ -4075,7 +5020,7 @@ index a6dae71cbff5..8467b2fbee1f 100644 } __attribute__((__packed__)); /** -@@ -202,7 +101,8 @@ struct peci_xfer_msg { +@@ -202,7 +102,8 @@ struct peci_xfer_msg { * powered-off, etc. */ struct peci_ping_msg { @@ -4085,7 +5030,7 @@ index a6dae71cbff5..8467b2fbee1f 100644 } __attribute__((__packed__)); /** -@@ -216,8 +116,13 @@ struct peci_ping_msg { +@@ -216,8 +117,13 @@ struct peci_ping_msg { * command. */ struct peci_get_dib_msg { @@ -4101,7 +5046,7 @@ index a6dae71cbff5..8467b2fbee1f 100644 } __attribute__((__packed__)); /** -@@ -232,8 +137,14 @@ struct peci_get_dib_msg { +@@ -232,8 +138,13 @@ struct peci_get_dib_msg { * below the maximum processor junction temperature. */ struct peci_get_temp_msg { @@ -4112,13 +5057,20 @@ index a6dae71cbff5..8467b2fbee1f 100644 +#define PECI_GET_TEMP_CMD 0x01 + + __u8 addr; -+ __u8 padding0[3]; ++ __u8 padding; + __s16 temp_raw; -+ __u8 padding1[2]; } __attribute__((__packed__)); /** -@@ -251,11 +162,72 @@ struct peci_get_temp_msg { +@@ -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 { @@ -4191,12 +5143,21 @@ index a6dae71cbff5..8467b2fbee1f 100644 +#define PECI_PKG_ID_MACHINE_CHECK_STATUS 0x0005 /* Machine Check Status */ + + __u8 rx_len; -+ __u8 padding[3]; ++ __u8 cc; ++ __u8 padding[2]; + __u8 pkg_config[4]; } __attribute__((__packed__)); /** -@@ -272,11 +244,19 @@ struct peci_rd_pkg_cfg_msg { +@@ -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 { @@ -4216,12 +5177,20 @@ index a6dae71cbff5..8467b2fbee1f 100644 + + __u16 param; + __u8 tx_len; -+ __u8 padding[3]; ++ __u8 cc; ++ __u8 padding[2]; + __u32 value; } __attribute__((__packed__)); /** -@@ -290,10 +270,34 @@ struct peci_wr_pkg_cfg_msg { +@@ -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 { @@ -4236,6 +5205,8 @@ index a6dae71cbff5..8467b2fbee1f 100644 + __u8 addr; + __u8 thread_id; + __u16 address; ++ __u8 cc; ++ __u8 padding[3]; + __u64 value; +} __attribute__((__packed__)); + @@ -4245,22 +5216,34 @@ index a6dae71cbff5..8467b2fbee1f 100644 + * @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 padding[3]; ++ __u8 cc; ++ __u8 padding[2]; + __u64 value; } __attribute__((__packed__)); /** -@@ -310,12 +314,52 @@ struct peci_rd_ia_msr_msg { +@@ -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 { @@ -4289,7 +5272,8 @@ index a6dae71cbff5..8467b2fbee1f 100644 + __u8 device; + __u8 function; + __u16 reg; -+ __u8 padding[2]; ++ __u8 cc; ++ __u8 padding[1]; + __u8 pci_config[4]; +} __attribute__((__packed__)); + @@ -4301,6 +5285,7 @@ index a6dae71cbff5..8467b2fbee1f 100644 + * @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 @@ -4308,18 +5293,28 @@ index a6dae71cbff5..8467b2fbee1f 100644 + * 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 padding; ++ __u8 cc; + __u8 pci_config[4]; } __attribute__((__packed__)); /** -@@ -333,13 +377,18 @@ struct peci_rd_pci_cfg_msg { +@@ -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 { @@ -4340,12 +5335,20 @@ index a6dae71cbff5..8467b2fbee1f 100644 + __u8 function; + __u16 reg; + __u8 rx_len; -+ __u8 padding[3]; ++ __u8 cc; + __u8 pci_config[4]; } __attribute__((__packed__)); /** -@@ -357,13 +406,18 @@ struct peci_rd_pci_cfg_local_msg { +@@ -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 { @@ -4366,12 +5369,12 @@ index a6dae71cbff5..8467b2fbee1f 100644 + __u8 function; + __u16 reg; + __u8 tx_len; -+ __u8 padding[3]; ++ __u8 cc; + __u32 value; } __attribute__((__packed__)); #define PECI_IOC_BASE 0xb7 -@@ -389,9 +443,15 @@ struct peci_wr_pci_cfg_local_msg { +@@ -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) 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 index e6dd44cd7..d1745ce5f 100644 --- 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 @@ -104,17 +104,14 @@ diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 768278b059c3..de2d5c6d186c 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile -@@ -56,6 +56,7 @@ obj-$(CONFIG_CXL_BASE) += cxl/ +@@ -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 -+obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.o - obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o - obj-$(CONFIG_OCXL) += ocxl/ - obj-y += cardreader/ -diff --git a/drivers/misc/aspeed-lpc-sio.c b/drivers/misc/aspeed-lpc-sio.c -new file mode 100644 -index 000000000000..c717a3182320 --- /dev/null +++ b/drivers/misc/aspeed-lpc-sio.c @@ -0,0 +1,450 @@ 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 index 216c750de..303bff0b1 100644 --- 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 @@ -225,10 +225,10 @@ diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 7b018962cad3..89b051f82391 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile -@@ -53,6 +53,7 @@ obj-$(CONFIG_GENWQE) += genwqe/ - obj-$(CONFIG_ECHO) += echo/ +@@ -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 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 index 922a45787..1532a5642 100644 --- 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 @@ -1,19 +1,19 @@ -From 23a7407c3f1bab7c01b93eeced4e137601ac1c94 Mon Sep 17 00:00:00 2001 +From d1d221aedb9b366e3477f253386b1cf829c56193 Mon Sep 17 00:00:00 2001 From: "Jason M. Bills" Date: Wed, 4 Apr 2018 13:52:39 -0700 Subject: [PATCH] Add support for new PECI commands Signed-off-by: Jason M. Bills --- - drivers/peci/peci-core.c | 223 ++++++++++++++++++++++++++++++++++++++++ - include/uapi/linux/peci-ioctl.h | 102 ++++++++++++++++++ - 2 files changed, 325 insertions(+) + 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 14048a13ef8a..2f7e795158ce 100644 +index e282f9ef383b..9af11accc1d4 100644 --- a/drivers/peci/peci-core.c +++ b/drivers/peci/peci-core.c -@@ -344,6 +344,9 @@ static int peci_scan_cmd_mask(struct peci_adapter *adapter) +@@ -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); @@ -23,7 +23,7 @@ index 14048a13ef8a..2f7e795158ce 100644 out: peci_put_xfer_msg(msg); -@@ -686,6 +689,223 @@ static int peci_cmd_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) +@@ -693,6 +696,226 @@ static int peci_cmd_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) return ret; } @@ -157,6 +157,7 @@ index 14048a13ef8a..2f7e795158ce 100644 + 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; @@ -197,6 +198,7 @@ index 14048a13ef8a..2f7e795158ce 100644 + 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; @@ -239,6 +241,7 @@ index 14048a13ef8a..2f7e795158ce 100644 + 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; @@ -247,7 +250,7 @@ index 14048a13ef8a..2f7e795158ce 100644 typedef int (*peci_cmd_fn_type)(struct peci_adapter *, void *); static const peci_cmd_fn_type peci_cmd_fn[PECI_CMD_MAX] = { -@@ -701,6 +921,9 @@ 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, @@ -258,10 +261,10 @@ index 14048a13ef8a..2f7e795158ce 100644 /** diff --git a/include/uapi/linux/peci-ioctl.h b/include/uapi/linux/peci-ioctl.h -index 8467b2fbee1f..090b02c4de49 100644 +index 4c28abe2c17a..e67b0735f606 100644 --- a/include/uapi/linux/peci-ioctl.h +++ b/include/uapi/linux/peci-ioctl.h -@@ -70,6 +70,9 @@ enum peci_cmd { +@@ -72,6 +72,9 @@ enum peci_cmd { PECI_CMD_WR_PCI_CFG, PECI_CMD_RD_PCI_CFG_LOCAL, PECI_CMD_WR_PCI_CFG_LOCAL, @@ -271,7 +274,7 @@ index 8467b2fbee1f..090b02c4de49 100644 PECI_CMD_MAX }; -@@ -420,6 +423,93 @@ struct peci_wr_pci_cfg_local_msg { +@@ -439,6 +442,95 @@ struct peci_wr_pci_cfg_local_msg { __u32 value; } __attribute__((__packed__)); @@ -311,7 +314,8 @@ index 8467b2fbee1f..090b02c4de49 100644 + } mmio; + } params; + __u8 rx_len; -+ __u8 padding[3]; ++ __u8 cc; ++ __u8 padding[2]; + __u8 data[8]; +} __attribute__((__packed__)); + @@ -333,8 +337,8 @@ index 8467b2fbee1f..090b02c4de49 100644 +#define PECI_CRASHDUMP_NUM_AGENTS 0x01 +#define PECI_CRASHDUMP_AGENT_DATA 0x02 + ++ __u8 cc; + __u8 param0; -+ __u8 padding; + __u16 param1; + __u8 param2; + __u8 rx_len; @@ -344,11 +348,11 @@ index 8467b2fbee1f..090b02c4de49 100644 +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 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 3 ++#define PECI_CRASHDUMP_GET_FRAME_VERSION 0 +#define PECI_CRASHDUMP_GET_FRAME_OPCODE 3 +#define PECI_CRASHDUMP_CMD 0x71 + @@ -358,14 +362,15 @@ index 8467b2fbee1f..090b02c4de49 100644 + __u16 param1; + __u16 param2; + __u8 rx_len; -+ __u8 padding1[3]; ++ __u8 cc; ++ __u8 padding1[2]; + __u8 data[16]; +} __attribute__((__packed__)); + #define PECI_IOC_BASE 0xb7 #define PECI_IOC_XFER \ -@@ -460,4 +550,16 @@ struct peci_wr_pci_cfg_local_msg { +@@ -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) 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 index 89a667e95..56dca7345 100644 --- 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 @@ -1,1059 +1,14 @@ -From 409ea2cede8588a59badd5dd7cf8721879d4c68a Mon Sep 17 00:00:00 2001 -From: "Hunt, Bryan" -Date: Fri, 30 Mar 2018 10:48:01 -0700 -Subject: [PATCH] Add AST2500d JTAG driver +From 43470f186979483ba6c1e6374c7ea3a129622862 Mon Sep 17 00:00:00 2001 +From: "Corona, Ernesto" +Date: Mon, 1 Mar 2019 11:46:09 -0700 +Subject: [PATCH] Update AST2500d JTAG driver. Step 1 -Adding aspeed jtag driver +Update AST2500d JTAG driver. Remove Legacy driver but keep headers. -Signed-off-by: Hunt, Bryan +Signed-off-by: Corona, Ernesto --- - arch/arm/boot/dts/aspeed-g5.dtsi | 9 + - drivers/Kconfig | 1 + - drivers/Makefile | 1 + - drivers/jtag/Kconfig | 13 + - drivers/jtag/Makefile | 1 + - drivers/jtag/jtag_aspeed.c | 963 +++++++++++++++++++++++++++++++++++++++ - include/uapi/linux/jtag_drv.h | 73 +++ - 7 files changed, 1061 insertions(+) - create mode 100644 drivers/jtag/Kconfig - create mode 100644 drivers/jtag/Makefile - create mode 100644 drivers/jtag/jtag_aspeed.c create mode 100644 include/uapi/linux/jtag_drv.h -diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi -index 01d27e845982..adde826ac1d9 100644 ---- a/arch/arm/boot/dts/aspeed-g5.dtsi -+++ b/arch/arm/boot/dts/aspeed-g5.dtsi -@@ -367,6 +367,15 @@ - pinctrl-0 = <&pinctrl_espi_default>; - }; - -+ jtag: jtag@1e6e4000 { -+ compatible = "aspeed,ast2500-jtag"; -+ reg = <0x1e6e2004 0x4 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>; -diff --git a/drivers/Kconfig b/drivers/Kconfig -index bbb66439a307..a1579d66f47d 100644 ---- a/drivers/Kconfig -+++ b/drivers/Kconfig -@@ -230,4 +230,5 @@ source "drivers/slimbus/Kconfig" - - source "drivers/peci/Kconfig" - -+source "drivers/jtag/Kconfig" - endmenu -diff --git a/drivers/Makefile b/drivers/Makefile -index 9ec44c032a42..69b201766154 100644 ---- a/drivers/Makefile -+++ b/drivers/Makefile -@@ -187,3 +187,4 @@ obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ - obj-$(CONFIG_SIOX) += siox/ - obj-$(CONFIG_GNSS) += gnss/ - obj-$(CONFIG_PECI) += peci/ -+obj-$(CONFIG_JTAG_ASPEED) += jtag/ -diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig -new file mode 100644 -index 000000000000..2e5d0a5bea90 ---- /dev/null -+++ b/drivers/jtag/Kconfig -@@ -0,0 +1,13 @@ -+menuconfig JTAG_ASPEED -+ tristate "ASPEED SoC JTAG controller support" -+ depends on 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 -new file mode 100644 -index 000000000000..db9b660e9f90 ---- /dev/null -+++ b/drivers/jtag/Makefile -@@ -0,0 +1 @@ -+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 000000000000..42e2a131873c ---- /dev/null -+++ b/drivers/jtag/jtag_aspeed.c -@@ -0,0 +1,963 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (C) 2012-2017 ASPEED Technology Inc. -+// Copyright (c) 2018 Intel Corporation -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define SCU_RESET_JTAG BIT(22) -+ -+#define AST_JTAG_DATA 0x00 -+#define AST_JTAG_INST 0x04 -+#define AST_JTAG_CTRL 0x08 -+#define AST_JTAG_ISR 0x0C -+#define AST_JTAG_SW 0x10 -+#define AST_JTAG_TCK 0x14 -+#define AST_JTAG_IDLE 0x18 -+ -+/* AST_JTAG_CTRL - 0x08 : Engine Control */ -+#define JTAG_ENG_EN BIT(31) -+#define JTAG_ENG_OUT_EN BIT(30) -+#define JTAG_ENGINE_EN (JTAG_ENG_EN | JTAG_ENG_OUT_EN) -+#define JTAG_FORCE_TMS BIT(29) -+ -+#define JTAG_IR_UPDATE BIT(26) /* AST2500 only */ -+#define JTAG_INST_LEN_MASK GENMASK(25, 20) -+#define JTAG_LAST_INST BIT(17) -+#define JTAG_INST_EN BIT(16) -+#define JTAG_DATA_LEN_MASK GENMASK(9, 4) -+ -+#define JTAG_DR_UPDATE BIT(10) /* AST2500 only */ -+#define JTAG_LAST_DATA BIT(1) -+#define JTAG_DATA_EN BIT(0) -+ -+/* AST_JTAG_ISR - 0x0C : Interrupt status and enable */ -+#define JTAG_INST_PAUSE BIT(19) -+#define JTAG_INST_COMPLETE BIT(18) -+#define JTAG_DATA_PAUSE BIT(17) -+#define JTAG_DATA_COMPLETE BIT(16) -+ -+#define JTAG_INST_PAUSE_EN BIT(3) -+#define JTAG_INST_COMPLETE_EN BIT(2) -+#define JTAG_DATA_PAUSE_EN BIT(1) -+#define JTAG_DATA_COMPLETE_EN BIT(0) -+ -+/* AST_JTAG_SW - 0x10 : Software Mode and Status */ -+#define JTAG_SW_MODE_EN BIT(19) -+#define JTAG_SW_MODE_TCK BIT(18) -+#define JTAG_SW_MODE_TMS BIT(17) -+#define JTAG_SW_MODE_TDIO BIT(16) -+ -+/* AST_JTAG_TCK - 0x14 : TCK Control */ -+#define JTAG_TCK_DIVISOR_MASK GENMASK(10, 0) -+ -+/* #define USE_INTERRUPTS */ -+#define AST_JTAG_NAME "jtag" -+ -+static DEFINE_SPINLOCK(jtag_state_lock); -+ -+struct ast_jtag_info { -+ void __iomem *reg_base; -+ void __iomem *reg_base_scu; -+ int irq; -+ u32 flag; -+ wait_queue_head_t jtag_wq; -+ bool is_open; -+ struct device *dev; -+ struct miscdevice miscdev; -+}; -+ -+/* -+ * 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; -+}; -+ -+/* -+ * These are the string representations of the TAP states corresponding to the -+ * enums literals in JtagStateEncode -+ */ -+static const char * const c_statestr[] = {"TLR", "RTI", "SelDR", "CapDR", -+ "ShfDR", "Ex1DR", "PauDR", "Ex2DR", -+ "UpdDR", "SelIR", "CapIR", "ShfIR", -+ "Ex1IR", "PauIR", "Ex2IR", "UpdIR"}; -+ -+/* -+ * 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 PDR Ex2DR UpdDR SelIR CapIR SIR Ex1IR PIR Ex2IR UpdIR*/ -+/* TLR */{ {0x00, 0}, {0x00, 1}, {0x02, 2}, {0x02, 3}, {0x02, 4}, {0x0a, 4}, {0x0a, 5}, {0x2a, 6}, {0x1a, 5}, {0x06, 3}, {0x06, 4}, {0x06, 5}, {0x16, 5}, {0x16, 6}, {0x56, 7}, {0x36, 6} }, -+/* RTI */{ {0x07, 3}, {0x00, 0}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3}, {0x05, 4}, {0x15, 5}, {0x0d, 4}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5} }, -+/* SelDR*/{ {0x03, 2}, {0x03, 3}, {0x00, 0}, {0x00, 1}, {0x00, 2}, {0x02, 2}, {0x02, 3}, {0x0a, 4}, {0x06, 3}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3}, {0x05, 4}, {0x15, 5}, {0x0d, 4} }, -+/* CapDR*/{ {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x00, 0}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2}, {0x0f, 4}, {0x0f, 5}, {0x0f, 6}, {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, {0x6f, 7} }, -+/* SDR */{ {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x00, 0}, {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2}, {0x0f, 4}, {0x0f, 5}, {0x0f, 6}, {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, {0x6f, 7} }, -+/* Ex1DR*/{ {0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x02, 3}, {0x00, 0}, {0x00, 1}, {0x02, 2}, {0x01, 1}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6} }, -+/* PDR */{ {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x01, 2}, {0x05, 3}, {0x00, 0}, {0x01, 1}, {0x03, 2}, {0x0f, 4}, {0x0f, 5}, {0x0f, 6}, {0x2f, 6}, {0x2f, 7}, {0xaf, 8}, {0x6f, 7} }, -+/* Ex2DR*/{ {0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x00, 1}, {0x02, 2}, {0x02, 3}, {0x00, 0}, {0x01, 1}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6} }, -+/* UpdDR*/{ {0x07, 3}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3}, {0x05, 4}, {0x15, 5}, {0x00, 0}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5} }, -+/* SelIR*/{ {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x05, 4}, {0x05, 5}, {0x15, 5}, {0x15, 6}, {0x55, 7}, {0x35, 6}, {0x00, 0}, {0x00, 1}, {0x00, 2}, {0x02, 2}, {0x02, 3}, {0x0a, 4}, {0x06, 3} }, -+/* CapIR*/{ {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, {0x00, 0}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2} }, -+/* SIR */{ {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, {0x0f, 5}, {0x00, 0}, {0x01, 1}, {0x01, 2}, {0x05, 3}, {0x03, 2} }, -+/* Ex1IR*/{ {0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5}, {0x07, 3}, {0x07, 4}, {0x02, 3}, {0x00, 0}, {0x00, 1}, {0x02, 2}, {0x01, 1} }, -+/* PIR */{ {0x1f, 5}, {0x03, 3}, {0x07, 3}, {0x07, 4}, {0x07, 5}, {0x17, 5}, {0x17, 6}, {0x57, 7}, {0x37, 6}, {0x0f, 4}, {0x0f, 5}, {0x01, 2}, {0x05, 3}, {0x00, 0}, {0x01, 1}, {0x03, 2} }, -+/* Ex2IR*/{ {0x0f, 4}, {0x01, 2}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x1b, 5}, {0x07, 3}, {0x07, 4}, {0x00, 1}, {0x02, 2}, {0x02, 3}, {0x00, 0}, {0x01, 1} }, -+/* UpdIR*/{ {0x07, 3}, {0x00, 1}, {0x01, 1}, {0x01, 2}, {0x01, 3}, {0x05, 3}, {0x05, 4}, {0x15, 5}, {0x0d, 4}, {0x03, 2}, {0x03, 3}, {0x03, 4}, {0x0b, 4}, {0x0b, 5}, {0x2b, 6}, {0x00, 0} }, -+}; -+ -+static const char * const regnames[] = { -+ [AST_JTAG_DATA] = "AST_JTAG_DATA", -+ [AST_JTAG_INST] = "AST_JTAG_INST", -+ [AST_JTAG_CTRL] = "AST_JTAG_CTRL", -+ [AST_JTAG_ISR] = "AST_JTAG_ISR", -+ [AST_JTAG_SW] = "AST_JTAG_SW", -+ [AST_JTAG_TCK] = "AST_JTAG_TCK", -+ [AST_JTAG_IDLE] = "AST_JTAG_IDLE", -+}; -+ -+static inline u32 ast_jtag_read(struct ast_jtag_info *ast_jtag, u32 reg) -+{ -+ u32 val = readl(ast_jtag->reg_base + reg); -+ -+ dev_dbg(ast_jtag->dev, "read:%s val = 0x%08x\n", regnames[reg], val); -+ return val; -+} -+ -+static inline void ast_jtag_write(struct ast_jtag_info *ast_jtag, u32 val, -+ u32 reg) -+{ -+ dev_dbg(ast_jtag->dev, "write:%s val = 0x%08x\n", regnames[reg], val); -+ writel(val, ast_jtag->reg_base + reg); -+} -+ -+static void ast_jtag_set_tck(struct ast_jtag_info *ast_jtag, -+ enum xfer_mode mode, uint tck) -+{ -+ u32 read_value; -+ -+ if (tck == 0) -+ tck = 1; -+ else if (tck > JTAG_TCK_DIVISOR_MASK) -+ tck = JTAG_TCK_DIVISOR_MASK; -+ read_value = ast_jtag_read(ast_jtag, AST_JTAG_TCK); -+ ast_jtag_write(ast_jtag, -+ ((read_value & ~JTAG_TCK_DIVISOR_MASK) | tck), -+ AST_JTAG_TCK); -+} -+ -+static void ast_jtag_get_tck(struct ast_jtag_info *ast_jtag, -+ enum xfer_mode mode, uint *tck) -+{ -+ *tck = FIELD_GET(JTAG_TCK_DIVISOR_MASK, -+ ast_jtag_read(ast_jtag, AST_JTAG_TCK)); -+} -+ -+/* -+ * Used only in SW mode to walk the JTAG state machine. -+ */ -+static u8 tck_cycle(struct ast_jtag_info *ast_jtag, u8 TMS, u8 TDI, -+ bool do_read) -+{ -+ u8 result = 0; -+ u32 regwriteval = JTAG_SW_MODE_EN | (TMS * JTAG_SW_MODE_TMS) -+ | (TDI * JTAG_SW_MODE_TDIO); -+ -+ /* TCK = 0 */ -+ ast_jtag_write(ast_jtag, regwriteval, AST_JTAG_SW); -+ -+ ast_jtag_read(ast_jtag, AST_JTAG_SW); -+ -+ /* TCK = 1 */ -+ ast_jtag_write(ast_jtag, JTAG_SW_MODE_TCK | regwriteval, AST_JTAG_SW); -+ -+ if (do_read) { -+ result = (ast_jtag_read(ast_jtag, AST_JTAG_SW) -+ & JTAG_SW_MODE_TDIO) ? 1 : 0; -+ } -+ return result; -+} -+ -+#define WAIT_ITERATIONS 75 -+ -+static int ast_jtag_wait_instr_pause_complete(struct ast_jtag_info *ast_jtag) -+{ -+ int res = 0; -+#ifdef USE_INTERRUPTS -+ res = wait_event_interruptible(ast_jtag->jtag_wq, -+ (ast_jtag->flag == JTAG_INST_PAUSE)); -+ ast_jtag->flag = 0; -+#else -+ u32 status = 0; -+ u32 iterations = 0; -+ -+ while ((status & JTAG_INST_PAUSE) == 0) { -+ status = ast_jtag_read(ast_jtag, AST_JTAG_ISR); -+ dev_dbg(ast_jtag->dev, "%s = 0x%08x\n", __func__, status); -+ iterations++; -+ if (iterations > WAIT_ITERATIONS) { -+ dev_err(ast_jtag->dev, -+ "ast_jtag driver timed out waiting for instruction pause complete\n"); -+ res = -EFAULT; -+ break; -+ } -+ if ((status & JTAG_DATA_COMPLETE) == 0) { -+ if (iterations % 25 == 0) -+ usleep_range(1, 5); -+ else -+ udelay(1); -+ } -+ } -+ ast_jtag_write(ast_jtag, JTAG_INST_PAUSE | (status & 0xf), -+ AST_JTAG_ISR); -+#endif -+ return res; -+} -+ -+static int ast_jtag_wait_instr_complete(struct ast_jtag_info *ast_jtag) -+{ -+ int res = 0; -+#ifdef USE_INTERRUPTS -+ res = wait_event_interruptible(ast_jtag->jtag_wq, -+ (ast_jtag->flag == JTAG_INST_COMPLETE)); -+ ast_jtag->flag = 0; -+#else -+ u32 status = 0; -+ u32 iterations = 0; -+ -+ while ((status & JTAG_INST_COMPLETE) == 0) { -+ status = ast_jtag_read(ast_jtag, AST_JTAG_ISR); -+ dev_dbg(ast_jtag->dev, "%s = 0x%08x\n", __func__, status); -+ iterations++; -+ if (iterations > WAIT_ITERATIONS) { -+ dev_err(ast_jtag->dev, -+ "ast_jtag driver timed out waiting for instruction complete\n"); -+ res = -EFAULT; -+ break; -+ } -+ if ((status & JTAG_DATA_COMPLETE) == 0) { -+ if (iterations % 25 == 0) -+ usleep_range(1, 5); -+ else -+ udelay(1); -+ } -+ } -+ ast_jtag_write(ast_jtag, JTAG_INST_COMPLETE | (status & 0xf), -+ AST_JTAG_ISR); -+#endif -+ return res; -+} -+ -+static int ast_jtag_wait_data_pause_complete(struct ast_jtag_info *ast_jtag) -+{ -+ int res = 0; -+#ifdef USE_INTERRUPTS -+ res = wait_event_interruptible(ast_jtag->jtag_wq, -+ (ast_jtag->flag == JTAG_DATA_PAUSE)); -+ ast_jtag->flag = 0; -+#else -+ u32 status = 0; -+ u32 iterations = 0; -+ -+ while ((status & JTAG_DATA_PAUSE) == 0) { -+ status = ast_jtag_read(ast_jtag, AST_JTAG_ISR); -+ dev_dbg(ast_jtag->dev, "%s = 0x%08x\n", __func__, status); -+ iterations++; -+ if (iterations > WAIT_ITERATIONS) { -+ dev_err(ast_jtag->dev, -+ "ast_jtag driver timed out waiting for data pause complete\n"); -+ res = -EFAULT; -+ break; -+ } -+ if ((status & JTAG_DATA_COMPLETE) == 0) { -+ if (iterations % 25 == 0) -+ usleep_range(1, 5); -+ else -+ udelay(1); -+ } -+ } -+ ast_jtag_write(ast_jtag, JTAG_DATA_PAUSE | (status & 0xf), -+ AST_JTAG_ISR); -+#endif -+ return res; -+} -+ -+static int ast_jtag_wait_data_complete(struct ast_jtag_info *ast_jtag) -+{ -+ int res = 0; -+#ifdef USE_INTERRUPTS -+ res = wait_event_interruptible(ast_jtag->jtag_wq, -+ (ast_jtag->flag == JTAG_DATA_COMPLETE)); -+ ast_jtag->flag = 0; -+#else -+ u32 status = 0; -+ u32 iterations = 0; -+ -+ while ((status & JTAG_DATA_COMPLETE) == 0) { -+ status = ast_jtag_read(ast_jtag, AST_JTAG_ISR); -+ dev_dbg(ast_jtag->dev, "%s = 0x%08x\n", __func__, status); -+ iterations++; -+ if (iterations > WAIT_ITERATIONS) { -+ dev_err(ast_jtag->dev, -+ "ast_jtag driver timed out waiting for data complete\n"); -+ res = -EFAULT; -+ break; -+ } -+ if ((status & JTAG_DATA_COMPLETE) == 0) { -+ if (iterations % 25 == 0) -+ usleep_range(1, 5); -+ else -+ udelay(1); -+ } -+ } -+ ast_jtag_write(ast_jtag, -+ JTAG_DATA_COMPLETE | (status & 0xf), -+ AST_JTAG_ISR); -+#endif -+ return res; -+} -+ -+static void ast_jtag_bitbang(struct ast_jtag_info *ast_jtag, -+ struct tck_bitbang *bit_bang) -+{ -+ bit_bang->tdo = tck_cycle(ast_jtag, bit_bang->tms, bit_bang->tdi, true); -+} -+ -+static void reset_tap(struct ast_jtag_info *ast_jtag, enum xfer_mode mode) -+{ -+ unsigned char i; -+ -+ if (mode == SW_MODE) { -+ for (i = 0; i < 9; i++) -+ tck_cycle(ast_jtag, 1, 0, false); -+ } else { -+ ast_jtag_write(ast_jtag, 0, AST_JTAG_SW); -+ mdelay(1); -+ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_FORCE_TMS, -+ AST_JTAG_CTRL); -+ mdelay(1); -+ ast_jtag_write(ast_jtag, -+ JTAG_SW_MODE_EN | JTAG_SW_MODE_TDIO, -+ AST_JTAG_SW); -+ } -+} -+ -+static int ast_jtag_set_tapstate(struct ast_jtag_info *ast_jtag, -+ enum xfer_mode mode, uint from, uint to) -+{ -+ unsigned char num_cycles; -+ unsigned char cycle; -+ unsigned char tms_bits; -+ -+ /* -+ * Ensure that the requested and current tap states are within -+ * 0 to 15. -+ */ -+ if (from >= ARRAY_SIZE(_tms_cycle_lookup[0]) || /* Column */ -+ to >= ARRAY_SIZE(_tms_cycle_lookup)) { /* row */ -+ return -1; -+ } -+ -+ dev_dbg(ast_jtag->dev, "Set TAP state: %s\n", c_statestr[to]); -+ -+ if (mode == SW_MODE) { -+ ast_jtag_write(ast_jtag, -+ JTAG_SW_MODE_EN | JTAG_SW_MODE_TDIO, -+ AST_JTAG_SW); -+ -+ if (to == jtag_tlr) { -+ reset_tap(ast_jtag, mode); -+ } else { -+ tms_bits = _tms_cycle_lookup[from][to].tmsbits; -+ num_cycles = _tms_cycle_lookup[from][to].count; -+ -+ if (num_cycles == 0) -+ return 0; -+ -+ for (cycle = 0; cycle < num_cycles; cycle++) { -+ tck_cycle(ast_jtag, (tms_bits & 1), 0, false); -+ tms_bits >>= 1; -+ } -+ } -+ } else if (to == jtag_tlr) { -+ reset_tap(ast_jtag, mode); -+ } -+ return 0; -+} -+ -+static void software_readwrite_scan(struct ast_jtag_info *ast_jtag, -+ struct scan_xfer *scan_xfer) -+{ -+ uint bit_index = 0; -+ bool is_IR = (scan_xfer->tap_state == jtag_shf_ir); -+ uint exit_tap_state = is_IR ? jtag_ex1_ir : jtag_ex1_dr; -+ unsigned char *tdi = scan_xfer->tdi; -+ unsigned char *tdo = scan_xfer->tdo; -+ -+ dev_dbg(ast_jtag->dev, "SW JTAG SHIFT %s, length = %d\n", -+ is_IR ? "IR" : "DR", scan_xfer->length); -+ -+ ast_jtag_write(ast_jtag, -+ JTAG_SW_MODE_EN | JTAG_SW_MODE_TDIO, -+ AST_JTAG_SW); -+ -+ while (bit_index < scan_xfer->length) { -+ int bit_offset = (bit_index % 8); -+ int this_input_bit = 0; -+ int tms_high_or_low; -+ int this_output_bit; -+ -+ if (bit_index / 8 < scan_xfer->tdi_bytes) { -+ /* -+ * If we are on a byte boundary, increment the byte -+ * pointers. Don't increment on 0, pointer is already -+ * on the first byte. -+ */ -+ if (bit_index % 8 == 0 && bit_index != 0) -+ tdi++; -+ this_input_bit = (*tdi >> bit_offset) & 1; -+ } -+ /* If this is the last bit, leave TMS high */ -+ tms_high_or_low = (bit_index == scan_xfer->length - 1) && -+ (scan_xfer->end_tap_state != jtag_shf_dr) && -+ (scan_xfer->end_tap_state != jtag_shf_ir); -+ this_output_bit = tck_cycle(ast_jtag, tms_high_or_low, -+ this_input_bit, !!tdo); -+ /* -+ * If it was the last bit in the scan and the end_tap_state is -+ * something other than shiftDR or shiftIR then go to Exit1. -+ * IMPORTANT Note: if the end_tap_state is ShiftIR/DR and -+ * the next call to this function is a shiftDR/IR then the -+ * driver will not change state! -+ */ -+ if (tms_high_or_low) -+ scan_xfer->tap_state = exit_tap_state; -+ if (tdo && bit_index / 8 < scan_xfer->tdo_bytes) { -+ if (bit_index % 8 == 0) { -+ if (bit_index != 0) -+ tdo++; -+ *tdo = 0; -+ } -+ *tdo |= this_output_bit << bit_offset; -+ } -+ bit_index++; -+ } -+ ast_jtag_set_tapstate(ast_jtag, scan_xfer->mode, scan_xfer->tap_state, -+ scan_xfer->end_tap_state); -+} -+ -+static int fire_ir_command(struct ast_jtag_info *ast_jtag, bool last, -+ u32 length) -+{ -+ int res; -+ -+ if (last) { -+ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_LAST_INST -+ | FIELD_PREP(JTAG_INST_LEN_MASK, length), -+ AST_JTAG_CTRL); -+ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_LAST_INST -+ | FIELD_PREP(JTAG_INST_LEN_MASK, length) -+ | JTAG_INST_EN, -+ AST_JTAG_CTRL); -+ res = ast_jtag_wait_instr_complete(ast_jtag); -+ } else { -+ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_IR_UPDATE -+ | FIELD_PREP(JTAG_INST_LEN_MASK, length), -+ AST_JTAG_CTRL); -+ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_IR_UPDATE -+ | FIELD_PREP(JTAG_INST_LEN_MASK, length) -+ | JTAG_INST_EN, -+ AST_JTAG_CTRL); -+ res = ast_jtag_wait_instr_pause_complete(ast_jtag); -+ } -+ return res; -+} -+ -+static int fire_dr_command(struct ast_jtag_info *ast_jtag, bool last, -+ u32 length) -+{ -+ int res; -+ -+ if (last) { -+ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_LAST_DATA -+ | FIELD_PREP(JTAG_DATA_LEN_MASK, length), -+ AST_JTAG_CTRL); -+ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_LAST_DATA -+ | FIELD_PREP(JTAG_DATA_LEN_MASK, length) -+ | JTAG_DATA_EN, -+ AST_JTAG_CTRL); -+ res = ast_jtag_wait_data_complete(ast_jtag); -+ } else { -+ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_DR_UPDATE -+ | FIELD_PREP(JTAG_DATA_LEN_MASK, length), -+ AST_JTAG_CTRL); -+ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN | JTAG_DR_UPDATE -+ | FIELD_PREP(JTAG_DATA_LEN_MASK, length) -+ | JTAG_DATA_EN, -+ AST_JTAG_CTRL); -+ res = ast_jtag_wait_data_pause_complete(ast_jtag); -+ } -+ return res; -+} -+ -+static int hardware_readwrite_scan(struct ast_jtag_info *ast_jtag, -+ struct scan_xfer *scan_xfer) -+{ -+ int res = 0; -+ u32 bits_received = 0; -+ u32 bits_to_send = 0; -+ u32 chunk_len = 0; -+ bool is_IR = (scan_xfer->tap_state == jtag_shf_ir); -+ bool is_last = false; -+ u32 length = scan_xfer->length; -+ u32 *tdi = (u32 *)scan_xfer->tdi; -+ u32 *tdo = (u32 *)scan_xfer->tdo; -+ u32 remaining_bytes; -+ int scan_end = 0; -+ u32 ast_reg = is_IR ? AST_JTAG_INST : AST_JTAG_DATA; -+ -+ dev_dbg(ast_jtag->dev, "HW JTAG SHIFT %s, length = %d\n", -+ is_IR ? "IR" : "DR", length); -+ -+ ast_jtag_write(ast_jtag, 0, AST_JTAG_SW); -+ if (scan_xfer->end_tap_state == jtag_pau_dr || -+ scan_xfer->end_tap_state == jtag_pau_ir || -+ scan_xfer->end_tap_state == jtag_shf_dr || -+ scan_xfer->end_tap_state == jtag_shf_ir) { -+ scan_end = 0; -+ } else { -+ scan_end = 1; -+ } -+ -+ while (length > 0) { -+ chunk_len = (length > 32) ? 32 : length; -+ -+ if (length <= 32 && scan_end == 1) -+ is_last = true; -+ -+ dev_dbg(ast_jtag->dev, "HW SHIFT, length=%d, scan_end=%d, chunk_len=%d, is_last=%d\n", -+ length, scan_end, chunk_len, is_last); -+ -+ remaining_bytes = (scan_xfer->length - length) / 8; -+ if (tdi && remaining_bytes < scan_xfer->tdi_bytes) { -+ bits_to_send = *tdi++; -+ ast_jtag_write(ast_jtag, bits_to_send, ast_reg); -+ } else { -+ bits_to_send = 0; -+ ast_jtag_write(ast_jtag, 0, ast_reg); -+ } -+ -+ dev_dbg(ast_jtag->dev, "HW SHIFT, len=%d chunk_len=%d is_last=%x bits_to_send=%x\n", -+ length, chunk_len, is_last, bits_to_send); -+ -+ if (is_IR) -+ res = fire_ir_command(ast_jtag, is_last, chunk_len); -+ else -+ res = fire_dr_command(ast_jtag, is_last, chunk_len); -+ if (res != 0) -+ break; -+ -+ if (tdo) { -+ bits_received = ast_jtag_read(ast_jtag, ast_reg); -+ bits_received >>= (32 - chunk_len); -+ *tdo++ = bits_received; -+ } -+ dev_dbg(ast_jtag->dev, -+ "HW SHIFT, len=%d chunk_len=%d is_last=%x bits_received=%x\n", -+ length, chunk_len, is_last, -+ bits_received); -+ length -= chunk_len; -+ } -+ return res; -+} -+ -+static int ast_jtag_readwrite_scan(struct ast_jtag_info *ast_jtag, -+ struct scan_xfer *scan_xfer) -+{ -+ int res = 0; -+ -+ if (scan_xfer->tap_state != jtag_shf_dr && -+ scan_xfer->tap_state != jtag_shf_ir) { -+ if (scan_xfer->tap_state < ARRAY_SIZE(c_statestr)) -+ dev_err(ast_jtag->dev, -+ "readwrite_scan bad current tap state = %s\n", -+ c_statestr[scan_xfer->tap_state]); -+ else -+ dev_err(ast_jtag->dev, -+ "readwrite_scan bad current tap state = %u\n", -+ scan_xfer->tap_state); -+ return -EFAULT; -+ } -+ -+ if (scan_xfer->length == 0) { -+ dev_err(ast_jtag->dev, "readwrite_scan bad length 0\n"); -+ return -EFAULT; -+ } -+ -+ if (!scan_xfer->tdi && scan_xfer->tdi_bytes != 0) { -+ dev_err(ast_jtag->dev, -+ "readwrite_scan null tdi with non-zero length %u!\n", -+ scan_xfer->tdi_bytes); -+ return -EFAULT; -+ } -+ -+ if (!scan_xfer->tdo && scan_xfer->tdo_bytes != 0) { -+ dev_err(ast_jtag->dev, -+ "readwrite_scan null tdo with non-zero length %u!\n", -+ scan_xfer->tdo_bytes); -+ return -EFAULT; -+ } -+ -+ if (!scan_xfer->tdi && !scan_xfer->tdo) { -+ dev_err(ast_jtag->dev, "readwrite_scan null tdo and tdi!\n"); -+ return -EFAULT; -+ } -+ -+ if (scan_xfer->mode == SW_MODE) -+ software_readwrite_scan(ast_jtag, scan_xfer); -+ else -+ res = hardware_readwrite_scan(ast_jtag, scan_xfer); -+ return res; -+} -+ -+#ifdef USE_INTERRUPTS -+static irqreturn_t ast_jtag_interrupt(int this_irq, void *dev_id) -+{ -+ u32 status; -+ struct ast_jtag_info *ast_jtag = dev_id; -+ -+ status = ast_jtag_read(ast_jtag, AST_JTAG_ISR); -+ -+ if (status & JTAG_INST_PAUSE) { -+ ast_jtag_write(ast_jtag, -+ JTAG_INST_PAUSE | (status & 0xf), -+ AST_JTAG_ISR); -+ ast_jtag->flag = JTAG_INST_PAUSE; -+ } -+ -+ if (status & JTAG_INST_COMPLETE) { -+ ast_jtag_write(ast_jtag, -+ JTAG_INST_COMPLETE | (status & 0xf), -+ AST_JTAG_ISR); -+ ast_jtag->flag = JTAG_INST_COMPLETE; -+ } -+ -+ if (status & JTAG_DATA_PAUSE) { -+ ast_jtag_write(ast_jtag, -+ JTAG_DATA_PAUSE | (status & 0xf), AST_JTAG_ISR); -+ ast_jtag->flag = JTAG_DATA_PAUSE; -+ } -+ -+ if (status & JTAG_DATA_COMPLETE) { -+ ast_jtag_write(ast_jtag, -+ JTAG_DATA_COMPLETE | (status & 0xf), -+ AST_JTAG_ISR); -+ ast_jtag->flag = JTAG_DATA_COMPLETE; -+ } -+ -+ if (ast_jtag->flag) { -+ wake_up_interruptible(&ast_jtag->jtag_wq); -+ return IRQ_HANDLED; -+ } else { -+ return IRQ_NONE; -+ } -+} -+#endif -+ -+static inline void ast_jtag_slave(struct ast_jtag_info *ast_jtag) -+{ -+ u32 currReg = readl((void *)(ast_jtag->reg_base_scu)); -+ -+ writel(currReg | SCU_RESET_JTAG, (void *)ast_jtag->reg_base_scu); -+} -+ -+static inline void ast_jtag_master(struct ast_jtag_info *ast_jtag) -+{ -+ u32 currReg = readl((void *)(ast_jtag->reg_base_scu)); -+ -+ writel(currReg & ~SCU_RESET_JTAG, (void *)ast_jtag->reg_base_scu); -+ ast_jtag_write(ast_jtag, JTAG_ENGINE_EN, AST_JTAG_CTRL); -+ ast_jtag_write(ast_jtag, JTAG_SW_MODE_EN | JTAG_SW_MODE_TDIO, -+ AST_JTAG_SW); -+ ast_jtag_write(ast_jtag, JTAG_INST_PAUSE | JTAG_INST_COMPLETE | -+ JTAG_DATA_PAUSE | JTAG_DATA_COMPLETE | -+ JTAG_INST_PAUSE_EN | JTAG_INST_COMPLETE_EN | -+ JTAG_DATA_PAUSE_EN | JTAG_DATA_COMPLETE_EN, -+ AST_JTAG_ISR); /* Enable Interrupt */ -+} -+ -+static long jtag_ioctl(struct file *file, uint cmd, ulong arg) -+{ -+ int ret = 0; -+ struct ast_jtag_info *ast_jtag = file->private_data; -+ void __user *argp = (void __user *)arg; -+ struct tck_bitbang bitbang; -+ struct scan_xfer xfer; -+ struct set_tck_param set_tck_param; -+ struct get_tck_param get_tck_param; -+ struct tap_state_param tap_state_param; -+ unsigned char *kern_tdi = NULL; -+ unsigned char *kern_tdo = NULL; -+ unsigned char *user_tdi; -+ unsigned char *user_tdo; -+ -+ switch (cmd) { -+ case AST_JTAG_SET_TCK: -+ if (copy_from_user(&set_tck_param, argp, -+ sizeof(struct set_tck_param))) { -+ ret = -EFAULT; -+ } else { -+ ast_jtag_set_tck(ast_jtag, -+ set_tck_param.mode, -+ set_tck_param.tck); -+ } -+ break; -+ case AST_JTAG_GET_TCK: -+ if (copy_from_user(&get_tck_param, argp, -+ sizeof(struct get_tck_param))) -+ ret = -EFAULT; -+ else -+ ast_jtag_get_tck(ast_jtag, -+ get_tck_param.mode, -+ &get_tck_param.tck); -+ if (copy_to_user(argp, &get_tck_param, -+ sizeof(struct get_tck_param))) -+ ret = -EFAULT; -+ break; -+ case AST_JTAG_BITBANG: -+ if (copy_from_user(&bitbang, argp, -+ sizeof(struct tck_bitbang))) { -+ ret = -EFAULT; -+ } else { -+ if (bitbang.tms > 1 || bitbang.tdi > 1) -+ ret = -EFAULT; -+ else -+ ast_jtag_bitbang(ast_jtag, &bitbang); -+ } -+ if (copy_to_user(argp, &bitbang, sizeof(struct tck_bitbang))) -+ ret = -EFAULT; -+ break; -+ case AST_JTAG_SET_TAPSTATE: -+ if (copy_from_user(&tap_state_param, argp, -+ sizeof(struct tap_state_param))) -+ ret = -EFAULT; -+ else -+ ast_jtag_set_tapstate(ast_jtag, tap_state_param.mode, -+ tap_state_param.from_state, -+ tap_state_param.to_state); -+ break; -+ case AST_JTAG_READWRITESCAN: -+ if (copy_from_user(&xfer, argp, -+ sizeof(struct scan_xfer))) { -+ ret = -EFAULT; -+ } else { -+ if (xfer.tdi) { -+ user_tdi = xfer.tdi; -+ kern_tdi = memdup_user(user_tdi, -+ xfer.tdi_bytes); -+ if (IS_ERR(kern_tdi)) -+ ret = -EFAULT; -+ else -+ xfer.tdi = kern_tdi; -+ } -+ -+ if (ret == 0 && xfer.tdo) { -+ user_tdo = xfer.tdo; -+ kern_tdo = memdup_user(user_tdo, -+ xfer.tdo_bytes); -+ if (IS_ERR(kern_tdo)) -+ ret = -EFAULT; -+ else -+ xfer.tdo = kern_tdo; -+ } -+ -+ if (ret == 0) -+ ret = ast_jtag_readwrite_scan(ast_jtag, &xfer); -+ -+ kfree(kern_tdi); -+ if (kern_tdo) { -+ if (ret == 0) { -+ if (copy_to_user(user_tdo, -+ xfer.tdo, -+ xfer.tdo_bytes)) -+ ret = -EFAULT; -+ } -+ kfree(kern_tdo); -+ } -+ } -+ break; -+ default: -+ return -ENOTTY; -+ } -+ -+ return ret; -+} -+ -+static int jtag_open(struct inode *inode, struct file *file) -+{ -+ struct ast_jtag_info *ast_jtag = container_of(file->private_data, -+ struct ast_jtag_info, -+ miscdev); -+ -+ spin_lock(&jtag_state_lock); -+ if (ast_jtag->is_open) { -+ spin_unlock(&jtag_state_lock); -+ return -EBUSY; -+ } -+ -+ ast_jtag->is_open = true; -+ file->private_data = ast_jtag; -+ ast_jtag_master(ast_jtag); -+ spin_unlock(&jtag_state_lock); -+ -+ return 0; -+} -+ -+static int jtag_release(struct inode *inode, struct file *file) -+{ -+ struct ast_jtag_info *ast_jtag = file->private_data; -+ -+ spin_lock(&jtag_state_lock); -+ ast_jtag_slave(ast_jtag); -+ ast_jtag->is_open = false; -+ -+ spin_unlock(&jtag_state_lock); -+ -+ return 0; -+} -+ -+static const struct file_operations ast_jtag_fops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = jtag_ioctl, -+ .open = jtag_open, -+ .release = jtag_release, -+}; -+ -+static int ast_jtag_probe(struct platform_device *pdev) -+{ -+ struct resource *scu_res; -+ struct resource *jtag_res; -+ int ret = 0; -+ struct ast_jtag_info *ast_jtag; -+ -+ dev_dbg(&pdev->dev, "%s started\n", __func__); -+ -+ scu_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!scu_res) { -+ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM for SCU\n"); -+ ret = -ENOENT; -+ goto out; -+ } -+ -+ jtag_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); -+ if (!jtag_res) { -+ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM for JTAG\n"); -+ ret = -ENOENT; -+ goto out; -+ } -+ -+ ast_jtag = devm_kzalloc(&pdev->dev, sizeof(*ast_jtag), GFP_KERNEL); -+ if (!ast_jtag) -+ return -ENOMEM; -+ -+ ast_jtag->reg_base_scu = devm_ioremap_resource(&pdev->dev, scu_res); -+ if (!ast_jtag->reg_base_scu) { -+ ret = -EIO; -+ goto out; -+ } -+ -+ ast_jtag->reg_base = devm_ioremap_resource(&pdev->dev, jtag_res); -+ if (!ast_jtag->reg_base) { -+ ret = -EIO; -+ goto out; -+ } -+ -+ ast_jtag->dev = &pdev->dev; -+ -+#ifdef USE_INTERRUPTS -+ ast_jtag->irq = platform_get_irq(pdev, 0); -+ if (ast_jtag->irq < 0) { -+ dev_err(&pdev->dev, "no irq specified.\n"); -+ ret = -ENOENT; -+ goto out; -+ } -+ -+ ret = devm_request_irq(&pdev->dev, ast_jtag->irq, ast_jtag_interrupt, -+ IRQF_SHARED, "ast-jtag", ast_jtag); -+ if (ret) { -+ dev_err(ast_jtag->dev, "JTAG Unable to get IRQ.\n"); -+ goto out; -+ } -+#endif -+ -+ ast_jtag->flag = 0; -+ init_waitqueue_head(&ast_jtag->jtag_wq); -+ -+ ast_jtag->miscdev.minor = MISC_DYNAMIC_MINOR, -+ ast_jtag->miscdev.name = AST_JTAG_NAME, -+ ast_jtag->miscdev.fops = &ast_jtag_fops, -+ ast_jtag->miscdev.parent = &pdev->dev; -+ ret = misc_register(&ast_jtag->miscdev); -+ if (ret) { -+ dev_err(ast_jtag->dev, "Unable to register misc device.\n"); -+ goto out; -+ } -+ -+ platform_set_drvdata(pdev, ast_jtag); -+ -+ ast_jtag_slave(ast_jtag); -+ -+ dev_dbg(&pdev->dev, "%s completed\n", __func__); -+ return 0; -+ -+out: -+ dev_warn(&pdev->dev, "ast_jtag: driver init failed (ret=%d).\n", ret); -+ return ret; -+} -+ -+static int ast_jtag_remove(struct platform_device *pdev) -+{ -+ dev_dbg(&pdev->dev, "%s\n", __func__); -+ -+ platform_set_drvdata(pdev, NULL); -+ -+ dev_dbg(&pdev->dev, "JTAG driver removed successfully.\n"); -+ -+ return 0; -+} -+ -+static const struct of_device_id ast_jtag_of_match[] = { -+ { .compatible = "aspeed,ast2400-jtag", }, -+ { .compatible = "aspeed,ast2500-jtag", }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, ast_jtag_of_match); -+ -+static struct platform_driver ast_jtag_driver = { -+ .probe = ast_jtag_probe, -+ .remove = ast_jtag_remove, -+ .driver = { -+ .name = AST_JTAG_NAME, -+ .of_match_table = of_match_ptr(ast_jtag_of_match), -+ }, -+}; -+module_platform_driver(ast_jtag_driver); -+ -+MODULE_AUTHOR("Ryan Chen "); -+MODULE_AUTHOR("Bryan Hunt "); -+MODULE_DESCRIPTION("ASPEED JTAG driver"); -+MODULE_LICENSE("GPL v2"); diff --git a/include/uapi/linux/jtag_drv.h b/include/uapi/linux/jtag_drv.h new file mode 100644 index 000000000000..4df638f8fa43 diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0029-i2c-aspeed-Improve-driver-to-support-multi-master-us.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0029-i2c-aspeed-Improve-driver-to-support-multi-master-us.patch deleted file mode 100644 index e2dee0d5b..000000000 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0029-i2c-aspeed-Improve-driver-to-support-multi-master-us.patch +++ /dev/null @@ -1,291 +0,0 @@ -From a7ad8d09cdf0ec86612df0714d3e69ee92e6140b Mon Sep 17 00:00:00 2001 -From: Jae Hyun Yoo -Date: Tue, 20 Nov 2018 09:30:17 -0800 -Subject: [PATCH] i2c: aspeed: Improve driver to support multi-master use cases - stably - -In multi-master environment, this driver's master cannot know -exactly when peer master sends data to this driver's slave so -cases can be happened that this master tries to send data through -the master_xfer function but slave data from a peer master is still -being processed or slave xfer is started by a peer very after it -queues a master command. - -To prevent state corruption in these cases, this patch adds the -'pending' state of master and its handling code so that the pending -master xfer can be continued after slave mode session. - -Signed-off-by: Jae Hyun Yoo ---- - drivers/i2c/busses/i2c-aspeed.c | 119 ++++++++++++++++++++++++++++++---------- - 1 file changed, 91 insertions(+), 28 deletions(-) - -diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c -index 8dc9161ced38..d11b2ea97259 100644 ---- a/drivers/i2c/busses/i2c-aspeed.c -+++ b/drivers/i2c/busses/i2c-aspeed.c -@@ -117,6 +117,7 @@ - - enum aspeed_i2c_master_state { - ASPEED_I2C_MASTER_INACTIVE, -+ ASPEED_I2C_MASTER_PENDING, - ASPEED_I2C_MASTER_START, - ASPEED_I2C_MASTER_TX_FIRST, - ASPEED_I2C_MASTER_TX, -@@ -126,12 +127,13 @@ enum aspeed_i2c_master_state { - }; - - enum aspeed_i2c_slave_state { -- ASPEED_I2C_SLAVE_STOP, -+ ASPEED_I2C_SLAVE_INACTIVE, - ASPEED_I2C_SLAVE_START, - ASPEED_I2C_SLAVE_READ_REQUESTED, - ASPEED_I2C_SLAVE_READ_PROCESSED, - ASPEED_I2C_SLAVE_WRITE_REQUESTED, - ASPEED_I2C_SLAVE_WRITE_RECEIVED, -+ ASPEED_I2C_SLAVE_STOP, - }; - - struct aspeed_i2c_bus { -@@ -156,6 +158,8 @@ struct aspeed_i2c_bus { - int cmd_err; - /* Protected only by i2c_lock_bus */ - int master_xfer_result; -+ /* Multi-master */ -+ bool multi_master; - #if IS_ENABLED(CONFIG_I2C_SLAVE) - struct i2c_client *slave; - enum aspeed_i2c_slave_state slave_state; -@@ -251,7 +255,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) - } - - /* Slave is not currently active, irq was for someone else. */ -- if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) -+ if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE) - return irq_handled; - - dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n", -@@ -277,16 +281,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) - irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP; - bus->slave_state = ASPEED_I2C_SLAVE_STOP; - } -- if (irq_status & ASPEED_I2CD_INTR_TX_NAK) { -+ if (irq_status & ASPEED_I2CD_INTR_TX_NAK && -+ bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) { - irq_handled |= ASPEED_I2CD_INTR_TX_NAK; - bus->slave_state = ASPEED_I2C_SLAVE_STOP; - } -- if (irq_status & ASPEED_I2CD_INTR_TX_ACK) -- irq_handled |= ASPEED_I2CD_INTR_TX_ACK; - - switch (bus->slave_state) { - case ASPEED_I2C_SLAVE_READ_REQUESTED: -- if (irq_status & ASPEED_I2CD_INTR_TX_ACK) -+ if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_ACK)) - dev_err(bus->dev, "Unexpected ACK on read request.\n"); - bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED; - i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value); -@@ -294,9 +297,12 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) - writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); - break; - case ASPEED_I2C_SLAVE_READ_PROCESSED: -- if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK)) -+ if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { - dev_err(bus->dev, - "Expected ACK after processed read.\n"); -+ break; -+ } -+ irq_handled |= ASPEED_I2CD_INTR_TX_ACK; - i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value); - writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG); - writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); -@@ -310,10 +316,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) - break; - case ASPEED_I2C_SLAVE_STOP: - i2c_slave_event(slave, I2C_SLAVE_STOP, &value); -+ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; -+ break; -+ case ASPEED_I2C_SLAVE_START: -+ /* Slave was just started. Waiting for the next event. */; - break; - default: -- dev_err(bus->dev, "unhandled slave_state: %d\n", -+ dev_err(bus->dev, "unknown slave_state: %d\n", - bus->slave_state); -+ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; - break; - } - -@@ -328,7 +339,17 @@ 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 idle state. -+ */ -+ if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) -+ bus->master_state = ASPEED_I2C_MASTER_PENDING; -+ else -+#endif /* CONFIG_I2C_SLAVE */ -+ bus->master_state = ASPEED_I2C_MASTER_START; - bus->buf_index = 0; - - if (msg->flags & I2C_M_RD) { -@@ -384,10 +405,6 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) - bus->master_state = ASPEED_I2C_MASTER_INACTIVE; - irq_handled |= ASPEED_I2CD_INTR_BUS_RECOVER_DONE; - goto out_complete; -- } else { -- /* Master is not currently active, irq was for someone else. */ -- if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE) -- goto out_no_complete; - } - - /* -@@ -399,12 +416,33 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) - if (ret) { - dev_dbg(bus->dev, "received error interrupt: 0x%08x\n", - irq_status); -- bus->cmd_err = ret; -- bus->master_state = ASPEED_I2C_MASTER_INACTIVE; - irq_handled |= (irq_status & ASPEED_I2CD_INTR_MASTER_ERRORS); -- goto out_complete; -+ if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { -+ bus->cmd_err = ret; -+ bus->master_state = ASPEED_I2C_MASTER_INACTIVE; -+ goto out_complete; -+ } - } - -+#if IS_ENABLED(CONFIG_I2C_SLAVE) -+ /* -+ * A pending master command will be started by H/W when the bus comes -+ * back to idle state after completing a slave operation so change the -+ * master state from 'pending' to 'start' at here if slave is inactive. -+ */ -+ if (bus->master_state == ASPEED_I2C_MASTER_PENDING) { -+ if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) -+ goto out_no_complete; -+ -+ bus->master_state = ASPEED_I2C_MASTER_START; -+ } -+#endif /* CONFIG_I2C_SLAVE */ -+ -+ /* Master is not currently active, irq was for someone else. */ -+ if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE || -+ bus->master_state == ASPEED_I2C_MASTER_PENDING) -+ goto out_no_complete; -+ - /* We are in an invalid state; reset bus to a known state. */ - if (!bus->msgs) { - dev_err(bus->dev, "bus in unknown state. irq_status: 0x%x\n", -@@ -423,6 +461,20 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) - * then update the state and handle the new state below. - */ - if (bus->master_state == ASPEED_I2C_MASTER_START) { -+#if IS_ENABLED(CONFIG_I2C_SLAVE) -+ /* -+ * If a peer master starts a xfer very after it queues a master -+ * command, change its state to 'pending' then H/W will continue -+ * the queued master xfer just after completing the slave mode -+ * session. -+ */ -+ if (unlikely(irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH)) { -+ bus->master_state = ASPEED_I2C_MASTER_PENDING; -+ dev_dbg(bus->dev, -+ "master goes pending due to a slave start\n"); -+ goto out_no_complete; -+ } -+#endif /* CONFIG_I2C_SLAVE */ - if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { - if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_NAK))) { - bus->cmd_err = -ENXIO; -@@ -566,7 +618,8 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id) - * interrupt bits. Each case needs to be handled using corresponding - * handlers depending on the current state. - */ -- if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { -+ if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE && -+ bus->master_state != ASPEED_I2C_MASTER_PENDING) { - irq_handled = aspeed_i2c_master_irq(bus, irq_remaining); - irq_remaining &= ~irq_handled; - if (irq_remaining) -@@ -601,15 +654,14 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, - { - struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap); - unsigned long time_left, flags; -- int ret = 0; -+ int ret; - - spin_lock_irqsave(&bus->lock, flags); - bus->cmd_err = 0; - -- /* If bus is busy, attempt recovery. We assume a single master -- * environment. -- */ -- if (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS) { -+ /* If bus is busy in a single master environment, attempt recovery. */ -+ if (!bus->multi_master && -+ (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS)) { - spin_unlock_irqrestore(&bus->lock, flags); - ret = aspeed_i2c_recover_bus(bus); - if (ret) -@@ -629,10 +681,20 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, - time_left = wait_for_completion_timeout(&bus->cmd_complete, - bus->adap.timeout); - -- if (time_left == 0) -+ if (time_left == 0) { -+ /* -+ * If timed out and bus is still busy in a multi master -+ * environment, attempt recovery at here. -+ */ -+ if (bus->multi_master && -+ (readl(bus->base + ASPEED_I2C_CMD_REG) & -+ ASPEED_I2CD_BUS_BUSY_STS)) -+ ret = aspeed_i2c_recover_bus(bus); -+ - return -ETIMEDOUT; -- else -- return bus->master_xfer_result; -+ } -+ -+ return bus->master_xfer_result; - } - - static u32 aspeed_i2c_functionality(struct i2c_adapter *adap) -@@ -672,7 +734,7 @@ static int aspeed_i2c_reg_slave(struct i2c_client *client) - __aspeed_i2c_reg_slave(bus, client->addr); - - bus->slave = client; -- bus->slave_state = ASPEED_I2C_SLAVE_STOP; -+ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; - spin_unlock_irqrestore(&bus->lock, flags); - - return 0; -@@ -827,7 +889,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus, - if (ret < 0) - return ret; - -- if (!of_property_read_bool(pdev->dev.of_node, "multi-master")) -+ if (of_property_read_bool(pdev->dev.of_node, "multi-master")) -+ bus->multi_master = true; -+ else - fun_ctrl_reg |= ASPEED_I2CD_MULTI_MASTER_DIS; - - /* Enable Master Mode */ -@@ -930,7 +994,6 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) - init_completion(&bus->cmd_complete); - bus->adap.owner = THIS_MODULE; - bus->adap.retries = 0; -- bus->adap.timeout = 5 * HZ; - bus->adap.algo = &aspeed_i2c_algo; - bus->adap.dev.parent = &pdev->dev; - bus->adap.dev.of_node = pdev->dev.of_node; --- -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 index b735ab38b..e984623ba 100644 --- 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 @@ -1,4 +1,4 @@ -From 577b65960842f4098cdfc85a311261477c051d84 Mon Sep 17 00:00:00 2001 +From f4c13be7b95899d6eca3fa7ae38224bc9c7d7902 Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Fri, 29 Jun 2018 11:00:02 -0700 Subject: [PATCH] Add dump debug code into I2C drivers @@ -16,11 +16,11 @@ echo 1 > /sys/module/i2c_slave_mqueue/parameters/dump_debug Signed-off-by: Jae Hyun Yoo --- drivers/i2c/busses/i2c-aspeed.c | 25 +++++++++++++++++++++++++ - drivers/i2c/i2c-slave-mqueue.c | 24 ++++++++++++++++++++++++ - 2 files changed, 49 insertions(+) + 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 d11b2ea97259..506d867b43d9 100644 +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 { @@ -44,14 +44,14 @@ index d11b2ea97259..506d867b43d9 100644 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 ret; + int i; spin_lock_irqsave(&bus->lock, flags); bus->cmd_err = 0; -@@ -694,6 +708,11 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, +@@ -697,6 +711,11 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, return -ETIMEDOUT; } @@ -63,7 +63,7 @@ index d11b2ea97259..506d867b43d9 100644 return bus->master_xfer_result; } -@@ -1061,6 +1080,12 @@ static struct platform_driver aspeed_i2c_bus_driver = { +@@ -1064,6 +1083,12 @@ static struct platform_driver aspeed_i2c_bus_driver = { }; module_platform_driver(aspeed_i2c_bus_driver); @@ -77,7 +77,7 @@ index d11b2ea97259..506d867b43d9 100644 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..0140c0dc4c03 100644 +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 { @@ -88,7 +88,7 @@ index 6014bca0ff2a..0140c0dc4c03 100644 spinlock_t lock; /* spinlock for queue index handling */ int in; -@@ -31,6 +32,19 @@ struct mq_queue { +@@ -31,6 +32,18 @@ struct mq_queue { struct mq_msg *queue; }; @@ -99,8 +99,7 @@ index 6014bca0ff2a..0140c0dc4c03 100644 + 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, addr:0x%02x): ", \ -+ __func__, client->adapter->nr, client->addr); \ ++ "%s (bus_id:%d): ", __func__, client->adapter->nr); \ + print_hex_dump(KERN_ERR, dump_info, DUMP_PREFIX_NONE, 16, 1, \ + buf, len, true); \ + } @@ -108,15 +107,7 @@ index 6014bca0ff2a..0140c0dc4c03 100644 static int i2c_slave_mqueue_callback(struct i2c_client *client, enum i2c_slave_event event, u8 *val) { -@@ -49,6 +63,7 @@ static int i2c_slave_mqueue_callback(struct i2c_client *client, - case I2C_SLAVE_WRITE_RECEIVED: - if (msg->len < MQ_MSGBUF_SIZE) { - msg->buf[msg->len++] = *val; -+ I2C_HEX_DUMP(client, val, 1); - } else { - dev_err(&client->dev, "message is truncated!\n"); - mq->truncated = 1; -@@ -101,6 +116,7 @@ static ssize_t i2c_slave_mqueue_bin_read(struct file *filp, +@@ -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); @@ -124,7 +115,7 @@ index 6014bca0ff2a..0140c0dc4c03 100644 } else { ret = -EOVERFLOW; /* Drop this HUGE one. */ } -@@ -131,6 +147,8 @@ static int i2c_slave_mqueue_probe(struct i2c_client *client, +@@ -131,6 +145,8 @@ static int i2c_slave_mqueue_probe(struct i2c_client *client, BUILD_BUG_ON(!is_power_of_2(MQ_QUEUE_SIZE)); @@ -133,7 +124,7 @@ index 6014bca0ff2a..0140c0dc4c03 100644 buf = devm_kmalloc_array(dev, MQ_QUEUE_SIZE, MQ_MSGBUF_SIZE, GFP_KERNEL); if (!buf) -@@ -212,6 +230,12 @@ static struct i2c_driver i2c_slave_mqueue_driver = { +@@ -212,6 +228,12 @@ static struct i2c_driver i2c_slave_mqueue_driver = { }; module_i2c_driver(i2c_slave_mqueue_driver); 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 index 3863ea8f6..bb6465023 100644 --- 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 @@ -69,14 +69,14 @@ diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 8f70b888a9ca..30ee065491ef 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile -@@ -59,6 +59,7 @@ 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 +@@ -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_PCI_ENDPOINT_TEST) += pci_endpoint_test.o - obj-$(CONFIG_OCXL) += ocxl/ - obj-y += cardreader/ + 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 diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0038-media-aspeed-backport-ikvm-patches.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0038-media-aspeed-backport-ikvm-patches.patch deleted file mode 100644 index 92d0a045d..000000000 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0038-media-aspeed-backport-ikvm-patches.patch +++ /dev/null @@ -1,174 +0,0 @@ -From 13d6fd0f71b3d0d69370878613bf7eb78fefa18f Mon Sep 17 00:00:00 2001 -From: Jae Hyun Yoo -Date: Fri, 9 Nov 2018 11:32:27 -0800 -Subject: [PATCH] Add Aspeed Video Engine Driver - -media: platform: Fix missing spin_lock_init() - -The driver allocates the spinlock but not initialize it. -Use spin_lock_init() on it to initialize it correctly. - -This is detected by Coccinelle semantic patch. - -Fixes: d2b4387f3bdf ("media: platform: Add Aspeed Video Engine driver") - -Signed-off-by: Eddie James -Signed-off-by: Wei Yongjun -Reviewed-by: Rob Herring -Signed-off-by: Hans Verkuil -Signed-off-by: Mauro Carvalho Chehab -Signed-off-by: Jae Hyun Yoo ---- - arch/arm/boot/dts/aspeed-g5.dtsi | 11 +++++++++ - drivers/clk/clk-aspeed.c | 41 ++++++++++++++++++++++++++++++-- - drivers/media/platform/aspeed-video.c | 1 + - include/dt-bindings/clock/aspeed-clock.h | 1 + - 4 files changed, 52 insertions(+), 2 deletions(-) - -diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi -index 6f26e0d323d6..d5783eaf30ae 100644 ---- a/arch/arm/boot/dts/aspeed-g5.dtsi -+++ b/arch/arm/boot/dts/aspeed-g5.dtsi -@@ -243,6 +243,17 @@ - interrupts = <0x19>; - }; - -+ video: video@1e700000 { -+ compatible = "aspeed,ast2500-video-engine"; -+ reg = <0x1e700000 0x20000>; -+ clocks = <&syscon ASPEED_CLK_GATE_VCLK>, -+ <&syscon ASPEED_CLK_GATE_ECLK>; -+ clock-names = "vclk", "eclk"; -+ resets = <&syscon ASPEED_RESET_VIDEO>; -+ interrupts = <7>; -+ status = "disabled"; -+ }; -+ - adc: adc@1e6e9000 { - compatible = "aspeed,ast2500-adc"; - reg = <0x1e6e9000 0xb0>; -diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c -index 3bbb4fbf00c9..6cea55de485f 100644 ---- a/drivers/clk/clk-aspeed.c -+++ b/drivers/clk/clk-aspeed.c -@@ -95,7 +95,7 @@ struct aspeed_clk_gate { - /* TODO: ask Aspeed about the actual parent data */ - static const struct aspeed_gate_data aspeed_gates[] = { - /* clk rst name parent flags */ -- [ASPEED_CLK_GATE_ECLK] = { 0, -1, "eclk-gate", "eclk", 0 }, /* Video Engine */ -+ [ASPEED_CLK_GATE_ECLK] = { 0, 6, "eclk-gate", "eclk", 0 }, /* Video Engine */ - [ASPEED_CLK_GATE_GCLK] = { 1, 7, "gclk-gate", NULL, 0 }, /* 2D engine */ - [ASPEED_CLK_GATE_MCLK] = { 2, -1, "mclk-gate", "mpll", CLK_IS_CRITICAL }, /* SDRAM */ - [ASPEED_CLK_GATE_VCLK] = { 3, 6, "vclk-gate", NULL, 0 }, /* Video Capture */ -@@ -121,6 +121,24 @@ static const struct aspeed_gate_data aspeed_gates[] = { - [ASPEED_CLK_GATE_LHCCLK] = { 28, -1, "lhclk-gate", "lhclk", 0 }, /* LPC master/LPC+ */ - }; - -+static const char * const eclk_parent_names[] = { -+ "mpll", -+ "hpll", -+ "dpll", -+}; -+ -+static const struct clk_div_table ast2500_eclk_div_table[] = { -+ { 0x0, 2 }, -+ { 0x1, 2 }, -+ { 0x2, 3 }, -+ { 0x3, 4 }, -+ { 0x4, 5 }, -+ { 0x5, 6 }, -+ { 0x6, 7 }, -+ { 0x7, 8 }, -+ { 0 } -+}; -+ - static const struct clk_div_table ast2500_mac_div_table[] = { - { 0x0, 4 }, /* Yep, really. Aspeed confirmed this is correct */ - { 0x1, 4 }, -@@ -200,18 +218,21 @@ static struct clk_hw *aspeed_ast2500_calc_pll(const char *name, u32 val) - - struct aspeed_clk_soc_data { - const struct clk_div_table *div_table; -+ const struct clk_div_table *eclk_div_table; - const struct clk_div_table *mac_div_table; - struct clk_hw *(*calc_pll)(const char *name, u32 val); - }; - - static const struct aspeed_clk_soc_data ast2500_data = { - .div_table = ast2500_div_table, -+ .eclk_div_table = ast2500_eclk_div_table, - .mac_div_table = ast2500_mac_div_table, - .calc_pll = aspeed_ast2500_calc_pll, - }; - - static const struct aspeed_clk_soc_data ast2400_data = { - .div_table = ast2400_div_table, -+ .eclk_div_table = ast2400_div_table, - .mac_div_table = ast2400_div_table, - .calc_pll = aspeed_ast2400_calc_pll, - }; -@@ -325,6 +346,7 @@ static const u8 aspeed_resets[] = { - [ASPEED_RESET_PECI] = 10, - [ASPEED_RESET_I2C] = 2, - [ASPEED_RESET_AHB] = 1, -+ [ASPEED_RESET_VIDEO] = 6, - - /* - * SCUD4 resets start at an offset to separate them from -@@ -538,6 +560,22 @@ static int aspeed_clk_probe(struct platform_device *pdev) - return PTR_ERR(hw); - aspeed_clk_data->hws[ASPEED_CLK_24M] = hw; - -+ hw = clk_hw_register_mux(dev, "eclk-mux", eclk_parent_names, -+ ARRAY_SIZE(eclk_parent_names), 0, -+ scu_base + ASPEED_CLK_SELECTION, 2, 0x3, 0, -+ &aspeed_clk_lock); -+ if (IS_ERR(hw)) -+ return PTR_ERR(hw); -+ aspeed_clk_data->hws[ASPEED_CLK_ECLK_MUX] = hw; -+ -+ hw = clk_hw_register_divider_table(dev, "eclk", "eclk-mux", 0, -+ scu_base + ASPEED_CLK_SELECTION, 28, -+ 3, 0, soc_data->eclk_div_table, -+ &aspeed_clk_lock); -+ if (IS_ERR(hw)) -+ return PTR_ERR(hw); -+ aspeed_clk_data->hws[ASPEED_CLK_ECLK] = hw; -+ - /* - * TODO: There are a number of clocks that not included in this driver - * as more information is required: -@@ -547,7 +585,6 @@ static int aspeed_clk_probe(struct platform_device *pdev) - * RGMII - * RMII - * UART[1..5] clock source mux -- * Video Engine (ECLK) mux and clock divider - */ - - /* Get the uart clock source configuration from SCU4C*/ -diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c -index dfec813f50a9..692e08ef38c0 100644 ---- a/drivers/media/platform/aspeed-video.c -+++ b/drivers/media/platform/aspeed-video.c -@@ -1661,6 +1661,7 @@ static int aspeed_video_probe(struct platform_device *pdev) - - video->frame_rate = 30; - video->dev = &pdev->dev; -+ spin_lock_init(&video->lock); - mutex_init(&video->video_lock); - init_waitqueue_head(&video->wait); - INIT_DELAYED_WORK(&video->res_work, aspeed_video_resolution_work); -diff --git a/include/dt-bindings/clock/aspeed-clock.h b/include/dt-bindings/clock/aspeed-clock.h -index 335879505a72..0b0f3a0ebe9b 100644 ---- a/include/dt-bindings/clock/aspeed-clock.h -+++ b/include/dt-bindings/clock/aspeed-clock.h -@@ -52,5 +52,6 @@ - #define ASPEED_RESET_I2C 7 - #define ASPEED_RESET_AHB 8 - #define ASPEED_RESET_CRT1 9 -+#define ASPEED_RESET_VIDEO 10 - - #endif --- -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 index 02ca65e9f..3e8f86666 100644 --- 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 @@ -1,4 +1,4 @@ -From 95bae3d3051ee13627e5ef92bb9d60cfb5731118 Mon Sep 17 00:00:00 2001 +From ef2e1d9d2e8c97daf806f4da74738a84de054116 Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Mon, 11 Feb 2019 17:02:35 -0800 Subject: [PATCH] Add Aspeed PWM driver which uses FTTMR010 timer IP @@ -10,21 +10,18 @@ structure changes only for Aspeed SoCs. Signed-off-by: Jae Hyun Yoo --- - arch/arm/boot/dts/aspeed-g5.dtsi | 2 +- - drivers/clocksource/timer-fttmr010.c | 25 ++ - drivers/pwm/Kconfig | 9 + - drivers/pwm/Makefile | 1 + - drivers/pwm/pwm-fttmr010.c | 465 +++++++++++++++++++++++++++++++++++ - include/clocksource/timer-fttmr010.h | 17 ++ - 6 files changed, 514 insertions(+), 5 deletions(-) + 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 - create mode 100644 include/clocksource/timer-fttmr010.h diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi -index d5783eaf30ae..992de63d7a19 100644 +index 8a7c4257b917..c24197232385 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi -@@ -301,7 +301,7 @@ +@@ -300,7 +300,7 @@ timer: timer@1e782000 { /* This timer is a Faraday FTTMR010 derivative */ @@ -33,109 +30,6 @@ index d5783eaf30ae..992de63d7a19 100644 reg = <0x1e782000 0x90>; interrupts = <16 17 18 35 36 37 38 39>; clocks = <&syscon ASPEED_CLK_APB>; -diff --git a/drivers/clocksource/timer-fttmr010.c b/drivers/clocksource/timer-fttmr010.c -index fadff7915dd9..49a790924360 100644 ---- a/drivers/clocksource/timer-fttmr010.c -+++ b/drivers/clocksource/timer-fttmr010.c -@@ -20,6 +20,8 @@ - #include - #include - -+#include -+ - /* - * Register definitions common for all the timer variants. - */ -@@ -91,6 +93,9 @@ - #define TIMER_3_INT_OVERFLOW BIT(8) - #define TIMER_INT_ALL_MASK 0x1ff - -+DEFINE_SPINLOCK(timer_fttmr010_lock); -+EXPORT_SYMBOL(timer_fttmr010_lock); -+ - struct fttmr010 { - void __iomem *base; - unsigned int tick_rate; -@@ -137,8 +142,11 @@ static int fttmr010_timer_set_next_event(unsigned long cycles, - struct clock_event_device *evt) - { - struct fttmr010 *fttmr010 = to_fttmr010(evt); -+ unsigned long flags; - u32 cr; - -+ spin_lock_irqsave(&timer_fttmr010_lock, flags); -+ - /* Stop */ - cr = readl(fttmr010->base + TIMER_CR); - cr &= ~fttmr010->t1_enable_val; -@@ -161,27 +169,37 @@ static int fttmr010_timer_set_next_event(unsigned long cycles, - cr |= fttmr010->t1_enable_val; - writel(cr, fttmr010->base + TIMER_CR); - -+ spin_unlock_irqrestore(&timer_fttmr010_lock, flags); -+ - return 0; - } - - static int fttmr010_timer_shutdown(struct clock_event_device *evt) - { - struct fttmr010 *fttmr010 = to_fttmr010(evt); -+ unsigned long flags; - u32 cr; - -+ spin_lock_irqsave(&timer_fttmr010_lock, flags); -+ - /* Stop */ - cr = readl(fttmr010->base + TIMER_CR); - cr &= ~fttmr010->t1_enable_val; - writel(cr, fttmr010->base + TIMER_CR); - -+ spin_unlock_irqrestore(&timer_fttmr010_lock, flags); -+ - return 0; - } - - static int fttmr010_timer_set_oneshot(struct clock_event_device *evt) - { - struct fttmr010 *fttmr010 = to_fttmr010(evt); -+ unsigned long flags; - u32 cr; - -+ spin_lock_irqsave(&timer_fttmr010_lock, flags); -+ - /* Stop */ - cr = readl(fttmr010->base + TIMER_CR); - cr &= ~fttmr010->t1_enable_val; -@@ -201,6 +219,8 @@ static int fttmr010_timer_set_oneshot(struct clock_event_device *evt) - writel(cr, fttmr010->base + TIMER_INTR_MASK); - } - -+ spin_unlock_irqrestore(&timer_fttmr010_lock, flags); -+ - return 0; - } - -@@ -208,8 +228,11 @@ static int fttmr010_timer_set_periodic(struct clock_event_device *evt) - { - struct fttmr010 *fttmr010 = to_fttmr010(evt); - u32 period = DIV_ROUND_CLOSEST(fttmr010->tick_rate, HZ); -+ unsigned long flags; - u32 cr; - -+ spin_lock_irqsave(&timer_fttmr010_lock, flags); -+ - /* Stop */ - cr = readl(fttmr010->base + TIMER_CR); - cr &= ~fttmr010->t1_enable_val; -@@ -235,6 +258,8 @@ static int fttmr010_timer_set_periodic(struct clock_event_device *evt) - cr |= fttmr010->t1_enable_val; - writel(cr, fttmr010->base + TIMER_CR); - -+ spin_unlock_irqrestore(&timer_fttmr010_lock, flags); -+ - return 0; - } - diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index a8f47df0655a..92a8fbebe2d9 100644 --- a/drivers/pwm/Kconfig @@ -170,10 +64,10 @@ index 9c676a0dadf5..13b7b20ad5ab 100644 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..459ace3eba6a +index 000000000000..32e508c962dc --- /dev/null +++ b/drivers/pwm/pwm-fttmr010.c -@@ -0,0 +1,465 @@ +@@ -0,0 +1,437 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Intel Corporation + @@ -185,9 +79,6 @@ index 000000000000..459ace3eba6a +#include +#include + -+/* For timer_fttmr010_lock */ -+#include -+ +#define TIMER_CR 0x30 + +#define TIMER5_ASPEED_COUNT 0x50 @@ -265,21 +156,6 @@ index 000000000000..459ace3eba6a + u32 clk_tick_ns; +}; + -+#if !defined(CONFIG_FTTMR010_TIMER) -+/* -+ * Timer block is shared between timer-fttmr010 and pwm-fttmr010 drivers -+ * and some registers need access synchronization. If both drivers are -+ * compiled in, the spinlock is defined in the clocksource driver, -+ * otherwise following definition is used. -+ * -+ * Currently we do not need any more complex synchronization method -+ * because all the supported SoCs contain only one instance of the Timer -+ * IP. Once this changes, both drivers will need to be modified to -+ * properly synchronize accesses to particular instances. -+ */ -+static DEFINE_SPINLOCK(timer_fttmr010_lock); -+#endif -+ +static inline +struct pwm_fttmr010 *to_pwm_fttmr010(struct pwm_chip *chip) +{ @@ -319,8 +195,6 @@ index 000000000000..459ace3eba6a + ulong flags; + u32 cr; + -+ spin_lock_irqsave(&timer_fttmr010_lock, flags); -+ + cr = readl(priv->base + TIMER_CR); + + switch (pwm->hwpwm) { @@ -339,9 +213,6 @@ index 000000000000..459ace3eba6a + } + + writel(cr, priv->base + TIMER_CR); -+ -+ spin_unlock_irqrestore(&timer_fttmr010_lock, flags); -+ + priv->disabled_mask &= ~BIT(pwm->hwpwm); + + return 0; @@ -353,8 +224,6 @@ index 000000000000..459ace3eba6a + ulong flags; + u32 cr; + -+ spin_lock_irqsave(&timer_fttmr010_lock, flags); -+ + cr = readl(priv->base + TIMER_CR); + + switch (pwm->hwpwm) { @@ -373,9 +242,6 @@ index 000000000000..459ace3eba6a + } + + writel(cr, priv->base + TIMER_CR); -+ -+ spin_unlock_irqrestore(&timer_fttmr010_lock, flags); -+ + priv->disabled_mask |= BIT(pwm->hwpwm); +} + @@ -639,29 +505,6 @@ index 000000000000..459ace3eba6a +MODULE_AUTHOR("Jae Hyun Yoo "); +MODULE_DESCRIPTION("FTTMR010 PWM Driver for timer pulse outputs"); +MODULE_LICENSE("GPL v2"); -diff --git a/include/clocksource/timer-fttmr010.h b/include/clocksource/timer-fttmr010.h -new file mode 100644 -index 000000000000..d8d6a2f14130 ---- /dev/null -+++ b/include/clocksource/timer-fttmr010.h -@@ -0,0 +1,17 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+ -+#ifndef __CLOCKSOURCE_TIMER_FTTMR010_H -+#define __CLOCKSOURCE_TIMER_FTTMR010_H -+ -+#include -+ -+/* -+ * Following declaration must be in an ifdef due to this symbol being static -+ * in timer-fttmr010 driver if the clocksource driver is not compiled in and the -+ * spinlock is not shared between both drivers. -+ */ -+#ifdef CONFIG_FTTMR010_TIMER -+extern spinlock_t timer_fttmr010_lock; -+#endif -+ -+#endif /* __CLOCKSOURCE_TIMER_FTTMR010_H */ -- 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 index 9aee6f0c0..7a4b090ec 100644 --- 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 @@ -198,10 +198,10 @@ index d1adfdf50fb3..4f9fdd25c6d7 100644 + val = !!val; + if (test_bit(FLAG_PASS_THROUGH, &desc->flags)) { + if (val) -+ gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc), ++ gpio_set_config(gc, gpio_chip_hwgpio(desc), + PIN_CONFIG_PASS_THROUGH_ENABLE); + else -+ gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc), ++ gpio_set_config(gc, gpio_chip_hwgpio(desc), + PIN_CONFIG_PASS_THROUGH_DISABLE); + } else { + gpiod_err(desc, diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0050-media-platform-Fix-a-kernel-warning-on-clk-control.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0050-media-platform-Fix-a-kernel-warning-on-clk-control.patch deleted file mode 100644 index a0168c889..000000000 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0050-media-platform-Fix-a-kernel-warning-on-clk-control.patch +++ /dev/null @@ -1,177 +0,0 @@ -From 1775e41d085b24a672dc271d08bfc83401288f0b Mon Sep 17 00:00:00 2001 -From: Jae Hyun Yoo -Date: Fri, 22 Mar 2019 16:34:54 -0700 -Subject: [PATCH] media: platform: Fix a kernel warning on clk control - -Video engine clock control functions in the Aspeed video engine driver are -being called from multiple context without any protection so video clocks -can be disabled twice and eventually it causes a kernel warning with stack -dump printing out like below: - -[ 120.034729] WARNING: CPU: 0 PID: 1334 at drivers/clk/clk.c:684 clk_core_unprepare+0x13c/0x170 -[ 120.043252] eclk-gate already unprepared -[ 120.047283] CPU: 0 PID: 1334 Comm: obmc-ikvm Tainted: G W 5.0.3-b94b74e8b52db91fe4e99e0bb481ec8bf2b5b47c #1 -[ 120.058417] Hardware name: Generic DT based system -[ 120.063219] Backtrace: -[ 120.065787] [<80107cdc>] (dump_backtrace) from [<80107f10>] (show_stack+0x20/0x24) -[ 120.073371] r7:803a4ff0 r6:00000009 r5:00000000 r4:96197e1c -[ 120.079152] [<80107ef0>] (show_stack) from [<8068f7d8>] (dump_stack+0x20/0x28) -[ 120.086479] [<8068f7b8>] (dump_stack) from [<8011604c>] (__warn.part.3+0xb4/0xdc) -[ 120.094068] [<80115f98>] (__warn.part.3) from [<801160e0>] (warn_slowpath_fmt+0x6c/0x90) -[ 120.102164] r6:000002ac r5:8080c0b8 r4:80a07008 -[ 120.106893] [<80116078>] (warn_slowpath_fmt) from [<803a4ff0>] (clk_core_unprepare+0x13c/0x170) -[ 120.115686] r3:8080cf8c r2:8080c17c -[ 120.119276] r7:97d68e58 r6:9df23200 r5:9668c260 r4:96459260 -[ 120.125046] [<803a4eb4>] (clk_core_unprepare) from [<803a707c>] (clk_unprepare+0x34/0x3c) -[ 120.133226] r5:9668c260 r4:96459260 -[ 120.136932] [<803a7048>] (clk_unprepare) from [<804f34bc>] (aspeed_video_off+0x44/0x48) -[ 120.145031] r5:9668c260 r4:9668cbc0 -[ 120.148647] [<804f3478>] (aspeed_video_off) from [<804f3fd0>] (aspeed_video_release+0x94/0x118) -[ 120.157435] r5:966a0cb8 r4:966a0800 -[ 120.161049] [<804f3f3c>] (aspeed_video_release) from [<804d2c58>] (v4l2_release+0xd4/0xe8) -[ 120.169404] r7:97d68e58 r6:9d087810 r5:9df23200 r4:966a0b20 -[ 120.175168] [<804d2b84>] (v4l2_release) from [<80236224>] (__fput+0x98/0x1c4) -[ 120.182316] r5:96698e78 r4:9df23200 -[ 120.185994] [<8023618c>] (__fput) from [<802363b8>] (____fput+0x18/0x1c) -[ 120.192712] r9:80a0700c r8:801011e4 r7:00000000 r6:80a64bbc r5:961dd560 r4:961dd89c -[ 120.200562] [<802363a0>] (____fput) from [<80131c08>] (task_work_run+0x7c/0xa4) -[ 120.207994] [<80131b8c>] (task_work_run) from [<80106884>] (do_work_pending+0x4a8/0x578) -[ 120.216163] r7:801011e4 r6:80a07008 r5:96197fb0 r4:ffffe000 -[ 120.221856] [<801063dc>] (do_work_pending) from [<8010106c>] (slow_work_pending+0xc/0x20) -[ 120.230116] Exception stack(0x96197fb0 to 0x96197ff8) -[ 120.235254] 7fa0: 00000000 76ccf094 00000000 00000000 -[ 120.243438] 7fc0: 00000008 00a11978 7eab3c30 00000006 00000000 00000000 475b0fa4 00000000 -[ 120.251692] 7fe0: 00000002 7eab3a40 00000000 47720e38 80000010 00000008 -[ 120.258396] r10:00000000 r9:96196000 r8:801011e4 r7:00000006 r6:7eab3c30 r5:00a11978 -[ 120.266291] r4:00000008 - -To prevent this issue, this commit adds spinlock protection and clock -status checking logic into the Aspeed video engine driver. - -Fixes: d2b4387f3bdf ("media: platform: Add Aspeed Video Engine driver") -Signed-off-by: Jae Hyun Yoo -Cc: Eddie James -Cc: Mauro Carvalho Chehab -Cc: Joel Stanley -Cc: Andrew Jeffery ---- - drivers/media/platform/aspeed-video.c | 32 +++++++++++++++++++++++++++++--- - 1 file changed, 29 insertions(+), 3 deletions(-) - -diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c -index 8144fe36ad48..e70be8fdbde5 100644 ---- a/drivers/media/platform/aspeed-video.c -+++ b/drivers/media/platform/aspeed-video.c -@@ -187,6 +187,7 @@ enum { - VIDEO_STREAMING, - VIDEO_FRAME_INPRG, - VIDEO_STOPPED, -+ VIDEO_CLOCKS_ON, - }; - - struct aspeed_video_addr { -@@ -483,19 +484,29 @@ static void aspeed_video_enable_mode_detect(struct aspeed_video *video) - - static void aspeed_video_off(struct aspeed_video *video) - { -+ if (!test_bit(VIDEO_CLOCKS_ON, &video->flags)) -+ return; -+ - /* Disable interrupts */ - aspeed_video_write(video, VE_INTERRUPT_CTRL, 0); - - /* Turn off the relevant clocks */ - clk_disable_unprepare(video->vclk); - clk_disable_unprepare(video->eclk); -+ -+ clear_bit(VIDEO_CLOCKS_ON, &video->flags); - } - - static void aspeed_video_on(struct aspeed_video *video) - { -+ if (test_bit(VIDEO_CLOCKS_ON, &video->flags)) -+ return; -+ - /* Turn on the relevant clocks */ - clk_prepare_enable(video->eclk); - clk_prepare_enable(video->vclk); -+ -+ set_bit(VIDEO_CLOCKS_ON, &video->flags); - } - - static void aspeed_video_bufs_done(struct aspeed_video *video, -@@ -513,12 +524,14 @@ static void aspeed_video_bufs_done(struct aspeed_video *video, - - static void aspeed_video_irq_res_change(struct aspeed_video *video) - { -+ spin_lock(&video->lock); - dev_dbg(video->dev, "Resolution changed; resetting\n"); - - set_bit(VIDEO_RES_CHANGE, &video->flags); - clear_bit(VIDEO_FRAME_INPRG, &video->flags); - - aspeed_video_off(video); -+ spin_unlock(&video->lock); - aspeed_video_bufs_done(video, VB2_BUF_STATE_ERROR); - - schedule_delayed_work(&video->res_work, RESOLUTION_CHANGE_DELAY); -@@ -938,9 +951,13 @@ static void aspeed_video_init_regs(struct aspeed_video *video) - - static void aspeed_video_start(struct aspeed_video *video) - { -+ unsigned long flags; -+ -+ spin_lock_irqsave(&video->lock, flags); - aspeed_video_on(video); - - aspeed_video_init_regs(video); -+ spin_unlock_irqrestore(&video->lock, flags); - - /* Resolution set to 640x480 if no signal found */ - aspeed_video_get_resolution(video); -@@ -956,6 +973,9 @@ static void aspeed_video_start(struct aspeed_video *video) - - static void aspeed_video_stop(struct aspeed_video *video) - { -+ unsigned long flags; -+ -+ spin_lock_irqsave(&video->lock, flags); - set_bit(VIDEO_STOPPED, &video->flags); - cancel_delayed_work_sync(&video->res_work); - -@@ -969,6 +989,7 @@ static void aspeed_video_stop(struct aspeed_video *video) - - video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; - video->flags = 0; -+ spin_unlock_irqrestore(&video->lock, flags); - } - - static int aspeed_video_querycap(struct file *file, void *fh, -@@ -1306,16 +1327,21 @@ static void aspeed_video_resolution_work(struct work_struct *work) - struct delayed_work *dwork = to_delayed_work(work); - struct aspeed_video *video = container_of(dwork, struct aspeed_video, - res_work); -- u32 input_status = video->v4l2_input_status; -+ unsigned long flags; -+ u32 input_status; - -+ spin_lock_irqsave(&video->lock, flags); -+ input_status = video->v4l2_input_status; - aspeed_video_on(video); - - /* Exit early in case no clients remain */ -- if (test_bit(VIDEO_STOPPED, &video->flags)) -+ if (test_bit(VIDEO_STOPPED, &video->flags)) { -+ spin_unlock_irqrestore(&video->lock, flags); - goto done; -+ } - - aspeed_video_init_regs(video); -- -+ spin_unlock_irqrestore(&video->lock, flags); - aspeed_video_get_resolution(video); - - if (video->detected_timings.width != video->active_timings.width || --- -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" +Date: Mon, 6 May 2019 10:02:14 -0700 +Subject: [PATCH] Add AST2500d JTAG driver + +Adding aspeed jtag device + +Signed-off-by: Hunt, Bryan +--- + 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..7ba94a7bc --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0052-drivers-jtag-Add-JTAG-core-driver.patch @@ -0,0 +1,904 @@ +From 0b8b93851bb79e70e91159f310afd4b56084977f Mon Sep 17 00:00:00 2001 +From: "Corona, Ernesto" +Date: Mon, 6 May 2019 10:05:51 -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 +Signed-off-by: Jiri Pirko +Signed-off-by: Corona, Ernesto +Acked-by: Philippe Ombredanne +Cc: Vadim Pasternak +Cc: Masahiro Yamada +Cc: Paul Burton +Cc: Thomas Gleixner +Cc: Greg Kroah-Hartman +Cc: Arnd Bergmann +Cc: Boris Brezillon +Cc: Randy Dunlap +Cc: Johan Hovold +Cc: Jens Axboe +Cc: Joel Stanley +Cc: Palmer Dabbelt +Cc: Kees Cook +Cc: Steven A Filary +Cc: Bryan Hunt +--- +v28->v29 +Comments pointed by Steven Filary +- 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). + +v27->v28 +Comments pointed by Steven Filary +- 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 +- set values to enums in jtag.h + +v23->v24 +Notifications from kbuild test robot +- Add include types.h header to jtag.h +- remove unecessary jtag_release + +v22->v23 +Comments pointed by Greg KH +- 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 +- 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 +- fix spell in ABI doccumentation + +v20->v21 + Comments pointed by Randy Dunlap + - Fix JTAG dirver help in Kconfig + +v19->v20 +Comments pointed by Randy Dunlap +- Fix JTAG dirver help in Kconfig + +Notifications from kbuild test robot +- fix incompatible type casts + +v18->v19 +Comments pointed by Julia Cartwright +- Fix memory leak on jtag_alloc exit + +v17->v18 +Comments pointed by Julia Cartwright +- 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 +- 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 +- 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 +- Change style of head block comment from /**/ to // + +v12->v13 +Comments pointed by Philippe Ombredanne +- 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 +- 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 +- 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 +- 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 +- 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 +- 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 +- Fix misspelling s/friver/driver + +v6->v7 +Notifications from kbuild test robot +- Remove include asm/types.h from jtag.h +- Add include to jtag.c + +v5->v6 +v4->v5 + +v3->v4 +Comments pointed by Arnd Bergmann +- 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 +- Change include path to in jtag.h + +v1->v2 +Comments pointed by Greg KH +- 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 +- Change list_add_tail in jtag_unregister to list_del + +Comments pointed by Neil Armstrong +- Add SPDX-License-Identifier instead of license text + +Comments pointed by Arnd Bergmann +- 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 +- 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 | 314 ++++++++++++++++++++++++++++++++++++++++++++++ + include/linux/jtag.h | 47 +++++++ + include/uapi/linux/jtag.h | 208 ++++++++++++++++++++++++++++++ + 7 files changed, 589 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,314 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++// Copyright (c) 2018 Mellanox Technologies. All rights reserved. ++// Copyright (c) 2018 Oleksandr Shamray ++// Copyright (c) 2019 Intel Corporation ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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_end_tap_state endstate; ++ 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(&endstate, (const void __user *)arg, ++ sizeof(struct jtag_end_tap_state))) ++ return -EFAULT; ++ ++ if (endstate.endstate > JTAG_STATE_UPDATEIR) ++ return -EINVAL; ++ ++ if (endstate.reset > JTAG_FORCE_RESET) ++ return -EINVAL; ++ ++ err = jtag->ops->status_set(jtag, &endstate); ++ 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.endstate > JTAG_STATE_UPDATEIR) ++ 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(*ptr), 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 "); ++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 */ ++/* Copyright (c) 2019 Intel Corporation */ ++ ++#ifndef __LINUX_JTAG_H ++#define __LINUX_JTAG_H ++ ++#include ++#include ++ ++#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_end_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,208 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (c) 2018 Mellanox Technologies. All rights reserved. */ ++/* Copyright (c) 2018 Oleksandr Shamray */ ++/* 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_endstate: ++ * ++ * @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 ++ */ ++enum jtag_endstate { ++ 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 ++}; ++ ++/** ++ * 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_end_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_end_tap_state { ++ __u8 reset; ++ __u8 endstate; ++ __u8 tck; ++}; ++ ++/** ++ * struct jtag_xfer - jtag xfer: ++ * ++ * @type: transfer type ++ * @direction: xfer direction ++ * @length: xfer bits len ++ * @tdio : xfer data array ++ * @endir: xfer end state ++ * ++ * Structure provide interface to JTAG device for JTAG SDR/SIR xfer execution. ++ */ ++struct jtag_xfer { ++ __u8 type; ++ __u8 direction; ++ __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_end_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_endstate) ++#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..d5d43f31b --- /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,1282 @@ +From 817a43d1b1e197e7eff43492599469bbc23bf0fd Mon Sep 17 00:00:00 2001 +From: "Corona, Ernesto" +Date: Fri, 17 May 2019 11:18:13 -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 +Signed-off-by: Jiri Pirko +Signed-off-by: Corona, Ernesto +Acked-by: Arnd Bergmann +Acked-by: Philippe Ombredanne +Acked-by: Joel Stanley +Cc: Vadim Pasternak +Cc: Andrew Jeffery +Cc: Steven A Filary +Cc: Bryan Hunt +--- +v28->v29 +Comments pointed by Steven Filary +- 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. + +v27->v28 +Comments pointed by Steven Filary +- 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 +- change aspeed_jtag_sw_delay to udelay function in bit-bang operation + +v25->v26 +v24->v25 +Comments pointed by Greg KH +- reduced debug printouts + +v23->v24 +v22->v23 +v21->v22 +Comments pointed by Andy Shevchenko +- 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 +- 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 +- Add reset_control on Jtag init/deinit + +v14->v15 +Comments pointed by Joel Stanley +- 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 +- Change style of head block comment from /**/ to // + +v12->v13 +Comments pointed by Philippe Ombredanne +- 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 +- Changed capability check for aspeed,ast2400-jtag/ast200-jtag + +v11->v12 +Comments pointed by Chip Bilbrey +- 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 +- add *data parameter to xfer function prototype + +v7->v8 +Comments pointed by Joel Stanley +- 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 +- Add include to jtag-asapeed.c + +v5->v6 +v4->v5 +Comments pointed by Arnd Bergmann +- 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 +- change transaction pointer tdio type to __u64 +- change internal status type from enum to __u32 + +v2->v3 + +v1->v2 +Comments pointed by Greg KH +- change license type from GPLv2/BSD to GPLv2 + +Comments pointed by Neil Armstrong +- 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 +- 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 +- Change module name jtag-aspeed in description in Kconfig + +Comments pointed by kbuild test robot +- Remove invalid include +- add resource_size instead of calculation +--- + drivers/jtag/Kconfig | 14 + + drivers/jtag/Makefile | 1 + + drivers/jtag/jtag-aspeed.c | 1040 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 1055 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,1040 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (c) 2018 Mellanox Technologies. All rights reserved. ++// Copyright (c) 2018 Oleksandr Shamray ++// Copyright (c) 2019 Intel Corporation ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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*/ ++ ++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_endstate 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} }, ++}; ++ ++static char *end_status_str[] = { ++ "tlr", "idle", "selDR", "capDR", "sDR", "ex1DR", "pDR", "ex2DR", ++ "updDR", "selIR", "capIR", "sIR", "ex1IR", "pIR", "ex2IR", "updIR" ++}; ++ ++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_endstate endstate) ++{ ++ int i = 0; ++ enum jtag_endstate from, to; ++ ++ from = aspeed_jtag->status; ++ to = endstate; ++ 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 = endstate; ++} ++ ++static void aspeed_jtag_end_tap_state_sw(struct aspeed_jtag *aspeed_jtag, ++ struct jtag_end_tap_state *endstate) ++{ ++ /* SW mode from curent tap state -> to end_state */ ++ if (endstate->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, endstate->endstate); ++} ++ ++static int aspeed_jtag_status_set(struct jtag *jtag, ++ struct jtag_end_tap_state *endstate) ++{ ++ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag); ++ ++#ifdef DEBUG_JTAG ++ dev_dbg(aspeed_jtag->dev, "Set TAP state: %s\n", ++ end_status_str[endstate->endstate]); ++#endif ++ ++ if (!(aspeed_jtag->mode & JTAG_XFER_HW_MODE)) { ++ aspeed_jtag_end_tap_state_sw(aspeed_jtag, endstate); ++ return 0; ++ } ++ ++ /* x TMS high + 1 TMS low */ ++ if (endstate->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, JTAG_STATE_SHIFTIR); ++ else ++ aspeed_jtag_set_tap_state(aspeed_jtag, 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, 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, ++ enum jtag_endstate endstate) ++{ ++ 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, ++ xfer->endstate) != 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 "); ++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" +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 +Signed-off-by: Jiri Pirko +Signed-off-by: Corona, Ernesto +Acked-by: Rob Herring +Cc: Mark Rutland +Cc: Joel Stanley +Cc: Andrew Jeffery +Cc: Steven A Filary +Cc: Bryan Hunt +--- +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 +- 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 +- Change compatible string to ast2400 and ast2000 + +V6->v7 +Comments pointed by Tobias Klauser + - Fix spell "Doccumentation" -> "Documentation" + +v5->v6 +Comments pointed by Tobias Klauser +- Small nit: s/documentation/Documentation/ + +v4->v5 + +V3->v4 +Comments pointed by Rob Herring +- delete unnecessary "status" and "reg-shift" descriptions in + bindings file + +v2->v3 +Comments pointed by Rob Herring +- 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..af641ffe2 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0055-Documentation-jtag-Add-ABI-documentation.patch @@ -0,0 +1,303 @@ +From c2d57900820475b50affd171f4dc423a278887ae Mon Sep 17 00:00:00 2001 +From: "Corona, Ernesto" +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 +Signed-off-by: Corona, Ernesto +Acked-by: Arnd Bergmann +Cc: Jonathan Corbet +Cc: Jiri Pirko +Cc: Vadim Pasternak +Cc: Steven A Filary +Cc: Bryan Hunt +--- +v28->v29 +Comments pointed by Steven Filary +- 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 +- 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 +- fix spell in ABI documentation + +v24->v25 +Comments pointed by Greg KH +- Fixed documentation according to new open() behavior + +v23->v24 +v22->v23 +Comments pointed by Randy Dunlap +- fix spell in ABI doccumentation + +v21->v22 +Comments pointed by Randy Dunlap +- fix spell in ABI doccumentation + +v20->v21 +Comments pointed by Randy Dunlap +- Fix JTAG dirver help in Kconfig + +v19->v20 +Comments pointed by Randy Dunlap +- Fix JTAG doccumentation + +v18->v19 +Pavel Machek +- Added JTAG doccumentation to Documentation/jtag + +v17->v18 +v16->v17 +v15->v16 +v14->v15 +v13->v14 +v12->v13 +v11->v12 Tobias Klauser +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 +- Added jtag-cdev documentation to Documentation/ABI/testing folder +--- + Documentation/ABI/testing/jtag-dev | 23 +++++++ + Documentation/jtag/overview | 27 ++++++++ + Documentation/jtag/transactions | 138 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 188 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]. ++ ++ 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] ++ 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,138 @@ ++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_end_tap_state { ++ __u8 reset; ++ __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_end_tap_state end_state; ++ ++ end_state.endstate = JTAG_STATE_IDLE; ++ end_state.reset = 0; ++ end_state.tck = data_p->tck; ++ usleep(25 * 1000); ++ ioctl(jtag_fd, JTAG_SIOCSTATE, &end_state); ++ ++JTAG_GIOCSTATUS ++------ ++Get JTAG TAPC machine state ++ ++ unsigned int jtag_fd; ++ jtag_endstate endstate; ++ ioctl(jtag_fd, JTAG_GIOCSTATUS, &endstate); ++ ++JTAG_IOCXFER ++------ ++Send SDR/SIR transaction ++ ++struct jtag_xfer { ++ __u8 type; ++ __u8 direction; ++ __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 ++endstate: xfer end state after transaction finish ++ can be: any state listed in jtag_endstate 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.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" +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 +Signed-off-by: Corona, Ernesto +Acked-by: Philippe Ombredanne +Cc: Jiri Pirko +Cc: Vadim Pasternak +Cc: Jonathan Corbet +Cc: Greg Kroah-Hartman +Cc: Gustavo Pimentel +Cc: Lorenzo Pieralisi +Cc: Kishon Vijay Abraham I +Cc: Darrick J. Wong +Cc: Bryant G. Ly +Cc: Eric Sandeen +Cc: Randy Dunlap +Cc: Tomohiro Kusumi +Cc: Arnd Bergmann +Cc: Steven A Filary +Cc: Bryan Hunt +--- + 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: + + 0xB1 00-1F PPPoX ++0xB2 00-0F linux/jtag.h JTAG driver ++ + 0xB3 00 linux/mmc/ioctl.h + 0xB4 00-0F linux/gpio.h + 0xB5 00-0F uapi/linux/rpmsg.h +-- +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" +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 +Signed-off-by: Corona, Ernesto +Acked-by: Arnd Bergmann +Cc: Jiri Pirko +Cc: Vadim Pasternak +Cc: Mauro Carvalho Chehab +Cc: Greg Kroah-Hartman +Cc: David S. Miller +Cc: Nicolas Ferre +Cc: Steven A Filary +Cc: Bryan Hunt +--- + 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 ++M: Vadim Pasternak ++M Ernesto Corona ++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 + 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 +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 +--- + .../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/0059-media-aspeed-remove-source-buffer-allocation-before-.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0059-media-aspeed-remove-source-buffer-allocation-before-.patch new file mode 100644 index 000000000..e4161961e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0059-media-aspeed-remove-source-buffer-allocation-before-.patch @@ -0,0 +1,49 @@ +From aa8f405609038693481bad4393d58f0c665569a6 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo +Date: Tue, 21 May 2019 16:00:28 -0700 +Subject: [PATCH 1/4] media: aspeed: remove source buffer allocation before + mode detection + +Mode detection doesn't require source buffer allocation so this +commit removes that. + +Signed-off-by: Jae Hyun Yoo +--- + drivers/media/platform/aspeed-video.c | 21 --------------------- + 1 file changed, 21 deletions(-) + +diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c +index 1bb863b32836..ee1f87a08c7c 100644 +--- a/drivers/media/platform/aspeed-video.c ++++ b/drivers/media/platform/aspeed-video.c +@@ -733,27 +733,6 @@ static void aspeed_video_get_resolution(struct aspeed_video *video) + det->height = MIN_HEIGHT; + video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; + +- /* +- * Since we need max buffer size for detection, free the second source +- * buffer first. +- */ +- if (video->srcs[1].size) +- aspeed_video_free_buf(video, &video->srcs[1]); +- +- if (video->srcs[0].size < VE_MAX_SRC_BUFFER_SIZE) { +- if (video->srcs[0].size) +- aspeed_video_free_buf(video, &video->srcs[0]); +- +- if (!aspeed_video_alloc_buf(video, &video->srcs[0], +- VE_MAX_SRC_BUFFER_SIZE)) { +- dev_err(video->dev, +- "Failed to allocate source buffers\n"); +- return; +- } +- } +- +- aspeed_video_write(video, VE_SRC0_ADDR, video->srcs[0].dma); +- + do { + if (tries) { + set_current_state(TASK_INTERRUPTIBLE); +-- +2.21.0 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0060-media-aspeed-use-different-delays-for-triggering-VE-.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0060-media-aspeed-use-different-delays-for-triggering-VE-.patch new file mode 100644 index 000000000..3e158c628 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0060-media-aspeed-use-different-delays-for-triggering-VE-.patch @@ -0,0 +1,60 @@ +From 431c7974302fad5ae835adb46d3c8fa4034c845a Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo +Date: Tue, 21 May 2019 16:06:56 -0700 +Subject: [PATCH 2/4] media: aspeed: use different delays for triggering VE H/W + reset + +In case of watchdog timeout detected while doing mode detection, +it's better triggering video engine hardware reset immediately so +this commit fixes code for the case. Other than the case, it will +trigger video engine hardware reset after RESOLUTION_CHANGE_DELAY. + +Signed-off-by: Jae Hyun Yoo +--- + drivers/media/platform/aspeed-video.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c +index ee1f87a08c7c..b8540cc7848d 100644 +--- a/drivers/media/platform/aspeed-video.c ++++ b/drivers/media/platform/aspeed-video.c +@@ -522,7 +522,7 @@ static void aspeed_video_bufs_done(struct aspeed_video *video, + spin_unlock_irqrestore(&video->lock, flags); + } + +-static void aspeed_video_irq_res_change(struct aspeed_video *video) ++static void aspeed_video_irq_res_change(struct aspeed_video *video, ulong delay) + { + spin_lock(&video->lock); + dev_dbg(video->dev, "Resolution changed; resetting\n"); +@@ -534,7 +534,7 @@ static void aspeed_video_irq_res_change(struct aspeed_video *video) + spin_unlock(&video->lock); + aspeed_video_bufs_done(video, VB2_BUF_STATE_ERROR); + +- schedule_delayed_work(&video->res_work, RESOLUTION_CHANGE_DELAY); ++ schedule_delayed_work(&video->res_work, delay); + } + + static irqreturn_t aspeed_video_irq(int irq, void *arg) +@@ -547,7 +547,7 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg) + * re-initialize + */ + if (sts & VE_INTERRUPT_MODE_DETECT_WD) { +- aspeed_video_irq_res_change(video); ++ aspeed_video_irq_res_change(video, 0); + return IRQ_HANDLED; + } + +@@ -565,7 +565,8 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg) + * Signal acquired while NOT doing resolution + * detection; reset the engine and re-initialize + */ +- aspeed_video_irq_res_change(video); ++ aspeed_video_irq_res_change(video, ++ RESOLUTION_CHANGE_DELAY); + return IRQ_HANDLED; + } + } +-- +2.21.0 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0061-media-aspeed-fix-an-incorrect-timeout-checking-in-mo.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0061-media-aspeed-fix-an-incorrect-timeout-checking-in-mo.patch new file mode 100644 index 000000000..7739d5214 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0061-media-aspeed-fix-an-incorrect-timeout-checking-in-mo.patch @@ -0,0 +1,30 @@ +From 294391f66df034de8dc63ac2e78f3a00d14075d9 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo +Date: Thu, 23 May 2019 14:24:25 -0700 +Subject: [PATCH 3/4] media: aspeed: fix an incorrect timeout checking in mode + detection + +There is an incorrect timeout checking in mode detection logic so +it misses resolution detecting chances. This commit fixes the bug. + +Signed-off-by: Jae Hyun Yoo +--- + drivers/media/platform/aspeed-video.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c +index b8540cc7848d..da20e93f58d3 100644 +--- a/drivers/media/platform/aspeed-video.c ++++ b/drivers/media/platform/aspeed-video.c +@@ -737,7 +737,7 @@ static void aspeed_video_get_resolution(struct aspeed_video *video) + do { + if (tries) { + set_current_state(TASK_INTERRUPTIBLE); +- if (schedule_timeout(INVALID_RESOLUTION_DELAY)) ++ if (!schedule_timeout(INVALID_RESOLUTION_DELAY)) + return; + } + +-- +2.21.0 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0062-media-aspeed-add-a-workaround-to-fix-a-silicon-bug.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0062-media-aspeed-add-a-workaround-to-fix-a-silicon-bug.patch new file mode 100644 index 000000000..efa6f5023 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0062-media-aspeed-add-a-workaround-to-fix-a-silicon-bug.patch @@ -0,0 +1,66 @@ +From 09ec380a1d6ae66b2a8124c8fdd984ff829b41d1 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo +Date: Thu, 23 May 2019 14:33:03 -0700 +Subject: [PATCH 4/4] media: aspeed: add a workaround to fix a silicon bug + +AST2500 silicon revision A1 and A2 have a silicon bug which causes +extremly long capturing time on specific resolutions (1680 width). +To fix the bug, this commit adjusts the capturing window register +setting to 1728 if detected width is 1680. The compression window +register setting will be kept as the original width so output +result will be the same. + +Signed-off-by: Jae Hyun Yoo +--- + drivers/media/platform/aspeed-video.c | 26 +++++++++++++++++++------- + 1 file changed, 19 insertions(+), 7 deletions(-) + +diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c +index da20e93f58d3..c2d4a2e6f20f 100644 +--- a/drivers/media/platform/aspeed-video.c ++++ b/drivers/media/platform/aspeed-video.c +@@ -826,8 +826,27 @@ static void aspeed_video_set_resolution(struct aspeed_video *video) + struct v4l2_bt_timings *act = &video->active_timings; + unsigned int size = act->width * act->height; + ++ /* Set capture/compression frame sizes */ + aspeed_video_calc_compressed_size(video, size); + ++ if (video->active_timings.width == 1680) { ++ /* ++ * This is a workaround to fix a silicon bug on A1 and A2 ++ * revisions. Since it doesn't break capturing operation on A0 ++ * revision, use it for all revisions without checking the ++ * revision ID. ++ */ ++ aspeed_video_write(video, VE_CAP_WINDOW, ++ 1728 << 16 | act->height); ++ size += (1728 - 1680) * video->active_timings.height; ++ } else { ++ aspeed_video_write(video, VE_CAP_WINDOW, ++ act->width << 16 | act->height); ++ } ++ aspeed_video_write(video, VE_COMP_WINDOW, ++ act->width << 16 | act->height); ++ aspeed_video_write(video, VE_SRC_SCANLINE_OFFSET, act->width * 4); ++ + /* Don't use direct mode below 1024 x 768 (irqs don't fire) */ + if (size < DIRECT_FETCH_THRESHOLD) { + aspeed_video_write(video, VE_TGS_0, +@@ -844,13 +863,6 @@ static void aspeed_video_set_resolution(struct aspeed_video *video) + aspeed_video_update(video, VE_CTRL, 0, VE_CTRL_DIRECT_FETCH); + } + +- /* Set capture/compression frame sizes */ +- aspeed_video_write(video, VE_CAP_WINDOW, +- act->width << 16 | act->height); +- aspeed_video_write(video, VE_COMP_WINDOW, +- act->width << 16 | act->height); +- aspeed_video_write(video, VE_SRC_SCANLINE_OFFSET, act->width * 4); +- + size *= 4; + + if (size == video->srcs[0].size / 2) { +-- +2.21.0 + 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 index 2a9984661..c5d48eb90 100644 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend @@ -17,7 +17,6 @@ SRC_URI += " \ file://0022-Add-AST2500-eSPI-driver.patch \ file://0026-Add-support-for-new-PECI-commands.patch \ file://0028-Add-AST2500-JTAG-driver.patch \ - file://0029-i2c-aspeed-Improve-driver-to-support-multi-master-us.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 \ @@ -34,5 +33,16 @@ SRC_URI += " \ 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://0050-media-platform-Fix-a-kernel-warning-on-clk-control.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://0059-media-aspeed-remove-source-buffer-allocation-before-.patch \ + file://0060-media-aspeed-use-different-delays-for-triggering-VE-.patch \ + file://0061-media-aspeed-fix-an-incorrect-timeout-checking-in-mo.patch \ + file://0062-media-aspeed-add-a-workaround-to-fix-a-silicon-bug.patch \ " -- cgit v1.2.3