cmd_sf.c 13.7 KB
Newer Older
1 2 3 4
/*
 * Command for accessing SPI flash.
 *
 * Copyright (C) 2008 Atmel Corporation
5 6
 *
 * SPDX-License-Identifier:	GPL-2.0+
7
 */
Mike Frysinger's avatar
Mike Frysinger committed
8

9
#include <common.h>
10
#include <div64.h>
11
#include <dm.h>
12
#include <malloc.h>
13
#include <mapmem.h>
14
#include <spi.h>
15
#include <spi_flash.h>
16 17
#include <jffs2/jffs2.h>
#include <linux/mtd/mtd.h>
18 19

#include <asm/io.h>
20
#include <dm/device-internal.h>
21 22 23

static struct spi_flash *flash;

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
/*
 * This function computes the length argument for the erase command.
 * The length on which the command is to operate can be given in two forms:
 * 1. <cmd> offset len  - operate on <'offset',  'len')
 * 2. <cmd> offset +len - operate on <'offset',  'round_up(len)')
 * If the second form is used and the length doesn't fall on the
 * sector boundary, than it will be adjusted to the next sector boundary.
 * If it isn't in the flash, the function will fail (return -1).
 * Input:
 *    arg: length specification (i.e. both command arguments)
 * Output:
 *    len: computed length for operation
 * Return:
 *    1: success
 *   -1: failure (bad format, bad address).
 */
static int sf_parse_len_arg(char *arg, ulong *len)
{
	char *ep;
	char round_up_len; /* indicates if the "+length" form used */
	ulong len_arg;

	round_up_len = 0;
	if (*arg == '+') {
		round_up_len = 1;
		++arg;
	}

	len_arg = simple_strtoul(arg, &ep, 16);
	if (ep == arg || *ep != '\0')
		return -1;

	if (round_up_len && flash->sector_size > 0)
57
		*len = ROUND(len_arg, flash->sector_size);
58 59 60 61 62 63
	else
		*len = len_arg;

	return 1;
}

64 65 66 67 68 69 70 71 72 73 74 75
/**
 * This function takes a byte length and a delta unit of time to compute the
 * approximate bytes per second
 *
 * @param len		amount of bytes currently processed
 * @param start_ms	start time of processing in ms
 * @return bytes per second if OK, 0 on error
 */
static ulong bytes_per_second(unsigned int len, ulong start_ms)
{
	/* less accurate but avoids overflow */
	if (len >= ((unsigned int) -1) / 1024)
76
		return len / (max(get_timer(start_ms) / 1024, 1UL));
77
	else
78
		return 1024 * len / max(get_timer(start_ms), 1UL);
79 80
}

81
static int do_spi_flash_probe(int argc, char * const argv[])
82
{
83 84
	unsigned int bus = CONFIG_SF_DEFAULT_BUS;
	unsigned int cs = CONFIG_SF_DEFAULT_CS;
85 86 87
	unsigned int speed = CONFIG_SF_DEFAULT_SPEED;
	unsigned int mode = CONFIG_SF_DEFAULT_MODE;
	char *endp;
88 89 90 91
#ifdef CONFIG_DM_SPI_FLASH
	struct udevice *new, *bus_dev;
	int ret;
#else
92
	struct spi_flash *new;
93
#endif
94

95 96 97
	if (argc >= 2) {
		cs = simple_strtoul(argv[1], &endp, 0);
		if (*argv[1] == 0 || (*endp != 0 && *endp != ':'))
98
			return -1;
99 100 101 102 103 104 105 106 107
		if (*endp == ':') {
			if (endp[1] == 0)
				return -1;

			bus = cs;
			cs = simple_strtoul(endp + 1, &endp, 0);
			if (*endp != 0)
				return -1;
		}
108 109 110 111 112
	}

	if (argc >= 3) {
		speed = simple_strtoul(argv[2], &endp, 0);
		if (*argv[2] == 0 || *endp != 0)
113
			return -1;
114 115
	}
	if (argc >= 4) {
116
		mode = simple_strtoul(argv[3], &endp, 16);
117
		if (*argv[3] == 0 || *endp != 0)
118
			return -1;
119 120
	}

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
#ifdef CONFIG_DM_SPI_FLASH
	/* Remove the old device, otherwise probe will just be a nop */
	ret = spi_find_bus_and_cs(bus, cs, &bus_dev, &new);
	if (!ret) {
		device_remove(new);
		device_unbind(new);
	}
	flash = NULL;
	ret = spi_flash_probe_bus_cs(bus, cs, speed, mode, &new);
	if (ret) {
		printf("Failed to initialize SPI flash at %u:%u (error %d)\n",
		       bus, cs, ret);
		return 1;
	}

136
	flash = dev_get_uclass_priv(new);
137
#else
138 139 140
	if (flash)
		spi_flash_free(flash);

141
	new = spi_flash_probe(bus, cs, speed, mode);
142 143
	flash = new;

144 145 146 147 148 149
	if (!new) {
		printf("Failed to initialize SPI flash at %u:%u\n", bus, cs);
		return 1;
	}

	flash = new;
150
#endif
151 152 153 154

	return 0;
}

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
/**
 * Write a block of data to SPI flash, first checking if it is different from
 * what is already there.
 *
 * If the data being written is the same, then *skipped is incremented by len.
 *
 * @param flash		flash context pointer
 * @param offset	flash offset to write
 * @param len		number of bytes to write
 * @param buf		buffer to write from
 * @param cmp_buf	read buffer to use to compare data
 * @param skipped	Count of skipped data (incremented by this function)
 * @return NULL if OK, else a string containing the stage which failed
 */
static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset,
		size_t len, const char *buf, char *cmp_buf, size_t *skipped)
{
172 173
	char *ptr = (char *)buf;

174
	debug("offset=%#x, sector_size=%#x, len=%#zx\n",
175
	      offset, flash->sector_size, len);
176 177
	/* Read the entire sector so to allow for rewriting */
	if (spi_flash_read(flash, offset, flash->sector_size, cmp_buf))
178
		return "read";
179
	/* Compare only what is meaningful (len) */
180
	if (memcmp(cmp_buf, buf, len) == 0) {
181
		debug("Skip region %x size %zx: no change\n",
182
		      offset, len);
183 184 185
		*skipped += len;
		return NULL;
	}
186 187
	/* Erase the entire sector */
	if (spi_flash_erase(flash, offset, flash->sector_size))
188
		return "erase";
189
	/* If it's a partial sector, copy the data into the temp-buffer */
190
	if (len != flash->sector_size) {
191 192
		memcpy(cmp_buf, buf, len);
		ptr = cmp_buf;
193
	}
194 195 196
	/* Write one complete sector */
	if (spi_flash_write(flash, offset, flash->sector_size, ptr))
		return "write";
197

198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
	return NULL;
}

/**
 * Update an area of SPI flash by erasing and writing any blocks which need
 * to change. Existing blocks with the correct data are left unchanged.
 *
 * @param flash		flash context pointer
 * @param offset	flash offset to write
 * @param len		number of bytes to write
 * @param buf		buffer to write from
 * @return 0 if ok, 1 on error
 */
static int spi_flash_update(struct spi_flash *flash, u32 offset,
		size_t len, const char *buf)
{
	const char *err_oper = NULL;
	char *cmp_buf;
	const char *end = buf + len;
	size_t todo;		/* number of bytes to do in this pass */
Kumar Gala's avatar
Kumar Gala committed
218
	size_t skipped = 0;	/* statistics */
219 220 221 222
	const ulong start_time = get_timer(0);
	size_t scale = 1;
	const char *start_buf = buf;
	ulong delta;
223

224 225
	if (end - buf >= 200)
		scale = (end - buf) / 100;
226
	cmp_buf = memalign(ARCH_DMA_MINALIGN, flash->sector_size);
227
	if (cmp_buf) {
228 229
		ulong last_update = get_timer(0);

Kumar Gala's avatar
Kumar Gala committed
230
		for (; buf < end && !err_oper; buf += todo, offset += todo) {
231
			todo = min_t(size_t, end - buf, flash->sector_size);
232 233
			if (get_timer(last_update) > 100) {
				printf("   \rUpdating, %zu%% %lu B/s",
234
				       100 - (end - buf) / scale,
235 236 237 238
					bytes_per_second(buf - start_buf,
							 start_time));
				last_update = get_timer(0);
			}
239 240 241 242 243 244 245
			err_oper = spi_flash_update_block(flash, offset, todo,
					buf, cmp_buf, &skipped);
		}
	} else {
		err_oper = "malloc";
	}
	free(cmp_buf);
246
	putc('\r');
247 248 249 250
	if (err_oper) {
		printf("SPI flash failed in %s step\n", err_oper);
		return 1;
	}
251 252 253

	delta = get_timer(start_time);
	printf("%zu bytes written, %zu bytes skipped", len - skipped,
254
	       skipped);
255
	printf(" in %ld.%lds, speed %ld B/s\n",
256
	       delta / 1000, delta % 1000, bytes_per_second(len, start_time));
Kumar Gala's avatar
Kumar Gala committed
257

258 259 260
	return 0;
}

261
static int do_spi_flash_read_write(int argc, char * const argv[])
262 263 264 265
{
	unsigned long addr;
	void *buf;
	char *endp;
266
	int ret = 1;
267 268
	int dev = 0;
	loff_t offset, len, maxsize;
269

270
	if (argc < 3)
271
		return -1;
272 273 274

	addr = simple_strtoul(argv[1], &endp, 16);
	if (*argv[1] == 0 || *endp != 0)
275
		return -1;
276 277 278

	if (mtd_arg_off_size(argc - 2, &argv[2], &dev, &offset, &len,
			     &maxsize, MTD_DEV_TYPE_NOR, flash->size))
279
		return -1;
280

281 282 283
	/* Consistency checking */
	if (offset + len > flash->size) {
		printf("ERROR: attempting %s past flash size (%#x)\n",
284
		       argv[0], flash->size);
285 286 287
		return 1;
	}

288 289 290 291 292 293
	buf = map_physmem(addr, len, MAP_WRBACK);
	if (!buf) {
		puts("Failed to map physical memory\n");
		return 1;
	}

294
	if (strcmp(argv[0], "update") == 0) {
295
		ret = spi_flash_update(flash, offset, len, buf);
296
	} else if (strncmp(argv[0], "read", 4) == 0 ||
297 298 299 300 301 302 303 304 305
			strncmp(argv[0], "write", 5) == 0) {
		int read;

		read = strncmp(argv[0], "read", 4) == 0;
		if (read)
			ret = spi_flash_read(flash, offset, len, buf);
		else
			ret = spi_flash_write(flash, offset, len, buf);

306 307 308 309 310 311
		printf("SF: %zu bytes @ %#x %s: ", (size_t)len, (u32)offset,
		       read ? "Read" : "Written");
		if (ret)
			printf("ERROR %d\n", ret);
		else
			printf("OK\n");
312
	}
313 314 315

	unmap_physmem(buf, len);

316
	return ret == 0 ? 0 : 1;
317 318
}

319
static int do_spi_flash_erase(int argc, char * const argv[])
320 321
{
	int ret;
322 323 324
	int dev = 0;
	loff_t offset, len, maxsize;
	ulong size;
325 326

	if (argc < 3)
327
		return -1;
328

329 330
	if (mtd_arg_off(argv[1], &dev, &offset, &len, &maxsize,
			MTD_DEV_TYPE_NOR, flash->size))
331
		return -1;
332

333
	ret = sf_parse_len_arg(argv[2], &size);
334
	if (ret != 1)
335
		return -1;
336

337
	/* Consistency checking */
338
	if (offset + size > flash->size) {
339
		printf("ERROR: attempting %s past flash size (%#x)\n",
340
		       argv[0], flash->size);
341 342 343
		return 1;
	}

344 345
	ret = spi_flash_erase(flash, offset, size);
	printf("SF: %zu bytes @ %#x Erased: %s\n", (size_t)size, (u32)offset,
346
	       ret ? "ERROR" : "OK");
347

348
	return ret == 0 ? 0 : 1;
349 350
}

Simon Glass's avatar
Simon Glass committed
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
#ifdef CONFIG_CMD_SF_TEST
enum {
	STAGE_ERASE,
	STAGE_CHECK,
	STAGE_WRITE,
	STAGE_READ,

	STAGE_COUNT,
};

static char *stage_name[STAGE_COUNT] = {
	"erase",
	"check",
	"write",
	"read",
};

struct test_info {
	int stage;
	int bytes;
	unsigned base_ms;
	unsigned time_ms[STAGE_COUNT];
};

static void show_time(struct test_info *test, int stage)
{
	uint64_t speed;	/* KiB/s */
	int bps;	/* Bits per second */

	speed = (long long)test->bytes * 1000;
381 382
	if (test->time_ms[stage])
		do_div(speed, test->time_ms[stage] * 1024);
Simon Glass's avatar
Simon Glass committed
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
	bps = speed * 8;

	printf("%d %s: %d ticks, %d KiB/s %d.%03d Mbps\n", stage,
	       stage_name[stage], test->time_ms[stage],
	       (int)speed, bps / 1000, bps % 1000);
}

static void spi_test_next_stage(struct test_info *test)
{
	test->time_ms[test->stage] = get_timer(test->base_ms);
	show_time(test, test->stage);
	test->base_ms = get_timer(0);
	test->stage++;
}

/**
 * Run a test on the SPI flash
 *
 * @param flash		SPI flash to use
 * @param buf		Source buffer for data to write
 * @param len		Size of data to read/write
 * @param offset	Offset within flash to check
 * @param vbuf		Verification buffer
 * @return 0 if ok, -1 on error
 */
408 409
static int spi_flash_test(struct spi_flash *flash, uint8_t *buf, ulong len,
			   ulong offset, uint8_t *vbuf)
Simon Glass's avatar
Simon Glass committed
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
{
	struct test_info test;
	int i;

	printf("SPI flash test:\n");
	memset(&test, '\0', sizeof(test));
	test.base_ms = get_timer(0);
	test.bytes = len;
	if (spi_flash_erase(flash, offset, len)) {
		printf("Erase failed\n");
		return -1;
	}
	spi_test_next_stage(&test);

	if (spi_flash_read(flash, offset, len, vbuf)) {
		printf("Check read failed\n");
		return -1;
	}
	for (i = 0; i < len; i++) {
		if (vbuf[i] != 0xff) {
			printf("Check failed at %d\n", i);
431 432
			print_buffer(i, vbuf + i, 1,
				     min_t(uint, len - i, 0x40), 0);
Simon Glass's avatar
Simon Glass committed
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
			return -1;
		}
	}
	spi_test_next_stage(&test);

	if (spi_flash_write(flash, offset, len, buf)) {
		printf("Write failed\n");
		return -1;
	}
	memset(vbuf, '\0', len);
	spi_test_next_stage(&test);

	if (spi_flash_read(flash, offset, len, vbuf)) {
		printf("Read failed\n");
		return -1;
	}
	spi_test_next_stage(&test);

	for (i = 0; i < len; i++) {
		if (buf[i] != vbuf[i]) {
			printf("Verify failed at %d, good data:\n", i);
454 455
			print_buffer(i, buf + i, 1,
				     min_t(uint, len - i, 0x40), 0);
Simon Glass's avatar
Simon Glass committed
456
			printf("Bad data:\n");
457 458
			print_buffer(i, vbuf + i, 1,
				     min_t(uint, len - i, 0x40), 0);
Simon Glass's avatar
Simon Glass committed
459 460 461 462 463 464 465 466 467 468 469 470 471 472
			return -1;
		}
	}
	printf("Test passed\n");
	for (i = 0; i < STAGE_COUNT; i++)
		show_time(&test, i);

	return 0;
}

static int do_spi_flash_test(int argc, char * const argv[])
{
	unsigned long offset;
	unsigned long len;
473
	uint8_t *buf, *from;
Simon Glass's avatar
Simon Glass committed
474
	char *endp;
475
	uint8_t *vbuf;
Simon Glass's avatar
Simon Glass committed
476 477
	int ret;

478 479
	if (argc < 3)
		return -1;
Simon Glass's avatar
Simon Glass committed
480 481 482 483 484 485 486
	offset = simple_strtoul(argv[1], &endp, 16);
	if (*argv[1] == 0 || *endp != 0)
		return -1;
	len = simple_strtoul(argv[2], &endp, 16);
	if (*argv[2] == 0 || *endp != 0)
		return -1;

487
	vbuf = memalign(ARCH_DMA_MINALIGN, len);
Simon Glass's avatar
Simon Glass committed
488
	if (!vbuf) {
489
		printf("Cannot allocate memory (%lu bytes)\n", len);
Simon Glass's avatar
Simon Glass committed
490 491
		return 1;
	}
492
	buf = memalign(ARCH_DMA_MINALIGN, len);
Simon Glass's avatar
Simon Glass committed
493 494
	if (!buf) {
		free(vbuf);
495
		printf("Cannot allocate memory (%lu bytes)\n", len);
Simon Glass's avatar
Simon Glass committed
496 497 498
		return 1;
	}

499 500
	from = map_sysmem(CONFIG_SYS_TEXT_BASE, 0);
	memcpy(buf, from, len);
Simon Glass's avatar
Simon Glass committed
501 502 503 504 505 506 507 508 509 510 511 512
	ret = spi_flash_test(flash, buf, len, offset, vbuf);
	free(vbuf);
	free(buf);
	if (ret) {
		printf("Test failed\n");
		return 1;
	}

	return 0;
}
#endif /* CONFIG_CMD_SF_TEST */

513 514
static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc,
			char * const argv[])
515 516
{
	const char *cmd;
517
	int ret;
518 519 520 521 522 523

	/* need at least two arguments */
	if (argc < 2)
		goto usage;

	cmd = argv[1];
524 525
	--argc;
	++argv;
526

527 528 529 530
	if (strcmp(cmd, "probe") == 0) {
		ret = do_spi_flash_probe(argc, argv);
		goto done;
	}
531 532 533 534 535 536 537

	/* The remaining commands require a selected device */
	if (!flash) {
		puts("No SPI flash selected. Please run `sf probe'\n");
		return 1;
	}

538 539
	if (strcmp(cmd, "read") == 0 || strcmp(cmd, "write") == 0 ||
	    strcmp(cmd, "update") == 0)
540 541 542
		ret = do_spi_flash_read_write(argc, argv);
	else if (strcmp(cmd, "erase") == 0)
		ret = do_spi_flash_erase(argc, argv);
Simon Glass's avatar
Simon Glass committed
543 544 545 546
#ifdef CONFIG_CMD_SF_TEST
	else if (!strcmp(cmd, "test"))
		ret = do_spi_flash_test(argc, argv);
#endif
547 548 549 550 551 552
	else
		ret = -1;

done:
	if (ret != -1)
		return ret;
553 554

usage:
555
	return CMD_RET_USAGE;
556 557
}

Simon Glass's avatar
Simon Glass committed
558 559 560 561 562 563 564
#ifdef CONFIG_CMD_SF_TEST
#define SF_TEST_HELP "\nsf test offset len		" \
		"- run a very basic destructive test"
#else
#define SF_TEST_HELP
#endif

565 566
U_BOOT_CMD(
	sf,	5,	1,	do_spi_flash,
Peter Tyser's avatar
Peter Tyser committed
567
	"SPI flash sub-system",
568
	"probe [[bus:]cs] [hz] [mode]	- init flash device on given SPI bus\n"
569
	"				  and chip select\n"
570 571 572 573 574 575 576 577 578 579 580 581
	"sf read addr offset|partition len	- read `len' bytes starting at\n"
	"				          `offset' or from start of mtd\n"
	"					  `partition'to memory at `addr'\n"
	"sf write addr offset|partition len	- write `len' bytes from memory\n"
	"				          at `addr' to flash at `offset'\n"
	"					  or to start of mtd `partition'\n"
	"sf erase offset|partition [+]len	- erase `len' bytes from `offset'\n"
	"					  or from start of mtd `partition'\n"
	"					 `+len' round up `len' to block size\n"
	"sf update addr offset|partition len	- erase and write `len' bytes from memory\n"
	"					  at `addr' to flash at `offset'\n"
	"					  or to start of mtd `partition'\n"
Simon Glass's avatar
Simon Glass committed
582
	SF_TEST_HELP
Wolfgang Denk's avatar
Wolfgang Denk committed
583
);