Add AMD Fam10 B3 default settings to match AMD example code.
[coreboot.git] / util / flashrom / board_enable.c
1 /*
2  * This file is part of the flashrom project.
3  *
4  * Copyright (C) 2005-2007 coresystems GmbH <stepan@coresystems.de>
5  * Copyright (C) 2006 Uwe Hermann <uwe@hermann-uwe.de>
6  * Copyright (C) 2007-2008 Luc Verhaegen <libv@skynet.be>
7  * Copyright (C) 2007 Carl-Daniel Hailfinger
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; version 2 of the License.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
21  */
22
23 /*
24  * Contains the board specific flash enables.
25  */
26
27 #include <stdio.h>
28 #include <pci/pci.h>
29 #include <stdint.h>
30 #include <string.h>
31 #include <fcntl.h>
32 #include "flash.h"
33
34 /*
35  * Helper functions for many Winbond Super I/Os of the W836xx range.
36  */
37 /* Enter extended functions */
38 static void w836xx_ext_enter(uint16_t port)
39 {
40         OUTB(0x87, port);
41         OUTB(0x87, port);
42 }
43
44 /* Leave extended functions */
45 static void w836xx_ext_leave(uint16_t port)
46 {
47         OUTB(0xAA, port);
48 }
49
50 /* General functions for reading/writing Winbond Super I/Os. */
51 static unsigned char wbsio_read(uint16_t index, uint8_t reg)
52 {
53         OUTB(reg, index);
54         return INB(index + 1);
55 }
56
57 static void wbsio_write(uint16_t index, uint8_t reg, uint8_t data)
58 {
59         OUTB(reg, index);
60         OUTB(data, index + 1);
61 }
62
63 static void wbsio_mask(uint16_t index, uint8_t reg, uint8_t data, uint8_t mask)
64 {
65         uint8_t tmp;
66
67         OUTB(reg, index);
68         tmp = INB(index + 1) & ~mask;
69         OUTB(tmp | (data & mask), index + 1);
70 }
71
72 /**
73  * Winbond W83627HF: Raise GPIO24.
74  *
75  * Suited for:
76  *  - Agami Aruma
77  *  - IWILL DK8-HTX
78  */
79 static int w83627hf_gpio24_raise(uint16_t index, const char *name)
80 {
81         w836xx_ext_enter(index);
82
83         /* Is this the W83627HF? */
84         if (wbsio_read(index, 0x20) != 0x52) {  /* Super I/O device ID reg. */
85                 fprintf(stderr, "\nERROR: %s: W83627HF: Wrong ID: 0x%02X.\n",
86                         name, wbsio_read(index, 0x20));
87                 w836xx_ext_leave(index);
88                 return -1;
89         }
90
91         /* PIN89S: WDTO/GP24 multiplex -> GPIO24 */
92         wbsio_mask(index, 0x2B, 0x10, 0x10);
93
94         /* Select logical device 8: GPIO port 2 */
95         wbsio_write(index, 0x07, 0x08);
96
97         wbsio_mask(index, 0x30, 0x01, 0x01);    /* Activate logical device. */
98         wbsio_mask(index, 0xF0, 0x00, 0x10);    /* GPIO24 -> output */
99         wbsio_mask(index, 0xF2, 0x00, 0x10);    /* Clear GPIO24 inversion */
100         wbsio_mask(index, 0xF1, 0x10, 0x10);    /* Raise GPIO24 */
101
102         w836xx_ext_leave(index);
103
104         return 0;
105 }
106
107 static int w83627hf_gpio24_raise_2e(const char *name)
108 {
109         /* TODO: Typo? Shouldn't this be 0x2e? */
110         return w83627hf_gpio24_raise(0x2d, name);
111 }
112
113 /**
114  * Winbond W83627THF: GPIO 4, bit 4
115  *
116  * Suited for:
117  *  - MSI K8T Neo2-F
118  *  - MSI K8N-NEO3
119  */
120 static int w83627thf_gpio4_4_raise(uint16_t index, const char *name)
121 {
122         w836xx_ext_enter(index);
123
124         /* Is this the W83627THF? */
125         if (wbsio_read(index, 0x20) != 0x82) {  /* Super I/O device ID reg. */
126                 fprintf(stderr, "\nERROR: %s: W83627THF: Wrong ID: 0x%02X.\n",
127                         name, wbsio_read(index, 0x20));
128                 w836xx_ext_leave(index);
129                 return -1;
130         }
131
132         /* PINxxxxS: GPIO4/bit 4 multiplex -> GPIOXXX */
133
134         wbsio_write(index, 0x07, 0x09);      /* Select LDN 9: GPIO port 4 */
135         wbsio_mask(index, 0x30, 0x02, 0x02); /* Activate logical device. */
136         wbsio_mask(index, 0xF4, 0x00, 0x10); /* GPIO4 bit 4 -> output */
137         wbsio_mask(index, 0xF6, 0x00, 0x10); /* Clear GPIO4 bit 4 inversion */
138         wbsio_mask(index, 0xF5, 0x10, 0x10); /* Raise GPIO4 bit 4 */
139
140         w836xx_ext_leave(index);
141
142         return 0;
143 }
144
145 static int w83627thf_gpio4_4_raise_2e(const char *name)
146 {
147         return w83627thf_gpio4_4_raise(0x2e, name);
148 }
149
150 static int w83627thf_gpio4_4_raise_4e(const char *name)
151 {
152         return w83627thf_gpio4_4_raise(0x4e, name);
153 }
154
155 /**
156  * Suited for VIAs EPIA M and MII, and maybe other CLE266 based EPIAs.
157  *
158  * We don't need to do this when using coreboot, GPIO15 is never lowered there.
159  */
160 static int board_via_epia_m(const char *name)
161 {
162         struct pci_dev *dev;
163         uint16_t base;
164         uint8_t val;
165
166         dev = pci_dev_find(0x1106, 0x3177);     /* VT8235 ISA bridge */
167         if (!dev) {
168                 fprintf(stderr, "\nERROR: VT8235 ISA bridge not found.\n");
169                 return -1;
170         }
171
172         /* GPIO12-15 -> output */
173         val = pci_read_byte(dev, 0xE4);
174         val |= 0x10;
175         pci_write_byte(dev, 0xE4, val);
176
177         /* Get Power Management IO address. */
178         base = pci_read_word(dev, 0x88) & 0xFF80;
179
180         /* Enable GPIO15 which is connected to write protect. */
181         val = INB(base + 0x4D);
182         val |= 0x80;
183         OUTB(val, base + 0x4D);
184
185         return 0;
186 }
187
188 /**
189  * Suited for:
190  *   - ASUS A7V8X-MX SE and A7V400-MX: AMD K7 + VIA KM400A + VT8235
191  *   - Tyan Tomcat K7M: AMD Geode NX + VIA KM400 + VT8237.
192  */
193 static int board_asus_a7v8x_mx(const char *name)
194 {
195         struct pci_dev *dev;
196         uint8_t val;
197
198         dev = pci_dev_find(0x1106, 0x3177);     /* VT8235 ISA bridge */
199         if (!dev)
200                 dev = pci_dev_find(0x1106, 0x3227);     /* VT8237 ISA bridge */
201         if (!dev) {
202                 fprintf(stderr, "\nERROR: VT823x ISA bridge not found.\n");
203                 return -1;
204         }
205
206         /* This bit is marked reserved actually. */
207         val = pci_read_byte(dev, 0x59);
208         val &= 0x7F;
209         pci_write_byte(dev, 0x59, val);
210
211         /* Raise ROM MEMW# line on Winbond W83697 Super I/O. */
212         w836xx_ext_enter(0x2E);
213
214         if (!(wbsio_read(0x2E, 0x24) & 0x02))   /* Flash ROM enabled? */
215                 wbsio_mask(0x2E, 0x24, 0x08, 0x08);     /* Enable MEMW#. */
216
217         w836xx_ext_leave(0x2E);
218
219         return 0;
220 }
221
222 /**
223  * Suited for VIAs EPIA SP.
224  */
225 static int board_via_epia_sp(const char *name)
226 {
227         struct pci_dev *dev;
228         uint8_t val;
229
230         dev = pci_dev_find(0x1106, 0x3227);     /* VT8237R ISA bridge */
231         if (!dev) {
232                 fprintf(stderr, "\nERROR: VT8237R ISA bridge not found.\n");
233                 return -1;
234         }
235
236         /* All memory cycles, not just ROM ones, go to LPC */
237         val = pci_read_byte(dev, 0x59);
238         val &= ~0x80;
239         pci_write_byte(dev, 0x59, val);
240
241         return 0;
242 }
243
244 /**
245  * Suited for ASUS P5A.
246  *
247  * This is rather nasty code, but there's no way to do this cleanly.
248  * We're basically talking to some unknown device on SMBus, my guess
249  * is that it is the Winbond W83781D that lives near the DIP BIOS.
250  */
251 static int board_asus_p5a(const char *name)
252 {
253         uint8_t tmp;
254         int i;
255
256 #define ASUSP5A_LOOP 5000
257
258         OUTB(0x00, 0xE807);
259         OUTB(0xEF, 0xE803);
260
261         OUTB(0xFF, 0xE800);
262
263         for (i = 0; i < ASUSP5A_LOOP; i++) {
264                 OUTB(0xE1, 0xFF);
265                 if (INB(0xE800) & 0x04)
266                         break;
267         }
268
269         if (i == ASUSP5A_LOOP) {
270                 printf("%s: Unable to contact device.\n", name);
271                 return -1;
272         }
273
274         OUTB(0x20, 0xE801);
275         OUTB(0x20, 0xE1);
276
277         OUTB(0xFF, 0xE802);
278
279         for (i = 0; i < ASUSP5A_LOOP; i++) {
280                 tmp = INB(0xE800);
281                 if (tmp & 0x70)
282                         break;
283         }
284
285         if ((i == ASUSP5A_LOOP) || !(tmp & 0x10)) {
286                 printf("%s: failed to read device.\n", name);
287                 return -1;
288         }
289
290         tmp = INB(0xE804);
291         tmp &= ~0x02;
292
293         OUTB(0x00, 0xE807);
294         OUTB(0xEE, 0xE803);
295
296         OUTB(tmp, 0xE804);
297
298         OUTB(0xFF, 0xE800);
299         OUTB(0xE1, 0xFF);
300
301         OUTB(0x20, 0xE801);
302         OUTB(0x20, 0xE1);
303
304         OUTB(0xFF, 0xE802);
305
306         for (i = 0; i < ASUSP5A_LOOP; i++) {
307                 tmp = INB(0xE800);
308                 if (tmp & 0x70)
309                         break;
310         }
311
312         if ((i == ASUSP5A_LOOP) || !(tmp & 0x10)) {
313                 printf("%s: failed to write to device.\n", name);
314                 return -1;
315         }
316
317         return 0;
318 }
319
320 static int board_ibm_x3455(const char *name)
321 {
322         uint8_t byte;
323
324         /* Set GPIO lines in the Broadcom HT-1000 southbridge. */
325         OUTB(0x45, 0xcd6);
326         byte = INB(0xcd7);
327         OUTB(byte | 0x20, 0xcd7);
328
329         return 0;
330 }
331
332 /**
333  * Suited for EPoX EP-BX3, and maybe some other Intel 440BX based boards.
334  */
335 static int board_epox_ep_bx3(const char *name)
336 {
337         uint8_t tmp;
338
339         /* Raise GPIO22. */
340         tmp = INB(0x4036);
341         OUTB(tmp, 0xEB);
342
343         tmp |= 0x40;
344
345         OUTB(tmp, 0x4036);
346         OUTB(tmp, 0xEB);
347
348         return 0;
349 }
350
351 /**
352  * Suited for Acorp 6A815EPD.
353  */
354 static int board_acorp_6a815epd(const char *name)
355 {
356         struct pci_dev *dev;
357         uint16_t port;
358         uint8_t val;
359
360         dev = pci_dev_find(0x8086, 0x2440);     /* Intel ICH2 LPC */
361         if (!dev) {
362                 fprintf(stderr, "\nERROR: ICH2 LPC bridge not found.\n");
363                 return -1;
364         }
365
366         /* Use GPIOBASE register to find where the GPIO is mapped. */
367         port = (pci_read_word(dev, 0x58) & 0xFFC0) + 0xE;
368
369         val = INB(port);
370         val |= 0x80; /* Top Block Lock -- pin 8 of PLCC32 */
371         val |= 0x40; /* Lower Blocks Lock -- pin 7 of PLCC32 */
372         OUTB(val, port);
373
374         return 0;
375 }
376
377 /**
378  * Suited for Artec Group DBE61 and DBE62.
379  */
380 static int board_artecgroup_dbe6x(const char *name)
381 {
382 #define DBE6x_MSR_DIVIL_BALL_OPTS       0x51400015
383 #define DBE6x_PRI_BOOT_LOC_SHIFT        (2)
384 #define DBE6x_BOOT_OP_LATCHED_SHIFT     (8)
385 #define DBE6x_SEC_BOOT_LOC_SHIFT        (10)
386 #define DBE6x_PRI_BOOT_LOC              (3 << DBE6x_PRI_BOOT_LOC_SHIFT)
387 #define DBE6x_BOOT_OP_LATCHED           (3 << DBE6x_BOOT_OP_LATCHED_SHIFT)
388 #define DBE6x_SEC_BOOT_LOC              (3 << DBE6x_SEC_BOOT_LOC_SHIFT)
389 #define DBE6x_BOOT_LOC_FLASH            (2)
390 #define DBE6x_BOOT_LOC_FWHUB            (3)
391
392         unsigned long msr[2];
393         int msr_fd;
394         unsigned long boot_loc;
395
396         msr_fd = open("/dev/cpu/0/msr", O_RDWR);
397         if (msr_fd == -1) {
398                 perror("open /dev/cpu/0/msr");
399                 return -1;
400         }
401
402         if (lseek(msr_fd, DBE6x_MSR_DIVIL_BALL_OPTS, SEEK_SET) == -1) {
403                 perror("lseek");
404                 close(msr_fd);
405                 return -1;
406         }
407
408         if (read(msr_fd, (void*) msr, 8) != 8) {
409                 perror("read");
410                 close(msr_fd);
411                 return -1;
412         }
413
414         if ((msr[0] & (DBE6x_BOOT_OP_LATCHED)) ==
415             (DBE6x_BOOT_LOC_FWHUB << DBE6x_BOOT_OP_LATCHED_SHIFT))
416                 boot_loc = DBE6x_BOOT_LOC_FWHUB;
417         else
418                 boot_loc = DBE6x_BOOT_LOC_FLASH;
419
420         msr[0] &= ~(DBE6x_PRI_BOOT_LOC | DBE6x_SEC_BOOT_LOC);
421         msr[0] |= ((boot_loc << DBE6x_PRI_BOOT_LOC_SHIFT) |
422             (boot_loc << DBE6x_SEC_BOOT_LOC_SHIFT));
423
424         if (lseek(msr_fd, DBE6x_MSR_DIVIL_BALL_OPTS, SEEK_SET) == -1) {
425                 perror("lseek");
426                 close(msr_fd);
427                 return -1;
428         }
429
430         if (write(msr_fd, (void*) msr, 8) != 8) {
431                 perror("write");
432                 close(msr_fd);
433                 return -1;
434         }
435
436         close(msr_fd);
437         return 0;
438 }
439
440 /**
441  * Set the specified GPIO on the specified ICHx southbridge to high.
442  *
443  * @param name The name of this board.
444  * @param ich_vendor PCI vendor ID of the specified ICHx southbridge.
445  * @param ich_device PCI device ID of the specified ICHx southbridge.
446  * @param gpiobase_reg GPIOBASE register offset in the LPC bridge.
447  * @param gp_lvl Offset of GP_LVL register in I/O space, relative to GPIOBASE.
448  * @param gp_lvl_bitmask GP_LVL bitmask (set GPIO bits to 1, all others to 0).
449  * @param gpio_bit The bit (GPIO) which shall be set to high.
450  * @return If the write-enable was successful return 0, otherwise return -1.
451  */
452 static int ich_gpio_raise(const char *name, uint16_t ich_vendor,
453                           uint16_t ich_device, uint8_t gpiobase_reg,
454                           uint8_t gp_lvl, uint32_t gp_lvl_bitmask,
455                           unsigned int gpio_bit)
456 {
457         struct pci_dev *dev;
458         uint16_t gpiobar;
459         uint32_t reg32;
460
461         dev = pci_dev_find(ich_vendor, ich_device);     /* Intel ICHx LPC */
462         if (!dev) {
463                 fprintf(stderr, "\nERROR: ICHx LPC dev %4x:%4x not found.\n",
464                         ich_vendor, ich_device);
465                 return -1;
466         }
467
468         /* Use GPIOBASE register to find the I/O space for GPIO. */
469         gpiobar = pci_read_word(dev, gpiobase_reg) & gp_lvl_bitmask;
470
471         /* Set specified GPIO to high. */
472         reg32 = INL(gpiobar + gp_lvl);
473         reg32 |= (1 << gpio_bit);
474         OUTL(reg32, gpiobar + gp_lvl);
475
476         return 0;
477 }
478
479 /**
480  * Suited for ASUS P4B266.
481  */
482 static int ich2_gpio22_raise(const char *name)
483 {
484         return ich_gpio_raise(name, 0x8086, 0x2440, 0x58, 0x0c, 0xffc0, 22);
485 }
486
487 static int board_kontron_986lcd_m(const char *name)
488 {
489         struct pci_dev *dev;
490         uint16_t gpiobar;
491         uint32_t val;
492
493 #define ICH7_GPIO_LVL2 0x38
494
495         dev = pci_dev_find(0x8086, 0x27b8);     /* Intel ICH7 LPC */
496         if (!dev) {
497                 // This will never happen on this board
498                 fprintf(stderr, "\nERROR: ICH7 LPC bridge not found.\n");
499                 return -1;
500         }
501
502         /* Use GPIOBASE register to find where the GPIO is mapped. */
503         gpiobar = pci_read_word(dev, 0x48) & 0xfffc;
504
505         val = INL(gpiobar + ICH7_GPIO_LVL2);    /* GP_LVL2 */
506         printf_debug("\nGPIOBAR=0x%04x GP_LVL: 0x%08x\n", gpiobar, val);
507
508         /* bit 2 (0x04) = 0 #TBL --> bootblock locking = 1
509          * bit 2 (0x04) = 1 #TBL --> bootblock locking = 0
510          * bit 3 (0x08) = 0 #WP --> block locking = 1
511          * bit 3 (0x08) = 1 #WP --> block locking = 0
512          *
513          * To enable full block locking, you would do:
514          *     val &= ~ ((1 << 2) | (1 << 3));
515          */
516         val |= (1 << 2) | (1 << 3);
517
518         OUTL(val, gpiobar + ICH7_GPIO_LVL2);
519
520         return 0;
521 }
522
523 /**
524  * Suited for:
525  *   - BioStar P4M80-M4: Intel P4 + VIA P4M800 + VT8237
526  *   - GIGABYTE GA-7VT600: AMD K7 + VIA KT600 + VT8237
527  */
528 static int board_biostar_p4m80_m4(const char *name)
529 {
530         /* enter IT87xx conf mode */
531         OUTB(0x87, 0x2e);
532         OUTB(0x01, 0x2e);
533         OUTB(0x55, 0x2e);
534         OUTB(0x55, 0x2e);
535
536         /* select right flash chip */
537         wbsio_mask(0x2e, 0x22, 0x80, 0x80);
538
539         /* bit 3: flash chip write enable
540          * bit 7: map flash chip at 1MB-128K (why though? ignoring this.)
541          */
542         wbsio_mask(0x2e, 0x24, 0x04, 0x04);
543
544         /* exit IT87xx conf mode */
545         wbsio_write(0x2, 0x2e, 0x2);
546
547         return 0;
548 }
549
550 /**
551  * We use 2 sets of IDs here, you're free to choose which is which. This
552  * is to provide a very high degree of certainty when matching a board on
553  * the basis of subsystem/card IDs. As not every vendor handles
554  * subsystem/card IDs in a sane manner.
555  *
556  * Keep the second set NULLed if it should be ignored.
557  *
558  * Keep the subsystem IDs NULLed if they don't identify the board fully.
559  */
560 struct board_pciid_enable {
561         /* Any device, but make it sensible, like the ISA bridge. */
562         uint16_t first_vendor;
563         uint16_t first_device;
564         uint16_t first_card_vendor;
565         uint16_t first_card_device;
566
567         /* Any device, but make it sensible, like 
568          * the host bridge. May be NULL.
569          */
570         uint16_t second_vendor;
571         uint16_t second_device;
572         uint16_t second_card_vendor;
573         uint16_t second_card_device;
574
575         /* The vendor / part name from the coreboot table. */
576         const char *lb_vendor;
577         const char *lb_part;
578
579         const char *name;
580         int (*enable) (const char *name);
581 };
582
583 struct board_pciid_enable board_pciid_enables[] = {
584         {0x8086, 0x1a30, 0x1043, 0x8070, 0x8086, 0x244b, 0x1043, 0x8028,
585          NULL, NULL, "ASUS P4B266", ich2_gpio22_raise},
586         {0x10de, 0x0360, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
587          "gigabyte", "m57sli", "GIGABYTE GA-M57SLI-S4", it87xx_probe_spi_flash},
588         {0x10de, 0x03e0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
589          "gigabyte", "m61p", "GIGABYTE GA-M61P-S3", it87xx_probe_spi_flash},
590         {0x1039, 0x0761, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
591          "gigabyte", "2761gxdk", "GIGABYTE GA-2761GXDK", it87xx_probe_spi_flash},
592         {0x1022, 0x7468, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
593          "iwill", "dk8_htx", "IWILL DK8-HTX", w83627hf_gpio24_raise_2e},
594         {0x10de, 0x005e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
595          "msi", "k8n-neo3", "MSI K8N Neo3", w83627thf_gpio4_4_raise_4e},
596         {0x1022, 0x746B, 0x1022, 0x36C0, 0x0000, 0x0000, 0x0000, 0x0000,
597          "AGAMI", "ARUMA", "agami Aruma", w83627hf_gpio24_raise_2e},
598         {0x1106, 0x3177, 0x1106, 0xAA01, 0x1106, 0x3123, 0x1106, 0xAA01,
599          NULL, NULL, "VIA EPIA M/MII/...", board_via_epia_m},
600         {0x1106, 0x3177, 0x1043, 0x80A1, 0x1106, 0x3205, 0x1043, 0x8118,
601          NULL, NULL, "ASUS A7V8-MX SE", board_asus_a7v8x_mx},
602         {0x1106, 0x3227, 0x1106, 0xAA01, 0x1106, 0x0259, 0x1106, 0xAA01,
603          NULL, NULL, "VIA EPIA SP", board_via_epia_sp},
604         {0x1106, 0x0314, 0x1106, 0xaa08, 0x1106, 0x3227, 0x1106, 0xAA08,
605          NULL, NULL, "VIA EPIA-CN", board_via_epia_sp},
606         {0x8086, 0x1076, 0x8086, 0x1176, 0x1106, 0x3059, 0x10f1, 0x2498,
607          NULL, NULL, "Tyan Tomcat K7M", board_asus_a7v8x_mx},
608         {0x10B9, 0x1541, 0x0000, 0x0000, 0x10B9, 0x1533, 0x0000, 0x0000,
609          "asus", "p5a", "ASUS P5A", board_asus_p5a},
610         {0x1166, 0x0205, 0x1014, 0x0347, 0x0000, 0x0000, 0x0000, 0x0000,
611          "ibm", "x3455", "IBM x3455", board_ibm_x3455},
612         {0x8086, 0x7110, 0x0000, 0x0000, 0x8086, 0x7190, 0x0000, 0x0000,
613          "epox", "ep-bx3", "EPoX EP-BX3", board_epox_ep_bx3},
614         {0x8086, 0x1130, 0x0000, 0x0000, 0x105a, 0x0d30, 0x105a, 0x4d33,
615          "acorp", "6a815epd", "Acorp 6A815EPD", board_acorp_6a815epd},
616         {0x1022, 0x2090, 0x0000, 0x0000, 0x1022, 0x2080, 0x0000, 0x0000,
617          "artecgroup", "dbe61", "Artec Group DBE61", board_artecgroup_dbe6x},
618         {0x1022, 0x2090, 0x0000, 0x0000, 0x1022, 0x2080, 0x0000, 0x0000,
619          "artecgroup", "dbe62", "Artec Group DBE62", board_artecgroup_dbe6x},
620         {0x8086, 0x27b8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
621          "kontron", "986lcd-m", "Kontron 986LCD-M", board_kontron_986lcd_m},
622         {0x1106, 0x3149, 0x1565, 0x3206, 0x1106, 0x3344, 0x1565, 0x1202,
623          NULL, NULL, "BioStar P4M80-M4", board_biostar_p4m80_m4},
624         {0x1106, 0x3227, 0x1458, 0x5001, 0x10ec, 0x8139, 0x1458, 0xe000,
625          NULL, NULL, "GIGABYTE GA-7VT600", board_biostar_p4m80_m4},
626         {0x1106, 0x3149, 0x1462, 0x7094, 0x10ec, 0x8167, 0x1462, 0x094c,
627          NULL, NULL, "MSI K8T Neo2", w83627thf_gpio4_4_raise_2e},
628         {0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL}    /* Keep this */
629 };
630
631 void print_supported_boards(void)
632 {
633         int i;
634
635         printf("\nSupported mainboards (this list is not exhaustive!):\n\n");
636
637         for (i = 0; board_pciid_enables[i].name != NULL; i++) {
638                 if (board_pciid_enables[i].lb_vendor != NULL) {
639                         printf("%s (-m %s:%s)\n", board_pciid_enables[i].name,
640                                board_pciid_enables[i].lb_vendor,
641                                board_pciid_enables[i].lb_part);
642                 } else {
643                         printf("%s (autodetected)\n",
644                                board_pciid_enables[i].name);
645                 }
646         }
647
648         printf("\nSee also: http://coreboot.org/Flashrom\n");
649 }
650
651 /**
652  * Match boards on coreboot table gathered vendor and part name.
653  * Require main PCI IDs to match too as extra safety.
654  */
655 static struct board_pciid_enable *board_match_coreboot_name(const char *vendor, const char *part)
656 {
657         struct board_pciid_enable *board = board_pciid_enables;
658         struct board_pciid_enable *partmatch = NULL;
659
660         for (; board->name; board++) {
661                 if (vendor && (!board->lb_vendor || strcasecmp(board->lb_vendor, vendor)))
662                         continue;
663
664                 if (!board->lb_part || strcasecmp(board->lb_part, part))
665                         continue;
666
667                 if (!pci_dev_find(board->first_vendor, board->first_device))
668                         continue;
669
670                 if (board->second_vendor &&
671                         !pci_dev_find(board->second_vendor, board->second_device))
672                         continue;
673
674                 if (vendor)
675                         return board;
676
677                 if (partmatch) {
678                         /* a second entry has a matching part name */
679                         printf("AMBIGUOUS BOARD NAME: %s\n", part);
680                         printf("At least vendors '%s' and '%s' match.\n",
681                                 partmatch->lb_vendor, board->lb_vendor);
682                         printf("Please use the full -m vendor:part syntax.\n");
683                         return NULL;
684                 }
685                 partmatch = board;
686         }
687
688         if (partmatch)
689                 return partmatch;
690
691         printf("\nUnknown vendor:board from coreboot table or -m option: %s:%s\n\n", vendor, part);
692         return NULL;
693 }
694
695 /**
696  * Match boards on PCI IDs and subsystem IDs.
697  * Second set of IDs can be main only or missing completely.
698  */
699 static struct board_pciid_enable *board_match_pci_card_ids(void)
700 {
701         struct board_pciid_enable *board = board_pciid_enables;
702
703         for (; board->name; board++) {
704                 if (!board->first_card_vendor || !board->first_card_device)
705                         continue;
706
707                 if (!pci_card_find(board->first_vendor, board->first_device,
708                                         board->first_card_vendor,
709                                         board->first_card_device))
710                         continue;
711
712                 if (board->second_vendor) {
713                         if (board->second_card_vendor) {
714                                 if (!pci_card_find(board->second_vendor,
715                                                 board->second_device,
716                                                 board->second_card_vendor,
717                                                 board->second_card_device))
718                                         continue;
719                         } else {
720                                 if (!pci_dev_find(board->second_vendor,
721                                                         board->second_device))
722                                         continue;
723                         }
724                 }
725
726                 return board;
727         }
728
729         return NULL;
730 }
731
732 int board_flash_enable(const char *vendor, const char *part)
733 {
734         struct board_pciid_enable *board = NULL;
735         int ret = 0;
736
737         if (part)
738                 board = board_match_coreboot_name(vendor, part);
739
740         if (!board)
741                 board = board_match_pci_card_ids();
742
743         if (board) {
744                 printf("Found board \"%s\", enabling flash write... ",
745                         board->name);
746
747                 ret = board->enable(board->name);
748                 if (ret)
749                         printf("FAILED!\n");
750                 else
751                         printf("OK.\n");
752         }
753
754         return ret;
755 }