drivers/spi: Removing QMSI driver as it does not support new API
Native DW driver is relevantly used instead. Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
This commit is contained in:
parent
1ac6f4bd56
commit
0a43cac3bb
5 changed files with 0 additions and 940 deletions
|
@ -3,8 +3,6 @@ if(CONFIG_SPI_LEGACY_API)
|
|||
zephyr_sources_ifdef(CONFIG_SPIS_NRF5 spis_nrf5_legacy.c)
|
||||
zephyr_sources_ifdef(CONFIG_SPI_INTEL spi_intel.c)
|
||||
zephyr_sources_ifdef(CONFIG_SPI_MCUX_DSPI spi_mcux_dspi.c)
|
||||
zephyr_sources_ifdef(CONFIG_SPI_QMSI spi_qmsi.c)
|
||||
zephyr_sources_ifdef(CONFIG_SPI_QMSI_SS spi_qmsi_ss.c)
|
||||
else()
|
||||
zephyr_sources_ifdef(CONFIG_SPI_DW spi_dw.c)
|
||||
zephyr_sources_ifdef(CONFIG_SPI_STM32 spi_ll_stm32.c)
|
||||
|
|
|
@ -299,8 +299,6 @@ config SPI_INTEL
|
|||
was formerly found on XScale chips. It can be found nowadays
|
||||
on CEXXXX Intel media controller and Quark CPU (2 of them).
|
||||
|
||||
source "drivers/spi/Kconfig.qmsi"
|
||||
|
||||
source "drivers/spi/Kconfig.mcux_dspi"
|
||||
|
||||
source "drivers/spi/Kconfig.nrf5_legacy"
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
# Kconfig.qmsi - QMSI SPI shim driver configuration options
|
||||
|
||||
#
|
||||
# Copyright (c) 2017 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
menuconfig SPI_QMSI
|
||||
bool "QMSI driver for SPI controller"
|
||||
depends on QMSI
|
||||
default n
|
||||
help
|
||||
SPI driver implementation using QMSI library. QMSI is the
|
||||
Quark Microcontroller Software Interface, providing a common
|
||||
interface to the Quark family of microcontrollers.
|
||||
|
||||
config SPI_QMSI_SS
|
||||
bool "QMSI driver for SPI controller on Sensor Subsystem"
|
||||
depends on QMSI
|
||||
default n
|
||||
help
|
||||
SPI driver implementation using QMSI library. This instance is
|
||||
for the Sensor Subsystem.
|
||||
|
||||
if QMSI_SS
|
||||
|
||||
config SPI_SS_INIT_PRIORITY
|
||||
int "Init priority"
|
||||
default 70
|
||||
help
|
||||
Device driver initialization priority.
|
||||
|
||||
config SPI_SS_CS_GPIO
|
||||
bool "SPI port CS pin is controlled via a GPIO port"
|
||||
select GPIO
|
||||
default n
|
||||
|
||||
config SPI_SS_0
|
||||
bool "SPI SS port 0"
|
||||
default n
|
||||
help
|
||||
Enable SPI controller port 0.
|
||||
|
||||
if SPI_SS_0
|
||||
|
||||
config SPI_SS_0_NAME
|
||||
string "SPI SS port 0 device name"
|
||||
default "SPI_SS_0"
|
||||
|
||||
config SPI_SS_0_IRQ_PRI
|
||||
int "Port 0 interrupt priority"
|
||||
|
||||
config SPI_SS_0_CS_GPIO_PORT
|
||||
string "The GPIO port which is used to control CS"
|
||||
depends on SPI_SS_CS_GPIO
|
||||
default "GPIO_0"
|
||||
|
||||
config SPI_SS_0_CS_GPIO_PIN
|
||||
int "The GPIO PIN which is used to act as a CS pin"
|
||||
depends on SPI_SS_CS_GPIO
|
||||
default 0
|
||||
|
||||
endif # SPI_SS_0
|
||||
|
||||
config SPI_SS_1
|
||||
bool "SPI SS port 1"
|
||||
default n
|
||||
help
|
||||
Enable SPI controller port 1.
|
||||
|
||||
if SPI_SS_1
|
||||
|
||||
config SPI_SS_1_NAME
|
||||
string "SPI port 1 device name"
|
||||
default "SPI_SS_1"
|
||||
|
||||
config SPI_SS_1_IRQ_PRI
|
||||
int "Port 0 interrupt priority"
|
||||
|
||||
config SPI_SS_1_CS_GPIO_PORT
|
||||
string "The GPIO port which is used to control CS"
|
||||
depends on SPI_SS_CS_GPIO
|
||||
default "GPIO_0"
|
||||
|
||||
config SPI_SS_1_CS_GPIO_PIN
|
||||
int "The GPIO PIN which is used to act as a CS pin"
|
||||
depends on SPI_SS_CS_GPIO
|
||||
default 0
|
||||
|
||||
endif # SPI_SS_1
|
||||
endif # QMSI_SS
|
|
@ -1,384 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Intel Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <device.h>
|
||||
#include <drivers/ioapic.h>
|
||||
#include <init.h>
|
||||
#include <kernel.h>
|
||||
#include <spi.h>
|
||||
#include <gpio.h>
|
||||
#include <power.h>
|
||||
|
||||
#include "qm_spi.h"
|
||||
#include "clk.h"
|
||||
#include "qm_isr.h"
|
||||
#include "soc.h"
|
||||
|
||||
struct pending_transfer {
|
||||
struct device *dev;
|
||||
qm_spi_async_transfer_t xfer;
|
||||
};
|
||||
|
||||
static struct pending_transfer pending_transfers[QM_SPI_NUM];
|
||||
|
||||
struct spi_qmsi_config {
|
||||
qm_spi_t spi;
|
||||
char *cs_port;
|
||||
u32_t cs_pin;
|
||||
};
|
||||
|
||||
struct spi_qmsi_runtime {
|
||||
struct device *gpio_cs;
|
||||
struct k_sem device_sync_sem;
|
||||
qm_spi_config_t cfg;
|
||||
int rc;
|
||||
bool loopback;
|
||||
struct k_sem sem;
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
u32_t device_power_state;
|
||||
qm_spi_context_t spi_ctx;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline qm_spi_bmode_t config_to_bmode(u8_t mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case SPI_MODE_CPHA:
|
||||
return QM_SPI_BMODE_1;
|
||||
case SPI_MODE_CPOL:
|
||||
return QM_SPI_BMODE_2;
|
||||
case SPI_MODE_CPOL | SPI_MODE_CPHA:
|
||||
return QM_SPI_BMODE_3;
|
||||
default:
|
||||
return QM_SPI_BMODE_0;
|
||||
}
|
||||
}
|
||||
|
||||
static void spi_control_cs(struct device *dev, bool active)
|
||||
{
|
||||
struct spi_qmsi_runtime *context = dev->driver_data;
|
||||
const struct spi_qmsi_config *config = dev->config->config_info;
|
||||
struct device *gpio = context->gpio_cs;
|
||||
|
||||
if (!gpio) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_pin_write(gpio, config->cs_pin, !active);
|
||||
}
|
||||
|
||||
static int spi_qmsi_configure(struct device *dev,
|
||||
struct spi_config *config)
|
||||
{
|
||||
struct spi_qmsi_runtime *context = dev->driver_data;
|
||||
qm_spi_config_t *cfg = &context->cfg;
|
||||
|
||||
cfg->frame_size = SPI_WORD_SIZE_GET(config->config) - 1;
|
||||
cfg->bus_mode = config_to_bmode(SPI_MODE(config->config));
|
||||
/* As loopback is implemented inside the controller,
|
||||
* the bus mode doesn't matter.
|
||||
*/
|
||||
context->loopback = SPI_MODE(config->config) & SPI_MODE_LOOP;
|
||||
cfg->clk_divider = config->max_sys_freq;
|
||||
|
||||
/* Will set the configuration before the transfer starts */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void transfer_complete(void *data, int error, qm_spi_status_t status,
|
||||
u16_t len)
|
||||
{
|
||||
const struct spi_qmsi_config *spi_config =
|
||||
((struct device *)data)->config->config_info;
|
||||
qm_spi_t spi = spi_config->spi;
|
||||
struct pending_transfer *pending = &pending_transfers[spi];
|
||||
struct device *dev = pending->dev;
|
||||
struct spi_qmsi_runtime *context;
|
||||
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
|
||||
context = dev->driver_data;
|
||||
|
||||
spi_control_cs(dev, false);
|
||||
|
||||
pending->dev = NULL;
|
||||
context->rc = error;
|
||||
k_sem_give(&context->device_sync_sem);
|
||||
}
|
||||
|
||||
static int spi_qmsi_slave_select(struct device *dev, u32_t slave)
|
||||
{
|
||||
const struct spi_qmsi_config *spi_config = dev->config->config_info;
|
||||
qm_spi_t spi = spi_config->spi;
|
||||
|
||||
return qm_spi_slave_select(spi, 1 << (slave - 1)) ? -EIO : 0;
|
||||
}
|
||||
|
||||
static inline u8_t frame_size_to_dfs(qm_spi_frame_size_t frame_size)
|
||||
{
|
||||
if (frame_size <= QM_SPI_FRAME_SIZE_8_BIT) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (frame_size <= QM_SPI_FRAME_SIZE_16_BIT) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (frame_size <= QM_SPI_FRAME_SIZE_32_BIT) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
/* This should never happen, it will crash later on. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_qmsi_transceive(struct device *dev,
|
||||
const void *tx_buf, u32_t tx_buf_len,
|
||||
void *rx_buf, u32_t rx_buf_len)
|
||||
{
|
||||
const struct spi_qmsi_config *spi_config = dev->config->config_info;
|
||||
qm_spi_t spi = spi_config->spi;
|
||||
struct spi_qmsi_runtime *context = dev->driver_data;
|
||||
qm_spi_config_t *cfg = &context->cfg;
|
||||
u8_t dfs = frame_size_to_dfs(cfg->frame_size);
|
||||
qm_spi_async_transfer_t *xfer;
|
||||
int rc;
|
||||
|
||||
k_sem_take(&context->sem, K_FOREVER);
|
||||
if (pending_transfers[spi].dev) {
|
||||
k_sem_give(&context->sem);
|
||||
return -EBUSY;
|
||||
}
|
||||
pending_transfers[spi].dev = dev;
|
||||
k_sem_give(&context->sem);
|
||||
|
||||
device_busy_set(dev);
|
||||
|
||||
xfer = &pending_transfers[spi].xfer;
|
||||
|
||||
xfer->rx = rx_buf;
|
||||
xfer->rx_len = rx_buf_len / dfs;
|
||||
/* This cast is necessary to drop the "const" modifier, since QMSI xfer
|
||||
* does not take a const pointer.
|
||||
*/
|
||||
xfer->tx = (u8_t *)tx_buf;
|
||||
xfer->tx_len = tx_buf_len / dfs;
|
||||
xfer->callback_data = dev;
|
||||
xfer->callback = transfer_complete;
|
||||
|
||||
if (tx_buf_len == 0) {
|
||||
cfg->transfer_mode = QM_SPI_TMOD_RX;
|
||||
} else if (rx_buf_len == 0) {
|
||||
cfg->transfer_mode = QM_SPI_TMOD_TX;
|
||||
} else {
|
||||
/* FIXME: QMSI expects rx_buf_len and tx_buf_len to
|
||||
* have the same size.
|
||||
*/
|
||||
cfg->transfer_mode = QM_SPI_TMOD_TX_RX;
|
||||
}
|
||||
|
||||
if (context->loopback) {
|
||||
QM_SPI[spi]->ctrlr0 |= BIT(11);
|
||||
}
|
||||
|
||||
rc = qm_spi_set_config(spi, cfg);
|
||||
if (rc != 0) {
|
||||
device_busy_clear(dev);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spi_control_cs(dev, true);
|
||||
|
||||
rc = qm_spi_irq_transfer(spi, xfer);
|
||||
if (rc != 0) {
|
||||
spi_control_cs(dev, false);
|
||||
device_busy_clear(dev);
|
||||
return -EIO;
|
||||
}
|
||||
k_sem_take(&context->device_sync_sem, K_FOREVER);
|
||||
|
||||
device_busy_clear(dev);
|
||||
|
||||
return context->rc ? -EIO : 0;
|
||||
}
|
||||
|
||||
static const struct spi_driver_api spi_qmsi_api = {
|
||||
.configure = spi_qmsi_configure,
|
||||
.slave_select = spi_qmsi_slave_select,
|
||||
.transceive = spi_qmsi_transceive,
|
||||
};
|
||||
|
||||
static struct device *gpio_cs_init(const struct spi_qmsi_config *config)
|
||||
{
|
||||
struct device *gpio;
|
||||
|
||||
if (!config->cs_port) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gpio = device_get_binding(config->cs_port);
|
||||
if (!gpio) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (gpio_pin_configure(gpio, config->cs_pin, GPIO_DIR_OUT) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (gpio_pin_write(gpio, config->cs_pin, 1) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return gpio;
|
||||
}
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
static void spi_master_set_power_state(struct device *dev, u32_t power_state)
|
||||
{
|
||||
struct spi_qmsi_runtime *context = dev->driver_data;
|
||||
|
||||
context->device_power_state = power_state;
|
||||
}
|
||||
|
||||
static u32_t spi_master_get_power_state(struct device *dev)
|
||||
{
|
||||
struct spi_qmsi_runtime *context = dev->driver_data;
|
||||
|
||||
return context->device_power_state;
|
||||
}
|
||||
#else
|
||||
#define spi_master_set_power_state(...)
|
||||
#endif
|
||||
|
||||
static int spi_qmsi_init(struct device *dev)
|
||||
{
|
||||
const struct spi_qmsi_config *spi_config = dev->config->config_info;
|
||||
struct spi_qmsi_runtime *context = dev->driver_data;
|
||||
|
||||
switch (spi_config->spi) {
|
||||
case QM_SPI_MST_0:
|
||||
IRQ_CONNECT(IRQ_GET_NUMBER(QM_IRQ_SPI_MASTER_0_INT),
|
||||
CONFIG_SPI_0_IRQ_PRI, qm_spi_master_0_isr,
|
||||
0, IOAPIC_LEVEL | IOAPIC_HIGH);
|
||||
irq_enable(IRQ_GET_NUMBER(QM_IRQ_SPI_MASTER_0_INT));
|
||||
clk_periph_enable(CLK_PERIPH_CLK | CLK_PERIPH_SPI_M0_REGISTER);
|
||||
QM_IR_UNMASK_INTERRUPTS(
|
||||
QM_INTERRUPT_ROUTER->spi_master_0_int_mask);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_SPI_1
|
||||
case QM_SPI_MST_1:
|
||||
IRQ_CONNECT(IRQ_GET_NUMBER(QM_IRQ_SPI_MASTER_1_INT),
|
||||
CONFIG_SPI_1_IRQ_PRI, qm_spi_master_1_isr,
|
||||
0, IOAPIC_LEVEL | IOAPIC_HIGH);
|
||||
irq_enable(IRQ_GET_NUMBER(QM_IRQ_SPI_MASTER_1_INT));
|
||||
clk_periph_enable(CLK_PERIPH_CLK | CLK_PERIPH_SPI_M1_REGISTER);
|
||||
QM_IR_UNMASK_INTERRUPTS(
|
||||
QM_INTERRUPT_ROUTER->spi_master_1_int_mask);
|
||||
break;
|
||||
#endif /* CONFIG_SPI_1 */
|
||||
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
context->gpio_cs = gpio_cs_init(spi_config);
|
||||
|
||||
k_sem_init(&context->device_sync_sem, 0, UINT_MAX);
|
||||
k_sem_init(&context->sem, 1, UINT_MAX);
|
||||
|
||||
spi_master_set_power_state(dev, DEVICE_PM_ACTIVE_STATE);
|
||||
|
||||
dev->driver_api = &spi_qmsi_api;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
static int spi_master_suspend_device(struct device *dev)
|
||||
{
|
||||
if (device_busy_check(dev)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
const struct spi_qmsi_config *config = dev->config->config_info;
|
||||
struct spi_qmsi_runtime *drv_data = dev->driver_data;
|
||||
|
||||
qm_spi_save_context(config->spi, &drv_data->spi_ctx);
|
||||
|
||||
spi_master_set_power_state(dev, DEVICE_PM_SUSPEND_STATE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_master_resume_device_from_suspend(struct device *dev)
|
||||
{
|
||||
const struct spi_qmsi_config *config = dev->config->config_info;
|
||||
struct spi_qmsi_runtime *drv_data = dev->driver_data;
|
||||
|
||||
qm_spi_restore_context(config->spi, &drv_data->spi_ctx);
|
||||
|
||||
spi_master_set_power_state(dev, DEVICE_PM_ACTIVE_STATE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements the driver control management functionality
|
||||
* the *context may include IN data or/and OUT data
|
||||
*/
|
||||
static int spi_master_qmsi_device_ctrl(struct device *port,
|
||||
u32_t ctrl_command, void *context)
|
||||
{
|
||||
if (ctrl_command == DEVICE_PM_SET_POWER_STATE) {
|
||||
if (*((u32_t *)context) == DEVICE_PM_SUSPEND_STATE) {
|
||||
return spi_master_suspend_device(port);
|
||||
} else if (*((u32_t *)context) == DEVICE_PM_ACTIVE_STATE) {
|
||||
return spi_master_resume_device_from_suspend(port);
|
||||
}
|
||||
} else if (ctrl_command == DEVICE_PM_GET_POWER_STATE) {
|
||||
*((u32_t *)context) = spi_master_get_power_state(port);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_DEVICE_POWER_MANAGEMENT */
|
||||
|
||||
#ifdef CONFIG_SPI_0
|
||||
static const struct spi_qmsi_config spi_qmsi_mst_0_config = {
|
||||
.spi = QM_SPI_MST_0,
|
||||
#ifdef CONFIG_SPI_CS_GPIO
|
||||
.cs_port = CONFIG_SPI_0_CS_GPIO_PORT,
|
||||
.cs_pin = CONFIG_SPI_0_CS_GPIO_PIN,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct spi_qmsi_runtime spi_qmsi_mst_0_runtime;
|
||||
|
||||
DEVICE_DEFINE(spi_master_0, CONFIG_SPI_0_NAME, spi_qmsi_init,
|
||||
spi_master_qmsi_device_ctrl, &spi_qmsi_mst_0_runtime,
|
||||
&spi_qmsi_mst_0_config, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY,
|
||||
NULL);
|
||||
#endif /* CONFIG_SPI_0 */
|
||||
|
||||
#ifdef CONFIG_SPI_1
|
||||
static const struct spi_qmsi_config spi_qmsi_mst_1_config = {
|
||||
.spi = QM_SPI_MST_1,
|
||||
#ifdef CONFIG_SPI_CS_GPIO
|
||||
.cs_port = CONFIG_SPI_1_CS_GPIO_PORT,
|
||||
.cs_pin = CONFIG_SPI_1_CS_GPIO_PIN,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct spi_qmsi_runtime spi_qmsi_mst_1_runtime;
|
||||
|
||||
DEVICE_DEFINE(spi_master_1, CONFIG_SPI_1_NAME, spi_qmsi_init,
|
||||
spi_master_qmsi_device_ctrl, &spi_qmsi_mst_1_runtime,
|
||||
&spi_qmsi_mst_1_config, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY,
|
||||
NULL);
|
||||
#endif /* CONFIG_SPI_1 */
|
|
@ -1,460 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Intel Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <device.h>
|
||||
#include <spi.h>
|
||||
#include <gpio.h>
|
||||
#include <board.h>
|
||||
|
||||
#include "qm_ss_spi.h"
|
||||
#include "qm_ss_isr.h"
|
||||
#include "ss_clk.h"
|
||||
|
||||
struct ss_pending_transfer {
|
||||
struct device *dev;
|
||||
qm_ss_spi_async_transfer_t xfer;
|
||||
};
|
||||
|
||||
static struct ss_pending_transfer pending_transfers[2];
|
||||
|
||||
struct ss_spi_qmsi_config {
|
||||
qm_ss_spi_t spi;
|
||||
#ifdef CONFIG_SPI_SS_CS_GPIO
|
||||
char *cs_port;
|
||||
u32_t cs_pin;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct ss_spi_qmsi_runtime {
|
||||
#ifdef CONFIG_SPI_SS_CS_GPIO
|
||||
struct device *gpio_cs;
|
||||
#endif
|
||||
struct k_sem device_sync_sem;
|
||||
struct k_sem sem;
|
||||
qm_ss_spi_config_t cfg;
|
||||
int rc;
|
||||
bool loopback;
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
u32_t device_power_state;
|
||||
qm_ss_spi_context_t spi_ctx;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline qm_ss_spi_bmode_t config_to_bmode(u8_t mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case SPI_MODE_CPHA:
|
||||
return QM_SS_SPI_BMODE_1;
|
||||
case SPI_MODE_CPOL:
|
||||
return QM_SS_SPI_BMODE_2;
|
||||
case SPI_MODE_CPOL | SPI_MODE_CPHA:
|
||||
return QM_SS_SPI_BMODE_3;
|
||||
default:
|
||||
return QM_SS_SPI_BMODE_0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPI_SS_CS_GPIO
|
||||
static void spi_control_cs(struct device *dev, bool active)
|
||||
{
|
||||
struct ss_spi_qmsi_runtime *context = dev->driver_data;
|
||||
const struct ss_spi_qmsi_config *config = dev->config->config_info;
|
||||
struct device *gpio = context->gpio_cs;
|
||||
|
||||
if (!gpio)
|
||||
return;
|
||||
|
||||
gpio_pin_write(gpio, config->cs_pin, !active);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ss_spi_qmsi_configure(struct device *dev,
|
||||
struct spi_config *config)
|
||||
{
|
||||
struct ss_spi_qmsi_runtime *context = dev->driver_data;
|
||||
qm_ss_spi_config_t *cfg = &context->cfg;
|
||||
|
||||
cfg->frame_size = SPI_WORD_SIZE_GET(config->config) - 1;
|
||||
cfg->bus_mode = config_to_bmode(SPI_MODE(config->config));
|
||||
/* As loopback is implemented inside the controller,
|
||||
* the bus mode doesn't matter.
|
||||
*/
|
||||
context->loopback = SPI_MODE(config->config) & SPI_MODE_LOOP;
|
||||
cfg->clk_divider = config->max_sys_freq;
|
||||
|
||||
/* Will set the configuration before the transfer starts */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_qmsi_callback(void *data, int error, qm_ss_spi_status_t status,
|
||||
u16_t len)
|
||||
{
|
||||
const struct ss_spi_qmsi_config *spi_config =
|
||||
((struct device *)data)->config->config_info;
|
||||
qm_ss_spi_t spi_id = spi_config->spi;
|
||||
struct ss_pending_transfer *pending = &pending_transfers[spi_id];
|
||||
struct device *dev = pending->dev;
|
||||
struct ss_spi_qmsi_runtime *context;
|
||||
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
context = dev->driver_data;
|
||||
|
||||
#ifdef CONFIG_SPI_SS_CS_GPIO
|
||||
spi_control_cs(dev, false);
|
||||
#endif
|
||||
|
||||
pending->dev = NULL;
|
||||
context->rc = error;
|
||||
k_sem_give(&context->device_sync_sem);
|
||||
}
|
||||
|
||||
static int ss_spi_qmsi_slave_select(struct device *dev, u32_t slave)
|
||||
{
|
||||
const struct ss_spi_qmsi_config *spi_config = dev->config->config_info;
|
||||
qm_ss_spi_t spi_id = spi_config->spi;
|
||||
|
||||
return qm_ss_spi_slave_select(spi_id, 1 << (slave - 1)) ? -EIO : 0;
|
||||
}
|
||||
|
||||
static inline u8_t frame_size_to_dfs(qm_ss_spi_frame_size_t frame_size)
|
||||
{
|
||||
if (frame_size <= QM_SS_SPI_FRAME_SIZE_8_BIT) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (frame_size <= QM_SS_SPI_FRAME_SIZE_16_BIT) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* This should never happen, it will crash later on. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ss_spi_qmsi_transceive(struct device *dev,
|
||||
const void *tx_buf, u32_t tx_buf_len,
|
||||
void *rx_buf, u32_t rx_buf_len)
|
||||
{
|
||||
const struct ss_spi_qmsi_config *spi_config = dev->config->config_info;
|
||||
qm_ss_spi_t spi_id = spi_config->spi;
|
||||
struct ss_spi_qmsi_runtime *context = dev->driver_data;
|
||||
qm_ss_spi_config_t *cfg = &context->cfg;
|
||||
u8_t dfs = frame_size_to_dfs(cfg->frame_size);
|
||||
qm_ss_spi_async_transfer_t *xfer;
|
||||
int rc;
|
||||
|
||||
k_sem_take(&context->sem, K_FOREVER);
|
||||
if (pending_transfers[spi_id].dev) {
|
||||
k_sem_give(&context->sem);
|
||||
return -EBUSY;
|
||||
}
|
||||
pending_transfers[spi_id].dev = dev;
|
||||
k_sem_give(&context->sem);
|
||||
|
||||
device_busy_set(dev);
|
||||
|
||||
xfer = &pending_transfers[spi_id].xfer;
|
||||
|
||||
xfer->rx = rx_buf;
|
||||
xfer->rx_len = rx_buf_len / dfs;
|
||||
xfer->tx = (u8_t *)tx_buf;
|
||||
xfer->tx_len = tx_buf_len / dfs;
|
||||
xfer->callback_data = dev;
|
||||
xfer->callback = spi_qmsi_callback;
|
||||
|
||||
if (tx_buf_len == 0) {
|
||||
cfg->transfer_mode = QM_SS_SPI_TMOD_RX;
|
||||
} else if (rx_buf_len == 0) {
|
||||
cfg->transfer_mode = QM_SS_SPI_TMOD_TX;
|
||||
} else {
|
||||
cfg->transfer_mode = QM_SS_SPI_TMOD_TX_RX;
|
||||
}
|
||||
|
||||
if (context->loopback) {
|
||||
u32_t ctrl;
|
||||
|
||||
if (spi_id == 0) {
|
||||
ctrl = __builtin_arc_lr(QM_SS_SPI_0_BASE +
|
||||
QM_SS_SPI_CTRL);
|
||||
ctrl |= BIT(11);
|
||||
__builtin_arc_sr(ctrl, QM_SS_SPI_0_BASE +
|
||||
QM_SS_SPI_CTRL);
|
||||
} else {
|
||||
ctrl = __builtin_arc_lr(QM_SS_SPI_1_BASE +
|
||||
QM_SS_SPI_CTRL);
|
||||
ctrl |= BIT(11);
|
||||
__builtin_arc_sr(ctrl, QM_SS_SPI_1_BASE +
|
||||
QM_SS_SPI_CTRL);
|
||||
}
|
||||
}
|
||||
|
||||
rc = qm_ss_spi_set_config(spi_id, cfg);
|
||||
if (rc != 0) {
|
||||
device_busy_clear(dev);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPI_SS_CS_GPIO
|
||||
spi_control_cs(dev, true);
|
||||
#endif
|
||||
|
||||
rc = qm_ss_spi_irq_transfer(spi_id, xfer);
|
||||
if (rc != 0) {
|
||||
#ifdef CONFIG_SPI_SS_CS_GPIO
|
||||
spi_control_cs(dev, false);
|
||||
#endif
|
||||
device_busy_clear(dev);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
k_sem_take(&context->device_sync_sem, K_FOREVER);
|
||||
|
||||
device_busy_clear(dev);
|
||||
return context->rc ? -EIO : 0;
|
||||
}
|
||||
|
||||
static const struct spi_driver_api ss_spi_qmsi_api = {
|
||||
.configure = ss_spi_qmsi_configure,
|
||||
.slave_select = ss_spi_qmsi_slave_select,
|
||||
.transceive = ss_spi_qmsi_transceive,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SPI_SS_CS_GPIO
|
||||
static struct device *gpio_cs_init(const struct ss_spi_qmsi_config *config)
|
||||
{
|
||||
struct device *gpio;
|
||||
|
||||
if (!config->cs_port)
|
||||
return NULL;
|
||||
|
||||
gpio = device_get_binding(config->cs_port);
|
||||
if (!gpio)
|
||||
return NULL;
|
||||
|
||||
gpio_pin_configure(gpio, config->cs_pin, GPIO_DIR_OUT);
|
||||
gpio_pin_write(gpio, config->cs_pin, 1);
|
||||
|
||||
return gpio;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ss_spi_qmsi_init(struct device *dev);
|
||||
|
||||
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
static void ss_spi_master_set_power_state(struct device *dev,
|
||||
u32_t power_state)
|
||||
{
|
||||
struct ss_spi_qmsi_runtime *context = dev->driver_data;
|
||||
|
||||
context->device_power_state = power_state;
|
||||
}
|
||||
|
||||
static u32_t ss_spi_master_get_power_state(struct device *dev)
|
||||
{
|
||||
struct ss_spi_qmsi_runtime *context = dev->driver_data;
|
||||
|
||||
return context->device_power_state;
|
||||
}
|
||||
|
||||
static int ss_spi_master_suspend_device(struct device *dev)
|
||||
{
|
||||
if (device_busy_check(dev)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
const struct ss_spi_qmsi_config *config = dev->config->config_info;
|
||||
struct ss_spi_qmsi_runtime *drv_data = dev->driver_data;
|
||||
|
||||
qm_ss_spi_save_context(config->spi, &drv_data->spi_ctx);
|
||||
|
||||
ss_spi_master_set_power_state(dev, DEVICE_PM_SUSPEND_STATE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ss_spi_master_resume_device_from_suspend(struct device *dev)
|
||||
{
|
||||
const struct ss_spi_qmsi_config *config = dev->config->config_info;
|
||||
struct ss_spi_qmsi_runtime *drv_data = dev->driver_data;
|
||||
|
||||
qm_ss_spi_restore_context(config->spi, &drv_data->spi_ctx);
|
||||
|
||||
ss_spi_master_set_power_state(dev, DEVICE_PM_ACTIVE_STATE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements the driver control management functionality
|
||||
* the *context may include IN data or/and OUT data
|
||||
*/
|
||||
static int ss_spi_master_qmsi_device_ctrl(struct device *port,
|
||||
u32_t ctrl_command, void *context)
|
||||
{
|
||||
if (ctrl_command == DEVICE_PM_SET_POWER_STATE) {
|
||||
if (*((u32_t *)context) == DEVICE_PM_SUSPEND_STATE) {
|
||||
return ss_spi_master_suspend_device(port);
|
||||
} else if (*((u32_t *)context) == DEVICE_PM_ACTIVE_STATE) {
|
||||
return ss_spi_master_resume_device_from_suspend(port);
|
||||
}
|
||||
} else if (ctrl_command == DEVICE_PM_GET_POWER_STATE) {
|
||||
*((u32_t *)context) = ss_spi_master_get_power_state(port);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define ss_spi_master_set_power_state(...)
|
||||
#endif /* CONFIG_DEVICE_POWER_MANAGEMENT */
|
||||
|
||||
#ifdef CONFIG_SPI_SS_0
|
||||
static const struct ss_spi_qmsi_config spi_qmsi_mst_0_config = {
|
||||
.spi = QM_SS_SPI_0,
|
||||
#ifdef CONFIG_SPI_SS_CS_GPIO
|
||||
.cs_port = CONFIG_SPI_SS_0_CS_GPIO_PORT,
|
||||
.cs_pin = CONFIG_SPI_SS_0_CS_GPIO_PIN,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct ss_spi_qmsi_runtime spi_qmsi_mst_0_runtime;
|
||||
|
||||
DEVICE_DEFINE(ss_spi_master_0, CONFIG_SPI_SS_0_NAME, ss_spi_qmsi_init,
|
||||
ss_spi_master_qmsi_device_ctrl, &spi_qmsi_mst_0_runtime,
|
||||
&spi_qmsi_mst_0_config, POST_KERNEL, CONFIG_SPI_SS_INIT_PRIORITY,
|
||||
NULL);
|
||||
#endif /* CONFIG_SPI_SS_0 */
|
||||
|
||||
#ifdef CONFIG_SPI_SS_1
|
||||
static const struct ss_spi_qmsi_config spi_qmsi_mst_1_config = {
|
||||
.spi = QM_SS_SPI_1,
|
||||
#ifdef CONFIG_SPI_SS_CS_GPIO
|
||||
.cs_port = CONFIG_SPI_SS_1_CS_GPIO_PORT,
|
||||
.cs_pin = CONFIG_SPI_SS_1_CS_GPIO_PIN,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct ss_spi_qmsi_runtime spi_qmsi_mst_1_runtime;
|
||||
|
||||
DEVICE_DEFINE(ss_spi_master_1, CONFIG_SPI_SS_1_NAME, ss_spi_qmsi_init,
|
||||
ss_spi_master_qmsi_device_ctrl, &spi_qmsi_mst_1_runtime,
|
||||
&spi_qmsi_mst_1_config, POST_KERNEL, CONFIG_SPI_SS_INIT_PRIORITY,
|
||||
NULL);
|
||||
#endif /* CONFIG_SPI_SS_1 */
|
||||
|
||||
static void ss_spi_err_isr(void *arg)
|
||||
{
|
||||
struct device *dev = arg;
|
||||
const struct ss_spi_qmsi_config *spi_config = dev->config->config_info;
|
||||
|
||||
if (spi_config->spi == QM_SS_SPI_0) {
|
||||
qm_ss_spi_0_error_isr(NULL);
|
||||
} else {
|
||||
qm_ss_spi_1_error_isr(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void ss_spi_rx_isr(void *arg)
|
||||
{
|
||||
struct device *dev = arg;
|
||||
const struct ss_spi_qmsi_config *spi_config = dev->config->config_info;
|
||||
|
||||
if (spi_config->spi == QM_SS_SPI_0) {
|
||||
qm_ss_spi_0_rx_avail_isr(NULL);
|
||||
} else {
|
||||
qm_ss_spi_1_rx_avail_isr(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void ss_spi_tx_isr(void *arg)
|
||||
{
|
||||
struct device *dev = arg;
|
||||
const struct ss_spi_qmsi_config *spi_config = dev->config->config_info;
|
||||
|
||||
if (spi_config->spi == QM_SS_SPI_0) {
|
||||
qm_ss_spi_0_tx_req_isr(NULL);
|
||||
} else {
|
||||
qm_ss_spi_1_tx_req_isr(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int ss_spi_qmsi_init(struct device *dev)
|
||||
{
|
||||
const struct ss_spi_qmsi_config *spi_config = dev->config->config_info;
|
||||
struct ss_spi_qmsi_runtime *context = dev->driver_data;
|
||||
u32_t *scss_intmask = NULL;
|
||||
|
||||
switch (spi_config->spi) {
|
||||
#ifdef CONFIG_SPI_SS_0
|
||||
case QM_SS_SPI_0:
|
||||
IRQ_CONNECT(IRQ_SPI0_ERR_INT, CONFIG_SPI_SS_0_IRQ_PRI,
|
||||
ss_spi_err_isr, DEVICE_GET(ss_spi_master_0), 0);
|
||||
irq_enable(IRQ_SPI0_ERR_INT);
|
||||
|
||||
IRQ_CONNECT(IRQ_SPI0_RX_AVAIL, CONFIG_SPI_SS_0_IRQ_PRI,
|
||||
ss_spi_rx_isr, DEVICE_GET(ss_spi_master_0), 0);
|
||||
irq_enable(IRQ_SPI0_RX_AVAIL);
|
||||
|
||||
IRQ_CONNECT(IRQ_SPI0_TX_REQ, CONFIG_SPI_SS_0_IRQ_PRI,
|
||||
ss_spi_tx_isr, DEVICE_GET(ss_spi_master_0), 0);
|
||||
irq_enable(IRQ_SPI0_TX_REQ);
|
||||
|
||||
ss_clk_spi_enable(0);
|
||||
|
||||
/* Route SPI interrupts to Sensor Subsystem */
|
||||
scss_intmask = (u32_t *)&QM_INTERRUPT_ROUTER->ss_spi_0_int;
|
||||
*scss_intmask &= ~BIT(8);
|
||||
scss_intmask++;
|
||||
*scss_intmask &= ~BIT(8);
|
||||
scss_intmask++;
|
||||
*scss_intmask &= ~BIT(8);
|
||||
break;
|
||||
#endif /* CONFIG_SPI_SS_0 */
|
||||
|
||||
#ifdef CONFIG_SPI_SS_1
|
||||
case QM_SS_SPI_1:
|
||||
IRQ_CONNECT(IRQ_SPI1_ERR_INT, CONFIG_SPI_SS_1_IRQ_PRI,
|
||||
ss_spi_err_isr, DEVICE_GET(ss_spi_master_1), 0);
|
||||
irq_enable(IRQ_SPI1_ERR_INT);
|
||||
|
||||
IRQ_CONNECT(IRQ_SPI1_RX_AVAIL, CONFIG_SPI_SS_1_IRQ_PRI,
|
||||
ss_spi_rx_isr, DEVICE_GET(ss_spi_master_1), 0);
|
||||
irq_enable(IRQ_SPI1_RX_AVAIL);
|
||||
|
||||
IRQ_CONNECT(IRQ_SPI1_TX_REQ, CONFIG_SPI_SS_1_IRQ_PRI,
|
||||
ss_spi_tx_isr, DEVICE_GET(ss_spi_master_1), 0);
|
||||
irq_enable(IRQ_SPI1_TX_REQ);
|
||||
|
||||
ss_clk_spi_enable(1);
|
||||
|
||||
/* Route SPI interrupts to Sensor Subsystem */
|
||||
scss_intmask = (u32_t *)&QM_INTERRUPT_ROUTER->ss_spi_1_int;
|
||||
*scss_intmask &= ~BIT(8);
|
||||
scss_intmask++;
|
||||
*scss_intmask &= ~BIT(8);
|
||||
scss_intmask++;
|
||||
*scss_intmask &= ~BIT(8);
|
||||
break;
|
||||
#endif /* CONFIG_SPI_SS_1 */
|
||||
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPI_SS_CS_GPIO
|
||||
context->gpio_cs = gpio_cs_init(spi_config);
|
||||
#endif
|
||||
k_sem_init(&context->device_sync_sem, 0, UINT_MAX);
|
||||
k_sem_init(&context->sem, 1, UINT_MAX);
|
||||
|
||||
ss_spi_master_set_power_state(dev, DEVICE_PM_ACTIVE_STATE);
|
||||
|
||||
dev->driver_api = &ss_spi_qmsi_api;
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue