Norman James | 6a58a27 | 2015-10-07 14:34:16 -0500 | [diff] [blame] | 1 | #include <stdint.h>
|
| 2 | #include <stdbool.h>
|
| 3 | #include <stdlib.h>
|
| 4 | #include <errno.h>
|
| 5 | #include <stdio.h>
|
| 6 | #include <string.h>
|
| 7 |
|
| 8 | #include <libflash/libflash.h>
|
| 9 | #include <libflash/libflash-priv.h>
|
| 10 |
|
| 11 | #include "io.h"
|
| 12 |
|
| 13 | /* Offset of SFC registers in FW space */
|
| 14 | #define SFC_CMDREG_OFFSET 0x00000c00
|
| 15 | /* Offset of SFC command buffer in FW space */
|
| 16 | #define SFC_CMDBUF_OFFSET 0x00000d00
|
| 17 | /* Offset of flash MMIO mapping in FW space */
|
| 18 | #define SFC_MMIO_OFFSET 0x0c000000
|
| 19 |
|
| 20 |
|
| 21 | /*
|
| 22 | * Register definitions
|
| 23 | */
|
| 24 | #define SFC_REG_CONF 0x10 /* CONF: Direct Access Configuration */
|
| 25 | #define SFC_REG_CONF_FRZE (1 << 3)
|
| 26 | #define SFC_REG_CONF_ECCEN (1 << 2)
|
| 27 | #define SFC_REG_CONF_DRCD (1 << 1)
|
| 28 | #define SFC_REG_CONF_FLRLD (1 << 0)
|
| 29 |
|
| 30 | #define SFC_REG_STATUS 0x0C /* STATUS : Status Reg */
|
| 31 | #define SFC_REG_STATUS_NX_ON_SHFT 28
|
| 32 | #define SFC_REG_STATUS_RWP (1 << 27)
|
| 33 | #define SFC_REG_STATUS_FOURBYTEAD (1 << 26)
|
| 34 | #define SFC_REG_STATUS_ILLEGAL (1 << 4)
|
| 35 | #define SFC_REG_STATUS_ECCERRCNTN (1 << 3)
|
| 36 | #define SFC_REG_STATUS_ECCUEN (1 << 2)
|
| 37 | #define SFC_REG_STATUS_DONE (1 << 0)
|
| 38 |
|
| 39 | #define SFC_REG_CMD 0x40 /* CMD : Command */
|
| 40 | #define SFC_REG_CMD_OPCODE_SHFT 9
|
| 41 | #define SFC_REG_CMD_LENGTH_SHFT 0
|
| 42 |
|
| 43 | #define SFC_REG_SPICLK 0x3C /* SPICLK: SPI clock rate config */
|
| 44 | #define SFC_REG_SPICLK_OUTDLY_SHFT 24
|
| 45 | #define SFC_REG_SPICLK_INSAMPDLY_SHFT 16
|
| 46 | #define SFC_REG_SPICLK_CLKHI_SHFT 8
|
| 47 | #define SFC_REG_SPICLK_CLKLO_SHFT 0
|
| 48 |
|
| 49 | #define SFC_REG_ADR 0x44 /* ADR : Address */
|
| 50 | #define SFC_REG_ERASMS 0x48 /* ERASMS : Small Erase Block Size */
|
| 51 | #define SFC_REG_ERASLGS 0x4C /* ERALGS : Large Erase Block Size */
|
| 52 | #define SFC_REG_CONF4 0x54 /* CONF4 : SPI Op Code for Small Erase */
|
| 53 | #define SFC_REG_CONF5 0x58 /* CONF5 : Small Erase Size config reg */
|
| 54 |
|
| 55 | #define SFC_REG_CONF8 0x64 /* CONF8 : Read Command */
|
| 56 | #define SFC_REG_CONF8_CSINACTIVERD_SHFT 18
|
| 57 | #define SFC_REG_CONF8_DUMMY_SHFT 8
|
| 58 | #define SFC_REG_CONF8_READOP_SHFT 0
|
| 59 |
|
| 60 | #define SFC_REG_ADRCBF 0x80 /* ADRCBF : First Intf NOR Addr Offset */
|
| 61 | #define SFC_REG_ADRCMF 0x84 /* ADRCMF : First Intf NOR Allocation */
|
| 62 | #define SFC_REG_ADRCBS 0x88 /* ADRCBS : Second Intf NOR Addr Offset */
|
| 63 | #define SFC_REG_ADRCMS 0x8C /* ADRCMS : Second Intf NOR Allocation */
|
| 64 | #define SFC_REG_OADRNB 0x90 /* OADRNB : Direct Access OBP Window Base Address */
|
| 65 | #define SFC_REG_OADRNS 0x94 /* OADRNS : DIrect Access OPB Window Size */
|
| 66 |
|
| 67 | #define SFC_REG_CHIPIDCONF 0x9C /* CHIPIDCONF : config ChipId CMD */
|
| 68 | #define SFC_REG_CHIPIDCONF_OPCODE_SHFT 24
|
| 69 | #define SFC_REG_CHIPIDCONF_READ (1 << 23)
|
| 70 | #define SFC_REG_CHIPIDCONF_WRITE (1 << 22)
|
| 71 | #define SFC_REG_CHIPIDCONF_USE_ADDR (1 << 21)
|
| 72 | #define SFC_REG_CHIPIDCONF_DUMMY_SHFT 16
|
| 73 | #define SFC_REG_CHIPIDCONF_LEN_SHFT 0
|
| 74 |
|
| 75 | /*
|
| 76 | * SFC Opcodes
|
| 77 | */
|
| 78 | #define SFC_OP_READRAW 0x03 /* Read Raw */
|
| 79 | #define SFC_OP_WRITERAW 0x02 /* Write Raw */
|
| 80 | #define SFC_OP_ERASM 0x32 /* Erase Small */
|
| 81 | #define SFC_OP_ERALG 0x34 /* Erase Large */
|
| 82 | #define SFC_OP_ENWRITPROT 0x53 /* Enable WRite Protect */
|
| 83 | #define SFC_OP_CHIPID 0x1F /* Get Chip ID */
|
| 84 | #define SFC_OP_STATUS 0x05 /* Get Status */
|
| 85 | #define SFC_OP_TURNOFF 0x5E /* Turn Off */
|
| 86 | #define SFC_OP_TURNON 0x50 /* Turn On */
|
| 87 | #define SFC_OP_ABORT 0x6F /* Super-Abort */
|
| 88 | #define SFC_OP_START4BA 0x37 /* Start 4BA */
|
| 89 | #define SFC_OP_END4BA 0x69 /* End 4BA */
|
| 90 |
|
| 91 | /* Command buffer size */
|
| 92 | #define SFC_CMDBUF_SIZE 256
|
| 93 |
|
| 94 | struct sfc_ctrl {
|
| 95 | /* Erase sizes */
|
| 96 | uint32_t small_er_size;
|
| 97 | uint32_t large_er_size;
|
| 98 |
|
| 99 | /* Current 4b mode */
|
| 100 | bool mode_4b;
|
| 101 |
|
| 102 | /* Callbacks */
|
| 103 | struct spi_flash_ctrl ops;
|
| 104 | };
|
| 105 |
|
| 106 | /* Command register support */
|
| 107 | static inline int sfc_reg_read(uint8_t reg, uint32_t *val)
|
| 108 | {
|
| 109 | uint32_t tmp;
|
| 110 | int rc;
|
| 111 |
|
| 112 | *val = 0xffffffff;
|
| 113 | rc = lpc_fw_read32(&tmp, SFC_CMDREG_OFFSET + reg);
|
| 114 | if (rc)
|
| 115 | return rc;
|
| 116 | *val = be32_to_cpu(tmp);
|
| 117 | return 0;
|
| 118 | }
|
| 119 |
|
| 120 | static inline int sfc_reg_write(uint8_t reg, uint32_t val)
|
| 121 | {
|
| 122 | return lpc_fw_write32(cpu_to_be32(val), SFC_CMDREG_OFFSET + reg);
|
| 123 | }
|
| 124 |
|
| 125 | static int sfc_buf_write(uint32_t len, const void *data)
|
| 126 | {
|
| 127 | uint32_t tmp, off = 0;
|
| 128 | int rc;
|
| 129 |
|
| 130 | if (len > SFC_CMDBUF_SIZE)
|
| 131 | return FLASH_ERR_PARM_ERROR;
|
| 132 |
|
| 133 | while (len >= 4) {
|
| 134 | tmp = *(const uint32_t *)data;
|
| 135 | rc = lpc_fw_write32(tmp, SFC_CMDBUF_OFFSET + off);
|
| 136 | if (rc)
|
| 137 | return rc;
|
| 138 | off += 4;
|
| 139 | len -= 4;
|
| 140 | data += 4;
|
| 141 | }
|
| 142 | if (!len)
|
| 143 | return 0;
|
| 144 |
|
| 145 | /* lpc_fw_write operates on BE values so that's what we layout
|
| 146 | * in memory with memcpy. The swap in the register on LE doesn't
|
| 147 | * matter, the result in memory will be in the right order.
|
| 148 | */
|
| 149 | tmp = -1;
|
| 150 | memcpy(&tmp, data, len);
|
| 151 | return lpc_fw_write32(tmp, SFC_CMDBUF_OFFSET + off);
|
| 152 | }
|
| 153 |
|
| 154 | static int sfc_buf_read(uint32_t len, void *data)
|
| 155 | {
|
| 156 | uint32_t tmp, off = 0;
|
| 157 | int rc;
|
| 158 |
|
| 159 | if (len > SFC_CMDBUF_SIZE)
|
| 160 | return FLASH_ERR_PARM_ERROR;
|
| 161 |
|
| 162 | while (len >= 4) {
|
| 163 | rc = lpc_fw_read32(data, SFC_CMDBUF_OFFSET + off);
|
| 164 | if (rc)
|
| 165 | return rc;
|
| 166 | off += 4;
|
| 167 | len -= 4;
|
| 168 | data += 4;
|
| 169 | }
|
| 170 | if (!len)
|
| 171 | return 0;
|
| 172 |
|
| 173 | rc = lpc_fw_read32(&tmp, SFC_CMDBUF_OFFSET + off);
|
| 174 | if (rc)
|
| 175 | return rc;
|
| 176 | /* We know tmp contains a big endian value, so memcpy is
|
| 177 | * our friend here
|
| 178 | */
|
| 179 | memcpy(data, &tmp, len);
|
| 180 | return 0;
|
| 181 | }
|
| 182 |
|
| 183 | /* Polls until SFC indicates command is complete */
|
| 184 | static int sfc_poll_complete(void)
|
| 185 | {
|
| 186 | uint32_t status;
|
| 187 |
|
| 188 | /* XXX Add timeout */
|
| 189 | do {
|
| 190 | int rc;
|
| 191 |
|
| 192 | rc = sfc_reg_read(SFC_REG_STATUS, &status);
|
| 193 | if (rc)
|
| 194 | return rc;
|
| 195 | if (status & SFC_REG_STATUS_DONE)
|
| 196 | break;
|
| 197 |
|
| 198 | } while (true);
|
| 199 |
|
| 200 | return 0;
|
| 201 | }
|
| 202 |
|
| 203 | static int sfc_exec_command(uint8_t opcode, uint32_t length)
|
| 204 | {
|
| 205 | int rc = 0;
|
| 206 | uint32_t cmd_reg = 0;
|
| 207 |
|
| 208 | if (opcode > 0x7f || length > 0x1ff)
|
| 209 | return FLASH_ERR_PARM_ERROR;
|
| 210 |
|
| 211 | /* Write command register to start execution */
|
| 212 | cmd_reg |= (opcode << SFC_REG_CMD_OPCODE_SHFT);
|
| 213 | cmd_reg |= (length << SFC_REG_CMD_LENGTH_SHFT);
|
| 214 | rc = sfc_reg_write(SFC_REG_CMD, cmd_reg);
|
| 215 | if (rc)
|
| 216 | return rc;
|
| 217 |
|
| 218 | /* Wait for command to complete */
|
| 219 | return sfc_poll_complete();
|
| 220 | }
|
| 221 |
|
| 222 | static int sfc_chip_id(struct spi_flash_ctrl *ctrl, uint8_t *id_buf,
|
| 223 | uint32_t *id_size)
|
| 224 | {
|
| 225 | uint32_t idconf;
|
| 226 | int rc;
|
| 227 |
|
| 228 | if ((*id_size) < 3)
|
| 229 | return FLASH_ERR_PARM_ERROR;
|
| 230 |
|
| 231 | /*
|
| 232 | * XXX This will not work in locked down mode but we assume that
|
| 233 | * in this case, the chip ID command is already properly programmed
|
| 234 | * and the SFC will ignore this. However I haven't verified...
|
| 235 | */
|
| 236 | idconf = ((uint64_t)CMD_RDID) << SFC_REG_CHIPIDCONF_OPCODE_SHFT;
|
| 237 | idconf |= SFC_REG_CHIPIDCONF_READ;
|
| 238 | idconf |= (3ul << SFC_REG_CHIPIDCONF_LEN_SHFT);
|
| 239 | (void)sfc_reg_write(SFC_REG_CHIPIDCONF, idconf);
|
| 240 |
|
| 241 | /* Perform command */
|
| 242 | rc = sfc_exec_command(SFC_OP_CHIPID, 0);
|
| 243 | if (rc)
|
| 244 | return rc;
|
| 245 |
|
| 246 | /* Read chip ID */
|
| 247 | rc = sfc_buf_read(3, id_buf);
|
| 248 | if (rc)
|
| 249 | return rc;
|
| 250 | *id_size = 3;
|
| 251 |
|
| 252 | return 0;
|
| 253 | }
|
| 254 |
|
| 255 |
|
| 256 | static int sfc_read(struct spi_flash_ctrl *ctrl, uint32_t pos,
|
| 257 | void *buf, uint32_t len)
|
| 258 | {
|
| 259 | while(len) {
|
| 260 | uint32_t chunk = len;
|
| 261 | int rc;
|
| 262 |
|
| 263 | if (chunk > SFC_CMDBUF_SIZE)
|
| 264 | chunk = SFC_CMDBUF_SIZE;
|
| 265 | rc = sfc_reg_write(SFC_REG_ADR, pos);
|
| 266 | if (rc)
|
| 267 | return rc;
|
| 268 | rc = sfc_exec_command(SFC_OP_READRAW, chunk);
|
| 269 | if (rc)
|
| 270 | return rc;
|
| 271 | rc = sfc_buf_read(chunk, buf);
|
| 272 | if (rc)
|
| 273 | return rc;
|
| 274 | len -= chunk;
|
| 275 | pos += chunk;
|
| 276 | buf += chunk;
|
| 277 | }
|
| 278 | return 0;
|
| 279 | }
|
| 280 |
|
| 281 | static int sfc_write(struct spi_flash_ctrl *ctrl, uint32_t addr,
|
| 282 | const void *buf, uint32_t size)
|
| 283 | {
|
| 284 | uint32_t chunk;
|
| 285 | int rc;
|
| 286 |
|
| 287 | while(size) {
|
| 288 | /* We shall not cross a page boundary */
|
| 289 | chunk = 0x100 - (addr & 0xff);
|
| 290 | if (chunk > size)
|
| 291 | chunk = size;
|
| 292 |
|
| 293 | /* Write to SFC write buffer */
|
| 294 | rc = sfc_buf_write(chunk, buf);
|
| 295 | if (rc)
|
| 296 | return rc;
|
| 297 |
|
| 298 | /* Program address */
|
| 299 | rc = sfc_reg_write(SFC_REG_ADR, addr);
|
| 300 | if (rc)
|
| 301 | return rc;
|
| 302 |
|
| 303 | /* Send command */
|
| 304 | rc = sfc_exec_command(SFC_OP_WRITERAW, chunk);
|
| 305 | if (rc)
|
| 306 | return rc;
|
| 307 |
|
| 308 | addr += chunk;
|
| 309 | buf += chunk;
|
| 310 | size -= chunk;
|
| 311 | }
|
| 312 | return 0;
|
| 313 | }
|
| 314 |
|
| 315 | static int sfc_erase(struct spi_flash_ctrl *ctrl, uint32_t addr,
|
| 316 | uint32_t size)
|
| 317 | {
|
| 318 | struct sfc_ctrl *ct = container_of(ctrl, struct sfc_ctrl, ops);
|
| 319 | uint32_t sm_mask = ct->small_er_size - 1;
|
| 320 | uint32_t lg_mask = ct->large_er_size - 1;
|
| 321 | uint32_t chunk;
|
| 322 | uint8_t cmd;
|
| 323 | int rc;
|
| 324 |
|
| 325 | while(size) {
|
| 326 | /* Choose erase size for this chunk */
|
| 327 | if (((addr | size) & lg_mask) == 0) {
|
| 328 | chunk = ct->large_er_size;
|
| 329 | cmd = SFC_OP_ERALG;
|
| 330 | } else if (((addr | size) & sm_mask) == 0) {
|
| 331 | chunk = ct->small_er_size;
|
| 332 | cmd = SFC_OP_ERASM;
|
| 333 | } else
|
| 334 | return FLASH_ERR_ERASE_BOUNDARY;
|
| 335 |
|
| 336 | rc = sfc_reg_write(SFC_REG_ADR, addr);
|
| 337 | if (rc)
|
| 338 | return rc;
|
| 339 | rc = sfc_exec_command(cmd, 0);
|
| 340 | if (rc)
|
| 341 | return rc;
|
| 342 | addr += chunk;
|
| 343 | size -= chunk;
|
| 344 | }
|
| 345 | return 0;
|
| 346 | }
|
| 347 |
|
| 348 | static int sfc_setup(struct spi_flash_ctrl *ctrl, uint32_t *tsize)
|
| 349 | {
|
| 350 | struct sfc_ctrl *ct = container_of(ctrl, struct sfc_ctrl, ops);
|
| 351 | struct flash_info *info = ctrl->finfo;
|
| 352 | uint32_t er_flags;
|
| 353 |
|
| 354 | /* Keep non-erase related flags */
|
| 355 | er_flags = ~FL_ERASE_ALL;
|
| 356 |
|
| 357 | /* Add supported erase sizes */
|
| 358 | if (ct->small_er_size == 0x1000 || ct->large_er_size == 0x1000)
|
| 359 | er_flags |= FL_ERASE_4K;
|
| 360 | if (ct->small_er_size == 0x8000 || ct->large_er_size == 0x8000)
|
| 361 | er_flags |= FL_ERASE_32K;
|
| 362 | if (ct->small_er_size == 0x10000 || ct->large_er_size == 0x10000)
|
| 363 | er_flags |= FL_ERASE_64K;
|
| 364 |
|
| 365 | /* Mask the flags out */
|
| 366 | info->flags &= er_flags;
|
| 367 |
|
| 368 | return 0;
|
| 369 | }
|
| 370 |
|
| 371 | static int sfc_set_4b(struct spi_flash_ctrl *ctrl, bool enable)
|
| 372 | {
|
| 373 | struct sfc_ctrl *ct = container_of(ctrl, struct sfc_ctrl, ops);
|
| 374 | int rc;
|
| 375 |
|
| 376 | rc = sfc_exec_command(enable ? SFC_OP_START4BA : SFC_OP_END4BA, 0);
|
| 377 | if (rc)
|
| 378 | return rc;
|
| 379 | ct->mode_4b = enable;
|
| 380 | return 0;
|
| 381 | }
|
| 382 |
|
| 383 | static void sfc_validate_er_size(uint32_t *size)
|
| 384 | {
|
| 385 | if (*size == 0)
|
| 386 | return;
|
| 387 |
|
| 388 | /* We only support 4k, 32k and 64k */
|
| 389 | if (*size != 0x1000 && *size != 0x8000 && *size != 0x10000) {
|
| 390 | FL_ERR("SFC: Erase size %d bytes unsupported\n", *size);
|
| 391 | *size = 0;
|
| 392 | }
|
| 393 | }
|
| 394 |
|
| 395 | static int sfc_init(struct sfc_ctrl *ct)
|
| 396 | {
|
| 397 | int rc;
|
| 398 | uint32_t status;
|
| 399 |
|
| 400 | /*
|
| 401 | * Assumptions: The controller has been fully initialized
|
| 402 | * by an earlier FW layer setting the chip ID command, the
|
| 403 | * erase sizes, and configuring the timings for reads and
|
| 404 | * writes.
|
| 405 | *
|
| 406 | * This driver is meant to be usable if the configuration
|
| 407 | * is in lock down.
|
| 408 | *
|
| 409 | * If that wasn't the case, we could configure some sane
|
| 410 | * defaults here and tuned values in setup() after the
|
| 411 | * chip has been identified.
|
| 412 | */
|
| 413 |
|
| 414 | /* Read erase sizes from flash */
|
| 415 | rc = sfc_reg_read(SFC_REG_ERASMS, &ct->small_er_size);
|
| 416 | if (rc)
|
| 417 | return rc;
|
| 418 | sfc_validate_er_size(&ct->small_er_size);
|
| 419 | rc = sfc_reg_read(SFC_REG_ERASLGS, &ct->large_er_size);
|
| 420 | if (rc)
|
| 421 | return rc;
|
| 422 | sfc_validate_er_size(&ct->large_er_size);
|
| 423 |
|
| 424 | /* No erase sizes we can cope with ? Ouch... */
|
| 425 | if ((ct->small_er_size == 0 && ct->large_er_size == 0) ||
|
| 426 | (ct->large_er_size && (ct->small_er_size > ct->large_er_size))) {
|
| 427 | FL_ERR("SFC: No supported erase sizes !\n");
|
| 428 | return FLASH_ERR_CTRL_CONFIG_MISMATCH;
|
| 429 | }
|
| 430 |
|
| 431 | FL_INF("SFC: Suppored erase sizes:");
|
| 432 | if (ct->small_er_size)
|
| 433 | FL_INF(" %dKB", ct->small_er_size >> 10);
|
| 434 | if (ct->large_er_size)
|
| 435 | FL_INF(" %dKB", ct->large_er_size >> 10);
|
| 436 | FL_INF("\n");
|
| 437 |
|
| 438 | /* Read current state of 4 byte addressing */
|
| 439 | rc = sfc_reg_read(SFC_REG_STATUS, &status);
|
| 440 | if (rc)
|
| 441 | return rc;
|
| 442 | ct->mode_4b = !!(status & SFC_REG_STATUS_FOURBYTEAD);
|
| 443 |
|
| 444 | return 0;
|
| 445 | }
|
| 446 |
|
| 447 | int sfc_open(struct spi_flash_ctrl **ctrl)
|
| 448 | {
|
| 449 | struct sfc_ctrl *ct;
|
| 450 | int rc;
|
| 451 |
|
| 452 | *ctrl = NULL;
|
| 453 | ct = malloc(sizeof(*ct));
|
| 454 | if (!ct) {
|
| 455 | FL_ERR("SFC: Failed to allocate\n");
|
| 456 | return FLASH_ERR_MALLOC_FAILED;
|
| 457 | }
|
| 458 | memset(ct, 0, sizeof(*ct));
|
| 459 | ct->ops.chip_id = sfc_chip_id;
|
| 460 | ct->ops.setup = sfc_setup;
|
| 461 | ct->ops.set_4b = sfc_set_4b;
|
| 462 | ct->ops.read = sfc_read;
|
| 463 | ct->ops.write = sfc_write;
|
| 464 | ct->ops.erase = sfc_erase;
|
| 465 |
|
| 466 | rc = sfc_init(ct);
|
| 467 | if (rc)
|
| 468 | goto fail;
|
| 469 | *ctrl = &ct->ops;
|
| 470 | return 0;
|
| 471 | fail:
|
| 472 | free(ct);
|
| 473 | return rc;
|
| 474 | }
|
| 475 |
|
| 476 | void sfc_close(struct spi_flash_ctrl *ctrl)
|
| 477 | {
|
| 478 | struct sfc_ctrl *ct = container_of(ctrl, struct sfc_ctrl, ops);
|
| 479 |
|
| 480 | /* Free the whole lot */
|
| 481 | free(ct);
|
| 482 | }
|
| 483 |
|