flashrom: Add board specific flash enables based on matching pci-ids. * Matches mainboards with up to two full sets of pci-ids, and if matched calls the respective enable function. * Falls back to name based matching/enable. * Cleans up VT823x enable code. Moves GPIO15 enable code to EPIA-M board specific code as GPIO15 doubles up as backlight enable in at least one laptop. * Adds support for Asus A7V8X-MX/A7V400-MX, a VIA KM400/VT8235 mainboard. signed-off-by: Luc Verhaegen Index: util/flashrom/flash_enable.c =================================================================== --- util/flashrom/flash_enable.c (revision 2573) +++ util/flashrom/flash_enable.c (working copy) @@ -158,10 +158,13 @@ return enable_flash_ich(dev, name, 0xdc); } -static int enable_flash_vt823x(struct pci_dev *dev, char *name) +/* + * + */ +static int +enable_flash_vt823x(struct pci_dev *dev, char *name) { uint8_t val; - int ret = 0; /* ROM Write enable */ val = pci_read_byte(dev, 0x40); @@ -170,37 +173,10 @@ if (pci_read_byte(dev, 0x40) != val) { printf("Warning: Failed to enable ROM Write on %s\n", name); - ret = -1; + return -1; } - if (dev->device_id == 0x3177) { /* VT8235 */ - if (!iopl(3)) { /* enable full IO access */ - unsigned int base; - - /* GPIO12-15 -> output */ - val = pci_read_byte(dev, 0xE4); - val |= 0x38; - pci_write_byte(dev, 0xE4, val); - - /* Get Power Management IO address. */ - base = pci_read_word(dev, 0x88) & 0xFF80; - - /* enable GPIO15 which is connected to write protect. */ - val = inb(base + 0x4d); - val |= 0xFF; - outb(val, base + 0x4d); - - val = inb(base + 0x4E); - val |= 0x0F; - outb(val, base + 0x4E); - } else { - printf("Warning; Failed to disable Write Protect" - " on %s (iopl failed)\n", name); - return -1; - } - } - - return ret; + return 0; } static int enable_flash_cs5530(struct pci_dev *dev, char *name) @@ -531,9 +507,194 @@ { "ISLAND", "ARUMA", mbenable_island_aruma }, }; +/* + * + * Board specific handling, matched by up to two full sets of pci-ids. + * + */ + +/* + * Suited for VIAs EPIA M and MII, and maybe other CLE266 based EPIAs. + * + * First pci_dev is used and a VT8235 ISA Bridge (0x1106:0x3177) + * Second pci_dev is VIA CLE266 Host Bridge and not used. + * + * This line doesn't really need to be raised when using linuxbios, + * but it doesn't hurt. + */ +static int +enable_flash_via_epia_m(struct pci_dev *first, struct pci_dev *second, + char *name) +{ + unsigned int base; + uint8_t val; + + /* GPIO12-15 -> output */ + val = pci_read_byte(first, 0xE4); + val |= 0x10; + pci_write_byte(first, 0xE4, val); + + /* Get Power Management IO address. */ + base = pci_read_word(first, 0x88) & 0xFF80; + + /* enable GPIO15 which is connected to write protect. */ + val = inb(base + 0x4D); + val |= 0x80; + outb(val, base + 0x4D); + + return 0; +} + +/* + * Winbond LPC super IO. + * + * Raises the ROM MEMW# line. + */ +static void +w83697_rom_memw_enable(void) +{ + uint8_t val; + + outb(0x87, 0x2E); /* enable extended functions */ + outb(0x87, 0x2E); + + outb(0x24, 0x2E); /* rom bits live here */ + + val = inb(0x2F); + if (!(val & 0x02)) /* flash rom enabled? */ + outb(val | 0x08, 0x2F); /* enable MEMW# */ + + outb(0xAA, 0x2E); /* disable extended functions */ +} + +/* + * Suited for Asus A7V8X-MX SE and A7V400-MX. + * + * First pci_dev is used and a VT8235 ISA Bridge (0x1106:0x3177) + * Second pci_dev is VIA KM400 Host Bridge and not used. + */ +static int +enable_flash_asus_a7v8x_mx(struct pci_dev *first, struct pci_dev *second, + char *name) +{ + uint8_t val; + + /* This bit is marked reserved actually */ + val = pci_read_byte(first, 0x59); + val &= 0x7F; + pci_write_byte(first, 0x59, val); + + w83697_rom_memw_enable(); + + return 0; +} + +/* + * We use 2 sets of ids here, you're free to choose which is which. This to + * provide a very high degree of certainty when matching a board. Not every + * vendor handles subsystem/card ids in a sane manner. + * + * Keep the second set nulled if it should be ignored. + * + * This does require that linuxbios sets up the subsystem ids properly. + * + */ +struct board_pciid_enable { + /* any device, but make it sensible, like the isa bridge*/ + uint16_t first_vendor; + uint16_t first_device; + uint16_t first_card_vendor; + uint16_t first_card_device; + + /* any device, but make it sensible, like the host bridge */ + uint16_t second_vendor; + uint16_t second_device; + uint16_t second_card_vendor; + uint16_t second_card_device; + + char *name; + int (*enable)(struct pci_dev *first, struct pci_dev *second, char *name); +}; + +struct board_pciid_enable board_pciid_enables[] = { + { 0x1106, 0x3177, 0x1106, 0xAA01, 0x1106, 0x3123, 0x1106, 0xAA01, "VIA EPIA M/MII/...", enable_flash_via_epia_m }, + { 0x1106, 0x3177, 0x1043, 0x80A1, 0x1106, 0x3205, 0x1043, 0x8118, "Asus A7V8-MX SE", enable_flash_asus_a7v8x_mx }, + { 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL } /* Keep this */ + +}; + +/* + * + */ +static struct pci_dev * +device_pciids_match(uint16_t vendor, uint16_t device, + uint16_t card_vendor, uint16_t card_device) +{ + struct pci_dev *temp; + struct pci_filter filter; + + pci_filter_init(NULL, &filter); + filter.vendor = vendor; + filter.device = device; + + for (temp = pacc->devices; temp; temp = temp->next) + if (pci_filter_match(&filter, temp)) { + if ((card_vendor == pci_read_word(temp, 0x2C)) && + (card_device == pci_read_word(temp, 0x2E))) + return temp; + } + + return NULL; +} + +/* + * + */ +static int +board_flash_enable_from_pciids(void) +{ + struct board_pciid_enable *board = board_pciid_enables; + struct pci_dev *first, *second; + int ret; + + for (; board->name; board++) { + first = device_pciids_match(board->first_vendor, + board->first_device, + board->first_card_vendor, + board->first_card_device); + if (!first) + continue; + + if (board->second_vendor) { + second = device_pciids_match(board->second_vendor, + board->second_device, + board->second_card_vendor, + board->second_card_device); + if (!second) + continue; + } else + second = NULL; + + + printf("Found \"%s\": Enabling flash write...", + board->name); + + ret = board->enable(first, second, board->name); + if (!ret) { + printf(" OK.\n"); + return 1; /* Found and success */ + } else { + printf(" Failed!\n"); + return ret; /* error */ + } + } + + return 0; /* nothing found */ +} + int enable_flash_write() { - int i; + int i, ret; struct pci_dev *dev = 0; FLASH_ENABLE *enable = 0; @@ -558,13 +719,19 @@ /* First look whether we have to do something for this * motherboard. */ - for (i = 0; i < sizeof(mbenables) / sizeof(mbenables[0]); i++) { - if(lb_vendor && !strcmp(mbenables[i].vendor, lb_vendor) && - lb_part && !strcmp(mbenables[i].part, lb_part)) { - mbenables[i].doit(); - break; - } - } + ret = board_flash_enable_from_pciids(); + if (ret < 0) + printf("Attempting to continue regardless.\n"); + + if (ret != 1) { /* Try to match on name instead. */ + for (i = 0; i < sizeof(mbenables) / sizeof(mbenables[0]); i++) { + if(lb_vendor && !strcmp(mbenables[i].vendor, lb_vendor) && + lb_part && !strcmp(mbenables[i].part, lb_part)) { + mbenables[i].doit(); + break; + } + } + } /* now let's try to find the chipset we have ... */ for (i = 0; i < sizeof(enables) / sizeof(enables[0]) && (!dev);