satip-axe/kernel/drivers/stm/tap.c

244 lines
5.4 KiB
C

/*
* (c) 2010 STMicroelectronics Limited
*
* Author: Pawel Moll <pawel.moll@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include "tap.h"
enum stm_tap_state {
TAP_TEST_LOGIC_RESET,
TAP_RUN_TEST_IDLE,
TAP_SELECT_DR_SCAN,
TAP_SELECT_IR_SCAN,
TAP_CAPTURE_DR,
TAP_SHIFT_DR,
TAP_EXIT1_DR,
TAP_PAUSE_DR,
TAP_EXIT2_DR,
TAP_UPDATE_DR,
TAP_CAPTURE_IR,
TAP_SHIFT_IR,
TAP_EXIT1_IR,
TAP_PAUSE_IR,
TAP_EXIT2_IR,
TAP_UPDATE_IR,
__TAP_LAST
};
static enum stm_tap_state stm_tap_fsm[__TAP_LAST][2] = {
/* TMS = 0 TMS = 1 */
[TAP_TEST_LOGIC_RESET] = { TAP_RUN_TEST_IDLE, TAP_TEST_LOGIC_RESET },
[TAP_RUN_TEST_IDLE] = { TAP_RUN_TEST_IDLE, TAP_SELECT_DR_SCAN },
[TAP_SELECT_DR_SCAN] = { TAP_CAPTURE_DR, TAP_SELECT_IR_SCAN },
[TAP_SELECT_IR_SCAN] = { TAP_CAPTURE_IR, TAP_TEST_LOGIC_RESET },
[TAP_CAPTURE_DR] = { TAP_SHIFT_DR, TAP_EXIT1_DR },
[TAP_SHIFT_DR] = { TAP_SHIFT_DR, TAP_EXIT1_DR },
[TAP_EXIT1_DR] = { TAP_PAUSE_DR, TAP_UPDATE_DR },
[TAP_PAUSE_DR] = { TAP_PAUSE_DR, TAP_EXIT2_DR },
[TAP_EXIT2_DR] = { TAP_SHIFT_DR, TAP_UPDATE_DR },
[TAP_UPDATE_DR] = { TAP_RUN_TEST_IDLE, TAP_SELECT_DR_SCAN },
[TAP_CAPTURE_IR] = { TAP_SHIFT_IR, TAP_EXIT1_IR },
[TAP_SHIFT_IR] = { TAP_SHIFT_IR, TAP_EXIT1_IR },
[TAP_EXIT1_IR] = { TAP_PAUSE_IR, TAP_UPDATE_IR },
[TAP_PAUSE_IR] = { TAP_PAUSE_IR, TAP_EXIT2_IR },
[TAP_EXIT2_IR] = { TAP_SHIFT_IR, TAP_UPDATE_IR },
[TAP_UPDATE_IR] = { TAP_RUN_TEST_IDLE, TAP_SELECT_DR_SCAN }
};
struct stm_tap {
enum stm_tap_state state;
stm_tap_tick tick;
};
static struct stm_tap stm_tap;
static int stm_tap_in_use;
struct stm_tap *stm_tap_init(stm_tap_tick tick)
{
struct stm_tap *tap;
int i;
if (stm_tap_in_use)
return NULL;
tap = &stm_tap;
stm_tap_in_use = 1;
tap->state = TAP_TEST_LOGIC_RESET;
tap->tick = tick;
/* Five transitions with TMS=1 should get us to Test-Logic/Reset
* state from any other state of the FSM... */
for (i = 0; i < 5; i++)
tick(1, 0);
return tap;
}
void stm_tap_free(struct stm_tap *tap)
{
if (tap != &stm_tap)
return;
BUG_ON(tap->state != TAP_TEST_LOGIC_RESET);
tap->tick = NULL;
stm_tap_in_use = 0;
}
static int stm_tap_step(struct stm_tap *tap, enum stm_tap_state state, int tdi)
{
int tms;
pr_debug("%s(tap->state=%d, state=%d, tdi=%d)\n",
__func__, tap->state, state, tdi);
if (stm_tap_fsm[tap->state][0] == state) {
tms = 0;
} else if (stm_tap_fsm[tap->state][1] == state) {
tms = 1;
} else {
BUG();
return -1;
}
tap->state = state;
return tap->tick(tms, tdi);
}
static int stm_tap_goto(struct stm_tap *tap, const enum stm_tap_state *path)
{
while (*path != __TAP_LAST) {
int tdo = stm_tap_step(tap, *path, 0);
if (tdo < 0)
return tdo;
path++;
}
return 0;
}
int stm_tap_enable(struct stm_tap *tap)
{
static const enum stm_tap_state const path[] = {
TAP_RUN_TEST_IDLE,
__TAP_LAST
};
BUG_ON(tap->state != TAP_TEST_LOGIC_RESET);
return stm_tap_goto(tap, path);
}
int stm_tap_disable(struct stm_tap *tap)
{
static const enum stm_tap_state const path[] = {
TAP_SELECT_DR_SCAN,
TAP_SELECT_IR_SCAN,
TAP_TEST_LOGIC_RESET,
__TAP_LAST
};
BUG_ON(tap->state != TAP_RUN_TEST_IDLE);
return stm_tap_goto(tap, path);
}
int stm_tap_shift(struct stm_tap *tap, int ir_not_dr,
unsigned int *in, unsigned int *out, int bits)
{
int result;
static const enum stm_tap_state const dr_path_there[] = {
TAP_SELECT_DR_SCAN,
TAP_CAPTURE_DR,
TAP_SHIFT_DR,
__TAP_LAST
};
static const enum stm_tap_state const dr_path_back[] = {
TAP_UPDATE_DR,
TAP_RUN_TEST_IDLE,
__TAP_LAST
};
static const enum stm_tap_state const ir_path_there[] = {
TAP_SELECT_DR_SCAN,
TAP_SELECT_IR_SCAN,
TAP_CAPTURE_IR,
TAP_SHIFT_IR,
__TAP_LAST
};
static const enum stm_tap_state const ir_path_back[] = {
TAP_UPDATE_IR,
TAP_RUN_TEST_IDLE,
__TAP_LAST
};
unsigned int tdi = 0;
unsigned int tdo = 0;
unsigned int tdo_mask = 1;
enum stm_tap_state next_state;
pr_debug("%s(ir_not_dr=%d, in=%p, out=%p, bits=%d)\n",
__func__, ir_not_dr, in, out, bits);
BUG_ON(tap->state != TAP_RUN_TEST_IDLE);
/* This code handles up to 32 bits data blocks in this moment.
* If more is needed, it shouldn't be hard to implement... */
if (bits < 1 || bits > sizeof(*in) * 8)
return -1;
result = stm_tap_goto(tap, ir_not_dr ? ir_path_there : dr_path_there);
if (result < 0)
return result;
if (in)
tdi = *in;
pr_debug("%s(): tdi=0x%x\n", __func__, tdi);
/* We are in Shift-IR or Shift-DR state here. Time to start with
* the data. Least significant bit enters goes in & out first... */
next_state = ir_not_dr ? TAP_SHIFT_IR : TAP_SHIFT_DR;
while (bits) {
/* Last bit is shifted in during Shift-xR to Exit1-xR
* transition */
if (bits == 1)
next_state = ir_not_dr ? TAP_EXIT1_IR : TAP_EXIT1_DR;
result = stm_tap_step(tap, next_state, tdi & 0x1);
if (result < 0)
return result;
BUG_ON(result > 1);
tdi >>= 1;
tdo |= result ? tdo_mask : 0;
tdo_mask <<= 1;
bits--;
}
if (out)
*out = tdo;
pr_debug("%s(): tdo=0x%x\n", __func__, tdo);
return stm_tap_goto(tap, ir_not_dr ? ir_path_back : dr_path_back);
}