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