wpce775x.c 28 KB

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