diff options
Diffstat (limited to 'src/sdcard.c')
-rw-r--r-- | src/sdcard.c | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/src/sdcard.c b/src/sdcard.c new file mode 100644 index 0000000..6cf602b --- /dev/null +++ b/src/sdcard.c @@ -0,0 +1,222 @@ +/** + * @file sdcard.c + * Provides a basic library for accessing an SD card + * + * Copyright (C) 2018 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <clock.h> +#include <gpio.h> +#include <heap.h> +#include <sdcard.h> +#include <string.h> + +#define SCK GPIO_PORT(A, 5) +#define SO GPIO_PORT(A, 6) +#define SI GPIO_PORT(A, 7) +#define CS GPIO_PORT(B, 6) + +typedef struct { + uint8_t cmd; + uint32_t arg; + uint8_t crc; +} __attribute__ ((packed)) sdcmd_t; + +typedef struct { + uint8_t zero :1; + uint8_t param :1; + uint8_t address :1; + uint8_t erase :1; + uint8_t crc :1; + uint8_t badcmd :1; + uint8_t ereset :1; + uint8_t idle :1; +} __attribute__ ((packed)) r1_t; + +void flash_out(uint8_t byte) +{ + for (int i = 0; i < 8; i++) { + gpio_dout(SI, byte & (1 << (7 - i))); + gpio_dout(SCK, 1); + gpio_dout(SCK, 0); + } +} + +uint8_t flash_in(void) +{ + uint8_t byte = 0; + for (int i = 0; i < 8; i++) { + if (gpio_din(SO)) + byte |= 1 << (7 - i); + gpio_dout(SCK, 1); + gpio_dout(SCK, 0); + } + return byte; +} + +void sd_delay(void) +{ + gpio_dout(SI, 1); + uint8_t so; + int i = 0; + do { + so = flash_in(); + if (i < 8) + i++; + } while (i < 8 || so != 0xFF); +} + +uint8_t sd_send_cmd(uint8_t cmd, uint32_t arg) +{ + sdcmd_t cmdp; + cmdp.cmd = (cmd & 0x3F) | 0x40; + cmdp.arg = arg; + + sd_delay(); + flash_out(cmdp.cmd); + flash_out(cmdp.arg >> 24); + flash_out(cmdp.arg >> 16); + flash_out(cmdp.arg >> 8); + flash_out(cmdp.arg); + flash_out(cmd == 8 ? 0x87 : 0x95); + + // wait for a zero + gpio_dout(SI, 1); + uint8_t r1; + do { + r1 = flash_in(); + } while (r1 & 0x80); + + return r1; +} + +void sd_init(void) +{ + gpio_mode(SCK, OUTPUT); + gpio_mode(SI, OUTPUT); + gpio_mode(CS, OUTPUT); + gpio_mode(SO, OUTPUT); + gpio_speed(SCK, VERYHIGH); + gpio_speed(SI, VERYHIGH); + gpio_speed(SO, VERYHIGH); + gpio_speed(CS, VERYHIGH); + gpio_pupd(SCK, PULLUP); + gpio_pupd(SI, PULLUP); + gpio_pupd(SO, PULLUP); + gpio_pupd(CS, PULLUP); + gpio_dout(SO, 0); + gpio_mode(SO, INPUT); + gpio_dout(CS, 1); + gpio_dout(SCK, 0); + gpio_dout(SI, 1); + + // init? cs and si are high + delay(10); + sd_delay(); + + // pull cs low, send cmd0 + gpio_dout(CS, 0); + uint8_t resp = sd_send_cmd(0, 0); + sd_delay(); + gpio_dout(CS, 1); + + if (resp != 0x01) + while (1); + + // do cmd8 + delay(10); + gpio_dout(CS, 0); + resp = sd_send_cmd(8, 0x1AA); + uint8_t ocr[4]; + for (int i = 0; i < 4; i++) + ocr[i] = flash_in(); + sd_delay(); + gpio_dout(CS, 1); + (void)ocr; + + // initialize + do { + // cmd55 + gpio_dout(CS, 0); + resp = sd_send_cmd(55, 0); + sd_delay(); + gpio_dout(CS, 1); + delay(10); + + // acmd41 + gpio_dout(CS, 0); + resp = sd_send_cmd(41, 0x40000000); + sd_delay(); + gpio_dout(CS, 1); + } while (resp != 0x00); + + // set block length + gpio_dout(CS, 0); + resp = sd_send_cmd(16, 512); + sd_delay(); + gpio_dout(CS, 1); +} + +uint8_t *sd_read_block(uint8_t *buf, uint32_t lba) +{ + if (buf == 0) + return 0; + + // send command + gpio_dout(CS, 0); + if (sd_send_cmd(17, lba) != 0) + return 0; + + // wait for block + gpio_dout(SI, 1); + uint8_t byte; + do byte = flash_in(); + while (byte == 0xFF); + if (byte != 0xFE) + return 0; + + // get block + uint32_t i = 0; + for (i = 0; i < 512; i++) + buf[i] = flash_in(); + + sd_delay(); + gpio_dout(CS, 1); + return buf; +} + +uint8_t *sd_read(uint8_t *buf, uint32_t lba, uint32_t count) +{ + if (buf == 0) + return 0; + + uint8_t *block_buf = malloc(512); + uint32_t i = 0; + while (count > 0) { + sd_read_block(block_buf, lba); + memcpy(buf + i, block_buf, (count < 512) ? count : 512); + if (count < 512) + count = 0; + else + count -= 512; + i += 512; + lba++; + } + + free(block_buf); + return buf; +} + |