Browse Source

drillbit: Read/write access to clock and voltage configuration from RPC and ManageTUI

Luke Dashjr 12 years ago
parent
commit
70cf66c055
1 changed files with 208 additions and 0 deletions
  1. 208 0
      driver-drillbit.c

+ 208 - 0
driver-drillbit.c

@@ -44,6 +44,7 @@ struct drillbit_board {
 	bool clock_div2;
 	bool clock_div2;
 	bool use_ext_clock;
 	bool use_ext_clock;
 	unsigned ext_clock_freq;
 	unsigned ext_clock_freq;
+	bool need_reinit;
 };
 };
 
 
 static
 static
@@ -249,6 +250,38 @@ bool drillbit_job_prepare(struct thr_info * const thr, struct work * const work,
 	return true;
 	return true;
 }
 }
 
 
+static
+bool drillbit_resend_jobs(struct cgpu_info * const proc)
+{
+	struct thr_info * const thr = proc->thr[0];
+	bool rv;
+	
+	if (thr->work)
+		if (!drillbit_job_prepare(thr, thr->work, 0))
+		{
+			applog(LOG_WARNING, "%"PRIpreprv": Failed to resend %s work",
+			       proc->proc_repr, "current");
+			rv = false;
+		}
+	if (thr->next_work)
+	{
+		if (!drillbit_job_prepare(thr, thr->next_work, 0))
+		{
+			applog(LOG_WARNING, "%"PRIpreprv": Failed to resend %s work",
+			       proc->proc_repr, "next");
+			rv = false;
+		}
+		if (!rv)
+		{
+			// Fake transition so we kinda recover eventually
+			mt_job_transition(thr);
+			job_start_complete(thr);
+			timer_set_now(&thr->tv_morework);
+		}
+	}
+	return rv;
+}
+
 static
 static
 void drillbit_first_job_start(struct thr_info __maybe_unused * const thr)
 void drillbit_first_job_start(struct thr_info __maybe_unused * const thr)
 {
 {
@@ -381,9 +414,21 @@ static
 void drillbit_poll(struct thr_info * const master_thr)
 void drillbit_poll(struct thr_info * const master_thr)
 {
 {
 	struct cgpu_info * const dev = master_thr->cgpu;
 	struct cgpu_info * const dev = master_thr->cgpu;
+	struct drillbit_board * const board = dev->device_data;
 	
 	
 	drillbit_get_work_results(dev);
 	drillbit_get_work_results(dev);
 	
 	
+	if (board->need_reinit)
+	{
+		applog(LOG_NOTICE, "%s: Reinitialisation needed for configuration changes",
+		       dev->dev_repr);
+		drillbit_reset(dev);
+		drillbit_send_config(dev);
+		for (struct cgpu_info *proc = dev; proc; proc = proc->next_proc)
+			drillbit_resend_jobs(proc);
+		board->need_reinit = false;
+	}
+	
 	timer_set_delay_from_now(&master_thr->tv_poll, 10000);
 	timer_set_delay_from_now(&master_thr->tv_poll, 10000);
 }
 }
 
 
@@ -413,6 +458,161 @@ bool drillbit_get_stats(struct cgpu_info * const dev)
 	return true;
 	return true;
 }
 }
 
 
+static
+float drillbit_voltagecfg_volts(const enum drillbit_voltagecfg vcfg)
+{
+	switch (vcfg)
+	{
+		case DBV_650mV: return 0.65;
+		case DBV_750mV: return 0.75;
+		case DBV_850mV: return 0.85;
+		case DBV_950mV: return 0.95;
+	}
+	return 0;
+}
+
+static
+void drillbit_clockcfg_str(char * const buf, size_t bufsz, struct drillbit_board * const board)
+{
+	if (board->use_ext_clock)
+		snprintf(buf, bufsz, "%u", board->ext_clock_freq);
+	else
+		snprintf(buf, bufsz, "L%u", board->clock_level);
+	if (board->clock_div2)
+		tailsprintf(buf, bufsz, ":2");
+}
+
+static
+struct api_data *drillbit_api_stats(struct cgpu_info * const proc)
+{
+	struct cgpu_info * const dev = proc->device;
+	struct drillbit_board * const board = dev->device_data;
+	struct api_data *root = NULL;
+	char buf[0x100];
+	
+	drillbit_clockcfg_str(buf, sizeof(buf), board);
+	root = api_add_string(root, "ClockCfg", buf, true);
+	
+	float volts = drillbit_voltagecfg_volts(board->core_voltage_cfg);
+	root = api_add_volts(root, "Voltage", &volts, true);
+	
+	return root;
+}
+
+static
+char *drillbit_set_device(struct cgpu_info * const proc, char * const option, char *setting, char * const replybuf)
+{
+	struct cgpu_info * const dev = proc->device;
+	struct drillbit_board * const board = dev->device_data;
+	
+	if (!strcasecmp(option, "help"))
+	{
+		sprintf(replybuf,
+			"voltage: 0.65, 0.75, 0.85, or 0.95 (volts)\n"
+			"clock: 80-230 (MHz) using external clock, or L0-L63 for internal clock levels; append :2 to activate div2"
+		);
+		return replybuf;
+	}
+	
+	if (!strcasecmp(option, "voltage"))
+	{
+		// NOTE: Do not use replybuf in here without implementing it in drillbit_tui_handle_choice
+		if (!setting || !*setting)
+			return "Missing voltage setting";
+		const int val = atof(setting) * 1000;
+		enum drillbit_voltagecfg vcfg;
+		switch (val)
+		{
+			case 650: case 649:
+				vcfg = DBV_650mV;
+				break;
+			case 750: case 749:
+				vcfg = DBV_750mV;
+				break;
+			case 850: case 849:
+				vcfg = DBV_850mV;
+				break;
+			case 950: case 949:
+				vcfg = DBV_950mV;
+				break;
+			default:
+				return "Invalid voltage value";
+		}
+		
+		board->core_voltage_cfg = vcfg;
+		board->need_reinit = true;
+		
+		return NULL;
+	}
+	
+	if (!strcasecmp(option, "clock"))
+	{
+		// NOTE: Do not use replybuf in here without implementing it in drillbit_tui_handle_choice
+		const bool use_ext_clock = !(setting[0] == 'L');
+		char *end = &setting[use_ext_clock ? 0 : 1];
+		const unsigned num = strtol(end, &end, 0);
+		const bool div2 = (end[0] == ':' && end[1] == '2');
+		// NOTE: board assignments are ordered such that it is safe to race
+		if (use_ext_clock)
+		{
+			if (num < 80 || num > 230)
+				return "External clock frequency out of range (80-230)";
+			board->clock_div2 = div2;
+			board->ext_clock_freq = num;
+			board->use_ext_clock = true;
+		}
+		else
+		{
+			if (num < 0 || num > 63)
+				return "Internal clock level out of range (0-63)";
+			board->clock_div2 = div2;
+			board->clock_level = num;
+			board->use_ext_clock = false;
+		}
+		board->need_reinit = true;
+		return NULL;
+	}
+	
+	sprintf(replybuf, "Unknown option: %s", option);
+	return replybuf;
+}
+
+#ifdef HAVE_CURSES
+static
+void drillbit_tui_wlogprint_choices(struct cgpu_info * const proc)
+{
+	wlogprint("[C]lock [V]oltage ");
+}
+
+static
+const char *drillbit_tui_handle_choice(struct cgpu_info * const proc, const int input)
+{
+	char *val;
+	switch (input)
+	{
+		case 'c': case 'C':
+			val = curses_input("Set clock (80-230 MHz using external clock, or L0-L63 for internal clock levels; append :2 to activate div2");
+			return drillbit_set_device(proc, "clock", val, NULL) ?: "Requesting clock change";
+		case 'v': case 'V':
+			val = curses_input("Set voltage (0.65, 0.75, 0.85, or 0.95)");
+			return drillbit_set_device(proc, "voltage", val, NULL) ?: "Requesting voltage change";
+	}
+	return NULL;
+}
+
+static
+void drillbit_wlogprint_status(struct cgpu_info * const proc)
+{
+	struct cgpu_info * const dev = proc->device;
+	struct drillbit_board * const board = dev->device_data;
+	char buf[0x100];
+	
+	drillbit_clockcfg_str(buf, sizeof(buf), board);
+	wlogprint("Clock: %s\n", buf);
+	wlogprint("Voltage: %.2f\n", drillbit_voltagecfg_volts(board->core_voltage_cfg));
+}
+#endif
+
 struct device_drv drillbit_drv = {
 struct device_drv drillbit_drv = {
 	.dname = "drillbit",
 	.dname = "drillbit",
 	.name = "DRB",
 	.name = "DRB",
@@ -428,4 +628,12 @@ struct device_drv drillbit_drv = {
 	.job_process_results = drillbit_job_process_results,
 	.job_process_results = drillbit_job_process_results,
 	.poll = drillbit_poll,
 	.poll = drillbit_poll,
 	.get_stats = drillbit_get_stats,
 	.get_stats = drillbit_get_stats,
+	
+	.get_api_stats = drillbit_api_stats,
+	.set_device = drillbit_set_device,
+#ifdef HAVE_CURSES
+	.proc_wlogprint_status = drillbit_wlogprint_status,
+	.proc_tui_wlogprint_choices = drillbit_tui_wlogprint_choices,
+	.proc_tui_handle_choice = drillbit_tui_handle_choice,
+#endif
 };
 };