This commit is contained in:
Polprzewodnikowy 2021-02-06 18:16:00 +01:00
parent d8da4c7f1f
commit fb17b9c540
12 changed files with 769 additions and 484 deletions

View File

@ -19,7 +19,7 @@ fi
# Build bootloader # Build bootloader
echo "Building bootloader" echo "Building bootloader"
pushd sw/bootloader pushd sw/bootloader
docker run -t --mount type=bind,src="$(pwd)",target="/libdragon" anacierdem/libdragon:4.1.1 /bin/bash -c "/usr/bin/make clean; /usr/bin/make all N64_BYTE_SWAP=false" ./build.sh
popd popd

View File

@ -14,15 +14,195 @@ module sd_dat (
input i_dat_start, input i_dat_start,
input i_dat_stop, input i_dat_stop,
output o_dat_busy, output o_dat_busy,
output o_dat_crc_error, output reg o_dat_crc_error,
output reg o_rx_fifo_push, output reg o_rx_fifo_push,
input i_rx_fifo_overrun, input i_rx_fifo_overrun,
output [31:0] o_rx_fifo_data, output reg [31:0] o_rx_fifo_data,
input i_tx_fifo_full, input i_tx_fifo_full,
output reg o_tx_fifo_pop, output reg o_tx_fifo_pop,
input [31:0] i_tx_fifo_data input [31:0] i_tx_fifo_data
); );
// Module state
localparam STATE_IDLE = 0;
localparam STATE_READ_WAIT = 1;
localparam STATE_RECEIVING = 2;
reg [2:0] r_state;
// Bit counter logic
reg [12:0] r_bit_counter;
reg r_bit_done;
wire w_start_bit = !io_sd_dat[0] && i_sd_clk_strobe_rising && r_state[STATE_READ_WAIT];
wire w_data_end = r_bit_done && r_state[STATE_RECEIVING];
assign o_dat_busy = !r_state[STATE_IDLE];
always @(posedge i_clk) begin
if (w_start_bit) begin
r_bit_counter <= (i_dat_width ? (
{2'b00, {1'b0, i_dat_block_size} + 1'd1, 3'b000}
) : (
{{1'b0, i_dat_block_size} + 1'd1, 5'b00000}
)) + 13'd16;
r_bit_done <= 1'b0;
end else if (i_sd_clk_strobe_rising) begin
if (r_bit_counter > 13'd0) begin
r_bit_counter <= r_bit_counter - 1'd1;
end else begin
r_bit_done <= 1'b1;
end
end
end
// Block counter logic
reg [10:0] r_block_counter;
wire w_read_start = i_dat_start && r_state[STATE_IDLE];
wire w_read_stop = r_block_counter == 11'd0;
always @(posedge i_clk) begin
if (w_read_start) begin
r_block_counter <= i_dat_num_blocks;
end else if (w_data_end) begin
if (r_block_counter > 11'd0) begin
r_block_counter <= r_block_counter - 1'd1;
end
end
end
// CRC16 generator
reg [15:0] r_crc_16_received [0:3];
wire w_crc_shift_reset = !r_state[STATE_RECEIVING];
wire w_crc_shift_enabled = r_bit_counter > 13'd16;
wire w_crc_shift = w_crc_shift_enabled && i_sd_clk_strobe_rising;
wire [15:0] w_crc_16_calculated [0:3];
wire w_crc_error = (r_bit_counter == 13'd0) && (i_dat_width ? (
(w_crc_16_calculated[0] != r_crc_16_received[0]) &&
(w_crc_16_calculated[1] != r_crc_16_received[1]) &&
(w_crc_16_calculated[2] != r_crc_16_received[2]) &&
(w_crc_16_calculated[3] != r_crc_16_received[3])
) : (
w_crc_16_calculated[0] != r_crc_16_received[0]
));
genvar dat_index;
generate
for (dat_index = 0; dat_index < 4; dat_index = dat_index + 1) begin : crc_16_loop
sd_crc_16 sd_crc_16_inst (
.i_clk(i_clk),
.i_crc_reset(w_crc_shift_reset),
.i_crc_shift(w_crc_shift),
.i_crc_input(io_sd_dat[dat_index]),
.o_crc_output(w_crc_16_calculated[dat_index])
);
end
endgenerate
// Control signals
always @(posedge i_clk) begin
if (i_reset) begin
o_dat_crc_error <= 1'b0;
end else begin
if (w_data_end) begin
o_dat_crc_error <= w_crc_error;
end
end
end
// State machine
always @(posedge i_clk) begin
if (i_reset) begin
r_state <= (1'b1 << STATE_IDLE);
end else begin
r_state <= 3'b000;
if (i_dat_stop) begin
r_state[STATE_IDLE] <= 1'b1;
end else begin
unique case (1'b1)
r_state[STATE_IDLE]: begin
if (i_dat_start) begin
if (i_dat_direction) begin
r_state[STATE_IDLE] <= 1'b1; // TODO: Sending
end else begin
r_state[STATE_READ_WAIT] <= 1'b1;
end
end else begin
r_state[STATE_IDLE] <= 1'b1;
end
end
r_state[STATE_READ_WAIT]: begin
if (w_start_bit) begin
r_state[STATE_RECEIVING] <= 1'b1;
end else begin
r_state[STATE_READ_WAIT] <= 1'b1;
end
end
r_state[STATE_RECEIVING]: begin
if (w_crc_error || i_rx_fifo_overrun) begin
r_state[STATE_IDLE] <= 1'b1;
end else if (w_data_end) begin
if (w_read_stop) begin
r_state[STATE_IDLE] <= 1'b1;
end else begin
r_state[STATE_READ_WAIT] <= 1'b1;
end
end else begin
r_state[STATE_RECEIVING] <= 1'b1;
end
end
endcase
end
end
end
// Shifting operation
wire [31:0] w_shift_1_bit = {o_rx_fifo_data[30:0], io_sd_dat[0]};
wire [31:0] w_shift_4_bit = {o_rx_fifo_data[27:0], io_sd_dat};
always @(posedge i_clk) begin
o_rx_fifo_push <= 1'b0;
if (i_sd_clk_strobe_rising && r_state[STATE_RECEIVING]) begin
if (r_bit_counter > 13'd16) begin
if (i_dat_width) begin
o_rx_fifo_data <= w_shift_4_bit;
if (r_bit_counter[2:0] == 3'd1) begin
o_rx_fifo_push <= 1'b1;
end
end else begin
o_rx_fifo_data <= w_shift_1_bit;
if (r_bit_counter[4:0] == 5'd17) begin
o_rx_fifo_push <= 1'b1;
end
end
end else if (r_bit_counter > 13'd0) begin
for (integer i = 0; i < 4; i = i + 1) begin
r_crc_16_received[i] <= {r_crc_16_received[i][14:0], io_sd_dat[i]};
end
end
end
end
endmodule endmodule

View File

@ -5,7 +5,7 @@ module sd_dma (
input [3:0] i_dma_bank, input [3:0] i_dma_bank,
input [23:0] i_dma_address, input [23:0] i_dma_address,
input [14:0] i_dma_length, input [14:0] i_dma_length,
output reg [14:0] o_dma_left, output [14:0] o_dma_left,
input i_dma_load_bank_address, input i_dma_load_bank_address,
input i_dma_load_length, input i_dma_load_length,
input i_dma_direction, input i_dma_direction,
@ -13,22 +13,81 @@ module sd_dma (
input i_dma_stop, input i_dma_stop,
output reg o_dma_busy, output reg o_dma_busy,
output reg o_rx_fifo_pop, output o_rx_fifo_pop,
input i_rx_fifo_empty, input i_rx_fifo_empty,
input [31:0] i_rx_fifo_data, input [31:0] i_rx_fifo_data,
output reg o_tx_fifo_push, output reg o_tx_fifo_push,
input i_tx_fifo_full, input i_tx_fifo_full,
output reg [31:0] o_tx_fifo_data, output [31:0] o_tx_fifo_data,
output reg o_request, output o_request,
output reg o_write, output reg o_write,
input i_busy, input i_busy,
input i_ack, input i_ack,
output reg [3:0] o_bank, output reg [3:0] o_bank,
output reg [23:0] o_address, output reg [23:0] o_address,
input [31:0] i_data, input [31:0] i_data,
output reg [31:0] o_data output [31:0] o_data
); );
wire w_request_successful = o_request && !i_busy;
always @(posedge i_clk) begin
if (i_dma_load_bank_address && !o_dma_busy) begin
o_bank <= i_dma_bank;
end
end
always @(posedge i_clk) begin
if (i_dma_load_bank_address && !o_dma_busy) begin
o_address <= i_dma_address;
end else if (w_request_successful) begin
o_address <= o_address + 1'd1;
end
end
reg [14:0] r_remaining;
assign o_dma_left = r_remaining;
always @(posedge i_clk) begin
if (i_dma_load_length && !o_dma_busy) begin
r_remaining <= i_dma_length;
end else if (w_request_successful && r_remaining > 15'd0) begin
r_remaining <= r_remaining - 1'd1;
end
end
always @(posedge i_clk) begin
if (i_reset) begin
o_dma_busy <= 1'b0;
end else begin
if (i_dma_start && !o_dma_busy) begin
o_dma_busy <= 1'b1;
end
if (i_dma_stop || (w_request_successful && r_remaining == 15'd0)) begin
o_dma_busy <= 1'b0;
end
end
end
assign o_rx_fifo_pop = o_dma_busy && o_write && w_request_successful;
assign o_tx_fifo_data = i_data;
assign o_request = o_dma_busy && (o_write ? (
!i_rx_fifo_empty
) : (
1'b0 // TODO: Reading
));
always @(posedge i_clk) begin
if (i_dma_start) begin
o_write <= i_dma_direction;
end
end
assign o_data = i_rx_fifo_data;
endmodule endmodule

View File

@ -98,9 +98,9 @@ module sd_interface (
.i_fifo_push(w_rx_fifo_push), .i_fifo_push(w_rx_fifo_push),
.i_fifo_pop(w_rx_fifo_regs_pop || w_rx_fifo_dma_pop), .i_fifo_pop(w_rx_fifo_regs_pop || w_rx_fifo_dma_pop),
.o_fifo_empty(w_rx_fifo_empty), .o_fifo_empty(w_rx_fifo_empty),
// .o_fifo_full(), .o_fifo_full(),
.o_fifo_items(w_rx_fifo_items), .o_fifo_items(w_rx_fifo_items),
// .o_fifo_underrun(), .o_fifo_underrun(),
.o_fifo_overrun(w_rx_fifo_overrun), .o_fifo_overrun(w_rx_fifo_overrun),
.i_fifo_data(w_rx_fifo_i_data), .i_fifo_data(w_rx_fifo_i_data),
.o_fifo_data(w_rx_fifo_o_data) .o_fifo_data(w_rx_fifo_o_data)
@ -136,9 +136,9 @@ module sd_interface (
.i_fifo_pop(w_tx_fifo_pop), .i_fifo_pop(w_tx_fifo_pop),
.o_fifo_empty(w_tx_fifo_empty), .o_fifo_empty(w_tx_fifo_empty),
.o_fifo_full(w_tx_fifo_full), .o_fifo_full(w_tx_fifo_full),
// .o_fifo_items(), .o_fifo_items(),
// .o_fifo_underrun(), .o_fifo_underrun(),
// .o_fifo_overrun(), .o_fifo_overrun(),
.i_fifo_data(r_tx_fifo_i_data), .i_fifo_data(r_tx_fifo_i_data),
.o_fifo_data(w_tx_fifo_o_data) .o_fifo_data(w_tx_fifo_o_data)
); );

View File

@ -65,7 +65,7 @@ module sd_regs (
localparam [2:0] SD_REG_DAT = 3'd4; localparam [2:0] SD_REG_DAT = 3'd4;
localparam [2:0] SD_REG_DMA_SCR = 3'd5; localparam [2:0] SD_REG_DMA_SCR = 3'd5;
localparam [2:0] SD_REG_DMA_ADDR = 3'd6; localparam [2:0] SD_REG_DMA_ADDR = 3'd6;
localparam [2:0] SD_REG_DMA_LENGTH = 3'd7; localparam [2:0] SD_REG_DMA_LEN = 3'd7;
wire w_write_request = i_request && i_write && !o_busy; wire w_write_request = i_request && i_write && !o_busy;
wire w_read_request = i_request && !i_write && !o_busy; wire w_read_request = i_request && !i_write && !o_busy;
@ -75,7 +75,7 @@ module sd_regs (
o_dma_address = i_data[25:2]; o_dma_address = i_data[25:2];
o_dma_length = i_data[14:0]; o_dma_length = i_data[14:0];
o_dma_load_bank_address = w_write_request && !i_address[3] && (i_address[2:0] == SD_REG_DMA_ADDR); o_dma_load_bank_address = w_write_request && !i_address[3] && (i_address[2:0] == SD_REG_DMA_ADDR);
o_dma_load_length = w_write_request && !i_address[3] && (i_address[2:0] == SD_REG_DMA_LENGTH); o_dma_load_length = w_write_request && !i_address[3] && (i_address[2:0] == SD_REG_DMA_LEN);
o_busy = 1'b0; o_busy = 1'b0;
end end
@ -142,7 +142,7 @@ module sd_regs (
SD_REG_DMA_ADDR: begin SD_REG_DMA_ADDR: begin
end end
SD_REG_DMA_LENGTH: begin SD_REG_DMA_LEN: begin
end end
endcase endcase
end else begin end else begin
@ -164,7 +164,7 @@ module sd_regs (
if (!i_address[3]) begin if (!i_address[3]) begin
case (i_address[2:0]) case (i_address[2:0])
SD_REG_SCR: begin SD_REG_SCR: begin
o_data <= {30'd0, o_sd_clk_config}; o_data <= {29'd0, o_dat_width, o_sd_clk_config};
end end
SD_REG_ARG: begin SD_REG_ARG: begin
@ -176,7 +176,8 @@ module sd_regs (
21'd0, 21'd0,
i_command_response_crc_error, i_command_response_crc_error,
i_command_timeout, i_command_timeout,
2'b00, o_command_skip_response,
o_command_long_response,
i_command_busy, i_command_busy,
i_command_index i_command_index
}; };
@ -208,7 +209,7 @@ module sd_regs (
o_data <= {i_dma_bank, 2'd0, i_dma_address, 2'b00}; o_data <= {i_dma_bank, 2'd0, i_dma_address, 2'b00};
end end
SD_REG_DMA_LENGTH: begin SD_REG_DMA_LEN: begin
o_data <= {17'd0, i_dma_left}; o_data <= {17'd0, i_dma_left};
end end
endcase endcase

3
sw/bootloader/build.sh Normal file
View File

@ -0,0 +1,3 @@
#!/bin/bash
docker run -t --mount type=bind,src="$(pwd)",target="/libdragon" anacierdem/libdragon:4.1.1 /bin/bash -c "/usr/bin/make clean; /usr/bin/make all N64_BYTE_SWAP=false"

View File

@ -29,8 +29,7 @@ DSTATUS disk_initialize(BYTE pdrv) {
} }
DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) { DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
uint8_t success; sc64_sd_err_t error;
uint8_t response;
if ((pdrv > 0) || (count == 0)) { if ((pdrv > 0) || (count == 0)) {
return RES_PARERR; return RES_PARERR;
@ -40,41 +39,11 @@ DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
return RES_NOTRDY; return RES_NOTRDY;
} }
if (count == 1) { error = sc64_sd_read_sectors(sector, count, buff);
success = sc64_sd_cmd_send(CMD17, sector, &response);
if (!success || response) { if (error != E_OK) {
return RES_PARERR;
}
success = sc64_sd_block_read(buff, SD_BLOCK_SIZE, FALSE);
if (!success) {
return RES_ERROR; return RES_ERROR;
} }
} else {
success = sc64_sd_cmd_send(CMD18, sector, &response);
if (!success || response) {
return RES_PARERR;
}
for (size_t i = 0; i < count; i++) {
success = sc64_sd_block_read(buff, SD_BLOCK_SIZE, FALSE);
if (!success) {
return RES_ERROR;
}
buff += SD_BLOCK_SIZE;
}
success = sc64_sd_cmd_send(CMD12, 0, &response);
if (!success || response) {
return RES_ERROR;
}
}
return RES_OK; return RES_OK;
} }

View File

@ -2,27 +2,27 @@
static void sc64_enable_peripheral(uint32_t mask) { static void sc64_enable_peripheral(uint32_t mask) {
uint32_t config = platform_pi_io_read(&SC64_CART->scr); uint32_t config = platform_pi_io_read(&SC64_CART->SCR);
config |= mask; config |= mask;
platform_pi_io_write(&SC64_CART->scr, config); platform_pi_io_write(&SC64_CART->SCR, config);
} }
static void sc64_disable_peripheral(uint32_t mask) { static void sc64_disable_peripheral(uint32_t mask) {
uint32_t config = platform_pi_io_read(&SC64_CART->scr); uint32_t config = platform_pi_io_read(&SC64_CART->SCR);
config &= ~mask; config &= ~mask;
platform_pi_io_write(&SC64_CART->scr, config); platform_pi_io_write(&SC64_CART->SCR, config);
} }
uint32_t sc64_get_scr(void) { uint32_t sc64_get_scr(void) {
return platform_pi_io_read(&SC64_CART->scr); return platform_pi_io_read(&SC64_CART->SCR);
} }
void sc64_set_scr(uint32_t scr) { void sc64_set_scr(uint32_t scr) {
platform_pi_io_write(&SC64_CART->scr, scr); platform_pi_io_write(&SC64_CART->SCR, scr);
} }
void sc64_enable_flashram(void) { void sc64_enable_flashram(void) {
@ -100,29 +100,29 @@ void sc64_disable_sdram_writable(void) {
} }
uint32_t sc64_get_boot_mode(void) { uint32_t sc64_get_boot_mode(void) {
return platform_pi_io_read(&SC64_CART->boot); return platform_pi_io_read(&SC64_CART->BOOT);
} }
void sc64_set_boot_mode(uint32_t boot) { void sc64_set_boot_mode(uint32_t boot) {
platform_pi_io_write(&SC64_CART->boot, boot); platform_pi_io_write(&SC64_CART->BOOT, boot);
} }
uint32_t sc64_get_version(void) { uint32_t sc64_get_version(void) {
return platform_pi_io_read(&SC64_CART->version); return platform_pi_io_read(&SC64_CART->VERSION);
} }
uint32_t sc64_get_ddipl_address(void) { uint32_t sc64_get_ddipl_address(void) {
return platform_pi_io_read(&SC64_CART->ddipl_addr) & SC64_CART_DDIPL_ADDR_ADDRESS_MASK; return platform_pi_io_read(&SC64_CART->DDIPL_ADDR) & SC64_CART_DDIPL_ADDR_ADDRESS_MASK;
} }
void sc64_set_ddipl_address(uint32_t address) { void sc64_set_ddipl_address(uint32_t address) {
platform_pi_io_write(&SC64_CART->ddipl_addr, address & SC64_CART_DDIPL_ADDR_ADDRESS_MASK); platform_pi_io_write(&SC64_CART->DDIPL_ADDR, address & SC64_CART_DDIPL_ADDR_ADDRESS_MASK);
} }
uint32_t sc64_get_sram_address(void) { uint32_t sc64_get_sram_address(void) {
return platform_pi_io_read(&SC64_CART->sram_addr) & SC64_CART_SRAM_ADDR_ADDRESS_MASK; return platform_pi_io_read(&SC64_CART->SRAM_ADDR) & SC64_CART_SRAM_ADDR_ADDRESS_MASK;
} }
void sc64_set_sram_address(uint32_t address) { void sc64_set_sram_address(uint32_t address) {
platform_pi_io_write(&SC64_CART->sram_addr, address & SC64_CART_SRAM_ADDR_ADDRESS_MASK); platform_pi_io_write(&SC64_CART->SRAM_ADDR, address & SC64_CART_SRAM_ADDR_ADDRESS_MASK);
} }

View File

@ -18,17 +18,17 @@
// Cart Interface Registers // Cart Interface Registers
typedef struct sc64_cart_registers { typedef struct sc64_cart_registers {
__IO reg_t scr; // Cart status and config __IO reg_t SCR; // Cart status and config
__IO reg_t boot; // Boot behavior override __IO reg_t BOOT; // Boot behavior override
__IO reg_t version; // Cart firmware version __IO reg_t VERSION; // Cart firmware version
__IO reg_t gpio; // Fixed I/O control __IO reg_t GPIO; // Fixed I/O control
__IO reg_t usb_scr; // USB interface status and control __IO reg_t USB_SCR; // USB interface status and control
__IO reg_t usb_dma_addr; // USB bank and address for DMA to PC __IO reg_t USB_DMA_ADDR; // USB bank and address for DMA to PC
__IO reg_t usb_dma_len; // USB transfer length for DMA to PC __IO reg_t USB_DMA_LEN; // USB transfer length for DMA to PC
__IO reg_t ddipl_addr; // 64 Disk Drive IPL location in SDRAM __IO reg_t DDIPL_ADDR; // 64 Disk Drive IPL location in SDRAM
__IO reg_t sram_addr; // SRAM save emulation location in SDRAM __IO reg_t SRAM_ADDR; // SRAM save emulation location in SDRAM
__IO reg_t __reserved[1015]; __IO reg_t __reserved[1015];
__IO reg_t usb_fifo[1024]; // USB data from PC read FIFO memory end __IO reg_t USB_FIFO[1024]; // USB data from PC read FIFO memory end
} sc64_cart_registers_t; } sc64_cart_registers_t;
#define SC64_CART_BASE (0x1E000000) #define SC64_CART_BASE (0x1E000000)
@ -96,68 +96,77 @@ typedef struct sc64_cart_registers {
// EEPROM Registers // EEPROM Registers
typedef struct sc64_eeprom_registers { typedef struct sc64_eeprom_registers {
__IO reg_t mem[512]; // EEPROM memory __IO reg_t MEM[512]; // EEPROM memory
} sc64_eeprom_registers_t; } sc64_eeprom_registers_t;
#define SC64_EEPROM_BASE (0x1E004000) #define SC64_EEPROM_BASE (0x1E004000)
#define SC64_EEPROM ((__IO sc64_sd_registers_t *) SC64_EEPROM_BASE) #define SC64_EEPROM ((__IO sc64_eeprom_registers_t *) SC64_EEPROM_BASE)
// SD Card Interface Registers // SD Card Interface Registers
typedef struct sc64_sd_registers { typedef struct sc64_sd_registers_s {
__IO reg_t scr; // Clock prescaler and busy flag __IO reg_t SCR; // Clock control and bus width selection
__IO reg_t cs; // Chip select pin control __IO reg_t ARG; // SD command argument
__IO reg_t dr; // Data to be sent and return value __IO reg_t CMD; // SD command index and flags
__IO reg_t multi; // Multi byte transmission __IO reg_t RSP; // SD command response
__IO reg_t dma_scr; // DMA configuration __IO reg_t DAT; // SD data path control
__IO reg_t dma_addr; // DMA starting address __IO reg_t DMA_SCR; // DMA status and configuration
__IO reg_t __reserved[122]; __IO reg_t DMA_ADDR; // DMA current address
__IO reg_t buffer[128]; // Multi byte transmission buffer __IO reg_t DMA_LEN; // DMA remaining length
__IO reg_t __unused[120];
__IO reg_t FIFO[128]; // SD data path FIFO buffer
} sc64_sd_registers_t; } sc64_sd_registers_t;
#define SC64_SD_BASE (0x1E008000) #define SC64_SD_BASE (0x1E008000)
#define SC64_SD ((__IO sc64_sd_registers_t *) SC64_SD_BASE) #define SC64_SD ((__IO sc64_sd_registers_t *) SC64_SD_BASE)
#define SC64_SC_SCR_PRESCALER_BIT (1) #define SC64_SD_SCR_DAT_WIDTH (1 << 2)
#define SC64_SD_SCR_PRESCALER_MASK (0x7 << SC64_SC_SCR_PRESCALER_BIT) #define SC64_SD_SCR_CLK_MASK (0x3 << 0)
#define SC64_SD_SCR_PRESCALER_2 (0 << SC64_SC_SCR_PRESCALER_BIT) #define SC64_SD_SCR_CLK_STOP (0 << 0)
#define SC64_SD_SCR_PRESCALER_4 (1 << SC64_SC_SCR_PRESCALER_BIT) #define SC64_SD_SCR_CLK_400_KHZ (1 << 0)
#define SC64_SD_SCR_PRESCALER_8 (2 << SC64_SC_SCR_PRESCALER_BIT) #define SC64_SD_SCR_CLK_25_MHZ (2 << 0)
#define SC64_SD_SCR_PRESCALER_16 (3 << SC64_SC_SCR_PRESCALER_BIT) #define SC64_SD_SCR_CLK_50_MHZ (3 << 0)
#define SC64_SD_SCR_PRESCALER_32 (4 << SC64_SC_SCR_PRESCALER_BIT)
#define SC64_SD_SCR_PRESCALER_64 (5 << SC64_SC_SCR_PRESCALER_BIT)
#define SC64_SD_SCR_PRESCALER_128 (6 << SC64_SC_SCR_PRESCALER_BIT)
#define SC64_SD_SCR_PRESCALER_256 (7 << SC64_SC_SCR_PRESCALER_BIT)
#define SC64_SD_SCR_BUSY (1 << 0)
#define SC64_SD_CS_PIN (1 << 0) #define SC64_SD_CMD_RESPONSE_CRC_ERROR (1 << 10)
#define SC64_SD_CMD_TIMEOUT (1 << 9)
#define SC64_SD_CMD_SKIP_RESPONSE (1 << 8)
#define SC64_SD_CMD_LONG_RESPONSE (1 << 7)
#define SC64_SD_CMD_BUSY (1 << 6)
#define SC64_SD_CMD_START (1 << 6)
#define SC64_SD_CMD_INDEX_GET(cmd) ((cmd) & 0x3F)
#define SC64_SD_CMD_INDEX(i) ((i) & 0x3F)
#define SC64_SD_DR_BUSY (1 << 8) #define SC64_SD_DAT_RX_FIFO_FULL (1 << 31)
#define SC64_SD_DR_DATA_BIT (0) #define SC64_SD_DAT_RX_FIFO_ITEMS_GET(dat) (((dat) >> 24) & 0xFF)
#define SC64_SD_DR_DATA_MASK (0xFF << SC64_SD_DR_DATA_BIT) #define SC64_SD_DAT_TX_FIFO_FULL (1 << 23)
#define SC64_SD_DAT_TX_FIFO_EMPTY (1 << 22)
#define SC64_SD_DAT_TX_FIFO_FLUSH (1 << 22)
#define SC64_SD_DAT_RX_FIFO_OVERRUN (1 << 21)
#define SC64_SD_DAT_RX_FIFO_FLUSH (1 << 21)
#define SC64_SD_DAT_NUM_BLOCKS_GET(dat) ((((dat) >> 10) & 0x7FF) + 1)
#define SC64_SD_DAT_NUM_BLOCKS(nb) ((((nb) - 1) & 0x7FF) << 10)
#define SC64_SD_DAT_BLOCK_SIZE_GET(dat) (((((dat) >> 3) & 0x7F) + 1) * 4)
#define SC64_SD_DAT_BLOCK_SIZE(bs) (((((bs) / 4) - 1) & 0x7F) << 3)
#define SC64_SD_DAT_DIRECTION (1 << 2)
#define SC64_SD_DAT_CRC_ERROR (1 << 1)
#define SC64_SD_DAT_STOP (1 << 1)
#define SC64_SD_DAT_BUSY (1 << 0)
#define SC64_SD_DAT_START (1 << 0)
#define SC64_SD_DR_DATA(d) (((d) & SC64_SD_DR_DATA_MASK) >> SC64_SD_DR_DATA_BIT) #define SC64_SD_DMA_SCR_DIRECTION (1 << 2)
#define SC64_SD_DMA_SCR_STOP (1 << 1)
#define SC64_SD_DMA_SCR_BUSY (1 << 0)
#define SC64_SD_DMA_SCR_START (1 << 0)
#define SC64_SD_MULTI_DMA (1 << 10) #define SC64_SD_DMA_ADDR_GET(addr) ((addr) & 0x3FFFFFC)
#define SC64_SD_MULTI_RX_ONLY (1 << 9) #define SC64_SD_DMA_BANK_GET(addr) (((addr) >> 28) & 0xF)
#define SC64_SD_MULTI_LENGTH_BIT (0) #define SC64_SD_DMA_BANK_ADDR(b, a) ((((b) & 0xF) << 28) | ((a) & 0x3FFFFFC))
#define SC64_SD_MULTI_LENGTH_MASK (0x1FF << SC64_SD_MULTI_LENGTH_BIT)
#define SC64_SD_MULTI(d, r, l) ((d ? (SC64_SD_MULTI_DMA) : 0) | ((r) ? (SC64_SD_MULTI_RX_ONLY) : 0) | ((((l) - 1) & SC64_SD_MULTI_LENGTH_MASK) << SC64_SD_MULTI_LENGTH_BIT)) #define SC64_SD_DMA_LEN_GET(len) (((len) & 0x7FFF) * 4)
#define SC64_SD_DMA_LEN(l) ((((l) / 4) - 1) & 0x7FFF)
#define SC64_SD_DMA_SCR_FLUSH (1 << 0)
#define SC64_SD_DMA_SCR_FIFO_FULL (1 << 1)
#define SC64_SD_DMA_SCR_FIFO_EMPTY (1 << 0)
#define SC64_SD_DMA_ADDR_BANK_BIT (28)
#define SC64_SD_DMA_ADDR_BANK_MASK (0xF << SC64_SD_DMA_ADDR_BANK_BIT)
#define SC64_SD_DMA_ADDR_ADDRESS_BIT (0)
#define SC64_SD_DMA_ADDR_ADDRESS_MASK (0x3FFFFFC << SC64_SD_DMA_ADDR_ADDRESS_BIT)
#define SC64_SD_DMA_ADDR(b, a) ((((b) << SC64_SD_DMA_ADDR_BANK_BIT) & SC64_SD_DMA_ADDR_BANK_MASK) | (((a) << SC64_SD_DMA_ADDR_ADDRESS_BIT) & SC64_SD_DMA_ADDR_ADDRESS_MASK))
#endif #endif

View File

@ -1,331 +1,458 @@
#include <string.h>
#include "sc64.h" #include "sc64.h"
#include "sc64_sd.h" #include "sc64_sd.h"
static const size_t SC64_SD_BUFFER_SIZE = sizeof(SC64_SD->buffer); #define CMD8_ARG_SUPPLY_VOLTAGE_27_36_V (1 << 8)
#define CMD8_ARG_CHECK_PATTERN_AA (0xAA << 0)
static uint8_t sd_spi_dma_buffer[sizeof(SC64_SD->buffer)] __attribute__((aligned(16))); #define ACMD41_ARG_HCS (1 << 30)
static uint8_t sd_card_initialized = FALSE;
static uint8_t sd_card_type_block = FALSE; #define R3_CCS (1 << 30)
#define R3_BUSY (1 << 31)
#define R7_SUPPLY_VOLTAGE_27_36_V (1 << 8)
#define R7_CHECK_PATTERN_AA (0xAA << 0)
#define SD_BLOCK_SIZE (512)
static void sc64_sd_spi_set_prescaler(uint8_t prescaler) { typedef enum sc64_sd_clock_e {
uint32_t scr = platform_pi_io_read(&SC64_SD->scr); CLOCK_STOP,
CLOCK_400_KHZ,
CLOCK_25_MHZ,
CLOCK_50_MHZ,
} sc64_sd_clock_t;
scr = (scr & ~SC64_SD_SCR_PRESCALER_MASK) | (prescaler & SC64_SD_SCR_PRESCALER_MASK); typedef enum sc64_sd_dat_width_e {
DAT_WIDTH_1BIT,
DAT_WIDTH_4BIT,
} sc64_sd_dat_width_t;
platform_pi_io_write(&SC64_SD->scr, scr); typedef enum sc64_sd_cmd_flags_e {
} NO_FLAGS = 0,
ACMD = (1 << 0),
SKIP_RESPONSE = (1 << 1),
LONG_RESPONSE = (1 << 2),
IGNORE_CRC = (1 << 3),
IGNORE_INDEX = (1 << 4),
} sc64_sd_cmd_flags_t;
static void sc64_sd_spi_set_chip_select(uint8_t select) { typedef enum sc64_sd_dat_direction_e {
platform_pi_io_write(&SC64_SD->cs, (select == TRUE) ? 0 : SC64_SD_CS_PIN); DAT_DIR_RX,
} DAT_DIR_TX,
} sc64_sd_dat_direction_t;
static uint8_t sc64_sd_spi_exchange(uint8_t data) {
uint32_t dr;
platform_pi_io_write(&SC64_SD->dr, (uint32_t) (data & SC64_SD_DR_DATA_MASK)); static bool sd_card_initialized = false;
static bool sd_card_type_block = false;
static bool sd_card_selected = false;
static uint32_t rca = 0;
do {
dr = platform_pi_io_read(&SC64_SD->dr);
} while (dr & SC64_SD_DR_BUSY);
return (uint8_t) SC64_SD_DR_DATA(dr); static void sc64_sd_set_clock(sc64_sd_clock_t clock) {
} uint32_t scr = platform_pi_io_read(&SC64_SD->SCR);
static uint8_t sc64_sd_spi_read(void) { scr &= ~SC64_SD_SCR_CLK_MASK;
return sc64_sd_spi_exchange(0xFF);
}
static void sc64_sd_spi_release(void) { switch (clock) {
sc64_sd_spi_set_chip_select(FALSE); case CLOCK_400_KHZ:
sc64_sd_spi_read(); scr |= SC64_SD_SCR_CLK_400_KHZ;
} break;
case CLOCK_25_MHZ:
static void sc64_sd_spi_multi_write(uint8_t *data, size_t length) { scr |= SC64_SD_SCR_CLK_25_MHZ;
size_t remaining = length; break;
size_t offset = 0; case CLOCK_50_MHZ:
scr |= SC64_SD_SCR_CLK_50_MHZ;
do { break;
size_t block_size = remaining < SC64_SD_BUFFER_SIZE ? remaining : SC64_SD_BUFFER_SIZE; default:
uint32_t dma_transfer_length = block_size + ((block_size % 4) ? (4 - (block_size % 4)) : 0);
memcpy(sd_spi_dma_buffer, data + offset, block_size);
platform_cache_writeback(sd_spi_dma_buffer, dma_transfer_length);
platform_pi_dma_write(sd_spi_dma_buffer, &SC64_SD->buffer, dma_transfer_length);
platform_pi_io_write(&SC64_SD->multi, SC64_SD_MULTI(FALSE, FALSE, block_size));
while (platform_pi_io_read(&SC64_SD->scr) & SC64_SD_SCR_BUSY);
remaining -= block_size;
offset += block_size;
} while (remaining > 0);
}
static void sc64_sd_spi_multi_read(uint8_t *data, size_t length, uint8_t is_buffer_aligned) {
size_t remaining = length;
size_t offset = 0;
do {
size_t block_size = remaining < SC64_SD_BUFFER_SIZE ? remaining : SC64_SD_BUFFER_SIZE;
uint32_t dma_transfer_length = block_size + ((block_size % 4) ? (4 - (block_size % 4)) : 0);
uint8_t *buffer_pointer = is_buffer_aligned ? (data + offset) : sd_spi_dma_buffer;
platform_pi_io_write(&SC64_SD->multi, SC64_SD_MULTI(FALSE, TRUE, block_size));
while (platform_pi_io_read(&SC64_SD->scr) & SC64_SD_SCR_BUSY);
platform_pi_dma_read(buffer_pointer, &SC64_SD->buffer, dma_transfer_length);
platform_cache_invalidate(buffer_pointer, dma_transfer_length);
if (!is_buffer_aligned) {
memcpy(data + offset, sd_spi_dma_buffer, block_size);
}
remaining -= block_size;
offset += block_size;
} while (remaining > 0);
}
static void sc64_sd_spi_multi_read_dma(size_t length) {
size_t remaining = length;
do {
size_t block_size = remaining < SC64_SD_BUFFER_SIZE ? remaining : SC64_SD_BUFFER_SIZE;
platform_pi_io_write(&SC64_SD->multi, SC64_SD_MULTI(TRUE, TRUE, block_size));
while (platform_pi_io_read(&SC64_SD->scr) & SC64_SD_SCR_BUSY);
remaining -= block_size;
} while (remaining > 0);
}
static uint8_t sc64_sd_wait_ready(void) {
uint8_t response;
for (size_t attempts = 10000; attempts > 0; --attempts) {
response = sc64_sd_spi_read();
if (response == SD_NOT_BUSY_RESPONSE) {
break; break;
} }
platform_pi_io_write(&SC64_SD->SCR, scr);
} }
return response; static void sc64_sd_set_dat_width(sc64_sd_dat_width_t dat_width) {
uint32_t scr = platform_pi_io_read(&SC64_SD->SCR);
scr &= ~SC64_SD_SCR_DAT_WIDTH;
if (dat_width == DAT_WIDTH_4BIT) {
scr |= SC64_SD_SCR_DAT_WIDTH;
} }
uint8_t sc64_sd_init(void) { platform_pi_io_write(&SC64_SD->SCR, scr);
uint32_t arg; }
uint8_t success;
uint8_t response[5];
uint8_t *short_response = &response[0];
uint32_t *extended_response = (uint32_t *) &response[1];
uint8_t sd_version_2_or_later;
uint8_t status_data[64] __attribute__((aligned(16)));
if (!sd_card_initialized) { static void sc64_sd_hw_init(void) {
sc64_enable_sd(); sc64_enable_sd();
platform_pi_io_write(&SC64_SD->dma_scr, SC64_SD_DMA_SCR_FLUSH); platform_pi_io_write(&SC64_SD->DMA_SCR, SC64_SD_DMA_SCR_STOP);
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_TX_FIFO_FLUSH | SC64_SD_DAT_RX_FIFO_FLUSH | SC64_SD_DAT_STOP);
platform_pi_io_write(&SC64_SD->SCR, 0);
sc64_sd_spi_set_prescaler(SC64_SD_SCR_PRESCALER_256); sc64_sd_set_clock(CLOCK_400_KHZ);
sc64_sd_spi_release(); sc64_sd_set_dat_width(DAT_WIDTH_1BIT);
sc64_sd_spi_set_chip_select(TRUE);
for (uint8_t dummy_clocks = 10; dummy_clocks > 0; --dummy_clocks) {
sc64_sd_spi_read();
} }
do { static void sc64_sd_hw_deinit(void) {
success = sc64_sd_cmd_send(CMD0, 0, response); if (sc64_get_scr() & SC64_CART_SCR_SD_ENABLE) {
if (!(success && *short_response == R1_IN_IDLE_STATE)) break; while (platform_pi_io_read(&SC64_SD->CMD) & SC64_SD_CMD_BUSY);
platform_pi_io_write(&SC64_SD->DMA_SCR, SC64_SD_DMA_SCR_STOP);
arg = CMD8_ARG_SUPPLY_VOLTAGE_27_36_V | CMD8_ARG_CHECK_PATTERN_AA; platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_TX_FIFO_FLUSH | SC64_SD_DAT_RX_FIFO_FLUSH | SC64_SD_DAT_STOP);
success = sc64_sd_cmd_send(CMD8, CMD8_ARG_SUPPLY_VOLTAGE_27_36_V | CMD8_ARG_CHECK_PATTERN_AA, response); platform_pi_io_write(&SC64_SD->SCR, 0);
if (!success) {
break;
} else if (*short_response & R1_ILLEGAL_COMMAND) {
sd_version_2_or_later = FALSE;
} else if (
((*extended_response & R7_VOLTAGE_ACCEPTED_MASK) == R7_SUPPLY_VOLTAGE_27_36_V) &&
((*extended_response & R7_CHECK_PATTERN_MASK) == R7_CHECK_PATTERN_AA)
) {
sd_version_2_or_later = TRUE;
} else {
break;
} }
success = sc64_sd_cmd_send(CMD58, 0, response);
if (!success || (!(*extended_response & R3_27_36_V_MASK))) break;
arg = sd_version_2_or_later ? ACMD41_ARG_HCS : 0;
for (size_t attempts = 0; attempts < 10000; attempts++) {
success = sc64_sd_cmd_send(ACMD41, arg, response);
if (!success || (!(*short_response & R1_IN_IDLE_STATE))) {
break;
}
}
if (!success) break;
success = sc64_sd_cmd_send(CMD58, 0, response);
if (!success) break;
sd_card_type_block = (*extended_response & R3_CCS) ? TRUE : FALSE;
sc64_sd_spi_set_prescaler(SC64_SD_SCR_PRESCALER_4);
// Set high speed mode
success = sc64_sd_cmd_send(CMD6, 0x00000001, response);
if (!success) break;
success = sc64_sd_block_read(status_data, 64, FALSE);
if (!success) break;
if (status_data[13] & 0x02) {
success = sc64_sd_cmd_send(CMD6, 0x80000001, response);
if (!success) break;
success = sc64_sd_block_read(status_data, 64, FALSE);
if (!success) break;
sc64_sd_spi_read();
sc64_sd_spi_set_prescaler(SC64_SD_SCR_PRESCALER_2);
}
sd_card_initialized = TRUE;
return TRUE;
} while (0);
sc64_sd_deinit();
return FALSE;
}
return TRUE;
}
void sc64_sd_deinit(void) {
sd_card_initialized = FALSE;
sc64_sd_spi_release();
while (platform_pi_io_read(&SC64_SD->scr) & SC64_SD_SCR_BUSY);
platform_pi_io_write(&SC64_SD->dma_scr, SC64_SD_DMA_SCR_FLUSH);
sc64_sd_spi_set_prescaler(SC64_SD_SCR_PRESCALER_256);
sc64_disable_sd(); sc64_disable_sd();
} }
uint8_t sc64_sd_get_status(void) { static sc64_sd_err_t sc64_sd_cmd_send(uint8_t index, uint32_t arg, sc64_sd_cmd_flags_t flags, uint32_t *response) {
return sd_card_initialized ? TRUE : FALSE; sc64_sd_err_t error;
} uint32_t reg;
void sc64_sd_dma_wait_for_finish(void) { if (flags & ACMD) {
while (!(platform_pi_io_read(&SC64_SD->dma_scr) & SC64_SD_DMA_SCR_FIFO_EMPTY)); error = sc64_sd_cmd_send(55, rca, NO_FLAGS, response);
}
void sc64_sd_dma_set_address(uint8_t bank, uint32_t address) { if (error != E_OK) {
platform_pi_io_write(&SC64_SD->dma_addr, SC64_SD_DMA_ADDR(bank, address)); return error;
}
uint8_t sc64_sd_cmd_send(uint8_t cmd, uint32_t arg, uint8_t *response) {
uint8_t cmd_data[7];
uint8_t cmd_data_length;
uint8_t is_long_response;
if (cmd & ACMD_MARK) {
sc64_sd_cmd_send(CMD55, 0, response);
if (*response & ~(R1_IN_IDLE_STATE)) {
return FALSE;
} }
} }
if (sc64_sd_wait_ready() != SD_NOT_BUSY_RESPONSE) { platform_pi_io_write(&SC64_SD->ARG, arg);
return FALSE;
reg = SC64_SD_CMD_START | SC64_SD_CMD_INDEX(index);
if (flags & SKIP_RESPONSE) {
reg |= SC64_SD_CMD_SKIP_RESPONSE;
} }
if ((cmd == CMD17 || cmd == CMD18) && sd_card_type_block) { if (flags & LONG_RESPONSE) {
reg |= SC64_SD_CMD_LONG_RESPONSE;
} }
cmd_data[0] = cmd & ~ACMD_MARK; // Command index platform_pi_io_write(&SC64_SD->CMD, reg);
cmd_data[5] = 0x01; // CRC7 and stop bit
cmd_data[6] = 0xFF; // CMD12 stuff byte
cmd_data_length = 6; do {
is_long_response = FALSE; reg = platform_pi_io_read(&SC64_SD->CMD);
} while (reg & SC64_SD_CMD_BUSY);
switch (cmd) { *response = platform_pi_io_read(&SC64_SD->RSP);
case CMD0:
cmd_data[5] = 0x95; if (reg & SC64_SD_CMD_TIMEOUT) {
return E_TIMEOUT;
}
if ((!(flags & IGNORE_CRC)) && (!(flags & SKIP_RESPONSE)) && (reg & SC64_SD_CMD_RESPONSE_CRC_ERROR)) {
return E_CRC_ERROR;
}
if ((!(flags & SKIP_RESPONSE)) && (!(flags & IGNORE_INDEX)) && (SC64_SD_CMD_INDEX_GET(reg) != index)) {
return E_BAD_INDEX;
}
return E_OK;
}
static void sc64_sd_dat_prepare(size_t num_blocks, size_t block_size, sc64_sd_dat_direction_t direction) {
platform_pi_io_write(&SC64_SD->DAT, (
SC64_SD_DAT_NUM_BLOCKS(num_blocks) |
SC64_SD_DAT_BLOCK_SIZE(block_size) |
(direction ? SC64_SD_DAT_DIRECTION : 0) |
SC64_SD_DAT_START
));
}
static void sc64_sd_dat_abort(void) {
platform_pi_io_write(&SC64_SD->DAT, (
SC64_SD_DAT_TX_FIFO_FLUSH |
SC64_SD_DAT_RX_FIFO_FLUSH |
SC64_SD_DAT_STOP
));
}
static sc64_sd_err_t sc64_sd_dat_read(size_t block_size, void *buffer) {
int timeout;
uint32_t reg;
timeout = 100000;
do {
reg = platform_pi_io_read(&SC64_SD->DAT);
if (SC64_SD_DAT_RX_FIFO_ITEMS_GET(reg) >= block_size) {
break; break;
case CMD8: }
cmd_data[5] = 0x87; } while ((reg & SC64_SD_DAT_BUSY) && (timeout--));
is_long_response = TRUE;
if (reg & SC64_SD_DAT_CRC_ERROR) {
return E_CRC_ERROR;
}
if (reg & SC64_SD_DAT_RX_FIFO_OVERRUN) {
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH);
return E_FIFO_ERROR;
}
if (timeout == 0) {
sc64_sd_dat_abort();
return E_TIMEOUT;
}
platform_pi_dma_read(buffer, &SC64_SD->FIFO, block_size);
platform_cache_invalidate(buffer, block_size);
return E_OK;
}
bool sc64_sd_init(void) {
sc64_sd_err_t error;
uint32_t response;
uint32_t argument;
bool sd_version_2_or_later;
uint8_t buffer[64] __attribute__((aligned(16)));
if (sd_card_initialized) {
return true;
}
sc64_sd_hw_init();
do {
error = sc64_sd_cmd_send(0, 0, SKIP_RESPONSE, &response);
argument = CMD8_ARG_SUPPLY_VOLTAGE_27_36_V | CMD8_ARG_CHECK_PATTERN_AA;
error = sc64_sd_cmd_send(8, argument, NO_FLAGS, &response);
sd_version_2_or_later = (error == E_OK);
if (sd_version_2_or_later && (response != (R7_SUPPLY_VOLTAGE_27_36_V | R7_CHECK_PATTERN_AA))) {
break; break;
case CMD12: }
cmd_data_length = 7;
argument = (sd_version_2_or_later ? ACMD41_ARG_HCS : 0) | 0x00FF8000;
for (int i = 0; i < 4000; i++) {
error = sc64_sd_cmd_send(41, argument, ACMD | IGNORE_CRC | IGNORE_INDEX, &response);
if ((error != E_OK) || (response & R3_BUSY)) {
break; break;
case CMD17: }
case CMD18: }
if ((error != E_OK) || ((response & 0x00FF8000) == 0)) {
break;
}
sd_card_type_block = (response & R3_CCS) ? true : false;
error = sc64_sd_cmd_send(2, 0, LONG_RESPONSE | IGNORE_INDEX, &response);
if (error != E_OK) {
break;
}
error = sc64_sd_cmd_send(3, 0, NO_FLAGS, &response);
if (error != E_OK) {
break;
}
rca = response & 0xFFFF0000;
error = sc64_sd_cmd_send(7, rca, NO_FLAGS, &response);
if (error != E_OK) {
break;
}
sd_card_selected = true;
error = sc64_sd_cmd_send(6, 2, ACMD, &response);
if (error != E_OK) {
break;
}
sc64_sd_set_clock(CLOCK_25_MHZ);
sc64_sd_set_dat_width(DAT_WIDTH_4BIT);
sc64_sd_dat_prepare(1, 64, DAT_DIR_RX);
error = sc64_sd_cmd_send(6, 0x00000001, NO_FLAGS, &response);
if (error != E_OK) {
sc64_sd_dat_abort();
break;
}
error = sc64_sd_dat_read(64, buffer);
if (error != E_OK) {
break;
}
if (buffer[13] & 0x02) {
sc64_sd_dat_prepare(1, 64, DAT_DIR_RX);
error = sc64_sd_cmd_send(6, 0x80000001, NO_FLAGS, &response);
if (error != E_OK) {
sc64_sd_dat_abort();
break;
}
error = sc64_sd_dat_read(64, buffer);
if (error != E_OK) {
break;
}
sc64_sd_set_clock(CLOCK_50_MHZ);
}
sd_card_initialized = true;
return true;
} while(0);
sc64_sd_deinit();
return false;
}
void sc64_sd_deinit(void) {
uint32_t response;
if (sd_card_selected) {
sc64_sd_cmd_send(7, rca, NO_FLAGS, &response);
sd_card_selected = false;
}
sc64_sd_cmd_send(0, 0, SKIP_RESPONSE, &response);
sc64_sd_hw_deinit();
}
bool sc64_sd_get_status(void) {
return sd_card_initialized;
}
sc64_sd_err_t sc64_sd_read_sectors(uint32_t starting_sector, size_t count, void *buffer) {
sc64_sd_err_t error;
uint32_t response;
uint32_t current_sector;
uint32_t reg;
int timeout;
if (!sd_card_initialized) {
return E_NO_INIT;
}
if ((count == 0) || (buffer == NULL)) {
return E_PAR_ERROR;
}
current_sector = starting_sector;
if (!sd_card_type_block) { if (!sd_card_type_block) {
arg *= SD_BLOCK_SIZE; current_sector *= SD_BLOCK_SIZE;
}
break;
case CMD58:
is_long_response = TRUE;
break;
} }
cmd_data[1] = (uint8_t) (arg >> 24); // Command argument for (size_t i = 0; i < count; i++) {
cmd_data[2] = (uint8_t) (arg >> 16); platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_NUM_BLOCKS(1) | SC64_SD_DAT_BLOCK_SIZE(SD_BLOCK_SIZE) | SC64_SD_DAT_START);
cmd_data[3] = (uint8_t) (arg >> 8);
cmd_data[4] = (uint8_t) (arg >> 0);
sc64_sd_spi_multi_write(cmd_data, cmd_data_length); error = sc64_sd_cmd_send(17, current_sector, NO_FLAGS, &response);
if (error != E_OK) {
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH | SC64_SD_DAT_STOP);
for (uint32_t response_timeout = 0; response_timeout < 8; response_timeout++) { return error;
*response = sc64_sd_spi_read();
if (!(*response & RESPONSE_START_BIT)) {
if (is_long_response) {
sc64_sd_spi_multi_read(response + 1, 4, FALSE);
} }
return TRUE; timeout = 100000;
} do {
reg = platform_pi_io_read(&SC64_SD->DAT);
} while ((reg & SC64_SD_DAT_BUSY) && (timeout--));
if (reg & SC64_SD_DAT_CRC_ERROR) {
return E_CRC_ERROR;
} }
return FALSE; if (reg & SC64_SD_DAT_RX_FIFO_OVERRUN) {
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH);
return E_FIFO_ERROR;
} }
uint8_t sc64_sd_block_read(uint8_t *buffer, size_t length, uint8_t dma) { if (timeout == 0) {
uint8_t token; platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH | SC64_SD_DAT_STOP);
uint8_t crc[2];
for (size_t attempts = 10000; attempts > 0; --attempts) { return E_TIMEOUT;
token = sc64_sd_spi_read();
if (token != SD_NOT_BUSY_RESPONSE) {
break;
}
} }
if (token != DATA_TOKEN_BLOCK_READ) { platform_pi_dma_read(buffer, &SC64_SD->FIFO, SD_BLOCK_SIZE);
return FALSE; platform_cache_invalidate(buffer, SD_BLOCK_SIZE);
buffer += SD_BLOCK_SIZE;
current_sector += sd_card_type_block ? 1 : SD_BLOCK_SIZE;
} }
if (dma) { return E_OK;
sc64_sd_spi_multi_read_dma(length);
} else {
sc64_sd_spi_multi_read(buffer, length, TRUE);
} }
sc64_sd_spi_multi_read(crc, 2, FALSE);
return TRUE; sc64_sd_err_t sc64_sd_read_sectors_dma(uint32_t starting_sector, size_t count, uint8_t bank, uint32_t address) {
size_t num_transfers;
size_t sectors_left;
uint32_t current_sector;
uint32_t current_address;
uint32_t num_blocks;
uint32_t reg;
sc64_sd_err_t error;
uint32_t response;
int timeout;
if (!sd_card_initialized) {
return E_NO_INIT;
}
if (count == 0) {
return E_PAR_ERROR;
}
num_transfers = (count / 2048) + 1;
sectors_left = count;
current_sector = starting_sector;
if (!sd_card_type_block) {
current_sector *= SD_BLOCK_SIZE;
}
current_address = address;
for (size_t i = 0; i < num_transfers; i++) {
num_blocks = (sectors_left > 2048) ? 2048 : sectors_left;
platform_pi_io_write(&SC64_SD->DMA_ADDR, SC64_SD_DMA_BANK_ADDR(bank, current_address));
platform_pi_io_write(&SC64_SD->DMA_LEN, SC64_SD_DMA_LEN(num_blocks * SD_BLOCK_SIZE));
platform_pi_io_write(&SC64_SD->DMA_SCR, SC64_SD_DMA_SCR_DIRECTION | SC64_SD_DMA_SCR_START);
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_NUM_BLOCKS(num_blocks) | SC64_SD_DAT_BLOCK_SIZE(SD_BLOCK_SIZE) | SC64_SD_DAT_START);
error = sc64_sd_cmd_send(18, current_sector, NO_FLAGS, &response);
if (error != E_OK) {
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH | SC64_SD_DAT_STOP);
return error;
}
timeout = 1000000;
do {
reg = platform_pi_io_read(&SC64_SD->DAT);
} while ((reg & SC64_SD_DAT_BUSY) && (timeout--));
error = sc64_sd_cmd_send(12, 0, NO_FLAGS, &response);
if (error != E_OK) {
return error;
}
if (reg & SC64_SD_DAT_CRC_ERROR) {
return E_CRC_ERROR;
}
if (reg & SC64_SD_DAT_RX_FIFO_OVERRUN) {
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH);
platform_pi_io_write(&SC64_SD->DMA_SCR, SC64_SD_DMA_SCR_STOP);
return E_FIFO_ERROR;
}
if (timeout == 0) {
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH | SC64_SD_DAT_STOP);
platform_pi_io_write(&SC64_SD->DMA_SCR, SC64_SD_DMA_SCR_STOP);
return E_TIMEOUT;
}
while (platform_pi_io_read(&SC64_SD->DMA_SCR) & SC64_SD_DMA_SCR_BUSY);
current_sector += sd_card_type_block ? 1 : SD_BLOCK_SIZE;
current_address += num_blocks * SD_BLOCK_SIZE;
}
return E_OK;
} }

View File

@ -5,62 +5,22 @@
#include "platform.h" #include "platform.h"
#define RESPONSE_START_BIT (1 << 7) typedef enum sc64_sd_err_e {
E_OK,
#define R1_PARAMETER_ERROR (1 << 6) E_TIMEOUT,
#define R1_ADDRESS_ERROR (1 << 5) E_CRC_ERROR,
#define R1_ERASE_SEQUENCE_ERROR (1 << 4) E_BAD_INDEX,
#define R1_COM_CRC_ERROR (1 << 3) E_PAR_ERROR,
#define R1_ILLEGAL_COMMAND (1 << 2) E_FIFO_ERROR,
#define R1_ERASE_RESET (1 << 1) E_NO_INIT,
#define R1_IN_IDLE_STATE (1 << 0) } sc64_sd_err_t;
#define R3_27_36_V_BIT (15)
#define R3_27_36_V_MASK (0x1FF << R3_27_36_V_BIT)
#define R3_CCS (1 << 30)
#define R7_COMMAND_VERSION_BIT (28)
#define R7_COMMAND_VERSION_MASK (0xF << R7_COMMAND_VERSION_BIT)
#define R7_VOLTAGE_ACCEPTED_BIT (8)
#define R7_VOLTAGE_ACCEPTED_MASK (0xF << R7_VOLTAGE_ACCEPTED_BIT)
#define R7_CHECK_PATTERN_BIT (0)
#define R7_CHECK_PATTERN_MASK (0xFF << R7_CHECK_PATTERN_BIT)
#define R7_SUPPLY_VOLTAGE_27_36_V (1 << 8)
#define R7_CHECK_PATTERN_AA (0xAA << 0)
#define SD_NOT_BUSY_RESPONSE (0xFF)
#define CMD_START_BIT (1 << 6)
#define ACMD_MARK (1 << 7)
#define CMD0 (CMD_START_BIT | 0)
#define CMD6 (CMD_START_BIT | 6)
#define CMD8 (CMD_START_BIT | 8)
#define CMD12 (CMD_START_BIT | 12)
#define CMD17 (CMD_START_BIT | 17)
#define CMD18 (CMD_START_BIT | 18)
#define CMD55 (CMD_START_BIT | 55)
#define CMD58 (CMD_START_BIT | 58)
#define ACMD41 (ACMD_MARK | CMD_START_BIT | 41)
#define CMD8_ARG_SUPPLY_VOLTAGE_27_36_V (1 << 8)
#define CMD8_ARG_CHECK_PATTERN_AA (0xAA << 0)
#define ACMD41_ARG_HCS (1 << 30)
#define DATA_TOKEN_BLOCK_READ (0xFE)
#define SD_BLOCK_SIZE (512)
uint8_t sc64_sd_init(void); bool sc64_sd_init(void);
void sc64_sd_deinit(void); void sc64_sd_deinit(void);
uint8_t sc64_sd_get_status(void); bool sc64_sd_get_status(void);
void sc64_sd_dma_wait_for_finish(void); sc64_sd_err_t sc64_sd_read_sectors(uint32_t starting_sector, size_t count, void *buffer);
void sc64_sd_dma_set_address(uint8_t bank, uint32_t address); sc64_sd_err_t sc64_sd_read_sectors_dma(uint32_t starting_sector, size_t count, uint8_t bank, uint32_t address);
uint8_t sc64_sd_cmd_send(uint8_t cmd, uint32_t arg, uint8_t *response);
uint8_t sc64_sd_block_read(uint8_t *buffer, size_t length, uint8_t dma);
#endif #endif

View File

@ -6,10 +6,7 @@
#include "sc64_sd_fs.h" #include "sc64_sd_fs.h"
static DRESULT sc64_sd_fs_load_with_dma(BYTE pdrv, FSIZE_t offset, LBA_t sector, UINT count) { static DRESULT sc64_sd_fs_load_rom_with_dma(BYTE pdrv, FSIZE_t offset, LBA_t sector, UINT count) {
uint8_t success;
uint8_t response;
if ((pdrv > 0) || (count == 0)) { if ((pdrv > 0) || (count == 0)) {
return RES_PARERR; return RES_PARERR;
} }
@ -18,29 +15,9 @@ static DRESULT sc64_sd_fs_load_with_dma(BYTE pdrv, FSIZE_t offset, LBA_t sector,
return RES_NOTRDY; return RES_NOTRDY;
} }
sc64_sd_dma_set_address(SC64_BANK_SDRAM, (uint32_t) offset); if (sc64_sd_read_sectors_dma(sector, count, SC64_BANK_SDRAM, offset) != E_OK) {
success = sc64_sd_cmd_send(CMD18, sector, &response);
if (!success || response) {
return RES_PARERR;
}
for (size_t i = 0; i < count; i++) {
success = sc64_sd_block_read(NULL, SD_BLOCK_SIZE, TRUE);
if (!success) {
return RES_ERROR; return RES_ERROR;
} }
}
success = sc64_sd_cmd_send(CMD12, 0, &response);
if (!success || response) {
return RES_ERROR;
}
sc64_sd_dma_wait_for_finish();
return RES_OK; return RES_OK;
} }
@ -69,7 +46,7 @@ sc64_sd_fs_error_t sc64_sd_fs_load_rom(const char *path) {
break; break;
} }
fresult = fe_load(path, SC64_SDRAM_SIZE, sc64_sd_fs_load_with_dma); fresult = fe_load(path, SC64_SDRAM_SIZE, sc64_sd_fs_load_rom_with_dma);
if (fresult) { if (fresult) {
switch (fresult) { switch (fresult) {
case FR_DISK_ERR: case FR_DISK_ERR: