Skip to content

Commit

Permalink
Merge pull request #171 from sifive/add_qspi
Browse files Browse the repository at this point in the history
Add QSPI mode and update spi driver.
  • Loading branch information
nategraff-sifive authored Sep 3, 2019
2 parents 4512b7d + 1468e0f commit 7707e18
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 3 deletions.
12 changes: 12 additions & 0 deletions metal/spi.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ struct metal_spi_config {
unsigned int cs_active_high : 1;
/*! @brief The chip select ID to activate for the SPI transfer */
unsigned int csid;
/*! @brief The spi command frame number (cycles = num * frame_len) */
unsigned int cmd_num;
/*! @brief The spi address frame number */
unsigned int addr_num;
/*! @brief The spi dummy frame number */
unsigned int dummy_num;
/*! @brief The Dual/Quad spi mode selection.*/
enum {
MULTI_WIRE_ALL,
MULTI_WIRE_DATA_ONLY,
MULTI_WIRE_ADDR_DATA
} multi_wire;
};

struct metal_spi_vtable {
Expand Down
125 changes: 122 additions & 3 deletions src/drivers/sifive_spi0.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,16 @@ static int configure_spi(struct __metal_driver_sifive_spi0 *spi,
METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_SINGLE;
break;
case METAL_SPI_DUAL:
METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_DUAL;
if (config->multi_wire == MULTI_WIRE_ALL)
METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_DUAL;
else
METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_SINGLE;
break;
case METAL_SPI_QUAD:
METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_QUAD;
if (config->multi_wire == MULTI_WIRE_ALL)
METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_QUAD;
else
METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_SINGLE;
break;
default:
/* Unsupported value */
Expand Down Expand Up @@ -131,13 +137,36 @@ static int configure_spi(struct __metal_driver_sifive_spi0 *spi,
return 0;
}

static void spi_mode_switch(struct __metal_driver_sifive_spi0 *spi,
struct metal_spi_config *config,
unsigned int trans_stage) {
long control_base =
__metal_driver_sifive_spi0_control_base((struct metal_spi *)spi);

if (config->multi_wire == trans_stage) {
METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) &= ~(METAL_SPI_PROTO_MASK);
switch (config->protocol) {
case METAL_SPI_DUAL:
METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_DUAL;
break;
case METAL_SPI_QUAD:
METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_QUAD;
break;
default:
/* Unsupported value */
return;
}
}
}

int __metal_driver_sifive_spi0_transfer(struct metal_spi *gspi,
struct metal_spi_config *config,
size_t len, char *tx_buf,
char *rx_buf) {
struct __metal_driver_sifive_spi0 *spi = (void *)gspi;
long control_base = __metal_driver_sifive_spi0_control_base(gspi);
int rc = 0;
size_t i = 0;

rc = configure_spi(spi, config);
if (rc != 0) {
Expand All @@ -153,7 +182,97 @@ int __metal_driver_sifive_spi0_transfer(struct metal_spi *gspi,
/* Declare time_t variables to break out of infinite while loop */
time_t endwait;

for (size_t i = 0; i < len; i++) {
for (i = 0; i < config->cmd_num; i++) {

while (METAL_SPI_REGW(METAL_SIFIVE_SPI0_TXDATA) & METAL_SPI_TXDATA_FULL)
;

if (tx_buf) {
METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = tx_buf[i];
} else {
METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = 0;
}

endwait = metal_time() + METAL_SPI_RXDATA_TIMEOUT;

while ((rxdata = METAL_SPI_REGW(METAL_SIFIVE_SPI0_RXDATA)) &
METAL_SPI_RXDATA_EMPTY) {
if (metal_time() > endwait) {
METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSMODE) &=
~(METAL_SPI_CSMODE_MASK);

return 1;
}
}

if (rx_buf) {
rx_buf[i] = (char)(rxdata & METAL_SPI_TXRXDATA_MASK);
}
}

/* switch to Dual/Quad mode */
spi_mode_switch(spi, config, MULTI_WIRE_ADDR_DATA);

/* Send Addr data */
for (; i < (config->cmd_num + config->addr_num); i++) {

while (METAL_SPI_REGW(METAL_SIFIVE_SPI0_TXDATA) & METAL_SPI_TXDATA_FULL)
;

if (tx_buf) {
METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = tx_buf[i];
} else {
METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = 0;
}

endwait = metal_time() + METAL_SPI_RXDATA_TIMEOUT;

while ((rxdata = METAL_SPI_REGW(METAL_SIFIVE_SPI0_RXDATA)) &
METAL_SPI_RXDATA_EMPTY) {
if (metal_time() > endwait) {
METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSMODE) &=
~(METAL_SPI_CSMODE_MASK);

return 1;
}
}

if (rx_buf) {
rx_buf[i] = (char)(rxdata & METAL_SPI_TXRXDATA_MASK);
}
}

/* Send Dummy data */
for (; i < (config->cmd_num + config->addr_num + config->dummy_num); i++) {

while (METAL_SPI_REGW(METAL_SIFIVE_SPI0_TXDATA) & METAL_SPI_TXDATA_FULL)
;

if (tx_buf) {
METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = tx_buf[i];
} else {
METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = 0;
}

endwait = metal_time() + METAL_SPI_RXDATA_TIMEOUT;

while ((rxdata = METAL_SPI_REGW(METAL_SIFIVE_SPI0_RXDATA)) &
METAL_SPI_RXDATA_EMPTY) {
if (metal_time() > endwait) {
METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSMODE) &=
~(METAL_SPI_CSMODE_MASK);
return 1;
}
}
if (rx_buf) {
rx_buf[i] = (char)(rxdata & METAL_SPI_TXRXDATA_MASK);
}
}

/* switch to Dual/Quad mode */
spi_mode_switch(spi, config, MULTI_WIRE_DATA_ONLY);

for (; i < len; i++) {
/* Master send bytes to the slave */

/* Wait for TXFIFO to not be full */
Expand Down

0 comments on commit 7707e18

Please sign in to comment.