|
|
@@ -16,6 +16,7 @@
|
|
|
#include <string.h>
|
|
|
|
|
|
#include <linux/spi/spidev.h>
|
|
|
+#include <uthash.h>
|
|
|
|
|
|
#include "deviceapi.h"
|
|
|
#include "logging.h"
|
|
|
@@ -25,10 +26,32 @@
|
|
|
|
|
|
static const uint8_t minion_max_chipid = 0x1f;
|
|
|
static const uint8_t minion_chip_signature[] = {0x44, 0x8a, 0xac, 0xb1};
|
|
|
+static const unsigned minion_max_queued = 0x10;
|
|
|
+static const unsigned minion_poll_us = 10000;
|
|
|
|
|
|
enum minion_register {
|
|
|
MRA_SIGNATURE = 0x00,
|
|
|
MRA_STATUS = 0x01,
|
|
|
+ MRA_MISC_CTL = 0x06,
|
|
|
+ MRA_FIFO_STATUS = 0x0b,
|
|
|
+
|
|
|
+ MRA_RESULT = 0x20,
|
|
|
+
|
|
|
+ MRA_TASK = 0x30,
|
|
|
+
|
|
|
+ MRA_NONCE_START = 0x70,
|
|
|
+ MRA_NONCE_INC = 0x71,
|
|
|
+};
|
|
|
+
|
|
|
+struct minion_chip {
|
|
|
+ uint8_t chipid;
|
|
|
+ uint8_t core_count;
|
|
|
+ uint16_t next_taskid;
|
|
|
+ struct cgpu_info *first_proc;
|
|
|
+};
|
|
|
+
|
|
|
+struct minion_bus {
|
|
|
+ struct spi_port *spi;
|
|
|
};
|
|
|
|
|
|
static
|
|
|
@@ -46,6 +69,16 @@ void minion_get(struct spi_port * const spi, const uint8_t chipid, const uint8_t
|
|
|
memcpy(buf, &rdbuf[sizeof(header)], bufsz);
|
|
|
}
|
|
|
|
|
|
+static
|
|
|
+void minion_set(struct spi_port * const spi, const uint8_t chipid, const uint8_t addr, const void * const buf, const size_t bufsz)
|
|
|
+{
|
|
|
+ const uint8_t header[] = {chipid, addr, bufsz & 0xff, bufsz >> 8};
|
|
|
+ spi_clear_buf(spi);
|
|
|
+ spi_emit_buf(spi, header, sizeof(header));
|
|
|
+ spi_emit_buf(spi, buf, bufsz);
|
|
|
+ spi_txrx(spi);
|
|
|
+}
|
|
|
+
|
|
|
static
|
|
|
unsigned minion_count_cores(struct spi_port * const spi)
|
|
|
{
|
|
|
@@ -80,6 +113,190 @@ unsigned minion_count_cores(struct spi_port * const spi)
|
|
|
return total_core_count;
|
|
|
}
|
|
|
|
|
|
+static
|
|
|
+bool minion_init(struct thr_info * const thr)
|
|
|
+{
|
|
|
+ struct cgpu_info * const dev = thr->cgpu, *proc = dev;
|
|
|
+ struct minion_bus * const mbus = dev->device_data;
|
|
|
+ struct spi_port * const spi = mbus->spi;
|
|
|
+ uint8_t buf[max(4, sizeof(minion_chip_signature))];
|
|
|
+
|
|
|
+ struct minion_chip * const chips = malloc(sizeof(*chips) * ((size_t)minion_max_chipid + 1));
|
|
|
+ for (unsigned chipid = 0; proc; ++chipid)
|
|
|
+ {
|
|
|
+ struct minion_chip * const chip = &chips[chipid];
|
|
|
+ spi->repr = proc->proc_repr;
|
|
|
+
|
|
|
+ minion_get(spi, chipid, MRA_SIGNATURE, buf, sizeof(minion_chip_signature));
|
|
|
+ if (memcmp(buf, minion_chip_signature, sizeof(minion_chip_signature)))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ minion_get(spi, chipid, MRA_STATUS, buf, 4);
|
|
|
+ if (!buf[2])
|
|
|
+ continue;
|
|
|
+
|
|
|
+ *chip = (struct minion_chip){
|
|
|
+ .chipid = chipid,
|
|
|
+ .core_count = buf[2],
|
|
|
+ .first_proc = proc,
|
|
|
+ };
|
|
|
+ minion_set(spi, chipid, MRA_NONCE_START, "\0\0\0\0", 4);
|
|
|
+ pk_u32le(buf, 0, 0xffffffff / chip->core_count);
|
|
|
+ minion_set(spi, chipid, MRA_NONCE_INC, buf, 4);
|
|
|
+
|
|
|
+ minion_get(spi, chipid, MRA_MISC_CTL, buf, 4);
|
|
|
+ buf[0] |= 1 << 2; // Enable "no nonce" result reports
|
|
|
+ minion_set(spi, chipid, MRA_MISC_CTL, buf, 4);
|
|
|
+
|
|
|
+ timer_set_delay_from_now(&proc->thr[0]->tv_poll, minion_poll_us);
|
|
|
+
|
|
|
+ for (unsigned i = 0; i < chip->core_count; ++i)
|
|
|
+ {
|
|
|
+ struct thr_info * const thr = proc->thr[0];
|
|
|
+
|
|
|
+ thr->cgpu_data = chip;
|
|
|
+
|
|
|
+ proc = proc->next_proc;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static
|
|
|
+bool minion_queue_full(struct minion_chip * const chip)
|
|
|
+{
|
|
|
+ struct cgpu_info *proc = chip->first_proc;
|
|
|
+ struct thr_info *thr = proc->thr[0];
|
|
|
+
|
|
|
+ const bool full = (HASH_COUNT(thr->work) >= minion_max_queued);
|
|
|
+ if (full != thr->queue_full)
|
|
|
+ {
|
|
|
+ for (unsigned i = 0; i < chip->core_count; (proc = proc->next_proc), ++i)
|
|
|
+ {
|
|
|
+ thr = proc->thr[0];
|
|
|
+
|
|
|
+ thr->queue_full = full;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return full;
|
|
|
+}
|
|
|
+
|
|
|
+static
|
|
|
+bool minion_queue_append(struct thr_info *thr, struct work * const work)
|
|
|
+{
|
|
|
+ struct cgpu_info *proc = thr->cgpu;
|
|
|
+ struct minion_bus * const mbus = proc->device_data;
|
|
|
+ struct minion_chip * const chip = thr->cgpu_data;
|
|
|
+ proc = chip->first_proc;
|
|
|
+ thr = proc->thr[0];
|
|
|
+
|
|
|
+ if (minion_queue_full(chip))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ struct spi_port * const spi = mbus->spi;
|
|
|
+ const uint8_t chipid = chip->chipid;
|
|
|
+ uint8_t taskdata[0x30];
|
|
|
+ spi->repr = proc->proc_repr;
|
|
|
+
|
|
|
+ work->device_id = ++chip->next_taskid;
|
|
|
+
|
|
|
+ pk_u16be(taskdata, 0, work->device_id);
|
|
|
+ memset(&taskdata[2], 0, 2);
|
|
|
+ memcpy(&taskdata[4], work->midstate, 0x20);
|
|
|
+ memcpy(&taskdata[0x24], &work->data[0x40], 0xc);
|
|
|
+
|
|
|
+ minion_set(spi, chipid, MRA_TASK, taskdata, sizeof(taskdata));
|
|
|
+
|
|
|
+ HASH_ADD(hh, thr->work, device_id, sizeof(work->device_id), work);
|
|
|
+
|
|
|
+ minion_queue_full(chip);
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static
|
|
|
+void minion_queue_flush(struct thr_info * const thr)
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+static
|
|
|
+void minion_poll(struct thr_info * const chip_thr)
|
|
|
+{
|
|
|
+ struct cgpu_info * const first_proc = chip_thr->cgpu;
|
|
|
+ struct minion_bus * const mbus = first_proc->device_data;
|
|
|
+ struct minion_chip * const chip = chip_thr->cgpu_data;
|
|
|
+ struct spi_port * const spi = mbus->spi;
|
|
|
+ const uint8_t chipid = chip->chipid;
|
|
|
+ spi->repr = first_proc->proc_repr;
|
|
|
+
|
|
|
+ uint8_t buf[4];
|
|
|
+ minion_get(spi, chipid, MRA_FIFO_STATUS, buf, 4);
|
|
|
+
|
|
|
+ const uint8_t res_fifo_len = buf[0];
|
|
|
+ if (res_fifo_len)
|
|
|
+ {
|
|
|
+ static const size_t resbuf_i_len = 8;
|
|
|
+ const size_t resbuf_len = (size_t)res_fifo_len * resbuf_i_len;
|
|
|
+ uint8_t resbuf[resbuf_len], *resbuf_i = resbuf;
|
|
|
+ minion_get(spi, chipid, MRA_RESULT, resbuf, resbuf_len);
|
|
|
+
|
|
|
+ for (unsigned i = 0; i < res_fifo_len; (resbuf_i += resbuf_i_len), ++i)
|
|
|
+ {
|
|
|
+ const uint8_t coreid = resbuf_i[2];
|
|
|
+ work_device_id_t taskid = upk_u16be(resbuf_i, 0);
|
|
|
+ const bool have_nonce = !(resbuf_i[3] & 0x80);
|
|
|
+ struct cgpu_info *proc;
|
|
|
+ struct thr_info *core_thr;
|
|
|
+
|
|
|
+ if (likely(coreid < chip->core_count))
|
|
|
+ {
|
|
|
+ proc = first_proc;
|
|
|
+ for (int j = 0; j < coreid; ++j)
|
|
|
+ proc = proc->next_proc;
|
|
|
+ core_thr = proc->thr[0];
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ proc = first_proc;
|
|
|
+ core_thr = proc->thr[0];
|
|
|
+ inc_hw_errors_only(core_thr);
|
|
|
+ applog(LOG_ERR, "%"PRIpreprv": Core id out of range (%u >= %u)", proc->proc_repr, coreid, chip->core_count);
|
|
|
+ }
|
|
|
+
|
|
|
+ struct work *work;
|
|
|
+ HASH_FIND(hh, chip_thr->work, &taskid, sizeof(taskid), work);
|
|
|
+ if (unlikely(!work))
|
|
|
+ {
|
|
|
+ inc_hw_errors_only(core_thr);
|
|
|
+ applog(LOG_ERR, "%"PRIpreprv": Unknown task %"PRIwdi, proc->proc_repr, taskid);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (have_nonce)
|
|
|
+ {
|
|
|
+ const uint32_t nonce = upk_u32le(resbuf_i, 4);
|
|
|
+
|
|
|
+ submit_nonce(core_thr, work, nonce);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Delete the previous work
|
|
|
+ uint16_t taskid_truncated = taskid;
|
|
|
+ --taskid_truncated;
|
|
|
+ taskid = taskid_truncated;
|
|
|
+ HASH_FIND(hh, chip_thr->work, &taskid, sizeof(taskid), work);
|
|
|
+ if (work)
|
|
|
+ {
|
|
|
+ HASH_DEL(chip_thr->work, work);
|
|
|
+ free_work(work);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ minion_queue_full(chip);
|
|
|
+ }
|
|
|
+
|
|
|
+ timer_set_delay_from_now(&chip_thr->tv_poll, minion_poll_us);
|
|
|
+}
|
|
|
+
|
|
|
BFG_REGISTER_DRIVER(minion_drv)
|
|
|
|
|
|
static
|
|
|
@@ -93,7 +310,7 @@ bool minion_detect_one(const char * const devpath)
|
|
|
spi->speed = 50000000;
|
|
|
spi->mode = SPI_MODE_0;
|
|
|
spi->bits = 8;
|
|
|
- spi->txrx = linux_spi_txrx;
|
|
|
+ spi->txrx = linux_spi_txrx2;
|
|
|
if (spi_open(spi, devpath) < 0)
|
|
|
{
|
|
|
free(spi);
|
|
|
@@ -104,13 +321,16 @@ bool minion_detect_one(const char * const devpath)
|
|
|
spi->logprio = LOG_WARNING;
|
|
|
const unsigned total_core_count = minion_count_cores(spi);
|
|
|
|
|
|
- close(spi->fd);
|
|
|
- free(spi);
|
|
|
+ struct minion_bus * const mbus = malloc(sizeof(*mbus));
|
|
|
+ *mbus = (struct minion_bus){
|
|
|
+ .spi = spi,
|
|
|
+ };
|
|
|
|
|
|
struct cgpu_info * const cgpu = malloc(sizeof(*cgpu));
|
|
|
*cgpu = (struct cgpu_info){
|
|
|
.drv = &minion_drv,
|
|
|
.device_path = strdup(devpath),
|
|
|
+ .device_data = mbus,
|
|
|
.deven = DEV_ENABLED,
|
|
|
.procs = total_core_count,
|
|
|
.threads = 1,
|
|
|
@@ -134,4 +354,11 @@ struct device_drv minion_drv = {
|
|
|
.dname = "minion",
|
|
|
.name = "MNN",
|
|
|
.drv_detect = minion_detect,
|
|
|
+
|
|
|
+ .thread_init = minion_init,
|
|
|
+ .minerloop = minerloop_queue,
|
|
|
+
|
|
|
+ .queue_append = minion_queue_append,
|
|
|
+ .queue_flush = minion_queue_flush,
|
|
|
+ .poll = minion_poll,
|
|
|
};
|