diff options
Diffstat (limited to 'ni845x_spi.c')
-rw-r--r-- | ni845x_spi.c | 347 |
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, }; |