SNAPKITTYWEST's picture
October 2026 main drop: mirror from GitHub
829b19c verified
Raw History Blame Contribute Delete
4.03 kB
/*
* OMAP3530 McSPI driver
*/
#include "spi.h"
int spi_init(spi_dev_t *dev, uint32_t base, uint8_t channel,
uint32_t clock_hz, uint32_t speed_hz, uint8_t mode, uint8_t bpw) {
dev->base = (hw_reg32_t)base;
dev->channel = channel;
dev->clock_hz = clock_hz;
dev->speed_hz = speed_hz;
dev->mode = mode;
dev->bits_per_word = bpw;
/* Reset McSPI module */
reg_write32(dev->base + MCSPI_SYSCONFIG, 0x02);
delay_cycles(100);
while (!(reg_read32(dev->base + MCSPI_SYSSTATUS) & 0x01))
;
/* Single-channel master mode */
reg_write32(dev->base + MCSPI_MODULCTRL, MCSPI_MODULCTRL_SINGLE);
/* Configure channel */
uint32_t chconf = 0;
/* Word length: value = (bpw - 1), 0 means 32-bit */
if (bpw < 32)
chconf |= ((bpw - 1) << MCSPI_CHCONF_WL_SHIFT);
/* Clock divider: f_spi = f_input / (2 * (CLKD + 1)) */
uint32_t clkdiv = (dev->clock_hz / (2 * dev->speed_hz)) - 1;
if (clkdiv > 0xF) clkdiv = 0xF;
chconf |= (clkdiv << MCSPI_CHCONF_CLKD_SHIFT);
/* Clock polarity and phase */
if (mode & 2) chconf |= MCSPI_CHCONF_POL;
if (mode & 1) chconf |= MCSPI_CHCONF_PHA;
/* Enable TX on channel 0, RX enabled */
chconf &= ~MCSPI_CHCONF_DPE0; /* DPE0=0: TX enabled on SPIEN0 */
chconf |= MCSPI_CHCONF_DPE1; /* DPE1=1: TX disabled on SPIEN1 */
chconf &= ~MCSPI_CHCONF_IS; /* IS=0: data line 0 for RX */
reg_write32(dev->base + MCSPI_CHCONF(dev->channel), chconf);
/* Disable FIFOs for simple polled mode */
chconf = reg_read32(dev->base + MCSPI_CHCONF(dev->channel));
chconf &= ~(MCSPI_CHCONF_FFER | MCSPI_CHCONF_FFEW);
reg_write32(dev->base + MCSPI_CHCONF(dev->channel), chconf);
/* Enable channel */
reg_write32(dev->base + MCSPI_CHCTRL(dev->channel), MCSPI_CHCTRL_EN);
return 0;
}
void spi_set_speed(spi_dev_t *dev, uint32_t speed_hz) {
dev->speed_hz = speed_hz;
uint32_t clkdiv = (dev->clock_hz / (2 * dev->speed_hz)) - 1;
if (clkdiv > 0xF) clkdiv = 0xF;
uint32_t chconf = reg_read32(dev->base + MCSPI_CHCONF(dev->channel));
chconf &= ~(0xF << MCSPI_CHCONF_CLKD_SHIFT);
chconf |= (clkdiv << MCSPI_CHCONF_CLKD_SHIFT);
reg_write32(dev->base + MCSPI_CHCONF(dev->channel), chconf);
}
int spi_transfer(spi_dev_t *dev, const uint8_t *tx, uint8_t *rx, uint32_t len) {
uint32_t ch = dev->channel;
for (uint32_t i = 0; i < len; i++) {
/* Force SPIEN active */
uint32_t chconf = reg_read32(dev->base + MCSPI_CHCONF(ch));
chconf |= MCSPI_CHCONF_FORCE;
reg_write32(dev->base + MCSPI_CHCONF(ch), chconf);
/* Wait for TX empty */
while (!(reg_read32(dev->base + MCSPI_CHSTAT(ch)) & MCSPI_CHSTAT_TXS))
;
/* Write TX data */
uint32_t data = tx ? tx[i] : 0xFF;
reg_write32(dev->base + MCSPI_TX(ch), data);
/* Wait for RX ready */
while (!(reg_read32(dev->base + MCSPI_CHSTAT(ch)) & MCSPI_CHSTAT_RXS))
;
/* Read RX data */
if (rx)
rx[i] = (uint8_t)(reg_read32(dev->base + MCSPI_RX(ch)) & 0xFF);
else
reg_read32(dev->base + MCSPI_RX(ch)); /* Discard */
/* De-assert SPIEN on last byte */
if (i == len - 1) {
chconf &= ~MCSPI_CHCONF_FORCE;
reg_write32(dev->base + MCSPI_CHCONF(ch), chconf);
}
}
return 0;
}
int spi_write_then_read(spi_dev_t *dev, const uint8_t *tx, uint32_t tx_len,
uint8_t *rx, uint32_t rx_len) {
if (spi_transfer(dev, tx, (uint8_t *)0, tx_len) != 0)
return -1;
return spi_transfer(dev, (const uint8_t *)0, rx, rx_len);
}
uint8_t spi_read_reg8(spi_dev_t *dev, uint8_t reg) {
uint8_t tx = reg | 0x80; /* Read bit */
uint8_t rx;
spi_write_then_read(dev, &tx, 1, &rx, 1);
return rx;
}
void spi_write_reg8(spi_dev_t *dev, uint8_t reg, uint8_t val) {
uint8_t tx[2] = { reg & 0x7F, val };
spi_transfer(dev, tx, (uint8_t *)0, 2);
}