-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
210 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
#ifndef __SPI_H__ | ||
#define __SPI_H__ | ||
#include <stdint.h> | ||
#include <stdbool.h> | ||
|
||
/** | ||
* Enable: enable/disable SPI | ||
* pol: Polarity | ||
* - true: inactive state of SCK is high | ||
* - false: inactive state of SCK is low | ||
* pha: Phase | ||
* - true: data shifted on leading edge, sampled on trailing edge | ||
* - false: data sampled on leading edge, shifted on trailing edge | ||
* end: Endianness | ||
* - true: LSB first | ||
* - false: MSB first | ||
* Baudrate: number of bits transferred per second | ||
* Post_cs_low_delay: number of cycles to wait after de-asserting CS | ||
* Pre_cs_high_delay: number of cycles to wait before asserting CS | ||
*/ | ||
|
||
typedef enum {CSMODE_AUTO=0b00, CSMODE_DISABLE=0b11} csmode_t; | ||
|
||
struct SPI_Config { | ||
uint32_t base_addr; | ||
bool enable; | ||
bool pol; | ||
bool pha; | ||
bool lsb_first; | ||
uint32_t baudrate; | ||
uint8_t device_num; | ||
csmode_t cs_mode; | ||
uint8_t post_cs_low_delay; | ||
uint8_t pre_cs_high_delay; | ||
bool loopback_enable; | ||
}; | ||
|
||
|
||
#define SPI_Config_defaults { \ | ||
.base_addr = SPI_ADDR, \ | ||
.enable = true, \ | ||
.pha = false, \ | ||
.pol = false, \ | ||
.lsb_first = false, \ | ||
.baudrate = 1000000, \ | ||
.device_num = 0, \ | ||
.cs_mode = CSMODE_AUTO, \ | ||
.post_cs_low_delay = 1, \ | ||
.pre_cs_high_delay = 1, \ | ||
.loopback_enable=false \ | ||
}; \ | ||
|
||
/** | ||
* @brief Initialize SPI | ||
*/ | ||
int spi_init(struct SPI_Config * cfg); | ||
|
||
/** | ||
* @brief Starts SPI transaction by deasserting CS pin | ||
* @param cfg config struct | ||
*/ | ||
void spi_select(struct SPI_Config * cfg); | ||
|
||
/** | ||
* @brief Finishes SPI transaction by re-asserting CS pin | ||
* @param cfg config struct | ||
*/ | ||
void spi_deselect(struct SPI_Config * cfg); | ||
|
||
/** | ||
* @brief Transfer a byte of data (Send as well as recieve) | ||
* @param cfg config struct | ||
* @param b byte to be transferred | ||
*/ | ||
char spi_transfer(struct SPI_Config * cfg, char b); | ||
|
||
/** | ||
* @brief Transfer multiple bytes of data (Send as well as recieve) | ||
* @param cfg config struct | ||
* @param send_buf char buffer to send | ||
* @param recv_buf char buffer to recieve data in | ||
* @param len lenth of send buffer | ||
* @return char | ||
*/ | ||
char *spi_transfer_buf(struct SPI_Config * cfg, char *send_buf, char *recv_buf, unsigned int len); | ||
|
||
#endif // __SPI_H__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
#include "platform.h" | ||
#include "mmio.h" | ||
#include "spi.h" | ||
|
||
#define SPI_REG_SCKDIV_OFFSET 0x00 | ||
#define SPI_REG_SCTRL_OFFSET 0x04 | ||
#define SPI_REG_TDATA_OFFSET 0x08 | ||
#define SPI_REG_RDATA_OFFSET 0x0c | ||
#define SPI_REG_CSCTRL_OFFSET 0x10 | ||
#define SPI_REG_DCTRL_OFFSET 0x14 | ||
|
||
#define _spi_busy() (REG32(SPI_ADDR, SPI_REG_SCTRL_OFFSET) >> 31) | ||
|
||
int spi_init(struct SPI_Config * cfg) { | ||
while(_spi_busy()) { | ||
asm volatile(""); | ||
} | ||
|
||
// set divisor reg | ||
uint32_t fratio = (CLK_FREQ/cfg->baudrate); | ||
REG32(cfg->base_addr, SPI_REG_SCKDIV_OFFSET) = fratio - 1; | ||
|
||
// set sctrl reg | ||
uint32_t spi_sctrl = 0; | ||
spi_sctrl = cfg->enable ? bitset(spi_sctrl, 0): spi_sctrl; | ||
spi_sctrl = cfg->pol ? bitset(spi_sctrl, 1): spi_sctrl; | ||
spi_sctrl = cfg->pha ? bitset(spi_sctrl, 2): spi_sctrl; | ||
spi_sctrl = cfg->lsb_first ? bitset(spi_sctrl, 3): spi_sctrl; | ||
spi_sctrl = cfg->loopback_enable ? bitset(spi_sctrl, 4): spi_sctrl; | ||
REG32(cfg->base_addr, SPI_REG_SCTRL_OFFSET) = spi_sctrl; | ||
|
||
uint32_t spi_csctrl = cfg->cs_mode; | ||
if(cfg->cs_mode == CSMODE_AUTO) { | ||
spi_csctrl = bitset(spi_csctrl, 24 + cfg->device_num); | ||
} | ||
else if (cfg->cs_mode == CSMODE_DISABLE) { | ||
// In this case device_num is used by select/deselect functions | ||
spi_csctrl &= 0x00ffffff; // clear active cs | ||
} else { | ||
return 1; // invalid mode | ||
} | ||
REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = spi_csctrl; | ||
|
||
// set dctrl reg | ||
uint32_t spi_dctrl=0; | ||
spi_dctrl |= ((uint32_t)cfg->pre_cs_high_delay) << 8; | ||
spi_dctrl |= ((uint32_t)cfg->post_cs_low_delay); | ||
REG32(cfg->base_addr, SPI_REG_DCTRL_OFFSET) = spi_dctrl; | ||
|
||
return 0; | ||
} | ||
|
||
char spi_transfer(struct SPI_Config * cfg, char b) { | ||
REG8(cfg->base_addr, SPI_REG_TDATA_OFFSET) = b; | ||
while(_spi_busy()) { | ||
asm volatile(""); | ||
} | ||
return REG8(cfg->base_addr, SPI_REG_RDATA_OFFSET); | ||
} | ||
|
||
void spi_select(struct SPI_Config * cfg) { | ||
REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = bitset(REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET), 24+cfg->device_num); | ||
} | ||
|
||
void spi_deselect(struct SPI_Config * cfg) { | ||
REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = bitclear(REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET), 24+cfg->device_num); | ||
} | ||
|
||
char *spi_transfer_buf(struct SPI_Config * cfg, char *send_buf, char *recv_buf, unsigned int len) | ||
{ | ||
// Disable hardware control | ||
uint32_t curr_csctrl = REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET); | ||
REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = curr_csctrl | 0b11; | ||
|
||
// Select | ||
REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = bitset(REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET), 24+cfg->device_num); | ||
|
||
// transfer | ||
while(len--) { | ||
REG8(cfg->base_addr, SPI_REG_TDATA_OFFSET) = *(send_buf++); | ||
while(_spi_busy()) | ||
asm volatile(""); | ||
*(recv_buf++) = REG8(cfg->base_addr, SPI_REG_RDATA_OFFSET); | ||
} | ||
|
||
// Deselect | ||
REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = bitclear(REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET), 24+cfg->device_num); | ||
|
||
// restore previous mode | ||
REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = curr_csctrl; | ||
return recv_buf; | ||
} | ||
|
||
char *spi_get_buf(struct SPI_Config * cfg, char send_val, char *recv_buf, unsigned int len) | ||
{ | ||
// Disable hardware control | ||
uint32_t curr_csctrl = REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET); | ||
REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = curr_csctrl | 0b11; | ||
|
||
// Select | ||
REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = bitset(REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET), 24+cfg->device_num); | ||
|
||
// transfer | ||
while(len--) { | ||
REG8(cfg->base_addr, SPI_REG_TDATA_OFFSET) = send_val; | ||
while(_spi_busy()) | ||
asm volatile(""); | ||
*(recv_buf++) = REG8(cfg->base_addr, SPI_REG_RDATA_OFFSET); | ||
} | ||
|
||
// Deselect | ||
REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = bitclear(REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET), 24+cfg->device_num); | ||
|
||
// restore previous mode | ||
REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = curr_csctrl; | ||
return recv_buf; | ||
} |