summaryrefslogtreecommitdiffstats
path: root/ni845x_spi.c
diff options
context:
space:
mode:
Diffstat (limited to 'ni845x_spi.c')
-rw-r--r--ni845x_spi.c347
1 files changed, 191 insertions, 156 deletions
diff --git a/ni845x_spi.c b/ni845x_spi.c
index 153e03ed8..1948387bc 100644
--- a/ni845x_spi.c
+++ b/ni845x_spi.c
@@ -15,6 +15,13 @@
*
*/
+/* The ni845x header does need the WIN32 symbol to be defined and meson does not do it.
+ * Define it just here, since this driver will only work on 32-bit Windows.
+ */
+#ifndef WIN32
+#define WIN32
+#endif
+
#include <ctype.h>
#include <inttypes.h>
#include <string.h>
@@ -40,15 +47,14 @@ enum voltage_coerce_mode {
USE_HIGHER
};
-static const struct spi_master spi_programmer_ni845x;
-
-static unsigned char CS_number; // use chip select 0 as default
-static enum USB845x_type device_pid = Unknown_NI845X_Device;
-
-static uInt32 device_handle;
-static NiHandle configuration_handle;
-static uint16_t io_voltage_in_mV;
-static bool ignore_io_voltage_limits;
+struct ni845x_spi_data {
+ unsigned char CS_number; // use chip select 0 as default
+ enum USB845x_type device_pid;
+ uInt32 device_handle;
+ NiHandle configuration_handle;
+ uint16_t io_voltage_in_mV;
+ bool ignore_io_voltage_limits;
+};
// USB-8452 supported voltages, keep this array in ascending order!
static const uint8_t usb8452_io_voltages_in_100mV[5] = {
@@ -130,11 +136,11 @@ static void ni845x_report_warning(const char *const func, const int32 err)
* @param opened_handle the opened handle from the ni845xOpen
* @return the 0 on successful competition, negative error code on failure positive code on warning
*/
-static int32 ni845x_spi_open_resource(char *resource_handle, uInt32 *opened_handle)
+static int32 ni845x_spi_open_resource(char *resource_handle, uInt32 *opened_handle, enum USB845x_type pid)
{
// NI-845x driver loads the FPGA bitfile at the first time
// which can take couple seconds
- if (device_pid == USB8452)
+ if (pid == USB8452)
msg_pwarn("Opening NI-8452, this might take a while for the first time\n");
int32 tmp = ni845xOpen(resource_handle, opened_handle);
@@ -148,22 +154,22 @@ static int32 ni845x_spi_open_resource(char *resource_handle, uInt32 *opened_hand
/**
* @param serial a null terminated string containing the serial number of the specific device or NULL
- * @return the 0 on successful completition, negative error code on failure
+ * @return the 0 on successful completion, negative error code on failure
*/
-static int ni845x_spi_open(const char *serial, uInt32 *return_handle)
+static int ni845x_spi_open(const char *serial, uInt32 *return_handle, enum USB845x_type *pid)
{
char resource_name[256];
NiHandle device_find_handle;
uInt32 found_devices_count = 0;
int32 tmp = 0;
- unsigned int vid, pid, usb_bus;
+ unsigned int vid, dev_pid, usb_bus;
unsigned long int serial_as_number;
int ret = -1;
tmp = ni845xFindDevice(resource_name, &device_find_handle, &found_devices_count);
if (tmp != 0) {
- // supress warning if no device found
+ // suppress warning if no device found
if (tmp != NI845x_FIND_DEVICE_NO_DEVICE_FOUND)
ni845x_report_error("ni845xFindDevice", tmp);
return -1;
@@ -177,7 +183,7 @@ static int ni845x_spi_open(const char *serial, uInt32 *return_handle)
// and the DEADBEEF is the serial of the device
if (sscanf(resource_name,
"USB%u::0x%04X::0x%04X::%08lX::RAW",
- &usb_bus, &vid, &pid, &serial_as_number) != 4) {
+ &usb_bus, &vid, &dev_pid, &serial_as_number) != 4) {
// malformed resource string detected
msg_pwarn("Warning: Unable to parse the %s NI-845x resource string.\n",
resource_name);
@@ -185,9 +191,9 @@ static int ni845x_spi_open(const char *serial, uInt32 *return_handle)
continue;
}
- device_pid = pid;
+ *pid = dev_pid;
- if (!serial || strtol(serial, NULL, 16) == serial_as_number)
+ if (!serial || strtoul(serial, NULL, 16) == serial_as_number)
break;
if (found_devices_count > 1) {
@@ -200,7 +206,7 @@ static int ni845x_spi_open(const char *serial, uInt32 *return_handle)
}
if (found_devices_count)
- ret = ni845x_spi_open_resource(resource_name, return_handle);
+ ret = ni845x_spi_open_resource(resource_name, return_handle, *pid);
_close_ret:
tmp = ni845xCloseFindDeviceHandle(device_find_handle);
@@ -216,20 +222,22 @@ _close_ret:
* @param requested_io_voltage_mV the desired IO voltage in mVolts
* @param set_io_voltage_mV the IO voltage which was set in mVolts
* @param coerce_mode if set to USE_LOWER the closest supported IO voltage which is lower or equal to
- * the requested_io_voltage_mV will be selected. Otherwise the next closest supported voltage will be choosen
+ * the requested_io_voltage_mV will be selected. Otherwise the next closest supported voltage will be chosen
* which is higher or equal to the requested_io_voltage_mV.
* @return 0 on success, negative on error, positive on warning
*/
static int usb8452_spi_set_io_voltage(uint16_t requested_io_voltage_mV,
uint16_t *set_io_voltage_mV,
- enum voltage_coerce_mode coerce_mode)
+ enum voltage_coerce_mode coerce_mode,
+ enum USB845x_type pid,
+ uInt32 device_handle)
{
- int i = 0;
+ unsigned int i = 0;
uint8_t selected_voltage_100mV = 0;
uint8_t requested_io_voltage_100mV = 0;
- if (device_pid == USB8451) {
- io_voltage_in_mV = 3300;
+ if (pid == USB8451) {
+ *set_io_voltage_mV = 3300;
msg_pwarn("USB-8451 does not support the changing of the SPI IO voltage\n");
return 0;
}
@@ -302,7 +310,7 @@ static int usb8452_spi_set_io_voltage(uint16_t requested_io_voltage_mV,
* @param SCK_freq_in_KHz SCK speed in KHz
* @return
*/
-static int ni845x_spi_set_speed(uint16_t SCK_freq_in_KHz)
+static int ni845x_spi_set_speed(NiHandle configuration_handle, uint16_t SCK_freq_in_KHz)
{
int32 i = ni845xSpiConfigurationSetClockRate(configuration_handle, SCK_freq_in_KHz);
uInt16 clock_freq_read_KHz;
@@ -342,7 +350,7 @@ static void ni845x_spi_print_available_devices(void)
tmp = ni845xFindDevice(resource_handle, &device_find_handle, &found_devices_count);
if (tmp != 0) {
- // supress warning if no device found
+ // suppress warning if no device found
if (tmp != NI845x_FIND_DEVICE_NO_DEVICE_FOUND)
ni845x_report_error("ni845xFindDevice", tmp);
return;
@@ -387,191 +395,104 @@ static void ni845x_spi_print_available_devices(void)
static int ni845x_spi_shutdown(void *data)
{
+ struct ni845x_spi_data *ni_data = data;
int32 ret = 0;
- if (configuration_handle != 0) {
- ret = ni845xSpiConfigurationClose(configuration_handle);
+ if (ni_data->configuration_handle != 0) {
+ ret = ni845xSpiConfigurationClose(ni_data->configuration_handle);
if (ret)
ni845x_report_error("ni845xSpiConfigurationClose", ret);
}
- if (device_handle != 0) {
- ret = ni845xClose(device_handle);
+ if (ni_data->device_handle != 0) {
+ ret = ni845xClose(ni_data->device_handle);
if (ret)
ni845x_report_error("ni845xClose", ret);
}
- return 0;
-}
-
-int ni845x_spi_init(void)
-{
- char *speed_str = NULL;
- char *CS_str = NULL;
- char *voltage = NULL;
- char *endptr = NULL;
- int requested_io_voltage_mV = 1200; // default the IO voltage to 1.2V
- int spi_speed_KHz = 1000; // selecting 1 MHz SCK is a good bet
- char *serial_number = NULL; // by default open the first connected device
- char *ignore_io_voltage_limits_str = NULL;
- int32 tmp = 0;
-
- // read the cs parameter (which Chip select should we use)
- CS_str = extract_programmer_param("cs");
- if (CS_str) {
- CS_number = CS_str[0] - '0';
- free(CS_str);
- if (strlen(CS_str) > 1 || CS_number < 0 || 7 < CS_number) {
- msg_perr("Only CS 0-7 supported\n");
- return 1;
- }
- }
-
- voltage = extract_programmer_param("voltage");
- if (voltage != NULL) {
- requested_io_voltage_mV = parse_voltage(voltage);
- free(voltage);
- if (requested_io_voltage_mV < 0)
- return 1;
- }
-
- serial_number = extract_programmer_param("serial");
-
- speed_str = extract_programmer_param("spispeed");
- if (speed_str) {
- spi_speed_KHz = strtoul(speed_str, &endptr, 0);
- if (*endptr) {
- msg_perr("The spispeed parameter passed with invalid format: %s\n",
- speed_str);
- msg_perr("Please pass the parameter with a simple number in kHz\n");
- return 1;
- }
- free(speed_str);
- }
-
- ignore_io_voltage_limits = false;
- ignore_io_voltage_limits_str = extract_programmer_param("ignore_io_voltage_limits");
- if (ignore_io_voltage_limits_str
- && strcmp(ignore_io_voltage_limits_str, "yes") == 0) {
- ignore_io_voltage_limits = true;
- }
-
- if (ni845x_spi_open(serial_number, &device_handle)) {
- if (serial_number) {
- msg_pinfo("Could not find any connected NI USB-8451/8452 with serialnumber: %s!\n",
- serial_number);
- ni845x_spi_print_available_devices();
- msg_pinfo("Check the S/N field on the bottom of the device,\n"
- "or use 'lsusb -v -d 3923:7166 | grep Serial' for USB-8451\n"
- "or 'lsusb -v -d 3923:7514 | grep Serial' for USB-8452\n");
- free(serial_number);
- } else {
- msg_pinfo("Could not find any connected NI USB-845x device!\n");
- }
- return 1;
- }
- free(serial_number);
-
- // open the SPI config handle
- tmp = ni845xSpiConfigurationOpen(&configuration_handle);
- if (tmp != 0) {
- ni845x_report_error("ni845xSpiConfigurationOpen", tmp);
- ni845x_spi_shutdown(NULL);
- return 1;
- }
-
- if (usb8452_spi_set_io_voltage(requested_io_voltage_mV, &io_voltage_in_mV, USE_LOWER) < 0) {
- ni845x_spi_shutdown(NULL);
- return 1; // no alert here usb8452_spi_set_io_voltage already printed that
- }
-
- if (ni845x_spi_set_speed(spi_speed_KHz)) {
- msg_perr("Unable to set SPI speed\n");
- ni845x_spi_shutdown(NULL);
- return 1;
- }
-
- if (register_shutdown(ni845x_spi_shutdown, NULL)) {
- ni845x_spi_shutdown(NULL);
- return 1;
- }
- register_spi_master(&spi_programmer_ni845x);
-
- return 0;
+ free(data);
+ return ret;
}
static void ni845x_warn_over_max_voltage(const struct flashctx *flash)
{
- if (device_pid == USB8451) {
+ const struct ni845x_spi_data *data = flash->mst->spi.data;
+
+ if (data->device_pid == USB8451) {
msg_pwarn("The %s chip maximum voltage is %.1fV, while the USB-8451 "
"IO voltage levels are 3.3V.\n"
"Ignoring this because ignore_io_voltage_limits parameter is set.\n",
flash->chip->name,
flash->chip->voltage.max / 1000.0f);
- } else if (device_pid == USB8452) {
+ } else if (data->device_pid == USB8452) {
msg_pwarn("The %s chip maximum voltage is %.1fV, while the USB-8452 "
"IO voltage is set to %.1fV.\n"
"Ignoring this because ignore_io_voltage_limits parameter is set.\n",
flash->chip->name,
flash->chip->voltage.max / 1000.0f,
- io_voltage_in_mV / 1000.0f);
+ data->io_voltage_in_mV / 1000.0f);
}
}
static int ni845x_spi_io_voltage_check(const struct flashctx *flash)
{
+ struct ni845x_spi_data *data = flash->mst->spi.data;
static bool first_transmit = true;
if (first_transmit && flash->chip) {
first_transmit = false;
- if (io_voltage_in_mV > flash->chip->voltage.max) {
- if (ignore_io_voltage_limits) {
+ if (data->io_voltage_in_mV > flash->chip->voltage.max) {
+ if (data->ignore_io_voltage_limits) {
ni845x_warn_over_max_voltage(flash);
return 0;
}
- if (device_pid == USB8451) {
+ if (data->device_pid == USB8451) {
msg_perr("The %s chip maximum voltage is %.1fV, while the USB-8451 "
"IO voltage levels are 3.3V.\nAborting operations\n",
flash->chip->name,
flash->chip->voltage.max / 1000.0f);
return -1;
- } else if (device_pid == USB8452) {
+ } else if (data->device_pid == USB8452) {
msg_perr("Lowering IO voltage because the %s chip maximum voltage is %.1fV, "
"(%.1fV was set)\n",
flash->chip->name,
flash->chip->voltage.max / 1000.0f,
- io_voltage_in_mV / 1000.0f);
+ data->io_voltage_in_mV / 1000.0f);
if (usb8452_spi_set_io_voltage(flash->chip->voltage.max,
- &io_voltage_in_mV,
- USE_LOWER)) {
+ &data->io_voltage_in_mV,
+ USE_LOWER,
+ data->device_pid,
+ data->device_handle)) {
msg_perr("Unable to lower the IO voltage below "
"the chip's maximum voltage\n");
return -1;
}
}
- } else if (io_voltage_in_mV < flash->chip->voltage.min) {
- if (device_pid == USB8451) {
+ } else if (data->io_voltage_in_mV < flash->chip->voltage.min) {
+ if (data->device_pid == USB8451) {
msg_pwarn("Flash operations might be unreliable, because the %s chip's "
"minimum voltage is %.1fV, while the USB-8451's "
"IO voltage levels are 3.3V.\n",
flash->chip->name,
flash->chip->voltage.min / 1000.0f);
- return ignore_io_voltage_limits ? 0 : -1;
- } else if (device_pid == USB8452) {
+ return data->ignore_io_voltage_limits ? 0 : -1;
+ } else if (data->device_pid == USB8452) {
msg_pwarn("Raising the IO voltage because the %s chip's "
"minimum voltage is %.1fV, "
"(%.1fV was set)\n",
flash->chip->name,
flash->chip->voltage.min / 1000.0f,
- io_voltage_in_mV / 1000.0f);
+ data->io_voltage_in_mV / 1000.0f);
if (usb8452_spi_set_io_voltage(flash->chip->voltage.min,
- &io_voltage_in_mV,
- USE_HIGHER)) {
+ &data->io_voltage_in_mV,
+ USE_HIGHER,
+ data->device_pid,
+ data->device_handle)) {
msg_pwarn("Unable to raise the IO voltage above the chip's "
"minimum voltage\n"
"Flash operations might be unreliable.\n");
- return ignore_io_voltage_limits ? 0 : -1;
+ return data->ignore_io_voltage_limits ? 0 : -1;
}
}
}
@@ -579,12 +500,13 @@ static int ni845x_spi_io_voltage_check(const struct flashctx *flash)
return 0;
}
-static int ni845x_spi_transmit(struct flashctx *flash,
+static int ni845x_spi_transmit(const struct flashctx *flash,
unsigned int write_cnt,
unsigned int read_cnt,
const unsigned char *write_arr,
unsigned char *read_arr)
{
+ const struct ni845x_spi_data *data = flash->mst->spi.data;
uInt32 read_size = 0;
uInt8 *transfer_buffer = NULL;
int32 ret = 0;
@@ -600,8 +522,8 @@ static int ni845x_spi_transmit(struct flashctx *flash,
memcpy(transfer_buffer, write_arr, write_cnt);
- ret = ni845xSpiWriteRead(device_handle,
- configuration_handle,
+ ret = ni845xSpiWriteRead(data->device_handle,
+ data->configuration_handle,
(write_cnt + read_cnt), transfer_buffer, &read_size, transfer_buffer);
if (ret < 0) {
// Negative specifies an error, meaning the function did not perform the expected behavior.
@@ -616,7 +538,7 @@ static int ni845x_spi_transmit(struct flashctx *flash,
if (read_cnt != 0 && read_arr != NULL) {
if ((read_cnt + write_cnt) != read_size) {
- msg_perr("%s: expected and returned read count mismatch: %u expected, %ld recieved\n",
+ msg_perr("%s: expected and returned read count mismatch: %u expected, %ld received\n",
__func__, read_cnt, read_size);
free(transfer_buffer);
return -1;
@@ -628,11 +550,124 @@ static int ni845x_spi_transmit(struct flashctx *flash,
}
static const struct spi_master spi_programmer_ni845x = {
- .max_data_read = MAX_DATA_READ_UNLIMITED,
- .max_data_write = MAX_DATA_WRITE_UNLIMITED,
- .command = ni845x_spi_transmit,
- .multicommand = default_spi_send_multicommand,
- .read = default_spi_read,
- .write_256 = default_spi_write_256,
- .write_aai = default_spi_write_aai,
+ .max_data_read = MAX_DATA_READ_UNLIMITED,
+ .max_data_write = MAX_DATA_WRITE_UNLIMITED,
+ .command = ni845x_spi_transmit,
+ .read = default_spi_read,
+ .write_256 = default_spi_write_256,
+ .shutdown = ni845x_spi_shutdown,
+};
+
+static int ni845x_spi_init(const struct programmer_cfg *cfg)
+{
+ char *speed_str = NULL;
+ char *CS_str = NULL;
+ char *voltage = NULL;
+ char *endptr = NULL;
+ int requested_io_voltage_mV = 1200; // default the IO voltage to 1.2V
+ int spi_speed_KHz = 1000; // selecting 1 MHz SCK is a good bet
+ char *serial_number = NULL; // by default open the first connected device
+ char *ignore_io_voltage_limits_str = NULL;
+ bool ignore_io_voltage_limits;
+ unsigned char CS_number = 0;
+ enum USB845x_type device_pid = Unknown_NI845X_Device;
+ uInt32 device_handle;
+ int32 tmp = 0;
+
+ // read the cs parameter (which Chip select should we use)
+ CS_str = extract_programmer_param_str(cfg, "cs");
+ if (CS_str) {
+ CS_number = strtoul(CS_str, NULL, 10);
+ free(CS_str);
+ if (CS_number > 7) {
+ msg_perr("Only CS 0-7 supported\n");
+ return 1;
+ }
+ }
+
+ voltage = extract_programmer_param_str(cfg, "voltage");
+ if (voltage != NULL) {
+ requested_io_voltage_mV = parse_voltage(voltage);
+ free(voltage);
+ if (requested_io_voltage_mV < 0)
+ return 1;
+ }
+
+ serial_number = extract_programmer_param_str(cfg, "serial");
+
+ speed_str = extract_programmer_param_str(cfg, "spispeed");
+ if (speed_str) {
+ spi_speed_KHz = strtoul(speed_str, &endptr, 0);
+ if (*endptr) {
+ msg_perr("The spispeed parameter passed with invalid format: %s\n",
+ speed_str);
+ msg_perr("Please pass the parameter with a simple number in kHz\n");
+ return 1;
+ }
+ free(speed_str);
+ }
+
+ ignore_io_voltage_limits = false;
+ ignore_io_voltage_limits_str = extract_programmer_param_str(cfg, "ignore_io_voltage_limits");
+ if (ignore_io_voltage_limits_str
+ && strcmp(ignore_io_voltage_limits_str, "yes") == 0) {
+ ignore_io_voltage_limits = true;
+ }
+
+ if (ni845x_spi_open(serial_number, &device_handle, &device_pid)) {
+ if (serial_number) {
+ msg_pinfo("Could not find any connected NI USB-8451/8452 with serialnumber: %s!\n",
+ serial_number);
+ ni845x_spi_print_available_devices();
+ msg_pinfo("Check the S/N field on the bottom of the device,\n"
+ "or use 'lsusb -v -d 3923:7166 | grep Serial' for USB-8451\n"
+ "or 'lsusb -v -d 3923:7514 | grep Serial' for USB-8452\n");
+ free(serial_number);
+ } else {
+ msg_pinfo("Could not find any connected NI USB-845x device!\n");
+ }
+ return 1;
+ }
+ free(serial_number);
+
+ struct ni845x_spi_data *data = calloc(1, sizeof(*data));
+ if (!data) {
+ msg_perr("Unable to allocate space for SPI master data\n");
+ return 1;
+ }
+ data->CS_number = CS_number;
+ data->device_pid = device_pid;
+ data->device_handle = device_handle;
+ data->ignore_io_voltage_limits = ignore_io_voltage_limits;
+
+ // open the SPI config handle
+ tmp = ni845xSpiConfigurationOpen(&data->configuration_handle);
+ if (tmp != 0) {
+ ni845x_report_error("ni845xSpiConfigurationOpen", tmp);
+ goto err;
+ }
+
+ if (usb8452_spi_set_io_voltage(requested_io_voltage_mV, &data->io_voltage_in_mV,
+ USE_LOWER, data->device_pid, data->device_handle) < 0) {
+ // no alert here usb8452_spi_set_io_voltage already printed that
+ goto err;
+ }
+
+ if (ni845x_spi_set_speed(data->configuration_handle, spi_speed_KHz)) {
+ msg_perr("Unable to set SPI speed\n");
+ goto err;
+ }
+
+ return register_spi_master(&spi_programmer_ni845x, data);
+
+err:
+ ni845x_spi_shutdown(data);
+ return 1;
+}
+
+const struct programmer_entry programmer_ni845x_spi = {
+ .name = "ni845x_spi",
+ .type = OTHER, // choose other because NI-845x uses own USB implementation
+ .devs.note = "National Instruments USB-845x\n",
+ .init = ni845x_spi_init,
};