You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
627 lines
20 KiB
627 lines
20 KiB
/* |
|
* Copyright (c) 2022 Microchip Technology Inc. |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT microchip_mpfs_qspi |
|
|
|
#include <zephyr/device.h> |
|
#include <zephyr/drivers/spi.h> |
|
#include <zephyr/drivers/spi/rtio.h> |
|
#include <zephyr/sys/sys_io.h> |
|
#include <zephyr/sys/util.h> |
|
#include <zephyr/logging/log.h> |
|
#include <zephyr/irq.h> |
|
|
|
LOG_MODULE_REGISTER(mss_qspi, CONFIG_SPI_LOG_LEVEL); |
|
|
|
/* Is MSS QSPI module 'resets' line property defined */ |
|
#define MSS_QSPI_RESET_ENABLED DT_ANY_INST_HAS_PROP_STATUS_OKAY(resets) |
|
|
|
#if MSS_QSPI_RESET_ENABLED |
|
#include <zephyr/drivers/reset.h> |
|
#endif |
|
|
|
#include "spi_context.h" |
|
|
|
/*MSS QSPI Register offsets */ |
|
#define MSS_QSPI_REG_CONTROL (0x00) |
|
#define MSS_QSPI_REG_FRAMES (0x04) |
|
#define MSS_QSPI_REG_IEN (0x0c) |
|
#define MSS_QSPI_REG_STATUS (0x10) |
|
#define MSS_QSPI_REG_DIRECT_ACCESS (0x14) |
|
#define MSS_QSPI_REG_UPPER_ACCESS (0x18) |
|
#define MSS_QSPI_REG_RX_DATA (0x40) |
|
#define MSS_QSPI_REG_TX_DATA (0x44) |
|
#define MSS_QSPI_REG_X4_RX_DATA (0x48) |
|
#define MSS_QSPI_REG_X4_TX_DATA (0x4c) |
|
#define MSS_QSPI_REG_FRAMESUP (0x50) |
|
|
|
/* QSPICR bit definitions */ |
|
#define MSS_QSPI_CONTROL_ENABLE BIT(0) |
|
#define MSS_QSPI_CONTROL_MASTER BIT(1) |
|
#define MSS_QSPI_CONTROL_XIP BIT(2) |
|
#define MSS_QSPI_CONTROL_XIPADDR BIT(3) |
|
#define MSS_QSPI_CONTROL_CLKIDLE BIT(10) |
|
#define MSS_QSPI_CONTROL_SAMPLE_MSK (3 << 11) |
|
#define MSS_QSPI_CONTROL_MODE0 BIT(13) |
|
#define MSS_QSPI_CONTROL_MODE_EXQUAD (0x6 << 13) |
|
#define MSS_QSPI_CONTROL_MODE_EXDUAL (0x2 << 13) |
|
#define MSS_QSPI_CONTROL_MODE12_MSK (3 << 14) |
|
#define MSS_QSPI_CONTROL_FLAGSX4 BIT(16) |
|
#define MSS_QSPI_CONTROL_CLKRATE_MSK (0xf << 24) |
|
#define MSS_QSPI_CONTROL_CLKRATE 24 |
|
|
|
/* QSPIFRAMES bit definitions */ |
|
#define MSS_QSPI_FRAMES_TOTALBYTES_MSK (0xffff << 0) |
|
#define MSS_QSPI_FRAMES_TOTALBYTES_MSK (0xffff << 0) |
|
#define MSS_QSPI_FRAMES_CMDBYTES_MSK (0x1ff << 16) |
|
#define MSS_QSPI_FRAMES_CMDBYTES 16 |
|
#define MSS_QSPI_FRAMES_QSPI BIT(25) |
|
#define MSS_QSPI_FRAMES_IDLE_MSK (0xf << 26) |
|
#define MSS_QSPI_FRAMES_FLAGBYTE BIT(30) |
|
#define MSS_QSPI_FRAMES_FLAGWORD BIT(31) |
|
|
|
/* QSPIIEN bit definitions */ |
|
#define MSS_QSPI_IEN_TXDONE BIT(0) |
|
#define MSS_QSPI_IEN_RXDONE BIT(1) |
|
#define MSS_QSPI_IEN_RXAVAILABLE BIT(2) |
|
#define MSS_QSPI_IEN_TXAVAILABLE BIT(3) |
|
#define MSS_QSPI_IEN_RXFIFOEMPTY BIT(4) |
|
#define MSS_QSPI_IEN_TXFIFOFULL BIT(5) |
|
#define MSS_QSPI_IEN_FLAGSX4 BIT(8) |
|
|
|
/* QSPIST bit definitions */ |
|
#define MSS_QSPI_STATUS_TXDONE BIT(0) |
|
#define MSS_QSPI_STATUS_RXDONE BIT(1) |
|
#define MSS_QSPI_STATUS_RXAVAILABLE BIT(2) |
|
#define MSS_QSPI_STATUS_TXAVAILABLE BIT(3) |
|
#define MSS_QSPI_STATUS_RXFIFOEMPTY BIT(4) |
|
#define MSS_QSPI_STATUS_TXFIFOFULL BIT(5) |
|
#define MSS_QSPI_STATUS_READY BIT(7) |
|
#define MSS_QSPI_STATUS_FLAGSX4 BIT(8) |
|
|
|
/* QSPIDA bit definitions */ |
|
#define MSS_QSPI_DA_EN_SSEL BIT(0) |
|
#define MSS_QSPI_DA_OP_SSEL BIT(1) |
|
#define MSS_QSPI_DA_EN_SCLK BIT(2) |
|
#define MSS_QSPI_DA_OP_SCLK BIT(3) |
|
#define MSS_QSPI_DA_EN_SDO_MSK (0xf << 4) |
|
#define MSS_QSPI_DA_OP_SDO_MSK (0xf << 8) |
|
#define MSS_QSPI_DA_OP_SDATA_MSK (0xf << 12) |
|
#define MSS_QSPI_DA_IP_SDI_MSK (0xf << 16) |
|
#define MSS_QSPI_DA_IP_SCLK BIT(21) |
|
#define MSS_QSPI_DA_IP_SSEL BIT(22) |
|
#define MSS_QSPI_DA_IDLE BIT(23) |
|
#define MSS_QSPI_RXDATA_MSK (0xff << 0) |
|
#define MSS_QSPI_TXDATA_MSK (0xff << 0) |
|
|
|
/* QSPIFRAMESUP bit definitions */ |
|
#define MSS_QSPI_FRAMESUP_UP_BYTES_MSK (0xFFFF << 16) |
|
#define MSS_QSPI_FRAMESUP_LO_BYTES_MSK (0xFFFF << 0) |
|
|
|
/* |
|
* Private data structure for an SPI slave |
|
*/ |
|
struct mss_qspi_config { |
|
mm_reg_t base; |
|
void (*irq_config_func)(const struct device *dev); |
|
int irq; |
|
uint32_t clock_freq; |
|
#if MSS_QSPI_RESET_ENABLED |
|
struct reset_dt_spec reset_spec; |
|
#endif |
|
}; |
|
|
|
/* Device run time data */ |
|
struct mss_qspi_data { |
|
struct spi_context ctx; |
|
}; |
|
|
|
static inline uint32_t mss_qspi_read(const struct mss_qspi_config *cfg, |
|
mm_reg_t offset) |
|
{ |
|
return sys_read32(cfg->base + offset); |
|
} |
|
|
|
static inline void mss_qspi_write(const struct mss_qspi_config *cfg, |
|
uint32_t val, mm_reg_t offset) |
|
{ |
|
sys_write32(val, cfg->base + offset); |
|
} |
|
|
|
static void mss_qspi_enable_ints(const struct mss_qspi_config *s) |
|
{ |
|
uint32_t mask = MSS_QSPI_IEN_TXDONE | |
|
MSS_QSPI_IEN_RXDONE | |
|
MSS_QSPI_IEN_RXAVAILABLE; |
|
|
|
mss_qspi_write(s, mask, MSS_QSPI_REG_IEN); |
|
} |
|
|
|
static void mss_qspi_disable_ints(const struct mss_qspi_config *s) |
|
{ |
|
uint32_t mask = 0; |
|
|
|
mss_qspi_write(s, mask, MSS_QSPI_REG_IEN); |
|
} |
|
|
|
static inline void mss_qspi_transmit_x8(const struct device *dev, uint32_t len) |
|
{ |
|
const struct mss_qspi_config *s = dev->config; |
|
struct mss_qspi_data *data = dev->data; |
|
struct spi_context *ctx = &data->ctx; |
|
uint32_t count, skips; |
|
|
|
skips = mss_qspi_read(s, MSS_QSPI_REG_CONTROL); |
|
skips &= ~MSS_QSPI_CONTROL_FLAGSX4; |
|
mss_qspi_write(s, skips, MSS_QSPI_REG_CONTROL); |
|
for (count = 0; count < len; ++count) { |
|
while (mss_qspi_read(s, MSS_QSPI_REG_STATUS) & MSS_QSPI_STATUS_TXFIFOFULL) { |
|
; |
|
} |
|
if (spi_context_tx_buf_on(ctx)) { |
|
mss_qspi_write(s, ctx->tx_buf[0], MSS_QSPI_REG_TX_DATA); |
|
spi_context_update_tx(ctx, 1, 1); |
|
} |
|
} |
|
} |
|
|
|
static inline void mss_qspi_transmit_x32(const struct device *dev, uint32_t len) |
|
{ |
|
const struct mss_qspi_config *s = dev->config; |
|
struct mss_qspi_data *data = dev->data; |
|
struct spi_context *ctx = &data->ctx; |
|
uint32_t count, ctrl, wdata; |
|
|
|
ctrl = mss_qspi_read(s, MSS_QSPI_REG_CONTROL); |
|
ctrl |= MSS_QSPI_CONTROL_FLAGSX4; |
|
mss_qspi_write(s, ctrl, MSS_QSPI_REG_CONTROL); |
|
for (count = 0; count < len / 4; ++count) { |
|
while (mss_qspi_read(s, MSS_QSPI_REG_STATUS) & MSS_QSPI_STATUS_TXFIFOFULL) { |
|
; |
|
} |
|
if (spi_context_tx_buf_on(ctx)) { |
|
wdata = UNALIGNED_GET((uint32_t *)(ctx->tx_buf)); |
|
mss_qspi_write(s, wdata, MSS_QSPI_REG_X4_TX_DATA); |
|
spi_context_update_tx(ctx, 1, 4); |
|
} |
|
} |
|
} |
|
|
|
static inline void mss_qspi_receive_x32(const struct device *dev, uint32_t len) |
|
{ |
|
const struct mss_qspi_config *s = dev->config; |
|
struct mss_qspi_data *data = dev->data; |
|
struct spi_context *ctx = &data->ctx; |
|
uint32_t count, ctrl, temp; |
|
|
|
ctrl = mss_qspi_read(s, MSS_QSPI_REG_CONTROL); |
|
ctrl |= MSS_QSPI_CONTROL_FLAGSX4; |
|
mss_qspi_write(s, ctrl, MSS_QSPI_REG_CONTROL); |
|
for (count = 0; count < len / 4; ++count) { |
|
while ((mss_qspi_read(s, MSS_QSPI_REG_STATUS) & MSS_QSPI_STATUS_RXFIFOEMPTY)) { |
|
; |
|
} |
|
if (spi_context_rx_buf_on(ctx)) { |
|
temp = mss_qspi_read(s, MSS_QSPI_REG_X4_RX_DATA); |
|
UNALIGNED_PUT(temp, (uint32_t *)ctx->rx_buf); |
|
spi_context_update_rx(ctx, 1, 4); |
|
} |
|
} |
|
} |
|
|
|
static inline void mss_qspi_receive_x8(const struct device *dev, uint32_t len) |
|
{ |
|
const struct mss_qspi_config *s = dev->config; |
|
struct mss_qspi_data *data = dev->data; |
|
struct spi_context *ctx = &data->ctx; |
|
uint32_t rdata, count; |
|
|
|
rdata = mss_qspi_read(s, MSS_QSPI_REG_CONTROL); |
|
rdata &= ~MSS_QSPI_CONTROL_FLAGSX4; |
|
mss_qspi_write(s, rdata, MSS_QSPI_REG_CONTROL); |
|
for (count = 0; count < len; ++count) { |
|
while (mss_qspi_read(s, MSS_QSPI_REG_STATUS) & MSS_QSPI_STATUS_RXFIFOEMPTY) { |
|
; |
|
} |
|
if (spi_context_rx_buf_on(ctx)) { |
|
rdata = mss_qspi_read(s, MSS_QSPI_REG_RX_DATA); |
|
UNALIGNED_PUT(rdata, (uint8_t *)ctx->rx_buf); |
|
spi_context_update_rx(ctx, 1, 1); |
|
} |
|
} |
|
} |
|
|
|
static inline void mss_qspi_config_frames(const struct device *dev, |
|
uint32_t total_bytes, |
|
uint32_t cmd_bytes, bool x8) |
|
{ |
|
const struct mss_qspi_config *s = dev->config; |
|
uint32_t skips; |
|
|
|
mss_qspi_write(s, (total_bytes & MSS_QSPI_FRAMESUP_UP_BYTES_MSK), |
|
MSS_QSPI_REG_FRAMESUP); |
|
skips = (total_bytes & MSS_QSPI_FRAMESUP_LO_BYTES_MSK); |
|
if (cmd_bytes) { |
|
skips |= ((cmd_bytes << MSS_QSPI_FRAMES_CMDBYTES) & MSS_QSPI_FRAMES_CMDBYTES_MSK); |
|
} else { |
|
skips |= ((total_bytes << MSS_QSPI_FRAMES_CMDBYTES) & MSS_QSPI_FRAMES_CMDBYTES_MSK); |
|
} |
|
if (mss_qspi_read(s, MSS_QSPI_REG_CONTROL) & MSS_QSPI_CONTROL_MODE0) { |
|
skips |= MSS_QSPI_FRAMES_QSPI; |
|
} |
|
|
|
skips &= ~MSS_QSPI_FRAMES_IDLE_MSK; |
|
if (x8) { |
|
skips |= MSS_QSPI_FRAMES_FLAGBYTE; |
|
} else { |
|
skips |= MSS_QSPI_FRAMES_FLAGWORD; |
|
} |
|
|
|
mss_qspi_write(s, skips, MSS_QSPI_REG_FRAMES); |
|
} |
|
|
|
static inline void mss_qspi_transmit(const struct device *dev) |
|
{ |
|
const struct mss_qspi_config *s = dev->config; |
|
struct mss_qspi_data *data = dev->data; |
|
struct spi_context *ctx = &data->ctx; |
|
uint32_t total_byte_cnt, cmd_bytes; |
|
|
|
cmd_bytes = spi_context_longest_current_buf(ctx); |
|
total_byte_cnt = spi_context_total_tx_len(ctx); |
|
|
|
/* |
|
* As per the MSS QSPI IP spec, |
|
* The number of command and data bytes are controlled by the frames register |
|
* for each SPI sequence. This supports the SPI flash memory read and writes |
|
* sequences as below. so configure the cmd and total bytes accordingly. |
|
* --------------------------------------------------------------------- |
|
* TOTAL BYTES | CMD BYTES | What happens | |
|
* ______________________________________________________________________ |
|
* | | | |
|
* 1 | 1 | The SPI core will transmit a single byte | |
|
* | | and receive data is discarded | |
|
* | | | |
|
* 1 | 0 | The SPI core will transmit a single byte | |
|
* | | and return a single byte | |
|
* | | | |
|
* 10 | 4 | The SPI core will transmit 4 command | |
|
* | | bytes discarding the receive data and | |
|
* | | transmits 6 dummy bytes returning the 6 | |
|
* | | received bytes and return a single byte | |
|
* | | | |
|
* 10 | 10 | The SPI core will transmit 10 command | |
|
* | | | |
|
* 10 | 0 | The SPI core will transmit 10 command | |
|
* | | bytes and returning 10 received bytes | |
|
* ______________________________________________________________________ |
|
*/ |
|
if (!ctx->rx_buf) { |
|
if (total_byte_cnt - cmd_bytes) { |
|
mss_qspi_config_frames(dev, total_byte_cnt, 0, false); |
|
mss_qspi_transmit_x8(dev, cmd_bytes); |
|
mss_qspi_transmit_x32(dev, (total_byte_cnt - cmd_bytes)); |
|
|
|
} else { |
|
mss_qspi_config_frames(dev, total_byte_cnt, cmd_bytes, true); |
|
mss_qspi_transmit_x8(dev, cmd_bytes); |
|
} |
|
} else { |
|
mss_qspi_config_frames(dev, total_byte_cnt, cmd_bytes, true); |
|
mss_qspi_transmit_x8(dev, cmd_bytes); |
|
} |
|
|
|
mss_qspi_enable_ints(s); |
|
} |
|
|
|
static inline void mss_qspi_receive(const struct device *dev) |
|
{ |
|
const struct mss_qspi_config *s = dev->config; |
|
struct mss_qspi_data *data = dev->data; |
|
struct spi_context *ctx = &data->ctx; |
|
uint32_t rd_bytes, skips, idx, rdata; |
|
|
|
/* |
|
* Point the rx buffer where the actual read data |
|
* will be stored |
|
*/ |
|
spi_context_update_rx(ctx, 1, ctx->rx_len); |
|
|
|
rd_bytes = spi_context_longest_current_buf(ctx); |
|
if (rd_bytes) { |
|
if (rd_bytes >= 4) { |
|
mss_qspi_receive_x32(dev, rd_bytes); |
|
} |
|
|
|
skips = mss_qspi_read(s, MSS_QSPI_REG_CONTROL); |
|
skips &= ~MSS_QSPI_CONTROL_FLAGSX4; |
|
mss_qspi_write(s, skips, MSS_QSPI_REG_CONTROL); |
|
idx = (rd_bytes - (rd_bytes % 4u)); |
|
for (; idx < rd_bytes; ++idx) { |
|
while (mss_qspi_read(s, MSS_QSPI_REG_STATUS) & |
|
MSS_QSPI_STATUS_RXFIFOEMPTY) { |
|
; |
|
} |
|
if (spi_context_rx_buf_on(ctx)) { |
|
rdata = mss_qspi_read(s, MSS_QSPI_REG_RX_DATA); |
|
UNALIGNED_PUT(rdata, (uint8_t *)ctx->rx_buf); |
|
spi_context_update_rx(ctx, 1, 1); |
|
} |
|
} |
|
} |
|
} |
|
|
|
static inline int mss_qspi_clk_gen_set(const struct mss_qspi_config *s, |
|
const struct spi_config *spi_cfg) |
|
{ |
|
uint32_t control = mss_qspi_read(s, MSS_QSPI_REG_CONTROL); |
|
uint32_t idx, clkrate, val = 0, speed; |
|
|
|
if (spi_cfg->frequency > s->clock_freq) { |
|
speed = s->clock_freq / 2; |
|
} |
|
|
|
for (idx = 1; idx < 16; idx++) { |
|
clkrate = s->clock_freq / (2 * idx); |
|
if (clkrate <= spi_cfg->frequency) { |
|
val = idx; |
|
break; |
|
} |
|
} |
|
|
|
if (val) { |
|
control = mss_qspi_read(s, MSS_QSPI_REG_CONTROL); |
|
control &= ~MSS_QSPI_CONTROL_CLKRATE_MSK; |
|
control |= (val << MSS_QSPI_CONTROL_CLKRATE); |
|
mss_qspi_write(s, control, MSS_QSPI_REG_CONTROL); |
|
} else { |
|
return -1; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static inline int mss_qspi_hw_mode_set(const struct mss_qspi_config *s, |
|
uint16_t mode) |
|
{ |
|
uint32_t ctrl = mss_qspi_read(s, MSS_QSPI_REG_CONTROL); |
|
|
|
if ((mode & SPI_MODE_CPHA) && (mode & SPI_MODE_CPOL)) { |
|
/* mode 3 */ |
|
ctrl |= MSS_QSPI_CONTROL_CLKIDLE; |
|
} else if (!(mode & SPI_MODE_CPHA) && !(mode & SPI_MODE_CPOL)) { |
|
/* mode 0 */ |
|
ctrl &= ~MSS_QSPI_CONTROL_CLKIDLE; |
|
} else { |
|
return -1; |
|
} |
|
|
|
if ((mode & SPI_LINES_QUAD)) { |
|
/* Quad mode */ |
|
ctrl &= ~(MSS_QSPI_CONTROL_MODE0); |
|
ctrl |= (MSS_QSPI_CONTROL_MODE_EXQUAD); |
|
} else if ((mode & SPI_LINES_DUAL)) { |
|
/* Dual mode */ |
|
ctrl &= ~(MSS_QSPI_CONTROL_MODE0); |
|
ctrl |= (MSS_QSPI_CONTROL_MODE_EXDUAL); |
|
} else { |
|
/* Normal mode */ |
|
ctrl &= ~(MSS_QSPI_CONTROL_MODE0); |
|
} |
|
|
|
mss_qspi_write(s, ctrl, MSS_QSPI_REG_CONTROL); |
|
|
|
return 0; |
|
} |
|
|
|
static int mss_qspi_release(const struct device *dev, |
|
const struct spi_config *config) |
|
{ |
|
struct mss_qspi_data *data = dev->data; |
|
const struct mss_qspi_config *cfg = dev->config; |
|
uint32_t control = mss_qspi_read(cfg, MSS_QSPI_REG_CONTROL); |
|
|
|
mss_qspi_disable_ints(cfg); |
|
|
|
control &= ~MSS_QSPI_CONTROL_ENABLE; |
|
mss_qspi_write(cfg, control, MSS_QSPI_REG_CONTROL); |
|
|
|
spi_context_unlock_unconditionally(&data->ctx); |
|
|
|
return 0; |
|
} |
|
|
|
static void mss_qspi_interrupt(const struct device *dev) |
|
{ |
|
const struct mss_qspi_config *cfg = dev->config; |
|
struct mss_qspi_data *data = dev->data; |
|
struct spi_context *ctx = &data->ctx; |
|
|
|
int intfield = mss_qspi_read(cfg, MSS_QSPI_REG_STATUS); |
|
int ienfield = mss_qspi_read(cfg, MSS_QSPI_REG_IEN); |
|
|
|
if ((intfield & ienfield) == 0) { |
|
return; |
|
} |
|
|
|
if (intfield & MSS_QSPI_IEN_TXDONE) { |
|
mss_qspi_write(cfg, MSS_QSPI_IEN_TXDONE, MSS_QSPI_REG_STATUS); |
|
} |
|
|
|
if (intfield & MSS_QSPI_IEN_RXAVAILABLE) { |
|
mss_qspi_write(cfg, MSS_QSPI_IEN_RXAVAILABLE, MSS_QSPI_REG_STATUS); |
|
mss_qspi_receive(dev); |
|
} |
|
|
|
if ((intfield & MSS_QSPI_IEN_RXDONE)) { |
|
mss_qspi_write(cfg, MSS_QSPI_IEN_RXDONE, MSS_QSPI_REG_STATUS); |
|
spi_context_complete(ctx, dev, 0); |
|
} |
|
|
|
if (intfield & MSS_QSPI_IEN_TXAVAILABLE) { |
|
mss_qspi_write(cfg, MSS_QSPI_IEN_TXAVAILABLE, MSS_QSPI_REG_STATUS); |
|
} |
|
|
|
if (intfield & MSS_QSPI_IEN_RXFIFOEMPTY) { |
|
mss_qspi_write(cfg, MSS_QSPI_IEN_RXFIFOEMPTY, MSS_QSPI_REG_STATUS); |
|
} |
|
|
|
if (intfield & MSS_QSPI_IEN_TXFIFOFULL) { |
|
mss_qspi_write(cfg, MSS_QSPI_IEN_TXFIFOFULL, MSS_QSPI_REG_STATUS); |
|
} |
|
} |
|
|
|
static int mss_qspi_configure(const struct device *dev, |
|
const struct spi_config *spi_cfg) |
|
{ |
|
const struct mss_qspi_config *cfg = dev->config; |
|
|
|
if (spi_cfg->operation & SPI_OP_MODE_SLAVE) { |
|
LOG_ERR("Slave mode is not supported\n\r"); |
|
return -ENOTSUP; |
|
} |
|
|
|
if (spi_cfg->operation & SPI_MODE_LOOP) { |
|
LOG_ERR("Loop back mode is not supported\n\r"); |
|
return -ENOTSUP; |
|
} |
|
|
|
if (spi_cfg->operation & (SPI_TRANSFER_LSB) || |
|
((IS_ENABLED(CONFIG_SPI_EXTENDED_MODES) && |
|
(spi_cfg->operation & (SPI_LINES_DUAL | |
|
SPI_LINES_QUAD | |
|
SPI_LINES_OCTAL))))) { |
|
LOG_ERR("Unsupported configuration\n\r"); |
|
return -ENOTSUP; |
|
} |
|
|
|
if (mss_qspi_clk_gen_set(cfg, spi_cfg)) { |
|
LOG_ERR("can't set clk divider\n"); |
|
return -EINVAL; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int mss_qspi_transceive(const struct device *dev, |
|
const struct spi_config *spi_cfg, |
|
const struct spi_buf_set *tx_bufs, |
|
const struct spi_buf_set *rx_bufs, |
|
bool async, |
|
spi_callback_t cb, |
|
void *userdata) |
|
{ |
|
const struct mss_qspi_config *config = dev->config; |
|
struct mss_qspi_data *data = dev->data; |
|
struct spi_context *ctx = &data->ctx; |
|
int ret = 0; |
|
|
|
spi_context_lock(ctx, async, cb, userdata, spi_cfg); |
|
ret = mss_qspi_configure(dev, spi_cfg); |
|
if (ret) { |
|
goto out; |
|
} |
|
|
|
mss_qspi_hw_mode_set(config, spi_cfg->operation); |
|
spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, |
|
1); |
|
mss_qspi_transmit(dev); |
|
ret = spi_context_wait_for_completion(ctx); |
|
out: |
|
spi_context_release(ctx, ret); |
|
mss_qspi_disable_ints(config); |
|
|
|
return ret; |
|
} |
|
|
|
static int mss_qspi_transceive_blocking(const struct device *dev, |
|
const struct spi_config *spi_cfg, |
|
const struct spi_buf_set *tx_bufs, |
|
const struct spi_buf_set *rx_bufs) |
|
{ |
|
return mss_qspi_transceive(dev, spi_cfg, tx_bufs, rx_bufs, false, |
|
NULL, NULL); |
|
} |
|
|
|
#ifdef CONFIG_SPI_ASYNC |
|
static int mss_qspi_transceive_async(const struct device *dev, |
|
const struct spi_config *spi_cfg, |
|
const struct spi_buf_set *tx_bufs, |
|
const struct spi_buf_set *rx_bufs, |
|
spi_callback_t cb, |
|
void *userdata) |
|
{ |
|
return mss_qspi_transceive(dev, spi_cfg, tx_bufs, rx_bufs, true, |
|
cb, userdata); |
|
} |
|
#endif /* CONFIG_SPI_ASYNC */ |
|
|
|
static int mss_qspi_init(const struct device *dev) |
|
{ |
|
const struct mss_qspi_config *cfg = dev->config; |
|
struct mss_qspi_data *data = dev->data; |
|
unsigned int ret = 0; |
|
uint32_t control = 0; |
|
|
|
#if MSS_QSPI_RESET_ENABLED |
|
if (cfg->reset_spec.dev != NULL) { |
|
(void)reset_line_deassert_dt(&cfg->reset_spec); |
|
} |
|
#endif |
|
|
|
cfg->irq_config_func(dev); |
|
|
|
control &= ~(MSS_QSPI_CONTROL_SAMPLE_MSK); |
|
control &= ~(MSS_QSPI_CONTROL_MODE0); |
|
control |= (MSS_QSPI_CONTROL_CLKRATE_MSK); |
|
control &= ~(MSS_QSPI_CONTROL_XIP); |
|
control |= (MSS_QSPI_CONTROL_CLKIDLE | MSS_QSPI_CONTROL_ENABLE); |
|
mss_qspi_write(cfg, control, MSS_QSPI_REG_CONTROL); |
|
mss_qspi_disable_ints(cfg); |
|
spi_context_unlock_unconditionally(&data->ctx); |
|
|
|
return ret; |
|
} |
|
|
|
static DEVICE_API(spi, mss_qspi_driver_api) = { |
|
.transceive = mss_qspi_transceive_blocking, |
|
#ifdef CONFIG_SPI_ASYNC |
|
.transceive_async = mss_qspi_transceive_async, |
|
#endif /* CONFIG_SPI_ASYNC */ |
|
#ifdef CONFIG_SPI_RTIO |
|
.iodev_submit = spi_rtio_iodev_default_submit, |
|
#endif |
|
.release = mss_qspi_release, |
|
}; |
|
|
|
#define MSS_QSPI_INIT(n) \ |
|
static void mss_qspi_config_func_##n(const struct device *dev); \ |
|
\ |
|
static const struct mss_qspi_config mss_qspi_config_##n = { \ |
|
.base = DT_INST_REG_ADDR(n), \ |
|
.irq_config_func = mss_qspi_config_func_##n, \ |
|
.clock_freq = DT_INST_PROP(n, clock_frequency), \ |
|
IF_ENABLED(DT_INST_NODE_HAS_PROP(n, resets), \ |
|
(.reset_spec = RESET_DT_SPEC_INST_GET(n),)) \ |
|
}; \ |
|
\ |
|
static struct mss_qspi_data mss_qspi_data_##n = { \ |
|
SPI_CONTEXT_INIT_LOCK(mss_qspi_data_##n, ctx), \ |
|
SPI_CONTEXT_INIT_SYNC(mss_qspi_data_##n, ctx), \ |
|
}; \ |
|
\ |
|
SPI_DEVICE_DT_INST_DEFINE(n, mss_qspi_init, NULL, &mss_qspi_data_##n, \ |
|
&mss_qspi_config_##n, POST_KERNEL, \ |
|
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &mss_qspi_driver_api); \ |
|
\ |
|
static void mss_qspi_config_func_##n(const struct device *dev) \ |
|
{ \ |
|
IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), mss_qspi_interrupt, \ |
|
DEVICE_DT_INST_GET(n), 0); \ |
|
irq_enable(DT_INST_IRQN(n)); \ |
|
} |
|
|
|
DT_INST_FOREACH_STATUS_OKAY(MSS_QSPI_INIT)
|
|
|