blob: 264f3fe4e97fdf5b7e9b8e99d7182638aad9bbff [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
41#include <assert.h>
42#include <string.h>
43#include <sys/time.h>
44#include <time.h>
45#include <unistd.h>
David Hendricksc801adb2010-12-09 16:58:56 -080046#include <stdlib.h>
David Hendricksbabd1b52010-08-19 13:16:19 -070047#include "flash.h"
48#include "chipdrivers.h"
49#include "flashchips.h"
50#include "programmer.h"
51#include "spi.h"
David Hendricks21e06302010-09-03 14:51:46 -070052#include "writeprotect.h"
David Hendricksbabd1b52010-08-19 13:16:19 -070053
54/**
55 * Definition of WPCE775X WCB (Write Command Buffer), as known as Shared Access
56 * Window 2.
57 *
58 * The document name is "WPCE775X Software User Guide Revision 1.2".
59 *
60 * Assume the host is little endian.
61 */
62__attribute__((packed))
63struct wpce775x_wcb {
64 /* Byte 0: semaphore byte */
65 unsigned char exe:1; /* Bit0-RW- set by host. means wcb is ready to execute.
66 should be cleared by host after RDY=1. */
67 unsigned char resv0_41:4;
68 unsigned char pcp:1; /* Bit5-RO- set by EPCE775x. means preparation operations for
69 flash update process is complete. */
70 unsigned char err:1; /* Bit6-RO- set by EPCE775x. means an error occurs. */
71 unsigned char rdy:1; /* Bit7-RO- set by EPCE775x. means operation is completed. */
72
73 /* Byte 1-2: reserved */
74 unsigned char byte1;
75 unsigned char byte2;
76
77 /* Byte 3: command code */
78 unsigned char code;
79
80 /* Byte 4-15: command field */
81 unsigned char field[12];
82};
83
84/* The physical address of WCB -- Shared Access Window 2. */
85static chipaddr wcb_physical_address;
86
87/* The virtual address of WCB -- Shared Access Window 2. */
88static volatile struct wpce775x_wcb *volatile wcb;
89
90/* count of entering flash update mode */
91static int in_flash_update_mode;
92
David Hendricksc801adb2010-12-09 16:58:56 -080093static int firmware_changed;
94
95/*
96 * Bytes 0x4-0xf of InitFlash command. These represent opcodes and various
97 * parameters the WPCE775x will use when communicating with the SPI flash
98 * device. DO NOT RE-ORDER THIS STRUCTURE.
99 */
100struct wpce775x_initflash_cfg {
101 uint8_t read_device_id; /* Byte 0x04. Ex: JEDEC_RDID */
102 uint8_t write_status_enable; /* Byte 0x05. Ex: JEDEC_EWSR */
103 uint8_t write_enable; /* Byte 0x06. Ex: JEDEC_WREN */
104 uint8_t read_status_register; /* Byte 0x07. Ex: JEDEC_RDSR */
105 uint8_t write_status_register; /* Byte 0x08. Ex: JEDEC_WRSR */
106 uint8_t flash_program; /* Byte 0x09. Ex: JEDEC_BYTE_PROGRAM */
107
108 /* Byte 0x0A. Ex: sector/block/chip erase opcode */
109 uint8_t block_erase;
110
111 uint8_t status_busy_mask; /* Byte B: bit position of BUSY bit */
112
113 /* Byte 0x0C: value to remove write protection */
114 uint8_t status_reg_value;
115
116 /* Byte 0x0D: Number of bytes to program in each write transaction. */
117 uint8_t program_unit_size;
118
119 uint8_t page_size; /* Byte 0x0E: 2^n bytes */
120
121 /*
122 * Byte 0x0F: Method to read device ID. 0x47 will cause ID bytes to be
123 * read immediately after read_device_id command is issued. Otherwise,
124 * 3 dummy address bytes are sent after the read_device_id code.
125 */
126 uint8_t read_device_id_type;
127} __attribute__((packed));
128
129/*
130 * The WPCE775x can use InitFlash multiple times during an update. We'll use
131 * this ability primarily for changing write protection bits.
132 */
133static struct wpce775x_initflash_cfg *initflash_cfg;
134
135static struct flashchip *flash_internal;
136
137
David Hendricksbabd1b52010-08-19 13:16:19 -0700138/* Indicate the flash chip attached to the WPCE7xxx chip.
139 * This variable should be set in probe_wpce775x().
140 * 0 means we haven't or cannot detect the chip type. */
141struct flashchip *scan = 0;
142
143/* SuperI/O related definitions and functions. */
144/* Strapping options */
145#define NUVOTON_SIO_PORT1 0x2e /* No pull-down resistor */
146#define NUVOTON_SIO_PORT2 0x164e /* Pull-down resistor on BADDR0 */
147/* Note: There's another funky state that we won't worry about right now */
148
149/* SuperI/O Config */
150#define NUVOTON_SIOCFG_LDN 0x07 /* LDN Bank Selector */
151#define NUVOTON_SIOCFG_SID 0x20 /* SuperI/O ID */
152#define NUVOTON_SIOCFG_SRID 0x27 /* SuperI/O Revision ID */
153#define NUVOTON_LDN_SHM 0x0f /* LDN of SHM module */
154
155/* WPCE775x shared memory config registers (LDN 0x0f) */
156#define WPCE775X_SHM_BASE_MSB 0x60
157#define WPCE775X_SHM_BASE_LSB 0x61
158#define WPCE775X_SHM_CFG 0xf0
159#define WPCE775X_SHM_CFG_BIOS_FWH_EN (1 << 3)
160#define WPCE775X_SHM_CFG_FLASH_ACC_EN (1 << 2)
161#define WPCE775X_SHM_CFG_BIOS_EXT_EN (1 << 1)
162#define WPCE775X_SHM_CFG_BIOS_LPC_EN (1 << 0)
163#define WPCE775X_WIN_CFG 0xf1 /* window config */
164#define WPCE775X_WIN_CFG_SHWIN_ACC (1 << 6)
165
166/* Shared access window 2 bar address registers */
167#define WPCE775X_SHAW2BA_0 0xf8
168#define WPCE775X_SHAW2BA_1 0xf9
169#define WPCE775X_SHAW2BA_2 0xfa
170#define WPCE775X_SHAW2BA_3 0xfb
171
David Hendricksc801adb2010-12-09 16:58:56 -0800172/* Read/write buffer size */
173#define WPCE775X_MAX_WRITE_SIZE 8
174#define WPCE775X_MAX_READ_SIZE 12
175
David Hendricksbabd1b52010-08-19 13:16:19 -0700176/** probe for super i/o index
177 * @returns 0 to indicate success, <0 to indicate error
178 */
179static int nuvoton_get_sio_index(uint16_t *port)
180{
181 uint16_t ports[] = { NUVOTON_SIO_PORT2,
182 NUVOTON_SIO_PORT1,
183 };
184 int i;
185 static uint16_t port_internal, port_found = 0;
186
187 if (port_found) {
188 *port = port_internal;
189 return 0;
190 }
191
192 get_io_perms();
193
194 for (i = 0; i < ARRAY_SIZE(ports); i++) {
195 uint8_t sid = sio_read(ports[i], NUVOTON_SIOCFG_SID);
196
197 if (sid == 0xfc) { /* Family ID */
198 port_internal = ports[i];
199 port_found = 1;
200 break;
201 }
202 }
203
204 if (!port_found) {
205 msg_cdbg("\nfailed to obtain super i/o index");
206 return -1;
207 }
208
209 msg_cdbg("\nsuper i/o index = 0x%04x\n", port_internal);
210 *port = port_internal;
211 return 0;
212}
213
214/** Call superio to get pre-configured WCB address.
215 * Read LDN 0x0f (SHM) idx:f8-fb (little-endian).
216 */
217static int get_shaw2ba(chipaddr *shaw2ba)
218{
219 uint16_t idx;
220 uint8_t org_ldn;
221 uint8_t win_cfg;
222 uint8_t shm_cfg;
223
224 if (nuvoton_get_sio_index(&idx) < 0)
225 return -1;
226
227 org_ldn = sio_read(idx, NUVOTON_SIOCFG_LDN);
228 sio_write(idx, NUVOTON_SIOCFG_LDN, NUVOTON_LDN_SHM);
229
230 /*
231 * To obtain shared access window 2 base address, we must OR the base
232 * address bytes, where SHAW2BA_0 is least significant and SHAW2BA_3
233 * most significant.
234 */
235 *shaw2ba = sio_read(idx, WPCE775X_SHAW2BA_0) |
236 (sio_read(idx, WPCE775X_SHAW2BA_1) << 8) |
237 (sio_read(idx, WPCE775X_SHAW2BA_2) << 16) |
238 (sio_read(idx, WPCE775X_SHAW2BA_3) << 24);
239
240 /*
241 * If SHWIN_ACC is cleared, then we're using LPC memory access
242 * and SHAW2BA_3-0 indicate bits 31-0. If SHWIN_ACC is set, then
243 * bits 7-4 of SHAW2BA_3 are ignored and bits 31-28 are indicated
244 * by the idsel nibble. (See table 25 "supported host address ranges"
245 * for more details)
246 */
247 win_cfg = sio_read(idx, WPCE775X_WIN_CFG);
248 if (win_cfg & WPCE775X_WIN_CFG_SHWIN_ACC) {
249 uint8_t idsel;
250
251 /* Make sure shared BIOS memory is enabled */
252 shm_cfg = sio_read(idx, WPCE775X_SHM_CFG);
253 if ((shm_cfg & WPCE775X_SHM_CFG_BIOS_FWH_EN))
254 idsel = 0xf;
255 else {
256 msg_cdbg("Shared BIOS memory is diabled.\n");
257 msg_cdbg("Please check SHM_CFG:BIOS_FWH_EN.\n");
258 goto error;
259 }
260
261 *shaw2ba &= 0x0fffffff;
262 *shaw2ba |= idsel << 28;
263 }
264
265 sio_write(idx, NUVOTON_SIOCFG_LDN, org_ldn);
266 return 0;
267error:
268 sio_write(idx, NUVOTON_SIOCFG_LDN, org_ldn);
269 return -1;
270}
271
272/* Call superio to get pre-configured fwh_id.
273 * Read LDN 0x0f (SHM) idx:f0.
274 */
275static int get_fwh_id(uint8_t *fwh_id)
276{
277 uint16_t idx;
278 uint8_t org_ldn;
279
280 if (nuvoton_get_sio_index(&idx) < 0)
281 return -1;
282
283 org_ldn = sio_read(idx, NUVOTON_SIOCFG_LDN);
284 sio_write(idx, NUVOTON_SIOCFG_LDN, NUVOTON_LDN_SHM);
285 *fwh_id = sio_read(idx, WPCE775X_SHM_CFG);
286 sio_write(idx, NUVOTON_SIOCFG_LDN, org_ldn);
287
288 return 0;
289}
290
291/** helper function to make sure the exe bit is 0 (no one is using EC).
292 * @return 1 for error; 0 for success.
293 */
294static int assert_ec_is_free(void)
295{
296 if (wcb->exe)
297 msg_perr("ASSERT(wcb->exe==0), entering busy loop.\n");
298 while(wcb->exe);
299 return 0;
300}
301
302/** Trigger EXE bit, and block until operation completes.
303 * @return 1 for error; and 0 for success.
304 */
305static int blocked_exec(void)
306{
307 struct timeval begin, now;
308 int timeout; /* not zero if timeout occurs */
309 int err;
310
311 assert(wcb->rdy==0);
312
313 /* raise EXE bit, and wait for operation complete or error occur. */
314 wcb->exe = 1;
315
316 timeout = 0;
317 gettimeofday(&begin, NULL);
318 while(wcb->rdy==0 && wcb->err==0) {
319 gettimeofday(&now, NULL);
320 /* According to Nuvoton's suggestion, few seconds is enough for
321 * longest flash operation, which is erase.
322 * Cutted from W25X16 datasheet, for max operation time
323 * Byte program tBP1 50us
324 * Page program tPP 3ms
325 * Sector Erase (4KB) tSE 200ms
326 * Block Erase (64KB) tBE 1s
327 * Chip Erase tCE 20s
328 * Since WPCE775x doesn't support chip erase,
329 * 3 secs is long enough for block erase.
330 */
331 if ((now.tv_sec - begin.tv_sec) >= 4) {
332 timeout += 1;
333 break;
334 }
335 }
336
337 /* keep ERR bit before clearing EXE bit. */
338 err = wcb->err;
339
340 /* Clear EXE bit, and wait for RDY back to 0. */
341 wcb->exe = 0;
342 gettimeofday(&begin, NULL);
343 while(wcb->rdy) {
344 gettimeofday(&now, NULL);
345 /* 1 sec should be long enough for clearing rdy bit. */
346 if (((now.tv_sec - begin.tv_sec)*1000*1000 +
347 (now.tv_usec - begin.tv_usec)) >= 1000*1000) {
348 timeout += 1;
349 break;
350 }
351 }
352
353 if (err || timeout) {
354 msg_cdbg("err=%d timeout=%d\n", err, timeout);
355 return 1;
356 }
357 return 0;
358}
359
360/** Initialize the EC parameters.
David Hendricksc801adb2010-12-09 16:58:56 -0800361
David Hendricksbabd1b52010-08-19 13:16:19 -0700362 * @return 1 for error; 0 for success.
363 */
David Hendricksc801adb2010-12-09 16:58:56 -0800364static int InitFlash()
David Hendricksbabd1b52010-08-19 13:16:19 -0700365{
David Hendricksc801adb2010-12-09 16:58:56 -0800366 int i;
David Hendricksbabd1b52010-08-19 13:16:19 -0700367
David Hendricksc801adb2010-12-09 16:58:56 -0800368 if (!initflash_cfg) {
369 msg_perr("%s(): InitFlash config is not defined\n", __func__);
370 return 1;
371 }
372
373 assert_ec_is_free();
David Hendricksbabd1b52010-08-19 13:16:19 -0700374 /* Byte 3: command code: Init Flash */
375 wcb->code = 0x5A;
David Hendricksc801adb2010-12-09 16:58:56 -0800376 msg_pdbg("%s(): InitFlash bytes: ", __func__);
377 for (i = 0; i < sizeof(struct wpce775x_initflash_cfg); i++) {
378 wcb->field[i] = *((uint8_t *)initflash_cfg + i);
379 msg_pdbg("%02x ", wcb->field[i]);
380 }
381 msg_pdbg("\n");
David Hendricksbabd1b52010-08-19 13:16:19 -0700382
383 if (blocked_exec())
384 return 1;
385 return 0;
386}
387
David Hendricksc801adb2010-12-09 16:58:56 -0800388/* log2() could be used if we link with -lm */
389static int logbase2(int x)
390{
391 int log = 0;
392
393 /* naive way */
394 while (x) {
395 x >>= 1;
396 log++;
397 }
398 return log;
399}
400
401/* initialize initflash_cfg struct */
402int initflash_cfg_setup(struct flashchip *flash)
403{
404 if (!initflash_cfg)
405 initflash_cfg = malloc(sizeof(*initflash_cfg));
406
407 /* Copy flash struct pointer so that raw SPI commands that do not get
408 it passed in (e.g. called by spi_send_command) can access it. */
409 if (flash)
410 flash_internal = flash;
411
412 /* Set "sane" defaults. If the flash chip is known, then use parameters
413 from it. */
414 initflash_cfg->read_device_id = JEDEC_RDID;
415 if (flash && (flash->feature_bits | FEATURE_WRSR_WREN))
416 initflash_cfg->write_status_enable = JEDEC_WREN;
417 else if (flash && (flash->feature_bits | FEATURE_WRSR_EWSR))
418 initflash_cfg->write_status_enable = JEDEC_EWSR;
419 else
420 initflash_cfg->write_status_enable = JEDEC_WREN;
421 initflash_cfg->write_enable = JEDEC_WREN;
422 initflash_cfg->read_status_register = JEDEC_RDSR;
423 initflash_cfg->write_status_register = JEDEC_WRSR;
424 initflash_cfg->flash_program = JEDEC_BYTE_PROGRAM;
425
426 /* note: these members are likely to be overridden later */
427 initflash_cfg->block_erase = JEDEC_SE;
428 initflash_cfg->status_busy_mask = 0x01;
429 initflash_cfg->status_reg_value = 0x00;
430
431 /* back to "sane" defaults... */
432 initflash_cfg->program_unit_size = 0x01;
433 if (flash)
434 initflash_cfg->page_size = logbase2(flash->page_size);
435 else
436 initflash_cfg->page_size = 0x08;
437
438 initflash_cfg->read_device_id_type = 0x00;
439
440 return 0;
441}
442
David Hendricksbabd1b52010-08-19 13:16:19 -0700443/** Read flash vendor/device IDs through EC.
444 * @param id0, id1, id2, id3 Pointers to store detected IDs. NULL will be ignored.
445 * @return 1 for error; 0 for success.
446 */
447static int ReadId(unsigned char* id0, unsigned char* id1,
448 unsigned char* id2, unsigned char* id3)
449{
David Hendricksc801adb2010-12-09 16:58:56 -0800450 if (!initflash_cfg) {
451 initflash_cfg_setup(NULL);
452 InitFlash();
453 }
454
David Hendricksbabd1b52010-08-19 13:16:19 -0700455 assert_ec_is_free();
456
457 wcb->code = 0xC0; /* Byte 3: command code: Read ID */
458 if (blocked_exec())
459 return 1;
460
461 msg_cdbg("id0: 0x%2x, id1: 0x%2x, id2: 0x%2x, id3: 0x%2x\n",
462 wcb->field[0], wcb->field[1], wcb->field[2], wcb->field[3]);
463 if (id0) {
464 *id0 = wcb->field[0];
465 }
466 if (id1) {
467 *id1 = wcb->field[1];
468 }
469 if (id2) {
470 *id2 = wcb->field[2];
471 }
472 if (id3) {
473 *id3 = wcb->field[3];
474 }
475
476 return 0;
477}
478
David Hendricksbabd1b52010-08-19 13:16:19 -0700479/** Tell EC to "enter flash update" mode. */
David Hendricksc801adb2010-12-09 16:58:56 -0800480int EnterFlashUpdate()
David Hendricksbabd1b52010-08-19 13:16:19 -0700481{
482 if (in_flash_update_mode) {
483 /* already in update mode */
David Hendricksc801adb2010-12-09 16:58:56 -0800484 msg_pdbg("%s: in_flash_update_mode: %d\n",
485 __func__, in_flash_update_mode);
David Hendricksbabd1b52010-08-19 13:16:19 -0700486 return 0;
487 }
David Hendricksc801adb2010-12-09 16:58:56 -0800488 assert_ec_is_free();
David Hendricksbabd1b52010-08-19 13:16:19 -0700489
490 wcb->code = 0x10; /* Enter Flash Update */
491 wcb->field[0] = 0x55; /* required pattern by EC */
492 wcb->field[1] = 0xAA; /* required pattern by EC */
493 wcb->field[2] = 0xCD; /* required pattern by EC */
494 wcb->field[3] = 0xBE; /* required pattern by EC */
495 if (blocked_exec()) {
496 return 1;
497 } else {
David Hendricksc801adb2010-12-09 16:58:56 -0800498 in_flash_update_mode = 1;
David Hendricksbabd1b52010-08-19 13:16:19 -0700499 return 0;
500 }
501}
502
503/** Tell EC to "exit flash update" mode.
504 * Without calling this function, the EC stays in busy-loop and will not
505 * response further request from host, which means system will halt.
506 */
507int ExitFlashUpdate(unsigned char exit_code)
508{
David Hendricksc801adb2010-12-09 16:58:56 -0800509 /*
510 * Note: ExitFlashUpdate must be called before shutting down the
511 * machine, otherwise the EC will be stuck in update mode, leaving
512 * the machine in a "wedged" state until power cycled.
513 */
514 if (!in_flash_update_mode) {
David Hendricksbabd1b52010-08-19 13:16:19 -0700515 msg_cdbg("Not in flash update mode yet.\n");
516 return 1;
517 }
518
David Hendricksbabd1b52010-08-19 13:16:19 -0700519 wcb->code = exit_code; /* Exit Flash Update */
520 if (blocked_exec()) {
521 return 1;
David Hendricksbabd1b52010-08-19 13:16:19 -0700522 }
David Hendricksc801adb2010-12-09 16:58:56 -0800523
524 in_flash_update_mode = 0;
525 return 0;
David Hendricksbabd1b52010-08-19 13:16:19 -0700526}
527
528/*
529 * Note: The EC firmware this patch has been tested with uses the following
530 * codes to indicate flash update status:
531 * 0x20 is used for EC F/W no change, but BIOS changed (in Share mode)
532 * 0x21 is used for EC F/W changed. Goto EC F/W, wait system reboot.
533 * 0x22 is used for EC F/W changed, Goto EC Watchdog reset. */
534int ExitFlashUpdateFirmwareNoChange(void) {
535 return ExitFlashUpdate(0x20);
536}
537
538int ExitFlashUpdateFirmwareChanged(void) {
539 return ExitFlashUpdate(0x21);
540}
541
David Hendricksc801adb2010-12-09 16:58:56 -0800542int wpce775x_spi_common_init(void)
David Hendricksbabd1b52010-08-19 13:16:19 -0700543{
David Hendricksc801adb2010-12-09 16:58:56 -0800544 uint16_t sio_port;
545 uint8_t srid;
546 uint8_t fwh_id;
547
548 msg_pdbg("%s(): entered\n", __func__);
549
550 /* detect if wpce775x exists */
551 if (nuvoton_get_sio_index(&sio_port) < 0) {
552 msg_pdbg("No Nuvoton chip is found.\n");
553 return 0;
554 }
555 srid = sio_read(sio_port, NUVOTON_SIOCFG_SRID);
556 if ((srid & 0xE0) == 0xA0) {
557 msg_pdbg("Found EC: WPCE775x (Vendor:0x%02x,ID:0x%02x,Rev:0x%02x) on sio_port:0x%x.\n",
558 sio_read(sio_port, NUVOTON_SIOCFG_SID),
559 srid >> 5, srid & 0x1f, sio_port);
560
561 } else {
562 msg_pdbg("Found EC: Nuvoton (Vendor:0x%02x,ID:0x%02x,Rev:0x%02x) on sio_port:0x%x.\n",
563 sio_read(sio_port, NUVOTON_SIOCFG_SID),
564 srid >> 5, srid & 0x1f, sio_port);
565 }
566
567 /* get the address of Shadow Window 2. */
568 if (get_shaw2ba(&wcb_physical_address) < 0) {
569 msg_pdbg("Cannot get the address of Shadow Window 2");
570 return 0;
571 }
572 msg_pdbg("Get the address of WCB(SHA WIN2) at 0x%08x\n",
573 (uint32_t)wcb_physical_address);
574 wcb = (struct wpce775x_wcb *)
575 programmer_map_flash_region("WPCE775X WCB",
576 wcb_physical_address,
577 getpagesize() /* min page size */);
578 msg_pdbg("mapped wcb address: %p for physical addr: 0x%08lx\n", wcb, wcb_physical_address);
579 if (!wcb) {
580 msg_perr("FATAL! Cannot map memory area for wcb physical address.\n");
581 return 0;
582 }
583 memset((void*)wcb, 0, sizeof(*wcb));
584
585 if (get_fwh_id(&fwh_id) < 0) {
586 msg_pdbg("Cannot get fwh_id value.\n");
587 return 0;
588 }
589 msg_pdbg("get fwh_id: 0x%02x\n", fwh_id);
590
591 /* TODO: set fwh_idsel of chipset.
592 Currently, we employ "-p internal:fwh_idsel=0x0000223e". */
593
594 /* Enter flash update mode unconditionally. This is required even
595 for reading. */
596 if (EnterFlashUpdate()) return 1;
597
598 spi_controller = SPI_CONTROLLER_WPCE775X;
599 msg_pdbg("%s(): successfully initialized wpce775x\n", __func__);
600 return 0;
601
602}
603
604int wpce775x_shutdown(void)
605{
606 if (spi_controller != SPI_CONTROLLER_WPCE775X)
607 return 0;
608
609 msg_pdbg("%s(): firmware %s\n", __func__,
610 firmware_changed ? "changed" : "not changed");
611
612 msg_pdbg("%s: in_flash_update_mode: %d\n", __func__, in_flash_update_mode);
613 if (in_flash_update_mode) {
614 if (firmware_changed)
615 ExitFlashUpdateFirmwareChanged();
616 else
617 ExitFlashUpdateFirmwareNoChange();
618
619 in_flash_update_mode = 0;
620 }
621
622 if (initflash_cfg)
623 free(initflash_cfg);
624 else
625 msg_perr("%s(): No initflash_cfg to free?!?\n", __func__);
626
627 return 0;
628}
629
630/* Called by internal_init() */
631int wpce775x_probe_spi_flash(const char *name)
632{
633 int ret;
634
635 if (!(buses_supported & CHIP_BUSTYPE_FWH)) {
636 msg_pdbg("%s():%d buses not support FWH\n", __func__, __LINE__);
637 return 1;
638 }
639 ret = wpce775x_spi_common_init();
640 msg_pdbg("FWH: %s():%d ret=%d\n", __func__, __LINE__, ret);
641 if (!ret) {
642 msg_pdbg("%s():%d buses_supported=0x%x\n", __func__, __LINE__,
643 buses_supported);
644 if (buses_supported & CHIP_BUSTYPE_FWH)
645 msg_pdbg("Overriding chipset SPI with WPCE775x FWH|SPI.\n");
646 buses_supported |= CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_SPI;
647 }
648 return ret;
649}
650
651int wpce775x_read(int addr, unsigned char *buf, unsigned int nbytes)
652{
653 int offset;
654 unsigned int bytes_read = 0;
655
David Hendricksbabd1b52010-08-19 13:16:19 -0700656 assert_ec_is_free();
David Hendricksc801adb2010-12-09 16:58:56 -0800657 msg_pspew("%s: reading %d bytes at 0x%06x\n", __func__, nbytes, addr);
658
659 /* Set initial address; WPCE775x auto-increments address for successive
660 read and write operations. */
661 wcb->code = 0xA0;
662 wcb->field[0] = addr & 0xff;
663 wcb->field[1] = (addr >> 8) & 0xff;
664 wcb->field[2] = (addr >> 16) & 0xff;
665 wcb->field[3] = (addr >> 24) & 0xff;
666 if (blocked_exec()) {
667 return 1;
668 }
669
670 for (offset = 0;
671 offset < nbytes;
672 offset += bytes_read) {
673 int i;
674 unsigned int bytes_left;
675
676 bytes_left = nbytes - offset;
677 if (bytes_left > 0 && bytes_left < WPCE775X_MAX_READ_SIZE)
678 bytes_read = bytes_left;
679 else
680 bytes_read = WPCE775X_MAX_READ_SIZE;
681 wcb->code = 0xD0 | bytes_read;
682 if (blocked_exec()) {
683 return 1;
684 }
685
686 for (i = 0; i < bytes_read; i++)
687 buf[offset + i] = wcb->field[i];
688 }
689
690 return 0;
691}
692
693int wpce775x_erase_new(int blockaddr, uint8_t opcode) {
694 unsigned int current;
695 int blocksize;
696 int ret = 0;
697
698 assert_ec_is_free();
699
700 /*
701 * FIXME: In the long-run we should examine block_erasers within the
702 * flash struct to ensure the proper blocksize is used. This is because
703 * some chips implement commands differently. For now, we'll support
704 * only a few "safe" block erase commands with predictable block size.
705 *
706 * Looking thru the list of flashchips, it seems JEDEC_BE_52 and
707 * JEDEC_BE_D8 are not uniformly implemented. Thus, we cannot safely
708 * assume a blocksize.
709 *
710 * Also, I was unable to test chip erase (due to equipment and time
711 * constraints), but they might work.
712 */
713 switch(opcode) {
714 case JEDEC_SE:
715 case JEDEC_BE_D7:
716 blocksize = 4 * 1024;
717 break;
718 case JEDEC_BE_52:
719 case JEDEC_BE_D8:
720 case JEDEC_CE_60:
721 case JEDEC_CE_C7:
722 default:
723 msg_perr("%s(): erase opcode=0x%02x not supported\n",
724 __func__, opcode);
725 return 1;
726 }
727
728 msg_pspew("%s(): blockaddr=%d, blocksize=%d, opcode=0x%02x\n",
729 __func__, blockaddr, blocksize, opcode);
730
731 if (!initflash_cfg)
732 initflash_cfg_setup(flash_internal);
733 initflash_cfg->block_erase = opcode;
734 InitFlash();
David Hendricksbabd1b52010-08-19 13:16:19 -0700735
736 /* Set Write Window on flash chip (optional).
737 * You may limit the window to partial flash for experimental. */
738 wcb->code = 0xC5; /* Set Write Window */
739 wcb->field[0] = 0x00; /* window base: little-endian */
740 wcb->field[1] = 0x00;
741 wcb->field[2] = 0x00;
742 wcb->field[3] = 0x00;
743 wcb->field[4] = 0x00; /* window length: little-endian */
744 wcb->field[5] = 0x00;
745 wcb->field[6] = 0x20;
746 wcb->field[7] = 0x00;
747 if (blocked_exec())
748 return 1;
749
David Hendricksc801adb2010-12-09 16:58:56 -0800750 msg_pspew("Erasing ... 0x%08x 0x%08x\n", blockaddr, blocksize);
David Hendricksbabd1b52010-08-19 13:16:19 -0700751
752 for (current = 0;
David Hendricksc801adb2010-12-09 16:58:56 -0800753 current < blocksize;
754 current += blocksize) {
David Hendricksbabd1b52010-08-19 13:16:19 -0700755 wcb->code = 0x80; /* Sector/block erase */
756
David Hendricksc801adb2010-12-09 16:58:56 -0800757 /* WARNING: assume the block address for EC is always little-endian. */
David Hendricksbabd1b52010-08-19 13:16:19 -0700758 unsigned int addr = blockaddr + current;
759 wcb->field[0] = addr & 0xff;
760 wcb->field[1] = (addr >> 8) & 0xff;
761 wcb->field[2] = (addr >> 16) & 0xff;
762 wcb->field[3] = (addr >> 24) & 0xff;
David Hendricksc801adb2010-12-09 16:58:56 -0800763 if (blocked_exec()) {
764 ret = 1;
765 goto wpce775x_erase_new_exit;
766 }
David Hendricksbabd1b52010-08-19 13:16:19 -0700767 }
768
David Hendricksc801adb2010-12-09 16:58:56 -0800769wpce775x_erase_new_exit:
770 firmware_changed = 1;
771 return ret;
772}
David Hendricksbabd1b52010-08-19 13:16:19 -0700773
David Hendricksc801adb2010-12-09 16:58:56 -0800774int wpce775x_nbyte_program(int addr, const unsigned char *buf,
775 unsigned int nbytes)
776{
777 int offset, ret = 0;
778 unsigned int written = 0;
779
780 assert_ec_is_free();
781 msg_pspew("%s: writing %d bytes to 0x%06x\n", __func__, nbytes, addr);
782
783 /* Set initial address; WPCE775x auto-increments address for successive
784 read and write operations. */
785 wcb->code = 0xA0;
786 wcb->field[0] = addr & 0xff;
787 wcb->field[1] = (addr >> 8) & 0xff;
788 wcb->field[2] = (addr >> 16) & 0xff;
789 wcb->field[3] = (addr >> 24) & 0xff;
790 if (blocked_exec()) {
David Hendricksbabd1b52010-08-19 13:16:19 -0700791 return 1;
792 }
793
David Hendricksc801adb2010-12-09 16:58:56 -0800794 for (offset = 0;
795 offset < nbytes;
796 offset += written) {
David Hendricksbabd1b52010-08-19 13:16:19 -0700797 int i;
David Hendricksc801adb2010-12-09 16:58:56 -0800798 unsigned int bytes_left;
799
800 bytes_left = nbytes - offset;
801 if (bytes_left > 0 && bytes_left < WPCE775X_MAX_WRITE_SIZE)
802 written = bytes_left;
803 else
804 written = WPCE775X_MAX_WRITE_SIZE;
805 wcb->code = 0xB0 | written;
806
807 for (i = 0; i < written; i++)
808 wcb->field[i] = buf[offset + i];
809 if (blocked_exec()) {
810 ret = 1;
811 goto wpce775x_nbyte_program_exit;
David Hendricksbabd1b52010-08-19 13:16:19 -0700812 }
David Hendricksbabd1b52010-08-19 13:16:19 -0700813 }
814
David Hendricksc801adb2010-12-09 16:58:56 -0800815wpce775x_nbyte_program_exit:
816 firmware_changed = 1;
817 return ret;
David Hendricksbabd1b52010-08-19 13:16:19 -0700818}
819
David Hendricksc801adb2010-12-09 16:58:56 -0800820int wpce775x_spi_read(struct flashchip *flash, uint8_t * buf, int start, int len)
David Hendricksbabd1b52010-08-19 13:16:19 -0700821{
David Hendricksc801adb2010-12-09 16:58:56 -0800822 if (!initflash_cfg) {
823 initflash_cfg_setup(flash);
824 InitFlash();
825 }
826 return spi_read_chunked(flash, buf, start, len, flash->page_size);
David Hendricksbabd1b52010-08-19 13:16:19 -0700827}
David Hendricks21e06302010-09-03 14:51:46 -0700828
David Hendricksc801adb2010-12-09 16:58:56 -0800829int wpce775x_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
830{
831 if (!initflash_cfg) {
832 initflash_cfg_setup(flash);
833 InitFlash();
834 }
835 return spi_write_chunked(flash, buf, start, len, flash->page_size);
836}
David Hendricks21e06302010-09-03 14:51:46 -0700837
David Hendricksc801adb2010-12-09 16:58:56 -0800838int wpce775x_spi_write_status_register(uint8_t val)
839{
840 assert_ec_is_free();
841 msg_pdbg("%s(): writing 0x%02x to status register\n", __func__, val);
David Hendricks21e06302010-09-03 14:51:46 -0700842
David Hendricksc801adb2010-12-09 16:58:56 -0800843 if (!initflash_cfg)
844 initflash_cfg_setup(flash_internal);
David Hendricks21e06302010-09-03 14:51:46 -0700845
David Hendricksc801adb2010-12-09 16:58:56 -0800846 initflash_cfg->status_reg_value = val;
847 if (in_flash_update_mode) {
848 ExitFlashUpdateFirmwareNoChange();
849 in_flash_update_mode = 0;
850 }
851 if (InitFlash())
852 return 1;
Louis Yung-Chieh Lo623809c2010-11-30 15:43:18 +0800853 if (EnterFlashUpdate())
David Hendricksc801adb2010-12-09 16:58:56 -0800854 return 1;
Louis Yung-Chieh Lo623809c2010-11-30 15:43:18 +0800855 ExitFlashUpdateFirmwareNoChange();
David Hendricks21e06302010-09-03 14:51:46 -0700856 return 0;
857}
858
David Hendricksc801adb2010-12-09 16:58:56 -0800859/*
860 * WPCE775x does not allow direct access to SPI chip from host. This function
861 * will translate SPI commands to valid WPCE775x WCB commands.
Louis Yung-Chieh Lo623809c2010-11-30 15:43:18 +0800862 */
David Hendricksc801adb2010-12-09 16:58:56 -0800863int wpce775x_spi_send_command(unsigned int writecnt, unsigned int readcnt,
864 const unsigned char *writearr, unsigned char *readarr)
David Hendricks21e06302010-09-03 14:51:46 -0700865{
David Hendricksc801adb2010-12-09 16:58:56 -0800866 int rc = 0;
867 uint8_t opcode = writearr[0];
868
869 switch(opcode){
870 case JEDEC_RDID:{
871 unsigned char dummy = 0;
872 if (readcnt == 3)
873 ReadId(&readarr[0], &readarr[1], &readarr[2], &dummy);
874 else if (readcnt == 4)
875 ReadId(&readarr[0], &readarr[1], &readarr[2], &readarr[3]);
876 break;
877 }
878 case JEDEC_RDSR:
879 /*
880 * FIXME: WPCE775x does not support reading status register
881 * directly. Instead, we rely on the internally-kept value.
882 * Consequently, this RDSR wrapper does not reflect the genuine
883 * value of the status register.
884 */
885 if (initflash_cfg)
886 readarr[0] = initflash_cfg->status_reg_value;
887 else
888 readarr[0] = 0x00;
889 break;
890 case JEDEC_READ:{
891 int blockaddr = (writearr[1] << 16) |
892 (writearr[2] << 8) |
893 writearr[3];
894 rc = wpce775x_read(blockaddr, readarr, readcnt);
895 break;
896 }
897 case JEDEC_WRSR:
898 wpce775x_spi_write_status_register(writearr[1]);
899 rc = 0;
900 break;
901 case JEDEC_WREN:
902 case JEDEC_EWSR:
903 /* Handled by InitFlash() */
904 rc = 0;
905 break;
906 case JEDEC_SE:
907 case JEDEC_BE_52:
908 case JEDEC_BE_D7:
909 case JEDEC_BE_D8:
910 case JEDEC_CE_60:
911 case JEDEC_CE_C7:{
912 int blockaddr = (writearr[1] << 16) |
913 (writearr[2] << 8) |
914 writearr[3];
915
916 rc = wpce775x_erase_new(blockaddr, opcode);
917 break;
918 }
919 case JEDEC_BYTE_PROGRAM:{
920 int blockaddr = (writearr[1] << 16) |
921 (writearr[2] << 8) |
922 writearr[3];
923 int nbytes = writecnt - 4;
924
925 rc = wpce775x_nbyte_program(blockaddr, &writearr[4], nbytes);
926 break;
927 }
928 case JEDEC_REMS:
929 case JEDEC_RES:
930 case JEDEC_WRDI:
931 case JEDEC_AAI_WORD_PROGRAM:
932 default:
933 /* unsupported opcodes */
934 msg_pdbg("unsupported SPI opcode: %02x\n", opcode);
935 rc = 1;
936 break;
937 }
938
939 msg_pdbg("%s: opcode: 0x%02x\n", __func__, opcode);
940 return rc;
Louis Yung-Chieh Lo623809c2010-11-30 15:43:18 +0800941}