spi: Introduce new mcux shim driver
Adds a shim layer around the mcux dspi driver to adapt it to the Zephyr spi interface. Unlike the existing k64 spi driver, this driver can be used for other Kinetis SoCs that contain the dspi module. Jira: ZEP-1374 Change-Id: I9417c1513565dfcc47ccda098492f60e840f4f84 Signed-off-by: Maureen Helm <maureen.helm@nxp.com>
This commit is contained in:
parent
bd562921e8
commit
d138a4fad1
5 changed files with 361 additions and 1 deletions
|
@ -270,4 +270,6 @@ config SPI_SS_1_CS_GPIO_PIN
|
|||
|
||||
source "drivers/spi/Kconfig.dw"
|
||||
|
||||
source "drivers/spi/Kconfig.mcux"
|
||||
|
||||
endif # SPI
|
||||
|
|
36
drivers/spi/Kconfig.mcux
Normal file
36
drivers/spi/Kconfig.mcux
Normal file
|
@ -0,0 +1,36 @@
|
|||
# Kconfig - MCUXpresso SDK SPI
|
||||
#
|
||||
# Copyright (c) 2016, Freescale Semiconductor, Inc.
|
||||
# Copyright (c) 2017, NXP
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
menuconfig SPI_MCUX
|
||||
bool
|
||||
prompt "MCUX SPI driver"
|
||||
depends on SPI && HAS_MCUX
|
||||
default n
|
||||
help
|
||||
Enable support for mcux spi driver.
|
||||
|
||||
if SPI_MCUX
|
||||
|
||||
config SPI_MCUX_BUF_SIZE
|
||||
int "Number of bytes in the local buffer"
|
||||
default 16
|
||||
help
|
||||
The mcux driver requires that the rx and tx buffers are the same
|
||||
length, however the Zephyr spi interface allows them to be different.
|
||||
When they are different, the mcux shim driver uses a local buffer. This
|
||||
option defines the size of the local buffer.
|
||||
|
||||
config SPI_MCUX_DUMMY_CHAR
|
||||
hex "Dummy character"
|
||||
default 0x00
|
||||
range 0x00 0xff
|
||||
help
|
||||
This option configures what value to send when the tx buffer length is
|
||||
less than the rx buffer length.
|
||||
|
||||
endif # SPI_MCUX
|
|
@ -1,5 +1,6 @@
|
|||
obj-$(CONFIG_SPI_INTEL) += spi_intel.o
|
||||
obj-$(CONFIG_SPI_DW) += spi_dw.o
|
||||
obj-$(CONFIG_SPI_MCUX) += spi_mcux.o
|
||||
obj-$(CONFIG_SPI_QMSI) += spi_qmsi.o
|
||||
obj-$(CONFIG_SPI_QMSI_SS) += spi_qmsi_ss.o
|
||||
obj-$(CONFIG_SPI_K64) += spi_k64.o
|
||||
|
|
321
drivers/spi/spi_mcux.c
Normal file
321
drivers/spi/spi_mcux.c
Normal file
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Freescale Semiconductor, Inc.
|
||||
* Copyright (c) 2017, NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <spi.h>
|
||||
#include <soc.h>
|
||||
#include <fsl_dspi.h>
|
||||
#include <fsl_clock.h>
|
||||
|
||||
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_SPI_LEVEL
|
||||
#include <logging/sys_log.h>
|
||||
|
||||
struct spi_mcux_config {
|
||||
SPI_Type *base;
|
||||
clock_name_t clock_source;
|
||||
void (*irq_config_func)(struct device *dev);
|
||||
struct spi_config default_cfg;
|
||||
};
|
||||
|
||||
struct spi_mcux_data {
|
||||
dspi_master_handle_t handle;
|
||||
struct k_sem sync;
|
||||
status_t callback_status;
|
||||
uint32_t slave;
|
||||
};
|
||||
|
||||
static void spi_mcux_master_transfer_callback(SPI_Type *base,
|
||||
dspi_master_handle_t *handle, status_t status, void *userData)
|
||||
{
|
||||
struct device *dev = userData;
|
||||
struct spi_mcux_data *data = dev->driver_data;
|
||||
|
||||
data->callback_status = status;
|
||||
k_sem_give(&data->sync);
|
||||
}
|
||||
|
||||
static int spi_mcux_configure(struct device *dev, struct spi_config *spi_config)
|
||||
{
|
||||
const struct spi_mcux_config *config = dev->config->config_info;
|
||||
struct spi_mcux_data *data = dev->driver_data;
|
||||
SPI_Type *base = config->base;
|
||||
dspi_master_config_t master_config;
|
||||
uint32_t flags = spi_config->config;
|
||||
uint32_t clock_freq;
|
||||
uint32_t word_size;
|
||||
|
||||
DSPI_MasterGetDefaultConfig(&master_config);
|
||||
|
||||
word_size = SPI_WORD_SIZE_GET(flags);
|
||||
if (word_size > FSL_FEATURE_DSPI_MAX_DATA_WIDTH) {
|
||||
SYS_LOG_ERR("Word size %d is greater than %d",
|
||||
word_size, FSL_FEATURE_DSPI_MAX_DATA_WIDTH);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
master_config.ctarConfig.bitsPerFrame = word_size;
|
||||
|
||||
master_config.ctarConfig.cpol = (flags & SPI_MODE_CPOL)
|
||||
? kDSPI_ClockPolarityActiveLow
|
||||
: kDSPI_ClockPolarityActiveHigh;
|
||||
|
||||
master_config.ctarConfig.cpha = (flags & SPI_MODE_CPHA)
|
||||
? kDSPI_ClockPhaseSecondEdge
|
||||
: kDSPI_ClockPhaseFirstEdge;
|
||||
|
||||
master_config.ctarConfig.direction = (flags & SPI_TRANSFER_LSB)
|
||||
? kDSPI_LsbFirst
|
||||
: kDSPI_MsbFirst;
|
||||
|
||||
master_config.ctarConfig.baudRate = spi_config->max_sys_freq;
|
||||
|
||||
SYS_LOG_DBG("word size = %d, baud rate = %d",
|
||||
word_size, spi_config->max_sys_freq);
|
||||
|
||||
clock_freq = CLOCK_GetFreq(config->clock_source);
|
||||
DSPI_MasterInit(base, &master_config, clock_freq);
|
||||
|
||||
DSPI_MasterTransferCreateHandle(base, &data->handle,
|
||||
spi_mcux_master_transfer_callback, dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_mcux_slave_select(struct device *dev, uint32_t slave)
|
||||
{
|
||||
struct spi_mcux_data *data = dev->driver_data;
|
||||
|
||||
if (slave > FSL_FEATURE_DSPI_CHIP_SELECT_COUNT) {
|
||||
SYS_LOG_ERR("Slave %d is greater than %d",
|
||||
slave, FSL_FEATURE_DSPI_CHIP_SELECT_COUNT);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data->slave = slave;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_mcux_transceive(struct device *dev,
|
||||
const void *tx_buf, uint32_t tx_buf_len,
|
||||
void *rx_buf, uint32_t rx_buf_len)
|
||||
{
|
||||
const struct spi_mcux_config *config = dev->config->config_info;
|
||||
struct spi_mcux_data *data = dev->driver_data;
|
||||
SPI_Type *base = config->base;
|
||||
uint8_t buf[CONFIG_SPI_MCUX_BUF_SIZE];
|
||||
dspi_transfer_t transfer;
|
||||
status_t status;
|
||||
|
||||
/* Initialize the transfer descriptor */
|
||||
|
||||
SYS_LOG_DBG("tx_buf_len = %d, rx_buf_len = %d, local buf len = %d",
|
||||
tx_buf_len, rx_buf_len, sizeof(buf));
|
||||
|
||||
if (tx_buf_len == rx_buf_len) {
|
||||
/* The tx and rx buffers are the same length, so we can use
|
||||
* them directly.
|
||||
*/
|
||||
transfer.txData = (uint8_t *)tx_buf;
|
||||
transfer.rxData = rx_buf;
|
||||
transfer.dataSize = rx_buf_len;
|
||||
SYS_LOG_DBG("Using tx and rx buffers directly");
|
||||
} else if (tx_buf_len == 0) {
|
||||
/* The tx buffer length is zero, so this is a one-way spi read
|
||||
* operation.
|
||||
*/
|
||||
transfer.txData = NULL;
|
||||
transfer.rxData = rx_buf;
|
||||
transfer.dataSize = rx_buf_len;
|
||||
SYS_LOG_DBG("Using rx buffer directly, tx buffer is null");
|
||||
} else if (rx_buf_len == 0) {
|
||||
/* The rx buffer length is zero, so this is a one-way spi write
|
||||
* operation.
|
||||
*/
|
||||
transfer.txData = (uint8_t *)tx_buf;
|
||||
transfer.rxData = NULL;
|
||||
transfer.dataSize = tx_buf_len;
|
||||
SYS_LOG_DBG("Using tx buffer directly, rx buffer is null");
|
||||
} else if ((tx_buf_len < rx_buf_len) && (rx_buf_len <= sizeof(buf))) {
|
||||
/* The tx buffer is shorter than the rx buffer, so copy the tx
|
||||
* buffer to the longer local buffer.
|
||||
*/
|
||||
transfer.txData = buf;
|
||||
transfer.rxData = rx_buf;
|
||||
transfer.dataSize = rx_buf_len;
|
||||
memcpy(buf, tx_buf, tx_buf_len);
|
||||
memset(&buf[tx_buf_len], CONFIG_SPI_MCUX_DUMMY_CHAR,
|
||||
rx_buf_len - tx_buf_len);
|
||||
SYS_LOG_DBG("Using local buffer for tx");
|
||||
} else if ((rx_buf_len < tx_buf_len) && (tx_buf_len <= sizeof(buf))) {
|
||||
/* The rx buffer is shorter than the tx buffer, so use the
|
||||
* longer local buffer for rx. After the transfer is complete,
|
||||
* we'll need to copy the local buffer back to the rx buffer.
|
||||
*/
|
||||
transfer.txData = (uint8_t *)tx_buf;
|
||||
transfer.rxData = buf;
|
||||
transfer.dataSize = tx_buf_len;
|
||||
SYS_LOG_DBG("Using local buffer for rx");
|
||||
} else {
|
||||
SYS_LOG_ERR("Local buffer too small for transfer");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
transfer.configFlags = kDSPI_MasterCtar0 | kDSPI_MasterPcsContinuous |
|
||||
(data->slave << DSPI_MASTER_PCS_SHIFT);
|
||||
|
||||
/* Start the transfer */
|
||||
status = DSPI_MasterTransferNonBlocking(base, &data->handle, &transfer);
|
||||
|
||||
/* Return an error if the transfer didn't start successfully e.g., if
|
||||
* the bus was busy
|
||||
*/
|
||||
if (status != kStatus_Success) {
|
||||
SYS_LOG_ERR("Transfer could not start");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Wait for the transfer to complete */
|
||||
k_sem_take(&data->sync, K_FOREVER);
|
||||
|
||||
/* Return an error if the transfer didn't complete successfully. */
|
||||
if (data->callback_status != kStatus_Success) {
|
||||
SYS_LOG_ERR("Transfer could not complete");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Copy the local buffer back to the rx buffer. */
|
||||
if ((rx_buf_len < tx_buf_len) && (tx_buf_len <= sizeof(buf))) {
|
||||
memcpy(rx_buf, buf, rx_buf_len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_mcux_isr(void *arg)
|
||||
{
|
||||
struct device *dev = (struct device *)arg;
|
||||
const struct spi_mcux_config *config = dev->config->config_info;
|
||||
struct spi_mcux_data *data = dev->driver_data;
|
||||
SPI_Type *base = config->base;
|
||||
|
||||
DSPI_MasterTransferHandleIRQ(base, &data->handle);
|
||||
}
|
||||
|
||||
static int spi_mcux_init(struct device *dev)
|
||||
{
|
||||
const struct spi_mcux_config *config = dev->config->config_info;
|
||||
struct spi_mcux_data *data = dev->driver_data;
|
||||
struct spi_config *spi_config;
|
||||
int error;
|
||||
|
||||
k_sem_init(&data->sync, 0, UINT_MAX);
|
||||
|
||||
spi_config = (struct spi_config *)&config->default_cfg;
|
||||
error = spi_mcux_configure(dev, spi_config);
|
||||
if (error) {
|
||||
SYS_LOG_ERR("Could not configure");
|
||||
return error;
|
||||
}
|
||||
|
||||
config->irq_config_func(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_driver_api spi_mcux_driver_api = {
|
||||
.configure = spi_mcux_configure,
|
||||
.slave_select = spi_mcux_slave_select,
|
||||
.transceive = spi_mcux_transceive,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SPI_0
|
||||
static void spi_mcux_config_func_0(struct device *dev);
|
||||
|
||||
static const struct spi_mcux_config spi_mcux_config_0 = {
|
||||
.base = DSPI0,
|
||||
.clock_source = DSPI0_CLK_SRC,
|
||||
.irq_config_func = spi_mcux_config_func_0,
|
||||
.default_cfg = {
|
||||
.config = CONFIG_SPI_0_DEFAULT_CFG,
|
||||
.max_sys_freq = CONFIG_SPI_0_DEFAULT_BAUD_RATE,
|
||||
}
|
||||
};
|
||||
|
||||
static struct spi_mcux_data spi_mcux_data_0;
|
||||
|
||||
DEVICE_AND_API_INIT(spi_mcux_0, CONFIG_SPI_0_NAME, &spi_mcux_init,
|
||||
&spi_mcux_data_0, &spi_mcux_config_0,
|
||||
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
||||
&spi_mcux_driver_api);
|
||||
|
||||
static void spi_mcux_config_func_0(struct device *dev)
|
||||
{
|
||||
IRQ_CONNECT(IRQ_SPI0, CONFIG_SPI_0_IRQ_PRI,
|
||||
spi_mcux_isr, DEVICE_GET(spi_mcux_0), 0);
|
||||
|
||||
irq_enable(IRQ_SPI0);
|
||||
}
|
||||
#endif /* CONFIG_SPI_0 */
|
||||
|
||||
#ifdef CONFIG_SPI_1
|
||||
static void spi_mcux_config_func_1(struct device *dev);
|
||||
|
||||
static const struct spi_mcux_config spi_mcux_config_1 = {
|
||||
.base = DSPI1,
|
||||
.clock_source = DSPI1_CLK_SRC,
|
||||
.irq_config_func = spi_mcux_config_func_1,
|
||||
.default_cfg = {
|
||||
.config = CONFIG_SPI_1_DEFAULT_CFG,
|
||||
.max_sys_freq = CONFIG_SPI_1_DEFAULT_BAUD_RATE,
|
||||
}
|
||||
};
|
||||
|
||||
static struct spi_mcux_data spi_mcux_data_1;
|
||||
|
||||
DEVICE_AND_API_INIT(spi_mcux_1, CONFIG_SPI_1_NAME, &spi_mcux_init,
|
||||
&spi_mcux_data_1, &spi_mcux_config_1,
|
||||
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
||||
&spi_mcux_driver_api);
|
||||
|
||||
static void spi_mcux_config_func_1(struct device *dev)
|
||||
{
|
||||
IRQ_CONNECT(IRQ_SPI1, CONFIG_SPI_1_IRQ_PRI,
|
||||
spi_mcux_isr, DEVICE_GET(spi_mcux_1), 0);
|
||||
|
||||
irq_enable(IRQ_SPI1);
|
||||
}
|
||||
#endif /* CONFIG_SPI_1 */
|
||||
|
||||
#ifdef CONFIG_SPI_2
|
||||
static void spi_mcux_config_func_2(struct device *dev);
|
||||
|
||||
static const struct spi_mcux_config spi_mcux_config_2 = {
|
||||
.base = DSPI2,
|
||||
.clock_source = DSPI2_CLK_SRC,
|
||||
.irq_config_func = spi_mcux_config_func_2,
|
||||
.default_cfg = {
|
||||
.config = CONFIG_SPI_2_DEFAULT_CFG,
|
||||
.max_sys_freq = CONFIG_SPI_2_DEFAULT_BAUD_RATE,
|
||||
}
|
||||
};
|
||||
|
||||
static struct spi_mcux_data spi_mcux_data_2;
|
||||
|
||||
DEVICE_AND_API_INIT(spi_mcux_2, CONFIG_SPI_2_NAME, &spi_mcux_init,
|
||||
&spi_mcux_data_2, &spi_mcux_config_2,
|
||||
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
||||
&spi_mcux_driver_api);
|
||||
|
||||
static void spi_mcux_config_func_2(struct device *dev)
|
||||
{
|
||||
IRQ_CONNECT(IRQ_SPI2, CONFIG_SPI_2_IRQ_PRI,
|
||||
spi_mcux_isr, DEVICE_GET(spi_mcux_2), 0);
|
||||
|
||||
irq_enable(IRQ_SPI2);
|
||||
}
|
||||
#endif /* CONFIG_SPI_2 */
|
|
@ -8,5 +8,5 @@ obj-$(CONFIG_ETH_MCUX) += fsl_enet.o
|
|||
obj-$(CONFIG_I2C_MCUX) += fsl_i2c.o
|
||||
obj-$(CONFIG_RANDOM_MCUX) += fsl_rnga.o
|
||||
obj-$(CONFIG_SOC_FLASH_MCUX) += fsl_flash.o
|
||||
obj-$(CONFIG_SPI_MCUX) += fsl_dspi.o
|
||||
obj-$(CONFIG_UART_MCUX) += fsl_uart.o
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue