1 /**
2  * Copyright (C) 2012-2014 Analog Devices, Inc.
3  *
4  * Licensed under the GPL-2.
5  *
6  **/
7 #include <stdio.h>
8 
9 #include <gtk/gtk.h>
10 #include <gtkdatabox.h>
11 #include <glib.h>
12 #include <gtkdatabox_grid.h>
13 #include <gtkdatabox_points.h>
14 #include <gtkdatabox_lines.h>
15 #include <math.h>
16 #include <stdint.h>
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <stdbool.h>
20 #include <stdlib.h>
21 #include <sys/stat.h>
22 #include <string.h>
23 #include <unistd.h>
24 
25 #include "../datatypes.h"
26 #include "../osc.h"
27 #include "../iio_widget.h"
28 #include "../osc_plugin.h"
29 #include "../config.h"
30 #include "../libini2.h"
31 
32 #define THIS_DRIVER "Partial Reconfiguration"
33 
34 #define ARRAY_SIZE(x) (!sizeof(x) ?: sizeof(x) / sizeof((x)[0]))
35 
36 #define PHY_DEVICE	"ad9361-phy"
37 #define DEVICE_NAME_ADC	"cf-ad9361-lpc"
38 #define DEVICE_NAME_DAC	"cf-ad9361-dds-core-lpc"
39 
40 #define PR_STATUS_ADDR	0x800000B8
41 #define PR_CONTROL_ADDR	0x800000BC
42 
43 #define PR_LOGIC_DEFAULT_ID	0xA0
44 #define PR_LOGIC_BIST_ID	0xA1
45 #define PR_LOGIC_QPSK_ID	0xA2
46 
47 #define IS_PARTIAL_BITSTREAM_FILEPATH "/sys/bus/platform/devices/f8007000.devcfg/is_partial_bitstream"
48 #define XDEVCFG_FILEPATH "/dev/xdevcfg"
49 
50 #define BUF_SIZE 0x00300000
51 static char buf_pr[BUF_SIZE];
52 static int fd_devcfg = 0;
53 static int fd_is_partial = 0;
54 static char *config_file_path;
55 
56 static struct iio_context *ctx;
57 static struct iio_device *phy, *adc, *dac;
58 static bool context_is_local;
59 
60 enum regmaps {
61 	ADC_REGMAP,
62 	DAC_REGMAP,
63 };
64 
65 static GtkWidget *reconf_chooser;
66 static GtkWidget *reconf_path_text;
67 static GtkWidget *regmap_select;
68 static GtkWidget *pr_stat_text;
69 static GtkWidget *pr_conf_text;
70 static GtkWidget *reg_read;
71 static GtkWidget *reg_write;
72 
73 static gint this_page;
74 static GtkWidget *pr_config_panel;
75 static gboolean plugin_detached;
76 
77 static const char * pr_config_driver_attribs[] = {
78 	"config_file",
79 	"adc_active",
80 };
81 
entry_set_hex_int(GtkWidget * entry,unsigned data)82 static void entry_set_hex_int(GtkWidget *entry, unsigned data)
83 {
84 	gchar *buf;
85 
86 	g_return_if_fail(GTK_ENTRY(entry));
87 
88 	buf = g_strdup_printf("0x%.8x", data);
89 	gtk_entry_set_text(GTK_ENTRY(entry), buf);
90 	g_free(buf);
91 }
92 
93 /*
94  * Update the PR buffer with the specified bin
95  */
updatePR(const char * pr_bin_path)96 static const char * updatePR(const char * pr_bin_path) {
97 
98 	ssize_t status = 0;
99 	int ret, fd;
100 
101 	fd = open(pr_bin_path, O_RDONLY);
102 	if(fd < 0) {
103 		return "Could not open file!";
104 	} else {
105 		status = read(fd, buf_pr, BUF_SIZE);
106 		if(status < 0) {
107 			close(fd);
108 			return "Could not read file!";
109 		}
110 		close(fd);
111 	}
112 
113 	/* set is_partial_bitfile device attribute */
114 	fd_is_partial = open(IS_PARTIAL_BITSTREAM_FILEPATH, O_RDWR);
115 	if (fd_is_partial < 0) {
116 		return "Could not open "IS_PARTIAL_BITSTREAM_FILEPATH;
117 	} else {
118 		ret = write(fd_is_partial, "1", 2);
119 		close(fd_is_partial);
120 	}
121 	if (ret != 2)
122 		return "Could not write to "IS_PARTIAL_BITSTREAM_FILEPATH;
123 
124 	/* write partial bitfile to devcfg device */
125 	fd_devcfg = open(XDEVCFG_FILEPATH, O_RDWR);
126 	if(fd_devcfg < 0) {
127 		return "Could not open "XDEVCFG_FILEPATH;
128 	} else {
129 		ret = write(fd_devcfg, buf_pr, BUF_SIZE);
130 		sleep(1);
131 		close(fd_devcfg);
132 	}
133 	if (ret != BUF_SIZE)
134 		return "Could not write to "XDEVCFG_FILEPATH;
135 
136 	return NULL;
137 }
138 
writeReg(char * device,uint32_t address,uint32_t data)139 static void writeReg(char* device, uint32_t address, uint32_t data) {
140 	struct iio_device *dev;
141 
142 	if (!device) {
143 		perror("writeReg() - device is NULL");
144 	}
145 
146 	dev = iio_context_find_device(ctx, device);
147 
148 	if (!dev) {
149 		perror("writeReg() - Unable to find device!");
150 		return;
151 	}
152 	/* register write */
153 	iio_device_reg_write(dev, address, data);
154 }
155 
readReg(char * device,uint32_t address,uint32_t * data)156 static void readReg(char* device, uint32_t address, uint32_t* data) {
157 	struct iio_device *dev;
158 
159 	if (!device) {
160 		perror("readReg() - device is NULL");
161 	}
162 
163 	dev = iio_context_find_device(ctx, device);
164 
165 	if (!dev) {
166 		perror("readReg() - Unable to find device!");
167 		return;
168 	}
169 	/* register read */
170 	iio_device_reg_read(dev, address, data);
171 }
172 
getPrId()173 static int getPrId() {
174 
175 	uint32_t data = 0;
176 
177 	/* read the current status register */
178 	readReg(DEVICE_NAME_ADC, PR_STATUS_ADDR, &data);
179 
180 	return (data & 0xFF);
181 }
182 
pr_config_file_apply(const char * filename)183 static void pr_config_file_apply(const char *filename)
184 {
185 	GtkTextBuffer *buffer;
186 	gchar *msg_error = NULL;
187 	const gchar *ret_msg = NULL;
188 
189 	if (!context_is_local) {
190 		msg_error = g_strdup_printf("Partial Reconfiguration is not supported in remote mode");
191 	} else if(!filename) {
192 		msg_error = g_strdup_printf("No file selected");
193 	} else if (!g_str_has_suffix(filename, ".bin")) {
194 		msg_error = g_strdup_printf("The selected file is not a .bin file");
195 	} else {
196 		ret_msg = updatePR(filename);
197 		if (ret_msg)
198 			msg_error = g_strdup_printf("%s", ret_msg);
199 	}
200 
201 	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(reconf_path_text));
202 	if (!msg_error) {
203 		gtk_text_buffer_set_text(buffer, filename, -1);
204 	} else {
205 		gtk_text_buffer_set_text(buffer, msg_error, -1);
206 	}
207 
208 	if (msg_error)
209 		g_free(msg_error);
210 }
211 
reconfig_file_set_cb(GtkFileChooser * chooser,gpointer data)212 static void reconfig_file_set_cb (GtkFileChooser *chooser, gpointer data)
213 {
214 	if (config_file_path)
215 		g_free(config_file_path);
216 	config_file_path = gtk_file_chooser_get_filename(chooser);
217 
218 	pr_config_file_apply(config_file_path);
219 }
220 
device_changed_cb(GtkComboBox * button,gpointer data)221 static void device_changed_cb(GtkComboBox *button, gpointer data)
222 {
223 	gtk_entry_set_text(GTK_ENTRY(pr_stat_text), "");
224 	gtk_entry_set_text(GTK_ENTRY(pr_conf_text), "");
225 }
226 
reg_read_clicked_cb(GtkButton * button,gpointer data)227 static void reg_read_clicked_cb(GtkButton *button, gpointer data)
228 {
229 	uint32_t stat_reg, ctrl_reg;
230 	gchar *device;
231 	gchar *active_device;
232 
233 	active_device = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(regmap_select));
234 	if (!active_device)
235 		return;
236 	if (!strcmp(active_device, "ADC"))
237 		device = DEVICE_NAME_ADC;
238 	else if (!strcmp(active_device, "DAC"))
239 		device = DEVICE_NAME_DAC;
240 	else {
241 		printf("Unknown device selection\n");
242 		g_free(active_device);
243 		return;
244 	}
245 	g_free(active_device);
246 
247 	readReg(device, PR_STATUS_ADDR, &stat_reg);
248 	readReg(device, PR_CONTROL_ADDR, &ctrl_reg);
249 
250 	entry_set_hex_int(pr_stat_text, stat_reg);
251 	entry_set_hex_int(pr_conf_text, ctrl_reg);
252 }
253 
reg_write_clicked_cb(GtkButton * button,gpointer data)254 static void reg_write_clicked_cb(GtkButton *button, gpointer data)
255 {
256 	uint32_t reg_data;
257 	const char *buf;
258 	gchar *device;
259 	gchar *active_device;
260 	int ret;
261 
262 	active_device = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(regmap_select));
263 	if (!active_device)
264 		return;
265 	if (!strcmp(active_device, "ADC"))
266 		device = DEVICE_NAME_ADC;
267 	else if (!strcmp(active_device, "DAC"))
268 		device = DEVICE_NAME_DAC;
269 	else {
270 		printf("Unknown device selection\n");
271 		g_free(active_device);
272 		return;
273 	}
274 	g_free(active_device);
275 
276 	buf = gtk_entry_get_text(GTK_ENTRY(pr_conf_text));
277 
278 	ret = sscanf(buf, "0x%x", &reg_data);
279 	if (ret != 1)
280 		ret = sscanf(buf, "%d", &reg_data);
281 
282 	if (ret != 1)
283 		reg_data = 0;
284 
285 	entry_set_hex_int(pr_conf_text, reg_data);
286 	writeReg(device, PR_CONTROL_ADDR, reg_data);
287 }
288 
pr_config_handle_driver(struct osc_plugin * plugin,const char * attrib,const char * value)289 static int pr_config_handle_driver(struct osc_plugin *plugin, const char *attrib, const char *value)
290 {
291 	if (MATCH_ATTRIB("config_file")) {
292 		pr_config_file_apply(value);
293 	} else if (MATCH_ATTRIB("adc_active")) {
294 		gtk_combo_box_set_active(GTK_COMBO_BOX(regmap_select),
295 				atoi(value) ? ADC_REGMAP : DAC_REGMAP);
296 	} else {
297 		return -EINVAL;
298 	}
299 
300 	return 0;
301 }
302 
pr_config_handle(struct osc_plugin * plugin,int line,const char * attrib,const char * value)303 static int pr_config_handle(struct osc_plugin *plugin, int line, const char *attrib, const char *value)
304 {
305 	return osc_plugin_default_handle(ctx, line, attrib, value,
306 			pr_config_handle_driver, NULL);
307 }
308 
load_profile(struct osc_plugin * plugin,const char * ini_fn)309 static void load_profile(struct osc_plugin *plugin, const char *ini_fn)
310 {
311 	unsigned int i;
312 
313 	for (i = 0; i < ARRAY_SIZE(pr_config_driver_attribs); i++) {
314 		char *value = read_token_from_ini(ini_fn, THIS_DRIVER,
315 				pr_config_driver_attribs[i]);
316 		if (value) {
317 			pr_config_handle_driver(NULL,
318 					pr_config_driver_attribs[i], value);
319 			free(value);
320 		}
321 	}
322 }
323 
pr_config_init(struct osc_plugin * plugin,GtkWidget * notebook,const char * ini_fn)324 static GtkWidget * pr_config_init(struct osc_plugin *plugin, GtkWidget *notebook, const char *ini_fn)
325 {
326 	GtkBuilder *builder;
327 
328 	builder = gtk_builder_new();
329 
330 	if (osc_load_glade_file(builder, "pr_config") < 0) {
331 		osc_destroy_context(ctx);
332 		return NULL;
333 	}
334 
335 	pr_config_panel = GTK_WIDGET(gtk_builder_get_object(builder, "pr_config_panel"));
336 	reconf_chooser = GTK_WIDGET(gtk_builder_get_object(builder, "filechooserbutton_reconf"));
337 	reconf_path_text = GTK_WIDGET(gtk_builder_get_object(builder, "textview_reconf_file_path"));
338 	regmap_select = GTK_WIDGET(gtk_builder_get_object(builder, "comboboxtext_regmap_select"));
339 	pr_stat_text = GTK_WIDGET(gtk_builder_get_object(builder, "entry_pr_stat"));
340 	pr_conf_text = GTK_WIDGET(gtk_builder_get_object(builder, "entry_pr_ctrl"));
341 	reg_read = GTK_WIDGET(gtk_builder_get_object(builder, "button_regs_read"));
342 	reg_write = GTK_WIDGET(gtk_builder_get_object(builder, "button_regs_write"));
343 
344 	if (ini_fn)
345 		load_profile(NULL, ini_fn);
346 
347 	g_signal_connect(reconf_chooser, "file-set",
348 		G_CALLBACK(reconfig_file_set_cb), NULL);
349 	g_signal_connect(regmap_select, "changed",
350 		G_CALLBACK(device_changed_cb), NULL);
351 	g_signal_connect(reg_read, "clicked",
352 		G_CALLBACK(reg_read_clicked_cb), NULL);
353 	g_signal_connect(reg_write, "clicked",
354 		G_CALLBACK(reg_write_clicked_cb), NULL);
355 
356 	gtk_combo_box_set_active(GTK_COMBO_BOX(regmap_select), ADC_REGMAP);
357 
358 	return pr_config_panel;
359 }
360 
update_active_page(struct osc_plugin * plugin,gint active_page,gboolean is_detached)361 static void update_active_page(struct osc_plugin *plugin, gint active_page, gboolean is_detached)
362 {
363 	this_page = active_page;
364 	plugin_detached = is_detached;
365 }
366 
pr_config_get_preferred_size(const struct osc_plugin * plugin,int * width,int * height)367 static void pr_config_get_preferred_size(const struct osc_plugin *plugin, int *width, int *height)
368 {
369 	if (width)
370 		*width = 640;
371 	if (height)
372 		*height = 480;
373 }
374 
save_widgets_to_ini(FILE * f)375 static void save_widgets_to_ini(FILE *f)
376 {
377 	fprintf(f, "config_file = %s\n"
378 			"adc_active = %i\n",
379 			config_file_path,
380 			gtk_combo_box_get_active(GTK_COMBO_BOX(regmap_select)) == ADC_REGMAP);
381 }
382 
save_profile(const struct osc_plugin * plugin,const char * ini_fn)383 static void save_profile(const struct osc_plugin *plugin, const char *ini_fn)
384 {
385 	FILE *f = fopen(ini_fn, "a");
386 	if (f) {
387 		save_widgets_to_ini(f);
388 		fclose(f);
389 	}
390 }
391 
context_destroy(struct osc_plugin * plugin,const char * ini_fn)392 static void context_destroy(struct osc_plugin *plugin, const char *ini_fn)
393 {
394 	save_profile(NULL, ini_fn);
395 	osc_destroy_context(ctx);
396 }
397 
398 struct osc_plugin plugin;
399 
pr_config_identify(const struct osc_plugin * plugin)400 static bool pr_config_identify(const struct osc_plugin *plugin)
401 {
402 	/* Use the OSC's IIO context just to detect the devices */
403 	struct iio_context *osc_ctx = get_context_from_osc();
404 
405 	if (!iio_context_find_device(osc_ctx, PHY_DEVICE) ||
406 		!iio_context_find_device(osc_ctx, DEVICE_NAME_ADC) ||
407 		!iio_context_find_device(osc_ctx, DEVICE_NAME_DAC))
408 		return false;
409 
410 	ctx = osc_create_context();
411 	phy = iio_context_find_device(ctx, PHY_DEVICE);
412 	adc = iio_context_find_device(ctx, DEVICE_NAME_ADC);
413 	dac = iio_context_find_device(ctx, DEVICE_NAME_DAC);
414 
415 	context_is_local = !strncmp(iio_context_get_name(ctx), "local", strlen("local"));
416 
417 	int id;
418 	bool init = true;
419 
420 	if (!phy || !adc || !dac) {
421 		init = false;
422 	} else {
423 		id = getPrId();
424 		if ((id != PR_LOGIC_DEFAULT_ID) &&
425 				(id != PR_LOGIC_BIST_ID) &&
426 				(id != PR_LOGIC_QPSK_ID))
427 			init = false;
428 	}
429 	if (phy && !iio_device_get_debug_attrs_count(phy))
430 		init = false;
431 	if (!init)
432 		osc_destroy_context(ctx);
433 
434 	return init;
435 }
436 
437 struct osc_plugin plugin = {
438 	.name = THIS_DRIVER,
439 	.identify = pr_config_identify,
440 	.init = pr_config_init,
441 	.update_active_page = update_active_page,
442 	.get_preferred_size = pr_config_get_preferred_size,
443 	.handle_item = pr_config_handle,
444 	.save_profile = save_profile,
445 	.load_profile = load_profile,
446 	.destroy = context_destroy,
447 };
448