Browse Source

Merge branch 'master' into hfa

Conflicts:
	Makefile.am
	api.c
	configure.ac
	miner.h
Con Kolivas 12 years ago
parent
commit
c4542f2b96
11 changed files with 803 additions and 102 deletions
  1. 6 6
      Makefile.am
  2. 4 1
      api.c
  3. 0 24
      ccan/opt/helpers.d
  4. 0 26
      ccan/opt/opt.d
  5. 0 18
      ccan/opt/parse.d
  6. 0 22
      ccan/opt/usage.d
  7. 4 1
      cgminer.c
  8. 19 2
      configure.ac
  9. 3 2
      driver-klondike.c
  10. 762 0
      driver-knc-spi-fpga.c
  11. 5 0
      miner.h

+ 6 - 6
Makefile.am

@@ -1,10 +1,10 @@
 
 ACLOCAL_AMFLAGS = -I m4
 
-JANSSON_INCLUDES= -I$(top_srcdir)/compat/jansson-2.5/src
+JANSSON_INCLUDES= -I$(top_builddir)/compat/jansson-2.5/src -I$(top_srcdir)/compat/jansson-2.5/src
 
 if WANT_USBUTILS
-USBUTILS_INCLUDES = -I$(top_srcdir)/compat/libusb-1.0/libusb
+USBUTILS_INCLUDES = -I$(top_builddir)/compat/libusb-1.0/libusb -I$(top_srcdir)/compat/libusb-1.0/libusb
 else
 USBUTILS_INCLUDES =
 endif
@@ -72,6 +72,10 @@ if HAS_AVALON
 cgminer_SOURCES += driver-avalon.c driver-avalon.h
 endif
 
+if HAS_KNC
+cgminer_SOURCES += driver-knc-spi-fpga.c
+endif
+
 if HAS_BFLSC
 cgminer_SOURCES += driver-bflsc.c driver-bflsc.h
 endif
@@ -92,10 +96,6 @@ if HAS_ICARUS
 cgminer_SOURCES += driver-icarus.c
 endif
 
-if HAS_ICARUS
-cgminer_SOURCES += driver-icarus.c
-endif
-
 if HAS_KLONDIKE
 cgminer_SOURCES += driver-klondike.c
 endif

+ 4 - 1
api.c

@@ -29,7 +29,7 @@
 #include "miner.h"
 #include "util.h"
 
-#if defined(USE_BFLSC) || defined(USE_AVALON) || defined(USE_HASHFAST) || defined(USE_BITFURY) || defined(USE_KLONDIKE)
+#if defined(USE_BFLSC) || defined(USE_AVALON) || defined(USE_HASHFAST) || defined(USE_BITFURY) || defined(USE_KLONDIKE) || defined(USE_KNC)
 #define HAVE_AN_ASIC 1
 #endif
 
@@ -191,6 +191,9 @@ static const char *DEVICECODE = ""
 #ifdef USE_MODMINER
 			"MMQ "
 #endif
+#ifdef USE_KNC
+			"KnC "
+#endif
 #ifdef USE_MODMINER
 			"MMQ "
 #endif

+ 0 - 24
ccan/opt/helpers.d

@@ -1,24 +0,0 @@
-ccan/opt/helpers.o: ccan/opt/helpers.c ccan/opt/opt.h \
- ccan/compiler/compiler.h config.h ccan/typesafe_cb/typesafe_cb.h \
- /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdbool.h \
- /usr/include/stdlib.h /usr/include/features.h \
- /usr/include/bits/predefs.h /usr/include/sys/cdefs.h \
- /usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
- /usr/include/gnu/stubs-32.h \
- /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stddef.h \
- /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
- /usr/include/endian.h /usr/include/bits/endian.h \
- /usr/include/bits/byteswap.h /usr/include/xlocale.h \
- /usr/include/sys/types.h /usr/include/bits/types.h \
- /usr/include/bits/typesizes.h /usr/include/time.h \
- /usr/include/sys/select.h /usr/include/bits/select.h \
- /usr/include/bits/sigset.h /usr/include/bits/time.h \
- /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h \
- /usr/include/alloca.h /usr/include/string.h /usr/include/errno.h \
- /usr/include/bits/errno.h /usr/include/linux/errno.h \
- /usr/include/i386-linux-gnu/asm/errno.h /usr/include/asm-generic/errno.h \
- /usr/include/asm-generic/errno-base.h /usr/include/stdio.h \
- /usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
- /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdarg.h \
- /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
- ccan/opt/private.h

+ 0 - 26
ccan/opt/opt.d

@@ -1,26 +0,0 @@
-ccan/opt/opt.o: ccan/opt/opt.c ccan/opt/opt.h ccan/compiler/compiler.h \
- config.h ccan/typesafe_cb/typesafe_cb.h \
- /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdbool.h \
- /usr/include/stdlib.h /usr/include/features.h \
- /usr/include/bits/predefs.h /usr/include/sys/cdefs.h \
- /usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
- /usr/include/gnu/stubs-32.h \
- /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stddef.h \
- /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
- /usr/include/endian.h /usr/include/bits/endian.h \
- /usr/include/bits/byteswap.h /usr/include/xlocale.h \
- /usr/include/sys/types.h /usr/include/bits/types.h \
- /usr/include/bits/typesizes.h /usr/include/time.h \
- /usr/include/sys/select.h /usr/include/bits/select.h \
- /usr/include/bits/sigset.h /usr/include/bits/time.h \
- /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h \
- /usr/include/alloca.h /usr/include/string.h /usr/include/errno.h \
- /usr/include/bits/errno.h /usr/include/linux/errno.h \
- /usr/include/i386-linux-gnu/asm/errno.h /usr/include/asm-generic/errno.h \
- /usr/include/asm-generic/errno-base.h /usr/include/stdio.h \
- /usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
- /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdarg.h \
- /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
- /usr/include/err.h /usr/include/assert.h \
- /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdint.h \
- /usr/include/stdint.h /usr/include/bits/wchar.h ccan/opt/private.h

+ 0 - 18
ccan/opt/parse.d

@@ -1,18 +0,0 @@
-ccan/opt/parse.o: ccan/opt/parse.c ccan/opt/opt.h \
- ccan/compiler/compiler.h config.h ccan/typesafe_cb/typesafe_cb.h \
- /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdbool.h \
- /usr/include/stdlib.h /usr/include/features.h \
- /usr/include/bits/predefs.h /usr/include/sys/cdefs.h \
- /usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
- /usr/include/gnu/stubs-32.h \
- /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stddef.h \
- /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
- /usr/include/endian.h /usr/include/bits/endian.h \
- /usr/include/bits/byteswap.h /usr/include/xlocale.h \
- /usr/include/sys/types.h /usr/include/bits/types.h \
- /usr/include/bits/typesizes.h /usr/include/time.h \
- /usr/include/sys/select.h /usr/include/bits/select.h \
- /usr/include/bits/sigset.h /usr/include/bits/time.h \
- /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h \
- /usr/include/alloca.h /usr/include/string.h /usr/include/assert.h \
- ccan/opt/private.h

+ 0 - 22
ccan/opt/usage.d

@@ -1,22 +0,0 @@
-ccan/opt/usage.o: ccan/opt/usage.c ccan/opt/opt.h \
- ccan/compiler/compiler.h config.h ccan/typesafe_cb/typesafe_cb.h \
- /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdbool.h \
- /usr/include/stdlib.h /usr/include/features.h \
- /usr/include/bits/predefs.h /usr/include/sys/cdefs.h \
- /usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
- /usr/include/gnu/stubs-32.h \
- /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stddef.h \
- /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
- /usr/include/endian.h /usr/include/bits/endian.h \
- /usr/include/bits/byteswap.h /usr/include/xlocale.h \
- /usr/include/sys/types.h /usr/include/bits/types.h \
- /usr/include/bits/typesizes.h /usr/include/time.h \
- /usr/include/sys/select.h /usr/include/bits/select.h \
- /usr/include/bits/sigset.h /usr/include/bits/time.h \
- /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h \
- /usr/include/alloca.h /usr/include/string.h /usr/include/stdio.h \
- /usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
- /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdarg.h \
- /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
- /usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdint.h \
- /usr/include/stdint.h /usr/include/bits/wchar.h ccan/opt/private.h

+ 4 - 1
cgminer.c

@@ -1598,6 +1598,9 @@ extern const char *opt_argv0;
 static char *opt_verusage_and_exit(const char *extra)
 {
 	printf("%s\nBuilt with "
+#ifdef USE_KNC
+		"KnC "
+#endif
 #ifdef USE_AVALON
 		"avalon "
 #endif
@@ -5117,7 +5120,7 @@ static void hashmeter(int thr_id, struct timeval *diff,
 
 	local_secs = (double)total_diff.tv_sec + ((double)total_diff.tv_usec / 1000000.0);
 	decay_time(&total_rolling, local_mhashes_done / local_secs, local_secs);
-	global_hashrate = roundl(total_rolling) * 1000000;
+	global_hashrate = llround(total_rolling) * 1000000;
 
 	timersub(&total_tv_end, &total_tv_start, &total_diff);
 	total_secs = (double)total_diff.tv_sec +

+ 19 - 2
configure.ac

@@ -232,6 +232,17 @@ if test "x$avalon" = xyes; then
 fi
 AM_CONDITIONAL([HAS_AVALON], [test x$avalon = xyes])
 
+knc="no"
+
+AC_ARG_ENABLE([knc],
+	[AC_HELP_STRING([--enable-knc],[Compile support for KnC miners (default disabled)])],
+	[knc=$enableval]
+	)
+if test "x$knc" = xyes; then
+	AC_DEFINE([USE_KNC], [1], [Defined to 1 if KnC miner support is wanted])
+fi
+AM_CONDITIONAL([HAS_KNC], [test x$knc = xyes])
+
 bflsc="no"
 
 AC_ARG_ENABLE([bflsc],
@@ -526,14 +537,14 @@ if test "x$opencl" != xno; then
 
 	else
 		echo "  OpenCL...............: NOT FOUND. GPU mining support DISABLED"
-		if test "x$avalon$bitforce$bitfury$icarus$ztex$modminer$bflsc$hashfast$klondike" = xnonononononononono; then
+		if test "x$avalon$bitforce$bitfury$icarus$modminer$bflsc$hashfast$klondike$knc" = xnonononononononono; then
 			AC_MSG_ERROR([No mining configured in])
 		fi
 		echo "  scrypt...............: Disabled (needs OpenCL)"
 	fi
 else
 	echo "  OpenCL...............: Detection overrided. GPU mining support DISABLED"
-	if test "x$avalon$bitforce$bitfury$icarus$ztex$modminer$bflsc$hashfast$klondike" = xnonononononononono; then
+	if test "x$avalon$bitforce$bitfury$icarus$modminer$bflsc$hashfast$klondike$knc" = xnonononononononono; then
 		AC_MSG_ERROR([No mining configured in])
 	fi
 	echo "  scrypt...............: Disabled (needs OpenCL)"
@@ -562,6 +573,12 @@ else
 	echo "  BFL.ASICs............: Disabled"
 fi
 
+if test "x$knc" = xyes; then
+	echo "  KnC.miners...........: Enabled"
+else
+	echo "  KnC.miners...........: Disabled"
+fi
+
 if test "x$bitforce" = xyes; then
 	echo "  BitForce.FPGAs.......: Enabled"
 else

+ 3 - 2
driver-klondike.c

@@ -821,6 +821,7 @@ static bool klondike_detect_one(struct libusb_device *dev, struct usb_find_devic
 					break;
 				update_usb_stats(klncgpu);
 				applog(LOG_DEBUG, "Klondike cgpu added");
+				rwlock_init(&klninfo->stat_lock);
 				cglock_init(&klninfo->klist_lock);
 				return true;
 			}
@@ -1017,7 +1018,7 @@ static void *klondike_get_replies(void *userdata)
 							if (klninfo->jobque[dev].flushed == false)
 								idle = true;
 							slaves = klninfo->status[0].kline.ws.slavecount;
-							rd_lock(&(klninfo->stat_lock));
+							rd_unlock(&(klninfo->stat_lock));
 							if (idle)
 								applog(LOG_WARNING, "%s%i:%d went idle before work was sent",
 										    klncgpu->drv->name,
@@ -1026,7 +1027,7 @@ static void *klondike_get_replies(void *userdata)
 						}
 						wr_lock(&(klninfo->stat_lock));
 						klninfo->jobque[dev].flushed = false;
-						wr_lock(&(klninfo->stat_lock));
+						wr_unlock(&(klninfo->stat_lock));
 					}
 				case KLN_CMD_STATUS:
 				case KLN_CMD_ABORT:

+ 762 - 0
driver-knc-spi-fpga.c

@@ -0,0 +1,762 @@
+/*
+ * cgminer driver for KnCminer devices
+ *
+ * Copyright 2013 Con Kolivas <kernel@kolivas.org>
+ * Copyright 2013 KnCminer
+ *
+ * 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 <stdlib.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <linux/types.h>
+#include <linux/spi/spidev.h>
+
+#include "logging.h"
+#include "miner.h"
+
+#define MAX_SPIS		1
+#define	MAX_BYTES_IN_SPI_XSFER	4096
+/* /dev/spidevB.C, where B = bus, C = chipselect */
+#define SPI_DEVICE_TEMPLATE	"/dev/spidev%d.%d"
+#define SPI_MODE		(SPI_CPHA | SPI_CPOL | SPI_CS_HIGH)
+#define SPI_BITS_PER_WORD	32
+#define SPI_MAX_SPEED		3000000
+#define SPI_DELAY_USECS		0
+/* Max number of ASICs permitted on one SPI device */
+#define MAX_ASICS		6
+
+/* How many hardware errors in a row before disabling the core */
+#define HW_ERR_LIMIT		10
+#define DISA_ERR_LIMIT		3
+
+#define MAX_ACTIVE_WORKS	(192 * 2 * 6 * 2)
+
+#define WORK_MIDSTATE_WORDS	8
+#define WORK_DATA_WORDS		3
+
+#define WORK_STALE_US		60000000
+
+/* Keep core disabled for no longer than 15 minutes */
+#define CORE_DISA_PERIOD_US	(15 * 60 * 1000000)
+
+struct spidev_context {
+	int fd;
+	uint32_t speed;
+	uint16_t delay;
+	uint8_t mode;
+	uint8_t bits;
+};
+
+struct spi_request {
+#define	CMD_NOP		0
+#define	CMD_GET_VERSION	1
+#define	CMD_SUBMIT_WORK	2
+#define	CMD_FLUSH_QUEUE	3
+
+#define	WORK_ID_MASK	0x7FFF
+
+#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+	uint32_t cmd		:4;
+	uint32_t rsvd		:1; /* set to zero */
+	uint32_t queue_id	:12;
+	uint32_t work_id	:15;
+#else
+	uint32_t work_id	:15;
+	uint32_t queue_id	:12;
+	uint32_t rsvd		:1; /* set to zero */
+	uint32_t cmd		:4;
+#endif
+	uint32_t midstate[WORK_MIDSTATE_WORDS];
+	uint32_t data[WORK_DATA_WORDS];
+};
+
+struct spi_response {
+#define	RESPONSE_TYPE_NOP		0
+#define	RESPONSE_TYPE_NONCE_FOUND	1
+#define	RESPONSE_TYPE_WORK_DONE		2
+#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+	uint32_t type		:2;
+	uint32_t asic		:3;
+	uint32_t queue_id	:12;
+	uint32_t work_id	:15;
+#else
+	uint32_t work_id	:15;
+	uint32_t queue_id	:12;
+	uint32_t asic		:3;
+	uint32_t type		:2;
+#endif
+	uint32_t nonce;
+	uint32_t core;
+};
+
+#define MAX_REQUESTS_IN_BATCH	( MAX_BYTES_IN_SPI_XSFER /	\
+				  sizeof(struct spi_request)	\
+				)
+
+static struct spi_request spi_txbuf[MAX_REQUESTS_IN_BATCH];
+
+#define MAX_RESPONSES_IN_BATCH	( (sizeof(spi_txbuf) - 12) /	\
+				   sizeof(struct spi_response)	\
+				)
+
+struct spi_rx_t {
+#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+	uint32_t rsvd_1			:31;
+	uint32_t response_queue_full	:1;
+#else
+	uint32_t response_queue_full	:1;
+	uint32_t rsvd_1			:31;
+#endif
+#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+	uint32_t rsvd_2			:16;
+	uint32_t works_accepted		:16;
+#else
+	uint32_t works_accepted		:16;
+	uint32_t rsvd_2			:16;
+#endif
+	uint32_t rsvd_3;
+	struct spi_response responses[MAX_RESPONSES_IN_BATCH];
+};
+
+static struct spi_rx_t spi_rxbuf;
+
+struct active_work {
+	struct work *work;
+	uint32_t work_id;
+	struct timeval begin;
+};
+
+struct core_disa_data {
+	struct timeval disa_begin;
+	uint8_t asic;
+	uint8_t core;
+};
+
+struct knc_state {
+	struct spidev_context *ctx;
+	int devices;
+	uint32_t salt;
+	uint32_t next_work_id;
+
+	/* read - last read item, next is at (read + 1) mod BUFSIZE
+	 * write - next write item, last written at (write - 1) mod BUFSIZE
+	 *  When buffer is empty, read + 1 == write
+	 *  Buffer full condition: read == write
+	 */
+	int read_q, write_q;
+#define KNC_QUEUED_BUFFER_SIZE	(MAX_REQUESTS_IN_BATCH + 1)
+	struct active_work queued_fifo[KNC_QUEUED_BUFFER_SIZE];
+
+	int read_a, write_a;
+#define KNC_ACTIVE_BUFFER_SIZE	(MAX_ACTIVE_WORKS + 1)
+	struct active_work active_fifo[KNC_ACTIVE_BUFFER_SIZE];
+
+	uint8_t hwerrs[MAX_ASICS * 256];
+	uint8_t disa_cnt[MAX_ASICS * 256];
+	uint32_t hwerr_work_id[MAX_ASICS * 256];
+	int read_d, write_d;
+#define KNC_DISA_CORES_SIZE	(MAX_ASICS * 256)
+	struct core_disa_data disa_cores_fifo[KNC_DISA_CORES_SIZE];
+
+	pthread_mutex_t lock;
+};
+
+static inline bool knc_queued_fifo_full(struct knc_state *knc)
+{
+	return (knc->read_q == knc->write_q);
+}
+
+static inline bool knc_active_fifo_full(struct knc_state *knc)
+{
+	return (knc->read_a == knc->write_a);
+}
+
+static inline void knc_queued_fifo_inc_idx(int *idx)
+{
+	if (unlikely(*idx >= ((int)KNC_QUEUED_BUFFER_SIZE - 1)))
+		*idx = 0;
+	else
+		++(*idx);
+}
+
+static inline void knc_active_fifo_inc_idx(int *idx)
+{
+	if (unlikely(*idx >= (KNC_ACTIVE_BUFFER_SIZE - 1)))
+		*idx = 0;
+	else
+		++(*idx);
+}
+
+static inline void knc_disa_cores_fifo_inc_idx(int *idx)
+{
+	if (unlikely(*idx >= (KNC_DISA_CORES_SIZE - 1)))
+		*idx = 0;
+	else
+		++(*idx);
+}
+
+/* Find SPI device with index idx, init it */
+static struct spidev_context *spi_new(int idx)
+{
+	struct spidev_context *ctx;
+	char dev_fname[PATH_MAX];
+
+	if (NULL == (ctx = malloc(sizeof(struct spidev_context)))) {
+		applog(LOG_ERR, "KnC spi: Out of memory");
+		goto l_exit_error;
+	}
+	ctx->mode = SPI_MODE;
+	ctx->bits = SPI_BITS_PER_WORD;
+	ctx->speed = SPI_MAX_SPEED;
+	ctx->delay = SPI_DELAY_USECS;
+
+	ctx->fd = -1;
+
+	sprintf(dev_fname, SPI_DEVICE_TEMPLATE,
+		idx, /* bus */
+		0    /* chipselect */
+	       );
+	if (0 > (ctx->fd = open(dev_fname, O_RDWR))) {
+		applog(LOG_ERR, "KnC spi: Can not open SPI device %s: %m",
+		       dev_fname);
+		goto l_free_exit_error;
+	}
+
+	/*
+	 * spi mode
+	 */
+	if (0 > ioctl(ctx->fd, SPI_IOC_WR_MODE, &ctx->mode))
+		goto l_ioctl_error;
+	if (0 > ioctl(ctx->fd, SPI_IOC_RD_MODE, &ctx->mode))
+		goto l_ioctl_error;
+
+	/*
+	 * bits per word
+	 */
+	if (0 > ioctl(ctx->fd, SPI_IOC_WR_BITS_PER_WORD, &ctx->bits))
+		goto l_ioctl_error;
+	if (0 > ioctl(ctx->fd, SPI_IOC_RD_BITS_PER_WORD, &ctx->bits))
+		goto l_ioctl_error;
+
+	/*
+	 * max speed hz
+	 */
+	if (0 > ioctl(ctx->fd, SPI_IOC_WR_MAX_SPEED_HZ, &ctx->speed))
+		goto l_ioctl_error;
+	if (0 > ioctl(ctx->fd, SPI_IOC_RD_MAX_SPEED_HZ, &ctx->speed))
+		goto l_ioctl_error;
+
+	applog(LOG_INFO, "KnC spi: device %s uses mode %hhu, bits %hhu, speed %u",
+	       dev_fname, ctx->mode, ctx->bits, ctx->speed);
+
+	return ctx;
+
+l_ioctl_error:
+	applog(LOG_ERR, "KnC spi: ioctl error on SPI device %s: %m", dev_fname);
+	close(ctx->fd);
+l_free_exit_error:
+	free(ctx);
+l_exit_error:
+	return NULL;
+}
+
+static void spi_free(struct spidev_context *ctx)
+{
+	if (NULL == ctx)
+		return;
+
+	close(ctx->fd);
+	free(ctx);
+}
+
+static int spi_transfer(struct spidev_context *ctx, uint8_t *txbuf,
+			uint8_t *rxbuf, int len)
+{
+	struct spi_ioc_transfer xfr;
+	int ret;
+
+	memset(rxbuf, 0xff, len);
+
+	ret = len;
+
+	xfr.tx_buf = (unsigned long)txbuf;
+	xfr.rx_buf = (unsigned long)rxbuf;
+	xfr.len = len;
+	xfr.speed_hz = ctx->speed;
+	xfr.delay_usecs = ctx->delay;
+	xfr.bits_per_word = ctx->bits;
+	xfr.cs_change = 0;
+	xfr.pad = 0;
+
+	if (1 > (ret = ioctl(ctx->fd, SPI_IOC_MESSAGE(1), &xfr)))
+		applog(LOG_ERR, "KnC spi xfer: ioctl error on SPI device: %m");
+
+	return ret;
+}
+
+static void disable_core(uint8_t asic, uint8_t core)
+{
+	char str[256];
+
+	snprintf(str, sizeof(str), "i2cset -y 2 0x2%hhu %hhu 0", asic, core);
+	if (0 != WEXITSTATUS(system(str)))
+		applog(LOG_ERR, "KnC: system call failed");
+}
+
+static void enable_core(uint8_t asic, uint8_t core)
+{
+	char str[256];
+
+	snprintf(str, sizeof(str), "i2cset -y 2 0x2%hhu %hhu 1", asic, core);
+	if (0 != WEXITSTATUS(system(str)))
+		applog(LOG_ERR, "KnC: system call failed");
+}
+
+static int64_t timediff(const struct timeval *a, const struct timeval *b)
+{
+	struct timeval diff;
+
+	timersub(a, b, &diff);
+
+	return diff.tv_sec * 1000000 + diff.tv_usec;
+}
+
+static void knc_check_disabled_cores(struct knc_state *knc)
+{
+	struct core_disa_data *core;
+	int next_read_d, cidx;
+	struct timeval now;
+	int64_t us;
+
+	next_read_d = knc->read_d;
+	knc_disa_cores_fifo_inc_idx(&next_read_d);
+	if (next_read_d == knc->write_d)
+		return; /* queue empty */
+
+	core = &knc->disa_cores_fifo[next_read_d];
+	gettimeofday(&now, NULL);
+	us = timediff(&now, &core->disa_begin);
+	if ((us >= 0) && (us < CORE_DISA_PERIOD_US))
+		return; /* latest disabled core still not expired */
+
+	cidx = core->asic * 256 + core->core;
+	enable_core(core->asic, core->core);
+	knc->hwerrs[cidx] = 0;
+	applog(LOG_NOTICE,
+	       "KnC: core %u-%u was enabled back from disabled state",
+	       core->asic, core->core);
+	knc->read_d = next_read_d;
+}
+
+static void knc_work_from_queue_to_spi(struct knc_state *knc,
+				       struct active_work *q_work,
+				       struct spi_request *spi_req)
+{
+	uint32_t *buf_from, *buf_to;
+	int i;
+
+	spi_req->cmd = CMD_SUBMIT_WORK;
+	spi_req->queue_id = 0; /* at the moment we have one and only queue #0 */
+	spi_req->work_id = (knc->next_work_id ^ knc->salt) & WORK_ID_MASK;
+	q_work->work_id = spi_req->work_id;
+	++(knc->next_work_id);
+	buf_to = spi_req->midstate;
+	buf_from = (uint32_t *)q_work->work->midstate;
+
+	for (i = 0; i < WORK_MIDSTATE_WORDS; ++i)
+		buf_to[i] = le32toh(buf_from[8 - i - 1]);
+	buf_to = spi_req->data;
+	buf_from = (uint32_t *)&(q_work->work->data[16 * 4]);
+
+	for (i = 0; i < WORK_DATA_WORDS; ++i)
+		buf_to[i] = le32toh(buf_from[3 - i - 1]);
+}
+
+static int64_t knc_process_response(struct thr_info *thr, struct cgpu_info *cgpu,
+				    struct spi_rx_t *rxbuf)
+{
+	struct knc_state *knc = cgpu->device_data;
+	int submitted, successful, i, num_sent;
+	int next_read_q, next_read_a;
+	struct timeval now;
+	struct work *work;
+	int64_t us;
+
+	num_sent = knc->write_q - knc->read_q - 1;
+	if (knc->write_q <= knc->read_q)
+		num_sent += KNC_QUEUED_BUFFER_SIZE;
+
+	/* Actually process SPI response */
+	if (rxbuf->works_accepted) {
+		applog(LOG_DEBUG, "KnC spi: raw response %08X %08X",
+		       ((uint32_t *)rxbuf)[0], ((uint32_t *)rxbuf)[1]);
+		applog(LOG_DEBUG,
+		       "KnC spi: response, accepted %u (from %u), full %u",
+		       rxbuf->works_accepted, num_sent,
+		       rxbuf->response_queue_full);
+	}
+	/* move works_accepted number of items from queued_fifo to active_fifo */
+	gettimeofday(&now, NULL);
+	submitted = 0;
+
+	for (i = 0; i < rxbuf->works_accepted; ++i) {
+		next_read_q = knc->read_q;
+		knc_queued_fifo_inc_idx(&next_read_q);
+		if ((next_read_q == knc->write_q) || knc_active_fifo_full(knc))
+			break;
+
+		memcpy(&knc->active_fifo[knc->write_a],
+		       &knc->queued_fifo[next_read_q],
+		       sizeof(struct active_work));
+		knc->active_fifo[knc->write_a].begin = now;
+		knc->queued_fifo[next_read_q].work = NULL;
+		knc->read_q = next_read_q;
+		knc_active_fifo_inc_idx(&knc->write_a);
+		++submitted;
+	}
+	if (submitted != rxbuf->works_accepted) {
+		applog(LOG_ERR,
+		       "KnC: accepted by FPGA %u works, but only %d submitted",
+		       rxbuf->works_accepted, submitted);
+	}
+
+	/* check for completed works and calculated nonces */
+	gettimeofday(&now, NULL);
+	successful = 0;
+
+	for (i = 0; i < (int)MAX_RESPONSES_IN_BATCH; ++i) {
+		if ((rxbuf->responses[i].type != RESPONSE_TYPE_NONCE_FOUND) &&
+		    (rxbuf->responses[i].type != RESPONSE_TYPE_WORK_DONE))
+			continue;
+
+		applog(LOG_DEBUG, "KnC spi: raw response %08X %08X",
+		       ((uint32_t *)&rxbuf->responses[i])[0],
+		       ((uint32_t *)&rxbuf->responses[i])[1]);
+		applog(LOG_DEBUG, "KnC spi: response, T:%u C:%u-%u Q:%u W:%u",
+		       rxbuf->responses[i].type,
+		       rxbuf->responses[i].asic, rxbuf->responses[i].core,
+		       rxbuf->responses[i].queue_id,
+		       rxbuf->responses[i].work_id);
+		/* Find active work with matching ID */
+		next_read_a = knc->read_a;
+		knc_active_fifo_inc_idx(&next_read_a);
+
+		while (next_read_a != knc->write_a) {
+			if (knc->active_fifo[next_read_a].work_id ==
+			    rxbuf->responses[i].work_id)
+				break;
+
+			/* check for stale works */
+			us = timediff(&now,
+				      &knc->active_fifo[next_read_a].begin);
+			if ((us < 0) || (us >= WORK_STALE_US)) {
+				applog(LOG_DEBUG,
+				       "KnC spi: remove stale work %u",
+				       knc->active_fifo[next_read_a].work_id);
+				work = knc->active_fifo[next_read_a].work;
+				knc_active_fifo_inc_idx(&knc->read_a);
+				work_completed(cgpu, work);
+				if (next_read_a != knc->read_a) {
+					memcpy(&(knc->active_fifo[next_read_a]),
+					       &(knc->active_fifo[knc->read_a]),
+					       sizeof(struct active_work));
+				}
+				knc->active_fifo[knc->read_a].work = NULL;
+			}
+
+			knc_active_fifo_inc_idx(&next_read_a);
+		}
+		if (next_read_a == knc->write_a)
+			continue;
+
+		applog(LOG_DEBUG, "KnC spi: response work %u found",
+		       rxbuf->responses[i].work_id);
+		work = knc->active_fifo[next_read_a].work;
+
+		if (rxbuf->responses[i].type == RESPONSE_TYPE_NONCE_FOUND) {
+			if (NULL != thr) {
+				int cidx = rxbuf->responses[i].asic * 256 +
+					   rxbuf->responses[i].core;
+
+				if (submit_nonce(thr, work,
+						 rxbuf->responses[i].nonce)) {
+					if (cidx < (int)sizeof(knc->hwerrs)) {
+						knc->hwerrs[cidx] = 0;
+						knc->disa_cnt[cidx] = 0;
+						knc->hwerr_work_id[cidx] = 0xFFFFFFFF;
+					}
+					successful++;
+				} else  {
+					if ((cidx < (int)sizeof(knc->hwerrs)) &&
+					    (knc->hwerr_work_id[cidx] != rxbuf->responses[i].work_id)) {
+						knc->hwerr_work_id[cidx] = rxbuf->responses[i].work_id;
+						if (++(knc->hwerrs[cidx]) >= HW_ERR_LIMIT) {
+						    struct core_disa_data *core;
+
+						    core = &knc->disa_cores_fifo[knc->write_d];
+						    core->disa_begin = now;
+						    core->asic = rxbuf->responses[i].asic;
+						    core->core = rxbuf->responses[i].core;
+						    disable_core(core->asic, core->core);
+						    if (++(knc->disa_cnt[cidx]) >= DISA_ERR_LIMIT) {
+							    applog(LOG_WARNING,
+			"KnC: core %u-%u was disabled permanently", core->asic, core->core);
+						    } else {
+							    applog(LOG_WARNING,
+			"KnC: core %u-%u was disabled due to %u HW errors in a row",
+								   core->asic, core->core, HW_ERR_LIMIT);
+							    knc_disa_cores_fifo_inc_idx(&knc->write_d);
+						    }
+						}
+					}
+				};
+			}
+			continue;
+		}
+
+		/* Work completed */
+		knc_active_fifo_inc_idx(&knc->read_a);
+		work_completed(cgpu, work);
+		if (next_read_a != knc->read_a) {
+			memcpy(&(knc->active_fifo[next_read_a]),
+			       &(knc->active_fifo[knc->read_a]),
+			       sizeof(struct active_work));
+		}
+		knc->active_fifo[knc->read_a].work = NULL;
+	}
+
+	return ((uint64_t)successful) * 0x100000000UL;
+}
+
+/* Send flush command via SPI */
+static int _internal_knc_flush_fpga(struct knc_state *knc)
+{
+	int len;
+
+	spi_txbuf[0].cmd = CMD_FLUSH_QUEUE;
+	spi_txbuf[0].queue_id = 0; /* at the moment we have one and only queue #0 */
+	len = spi_transfer(knc->ctx, (uint8_t *)spi_txbuf,
+			   (uint8_t *)&spi_rxbuf, sizeof(struct spi_request));
+	if (len != sizeof(struct spi_request))
+		return -1;
+
+	len /= sizeof(struct spi_response);
+
+	return len;
+}
+
+static bool knc_detect_one(struct spidev_context *ctx)
+{
+	/* Scan device for ASICs */
+	int chip_id, devices = 0;
+	struct cgpu_info *cgpu;
+	struct knc_state *knc;
+
+	for (chip_id = 0; chip_id < MAX_ASICS; ++chip_id) {
+		/* TODO: perform the ASIC test/detection */
+		++devices;
+	}
+
+	if (!devices) {
+		applog(LOG_INFO, "SPI detected, but not KnCminer ASICs");
+		return false;
+	}
+
+	applog(LOG_INFO, "Found a KnC miner with %d ASICs", devices);
+
+	cgpu = calloc(1, sizeof(*cgpu));
+	knc = calloc(1, sizeof(*knc));
+	if (!cgpu || !knc) {
+		applog(LOG_ERR, "KnC miner detected, but failed to allocate memory");
+		return false;
+	}
+
+	knc->ctx = ctx;
+	knc->devices = devices;
+	knc->read_q = 0;
+	knc->write_q = 1;
+	knc->read_a = 0;
+	knc->write_a = 1;
+	knc->read_d = 0;
+	knc->write_d = 1;
+	knc->salt = rand();
+	mutex_init(&knc->lock);
+
+	memset(knc->hwerr_work_id, 0xFF, sizeof(knc->hwerr_work_id));
+
+	_internal_knc_flush_fpga(knc);
+
+	cgpu->drv = &knc_drv;
+	cgpu->name = "KnCminer";
+	cgpu->threads = 1;	// .. perhaps our number of devices?
+
+	cgpu->device_data = knc;
+	add_cgpu(cgpu);
+
+	return true;
+}
+
+// http://www.concentric.net/~Ttwang/tech/inthash.htm
+static unsigned long mix(unsigned long a, unsigned long b, unsigned long c)
+{
+	a = a - b;  a = a - c;  a = a ^ (c >> 13);
+	b = b - c;  b = b - a;  b = b ^ (a << 8);
+	c = c - a;  c = c - b;  c = c ^ (b >> 13);
+	a = a - b;  a = a - c;  a = a ^ (c >> 12);
+	b = b - c;  b = b - a;  b = b ^ (a << 16);
+	c = c - a;  c = c - b;  c = c ^ (b >> 5);
+	a = a - b;  a = a - c;  a = a ^ (c >> 3);
+	b = b - c;  b = b - a;  b = b ^ (a << 10);
+	c = c - a;  c = c - b;  c = c ^ (b >> 15);
+
+	return c;
+}
+
+/* Probe devices and register with add_cgpu */
+void knc_detect(bool __maybe_unused hotplug)
+{
+	int idx;
+
+	srand(mix(clock(), time(NULL), getpid()));
+
+	/* Loop through all possible SPI interfaces */
+	for (idx = 0; idx < MAX_SPIS; ++idx) {
+		struct spidev_context *ctx = spi_new(idx + 1);
+
+		if (ctx != NULL) {
+			if (!knc_detect_one(ctx))
+				spi_free(ctx);
+		}
+	}
+}
+
+/* return value is number of nonces that have been checked since
+ * previous call
+ */
+static int64_t knc_scanwork(struct thr_info *thr)
+{
+	struct cgpu_info *cgpu = thr->cgpu;
+	struct knc_state *knc = cgpu->device_data;
+	int len, num, next_read_q;
+	int64_t ret;
+
+	applog(LOG_DEBUG, "KnC running scanwork");
+
+	knc_check_disabled_cores(knc);
+
+	/* Prepare tx buffer */
+	memset(spi_txbuf, 0, sizeof(spi_txbuf));
+	num = 0;
+
+	mutex_lock(&knc->lock);
+	next_read_q = knc->read_q;
+	knc_queued_fifo_inc_idx(&next_read_q);
+
+	while (next_read_q != knc->write_q) {
+		knc_work_from_queue_to_spi(knc, &knc->queued_fifo[next_read_q],
+					   &spi_txbuf[num]);
+		knc_queued_fifo_inc_idx(&next_read_q);
+		++num;
+	}
+	/* knc->read_q is advanced in knc_process_response, not here */
+
+	len = spi_transfer(knc->ctx, (uint8_t *)spi_txbuf,
+			   (uint8_t *)&spi_rxbuf, sizeof(spi_txbuf));
+	if (len != sizeof(spi_rxbuf)) {
+		ret = -1;
+		goto out_unlock;
+	}
+
+	applog(LOG_DEBUG, "KnC spi: %d works in request", num);
+
+	ret = knc_process_response(thr, cgpu, &spi_rxbuf);
+out_unlock:
+	mutex_unlock(&knc->lock);
+
+	return ret;
+}
+
+static bool knc_queue_full(struct cgpu_info *cgpu)
+{
+	struct knc_state *knc = cgpu->device_data;
+	int queue_full = false;
+	struct work *work;
+
+	applog(LOG_DEBUG, "KnC running queue full");
+
+	mutex_lock(&knc->lock);
+	if (knc_queued_fifo_full(knc)) {
+		queue_full = true;
+		goto out_unlock;
+	}
+	work = get_queued(cgpu);
+	if (!work)
+		goto out_unlock;
+	knc->queued_fifo[knc->write_q].work = work;
+	knc_queued_fifo_inc_idx(&(knc->write_q));
+	if (knc_queued_fifo_full(knc))
+		queue_full = true;
+out_unlock:
+	mutex_unlock(&knc->lock);
+
+	return queue_full;
+}
+
+static void knc_flush_work(struct cgpu_info *cgpu)
+{
+	struct knc_state *knc = cgpu->device_data;
+	int len, next_read_q, next_read_a;
+	struct work *work;
+
+	applog(LOG_ERR, "KnC running flushwork");
+
+	mutex_lock(&knc->lock);
+	/* Drain queued works */
+	next_read_q = knc->read_q;
+	knc_queued_fifo_inc_idx(&next_read_q);
+
+	while (next_read_q != knc->write_q) {
+		work = knc->queued_fifo[next_read_q].work;
+		work_completed(cgpu, work);
+		knc->queued_fifo[next_read_q].work = NULL;
+		knc->read_q = next_read_q;
+		knc_queued_fifo_inc_idx(&next_read_q);
+	}
+
+	/* Drain active works */
+	next_read_a = knc->read_a;
+	knc_active_fifo_inc_idx(&next_read_a);
+
+	while (next_read_a != knc->write_a) {
+		work = knc->active_fifo[next_read_a].work;
+		work_completed(cgpu, work);
+		knc->active_fifo[next_read_a].work = NULL;
+		knc->read_a = next_read_a;
+		knc_active_fifo_inc_idx(&next_read_a);
+	}
+
+	len = _internal_knc_flush_fpga(knc);
+	if (len > 0)
+		knc_process_response(NULL, cgpu, &spi_rxbuf);
+	mutex_unlock(&knc->lock);
+}
+
+struct device_drv knc_drv = {
+	.drv_id = DRIVER_knc,
+	.dname = "KnCminer",
+	.name = "KnC",
+	.drv_detect = knc_detect,	// Probe for devices, add with add_cgpu
+
+	.hash_work = hash_queued_work,
+	.scanwork = knc_scanwork,
+	.queue_full = knc_queue_full,
+	.flush_work = knc_flush_work,
+};

+ 5 - 0
miner.h

@@ -223,8 +223,12 @@ static inline int fsync (int fd)
 #define semtimedop(SEM, SOPS, VAL, TIMEOUT) semop(SEM, SOPS, VAL)
 #endif
 
+#ifndef MIN
 #define MIN(x, y)	((x) > (y) ? (y) : (x))
+#endif
+#ifndef MAX
 #define MAX(x, y)	((x) > (y) ? (x) : (y))
+#endif
 
 /* Put avalon last to make it the last device it tries to detect to prevent it
  * trying to claim same chip but different devices. Adding a device here will
@@ -240,6 +244,7 @@ static inline int fsync (int fd)
 	DRIVER_ADD_COMMAND(bitfury) \
 	DRIVER_ADD_COMMAND(hashfast) \
 	DRIVER_ADD_COMMAND(klondike) \
+	DRIVER_ADD_COMMAND(knc) \
 	DRIVER_ADD_COMMAND(avalon)
 
 #define DRIVER_PARSE_COMMANDS(DRIVER_ADD_COMMAND) \