├── .gitignore ├── Makefile ├── systemd ├── upd72020x-fwload-resume.service └── upd72020x-fwload.service ├── upd72020x-check-and-init ├── README.md └── upd72020x-load.c /.gitignore: -------------------------------------------------------------------------------- 1 | upd72020x-load 2 | *.mem 3 | 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME=upd72020x-load 2 | CC ?= gcc 3 | 4 | .PHONY: all clean 5 | 6 | all: $(NAME) 7 | 8 | clean: 9 | rm $(NAME) 10 | 11 | $(NAME): upd72020x-load.c 12 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ 13 | -------------------------------------------------------------------------------- /systemd/upd72020x-fwload-resume.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Restart firmware loader after system resume 3 | After=suspend.target 4 | 5 | [Service] 6 | Type=simple 7 | ExecStart=/usr/bin/systemctl restart upd72020x-fwload 8 | 9 | [Install] 10 | WantedBy=suspend.target 11 | 12 | -------------------------------------------------------------------------------- /systemd/upd72020x-fwload.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Load firmware for Renesas uPD72020x USB3.0 host. 3 | DefaultDependencies=false 4 | After=local-fs.target 5 | 6 | [Service] 7 | Type=oneshot 8 | RemainAfterExit=yes 9 | ExecStart=/usr/sbin/upd72020x-check-and-init 10 | Restart=no 11 | Environment="UPD72020X_FW=/lib/firmware/renesas/K2026.mem" 12 | Environment="UPD72020X_CMD=/usr/sbin/upd72020x-load" 13 | 14 | [Install] 15 | WantedBy=remote-fs.target 16 | 17 | -------------------------------------------------------------------------------- /upd72020x-check-and-init: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | dir=$(dirname $(readlink -f "$0")) 4 | 5 | cd "$dir" 6 | 7 | # set paths to loader and firmware, if not provided by environment 8 | if [ -z "$UPD72020X_CMD" ]; then 9 | UPD72020X_CMD=./upd72020x-load 10 | fi 11 | if [ -z "$UPD72020X_FW" ]; then 12 | UPD72020X_FW=K2026.mem 13 | fi 14 | 15 | # test if loader and firmware can be found 16 | if ! [ -x "$UPD72020X_CMD" ]; then 17 | echo "upd72020x loader not found at $UPD72020X_CMD" 18 | 19 | exit 1 20 | fi 21 | if ! [ -r "$UPD72020X_FW" ]; then 22 | echo "upd72020x firmware not found at $UPD72020X_FW" 23 | 24 | exit 2 25 | fi 26 | 27 | 28 | lspci -m | egrep "uPD720201|uPD720202" | while read device trailer; do 29 | echo "Found possible uPD72020x on $device" 30 | if ! dmesg | grep "0000:$device" | tail -n 1 | grep -e "-110" > /dev/null; then 31 | echo "May already be programmed, skipping." 32 | continue # not affected, device got up normally 33 | fi 34 | 35 | # attempt to avoid kernel interference on upload process 36 | if [ -e /sys/bus/pci/drivers/xhci_hcd/unbind ]; then 37 | echo -n 0000:$device > /sys/bus/pci/drivers/xhci_hcd/unbind 38 | fi 39 | 40 | bus=$( echo "$device" | cut -d : -f 1) 41 | dev=$( echo "$device" | cut -d : -f 2 | cut -d . -f 1) 42 | fun=$( echo "$device" | cut -d : -f 2 | cut -d . -f 2) 43 | 44 | # upload to RAM only, do not write EEPROM 45 | echo "Uploading firmware to $device" 46 | "$UPD72020X_CMD" -u -b 0x$bus -d 0x$dev -f 0x$fun -i "$UPD72020X_FW" 47 | 48 | # revert change 49 | if [ -e /sys/bus/pci/drivers/xhci_hcd/unbind ]; then 50 | echo -n 0000:$device > /sys/bus/pci/drivers/xhci_hcd/bind 51 | fi 52 | 53 | sleep 2 54 | 55 | echo 1 > "/sys/bus/pci/devices/0000:$device/remove" 56 | echo "Done with $device" 57 | done 58 | 59 | sleep 1 60 | 61 | echo 1 > /sys/bus/pci/rescan 62 | 63 | echo "Done with all devices" 64 | 65 | exit 0 66 | 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # upd72020x-load 2 | 3 | upd72020x-load is a Linux userspace firmware loader for Renesas uPD720201/uPD720202 familiy USB 3.0 controllers. 4 | It provides the functionality to upload the firmware required for certain extension cards before they can used with Linux. 5 | 6 | ## Usage 7 | 8 | * First, a firmware image for the chipset is required. 9 | It might be extracted from the update tool for Windows (either downloaded from the Internet or obtained from a driver disk shipped with the card). 10 | I am using firmware version 2.0.2.6 with my uPD720202-based card. I extracted the firmware image from an updater named `k2026fwup1` with SHA256 hash `9fe8fa750e45580ab594df2ba9435b91f10a6f1281029531c13f179cd8a6523c`. The firmware image has the SHA256 has `177560c224c73d040836b17082348036430ecf59e8a32d7736ce2b80b2634f97`. 11 | * The firmware image is uploaded into the chipset RAM using the command `./upd72020x-load -u -b 0x02 -d 0x00 -f 0x0 -i K2026.mem` with -b, -d and -f specifying the PCI bus, device and function address. 12 | This process is non-persistent, the chipset RAM is cleared when the power supply is removed. 13 | * The error message `ERROR: SET_DATAx never go to zero` is apparently sometimes(?) caused by a conflict with the XHCI kernel driver. 14 | Use `echo -n 0000:02:00.0 > /sys/bus/pci/drivers/xhci_hcd/unbind` before and `echo -n 0000:02:00.0 > /sys/bus/pci/drivers/xhci_hcd/bind` after running upd72020x-load, with the correct PCI address for your computer. 15 | * The script `upd72020x-check-and-init` automates the upload process. It parses the output of `dmesg` for uPD72020x controllers which failed to initialize during boot and attemps to upload the firmware file to them. 16 | It also performs the driver unbind/bind commands to work around the conflict with the XHCI kernel driver. 17 | I simply call the script from rc.local, but a SystemD service file is also included. 18 | For using SystemD, please adjust the paths/environment variables in the unit file according to your install locations of script, loader and firmware image. 19 | If no environment variables are set, the script presumes that loader and firmware image are co-located with itself in the same directory. 20 | * Code to read and write the (optional) EEPROM (commands `-r` and `-w`) is implemented as well. 21 | Reading should work, the write feature is untested, but shares the codebase with the upload feature. 22 | However, the documentation indicates a special memory layout for the EEPROM. 23 | Besides the firmware, it may contain a Vendor Specific Configuration Data section (VSCD) (see also discussion in [#4](https://github.com/markusj/upd72020x-load/issues/4)). 24 | This should be taken into account when trying to write to the EEPROM since this tool does not deal with the memory layout. 25 | Thus, I strongly discourage you from using the `-w` command unless you surely known what you are doing. 26 | 27 | ## Some technical details 28 | 29 | The uPD72020x USB 3.0 chipset familiy supports two modes of operation. 30 | Either the firmware is stored at an external EEPROM chip and downloaded into the chipset at boot time, or it must be uploaded into the chipset by the operating system / driver. 31 | The second option always works and overrides any firmware stored into the EEPROM of the card. 32 | 33 | The story behind this tool and more technical details are discussed in a [blog post](https://mjott.de/blog/881-renesas-usb-3-0-controllers-vs-linux/). 34 | 35 | ## Closing remarks 36 | 37 | This tool was only tested with an uPD720202 based extension card. 38 | The up- and download protocol of the uPD720201 chipset is identical according to the specification, and reports confirmed the tool to work for them, too. 39 | 40 | And of course: Use this tool at your own risk! 41 | 42 | ## Acknowledgements 43 | 44 | This code is based on the code which was written the comments section of [this blogpost](http://billauer.co.il/blog/2015/11/renesas-rom-setpci/). 45 | 46 | * The SystemD unit file was originally provided by K.Ohta (@Artanejp) 47 | * Vendor and device IDs for uPD720201 chipsets were provided by @j1warren 48 | * The workaround for kernel drivers interfering with the upload process was found by @cranphin 49 | -------------------------------------------------------------------------------- /upd72020x-load.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define FAILED " ======> FAILED\n" 11 | #define PASSED " ======> PASSED\n" 12 | #define LOOPNB 100000 13 | #define POLL_US 10 14 | #define DELAY_US 1000 15 | #define ROM_PARAM_INVALID 0xffffffff 16 | 17 | // Register addresses 18 | #define EXT_FW_VERSION 0x6C 19 | #define EXT_ROM_INFO_REG 0xEC 20 | #define EXT_ROM_CONFIG_REG 0xF0 21 | #define EXT_FW_DLOAD_CTRL_STATUS 0xF4 22 | #define EXT_ROM_CTRL_STATUS 0xF6 23 | #define EXT_DATA0 0xF8 24 | #define EXT_DATA1 0xFC 25 | 26 | // FW Control and Status Register 27 | #define ROM_ACCESS_ENABLE 0 28 | #define ROM_ERASE 1 29 | #define ROM_RELOAD 2 30 | #define ROM_RESULT_0 4 31 | #define ROM_RESULT_1 5 32 | #define ROM_RESULT_2 6 33 | #define ROM_SET_DATA0 8 34 | #define ROM_SET_DATA1 9 35 | #define ROM_GET_DATA0 10 36 | #define ROM_GET_DATA1 11 37 | #define ROM_EXISTS 15 38 | 39 | // FW Download Control and Status Register 40 | #define FW_DLOAD_ENABLE 0 41 | #define FW_DLOAD_LOCK 1 42 | #define FW_RESULT_0 4 43 | #define FW_RESULT_1 5 44 | #define FW_RESULT_2 6 45 | #define FW_SET_DATA0 8 46 | #define FW_SET_DATA1 9 47 | 48 | #define RESULT_BITMASK (0x0070) 49 | #define RESULT_INVALID (0x0000) 50 | #define RESULT_SUCCESS (0x0010) 51 | #define RESULT_ERROR (0x0020)) 52 | 53 | #define DATAREG(index) \ 54 | (((index & 0x1) != 0) ? EXT_DATA1 : EXT_DATA0) 55 | 56 | #define RETURN_ON_ERR(condition, msg, ...) \ 57 | if (condition) { \ 58 | printf(msg, ##__VA_ARGS__); \ 59 | return -1; \ 60 | } 61 | #define RETURN_ON_REG_ERR(condition, ...) \ 62 | RETURN_ON_ERR(condition, "ERROR: failed to access register", ##__VA_ARGS__) 63 | 64 | u_int lookup_rompar(const u_int rominfo) { 65 | switch (rominfo) { 66 | case 0x00C22010: // MX25L512E 67 | case 0x00C22011: // MX25L1006E 68 | case 0x00C22012: // MX25L2006E 69 | case 0x00C22013: // MX25L4006E 70 | case 0x00C22016: // MX25L3233F, undocumented but working in 0x700 mode 71 | return 0x700; 72 | case 0x00C22210: // MX25L5121E 73 | case 0x00C22211: // MX25L1021E 74 | return 0x500; 75 | case 0x00EF3011: // W25X10BV 76 | case 0x00EF3012: // W25X20BV 77 | case 0x00EF3013: // W25X40BV 78 | return 0x700; 79 | case 0x00202010: // M25P05-A 80 | case 0x00202011: // M25P10-A 81 | return 0x750; 82 | case 0x00202012: // M25P20 83 | case 0x00202013: // M25P40 84 | return 0x760; 85 | case 0x005e2013: // T25S40, undocumented but working in 0x700 mode 86 | case 0x00856013: // P25Q40H, undocumented but working in 0x700 mode 87 | case 0x000B4013: // XT25F04B, undocumented but working in 0x700 mode 88 | case 0x019D20FF: // Pm25LD512C 89 | case 0x019D207F: // Pm25LD512C2 90 | case 0x001F6500: // AT25F512B 91 | case 0x001C3110: // EN25F05 92 | case 0x001C3111: // EN25F10 93 | case 0x001C3112: // EN25F20 94 | case 0x001C3113: // EN25F40 95 | case 0x00373010: // A25L512 96 | case 0x00373011: // A25L010 97 | case 0x00373012: // A25L020 98 | case 0x00373013: // A25L040 99 | return 0x700; 100 | case 0x00BF0048: // SST25VF512A 101 | case 0x00BF0049: // SST25VF010A 102 | return 0x10791; 103 | default: 104 | return ROM_PARAM_INVALID; 105 | } 106 | } 107 | 108 | int pci_cfg_read16(int fd, u_int off, u_int * val16) { 109 | lseek(fd, off, SEEK_SET); 110 | 111 | return read(fd, val16, 2); 112 | } 113 | 114 | int pci_cfg_write16(int fd, u_int off, u_int val16) { 115 | lseek(fd, off, SEEK_SET); 116 | 117 | return write(fd, &val16, 2); 118 | } 119 | 120 | int pci_cfg_read32(int fd, u_int off, u_int * val32) { 121 | lseek(fd, off, SEEK_SET); 122 | 123 | return read(fd, val32, 4); 124 | } 125 | 126 | int pci_cfg_write32(int fd, u_int off, u_int val32) { 127 | lseek(fd, off, SEEK_SET); 128 | 129 | return write(fd, &val32, 4); 130 | } 131 | 132 | /** 133 | * Support function for reading certain bits from a register 134 | * 135 | * @param fd 136 | * card file descriptor 137 | * @param reg 138 | * register address 139 | * @param bitmask 140 | * bit mask used to extract the desired bits 141 | * @param value 142 | * value to which extracted bits whould be written 143 | * @return 144 | * error code 145 | */ 146 | int read_bitmask(u_int fd, u_int reg, u_int bitmask, u_int * value) { 147 | int result; 148 | 149 | result = pci_cfg_read16(fd, reg, value); 150 | 151 | *value &= bitmask; 152 | 153 | return result; 154 | } 155 | 156 | /** 157 | * Support function for writing certain bits to a register 158 | * 159 | * @param fd 160 | * card file descriptor 161 | * @param reg 162 | * register address 163 | * @param bitmask 164 | * bitmask used to select which bits to write 165 | * @param value 166 | * value from which the masked bits should be written into the register 167 | * @return 168 | * error code 169 | */ 170 | int write_bitmask(u_int fd, u_int reg, u_int bitmask, u_int value) { 171 | int result; 172 | u_int val; 173 | 174 | result = pci_cfg_read16(fd, reg, &val); 175 | 176 | if (result >= 0) { 177 | val &= ~bitmask; 178 | val |= (value & bitmask); 179 | 180 | return pci_cfg_write16(fd, reg, val); 181 | } else { 182 | return result; 183 | } 184 | } 185 | 186 | /** 187 | * Support function for reading a bit from a register 188 | * 189 | * @param fd 190 | * card file descriptor 191 | * @param reg 192 | * register address 193 | * @param bit 194 | * bit offset 195 | * @param value 196 | * value to which extracted bit whould be written 197 | * @return 198 | * error code 199 | */ 200 | int read_bit(u_int fd, u_int reg, u_int bit, u_int * value) { 201 | return read_bitmask(fd, reg, 1 << bit, value); 202 | } 203 | 204 | /** 205 | * Support function for writing a bit to a register 206 | * 207 | * @param fd 208 | * card file descriptor 209 | * @param reg 210 | * register address 211 | * @param bit 212 | * bit offset 213 | * @param value 214 | * bit value 215 | * @return 216 | * error code 217 | */ 218 | int write_bit(u_int fd, u_int reg, u_int bit, u_int value) { 219 | return write_bitmask(fd, reg, 1 << bit, value << bit); 220 | } 221 | 222 | int eeprom_exists(int fd) { 223 | u_int reg; 224 | 225 | // is ROM present? 226 | RETURN_ON_ERR( 227 | read_bit(fd, EXT_ROM_CTRL_STATUS, ROM_EXISTS, ®) < 0, 228 | "ERROR: PCI CFG read of EXT_ROM_CTRL_STATUS register failed\n" 229 | ); 230 | 231 | return reg == 0 ? -1 : 0; 232 | } 233 | 234 | int external_rom_access(int fd, bool enable) { 235 | u_int reg, ix; 236 | 237 | RETURN_ON_ERR(eeprom_exists(fd) < 0, "ERROR: ROM doesnt exist\n"); 238 | 239 | if (enable) { 240 | RETURN_ON_ERR( 241 | pci_cfg_write32(fd, EXT_DATA0, 0x53524F4D) < 0, 242 | "ERROR: PCI CFG write of EXT_ROM_DATA0 register failed\n" 243 | ); 244 | 245 | usleep(DELAY_US); 246 | 247 | RETURN_ON_ERR( 248 | write_bit(fd, EXT_ROM_CTRL_STATUS, ROM_ACCESS_ENABLE, 1) < 0, 249 | "ERROR: PCI CFG write to enable ROM access failed\n" 250 | ); 251 | 252 | for (ix = 0; ix < LOOPNB; ix++) { 253 | usleep(POLL_US); 254 | 255 | pci_cfg_read16(fd, EXT_ROM_CTRL_STATUS, ®); 256 | 257 | if ((reg & RESULT_BITMASK) == RESULT_INVALID) { 258 | return 0; 259 | } 260 | } 261 | 262 | printf("cant enable ext rom access\n"); 263 | 264 | return -1; 265 | } else { 266 | RETURN_ON_ERR( 267 | write_bit(fd, EXT_ROM_CTRL_STATUS, ROM_ACCESS_ENABLE, 0) < 0, 268 | "ERROR: PCI CFG write to disable ROM access failed\n" 269 | ); 270 | 271 | return 0; 272 | } 273 | } 274 | 275 | int read_eeprom(int fd, char *filename, unsigned int len) { 276 | int ofile; 277 | u_int ix, data01, jx, val32, status; 278 | 279 | ofile = open(filename, O_CREAT | O_RDWR | O_TRUNC, 0644); 280 | 281 | RETURN_ON_ERR(ofile < 0, "ERROR: cant open file %s\n", filename); 282 | 283 | // enable access 284 | RETURN_ON_ERR( 285 | external_rom_access(fd, true) < 0, 286 | "ERROR: cant enable access to ROM \n" 287 | ); 288 | 289 | sleep(2); 290 | 291 | RETURN_ON_ERR( 292 | write_bit(fd, EXT_ROM_CTRL_STATUS, ROM_GET_DATA0, 1) < 0, 293 | "ERROR: cant set GET_DATA0\n" 294 | ); 295 | RETURN_ON_ERR( 296 | write_bit(fd, EXT_ROM_CTRL_STATUS, ROM_GET_DATA1, 1) < 0, 297 | "ERROR: cant set GET_DATA1\n" 298 | ); 299 | 300 | sleep(2); 301 | 302 | for (ix = 0; ix < len / 8; ix++) { 303 | for (data01 = 0; data01 < 2; data01++) { 304 | u_int databit = ROM_GET_DATA0 + data01; 305 | 306 | for (jx = 0; jx < LOOPNB; jx++) { 307 | usleep(POLL_US); 308 | 309 | if ((read_bit(fd, EXT_ROM_CTRL_STATUS, databit, &status) >= 0) 310 | && (status == 0)) { 311 | break; 312 | } 313 | } 314 | 315 | RETURN_ON_ERR(jx == LOOPNB, "ERROR: GET_DATAx never go to zero\n"); 316 | 317 | usleep(POLL_US); 318 | 319 | //read eeprom 320 | RETURN_ON_ERR( 321 | pci_cfg_read32(fd, DATAREG(databit), &val32) < 0, 322 | "ERROR: PCI CFG read of EXT_ROM_DATAx register failed\n" 323 | ); 324 | 325 | write(ofile, &val32, 4); 326 | 327 | RETURN_ON_ERR( 328 | write_bit(fd, EXT_ROM_CTRL_STATUS, databit, 1) < 0, 329 | "ERROR: cant set GET_DATAx\n" 330 | ); 331 | 332 | } 333 | } 334 | 335 | RETURN_ON_ERR( 336 | external_rom_access(fd, false) < 0, 337 | "ERROR: cant DISABLE access to ROM\n" 338 | ); 339 | 340 | return 0; //Success! 341 | 342 | } 343 | 344 | int do_upload(int fd, int ifile, u_int ctrl_reg) { 345 | u_int status, jx, val32, rc; 346 | 347 | rc = 1; // non-zero start value 348 | 349 | for (u_int i = 0; rc > 0; i++) { 350 | // lsb selects wheter upper or lower data register is used 351 | const u_int data01 = i & 1; 352 | // bit index for "Set DATAx" bit in Contol and Status Register 353 | const u_int databit = ROM_SET_DATA0 + data01; 354 | 355 | // read next dword 356 | rc = read(ifile, &val32, 4); 357 | 358 | if (rc == 0) { 359 | break; // done, no more data to write 360 | } 361 | RETURN_ON_ERR(rc < 0, "ERROR: Can't read image file\n"); 362 | RETURN_ON_ERR(rc != 4, "ERROR: Could not get 4 bytes. Only got %x\n", rc); 363 | 364 | // wait for Set DATAx to become zero 365 | for (jx = 0; jx < LOOPNB; jx++) { 366 | usleep(POLL_US); 367 | 368 | if ((read_bit(fd, ctrl_reg, databit, &status) >= 0) 369 | && (status == 0)) { 370 | break; 371 | } 372 | } 373 | 374 | RETURN_ON_ERR(jx == LOOPNB, "ERROR: SET_DATAx never go to zero\n"); 375 | 376 | // write dword to DATAx register 377 | RETURN_ON_ERR( 378 | pci_cfg_write32(fd, DATAREG(data01), val32) < 0, 379 | "ERROR: Cant write DATAx register\n" 380 | ); 381 | 382 | usleep(POLL_US); 383 | 384 | // trigger write 385 | // datasheet says, first two bytes should be uploaded together 386 | // before switching to an alternating upload order 387 | if (i == 1) { 388 | const u_int mask = (1 << ROM_SET_DATA0) | (1 << ROM_SET_DATA1); 389 | 390 | RETURN_ON_ERR( 391 | write_bitmask(fd, ctrl_reg, mask, mask) < 0, 392 | "ERROR: can't set SET_DATA01\n" 393 | ); 394 | } else if (i > 1) { 395 | RETURN_ON_ERR( 396 | write_bit(fd, ctrl_reg, databit, 1) < 0, 397 | "ERROR: can't set SET_DATAx\n" 398 | ); 399 | } 400 | } 401 | 402 | return 0; //Success! 403 | } 404 | 405 | int test_upload_result(int fd, u_int ctrl_reg) { 406 | u_int jx, status; 407 | 408 | // test result code 409 | for (jx = 0; jx < LOOPNB; jx++) { 410 | usleep(POLL_US); 411 | 412 | if (pci_cfg_read32(fd, ctrl_reg, &status) < 0) { 413 | status = -1; 414 | 415 | continue; // read might fail during update 416 | } 417 | 418 | if ((status & RESULT_BITMASK) == RESULT_SUCCESS) { 419 | break; 420 | } 421 | } 422 | 423 | RETURN_ON_ERR( 424 | (status & RESULT_BITMASK) != RESULT_SUCCESS, 425 | "ERROR: Writing firmware did not succeed, status register value: %x", 426 | status 427 | ); 428 | 429 | return 0; 430 | } 431 | 432 | int write_eeprom(int fd, char *filename, unsigned int len) { 433 | 434 | int ifile; 435 | 436 | ifile = open(filename, O_RDONLY); 437 | RETURN_ON_ERR(ifile < 0, "ERROR: cant open file image %s\n", filename); 438 | 439 | printf("STATUS: enabling EEPROM write\n"); 440 | 441 | // enable access 442 | RETURN_ON_ERR( 443 | external_rom_access(fd, true) < 0, 444 | "ERROR: cant enable access to ROM \n" 445 | ); 446 | 447 | sleep(1); 448 | 449 | printf("STATUS: performing EEPROM write\n"); 450 | // perform upload 451 | if (do_upload(fd, ifile, EXT_ROM_CTRL_STATUS) < 0) { 452 | return -1; 453 | } 454 | 455 | sleep(1); 456 | 457 | printf("STATUS: finishing EEPROM write\n"); 458 | // disable access 459 | RETURN_ON_ERR( 460 | external_rom_access(fd, false) < 0, 461 | "ERROR: cant DISABLE access to ROM\n" 462 | ); 463 | 464 | sleep(1); 465 | 466 | printf("STATUS: confirming EEPROM write\n"); 467 | 468 | // test result code 469 | if (test_upload_result(fd, EXT_ROM_CTRL_STATUS) < 0) { 470 | return -1; 471 | } 472 | 473 | return 0; //Success! 474 | } 475 | 476 | int write_firmware(int fd, char *filename, unsigned int len) { 477 | int ifile; 478 | u_int testVal; 479 | 480 | ifile = open(filename, O_RDONLY); 481 | RETURN_ON_ERR(ifile < 0, "ERROR: cant open file image %s\n", filename); 482 | 483 | // test if firmware download is locked 484 | RETURN_ON_REG_ERR( 485 | read_bit(fd, EXT_FW_DLOAD_CTRL_STATUS, FW_DLOAD_LOCK, &testVal) < 0 486 | ); 487 | if (testVal != 0) { 488 | printf("ERROR: firmware download lock is engaged. " 489 | "firmware download is not possible until chipset gets a power-on-reset signal. " 490 | "try rebooting or even unplugging the powe supply from your computer.\n"); 491 | return -1; 492 | } 493 | 494 | printf("STATUS: enabling firmware upload\n"); 495 | 496 | // enable access 497 | RETURN_ON_REG_ERR( 498 | write_bit(fd, EXT_FW_DLOAD_CTRL_STATUS, FW_DLOAD_ENABLE, 1) < 0 499 | ); 500 | RETURN_ON_REG_ERR( 501 | read_bit(fd, EXT_FW_DLOAD_CTRL_STATUS, FW_DLOAD_ENABLE, &testVal) < 0 502 | ); 503 | if (testVal != 1) { 504 | printf("ERROR: failed to enable firmware upload\n"); 505 | return -1; 506 | } 507 | 508 | sleep(1); 509 | 510 | printf("STATUS: performing firmware upload\n"); 511 | // perform upload 512 | if (do_upload(fd, ifile, EXT_FW_DLOAD_CTRL_STATUS) < 0) { 513 | return -1; 514 | } 515 | 516 | sleep(1); 517 | 518 | printf("STATUS: finishing firmware upload\n"); 519 | 520 | // disable access 521 | RETURN_ON_REG_ERR( 522 | write_bit(fd, EXT_FW_DLOAD_CTRL_STATUS, FW_DLOAD_ENABLE, 0) < 0 523 | ); 524 | RETURN_ON_REG_ERR( 525 | read_bit(fd, EXT_FW_DLOAD_CTRL_STATUS, FW_DLOAD_ENABLE, &testVal) < 0 526 | ); 527 | if (testVal != 0) { 528 | printf("ERROR: failed to finish and disable firmware upload \n"); 529 | return -1; 530 | } 531 | 532 | sleep(1); 533 | 534 | printf("STATUS: confirming firmware upload\n"); 535 | 536 | // test result code 537 | if (test_upload_result(fd, EXT_FW_DLOAD_CTRL_STATUS) < 0) { 538 | return -1; 539 | } 540 | 541 | return 0; //Success! 542 | } 543 | 544 | void usage() { 545 | 546 | printf("upd72020x-load: version 0.1\n"); 547 | printf("usage: upd72020 -r -b bus -d dev -f fct -s -o outfile : read eeprom to file (size default is 0x10000 or 64KB)\n"); 548 | //printf("usage: upd7202 -c -b -d -f -s -i outfile : check eeprom against file\n"); 549 | printf("usage: upd72020 -w -b bus -d dev -f fct -i infile : write file to eeprom\n"); 550 | printf("usage: upd72020 -u -b bus -d dev -f fct -i infile : upload file to firmware memory\n"); 551 | } 552 | 553 | int main(int argc, char **argv) { 554 | unsigned char pcidevid_x1[] = { 0x12, 0x19, 0x14, 0x00 }; //uPD720201 vendor id = 1912 devid = 0014 555 | unsigned char pcidevid_x2[] = { 0x12, 0x19, 0x15, 0x00 }; //uPD720202 vendor id = 1912 devid = 0015 556 | unsigned char buf[100]; 557 | unsigned int len; 558 | 559 | int i, fd; 560 | 561 | bool is_x1 = true, is_x2 = true; 562 | uint32_t bus, dev, fct; 563 | uint32_t size = 0x10000; 564 | uint32_t rflag = 0; 565 | uint32_t wflag = 0; 566 | uint32_t uflag = 0; 567 | uint32_t bflag, dflag, fflag, sflag, fileflag = 0; 568 | char *filename = NULL; 569 | char pcicfgfile[100]; 570 | int c; 571 | opterr = 0; 572 | 573 | if (argc < 10) { 574 | usage(); 575 | exit(1); 576 | } 577 | 578 | while ((c = getopt(argc, argv, "rwub:d:f:o:i:l:s:")) != -1) { 579 | switch (c) { 580 | case 'r': 581 | printf("Doing the reading\n"); 582 | rflag = 1; 583 | break; 584 | case 'w': 585 | printf("Doing the writing\n"); 586 | wflag = 1; 587 | break; 588 | case 'u': 589 | printf("Doing the upload\n"); 590 | uflag = 1; 591 | break; 592 | case 'b': 593 | bflag = 1; 594 | bus = strtoul(optarg, NULL, 16); //hex numbers for size!!! 595 | break; 596 | case 'd': 597 | dflag = 1; 598 | dev = strtol(optarg, NULL, 16); //hex numbers for size!!! 599 | break; 600 | case 'f': 601 | fflag = 1; 602 | fct = strtol(optarg, NULL, 16); //hex numbers for size!!! 603 | break; 604 | case 's': 605 | sflag = 1; 606 | size = strtol(optarg, NULL, 16); //hex numbers for size!!! 607 | break; 608 | case 'o': 609 | fileflag = 1; 610 | filename = optarg; //hex numbers for size!!! 611 | break; 612 | case 'i': 613 | fileflag = 1; 614 | filename = optarg; //hex numbers for size!!! 615 | break; 616 | 617 | default: 618 | break; 619 | } 620 | } 621 | 622 | printf("bus = %x \n", bus); 623 | printf("dev = %x \n", dev); 624 | printf("fct = %x \n", fct); 625 | printf("fname = %s \n", filename); 626 | 627 | sprintf(pcicfgfile, "/sys/bus/pci/devices/0000:%02x:%02x.%01x/config", 628 | bus, dev, fct); 629 | 630 | fd = open(pcicfgfile, O_RDWR); 631 | if (fd < 0) { 632 | printf("ERROR: cant open PCI CONFIGURATION file %s\n", pcicfgfile); 633 | printf("FAILED\n"); 634 | exit(1); 635 | } 636 | 637 | // make sure the device is the right one. 638 | len = 4; 639 | read(fd, buf, len); 640 | 641 | for (i = 0; i < 4; i++) { 642 | if (pcidevid_x1[i] != buf[i]) { 643 | is_x1 = false; 644 | } 645 | if (pcidevid_x2[i] != buf[i]) { 646 | is_x2 = false; 647 | } 648 | } 649 | 650 | if (is_x1) { 651 | printf("Found an UPD720201 chipset\n"); 652 | } else if (is_x2) { 653 | printf("Found an UPD720202 chipset\n"); 654 | } else { 655 | printf("ERROR: wrong vendorid/devid. Expected an UPD720201 or UPD720202 chip and this is not one!\n"); 656 | printf(" reported vendorid/devid: %.2x%.2x:%.2x%.2x \n", buf[1], buf[0], buf[3], buf[2]); 657 | printf(FAILED); 658 | exit(1); 659 | } 660 | 661 | u_int fw_info, rom_info, rom_config; 662 | 663 | if (pci_cfg_read32(fd, EXT_FW_VERSION, &fw_info) < 0 664 | || pci_cfg_read32(fd, EXT_ROM_INFO_REG, &rom_info) < 0 665 | || pci_cfg_read32(fd, EXT_ROM_CONFIG_REG, &rom_config) < 0) { 666 | printf("ERROR: unable to read configuration registers\n"); 667 | exit(1); 668 | } 669 | 670 | printf("got firmware version: %x\n", fw_info); 671 | 672 | if (eeprom_exists(fd) < 0) { 673 | printf("no EEPROM installed\n"); 674 | 675 | if ((rflag | wflag) > 0) { 676 | printf("ERROR: can not perform action\n"); 677 | exit(1); 678 | } 679 | } else { 680 | printf("EEPROM installed\n"); 681 | printf("got rom_info: %x\n", rom_info); 682 | printf("got rom_config: %x\n", rom_config); 683 | 684 | rom_config = lookup_rompar(rom_info); 685 | 686 | if (rom_config != ROM_PARAM_INVALID) { 687 | printf("setting rom_config: %x\n", rom_config); 688 | 689 | if (pci_cfg_write32(fd, EXT_ROM_CONFIG_REG, rom_config) < 0) { 690 | printf("ERROR: failed to set ROM parameter register\n"); 691 | exit(1); 692 | } 693 | } else { 694 | printf("unknown EEPROM, no parameters found\n"); 695 | 696 | if ((rflag | wflag) > 0) { 697 | printf("ERROR: can not perform action\n"); 698 | exit(1); 699 | } 700 | } 701 | } 702 | 703 | 704 | if (rflag == 1) { 705 | if (read_eeprom(fd, filename, size)) { 706 | printf(FAILED); 707 | exit(1); 708 | } else { 709 | printf(PASSED); 710 | exit(0); 711 | } 712 | 713 | } 714 | if (wflag == 1) { 715 | if (write_eeprom(fd, filename, size)) { 716 | printf(FAILED); 717 | exit(1); 718 | } else { 719 | printf(PASSED); 720 | exit(0); 721 | } 722 | } 723 | if (uflag == 1) { 724 | if (write_firmware(fd, filename, size)) { 725 | printf(FAILED); 726 | exit(1); 727 | } else { 728 | printf(PASSED); 729 | exit(0); 730 | } 731 | } 732 | 733 | printf("ERROR: Please specify an action. See help\n"); 734 | } 735 | --------------------------------------------------------------------------------