From d332210ca0233f236f7b2724c2cd9f8ff62068b1 Mon Sep 17 00:00:00 2001 From: danielzhang Date: Tue, 14 Nov 2023 16:16:59 +0800 Subject: [PATCH] add power management block device driver --- .../blockdevice/PowerManagementBlockDevice.h | 163 ++++++++++++++ .../source/PowerManagementBlockDevice.cpp | 201 ++++++++++++++++++ .../CMakeLists.txt | 21 ++ .../power_management_block_device/main.cpp | 129 +++++++++++ 4 files changed, 514 insertions(+) create mode 100644 storage/blockdevice/include/blockdevice/PowerManagementBlockDevice.h create mode 100644 storage/blockdevice/source/PowerManagementBlockDevice.cpp create mode 100644 storage/blockdevice/tests/TESTS/blockdevice/power_management_block_device/CMakeLists.txt create mode 100644 storage/blockdevice/tests/TESTS/blockdevice/power_management_block_device/main.cpp diff --git a/storage/blockdevice/include/blockdevice/PowerManagementBlockDevice.h b/storage/blockdevice/include/blockdevice/PowerManagementBlockDevice.h new file mode 100644 index 00000000000..8b14e0034d1 --- /dev/null +++ b/storage/blockdevice/include/blockdevice/PowerManagementBlockDevice.h @@ -0,0 +1,163 @@ +/* mbed Microcontroller Library + * Copyright (c) 2022, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_POWER_MANAGEMENT_BLOCK_DEVICE_H +#define MBED_POWER_MANAGEMENT_BLOCK_DEVICE_H + +#include "BlockDevice.h" +#include "platform/mbed_assert.h" + +#if COMPONENT_QSPIF +enum { + QSPIF_HIGH_PERFORMANCE_MODE, + QSPIF_LOW_POWER_MODE, + QSPIF_DEEP_DOWN_MODE, + QSPIF_STANDBY_MODE, +}; +#endif + +namespace mbed { + +class PowerManagementBlockDevice : public BlockDevice { +public: + + /** Lifetime of the block device + * + * @param bd Block device to wrap as read only + */ + PowerManagementBlockDevice(BlockDevice *bd); + + virtual ~PowerManagementBlockDevice(); + + /** Initialize a block device + * + * @return 0 on success or a negative error code on failure + */ + virtual int init(); + + /** Deinitialize a block device + * + * @return 0 on success or a negative error code on failure + */ + virtual int deinit(); + + /** Ensure data on storage is in sync with the driver + * + * @return 0 on success or a negative error code on failure + */ + virtual int sync(); + + /** Read blocks from a block device + * + * @param buffer Buffer to read blocks into + * @param addr Address of block to begin reading from + * @param size Size to read in bytes, must be a multiple of read block size + * @return 0 on success, negative error code on failure + */ + virtual int read(void *buffer, bd_addr_t addr, bd_size_t size); + + /** Program blocks to a block device + * + * The blocks must have been erased prior to being programmed + * + * @param buffer Buffer of data to write to blocks + * @param addr Address of block to begin writing to + * @param size Size to write in bytes, must be a multiple of program block size + * @return 0 on success, negative error code on failure + */ + virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size); + + /** Erase blocks on a block device + * + * The state of an erased block is undefined until it has been programmed, + * unless get_erase_value returns a non-negative byte value + * + * @param addr Address of block to begin erasing + * @param size Size to erase in bytes, must be a multiple of erase block size + * @return 0 on success, negative error code on failure + */ + virtual int erase(bd_addr_t addr, bd_size_t size); + + /** Get the size of a readable block + * + * @return Size of a readable block in bytes + */ + virtual bd_size_t get_read_size() const; + + /** Get the size of a programmable block + * + * @return Size of a programmable block in bytes + */ + virtual bd_size_t get_program_size() const; + + /** Get the size of an erasable block + * + * @return Size of an erasable block in bytes + */ + virtual bd_size_t get_erase_size() const; + + /** Get the size of an erasable block given address + * + * @param addr Address within the erasable block + * @return Size of an erasable block in bytes + * @note Must be a multiple of the program size + */ + virtual bd_size_t get_erase_size(bd_addr_t addr) const; + + /** Get the value of storage when erased + * + * If get_erase_value returns a non-negative byte value, the underlying + * storage is set to that value when erased, and storage containing + * that value can be programmed without another erase. + * + * @return The value of storage when erased, or -1 if you can't + * rely on the value of erased storage + */ + virtual int get_erase_value() const; + + /** Get the total size of the underlying device + * + * @return Size of the underlying device in bytes + */ + virtual bd_size_t size() const; + + /** Get the BlockDevice class type. + * + * @return A string represent the BlockDevice class type. + */ + virtual const char *get_type() const; + + /** Switch the Block Device power management mode + * @param pm_mode Power management mode of Block Device + * @return 0 on success, negative error code on failure + */ + virtual int switch_power_management_mode(int pm_mode); + +private: + BlockDevice *_bd; +}; + +} // namespace mbed + +// Added "using" for backwards compatibility +#ifndef MBED_NO_GLOBAL_USING_DIRECTIVE +using mbed::PowerManagementBlockDevice; +#endif + +#endif + +/** @}*/ diff --git a/storage/blockdevice/source/PowerManagementBlockDevice.cpp b/storage/blockdevice/source/PowerManagementBlockDevice.cpp new file mode 100644 index 00000000000..8dca0379f4e --- /dev/null +++ b/storage/blockdevice/source/PowerManagementBlockDevice.cpp @@ -0,0 +1,201 @@ +/* mbed Microcontroller Library + * Copyright (c) 2022, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "blockdevice/PowerManagementBlockDevice.h" +#include "platform/mbed_error.h" + +#if COMPONENT_QSPIF +#include "drivers/QSPI.h" +#define QSPI_CMD_WREN 0x06 +#define QSPI_CMD_RDCR0 0x15 +#define QSPI_CMD_WRSR 0x01 +#define QSPI_CMD_RDSR 0x05 +#define QSPI_CMD_NOP 0x00 +#define QSPI_CMD_DP 0xB9 +#define QSPI_LH_BIT_MASK 0x02 + +mbed::QSPI _qspif(MBED_CONF_QSPIF_QSPI_IO0, + MBED_CONF_QSPIF_QSPI_IO1, + MBED_CONF_QSPIF_QSPI_IO2, + MBED_CONF_QSPIF_QSPI_IO3, + MBED_CONF_QSPIF_QSPI_SCK, + MBED_CONF_QSPIF_QSPI_CSN, + MBED_CONF_QSPIF_QSPI_POLARITY_MODE); +#endif + +namespace mbed { + +PowerManagementBlockDevice::PowerManagementBlockDevice(BlockDevice *bd) + : _bd(bd) +{ + MBED_ASSERT(_bd); +} + +PowerManagementBlockDevice::~PowerManagementBlockDevice() +{ + deinit(); +} + +int PowerManagementBlockDevice::init() +{ + return _bd->init(); +} + +int PowerManagementBlockDevice::deinit() +{ + return _bd->deinit(); +} + +int PowerManagementBlockDevice::sync() +{ + return _bd->sync(); +} + +int PowerManagementBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) +{ + return _bd->read(buffer, addr, size); +} + +int PowerManagementBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size) +{ + return _bd->program(buffer, addr, size); +} + +int PowerManagementBlockDevice::erase(bd_addr_t addr, bd_size_t size) +{ + return _bd->erase(addr, size); +} + +bd_size_t PowerManagementBlockDevice::get_read_size() const +{ + return _bd->get_read_size(); +} + +bd_size_t PowerManagementBlockDevice::get_program_size() const +{ + return _bd->get_program_size(); +} + +bd_size_t PowerManagementBlockDevice::get_erase_size() const +{ + return _bd->get_erase_size(); +} + +bd_size_t PowerManagementBlockDevice::get_erase_size(bd_addr_t addr) const +{ + return _bd->get_erase_size(addr); +} + +int PowerManagementBlockDevice::get_erase_value() const +{ + return _bd->get_erase_value(); +} + +bd_size_t PowerManagementBlockDevice::size() const +{ + return _bd->size(); +} + +const char *PowerManagementBlockDevice::get_type() const +{ + if (_bd != NULL) { + return _bd->get_type(); + } + + return NULL; +} + +int PowerManagementBlockDevice::switch_power_management_mode(int pm_mode) +{ +#if COMPONENT_QSPIF + qspi_status_t status = QSPI_STATUS_OK ; + uint8_t wren_inst = QSPI_CMD_WREN; + uint8_t sr_reg[3] = {0}; + uint8_t rdcr_inst = QSPI_CMD_RDCR0, wrsr_inst = QSPI_CMD_WRSR, rdsr_inst = QSPI_CMD_RDSR; + uint8_t dp_inst = 0; + + if (QSPI_STATUS_OK != _qspif.configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ADDR_SIZE_24, QSPI_CFG_BUS_SINGLE, + 0, QSPI_CFG_BUS_SINGLE, 0)) { + return -1; + } + + if (QSPI_STATUS_OK != _qspif.command_transfer(wren_inst, -1, NULL, 0, NULL, 0)) { + return -1; + } + + switch (pm_mode) { + case QSPIF_HIGH_PERFORMANCE_MODE : + if (QSPI_STATUS_OK != _qspif.command_transfer(rdsr_inst, -1, NULL, 0, (const char *)&sr_reg[0], 1)) { + return -1; + } + + if (QSPI_STATUS_OK != _qspif.command_transfer(rdcr_inst, -1, NULL, 0, (const char *)&sr_reg[1], 2)) { + return -1; + } + + sr_reg[2] = QSPI_LH_BIT_MASK; + + if (QSPI_STATUS_OK != _qspif.command_transfer(wrsr_inst, -1, (const char *)&sr_reg[0], 3, NULL, 0)) { + return -1; + } + break; + case QSPIF_LOW_POWER_MODE : + if (QSPI_STATUS_OK != _qspif.command_transfer(rdsr_inst, -1, NULL, 0, (const char *)&sr_reg[0], 1)) { + return -1; + } + + if (QSPI_STATUS_OK != _qspif.command_transfer(rdcr_inst, -1, NULL, 0, (const char *)&sr_reg[1], 2)) { + return -1; + } + + sr_reg[2] = 0; + + if (QSPI_STATUS_OK != _qspif.command_transfer(wrsr_inst, -1, (const char *)&sr_reg[0], 3, NULL, 0)) { + return -1; + } + break; + case QSPIF_DEEP_DOWN_MODE : + dp_inst = QSPI_CMD_DP; + if (QSPI_STATUS_OK != _qspif.command_transfer(dp_inst, -1, NULL, 0, NULL, 0)) { + return -1; + } + + for (int i, k = 0; i < 10000; i++) { + k++; + } + break; + case QSPIF_STANDBY_MODE : + dp_inst = QSPI_CMD_NOP; + if (QSPI_STATUS_OK != _qspif.command_transfer(dp_inst, -1, NULL, 0, NULL, 0)) { + return -1; + } + + for (int i, k = 0; i < 10000; i++) { + k++; + } + break; + default : + break; + } + + return 0; +#endif +} + +} // namespace mbed + +/** @}*/ diff --git a/storage/blockdevice/tests/TESTS/blockdevice/power_management_block_device/CMakeLists.txt b/storage/blockdevice/tests/TESTS/blockdevice/power_management_block_device/CMakeLists.txt new file mode 100644 index 00000000000..2cbd8a4b33f --- /dev/null +++ b/storage/blockdevice/tests/TESTS/blockdevice/power_management_block_device/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2022 ARM Limited. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.19.0 FATAL_ERROR) + +set(MBED_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../.. CACHE INTERNAL "") +set(TEST_TARGET mbed-storage-blockdevice-power_management_block_device) + +include(${MBED_PATH}/tools/cmake/mbed_greentea.cmake) + +project(${TEST_TARGET}) + +mbed_greentea_add_test( + TEST_NAME + ${TEST_TARGET} + TEST_SOURCES + main.cpp + TEST_REQUIRED_LIBS + mbed-storage-blockdevice +) + diff --git a/storage/blockdevice/tests/TESTS/blockdevice/power_management_block_device/main.cpp b/storage/blockdevice/tests/TESTS/blockdevice/power_management_block_device/main.cpp new file mode 100644 index 00000000000..1284dad016a --- /dev/null +++ b/storage/blockdevice/tests/TESTS/blockdevice/power_management_block_device/main.cpp @@ -0,0 +1,129 @@ +/* mbed Microcontroller Library + * Copyright (c) 2022 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include "BlockDevice.h" +#include +#include "PowerManagementBlockDevice.h" +#include + +using namespace utest::v1; + +#if COMPONENT_QSPIF +#include "QSPIFBlockDevice.h" +QSPIFBlockDevice pm_bd(MBED_CONF_QSPIF_QSPI_IO0, + MBED_CONF_QSPIF_QSPI_IO1, + MBED_CONF_QSPIF_QSPI_IO2, + MBED_CONF_QSPIF_QSPI_IO3, + MBED_CONF_QSPIF_QSPI_SCK, + MBED_CONF_QSPIF_QSPI_CSN, + MBED_CONF_QSPIF_QSPI_POLARITY_MODE, + MBED_CONF_QSPIF_QSPI_FREQ); +#endif + +BlockDevice *_bd = &pm_bd; +static const bd_size_t read_size = 1; +static const bd_size_t prog_size = 1; +static const bd_size_t erase_size = 4096; +static const bd_size_t block_size = 65536; +static const bd_size_t num_blocks = 128; +static const bd_size_t test_buf_size = 64; +static const uint8_t blank = 0xFF; + +// Simple test for all APIs +void functionality_test() +{ + /*before the test, set the frequency to 80Mhz.*/ + PowerManagementBlockDevice bd(_bd); + int err = bd.init(); + TEST_ASSERT_EQUAL(0, err); + + TEST_ASSERT_EQUAL(num_blocks * block_size, bd.size()); + TEST_ASSERT_EQUAL(read_size, bd.get_read_size()); + TEST_ASSERT_EQUAL(prog_size, bd.get_program_size()); + TEST_ASSERT_EQUAL(erase_size, bd.get_erase_size()); + TEST_ASSERT_EQUAL(blank, bd.get_erase_value()); + + uint8_t read_buf[test_buf_size], write_buf[test_buf_size]; + + srand(1); + for (bd_size_t i = 0; i < test_buf_size; i++) { + write_buf[i] = 0xff & rand(); + } + + err = bd.erase(0, erase_size); + TEST_ASSERT_EQUAL(err, 0); + + err = bd.switch_power_management_mode(QSPIF_DEEP_DOWN_MODE); + TEST_ASSERT_EQUAL(err, 0); + + err = bd.program(write_buf, 0, prog_size); + if (err) { + utest_printf("\nprogram fail, the device switch into deep down mode\n"); + } + + err = bd.switch_power_management_mode(QSPIF_STANDBY_MODE); + TEST_ASSERT_EQUAL(err, 0); + + err = bd.program(write_buf, 0, prog_size); + TEST_ASSERT_EQUAL(err, 0); + + if (!err) { + utest_printf("\nprogram successfully, the device switch into standby mode\n"); + } + + for (int i, k = 0; i < 10000; i++) { + k++; + } + + err = bd.switch_power_management_mode(QSPIF_LOW_POWER_MODE); + TEST_ASSERT_EQUAL(err, 0); + + err = bd.read(read_buf, 0, prog_size); + if (memcmp(write_buf, read_buf, prog_size) != 0) { + utest_printf("\nread fail, the device switch into low power mode\n"); + } + + err = bd.switch_power_management_mode(QSPIF_HIGH_PERFORMANCE_MODE); + TEST_ASSERT_EQUAL(err, 0); + + err = bd.read(read_buf, 0, prog_size); + TEST_ASSERT_EQUAL(err, 0); + if (memcmp(write_buf, read_buf, prog_size) == 0) { + utest_printf("\nread successfully, the device switch into high performance mode\n"); + } +} + +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(30, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("PowerManagementBlockDevice functionality test", functionality_test), +}; + +Specification specification(test_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +