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