Skip to content

Commit

Permalink
Add SPI driver
Browse files Browse the repository at this point in the history
  • Loading branch information
saursin committed Sep 10, 2023
1 parent eaa00c8 commit 288fb89
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 1 deletion.
7 changes: 6 additions & 1 deletion sw/lib/include/platform.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#ifndef __PLATFORM_H__
#define __PLATFORM_H__
#include "stddef.h"

// ============== Target Independent Definitions ==============

Expand All @@ -14,6 +13,8 @@

// ============== Target Specific Definitions ==============
#ifdef TARGET_HYDROGENSOC
// #define EXCEPTION

#define MEM_ROM_BEGIN 0x00010000
#define MEM_ROM_SIZE 32*1024 // 32 KB

Expand All @@ -29,6 +30,10 @@
#define GPIO_SETWIDTH 16
#define GPIO_ADDR 0x40002000

// ---------- SPI ------------
#define SPI
#define SPI_ADDR 0x40003000

// ------ CLOCK FREQUENCY --------
#define CLK_FREQ 12000000

Expand Down
87 changes: 87 additions & 0 deletions sw/lib/include/spi.h
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__
117 changes: 117 additions & 0 deletions sw/lib/libcatom/hydrogensoc/spi.c
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;
}

0 comments on commit 288fb89

Please sign in to comment.