Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cpu/fe310: add spi peripheral driver #12957

Merged
merged 3 commits into from
Jan 14, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion boards/hifive1/Makefile.features
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ CPU_MODEL = fe310_g000
#FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_rtt
#FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart
17 changes: 17 additions & 0 deletions boards/hifive1/include/periph_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,23 @@ static const uart_conf_t uart_config[] = {
#define UART_NUMOF ARRAY_SIZE(uart_config)
/** @} */

/**
* @name SPI device configuration
*
* @{
*/
static const spi_conf_t spi_config[] = {
{
.addr = SPI1_CTRL_ADDR,
.mosi = GPIO_PIN(0, 3), /* D11 */
.miso = GPIO_PIN(0, 4), /* D12 */
.sclk = GPIO_PIN(0, 5), /* D13 */
},
};

#define SPI_NUMOF ARRAY_SIZE(spi_config)
/** @} */

/**
* @name RTT/RTC configuration
*
Expand Down
2 changes: 1 addition & 1 deletion boards/hifive1b/Makefile.features
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ CPU_MODEL = fe310_g002
#FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_rtt
#FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

Expand Down
17 changes: 17 additions & 0 deletions boards/hifive1b/include/periph_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,23 @@ static const uart_conf_t uart_config[] = {
#define UART_NUMOF ARRAY_SIZE(uart_config)
/** @} */

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like they could share the configuration file no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes and I'm planning to merge them in a common folder once all the new features are in: all other board peripheral configurations could also be shared in fact.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you don't want to address here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But ina follow up?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, otherwise, I'll have to rework all the other ones (i2c, pwm).

* @name SPI device configuration
*
* @{
*/
static const spi_conf_t spi_config[] = {
{
.addr = SPI1_CTRL_ADDR,
.mosi = GPIO_PIN(0, 3), /* D11 */
.miso = GPIO_PIN(0, 4), /* D12 */
.sclk = GPIO_PIN(0, 5), /* D13 */
},
};

#define SPI_NUMOF ARRAY_SIZE(spi_config)
/** @} */

/**
* @name RTT/RTC configuration
*
Expand Down
19 changes: 19 additions & 0 deletions cpu/fe310/include/periph_cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,25 @@ typedef struct {
*/
#define UART_ISR_PRIO (2)

/**
* @name This CPU makes use of the following shared SPI functions
* @{
*/
#define PERIPH_SPI_NEEDS_TRANSFER_BYTE 1
#define PERIPH_SPI_NEEDS_TRANSFER_REG 1
#define PERIPH_SPI_NEEDS_TRANSFER_REGS 1
/** @} */

/**
* @brief Structure for SPI configuration data
*/
typedef struct {
uint32_t addr; /**< SPI control register address */
gpio_t mosi; /**< MOSI pin */
gpio_t miso; /**< MISO pin */
gpio_t sclk; /**< SCLK pin */
} spi_conf_t;

/**
* @brief Prevent shared timer functions from being used
*/
Expand Down
156 changes: 156 additions & 0 deletions cpu/fe310/periph/spi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Copyright (C) 2019 Tristan Bruns
* 2019 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @ingroup cpu_fe310
* @ingroup drivers_periph_spi
*
* @{
*
* @file spi.c
* @brief Low-level SPI driver implementation
*
* @author Tristan Bruns
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*
* @}
*/

#include "cpu.h"
#include "mutex.h"
#include "assert.h"
#include "periph/spi.h"

#include "vendor/spi.h"

#define ENABLE_DEBUG (0)
#include "debug.h"

static const uint32_t _spi_clks[] = {
100000,
400000,
1000000,
5000000,
10000000,
};

#define SPI_CLK_NUMOF ARRAY_SIZE(_spi_clks)

static uint32_t _spi_clks_config[SPI_CLK_NUMOF] = { 0 };

/* DIV_UP is division which rounds up instead of down */
#define SPI_DIV_UP(a,b) (((a) + ((b) - 1)) / (b))

/**
* @brief Allocation device locks
*/
static mutex_t lock;

void spi_init(spi_t dev)
{
/* make sure given bus device is valid */
assert(dev < SPI_NUMOF);

/* initialize the buses lock */
mutex_init(&lock);

for (uint8_t i = 0; i < SPI_CLK_NUMOF; ++i) {
_spi_clks_config[i] = SPI_DIV_UP(cpu_freq(), 2 * _spi_clks[i]) - 1;
}

/* trigger pin initialization */
spi_init_pins(dev);

/* disable hardware chip select
(hardware chip select only supports one-byte transfers...) */
_REG32(spi_config[dev].addr, SPI_REG_CSMODE) = SPI_CSMODE_OFF;
}

void spi_init_pins(spi_t dev)
{
assert(dev < SPI_NUMOF);

const gpio_t spi1_pins =
(1 << spi_config[dev].mosi) |
(1 << spi_config[dev].miso) |
(1 << spi_config[dev].sclk);

/* Enable I/O Function 0 */
GPIO_REG(GPIO_IOF_EN) |= spi1_pins;
GPIO_REG(GPIO_IOF_SEL) &= ~spi1_pins;
}

int spi_init_cs(spi_t dev, spi_cs_t cs)
{
(void)dev;
assert(dev < SPI_NUMOF);

/* setting the CS high before configuring it as an
output should be fine on FE310. */
Comment on lines +94 to +95
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, not really, this was already implemented this way in the original PR. Maybe @pyropeter can give some insight ?

gpio_set(cs);

if (gpio_init(cs, GPIO_OUT)) {
return SPI_NOCS;
}

return SPI_OK;
}

int spi_acquire(spi_t dev, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk)
{
(void)cs;
assert(dev < SPI_NUMOF);

mutex_lock(&lock);

_REG32(spi_config[dev].addr, SPI_REG_SCKDIV) = _spi_clks_config[clk];
_REG32(spi_config[dev].addr, SPI_REG_SCKMODE) = mode;

return SPI_OK;
}

void spi_release(spi_t dev)
{
(void)dev;

mutex_unlock(&lock);
}

void spi_transfer_bytes(spi_t dev, spi_cs_t cs, bool cont,
const void *out_, void *in_, size_t len)
{
assert(dev < SPI_NUMOF);
assert((out_ || in_) && len > 0);
assert(_REG32(spi_config[dev].addr, SPI_REG_RXFIFO) & SPI_RXFIFO_EMPTY);
assert(!(_REG32(spi_config[dev].addr, SPI_REG_TXFIFO) & SPI_TXFIFO_FULL));

const uint8_t *out = out_;
uint8_t *in = in_;

if (cs != SPI_CS_UNDEF) {
gpio_clear(cs);
}

for (size_t i = 0; i < len; i++) {
_REG32(spi_config[dev].addr, SPI_REG_TXFIFO) = out ? out[i] : 0;

uint32_t rxdata;
do {
rxdata = _REG32(spi_config[dev].addr, SPI_REG_RXFIFO);
} while (rxdata & SPI_RXFIFO_EMPTY);

if (in) {
in[i] = (uint8_t)rxdata;
}
}

if (cs != SPI_CS_UNDEF && !cont) {
gpio_set(cs);
}
}