| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 | // Driver and interface for accessing SD card in SPI mode// Used on BlueSCSI v1.0.#include "BlueSCSI_platform.h"#include "BlueSCSI_log.h"#include "gd32f20x_spi.h"#include "gd32f20x_dma.h"#include <SdFat.h>#ifndef SD_USE_SDIOclass GD32SPIDriver : public SdSpiBaseClass{public:    void begin(SdSpiConfig config) {        rcu_periph_clock_enable(RCU_SPI0);        rcu_periph_clock_enable(RCU_DMA0);        dma_parameter_struct rx_dma_config =        {            .periph_addr = (uint32_t)&SPI_DATA(SD_SPI),            .periph_width = DMA_PERIPHERAL_WIDTH_8BIT,            .memory_addr = 0, // Set before transfer            .memory_width = DMA_MEMORY_WIDTH_8BIT,            .number = 0, // Set before transfer            .priority = DMA_PRIORITY_ULTRA_HIGH,            .periph_inc = DMA_PERIPH_INCREASE_DISABLE,            .memory_inc = DMA_MEMORY_INCREASE_ENABLE,            .direction = DMA_PERIPHERAL_TO_MEMORY        };        dma_init(DMA0, SD_SPI_RX_DMA_CHANNEL, &rx_dma_config);        dma_parameter_struct tx_dma_config =        {            .periph_addr = (uint32_t)&SPI_DATA(SD_SPI),            .periph_width = DMA_PERIPHERAL_WIDTH_8BIT,            .memory_addr = 0, // Set before transfer            .memory_width = DMA_MEMORY_WIDTH_8BIT,            .number = 0, // Set before transfer            .priority = DMA_PRIORITY_HIGH,            .periph_inc = DMA_PERIPH_INCREASE_DISABLE,            .memory_inc = DMA_MEMORY_INCREASE_ENABLE,            .direction = DMA_MEMORY_TO_PERIPHERAL        };        dma_init(DMA0, SD_SPI_TX_DMA_CHANNEL, &tx_dma_config);    }    void activate() {        spi_parameter_struct config = {            SPI_MASTER,            SPI_TRANSMODE_FULLDUPLEX,            SPI_FRAMESIZE_8BIT,            SPI_NSS_SOFT,            SPI_ENDIAN_MSB,            SPI_CK_PL_LOW_PH_1EDGE,            SPI_PSC_256        };        // Select closest available divider based on system frequency        int divider = (SystemCoreClock + m_sckfreq / 2) / m_sckfreq;        if (divider <= 2)            config.prescale = SPI_PSC_2;        else if (divider <= 4)            config.prescale = SPI_PSC_4;        else if (divider <= 8)            config.prescale = SPI_PSC_8;        else if (divider <= 16)            config.prescale = SPI_PSC_16;        else if (divider <= 32)            config.prescale = SPI_PSC_32;        else if (divider <= 64)            config.prescale = SPI_PSC_64;        else if (divider <= 128)            config.prescale = SPI_PSC_128;        else            config.prescale = SPI_PSC_256;        spi_init(SD_SPI, &config);        spi_enable(SD_SPI);    }    void deactivate() {        spi_disable(SD_SPI);    }    void wait_idle() {        while (!(SPI_STAT(SD_SPI) & SPI_STAT_TBE));        while (SPI_STAT(SD_SPI) & SPI_STAT_TRANS);    }    // Single byte receive    uint8_t receive() {        // Wait for idle and clear RX buffer        wait_idle();        (void)SPI_DATA(SD_SPI);        // Send dummy byte and wait for receive        SPI_DATA(SD_SPI) = 0xFF;        while (!(SPI_STAT(SD_SPI) & SPI_STAT_RBNE));        return SPI_DATA(SD_SPI);    }    // Single byte send    void send(uint8_t data) {        SPI_DATA(SD_SPI) = data;        wait_idle();    }    // Multiple byte receive    uint8_t receive(uint8_t* buf, size_t count)    {        // Wait for idle and clear RX buffer        wait_idle();        (void)SPI_DATA(SD_SPI);        // Check if this is part of callback streaming request        bool stream = false;        if (m_stream_callback && buf == m_stream_buffer + m_stream_count)        {            stream = true;        }        else if (m_stream_callback)        {            bluedbg("Stream buffer mismatch: ", (uint32_t)buf, " vs. ", (uint32_t)(m_stream_buffer + m_stream_count));        }        // Use DMA to stream dummy TX data and store RX data        uint8_t tx_data = 0xFF;        DMA_INTC(DMA0) = DMA_FLAG_ADD(DMA_FLAG_FTF | DMA_FLAG_ERR, SD_SPI_RX_DMA_CHANNEL);        DMA_INTC(DMA0) = DMA_FLAG_ADD(DMA_FLAG_FTF | DMA_FLAG_ERR, SD_SPI_TX_DMA_CHANNEL);        DMA_CHMADDR(DMA0, SD_SPI_RX_DMA_CHANNEL) = (uint32_t)buf;        DMA_CHMADDR(DMA0, SD_SPI_TX_DMA_CHANNEL) = (uint32_t)&tx_data;        DMA_CHCTL(DMA0, SD_SPI_TX_DMA_CHANNEL) &= ~DMA_CHXCTL_MNAGA; // No memory increment for TX        DMA_CHCNT(DMA0, SD_SPI_RX_DMA_CHANNEL) = count;        DMA_CHCNT(DMA0, SD_SPI_TX_DMA_CHANNEL) = count;        DMA_CHCTL(DMA0, SD_SPI_RX_DMA_CHANNEL) |= DMA_CHXCTL_CHEN;        DMA_CHCTL(DMA0, SD_SPI_TX_DMA_CHANNEL) |= DMA_CHXCTL_CHEN;        SPI_CTL1(SD_SPI) |= SPI_CTL1_DMAREN | SPI_CTL1_DMATEN;        uint32_t start = millis();        while (!(DMA_INTF(DMA0) & DMA_FLAG_ADD(DMA_FLAG_FTF | DMA_FLAG_ERR, SD_SPI_RX_DMA_CHANNEL)))        {            if (millis() - start > 500)            {                bluelog("ERROR: SPI DMA receive of ", (int)count, " bytes timeouted");                return 1;            }            if (stream)            {                uint32_t complete = m_stream_count + (count - DMA_CHCNT(DMA0, SD_SPI_RX_DMA_CHANNEL));                m_stream_callback(complete);            }        }        if (DMA_INTF(DMA0) & DMA_FLAG_ADD(DMA_FLAG_ERR, SD_SPI_RX_DMA_CHANNEL))        {            bluelog("ERROR: SPI DMA receive set DMA_FLAG_ERR");        }        SPI_CTL1(SD_SPI) &= ~(SPI_CTL1_DMAREN | SPI_CTL1_DMATEN);        DMA_CHCTL(DMA0, SD_SPI_RX_DMA_CHANNEL) &= ~DMA_CHXCTL_CHEN;        DMA_CHCTL(DMA0, SD_SPI_TX_DMA_CHANNEL) &= ~DMA_CHXCTL_CHEN;        if (stream)        {            m_stream_count += count;        }        return 0;    }    // Multiple byte send    void send(const uint8_t* buf, size_t count) {        // Check if this is part of callback streaming request        bool stream = false;        if (m_stream_callback && buf == m_stream_buffer + m_stream_count)        {            stream = true;        }        else if (m_stream_callback)        {            bluedbg("Stream buffer mismatch: ", (uint32_t)buf, " vs. ", (uint32_t)(m_stream_buffer + m_stream_count));        }        // Use DMA to stream TX data        DMA_INTC(DMA0) = DMA_FLAG_ADD(DMA_FLAG_FTF | DMA_FLAG_ERR, SD_SPI_TX_DMA_CHANNEL);        DMA_CHMADDR(DMA0, SD_SPI_TX_DMA_CHANNEL) = (uint32_t)buf;        DMA_CHCTL(DMA0, SD_SPI_TX_DMA_CHANNEL) |= DMA_CHXCTL_MNAGA; // Memory increment for TX        DMA_CHCNT(DMA0, SD_SPI_TX_DMA_CHANNEL) = count;        DMA_CHCTL(DMA0, SD_SPI_TX_DMA_CHANNEL) |= DMA_CHXCTL_CHEN;        SPI_CTL1(SD_SPI) |= SPI_CTL1_DMATEN;        uint32_t start = millis();        while (!(DMA_INTF(DMA0) & DMA_FLAG_ADD(DMA_FLAG_FTF | DMA_FLAG_ERR, SD_SPI_TX_DMA_CHANNEL)))        {            if (millis() - start > 500)            {                bluelog("ERROR: SPI DMA transmit of ", (int)count, " bytes timeouted");                return;            }            if (stream)            {                uint32_t complete = m_stream_count + (count - DMA_CHCNT(DMA0, SD_SPI_TX_DMA_CHANNEL));                m_stream_callback(complete);            }        }        if (DMA_INTF(DMA0) & DMA_FLAG_ADD(DMA_FLAG_ERR, SD_SPI_TX_DMA_CHANNEL))        {            bluelog("ERROR: SPI DMA transmit set DMA_FLAG_ERR");        }        wait_idle();        SPI_CTL1(SD_SPI) &= ~(SPI_CTL1_DMAREN | SPI_CTL1_DMATEN);        DMA_CHCTL(DMA0, SD_SPI_TX_DMA_CHANNEL) &= ~DMA_CHXCTL_CHEN;        if (stream)        {            m_stream_count += count;        }    }    void setSckSpeed(uint32_t maxSck) {        m_sckfreq = maxSck;    }    void set_sd_callback(sd_callback_t func, const uint8_t *buffer)    {        m_stream_buffer = buffer;        m_stream_count = 0;        m_stream_callback = func;    }private:    uint32_t m_sckfreq;    const uint8_t *m_stream_buffer;    uint32_t m_stream_count;    sd_callback_t m_stream_callback;};void sdCsInit(SdCsPin_t pin){}void sdCsWrite(SdCsPin_t pin, bool level){    if (level)        GPIO_BOP(SD_PORT) = SD_CS_PIN;    else        GPIO_BC(SD_PORT) = SD_CS_PIN;}GD32SPIDriver g_sd_spi_port;SdSpiConfig g_sd_spi_config(0, DEDICATED_SPI, SD_SCK_MHZ(30), &g_sd_spi_port);void bluescsiplatform_set_sd_callback(sd_callback_t func, const uint8_t *buffer){    g_sd_spi_port.set_sd_callback(func, buffer);}// Check if a DMA request for SD card read has completed.// This is used to optimize the timing of data transfers on SCSI bus.bool check_sd_read_done(){    return (DMA_CHCTL(DMA0, SD_SPI_RX_DMA_CHANNEL) & DMA_CHXCTL_CHEN)        && (DMA_INTF(DMA0) & DMA_FLAG_ADD(DMA_FLAG_FTF, SD_SPI_RX_DMA_CHANNEL));}#endif
 |