| // 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; |
| } |