/***************************************************************************
* Copyright (C) 2008 by Spencer Oliver *
* spen@spen-soft.co.uk *
* *
* Copyright (C) 2008 by David T.L. Wong *
* *
* Copyright (C) 2009 by David N. Claffey <dnclaffey@gmail.com> *
* *
* Copyright (C) 2011 by Drasko DRASKOVIC *
* drasko.draskovic@gmail.com *
* *
* 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., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
***************************************************************************/
/*
* This version has optimized assembly routines for 32 bit operations:
* - read word
* - write word
* - write array of words
*
* One thing to be aware of is that the MIPS32 cpu will execute the
* instruction after a branch instruction (one delay slot).
*
* For example:
* LW $2, ($5 +10)
* B foo
* LW $1, ($2 +100)
*
* The LW $1, ($2 +100) instruction is also executed. If this is
* not wanted a NOP can be inserted:
*
* LW $2, ($5 +10)
* B foo
* NOP
* LW $1, ($2 +100)
*
* or the code can be changed to:
*
* B foo
* LW $2, ($5 +10)
* LW $1, ($2 +100)
*
* The original code contained NOPs. I have removed these and moved
* the branches.
*
* These changes result in a 35% speed increase when programming an
* external flash.
*
* More improvement could be gained if the registers do no need
* to be preserved but in that case the routines should be aware
* OpenOCD is used as a flash programmer or as a debug tool.
*
* Nico Coesel
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <helper/time_support.h>
#include "mips32.h"
#include "mips32_pracc.h"
struct mips32_pracc_context {
uint32_t *local_oparam;
int num_oparam;
const uint32_t *code;
int code_len;
uint32_t stack[32];
int stack_offset;
struct mips_ejtag *ejtag_info;
};
static int wait_for_pracc_rw(struct mips_ejtag *ejtag_info, uint32_t *ctrl)
{
uint32_t ejtag_ctrl;
long long then = timeval_ms();
/* wait for the PrAcc to become "1" */
mips_ejtag_set_instr(ejtag_info, EJTAG_INST_CONTROL);
while (1) {
ejtag_ctrl = ejtag_info->ejtag_ctrl;
int retval = mips_ejtag_drscan_32(ejtag_info, &ejtag_ctrl);
if (retval != ERROR_OK)
return retval;
if (ejtag_ctrl & EJTAG_CTRL_PRACC)
break;
int timeout = timeval_ms() - then;
if (timeout > 1000) {
LOG_DEBUG("DEBUGMODULE: No memory access in progress!");
return ERROR_JTAG_DEVICE_ERROR;
}
}
*ctrl = ejtag_ctrl;
return ERROR_OK;
}
/* Shift in control and address for a new processor access, save them in ejtag_info */
static int mips32_pracc_read_ctrl_addr(struct mips_ejtag *ejtag_info)
{
int retval = wait_for_pracc_rw(ejtag_info, &ejtag_info->pa_ctrl);
if (retval != ERROR_OK)
return retval;
mips_ejtag_set_instr(ejtag_info, EJTAG_INST_ADDRESS);
ejtag_info->pa_addr = 0;
retval = mips_ejtag_drscan_32(ejtag_info, &ejtag_info->pa_addr);
return retval;
}
/* Finish processor access */
static int mips32_pracc_finish(struct mips_ejtag *ejtag_info)
{
uint32_t ctrl = ejtag_info->ejtag_ctrl & ~EJTAG_CTRL_PRACC;
mips_ejtag_set_instr(ejtag_info, EJTAG_INST_CONTROL);
mips_ejtag_drscan_32_out(ejtag_info, ctrl);
return jtag_execute_queue();
}
int mips32_pracc_clean_text_jump(struct mips_ejtag *ejtag_info)
{
uint32_t jt_code = MIPS32_J((0x0FFFFFFF & MIPS32_PRACC_TEXT) >> 2);
int retval;
/* do 3 0/nops to clean pipeline before a jump to pracc text, NOP in delay slot */
for (int i = 0; i != 5; i++) {
/* Wait for pracc */
retval = wait_for_pracc_rw(ejtag_info, &ejtag_info->pa_ctrl);
if (retval != ERROR_OK)
return retval;
/* Data or instruction out */
mips_ejtag_set_instr(ejtag_info, EJTAG_INST_DATA);
uint32_t data = (i == 3) ? jt_code : MIPS32_NOP;
mips_ejtag_drscan_32_out(ejtag_info, data);
/* finish pa */
retval = mips32_pracc_finish(ejtag_info);
if (retval != ERROR_OK)
return retval;
}
if (ejtag_in