diff --git a/cores/beken-72xx/arduino/libraries/SPI/SPI.cpp b/cores/beken-72xx/arduino/libraries/SPI/SPI.cpp new file mode 100644 index 000000000..1a33d9d1d --- /dev/null +++ b/cores/beken-72xx/arduino/libraries/SPI/SPI.cpp @@ -0,0 +1,198 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2024-03-15. */ + +#include "SPIPrivate.h" + +#define SPI_PERI_CLK_26M (26 * 1000 * 1000) +#define SPI_PERI_CLK_DCO (80 * 1000 * 1000) + +bool SPIClass::beginPrivate() { + if (!this->data) + return false; + uint32_t param; + + REG_SPI0->ctrl = 0; + + intc_service_change_handler(IRQ_SPI, (FUNCPTR)SPIClass::isrHandlerStatic); + // bk_spi_configure() + REG_SPI0->BIT_WIDTH = 0; + REG_SPI0->MSTEN = true; + // disable hardware CS - issues with per-byte data transfer otherwise +#if LT_BK7231N + REG_SPI0->WIRE3_EN = true; +#else + REG_SPI0->NSSMD = 3; +#endif + // spi_init_msten(0) + REG_SPI0->TX_FIFO_INT_LEVEL = 0; + REG_SPI0->TX_FIFO_INT_LEVEL = 0; + REG_SPI0->TX_UDF_INT_EN = true; + REG_SPI0->RX_OVF_INT_EN = true; + // spi_active(1) + REG_SPI0->SPIEN = true; + // spi_icu_configuration(1) + param = PWD_SPI_CLK_BIT; + icu_ctrl(CMD_CLK_PWR_UP, ¶m); + param = IRQ_SPI_BIT; + icu_ctrl(CMD_ICU_INT_ENABLE, ¶m); + // spi_gpio_configuration() + param = GFUNC_MODE_SPI; + gpio_ctrl(CMD_GPIO_ENABLE_SECOND, ¶m); + + return true; +} + +bool SPIClass::endPrivate() { + if (!this->data) + return true; + uint32_t param; + + param = IRQ_SPI_BIT; + icu_ctrl(CMD_ICU_INT_DISABLE, ¶m); + param = PWD_SPI_CLK_BIT; + icu_ctrl(CMD_CLK_PWR_DOWN, ¶m); + + REG_SPI0->ctrl = 0; + + return true; +} + +void SPIClass::setFrequency(uint32_t frequency) { + if (!this->data) + return; + uint32_t param; + uint32_t maxFrequency = 30000000; + uint32_t sourceClk; + uint32_t clockDiv; + + REG_SPI0->SPIEN = false; + this->settings._clock = frequency; + + if (frequency == 26000000 || frequency == 13000000 || frequency == 6500000 || frequency <= 4333000) { +#if CFG_XTAL_FREQUENCE + sourceClk = CFG_XTAL_FREQUENCE; +#else + sourceClk = SPI_PERI_CLK_26M; +#endif + param = PCLK_POSI_SPI; + icu_ctrl(CMD_CONF_PCLK_26M, ¶m); + } else { + if (frequency > maxFrequency) { + LT_WM(SPI, "Clock freq too high! %lu > %lu", frequency, maxFrequency); + frequency = maxFrequency; + } + sourceClk = SPI_PERI_CLK_DCO; + param = PWD_SPI_CLK_BIT; + icu_ctrl(CMD_CLK_PWR_DOWN, ¶m); + param = PCLK_POSI_SPI; + icu_ctrl(CMD_CONF_PCLK_DCO, ¶m); + param = PWD_SPI_CLK_BIT; + icu_ctrl(CMD_CLK_PWR_UP, ¶m); + } + + if (frequency == 26000000 || frequency == 13000000 || frequency == 6500000) { + clockDiv = sourceClk / frequency - 1; + } else { + clockDiv = ((sourceClk >> 1) / frequency); + if (clockDiv < 2) + clockDiv = 2; + else if (clockDiv >= 255) + clockDiv = 255; + } + + REG_SPI0->SPI_CKR = clockDiv; + REG_SPI0->SPIEN = true; +} + +void SPIClass::setBitOrder(uint8_t bitOrder) { + if (!this->data) + return; + this->settings._bitOrder = bitOrder; +#if LT_BK7231N + REG_SPI0->LSB_FIRST = bitOrder == SPI_LSBFIRST; +#endif +} + +void SPIClass::setDataMode(uint8_t dataMode) { + if (!this->data) + return; + this->settings._dataMode = dataMode; + REG_SPI0->CKPOL = (dataMode >> 1) & 0b1; + REG_SPI0->CKPHA = (dataMode >> 0) & 0b1; +} + +void SPIClass::commitTransaction() { + if (this->txLen != 0) { + REG_SPI0->TX_FIFO_CLR = true; + REG_SPI0->TX_TRANS_LEN = this->txLen * 8; + REG_SPI0->TX_FIFO_INT_EN = false; + REG_SPI0->TX_EN = true; + while (this->txLen--) { + while (REG_SPI0->TX_FIFO_WR_READY == false) {} + uint32_t data = *this->txBuf++; + // LT_I("<- TX: %02x", data); + REG_SPI0->SPI_DAT = data; + } + REG_SPI0->TX_FIFO_INT_EN = false; + REG_SPI0->TX_EN = false; + } + if (this->rxLen != 0) { + // REG_SPI0->RX_FIFO_CLR = true; + // REG_SPI0->RX_TRANS_LEN = this->rxLen * 8; + // REG_SPI0->RX_FIFO_INT_EN = false; + // REG_SPI0->RX_FINISH_INT_EN = false; + // REG_SPI0->RX_EN = true; + // while (this->rxLen--) { + // while (REG_SPI0->RX_FIFO_RD_READY == false) {} + // uint32_t data = REG_SPI0->SPI_DAT; + // // LT_I("-> RX: %02x", data); + // *this->rxBuf++ = data; + // } + // REG_SPI0->RX_EN = false; + // REG_SPI0->TX_FIFO_INT_EN = false; + // REG_SPI0->RX_FIFO_INT_EN = false; + } +} + +void SPIClass::isrHandlerStatic(void *param) { + SPI.isrHandler(param); +} + +void SPIClass::isrHandler(void *param) { + if (REG_SPI0->RX_FIFO_INT) { + LT_I("RX_FIFO_INT, rxLen=%d", this->rxLen); + while (this->rxLen != 0 && REG_SPI0->RX_FIFO_RD_READY == true) { + uint8_t data = REG_SPI0->SPI_DAT; + LT_I("RX data in ISR #1 %02x", data); + *this->rxBuf++ = data; + this->rxLen--; + } + if (this->rxLen == 0) { + REG_SPI0->RX_EN = false; + REG_SPI0->TX_FIFO_INT_EN = false; + REG_SPI0->RX_FIFO_INT_EN = false; + } + REG_SPI0->RX_FIFO_INT = true; + } + if (REG_SPI0->RX_FINISH_INT) { + LT_I("RX_FINISH_INT, rxLen=%d", this->rxLen); + while (this->rxLen != 0 && REG_SPI0->RX_FIFO_RD_READY == true) { + uint8_t data = REG_SPI0->SPI_DAT; + LT_I("RX data in ISR #2 %02x", data); + *this->rxBuf++ = data; + this->rxLen--; + } + REG_SPI0->RX_EN = false; + REG_SPI0->TX_FIFO_INT_EN = false; + REG_SPI0->RX_FIFO_INT_EN = false; + REG_SPI0->RX_FINISH_INT = true; + } + + if (REG_SPI0->TX_UDF_INT) { + LT_W("TX underflow"); + REG_SPI0->TX_UDF_INT = true; + } + if (REG_SPI0->RX_OVF_INT) { + LT_W("RX overflow"); + REG_SPI0->RX_OVF_INT = true; + } +} diff --git a/cores/beken-72xx/arduino/libraries/SPI/SPIPrivate.h b/cores/beken-72xx/arduino/libraries/SPI/SPIPrivate.h new file mode 100644 index 000000000..96aa8c5ef --- /dev/null +++ b/cores/beken-72xx/arduino/libraries/SPI/SPIPrivate.h @@ -0,0 +1,143 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2024-03-15. */ + +#pragma once + +#include +#include + +struct SPIData {}; + +// Register structures based on: +// - https://github.com/bekencorp/armino/blob/main/middleware/soc/bk7231n/soc/spi_struct.h +// - https://wiki.bekencorp.com/pages/viewpage.action?pageId=23692278 +// - BDK beken378/driver/spi/*.h + +typedef volatile struct { + union { + struct { + uint32_t TX_FIFO_INT_LEVEL : 2; //!< TX FIFO interrupt generation condition (1/16/32/48) [RW] (0x0[0:1]) + uint32_t RX_FIFO_INT_LEVEL : 2; //!< RX FIFO interrupt generation condition (1/16/32/48) [RW] (0x0[2:3]) + uint32_t TX_UDF_INT_EN : 1; //!< TX FIFO underflow interrupt enable [RW] (0x0[4]) + uint32_t RX_OVF_INT_EN : 1; //!< RX FIFO overflow interrupt enable [RW] (0x0[5]) + uint32_t TX_FIFO_INT_EN : 1; //!< TX FIFO interrupt enable [RW] (0x0[6]) + uint32_t RX_FIFO_INT_EN : 1; //!< RX FIFO interrupt enable [RW] (0x0[7]) + uint32_t SPI_CKR : 8; //!< Prescaler factor [RW] (0x0[8:15]) +#if LT_BK7231N || LT_BK7271 + uint32_t SLV_RELEASE_INT_EN : 1; //!< Slave release interrupt enable (four-wire slave mode) [RW] (0x0[16]) + uint32_t WIRE3_EN : 1; //!< Three-wire mode (no CSN signal) [RW] (0x0[17]) +#else + uint32_t NSSMD : 2; //!< (0x0[16:17]) +#endif + uint32_t BIT_WIDTH : 1; //!< Data bit width (0: 8-bit, 1: 16-bit) [RW] (0x0[18]) +#if LT_BK7231N || LT_BK7271 + uint32_t LSB_FIRST : 1; //!< Data frame format (0: MSB-first, 1: LSB-first) [RW] (0x0[19]) +#else + uint32_t _reserved0 : 1; //!< (0x0[19]) +#endif + uint32_t CKPOL : 1; //!< SPI clock polarity [RW] (0x0[20]) + uint32_t CKPHA : 1; //!< SPI clock phase [RW] (0x0[21]) + uint32_t MSTEN : 1; //!< Master mode enable [RW] (0x0[22]) + uint32_t SPIEN : 1; //!< SPI enable [RW] (0x0[23]) +#if LT_BK7231N || LT_BK7271 + uint32_t BYTE_INTLVAL : 8; //!< SCK interval between data units (master) [RW] (0x0[24:31]) +#else + uint32_t _reserved1 : 8; //!< (0x0[24:31]) +#endif + }; + + uint32_t ctrl; + }; + +#if LT_BK7231N || LT_BK7271 + union { + struct { + uint32_t TX_EN : 1; //!< TX enable [RW] (0x1[0]) + uint32_t RX_EN : 1; //!< RX enable [RW] (0x1[1]) + uint32_t TX_FINISH_INT_EN : 1; //!< TX_TRANS_LEN completed interrupt enabled [RW] (0x1[2]) + uint32_t RX_FINISH_INT_EN : 1; //!< RX_TRANS_LEN completed interrupt enable [RW] (0x1[3]) + uint32_t _reserved2 : 4; //!< (0x1[4:7]) + uint32_t TX_TRANS_LEN : 12; //!< TX data length (bits), 0: unlimited [RW] (0x1[8:19]) + uint32_t RX_TRANS_LEN : 12; //!< RX data length (bits), 0: unlimited [RW] (0x1[20:31]) + }; + + uint32_t config; + }; +#endif + +#if LT_BK7231N || LT_BK7271 + union { + struct { + uint32_t _reserved3 : 1; //!< (0x2[0]) + uint32_t TX_FIFO_WR_READY : 1; //!< TX FIFO write allowed [R] (0x2[1]) + uint32_t RX_FIFO_RD_READY : 1; //!< RX FIFO read allowed [R] (0x2[2]) + uint32_t _reserved4 : 1; //!< (0x2[3]) + uint32_t _reserved5 : 4; //!< (0x2[4:7]) + uint32_t TX_FIFO_INT : 1; //!< TX FIFO interrupt (1: clear) [RW] (0x2[8]) + uint32_t RX_FIFO_INT : 1; //!< RX FIFO interrupt (1: clear) [RW] (0x2[9]) + uint32_t SLV_RELEASE_INT : 1; //!< Slave release interrupt (1: clear) [RW] (0x2[10]) + uint32_t TX_UDF_INT : 1; //!< TX FIFO underflow interrupt (1: clear) [RW] (0x2[11]) + uint32_t RX_OVF_INT : 1; //!< RX FIFO overflow interrupt (1: clear) [RW] (0x2[12]) + uint32_t TX_FINISH_INT : 1; //!< TX_TRANS_LEN completed interrupt [R] (0x2[13]) + uint32_t RX_FINISH_INT : 1; //!< RX_TRANS_LEN completed interrupt [R] (0x2[14]) + uint32_t _reserved6 : 1; //!< (0x2[15]) + uint32_t TX_FIFO_CLR : 1; //!< TX FIFO clear enable [W] (0x2[16]) + uint32_t RX_FIFO_CLR : 1; //!< RX FIFO clear enable [W] (0x2[17]) + uint32_t _reserved7 : 14; //!< (0x2[18:31]) + }; + + uint32_t status; + }; +#else + union { + struct { + uint32_t TX_FIFO_EMPTY : 1; //!< (0x1[0]) + uint32_t TX_FIFO_FULL : 1; //!< (0x1[1]) + uint32_t RX_FIFO_EMPTY : 1; //!< (0x1[2]) + uint32_t RX_FIFO_FULL : 1; //!< (0x1[3]) + uint32_t _reserved8 : 4; //!< (0x2[4:7]) + uint32_t TX_FIFO_INT : 1; //!< TX FIFO interrupt (1: clear) [RW] (0x2[8]) + uint32_t RX_FIFO_INT : 1; //!< RX FIFO interrupt (1: clear) [RW] (0x2[9]) + uint32_t MODF : 1; //!< (0x1[10]) + uint32_t TX_UDF_INT : 1; //!< TX FIFO underflow interrupt (1: clear) [RW] (0x2[11]) + uint32_t RX_OVF_INT : 1; //!< RX FIFO overflow interrupt (1: clear) [RW] (0x2[12]) + uint32_t _reserved9 : 1; //!< (0x1[13]) + uint32_t SLVSEL : 1; //!< (0x1[14]) + uint32_t SPIBUSY : 1; //!< (0x1[15]) + uint32_t _reserved10 : 16; //!< (0x1[16:31]) + }; + + uint32_t status; + }; +#endif + + union { + struct { + uint32_t SPI_DAT : 16; //!< FIFO read/write operation [RW] (0x2[0:15]) + uint32_t _reserved11 : 16; //!< (0x2[16:31]) + }; + + uint32_t data; + }; + +#if !(LT_BK7231N || LT_BK7271) + union { + struct { + uint32_t _reserved12 : 1; //!< (0x3[0]) + uint32_t S_CS_UP_INT_EN : 1; //!< (0x3[1]) + uint32_t _reserved13 : 2; //!< (0x3[2:3]) + uint32_t S_CS_UP_INT_STATUS : 1; //!< (0x3[4]) + uint32_t _reserved14 : 27; //!< (0x3[5:31]) + }; + + uint32_t slave_ctrl; + }; +#endif +} spi_hw_t; + +#if LT_BK7271 +#define REG_SPI0 ((spi_hw_t *)0x00802500) +#define REG_SPI1 ((spi_hw_t *)0x00802540) +#define REG_SPI2 ((spi_hw_t *)0x00802580) +#else +#define REG_SPI0 ((spi_hw_t *)0x00802700) +#endif diff --git a/cores/beken-72xx/arduino/src/lt_defs.h b/cores/beken-72xx/arduino/src/lt_defs.h index 9e88201f1..cd52f5fa2 100644 --- a/cores/beken-72xx/arduino/src/lt_defs.h +++ b/cores/beken-72xx/arduino/src/lt_defs.h @@ -4,5 +4,6 @@ #define LT_ARD_HAS_WIFI 1 #define LT_ARD_HAS_SERIAL 1 +#define LT_ARD_HAS_SPI 1 #define LT_ARD_HAS_WIRE 1 #define LT_ARD_MD5_HOSTAPD 1 diff --git a/cores/beken-72xx/base/sdk_private.h b/cores/beken-72xx/base/sdk_private.h index b9a9489d0..6cbec2e04 100644 --- a/cores/beken-72xx/base/sdk_private.h +++ b/cores/beken-72xx/base/sdk_private.h @@ -24,6 +24,7 @@ extern "C" { #include #include #include +#include #include #include #include