blob: f394e64b6fb8d132bd9468233a905c2fc648850f [file] [log] [blame]
David Hendricksbabd1b52010-08-19 13:16:19 -07001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2010 Google, Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * Neither the name of Nuvoton Technology Corporation. or the names of
18 * contributors or licensors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * This software is provided "AS IS," without a warranty of any kind.
22 * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
23 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
24 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
25 * NUVOTON TECHNOLOGY CORPORATION. ("NUVOTON") AND ITS LICENSORS SHALL NOT BE LIABLE
26 * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
27 * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
28 * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
29 * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
30 * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
31 * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
32 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
33 *
34 * This is an UNOFFICIAL patch for the Nuvoton WPCE775x/NPCE781x. It was tested
35 * for a specific hardware and firmware configuration and should be considered
36 * unreliable. Please see the following URL for Nuvoton's authoritative,
37 * officially supported flash update utility:
38 * http://sourceforge.net/projects/nuvflashupdate/
39 */
40
David Hendrickscb2cec32011-03-24 18:44:49 -070041#if defined(__i386__) || defined(__x86_64__)
David Hendricksbabd1b52010-08-19 13:16:19 -070042#include <assert.h>
43#include <string.h>
44#include <sys/time.h>
45#include <time.h>
46#include <unistd.h>
David Hendricksc801adb2010-12-09 16:58:56 -080047#include <stdlib.h>
David Hendricksbabd1b52010-08-19 13:16:19 -070048#include "flash.h"
49#include "chipdrivers.h"
50#include "flashchips.h"
51#include "programmer.h"
52#include "spi.h"
David Hendricks21e06302010-09-03 14:51:46 -070053#include "writeprotect.h"
David Hendricksbabd1b52010-08-19 13:16:19 -070054
55/**
56 * Definition of WPCE775X WCB (Write Command Buffer), as known as Shared Access
57 * Window 2.
58 *
59 * The document name is "WPCE775X Software User Guide Revision 1.2".
60 *
61 * Assume the host is little endian.
62 */
63__attribute__((packed))
64struct wpce775x_wcb {
65 /* Byte 0: semaphore byte */
66 unsigned char exe:1; /* Bit0-RW- set by host. means wcb is ready to execute.
67 should be cleared by host after RDY=1. */
68 unsigned char resv0_41:4;
69 unsigned char pcp:1; /* Bit5-RO- set by EPCE775x. means preparation operations for
70 flash update process is complete. */
71 unsigned char err:1; /* Bit6-RO- set by EPCE775x. means an error occurs. */
72 unsigned char rdy:1; /* Bit7-RO- set by EPCE775x. means operation is completed. */
73
74 /* Byte 1-2: reserved */
75 unsigned char byte1;
76 unsigned char byte2;
77
78 /* Byte 3: command code */
79 unsigned char code;
80
81 /* Byte 4-15: command field */
82 unsigned char field[12];
83};
84
85/* The physical address of WCB -- Shared Access Window 2. */
86static chipaddr wcb_physical_address;
87
88/* The virtual address of WCB -- Shared Access Window 2. */
89static volatile struct wpce775x_wcb *volatile wcb;
90
91/* count of entering flash update mode */
92static int in_flash_update_mode;
93
David Hendricksc801adb2010-12-09 16:58:56 -080094static int firmware_changed;
95
96/*
97 * Bytes 0x4-0xf of InitFlash command. These represent opcodes and various
98 * parameters the WPCE775x will use when communicating with the SPI flash
99 * device. DO NOT RE-ORDER THIS STRUCTURE.
100 */
101struct wpce775x_initflash_cfg {
102 uint8_t read_device_id; /* Byte 0x04. Ex: JEDEC_RDID */
103 uint8_t write_status_enable; /* Byte 0x05. Ex: JEDEC_EWSR */
104 uint8_t write_enable; /* Byte 0x06. Ex: JEDEC_WREN */
105 uint8_t read_status_register; /* Byte 0x07. Ex: JEDEC_RDSR */
106 uint8_t write_status_register; /* Byte 0x08. Ex: JEDEC_WRSR */
107 uint8_t flash_program; /* Byte 0x09. Ex: JEDEC_BYTE_PROGRAM */
108
109 /* Byte 0x0A. Ex: sector/block/chip erase opcode */
110 uint8_t block_erase;
111
112 uint8_t status_busy_mask; /* Byte B: bit position of BUSY bit */
113
114 /* Byte 0x0C: value to remove write protection */
115 uint8_t status_reg_value;
116
117 /* Byte 0x0D: Number of bytes to program in each write transaction. */
118 uint8_t program_unit_size;
119
120 uint8_t page_size; /* Byte 0x0E: 2^n bytes */
121
122 /*
123 * Byte 0x0F: Method to read device ID. 0x47 will cause ID bytes to be
124 * read immediately after read_device_id command is issued. Otherwise,
125 * 3 dummy address bytes are sent after the read_device_id code.
126 */
127 uint8_t read_device_id_type;
128} __attribute__((packed));
129
130/*
131 * The WPCE775x can use InitFlash multiple times during an update. We'll use
132 * this ability primarily for changing write protection bits.
133 */
134static struct wpce775x_initflash_cfg *initflash_cfg;
135
136static struct flashchip *flash_internal;
137
138
David Hendricksbabd1b52010-08-19 13:16:19 -0700139/* Indicate the flash chip attached to the WPCE7xxx chip.
140 * This variable should be set in probe_wpce775x().
141 * 0 means we haven't or cannot detect the chip type. */
142struct flashchip *scan = 0;
143
144/* SuperI/O related definitions and functions. */
145/* Strapping options */
146#define NUVOTON_SIO_PORT1 0x2e /* No pull-down resistor */
147#define NUVOTON_SIO_PORT2 0x164e /* Pull-down resistor on BADDR0 */
148/* Note: There's another funky state that we won't worry about right now */
149
150/* SuperI/O Config */
151#define NUVOTON_SIOCFG_LDN 0x07 /* LDN Bank Selector */
152#define NUVOTON_SIOCFG_SID 0x20 /* SuperI/O ID */
153#define NUVOTON_SIOCFG_SRID 0x27 /* SuperI/O Revision ID */
154#define NUVOTON_LDN_SHM 0x0f /* LDN of SHM module */
155
156/* WPCE775x shared memory config registers (LDN 0x0f) */
157#define WPCE775X_SHM_BASE_MSB 0x60
158#define WPCE775X_SHM_BASE_LSB 0x61
159#define WPCE775X_SHM_CFG 0xf0
160#define WPCE775X_SHM_CFG_BIOS_FWH_EN (1 << 3)
161#define WPCE775X_SHM_CFG_FLASH_ACC_EN (1 << 2)
162#define WPCE775X_SHM_CFG_BIOS_EXT_EN (1 << 1)
163#define WPCE775X_SHM_CFG_BIOS_LPC_EN (1 << 0)
164#define WPCE775X_WIN_CFG 0xf1 /* window config */
165#define WPCE775X_WIN_CFG_SHWIN_ACC (1 << 6)
166
167/* Shared access window 2 bar address registers */
168#define WPCE775X_SHAW2BA_0 0xf8
169#define WPCE775X_SHAW2BA_1 0xf9
170#define WPCE775X_SHAW2BA_2 0xfa
171#define WPCE775X_SHAW2BA_3 0xfb
172
David Hendricksc801adb2010-12-09 16:58:56 -0800173/* Read/write buffer size */
174#define WPCE775X_MAX_WRITE_SIZE 8
175#define WPCE775X_MAX_READ_SIZE 12
176
David Hendricksbabd1b52010-08-19 13:16:19 -0700177/** probe for super i/o index
178 * @returns 0 to indicate success, <0 to indicate error
179 */
180static int nuvoton_get_sio_index(uint16_t *port)
181{
182 uint16_t ports[] = { NUVOTON_SIO_PORT2,
183 NUVOTON_SIO_PORT1,
184 };
185 int i;
186 static uint16_t port_internal, port_found = 0;
187
188 if (port_found) {
189 *port = port_internal;
190 return 0;
191 }
192
193 get_io_perms();
194
195 for (i = 0; i < ARRAY_SIZE(ports); i++) {
196 uint8_t sid = sio_read(ports[i], NUVOTON_SIOCFG_SID);
197
198 if (sid == 0xfc) { /* Family ID */
199 port_internal = ports[i];
200 port_found = 1;
201 break;
202 }
203 }
204
205 if (!port_found) {
206 msg_cdbg("\nfailed to obtain super i/o index");
207 return -1;
208 }
209
210 msg_cdbg("\nsuper i/o index = 0x%04x\n", port_internal);
211 *port = port_internal;
212 return 0;
213}
214
215/** Call superio to get pre-configured WCB address.
216 * Read LDN 0x0f (SHM) idx:f8-fb (little-endian).
217 */
218static int get_shaw2ba(chipaddr *shaw2ba)
219{
220 uint16_t idx;
221 uint8_t org_ldn;
222 uint8_t win_cfg;
223 uint8_t shm_cfg;
224
225 if (nuvoton_get_sio_index(&idx) < 0)
226 return -1;
227
228 org_ldn = sio_read(idx, NUVOTON_SIOCFG_LDN);
229 sio_write(idx, NUVOTON_SIOCFG_LDN, NUVOTON_LDN_SHM);
230
231 /*
232 * To obtain shared access window 2 base address, we must OR the base
233 * address bytes, where SHAW2BA_0 is least significant and SHAW2BA_3
234 * most significant.
235 */
236 *shaw2ba = sio_read(idx, WPCE775X_SHAW2BA_0) |
237 (sio_read(idx, WPCE775X_SHAW2BA_1) << 8) |
238 (sio_read(idx, WPCE775X_SHAW2BA_2) << 16) |
239 (sio_read(idx, WPCE775X_SHAW2BA_3) << 24);
240
241 /*
242 * If SHWIN_ACC is cleared, then we're using LPC memory access
243 * and SHAW2BA_3-0 indicate bits 31-0. If SHWIN_ACC is set, then
244 * bits 7-4 of SHAW2BA_3 are ignored and bits 31-28 are indicated
245 * by the idsel nibble. (See table 25 "supported host address ranges"
246 * for more details)
247 */
248 win_cfg = sio_read(idx, WPCE775X_WIN_CFG);
249 if (win_cfg & WPCE775X_WIN_CFG_SHWIN_ACC) {
250 uint8_t idsel;
251
252 /* Make sure shared BIOS memory is enabled */
253 shm_cfg = sio_read(idx, WPCE775X_SHM_CFG);
254 if ((shm_cfg & WPCE775X_SHM_CFG_BIOS_FWH_EN))
255 idsel = 0xf;
256 else {
257 msg_cdbg("Shared BIOS memory is diabled.\n");
258 msg_cdbg("Please check SHM_CFG:BIOS_FWH_EN.\n");
259 goto error;
260 }
261
262 *shaw2ba &= 0x0fffffff;
263 *shaw2ba |= idsel << 28;
264 }
265
266 sio_write(idx, NUVOTON_SIOCFG_LDN, org_ldn);
267 return 0;
268error:
269 sio_write(idx, NUVOTON_SIOCFG_LDN, org_ldn);
270 return -1;
271}
272
273/* Call superio to get pre-configured fwh_id.
274 * Read LDN 0x0f (SHM) idx:f0.
275 */
276static int get_fwh_id(uint8_t *fwh_id)
277{
278 uint16_t idx;
279 uint8_t org_ldn;
280
281 if (nuvoton_get_sio_index(&idx) < 0)
282 return -1;
283
284 org_ldn = sio_read(idx, NUVOTON_SIOCFG_LDN);
285 sio_write(idx, NUVOTON_SIOCFG_LDN, NUVOTON_LDN_SHM);
286 *fwh_id = sio_read(idx, WPCE775X_SHM_CFG);
287 sio_write(idx, NUVOTON_SIOCFG_LDN, org_ldn);
288
289 return 0;
290}
291
292/** helper function to make sure the exe bit is 0 (no one is using EC).
293 * @return 1 for error; 0 for success.
294 */
295static int assert_ec_is_free(void)
296{
297 if (wcb->exe)
298 msg_perr("ASSERT(wcb->exe==0), entering busy loop.\n");
299 while(wcb->exe);
300 return 0;
301}
302
303/** Trigger EXE bit, and block until operation completes.
304 * @return 1 for error; and 0 for success.
305 */
306static int blocked_exec(void)
307{
308 struct timeval begin, now;
309 int timeout; /* not zero if timeout occurs */
310 int err;
311
312 assert(wcb->rdy==0);
313
314 /* raise EXE bit, and wait for operation complete or error occur. */
315 wcb->exe = 1;
316
317 timeout = 0;
318 gettimeofday(&begin, NULL);
319 while(wcb->rdy==0 && wcb->err==0) {
320 gettimeofday(&now, NULL);
321 /* According to Nuvoton's suggestion, few seconds is enough for
322 * longest flash operation, which is erase.
323 * Cutted from W25X16 datasheet, for max operation time
324 * Byte program tBP1 50us
325 * Page program tPP 3ms
326 * Sector Erase (4KB) tSE 200ms
327 * Block Erase (64KB) tBE 1s
328 * Chip Erase tCE 20s
329 * Since WPCE775x doesn't support chip erase,
330 * 3 secs is long enough for block erase.
331 */
332 if ((now.tv_sec - begin.tv_sec) >= 4) {
333 timeout += 1;
334 break;
335 }
336 }
337
338 /* keep ERR bit before clearing EXE bit. */
339 err = wcb->err;
340
341 /* Clear EXE bit, and wait for RDY back to 0. */
342 wcb->exe = 0;
343 gettimeofday(&begin, NULL);
344 while(wcb->rdy) {
345 gettimeofday(&now, NULL);
346 /* 1 sec should be long enough for clearing rdy bit. */
347 if (((now.tv_sec - begin.tv_sec)*1000*1000 +
348 (now.tv_usec - begin.tv_usec)) >= 1000*1000) {
349 timeout += 1;
350 break;
351 }
352 }
353
354 if (err || timeout) {
355 msg_cdbg("err=%d timeout=%d\n", err, timeout);
356 return 1;
357 }
358 return 0;
359}
360
361/** Initialize the EC parameters.
David Hendricksc801adb2010-12-09 16:58:56 -0800362
David Hendricksbabd1b52010-08-19 13:16:19 -0700363 * @return 1 for error; 0 for success.
364 */
David Hendricksc801adb2010-12-09 16:58:56 -0800365static int InitFlash()
David Hendricksbabd1b52010-08-19 13:16:19 -0700366{
David Hendricksc801adb2010-12-09 16:58:56 -0800367 int i;
David Hendricksbabd1b52010-08-19 13:16:19 -0700368
David Hendricksc801adb2010-12-09 16:58:56 -0800369 if (!initflash_cfg) {
370 msg_perr("%s(): InitFlash config is not defined\n", __func__);
371 return 1;
372 }
373
374 assert_ec_is_free();
David Hendricksbabd1b52010-08-19 13:16:19 -0700375 /* Byte 3: command code: Init Flash */
376 wcb->code = 0x5A;
David Hendricksc801adb2010-12-09 16:58:56 -0800377 msg_pdbg("%s(): InitFlash bytes: ", __func__);
378 for (i = 0; i < sizeof(struct wpce775x_initflash_cfg); i++) {
379 wcb->field[i] = *((uint8_t *)initflash_cfg + i);
380 msg_pdbg("%02x ", wcb->field[i]);
381 }
382 msg_pdbg("\n");
David Hendricksbabd1b52010-08-19 13:16:19 -0700383
384 if (blocked_exec())
385 return 1;
386 return 0;
387}
388
David Hendricksc801adb2010-12-09 16:58:56 -0800389/* log2() could be used if we link with -lm */
390static int logbase2(int x)
391{
392 int log = 0;
393
394 /* naive way */
395 while (x) {
396 x >>= 1;
397 log++;
398 }
399 return log;
400}
401
402/* initialize initflash_cfg struct */
403int initflash_cfg_setup(struct flashchip *flash)
404{
405 if (!initflash_cfg)
406 initflash_cfg = malloc(sizeof(*initflash_cfg));
407
408 /* Copy flash struct pointer so that raw SPI commands that do not get
409 it passed in (e.g. called by spi_send_command) can access it. */
410 if (flash)
411 flash_internal = flash;
412
413 /* Set "sane" defaults. If the flash chip is known, then use parameters
414 from it. */
415 initflash_cfg->read_device_id = JEDEC_RDID;
416 if (flash && (flash->feature_bits | FEATURE_WRSR_WREN))
417 initflash_cfg->write_status_enable = JEDEC_WREN;
418 else if (flash && (flash->feature_bits | FEATURE_WRSR_EWSR))
419 initflash_cfg->write_status_enable = JEDEC_EWSR;
420 else
421 initflash_cfg->write_status_enable = JEDEC_WREN;
422 initflash_cfg->write_enable = JEDEC_WREN;
423 initflash_cfg->read_status_register = JEDEC_RDSR;
424 initflash_cfg->write_status_register = JEDEC_WRSR;
425 initflash_cfg->flash_program = JEDEC_BYTE_PROGRAM;
426
427 /* note: these members are likely to be overridden later */
428 initflash_cfg->block_erase = JEDEC_SE;
429 initflash_cfg->status_busy_mask = 0x01;
430 initflash_cfg->status_reg_value = 0x00;
431
432 /* back to "sane" defaults... */
433 initflash_cfg->program_unit_size = 0x01;
434 if (flash)
435 initflash_cfg->page_size = logbase2(flash->page_size);
436 else
437 initflash_cfg->page_size = 0x08;
438
439 initflash_cfg->read_device_id_type = 0x00;
440
441 return 0;
442}
443
David Hendricksbabd1b52010-08-19 13:16:19 -0700444/** Read flash vendor/device IDs through EC.
445 * @param id0, id1, id2, id3 Pointers to store detected IDs. NULL will be ignored.
446 * @return 1 for error; 0 for success.
447 */
448static int ReadId(unsigned char* id0, unsigned char* id1,
449 unsigned char* id2, unsigned char* id3)
450{
David Hendricksc801adb2010-12-09 16:58:56 -0800451 if (!initflash_cfg) {
452 initflash_cfg_setup(NULL);
453 InitFlash();
454 }
455
David Hendricksbabd1b52010-08-19 13:16:19 -0700456 assert_ec_is_free();
457
458 wcb->code = 0xC0; /* Byte 3: command code: Read ID */
459 if (blocked_exec())
460 return 1;
461
462 msg_cdbg("id0: 0x%2x, id1: 0x%2x, id2: 0x%2x, id3: 0x%2x\n",
463 wcb->field[0], wcb->field[1], wcb->field[2], wcb->field[3]);
464 if (id0) {
465 *id0 = wcb->field[0];
466 }
467 if (id1) {
468 *id1 = wcb->field[1];
469 }
470 if (id2) {
471 *id2 = wcb->field[2];
472 }
473 if (id3) {
474 *id3 = wcb->field[3];
475 }
476
477 return 0;
478}
479
David Hendricksbabd1b52010-08-19 13:16:19 -0700480/** Tell EC to "enter flash update" mode. */
David Hendricksc801adb2010-12-09 16:58:56 -0800481int EnterFlashUpdate()
David Hendricksbabd1b52010-08-19 13:16:19 -0700482{
483 if (in_flash_update_mode) {
484 /* already in update mode */
David Hendricksc801adb2010-12-09 16:58:56 -0800485 msg_pdbg("%s: in_flash_update_mode: %d\n",
486 __func__, in_flash_update_mode);
David Hendricksbabd1b52010-08-19 13:16:19 -0700487 return 0;
488 }
David Hendricksc801adb2010-12-09 16:58:56 -0800489 assert_ec_is_free();
David Hendricksbabd1b52010-08-19 13:16:19 -0700490
491 wcb->code = 0x10; /* Enter Flash Update */
492 wcb->field[0] = 0x55; /* required pattern by EC */
493 wcb->field[1] = 0xAA; /* required pattern by EC */
494 wcb->field[2] = 0xCD; /* required pattern by EC */
495 wcb->field[3] = 0xBE; /* required pattern by EC */
496 if (blocked_exec()) {
497 return 1;
498 } else {
David Hendricksc801adb2010-12-09 16:58:56 -0800499 in_flash_update_mode = 1;
David Hendricksbabd1b52010-08-19 13:16:19 -0700500 return 0;
501 }
502}
503
504/** Tell EC to "exit flash update" mode.
505 * Without calling this function, the EC stays in busy-loop and will not
506 * response further request from host, which means system will halt.
507 */
508int ExitFlashUpdate(unsigned char exit_code)
509{
David Hendricksc801adb2010-12-09 16:58:56 -0800510 /*
511 * Note: ExitFlashUpdate must be called before shutting down the
512 * machine, otherwise the EC will be stuck in update mode, leaving
513 * the machine in a "wedged" state until power cycled.
514 */
515 if (!in_flash_update_mode) {
David Hendricksbabd1b52010-08-19 13:16:19 -0700516 msg_cdbg("Not in flash update mode yet.\n");
517 return 1;
518 }
519
David Hendricksbabd1b52010-08-19 13:16:19 -0700520 wcb->code = exit_code; /* Exit Flash Update */
521 if (blocked_exec()) {
522 return 1;
David Hendricksbabd1b52010-08-19 13:16:19 -0700523 }
David Hendricksc801adb2010-12-09 16:58:56 -0800524
525 in_flash_update_mode = 0;
526 return 0;
David Hendricksbabd1b52010-08-19 13:16:19 -0700527}
528
529/*
530 * Note: The EC firmware this patch has been tested with uses the following
531 * codes to indicate flash update status:
532 * 0x20 is used for EC F/W no change, but BIOS changed (in Share mode)
533 * 0x21 is used for EC F/W changed. Goto EC F/W, wait system reboot.
534 * 0x22 is used for EC F/W changed, Goto EC Watchdog reset. */
535int ExitFlashUpdateFirmwareNoChange(void) {
536 return ExitFlashUpdate(0x20);
537}
538
539int ExitFlashUpdateFirmwareChanged(void) {
540 return ExitFlashUpdate(0x21);
541}
542
David Hendricksc801adb2010-12-09 16:58:56 -0800543int wpce775x_spi_common_init(void)
David Hendricksbabd1b52010-08-19 13:16:19 -0700544{
David Hendricksc801adb2010-12-09 16:58:56 -0800545 uint16_t sio_port;
546 uint8_t srid;
547 uint8_t fwh_id;
548
549 msg_pdbg("%s(): entered\n", __func__);
550
551 /* detect if wpce775x exists */
552 if (nuvoton_get_sio_index(&sio_port) < 0) {
553 msg_pdbg("No Nuvoton chip is found.\n");
554 return 0;
555 }
556 srid = sio_read(sio_port, NUVOTON_SIOCFG_SRID);
557 if ((srid & 0xE0) == 0xA0) {
558 msg_pdbg("Found EC: WPCE775x (Vendor:0x%02x,ID:0x%02x,Rev:0x%02x) on sio_port:0x%x.\n",
559 sio_read(sio_port, NUVOTON_SIOCFG_SID),
560 srid >> 5, srid & 0x1f, sio_port);
561
562 } else {
563 msg_pdbg("Found EC: Nuvoton (Vendor:0x%02x,ID:0x%02x,Rev:0x%02x) on sio_port:0x%x.\n",
564 sio_read(sio_port, NUVOTON_SIOCFG_SID),
565 srid >> 5, srid & 0x1f, sio_port);
566 }
567
568 /* get the address of Shadow Window 2. */
569 if (get_shaw2ba(&wcb_physical_address) < 0) {
570 msg_pdbg("Cannot get the address of Shadow Window 2");
571 return 0;
572 }
573 msg_pdbg("Get the address of WCB(SHA WIN2) at 0x%08x\n",
574 (uint32_t)wcb_physical_address);
575 wcb = (struct wpce775x_wcb *)
576 programmer_map_flash_region("WPCE775X WCB",
577 wcb_physical_address,
578 getpagesize() /* min page size */);
579 msg_pdbg("mapped wcb address: %p for physical addr: 0x%08lx\n", wcb, wcb_physical_address);
580 if (!wcb) {
581 msg_perr("FATAL! Cannot map memory area for wcb physical address.\n");
582 return 0;
583 }
584 memset((void*)wcb, 0, sizeof(*wcb));
585
586 if (get_fwh_id(&fwh_id) < 0) {
587 msg_pdbg("Cannot get fwh_id value.\n");
588 return 0;
589 }
590 msg_pdbg("get fwh_id: 0x%02x\n", fwh_id);
591
592 /* TODO: set fwh_idsel of chipset.
593 Currently, we employ "-p internal:fwh_idsel=0x0000223e". */
594
595 /* Enter flash update mode unconditionally. This is required even
596 for reading. */
597 if (EnterFlashUpdate()) return 1;
598
599 spi_controller = SPI_CONTROLLER_WPCE775X;
600 msg_pdbg("%s(): successfully initialized wpce775x\n", __func__);
601 return 0;
602
603}
604
605int wpce775x_shutdown(void)
606{
607 if (spi_controller != SPI_CONTROLLER_WPCE775X)
608 return 0;
609
610 msg_pdbg("%s(): firmware %s\n", __func__,
611 firmware_changed ? "changed" : "not changed");
612
613 msg_pdbg("%s: in_flash_update_mode: %d\n", __func__, in_flash_update_mode);
614 if (in_flash_update_mode) {
615 if (firmware_changed)
616 ExitFlashUpdateFirmwareChanged();
617 else
618 ExitFlashUpdateFirmwareNoChange();
619
620 in_flash_update_mode = 0;
621 }
622
623 if (initflash_cfg)
624 free(initflash_cfg);
625 else
626 msg_perr("%s(): No initflash_cfg to free?!?\n", __func__);
627
628 return 0;
629}
630
631/* Called by internal_init() */
632int wpce775x_probe_spi_flash(const char *name)
633{
634 int ret;
635
636 if (!(buses_supported & CHIP_BUSTYPE_FWH)) {
637 msg_pdbg("%s():%d buses not support FWH\n", __func__, __LINE__);
638 return 1;
639 }
640 ret = wpce775x_spi_common_init();
641 msg_pdbg("FWH: %s():%d ret=%d\n", __func__, __LINE__, ret);
642 if (!ret) {
643 msg_pdbg("%s():%d buses_supported=0x%x\n", __func__, __LINE__,
644 buses_supported);
645 if (buses_supported & CHIP_BUSTYPE_FWH)
646 msg_pdbg("Overriding chipset SPI with WPCE775x FWH|SPI.\n");
647 buses_supported |= CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_SPI;
648 }
649 return ret;
650}
651
652int wpce775x_read(int addr, unsigned char *buf, unsigned int nbytes)
653{
654 int offset;
655 unsigned int bytes_read = 0;
656
David Hendricksbabd1b52010-08-19 13:16:19 -0700657 assert_ec_is_free();
David Hendricksc801adb2010-12-09 16:58:56 -0800658 msg_pspew("%s: reading %d bytes at 0x%06x\n", __func__, nbytes, addr);
659
660 /* Set initial address; WPCE775x auto-increments address for successive
661 read and write operations. */
662 wcb->code = 0xA0;
663 wcb->field[0] = addr & 0xff;
664 wcb->field[1] = (addr >> 8) & 0xff;
665 wcb->field[2] = (addr >> 16) & 0xff;
666 wcb->field[3] = (addr >> 24) & 0xff;
667 if (blocked_exec()) {
668 return 1;
669 }
670
671 for (offset = 0;
672 offset < nbytes;
673 offset += bytes_read) {
674 int i;
675 unsigned int bytes_left;
676
677 bytes_left = nbytes - offset;
678 if (bytes_left > 0 && bytes_left < WPCE775X_MAX_READ_SIZE)
679 bytes_read = bytes_left;
680 else
681 bytes_read = WPCE775X_MAX_READ_SIZE;
682 wcb->code = 0xD0 | bytes_read;
683 if (blocked_exec()) {
684 return 1;
685 }
686
687 for (i = 0; i < bytes_read; i++)
688 buf[offset + i] = wcb->field[i];
689 }
690
691 return 0;
692}
693
694int wpce775x_erase_new(int blockaddr, uint8_t opcode) {
695 unsigned int current;
696 int blocksize;
697 int ret = 0;
698
699 assert_ec_is_free();
700
701 /*
702 * FIXME: In the long-run we should examine block_erasers within the
703 * flash struct to ensure the proper blocksize is used. This is because
704 * some chips implement commands differently. For now, we'll support
705 * only a few "safe" block erase commands with predictable block size.
706 *
707 * Looking thru the list of flashchips, it seems JEDEC_BE_52 and
708 * JEDEC_BE_D8 are not uniformly implemented. Thus, we cannot safely
709 * assume a blocksize.
710 *
711 * Also, I was unable to test chip erase (due to equipment and time
712 * constraints), but they might work.
713 */
714 switch(opcode) {
715 case JEDEC_SE:
716 case JEDEC_BE_D7:
717 blocksize = 4 * 1024;
718 break;
719 case JEDEC_BE_52:
720 case JEDEC_BE_D8:
721 case JEDEC_CE_60:
722 case JEDEC_CE_C7:
723 default:
724 msg_perr("%s(): erase opcode=0x%02x not supported\n",
725 __func__, opcode);
726 return 1;
727 }
728
729 msg_pspew("%s(): blockaddr=%d, blocksize=%d, opcode=0x%02x\n",
730 __func__, blockaddr, blocksize, opcode);
731
732 if (!initflash_cfg)
733 initflash_cfg_setup(flash_internal);
734 initflash_cfg->block_erase = opcode;
735 InitFlash();
David Hendricksbabd1b52010-08-19 13:16:19 -0700736
737 /* Set Write Window on flash chip (optional).
738 * You may limit the window to partial flash for experimental. */
739 wcb->code = 0xC5; /* Set Write Window */
740 wcb->field[0] = 0x00; /* window base: little-endian */
741 wcb->field[1] = 0x00;
742 wcb->field[2] = 0x00;
743 wcb->field[3] = 0x00;
744 wcb->field[4] = 0x00; /* window length: little-endian */
745 wcb->field[5] = 0x00;
746 wcb->field[6] = 0x20;
747 wcb->field[7] = 0x00;
748 if (blocked_exec())
749 return 1;
750
David Hendricksc801adb2010-12-09 16:58:56 -0800751 msg_pspew("Erasing ... 0x%08x 0x%08x\n", blockaddr, blocksize);
David Hendricksbabd1b52010-08-19 13:16:19 -0700752
753 for (current = 0;
David Hendricksc801adb2010-12-09 16:58:56 -0800754 current < blocksize;
755 current += blocksize) {
David Hendricksbabd1b52010-08-19 13:16:19 -0700756 wcb->code = 0x80; /* Sector/block erase */
757
David Hendricksc801adb2010-12-09 16:58:56 -0800758 /* WARNING: assume the block address for EC is always little-endian. */
David Hendricksbabd1b52010-08-19 13:16:19 -0700759 unsigned int addr = blockaddr + current;
760 wcb->field[0] = addr & 0xff;
761 wcb->field[1] = (addr >> 8) & 0xff;
762 wcb->field[2] = (addr >> 16) & 0xff;
763 wcb->field[3] = (addr >> 24) & 0xff;
David Hendricksc801adb2010-12-09 16:58:56 -0800764 if (blocked_exec()) {
765 ret = 1;
766 goto wpce775x_erase_new_exit;
767 }
David Hendricksbabd1b52010-08-19 13:16:19 -0700768 }
769
David Hendricksc801adb2010-12-09 16:58:56 -0800770wpce775x_erase_new_exit:
771 firmware_changed = 1;
772 return ret;
773}
David Hendricksbabd1b52010-08-19 13:16:19 -0700774
David Hendricksc801adb2010-12-09 16:58:56 -0800775int wpce775x_nbyte_program(int addr, const unsigned char *buf,
776 unsigned int nbytes)
777{
778 int offset, ret = 0;
779 unsigned int written = 0;
780
781 assert_ec_is_free();
782 msg_pspew("%s: writing %d bytes to 0x%06x\n", __func__, nbytes, addr);
783
784 /* Set initial address; WPCE775x auto-increments address for successive
785 read and write operations. */
786 wcb->code = 0xA0;
787 wcb->field[0] = addr & 0xff;
788 wcb->field[1] = (addr >> 8) & 0xff;
789 wcb->field[2] = (addr >> 16) & 0xff;
790 wcb->field[3] = (addr >> 24) & 0xff;
791 if (blocked_exec()) {
David Hendricksbabd1b52010-08-19 13:16:19 -0700792 return 1;
793 }
794
David Hendricksc801adb2010-12-09 16:58:56 -0800795 for (offset = 0;
796 offset < nbytes;
797 offset += written) {
David Hendricksbabd1b52010-08-19 13:16:19 -0700798 int i;
David Hendricksc801adb2010-12-09 16:58:56 -0800799 unsigned int bytes_left;
800
801 bytes_left = nbytes - offset;
802 if (bytes_left > 0 && bytes_left < WPCE775X_MAX_WRITE_SIZE)
803 written = bytes_left;
804 else
805 written = WPCE775X_MAX_WRITE_SIZE;
806 wcb->code = 0xB0 | written;
807
808 for (i = 0; i < written; i++)
809 wcb->field[i] = buf[offset + i];
810 if (blocked_exec()) {
811 ret = 1;
812 goto wpce775x_nbyte_program_exit;
David Hendricksbabd1b52010-08-19 13:16:19 -0700813 }
David Hendricksbabd1b52010-08-19 13:16:19 -0700814 }
815
David Hendricksc801adb2010-12-09 16:58:56 -0800816wpce775x_nbyte_program_exit:
817 firmware_changed = 1;
818 return ret;
David Hendricksbabd1b52010-08-19 13:16:19 -0700819}
820
David Hendricksc801adb2010-12-09 16:58:56 -0800821int wpce775x_spi_read(struct flashchip *flash, uint8_t * buf, int start, int len)
David Hendricksbabd1b52010-08-19 13:16:19 -0700822{
David Hendricksc801adb2010-12-09 16:58:56 -0800823 if (!initflash_cfg) {
824 initflash_cfg_setup(flash);
825 InitFlash();
826 }
827 return spi_read_chunked(flash, buf, start, len, flash->page_size);
David Hendricksbabd1b52010-08-19 13:16:19 -0700828}
David Hendricks21e06302010-09-03 14:51:46 -0700829
David Hendricksc801adb2010-12-09 16:58:56 -0800830int wpce775x_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
831{
832 if (!initflash_cfg) {
833 initflash_cfg_setup(flash);
834 InitFlash();
835 }
836 return spi_write_chunked(flash, buf, start, len, flash->page_size);
837}
David Hendricks21e06302010-09-03 14:51:46 -0700838
Louis Yung-Chieh Loa915bb12011-03-30 09:47:40 +0800839int wpce775x_spi_read_status_register(unsigned int readcnt, uint8_t *readarr)
840{
Louis Yung-Chieh Lof6f03c42011-03-30 17:24:49 +0800841 uint8_t before[2];
Louis Yung-Chieh Loa915bb12011-03-30 09:47:40 +0800842 int ret = 0;
843 int i;
844
845 assert_ec_is_free();
846 msg_pdbg("%s(): reading status register.\n", __func__);
847
Louis Yung-Chieh Lof6f03c42011-03-30 17:24:49 +0800848 /* We need to detect if EC firmware support this 0x30 command.
849 * 1. write a non-sense value to field[0] and field[1].
850 * 2. execute command 0x30.
851 * 3. if field[0] is NOT changed, that means 0x30 is not supported,
852 * use initflash_cfg->status_reg_value instead.
853 * else, 0x30 works and returns field[].
854 */
855 wcb->field[0] = ~initflash_cfg->status_reg_value;
856 wcb->field[1] = 0xfc; /* set reserved bits to 1s */
857 /* save original values */
858 for (i = 0; i < ARRAY_SIZE(before); i++)
859 before[i] = wcb->field[i];
860
Louis Yung-Chieh Loa915bb12011-03-30 09:47:40 +0800861 wcb->code = 0x30; /* JEDEC_RDSR */
862 if (blocked_exec()) {
863 ret = 1;
Louis Yung-Chieh Lof6f03c42011-03-30 17:24:49 +0800864 msg_perr("%s(): blocked_exec() returns error.\n", __func__);
Louis Yung-Chieh Loa915bb12011-03-30 09:47:40 +0800865 }
866 msg_pdbg("%s(): WCB code=0x%02x field[]= ", __func__, wcb->code);
867 for (i = 0; i < readcnt; ++i) {
868 readarr[i] = wcb->field[i];
869 msg_pdbg("%02x ", wcb->field[i]);
870 }
871 msg_pdbg("\n");
Louis Yung-Chieh Lof6f03c42011-03-30 17:24:49 +0800872
873 /* FIXME: not sure EC returns 1 or 2 bytes for command 0x30,
874 * now we check field[0] only.
875 * Shall check field[1] if EC always returns 2 bytes. */
876 if (wcb->field[0] == before[0]) {
877 /* field is not changed, 0x30 command doesn't work. */
878 readarr[0] = initflash_cfg->status_reg_value;
879 readarr[1] = 0; /* TODO: if second status register exists */
880 msg_pdbg("%s(): command 0x30 seems NOT working.\n", __func__);
881 } else {
882 /* 0x30 command seems working! */
883 initflash_cfg->status_reg_value = readarr[0];
884 msg_pdbg("%s(): command 0x30 seems working.\n", __func__);
885 }
Louis Yung-Chieh Loa915bb12011-03-30 09:47:40 +0800886
887 return ret;
888}
889
David Hendricksc801adb2010-12-09 16:58:56 -0800890int wpce775x_spi_write_status_register(uint8_t val)
891{
892 assert_ec_is_free();
893 msg_pdbg("%s(): writing 0x%02x to status register\n", __func__, val);
David Hendricks21e06302010-09-03 14:51:46 -0700894
David Hendricksc801adb2010-12-09 16:58:56 -0800895 if (!initflash_cfg)
896 initflash_cfg_setup(flash_internal);
David Hendricks21e06302010-09-03 14:51:46 -0700897
David Hendricksc801adb2010-12-09 16:58:56 -0800898 initflash_cfg->status_reg_value = val;
899 if (in_flash_update_mode) {
900 ExitFlashUpdateFirmwareNoChange();
901 in_flash_update_mode = 0;
902 }
903 if (InitFlash())
904 return 1;
Louis Yung-Chieh Lo623809c2010-11-30 15:43:18 +0800905 if (EnterFlashUpdate())
David Hendricksc801adb2010-12-09 16:58:56 -0800906 return 1;
Louis Yung-Chieh Lo623809c2010-11-30 15:43:18 +0800907 ExitFlashUpdateFirmwareNoChange();
David Hendricks21e06302010-09-03 14:51:46 -0700908 return 0;
909}
910
David Hendricksc801adb2010-12-09 16:58:56 -0800911/*
912 * WPCE775x does not allow direct access to SPI chip from host. This function
913 * will translate SPI commands to valid WPCE775x WCB commands.
Louis Yung-Chieh Lo623809c2010-11-30 15:43:18 +0800914 */
David Hendricksc801adb2010-12-09 16:58:56 -0800915int wpce775x_spi_send_command(unsigned int writecnt, unsigned int readcnt,
916 const unsigned char *writearr, unsigned char *readarr)
David Hendricks21e06302010-09-03 14:51:46 -0700917{
David Hendricksc801adb2010-12-09 16:58:56 -0800918 int rc = 0;
919 uint8_t opcode = writearr[0];
920
921 switch(opcode){
922 case JEDEC_RDID:{
923 unsigned char dummy = 0;
924 if (readcnt == 3)
925 ReadId(&readarr[0], &readarr[1], &readarr[2], &dummy);
926 else if (readcnt == 4)
927 ReadId(&readarr[0], &readarr[1], &readarr[2], &readarr[3]);
928 break;
929 }
930 case JEDEC_RDSR:
Louis Yung-Chieh Loa915bb12011-03-30 09:47:40 +0800931 rc = wpce775x_spi_read_status_register(readcnt, readarr);
David Hendricksc801adb2010-12-09 16:58:56 -0800932 break;
933 case JEDEC_READ:{
934 int blockaddr = (writearr[1] << 16) |
935 (writearr[2] << 8) |
936 writearr[3];
937 rc = wpce775x_read(blockaddr, readarr, readcnt);
938 break;
939 }
940 case JEDEC_WRSR:
941 wpce775x_spi_write_status_register(writearr[1]);
942 rc = 0;
943 break;
944 case JEDEC_WREN:
945 case JEDEC_EWSR:
946 /* Handled by InitFlash() */
947 rc = 0;
948 break;
949 case JEDEC_SE:
950 case JEDEC_BE_52:
951 case JEDEC_BE_D7:
952 case JEDEC_BE_D8:
953 case JEDEC_CE_60:
954 case JEDEC_CE_C7:{
955 int blockaddr = (writearr[1] << 16) |
956 (writearr[2] << 8) |
957 writearr[3];
958
959 rc = wpce775x_erase_new(blockaddr, opcode);
960 break;
961 }
962 case JEDEC_BYTE_PROGRAM:{
963 int blockaddr = (writearr[1] << 16) |
964 (writearr[2] << 8) |
965 writearr[3];
966 int nbytes = writecnt - 4;
967
968 rc = wpce775x_nbyte_program(blockaddr, &writearr[4], nbytes);
969 break;
970 }
971 case JEDEC_REMS:
972 case JEDEC_RES:
973 case JEDEC_WRDI:
974 case JEDEC_AAI_WORD_PROGRAM:
975 default:
976 /* unsupported opcodes */
977 msg_pdbg("unsupported SPI opcode: %02x\n", opcode);
978 rc = 1;
979 break;
980 }
981
982 msg_pdbg("%s: opcode: 0x%02x\n", __func__, opcode);
983 return rc;
Louis Yung-Chieh Lo623809c2010-11-30 15:43:18 +0800984}
David Hendrickscb2cec32011-03-24 18:44:49 -0700985#endif