Added sd_cmd and utility SPI functions.

This commit is contained in:
Jacob Pease 2024-07-22 16:57:04 -05:00
parent c50df29e58
commit ef1f55626c
4 changed files with 164 additions and 51 deletions

View file

@ -12,6 +12,7 @@ uint8_t crc7(uint8_t prev, uint8_t in) {
return remainder & 0xff;
}
// Need to check this. This could be wrong as well.
uint16_t crc16(uint16_t crc, uint8_t data) {
// CRC polynomial 0x11021
crc = (uint8_t)(crc >> 8) | (crc << 8);
@ -22,13 +23,74 @@ uint16_t crc16(uint16_t crc, uint8_t data) {
return crc;
}
uint8_t sd_cmd(uint8_t cmd, uint32_t arg, uint8_t crc) {
spi_send_byte
uint64_t sd_cmd(uint8_t cmd, uint32_t arg, uint8_t crc) {
uint8_t response_len;
uint8_t i;
uint64_t r;
uint8_t rbyte;
switch (cmd) {
case 0:
response_len = 1;
break;
case 8:
response_len = 7
break;
default:
response_len = 1;
break;
}
// Make interrupt pending after response fifo receives the correct
// response length.
write_reg(SPI_RXMARK, response_len);
// Write all 6 bytes into transfer fifo
spi_sendbyte(0x40 | cmd);
spi_sendbyte(arg >> 24);
spi_sendbyte(arg >> 16);
spi_sendbyte(arg >> 8);
spi_sendbyte(arg);
spi_sendbyte(crc);
// Wait for command to send
// The Transfer IP bit should go high when the txFIFO is empty
// while(!(read_reg(SPI_IP) & 1)) {}
waittx();
// Read the dummy rxFIFO entries to move the head back to the tail
for (i = 0; i < 6; i++) {
spi_readbyte();
}
// Send "dummy signals". Since SPI is duplex,
// useless bytes must be transferred
for (i = 0; i < response_len; i++) {
spi_sendbyte(0xFF);
}
// Wait for transfer fifo again
waittx();
// Read rxfifo response
for (i = 0; i < response_len; i++) {
rbyte = spi_readbyte();
r = r | (rbyte << ((response_len - 1 - i)*8));
}
return r;
}
#define cmd0() sd_cmd( 0, 0x00000000, 0x95)
#define cmd8() sd_cmd( 8, 0x000001aa, 0x87)
// CMD55 has to be sent before ACMD41 (it means the next command is
// application specific)
#define cmd55() sd_cmd(55, 0x00000000, 0x65)
#defube acmd41() sd_cmd(41, 0x40000000, 0x77)
void init_sd(){
init_spi();
cmd0()
}

View file

@ -4,6 +4,5 @@
uint8_t crc7(uint8_t prev, uint8_t in);
uint16_t crc16(uint16_t crc, uint8_t data);
uint8_t sd_cmd(uint8_t cmd, uint32_t arg, uint8_t crc);
uint64_t sd_cmd(uint8_t cmd, uint32_t arg, uint8_t crc);
void init_sd();

View file

@ -1,50 +1,87 @@
///////////////////////////////////////////////////////////////////////
// spi.c
//
// Written: Jaocb Pease jacob.pease@okstate.edu 7/22/2024
//
// Purpose: SPI Controller API for bootloader
//
//
//
// A component of the Wally configurable RISC-V project.
//
// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University
//
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
//
// Licensed under the Solderpad Hardware License v 2.1 (the
// “License”); you may not use this file except in compliance with the
// License, or, at your option, the Apache License version 2.0. You
// may obtain a copy of the License at
//
// https://solderpad.org/licenses/SHL-2.1/
//
// Unless required by applicable law or agreed to in writing, any work
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
///////////////////////////////////////////////////////////////////////
#include "spi.h"
void write_reg(uintptr_t addr, uint32_t value) {
// Write to a register
inline void write_reg(uintptr_t addr, uint32_t value) {
volatile uint32_t * loc = (volatile uint32_t *) addr;
*loc = value;
}
void read_red(uintptr_t addr) {
// Read a register
inline void read_reg(uintptr_t addr) {
return *(volatile uint32_t *) addr;
}
// Queues a single byte in the transfer fifo
inline void spi_sendbyte(uint8_t byte) {
// Write byte to transfer fifo
write_reg(SPI_TXDATA, byte);
}
inline uint8_t spi_readbyte() {
return read_reg(SPI_RXDATA);
}
inline void waittx() {
while(!(read_reg(SPI_IP) & 1)) {}
}
inline void waitrx() {
while(read_reg(SPI_IP) & 2)) {}
}
// Initialize Sifive FU540 based SPI Controller
void spi_init() {
// Disable interrupts by default
// write_reg(SPI_IE, 0);
// Enable interrupts
write_reg(SPI_IE, 0x3);
// Set TXMARK to 1. If the number of entries is < 1
// IP's txwm field will go high.
// Set RXMARK to 0. If the number of entries is > 0
// IP's rwxm field will go high.
write_reg(SPI_TXMARK, 1);
write_reg(SPI_RXMARK, 0);
// Set Delay 0 to default
write_reg(SPI_DELAY0,
SIFIVE_SPI_DELAY0_CSSCK(1) |
SIFIVE_SPI_DELAY0_SCKCS(1));
// Set Delay 1 to default
write_reg(SPI_DELAY1,
SIFIVE_SPI_DELAY1_INTERCS(1) |
SIFIVE_SPI_DELAY1_INTERXFR(0));
// Initialize the SPI controller clock to
// div = (20MHz/(2*400kHz)) - 1 = 24 = 0x18
write_reg(SPI_SCKDIV, 0x18);
}
// Sends and receives a single byte
uint8_t spi_send_byte(uint8_t byte) {
// Write byte to transfer fifo
write_reg(SPI_TXDATA, byte);
/* Not sure how necessary this is. Will keep commented for now.
// Wait a decent amount of time for data to send
for (int i = 0; i < 100; i++) {
__asm__ volatile("nop");
}
*/
// Wait for data to come into receive fifo
while (read_reg(SPI_IP) != 2) {}
// Read received data
result = read_reg(SPI_RXDATA);
// Return result
return result;
}

View file

@ -4,26 +4,28 @@
#include <stdint.h>
#define SPI_BASE 0x13000 /* Base address of SPI device used for SDC */
/* register offsets */
#define SPI_SCKDIV 0x00 /* Serial clock divisor */
#define SPI_SCKMODE 0x04 /* Serial clock mode */
#define SPI_CSID 0x10 /* Chip select ID */
#define SPI_CSDEF 0x14 /* Chip select default */
#define SPI_CSMODE 0x18 /* Chip select mode */
#define SPI_DELAY0 0x28 /* Delay control 0 */
#define SPI_DELAY1 0x2c /* Delay control 1 */
#define SPI_FMT 0x40 /* Frame format */
#define SPI_TXDATA 0x48 /* Tx FIFO data */
#define SPI_RXDATA 0x4c /* Rx FIFO data */
#define SPI_TXMARK 0x50 /* Tx FIFO [<35;39;29Mwatermark */
#define SPI_RXMARK 0x54 /* Rx FIFO watermark */
#define SPI_SCKDIV SPI_BASE + 0x00 /* Serial clock divisor */
#define SPI_SCKMODE SPI_BASE + 0x04 /* Serial clock mode */
#define SPI_CSID SPI_BASE + 0x10 /* Chip select ID */
#define SPI_CSDEF SPI_BASE + 0x14 /* Chip select default */
#define SPI_CSMODE SPI_BASE + 0x18 /* Chip select mode */
#define SPI_DELAY0 SPI_BASE + 0x28 /* Delay control 0 */
#define SPI_DELAY1 SPI_BASE + 0x2c /* Delay control 1 */
#define SPI_FMT SPI_BASE + 0x40 /* Frame format */
#define SPI_TXDATA SPI_BASE + 0x48 /* Tx FIFO data */
#define SPI_RXDATA SPI_BASE + 0x4c /* Rx FIFO data */
#define SPI_TXMARK SPI_BASE + 0x50 /* Tx FIFO [<35;39;29Mwatermark */
#define SPI_RXMARK SPI_BASE + 0x54 /* Rx FIFO watermark */
/* Non-implemented
#define SPI_FCTRL 0x60 // SPI flash interface control
#define SPI_FFMT 0x64 // SPI flash instruction format
#define SPI_FCTRL SPI_BASE + 0x60 // SPI flash interface control
#define SPI_FFMT SPI_BASE + 0x64 // SPI flash instruction format
*/
#define SPI_IE 0x70 /* Interrupt Enable Register */
#define SPI_IP 0x74 /* Interrupt Pendings Register */
#define SPI_IE SPI_BASE + 0x70 /* Interrupt Enable Register */
#define SPI_IP SPI_BASE + 0x74 /* Interrupt Pendings Register */
/* delay0 bits */
#define SIFIVE_SPI_DELAY0_CSSCK(x) ((u32)(x))
@ -37,9 +39,22 @@
#define SIFIVE_SPI_DELAY1_INTERXFR(x) ((u32)(x) << 16)
#define SIFIVE_SPI_DELAY1_INTERXFR_MASK (0xffU << 16)
void write_reg(uintptr_t addr, uint32_t value);
uint32_t read_reg(uintptr_t addr);
uint8_t spi_send_byte(uint8_t byte);
/* csmode bits */
#define SIFIVE_SPI_CSMODE_MODE_AUTO 0U
#define SIFIVE_SPI_CSMODE_MODE_HOLD 2U
#define SIFIVE_SPI_CSMODE_MODE_OFF 3U
#define WAITTX while(!(read_reg(SPI_IP) & 1) {}
#define WAITRX while(read_reg(SPI_IP) & 2) {}
inline void write_reg(uintptr_t addr, uint32_t value);
inline uint32_t read_reg(uintptr_t addr);
inline void spi_sendbyte(uint8_t byte);
inline void waittx();
inline void waitrx();
uint8_t spi_txrx(uint8_t byte);
inline uint8_t spi_readbyte();
void spi_init();