drivers: spi: Add simple_spi driver
Add support for the OpenCores simple_spi controller Signed-off-by: Olof Kindgren <olof.kindgren@gmail.com>
This commit is contained in:
parent
4d97252cdf
commit
d09614ab59
6 changed files with 313 additions and 0 deletions
|
@ -14,5 +14,6 @@ zephyr_library_sources_ifdef(CONFIG_NRFX_SPI spi_nrfx_spi.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_NRFX_SPIM spi_nrfx_spim.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_NRFX_SPIS spi_nrfx_spis.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_SPI_LITESPI spi_litespi.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_SPI_OC_SIMPLE spi_oc_simple.c)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_USERSPACE spi_handlers.c)
|
||||
|
|
|
@ -199,4 +199,6 @@ source "drivers/spi/Kconfig.cc13xx_cc26xx"
|
|||
|
||||
source "drivers/spi/Kconfig.litex"
|
||||
|
||||
source "drivers/spi/Kconfig.oc_simple"
|
||||
|
||||
endif # SPI
|
||||
|
|
19
drivers/spi/Kconfig.oc_simple
Normal file
19
drivers/spi/Kconfig.oc_simple
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Kconfig - Simple SPI Driver configuration options
|
||||
|
||||
#
|
||||
# Copyright (c) 2019 Western Digital Corporation or its affiliates
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
menuconfig SPI_OC_SIMPLE
|
||||
bool "OpenCores Simple SPI controller driver"
|
||||
help
|
||||
Enable the Simple SPI controller
|
||||
|
||||
if SPI_OC_SIMPLE
|
||||
|
||||
config SPI_OC_SIMPLE_BUS_WIDTH
|
||||
def_int 8
|
||||
|
||||
endif # SPI_OC_SIMPLE
|
242
drivers/spi/spi_oc_simple.c
Normal file
242
drivers/spi/spi_oc_simple.c
Normal file
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Western Digital Corporation or its affiliates
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define LOG_LEVEL CONFIG_SPI_LOG_LEVEL
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(spi_oc_simple);
|
||||
|
||||
#include <sys_io.h>
|
||||
#include <spi.h>
|
||||
|
||||
#include "spi_context.h"
|
||||
#include "spi_oc_simple.h"
|
||||
|
||||
/* Bit 5:4 == ESPR, Bit 1:0 == SPR */
|
||||
u8_t DIVIDERS[] = { 0x00, /* 2 */
|
||||
0x01, /* 4 */
|
||||
0x10, /* 8 */
|
||||
0x02, /* 16 */
|
||||
0x03, /* 32 */
|
||||
0x11, /* 64 */
|
||||
0x12, /* 128 */
|
||||
0x13, /* 256 */
|
||||
0x20, /* 512 */
|
||||
0x21, /* 1024 */
|
||||
0x22, /* 2048 */
|
||||
0x23 }; /* 4096 */
|
||||
|
||||
static int spi_oc_simple_configure(const struct spi_oc_simple_cfg *info,
|
||||
struct spi_oc_simple_data *spi,
|
||||
const struct spi_config *config)
|
||||
{
|
||||
u8_t spcr = 0U;
|
||||
int i;
|
||||
|
||||
if (spi_context_configured(&spi->ctx, config)) {
|
||||
/* Nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Simple SPI only supports master mode */
|
||||
if (spi_context_is_slave(&spi->ctx)) {
|
||||
LOG_ERR("Slave mode not supported");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (config->operation & (SPI_MODE_LOOP | SPI_TRANSFER_LSB |
|
||||
SPI_LINES_DUAL | SPI_LINES_QUAD)) {
|
||||
LOG_ERR("Unsupported configuration");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* SPI mode */
|
||||
if (SPI_MODE_GET(config->operation) & SPI_MODE_CPOL) {
|
||||
spcr |= SPI_OC_SIMPLE_SPCR_CPOL;
|
||||
}
|
||||
|
||||
if (SPI_MODE_GET(config->operation) & SPI_MODE_CPHA) {
|
||||
spcr |= SPI_OC_SIMPLE_SPCR_CPHA;
|
||||
}
|
||||
|
||||
/* Set clock divider */
|
||||
for (i = 0; i < 12; i++)
|
||||
if ((config->frequency << (i + 1)) >
|
||||
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC) {
|
||||
break;
|
||||
}
|
||||
|
||||
sys_write8((DIVIDERS[i] >> 4) & 0x3, SPI_OC_SIMPLE_SPER(info));
|
||||
spcr |= (DIVIDERS[i] & 0x3);
|
||||
|
||||
/* Configure and Enable SPI controller */
|
||||
sys_write8(spcr | SPI_OC_SIMPLE_SPCR_SPE, SPI_OC_SIMPLE_SPCR(info));
|
||||
|
||||
spi->ctx.config = config;
|
||||
|
||||
if (config->cs) {
|
||||
spi_context_cs_configure(&spi->ctx);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spi_oc_simple_transceive(struct device *dev,
|
||||
const struct spi_config *config,
|
||||
const struct spi_buf_set *tx_bufs,
|
||||
const struct spi_buf_set *rx_bufs)
|
||||
{
|
||||
const struct spi_oc_simple_cfg *info = dev->config->config_info;
|
||||
struct spi_oc_simple_data *spi = SPI_OC_SIMPLE_DATA(dev);
|
||||
struct spi_context *ctx = &spi->ctx;
|
||||
|
||||
u8_t rx_byte;
|
||||
size_t i;
|
||||
size_t cur_xfer_len;
|
||||
int rc;
|
||||
|
||||
/* Lock the SPI Context */
|
||||
spi_context_lock(ctx, false, NULL);
|
||||
|
||||
spi_oc_simple_configure(info, spi, config);
|
||||
|
||||
/* Set chip select */
|
||||
if (config->cs) {
|
||||
spi_context_cs_control(&spi->ctx, true);
|
||||
} else {
|
||||
sys_write8(1 << config->slave, SPI_OC_SIMPLE_SPSS(info));
|
||||
}
|
||||
|
||||
spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1);
|
||||
|
||||
while (spi_context_tx_buf_on(ctx) || spi_context_rx_buf_on(ctx)) {
|
||||
cur_xfer_len = spi_context_longest_current_buf(ctx);
|
||||
|
||||
for (i = 0; i < cur_xfer_len; i++) {
|
||||
|
||||
/* Write byte */
|
||||
if (spi_context_tx_buf_on(ctx)) {
|
||||
sys_write8(*ctx->tx_buf,
|
||||
SPI_OC_SIMPLE_SPDR(info));
|
||||
spi_context_update_tx(ctx, 1, 1);
|
||||
} else {
|
||||
sys_write8(0, SPI_OC_SIMPLE_SPDR(info));
|
||||
}
|
||||
|
||||
/* Wait for rx FIFO empty flag to clear */
|
||||
while (sys_read8(SPI_OC_SIMPLE_SPSR(info)) & 0x1) {
|
||||
}
|
||||
|
||||
/* Get received byte */
|
||||
rx_byte = sys_read8(SPI_OC_SIMPLE_SPDR(info));
|
||||
|
||||
/* Store received byte if rx buffer is on */
|
||||
if (spi_context_rx_on(ctx)) {
|
||||
*ctx->rx_buf = rx_byte;
|
||||
spi_context_update_rx(ctx, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear chip-select */
|
||||
if (config->cs) {
|
||||
spi_context_cs_control(&spi->ctx, false);
|
||||
} else {
|
||||
sys_write8(0 << config->slave, SPI_OC_SIMPLE_SPSS(info));
|
||||
}
|
||||
|
||||
spi_context_complete(ctx, 0);
|
||||
rc = spi_context_wait_for_completion(ctx);
|
||||
|
||||
spi_context_release(ctx, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPI_ASYNC
|
||||
static int spi_oc_simple_transceive_async(struct device *dev,
|
||||
const struct spi_config *config,
|
||||
const struct spi_buf_set *tx_bufs,
|
||||
const struct spi_buf_set *rx_bufs,
|
||||
struct k_poll_signal *async)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
#endif /* CONFIG_SPI_ASYNC */
|
||||
|
||||
int spi_oc_simple_release(struct device *dev, const struct spi_config *config)
|
||||
{
|
||||
spi_context_unlock_unconditionally(&SPI_OC_SIMPLE_DATA(dev)->ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver_api spi_oc_simple_api = {
|
||||
.transceive = spi_oc_simple_transceive,
|
||||
.release = spi_oc_simple_release,
|
||||
#ifdef CONFIG_SPI_ASYNC
|
||||
.transceive_async = spi_oc_simple_transceive_async,
|
||||
#endif /* CONFIG_SPI_ASYNC */
|
||||
};
|
||||
|
||||
int spi_oc_simple_init(struct device *dev)
|
||||
{
|
||||
const struct spi_oc_simple_cfg *info = dev->config->config_info;
|
||||
|
||||
/* Clear chip selects */
|
||||
sys_write8(0, SPI_OC_SIMPLE_SPSS(info));
|
||||
|
||||
/* Make sure the context is unlocked */
|
||||
spi_context_unlock_unconditionally(&SPI_OC_SIMPLE_DATA(dev)->ctx);
|
||||
|
||||
/* Initial clock stucks high, so add this workaround */
|
||||
sys_write8(SPI_OC_SIMPLE_SPCR_SPE, SPI_OC_SIMPLE_SPCR(info));
|
||||
sys_write8(0, SPI_OC_SIMPLE_SPDR(info));
|
||||
while (sys_read8(SPI_OC_SIMPLE_SPSR(info)) & 0x1) {
|
||||
}
|
||||
|
||||
sys_read8(SPI_OC_SIMPLE_SPDR(info));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DT_INST_0_OPENCORES_SPI_SIMPLE
|
||||
static struct spi_oc_simple_cfg spi_oc_simple_cfg_0 = {
|
||||
.base = DT_INST_0_OPENCORES_SPI_SIMPLE_CONTROL_BASE_ADDRESS,
|
||||
};
|
||||
|
||||
static struct spi_oc_simple_data spi_oc_simple_data_0 = {
|
||||
SPI_CONTEXT_INIT_LOCK(spi_oc_simple_data_0, ctx),
|
||||
SPI_CONTEXT_INIT_SYNC(spi_oc_simple_data_0, ctx),
|
||||
};
|
||||
|
||||
DEVICE_AND_API_INIT(spi_oc_simple_0,
|
||||
DT_INST_0_OPENCORES_SPI_SIMPLE_LABEL,
|
||||
spi_oc_simple_init,
|
||||
&spi_oc_simple_data_0,
|
||||
&spi_oc_simple_cfg_0,
|
||||
POST_KERNEL,
|
||||
CONFIG_SPI_INIT_PRIORITY,
|
||||
&spi_oc_simple_api);
|
||||
#endif
|
||||
|
||||
#ifdef DT_INST_1_OPENCORES_SPI_SIMPLE
|
||||
static struct spi_oc_simple_cfg spi_oc_simple_cfg_1 = {
|
||||
.base = DT_INST_1_OPENCORES_SPI_SIMPLE_CONTROL_BASE_ADDRESS,
|
||||
};
|
||||
|
||||
static struct spi_oc_simple_data spi_oc_simple_data_1 = {
|
||||
SPI_CONTEXT_INIT_LOCK(spi_oc_simple_data_1, ctx),
|
||||
SPI_CONTEXT_INIT_SYNC(spi_oc_simple_data_1, ctx),
|
||||
};
|
||||
|
||||
DEVICE_AND_API_INIT(spi_oc_simple_1,
|
||||
DT_INST_1_OPENCORES_SPI_SIMPLE_LABEL,
|
||||
spi_oc_simple_init,
|
||||
&spi_oc_simple_data_1,
|
||||
&spi_oc_simple_cfg_1,
|
||||
POST_KERNEL,
|
||||
CONFIG_SPI_INIT_PRIORITY,
|
||||
&spi_oc_simple_api);
|
||||
#endif
|
34
drivers/spi/spi_oc_simple.h
Normal file
34
drivers/spi/spi_oc_simple.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Western Digital Corporation or its affiliates
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "spi_context.h"
|
||||
|
||||
#define SPI_OC_SIMPLE_DATA(dev) \
|
||||
((struct spi_oc_simple_data *) ((dev)->driver_data))
|
||||
|
||||
#define SPI_OC_SIMPLE_REG(info, offset) \
|
||||
((mem_addr_t) (info->base + \
|
||||
(offset * CONFIG_SPI_OC_SIMPLE_BUS_WIDTH / 8)))
|
||||
|
||||
#define SPI_OC_SIMPLE_SPCR(dev) SPI_OC_SIMPLE_REG(dev, 0x0)
|
||||
#define SPI_OC_SIMPLE_SPSR(dev) SPI_OC_SIMPLE_REG(dev, 0x1)
|
||||
#define SPI_OC_SIMPLE_SPDR(dev) SPI_OC_SIMPLE_REG(dev, 0x2)
|
||||
#define SPI_OC_SIMPLE_SPER(dev) SPI_OC_SIMPLE_REG(dev, 0x3)
|
||||
#define SPI_OC_SIMPLE_SPSS(dev) SPI_OC_SIMPLE_REG(dev, 0x4)
|
||||
|
||||
#define SPI_OC_SIMPLE_SPCR_SPE BIT(6)
|
||||
#define SPI_OC_SIMPLE_SPCR_CPOL BIT(3)
|
||||
#define SPI_OC_SIMPLE_SPCR_CPHA BIT(2)
|
||||
|
||||
struct spi_oc_simple_cfg {
|
||||
u32_t base;
|
||||
u32_t f_sys;
|
||||
};
|
||||
|
||||
struct spi_oc_simple_data {
|
||||
struct spi_context ctx;
|
||||
};
|
||||
|
15
dts/bindings/spi/opencores,spi-simple.yaml
Normal file
15
dts/bindings/spi/opencores,spi-simple.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Copyright (c) 2019 Western Digital Corporation or its affiliates
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
title: OpenCores Simple SPI driver
|
||||
|
||||
description: >
|
||||
This binding gives a base representation of the OpenCores Simple SPI controller
|
||||
|
||||
compatible: "opencores,spi-simple"
|
||||
|
||||
include: spi-controller.yaml
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
Loading…
Add table
Add a link
Reference in a new issue