248 lines
6.0 KiB
C
248 lines
6.0 KiB
C
#include "bsp/pmod_spi.h"
|
|
|
|
#include "bsp/mmio.h"
|
|
#include "bsp/soc.h"
|
|
|
|
#define PMOD_SPI_CTRL (BSP_PMOD_SPI_BASE + 0x00u)
|
|
#define PMOD_SPI_TX (BSP_PMOD_SPI_BASE + 0x04u)
|
|
#define PMOD_SPI_RX (BSP_PMOD_SPI_BASE + 0x08u)
|
|
#define PMOD_SPI_READY (BSP_PMOD_SPI_BASE + 0x0cu)
|
|
|
|
#define PMOD_SPI_CTRL_BUSY 0x1u
|
|
#define PMOD_SPI_CTRL_CSN 0x2u
|
|
#define PMOD_SPI_CTRL_QUAD 0x4u
|
|
#define PMOD_SPI_CTRL_CPOL 0x8u
|
|
#define PMOD_SPI_CTRL_CPHA 0x10u
|
|
#define PMOD_SPI_CTRL_WRITABLE \
|
|
(PMOD_SPI_CTRL_CSN | PMOD_SPI_CTRL_QUAD | \
|
|
PMOD_SPI_CTRL_CPOL | PMOD_SPI_CTRL_CPHA)
|
|
|
|
static uint32_t pmod_spi_ctrl_shadow = PMOD_SPI_CTRL_CSN;
|
|
static uint32_t pmod_spi_cs_setup_us;
|
|
static uint32_t pmod_spi_inter_byte_us;
|
|
static uint32_t pmod_spi_cs_hold_us;
|
|
static uint32_t pmod_spi_cs_inactive_us;
|
|
|
|
static void pmod_spi_delay_us(uint32_t us)
|
|
{
|
|
if (us == 0u) {
|
|
return;
|
|
}
|
|
|
|
const uint64_t ticks =
|
|
((uint64_t)BSP_CLINT_TIMEBASE_HZ * us + 999999u) / 1000000u;
|
|
const uint64_t start = bsp_read64(BSP_CLINT_MTIME);
|
|
while ((bsp_read64(BSP_CLINT_MTIME) - start) < ticks) {
|
|
}
|
|
}
|
|
|
|
static void pmod_spi_wait_ready(void)
|
|
{
|
|
while ((bsp_read32(PMOD_SPI_READY) & 1u) == 0u) {
|
|
}
|
|
while ((bsp_read32(PMOD_SPI_CTRL) & PMOD_SPI_CTRL_BUSY) != 0u) {
|
|
}
|
|
}
|
|
|
|
static void pmod_spi_write_ctrl(uint32_t ctrl)
|
|
{
|
|
pmod_spi_wait_ready();
|
|
pmod_spi_ctrl_shadow = ctrl & PMOD_SPI_CTRL_WRITABLE;
|
|
bsp_write32(PMOD_SPI_CTRL, pmod_spi_ctrl_shadow);
|
|
}
|
|
|
|
void bsp_pmod_spi_init(void)
|
|
{
|
|
pmod_spi_write_ctrl(PMOD_SPI_CTRL_CSN);
|
|
}
|
|
|
|
void bsp_pmod_spi_set_mode(bsp_pmod_spi_mode_t mode)
|
|
{
|
|
uint32_t ctrl = pmod_spi_ctrl_shadow & ~PMOD_SPI_CTRL_QUAD;
|
|
if (mode == BSP_PMOD_SPI_MODE_QUAD) {
|
|
ctrl |= PMOD_SPI_CTRL_QUAD;
|
|
}
|
|
pmod_spi_write_ctrl(ctrl);
|
|
}
|
|
|
|
bsp_pmod_spi_mode_t bsp_pmod_spi_mode(void)
|
|
{
|
|
return (pmod_spi_ctrl_shadow & PMOD_SPI_CTRL_QUAD) != 0u
|
|
? BSP_PMOD_SPI_MODE_QUAD
|
|
: BSP_PMOD_SPI_MODE_SINGLE;
|
|
}
|
|
|
|
void bsp_pmod_spi_set_quad(int enabled)
|
|
{
|
|
bsp_pmod_spi_set_mode(enabled ? BSP_PMOD_SPI_MODE_QUAD : BSP_PMOD_SPI_MODE_SINGLE);
|
|
}
|
|
|
|
void bsp_pmod_spi_set_clock_mode(bsp_pmod_spi_clock_mode_t mode)
|
|
{
|
|
uint32_t ctrl =
|
|
pmod_spi_ctrl_shadow & ~(PMOD_SPI_CTRL_CPOL | PMOD_SPI_CTRL_CPHA);
|
|
|
|
switch (mode) {
|
|
case BSP_PMOD_SPI_CLOCK_MODE1:
|
|
ctrl |= PMOD_SPI_CTRL_CPHA;
|
|
break;
|
|
case BSP_PMOD_SPI_CLOCK_MODE2:
|
|
ctrl |= PMOD_SPI_CTRL_CPOL;
|
|
break;
|
|
case BSP_PMOD_SPI_CLOCK_MODE3:
|
|
ctrl |= PMOD_SPI_CTRL_CPOL | PMOD_SPI_CTRL_CPHA;
|
|
break;
|
|
case BSP_PMOD_SPI_CLOCK_MODE0:
|
|
default:
|
|
break;
|
|
}
|
|
pmod_spi_write_ctrl(ctrl);
|
|
}
|
|
|
|
bsp_pmod_spi_clock_mode_t bsp_pmod_spi_clock_mode(void)
|
|
{
|
|
const uint32_t bits =
|
|
pmod_spi_ctrl_shadow & (PMOD_SPI_CTRL_CPOL | PMOD_SPI_CTRL_CPHA);
|
|
if (bits == (PMOD_SPI_CTRL_CPOL | PMOD_SPI_CTRL_CPHA)) {
|
|
return BSP_PMOD_SPI_CLOCK_MODE3;
|
|
}
|
|
if (bits == PMOD_SPI_CTRL_CPOL) {
|
|
return BSP_PMOD_SPI_CLOCK_MODE2;
|
|
}
|
|
if (bits == PMOD_SPI_CTRL_CPHA) {
|
|
return BSP_PMOD_SPI_CLOCK_MODE1;
|
|
}
|
|
return BSP_PMOD_SPI_CLOCK_MODE0;
|
|
}
|
|
|
|
void bsp_pmod_spi_set_timing(uint32_t cs_setup_us,
|
|
uint32_t inter_byte_us,
|
|
uint32_t cs_hold_us,
|
|
uint32_t cs_inactive_us)
|
|
{
|
|
pmod_spi_cs_setup_us = cs_setup_us;
|
|
pmod_spi_inter_byte_us = inter_byte_us;
|
|
pmod_spi_cs_hold_us = cs_hold_us;
|
|
pmod_spi_cs_inactive_us = cs_inactive_us;
|
|
}
|
|
|
|
void bsp_pmod_spi_select(void)
|
|
{
|
|
pmod_spi_write_ctrl(pmod_spi_ctrl_shadow & ~PMOD_SPI_CTRL_CSN);
|
|
}
|
|
|
|
void bsp_pmod_spi_deselect(void)
|
|
{
|
|
pmod_spi_write_ctrl(pmod_spi_ctrl_shadow | PMOD_SPI_CTRL_CSN);
|
|
}
|
|
|
|
int bsp_pmod_spi_ready(void)
|
|
{
|
|
return ((bsp_read32(PMOD_SPI_READY) & 1u) != 0u) &&
|
|
((bsp_read32(PMOD_SPI_CTRL) & PMOD_SPI_CTRL_BUSY) == 0u);
|
|
}
|
|
|
|
uint32_t bsp_pmod_spi_ctrl(void)
|
|
{
|
|
return bsp_read32(PMOD_SPI_CTRL);
|
|
}
|
|
|
|
uint32_t bsp_pmod_spi_rx(void)
|
|
{
|
|
return bsp_read32(PMOD_SPI_RX);
|
|
}
|
|
|
|
uint8_t bsp_pmod_spi_xfer8(uint8_t value)
|
|
{
|
|
pmod_spi_wait_ready();
|
|
bsp_pmod_spi_set_mode(BSP_PMOD_SPI_MODE_SINGLE);
|
|
bsp_write32(PMOD_SPI_TX, ((uint32_t)value) << 24);
|
|
pmod_spi_wait_ready();
|
|
return (uint8_t)(bsp_read32(PMOD_SPI_RX) & 0xffu);
|
|
}
|
|
|
|
uint32_t bsp_pmod_spi_xfer32(uint32_t value)
|
|
{
|
|
pmod_spi_wait_ready();
|
|
bsp_pmod_spi_set_mode(BSP_PMOD_SPI_MODE_QUAD);
|
|
bsp_write32(PMOD_SPI_TX, value);
|
|
pmod_spi_wait_ready();
|
|
return bsp_read32(PMOD_SPI_RX);
|
|
}
|
|
|
|
void bsp_pmod_spi_transfer(const uint8_t *tx, uint8_t *rx, size_t len)
|
|
{
|
|
for (size_t i = 0; i < len; ++i) {
|
|
uint8_t out = tx ? tx[i] : 0xffu;
|
|
uint8_t in = bsp_pmod_spi_xfer8(out);
|
|
if (rx) {
|
|
rx[i] = in;
|
|
}
|
|
}
|
|
}
|
|
|
|
void bsp_pmod_spi_transaction(const uint8_t *tx, uint8_t *rx, size_t len)
|
|
{
|
|
if (len == 0u) {
|
|
return;
|
|
}
|
|
|
|
bsp_pmod_spi_select();
|
|
pmod_spi_delay_us(pmod_spi_cs_setup_us);
|
|
|
|
for (size_t i = 0; i < len; ++i) {
|
|
const uint8_t out = tx ? tx[i] : 0xffu;
|
|
const uint8_t in = bsp_pmod_spi_xfer8(out);
|
|
if (rx) {
|
|
rx[i] = in;
|
|
}
|
|
if ((i + 1u) < len) {
|
|
pmod_spi_delay_us(pmod_spi_inter_byte_us);
|
|
}
|
|
}
|
|
|
|
pmod_spi_delay_us(pmod_spi_cs_hold_us);
|
|
bsp_pmod_spi_deselect();
|
|
pmod_spi_delay_us(pmod_spi_cs_inactive_us);
|
|
}
|
|
|
|
void bsp_pmod_spi_write_reg8(uint8_t command, uint8_t value)
|
|
{
|
|
const uint8_t tx[2] = {command, value};
|
|
bsp_pmod_spi_transaction(tx, NULL, sizeof(tx));
|
|
}
|
|
|
|
void bsp_pmod_spi_read_regs(uint8_t command,
|
|
uint8_t dummy,
|
|
uint8_t *data,
|
|
size_t len)
|
|
{
|
|
if ((data == NULL) || (len == 0u)) {
|
|
return;
|
|
}
|
|
|
|
bsp_pmod_spi_select();
|
|
pmod_spi_delay_us(pmod_spi_cs_setup_us);
|
|
|
|
(void)bsp_pmod_spi_xfer8(command);
|
|
pmod_spi_delay_us(pmod_spi_inter_byte_us);
|
|
|
|
for (size_t i = 0; i < len; ++i) {
|
|
data[i] = bsp_pmod_spi_xfer8(dummy);
|
|
if ((i + 1u) < len) {
|
|
pmod_spi_delay_us(pmod_spi_inter_byte_us);
|
|
}
|
|
}
|
|
|
|
pmod_spi_delay_us(pmod_spi_cs_hold_us);
|
|
bsp_pmod_spi_deselect();
|
|
pmod_spi_delay_us(pmod_spi_cs_inactive_us);
|
|
}
|
|
|
|
uint8_t bsp_pmod_spi_read_reg8(uint8_t command, uint8_t dummy)
|
|
{
|
|
uint8_t value = 0xffu;
|
|
bsp_pmod_spi_read_regs(command, dummy, &value, 1u);
|
|
return value;
|
|
}
|