Skip to content

Commit

Permalink
drivers: spi: Add simple_spi driver
Browse files Browse the repository at this point in the history
Add support for the OpenCores simple_spi controller

Signed-off-by: Olof Kindgren <[email protected]>
  • Loading branch information
olofk authored and galak committed Oct 22, 2019
1 parent 4d97252 commit d09614a
Show file tree
Hide file tree
Showing 6 changed files with 313 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/spi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
2 changes: 2 additions & 0 deletions drivers/spi/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,6 @@ source "drivers/spi/Kconfig.cc13xx_cc26xx"

source "drivers/spi/Kconfig.litex"

source "drivers/spi/Kconfig.oc_simple"

endif # SPI
19 changes: 19 additions & 0 deletions drivers/spi/Kconfig.oc_simple
Original file line number Diff line number Diff line change
@@ -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 changes: 242 additions & 0 deletions drivers/spi/spi_oc_simple.c
Original file line number Diff line number Diff line change
@@ -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 changes: 34 additions & 0 deletions drivers/spi/spi_oc_simple.h
Original file line number Diff line number Diff line change
@@ -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 changes: 15 additions & 0 deletions dts/bindings/spi/opencores,spi-simple.yaml
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit d09614a

Please sign in to comment.