/* * Copyright 2013-2014 Andrew Smith - BlackArrow Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. See COPYING for more details. */ #include "config.h" #include "compat.h" #include "miner.h" #include "klist.h" #include #include #ifndef LINUX static void minion_detect(__maybe_unused bool hotplug) { } #else #include #include #include #include #include #include // Define this to 1 to enable interrupt code and enable no_nonce #define ENABLE_INT_NONO 0 // Define this to 1 if compiling on RockChip and not on RPi #define MINION_ROCKCHIP 0 // The code is always in - this just decides if it does it static bool minreread = true; #define MINION_SPI_BUS 0 #define MINION_SPI_CHIP 0 //#define MINION_SPI_SPEED 2000000 #define MINION_SPI_SPEED 1000000 #define MINION_SPI_BUFSIZ 1024 #define MINION_CHIPS 32 #define MINION_CORES 99 #define FAKE_CORE MINION_CORES /* * 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 #define MINION_FFL " - from %s %s() line %d" #define MINION_FFL_HERE __FILE__, __func__, __LINE__ #define MINION_FFL_PASS file, func, line #define MINION_FFL_ARGS __maybe_unused const char *file, \ __maybe_unused const char *func, \ __maybe_unused const int line #define minion_txrx(_task) _minion_txrx(minioncgpu, minioninfo, _task, MINION_FFL_HERE) #define MINION_SYS_REGS 0x00 #define MINION_CORE_REGS 0x10 #define MINION_RES_BUF 0x20 #define MINION_CMD_QUE 0x30 #define MINION_NONCE_RANGES 0x70 #define DATA_SIZ (sizeof(uint32_t)) // All SYS data sizes are DATA_SIZ #define MINION_SYS_CHIP_SIG 0x00 #define MINION_SYS_CHIP_STA 0x01 #define MINION_SYS_TEMP_CTL 0x03 #define MINION_SYS_FREQ_CTL 0x04 #define MINION_SYS_NONCE_LED 0x05 #define MINION_SYS_MISC_CTL 0x06 #define MINION_SYS_RSTN_CTL 0x07 #define MINION_SYS_INT_ENA 0x08 #define MINION_SYS_INT_CLR 0x09 #define MINION_SYS_INT_STA 0x0a #define MINION_SYS_FIFO_STA 0x0b #define MINION_SYS_QUE_TRIG 0x0c #define MINION_SYS_BUF_TRIG 0x0d #define MINION_SYS_IDLE_CNT 0x0e // 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) // All SYS data sizes are DATA_SIZ #define MINION_SYS_SIZ DATA_SIZ // Header Pin 18 = GPIO5 = BCM 24 #define MINION_GPIO_RESULT_INT_PIN 24 // RockChip is pin 172 ... #define MINION_GPIO_SYS "/sys/class/gpio" #define MINION_GPIO_ENA "/export" #define MINION_GPIO_ENA_VAL "%d" #define MINION_GPIO_DIS "/unexport" #define MINION_GPIO_PIN "/gpio%d" #define MINION_GPIO_DIR "/direction" #define MINION_GPIO_DIR_READ "in" #define MINION_GPIO_DIR_WRITE "out" #define MINION_GPIO_EDGE "/edge" #define MINION_GPIO_EDGE_NONE "none" #define MINION_GPIO_EDGE_RISING "rising" #define MINION_GPIO_EDGE_FALLING "falling" #define MINION_GPIO_EDGE_BOTH "both" #define MINION_GPIO_ACT "/active_low" #define MINION_GPIO_ACT_LO "1" #define MINION_GPIO_ACT_HI "0" #define MINION_GPIO_VALUE "/value" #define MINION_RESULT_INT 0x01 #define MINION_RESULT_FULL_INT 0x02 #define MINION_CMD_INT 0x04 #define MINION_CMD_FULL_INT 0x08 #define MINION_TEMP_LOW_INT 0x10 #define MINION_TEMP_HI_INT 0x20 #define MINION_ALL_INT MINION_RESULT_INT | \ MINION_RESULT_FULL_INT | \ MINION_CMD_INT | \ MINION_CMD_FULL_INT | \ MINION_TEMP_LOW_INT | \ MINION_TEMP_HI_INT #define RSTN_CTL_RESET_CORES 0x01 #define RSTN_CTL_FLUSH_RESULTS 0x02 #define RSTN_CTL_FLUSH_CMD_QUEUE 0x04 #define RSTN_CTL_SPI_SW_RSTN 0x08 #define RSTN_CTL_SHA_MGR_RESET 0x10 // 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) // LP #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 returned by MINION_SYS_CHIP_STA 0x01 STA_TEMP() #define MINION_TEMP_40 0 #define MINION_TEMP_60 1 #define MINION_TEMP_80 3 #define MINION_TEMP_100 7 #define MINION_TEMP_OVER 15 static const char *min_temp_40 = "<40"; static const char *min_temp_60 = "40-60"; static const char *min_temp_80 = "60-80"; static const char *min_temp_100 = "80-100"; static const char *min_temp_over = ">100"; static const char *min_temp_invalid = "?"; /* * 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 // CORE data size is DATA_SIZ #define MINION_CORE_ENA0_31 0x10 #define MINION_CORE_ENA32_63 0x11 #define MINION_CORE_ENA64_95 0x12 #define MINION_CORE_ENA96_98 0x13 #define MINION_CORE_ACT0_31 0x14 #define MINION_CORE_ACT32_63 0x15 #define MINION_CORE_ACT64_95 0x16 #define MINION_CORE_ACT96_98 0x17 // All CORE data sizes are DATA_SIZ #define MINION_CORE_SIZ DATA_SIZ #define MINION_CORE_ALL "all" // RES data size is minion_result #define MINION_RES_DATA 0x20 #define MINION_RES_PEEK 0x21 // QUE data size is minion_que #define MINION_QUE_0 0x30 #define MINION_QUE_R 0x31 // RANGE data sizes are DATA_SIZ #define MINION_NONCE_START 0x70 #define MINION_NONCE_RANGE 0x71 // This must be >= max txsiz + max rxsiz #define MINION_BUFSIZ 1024 #define u8tou32(_c, _off) (((uint8_t *)(_c))[(_off)+0] + \ ((uint8_t *)(_c))[(_off)+1] * 0x100 + \ ((uint8_t *)(_c))[(_off)+2] * 0x10000 + \ ((uint8_t *)(_c))[(_off)+3] * 0x1000000 ) #define MINION_ADDR_WRITE 0x7f #define MINION_ADDR_READ 0x80 #define READ_ADDR(_reg) ((_reg) | MINION_ADDR_READ) #define WRITE_ADDR(_reg) ((_reg) & MINION_ADDR_WRITE) #define IS_ADDR_READ(_reg) (((_reg) & MINION_ADDR_READ) == MINION_ADDR_READ) #define IS_ADDR_WRITE(_reg) (((_reg) & MINION_ADDR_READ) == 0) #define SET_HEAD_WRITE(_h, _reg) ((_h)->reg) = WRITE_ADDR(_reg) #define SET_HEAD_READ(_h, _reg) ((_h)->reg) = READ_ADDR(_reg) #define SET_HEAD_SIZ(_h, _siz) \ do { \ ((_h)->siz)[0] = (uint8_t)((_siz) & 0xff); \ ((_h)->siz)[1] = (uint8_t)(((_siz) & 0xff00) >> 8); \ } while (0) struct minion_header { uint8_t chip; uint8_t reg; uint8_t siz[2]; uint8_t data[4]; // placeholder }; #define HSIZE() (sizeof(struct minion_header) - 4) #define MINION_NOCHIP_SIG 0x00000000 #define MINION_NOCHIP_SIG2 0xffffffff #define MINION_CHIP_SIG 0xb1ac8a44 /* * 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) #define MINION_FREQ_MIN 100 #define MINION_FREQ_DEF 1000 #define MINION_FREQ_MAX 1400 #define MINION_FREQ_FACTOR 100 #define MINION_FREQ_FACTOR_MIN 1 #define MINION_FREQ_FACTOR_MAX 14 static uint32_t minion_freq[] = { 0x0, 0x205032, // 1 = 100Mhz 0x203042, // 2 = 200Mhz 0x20204B, // 3 = 300Mhz 0x201042, // 4 = 400Mhz 0x201053, // 5 = 500Mhz 0x200032, // 6 = 600Mhz 0x20003A, // 7 = 700Mhz 0x200042, // 8 = 800Mhz 0x20004B, // 9 = 900Mhz 0x200053, // 10 = 1000Mhz 0x21005B, // 11 = 1100Mhz 0x210064, // 12 = 1200Mhz 0x21006C, // 13 = 1300Mhz 0x210074 // 14 = 1400Mhz }; #define MINION_RESET_PERCENT 50.0 #define STA_TEMP(_sta) ((uint16_t)((_sta)[3] & 0x1f)) #define STA_CORES(_sta) ((uint16_t)((_sta)[2])) #define STA_FREQ(_sta) ((uint32_t)((_sta)[1]) * 0x100 + (uint32_t)((_sta)[0])) // Randomly between 1s and 2s per chip #define MINION_STATS_UPDATE_TIME_mS 1000 #define MINION_STATS_UPDATE_RAND_mS 1000 // Don't report it more than once every ... 5s #define MINION_IDLE_MESSAGE_ms 5000 struct minion_status { uint16_t temp; uint16_t cores; uint32_t freq; uint32_t quework; uint32_t chipwork; uint32_t realwork; // chipwork, but FIFO_STA can update it struct timeval last; bool overheat; bool islow; bool tohigh; int lowcount; uint32_t freqsent; uint32_t overheats; struct timeval lastoverheat; struct timeval lastrecover; double overheattime; uint32_t tempsent; uint32_t idle; uint32_t last_rpt_idle; struct timeval idle_rpt; }; #define ENABLE_CORE(_core, _n) ((_core[_n >> 3]) |= (1 << (_n % 8))) #define CORE_IDLE(_core, _n) ((_core[_n >> 3]) & (1 << (_n % 8))) #define FIFO_RES(_fifo, _off) ((_fifo)[(_off) + 0]) #define FIFO_CMD(_fifo, _off) ((_fifo)[(_off) + 1]) #define RES_GOLD(_res) ((((_res)->status[3]) & 0x80) == 0) #define RES_CHIP(_res) (((_res)->status[3]) & 0x1f) #define RES_CORE(_res) ((_res)->status[2]) #define RES_TASK(_res) ((int)((_res)->status[1]) * 0x100 + (int)((_res)->status[0])) #define RES_NONCE(_res) u8tou32((_res)->nonce, 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]; }; #define MINION_RES_DATA_SIZ sizeof(struct minion_result) /* * (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 #define MINION_MAX_TASK_ID 0xffff 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 // Number of results to make a GPIO interrupt //#define MINION_RESULT_INT_SIZE 1 #define MINION_RESULT_INT_SIZE 2 /* * 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 #define ALLOC_WITEMS 4096 #define LIMIT_WITEMS 0 typedef struct witem { struct work *work; uint32_t task_id; struct timeval sent; int nonces; bool urgent; bool stale; // if stale, don't decrement que/chip/realwork when discarded bool rolled; int errors; // uncertain since the error could mean task_id is wrong } WITEM; #define ALLOC_TITEMS 256 #define LIMIT_TITEMS 0 typedef struct titem { uint64_t tid; uint8_t chip; bool write; uint8_t address; uint32_t task_id; uint32_t wsiz; uint32_t osiz; uint32_t rsiz; uint8_t wbuf[MINION_BUFSIZ]; uint8_t obuf[MINION_BUFSIZ]; uint8_t rbuf[MINION_BUFSIZ]; int reply; bool urgent; uint8_t work_state; struct work *work; K_ITEM *witem; } TITEM; #define ALLOC_RITEMS 256 #define LIMIT_RITEMS 0 typedef struct ritem { int chip; int core; uint32_t task_id; uint32_t nonce; struct timeval when; /* * Only once per task_id if no nonces were found * Sent with core = 0 * However, currently it always sends it at the end of every task * TODO: code assumes it doesn't - change later when we * see what the final hardware does (minor code performance gain) */ bool no_nonce; // If we requested the result twice: bool another; uint32_t task_id2; uint32_t nonce2; } RITEM; #define ALLOC_HITEMS 4096 #define LIMIT_HITEMS 0 // How much history to keep (5min) #define MINION_HISTORY_s 300 // History must be always generated for the reset check #define MINION_MAX_RESET_CHECK 2 typedef struct hitem { struct timeval when; } HITEM; #define DATAW(_item) ((WITEM *)(_item->data)) #define DATAT(_item) ((TITEM *)(_item->data)) #define DATAR(_item) ((RITEM *)(_item->data)) #define DATAH(_item) ((HITEM *)(_item->data)) // Set this to 1 to enable iostats processing // N.B. it slows down mining #define DO_IO_STATS 0 #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); typedef struct iostat { uint64_t count; // total ioctl() double total_delay; // total elapsed ioctl() double min_delay; double max_delay; uint64_t zero_delay; // how many had <= 0 delay // Above but including locking double total_dlock; double min_dlock; double max_dlock; uint64_t zero_dlock; // Total time waiting to get lock double total_dlwait; // 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; uint64_t total_bytes; // ioctl() bytes uint64_t min_bytes; uint64_t max_bytes; uint64_t zero_bytes; // how many had siz == 0 double tsd; // total doing one extra cgtime() each time } IOSTAT; #else #define IO_STAT_NOW(_tv) #define IO_STAT_STORE(_sta, _fin, _lsta, _lfin, _tsd, _buf, _siz, _reply, _ioc) #endif struct minion_info { 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; // TODO: need to track disabled chips - done? int chips; bool chip[MINION_CHIPS]; int init_freq[MINION_CHIPS]; int init_temp[MINION_CHIPS]; uint8_t init_cores[MINION_CHIPS][DATA_SIZ*MINION_CORE_REPS]; 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 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]; struct minion_status chip_status[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]; // Work items K_LIST *wfree_list; K_STORE *wwork_list; K_STORE *wque_list[MINION_CHIPS]; K_STORE *wchip_list[MINION_CHIPS]; // 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; struct timeval last_did; // 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]; // Gets reset to zero each time it is used in reporting int res_err_count[MINION_CHIPS]; #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 initialised; }; 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); DATAW(item)->work = work; DATAW(item)->task_id = 0; memset(&(DATAW(item)->sent), 0, sizeof(DATAW(item)->sent)); DATAW(item)->nonces = 0; DATAW(item)->urgent = false; DATAW(item)->rolled = rolled; DATAW(item)->errors = 0; k_add_head(minioninfo->wwork_list, item); K_WUNLOCK(minioninfo->wfree_list); } static bool oldest_nonce(struct cgpu_info *minioncgpu, int *chip, int *core, uint32_t *task_id, uint32_t *nonce, bool *no_nonce, struct timeval *when, bool *another, uint32_t *task_id2, uint32_t *nonce2) { struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); K_ITEM *item = NULL; bool found = false; K_WLOCK(minioninfo->rnonce_list); item = k_unlink_tail(minioninfo->rnonce_list); if (item) { found = true; *chip = DATAR(item)->chip; *core = DATAR(item)->core; *task_id = DATAR(item)->task_id; *nonce = DATAR(item)->nonce; *no_nonce = DATAR(item)->no_nonce; memcpy(when, &(DATAR(item)->when), sizeof(*when)); *another = DATAR(item)->another; *task_id2 = DATAR(item)->task_id2; *nonce2 = DATAR(item)->nonce2; k_free_head(minioninfo->rfree_list, item); } K_WUNLOCK(minioninfo->rnonce_list); return found; } static const char *addr2txt(uint8_t addr) { switch (addr) { case READ_ADDR(MINION_SYS_CHIP_SIG): return "RChipSig"; case READ_ADDR(MINION_SYS_CHIP_STA): return "RChipSta"; case WRITE_ADDR(MINION_SYS_MISC_CTL): return "WMiscCtrl"; case WRITE_ADDR(MINION_SYS_RSTN_CTL): return "WResetCtrl"; case READ_ADDR(MINION_SYS_FIFO_STA): return "RFifoSta"; case READ_ADDR(MINION_CORE_ENA0_31): return "RCoreEna0-31"; case WRITE_ADDR(MINION_CORE_ENA0_31): return "WCoreEna0-31"; case READ_ADDR(MINION_CORE_ENA32_63): return "RCoreEna32-63"; case WRITE_ADDR(MINION_CORE_ENA32_63): return "WCoreEna32-63"; case READ_ADDR(MINION_CORE_ENA64_95): return "RCoreEna64-95"; case WRITE_ADDR(MINION_CORE_ENA64_95): return "WCoreEna64-95"; case READ_ADDR(MINION_CORE_ENA96_98): return "RCoreEna96-98"; case WRITE_ADDR(MINION_CORE_ENA96_98): return "WCoreEna96-98"; case READ_ADDR(MINION_CORE_ACT0_31): return "RCoreAct0-31"; case READ_ADDR(MINION_CORE_ACT32_63): return "RCoreAct32-63"; case READ_ADDR(MINION_CORE_ACT64_95): return "RCoreAct64-95"; case READ_ADDR(MINION_CORE_ACT96_98): return "RCoreAct96-98"; case READ_ADDR(MINION_RES_DATA): return "RResData"; case READ_ADDR(MINION_RES_PEEK): return "RResPeek"; case WRITE_ADDR(MINION_QUE_0): return "WQueWork"; case READ_ADDR(MINION_NONCE_START): return "RNonceStart"; case WRITE_ADDR(MINION_NONCE_START): return "WNonceStart"; case READ_ADDR(MINION_NONCE_RANGE): return "RNonceRange"; case WRITE_ADDR(MINION_NONCE_RANGE): return "WNonceRange"; case READ_ADDR(MINION_SYS_INT_STA): return "RIntSta"; case WRITE_ADDR(MINION_SYS_INT_ENA): return "WIntEna"; case WRITE_ADDR(MINION_SYS_INT_CLR): return "WIntClear"; case WRITE_ADDR(MINION_SYS_BUF_TRIG): return "WResTrigger"; case WRITE_ADDR(MINION_SYS_QUE_TRIG): return "WCmdTrigger"; case READ_ADDR(MINION_SYS_TEMP_CTL): return "RTempCtrl"; case WRITE_ADDR(MINION_SYS_TEMP_CTL): return "WTempCtrl"; case READ_ADDR(MINION_SYS_FREQ_CTL): return "RFreqCtrl"; case WRITE_ADDR(MINION_SYS_FREQ_CTL): return "WFreqCtrl"; case READ_ADDR(MINION_SYS_IDLE_CNT): return "RIdleCnt"; } // gcc warning if this is in default: if (IS_ADDR_READ(addr)) return "RUnhandled"; else return "WUnhandled"; } // For display_ioctl() #define IOCTRL_LOG LOG_WARNING // For all other debug so it can easily be switched always on #define MINION_LOG LOG_DEBUG // For task corruption logging #define MINTASK_LOG LOG_DEBUG // Set to 1 for debug #define MINION_SHOW_IO 0 #define DATA_ALL 2048 #define DATA_OFF 512 #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_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 chip %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 chip %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 chip %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" " chip %d core %d gold %s nonce 0x%s", name, i+1, rescount, reply, rsiz, RES_TASK(res), (int)RES_CHIP(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 chip %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 #define MINION_UNEXPECTED_TASK -999 #define MINION_OVERSIZE_TASK -998 static int __do_ioctl(struct minion_info *minioninfo, uint8_t *obuf, uint32_t osiz, uint8_t *rbuf, uint32_t rsiz, MINION_FFL_ARGS) { struct spi_ioc_transfer tran; int ret; #if MINION_SHOW_IO char dataw[DATA_ALL], datar[DATA_ALL]; #endif #if DO_IO_STATS struct timeval sta, fin, lsta, lfin, tsd; #endif if ((int)osiz > MINION_BUFSIZ) quitfrom(1, file, func, line, "%s() invalid osiz %u > %d (chip=%d reg=0x%02x)", __func__, osiz, MINION_BUFSIZ, (int)(obuf[0]), obuf[1]); if (rsiz >= osiz) quitfrom(1, file, func, line, "%s() invalid rsiz %u >= osiz %u (chip=%u reg=0x%02x)", __func__, rsiz, osiz, (int)(obuf[0]), obuf[1]); memset(&obuf[0] + osiz - rsiz, 0xff, rsiz); #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); char *buf = bin2hex((unsigned char *)&(dataw[DATA_OFF]), osiz); applog(IOCTRL_LOG, "*** %s() sending %02x %02x %s %02x %02x", __func__, dataw[0], dataw[DATA_OFF-1], buf, dataw[DATA_OFF+osiz], dataw[DATA_ALL-1]); free(buf); #endif memset((char *)rbuf, 0x00, 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 = 0; 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)); IO_STAT_NOW(&sta); ret = ioctl(minioninfo->spifd, SPI_IOC_MESSAGE(1), (void *)&tran); IO_STAT_NOW(&fin); mutex_unlock(&(minioninfo->spi_lock)); IO_STAT_NOW(&lfin); IO_STAT_NOW(&tsd); IO_STAT_STORE(&sta, &fin, &lsta, &lfin, &tsd, obuf, osiz, ret, 1); #if MINION_SHOW_IO if (ret > 0) { buf = bin2hex((unsigned char *)&(datar[DATA_OFF]), ret); applog(IOCTRL_LOG, "*** %s() reply %d = %02x %02x %s %02x %02x", __func__, ret, 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); memcpy(&rbuf[0], &datar[DATA_OFF], osiz); display_ioctl(ret, osiz, (uint8_t *)(&dataw[DATA_OFF]), rsiz, (uint8_t *)(&datar[DATA_OFF])); #endif return ret; } #if 1 #define do_ioctl(_obuf, _osiz, _rbuf, _rsiz) __do_ioctl(minioninfo, _obuf, _osiz, _rbuf, _rsiz, MINION_FFL_HERE) #else #define do_ioctl(_obuf, _osiz, _rbuf, _rsiz) _do_ioctl(minioninfo, _obuf, _osiz, _rbuf, _rsiz, MINION_FFL_HERE) // This sends an expected to work, SPI command before each SPI command static int _do_ioctl(struct minion_info *minioninfo, uint8_t *obuf, uint32_t osiz, uint8_t *rbuf, uint32_t rsiz, MINION_FFL_ARGS) { struct minion_header *head; uint8_t buf1[MINION_BUFSIZ]; uint8_t buf2[MINION_BUFSIZ]; uint32_t siz; head = (struct minion_header *)buf1; head->chip = 1; // Needs to be set to a valid chip head->reg = READ_ADDR(MINION_SYS_FIFO_STA); SET_HEAD_SIZ(head, DATA_SIZ); siz = HSIZE() + DATA_SIZ; __do_ioctl(minioninfo, buf1, siz, buf2, MINION_CORE_SIZ, MINION_FFL_PASS); return __do_ioctl(minioninfo, obuf, osiz, rbuf, rsiz, MINION_FFL_PASS); } #endif static bool _minion_txrx(struct cgpu_info *minioncgpu, struct minion_info *minioninfo, TITEM *task, MINION_FFL_ARGS) { struct minion_header *head; head = (struct minion_header *)(task->obuf); head->chip = task->chip; if (task->write) SET_HEAD_WRITE(head, task->address); else SET_HEAD_READ(head, task->address); SET_HEAD_SIZ(head, task->wsiz + task->rsiz); if (task->wsiz) memcpy(&(head->data[0]), task->wbuf, task->wsiz); task->osiz = HSIZE() + task->wsiz + task->rsiz; task->reply = do_ioctl(task->obuf, task->osiz, task->rbuf, task->rsiz); if (task->reply < 0) { applog(LOG_ERR, "%s%d: chip=%d ioctl failed reply=%d err=%d" MINION_FFL, minioncgpu->drv->name, minioncgpu->device_id, task->chip, task->reply, errno, MINION_FFL_PASS); } else if (task->reply < (int)(task->osiz)) { applog(LOG_ERR, "%s%d: chip=%d ioctl failed to write %d only wrote %d (err=%d)" MINION_FFL, minioncgpu->drv->name, minioncgpu->device_id, task->chip, (int)(task->osiz), task->reply, errno, MINION_FFL_PASS); } return (task->reply >= (int)(task->osiz)); } // Only for DATA_SIZ commands static int build_cmd(struct cgpu_info *minioncgpu, struct minion_info *minioninfo, int chip, uint8_t reg, uint8_t *rbuf, uint32_t rsiz, uint8_t *data) { struct minion_header *head; uint8_t wbuf[MINION_BUFSIZ]; uint32_t wsiz; int reply; head = (struct minion_header *)wbuf; head->chip = chip; head->reg = reg; SET_HEAD_SIZ(head, DATA_SIZ); head->data[0] = data[0]; head->data[1] = data[1]; head->data[2] = data[2]; head->data[3] = data[3]; wsiz = HSIZE() + DATA_SIZ; reply = do_ioctl(wbuf, wsiz, rbuf, rsiz); 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; } static void init_chip(struct cgpu_info *minioncgpu, struct minion_info *minioninfo, int chip) { uint8_t rbuf[MINION_BUFSIZ]; uint8_t data[4]; __maybe_unused int reply; int choice; uint32_t freq; // Complete chip reset data[0] = 0x00; data[1] = 0x00; data[2] = 0xa5; data[3] = 0xf5; reply = build_cmd(minioncgpu, minioninfo, chip, WRITE_ADDR(MINION_SYS_RSTN_CTL), rbuf, 0, data); // Default reset data[0] = SYS_RSTN_CTL_INIT; data[1] = 0x00; data[2] = 0x00; data[3] = 0x00; reply = build_cmd(minioncgpu, minioninfo, chip, WRITE_ADDR(MINION_SYS_RSTN_CTL), rbuf, 0, data); // Default initialisation data[0] = SYS_MISC_CTL_DEFAULT; data[1] = 0x00; data[2] = 0x00; data[3] = 0x00; reply = build_cmd(minioncgpu, minioninfo, chip, WRITE_ADDR(MINION_SYS_MISC_CTL), rbuf, 0, data); // Set chip frequency choice = minioninfo->init_freq[chip]; if (choice < MINION_FREQ_MIN || choice > MINION_FREQ_MAX) choice = MINION_FREQ_DEF; choice /= MINION_FREQ_FACTOR; if (choice < MINION_FREQ_FACTOR_MIN) choice = MINION_FREQ_FACTOR_MIN; if (choice > MINION_FREQ_FACTOR_MAX) choice = MINION_FREQ_FACTOR_MAX; freq = minion_freq[choice]; data[0] = (uint8_t)(freq & 0xff); data[1] = (uint8_t)(((freq & 0xff00) >> 8) & 0xff); data[2] = (uint8_t)(((freq & 0xff0000) >> 16) & 0xff); data[3] = (uint8_t)(((freq & 0xff000000) >> 24) & 0xff); minioninfo->chip_status[chip].freqsent = freq; reply = build_cmd(minioncgpu, minioninfo, chip, WRITE_ADDR(MINION_SYS_FREQ_CTL), rbuf, 0, data); // Set temp threshold choice = minioninfo->init_temp[chip]; if (choice == MINION_TEMP_CTL_DISABLE) choice = MINION_TEMP_CTL_DISABLE_VALUE; else { if (choice < MINION_TEMP_CTL_MIN_VALUE || choice > MINION_TEMP_CTL_MAX_VALUE) choice = MINION_TEMP_CTL_DEF; choice -= MINION_TEMP_CTL_MIN_VALUE; choice /= MINION_TEMP_CTL_STEP; choice += MINION_TEMP_CTL_MIN; if (choice < MINION_TEMP_CTL_MIN) choice = MINION_TEMP_CTL_MIN; if (choice > MINION_TEMP_CTL_MAX) choice = MINION_TEMP_CTL_MAX; } data[0] = (uint8_t)choice; data[1] = 0; data[2] = 0; data[3] = 0; minioninfo->chip_status[chip].tempsent = choice; reply = build_cmd(minioncgpu, minioninfo, chip, WRITE_ADDR(MINION_SYS_TEMP_CTL), rbuf, 0, data); } static void enable_chip_cores(struct cgpu_info *minioncgpu, struct minion_info *minioninfo, int chip) { uint8_t rbuf[MINION_BUFSIZ]; uint8_t data[4]; __maybe_unused int reply; int rep, i; for (i = 0; i < 4; i++) data[i] = minioninfo->init_cores[chip][i]; reply = build_cmd(minioncgpu, minioninfo, chip, WRITE_ADDR(MINION_CORE_ENA0_31), rbuf, 0, data); for (i = 0; i < 4; i++) data[i] = minioninfo->init_cores[chip][i+4]; reply = build_cmd(minioncgpu, minioninfo, chip, WRITE_ADDR(MINION_CORE_ENA32_63), rbuf, 0, data); for (i = 0; i < 4; i++) data[i] = minioninfo->init_cores[chip][i+8]; reply = build_cmd(minioncgpu, minioninfo, chip, WRITE_ADDR(MINION_CORE_ENA64_95), rbuf, 0, data); for (i = 0; i < 4; i++) data[i] = minioninfo->init_cores[chip][i+12]; reply = build_cmd(minioncgpu, minioninfo, chip, WRITE_ADDR(MINION_CORE_ENA96_98), rbuf, 0, data); /* Below is for testing - disabled/use default // 1/3 range for each of the 3 cores // data[0] = 0x55; // data[1] = 0x55; // data[2] = 0x55; // data[3] = 0x55; // quicker replies // data[0] = 0x05; // data[1] = 0x05; // data[2] = 0x05; // data[3] = 0x05; // 0x00000100 at 20MH/s per core = 336TH/s if 1 nonce per work item // 0x00001000 = 21.0TH/s - so well above 2TH/s // 0x00002000 = 10.5TH/s - above 2TH/s // speed test data[0] = 0x00; data[1] = 0x01; data[2] = 0x00; data[3] = 0x00; // data[3] = 0x20; // slow it down for other testing // 2 cores // data[0] = 0xff; // data[1] = 0xff; // data[2] = 0xff; // data[3] = 0x7f; reply = build_cmd(minioncgpu, minioninfo, chip, WRITE_ADDR(MINION_NONCE_RANGE), rbuf, 0, data); // find lots more nonces in a short time on my test data // i.e. emulate a MUCH higher hash rate on SPI and work // generation/testing // Current test data (same repeated 10 times) has nonce 0x05e0ed6d data[0] = 0x00; data[1] = 0xed; data[2] = 0xe0; data[3] = 0x05; reply = build_cmd(minioncgpu, minioninfo, chip, WRITE_ADDR(MINION_NONCE_START), rbuf, 0, data); */ // store the core ena state for (rep = 0; rep < MINION_CORE_REPS; rep++) { data[0] = 0x0; data[1] = 0x0; data[2] = 0x0; data[3] = 0x0; reply = build_cmd(minioncgpu, minioninfo, chip, READ_ADDR(MINION_CORE_ENA0_31 + rep), rbuf, MINION_CORE_SIZ, data); minioninfo->chip_core_ena[rep][chip] = *((uint32_t *)&(rbuf[HSIZE()])); } // store the core active state for (rep = 0; rep < MINION_CORE_REPS; rep++) { data[0] = 0x0; data[1] = 0x0; data[2] = 0x0; data[3] = 0x0; reply = build_cmd(minioncgpu, minioninfo, chip, READ_ADDR(MINION_CORE_ACT0_31 + rep), rbuf, MINION_CORE_SIZ, data); minioninfo->chip_core_act[rep][chip] = *((uint32_t *)&(rbuf[HSIZE()])); } } #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; reply = build_cmd(minioncgpu, minioninfo, chip, WRITE_ADDR(MINION_SYS_BUF_TRIG), rbuf, 0, data); // data[0] = MINION_QUE_MAX; // spaces available ... i.e. empty // data[0] = MINION_QUE_LOW; // spaces in use data[0] = MINION_QUE_MAX - MINION_QUE_LOW; // spaces available data[1] = 0x00; data[2] = 0x00; data[3] = 0x00; reply = build_cmd(minioncgpu, minioninfo, chip, WRITE_ADDR(MINION_SYS_QUE_TRIG), rbuf, 0, data); // data[0] = MINION_RESULT_INT; data[0] = MINION_RESULT_INT | MINION_CMD_INT; data[1] = 0x00; data[2] = 0x00; data[3] = 0x00; reply = build_cmd(minioncgpu, minioninfo, chip, WRITE_ADDR(MINION_SYS_INT_ENA), rbuf, 0, data); } #endif // Simple detect - just check each chip for the signature static void minion_detect_chips(struct cgpu_info *minioncgpu, struct minion_info *minioninfo) { struct minion_header *head; uint8_t wbuf[MINION_BUFSIZ]; uint8_t rbuf[MINION_BUFSIZ]; uint32_t wsiz, rsiz; int chip, reply, tries; bool ok; head = (struct minion_header *)wbuf; rsiz = MINION_SYS_SIZ; SET_HEAD_READ(head, MINION_SYS_CHIP_SIG); SET_HEAD_SIZ(head, rsiz); wsiz = HSIZE() + rsiz; for (chip = 0; chip < MINION_CHIPS; chip++) { head->chip = (uint8_t)chip; tries = 0; ok = false; do { reply = do_ioctl(wbuf, wsiz, rbuf, rsiz); if (reply == (int)(wsiz)) { uint32_t sig = u8tou32(rbuf, wsiz - rsiz); if (sig == MINION_CHIP_SIG) { minioninfo->chip[chip] = true; minioninfo->chips++; 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: chip %d detect offset got" " 0x%08x wanted 0x%08x", minioncgpu->drv->dname, chip, sig, MINION_CHIP_SIG); } else { if (sig == MINION_NOCHIP_SIG || sig == MINION_NOCHIP_SIG2) // Assume no chip ok = true; else { applog(LOG_ERR, "%s: chip %d detect failed got" " 0x%08x wanted 0x%08x", minioncgpu->drv->dname, chip, sig, MINION_CHIP_SIG); } } } } else { applog(LOG_ERR, "%s: chip %d reply %d ignored should be %d", minioncgpu->drv->dname, chip, reply, (int)(wsiz)); } } while (!ok && ++tries <= MINION_SIG_TRIES); if (!ok) { applog(LOG_ERR, "%s: chip %d - detect failure status", minioncgpu->drv->dname, chip); } } if (minioninfo->chips) { for (chip = 0; chip < MINION_CHIPS; chip++) { if (minioninfo->chip[chip]) { 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->chip[chip]) enable_interrupt(minioncgpu, minioninfo, chip); #endif } } static const char *minion_modules[] = { #if MINION_ROCKCHIP == 0 "i2c-dev", "i2c-bcm2708", "spidev", "spi-bcm2708", #endif NULL }; 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 } }; static bool minion_init_spi(struct cgpu_info *minioncgpu, struct minion_info *minioninfo, int bus, int chip) { int i, err, data; char buf[64]; for (i = 0; minion_modules[i]; i++) { snprintf(buf, sizeof(buf), "modprobe %s", minion_modules[i]); err = system(buf); if (err) { applog(LOG_ERR, "%s: failed to modprobe %s (%d) - you need to be root?", minioncgpu->drv->dname, minion_modules[i], err); goto bad_out; } } snprintf(buf, sizeof(buf), "/dev/spidev%d.%d", bus, chip); minioninfo->spifd = open(buf, O_RDWR); if (minioninfo->spifd < 0) { applog(LOG_ERR, "%s: failed to open spidev (%d)", minioncgpu->drv->dname, errno); goto bad_out; } minioncgpu->device_path = strdup(buf); for (i = 0; minion_ioc[i].value != -1; i++) { data = minion_ioc[i].value; err = ioctl(minioninfo->spifd, minion_ioc[i].request, (void *)&data); if (err < 0) { applog(LOG_ERR, "%s: failed ioctl configuration (%d) (%d)", minioncgpu->drv->dname, i, errno); goto close_out; } } return true; close_out: close(minioninfo->spifd); minioninfo->spifd = 0; free(minioncgpu->device_path); minioncgpu->device_path = NULL; bad_out: return false; } #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; snprintf(pindir, sizeof(pindir), MINION_GPIO_SYS MINION_GPIO_PIN, MINION_GPIO_RESULT_INT_PIN); memset(&st, 0, sizeof(st)); if (stat(pindir, &st) == 0) { // already exists if (!S_ISDIR(st.st_mode)) { applog(LOG_ERR, "%s: failed1 to enable GPIO pin %d interrupt" " - not a directory", minioncgpu->drv->dname, MINION_GPIO_RESULT_INT_PIN); return false; } } else { snprintf(ena, sizeof(ena), MINION_GPIO_SYS MINION_GPIO_ENA); file = open(ena, O_WRONLY | O_SYNC); if (file == -1) { applog(LOG_ERR, "%s: failed2 to enable GPIO pin %d interrupt (%d)" " - you need to be root?", minioncgpu->drv->dname, MINION_GPIO_RESULT_INT_PIN, errno); return false; } snprintf(pin, sizeof(pin), MINION_GPIO_ENA_VAL, MINION_GPIO_RESULT_INT_PIN); ret = write(file, pin, (size_t)strlen(pin)); if (ret != (ssize_t)strlen(pin)) { if (ret < 0) err = errno; else err = (int)ret; close(file); applog(LOG_ERR, "%s: failed3 to enable GPIO pin %d interrupt (%d:%d)", minioncgpu->drv->dname, MINION_GPIO_RESULT_INT_PIN, err, (int)strlen(pin)); return false; } close(file); // Check again if it exists memset(&st, 0, sizeof(st)); if (stat(pindir, &st) != 0) { applog(LOG_ERR, "%s: failed4 to enable GPIO pin %d interrupt (%d)", minioncgpu->drv->dname, MINION_GPIO_RESULT_INT_PIN, errno); return false; } } // Set the pin attributes // Direction snprintf(dir, sizeof(dir), MINION_GPIO_SYS MINION_GPIO_PIN MINION_GPIO_DIR, MINION_GPIO_RESULT_INT_PIN); file = open(dir, O_WRONLY | O_SYNC); if (file == -1) { applog(LOG_ERR, "%s: failed5 to enable GPIO pin %d interrupt (%d)" " - you need to be root?", minioncgpu->drv->dname, MINION_GPIO_RESULT_INT_PIN, errno); return false; } ret = write(file, MINION_GPIO_DIR_READ, (size_t)strlen(MINION_GPIO_DIR_READ)); if (ret != (ssize_t)strlen(MINION_GPIO_DIR_READ)) { if (ret < 0) err = errno; else err = (int)ret; close(file); applog(LOG_ERR, "%s: failed6 to enable GPIO pin %d interrupt (%d:%d)", minioncgpu->drv->dname, MINION_GPIO_RESULT_INT_PIN, err, (int)strlen(MINION_GPIO_DIR_READ)); return false; } close(file); // 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); // Setup fd access to Value snprintf(minioninfo->gpiointvalue, sizeof(minioninfo->gpiointvalue), MINION_GPIO_SYS MINION_GPIO_PIN MINION_GPIO_VALUE, MINION_GPIO_RESULT_INT_PIN); minioninfo->gpiointfd = open(minioninfo->gpiointvalue, O_RDONLY); if (minioninfo->gpiointfd == -1) { applog(LOG_ERR, "%s: failed11 to enable GPIO pin %d interrupt (%d)", minioncgpu->drv->dname, MINION_GPIO_RESULT_INT_PIN, errno); return false; } return true; } #endif // Default meaning all cores static void default_all_cores(uint8_t *cores) { int i; // clear all bits for (i = 0; i < (int)(DATA_SIZ * MINION_CORE_REPS); i++) cores[i] = 0x00; // enable (only) all cores for (i = 0; i < MINION_CORES; i++) ENABLE_CORE(cores, i); } static void minion_process_options(struct minion_info *minioninfo) { int last_freq, last_temp; char *freq, *temp, *core, *comma, *buf, *plus, *minus; uint8_t last_cores[DATA_SIZ*MINION_CORE_REPS]; int i, core1, core2; bool cleared; 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'; for (i = 0; i < MINION_CHIPS; i++) { if (freq && isdigit(*freq)) { last_freq = (int)(round((double)atoi(freq) / (double)MINION_FREQ_FACTOR)) * MINION_FREQ_FACTOR; if (last_freq < MINION_FREQ_MIN) last_freq = MINION_FREQ_MIN; if (last_freq > MINION_FREQ_MAX) last_freq = MINION_FREQ_MAX; 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'; for (i = 0; i < MINION_CHIPS; i++) { if (temp) { if (isdigit(*temp)) { last_temp = atoi(temp); last_temp -= (last_temp % MINION_TEMP_CTL_STEP); if (last_temp < MINION_TEMP_CTL_MIN_VALUE) last_temp = MINION_TEMP_CTL_MIN_VALUE; if (last_temp > MINION_TEMP_CTL_MAX_VALUE) last_temp = MINION_TEMP_CTL_MAX_VALUE; } else { if (strcasecmp(temp, MINION_TEMP_DISABLE) == 0) last_temp = MINION_TEMP_CTL_DISABLE; } 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'; for (i = 0; i < MINION_CHIPS; i++) { // default to previous until we find valid data cleared = false; if (core) { plus = strchr(core, '+'); if (plus) *(plus++) = '\0'; while (core) { minus = strchr(core, '-'); if (minus) *(minus++) = '\0'; if (isdigit(*core)) { core1 = atoi(core); if (core1 >= 0 && core1 < MINION_CORES) { if (!minus) { if (!cleared) { memset(last_cores, 0, sizeof(last_cores)); cleared = true; } ENABLE_CORE(last_cores, core1); } else { core2 = atoi(minus); if (core2 >= core1) { if (core2 >= MINION_CORES) core2 = MINION_CORES - 1; while (core1 <= core2) { if (!cleared) { memset(last_cores, 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); } } static void minion_detect(bool hotplug) { struct cgpu_info *minioncgpu = NULL; struct minion_info *minioninfo = NULL; char buf[512]; size_t off; int i; if (hotplug) return; minioncgpu = calloc(1, sizeof(*minioncgpu)); if (unlikely(!minioncgpu)) quithere(1, "Failed to calloc minioncgpu"); minioncgpu->drv = &minion_drv; minioncgpu->deven = DEV_ENABLED; minioncgpu->threads = 1; minioninfo = calloc(1, sizeof(*minioninfo)); // everything '0' if (unlikely(!minioninfo)) quithere(1, "Failed to calloc minioninfo"); minioncgpu->device_data = (void *)minioninfo; if (!minion_init_spi(minioncgpu, minioninfo, MINION_SPI_BUS, MINION_SPI_CHIP)) goto unalloc; #if ENABLE_INT_NONO if (!minion_init_gpio_interrupt(minioncgpu, minioninfo)) goto unalloc; #endif mutex_init(&(minioninfo->spi_lock)); mutex_init(&(minioninfo->sta_lock)); for (i = 0; i < MINION_CHIPS; i++) { minioninfo->init_freq[i] = MINION_FREQ_DEF; minioninfo->init_temp[i] = MINION_TEMP_CTL_DEF; default_all_cores(&(minioninfo->init_cores[i][0])); } minion_process_options(minioninfo); applog(LOG_WARNING, "%s: checking for chips ...", minioncgpu->drv->dname); minion_detect_chips(minioncgpu, minioninfo); buf[0] = '\0'; for (i = 0; i < MINION_CHIPS; i++) { if (minioninfo->chip[i]) { off = strlen(buf); snprintf(buf + off, sizeof(buf) - off, " %d", i); } } applog(LOG_WARNING, "%s: found %d chip%s:%s", minioncgpu->drv->dname, minioninfo->chips, (minioninfo->chips == 1) ? "" : "s", buf); if (minioninfo->chips == 0) goto cleanup; if (!add_cgpu(minioncgpu)) goto cleanup; mutex_init(&(minioninfo->nonce_lock)); minioninfo->wfree_list = k_new_list("Work", sizeof(WITEM), ALLOC_WITEMS, LIMIT_WITEMS, true); minioninfo->wwork_list = k_new_store(minioninfo->wfree_list); // Initialise them all in case we later decide to enable chips for (i = 0; i < MINION_CHIPS; i++) { minioninfo->wque_list[i] = k_new_store(minioninfo->wfree_list); minioninfo->wchip_list[i] = k_new_store(minioninfo->wfree_list); } minioninfo->tfree_list = k_new_list("Task", sizeof(TITEM), ALLOC_TITEMS, LIMIT_TITEMS, true); minioninfo->task_list = k_new_store(minioninfo->tfree_list); minioninfo->treply_list = k_new_store(minioninfo->tfree_list); minioninfo->rfree_list = k_new_list("Reply", sizeof(RITEM), ALLOC_RITEMS, LIMIT_RITEMS, true); minioninfo->rnonce_list = k_new_store(minioninfo->rfree_list); minioninfo->history_gen = MINION_MAX_RESET_CHECK; minioninfo->hfree_list = k_new_list("History", sizeof(HITEM), ALLOC_HITEMS, LIMIT_HITEMS, true); for (i = 0; i < 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; return; cleanup: close(minioninfo->gpiointfd); close(minioninfo->spifd); mutex_destroy(&(minioninfo->sta_lock)); mutex_destroy(&(minioninfo->spi_lock)); unalloc: free(minioninfo); free(minioncgpu); } static void minion_identify(__maybe_unused struct cgpu_info *minioncgpu) { // flash a led } /* * SPI/ioctl write thread * Non urgent work is to keep the queue full * Urgent work is when an LP occurs (or the queue is empty/low) */ static void *minion_spi_write(void *userdata) { struct cgpu_info *minioncgpu = (struct cgpu_info *)userdata; struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); K_ITEM *item, *tail, *task, *work; TITEM *titem; applog(MINION_LOG, "%s%i: SPI writing...", minioncgpu->drv->name, minioncgpu->device_id); // Wait until we're ready while (minioncgpu->shutdown == false) { if (minioninfo->initialised) { break; } cgsleep_ms(1); // asap to start mining } // TODO: combine all urgent into a single I/O? // Then combine all state 1 for the same chip into a single I/O ? // (then again for state 2?) while (minioncgpu->shutdown == false) { item = NULL; K_WLOCK(minioninfo->task_list); tail = minioninfo->task_list->tail; if (tail) { // Find first urgent item item = tail; while (item && !(DATAT(item)->urgent)) item = item->prev; // No urgent items, just do the tail if (!item) item = tail; k_unlink_item(minioninfo->task_list, item); } K_WUNLOCK(minioninfo->task_list); if (item) { bool do_txrx = true; bool store_reply = true; titem = DATAT(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_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) { minion_txrx(titem); int chip = titem->chip; switch (titem->address) { case READ_ADDR(MINION_SYS_CHIP_STA): if (titem->reply >= (int)(titem->osiz)) { uint8_t *rep = &(titem->rbuf[titem->osiz - titem->rsiz]); mutex_lock(&(minioninfo->sta_lock)); minioninfo->chip_status[chip].temp = STA_TEMP(rep); minioninfo->chip_status[chip].cores = STA_CORES(rep); minioninfo->chip_status[chip].freq = STA_FREQ(rep); mutex_unlock(&(minioninfo->sta_lock)); 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); DATAT(task)->tid = ++(minioninfo->next_tid); DATAT(task)->chip = chip; DATAT(task)->write = true; DATAT(task)->address = MINION_SYS_RSTN_CTL; DATAT(task)->task_id = 0; // ignored DATAT(task)->wsiz = MINION_SYS_SIZ; DATAT(task)->rsiz = 0; DATAT(task)->wbuf[0] = SYS_RSTN_CTL_FLUSH; DATAT(task)->wbuf[1] = 0; DATAT(task)->wbuf[2] = 0; DATAT(task)->wbuf[3] = 0; DATAT(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) { K_WLOCK(minioninfo->wwork_list); work = minioninfo->wchip_list[chip]->head; while (work) { DATAW(work)->stale = true; minioninfo->chip_status[chip].chipwork--; if (minioninfo->chip_status[chip].realwork > 0) minioninfo->chip_status[chip].realwork--; work = work->next; } minioninfo->chip_status[chip].chipwork = 0; minioninfo->chip_status[chip].realwork = 0; 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); minioninfo->chip_status[chip].quework--; minioninfo->chip_status[chip].chipwork++; minioninfo->chip_status[chip].realwork++; K_WUNLOCK(minioninfo->wchip_list[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): 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; TITEM fifo_task, res1_task, res2_task; int chip, resoff; int chipwork, gap; bool somelow; struct timeval now; #if ENABLE_INT_NONO TITEM 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); // Wait until we're ready while (minioncgpu->shutdown == false) { if (minioninfo->initialised) { break; } cgsleep_ms(2); } 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; head = (struct minion_header *)wbuf; SET_HEAD_SIZ(head, MINION_SYS_SIZ); wsiz = HSIZE() + MINION_SYS_SIZ; rsiz = MINION_SYS_SIZ; // for READ, use 0 for WRITE #endif somelow = false; while (minioncgpu->shutdown == false) { for (chip = 0; chip < MINION_CHIPS; chip++) { if (minioninfo->chip[chip]) { int tries = 0; uint8_t res, cmd; while (++tries < 4) { res = cmd = 0; fifo_task.chip = chip; fifo_task.reply = 0; minion_txrx(&fifo_task); if (fifo_task.reply <= 0) break; else { 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: Bad fifo reply (%s) size %d, should be %d", minioncgpu->drv->name, minioncgpu->device_id, 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: Unexpected fifo reply size %d, expected only %d", minioncgpu->drv->name, minioncgpu->device_id, fifo_task.reply, (int)(fifo_task.osiz)); } res = FIFO_RES(fifo_task.rbuf, fifo_task.osiz - fifo_task.rsiz); cmd = FIFO_CMD(fifo_task.rbuf, fifo_task.osiz - fifo_task.rsiz); // valid reply? if (res <= MINION_QUE_MAX && cmd <= MINION_QUE_HIGH) break; applog(LOG_ERR, "%s%i: Bad fifo reply res %d (max is %d) cmd %d (max is %d)", minioncgpu->drv->name, minioncgpu->device_id, (int)res, MINION_QUE_MAX, (int)cmd, MINION_QUE_HIGH); minioninfo->spi_errors++; minioninfo->fifo_spi_errors[chip]++; minioninfo->res_err_count[chip]++; } } } // Give up on this chip this round if (tries >= 4) continue; K_WLOCK(minioninfo->wwork_list); // it shouldn't go up if (cmd < minioninfo->chip_status[chip].realwork) minioninfo->chip_status[chip].realwork = (uint32_t)cmd; else { cmd = (uint8_t)(minioninfo->chip_status[chip].realwork); minioninfo->spi_errors++; minioninfo->fifo_spi_errors[chip]++; minioninfo->res_err_count[chip]++; } chipwork = (int)(minioninfo->chip_status[chip].chipwork); K_WUNLOCK(minioninfo->wwork_list); gap = chipwork - (int)cmd; if (gap < -1 || gap > 1) { // applog(LOG_ERR, "%s%i: fifo cmd difference > 1 for chip %d - work %d cmd %d gap %d", // minioncgpu->drv->name, minioncgpu->device_id, // chip, chipwork, (int)cmd, gap); } if (cmd < MINION_QUE_LOW) { somelow = true; // Flag it in case the count is wrong K_WLOCK(minioninfo->wwork_list); minioninfo->chip_status[chip].islow = true; minioninfo->chip_status[chip].lowcount = (int)cmd; 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: Bad work reply (%s) size %d, should be at least %d", minioncgpu->drv->name, minioncgpu->device_id, 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: Unexpected work reply size %d, expected %d", minioncgpu->drv->name, minioncgpu->device_id, 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]++; } } for (resoff = res1_task.osiz - res1_task.rsiz; resoff < (int)res1_task.osiz; resoff += MINION_RES_DATA_SIZ) { result1 = (struct minion_result *)&(res1_task.rbuf[resoff]); if (minreread && resoff < (int)res2_task.osiz) result2 = (struct minion_result *)&(res2_task.rbuf[resoff]); else result2 = NULL; 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]++; } //DATAR(item)->chip = RES_CHIP(use1); // We can avoid any SPI transmission error of the chip number DATAR(item)->chip = (uint8_t)chip; if ((uint8_t)chip != RES_CHIP(use1)) { minioninfo->spi_errors++; minioninfo->res_spi_errors[chip]++; minioninfo->res_err_count[chip]++; } if (use2 && (uint8_t)chip != RES_CHIP(use2)) { minioninfo->spi_errors++; minioninfo->res_spi_errors[chip]++; minioninfo->res_err_count[chip]++; } DATAR(item)->core = RES_CORE(use1); DATAR(item)->task_id = RES_TASK(use1); DATAR(item)->nonce = RES_NONCE(use1); DATAR(item)->no_nonce = !RES_GOLD(use1); memcpy(&(DATAR(item)->when), &now, sizeof(now)); if (!use2) DATAR(item)->another = false; else { DATAR(item)->another = true; DATAR(item)->task_id2 = RES_TASK(use2); DATAR(item)->nonce2 = RES_NONCE(use2); } //applog(MINTASK_LOG, "%s%i: ZZZ reply task_id 0x%04x - chip %d - gold %d", minioncgpu->drv->name, minioncgpu->device_id, RES_TASK(use1), (int)RES_CHIP(use1), (int)RES_GOLD(use1)); //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, DATAR(item)->chip, DATAR(item)->core, DATAR(item)->task_id, DATAR(item)->nonce, (int)RES_GOLD(use1)); K_WLOCK(minioninfo->rnonce_list); k_add_head(minioninfo->rnonce_list, item); K_WUNLOCK(minioninfo->rnonce_list); 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++; read(minioninfo->gpiointfd, &c, 1); // applog(LOG_ERR, "%s%i: Interrupt2", // minioncgpu->drv->name, // minioncgpu->device_id); gotres = false; for (chip = 0; chip < MINION_CHIPS; chip++) { if (minioninfo->chip[chip]) { SET_HEAD_READ(head, MINION_SYS_INT_STA); head->chip = chip; reply = do_ioctl(wbuf, wsiz, rbuf, rsiz); 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); if ((rbuf[wsiz - rsiz] & MINION_RESULT_INT) != 0) { gotres = true; (minioninfo->result_interrupts)++; // applog(LOG_ERR, "%s%i: chip %d got RES interrupt", // minioncgpu->drv->name, // minioncgpu->device_id, // chip); } if ((rbuf[wsiz - rsiz] & MINION_CMD_INT) != 0) { // Work queue is empty (minioninfo->command_interrupts)++; // applog(LOG_ERR, "%s%i: chip %d got CMD interrupt", // minioncgpu->drv->name, // minioncgpu->device_id, // chip); } // 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); // Don't clear either interrupt until after send/recv } } // Doing this last means we can't miss an interrupt if (gotres) cgsem_post(&(minioninfo->scan_work)); } #endif } 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_BAD_NONCE, NONCE_BAD_WORK, NONCE_NO_WORK, NONCE_SPI_ERR }; static void cleanup_older(struct cgpu_info *minioncgpu, int chip, K_ITEM *item, bool no_nonce) { struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); K_ITEM *tail; // bool errs; /* * remove older work items (no_nonce means this 'item' has finished also) */ if (item->next || no_nonce) { K_WLOCK(minioninfo->wchip_list[chip]); tail = minioninfo->wchip_list[chip]->tail; while (tail && tail != item) { k_unlink_item(minioninfo->wchip_list[chip], tail); if (!(DATAW(tail)->stale)) { minioninfo->chip_status[chip].chipwork--; if (minioninfo->chip_status[chip].realwork > 0) minioninfo->chip_status[chip].realwork--; /* // If it had no valid work (only errors) then it won't have been cleaned up errs = (DATAW(tail)->errors > 0); applog(errs ? LOG_DEBUG : LOG_ERR, applog(LOG_ERR, "%s%i: discarded old task 0x%04x chip %d no reply errs=%d", minioncgpu->drv->name, minioncgpu->device_id, DATAW(tail)->task_id, chip, DATAW(tail)->errors); */ } K_WUNLOCK(minioninfo->wchip_list[chip]); applog(MINION_LOG, "%s%i: marking complete - old task 0x%04x chip %d", minioncgpu->drv->name, minioncgpu->device_id, DATAW(tail)->task_id, chip); if (DATAW(tail)->rolled) free_work(DATAW(tail)->work); else work_completed(minioncgpu, DATAW(tail)->work); K_WLOCK(minioninfo->wchip_list[chip]); k_free_head(minioninfo->wfree_list, tail); tail = minioninfo->wchip_list[chip]->tail; } if (no_nonce) { k_unlink_item(minioninfo->wchip_list[chip], item); if (!(DATAW(item)->stale)) { minioninfo->chip_status[chip].chipwork--; if (minioninfo->chip_status[chip].realwork > 0) minioninfo->chip_status[chip].realwork--; } K_WUNLOCK(minioninfo->wchip_list[chip]); applog(MINION_LOG, "%s%i: marking complete - no_nonce task 0x%04x chip %d", minioncgpu->drv->name, minioncgpu->device_id, DATAW(item)->task_id, chip); if (DATAW(item)->rolled) free_work(DATAW(item)->work); else work_completed(minioncgpu, DATAW(item)->work); K_WLOCK(minioninfo->wchip_list[chip]); k_free_head(minioninfo->wfree_list, item); } K_WUNLOCK(minioninfo->wchip_list[chip]); } } static enum nonce_state oknonce(struct thr_info *thr, struct cgpu_info *minioncgpu, int chip, int core, uint32_t task_id, uint32_t nonce, bool no_nonce, struct timeval *when, bool another, uint32_t task_id2, uint32_t nonce2) { struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); struct timeval now; K_ITEM *item, *tail; uint32_t min_task_id, max_task_id; bool redo; // if the chip has been disabled - but we don't do that - so not possible (yet) if (!(minioninfo->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 (core < 0 || core >= MINION_CORES) { minioninfo->spi_errors++; minioninfo->res_spi_errors[chip]++; minioninfo->res_err_count[chip]++; applog(MINTASK_LOG, "%s%i: SPI nonce error invalid core %d (chip %d)", minioncgpu->drv->name, minioncgpu->device_id, core, chip); // use the fake core number so we don't discard the result core = FAKE_CORE; } if (no_nonce) minioninfo->chip_nononces[chip]++; else minioninfo->chip_nonces[chip]++; redo = false; retry: K_RLOCK(minioninfo->wchip_list[chip]); item = minioninfo->wchip_list[chip]->tail; if (!item) { K_RUNLOCK(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 = DATAW(item)->task_id; while (item) { if (DATAW(item)->task_id == task_id) break; item = item->prev; } max_task_id = DATAW(minioninfo->wchip_list[chip]->head)->task_id; K_RUNLOCK(minioninfo->wchip_list[chip]); if (!item) { 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]++; if (no_nonce) { cleanup_older(minioncgpu, chip, item, no_nonce); return NONCE_NO_NONCE; } minioninfo->tested_nonces++; redo = false; retest: if (test_nonce(DATAW(item)->work, 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, DATAW(item)->work); if (redo) minioninfo->nonces_recovered[chip]++; minioninfo->chip_good[chip]++; minioninfo->core_good[chip][core]++; DATAW(item)->nonces++; mutex_lock(&(minioninfo->nonce_lock)); minioninfo->new_nonces++; mutex_unlock(&(minioninfo->nonce_lock)); minioninfo->ok_nonces++; cleanup_older(minioncgpu, chip, item, no_nonce); int chip_tmp; cgtime(&now); K_WLOCK(minioninfo->hfree_list); item = k_unlink_head(minioninfo->hfree_list); memcpy(&(DATAH(item)->when), when, sizeof(*when)); k_add_head(minioninfo->hchip_list[chip], item); for (chip_tmp = 0; chip_tmp < MINION_CHIPS; chip_tmp++) { tail = minioninfo->hchip_list[chip_tmp]->tail; while (tail && tdiff(&(DATAH(tail)->when), &now) > MINION_HISTORY_s) { tail = k_unlink_tail(minioninfo->hchip_list[chip_tmp]); k_add_head(minioninfo->hfree_list, item); tail = minioninfo->hchip_list[chip_tmp]->tail; } } K_WUNLOCK(minioninfo->hfree_list); return NONCE_GOOD_NONCE; } if (another && nonce != nonce2) { minioninfo->nonces_failed[chip]++; nonce = nonce2; redo = true; goto retest; } DATAW(item)->errors++; 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; } // Results checking thread static void *minion_results(void *userdata) { struct cgpu_info *minioncgpu = (struct cgpu_info *)userdata; struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); struct thr_info *thr = minioncgpu->thr[0]; int chip, core; uint32_t task_id; uint32_t nonce; bool no_nonce; struct timeval when; bool another; uint32_t task_id2; uint32_t nonce2; applog(MINION_LOG, "%s%i: Results...", minioncgpu->drv->name, minioncgpu->device_id); // Wait until we're ready while (minioncgpu->shutdown == false) { if (minioninfo->initialised) { break; } cgsleep_ms(3); } while (minioncgpu->shutdown == false) { if (!oldest_nonce(minioncgpu, &chip, &core, &task_id, &nonce, &no_nonce, &when, &another, &task_id2, &nonce2)) { cgsem_mswait(&(minioninfo->nonce_ready), MINION_NONCE_mS); continue; } oknonce(thr, minioncgpu, chip, core, task_id, nonce, no_nonce, &when, another, task_id2, nonce2); } return NULL; } static void minion_flush_work(struct cgpu_info *minioncgpu) { struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); K_ITEM *stale_unused_work, *prev_unused, *task, *prev_task, *witem; int i; applog(MINION_LOG, "%s%i: flushing work", minioncgpu->drv->name, minioncgpu->device_id); // TODO: N.B. scanwork also gets work locks - which master thread calls flush? K_WLOCK(minioninfo->wwork_list); // Simply remove the whole unused wwork_list stale_unused_work = minioninfo->wwork_list->tail; if (stale_unused_work) { minioninfo->wwork_list->head = NULL; minioninfo->wwork_list->tail = NULL; minioninfo->wwork_list->count = 0; } // 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 // No deadlock since this is the only code to get 2 locks K_WLOCK(minioninfo->tfree_list); task = minioninfo->task_list->tail; while (task) { prev_task = task->prev; if (DATAT(task)->address == WRITE_ADDR(MINION_QUE_0)) { minioninfo->chip_status[DATAT(task)->chip].quework--; witem = DATAT(task)->witem; k_unlink_item(minioninfo->wque_list[DATAT(task)->chip], witem); k_free_head(minioninfo->wfree_list, witem); k_unlink_item(minioninfo->task_list, task); k_free_head(minioninfo->tfree_list, task); } task = prev_task; } for (i = 0; i < MINION_CHIPS; i++) { if (minioninfo->chip[i]) { // TODO: consider sending it now rather than adding to the task list? task = k_unlink_head(minioninfo->tfree_list); DATAT(task)->tid = ++(minioninfo->next_tid); DATAT(task)->chip = i; DATAT(task)->write = true; DATAT(task)->address = MINION_SYS_RSTN_CTL; DATAT(task)->task_id = 0; // ignored DATAT(task)->wsiz = MINION_SYS_SIZ; DATAT(task)->rsiz = 0; DATAT(task)->wbuf[0] = SYS_RSTN_CTL_FLUSH; DATAT(task)->wbuf[1] = 0; DATAT(task)->wbuf[2] = 0; DATAT(task)->wbuf[3] = 0; DATAT(task)->urgent = true; k_add_head(minioninfo->task_list, task); } } K_WUNLOCK(minioninfo->tfree_list); K_WUNLOCK(minioninfo->wwork_list); // TODO: send a signal to force getting and sending new work - needs cgsem_wait in the sending thread // TODO: should we use this thread to do the following work? if (stale_unused_work) { // mark complete all stale unused work (oldest first) prev_unused = stale_unused_work; while (prev_unused) { if (DATAW(prev_unused)->rolled) free_work(DATAW(prev_unused)->work); else work_completed(minioncgpu, DATAW(prev_unused)->work); prev_unused = prev_unused->prev; } // put the items back in the wfree_list (oldest first) K_WLOCK(minioninfo->wfree_list); while (stale_unused_work) { prev_unused = stale_unused_work->prev; k_free_head(minioninfo->wfree_list, stale_unused_work); stale_unused_work = prev_unused; } K_WUNLOCK(minioninfo->wfree_list); } } static void sys_chip_sta(struct cgpu_info *minioncgpu, int chip) { struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); struct timeval now; K_ITEM *item; int limit, rep; 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); DATAT(item)->tid = ++(minioninfo->next_tid); K_WUNLOCK(minioninfo->tfree_list); DATAT(item)->chip = chip; DATAT(item)->write = false; DATAT(item)->address = READ_ADDR(MINION_SYS_CHIP_STA); DATAT(item)->task_id = 0; DATAT(item)->wsiz = 0; DATAT(item)->rsiz = MINION_SYS_SIZ; DATAT(item)->urgent = false; K_WLOCK(minioninfo->task_list); k_add_head(minioninfo->task_list, item); item = k_unlink_head(minioninfo->tfree_list); DATAT(item)->tid = ++(minioninfo->next_tid); K_WUNLOCK(minioninfo->task_list); DATAT(item)->chip = chip; DATAT(item)->write = false; DATAT(item)->address = READ_ADDR(MINION_SYS_IDLE_CNT); DATAT(item)->task_id = 0; DATAT(item)->wsiz = 0; DATAT(item)->rsiz = MINION_SYS_SIZ; DATAT(item)->urgent = false; K_WLOCK(minioninfo->task_list); k_add_head(minioninfo->task_list, item); K_WUNLOCK(minioninfo->task_list); // Get the core ena and act state for (rep = 0; rep < MINION_CORE_REPS; rep++) { // Ena K_WLOCK(minioninfo->tfree_list); item = k_unlink_head(minioninfo->tfree_list); DATAT(item)->tid = ++(minioninfo->next_tid); K_WUNLOCK(minioninfo->tfree_list); DATAT(item)->chip = chip; DATAT(item)->write = false; DATAT(item)->address = READ_ADDR(MINION_CORE_ENA0_31 + rep); DATAT(item)->task_id = 0; DATAT(item)->wsiz = 0; DATAT(item)->rsiz = MINION_SYS_SIZ; DATAT(item)->urgent = false; K_WLOCK(minioninfo->task_list); k_add_head(minioninfo->task_list, item); // Act item = k_unlink_head(minioninfo->tfree_list); DATAT(item)->tid = ++(minioninfo->next_tid); K_WUNLOCK(minioninfo->task_list); DATAT(item)->chip = chip; DATAT(item)->write = false; DATAT(item)->address = READ_ADDR(MINION_CORE_ACT0_31 + rep); DATAT(item)->task_id = 0; DATAT(item)->wsiz = 0; DATAT(item)->rsiz = MINION_SYS_SIZ; DATAT(item)->urgent = false; K_WLOCK(minioninfo->task_list); k_add_head(minioninfo->task_list, item); K_WUNLOCK(minioninfo->task_list); } } } } static void new_work_task(struct cgpu_info *minioncgpu, K_ITEM *witem, int chip, bool urgent, uint8_t state) { struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); struct minion_que *que; K_ITEM *item; K_WLOCK(minioninfo->tfree_list); item = k_unlink_head(minioninfo->tfree_list); DATAT(item)->tid = ++(minioninfo->next_tid); K_WUNLOCK(minioninfo->tfree_list); DATAT(item)->chip = chip; DATAT(item)->write = true; DATAT(item)->address = MINION_QUE_0; // if threaded access to new_work_task() is added, this will need locking // Don't use task_id 0 so that we can ignore all '0' work replies // ... and report them as errors if (minioninfo->next_task_id == 0) minioninfo->next_task_id = 1; DATAT(item)->task_id = minioninfo->next_task_id; DATAW(witem)->task_id = minioninfo->next_task_id; minioninfo->next_task_id = (minioninfo->next_task_id + 1) & MINION_MAX_TASK_ID; DATAT(item)->urgent = urgent; DATAT(item)->work_state = state; DATAT(item)->work = DATAW(witem)->work; DATAT(item)->witem = witem; que = (struct minion_que *)&(DATAT(item)->wbuf[0]); que->task_id[0] = DATAT(item)->task_id & 0xff; que->task_id[1] = (DATAT(item)->task_id & 0xff00) >> 8; memcpy(&(que->midstate[0]), &(DATAW(witem)->work->midstate[0]), MIDSTATE_BYTES); memcpy(&(que->merkle7[0]), &(DATAW(witem)->work->data[MERKLE7_OFFSET]), MERKLE_BYTES); DATAT(item)->wsiz = (int)sizeof(*que); DATAT(item)->rsiz = 0; K_WLOCK(minioninfo->wque_list[chip]); k_add_head(minioninfo->wque_list[chip], witem); minioninfo->chip_status[chip].quework++; 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); } // TODO: stale work ... static K_ITEM *next_work(struct minion_info *minioninfo) { K_ITEM *item; K_WLOCK(minioninfo->wwork_list); item = k_unlink_tail(minioninfo->wwork_list); K_WUNLOCK(minioninfo->wwork_list); return item; } static void minion_do_work(struct cgpu_info *minioncgpu) { struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); int count, chip, j, lowcount; uint8_t state; K_ITEM *item; #if ENABLE_INT_NONO K_ITEM *task; #endif bool islow, sentwork; // TODO: (remove this) Fake starved of work to test CMD Interrupt // if (total_secs > 120) { // cgsleep_ms(888); // return; // } /* * 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 < MINION_CHIPS; chip++) minioninfo->chip_status[chip].tohigh = false; for (chip = 0; chip < MINION_CHIPS; chip++) { if (minioninfo->chip[chip] && !minioninfo->chip_status[chip].overheat) { 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, DATAW(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, DATAW(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, DATAW(item)->task_id, chip); } else { applog(LOG_ERR, "%s%i: chip %d non-urgent hi " "empty work list (count=%d)", minioncgpu->drv->name, minioncgpu->device_id, chip, j); } } } break; } } else if (minioninfo->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); DATAT(task)->tid = ++(minioninfo->next_tid); DATAT(task)->chip = 0; // ignored DATAT(task)->write = true; DATAT(task)->address = MINION_SYS_INT_CLR; DATAT(task)->task_id = 0; // ignored DATAT(task)->wsiz = MINION_SYS_SIZ; DATAT(task)->rsiz = 0; DATAT(task)->wbuf[0] = MINION_CMD_INT; DATAT(task)->wbuf[1] = 0; DATAT(task)->wbuf[2] = 0; DATAT(task)->wbuf[3] = 0; DATAT(task)->urgent = false; k_add_head(minioninfo->task_list, task); K_WUNLOCK(minioninfo->tfree_list); } #endif //applog(LOG_ERR, "%s%i: chip %d fin: quew %d chw %d", minioncgpu->drv->name, minioncgpu->device_id, CHP, minioninfo->chip_status[CHP].quework, minioninfo->chip_status[CHP].chipwork); } static bool minion_thread_prepare(struct thr_info *thr) { struct cgpu_info *minioncgpu = thr->cgpu; struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); /* * 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; } static void minion_shutdown(struct thr_info *thr) { struct cgpu_info *minioncgpu = thr->cgpu; struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); int i; applog(MINION_LOG, "%s%i: shutting down", minioncgpu->drv->name, minioncgpu->device_id); for (i = 0; i < MINION_CHIPS; i++) if (minioninfo->chip[i]) // TODO: minion_shutdown(minioncgpu, minioninfo, i); i = i; 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, need, roll, roll_limit; bool ret, rolled; K_RLOCK(minioninfo->wwork_list); count = minioninfo->wwork_list->count; K_RUNLOCK(minioninfo->wwork_list); if (count >= (MINION_QUE_HIGH * minioninfo->chips)) ret = true; else { need = (MINION_QUE_HIGH * minioninfo->chips) - count; work = get_queued(minioncgpu); if (work) { roll_limit = work->drv_rolllimit; roll = 0; do { if (roll == 0) { usework = work; minioninfo->work_unrolled++; rolled = false; } else { usework = copy_work_noffset(work, roll); minioninfo->work_rolled++; rolled = true; } ready_work(minioncgpu, usework, rolled); } while (--need > 0 && ++roll <= roll_limit); } else { // Avoid a hard loop when we can't get work fast enough cgsleep_us(42); } if (need > 0) ret = false; else ret = true; } return ret; } static void idle_report(struct cgpu_info *minioncgpu) { struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); struct timeval now; uint32_t idle; int msdiff; int chip; for (chip = 0; chip < MINION_CHIPS; chip++) { if (minioninfo->chip[chip]) { idle = minioninfo->chip_status[chip].idle; if (idle != minioninfo->chip_status[chip].last_rpt_idle) { cgtime(&now); msdiff = ms_tdiff(&now, &(minioninfo->chip_status[chip].idle_rpt)); if (msdiff >= MINION_IDLE_MESSAGE_ms) { memcpy(&(minioninfo->chip_status[chip].idle_rpt), &now, sizeof(now)); applog(LOG_WARNING, "%s%d: chip %d internal idle changed %08x", minioncgpu->drv->name, minioncgpu->device_id, chip, idle); minioninfo->chip_status[chip].last_rpt_idle = idle; } } } } } static void chip_report(struct cgpu_info *minioncgpu) { struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); struct timeval now; char buf[512]; char res_err_msg[2]; size_t len; double ghs, expect, howlong; int msdiff; int chip; int res_err_count; cgtime(&now); if (!(minioninfo->chip_chk.tv_sec)) { memcpy(&(minioninfo->chip_chk), &now, sizeof(now)); memcpy(&(minioninfo->chip_rpt), &now, sizeof(now)); return; } if (opt_minion_chipreport > 0) { msdiff = ms_tdiff(&now, &(minioninfo->chip_rpt)); if (msdiff >= (opt_minion_chipreport * 1000)) { buf[0] = '\0'; res_err_msg[0] = '\0'; res_err_msg[1] = '\0'; K_RLOCK(minioninfo->hfree_list); for (chip = 0; chip < MINION_CHIPS; chip++) { if (minioninfo->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, &(DATAH(minioninfo->hchip_list[chip]->tail)->when)); } 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", chip, res_err_msg, ghs); minioninfo->history_ghs[chip] = ghs; } } K_RUNLOCK(minioninfo->hfree_list); memcpy(&(minioninfo->chip_chk), &now, sizeof(now)); applogsiz(LOG_WARNING, 512, "%s%d: Chip GHs%s", minioncgpu->drv->name, minioncgpu->device_id, buf); memcpy(&(minioninfo->chip_rpt), &now, sizeof(now)); } } msdiff = ms_tdiff(&now, &(minioninfo->chip_chk)); if (total_secs >= MINION_HISTORY_s && msdiff >= (minioninfo->history_gen * 1000)) { K_RLOCK(minioninfo->hfree_list); for (chip = 0; chip < MINION_CHIPS; chip++) { if (minioninfo->chip[chip]) { 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, &(DATAH(minioninfo->hchip_list[chip]->tail)->when)); } expect = (double)(minioninfo->init_freq[chip]) * MINION_RESET_PERCENT / 1000.0; howlong = tdiff(&now, &(minioninfo->last_reset[chip])); if (ghs <= expect && howlong >= MINION_HISTORY_s) minioninfo->do_reset[chip] = expect; minioninfo->history_ghs[chip] = ghs; } } K_RUNLOCK(minioninfo->hfree_list); for (chip = 0; chip < MINION_CHIPS; chip++) { if (minioninfo->chip[chip]) { if (minioninfo->do_reset[chip] > 1.0) { applog(LOG_WARNING, "%s%d: Chip %d down to threshold %.2fGHs - resetting ...", minioncgpu->drv->name, minioncgpu->device_id, chip, minioninfo->do_reset[chip]); minioninfo->do_reset[chip] = 0.0; memcpy(&(minioninfo->last_reset[chip]), &now, sizeof(now)); init_chip(minioncgpu, minioninfo, chip); } } } memcpy(&(minioninfo->chip_chk), &now, sizeof(now)); } } static int64_t minion_scanwork(__maybe_unused struct thr_info *thr) { struct cgpu_info *minioncgpu = thr->cgpu; struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); int64_t hashcount = 0; 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); // Must always generate data to check/allow for chip reset chip_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; } static const char *temp_str(uint16_t temp) { switch (temp) { case MINION_TEMP_40: return min_temp_40; case MINION_TEMP_60: return min_temp_60; case MINION_TEMP_80: return min_temp_80; case MINION_TEMP_100: return min_temp_100; case MINION_TEMP_OVER: return min_temp_over; } return min_temp_invalid; } static void minion_get_statline_before(char *buf, size_t bufsiz, struct cgpu_info *minioncgpu) { struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); uint16_t max_temp, cores; int chip, core; max_temp = 0; cores = 0; mutex_lock(&(minioninfo->sta_lock)); for (chip = 0; chip < MINION_CHIPS; chip++) { if (minioninfo->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)); tailsprintf(buf, bufsiz, "max%sC Ch:%d Co:%d", temp_str(max_temp), minioninfo->chips, (int)cores); } #define CHIPS_PER_STAT 5 static struct api_data *minion_api_stats(struct cgpu_info *minioncgpu) { struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data); struct api_data *root = NULL; char cores[MINION_CORES+1]; char data[2048]; char buf[32]; int i, to, j; int chip, max_chip, que_work, chip_work, temp; if (minioninfo->initialised == false) return NULL; root = api_add_uint64(root, "OK Nonces", &(minioninfo->ok_nonces), true); root = api_add_uint64(root, "New Nonces", &(minioninfo->new_nonces), true); root = api_add_uint64(root, "Tested Nonces", &(minioninfo->tested_nonces), true); root = api_add_uint64(root, "Untested Nonces", &(minioninfo->untested_nonces), true); root = api_add_int(root, "Chips", &(minioninfo->chips), true); max_chip = 0; for (chip = 0; chip < MINION_CHIPS; chip++) if (minioninfo->chip[chip]) { max_chip = chip; snprintf(buf, sizeof(buf), "Chip %d Temperature", chip); root = api_add_const(root, buf, temp_str(minioninfo->chip_status[chip].temp), false); snprintf(buf, sizeof(buf), "Chip %d Cores", chip); root = api_add_uint16(root, buf, &(minioninfo->chip_status[chip].cores), true); snprintf(buf, sizeof(buf), "Chip %d Frequency", chip); root = api_add_uint32(root, buf, &(minioninfo->chip_status[chip].freq), true); snprintf(buf, sizeof(buf), "Chip %d InitFreq", chip); root = api_add_int(root, buf, &(minioninfo->init_freq[chip]), true); snprintf(buf, sizeof(buf), "Chip %d FreqSent", chip); root = api_add_hex32(root, buf, &(minioninfo->chip_status[chip].freqsent), true); snprintf(buf, sizeof(buf), "Chip %d InitTemp", chip); temp = minioninfo->init_temp[chip]; if (temp == MINION_TEMP_CTL_DISABLE) root = api_add_string(root, buf, MINION_TEMP_DISABLE, true); else { snprintf(data, sizeof(data), "%d", temp); root = api_add_string(root, buf, data, true); } snprintf(buf, sizeof(buf), "Chip %d TempSent", chip); root = api_add_hex32(root, buf, &(minioninfo->chip_status[chip].tempsent), true); __bin2hex(data, (unsigned char *)(&(minioninfo->init_cores[chip][0])), sizeof(minioninfo->init_cores[chip])); snprintf(buf, sizeof(buf), "Chip %d InitCores", chip); root = api_add_string(root, buf, data, true); snprintf(buf, sizeof(buf), "Chip %d IdleCount", chip); root = api_add_hex32(root, buf, &(minioninfo->chip_status[chip].idle), true); snprintf(buf, sizeof(buf), "Chip %d QueWork", chip); root = api_add_uint32(root, buf, &(minioninfo->chip_status[chip].quework), true); snprintf(buf, sizeof(buf), "Chip %d ChipWork", chip); root = api_add_uint32(root, buf, &(minioninfo->chip_status[chip].chipwork), true); snprintf(buf, sizeof(buf), "Chip %d RealWork", chip); root = api_add_uint32(root, buf, &(minioninfo->chip_status[chip].realwork), true); snprintf(buf, sizeof(buf), "Chip %d QueListCount", chip); root = api_add_int(root, buf, &(minioninfo->wque_list[chip]->count), true); snprintf(buf, sizeof(buf), "Chip %d WorkListCount", chip); root = api_add_int(root, buf, &(minioninfo->wchip_list[chip]->count), true); snprintf(buf, sizeof(buf), "Chip %d Overheat", chip); root = api_add_bool(root, buf, &(minioninfo->chip_status[chip].overheat), true); snprintf(buf, sizeof(buf), "Chip %d Overheats", chip); root = api_add_uint32(root, buf, &(minioninfo->chip_status[chip].overheats), true); snprintf(buf, sizeof(buf), "Chip %d LastOverheat", chip); root = api_add_timeval(root, buf, &(minioninfo->chip_status[chip].lastoverheat), true); snprintf(buf, sizeof(buf), "Chip %d LastRecover", chip); root = api_add_timeval(root, buf, &(minioninfo->chip_status[chip].lastrecover), true); snprintf(buf, sizeof(buf), "Chip %d OverheatIdle", chip); root = api_add_double(root, buf, &(minioninfo->chip_status[chip].overheattime), true); for (i = 0; i < MINION_CORES; i++) { if (minioninfo->chip_core_ena[i >> 5][chip] & (0x1 << (i % 32))) cores[i] = 'o'; else cores[i] = 'x'; } cores[MINION_CORES] = '\0'; snprintf(buf, sizeof(buf), "Chip %d CoresEna", chip); root = api_add_string(root, buf, cores, true); for (i = 0; i < MINION_CORES; i++) { if (minioninfo->chip_core_act[i >> 5][chip] & (0x1 << (i % 32))) cores[i] = '-'; else cores[i] = 'o'; } cores[MINION_CORES] = '\0'; snprintf(buf, sizeof(buf), "Chip %d CoresAct", chip); root = api_add_string(root, buf, cores, true); snprintf(buf, sizeof(buf), "Chip %d History GHs", chip); root = api_add_mhs(root, buf, &(minioninfo->history_ghs[chip]), true); } double his = MINION_HISTORY_s; root = api_add_double(root, "History length", &his, true); for (i = 0; i <= max_chip; i += CHIPS_PER_STAT) { to = i + CHIPS_PER_STAT - 1; if (to > max_chip) to = max_chip; data[0] = '\0'; for (j = i; j <= to; j++) { snprintf(buf, sizeof(buf), "%s%d", j == i ? "" : " ", minioninfo->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->chip[chip]) { que_work += minioninfo->wchip_list[chip]->count; chip_work += minioninfo->wchip_list[chip]->count; } root = api_add_int(root, "WFree Total", &(minioninfo->wfree_list->total), true); root = api_add_int(root, "WFree Count", &(minioninfo->wfree_list->count), true); root = api_add_int(root, "WWork Count", &(minioninfo->wwork_list->count), true); root = api_add_int(root, "WQue Count", &que_work, true); root = api_add_int(root, "WChip Count", &chip_work, true); root = api_add_int(root, "TFree Total", &(minioninfo->tfree_list->total), true); root = api_add_int(root, "TFree Count", &(minioninfo->tfree_list->count), true); root = api_add_int(root, "Task Count", &(minioninfo->task_list->count), true); root = api_add_int(root, "Reply Count", &(minioninfo->treply_list->count), true); root = api_add_int(root, "RFree Total", &(minioninfo->rfree_list->total), true); root = api_add_int(root, "RFree Count", &(minioninfo->rfree_list->count), true); root = api_add_int(root, "RNonce Count", &(minioninfo->rnonce_list->count), true); #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); for (i = 0; i < 0x200; i++) { snprintf(buf, sizeof(buf), "Stat-0x%02x", i); sta_api(addr2txt((uint8_t)(i & 0xff)), minioninfo->iostats[i]); } // Test to avoid showing applog if (minioninfo->summary.count) { snprintf(buf, sizeof(buf), "Stat-S"); sta_api("Summary", minioninfo->summary); applog(LOG_WARNING, "%s %d: (%.0f) %s - %s", minioncgpu->drv->name, minioncgpu->device_id, total_secs, buf, data); } #endif root = api_add_uint64(root, "Total SPI Errors", &(minioninfo->spi_errors), true); root = api_add_uint64(root, "Work Unrolled", &(minioninfo->work_unrolled), true); root = api_add_uint64(root, "Work Rolled", &(minioninfo->work_rolled), true); root = api_add_uint64(root, "Ints", &(minioninfo->interrupts), true); root = api_add_uint64(root, "Res Ints", &(minioninfo->result_interrupts), true); root = api_add_uint64(root, "Cmd Ints", &(minioninfo->command_interrupts), true); root = api_add_string(root, "Last Int", minioninfo->last_interrupt, true); root = api_add_hex32(root, "Next TaskID", &(minioninfo->next_task_id), true); root = api_add_elapsed(root, "Elapsed", &(total_secs), true); return root; } #endif struct device_drv minion_drv = { .drv_id = DRIVER_minion, .dname = "Minion BlackArrow", .name = "MBA", .drv_detect = minion_detect, #ifdef LINUX .get_api_stats = minion_api_stats, .get_statline_before = minion_get_statline_before, .identify_device = minion_identify, .thread_prepare = minion_thread_prepare, .hash_work = hash_queued_work, .scanwork = minion_scanwork, .queue_full = minion_queue_full, .flush_work = minion_flush_work, .thread_shutdown = minion_shutdown #endif };