drivers/spi: Adapt DW driver to new SPI API
Introducing as well a generic driver helper for CS gpio control and buffer management. Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
This commit is contained in:
parent
dd0c35919b
commit
19b36aea0c
7 changed files with 842 additions and 204 deletions
|
@ -1,5 +1,9 @@
|
||||||
obj-$(CONFIG_SPI_INTEL) += spi_intel.o
|
obj-$(CONFIG_SPI_INTEL) += spi_intel.o
|
||||||
|
ifeq ($(CONFIG_SPI_LEGACY_API),y)
|
||||||
|
obj-$(CONFIG_SPI_DW) += spi_dw_legacy.o
|
||||||
|
else
|
||||||
obj-$(CONFIG_SPI_DW) += spi_dw.o
|
obj-$(CONFIG_SPI_DW) += spi_dw.o
|
||||||
|
endif
|
||||||
obj-$(CONFIG_SPI_MCUX_DSPI) += spi_mcux_dspi.o
|
obj-$(CONFIG_SPI_MCUX_DSPI) += spi_mcux_dspi.o
|
||||||
obj-$(CONFIG_SPIM_NRF52) += spim_nrf52.o
|
obj-$(CONFIG_SPIM_NRF52) += spim_nrf52.o
|
||||||
obj-$(CONFIG_SPIS_NRF5) += spis_nrf5.o
|
obj-$(CONFIG_SPIS_NRF5) += spis_nrf5.o
|
||||||
|
|
161
drivers/spi/spi_context.h
Normal file
161
drivers/spi/spi_context.h
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017 Intel Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Private API for SPI drivers
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SPI_DRIVER_COMMON_H__
|
||||||
|
#define __SPI_DRIVER_COMMON_H__
|
||||||
|
|
||||||
|
#include <gpio.h>
|
||||||
|
#include <spi.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct spi_context {
|
||||||
|
struct spi_config *config;
|
||||||
|
|
||||||
|
const struct spi_buf **current_tx;
|
||||||
|
struct spi_buf **current_rx;
|
||||||
|
|
||||||
|
void *tx_buf;
|
||||||
|
u32_t tx_len;
|
||||||
|
void *rx_buf;
|
||||||
|
u32_t rx_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline bool spi_context_configured(struct spi_context *ctx,
|
||||||
|
struct spi_config *config)
|
||||||
|
{
|
||||||
|
return !!(ctx->config == config);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void spi_context_cs_configure(struct spi_context *ctx)
|
||||||
|
{
|
||||||
|
if (ctx->config->cs) {
|
||||||
|
gpio_pin_configure(ctx->config->cs->gpio_dev,
|
||||||
|
ctx->config->cs->gpio_pin, GPIO_DIR_OUT);
|
||||||
|
gpio_pin_write(ctx->config->cs->gpio_dev,
|
||||||
|
ctx->config->cs->gpio_pin, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void spi_context_cs_control(struct spi_context *ctx, bool on)
|
||||||
|
{
|
||||||
|
if (ctx->config->cs) {
|
||||||
|
if (on) {
|
||||||
|
gpio_pin_write(ctx->config->cs->gpio_dev,
|
||||||
|
ctx->config->cs->gpio_pin, 0);
|
||||||
|
k_busy_wait(ctx->config->cs->delay);
|
||||||
|
} else {
|
||||||
|
k_busy_wait(ctx->config->cs->delay);
|
||||||
|
gpio_pin_write(ctx->config->cs->gpio_dev,
|
||||||
|
ctx->config->cs->gpio_pin, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void spi_context_buffers_setup(struct spi_context *ctx,
|
||||||
|
const struct spi_buf **tx_bufs,
|
||||||
|
struct spi_buf **rx_bufs,
|
||||||
|
uint8_t dfs)
|
||||||
|
{
|
||||||
|
SYS_LOG_DBG("tx_bufs %p (%p) - rx_bufs %p (%p) - %u",
|
||||||
|
tx_bufs, tx_bufs ? *tx_bufs : NULL,
|
||||||
|
rx_bufs, rx_bufs ? *rx_bufs : NULL, dfs);
|
||||||
|
|
||||||
|
ctx->current_tx = tx_bufs;
|
||||||
|
ctx->current_rx = rx_bufs;
|
||||||
|
|
||||||
|
if (*tx_bufs) {
|
||||||
|
ctx->tx_buf = (*tx_bufs)->buf;
|
||||||
|
ctx->tx_len = (*tx_bufs)->len/dfs;
|
||||||
|
} else {
|
||||||
|
ctx->tx_buf = NULL;
|
||||||
|
ctx->tx_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*rx_bufs) {
|
||||||
|
ctx->rx_buf = (*rx_bufs)->buf;
|
||||||
|
ctx->rx_len = (*rx_bufs)->len/dfs;
|
||||||
|
} else {
|
||||||
|
ctx->rx_buf = NULL;
|
||||||
|
ctx->rx_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SYS_LOG_DBG("current_tx %p, current_rx %p,"
|
||||||
|
" tx buf/len %p/%u, rx buf/len %p/%u",
|
||||||
|
ctx->current_tx, ctx->current_rx,
|
||||||
|
ctx->tx_buf, ctx->tx_len, ctx->rx_buf, ctx->rx_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ALWAYS_INLINE
|
||||||
|
void spi_context_update_tx(struct spi_context *ctx, uint8_t dfs)
|
||||||
|
{
|
||||||
|
if (!ctx->tx_len) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->tx_len--;
|
||||||
|
if (!ctx->tx_len) {
|
||||||
|
ctx->current_tx++;
|
||||||
|
if (*ctx->current_tx) {
|
||||||
|
ctx->tx_buf = (*ctx->current_tx)->buf;
|
||||||
|
ctx->tx_len = (*ctx->current_tx)->len/dfs;
|
||||||
|
} else {
|
||||||
|
ctx->tx_buf = NULL;
|
||||||
|
}
|
||||||
|
} else if (ctx->tx_buf) {
|
||||||
|
ctx->tx_buf += dfs;
|
||||||
|
}
|
||||||
|
|
||||||
|
SYS_LOG_DBG("tx buf/len %p/%u", ctx->tx_buf, ctx->tx_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ALWAYS_INLINE
|
||||||
|
bool spi_context_tx_on(struct spi_context *ctx)
|
||||||
|
{
|
||||||
|
return !!(ctx->tx_buf || ctx->tx_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ALWAYS_INLINE
|
||||||
|
void spi_context_update_rx(struct spi_context *ctx, uint8_t dfs)
|
||||||
|
{
|
||||||
|
if (!ctx->rx_len) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->rx_len--;
|
||||||
|
if (!ctx->rx_len) {
|
||||||
|
ctx->current_rx++;
|
||||||
|
if (*ctx->current_rx) {
|
||||||
|
ctx->rx_buf = (*ctx->current_rx)->buf;
|
||||||
|
ctx->rx_len = (*ctx->current_rx)->len/dfs;
|
||||||
|
} else {
|
||||||
|
ctx->rx_buf = NULL;
|
||||||
|
}
|
||||||
|
} else if (ctx->rx_buf) {
|
||||||
|
ctx->rx_buf += dfs;
|
||||||
|
}
|
||||||
|
|
||||||
|
SYS_LOG_DBG("rx buf/len %p/%u", ctx->rx_buf, ctx->rx_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ALWAYS_INLINE
|
||||||
|
bool spi_context_rx_on(struct spi_context *ctx)
|
||||||
|
{
|
||||||
|
return !!(ctx->rx_buf || ctx->rx_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __SPI_DRIVER_COMMON_H__ */
|
|
@ -5,28 +5,6 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <kernel.h>
|
|
||||||
#include <arch/cpu.h>
|
|
||||||
|
|
||||||
#include <misc/__assert.h>
|
|
||||||
#include <board.h>
|
|
||||||
#include <device.h>
|
|
||||||
#include <init.h>
|
|
||||||
|
|
||||||
#include <sys_io.h>
|
|
||||||
#include <clock_control.h>
|
|
||||||
#include <misc/util.h>
|
|
||||||
|
|
||||||
#include <spi.h>
|
|
||||||
#include <spi_dw.h>
|
|
||||||
|
|
||||||
#ifdef CONFIG_IOAPIC
|
|
||||||
#include <drivers/ioapic.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SYS_LOG_DOMAIN "SPI DW"
|
#define SYS_LOG_DOMAIN "SPI DW"
|
||||||
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_SPI_LEVEL
|
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_SPI_LEVEL
|
||||||
#include <logging/sys_log.h>
|
#include <logging/sys_log.h>
|
||||||
|
@ -44,16 +22,29 @@
|
||||||
#define DBG_COUNTER_RESULT() 0
|
#define DBG_COUNTER_RESULT() 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SPI_DW_SPI_CLOCK
|
#include <errno.h>
|
||||||
#define SPI_DW_CLK_DIVIDER(ssi_clk_hz) \
|
|
||||||
((SPI_DW_SPI_CLOCK / ssi_clk_hz) & 0xFFFF)
|
#include <kernel.h>
|
||||||
/* provision for soc.h providing a clock that is different than CPU clock */
|
#include <arch/cpu.h>
|
||||||
#else
|
|
||||||
#define SPI_DW_CLK_DIVIDER(ssi_clk_hz) \
|
#include <board.h>
|
||||||
((CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / ssi_clk_hz) & 0xFFFF)
|
#include <device.h>
|
||||||
|
#include <init.h>
|
||||||
|
|
||||||
|
#include <sys_io.h>
|
||||||
|
#include <clock_control.h>
|
||||||
|
#include <misc/util.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_IOAPIC
|
||||||
|
#include <drivers/ioapic.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void completed(struct device *dev, int error)
|
#include <spi.h>
|
||||||
|
|
||||||
|
#include "spi_dw.h"
|
||||||
|
#include "spi_context.h"
|
||||||
|
|
||||||
|
static void completed(struct device *dev, uint8_t error)
|
||||||
{
|
{
|
||||||
const struct spi_dw_config *info = dev->config->config_info;
|
const struct spi_dw_config *info = dev->config->config_info;
|
||||||
struct spi_dw_data *spi = dev->driver_data;
|
struct spi_dw_data *spi = dev->driver_data;
|
||||||
|
@ -62,24 +53,9 @@ static void completed(struct device *dev, int error)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (spi_context_tx_on(&spi->ctx) ||
|
||||||
* There are several situations here.
|
spi_context_rx_on(&spi->ctx)) {
|
||||||
* 1. spi_write w rx_buf - need last_tx && rx_buf_len zero to be done.
|
return;
|
||||||
* 2. spi_write w/o rx_buf - only need to determine when write is done.
|
|
||||||
* 3. spi_read - need rx_buf_len zero.
|
|
||||||
*/
|
|
||||||
if (spi->tx_buf && spi->rx_buf) {
|
|
||||||
if (!spi->last_tx || spi->rx_buf_len) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (spi->tx_buf) {
|
|
||||||
if (!spi->last_tx) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else { /* or, spi->rx_buf!=0 */
|
|
||||||
if (spi->rx_buf_len) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -94,7 +70,7 @@ out:
|
||||||
/* Disabling the controller */
|
/* Disabling the controller */
|
||||||
clear_bit_ssienr(info->regs);
|
clear_bit_ssienr(info->regs);
|
||||||
|
|
||||||
_spi_control_cs(dev, 0);
|
spi_context_cs_control(&spi->ctx, false);
|
||||||
|
|
||||||
SYS_LOG_DBG("SPI transaction completed %s error",
|
SYS_LOG_DBG("SPI transaction completed %s error",
|
||||||
error ? "with" : "without");
|
error ? "with" : "without");
|
||||||
|
@ -108,11 +84,12 @@ static void push_data(struct device *dev)
|
||||||
struct spi_dw_data *spi = dev->driver_data;
|
struct spi_dw_data *spi = dev->driver_data;
|
||||||
u32_t data = 0;
|
u32_t data = 0;
|
||||||
u32_t f_tx;
|
u32_t f_tx;
|
||||||
|
|
||||||
DBG_COUNTER_INIT();
|
DBG_COUNTER_INIT();
|
||||||
|
|
||||||
if (spi->rx_buf) {
|
if (spi_context_rx_on(&spi->ctx)) {
|
||||||
f_tx = DW_SPI_FIFO_DEPTH - read_txflr(info->regs) -
|
f_tx = DW_SPI_FIFO_DEPTH - read_txflr(info->regs) -
|
||||||
read_rxflr(info->regs);
|
read_rxflr(info->regs);
|
||||||
if ((int)f_tx < 0) {
|
if ((int)f_tx < 0) {
|
||||||
f_tx = 0; /* if rx-fifo is full, hold off tx */
|
f_tx = 0; /* if rx-fifo is full, hold off tx */
|
||||||
}
|
}
|
||||||
|
@ -120,32 +97,27 @@ static void push_data(struct device *dev)
|
||||||
f_tx = DW_SPI_FIFO_DEPTH - read_txflr(info->regs);
|
f_tx = DW_SPI_FIFO_DEPTH - read_txflr(info->regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f_tx && (spi->tx_buf_len == 0)) {
|
|
||||||
/* room in fifo, yet nothing to send */
|
|
||||||
spi->last_tx = 1; /* setting last_tx indicates TX is done */
|
|
||||||
}
|
|
||||||
|
|
||||||
while (f_tx) {
|
while (f_tx) {
|
||||||
if (spi->tx_buf && spi->tx_buf_len > 0) {
|
if (spi_context_tx_on(&spi->ctx)) {
|
||||||
switch (spi->dfs) {
|
switch (spi->dfs) {
|
||||||
case 1:
|
case 1:
|
||||||
data = UNALIGNED_GET((u8_t *)(spi->tx_buf));
|
data = UNALIGNED_GET((u8_t *)
|
||||||
|
(spi->ctx.tx_buf));
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
data = UNALIGNED_GET((u16_t *)(spi->tx_buf));
|
data = UNALIGNED_GET((u16_t *)
|
||||||
|
(spi->ctx.tx_buf));
|
||||||
break;
|
break;
|
||||||
#ifndef CONFIG_ARC
|
#ifndef CONFIG_ARC
|
||||||
case 4:
|
case 4:
|
||||||
data = UNALIGNED_GET((u32_t *)(spi->tx_buf));
|
data = UNALIGNED_GET((u32_t *)
|
||||||
|
(spi->ctx.tx_buf));
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
} else if (spi_context_rx_on(&spi->ctx)) {
|
||||||
spi->tx_buf += spi->dfs;
|
|
||||||
spi->tx_buf_len--;
|
|
||||||
} else if (spi->rx_buf && spi->rx_buf_len > 0) {
|
|
||||||
/* No need to push more than necessary */
|
/* No need to push more than necessary */
|
||||||
if (spi->rx_buf_len - spi->fifo_diff <= 0) {
|
if ((int)(spi->ctx.rx_len - spi->fifo_diff) <= 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,14 +128,18 @@ static void push_data(struct device *dev)
|
||||||
}
|
}
|
||||||
|
|
||||||
write_dr(data, info->regs);
|
write_dr(data, info->regs);
|
||||||
f_tx--;
|
|
||||||
|
spi_context_update_tx(&spi->ctx, spi->dfs);
|
||||||
spi->fifo_diff++;
|
spi->fifo_diff++;
|
||||||
|
|
||||||
|
f_tx--;
|
||||||
|
|
||||||
DBG_COUNTER_INC();
|
DBG_COUNTER_INC();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spi->last_tx) {
|
if (!spi_context_tx_on(&spi->ctx)) {
|
||||||
write_txftlr(0, info->regs);
|
|
||||||
/* prevents any further interrupts demanding TX fifo fill */
|
/* prevents any further interrupts demanding TX fifo fill */
|
||||||
|
write_txftlr(0, info->regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
SYS_LOG_DBG("Pushed: %d", DBG_COUNTER_RESULT());
|
SYS_LOG_DBG("Pushed: %d", DBG_COUNTER_RESULT());
|
||||||
|
@ -173,186 +149,156 @@ static void pull_data(struct device *dev)
|
||||||
{
|
{
|
||||||
const struct spi_dw_config *info = dev->config->config_info;
|
const struct spi_dw_config *info = dev->config->config_info;
|
||||||
struct spi_dw_data *spi = dev->driver_data;
|
struct spi_dw_data *spi = dev->driver_data;
|
||||||
u32_t data = 0;
|
|
||||||
DBG_COUNTER_INIT();
|
DBG_COUNTER_INIT();
|
||||||
|
|
||||||
while (read_rxflr(info->regs)) {
|
while (read_rxflr(info->regs)) {
|
||||||
data = read_dr(info->regs);
|
u32_t data = read_dr(info->regs);
|
||||||
|
|
||||||
DBG_COUNTER_INC();
|
DBG_COUNTER_INC();
|
||||||
|
|
||||||
if (spi->rx_buf && spi->rx_buf_len > 0) {
|
if (spi_context_rx_on(&spi->ctx)) {
|
||||||
switch (spi->dfs) {
|
switch (spi->dfs) {
|
||||||
case 1:
|
case 1:
|
||||||
UNALIGNED_PUT(data, (u8_t *)spi->rx_buf);
|
UNALIGNED_PUT(data, (u8_t *)spi->ctx.rx_buf);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
UNALIGNED_PUT(data, (u16_t *)spi->rx_buf);
|
UNALIGNED_PUT(data, (u16_t *)spi->ctx.rx_buf);
|
||||||
break;
|
break;
|
||||||
#ifndef CONFIG_ARC
|
#ifndef CONFIG_ARC
|
||||||
case 4:
|
case 4:
|
||||||
UNALIGNED_PUT(data, (u32_t *)spi->rx_buf);
|
UNALIGNED_PUT(data, (u32_t *)spi->ctx.rx_buf);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
spi->rx_buf += spi->dfs;
|
|
||||||
spi->rx_buf_len--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spi_context_update_rx(&spi->ctx, spi->dfs);
|
||||||
spi->fifo_diff--;
|
spi->fifo_diff--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!spi->rx_buf_len && spi->tx_buf_len < DW_SPI_FIFO_DEPTH) {
|
if (!spi->ctx.rx_len && spi->ctx.tx_len < DW_SPI_FIFO_DEPTH) {
|
||||||
write_rxftlr(spi->tx_buf_len - 1, info->regs);
|
write_rxftlr(spi->ctx.tx_len - 1, info->regs);
|
||||||
} else if (read_rxftlr(info->regs) >= spi->rx_buf_len) {
|
} else if (read_rxftlr(info->regs) >= spi->ctx.rx_len) {
|
||||||
write_rxftlr(spi->rx_buf_len - 1, info->regs);
|
write_rxftlr(spi->ctx.rx_len - 1, info->regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
SYS_LOG_DBG("Pulled: %d", DBG_COUNTER_RESULT());
|
SYS_LOG_DBG("Pulled: %d", DBG_COUNTER_RESULT());
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool _spi_dw_is_controller_ready(struct device *dev)
|
static int spi_dw_configure(const struct spi_dw_config *info,
|
||||||
{
|
struct spi_dw_data *spi,
|
||||||
const struct spi_dw_config *info = dev->config->config_info;
|
|
||||||
|
|
||||||
if (test_bit_ssienr(info->regs) || test_bit_sr_busy(info->regs)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int spi_dw_configure(struct device *dev,
|
|
||||||
struct spi_config *config)
|
struct spi_config *config)
|
||||||
{
|
{
|
||||||
const struct spi_dw_config *info = dev->config->config_info;
|
|
||||||
struct spi_dw_data *spi = dev->driver_data;
|
|
||||||
u32_t flags = config->config;
|
|
||||||
u32_t ctrlr0 = 0;
|
u32_t ctrlr0 = 0;
|
||||||
u32_t mode;
|
|
||||||
|
|
||||||
SYS_LOG_DBG("%p (0x%x), %p", dev, info->regs, config);
|
SYS_LOG_DBG("%p (prev %p)", config, spi->ctx.config);
|
||||||
|
|
||||||
/* Check status */
|
if (spi_context_configured(&spi->ctx, config)) {
|
||||||
if (!_spi_dw_is_controller_ready(dev)) {
|
/* Nothing to do */
|
||||||
SYS_LOG_DBG("Controller is busy");
|
return 0;
|
||||||
return -EBUSY;
|
}
|
||||||
|
|
||||||
|
if (config->operation & (SPI_OP_MODE_SLAVE || SPI_TRANSFER_LSB
|
||||||
|
|| SPI_LINES_DUAL || SPI_LINES_QUAD)) {
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Word size */
|
/* Word size */
|
||||||
ctrlr0 |= DW_SPI_CTRLR0_DFS(SPI_WORD_SIZE_GET(flags));
|
ctrlr0 |= DW_SPI_CTRLR0_DFS(SPI_WORD_SIZE_GET(config->operation));
|
||||||
|
|
||||||
/* Determine how many bytes are required per-frame */
|
/* Determine how many bytes are required per-frame */
|
||||||
spi->dfs = SPI_DFS_TO_BYTES(SPI_WORD_SIZE_GET(flags));
|
spi->dfs = SPI_WS_TO_DFS(SPI_WORD_SIZE_GET(config->operation));
|
||||||
|
|
||||||
/* SPI mode */
|
/* SPI mode */
|
||||||
mode = SPI_MODE(flags);
|
if (SPI_MODE_GET(config->operation) & SPI_MODE_CPOL) {
|
||||||
if (mode & SPI_MODE_CPOL) {
|
|
||||||
ctrlr0 |= DW_SPI_CTRLR0_SCPOL;
|
ctrlr0 |= DW_SPI_CTRLR0_SCPOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode & SPI_MODE_CPHA) {
|
if (SPI_MODE_GET(config->operation) & SPI_MODE_CPHA) {
|
||||||
ctrlr0 |= DW_SPI_CTRLR0_SCPH;
|
ctrlr0 |= DW_SPI_CTRLR0_SCPH;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode & SPI_MODE_LOOP) {
|
if (SPI_MODE_GET(config->operation) & SPI_MODE_LOOP) {
|
||||||
ctrlr0 |= DW_SPI_CTRLR0_SRL;
|
ctrlr0 |= DW_SPI_CTRLR0_SRL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Installing the configuration */
|
/* Installing the configuration */
|
||||||
write_ctrlr0(ctrlr0, info->regs);
|
write_ctrlr0(ctrlr0, info->regs);
|
||||||
|
|
||||||
/*
|
/* Setting up baud rate */
|
||||||
* Configure the rate. Use this small hack to allow the user to call
|
write_baudr(SPI_DW_CLK_DIVIDER(config->frequency), info->regs);
|
||||||
* spi_configure() with both a divider (as the driver was initially
|
|
||||||
* written) and a frequency (as the SPI API suggests to). The clock
|
|
||||||
* divider is a 16bit value, hence we can fairly, and safely, assume
|
|
||||||
* that everything above this value is a frequency. The trade-off is
|
|
||||||
* that if one wants to use a bus frequency of 64kHz (or less), it has
|
|
||||||
* the use a divider...
|
|
||||||
*/
|
|
||||||
if (config->max_sys_freq > 0xffff) {
|
|
||||||
write_baudr(SPI_DW_CLK_DIVIDER(config->max_sys_freq),
|
|
||||||
info->regs);
|
|
||||||
} else {
|
|
||||||
write_baudr(config->max_sys_freq, info->regs);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
/* Slave select */
|
||||||
}
|
write_ser(config->slave, info->regs);
|
||||||
|
|
||||||
static int spi_dw_slave_select(struct device *dev, u32_t slave)
|
/* At this point, it's mandatory to set this on the context! */
|
||||||
{
|
spi->ctx.config = config;
|
||||||
struct spi_dw_data *spi = dev->driver_data;
|
|
||||||
|
|
||||||
SYS_LOG_DBG("%p %d", dev, slave);
|
spi_context_cs_configure(&spi->ctx);
|
||||||
|
|
||||||
if (slave == 0 || slave > 16) {
|
SYS_LOG_DBG("Installed config %p: freq %uHz (div = %u),"
|
||||||
return -EINVAL;
|
" ws/dfs %u/%u, mode %u/%u/%u, slave %u",
|
||||||
}
|
config, config->frequency,
|
||||||
|
SPI_DW_CLK_DIVIDER(config->frequency), spi->dfs,
|
||||||
spi->slave = 1 << (slave - 1);
|
SPI_WORD_SIZE_GET(config->operation),
|
||||||
|
(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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int spi_dw_transceive(struct device *dev,
|
static int spi_dw_transceive(struct device *dev,
|
||||||
const void *tx_buf, u32_t tx_buf_len,
|
struct spi_config *config,
|
||||||
void *rx_buf, u32_t rx_buf_len)
|
const struct spi_buf **tx_bufs,
|
||||||
|
struct spi_buf **rx_bufs)
|
||||||
{
|
{
|
||||||
const struct spi_dw_config *info = dev->config->config_info;
|
const struct spi_dw_config *info = dev->config->config_info;
|
||||||
struct spi_dw_data *spi = dev->driver_data;
|
struct spi_dw_data *spi = dev->driver_data;
|
||||||
u32_t rx_thsld = DW_SPI_RXFTLR_DFLT;
|
u32_t rx_thsld = DW_SPI_RXFTLR_DFLT;
|
||||||
u32_t imask;
|
u32_t imask = DW_SPI_IMR_UNMASK;
|
||||||
|
|
||||||
SYS_LOG_DBG("%p, %p, %u, %p, %u",
|
SYS_LOG_DBG("%p, %p, %p", dev, tx_bufs, rx_bufs);
|
||||||
dev, tx_buf, tx_buf_len, rx_buf, rx_buf_len);
|
|
||||||
|
|
||||||
/* Check status */
|
/* Check status */
|
||||||
if (!_spi_dw_is_controller_ready(dev)) {
|
if (test_bit_ssienr(info->regs) || test_bit_sr_busy(info->regs)) {
|
||||||
SYS_LOG_DBG("Controller is busy");
|
SYS_LOG_DBG("Controller is busy");
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set buffers info */
|
/* Configure */
|
||||||
spi->tx_buf = tx_buf;
|
if (spi_dw_configure(info, spi, config)) {
|
||||||
spi->tx_buf_len = tx_buf_len/spi->dfs;
|
return -EINVAL;
|
||||||
spi->rx_buf = rx_buf;
|
|
||||||
if (rx_buf) {
|
|
||||||
spi->rx_buf_len = rx_buf_len/spi->dfs;
|
|
||||||
} else {
|
|
||||||
spi->rx_buf_len = 0; /* must be zero if no buffer */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set buffers info */
|
||||||
|
spi_context_buffers_setup(&spi->ctx, tx_bufs, rx_bufs, spi->dfs);
|
||||||
|
|
||||||
spi->fifo_diff = 0;
|
spi->fifo_diff = 0;
|
||||||
spi->last_tx = 0;
|
|
||||||
|
|
||||||
/* Tx Threshold */
|
/* Tx Threshold */
|
||||||
write_txftlr(DW_SPI_TXFTLR_DFLT, info->regs);
|
write_txftlr(DW_SPI_TXFTLR_DFLT, info->regs);
|
||||||
|
|
||||||
/* Does Rx thresholds needs to be lower? */
|
/* Does Rx thresholds needs to be lower? */
|
||||||
if (spi->rx_buf_len && spi->rx_buf_len < DW_SPI_FIFO_DEPTH) {
|
if (spi->ctx.rx_len && spi->ctx.rx_len < DW_SPI_FIFO_DEPTH) {
|
||||||
rx_thsld = spi->rx_buf_len - 1;
|
rx_thsld = spi->ctx.rx_len - 1;
|
||||||
} else if (!spi->rx_buf_len && spi->tx_buf_len < DW_SPI_FIFO_DEPTH) {
|
|
||||||
rx_thsld = spi->tx_buf_len - 1;
|
|
||||||
/* TODO: why? */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Rx Threshold */
|
||||||
write_rxftlr(rx_thsld, info->regs);
|
write_rxftlr(rx_thsld, info->regs);
|
||||||
|
|
||||||
/* Slave select */
|
if (!rx_bufs) {
|
||||||
write_ser(spi->slave, info->regs);
|
|
||||||
|
|
||||||
_spi_control_cs(dev, 1);
|
|
||||||
|
|
||||||
/* Enable interrupts */
|
|
||||||
imask = DW_SPI_IMR_UNMASK;
|
|
||||||
if (!rx_buf) {
|
|
||||||
/* if there is no rx buffer, keep all rx interrupts masked */
|
/* if there is no rx buffer, keep all rx interrupts masked */
|
||||||
imask &= DW_SPI_IMR_MASK_RX;
|
imask &= DW_SPI_IMR_MASK_RX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Enable interrupts */
|
||||||
write_imr(imask, info->regs);
|
write_imr(imask, info->regs);
|
||||||
|
|
||||||
|
spi_context_cs_control(&spi->ctx, true);
|
||||||
|
|
||||||
/* Enable the controller */
|
/* Enable the controller */
|
||||||
set_bit_ssienr(info->regs);
|
set_bit_ssienr(info->regs);
|
||||||
|
|
||||||
|
@ -366,12 +312,11 @@ static int spi_dw_transceive(struct device *dev,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void spi_dw_isr(void *arg)
|
void spi_dw_isr(struct device *dev)
|
||||||
{
|
{
|
||||||
struct device *dev = (struct device *)arg;
|
|
||||||
const struct spi_dw_config *info = dev->config->config_info;
|
const struct spi_dw_config *info = dev->config->config_info;
|
||||||
u32_t error = 0;
|
|
||||||
u32_t int_status;
|
u32_t int_status;
|
||||||
|
u8_t error;
|
||||||
|
|
||||||
int_status = read_isr(info->regs);
|
int_status = read_isr(info->regs);
|
||||||
|
|
||||||
|
@ -383,6 +328,8 @@ void spi_dw_isr(void *arg)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error = 0;
|
||||||
|
|
||||||
if (int_status & DW_SPI_ISR_RXFIS) {
|
if (int_status & DW_SPI_ISR_RXFIS) {
|
||||||
pull_data(dev);
|
pull_data(dev);
|
||||||
}
|
}
|
||||||
|
@ -397,8 +344,6 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct spi_driver_api dw_spi_api = {
|
static const struct spi_driver_api dw_spi_api = {
|
||||||
.configure = spi_dw_configure,
|
|
||||||
.slave_select = spi_dw_slave_select,
|
|
||||||
.transceive = spi_dw_transceive,
|
.transceive = spi_dw_transceive,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -410,22 +355,10 @@ int spi_dw_init(struct device *dev)
|
||||||
_clock_config(dev);
|
_clock_config(dev);
|
||||||
_clock_on(dev);
|
_clock_on(dev);
|
||||||
|
|
||||||
#if 0 /* TODO: Not correct version for every target. Don't check. */
|
|
||||||
#ifndef CONFIG_SOC_QUARK_SE_C1000_SS
|
|
||||||
if (read_ssi_comp_version(info->regs) != DW_SSI_COMP_VERSION) {
|
|
||||||
dev->driver_api = NULL;
|
|
||||||
_clock_off(dev);
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
info->config_func();
|
info->config_func();
|
||||||
|
|
||||||
k_sem_init(&spi->device_sync_sem, 0, UINT_MAX);
|
k_sem_init(&spi->device_sync_sem, 0, UINT_MAX);
|
||||||
|
|
||||||
_spi_config_cs(dev);
|
|
||||||
|
|
||||||
/* Masking interrupt and making sure controller is disabled */
|
/* Masking interrupt and making sure controller is disabled */
|
||||||
write_imr(DW_SPI_IMR_MASK, info->regs);
|
write_imr(DW_SPI_IMR_MASK, info->regs);
|
||||||
clear_bit_ssienr(info->regs);
|
clear_bit_ssienr(info->regs);
|
||||||
|
@ -446,10 +379,6 @@ const struct spi_dw_config spi_dw_config_0 = {
|
||||||
#ifdef CONFIG_SPI_DW_CLOCK_GATE
|
#ifdef CONFIG_SPI_DW_CLOCK_GATE
|
||||||
.clock_data = UINT_TO_POINTER(CONFIG_SPI_0_CLOCK_GATE_SUBSYS),
|
.clock_data = UINT_TO_POINTER(CONFIG_SPI_0_CLOCK_GATE_SUBSYS),
|
||||||
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
|
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
|
||||||
#ifdef CONFIG_SPI_DW_CS_GPIO
|
|
||||||
.cs_gpio_name = CONFIG_SPI_0_CS_GPIO_PORT,
|
|
||||||
.cs_gpio_pin = CONFIG_SPI_0_CS_GPIO_PIN,
|
|
||||||
#endif
|
|
||||||
.config_func = spi_config_0_irq
|
.config_func = spi_config_0_irq
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -493,10 +422,6 @@ static const struct spi_dw_config spi_dw_config_1 = {
|
||||||
#ifdef CONFIG_SPI_DW_CLOCK_GATE
|
#ifdef CONFIG_SPI_DW_CLOCK_GATE
|
||||||
.clock_data = UINT_TO_POINTER(CONFIG_SPI_1_CLOCK_GATE_SUBSYS),
|
.clock_data = UINT_TO_POINTER(CONFIG_SPI_1_CLOCK_GATE_SUBSYS),
|
||||||
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
|
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
|
||||||
#ifdef CONFIG_SPI_DW_CS_GPIO
|
|
||||||
.cs_gpio_name = CONFIG_SPI_1_CS_GPIO_PORT,
|
|
||||||
.cs_gpio_pin = CONFIG_SPI_1_CS_GPIO_PIN,
|
|
||||||
#endif
|
|
||||||
.config_func = spi_config_1_irq
|
.config_func = spi_config_1_irq
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -23,13 +23,10 @@ struct spi_dw_config {
|
||||||
#ifdef CONFIG_SPI_DW_CLOCK_GATE
|
#ifdef CONFIG_SPI_DW_CLOCK_GATE
|
||||||
void *clock_data;
|
void *clock_data;
|
||||||
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
|
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
|
||||||
#ifdef CONFIG_SPI_DW_CS_GPIO
|
|
||||||
char *cs_gpio_name;
|
|
||||||
u32_t cs_gpio_pin;
|
|
||||||
#endif /* CONFIG_SPI_DW_CS_GPIO */
|
|
||||||
spi_dw_config_t config_func;
|
spi_dw_config_t config_func;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if defined(CONFIG_SPI_LEGACY_API)
|
||||||
struct spi_dw_data {
|
struct spi_dw_data {
|
||||||
struct k_sem device_sync_sem;
|
struct k_sem device_sync_sem;
|
||||||
u32_t error:1;
|
u32_t error:1;
|
||||||
|
@ -41,17 +38,40 @@ struct spi_dw_data {
|
||||||
#ifdef CONFIG_SPI_DW_CLOCK_GATE
|
#ifdef CONFIG_SPI_DW_CLOCK_GATE
|
||||||
struct device *clock;
|
struct device *clock;
|
||||||
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
|
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
|
||||||
#ifdef CONFIG_SPI_DW_CS_GPIO
|
|
||||||
struct device *cs_gpio_port;
|
|
||||||
#endif /* CONFIG_SPI_DW_CS_GPIO */
|
|
||||||
const u8_t *tx_buf;
|
const u8_t *tx_buf;
|
||||||
u32_t tx_buf_len;
|
u32_t tx_buf_len;
|
||||||
u8_t *rx_buf;
|
u8_t *rx_buf;
|
||||||
u32_t rx_buf_len;
|
u32_t rx_buf_len;
|
||||||
};
|
};
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include "spi_context.h"
|
||||||
|
|
||||||
|
struct spi_dw_data {
|
||||||
|
#ifdef CONFIG_SPI_DW_CLOCK_GATE
|
||||||
|
struct device *clock;
|
||||||
|
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
|
||||||
|
struct k_sem device_sync_sem;
|
||||||
|
struct spi_context ctx;
|
||||||
|
u8_t error;
|
||||||
|
u8_t dfs; /* dfs in bytes: 1,2 or 4 */
|
||||||
|
u8_t fifo_diff; /* cannot be bigger than FIFO depth */
|
||||||
|
u8_t _unused;
|
||||||
|
};
|
||||||
|
#endif /* CONFIG_SPI_LEGACY_API */
|
||||||
|
|
||||||
/* Helper macros */
|
/* Helper macros */
|
||||||
|
|
||||||
|
#ifdef SPI_DW_SPI_CLOCK
|
||||||
|
#define SPI_DW_CLK_DIVIDER(ssi_clk_hz) \
|
||||||
|
((SPI_DW_SPI_CLOCK / ssi_clk_hz) & 0xFFFF)
|
||||||
|
/* provision for soc.h providing a clock that is different than CPU clock */
|
||||||
|
#else
|
||||||
|
#define SPI_DW_CLK_DIVIDER(ssi_clk_hz) \
|
||||||
|
((CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / ssi_clk_hz) & 0xFFFF)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifdef CONFIG_SPI_DW_ARC_AUX_REGS
|
#ifdef CONFIG_SPI_DW_ARC_AUX_REGS
|
||||||
#define _REG_READ(__sz) sys_in##__sz
|
#define _REG_READ(__sz) sys_in##__sz
|
||||||
#define _REG_WRITE(__sz) sys_out##__sz
|
#define _REG_WRITE(__sz) sys_out##__sz
|
||||||
|
@ -67,7 +87,7 @@ struct spi_dw_data {
|
||||||
#endif /* CONFIG_SPI_DW_ARC_AUX_REGS */
|
#endif /* CONFIG_SPI_DW_ARC_AUX_REGS */
|
||||||
|
|
||||||
#define DEFINE_MM_REG_READ(__reg, __off, __sz) \
|
#define DEFINE_MM_REG_READ(__reg, __off, __sz) \
|
||||||
static inline u32_t read_##__reg(u32_t addr) \
|
static inline u32_t read_##__reg(u32_t addr) \
|
||||||
{ \
|
{ \
|
||||||
return _REG_READ(__sz)(addr + __off); \
|
return _REG_READ(__sz)(addr + __off); \
|
||||||
}
|
}
|
||||||
|
@ -115,14 +135,14 @@ struct spi_dw_data {
|
||||||
#define DW_SPI_CTRLR0_DFS DW_SPI_CTRLR0_DFS_32
|
#define DW_SPI_CTRLR0_DFS DW_SPI_CTRLR0_DFS_32
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* 0x38 represents the bits 8,16 and 32. Knowing that 24 is bits 8 and 16
|
/* 0x38 represents the bits 8, 16 and 32. Knowing that 24 is bits 8 and 16
|
||||||
* These are the bits were when you divide by 8, you keep the result as it is.
|
* These are the bits were when you divide by 8, you keep the result as it is.
|
||||||
* For all the other ones, 4 to 7, 9 to 15, etc... you need a +1,
|
* For all the other ones, 4 to 7, 9 to 15, etc... you need a +1,
|
||||||
* since on such division it takes only the result above 0
|
* since on such division it takes only the result above 0
|
||||||
*/
|
*/
|
||||||
#define SPI_DFS_TO_BYTES(__bpw) (((__bpw) & ~0x38) ? \
|
#define SPI_WS_TO_DFS(__bpw) (((__bpw) & ~0x38) ? \
|
||||||
(((__bpw) / 8) + 1) : \
|
(((__bpw) / 8) + 1) : \
|
||||||
((__bpw) / 8))
|
((__bpw) / 8))
|
||||||
|
|
||||||
/* SSIENR bits */
|
/* SSIENR bits */
|
||||||
#define DW_SPI_SSIENR_SSIEN_BIT (0)
|
#define DW_SPI_SSIENR_SSIEN_BIT (0)
|
||||||
|
@ -166,8 +186,8 @@ struct spi_dw_data {
|
||||||
|
|
||||||
/* Threshold defaults */
|
/* Threshold defaults */
|
||||||
#define DW_SPI_FIFO_DEPTH CONFIG_SPI_DW_FIFO_DEPTH
|
#define DW_SPI_FIFO_DEPTH CONFIG_SPI_DW_FIFO_DEPTH
|
||||||
#define DW_SPI_TXFTLR_DFLT ((DW_SPI_FIFO_DEPTH*1)/2) /* 50% */
|
#define DW_SPI_TXFTLR_DFLT ((DW_SPI_FIFO_DEPTH * 1) / 2) /* 50% */
|
||||||
#define DW_SPI_RXFTLR_DFLT ((DW_SPI_FIFO_DEPTH*5)/8)
|
#define DW_SPI_RXFTLR_DFLT ((DW_SPI_FIFO_DEPTH * 5) / 8)
|
||||||
|
|
||||||
/* Interrupt mask (IMR) */
|
/* Interrupt mask (IMR) */
|
||||||
#define DW_SPI_IMR_MASK (0x0)
|
#define DW_SPI_IMR_MASK (0x0)
|
||||||
|
@ -193,6 +213,7 @@ struct spi_dw_data {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* GPIO used to emulate CS */
|
/* GPIO used to emulate CS */
|
||||||
|
#if defined(CONFIG_SPI_LEGACY_API)
|
||||||
#ifdef CONFIG_SPI_DW_CS_GPIO
|
#ifdef CONFIG_SPI_DW_CS_GPIO
|
||||||
|
|
||||||
#include <gpio.h>
|
#include <gpio.h>
|
||||||
|
@ -229,6 +250,7 @@ static inline void _spi_control_cs(struct device *dev, int on)
|
||||||
#define _spi_control_cs(...)
|
#define _spi_control_cs(...)
|
||||||
#define _spi_config_cs(...)
|
#define _spi_config_cs(...)
|
||||||
#endif /* CONFIG_SPI_DW_CS_GPIO */
|
#endif /* CONFIG_SPI_DW_CS_GPIO */
|
||||||
|
#endif /* CONFIG_SPI_LEGACY_API */
|
||||||
|
|
||||||
/* Interrupt mask
|
/* Interrupt mask
|
||||||
* SoC SPECIFIC!
|
* SoC SPECIFIC!
|
||||||
|
@ -262,4 +284,3 @@ DEFINE_TEST_BIT_OP(sr_busy, DW_SPI_REG_SR, DW_SPI_SR_BUSY_BIT)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif /* __SPI_DW_H__ */
|
#endif /* __SPI_DW_H__ */
|
||||||
|
|
||||||
|
|
513
drivers/spi/spi_dw_legacy.c
Normal file
513
drivers/spi/spi_dw_legacy.c
Normal file
|
@ -0,0 +1,513 @@
|
||||||
|
/* spi_dw.c - Designware SPI driver implementation */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Intel Corporation.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <arch/cpu.h>
|
||||||
|
|
||||||
|
#include <misc/__assert.h>
|
||||||
|
#include <board.h>
|
||||||
|
#include <device.h>
|
||||||
|
#include <init.h>
|
||||||
|
|
||||||
|
#include <sys_io.h>
|
||||||
|
#include <clock_control.h>
|
||||||
|
#include <misc/util.h>
|
||||||
|
|
||||||
|
#include <spi.h>
|
||||||
|
#include <spi_dw.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_IOAPIC
|
||||||
|
#include <drivers/ioapic.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SYS_LOG_DOMAIN "SPI DW"
|
||||||
|
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_SPI_LEVEL
|
||||||
|
#include <logging/sys_log.h>
|
||||||
|
|
||||||
|
#if (CONFIG_SYS_LOG_SPI_LEVEL == 4)
|
||||||
|
#define DBG_COUNTER_INIT() \
|
||||||
|
u32_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
|
||||||
|
|
||||||
|
static void completed(struct device *dev, int error)
|
||||||
|
{
|
||||||
|
const struct spi_dw_config *info = dev->config->config_info;
|
||||||
|
struct spi_dw_data *spi = dev->driver_data;
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There are several situations here.
|
||||||
|
* 1. spi_write w rx_buf - need last_tx && rx_buf_len zero to be done.
|
||||||
|
* 2. spi_write w/o rx_buf - only need to determine when write is done.
|
||||||
|
* 3. spi_read - need rx_buf_len zero.
|
||||||
|
*/
|
||||||
|
if (spi->tx_buf && spi->rx_buf) {
|
||||||
|
if (!spi->last_tx || spi->rx_buf_len) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (spi->tx_buf) {
|
||||||
|
if (!spi->last_tx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else { /* or, spi->rx_buf!=0 */
|
||||||
|
if (spi->rx_buf_len) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
/* need to give time for FIFOs to drain before issuing more commands */
|
||||||
|
while (test_bit_sr_busy(info->regs)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
spi->error = error;
|
||||||
|
|
||||||
|
/* Disabling interrupts */
|
||||||
|
write_imr(DW_SPI_IMR_MASK, info->regs);
|
||||||
|
/* Disabling the controller */
|
||||||
|
clear_bit_ssienr(info->regs);
|
||||||
|
|
||||||
|
_spi_control_cs(dev, 0);
|
||||||
|
|
||||||
|
SYS_LOG_DBG("SPI transaction completed %s error",
|
||||||
|
error ? "with" : "without");
|
||||||
|
|
||||||
|
k_sem_give(&spi->device_sync_sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void push_data(struct device *dev)
|
||||||
|
{
|
||||||
|
const struct spi_dw_config *info = dev->config->config_info;
|
||||||
|
struct spi_dw_data *spi = dev->driver_data;
|
||||||
|
u32_t data = 0;
|
||||||
|
u32_t f_tx;
|
||||||
|
DBG_COUNTER_INIT();
|
||||||
|
|
||||||
|
if (spi->rx_buf) {
|
||||||
|
f_tx = DW_SPI_FIFO_DEPTH - read_txflr(info->regs) -
|
||||||
|
read_rxflr(info->regs);
|
||||||
|
if ((int)f_tx < 0) {
|
||||||
|
f_tx = 0; /* if rx-fifo is full, hold off tx */
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f_tx = DW_SPI_FIFO_DEPTH - read_txflr(info->regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f_tx && (spi->tx_buf_len == 0)) {
|
||||||
|
/* room in fifo, yet nothing to send */
|
||||||
|
spi->last_tx = 1; /* setting last_tx indicates TX is done */
|
||||||
|
}
|
||||||
|
|
||||||
|
while (f_tx) {
|
||||||
|
if (spi->tx_buf && spi->tx_buf_len > 0) {
|
||||||
|
switch (spi->dfs) {
|
||||||
|
case 1:
|
||||||
|
data = UNALIGNED_GET((u8_t *)(spi->tx_buf));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
data = UNALIGNED_GET((u16_t *)(spi->tx_buf));
|
||||||
|
break;
|
||||||
|
#ifndef CONFIG_ARC
|
||||||
|
case 4:
|
||||||
|
data = UNALIGNED_GET((u32_t *)(spi->tx_buf));
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
spi->tx_buf += spi->dfs;
|
||||||
|
spi->tx_buf_len--;
|
||||||
|
} else if (spi->rx_buf && spi->rx_buf_len > 0) {
|
||||||
|
/* No need to push more than necessary */
|
||||||
|
if (spi->rx_buf_len - spi->fifo_diff <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = 0;
|
||||||
|
} else {
|
||||||
|
/* Nothing to push anymore */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_dr(data, info->regs);
|
||||||
|
f_tx--;
|
||||||
|
spi->fifo_diff++;
|
||||||
|
DBG_COUNTER_INC();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spi->last_tx) {
|
||||||
|
write_txftlr(0, info->regs);
|
||||||
|
/* prevents any further interrupts demanding TX fifo fill */
|
||||||
|
}
|
||||||
|
|
||||||
|
SYS_LOG_DBG("Pushed: %d", DBG_COUNTER_RESULT());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pull_data(struct device *dev)
|
||||||
|
{
|
||||||
|
const struct spi_dw_config *info = dev->config->config_info;
|
||||||
|
struct spi_dw_data *spi = dev->driver_data;
|
||||||
|
u32_t data = 0;
|
||||||
|
DBG_COUNTER_INIT();
|
||||||
|
|
||||||
|
while (read_rxflr(info->regs)) {
|
||||||
|
data = read_dr(info->regs);
|
||||||
|
DBG_COUNTER_INC();
|
||||||
|
|
||||||
|
if (spi->rx_buf && spi->rx_buf_len > 0) {
|
||||||
|
switch (spi->dfs) {
|
||||||
|
case 1:
|
||||||
|
UNALIGNED_PUT(data, (u8_t *)spi->rx_buf);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
UNALIGNED_PUT(data, (u16_t *)spi->rx_buf);
|
||||||
|
break;
|
||||||
|
#ifndef CONFIG_ARC
|
||||||
|
case 4:
|
||||||
|
UNALIGNED_PUT(data, (u32_t *)spi->rx_buf);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
spi->rx_buf += spi->dfs;
|
||||||
|
spi->rx_buf_len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi->fifo_diff--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!spi->rx_buf_len && spi->tx_buf_len < DW_SPI_FIFO_DEPTH) {
|
||||||
|
write_rxftlr(spi->tx_buf_len - 1, info->regs);
|
||||||
|
} else if (read_rxftlr(info->regs) >= spi->rx_buf_len) {
|
||||||
|
write_rxftlr(spi->rx_buf_len - 1, info->regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
SYS_LOG_DBG("Pulled: %d", DBG_COUNTER_RESULT());
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool _spi_dw_is_controller_ready(struct device *dev)
|
||||||
|
{
|
||||||
|
const struct spi_dw_config *info = dev->config->config_info;
|
||||||
|
|
||||||
|
if (test_bit_ssienr(info->regs) || test_bit_sr_busy(info->regs)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_dw_configure(struct device *dev,
|
||||||
|
struct spi_config *config)
|
||||||
|
{
|
||||||
|
const struct spi_dw_config *info = dev->config->config_info;
|
||||||
|
struct spi_dw_data *spi = dev->driver_data;
|
||||||
|
u32_t flags = config->config;
|
||||||
|
u32_t ctrlr0 = 0;
|
||||||
|
u32_t mode;
|
||||||
|
|
||||||
|
SYS_LOG_DBG("%p (0x%x), %p", dev, info->regs, config);
|
||||||
|
|
||||||
|
/* Check status */
|
||||||
|
if (!_spi_dw_is_controller_ready(dev)) {
|
||||||
|
SYS_LOG_DBG("Controller is busy");
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Word size */
|
||||||
|
ctrlr0 |= DW_SPI_CTRLR0_DFS(SPI_WORD_SIZE_GET(flags));
|
||||||
|
|
||||||
|
/* Determine how many bytes are required per-frame */
|
||||||
|
spi->dfs = SPI_WS_TO_DFS(SPI_WORD_SIZE_GET(flags));
|
||||||
|
|
||||||
|
/* SPI mode */
|
||||||
|
mode = SPI_MODE(flags);
|
||||||
|
if (mode & SPI_MODE_CPOL) {
|
||||||
|
ctrlr0 |= DW_SPI_CTRLR0_SCPOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode & SPI_MODE_CPHA) {
|
||||||
|
ctrlr0 |= DW_SPI_CTRLR0_SCPH;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode & SPI_MODE_LOOP) {
|
||||||
|
ctrlr0 |= DW_SPI_CTRLR0_SRL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Installing the configuration */
|
||||||
|
write_ctrlr0(ctrlr0, info->regs);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configure the rate. Use this small hack to allow the user to call
|
||||||
|
* spi_configure() with both a divider (as the driver was initially
|
||||||
|
* written) and a frequency (as the SPI API suggests to). The clock
|
||||||
|
* divider is a 16bit value, hence we can fairly, and safely, assume
|
||||||
|
* that everything above this value is a frequency. The trade-off is
|
||||||
|
* that if one wants to use a bus frequency of 64kHz (or less), it has
|
||||||
|
* the use a divider...
|
||||||
|
*/
|
||||||
|
if (config->max_sys_freq > 0xffff) {
|
||||||
|
write_baudr(SPI_DW_CLK_DIVIDER(config->max_sys_freq),
|
||||||
|
info->regs);
|
||||||
|
} else {
|
||||||
|
write_baudr(config->max_sys_freq, info->regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_dw_slave_select(struct device *dev, u32_t slave)
|
||||||
|
{
|
||||||
|
struct spi_dw_data *spi = dev->driver_data;
|
||||||
|
|
||||||
|
SYS_LOG_DBG("%p %d", dev, slave);
|
||||||
|
|
||||||
|
if (slave == 0 || slave > 16) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi->slave = 1 << (slave - 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_dw_transceive(struct device *dev,
|
||||||
|
const void *tx_buf, u32_t tx_buf_len,
|
||||||
|
void *rx_buf, u32_t rx_buf_len)
|
||||||
|
{
|
||||||
|
const struct spi_dw_config *info = dev->config->config_info;
|
||||||
|
struct spi_dw_data *spi = dev->driver_data;
|
||||||
|
u32_t rx_thsld = DW_SPI_RXFTLR_DFLT;
|
||||||
|
u32_t imask;
|
||||||
|
|
||||||
|
SYS_LOG_DBG("%p, %p, %u, %p, %u",
|
||||||
|
dev, tx_buf, tx_buf_len, rx_buf, rx_buf_len);
|
||||||
|
|
||||||
|
/* Check status */
|
||||||
|
if (!_spi_dw_is_controller_ready(dev)) {
|
||||||
|
SYS_LOG_DBG("Controller is busy");
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set buffers info */
|
||||||
|
spi->tx_buf = tx_buf;
|
||||||
|
spi->tx_buf_len = tx_buf_len/spi->dfs;
|
||||||
|
spi->rx_buf = rx_buf;
|
||||||
|
if (rx_buf) {
|
||||||
|
spi->rx_buf_len = rx_buf_len/spi->dfs;
|
||||||
|
} else {
|
||||||
|
spi->rx_buf_len = 0; /* must be zero if no buffer */
|
||||||
|
}
|
||||||
|
spi->fifo_diff = 0;
|
||||||
|
spi->last_tx = 0;
|
||||||
|
|
||||||
|
/* Tx Threshold */
|
||||||
|
write_txftlr(DW_SPI_TXFTLR_DFLT, info->regs);
|
||||||
|
|
||||||
|
/* Does Rx thresholds needs to be lower? */
|
||||||
|
if (spi->rx_buf_len && spi->rx_buf_len < DW_SPI_FIFO_DEPTH) {
|
||||||
|
rx_thsld = spi->rx_buf_len - 1;
|
||||||
|
} else if (!spi->rx_buf_len && spi->tx_buf_len < DW_SPI_FIFO_DEPTH) {
|
||||||
|
rx_thsld = spi->tx_buf_len - 1;
|
||||||
|
/* TODO: why? */
|
||||||
|
}
|
||||||
|
|
||||||
|
write_rxftlr(rx_thsld, info->regs);
|
||||||
|
|
||||||
|
/* Slave select */
|
||||||
|
write_ser(spi->slave, info->regs);
|
||||||
|
|
||||||
|
_spi_control_cs(dev, 1);
|
||||||
|
|
||||||
|
/* Enable interrupts */
|
||||||
|
imask = DW_SPI_IMR_UNMASK;
|
||||||
|
if (!rx_buf) {
|
||||||
|
/* if there is no rx buffer, keep all rx interrupts masked */
|
||||||
|
imask &= DW_SPI_IMR_MASK_RX;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_imr(imask, info->regs);
|
||||||
|
|
||||||
|
/* Enable the controller */
|
||||||
|
set_bit_ssienr(info->regs);
|
||||||
|
|
||||||
|
k_sem_take(&spi->device_sync_sem, K_FOREVER);
|
||||||
|
|
||||||
|
if (spi->error) {
|
||||||
|
spi->error = 0;
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void spi_dw_isr(void *arg)
|
||||||
|
{
|
||||||
|
struct device *dev = (struct device *)arg;
|
||||||
|
const struct spi_dw_config *info = dev->config->config_info;
|
||||||
|
u32_t error = 0;
|
||||||
|
u32_t int_status;
|
||||||
|
|
||||||
|
int_status = read_isr(info->regs);
|
||||||
|
|
||||||
|
SYS_LOG_DBG("SPI int_status 0x%x - (tx: %d, rx: %d)",
|
||||||
|
int_status, read_txflr(info->regs), read_rxflr(info->regs));
|
||||||
|
|
||||||
|
if (int_status & DW_SPI_ISR_ERRORS_MASK) {
|
||||||
|
error = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = {
|
||||||
|
.configure = spi_dw_configure,
|
||||||
|
.slave_select = spi_dw_slave_select,
|
||||||
|
.transceive = spi_dw_transceive,
|
||||||
|
};
|
||||||
|
|
||||||
|
int spi_dw_init(struct device *dev)
|
||||||
|
{
|
||||||
|
const struct spi_dw_config *info = dev->config->config_info;
|
||||||
|
struct spi_dw_data *spi = dev->driver_data;
|
||||||
|
|
||||||
|
_clock_config(dev);
|
||||||
|
_clock_on(dev);
|
||||||
|
|
||||||
|
info->config_func();
|
||||||
|
|
||||||
|
k_sem_init(&spi->device_sync_sem, 0, UINT_MAX);
|
||||||
|
|
||||||
|
_spi_config_cs(dev);
|
||||||
|
|
||||||
|
/* Masking interrupt and making sure controller is disabled */
|
||||||
|
write_imr(DW_SPI_IMR_MASK, info->regs);
|
||||||
|
clear_bit_ssienr(info->regs);
|
||||||
|
|
||||||
|
SYS_LOG_DBG("Designware SPI driver initialized on device: %p", dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_SPI_0
|
||||||
|
void spi_config_0_irq(void);
|
||||||
|
|
||||||
|
struct spi_dw_data spi_dw_data_port_0;
|
||||||
|
|
||||||
|
const struct spi_dw_config spi_dw_config_0 = {
|
||||||
|
.regs = SPI_DW_PORT_0_REGS,
|
||||||
|
#ifdef CONFIG_SPI_DW_CLOCK_GATE
|
||||||
|
.clock_data = UINT_TO_POINTER(CONFIG_SPI_0_CLOCK_GATE_SUBSYS),
|
||||||
|
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
|
||||||
|
#ifdef CONFIG_SPI_DW_CS_GPIO
|
||||||
|
.cs_gpio_name = CONFIG_SPI_0_CS_GPIO_PORT,
|
||||||
|
.cs_gpio_pin = CONFIG_SPI_0_CS_GPIO_PIN,
|
||||||
|
#endif
|
||||||
|
.config_func = spi_config_0_irq
|
||||||
|
};
|
||||||
|
|
||||||
|
DEVICE_AND_API_INIT(spi_dw_port_0, CONFIG_SPI_0_NAME, spi_dw_init,
|
||||||
|
&spi_dw_data_port_0, &spi_dw_config_0,
|
||||||
|
POST_KERNEL, CONFIG_SPI_INIT_PRIORITY,
|
||||||
|
&dw_spi_api);
|
||||||
|
|
||||||
|
void spi_config_0_irq(void)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_SPI_DW_INTERRUPT_SINGLE_LINE
|
||||||
|
IRQ_CONNECT(SPI_DW_PORT_0_IRQ, CONFIG_SPI_0_IRQ_PRI,
|
||||||
|
spi_dw_isr, DEVICE_GET(spi_dw_port_0), SPI_DW_IRQ_FLAGS);
|
||||||
|
irq_enable(SPI_DW_PORT_0_IRQ);
|
||||||
|
_spi_int_unmask(SPI_DW_PORT_0_INT_MASK);
|
||||||
|
#else /* SPI_DW_INTERRUPT_SEPARATED_LINES */
|
||||||
|
IRQ_CONNECT(IRQ_SPI0_RX_AVAIL, CONFIG_SPI_0_IRQ_PRI,
|
||||||
|
spi_dw_isr, DEVICE_GET(spi_dw_port_0), SPI_DW_IRQ_FLAGS);
|
||||||
|
IRQ_CONNECT(IRQ_SPI0_TX_REQ, CONFIG_SPI_0_IRQ_PRI,
|
||||||
|
spi_dw_isr, DEVICE_GET(spi_dw_port_0), SPI_DW_IRQ_FLAGS);
|
||||||
|
IRQ_CONNECT(IRQ_SPI0_ERR_INT, CONFIG_SPI_0_IRQ_PRI,
|
||||||
|
spi_dw_isr, DEVICE_GET(spi_dw_port_0), SPI_DW_IRQ_FLAGS);
|
||||||
|
|
||||||
|
irq_enable(IRQ_SPI0_RX_AVAIL);
|
||||||
|
irq_enable(IRQ_SPI0_TX_REQ);
|
||||||
|
irq_enable(IRQ_SPI0_ERR_INT);
|
||||||
|
|
||||||
|
_spi_int_unmask(SPI_DW_PORT_0_RX_INT_MASK);
|
||||||
|
_spi_int_unmask(SPI_DW_PORT_0_TX_INT_MASK);
|
||||||
|
_spi_int_unmask(SPI_DW_PORT_0_ERROR_INT_MASK);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_SPI_0 */
|
||||||
|
#ifdef CONFIG_SPI_1
|
||||||
|
void spi_config_1_irq(void);
|
||||||
|
|
||||||
|
struct spi_dw_data spi_dw_data_port_1;
|
||||||
|
|
||||||
|
static const struct spi_dw_config spi_dw_config_1 = {
|
||||||
|
.regs = SPI_DW_PORT_1_REGS,
|
||||||
|
#ifdef CONFIG_SPI_DW_CLOCK_GATE
|
||||||
|
.clock_data = UINT_TO_POINTER(CONFIG_SPI_1_CLOCK_GATE_SUBSYS),
|
||||||
|
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
|
||||||
|
#ifdef CONFIG_SPI_DW_CS_GPIO
|
||||||
|
.cs_gpio_name = CONFIG_SPI_1_CS_GPIO_PORT,
|
||||||
|
.cs_gpio_pin = CONFIG_SPI_1_CS_GPIO_PIN,
|
||||||
|
#endif
|
||||||
|
.config_func = spi_config_1_irq
|
||||||
|
};
|
||||||
|
|
||||||
|
DEVICE_AND_API_INIT(spi_dw_port_1, CONFIG_SPI_1_NAME, spi_dw_init,
|
||||||
|
&spi_dw_data_port_1, &spi_dw_config_1,
|
||||||
|
POST_KERNEL, CONFIG_SPI_INIT_PRIORITY,
|
||||||
|
&dw_spi_api);
|
||||||
|
|
||||||
|
void spi_config_1_irq(void)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_SPI_DW_INTERRUPT_SINGLE_LINE
|
||||||
|
IRQ_CONNECT(SPI_DW_PORT_1_IRQ, CONFIG_SPI_1_IRQ_PRI,
|
||||||
|
spi_dw_isr, DEVICE_GET(spi_dw_port_1), SPI_DW_IRQ_FLAGS);
|
||||||
|
irq_enable(SPI_DW_PORT_1_IRQ);
|
||||||
|
_spi_int_unmask(SPI_DW_PORT_1_INT_MASK);
|
||||||
|
#else /* SPI_DW_INTERRUPT_SEPARATED_LINES */
|
||||||
|
IRQ_CONNECT(IRQ_SPI1_RX_AVAIL, CONFIG_SPI_1_IRQ_PRI,
|
||||||
|
spi_dw_isr, DEVICE_GET(spi_dw_port_1), SPI_DW_IRQ_FLAGS);
|
||||||
|
IRQ_CONNECT(IRQ_SPI1_TX_REQ, CONFIG_SPI_1_IRQ_PRI,
|
||||||
|
spi_dw_isr, DEVICE_GET(spi_dw_port_1), SPI_DW_IRQ_FLAGS);
|
||||||
|
IRQ_CONNECT(IRQ_SPI1_ERR_INT, CONFIG_SPI_1_IRQ_PRI,
|
||||||
|
spi_dw_isr, DEVICE_GET(spi_dw_port_1), SPI_DW_IRQ_FLAGS);
|
||||||
|
|
||||||
|
irq_enable(IRQ_SPI1_RX_AVAIL);
|
||||||
|
irq_enable(IRQ_SPI1_TX_REQ);
|
||||||
|
irq_enable(IRQ_SPI1_ERR_INT);
|
||||||
|
|
||||||
|
_spi_int_unmask(SPI_DW_PORT_1_RX_INT_MASK);
|
||||||
|
_spi_int_unmask(SPI_DW_PORT_1_TX_INT_MASK);
|
||||||
|
_spi_int_unmask(SPI_DW_PORT_1_ERROR_INT_MASK);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_SPI_1 */
|
|
@ -9,6 +9,10 @@
|
||||||
#ifndef __SPI_DW_QUARK_SE_SS_H__
|
#ifndef __SPI_DW_QUARK_SE_SS_H__
|
||||||
#define __SPI_DW_QUARK_SE_SS_H__
|
#define __SPI_DW_QUARK_SE_SS_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Registers:
|
/* Registers:
|
||||||
* Some registers have been collapsed into one
|
* Some registers have been collapsed into one
|
||||||
* - SER is part of SSIENR
|
* - SER is part of SSIENR
|
||||||
|
@ -132,4 +136,8 @@ static inline void _clock_off(struct device *dev)
|
||||||
clear_bit_clk_ena(info->regs);
|
clear_bit_clk_ena(info->regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* __SPI_DW_QUARK_SE_SS_H__ */
|
#endif /* __SPI_DW_QUARK_SE_SS_H__ */
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
#ifndef __SPI_DW_REGS_H__
|
#ifndef __SPI_DW_REGS_H__
|
||||||
#define __SPI_DW_REGS_H__
|
#define __SPI_DW_REGS_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
#define DW_SPI_REG_CTRLR0 (0x00)
|
#define DW_SPI_REG_CTRLR0 (0x00)
|
||||||
#define DW_SPI_REG_CTRLR1 (0x04)
|
#define DW_SPI_REG_CTRLR1 (0x04)
|
||||||
#define DW_SPI_REG_SSIENR (0x08)
|
#define DW_SPI_REG_SSIENR (0x08)
|
||||||
|
@ -36,8 +40,6 @@
|
||||||
#define DW_SPI_REG_DR (0x60)
|
#define DW_SPI_REG_DR (0x60)
|
||||||
#define DW_SPI_REG_RX_SAMPLE_DLY (0xf0)
|
#define DW_SPI_REG_RX_SAMPLE_DLY (0xf0)
|
||||||
|
|
||||||
#define DW_SSI_COMP_VERSION (0x3332332a)
|
|
||||||
|
|
||||||
/* Register helpers */
|
/* Register helpers */
|
||||||
DEFINE_MM_REG_WRITE(ctrlr0, DW_SPI_REG_CTRLR0, 32)
|
DEFINE_MM_REG_WRITE(ctrlr0, DW_SPI_REG_CTRLR0, 32)
|
||||||
DEFINE_MM_REG_WRITE(ser, DW_SPI_REG_SER, 8)
|
DEFINE_MM_REG_WRITE(ser, DW_SPI_REG_SER, 8)
|
||||||
|
@ -88,4 +90,8 @@ static inline void _clock_off(struct device *dev)
|
||||||
#define _clock_off(...)
|
#define _clock_off(...)
|
||||||
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
|
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* __SPI_DW_REGS_H__ */
|
#endif /* __SPI_DW_REGS_H__ */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue