Commit afbd9f172e09f92ac3057ddce1cd7875eb7c0861

kanoi 2014-05-16T19:11:13

minion - chip ghs reporting and chip/core validation in nonce processing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
diff --git a/ASIC-README b/ASIC-README
index 866afe4..648142f 100644
--- a/ASIC-README
+++ b/ASIC-README
@@ -234,10 +234,11 @@ ASIC SPECIFIC COMMANDS
 --hfa-temp-overheat <arg> Set the hashfast overheat throttling temperature (default: 95)
 --hfa-temp-target <arg> Set the hashfast target temperature (0 to disable) (default: 88)
 --klondike-options <arg> Set klondike options clock:temptarget
+--minion-chipreport <arg> Seconds to report chip 5min hashrate, range 0-100 (default: 0=disabled)
 --minion-freq <arg> Set minion chip frequencies in MHz, single value or comma list, range 100-1400 (default: 1000)
---minion-temp <arg> Set minion chip temperature threshold, single value or comma list, range 120-160 (default: 135C)
---minion-overheat   Enable directly halting any chip when the status exceeds 100C
 --minion-idlecount  Report when IdleCount is >0 or changes
+--minion-overheat   Enable directly halting any chip when the status exceeds 100C
+--minion-temp <arg> Set minion chip temperature threshold, single value or comma list, range 120-160 (default: 135C)
 --nfu-bits <arg>    Set nanofury bits for overclocking, range 32-63 (default: 50)
 
 
diff --git a/README b/README
index b50f459..55f6b2b 100644
--- a/README
+++ b/README
@@ -218,10 +218,11 @@ Options for both config file and command line:
 --load-balance      Change multipool strategy from failover to quota based balance
 --log|-l <arg>      Interval in seconds between log output (default: 5)
 --lowmem            Minimise caching of shares for low memory applications
+--minion-chipreport <arg> Seconds to report chip 5min hashrate, range 0-100 (default: 0=disabled)
 --minion-freq <arg> Set minion chip frequencies in MHz, single value or comma list, range 100-1400 (default: 1000)
---minion-temp <arg> Set minion chip temperature threshold, single value or comma list, range 120-160 (default: 135C)
---minion-overheat   Enable directly halting any chip when the status exceeds 100C
 --minion-idlecount  Report when IdleCount is >0 or changes
+--minion-overheat   Enable directly halting any chip when the status exceeds 100C
+--minion-temp <arg> Set minion chip temperature threshold, single value or comma list, range 120-160 (default: 135C)
 --monitor|-m <arg>  Use custom pipe cmd for output messages
 --nfu-bits <arg>    Set nanofury bits for overclocking, range 32-63 (default: 50)
 --net-delay         Impose small delays in networking to not overload slow routers
diff --git a/cgminer.c b/cgminer.c
index be27d60..ff7cf41 100644
--- a/cgminer.c
+++ b/cgminer.c
@@ -239,10 +239,11 @@ static char *opt_set_hfa_fan;
 #endif
 static char *opt_set_null;
 #ifdef USE_MINION
+int opt_minion_chipreport;
+bool opt_minion_idlecount;
 char *opt_minion_freq;
-char *opt_minion_temp;
 bool opt_minion_overheat;
-bool opt_minion_idlecount;
+char *opt_minion_temp;
 #endif
 
 #ifdef USE_USBUTILS
@@ -1316,18 +1317,21 @@ static struct opt_table opt_config_table[] = {
 			opt_set_bool, &opt_lowmem,
 			"Minimise caching of shares for low memory applications"),
 #ifdef USE_MINION
+	OPT_WITH_ARG("--minion-chipreport",
+		     set_int_0_to_100, opt_show_intval, &opt_minion_chipreport,
+		     "Seconds to report chip 5min hashrate, range 0-100 (default: 0=disabled)"),
 	OPT_WITH_ARG("--minion-freq",
 		     opt_set_charp, NULL, &opt_minion_freq,
 		     "Set minion chip frequencies in MHz, single value or comma list, range 100-1400 (default: 1000)"),
-	OPT_WITH_ARG("--minion-temp",
-		     opt_set_charp, NULL, &opt_minion_temp,
-		     "Set minion chip temperature threshold, single value or comma list, range 120-160 (default: 135C)"),
-	OPT_WITHOUT_ARG("--minion-overheat",
-		     opt_set_bool, &opt_minion_overheat,
-		     "Enable directly halting any chip when the status exceeds 100C"),
 	OPT_WITHOUT_ARG("--minion-idlecount",
 		     opt_set_bool, &opt_minion_idlecount,
 		     "Report when IdleCount is >0 or changes"),
+	OPT_WITHOUT_ARG("--minion-overheat",
+		     opt_set_bool, &opt_minion_overheat,
+		     "Enable directly halting any chip when the status exceeds 100C"),
+	OPT_WITH_ARG("--minion-temp",
+		     opt_set_charp, NULL, &opt_minion_temp,
+		     "Set minion chip temperature threshold, single value or comma list, range 120-160 (default: 135C)"),
 #endif
 #if defined(unix) || defined(__APPLE__)
 	OPT_WITH_ARG("--monitor|-m",
diff --git a/driver-minion.c b/driver-minion.c
index 9a5d28c..aaf0926 100644
--- a/driver-minion.c
+++ b/driver-minion.c
@@ -433,6 +433,7 @@ typedef struct ritem {
 	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
@@ -443,9 +444,21 @@ typedef struct ritem {
 	bool no_nonce;
 } RITEM;
 
+#define ALLOC_HITEMS 4096
+#define LIMIT_HITEMS 0
+
+// How much history to keep (5min)
+#define MINION_HISTORY_s 300
+
+typedef struct hitem {
+	int chip;
+	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
@@ -608,6 +621,9 @@ struct minion_info {
 	uint64_t work_unrolled;
 	uint64_t work_rolled;
 
+	uint64_t spi_errors;
+	uint64_t chip_spi_errors[MINION_CHIPS];
+
 	// Work items
 	K_LIST *wfree_list;
 	K_STORE *wwork_list;
@@ -624,6 +640,20 @@ struct minion_info {
 
 	struct timeval last_did;
 
+	// Nonce history
+	K_LIST *hfree_list;
+	// A single list for quick removal
+	K_STORE *hnonce_list;
+	/* Note this stores a duplicate of hnonce_list, but per chip
+	   Thus allows to immeditaley find the 2nd last nonce per chip */
+	K_STORE *hchip_list[MINION_CHIPS];
+
+	struct timeval chip_rpt;
+	struct timeval oldest_nonce[MINION_CHIPS];
+	struct timeval newest_nonce[MINION_CHIPS];
+	uint64_t history_nonces[MINION_CHIPS];
+	double history_ghs[MINION_CHIPS];
+
 #if DO_IO_STATS
 	// Total
 	IOSTAT summary;
@@ -658,7 +688,8 @@ static void ready_work(struct cgpu_info *minioncgpu, struct work *work, bool rol
 	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)
+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)
 {
 	struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data);
 	K_ITEM *item = NULL;
@@ -674,6 +705,7 @@ static bool oldest_nonce(struct cgpu_info *minioncgpu, int *chip, int *core, uin
 		*task_id = DATAR(item)->task_id;
 		*nonce = DATAR(item)->nonce;
 		*no_nonce = DATAR(item)->no_nonce;
+		memcpy(when, &(DATAR(item)->when), sizeof(*when));
 
 		k_free_head(minioninfo->rfree_list, item);
 	}
@@ -1661,6 +1693,13 @@ static void minion_detect(bool hotplug)
 	minioninfo->rfree_list = k_new_list("Reply", sizeof(RITEM), ALLOC_RITEMS, LIMIT_RITEMS, true);
 	minioninfo->rnonce_list = k_new_store(minioninfo->rfree_list);
 
+	if (opt_minion_chipreport > 0) {
+		minioninfo->hfree_list = k_new_list("History", sizeof(HITEM), ALLOC_HITEMS, LIMIT_HITEMS, true);
+		minioninfo->hnonce_list = k_new_store(minioninfo->hfree_list);
+		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));
@@ -1932,6 +1971,7 @@ static void *minion_spi_reply(void *userdata)
 	int ret, chipwork, gap;
 	struct pollfd pfd;
 	bool somelow, gotreplies;
+	struct timeval now;
 
 	struct minion_header *head;
 	uint8_t rbuf[MINION_BUFSIZ];
@@ -2053,6 +2093,7 @@ applog(LOG_ERR, "%s%i: Large work reply res %d", minioncgpu->drv->name, minioncg
 					if (res_task.reply <= 0)
 						break;
 					else {
+						cgtime(&now);
 						if (res_task.reply < (int)MINION_RES_DATA_SIZ) {
 							char *buf = bin2hex((unsigned char *)(&(res_task.rbuf[res_task.osiz - res_task.rsiz])), (int)(res_task.rsiz));
 							applog(LOG_ERR, "%s%i: Bad work reply (%s) size %d, should be at least %d",
@@ -2078,6 +2119,7 @@ applog(LOG_ERR, "%s%i: Large work reply res %d", minioncgpu->drv->name, minioncg
 									DATAR(item)->task_id = RES_TASK(result);
 									DATAR(item)->nonce = RES_NONCE(result);
 									DATAR(item)->no_nonce = !RES_GOLD(result);
+									memcpy(&(DATAR(item)->when), &now, sizeof(now));
 //applog(LOG_ERR, "%s%i: ZZZ reply task_id 0x%04x - chip %d - gold %d", minioncgpu->drv->name, minioncgpu->device_id, RES_TASK(result), (int)RES_CHIP(result), (int)RES_GOLD(result));
 
 //if (RES_GOLD(result))
@@ -2201,7 +2243,8 @@ enum nonce_state {
 	NONCE_NO_NONCE,
 	NONCE_BAD_NONCE,
 	NONCE_BAD_WORK,
-	NONCE_NO_WORK
+	NONCE_NO_WORK,
+	NONCE_SPI_ERR
 };
 
 static void cleanup_older(struct cgpu_info *minioncgpu, int chip, K_ITEM *item, bool no_nonce)
@@ -2225,16 +2268,18 @@ static void cleanup_older(struct cgpu_info *minioncgpu, int chip, K_ITEM *item, 
 				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);
+				errs = (DATAW(tail)->errors > 0);
 				// queue must be false since older work than the reply must have already been sent
 				// reported for debugging i.e. if que=non-zero then there's a bug
-//				applog(errs ? LOG_DEBUG : LOG_ERR,
-//				applog(LOG_ERR,
-//					"%s%i: discarded old task 0x%04x chip %d no reply errs=%d (que=%d)",
-//					minioncgpu->drv->name, minioncgpu->device_id,
-//					DATAW(tail)->task_id, chip, DATAW(tail)->errors,
-//					DATAW(tail)->queued);
+				applog(errs ? LOG_DEBUG : LOG_ERR,
+				applog(LOG_ERR,
+					"%s%i: discarded old task 0x%04x chip %d no reply errs=%d (que=%d)",
+					minioncgpu->drv->name, minioncgpu->device_id,
+					DATAW(tail)->task_id, chip, DATAW(tail)->errors,
+					DATAW(tail)->queued);
+*/
 			}
 			K_WUNLOCK(minioninfo->wchip_list[chip]);
 			applog(MINION_LOG, "%s%i: marking complete - old task 0x%04x chip %d",
@@ -2270,10 +2315,34 @@ static void cleanup_older(struct cgpu_info *minioncgpu, int chip, K_ITEM *item, 
 	}
 }
 
-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)
+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)
 {
 	struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data);
-	K_ITEM *item;
+	struct timeval now;
+	K_ITEM *item, *tail;
+
+	if (chip < 0 || chip >= MINION_CHIPS) {
+		minioninfo->spi_errors++;
+		applog(LOG_ERR, "%s%i: SPI nonce error invalid chip %d",
+				minioncgpu->drv->name, minioncgpu->device_id, chip);
+		return NONCE_SPI_ERR;
+	}
+
+	if (!(minioninfo->chip[chip])) {
+		minioninfo->spi_errors++;
+		applog(LOG_ERR, "%s%i: SPI nonce error chip %d not present",
+				minioncgpu->drv->name, minioncgpu->device_id, chip);
+		return NONCE_SPI_ERR;
+	}
+
+	if (core < 0 || core >= MINION_CORES) {
+		minioninfo->spi_errors++;
+		minioninfo->chip_spi_errors[chip]++;
+		applog(LOG_ERR, "%s%i: SPI nonce error invalid core %d (chip %d)",
+				minioncgpu->drv->name, minioncgpu->device_id, core, chip);
+		return NONCE_SPI_ERR;
+	}
 
 	if (no_nonce)
 		minioninfo->chip_nononces[chip]++;
@@ -2285,7 +2354,7 @@ static enum nonce_state oknonce(struct thr_info *thr, struct cgpu_info *minioncg
 
 	if (!item) {
 		K_RUNLOCK(minioninfo->wchip_list[chip]);
-		applog(LOG_ERR, "%s%i: no work (chip %d core %d task 0x%04x)",
+		applog(LOG_ERR, "%s%i: no chip work (chip %d core %d task 0x%04x)",
 				minioncgpu->drv->name, minioncgpu->device_id,
 				chip, core, (int)task_id);
 		if (!no_nonce) {
@@ -2336,6 +2405,41 @@ static enum nonce_state oknonce(struct thr_info *thr, struct cgpu_info *minioncg
 		minioninfo->ok_nonces++;
 
 		cleanup_older(minioncgpu, chip, item, no_nonce);
+
+		if (opt_minion_chipreport > 0) {
+			cgtime(&now);
+
+			K_WLOCK(minioninfo->hnonce_list);
+			memcpy(&(minioninfo->newest_nonce[chip]), when, sizeof(*when));
+			if (minioninfo->history_nonces[chip] == 0)
+				memcpy(&(minioninfo->oldest_nonce[chip]), when, sizeof(*when));
+			minioninfo->history_nonces[chip]++;
+			item = k_unlink_head(minioninfo->hfree_list);
+			DATAH(item)->chip = chip;
+			memcpy(when, &(DATAH(item)->when), sizeof(*when));
+			k_add_head(minioninfo->hnonce_list, item);
+			k_add_head(minioninfo->hchip_list[chip], item);
+			tail = minioninfo->hnonce_list->tail;
+			while (tail && tdiff(&(DATAH(tail)->when), &now) > MINION_HISTORY_s) {
+				int chip_tmp = DATAH(tail)->chip;
+				// The 2 tails should be the same hitem
+				k_unlink_tail(minioninfo->hchip_list[chip_tmp]);
+				tail = k_unlink_tail(minioninfo->hnonce_list);
+				minioninfo->history_nonces[chip_tmp]--;
+				k_add_head(minioninfo->hfree_list, item);
+
+				// New oldest nonce
+				tail = minioninfo->hchip_list[chip_tmp]->tail;
+				if (tail) {
+					memcpy(&(minioninfo->oldest_nonce[chip_tmp]),
+						&(DATAH(tail)->when), sizeof(*when));
+				}
+
+				tail = minioninfo->hnonce_list->tail;
+			}
+			K_WUNLOCK(minioninfo->hnonce_list);
+		}
+
 		return NONCE_GOOD_NONCE;
 	}
 
@@ -2358,6 +2462,7 @@ static void *minion_results(void *userdata)
 	uint32_t task_id;
 	uint32_t nonce;
 	bool no_nonce;
+	struct timeval when;
 
 	applog(MINION_LOG, "%s%i: Results...",
 				minioncgpu->drv->name, minioncgpu->device_id);
@@ -2371,12 +2476,12 @@ static void *minion_results(void *userdata)
 	}
 
 	while (minioncgpu->shutdown == false) {
-		if (!oldest_nonce(minioncgpu, &chip, &core, &task_id, &nonce, &no_nonce)) {
+		if (!oldest_nonce(minioncgpu, &chip, &core, &task_id, &nonce, &no_nonce, &when)) {
 			cgsem_mswait(&(minioninfo->nonce_ready), MINION_NONCE_mS);
 			continue;
 		}
 
-		oknonce(thr, minioncgpu, chip, core, task_id, nonce, no_nonce);
+		oknonce(thr, minioncgpu, chip, core, task_id, nonce, no_nonce, &when);
 	}
 
 	return NULL;
@@ -2871,15 +2976,84 @@ static bool minion_queue_full(struct cgpu_info *minioncgpu)
 	return ret;
 }
 
-static int64_t minion_scanwork(__maybe_unused struct thr_info *thr)
+static void idle_report(struct cgpu_info *minioncgpu)
 {
-	struct cgpu_info *minioncgpu = thr->cgpu;
 	struct minion_info *minioninfo = (struct minion_info *)(minioncgpu->device_data);
-	int64_t hashcount = 0;
 	struct timeval now;
 	int msdiff;
 	int chip;
 
+	for (chip = 0; chip < MINION_CHIPS; chip++) {
+		if (minioninfo->chip[chip]) {
+			if (minioninfo->chip_status[chip].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 increased %08x",
+						minioncgpu->drv->name, minioncgpu->device_id,
+						chip, minioninfo->chip_status[chip].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];
+	size_t len;
+	double ghs;
+	int msdiff;
+	int chip;
+	bool any;
+
+	cgtime(&now);
+	if (!(minioninfo->chip_rpt.tv_sec)) {
+		// No report yet so don't until now + opt_minion_chipreport
+		memcpy(&(minioninfo->chip_rpt), &now, sizeof(now));
+		return;
+	}
+	msdiff = ms_tdiff(&now, &(minioninfo->chip_rpt));
+	if (msdiff >= (opt_minion_chipreport * 1000)) {
+		buf[0] = '\0';
+		any = false;
+		K_RLOCK(minioninfo->hnonce_list);
+		for (chip = 0; chip < MINION_CHIPS; chip++) {
+			if (minioninfo->chip[chip]) {
+				len = strlen(buf);
+				if (minioninfo->history_nonces[chip] < 2)
+					ghs = 0.0;
+				else {
+					ghs = 0xffffffffull * (minioninfo->history_nonces[chip] - 1);
+					ghs /= 1000000000.0;
+					ghs /= tdiff(&(minioninfo->newest_nonce[chip]),
+							&(minioninfo->oldest_nonce[chip]));
+				}
+				snprintf(buf + len, sizeof(buf) - len,
+					 "%s%d:%.2f", any ? " " : "", chip, ghs);
+				minioninfo->history_ghs[chip] = ghs;
+				any = true;
+			}
+		}
+		K_RUNLOCK(minioninfo->hnonce_list);
+		applogsiz(LOG_WARNING, 512,
+			  "%s%d: Chip GHs %s",
+			  minioncgpu->drv->name, minioncgpu->device_id, buf);
+		memcpy(&(minioninfo->chip_rpt), &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));
@@ -2889,24 +3063,12 @@ static int64_t minion_scanwork(__maybe_unused struct thr_info *thr)
 	}
 	mutex_unlock(&(minioninfo->nonce_lock));
 
-	if (opt_minion_idlecount) {
-		for (chip = 0; chip < MINION_CHIPS; chip++) {
-			if (minioninfo->chip[chip]) {
-				if (minioninfo->chip_status[chip].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 increased %08x",
-							minioncgpu->drv->name, minioncgpu->device_id,
-							chip, minioninfo->chip_status[chip].idle);
-					}
-				}
-			}
-		}
-	}
+	if (opt_minion_idlecount)
+		idle_report(minioncgpu);
+
+	if (opt_minion_chipreport > 0)
+		chip_report(minioncgpu);
+
 	/*
 	 * To avoid wasting CPU, wait until we get an interrupt
 	 * before returning back to the main cgminer work loop
@@ -3035,8 +3197,13 @@ static struct api_data *minion_api_stats(struct cgpu_info *minioncgpu)
 			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)
@@ -3174,6 +3341,7 @@ static struct api_data *minion_api_stats(struct cgpu_info *minioncgpu)
 	}
 #endif
 
+	root = api_add_uint64(root, "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);
diff --git a/miner.h b/miner.h
index 1b9f2c0..b5bb2de 100644
--- a/miner.h
+++ b/miner.h
@@ -1009,10 +1009,11 @@ extern char *opt_bitmain_options;
 extern bool opt_bitmain_hwerror;
 #endif
 #ifdef USE_MINION
+extern int opt_minion_chipreport;
+extern bool opt_minion_idlecount;
 extern char *opt_minion_freq;
-extern char *opt_minion_temp;
 extern bool opt_minion_overheat;
-extern bool opt_minion_idlecount;
+extern char *opt_minion_temp;
 #endif
 #ifdef USE_USBUTILS
 extern char *opt_usb_select;