drivers/spi: SPI driver for SiFive Freedom
A driver for the sifive,spi0 SPI device on SiFive Freedom platforms Signed-off-by: Nathaniel Graff <nathaniel.graff@sifive.com>
This commit is contained in:
parent
5dbb147993
commit
9e2ef8db6d
5 changed files with 384 additions and 0 deletions
|
@ -7,6 +7,7 @@ zephyr_library_sources_ifdef(CONFIG_SPI_MCUX_DSPI spi_mcux_dspi.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_SPI_MCUX_LPSPI spi_mcux_lpspi.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_SPI_SAM spi_sam.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_SPI_SAM0 spi_sam0.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_SPI_SIFIVE spi_sifive.c)
|
||||
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)
|
||||
|
|
|
@ -180,6 +180,8 @@ source "drivers/spi/Kconfig.sam"
|
|||
|
||||
source "drivers/spi/Kconfig.sam0"
|
||||
|
||||
source "drivers/spi/Kconfig.sifive"
|
||||
|
||||
source "drivers/spi/Kconfig.nrfx"
|
||||
|
||||
endif # SPI
|
||||
|
|
31
drivers/spi/Kconfig.sifive
Normal file
31
drivers/spi/Kconfig.sifive
Normal file
|
@ -0,0 +1,31 @@
|
|||
# Kconfig - SiFive SPI Driver configuration options
|
||||
|
||||
#
|
||||
# Copyright (c) 2018 SiFive Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
menuconfig SPI_SIFIVE
|
||||
bool "SiFive SPI controller driver"
|
||||
depends on SPI
|
||||
depends on SOC_SERIES_RISCV32_SIFIVE_FREEDOM
|
||||
help
|
||||
Enable the SPI peripherals on SiFive Freedom processors
|
||||
|
||||
if SPI_SIFIVE
|
||||
|
||||
config SIFIVE_SPI_0_ROM
|
||||
bool "SPI 0 is used to access SPI Flash ROM"
|
||||
default y
|
||||
depends on SPI_SIFIVE
|
||||
help
|
||||
If enabled, SPI 0 is reserved for accessing the SPI flash ROM and a
|
||||
driver interface won't be instantiated for SPI 0.
|
||||
|
||||
Beware disabling this option on HiFive 1! The SPI flash ROM is where the
|
||||
program is stored, and if this driver initializes the interface for
|
||||
peripheral control the FE310 will crash on boot.
|
||||
|
||||
endif # SPI_SIFIVE
|
||||
|
275
drivers/spi/spi_sifive.c
Normal file
275
drivers/spi/spi_sifive.c
Normal file
|
@ -0,0 +1,275 @@
|
|||
/*
|
||||
* Copyright (c) 2018 SiFive Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define LOG_LEVEL CONFIG_SPI_LOG_LEVEL
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(spi_sifive);
|
||||
|
||||
#include "spi_sifive.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Helper Functions */
|
||||
|
||||
static inline void sys_set_mask(mem_addr_t addr, u32_t mask, u32_t value)
|
||||
{
|
||||
u32_t temp = sys_read32(addr);
|
||||
|
||||
temp &= ~(mask);
|
||||
temp |= value;
|
||||
|
||||
sys_write32(temp, addr);
|
||||
}
|
||||
|
||||
int spi_config(struct device *dev, u32_t frequency, u16_t operation)
|
||||
{
|
||||
u32_t div;
|
||||
u32_t fmt_len;
|
||||
|
||||
if (SPI_OP_MODE_GET(operation) != SPI_OP_MODE_MASTER) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (operation & SPI_MODE_LOOP) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Set the SPI frequency */
|
||||
div = (SPI_CFG(dev)->f_sys / (2 * frequency)) - 1;
|
||||
sys_write32((SF_SCKDIV_DIV_MASK & div), SPI_REG(dev, REG_SCKDIV));
|
||||
|
||||
/* Set the polarity */
|
||||
if (operation & SPI_MODE_CPOL) {
|
||||
/* If CPOL is set, then SCK idles at logical 1 */
|
||||
sys_set_bit(SPI_REG(dev, REG_SCKMODE), SF_SCKMODE_POL);
|
||||
} else {
|
||||
/* SCK idles at logical 0 */
|
||||
sys_clear_bit(SPI_REG(dev, REG_SCKMODE), SF_SCKMODE_POL);
|
||||
}
|
||||
|
||||
/* Set the phase */
|
||||
if (operation & SPI_MODE_CPHA) {
|
||||
/*
|
||||
* If CPHA is set, then data is sampled
|
||||
* on the trailing SCK edge
|
||||
*/
|
||||
sys_set_bit(SPI_REG(dev, REG_SCKMODE), SF_SCKMODE_PHA);
|
||||
} else {
|
||||
/* Data is sampled on the leading SCK edge */
|
||||
sys_clear_bit(SPI_REG(dev, REG_SCKMODE), SF_SCKMODE_PHA);
|
||||
}
|
||||
|
||||
/* Get the frame length */
|
||||
fmt_len = SPI_WORD_SIZE_GET(operation);
|
||||
if (fmt_len > SF_FMT_LEN_MASK) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Set the frame length */
|
||||
fmt_len = fmt_len << SF_FMT_LEN;
|
||||
fmt_len &= SF_FMT_LEN_MASK;
|
||||
sys_set_mask(SPI_REG(dev, REG_FMT), SF_FMT_LEN_MASK, fmt_len);
|
||||
|
||||
if ((operation & SPI_LINES_MASK) != SPI_LINES_SINGLE) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
/* Set single line operation */
|
||||
sys_set_mask(SPI_REG(dev, REG_FMT),
|
||||
SF_FMT_PROTO_MASK,
|
||||
SF_FMT_PROTO_SINGLE);
|
||||
|
||||
/* Set the endianness */
|
||||
if (operation & SPI_TRANSFER_LSB) {
|
||||
sys_set_bit(SPI_REG(dev, REG_FMT), SF_FMT_ENDIAN);
|
||||
} else {
|
||||
sys_clear_bit(SPI_REG(dev, REG_FMT), SF_FMT_ENDIAN);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spi_sifive_send(struct device *dev, u16_t frame)
|
||||
{
|
||||
while (SPI_REG(dev, REG_TXDATA) & SF_TXDATA_FULL)
|
||||
;
|
||||
|
||||
sys_write32((u32_t) frame, SPI_REG(dev, REG_TXDATA));
|
||||
}
|
||||
|
||||
u16_t spi_sifive_recv(struct device *dev)
|
||||
{
|
||||
u32_t val;
|
||||
|
||||
while ((val = sys_read32(SPI_REG(dev, REG_RXDATA))) & SF_RXDATA_EMPTY)
|
||||
;
|
||||
|
||||
return (u16_t) val;
|
||||
}
|
||||
|
||||
void spi_sifive_xfer(struct device *dev, const bool hw_cs_control)
|
||||
{
|
||||
struct spi_context *ctx = &SPI_DATA(dev)->ctx;
|
||||
|
||||
u32_t send_len = spi_context_longest_current_buf(ctx);
|
||||
|
||||
for (u32_t i = 0; i < send_len; i++) {
|
||||
|
||||
/* Send a frame */
|
||||
if (i < ctx->tx_len) {
|
||||
spi_sifive_send(dev, (u16_t) (ctx->tx_buf)[i]);
|
||||
} else {
|
||||
/* Send dummy bytes */
|
||||
spi_sifive_send(dev, 0);
|
||||
}
|
||||
|
||||
/* Receive a frame */
|
||||
if (i < ctx->rx_len) {
|
||||
ctx->rx_buf[i] = (u8_t) spi_sifive_recv(dev);
|
||||
} else {
|
||||
/* Discard returned value */
|
||||
spi_sifive_recv(dev);
|
||||
}
|
||||
}
|
||||
|
||||
/* Deassert the CS line */
|
||||
if (!hw_cs_control) {
|
||||
spi_context_cs_control(&SPI_DATA(dev)->ctx, false);
|
||||
} else {
|
||||
sys_write32(SF_CSMODE_OFF, SPI_REG(dev, REG_CSMODE));
|
||||
}
|
||||
|
||||
spi_context_complete(ctx, 0);
|
||||
}
|
||||
|
||||
/* API Functions */
|
||||
|
||||
int spi_sifive_init(struct device *dev)
|
||||
{
|
||||
/* Disable SPI Flash mode */
|
||||
sys_clear_bit(SPI_REG(dev, REG_FCTRL), SF_FCTRL_EN);
|
||||
|
||||
/* Make sure the context is unlocked */
|
||||
spi_context_unlock_unconditionally(&SPI_DATA(dev)->ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spi_sifive_transceive(struct device *dev,
|
||||
const struct spi_config *config,
|
||||
const struct spi_buf_set *tx_bufs,
|
||||
const struct spi_buf_set *rx_bufs)
|
||||
{
|
||||
int rc = 0;
|
||||
bool hw_cs_control = false;
|
||||
|
||||
/* Lock the SPI Context */
|
||||
spi_context_lock(&SPI_DATA(dev)->ctx, false, NULL);
|
||||
|
||||
/* Configure the SPI bus */
|
||||
SPI_DATA(dev)->ctx.config = config;
|
||||
|
||||
/*
|
||||
* If the chip select configuration is not present, we'll ask the
|
||||
* SPI peripheral itself to control the CS line
|
||||
*/
|
||||
if (config->cs == NULL) {
|
||||
hw_cs_control = true;
|
||||
}
|
||||
|
||||
if (!hw_cs_control) {
|
||||
/*
|
||||
* If the user has requested manual GPIO control, ask the
|
||||
* context for control and disable HW control
|
||||
*/
|
||||
spi_context_cs_configure(&SPI_DATA(dev)->ctx);
|
||||
sys_write32(SF_CSMODE_OFF, SPI_REG(dev, REG_CSMODE));
|
||||
} else {
|
||||
/*
|
||||
* Tell the hardware to control the requested CS pin.
|
||||
* NOTE:
|
||||
* For the SPI peripheral, the pin number is not the
|
||||
* GPIO pin, but the index into the list of available
|
||||
* CS lines for the SPI peripheral.
|
||||
*/
|
||||
sys_write32(config->slave, SPI_REG(dev, REG_CSID));
|
||||
sys_write32(SF_CSMODE_OFF, SPI_REG(dev, REG_CSMODE));
|
||||
}
|
||||
|
||||
rc = spi_config(dev, config->frequency, config->operation);
|
||||
if (rc < 0) {
|
||||
spi_context_release(&SPI_DATA(dev)->ctx, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
spi_context_buffers_setup(&SPI_DATA(dev)->ctx, tx_bufs, rx_bufs, 1);
|
||||
|
||||
/* Assert the CS line */
|
||||
if (!hw_cs_control) {
|
||||
spi_context_cs_control(&SPI_DATA(dev)->ctx, true);
|
||||
} else {
|
||||
sys_write32(SF_CSMODE_HOLD, SPI_REG(dev, REG_CSMODE));
|
||||
}
|
||||
|
||||
/* Perform transfer */
|
||||
spi_sifive_xfer(dev, hw_cs_control);
|
||||
|
||||
rc = spi_context_wait_for_completion(&SPI_DATA(dev)->ctx);
|
||||
|
||||
spi_context_release(&SPI_DATA(dev)->ctx, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int spi_sifive_release(struct device *dev, const struct spi_config *config)
|
||||
{
|
||||
spi_context_unlock_unconditionally(&SPI_DATA(dev)->ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Device Instantiation */
|
||||
|
||||
static struct spi_driver_api spi_sifive_api = {
|
||||
.transceive = spi_sifive_transceive,
|
||||
.release = spi_sifive_release,
|
||||
};
|
||||
|
||||
#define SPI_INIT(n) \
|
||||
static struct spi_sifive_data spi_sifive_data_##n = { \
|
||||
SPI_CONTEXT_INIT_LOCK(spi_sifive_data_##n, ctx), \
|
||||
SPI_CONTEXT_INIT_SYNC(spi_sifive_data_##n, ctx), \
|
||||
}; \
|
||||
static struct spi_sifive_cfg spi_sifive_cfg_##n = { \
|
||||
.base = DT_SIFIVE_SPI0_##n##_CONTROL_BASE_ADDRESS, \
|
||||
.f_sys = DT_SIFIVE_SPI0_##n##_CLOCK_FREQUENCY, \
|
||||
}; \
|
||||
DEVICE_AND_API_INIT(spi_##n, \
|
||||
DT_SIFIVE_SPI0_##n##_LABEL, \
|
||||
spi_sifive_init, \
|
||||
&spi_sifive_data_##n, \
|
||||
&spi_sifive_cfg_##n, \
|
||||
POST_KERNEL, \
|
||||
CONFIG_SPI_INIT_PRIORITY, \
|
||||
&spi_sifive_api)
|
||||
|
||||
#ifndef CONFIG_SIFIVE_SPI_0_ROM
|
||||
#ifdef DT_SIFIVE_SPI0_0_LABEL
|
||||
|
||||
SPI_INIT(0);
|
||||
|
||||
#endif /* DT_SIFIVE_SPI0_0_LABEL */
|
||||
#endif /* !DT_SIFIVE_SPI0_0_ROM */
|
||||
|
||||
#ifdef DT_SIFIVE_SPI0_1_LABEL
|
||||
|
||||
SPI_INIT(1);
|
||||
|
||||
#endif /* DT_SIFIVE_SPI0_1_LABEL */
|
||||
|
||||
#ifdef DT_SIFIVE_SPI0_2_LABEL
|
||||
|
||||
SPI_INIT(2);
|
||||
|
||||
#endif /* DT_SIFIVE_SPI0_2_LABEL */
|
||||
|
75
drivers/spi/spi_sifive.h
Normal file
75
drivers/spi/spi_sifive.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2018 SiFive Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _SPI_SIFIVE__H
|
||||
#define _SPI_SIFIVE__H
|
||||
|
||||
#include "spi_context.h"
|
||||
|
||||
#include <sys_io.h>
|
||||
#include <device.h>
|
||||
#include <spi.h>
|
||||
|
||||
#define SPI_CFG(dev) ((struct spi_sifive_cfg *) ((dev)->config->config_info))
|
||||
#define SPI_DATA(dev) ((struct spi_sifive_data *) ((dev)->driver_data))
|
||||
|
||||
#define SPI_REG(dev, offset) ((mem_addr_t) (SPI_CFG(dev)->base + (offset)))
|
||||
|
||||
/* Register Offsets */
|
||||
#define REG_SCKDIV 0x000
|
||||
#define REG_SCKMODE 0x004
|
||||
#define REG_CSID 0x010
|
||||
#define REG_CSDEF 0x014
|
||||
#define REG_CSMODE 0x018
|
||||
#define REG_DELAY0 0x028
|
||||
#define REG_DELAY1 0x02C
|
||||
#define REG_FMT 0x040
|
||||
#define REG_TXDATA 0x048
|
||||
#define REG_RXDATA 0x04C
|
||||
#define REG_TXMARK 0x050
|
||||
#define REG_RXMARK 0x054
|
||||
#define REG_FCTRL 0x060
|
||||
#define REG_FFMT 0x064
|
||||
#define REG_IE 0x070
|
||||
#define REG_IP 0x074
|
||||
|
||||
/* Masks */
|
||||
#define SF_SCKDIV_DIV_MASK (0xFFF << 0)
|
||||
#define SF_FMT_PROTO_MASK (0x3 << 0)
|
||||
#define SF_FMT_LEN_MASK (0xF << 16)
|
||||
|
||||
/* Offsets */
|
||||
#define SF_SCKMODE_POL 1
|
||||
#define SF_SCKMODE_PHA 0
|
||||
|
||||
#define SF_FMT_LEN 16
|
||||
#define SF_FMT_ENDIAN 2
|
||||
|
||||
#define SF_FCTRL_EN 0
|
||||
|
||||
/* Values */
|
||||
#define SF_CSMODE_AUTO 0
|
||||
#define SF_CSMODE_HOLD 2
|
||||
#define SF_CSMODE_OFF 3
|
||||
|
||||
#define SF_FMT_PROTO_SINGLE 0
|
||||
|
||||
#define SF_TXDATA_FULL (1 << 31)
|
||||
#define SF_RXDATA_EMPTY (1 << 31)
|
||||
|
||||
/* Structure Declarations */
|
||||
|
||||
struct spi_sifive_data {
|
||||
struct spi_context ctx;
|
||||
};
|
||||
|
||||
struct spi_sifive_cfg {
|
||||
u32_t base;
|
||||
u32_t f_sys;
|
||||
};
|
||||
|
||||
#endif /* _SPI_SIFIVE__H */
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue