Reading the firmware ROM from a Renesas uPD720202 USB 3.0 Host Controller using Linux

This post was written by eli on November 3, 2015
Posted Under: Linux,USB

Pretty much as a side note, I should mention that the firmware should and can be loaded with a Windows utility named K2024FWUP1.exe. Get it from whereever you can, and verify it isn’t dirty with

$ shasum K2024FWUP1.exe
c9414cb825af79f5d87bd9772e10e87633fbf125  K2024FWUP1.exe

If this isn’t done, Window’s Device Manager will say that the device can’t be started, and Linux kernel will complain with

pci 0000:06:00.0: xHCI HW not ready after 5 sec (HC bug?) status = 0x1801

[...]

xhci_hcd 0000:06:00.0: can't setup: -110
xhci_hcd 0000:06:00.0: USB bus 3 deregistered
xhci_hcd 0000:06:00.0: init 0000:06:00.0 fail, -110
xhci_hcd: probe of 0000:06:00.0 failed with error -110

Now to the Linux part. This is just the series of commands I used to read from the firmware ROM of a Renesas USB controller detected as:

# lspci -s 06:00
06:00.0 USB controller: Renesas Technology Corp. uPD720202 USB 3.0 Host Controller (rev 02)

The point was to check if the ROM was erased (it was). I followed the instructions in the “μPD720201/μPD720202 User’s Manual: Hardware” (R19UH0078EJ0600, Rev.6.00), section 6.

Check if ROM exists:

# setpci -s 06:00.0 f6.w
8000

Bit 15=1, so yes, ROM exists. Check type and parameter:

# setpci -s 06:00.0 ec.l
00c22210
# setpci -s 06:00.0 f0.l
00000500

OK, according to table 6-1 of the Hardware User Manual, it’s a MX25L5121E.

Write magic word to DATA0:

# setpci -s 06:00.0 f8.l=53524F4D

Set “External ROM Access Enable”:

# setpci -s 06:00.0 f6.w=8001

Check “Result Code”:

# setpci -s 06:00.0 f6.w
8001

Indeed, bits 6:4 are zero — no result yet, as required for this stage in the Guide.

Now set Get DATA0 and Get DATA1, and check that they have been cleared:

# setpci -s 06:00.0 f6.w=8c01
# setpci -s 06:00.0 f6.w
8001

Get first piece of data from DATA0:

# setpci -s 06:00.0 f8.l
ffffffff

The ROM appears to be erased… Set Get DATA0 again, and read DATA1 (this is really what the Guide says)

# setpci -s 06:00.0 f6.w=8401
# setpci -s 06:00.0 fc.l
ffffffff

Yet another erased word. And now the other way around: Set Get DATA1 and read DATA0 again:

# setpci -s 06:00.0 f6.w=8801
# setpci -s 06:00.0 f8.l
ffffffff

And the other way around again…

# setpci -s 06:00.0 f6.w=8401
# setpci -s 06:00.0 fc.l
ffffffff

When done, clear “External ROM Access Enable”

# setpci -s 06:00.0 f6.w=8000

This rewinds the next set of operation to the beginning, of the ROM, as I’ve seen by trying it out, even though the Guide wasn’t so clear about it. So if the sequence shown above starts from the beginning, we read the beginning of the ROM again.

Again, with the ROM loaded with firmware

# setpci -s 06:00.0 f6.w
8000
# setpci -s 06:00.0 f8.l=53524F4D
# setpci -s 06:00.0 f6.w=8001
# setpci -s 06:00.0 f6.w
8001
# setpci -s 06:00.0 f6.w=8c01
# setpci -s 06:00.0 f6.w
8001
# setpci -s 06:00.0 f8.l
7da655aa
# setpci -s 06:00.0 f6.w=8401
# setpci -s 06:00.0 fc.l
00f60014
# setpci -s 06:00.0 f6.w=8801
# setpci -s 06:00.0 f8.l
004c010c
# setpci -s 06:00.0 f6.w=8401
# setpci -s 06:00.0 fc.l
2ffc015c
# setpci -s 06:00.0 f6.w=8801
# setpci -s 06:00.0 f8.l
0008315c
# setpci -s 06:00.0 f6.w=8401
# setpci -s 06:00.0 fc.l
1a5c2024
# setpci -s 06:00.0 f6.w=8000

I stopped after a few words, of course. Note that the first word is indeed the correct signature.

Reader Comments

i hated that chip. I got this work though. for what it is worth.
-jfs

usage: upd72020 -r -b -d -f -s -o outfile : read eeprom to file (size default is 0x10000 or 64KB)
usage: upd72020 -w -b -d -f -i iinfile : write file to eeprom

upd720202.c:

#include
#include
#include
#include
#include
#include
#include
#include

#define FAILED ” ======> FAILED”
#define PASSED ” ======> PASSED”

#define ROM_INFORMATION_NUMONYX_M25P40 0x00202013 //ROM information for our eeprom device. See table 6.1 in doc
#define ROM_PARAMETER_NUMONYX_M25P40 0x00000760 //ROM information for our eeprom device. See table 6.1 in doc

#define EXT_ROM_INFO_REG 0xEC
#define EXT_ROM_CONFIG_REG 0xF0
#define EXT_ROM_FW_DLOAD_CTRL_STATUS 0xF4
#define EXT_ROM_CTRL_STATUS 0xF6
#define EXT_ROM_DATA0 0xF8
#define EXT_ROM_DATA1 0xFC

#define GET_DATA0 0
#define GET_DATA1 1
#define GET_DATA0_OFF 10
#define GET_DATA1_OFF 11
#define SET_DATA0 0
#define SET_DATA1 1
#define SET_DATA0_OFF 8
#define SET_DATA1_OFF 9
#define GET 0
#define SET 1
#define DISABLE 0
#define ENABLE 1
#define LOOPNB 100000

int pci_cfg_read32( int fd, u_int off){
u_int val32;
lseek(fd, off, SEEK_SET);
read(fd, &val32, 4);
return val32;
}
int pci_cfg_write32( int fd, u_int off, u_int val32){
lseek(fd, off, SEEK_SET);
return write(fd, &val32, 4);
}

int pci_cfg_read16( int fd, u_int off){
u_int val16;
lseek(fd, off, SEEK_SET);
read(fd, &val16, 2);
return val16;
}
int pci_cfg_write16( int fd, u_int off, u_int val16){
lseek(fd, off, SEEK_SET);
return write(fd, &val16, 2);
}

int set_data0_1( u_int fd, u_int datareg, u_int action){

u_int reg;
reg = pci_cfg_read16 (fd, EXT_ROM_CTRL_STATUS );
if (datareg == SET_DATA0){
if (action == SET){
reg = reg | (0x1 << SET_DATA0_OFF);
return pci_cfg_write16(fd, EXT_ROM_CTRL_STATUS, reg );
}else{
reg = pci_cfg_read16(fd, EXT_ROM_CTRL_STATUS );
reg &= (0x1 << SET_DATA0_OFF);
return reg ;
}
}else{
if (action == SET){
reg = reg | (0x1 << SET_DATA1_OFF);
return pci_cfg_write16(fd, EXT_ROM_CTRL_STATUS, reg );
}else{
reg = pci_cfg_read16(fd, EXT_ROM_CTRL_STATUS );
reg &=(0x1 << SET_DATA1_OFF);
return reg ;
}
}
}
int get_data0_1( u_int fd, u_int datareg, u_int action){

u_int reg=0;
if (datareg == GET_DATA0){
if (action == SET){
reg = pci_cfg_read16 (fd, EXT_ROM_CTRL_STATUS );
reg = reg | (0x1 << GET_DATA0_OFF);
pci_cfg_write16(fd, EXT_ROM_CTRL_STATUS, reg );
return 0;
}else{
reg = pci_cfg_read16(fd, EXT_ROM_CTRL_STATUS );
reg &= (0x1 << GET_DATA0_OFF);
return reg ;
}
}else{
if (action == SET){
reg = pci_cfg_read16 (fd, EXT_ROM_CTRL_STATUS );
reg = reg | (0x1 << GET_DATA1_OFF);
pci_cfg_write16(fd, EXT_ROM_CTRL_STATUS, reg );
return 0;
}else{
reg = pci_cfg_read16(fd, EXT_ROM_CTRL_STATUS );
reg &=(0x1 << GET_DATA1_OFF);
return reg ;
}
}
}

int external_rom_access( int fd, int enable){
u_int reg, ix;

reg = pci_cfg_read16 (fd, EXT_ROM_CTRL_STATUS );
if (enable){
reg = reg | 0x1;
pci_cfg_write16(fd, EXT_ROM_CTRL_STATUS, reg );

for (ix<0;ix<LOOPNB;ix++){
reg = pci_cfg_read16(fd, EXT_ROM_CTRL_STATUS);
//printf ("reg %x\n", reg);
if ( (reg &0x70) ==0 )
return 0;
}
printf("cant enable ext rom access\n");
return -1;
}else{
//disable it
reg = reg & ~0x1;
pci_cfg_write16(fd, EXT_ROM_CTRL_STATUS, reg );
return 0;
}
}

int external_rom_erase( int fd){
u_int reg, ix;

reg = pci_cfg_read16 (fd, EXT_ROM_CTRL_STATUS );
reg = reg | 0x2;
pci_cfg_write16(fd, EXT_ROM_CTRL_STATUS, reg );

for (ix<0;ix<LOOPNB;ix++){
sleep(1);
reg = pci_cfg_read16(fd, EXT_ROM_CTRL_STATUS);
if ( (reg &0x02) ==0 )
return 0;
}
printf("ERROR: cant erase ext rom \n");
return -1;
}

int read_eeprom(int fd, unsigned char *filename, unsigned int len){

int ofile;
u_int reg, ix, jx, val32;

ofile = open(filename, O_CREAT | O_RDWR | O_TRUNC);
if (ofile <0){
printf("ERROR: cant open file %s\n", filename);
return 1;
}

//–is ROM present?
reg = pci_cfg_read16 (fd, EXT_ROM_CTRL_STATUS );
if (reg < 0){
printf("ERROR: PCI CFG read of EXT_ROM_CTRL_STATUS register failed\n");
return 1;
}

if (!(reg & 0x8000)){
printf("ERROR: ROM doesnt exist\n");
return 1;
}

//–write pattern
if (pci_cfg_write32(fd, EXT_ROM_DATA0, 0x53524F4D) <0){
printf("ERROR: PCI CFG write of EXT_ROM_DATA0 register failed\n");
return 1;
}

//–enable access
if (external_rom_access(fd, ENABLE) < 0){
printf("ERROR: cant enable access to ROM \n");
return 1;
}
sleep(2);

if (get_data0_1(fd, GET_DATA0, SET) <0){
printf("ERROR: cant set GET_DATA0\n");
return 1;
}
if (get_data0_1(fd, GET_DATA1, SET) <0){
printf("ERROR: cant set GET_DATA1\n");
return 1;
}
sleep(2);
//printf("before reg %x\n", pci_cfg_read16 (fd, EXT_ROM_CTRL_STATUS ));

for (ix=0;ix<len/8;ix++){

for (jx=0;jx<LOOPNB;jx++){
if (get_data0_1(fd, GET_DATA0, GET) == 0)
break;
}
if (jx == LOOPNB){
printf("ERROR: GET_DATA0 never go to zero\n");
return 1;
}
usleep(8000);
//printf("after reg %x\n", pci_cfg_read16 (fd, EXT_ROM_CTRL_STATUS ));

//read eeprom
val32 = pci_cfg_read32(fd, EXT_ROM_DATA0);
//printf ("val32=%x\n", val32);
if (val32 <0){
printf("ERROR: PCI CFG read of EXT_ROM_DATA0 register failed\n");
return 1;
}
usleep(4000);
if (get_data0_1(fd, GET_DATA0, SET) <0){
printf("ERROR: cant set GET_DATA0\n");
return 1;
}
write(ofile, &val32, 4);

usleep(8000);

for (jx<0;jx<LOOPNB;jx++){
usleep(1000);
if (get_data0_1(fd, GET_DATA1, GET) == 0)
break;
}
if (jx == LOOPNB){
printf("ERROR: GET_DATA1 not zero\n");
return 1;
}
usleep(4000);

//read eeprom
val32 = pci_cfg_read32(fd, EXT_ROM_DATA1);
//printf ("val32=%x\n", val32);
if (val32 <0){
printf("ERROR: PCI CFG read of EXT_ROM_DATA0 register failed\n");
return 1;
}

if (get_data0_1(fd, GET_DATA1, SET) <0){
printf("ERROR: cant set GET_DATA1\n");
return 1;
}
write(ofile, &val32, 4);

}
if (external_rom_access(fd, DISABLE) < 0){
printf("ERROR: cant DISABLE access to ROM\n");
return 1;
}

return 0; //Success!

}

int write_eeprom(int fd, unsigned char *filename, unsigned int len){

int ifile;
u_int reg, ix, jx, val32, rc;

ifile = open(filename, O_RDWR );
if (ifile <0){
printf("ERROR: cant open file image %s\n", filename);
return 1;
}

//–is ROM present?
reg = pci_cfg_read16 (fd, EXT_ROM_CTRL_STATUS );
if (reg < 0){
printf("ERROR: PCI CFG read of EXT_ROM_CTRL_STATUS register failed\n");
return 1;
}

if (!(reg & 0x8000)){
printf("ERROR: ROM doesnt exist\n");
return 1;
}

//–Doing the EEPROM erase: write pattern
printf("Erasing eeprom\n");
if (pci_cfg_write32(fd, EXT_ROM_DATA0, 0x5A65726F) <0){
printf("ERROR: PCI CFG write of EXT_ROM_DATA0 register failed\n");
return 1;
}
if (external_rom_erase(fd)<0)
return 1;

printf("…..erase done\n");

//–step2: write pattern
if (pci_cfg_write32(fd, EXT_ROM_DATA0, 0x53524F4D) <0){
printf("ERROR: PCI CFG write of EXT_ROM_DATA0 register failed\n");
return 1;
}

//–enable access
if (external_rom_access(fd, ENABLE) < 0){
printf("ERROR: cant enable access to ROM \n");
return 1;
}
sleep(1);
//– step5: Read “Set DATA0” and confirm it is ‘0b’.

for (jx=0;jx<LOOPNB;jx++){
if (set_data0_1(fd, SET_DATA0, GET) == 0)
break;
}
if (jx == LOOPNB){
printf("ERROR: SET_DATA0 not zero\n");
return 1;
}

//– step6: Write FW data to”DATA0”
rc = read(ifile, &val32, 4);
if ( rc <0){
printf("ERROR: Cant read image file %s \n", filename);
return 1;
}
if ( rc != 4){
printf("ERROR: Could not get 4 bytes. Only got %x\n", rc);
return 1;
}
//printf("fw val = %x\n", val32);
if (pci_cfg_write32(fd, EXT_ROM_DATA0, val32) <0){
printf("ERROR: Cant write eeprom at step6\n");
return 1;
}

//– step7: Read “Set DATA1” and confirm it is ‘0b’.

for (jx=0;jx<LOOPNB;jx++){
if (set_data0_1(fd, SET_DATA1, GET) == 0)
break;
}
if (jx == LOOPNB){
printf("ERROR: SET_DATA1 not zero\n");
return 1;
}

//– step8: Write FW data to”DATA1”
rc = read(ifile, &val32, 4);
if ( rc <0){
printf("ERROR: Cant read image file %s \n", filename);
return 1;
}
if ( rc != 4){
printf("ERROR: Could not get 4 bytes. Only got %x\n", rc);
return 1;
}

//printf("fw val = %x\n", val32);
if (pci_cfg_write32(fd, EXT_ROM_DATA1, val32) <0){
printf("ERROR: Cant write eeprom at step8\n");
return 1;
}

//– step9: Set “Set DATA0” and “Set DATA1” to ‘1b’.
if (set_data0_1(fd, SET_DATA0, SET) <0){
printf("ERROR: cant set SET_DATA0\n");
return 1;
}
if (set_data0_1(fd, SET_DATA1, SET) <0){
printf("ERROR: cant set SET_DATA1\n");
return 1;
}

//printf("write01\n");

//for (ix=0;ix<len/8;ix++){
while(1){ //do it til end of file image
//printf("writeloop\n");

//– step10: Read “Set DATA0” and confirm it is ‘0b’.

for (jx=0;jx<LOOPNB;jx++){
if (set_data0_1(fd, SET_DATA0, GET) == 0)
break;
}
if (jx == LOOPNB){
printf("ERROR: SET_DATA0 not zero\n");
return 1;
}

//usleep(8000);
//– step11: Write FW data to”DATA0”
rc = read(ifile, &val32, 4);
if ( rc <0){
printf("ERROR: Cant read image file %s \n", filename);
return 1;
}
if ( rc != 4){
printf("ERROR: Could not get 4 bytes. Only got %x\n", rc);
return 1;
}
//printf("fw val = %x\n", val32);

if (pci_cfg_write32(fd, EXT_ROM_DATA0, val32) <0){
printf("ERROR: Cant write eeprom at step11\n");
return 1;
}
usleep(8000);

//– step12: Set “Set DATA0” to ‘1b’.
if (set_data0_1(fd, SET_DATA0, SET) <0){
printf("ERROR: cant set SET_DATA0\n");
return 1;
}

usleep(8000);
//– step13: Read “Set DATA1” and confirm it is ‘0b’.
for (jx=0;jx<LOOPNB;jx++){
if (set_data0_1(fd, SET_DATA1, GET) == 0)
break;
}
if (jx == LOOPNB){
printf("ERROR: SET_DATA1 not zero\n");
return 1;
}

usleep(8000);
//– step14: Write FW data to”DATA1”
rc = read(ifile, &val32, 4);
if ( rc <0){
printf("ERROR: Cant read image file %s \n", filename);
return 1;
}
if ( rc != 4){
printf("ERROR: Could not get 4 bytes. Only got %x\n", rc);
return 1;
}
//printf("fw val = %x\n", val32);
if (pci_cfg_write32(fd, EXT_ROM_DATA1, val32) <0){
printf("ERROR: Cant write eeprom at step14\n");
return 1;
}

usleep(8000);

//– step15: Set “Set DATA1” to ‘1b’.
if (set_data0_1(fd, SET_DATA1, SET) <0){
printf("ERROR: cant set SET_DATA1\n");
return 1;
}
usleep(8000);
}
if (external_rom_access(fd, DISABLE) < 0){
printf("ERROR: cant DISABLE access to ROM\n");
return 1;
}

return 0; //Success!
}

void usage(){

printf("upd72020: version 1.0\n");
printf("usage: upd72020 -r -b -d -f -s -o outfile : read eeprom to file (size default is 0x10000 or 64KB)\n”);
//printf(“usage: upd7202 -c -b -d -f -s -i outfile : check eeprom against file\n”);
printf(“usage: upd72020 -w -b -d -f -i iinfile : write file to eeprom\n”);

}

int
main (int argc, char **argv)
{
//const uint32_t BAR_LENGTH = 0x2000;
//const uint32_t CTRL_REGS = 0x80000;
//const uint32_t CTRL_LEN = 0x30;

unsigned char pcidevid[] = {0x12, 0x19, 0x15, 0x00}; //vendor id = 1912 devid = 0015
unsigned char val;
unsigned char buf[100];
unsigned int len;

int i, fd;
uint32_t *ptr;

uint32_t bus, dev, fct, size;
uint32_t rflag=0;
uint32_t wflag=0;
uint32_t cflag, bflag, dflag, fflag, sflag, fileflag = 0;
char *filename = NULL;
char pcicfgfile[100];
int c;
opterr = 0;

//printf (“argc %d\n”, argc);

if (argc < 10){
usage();
exit(1);
}

// -b bus -d dev -f fct -f file -l len

while ((c = getopt (argc, argv, "rwb:d:f:o:i:l:s:")) != -1){
switch (c) {
case 'r':
printf("Doing the reading\n");
rflag =1;
break;
case 'c':
cflag =1;
break;
case 'w':
printf("Doing the writing\n");
wflag =1;
break;
case 'b':
bflag = 1;
printf("bus is %s\n", optarg);
bus = strtoul(optarg, NULL, 16); //hex numbers for size!!!
break;
case 'd':
dflag = 1;
dev = strtol(optarg, NULL, 16); //hex numbers for size!!!
break;
case 'f':
fflag = 1;
fct = strtol(optarg, NULL, 16); //hex numbers for size!!!
break;
case 's':
sflag = 1;
size = strtol(optarg, NULL, 16); //hex numbers for size!!!
break;
case 'o':
fileflag = 1;
filename = optarg; //hex numbers for size!!!
break;
case 'i':
fileflag = 1;
filename = optarg; //hex numbers for size!!!
break;

default:
break;
}

}
printf ("bus = %x \n", bus);
printf ("dev = %x \n", dev);
printf ("fct = %x \n", fct);
printf ("fname = %s \n", filename);

sprintf(pcicfgfile,"/sys/bus/pci/devices/0000:%02x:%02x.%01x/config", bus, dev, fct);
//printf("%s", pcicfgfile);

fd = open(pcicfgfile, O_RDWR);
if (fd <0){
printf("ERROR: cant open PCI CONFIGURATION file %s", pcicfgfile);
printf("FAILED");
exit(1);
}

//–make sure the device is the right one.
len =4;
read (fd, buf, len);

for (i=0;i<4;i++){
if (pcidevid[i] != buf[i]){
printf("ERROR: wrong vendorid/devid. We expect a UPD720202 chip and this is not one!");
printf(FAILED);
exit(1);
}
}

#if 0
#define ROM_INFORMATION_NUMONYX_M25P40 0x00202013 //ROM information for our eeprom device. See table 6.1 in doc
#define ROM_PARAMETER_NUMONYX_M25P40 0x00000760 //ROM information for our eeprom device. See table 6.1 in doc

#define EXT_ROM_INFO_REG 0xEC
#define EXT_ROM_CONFIG_REG 0xF0
#endif

pci_cfg_write32( fd, EXT_ROM_INFO_REG , ROM_INFORMATION_NUMONYX_M25P40);
pci_cfg_write32( fd, EXT_ROM_CONFIG_REG , ROM_PARAMETER_NUMONYX_M25P40);

//printf ("%x\n", pci_cfg_read16(fd, 0));
//printf ("%x\n", pci_cfg_read16(fd, 2));
//printf ("%x\n", pci_cfg_read32(fd, 0xec));
//pci_cfg_write32(fd, 0xf0, 0xdeadbeef);
//printf ("%x\n", pci_cfg_read32(fd, 0xf0));

if (rflag==1){
if (read_eeprom(fd, filename, size)){
printf(FAILED);
exit(1);
}else{
printf(PASSED);
exit(0);
}

}
if (wflag==1){
if (write_eeprom(fd, filename, size)){
printf(FAILED);
exit(1);
}else{
printf(PASSED);
exit(0);
}

}

printf("ERROR: Please specify an action. See help\n");

}

#1 
Written By jf simon on November 5th, 2015 @ 18:12

Thanks for that input. Too bad the indentation was lost (hope only that).

#2 
Written By eli on November 5th, 2015 @ 19:12

Unfortunately, more than just the indentation was lost. Have a .c file?

#3 
Written By Bushytails on January 28th, 2017 @ 00:23

Hi,

I have the following;
03:00.0 USB controller: Renesas Technology Corp. uPD720201 USB 3.0 Host Controller (rev 03)

When I run setpci -s 03:00.0 fw.6, I get 8000 as expected. However, when I run setpci -s 03:00.0 ec.1 I get the following error message.

setpci: Invalid width “1″.

My knowledge about eeprom etc is zilch so would appreciate if you can shed some light on what is going on with my card.

Many thanks

#5 
Written By gdes on September 3rd, 2020 @ 12:13

Add a Comment

required, use real name
required, will not be published
optional, your blog address