pull/15303/merge
rogeryou 2024-10-08 18:46:17 +01:00 committed by GitHub
commit 92f0c882c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 514 additions and 0 deletions

View File

@ -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
/** @}*/

View File

@ -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
/** @}*/

View File

@ -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
)

View File

@ -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 <algorithm>
#include "PowerManagementBlockDevice.h"
#include <stdlib.h>
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);
}