blob: 31b6e0604cb74d9d4aaede99424b1707b92458fe [file] [log] [blame]
/*
* Copyright 2012-2015 Google Inc.
*
* 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; version 2 of the License.
*
* 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.
*/
#include <stdio.h>
#include <string.h>
#include "em100.h"
/* SPI Trace related operations */
/**
* reset_spi_trace: clear SPI trace buffer
* @param em100: em100 device structure
*
* out(16 bytes): 0xbd 0 .. 0
*/
int reset_spi_trace(struct em100 *em100)
{
unsigned char cmd[16];
memset(cmd, 0, 16);
cmd[0] = 0xbd; /* reset SPI trace buffer*/
if (!send_cmd(em100->dev, cmd)) {
return 0;
}
return 1;
}
/**
* read_spi_trace: fetch SPI trace data
* @param em100: em100 device structure
* globals: curpos, counter, cmdid
*
* out(16 bytes): bc 00 00 00 08 00 00 00 00 15 00 00 00 00 00 00
* in(8x8192 bytes): 2 bytes (BE) number of records (0..0x3ff),
* then records of 8 bytes each
*/
static unsigned int counter = 0;
static unsigned char curpos = 0;
static unsigned char cmdid = 0xff; // timestamp, so never a valid command id
#define REPORT_BUFFER_LENGTH 8192
#define REPORT_BUFFER_COUNT 8
static int read_report_buffer(struct em100 *em100,
unsigned char reportdata[REPORT_BUFFER_COUNT][REPORT_BUFFER_LENGTH])
{
unsigned char cmd[16] = {0};
int len;
unsigned int report;
cmd[0] = 0xbc; /* read SPI trace buffer*/
/*
* Trace length, unit is 4k according to specs
*
* cmd1..cmd4 are probably u32BE on how many
* reports (8192 bytes each) to fetch
*/
cmd[1] = 0x00;
cmd[2] = 0x00;
cmd[3] = 0x00;
cmd[4] = REPORT_BUFFER_COUNT;
/* Timeout in ms */
cmd[5] = 0x00;
cmd[6] = 0x00;
cmd[7] = 0x00;
cmd[8] = 0x00;
/* Trace Config
* [1:0] 00 start/stop spi trace according to emulation status
* 01 start when TraceConfig[2] == 1
* 10 start when trig signal goes high
* 11 RFU
* [2] When TraceConfig[1:0] == 01 this bit starts the trace
* [7:3] RFU
*/
cmd[9] = 0x15;
if (!send_cmd(em100->dev, cmd)) {
printf("sending trace command failed\n");
return 0;
}
for (report = 0; report < REPORT_BUFFER_COUNT; report++) {
len = get_response(em100->dev, &reportdata[report][0],
REPORT_BUFFER_LENGTH);
if (len != REPORT_BUFFER_LENGTH) {
printf("error, report length = %d instead of %d.\n\n",
len, REPORT_BUFFER_LENGTH);
return 0;
}
}
return 1;
}
struct spi_cmd_values {
const char *cmd_name;
uint8_t cmd;
uint8_t uses_address;
uint8_t pad_bytes;
};
struct spi_cmd_values spi_command_list[] = {
/* name cmd, addr, pad */
{"write status register", 0x01, 0, 0},
{"page program", 0x02, 1, 0},
{"read", 0x03, 1, 0},
{"write disable", 0x04, 0, 0},
{"read status register", 0x05, 0, 0},
{"write enable", 0x06, 0, 0},
{"fast read", 0x0b, 1, 1},
{"EM100 specific", 0x11, 0, 0},
{"fast dual read", 0x3b, 1, 2},
{"chip erase", 0x60, 0, 0},
{"read JEDEC ID", 0x9f, 0, 0},
{"chip erase", 0xc7, 0, 0},
{"sector erase", 0xd8, 1, 0},
{"unknown command", 0xff, 0, 0}
};
static struct spi_cmd_values * get_command_vals(uint8_t command) {
/* cache last command so a search isn't needed every time */
static struct spi_cmd_values *spi_cmd = &spi_command_list[3]; /* init to read */
int i;
if (spi_cmd->cmd != command) {
for (i = 0; spi_command_list[i].cmd != 0xff; i++) {
if (spi_command_list[i].cmd == command)
break;
}
spi_cmd = &spi_command_list[i];
}
return spi_cmd;
}
#define MAX_TRACE_BLOCKLENGTH 6
int read_spi_trace(struct em100 *em100, int display_terminal,
unsigned long addr_offset)
{
unsigned char reportdata[REPORT_BUFFER_COUNT][REPORT_BUFFER_LENGTH] =
{{0}};
unsigned char *data;
unsigned int count, i, report;
static int outbytes = 0;
static int additional_pad_bytes = 0;
static unsigned int address = 0;
static unsigned long long timestamp = 0;
static unsigned long long start_timestamp = 0;
static struct spi_cmd_values *spi_cmd_vals = &spi_command_list[3];
if (!read_report_buffer(em100, reportdata))
return 0;
for (report = 0; report < REPORT_BUFFER_COUNT; report++) {
data = &reportdata[report][0];
count = (data[0] << 8) | data[1];
if (count > 1022) {
printf("Warning: EM100pro sends too much data.\n");
count = 1022;
}
for (i = 0; i < count; i++) {
unsigned int j = additional_pad_bytes;
additional_pad_bytes = 0;
unsigned char cmd = data[2 + i*8];
if (cmd == 0xff) {
/* timestamp */
timestamp = data[2 + i*8 + 2];
timestamp = (timestamp << 8) | data[2 + i*8 + 3];
timestamp = (timestamp << 8) | data[2 + i*8 + 4];
timestamp = (timestamp << 8) | data[2 + i*8 + 5];
timestamp = (timestamp << 8) | data[2 + i*8 + 6];
timestamp = (timestamp << 8) | data[2 + i*8 + 7];
if (display_terminal)
read_spi_terminal(em100, 1);
continue;
}
/* from here, it must be data */
if (cmd != cmdid) {
unsigned char spi_command = data[i * 8 + 4];
spi_cmd_vals = get_command_vals(spi_command);
/* new command */
cmdid = cmd;
if (counter == 0)
start_timestamp = timestamp;
/* set up address if used by this command*/
if (!spi_cmd_vals->uses_address) {
j = 1; /* skip command byte */
} else {
address = (data[i * 8 + 5] << 16) +
(data[i * 8 + 6] << 8) +
data[i * 8 + 7];
/* skip command, address bytes, and padding */
j = 4 + spi_cmd_vals->pad_bytes;
if (j > MAX_TRACE_BLOCKLENGTH) {
additional_pad_bytes = j -
MAX_TRACE_BLOCKLENGTH;
j = MAX_TRACE_BLOCKLENGTH;
}
}
printf("\nTime: %06lld.%08lld",
(timestamp - start_timestamp) /
100000000,
(timestamp - start_timestamp) %
100000000);
printf(" command # %-6d : 0x%02x - %s",
++counter, spi_command,
spi_cmd_vals->cmd_name);
curpos = 0;
outbytes = 0;
}
/* this exploits 8bit wrap around in curpos */
unsigned char blocklen = (data[2 + i*8 + 1] - curpos);
blocklen /= 8;
for (; j < blocklen; j++) {
if (outbytes == 0) {
if (spi_cmd_vals->uses_address) {
printf("\n%08lx : ",
addr_offset +
address);
} else {
printf("\n : ");
}
}
printf("%02x ", data[i * 8 + 4 + j]);
outbytes++;
if (outbytes == 16) {
outbytes = 0;
if (spi_cmd_vals->uses_address)
address += 16;
}
}
// this is because the em100 counts funny
curpos = data[2 + i*8 + 1] + 0x10;
fflush(stdout);
}
}
return 1;
}
#define UFIFO_SIZE 512
#define UFIFO_TIMEOUT 0x00
/*
* Polls the uFIFO buffer to see if there's any data. The HT registers don't
* seem to ever be updated to reflect that there's data present, and the
* Dediprog software doesn't use them either.
*
* Multiple messages can be in a single uFIFO transfer, so loop through
* the data looking for the signature.
*/
int read_spi_terminal(struct em100 *em100, int show_counter) {
unsigned char data[UFIFO_SIZE] = { 0 };
static unsigned int msg_counter = 1; /* Number of messages */
uint16_t data_length;
unsigned char *data_start;
unsigned int j, k;
struct em100_msg *msg = NULL;
if (!read_ufifo(em100, UFIFO_SIZE, UFIFO_TIMEOUT, &data[0]))
return 0;
/* the first two bytes are the amount of valid data */
data_length = (data[0] << 8) + data[1];
if (data_length == 0)
return 1;
/* actual data starts after the length */
data_start = &data[sizeof(uint16_t)];
/* examine data; stop when we run out of message or buffer */
for (j = 0; j < data_length &&
j < UFIFO_SIZE - sizeof(struct em100_msg_header); j++) {
msg = (struct em100_msg *)(data_start + j);
if (msg->header.signature == EM100_MSG_SIGNATURE) {
if (show_counter)
printf("\nHT%06d: ", msg_counter);
/* print message byte according to format */
for (k = 0; k < msg->header.data_length; k++) {
if (&msg->data[k] >= data_start + data_length)
break;
if (&msg->data[k] >= &data[0] + UFIFO_SIZE)
break;
switch (msg->header.data_type) {
case ht_checkpoint_1byte:
case ht_checkpoint_2bytes:
case ht_checkpoint_4bytes:
case ht_hexadecimal_data:
case ht_timestamp_data:
printf("%02x ", msg->data[k]);
break;
case ht_ascii_data:
printf("%c", msg->data[k]);
break;
case ht_lookup_table:
/* TODO - support lookup table */
printf("Lookup unsupported: %02x%02x",
msg->data[k], msg->data[k + 1]);
k++;
break;
}
}
/* advance to the end of the message */
j += msg->header.data_length +
sizeof(struct em100_msg_header) - 1;
msg_counter++;
fflush(stdout);
}
}
return 1;
}
int init_spi_terminal (struct em100 *em100)
{
int retval = 0x01;
uint16_t val;
retval &= write_ht_register(em100, ufifo_data_fmt_reg, 0);
retval &= write_ht_register(em100, status_reg, START_SPI_EMULATION);
/* set em100 to recognize spi command 0x11 */
retval &= write_fpga_register(em100, 0x82, EM100_SPECIFIC_CMD);
retval &= read_fpga_register(em100, 0x28, &val);
return retval;
}