mirror of
				https://github.com/openhwgroup/cvw
				synced 2025-02-11 06:05:49 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			256 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			256 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| ///////////////////////////////////////////////////////////////////////
 | |
| // sd.c
 | |
| //
 | |
| // Written: Jaocb Pease jacob.pease@okstate.edu 7/22/2024
 | |
| //
 | |
| // Purpose: SD Card protocol functions
 | |
| //
 | |
| // 
 | |
| //
 | |
| // 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 "sd.h"
 | |
| #include "spi.h"
 | |
| #include "uart.h"
 | |
| #include "fail.h"
 | |
| #include "time.h"
 | |
| 
 | |
| // Parallel byte update CRC7-CCITT algorithm.
 | |
| // The result is the CRC7 result, left shifted over by 1
 | |
| // which is perfect, since we append a 1 at the end anyway
 | |
| uint8_t crc7(uint8_t prev, uint8_t in) {
 | |
|     // CRC polynomial 0x89
 | |
|     uint8_t remainder = prev ^ in;
 | |
|     remainder ^= (remainder >> 4) ^ (remainder >> 7);
 | |
|     remainder = (remainder << 1) ^ (remainder << 4);
 | |
|     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);
 | |
|     crc ^= data;
 | |
|     crc ^= (uint8_t)(crc >> 4) & 0xf;
 | |
|     crc ^= crc << 12;
 | |
|     crc ^= (crc & 0xff) << 5;
 | |
|     return crc;
 | |
| }
 | |
| 
 | |
| // sd_cmd ------------------------------------------------------------
 | |
| // Sends SD card command using SPI mode.
 | |
| // This function:
 | |
| // * Chooses the response length based on the input command
 | |
| // * Makes use of SPI's full duplex. For every byte sent,
 | |
| //   a byte is received. Thus for every byte sent as part of
 | |
| //   a command, a useless byte must be read from the receive
 | |
| //   FIFO.
 | |
| // * Takes advantage of the Sifive SPI peripheral spec's
 | |
| //   watermark and interrupt features to determine when a
 | |
| //   transfer is complete. This should save on cycles since
 | |
| //   no arbitrary delays need to be added.
 | |
| 
 | |
| uint64_t sd_cmd(uint8_t cmd, uint32_t arg, uint8_t crc) {
 | |
|   uint8_t response_len;
 | |
|   uint8_t i;
 | |
|   uint8_t shiftAmnt;
 | |
|   uint64_t r;
 | |
|   uint8_t rbyte;
 | |
| 
 | |
|   // Initialize the response with 0's.
 | |
|   r = 0;
 | |
| 
 | |
|   // Choose response length based on cmd input.
 | |
|   // Most commands return an R1 format response.
 | |
|   switch (cmd) {
 | |
|     case 8:
 | |
|       response_len = R7_RESPONSE;
 | |
|       break;
 | |
|     case 12:
 | |
|       response_len = R1B_RESPONSE;
 | |
|       break;
 | |
|     default:
 | |
|       response_len = R1_RESPONSE;
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   // Make interrupt pending after response fifo receives the correct
 | |
|   // response length.  Probably unecessary so let's wait and see what
 | |
|   // happens.
 | |
|   // write_reg(SPI_RXMARK, response_len);
 | |
| 
 | |
|   // Chip select must remain asserted during transaction
 | |
|   if (cmd != SD_CMD_STOP_TRANSMISSION) {
 | |
|     write_reg(SPI_CSMODE, SIFIVE_SPI_CSMODE_MODE_HOLD);
 | |
|   }
 | |
|   
 | |
|   // Write all 7 bytes into transfer fifo
 | |
|   // spi_sendbyte(0xff);
 | |
|   spi_dummy();
 | |
|   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 < 7; 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(); */
 | |
| 
 | |
|   // Wait for actual response from SD card
 | |
|   // All responses start with a 0. Output of SDCIn is high, unless
 | |
|   // a message is being transferred.
 | |
|   do {
 | |
|     rbyte = spi_dummy();
 | |
|   } while ( (rbyte & 0x80) != 0 );
 | |
| 
 | |
|   // Note about the compiler. In order to compile as sll instead of
 | |
|   // sllw, the number to shift has to be a 64 bit number.
 | |
|   r = ((uint64_t)rbyte) << ((response_len - 1)*8);
 | |
|   
 | |
|   // Read rxfifo response
 | |
|   for (i = 1; i < response_len; i++) {
 | |
|     rbyte = spi_dummy();
 | |
|     r = r | (((uint64_t)rbyte) << ((response_len - 1 - i)*8));
 | |
|   }
 | |
| 
 | |
|   if (cmd != 18) {
 | |
|     write_reg(SPI_CSMODE, SIFIVE_SPI_CSMODE_MODE_AUTO);
 | |
|   } else {
 | |
|     spi_dummy();
 | |
|   }
 | |
|   return r;
 | |
| } // sd_cmd
 | |
| 
 | |
| uint64_t sd_read64(uint16_t * crc) {
 | |
|   uint64_t r;
 | |
|   uint8_t rbyte;
 | |
|   int i;
 | |
| 
 | |
|   /* for (i = 0; i < 8; i++) { */
 | |
|   /*   spi_sendbyte(0xFF); */
 | |
|   /* } */
 | |
| 
 | |
|   /* waittx(); */
 | |
| 
 | |
|   for (i = 0; i < 8; i++) {
 | |
|     rbyte = spi_dummy();
 | |
|     *crc = crc16(*crc, rbyte);
 | |
|     r = r | ((uint64_t)(rbyte) << ((8 - 1 - i)*8));
 | |
|   }
 | |
| 
 | |
|   return r;
 | |
| }
 | |
| 
 | |
| // Utility defines for CMD0, CMD8, CMD55, and ACMD41
 | |
| #define CMD0()   sd_cmd( 0, 0x00000000, 0x95) // Reset SD card into IDLE state
 | |
| #define CMD8()   sd_cmd( 8, 0x000001aa, 0x87) // 
 | |
| #define CMD55()  sd_cmd(55, 0x00000000, 0x65) //
 | |
| #define ACMD41() sd_cmd(41, 0x40000000, 0x77) //
 | |
| 
 | |
| // init_sd: ----------------------------------------------------------
 | |
| // This first initializes the SPI peripheral then initializes the SD
 | |
| // card itself. We use the uart to display anything that goes wrong.
 | |
| int init_sd(uint32_t freq, uint32_t sdclk){
 | |
|   print_time();
 | |
|   println("Initializing SPI Controller.");
 | |
|   spi_init();
 | |
| 
 | |
|   uint64_t r;
 | |
|   uint32_t newClockDiv;
 | |
|   int n;
 | |
| 
 | |
|   print_time();
 | |
|   println("Initializing SD Card in SPI mode.");
 | |
|   // This is necessary. This is the card's pre-init state initialization.
 | |
|   write_reg(SPI_CSMODE, SIFIVE_SPI_CSMODE_MODE_OFF);
 | |
|   for (int i = 0; i < 10; i++) {
 | |
|     spi_txrx(0xff);
 | |
|   }
 | |
|   write_reg(SPI_CSMODE, SIFIVE_SPI_CSMODE_MODE_AUTO);
 | |
|   
 | |
|   // CMD0 --------------------------------------------------------------
 | |
|   // Reset SD Card command
 | |
|   // Initializes SD card into SPI mode if CS is asserted '0'
 | |
|   // We expect to get the R1 response 0x01 which means that the
 | |
|   // card has been put into the idle state.
 | |
|   print_time();
 | |
|   print_uart("CMD0: ");
 | |
|   n = 0;
 | |
|   do {
 | |
|     r = CMD0();
 | |
|     n++;
 | |
|     if (n == 1000) {
 | |
|       fail();
 | |
|     }
 | |
|   } while ( r != 0x01 );
 | |
|   println_with_r1("Success, r = 0x", r & 0xff);
 | |
| 
 | |
|   // CMD8 -------------------------------------------------------------
 | |
|   //
 | |
|   print_time();
 | |
|   print_uart("CMD8: ");
 | |
|   r = CMD8();
 | |
|   if ((r & 0x000000ff0000ffff) != 0x01000001aa) {
 | |
|     println_with_r7("Failed, 0x", r);
 | |
|     fail();
 | |
|   }
 | |
|   println_with_r7("Success, 0x", r);
 | |
| 
 | |
|   // ACMD41 -----------------------------------------------------------
 | |
|   print_time();
 | |
|   print_uart("ACMD41: ");
 | |
|   n = 0;
 | |
|   do {
 | |
|     CMD55();
 | |
|     r = ACMD41();
 | |
|     n++;
 | |
|     if (n == 1000) {
 | |
|       fail();
 | |
|     }
 | |
|   } while (r == 0x1);
 | |
|   println_with_r1("Success, r = 0x", r & 0xff);
 | |
| 
 | |
|   print_time();
 | |
|   println_with_dec("New clock frequency: ", (uint64_t)sdclk);
 | |
|   spi_set_clock(freq, sdclk);
 | |
|   
 | |
|   print_time();
 | |
|   println("SD card is initialized.");
 | |
| }
 | |
| 
 |