blob: 983fd18ec6e8324ebf2c8dcce3dd234a2c700a80 [file] [log] [blame]
stepan927d4e22007-04-04 22:45:58 +00001/*
2 * flash rom utility: enable flash writes (board specific)
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 Luc Verhaegen <libv@skynet.be>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2
11 *
12 */
13
14/*
15 * Contains the board specific flash enables.
16 */
17
18#include <stdio.h>
19#include <pci/pci.h>
20#include <stdint.h>
21#include <string.h>
stepan927d4e22007-04-04 22:45:58 +000022#include "flash.h"
stepan927d4e22007-04-04 22:45:58 +000023
stuge04909772007-05-04 04:47:04 +000024/*
25 * Helper functions for many Winbond superIOs of the w836xx range.
26 */
27#define W836_INDEX 0x2E
28#define W836_DATA 0x2F
29
30/* Enter extended functions */
uwef6641642007-05-09 10:17:44 +000031static void w836xx_ext_enter(void)
uwe23438a02007-05-03 10:09:23 +000032{
stuge04909772007-05-04 04:47:04 +000033 outb(0x87, W836_INDEX);
34 outb(0x87, W836_INDEX);
35}
uwe23438a02007-05-03 10:09:23 +000036
stuge04909772007-05-04 04:47:04 +000037/* Leave extended functions */
uwef6641642007-05-09 10:17:44 +000038static void w836xx_ext_leave(void)
stuge04909772007-05-04 04:47:04 +000039{
40 outb(0xAA, W836_INDEX);
41}
uwe23438a02007-05-03 10:09:23 +000042
stuge04909772007-05-04 04:47:04 +000043/* General functions for read/writing WB SuperIOs */
uwef6641642007-05-09 10:17:44 +000044static unsigned char wbsio_read(unsigned char index)
stuge04909772007-05-04 04:47:04 +000045{
46 outb(index, W836_INDEX);
47 return inb(W836_DATA);
48}
uwe23438a02007-05-03 10:09:23 +000049
uwef6641642007-05-09 10:17:44 +000050static void wbsio_write(unsigned char index, unsigned char data)
stuge04909772007-05-04 04:47:04 +000051{
52 outb(index, W836_INDEX);
53 outb(data, W836_DATA);
54}
uwe23438a02007-05-03 10:09:23 +000055
stuge04909772007-05-04 04:47:04 +000056static void
uwef6641642007-05-09 10:17:44 +000057wbsio_mask(unsigned char index, unsigned char data, unsigned char mask)
stuge04909772007-05-04 04:47:04 +000058{
59 unsigned char tmp;
uwe23438a02007-05-03 10:09:23 +000060
stuge04909772007-05-04 04:47:04 +000061 outb(index, W836_INDEX);
62 tmp = inb(W836_DATA) & ~mask;
63 outb(tmp | (data & mask), W836_DATA);
uwe23438a02007-05-03 10:09:23 +000064}
65
stepan927d4e22007-04-04 22:45:58 +000066/*
stuge04909772007-05-04 04:47:04 +000067 * WinBond w83627hf: raise GPIO24.
68 *
69 * Suited for:
70 * * Agami Aruma
71 * * IWILL DK8-HTX
stepan927d4e22007-04-04 22:45:58 +000072 */
73
stuge04909772007-05-04 04:47:04 +000074static int w83627hf_gpio24_raise(const char *name)
stepan927d4e22007-04-04 22:45:58 +000075{
stuge04909772007-05-04 04:47:04 +000076 w836xx_ext_enter();
stepan927d4e22007-04-04 22:45:58 +000077
stuge04909772007-05-04 04:47:04 +000078 /* Is this the w83627hf? */
uwef6641642007-05-09 10:17:44 +000079 if (wbsio_read(0x20) != 0x52) { /* SIO device ID register */
stuge04909772007-05-04 04:47:04 +000080 fprintf(stderr, "\nERROR: %s: W83627HF: Wrong ID: 0x%02X.\n",
81 name, wbsio_read(0x20));
82 w836xx_ext_leave();
stepan927d4e22007-04-04 22:45:58 +000083 return -1;
84 }
85
stuge04909772007-05-04 04:47:04 +000086 /* PIN89S: WDTO/GP24 multiplex -> GPIO24 */
87 wbsio_mask(0x2B, 0x10, 0x10);
stepan927d4e22007-04-04 22:45:58 +000088
uwef6641642007-05-09 10:17:44 +000089 wbsio_write(0x07, 0x08); /* Select logical device 8: GPIO port 2 */
stepan927d4e22007-04-04 22:45:58 +000090
uwef6641642007-05-09 10:17:44 +000091 wbsio_mask(0x30, 0x01, 0x01); /* Activate logical device. */
stepan927d4e22007-04-04 22:45:58 +000092
uwef6641642007-05-09 10:17:44 +000093 wbsio_mask(0xF0, 0x00, 0x10); /* GPIO24 -> output */
stepan927d4e22007-04-04 22:45:58 +000094
uwef6641642007-05-09 10:17:44 +000095 wbsio_mask(0xF2, 0x00, 0x10); /* Clear GPIO24 inversion */
stepan927d4e22007-04-04 22:45:58 +000096
uwef6641642007-05-09 10:17:44 +000097 wbsio_mask(0xF1, 0x10, 0x10); /* Raise GPIO24 */
stuge04909772007-05-04 04:47:04 +000098
99 w836xx_ext_leave();
stepan927d4e22007-04-04 22:45:58 +0000100
101 return 0;
102}
103
104/*
105 * Suited for VIAs EPIA M and MII, and maybe other CLE266 based EPIAs.
106 *
107 * We don't need to do this when using linuxbios, GPIO15 is never lowered there.
108 */
109
stuge04909772007-05-04 04:47:04 +0000110static int board_via_epia_m(const char *name)
stepan927d4e22007-04-04 22:45:58 +0000111{
uwef6641642007-05-09 10:17:44 +0000112 struct pci_dev *dev;
113 unsigned int base;
114 uint8_t val;
stepan927d4e22007-04-04 22:45:58 +0000115
uwef6641642007-05-09 10:17:44 +0000116 dev = pci_dev_find(0x1106, 0x3177); /* VT8235 ISA bridge */
117 if (!dev) {
118 fprintf(stderr, "\nERROR: VT8235 ISA Bridge not found.\n");
119 return -1;
120 }
stepan927d4e22007-04-04 22:45:58 +0000121
uwef6641642007-05-09 10:17:44 +0000122 /* GPIO12-15 -> output */
123 val = pci_read_byte(dev, 0xE4);
124 val |= 0x10;
125 pci_write_byte(dev, 0xE4, val);
stepan927d4e22007-04-04 22:45:58 +0000126
uwef6641642007-05-09 10:17:44 +0000127 /* Get Power Management IO address. */
128 base = pci_read_word(dev, 0x88) & 0xFF80;
stepan927d4e22007-04-04 22:45:58 +0000129
uwef6641642007-05-09 10:17:44 +0000130 /* enable GPIO15 which is connected to write protect. */
131 val = inb(base + 0x4D);
132 val |= 0x80;
133 outb(val, base + 0x4D);
stepan927d4e22007-04-04 22:45:58 +0000134
uwef6641642007-05-09 10:17:44 +0000135 return 0;
stepan927d4e22007-04-04 22:45:58 +0000136}
137
138/*
uwe1d7c23d2007-07-04 17:51:49 +0000139 * Suited for:
140 * ASUS A7V8X-MX SE and A7V400-MX: AMD K7 + VIA KM400A + VT8235
141 * Tyan Tomcat K7M: AMD Geode NX + VIA KM400 + VT8237.
stepan927d4e22007-04-04 22:45:58 +0000142 */
143
stuge04909772007-05-04 04:47:04 +0000144static int board_asus_a7v8x_mx(const char *name)
stepan927d4e22007-04-04 22:45:58 +0000145{
uwef6641642007-05-09 10:17:44 +0000146 struct pci_dev *dev;
147 uint8_t val;
stepan927d4e22007-04-04 22:45:58 +0000148
uwef6641642007-05-09 10:17:44 +0000149 dev = pci_dev_find(0x1106, 0x3177); /* VT8235 ISA bridge */
uwe1d7c23d2007-07-04 17:51:49 +0000150 if (!dev)
151 dev = pci_dev_find(0x1106, 0x3227); /* VT8237 ISA bridge */
uwef6641642007-05-09 10:17:44 +0000152 if (!dev) {
uwe1d7c23d2007-07-04 17:51:49 +0000153 fprintf(stderr, "\nERROR: VT823x ISA bridge not found.\n");
uwef6641642007-05-09 10:17:44 +0000154 return -1;
155 }
stepan927d4e22007-04-04 22:45:58 +0000156
uwef6641642007-05-09 10:17:44 +0000157 /* This bit is marked reserved actually */
158 val = pci_read_byte(dev, 0x59);
159 val &= 0x7F;
160 pci_write_byte(dev, 0x59, val);
stepan927d4e22007-04-04 22:45:58 +0000161
stuge04909772007-05-04 04:47:04 +0000162 /* Raise ROM MEMW# line on Winbond w83697 SuperIO */
uwef6641642007-05-09 10:17:44 +0000163 w836xx_ext_enter();
stuge04909772007-05-04 04:47:04 +0000164
uwef6641642007-05-09 10:17:44 +0000165 if (!(wbsio_read(0x24) & 0x02)) /* flash rom enabled? */
166 wbsio_mask(0x24, 0x08, 0x08); /* enable MEMW# */
stuge04909772007-05-04 04:47:04 +0000167
168 w836xx_ext_leave();
stepan927d4e22007-04-04 22:45:58 +0000169
uwef6641642007-05-09 10:17:44 +0000170 return 0;
stepan927d4e22007-04-04 22:45:58 +0000171}
172
173/*
uwe691ddb62007-05-20 16:16:13 +0000174 * Suited for ASUS P5A.
175 *
176 * This is rather nasty code, but there's no way to do this cleanly.
177 * We're basically talking to some unknown device on SMBus, my guess
178 * is that it is the Winbond W83781D that lives near the DIP BIOS.
179 */
180
181static int board_asus_p5a(const char *name)
182{
183 uint8_t tmp;
184 int i;
185
186#define ASUSP5A_LOOP 5000
187
188 outb(0x00, 0xE807);
189 outb(0xEF, 0xE803);
190
191 outb(0xFF, 0xE800);
192
193 for (i = 0; i < ASUSP5A_LOOP; i++) {
194 outb(0xE1, 0xFF);
195 if (inb(0xE800) & 0x04)
196 break;
197 }
198
199 if (i == ASUSP5A_LOOP) {
200 printf("%s: Unable to contact device.\n", name);
201 return -1;
202 }
203
204 outb(0x20, 0xE801);
205 outb(0x20, 0xE1);
206
207 outb(0xFF, 0xE802);
208
209 for (i = 0; i < ASUSP5A_LOOP; i++) {
210 tmp = inb(0xE800);
211 if (tmp & 0x70)
212 break;
213 }
214
215 if ((i == ASUSP5A_LOOP) || !(tmp & 0x10)) {
216 printf("%s: failed to read device.\n", name);
217 return -1;
218 }
219
220 tmp = inb(0xE804);
221 tmp &= ~0x02;
222
223 outb(0x00, 0xE807);
224 outb(0xEE, 0xE803);
225
226 outb(tmp, 0xE804);
227
228 outb(0xFF, 0xE800);
229 outb(0xE1, 0xFF);
230
231 outb(0x20, 0xE801);
232 outb(0x20, 0xE1);
233
234 outb(0xFF, 0xE802);
235
236 for (i = 0; i < ASUSP5A_LOOP; i++) {
237 tmp = inb(0xE800);
238 if (tmp & 0x70)
239 break;
240 }
241
242 if ((i == ASUSP5A_LOOP) || !(tmp & 0x10)) {
243 printf("%s: failed to write to device.\n", name);
244 return -1;
245 }
246
247 return 0;
248}
249
stepan60b4d872007-06-05 12:51:52 +0000250static int board_ibm_x3455(const char *name)
251{
252 uint8_t byte;
253
uwefcce12f2007-06-05 15:02:18 +0000254 /* Set GPIO lines in the Broadcom HT-1000 southbridge. */
stepan60b4d872007-06-05 12:51:52 +0000255 outb(0x45, 0xcd6);
256 byte = inb(0xcd7);
uwefcce12f2007-06-05 15:02:18 +0000257 outb(byte | 0x20, 0xcd7);
stepan60b4d872007-06-05 12:51:52 +0000258
259 return 0;
260}
261
uwe0b88fc32007-08-11 16:59:11 +0000262/**
263 * Suited for EPoX EP-BX3, and maybe some other Intel 440BX based boards.
264 */
265static int board_epox_ep_bx3(const char *name)
266{
267 uint8_t tmp;
268
269 /* Raise GPIO22. */
270 tmp = inb(0x4036);
271 outb(tmp, 0xEB);
272
273 tmp |= 0x40;
274
275 outb(tmp, 0x4036);
276 outb(tmp, 0xEB);
277
278 return 0;
279}
280
uwe691ddb62007-05-20 16:16:13 +0000281/*
stepan927d4e22007-04-04 22:45:58 +0000282 * We use 2 sets of ids here, you're free to choose which is which. This
283 * to provide a very high degree of certainty when matching a board on
284 * the basis of Subsystem/card ids. As not every vendor handles
285 * subsystem/card ids in a sane manner.
286 *
287 * Keep the second set nulled if it should be ignored.
288 *
289 */
290
291struct board_pciid_enable {
uwef6641642007-05-09 10:17:44 +0000292 /* Any device, but make it sensible, like the isa bridge. */
293 uint16_t first_vendor;
294 uint16_t first_device;
295 uint16_t first_card_vendor;
296 uint16_t first_card_device;
stepan927d4e22007-04-04 22:45:58 +0000297
uwef6641642007-05-09 10:17:44 +0000298 /* Any device, but make it sensible, like
stepan927d4e22007-04-04 22:45:58 +0000299 * the host bridge. May be NULL
300 */
uwef6641642007-05-09 10:17:44 +0000301 uint16_t second_vendor;
302 uint16_t second_device;
303 uint16_t second_card_vendor;
304 uint16_t second_card_device;
stepan927d4e22007-04-04 22:45:58 +0000305
uwef6641642007-05-09 10:17:44 +0000306 /* From linuxbios table */
307 char *lb_vendor;
308 char *lb_part;
stepan927d4e22007-04-04 22:45:58 +0000309
uwef6641642007-05-09 10:17:44 +0000310 char *name;
311 int (*enable) (const char *name);
stepan927d4e22007-04-04 22:45:58 +0000312};
313
314struct board_pciid_enable board_pciid_enables[] = {
uwef6641642007-05-09 10:17:44 +0000315 {0x1022, 0x7468, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
316 "iwill", "dk8_htx", "IWILL DK8-HTX", w83627hf_gpio24_raise},
317 {0x1022, 0x746B, 0x1022, 0x36C0, 0x0000, 0x0000, 0x0000, 0x0000,
318 "AGAMI", "ARUMA", "agami Aruma", w83627hf_gpio24_raise},
319 {0x1106, 0x3177, 0x1106, 0xAA01, 0x1106, 0x3123, 0x1106, 0xAA01,
320 NULL, NULL, "VIA EPIA M/MII/...", board_via_epia_m},
321 {0x1106, 0x3177, 0x1043, 0x80A1, 0x1106, 0x3205, 0x1043, 0x8118,
322 NULL, NULL, "ASUS A7V8-MX SE", board_asus_a7v8x_mx},
uwe1d7c23d2007-07-04 17:51:49 +0000323 {0x8086, 0x1076, 0x8086, 0x1176, 0x1106, 0x3059, 0x10f1, 0x2498,
324 NULL, NULL, "Tyan Tomcat K7M", board_asus_a7v8x_mx},
uwe691ddb62007-05-20 16:16:13 +0000325 {0x10B9, 0x1541, 0x0000, 0x0000, 0x10B9, 0x1533, 0x0000, 0x0000,
326 "asus", "p5a", "ASUS P5A", board_asus_p5a},
stepan60b4d872007-06-05 12:51:52 +0000327 {0x1166, 0x0205, 0x1014, 0x0347, 0x0000, 0x0000, 0x0000, 0x0000,
328 "ibm", "x3455", "IBM x3455", board_ibm_x3455},
uwe0b88fc32007-08-11 16:59:11 +0000329 {0x8086, 0x7110, 0x0000, 0x0000, 0x8086, 0x7190, 0x0000, 0x0000,
330 "epox", "ep-bx3", "EPoX EP-BX3", board_epox_ep_bx3},
uwef6641642007-05-09 10:17:44 +0000331 {0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL} /* Keep this */
stepan927d4e22007-04-04 22:45:58 +0000332};
333
334/*
335 * Match boards on linuxbios table gathered vendor and part name.
336 * Require main pci-ids to match too as extra safety.
337 *
338 */
uwef6641642007-05-09 10:17:44 +0000339static struct board_pciid_enable *board_match_linuxbios_name(char *vendor,
340 char *part)
stepan927d4e22007-04-04 22:45:58 +0000341{
uwef6641642007-05-09 10:17:44 +0000342 struct board_pciid_enable *board = board_pciid_enables;
stepan927d4e22007-04-04 22:45:58 +0000343
uwef6641642007-05-09 10:17:44 +0000344 for (; board->name; board++) {
345 if (!board->lb_vendor || strcmp(board->lb_vendor, vendor))
346 continue;
stepan927d4e22007-04-04 22:45:58 +0000347
uwef6641642007-05-09 10:17:44 +0000348 if (!board->lb_part || strcmp(board->lb_part, part))
349 continue;
stepan927d4e22007-04-04 22:45:58 +0000350
uwef6641642007-05-09 10:17:44 +0000351 if (!pci_dev_find(board->first_vendor, board->first_device))
352 continue;
stepan927d4e22007-04-04 22:45:58 +0000353
uwef6641642007-05-09 10:17:44 +0000354 if (board->second_vendor &&
355 !pci_dev_find(board->second_vendor, board->second_device))
356 continue;
357 return board;
358 }
359 return NULL;
stepan927d4e22007-04-04 22:45:58 +0000360}
361
362/*
363 * Match boards on pci ids and subsystem ids.
364 * Second set of ids can be main only or missing completely.
365 */
366static struct board_pciid_enable *board_match_pci_card_ids(void)
367{
uwef6641642007-05-09 10:17:44 +0000368 struct board_pciid_enable *board = board_pciid_enables;
stepan927d4e22007-04-04 22:45:58 +0000369
uwef6641642007-05-09 10:17:44 +0000370 for (; board->name; board++) {
371 if (!board->first_card_vendor || !board->first_card_device)
372 continue;
stepan927d4e22007-04-04 22:45:58 +0000373
uwef6641642007-05-09 10:17:44 +0000374 if (!pci_card_find(board->first_vendor, board->first_device,
375 board->first_card_vendor,
376 board->first_card_device))
377 continue;
stepan927d4e22007-04-04 22:45:58 +0000378
uwef6641642007-05-09 10:17:44 +0000379 if (board->second_vendor) {
380 if (board->second_card_vendor) {
381 if (!pci_card_find(board->second_vendor,
382 board->second_device,
383 board->second_card_vendor,
384 board->second_card_device))
385 continue;
386 } else {
387 if (!pci_dev_find(board->second_vendor,
388 board->second_device))
389 continue;
390 }
391 }
stepan927d4e22007-04-04 22:45:58 +0000392
uwef6641642007-05-09 10:17:44 +0000393 return board;
394 }
stepan927d4e22007-04-04 22:45:58 +0000395
uwef6641642007-05-09 10:17:44 +0000396 return NULL;
stepan927d4e22007-04-04 22:45:58 +0000397}
398
399/*
400 *
401 */
402int board_flash_enable(char *vendor, char *part)
403{
uwef6641642007-05-09 10:17:44 +0000404 struct board_pciid_enable *board = NULL;
405 int ret = 0;
stepan927d4e22007-04-04 22:45:58 +0000406
uwef6641642007-05-09 10:17:44 +0000407 if (vendor && part)
408 board = board_match_linuxbios_name(vendor, part);
stepan927d4e22007-04-04 22:45:58 +0000409
uwef6641642007-05-09 10:17:44 +0000410 if (!board)
411 board = board_match_pci_card_ids();
stepan927d4e22007-04-04 22:45:58 +0000412
uwef6641642007-05-09 10:17:44 +0000413 if (board) {
414 printf("Found board \"%s\": Enabling flash write... ",
415 board->name);
stepan927d4e22007-04-04 22:45:58 +0000416
uwef6641642007-05-09 10:17:44 +0000417 ret = board->enable(board->name);
418 if (ret)
419 printf("Failed!\n");
420 else
421 printf("OK.\n");
422 }
stepan927d4e22007-04-04 22:45:58 +0000423
uwef6641642007-05-09 10:17:44 +0000424 return ret;
stepan927d4e22007-04-04 22:45:58 +0000425}