diff options
author | Alistair Delva <adelva@google.com> | 2021-02-16 21:01:22 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2021-02-16 21:01:22 +0000 |
commit | efb2826bb8160e2d8e0fcec85133a7468484f9fd (patch) | |
tree | 37a21c69306801ee7cdda5167a30896c8740155b /drivers/brcm/spi_flash.c | |
parent | b00a71fc312c9781fa6f404dccfb55b062b2ccac (diff) | |
parent | faa476c0caaa598afa5a6109d17102db5fe35ec6 (diff) | |
download | platform_external_arm-trusted-firmware-master.tar.gz platform_external_arm-trusted-firmware-master.tar.bz2 platform_external_arm-trusted-firmware-master.zip |
Merge branch 'aosp/upstream-master' into HEAD am: faa476c0caHEADandroid-s-beta-5android-s-beta-4android-s-beta-3android-s-beta-2android-s-beta-1mastermain-cg-testing-releaseandroid-s-beta-5android-s-beta-4
Original change: https://android-review.googlesource.com/c/platform/external/arm-trusted-firmware/+/1589611
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: I3a25534ceed4f8e188510641080d8b8ed49b8f62
Diffstat (limited to 'drivers/brcm/spi_flash.c')
-rw-r--r-- | drivers/brcm/spi_flash.c | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/drivers/brcm/spi_flash.c b/drivers/brcm/spi_flash.c new file mode 100644 index 000000000..336d2304b --- /dev/null +++ b/drivers/brcm/spi_flash.c @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2019-2020, Broadcom + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include <errno.h> + +#include <sf.h> +#include <spi.h> + +#define SPI_FLASH_CMD_LEN 4 +#define QSPI_WAIT_TIMEOUT_US 200000U /* usec */ + +#define FINFO(jedec_id, ext_id, _sector_size, _n_sectors, _page_size, _flags) \ + .id = { \ + ((jedec_id) >> 16) & 0xff, \ + ((jedec_id) >> 8) & 0xff, \ + (jedec_id) & 0xff, \ + ((ext_id) >> 8) & 0xff, \ + (ext_id) & 0xff, \ + }, \ + .id_len = (!(jedec_id) ? 0 : (3 + ((ext_id) ? 2 : 0))), \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = _page_size, \ + .flags = (_flags), + +/* SPI/QSPI flash device params structure */ +const struct spi_flash_info spi_flash_ids[] = { + {"W25Q64CV", FINFO(0xef4017, 0x0, 64 * 1024, 128, 256, WR_QPP | SECT_4K)}, + {"W25Q64DW", FINFO(0xef6017, 0x0, 64 * 1024, 128, 256, WR_QPP | SECT_4K)}, + {"W25Q32", FINFO(0xef4016, 0x0, 64 * 1024, 64, 256, SECT_4K)}, + {"MX25l3205D", FINFO(0xc22016, 0x0, 64 * 1024, 64, 256, SECT_4K)}, +}; + +static void spi_flash_addr(uint32_t addr, uint8_t *cmd) +{ + /* + * cmd[0] holds a SPI Flash command, stored earlier + * cmd[1/2/3] holds 24bit flash address + */ + cmd[1] = addr >> 16; + cmd[2] = addr >> 8; + cmd[3] = addr >> 0; +} + +static const struct spi_flash_info *spi_flash_read_id(void) +{ + const struct spi_flash_info *info; + uint8_t id[SPI_FLASH_MAX_ID_LEN]; + int ret; + + ret = spi_flash_cmd(CMD_READ_ID, id, SPI_FLASH_MAX_ID_LEN); + if (ret < 0) { + ERROR("SF: Error %d reading JEDEC ID\n", ret); + return NULL; + } + + for (info = spi_flash_ids; info->name != NULL; info++) { + if (info->id_len) { + if (!memcmp(info->id, id, info->id_len)) + return info; + } + } + + printf("SF: unrecognized JEDEC id bytes: %02x, %02x, %02x\n", + id[0], id[1], id[2]); + return NULL; +} + +/* Enable writing on the SPI flash */ +static inline int spi_flash_cmd_write_enable(struct spi_flash *flash) +{ + return spi_flash_cmd(CMD_WRITE_ENABLE, NULL, 0); +} + +static int spi_flash_cmd_wait(struct spi_flash *flash) +{ + uint8_t cmd; + uint32_t i; + uint8_t status; + int ret; + + i = 0; + while (1) { + cmd = CMD_RDSR; + ret = spi_flash_cmd_read(&cmd, 1, &status, 1); + if (ret < 0) { + ERROR("SF: cmd wait failed\n"); + break; + } + if (!(status & STATUS_WIP)) + break; + + i++; + if (i >= QSPI_WAIT_TIMEOUT_US) { + ERROR("SF: cmd wait timeout\n"); + ret = -1; + break; + } + udelay(1); + } + + return ret; +} + +static int spi_flash_write_common(struct spi_flash *flash, const uint8_t *cmd, + size_t cmd_len, const void *buf, + size_t buf_len) +{ + int ret; + + ret = spi_flash_cmd_write_enable(flash); + if (ret < 0) { + ERROR("SF: enabling write failed\n"); + return ret; + } + + ret = spi_flash_cmd_write(cmd, cmd_len, buf, buf_len); + if (ret < 0) { + ERROR("SF: write cmd failed\n"); + return ret; + } + + ret = spi_flash_cmd_wait(flash); + if (ret < 0) { + ERROR("SF: write timed out\n"); + return ret; + } + + return ret; +} + +static int spi_flash_read_common(const uint8_t *cmd, size_t cmd_len, + void *data, size_t data_len) +{ + int ret; + + ret = spi_flash_cmd_read(cmd, cmd_len, data, data_len); + if (ret < 0) { + ERROR("SF: read cmd failed\n"); + return ret; + } + + return ret; +} + +int spi_flash_read(struct spi_flash *flash, uint32_t offset, + uint32_t len, void *data) +{ + uint32_t read_len = 0, read_addr; + uint8_t cmd[SPI_FLASH_CMD_LEN]; + int ret; + + ret = spi_claim_bus(); + if (ret) { + ERROR("SF: unable to claim SPI bus\n"); + return ret; + } + + cmd[0] = CMD_READ_NORMAL; + while (len) { + read_addr = offset; + read_len = MIN(flash->page_size, (len - read_len)); + spi_flash_addr(read_addr, cmd); + + ret = spi_flash_read_common(cmd, sizeof(cmd), data, read_len); + if (ret < 0) { + ERROR("SF: read failed\n"); + break; + } + + offset += read_len; + len -= read_len; + data += read_len; + } + SPI_DEBUG("SF read done\n"); + + spi_release_bus(); + return ret; +} + +int spi_flash_write(struct spi_flash *flash, uint32_t offset, + uint32_t len, void *buf) +{ + unsigned long byte_addr, page_size; + uint8_t cmd[SPI_FLASH_CMD_LEN]; + uint32_t chunk_len, actual; + uint32_t write_addr; + int ret; + + ret = spi_claim_bus(); + if (ret) { + ERROR("SF: unable to claim SPI bus\n"); + return ret; + } + + page_size = flash->page_size; + + cmd[0] = flash->write_cmd; + for (actual = 0; actual < len; actual += chunk_len) { + write_addr = offset; + byte_addr = offset % page_size; + chunk_len = MIN(len - actual, + (uint32_t)(page_size - byte_addr)); + spi_flash_addr(write_addr, cmd); + + SPI_DEBUG("SF:0x%p=>cmd:{0x%02x 0x%02x%02x%02x} chunk_len:%d\n", + buf + actual, cmd[0], cmd[1], + cmd[2], cmd[3], chunk_len); + + ret = spi_flash_write_common(flash, cmd, sizeof(cmd), + buf + actual, chunk_len); + if (ret < 0) { + ERROR("SF: write cmd failed\n"); + break; + } + + offset += chunk_len; + } + SPI_DEBUG("SF write done\n"); + + spi_release_bus(); + return ret; +} + +int spi_flash_erase(struct spi_flash *flash, uint32_t offset, uint32_t len) +{ + uint8_t cmd[SPI_FLASH_CMD_LEN]; + uint32_t erase_size, erase_addr; + int ret; + + erase_size = flash->erase_size; + + if (offset % erase_size || len % erase_size) { + ERROR("SF: Erase offset/length not multiple of erase size\n"); + return -1; + } + + ret = spi_claim_bus(); + if (ret) { + ERROR("SF: unable to claim SPI bus\n"); + return ret; + } + + cmd[0] = flash->erase_cmd; + while (len) { + erase_addr = offset; + spi_flash_addr(erase_addr, cmd); + + SPI_DEBUG("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], + cmd[2], cmd[3], erase_addr); + + ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0); + if (ret < 0) { + ERROR("SF: erase failed\n"); + break; + } + + offset += erase_size; + len -= erase_size; + } + SPI_DEBUG("sf erase done\n"); + + spi_release_bus(); + return ret; +} + +int spi_flash_probe(struct spi_flash *flash) +{ + const struct spi_flash_info *info = NULL; + int ret; + + ret = spi_claim_bus(); + if (ret) { + ERROR("SF: Unable to claim SPI bus\n"); + ERROR("SF: probe failed\n"); + return ret; + } + + info = spi_flash_read_id(); + if (!info) + goto probe_fail; + + INFO("Flash Name: %s sectors %x, sec size %x\n", + info->name, info->n_sectors, + info->sector_size); + flash->size = info->n_sectors * info->sector_size; + flash->sector_size = info->sector_size; + flash->page_size = info->page_size; + flash->flags = info->flags; + + flash->read_cmd = CMD_READ_NORMAL; + flash->write_cmd = CMD_PAGE_PROGRAM; + flash->erase_cmd = CMD_ERASE_64K; + flash->erase_size = ERASE_SIZE_64K; + +probe_fail: + spi_release_bus(); + return ret; +} |