1444 lines
44 KiB
C
1444 lines
44 KiB
C
/*
|
|
* Copyright (c) 2024, Ambiq Micro Inc. <www.ambiq.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT ambiq_mspi_controller
|
|
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/logging/log_instance.h>
|
|
LOG_MODULE_REGISTER(mspi_ambiq_ap3);
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/sys/util.h>
|
|
#include <zephyr/pm/device.h>
|
|
#include <zephyr/drivers/pinctrl.h>
|
|
#include <zephyr/drivers/mspi.h>
|
|
#include <zephyr/drivers/gpio.h>
|
|
#include <zephyr/sys_clock.h>
|
|
#include <zephyr/irq.h>
|
|
|
|
#include "mspi_ambiq.h"
|
|
|
|
#define MSPI_MAX_FREQ 48000000
|
|
#define MSPI_MAX_DEVICE 2
|
|
#define MSPI_TIMEOUT_US 1000000
|
|
#define PWRCTRL_MAX_WAIT_US 5
|
|
#define MSPI_BUSY BIT(2)
|
|
|
|
typedef int (*mspi_ambiq_pwr_func_t)(void);
|
|
typedef void (*irq_config_func_t)(void);
|
|
|
|
struct mspi_context {
|
|
const struct mspi_dev_id *owner;
|
|
|
|
struct mspi_xfer xfer;
|
|
|
|
int packets_left;
|
|
int packets_done;
|
|
|
|
mspi_callback_handler_t callback;
|
|
struct mspi_callback_context *callback_ctx;
|
|
bool asynchronous;
|
|
|
|
struct k_sem lock;
|
|
};
|
|
|
|
struct mspi_ambiq_config {
|
|
uint32_t reg_base;
|
|
uint32_t reg_size;
|
|
|
|
struct mspi_cfg mspicfg;
|
|
|
|
const struct pinctrl_dev_config *pcfg;
|
|
irq_config_func_t irq_cfg_func;
|
|
|
|
LOG_INSTANCE_PTR_DECLARE(log);
|
|
};
|
|
|
|
struct mspi_ambiq_data {
|
|
void *mspiHandle;
|
|
am_hal_mspi_dev_config_t hal_dev_cfg;
|
|
|
|
struct mspi_dev_id *dev_id;
|
|
struct k_mutex lock;
|
|
|
|
struct mspi_dev_cfg dev_cfg;
|
|
struct mspi_xip_cfg xip_cfg;
|
|
struct mspi_scramble_cfg scramble_cfg;
|
|
|
|
mspi_callback_handler_t cbs[MSPI_BUS_EVENT_MAX];
|
|
struct mspi_callback_context *cb_ctxs[MSPI_BUS_EVENT_MAX];
|
|
|
|
struct mspi_context ctx;
|
|
};
|
|
|
|
static int mspi_set_freq(const struct mspi_ambiq_config *cfg, uint32_t freq)
|
|
{
|
|
uint32_t d = MSPI_MAX_FREQ / freq;
|
|
|
|
switch (d) {
|
|
case AM_HAL_MSPI_CLK_48MHZ:
|
|
case AM_HAL_MSPI_CLK_24MHZ:
|
|
case AM_HAL_MSPI_CLK_16MHZ:
|
|
case AM_HAL_MSPI_CLK_12MHZ:
|
|
case AM_HAL_MSPI_CLK_8MHZ:
|
|
case AM_HAL_MSPI_CLK_6MHZ:
|
|
case AM_HAL_MSPI_CLK_4MHZ:
|
|
case AM_HAL_MSPI_CLK_3MHZ:
|
|
break;
|
|
default:
|
|
LOG_INST_ERR(cfg->log, "%u,Frequency not supported!", __LINE__);
|
|
d = 0;
|
|
break;
|
|
}
|
|
|
|
return d;
|
|
}
|
|
|
|
static am_hal_mspi_device_e mspi_set_line(const struct mspi_ambiq_config *cfg,
|
|
enum mspi_io_mode io_mode,
|
|
enum mspi_data_rate data_rate,
|
|
uint8_t ce_num)
|
|
{
|
|
if (data_rate != MSPI_DATA_RATE_SINGLE) {
|
|
LOG_INST_ERR(cfg->log, "%u, incorrect data rate, only SDR is supported.", __LINE__);
|
|
return AM_HAL_MSPI_FLASH_MAX;
|
|
}
|
|
|
|
if (ce_num == 0) {
|
|
switch (io_mode) {
|
|
case MSPI_IO_MODE_SINGLE:
|
|
return AM_HAL_MSPI_FLASH_SERIAL_CE0;
|
|
case MSPI_IO_MODE_DUAL:
|
|
return AM_HAL_MSPI_FLASH_DUAL_CE0;
|
|
case MSPI_IO_MODE_DUAL_1_1_2:
|
|
return AM_HAL_MSPI_FLASH_DUAL_CE0_1_1_2;
|
|
case MSPI_IO_MODE_DUAL_1_2_2:
|
|
return AM_HAL_MSPI_FLASH_DUAL_CE0_1_2_2;
|
|
case MSPI_IO_MODE_QUAD:
|
|
return AM_HAL_MSPI_FLASH_QUAD_CE0;
|
|
case MSPI_IO_MODE_QUAD_1_1_4:
|
|
return AM_HAL_MSPI_FLASH_QUAD_CE0_1_1_4;
|
|
case MSPI_IO_MODE_QUAD_1_4_4:
|
|
return AM_HAL_MSPI_FLASH_QUAD_CE0_1_4_4;
|
|
case MSPI_IO_MODE_OCTAL:
|
|
return AM_HAL_MSPI_FLASH_OCTAL_CE0;
|
|
default:
|
|
return AM_HAL_MSPI_FLASH_MAX;
|
|
}
|
|
} else if (ce_num == 1) {
|
|
switch (io_mode) {
|
|
case MSPI_IO_MODE_SINGLE:
|
|
return AM_HAL_MSPI_FLASH_SERIAL_CE1;
|
|
case MSPI_IO_MODE_DUAL:
|
|
return AM_HAL_MSPI_FLASH_DUAL_CE1;
|
|
case MSPI_IO_MODE_DUAL_1_1_2:
|
|
return AM_HAL_MSPI_FLASH_DUAL_CE1_1_1_2;
|
|
case MSPI_IO_MODE_DUAL_1_2_2:
|
|
return AM_HAL_MSPI_FLASH_DUAL_CE1_1_2_2;
|
|
case MSPI_IO_MODE_QUAD:
|
|
return AM_HAL_MSPI_FLASH_QUAD_CE1;
|
|
case MSPI_IO_MODE_QUAD_1_1_4:
|
|
return AM_HAL_MSPI_FLASH_QUAD_CE1_1_1_4;
|
|
case MSPI_IO_MODE_QUAD_1_4_4:
|
|
return AM_HAL_MSPI_FLASH_QUAD_CE1_1_4_4;
|
|
case MSPI_IO_MODE_OCTAL:
|
|
return AM_HAL_MSPI_FLASH_OCTAL_CE1;
|
|
default:
|
|
return AM_HAL_MSPI_FLASH_MAX;
|
|
}
|
|
} else {
|
|
return AM_HAL_MSPI_FLASH_MAX;
|
|
}
|
|
}
|
|
|
|
static am_hal_mspi_dma_boundary_e mspi_set_mem_boundary(uint32_t mem_boundary)
|
|
{
|
|
switch (mem_boundary) {
|
|
case 0:
|
|
return AM_HAL_MSPI_BOUNDARY_NONE;
|
|
case 32:
|
|
return AM_HAL_MSPI_BOUNDARY_BREAK32;
|
|
case 64:
|
|
return AM_HAL_MSPI_BOUNDARY_BREAK64;
|
|
case 128:
|
|
return AM_HAL_MSPI_BOUNDARY_BREAK128;
|
|
case 256:
|
|
return AM_HAL_MSPI_BOUNDARY_BREAK256;
|
|
case 512:
|
|
return AM_HAL_MSPI_BOUNDARY_BREAK512;
|
|
case 1024:
|
|
return AM_HAL_MSPI_BOUNDARY_BREAK1K;
|
|
case 2048:
|
|
return AM_HAL_MSPI_BOUNDARY_BREAK2K;
|
|
case 4096:
|
|
return AM_HAL_MSPI_BOUNDARY_BREAK4K;
|
|
case 8192:
|
|
return AM_HAL_MSPI_BOUNDARY_BREAK8K;
|
|
case 16384:
|
|
return AM_HAL_MSPI_BOUNDARY_BREAK16K;
|
|
default:
|
|
return AM_HAL_MSPI_BOUNDARY_MAX;
|
|
}
|
|
}
|
|
|
|
static inline void mspi_context_ce_control(struct mspi_context *ctx, bool on)
|
|
{
|
|
if (ctx->owner) {
|
|
if (ctx->xfer.hold_ce &&
|
|
ctx->xfer.ce_sw_ctrl.gpio.port != NULL) {
|
|
if (on) {
|
|
gpio_pin_set_dt(&ctx->xfer.ce_sw_ctrl.gpio, 1);
|
|
k_busy_wait(ctx->xfer.ce_sw_ctrl.delay);
|
|
} else {
|
|
k_busy_wait(ctx->xfer.ce_sw_ctrl.delay);
|
|
gpio_pin_set_dt(&ctx->xfer.ce_sw_ctrl.gpio, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void mspi_context_release(struct mspi_context *ctx)
|
|
{
|
|
ctx->owner = NULL;
|
|
k_sem_give(&ctx->lock);
|
|
}
|
|
|
|
static inline void mspi_context_unlock_unconditionally(struct mspi_context *ctx)
|
|
{
|
|
mspi_context_ce_control(ctx, false);
|
|
|
|
if (!k_sem_count_get(&ctx->lock)) {
|
|
ctx->owner = NULL;
|
|
k_sem_give(&ctx->lock);
|
|
}
|
|
}
|
|
|
|
static inline int mspi_context_lock(struct mspi_context *ctx,
|
|
const struct mspi_dev_id *req,
|
|
const struct mspi_xfer *xfer,
|
|
mspi_callback_handler_t callback,
|
|
struct mspi_callback_context *callback_ctx,
|
|
bool lockon)
|
|
{
|
|
int ret = 1;
|
|
|
|
if ((k_sem_count_get(&ctx->lock) == 0) && !lockon &&
|
|
(ctx->owner == req)) {
|
|
return 0;
|
|
}
|
|
|
|
if (k_sem_take(&ctx->lock, K_MSEC(xfer->timeout))) {
|
|
return -EBUSY;
|
|
}
|
|
if (ctx->xfer.async) {
|
|
if ((xfer->tx_dummy == ctx->xfer.tx_dummy) &&
|
|
(xfer->rx_dummy == ctx->xfer.rx_dummy) &&
|
|
(xfer->cmd_length == ctx->xfer.cmd_length) &&
|
|
(xfer->addr_length == ctx->xfer.addr_length)) {
|
|
ret = 0;
|
|
} else if (ctx->packets_left == 0) {
|
|
if (ctx->callback_ctx) {
|
|
volatile struct mspi_event_data *evt_data;
|
|
|
|
evt_data = &ctx->callback_ctx->mspi_evt.evt_data;
|
|
while (evt_data->status != 0) {
|
|
}
|
|
ret = 1;
|
|
} else {
|
|
ret = 0;
|
|
}
|
|
} else {
|
|
return -EIO;
|
|
}
|
|
}
|
|
ctx->owner = req;
|
|
ctx->xfer = *xfer;
|
|
ctx->packets_done = 0;
|
|
ctx->packets_left = ctx->xfer.num_packet;
|
|
ctx->callback = callback;
|
|
ctx->callback_ctx = callback_ctx;
|
|
return ret;
|
|
}
|
|
|
|
static inline bool mspi_is_inp(const struct device *controller)
|
|
{
|
|
struct mspi_ambiq_data *data = controller->data;
|
|
|
|
return (k_sem_count_get(&data->ctx.lock) == 0);
|
|
}
|
|
|
|
static inline int mspi_verify_device(const struct device *controller,
|
|
const struct mspi_dev_id *dev_id)
|
|
{
|
|
const struct mspi_ambiq_config *cfg = controller->config;
|
|
int device_index = cfg->mspicfg.num_periph;
|
|
int ret = 0;
|
|
|
|
for (int i = 0; i < cfg->mspicfg.num_periph; i++) {
|
|
if (dev_id->ce.port == cfg->mspicfg.ce_group[i].port &&
|
|
dev_id->ce.pin == cfg->mspicfg.ce_group[i].pin &&
|
|
dev_id->ce.dt_flags == cfg->mspicfg.ce_group[i].dt_flags) {
|
|
device_index = i;
|
|
}
|
|
}
|
|
|
|
if (device_index >= cfg->mspicfg.num_periph ||
|
|
device_index != dev_id->dev_idx) {
|
|
LOG_INST_ERR(cfg->log, "%u, invalid device ID.", __LINE__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mspi_ambiq_deinit(const struct device *controller)
|
|
{
|
|
const struct mspi_ambiq_config *cfg = controller->config;
|
|
struct mspi_ambiq_data *data = controller->data;
|
|
int ret = 0;
|
|
|
|
if (!data->mspiHandle) {
|
|
LOG_INST_ERR(cfg->log, "%u, the mspi not yet initialized.", __LINE__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (k_mutex_lock(&data->lock, K_MSEC(CONFIG_MSPI_COMPLETION_TIMEOUT_TOLERANCE))) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to gain controller access.", __LINE__);
|
|
return -EBUSY;
|
|
}
|
|
|
|
ret = am_hal_mspi_interrupt_disable(data->mspiHandle, 0xFFFFFFFF);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to disable interrupt, code:%d.",
|
|
__LINE__, ret);
|
|
ret = -EHOSTDOWN;
|
|
goto e_deinit_return;
|
|
}
|
|
|
|
ret = am_hal_mspi_interrupt_clear(data->mspiHandle, 0xFFFFFFFF);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to clear interrupt, code:%d.",
|
|
__LINE__, ret);
|
|
ret = -EHOSTDOWN;
|
|
goto e_deinit_return;
|
|
}
|
|
|
|
ret = am_hal_mspi_disable(data->mspiHandle);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to disable MSPI, code:%d.",
|
|
__LINE__, ret);
|
|
ret = -EHOSTDOWN;
|
|
goto e_deinit_return;
|
|
}
|
|
|
|
ret = am_hal_mspi_power_control(data->mspiHandle, AM_HAL_SYSCTRL_DEEPSLEEP, false);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to power off MSPI, code:%d.",
|
|
__LINE__, ret);
|
|
ret = -EHOSTDOWN;
|
|
goto e_deinit_return;
|
|
}
|
|
|
|
ret = am_hal_mspi_deinitialize(data->mspiHandle);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to deinit MSPI.", __LINE__);
|
|
ret = -ENODEV;
|
|
goto e_deinit_return;
|
|
}
|
|
return ret;
|
|
|
|
e_deinit_return:
|
|
k_mutex_unlock(&data->lock);
|
|
return ret;
|
|
}
|
|
|
|
/** DMA specific config */
|
|
static int mspi_xfer_config(const struct device *controller,
|
|
const struct mspi_xfer *xfer)
|
|
{
|
|
const struct mspi_ambiq_config *cfg = controller->config;
|
|
struct mspi_ambiq_data *data = controller->data;
|
|
am_hal_mspi_dev_config_t hal_dev_cfg = data->hal_dev_cfg;
|
|
am_hal_mspi_request_e eRequest;
|
|
int ret = 0;
|
|
|
|
if (data->scramble_cfg.enable) {
|
|
eRequest = AM_HAL_MSPI_REQ_SCRAMB_EN;
|
|
} else {
|
|
eRequest = AM_HAL_MSPI_REQ_SCRAMB_DIS;
|
|
}
|
|
|
|
ret = am_hal_mspi_disable(data->mspiHandle);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to disable MSPI, code:%d.",
|
|
__LINE__, ret);
|
|
return -EHOSTDOWN;
|
|
}
|
|
|
|
ret = am_hal_mspi_control(data->mspiHandle, eRequest, NULL);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u,Unable to complete scramble config:%d.",
|
|
__LINE__, data->scramble_cfg.enable);
|
|
return -EHOSTDOWN;
|
|
}
|
|
|
|
if (xfer->cmd_length > AM_HAL_MSPI_INSTR_2_BYTE + 1) {
|
|
LOG_INST_ERR(cfg->log, "%u, cmd_length is too large.", __LINE__);
|
|
return -ENOTSUP;
|
|
}
|
|
if (xfer->cmd_length == 0) {
|
|
hal_dev_cfg.bSendInstr = false;
|
|
} else {
|
|
hal_dev_cfg.bSendInstr = true;
|
|
hal_dev_cfg.eInstrCfg = xfer->cmd_length - 1;
|
|
}
|
|
|
|
if (xfer->addr_length > AM_HAL_MSPI_ADDR_4_BYTE + 1) {
|
|
LOG_INST_ERR(cfg->log, "%u, addr_length is too large.", __LINE__);
|
|
return -ENOTSUP;
|
|
}
|
|
if (xfer->addr_length == 0) {
|
|
hal_dev_cfg.bSendAddr = false;
|
|
} else {
|
|
hal_dev_cfg.bSendAddr = true;
|
|
hal_dev_cfg.eAddrCfg = xfer->addr_length - 1;
|
|
}
|
|
|
|
hal_dev_cfg.bTurnaround = (xfer->rx_dummy != 0);
|
|
hal_dev_cfg.ui8TurnAround = (uint8_t)xfer->rx_dummy;
|
|
hal_dev_cfg.bEnWriteLatency = (xfer->tx_dummy != 0);
|
|
hal_dev_cfg.ui8WriteLatency = (uint8_t)xfer->tx_dummy;
|
|
|
|
ret = am_hal_mspi_device_configure(data->mspiHandle, &hal_dev_cfg);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to configure MSPI, code:%d.",
|
|
__LINE__, ret);
|
|
return -EHOSTDOWN;
|
|
}
|
|
|
|
ret = am_hal_mspi_enable(data->mspiHandle);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to enable MSPI, code:%d.",
|
|
__LINE__, ret);
|
|
return -EHOSTDOWN;
|
|
}
|
|
|
|
data->hal_dev_cfg = hal_dev_cfg;
|
|
return ret;
|
|
}
|
|
|
|
static int mspi_ambiq_config(const struct mspi_dt_spec *spec)
|
|
{
|
|
const struct mspi_cfg *config = &spec->config;
|
|
const struct mspi_ambiq_config *cfg = spec->bus->config;
|
|
struct mspi_ambiq_data *data = spec->bus->data;
|
|
|
|
int ret = 0;
|
|
|
|
if (config->op_mode != MSPI_OP_MODE_CONTROLLER) {
|
|
LOG_INST_ERR(cfg->log, "%u, only support MSPI controller mode.", __LINE__);
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (config->max_freq > MSPI_MAX_FREQ) {
|
|
LOG_INST_ERR(cfg->log, "%u, max_freq too large.", __LINE__);
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (config->duplex != MSPI_HALF_DUPLEX) {
|
|
LOG_INST_ERR(cfg->log, "%u, only support half duplex mode.", __LINE__);
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (config->dqs_support) {
|
|
LOG_INST_ERR(cfg->log, "%u, only support non-DQS mode.", __LINE__);
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (config->re_init) {
|
|
ret = mspi_ambiq_deinit(spec->bus);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ret = am_hal_mspi_initialize(config->channel_num, &data->mspiHandle);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to initialize MSPI, code:%d.",
|
|
__LINE__, ret);
|
|
return -EPERM;
|
|
}
|
|
|
|
ret = am_hal_mspi_power_control(data->mspiHandle, AM_HAL_SYSCTRL_WAKE, false);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to power on MSPI, code:%d.",
|
|
__LINE__, ret);
|
|
return -EHOSTDOWN;
|
|
}
|
|
|
|
ret = am_hal_mspi_enable(data->mspiHandle);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to Enable MSPI, code:%d.",
|
|
__LINE__, ret);
|
|
return -EHOSTDOWN;
|
|
}
|
|
|
|
ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = am_hal_mspi_interrupt_clear(data->mspiHandle, AM_HAL_MSPI_INT_CQUPD |
|
|
AM_HAL_MSPI_INT_ERR);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to clear interrupt, code:%d.",
|
|
__LINE__, ret);
|
|
return -EHOSTDOWN;
|
|
}
|
|
|
|
ret = am_hal_mspi_interrupt_enable(data->mspiHandle, AM_HAL_MSPI_INT_CQUPD |
|
|
AM_HAL_MSPI_INT_ERR);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to turn on interrupt, code:%d.",
|
|
__LINE__, ret);
|
|
return -EHOSTDOWN;
|
|
}
|
|
|
|
cfg->irq_cfg_func();
|
|
|
|
mspi_context_unlock_unconditionally(&data->ctx);
|
|
|
|
if (config->re_init) {
|
|
k_mutex_unlock(&data->lock);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mspi_ambiq_dev_config(const struct device *controller,
|
|
const struct mspi_dev_id *dev_id,
|
|
const enum mspi_dev_cfg_mask param_mask,
|
|
const struct mspi_dev_cfg *dev_cfg)
|
|
{
|
|
const struct mspi_ambiq_config *cfg = controller->config;
|
|
struct mspi_ambiq_data *data = controller->data;
|
|
am_hal_mspi_dev_config_t hal_dev_cfg = data->hal_dev_cfg;
|
|
int ret = 0;
|
|
|
|
if (data->dev_id != dev_id) {
|
|
if (k_mutex_lock(&data->lock, K_MSEC(CONFIG_MSPI_COMPLETION_TIMEOUT_TOLERANCE))) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to gain controller access.", __LINE__);
|
|
return -EBUSY;
|
|
}
|
|
|
|
ret = mspi_verify_device(controller, dev_id);
|
|
if (ret) {
|
|
goto e_return;
|
|
}
|
|
}
|
|
|
|
if (mspi_is_inp(controller)) {
|
|
ret = -EBUSY;
|
|
goto e_return;
|
|
}
|
|
|
|
if (param_mask == MSPI_DEVICE_CONFIG_NONE &&
|
|
!cfg->mspicfg.sw_multi_periph) {
|
|
/* Do nothing except obtaining the controller lock */
|
|
data->dev_id = (struct mspi_dev_id *)dev_id;
|
|
return ret;
|
|
|
|
} else if (param_mask != MSPI_DEVICE_CONFIG_ALL) {
|
|
if (data->dev_id != dev_id) {
|
|
LOG_INST_ERR(cfg->log, "%u, config failed, must be the same device.",
|
|
__LINE__);
|
|
ret = -ENOTSUP;
|
|
goto e_return;
|
|
}
|
|
|
|
if ((param_mask & (~(MSPI_DEVICE_CONFIG_FREQUENCY |
|
|
MSPI_DEVICE_CONFIG_IO_MODE |
|
|
MSPI_DEVICE_CONFIG_CE_NUM |
|
|
MSPI_DEVICE_CONFIG_DATA_RATE |
|
|
MSPI_DEVICE_CONFIG_CMD_LEN |
|
|
MSPI_DEVICE_CONFIG_ADDR_LEN)))) {
|
|
LOG_INST_ERR(cfg->log, "%u, config type not supported.", __LINE__);
|
|
ret = -ENOTSUP;
|
|
goto e_return;
|
|
}
|
|
|
|
if (param_mask & MSPI_DEVICE_CONFIG_FREQUENCY) {
|
|
hal_dev_cfg.eClockFreq = mspi_set_freq(cfg, dev_cfg->freq);
|
|
if (hal_dev_cfg.eClockFreq == 0) {
|
|
ret = -ENOTSUP;
|
|
goto e_return;
|
|
}
|
|
ret = am_hal_mspi_control(data->mspiHandle,
|
|
AM_HAL_MSPI_REQ_CLOCK_CONFIG,
|
|
&hal_dev_cfg.eClockFreq);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, failed to configure eClockFreq.",
|
|
__LINE__);
|
|
ret = -EHOSTDOWN;
|
|
goto e_return;
|
|
}
|
|
data->dev_cfg.freq = dev_cfg->freq;
|
|
}
|
|
|
|
if ((param_mask & MSPI_DEVICE_CONFIG_IO_MODE) ||
|
|
(param_mask & MSPI_DEVICE_CONFIG_CE_NUM) ||
|
|
(param_mask & MSPI_DEVICE_CONFIG_DATA_RATE)) {
|
|
hal_dev_cfg.eDeviceConfig = mspi_set_line(cfg, dev_cfg->io_mode,
|
|
dev_cfg->data_rate,
|
|
dev_cfg->ce_num);
|
|
if (hal_dev_cfg.eDeviceConfig == AM_HAL_MSPI_FLASH_MAX) {
|
|
ret = -ENOTSUP;
|
|
goto e_return;
|
|
}
|
|
ret = am_hal_mspi_control(data->mspiHandle,
|
|
AM_HAL_MSPI_REQ_DEVICE_CONFIG,
|
|
&hal_dev_cfg.eDeviceConfig);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, failed to configure device.", __LINE__);
|
|
ret = -EHOSTDOWN;
|
|
goto e_return;
|
|
}
|
|
data->dev_cfg.freq = dev_cfg->io_mode;
|
|
data->dev_cfg.data_rate = dev_cfg->data_rate;
|
|
data->dev_cfg.ce_num = dev_cfg->ce_num;
|
|
}
|
|
|
|
if (param_mask & MSPI_DEVICE_CONFIG_CMD_LEN) {
|
|
if (dev_cfg->cmd_length > AM_HAL_MSPI_INSTR_2_BYTE + 1 ||
|
|
dev_cfg->cmd_length == 0) {
|
|
LOG_INST_ERR(cfg->log, "%u, invalid cmd_length.", __LINE__);
|
|
ret = -ENOTSUP;
|
|
goto e_return;
|
|
}
|
|
hal_dev_cfg.eInstrCfg = dev_cfg->cmd_length - 1;
|
|
ret = am_hal_mspi_control(data->mspiHandle,
|
|
AM_HAL_MSPI_REQ_ISIZE_SET,
|
|
&hal_dev_cfg.eInstrCfg);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, failed to configure cmd_length.",
|
|
__LINE__);
|
|
ret = -EHOSTDOWN;
|
|
goto e_return;
|
|
}
|
|
data->dev_cfg.cmd_length = dev_cfg->cmd_length;
|
|
}
|
|
|
|
if (param_mask & MSPI_DEVICE_CONFIG_ADDR_LEN) {
|
|
if (dev_cfg->addr_length > AM_HAL_MSPI_ADDR_4_BYTE + 1 ||
|
|
dev_cfg->addr_length == 0) {
|
|
LOG_INST_ERR(cfg->log, "%u, invalid addr_length.", __LINE__);
|
|
ret = -ENOTSUP;
|
|
goto e_return;
|
|
}
|
|
hal_dev_cfg.eAddrCfg = dev_cfg->addr_length - 1;
|
|
ret = am_hal_mspi_control(data->mspiHandle,
|
|
AM_HAL_MSPI_REQ_ASIZE_SET,
|
|
&hal_dev_cfg.eAddrCfg);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, failed to configure addr_length.",
|
|
__LINE__);
|
|
ret = -EHOSTDOWN;
|
|
goto e_return;
|
|
}
|
|
data->dev_cfg.addr_length = dev_cfg->addr_length;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (data->dev_id != dev_id) {
|
|
ret = pinctrl_apply_state(cfg->pcfg,
|
|
PINCTRL_STATE_PRIV_START + dev_id->dev_idx);
|
|
if (ret) {
|
|
goto e_return;
|
|
}
|
|
}
|
|
|
|
if (memcmp(&data->dev_cfg, dev_cfg, sizeof(struct mspi_dev_cfg)) == 0) {
|
|
/** Nothing to config */
|
|
data->dev_id = (struct mspi_dev_id *)dev_id;
|
|
return ret;
|
|
}
|
|
|
|
if (dev_cfg->endian != MSPI_XFER_LITTLE_ENDIAN) {
|
|
LOG_INST_ERR(cfg->log, "%u, only support MSB first.", __LINE__);
|
|
ret = -ENOTSUP;
|
|
goto e_return;
|
|
}
|
|
|
|
if (dev_cfg->dqs_enable && !cfg->mspicfg.dqs_support) {
|
|
LOG_INST_ERR(cfg->log, "%u, only support non-DQS mode.", __LINE__);
|
|
ret = -ENOTSUP;
|
|
goto e_return;
|
|
}
|
|
|
|
hal_dev_cfg.eSpiMode = dev_cfg->cpp;
|
|
hal_dev_cfg.bEnWriteLatency = (dev_cfg->tx_dummy != 0);
|
|
hal_dev_cfg.ui8WriteLatency = dev_cfg->tx_dummy;
|
|
hal_dev_cfg.bTurnaround = (dev_cfg->rx_dummy != 0);
|
|
hal_dev_cfg.ui8TurnAround = dev_cfg->rx_dummy;
|
|
|
|
hal_dev_cfg.eClockFreq = mspi_set_freq(cfg, dev_cfg->freq);
|
|
if (hal_dev_cfg.eClockFreq == 0) {
|
|
ret = -ENOTSUP;
|
|
goto e_return;
|
|
}
|
|
|
|
hal_dev_cfg.eDeviceConfig = mspi_set_line(cfg, dev_cfg->io_mode, dev_cfg->data_rate,
|
|
dev_cfg->ce_num);
|
|
if (hal_dev_cfg.eDeviceConfig == AM_HAL_MSPI_FLASH_MAX) {
|
|
ret = -ENOTSUP;
|
|
goto e_return;
|
|
}
|
|
|
|
if (dev_cfg->cmd_length > AM_HAL_MSPI_INSTR_2_BYTE + 1) {
|
|
LOG_INST_ERR(cfg->log, "%u, cmd_length too large.", __LINE__);
|
|
ret = -ENOTSUP;
|
|
goto e_return;
|
|
}
|
|
if (dev_cfg->cmd_length == 0) {
|
|
hal_dev_cfg.bSendInstr = false;
|
|
} else {
|
|
hal_dev_cfg.bSendInstr = true;
|
|
hal_dev_cfg.eInstrCfg = dev_cfg->cmd_length - 1;
|
|
}
|
|
|
|
if (dev_cfg->addr_length > AM_HAL_MSPI_ADDR_4_BYTE + 1) {
|
|
LOG_INST_ERR(cfg->log, "%u, addr_length too large.", __LINE__);
|
|
ret = -ENOTSUP;
|
|
goto e_return;
|
|
}
|
|
if (dev_cfg->addr_length == 0) {
|
|
hal_dev_cfg.bSendAddr = false;
|
|
} else {
|
|
hal_dev_cfg.bSendAddr = true;
|
|
hal_dev_cfg.eAddrCfg = dev_cfg->addr_length - 1;
|
|
}
|
|
|
|
hal_dev_cfg.ui8ReadInstr = (uint8_t)dev_cfg->read_cmd;
|
|
hal_dev_cfg.ui8WriteInstr = (uint8_t)dev_cfg->write_cmd;
|
|
|
|
hal_dev_cfg.eDMABoundary = mspi_set_mem_boundary(dev_cfg->mem_boundary);
|
|
if (hal_dev_cfg.eDMABoundary >= AM_HAL_MSPI_BOUNDARY_MAX) {
|
|
LOG_INST_ERR(cfg->log, "%u, mem_boundary too large.", __LINE__);
|
|
ret = -ENOTSUP;
|
|
goto e_return;
|
|
}
|
|
|
|
/** ui16DMATimeLimit unit is in 0.1us */
|
|
hal_dev_cfg.ui16DMATimeLimit = dev_cfg->time_to_break * 10;
|
|
|
|
ret = am_hal_mspi_disable(data->mspiHandle);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to disable MSPI, code:%d.", __LINE__, ret);
|
|
ret = -EHOSTDOWN;
|
|
goto e_return;
|
|
}
|
|
|
|
ret = am_hal_mspi_device_configure(data->mspiHandle, &hal_dev_cfg);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to configure MSPI, code:%d.", __LINE__,
|
|
ret);
|
|
ret = -EHOSTDOWN;
|
|
goto e_return;
|
|
}
|
|
|
|
ret = am_hal_mspi_enable(data->mspiHandle);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to enable MSPI, code:%d.", __LINE__, ret);
|
|
ret = -EHOSTDOWN;
|
|
goto e_return;
|
|
}
|
|
data->dev_cfg = *dev_cfg;
|
|
data->dev_id = (struct mspi_dev_id *)dev_id;
|
|
}
|
|
data->hal_dev_cfg = hal_dev_cfg;
|
|
|
|
return ret;
|
|
|
|
e_return:
|
|
k_mutex_unlock(&data->lock);
|
|
return ret;
|
|
}
|
|
|
|
static int mspi_ambiq_xip_config(const struct device *controller,
|
|
const struct mspi_dev_id *dev_id,
|
|
const struct mspi_xip_cfg *xip_cfg)
|
|
{
|
|
const struct mspi_ambiq_config *cfg = controller->config;
|
|
struct mspi_ambiq_data *data = controller->data;
|
|
am_hal_mspi_request_e eRequest;
|
|
int ret = 0;
|
|
|
|
if (dev_id != data->dev_id) {
|
|
LOG_INST_ERR(cfg->log, "%u, dev_id don't match.", __LINE__);
|
|
return -ESTALE;
|
|
}
|
|
|
|
if (xip_cfg->enable) {
|
|
eRequest = AM_HAL_MSPI_REQ_XIP_EN;
|
|
} else {
|
|
eRequest = AM_HAL_MSPI_REQ_XIP_DIS;
|
|
}
|
|
|
|
ret = am_hal_mspi_control(data->mspiHandle, eRequest, NULL);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u,Unable to complete xip config:%d.", __LINE__,
|
|
xip_cfg->enable);
|
|
return -EHOSTDOWN;
|
|
}
|
|
|
|
data->xip_cfg = *xip_cfg;
|
|
return ret;
|
|
}
|
|
|
|
static int mspi_ambiq_scramble_config(const struct device *controller,
|
|
const struct mspi_dev_id *dev_id,
|
|
const struct mspi_scramble_cfg *scramble_cfg)
|
|
{
|
|
const struct mspi_ambiq_config *cfg = controller->config;
|
|
struct mspi_ambiq_data *data = controller->data;
|
|
am_hal_mspi_dev_config_t hal_dev_cfg = data->hal_dev_cfg;
|
|
am_hal_mspi_request_e eRequest;
|
|
int ret = 0;
|
|
|
|
if (mspi_is_inp(controller)) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
if (dev_id != data->dev_id) {
|
|
LOG_INST_ERR(cfg->log, "%u, dev_id don't match.", __LINE__);
|
|
return -ESTALE;
|
|
}
|
|
|
|
if (scramble_cfg->enable) {
|
|
eRequest = AM_HAL_MSPI_REQ_SCRAMB_EN;
|
|
} else {
|
|
eRequest = AM_HAL_MSPI_REQ_SCRAMB_DIS;
|
|
}
|
|
|
|
ret = am_hal_mspi_disable(data->mspiHandle);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to disable MSPI, code:%d.", __LINE__, ret);
|
|
return -EHOSTDOWN;
|
|
}
|
|
|
|
ret = am_hal_mspi_control(data->mspiHandle, eRequest, NULL);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u,Unable to complete scramble config:%d.", __LINE__,
|
|
scramble_cfg->enable);
|
|
return -EHOSTDOWN;
|
|
}
|
|
|
|
hal_dev_cfg.scramblingStartAddr = 0 + scramble_cfg->address_offset;
|
|
hal_dev_cfg.scramblingEndAddr = hal_dev_cfg.scramblingStartAddr + scramble_cfg->size;
|
|
|
|
ret = am_hal_mspi_device_configure(data->mspiHandle, &hal_dev_cfg);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to configure MSPI, code:%d.", __LINE__, ret);
|
|
return -EHOSTDOWN;
|
|
}
|
|
|
|
ret = am_hal_mspi_enable(data->mspiHandle);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to enable MSPI, code:%d.", __LINE__, ret);
|
|
return -EHOSTDOWN;
|
|
}
|
|
|
|
data->scramble_cfg = *scramble_cfg;
|
|
data->hal_dev_cfg = hal_dev_cfg;
|
|
return ret;
|
|
}
|
|
|
|
static int mspi_ambiq_timing_config(const struct device *controller,
|
|
const struct mspi_dev_id *dev_id,
|
|
const uint32_t param_mask,
|
|
void *timing_cfg)
|
|
{
|
|
const struct mspi_ambiq_config *cfg = controller->config;
|
|
struct mspi_ambiq_data *data = controller->data;
|
|
am_hal_mspi_dev_config_t hal_dev_cfg = data->hal_dev_cfg;
|
|
struct mspi_ambiq_timing_cfg *time_cfg = timing_cfg;
|
|
am_hal_mspi_timing_scan_t timing;
|
|
int ret = 0;
|
|
|
|
if (mspi_is_inp(controller)) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
if (dev_id != data->dev_id) {
|
|
LOG_INST_ERR(cfg->log, "%u, dev_id don't match.", __LINE__);
|
|
return -ESTALE;
|
|
}
|
|
|
|
if (param_mask & (~(MSPI_AMBIQ_SET_WLC | MSPI_AMBIQ_SET_RLC))) {
|
|
LOG_INST_ERR(cfg->log, "%u, config type not supported.", __LINE__);
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (param_mask & MSPI_AMBIQ_SET_WLC) {
|
|
if (time_cfg->ui8WriteLatency) {
|
|
hal_dev_cfg.bEnWriteLatency = true;
|
|
} else {
|
|
hal_dev_cfg.bEnWriteLatency = false;
|
|
}
|
|
hal_dev_cfg.ui8WriteLatency = time_cfg->ui8WriteLatency;
|
|
}
|
|
|
|
if (param_mask & MSPI_AMBIQ_SET_RLC) {
|
|
if (time_cfg->ui8TurnAround) {
|
|
hal_dev_cfg.bTurnaround = true;
|
|
} else {
|
|
hal_dev_cfg.bTurnaround = false;
|
|
}
|
|
hal_dev_cfg.ui8TurnAround = time_cfg->ui8TurnAround;
|
|
}
|
|
|
|
timing.ui8Turnaround = hal_dev_cfg.ui8TurnAround;
|
|
timing.ui8WriteLatency = hal_dev_cfg.ui8WriteLatency;
|
|
|
|
ret = am_hal_mspi_control(data->mspiHandle, AM_HAL_MSPI_REQ_TIMING_SCAN, &timing);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to configure timing.", __LINE__);
|
|
return -EHOSTDOWN;
|
|
}
|
|
|
|
data->hal_dev_cfg = hal_dev_cfg;
|
|
return ret;
|
|
}
|
|
|
|
static int mspi_ambiq_get_channel_status(const struct device *controller, uint8_t ch)
|
|
{
|
|
ARG_UNUSED(ch);
|
|
|
|
const struct mspi_ambiq_config *cfg = controller->config;
|
|
struct mspi_ambiq_data *data = controller->data;
|
|
int ret = 0;
|
|
|
|
if (sys_read32(cfg->reg_base) & MSPI_BUSY) {
|
|
ret = -EBUSY;
|
|
}
|
|
|
|
if (mspi_is_inp(controller)) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
data->dev_id = NULL;
|
|
k_mutex_unlock(&data->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void mspi_ambiq_isr(const struct device *dev)
|
|
{
|
|
struct mspi_ambiq_data *data = dev->data;
|
|
uint32_t ui32Status;
|
|
|
|
am_hal_mspi_interrupt_status_get(data->mspiHandle, &ui32Status, false);
|
|
am_hal_mspi_interrupt_clear(data->mspiHandle, ui32Status);
|
|
am_hal_mspi_interrupt_service(data->mspiHandle, ui32Status);
|
|
}
|
|
|
|
/** Manage sync dma transceive */
|
|
static void hal_mspi_callback(void *pCallbackCtxt, uint32_t status)
|
|
{
|
|
const struct device *controller = pCallbackCtxt;
|
|
struct mspi_ambiq_data *data = controller->data;
|
|
|
|
data->ctx.packets_done++;
|
|
}
|
|
|
|
static int mspi_pio_prepare(const struct device *controller,
|
|
am_hal_mspi_pio_transfer_t *trans)
|
|
{
|
|
const struct mspi_ambiq_config *cfg = controller->config;
|
|
struct mspi_ambiq_data *data = controller->data;
|
|
const struct mspi_xfer *xfer = &data->ctx.xfer;
|
|
int ret = 0;
|
|
|
|
trans->bScrambling = false;
|
|
trans->bSendAddr = (xfer->addr_length != 0);
|
|
trans->bSendInstr = (xfer->cmd_length != 0);
|
|
trans->bTurnaround = (xfer->rx_dummy != 0);
|
|
trans->bEnWRLatency = (xfer->tx_dummy != 0);
|
|
trans->bDCX = false;
|
|
trans->bQuadCmd = false;
|
|
trans->bContinue = false;
|
|
|
|
if (xfer->cmd_length > AM_HAL_MSPI_INSTR_2_BYTE + 1) {
|
|
LOG_INST_ERR(cfg->log, "%u, invalid cmd_length.", __LINE__);
|
|
return -ENOTSUP;
|
|
}
|
|
if (xfer->cmd_length != 0) {
|
|
am_hal_mspi_instr_e eInstrCfg = xfer->cmd_length - 1;
|
|
|
|
ret = am_hal_mspi_control(data->mspiHandle, AM_HAL_MSPI_REQ_ISIZE_SET, &eInstrCfg);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, failed to configure cmd_length.",
|
|
__LINE__);
|
|
return -EHOSTDOWN;
|
|
}
|
|
data->hal_dev_cfg.eInstrCfg = eInstrCfg;
|
|
}
|
|
data->dev_cfg.cmd_length = xfer->cmd_length;
|
|
|
|
if (xfer->addr_length > AM_HAL_MSPI_ADDR_4_BYTE + 1) {
|
|
LOG_INST_ERR(cfg->log, "%u, invalid addr_length.", __LINE__);
|
|
return -ENOTSUP;
|
|
}
|
|
if (xfer->addr_length != 0) {
|
|
am_hal_mspi_addr_e eAddrCfg = xfer->addr_length - 1;
|
|
|
|
ret = am_hal_mspi_control(data->mspiHandle, AM_HAL_MSPI_REQ_ASIZE_SET, &eAddrCfg);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, failed to configure addr_length.", __LINE__);
|
|
return -EHOSTDOWN;
|
|
}
|
|
data->hal_dev_cfg.eAddrCfg = eAddrCfg;
|
|
}
|
|
data->dev_cfg.addr_length = xfer->addr_length;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mspi_pio_transceive(const struct device *controller,
|
|
const struct mspi_xfer *xfer,
|
|
mspi_callback_handler_t cb,
|
|
struct mspi_callback_context *cb_ctx)
|
|
{
|
|
const struct mspi_ambiq_config *cfg = controller->config;
|
|
struct mspi_ambiq_data *data = controller->data;
|
|
struct mspi_context *ctx = &data->ctx;
|
|
const struct mspi_xfer_packet *packet;
|
|
uint32_t packet_idx;
|
|
am_hal_mspi_pio_transfer_t trans;
|
|
int ret = 0;
|
|
int cfg_flag = 0;
|
|
|
|
if (xfer->num_packet == 0 ||
|
|
!xfer->packets ||
|
|
xfer->timeout > CONFIG_MSPI_COMPLETION_TIMEOUT_TOLERANCE) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
cfg_flag = mspi_context_lock(ctx, data->dev_id, xfer, cb, cb_ctx, true);
|
|
/** For async, user must make sure when cfg_flag = 0 the dummy and instr addr length
|
|
* in mspi_xfer of the two calls are the same if the first one has not finished yet.
|
|
*/
|
|
if (cfg_flag) {
|
|
if (cfg_flag == 1) {
|
|
ret = mspi_pio_prepare(controller, &trans);
|
|
if (ret) {
|
|
goto pio_err;
|
|
}
|
|
} else {
|
|
ret = cfg_flag;
|
|
goto pio_err;
|
|
}
|
|
}
|
|
|
|
if (!ctx->xfer.async) {
|
|
|
|
while (ctx->packets_left > 0) {
|
|
packet_idx = ctx->xfer.num_packet - ctx->packets_left;
|
|
packet = &ctx->xfer.packets[packet_idx];
|
|
trans.eDirection = packet->dir;
|
|
trans.ui16DeviceInstr = (uint16_t)packet->cmd;
|
|
trans.ui32DeviceAddr = packet->address;
|
|
trans.ui32NumBytes = packet->num_bytes;
|
|
trans.pui32Buffer = (uint32_t *)packet->data_buf;
|
|
|
|
ret = am_hal_mspi_blocking_transfer(data->mspiHandle, &trans,
|
|
MSPI_TIMEOUT_US);
|
|
ctx->packets_left--;
|
|
if (ret) {
|
|
ret = -EIO;
|
|
goto pio_err;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
ret = am_hal_mspi_interrupt_enable(data->mspiHandle, AM_HAL_MSPI_INT_DMACMP);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, failed to enable interrupt.", __LINE__);
|
|
ret = -EHOSTDOWN;
|
|
goto pio_err;
|
|
}
|
|
|
|
while (ctx->packets_left > 0) {
|
|
packet_idx = ctx->xfer.num_packet - ctx->packets_left;
|
|
packet = &ctx->xfer.packets[packet_idx];
|
|
trans.eDirection = packet->dir;
|
|
trans.ui16DeviceInstr = (uint16_t)packet->cmd;
|
|
trans.ui32DeviceAddr = packet->address;
|
|
trans.ui32NumBytes = packet->num_bytes;
|
|
trans.pui32Buffer = (uint32_t *)packet->data_buf;
|
|
|
|
if (ctx->callback && packet->cb_mask == MSPI_BUS_XFER_COMPLETE_CB) {
|
|
ctx->callback_ctx->mspi_evt.evt_type = MSPI_BUS_XFER_COMPLETE;
|
|
ctx->callback_ctx->mspi_evt.evt_data.controller = controller;
|
|
ctx->callback_ctx->mspi_evt.evt_data.dev_id = data->ctx.owner;
|
|
ctx->callback_ctx->mspi_evt.evt_data.packet = packet;
|
|
ctx->callback_ctx->mspi_evt.evt_data.packet_idx = packet_idx;
|
|
ctx->callback_ctx->mspi_evt.evt_data.status = ~0;
|
|
}
|
|
|
|
am_hal_mspi_callback_t callback = NULL;
|
|
|
|
if (packet->cb_mask == MSPI_BUS_XFER_COMPLETE_CB) {
|
|
callback = (am_hal_mspi_callback_t)ctx->callback;
|
|
}
|
|
|
|
ret = am_hal_mspi_nonblocking_transfer(data->mspiHandle, &trans, MSPI_PIO,
|
|
callback, (void *)ctx->callback_ctx);
|
|
ctx->packets_left--;
|
|
if (ret) {
|
|
if (ret == AM_HAL_STATUS_OUT_OF_RANGE) {
|
|
ret = -ENOMEM;
|
|
} else {
|
|
ret = -EIO;
|
|
}
|
|
goto pio_err;
|
|
}
|
|
}
|
|
}
|
|
|
|
pio_err:
|
|
mspi_context_release(ctx);
|
|
return ret;
|
|
}
|
|
|
|
static int mspi_dma_transceive(const struct device *controller,
|
|
const struct mspi_xfer *xfer,
|
|
mspi_callback_handler_t cb,
|
|
struct mspi_callback_context *cb_ctx)
|
|
{
|
|
const struct mspi_ambiq_config *cfg = controller->config;
|
|
struct mspi_ambiq_data *data = controller->data;
|
|
struct mspi_context *ctx = &data->ctx;
|
|
am_hal_mspi_dma_transfer_t trans;
|
|
int ret = 0;
|
|
int cfg_flag = 0;
|
|
|
|
if (xfer->num_packet == 0 ||
|
|
!xfer->packets ||
|
|
xfer->timeout > CONFIG_MSPI_COMPLETION_TIMEOUT_TOLERANCE) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
cfg_flag = mspi_context_lock(ctx, data->dev_id, xfer, cb, cb_ctx, true);
|
|
/** For async, user must make sure when cfg_flag = 0 the dummy and instr addr length
|
|
* in mspi_xfer of the two calls are the same if the first one has not finished yet.
|
|
*/
|
|
if (cfg_flag) {
|
|
if (cfg_flag == 1) {
|
|
ret = mspi_xfer_config(controller, xfer);
|
|
if (ret) {
|
|
goto dma_err;
|
|
}
|
|
} else {
|
|
ret = cfg_flag;
|
|
goto dma_err;
|
|
}
|
|
}
|
|
|
|
ret = am_hal_mspi_interrupt_enable(data->mspiHandle, AM_HAL_MSPI_INT_DMACMP);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, failed to enable interrupt.", __LINE__);
|
|
ret = -EHOSTDOWN;
|
|
goto dma_err;
|
|
}
|
|
|
|
while (ctx->packets_left > 0) {
|
|
uint32_t packet_idx = ctx->xfer.num_packet - ctx->packets_left;
|
|
const struct mspi_xfer_packet *packet;
|
|
|
|
packet = &ctx->xfer.packets[packet_idx];
|
|
trans.ui8Priority = ctx->xfer.priority;
|
|
trans.eDirection = packet->dir;
|
|
trans.ui32TransferCount = packet->num_bytes;
|
|
trans.ui32DeviceAddress = packet->address;
|
|
trans.ui32SRAMAddress = (uint32_t)packet->data_buf;
|
|
trans.ui32PauseCondition = 0;
|
|
trans.ui32StatusSetClr = 0;
|
|
|
|
if (ctx->xfer.async) {
|
|
|
|
if (ctx->callback && packet->cb_mask == MSPI_BUS_XFER_COMPLETE_CB) {
|
|
ctx->callback_ctx->mspi_evt.evt_type = MSPI_BUS_XFER_COMPLETE;
|
|
ctx->callback_ctx->mspi_evt.evt_data.controller = controller;
|
|
ctx->callback_ctx->mspi_evt.evt_data.dev_id = data->ctx.owner;
|
|
ctx->callback_ctx->mspi_evt.evt_data.packet = packet;
|
|
ctx->callback_ctx->mspi_evt.evt_data.packet_idx = packet_idx;
|
|
ctx->callback_ctx->mspi_evt.evt_data.status = ~0;
|
|
}
|
|
|
|
am_hal_mspi_callback_t callback = NULL;
|
|
|
|
if (packet->cb_mask == MSPI_BUS_XFER_COMPLETE_CB) {
|
|
callback = (am_hal_mspi_callback_t)ctx->callback;
|
|
}
|
|
|
|
ret = am_hal_mspi_nonblocking_transfer(data->mspiHandle, &trans, MSPI_DMA,
|
|
callback, (void *)ctx->callback_ctx);
|
|
} else {
|
|
ret = am_hal_mspi_nonblocking_transfer(data->mspiHandle, &trans, MSPI_DMA,
|
|
hal_mspi_callback,
|
|
(void *)controller);
|
|
}
|
|
ctx->packets_left--;
|
|
if (ret) {
|
|
if (ret == AM_HAL_STATUS_OUT_OF_RANGE) {
|
|
ret = -ENOMEM;
|
|
} else {
|
|
ret = -EIO;
|
|
}
|
|
goto dma_err;
|
|
}
|
|
}
|
|
|
|
if (!ctx->xfer.async) {
|
|
while (ctx->packets_done < ctx->xfer.num_packet) {
|
|
k_busy_wait(10);
|
|
}
|
|
}
|
|
|
|
dma_err:
|
|
mspi_context_release(ctx);
|
|
return ret;
|
|
}
|
|
|
|
static int mspi_ambiq_transceive(const struct device *controller,
|
|
const struct mspi_dev_id *dev_id,
|
|
const struct mspi_xfer *xfer)
|
|
{
|
|
const struct mspi_ambiq_config *cfg = controller->config;
|
|
struct mspi_ambiq_data *data = controller->data;
|
|
mspi_callback_handler_t cb = NULL;
|
|
struct mspi_callback_context *cb_ctx = NULL;
|
|
|
|
if (dev_id != data->dev_id) {
|
|
LOG_INST_ERR(cfg->log, "%u, dev_id don't match.", __LINE__);
|
|
return -ESTALE;
|
|
}
|
|
|
|
if (xfer->async) {
|
|
cb = data->cbs[MSPI_BUS_XFER_COMPLETE];
|
|
cb_ctx = data->cb_ctxs[MSPI_BUS_XFER_COMPLETE];
|
|
}
|
|
|
|
if (xfer->xfer_mode == MSPI_PIO) {
|
|
return mspi_pio_transceive(controller, xfer, cb, cb_ctx);
|
|
} else if (xfer->xfer_mode == MSPI_DMA) {
|
|
return mspi_dma_transceive(controller, xfer, cb, cb_ctx);
|
|
} else {
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
static int mspi_ambiq_register_callback(const struct device *controller,
|
|
const struct mspi_dev_id *dev_id,
|
|
const enum mspi_bus_event evt_type,
|
|
mspi_callback_handler_t cb,
|
|
struct mspi_callback_context *ctx)
|
|
{
|
|
const struct mspi_ambiq_config *cfg = controller->config;
|
|
struct mspi_ambiq_data *data = controller->data;
|
|
|
|
if (mspi_is_inp(controller)) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
if (dev_id != data->dev_id) {
|
|
LOG_INST_ERR(cfg->log, "%u, dev_id don't match.", __LINE__);
|
|
return -ESTALE;
|
|
}
|
|
|
|
if (evt_type != MSPI_BUS_XFER_COMPLETE) {
|
|
LOG_INST_ERR(cfg->log, "%u, callback types not supported.", __LINE__);
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
data->cbs[evt_type] = cb;
|
|
data->cb_ctxs[evt_type] = ctx;
|
|
return 0;
|
|
}
|
|
|
|
#if CONFIG_PM_DEVICE
|
|
static int mspi_ambiq_pm_action(const struct device *controller, enum pm_device_action action)
|
|
{
|
|
const struct mspi_ambiq_config *cfg = controller->config;
|
|
struct mspi_ambiq_data *data = controller->data;
|
|
int ret = 0;
|
|
|
|
if (mspi_is_inp(controller)) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
switch (action) {
|
|
case PM_DEVICE_ACTION_TURN_ON:
|
|
ret = am_hal_mspi_power_control(data->mspiHandle, AM_HAL_SYSCTRL_WAKE, true);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to power on MSPI, code:%d.", __LINE__,
|
|
ret);
|
|
return -EHOSTDOWN;
|
|
}
|
|
break;
|
|
|
|
case PM_DEVICE_ACTION_TURN_OFF:
|
|
ret = am_hal_mspi_power_control(data->mspiHandle, AM_HAL_SYSCTRL_DEEPSLEEP, true);
|
|
if (ret) {
|
|
LOG_INST_ERR(cfg->log, "%u, fail to power off MSPI, code:%d.", __LINE__,
|
|
ret);
|
|
return -EHOSTDOWN;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int mspi_ambiq_init(const struct device *controller)
|
|
{
|
|
const struct mspi_ambiq_config *cfg = controller->config;
|
|
const struct mspi_dt_spec spec = {
|
|
.bus = controller,
|
|
.config = cfg->mspicfg,
|
|
};
|
|
|
|
return mspi_ambiq_config(&spec);
|
|
}
|
|
|
|
static struct mspi_driver_api mspi_ambiq_driver_api = {
|
|
.config = mspi_ambiq_config,
|
|
.dev_config = mspi_ambiq_dev_config,
|
|
.xip_config = mspi_ambiq_xip_config,
|
|
.scramble_config = mspi_ambiq_scramble_config,
|
|
.timing_config = mspi_ambiq_timing_config,
|
|
.get_channel_status = mspi_ambiq_get_channel_status,
|
|
.register_callback = mspi_ambiq_register_callback,
|
|
.transceive = mspi_ambiq_transceive,
|
|
};
|
|
|
|
#define MSPI_PINCTRL_STATE_INIT(state_idx, node_id) \
|
|
COND_CODE_1(Z_PINCTRL_SKIP_STATE(state_idx, node_id), (), \
|
|
({ \
|
|
.id = state_idx, \
|
|
.pins = Z_PINCTRL_STATE_PINS_NAME(state_idx, node_id), \
|
|
.pin_cnt = ARRAY_SIZE(Z_PINCTRL_STATE_PINS_NAME(state_idx, node_id)) \
|
|
}))
|
|
|
|
#define MSPI_PINCTRL_STATES_DEFINE(node_id) \
|
|
static const struct pinctrl_state \
|
|
Z_PINCTRL_STATES_NAME(node_id)[] = { \
|
|
LISTIFY(DT_NUM_PINCTRL_STATES(node_id), \
|
|
MSPI_PINCTRL_STATE_INIT, (,), node_id) \
|
|
};
|
|
|
|
#define MSPI_PINCTRL_DT_DEFINE(node_id) \
|
|
LISTIFY(DT_NUM_PINCTRL_STATES(node_id), \
|
|
Z_PINCTRL_STATE_PINS_DEFINE, (;), node_id); \
|
|
MSPI_PINCTRL_STATES_DEFINE(node_id) \
|
|
Z_PINCTRL_DEV_CONFIG_STATIC Z_PINCTRL_DEV_CONFIG_CONST \
|
|
struct pinctrl_dev_config Z_PINCTRL_DEV_CONFIG_NAME(node_id) = \
|
|
Z_PINCTRL_DEV_CONFIG_INIT(node_id)
|
|
|
|
#define MSPI_CONFIG(n) \
|
|
{ \
|
|
.channel_num = (DT_INST_REG_ADDR(n) - MSPI0_BASE) / \
|
|
(DT_INST_REG_SIZE(n) * 4), \
|
|
.op_mode = MSPI_OP_MODE_CONTROLLER, \
|
|
.duplex = MSPI_HALF_DUPLEX, \
|
|
.max_freq = MSPI_MAX_FREQ, \
|
|
.dqs_support = false, \
|
|
.num_periph = DT_INST_CHILD_NUM(n), \
|
|
.sw_multi_periph = DT_INST_PROP(n, software_multiperipheral), \
|
|
}
|
|
|
|
#define MSPI_HAL_DEVICE_CONFIG(n, cmdq, cmdq_size) \
|
|
{ \
|
|
.ui8WriteLatency = 0, \
|
|
.ui8TurnAround = 0, \
|
|
.eAddrCfg = 0, \
|
|
.eInstrCfg = 0, \
|
|
.ui8ReadInstr = 0, \
|
|
.ui8WriteInstr = 0, \
|
|
.eDeviceConfig = AM_HAL_MSPI_FLASH_SERIAL_CE0, \
|
|
.eSpiMode = AM_HAL_MSPI_SPI_MODE_0, \
|
|
.eClockFreq = MSPI_MAX_FREQ / DT_INST_PROP_OR(n, \
|
|
clock_frequency, \
|
|
MSPI_MAX_FREQ), \
|
|
.bEnWriteLatency = false, \
|
|
.bSendAddr = false, \
|
|
.bSendInstr = false, \
|
|
.bTurnaround = false, \
|
|
.bEmulateDDR = false, \
|
|
.ui16DMATimeLimit = 0, \
|
|
.eDMABoundary = AM_HAL_MSPI_BOUNDARY_NONE, \
|
|
.ui32TCBSize = cmdq_size, \
|
|
.pTCB = cmdq, \
|
|
.scramblingStartAddr = 0, \
|
|
.scramblingEndAddr = 0, \
|
|
}
|
|
|
|
#define AMBIQ_MSPI_DEFINE(n) \
|
|
LOG_INSTANCE_REGISTER(DT_DRV_INST(n), mspi##n, CONFIG_MSPI_LOG_LEVEL); \
|
|
MSPI_PINCTRL_DT_DEFINE(DT_DRV_INST(n)); \
|
|
static void mspi_ambiq_irq_cfg_func_##n(void) \
|
|
{ \
|
|
IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \
|
|
mspi_ambiq_isr, DEVICE_DT_INST_GET(n), 0); \
|
|
irq_enable(DT_INST_IRQN(n)); \
|
|
} \
|
|
static uint32_t mspi_ambiq_cmdq##n[DT_INST_PROP_OR(n, cmdq_buffer_size, 1024) / 4] \
|
|
__attribute__((section(DT_INST_PROP_OR(n, cmdq_buffer_location, ".mspi_buff")))); \
|
|
static struct gpio_dt_spec ce_gpios##n[] = MSPI_CE_GPIOS_DT_SPEC_INST_GET(n); \
|
|
static struct mspi_ambiq_data mspi_ambiq_data##n = { \
|
|
.mspiHandle = NULL, \
|
|
.hal_dev_cfg = MSPI_HAL_DEVICE_CONFIG(n, mspi_ambiq_cmdq##n, \
|
|
DT_INST_PROP_OR(n, cmdq_buffer_size, 1024)), \
|
|
.dev_id = 0, \
|
|
.lock = Z_MUTEX_INITIALIZER(mspi_ambiq_data##n.lock), \
|
|
.dev_cfg = {0}, \
|
|
.xip_cfg = {0}, \
|
|
.scramble_cfg = {0}, \
|
|
.cbs = {0}, \
|
|
.cb_ctxs = {0}, \
|
|
.ctx.lock = Z_SEM_INITIALIZER(mspi_ambiq_data##n.ctx.lock, 0, 1), \
|
|
.ctx.callback = 0, \
|
|
.ctx.callback_ctx = 0, \
|
|
}; \
|
|
static const struct mspi_ambiq_config mspi_ambiq_config##n = { \
|
|
.reg_base = DT_INST_REG_ADDR(n), \
|
|
.reg_size = DT_INST_REG_SIZE(n), \
|
|
.mspicfg = MSPI_CONFIG(n), \
|
|
.mspicfg.ce_group = (struct gpio_dt_spec *)ce_gpios##n, \
|
|
.mspicfg.num_ce_gpios = ARRAY_SIZE(ce_gpios##n), \
|
|
.mspicfg.re_init = false, \
|
|
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
|
|
.irq_cfg_func = mspi_ambiq_irq_cfg_func_##n, \
|
|
LOG_INSTANCE_PTR_INIT(log, DT_DRV_INST(n), mspi##n) \
|
|
}; \
|
|
PM_DEVICE_DT_INST_DEFINE(n, mspi_ambiq_pm_action); \
|
|
DEVICE_DT_INST_DEFINE(n, \
|
|
mspi_ambiq_init, \
|
|
PM_DEVICE_DT_INST_GET(n), \
|
|
&mspi_ambiq_data##n, \
|
|
&mspi_ambiq_config##n, \
|
|
POST_KERNEL, \
|
|
CONFIG_MSPI_INIT_PRIORITY, \
|
|
&mspi_ambiq_driver_api);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(AMBIQ_MSPI_DEFINE)
|