SPI Flash: Add "sf" command

This adds a new command, "sf" which can be used to manipulate SPI
flash. Currently, initialization, reading, writing and erasing is
supported.

Signed-off-by: Haavard Skinnemoen <haavard.skinnemoen@atmel.com>
diff --git a/common/cmd_sf.c b/common/cmd_sf.c
new file mode 100644
index 0000000..8c0a751
--- /dev/null
+++ b/common/cmd_sf.c
@@ -0,0 +1,191 @@
+/*
+ * Command for accessing SPI flash.
+ *
+ * Copyright (C) 2008 Atmel Corporation
+ */
+#include <common.h>
+#include <spi_flash.h>
+
+#include <asm/io.h>
+
+#ifndef CONFIG_SF_DEFAULT_SPEED
+# define CONFIG_SF_DEFAULT_SPEED	1000000
+#endif
+#ifndef CONFIG_SF_DEFAULT_MODE
+# define CONFIG_SF_DEFAULT_MODE		SPI_MODE_3
+#endif
+
+static struct spi_flash *flash;
+
+static int do_spi_flash_probe(int argc, char *argv[])
+{
+	unsigned int bus = 0;
+	unsigned int cs;
+	unsigned int speed = CONFIG_SF_DEFAULT_SPEED;
+	unsigned int mode = CONFIG_SF_DEFAULT_MODE;
+	char *endp;
+	struct spi_flash *new;
+
+	if (argc < 2)
+		goto usage;
+
+	cs = simple_strtoul(argv[1], &endp, 0);
+	if (*argv[1] == 0 || (*endp != 0 && *endp != ':'))
+		goto usage;
+	if (*endp == ':') {
+		if (endp[1] == 0)
+			goto usage;
+
+		bus = cs;
+		cs = simple_strtoul(endp + 1, &endp, 0);
+		if (*endp != 0)
+			goto usage;
+	}
+
+	if (argc >= 3) {
+		speed = simple_strtoul(argv[2], &endp, 0);
+		if (*argv[2] == 0 || *endp != 0)
+			goto usage;
+	}
+	if (argc >= 4) {
+		mode = simple_strtoul(argv[3], &endp, 0);
+		if (*argv[3] == 0 || *endp != 0)
+			goto usage;
+	}
+
+	new = spi_flash_probe(bus, cs, speed, mode);
+	if (!new) {
+		printf("Failed to initialize SPI flash at %u:%u\n", bus, cs);
+		return 1;
+	}
+
+	if (flash)
+		spi_flash_free(flash);
+	flash = new;
+
+	printf("%u KiB %s at %u:%u is now current device\n",
+			flash->size >> 10, flash->name, bus, cs);
+
+	return 0;
+
+usage:
+	puts("Usage: sf probe [bus:]cs [hz] [mode]\n");
+	return 1;
+}
+
+static int do_spi_flash_read_write(int argc, char *argv[])
+{
+	unsigned long addr;
+	unsigned long offset;
+	unsigned long len;
+	void *buf;
+	char *endp;
+	int ret;
+
+	if (argc < 4)
+		goto usage;
+
+	addr = simple_strtoul(argv[1], &endp, 16);
+	if (*argv[1] == 0 || *endp != 0)
+		goto usage;
+	offset = simple_strtoul(argv[2], &endp, 16);
+	if (*argv[2] == 0 || *endp != 0)
+		goto usage;
+	len = simple_strtoul(argv[3], &endp, 16);
+	if (*argv[3] == 0 || *endp != 0)
+		goto usage;
+
+	buf = map_physmem(addr, len, MAP_WRBACK);
+	if (!buf) {
+		puts("Failed to map physical memory\n");
+		return 1;
+	}
+
+	if (strcmp(argv[0], "read") == 0)
+		ret = spi_flash_read(flash, offset, len, buf);
+	else
+		ret = spi_flash_write(flash, offset, len, buf);
+
+	unmap_physmem(buf, len);
+
+	if (ret) {
+		printf("SPI flash %s failed\n", argv[0]);
+		return 1;
+	}
+
+	return 0;
+
+usage:
+	printf("Usage: sf %s addr offset len\n", argv[0]);
+	return 1;
+}
+
+static int do_spi_flash_erase(int argc, char *argv[])
+{
+	unsigned long offset;
+	unsigned long len;
+	char *endp;
+	int ret;
+
+	if (argc < 3)
+		goto usage;
+
+	offset = simple_strtoul(argv[1], &endp, 16);
+	if (*argv[1] == 0 || *endp != 0)
+		goto usage;
+	len = simple_strtoul(argv[2], &endp, 16);
+	if (*argv[2] == 0 || *endp != 0)
+		goto usage;
+
+	ret = spi_flash_erase(flash, offset, len);
+	if (ret) {
+		printf("SPI flash %s failed\n", argv[0]);
+		return 1;
+	}
+
+	return 0;
+
+usage:
+	puts("Usage: sf erase offset len\n");
+	return 1;
+}
+
+static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+	const char *cmd;
+
+	/* need at least two arguments */
+	if (argc < 2)
+		goto usage;
+
+	cmd = argv[1];
+
+	if (strcmp(cmd, "probe") == 0)
+		return do_spi_flash_probe(argc - 1, argv + 1);
+
+	/* The remaining commands require a selected device */
+	if (!flash) {
+		puts("No SPI flash selected. Please run `sf probe'\n");
+		return 1;
+	}
+
+	if (strcmp(cmd, "read") == 0 || strcmp(cmd, "write") == 0)
+		return do_spi_flash_read_write(argc - 1, argv + 1);
+	if (strcmp(cmd, "erase") == 0)
+		return do_spi_flash_erase(argc - 1, argv + 1);
+
+usage:
+	printf("Usage:\n%s\n", cmdtp->usage);
+	return 1;
+}
+
+U_BOOT_CMD(
+	sf,	5,	1,	do_spi_flash,
+	"sf	- SPI flash sub-system\n",
+	"probe [bus:]cs [hz] [mode]	- init flash device on given SPI bus\n"
+	"				  and chip select\n"
+	"sf read addr offset len 	- read `len' bytes starting at\n"
+	"				  `offset' to memory at `addr'\n"
+	"sf write addr offset len	- write `len' bytes from memory\n"
+	"				  at `addr' to flash at `offset'\n"
+	"sf erase offset len		- erase `len' bytes from `offset'\n");