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