Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Command for accessing SPI flash. |
| 3 | * |
| 4 | * Copyright (C) 2008 Atmel Corporation |
Mike Frysinger | 4166ee5 | 2009-10-09 17:12:44 -0400 | [diff] [blame] | 5 | * Licensed under the GPL-2 or later. |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 6 | */ |
Mike Frysinger | 4166ee5 | 2009-10-09 17:12:44 -0400 | [diff] [blame] | 7 | |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 8 | #include <common.h> |
| 9 | #include <spi_flash.h> |
| 10 | |
| 11 | #include <asm/io.h> |
| 12 | |
| 13 | #ifndef CONFIG_SF_DEFAULT_SPEED |
| 14 | # define CONFIG_SF_DEFAULT_SPEED 1000000 |
| 15 | #endif |
| 16 | #ifndef CONFIG_SF_DEFAULT_MODE |
| 17 | # define CONFIG_SF_DEFAULT_MODE SPI_MODE_3 |
| 18 | #endif |
| 19 | |
| 20 | static struct spi_flash *flash; |
| 21 | |
Richard Retanubun | 334eb4b | 2011-02-16 16:39:44 -0500 | [diff] [blame] | 22 | |
| 23 | /* |
| 24 | * This function computes the length argument for the erase command. |
| 25 | * The length on which the command is to operate can be given in two forms: |
| 26 | * 1. <cmd> offset len - operate on <'offset', 'len') |
| 27 | * 2. <cmd> offset +len - operate on <'offset', 'round_up(len)') |
| 28 | * If the second form is used and the length doesn't fall on the |
| 29 | * sector boundary, than it will be adjusted to the next sector boundary. |
| 30 | * If it isn't in the flash, the function will fail (return -1). |
| 31 | * Input: |
| 32 | * arg: length specification (i.e. both command arguments) |
| 33 | * Output: |
| 34 | * len: computed length for operation |
| 35 | * Return: |
| 36 | * 1: success |
| 37 | * -1: failure (bad format, bad address). |
| 38 | */ |
| 39 | static int sf_parse_len_arg(char *arg, ulong *len) |
| 40 | { |
| 41 | char *ep; |
| 42 | char round_up_len; /* indicates if the "+length" form used */ |
| 43 | ulong len_arg; |
| 44 | |
| 45 | round_up_len = 0; |
| 46 | if (*arg == '+') { |
| 47 | round_up_len = 1; |
| 48 | ++arg; |
| 49 | } |
| 50 | |
| 51 | len_arg = simple_strtoul(arg, &ep, 16); |
| 52 | if (ep == arg || *ep != '\0') |
| 53 | return -1; |
| 54 | |
| 55 | if (round_up_len && flash->sector_size > 0) |
| 56 | *len = ROUND(len_arg - 1, flash->sector_size); |
| 57 | else |
| 58 | *len = len_arg; |
| 59 | |
| 60 | return 1; |
| 61 | } |
| 62 | |
Wolfgang Denk | 54841ab | 2010-06-28 22:00:46 +0200 | [diff] [blame] | 63 | static int do_spi_flash_probe(int argc, char * const argv[]) |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 64 | { |
| 65 | unsigned int bus = 0; |
| 66 | unsigned int cs; |
| 67 | unsigned int speed = CONFIG_SF_DEFAULT_SPEED; |
| 68 | unsigned int mode = CONFIG_SF_DEFAULT_MODE; |
| 69 | char *endp; |
| 70 | struct spi_flash *new; |
| 71 | |
| 72 | if (argc < 2) |
Mike Frysinger | 8a07de0 | 2011-04-12 02:21:31 -0400 | [diff] [blame] | 73 | return -1; |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 74 | |
| 75 | cs = simple_strtoul(argv[1], &endp, 0); |
| 76 | if (*argv[1] == 0 || (*endp != 0 && *endp != ':')) |
Mike Frysinger | 8a07de0 | 2011-04-12 02:21:31 -0400 | [diff] [blame] | 77 | return -1; |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 78 | if (*endp == ':') { |
| 79 | if (endp[1] == 0) |
Mike Frysinger | 8a07de0 | 2011-04-12 02:21:31 -0400 | [diff] [blame] | 80 | return -1; |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 81 | |
| 82 | bus = cs; |
| 83 | cs = simple_strtoul(endp + 1, &endp, 0); |
| 84 | if (*endp != 0) |
Mike Frysinger | 8a07de0 | 2011-04-12 02:21:31 -0400 | [diff] [blame] | 85 | return -1; |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 86 | } |
| 87 | |
| 88 | if (argc >= 3) { |
| 89 | speed = simple_strtoul(argv[2], &endp, 0); |
| 90 | if (*argv[2] == 0 || *endp != 0) |
Mike Frysinger | 8a07de0 | 2011-04-12 02:21:31 -0400 | [diff] [blame] | 91 | return -1; |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 92 | } |
| 93 | if (argc >= 4) { |
TsiChung Liew | 6e8d58d | 2009-06-30 14:30:19 +0000 | [diff] [blame] | 94 | mode = simple_strtoul(argv[3], &endp, 16); |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 95 | if (*argv[3] == 0 || *endp != 0) |
Mike Frysinger | 8a07de0 | 2011-04-12 02:21:31 -0400 | [diff] [blame] | 96 | return -1; |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 97 | } |
| 98 | |
| 99 | new = spi_flash_probe(bus, cs, speed, mode); |
| 100 | if (!new) { |
| 101 | printf("Failed to initialize SPI flash at %u:%u\n", bus, cs); |
| 102 | return 1; |
| 103 | } |
| 104 | |
| 105 | if (flash) |
| 106 | spi_flash_free(flash); |
| 107 | flash = new; |
| 108 | |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 109 | return 0; |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 110 | } |
| 111 | |
Wolfgang Denk | 54841ab | 2010-06-28 22:00:46 +0200 | [diff] [blame] | 112 | static int do_spi_flash_read_write(int argc, char * const argv[]) |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 113 | { |
| 114 | unsigned long addr; |
| 115 | unsigned long offset; |
| 116 | unsigned long len; |
| 117 | void *buf; |
| 118 | char *endp; |
| 119 | int ret; |
| 120 | |
| 121 | if (argc < 4) |
Mike Frysinger | 8a07de0 | 2011-04-12 02:21:31 -0400 | [diff] [blame] | 122 | return -1; |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 123 | |
| 124 | addr = simple_strtoul(argv[1], &endp, 16); |
| 125 | if (*argv[1] == 0 || *endp != 0) |
Mike Frysinger | 8a07de0 | 2011-04-12 02:21:31 -0400 | [diff] [blame] | 126 | return -1; |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 127 | offset = simple_strtoul(argv[2], &endp, 16); |
| 128 | if (*argv[2] == 0 || *endp != 0) |
Mike Frysinger | 8a07de0 | 2011-04-12 02:21:31 -0400 | [diff] [blame] | 129 | return -1; |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 130 | len = simple_strtoul(argv[3], &endp, 16); |
| 131 | if (*argv[3] == 0 || *endp != 0) |
Mike Frysinger | 8a07de0 | 2011-04-12 02:21:31 -0400 | [diff] [blame] | 132 | return -1; |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 133 | |
| 134 | buf = map_physmem(addr, len, MAP_WRBACK); |
| 135 | if (!buf) { |
| 136 | puts("Failed to map physical memory\n"); |
| 137 | return 1; |
| 138 | } |
| 139 | |
| 140 | if (strcmp(argv[0], "read") == 0) |
| 141 | ret = spi_flash_read(flash, offset, len, buf); |
| 142 | else |
| 143 | ret = spi_flash_write(flash, offset, len, buf); |
| 144 | |
| 145 | unmap_physmem(buf, len); |
| 146 | |
| 147 | if (ret) { |
| 148 | printf("SPI flash %s failed\n", argv[0]); |
| 149 | return 1; |
| 150 | } |
| 151 | |
| 152 | return 0; |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 153 | } |
| 154 | |
Wolfgang Denk | 54841ab | 2010-06-28 22:00:46 +0200 | [diff] [blame] | 155 | static int do_spi_flash_erase(int argc, char * const argv[]) |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 156 | { |
| 157 | unsigned long offset; |
| 158 | unsigned long len; |
| 159 | char *endp; |
| 160 | int ret; |
| 161 | |
| 162 | if (argc < 3) |
Mike Frysinger | 8a07de0 | 2011-04-12 02:21:31 -0400 | [diff] [blame] | 163 | return -1; |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 164 | |
| 165 | offset = simple_strtoul(argv[1], &endp, 16); |
| 166 | if (*argv[1] == 0 || *endp != 0) |
Mike Frysinger | 8a07de0 | 2011-04-12 02:21:31 -0400 | [diff] [blame] | 167 | return -1; |
Richard Retanubun | 334eb4b | 2011-02-16 16:39:44 -0500 | [diff] [blame] | 168 | |
| 169 | ret = sf_parse_len_arg(argv[2], &len); |
| 170 | if (ret != 1) |
Mike Frysinger | 8a07de0 | 2011-04-12 02:21:31 -0400 | [diff] [blame] | 171 | return -1; |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 172 | |
| 173 | ret = spi_flash_erase(flash, offset, len); |
| 174 | if (ret) { |
| 175 | printf("SPI flash %s failed\n", argv[0]); |
| 176 | return 1; |
| 177 | } |
| 178 | |
| 179 | return 0; |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 180 | } |
| 181 | |
Wolfgang Denk | 54841ab | 2010-06-28 22:00:46 +0200 | [diff] [blame] | 182 | static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 183 | { |
| 184 | const char *cmd; |
Mike Frysinger | 8a07de0 | 2011-04-12 02:21:31 -0400 | [diff] [blame] | 185 | int ret; |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 186 | |
| 187 | /* need at least two arguments */ |
| 188 | if (argc < 2) |
| 189 | goto usage; |
| 190 | |
| 191 | cmd = argv[1]; |
Mike Frysinger | 8a07de0 | 2011-04-12 02:21:31 -0400 | [diff] [blame] | 192 | --argc; |
| 193 | ++argv; |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 194 | |
Mike Frysinger | 8a07de0 | 2011-04-12 02:21:31 -0400 | [diff] [blame] | 195 | if (strcmp(cmd, "probe") == 0) { |
| 196 | ret = do_spi_flash_probe(argc, argv); |
| 197 | goto done; |
| 198 | } |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 199 | |
| 200 | /* The remaining commands require a selected device */ |
| 201 | if (!flash) { |
| 202 | puts("No SPI flash selected. Please run `sf probe'\n"); |
| 203 | return 1; |
| 204 | } |
| 205 | |
| 206 | if (strcmp(cmd, "read") == 0 || strcmp(cmd, "write") == 0) |
Mike Frysinger | 8a07de0 | 2011-04-12 02:21:31 -0400 | [diff] [blame] | 207 | ret = do_spi_flash_read_write(argc, argv); |
| 208 | else if (strcmp(cmd, "erase") == 0) |
| 209 | ret = do_spi_flash_erase(argc, argv); |
| 210 | else |
| 211 | ret = -1; |
| 212 | |
| 213 | done: |
| 214 | if (ret != -1) |
| 215 | return ret; |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 216 | |
| 217 | usage: |
Wolfgang Denk | 47e26b1 | 2010-07-17 01:06:04 +0200 | [diff] [blame] | 218 | return cmd_usage(cmdtp); |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 219 | } |
| 220 | |
| 221 | U_BOOT_CMD( |
| 222 | sf, 5, 1, do_spi_flash, |
Peter Tyser | 2fb2604 | 2009-01-27 18:03:12 -0600 | [diff] [blame] | 223 | "SPI flash sub-system", |
Haavard Skinnemoen | b636846 | 2008-05-16 11:10:34 +0200 | [diff] [blame] | 224 | "probe [bus:]cs [hz] [mode] - init flash device on given SPI bus\n" |
| 225 | " and chip select\n" |
| 226 | "sf read addr offset len - read `len' bytes starting at\n" |
| 227 | " `offset' to memory at `addr'\n" |
| 228 | "sf write addr offset len - write `len' bytes from memory\n" |
| 229 | " at `addr' to flash at `offset'\n" |
Richard Retanubun | 334eb4b | 2011-02-16 16:39:44 -0500 | [diff] [blame] | 230 | "sf erase offset [+]len - erase `len' bytes from `offset'\n" |
| 231 | " `+len' round up `len' to block size" |
Wolfgang Denk | a89c33d | 2009-05-24 17:06:54 +0200 | [diff] [blame] | 232 | ); |