drivers: memc: update interface of memc flexspi driver for multi device

Update interface of memc flexspi driver to better handle multiple
devices. Previously, using multiple devices on one FlexSPI bus would
require the user to configure each device to install its command table
(referred to as a LUT table by the driver) at an offset, so that it did
not overlap with other devices on the bus.

This commit changes the interface of the memc flexspi driver to instead
configure the LUT and flash device in one call. This allows the memc
driver to record the port each LUT sequence is used with, so that
future FlexSPI transfer requests can have their LUT offsets adjusted
based on the target port (which will correspond to a target device)

Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
This commit is contained in:
Daniel DeGrasse 2023-08-04 22:02:34 +00:00 committed by Carles Cufí
parent f9b7f8c96d
commit 9a63f39cd8
8 changed files with 169 additions and 105 deletions

View file

@ -596,7 +596,6 @@ static int flash_flexspi_hyperflash_init(const struct device *dev)
{
const struct flash_flexspi_hyperflash_config *config = dev->config;
struct flash_flexspi_hyperflash_data *data = dev->data;
uint32_t temp_lut[sizeof(flash_flexspi_hyperflash_lut) / sizeof(uint32_t)];
/* Since the controller variable may be used in critical sections,
* copy the device pointer into a variable stored in RAM
@ -610,27 +609,15 @@ static int flash_flexspi_hyperflash_init(const struct device *dev)
memc_flexspi_wait_bus_idle(&data->controller);
if (!memc_flexspi_is_running_xip(&data->controller) &&
memc_flexspi_set_device_config(&data->controller, &data->config,
data->port)) {
LOG_ERR("Could not set device configuration");
return -EINVAL;
if (memc_flexspi_is_running_xip(&data->controller)) {
/* Wait for bus idle before configuring */
memc_flexspi_wait_bus_idle(&data->controller);
}
/*
* Using the LUT stored in the FlexSPI directly when updating
* the FlexSPI can result in an invalid LUT entry being stored,
* as the LUT itself describes how the FlexSPI should access the flash.
* To resolve this, copy the LUT to a array placed in RAM before
* updating the FlexSPI.
*/
memcpy(temp_lut, flash_flexspi_hyperflash_lut,
sizeof(flash_flexspi_hyperflash_lut));
if (memc_flexspi_update_lut(&data->controller, 0,
(const uint32_t *) temp_lut,
sizeof(temp_lut) / sizeof(uint32_t))) {
LOG_ERR("Could not update lut");
if (memc_flexspi_set_device_config(&data->controller, &data->config,
(const uint32_t *)flash_flexspi_hyperflash_lut,
sizeof(flash_flexspi_hyperflash_lut) / MEMC_FLEXSPI_CMD_SIZE,
data->port)) {
LOG_ERR("Could not set device configuration");
return -EINVAL;
}

View file

@ -512,34 +512,22 @@ static int flash_flexspi_nor_init(const struct device *dev)
{
struct flash_flexspi_nor_data *data = dev->data;
uint8_t vendor_id;
uint32_t temp_lut[sizeof(flash_flexspi_nor_lut) / sizeof(uint32_t)];
if (!device_is_ready(data->controller)) {
LOG_ERR("Controller device not ready");
return -ENODEV;
}
if (!memc_flexspi_is_running_xip(data->controller) &&
memc_flexspi_set_device_config(data->controller, &data->config,
data->port)) {
LOG_ERR("Could not set device configuration");
return -EINVAL;
if (memc_flexspi_is_running_xip(data->controller)) {
/* Wait for bus idle before configuring */
memc_flexspi_wait_bus_idle(data->controller);
}
/*
* Using the LUT stored in the FlexSPI directly when updating
* the FlexSPI can result in an invalid LUT entry being stored,
* as the LUT itself describes how the FlexSPI should access the flash.
* To resolve this, copy the LUT to a array placed in RAM before
* updating the FlexSPI.
*/
memcpy(temp_lut, flash_flexspi_nor_lut,
sizeof(flash_flexspi_nor_lut));
if (memc_flexspi_update_lut(data->controller, 0,
(const uint32_t *) temp_lut,
sizeof(temp_lut) / sizeof(uint32_t))) {
LOG_ERR("Could not update lut");
if (memc_flexspi_set_device_config(data->controller, &data->config,
(const uint32_t *)flash_flexspi_nor_lut,
sizeof(flash_flexspi_nor_lut) / MEMC_FLEXSPI_CMD_SIZE,
data->port)) {
LOG_ERR("Could not set device configuration");
return -EINVAL;
}

View file

@ -506,34 +506,21 @@ static int flash_flexspi_nor_init(const struct device *dev)
{
struct flash_flexspi_nor_data *data = dev->data;
uint8_t vendor_id;
uint32_t temp_lut[sizeof(flash_flexspi_nor_lut) / sizeof(uint32_t)];
if (!device_is_ready(data->controller)) {
LOG_ERR("Controller device is not ready");
return -ENODEV;
}
if (!memc_flexspi_is_running_xip(data->controller) &&
memc_flexspi_set_device_config(data->controller, &data->config,
data->port)) {
LOG_ERR("Could not set device configuration");
return -EINVAL;
if (memc_flexspi_is_running_xip(data->controller)) {
/* Wait for bus idle before configuring */
memc_flexspi_wait_bus_idle(data->controller);
}
/*
* Using the LUT stored in the FlexSPI directly when updating
* the FlexSPI can result in an invalid LUT entry being stored,
* as the LUT itself describes how the FlexSPI should access the flash.
* To resolve this, copy the LUT to a array placed in RAM before
* updating the FlexSPI.
*/
memcpy(temp_lut, flash_flexspi_nor_lut,
sizeof(flash_flexspi_nor_lut));
if (memc_flexspi_update_lut(data->controller, 0,
(const uint32_t *) temp_lut,
sizeof(temp_lut) / sizeof(uint32_t))) {
LOG_ERR("Could not update lut");
if (memc_flexspi_set_device_config(data->controller, &data->config,
(const uint32_t *)flash_flexspi_nor_lut,
sizeof(flash_flexspi_nor_lut) / MEMC_FLEXSPI_CMD_SIZE,
data->port)) {
LOG_ERR("Could not set device configuration");
return -EINVAL;
}

View file

@ -26,6 +26,8 @@
read-while-write hazards. This configuration is not recommended."
#endif
#define FLEXSPI_MAX_LUT 64U
LOG_MODULE_REGISTER(memc_flexspi, CONFIG_MEMC_LOG_LEVEL);
struct memc_flexspi_buf_cfg {
@ -35,6 +37,12 @@ struct memc_flexspi_buf_cfg {
uint16_t buf_size;
} __packed;
/* Structure tracking LUT offset and usage per each port */
struct port_lut {
uint8_t lut_offset;
uint8_t lut_used;
};
/* flexspi device data should be stored in RAM to avoid read-while-write hazards */
struct memc_flexspi_data {
FLEXSPI_Type *base;
@ -49,6 +57,7 @@ struct memc_flexspi_data {
flexspi_read_sample_clock_t rx_sample_clock;
const struct pinctrl_dev_config *pincfg;
size_t size[kFLEXSPI_PortCount];
struct port_lut port_luts[kFLEXSPI_PortCount];
struct memc_flexspi_buf_cfg *buf_cfg;
uint8_t buf_cfg_cnt;
};
@ -68,16 +77,6 @@ bool memc_flexspi_is_running_xip(const struct device *dev)
return data->xip;
}
int memc_flexspi_update_lut(const struct device *dev, uint32_t index,
const uint32_t *cmd, uint32_t count)
{
struct memc_flexspi_data *data = dev->data;
FLEXSPI_UpdateLUT(data->base, index, cmd, count);
return 0;
}
int memc_flexspi_update_clock(const struct device *dev,
flexspi_device_config_t *device_config,
flexspi_port_t port, enum memc_flexspi_clock_t clock)
@ -108,20 +107,65 @@ int memc_flexspi_update_clock(const struct device *dev,
int memc_flexspi_set_device_config(const struct device *dev,
const flexspi_device_config_t *device_config,
const uint32_t *lut_array,
uint8_t lut_count,
flexspi_port_t port)
{
flexspi_device_config_t tmp_config;
uint32_t tmp_lut[FLEXSPI_MAX_LUT];
struct memc_flexspi_data *data = dev->data;
const uint32_t *lut_ptr = lut_array;
uint8_t lut_used = 0U;
unsigned int key = 0;
if (port >= kFLEXSPI_PortCount) {
LOG_ERR("Invalid port number");
return -EINVAL;
}
if (data->port_luts[port].lut_used < lut_count) {
/* We cannot reuse the existing LUT slot,
* Check if the LUT table will fit into the remaining LUT slots
*/
for (uint8_t i = 0; i < kFLEXSPI_PortCount; i++) {
lut_used += data->port_luts[i].lut_used;
}
if ((lut_used + lut_count) > FLEXSPI_MAX_LUT) {
return -ENOBUFS;
}
}
data->size[port] = device_config->flashSize * KB(1);
FLEXSPI_SetFlashConfig(data->base,
(flexspi_device_config_t *) device_config,
port);
if (memc_flexspi_is_running_xip(dev)) {
/* We need to avoid flash access while configuring the FlexSPI.
* To do this, we will copy the LUT array into stack-allocated
* temporary memory
*/
memcpy(tmp_lut, lut_array, lut_count * MEMC_FLEXSPI_CMD_SIZE);
lut_ptr = tmp_lut;
}
memcpy(&tmp_config, device_config, sizeof(tmp_config));
/* Update FlexSPI AWRSEQID and ARDSEQID values based on where the LUT
* array will actually be loaded.
*/
if (data->port_luts[port].lut_used < lut_count) {
/* Update lut offset with new value */
data->port_luts[port].lut_offset = lut_used;
}
data->port_luts[port].lut_used = lut_count;
tmp_config.ARDSeqIndex += data->port_luts[port].lut_offset;
tmp_config.AWRSeqIndex += data->port_luts[port].lut_offset;
/* Lock IRQs before reconfiguring FlexSPI, to prevent XIP */
key = irq_lock();
FLEXSPI_SetFlashConfig(data->base, &tmp_config, port);
FLEXSPI_UpdateLUT(data->base, data->port_luts[port].lut_offset,
lut_ptr, lut_count);
irq_unlock(key);
return 0;
}
@ -139,7 +183,11 @@ int memc_flexspi_transfer(const struct device *dev,
flexspi_transfer_t *transfer)
{
struct memc_flexspi_data *data = dev->data;
status_t status = FLEXSPI_TransferBlocking(data->base, transfer);
status_t status;
/* Adjust transfer LUT index based on port */
transfer->seqIndex += data->port_luts[transfer->port].lut_offset;
status = FLEXSPI_TransferBlocking(data->base, transfer);
if (status != kStatus_Success) {
LOG_ERR("Transfer error: %d", status);
@ -161,7 +209,7 @@ void *memc_flexspi_get_ahb_address(const struct device *dev,
}
for (i = 0; i < port; i++) {
offset += data->size[port];
offset += data->size[i];
}
return data->ahb_base + offset;

View file

@ -1,5 +1,5 @@
/*
* Copyright 2020 NXP
* Copyright 2020,2023 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -15,25 +15,94 @@ enum memc_flexspi_clock_t {
MEMC_FLEXSPI_CLOCK_42M,
};
/* Size of a command in the LUT table */
#define MEMC_FLEXSPI_CMD_SIZE 4U
/**
* @brief Wait for the FlexSPI bus to be idle
*
* Waits for the FlexSPI bus to be idle. Can be used when reconfiguring
* the FlexSPI to make sure no flash access is occurring before changing
* settings.
*
* @param dev: FlexSPI device
*/
void memc_flexspi_wait_bus_idle(const struct device *dev);
/**
* @brief Check if FlexSPI is being used in XIP mode.
*
* Checks if the FlexSPI is being used for code execution in the current
* application.
*
* @param dev: FlexSPI device
* @retval true if FlexSPI being used for XIP
*/
bool memc_flexspi_is_running_xip(const struct device *dev);
int memc_flexspi_update_lut(const struct device *dev, uint32_t index,
const uint32_t *cmd, uint32_t count);
/**
* @brief Update clock selection of the FlexSPI device
*
* Updates clock selection of the FlexSPI device to a new clock speed.
*
* @param dev: FlexSPI device
* @param device_config: External device configuration.
* @param port: FlexSPI port to use for this external device
* @param clock: new clock selection to apply
* @return 0 on success, negative value on failure
*/
int memc_flexspi_update_clock(const struct device *dev,
flexspi_device_config_t *device_config,
flexspi_port_t port, enum memc_flexspi_clock_t clock);
/**
* @brief configure new FlexSPI device
*
* Configures new device on the FlexSPI bus.
* @param dev: FlexSPI device
* @param device_config: External device configuration.
* @param lut_array: Lookup table of FlexSPI flash commands for device
* @param lut_count: number of command entries (16 bytes each) in LUT
* @param port: FlexSPI port to use for this external device
* @return 0 on success, negative value on failure
*/
int memc_flexspi_set_device_config(const struct device *dev,
const flexspi_device_config_t *device_config,
const uint32_t *lut_array,
uint8_t lut_count,
flexspi_port_t port);
/**
* @brief Perform software reset of FlexSPI
*
* Software reset of FlexSPI. Does not clear configuration registers.
* @param dev: FlexSPI device
* @return 0 on success, negative value on failure
*/
int memc_flexspi_reset(const struct device *dev);
/**
* @brief Send blocking IP transfer
*
* Send blocking IP transfer using FlexSPI.
* @param dev: FlexSPI device
* @param transfer: FlexSPI transfer. seqIndex should be set as though the
* LUT array was loaded at offset 0.
* @return 0 on success, negative value on failure
*/
int memc_flexspi_transfer(const struct device *dev,
flexspi_transfer_t *transfer);
/**
* @brief Get AHB address for FlexSPI port
*
* Get AHB address for FlexSPI port. This address is memory mapped, and can be
* read from (and written to, for PSRAM) as though it were internal memory.
* @param dev: FlexSPI device
* @param port: FlexSPI port external device is on
* @param offset: byte offset from start of device to get AHB address for
* @return 0 on success, negative value on failure
*/
void *memc_flexspi_get_ahb_address(const struct device *dev,
flexspi_port_t port, off_t offset);

View file

@ -215,18 +215,13 @@ static int memc_flexspi_aps6408l_init(const struct device *dev)
}
if (memc_flexspi_set_device_config(data->controller, &config->config,
config->port)) {
(const uint32_t *) memc_flexspi_aps6408l_lut,
sizeof(memc_flexspi_aps6408l_lut) / MEMC_FLEXSPI_CMD_SIZE,
config->port)) {
LOG_ERR("Could not set device configuration");
return -EINVAL;
}
if (memc_flexspi_update_lut(data->controller, 0,
(const uint32_t *) memc_flexspi_aps6408l_lut,
sizeof(memc_flexspi_aps6408l_lut) / 4)) {
LOG_ERR("Could not update lut");
return -EINVAL;
}
memc_flexspi_reset(data->controller);
if (memc_flexspi_aps6408l_reset(dev)) {

View file

@ -122,18 +122,13 @@ static int memc_flexspi_s27ks0641_init(const struct device *dev)
}
if (memc_flexspi_set_device_config(data->controller, &config->config,
config->port)) {
(const uint32_t *) memc_flexspi_s27ks0641_lut,
sizeof(memc_flexspi_s27ks0641_lut) / MEMC_FLEXSPI_CMD_SIZE,
config->port)) {
LOG_ERR("Could not set device configuration");
return -EINVAL;
}
if (memc_flexspi_update_lut(data->controller, 0,
(const uint32_t *) memc_flexspi_s27ks0641_lut,
sizeof(memc_flexspi_s27ks0641_lut) / 4)) {
LOG_ERR("Could not update lut");
return -EINVAL;
}
memc_flexspi_reset(data->controller);
if (memc_flexspi_s27ks0641_get_vendor_id(dev, &vendor_id)) {

View file

@ -114,18 +114,13 @@ static int memc_flexspi_w956a8mbya_init(const struct device *dev)
}
if (memc_flexspi_set_device_config(data->controller, &config->config,
config->port)) {
(const uint32_t *) memc_flexspi_w956a8mbya_lut,
sizeof(memc_flexspi_w956a8mbya_lut) / MEMC_FLEXSPI_CMD_SIZE,
config->port)) {
LOG_ERR("Could not set device configuration");
return -EINVAL;
}
if (memc_flexspi_update_lut(data->controller, 0,
(const uint32_t *) memc_flexspi_w956a8mbya_lut,
sizeof(memc_flexspi_w956a8mbya_lut) / 4)) {
LOG_ERR("Could not update lut");
return -EINVAL;
}
memc_flexspi_reset(data->controller);
if (memc_flexspi_w956a8mbya_get_vendor_id(dev, &vendor_id)) {