diff options
author | Ed Tanous <ed.tanous@intel.com> | 2019-05-29 20:29:58 +0300 |
---|---|---|
committer | Ed Tanous <ed.tanous@intel.com> | 2019-06-06 04:30:22 +0300 |
commit | 87a65e63bac789bca0607e0b4ab09d62517b95e7 (patch) | |
tree | 3254b912d6468012543e127a19ba2f1cd13b108f /meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch | |
parent | 5364646cb66fa75cdcbf148e039e0383cda94f2a (diff) | |
download | openbmc-87a65e63bac789bca0607e0b4ab09d62517b95e7.tar.xz |
Update to internal
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch')
-rw-r--r-- | meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch | 1057 |
1 files changed, 6 insertions, 1051 deletions
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" <bryan.hunt@intel.com> -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" <ernesto.corona@intel.com> +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 <bryan.hunt@intel.com> +Signed-off-by: Corona, Ernesto <ernesto.corona@intel.com> --- - 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 <linux/bitfield.h> -+#include <linux/delay.h> -+#include <linux/fs.h> -+#include <linux/interrupt.h> -+#include <linux/io.h> -+#include <linux/jtag_drv.h> -+#include <linux/miscdevice.h> -+#include <linux/module.h> -+#include <linux/of.h> -+#include <linux/platform_device.h> -+#include <linux/slab.h> -+#include <linux/uaccess.h> -+ -+#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 <ryan_chen@aspeedtech.com>"); -+MODULE_AUTHOR("Bryan Hunt <bryan.hunt@intel.com>"); -+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 |