Files
Custom-Operating-System/boot/arm32/drivers/pl181.c

163 lines
4.6 KiB
C

#include "pl181.h"
#include <libboot/devtree/devtree.h>
#include <libboot/log/log.h>
// #define DEBUG_PL181
static sd_card_t sd_card;
static volatile pl181_registers_t* registers;
static inline int _pl181_map_itself(devtree_entry_t* dev)
{
registers = (pl181_registers_t*)(uint32_t)dev->region_base;
return 0;
}
static inline void _pl181_clear_flags()
{
registers->clear = 0x5FF;
}
static int _pl181_send_cmd(uint32_t cmd, uint32_t param)
{
_pl181_clear_flags();
registers->arg = param;
registers->cmd = cmd;
while (registers->status & MMC_STAT_CMD_ACTIVE_MASK) { }
if ((cmd & MMC_CMD_RESP_MASK) == MMC_CMD_RESP_MASK) {
while (((registers->status & MMC_STAT_CMD_RESP_END_MASK) != MMC_STAT_CMD_RESP_END_MASK) || (registers->status & MMC_STAT_CMD_ACTIVE_MASK)) {
if (registers->status & MMC_STAT_CMD_TIMEOUT_MASK) {
return -1;
}
}
} else {
while ((registers->status & MMC_STAT_CMD_SENT_MASK) != MMC_STAT_CMD_SENT_MASK) { }
}
return 0;
}
static int _pl181_send_app_cmd(uint32_t cmd, uint32_t param)
{
for (int i = 0; i < 5; i++) {
_pl181_send_cmd(CMD_APP_CMD | MMC_CMD_ENABLE_MASK | MMC_CMD_RESP_MASK, 0);
if (registers->response[0] & (1 << 5)) {
return _pl181_send_cmd(cmd, param);
}
}
log_error("Failed to send app command");
return -4;
}
inline static int _pl181_select_card(uint32_t rca)
{
return _pl181_send_cmd(CMD_SELECT | MMC_CMD_ENABLE_MASK | MMC_CMD_RESP_MASK, rca);
}
static int _pl181_read_block(uint32_t lba_like, void* read_data)
{
sd_card_t* sd_card_ptr = &sd_card;
uint32_t* read_data32 = (uint32_t*)read_data;
uint32_t bytes_read = 0;
registers->data_length = PL181_SECTOR_SIZE; // Set length of bytes to transfer
registers->data_control = 0b11; // Enable dpsm and set direction from card to host
if (sd_card_ptr->ishc) {
_pl181_send_cmd(CMD_READ_SINGLE_BLOCK | MMC_CMD_ENABLE_MASK | MMC_CMD_RESP_MASK, lba_like);
} else {
_pl181_send_cmd(CMD_READ_SINGLE_BLOCK | MMC_CMD_ENABLE_MASK | MMC_CMD_RESP_MASK, lba_like * PL181_SECTOR_SIZE);
}
while (registers->status & MMC_STAT_FIFO_DATA_AVAIL_TO_READ_MASK) {
*read_data32 = registers->fifo_data[0];
read_data32++;
bytes_read += 4;
}
return bytes_read;
}
static int _pl181_register_device(uint32_t rca, bool ishc, uint32_t capacity)
{
sd_card.rca = rca;
sd_card.ishc = ishc;
sd_card.capacity = capacity;
_pl181_select_card(rca);
_pl181_send_cmd(CMD_SET_SECTOR_SIZE | MMC_CMD_ENABLE_MASK | MMC_CMD_RESP_MASK, PL181_SECTOR_SIZE);
if (registers->response[0] != 0x900) {
log_error("PL181(pl181_add_new_device): Can't set sector size");
return -1;
}
return 0;
}
int pl181_init(drive_desc_t* drive_desc)
{
devtree_entry_t* dev = devtree_find_device("pl181");
if (!dev) {
return -1;
}
if (_pl181_map_itself(dev)) {
#ifdef DEBUG_PL181
log_error("PL181: Can't map itself!");
#endif
return -1;
}
#ifdef DEBUG_PL181
log("PL181: Turning on");
#endif
registers->clock = 0x1C6;
registers->power = 0x86;
// Send cmd 0 to set all cards into idle state
_pl181_send_cmd(CMD_GO_IDLE_STATE | MMC_CMD_ENABLE_MASK, 0x0);
// Voltage Check
_pl181_send_cmd(8 | MMC_CMD_ENABLE_MASK | MMC_CMD_RESP_MASK, 0x1AA);
if ((registers->response[0] & 0x1AA) != 0x1AA) {
#ifdef DEBUG_PL181
log_error("PL181: Can't set voltage!");
#endif
return -1;
}
if (_pl181_send_app_cmd(CMD_SD_SEND_OP_COND | MMC_CMD_ENABLE_MASK | MMC_CMD_RESP_MASK, 1 << 30 | 0x1AA)) {
#ifdef DEBUG_PL181
log_error("PL181: Can't send APP_CMD!");
#endif
return -1;
}
bool ishc = 0;
if (registers->response[0] & 1 << 30) {
ishc = 1;
#ifdef DEBUG_PL181
log("PL181: ishc = 1");
#endif
}
_pl181_send_cmd(CMD_ALL_SEND_CID | MMC_CMD_ENABLE_MASK | MMC_CMD_LONG_RESP_MASK, 0x0);
_pl181_send_cmd(CMD_SET_RELATIVE_ADDR | MMC_CMD_ENABLE_MASK | MMC_CMD_RESP_MASK, 0x0);
// Get the card RCA from the response it is the top 16 bits of the 32 bit response
uint32_t rca = (registers->response[0] & 0xFFFF0000);
_pl181_send_cmd(CMD_SEND_CSD | MMC_CMD_ENABLE_MASK | MMC_CMD_RESP_MASK | MMC_CMD_LONG_RESP_MASK, rca);
uint32_t resp1 = registers->response[1];
uint32_t capacity = ((resp1 >> 8) & 0x3) << 10;
capacity |= (resp1 & 0xFF) << 2;
capacity = 256 * 1024 * (capacity + 1);
_pl181_register_device(rca, ishc, capacity);
drive_desc->read = _pl181_read_block;
return 0;
}