Files
gps/soc/software/bsp/drivers/pmod_spi.c
T
2026-07-12 12:55:57 +03:00

205 lines
4.9 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
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_CSN | PMOD_SPI_CTRL_QUAD);
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_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;
}