forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for the OpenCores simple_spi controller Signed-off-by: Olof Kindgren <[email protected]>
- Loading branch information
Showing
6 changed files
with
313 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |