Sheetal Tigadoli | 6c4db5d | 2020-01-06 00:08:24 +0530 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2019-2020, Broadcom |
| 3 | * |
| 4 | * SPDX-License-Identifier: BSD-3-Clause |
| 5 | */ |
| 6 | |
| 7 | #include <stdbool.h> |
| 8 | #include <stddef.h> |
| 9 | #include <stdint.h> |
| 10 | |
| 11 | #include <common/debug.h> |
| 12 | #include <drivers/delay_timer.h> |
| 13 | #include <errno.h> |
| 14 | |
| 15 | #include <sf.h> |
| 16 | #include <spi.h> |
| 17 | |
| 18 | #define SPI_FLASH_CMD_LEN 4 |
| 19 | #define QSPI_WAIT_TIMEOUT_US 200000U /* usec */ |
| 20 | |
| 21 | #define FINFO(jedec_id, ext_id, _sector_size, _n_sectors, _page_size, _flags) \ |
| 22 | .id = { \ |
| 23 | ((jedec_id) >> 16) & 0xff, \ |
| 24 | ((jedec_id) >> 8) & 0xff, \ |
| 25 | (jedec_id) & 0xff, \ |
| 26 | ((ext_id) >> 8) & 0xff, \ |
| 27 | (ext_id) & 0xff, \ |
| 28 | }, \ |
| 29 | .id_len = (!(jedec_id) ? 0 : (3 + ((ext_id) ? 2 : 0))), \ |
| 30 | .sector_size = (_sector_size), \ |
| 31 | .n_sectors = (_n_sectors), \ |
| 32 | .page_size = _page_size, \ |
| 33 | .flags = (_flags), |
| 34 | |
| 35 | /* SPI/QSPI flash device params structure */ |
| 36 | const struct spi_flash_info spi_flash_ids[] = { |
| 37 | {"W25Q64CV", FINFO(0xef4017, 0x0, 64 * 1024, 128, 256, WR_QPP | SECT_4K)}, |
| 38 | {"W25Q64DW", FINFO(0xef6017, 0x0, 64 * 1024, 128, 256, WR_QPP | SECT_4K)}, |
| 39 | {"W25Q32", FINFO(0xef4016, 0x0, 64 * 1024, 64, 256, SECT_4K)}, |
| 40 | {"MX25l3205D", FINFO(0xc22016, 0x0, 64 * 1024, 64, 256, SECT_4K)}, |
| 41 | }; |
| 42 | |
| 43 | static void spi_flash_addr(uint32_t addr, uint8_t *cmd) |
| 44 | { |
| 45 | /* |
| 46 | * cmd[0] holds a SPI Flash command, stored earlier |
| 47 | * cmd[1/2/3] holds 24bit flash address |
| 48 | */ |
| 49 | cmd[1] = addr >> 16; |
| 50 | cmd[2] = addr >> 8; |
| 51 | cmd[3] = addr >> 0; |
| 52 | } |
| 53 | |
| 54 | static const struct spi_flash_info *spi_flash_read_id(void) |
| 55 | { |
| 56 | const struct spi_flash_info *info; |
| 57 | uint8_t id[SPI_FLASH_MAX_ID_LEN]; |
| 58 | int ret; |
| 59 | |
| 60 | ret = spi_flash_cmd(CMD_READ_ID, id, SPI_FLASH_MAX_ID_LEN); |
| 61 | if (ret < 0) { |
| 62 | ERROR("SF: Error %d reading JEDEC ID\n", ret); |
| 63 | return NULL; |
| 64 | } |
| 65 | |
| 66 | for (info = spi_flash_ids; info->name != NULL; info++) { |
| 67 | if (info->id_len) { |
| 68 | if (!memcmp(info->id, id, info->id_len)) |
| 69 | return info; |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | printf("SF: unrecognized JEDEC id bytes: %02x, %02x, %02x\n", |
| 74 | id[0], id[1], id[2]); |
| 75 | return NULL; |
| 76 | } |
| 77 | |
| 78 | /* Enable writing on the SPI flash */ |
| 79 | static inline int spi_flash_cmd_write_enable(struct spi_flash *flash) |
| 80 | { |
| 81 | return spi_flash_cmd(CMD_WRITE_ENABLE, NULL, 0); |
| 82 | } |
| 83 | |
| 84 | static int spi_flash_cmd_wait(struct spi_flash *flash) |
| 85 | { |
| 86 | uint8_t cmd; |
| 87 | uint32_t i; |
| 88 | uint8_t status; |
| 89 | int ret; |
| 90 | |
| 91 | i = 0; |
| 92 | while (1) { |
| 93 | cmd = CMD_RDSR; |
| 94 | ret = spi_flash_cmd_read(&cmd, 1, &status, 1); |
| 95 | if (ret < 0) { |
| 96 | ERROR("SF: cmd wait failed\n"); |
| 97 | break; |
| 98 | } |
| 99 | if (!(status & STATUS_WIP)) |
| 100 | break; |
| 101 | |
| 102 | i++; |
| 103 | if (i >= QSPI_WAIT_TIMEOUT_US) { |
| 104 | ERROR("SF: cmd wait timeout\n"); |
| 105 | ret = -1; |
| 106 | break; |
| 107 | } |
| 108 | udelay(1); |
| 109 | } |
| 110 | |
| 111 | return ret; |
| 112 | } |
| 113 | |
| 114 | static int spi_flash_write_common(struct spi_flash *flash, const uint8_t *cmd, |
| 115 | size_t cmd_len, const void *buf, |
| 116 | size_t buf_len) |
| 117 | { |
| 118 | int ret; |
| 119 | |
| 120 | ret = spi_flash_cmd_write_enable(flash); |
| 121 | if (ret < 0) { |
| 122 | ERROR("SF: enabling write failed\n"); |
| 123 | return ret; |
| 124 | } |
| 125 | |
| 126 | ret = spi_flash_cmd_write(cmd, cmd_len, buf, buf_len); |
| 127 | if (ret < 0) { |
| 128 | ERROR("SF: write cmd failed\n"); |
| 129 | return ret; |
| 130 | } |
| 131 | |
| 132 | ret = spi_flash_cmd_wait(flash); |
| 133 | if (ret < 0) { |
| 134 | ERROR("SF: write timed out\n"); |
| 135 | return ret; |
| 136 | } |
| 137 | |
| 138 | return ret; |
| 139 | } |
| 140 | |
| 141 | static int spi_flash_read_common(const uint8_t *cmd, size_t cmd_len, |
| 142 | void *data, size_t data_len) |
| 143 | { |
| 144 | int ret; |
| 145 | |
| 146 | ret = spi_flash_cmd_read(cmd, cmd_len, data, data_len); |
| 147 | if (ret < 0) { |
| 148 | ERROR("SF: read cmd failed\n"); |
| 149 | return ret; |
| 150 | } |
| 151 | |
| 152 | return ret; |
| 153 | } |
| 154 | |
| 155 | int spi_flash_read(struct spi_flash *flash, uint32_t offset, |
| 156 | uint32_t len, void *data) |
| 157 | { |
| 158 | uint32_t read_len = 0, read_addr; |
| 159 | uint8_t cmd[SPI_FLASH_CMD_LEN]; |
| 160 | int ret; |
| 161 | |
| 162 | ret = spi_claim_bus(); |
| 163 | if (ret) { |
| 164 | ERROR("SF: unable to claim SPI bus\n"); |
| 165 | return ret; |
| 166 | } |
| 167 | |
| 168 | cmd[0] = CMD_READ_NORMAL; |
| 169 | while (len) { |
| 170 | read_addr = offset; |
| 171 | read_len = MIN(flash->page_size, (len - read_len)); |
| 172 | spi_flash_addr(read_addr, cmd); |
| 173 | |
| 174 | ret = spi_flash_read_common(cmd, sizeof(cmd), data, read_len); |
| 175 | if (ret < 0) { |
| 176 | ERROR("SF: read failed\n"); |
| 177 | break; |
| 178 | } |
| 179 | |
| 180 | offset += read_len; |
| 181 | len -= read_len; |
| 182 | data += read_len; |
| 183 | } |
| 184 | SPI_DEBUG("SF read done\n"); |
| 185 | |
| 186 | spi_release_bus(); |
| 187 | return ret; |
| 188 | } |
| 189 | |
| 190 | int spi_flash_write(struct spi_flash *flash, uint32_t offset, |
| 191 | uint32_t len, void *buf) |
| 192 | { |
| 193 | unsigned long byte_addr, page_size; |
| 194 | uint8_t cmd[SPI_FLASH_CMD_LEN]; |
| 195 | uint32_t chunk_len, actual; |
| 196 | uint32_t write_addr; |
| 197 | int ret; |
| 198 | |
| 199 | ret = spi_claim_bus(); |
| 200 | if (ret) { |
| 201 | ERROR("SF: unable to claim SPI bus\n"); |
| 202 | return ret; |
| 203 | } |
| 204 | |
| 205 | page_size = flash->page_size; |
| 206 | |
| 207 | cmd[0] = flash->write_cmd; |
| 208 | for (actual = 0; actual < len; actual += chunk_len) { |
| 209 | write_addr = offset; |
| 210 | byte_addr = offset % page_size; |
| 211 | chunk_len = MIN(len - actual, |
| 212 | (uint32_t)(page_size - byte_addr)); |
| 213 | spi_flash_addr(write_addr, cmd); |
| 214 | |
| 215 | SPI_DEBUG("SF:0x%p=>cmd:{0x%02x 0x%02x%02x%02x} chunk_len:%d\n", |
| 216 | buf + actual, cmd[0], cmd[1], |
| 217 | cmd[2], cmd[3], chunk_len); |
| 218 | |
| 219 | ret = spi_flash_write_common(flash, cmd, sizeof(cmd), |
| 220 | buf + actual, chunk_len); |
| 221 | if (ret < 0) { |
| 222 | ERROR("SF: write cmd failed\n"); |
| 223 | break; |
| 224 | } |
| 225 | |
| 226 | offset += chunk_len; |
| 227 | } |
| 228 | SPI_DEBUG("SF write done\n"); |
| 229 | |
| 230 | spi_release_bus(); |
| 231 | return ret; |
| 232 | } |
| 233 | |
| 234 | int spi_flash_erase(struct spi_flash *flash, uint32_t offset, uint32_t len) |
| 235 | { |
| 236 | uint8_t cmd[SPI_FLASH_CMD_LEN]; |
| 237 | uint32_t erase_size, erase_addr; |
| 238 | int ret; |
| 239 | |
| 240 | erase_size = flash->erase_size; |
| 241 | |
| 242 | if (offset % erase_size || len % erase_size) { |
| 243 | ERROR("SF: Erase offset/length not multiple of erase size\n"); |
| 244 | return -1; |
| 245 | } |
| 246 | |
| 247 | ret = spi_claim_bus(); |
| 248 | if (ret) { |
| 249 | ERROR("SF: unable to claim SPI bus\n"); |
| 250 | return ret; |
| 251 | } |
| 252 | |
| 253 | cmd[0] = flash->erase_cmd; |
| 254 | while (len) { |
| 255 | erase_addr = offset; |
| 256 | spi_flash_addr(erase_addr, cmd); |
| 257 | |
| 258 | SPI_DEBUG("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], |
| 259 | cmd[2], cmd[3], erase_addr); |
| 260 | |
| 261 | ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0); |
| 262 | if (ret < 0) { |
| 263 | ERROR("SF: erase failed\n"); |
| 264 | break; |
| 265 | } |
| 266 | |
| 267 | offset += erase_size; |
| 268 | len -= erase_size; |
| 269 | } |
| 270 | SPI_DEBUG("sf erase done\n"); |
| 271 | |
| 272 | spi_release_bus(); |
| 273 | return ret; |
| 274 | } |
| 275 | |
| 276 | int spi_flash_probe(struct spi_flash *flash) |
| 277 | { |
| 278 | const struct spi_flash_info *info = NULL; |
| 279 | int ret; |
| 280 | |
| 281 | ret = spi_claim_bus(); |
| 282 | if (ret) { |
| 283 | ERROR("SF: Unable to claim SPI bus\n"); |
| 284 | ERROR("SF: probe failed\n"); |
| 285 | return ret; |
| 286 | } |
| 287 | |
| 288 | info = spi_flash_read_id(); |
| 289 | if (!info) |
| 290 | goto probe_fail; |
| 291 | |
| 292 | INFO("Flash Name: %s sectors %x, sec size %x\n", |
| 293 | info->name, info->n_sectors, |
| 294 | info->sector_size); |
| 295 | flash->size = info->n_sectors * info->sector_size; |
| 296 | flash->sector_size = info->sector_size; |
| 297 | flash->page_size = info->page_size; |
| 298 | flash->flags = info->flags; |
| 299 | |
| 300 | flash->read_cmd = CMD_READ_NORMAL; |
| 301 | flash->write_cmd = CMD_PAGE_PROGRAM; |
| 302 | flash->erase_cmd = CMD_ERASE_64K; |
| 303 | flash->erase_size = ERASE_SIZE_64K; |
| 304 | |
| 305 | probe_fail: |
| 306 | spi_release_bus(); |
| 307 | return ret; |
| 308 | } |