blob: bbbb0165be8fdd7402095ee5ba874228d00236d4 [file] [log] [blame]
/*
* Copyright (c) "2012,2014" The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/*
*
* $ATH_LICENSE_HOSTSDK0_C$
*/
#include <sys/types.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/types.h>
#include <linux/if.h>
#include <linux/wireless.h>
#include <linux/version.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>
#include <a_osapi.h>
#include <athdefs.h>
#include <a_types.h>
#include "apb_athr_wlan_map.h"
#include "rtc_soc_reg.h"
#include "efuse_reg.h"
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
/*
* This is a user-level agent which provides diagnostic read/write
* access to Target space. This may be used
* to collect information for analysis
* to read/write Target registers
* etc.
*/
#define DIAG_READ_TARGET 1
#define DIAG_WRITE_TARGET 2
#define DIAG_READ_WORD 3
#define DIAG_WRITE_WORD 4
#define DIAG_DUMP_TARGET 5
#define ADDRESS_FLAG 0x0001
#define LENGTH_FLAG 0x0002
#define PARAM_FLAG 0x0004
#define FILE_FLAG 0x0008
#define UNUSED0x010 0x0010
#define AND_OP_FLAG 0x0020
#define BITWISE_OP_FLAG 0x0040
#define QUIET_FLAG 0x0080
#define OTP_FLAG 0x0100
#define HEX_FLAG 0x0200
/* dump file mode,x: hex mode; other binary mode. */
#define UNUSED0x400 0x0400
#define DEVICE_FLAG 0x0800
#define TARGET_FLAG 0x1000
#define PATH_FLAG 0x2000
/* Limit malloc size when reading/writing file */
#define MAX_BUF (8*1024)
#define DUMP_DRAM_START_ADDR 0x400000
#define DUMP_DRAM_LEN 0x50000
#define PEREGRINE_REG_PART1_START_ADDR 0x4000
#define PEREGRINE_REG_PART1_LEN 0x2000
#define PEREGRINE_REG_PART2_START_ADDR 0x8000
#define PEREGRINE_REG_PART2_LEN 0x58000
#define AR6320V1_REG_PART1_START_ADDR 0x0 /*RTC_SOC_BASE_ADDRESS*/
#define AR6320V1_REG_PART1_LEN (0x800 - 0x0) /*WLAN_MBOX_BASE_ADDRESS - RTC_SOC_BASE_ADDRESS*/
#define AR6320V1_REG_PART2_START_ADDR 0x27000 /*STEREO_BASE_ADDRESS*/
#define AR6320V1_REG_PART2_LEN (0x60000 - 0x27000) /*USB_BASE_ADDRESS - STEREO_BASE_ADDRESS*/
struct ath_target_reg_info {
A_UINT32 reg_start;
A_UINT32 reg_len;
const char *reg_info;
const char *save_file;
};
static const struct ath_target_reg_info reg_ar9888_v2[] = {
{DUMP_DRAM_START_ADDR, DUMP_DRAM_LEN, "DRAM", "fwdump_prgr_v2_dram"},
{PEREGRINE_REG_PART1_START_ADDR, PEREGRINE_REG_PART1_LEN, "REG_PART1", "fwdump_prgr_v2_reg1"},
{PEREGRINE_REG_PART2_START_ADDR, PEREGRINE_REG_PART2_LEN, "REG_PART2", "fwdump_prgr_v2_reg2"},
{0, 0, 0, 0}
};
static const struct ath_target_reg_info reg_ar6320_v1[] = {
{DUMP_DRAM_START_ADDR, DUMP_DRAM_LEN, "DRAM", "fwdump_rome_v1_dram"},
{AR6320V1_REG_PART1_START_ADDR, AR6320V1_REG_PART1_LEN, "REG_PART1", "fwdump_rome_v1_reg1"},
{AR6320V1_REG_PART2_START_ADDR, AR6320V1_REG_PART2_LEN, "REG_PART2", "fwdump_rome_v1_reg2"},
{0, 0, 0, 0}
};
#define INVALID_TARGET_INDEX 0xffff
#define MIN_TARGET_INDEX 0
#define MAX_TARGET_INDEX 2
struct ath_target_info {
const char *name;
const struct ath_target_reg_info *reg_info;
};
static const struct ath_target_info target_info[] = {
{"AR9888_v2", reg_ar9888_v2},
{"AR6320_v1", reg_ar6320_v1},
};
unsigned int flag;
const char *progname;
const char commands[] =
"commands and options:\n\
--get --address=<target word address>\n\
--set --address=<target word address> --[value|param]=<value>\n\
--or=<OR-ing value>\n\
--and=<AND-ing value>\n\
--read --address=<target address> --length=<bytes> --file=<filename>\n\
--write --address=<target address> --file=<filename>\n\
--[value|param]=<value>\n\
--otp --read --address=<otp offset> --length=<bytes> --file=<filename>\n\
--otp --write --address=<otp offset> --file=<filename>\n\
--dump --target=<target name> [--hex] [--path=<pathname>]\n\
--quiet\n\
--device=<device name> (if not default)\n\
The options can also be given in the abbreviated form --option=x or -o x.\n\
The options can be given in any order.";
#define A_ROUND_UP(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
#define quiet() (flag & QUIET_FLAG)
#define nqprintf(args...) if (!quiet()) {printf(args);}
#define min(x,y) ((x) < (y) ? (x) : (y))
void ReadTargetRange(int dev, A_UINT32 address, A_UINT8 *buffer,
A_UINT32 length);
void ReadTargetWord(int dev, A_UINT32 address, A_UINT32 *buffer);
void WriteTargetRange(int dev, A_UINT32 address, A_UINT8 *buffer,
A_UINT32 length);
void WriteTargetWord(int dev, A_UINT32 address, A_UINT32 value);
int ValidWriteOTP(int dev, A_UINT32 address, A_UINT8 *buffer, A_UINT32 length);
static inline void *
MALLOC(int nbytes)
{
void *p= malloc(nbytes);
if (!p)
{
fprintf(stderr, "err -Cannot allocate memory\n");
}
return p;
}
void
usage(void)
{
fprintf(stderr, "usage:\n%s ", progname);
fprintf(stderr, "%s\n", commands);
exit(-1);
}
void
list_supported_target_names()
{
int i, target_num = sizeof(target_info)/sizeof(target_info[0]);
fprintf(stderr, "supported target parameter as follow:\n");
for (i = 0; i < target_num; i++) {
fprintf(stderr, "\t--target=%s\n", target_info[i].name);
}
}
void
ReadTargetRange(int dev, A_UINT32 address, A_UINT8 *buffer, A_UINT32 length)
{
int nbyte;
unsigned int remaining;
(void)lseek(dev, address, SEEK_SET);
remaining = length;
while (remaining) {
nbyte = read(dev, buffer, (size_t)remaining);
if (nbyte <= 0) {
fprintf(stderr, "err %s failed (nbyte=%d, address=0x%x"
" remaining=%d).\n",
__FUNCTION__, nbyte, address, remaining);
exit(1);
}
remaining -= nbyte;
buffer += nbyte;
address += nbyte;
}
}
void
ReadTargetWord(int dev, A_UINT32 address, A_UINT32 *buffer)
{
ReadTargetRange(dev, address, (A_UINT8 *)buffer, sizeof(*buffer));
}
void
ReadTargetOTP(int dev, A_UINT32 offset, A_UINT8 *buffer, A_UINT32 length)
{
A_UINT32 status_mask;
A_UINT32 otp_status, i;
/* Enable OTP reads */
WriteTargetWord(dev, RTC_SOC_BASE_ADDRESS+OTP_OFFSET, OTP_VDD12_EN_SET(1));
status_mask = OTP_STATUS_VDD12_EN_READY_SET(1);
do {
ReadTargetWord(dev, RTC_SOC_BASE_ADDRESS+OTP_STATUS_OFFSET,
&otp_status);
} while ((otp_status & OTP_STATUS_VDD12_EN_READY_MASK) != status_mask);
/* Conservatively set OTP read timing */
WriteTargetWord(dev, EFUSE_BASE_ADDRESS+RD_STROBE_PW_REG_OFFSET, 6);
/* Read data from OTP */
for (i=0; i<length; i++, offset++) {
A_UINT32 efuse_word;
ReadTargetWord(dev, EFUSE_BASE_ADDRESS+EFUSE_INTF0_OFFSET+(offset<<2),
&efuse_word);
buffer[i] = (A_UINT8)efuse_word;
}
/* Disable OTP */
WriteTargetWord(dev, RTC_SOC_BASE_ADDRESS+OTP_OFFSET, 0);
}
void
WriteTargetRange(int dev, A_UINT32 address, A_UINT8 *buffer, A_UINT32 length)
{
int nbyte;
unsigned int remaining;
(void)lseek(dev, address, SEEK_SET);
remaining = length;
while (remaining) {
nbyte = write(dev, buffer, (size_t)remaining);
if (nbyte <= 0) {
fprintf(stderr, "err %s failed (nbyte=%d, address=0x%x"
" remaining=%d).\n",
__FUNCTION__, nbyte, address, remaining);
exit(1);
}
remaining -= nbyte;
buffer += nbyte;
address += nbyte;
}
}
void
WriteTargetWord(int dev, A_UINT32 address, A_UINT32 value)
{
A_UINT32 param = value;
WriteTargetRange(dev, address, (A_UINT8 *)&param, sizeof(param));
}
#define BAD_OTP_WRITE(have, want) ((((have) ^ (want)) & (have)) != 0)
/*
* Check if the current contents of OTP and the desired
* contents specified by buffer/length are compatible.
* If we're trying to CLEAR an OTP bit, then this request
* is invalid.
* returns: 0-->INvalid; 1-->valid
*/
int
ValidWriteOTP(int dev, A_UINT32 offset, A_UINT8 *buffer, A_UINT32 length)
{
A_UINT32 i;
A_UINT8 *otp_contents;
otp_contents = MALLOC(length);
if (otp_contents == NULL)
return 0;
ReadTargetOTP(dev, offset, otp_contents, length);
for (i=0; i<length; i++) {
if (BAD_OTP_WRITE(otp_contents[i], buffer[i])) {
fprintf(stderr, "Abort. Cannot change offset %d from 0x%02x"
" to 0x%02x\n",
offset+i, otp_contents[i], buffer[i]);
free(otp_contents);
return 0;
}
}
free(otp_contents);
return 1;
}
/*
* This is NOT the ideal way to write OTP since it does not handle
* media errors. It's much better to use the otpstream_* API.
* This capability is here to help salvage parts that have previously
* had OTP written.
*/
void
WriteTargetOTP(int dev, A_UINT32 offset, A_UINT8 *buffer, A_UINT32 length)
{
A_UINT32 status_mask;
A_UINT32 otp_status, i;
/* Enable OTP read/write power */
WriteTargetWord(dev, RTC_SOC_BASE_ADDRESS+OTP_OFFSET,
OTP_VDD12_EN_SET(1) | OTP_LDO25_EN_SET(1));
status_mask = OTP_STATUS_VDD12_EN_READY_SET(1) |
OTP_STATUS_LDO25_EN_READY_SET(1);
do {
ReadTargetWord(dev, RTC_SOC_BASE_ADDRESS+OTP_STATUS_OFFSET,
&otp_status);
} while ((otp_status & (OTP_STATUS_VDD12_EN_READY_MASK|
OTP_STATUS_LDO25_EN_READY_MASK)) != status_mask);
/* Conservatively set OTP read/write timing for 110MHz core clock */
WriteTargetWord(dev, EFUSE_BASE_ADDRESS+VDDQ_SETTLE_TIME_REG_OFFSET, 2200);
WriteTargetWord(dev, EFUSE_BASE_ADDRESS+PG_STROBE_PW_REG_OFFSET, 605);
WriteTargetWord(dev, EFUSE_BASE_ADDRESS+RD_STROBE_PW_REG_OFFSET, 6);
/* Enable eFuse for write */
WriteTargetWord(dev, EFUSE_BASE_ADDRESS+EFUSE_WR_ENABLE_REG_OFFSET,
EFUSE_WR_ENABLE_REG_V_SET(1));
WriteTargetWord(dev, EFUSE_BASE_ADDRESS+BITMASK_WR_REG_OFFSET, 0x00);
/* Write data to OTP */
for (i=0; i<length; i++, offset++) {
A_UINT32 efuse_word;
A_UINT32 readback;
int attempt;
#define EFUSE_WRITE_COUNT 3
efuse_word = (A_UINT32)buffer[i];
for (attempt=1; attempt<=EFUSE_WRITE_COUNT; attempt++) {
WriteTargetWord(dev,
EFUSE_BASE_ADDRESS+EFUSE_INTF0_OFFSET+(offset<<2),
efuse_word);
}
/* verify */
ReadTargetWord(dev, EFUSE_BASE_ADDRESS+EFUSE_INTF0_OFFSET+(offset<<2),
&readback);
if (efuse_word != readback) {
fprintf(stderr, "OTP write failed. Offset=%d, Value=0x%x,"
" Readback=0x%x\n", offset, efuse_word, readback);
break;
}
}
/* Disable OTP */
WriteTargetWord(dev, RTC_SOC_BASE_ADDRESS+OTP_OFFSET, 0);
}
void
DumpTargetMem(int dev, unsigned int target_idx, char *pathname)
{
const struct ath_target_reg_info *reg_info;
FILE * dump_fd;
char filename[PATH_MAX], tempfn[PATH_MAX];
A_UINT8 *buffer;
unsigned int i, address, length, remaining;
if ((target_idx < MIN_TARGET_INDEX) || (target_idx >= MAX_TARGET_INDEX))
return;
buffer = (A_UINT8 *)MALLOC(MAX_BUF);
if (buffer == NULL)
return;
reg_info = target_info[target_idx].reg_info;
while ((reg_info->reg_start != 0) || (reg_info->reg_len != 0)) {
memset(filename, 0, sizeof(filename));
snprintf(filename, sizeof(filename), "%s%s", pathname,
reg_info->save_file);
snprintf(tempfn, sizeof(tempfn), "%s", filename);
if(flag & HEX_FLAG) {
snprintf(filename, sizeof(filename), "%s.txt", tempfn);
} else {
snprintf(filename, sizeof(filename), "%s.bin", tempfn);
}
if ((dump_fd = fopen(filename, "wb+")) == NULL) {
fprintf(stderr, "err %s cannot create/open output file (%s)\n",
__FUNCTION__, filename);
reg_info++;
continue;
}
remaining = length = reg_info->reg_len;
address = reg_info->reg_start;
if(flag & HEX_FLAG) {
fprintf(dump_fd,"target mem dump area[0x%08x - 0x%08x]",address,
address+length);
}
nqprintf("DIAG Read Target (address: 0x%x, length: %d, filename: %s)\n",
address, length, filename);
while (remaining) {
length = (remaining > MAX_BUF) ? MAX_BUF : remaining;
ReadTargetRange(dev, address, buffer, length);
if(flag & HEX_FLAG) {
for(i=0; i<length; i+=4) {
if(i%16 == 0)
fprintf(dump_fd,"\n0x%08x:\t",address+i);
fprintf(dump_fd,"0x%08x\t",*(A_UINT32*)(buffer+i));
}
} else {
fwrite(buffer,1 , length, dump_fd);
}
remaining -= length;
address += length;
}
fclose(dump_fd);
reg_info++;
}
free(buffer);
}
unsigned int
parse_address(char *optarg)
{
unsigned int address;
/* may want to add support for symbolic addresses here */
address = strtoul(optarg, NULL, 0);
return address;
}
unsigned int
parse_target_index(char *optarg)
{
unsigned int i, index = INVALID_TARGET_INDEX;
for (i = 0; i < sizeof(target_info)/sizeof(target_info[0]); i++) {
if (strncmp(optarg, target_info[i].name, sizeof(target_info[i].name)) == 0) {
/* found */
index = i;
break;
}
}
return index;
}
int
main (int argc, char **argv) {
int c, fd, dev;
int i;
FILE * dump_fd;
unsigned int address = 0, target_idx = 0, length = 0;
A_UINT32 param = 0;
char filename[PATH_MAX], tempfn[PATH_MAX];
char pathname[PATH_MAX];
char devicename[PATH_MAX];
unsigned int cmd = 0;
A_UINT8 *buffer;
unsigned int bitwise_mask = 0;
progname = argv[0];
if (argc == 1) usage();
flag = 0;
memset(filename, '\0', sizeof(filename));
memset(devicename, '\0', sizeof(devicename));
memset(tempfn, '\0', sizeof(tempfn));
while (1) {
int option_index = 0;
static struct option long_options[] = {
{"address", 1, NULL, 'a'},
{"and", 1, NULL, 'n'},
{"device", 1, NULL, 'D'},
{"dump", 0, NULL, 'd'},
{"get", 0, NULL, 'g'},
{"file", 1, NULL, 'f'},
{"hex", 0, NULL, 'x'},
{"length", 1, NULL, 'l'},
{"or", 1, NULL, 'o'},
{"otp", 0, NULL, 'O'},
{"param", 1, NULL, 'p'},
{"path", 1, NULL, 'P'},
{"quiet", 0, NULL, 'q'},
{"read", 0, NULL, 'r'},
{"set", 0, NULL, 's'},
{"target", 1, NULL, 't'},
{"value", 1, NULL, 'p'},
{"write", 0, NULL, 'w'},
{0, 0, 0, 0}
};
c = getopt_long (argc, argv, "xrwgsqdOf:l:a:p:c:n:o:D:t:P:",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'r':
cmd = DIAG_READ_TARGET;
break;
case 'w':
cmd = DIAG_WRITE_TARGET;
break;
case 'g':
cmd = DIAG_READ_WORD;
break;
case 's':
cmd = DIAG_WRITE_WORD;
break;
case 'd':
cmd = DIAG_DUMP_TARGET;
break;
case 'f':
memset(filename, '\0', sizeof(filename));
snprintf(filename, sizeof(filename), "%s", optarg);
flag |= FILE_FLAG;
break;
case 'l':
length = parse_address(optarg);
flag |= LENGTH_FLAG;
break;
case 'a':
address = parse_address(optarg);
flag |= ADDRESS_FLAG;
break;
case 'p':
param = strtoul(optarg, NULL, 0);
flag |= PARAM_FLAG;
break;
case 'n':
flag |= PARAM_FLAG | AND_OP_FLAG | BITWISE_OP_FLAG;
bitwise_mask = strtoul(optarg, NULL, 0);
break;
case 'o':
flag |= PARAM_FLAG | BITWISE_OP_FLAG;
bitwise_mask = strtoul(optarg, NULL, 0);
break;
case 'O':
flag |= OTP_FLAG;
break;
case 'q':
flag |= QUIET_FLAG;
break;
case 't':
target_idx = parse_target_index(optarg);
if (target_idx != INVALID_TARGET_INDEX) {
flag |= TARGET_FLAG;
}
break;
case 'P':
memset(pathname, '\0', sizeof(pathname));
snprintf(pathname, sizeof(pathname), "%s", optarg);
if (pathname[strlen(pathname)-1] != '/') {
snprintf(tempfn, sizeof(tempfn),"%s", pathname);
snprintf(pathname, sizeof(pathname),"%s/", tempfn);
}
flag |= PATH_FLAG;
break;
case 'D':
snprintf(devicename, sizeof(devicename), "%s%s", optarg,
"/athdiagpfs");
flag |= DEVICE_FLAG;
break;
case 'x':
flag |= HEX_FLAG;
break;
default:
fprintf(stderr, "Cannot understand '%s'\n", argv[option_index]);
usage();
}
}
for (;;) {
/* DIAG uses a sysfs special file which may be auto-detected */
if (!(flag & DEVICE_FLAG)) {
FILE *find_dev;
size_t nbytes;
/*
* Convenience: if no device was specified on the command
* line, try to figure it out. Typically there's only a
* single device anyway.
*/
find_dev = popen("echo /proc/cld/athdiagpfs", "r");
if (find_dev) {
nbytes=fread(devicename, 1, sizeof(devicename), find_dev);
pclose(find_dev);
if (nbytes > 15) {
/* auto-detect possibly successful */
devicename[nbytes-1]='\0'; /* replace \n with 0 */
} else {
snprintf(devicename, sizeof(devicename), "%s",
"unknown_DIAG_device");
}
}
}
dev = open(devicename, O_RDWR);
if (dev >= 0) {
break; /* successfully opened diag special file */
} else {
fprintf(stderr, "err %s failed (%d) to open DIAG file (%s)\n",
__FUNCTION__, errno, devicename);
exit(1);
}
}
switch(cmd)
{
case DIAG_READ_TARGET:
if ((flag & (ADDRESS_FLAG | LENGTH_FLAG | FILE_FLAG)) ==
(ADDRESS_FLAG | LENGTH_FLAG | FILE_FLAG))
{
if ((dump_fd = fopen(filename, "wb+")) == NULL)
{
fprintf(stderr, "err %s cannot create/open output file (%s)\n",
__FUNCTION__, filename);
exit(1);
}
buffer = (A_UINT8 *)MALLOC(MAX_BUF);
if (buffer == NULL) {
fclose(dump_fd);
close(dev);
exit(1);
}
nqprintf(
"DIAG Read Target (address: 0x%x, length: %d,"
" filename: %s)\n", address, length, filename);
{
unsigned int remaining = length;
if(flag & HEX_FLAG)
{
if (flag & OTP_FLAG) {
fprintf(dump_fd,"target otp dump area"
" [0x%08x - 0x%08x]",address,address+length);
} else {
fprintf(dump_fd,"target mem dump area"
" [0x%08x - 0x%08x]",address,address+length);
}
}
while (remaining)
{
length = (remaining > MAX_BUF) ? MAX_BUF : remaining;
if (flag & OTP_FLAG) {
ReadTargetOTP(dev, address, buffer, length);
} else {
ReadTargetRange(dev, address, buffer, length);
}
if(flag & HEX_FLAG)
{
for(i=0;i<(int)length;i+=4)
{
if(i%16 == 0)
fprintf(dump_fd,"\n0x%08x:\t",address+i);
fprintf(dump_fd,"0x%08x\t",*(A_UINT32*)(buffer+i));
}
}
else
{
fwrite(buffer,1 , length, dump_fd);
}
remaining -= length;
address += length;
}
}
fclose(dump_fd);
free(buffer);
} else {
usage();
}
break;
case DIAG_WRITE_TARGET:
if (!(flag & ADDRESS_FLAG))
{
usage(); /* no address specified */
}
if (!(flag & (FILE_FLAG | PARAM_FLAG)))
{
usage(); /* no data specified */
}
if ((flag & FILE_FLAG) && (flag & PARAM_FLAG))
{
usage(); /* too much data specified */
}
if (flag & FILE_FLAG)
{
struct stat filestat;
unsigned int file_length;
if ((fd = open(filename, O_RDONLY)) < 0)
{
fprintf(stderr, "err %s Could not open file"
" (%s)\n", __FUNCTION__, filename);
exit(1);
}
memset(&filestat, '\0', sizeof(struct stat));
buffer = (A_UINT8 *)MALLOC(MAX_BUF);
if (buffer == NULL) {
close(fd);
exit(1);
}
fstat(fd, &filestat);
file_length = filestat.st_size;
if (file_length == 0) {
fprintf(stderr, "err %s Zero length input file"
" (%s)\n", __FUNCTION__, filename);
exit(1);
}
if (flag & LENGTH_FLAG) {
if (length > file_length) {
fprintf(stderr, "err %s file %s: length (%d)"
" too short (%d)\n", __FUNCTION__,
filename, file_length, length);
exit(1);
}
} else {
length = file_length;
}
nqprintf(
"DIAG Write Target (address: 0x%x, filename: %s,"
" length: %d)\n", address, filename, length);
}
else
{ /* PARAM_FLAG */
nqprintf(
"DIAG Write Word (address: 0x%x, value: 0x%x)\n",
address, param);
length = sizeof(param);
buffer = (A_UINT8 *)&param;
fd = -1;
}
/*
* Write length bytes of data to memory/OTP.
* Data is either present in buffer OR
* needs to be read from fd in MAX_BUF chunks.
*
* Within the kernel, the implementation of
* DIAG_WRITE_TARGET further limits the size
* of each transfer over the interconnect.
*/
{
unsigned int remaining;
unsigned int otp_check_address = address;
if (flag & OTP_FLAG) {
/* Validate OTP write before committing anything */
remaining = length;
while (remaining)
{
int nbyte;
length = (remaining > MAX_BUF) ? MAX_BUF : remaining;
if (fd > 0)
{
nbyte = read(fd, buffer, length);
if (nbyte != (int)length) {
fprintf(stderr, "err %s read from file failed"
" (%d)\n", __FUNCTION__, nbyte);
exit(1);
}
}
if ((flag & OTP_FLAG) && !ValidWriteOTP(dev,
otp_check_address, buffer, length))
{
exit(1);
}
remaining -= length;
otp_check_address += length;
}
(void)lseek(fd, 0, SEEK_SET);
}
remaining = length;
while (remaining)
{
int nbyte;
length = (remaining > MAX_BUF) ? MAX_BUF : remaining;
if (fd > 0)
{
nbyte = read(fd, buffer, length);
if (nbyte != (int)length) {
fprintf(stderr, "err %s read from file failed"
" (%d)\n", __FUNCTION__, nbyte);
exit(1);
}
}
if (flag & OTP_FLAG) {
WriteTargetOTP(dev, address, buffer, length);
} else {
WriteTargetRange(dev, address, buffer, length);
}
remaining -= length;
address += length;
}
}
if (flag & FILE_FLAG) {
free(buffer);
close(fd);
}
break;
case DIAG_READ_WORD:
if ((flag & (ADDRESS_FLAG)) == (ADDRESS_FLAG))
{
nqprintf("DIAG Read Word (address: 0x%x)\n", address);
ReadTargetWord(dev, address, &param);
if (quiet()) {
printf("0x%x\n", param);
} else {
printf("Value in target at 0x%x: 0x%x (%d)\n",
address, param, param);
}
}
else usage();
break;
case DIAG_WRITE_WORD:
if ((flag & (ADDRESS_FLAG | PARAM_FLAG)) == (ADDRESS_FLAG | PARAM_FLAG))
{
A_UINT32 origvalue = 0;
if (flag & BITWISE_OP_FLAG) {
/* first read */
ReadTargetWord(dev, address, &origvalue);
param = origvalue;
/* now modify */
if (flag & AND_OP_FLAG) {
param &= bitwise_mask;
} else {
param |= bitwise_mask;
}
/* fall through to write out the parameter */
}
if (flag & BITWISE_OP_FLAG) {
if (quiet()) {
printf("0x%x\n", origvalue);
} else {
printf("DIAG Bit-Wise (%s) modify Word (address: 0x%x,"
" orig:0x%x, new: 0x%x, mask:0x%X)\n",
(flag & AND_OP_FLAG) ? "AND" : "OR", address,
origvalue, param, bitwise_mask );
}
} else{
nqprintf("DIAG Write Word (address: 0x%x, param:"
" 0x%x)\n", address, param);
}
WriteTargetWord(dev, address, param);
}
else usage();
break;
case DIAG_DUMP_TARGET:
if (!(flag & TARGET_FLAG)) {
list_supported_target_names();
usage(); /* no target specified */
}
if (!(flag & PATH_FLAG)) {
memset(pathname, '\0', sizeof(filename));
if (getcwd(pathname, sizeof(pathname)-1) != NULL)
printf("%s\n",pathname);
snprintf(tempfn, sizeof(tempfn), "%s", pathname);
snprintf(pathname, sizeof(pathname), "%s/", tempfn);
}
DumpTargetMem(dev, target_idx, pathname);
break;
default:
usage();
}
exit (0);
}