// SPDX-License-Identifier: GPL-2.0-or-later
|
/*
|
* altera-jtag.c
|
*
|
* altera FPGA driver
|
*
|
* Copyright (C) Altera Corporation 1998-2001
|
* Copyright (C) 2010 NetUP Inc.
|
* Copyright (C) 2010 Igor M. Liplianin <liplianin@netup.ru>
|
*/
|
|
#include <linux/delay.h>
|
#include <linux/firmware.h>
|
#include <linux/slab.h>
|
#include <misc/altera.h>
|
#include "altera-exprt.h"
|
#include "altera-jtag.h"
|
|
#define alt_jtag_io(a, b, c)\
|
astate->config->jtag_io(astate->config->dev, a, b, c);
|
|
#define alt_malloc(a) kzalloc(a, GFP_KERNEL);
|
|
/*
|
* This structure shows, for each JTAG state, which state is reached after
|
* a single TCK clock cycle with TMS high or TMS low, respectively. This
|
* describes all possible state transitions in the JTAG state machine.
|
*/
|
struct altera_jtag_machine {
|
enum altera_jtag_state tms_high;
|
enum altera_jtag_state tms_low;
|
};
|
|
static const struct altera_jtag_machine altera_transitions[] = {
|
/* RESET */ { RESET, IDLE },
|
/* IDLE */ { DRSELECT, IDLE },
|
/* DRSELECT */ { IRSELECT, DRCAPTURE },
|
/* DRCAPTURE */ { DREXIT1, DRSHIFT },
|
/* DRSHIFT */ { DREXIT1, DRSHIFT },
|
/* DREXIT1 */ { DRUPDATE, DRPAUSE },
|
/* DRPAUSE */ { DREXIT2, DRPAUSE },
|
/* DREXIT2 */ { DRUPDATE, DRSHIFT },
|
/* DRUPDATE */ { DRSELECT, IDLE },
|
/* IRSELECT */ { RESET, IRCAPTURE },
|
/* IRCAPTURE */ { IREXIT1, IRSHIFT },
|
/* IRSHIFT */ { IREXIT1, IRSHIFT },
|
/* IREXIT1 */ { IRUPDATE, IRPAUSE },
|
/* IRPAUSE */ { IREXIT2, IRPAUSE },
|
/* IREXIT2 */ { IRUPDATE, IRSHIFT },
|
/* IRUPDATE */ { DRSELECT, IDLE }
|
};
|
|
/*
|
* This table contains the TMS value to be used to take the NEXT STEP on
|
* the path to the desired state. The array index is the current state,
|
* and the bit position is the desired endstate. To find out which state
|
* is used as the intermediate state, look up the TMS value in the
|
* altera_transitions[] table.
|
*/
|
static const u16 altera_jtag_path_map[16] = {
|
/* RST RTI SDRS CDR SDR E1DR PDR E2DR */
|
0x0001, 0xFFFD, 0xFE01, 0xFFE7, 0xFFEF, 0xFF0F, 0xFFBF, 0xFFFF,
|
/* UDR SIRS CIR SIR E1IR PIR E2IR UIR */
|
0xFEFD, 0x0001, 0xF3FF, 0xF7FF, 0x87FF, 0xDFFF, 0xFFFF, 0x7FFD
|
};
|
|
/* Flag bits for alt_jtag_io() function */
|
#define TMS_HIGH 1
|
#define TMS_LOW 0
|
#define TDI_HIGH 1
|
#define TDI_LOW 0
|
#define READ_TDO 1
|
#define IGNORE_TDO 0
|
|
int altera_jinit(struct altera_state *astate)
|
{
|
struct altera_jtag *js = &astate->js;
|
|
/* initial JTAG state is unknown */
|
js->jtag_state = ILLEGAL_JTAG_STATE;
|
|
/* initialize to default state */
|
js->drstop_state = IDLE;
|
js->irstop_state = IDLE;
|
js->dr_pre = 0;
|
js->dr_post = 0;
|
js->ir_pre = 0;
|
js->ir_post = 0;
|
js->dr_length = 0;
|
js->ir_length = 0;
|
|
js->dr_pre_data = NULL;
|
js->dr_post_data = NULL;
|
js->ir_pre_data = NULL;
|
js->ir_post_data = NULL;
|
js->dr_buffer = NULL;
|
js->ir_buffer = NULL;
|
|
return 0;
|
}
|
|
int altera_set_drstop(struct altera_jtag *js, enum altera_jtag_state state)
|
{
|
js->drstop_state = state;
|
|
return 0;
|
}
|
|
int altera_set_irstop(struct altera_jtag *js, enum altera_jtag_state state)
|
{
|
js->irstop_state = state;
|
|
return 0;
|
}
|
|
int altera_set_dr_pre(struct altera_jtag *js,
|
u32 count, u32 start_index,
|
u8 *preamble_data)
|
{
|
int status = 0;
|
u32 i;
|
u32 j;
|
|
if (count > js->dr_pre) {
|
kfree(js->dr_pre_data);
|
js->dr_pre_data = (u8 *)alt_malloc((count + 7) >> 3);
|
if (js->dr_pre_data == NULL)
|
status = -ENOMEM;
|
else
|
js->dr_pre = count;
|
} else
|
js->dr_pre = count;
|
|
if (status == 0) {
|
for (i = 0; i < count; ++i) {
|
j = i + start_index;
|
|
if (preamble_data == NULL)
|
js->dr_pre_data[i >> 3] |= (1 << (i & 7));
|
else {
|
if (preamble_data[j >> 3] & (1 << (j & 7)))
|
js->dr_pre_data[i >> 3] |=
|
(1 << (i & 7));
|
else
|
js->dr_pre_data[i >> 3] &=
|
~(u32)(1 << (i & 7));
|
|
}
|
}
|
}
|
|
return status;
|
}
|
|
int altera_set_ir_pre(struct altera_jtag *js, u32 count, u32 start_index,
|
u8 *preamble_data)
|
{
|
int status = 0;
|
u32 i;
|
u32 j;
|
|
if (count > js->ir_pre) {
|
kfree(js->ir_pre_data);
|
js->ir_pre_data = (u8 *)alt_malloc((count + 7) >> 3);
|
if (js->ir_pre_data == NULL)
|
status = -ENOMEM;
|
else
|
js->ir_pre = count;
|
|
} else
|
js->ir_pre = count;
|
|
if (status == 0) {
|
for (i = 0; i < count; ++i) {
|
j = i + start_index;
|
if (preamble_data == NULL)
|
js->ir_pre_data[i >> 3] |= (1 << (i & 7));
|
else {
|
if (preamble_data[j >> 3] & (1 << (j & 7)))
|
js->ir_pre_data[i >> 3] |=
|
(1 << (i & 7));
|
else
|
js->ir_pre_data[i >> 3] &=
|
~(u32)(1 << (i & 7));
|
|
}
|
}
|
}
|
|
return status;
|
}
|
|
int altera_set_dr_post(struct altera_jtag *js, u32 count, u32 start_index,
|
u8 *postamble_data)
|
{
|
int status = 0;
|
u32 i;
|
u32 j;
|
|
if (count > js->dr_post) {
|
kfree(js->dr_post_data);
|
js->dr_post_data = (u8 *)alt_malloc((count + 7) >> 3);
|
|
if (js->dr_post_data == NULL)
|
status = -ENOMEM;
|
else
|
js->dr_post = count;
|
|
} else
|
js->dr_post = count;
|
|
if (status == 0) {
|
for (i = 0; i < count; ++i) {
|
j = i + start_index;
|
|
if (postamble_data == NULL)
|
js->dr_post_data[i >> 3] |= (1 << (i & 7));
|
else {
|
if (postamble_data[j >> 3] & (1 << (j & 7)))
|
js->dr_post_data[i >> 3] |=
|
(1 << (i & 7));
|
else
|
js->dr_post_data[i >> 3] &=
|
~(u32)(1 << (i & 7));
|
|
}
|
}
|
}
|
|
return status;
|
}
|
|
int altera_set_ir_post(struct altera_jtag *js, u32 count, u32 start_index,
|
u8 *postamble_data)
|
{
|
int status = 0;
|
u32 i;
|
u32 j;
|
|
if (count > js->ir_post) {
|
kfree(js->ir_post_data);
|
js->ir_post_data = (u8 *)alt_malloc((count + 7) >> 3);
|
if (js->ir_post_data == NULL)
|
status = -ENOMEM;
|
else
|
js->ir_post = count;
|
|
} else
|
js->ir_post = count;
|
|
if (status != 0)
|
return status;
|
|
for (i = 0; i < count; ++i) {
|
j = i + start_index;
|
|
if (postamble_data == NULL)
|
js->ir_post_data[i >> 3] |= (1 << (i & 7));
|
else {
|
if (postamble_data[j >> 3] & (1 << (j & 7)))
|
js->ir_post_data[i >> 3] |= (1 << (i & 7));
|
else
|
js->ir_post_data[i >> 3] &=
|
~(u32)(1 << (i & 7));
|
|
}
|
}
|
|
return status;
|
}
|
|
static void altera_jreset_idle(struct altera_state *astate)
|
{
|
struct altera_jtag *js = &astate->js;
|
int i;
|
/* Go to Test Logic Reset (no matter what the starting state may be) */
|
for (i = 0; i < 5; ++i)
|
alt_jtag_io(TMS_HIGH, TDI_LOW, IGNORE_TDO);
|
|
/* Now step to Run Test / Idle */
|
alt_jtag_io(TMS_LOW, TDI_LOW, IGNORE_TDO);
|
js->jtag_state = IDLE;
|
}
|
|
int altera_goto_jstate(struct altera_state *astate,
|
enum altera_jtag_state state)
|
{
|
struct altera_jtag *js = &astate->js;
|
int tms;
|
int count = 0;
|
int status = 0;
|
|
if (js->jtag_state == ILLEGAL_JTAG_STATE)
|
/* initialize JTAG chain to known state */
|
altera_jreset_idle(astate);
|
|
if (js->jtag_state == state) {
|
/*
|
* We are already in the desired state.
|
* If it is a stable state, loop here.
|
* Otherwise do nothing (no clock cycles).
|
*/
|
if ((state == IDLE) || (state == DRSHIFT) ||
|
(state == DRPAUSE) || (state == IRSHIFT) ||
|
(state == IRPAUSE)) {
|
alt_jtag_io(TMS_LOW, TDI_LOW, IGNORE_TDO);
|
} else if (state == RESET)
|
alt_jtag_io(TMS_HIGH, TDI_LOW, IGNORE_TDO);
|
|
} else {
|
while ((js->jtag_state != state) && (count < 9)) {
|
/* Get TMS value to take a step toward desired state */
|
tms = (altera_jtag_path_map[js->jtag_state] &
|
(1 << state))
|
? TMS_HIGH : TMS_LOW;
|
|
/* Take a step */
|
alt_jtag_io(tms, TDI_LOW, IGNORE_TDO);
|
|
if (tms)
|
js->jtag_state =
|
altera_transitions[js->jtag_state].tms_high;
|
else
|
js->jtag_state =
|
altera_transitions[js->jtag_state].tms_low;
|
|
++count;
|
}
|
}
|
|
if (js->jtag_state != state)
|
status = -EREMOTEIO;
|
|
return status;
|
}
|
|
int altera_wait_cycles(struct altera_state *astate,
|
s32 cycles,
|
enum altera_jtag_state wait_state)
|
{
|
struct altera_jtag *js = &astate->js;
|
int tms;
|
s32 count;
|
int status = 0;
|
|
if (js->jtag_state != wait_state)
|
status = altera_goto_jstate(astate, wait_state);
|
|
if (status == 0) {
|
/*
|
* Set TMS high to loop in RESET state
|
* Set TMS low to loop in any other stable state
|
*/
|
tms = (wait_state == RESET) ? TMS_HIGH : TMS_LOW;
|
|
for (count = 0L; count < cycles; count++)
|
alt_jtag_io(tms, TDI_LOW, IGNORE_TDO);
|
|
}
|
|
return status;
|
}
|
|
int altera_wait_msecs(struct altera_state *astate,
|
s32 microseconds, enum altera_jtag_state wait_state)
|
/*
|
* Causes JTAG hardware to sit in the specified stable
|
* state for the specified duration of real time. If
|
* no JTAG operations have been performed yet, then only
|
* a delay is performed. This permits the WAIT USECS
|
* statement to be used in VECTOR programs without causing
|
* any JTAG operations.
|
* Returns 0 for success, else appropriate error code.
|
*/
|
{
|
struct altera_jtag *js = &astate->js;
|
int status = 0;
|
|
if ((js->jtag_state != ILLEGAL_JTAG_STATE) &&
|
(js->jtag_state != wait_state))
|
status = altera_goto_jstate(astate, wait_state);
|
|
if (status == 0)
|
/* Wait for specified time interval */
|
udelay(microseconds);
|
|
return status;
|
}
|
|
static void altera_concatenate_data(u8 *buffer,
|
u8 *preamble_data,
|
u32 preamble_count,
|
u8 *target_data,
|
u32 start_index,
|
u32 target_count,
|
u8 *postamble_data,
|
u32 postamble_count)
|
/*
|
* Copies preamble data, target data, and postamble data
|
* into one buffer for IR or DR scans.
|
*/
|
{
|
u32 i, j, k;
|
|
for (i = 0L; i < preamble_count; ++i) {
|
if (preamble_data[i >> 3L] & (1L << (i & 7L)))
|
buffer[i >> 3L] |= (1L << (i & 7L));
|
else
|
buffer[i >> 3L] &= ~(u32)(1L << (i & 7L));
|
|
}
|
|
j = start_index;
|
k = preamble_count + target_count;
|
for (; i < k; ++i, ++j) {
|
if (target_data[j >> 3L] & (1L << (j & 7L)))
|
buffer[i >> 3L] |= (1L << (i & 7L));
|
else
|
buffer[i >> 3L] &= ~(u32)(1L << (i & 7L));
|
|
}
|
|
j = 0L;
|
k = preamble_count + target_count + postamble_count;
|
for (; i < k; ++i, ++j) {
|
if (postamble_data[j >> 3L] & (1L << (j & 7L)))
|
buffer[i >> 3L] |= (1L << (i & 7L));
|
else
|
buffer[i >> 3L] &= ~(u32)(1L << (i & 7L));
|
|
}
|
}
|
|
static int alt_jtag_drscan(struct altera_state *astate,
|
int start_state,
|
int count,
|
u8 *tdi,
|
u8 *tdo)
|
{
|
int i = 0;
|
int tdo_bit = 0;
|
int status = 1;
|
|
/* First go to DRSHIFT state */
|
switch (start_state) {
|
case 0: /* IDLE */
|
alt_jtag_io(1, 0, 0); /* DRSELECT */
|
alt_jtag_io(0, 0, 0); /* DRCAPTURE */
|
alt_jtag_io(0, 0, 0); /* DRSHIFT */
|
break;
|
|
case 1: /* DRPAUSE */
|
alt_jtag_io(1, 0, 0); /* DREXIT2 */
|
alt_jtag_io(1, 0, 0); /* DRUPDATE */
|
alt_jtag_io(1, 0, 0); /* DRSELECT */
|
alt_jtag_io(0, 0, 0); /* DRCAPTURE */
|
alt_jtag_io(0, 0, 0); /* DRSHIFT */
|
break;
|
|
case 2: /* IRPAUSE */
|
alt_jtag_io(1, 0, 0); /* IREXIT2 */
|
alt_jtag_io(1, 0, 0); /* IRUPDATE */
|
alt_jtag_io(1, 0, 0); /* DRSELECT */
|
alt_jtag_io(0, 0, 0); /* DRCAPTURE */
|
alt_jtag_io(0, 0, 0); /* DRSHIFT */
|
break;
|
|
default:
|
status = 0;
|
}
|
|
if (status) {
|
/* loop in the SHIFT-DR state */
|
for (i = 0; i < count; i++) {
|
tdo_bit = alt_jtag_io(
|
(i == count - 1),
|
tdi[i >> 3] & (1 << (i & 7)),
|
(tdo != NULL));
|
|
if (tdo != NULL) {
|
if (tdo_bit)
|
tdo[i >> 3] |= (1 << (i & 7));
|
else
|
tdo[i >> 3] &= ~(u32)(1 << (i & 7));
|
|
}
|
}
|
|
alt_jtag_io(0, 0, 0); /* DRPAUSE */
|
}
|
|
return status;
|
}
|
|
static int alt_jtag_irscan(struct altera_state *astate,
|
int start_state,
|
int count,
|
u8 *tdi,
|
u8 *tdo)
|
{
|
int i = 0;
|
int tdo_bit = 0;
|
int status = 1;
|
|
/* First go to IRSHIFT state */
|
switch (start_state) {
|
case 0: /* IDLE */
|
alt_jtag_io(1, 0, 0); /* DRSELECT */
|
alt_jtag_io(1, 0, 0); /* IRSELECT */
|
alt_jtag_io(0, 0, 0); /* IRCAPTURE */
|
alt_jtag_io(0, 0, 0); /* IRSHIFT */
|
break;
|
|
case 1: /* DRPAUSE */
|
alt_jtag_io(1, 0, 0); /* DREXIT2 */
|
alt_jtag_io(1, 0, 0); /* DRUPDATE */
|
alt_jtag_io(1, 0, 0); /* DRSELECT */
|
alt_jtag_io(1, 0, 0); /* IRSELECT */
|
alt_jtag_io(0, 0, 0); /* IRCAPTURE */
|
alt_jtag_io(0, 0, 0); /* IRSHIFT */
|
break;
|
|
case 2: /* IRPAUSE */
|
alt_jtag_io(1, 0, 0); /* IREXIT2 */
|
alt_jtag_io(1, 0, 0); /* IRUPDATE */
|
alt_jtag_io(1, 0, 0); /* DRSELECT */
|
alt_jtag_io(1, 0, 0); /* IRSELECT */
|
alt_jtag_io(0, 0, 0); /* IRCAPTURE */
|
alt_jtag_io(0, 0, 0); /* IRSHIFT */
|
break;
|
|
default:
|
status = 0;
|
}
|
|
if (status) {
|
/* loop in the SHIFT-IR state */
|
for (i = 0; i < count; i++) {
|
tdo_bit = alt_jtag_io(
|
(i == count - 1),
|
tdi[i >> 3] & (1 << (i & 7)),
|
(tdo != NULL));
|
if (tdo != NULL) {
|
if (tdo_bit)
|
tdo[i >> 3] |= (1 << (i & 7));
|
else
|
tdo[i >> 3] &= ~(u32)(1 << (i & 7));
|
|
}
|
}
|
|
alt_jtag_io(0, 0, 0); /* IRPAUSE */
|
}
|
|
return status;
|
}
|
|
static void altera_extract_target_data(u8 *buffer,
|
u8 *target_data,
|
u32 start_index,
|
u32 preamble_count,
|
u32 target_count)
|
/*
|
* Copies target data from scan buffer, filtering out
|
* preamble and postamble data.
|
*/
|
{
|
u32 i;
|
u32 j;
|
u32 k;
|
|
j = preamble_count;
|
k = start_index + target_count;
|
for (i = start_index; i < k; ++i, ++j) {
|
if (buffer[j >> 3] & (1 << (j & 7)))
|
target_data[i >> 3] |= (1 << (i & 7));
|
else
|
target_data[i >> 3] &= ~(u32)(1 << (i & 7));
|
|
}
|
}
|
|
int altera_irscan(struct altera_state *astate,
|
u32 count,
|
u8 *tdi_data,
|
u32 start_index)
|
/* Shifts data into instruction register */
|
{
|
struct altera_jtag *js = &astate->js;
|
int start_code = 0;
|
u32 alloc_chars = 0;
|
u32 shift_count = js->ir_pre + count + js->ir_post;
|
int status = 0;
|
enum altera_jtag_state start_state = ILLEGAL_JTAG_STATE;
|
|
switch (js->jtag_state) {
|
case ILLEGAL_JTAG_STATE:
|
case RESET:
|
case IDLE:
|
start_code = 0;
|
start_state = IDLE;
|
break;
|
|
case DRSELECT:
|
case DRCAPTURE:
|
case DRSHIFT:
|
case DREXIT1:
|
case DRPAUSE:
|
case DREXIT2:
|
case DRUPDATE:
|
start_code = 1;
|
start_state = DRPAUSE;
|
break;
|
|
case IRSELECT:
|
case IRCAPTURE:
|
case IRSHIFT:
|
case IREXIT1:
|
case IRPAUSE:
|
case IREXIT2:
|
case IRUPDATE:
|
start_code = 2;
|
start_state = IRPAUSE;
|
break;
|
|
default:
|
status = -EREMOTEIO;
|
break;
|
}
|
|
if (status == 0)
|
if (js->jtag_state != start_state)
|
status = altera_goto_jstate(astate, start_state);
|
|
if (status == 0) {
|
if (shift_count > js->ir_length) {
|
alloc_chars = (shift_count + 7) >> 3;
|
kfree(js->ir_buffer);
|
js->ir_buffer = (u8 *)alt_malloc(alloc_chars);
|
if (js->ir_buffer == NULL)
|
status = -ENOMEM;
|
else
|
js->ir_length = alloc_chars * 8;
|
|
}
|
}
|
|
if (status == 0) {
|
/*
|
* Copy preamble data, IR data,
|
* and postamble data into a buffer
|
*/
|
altera_concatenate_data(js->ir_buffer,
|
js->ir_pre_data,
|
js->ir_pre,
|
tdi_data,
|
start_index,
|
count,
|
js->ir_post_data,
|
js->ir_post);
|
/* Do the IRSCAN */
|
alt_jtag_irscan(astate,
|
start_code,
|
shift_count,
|
js->ir_buffer,
|
NULL);
|
|
/* alt_jtag_irscan() always ends in IRPAUSE state */
|
js->jtag_state = IRPAUSE;
|
}
|
|
if (status == 0)
|
if (js->irstop_state != IRPAUSE)
|
status = altera_goto_jstate(astate, js->irstop_state);
|
|
|
return status;
|
}
|
|
int altera_swap_ir(struct altera_state *astate,
|
u32 count,
|
u8 *in_data,
|
u32 in_index,
|
u8 *out_data,
|
u32 out_index)
|
/* Shifts data into instruction register, capturing output data */
|
{
|
struct altera_jtag *js = &astate->js;
|
int start_code = 0;
|
u32 alloc_chars = 0;
|
u32 shift_count = js->ir_pre + count + js->ir_post;
|
int status = 0;
|
enum altera_jtag_state start_state = ILLEGAL_JTAG_STATE;
|
|
switch (js->jtag_state) {
|
case ILLEGAL_JTAG_STATE:
|
case RESET:
|
case IDLE:
|
start_code = 0;
|
start_state = IDLE;
|
break;
|
|
case DRSELECT:
|
case DRCAPTURE:
|
case DRSHIFT:
|
case DREXIT1:
|
case DRPAUSE:
|
case DREXIT2:
|
case DRUPDATE:
|
start_code = 1;
|
start_state = DRPAUSE;
|
break;
|
|
case IRSELECT:
|
case IRCAPTURE:
|
case IRSHIFT:
|
case IREXIT1:
|
case IRPAUSE:
|
case IREXIT2:
|
case IRUPDATE:
|
start_code = 2;
|
start_state = IRPAUSE;
|
break;
|
|
default:
|
status = -EREMOTEIO;
|
break;
|
}
|
|
if (status == 0)
|
if (js->jtag_state != start_state)
|
status = altera_goto_jstate(astate, start_state);
|
|
if (status == 0) {
|
if (shift_count > js->ir_length) {
|
alloc_chars = (shift_count + 7) >> 3;
|
kfree(js->ir_buffer);
|
js->ir_buffer = (u8 *)alt_malloc(alloc_chars);
|
if (js->ir_buffer == NULL)
|
status = -ENOMEM;
|
else
|
js->ir_length = alloc_chars * 8;
|
|
}
|
}
|
|
if (status == 0) {
|
/*
|
* Copy preamble data, IR data,
|
* and postamble data into a buffer
|
*/
|
altera_concatenate_data(js->ir_buffer,
|
js->ir_pre_data,
|
js->ir_pre,
|
in_data,
|
in_index,
|
count,
|
js->ir_post_data,
|
js->ir_post);
|
|
/* Do the IRSCAN */
|
alt_jtag_irscan(astate,
|
start_code,
|
shift_count,
|
js->ir_buffer,
|
js->ir_buffer);
|
|
/* alt_jtag_irscan() always ends in IRPAUSE state */
|
js->jtag_state = IRPAUSE;
|
}
|
|
if (status == 0)
|
if (js->irstop_state != IRPAUSE)
|
status = altera_goto_jstate(astate, js->irstop_state);
|
|
|
if (status == 0)
|
/* Now extract the returned data from the buffer */
|
altera_extract_target_data(js->ir_buffer,
|
out_data, out_index,
|
js->ir_pre, count);
|
|
return status;
|
}
|
|
int altera_drscan(struct altera_state *astate,
|
u32 count,
|
u8 *tdi_data,
|
u32 start_index)
|
/* Shifts data into data register (ignoring output data) */
|
{
|
struct altera_jtag *js = &astate->js;
|
int start_code = 0;
|
u32 alloc_chars = 0;
|
u32 shift_count = js->dr_pre + count + js->dr_post;
|
int status = 0;
|
enum altera_jtag_state start_state = ILLEGAL_JTAG_STATE;
|
|
switch (js->jtag_state) {
|
case ILLEGAL_JTAG_STATE:
|
case RESET:
|
case IDLE:
|
start_code = 0;
|
start_state = IDLE;
|
break;
|
|
case DRSELECT:
|
case DRCAPTURE:
|
case DRSHIFT:
|
case DREXIT1:
|
case DRPAUSE:
|
case DREXIT2:
|
case DRUPDATE:
|
start_code = 1;
|
start_state = DRPAUSE;
|
break;
|
|
case IRSELECT:
|
case IRCAPTURE:
|
case IRSHIFT:
|
case IREXIT1:
|
case IRPAUSE:
|
case IREXIT2:
|
case IRUPDATE:
|
start_code = 2;
|
start_state = IRPAUSE;
|
break;
|
|
default:
|
status = -EREMOTEIO;
|
break;
|
}
|
|
if (status == 0)
|
if (js->jtag_state != start_state)
|
status = altera_goto_jstate(astate, start_state);
|
|
if (status == 0) {
|
if (shift_count > js->dr_length) {
|
alloc_chars = (shift_count + 7) >> 3;
|
kfree(js->dr_buffer);
|
js->dr_buffer = (u8 *)alt_malloc(alloc_chars);
|
if (js->dr_buffer == NULL)
|
status = -ENOMEM;
|
else
|
js->dr_length = alloc_chars * 8;
|
|
}
|
}
|
|
if (status == 0) {
|
/*
|
* Copy preamble data, DR data,
|
* and postamble data into a buffer
|
*/
|
altera_concatenate_data(js->dr_buffer,
|
js->dr_pre_data,
|
js->dr_pre,
|
tdi_data,
|
start_index,
|
count,
|
js->dr_post_data,
|
js->dr_post);
|
/* Do the DRSCAN */
|
alt_jtag_drscan(astate, start_code, shift_count,
|
js->dr_buffer, NULL);
|
/* alt_jtag_drscan() always ends in DRPAUSE state */
|
js->jtag_state = DRPAUSE;
|
}
|
|
if (status == 0)
|
if (js->drstop_state != DRPAUSE)
|
status = altera_goto_jstate(astate, js->drstop_state);
|
|
return status;
|
}
|
|
int altera_swap_dr(struct altera_state *astate, u32 count,
|
u8 *in_data, u32 in_index,
|
u8 *out_data, u32 out_index)
|
/* Shifts data into data register, capturing output data */
|
{
|
struct altera_jtag *js = &astate->js;
|
int start_code = 0;
|
u32 alloc_chars = 0;
|
u32 shift_count = js->dr_pre + count + js->dr_post;
|
int status = 0;
|
enum altera_jtag_state start_state = ILLEGAL_JTAG_STATE;
|
|
switch (js->jtag_state) {
|
case ILLEGAL_JTAG_STATE:
|
case RESET:
|
case IDLE:
|
start_code = 0;
|
start_state = IDLE;
|
break;
|
|
case DRSELECT:
|
case DRCAPTURE:
|
case DRSHIFT:
|
case DREXIT1:
|
case DRPAUSE:
|
case DREXIT2:
|
case DRUPDATE:
|
start_code = 1;
|
start_state = DRPAUSE;
|
break;
|
|
case IRSELECT:
|
case IRCAPTURE:
|
case IRSHIFT:
|
case IREXIT1:
|
case IRPAUSE:
|
case IREXIT2:
|
case IRUPDATE:
|
start_code = 2;
|
start_state = IRPAUSE;
|
break;
|
|
default:
|
status = -EREMOTEIO;
|
break;
|
}
|
|
if (status == 0)
|
if (js->jtag_state != start_state)
|
status = altera_goto_jstate(astate, start_state);
|
|
if (status == 0) {
|
if (shift_count > js->dr_length) {
|
alloc_chars = (shift_count + 7) >> 3;
|
kfree(js->dr_buffer);
|
js->dr_buffer = (u8 *)alt_malloc(alloc_chars);
|
|
if (js->dr_buffer == NULL)
|
status = -ENOMEM;
|
else
|
js->dr_length = alloc_chars * 8;
|
|
}
|
}
|
|
if (status == 0) {
|
/*
|
* Copy preamble data, DR data,
|
* and postamble data into a buffer
|
*/
|
altera_concatenate_data(js->dr_buffer,
|
js->dr_pre_data,
|
js->dr_pre,
|
in_data,
|
in_index,
|
count,
|
js->dr_post_data,
|
js->dr_post);
|
|
/* Do the DRSCAN */
|
alt_jtag_drscan(astate,
|
start_code,
|
shift_count,
|
js->dr_buffer,
|
js->dr_buffer);
|
|
/* alt_jtag_drscan() always ends in DRPAUSE state */
|
js->jtag_state = DRPAUSE;
|
}
|
|
if (status == 0)
|
if (js->drstop_state != DRPAUSE)
|
status = altera_goto_jstate(astate, js->drstop_state);
|
|
if (status == 0)
|
/* Now extract the returned data from the buffer */
|
altera_extract_target_data(js->dr_buffer,
|
out_data,
|
out_index,
|
js->dr_pre,
|
count);
|
|
return status;
|
}
|
|
void altera_free_buffers(struct altera_state *astate)
|
{
|
struct altera_jtag *js = &astate->js;
|
/* If the JTAG interface was used, reset it to TLR */
|
if (js->jtag_state != ILLEGAL_JTAG_STATE)
|
altera_jreset_idle(astate);
|
|
kfree(js->dr_pre_data);
|
js->dr_pre_data = NULL;
|
|
kfree(js->dr_post_data);
|
js->dr_post_data = NULL;
|
|
kfree(js->dr_buffer);
|
js->dr_buffer = NULL;
|
|
kfree(js->ir_pre_data);
|
js->ir_pre_data = NULL;
|
|
kfree(js->ir_post_data);
|
js->ir_post_data = NULL;
|
|
kfree(js->ir_buffer);
|
js->ir_buffer = NULL;
|
}
|