mirror of https://github.com/ARMmbed/mbed-os.git
Merge d332210ca0
into d723bf9e55
commit
92f0c882c8
|
@ -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
|
||||
|
||||
/** @}*/
|
|
@ -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
|
||||
|
||||
/** @}*/
|
|
@ -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
|
||||
)
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue