drivers: mspi: add ambiq mspi timing scan utility
The utility may be used during development stage to get ambiq platform specific timing parameters for mspi devices. Signed-off-by: Swift Tian <swift.tian@ambiq.com>
This commit is contained in:
parent
cc5c142535
commit
69c14e37ac
@ -156,7 +156,7 @@
|
||||
xip-config = <1 0 DT_SIZE_M(64) 0>;
|
||||
ce-break-config = <1024 4>;
|
||||
ambiq,timing-config-mask = <0x62>;
|
||||
ambiq,timing-config = <8 7 1 0 0 3 10 0>;
|
||||
ambiq,timing-config = <8 7 1 0 0 3 10>;
|
||||
zephyr,pm-device-runtime-auto;
|
||||
};
|
||||
};
|
||||
@ -195,7 +195,7 @@
|
||||
xip-config = <1 0 DT_SIZE_M(8) 0>;
|
||||
ce-break-config = <0 0>;
|
||||
ambiq,timing-config-mask = <0x62>;
|
||||
ambiq,timing-config = <0 16 1 0 0 5 20 0>;
|
||||
ambiq,timing-config = <0 16 1 0 0 5 20>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -48,6 +48,7 @@ struct memc_mspi_aps_z8_config {
|
||||
MSPI_SCRAMBLE_CFG_STRUCT_DECLARE(tar_scramble_cfg)
|
||||
MSPI_TIMING_CFG_STRUCT_DECLARE(tar_timing_cfg)
|
||||
MSPI_TIMING_PARAM_DECLARE(timing_cfg_mask)
|
||||
MSPI_XIP_BASE_ADDR_DECLARE(xip_base_addr)
|
||||
|
||||
bool sw_multi_periph;
|
||||
bool pm_dev_rt_auto;
|
||||
@ -598,6 +599,7 @@ static int memc_mspi_aps_z8_init(const struct device *psram)
|
||||
tar_timing_cfg, MSPI_TIMING_CONFIG(n)) \
|
||||
MSPI_OPTIONAL_CFG_STRUCT_INIT(CONFIG_MSPI_TIMING, \
|
||||
timing_cfg_mask, MSPI_TIMING_CONFIG_MASK(n)) \
|
||||
MSPI_XIP_BASE_ADDR_INIT(xip_base_addr, DT_INST_BUS(n)) \
|
||||
.sw_multi_periph = DT_PROP(DT_INST_BUS(n), software_multiperipheral), \
|
||||
.pm_dev_rt_auto = DT_INST_PROP(n, zephyr_pm_device_runtime_auto) \
|
||||
}; \
|
||||
|
||||
@ -5,5 +5,6 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/mspi.h)
|
||||
zephyr_library()
|
||||
zephyr_library_sources_ifdef(CONFIG_MSPI_AMBIQ_AP3 mspi_ambiq_ap3.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MSPI_AMBIQ_AP5 mspi_ambiq_ap5.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MSPI_AMBIQ_TIMING_SCAN mspi_ambiq_timing_scan.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MSPI_DW mspi_dw.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MSPI_EMUL mspi_emul.c)
|
||||
|
||||
@ -46,4 +46,29 @@ config MSPI_AMBIQ_BUFF_ALIGNMENT
|
||||
help
|
||||
This option specifies the mspi buffer alignment
|
||||
|
||||
config MSPI_AMBIQ_TIMING_SCAN
|
||||
bool "Turn on Ambiq timing scan utility"
|
||||
depends on MSPI_TIMING
|
||||
help
|
||||
Enable timing scan will cost time and space during init
|
||||
|
||||
if MSPI_AMBIQ_TIMING_SCAN
|
||||
|
||||
config MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE
|
||||
int "Timing scan buffer size in bytes"
|
||||
default 131072
|
||||
help
|
||||
This option specifies the memory buffer size used for timing scan,
|
||||
carefully adjust this size between a few KBs to hundreds of KBs to achieve
|
||||
balance between time and accurary
|
||||
|
||||
config MSPI_AMBIQ_TIMING_SCAN_DATA_SIZE
|
||||
int "Total data sizes in bytes used in timing scan"
|
||||
default 131072
|
||||
help
|
||||
This option specifies the total data size used in timing scan,
|
||||
it must be divisible by MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE
|
||||
|
||||
endif # MSPI_AMBIQ_TIMING_SCAN
|
||||
|
||||
endif # MSPI_AMBIQ_CONTROLLER
|
||||
|
||||
@ -27,6 +27,54 @@
|
||||
|
||||
#define MSPI_CQ_MAX_ENTRY MSPI0_CQCURIDX_CQCURIDX_Msk
|
||||
|
||||
enum mspi_ambiq_timing_param {
|
||||
MSPI_AMBIQ_SET_WLC = BIT(0),
|
||||
MSPI_AMBIQ_SET_RLC = BIT(1),
|
||||
MSPI_AMBIQ_SET_TXNEG = BIT(2),
|
||||
MSPI_AMBIQ_SET_RXNEG = BIT(3),
|
||||
MSPI_AMBIQ_SET_RXCAP = BIT(4),
|
||||
MSPI_AMBIQ_SET_TXDQSDLY = BIT(5),
|
||||
MSPI_AMBIQ_SET_RXDQSDLY = BIT(6),
|
||||
};
|
||||
|
||||
enum mspi_ambiq_timing_scan_type {
|
||||
MSPI_AMBIQ_TIMING_SCAN_MEMC = 0,
|
||||
MSPI_AMBIQ_TIMING_SCAN_FLASH,
|
||||
};
|
||||
|
||||
struct mspi_ambiq_timing_scan_range {
|
||||
int8_t rlc_start;
|
||||
int8_t rlc_end;
|
||||
int8_t txneg_start;
|
||||
int8_t txneg_end;
|
||||
int8_t rxneg_start;
|
||||
int8_t rxneg_end;
|
||||
int8_t rxcap_start;
|
||||
int8_t rxcap_end;
|
||||
int8_t txdqs_start;
|
||||
int8_t txdqs_end;
|
||||
int8_t rxdqs_start;
|
||||
int8_t rxdqs_end;
|
||||
};
|
||||
|
||||
struct mspi_ambiq_timing_cfg {
|
||||
uint8_t ui8WriteLatency;
|
||||
uint8_t ui8TurnAround;
|
||||
bool bTxNeg;
|
||||
bool bRxNeg;
|
||||
bool bRxCap;
|
||||
uint32_t ui32TxDQSDelay;
|
||||
uint32_t ui32RxDQSDelay;
|
||||
};
|
||||
|
||||
struct mspi_ambiq_timing_scan {
|
||||
struct mspi_ambiq_timing_scan_range range;
|
||||
enum mspi_ambiq_timing_scan_type scan_type;
|
||||
uint32_t min_window;
|
||||
uint32_t device_addr;
|
||||
struct mspi_ambiq_timing_cfg result;
|
||||
};
|
||||
|
||||
#define TIMING_CFG_GET_RX_DUMMY(cfg) \
|
||||
{ \
|
||||
mspi_timing_cfg *timing = (mspi_timing_cfg *)cfg; \
|
||||
@ -48,7 +96,6 @@
|
||||
.bRxCap = DT_INST_PROP_BY_IDX(n, ambiq_timing_config, 4), \
|
||||
.ui32TxDQSDelay = DT_INST_PROP_BY_IDX(n, ambiq_timing_config, 5), \
|
||||
.ui32RxDQSDelay = DT_INST_PROP_BY_IDX(n, ambiq_timing_config, 6), \
|
||||
.ui32RXDQSDelayEXT = DT_INST_PROP_BY_IDX(n, ambiq_timing_config, 7), \
|
||||
}
|
||||
|
||||
#define MSPI_AMBIQ_TIMING_CONFIG_MASK(n) DT_INST_PROP(n, ambiq_timing_config_mask)
|
||||
@ -57,38 +104,11 @@
|
||||
(MSPI1_BASE - MSPI0_BASE))
|
||||
|
||||
|
||||
struct mspi_ambiq_timing_cfg {
|
||||
uint8_t ui8WriteLatency;
|
||||
uint8_t ui8TurnAround;
|
||||
bool bTxNeg;
|
||||
bool bRxNeg;
|
||||
bool bRxCap;
|
||||
uint32_t ui32TxDQSDelay;
|
||||
uint32_t ui32RxDQSDelay;
|
||||
uint32_t ui32RXDQSDelayEXT;
|
||||
};
|
||||
|
||||
enum mspi_ambiq_timing_param {
|
||||
MSPI_AMBIQ_SET_WLC = BIT(0),
|
||||
MSPI_AMBIQ_SET_RLC = BIT(1),
|
||||
MSPI_AMBIQ_SET_TXNEG = BIT(2),
|
||||
MSPI_AMBIQ_SET_RXNEG = BIT(3),
|
||||
MSPI_AMBIQ_SET_RXCAP = BIT(4),
|
||||
MSPI_AMBIQ_SET_TXDQSDLY = BIT(5),
|
||||
MSPI_AMBIQ_SET_RXDQSDLY = BIT(6),
|
||||
MSPI_AMBIQ_SET_RXDQSDLYEXT = BIT(7),
|
||||
};
|
||||
|
||||
#define TIMING_CFG_GET_RX_DUMMY(cfg) \
|
||||
{ \
|
||||
mspi_timing_cfg *timing = (mspi_timing_cfg *)cfg; \
|
||||
timing->ui8TurnAround; \
|
||||
}
|
||||
|
||||
#define TIMING_CFG_SET_RX_DUMMY(cfg, num) \
|
||||
{ \
|
||||
mspi_timing_cfg *timing = (mspi_timing_cfg *)cfg; \
|
||||
timing->ui8TurnAround = num; \
|
||||
}
|
||||
int mspi_ambiq_timing_scan(const struct device *dev,
|
||||
const struct device *bus,
|
||||
const struct mspi_dev_id *dev_id,
|
||||
uint32_t param_mask,
|
||||
struct mspi_ambiq_timing_cfg *timing,
|
||||
struct mspi_ambiq_timing_scan *scan);
|
||||
|
||||
#endif
|
||||
|
||||
497
drivers/mspi/mspi_ambiq_timing_scan.c
Normal file
497
drivers/mspi/mspi_ambiq_timing_scan.c
Normal file
@ -0,0 +1,497 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Ambiq Micro Inc. <www.ambiq.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/mspi.h>
|
||||
#include <zephyr/cache.h>
|
||||
#include <zephyr/drivers/flash.h>
|
||||
|
||||
#define LOG_LEVEL CONFIG_MSPI_LOG_LEVEL
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(mspi_ambiq_timing_scan);
|
||||
|
||||
#include "mspi_ambiq.h"
|
||||
|
||||
BUILD_ASSERT(CONFIG_MSPI_AMBIQ_TIMING_SCAN_DATA_SIZE %
|
||||
CONFIG_MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE == 0);
|
||||
#if defined(CONFIG_SOC_SERIES_APOLLO4X)
|
||||
BUILD_ASSERT(CONFIG_MSPI_AMBIQ_BUFF_ALIGNMENT == 16);
|
||||
#elif defined(CONFIG_SOC_SERIES_APOLLO5X)
|
||||
#if CONFIG_DCACHE
|
||||
BUILD_ASSERT(CONFIG_MSPI_AMBIQ_BUFF_ALIGNMENT == CONFIG_DCACHE_LINE_SIZE);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct longest_ones {
|
||||
int start;
|
||||
int length;
|
||||
};
|
||||
|
||||
uint8_t txdata_buff[CONFIG_MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE]
|
||||
__attribute__((section(".ambiq_dma_buff")));
|
||||
uint8_t rxdata_buff[CONFIG_MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE]
|
||||
__attribute__((section(".ambiq_dma_buff")));
|
||||
|
||||
static int flash_write_data(const struct device *dev,
|
||||
uint32_t device_addr)
|
||||
{
|
||||
int ret;
|
||||
uint32_t num_bytes_left = CONFIG_MSPI_AMBIQ_TIMING_SCAN_DATA_SIZE;
|
||||
uint32_t test_bytes = 0;
|
||||
|
||||
ret = flash_erase(dev, device_addr, num_bytes_left);
|
||||
if (ret) {
|
||||
LOG_ERR("timing scan flash erase failed.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (num_bytes_left) {
|
||||
if (num_bytes_left > CONFIG_MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE) {
|
||||
test_bytes = CONFIG_MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE;
|
||||
num_bytes_left -= CONFIG_MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE;
|
||||
} else {
|
||||
test_bytes = num_bytes_left;
|
||||
num_bytes_left = 0;
|
||||
}
|
||||
|
||||
LOG_DBG("Write at %08x, size %08x\n", device_addr, test_bytes);
|
||||
ret = flash_write(dev, device_addr, txdata_buff, test_bytes);
|
||||
if (ret) {
|
||||
LOG_ERR("timing scan flash write failed.\n");
|
||||
return ret;
|
||||
}
|
||||
device_addr += test_bytes;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int flash_read_scan(const struct device *dev,
|
||||
uint32_t device_addr)
|
||||
{
|
||||
int ret;
|
||||
uint32_t num_bytes_left = CONFIG_MSPI_AMBIQ_TIMING_SCAN_DATA_SIZE;
|
||||
uint32_t test_bytes = 0;
|
||||
|
||||
while (num_bytes_left) {
|
||||
if (num_bytes_left > CONFIG_MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE) {
|
||||
test_bytes = CONFIG_MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE;
|
||||
num_bytes_left -= CONFIG_MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE;
|
||||
} else {
|
||||
test_bytes = num_bytes_left;
|
||||
num_bytes_left = 0;
|
||||
}
|
||||
|
||||
LOG_DBG("Read at %08x, size %08x\n", device_addr, test_bytes);
|
||||
ret = flash_read(dev, device_addr, rxdata_buff, test_bytes);
|
||||
if (ret) {
|
||||
LOG_ERR("timing scan flash read failed.\n");
|
||||
return ret;
|
||||
}
|
||||
sys_cache_data_flush_and_invd_all();
|
||||
if (memcmp(txdata_buff, rxdata_buff, test_bytes)) {
|
||||
return 1;
|
||||
}
|
||||
device_addr += test_bytes;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SECTOR_SIZE 1024
|
||||
|
||||
static void prepare_test_pattern(uint8_t *buff, uint32_t len)
|
||||
{
|
||||
uint32_t *ui32_tx_ptr = (uint32_t *)buff;
|
||||
uint8_t *ui8_tx_ptr = (uint8_t *)buff;
|
||||
uint32_t pattern_index = 0;
|
||||
uint32_t byte_left = len - len % SECTOR_SIZE;
|
||||
|
||||
while (byte_left > 0) {
|
||||
|
||||
switch (pattern_index) {
|
||||
case 0:
|
||||
/* 0x5555AAAA */
|
||||
for (uint32_t i = 0; i < SECTOR_SIZE / 4; i++) {
|
||||
ui32_tx_ptr[i] = (0x5555AAAA);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
/* 0xFFFF0000 */
|
||||
for (uint32_t i = 0; i < SECTOR_SIZE / 4; i++) {
|
||||
ui32_tx_ptr[i] = (0xFFFF0000);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
/* walking */
|
||||
for (uint32_t i = 0; i < SECTOR_SIZE; i++) {
|
||||
ui8_tx_ptr[i] = 0x01 << (i % 8);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
/* incremental from 1 */
|
||||
for (uint32_t i = 0; i < SECTOR_SIZE; i++) {
|
||||
ui8_tx_ptr[i] = ((i + 1) & 0xFF);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
/* decremental from 0xff */
|
||||
for (uint32_t i = 0; i < SECTOR_SIZE; i++) {
|
||||
ui8_tx_ptr[i] = (0xff - i) & 0xFF;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* incremental from 1 */
|
||||
for (uint32_t i = 0; i < SECTOR_SIZE; i++) {
|
||||
ui8_tx_ptr[i] = ((i + 1) & 0xFF);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
byte_left -= SECTOR_SIZE;
|
||||
ui32_tx_ptr += SECTOR_SIZE / 4;
|
||||
ui8_tx_ptr += SECTOR_SIZE;
|
||||
pattern_index++;
|
||||
pattern_index = pattern_index % 5;
|
||||
}
|
||||
}
|
||||
|
||||
static void find_longest_ones(const unsigned char *data, int bit_len,
|
||||
struct longest_ones *result)
|
||||
{
|
||||
/* Default result if no 1s are found */
|
||||
int current_len = 0;
|
||||
int current_start = -1;
|
||||
|
||||
result->start = -1;
|
||||
result->length = 0;
|
||||
|
||||
for (int i = 0; i < bit_len; i++) {
|
||||
int byte_index = i / 8;
|
||||
int bit_index = i % 8;
|
||||
|
||||
if (data[byte_index] & (1 << bit_index)) {
|
||||
if (current_len == 0) {
|
||||
current_start = i;
|
||||
}
|
||||
current_len++;
|
||||
} else {
|
||||
if (current_len > result->length) {
|
||||
result->start = current_start;
|
||||
result->length = current_len;
|
||||
}
|
||||
current_len = 0;
|
||||
}
|
||||
}
|
||||
if (current_len > result->length) {
|
||||
result->start = current_start;
|
||||
result->length = current_len;
|
||||
}
|
||||
}
|
||||
|
||||
static int find_mid_point(const unsigned char *data, int bit_len)
|
||||
{
|
||||
struct longest_ones result;
|
||||
|
||||
find_longest_ones(data, bit_len, &result);
|
||||
|
||||
if (result.start == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return result.start + (result.length - 1) / 2;
|
||||
}
|
||||
|
||||
static int timing_scan_write_read_memc(const struct device *dev,
|
||||
uint32_t device_addr)
|
||||
{
|
||||
uint32_t num_bytes_left = CONFIG_MSPI_AMBIQ_TIMING_SCAN_DATA_SIZE;
|
||||
uint32_t test_bytes = 0;
|
||||
|
||||
while (num_bytes_left) {
|
||||
if (num_bytes_left > CONFIG_MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE) {
|
||||
test_bytes = CONFIG_MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE;
|
||||
num_bytes_left -= CONFIG_MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE;
|
||||
} else {
|
||||
test_bytes = num_bytes_left;
|
||||
num_bytes_left = 0;
|
||||
}
|
||||
|
||||
LOG_DBG("Write read at %08x, size %08x\n", device_addr, test_bytes);
|
||||
memcpy((void *)device_addr, txdata_buff, test_bytes);
|
||||
sys_cache_data_flush_and_invd_all();
|
||||
memcpy(rxdata_buff, (void *)device_addr, test_bytes);
|
||||
if (memcmp(txdata_buff, rxdata_buff, test_bytes)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_param(struct mspi_ambiq_timing_scan *scan, uint32_t param_mask)
|
||||
{
|
||||
struct mspi_ambiq_timing_scan_range *range = &scan->range;
|
||||
|
||||
if (scan->min_window > range->rxdqs_end - range->rxdqs_start) {
|
||||
LOG_ERR("invalid min_window or txdqs, rxdqs scan range.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(param_mask & MSPI_AMBIQ_SET_RLC) &&
|
||||
(range->rlc_start != 0) && (range->rlc_end != 0)) {
|
||||
LOG_ERR("invalid RLC range.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(param_mask & MSPI_AMBIQ_SET_TXNEG) &&
|
||||
(range->txneg_start != 0) && (range->txneg_end != 0)) {
|
||||
LOG_ERR("invalid TXNEG range.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(param_mask & MSPI_AMBIQ_SET_RXNEG) &&
|
||||
(range->rxneg_start != 0) && (range->rxneg_end != 0)) {
|
||||
LOG_ERR("invalid RXNEG range.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(param_mask & MSPI_AMBIQ_SET_RXCAP) &&
|
||||
(range->rxcap_start != 0) && (range->rxcap_end != 0)) {
|
||||
LOG_ERR("invalid RXCAP range.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(param_mask & MSPI_AMBIQ_SET_TXDQSDLY) &&
|
||||
(range->txdqs_start != 0) && (range->txdqs_end != 0)) {
|
||||
LOG_ERR("invalid TXDQSDLY range.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(param_mask & MSPI_AMBIQ_SET_RXDQSDLY) &&
|
||||
(range->rxdqs_start != 0) && (range->rxdqs_end != 0)) {
|
||||
LOG_ERR("invalid RXDQSDLY range.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int timing_scan(const struct device *dev,
|
||||
const struct device *bus,
|
||||
const struct mspi_dev_id *dev_id,
|
||||
uint32_t param_mask,
|
||||
struct mspi_ambiq_timing_scan *scan,
|
||||
struct mspi_ambiq_timing_cfg *param,
|
||||
uint32_t *max_window)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t txdqsdelay = 0;
|
||||
uint32_t rxdqsdelay = 0;
|
||||
uint32_t tx_result = 0;
|
||||
uint32_t address = scan->device_addr;
|
||||
struct mspi_ambiq_timing_scan_range *range = &scan->range;
|
||||
struct longest_ones longest;
|
||||
uint32_t rx_res[32];
|
||||
|
||||
memset(rx_res, 0, sizeof(rx_res));
|
||||
|
||||
if (scan->scan_type == MSPI_AMBIQ_TIMING_SCAN_FLASH) {
|
||||
ret = flash_write_data(dev, address);
|
||||
if (ret) {
|
||||
LOG_ERR("Flash write failed, code:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* LOOP_TXDQSDELAY */
|
||||
for (param->ui32TxDQSDelay = (param_mask & MSPI_AMBIQ_SET_TXDQSDLY) ?
|
||||
range->txdqs_start : 0;
|
||||
param->ui32TxDQSDelay <= ((param_mask & MSPI_AMBIQ_SET_TXDQSDLY) ?
|
||||
range->txdqs_end : 0);
|
||||
param->ui32TxDQSDelay++) {
|
||||
|
||||
/* LOOP_RXDQSDELAY */
|
||||
for (param->ui32RxDQSDelay = (param_mask & MSPI_AMBIQ_SET_RXDQSDLY) ?
|
||||
range->rxdqs_start : 0;
|
||||
param->ui32RxDQSDelay <= ((param_mask & MSPI_AMBIQ_SET_RXDQSDLY) ?
|
||||
range->rxdqs_end : 0);
|
||||
param->ui32RxDQSDelay++) {
|
||||
if (scan->scan_type == MSPI_AMBIQ_TIMING_SCAN_MEMC) {
|
||||
address = scan->device_addr;
|
||||
address += (param->bTxNeg + param->bRxNeg + param->bRxCap
|
||||
+ param->ui8TurnAround) *
|
||||
CONFIG_MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE +
|
||||
(param->ui32TxDQSDelay + param->ui32RxDQSDelay) * 2;
|
||||
|
||||
ret = mspi_dev_config(bus, dev_id, MSPI_DEVICE_CONFIG_NONE, NULL);
|
||||
if (ret) {
|
||||
LOG_ERR("failed to acquire controller, code:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = mspi_timing_config(bus, dev_id, param_mask, param);
|
||||
if (ret) {
|
||||
LOG_ERR("failed to configure mspi timing!!\n");
|
||||
return ret;
|
||||
}
|
||||
/* run data check */
|
||||
ret = timing_scan_write_read_memc(dev, address);
|
||||
} else if (scan->scan_type == MSPI_AMBIQ_TIMING_SCAN_FLASH) {
|
||||
|
||||
ret = mspi_dev_config(bus, dev_id, MSPI_DEVICE_CONFIG_NONE, NULL);
|
||||
if (ret) {
|
||||
LOG_ERR("failed to acquire controller, code:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = mspi_timing_config(bus, dev_id, param_mask, param);
|
||||
if (ret) {
|
||||
LOG_ERR("failed to configure mspi timing!!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* run data check */
|
||||
ret = flash_read_scan(dev, address);
|
||||
}
|
||||
if (ret == 0) {
|
||||
/* data check pass */
|
||||
rx_res[param->ui32TxDQSDelay] |= 0x01 << param->ui32RxDQSDelay;
|
||||
} else if (ret != 1) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (range->rxdqs_start != range->rxdqs_end &&
|
||||
(param_mask & MSPI_AMBIQ_SET_RXDQSDLY)) {
|
||||
find_longest_ones((const unsigned char *)&rx_res[param->ui32TxDQSDelay],
|
||||
32, &longest);
|
||||
if (longest.start != -1 && longest.length >= scan->min_window) {
|
||||
tx_result |= 0x01 << param->ui32TxDQSDelay;
|
||||
}
|
||||
LOG_INF(" TxDQSDelay: %d, RxDQSDelay Scan = 0x%08X, Window size = %d\n",
|
||||
param->ui32TxDQSDelay, rx_res[param->ui32TxDQSDelay],
|
||||
longest.length);
|
||||
} else {
|
||||
if (rx_res[param->ui32TxDQSDelay] != 0) {
|
||||
tx_result |= 0x01 << param->ui32TxDQSDelay;
|
||||
}
|
||||
LOG_INF(" TxDQSDelay: %d, RxDQSDelay Scan = 0x%08X\n",
|
||||
param->ui32TxDQSDelay, rx_res[param->ui32TxDQSDelay]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Find TXDQSDELAY Value */
|
||||
if (range->txdqs_start != range->txdqs_end &&
|
||||
(param_mask & MSPI_AMBIQ_SET_TXDQSDLY)) {
|
||||
txdqsdelay = find_mid_point((const unsigned char *)&tx_result, 32);
|
||||
} else {
|
||||
txdqsdelay = param->ui32TxDQSDelay;
|
||||
}
|
||||
|
||||
/* Find RXDQSDELAY Value */
|
||||
if (range->rxdqs_start != range->rxdqs_end &&
|
||||
(param_mask & MSPI_AMBIQ_SET_RXDQSDLY)) {
|
||||
rxdqsdelay = find_mid_point((const unsigned char *)&rx_res[txdqsdelay], 32);
|
||||
} else {
|
||||
rxdqsdelay = param->ui32RxDQSDelay;
|
||||
}
|
||||
|
||||
find_longest_ones((const unsigned char *)&tx_result, 32, &longest);
|
||||
if (*max_window < longest.length ||
|
||||
(range->txdqs_start == range->txdqs_end &&
|
||||
range->rxdqs_start == range->rxdqs_end) ||
|
||||
((param_mask & (MSPI_AMBIQ_SET_TXDQSDLY | MSPI_AMBIQ_SET_RXDQSDLY)) == 0)) {
|
||||
*max_window = longest.length;
|
||||
scan->result = *param;
|
||||
scan->result.ui32TxDQSDelay = txdqsdelay;
|
||||
scan->result.ui32RxDQSDelay = rxdqsdelay;
|
||||
LOG_INF("Selected setting: TxNeg=%d, RxNeg=%d, RxCap=%d, Turnaround=%d,"
|
||||
"TxDQSDelay=%d, RxDQSDelay=%d\n", param->bTxNeg, param->bRxNeg,
|
||||
param->bRxCap, param->ui8TurnAround,
|
||||
txdqsdelay, rxdqsdelay);
|
||||
} else {
|
||||
LOG_INF("Candidate setting: TxNeg=%d, RxNeg=%d, RxCap=%d, Turnaround=%d,"
|
||||
"TxDQSDelay=%d, RxDQSDelay=%d\n", param->bTxNeg, param->bRxNeg,
|
||||
param->bRxCap, param->ui8TurnAround,
|
||||
txdqsdelay, rxdqsdelay);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mspi_ambiq_timing_scan(const struct device *dev,
|
||||
const struct device *bus,
|
||||
const struct mspi_dev_id *dev_id,
|
||||
uint32_t param_mask,
|
||||
struct mspi_ambiq_timing_cfg *timing,
|
||||
struct mspi_ambiq_timing_scan *scan)
|
||||
{
|
||||
int ret;
|
||||
int txneg;
|
||||
int rxneg;
|
||||
int rxcap;
|
||||
int max_window = 0;
|
||||
struct mspi_ambiq_timing_cfg param;
|
||||
struct mspi_ambiq_timing_scan_range *range = &scan->range;
|
||||
|
||||
if (check_param(scan, param_mask)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Generate data into the buffer */
|
||||
prepare_test_pattern(txdata_buff, CONFIG_MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE);
|
||||
if (CONFIG_MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE > 64*1024) {
|
||||
sys_cache_data_flush_all();
|
||||
} else {
|
||||
sys_cache_data_flush_range(txdata_buff, CONFIG_MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
memset(¶m, 0, sizeof(struct mspi_ambiq_timing_cfg));
|
||||
|
||||
/* LOOP_TXNEG */
|
||||
for (txneg = (param_mask & MSPI_AMBIQ_SET_TXNEG) ? range->txneg_start : 0;
|
||||
txneg <= ((param_mask & MSPI_AMBIQ_SET_TXNEG) ? range->txneg_end : 0); txneg++) {
|
||||
param.bTxNeg = (bool)txneg;
|
||||
|
||||
/* LOOP_RXNEG */
|
||||
for (rxneg = (param_mask & MSPI_AMBIQ_SET_RXNEG) ? range->rxneg_start : 0;
|
||||
rxneg <= ((param_mask & MSPI_AMBIQ_SET_RXNEG) ? range->rxneg_end : 0);
|
||||
rxneg++) {
|
||||
param.bRxNeg = (bool)rxneg;
|
||||
|
||||
/* LOOP_RXCAP */
|
||||
for (rxcap = (param_mask & MSPI_AMBIQ_SET_RXCAP) ? range->rxcap_start : 0;
|
||||
rxcap <= ((param_mask & MSPI_AMBIQ_SET_RXCAP) ? range->rxcap_end : 0);
|
||||
rxcap++) {
|
||||
param.bRxCap = (bool)rxcap;
|
||||
/* LOOP_TURNAROUND */
|
||||
for (param.ui8TurnAround = (param_mask & MSPI_AMBIQ_SET_RLC) ?
|
||||
range->rlc_start + timing->ui8TurnAround : 0;
|
||||
param.ui8TurnAround <= ((param_mask & MSPI_AMBIQ_SET_RLC) ?
|
||||
range->rlc_end + timing->ui8TurnAround : 0);
|
||||
param.ui8TurnAround++) {
|
||||
param.ui8WriteLatency = timing->ui8WriteLatency;
|
||||
LOG_INF("TxNeg=%d, RxNeg=%d, RxCap=%d, Turnaround=%d\n",
|
||||
param.bTxNeg, param.bRxNeg, param.bRxCap,
|
||||
param.ui8TurnAround);
|
||||
ret = timing_scan(dev, bus, dev_id, param_mask,
|
||||
scan, ¶m, &max_window);
|
||||
if (ret) {
|
||||
LOG_ERR("Timing scan failed, code:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (((range->txdqs_start == range->txdqs_end &&
|
||||
range->rxdqs_start == range->rxdqs_end) ||
|
||||
((param_mask & (MSPI_AMBIQ_SET_TXDQSDLY |
|
||||
MSPI_AMBIQ_SET_RXDQSDLY)) == 0)) &&
|
||||
max_window != 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -48,7 +48,7 @@ properties:
|
||||
|
||||
ambiq,timing-config:
|
||||
type: array
|
||||
default: [0, 0, 0, 0, 0, 0, 0, 0]
|
||||
default: [0, 0, 0, 0, 0, 0, 0]
|
||||
description: |
|
||||
Array of tuples to configure the timing parameters
|
||||
default =
|
||||
@ -60,5 +60,4 @@ properties:
|
||||
.bRxCap = false,
|
||||
.ui32TxDQSDelay = 0,
|
||||
.ui32RxDQSDelay = 0,
|
||||
.ui32RXDQSDelayEXT = 0,
|
||||
>
|
||||
|
||||
13
samples/drivers/mspi/mspi_timing_scan/CMakeLists.txt
Normal file
13
samples/drivers/mspi/mspi_timing_scan/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(mspi_timing_scan)
|
||||
|
||||
FILE(GLOB app_sources src/*.c)
|
||||
target_sources(app PRIVATE ${app_sources})
|
||||
|
||||
if(CONFIG_MSPI)
|
||||
target_include_directories(app PRIVATE ${ZEPHYR_BASE}/include/zephyr/drivers)
|
||||
target_include_directories(app PRIVATE ${ZEPHYR_BASE}/drivers/mspi)
|
||||
endif()
|
||||
46
samples/drivers/mspi/mspi_timing_scan/README.rst
Normal file
46
samples/drivers/mspi/mspi_timing_scan/README.rst
Normal file
@ -0,0 +1,46 @@
|
||||
.. zephyr:code-sample:: mspi-timing-scan
|
||||
:name: Ambiq MSPI timing scan
|
||||
:relevant-api: flash_interface
|
||||
|
||||
Find the appropriate timing for a given device on a given board.
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
This sample demonstrates the usage of ambiq timing scan utility.
|
||||
|
||||
Building and Running
|
||||
********************
|
||||
|
||||
The application will build only for a target that has a :ref:`devicetree <dt-guide>`
|
||||
``flash0`` or ``psram0`` alias depending on the interface used.
|
||||
They refers to an entry with the following bindings as a compatible:
|
||||
|
||||
* :dtcompatible:`ambiq,mspi-device`
|
||||
|
||||
.. zephyr-app-commands::
|
||||
:zephyr-app: samples/drivers/mspi/mspi_timing_scan
|
||||
:board: apollo5_eb
|
||||
:goals: build flash
|
||||
:compact:
|
||||
|
||||
Sample Output
|
||||
=============
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
*** Booting Zephyr OS build zephyr-v3.4.0-27775-g750ed00d564b ***
|
||||
<inf> mspi_ambiq_timing_scan: TxNeg=0, RxNeg=0, RxCap=0, Turnaround=5
|
||||
<inf> mspi_ambiq_timing_scan: TxDQSDelay: 0, RxDQSDelay Scan = 0x0007FFFE, Window size = 18
|
||||
<inf> mspi_ambiq_timing_scan: TxDQSDelay: 1, RxDQSDelay Scan = 0x0007FFFF, Window size = 19
|
||||
<inf> mspi_ambiq_timing_scan: TxDQSDelay: 1, RxDQSDelay Scan = 0x0007FFFF, Window size = 19
|
||||
<inf> mspi_ambiq_timing_scan: TxDQSDelay: 2, RxDQSDelay Scan = 0x0007FFFE, Window size = 18
|
||||
<inf> mspi_ambiq_timing_scan: TxDQSDelay: 3, RxDQSDelay Scan = 0x0007FFFF, Window size = 19
|
||||
<inf> mspi_ambiq_timing_scan: TxDQSDelay: 4, RxDQSDelay Scan = 0x0007FFFE, Window size = 18
|
||||
<inf> mspi_ambiq_timing_scan: TxDQSDelay: 5, RxDQSDelay Scan = 0x0005FD54, Window size = 7
|
||||
<inf> mspi_ambiq_timing_scan: TxDQSDelay: 6, RxDQSDelay Scan = 0x00000000, Window size = 0
|
||||
<inf> mspi_ambiq_timing_scan: TxDQSDelay: 7, RxDQSDelay Scan = 0x00000000, Window size = 0
|
||||
<inf> mspi_ambiq_timing_scan: TxDQSDelay: 8, RxDQSDelay Scan = 0x00000000, Window size = 0
|
||||
<inf> mspi_ambiq_timing_scan: TxDQSDelay: 9, RxDQSDelay Scan = 0x00000000, Window size = 0
|
||||
<inf> mspi_ambiq_timing_scan: TxDQSDelay: 10, RxDQSDelay Scan = 0x00000000, Window size = 0
|
||||
<inf> mspi_ambiq_timing_scan: Selected setting: TxNeg=0, RxNeg=0, RxCap=0, Turnaround=5,TxDQSDelay=2, RxDQSDelay=9
|
||||
@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2025 Ambiq Micro Inc. <www.ambiq.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
CONFIG_MSPI_AMBIQ_TIMING_SCAN=y
|
||||
CONFIG_LOG=y
|
||||
CONFIG_LOG_MODE_IMMEDIATE=y
|
||||
CONFIG_LOG_DEFAULT_LEVEL=3
|
||||
CONFIG_MSPI_LOG_LEVEL_INF=y
|
||||
CONFIG_MSPI_INIT_PRIORITY=40
|
||||
CONFIG_MAIN_STACK_SIZE=8192
|
||||
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Ambiq Micro Inc. <www.ambiq.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/ {
|
||||
aliases {
|
||||
flash0 = &is25wx064;
|
||||
psram0 = &aps51216ba;
|
||||
};
|
||||
};
|
||||
|
||||
&mspi0 {
|
||||
/delete-property/ zephyr,pm-device-runtime-auto;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&aps51216ba {
|
||||
/delete-property/ zephyr,pm-device-runtime-auto;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&mspi1 {
|
||||
/delete-property/ zephyr,pm-device-runtime-auto;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&is25wx064 {
|
||||
status = "okay";
|
||||
};
|
||||
2
samples/drivers/mspi/mspi_timing_scan/prj.conf
Normal file
2
samples/drivers/mspi/mspi_timing_scan/prj.conf
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_STDOUT_CONSOLE=y
|
||||
CONFIG_MSPI=y
|
||||
47
samples/drivers/mspi/mspi_timing_scan/sample.yaml
Normal file
47
samples/drivers/mspi/mspi_timing_scan/sample.yaml
Normal file
@ -0,0 +1,47 @@
|
||||
sample:
|
||||
name: MSPI Flash Sample
|
||||
tests:
|
||||
sample.drivers.mspi.timing_scan.flash:
|
||||
tags:
|
||||
- mspi
|
||||
filter: dt_compat_enabled("ambiq,mspi-device")
|
||||
harness: console
|
||||
harness_config:
|
||||
type: multi_line
|
||||
ordered: true
|
||||
regex:
|
||||
- "<inf> mspi_ambiq_timing_scan: TxNeg=0, RxNeg=0, RxCap=0, Turnaround=[0-9]*"
|
||||
- "<inf> mspi_ambiq_timing_scan: TxDQSDelay: [0-9]*, RxDQSDelay Scan = 0x[0-9]*,(.*)"
|
||||
- "<inf> mspi_ambiq_timing_scan: Selected setting: TxNeg=0, RxNeg=0, RxCap=0, (.*)"
|
||||
platform_allow:
|
||||
- apollo510_evb
|
||||
integration_platforms:
|
||||
- apollo510_evb
|
||||
depends_on: mspi
|
||||
extra_configs:
|
||||
- CONFIG_FLASH=y
|
||||
- CONFIG_FLASH_INIT_PRIORITY=50
|
||||
- CONFIG_FLASH_MSPI_XIP_READ=y
|
||||
- CONFIG_MSPI_AMBIQ_TIMING_SCAN_BUFFER_SIZE=8192
|
||||
- CONFIG_MSPI_AMBIQ_TIMING_SCAN_DATA_SIZE=131072
|
||||
|
||||
sample.drivers.mspi.timing_scan.memc:
|
||||
tags:
|
||||
- mspi
|
||||
filter: dt_compat_enabled("ambiq,mspi-device")
|
||||
harness: console
|
||||
harness_config:
|
||||
type: multi_line
|
||||
ordered: true
|
||||
regex:
|
||||
- "<inf> mspi_ambiq_timing_scan: TxNeg=0, RxNeg=0, RxCap=0, Turnaround=[0-9]*"
|
||||
- "<inf> mspi_ambiq_timing_scan: TxDQSDelay: [0-9]*, RxDQSDelay Scan = 0x[0-9]*,(.*)"
|
||||
- "<inf> mspi_ambiq_timing_scan: Selected setting: TxNeg=0, RxNeg=0, RxCap=0, (.*)"
|
||||
platform_allow:
|
||||
- apollo510_evb
|
||||
integration_platforms:
|
||||
- apollo510_evb
|
||||
depends_on: mspi
|
||||
extra_configs:
|
||||
- CONFIG_MEMC=y
|
||||
- CONFIG_MEMC_INIT_PRIORITY=50
|
||||
85
samples/drivers/mspi/mspi_timing_scan/src/main.c
Normal file
85
samples/drivers/mspi/mspi_timing_scan/src/main.c
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Ambiq Micro Inc. <www.ambiq.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/flash.h>
|
||||
#include <zephyr/drivers/mspi.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mspi_ambiq.h"
|
||||
|
||||
#if CONFIG_FLASH_MSPI
|
||||
#define TARGET_DEVICE DT_ALIAS(flash0)
|
||||
#elif CONFIG_MEMC_MSPI
|
||||
#define TARGET_DEVICE DT_ALIAS(psram0)
|
||||
#else
|
||||
#error "Unsupported device type!!"
|
||||
#endif
|
||||
|
||||
#define MSPI_AMBIQ_TIMING_CONFIG_DT(n) \
|
||||
{ \
|
||||
.ui8WriteLatency = DT_PROP_BY_IDX(n, ambiq_timing_config, 0), \
|
||||
.ui8TurnAround = DT_PROP_BY_IDX(n, ambiq_timing_config, 1), \
|
||||
.bTxNeg = DT_PROP_BY_IDX(n, ambiq_timing_config, 2), \
|
||||
.bRxNeg = DT_PROP_BY_IDX(n, ambiq_timing_config, 3), \
|
||||
.bRxCap = DT_PROP_BY_IDX(n, ambiq_timing_config, 4), \
|
||||
.ui32TxDQSDelay = DT_PROP_BY_IDX(n, ambiq_timing_config, 5), \
|
||||
.ui32RxDQSDelay = DT_PROP_BY_IDX(n, ambiq_timing_config, 6), \
|
||||
}
|
||||
|
||||
#define MSPI_AMBIQ_TIMING_CONFIG_MASK_DT(n) DT_PROP(n, ambiq_timing_config_mask)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const struct device *tar_bus = DEVICE_DT_GET(DT_BUS(TARGET_DEVICE));
|
||||
const struct device *tar_dev = DEVICE_DT_GET(TARGET_DEVICE);
|
||||
struct mspi_dev_id dev_id = MSPI_DEVICE_ID_DT(TARGET_DEVICE);
|
||||
#if CONFIG_MEMC_MSPI
|
||||
struct mspi_xip_cfg tar_xip_cfg = MSPI_XIP_CONFIG_DT(TARGET_DEVICE);
|
||||
#endif
|
||||
struct mspi_ambiq_timing_cfg tar_timing_cfg = MSPI_AMBIQ_TIMING_CONFIG_DT(TARGET_DEVICE);
|
||||
uint32_t timing_cfg_mask = MSPI_AMBIQ_TIMING_CONFIG_MASK_DT(TARGET_DEVICE);
|
||||
struct mspi_ambiq_timing_scan scan;
|
||||
|
||||
if (!device_is_ready(tar_dev)) {
|
||||
printk("%s: device not ready.\n", tar_dev->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if CONFIG_MEMC_MSPI
|
||||
if (!tar_xip_cfg.enable) {
|
||||
printk("Need to enable XIP for timing scan.\n");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
memset(&scan, 0, sizeof(struct mspi_ambiq_timing_scan));
|
||||
scan.range.txdqs_end = 10;
|
||||
scan.range.rxdqs_end = 31;
|
||||
#if CONFIG_FLASH_MSPI
|
||||
scan.scan_type = MSPI_AMBIQ_TIMING_SCAN_FLASH;
|
||||
#elif CONFIG_MEMC_MSPI
|
||||
uint32_t base_addr = DT_REG_ADDR_BY_IDX(DT_BUS(TARGET_DEVICE), 1);
|
||||
|
||||
scan.scan_type = MSPI_AMBIQ_TIMING_SCAN_MEMC;
|
||||
scan.device_addr = base_addr +
|
||||
tar_xip_cfg.address_offset +
|
||||
tar_xip_cfg.size / 2;
|
||||
#endif
|
||||
scan.min_window = 6;
|
||||
|
||||
if (mspi_ambiq_timing_scan(tar_dev, tar_bus, &dev_id, timing_cfg_mask,
|
||||
(void *)&tar_timing_cfg, &scan)) {
|
||||
printk("Failed to timing scan\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("==========================\n");
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user