mirror of
https://github.com/openhwgroup/cvw.git
synced 2025-04-22 12:57:23 -04:00
Added sd_cmd and utility SPI functions.
This commit is contained in:
parent
c50df29e58
commit
ef1f55626c
4 changed files with 164 additions and 51 deletions
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue