Received: by 10.223.164.202 with SMTP id h10csp4949702wrb; Wed, 29 Nov 2017 14:52:48 -0800 (PST) X-Google-Smtp-Source: AGs4zMbqLVm3Uys2z5UEqnwWUryGQsGVpE/GVvCvLx1RH0NJybYW1kg3CsPz4hYNBSbZMMG4ZP6V X-Received: by 10.84.132.40 with SMTP id 37mr464002ple.246.1511995968389; Wed, 29 Nov 2017 14:52:48 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1511995968; cv=none; d=google.com; s=arc-20160816; b=TJYr7XrX0DYyCFTf/RVMGwkoQWblkkN9NSR9mzCXKRI2tF/Lwq5gLREamFkhhFP2oc PuoVMy5Ttz42wPHADSuAggMsFlIwPtr3TwqPM9Bt4riu8nndYWGiZQNx5VNcU1zPlyzn +in5juRCGRyGO+BiSzvjax0lQZs+aEpr9ei+52CDM5B8AFY43bx0qgBeMwuK+Y5/nqEo dLOVfgO+dcSPpCxDc//gymZgTASgJDXeprEVeEi/ZY0WKnjm2gBsihxnxXEyoAfKvDbG ZHAz6fiK1K+20XECbYgfrLcO2JLk3J2B19NNZyvcojWo4eXcmZy4V5cD3TUA7z6j3lZo tPNw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:subject:message-id:date:from :references:in-reply-to:mime-version:dkim-signature :arc-authentication-results; bh=JYQnrlnlI89smi40llUgal9ut+DRG8gGQlrYYR5rfyo=; b=HLR2RUKZK5NlAhL5UWQnzezjqaqhkDKkkvcxiCh/QJhLhALQHmU6cLTD6O+/95Zo8U 7zYHMfpj3woZ5bFDCxf1lfbGS/c1/YEce4m8ezreG3CfBC0f/eNRe2DShKHso1M4fyuL yimll4CVI/X5zGHTGXNLm7a8ib2ggrlUU0Zjb4JfUYWXo7udzh1WpYkVhbPDH+qgcEFK yDdoCJ8QtMPXbBafyv5oK+0f739Nk3/IOETozlETSl7eC317WXMrqF+OJaeRWGDoG2ha 2Fjny9NuZJpPNhYuc4bEsmKkrAcs1vubfFgBCuPOUDxKS9pmIhwhtLn74olbrI9cq2OF GfVA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=ugMBGHDi; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 70si1093893pga.601.2017.11.29.14.52.34; Wed, 29 Nov 2017 14:52:48 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=ugMBGHDi; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752514AbdK2Wvp (ORCPT + 99 others); Wed, 29 Nov 2017 17:51:45 -0500 Received: from mail-it0-f44.google.com ([209.85.214.44]:38648 "EHLO mail-it0-f44.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752071AbdK2Wvn (ORCPT ); Wed, 29 Nov 2017 17:51:43 -0500 Received: by mail-it0-f44.google.com with SMTP id r6so6169308itr.3 for ; Wed, 29 Nov 2017 14:51:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc; bh=JYQnrlnlI89smi40llUgal9ut+DRG8gGQlrYYR5rfyo=; b=ugMBGHDiu1XLGgw4oFpkDVA8KvN0MG3j8ImrJKYfkY7kcF3Ub+qODomDCUaxRCHk6S AlLY/t4U+R099nCd9VKebj/PAiuiD2GySMPbfl4KYhaekibLZyT9KjYsks407XJeYfa9 zfVWoKxQwqiivw/SAdr/ybx+Dz1uWR1+szZ4/0JmZW1xVJrSOR8DTQo0I42Yd3MBTYrC ptwSfs27RFrPQaPyvU7yCBDT3nlA9V8ghBsgeNVkY8OYKnYiv3b5fTLDCuIo9r+JUQYK u8EWSc3AGu7uCkg+b2CbXDY04VuiE6GnvecMD7IqhjCWMp4/RjLznkvvss+L6D9DlnDt lduQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:cc; bh=JYQnrlnlI89smi40llUgal9ut+DRG8gGQlrYYR5rfyo=; b=fDQ/eF01UJbZEyRKhgOdP3i8M3klX+tgnHJAjvY8rdfUxjJyQ3000gBqnOelqa05Sd 9egF5mt9dPt+xhe5eF6Io5QIL1Lfjffv+RErlwrokMSBloq0M5lU3UTBv+YRabPyDAHq ++4iATnL1/uLxeBKEQQZNsp/Kr4Uz9OLn4iVsHLYgeTYSrke1ZU/eFTCosihvhP6JkQc dE7iVwTAEPjPVXalrzyoAFgbMnZIAZ0XbxyQMWB5ad5kZvTg++qB/1TED7FVdhV7139D ZLYGtttOuhDASYtPErrbe383yoVGzpU2/QQAUlspYFON5V/MaqfgsNRdFndkTHugo8c8 2alw== X-Gm-Message-State: AJaThX4LxW1/o+gOTaOLdVe9EWFZOYqAS6YXPxHOOaCLWAyH/QaI/iBU 6/N8UvXODD5hJR/lBWggwHq1D1xq3tDqSlTvlM5O8Q== X-Received: by 10.36.104.6 with SMTP id v6mr588544itb.68.1511995901432; Wed, 29 Nov 2017 14:51:41 -0800 (PST) MIME-Version: 1.0 Received: by 10.107.3.234 with HTTP; Wed, 29 Nov 2017 14:51:10 -0800 (PST) In-Reply-To: <1510675867-24276-3-git-send-email-oleksandrs@mellanox.com> References: <1510675867-24276-1-git-send-email-oleksandrs@mellanox.com> <1510675867-24276-3-git-send-email-oleksandrs@mellanox.com> From: Kun Yi Date: Wed, 29 Nov 2017 14:51:10 -0800 Message-ID: Subject: Re: [patch v12 2/4] drivers: jtag: Add Aspeed SoC 24xx and 25xx families JTAG master driver To: Oleksandr Shamray Cc: gregkh@linuxfoundation.org, arnd@arndb.de, system-sw-low-level@mellanox.com, devicetree@vger.kernel.org, jiri@resnulli.us, vadimp@mellanox.com, linux-api@vger.kernel.org, OpenBMC Maillist , linux-kernel@vger.kernel.org, openocd-devel-owner@lists.sourceforge.net, mec@shout.net, Jiri Pirko , robh+dt@kernel.org, linux-serial@vger.kernel.org, tklauser@distanz.ch, mchehab@kernel.org, davem@davemloft.net, linux-arm-kernel@lists.infradead.org Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Thanks for working on the driver, Oleksandr. I gave this a try on a board with Aspeed 2520. One question below: On Tue, Nov 14, 2017 at 8:11 AM, Oleksandr Shamray wrote: > Driver adds support of Aspeed 2500/2400 series SOC JTAG master controller. > > Driver implements the following jtag ops: > - freq_get; > - freq_set; > - status_get; > - idle; > - xfer; > > It has been tested on Mellanox system with BMC equipped with > Aspeed 2520 SoC for programming CPLD devices. > > Signed-off-by: Oleksandr Shamray > Signed-off-by: Jiri Pirko > Acked-by: Arnd Bergmann > --- > 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 | 13 + > drivers/jtag/Makefile | 1 + > drivers/jtag/jtag-aspeed.c | 782 ++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 796 insertions(+), 0 deletions(-) > create mode 100644 drivers/jtag/jtag-aspeed.c > > diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig > index 0fad1a3..098beb0 100644 > --- a/drivers/jtag/Kconfig > +++ b/drivers/jtag/Kconfig > @@ -14,3 +14,16 @@ 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 > + ---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..a6e2417 > --- /dev/null > +++ b/drivers/jtag/jtag-aspeed.c > @@ -0,0 +1,782 @@ > +/* > + * drivers/jtag/aspeed-jtag.c > + * > + * Copyright (c) 2017 Mellanox Technologies. All rights reserved. > + * Copyright (c) 2017 Oleksandr Shamray > + * > + * Released under the GPLv2 only. > + * SPDX-License-Identifier: GPL-2.0 > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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_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_TCK_WAIT 10 > +#define ASPEED_JTAG_RESET_CNTR 10 > + > +#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; > + u32 flag; > + wait_queue_head_t jtag_wq; > + u32 mode; > +}; > + > +static char *end_status_str[] = {"idle", "ir pause", "drpause"}; > + > +static u32 aspeed_jtag_read(struct aspeed_jtag *aspeed_jtag, u32 reg) > +{ > + return readl(aspeed_jtag->reg_base + reg); > +} > + > +static void > +aspeed_jtag_write(struct aspeed_jtag *aspeed_jtag, u32 val, u32 reg) > +{ > + 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); > + div = (apb_frq % freq == 0) ? (apb_frq / freq) - 1 : (apb_frq / 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 int aspeed_jtag_mode_set(struct jtag *jtag, u32 mode) > +{ > + struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag); > + > + aspeed_jtag->mode = mode; > + return 0; > +} > + > +static void aspeed_jtag_sw_delay(struct aspeed_jtag *aspeed_jtag, int cnt) > +{ > + int i; > + > + for (i = 0; i < cnt; i++) > + aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_SW); > +} > + > +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_sw_delay(aspeed_jtag, ASPEED_JTAG_TCK_WAIT); > + > + /* 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; > + > + aspeed_jtag_sw_delay(aspeed_jtag, ASPEED_JTAG_TCK_WAIT); > + > + /* 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); > + return tdo; > +} > + > +static void aspeed_jtag_wait_instruction_pause(struct aspeed_jtag *aspeed_jtag) > +{ > + wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag & > + ASPEED_JTAG_ISR_INST_PAUSE); > + aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_INST_PAUSE; > +} > + > +static void > +aspeed_jtag_wait_instruction_complete(struct aspeed_jtag *aspeed_jtag) > +{ > + wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag & > + ASPEED_JTAG_ISR_INST_COMPLETE); > + aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_INST_COMPLETE; > +} > + > +static void > +aspeed_jtag_wait_data_pause_complete(struct aspeed_jtag *aspeed_jtag) > +{ > + wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag & > + ASPEED_JTAG_ISR_DATA_PAUSE); > + aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_DATA_PAUSE; > +} > + > +static void aspeed_jtag_wait_data_complete(struct aspeed_jtag *aspeed_jtag) > +{ > + wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag & > + ASPEED_JTAG_ISR_DATA_COMPLETE); > + aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_DATA_COMPLETE; > +} > + > +static void aspeed_jtag_sm_cycle(struct aspeed_jtag *aspeed_jtag, const u8 *tms, > + int len) > +{ > + int i; > + > + for (i = 0; i < len; i++) > + aspeed_jtag_tck_cycle(aspeed_jtag, tms[i], 0); > +} > + > +static void aspeed_jtag_run_test_idle_sw(struct aspeed_jtag *aspeed_jtag, > + struct jtag_run_test_idle *runtest) > +{ > + static const u8 sm_pause_irpause[] = {1, 1, 1, 1, 0, 1, 0}; > + static const u8 sm_pause_drpause[] = {1, 1, 1, 0, 1, 0}; > + static const u8 sm_idle_irpause[] = {1, 1, 0, 1, 0}; > + static const u8 sm_idle_drpause[] = {1, 0, 1, 0}; > + static const u8 sm_pause_idle[] = {1, 1, 0}; > + int i; > + > + /* SW mode from idle/pause-> to pause/idle */ > + if (runtest->reset) { > + for (i = 0; i < ASPEED_JTAG_RESET_CNTR; i++) > + aspeed_jtag_tck_cycle(aspeed_jtag, 1, 0); > + } > + > + switch (aspeed_jtag->status) { > + case JTAG_STATE_IDLE: > + switch (runtest->endstate) { > + case JTAG_STATE_PAUSEIR: > + /* ->DRSCan->IRSCan->IRCap->IRExit1->PauseIR */ > + aspeed_jtag_sm_cycle(aspeed_jtag, sm_idle_irpause, > + sizeof(sm_idle_irpause)); > + > + aspeed_jtag->status = JTAG_STATE_PAUSEIR; > + break; > + case JTAG_STATE_PAUSEDR: > + /* ->DRSCan->DRCap->DRExit1->PauseDR */ > + aspeed_jtag_sm_cycle(aspeed_jtag, sm_idle_drpause, > + sizeof(sm_idle_drpause)); > + > + aspeed_jtag->status = JTAG_STATE_PAUSEDR; > + break; > + case JTAG_STATE_IDLE: > + /* IDLE */ > + aspeed_jtag_tck_cycle(aspeed_jtag, 0, 0); > + aspeed_jtag->status = JTAG_STATE_IDLE; > + break; > + default: > + break; > + } > + break; > + > + case JTAG_STATE_PAUSEIR: > + /* Fall-through */ > + case JTAG_STATE_PAUSEDR: > + /* From IR/DR Pause */ > + switch (runtest->endstate) { > + case JTAG_STATE_PAUSEIR: > + /* > + * to Exit2 IR/DR->Updt IR/DR->DRSCan->IRSCan->IRCap-> > + * IRExit1->PauseIR > + */ > + aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_irpause, > + sizeof(sm_pause_irpause)); > + > + aspeed_jtag->status = JTAG_STATE_PAUSEIR; > + break; > + case JTAG_STATE_PAUSEDR: > + /* > + * to Exit2 IR/DR->Updt IR/DR->DRSCan->DRCap-> > + * DRExit1->PauseDR > + */ > + aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_drpause, > + sizeof(sm_pause_drpause)); > + aspeed_jtag->status = JTAG_STATE_PAUSEDR; > + break; > + case JTAG_STATE_IDLE: > + /* to Exit2 IR/DR->Updt IR/DR->IDLE */ > + aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_idle, > + sizeof(sm_pause_idle)); > + aspeed_jtag->status = JTAG_STATE_IDLE; > + break; > + default: > + break; > + } > + break; > + > + default: > + dev_err(aspeed_jtag->dev, "aspeed_jtag_run_test_idle error\n"); > + break; > + } > + > + /* Stay on IDLE for at least TCK cycle */ > + for (i = 0; i < runtest->tck; i++) > + aspeed_jtag_tck_cycle(aspeed_jtag, 0, 0); > +} > + > +/** > + * aspeed_jtag_run_test_idle: > + * JTAG reset: generates at least 9 TMS high and 1 TMS low to force > + * devices into Run-Test/Idle State. > + */ > +static int aspeed_jtag_idle(struct jtag *jtag, > + struct jtag_run_test_idle *runtest) > +{ > + struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag); > + > + dev_dbg(aspeed_jtag->dev, "aspeed_jtag runtest, status:%d, mode:%s, state:%s, reset:%d, tck:%d\n", > + aspeed_jtag->status, > + aspeed_jtag->mode & JTAG_XFER_HW_MODE ? "HW" : "SW", > + end_status_str[runtest->endstate], runtest->reset, > + runtest->tck); > + > + if (!(aspeed_jtag->mode & JTAG_XFER_HW_MODE)) { > + aspeed_jtag_run_test_idle_sw(aspeed_jtag, runtest); > + return 0; > + } > + > + /* Disable sw mode */ > + aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_SW); > + /* x TMS high + 1 TMS low */ > + if (runtest->reset) > + aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_CTL_ENG_EN | > + ASPEED_JTAG_CTL_ENG_OUT_EN | > + ASPEED_JTAG_CTL_FORCE_TMS, ASPEED_JTAG_CTRL); > + else > + aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_EC_GO_IDLE, > + ASPEED_JTAG_EC); > + > + aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN | > + ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW); > + > + aspeed_jtag->status = JTAG_STATE_IDLE; > + return 0; > +} > + > +static void aspeed_jtag_xfer_sw(struct aspeed_jtag *aspeed_jtag, > + struct jtag_xfer *xfer, unsigned long *data) > +{ > + unsigned long remain_xfer = xfer->length; > + unsigned long shift_bits = 0; > + unsigned long index = 0; > + unsigned long tdi; > + char tdo; > + > + if (xfer->direction == JTAG_READ_XFER) > + tdi = UINT_MAX; > + else > + tdi = data[index]; > + > + 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) { > + dev_dbg(aspeed_jtag->dev, "R/W data[%lu]:%lx\n", > + index, data[index]); > + > + tdo = 0; > + index++; > + > + if (xfer->direction == JTAG_READ_XFER) > + tdi = UINT_MAX; > + else > + tdi = data[index]; > + } > + } > + > + tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 1, tdi & ASPEED_JTAG_DATA_MSB); > + data[index] |= tdo << (shift_bits % ASPEED_JTAG_DATA_CHUNK_SIZE); > +} > + > +static void aspeed_jtag_xfer_push_data(struct aspeed_jtag *aspeed_jtag, > + enum jtag_xfer_type type, u32 bits_len) > +{ > + dev_dbg(aspeed_jtag->dev, "shift bits %d\n", bits_len); > + > + 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_DOUT_LEN(bits_len) | > + ASPEED_JTAG_CTL_INST_EN, ASPEED_JTAG_CTRL); > + } 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); > + } > +} > + > +static void aspeed_jtag_xfer_push_data_last(struct aspeed_jtag *aspeed_jtag, > + enum jtag_xfer_type type, > + u32 shift_bits, > + enum jtag_endstate endstate) > +{ > + if (endstate != JTAG_STATE_IDLE) { > + if (type == JTAG_SIR_XFER) { > + dev_dbg(aspeed_jtag->dev, "IR Keep Pause\n"); > + > + aspeed_jtag_write(aspeed_jtag, > + ASPEED_JTAG_IOUT_LEN(shift_bits), > + ASPEED_JTAG_CTRL); > + aspeed_jtag_write(aspeed_jtag, > + ASPEED_JTAG_IOUT_LEN(shift_bits) | > + ASPEED_JTAG_CTL_INST_EN, > + ASPEED_JTAG_CTRL); > + aspeed_jtag_wait_instruction_pause(aspeed_jtag); > + } else { > + dev_dbg(aspeed_jtag->dev, "DR Keep Pause\n"); > + aspeed_jtag_write(aspeed_jtag, > + ASPEED_JTAG_DOUT_LEN(shift_bits) | > + ASPEED_JTAG_CTL_DR_UPDATE, > + ASPEED_JTAG_CTRL); > + aspeed_jtag_write(aspeed_jtag, > + ASPEED_JTAG_DOUT_LEN(shift_bits) | > + ASPEED_JTAG_CTL_DR_UPDATE | > + ASPEED_JTAG_CTL_DATA_EN, > + ASPEED_JTAG_CTRL); > + aspeed_jtag_wait_data_pause_complete(aspeed_jtag); > + } > + } else { > + if (type == JTAG_SIR_XFER) { > + dev_dbg(aspeed_jtag->dev, "IR go IDLE\n"); > + > + 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); > + aspeed_jtag_wait_instruction_complete(aspeed_jtag); > + } else { > + dev_dbg(aspeed_jtag->dev, "DR go IDLE\n"); > + > + 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); > + aspeed_jtag_wait_data_complete(aspeed_jtag); > + } > + } > +} > + > +static void aspeed_jtag_xfer_hw(struct aspeed_jtag *aspeed_jtag, > + struct jtag_xfer *xfer, unsigned long *data) > +{ > + unsigned long remain_xfer = xfer->length; > + unsigned long index = 0; > + char shift_bits; > + u32 data_reg; > + > + data_reg = xfer->type == JTAG_SIR_XFER ? > + ASPEED_JTAG_INST : ASPEED_JTAG_DATA; > + while (remain_xfer) { > + if (xfer->direction == JTAG_WRITE_XFER) { > + dev_dbg(aspeed_jtag->dev, "W dr->dr_data[%lu]:%lx\n", > + index, data[index]); > + > + 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) { > + shift_bits = ASPEED_JTAG_DATA_CHUNK_SIZE; > + > + /* > + * Read bytes were not equals to column length > + * and go to Pause-DR > + */ > + aspeed_jtag_xfer_push_data(aspeed_jtag, xfer->type, > + shift_bits); > + } else { > + /* > + * Read bytes equals to column length => > + * Update-DR > + */ > + shift_bits = remain_xfer; > + aspeed_jtag_xfer_push_data_last(aspeed_jtag, xfer->type, > + shift_bits, > + xfer->endstate); > + } > + > + 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); > + } > + dev_dbg(aspeed_jtag->dev, "R dr->dr_data[%lu]:%lx\n", > + index, data[index]); > + } > + > + remain_xfer = remain_xfer - shift_bits; > + index++; > + dev_dbg(aspeed_jtag->dev, "remain_xfer %lu\n", remain_xfer); > + } > +} > + > +static int aspeed_jtag_xfer(struct jtag *jtag, struct jtag_xfer *xfer, > + u8 *xfer_data) > +{ > + static const u8 sm_update_shiftir[] = {1, 1, 0, 0}; > + static const u8 sm_update_shiftdr[] = {1, 0, 0}; > + static const u8 sm_pause_idle[] = {1, 1, 0}; > + static const u8 sm_pause_update[] = {1, 1}; > + struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag); > + unsigned long *data = (unsigned long *)xfer_data; > + unsigned long remain_xfer = xfer->length; > + unsigned long offset; > + char dbg_str[256]; > + int pos = 0; > + int i; > + > + for (offset = 0, i = 0; offset < xfer->length; > + offset += ASPEED_JTAG_DATA_CHUNK_SIZE, i++) { > + pos += snprintf(&dbg_str[pos], sizeof(dbg_str) - pos, > + "0x%08lx ", data[i]); > + } > + > + dev_dbg(aspeed_jtag->dev, "aspeed_jtag %s %s xfer, mode:%s, END:%d, len:%lu, TDI[%s]\n", > + xfer->type == JTAG_SIR_XFER ? "SIR" : "SDR", > + xfer->direction == JTAG_READ_XFER ? "READ" : "WRITE", > + aspeed_jtag->mode & JTAG_XFER_HW_MODE ? "HW" : "SW", > + xfer->endstate, remain_xfer, dbg_str); > + > + if (!(aspeed_jtag->mode & JTAG_XFER_HW_MODE)) { > + /* SW mode */ > + aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN | > + ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW); > + > + if (aspeed_jtag->status != JTAG_STATE_IDLE) { > + /*IR/DR Pause->Exit2 IR / DR->Update IR /DR */ > + aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_update, > + sizeof(sm_pause_update)); > + } > + > + if (xfer->type == JTAG_SIR_XFER) > + /* ->IRSCan->CapIR->ShiftIR */ > + aspeed_jtag_sm_cycle(aspeed_jtag, sm_update_shiftir, > + sizeof(sm_update_shiftir)); > + else > + /* ->DRScan->DRCap->DRShift */ > + aspeed_jtag_sm_cycle(aspeed_jtag, sm_update_shiftdr, > + sizeof(sm_update_shiftdr)); > + > + aspeed_jtag_xfer_sw(aspeed_jtag, xfer, data); > + > + /* DIPause/DRPause */ > + aspeed_jtag_tck_cycle(aspeed_jtag, 0, 0); > + > + if (xfer->endstate == JTAG_STATE_IDLE) { > + /* ->DRExit2->DRUpdate->IDLE */ > + aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_idle, > + sizeof(sm_pause_idle)); > + } > + } else { > + /* hw mode */ > + aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_SW); > + aspeed_jtag_xfer_hw(aspeed_jtag, xfer, data); > + } > + > + aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN | > + ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW); > + 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; > +} > + > +static irqreturn_t aspeed_jtag_interrupt(s32 this_irq, void *dev_id) > +{ > + struct aspeed_jtag *aspeed_jtag = dev_id; > + irqreturn_t ret; > + u32 status; > + > + status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR); > + dev_dbg(aspeed_jtag->dev, "status %x\n", status); > + > + 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, "aspeed_jtag irq status:%x\n", > + status); > + ret = IRQ_NONE; > + } > + return ret; > +} > + > +int aspeed_jtag_init(struct platform_device *pdev, > + struct aspeed_jtag *aspeed_jtag) > +{ > + struct resource *res; > + int err; > + > + 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); > + } > + > + clk_prepare_enable(aspeed_jtag->pclk); > + > + aspeed_jtag->irq = platform_get_irq(pdev, 0); > + if (aspeed_jtag->irq < 0) { > + dev_err(aspeed_jtag->dev, "no irq specified\n"); > + err = -ENOENT; > + goto clk_unprep; > + } > + > + /* Enable clock */ > + 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); > + > + 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, "aspeed_jtag unable to get IRQ"); > + goto clk_unprep; > + } > + dev_dbg(&pdev->dev, "aspeed_jtag:IRQ %d.\n", aspeed_jtag->irq); > + > + 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); > + > + aspeed_jtag->flag = 0; > + aspeed_jtag->mode = 0; > + init_waitqueue_head(&aspeed_jtag->jtag_wq); > + return 0; > + > +clk_unprep: > + clk_disable_unprepare(aspeed_jtag->pclk); > + return err; > +} > + > +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); > + 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, > + .idle = aspeed_jtag_idle, > + .xfer = aspeed_jtag_xfer, > + .mode_set = aspeed_jtag_mode_set > +}; > + > +static int aspeed_jtag_probe(struct platform_device *pdev) > +{ > + struct aspeed_jtag *aspeed_jtag; > + struct jtag *jtag; > + int err; > + > + if (!of_device_is_compatible(pdev->dev.of_node, "aspeed,aspeed-jtag")) Should this be "aspeed,ast2400-jtag"/"aspeed,ast2500-jtag" as specified in the compatible string below? > + return -ENOMEM; > + > + jtag = jtag_alloc(sizeof(*aspeed_jtag), &aspeed_jtag_ops); > + if (!jtag) > + return -ENODEV; > + > + 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 = jtag_register(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; > + > + jtag = platform_get_drvdata(pdev); > + aspeed_jtag_deinit(pdev, jtag_priv(jtag)); > + jtag_unregister(jtag); > + jtag_free(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"); > -- > 1.7.1 > -- Regards, Kun From 1584061622077044088@xxx Tue Nov 14 17:01:24 +0000 2017 X-GM-THRID: 1584061622077044088 X-Gmail-Labels: Inbox,Category Forums,HistoricalUnread