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