summaryrefslogtreecommitdiff
path: root/common/spi_nor.c
diff options
context:
space:
mode:
authorJack Rosenthal <jrosenth@chromium.org>2021-11-04 12:11:58 -0600
committerCommit Bot <commit-bot@chromium.org>2021-11-05 04:22:34 +0000
commit252457d4b21f46889eebad61d4c0a65331919cec (patch)
tree01856c4d31d710b20e85a74c8d7b5836e35c3b98 /common/spi_nor.c
parent08f5a1e6fc2c9467230444ac9b582dcf4d9f0068 (diff)
downloadchrome-ec-stabilize-14333.B-ish.tar.gz
In the interest of making long-term branch maintenance incur as little technical debt on us as possible, we should not maintain any files on the branch we are not actually using. This has the added effect of making it extremely clear when merging CLs from the main branch when changes have the possibility to affect us. The follow-on CL adds a convenience script to actually pull updates from the main branch and generate a CL for the update. BUG=b:204206272 BRANCH=ish TEST=make BOARD=arcada_ish && make BOARD=drallion_ish Signed-off-by: Jack Rosenthal <jrosenth@chromium.org> Change-Id: I17e4694c38219b5a0823e0a3e55a28d1348f4b18 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3262038 Reviewed-by: Jett Rink <jettrink@chromium.org> Reviewed-by: Tom Hughes <tomhughes@chromium.org>
Diffstat (limited to 'common/spi_nor.c')
-rw-r--r--common/spi_nor.c1091
1 files changed, 0 insertions, 1091 deletions
diff --git a/common/spi_nor.c b/common/spi_nor.c
deleted file mode 100644
index 0a719d63b3..0000000000
--- a/common/spi_nor.c
+++ /dev/null
@@ -1,1091 +0,0 @@
-/* Copyright 2015 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-/* SFDP-based Serial NOR flash device module for Chrome EC */
-
-#include "common.h"
-#include "console.h"
-#include "spi_nor.h"
-#include "shared_mem.h"
-#include "util.h"
-#include "task.h"
-#include "spi.h"
-#include "sfdp.h"
-#include "timer.h"
-#include "watchdog.h"
-
-#ifdef CONFIG_SPI_NOR_DEBUG
-#define CPRINTS(dev, string, args...) \
- cprints(CC_SPI, "SPI NOR %s: " string, (dev)->name, ## args)
-#else
-#define CPRINTS(dev, string, args...)
-#endif
-
-/* Time to sleep while serial NOR flash write is in progress. */
-#define SPI_NOR_WIP_SLEEP_USEC 10
-
-/* This driver only supports v1.* SFDP. */
-#define SPI_NOR_SUPPORTED_SFDP_MAJOR_VERSION 1
-
-/* Ensure a Serial NOR Flash read command in 4B addressing mode fits. */
-BUILD_ASSERT(CONFIG_SPI_NOR_MAX_READ_SIZE + 5 <=
- CONFIG_SPI_NOR_MAX_MESSAGE_SIZE);
-/* The maximum write size must be a power of two so it can be used as an
- * emulated maximum page size. */
-BUILD_ASSERT(POWER_OF_TWO(CONFIG_SPI_NOR_MAX_WRITE_SIZE));
-/* Ensure a Serial NOR Flash page program command in 4B addressing mode fits. */
-BUILD_ASSERT(CONFIG_SPI_NOR_MAX_WRITE_SIZE + 5 <=
- CONFIG_SPI_NOR_MAX_MESSAGE_SIZE);
-
-/* A single mutex is used to protect the single buffer, SPI port, and all of the
- * device mutable board defined device states, if the contention is too high it
- * may be worthwhile to change the global mutex granularity to a finer-grained
- * mutex granularity. */
-static struct mutex driver_mutex;
-
-/* Single internal buffer used to stage serial NOR flash commands for the
- * public APIs (read, write, erase). */
-static uint8_t buf[CONFIG_SPI_NOR_MAX_MESSAGE_SIZE];
-
-/******************************************************************************/
-/* Internal driver functions. */
-
-/**
- * Blocking read of the Serial Flash's first status register.
- */
-static int spi_nor_read_status(const struct spi_nor_device_t *spi_nor_device,
- uint8_t *status_register_value)
-{
- uint8_t cmd = SPI_NOR_OPCODE_READ_STATUS;
-
- return spi_transaction(&spi_devices[spi_nor_device->spi_controller],
- &cmd, 1, status_register_value, 1);
-}
-
-/**
- * Set the write enable latch. Device and shared buffer mutexes must be held!
- */
-static int spi_nor_write_enable(const struct spi_nor_device_t *spi_nor_device)
-{
- uint8_t cmd = SPI_NOR_OPCODE_WRITE_ENABLE;
- uint8_t status_register_value;
- int rv = EC_SUCCESS;
-
- /* Set the write enable latch. */
- rv = spi_transaction(&spi_devices[spi_nor_device->spi_controller],
- &cmd, 1, NULL, 0);
- if (rv)
- return rv;
-
- /* Verify the write enabled latch got set. */
- rv = spi_nor_read_status(spi_nor_device, &status_register_value);
- if (rv)
- return rv;
- if ((status_register_value & SPI_NOR_STATUS_REGISTER_WEL) == 0)
- return EC_ERROR_UNKNOWN; /* WEL not set but should be. */
-
- return rv;
-}
-
-/**
- * Read from the extended address register.
- * @param spi_nor_device The Serial NOR Flash device to use.
- * @param value The value to read to.
- * @return ec_error_list (non-zero on error and timeout).
- */
-static int spi_nor_read_ear(const struct spi_nor_device_t *spi_nor_device,
- uint8_t *value)
-{
- uint8_t command = SPI_NOR_OPCODE_RDEAR;
-
- return spi_transaction(&spi_devices[spi_nor_device->spi_controller],
- &command, sizeof(command), value, 1);
-}
-
-int spi_nor_write_ear(const struct spi_nor_device_t *spi_nor_device,
- const uint8_t value)
-{
- uint8_t buf[2];
- int rv;
- uint8_t ear;
-
- mutex_lock(&driver_mutex);
-
- rv = spi_nor_write_enable(spi_nor_device);
- if (rv) {
- CPRINTS(spi_nor_device, "Failed to write enable");
- goto err_free;
- }
-
- buf[0] = SPI_NOR_OPCODE_WREAR;
- buf[1] = value;
-
- rv = spi_transaction(&spi_devices[spi_nor_device->spi_controller],
- buf, sizeof(buf), NULL, 0);
- if (rv) {
- CPRINTS(spi_nor_device, "Failed to write EAR, rv=%d", rv);
- goto err_free;
- }
-
- rv = spi_nor_read_ear(spi_nor_device, &ear);
- if (rv)
- goto err_free;
-
- if (ear != value) {
- CPRINTS(spi_nor_device,
- "Write EAR error: write=%d, read=%d", value, ear);
- rv = EC_ERROR_UNKNOWN; /* WEL not set but should be. */
- goto err_free;
- }
-
-err_free:
- mutex_unlock(&driver_mutex);
- return rv;
-}
-
-/**
- * Block until the Serial NOR Flash clears the BUSY/WIP bit in its status reg.
- */
-static int spi_nor_wait(const struct spi_nor_device_t *spi_nor_device)
-{
- int rv = EC_SUCCESS;
- timestamp_t timeout;
- uint8_t status_register_value;
-
- rv = spi_nor_read_status(spi_nor_device, &status_register_value);
- if (rv)
- return rv;
- timeout.val =
- get_time().val + spi_nor_device->timeout_usec;
- while (status_register_value & SPI_NOR_STATUS_REGISTER_WIP) {
- /* Reload the watchdog before sleeping. */
- watchdog_reload();
- usleep(SPI_NOR_WIP_SLEEP_USEC);
-
- /* Give up if the deadline has been exceeded. */
- if (get_time().val > timeout.val)
- return EC_ERROR_TIMEOUT;
-
- /* Re-read the status register. */
- rv = spi_nor_read_status(spi_nor_device,
- &status_register_value);
- if (rv)
- return rv;
- }
-
- return rv;
-}
-
-/**
- * Read the Manufacturer bank and ID out of the JEDEC ID.
- */
-static int spi_nor_read_jedec_mfn_id(
- const struct spi_nor_device_t *spi_nor_device,
- uint8_t *out_mfn_bank,
- uint8_t *out_mfn_id)
-{
- int rv = EC_SUCCESS;
- uint8_t jedec_id[SPI_NOR_JEDEC_ID_BANKS];
- size_t i;
- uint8_t cmd = SPI_NOR_OPCODE_JEDEC_ID;
-
- /* Read the standardized part of the JEDEC ID. */
- rv = spi_transaction(&spi_devices[spi_nor_device->spi_controller],
- &cmd, 1, jedec_id, SPI_NOR_JEDEC_ID_BANKS);
- if (rv)
- return rv;
-
- *out_mfn_bank = 0;
- /* Go through the JEDEC ID a byte a time to looking for a manufacturer
- * ID instead of the next bank indicator (0x7F). */
- for (i = 0; i < SPI_NOR_JEDEC_ID_BANKS; i++) {
- *out_mfn_id = jedec_id[i];
- if (*out_mfn_id != 0x7F)
- return EC_SUCCESS;
- *out_mfn_bank += 1;
- }
- /* JEDEC Manufacturer ID should be available, perhaps there is a bus
- * problem or the JEP106 specification has grown the number of banks? */
- return EC_ERROR_UNKNOWN;
-}
-
-/**
- * Read a doubleword out of a SFDP table (DWs are 1-based like the SFDP spec).
- */
-static int spi_nor_read_sfdp_dword(
- const struct spi_nor_device_t *spi_nor_device,
- uint32_t table_offset,
- uint8_t table_double_word,
- uint32_t *out_dw) {
- uint8_t sfdp_cmd[5];
- /* Calculate the byte offset based on the double word. */
- uint32_t sfdp_offset = table_offset + ((table_double_word - 1) * 4);
-
- /* Read the DW out of the SFDP region. */
- sfdp_cmd[0] = SPI_NOR_OPCODE_SFDP;
- sfdp_cmd[1] = (sfdp_offset & 0xFF0000) >> 16;
- sfdp_cmd[2] = (sfdp_offset & 0xFF00) >> 8;
- sfdp_cmd[3] = (sfdp_offset & 0xFF);
- sfdp_cmd[4] = 0; /* Required extra cycle. */
- return spi_transaction(&spi_devices[spi_nor_device->spi_controller],
- sfdp_cmd, 5, (uint8_t *)out_dw, 4);
-}
-
-/**
- * Returns a bool (1 or 0) based on whether the parameter header double words
- * are for a SFDP v1.* Basic SPI Flash NOR Parameter Table.
- */
-static int is_basic_flash_parameter_table(uint8_t sfdp_major_rev,
- uint8_t sfdp_minor_rev,
- uint32_t parameter_header_dw1,
- uint32_t parameter_header_dw2)
-{
- if (sfdp_major_rev == 1 && sfdp_minor_rev < 5) {
- return (SFDP_GET_BITFIELD(SFDP_1_0_PARAMETER_HEADER_DW1_ID,
- parameter_header_dw1) ==
- BASIC_FLASH_PARAMETER_TABLE_1_0_ID);
- } else if (sfdp_major_rev == 1 && sfdp_minor_rev >= 5) {
- return ((SFDP_GET_BITFIELD(SFDP_1_5_PARAMETER_HEADER_DW1_ID_LSB,
- parameter_header_dw1) ==
- BASIC_FLASH_PARAMETER_TABLE_1_5_ID_LSB) &&
- (SFDP_GET_BITFIELD(SFDP_1_5_PARAMETER_HEADER_DW2_ID_MSB,
- parameter_header_dw2) ==
- BASIC_FLASH_PARAMETER_TABLE_1_5_ID_MSB));
- }
-
- return 0;
-}
-
-/**
- * Helper function to locate the SFDP Basic SPI Flash NOR Parameter Table.
- */
-static int locate_sfdp_basic_parameter_table(
- const struct spi_nor_device_t *spi_nor_device,
- uint8_t *out_sfdp_major_rev,
- uint8_t *out_sfdp_minor_rev,
- uint8_t *out_table_major_rev,
- uint8_t *out_table_minor_rev,
- uint32_t *out_table_offset,
- size_t *out_table_size)
-{
- int rv = EC_SUCCESS;
- uint8_t number_parameter_headers;
- uint32_t table_offset = 0;
- int table_found = 0;
- uint32_t dw1;
- uint32_t dw2;
-
- /* Read the SFDP header. */
- rv = spi_nor_read_sfdp_dword(spi_nor_device, 0, 1, &dw1);
- rv |= spi_nor_read_sfdp_dword(spi_nor_device, 0, 2, &dw2);
- if (rv)
- return rv;
-
- /* Ensure the SFDP table is valid. Note the versions are not checked
- * through the SFDP table header, as there may be a backwards
- * compatible, older basic parameter tables which are compatible with
- * this driver in the parameter headers. */
- if (!SFDP_HEADER_DW1_SFDP_SIGNATURE_VALID(dw1)) {
- CPRINTS(spi_nor_device, "SFDP signature invalid");
- return EC_ERROR_UNKNOWN;
- }
-
- *out_sfdp_major_rev =
- SFDP_GET_BITFIELD(SFDP_HEADER_DW2_SFDP_MAJOR, dw2);
- *out_sfdp_minor_rev =
- SFDP_GET_BITFIELD(SFDP_HEADER_DW2_SFDP_MINOR, dw2);
- CPRINTS(spi_nor_device, "SFDP v%d.%d discovered",
- *out_sfdp_major_rev, *out_sfdp_minor_rev);
-
- /* NPH is 0-based, so add 1. */
- number_parameter_headers =
- SFDP_GET_BITFIELD(SFDP_HEADER_DW2_NPH, dw2) + 1;
- CPRINTS(spi_nor_device, "There are %d SFDP parameter headers",
- number_parameter_headers);
-
- /* Search for the newest, compatible basic flash parameter table. */
- *out_table_major_rev = 0;
- *out_table_minor_rev = 0;
- while (number_parameter_headers) {
- uint8_t major_rev, minor_rev;
-
- table_offset += 8;
- number_parameter_headers--;
-
- /* Read this parameter header's two dwords. */
- rv = spi_nor_read_sfdp_dword(
- spi_nor_device, table_offset, 1, &dw1);
- rv |= spi_nor_read_sfdp_dword(
- spi_nor_device, table_offset, 2, &dw2);
- if (rv)
- return rv;
-
- /* Ensure it's the basic flash parameter table. */
- if (!is_basic_flash_parameter_table(*out_sfdp_major_rev,
- *out_sfdp_minor_rev,
- dw1, dw2))
- continue;
-
- /* The parameter header major and minor versioning is still the
- * same as SFDP 1.0. */
- major_rev = SFDP_GET_BITFIELD(
- SFDP_1_0_PARAMETER_HEADER_DW1_TABLE_MAJOR, dw1);
- minor_rev = SFDP_GET_BITFIELD(
- SFDP_1_0_PARAMETER_HEADER_DW1_TABLE_MINOR, dw1);
-
- /* Skip incompatible parameter tables. */
- if (major_rev != SPI_NOR_SUPPORTED_SFDP_MAJOR_VERSION)
- continue;
-
- /* If this parameter table has a lower revision compared to a
- * previously found compatible table, skip it. */
- if (minor_rev < *out_table_minor_rev)
- continue;
-
- table_found = 1;
- *out_table_major_rev = major_rev;
- *out_table_minor_rev = minor_rev;
- /* The parameter header ptp and ptl are still the same as
- * SFDP 1.0. */
- *out_table_offset = SFDP_GET_BITFIELD(
- SFDP_1_0_PARAMETER_HEADER_DW2_PTP, dw2);
- /* Convert the size from DW to Bytes. */
- *out_table_size = SFDP_GET_BITFIELD(
- SFDP_1_0_PARAMETER_HEADER_DW1_PTL, dw1) * 4;
- }
-
- if (!table_found) {
- CPRINTS(spi_nor_device,
- "No compatible Basic Flash Parameter Table found");
- return EC_ERROR_UNKNOWN;
- }
-
- CPRINTS(spi_nor_device, "Using Basic Flash Parameter Table v%d.%d",
- *out_sfdp_major_rev, *out_sfdp_minor_rev);
-
- return EC_SUCCESS;
-}
-
-/**
- * Helper function to lookup the part's page size in the SFDP Basic SPI Flash
- * NOR Parameter Table.
- */
-static int spi_nor_device_discover_sfdp_page_size(
- struct spi_nor_device_t *spi_nor_device,
- uint8_t basic_parameter_table_major_version,
- uint8_t basic_parameter_table_minor_version,
- uint32_t basic_parameter_table_offset,
- size_t *page_size)
-{
- int rv = EC_SUCCESS;
- uint32_t dw;
-
- if (basic_parameter_table_major_version == 1 &&
- basic_parameter_table_minor_version < 5) {
- /* Use the Basic Flash Parameter v1.0 page size reporting. */
- rv = spi_nor_read_sfdp_dword(
- spi_nor_device, basic_parameter_table_offset, 1, &dw);
- if (rv)
- return rv;
- if (SFDP_GET_BITFIELD(BFPT_1_0_DW1_WRITE_GRANULARITY, dw))
- *page_size = 64;
- else
- *page_size = 1;
-
- } else if (basic_parameter_table_major_version == 1 &&
- basic_parameter_table_minor_version >= 5) {
- /* Use the Basic Flash Parameter v1.5 page size reporting. */
- rv = spi_nor_read_sfdp_dword(spi_nor_device,
- basic_parameter_table_offset, 11, &dw);
- if (rv)
- return rv;
- *page_size =
- 1 << SFDP_GET_BITFIELD(BFPT_1_5_DW11_PAGE_SIZE, dw);
- }
-
- return EC_SUCCESS;
-}
-
-/**
- * Helper function to lookup the part's capacity in the SFDP Basic SPI Flash
- * NOR Parameter Table.
- */
-static int spi_nor_device_discover_sfdp_capacity(
- struct spi_nor_device_t *spi_nor_device,
- uint8_t basic_parameter_table_major_version,
- uint8_t basic_parameter_table_minor_version,
- uint32_t basic_parameter_table_offset,
- uint32_t *capacity)
-{
- int rv = EC_SUCCESS;
- uint32_t dw;
-
- /* First attempt to discover the device's capacity. */
- if (basic_parameter_table_major_version == 1) {
- /* Use the Basic Flash Parameter v1.0 capacity reporting. */
- rv = spi_nor_read_sfdp_dword(spi_nor_device,
- basic_parameter_table_offset, 2, &dw);
- if (rv)
- return rv;
-
- if (SFDP_GET_BITFIELD(BFPT_1_0_DW2_GT_2_GIBIBITS, dw)) {
- /* Ensure the capacity is less than 4GiB. */
- uint64_t tmp_capacity = 1 <<
- (SFDP_GET_BITFIELD(BFPT_1_0_DW2_N, dw) - 3);
- if (tmp_capacity > UINT32_MAX)
- return EC_ERROR_OVERFLOW;
- *capacity = tmp_capacity;
- } else {
- *capacity =
- 1 +
- (SFDP_GET_BITFIELD(BFPT_1_0_DW2_N, dw) >> 3);
- }
- }
-
- return EC_SUCCESS;
-}
-
-static int spi_nor_read_internal(const struct spi_nor_device_t *spi_nor_device,
- uint32_t offset, size_t size, uint8_t *data)
-{
- int rv;
-
- /* Split up the read operation into multiple transactions if the size
- * is larger than the maximum read size.
- */
- while (size > 0) {
- size_t read_size =
- MIN(size, CONFIG_SPI_NOR_MAX_READ_SIZE);
- size_t read_command_size;
-
- /* Set up the read command in the TX buffer. */
- buf[0] = SPI_NOR_OPCODE_SLOW_READ;
- if (spi_nor_device->in_4b_addressing_mode) {
- buf[1] = (offset & 0xFF000000) >> 24;
- buf[2] = (offset & 0xFF0000) >> 16;
- buf[3] = (offset & 0xFF00) >> 8;
- buf[4] = (offset & 0xFF);
- read_command_size = 5;
- } else { /* in 3 byte addressing mode */
- buf[1] = (offset & 0xFF0000) >> 16;
- buf[2] = (offset & 0xFF00) >> 8;
- buf[3] = (offset & 0xFF);
- read_command_size = 4;
- }
-
- rv = spi_transaction(
- &spi_devices[spi_nor_device->spi_controller],
- buf, read_command_size, data, read_size);
- if (rv)
- return rv;
-
- data += read_size;
- offset += read_size;
- size -= read_size;
- }
- return EC_SUCCESS;
-}
-
-/******************************************************************************/
-/* External Serial NOR Flash API available to other modules. */
-
-/**
- * Initialize the module, assumes the Serial NOR Flash devices are currently
- * all available for initialization. As part of the initialization the driver
- * will check if the part has a compatible SFDP Basic Flash Parameter table
- * and update the part's page_size, capacity, and forces the addressing mode.
- * Parts with more than 16MiB of capacity are initialized into 4B addressing
- * and parts with less are initialized into 3B addressing mode.
- *
- * WARNING: This must successfully return before invoking any other Serial NOR
- * Flash APIs.
- */
-int spi_nor_init(void)
-{
- int rv = EC_SUCCESS;
- size_t i;
-
- /* Initialize the state for each serial NOR flash device. */
- for (i = 0; i < SPI_NOR_DEVICE_COUNT; i++) {
- uint8_t sfdp_major_rev, sfdp_minor_rev;
- uint8_t table_major_rev, table_minor_rev;
- uint32_t table_offset;
- size_t table_size;
- struct spi_nor_device_t *spi_nor_device =
- &spi_nor_devices[i];
-
- rv |= locate_sfdp_basic_parameter_table(spi_nor_device,
- &sfdp_major_rev,
- &sfdp_minor_rev,
- &table_major_rev,
- &table_minor_rev,
- &table_offset,
- &table_size);
-
- /* If we failed to find a compatible SFDP Basic Flash Parameter
- * table, use the default capacity, page size, and addressing
- * mode values. */
- if (rv == EC_SUCCESS) {
- size_t page_size = 0;
- uint32_t capacity = 0;
-
- rv |= spi_nor_device_discover_sfdp_page_size(
- spi_nor_device,
- table_major_rev, table_minor_rev, table_offset,
- &page_size);
- rv |= spi_nor_device_discover_sfdp_capacity(
- spi_nor_device,
- table_major_rev, table_minor_rev, table_offset,
- &capacity);
- if (rv == EC_SUCCESS) {
- mutex_lock(&driver_mutex);
- spi_nor_device->capacity = capacity;
- spi_nor_device->page_size = page_size;
- CPRINTS(spi_nor_device,
- "Updated to SFDP params: %dKiB w/ %dB pages",
- spi_nor_device->capacity >> 10,
- spi_nor_device->page_size);
- mutex_unlock(&driver_mutex);
- }
- }
-
- /* Ensure the device is in a determined addressing state by
- * forcing a 4B addressing mode entry or exit depending on the
- * device capacity. If the device is larger than 16MiB, enter
- * 4B addressing mode. */
- rv |= spi_nor_set_4b_mode(spi_nor_device,
- spi_nor_device->capacity > 0x1000000);
- }
-
- return rv;
-}
-
-/**
- * Forces the Serial NOR Flash device to enter (or exit) 4 Byte addressing mode.
- *
- * WARNING:
- * 1) In 3 Byte addressing mode only 16MiB of Serial NOR Flash is accessible.
- * 2) If there's a second SPI controller communicating with this Serial
- * NOR Flash part on the board, the user is responsible for ensuring
- * addressing mode compatibility and cooperation.
- * 3) The user must ensure that multiple users do not trample on each other
- * by having multiple parties changing the device's addressing mode.
- *
- * @param spi_nor_device The Serial NOR Flash device to use.
- * @param enter_4b_addressing_mode Whether to enter (1) or exit (0) 4B mode.
- * @return ec_error_list (non-zero on error and timeout).
- */
-int spi_nor_set_4b_mode(struct spi_nor_device_t *spi_nor_device,
- int enter_4b_addressing_mode)
-{
- uint8_t cmd;
- int rv;
-
- rv = spi_nor_write_enable(spi_nor_device);
- if (rv)
- return rv;
-
- if (enter_4b_addressing_mode)
- cmd = SPI_NOR_DRIVER_SPECIFIED_OPCODE_ENTER_4B;
- else
- cmd = SPI_NOR_DRIVER_SPECIFIED_OPCODE_EXIT_4B;
-
- /* Claim the driver mutex to modify the device state. */
- mutex_lock(&driver_mutex);
-
- rv = spi_transaction(&spi_devices[spi_nor_device->spi_controller],
- &cmd, 1, NULL, 0);
- if (rv == EC_SUCCESS) {
- spi_nor_device->in_4b_addressing_mode =
- enter_4b_addressing_mode;
- }
-
- CPRINTS(spi_nor_device, "Entered %s Addressing Mode",
- enter_4b_addressing_mode ? "4-Byte" : "3-Byte");
-
- /* Release the driver mutex. */
- mutex_unlock(&driver_mutex);
- return rv;
-}
-
-/**
- * Read JEDEC Identifier.
- *
- * @param spi_nor_device The Serial NOR Flash device to use.
- * @param size Number of Bytes to read.
- * @param data Destination buffer for data.
- * @return ec_error_list (non-zero on error and timeout).
- */
-int spi_nor_read_jedec_id(const struct spi_nor_device_t *spi_nor_device,
- size_t size, uint8_t *data) {
- int rv;
- uint8_t cmd = SPI_NOR_OPCODE_JEDEC_ID;
-
- if (size > CONFIG_SPI_NOR_MAX_READ_SIZE)
- return EC_ERROR_INVAL;
- /* Claim the driver mutex. */
- mutex_lock(&driver_mutex);
- /* Read the JEDEC ID. */
- rv = spi_transaction(&spi_devices[spi_nor_device->spi_controller],
- &cmd, 1, data, size);
- /* Release the driver mutex. */
- mutex_unlock(&driver_mutex);
-
- return rv;
-}
-
-/**
- * Read from the Serial NOR Flash device.
- *
- * @param spi_nor_device The Serial NOR Flash device to use.
- * @param offset Flash offset to read.
- * @param size Number of Bytes to read.
- * @param data Destination buffer for data.
- * @return ec_error_list (non-zero on error and timeout).
- */
-int spi_nor_read(const struct spi_nor_device_t *spi_nor_device,
- uint32_t offset, size_t size, uint8_t *data)
-{
- int rv;
-
- /* Claim the driver mutex. */
- mutex_lock(&driver_mutex);
- rv = spi_nor_read_internal(spi_nor_device, offset, size, data);
- /* Release the driver mutex. */
- mutex_unlock(&driver_mutex);
-
- return rv;
-}
-
-/**
- * Erase flash on the Serial Flash Device.
- *
- * @param spi_nor_device The Serial NOR Flash device to use.
- * @param offset Flash offset to erase, must be aligned to the minimum physical
- * erase size.
- * @param size Number of Bytes to erase, must be a multiple of the the minimum
- * physical erase size.
- * @return ec_error_list (non-zero on error and timeout).
- */
-int spi_nor_erase(const struct spi_nor_device_t *spi_nor_device,
- uint32_t offset, size_t size)
-{
- int rv = EC_SUCCESS;
- size_t erase_command_size, erase_size;
- uint8_t erase_opcode;
-#ifdef CONFIG_SPI_NOR_SMART_ERASE
- BUILD_ASSERT((CONFIG_SPI_NOR_MAX_READ_SIZE % 4) == 0);
- uint8_t buffer[CONFIG_SPI_NOR_MAX_READ_SIZE] __aligned(4);
- size_t verify_offset, read_offset, read_size, read_left;
-#endif
-
- /* Invalid input */
- if ((offset % 4096 != 0) || (size % 4096 != 0) || (size < 4096))
- return EC_ERROR_INVAL;
-
- /* Claim the driver mutex. */
- mutex_lock(&driver_mutex);
-
- while (size > 0) {
- erase_opcode = SPI_NOR_DRIVER_SPECIFIED_OPCODE_4KIB_ERASE;
- erase_size = 4096;
-
- /* Wait for the previous operation to finish. */
- rv = spi_nor_wait(spi_nor_device);
- if (rv)
- goto err_free;
-
-#ifdef CONFIG_SPI_NOR_BLOCK_ERASE
- if (!(offset % 65536) && size >= 65536) {
- erase_opcode =
- SPI_NOR_DRIVER_SPECIFIED_OPCODE_64KIB_ERASE;
- erase_size = 65536;
- }
-#endif
-#ifdef CONFIG_SPI_NOR_SMART_ERASE
- read_offset = offset;
- read_left = erase_size;
- while (read_left) {
- read_size = MIN(read_left,
- CONFIG_SPI_NOR_MAX_READ_SIZE);
- /* Since CONFIG_SPI_NOR_MAX_READ_SIZE & erase_size are
- * both guaranteed to be multiples of 4.
- */
- assert(read_size >= 4 && (read_size % 4) == 0);
- rv = spi_nor_read_internal(spi_nor_device, read_offset,
- read_size, buffer);
-
- /* Note: the return value here is lost below
- * at the write enable, this is not a problem,
- * as this code is only an optimisation, if it
- * fails, the full erase functionality still
- * gets done, and the error from that returned
- */
- if (rv != EC_SUCCESS)
- break;
- /* Aligned word verify reduced the overall (read +
- * verify) time by ~20% (vs bytewise verify) on
- * an m3@24MHz & SPI@24MHz.
- */
- verify_offset = 0;
- while (verify_offset <= read_size - 4) {
- if (*(uint32_t *)(buffer + verify_offset)
- != 0xffffffff) {
- break;
- }
- verify_offset += 4;
- }
- if (verify_offset != read_size)
- break;
- read_offset += read_size;
- read_left -= read_size;
- watchdog_reload();
- }
- if (!read_left) {
- /* Sector/block already erased. */
- CPRINTS(spi_nor_device,
- "Skipping erase [%x:%x] "
- "(already erased)",
- offset, erase_size);
- offset += erase_size;
- size -= erase_size;
- continue;
- }
-#endif
- /* Enable writing to serial NOR flash. */
- rv = spi_nor_write_enable(spi_nor_device);
- if (rv)
- goto err_free;
-
- /* Set up the erase instruction. */
- buf[0] = erase_opcode;
- if (spi_nor_device->in_4b_addressing_mode) {
- buf[1] = (offset & 0xFF000000) >> 24;
- buf[2] = (offset & 0xFF0000) >> 16;
- buf[3] = (offset & 0xFF00) >> 8;
- buf[4] = (offset & 0xFF);
- erase_command_size = 5;
- } else { /* in 3 byte addressing mode */
- buf[1] = (offset & 0xFF0000) >> 16;
- buf[2] = (offset & 0xFF00) >> 8;
- buf[3] = (offset & 0xFF);
- erase_command_size = 4;
- }
-
- rv = spi_transaction(
- &spi_devices[spi_nor_device->spi_controller],
- buf, erase_command_size, NULL, 0);
- if (rv)
- goto err_free;
-
- offset += erase_size;
- size -= erase_size;
- }
-
- /* Wait for the previous operation to finish. */
- rv = spi_nor_wait(spi_nor_device);
-
-err_free:
- /* Release the driver mutex. */
- mutex_unlock(&driver_mutex);
-
- return rv;
-}
-
-/**
- * Write to the Serial NOR Flash device. Assumes already erased.
- *
- * @param spi_nor_device The Serial NOR Flash device to use.
- * @param offset Flash offset to write.
- * @param size Number of Bytes to write.
- * @param data Data to write to flash.
- * @return ec_error_list (non-zero on error and timeout).
- */
-int spi_nor_write(const struct spi_nor_device_t *spi_nor_device,
- uint32_t offset, size_t size, const uint8_t *data)
-{
- int rv = EC_SUCCESS;
- size_t effective_page_size;
-
- /* Claim the driver mutex. */
- mutex_lock(&driver_mutex);
-
- /* Ensure the device's page size fits in the driver's buffer, if not
- * emulate a smaller page size based on the buffer size. */
- effective_page_size = MIN(spi_nor_device->page_size,
- CONFIG_SPI_NOR_MAX_WRITE_SIZE);
-
- /* Split the write into multiple writes if the size is too large. */
- while (size > 0) {
- size_t prefix_size;
- /* Figure out the size of the next write within 1 page. */
- uint32_t page_offset = offset & (effective_page_size - 1);
- size_t write_size =
- MIN(size, effective_page_size - page_offset);
-
- /* Wait for the previous operation to finish. */
- rv = spi_nor_wait(spi_nor_device);
- if (rv)
- goto err_free;
-
- /* Enable writing to serial NOR flash. */
- rv = spi_nor_write_enable(spi_nor_device);
- if (rv)
- goto err_free;
-
- /* Set up the page program command. */
- buf[0] = SPI_NOR_OPCODE_PAGE_PROGRAM;
- if (spi_nor_device->in_4b_addressing_mode) {
- buf[1] = (offset & 0xFF000000) >> 24;
- buf[2] = (offset & 0xFF0000) >> 16;
- buf[3] = (offset & 0xFF00) >> 8;
- buf[4] = (offset & 0xFF);
- prefix_size = 5;
- } else { /* in 3 byte addressing mode */
- buf[1] = (offset & 0xFF0000) >> 16;
- buf[2] = (offset & 0xFF00) >> 8;
- buf[3] = (offset & 0xFF);
- prefix_size = 4;
- }
- /* Copy data to write into the buffer after the prefix. */
- memmove(buf + prefix_size, data, write_size);
-
- rv = spi_transaction(
- &spi_devices[spi_nor_device->spi_controller],
- buf, prefix_size + write_size, NULL, 0);
- if (rv)
- goto err_free;
-
- data += write_size;
- offset += write_size;
- size -= write_size;
- }
-
- /* Wait for the previous operation to finish. */
- rv = spi_nor_wait(spi_nor_device);
-
-err_free:
- /* Release the driver mutex. */
- mutex_unlock(&driver_mutex);
-
- return rv;
-}
-
-/******************************************************************************/
-/* Serial NOR Flash console commands. */
-
-#ifdef CONFIG_CMD_SPI_NOR
-static int command_spi_nor_info(int argc, char **argv)
-{
- int rv = EC_SUCCESS;
-
- uint8_t sfdp_major_rev, sfdp_minor_rev;
- uint8_t table_major_rev, table_minor_rev;
- uint32_t table_offset;
- uint8_t mfn_bank = 0, mfn_id = 0;
- size_t table_size;
- const struct spi_nor_device_t *spi_nor_device = 0;
- int spi_nor_device_index = 0;
- int spi_nor_device_index_limit = spi_nor_devices_used - 1;
-
- /* Set the device index limits if a device was specified. */
- if (argc == 2) {
- spi_nor_device_index = strtoi(argv[1], NULL, 0);
- if (spi_nor_device_index >= spi_nor_devices_used)
- return EC_ERROR_PARAM1;
- spi_nor_device_index_limit = spi_nor_device_index;
- } else if (argc != 1) {
- return EC_ERROR_PARAM_COUNT;
- }
-
- for (; spi_nor_device_index <= spi_nor_device_index_limit;
- spi_nor_device_index++) {
- spi_nor_device = &spi_nor_devices[spi_nor_device_index];
-
- ccprintf("Serial NOR Flash Device %d:\n", spi_nor_device_index);
- ccprintf("\tName: %s\n", spi_nor_device->name);
- ccprintf("\tSPI controller index: %d\n",
- spi_nor_device->spi_controller);
- ccprintf("\tTimeout: %d uSec\n",
- spi_nor_device->timeout_usec);
- ccprintf("\tCapacity: %d KiB\n",
- spi_nor_device->capacity >> 10),
- ccprintf("\tAddressing: %s addressing mode\n",
- spi_nor_device->in_4b_addressing_mode ? "4B" : "3B");
- ccprintf("\tPage Size: %d Bytes\n",
- spi_nor_device->page_size);
-
- /* Get JEDEC ID info. */
- rv = spi_nor_read_jedec_mfn_id(spi_nor_device, &mfn_bank,
- &mfn_id);
- if (rv != EC_SUCCESS)
- return rv;
- ccprintf("\tJEDEC ID bank %d manufacturing code 0x%x\n",
- mfn_bank, mfn_id);
-
- /* Get SFDP info. */
- if (locate_sfdp_basic_parameter_table(
- spi_nor_device, &sfdp_major_rev, &sfdp_minor_rev,
- &table_major_rev, &table_minor_rev, &table_offset,
- &table_size) != EC_SUCCESS) {
- ccputs("\tNo JEDEC SFDP support detected\n");
- continue; /* Go on to the next device. */
- }
- ccprintf("\tSFDP v%d.%d\n", sfdp_major_rev, sfdp_minor_rev);
- ccprintf("\tFlash Parameter Table v%d.%d (%dB @ 0x%x)\n",
- table_major_rev, table_minor_rev,
- table_size, table_offset);
- }
-
- return rv;
-}
-DECLARE_CONSOLE_COMMAND(spinorinfo, command_spi_nor_info,
- "[device]",
- "Report Serial NOR Flash device information");
-#endif /* CONFIG_CMD_SPI_NOR */
-
-#ifdef CONFIG_CMD_SPI_NOR
-static int command_spi_nor_erase(int argc, char **argv)
-{
- const struct spi_nor_device_t *spi_nor_device;
- int spi_nor_device_index;
- int offset = 0;
- int size = 4096;
- int rv;
-
- if (argc < 2)
- return EC_ERROR_PARAM_COUNT;
-
- spi_nor_device_index = strtoi(argv[1], NULL, 0);
- if (spi_nor_device_index >= spi_nor_devices_used)
- return EC_ERROR_PARAM1;
- spi_nor_device = &spi_nor_devices[spi_nor_device_index];
-
- rv = parse_offset_size(argc, argv, 2, &offset, &size);
- if (rv)
- return rv;
-
- ccprintf("Erasing %d bytes at 0x%x on %s...\n",
- size, offset, spi_nor_device->name);
- return spi_nor_erase(spi_nor_device, offset, size);
-}
-DECLARE_CONSOLE_COMMAND(spinorerase, command_spi_nor_erase,
- "device [offset] [size]",
- "Erase flash");
-#endif /* CONFIG_CMD_SPI_NOR */
-
-#ifdef CONFIG_CMD_SPI_NOR
-static int command_spi_nor_write(int argc, char **argv)
-{
- const struct spi_nor_device_t *spi_nor_device;
- int spi_nor_device_index;
- int offset = 0;
- int size = CONFIG_SPI_NOR_MAX_WRITE_SIZE;
- int rv;
- char *data;
- int i;
-
- if (argc < 2)
- return EC_ERROR_PARAM_COUNT;
-
- spi_nor_device_index = strtoi(argv[1], NULL, 0);
- if (spi_nor_device_index >= spi_nor_devices_used)
- return EC_ERROR_PARAM1;
- spi_nor_device = &spi_nor_devices[spi_nor_device_index];
-
- rv = parse_offset_size(argc, argv, 2, &offset, &size);
- if (rv)
- return rv;
-
- if (size > shared_mem_size())
- size = shared_mem_size();
-
- /* Acquire the shared memory buffer */
- rv = shared_mem_acquire(size, &data);
- if (rv) {
- ccputs("Can't get shared mem\n");
- return rv;
- }
-
- /* Fill the data buffer with a pattern */
- for (i = 0; i < size; i++)
- data[i] = i;
-
- ccprintf("Writing %d bytes to 0x%x on %s...\n",
- size, offset, spi_nor_device->name);
- rv = spi_nor_write(spi_nor_device, offset, size, data);
-
- /* Free the buffer */
- shared_mem_release(data);
-
- return rv;
-}
-DECLARE_CONSOLE_COMMAND(spinorwrite, command_spi_nor_write,
- "device [offset] [size]",
- "Write pattern to flash");
-#endif /* CONFIG_CMD_SPI_NOR */
-
-#ifdef CONFIG_CMD_SPI_NOR
-static int command_spi_nor_read(int argc, char **argv)
-{
- const struct spi_nor_device_t *spi_nor_device;
- int spi_nor_device_index;
- int offset = 0;
- int size = CONFIG_SPI_NOR_MAX_READ_SIZE;
- int rv;
- char *data;
- int i;
-
- if (argc < 2)
- return EC_ERROR_PARAM_COUNT;
-
- spi_nor_device_index = strtoi(argv[1], NULL, 0);
- if (spi_nor_device_index >= spi_nor_devices_used)
- return EC_ERROR_PARAM1;
- spi_nor_device = &spi_nor_devices[spi_nor_device_index];
-
- rv = parse_offset_size(argc, argv, 2, &offset, &size);
- if (rv)
- return rv;
-
- if (size > shared_mem_size())
- size = shared_mem_size();
-
- /* Acquire the shared memory buffer */
- rv = shared_mem_acquire(size, &data);
- if (rv) {
- ccputs("Can't get shared mem\n");
- return rv;
- }
-
- /* Read the data */
- ccprintf("Reading %d bytes from %s...",
- size, spi_nor_device->name);
- if (spi_nor_read(spi_nor_device, offset, size, data)) {
- rv = EC_ERROR_INVAL;
- goto err_free;
- }
-
- /* Dump it */
- for (i = 0; i < size; i++) {
- if ((offset + i) % 16) {
- ccprintf(" %02x", data[i]);
- } else {
- ccprintf("\n%08x: %02x", offset + i, data[i]);
- cflush();
- }
- }
- ccprintf("\n");
-
-err_free:
- /* Free the buffer */
- shared_mem_release(data);
-
- return rv;
-}
-DECLARE_CONSOLE_COMMAND(spinorread, command_spi_nor_read,
- "device [offset] [size]",
- "Read flash");
-#endif /* CONFIG_CMD_SPI_NOR */