806 lines
20 KiB
C
806 lines
20 KiB
C
/*
|
|
* Copyright (c) 2015 Intel Corporation.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT snps_designware_spi
|
|
|
|
/* spi_dw.c - Designware SPI driver implementation */
|
|
|
|
#define LOG_LEVEL CONFIG_SPI_LOG_LEVEL
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(spi_dw);
|
|
|
|
#if (CONFIG_SPI_LOG_LEVEL == 4)
|
|
#define DBG_COUNTER_INIT() \
|
|
uint32_t __cnt = 0
|
|
#define DBG_COUNTER_INC() \
|
|
(__cnt++)
|
|
#define DBG_COUNTER_RESULT() \
|
|
(__cnt)
|
|
#else
|
|
#define DBG_COUNTER_INIT() {; }
|
|
#define DBG_COUNTER_INC() {; }
|
|
#define DBG_COUNTER_RESULT() 0
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/arch/cpu.h>
|
|
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/init.h>
|
|
#include <zephyr/pm/device.h>
|
|
|
|
#include <zephyr/sys/sys_io.h>
|
|
#include <zephyr/sys/util.h>
|
|
|
|
#ifdef CONFIG_IOAPIC
|
|
#include <zephyr/drivers/interrupt_controller/ioapic.h>
|
|
#endif
|
|
|
|
#include <zephyr/drivers/spi.h>
|
|
#include <zephyr/irq.h>
|
|
|
|
#include "spi_dw.h"
|
|
#include "spi_context.h"
|
|
|
|
static inline bool spi_dw_is_slave(struct spi_dw_data *spi)
|
|
{
|
|
return (IS_ENABLED(CONFIG_SPI_SLAVE) &&
|
|
spi_context_is_slave(&spi->ctx));
|
|
}
|
|
|
|
static void completed(const struct device *dev, int error)
|
|
{
|
|
const struct spi_dw_config *info = dev->config;
|
|
struct spi_dw_data *spi = dev->data;
|
|
|
|
if (error) {
|
|
goto out;
|
|
}
|
|
|
|
if (spi_context_tx_on(&spi->ctx) ||
|
|
spi_context_rx_on(&spi->ctx)) {
|
|
return;
|
|
}
|
|
|
|
out:
|
|
/* need to give time for FIFOs to drain before issuing more commands */
|
|
while (test_bit_sr_busy(info->regs)) {
|
|
}
|
|
|
|
/* Disabling interrupts */
|
|
write_imr(DW_SPI_IMR_MASK, info->regs);
|
|
/* Disabling the controller */
|
|
clear_bit_ssienr(info->regs);
|
|
|
|
spi_context_cs_control(&spi->ctx, false);
|
|
|
|
LOG_DBG("SPI transaction completed %s error",
|
|
error ? "with" : "without");
|
|
|
|
spi_context_complete(&spi->ctx, dev, error);
|
|
}
|
|
|
|
static void push_data(const struct device *dev)
|
|
{
|
|
const struct spi_dw_config *info = dev->config;
|
|
struct spi_dw_data *spi = dev->data;
|
|
uint32_t data = 0U;
|
|
uint32_t f_tx;
|
|
|
|
DBG_COUNTER_INIT();
|
|
|
|
if (spi_context_rx_on(&spi->ctx)) {
|
|
f_tx = DW_SPI_FIFO_DEPTH - read_txflr(info->regs) -
|
|
read_rxflr(info->regs);
|
|
if ((int)f_tx < 0) {
|
|
f_tx = 0U; /* if rx-fifo is full, hold off tx */
|
|
}
|
|
} else {
|
|
f_tx = DW_SPI_FIFO_DEPTH - read_txflr(info->regs);
|
|
}
|
|
|
|
while (f_tx) {
|
|
if (spi_context_tx_buf_on(&spi->ctx)) {
|
|
switch (spi->dfs) {
|
|
case 1:
|
|
data = UNALIGNED_GET((uint8_t *)
|
|
(spi->ctx.tx_buf));
|
|
break;
|
|
case 2:
|
|
data = UNALIGNED_GET((uint16_t *)
|
|
(spi->ctx.tx_buf));
|
|
break;
|
|
#ifndef CONFIG_ARC
|
|
case 4:
|
|
data = UNALIGNED_GET((uint32_t *)
|
|
(spi->ctx.tx_buf));
|
|
break;
|
|
#endif
|
|
}
|
|
} else if (spi_context_rx_on(&spi->ctx)) {
|
|
/* No need to push more than necessary */
|
|
if ((int)(spi->ctx.rx_len - spi->fifo_diff) <= 0) {
|
|
break;
|
|
}
|
|
|
|
data = 0U;
|
|
} else if (spi_context_tx_on(&spi->ctx)) {
|
|
data = 0U;
|
|
} else {
|
|
/* Nothing to push anymore */
|
|
break;
|
|
}
|
|
|
|
write_dr(data, info->regs);
|
|
|
|
spi_context_update_tx(&spi->ctx, spi->dfs, 1);
|
|
spi->fifo_diff++;
|
|
|
|
f_tx--;
|
|
|
|
DBG_COUNTER_INC();
|
|
}
|
|
|
|
if (!spi_context_tx_on(&spi->ctx)) {
|
|
/* prevents any further interrupts demanding TX fifo fill */
|
|
write_txftlr(0, info->regs);
|
|
}
|
|
|
|
LOG_DBG("Pushed: %d", DBG_COUNTER_RESULT());
|
|
}
|
|
|
|
static void pull_data(const struct device *dev)
|
|
{
|
|
const struct spi_dw_config *info = dev->config;
|
|
struct spi_dw_data *spi = dev->data;
|
|
|
|
DBG_COUNTER_INIT();
|
|
|
|
while (read_rxflr(info->regs)) {
|
|
uint32_t data = read_dr(info->regs);
|
|
|
|
DBG_COUNTER_INC();
|
|
|
|
if (spi_context_rx_buf_on(&spi->ctx)) {
|
|
switch (spi->dfs) {
|
|
case 1:
|
|
UNALIGNED_PUT(data, (uint8_t *)spi->ctx.rx_buf);
|
|
break;
|
|
case 2:
|
|
UNALIGNED_PUT(data, (uint16_t *)spi->ctx.rx_buf);
|
|
break;
|
|
#ifndef CONFIG_ARC
|
|
case 4:
|
|
UNALIGNED_PUT(data, (uint32_t *)spi->ctx.rx_buf);
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
spi_context_update_rx(&spi->ctx, spi->dfs, 1);
|
|
spi->fifo_diff--;
|
|
}
|
|
|
|
if (!spi->ctx.rx_len && spi->ctx.tx_len < DW_SPI_FIFO_DEPTH) {
|
|
write_rxftlr(spi->ctx.tx_len - 1, info->regs);
|
|
} else if (read_rxftlr(info->regs) >= spi->ctx.rx_len) {
|
|
write_rxftlr(spi->ctx.rx_len - 1, info->regs);
|
|
}
|
|
|
|
LOG_DBG("Pulled: %d", DBG_COUNTER_RESULT());
|
|
}
|
|
|
|
static int spi_dw_configure(const struct spi_dw_config *info,
|
|
struct spi_dw_data *spi,
|
|
const struct spi_config *config)
|
|
{
|
|
uint32_t ctrlr0 = 0U;
|
|
|
|
LOG_DBG("%p (prev %p)", config, spi->ctx.config);
|
|
|
|
if (spi_context_configured(&spi->ctx, config)) {
|
|
/* Nothing to do */
|
|
return 0;
|
|
}
|
|
|
|
if (config->operation & SPI_HALF_DUPLEX) {
|
|
LOG_ERR("Half-duplex not supported");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/* Verify if requested op mode is relevant to this controller */
|
|
if (config->operation & SPI_OP_MODE_SLAVE) {
|
|
if (!(info->op_modes & SPI_CTX_RUNTIME_OP_MODE_SLAVE)) {
|
|
LOG_ERR("Slave mode not supported");
|
|
return -ENOTSUP;
|
|
}
|
|
} else {
|
|
if (!(info->op_modes & SPI_CTX_RUNTIME_OP_MODE_MASTER)) {
|
|
LOG_ERR("Master mode not supported");
|
|
return -ENOTSUP;
|
|
}
|
|
}
|
|
|
|
if ((config->operation & SPI_TRANSFER_LSB) ||
|
|
(IS_ENABLED(CONFIG_SPI_EXTENDED_MODES) &&
|
|
(config->operation & (SPI_LINES_DUAL |
|
|
SPI_LINES_QUAD | SPI_LINES_OCTAL)))) {
|
|
LOG_ERR("Unsupported configuration");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Word size */
|
|
ctrlr0 |= DW_SPI_CTRLR0_DFS(SPI_WORD_SIZE_GET(config->operation));
|
|
|
|
/* Determine how many bytes are required per-frame */
|
|
spi->dfs = SPI_WS_TO_DFS(SPI_WORD_SIZE_GET(config->operation));
|
|
|
|
/* SPI mode */
|
|
if (SPI_MODE_GET(config->operation) & SPI_MODE_CPOL) {
|
|
ctrlr0 |= DW_SPI_CTRLR0_SCPOL;
|
|
}
|
|
|
|
if (SPI_MODE_GET(config->operation) & SPI_MODE_CPHA) {
|
|
ctrlr0 |= DW_SPI_CTRLR0_SCPH;
|
|
}
|
|
|
|
if (SPI_MODE_GET(config->operation) & SPI_MODE_LOOP) {
|
|
ctrlr0 |= DW_SPI_CTRLR0_SRL;
|
|
}
|
|
|
|
/* Installing the configuration */
|
|
write_ctrlr0(ctrlr0, info->regs);
|
|
|
|
/* At this point, it's mandatory to set this on the context! */
|
|
spi->ctx.config = config;
|
|
|
|
if (!spi_dw_is_slave(spi)) {
|
|
/* Baud rate and Slave select, for master only */
|
|
write_baudr(SPI_DW_CLK_DIVIDER(info->clock_frequency,
|
|
config->frequency), info->regs);
|
|
write_ser(1 << config->slave, info->regs);
|
|
}
|
|
|
|
if (spi_dw_is_slave(spi)) {
|
|
LOG_DBG("Installed slave config %p:"
|
|
" ws/dfs %u/%u, mode %u/%u/%u",
|
|
config,
|
|
SPI_WORD_SIZE_GET(config->operation), spi->dfs,
|
|
(SPI_MODE_GET(config->operation) &
|
|
SPI_MODE_CPOL) ? 1 : 0,
|
|
(SPI_MODE_GET(config->operation) &
|
|
SPI_MODE_CPHA) ? 1 : 0,
|
|
(SPI_MODE_GET(config->operation) &
|
|
SPI_MODE_LOOP) ? 1 : 0);
|
|
} else {
|
|
LOG_DBG("Installed master config %p: freq %uHz (div = %u),"
|
|
" ws/dfs %u/%u, mode %u/%u/%u, slave %u",
|
|
config, config->frequency,
|
|
SPI_DW_CLK_DIVIDER(info->clock_frequency,
|
|
config->frequency),
|
|
SPI_WORD_SIZE_GET(config->operation), spi->dfs,
|
|
(SPI_MODE_GET(config->operation) &
|
|
SPI_MODE_CPOL) ? 1 : 0,
|
|
(SPI_MODE_GET(config->operation) &
|
|
SPI_MODE_CPHA) ? 1 : 0,
|
|
(SPI_MODE_GET(config->operation) &
|
|
SPI_MODE_LOOP) ? 1 : 0,
|
|
config->slave);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t spi_dw_compute_ndf(const struct spi_buf *rx_bufs,
|
|
size_t rx_count, uint8_t dfs)
|
|
{
|
|
uint32_t len = 0U;
|
|
|
|
for (; rx_count; rx_bufs++, rx_count--) {
|
|
if (len > (UINT16_MAX - rx_bufs->len)) {
|
|
goto error;
|
|
}
|
|
|
|
len += rx_bufs->len;
|
|
}
|
|
|
|
if (len) {
|
|
return (len / dfs) - 1;
|
|
}
|
|
error:
|
|
return UINT32_MAX;
|
|
}
|
|
|
|
static void spi_dw_update_txftlr(const struct spi_dw_config *info,
|
|
struct spi_dw_data *spi)
|
|
{
|
|
uint32_t reg_data = DW_SPI_TXFTLR_DFLT;
|
|
|
|
if (spi_dw_is_slave(spi)) {
|
|
if (!spi->ctx.tx_len) {
|
|
reg_data = 0U;
|
|
} else if (spi->ctx.tx_len < DW_SPI_TXFTLR_DFLT) {
|
|
reg_data = spi->ctx.tx_len - 1;
|
|
}
|
|
}
|
|
|
|
LOG_DBG("TxFTLR: %u", reg_data);
|
|
|
|
write_txftlr(reg_data, info->regs);
|
|
}
|
|
|
|
static int transceive(const struct device *dev,
|
|
const struct spi_config *config,
|
|
const struct spi_buf_set *tx_bufs,
|
|
const struct spi_buf_set *rx_bufs,
|
|
bool asynchronous,
|
|
spi_callback_t cb,
|
|
void *userdata)
|
|
{
|
|
const struct spi_dw_config *info = dev->config;
|
|
struct spi_dw_data *spi = dev->data;
|
|
uint32_t tmod = DW_SPI_CTRLR0_TMOD_TX_RX;
|
|
uint32_t reg_data;
|
|
int ret;
|
|
|
|
spi_context_lock(&spi->ctx, asynchronous, cb, userdata, config);
|
|
|
|
#ifdef CONFIG_PM_DEVICE
|
|
if (!pm_device_is_busy(dev)) {
|
|
pm_device_busy_set(dev);
|
|
}
|
|
#endif /* CONFIG_PM_DEVICE */
|
|
|
|
/* Configure */
|
|
ret = spi_dw_configure(info, spi, config);
|
|
if (ret) {
|
|
goto out;
|
|
}
|
|
|
|
if (!rx_bufs || !rx_bufs->buffers) {
|
|
tmod = DW_SPI_CTRLR0_TMOD_TX;
|
|
} else if (!tx_bufs || !tx_bufs->buffers) {
|
|
tmod = DW_SPI_CTRLR0_TMOD_RX;
|
|
}
|
|
|
|
/* ToDo: add a way to determine EEPROM mode */
|
|
|
|
if (tmod >= DW_SPI_CTRLR0_TMOD_RX &&
|
|
!spi_dw_is_slave(spi)) {
|
|
reg_data = spi_dw_compute_ndf(rx_bufs->buffers,
|
|
rx_bufs->count,
|
|
spi->dfs);
|
|
if (reg_data == UINT32_MAX) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
write_ctrlr1(reg_data, info->regs);
|
|
} else {
|
|
write_ctrlr1(0, info->regs);
|
|
}
|
|
|
|
if (spi_dw_is_slave(spi)) {
|
|
/* Enabling MISO line relevantly */
|
|
if (tmod == DW_SPI_CTRLR0_TMOD_RX) {
|
|
tmod |= DW_SPI_CTRLR0_SLV_OE;
|
|
} else {
|
|
tmod &= ~DW_SPI_CTRLR0_SLV_OE;
|
|
}
|
|
}
|
|
|
|
/* Updating TMOD in CTRLR0 register */
|
|
reg_data = read_ctrlr0(info->regs);
|
|
reg_data &= ~DW_SPI_CTRLR0_TMOD_RESET;
|
|
reg_data |= tmod;
|
|
|
|
write_ctrlr0(reg_data, info->regs);
|
|
|
|
/* Set buffers info */
|
|
spi_context_buffers_setup(&spi->ctx, tx_bufs, rx_bufs, spi->dfs);
|
|
|
|
spi->fifo_diff = 0U;
|
|
|
|
/* Tx Threshold */
|
|
spi_dw_update_txftlr(info, spi);
|
|
|
|
/* Does Rx thresholds needs to be lower? */
|
|
reg_data = DW_SPI_RXFTLR_DFLT;
|
|
|
|
if (spi_dw_is_slave(spi)) {
|
|
if (spi->ctx.rx_len &&
|
|
spi->ctx.rx_len < DW_SPI_RXFTLR_DFLT) {
|
|
reg_data = spi->ctx.rx_len - 1;
|
|
}
|
|
} else {
|
|
if (spi->ctx.rx_len && spi->ctx.rx_len < DW_SPI_FIFO_DEPTH) {
|
|
reg_data = spi->ctx.rx_len - 1;
|
|
}
|
|
}
|
|
|
|
/* Rx Threshold */
|
|
write_rxftlr(reg_data, info->regs);
|
|
|
|
/* Enable interrupts */
|
|
reg_data = !rx_bufs ?
|
|
DW_SPI_IMR_UNMASK & DW_SPI_IMR_MASK_RX :
|
|
DW_SPI_IMR_UNMASK;
|
|
write_imr(reg_data, info->regs);
|
|
|
|
spi_context_cs_control(&spi->ctx, true);
|
|
|
|
LOG_DBG("Enabling controller");
|
|
set_bit_ssienr(info->regs);
|
|
|
|
ret = spi_context_wait_for_completion(&spi->ctx);
|
|
out:
|
|
spi_context_release(&spi->ctx, ret);
|
|
|
|
pm_device_busy_clear(dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int spi_dw_transceive(const struct device *dev,
|
|
const struct spi_config *config,
|
|
const struct spi_buf_set *tx_bufs,
|
|
const struct spi_buf_set *rx_bufs)
|
|
{
|
|
LOG_DBG("%p, %p, %p", dev, tx_bufs, rx_bufs);
|
|
|
|
return transceive(dev, config, tx_bufs, rx_bufs, false, NULL, NULL);
|
|
}
|
|
|
|
#ifdef CONFIG_SPI_ASYNC
|
|
static int spi_dw_transceive_async(const struct device *dev,
|
|
const struct spi_config *config,
|
|
const struct spi_buf_set *tx_bufs,
|
|
const struct spi_buf_set *rx_bufs,
|
|
spi_callback_t cb,
|
|
void *userdata)
|
|
{
|
|
LOG_DBG("%p, %p, %p, %p, %p", dev, tx_bufs, rx_bufs, cb, userdata);
|
|
|
|
return transceive(dev, config, tx_bufs, rx_bufs, true, cb, userdata);
|
|
}
|
|
#endif /* CONFIG_SPI_ASYNC */
|
|
|
|
static int spi_dw_release(const struct device *dev,
|
|
const struct spi_config *config)
|
|
{
|
|
struct spi_dw_data *spi = dev->data;
|
|
|
|
if (!spi_context_configured(&spi->ctx, config)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
spi_context_unlock_unconditionally(&spi->ctx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void spi_dw_isr(const struct device *dev)
|
|
{
|
|
const struct spi_dw_config *info = dev->config;
|
|
uint32_t int_status;
|
|
int error;
|
|
|
|
int_status = read_isr(info->regs);
|
|
|
|
LOG_DBG("SPI %p int_status 0x%x - (tx: %d, rx: %d)", dev,
|
|
int_status, read_txflr(info->regs), read_rxflr(info->regs));
|
|
|
|
if (int_status & DW_SPI_ISR_ERRORS_MASK) {
|
|
error = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
error = 0;
|
|
|
|
if (int_status & DW_SPI_ISR_RXFIS) {
|
|
pull_data(dev);
|
|
}
|
|
|
|
if (int_status & DW_SPI_ISR_TXEIS) {
|
|
push_data(dev);
|
|
}
|
|
|
|
out:
|
|
clear_interrupts(info->regs);
|
|
completed(dev, error);
|
|
}
|
|
|
|
static const struct spi_driver_api dw_spi_api = {
|
|
.transceive = spi_dw_transceive,
|
|
#ifdef CONFIG_SPI_ASYNC
|
|
.transceive_async = spi_dw_transceive_async,
|
|
#endif /* CONFIG_SPI_ASYNC */
|
|
.release = spi_dw_release,
|
|
};
|
|
|
|
int spi_dw_init(const struct device *dev)
|
|
{
|
|
int err;
|
|
const struct spi_dw_config *info = dev->config;
|
|
struct spi_dw_data *spi = dev->data;
|
|
|
|
info->config_func();
|
|
|
|
/* Masking interrupt and making sure controller is disabled */
|
|
write_imr(DW_SPI_IMR_MASK, info->regs);
|
|
clear_bit_ssienr(info->regs);
|
|
|
|
LOG_DBG("Designware SPI driver initialized on device: %p", dev);
|
|
|
|
err = spi_context_cs_configure_all(&spi->ctx);
|
|
if (err < 0) {
|
|
return err;
|
|
}
|
|
|
|
spi_context_unlock_unconditionally(&spi->ctx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#if DT_NODE_HAS_STATUS(DT_DRV_INST(0), okay)
|
|
void spi_config_0_irq(void);
|
|
|
|
struct spi_dw_data spi_dw_data_port_0 = {
|
|
SPI_CONTEXT_INIT_LOCK(spi_dw_data_port_0, ctx),
|
|
SPI_CONTEXT_INIT_SYNC(spi_dw_data_port_0, ctx),
|
|
SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(0), ctx)
|
|
};
|
|
|
|
#if DT_NODE_HAS_PROP(DT_INST_PHANDLE(0, clocks), clock_frequency)
|
|
#define INST_0_SNPS_DESIGNWARE_SPI_CLOCK_FREQ \
|
|
DT_INST_PROP_BY_PHANDLE(0, clocks, clock_frequency)
|
|
#else
|
|
#define INST_0_SNPS_DESIGNWARE_SPI_CLOCK_FREQ \
|
|
DT_INST_PROP(0, clock_frequency)
|
|
#endif
|
|
|
|
const struct spi_dw_config spi_dw_config_0 = {
|
|
.regs = DT_INST_REG_ADDR(0),
|
|
.clock_frequency = INST_0_SNPS_DESIGNWARE_SPI_CLOCK_FREQ,
|
|
.config_func = spi_config_0_irq,
|
|
.op_modes = SPI_CTX_RUNTIME_OP_MODE_MASTER
|
|
};
|
|
|
|
DEVICE_DT_INST_DEFINE(0, spi_dw_init, NULL,
|
|
&spi_dw_data_port_0, &spi_dw_config_0,
|
|
POST_KERNEL, CONFIG_SPI_INIT_PRIORITY,
|
|
&dw_spi_api);
|
|
|
|
void spi_config_0_irq(void)
|
|
{
|
|
#if DT_NUM_IRQS(DT_DRV_INST(0)) == 1
|
|
#if DT_INST_IRQ_HAS_NAME(0, flags)
|
|
#define INST_0_IRQ_FLAGS DT_INST_IRQ_BY_NAME(0, flags, irq)
|
|
#else
|
|
#define INST_0_IRQ_FLAGS 0
|
|
#endif
|
|
IRQ_CONNECT(DT_INST_IRQN(0),
|
|
DT_INST_IRQ(0, priority),
|
|
spi_dw_isr, DEVICE_DT_INST_GET(0),
|
|
INST_0_IRQ_FLAGS);
|
|
irq_enable(DT_INST_IRQN(0));
|
|
#else
|
|
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, rx_avail, irq),
|
|
DT_INST_IRQ_BY_NAME(0, rx_avail_pri, irq),
|
|
spi_dw_isr, DEVICE_DT_INST_GET(0),
|
|
DT_INST_IRQ_BY_NAME(0, rx_avail, flags));
|
|
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, tx_req, irq),
|
|
DT_INST_IRQ_BY_NAME(0, tx_req_pri, irq),
|
|
spi_dw_isr, DEVICE_DT_INST_GET(0),
|
|
DT_INST_IRQ_BY_NAME(0, tx_req, flags));
|
|
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, err_int, irq),
|
|
DT_INST_IRQ_BY_NAME(0, err_int_pri, irq),
|
|
spi_dw_isr, DEVICE_DT_INST_GET(0),
|
|
DT_INST_IRQ_BY_NAME(0, err_int, flags));
|
|
|
|
irq_enable(DT_INST_IRQ_BY_NAME(0, rx_avail, irq));
|
|
irq_enable(DT_INST_IRQ_BY_NAME(0, tx_req, irq));
|
|
irq_enable(DT_INST_IRQ_BY_NAME(0, err_int, irq));
|
|
|
|
#endif
|
|
}
|
|
#endif /* DT_NODE_HAS_STATUS(DT_DRV_INST(0), okay) */
|
|
|
|
#if DT_NODE_HAS_STATUS(DT_DRV_INST(1), okay)
|
|
void spi_config_1_irq(void);
|
|
|
|
struct spi_dw_data spi_dw_data_port_1 = {
|
|
SPI_CONTEXT_INIT_LOCK(spi_dw_data_port_1, ctx),
|
|
SPI_CONTEXT_INIT_SYNC(spi_dw_data_port_1, ctx),
|
|
SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(1), ctx)
|
|
};
|
|
|
|
#if DT_NODE_HAS_PROP(DT_INST_PHANDLE(1, clocks), clock_frequency)
|
|
#define INST_1_SNPS_DESIGNWARE_SPI_CLOCK_FREQ \
|
|
DT_INST_PROP_BY_PHANDLE(1, clocks, clock_frequency)
|
|
#else
|
|
#define INST_1_SNPS_DESIGNWARE_SPI_CLOCK_FREQ \
|
|
DT_INST_PROP(1, clock_frequency)
|
|
#endif
|
|
|
|
static const struct spi_dw_config spi_dw_config_1 = {
|
|
.regs = DT_INST_REG_ADDR(1),
|
|
.clock_frequency = INST_1_SNPS_DESIGNWARE_SPI_CLOCK_FREQ,
|
|
.config_func = spi_config_1_irq,
|
|
.op_modes = SPI_CTX_RUNTIME_OP_MODE_MASTER
|
|
};
|
|
|
|
DEVICE_DT_INST_DEFINE(1, spi_dw_init, NULL,
|
|
&spi_dw_data_port_1, &spi_dw_config_1,
|
|
POST_KERNEL, CONFIG_SPI_INIT_PRIORITY,
|
|
&dw_spi_api);
|
|
|
|
void spi_config_1_irq(void)
|
|
{
|
|
#if DT_NUM_IRQS(DT_DRV_INST(1)) == 1
|
|
#if DT_INST_IRQ_HAS_NAME(1, flags)
|
|
#define INST_1_IRQ_FLAGS DT_INST_IRQ_BY_NAME(1, flags, irq)
|
|
#else
|
|
#define INST_1_IRQ_FLAGS 0
|
|
#endif
|
|
IRQ_CONNECT(DT_INST_IRQN(1),
|
|
DT_INST_IRQ(1, priority),
|
|
spi_dw_isr, DEVICE_DT_INST_GET(1),
|
|
INST_1_IRQ_FLAGS);
|
|
irq_enable(DT_INST_IRQN(1));
|
|
#else
|
|
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(1, rx_avail, irq),
|
|
DT_INST_IRQ_BY_NAME(1, rx_avail_pri, irq),
|
|
spi_dw_isr, DEVICE_DT_INST_GET(1),
|
|
DT_INST_IRQ_BY_NAME(1, rx_avail, flags));
|
|
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(1, tx_req, irq),
|
|
DT_INST_IRQ_BY_NAME(1, tx_req_pri, irq),
|
|
spi_dw_isr, DEVICE_DT_INST_GET(1),
|
|
DT_INST_IRQ_BY_NAME(1, tx_req, flags));
|
|
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(1, err_int, irq),
|
|
DT_INST_IRQ_BY_NAME(1, err_int_pri, irq),
|
|
spi_dw_isr, DEVICE_DT_INST_GET(1),
|
|
DT_INST_IRQ_BY_NAME(1, err_int, flags));
|
|
|
|
irq_enable(DT_INST_IRQ_BY_NAME(1, rx_avail, irq));
|
|
irq_enable(DT_INST_IRQ_BY_NAME(1, tx_req, irq));
|
|
irq_enable(DT_INST_IRQ_BY_NAME(1, err_int, irq));
|
|
|
|
#endif
|
|
}
|
|
#endif /* DT_NODE_HAS_STATUS(DT_DRV_INST(1), okay) */
|
|
|
|
#if DT_NODE_HAS_STATUS(DT_DRV_INST(2), okay)
|
|
void spi_config_2_irq(void);
|
|
|
|
struct spi_dw_data spi_dw_data_port_2 = {
|
|
SPI_CONTEXT_INIT_LOCK(spi_dw_data_port_2, ctx),
|
|
SPI_CONTEXT_INIT_SYNC(spi_dw_data_port_2, ctx),
|
|
SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(2), ctx)
|
|
};
|
|
|
|
#if DT_NODE_HAS_PROP(DT_INST_PHANDLE(2, clocks), clock_frequency)
|
|
#define INST_2_SNPS_DESIGNWARE_SPI_CLOCK_FREQ \
|
|
DT_INST_PROP_BY_PHANDLE(2, clocks, clock_frequency)
|
|
#else
|
|
#define INST_2_SNPS_DESIGNWARE_SPI_CLOCK_FREQ \
|
|
DT_INST_PROP(2, clock_frequency)
|
|
#endif
|
|
|
|
static const struct spi_dw_config spi_dw_config_2 = {
|
|
.regs = DT_INST_REG_ADDR(2),
|
|
.clock_frequency = INST_2_SNPS_DESIGNWARE_SPI_CLOCK_FREQ,
|
|
.config_func = spi_config_2_irq,
|
|
.op_modes = SPI_CTX_RUNTIME_OP_MODE_MASTER
|
|
};
|
|
|
|
DEVICE_DT_INST_DEFINE(2, spi_dw_init, NULL,
|
|
&spi_dw_data_port_2, &spi_dw_config_2,
|
|
POST_KERNEL, CONFIG_SPI_INIT_PRIORITY,
|
|
&dw_spi_api);
|
|
|
|
void spi_config_2_irq(void)
|
|
{
|
|
#if DT_NUM_IRQS(DT_DRV_INST(2)) == 1
|
|
#if DT_INST_IRQ_HAS_NAME(2, flags)
|
|
#define INST_2_IRQ_FLAGS DT_INST_IRQ_BY_NAME(2, flags, irq)
|
|
#else
|
|
#define INST_2_IRQ_FLAGS 0
|
|
#endif
|
|
IRQ_CONNECT(DT_INST_IRQN(2),
|
|
DT_INST_IRQ(2, priority),
|
|
spi_dw_isr, DEVICE_DT_INST_GET(2),
|
|
INST_2_IRQ_FLAGS);
|
|
irq_enable(DT_INST_IRQN(2));
|
|
#else
|
|
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(2, rx_avail, irq),
|
|
DT_INST_IRQ_BY_NAME(2, rx_avail_pri, irq),
|
|
spi_dw_isr, DEVICE_DT_INST_GET(2),
|
|
DT_INST_IRQ_BY_NAME(2, rx_avail, flags));
|
|
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(2, tx_req, irq),
|
|
DT_INST_IRQ_BY_NAME(2, tx_req_pri, irq),
|
|
spi_dw_isr, DEVICE_DT_INST_GET(2),
|
|
DT_INST_IRQ_BY_NAME(2, tx_req, flags));
|
|
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(2, err_int, irq),
|
|
DT_INST_IRQ_BY_NAME(2, err_int_pri, irq),
|
|
spi_dw_isr, DEVICE_DT_INST_GET(2),
|
|
DT_INST_IRQ_BY_NAME(2, err_int, flags));
|
|
|
|
irq_enable(DT_INST_IRQ_BY_NAME(2, rx_avail, irq));
|
|
irq_enable(DT_INST_IRQ_BY_NAME(2, tx_req, irq));
|
|
irq_enable(DT_INST_IRQ_BY_NAME(2, err_int, irq));
|
|
|
|
#endif
|
|
}
|
|
#endif /* DT_NODE_HAS_STATUS(DT_DRV_INST(2), okay) */
|
|
|
|
#if DT_NODE_HAS_STATUS(DT_DRV_INST(3), okay)
|
|
void spi_config_3_irq(void);
|
|
|
|
struct spi_dw_data spi_dw_data_port_3 = {
|
|
SPI_CONTEXT_INIT_LOCK(spi_dw_data_port_3, ctx),
|
|
SPI_CONTEXT_INIT_SYNC(spi_dw_data_port_3, ctx),
|
|
SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(3), ctx)
|
|
};
|
|
|
|
#if DT_NODE_HAS_PROP(DT_INST_PHANDLE(3, clocks), clock_frequency)
|
|
#define INST_3_SNPS_DESIGNWARE_SPI_CLOCK_FREQ \
|
|
DT_INST_PROP_BY_PHANDLE(3, clocks, clock_frequency)
|
|
#else
|
|
#define INST_3_SNPS_DESIGNWARE_SPI_CLOCK_FREQ \
|
|
DT_INST_PROP(3, clock_frequency)
|
|
#endif
|
|
|
|
static const struct spi_dw_config spi_dw_config_3 = {
|
|
.regs = DT_INST_REG_ADDR(3),
|
|
.clock_frequency = INST_3_SNPS_DESIGNWARE_SPI_CLOCK_FREQ,
|
|
.config_func = spi_config_3_irq,
|
|
.op_modes = SPI_CTX_RUNTIME_OP_MODE_MASTER
|
|
};
|
|
|
|
DEVICE_DT_INST_DEFINE(3, spi_dw_init, NULL,
|
|
&spi_dw_data_port_3, &spi_dw_config_3,
|
|
POST_KERNEL, CONFIG_SPI_INIT_PRIORITY,
|
|
&dw_spi_api);
|
|
|
|
void spi_config_3_irq(void)
|
|
{
|
|
#if DT_NUM_IRQS(DT_DRV_INST(3)) == 1
|
|
#if DT_INST_IRQ_HAS_NAME(3, flags)
|
|
#define INST_3_IRQ_FLAGS DT_INST_IRQ_BY_NAME(3, flags, irq)
|
|
#else
|
|
#define INST_3_IRQ_FLAGS 0
|
|
#endif
|
|
IRQ_CONNECT(DT_INST_IRQN(3),
|
|
DT_INST_IRQ(3, priority),
|
|
spi_dw_isr, DEVICE_DT_INST_GET(3),
|
|
INST_3_IRQ_FLAGS);
|
|
irq_enable(DT_INST_IRQN(3));
|
|
#else
|
|
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(3, rx_avail, irq),
|
|
DT_INST_IRQ_BY_NAME(3, rx_avail_pri, irq),
|
|
spi_dw_isr, DEVICE_DT_INST_GET(3),
|
|
DT_INST_IRQ_BY_NAME(3, rx_avail, flags));
|
|
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(3, tx_req, irq),
|
|
DT_INST_IRQ_BY_NAME(3, tx_req_pri, irq),
|
|
spi_dw_isr, DEVICE_DT_INST_GET(3),
|
|
DT_INST_IRQ_BY_NAME(3, tx_req, flags));
|
|
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(3, err_int, irq),
|
|
DT_INST_IRQ_BY_NAME(3, err_int_pri, irq),
|
|
spi_dw_isr, DEVICE_DT_INST_GET(3),
|
|
DT_INST_IRQ_BY_NAME(3, err_int, flags));
|
|
|
|
irq_enable(DT_INST_IRQ_BY_NAME(3, rx_avail, irq));
|
|
irq_enable(DT_INST_IRQ_BY_NAME(3, tx_req, irq));
|
|
irq_enable(DT_INST_IRQ_BY_NAME(3, err_int, irq));
|
|
|
|
#endif
|
|
}
|
|
#endif /* DT_NODE_HAS_STATUS(DT_DRV_INST(3), okay) */
|