Browse Source

hashfast: Support for changing clock at runtime

Luke Dashjr 12 years ago
parent
commit
e453751bbe
1 changed files with 89 additions and 3 deletions
  1. 89 3
      driver-hashfast.c

+ 89 - 3
driver-hashfast.c

@@ -64,6 +64,7 @@ enum hashfast_opcode {
 	HFOP_USB_SHUTDOWN  = 0x85,
 	HFOP_DIE_STATUS    = 0x86,
 	HFOP_GWQ_STATUS    = 0x87,
+	HFOP_UMS_CLOCK_CHANGE = 0x88,
 	HFOP_WORK_RESTART  = 0x88,
 	HFOP_USB_STATS1    = 0x89,
 	HFOP_USB_GWQSTATS  = 0x8a,
@@ -84,6 +85,10 @@ enum hashfast_config_flags {
 	HFCF_PWM_ACTIVE_LV = 1 << 0xe,
 };
 
+enum hashfast_clock_change_cmd {
+	HFWR_SET_CLOCK     = 1 << 0xc,
+};
+
 typedef unsigned long hashfast_isn_t;
 
 static inline
@@ -232,10 +237,12 @@ const char *hashfast_set_clock(struct cgpu_info * const proc, const char * const
 }
 
 static const struct bfg_set_device_definition hashfast_set_device_funcs_probe[] = {
-	{"clock", hashfast_set_clock, "clock frequency (can only be set at startup, with --set-device)"},
+	{"clock", hashfast_set_clock, "clock frequency"},
 	{NULL},
 };
 
+static const struct bfg_set_device_definition hashfast_set_device_funcs[];
+
 static
 bool hashfast_detect_one(const char * const devpath)
 {
@@ -284,6 +291,9 @@ bool hashfast_detect_one(const char * const devpath)
 	if (serial_claim_v(devpath, &hashfast_ums_drv))
 		return false;
 	
+	// Hijack hdata for a quick way to transfer clock to init
+	pmsg->hdata = clock;
+	
 	struct cgpu_info * const cgpu = malloc(sizeof(*cgpu));
 	*cgpu = (struct cgpu_info){
 		.drv = &hashfast_ums_drv,
@@ -295,6 +305,9 @@ bool hashfast_detect_one(const char * const devpath)
 		.cutofftemp = 100,
 	};
 	
+	if (fwrev >= 0x0005)
+		cgpu->set_device_funcs = hashfast_set_device_funcs;
+	
 	return add_cgpu(cgpu);
 
 err:
@@ -319,6 +332,8 @@ struct hashfast_chip_state {
 	struct cgpu_info **coreprocs;
 	hashfast_isn_t last_isn;
 	float voltages[HASHFAST_MAX_VOLTAGES];
+	uint16_t clock;
+	uint16_t clock_desired;
 	uint8_t cfgdata[HASHFAST_CONFIG_DATA_SIZE];
 };
 
@@ -333,6 +348,28 @@ struct hashfast_core_state {
 	unsigned queued;
 };
 
+static
+const char *hashfast_set_clock_runtime(struct cgpu_info * const proc, const char * const optname, const char * const newvalue, char * const replybuf, enum bfg_set_device_replytype * const out_success)
+{
+	struct thr_info * const thr = proc->thr[0];
+	struct hashfast_dev_state * const devstate = proc->device_data;
+	struct hashfast_core_state * const cs = thr->cgpu_data;
+	struct hashfast_chip_state * const chipstate = &devstate->chipstates[cs->chipaddr];
+	
+	const int nv = atoi(newvalue);
+	if (nv >= 0xfff)
+		return "Clock frequency too high";
+	
+	chipstate->clock_desired = nv;
+	
+	return NULL;
+}
+
+static const struct bfg_set_device_definition hashfast_set_device_funcs[] = {
+	{"clock", hashfast_set_clock_runtime, "clock frequency"},
+	{NULL},
+};
+
 static
 void hashfast_init_cfgdata(struct hashfast_chip_state * const chipstate)
 {
@@ -378,6 +415,13 @@ void hashfast_init_cfgdata(struct hashfast_chip_state * const chipstate)
 	pk_u16le(cfgdata, 0xe, pwm_pulse_period);
 }
 
+static
+uint16_t hashfast_chip_thermal_cutoff_hdata(const float temp)
+{
+	const uint16_t v = (temp + 61.5) * 0x1000 / 240;
+	return HFCH_THERMAL_LIMIT | (v & 0x3ff);
+}
+
 static
 bool hashfast_init(struct thr_info * const master_thr)
 {
@@ -395,11 +439,15 @@ bool hashfast_init(struct thr_info * const master_thr)
 		.fwrev = upk_u16le(pmsg->data, 0),
 	};
 	
+	const uint16_t clock = pmsg->hdata;
+	
 	for (i = 0; i < pmsg->chipaddr; ++i)
 	{
 		chipstate = &chipstates[i];
 		*chipstate = (struct hashfast_chip_state){
 			.coreprocs = malloc(sizeof(struct cgpu_info *) * pmsg->coreaddr),
+			.clock = clock,
+			.clock_desired = clock,
 		};
 		hashfast_init_cfgdata(chipstate);
 	}
@@ -446,7 +494,7 @@ bool hashfast_queue_append(struct thr_info * const thr, struct work * const work
 	hashfast_isn_t isn;
 	uint8_t seq;
 	
-	if (cs->has_pending)
+	if (cs->has_pending || chipstate->clock_desired != chipstate->clock)
 	{
 		thr->queue_full = true;
 		return false;
@@ -620,6 +668,7 @@ bool hashfast_poll_msg(struct thr_info * const master_thr)
 		{
 			const uint8_t *data = &msg.data[8];
 			struct cgpu_info *proc = hashfast_find_proc(master_thr, msg.chipaddr, 0);
+			struct cgpu_info *first_proc = proc;
 			if (unlikely(!proc))
 			{
 				applog(LOG_ERR, "%s: Unknown chip address %u",
@@ -660,13 +709,50 @@ bool hashfast_poll_msg(struct thr_info * const master_thr)
 				{
 					++cores_transitioned;
 					cs->has_pending = false;
-					thr->queue_full = false;
+					// Avoid refilling pending slot if we are preparing to change the clock frequency
+					if (chipstate->clock_desired == chipstate->clock)
+						thr->queue_full = false;
 				}
 			}
 			applog(LOG_DEBUG, "%s: STATUS from chipaddr=0x%02x with hdata=0x%04x (isn=0x%lx): total=%d uptodate=%d active=%d pending=%d transitioned=%d",
 			       dev->dev_repr, (unsigned)msg.chipaddr, (unsigned)msg.hdata, isn,
 			       devstate->cores_per_chip, cores_uptodate,
 			       cores_active, cores_pending, cores_transitioned);
+			if ((!cores_active) && chipstate->clock_desired != chipstate->clock)
+			{
+				// All cores finished their work, change clock frequency and then refill
+				uint8_t buf[HASHFAST_HEADER_SIZE + HASHFAST_CONFIG_DATA_SIZE];
+				uint16_t clock = chipstate->clock_desired, hdata;
+				if (!hashfast_send_msg(fd, buf, HFOP_UMS_CLOCK_CHANGE, msg.chipaddr, 0, HFWR_SET_CLOCK | clock, 0))
+				{
+					applog(LOG_ERR, "%"PRIpreprv": Clock change failure (%s)", proc->proc_repr, "OP_UMS_CLOCK_CHANGE");
+					goto clockchangefailed;
+				}
+				// Until we send HFOP_CONFIG, the state is undefined
+				chipstate->clock = 0;
+				
+				hdata = HFCH_WRITE;
+				hdata |= hashfast_chip_thermal_cutoff_hdata(110);
+				pk_uNle(chipstate->cfgdata, 8, 0, 0xc, clock);
+				memcpy(&buf[HASHFAST_HEADER_SIZE], chipstate->cfgdata, HASHFAST_CONFIG_DATA_SIZE);
+				if (!hashfast_send_msg(fd, buf, HFOP_CONFIG, msg.chipaddr, 0, hdata, HASHFAST_CONFIG_DATA_SIZE))
+				{
+					applog(LOG_ERR, "%"PRIpreprv": Clock change failure (%s)", proc->proc_repr, "OP_CONFIG");
+					goto clockchangefailed;
+				}
+				
+				chipstate->clock = clock;
+				
+				// Time to refill queues
+				proc = first_proc;
+				for (int i = 0; i < devstate->cores_per_chip; ++i, (proc = proc->next_proc))
+				{
+					struct thr_info * const thr = proc->thr[0];
+					thr->queue_full = false;
+				}
+				
+clockchangefailed: ;
+			}
 			break;
 		}
 	}