mec1308.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  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 Google or the names of contributors or
  18. * licensors may be used to endorse or promote products derived from this
  19. * 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. * GOOGLE INC 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. * GOOGLE 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 GOOGLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
  33. */
  34. #if defined(__i386__) || defined(__x86_64__)
  35. #include <inttypes.h>
  36. #include <stdlib.h>
  37. #include <string.h>
  38. #include <unistd.h>
  39. #include "flash.h"
  40. #include "chipdrivers.h"
  41. #include "programmer.h"
  42. #include "spi.h"
  43. #define MEC1308_SIO_PORT1 0x2e
  44. #define MEC1308_SIO_PORT2 0x4e
  45. #define MEC1308_SIO_ENTRY_KEY 0x55
  46. #define MEC1308_SIO_EXIT_KEY 0xaa
  47. #define MEC1308_SIOCFG_LDN 0x07 /* LDN Bank Selector */
  48. #define MEC1308_DEVICE_ID_REG 0x20 /* Device ID Register */
  49. #define MEC1308_DEVICE_ID_VAL 0x4d /* Device ID Value for MEC1308 */
  50. #define MEC1310_DEVICE_ID_VAL 0x04 /* Device ID Value for MEC1310 */
  51. #define MEC1308_DEVICE_REV 0x21 /* Device Revision ID Register */
  52. static unsigned int in_sio_cfgmode;
  53. #define MEC1308_MBX_CMD 0x82 /* mailbox command register offset */
  54. #define MEC1308_MBX_EXT_CMD 0x83 /* mailbox ext. command reg offset */
  55. #define MEC1308_MBX_DATA_START 0x84 /* first mailbox data register offset */
  56. #define MEC1308_MBX_DATA_END 0x91 /* last mailbox data register offset */
  57. static unsigned int mbx_idx; /* Mailbox register interface index address */
  58. static unsigned int mbx_data; /* Mailbox register interface data address*/
  59. /*
  60. * These command codes depend on EC firmware. The ones listed below are input
  61. * using the mailbox interface, though others may be input using the ACPI
  62. * interface. Some commands also have an output value (ie pass/failure code)
  63. * which EC writes to the mailbox command register after completion.
  64. */
  65. #define MEC1308_CMD_SMI_ENABLE 0x84
  66. #define MEC1308_CMD_SMI_DISABLE 0x85
  67. #define MEC1308_CMD_ACPI_ENABLE 0x86
  68. #define MEC1308_CMD_ACPI_DISABLE 0x87
  69. /*
  70. * Passthru commands are also input using the mailbox interface. Passthru mode
  71. * enter/start/end commands are special since they require a command word to
  72. * be written to the data registers. Other passthru commands are performed
  73. * after passthru mode has been started.
  74. *
  75. * Multiple passthru mode commands may be issued before ending passthru mode.
  76. * You do not need to enter, start, and end passthru mode for each SPI
  77. * command. However, other mailbox commands might not work when passthru mode
  78. * is enabled. For example, you may read all SPI chip content while in passthru
  79. * mode, but you should exit passthru mode before performing other EC commands
  80. * such as reading fan speed.
  81. */
  82. #define MEC1308_CMD_PASSTHRU 0x55 /* force EC to process word */
  83. #define MEC1308_CMD_PASSTHRU_SUCCESS 0xaa /* success code for passthru */
  84. #define MEC1308_CMD_PASSTHRU_FAIL 0xfe /* failure code for passthru */
  85. #define MEC1308_CMD_PASSTHRU_ENTER "PathThruMode" /* not a typo... */
  86. #define MEC1308_CMD_PASSTHRU_START "Start"
  87. #define MEC1308_CMD_PASSTHRU_EXIT "End_Mode"
  88. #define MEC1308_CMD_PASSTHRU_CS_EN 0xf0 /* chip-select enable */
  89. #define MEC1308_CMD_PASSTHRU_CS_DIS 0xf1 /* chip-select disable */
  90. #define MEC1308_CMD_PASSTHRU_SEND 0xf2 /* send byte from data0 */
  91. #define MEC1308_CMD_PASSTHRU_READ 0xf3 /* read byte, place in data0 */
  92. static void mec1308_sio_enter(uint16_t port)
  93. {
  94. if (in_sio_cfgmode)
  95. return;
  96. OUTB(MEC1308_SIO_ENTRY_KEY, port);
  97. in_sio_cfgmode = 1;
  98. }
  99. static void mec1308_sio_exit(uint16_t port)
  100. {
  101. if (!in_sio_cfgmode)
  102. return;
  103. OUTB(MEC1308_SIO_EXIT_KEY, port);
  104. in_sio_cfgmode = 0;
  105. }
  106. /** probe for super i/o index
  107. * @port: allocated buffer to store port
  108. *
  109. * returns 0 to indicate success, <0 to indicate error
  110. */
  111. static int mec1308_get_sio_index(uint16_t *port)
  112. {
  113. uint16_t ports[] = { MEC1308_SIO_PORT1,
  114. MEC1308_SIO_PORT2,
  115. };
  116. int i;
  117. static uint16_t port_internal, port_found = 0;
  118. if (port_found) {
  119. *port = port_internal;
  120. return 0;
  121. }
  122. get_io_perms();
  123. for (i = 0; i < ARRAY_SIZE(ports); i++) {
  124. uint8_t tmp8;
  125. /*
  126. * Only after config mode has been successfully entered will the
  127. * index port will read back the last value written to it.
  128. * So we will attempt to enter config mode, set the index
  129. * register, and see if the index register retains the value.
  130. *
  131. * Note: It seems to work "best" when using a device ID register
  132. * as the index and reading from the data port before reading
  133. * the index port.
  134. */
  135. mec1308_sio_enter(ports[i]);
  136. OUTB(MEC1308_DEVICE_ID_REG, ports[i]);
  137. tmp8 = INB(ports[i] + 1);
  138. tmp8 = INB(ports[i]);
  139. if ((tmp8 != MEC1308_DEVICE_ID_REG)) {
  140. in_sio_cfgmode = 0;
  141. continue;
  142. }
  143. port_internal = ports[i];
  144. port_found = 1;
  145. break;
  146. }
  147. if (!port_found) {
  148. msg_cdbg("\nfailed to obtain super i/o index");
  149. return -1;
  150. }
  151. msg_cdbg("\nsuper i/o index = 0x%04x\n", port_internal);
  152. *port = port_internal;
  153. return 0;
  154. }
  155. static uint8_t mbx_read(uint8_t idx)
  156. {
  157. OUTB(idx, mbx_idx);
  158. return INB(mbx_data);
  159. }
  160. static int mbx_wait(void)
  161. {
  162. int i;
  163. int max_attempts = 10000;
  164. int rc = 0;
  165. for (i = 0; mbx_read(MEC1308_MBX_CMD); i++) {
  166. if (i == max_attempts) {
  167. rc = 1;
  168. break;
  169. }
  170. /* FIXME: This delay adds determinism to the delay period. It
  171. was chosen arbitrarily thru some experiments. */
  172. programmer_delay(2);
  173. }
  174. return rc;
  175. }
  176. static int mbx_write(uint8_t idx, uint8_t data)
  177. {
  178. int rc = 0;
  179. if (idx == MEC1308_MBX_CMD && mbx_wait()) {
  180. msg_perr("%s: command register not clear\n", __func__);
  181. return 1;
  182. }
  183. OUTB(idx, mbx_idx);
  184. OUTB(data, mbx_data);
  185. if (idx == MEC1308_MBX_CMD)
  186. rc = mbx_wait();
  187. return rc;
  188. }
  189. static void mbx_clear()
  190. {
  191. int reg;
  192. for (reg = MEC1308_MBX_DATA_START; reg < MEC1308_MBX_DATA_END; reg++)
  193. mbx_write(reg, 0x00);
  194. mbx_write(MEC1308_MBX_CMD, 0x00);
  195. }
  196. static int mec1308_exit_passthru_mode(void)
  197. {
  198. uint8_t tmp8;
  199. int i;
  200. /* exit passthru mode */
  201. for (i = 0; i < strlen(MEC1308_CMD_PASSTHRU_EXIT); i++) {
  202. mbx_write(MEC1308_MBX_DATA_START + i,
  203. MEC1308_CMD_PASSTHRU_EXIT[i]);
  204. }
  205. if (mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU)) {
  206. msg_pdbg("%s(): exit passthru command timed out\n", __func__);
  207. return 1;
  208. }
  209. tmp8 = mbx_read(MEC1308_MBX_DATA_START);
  210. msg_pdbg("%s: result: 0x%02x ", __func__, tmp8);
  211. if (tmp8 == MEC1308_CMD_PASSTHRU_SUCCESS) {
  212. msg_pdbg("(exited passthru mode)\n");
  213. } else if (tmp8 == MEC1308_CMD_PASSTHRU_FAIL) {
  214. msg_pdbg("(failed to exit passthru mode)\n");
  215. }
  216. return 0;
  217. }
  218. static int enter_passthru_mode(void)
  219. {
  220. uint8_t tmp8;
  221. int i;
  222. /*
  223. * Enter passthru mode. If the EC does not successfully enter passthru
  224. * mode the first time, we'll clear the mailbox and issue the "exit
  225. * passthru mode" command sequence up to 3 times or until it arrives in
  226. * a known state.
  227. *
  228. * Note: This workaround was developed experimentally.
  229. */
  230. for (i = 0; i < 3; i++) {
  231. int j;
  232. msg_pdbg("%s(): entering passthru mode, attempt %d out of 3\n",
  233. __func__, i + 1);
  234. for (j = 0; j < strlen(MEC1308_CMD_PASSTHRU_ENTER); j++) {
  235. mbx_write(MEC1308_MBX_DATA_START + j,
  236. MEC1308_CMD_PASSTHRU_ENTER[j]);
  237. }
  238. if (mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU))
  239. msg_pdbg("%s(): enter passthru command timed out\n",
  240. __func__);
  241. tmp8 = mbx_read(MEC1308_MBX_DATA_START);
  242. if (tmp8 == MEC1308_CMD_PASSTHRU_SUCCESS)
  243. break;
  244. msg_pdbg("%s(): command failed, clearing data registers and "
  245. "issuing full exit passthru command...\n", __func__);
  246. mbx_clear();
  247. mec1308_exit_passthru_mode();
  248. }
  249. if (tmp8 != MEC1308_CMD_PASSTHRU_SUCCESS) {
  250. msg_perr("%s(): failed to enter passthru mode, result=0x%02x\n",
  251. __func__, tmp8);
  252. return 1;
  253. }
  254. msg_pdbg("%s(): enter passthru mode return code: 0x%02x\n",
  255. __func__, tmp8);
  256. /* start passthru mode */
  257. for (i = 0; i < strlen(MEC1308_CMD_PASSTHRU_START); i++)
  258. mbx_write(MEC1308_MBX_DATA_START + i,
  259. MEC1308_CMD_PASSTHRU_START[i]);
  260. if (mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU)) {
  261. msg_pdbg("%s(): start passthru command timed out\n", __func__);
  262. return 1;
  263. }
  264. tmp8 = mbx_read(MEC1308_MBX_DATA_START);
  265. if (tmp8 != MEC1308_CMD_PASSTHRU_SUCCESS) {
  266. msg_perr("%s(): failed to enter passthru mode, result=%02x\n",
  267. __func__, tmp8);
  268. return 1;
  269. }
  270. msg_pdbg("%s(): start passthru mode return code: 0x%02x\n",
  271. __func__, tmp8);
  272. return 0;
  273. }
  274. static int mec1308_shutdown(void *data)
  275. {
  276. /* Exit passthru mode before performing commands which do not affect
  277. the SPI ROM */
  278. mec1308_exit_passthru_mode();
  279. /* Re-enable SMI and ACPI.
  280. FIXME: is there an ordering dependency? */
  281. if (mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_SMI_ENABLE))
  282. msg_pdbg("%s: unable to re-enable SMI\n", __func__);
  283. if (mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_ACPI_ENABLE))
  284. msg_pdbg("%s: unable to re-enable ACPI\n", __func__);
  285. return 0;
  286. }
  287. int mec1308_spi_read(struct flashctx *flash, uint8_t * buf, int start, int len)
  288. {
  289. return spi_read_chunked(flash, buf, start, len, flash->page_size);
  290. }
  291. int mec1308_spi_write_256(struct flashctx *flash,
  292. uint8_t *buf, int start, int len)
  293. {
  294. return spi_write_chunked(flash, buf, start, len, flash->page_size);
  295. }
  296. static int mec1308_chip_select(void)
  297. {
  298. return mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_CS_EN);
  299. }
  300. static int mec1308_chip_deselect(void)
  301. {
  302. return mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_CS_DIS);
  303. }
  304. /*
  305. * MEC1308 will not allow direct access to SPI chip from host if EC is
  306. * connected to LPC bus. This function will forward commands issued thru
  307. * mailbox interface to the SPI flash chip.
  308. */
  309. int mec1308_spi_send_command(const struct flashctx *flash, unsigned int writecnt,
  310. unsigned int readcnt,
  311. const unsigned char *writearr,
  312. unsigned char *readarr)
  313. {
  314. int i, rc = 0;
  315. if (mec1308_chip_select())
  316. return 1;
  317. for (i = 0; i < writecnt; i++) {
  318. if (mbx_write(MEC1308_MBX_DATA_START, writearr[i]) ||
  319. mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_SEND)) {
  320. msg_pdbg("%s: failed to issue send command\n",__func__);
  321. rc = 1;
  322. goto mec1308_spi_send_command_exit;
  323. }
  324. }
  325. for (i = 0; i < readcnt; i++) {
  326. if (mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_READ)) {
  327. msg_pdbg("%s: failed to issue read command\n",__func__);
  328. rc = 1;
  329. goto mec1308_spi_send_command_exit;
  330. }
  331. readarr[i] = mbx_read(MEC1308_MBX_DATA_START);
  332. }
  333. mec1308_spi_send_command_exit:
  334. rc |= mec1308_chip_deselect();
  335. return rc;
  336. }
  337. static const struct spi_programmer spi_programmer_mec1308 = {
  338. .type = SPI_CONTROLLER_MEC1308,
  339. .max_data_read = 256, /* FIXME: should be MAX_DATA_READ_UNLIMITED? */
  340. .max_data_write = 256, /* FIXME: should be MAX_DATA_WRITE_UNLIMITED? */
  341. .command = mec1308_spi_send_command,
  342. .multicommand = default_spi_send_multicommand,
  343. .read = default_spi_read,
  344. .write_256 = default_spi_write_256,
  345. };
  346. /* Called by internal_init() */
  347. int mec1308_probe_spi_flash(const char *name)
  348. {
  349. uint16_t sio_port;
  350. uint8_t device_id;
  351. uint8_t tmp8;
  352. int ret = 0;
  353. char *p = NULL;
  354. msg_pdbg("%s(): entered\n", __func__);
  355. if (alias && alias->type != ALIAS_EC)
  356. return 1;
  357. p = extract_programmer_param("type");
  358. if (p && strcmp(p, "ec")) {
  359. msg_pdbg("mec1308 only supports \"ec\" type devices\n");
  360. ret = 1;
  361. goto mec1308_probe_spi_flash_exit;
  362. }
  363. if (mec1308_get_sio_index(&sio_port) < 0) {
  364. msg_pdbg("MEC1308 not found (probe failed).\n");
  365. ret = 1;
  366. goto mec1308_probe_spi_flash_exit;
  367. }
  368. device_id = sio_read(sio_port, MEC1308_DEVICE_ID_REG);
  369. switch(device_id) {
  370. case MEC1308_DEVICE_ID_VAL:
  371. msg_pdbg("Found EC: MEC1308 (ID:0x%02x,Rev:0x%02x) on "
  372. "sio_port:0x%x.\n", device_id,
  373. sio_read(sio_port, MEC1308_DEVICE_REV), sio_port);
  374. break;
  375. case MEC1310_DEVICE_ID_VAL:
  376. msg_pdbg("Found EC: MEC1310 (ID:0x%02x,Rev:0x%02x) on "
  377. "sio_port:0x%x.\n", device_id,
  378. sio_read(sio_port, MEC1308_DEVICE_REV), sio_port);
  379. break;
  380. default:
  381. msg_pdbg("MEC1308 not found\n");
  382. ret = 1;
  383. goto mec1308_probe_spi_flash_exit;
  384. }
  385. /*
  386. * setup mailbox interface at LDN 9
  387. */
  388. sio_write(sio_port, MEC1308_SIOCFG_LDN, 0x09);
  389. tmp8 = sio_read(sio_port, 0x30);
  390. tmp8 |= 1;
  391. sio_write(sio_port, 0x30, tmp8); /* activate logical device */
  392. mbx_idx = (unsigned int)sio_read(sio_port, 0x60) << 8 |
  393. sio_read(sio_port, 0x61);
  394. mbx_data = mbx_idx + 1;
  395. msg_pdbg("%s: mbx_idx: 0x%04x, mbx_data: 0x%04x\n",
  396. __func__, mbx_idx, mbx_data);
  397. /* Exit Super I/O config mode */
  398. mec1308_sio_exit(sio_port);
  399. /* Now that we can read the mailbox, we will wait for any remaining
  400. * command to finish.*/
  401. if (mbx_wait() != 0) {
  402. msg_perr("%s: mailbox is not available\n", __func__);
  403. ret = 1;
  404. goto mec1308_probe_spi_flash_exit;
  405. }
  406. /* Further setup -- disable SMI and ACPI.
  407. FIXME: is there an ordering dependency? */
  408. if (mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_ACPI_DISABLE)) {
  409. msg_pdbg("%s: unable to disable ACPI\n", __func__);
  410. ret = 1;
  411. goto mec1308_probe_spi_flash_exit;
  412. }
  413. if (mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_SMI_DISABLE)) {
  414. msg_pdbg("%s: unable to disable SMI\n", __func__);
  415. ret = 1;
  416. goto mec1308_probe_spi_flash_exit;
  417. }
  418. if (register_shutdown(mec1308_shutdown, NULL)) {
  419. ret = 1;
  420. goto mec1308_probe_spi_flash_exit;
  421. }
  422. /*
  423. * Enter SPI Pass-Thru Mode after commands which do not require access
  424. * to SPI ROM are complete. We'll start by doing the exit_passthru_mode
  425. * sequence, which is benign if the EC is already in passthru mode.
  426. */
  427. mec1308_exit_passthru_mode();
  428. if (enter_passthru_mode()) {
  429. ret = 1;
  430. goto mec1308_probe_spi_flash_exit;
  431. }
  432. buses_supported |= BUS_LPC; /* for LPC <--> SPI bridging */
  433. register_spi_programmer(&spi_programmer_mec1308);
  434. msg_pdbg("%s(): successfully initialized mec1308\n", __func__);
  435. mec1308_probe_spi_flash_exit:
  436. free(p);
  437. return ret;
  438. }
  439. #endif