/*
* 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>
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#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