Professional Documents
Culture Documents
Driver Minion
Driver Minion
#include "config.h"
#include "compat.h"
#include "miner.h"
#include "klist.h"
#include <ctype.h>
#include <math.h>
#ifndef LINUX
static void minion_detect(__maybe_unused bool hotplug)
{
}
#else
#include <unistd.h>
#include <linux/spi/spidev.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <poll.h>
#if MINION_ROCKCHIP == 1
#define MINION_POWERCYCLE_GPIO 173
#define MINION_CHIP_OFF "1"
#define MINION_CHIP_ON "0"
#define MINION_CHIP_DELAY 100
#endif
// Power cycle if the xff_list is full and the tail is less than
// this long ago
#define MINION_POWER_TIME 60
/*
* Use pins for board selection
* If disabled, it will test chips just as 'pin 0'
* but never do any gpio - the equivalent of the previous 'no pins' code
*/
static bool usepins = false;
#define MINION_SPI_BUS 0
#define MINION_SPI_CHIP 0
#if MINION_ROCKCHIP == 0
#define MINION_SPI_SPEED 8000000
#else
#define MINION_SPI_SPEED 500000
#endif
#define MINION_SPI_BUFSIZ 1024
/*
* uS delays for GPIO pin access
*/
#define MINION_PIN_BEFORE cgsleep_us(33)
#define MINION_PIN_SLEEP cgsleep_us(133)
#define MINION_PIN_AFTER
#define MINION_MIN_CHIP 0
#define MINION_MAX_CHIP 11
/*
* TODO: These will need adjusting for final hardware
* Look them up and calculate them?
*/
#define MINION_QUE_MAX 64
#define MINION_QUE_HIGH 48
#define MINION_QUE_SEND 16
#define MINION_QUE_LOW 8
// How many 32 bit reports make up all the cores - 99 cores = 4 reps
#define MINION_CORE_REPS (int)((((MINION_CORES-1) >> 5) & 0xff) + 1)
// Init
#define SYS_RSTN_CTL_INIT (RSTN_CTL_RESET_CORES | \
RSTN_CTL_FLUSH_RESULTS | \
RSTN_CTL_FLUSH_CMD_QUEUE | \
RSTN_CTL_SPI_SW_RSTN | \
RSTN_CTL_SHA_MGR_RESET)
// Block change
#define SYS_RSTN_CTL_FLUSH (RSTN_CTL_RESET_CORES | \
RSTN_CTL_SPI_SW_RSTN | \
RSTN_CTL_FLUSH_CMD_QUEUE)
#if ENABLE_INT_NONO
// enable 'no nonce' report
#define SYS_MISC_CTL_DEFAULT 0x04
#else
#define SYS_MISC_CTL_DEFAULT 0x00
#endif
/*
* Temperature for MINION_SYS_TEMP_CTL 0x03 temp_thres [0:3]
* i.e. it starts at 120 and goes up in steps of 5 to 160
*/
#define MINION_TEMP_CTL_MIN 1
#define MINION_TEMP_CTL_MAX 9
#define MINION_TEMP_CTL_BITS 0x0f
#define MINION_TEMP_CTL_DEF 135
#define MINION_TEMP_CTL_STEP 5
#define MINION_TEMP_CTL_MIN_VALUE 120
#define MINION_TEMP_CTL_MAX_VALUE (MINION_TEMP_CTL_MIN_VALUE + \
(MINION_TEMP_CTL_STEP * \
(MINION_TEMP_CTL_MAX - MINION_TEMP_CTL_MIN)))
#define MINION_TEMP_DISABLE "disable"
#define MINION_TEMP_CTL_DISABLE -1
#define MINION_TEMP_CTL_DISABLE_VALUE 0x20
struct minion_header {
uint8_t chipid;
uint8_t reg;
uint8_t siz[2];
uint8_t data[4]; // placeholder
};
/*
* Number of times to try and get the SIG with each chip,
* if the chip returns neither of the above values
* TODO: maybe need some reset between tries, to handle a shift value?
*/
#define MINION_SIG_TRIES 3
/*
* TODO: Finding these means the chip is there - but how to fix it?
* The extra &'s are to ensure there is no sign bit issue since
* the sign bit carry in a C bit-shift is compiler dependent
*/
#define MINION_CHIP_SIG_SHIFT1 (((MINION_CHIP_SIG & 0x0000ffff) << 16) &
0xffff0000)
#define MINION_CHIP_SIG_SHIFT2 (((MINION_CHIP_SIG & 0x00ffffff) << 8) &
0xffffff00)
#define MINION_CHIP_SIG_SHIFT3 (((MINION_CHIP_SIG & 0xffffff00) >> 8) &
0x00ffffff)
#define MINION_CHIP_SIG_SHIFT4 (((MINION_CHIP_SIG & 0xffff0000) >> 16) &
0x0000ffff)
// When hash rate falls below this in the history hash rate, reset it
#define MINION_RESET_PERCENT 75.0
// When hash rate falls below this after the longer test time
#define MINION_RESET2_PERCENT 85.0
/*
* This is only valid since we avoid using task_id 0 for work
* However, it isn't really necessary since we only request
* the number of results the result buffer says it has
* However, it is a simple failsafe
*/
#define IS_RESULT(_res) ((_res)->status[1] || (_res)->status[0])
struct minion_result {
uint8_t status[DATA_SIZ];
uint8_t nonce[DATA_SIZ];
};
/*
* (MINION_SPI_BUFSIZ - HSIZE()) / MINION_RES_DATA_SIZ
* less a little bit to round it out
*/
#define MINION_MAX_RES 120
#define MIDSTATE_BYTES 32
#define MERKLE7_OFFSET 64
#define MERKLE_BYTES 12
struct minion_que {
uint8_t task_id[2];
uint8_t reserved[2];
uint8_t midstate[MIDSTATE_BYTES];
uint8_t merkle7[DATA_SIZ];
uint8_t ntime[DATA_SIZ];
uint8_t bits[DATA_SIZ];
};
/*
* Max time to wait before checking the task list
* Required, since only urgent tasks trigger an immediate check
* TODO: ? for 2TH/s
*/
#define MINION_TASK_mS 8
/*
* Max time to wait before checking the result list for nonces
* This can be long since it's only a failsafe
* cgsem_post is always sent if there are nonces ready to check
*/
#define MINION_NONCE_mS 888
/*
* Max time to wait before checking for results
* The interrupt doesn't occur until MINION_RESULT_INT_SIZE results are found
* See comment in minion_spi_reply() at poll()
*/
#define MINION_REPLY_mS 88
/*
* Max time to wait before returning the amount of work done
* A result interrupt will send a trigger for this also
* See comment in minion_scanwork()
* This avoids the cgminer master work loop spinning doing nothing
*/
#define MINION_SCAN_mS 88
/* If there was no reset for this long, clear the reset history
* (except the last one) since this means the current clock is ok
* with rare resets */
#define MINION_CLR_s 300
/* Floating point reset settings required for the code to work properly
* Basically: RESET2 must be after RESET and CLR must be after RESET2 */
static void define_test()
{
float test;
test = FREQ_DELAY(MINION_FREQ_MIN);
if (test >= MINION_HISTORY_s) {
quithere(1, "FREQ_DELAY(MINION_FREQ_MIN)=%f must be "
"< MINION_HISTORY_s=%d",
test, MINION_HISTORY_s);
}
test = FREQ2_DELAY(MINION_FREQ_MIN);
if (test >= MINION_HISTORY_s) {
quithere(1, "FREQ2_DELAY(MINION_FREQ_MIN)=%f must be "
"< MINION_HISTORY_s=%d",
test, MINION_HISTORY_s);
}
#if DO_IO_STATS
#define IO_STAT_NOW(_tv) cgtime(_tv)
#define IO_STAT_STORE(_sta, _fin, _lsta, _lfin, _tsd, _buf, _siz, _reply, _ioc) \
do { \
double _diff, _ldiff, _lwdiff, _1time; \
int _off; \
_diff = us_tdiff(_fin, _sta); \
_ldiff = us_tdiff(_lfin, _lsta); \
_lwdiff = us_tdiff(_sta, _lsta); \
_1time = us_tdiff(_tsd, _lfin); \
_off = (int)(_buf[1]) + (_reply >= 0 ? 0 : 0x100); \
minioninfo->summary.count++; \
minioninfo->summary.tsd += _1time; \
minioninfo->iostats[_off].count++; \
minioninfo->iostats[_off].tsd += _1time; \
if (_diff <= 0) { \
minioninfo->summary.zero_delay++; \
minioninfo->iostats[_off].zero_delay++; \
} else { \
minioninfo->summary.total_delay += _diff; \
if (minioninfo->summary.max_delay < _diff) \
minioninfo->summary.max_delay = _diff; \
if (minioninfo->summary.min_delay == 0 || \
minioninfo->summary.min_delay > _diff) \
minioninfo->summary.min_delay = _diff; \
minioninfo->iostats[_off].total_delay += _diff; \
if (minioninfo->iostats[_off].max_delay < _diff) \
minioninfo->iostats[_off].max_delay = _diff; \
if (minioninfo->iostats[_off].min_delay == 0 || \
minioninfo->iostats[_off].min_delay > _diff) \
minioninfo->iostats[_off].min_delay = _diff; \
} \
if (_ldiff <= 0) { \
minioninfo->summary.zero_dlock++; \
minioninfo->iostats[_off].zero_dlock++; \
} else { \
minioninfo->summary.total_dlock += _ldiff; \
if (minioninfo->summary.max_dlock < _ldiff) \
minioninfo->summary.max_dlock = _ldiff; \
if (minioninfo->summary.min_dlock == 0 || \
minioninfo->summary.min_dlock > _ldiff) \
minioninfo->summary.min_dlock = _ldiff; \
minioninfo->iostats[_off].total_dlock += _ldiff; \
if (minioninfo->iostats[_off].max_dlock < _ldiff) \
minioninfo->iostats[_off].max_dlock = _ldiff; \
if (minioninfo->iostats[_off].min_dlock == 0 || \
minioninfo->iostats[_off].min_dlock > _ldiff) \
minioninfo->iostats[_off].min_dlock = _ldiff; \
} \
minioninfo->summary.total_dlwait += _lwdiff; \
minioninfo->iostats[_off].total_dlwait += _lwdiff; \
if (_siz == 0) { \
minioninfo->summary.zero_bytes++; \
minioninfo->iostats[_off].zero_bytes++; \
} else { \
minioninfo->summary.total_bytes += _siz; \
if (minioninfo->summary.max_bytes < _siz) \
minioninfo->summary.max_bytes = _siz; \
if (minioninfo->summary.min_bytes == 0 || \
minioninfo->summary.min_bytes > _siz) \
minioninfo->summary.min_bytes = _siz; \
minioninfo->iostats[_off].total_bytes += _siz; \
if (minioninfo->iostats[_off].max_bytes < _siz) \
minioninfo->iostats[_off].max_bytes = _siz; \
if (minioninfo->iostats[_off].min_bytes == 0 || \
minioninfo->iostats[_off].min_bytes > _siz) \
minioninfo->iostats[_off].min_bytes = _siz; \
} \
} while (0);
// these 3 fields are ignored for now since all are '1'
uint64_t total_ioc; // SPI_IOC_MESSAGE(x)
uint64_t min_ioc;
uint64_t max_ioc;
static double time_bands[] = { 0.1, 0.5, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0 };
#define TIME_BANDS ((int)(sizeof(time_bands)/sizeof(double)))
struct minion_info {
struct thr_info *thr;
struct thr_info spiw_thr;
struct thr_info spir_thr;
struct thr_info res_thr;
pthread_mutex_t spi_lock;
pthread_mutex_t sta_lock;
cgsem_t task_ready;
cgsem_t nonce_ready;
cgsem_t scan_work;
int spifd;
char gpiointvalue[64];
int gpiointfd;
// I/O or seconds
bool spi_reset_io;
int spi_reset_count;
time_t last_spi_reset;
uint64_t spi_resets;
uint64_t ioseq;
uint32_t next_task_id;
// Stats
uint64_t chip_nonces[MINION_CHIPS];
uint64_t chip_nononces[MINION_CHIPS];
uint64_t chip_good[MINION_CHIPS];
uint64_t chip_bad[MINION_CHIPS];
uint64_t chip_err[MINION_CHIPS];
uint64_t chip_dup[MINION_CHIPS];
uint64_t core_good[MINION_CHIPS][MINION_CORES+1];
uint64_t core_bad[MINION_CHIPS][MINION_CORES+1];
uint32_t chip_core_ena[MINION_CORE_REPS][MINION_CHIPS];
uint32_t chip_core_act[MINION_CORE_REPS][MINION_CHIPS];
uint64_t interrupts;
uint64_t result_interrupts;
uint64_t command_interrupts;
char last_interrupt[64];
pthread_mutex_t nonce_lock;
uint64_t new_nonces;
uint64_t ok_nonces;
uint64_t untested_nonces;
uint64_t tested_nonces;
uint64_t work_unrolled;
uint64_t work_rolled;
uint64_t spi_errors;
uint64_t fifo_spi_errors[MINION_CHIPS];
uint64_t res_spi_errors[MINION_CHIPS];
uint64_t use_res2[MINION_CHIPS];
uint64_t tasks_failed[MINION_CHIPS];
uint64_t tasks_recovered[MINION_CHIPS];
uint64_t nonces_failed[MINION_CHIPS];
uint64_t nonces_recovered[MINION_CHIPS];
struct timeval last_reset[MINION_CHIPS];
double do_reset[MINION_CHIPS];
bool flag_reset[MINION_CHIPS];
// Work items
K_LIST *wfree_list;
K_STORE *wwork_list;
K_STORE *wstale_list;
K_STORE *wque_list[MINION_CHIPS];
K_STORE *wchip_list[MINION_CHIPS];
uint64_t wwork_flushed;
uint64_t wque_flushed;
uint64_t wchip_staled;
// Task list
K_LIST *tfree_list;
K_STORE *task_list;
K_STORE *treply_list;
uint64_t next_tid;
// Nonce replies
K_LIST *rfree_list;
K_STORE *rnonce_list;
// Nonce history
K_LIST *hfree_list;
K_STORE *hchip_list[MINION_CHIPS];
int history_gen;
struct timeval chip_chk;
struct timeval chip_rpt;
double history_ghs[MINION_CHIPS];
// Point in history for MINION_RESET_s
int reset_time[MINION_CHIPS];
K_ITEM *reset_mark[MINION_CHIPS];
int reset_count[MINION_CHIPS];
// Point in history for MINION_RESET2_s
int reset2_time[MINION_CHIPS];
K_ITEM *reset2_mark[MINION_CHIPS];
int reset2_count[MINION_CHIPS];
// Performance history
K_LIST *pfree_list;
K_STORE *p_list[MINION_CHIPS];
// 0xff history
K_LIST *xfree_list;
K_STORE *xff_list;
time_t last_power_cycle;
uint64_t power_cycles;
time_t last_xff;
uint64_t xffs;
uint64_t last_displayed_xff;
#if DO_IO_STATS
// Total
IOSTAT summary;
// Two for each command plus wasted extras i.e. direct/fast lookup
// No error uses 0x0 to 0xff, error uses 0x100 to 0x1ff
IOSTAT iostats[0x200];
#endif
bool lednow[MINION_CHIPS];
bool setled[MINION_CHIPS];
bool initialised;
};
#if MINION_ROCKCHIP == 1
static bool minion_toggle_gpio(struct cgpu_info *minioncgpu, int gpionum)
{
struct minion_info *minioninfo = (struct minion_info *)(minioncgpu-
>device_data);
char pindir[64], ena[64], pin[8], dir[64];
char gpiointvalue[64];
struct stat st;
int file, err, chip;
ssize_t ret;
// Open it
snprintf(gpiointvalue, sizeof(gpiointvalue),
MINION_GPIO_SYS MINION_GPIO_PIN MINION_GPIO_VALUE,
gpionum);
int fd = open(gpiointvalue, O_WRONLY);
if (fd == -1) {
applog(LOG_ERR, "%s: failed7 to access GPIO pin %d (%d)",
minioncgpu->drv->dname,
gpionum, errno);
return false;
}
cgsleep_ms(MINION_CHIP_DELAY);
close(fd);
minioninfo->last_power_cycle = time(NULL);
minioninfo->power_cycles++;
// Reset all chip led counters
for (chip = 0; chip < (int)MINION_CHIPS; chip++) {
if (minioninfo->has_chip[chip])
minioninfo->chip_status[chip].first_nonce.tv_sec = 0L;
}
return true;
}
#endif
static void ready_work(struct cgpu_info *minioncgpu, struct work *work, bool
rolled)
{
struct minion_info *minioninfo = (struct minion_info *)(minioncgpu-
>device_data);
K_ITEM *item = NULL;
K_WLOCK(minioninfo->wfree_list);
item = k_unlink_head(minioninfo->wfree_list);
DATA_WORK(item)->work = work;
DATA_WORK(item)->task_id = 0;
memset(&(DATA_WORK(item)->sent), 0, sizeof(DATA_WORK(item)->sent));
DATA_WORK(item)->nonces = 0;
DATA_WORK(item)->urgent = false;
DATA_WORK(item)->rolled = rolled;
DATA_WORK(item)->errors = 0;
cgtime(&(DATA_WORK(item)->created));
k_add_head(minioninfo->wwork_list, item);
K_WUNLOCK(minioninfo->wfree_list);
}
K_WLOCK(minioninfo->rnonce_list);
item = k_unlink_tail(minioninfo->rnonce_list);
if (item) {
found = true;
*chip = DATA_RES(item)->chip;
*core = DATA_RES(item)->core;
*task_id = DATA_RES(item)->task_id;
*nonce = DATA_RES(item)->nonce;
*no_nonce = DATA_RES(item)->no_nonce;
memcpy(when, &(DATA_RES(item)->when), sizeof(*when));
*another = DATA_RES(item)->another;
*task_id2 = DATA_RES(item)->task_id2;
*nonce2 = DATA_RES(item)->nonce2;
k_free_head(minioninfo->rfree_list, item);
}
K_WUNLOCK(minioninfo->rnonce_list);
return found;
}
// For display_ioctl()
#define IOCTRL_LOG LOG_WARNING
#if MINION_SHOW_IO
static void display_ioctl(int reply, uint32_t osiz, uint8_t *obuf, uint32_t rsiz,
uint8_t *rbuf)
{
struct minion_result *res;
const char *name, *dir, *ex;
char buf[4096];
int i, rescount;
name = addr2txt(obuf[1]);
if (IS_ADDR_READ(obuf[1]))
dir = "from";
else
dir = "to";
buf[0] = '\0';
ex = "";
switch (obuf[1]) {
case READ_ADDR(MINION_SYS_CHIP_SIG):
case READ_ADDR(MINION_SYS_CHIP_STA):
break;
case WRITE_ADDR(MINION_SYS_SPI_LED):
case WRITE_ADDR(MINION_SYS_MISC_CTL):
case WRITE_ADDR(MINION_SYS_RSTN_CTL):
if (osiz > HSIZE()) {
ex = " wrote ";
__bin2hex(buf, obuf + HSIZE(), osiz - HSIZE());
} else
ex = " wrote nothing";
break;
default:
if (IS_ADDR_WRITE(obuf[1])) {
if (osiz > HSIZE()) {
ex = " wrote ";
__bin2hex(buf, obuf + HSIZE(), osiz - HSIZE());
} else
ex = " wrote nothing";
}
break;
}
if (reply < 0) {
applog(IOCTRL_LOG, "%s %s chipid %d osiz %d%s%s",
name, dir, (int)obuf[0], (int)osiz, ex, buf);
applog(IOCTRL_LOG, " reply was error %d", reply);
} else {
if (IS_ADDR_WRITE(obuf[1])) {
applog(IOCTRL_LOG, "%s %s chipid %d osiz %d%s%s",
name, dir, (int)obuf[0], (int)osiz, ex, buf);
applog(IOCTRL_LOG, " write ret was %d", reply);
} else {
switch (obuf[1]) {
case READ_ADDR(MINION_RES_DATA):
rescount = (int)((float)rsiz /
(float)MINION_RES_DATA_SIZ);
applog(IOCTRL_LOG, "%s %s chipid %d osiz %d%s%s",
name, dir, (int)obuf[0], (int)osiz,
ex, buf);
for (i = 0; i < rescount; i++) {
res = (struct minion_result *)(rbuf + osiz -
rsiz + (i * MINION_RES_DATA_SIZ));
if (!IS_RESULT(res)) {
applog(IOCTRL_LOG, " %s reply %d of %d -
none", name, i+1, rescount);
} else {
__bin2hex(buf, res->nonce, DATA_SIZ);
applog(IOCTRL_LOG, " %s reply %d of %d
%d(%d) was task 0x%04x"
" chipid %d core %d
gold %s nonce 0x%s",
name, i+1, rescount,
reply, rsiz,
RES_TASK(res),
(int)RES_CHIPID(res),
(int)RES_CORE(res),
(int)RES_GOLD(res) ? "Y" :
"N",
buf);
}
}
break;
case READ_ADDR(MINION_SYS_CHIP_SIG):
case READ_ADDR(MINION_SYS_CHIP_STA):
default:
applog(IOCTRL_LOG, "%s %s chipid %d osiz %d%s%s",
name, dir, (int)obuf[0], (int)osiz,
ex, buf);
__bin2hex(buf, rbuf + osiz - rsiz, rsiz);
applog(IOCTRL_LOG, " %s reply %d(%d) was %s", name,
reply, rsiz, buf);
break;
}
}
}
}
#endif
bcm = minionPins[pin].bcm;
*paddr = value;
*paddr = value;
}
#define EXTRA_LOG_IO 0
#if DO_IO_STATS
struct timeval sta, fin, lsta, lfin, tsd;
#endif
#if MINION_SHOW_IO
// if the a5/5a outside the data change, it means data overrun or corruption
memset(dataw, 0xa5, sizeof(dataw));
memset(datar, 0x5a, sizeof(datar));
memcpy(&dataw[DATA_OFF], &obuf[0], osiz);
// cgsleep_ms(5); // TODO: a delay ... based on the last command? But subtract
elapsed
// i.e. do any commands need a delay after the I/O has completed
before the next I/O?
memset(&tran, 0, sizeof(tran));
if (osiz < MINION_SPI_BUFSIZ)
tran.len = osiz;
else
return MINION_OVERSIZE_TASK;
tran.delay_usecs = opt_minion_spiusec;
tran.speed_hz = MINION_SPI_SPEED;
#if MINION_SHOW_IO
tran.tx_buf = (uintptr_t)&(dataw[DATA_OFF]);
tran.rx_buf = (uintptr_t)&(datar[DATA_OFF]);
#else
tran.tx_buf = (uintptr_t)obuf;
tran.rx_buf = (uintptr_t)rbuf;
#endif
IO_STAT_NOW(&lsta);
mutex_lock(&(minioninfo->spi_lock));
if (usepins) {
// Pin low for I/O
MINION_PIN_BEFORE;
set_pin(minioninfo, pin, false);
MINION_PIN_SLEEP;
}
IO_STAT_NOW(&sta);
ret = ioctl(minioninfo->spifd, SPI_IOC_MESSAGE(1), (void *)&tran);
*ioseq = minioninfo->ioseq++;
IO_STAT_NOW(&fin);
if (usepins) {
MINION_PIN_AFTER;
// Pin back high after I/O
set_pin(minioninfo, pin, true);
}
now = time(NULL);
if (ret >= 0 && rbuf[0] == 0xff && rbuf[ret-1] == 0xff &&
(obuf[1] == READ_ADDR(MINION_RES_DATA) || obuf[1] ==
READ_ADDR(MINION_SYS_FIFO_STA))) {
int i;
fail = true;
for (i = 1; i < ret-2; i++) {
if (rbuf[i] != 0xff) {
fail = false;
break;
}
}
if (fail) {
powercycle = show = false;
minioninfo->xffs++;
minioninfo->last_xff = now;
if (minioninfo->xfree_list->count > 0)
xitem = k_unlink_head(minioninfo->xfree_list);
else
xitem = k_unlink_tail(minioninfo->xff_list);
DATA_XFF(xitem)->when = now;
if (!minioninfo->xff_list->head)
show = true;
else {
// if !changing and xff_list is full
if (!minioninfo->changing[obuf[0]] &&
minioninfo->xfree_list->count == 0) {
total = DATA_XFF(xitem)->when -
DATA_XFF(minioninfo->xff_list->tail)->when;
if (total <= MINION_POWER_TIME) {
powercycle = true;
// Discard the history
k_list_transfer_to_head(minioninfo->xff_list,
minioninfo->xfree_list);
k_add_head(minioninfo->xfree_list, xitem);
xitem = NULL;
}
}
if (!powercycle) {
lastshow = DATA_XFF(xitem)->when -
DATA_XFF(minioninfo->xff_list->head)->when;
show = (lastshow >= 5);
}
}
if (xitem)
k_add_head(minioninfo->xff_list, xitem);
#if MINION_ROCKCHIP == 1
if (powercycle)
minion_toggle_gpio(minioncgpu, MINION_POWERCYCLE_GPIO);
#endif
minion_init_spi(minioncgpu, minioninfo, 0, 0, true);
}
} else if (minioninfo->spi_reset_count) {
if (minioninfo->spi_reset_io) {
if (*ioseq > 0 && (*ioseq % minioninfo->spi_reset_count) == 0)
minion_init_spi(minioncgpu, minioninfo, 0, 0, true);
} else {
if (minioninfo->last_spi_reset == 0)
minioninfo->last_spi_reset = now;
else {
if ((now - minioninfo->last_spi_reset) >= minioninfo-
>spi_reset_count)
minion_init_spi(minioncgpu, minioninfo, 0, 0, true);
minioninfo->last_spi_reset = now;
}
}
}
if (opt_minion_spidelay)
cgsleep_ms(opt_minion_spidelay);
mutex_unlock(&(minioninfo->spi_lock));
IO_STAT_NOW(&lfin);
IO_STAT_NOW(&tsd);
if (fail) {
if (powercycle) {
applog(LOG_ERR, "%s%d: power cycle ioctl %"PRIu64" (%"PRIu64")",
minioncgpu->drv->name, minioncgpu->device_id, *ioseq,
minioninfo->xffs - minioninfo->last_displayed_xff);
minioninfo->last_displayed_xff = minioninfo->xffs;
} else if (show) {
char *what = "unk";
switch (obuf[1]) {
case READ_ADDR(MINION_RES_DATA):
what = "nonce";
break;
case READ_ADDR(MINION_SYS_FIFO_STA):
what = "fifo";
break;
}
applog(LOG_ERR, "%s%d: reset ioctl %"PRIu64" %s all 0xff
(%"PRIu64")",
minioncgpu->drv->name, minioncgpu->device_id,
*ioseq, what, minioninfo->xffs - minioninfo-
>last_displayed_xff);
minioninfo->last_displayed_xff = minioninfo->xffs;
}
}
#if MINION_SHOW_IO
if (ret > 0) {
buf = bin2hex((unsigned char *)&(datar[DATA_OFF]), ret);
applog(IOCTRL_LOG, "*** %s() reply %d = pin %d cid %d %02x %02x %s %02x
%02x",
__func__, ret, pin, (int)(dataw[DATA_OFF]),
datar[0], datar[DATA_OFF-1], buf,
datar[DATA_OFF+osiz], datar[DATA_ALL-1]);
free(buf);
} else
applog(LOG_ERR, "*** %s() reply = %d", __func__, ret);
#if 1
#define do_ioctl(_pin, _obuf, _osiz, _rbuf, _rsiz, _ioseq) \
__do_ioctl(minioncgpu, minioninfo, _pin, _obuf, _osiz, _rbuf, \
_rsiz, _ioseq, MINION_FFL_HERE)
#else
#define do_ioctl(_pin, _obuf, _osiz, _rbuf, _rsiz, _ioseq) \
_do_ioctl(minioninfo, _pin, _obuf, _osiz, _rbuf, \
_rsiz, _ioseq, MINION_FFL_HERE)
// This sends an expected to work, SPI command before each SPI command
static int _do_ioctl(struct minion_info *minioninfo, int pin, uint8_t *obuf,
uint32_t osiz, uint8_t *rbuf, uint32_t rsiz, uint64_t *ioseq, MINION_FFL_ARGS)
{
struct minion_header *head;
uint8_t buf1[MINION_BUFSIZ];
uint8_t buf2[MINION_BUFSIZ];
uint32_t siz;
if (task->wsiz)
memcpy(&(head->data[0]), task->wbuf, task->wsiz);
task->osiz = HSIZE() + task->wsiz + task->rsiz;
head->data[0] = data[0];
head->data[1] = data[1];
head->data[2] = data[2];
head->data[3] = data[3];
if (reply != (int)wsiz) {
applog(LOG_ERR, "%s: chip %d %s returned %d (should be %d)",
minioncgpu->drv->dname, chip,
addr2txt(head->reg),
reply, (int)wsiz);
}
return reply;
}
freq /= MINION_FREQ_FACTOR;
if (freq < MINION_FREQ_FACTOR_MIN)
freq = MINION_FREQ_FACTOR_MIN;
if (freq > MINION_FREQ_FACTOR_MAX)
freq = MINION_FREQ_FACTOR_MAX;
value = minion_freq[freq];
data[0] = (uint8_t)(value & 0xff);
data[1] = (uint8_t)(((value & 0xff00) >> 8) & 0xff);
data[2] = (uint8_t)(((value & 0xff0000) >> 16) & 0xff);
data[3] = (uint8_t)(((value & 0xff000000) >> 24) & 0xff);
minioninfo->freqsent[chip] = value;
cgtime(&(minioninfo->lastfreq[chip]));
applog(LOG_DEBUG, "%s%i: chip %d freq %d sec %d usec %d",
minioncgpu->drv->name, minioncgpu->device_id,
chip, freq,
(int)(minioninfo->lastfreq[chip].tv_sec) % 10,
(int)(minioninfo->lastfreq[chip].tv_usec));
minioninfo->chip_status[chip].first_nonce.tv_sec = 0L;
// Default reset
data[0] = SYS_RSTN_CTL_INIT;
data[1] = 0x00;
data[2] = 0x00;
data[3] = 0x00;
minioninfo->chip_status[chip].tempsent = choice;
// quicker replies
// data[0] = 0x05;
// data[1] = 0x05;
// data[2] = 0x05;
// data[3] = 0x05;
// 2 cores
// data[0] = 0xff;
// data[1] = 0xff;
// data[2] = 0xff;
// data[3] = 0x7f;
#if ENABLE_INT_NONO
static void enable_interrupt(struct cgpu_info *minioncgpu, struct minion_info
*minioninfo, int chip)
{
uint8_t rbuf[MINION_BUFSIZ];
uint8_t data[4];
__maybe_unused int reply;
data[0] = MINION_RESULT_INT_SIZE;
data[1] = 0x00;
data[2] = 0x00;
data[3] = 0x00;
// data[0] = MINION_RESULT_INT;
data[0] = MINION_RESULT_INT | MINION_CMD_INT;
data[1] = 0x00;
data[2] = 0x00;
data[3] = 0x00;
tries = 0;
ok = false;
do {
reply = do_ioctl(pin, wbuf, wsiz, rbuf, rsiz, &ioseq);
if (reply == (int)(wsiz)) {
uint32_t sig = u8tou32(rbuf, wsiz - rsiz);
if (sig == MINION_CHIP_SIG) {
newchip = (minioninfo->chips)++;
minioninfo->has_chip[newchip] = true;
minioninfo->chipid[newchip] = chipid;
minioninfo->chip_pin[newchip] = pin;
ok = true;
} else {
if (sig == MINION_CHIP_SIG_SHIFT1 ||
sig == MINION_CHIP_SIG_SHIFT2 ||
sig == MINION_CHIP_SIG_SHIFT3 ||
sig == MINION_CHIP_SIG_SHIFT4) {
applog(LOG_WARNING, "%s: pin %d chipid %d detect
offset got"
" 0x%08x wanted 0x%08x",
minioncgpu->drv->dname, pin, chipid,
sig, MINION_CHIP_SIG);
} else {
if (sig == MINION_NOCHIP_SIG ||
sig == MINION_NOCHIP_SIG2) // Assume no chip
ok = true;
else {
applog(LOG_ERR, "%s: pin %d chipid %d detect
failed"
" got 0x%08x wanted 0x%08x",
minioncgpu->drv->dname, pin,
chipid, sig, MINION_CHIP_SIG);
}
}
}
} else {
applog(LOG_ERR, "%s: pin %d chipid %d reply %d ignored should be
%d",
minioncgpu->drv->dname, pin, chipid, reply, (int)
(wsiz));
}
} while (!ok && ++tries <= MINION_SIG_TRIES);
if (!ok) {
applog(LOG_ERR, "%s: pin %d chipid %d - detect failure status",
minioncgpu->drv->dname, pin, chipid);
}
}
#if MINION_ROCKCHIP == 1
minion_toggle_gpio(minioncgpu, MINION_POWERCYCLE_GPIO);
cgsleep_ms(100);
#endif
if (usepins) {
init_pins(minioninfo);
pinend = (int)MINION_PIN_COUNT;
} else
pinend = 1;
if (minioninfo->chips) {
for (chip = 0; chip < (int)MINION_CHIPS; chip++) {
if (minioninfo->has_chip[chip]) {
want_freq = minioninfo->init_freq[chip];
start_freq = want_freq * opt_minion_freqpercent / 100;
start_freq -= (start_freq % MINION_FREQ_FACTOR);
if (start_freq < MINION_FREQ_MIN)
start_freq = MINION_FREQ_MIN;
minioninfo->want_freq[chip] = want_freq;
minioninfo->init_freq[chip] = start_freq;
if (start_freq != want_freq) {
freqms = opt_minion_freqchange;
freqms /= ((want_freq - start_freq) /
MINION_FREQ_FACTOR);
if (freqms < 0)
freqms = -freqms;
minioninfo->freqms[chip] = freqms;
minioninfo->changing[chip] = true;
}
init_chip(minioncgpu, minioninfo, chip);
enable_chip_cores(minioncgpu, minioninfo, chip);
}
}
#if ENABLE_INT_NONO
// After everything is ready
for (chip = 0; chip < MINION_CHIPS; chip++)
if (minioninfo->has_chip[chip])
enable_interrupt(minioncgpu, minioninfo, chip);
#endif
}
}
static struct {
int request;
int value;
} minion_ioc[] = {
{ SPI_IOC_RD_MODE, 0 },
{ SPI_IOC_WR_MODE, 0 },
{ SPI_IOC_RD_BITS_PER_WORD, 8 },
{ SPI_IOC_WR_BITS_PER_WORD, 8 },
{ SPI_IOC_RD_MAX_SPEED_HZ, MINION_SPI_SPEED },
{ SPI_IOC_WR_MAX_SPEED_HZ, MINION_SPI_SPEED },
{ -1, -1 }
};
minioncgpu->device_path = strdup(buf);
}
return true;
close_out:
close(minioninfo->spifd);
minioninfo->spifd = 0;
free(minioncgpu->device_path);
minioncgpu->device_path = NULL;
bad_out:
return false;
}
static bool minion_setup_chip_select(struct cgpu_info *minioncgpu, struct
minion_info *minioninfo)
{
volatile uint32_t *paddr;
uint32_t mask, value, mem;
int count, memfd, pin, bcm;
close(memfd);
// Read settings
mem = *paddr;
*paddr;
count++;
}
if (count == 0)
return false;
else
return true;
}
#if ENABLE_INT_NONO
static bool minion_init_gpio_interrupt(struct cgpu_info *minioncgpu, struct
minion_info *minioninfo)
{
char pindir[64], ena[64], pin[8], dir[64], edge[64], act[64];
struct stat st;
int file, err;
ssize_t ret;
// Edge
snprintf(edge, sizeof(edge), MINION_GPIO_SYS MINION_GPIO_PIN
MINION_GPIO_EDGE,
MINION_GPIO_RESULT_INT_PIN);
file = open(edge, O_WRONLY | O_SYNC);
if (file == -1) {
applog(LOG_ERR, "%s: failed7 to enable GPIO pin %d interrupt (%d)",
minioncgpu->drv->dname,
MINION_GPIO_RESULT_INT_PIN,
errno);
return false;
}
ret = write(file, MINION_GPIO_EDGE_RISING,
(size_t)strlen(MINION_GPIO_EDGE_RISING));
if (ret != (ssize_t)strlen(MINION_GPIO_EDGE_RISING)) {
if (ret < 0)
err = errno;
else
err = (int)ret;
close(file);
applog(LOG_ERR, "%s: failed8 to enable GPIO pin %d interrupt (%d:%d)",
minioncgpu->drv->dname,
MINION_GPIO_RESULT_INT_PIN,
err, (int)strlen(MINION_GPIO_EDGE_RISING));
return false;
}
close(file);
// Active
snprintf(act, sizeof(act), MINION_GPIO_SYS MINION_GPIO_PIN MINION_GPIO_ACT,
MINION_GPIO_RESULT_INT_PIN);
file = open(act, O_WRONLY | O_SYNC);
if (file == -1) {
applog(LOG_ERR, "%s: failed9 to enable GPIO pin %d interrupt (%d)",
minioncgpu->drv->dname,
MINION_GPIO_RESULT_INT_PIN,
errno);
return false;
}
ret = write(file, MINION_GPIO_ACT_HI, (size_t)strlen(MINION_GPIO_ACT_HI));
if (ret != (ssize_t)strlen(MINION_GPIO_ACT_HI)) {
if (ret < 0)
err = errno;
else
err = (int)ret;
close(file);
applog(LOG_ERR, "%s: failed10 to enable GPIO pin %d interrupt (%d:%d)",
minioncgpu->drv->dname,
MINION_GPIO_RESULT_INT_PIN,
err, (int)strlen(MINION_GPIO_ACT_HI));
return false;
}
close(file);
return true;
}
#endif
switch (tolower(*opt_minion_spireset)) {
case 'i':
is_io = true;
break;
case 's':
is_io = false;
break;
default:
applog(LOG_WARNING, "ERR: Invalid SPI reset '%s'",
opt_minion_spireset);
goto skip;
}
val = atoi(opt_minion_spireset+1);
if (val < 0 || val > 9999) {
applog(LOG_WARNING, "ERR: Invalid SPI reset '%s'",
opt_minion_spireset);
} else {
minioninfo->spi_reset_io = is_io;
minioninfo->spi_reset_count = val;
minioninfo->last_spi_reset = time(NULL);
}
}
skip:
last_freq = MINION_FREQ_DEF;
if (opt_minion_freq && *opt_minion_freq) {
buf = freq = strdup(opt_minion_freq);
comma = strchr(freq, ',');
if (comma)
*(comma++) = '\0';
freq = comma;
if (comma) {
comma = strchr(freq, ',');
if (comma)
*(comma++) = '\0';
}
}
minioninfo->init_freq[i] = last_freq;
}
free(buf);
}
last_temp = MINION_TEMP_CTL_DEF;
if (opt_minion_temp && *opt_minion_temp) {
buf = temp = strdup(opt_minion_temp);
comma = strchr(temp, ',');
if (comma)
*(comma++) = '\0';
temp = comma;
if (comma) {
comma = strchr(temp, ',');
if (comma)
*(comma++) = '\0';
}
}
minioninfo->init_temp[i] = last_temp;
}
free(buf);
}
default_all_cores(&(last_cores[0]));
// default to all cores until we find valid data
cleared = false;
if (opt_minion_cores && *opt_minion_cores) {
buf = core = strdup(opt_minion_cores);
comma = strchr(core, ',');
if (comma)
*(comma++) = '\0';
sizeof(last_cores));
cleared = true;
}
ENABLE_CORE(last_cores,
core1);
core1++;
}
}
}
}
} else {
if (strcasecmp(core, MINION_CORE_ALL) == 0)
default_all_cores(&(last_cores[0]));
}
core = plus;
if (plus) {
plus = strchr(core, '+');
if (plus)
*(plus++) = '\0';
}
}
core = comma;
if (comma) {
comma = strchr(core, ',');
if (comma)
*(comma++) = '\0';
}
}
memcpy(&(minioninfo->init_cores[i][0]), &(last_cores[0]),
sizeof(last_cores));
}
free(buf);
}
}
define_test();
minioncgpu->drv = &minion_drv;
minioncgpu->deven = DEV_ENABLED;
minioncgpu->threads = 1;
#if ENABLE_INT_NONO
if (!minion_init_gpio_interrupt(minioncgpu, minioninfo))
goto unalloc;
#endif
if (usepins) {
if (!minion_setup_chip_select(minioncgpu, minioninfo))
goto unalloc;
}
mutex_init(&(minioninfo->spi_lock));
mutex_init(&(minioninfo->sta_lock));
minion_process_options(minioninfo);
minion_detect_chips(minioncgpu, minioninfo);
buf[0] = '\0';
for (i = 0; i < (int)MINION_CHIPS; i++) {
if (minioninfo->has_chip[i]) {
off = strlen(buf);
snprintf(buf + off, sizeof(buf) - off, " %d:%d/%d",
i, minioninfo->chip_pin[i], (int)(minioninfo->chipid[i]));
}
}
if (minioninfo->chips == 0)
goto cleanup;
if (!add_cgpu(minioncgpu))
goto cleanup;
mutex_init(&(minioninfo->nonce_lock));
minioninfo->history_gen = MINION_MAX_RESET_CHECK;
minioninfo->hfree_list = k_new_list("History", sizeof(HIST_ITEM),
ALLOC_HIST_ITEMS, LIMIT_HIST_ITEMS, true);
for (i = 0; i < (int)MINION_CHIPS; i++)
minioninfo->hchip_list[i] = k_new_store(minioninfo->hfree_list);
cgsem_init(&(minioninfo->task_ready));
cgsem_init(&(minioninfo->nonce_ready));
cgsem_init(&(minioninfo->scan_work));
minioninfo->initialised = true;
dupalloc(minioncgpu, 10);
return;
cleanup:
close(minioninfo->gpiointfd);
close(minioninfo->spifd);
mutex_destroy(&(minioninfo->sta_lock));
mutex_destroy(&(minioninfo->spi_lock));
unalloc:
free(minioninfo);
free(minioncgpu);
}
if (strcasecmp(option, "help") == 0) {
sprintf(replybuf, "reset: chip 0-%d freq: 0-%d:%d-%d "
"ledcount: 0-100 ledlimit: 0-200 "
"spidelay: 0-9999 spireset i|s0-9999 "
"spisleep: 0-9999",
minioninfo->chips - 1,
minioninfo->chips - 1,
MINION_FREQ_MIN, MINION_FREQ_MAX);
return replybuf;
}
if (strcasecmp(option, "reset") == 0) {
if (!setting || !*setting) {
sprintf(replybuf, "missing chip to reset");
return replybuf;
}
chip = atoi(setting);
if (chip < 0 || chip >= minioninfo->chips) {
sprintf(replybuf, "invalid reset: chip '%s' valid range 0-%d",
setting,
minioninfo->chips);
return replybuf;
}
if (!minioninfo->has_chip[chip]) {
sprintf(replybuf, "unable to reset chip %d - chip disabled",
chip);
return replybuf;
}
minioninfo->flag_reset[chip] = true;
return NULL;
}
// This sets up a freq step up/down to the given freq without a reset
if (strcasecmp(option, "freq") == 0) {
if (!setting || !*setting) {
sprintf(replybuf, "missing chip:freq");
return replybuf;
}
*(colon++) = '\0';
if (!*colon) {
sprintf(replybuf, "missing freq in chip:freq");
return replybuf;
}
chip = atoi(setting);
if (chip < 0 || chip >= minioninfo->chips) {
sprintf(replybuf, "invalid freq: chip '%s' valid range 0-%d",
setting,
minioninfo->chips);
return replybuf;
}
if (!minioninfo->has_chip[chip]) {
sprintf(replybuf, "unable to modify chip %d - chip not enabled",
chip);
return replybuf;
}
val = atoi(colon);
if (val < MINION_FREQ_MIN || val > MINION_FREQ_MAX) {
sprintf(replybuf, "invalid freq: '%s' valid range %d-%d",
setting,
MINION_FREQ_MIN, MINION_FREQ_MAX);
return replybuf;
}
if (want_freq != start_freq) {
minioninfo->changing[chip] = false;
freqms = opt_minion_freqchange;
freqms /= ((want_freq - start_freq) / MINION_FREQ_FACTOR);
if (freqms < 0)
freqms = -freqms;
minioninfo->freqms[chip] = freqms;
minioninfo->want_freq[chip] = want_freq;
cgtime(&(minioninfo->lastfreq[chip]));
minioninfo->changing[chip] = true;
}
return NULL;
}
if (strcasecmp(option, "ledcount") == 0) {
if (!setting || !*setting) {
sprintf(replybuf, "missing ledcount value");
return replybuf;
}
val = atoi(setting);
if (val < 0 || val > 100) {
sprintf(replybuf, "invalid ledcount: '%s' valid range 0-100",
setting);
return replybuf;
}
opt_minion_ledcount = val;
return NULL;
}
if (strcasecmp(option, "ledlimit") == 0) {
if (!setting || !*setting) {
sprintf(replybuf, "missing ledlimit value");
return replybuf;
}
val = atoi(setting);
if (val < 0 || val > 200) {
sprintf(replybuf, "invalid ledlimit: GHs '%s' valid range 0-200",
setting);
return replybuf;
}
opt_minion_ledlimit = val;
return NULL;
}
if (strcasecmp(option, "spidelay") == 0) {
if (!setting || !*setting) {
sprintf(replybuf, "missing spidelay value");
return replybuf;
}
val = atoi(setting);
if (val < 0 || val > 9999) {
sprintf(replybuf, "invalid spidelay: ms '%s' valid range 0-9999",
setting);
return replybuf;
}
opt_minion_spidelay = val;
return NULL;
}
if (strcasecmp(option, "spireset") == 0) {
bool is_io = true;
if (!setting || !*setting) {
sprintf(replybuf, "missing spireset value");
return replybuf;
}
switch (tolower(*setting)) {
case 'i':
is_io = true;
break;
case 's':
is_io = false;
break;
default:
sprintf(replybuf, "invalid spireset: '%s' must start with i
or s",
setting);
return replybuf;
}
val = atoi(setting+1);
if (val < 0 || val > 9999) {
sprintf(replybuf, "invalid spireset: %c '%s' valid range 0-9999",
*setting, setting+1);
return replybuf;
}
minioninfo->spi_reset_io = is_io;
minioninfo->spi_reset_count = val;
minioninfo->last_spi_reset = time(NULL);
return NULL;
}
if (strcasecmp(option, "spisleep") == 0) {
if (!setting || !*setting) {
sprintf(replybuf, "missing spisleep value");
return replybuf;
}
val = atoi(setting);
if (val < 0 || val > 9999) {
sprintf(replybuf, "invalid spisleep: ms '%s' valid range 0-9999",
setting);
return replybuf;
}
opt_minion_spisleep = val;
return NULL;
}
if (strcasecmp(option, "spiusec") == 0) {
if (!setting || !*setting) {
sprintf(replybuf, "missing spiusec value");
return replybuf;
}
val = atoi(setting);
if (val < 0 || val > 9999) {
sprintf(replybuf, "invalid spiusec: '%s' valid range 0-9999",
setting);
return replybuf;
}
opt_minion_spiusec = val;
return NULL;
}
k_unlink_item(minioninfo->task_list, item);
}
K_WUNLOCK(minioninfo->task_list);
if (item) {
bool do_txrx = true;
bool store_reply = true;
struct timeval now;
double howlong;
int i;
titem = DATA_TASK(item);
switch (titem->address) {
// TODO: case MINION_SYS_TEMP_CTL:
// TODO: case MINION_SYS_FREQ_CTL:
case READ_ADDR(MINION_SYS_CHIP_STA):
case WRITE_ADDR(MINION_SYS_SPI_LED):
case WRITE_ADDR(MINION_SYS_RSTN_CTL):
case WRITE_ADDR(MINION_SYS_INT_CLR):
case READ_ADDR(MINION_SYS_IDLE_CNT):
case READ_ADDR(MINION_CORE_ENA0_31):
case READ_ADDR(MINION_CORE_ENA32_63):
case READ_ADDR(MINION_CORE_ENA64_95):
case READ_ADDR(MINION_CORE_ENA96_98):
case READ_ADDR(MINION_CORE_ACT0_31):
case READ_ADDR(MINION_CORE_ACT32_63):
case READ_ADDR(MINION_CORE_ACT64_95):
case READ_ADDR(MINION_CORE_ACT96_98):
store_reply = false;
break;
case WRITE_ADDR(MINION_QUE_0):
//applog(LOG_ERR, "%s%i: ZZZ send task_id 0x%04x - chip %d", minioncgpu->drv->name,
minioncgpu->device_id, titem->task_id, titem->chip);
store_reply = false;
break;
default:
do_txrx = false;
titem->reply = MINION_UNEXPECTED_TASK;
applog(LOG_ERR, "%s%i: Unexpected task address 0x%02x
(%s)",
minioncgpu->drv->name, minioncgpu-
>device_id,
(unsigned int)(titem->address),
addr2txt(titem->address));
break;
}
if (do_txrx) {
if (titem->witem) {
cgtime(&now);
howlong = tdiff(&now, &(DATA_WORK(titem->witem)-
>created));
minioninfo->wt_work++;
minioninfo->wt_time += howlong;
if (minioninfo->wt_min == 0 || minioninfo->wt_min >
howlong)
minioninfo->wt_min = howlong;
else if (minioninfo->wt_max < howlong)
minioninfo->wt_max = howlong;
for (i = 0; i < TIME_BANDS; i++) {
if (howlong < time_bands[i]) {
minioninfo->wt_bands[i]++;
break;
}
}
if (i >= TIME_BANDS)
minioninfo->wt_bands[TIME_BANDS]++;
}
minion_txrx(titem);
if (minioninfo-
>chip_status[chip].overheat) {
switch (STA_TEMP(rep)) {
case MINION_TEMP_40:
case MINION_TEMP_60:
case MINION_TEMP_80:
cgtime(&(minioninfo-
>chip_status[chip].lastrecover));
minioninfo-
>chip_status[chip].overheat = false;
applog(LOG_WARNING, "%s
%d: chip %d cooled, restarting",
minioncgpu->drv->name,
minioncgpu->device_id,
chip);
cgtime(&(minioninfo-
>chip_status[chip].lastrecover));
minioninfo-
>chip_status[chip].overheattime +=
tdiff(&(minioninfo->chip_status[chip].lastrecover),
&(minioninfo->chip_status[chip].lastoverheat));
break;
default:
break;
}
} else {
if (opt_minion_overheat &&
STA_TEMP(rep) == MINION_TEMP_OVER) {
cgtime(&(minioninfo-
>chip_status[chip].lastoverheat));
minioninfo-
>chip_status[chip].overheat = true;
applog(LOG_WARNING, "%s%d:
chip %d overheated! idling",
minioncgpu-
>drv->name,
minioncgpu-
>device_id,
chip);
K_WLOCK(minioninfo-
>tfree_list);
task =
k_unlink_head(minioninfo->tfree_list);
DATA_TASK(task)->tid = ++
(minioninfo->next_tid);
DATA_TASK(task)->chip = chip;
DATA_TASK(task)->write =
true;
DATA_TASK(task)->address =
MINION_SYS_RSTN_CTL;
DATA_TASK(task)->task_id = 0;
// ignored
DATA_TASK(task)->wsiz =
MINION_SYS_SIZ;
DATA_TASK(task)->rsiz = 0;
DATA_TASK(task)->wbuf[0] =
SYS_RSTN_CTL_FLUSH;
DATA_TASK(task)->wbuf[1] = 0;
DATA_TASK(task)->wbuf[2] = 0;
DATA_TASK(task)->wbuf[3] = 0;
DATA_TASK(task)->urgent =
true;
k_add_head(minioninfo-
>task_list, task);
K_WUNLOCK(minioninfo-
>tfree_list);
minioninfo-
>chip_status[chip].overheats++;
}
}
}
break;
case READ_ADDR(MINION_SYS_IDLE_CNT):
{
uint32_t *cnt = (uint32_t *)&(titem-
>rbuf[titem->osiz - titem->rsiz]);
minioninfo->chip_status[chip].idle =
*cnt;
}
break;
case WRITE_ADDR(MINION_SYS_RSTN_CTL):
// Do this here after it has actually been
flushed
if ((titem->wbuf[0] & SYS_RSTN_CTL_FLUSH) ==
SYS_RSTN_CTL_FLUSH) {
int cnt = 0;
K_WLOCK(minioninfo->wwork_list);
work = minioninfo->wchip_list[chip]-
>head;
while (work) {
cnt++;
DATA_WORK(work)->stale = true;
work = work->next;
}
minioninfo->chip_status[chip].chipwork =
0;
minioninfo->chip_status[chip].realwork =
0;
minioninfo->wchip_staled += cnt;
#if MINION_SHOW_IO
applog(IOCTRL_LOG, "RSTN chip %d (cnt=%d)
cw0=%u rw0=%u qw=%u",
chip, cnt,
minioninfo-
>chip_status[chip].chipwork,
minioninfo-
>chip_status[chip].realwork,
minioninfo-
>chip_status[chip].quework);
#endif
K_WUNLOCK(minioninfo->wwork_list);
}
break;
case WRITE_ADDR(MINION_QUE_0):
K_WLOCK(minioninfo->wchip_list[chip]);
k_unlink_item(minioninfo->wque_list[chip],
titem->witem);
k_add_head(minioninfo->wchip_list[chip], titem-
>witem);
DATA_WORK(titem->witem)->ioseq = titem->ioseq;
minioninfo->chip_status[chip].quework--;
minioninfo->chip_status[chip].chipwork++;
#if MINION_SHOW_IO
applog(IOCTRL_LOG, "QUE_0 chip %d cw+1=%u rw=%u
qw-1=%u",
chip,
minioninfo-
>chip_status[chip].chipwork,
minioninfo-
>chip_status[chip].realwork,
minioninfo-
>chip_status[chip].quework);
#endif
K_WUNLOCK(minioninfo->wchip_list[chip]);
applog(LOG_DEBUG, "%s%d: task 0x%04x sent to
chip %d",
minioncgpu->drv->name,
minioncgpu->device_id,
titem->task_id, chip);
break;
case READ_ADDR(MINION_CORE_ENA0_31):
case READ_ADDR(MINION_CORE_ENA32_63):
case READ_ADDR(MINION_CORE_ENA64_95):
case READ_ADDR(MINION_CORE_ENA96_98):
{
uint32_t *rep = (uint32_t *)&(titem-
>rbuf[titem->osiz - titem->rsiz]);
int off = titem->address -
READ_ADDR(MINION_CORE_ENA0_31);
minioninfo->chip_core_ena[off][chip] =
*rep;
}
break;
case READ_ADDR(MINION_CORE_ACT0_31):
case READ_ADDR(MINION_CORE_ACT32_63):
case READ_ADDR(MINION_CORE_ACT64_95):
case READ_ADDR(MINION_CORE_ACT96_98):
{
uint32_t *rep = (uint32_t *)&(titem-
>rbuf[titem->osiz - titem->rsiz]);
int off = titem->address -
READ_ADDR(MINION_CORE_ACT0_31);
minioninfo->chip_core_act[off][chip] =
*rep;
}
break;
case WRITE_ADDR(MINION_SYS_INT_CLR):
case WRITE_ADDR(MINION_SYS_SPI_LED):
break;
default:
break;
}
}
K_WLOCK(minioninfo->treply_list);
if (store_reply)
k_add_head(minioninfo->treply_list, item);
else
k_free_head(minioninfo->tfree_list, item);
K_WUNLOCK(minioninfo->treply_list);
/*
* Always check for the next task immediately if we just did one
* i.e. empty the task queue
*/
continue;
}
cgsem_mswait(&(minioninfo->task_ready), MINION_TASK_mS);
}
return NULL;
}
/*
* SPI/ioctl reply thread
* ioctl done every interrupt or MINION_REPLY_mS checking for results
*/
static void *minion_spi_reply(void *userdata)
{
struct cgpu_info *minioncgpu = (struct cgpu_info *)userdata;
struct minion_info *minioninfo = (struct minion_info *)(minioncgpu-
>device_data);
struct minion_result *result1, *result2, *use1, *use2;
K_ITEM *item;
TASK_ITEM fifo_task, res1_task, res2_task;
int chip, resoff;
bool somelow;
struct timeval now;
#if ENABLE_INT_NONO
uint64_t ioseq;
TASK_ITEM clr_task;
struct pollfd pfd;
struct minion_header *head;
uint8_t rbuf[MINION_BUFSIZ];
uint8_t wbuf[MINION_BUFSIZ];
uint32_t wsiz, rsiz;
int ret, reply;
bool gotreplies = false;
#endif
applog(MINION_LOG, "%s%i: SPI replying...",
minioncgpu->drv->name, minioncgpu->device_id);
fifo_task.chip = 0;
fifo_task.write = false;
fifo_task.address = MINION_SYS_FIFO_STA;
fifo_task.wsiz = 0;
fifo_task.rsiz = MINION_SYS_SIZ;
res1_task.chip = 0;
res1_task.write = false;
if (minreread)
res1_task.address = MINION_RES_PEEK;
else
res1_task.address = MINION_RES_DATA;
res1_task.wsiz = 0;
res1_task.rsiz = MINION_RES_DATA_SIZ;
res2_task.chip = 0;
res2_task.write = false;
res2_task.address = MINION_RES_DATA;
res2_task.wsiz = 0;
res2_task.rsiz = MINION_RES_DATA_SIZ;
#if ENABLE_INT_NONO
// Clear RESULT_INT after reading all results
clr_task.chip = 0;
clr_task.write = true;
clr_task.address = MINION_SYS_INT_CLR;
clr_task.wsiz = MINION_SYS_SIZ;
clr_task.rsiz = 0;
clr_task.wbuf[0] = MINION_RESULT_INT;
clr_task.wbuf[1] = 0;
clr_task.wbuf[2] = 0;
clr_task.wbuf[3] = 0;
memset(&pfd, 0, sizeof(pfd));
pfd.fd = minioninfo->gpiointfd;
pfd.events = POLLPRI;
somelow = false;
while (minioncgpu->shutdown == false) {
for (chip = 0; chip < (int)MINION_CHIPS; chip++) {
if (minioninfo->has_chip[chip]) {
int tries = 0;
uint8_t res, cmd;
if (minioninfo->changing[chip] &&
ms_tdiff(&now, &minioninfo->lastfreq[chip]) >
minioninfo->freqms[chip]) {
int want_freq = minioninfo->want_freq[chip];
int init_freq = minioninfo->init_freq[chip];
if (init_freq == want_freq)
minioninfo->changing[chip] = false;
}
K_WLOCK(minioninfo->wwork_list);
// have to just assume it's always correct since we can't
verify it
minioninfo->chip_status[chip].realwork = (uint32_t)cmd;
#if MINION_SHOW_IO
applog(IOCTRL_LOG, "SetReal chip %d cw=%u rw==%u qw=%u",
chip,
minioninfo->chip_status[chip].chipwork,
minioninfo->chip_status[chip].realwork,
minioninfo->chip_status[chip].quework);
#endif
K_WUNLOCK(minioninfo->wwork_list);
/*
* Chip has results?
* You can't request results unless it says it has some.
* We don't ever directly flush the output queue while
processing
* (except at startup) so the answer is always valid
* i.e. there could be more, but never less ... unless the
reply was corrupt
*/
if (res > MINION_MAX_RES) {
applog(LOG_ERR, "%s%i: Large work reply chip %d res
%d",
minioncgpu->drv->name, minioncgpu-
>device_id, chip, res);
minioninfo->spi_errors++;
minioninfo->fifo_spi_errors[chip]++;
minioninfo->res_err_count[chip]++;
res = 1; // Just read one result
}
//else
//applog(LOG_ERR, "%s%i: work reply res %d", minioncgpu->drv->name, minioncgpu-
>device_id, res);
uint8_t left = res;
int peeks = 0;
while (left > 0) {
res = left;
if (res > MINION_MAX_RES)
res = MINION_MAX_RES;
left -= res;
repeek:
res1_task.chip = chip;
res1_task.reply = 0;
res1_task.rsiz = res * MINION_RES_DATA_SIZ;
minion_txrx(&res1_task);
if (res1_task.reply <= 0)
break;
else {
cgtime(&now);
if (res1_task.reply < (int)MINION_RES_DATA_SIZ)
{
char *buf = bin2hex((unsigned char *)
(&(res1_task.rbuf[res1_task.osiz - res1_task.rsiz])), (int)(res1_task.rsiz));
applog(LOG_ERR, "%s%i: Chip %d Bad work
reply (%s) size %d, should be at least %d",
minioncgpu->drv->name,
minioncgpu->device_id,
chip, buf,
res1_task.reply,
(int)MINION_RES_DATA_SIZ);
free(buf);
minioninfo->spi_errors++;
minioninfo->res_spi_errors[chip]++;
minioninfo->res_err_count[chip]++;
} else {
if (res1_task.reply != (int)
(res1_task.osiz)) {
applog(LOG_ERR, "%s%i: Chip %d
Unexpected work reply size %d, expected %d",
minioncgpu->drv->name,
minioncgpu->device_id,
chip, res1_task.reply,
(int)(res1_task.osiz));
minioninfo->spi_errors++;
minioninfo->res_spi_errors[chip]++;
minioninfo->res_err_count[chip]++;
// Can retry a PEEK without losing
data
if (minreread) {
if (++peeks < 4)
goto repeek;
break;
}
}
if (minreread) {
res2_task.chip = chip;
res2_task.reply = 0;
res2_task.rsiz = res *
MINION_RES_DATA_SIZ;
minion_txrx(&res2_task);
if (res2_task.reply <= 0) {
minioninfo->spi_errors++;
minioninfo-
>res_spi_errors[chip]++;
minioninfo-
>res_err_count[chip]++;
}
}
if (IS_RESULT(result1) ||
(minreread && result2 && IS_RESULT(result2))) {
K_WLOCK(minioninfo-
>rfree_list);
item =
k_unlink_head(minioninfo->rfree_list);
K_WUNLOCK(minioninfo-
>rfree_list);
if (IS_RESULT(result1)) {
use1 = result1;
if (minreread && result2
&& IS_RESULT(result2))
use2 = result2;
else
use2 = NULL;
} else {
use1 = result2;
use2 = NULL;
minioninfo-
>use_res2[chip]++;
}
//DATA_RES(item)->chip =
RES_CHIPID(use1);
// We can avoid any SPI
transmission error of the chip number
DATA_RES(item)->chip =
(uint8_t)chip;
if (minioninfo-
>chipid[chip] != RES_CHIPID(use1)) {
minioninfo->spi_errors+
+;
minioninfo-
>res_spi_errors[chip]++;
minioninfo-
>res_err_count[chip]++;
}
if (use2 && minioninfo-
>chipid[chip] != RES_CHIPID(use2)) {
minioninfo->spi_errors+
+;
minioninfo-
>res_spi_errors[chip]++;
minioninfo-
>res_err_count[chip]++;
}
DATA_RES(item)->core =
RES_CORE(use1);
DATA_RES(item)->task_id =
RES_TASK(use1);
DATA_RES(item)->nonce =
RES_NONCE(use1);
DATA_RES(item)->no_nonce = !
RES_GOLD(use1);
memcpy(&(DATA_RES(item)-
>when), &now, sizeof(now));
applog(LOG_DEBUG, "%s%i:
reply task_id 0x%04x"
" - chip %d -
gold %d",
minioncgpu->drv-
>name,
minioncgpu-
>device_id,
RES_TASK(use1),
(int)RES_CHIPID(use1),
(int)RES_GOLD(use1));
if (!use2)
DATA_RES(item)->another
= false;
else {
DATA_RES(item)->another
= true;
DATA_RES(item)->task_id2
= RES_TASK(use2);
DATA_RES(item)->nonce2 =
RES_NONCE(use2);
}
//if (RES_GOLD(use1))
//applog(MINTASK_LOG, "%s%i: found a result chip %d core %d task 0x%04x nonce 0x
%08x gold=%d", minioncgpu->drv->name, minioncgpu->device_id, DATA_RES(item)->chip,
DATA_RES(item)->core, DATA_RES(item)->task_id, DATA_RES(item)->nonce,
(int)RES_GOLD(use1));
K_WLOCK(minioninfo-
>rnonce_list);
k_add_head(minioninfo-
>rnonce_list, item);
K_WUNLOCK(minioninfo-
>rnonce_list);
if (!(minioninfo-
>chip_status[chip].first_nonce.tv_sec)) {
cgtime(&(minioninfo-
>chip_status[chip].first_nonce));
minioninfo-
>chip_status[chip].from_first_good = 0;
}
cgsem_post(&(minioninfo-
>nonce_ready));
} else {
minioninfo-
>res_err_count[chip]++;
applog(MINTASK_LOG, "%s%i:
Invalid res0 task_id 0x%04x - chip %d",
minioncgpu-
>drv->name, minioncgpu->device_id,
RES_TASK(result1), chip);
if (minreread && result2) {
applog(MINTASK_LOG, "%s
%i: Invalid res1 task_id 0x%04x - chip %d",
minioncgpu->drv->name, minioncgpu->device_id,
RES_TASK(result2), chip);
}
}
}
}
}
}
}
}
if (somelow)
cgsem_post(&(minioninfo->scan_work));
#if ENABLE_INT_NONO
if (gotreplies)
minion_txrx(&clr_task);
#endif
#if !ENABLE_INT_NONO
cgsleep_ms(MINION_REPLY_mS);
#else
// TODO: this is going to require a bit of tuning with 2TH/s mining:
// The interrupt size MINION_RESULT_INT_SIZE should be high enough to
expect
// most chips to have some results but low enough to cause negligible
latency
// If all chips don't have some results when an interrupt occurs, then
it is a waste
// since we have to check all chips for results anyway since we don't
know which one
// caused the interrupt
// MINION_REPLY_mS needs to be low enough in the case of bad luck where
no chip
// finds MINION_RESULT_INT_SIZE results in a short amount of time, so
we go check
// them all anyway - to avoid high latency when there are only a few
results due to low luck
ret = poll(&pfd, 1, MINION_REPLY_mS);
if (ret > 0) {
bool gotres;
int c;
minioninfo->interrupts++;
gotres = false;
for (chip = 0; chip < (int)MINION_CHIPS; chip++) {
if (minioninfo->has_chip[chip]) {
SET_HEAD_READ(head, MINION_SYS_INT_STA);
head->chipid = minioninfo->chipid[chip];
reply = do_ioctl(CHIP_PIN(chip), wbuf, wsiz, rbuf,
rsiz, &ioseq);
if (reply != (int)wsiz) {
applog(LOG_ERR, "%s: chip %d int status
returned %d"
" (should be %d)",
minioncgpu->drv->dname,
chip, reply, (int)wsiz);
}
snprintf(minioninfo->last_interrupt,
sizeof(minioninfo->last_interrupt),
"%d %d 0x%02x%02x%02x%02x%02x%02x%02x%02x %d
%d 0x%02x %d %d",
(int)(minioninfo->interrupts), chip,
rbuf[0], rbuf[1], rbuf[2], rbuf[3],
rbuf[4], rbuf[5], rbuf[6], rbuf[7],
(int)wsiz, (int)rsiz, rbuf[wsiz - rsiz],
rbuf[wsiz - rsiz] & MINION_RESULT_INT,
rbuf[wsiz - rsiz] & MINION_CMD_INT);
// char *tmp;
// tmp = bin2hex(rbuf, wsiz);
// applog(LOG_ERR, "%s%i: chip %d interrupt: %s",
// minioncgpu->drv->name,
// minioncgpu->device_id,
// chip, tmp);
// free(tmp);
return NULL;
}
/*
* Find the matching work item for this chip
* Discard any older work items for this chip
*/
enum nonce_state {
NONCE_GOOD_NONCE,
NONCE_NO_NONCE,
NONCE_DUP_NONCE,
NONCE_BAD_NONCE,
NONCE_BAD_WORK,
NONCE_NO_WORK,
NONCE_SPI_ERR
};
look = minioninfo->wchip_list[chip]->tail;
while (look && DATA_WORK(look)->ioseq < DATA_WORK(item)->ioseq)
look = look->prev;
if (!look)
k_add_head(minioninfo->wchip_list[chip], item);
else
k_insert_after(minioninfo->wchip_list[chip], item, look);
}
// if the chip has been disabled - but we don't do that - so not possible
(yet)
if (!(minioninfo->has_chip[chip])) {
minioninfo->spi_errors++;
applog(MINTASK_LOG, "%s%i: nonce error chip %d not present",
minioncgpu->drv->name, minioncgpu->device_id, chip);
return NONCE_NO_WORK;
}
if (no_nonce)
minioninfo->chip_nononces[chip]++;
else
minioninfo->chip_nonces[chip]++;
redo = false;
retry:
K_WLOCK(minioninfo->wchip_list[chip]);
item = minioninfo->wchip_list[chip]->tail;
if (!item) {
K_WUNLOCK(minioninfo->wchip_list[chip]);
minioninfo->spi_errors++;
minioninfo->res_spi_errors[chip]++;
minioninfo->res_err_count[chip]++;
applog(MINTASK_LOG, "%s%i: chip %d has no tasks (core %d task 0x%04x)",
minioncgpu->drv->name, minioncgpu->device_id,
chip, core, (int)task_id);
if (!no_nonce) {
minioninfo->untested_nonces++;
minioninfo->chip_err[chip]++;
}
return NONCE_NO_WORK;
}
min_task_id = DATA_WORK(item)->task_id;
while (item) {
if (DATA_WORK(item)->task_id == task_id)
break;
item = item->prev;
}
max_task_id = DATA_WORK(minioninfo->wchip_list[chip]->head)->task_id;
if (!item) {
K_WUNLOCK(minioninfo->wchip_list[chip]);
if (another && task_id != task_id2) {
minioninfo->tasks_failed[chip]++;
task_id = task_id2;
redo = true;
goto retry;
}
minioninfo->spi_errors++;
minioninfo->res_spi_errors[chip]++;
minioninfo->res_err_count[chip]++;
applog(MINTASK_LOG, "%s%i: chip %d core %d unknown task 0x%04x "
"(min=0x%04x max=0x%04x no_nonce=%d)",
minioncgpu->drv->name, minioncgpu->device_id,
chip, core, (int)task_id, (int)min_task_id,
(int)max_task_id, no_nonce);
if (!no_nonce) {
minioninfo->untested_nonces++;
minioninfo->chip_err[chip]++;
}
return NONCE_BAD_WORK;
}
if (redo)
minioninfo->tasks_recovered[chip]++;
k_unlink_item(minioninfo->wchip_list[chip], item);
if (no_nonce) {
cleanup_older(minioncgpu, chip, item, no_nonce);
k_free_head(minioninfo->wfree_list, item);
K_WUNLOCK(minioninfo->wchip_list[chip]);
return NONCE_NO_NONCE;
}
K_WUNLOCK(minioninfo->wchip_list[chip]);
minioninfo->tested_nonces++;
redo = false;
retest:
if (test_nonce(DATA_WORK(item)->work, nonce)) {
/*
if (isdupnonce(minioncgpu, DATA_WORK(item)->work, nonce)) {
minioninfo->chip_dup[chip]++;
applog(LOG_WARNING, " ... nonce %02x%02x%02x%02x chip %d core %d
task 0x%04x",
(nonce & 0xff), ((nonce >> 8) & 0xff),
((nonce >> 16) & 0xff), ((nonce >> 24) & 0xff),
chip, core, task_id);
K_WLOCK(minioninfo->wchip_list[chip]);
restorework(minioninfo, chip, item);
K_WUNLOCK(minioninfo->wchip_list[chip]);
return NONCE_DUP_NONCE;
}
*/
//applog(MINTASK_LOG, "%s%i: Valid Nonce chip %d core %d task 0x%04x nonce 0x%08x",
minioncgpu->drv->name, minioncgpu->device_id, chip, core, task_id, nonce);
//
submit_tested_work(thr, DATA_WORK(item)->work);
if (redo)
minioninfo->nonces_recovered[chip]++;
/* chip_good = */ ++(minioninfo->chip_good[chip]);
minioninfo->chip_status[chip].from_first_good++;
minioninfo->core_good[chip][core]++;
DATA_WORK(item)->nonces++;
mutex_lock(&(minioninfo->nonce_lock));
minioninfo->new_nonces++;
mutex_unlock(&(minioninfo->nonce_lock));
minioninfo->ok_nonces++;
K_WLOCK(minioninfo->wchip_list[chip]);
cleanup_older(minioncgpu, chip, item, no_nonce);
restorework(minioninfo, chip, item);
K_WUNLOCK(minioninfo->wchip_list[chip]);
// add to history and remove old history and keep track of the 2 reset
marks
int chip_tmp;
cgtime(&now);
K_WLOCK(minioninfo->hfree_list);
item = k_unlink_head(minioninfo->hfree_list);
memcpy(&(DATA_HIST(item)->when), when, sizeof(*when));
k_add_head(minioninfo->hchip_list[chip], item);
if (minioninfo->reset_mark[chip])
minioninfo->reset_count[chip]++;
if (second_check && minioninfo->reset2_mark[chip])
minioninfo->reset2_count[chip]++;
/*
// Reset the chip after 8 nonces found
if (chip_good == 8) {
memcpy(&(minioninfo->last_reset[chip]), &now, sizeof(now));
init_chip(minioncgpu, minioninfo, chip);
}
*/
return NONCE_GOOD_NONCE;
}
DATA_WORK(item)->errors++;
K_WLOCK(minioninfo->wchip_list[chip]);
restorework(minioninfo, chip, item);
K_WUNLOCK(minioninfo->wchip_list[chip]);
minioninfo->chip_bad[chip]++;
minioninfo->core_bad[chip][core]++;
inc_hw_errors(thr);
//applog(MINTASK_LOG, "%s%i: HW ERROR chip %d core %d task 0x%04x nonce 0x%08x",
minioncgpu->drv->name, minioncgpu->device_id, chip, core, task_id, nonce);
return NONCE_BAD_NONCE;
}
cgtime(&now);
K_RLOCK(minioninfo->hfree_list);
for (chip = 0; chip < (int)MINION_CHIPS; chip++) {
if (minioninfo->has_chip[chip] && !(minioninfo->changing[chip])) {
head = minioninfo->hchip_list[chip]->head;
if (head) {
howlong = tdiff(&now, &(DATA_HIST(head)->when));
if (howlong > ((double)MINION_RESET_s * 1.5)) {
// Setup a reset
minioninfo->flag_reset[chip] = true;
minioninfo->do_reset[chip] = 0.0;
}
}
}
}
K_RUNLOCK(minioninfo->hfree_list);
}
thr = minioninfo->thr;
last_check = 0;
while (minioncgpu->shutdown == false) {
if (!oldest_nonce(minioncgpu, &chip, &core, &task_id, &nonce,
&no_nonce, &when, &another, &task_id2, &nonce2)) {
check_last_nonce(minioncgpu);
last_check = 0;
cgsem_mswait(&(minioninfo->nonce_ready), MINION_NONCE_mS);
continue;
}
return NULL;
}
if (minioninfo->initialised == false)
return;
// TODO: flush/work tasks should have a block sequence number so this task
removal code
// might be better implemented in minion_spi_write where each work task would
// update the block sequence number and any work tasks with an old block
sequence
// number would be discarded rather than sent - minion_spi_write will also
need to
// prioritise flush urgent tasks above work urgent tasks - have 3 urgent
states?
// They should however be 2 seperate variables in minioninfo to reduce
locking
// - flush will increment one and put it in the flush task, (and work will
use that)
// minion_spi_write will check/update the other and thus not need a lock
K_WUNLOCK(minioninfo->wwork_list);
// TODO: send a signal to force getting and sending new work - needs
cgsem_wait in the sending thread
cgtime(&now);
// No lock required since 'last' is only accessed here
if (minioninfo->chip_status[chip].last.tv_sec == 0) {
memcpy(&(minioninfo->chip_status[chip].last), &now, sizeof(now));
} else {
limit = MINION_STATS_UPDATE_TIME_mS +
(int)(random() % MINION_STATS_UPDATE_RAND_mS);
if (ms_tdiff(&now, &(minioninfo->chip_status[chip].last)) > limit) {
memcpy(&(minioninfo->chip_status[chip].last), &now, sizeof(now));
K_WLOCK(minioninfo->tfree_list);
item = k_unlink_head(minioninfo->tfree_list);
DATA_TASK(item)->tid = ++(minioninfo->next_tid);
K_WUNLOCK(minioninfo->tfree_list);
DATA_TASK(item)->chip = chip;
DATA_TASK(item)->write = false;
DATA_TASK(item)->address = READ_ADDR(MINION_SYS_CHIP_STA);
DATA_TASK(item)->task_id = 0;
DATA_TASK(item)->wsiz = 0;
DATA_TASK(item)->rsiz = MINION_SYS_SIZ;
DATA_TASK(item)->urgent = false;
K_WLOCK(minioninfo->task_list);
k_add_head(minioninfo->task_list, item);
item = k_unlink_head(minioninfo->tfree_list);
DATA_TASK(item)->tid = ++(minioninfo->next_tid);
K_WUNLOCK(minioninfo->task_list);
DATA_TASK(item)->chip = chip;
DATA_TASK(item)->write = false;
DATA_TASK(item)->address = READ_ADDR(MINION_SYS_IDLE_CNT);
DATA_TASK(item)->task_id = 0;
DATA_TASK(item)->wsiz = 0;
DATA_TASK(item)->rsiz = MINION_SYS_SIZ;
DATA_TASK(item)->urgent = false;
K_WLOCK(minioninfo->task_list);
k_add_head(minioninfo->task_list, item);
K_WUNLOCK(minioninfo->task_list);
DATA_TASK(item)->chip = chip;
DATA_TASK(item)->write = false;
DATA_TASK(item)->address = READ_ADDR(MINION_CORE_ENA0_31 +
rep);
DATA_TASK(item)->task_id = 0;
DATA_TASK(item)->wsiz = 0;
DATA_TASK(item)->rsiz = MINION_SYS_SIZ;
DATA_TASK(item)->urgent = false;
K_WLOCK(minioninfo->task_list);
k_add_head(minioninfo->task_list, item);
// Act
item = k_unlink_head(minioninfo->tfree_list);
DATA_TASK(item)->tid = ++(minioninfo->next_tid);
K_WUNLOCK(minioninfo->task_list);
DATA_TASK(item)->chip = chip;
DATA_TASK(item)->write = false;
DATA_TASK(item)->address = READ_ADDR(MINION_CORE_ACT0_31 +
rep);
DATA_TASK(item)->task_id = 0;
DATA_TASK(item)->wsiz = 0;
DATA_TASK(item)->rsiz = MINION_SYS_SIZ;
DATA_TASK(item)->urgent = false;
K_WLOCK(minioninfo->task_list);
k_add_head(minioninfo->task_list, item);
K_WUNLOCK(minioninfo->task_list);
}
if (minioninfo->lednow[chip] != minioninfo->setled[chip]) {
uint32_t led;
minioninfo->lednow[chip] = minioninfo->setled[chip];
if (minioninfo->lednow[chip])
led = MINION_SPI_LED_ON;
else
led = MINION_SPI_LED_OFF;
K_WLOCK(minioninfo->tfree_list);
item = k_unlink_head(minioninfo->tfree_list);
DATA_TASK(item)->tid = ++(minioninfo->next_tid);
K_WUNLOCK(minioninfo->tfree_list);
DATA_TASK(item)->chip = chip;
DATA_TASK(item)->write = true;
DATA_TASK(item)->address = MINION_SYS_SPI_LED;
DATA_TASK(item)->task_id = 0;
DATA_TASK(item)->wsiz = MINION_SYS_SIZ;
DATA_TASK(item)->rsiz = 0;
DATA_TASK(item)->wbuf[0] = led & 0xff;
DATA_TASK(item)->wbuf[1] = (led >> 8) & 0xff;
DATA_TASK(item)->wbuf[2] = (led >> 16) & 0xff;
DATA_TASK(item)->wbuf[3] = (led >> 24) & 0xff;
DATA_TASK(item)->urgent = false;
K_WLOCK(minioninfo->task_list);
k_add_head(minioninfo->task_list, item);
K_WUNLOCK(minioninfo->task_list);
}
}
}
}
K_WLOCK(minioninfo->tfree_list);
item = k_unlink_head(minioninfo->tfree_list);
DATA_TASK(item)->tid = ++(minioninfo->next_tid);
K_WUNLOCK(minioninfo->tfree_list);
DATA_TASK(item)->chip = chip;
DATA_TASK(item)->write = true;
DATA_TASK(item)->address = MINION_QUE_0;
DATA_TASK(item)->urgent = urgent;
DATA_TASK(item)->work_state = state;
DATA_TASK(item)->work = DATA_WORK(witem)->work;
DATA_TASK(item)->witem = witem;
memcpy(&(que->midstate[0]), &(DATA_WORK(witem)->work->midstate[0]),
MIDSTATE_BYTES);
memcpy(&(que->merkle7[0]), &(DATA_WORK(witem)->work->data[MERKLE7_OFFSET]),
MERKLE_BYTES);
DATA_TASK(item)->wsiz = (int)sizeof(*que);
DATA_TASK(item)->rsiz = 0;
K_WLOCK(minioninfo->wque_list[chip]);
k_add_head(minioninfo->wque_list[chip], witem);
minioninfo->chip_status[chip].quework++;
#if MINION_SHOW_IO
applog(IOCTRL_LOG, "Que chip %d cw=%u rw=%u qw+1=%u",
chip,
minioninfo->chip_status[chip].chipwork,
minioninfo->chip_status[chip].realwork,
minioninfo->chip_status[chip].quework);
#endif
K_WUNLOCK(minioninfo->wque_list[chip]);
K_WLOCK(minioninfo->task_list);
k_add_head(minioninfo->task_list, item);
K_WUNLOCK(minioninfo->task_list);
if (urgent)
cgsem_post(&(minioninfo->task_ready));
// N.B. this will only update often enough if a chip is > ~2GH/s
if (!urgent)
sys_chip_sta(minioncgpu, chip);
}
K_WLOCK(minioninfo->wwork_list);
item = k_unlink_tail(minioninfo->wwork_list);
K_WUNLOCK(minioninfo->wwork_list);
if (item) {
cgtime(&now);
howlong = tdiff(&now, &(DATA_WORK(item)->created));
minioninfo->que_work++;
minioninfo->que_time += howlong;
if (minioninfo->que_min == 0 || minioninfo->que_min > howlong)
minioninfo->que_min = howlong;
else if (minioninfo->que_max < howlong)
minioninfo->que_max = howlong;
for (i = 0; i < TIME_BANDS; i++) {
if (howlong < time_bands[i]) {
minioninfo->que_bands[i]++;
break;
}
}
if (i >= TIME_BANDS)
minioninfo->que_bands[TIME_BANDS]++;
}
return item;
}
fifo_task.chip = 0;
fifo_task.write = false;
fifo_task.address = MINION_SYS_FIFO_STA;
fifo_task.wsiz = 0;
fifo_task.rsiz = MINION_SYS_SIZ;
/*
* Fill the queues as follows:
* 1) put at least 1 in each queue or if islow then add 1
* 2) push each queue up to LOW or if count is high but islow, then add
LOW-1
* 3) push each LOW queue up to HIGH
*/
sentwork = false;
for (state = 0; state < 3; state++) {
#define CHP 0
//applog(LOG_ERR, "%s%i: chip %d presta %d: quew %d chw %d", minioncgpu->drv->name,
minioncgpu->device_id, CHP, state, minioninfo->chip_status[CHP].quework,
minioninfo->chip_status[CHP].chipwork);
for (chip = 0; chip < (int)MINION_CHIPS; chip++)
minioninfo->chip_status[chip].tohigh = false;
int tries = 0;
while (tries++ < 4) {
cmd = 0;
fifo_task.chip = chip;
fifo_task.reply = 0;
minion_txrx(&fifo_task);
if (fifo_task.reply <= 0) {
if (fifo_task.reply < (int)(fifo_task.osiz)) {
char *buf = bin2hex((unsigned char *)
(&(fifo_task.rbuf[fifo_task.osiz - fifo_task.rsiz])),
(int)(fifo_task.rsiz));
applog(LOG_ERR, "%s%i: Chip %d Bad fifo
reply (%s) size %d, should be %d",
minioncgpu->drv->name,
minioncgpu->device_id,
chip, buf,
fifo_task.reply, (int)
(fifo_task.osiz));
free(buf);
minioninfo->spi_errors++;
minioninfo->fifo_spi_errors[chip]++;
minioninfo->res_err_count[chip]++;
} else {
if (fifo_task.reply > (int)
(fifo_task.osiz)) {
applog(LOG_ERR, "%s%i: Chip %d
Unexpected fifo reply size %d, expected only %d",
minioncgpu->drv->name,
minioncgpu->device_id,
chip, fifo_task.reply,
(int)(fifo_task.osiz));
}
cmd = FIFO_CMD(fifo_task.rbuf,
fifo_task.osiz - fifo_task.rsiz);
// valid reply?
if (cmd < MINION_QUE_MAX) {
K_WLOCK(minioninfo-
>wchip_list[chip]);
minioninfo-
>chip_status[chip].realwork = cmd;
K_WUNLOCK(minioninfo-
>wchip_list[chip]);
if (cmd <= MINION_QUE_LOW || cmd >=
MINION_QUE_HIGH) {
applog(LOG_DEBUG, "%s%i: Chip
%d fifo cmd %d",
minioncgpu->drv-
>name,
minioncgpu-
>device_id,
chip, (int)cmd);
}
break;
}
K_WLOCK(minioninfo->wchip_list[chip]);
count = minioninfo->chip_status[chip].quework +
minioninfo->chip_status[chip].realwork;
islow = minioninfo->chip_status[chip].islow;
minioninfo->chip_status[chip].islow = false;
lowcount = minioninfo->chip_status[chip].lowcount;
K_WUNLOCK(minioninfo->wchip_list[chip]);
switch (state) {
case 0:
if (count == 0 || islow) {
item = next_work(minioninfo);
if (item) {
new_work_task(minioncgpu, item,
chip, true, state);
sentwork = true;
applog(MINION_LOG, "%s%i: 0 task 0x
%04x in chip %d list",
minioncgpu->drv-
>name,
minioncgpu-
>device_id,
DATA_WORK(item)-
>task_id, chip);
} else {
applog(LOG_ERR, "%s%i: chip %d
urgent empty work list",
minioncgpu->drv->name,
minioncgpu->device_id,
chip);
}
}
break;
case 1:
if (count < MINION_QUE_LOW || islow) {
// do case 2: after we've done other
chips
minioninfo->chip_status[chip].tohigh =
true;
j = count;
if (count >= MINION_QUE_LOW) {
// islow means run a full case 1
j = 1;
applog(LOG_ERR, "%s%i: chip %d low
que (%d) with high count %d",
minioncgpu->drv->name,
minioncgpu->device_id,
chip, lowcount, count);
}
for (; j < MINION_QUE_LOW; j++) {
item = next_work(minioninfo);
if (item) {
new_work_task(minioncgpu,
item, chip, false, state);
sentwork = true;
applog(MINION_LOG, "%s%i: 1
task 0x%04x in chip %d list",
minioncgpu-
>drv->name,
minioncgpu-
>device_id,
DATA_WORK(item)->task_id, chip);
} else {
applog(LOG_ERR, "%s%i: chip
%d non-urgent lo "
"empty work
list (count=%d)",
minioncgpu-
>drv->name,
minioncgpu-
>device_id,
chip, j);
}
}
}
break;
case 2:
if (count <= MINION_QUE_LOW || minioninfo-
>chip_status[chip].tohigh) {
for (j = count; j < MINION_QUE_HIGH; j++)
{
item = next_work(minioninfo);
if (item) {
new_work_task(minioncgpu,
item, chip, false, state);
sentwork = true;
applog(MINION_LOG, "%s%i: 2
task 0x%04x in chip %d list",
minioncgpu-
>drv->name,
minioncgpu-
>device_id,
DATA_WORK(item)->task_id, chip);
} else {
applog(LOG_DEBUG, "%s%i: chip
%d non-urgent hi "
"empty work
list (count=%d)",
minioncgpu-
>drv->name,
minioncgpu-
>device_id,
chip, j);
}
}
}
break;
}
} else
if (minioninfo->has_chip[chip] && minioninfo-
>chip_status[chip].overheat && state == 2)
sys_chip_sta(minioncgpu, chip);
}
}
sentwork = sentwork;
#if ENABLE_INT_NONO
if (sentwork) {
// Clear CMD interrupt since we've now sent more
K_WLOCK(minioninfo->tfree_list);
task = k_unlink_head(minioninfo->tfree_list);
DATA_TASK(task)->tid = ++(minioninfo->next_tid);
DATA_TASK(task)->chip = 0; // ignored
DATA_TASK(task)->write = true;
DATA_TASK(task)->address = MINION_SYS_INT_CLR;
DATA_TASK(task)->task_id = 0; // ignored
DATA_TASK(task)->wsiz = MINION_SYS_SIZ;
DATA_TASK(task)->rsiz = 0;
DATA_TASK(task)->wbuf[0] = MINION_CMD_INT;
DATA_TASK(task)->wbuf[1] = 0;
DATA_TASK(task)->wbuf[2] = 0;
DATA_TASK(task)->wbuf[3] = 0;
DATA_TASK(task)->urgent = false;
k_add_head(minioninfo->task_list, task);
K_WUNLOCK(minioninfo->tfree_list);
}
#endif
minioninfo->thr = thr;
/*
* SPI/ioctl write thread
*/
if (thr_info_create(&(minioninfo->spiw_thr), NULL, minion_spi_write, (void
*)minioncgpu)) {
applog(LOG_ERR, "%s%i: SPI write thread create failed",
minioncgpu->drv->name, minioncgpu->device_id);
return false;
}
pthread_detach(minioninfo->spiw_thr.pth);
/*
* SPI/ioctl results thread
*/
if (thr_info_create(&(minioninfo->spir_thr), NULL, minion_spi_reply, (void
*)minioncgpu)) {
applog(LOG_ERR, "%s%i: SPI reply thread create failed",
minioncgpu->drv->name, minioncgpu->device_id);
return false;
}
pthread_detach(minioninfo->spir_thr.pth);
/*
* Seperate results checking thread so ioctl timing can ignore the results
checking
*/
if (thr_info_create(&(minioninfo->res_thr), NULL, minion_results, (void
*)minioncgpu)) {
applog(LOG_ERR, "%s%i: Results thread create failed",
minioncgpu->drv->name, minioncgpu->device_id);
return false;
}
pthread_detach(minioninfo->res_thr.pth);
return true;
}
minioncgpu->shutdown = true;
}
static bool minion_queue_full(struct cgpu_info *minioncgpu)
{
struct minion_info *minioninfo = (struct minion_info *)(minioncgpu-
>device_data);
struct work *work, *usework;
int count, totneed, need, roll, roll_limit, chip;
bool ret, rolled;
if (minioninfo->initialised == false) {
cgsleep_us(42);
return true;
}
K_RLOCK(minioninfo->wwork_list);
count = minioninfo->wwork_list->count;
totneed = 0;
for (chip = 0; chip < (int)MINION_CHIPS; chip++) {
if (minioninfo->has_chip[chip] &&
!minioninfo->chip_status[chip].overheat) {
totneed += MINION_QUE_HIGH;
totneed -= minioninfo->chip_status[chip].quework;
totneed -= minioninfo->chip_status[chip].realwork;
// One for the pot :)
totneed++;
}
}
K_RUNLOCK(minioninfo->wwork_list);
if (need > 0)
ret = false;
else
ret = true;
}
return ret;
}
cgtime(&now);
if (!(minioninfo->chip_chk.tv_sec)) {
memcpy(&(minioninfo->chip_chk), &now, sizeof(now));
memcpy(&(minioninfo->chip_rpt), &now, sizeof(now));
return;
}
// Always run the calculations to check chip GHs for the LED
buf[0] = '\0';
res_err_msg[0] = '\0';
res_err_msg[1] = '\0';
K_RLOCK(minioninfo->hfree_list);
for (chip = 0; chip < (int)MINION_CHIPS; chip++) {
if (minioninfo->has_chip[chip]) {
len = strlen(buf);
if (minioninfo->hchip_list[chip]->count < 2)
ghs = 0.0;
else {
ghs = 0xffffffffull * (minioninfo->hchip_list[chip]->count
- 1);
ghs /= 1000000000.0;
ghs /= tdiff(&now, &(DATA_HIST(minioninfo-
>hchip_list[chip]->tail)->when));
}
if (minioninfo->chip_status[chip].first_nonce.tv_sec == 0L ||
tdiff(&now, &minioninfo->chip_status[chip].first_nonce) <
MINION_LED_TEST_TIME) {
ghs2_display[0] = '\0';
minioninfo->setled[chip] = false;
} else {
ghs2 = 0xffffffffull * (minioninfo-
>chip_status[chip].from_first_good - 1);
ghs2 /= 1000000000.0;
ghs2 /= tdiff(&now, &minioninfo-
>chip_status[chip].first_nonce);
minioninfo->setled[chip] = (ghs2 >= opt_minion_ledlimit);
snprintf(ghs2_display, sizeof(ghs2_display), "[%.2f]",
ghs2);
}
res_err_count = minioninfo->res_err_count[chip];
minioninfo->res_err_count[chip] = 0;
if (res_err_count > 100)
res_err_msg[0] = '!';
else if (res_err_count > 50)
res_err_msg[0] = '*';
else if (res_err_count > 0)
res_err_msg[0] = '\'';
else
res_err_msg[0] = '\0';
snprintf(buf + len, sizeof(buf) - len,
" %d=%s%.2f%s", chip, res_err_msg, ghs, ghs2_display);
minioninfo->history_ghs[chip] = ghs;
}
}
K_RUNLOCK(minioninfo->hfree_list);
if (!minioninfo->reset_mark[chip] ||
minioninfo->reset_count[chip] < 2) {
elapsed = 0.0;
ghs = 0.0;
} else {
// 'now' includes that it may have stopped getting
nonces
elapsed = tdiff(&now, &(DATA_HIST(minioninfo-
>reset_mark[chip])->when));
ghs = 0xffffffffull * (minioninfo->reset_count[chip]
- 1);
ghs /= 1000000000.0;
ghs /= elapsed;
}
expect = (double)(minioninfo->init_freq[chip]) *
MINION_RESET_PERCENT / 1000.0;
howlong = tdiff(&now, &(minioninfo->last_reset[chip]));
if (ghs <= expect && howlong >= minioninfo-
>reset_time[chip]) {
minioninfo->do_reset[chip] = expect;
if (std_reset) {
if (minioninfo->do_reset[chip] > 1.0) {
applog(LOG_WARNING, "%s%d: Chip %d %dMHz
threshold "
"%.2fGHs - resetting",
minioncgpu->drv->name,
minioncgpu->device_id,
chip, curr_freq,
minioninfo->do_reset[chip]);
} else {
applog(LOG_WARNING, "%s%d: Chip %d %dMhz
flagged - "
"resetting",
minioncgpu->drv->name,
minioncgpu->device_id,
chip, curr_freq);
}
} else {
if (minioninfo->do_reset[chip] > 1.0) {
applog(LOG_WARNING, "%s%d: Chip %d %dMHz
threshold "
"%.2fGHs - resetting to %dMhz",
minioncgpu->drv->name,
minioncgpu->device_id,
chip, curr_freq,
minioninfo->do_reset[chip],
new_freq);
} else {
applog(LOG_WARNING, "%s%d: Chip %d %dMhz
flagged - "
"resetting to %dMHz",
minioncgpu->drv->name,
minioncgpu->device_id,
chip, curr_freq, new_freq);
}
}
minioninfo->do_reset[chip] = 0.0;
memcpy(&(minioninfo->last_reset[chip]), &now, sizeof(now));
init_chip(minioncgpu, minioninfo, chip);
minioninfo->flag_reset[chip] = false;
}
}
}
}
if (minioninfo->initialised == false)
return hashcount;
minion_do_work(minioncgpu);
mutex_lock(&(minioninfo->nonce_lock));
if (minioninfo->new_nonces) {
hashcount += 0xffffffffull * minioninfo->new_nonces;
minioninfo->new_nonces = 0;
}
mutex_unlock(&(minioninfo->nonce_lock));
if (opt_minion_idlecount)
idle_report(minioncgpu);
/*
* To avoid wasting CPU, wait until we get an interrupt
* before returning back to the main cgminer work loop
* i.e. we then know we'll need more work
*/
cgsem_mswait(&(minioninfo->scan_work), MINION_SCAN_mS);
return hashcount;
}
max_temp = 0;
cores = 0;
mutex_lock(&(minioninfo->sta_lock));
for (chip = 0; chip < (int)MINION_CHIPS; chip++) {
if (minioninfo->has_chip[chip]) {
if (max_temp < minioninfo->chip_status[chip].temp)
max_temp = minioninfo->chip_status[chip].temp;
for (core = 0; core < MINION_CORES; core++) {
if (minioninfo->chip_core_ena[core >> 5][chip] & (0x1 <<
(core % 32)))
cores++;
}
}
}
mutex_unlock(&(minioninfo->sta_lock));
#define CHIPS_PER_STAT 5
if (minioninfo->initialised == false)
return NULL;
max_chip = 0;
for (chip = 0; chip < (int)MINION_CHIPS; chip++)
if (minioninfo->has_chip[chip]) {
max_chip = chip;
if (opt_minion_extra) {
data[0] = '\0';
datalen = 0;
for (i = 0; i < MINION_CORES; i++) {
if (datalen < sizeof(data)) {
nlen = snprintf(data+datalen, sizeof(data)-
datalen,
"%s%"PRIu64"-%s%"PRIu64,
i == 0 ? "" : "/",
minioninfo->core_good[chip][i],
minioninfo->core_bad[chip][i] ? "'"
: "",
minioninfo->core_bad[chip][i]);
if (nlen < 1)
break;
datalen += nlen;
}
}
snprintf(buf, sizeof(buf), "Chip %d Cores Good-Bad", chip);
root = api_add_string(root, buf, data, true);
}
data[0] = '\0';
for (j = i; j <= to; j++) {
snprintf(buf, sizeof(buf),
"%s%d",
j == i ? "" : " ",
minioninfo->has_chip[j] ? 1 : 0);
strcat(data, buf);
}
snprintf(buf, sizeof(buf), "Detected %02d - %02d", i, to);
root = api_add_string(root, buf, data, true);
data[0] = '\0';
for (j = i; j <= to; j++) {
snprintf(buf, sizeof(buf),
"%s%8"PRIu64,
j == i ? "" : " ",
minioninfo->chip_nonces[j]);
strcat(data, buf);
}
snprintf(buf, sizeof(buf), "Nonces %02d - %02d", i, to);
root = api_add_string(root, buf, data, true);
data[0] = '\0';
for (j = i; j <= to; j++) {
snprintf(buf, sizeof(buf),
"%s%8"PRIu64,
j == i ? "" : " ",
minioninfo->chip_nononces[j]);
strcat(data, buf);
}
snprintf(buf, sizeof(buf), "NoNonces %02d - %02d", i, to);
root = api_add_string(root, buf, data, true);
data[0] = '\0';
for (j = i; j <= to; j++) {
snprintf(buf, sizeof(buf),
"%s%8"PRIu64,
j == i ? "" : " ",
minioninfo->chip_good[j]);
strcat(data, buf);
}
snprintf(buf, sizeof(buf), "Good %02d - %02d", i, to);
root = api_add_string(root, buf, data, true);
data[0] = '\0';
for (j = i; j <= to; j++) {
snprintf(buf, sizeof(buf),
"%s%8"PRIu64,
j == i ? "" : " ",
minioninfo->chip_bad[j]);
strcat(data, buf);
}
snprintf(buf, sizeof(buf), "Bad %02d - %02d", i, to);
root = api_add_string(root, buf, data, true);
data[0] = '\0';
for (j = i; j <= to; j++) {
snprintf(buf, sizeof(buf),
"%s%8"PRIu64,
j == i ? "" : " ",
minioninfo->chip_err[j]);
strcat(data, buf);
}
snprintf(buf, sizeof(buf), "Err %02d - %02d", i, to);
root = api_add_string(root, buf, data, true);
data[0] = '\0';
for (j = i; j <= to; j++) {
snprintf(buf, sizeof(buf),
"%s%8"PRIu64,
j == i ? "" : " ",
minioninfo->fifo_spi_errors[j]);
strcat(data, buf);
}
snprintf(buf, sizeof(buf), "FifoSpiErr %02d - %02d", i, to);
root = api_add_string(root, buf, data, true);
data[0] = '\0';
for (j = i; j <= to; j++) {
snprintf(buf, sizeof(buf),
"%s%8"PRIu64,
j == i ? "" : " ",
minioninfo->res_spi_errors[j]);
strcat(data, buf);
}
snprintf(buf, sizeof(buf), "ResSpiErr %02d - %02d", i, to);
root = api_add_string(root, buf, data, true);
data[0] = '\0';
for (j = i; j <= to; j++) {
snprintf(buf, sizeof(buf),
"%s%"PRIu64"/%"PRIu64"/%"PRIu64"/%"PRIu64"/%"PRIu64,
j == i ? "" : " ",
minioninfo->use_res2[j],
minioninfo->tasks_failed[j],
minioninfo->tasks_recovered[j],
minioninfo->nonces_failed[j],
minioninfo->nonces_recovered[j]);
strcat(data, buf);
}
snprintf(buf, sizeof(buf), "Redo %02d - %02d", i, to);
root = api_add_string(root, buf, data, true);
}
que_work = chip_work = 0;
for (chip = 0; chip <= max_chip; chip++) {
if (minioninfo->has_chip[chip]) {
que_work += minioninfo->wque_list[chip]->count;
chip_work += minioninfo->wchip_list[chip]->count;
}
}
#if DO_IO_STATS
#define sta_api(_name, _iostat) \
do { \
if ((_iostat).count) { \
float _davg = (float)((_iostat).total_delay) / (float)
((_iostat).count); \
float _dlavg = (float)((_iostat).total_dlock) / (float)
((_iostat).count); \
float _dlwavg = (float)((_iostat).total_dlwait) / (float)
((_iostat).count); \
float _bavg = (float)((_iostat).total_bytes) / (float)
((_iostat).count); \
float _tavg = (float)((_iostat).tsd) / (float)
((_iostat).count); \
snprintf(data, sizeof(data), "%s Count=%"PRIu64 \
" Delay=%.0fus DAvg=%.3f" \
" DMin=%.0f DMax=%.0f DZ=%"PRIu64 \
" DLock=%.0fus DLAvg=%.3f" \
" DLMin=%.0f DLMax=%.0f DZ=%"PRIu64 \
" DLWait=%.0fus DLWAvg=%.3f" \
" Bytes=%"PRIu64" BAvg=%.3f" \
" BMin=%"PRIu64" BMax=%"PRIu64" BZ=%"PRIu64 \
" TSD=%.0fus TAvg=%.03f", \
_name, (_iostat).count, \
(_iostat).total_delay, _davg, (_iostat).min_delay, \
(_iostat).max_delay, (_iostat).zero_delay, \
(_iostat).total_dlock, _dlavg, (_iostat).min_dlock, \
(_iostat).max_dlock, (_iostat).zero_dlock, \
(_iostat).total_dlwait, _dlwavg, \
(_iostat).total_bytes, _bavg, (_iostat).min_bytes, \
(_iostat).max_bytes, (_iostat).zero_bytes, \
(_iostat).tsd, _tavg); \
root = api_add_string(root, buf, data, true); \
} \
} while(0);
double avg;
root = api_add_uint64(root, "ToQue", &(minioninfo->que_work), true);
if (minioninfo->que_work)
avg = minioninfo->que_time / (double)(minioninfo->que_work);
else
avg = 0;
root = api_add_double(root, "Que Avg", &avg, true);
root = api_add_double(root, "Que Min", &(minioninfo->que_min), true);
root = api_add_double(root, "Que Max", &(minioninfo->que_max), true);
data[0] = '\0';
for (i = 0; i <= TIME_BANDS; i++) {
snprintf(buf, sizeof(buf),
"%s%"PRIu64,
i == 0 ? "" : "/",
minioninfo->que_bands[i]);
strcat(data, buf);
}
root = api_add_string(root, "Que Bands", data, true);
root = api_add_uint64(root, "ToTxRx", &(minioninfo->wt_work), true);
if (minioninfo->wt_work)
avg = minioninfo->wt_time / (double)(minioninfo->wt_work);
else
avg = 0;
root = api_add_double(root, "TxRx Avg", &avg, true);
root = api_add_double(root, "TxRx Min", &(minioninfo->wt_min), true);
root = api_add_double(root, "TxRx Max", &(minioninfo->wt_max), true);
data[0] = '\0';
for (i = 0; i <= TIME_BANDS; i++) {
snprintf(buf, sizeof(buf),
"%s%"PRIu64,
i == 0 ? "" : "/",
minioninfo->wt_bands[i]);
strcat(data, buf);
}
root = api_add_string(root, "TxRx Bands", data, true);
return root;
}
#endif