blob: 270cf8117f0a2f14b14590627656342551eda612 [file] [log] [blame]
David Hendricksee712472012-05-23 21:50:59 -07001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2012 The Chromium OS Authors. All rights reserved.
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 */
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080034
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <unistd.h>
39#include "flashchips.h"
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080040#include "fmap.h"
Louis Yung-Chieh Loc0505242012-08-09 23:11:32 +080041#include "gec_lock.h"
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +080042#include "gec_ec_commands.h"
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080043#include "programmer.h"
44#include "spi.h"
45#include "writeprotect.h"
46
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +080047/* FIXME: used for wp hacks */
48#include <sys/types.h>
49#include <sys/stat.h>
50#include <fcntl.h>
51#include <unistd.h>
52struct wp_data {
53 int enable;
54 unsigned int start;
55 unsigned int len;
56};
57static struct wp_data fake_wp;
58#define WP_STATE_HACK_FILENAME "/mnt/stateful_partition/flashrom_wp_state"
59
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080060/* 1 if we want the flashrom to call erase_and_write_flash() again. */
61static int need_2nd_pass = 0;
62
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +080063/* 1 if we want the flashrom to try jumping to new firmware after update. */
64static int try_latest_firmware = 0;
65
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080066/* The range of each firmware copy from the image file to update.
67 * But re-define the .flags as the valid flag to indicate the firmware is
68 * new or not (if flags = 1).
69 */
70static struct fmap_area fwcopy[4]; // [0] is not used.
71
72/* The names of enum lpc_current_image to match in FMAP area names. */
David Hendricksbf8c4dd2012-07-19 12:13:17 -070073static const char *sections[3] = {
74 "UNKNOWN SECTION", // EC_IMAGE_UNKNOWN -- never matches
75 "EC_RO",
76 "EC_RW",
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080077};
78
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080079
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080080/* Given the range not able to update, mark the corresponding
81 * firmware as old.
82 */
83static void gec_invalidate_copy(unsigned int addr, unsigned int len)
84{
85 int i;
86
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +080087 for (i = EC_IMAGE_RO; i < ARRAY_SIZE(fwcopy); i++) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080088 struct fmap_area *fw = &fwcopy[i];
89 if ((addr >= fw->offset && (addr < fw->offset + fw->size)) ||
90 (fw->offset >= addr && (fw->offset < addr + len))) {
91 msg_pdbg("Mark firmware [%s] as old.\n",
92 sections[i]);
93 fw->flags = 0; // mark as old
94 }
95 }
96}
97
98
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +080099/* Asks EC to jump to a firmware copy. If target is EC_IMAGE_UNKNOWN,
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800100 * then this functions picks a NEW firmware copy and jumps to it. Note that
101 * RO is preferred, then A, finally B.
102 *
103 * Returns 0 for success.
104 */
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800105static int gec_jump_copy(enum ec_current_image target) {
106 struct ec_response_get_version c;
107 struct ec_params_reboot_ec p;
David Hendricks7cfbd022012-05-20 17:25:51 -0700108 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800109 int rc;
110
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800111 /* Since the EC may return EC_RES_SUCCESS twice if the EC doesn't
112 * jump to different firmware copy. The second EC_RES_SUCCESS would
113 * set the OBF=1 and the next command cannot be executed.
114 * Thus, we call EC to jump only if the target is different.
115 */
116 rc = priv->ec_command(EC_CMD_GET_VERSION, 0, NULL, 0, &c, sizeof(c));
Louis Yung-Chieh Lo5c97c9f2012-08-16 13:33:36 +0800117 if (rc < 0 || c.current_image == EC_IMAGE_UNKNOWN) {
118 msg_perr("GEC cannot get the running copy: rc=%d image=%s\n",
119 rc, sections[c.current_image]);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800120 return 1;
121 }
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800122
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800123 memset(&p, 0, sizeof(p));
124 p.cmd = target != EC_IMAGE_UNKNOWN ? target :
125 fwcopy[EC_IMAGE_RO].flags ? EC_IMAGE_RO :
126 fwcopy[EC_IMAGE_RW].flags ? EC_IMAGE_RW :
127 EC_IMAGE_UNKNOWN;
128 msg_pdbg("GEC is jumping to [%s]\n", sections[p.cmd]);
129 if (p.cmd == EC_IMAGE_UNKNOWN) return 1;
130
131 if (c.current_image == p.cmd) {
132 msg_pdbg("GEC is already in [%s]\n", sections[p.cmd]);
133 return 0;
134 }
135
136 rc = priv->ec_command(EC_CMD_REBOOT_EC, 0,
David Hendricks7cfbd022012-05-20 17:25:51 -0700137 &p, sizeof(p), NULL, 0);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800138 if (rc < 0) {
139 msg_perr("GEC cannot jump to [%s]:%d\n",
140 sections[p.cmd], rc);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800141 } else {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800142 msg_pdbg("GEC has jumped to [%s]\n", sections[p.cmd]);
143 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800144 }
145
146 /* Sleep 1 sec to wait the EC re-init. */
147 usleep(1000000);
148
149 return rc;
150}
151
152
153/* Given an image, this function parses FMAP and recognize the firmware
154 * ranges.
155 */
156int gec_prepare(uint8_t *image, int size) {
David Hendricks7cfbd022012-05-20 17:25:51 -0700157 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800158 struct fmap *fmap;
159 int i, j;
160
Louis Yung-Chieh Lo0eaa0ca2012-05-29 15:28:58 +0800161 if (!(priv && priv->detected)) return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800162
163 // Parse the fmap in the image file and cache the firmware ranges.
164 fmap = fmap_find_in_memory(image, size);
165 if (!fmap) return 0;
166
167 // Lookup RO/A/B sections in FMAP.
168 for (i = 0; i < fmap->nareas; i++) {
169 struct fmap_area *fa = &fmap->areas[i];
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800170 for (j = EC_IMAGE_RO; j < ARRAY_SIZE(sections); j++) {
David Hendricks5b06c882012-05-20 18:27:25 -0700171 if (!strcmp(sections[j], (const char *)fa->name)) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800172 msg_pdbg("Found '%s' in image.\n", fa->name);
173 memcpy(&fwcopy[j], fa, sizeof(*fa));
174 fwcopy[j].flags = 1; // mark as new
175 }
176 }
177 }
178
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800179 /* Warning: before update, we jump the EC to RO copy. If you want to
180 * change this behavior, please also check the gec_finish().
181 */
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800182 return gec_jump_copy(EC_IMAGE_RO);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800183}
184
185
186/* Returns >0 if we need 2nd pass of erase_and_write_flash().
187 * <0 if we cannot jump to any firmware copy.
188 * ==0 if no more pass is needed.
189 *
190 * This function also jumps to new-updated firmware copy before return >0.
191 */
192int gec_need_2nd_pass(void) {
David Hendricks7cfbd022012-05-20 17:25:51 -0700193 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
194
Louis Yung-Chieh Lo0eaa0ca2012-05-29 15:28:58 +0800195 if (!(priv && priv->detected)) return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800196
197 if (need_2nd_pass) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800198 if (gec_jump_copy(EC_IMAGE_UNKNOWN)) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800199 return -1;
200 }
201 }
202
203 return need_2nd_pass;
204}
205
206
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800207/* Returns 0 for success.
208 *
209 * Try latest firmware: B > A > RO
210 *
211 * This function assumes the EC jumps to RO at gec_prepare() so that
212 * the fwcopy[RO].flags is old (0) and A/B are new. Please also refine
213 * this code logic if you change the gec_prepare() behavior.
214 */
215int gec_finish(void) {
216 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
217
218 if (!(priv && priv->detected)) return 0;
219
220 if (try_latest_firmware) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800221 if (fwcopy[EC_IMAGE_RW].flags &&
222 gec_jump_copy(EC_IMAGE_RW) == 0) return 0;
223 return gec_jump_copy(EC_IMAGE_RO);
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800224 }
225
226 return 0;
227}
228
229
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800230int gec_read(struct flashchip *flash, uint8_t *readarr,
231 unsigned int blockaddr, unsigned int readcnt) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800232 int rc = 0;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800233 struct ec_params_flash_read p;
David Hendricks7cfbd022012-05-20 17:25:51 -0700234 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
David Hendricksd6a0f662012-05-29 14:39:50 -0700235 int maxlen = opaque_programmer->max_data_read;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800236 uint8_t buf[maxlen];
David Hendricks133083b2012-07-17 20:39:38 -0700237 int offset = 0, count;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800238
David Hendricks133083b2012-07-17 20:39:38 -0700239 while (offset < readcnt) {
240 count = min(maxlen, readcnt - offset);
241 p.offset = blockaddr + offset;
242 p.size = count;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800243 rc = priv->ec_command(EC_CMD_FLASH_READ, 0,
244 &p, sizeof(p), buf, count);
245 if (rc < 0) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800246 msg_perr("GEC: Flash read error at offset 0x%x\n",
David Hendricks133083b2012-07-17 20:39:38 -0700247 blockaddr + offset);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800248 return rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800249 } else {
250 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800251 }
252
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800253 memcpy(readarr + offset, buf, count);
David Hendricks133083b2012-07-17 20:39:38 -0700254 offset += count;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800255 }
256
257 return rc;
258}
259
260
David Hendricks7cfbd022012-05-20 17:25:51 -0700261int gec_block_erase(struct flashchip *flash,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800262 unsigned int blockaddr,
263 unsigned int len) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800264 struct ec_params_flash_erase erase;
David Hendricks7cfbd022012-05-20 17:25:51 -0700265 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800266 int rc;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800267
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800268 erase.offset = blockaddr;
269 erase.size = len;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800270 rc = priv->ec_command(EC_CMD_FLASH_ERASE, 0,
David Hendricks7cfbd022012-05-20 17:25:51 -0700271 &erase, sizeof(erase), NULL, 0);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800272 if (rc == -EC_RES_ACCESS_DENIED) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800273 // this is active image.
274 gec_invalidate_copy(blockaddr, len);
275 need_2nd_pass = 1;
276 return ACCESS_DENIED;
277 }
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800278 if (rc < 0) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800279 msg_perr("GEC: Flash erase error at address 0x%x, rc=%d\n",
280 blockaddr, rc);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800281 return rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800282 } else {
283 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800284 }
285
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800286 try_latest_firmware = 1;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800287 return rc;
288}
289
290
291int gec_write(struct flashchip *flash, uint8_t *buf, unsigned int addr,
292 unsigned int nbytes) {
293 int i, rc = 0;
294 unsigned int written = 0;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800295 struct ec_params_flash_write p;
David Hendricks7cfbd022012-05-20 17:25:51 -0700296 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
David Hendricksd6a0f662012-05-29 14:39:50 -0700297 int maxlen = opaque_programmer->max_data_write;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800298
299 for (i = 0; i < nbytes; i += written) {
David Hendricksd6a0f662012-05-29 14:39:50 -0700300 written = min(nbytes - i, maxlen);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800301 p.offset = addr + i;
302 p.size = written;
303 memcpy(p.data, &buf[i], written);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800304 rc = priv->ec_command(EC_CMD_FLASH_WRITE, 0,
David Hendricks7cfbd022012-05-20 17:25:51 -0700305 &p, sizeof(p), NULL, 0);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800306 if (rc == -EC_RES_ACCESS_DENIED) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800307 // this is active image.
308 gec_invalidate_copy(addr, nbytes);
309 need_2nd_pass = 1;
310 return ACCESS_DENIED;
311 }
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800312
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800313 if (rc < 0) break;
314 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800315 }
316
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800317 try_latest_firmware = 1;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800318 return rc;
319}
320
321
322static int gec_list_ranges(const struct flashchip *flash) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800323 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
324 struct ec_params_flash_region_info p;
325 struct ec_response_flash_region_info r;
326 int rc;
327
328 p.region = EC_FLASH_REGION_WP_RO;
329 rc = priv->ec_command(EC_CMD_FLASH_REGION_INFO,
330 EC_VER_FLASH_REGION_INFO, &p, sizeof(p), &r, sizeof(r));
331 if (rc < 0) {
332 msg_perr("Cannot get the WP_RO region info: %d\n", rc);
333 return 1;
334 }
335
336 msg_pinfo("Supported write protect range:\n");
337 msg_pinfo(" disable: start=0x%06x len=0x%06x\n", 0, 0);
338 msg_pinfo(" enable: start=0x%06x len=0x%06x\n", r.offset, r.size);
339
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800340 return 0;
341}
342
343
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800344/*
345 * Helper function for flash protection.
346 *
347 * On EC API v1, the EC write protection has been simplified to one-bit:
348 * EC_FLASH_PROTECT_RO_AT_BOOT, which means the state is either enabled
349 * or disabled. However, this is different from the SPI-style write protect
350 * behavior. Thus, we re-define the flashrom command (SPI-style) so that
351 * either SRP or range is non-zero, the EC_FLASH_PROTECT_RO_AT_BOOT is set.
352 *
353 * SRP Range | PROTECT_RO_AT_BOOT
354 * 0 0 | 0
355 * 0 non-zero | 1
356 * 1 0 | 1
357 * 1 non-zero | 1
358 *
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800359 */
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800360static int set_wp(int enable) {
361 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
362 struct ec_params_flash_protect p;
363 struct ec_response_flash_protect r;
364 int mask = EC_FLASH_PROTECT_RO_AT_BOOT;
365 int flag = enable ? EC_FLASH_PROTECT_RO_AT_BOOT : 0;
366 int rc;
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800367
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800368 memset(&p, 0, sizeof(p));
369 p.mask = mask;
370 p.flags = flag;
371 rc = priv->ec_command(EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT,
372 &p, sizeof(p), &r, sizeof(r));
373 if (rc < 0) {
374 msg_perr("Cannot set the write protection status: %d\n", rc);
375 return 1;
376 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800377
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800378 /* Read back */
379 memset(&p, 0, sizeof(p));
380 rc = priv->ec_command(EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT,
381 &p, sizeof(p), &r, sizeof(r));
382 if (rc < 0) {
383 msg_perr("Cannot get the write protection status: %d\n", rc);
384 return 1;
385 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800386
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800387 if ((r.flags & mask) != flag) {
388 msg_perr("WP status is not expected (0x%x). actual: 0x%x\n",
389 flag, r.flags & mask);
390 return 1;
391 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800392
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800393 return 0;
394}
395
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800396static int gec_set_range(const struct flashchip *flash,
397 unsigned int start, unsigned int len) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800398 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
399 struct ec_params_flash_region_info p;
400 struct ec_response_flash_region_info r;
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800401 int rc;
402
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800403 /* Check if the given range is supported */
404 p.region = EC_FLASH_REGION_WP_RO;
405 rc = priv->ec_command(EC_CMD_FLASH_REGION_INFO,
406 EC_VER_FLASH_REGION_INFO, &p, sizeof(p), &r, sizeof(r));
407 if (rc < 0) {
408 msg_perr("Cannot get the WP_RO region info: %d\n", rc);
409 return 1;
410 }
411 if ((!start && !len) || /* list supported ranges */
412 ((start == r.offset) && (len == r.size))) {
413 /* pass */
414 } else {
415 msg_perr("Unsupported write protection range (0x%06x,0x%06x)\n",
416 start, len);
417 msg_perr("Currently supported range:\n");
418 msg_perr(" disable: (0x%06x,0x%06x)\n", 0, 0);
419 msg_perr(" enable: (0x%06x,0x%06x)\n", r.offset, r.size);
420 return 1;
421 }
422
423 return set_wp(!!len);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800424}
425
426
427static int gec_enable_writeprotect(const struct flashchip *flash) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800428 return set_wp(1);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800429}
430
431
432static int gec_disable_writeprotect(const struct flashchip *flash) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800433 return set_wp(0);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800434}
435
436
437static int gec_wp_status(const struct flashchip *flash) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800438 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
439 struct ec_params_flash_protect p;
440 struct ec_response_flash_protect r;
441 int start, len; /* wp range */
442 int enabled;
443 int rc;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800444
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800445 memset(&p, 0, sizeof(p));
446 rc = priv->ec_command(EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT,
447 &p, sizeof(p), &r, sizeof(r));
448 if (rc < 0) {
449 msg_perr("Cannot get the write protection status: %d\n", rc);
450 return 1;
451 } else if (rc < sizeof(r)) {
452 msg_perr("Too little data returned (expected:%d, actual:%d)\n",
453 sizeof(r), rc);
454 return 1;
455 }
456
457 start = len = 0;
458 if (r.flags & EC_FLASH_PROTECT_RO_AT_BOOT) {
459 struct ec_params_flash_region_info reg_p;
460 struct ec_response_flash_region_info reg_r;
461
462 msg_pdbg("%s(): EC_FLASH_PROTECT_RO_AT_BOOT is set.\n",
463 __func__);
464 reg_p.region = EC_FLASH_REGION_WP_RO;
465 rc = priv->ec_command(EC_CMD_FLASH_REGION_INFO,
466 EC_VER_FLASH_REGION_INFO,
467 &reg_p, sizeof(reg_p), &reg_r, sizeof(reg_r));
468 if (rc < 0) {
469 msg_perr("Cannot get the WP_RO region info: %d\n", rc);
470 return 1;
471 }
472 start = reg_r.offset;
473 len = reg_r.size;
474 } else {
475 msg_pdbg("%s(): EC_FLASH_PROTECT_RO_AT_BOOT is clear.\n",
476 __func__);
477 }
478
479 /* Remove the SPI-style messages. */
480 enabled = r.flags & EC_FLASH_PROTECT_RO_AT_BOOT ? 1 : 0;
481 msg_pinfo("WP: status: 0x%02x\n", enabled ? 0x80 : 0x00);
482 msg_pinfo("WP: status.srp0: %x\n", enabled);
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800483 msg_pinfo("WP: write protect is %s.\n",
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800484 enabled ? "enabled" : "disabled");
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800485 msg_pinfo("WP: write protect range: start=0x%08x, len=0x%08x\n",
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800486 start, len);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800487
488 return 0;
489}
490
491
David Hendricks7cfbd022012-05-20 17:25:51 -0700492int gec_probe_size(struct flashchip *flash) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800493 int rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800494 struct ec_response_flash_info info;
David Hendricks7cfbd022012-05-20 17:25:51 -0700495 struct gec_priv *priv = (struct gec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800496 struct block_eraser *eraser;
497 static struct wp wp = {
498 .list_ranges = gec_list_ranges,
499 .set_range = gec_set_range,
500 .enable = gec_enable_writeprotect,
501 .disable = gec_disable_writeprotect,
502 .wp_status = gec_wp_status,
503 };
504
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800505 rc = priv->ec_command(EC_CMD_FLASH_INFO, 0,
David Hendricks7cfbd022012-05-20 17:25:51 -0700506 NULL, 0, &info, sizeof(info));
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800507 if (rc < 0) {
508 msg_perr("%s(): FLASH_INFO returns %d.\n", __func__, rc);
509 return 0;
510 }
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800511
512 flash->total_size = info.flash_size / 1024;
David Hendricks0d3fcb52012-07-08 18:37:43 -0700513 flash->page_size = 64;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800514 flash->tested = TEST_OK_PREW;
515 eraser = &flash->block_erasers[0];
516 eraser->eraseblocks[0].size = info.erase_block_size;
517 eraser->eraseblocks[0].count = info.flash_size /
518 eraser->eraseblocks[0].size;
519 flash->wp = &wp;
520
521 return 1;
522};