Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 1 | /* |
Stefan Reinauer | 5cf0da7 | 2015-08-17 15:22:20 -0700 | [diff] [blame] | 2 | * Copyright 2012-2015 Google Inc. |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 3 | * |
| 4 | * This program is free software; you can redistribute it and/or modify |
| 5 | * it under the terms of the GNU General Public License as published by |
| 6 | * the Free Software Foundation; version 2 of the License. |
| 7 | * |
| 8 | * This program is distributed in the hope that it will be useful, |
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | * GNU General Public License for more details. |
| 12 | * |
| 13 | * You should have received a copy of the GNU General Public License |
| 14 | * along with this program; if not, write to the Free Software |
| 15 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 16 | */ |
| 17 | |
| 18 | #include <stdio.h> |
| 19 | #include <stdlib.h> |
| 20 | #include <string.h> |
Martin Roth | 92c453b | 2015-09-01 10:35:02 -0600 | [diff] [blame] | 21 | #include <strings.h> |
Patrick Georgi | f3558c6 | 2014-10-28 18:16:57 +0100 | [diff] [blame] | 22 | #include <signal.h> |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 23 | #include <getopt.h> |
Stefan Reinauer | c566d20 | 2015-08-25 09:52:42 -0700 | [diff] [blame] | 24 | #include "em100.h" |
Martin Roth | 4203a38 | 2014-08-02 08:14:37 -0600 | [diff] [blame] | 25 | |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 26 | /* SPI flash chips parameters definition */ |
| 27 | #include "em100pro_chips.h" |
| 28 | |
Patrick Georgi | f3558c6 | 2014-10-28 18:16:57 +0100 | [diff] [blame] | 29 | volatile int do_exit_flag = 0; |
| 30 | |
| 31 | void exit_handler(int sig) { |
| 32 | do_exit_flag = 1; |
| 33 | } |
| 34 | |
Aaron Durbin | 637fe48 | 2012-10-17 17:39:19 -0500 | [diff] [blame] | 35 | struct em100_hold_pin_states { |
| 36 | const char *description; |
| 37 | int value; |
| 38 | }; |
| 39 | |
Stefan Reinauer | fba0063 | 2012-10-18 09:17:00 -0700 | [diff] [blame] | 40 | static const struct em100_hold_pin_states hold_pin_states[] = { |
Aaron Durbin | 637fe48 | 2012-10-17 17:39:19 -0500 | [diff] [blame] | 41 | { "FLOAT", 0x2 }, |
| 42 | { "LOW", 0x0 }, |
| 43 | { "INPUT", 0x3 }, |
| 44 | { NULL, 0x0 }, |
| 45 | }; |
| 46 | |
Stefan Reinauer | 54f91d9 | 2015-08-18 12:00:07 -0700 | [diff] [blame] | 47 | /* High Level functions */ |
| 48 | |
Stefan Reinauer | b82d79b | 2015-08-18 11:14:33 -0700 | [diff] [blame] | 49 | static int set_state(struct em100 *em100, int run) |
| 50 | { |
| 51 | return write_fpga_register(em100, 0x28, run & 1); |
| 52 | } |
| 53 | |
| 54 | static int set_hold_pin_state(struct em100 *em100, int pin_state) |
| 55 | { |
| 56 | uint16_t val; |
| 57 | |
| 58 | /* Read and acknowledge hold pin state setting bit 2 of pin state respone. */ |
| 59 | if (!read_fpga_register(em100, 0x2a, &val)) { |
| 60 | printf("Couldn't get hold pin state.\n"); |
| 61 | return 0; |
| 62 | } |
| 63 | write_fpga_register(em100, 0x2a, (1 << 2) | val); |
| 64 | |
| 65 | |
| 66 | if (!read_fpga_register(em100, 0x2a, &val)) { |
| 67 | printf("Couldn't get hold pin state.\n"); |
| 68 | return 0; |
| 69 | } |
| 70 | |
| 71 | /* Now set desired pin state. */ |
| 72 | write_fpga_register(em100, 0x2a, pin_state); |
| 73 | |
| 74 | /* Read the pin state. */ |
| 75 | if (!read_fpga_register(em100, 0x2a, &val)) { |
| 76 | printf("Couldn't get hold pin state.\n"); |
| 77 | return 0; |
| 78 | } |
| 79 | |
| 80 | if (val != pin_state) { |
| 81 | printf("Invalid pin state response: 0x%04x (expected 0x%04x)\n", |
| 82 | val, pin_state); |
| 83 | return 0; |
| 84 | } |
| 85 | |
| 86 | return 1; |
| 87 | } |
| 88 | |
| 89 | static int set_hold_pin_state_from_str(struct em100 *em100, const char *state) |
| 90 | { |
| 91 | int pin_state; |
| 92 | const struct em100_hold_pin_states *s = &hold_pin_states[0]; |
| 93 | |
| 94 | while (s->description != NULL) { |
| 95 | if (!strcmp(s->description, state)) |
| 96 | break; |
| 97 | s++; |
| 98 | } |
| 99 | if (s->description == NULL) { |
| 100 | printf("Invalid hold pin state: %s\n", state); |
| 101 | return 0; |
| 102 | } |
| 103 | pin_state = s->value; |
| 104 | |
| 105 | return set_hold_pin_state(em100, pin_state); |
| 106 | } |
| 107 | |
Stefan Reinauer | 5287511 | 2015-08-20 13:57:20 -0700 | [diff] [blame] | 108 | /** |
| 109 | * get_serialno: fetch device's serial number |
| 110 | * @param em100: initialized em100 device structure |
| 111 | */ |
| 112 | static int get_serialno(struct em100 *em100) |
| 113 | { |
| 114 | unsigned char data[256]; |
| 115 | if (read_spi_flash_page(em100, 0x1fff00, data)) { |
| 116 | em100->serialno = (data[5] << 24) | (data[4] << 16) | \ |
| 117 | (data[3] << 8) | data[2]; |
| 118 | return 1; |
| 119 | } |
| 120 | return 0; |
| 121 | } |
| 122 | |
Stefan Reinauer | e636d2a | 2015-08-28 16:58:57 -0700 | [diff] [blame] | 123 | static int set_serialno(struct em100 *em100, unsigned int serialno) |
| 124 | { |
| 125 | unsigned char data[512]; |
| 126 | unsigned int old_serialno; |
| 127 | |
| 128 | if (!read_spi_flash_page(em100, 0x1fff00, data)) |
| 129 | return 0; |
| 130 | |
| 131 | old_serialno = (data[5] << 24) | (data[4] << 16) | \ |
| 132 | (data[3] << 8) | data[2]; |
| 133 | |
| 134 | if (old_serialno == serialno) { |
| 135 | printf("Serial number unchanged.\n"); |
| 136 | return 1; |
| 137 | } |
| 138 | |
| 139 | data[2] = serialno; |
| 140 | data[3] = serialno >> 8; |
| 141 | data[4] = serialno >> 16; |
| 142 | data[5] = serialno >> 24; |
| 143 | |
| 144 | if (old_serialno != 0xffffffff) { |
| 145 | /* preserve magic */ |
| 146 | read_spi_flash_page(em100, 0x1f0000, data + 256); |
| 147 | /* Unlock and erase sector. Reading |
| 148 | * the SPI flash ID is requires to |
| 149 | * actually unlock the chip. |
| 150 | */ |
| 151 | unlock_spi_flash(em100); |
| 152 | get_spi_flash_id(em100); |
| 153 | erase_spi_flash_sector(em100, 0x1f); |
| 154 | /* write back magic */ |
| 155 | write_spi_flash_page(em100, 0x1f0000, data + 256); |
| 156 | } |
| 157 | |
| 158 | if (!write_spi_flash_page(em100, 0x1fff00, data)) { |
| 159 | printf("Error: Could not write SPI flash.\n"); |
| 160 | return 0; |
| 161 | } |
| 162 | get_serialno(em100); |
| 163 | if (em100->serialno != 0xffffffff) |
| 164 | printf("New serial number: DP%06d\n", em100->serialno); |
| 165 | else |
| 166 | printf("New serial number: N.A.\n"); |
| 167 | |
| 168 | return 1; |
| 169 | } |
| 170 | |
Stefan Reinauer | 14d3512 | 2015-09-03 16:02:55 -0700 | [diff] [blame] | 171 | static int em100_debug(struct em100 *em100) |
| 172 | { |
| 173 | int i; |
| 174 | printf("\nVoltages:\n"); |
| 175 | set_led(em100, both_off); |
| 176 | printf(" 1.2V: %dmV\n", get_voltage(em100, in_v1_2)); |
| 177 | printf(" E_VCC: %dmV\n", get_voltage(em100, in_e_vcc)); |
| 178 | set_led(em100, both_on); |
| 179 | printf(" REF+: %dmV\n", get_voltage(em100, in_ref_plus)); |
| 180 | printf(" REF-: %dmV\n", get_voltage(em100, in_ref_minus)); |
| 181 | set_led(em100, red_on); |
| 182 | printf(" Buffer VCC: %dmV\n", get_voltage(em100, in_buffer_vcc)); |
| 183 | printf(" Trig VCC: %dmV\n", get_voltage(em100, in_trigger_vcc)); |
| 184 | set_led(em100, both_on); |
| 185 | printf(" RST VCC: %dmV\n", get_voltage(em100, in_reset_vcc)); |
| 186 | printf(" 3.3V: %dmV\n", get_voltage(em100, in_v3_3)); |
| 187 | set_led(em100, red_on); |
| 188 | printf(" Buffer 3.3V: %dmV\n", get_voltage(em100, in_buffer_v3_3)); |
| 189 | printf(" 5V: %dmV\n", get_voltage(em100, in_v5)); |
| 190 | set_led(em100, green_on); |
| 191 | printf("\nFPGA registers:"); |
| 192 | for (i = 0; i < 256; i += 2) { |
| 193 | uint16_t val; |
| 194 | if ((i % 16) == 0) |
| 195 | printf("\n %04x: ", i); |
| 196 | if (read_fpga_register(em100, i, &val)) |
| 197 | printf("%04x ", val); |
| 198 | else |
| 199 | printf("XXXX "); |
| 200 | } |
| 201 | |
| 202 | printf("\n"); |
| 203 | return 1; |
| 204 | } |
| 205 | |
Stefan Reinauer | 5287511 | 2015-08-20 13:57:20 -0700 | [diff] [blame] | 206 | static int check_status(struct em100 *em100) |
| 207 | { |
| 208 | int spi_flash_id; |
| 209 | |
| 210 | spi_flash_id = get_spi_flash_id(em100); |
Stefan Reinauer | 22340e8 | 2015-08-24 12:26:20 -0700 | [diff] [blame] | 211 | /* Check for Micron (formerly Numonyx, formerly STMicro) |
| 212 | * M25P16 spi flash part |
| 213 | */ |
Stefan Reinauer | 5287511 | 2015-08-20 13:57:20 -0700 | [diff] [blame] | 214 | if (spi_flash_id == 0x202015) |
| 215 | return 1; |
| 216 | return 0; |
| 217 | } |
| 218 | |
Stefan Reinauer | 081bb82 | 2015-09-03 16:26:10 -0700 | [diff] [blame] | 219 | static int em100_attach(struct em100 *em100, int bus, int device) |
Stefan Reinauer | 5287511 | 2015-08-20 13:57:20 -0700 | [diff] [blame] | 220 | { |
Stefan Reinauer | 5287511 | 2015-08-20 13:57:20 -0700 | [diff] [blame] | 221 | libusb_device_handle *dev; |
| 222 | libusb_context *ctx = NULL; |
| 223 | |
| 224 | if (libusb_init(&ctx) < 0) { |
| 225 | printf("Could not init libusb.\n"); |
| 226 | return 0; |
| 227 | } |
| 228 | |
| 229 | libusb_set_debug(ctx, 3); |
| 230 | |
Stefan Reinauer | 081bb82 | 2015-09-03 16:26:10 -0700 | [diff] [blame] | 231 | if (!bus || !device) { |
| 232 | dev = libusb_open_device_with_vid_pid(ctx, 0x4b4, 0x1235); |
| 233 | } else { |
| 234 | libusb_device **devs, *d; |
| 235 | int i; |
| 236 | |
| 237 | if (libusb_get_device_list(ctx, &devs) < 0) { |
| 238 | printf("Could not find USB devices.\n"); |
| 239 | return 0; |
| 240 | } |
| 241 | |
| 242 | for (i = 0; (d = devs[i]) != NULL; i++) { |
| 243 | if ((bus > 0 && (libusb_get_bus_number(d) == bus)) && |
| 244 | (device > 0 && (libusb_get_device_address(d) == device))) { |
| 245 | struct libusb_device_descriptor desc; |
| 246 | libusb_get_device_descriptor(d, &desc); |
| 247 | if (desc.idVendor == 0x4b4 && desc.idProduct == 0x1235) { |
| 248 | if (libusb_open(d, &dev)) { |
| 249 | printf("Couldn't open EM100pro device.\n"); |
| 250 | return 0; |
| 251 | } |
| 252 | } else { |
| 253 | printf("USB device on bus %03d:%02d is not an EM100pro.\n", bus, device); |
| 254 | return 0; |
| 255 | } |
| 256 | break; |
| 257 | } |
| 258 | } |
| 259 | libusb_free_device_list(devs, 1); |
Stefan Reinauer | 5287511 | 2015-08-20 13:57:20 -0700 | [diff] [blame] | 260 | } |
| 261 | |
Stefan Reinauer | 5287511 | 2015-08-20 13:57:20 -0700 | [diff] [blame] | 262 | if (!dev) { |
Stefan Reinauer | 081bb82 | 2015-09-03 16:26:10 -0700 | [diff] [blame] | 263 | printf("Could not find EM100pro.\n"); |
Stefan Reinauer | 5287511 | 2015-08-20 13:57:20 -0700 | [diff] [blame] | 264 | return 0; |
| 265 | } |
| 266 | |
Stefan Reinauer | 5287511 | 2015-08-20 13:57:20 -0700 | [diff] [blame] | 267 | if (libusb_kernel_driver_active(dev, 0) == 1) { |
| 268 | if (libusb_detach_kernel_driver(dev, 0) != 0) { |
| 269 | printf("Could not detach kernel driver.\n"); |
| 270 | return 0; |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | if (libusb_claim_interface(dev, 0) < 0) { |
| 275 | printf("Could not claim interface.\n"); |
| 276 | return 0; |
| 277 | } |
| 278 | |
| 279 | em100->dev = dev; |
| 280 | em100->ctx = ctx; |
| 281 | |
| 282 | if (!check_status(em100)) { |
| 283 | printf("Device status unknown.\n"); |
| 284 | return 0; |
| 285 | } |
| 286 | |
| 287 | if (!get_version(em100)) { |
| 288 | printf("Failed fetching version information.\n"); |
| 289 | return 0; |
| 290 | } |
| 291 | |
| 292 | if (!get_serialno(em100)) { |
| 293 | printf("Failed fetching serial number.\n"); |
| 294 | return 0; |
| 295 | } |
| 296 | |
| 297 | return 1; |
| 298 | } |
| 299 | |
| 300 | static int em100_detach(struct em100 *em100) |
| 301 | { |
| 302 | if (libusb_release_interface(em100->dev, 0) != 0) { |
| 303 | printf("releasing interface failed.\n"); |
| 304 | return 1; |
| 305 | } |
| 306 | |
| 307 | libusb_close(em100->dev); |
| 308 | libusb_exit(em100->ctx); |
| 309 | |
| 310 | return 0; |
| 311 | } |
| 312 | |
Stefan Reinauer | 7eda75e | 2015-09-03 16:32:13 -0700 | [diff] [blame^] | 313 | static int em100_list(void) |
| 314 | { |
| 315 | struct em100 em100; |
| 316 | libusb_device **devs, *dev; |
| 317 | libusb_context *ctx = NULL; |
| 318 | int i, count = 0; |
| 319 | |
| 320 | if (libusb_init(&ctx) < 0) { |
| 321 | printf("Could not init libusb.\n"); |
| 322 | return 0; |
| 323 | } |
| 324 | |
| 325 | libusb_set_debug(ctx, 3); |
| 326 | |
| 327 | if (libusb_get_device_list(ctx, &devs) < 0) { |
| 328 | printf("Could not find USB devices.\n"); |
| 329 | return 0; |
| 330 | } |
| 331 | |
| 332 | for (i = 0; (dev = devs[i]) != NULL; i++) { |
| 333 | struct libusb_device_descriptor desc; |
| 334 | libusb_get_device_descriptor(dev, &desc); |
| 335 | if (desc.idVendor != 0x4b4 || desc.idProduct != 0x1235) |
| 336 | continue; |
| 337 | |
| 338 | em100_attach(&em100, libusb_get_bus_number(dev), |
| 339 | libusb_get_device_address(dev)); |
| 340 | printf(" Bus %03d Device %03d: EM100pro DP%06d\n", |
| 341 | libusb_get_bus_number(dev), |
| 342 | libusb_get_device_address(dev), |
| 343 | em100.serialno); |
| 344 | em100_detach(&em100); |
| 345 | count++; |
| 346 | } |
| 347 | if (count == 0) |
| 348 | printf("No EM100pro devices found.\n"); |
| 349 | libusb_exit(ctx); |
| 350 | return 1; |
| 351 | } |
| 352 | |
Stefan Reinauer | 5287511 | 2015-08-20 13:57:20 -0700 | [diff] [blame] | 353 | static int set_chip_type(struct em100 *em100, const chipdesc *desc) |
| 354 | { |
| 355 | unsigned char cmd[16]; |
| 356 | /* result counts unsuccessful send_cmd()s. |
| 357 | * These are then converted in a boolean success value |
| 358 | */ |
| 359 | int result = 0; |
| 360 | int i; |
Stefan Reinauer | 53cf234 | 2015-08-28 16:56:40 -0700 | [diff] [blame] | 361 | int fpga_voltage, chip_voltage = 0, wrong_voltage = 0; |
Stefan Reinauer | 5287511 | 2015-08-20 13:57:20 -0700 | [diff] [blame] | 362 | |
| 363 | printf("Sending flash chip configuration\n"); |
| 364 | |
| 365 | memset(cmd, 0, 16); |
| 366 | |
Stefan Reinauer | 53cf234 | 2015-08-28 16:56:40 -0700 | [diff] [blame] | 367 | fpga_voltage = em100->fpga & 0x8000 ? 1800 : 3300; |
| 368 | |
| 369 | for (i = 0; i < desc->init_len; i++) { |
| 370 | if(desc->init[i][0] != 0x11 || desc->init[i][1] != 0x04) |
| 371 | continue; |
| 372 | |
| 373 | chip_voltage = (desc->init[i][2] << 8) | desc->init[i][3]; |
| 374 | |
| 375 | switch (chip_voltage) { |
| 376 | case 1601: /* 1.65V-2V */ |
| 377 | case 1800: |
| 378 | if (fpga_voltage == 3300) |
| 379 | wrong_voltage = 1; |
| 380 | break; |
| 381 | case 2500: /* supported by both 1.8V and 3.3V FPGA */ |
| 382 | break; |
| 383 | case 3300: |
| 384 | if (fpga_voltage == 1800) |
| 385 | wrong_voltage = 1; |
| 386 | } |
| 387 | break; |
| 388 | } |
| 389 | |
| 390 | if (wrong_voltage) { |
| 391 | printf("Error: The current FPGA firmware (%.1fV) does not " |
| 392 | "support %s %s (%.1fV)\n", (float)fpga_voltage/1000, |
| 393 | desc->vendor, desc->name, (float)chip_voltage/1000); |
| 394 | return 0; |
| 395 | } |
| 396 | |
Stefan Reinauer | 5287511 | 2015-08-20 13:57:20 -0700 | [diff] [blame] | 397 | for (i = 0; i < desc->init_len; i++) { |
| 398 | memcpy(&cmd[0], &desc->init[i][0], BYTES_PER_INIT_ENTRY); |
| 399 | result += !send_cmd(em100->dev, cmd); |
| 400 | } |
| 401 | |
| 402 | return !result; |
| 403 | } |
| 404 | |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 405 | static const struct option longopts[] = { |
| 406 | {"set", 1, 0, 'c'}, |
| 407 | {"download", 1, 0, 'd'}, |
| 408 | {"start", 0, 0, 'r'}, |
| 409 | {"stop", 0, 0, 's'}, |
| 410 | {"verify", 0, 0, 'v'}, |
Aaron Durbin | 637fe48 | 2012-10-17 17:39:19 -0500 | [diff] [blame] | 411 | {"holdpin", 1, 0, 'p'}, |
Stefan Reinauer | 14d3512 | 2015-09-03 16:02:55 -0700 | [diff] [blame] | 412 | {"debug", 0, 0, 'D'}, |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 413 | {"help", 0, 0, 'h'}, |
Stefan Reinauer | a5b46d4 | 2015-09-03 16:14:25 -0700 | [diff] [blame] | 414 | {"trace", 0, 0, 't'}, |
Stefan Reinauer | e636d2a | 2015-08-28 16:58:57 -0700 | [diff] [blame] | 415 | {"set-serialno", 1, 0, 'S'}, |
Stefan Reinauer | a2b67d3 | 2015-09-01 16:30:43 -0700 | [diff] [blame] | 416 | {"firmware-update", 1, 0, 'F'}, |
| 417 | {"firmware-dump", 1, 0, 'f'}, |
Stefan Reinauer | 7eda75e | 2015-09-03 16:32:13 -0700 | [diff] [blame^] | 418 | {"device", 1, 0, 'x'}, |
| 419 | {"list-devices", 0, 0, 'l'}, |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 420 | {NULL, 0, 0, 0} |
| 421 | }; |
| 422 | |
Stefan Reinauer | a5b46d4 | 2015-09-03 16:14:25 -0700 | [diff] [blame] | 423 | static void usage(char *name) |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 424 | { |
Stefan Reinauer | a5b46d4 | 2015-09-03 16:14:25 -0700 | [diff] [blame] | 425 | printf("em100: EM100pro command line utility\n\nExample:\n" |
| 426 | " %s --stop --set M25P80 -d file.bin -v --start\n" |
| 427 | "\nUsage:\n" |
| 428 | " -c|--set CHIP: select chip emulation\n" |
| 429 | " -d|--download FILE: upload FILE into EM100pro\n" |
Stefan Reinauer | 75bcb1f | 2015-08-24 12:21:02 -0700 | [diff] [blame] | 430 | " -r|--start: em100 shall run\n" |
| 431 | " -s|--stop: em100 shall stop\n" |
| 432 | " -v|--verify: verify EM100 content matches the file\n" |
Stefan Reinauer | a5b46d4 | 2015-09-03 16:14:25 -0700 | [diff] [blame] | 433 | " -t|--trace: trace mode\n" |
Stefan Reinauer | a2b67d3 | 2015-09-01 16:30:43 -0700 | [diff] [blame] | 434 | " -F|--firmware-update FILE: update firmware in EM100pro (dangerous)\n" |
Stefan Reinauer | a5b46d4 | 2015-09-03 16:14:25 -0700 | [diff] [blame] | 435 | " -f|--firmware-dump FILE: export firmware in EM100pro to file\n" |
| 436 | " -S|--set-serialno NUM: set serial number to NUM\n" |
| 437 | " -p|--holdpin [LOW|FLOAT|INPUT]: set the hold pin state\n" |
Stefan Reinauer | 7eda75e | 2015-09-03 16:32:13 -0700 | [diff] [blame^] | 438 | " -x|--device BUS:DEV use EM100pro on USB bus/device\n" |
| 439 | " -l|--list-devices list all connected EM100pro devices\n" |
Stefan Reinauer | 14d3512 | 2015-09-03 16:02:55 -0700 | [diff] [blame] | 440 | " -D|--debug: print debug information.\n" |
Stefan Reinauer | a5b46d4 | 2015-09-03 16:14:25 -0700 | [diff] [blame] | 441 | " -h|--help: this help text\n\n", name); |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 442 | } |
| 443 | |
| 444 | /* get MCU and FPGA version, *100 encoded */ |
| 445 | int main(int argc, char **argv) |
| 446 | { |
| 447 | int opt, idx; |
| 448 | const char *desiredchip = NULL; |
Stefan Reinauer | e636d2a | 2015-08-28 16:58:57 -0700 | [diff] [blame] | 449 | const char *serialno = NULL; |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 450 | const char *filename = NULL; |
Stefan Reinauer | a2b67d3 | 2015-09-01 16:30:43 -0700 | [diff] [blame] | 451 | const char *firmware_in = NULL, *firmware_out = NULL; |
Aaron Durbin | 637fe48 | 2012-10-17 17:39:19 -0500 | [diff] [blame] | 452 | const char *holdpin = NULL; |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 453 | int do_start = 0, do_stop = 0; |
Stefan Reinauer | a2b67d3 | 2015-09-01 16:30:43 -0700 | [diff] [blame] | 454 | int verify = 0, trace = 0; |
Stefan Reinauer | a5b46d4 | 2015-09-03 16:14:25 -0700 | [diff] [blame] | 455 | int debug = 0; |
Stefan Reinauer | 081bb82 | 2015-09-03 16:26:10 -0700 | [diff] [blame] | 456 | int bus = 0, device = 0; |
Stefan Reinauer | 7eda75e | 2015-09-03 16:32:13 -0700 | [diff] [blame^] | 457 | while ((opt = getopt_long(argc, argv, "c:d:rsvtF:f:S:p:Dx:lh", |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 458 | longopts, &idx)) != -1) { |
| 459 | switch (opt) { |
| 460 | case 'c': |
| 461 | desiredchip = optarg; |
| 462 | break; |
| 463 | case 'd': |
| 464 | filename = optarg; |
| 465 | /* TODO: check that file exists */ |
| 466 | break; |
Aaron Durbin | 637fe48 | 2012-10-17 17:39:19 -0500 | [diff] [blame] | 467 | case 'p': |
| 468 | holdpin = optarg; |
| 469 | break; |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 470 | case 'r': |
| 471 | do_start = 1; |
| 472 | break; |
| 473 | case 's': |
Aaron Durbin | f558702 | 2012-10-16 15:37:12 -0500 | [diff] [blame] | 474 | do_stop = 1; |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 475 | break; |
| 476 | case 'v': |
| 477 | verify = 1; |
| 478 | break; |
Patrick Georgi | f3558c6 | 2014-10-28 18:16:57 +0100 | [diff] [blame] | 479 | case 't': |
| 480 | trace = 1; |
| 481 | break; |
Stefan Reinauer | e636d2a | 2015-08-28 16:58:57 -0700 | [diff] [blame] | 482 | case 'S': |
| 483 | serialno = optarg; |
Stefan Reinauer | 14d3512 | 2015-09-03 16:02:55 -0700 | [diff] [blame] | 484 | case 'D': |
| 485 | debug=1; |
Stefan Reinauer | e636d2a | 2015-08-28 16:58:57 -0700 | [diff] [blame] | 486 | break; |
Stefan Reinauer | a2b67d3 | 2015-09-01 16:30:43 -0700 | [diff] [blame] | 487 | case 'F': |
| 488 | firmware_in = optarg; |
| 489 | break; |
| 490 | case 'f': |
| 491 | firmware_out = optarg; |
| 492 | break; |
Stefan Reinauer | 7eda75e | 2015-09-03 16:32:13 -0700 | [diff] [blame^] | 493 | case 'x': |
| 494 | sscanf(optarg, "%d:%d", &bus, &device); |
| 495 | break; |
| 496 | case 'l': |
| 497 | em100_list(); |
| 498 | return 0; |
Stefan Reinauer | a5b46d4 | 2015-09-03 16:14:25 -0700 | [diff] [blame] | 499 | default: |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 500 | case 'h': |
Stefan Reinauer | a5b46d4 | 2015-09-03 16:14:25 -0700 | [diff] [blame] | 501 | usage(argv[0]); |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 502 | return 0; |
| 503 | } |
| 504 | } |
| 505 | |
| 506 | const chipdesc *chip = chips; |
| 507 | if (desiredchip) { |
| 508 | do { |
Martin Roth | 92c453b | 2015-09-01 10:35:02 -0600 | [diff] [blame] | 509 | if (strcasecmp(desiredchip, chip->name) == 0) { |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 510 | printf("will emulate '%s'\n", chip->name); |
| 511 | break; |
| 512 | } |
| 513 | } while ((++chip)->name); |
| 514 | |
| 515 | if (chip->name == NULL) { |
Stefan Reinauer | 677b4fe | 2012-08-15 13:44:45 -0700 | [diff] [blame] | 516 | printf("Supported chips:\n"); |
| 517 | chip = chips; |
| 518 | do { |
| 519 | printf("%s ", chip->name); |
| 520 | } while ((++chip)->name); |
| 521 | printf("\n\nCould not find emulation for '%s'.\n", desiredchip); |
| 522 | |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 523 | return 1; |
| 524 | } |
| 525 | } |
| 526 | |
| 527 | struct em100 em100; |
Stefan Reinauer | 081bb82 | 2015-09-03 16:26:10 -0700 | [diff] [blame] | 528 | if (!em100_attach(&em100, bus, device)) { |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 529 | return 1; |
| 530 | } |
| 531 | |
Stefan Reinauer | 56658b1 | 2015-08-18 12:41:49 -0700 | [diff] [blame] | 532 | printf("MCU version: %d.%02d\n", em100.mcu >> 8, em100.mcu & 0xff); |
Stefan Reinauer | 14deb03 | 2015-08-28 16:33:00 -0700 | [diff] [blame] | 533 | if (em100.fpga > 0x0033) { /* 0.51 */ |
| 534 | printf("FPGA version: %d.%02d (%s)\n", |
| 535 | em100.fpga >> 8 & 0x7f, em100.fpga & 0xff, |
| 536 | em100.fpga & 0x8000 ? "1.8V" : "3.3V"); |
| 537 | } else { |
| 538 | /* While the Dediprog software for Windows will refuse to work |
| 539 | * with 1.8V chips on older FPGA versions, it does not |
| 540 | * specifically output a voltage when reporting the FPGA |
| 541 | * version. We emulate this behavior here. Version 0.51 is |
| 542 | * known to behave the old way, 0.75 is behaving the new |
| 543 | * way. |
| 544 | */ |
| 545 | printf("FPGA version: %d.%02d\n", em100.fpga >> 8, em100.fpga & 0xff); |
| 546 | } |
| 547 | |
| 548 | if (em100.serialno != 0xffffffff) |
| 549 | printf("Serial number: DP%06d\n", em100.serialno); |
| 550 | else |
| 551 | printf("Serial number: N.A.\n"); |
Stefan Reinauer | 4859592 | 2015-08-24 12:24:05 -0700 | [diff] [blame] | 552 | printf("SPI flash database: %s\n", VERSION); |
Stefan Reinauer | 14d3512 | 2015-09-03 16:02:55 -0700 | [diff] [blame] | 553 | if(debug) { |
| 554 | em100_debug(&em100); |
| 555 | } |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 556 | |
Stefan Reinauer | a2b67d3 | 2015-09-01 16:30:43 -0700 | [diff] [blame] | 557 | if (firmware_in) { |
| 558 | firmware_update(&em100, firmware_in, verify); |
| 559 | return em100_detach(&em100); |
| 560 | } |
| 561 | |
| 562 | if (firmware_out) { |
| 563 | firmware_dump(&em100, firmware_out); |
| 564 | return em100_detach(&em100); |
| 565 | } |
| 566 | |
Stefan Reinauer | e636d2a | 2015-08-28 16:58:57 -0700 | [diff] [blame] | 567 | if (serialno) { |
| 568 | unsigned int serial_number; |
| 569 | if (sscanf(serialno, "%d", &serial_number) != 1) |
| 570 | printf("Error: Can't parse serial number '%s'\n", |
| 571 | serialno); |
| 572 | else |
| 573 | set_serialno(&em100, serial_number); |
| 574 | |
| 575 | return em100_detach(&em100); |
| 576 | } |
| 577 | |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 578 | if (do_stop) { |
Stefan Reinauer | 4e5f04a | 2015-08-18 09:23:22 -0700 | [diff] [blame] | 579 | set_state(&em100, 0); |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 580 | } |
| 581 | |
| 582 | if (desiredchip) { |
Stefan Reinauer | 4e5f04a | 2015-08-18 09:23:22 -0700 | [diff] [blame] | 583 | if (!set_chip_type(&em100, chip)) { |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 584 | printf("Failed configuring chip type.\n"); |
| 585 | return 0; |
| 586 | } |
| 587 | } |
| 588 | |
Aaron Durbin | 637fe48 | 2012-10-17 17:39:19 -0500 | [diff] [blame] | 589 | if (holdpin) { |
Stefan Reinauer | 4e5f04a | 2015-08-18 09:23:22 -0700 | [diff] [blame] | 590 | if (!set_hold_pin_state_from_str(&em100, holdpin)) { |
Aaron Durbin | 637fe48 | 2012-10-17 17:39:19 -0500 | [diff] [blame] | 591 | printf("Failed configuring hold pin state.\n"); |
| 592 | return 0; |
| 593 | } |
| 594 | } |
| 595 | |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 596 | if (filename) { |
Martin Roth | ac82bea | 2014-08-02 09:04:17 -0600 | [diff] [blame] | 597 | int maxlen = 0x1000000; /* largest size - 16MB */ |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 598 | void *data = malloc(maxlen); |
| 599 | if (data == NULL) { |
| 600 | printf("FATAL: couldn't allocate memory\n"); |
| 601 | return 1; |
| 602 | } |
| 603 | FILE *fdata = fopen(filename, "rb"); |
| 604 | if (!fdata) { |
| 605 | perror("Could not open upload file"); |
| 606 | return 1; |
| 607 | } |
| 608 | |
| 609 | int length = 0; |
| 610 | while ((!feof(fdata)) && (length < maxlen)) { |
| 611 | int blocksize = 65536; |
| 612 | length += blocksize * fread(data+length, blocksize, 1, fdata); |
| 613 | } |
| 614 | fclose(fdata); |
| 615 | |
| 616 | if (length > maxlen) { |
| 617 | printf("FATAL: length > maxlen\n"); |
| 618 | return 1; |
| 619 | } |
| 620 | |
Stefan Reinauer | ef3ec9f | 2015-08-18 09:55:00 -0700 | [diff] [blame] | 621 | write_sdram(&em100, (unsigned char *)data, 0x00000000, length); |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 622 | if (verify) { |
| 623 | int done; |
| 624 | void *readback = malloc(length); |
| 625 | if (data == NULL) { |
| 626 | printf("FATAL: couldn't allocate memory\n"); |
| 627 | return 1; |
| 628 | } |
Stefan Reinauer | ef3ec9f | 2015-08-18 09:55:00 -0700 | [diff] [blame] | 629 | done = read_sdram(&em100, readback, 0x00000000, length); |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 630 | if (done && (memcmp(data, readback, length) == 0)) |
| 631 | printf("Verify: PASS\n"); |
| 632 | else |
| 633 | printf("Verify: FAIL\n"); |
| 634 | free(readback); |
| 635 | } |
| 636 | |
| 637 | free(data); |
| 638 | } |
| 639 | |
| 640 | if (do_start) { |
Stefan Reinauer | 4e5f04a | 2015-08-18 09:23:22 -0700 | [diff] [blame] | 641 | set_state(&em100, 1); |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 642 | } |
| 643 | |
Patrick Georgi | f3558c6 | 2014-10-28 18:16:57 +0100 | [diff] [blame] | 644 | if (trace) { |
| 645 | struct sigaction signal_action; |
| 646 | |
Stefan Reinauer | 4e5f04a | 2015-08-18 09:23:22 -0700 | [diff] [blame] | 647 | if (!set_hold_pin_state(&em100, 3)) { |
Stefan Reinauer | cf24ed7 | 2015-08-24 12:21:45 -0700 | [diff] [blame] | 648 | printf("Error: Failed to set EM100 to input\n"); |
Patrick Georgi | f3558c6 | 2014-10-28 18:16:57 +0100 | [diff] [blame] | 649 | return 1; |
| 650 | } |
Stefan Reinauer | 4e5f04a | 2015-08-18 09:23:22 -0700 | [diff] [blame] | 651 | set_state(&em100, 1); |
| 652 | reset_spi_trace(&em100); |
Patrick Georgi | f3558c6 | 2014-10-28 18:16:57 +0100 | [diff] [blame] | 653 | |
| 654 | signal_action.sa_handler = exit_handler; |
| 655 | signal_action.sa_flags = 0; |
| 656 | sigemptyset(&signal_action.sa_mask); |
| 657 | sigaction(SIGINT, &signal_action, NULL); |
| 658 | |
| 659 | while (!do_exit_flag) { |
Stefan Reinauer | 4e5f04a | 2015-08-18 09:23:22 -0700 | [diff] [blame] | 660 | read_spi_trace(&em100); |
Patrick Georgi | f3558c6 | 2014-10-28 18:16:57 +0100 | [diff] [blame] | 661 | } |
| 662 | |
Stefan Reinauer | 4e5f04a | 2015-08-18 09:23:22 -0700 | [diff] [blame] | 663 | set_state(&em100, 0); |
| 664 | reset_spi_trace(&em100); |
Patrick Georgi | f3558c6 | 2014-10-28 18:16:57 +0100 | [diff] [blame] | 665 | |
Stefan Reinauer | 4e5f04a | 2015-08-18 09:23:22 -0700 | [diff] [blame] | 666 | if (!set_hold_pin_state(&em100, 2)) { |
Stefan Reinauer | cf24ed7 | 2015-08-24 12:21:45 -0700 | [diff] [blame] | 667 | printf("Error: Failed to set EM100 to float\n"); |
Patrick Georgi | f3558c6 | 2014-10-28 18:16:57 +0100 | [diff] [blame] | 668 | return 1; |
| 669 | } |
| 670 | } |
| 671 | |
Stefan Reinauer | 0ce691f | 2012-08-03 14:20:24 -0700 | [diff] [blame] | 672 | return em100_detach(&em100); |
| 673 | } |