/***************************************************************************
 *   Copyright (C) 2009 by Mathias Kuester                                 *
 *   mkdorg@users.sourceforge.net                                          *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <helper/jim.h>

#include "target.h"
#include "target_type.h"
#include "register.h"
#include "dsp563xx.h"
#include "dsp563xx_once.h"

#define DSP563XX_JTAG_INS_LEN		4

#define JTAG_STATUS_NORMAL		0x01
#define JTAG_STATUS_STOPWAIT		0x05
#define JTAG_STATUS_BUSY		0x09
#define JTAG_STATUS_DEBUG		0x0d

#define JTAG_INSTR_EXTEST		0x00
#define JTAG_INSTR_SAMPLE_PRELOAD	0x01
#define JTAG_INSTR_IDCODE		0x02
#define JTAG_INSTR_CLAMP		0x03
#define JTAG_INSTR_HIZ			0x04
#define JTAG_INSTR_ENABLE_ONCE		0x06
#define JTAG_INSTR_DEBUG_REQUEST	0x07
#define JTAG_INSTR_BYPASS		0x0F

/* forward declarations */
static int dsp563xx_write_ir_u8(struct jtag_tap *tap, uint8_t * ir_in, uint8_t ir_out,
		int ir_len, int rti);

/* IR and DR functions */
static int dsp563xx_jtag_sendinstr(struct jtag_tap *tap, uint8_t * ir_in, uint8_t ir_out);

#define ASM_REG_R_R0	0x607000
#define ASM_REG_R_R1	0x617000
#define ASM_REG_R_R2	0x627000
#define ASM_REG_R_R3	0x637000
#define ASM_REG_R_R4	0x647000
#define ASM_REG_R_R5	0x657000
#define ASM_REG_R_R6	0x667000
#define ASM_REG_R_R7	0x677000

#define ASM_REG_W_R0	0x60F400
#define ASM_REG_W_R1	0x61F400
#define ASM_REG_W_R2	0x62F400
#define ASM_REG_W_R3	0x63F400
#define ASM_REG_W_R4	0x64F400
#define ASM_REG_W_R5	0x65F400
#define ASM_REG_W_R6	0x66F400
#define ASM_REG_W_R7	0x67F400

#define ASM_REG_R_N0	0x707000
#define ASM_REG_R_N1	0x717000
#define ASM_REG_R_N2	0x727000
#define ASM_REG_R_N3	0x737000
#define ASM_REG_R_N4	0x747000
#define ASM_REG_R_N5	0x757000
#define ASM_REG_R_N6	0x767000
#define ASM_REG_R_N7	0x777000

#define ASM_REG_W_N0	0x70F400
#define ASM_REG_W_N1	0x71F400
#define ASM_REG_W_N2	0x72F400
#define ASM_REG_W_N3	0x73F400
#define ASM_REG_W_N4	0x74F400
#define ASM_REG_W_N5	0x75F400
#define ASM_REG_W_N6	0x76F400
#define ASM_REG_W_N7	0x77F400

#define ASM_REG_R_M0	0x057020	/* control register m[0..7] */
#define ASM_REG_R_M1	0x057021
#define ASM_REG_R_M2	0x057022
#define ASM_REG_R_M3	0x057023
#define ASM_REG_R_M4	0x057024
#define ASM_REG_R_M5	0x057025
#define ASM_REG_R_M6	0x057026
#define ASM_REG_R_M7	0x057027

#define ASM_REG_W_M0	0x05F420
#define ASM_REG_W_M1	0x05F421
#define ASM_REG_W_M2	0x05F422
#define ASM_REG_W_M3	0x05F423
#define ASM_REG_W_M4	0x05F424
#define ASM_REG_W_M5	0x05F425
#define ASM_REG_W_M6	0x05F426
#define ASM_REG_W_M7	0x05F427

#define ASM_REG_R_X0	0x447000
#define ASM_REG_R_X1	0x457000

#define ASM_REG_W_X0	0x44F400
#define ASM_REG_W_X1	0x45F400

#define ASM_REG_R_Y0	0x467000
#define ASM_REG_R_Y1	0x477000

#define ASM_REG_W_Y0	0x46F400
#define ASM_REG_W_Y1	0x47F400

#define ASM_REG_R_A0	0x507000
#define ASM_REG_R_A1	0x547000
#define ASM_REG_R_A2	0x527000

#define ASM_REG_W_A0	0x50F400
#define ASM_REG_W_A1	0x54F400
#define ASM_REG_W_A2	0x52F400

#define ASM_REG_R_B0	0x517000
#define ASM_REG_R_B1	0x557000
#define ASM_REG_R_B2	0x537000

#define ASM_REG_W_B0	0x51F400
#define ASM_REG_W_B1	0x55F400
#define ASM_REG_W_B2	0x53F400

#define ASM_REG_R_VBA	0x057030	/* control register */
#define ASM_REG_W_VBA	0x05F430

#define ASM_REG_R_OMR	0x05703A	/* control register */
#define ASM_REG_W_OMR	0x05F43A

#define ASM_REG_R_EP	0x05702A
#define ASM_REG_W_EP	0x05F42A

#define ASM_REG_R_SC	0x057031	/* stack counter */
#define ASM_REG_W_SC	0x05F431

#define ASM_REG_R_SZ	0x057038	/* stack size */
#define ASM_REG_W_SZ	0x05F438

#define ASM_REG_R_SR	0x057039	/* control register, status register */
#define ASM_REG_W_SR	0x05F439

#define ASM_REG_R_SP	0x05703B	/* control register, stack pointer */
#define ASM_REG_W_SP	0x05F43B

#define ASM_REG_R_SSH	0x05703C	/* control register, system stack high */
#define ASM_REG_W_SSH	0x05743C

#define ASM_REG_R_SSL	0x05703D	/* control register, system stack low */
#define ASM_REG_W_SSL	0x05F43D

#define ASM_REG_R_LA	0x05703E	/* control register, loop address */
#define ASM_REG_W_LA	0x05F43E

#define ASM_REG_R_LC	0x05703F	/* control register, loop count */
#define ASM_REG_W_LC	0x05F43F

#define ASM_REG_R_PC	0x000000
#define ASM_REG_W_PC	0x000000

static const struct
{
	unsigned id;
	char *name;
	unsigned bits;
	uint32_t r_cmd;
	uint32_t w_cmd;
} dsp563xx_regs[] =
{
	/* *INDENT-OFF* */
	{0, "r0", 24, ASM_REG_R_R0, ASM_REG_W_R0},
	{1, "r1", 24, ASM_REG_R_R1, ASM_REG_W_R1},
	{2, "r2", 24, ASM_REG_R_R2, ASM_REG_W_R2},
	{3, "r3", 24, ASM_REG_R_R3, ASM_REG_W_R3},
	{4, "r4", 24, ASM_REG_R_R4, ASM_REG_W_R4},
	{5, "r5", 24, ASM_REG_R_R5, ASM_REG_W_R5},
	{6, "r6", 24, ASM_REG_R_R6, ASM_REG_W_R6},
	{7, "r7", 24, ASM_REG_R_R7, ASM_REG_W_R7},
	{8, "n0", 24, ASM_REG_R_N0, ASM_REG_W_N0},
	{9, "n1", 24, ASM_REG_R_N1, ASM_REG_W_N1},
	{10, "n2", 24, ASM_REG_R_N2, ASM_REG_W_N2},
	{11, "n3", 24, ASM_REG_R_N3, ASM_REG_W_N3},
	{12, "n4", 24, ASM_REG_R_N4, ASM_REG_W_N4},
	{13, "n5", 24, ASM_REG_R_N5, ASM_REG_W_N5},
	{14, "n6", 24, ASM_REG_R_N6, ASM_REG_W_N6},
	{15, "n7", 24, ASM_REG_R_N7, ASM_REG_W_N7},
	{16, "m0", 24, ASM_REG_R_M0, ASM_REG_W_M0},
	{17, "m1", 24, ASM_REG_R_M1, ASM_REG_W_M1},
	{18, "m2", 24, ASM_REG_R_M2, ASM_REG_W_M2},
	{19, "m3", 24, ASM_REG_R_M3, ASM_REG_W_M3},
	{20, "m4", 24, ASM_REG_R_M4, ASM_REG_W_M4},
	{21, "m5", 24, ASM_REG_R_M5, ASM_REG_W_M5},
	{22, "m6", 24, ASM_REG_R_M6, ASM_REG_W_M6},
	{23, "m7", 24, ASM_REG_R_M7, ASM_REG_W_M7},
	{24, "x0", 24, ASM_REG_R_X0, ASM_REG_W_X0},
	{25, "x1", 24, ASM_REG_R_X1, ASM_REG_W_X1},
	{26, "y0", 24, ASM_REG_R_Y0, ASM_REG_W_Y0},
	{27, "y1", 24, ASM_REG_R_Y1, ASM_REG_W_Y1},
	{28, "a0", 24, ASM_REG_R_A0, ASM_REG_W_A0},
	{29, "a1", 24, ASM_REG_R_A1, ASM_REG_W_A1},
	{30, "a2", 8, ASM_REG_R_A2, ASM_REG_W_A2},
	{31, "b0", 24, ASM_REG_R_B0, ASM_REG_W_B0},
	{32, "b1", 24, ASM_REG_R_B1, ASM_REG_W_B1},
	{33, "b2", 8, ASM_REG_R_B2, ASM_REG_W_B2},
	{34, "omr", 24, ASM_REG_R_OMR, ASM_REG_W_OMR},
	{35, "vba", 24, ASM_REG_R_VBA, ASM_REG_W_VBA},
	{36, "ep", 24, ASM_REG_R_EP, ASM_REG_W_EP},
	{37, "sc", 24, ASM_REG_R_SC, ASM_REG_W_SC},
	{38, "sz", 24, ASM_REG_R_SZ, ASM_REG_W_SZ},
	{39, "sr", 24, ASM_REG_R_SR, ASM_REG_W_SR},
	{40, "sp", 24, ASM_REG_R_SP, ASM_REG_W_SP},
	{41, "la", 24, ASM_REG_R_LA, ASM_REG_W_LA},
	{42, "lc", 24, ASM_REG_R_LC, ASM_REG_W_LC},
	{43, "pc", 24, ASM_REG_R_PC, ASM_REG_W_PC}
	/* *INDENT-ON* */
};

static int dsp563xx_get_gdb_reg_list(struct target *target, struct reg **reg_list[],
			      int *reg_list_size)
{
	struct dsp563xx_common *dsp563xx = target_to_dsp563xx(target);
	int i;

	if (target->state != TARGET_HALTED)
	{
		return ERROR_TARGET_NOT_HALTED;
	}

	*reg_list_size = DSP563XX_NUMCOREREGS;
	*reg_list = malloc(sizeof(struct reg *) * (*reg_list_size));

	for (i = 0; i < DSP563XX_NUMCOREREGS; i++)
	{
		(*reg_list)[i] = &dsp563xx->core_cache->reg_list[i];
	}

	return ERROR_OK;

}

static int dsp563xx_read_core_reg(struct target *target, int num)
{
	uint32_t reg_value;
	struct dsp563xx_core_reg *dsp563xx_core_reg;
	struct dsp563xx_common *dsp563xx = target_to_dsp563xx(target);

	if ((num < 0) || (num >= DSP563XX_NUMCOREREGS))
		return ERROR_INVALID_ARGUMENTS;

	dsp563xx_core_reg = dsp563xx->core_cache->reg_list[num].arch_info;
	reg_value = dsp563xx->core_regs[num];
	buf_set_u32(dsp563xx->core_cache->reg_list[num].value, 0, 32, reg_value);
	dsp563xx->core_cache->reg_list[num].valid = 1;
	dsp563xx->core_cache->reg_list[num].dirty = 0;

	return ERROR_OK;
}

static int dsp563xx_write_core_reg(struct target *target, int num)
{
	uint32_t reg_value;
	struct dsp563xx_core_reg *dsp563xx_core_reg;
	struct dsp563xx_common *dsp563xx = target_to_dsp563xx(target);

	if ((num < 0) || (num >= DSP563XX_NUMCOREREGS))
		return ERROR_INVALID_ARGUMENTS;

	reg_value = buf_get_u32(dsp563xx->core_cache->reg_list[num].value, 0, 32);
	dsp563xx_core_reg = dsp563xx->core_cache->reg_list[num].arch_info;
	dsp563xx->core_regs[num] = reg_value;
	dsp563xx->core_cache->reg_list[num].valid = 1;
	dsp563xx->core_cache->reg_list[num].dirty = 0;

	return ERROR_OK;
}

static int dsp563xx_target_create(struct target *target, Jim_Interp * interp)
{
	struct dsp563xx_common *dsp563xx = calloc(1, sizeof(struct dsp563xx_common));

	dsp563xx->jtag_info.tap = target->tap;
	target->arch_info = dsp563xx;
	dsp563xx->read_core_reg = dsp563xx_read_core_reg;
	dsp563xx->write_core_reg = dsp563xx_write_core_reg;

	return ERROR_OK;
}

static int dsp563xx_get_core_reg(struct reg *reg)
{
	int retval = 0;

	LOG_DEBUG("%s", __FUNCTION__);

	struct dsp563xx_core_reg *dsp563xx_reg = reg->arch_info;
	struct target *target = dsp563xx_reg->target;
	struct dsp563xx_common *dsp563xx = target_to_dsp563xx(target);

	if (target->state != TARGET_HALTED)
	{
		return ERROR_TARGET_NOT_HALTED;
	}

	retval = dsp563xx->read_core_reg(target, dsp563xx_reg->num);

	return retval;
}

static int dsp563xx_set_core_reg(struct reg *reg, uint8_t * buf)
{
	LOG_DEBUG("%s", __FUNCTION__);

	struct dsp563xx_core_reg *dsp563xx_reg = reg->arch_info;
	struct target *target = dsp563xx_reg->target;
	uint32_t value = buf_get_u32(buf, 0, 32);

	if (target->state != TARGET_HALTED)
	{
		return ERROR_TARGET_NOT_HALTED;
	}

	buf_set_u32(reg->value, 0, reg->size, value);
	reg->dirty = 1;
	reg->valid = 1;

	return ERROR_OK;
}

static int dsp563xx_save_context(struct target *target)
{
	int i;
	uint32_t data = 0;
	struct dsp563xx_common *dsp563xx = target_to_dsp563xx(target);
	struct dsp563xx_core_reg *arch_info;

	for (i = 0; i < DSP563XX_NUMCOREREGS - 1; i++)
	{

//              if (!dsp563xx->core_cache->reg_list[i].valid)
		{
			arch_info = dsp563xx->core_cache->reg_list[i].arch_info;
			dsp563xx_once_execute_dw_ir(target->tap, arch_info->r_cmd,
						    0xfffffc);
			dsp563xx_once_execute_sw_ir(target->tap, 0x000000);
			dsp563xx_once_reg_read(target->tap, DSP563XX_ONCE_OGDBR,
					       &data);
			dsp563xx->core_regs[i] = data;
			dsp563xx->read_core_reg(target, i);
		}
	}

	/* read pc */
	dsp563xx_once_reg_read(target->tap, DSP563XX_ONCE_OPABEX, &data);
	dsp563xx->core_regs[i] = data;
	dsp563xx->read_core_reg(target, i);

	return ERROR_OK;
}

static int dsp563xx_restore_context(struct target *target)
{
	int i;
	struct dsp563xx_common *dsp563xx = target_to_dsp563xx(target);
	struct dsp563xx_core_reg *arch_info;

	for (i = 0; i < DSP563XX_NUMCOREREGS - 1; i++)
	{
		if (dsp563xx->core_cache->reg_list[i].dirty)
		{
			arch_info = dsp563xx->core_cache->reg_list[i].arch_info;

			dsp563xx->write_core_reg(target, i);

			dsp563xx_once_execute_dw_ir(target->tap, arch_info->w_cmd,
						    dsp563xx->core_regs[i]);
			dsp563xx_once_execute_sw_ir(target->tap, 0x000000);
		}
	}

	return ERROR_OK;
}

static const struct reg_arch_type dsp563xx_reg_type = {
	.get = dsp563xx_get_core_reg,
	.set = dsp563xx_set_core_reg,
};

static int dsp563xx_init_target(struct command_context *cmd_ctx, struct target *target)
{
	/* get pointers to arch-specific information */
	struct dsp563xx_common *dsp563xx = target_to_dsp563xx(target);

	struct reg_cache **cache_p = register_get_last_cache_p(&target->reg_cache);
	struct reg_cache *cache = malloc(sizeof(struct reg_cache));
	struct reg *reg_list = malloc(sizeof(struct reg) * DSP563XX_NUMCOREREGS);
	struct dsp563xx_core_reg *arch_info =
		malloc(sizeof(struct dsp563xx_core_reg) * DSP563XX_NUMCOREREGS);
	int i;

	LOG_DEBUG("%s", __FUNCTION__);

	/* Build the process context cache */
	cache->name = "dsp563xx registers";
	cache->next = NULL;
	cache->reg_list = reg_list;
	cache->num_regs = DSP563XX_NUMCOREREGS;
	(*cache_p) = cache;
	dsp563xx->core_cache = cache;

	for (i = 0; i < DSP563XX_NUMCOREREGS; i++)
	{
		arch_info[i].num = dsp563xx_regs[i].id;
		arch_info[i].name = dsp563xx_regs[i].name;
		arch_info[i].size = dsp563xx_regs[i].bits;
		arch_info[i].r_cmd = dsp563xx_regs[i].r_cmd;
		arch_info[i].w_cmd = dsp563xx_regs[i].w_cmd;
		arch_info[i].target = target;
		arch_info[i].dsp563xx_common = dsp563xx;
		reg_list[i].name = dsp563xx_regs[i].name;
		reg_list[i].size = dsp563xx_regs[i].bits;
		reg_list[i].value = calloc(1, 4);
		reg_list[i].dirty = 0;
		reg_list[i].valid = 0;
		reg_list[i].type = &dsp563xx_reg_type;
		reg_list[i].arch_info = &arch_info[i];
	}

	return ERROR_OK;
}

static int dsp563xx_arch_state(struct target *target)
{
	LOG_DEBUG("%s", __FUNCTION__);
	return ERROR_OK;
}

static int dsp563xx_jtag_status(struct target *target, uint8_t * status)
{
	uint8_t ir_in;

	ir_in = 0;

	dsp563xx_jtag_sendinstr(target->tap, &ir_in, JTAG_INSTR_ENABLE_ONCE);
	dsp563xx_execute_queue();

	*status = ir_in;

	return ERROR_OK;
}

static int dsp563xx_jtag_debug_request(struct target *target)
{
	uint8_t ir_in = 0;
	uint32_t retry = 0;

	while (ir_in != JTAG_STATUS_DEBUG)
	{
		dsp563xx_jtag_sendinstr(target->tap, &ir_in,
					JTAG_INSTR_DEBUG_REQUEST);
		dsp563xx_execute_queue();
		LOG_DEBUG("JTAG CMD 7 res: %02X", ir_in);
		dsp563xx_jtag_sendinstr(target->tap, &ir_in, JTAG_INSTR_ENABLE_ONCE);
		dsp563xx_execute_queue();
		LOG_DEBUG("JTAG CMD 6 res: %02X", ir_in);

		if (retry++ == 100)
			return ERROR_TARGET_FAILURE;
	}

	if (ir_in != JTAG_STATUS_DEBUG)
	{
		return ERROR_TARGET_FAILURE;
	}

	return ERROR_OK;
}

static int dsp563xx_poll(struct target *target)
{
	uint8_t jtag_status;
	uint32_t once_status;

	dsp563xx_jtag_status(target, &jtag_status);

	if ((jtag_status & 1) != 1)
	{
		target->state = TARGET_UNKNOWN;
		LOG_ERROR
			("jtag status contains invalid mode value - communication failure");
		return ERROR_TARGET_FAILURE;
	}

	if (jtag_status != JTAG_STATUS_DEBUG)
	{
		target->state = TARGET_RUNNING;
	}

	dsp563xx_once_reg_read(target->tap, DSP563XX_ONCE_OSCR, &once_status);

	if ((once_status & DSP563XX_ONCE_OSCR_DEBUG_M) == DSP563XX_ONCE_OSCR_DEBUG_M)
	{
		target->state = TARGET_HALTED;

	}

	return ERROR_OK;
}

static int dsp563xx_halt(struct target *target)
{
	uint8_t jtag_status;
	uint32_t once_status;
	struct dsp563xx_common *dsp563xx = target_to_dsp563xx(target);

	if (target->state == TARGET_HALTED)
	{
		LOG_DEBUG("target was already halted");
		return ERROR_OK;
	}

	if (target->state == TARGET_UNKNOWN)
	{
		LOG_WARNING("target was in unknown state when halt was requested");
	}

//      if ( jtag_status != 0x0d )
	{
		dsp563xx_jtag_debug_request(target);

		/* store pipeline register */
		dsp563xx_once_reg_read(target->tap, DSP563XX_ONCE_OPILR,
				       &dsp563xx->pipeline_context.once_opilr);
		dsp563xx_once_reg_read(target->tap, DSP563XX_ONCE_OPDBR,
				       &dsp563xx->pipeline_context.once_opdbr);

		dsp563xx_save_context(target);

		dsp563xx_jtag_status(target, &jtag_status);
		LOG_DEBUG("%02X", jtag_status);
		dsp563xx_once_reg_read(target->tap, DSP563XX_ONCE_OSCR,
				       &once_status);
		LOG_DEBUG("%02X", (unsigned) once_status);
	}

	LOG_DEBUG("target->state: %s", target_state_name(target));

	LOG_DEBUG("%s", __FUNCTION__);

	return ERROR_OK;
}

#define DSP563XX_ASM_CMD_JUMP	0x0AF080

static int dsp563xx_resume(struct target *target, int current, uint32_t address,
		    int handle_breakpoints, int debug_execution)
{
	struct dsp563xx_common *dsp563xx = target_to_dsp563xx(target);

	LOG_DEBUG("%s", __FUNCTION__);

	dsp563xx_restore_context(target);

	if (current)
	{
		/* restore pipeline registers and go */
		dsp563xx_once_reg_write(target->tap, DSP563XX_ONCE_OPILR,
					dsp563xx->pipeline_context.once_opilr);
		dsp563xx_once_reg_write(target->tap,
					DSP563XX_ONCE_OPDBR | DSP563XX_ONCE_OCR_EX |
					DSP563XX_ONCE_OCR_GO,
					dsp563xx->pipeline_context.once_opdbr);
	}
	else
	{
		/* set to go register and jump */
		dsp563xx_once_reg_write(target->tap, DSP563XX_ONCE_OPDBR,
					DSP563XX_ASM_CMD_JUMP);
		dsp563xx_once_reg_write(target->tap,
					DSP563XX_ONCE_PDBGOTO | DSP563XX_ONCE_OCR_EX
					| DSP563XX_ONCE_OCR_GO, address);
	}

	target->state = TARGET_RUNNING;

	return ERROR_OK;
}

static int dsp563xx_step(struct target *target, int current, uint32_t address,
		  int handle_breakpoints)
{
	uint32_t once_status;
	uint32_t dr_in, cnt;
	struct dsp563xx_common *dsp563xx = target_to_dsp563xx(target);

	if (target->state != TARGET_HALTED)
	{
		LOG_DEBUG("target was not halted");
		return ERROR_OK;
	}

	LOG_DEBUG("%s %08X %08X", __FUNCTION__, current, (unsigned) address);

	dsp563xx_jtag_debug_request(target);

	dsp563xx_restore_context(target);

	/* reset trace mode */
	dsp563xx_once_reg_write(target->tap, DSP563XX_ONCE_OSCR, 0x000000);
	/* enable trace mode */
	dsp563xx_once_reg_write(target->tap, DSP563XX_ONCE_OSCR,
				DSP563XX_ONCE_OSCR_TME);

	cnt = 0;

	/* on JUMP we need one extra cycle */
	if (!current)
		cnt++;

	/* load step counter with N-1 */
	dsp563xx_once_reg_write(target->tap, DSP563XX_ONCE_OTC, cnt);

	if (current)
	{
		/* restore pipeline registers and go */
		dsp563xx_once_reg_write(target->tap, DSP563XX_ONCE_OPILR,
					dsp563xx->pipeline_context.once_opilr);
		dsp563xx_once_reg_write(target->tap,
					DSP563XX_ONCE_OPDBR | DSP563XX_ONCE_OCR_EX |
					DSP563XX_ONCE_OCR_GO,
					dsp563xx->pipeline_context.once_opdbr);
	}
	else
	{
		/* set to go register and jump */
		dsp563xx_once_reg_write(target->tap, DSP563XX_ONCE_OPDBR,
					DSP563XX_ASM_CMD_JUMP);
		dsp563xx_once_reg_write(target->tap,
					DSP563XX_ONCE_PDBGOTO | DSP563XX_ONCE_OCR_EX
					| DSP563XX_ONCE_OCR_GO, address);
	}

	while (1)
	{
		dsp563xx_once_reg_read(target->tap, DSP563XX_ONCE_OSCR,
				       &once_status);

		if (once_status & DSP563XX_ONCE_OSCR_TO)
		{
			/* store pipeline register */
			dsp563xx_once_reg_read(target->tap, DSP563XX_ONCE_OPILR,
					       &dsp563xx->pipeline_context.
					       once_opilr);
			dsp563xx_once_reg_read(target->tap, DSP563XX_ONCE_OPDBR,
					       &dsp563xx->pipeline_context.
					       once_opdbr);

			dsp563xx_save_context(target);

			dsp563xx_once_reg_read(target->tap, DSP563XX_ONCE_OPABFR,
					       &dr_in);
			LOG_DEBUG("%08X", (unsigned) dr_in);
			dsp563xx_once_reg_read(target->tap, DSP563XX_ONCE_OPABDR,
					       &dr_in);
			LOG_DEBUG("%08X", (unsigned) dr_in);
			dsp563xx_once_reg_read(target->tap, DSP563XX_ONCE_OPABEX,
					       &dr_in);
			LOG_DEBUG("%08X", (unsigned) dr_in);

			/* reset trace mode */
			dsp563xx_once_reg_write(target->tap, DSP563XX_ONCE_OSCR,
						0x000000);

			break;
		}
	}

	return ERROR_OK;
}

static int dsp563xx_assert_reset(struct target *target)
{
	target->state = TARGET_RESET;

	LOG_DEBUG("%s", __FUNCTION__);
	return ERROR_OK;
}

static int dsp563xx_deassert_reset(struct target *target)
{
	target->state = TARGET_RUNNING;

	LOG_DEBUG("%s", __FUNCTION__);
	return ERROR_OK;
}

static int dsp563xx_soft_reset_halt(struct target *target)
{
	LOG_DEBUG("%s", __FUNCTION__);
	return ERROR_OK;
}

/*
* 000000			nop
* 46F400 AABBCC		move              #$aabbcc,y0
* 60F400 AABBCC		move              #$aabbcc,r0
* 467000 AABBCC		move              y0,x:AABBCC
* 607000 AABBCC		move              r0,x:AABBCC

* 46E000		move              x:(r0),y0
* 4EE000		move              y:(r0),y0
* 07E086		move              p:(r0),y0

* 0450B9		move              sr,r0
* 0446BA		move              omr,y0
* 0446BC		move              ssh,y0
* 0446BD		move              ssl,y0
* 0446BE		move              la,y0
* 0446BF		move              lc,y0
* 
* 61F000 AABBCC		move              x:AABBCC,r1
* 076190		movem             r0,p:(r1)
*
*/
static int dsp563xx_read_memory_p(struct target *target, uint32_t address,
			   uint32_t size, uint32_t count, uint8_t * buffer)
{
	uint32_t i, x;
	uint32_t data;
	uint8_t *b;

	LOG_DEBUG("address: 0x%8.8" PRIx32 ", size: 0x%8.8" PRIx32 ", count: 0x%8.8"
		  PRIx32, address, size, count);

	if (target->state != TARGET_HALTED)
	{
		LOG_WARNING("target not halted");
		return ERROR_TARGET_NOT_HALTED;
	}

	x = count;

	for (i = 0; i < x; i++)
	{
		dsp563xx_once_execute_dw_ir_nq(target->tap, 0x60F400, address + i);
		dsp563xx_once_execute_sw_ir_nq(target->tap, 0x07E086);
		dsp563xx_once_execute_dw_ir_nq(target->tap, 0x467000, 0xfffffc);
		dsp563xx_execute_queue();

		dsp563xx_once_reg_read(target->tap, DSP563XX_ONCE_OGDBR, &data);

		b = buffer + 4 * i;
		if (size > 0)
			*b++ = data >> 0;
		if (size > 1)
			*b++ = data >> 8;
		if (size > 2)
			*b++ = data >> 16;
		if (size > 3)
			*b++ = 0x00;
	}

	return ERROR_OK;
}

static int dsp563xx_write_memory_p(struct target *target, uint32_t address, uint32_t size,
			    uint32_t count, uint8_t * buffer)
{
	uint32_t i, x;
	uint32_t data;
	uint8_t *b;

	LOG_DEBUG("address: 0x%8.8" PRIx32 ", size: 0x%8.8" PRIx32 ", count: 0x%8.8"
		  PRIx32 "", address, size, count);

	if (target->state != TARGET_HALTED)
	{
		LOG_WARNING("target not halted");
		return ERROR_TARGET_NOT_HALTED;
	}

	x = count;

	for (i = 0; i < x; i++)
	{
		b = buffer + 4 * i;

		data = 0;
		if (size > 0)
			data = *buffer++;
		if (size > 1)
			data |= (*buffer++) << 8;
		if (size > 2)
			data |= (*buffer++) << 16;
		if (size > 3)
			data |= (*buffer++) << 24;

//              LOG_DEBUG("%08X", data);

		dsp563xx_once_execute_dw_ir_nq(target->tap, 0x61F400, address + i);
		dsp563xx_once_execute_dw_ir_nq(target->tap, 0x60F400, data);
		dsp563xx_once_execute_sw_ir_nq(target->tap, 0x076190);
		dsp563xx_execute_queue();
	}

	return ERROR_OK;
}

static int dsp563xx_jtag_sendinstr(struct jtag_tap *tap, uint8_t * ir_in, uint8_t ir_out)
{
	return dsp563xx_write_ir_u8(tap, ir_in, ir_out, DSP563XX_JTAG_INS_LEN, 1);
}

/* IR and DR functions */
static int dsp563xx_write_ir(struct jtag_tap *tap, uint8_t * ir_in, uint8_t * ir_out,
		      int ir_len, int rti)
{
	if (NULL == tap)
	{
		LOG_ERROR("invalid tap");
		return ERROR_FAIL;
	}
	if (ir_len != tap->ir_length)
	{
		LOG_ERROR("invalid ir_len");
		return ERROR_FAIL;
	}

	{
		jtag_add_plain_ir_scan(tap->ir_length, ir_out, ir_in, TAP_IDLE);
	}

	return ERROR_OK;
}

static int dsp563xx_write_dr(struct jtag_tap *tap, uint8_t * dr_in, uint8_t * dr_out,
		      int dr_len, int rti)
{
	if (NULL == tap)
	{
		LOG_ERROR("invalid tap");
		return ERROR_FAIL;
	}

	{
		jtag_add_plain_dr_scan(dr_len, dr_out, dr_in, TAP_IDLE);
	}

	return ERROR_OK;
}

static int dsp563xx_write_ir_u8(struct jtag_tap *tap, uint8_t * ir_in, uint8_t ir_out,
			 int ir_len, int rti)
{
	if (ir_len > 8)
	{
		LOG_ERROR("ir_len overflow, maxium is 8");
		return ERROR_FAIL;
	}

	dsp563xx_write_ir(tap, ir_in, &ir_out, ir_len, rti);

	return ERROR_OK;
}

int dsp563xx_write_dr_u8(struct jtag_tap *tap, uint8_t * dr_in, uint8_t dr_out,
			 int dr_len, int rti)
{
	if (dr_len > 8)
	{
		LOG_ERROR("dr_len overflow, maxium is 8");
		return ERROR_FAIL;
	}

	dsp563xx_write_dr(tap, dr_in, &dr_out, dr_len, rti);

	return ERROR_OK;
}

int dsp563xx_write_dr_u32(struct jtag_tap *tap, uint32_t * dr_in, uint32_t dr_out,
			  int dr_len, int rti)
{
	if (dr_len > 32)
	{
		LOG_ERROR("dr_len overflow, maxium is 32");
		return ERROR_FAIL;
	}

	dsp563xx_write_dr(tap, (uint8_t *) dr_in, (uint8_t *) & dr_out, dr_len, rti);

	return ERROR_OK;
}

int dsp563xx_execute_queue(void)
{
	return jtag_execute_queue();
}

/** Holds methods for DSP563XX targets. */
struct target_type dsp563xx_target = {
	.name = "dsp563xx",

	.poll = dsp563xx_poll,
	.arch_state = dsp563xx_arch_state,

	.target_request_data = NULL,

	.get_gdb_reg_list = dsp563xx_get_gdb_reg_list,

	.halt = dsp563xx_halt,
	.resume = dsp563xx_resume,
	.step = dsp563xx_step,

	.assert_reset = dsp563xx_assert_reset,
	.deassert_reset = dsp563xx_deassert_reset,
	.soft_reset_halt = dsp563xx_soft_reset_halt,

	.read_memory = dsp563xx_read_memory_p,
	.write_memory = dsp563xx_write_memory_p,

	.target_create = dsp563xx_target_create,
	.init_target = dsp563xx_init_target,
};
