zephyr/drivers/spi/spi_oc_simple.c
Martí Bolívar 7e0eed9235 devicetree: allow access to all nodes
Usually, we want to operate only on "available" device
nodes ("available" means "status is okay and a matching binding is
found"), but that's not true in all cases.

Sometimes we want to operate on special nodes without matching
bindings, such as those describing memory.

To handle the distinction, change various additional devicetree APIs
making it clear that they operate only on available device nodes,
adjusting gen_defines and devicetree.h implementation details
accordingly:

- emit macros for all existing nodes in gen_defines.py, regardless
  of status or matching binding
- rename DT_NUM_INST to DT_NUM_INST_STATUS_OKAY
- rename DT_NODE_HAS_COMPAT to DT_NODE_HAS_COMPAT_STATUS_OKAY
- rename DT_INST_FOREACH to DT_INST_FOREACH_STATUS_OKAY
- rename DT_ANY_INST_ON_BUS to DT_ANY_INST_ON_BUS_STATUS_OKAY
- rewrite DT_HAS_NODE_STATUS_OKAY in terms of a new DT_NODE_HAS_STATUS
- resurrect DT_HAS_NODE in the form of DT_NODE_EXISTS
- remove DT_COMPAT_ON_BUS as a public API
- use the new default_prop_types edtlib parameter

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
2020-05-08 19:37:18 -05:00

225 lines
5.6 KiB
C

/*
* Copyright (c) 2019 Western Digital Corporation or its affiliates
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT opencores_spi_simple
#define LOG_LEVEL CONFIG_SPI_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(spi_oc_simple);
#include <sys/sys_io.h>
#include <drivers/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_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_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;
}
#define SPI_OC_INIT(inst) \
static struct spi_oc_simple_cfg spi_oc_simple_cfg_##inst = { \
.base = DT_INST_REG_ADDR_BY_NAME(inst, control), \
}; \
\
static struct spi_oc_simple_data spi_oc_simple_data_##inst = { \
SPI_CONTEXT_INIT_LOCK(spi_oc_simple_data_##inst, ctx), \
SPI_CONTEXT_INIT_SYNC(spi_oc_simple_data_##inst, ctx), \
}; \
\
DEVICE_AND_API_INIT(spi_oc_simple_##inst, \
DT_INST_LABEL(inst), \
spi_oc_simple_init, \
&spi_oc_simple_data_##inst, \
&spi_oc_simple_cfg_##inst, \
POST_KERNEL, \
CONFIG_SPI_INIT_PRIORITY, \
&spi_oc_simple_api);
DT_INST_FOREACH_STATUS_OKAY(SPI_OC_INIT)