#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; }