blob: 642c3220b774dba92079e42b1e6b9e95a2796a72 [file] [log] [blame]
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +08001// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <unistd.h>
9#include "flashchips.h"
10#include "gec_lpc_commands.h"
11#include "programmer.h"
12#include "spi.h"
13#include "writeprotect.h"
14
15
David Hendrickscb760a22012-01-05 12:33:04 -080016static int ec_timeout_usec = 1000000;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080017
18/* Waits for the EC to be unbusy. Returns 1 if busy, 0 if not busy. */
19static int ec_busy(int timeout_usec)
20{
21 int i;
22 for (i = 0; i < timeout_usec; i += 10) {
23 usleep(10); /* Delay first, in case we just sent a command */
Louis Yung-Chieh Loc738d9d2012-03-06 13:06:26 +080024 if (!(inb(EC_LPC_ADDR_USER_CMD) & EC_LPC_STATUS_BUSY_MASK))
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080025 return 0;
26 }
27 return 1; /* Timeout */
28}
29
30
Louis Yung-Chieh Loc738d9d2012-03-06 13:06:26 +080031static enum lpc_status gec_get_result() {
32 return inb(EC_LPC_ADDR_USER_DATA);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080033}
34
35
36/* Sends a command to the EC. Returns the command status code, or
37 * -1 if other error. */
38int ec_command(int command, const void *indata, int insize,
39 void *outdata, int outsize) {
40 uint8_t *d;
41 int i;
42
Louis Yung-Chieh Loc738d9d2012-03-06 13:06:26 +080043 if ((insize + outsize) > EC_LPC_PARAM_SIZE) {
44 msg_pdbg2("Data size too big for buffer.\n");
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080045 return -1;
46 }
47
David Hendrickscb760a22012-01-05 12:33:04 -080048 if (ec_busy(ec_timeout_usec)) {
Louis Yung-Chieh Loc738d9d2012-03-06 13:06:26 +080049 msg_pdbg2("Timeout waiting for EC ready\n");
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080050 return -1;
51 }
52
53 /* Write data, if any */
54 /* TODO: optimized copy using outl() */
55 for (i = 0, d = (uint8_t *)indata; i < insize; i++, d++) {
56 msg_pdbg2("GEC: Port[0x%x] <-- 0x%x\n",
Louis Yung-Chieh Loc738d9d2012-03-06 13:06:26 +080057 EC_LPC_ADDR_USER_PARAM + i, *d);
58 outb(*d, EC_LPC_ADDR_USER_PARAM + i);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080059 }
60
61 msg_pdbg2("GEC: Run EC Command: 0x%x ----\n", command);
Louis Yung-Chieh Loc738d9d2012-03-06 13:06:26 +080062 outb(command, EC_LPC_ADDR_USER_CMD);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080063
64 if (ec_busy(1000000)) {
Louis Yung-Chieh Loc738d9d2012-03-06 13:06:26 +080065 msg_pdbg2("Timeout waiting for EC response\n");
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080066 return -1;
67 }
68
69 /* Check status */
Louis Yung-Chieh Loc738d9d2012-03-06 13:06:26 +080070 if ((i = gec_get_result()) != EC_LPC_RESULT_SUCCESS) {
71 msg_pdbg2("EC returned error status %d\n", i);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080072 return i;
73 }
74
75 /* Read data, if any */
76 for (i = 0, d = (uint8_t *)outdata; i < outsize; i++, d++) {
Louis Yung-Chieh Loc738d9d2012-03-06 13:06:26 +080077 *d = inb(EC_LPC_ADDR_USER_PARAM + i);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080078 msg_pdbg2("GEC: Port[0x%x] ---> 0x%x\n",
Louis Yung-Chieh Loc738d9d2012-03-06 13:06:26 +080079 EC_LPC_ADDR_USER_PARAM + i, *d);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080080 }
81
82 return 0;
83}
84
85
86#ifdef SUPPORT_CHECKSUM
87static verify_checksum(uint8_t* expected,
88 unsigned int addr,
89 unsigned int count) {
90 int rc;
91 struct lpc_params_flash_checksum csp;
92 struct lpc_response_flash_checksum csr;
93 uint8_t cs;
94 int j;
95
96 csp.offset = addr;
97 csp.size = count;
98
99 rc = ec_command(EC_LPC_COMMAND_FLASH_CHECKSUM,
100 &csp, sizeof(csp), &csr, sizeof(csr));
101 if (rc) {
102 msg_perr("GEC: verify_checksum() error.\n");
103 return rc;
104 }
105
106 for (cs = 0, j = 0; j < count; ++j) {
107 BYTE_IN(cs, expected[j]);
108 }
109 if (cs != csr.checksum) {
110 msg_pdbg("GEC: checksum dismatch at 0x%02x "
111 "(ec: 0x%02x, local: 0x%02x). Retry.\n",
112 addr, csr.checksum, cs);
113 msg_pdbg("GEC: ");
114 for (j = 0; j < count; ++j) {
115 msg_pdbg("%02x-", expected[j]);
116 if ((j & 15) == 15) msg_pdbg("\nGEC: ");
117 }
118 programmer_delay(1000);
119 return 1;
120 }
121 return 0;
122}
123#endif /* SUPPORT_CHECKSUM */
124
125
126int gec_read(struct flashchip *flash, uint8_t *readarr,
127 unsigned int blockaddr, unsigned int readcnt) {
128 int i;
129 int rc = 0;
130 struct lpc_params_flash_read p;
131 struct lpc_response_flash_read r;
132
133 for (i = 0; i < readcnt; i += EC_LPC_FLASH_SIZE_MAX) {
134 p.offset = blockaddr + i;
135 p.size = min(readcnt - i, EC_LPC_FLASH_SIZE_MAX);
136 rc = ec_command(EC_LPC_COMMAND_FLASH_READ,
137 &p, sizeof(p), &r, sizeof(r));
138 if (rc) {
139 msg_perr("GEC: Flash read error at offset 0x%x\n",
140 blockaddr + i);
141 return rc;
142 }
143
144#ifdef SUPPORT_CHECKSUM
145 if (verify_checksum(r.data, blockaddr + i,
146 min(readcnt - i, EC_LPC_FLASH_SIZE_MAX))) {
147 msg_pdbg("GEC: re-read...\n");
148 i -= EC_LPC_FLASH_SIZE_MAX;
149 continue;
150 }
151#endif
152 memcpy(readarr + i, r.data, p.size);
153 }
154
155 return rc;
156}
157
158
159static int gec_block_erase(struct flashchip *flash,
160 unsigned int blockaddr,
161 unsigned int len) {
162 struct lpc_params_flash_erase erase;
163 int rc;
164 uint8_t *blank;
165
166#ifdef SUPPORT_CHECKSUM
167re_erase:
168#endif
169 erase.offset = blockaddr;
170 erase.size = len;
171 rc = ec_command(EC_LPC_COMMAND_FLASH_ERASE, &erase, sizeof(erase),
172 NULL, 0);
173 if (rc) {
174 msg_perr("GEC: Flash erase error at address 0x%x\n", blockaddr);
175 return rc;
176 }
177
178#ifdef SUPPORT_CHECKSUM
179 blank = malloc(len);
180 memset(blank, 0xff, len);
181 if (verify_checksum(blank, blockaddr, len)) {
182 msg_pdbg("GEC: Re-erase...\n");
183 goto re_erase;
184 }
185#endif
186
187 return rc;
188}
189
190
191int gec_write(struct flashchip *flash, uint8_t *buf, unsigned int addr,
192 unsigned int nbytes) {
193 int i, rc = 0;
194 unsigned int written = 0;
195 struct lpc_params_flash_write p;
196
197 for (i = 0; i < nbytes; i += written) {
198 written = min(nbytes - i, EC_LPC_FLASH_SIZE_MAX);
199 p.offset = addr + i;
200 p.size = written;
201 memcpy(p.data, &buf[i], written);
202 rc = ec_command(EC_LPC_COMMAND_FLASH_WRITE, &p, sizeof(p),
203 NULL, 0);
204
205#ifdef SUPPORT_CHECKSUM
206 if (verify_checksum(&buf[i], addr + i, written)) {
207 msg_pdbg("GEC: re-write...\n");
208 i -= written;
209 continue;
210 }
211#endif
212
213 if (rc) break;
214 }
215
216 return rc;
217}
218
219
220static int gec_list_ranges(const struct flashchip *flash) {
221 msg_pinfo("You can specify any range:\n");
222 msg_pinfo(" from: 0x%06x, to: 0x%06x\n", 0, flash->total_size * 1024);
223 msg_pinfo(" unit: 0x%06x (%dKB)\n", 2048, 2048);
224 return 0;
225}
226
227
228static int gec_set_range(const struct flashchip *flash,
229 unsigned int start, unsigned int len) {
230 struct lpc_params_flash_wp_range p;
231 int rc;
232
233 p.offset = start;
234 p.size = len;
235 rc = ec_command(EC_LPC_COMMAND_FLASH_WP_SET_RANGE, &p, sizeof(p),
236 NULL, 0);
237 if (rc) {
238 msg_perr("GEC: wp_set_range error: rc=%d\n", rc);
239 return rc;
240 }
241
242 return 0;
243}
244
245
246static int gec_enable_writeprotect(const struct flashchip *flash) {
247 struct lpc_params_flash_wp_enable p;
248 int rc;
249
250 p.enable_wp = 1;
251 rc = ec_command(EC_LPC_COMMAND_FLASH_WP_ENABLE, &p, sizeof(p),
252 NULL, 0);
253 if (rc) {
254 msg_perr("GEC: wp_enable_wp error: rc=%d\n", rc);
255 }
256
257 return rc;
258}
259
260
261static int gec_disable_writeprotect(const struct flashchip *flash) {
262 struct lpc_params_flash_wp_enable p;
263 int rc;
264
265 p.enable_wp = 0;
266 rc = ec_command(EC_LPC_COMMAND_FLASH_WP_ENABLE, &p, sizeof(p),
267 NULL, 0);
268 if (rc) {
269 msg_perr("GEC: wp_disable_wp error: rc=%d\n", rc);
270 } else {
271 msg_pinfo("Disabled WP. Reboot EC and de-assert #WP.\n");
272 }
273
274 return rc;
275}
276
277
278static int gec_wp_status(const struct flashchip *flash) {
279 int rc;
280 struct lpc_response_flash_wp_range range;
281 struct lpc_response_flash_wp_enable en;
282 uint8_t value;
283
284 rc = ec_command(EC_LPC_COMMAND_FLASH_WP_GET_RANGE, NULL, 0,
285 &range, sizeof(range));
286 if (rc) {
287 msg_perr("GEC: wp_get_wp_range error: rc=%d\n", rc);
288 return rc;
289 }
290 rc = ec_command(EC_LPC_COMMAND_FLASH_WP_GET_STATE, NULL, 0,
291 &en, sizeof(en));
292 if (rc) {
293 msg_perr("GEC: wp_get_wp_state error: rc=%d\n", rc);
294 return rc;
295 }
296
297 /* TODO: Fix scripts which rely on SPI-specific terminology. */
298 value = (en.enable_wp << 7);
299 msg_pinfo("WP: status: 0x%02x\n", value);
300 msg_pinfo("WP: status.srp0: %x\n", en.enable_wp);
301 msg_pinfo("WP: write protect is %s.\n",
302 en.enable_wp ? "enabled" : "disabled");
303 msg_pinfo("WP: write protect range: start=0x%08x, len=0x%08x\n",
304 range.offset, range.size);
305
306 return 0;
307}
308
309
310static int gec_probe_size(struct flashchip *flash) {
311 int rc;
312 struct lpc_response_flash_info info;
313 struct block_eraser *eraser;
314 static struct wp wp = {
315 .list_ranges = gec_list_ranges,
316 .set_range = gec_set_range,
317 .enable = gec_enable_writeprotect,
318 .disable = gec_disable_writeprotect,
319 .wp_status = gec_wp_status,
320 };
321
322 rc = ec_command(EC_LPC_COMMAND_FLASH_INFO, NULL, 0,
323 &info, sizeof(info));
324 if (rc) return 0;
325
326 flash->total_size = info.flash_size / 1024;
327 flash->page_size = min(info.write_block_size,
328 info.erase_block_size);
329 flash->tested = TEST_OK_PREW;
330 eraser = &flash->block_erasers[0];
331 eraser->eraseblocks[0].size = info.erase_block_size;
332 eraser->eraseblocks[0].count = info.flash_size /
333 eraser->eraseblocks[0].size;
334 flash->wp = &wp;
335
336 return 1;
337};
338
339
340static const struct opaque_programmer opaque_programmer_gec = {
341 .max_data_read = EC_LPC_FLASH_SIZE_MAX,
342 .max_data_write = EC_LPC_FLASH_SIZE_MAX,
343 .probe = gec_probe_size,
344 .read = gec_read,
345 .write = gec_write,
346 .erase = gec_block_erase,
347};
348
349
350/* Sends HELLO command to ACPI port and expects a value from Google EC.
351 *
352 * TODO: This is an intrusive command for non-Google ECs. Needs a more proper
353 * and more friendly way to detect.
354 */
355static int detect_ec(void) {
356 struct lpc_params_hello request;
357 struct lpc_response_hello response;
358 int rc = 0;
David Hendrickscb760a22012-01-05 12:33:04 -0800359 int old_timeout = ec_timeout_usec;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800360
361 if (target_bus != BUS_LPC) {
362 msg_pdbg("%s():%d target_bus is not LPC.\n", __func__, __LINE__);
363 return 1;
364 }
365
David Hendrickscb760a22012-01-05 12:33:04 -0800366 /* reduce timeout period temporarily in case EC is not present */
367 ec_timeout_usec = 25000;
368
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800369 /* Say hello to EC. */
370 request.in_data = 0xf0e0d0c0; /* Expect EC will add on 0x01020304. */
371 rc = ec_command(EC_LPC_COMMAND_HELLO, &request, sizeof(request),
372 &response, sizeof(response));
David Hendrickscb760a22012-01-05 12:33:04 -0800373
374 ec_timeout_usec = old_timeout;
375
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800376 if (rc || response.out_data != 0xf1e2d3c4) {
377 msg_pdbg("response.out_data is not 0xf1e2d3c4.\n"
378 "rc=%d, request=0x%x response=0x%x\n",
379 rc, request.in_data, response.out_data);
380#ifdef SUPPORT_CHECKSUM
381 /* In this mode, we can tolerate some bit errors. */
382 {
383 int diff = response.out_data ^ 0xf1e2d3c4;
384 if (!(diff = (diff - 1) & diff)) return 0;// 1-bit error
385 if (!(diff = (diff - 1) & diff)) return 0;// 2-bit error
386 if (!(diff = (diff - 1) & diff)) return 0;// 3-bit error
387 if (!(diff = (diff - 1) & diff)) return 0;// 4-bit error
388 }
389#endif
390 return 1;
391 }
392
393 return 0;
394}
395
396/* Called by internal_init() */
397int gec_probe_programmer(const char *name) {
398 struct pci_dev *dev;
399
400 msg_pdbg("%s():%d ...\n", __func__, __LINE__);
401
402 if (detect_ec()) return 1;
403
404 register_opaque_programmer(&opaque_programmer_gec);
405 if (buses_supported & BUS_SPI) {
406 msg_pdbg("%s():%d remove BUS_SPI from buses_supported.\n",
407 __func__, __LINE__);
408 buses_supported &= ~BUS_SPI;
409 }
410 buses_supported |= BUS_LPC;
411
412 return 0;
413}