Browse Source

Merge pull request #501 from kanoi/cmr

Cairnsmore1 support clock/identify commands via the API
kanoi 12 years ago
parent
commit
a715d27a98
2 changed files with 224 additions and 28 deletions
  1. 7 6
      API-README
  2. 217 22
      driver-icarus.c

+ 7 - 6
API-README

@@ -277,13 +277,13 @@ The list of requests - a (*) means it requires privileged access - and replies:
                none           There is no reply section just the STATUS section
                               stating the results of the identify request
                               This is only available if PGA mining is enabled
-                              and currently only BFL singles support this
-                              command
+                              and currently only BFL singles and Cairnsmore1's
+                              with the appropriate firmware support this command
                               On a BFL single it will flash the led on the front
                               of the device for appoximately 4s
-                              All other non BFL PGA devices will return a
+                              All other non BFL,ICA PGA devices will return a
                               warning status message stating that they dont
-                              support it
+                              support it. Non-CMR ICAs will ignore the command.
                               This adds a 4s delay to the BFL share being
                               processed so you may get a message stating that
                               procssing took longer than 7000ms if the request
@@ -364,6 +364,7 @@ The list of requests - a (*) means it requires privileged access - and replies:
 
                               The current options are:
                                MMQ opt=clock val=160 to 230 (a multiple of 2)
+                               CMR opt=clock val=100 to 220
 
  zero|Which,true/false (*)
                none           There is no reply section just the STATUS section
@@ -433,8 +434,8 @@ The list of requests - a (*) means it requires privileged access - and replies:
                               help message about the options available
 
                               The current options are:
-                               AVA+BTB opt=freq val=256 to 450 - chip frequency
-                               BTB opt=millivolts val=1000 to 1310 - corevoltage
+                               AVA+BTB opt=freq val=256 to 1024 - chip frequency
+                               BTB opt=millivolts val=1000 to 1400 - corevoltage
 
 When you enable, disable or restart a GPU, PGA or ASC, you will also get
 Thread messages in the cgminer status window

+ 217 - 22
driver-icarus.c

@@ -108,6 +108,8 @@ ASSERT1(sizeof(uint32_t) == 4);
 #define CAIRNSMORE2_HASH_TIME 0.0000000066600
 #define NANOSEC 1000000000.0
 
+#define CAIRNSMORE2_INTS 4
+
 // Icarus Rev3 doesn't send a completion message when it finishes
 // the full nonce range, so to avoid being idle we must abort the
 // work (by starting a new work item) shortly before it finishes
@@ -178,6 +180,7 @@ static const char *MODE_VALUE_STR = "value";
 static const char *MODE_UNKNOWN_STR = "unknown";
 
 struct ICARUS_INFO {
+	enum sub_ident ident;
 	int intinfo;
 
 	// time to calculate the golden_ob
@@ -216,17 +219,43 @@ struct ICARUS_INFO {
 	int fpga_count;
 	uint32_t nonce_mask;
 
-	bool initialised;
+	uint8_t cmr2_speed;
+	bool speed_next_work;
+	bool flash_next_work;
 };
 
 #define ICARUS_MIDSTATE_SIZE 32
-#define ICARUS_UNUSED_SIZE 20
+#define ICARUS_UNUSED_SIZE 16
 #define ICARUS_WORK_SIZE 12
 
 #define ICARUS_WORK_DATA_OFFSET 64
 
+#define ICARUS_CMR2_SPEED_FACTOR 2.5
+#define ICARUS_CMR2_SPEED_MIN_INT 100
+#define ICARUS_CMR2_SPEED_DEF_INT 180
+#define ICARUS_CMR2_SPEED_MAX_INT 220
+#define CMR2_INT_TO_SPEED(_speed) ((uint8_t)((float)_speed / ICARUS_CMR2_SPEED_FACTOR))
+#define ICARUS_CMR2_SPEED_MIN CMR2_INT_TO_SPEED(ICARUS_CMR2_SPEED_MIN_INT)
+#define ICARUS_CMR2_SPEED_DEF CMR2_INT_TO_SPEED(ICARUS_CMR2_SPEED_DEF_INT)
+#define ICARUS_CMR2_SPEED_MAX CMR2_INT_TO_SPEED(ICARUS_CMR2_SPEED_MAX_INT)
+#define ICARUS_CMR2_SPEED_INC 1
+#define ICARUS_CMR2_SPEED_DEC -1
+#define ICARUS_CMR2_SPEED_FAIL -10
+
+#define ICARUS_CMR2_PREFIX ((uint8_t)0xB7)
+#define ICARUS_CMR2_CMD_SPEED ((uint8_t)0)
+#define ICARUS_CMR2_CMD_FLASH ((uint8_t)1)
+#define ICARUS_CMR2_DATA_FLASH_OFF ((uint8_t)0)
+#define ICARUS_CMR2_DATA_FLASH_ON ((uint8_t)1)
+#define ICARUS_CMR2_CHECK ((uint8_t)0x6D)
+
 struct ICARUS_WORK {
 	uint8_t midstate[ICARUS_MIDSTATE_SIZE];
+	// These 4 bytes are for CMR2 bitstreams that handle MHz adjustment
+	uint8_t check;
+	uint8_t data;
+	uint8_t cmd;
+	uint8_t prefix;
 	uint8_t unused[ICARUS_UNUSED_SIZE];
 	uint8_t work[ICARUS_WORK_SIZE];
 };
@@ -423,8 +452,6 @@ static void icarus_initialise(struct cgpu_info *icarus, int baud)
 			quit(1, "icarus_intialise() called with invalid %s cgid %i ident=%d",
 				icarus->drv->name, icarus->cgminer_id, ident);
 	}
-
-	info->initialised = true;
 }
 
 static void rev(unsigned char *s, size_t l)
@@ -820,9 +847,10 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices
 	char *nonce_hex;
 	int baud, uninitialised_var(work_division), uninitialised_var(fpga_count);
 	struct cgpu_info *icarus;
-	int ret, err, amount, tries;
-	enum sub_ident ident;
+	int ret, err, amount, tries, i;
 	bool ok;
+	bool cmr2_ok[CAIRNSMORE2_INTS];
+	int cmr2_count;
 
 	if ((sizeof(workdata) << 1) != (sizeof(golden_ob) - 1))
 		quithere(1, "Data and golden_ob sizes don't match");
@@ -843,8 +871,8 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices
 		quit(1, "Failed to malloc ICARUS_INFO");
 	icarus->device_data = (void *)info;
 
-	ident = usb_ident(icarus);
-	switch (ident) {
+	info->ident = usb_ident(icarus);
+	switch (info->ident) {
 		case IDENT_ICA:
 		case IDENT_BLT:
 		case IDENT_LLT:
@@ -853,19 +881,32 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices
 			info->timeout = ICARUS_WAIT_TIMEOUT;
 			break;
 		case IDENT_CMR2:
+			if (found->intinfo_count != CAIRNSMORE2_INTS) {
+				quithere(1, "CMR2 Interface count (%d) isn't expected: %d",
+						found->intinfo_count,
+						CAIRNSMORE2_INTS);
+			}
 			info->timeout = ICARUS_CMR2_TIMEOUT;
+			cmr2_count = 0;
+			for (i = 0; i < CAIRNSMORE2_INTS; i++)
+				cmr2_ok[i] = false;
 			break;
 		default:
 			quit(1, "%s icarus_detect_one() invalid %s ident=%d",
-				icarus->drv->dname, icarus->drv->dname, ident);
+				icarus->drv->dname, icarus->drv->dname, info->ident);
 	}
 
+// For CMR2 test each USB Interface
+
+cmr2_retry:
+
 	tries = 2;
 	ok = false;
 	while (!ok && tries-- > 0) {
 		icarus_initialise(icarus, baud);
 
-		err = usb_write(icarus, (void *)(&workdata), sizeof(workdata), &amount, C_SENDTESTWORK);
+		err = usb_write_ii(icarus, info->intinfo,
+				   (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK);
 
 		if (err != LIBUSB_SUCCESS || amount != sizeof(workdata))
 			continue;
@@ -879,7 +920,7 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices
 		if (strncmp(nonce_hex, golden_nonce, 8) == 0)
 			ok = true;
 		else {
-			if (tries < 0) {
+			if (tries < 0 && info->ident != IDENT_CMR2) {
 				applog(LOG_ERR,
 					"Icarus Detect: "
 					"Test failed at %s: get %s, should: %s",
@@ -889,13 +930,50 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices
 		free(nonce_hex);
 	}
 
-	if (!ok)
-		goto unshin;
+	if (!ok) {
+		if (info->ident != IDENT_CMR2)
+			goto unshin;
+
+		if (info->intinfo < CAIRNSMORE2_INTS-1) {
+			info->intinfo++;
+			goto cmr2_retry;
+		}
+	} else {
+		if (info->ident == IDENT_CMR2) {
+			applog(LOG_DEBUG,
+				"Icarus Detect: "
+				"Test succeeded at %s i%d: got %s",
+					icarus->device_path, info->intinfo, golden_nonce);
+
+			cmr2_ok[info->intinfo] = true;
+			cmr2_count++;
+			if (info->intinfo < CAIRNSMORE2_INTS-1) {
+				info->intinfo++;
+				goto cmr2_retry;
+			}
+		}
+	}
 
-	applog(LOG_DEBUG,
-		"Icarus Detect: "
-		"Test succeeded at %s: got %s",
-			icarus->device_path, golden_nonce);
+	if (info->ident == IDENT_CMR2) {
+		if (cmr2_count == 0) {
+			applog(LOG_ERR,
+				"Icarus Detect: Test failed at %s: for all %d CMR2 Interfaces",
+				icarus->device_path, CAIRNSMORE2_INTS);
+			goto unshin;
+		}
+
+		// set the interface to the first one that succeeded
+		for (i = 0; i < CAIRNSMORE2_INTS; i++)
+			if (cmr2_ok[i]) {
+				info->intinfo = i;
+				break;
+			}
+	} else {
+		applog(LOG_DEBUG,
+			"Icarus Detect: "
+			"Test succeeded at %s: got %s",
+				icarus->device_path, golden_nonce);
+	}
 
 	/* We have a real Icarus! */
 	if (!add_cgpu(icarus))
@@ -906,6 +984,18 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices
 	applog(LOG_INFO, "%s%d: Found at %s",
 		icarus->drv->name, icarus->device_id, icarus->device_path);
 
+	if (info->ident == IDENT_CMR2) {
+		applog(LOG_INFO, "%s%d: with %d Interface%s",
+				icarus->drv->name, icarus->device_id,
+				cmr2_count, cmr2_count > 1 ? "s" : "");
+
+		// Assume 1 or 2 are running FPGA pairs
+		if (cmr2_count < 3) {
+			work_division = fpga_count = 2;
+			info->Hs /= 2;
+		}
+	}
+
 	applog(LOG_DEBUG, "%s%d: Init baud=%d work_division=%d fpga_count=%d",
 		icarus->drv->name, icarus->device_id, baud, work_division, fpga_count);
 
@@ -919,12 +1009,15 @@ static bool icarus_detect_one(struct libusb_device *dev, struct usb_find_devices
 
 	set_timing_mode(this_option_offset, icarus);
 	
-	if (usb_ident(icarus) == IDENT_CMR2) {
+	if (info->ident == IDENT_CMR2) {
 		int i;
-		for (i = 1; i < icarus->usbdev->found->intinfo_count; i++) {
+		for (i = info->intinfo + 1; i < icarus->usbdev->found->intinfo_count; i++) {
 			struct cgpu_info *cgtmp;
 			struct ICARUS_INFO *intmp;
 
+			if (!cmr2_ok[i])
+				continue;
+
 			cgtmp = usb_copy_cgpu(icarus);
 			if (!cgtmp) {
 				applog(LOG_ERR, "%s%d: Init failed initinfo %d",
@@ -984,6 +1077,45 @@ static bool icarus_prepare(__maybe_unused struct thr_info *thr)
 	return true;
 }
 
+static void cmr2_command(struct cgpu_info *icarus, uint8_t cmd, uint8_t data)
+{
+	struct ICARUS_INFO *info = (struct ICARUS_INFO *)(icarus->device_data);
+	struct ICARUS_WORK workdata;
+	int amount;
+
+	memset((void *)(&workdata), 0, sizeof(workdata));
+
+	workdata.prefix = ICARUS_CMR2_PREFIX;
+	workdata.cmd = cmd;
+	workdata.data = data;
+	workdata.check = workdata.data ^ workdata.cmd ^ workdata.prefix ^ ICARUS_CMR2_CHECK;
+
+	usb_write_ii(icarus, info->intinfo, (char *)(&workdata), sizeof(workdata), &amount, C_SENDWORK);
+}
+
+static void cmr2_commands(struct cgpu_info *icarus)
+{
+	struct ICARUS_INFO *info = (struct ICARUS_INFO *)(icarus->device_data);
+
+	if (info->speed_next_work) {
+		info->speed_next_work = false;
+		cmr2_command(icarus, ICARUS_CMR2_CMD_SPEED, info->cmr2_speed);
+		return;
+	}
+
+	if (info->flash_next_work) {
+		info->flash_next_work = false;
+		cmr2_command(icarus, ICARUS_CMR2_CMD_FLASH, ICARUS_CMR2_DATA_FLASH_ON);
+		cgsleep_ms(250);
+		cmr2_command(icarus, ICARUS_CMR2_CMD_FLASH, ICARUS_CMR2_DATA_FLASH_OFF);
+		cgsleep_ms(250);
+		cmr2_command(icarus, ICARUS_CMR2_CMD_FLASH, ICARUS_CMR2_DATA_FLASH_ON);
+		cgsleep_ms(250);
+		cmr2_command(icarus, ICARUS_CMR2_CMD_FLASH, ICARUS_CMR2_DATA_FLASH_OFF);
+		return;
+	}
+}
+
 static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 				__maybe_unused int64_t max_nonce)
 {
@@ -1014,9 +1146,6 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	if (icarus->usbinfo.nodev)
 		return -1;
 
-	if (!info->initialised)
-		icarus_initialise(icarus, info->baud);
-
 	elapsed.tv_sec = elapsed.tv_usec = 0;
 
 	memset((void *)(&workdata), 0, sizeof(workdata));
@@ -1025,6 +1154,9 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	rev((void *)(&(workdata.midstate)), ICARUS_MIDSTATE_SIZE);
 	rev((void *)(&(workdata.work)), ICARUS_WORK_SIZE);
 
+	if (info->speed_next_work || info->flash_next_work)
+		cmr2_commands(icarus);
+
 	// We only want results for the work we are about to send
 	usb_buffer_clear(icarus);
 
@@ -1245,17 +1377,80 @@ static struct api_data *icarus_api_stats(struct cgpu_info *cgpu)
 	return root;
 }
 
+static void icarus_statline_before(char *buf, size_t bufsiz, struct cgpu_info *cgpu)
+{
+	struct ICARUS_INFO *info = (struct ICARUS_INFO *)(cgpu->device_data);
+
+	if (info->ident == IDENT_CMR2 && info->cmr2_speed > 0)
+		tailsprintf(buf, bufsiz, "%5.1fMhz", (float)(info->cmr2_speed) * ICARUS_CMR2_SPEED_FACTOR);
+	else
+		tailsprintf(buf, bufsiz, "        ");
+
+	tailsprintf(buf, bufsiz, "        | ");
+}
+
 static void icarus_shutdown(__maybe_unused struct thr_info *thr)
 {
 	// TODO: ?
 }
 
+static void icarus_identify(struct cgpu_info *cgpu)
+{
+	struct ICARUS_INFO *info = (struct ICARUS_INFO *)(cgpu->device_data);
+
+	if (info->ident == IDENT_CMR2)
+		info->flash_next_work = true;
+}
+
+static char *icarus_set(struct cgpu_info *cgpu, char *option, char *setting, char *replybuf)
+{
+	struct ICARUS_INFO *info = (struct ICARUS_INFO *)(cgpu->device_data);
+	int val;
+
+	if (info->ident != IDENT_CMR2) {
+		strcpy(replybuf, "no set options available");
+		return replybuf;
+	}
+
+	if (strcasecmp(option, "help") == 0) {
+		sprintf(replybuf, "clock: range %d-%d",
+				  ICARUS_CMR2_SPEED_MIN_INT, ICARUS_CMR2_SPEED_MAX_INT);
+		return replybuf;
+	}
+
+	if (strcasecmp(option, "clock") == 0) {
+		if (!setting || !*setting) {
+			sprintf(replybuf, "missing clock setting");
+			return replybuf;
+		}
+
+		val = atoi(setting);
+		if (val < ICARUS_CMR2_SPEED_MIN_INT || val > ICARUS_CMR2_SPEED_MAX_INT) {
+			sprintf(replybuf, "invalid clock: '%s' valid range %d-%d",
+					  setting,
+					  ICARUS_CMR2_SPEED_MIN_INT,
+					  ICARUS_CMR2_SPEED_MAX_INT);
+		}
+
+		info->cmr2_speed = CMR2_INT_TO_SPEED(val);
+		info->speed_next_work = true;
+
+		return NULL;
+	}
+
+	sprintf(replybuf, "Unknown option: %s", option);
+	return replybuf;
+}
+
 struct device_drv icarus_drv = {
 	.drv_id = DRIVER_icarus,
 	.dname = "Icarus",
 	.name = "ICA",
 	.drv_detect = icarus_detect,
 	.get_api_stats = icarus_api_stats,
+	.get_statline_before = icarus_statline_before,
+	.set_device = icarus_set,
+	.identify_device = icarus_identify,
 	.thread_prepare = icarus_prepare,
 	.scanhash = icarus_scanhash,
 	.thread_shutdown = icarus_shutdown,