diff mbox

[v1,2/2] drivers: jtag: Add Aspeed SoC 24xx and 25xx families JTAG master driver

Message ID 1501679918-20486-3-git-send-email-oleksandrs@mellanox.com (mailing list archive)
State New, archived
Headers show

Commit Message

Oleksandr Shamray Aug. 2, 2017, 1:18 p.m. UTC
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 <oleksandrs@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 drivers/jtag/Kconfig       |   13 +
 drivers/jtag/Makefile      |    1 +
 drivers/jtag/jtag-aspeed.c |  802 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 816 insertions(+), 0 deletions(-)
 create mode 100644 drivers/jtag/jtag-aspeed.c

Comments

Neil Armstrong Aug. 2, 2017, 2:30 p.m. UTC | #1
On 08/02/2017 03:18 PM, 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 <oleksandrs@mellanox.com>
> Signed-off-by: Jiri Pirko <jiri@mellanox.com>
> ---
>  drivers/jtag/Kconfig       |   13 +
>  drivers/jtag/Makefile      |    1 +
>  drivers/jtag/jtag-aspeed.c |  802 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 816 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/jtag/jtag-aspeed.c
> 
> diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig
> index a8d0149..7bf709c 100644
> --- a/drivers/jtag/Kconfig
> +++ b/drivers/jtag/Kconfig
> @@ -16,3 +16,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
> +	---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 aspeed_jtag.
> +
> diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile
> index e811330..e9fa7fa 100644
> --- a/drivers/jtag/Makefile
> +++ b/drivers/jtag/Makefile
> @@ -1,2 +1,3 @@
>  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..b820824
> --- /dev/null
> +++ b/drivers/jtag/jtag-aspeed.c
> @@ -0,0 +1,802 @@
> +/*
> + * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
> + * Copyright (c) 2017 Oleksandr Shamray <oleksandrs@mellanox.com>
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions are met:
> + *
> + * 1. Redistributions of source code must retain the above copyright
> + *    notice, this list of conditions and the following disclaimer.
> + * 2. Redistributions in binary form must reproduce the above copyright
> + *    notice, this list of conditions and the following disclaimer in the
> + *    documentation and/or other materials provided with the distribution.
> + * 3. Neither the names of the copyright holders nor the names of its
> + *    contributors may be used to endorse or promote products derived from
> + *    this software without specific prior written permission.
> + *
> + * Alternatively, this software may be distributed under the terms of the
> + * GNU General Public License ("GPL") version 2 as published by the Free
> + * Software Foundation.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
> + * POSSIBILITY OF SUCH DAMAGE.
> + */

Please use SPDX-License-Identifier here aswell.

> +
> +#include <asm/mach-types.h>
> +#include <asm/mach/arch.h>
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/interrupt.h>
> +#include <linux/jtag.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <uapi/linux/jtag.h>
> +
> +#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"
> +
> +static int aspeed_jtag_freq_set(struct jtag *jtag, unsigned long freq);
> +static int aspeed_jtag_freq_get(struct jtag *jtag, unsigned long *frq);
> +static int aspeed_jtag_status_get(struct jtag *jtag,
> +				  enum jtag_endstate *status);
> +static int aspeed_jtag_idle(struct jtag *jtag,
> +			    struct jtag_run_test_idle *runtest);
> +static int aspeed_jtag_xfer(struct jtag *jtag, struct jtag_xfer *xfer);
> +
> +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;
> +	bool				is_open;
> +};
> +
> +static char *end_status_str[] = {"idle", "ir pause", "drpause"};
> +
> +static 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
> +};
> +
> +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);
> +}

Maybe readl_relaxed/writel_relaxed would be enough here.

> +
> +static int aspeed_jtag_freq_set(struct jtag *jtag, unsigned long freq)
> +{
> +	struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
> +	u16 div;
> +	u32 tck_val;
> +	unsigned long apb_frq;
> +
> +	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, unsigned long *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 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, 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)
> +{
> +	char sm_pause_irpause[] = {1, 1, 1, 1, 0, 1, 0};
> +	char sm_pause_drpause[] = {1, 1, 1, 0, 1, 0};
> +	char sm_idle_irpause[] = {1, 1, 0, 1, 0};
> +	char sm_idle_drpause[] = {1, 0, 1, 0};
> +	char 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, runtest->mode ? "SW" : "HW",
> +		end_status_str[runtest->endstate], runtest->reset,
> +		runtest->tck);
> +
> +	if (runtest->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, char *tdio_data)
> +{
> +	unsigned long remain_xfer = xfer->length;
> +	unsigned long shift_bits = 0;
> +	unsigned long index = 0;
> +	unsigned long tdi;
> +	char          tdo;
> +	unsigned long *data = (unsigned long *)tdio_data;
> +
> +	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, char *tdio_data)
> +{
> +	unsigned long remain_xfer = xfer->length;
> +	unsigned long *data = (unsigned long *)tdio_data;
> +	unsigned long shift_bits;
> +	unsigned long index = 0;
> +	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)
> +{
> +	unsigned long remain_xfer = xfer->length;
> +	unsigned long *data = (unsigned long *)xfer->tdio;
> +	struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
> +	char sm_update_shiftir[] = {1, 1, 0, 0};
> +	char sm_update_shiftdr[] = {1, 0, 0};
> +	char sm_pause_idle[] = {1, 1, 0};
> +	char sm_pause_update[] = {1, 1};
> +	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",
> +		xfer->mode ? "SW" : "HW",
> +		xfer->endstate, remain_xfer, dbg_str);
> +
> +	if (xfer->mode == JTAG_XFER_SW_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, xfer->tdio);
> +
> +		/* 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, xfer->tdio);
> +	}
> +
> +	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, enum jtag_endstate *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;
> +	u32 status;
> +	irqreturn_t ret;
> +
> +	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)) {
> +		err = -ENOMEM;
> +		goto out_region;
> +	}
> +
> +	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->irq = platform_get_irq(pdev, 0);
> +	if (aspeed_jtag->irq < 0) {
> +		dev_err(aspeed_jtag->dev, "no irq specified\n");
> +		err = -ENOENT;
> +		goto out_region;
> +	}
> +
> +	/* 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 out_region;
> +	}
> +	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;
> +	init_waitqueue_head(&aspeed_jtag->jtag_wq);
> +	return 0;
> +
> +out_region:
> +	release_mem_region(res->start, res->end - res->start + 1);
> +	return err;
> +}
> +
> +int aspeed_jtag_deinit(struct platform_device *pdev,
> +		       struct aspeed_jtag *aspeed_jtag)
> +{
> +	aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_ISR);
> +	devm_free_irq(aspeed_jtag->dev, aspeed_jtag->irq, aspeed_jtag);
> +	/* Disabe clock */
> +	aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_CTRL);

clk_prepare_disable ?

> +	return 0;
> +}
> +
> +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"))
> +		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,aspeed-jtag", },

Please use soc-specific compatible name.

> +	{}
> +};
> +
> +static struct platform_driver aspeed_jtag_driver = {
> +	.probe = aspeed_jtag_probe,
> +	.remove = aspeed_jtag_remove,
> +	.driver = {
> +		.name = ASPEED_JTAG_NAME,
> +		.of_match_table = aspeed_jtag_of_match,
> +	},
> +};
> +module_platform_driver(aspeed_jtag_driver);
> +
> +MODULE_AUTHOR("Oleksandr Shamray <oleksandrs@mellanox.com>");
> +MODULE_DESCRIPTION("ASPEED JTAG driver");
> +MODULE_LICENSE("Dual BSD/GPL");
> 

Hi Oleksandr,

Great work, but you forgot to add proper dt-bindings for the driver.

Neil
Arnd Bergmann Aug. 2, 2017, 2:54 p.m. UTC | #2
On Wed, Aug 2, 2017 at 3:18 PM, Oleksandr Shamray
<oleksandrs@mellanox.com> 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 <oleksandrs@mellanox.com>
> Signed-off-by: Jiri Pirko <jiri@mellanox.com>

Looking at this one before the subsystem. Overall looks really nice,
it seems you got a good abstraction between the subsystem and the
driver.

> +
> +static int aspeed_jtag_freq_set(struct jtag *jtag, unsigned long freq);
> +static int aspeed_jtag_freq_get(struct jtag *jtag, unsigned long *frq);
> +static int aspeed_jtag_status_get(struct jtag *jtag,
> +                                 enum jtag_endstate *status);
> +static int aspeed_jtag_idle(struct jtag *jtag,
> +                           struct jtag_run_test_idle *runtest);
> +static int aspeed_jtag_xfer(struct jtag *jtag, struct jtag_xfer *xfer);

Please try to reorder the functions definitions in a way that lets you
remove the forward declarations.

> +
> +static void aspeed_jtag_run_test_idle_sw(struct aspeed_jtag *aspeed_jtag,
> +                                        struct jtag_run_test_idle *runtest)
> +{
> +       char sm_pause_irpause[] = {1, 1, 1, 1, 0, 1, 0};
> +       char sm_pause_drpause[] = {1, 1, 1, 0, 1, 0};
> +       char sm_idle_irpause[] = {1, 1, 0, 1, 0};
> +       char sm_idle_drpause[] = {1, 0, 1, 0};
> +       char sm_pause_idle[] = {1, 1, 0};

These could be 'static const' if you adapt the aspeed_jtag_sm_cycle
prototype accordingly.

> +
> +static const struct of_device_id aspeed_jtag_of_match[] = {
> +       { .compatible = "aspeed,aspeed-jtag", },
> +       {}
> +};

The series should include a patch for the DT binding for this device.
You may want to be a little more specific here, to avoid problems if
aspeed ever makes an updated version of this device with a slightly
different register interface. Usually we include the full name of the
SoC in the "compatible" string for that.

       Arnd
Arnd Bergmann Aug. 2, 2017, 2:56 p.m. UTC | #3
On Wed, Aug 2, 2017 at 4:30 PM, Neil Armstrong <narmstrong@baylibre.com> wrote:
> On 08/02/2017 03:18 PM, Oleksandr Shamray wrote:
>> Driver adds support of Aspeed 2500/2400 series SOC JTAG master controller.

>> +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);
>> +}
>
> Maybe readl_relaxed/writel_relaxed would be enough here.

I'd prefer keeping the regular accessors here, unless this is shown
to be a performance bottleneck, and there is a comment to explain
how the relaxed accessors are determined to be safe.

        Arnd
Randy Dunlap Aug. 2, 2017, 3:31 p.m. UTC | #4
On 08/02/2017 06:18 AM, Oleksandr Shamray wrote:
> 
> diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig
> index a8d0149..7bf709c 100644
> --- a/drivers/jtag/Kconfig
> +++ b/drivers/jtag/Kconfig
> @@ -16,3 +16,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
> +	---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 aspeed_jtag.

In the Makefile, it looks like it is called jtag-aspeed.

> +
> diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile
> index e811330..e9fa7fa 100644
> --- a/drivers/jtag/Makefile
> +++ b/drivers/jtag/Makefile
> @@ -1,2 +1,3 @@
>  obj-$(CONFIG_JTAG)		+= jtag.o
> +obj-$(CONFIG_JTAG_ASPEED)	+= jtag-aspeed.o
kernel test robot Aug. 3, 2017, 12:12 p.m. UTC | #5
Hi Oleksandr,

[auto build test ERROR on linus/master]
[also build test ERROR on v4.13-rc3 next-20170802]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Oleksandr-Shamray/JTAG-driver-introduction/20170803-110721
config: arm64-allmodconfig (attached as .config)
compiler: aarch64-linux-gnu-gcc (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
        wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=arm64 

All errors (new ones prefixed by >>):

>> drivers/jtag/jtag-aspeed.c:34:28: fatal error: asm/mach-types.h: No such file or directory
    #include <asm/mach-types.h>
                               ^
   compilation terminated.

vim +34 drivers/jtag/jtag-aspeed.c

  > 34	#include <asm/mach-types.h>
    35	#include <asm/mach/arch.h>
    36	#include <linux/clk.h>
    37	#include <linux/device.h>
    38	#include <linux/interrupt.h>
    39	#include <linux/jtag.h>
    40	#include <linux/kernel.h>
    41	#include <linux/module.h>
    42	#include <linux/of_address.h>
    43	#include <linux/platform_device.h>
    44	#include <linux/slab.h>
    45	#include <uapi/linux/jtag.h>
    46	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
kernel test robot Aug. 3, 2017, 2:35 p.m. UTC | #6
Hi Oleksandr,

[auto build test WARNING on linus/master]
[also build test WARNING on v4.13-rc3 next-20170803]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Oleksandr-Shamray/JTAG-driver-introduction/20170803-110721


coccinelle warnings: (new ones prefixed by >>)

>> drivers/jtag/jtag-aspeed.c:724:37-40: ERROR: Missing resource_size with res

Please review and possibly fold the followup patch.

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
diff mbox

Patch

diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig
index a8d0149..7bf709c 100644
--- a/drivers/jtag/Kconfig
+++ b/drivers/jtag/Kconfig
@@ -16,3 +16,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
+	---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 aspeed_jtag.
+
diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile
index e811330..e9fa7fa 100644
--- a/drivers/jtag/Makefile
+++ b/drivers/jtag/Makefile
@@ -1,2 +1,3 @@ 
 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..b820824
--- /dev/null
+++ b/drivers/jtag/jtag-aspeed.c
@@ -0,0 +1,802 @@ 
+/*
+ * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017 Oleksandr Shamray <oleksandrs@mellanox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/jtag.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <uapi/linux/jtag.h>
+
+#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"
+
+static int aspeed_jtag_freq_set(struct jtag *jtag, unsigned long freq);
+static int aspeed_jtag_freq_get(struct jtag *jtag, unsigned long *frq);
+static int aspeed_jtag_status_get(struct jtag *jtag,
+				  enum jtag_endstate *status);
+static int aspeed_jtag_idle(struct jtag *jtag,
+			    struct jtag_run_test_idle *runtest);
+static int aspeed_jtag_xfer(struct jtag *jtag, struct jtag_xfer *xfer);
+
+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;
+	bool				is_open;
+};
+
+static char *end_status_str[] = {"idle", "ir pause", "drpause"};
+
+static 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
+};
+
+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, unsigned long freq)
+{
+	struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+	u16 div;
+	u32 tck_val;
+	unsigned long apb_frq;
+
+	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, unsigned long *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 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, 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)
+{
+	char sm_pause_irpause[] = {1, 1, 1, 1, 0, 1, 0};
+	char sm_pause_drpause[] = {1, 1, 1, 0, 1, 0};
+	char sm_idle_irpause[] = {1, 1, 0, 1, 0};
+	char sm_idle_drpause[] = {1, 0, 1, 0};
+	char 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, runtest->mode ? "SW" : "HW",
+		end_status_str[runtest->endstate], runtest->reset,
+		runtest->tck);
+
+	if (runtest->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, char *tdio_data)
+{
+	unsigned long remain_xfer = xfer->length;
+	unsigned long shift_bits = 0;
+	unsigned long index = 0;
+	unsigned long tdi;
+	char          tdo;
+	unsigned long *data = (unsigned long *)tdio_data;
+
+	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, char *tdio_data)
+{
+	unsigned long remain_xfer = xfer->length;
+	unsigned long *data = (unsigned long *)tdio_data;
+	unsigned long shift_bits;
+	unsigned long index = 0;
+	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)
+{
+	unsigned long remain_xfer = xfer->length;
+	unsigned long *data = (unsigned long *)xfer->tdio;
+	struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+	char sm_update_shiftir[] = {1, 1, 0, 0};
+	char sm_update_shiftdr[] = {1, 0, 0};
+	char sm_pause_idle[] = {1, 1, 0};
+	char sm_pause_update[] = {1, 1};
+	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",
+		xfer->mode ? "SW" : "HW",
+		xfer->endstate, remain_xfer, dbg_str);
+
+	if (xfer->mode == JTAG_XFER_SW_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, xfer->tdio);
+
+		/* 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, xfer->tdio);
+	}
+
+	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, enum jtag_endstate *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;
+	u32 status;
+	irqreturn_t ret;
+
+	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)) {
+		err = -ENOMEM;
+		goto out_region;
+	}
+
+	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);
+	}
+
+	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 out_region;
+	}
+
+	/* 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 out_region;
+	}
+	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;
+	init_waitqueue_head(&aspeed_jtag->jtag_wq);
+	return 0;
+
+out_region:
+	release_mem_region(res->start, res->end - res->start + 1);
+	return err;
+}
+
+int aspeed_jtag_deinit(struct platform_device *pdev,
+		       struct aspeed_jtag *aspeed_jtag)
+{
+	aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_ISR);
+	devm_free_irq(aspeed_jtag->dev, aspeed_jtag->irq, aspeed_jtag);
+	/* Disabe clock */
+	aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_CTRL);
+	return 0;
+}
+
+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"))
+		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,aspeed-jtag", },
+	{}
+};
+
+static struct platform_driver aspeed_jtag_driver = {
+	.probe = aspeed_jtag_probe,
+	.remove = aspeed_jtag_remove,
+	.driver = {
+		.name = ASPEED_JTAG_NAME,
+		.of_match_table = aspeed_jtag_of_match,
+	},
+};
+module_platform_driver(aspeed_jtag_driver);
+
+MODULE_AUTHOR("Oleksandr Shamray <oleksandrs@mellanox.com>");
+MODULE_DESCRIPTION("ASPEED JTAG driver");
+MODULE_LICENSE("Dual BSD/GPL");