Browse Source

Merge branch 'aan' into bfgminer

Luke Dashjr 11 years ago
parent
commit
dc9df6e349
10 changed files with 1034 additions and 17 deletions
  1. 5 0
      Makefile.am
  2. 14 0
      configure.ac
  3. 3 0
      deviceapi.c
  4. 641 0
      driver-aan.c
  5. 50 0
      driver-aan.h
  6. 248 0
      driver-jingtian.c
  7. 1 17
      driver-knc.c
  8. 57 0
      lowl-spi.c
  9. 11 0
      lowl-spi.h
  10. 4 0
      miner.c

+ 5 - 0
Makefile.am

@@ -327,6 +327,11 @@ if USE_HASHFAST
 bfgminer_SOURCES += driver-hashfast.c
 bfgminer_SOURCES += driver-hashfast.c
 endif
 endif
 
 
+if USE_JINGTIAN
+bfgminer_SOURCES += driver-aan.c driver-aan.h
+bfgminer_SOURCES += driver-jingtian.c
+endif
+
 if USE_ROCKMINER
 if USE_ROCKMINER
 bfgminer_SOURCES += driver-rockminer.c
 bfgminer_SOURCES += driver-rockminer.c
 endif
 endif

+ 14 - 0
configure.ac

@@ -1044,6 +1044,20 @@ fi
 AM_CONDITIONAL([USE_HASHFAST], [test x$hashfast = xyes])
 AM_CONDITIONAL([USE_HASHFAST], [test x$hashfast = xyes])
 
 
 
 
+driverlist="$driverlist jingtian"
+AC_ARG_ENABLE([jingtian],
+	[AC_HELP_STRING([--enable-jingtian],[Compile support for JingTian (default disabled)])],
+	[jingtian=$enableval],
+	[jingtian=$ddno]
+	)
+if test "x$jingtian" = "xyes"; then
+	AC_DEFINE([USE_JINGTIAN], [1], [Defined to 1 if JingTian support is wanted])
+	need_lowl_spi=yes
+	has_asic=yes
+fi
+AM_CONDITIONAL([USE_JINGTIAN], [test x$jingtian = xyes])
+
+
 driverlist="$driverlist metabank"
 driverlist="$driverlist metabank"
 AC_ARG_ENABLE([metabank],
 AC_ARG_ENABLE([metabank],
 	[AC_HELP_STRING([--enable-metabank],[Compile support for Metabank (default disabled)])],
 	[AC_HELP_STRING([--enable-metabank],[Compile support for Metabank (default disabled)])],

+ 3 - 0
deviceapi.c

@@ -689,6 +689,9 @@ redo:
 			reduce_timeout_to(&tv_timeout, &mythr->tv_watchdog);
 			reduce_timeout_to(&tv_timeout, &mythr->tv_watchdog);
 		}
 		}
 		
 		
+		// HACK: Some designs set the main thr tv_poll from secondary thrs
+		reduce_timeout_to(&tv_timeout, &cgpu->thr[0]->tv_poll);
+		
 		do_notifier_select(thr, &tv_timeout);
 		do_notifier_select(thr, &tv_timeout);
 	}
 	}
 }
 }

+ 641 - 0
driver-aan.c

@@ -0,0 +1,641 @@
+/*
+ * Copyright 2014 Luke Dashjr
+ * Copyright 2013 Zefir Kurtisi
+ *
+ * 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 <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "deviceapi.h"
+#include "driver-aan.h"
+#include "logging.h"
+#include "lowl-spi.h"
+#include "miner.h"
+#include "util.h"
+
+#define AAN_DEFAULT_NONCE_PDIFF  8
+
+// WARNING: Do not just change this without fixing aan_freq2pll!
+#define AAN_MAX_FREQ  6132
+
+#define AAN_PROBE_TIMEOUT_US  3750000
+#define AAN_INIT_TIMEOUT_US   5000000
+#define AAN_READ_INTERVAL_US   100000
+
+#define AAN_REGISTER_SIZE  6
+
+enum aan_cmd {
+	AAN_BIST_START           = 0x01,
+	AAN_BIST_FIX             = 0x03,
+	AAN_RESET                = 0x04,
+	AAN_WRITE_JOB            = 0x07,
+	AAN_READ_RESULT          = 0x08,
+	AAN_WRITE_REG            = 0x09,
+	AAN_READ_REG             = 0x0a,
+	AAN_READ_REG_RESP        = 0x1a,
+};
+
+static
+unsigned aan_pll2freq(const uint16_t pll)
+{
+	const uint8_t pll_postdiv = (pll >> 0xe);
+	const uint8_t pll_prediv = (pll >> 9) & 0x1f;
+	const uint16_t pll_fbdiv = pll & 0x1ff;
+	return (12 * pll_fbdiv) / pll_prediv / (1 << (pll_postdiv - 1));
+}
+
+static
+uint16_t aan_freq2pll(unsigned freq)
+{
+retry: ;
+	uint8_t postdiv = 3, prediv = 3;
+	uint16_t fbdiv = freq / 3;
+	if (fbdiv * 3 == freq)
+		prediv = 1;
+	else
+		fbdiv = freq;
+	if (!(fbdiv & 3))
+	{
+		fbdiv >>= 2;
+		postdiv = 1;
+	}
+	else
+	if (!(fbdiv & 1))
+	{
+		fbdiv >>= 1;
+		postdiv = 2;
+	}
+	if (fbdiv > 0x1ff)
+	{
+		--freq;
+		goto retry;
+	}
+	const uint16_t pll = (((postdiv << 5) | prediv) << 9) | fbdiv;
+	return pll;
+}
+
+static
+void _test_aan_pll(const unsigned expect, const uint8_t postdiv, const uint8_t prediv, const uint16_t fbdiv)
+{
+	const uint16_t pll = (((postdiv << 5) | prediv) << 9) | fbdiv;
+	const unsigned got = aan_pll2freq(pll);
+	if (got != expect)
+		applog(LOG_WARNING, "%s test failed for %4u(%x,%02x,%3d): got %4u", "aan_pll2freq", expect, postdiv, prediv, fbdiv, got);
+}
+
+static
+void _test_aan_pll2(const unsigned freq)
+{
+	const uint16_t pll = aan_freq2pll(freq);
+	const unsigned got = aan_pll2freq(pll);
+	if (got / 12 != freq / 12)
+	{
+		const uint8_t postdiv = (pll >> 0xe);
+		const uint8_t prediv = (pll >> 9) & 0x1f;
+		const uint16_t fbdiv = pll & 0x1ff;
+		applog(LOG_WARNING, "%s test failed for %4u: got %4u(%x,%02x,%3d)", "aan_freq2pll", freq, got, postdiv, prediv, fbdiv);
+	}
+}
+
+void test_aan_pll(void)
+{
+	_test_aan_pll(1000, 0b01,0b00011,0b011111010);
+	_test_aan_pll( 950, 0b10,0b00011,0b111011011);
+	_test_aan_pll( 900, 0b01,0b00001,0b001001011);
+	_test_aan_pll( 850, 0b10,0b00011,0b110101001);
+	_test_aan_pll( 800, 0b01,0b00011,0b011001000);
+	_test_aan_pll( 750, 0b10,0b00001,0b001111101);
+	_test_aan_pll( 700, 0b01,0b00011,0b010101111);
+	_test_aan_pll( 650, 0b10,0b00011,0b101000101);
+	_test_aan_pll( 600, 0b01,0b00001,0b000110010);
+	_test_aan_pll( 550, 0b10,0b00011,0b100010011);
+	_test_aan_pll( 500, 0b10,0b00011,0b011111010);
+	_test_aan_pll( 100, 0b11,0b00011,0b001100100);
+	for (unsigned i = 1; i <= AAN_MAX_FREQ; ++i)
+		_test_aan_pll2(i);
+}
+
+static void aan_spi_parse_rx(struct spi_port *);
+
+static
+void aan_spi_cmd_queue(struct spi_port * const spi, const uint8_t cmd, const uint8_t chip, const void * const data, const size_t datalen)
+{
+	const struct aan_hooks * const hooks = spi->userp;
+	const uint8_t cmdbuf[2] = {cmd, chip};
+	hooks->precmd(spi);
+	spi_emit_buf(spi, cmdbuf, sizeof(cmdbuf));
+	if (datalen)
+		spi_emit_buf(spi, data, datalen);
+}
+
+static
+bool aan_spi_txrx(struct spi_port * const spi)
+{
+	if (unlikely(!spi_txrx(spi)))
+		return false;
+	
+	aan_spi_parse_rx(spi);
+	return true;
+}
+
+static
+bool aan_spi_cmd_send(struct spi_port * const spi, const uint8_t cmd, const uint8_t chip, const void * const data, const size_t datalen)
+{
+	aan_spi_cmd_queue(spi, cmd, chip, data, datalen);
+	return aan_spi_txrx(spi);
+}
+
+static
+bool aan_spi_cmd_resp(struct spi_port * const spi, const uint8_t cmd, const uint8_t chip, const struct timeval * const tvp_timeout)
+{
+	const uint8_t cmdbuf[2] = {cmd, chip};
+	
+	uint8_t * const rx = spi_getrxbuf(spi);
+	while (true)
+	{
+		spi_emit_nop(spi, 2);
+		if (unlikely(!spi_txrx(spi)))
+			return false;
+		if (!memcmp(rx, cmdbuf, 2))
+			break;
+		aan_spi_parse_rx(spi);
+		if (unlikely(tvp_timeout && timer_passed(tvp_timeout, NULL)))
+			return false;
+	}
+	spi_clear_buf(spi);
+	
+	return true;
+}
+
+static
+bool aan_spi_cmd(struct spi_port * const spi, const uint8_t cmd, const uint8_t chip, const void * const data, const size_t datalen, const struct timeval * const tvp_timeout)
+{
+	if (!aan_spi_cmd_send(spi, cmd, chip, data, datalen))
+		return false;
+	if (!aan_spi_cmd_resp(spi, cmd, chip, tvp_timeout))
+		return false;
+	return true;
+}
+
+bool aan_read_reg_direct(struct spi_port * const spi, const uint8_t chip, void * const out_buf, const struct timeval * const tvp_timeout)
+{
+	if (!aan_spi_cmd_send(spi, AAN_READ_REG, chip, NULL, 0))
+		return false;
+	if (!aan_spi_cmd_resp(spi, AAN_READ_REG_RESP, chip, tvp_timeout))
+		return false;
+	
+	spi_emit_nop(spi, AAN_REGISTER_SIZE);
+	if (!spi_txrx(spi))
+		applogr(false, LOG_DEBUG, "%s: %s failed", __func__, "spi_txrx");
+	
+	uint8_t * const rx = spi_getrxbuf(spi);
+	memcpy(out_buf, rx, AAN_REGISTER_SIZE);
+	
+	return true;
+}
+
+static inline
+bool aan_read_reg(struct spi_port * const spi, const uint8_t chip, void * const out_buf, const struct timeval * const tvp_timeout)
+{
+	const struct aan_hooks * const hooks = spi->userp;
+	return hooks->read_reg(spi, chip, out_buf, tvp_timeout);
+}
+
+int aan_detect_spi(int * const out_chipcount, struct spi_port * const * const spi_a, const int spi_n)
+{
+	struct timeval tv_timeout;
+	timer_set_delay_from_now(&tv_timeout, AAN_PROBE_TIMEOUT_US);
+	
+	int state[spi_n];
+	int completed = 0;
+	
+	for (int i = 0; i < spi_n; ++i)
+	{
+		struct spi_port * const spi = spi_a[i];
+		aan_spi_cmd_send(spi, state[i] = AAN_RESET, AAN_ALL_CHIPS, NULL, 0);
+		out_chipcount[i] = -1;
+	}
+	
+	do {
+		for (int i = 0; i < spi_n; ++i)
+		{
+			if (state[i] == -1)
+				continue;
+			struct spi_port * const spi = spi_a[i];
+			spi_emit_nop(spi, 2);
+			if (unlikely(!spi_txrx(spi)))
+			{
+spifail:
+				state[i] = -1;
+				continue;
+			}
+			uint8_t * const rx = spi_getrxbuf(spi);
+			if (rx[0] == state[i] && rx[1] == AAN_ALL_CHIPS)
+			{
+				switch (state[i])
+				{
+					case AAN_RESET:
+						applog(LOG_DEBUG, "%s: Reset complete", spi->repr);
+						spi_clear_buf(spi);
+						aan_spi_cmd_send(spi, state[i] = AAN_BIST_START, AAN_ALL_CHIPS, NULL, 0);
+						spi_emit_nop(spi, 2);
+						break;
+					case AAN_BIST_START:
+						if (unlikely(!spi_txrx(spi)))
+							goto spifail;
+						out_chipcount[i] = rx[1];
+						state[i] = -1;
+						++completed;
+						applog(LOG_DEBUG, "%s: BIST_START complete (%d chips)", spi->repr, rx[1]);
+						break;
+				}
+				spi_clear_buf(spi);
+				continue;
+			}
+			aan_spi_parse_rx(spi);
+		}
+	} while (completed < spi_n && likely(!timer_passed(&tv_timeout, NULL)));
+	
+	applog(LOG_DEBUG, "%s completed for %d out of %d SPI ports", __func__, completed, spi_n);
+	
+	return completed;
+}
+
+bool aan_init(struct thr_info * const master_thr)
+{
+	struct cgpu_info * const master_dev = master_thr->cgpu, *dev = NULL;
+	struct aan_board_data *board;
+	struct timeval tv_timeout, tv_now;
+	int chipid;
+	for_each_managed_proc(proc, master_dev)
+	{
+		struct spi_port * const spi = proc->device_data;
+		struct thr_info * const thr = proc->thr[0];
+		
+		if (dev != proc->device)
+		{
+			dev = proc->device;
+			chipid = 0;
+			timer_set_now(&tv_now);
+			board = malloc(sizeof(*board));
+			*board = (struct aan_board_data){
+				.master_dev = master_dev,
+				.spi = spi,
+				.tv_next_poll = tv_now,
+			};
+			spi->cgpu = dev;
+			
+			while (true)
+			{
+				timer_set_delay(&tv_timeout, &tv_now, AAN_INIT_TIMEOUT_US);
+				if (aan_spi_cmd(spi, AAN_BIST_FIX, AAN_ALL_CHIPS, NULL, 0, &tv_timeout))
+					break;
+				applog(LOG_ERR, "%s: Failed to %s", proc->dev_repr, "BIST_FIX");
+			}
+		}
+		
+		proc->device_data = board;
+		struct aan_chip_data * const chip = malloc(sizeof(*chip));
+		thr->cgpu_data = chip;
+		thr->queue_full = true;
+		*chip = (struct aan_chip_data){
+			.chipid = ++chipid,
+			.desired_nonce_pdiff = AAN_DEFAULT_NONCE_PDIFF,
+			.desired_pllreg = 0x87a9,  // 850 MHz
+		};
+		
+		cgpu_set_defaults(proc);
+	}
+	master_thr->tv_poll = tv_now;
+	
+	return true;
+}
+
+static
+bool aan_spi_send_work(struct spi_port * const spi, const uint8_t chipid, const uint8_t jobid, const struct work * const work)
+{
+	uint8_t buf[0x38];
+	
+	swab256(&buf[0], work->midstate);
+	swap32yes(&buf[0x20], &work->data[0x40], 3);
+	memset(&buf[0x2c], 0, 4);         // start nonce
+	uint32_t compressed_target = (uint32_t)(0x10000 / work->nonce_diff) | (/*exponent*/ 0x1d << 24);
+	pk_u32le(buf, 0x30, compressed_target);
+	memset(&buf[0x34], 0xff, 4);      // end nonce
+	
+	return aan_spi_cmd_send(spi, AAN_WRITE_JOB | (jobid << 4), chipid, buf, sizeof(buf));
+}
+
+static bool set_work(struct cgpu_info *, uint8_t, struct work *);
+
+bool aan_queue_append(struct thr_info * const thr, struct work * const work)
+{
+	struct cgpu_info *proc = thr->cgpu;
+	struct aan_chip_data * const chip = thr->cgpu_data;
+	struct cgpu_info *dev = proc->device;
+	struct aan_board_data *board = dev->device_data;
+	struct cgpu_info * const master_dev = board->master_dev;
+	struct aan_board_data * const master_board = master_dev->device_data;
+	
+	applog(LOG_DEBUG, "%s: queue_append queues_empty=%d", proc->proc_repr, master_board->queues_empty-1);
+	
+	work->nonce_diff = work->work_difficulty;
+	if (work->nonce_diff > chip->desired_nonce_pdiff)
+		work->nonce_diff = chip->desired_nonce_pdiff;
+	chip->current_nonce_pdiff = work->nonce_diff;
+	
+	if (set_work(dev, proc->proc_id + 1, work))
+		hashes_done2(thr, 0x100000000, NULL);
+	
+	thr->queue_full = true;
+	if (!--master_board->queues_empty)
+	{
+		struct thr_info * const master_thr = master_dev->thr[0];
+		
+		// Reactivate polling
+		dev = NULL;
+		for_each_managed_proc(proc, master_dev)
+		{
+			if (dev == proc->device)
+				continue;
+			dev = proc->device;
+			board = dev->device_data;
+			
+			reduce_timeout_to(&master_thr->tv_poll, &board->tv_next_poll);
+		}
+	}
+	return true;
+}
+
+void aan_queue_flush(struct thr_info * const thr)
+{
+	// TODO
+}
+
+struct cgpu_info *aan_proc_for_chipid(struct cgpu_info * const dev, const int chipid)
+{
+	struct cgpu_info *proc = dev;
+	for (int i = 1; i < chipid; ++i)
+	{
+		proc = proc->next_proc;
+		if (unlikely((!proc) || proc->device != dev))
+		{
+badchipid:
+			inc_hw_errors_only(dev->thr[0]);
+			applogr(NULL, LOG_ERR, "%s: Chip number %d out of range", dev->dev_repr, chipid);
+		}
+	}
+	if (unlikely(!chipid))
+		goto badchipid;
+	return proc;
+}
+
+static
+void aan_spi_parse_rx(struct spi_port * const spi)
+{
+	spi_clear_buf(spi);
+}
+
+#define MAX_POLL_NUM   20
+
+/* set work for given chip, returns true if a nonce range was finished */
+static
+bool set_work(struct cgpu_info * const dev, const uint8_t chip_id, struct work * const work)
+{
+	struct aan_board_data * const board = dev->device_data;
+	struct spi_port * const spi = board->spi;
+	
+	struct cgpu_info * const proc = aan_proc_for_chipid(dev, chip_id);
+	struct thr_info * const thr = proc->thr[0];
+	struct aan_chip_data * const chip = thr->cgpu_data;
+	bool retval = false;
+	
+	++chip->last_jobid;
+	chip->last_jobid &= 3;
+
+	if (chip->works[chip->last_jobid] != NULL)
+	{
+		free_work(chip->works[chip->last_jobid]);
+		chip->works[chip->last_jobid] = NULL;
+		retval = true;
+	}
+	
+	if (!aan_spi_send_work(spi, chip_id, chip->last_jobid + 1, work))
+	{
+		free_work(work);
+		applog(LOG_ERR, "%"PRIpreprv": Failed to set work %d", proc->proc_repr, chip->last_jobid + 1);
+	}
+	else
+		chip->works[chip->last_jobid] = work;
+	spi_clear_buf(spi);
+	
+	return retval;
+}
+
+/* check for pending results in a chain, returns false if output queue empty */
+static
+bool get_nonce(struct cgpu_info * const dev, uint8_t * const nonce, uint8_t * const chip, uint8_t * const job_id)
+{
+	struct aan_board_data * const board = dev->device_data;
+	struct spi_port * const spi = board->spi;
+	
+	int pollLen = MAX_POLL_NUM * dev->procs;
+	if (pollLen <= 0)
+		pollLen = MAX_POLL_NUM;
+	
+	if (!aan_spi_cmd_send(spi, AAN_READ_RESULT, AAN_ALL_CHIPS, NULL, 0))
+		return false;
+	
+	for (int i = 0; i < pollLen; ++i)
+	{
+		spi_clear_buf(spi);
+		spi_emit_nop(spi, 2);
+		if (!spi_txrx(spi))
+			applogr(false, LOG_ERR, "%s: SPI error in get_nonce", dev->dev_repr);
+		uint8_t * const spi_rx = spi_getrxbuf(spi);
+		if (spi_rx[0] == AAN_READ_RESULT && spi_rx[1] == 0x00)
+			applogr(false, LOG_DEBUG, "%s: Output queue empty", dev->dev_repr);
+		if ((spi_rx[0] & 0x0f) == AAN_READ_RESULT && spi_rx[1] != 0)
+		{
+			*job_id = spi_rx[0] >> 4;
+			*chip = spi_rx[1];
+			
+			spi_emit_nop(spi, 2);
+			if (!spi_txrx(spi))
+				applogr(false, LOG_ERR, "SPI Err(%s):get_nonce", dev->dev_repr);
+			memcpy(nonce, spi_rx, 4);
+			
+			applog(LOG_DEBUG, "%s: Got nonce for chip %d / job_id %d", dev->dev_repr, *chip, *job_id);
+			
+			return true;
+		}
+	}
+	
+	return false;
+}
+
+static
+void aan_scanwork(struct cgpu_info * const dev, struct thr_info * const master_thr)
+{
+	struct aan_board_data * const board = dev->device_data;
+	struct spi_port * const spi = board->spi;
+	
+	uint32_t nonce;
+	uint8_t chip_id;
+	uint8_t job_id;
+	bool work_updated = false;
+	
+	if (!timer_passed(&board->tv_next_poll, NULL))
+		goto out;
+	
+	while (get_nonce(dev, (uint8_t*)&nonce, &chip_id, &job_id))
+	{
+		nonce = bswap_32(nonce);
+		work_updated = true;
+		struct cgpu_info * const proc = aan_proc_for_chipid(dev, chip_id);
+		if (!proc)
+			continue;
+		struct thr_info * const thr = proc->thr[0];
+		struct aan_chip_data * const chip = thr->cgpu_data;
+		if (job_id < 1 || job_id > 4)
+		{
+badjob:
+			inc_hw_errors3(thr, NULL, &nonce, chip->current_nonce_pdiff);
+			continue;
+		}
+		struct work * const work = chip->works[job_id - 1];
+		if (!work)
+			goto badjob;
+		submit_nonce(thr, work, nonce);
+	}
+	
+	/* check for completed works */
+	for_each_logical_proc(proc, dev)
+	{
+		struct thr_info * const thr = proc->thr[0];
+		struct aan_chip_data * const chip = thr->cgpu_data;
+		const int i = proc->proc_id;
+		uint8_t reg[AAN_REGISTER_SIZE];
+		
+		if (!aan_read_reg(spi, i + 1, reg, NULL))
+		{
+			applog(LOG_ERR, "%"PRIpreprv": Failed to read reg", proc->proc_repr);
+			continue;
+		}
+		const uint16_t pllreg = upk_u16be(reg, 0);
+		chip->current_pllreg = pllreg;
+		if (pllreg != chip->desired_pllreg)
+		{
+			// Wait for chip to idle before changing register
+			if (!(reg[3] & 3))
+			{
+				applog(LOG_DEBUG, "%"PRIpreprv": Asserting PLL change: %04x->%04x", proc->proc_repr, pllreg, chip->desired_pllreg);
+				uint8_t regset[AAN_REGISTER_SIZE];
+				memcpy(&regset[2], &reg[2], AAN_REGISTER_SIZE - 2);
+				pk_u16be(regset, 0, chip->desired_pllreg);
+				aan_spi_cmd_send(spi, AAN_WRITE_REG, chip->chipid, regset, AAN_REGISTER_SIZE);
+			}
+		}
+		else
+		if ((reg[3] & 2) != 2)
+		{
+			struct cgpu_info * const master_dev = board->master_dev;
+			struct aan_board_data * const master_board = master_dev->device_data;
+			
+			work_updated = true;
+			thr->queue_full = false;
+			++master_board->queues_empty;
+			applog(LOG_DEBUG, "%s: queue_full=false queues_empty=%d", proc->proc_repr, master_board->queues_empty);
+		}
+	}
+	
+	if (!work_updated)
+		timer_set_delay_from_now(&board->tv_next_poll, AAN_READ_INTERVAL_US);
+
+out:
+	reduce_timeout_to(&master_thr->tv_poll, &board->tv_next_poll);
+}
+
+void aan_poll(struct thr_info * const master_thr)
+{
+	struct cgpu_info * const master_dev = master_thr->cgpu, *dev = NULL;
+	struct aan_board_data * const master_board = master_dev->device_data;
+	
+	timer_unset(&master_thr->tv_poll);
+	
+	for_each_managed_proc(proc, master_dev)
+	{
+		if (dev == proc->device)
+			continue;
+		dev = proc->device;
+		
+		aan_scanwork(dev, master_thr);
+	}
+	
+	if (master_board->queues_empty)
+		// Avoid polling when we have queues to fill
+		timer_unset(&master_thr->tv_poll);
+}
+
+const char *aan_set_clock(struct cgpu_info * const proc, const char * const optname, const char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const success)
+{
+	struct thr_info * const thr = proc->thr[0];
+	struct aan_chip_data * const chip = thr->cgpu_data;
+	
+	if (newvalue[0] == 'x')
+	{
+		char *p;
+		chip->desired_pllreg = strtol(&newvalue[1], &p, 0x10);
+		if (p != &newvalue[5])
+			return "Invalid hex PLL data";
+	}
+	else
+	{
+		const int nv = atoi(newvalue);
+		if (nv <= 0 || nv > AAN_MAX_FREQ)
+			return "Invalid clock frequency";
+		chip->desired_pllreg = aan_freq2pll(nv);
+	}
+	
+	return NULL;
+}
+
+const char *aan_set_diff(struct cgpu_info * const proc, const char * const optname, const char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const success)
+{
+	struct thr_info * const thr = proc->thr[0];
+	struct aan_chip_data * const chip = thr->cgpu_data;
+	
+	const double nv = atof(newvalue);
+	if (nv <= 0)
+		return "Invalid difficulty";
+	
+	chip->desired_nonce_pdiff = nv;
+	
+	return NULL;
+}
+
+struct api_data *aan_api_device_status(struct cgpu_info * const proc)
+{
+	struct thr_info * const thr = proc->thr[0];
+	struct aan_chip_data * const chip = thr->cgpu_data;
+	struct api_data *root = NULL;
+	
+	double mhz = aan_pll2freq(chip->current_pllreg);
+	root = api_add_freq(root, "Frequency", &mhz, true);
+	
+	return root;
+}
+
+const struct bfg_set_device_definition aan_set_device_funcs[] = {
+	{"clock", aan_set_clock, "clock frequency (MHz)"},
+	{"diff", aan_set_diff, "desired nonce difficulty"},
+	{NULL},
+};

+ 50 - 0
driver-aan.h

@@ -0,0 +1,50 @@
+#ifndef BFG_DRIVER_AAN
+#define BFG_DRIVER_AAN
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "lowl-spi.h"
+#include "miner.h"
+
+#define AAN_ALL_CHIPS  0
+#define AAN_MAX_JOBID  4
+
+struct aan_hooks {
+	void (*precmd)(struct spi_port *);
+	bool (*read_reg)(struct spi_port *, uint8_t chip, void *out_buf, const struct timeval *tvp_timeout);
+};
+
+struct aan_board_data {
+	struct spi_port *spi;
+	struct timeval tv_next_poll;
+	struct cgpu_info *master_dev;
+	
+	// Master board only
+	int queues_empty;
+};
+
+struct aan_chip_data {
+	uint8_t chipid;
+	int8_t last_jobid;
+	struct work *works[AAN_MAX_JOBID];
+	float desired_nonce_pdiff;
+	float current_nonce_pdiff;
+	uint16_t desired_pllreg;
+	uint16_t current_pllreg;
+};
+
+extern int aan_detect_spi(int *out_chipcount, struct spi_port * const *spi_a, int spi_n);
+extern bool aan_read_reg_direct(struct spi_port *, uint8_t chip, void *out_buf, const struct timeval *tvp_timeout);
+extern bool aan_init(struct thr_info *);
+extern bool aan_queue_append(struct thr_info *, struct work *);
+extern void aan_queue_flush(struct thr_info *);
+extern struct cgpu_info *aan_proc_for_chipid(struct cgpu_info *, int chipid);
+extern void aan_poll(struct thr_info *);
+
+extern const char *aan_set_diff(struct cgpu_info *, const char *optname, const char *newvalue, char *replybuf, enum bfg_set_device_replytype *);
+extern const struct bfg_set_device_definition aan_set_device_funcs[];
+
+extern struct api_data *aan_api_device_status(struct cgpu_info *);
+
+#endif

+ 248 - 0
driver-jingtian.c

@@ -0,0 +1,248 @@
+/*
+ * Copyright 2014 Luke Dashjr
+ *
+ * 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 <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <sys/ioctl.h>
+#include <linux/types.h>
+#include <linux/spi/spidev.h>
+
+#include "deviceapi.h"
+#include "driver-aan.h"
+#include "logging.h"
+#include "lowl-spi.h"
+#include "util.h"
+
+static const int jingtian_cs_gpio[] = {14, 15, 18};
+static const int jingtian_spi_disable_gpio = 25;
+static const int jingtian_reset_gpio = 3;
+static const int jingtian_max_cs = 1 << (sizeof(jingtian_cs_gpio) / sizeof(*jingtian_cs_gpio));
+static const uint8_t jingtian_pre_header[] = {0xb5, 0xb5};
+
+#define JINGTIAN_REGISTER_EXTRA_SIZE  2
+
+BFG_REGISTER_DRIVER(jingtian_drv)
+
+static
+bool jingtian_spi_txrx(struct spi_port * const port)
+{
+	if (*port->chipselect_current != port->chipselect)
+	{
+		unsigned cs_set_low = 0, cs_set_high = 0, cur_cs_bit;
+		bool bit_desired;
+		for (int i = 0; i < sizeof(jingtian_cs_gpio) / sizeof(*jingtian_cs_gpio); ++i)
+		{
+			cur_cs_bit = (1 << i);
+			bit_desired = (port->chipselect & cur_cs_bit);
+			if (bit_desired == (bool)(*port->chipselect_current & cur_cs_bit))
+				// No change needed
+				continue;
+			if (bit_desired)
+				cs_set_high |= (1 << jingtian_cs_gpio[i]);
+			else
+				cs_set_low  |= (1 << jingtian_cs_gpio[i]);
+		}
+		bfg_gpio_set_high(1 << jingtian_spi_disable_gpio);
+		if (cs_set_low)
+			bfg_gpio_set_low(cs_set_low);
+		if (cs_set_high)
+			bfg_gpio_set_high(cs_set_high);
+		bfg_gpio_set_low(1 << jingtian_spi_disable_gpio);
+		if (opt_dev_protocol)
+			applog(LOG_DEBUG, "%s(%p): CS %d", __func__, port, port->chipselect);
+		*port->chipselect_current = port->chipselect;
+	}
+	if (opt_dev_protocol)
+	{
+		char x[(spi_getbufsz(port) * 2) + 1];
+		bin2hex(x, spi_gettxbuf(port), spi_getbufsz(port));
+		applog(LOG_DEBUG, "%s(%p): %cX %s", __func__, port, 'T', x);
+	}
+	bool rv = linux_spi_txrx(port);
+	if (opt_dev_protocol)
+	{
+		char x[(spi_getbufsz(port) * 2) + 1];
+		bin2hex(x, spi_getrxbuf(port), spi_getbufsz(port));
+		applog(LOG_DEBUG, "%s(%p): %cX %s", __func__, port, 'R', x);
+	}
+	return rv;
+}
+
+static
+void jingtian_precmd(struct spi_port * const spi)
+{
+	spi_emit_buf(spi, jingtian_pre_header, sizeof(jingtian_pre_header));
+}
+
+static
+bool jingtian_read_reg(struct spi_port * const spi, const uint8_t chip, void * const out_buf, const struct timeval * const tvp_timeout)
+{
+	if (!aan_read_reg_direct(spi, chip, out_buf, tvp_timeout))
+		return false;
+	
+	spi_emit_nop(spi, JINGTIAN_REGISTER_EXTRA_SIZE);
+	if (!spi_txrx(spi))
+		applogr(false, LOG_DEBUG, "%s: %s failed", __func__, "spi_txrx");
+	
+	struct cgpu_info * const dev = spi->cgpu;
+	if (unlikely(!dev))
+		return true;
+	struct cgpu_info * const proc = aan_proc_for_chipid(dev, chip);
+	
+	uint8_t * const rx = spi_getrxbuf(spi);
+	proc->temp = upk_u16be(rx, 0);
+	
+	return true;
+}
+
+static
+struct aan_hooks jingtian_hooks = {
+	.precmd = jingtian_precmd,
+	.read_reg = jingtian_read_reg,
+};
+
+static
+void jingtian_common_init(void)
+{
+	RUNONCE();
+	spi_init();
+	for (int i = 0; i < sizeof(jingtian_cs_gpio) / sizeof(*jingtian_cs_gpio); ++i)
+		bfg_gpio_setpin_output(jingtian_cs_gpio[i]);
+	bfg_gpio_setpin_output(jingtian_spi_disable_gpio);
+	bfg_gpio_set_high(1 << jingtian_spi_disable_gpio);
+	
+	bfg_gpio_setpin_output(jingtian_reset_gpio);
+	bfg_gpio_set_high(1 << jingtian_reset_gpio);
+	cgsleep_ms(200);
+	bfg_gpio_set_low(1 << jingtian_reset_gpio);
+}
+
+static
+bool jingtian_detect_one(const char * const devpath)
+{
+	int found = 0, chips;
+	
+	jingtian_common_init();
+	
+	struct spi_port spi_cfg;
+	memset(&spi_cfg, 0, sizeof(spi_cfg));
+	spi_cfg.speed = 4000000;
+	spi_cfg.delay = 0;
+	spi_cfg.mode = SPI_MODE_1;
+	spi_cfg.bits = 8;
+	if (spi_open(&spi_cfg, devpath) < 0)
+		applogr(false, LOG_DEBUG, "%s: Failed to open %s", jingtian_drv.dname, devpath);
+	
+	struct cgpu_info *cgpu, *prev_cgpu = NULL;
+	struct spi_port *spi;
+	int * const chipselect_current = malloc(sizeof(*spi->chipselect_current));
+	*chipselect_current = -1;
+	
+	int devpath_len = strlen(devpath);
+	
+	int chipcount[jingtian_max_cs];
+	struct spi_port *spi_a[jingtian_max_cs];
+	for (int i = 0; i < jingtian_max_cs; ++i)
+	{
+		spi = spi_a[i] = calloc(sizeof(*spi), 1);
+		memcpy(spi, &spi_cfg, sizeof(*spi));
+		spi->repr = malloc(devpath_len + 0x10);
+		sprintf((void*)spi->repr, "%s(cs%d)", devpath, i);
+		spi->txrx = jingtian_spi_txrx;
+		spi->userp = &jingtian_hooks;
+		spi->chipselect = i;
+		spi->chipselect_current = chipselect_current;
+	}
+	
+	aan_detect_spi(chipcount, spi_a, jingtian_max_cs);
+	
+	for (int i = 0; i < jingtian_max_cs; ++i)
+	{
+		chips = chipcount[i];
+		free((void*)spi_a[i]->repr);
+		spi_a[i]->repr = NULL;
+		if (chips <= 0)
+		{
+			free(spi_a[i]);
+			continue;
+		}
+		cgpu = malloc(sizeof(*cgpu));
+		*cgpu = (struct cgpu_info){
+			.drv = &jingtian_drv,
+			.procs = chips,
+			.threads = prev_cgpu ? 0 : 1,
+			.device_data = spi_a[i],
+			.device_path = strdup(devpath),
+			.set_device_funcs = aan_set_device_funcs,
+		};
+		spi_a[i]->cgpu = cgpu;
+		add_cgpu_slave(cgpu, prev_cgpu);
+		prev_cgpu = cgpu;
+		found += chips;
+	}
+	
+	close(spi_cfg.fd);
+	if (!found)
+		free(chipselect_current);
+	return found;
+}
+
+static
+int jingtian_detect_auto(void)
+{
+	return jingtian_detect_one("/dev/spidev0.0") ? 1 : 0;
+}
+
+static
+void jingtian_detect(void)
+{
+	generic_detect(&jingtian_drv, jingtian_detect_one, jingtian_detect_auto, GDF_REQUIRE_DNAME | GDF_DEFAULT_NOAUTO);
+}
+
+static
+bool jingtian_init(struct thr_info * const master_thr)
+{
+	struct cgpu_info * const master_dev = master_thr->cgpu;
+	const char * const devpath = master_dev->device_path;
+	const int fd = open(devpath, O_RDWR);
+	if (fd < 0)
+		applogr(false, LOG_ERR, "%s: Failed to open %s", master_dev->dev_repr, devpath);
+	
+	for_each_managed_proc(proc, master_dev)
+	{
+		struct spi_port * const spi = proc->device_data;
+		spi->fd = fd;
+	}
+	
+	return aan_init(master_thr);
+}
+
+struct device_drv jingtian_drv = {
+	.dname = "jingtian",
+	.name = "JTN",
+	.drv_detect = jingtian_detect,
+	
+	.thread_init = jingtian_init,
+	
+	.minerloop = minerloop_queue,
+	.queue_append = aan_queue_append,
+	.queue_flush = aan_queue_flush,
+	.poll = aan_poll,
+	
+	.get_api_extra_device_status = aan_api_device_status,
+};

+ 1 - 17
driver-knc.c

@@ -214,23 +214,7 @@ fail:
 	return false;
 	return false;
 }
 }
 
 
-static
-bool knc_spi_txrx(struct spi_port * const spi)
-{
-	const void * const wrbuf = spi_gettxbuf(spi);
-	void * const rdbuf = spi_getrxbuf(spi);
-	const size_t bufsz = spi_getbufsz(spi);
-	const int fd = spi->fd;
-	struct spi_ioc_transfer xf = {
-		.tx_buf = (uintptr_t) wrbuf,
-		.rx_buf = (uintptr_t) rdbuf,
-		.len = bufsz,
-		.delay_usecs = spi->delay,
-		.speed_hz = spi->speed,
-		.bits_per_word = spi->bits,
-	};
-	return (ioctl(fd, SPI_IOC_MESSAGE(1), &xf) > 0);
-}
+#define knc_spi_txrx  linux_spi_txrx
 
 
 static
 static
 void knc_clean_flush(struct spi_port * const spi)
 void knc_clean_flush(struct spi_port * const spi)

+ 57 - 0
lowl-spi.c

@@ -85,12 +85,52 @@ void spi_init(void)
 
 
 #ifdef HAVE_LINUX_SPI
 #ifdef HAVE_LINUX_SPI
 
 
+int spi_open(struct spi_port * const spi, const char * const devpath)
+{
+	const int fd = open(devpath, O_RDWR);
+	if (fd < 0)
+		return fd;
+	
+	if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi->speed) < 0
+	 || ioctl(fd, SPI_IOC_WR_MODE, &spi->mode) < 0
+	 || ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &spi->bits) < 0)
+	{
+		close(fd);
+		return -1;
+	}
+	
+	spi->fd = fd;
+	return fd;
+}
+
 #define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
 #define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
 #define OUT_GPIO(g) *(gpio+((g)/10)) |=  (1<<(((g)%10)*3))
 #define OUT_GPIO(g) *(gpio+((g)/10)) |=  (1<<(((g)%10)*3))
 #define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
 #define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
 
 
 #define GPIO_SET *(gpio+7)  // sets   bits which are 1 ignores bits which are 0
 #define GPIO_SET *(gpio+7)  // sets   bits which are 1 ignores bits which are 0
 #define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0
 #define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0
+#define GPIO_LEV *(gpio+13)
+
+void bfg_gpio_setpin_output(const unsigned pin)
+{
+	INP_GPIO(pin);
+	OUT_GPIO(pin);
+}
+
+void bfg_gpio_set_high(const unsigned mask)
+{
+	GPIO_SET = mask;
+}
+
+void bfg_gpio_set_low(const unsigned mask)
+{
+	GPIO_CLR = mask;
+}
+
+unsigned bfg_gpio_get()
+{
+	return GPIO_LEV;
+}
 
 
 // Bit-banging reset, to reset more chips in chain - toggle for longer period... Each 3 reset cycles reset first chip in chain
 // Bit-banging reset, to reset more chips in chain - toggle for longer period... Each 3 reset cycles reset first chip in chain
 static
 static
@@ -196,6 +236,23 @@ bool sys_spi_txrx(struct spi_port *port)
 	return true;
 	return true;
 }
 }
 
 
+bool linux_spi_txrx(struct spi_port * const spi)
+{
+	const void * const wrbuf = spi_gettxbuf(spi);
+	void * const rdbuf = spi_getrxbuf(spi);
+	const size_t bufsz = spi_getbufsz(spi);
+	const int fd = spi->fd;
+	struct spi_ioc_transfer xf = {
+		.tx_buf = (uintptr_t) wrbuf,
+		.rx_buf = (uintptr_t) rdbuf,
+		.len = bufsz,
+		.delay_usecs = spi->delay,
+		.speed_hz = spi->speed,
+		.bits_per_word = spi->bits,
+	};
+	return (ioctl(fd, SPI_IOC_MESSAGE(1), &xf) > 0);
+}
+
 #endif
 #endif
 
 
 static
 static

+ 11 - 0
lowl-spi.h

@@ -10,6 +10,13 @@
 /* Initialize SPI using this function */
 /* Initialize SPI using this function */
 void spi_init(void);
 void spi_init(void);
 
 
+#ifdef HAVE_LINUX_SPI_SPIDEV_H
+extern void bfg_gpio_setpin_output(unsigned pin);
+extern void bfg_gpio_set_high(unsigned mask);
+extern void bfg_gpio_set_low(unsigned mask);
+extern unsigned bfg_gpio_get();
+#endif
+
 /* Do not allocate spi_port on the stack! OS X, at least, has a 512 KB default stack size for secondary threads
 /* Do not allocate spi_port on the stack! OS X, at least, has a 512 KB default stack size for secondary threads
    This includes struct assignments which get allocated on the stack before being assigned to */
    This includes struct assignments which get allocated on the stack before being assigned to */
 struct spi_port {
 struct spi_port {
@@ -29,6 +36,8 @@ struct spi_port {
 	uint16_t delay;
 	uint16_t delay;
 	uint8_t mode;
 	uint8_t mode;
 	uint8_t bits;
 	uint8_t bits;
+	int chipselect;
+	int *chipselect_current;
 };
 };
 
 
 extern struct spi_port *sys_spi;
 extern struct spi_port *sys_spi;
@@ -79,7 +88,9 @@ bool spi_txrx(struct spi_port *port)
 	return port->txrx(port);
 	return port->txrx(port);
 }
 }
 
 
+extern int spi_open(struct spi_port *, const char *);
 extern bool sys_spi_txrx(struct spi_port *);
 extern bool sys_spi_txrx(struct spi_port *);
+extern bool linux_spi_txrx(struct spi_port *);
 
 
 void spi_bfsb_select_bank(int bank);
 void spi_bfsb_select_bank(int bank);
 
 

+ 4 - 0
miner.c

@@ -12068,6 +12068,7 @@ void bfg_atexit(void)
 
 
 extern void bfg_init_threadlocal();
 extern void bfg_init_threadlocal();
 extern void stratumsrv_start();
 extern void stratumsrv_start();
+extern void test_aan_pll(void);
 
 
 int main(int argc, char *argv[])
 int main(int argc, char *argv[])
 {
 {
@@ -12321,6 +12322,9 @@ int main(int argc, char *argv[])
 		test_target();
 		test_target();
 		test_uri_get_param();
 		test_uri_get_param();
 		utf8_test();
 		utf8_test();
+#ifdef USE_JINGTIAN
+		test_aan_pll();
+#endif
 	}
 	}
 
 
 #ifdef HAVE_CURSES
 #ifdef HAVE_CURSES