blob: cfc6d63c8020c99e757bf5356e30ef421c7eccb0 [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 */
David Hendricks14935fe2014-08-14 17:38:24 -070034#include <errno.h>
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080035#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"
David Hendricksa5c5cf82014-08-11 16:40:17 -070041#include "cros_ec.h"
42#include "cros_ec_lock.h"
43#include "cros_ec_commands.h"
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080044#include "programmer.h"
45#include "spi.h"
46#include "writeprotect.h"
47
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +080048/* FIXME: used for wp hacks */
49#include <sys/types.h>
50#include <sys/stat.h>
51#include <fcntl.h>
52#include <unistd.h>
53struct wp_data {
54 int enable;
55 unsigned int start;
56 unsigned int len;
57};
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +080058#define WP_STATE_HACK_FILENAME "/mnt/stateful_partition/flashrom_wp_state"
59
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +080060/* If software sync is enabled, then we don't try the latest firmware copy
61 * after updating.
62 */
63#define SOFTWARE_SYNC_ENABLED
64
David Hendricks14935fe2014-08-14 17:38:24 -070065#define DEV(priv) (priv->dev_index << 14)
66
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080067/* 1 if we want the flashrom to call erase_and_write_flash() again. */
68static int need_2nd_pass = 0;
69
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +080070/* 1 if we want the flashrom to try jumping to new firmware after update. */
71static int try_latest_firmware = 0;
72
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080073/* The range of each firmware copy from the image file to update.
74 * But re-define the .flags as the valid flag to indicate the firmware is
75 * new or not (if flags = 1).
76 */
77static struct fmap_area fwcopy[4]; // [0] is not used.
78
79/* The names of enum lpc_current_image to match in FMAP area names. */
David Hendricksbf8c4dd2012-07-19 12:13:17 -070080static const char *sections[3] = {
81 "UNKNOWN SECTION", // EC_IMAGE_UNKNOWN -- never matches
82 "EC_RO",
83 "EC_RW",
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080084};
85
Simon Glassc453a642013-07-01 18:08:53 +090086/* EC_FLASH_REGION_WP_RO is the highest numbered region so it also indicates
87 * the number of regions */
88static struct ec_response_flash_region_info regions[EC_FLASH_REGION_WP_RO + 1];
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +080089
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080090/* Given the range not able to update, mark the corresponding
91 * firmware as old.
92 */
David Hendricksb907de32014-08-11 16:47:09 -070093static void cros_ec_invalidate_copy(unsigned int addr, unsigned int len)
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080094{
95 int i;
96
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +080097 for (i = EC_IMAGE_RO; i < ARRAY_SIZE(fwcopy); i++) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +080098 struct fmap_area *fw = &fwcopy[i];
99 if ((addr >= fw->offset && (addr < fw->offset + fw->size)) ||
100 (fw->offset >= addr && (fw->offset < addr + len))) {
101 msg_pdbg("Mark firmware [%s] as old.\n",
102 sections[i]);
103 fw->flags = 0; // mark as old
104 }
105 }
106}
107
108
David Hendricksb907de32014-08-11 16:47:09 -0700109static int cros_ec_get_current_image(struct cros_ec_priv *priv)
Simon Glass01c11672013-07-01 18:03:33 +0900110{
111 struct ec_response_get_version resp;
112 int rc;
113
David Hendricks14935fe2014-08-14 17:38:24 -0700114 rc = priv->ec_command(EC_CMD_GET_VERSION | DEV(priv),
115 0, NULL, 0, &resp, sizeof(resp));
Simon Glass01c11672013-07-01 18:03:33 +0900116 if (rc < 0) {
David Hendricksb907de32014-08-11 16:47:09 -0700117 msg_perr("CROS_EC cannot get the running copy: rc=%d\n", rc);
Simon Glass01c11672013-07-01 18:03:33 +0900118 return rc;
119 }
120 if (resp.current_image == EC_IMAGE_UNKNOWN) {
David Hendricksb907de32014-08-11 16:47:09 -0700121 msg_perr("CROS_EC gets unknown running copy\n");
Simon Glass01c11672013-07-01 18:03:33 +0900122 return -1;
123 }
124
125 return resp.current_image;
126}
127
128
David Hendricksb907de32014-08-11 16:47:09 -0700129static int cros_ec_get_region_info(struct cros_ec_priv *priv,
Simon Glass3c01dca2013-07-01 18:07:34 +0900130 enum ec_flash_region region,
131 struct ec_response_flash_region_info *info)
132{
133 struct ec_params_flash_region_info req;
134 struct ec_response_flash_region_info resp;
135 int rc;
136
137 req.region = region;
David Hendricks14935fe2014-08-14 17:38:24 -0700138 rc = priv->ec_command(EC_CMD_FLASH_REGION_INFO | DEV(priv),
Simon Glass3c01dca2013-07-01 18:07:34 +0900139 EC_VER_FLASH_REGION_INFO, &req, sizeof(req),
140 &resp, sizeof(resp));
141 if (rc < 0) {
142 msg_perr("Cannot get the WP_RO region info: %d\n", rc);
143 return rc;
144 }
145
146 info->offset = resp.offset;
147 info->size = resp.size;
148 return 0;
149}
150
David Hendricksf9461c72013-07-11 19:02:13 -0700151/**
152 * Get the versions of the command supported by the EC.
153 *
154 * @param cmd Command
155 * @param pmask Destination for version mask; will be set to 0 on
156 * error.
157 * @return 0 if success, <0 if error
158 */
159static int ec_get_cmd_versions(int cmd, uint32_t *pmask)
160{
David Hendricksb907de32014-08-11 16:47:09 -0700161 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
David Hendricksf9461c72013-07-11 19:02:13 -0700162 struct ec_params_get_cmd_versions pver;
163 struct ec_response_get_cmd_versions rver;
164 int rc;
165
166 *pmask = 0;
167
168 pver.cmd = cmd;
David Hendricks14935fe2014-08-14 17:38:24 -0700169 rc = priv->ec_command(EC_CMD_GET_CMD_VERSIONS | DEV(priv), 0,
David Hendricksf9461c72013-07-11 19:02:13 -0700170 &pver, sizeof(pver), &rver, sizeof(rver));
171
172 if (rc < 0)
173 return rc;
174
175 *pmask = rver.version_mask;
176 return rc;
177}
178
179/**
180 * Return non-zero if the EC supports the command and version
181 *
182 * @param cmd Command to check
183 * @param ver Version to check
184 * @return non-zero if command version supported; 0 if not.
185 */
186static int ec_cmd_version_supported(int cmd, int ver)
187{
188 uint32_t mask = 0;
189 int rc;
190
191 rc = ec_get_cmd_versions(cmd, &mask);
192 if (rc < 0)
193 return rc;
194
195 return (mask & EC_VER_MASK(ver)) ? 1 : 0;
196}
197
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700198/* returns 0 if successful or <0 to indicate error */
199static int set_ideal_write_size(struct cros_ec_priv *priv)
David Hendricksf9461c72013-07-11 19:02:13 -0700200{
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700201 int cmd_version, ret;
David Hendricksf9461c72013-07-11 19:02:13 -0700202
David Hendricksfb405f12014-08-19 22:42:30 -0700203 cmd_version = ec_cmd_version_supported(EC_CMD_FLASH_WRITE,
204 EC_VER_FLASH_WRITE);
205 if (cmd_version < 0) {
206 msg_perr("Cannot determine write command version\n");
207 return cmd_version;
208 } else if (cmd_version == 0) {
209 struct ec_response_flash_info info;
David Hendricksf9461c72013-07-11 19:02:13 -0700210
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700211 ret = priv->ec_command(EC_CMD_FLASH_INFO | DEV(priv),
David Hendricksfb405f12014-08-19 22:42:30 -0700212 cmd_version, NULL, 0, &info, sizeof(info));
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700213 if (ret < 0) {
David Hendricksfb405f12014-08-19 22:42:30 -0700214 msg_perr("%s(): Cannot get flash info.\n", __func__);
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700215 return ret;
David Hendricksfb405f12014-08-19 22:42:30 -0700216 }
217
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700218 priv->ideal_write_size = EC_FLASH_WRITE_VER0_SIZE;
David Hendricksfb405f12014-08-19 22:42:30 -0700219 } else {
220 struct ec_response_flash_info_1 info;
221
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700222 ret = priv->ec_command(EC_CMD_FLASH_INFO | DEV(priv),
David Hendricksfb405f12014-08-19 22:42:30 -0700223 cmd_version, NULL, 0, &info, sizeof(info));
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700224 if (ret < 0) {
David Hendricksfb405f12014-08-19 22:42:30 -0700225 msg_perr("%s(): Cannot get flash info.\n", __func__);
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700226 return ret;
David Hendricksfb405f12014-08-19 22:42:30 -0700227 }
228
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700229 priv->ideal_write_size = info.write_ideal_size;
David Hendricksf9461c72013-07-11 19:02:13 -0700230 }
231
David Hendricksfb405f12014-08-19 22:42:30 -0700232 return 0;
David Hendricksf9461c72013-07-11 19:02:13 -0700233}
Simon Glass3c01dca2013-07-01 18:07:34 +0900234
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800235/* Asks EC to jump to a firmware copy. If target is EC_IMAGE_UNKNOWN,
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800236 * then this functions picks a NEW firmware copy and jumps to it. Note that
237 * RO is preferred, then A, finally B.
238 *
239 * Returns 0 for success.
240 */
David Hendricksb907de32014-08-11 16:47:09 -0700241static int cros_ec_jump_copy(enum ec_current_image target) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800242 struct ec_params_reboot_ec p;
David Hendricksb907de32014-08-11 16:47:09 -0700243 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800244 int rc;
Vadim Bendebury9fa26e82013-09-19 13:56:32 -0700245 int current_image;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800246
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800247 /* Since the EC may return EC_RES_SUCCESS twice if the EC doesn't
248 * jump to different firmware copy. The second EC_RES_SUCCESS would
249 * set the OBF=1 and the next command cannot be executed.
250 * Thus, we call EC to jump only if the target is different.
251 */
David Hendricksb907de32014-08-11 16:47:09 -0700252 current_image = cros_ec_get_current_image(priv);
Vadim Bendebury9fa26e82013-09-19 13:56:32 -0700253 if (current_image < 0)
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800254 return 1;
Vadim Bendebury9fa26e82013-09-19 13:56:32 -0700255 if (current_image == target)
Simon Glassc453a642013-07-01 18:08:53 +0900256 return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800257
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800258 memset(&p, 0, sizeof(p));
Simon Glassc453a642013-07-01 18:08:53 +0900259
260 /* Translate target --> EC reboot command parameter */
261 switch (target) {
262 case EC_IMAGE_RO:
263 p.cmd = EC_REBOOT_JUMP_RO;
264 break;
265 case EC_IMAGE_RW:
266 p.cmd = EC_REBOOT_JUMP_RW;
267 break;
268 default:
269 /*
270 * If target is unspecified, set EC reboot command to use
271 * a new image. Also set "target" so that it may be used
272 * to update the priv->current_image if jump is successful.
273 */
274 if (fwcopy[EC_IMAGE_RO].flags) {
275 p.cmd = EC_REBOOT_JUMP_RO;
276 target = EC_IMAGE_RO;
277 } else if (fwcopy[EC_IMAGE_RW].flags) {
278 p.cmd = EC_REBOOT_JUMP_RW;
279 target = EC_IMAGE_RW;
280 } else {
281 p.cmd = EC_IMAGE_UNKNOWN;
282 }
283 break;
284 }
285
David Hendricksb907de32014-08-11 16:47:09 -0700286 msg_pdbg("CROS_EC is jumping to [%s]\n", sections[p.cmd]);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800287 if (p.cmd == EC_IMAGE_UNKNOWN) return 1;
288
Vadim Bendebury9fa26e82013-09-19 13:56:32 -0700289 if (current_image == p.cmd) {
David Hendricksb907de32014-08-11 16:47:09 -0700290 msg_pdbg("CROS_EC is already in [%s]\n", sections[p.cmd]);
Simon Glassc453a642013-07-01 18:08:53 +0900291 priv->current_image = target;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800292 return 0;
293 }
294
David Hendricks14935fe2014-08-14 17:38:24 -0700295 rc = priv->ec_command(EC_CMD_REBOOT_EC | DEV(priv),
296 0, &p, sizeof(p), NULL, 0);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800297 if (rc < 0) {
David Hendricksb907de32014-08-11 16:47:09 -0700298 msg_perr("CROS_EC cannot jump to [%s]:%d\n",
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800299 sections[p.cmd], rc);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800300 } else {
David Hendricksb907de32014-08-11 16:47:09 -0700301 msg_pdbg("CROS_EC has jumped to [%s]\n", sections[p.cmd]);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800302 rc = EC_RES_SUCCESS;
Simon Glass01c11672013-07-01 18:03:33 +0900303 priv->current_image = target;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800304 }
305
306 /* Sleep 1 sec to wait the EC re-init. */
307 usleep(1000000);
308
David Hendricksf9461c72013-07-11 19:02:13 -0700309 /* update max data write size in case we're jumping to an EC
310 * firmware with different protocol */
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700311 set_ideal_write_size(priv);
David Hendricksf9461c72013-07-11 19:02:13 -0700312
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800313 return rc;
314}
315
316
317/* Given an image, this function parses FMAP and recognize the firmware
318 * ranges.
319 */
David Hendricksb907de32014-08-11 16:47:09 -0700320int cros_ec_prepare(uint8_t *image, int size) {
321 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800322 struct fmap *fmap;
323 int i, j;
324
Louis Yung-Chieh Lo0eaa0ca2012-05-29 15:28:58 +0800325 if (!(priv && priv->detected)) return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800326
327 // Parse the fmap in the image file and cache the firmware ranges.
328 fmap = fmap_find_in_memory(image, size);
329 if (!fmap) return 0;
330
331 // Lookup RO/A/B sections in FMAP.
332 for (i = 0; i < fmap->nareas; i++) {
333 struct fmap_area *fa = &fmap->areas[i];
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800334 for (j = EC_IMAGE_RO; j < ARRAY_SIZE(sections); j++) {
David Hendricks5b06c882012-05-20 18:27:25 -0700335 if (!strcmp(sections[j], (const char *)fa->name)) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800336 msg_pdbg("Found '%s' in image.\n", fa->name);
337 memcpy(&fwcopy[j], fa, sizeof(*fa));
338 fwcopy[j].flags = 1; // mark as new
339 }
340 }
341 }
342
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800343 /* Warning: before update, we jump the EC to RO copy. If you want to
David Hendricksb907de32014-08-11 16:47:09 -0700344 * change this behavior, please also check the cros_ec_finish().
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800345 */
David Hendricksb907de32014-08-11 16:47:09 -0700346 return cros_ec_jump_copy(EC_IMAGE_RO);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800347}
348
349
350/* Returns >0 if we need 2nd pass of erase_and_write_flash().
351 * <0 if we cannot jump to any firmware copy.
352 * ==0 if no more pass is needed.
353 *
354 * This function also jumps to new-updated firmware copy before return >0.
355 */
David Hendricksb907de32014-08-11 16:47:09 -0700356int cros_ec_need_2nd_pass(void) {
357 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
David Hendricks7cfbd022012-05-20 17:25:51 -0700358
Louis Yung-Chieh Lo0eaa0ca2012-05-29 15:28:58 +0800359 if (!(priv && priv->detected)) return 0;
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800360
361 if (need_2nd_pass) {
David Hendricksb907de32014-08-11 16:47:09 -0700362 if (cros_ec_jump_copy(EC_IMAGE_UNKNOWN)) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800363 return -1;
364 }
365 }
366
367 return need_2nd_pass;
368}
369
370
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800371/* Returns 0 for success.
372 *
373 * Try latest firmware: B > A > RO
374 *
David Hendricksb907de32014-08-11 16:47:09 -0700375 * This function assumes the EC jumps to RO at cros_ec_prepare() so that
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800376 * the fwcopy[RO].flags is old (0) and A/B are new. Please also refine
David Hendricksb907de32014-08-11 16:47:09 -0700377 * this code logic if you change the cros_ec_prepare() behavior.
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800378 */
David Hendricksb907de32014-08-11 16:47:09 -0700379int cros_ec_finish(void) {
380 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800381
382 if (!(priv && priv->detected)) return 0;
383
384 if (try_latest_firmware) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800385 if (fwcopy[EC_IMAGE_RW].flags &&
David Hendricksb907de32014-08-11 16:47:09 -0700386 cros_ec_jump_copy(EC_IMAGE_RW) == 0) return 0;
387 return cros_ec_jump_copy(EC_IMAGE_RO);
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800388 }
389
390 return 0;
391}
392
393
David Hendricksb907de32014-08-11 16:47:09 -0700394int cros_ec_read(struct flashchip *flash, uint8_t *readarr,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800395 unsigned int blockaddr, unsigned int readcnt) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800396 int rc = 0;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800397 struct ec_params_flash_read p;
David Hendricksb907de32014-08-11 16:47:09 -0700398 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
David Hendricksd6a0f662012-05-29 14:39:50 -0700399 int maxlen = opaque_programmer->max_data_read;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800400 uint8_t buf[maxlen];
David Hendricks133083b2012-07-17 20:39:38 -0700401 int offset = 0, count;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800402
David Hendricks133083b2012-07-17 20:39:38 -0700403 while (offset < readcnt) {
404 count = min(maxlen, readcnt - offset);
405 p.offset = blockaddr + offset;
406 p.size = count;
David Hendricks14935fe2014-08-14 17:38:24 -0700407 rc = priv->ec_command(EC_CMD_FLASH_READ | DEV(priv),
408 0, &p, sizeof(p), buf, count);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800409 if (rc < 0) {
David Hendricksb907de32014-08-11 16:47:09 -0700410 msg_perr("CROS_EC: Flash read error at offset 0x%x\n",
David Hendricks133083b2012-07-17 20:39:38 -0700411 blockaddr + offset);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800412 return rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800413 } else {
414 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800415 }
416
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800417 memcpy(readarr + offset, buf, count);
David Hendricks133083b2012-07-17 20:39:38 -0700418 offset += count;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800419 }
420
421 return rc;
422}
423
424
Simon Glassc453a642013-07-01 18:08:53 +0900425/*
426 * returns 0 to indicate area does not overlap current EC image
427 * returns 1 to indicate area overlaps current EC image or error
428 */
David Hendricksb907de32014-08-11 16:47:09 -0700429static int in_current_image(struct cros_ec_priv *priv,
Simon Glassc453a642013-07-01 18:08:53 +0900430 unsigned int addr, unsigned int len)
431{
432 int ret;
433 enum ec_current_image image;
434 uint32_t region_offset;
435 uint32_t region_size;
436
437 image = priv->current_image;
438 region_offset = priv->region[image].offset;
439 region_size = priv->region[image].size;
440
441 if ((addr + len - 1 < region_offset) ||
442 (addr > region_offset + region_size - 1)) {
443 return 0;
444 }
445 return 1;
446}
447
448
David Hendricksb907de32014-08-11 16:47:09 -0700449int cros_ec_block_erase(struct flashchip *flash,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800450 unsigned int blockaddr,
451 unsigned int len) {
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800452 struct ec_params_flash_erase erase;
David Hendricksb907de32014-08-11 16:47:09 -0700453 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800454 int rc;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800455
Simon Glassc453a642013-07-01 18:08:53 +0900456 if (in_current_image(priv, blockaddr, len)) {
David Hendricksb907de32014-08-11 16:47:09 -0700457 cros_ec_invalidate_copy(blockaddr, len);
Simon Glassc453a642013-07-01 18:08:53 +0900458 need_2nd_pass = 1;
459 return ACCESS_DENIED;
460 }
461
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800462 erase.offset = blockaddr;
463 erase.size = len;
David Hendricks14935fe2014-08-14 17:38:24 -0700464 rc = priv->ec_command(EC_CMD_FLASH_ERASE | DEV(priv),
465 0, &erase, sizeof(erase), NULL, 0);
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800466 if (rc == -EC_RES_ACCESS_DENIED) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800467 // this is active image.
David Hendricksb907de32014-08-11 16:47:09 -0700468 cros_ec_invalidate_copy(blockaddr, len);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800469 need_2nd_pass = 1;
470 return ACCESS_DENIED;
471 }
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800472 if (rc < 0) {
David Hendricksb907de32014-08-11 16:47:09 -0700473 msg_perr("CROS_EC: Flash erase error at address 0x%x, rc=%d\n",
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800474 blockaddr, rc);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800475 return rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800476 } else {
477 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800478 }
479
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800480#ifndef SOFTWARE_SYNC_ENABLED
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800481 try_latest_firmware = 1;
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800482#endif
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800483 return rc;
484}
485
486
David Hendricksb907de32014-08-11 16:47:09 -0700487int cros_ec_write(struct flashchip *flash, uint8_t *buf, unsigned int addr,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800488 unsigned int nbytes) {
489 int i, rc = 0;
Ken Chang69c31b82014-10-28 15:17:21 +0800490 unsigned int written = 0, real_write_size;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800491 struct ec_params_flash_write p;
David Hendricksb907de32014-08-11 16:47:09 -0700492 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
David Hendricks2d6db772013-07-10 21:07:48 -0700493 uint8_t *packet;
494
Ken Chang69c31b82014-10-28 15:17:21 +0800495 /*
496 * For chrome-os-partner:33035, to workaround the undersized
497 * outdata buffer issue in kernel.
498 */
499 real_write_size = min(opaque_programmer->max_data_write,
500 priv->ideal_write_size);
501 packet = malloc(sizeof(p) + real_write_size);
David Hendricks2d6db772013-07-10 21:07:48 -0700502 if (!packet)
503 return -1;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800504
505 for (i = 0; i < nbytes; i += written) {
Ken Chang69c31b82014-10-28 15:17:21 +0800506 written = min(nbytes - i, real_write_size);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800507 p.offset = addr + i;
508 p.size = written;
Simon Glassc453a642013-07-01 18:08:53 +0900509
510 if (in_current_image(priv, p.offset, p.size)) {
David Hendricksb907de32014-08-11 16:47:09 -0700511 cros_ec_invalidate_copy(addr, nbytes);
Simon Glassc453a642013-07-01 18:08:53 +0900512 need_2nd_pass = 1;
513 return ACCESS_DENIED;
514 }
515
David Hendricks2d6db772013-07-10 21:07:48 -0700516 memcpy(packet, &p, sizeof(p));
517 memcpy(packet + sizeof(p), &buf[i], written);
David Hendricks14935fe2014-08-14 17:38:24 -0700518 rc = priv->ec_command(EC_CMD_FLASH_WRITE | DEV(priv),
519 0, packet, sizeof(p) + p.size, NULL, 0);
David Hendricks2d6db772013-07-10 21:07:48 -0700520
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800521 if (rc == -EC_RES_ACCESS_DENIED) {
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800522 // this is active image.
David Hendricksb907de32014-08-11 16:47:09 -0700523 cros_ec_invalidate_copy(addr, nbytes);
Louis Yung-Chieh Lo8d0971e2012-03-23 00:07:38 +0800524 need_2nd_pass = 1;
525 return ACCESS_DENIED;
526 }
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800527
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800528 if (rc < 0) break;
529 rc = EC_RES_SUCCESS;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800530 }
531
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800532#ifndef SOFTWARE_SYNC_ENABLED
Louis Yung-Chieh Lodeefd822012-07-09 17:07:43 +0800533 try_latest_firmware = 1;
Louis Yung-Chieh Loef88ec32012-09-20 10:39:35 +0800534#endif
David Hendricks2d6db772013-07-10 21:07:48 -0700535 free(packet);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800536 return rc;
537}
538
539
David Hendricksb907de32014-08-11 16:47:09 -0700540static int cros_ec_list_ranges(const struct flashchip *flash) {
541 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
Simon Glass3c01dca2013-07-01 18:07:34 +0900542 struct ec_response_flash_region_info info;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800543 int rc;
544
David Hendricksb907de32014-08-11 16:47:09 -0700545 rc = cros_ec_get_region_info(priv, EC_FLASH_REGION_WP_RO, &info);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800546 if (rc < 0) {
547 msg_perr("Cannot get the WP_RO region info: %d\n", rc);
548 return 1;
549 }
550
551 msg_pinfo("Supported write protect range:\n");
552 msg_pinfo(" disable: start=0x%06x len=0x%06x\n", 0, 0);
Simon Glass3c01dca2013-07-01 18:07:34 +0900553 msg_pinfo(" enable: start=0x%06x len=0x%06x\n", info.offset,
554 info.size);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800555
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800556 return 0;
557}
558
559
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800560/*
561 * Helper function for flash protection.
562 *
563 * On EC API v1, the EC write protection has been simplified to one-bit:
564 * EC_FLASH_PROTECT_RO_AT_BOOT, which means the state is either enabled
565 * or disabled. However, this is different from the SPI-style write protect
566 * behavior. Thus, we re-define the flashrom command (SPI-style) so that
567 * either SRP or range is non-zero, the EC_FLASH_PROTECT_RO_AT_BOOT is set.
568 *
569 * SRP Range | PROTECT_RO_AT_BOOT
570 * 0 0 | 0
571 * 0 non-zero | 1
572 * 1 0 | 1
573 * 1 non-zero | 1
574 *
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800575 *
576 * Besides, to make the protection take effect as soon as possible, we
577 * try to set EC_FLASH_PROTECT_RO_NOW at the same time. However, not
578 * every EC supports RO_NOW, thus we then try to protect the entire chip.
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800579 */
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800580static int set_wp(int enable) {
David Hendricksb907de32014-08-11 16:47:09 -0700581 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800582 struct ec_params_flash_protect p;
583 struct ec_response_flash_protect r;
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800584 const int ro_at_boot_flag = EC_FLASH_PROTECT_RO_AT_BOOT;
585 const int ro_now_flag = EC_FLASH_PROTECT_RO_NOW;
586 int need_an_ec_cold_reset = 0;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800587 int rc;
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800588
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800589 /* Try to set RO_AT_BOOT and RO_NOW first */
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800590 memset(&p, 0, sizeof(p));
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800591 p.mask = (ro_at_boot_flag | ro_now_flag);
592 p.flags = enable ? (ro_at_boot_flag | ro_now_flag) : 0;
David Hendricks14935fe2014-08-14 17:38:24 -0700593 rc = priv->ec_command(EC_CMD_FLASH_PROTECT | DEV(priv),
594 EC_VER_FLASH_PROTECT, &p, sizeof(p), &r, sizeof(r));
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800595 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800596 msg_perr("FAILED: Cannot set the RO_AT_BOOT and RO_NOW: %d\n",
597 rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800598 return 1;
599 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800600
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800601 /* Read back */
602 memset(&p, 0, sizeof(p));
David Hendricks14935fe2014-08-14 17:38:24 -0700603 rc = priv->ec_command(EC_CMD_FLASH_PROTECT | DEV(priv),
604 EC_VER_FLASH_PROTECT, &p, sizeof(p), &r, sizeof(r));
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800605 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800606 msg_perr("FAILED: Cannot get RO_AT_BOOT and RO_NOW: %d\n",
607 rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800608 return 1;
609 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800610
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800611 if (!enable) {
612 /* The disable case is easier to check. */
613 if (r.flags & ro_at_boot_flag) {
614 msg_perr("FAILED: RO_AT_BOOT is not clear.\n");
615 return 1;
616 } else if (r.flags & ro_now_flag) {
617 msg_perr("FAILED: RO_NOW is asserted unexpectedly.\n");
618 need_an_ec_cold_reset = 1;
619 goto exit;
620 }
621
622 msg_pdbg("INFO: RO_AT_BOOT is clear.\n");
623 return 0;
624 }
625
626 /* Check if RO_AT_BOOT is set. If not, fail in anyway. */
627 if (r.flags & ro_at_boot_flag) {
628 msg_pdbg("INFO: RO_AT_BOOT has been set.\n");
629 } else {
630 msg_perr("FAILED: RO_AT_BOOT is not set.\n");
631 return 1;
632 }
633
634 /* Then, we check if the protection has been activated. */
635 if (r.flags & ro_now_flag) {
636 /* Good, RO_NOW is set. */
637 msg_pdbg("INFO: RO_NOW is set. WP is active now.\n");
638 } else if (r.writable_flags & EC_FLASH_PROTECT_ALL_NOW) {
639 struct ec_params_reboot_ec reboot;
640
641 msg_pdbg("WARN: RO_NOW is not set. Trying ALL_NOW.\n");
642
643 memset(&p, 0, sizeof(p));
644 p.mask = EC_FLASH_PROTECT_ALL_NOW;
645 p.flags = EC_FLASH_PROTECT_ALL_NOW;
David Hendricks14935fe2014-08-14 17:38:24 -0700646 rc = priv->ec_command(EC_CMD_FLASH_PROTECT | DEV(priv),
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800647 EC_VER_FLASH_PROTECT,
648 &p, sizeof(p), &r, sizeof(r));
649 if (rc < 0) {
650 msg_perr("FAILED: Cannot set ALL_NOW: %d\n", rc);
651 return 1;
652 }
653
654 /* Read back */
655 memset(&p, 0, sizeof(p));
David Hendricks14935fe2014-08-14 17:38:24 -0700656 rc = priv->ec_command(EC_CMD_FLASH_PROTECT | DEV(priv),
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800657 EC_VER_FLASH_PROTECT,
658 &p, sizeof(p), &r, sizeof(r));
659 if (rc < 0) {
660 msg_perr("FAILED:Cannot get ALL_NOW: %d\n", rc);
661 return 1;
662 }
663
664 if (!(r.flags & EC_FLASH_PROTECT_ALL_NOW)) {
665 msg_perr("FAILED: ALL_NOW is not set.\n");
666 need_an_ec_cold_reset = 1;
667 goto exit;
668 }
669
670 msg_pdbg("INFO: ALL_NOW has been set. WP is active now.\n");
671
672 /*
673 * Our goal is to protect the RO ASAP. The entire protection
674 * is just a workaround for platform not supporting RO_NOW.
675 * It has side-effect that the RW is also protected and leads
676 * the RW update failed. So, we arrange an EC code reset to
677 * unlock RW ASAP.
678 */
679 memset(&reboot, 0, sizeof(reboot));
680 reboot.cmd = EC_REBOOT_COLD;
681 reboot.flags = EC_REBOOT_FLAG_ON_AP_SHUTDOWN;
David Hendricks14935fe2014-08-14 17:38:24 -0700682 rc = priv->ec_command(EC_CMD_REBOOT_EC | DEV(priv),
683 0, &reboot, sizeof(reboot), NULL, 0);
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800684 if (rc < 0) {
685 msg_perr("WARN: Cannot arrange a cold reset at next "
686 "shutdown to unlock entire protect.\n");
687 msg_perr(" But you can do it manually.\n");
688 } else {
689 msg_pdbg("INFO: A cold reset is arranged at next "
690 "shutdown.\n");
691 }
692
693 } else {
694 msg_perr("FAILED: RO_NOW is not set.\n");
695 msg_perr("FAILED: The PROTECT_RO_AT_BOOT is set, but cannot "
696 "make write protection active now.\n");
697 need_an_ec_cold_reset = 1;
698 }
699
700exit:
701 if (need_an_ec_cold_reset) {
702 msg_perr("FAILED: You may need a reboot to take effect of "
703 "PROTECT_RO_AT_BOOT.\n");
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800704 return 1;
705 }
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800706
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800707 return 0;
708}
709
David Hendricksb907de32014-08-11 16:47:09 -0700710static int cros_ec_set_range(const struct flashchip *flash,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800711 unsigned int start, unsigned int len) {
David Hendricksb907de32014-08-11 16:47:09 -0700712 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
Simon Glass3c01dca2013-07-01 18:07:34 +0900713 struct ec_response_flash_region_info info;
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800714 int rc;
715
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800716 /* Check if the given range is supported */
David Hendricksb907de32014-08-11 16:47:09 -0700717 rc = cros_ec_get_region_info(priv, EC_FLASH_REGION_WP_RO, &info);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800718 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800719 msg_perr("FAILED: Cannot get the WP_RO region info: %d\n", rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800720 return 1;
721 }
722 if ((!start && !len) || /* list supported ranges */
Simon Glass3c01dca2013-07-01 18:07:34 +0900723 ((start == info.offset) && (len == info.size))) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800724 /* pass */
725 } else {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800726 msg_perr("FAILED: Unsupported write protection range "
727 "(0x%06x,0x%06x)\n\n", start, len);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800728 msg_perr("Currently supported range:\n");
729 msg_perr(" disable: (0x%06x,0x%06x)\n", 0, 0);
Simon Glass3c01dca2013-07-01 18:07:34 +0900730 msg_perr(" enable: (0x%06x,0x%06x)\n", info.offset,
731 info.size);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800732 return 1;
733 }
734
735 return set_wp(!!len);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800736}
737
738
David Hendricksb907de32014-08-11 16:47:09 -0700739static int cros_ec_enable_writeprotect(const struct flashchip *flash,
David Hendricks1c09f802012-10-03 11:03:48 -0700740 enum wp_mode wp_mode) {
741 int ret;
742
743 switch (wp_mode) {
744 case WP_MODE_HARDWARE:
745 ret = set_wp(1);
746 break;
747 default:
748 msg_perr("%s():%d Unsupported write-protection mode\n",
749 __func__, __LINE__);
750 ret = 1;
751 break;
752 }
753
754 return ret;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800755}
756
757
David Hendricksb907de32014-08-11 16:47:09 -0700758static int cros_ec_disable_writeprotect(const struct flashchip *flash) {
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800759 return set_wp(0);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800760}
761
762
David Hendricksb907de32014-08-11 16:47:09 -0700763static int cros_ec_wp_status(const struct flashchip *flash) {
764 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800765 struct ec_params_flash_protect p;
766 struct ec_response_flash_protect r;
767 int start, len; /* wp range */
768 int enabled;
769 int rc;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800770
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800771 memset(&p, 0, sizeof(p));
David Hendricks14935fe2014-08-14 17:38:24 -0700772 rc = priv->ec_command(EC_CMD_FLASH_PROTECT | DEV(priv),
773 EC_VER_FLASH_PROTECT, &p, sizeof(p), &r, sizeof(r));
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800774 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800775 msg_perr("FAILED: Cannot get the write protection status: %d\n",
776 rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800777 return 1;
778 } else if (rc < sizeof(r)) {
David Hendricksf797dde2012-10-30 11:39:12 -0700779 msg_perr("FAILED: Too little data returned (expected:%zd, "
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800780 "actual:%d)\n", sizeof(r), rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800781 return 1;
782 }
783
784 start = len = 0;
785 if (r.flags & EC_FLASH_PROTECT_RO_AT_BOOT) {
Simon Glass3c01dca2013-07-01 18:07:34 +0900786 struct ec_response_flash_region_info info;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800787
788 msg_pdbg("%s(): EC_FLASH_PROTECT_RO_AT_BOOT is set.\n",
789 __func__);
David Hendricksb907de32014-08-11 16:47:09 -0700790 rc = cros_ec_get_region_info(priv, EC_FLASH_REGION_WP_RO, &info);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800791 if (rc < 0) {
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800792 msg_perr("FAILED: Cannot get the WP_RO region info: "
793 "%d\n", rc);
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800794 return 1;
795 }
Simon Glass3c01dca2013-07-01 18:07:34 +0900796 start = info.offset;
797 len = info.size;
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800798 } else {
799 msg_pdbg("%s(): EC_FLASH_PROTECT_RO_AT_BOOT is clear.\n",
800 __func__);
801 }
802
Louis Yung-Chieh Loca052c42012-08-24 14:12:21 +0800803 /*
804 * If neither RO_NOW or ALL_NOW is set, it means write protect is
805 * NOT active now.
806 */
807 if (!(r.flags & (EC_FLASH_PROTECT_RO_NOW | EC_FLASH_PROTECT_ALL_NOW)))
808 start = len = 0;
809
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800810 /* Remove the SPI-style messages. */
811 enabled = r.flags & EC_FLASH_PROTECT_RO_AT_BOOT ? 1 : 0;
812 msg_pinfo("WP: status: 0x%02x\n", enabled ? 0x80 : 0x00);
813 msg_pinfo("WP: status.srp0: %x\n", enabled);
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800814 msg_pinfo("WP: write protect is %s.\n",
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800815 enabled ? "enabled" : "disabled");
Louis Yung-Chieh Lo05b7a7b2012-08-06 19:10:39 +0800816 msg_pinfo("WP: write protect range: start=0x%08x, len=0x%08x\n",
Louis Yung-Chieh Lo3e6da212012-08-13 17:21:01 +0800817 start, len);
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800818
819 return 0;
820}
821
David Hendrickse5454932013-11-04 18:16:11 -0800822/* perform basic "hello" test to see if we can talk to the EC */
David Hendricksb907de32014-08-11 16:47:09 -0700823int cros_ec_test(struct cros_ec_priv *priv)
David Hendrickse5454932013-11-04 18:16:11 -0800824{
825 struct ec_params_hello request;
826 struct ec_response_hello response;
827 struct ec_response_proto_version proto;
828 int rc = 0;
829
830 /* Say hello to EC. */
831 request.in_data = 0xf0e0d0c0; /* Expect EC will add on 0x01020304. */
832 msg_pdbg("%s: sending HELLO request with 0x%08x\n",
833 __func__, request.in_data);
David Hendricks14935fe2014-08-14 17:38:24 -0700834 rc = priv->ec_command(EC_CMD_HELLO | DEV(priv), 0, &request,
David Hendrickse5454932013-11-04 18:16:11 -0800835 sizeof(request), &response, sizeof(response));
836 msg_pdbg("%s: response: 0x%08x\n", __func__, response.out_data);
837
838 if (rc < 0 || response.out_data != 0xf1e2d3c4) {
839 msg_pdbg("response.out_data is not 0xf1e2d3c4.\n"
840 "rc=%d, request=0x%x response=0x%x\n",
841 rc, request.in_data, response.out_data);
842 return 1;
843 }
844
845 return 0;
846}
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800847
Puthikorn Voravootivatc0993cf2014-08-28 16:04:58 -0700848void cros_ec_set_max_size(struct cros_ec_priv *priv,
849 struct opaque_programmer *op) {
850 struct ec_response_get_protocol_info info;
851 int rc = 0;
852 msg_pdbg("%s: sending protoinfo command\n", __func__);
853 rc = priv->ec_command(EC_CMD_GET_PROTOCOL_INFO | DEV(priv), 0, NULL, 0,
854 &info, sizeof(info));
855 msg_pdbg("%s: rc:%d\n", __func__, rc);
856
857 if (rc == sizeof(info)) {
858 op->max_data_write = min(op->max_data_write,
859 info.max_request_packet_size -
860 sizeof(struct ec_host_request));
861 op->max_data_read = min(op->max_data_read,
862 info.max_response_packet_size -
863 sizeof(struct ec_host_response));
864 msg_pdbg("%s: max_write:%d max_read:%d\n", __func__,
865 op->max_data_write, op->max_data_read);
866 }
867}
868
David Hendricks14935fe2014-08-14 17:38:24 -0700869
870/*
David Hendricks052446b2014-09-11 11:26:51 -0700871 * Returns 0 to indicate success, non-zero otherwise
David Hendricks14935fe2014-08-14 17:38:24 -0700872 *
873 * This function parses programmer parameters from the command line. Since
874 * CrOS EC hangs off the "internal programmer" (AP, PCH, etc) this gets
875 * run during internal programmer initialization.
876 */
877int cros_ec_parse_param(struct cros_ec_priv *priv)
878{
879 char *p;
880
881 p = extract_programmer_param("dev");
882 if (p) {
883 unsigned int index;
884 char *endptr = NULL;
885
886 errno = 0;
887 index = strtoul(p, &endptr, 10);
888 if (errno || (endptr != (p + 1)) || (strlen(p) > 1)) {
889 msg_perr("Invalid argument: \"%s\"\n", p);
890 return 1;
891 }
892
893 if (index > 3) {
894 msg_perr("%s: Invalid device index\n", __func__);
895 return 1;
896 }
897
898 priv->dev_index = index;
899 }
900
Duncan Laurie84328722014-09-10 23:25:01 -0700901 p = extract_programmer_param("block");
902 if (p) {
903 unsigned int block;
904 char *endptr = NULL;
905
906 errno = 0;
907 block = strtoul(p, &endptr, 0);
908 if (errno || (strlen(p) > 10) || (endptr != (p + strlen(p)))) {
909 msg_perr("Invalid argument: \"%s\"\n", p);
910 return 1;
911 }
912
913 if (block <= 0) {
914 msg_perr("%s: Invalid block size\n", __func__);
915 return 1;
916 }
917
918 msg_pdbg("Override block size to 0x%x\n", block);
919 priv->erase_block_size = block;
920 }
921
David Hendricks14935fe2014-08-14 17:38:24 -0700922 return 0;
923}
924
David Hendricksb907de32014-08-11 16:47:09 -0700925int cros_ec_probe_size(struct flashchip *flash) {
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800926 int rc;
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800927 struct ec_response_flash_info info;
David Hendricks194b3bb2013-07-16 14:32:26 -0700928 struct ec_response_get_chip_info chip_info;
David Hendricksb907de32014-08-11 16:47:09 -0700929 struct cros_ec_priv *priv = (struct cros_ec_priv *)opaque_programmer->data;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800930 struct block_eraser *eraser;
931 static struct wp wp = {
David Hendricksb907de32014-08-11 16:47:09 -0700932 .list_ranges = cros_ec_list_ranges,
933 .set_range = cros_ec_set_range,
934 .enable = cros_ec_enable_writeprotect,
935 .disable = cros_ec_disable_writeprotect,
936 .wp_status = cros_ec_wp_status,
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800937 };
938
David Hendricks14935fe2014-08-14 17:38:24 -0700939 if (priv->dev_index > 0) {
940 if (cros_ec_test(priv)) {
941 msg_perr("%s: Failed to say \"hello\" to device %d\n",
942 __func__, priv->dev_index);
943 return 1;
944 }
945 }
946
947 rc = priv->ec_command(EC_CMD_FLASH_INFO | DEV(priv),
948 0, NULL, 0, &info, sizeof(info));
Louis Yung-Chieh Lof779a7b2012-07-30 18:20:39 +0800949 if (rc < 0) {
950 msg_perr("%s(): FLASH_INFO returns %d.\n", __func__, rc);
951 return 0;
952 }
David Hendricksb907de32014-08-11 16:47:09 -0700953 rc = cros_ec_get_current_image(priv);
Simon Glass01c11672013-07-01 18:03:33 +0900954 if (rc < 0) {
955 msg_perr("%s(): Failed to probe (no current image): %d\n",
956 __func__, rc);
957 return 0;
958 }
959 priv->current_image = rc;
Simon Glassc453a642013-07-01 18:08:53 +0900960 priv->region = &regions[0];
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800961
962 flash->total_size = info.flash_size / 1024;
Simon Glass144fc2c2013-07-16 09:33:35 -0600963 flash->page_size = opaque_programmer->max_data_read;
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800964 flash->tested = TEST_OK_PREW;
965 eraser = &flash->block_erasers[0];
Duncan Laurie84328722014-09-10 23:25:01 -0700966
967 /* Allow overriding the erase block size in case EC is incorrect */
968 if (priv->erase_block_size > 0)
969 eraser->eraseblocks[0].size = priv->erase_block_size;
970 else
971 eraser->eraseblocks[0].size = info.erase_block_size;
972
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +0800973 eraser->eraseblocks[0].count = info.flash_size /
974 eraser->eraseblocks[0].size;
975 flash->wp = &wp;
976
David Hendricks194b3bb2013-07-16 14:32:26 -0700977 /*
978 * Some STM32 variants erase bits to 0. For now, assume that this
979 * applies to STM32L parts.
980 *
981 * FIXME: This info will eventually be exposed via some EC command.
982 * See chrome-os-partner:20973.
983 */
David Hendricks14935fe2014-08-14 17:38:24 -0700984 rc = priv->ec_command(EC_CMD_GET_CHIP_INFO | DEV(priv),
985 0, NULL, 0, &chip_info, sizeof(chip_info));
David Hendricks194b3bb2013-07-16 14:32:26 -0700986 if (rc < 0) {
987 msg_perr("%s(): CHIP_INFO returned %d.\n", __func__, rc);
988 return 0;
989 }
990 if (!strncmp(chip_info.name, "stm32l", 6))
991 flash->feature_bits |= FEATURE_ERASE_TO_ZERO;
992
David Hendricksfbd5e6d2014-08-21 15:01:43 -0700993 rc = set_ideal_write_size(priv);
994 if (rc < 0) {
995 msg_perr("%s(): Unable to set write size\n", __func__);
996 return 0;
997 }
David Hendricksf9461c72013-07-11 19:02:13 -0700998
Simon Glassc453a642013-07-01 18:08:53 +0900999 /* FIXME: EC_IMAGE_* is ordered differently from EC_FLASH_REGION_*,
1000 * so we need to be careful about using these enums as array indices */
David Hendricksb907de32014-08-11 16:47:09 -07001001 rc = cros_ec_get_region_info(priv, EC_FLASH_REGION_RO,
Simon Glassc453a642013-07-01 18:08:53 +09001002 &priv->region[EC_IMAGE_RO]);
1003 if (rc) {
1004 msg_perr("%s(): Failed to probe (cannot find RO region): %d\n",
1005 __func__, rc);
1006 return 0;
1007 }
1008
David Hendricksb907de32014-08-11 16:47:09 -07001009 rc = cros_ec_get_region_info(priv, EC_FLASH_REGION_RW,
Simon Glassc453a642013-07-01 18:08:53 +09001010 &priv->region[EC_IMAGE_RW]);
1011 if (rc) {
1012 msg_perr("%s(): Failed to probe (cannot find RW region): %d\n",
1013 __func__, rc);
1014 return 0;
1015 }
1016
Louis Yung-Chieh Loedb0cba2011-12-09 17:06:54 +08001017 return 1;
1018};