FoenixMCP/src/dev/pata.c
Peter Weingartner 863bbd9472 Fixed IDE issues
Can now read a directory.
2021-10-06 11:54:33 -04:00

443 lines
10 KiB
C

/**
* Implementation of the PATA hard drive low level driver
*/
#include <string.h>
#include "log.h"
#include "errors.h"
#include "constants.h"
// #include "fatfs/ff.h"
#include "dev/block.h"
#include "dev/pata.h"
#include "dev/text_screen_iii.h"
#include "pata_reg.h"
//
// Variables
//
short g_pata_error = 0; // Most recent error code received from the PATA drive
short g_pata_status = PATA_STAT_NOINIT; // Status of the PATA interface
//
// Code
//
//
// Wait until the PATA drive is no longer busy, or we've run out of time
//
// Returns:
// 0 on success (PATA drive is no longer busy), DEV_TIMEOUT on timeout
//
short pata_wait_not_busy() {
short count = MAX_TRIES_BUSY;
char status;
TRACE("pata_wait_not_busy");
do {
status = *PATA_CMD_STAT;
} while ((status & PATA_STAT_BSY) && (count-- > 0));
if (count == 0) {
return DEV_TIMEOUT;
} else {
return 0;
}
}
//
// Wait until the PATA drive is ready, or we've run out of time
//
// Returns:
// 0 on success (PATA drive is ready), DEV_TIMEOUT on timeout
//
short pata_wait_ready() {
short count = MAX_TRIES_BUSY;
char status;
TRACE("pata_wait_ready");
do {
status = *PATA_CMD_STAT;
} while (((status & PATA_STAT_DRDY) == 0) && (count-- > 0));
if (count == 0) {
return DEV_TIMEOUT;
} else {
return 0;
}
}
//
// Wait until the PATA drive is ready and not busy, or we've run out of time
//
// Returns:
// 0 on success (PATA drive is ready and not busy), DEV_TIMEOUT on timeout
//
short pata_wait_ready_not_busy() {
short count = MAX_TRIES_BUSY;
char status;
TRACE("pata_wait_ready_not_busy");
// do {
// status = *PATA_CMD_STAT;
// } while (((status & (PATA_STAT_DRDY | PATA_STAT_BSY)) != PATA_STAT_DRDY) && (count-- > 0));
do {
while ((*PATA_CMD_STAT & PATA_STAT_DRDY) != PATA_STAT_DRDY);
} while ((*PATA_CMD_STAT & PATA_STAT_BSY) == PATA_STAT_BSY);
if (count == 0) {
return DEV_TIMEOUT;
} else {
return 0;
}
}
//
// Wait until the PATA drive is ready to transfer data, or we've run out of time
//
// Returns:
// 0 on success (PATA drive is ready to transfer data), DEV_TIMEOUT on timeout
//
short pata_wait_data_request() {
short count = MAX_TRIES_BUSY;
char status;
TRACE("pata_wait_data_request");
do {
status = *PATA_CMD_STAT;
} while (((status & PATA_STAT_DRQ) != PATA_STAT_DRQ) && (count-- > 0));
if (count == 0) {
return DEV_TIMEOUT;
} else {
return 0;
}
}
char g_buffer[512];
//
// Identify the PATA drive
//
// Inputs:
// drive_info = pointer to a s_drive_info
//
// Returns:
// 0 on success, any negative number is an error code
//
short pata_identity(p_drive_info drive_info) {
char * buffer;
unsigned short *wptr;
char * cptr;
short i;
short count;
TRACE("pata_identity");
*PATA_HEAD = 0xe0; // Drive 0, lBA enabled, Head 0
*PATA_SECT_CNT = 1;
*PATA_SECT_SRT = 0;
*PATA_CLDR_LO = 0;
*PATA_CLDR_HI = 0;
// Issue identity command
*PATA_CMD_STAT = PATA_CMD_IDENTITY;
if (pata_wait_not_busy()) {
return DEV_TIMEOUT;
}
// TODO: Wait ~500ns
if (pata_wait_ready_not_busy()) {
return DEV_TIMEOUT;
}
TRACE("copying data");
// Copy the data... let the compiler and the FPGA worry about endianess
wptr = (unsigned short *)g_buffer;
for (i = 0; i < 512; ) {
unsigned short data = *PATA_DATA_16;
g_buffer[i++] = data & 0xff;
g_buffer[i++] = (data >> 8) & 0xff;
}
TRACE("data copied");
wptr = (unsigned short *)buffer;
drive_info->flags = g_buffer[1] << 16 | g_buffer[0];
drive_info->lba_enabled = g_buffer[99] << 16 | g_buffer[98];
drive_info->l.lbaw.lba_default_lo = g_buffer[121] << 8 | g_buffer[120];
drive_info->l.lbaw.lba_default_hi = g_buffer[123] << 8 | g_buffer[122];
// Copy the serial number (need to swap chars)
memcpy(&(drive_info->serial_number), g_buffer + 22, sizeof(drive_info->serial_number));
// Copy the firmware version (need to swap chars)
memcpy(&(drive_info->firmware_version), g_buffer + 46, sizeof(drive_info->firmware_version));
// Copy the model name (need to swap chars)
memcpy(&(drive_info->model_name), g_buffer + 54, sizeof(drive_info->model_name));
return 0;
}
//
// Initialize the PATA hard drive
//
// Returns:
// 0 on success, any negative number is an error code
//
short pata_init() {
short result;
TRACE("pata_init");
// Issue intialize command
*PATA_CMD_STAT = PATA_CMD_INIT;
if (pata_wait_not_busy()) {
return DEV_TIMEOUT;
}
*PATA_HEAD = 0xA0; // Drive 0, lBA enabled, Head 0
*PATA_SECT_CNT = 1;
*PATA_SECT_SRT = 0;
*PATA_CLDR_LO = 0;
*PATA_CLDR_HI = 0;
if (pata_wait_ready_not_busy()) {
return DEV_TIMEOUT;
}
// Mark that the drive is initialized and present
g_pata_status = PATA_STAT_PRESENT;
return 0;
}
//
// Read a block from the PATA hard drive
//
// Inputs:
// lba = the logical block address of the block to read
// buffer = the buffer into which to copy the block data
// size = the size of the buffer.
//
// Returns:
// number of chars read, any negative number is an error code
//
short pata_read(long lba, unsigned char * buffer, short size) {
short i;
unsigned short *wptr;
TRACE("pata_read");
log_num(LOG_VERBOSE, "pata_read lba: ", lba);
if (pata_wait_ready_not_busy()) {
return DEV_TIMEOUT;
}
*PATA_HEAD = ((lba >> 24) & 0x07) | 0xe0; // Upper 3 bits of LBA, Drive 0, LBA mode.
if (pata_wait_ready_not_busy()) {
return DEV_TIMEOUT;
}
*PATA_SECT_CNT = 1; // Read one sector (make this an option?)
*PATA_SECT_SRT = lba & 0xff; // Set the rest of the LBA
*PATA_CLDR_LO = (lba >> 8) & 0xff;
*PATA_CLDR_HI = (lba >> 16) & 0xff;
*PATA_CMD_STAT = PATA_CMD_READ_SECTOR; // Issue the READ command
// TODO: Wait ~500ns
if (pata_wait_ready_not_busy()) {
return DEV_TIMEOUT;
}
if (pata_wait_data_request()) {
return DEV_TIMEOUT;
}
// Copy the data... let the compiler and the FPGA worry about endianess
for (i = 0, wptr = (unsigned short *)buffer; i < size; i += 2) {
*wptr++ = *PATA_DATA_16;
}
return i;
}
//
// Write a block to the PATA hard drive
//
// Inputs:
// lba = the logical block address of the block to write
// buffer = the buffer containing the data to write
// size = the size of the buffer.
//
// Returns:
// number of chars written, any negative number is an error code
//
short pata_write(long lba, const unsigned char * buffer, short size) {
short i;
unsigned short *wptr;
TRACE("pata_write");
if (pata_wait_ready_not_busy()) {
return DEV_TIMEOUT;
}
*PATA_HEAD = ((lba >> 24) & 0x07) | 0xe0; // Upper 3 bits of LBA, Drive 0, LBA mode.
if (pata_wait_ready_not_busy()) {
return DEV_TIMEOUT;
}
*PATA_SECT_CNT = 1; // Read one sector (make this an option?)
*PATA_SECT_SRT = lba & 0xff; // Set the rest of the LBA
*PATA_CLDR_LO = (lba >> 8) & 0xff;
*PATA_CLDR_LO = (lba >> 16) & 0xff;
*PATA_CMD_STAT = PATA_CMD_WRITE_SECTOR; // Issue the WRITE command
// TODO: Wait ~500ns
if (pata_wait_ready_not_busy()) {
return DEV_TIMEOUT;
}
// Copy the data... let the compiler and the FPGA worry about endianess
for (i = 0, wptr = (unsigned short *)buffer; i < size; i += 2) {
*PATA_DATA_16 = *wptr++;
}
return 0;
}
//
// Return the status of the PATA hard drive
//
// Returns:
// the status of the device
//
short pata_status() {
TRACE("pata_status");
return g_pata_status;
}
//
// Return any error code of the PATA hard drive
//
// Returns:
// the error code of the device
//
short pata_error() {
TRACE("pata_error");
return g_pata_error;
}
//
// Ensure that any pending writes to teh device have been completed
//
// Returns:
// 0 on success, any negative number is an error code
//
short pata_flush() {
TRACE("pata_flush");
return 0;
}
//
// Issue a control command to the PATA hard drive
//
// Inputs:
// command = the number of the command to send
// buffer = pointer to chars of additional data for the command
// size = the size of the buffer
//
// Returns:
// 0 on success, any negative number is an error code
//
short pata_ioctrl(short command, unsigned char * buffer, short size) {
short result;
long *p_long;
unsigned short *p_word;
long *p_lba_word;
t_drive_info drive_info;
p_drive_info p_info;
TRACE("pata_ioctrl");
switch (command) {
case PATA_GET_SECTOR_COUNT:
p_lba_word = (long *)buffer;
result = pata_identity(&drive_info);
if (result != 0) {
return result;
}
*p_lba_word = drive_info.l.lba_default;
break;
case PATA_GET_SECTOR_SIZE:
// Return the size of a sector... always 512
p_word = (unsigned short *)buffer;
*p_word = PATA_SECTOR_SIZE;
break;
case PATA_GET_BLOCK_SIZE:
// This isn't a flash device... return 1
p_long = (long *)buffer;
*p_long = 1;
break;
case PATA_GET_DRIVE_INFO:
p_info = (p_drive_info *)buffer;
result = pata_identity(p_info);
if (result != 0) {
return result;
}
break;
default:
return 0;
}
return 0;
}
//
// Install the PATA driver
//
// Returns:
// 0 on success, any negative number is an error code
//
short pata_install() {
t_dev_block bdev;
TRACE("pata_install");
g_pata_error = 0;
g_pata_status = PATA_STAT_NOINIT;
// Check if drive is installed
// if ((*DIP_BOOTMODE & HD_INSTALLED) == 0) {
bdev.number = BDEV_HDC;
bdev.name = "HDD";
bdev.init = pata_init;
bdev.read = pata_read;
bdev.write = pata_write;
bdev.status = pata_status;
bdev.flush = pata_flush;
bdev.ioctrl = pata_ioctrl;
g_pata_status = PATA_STAT_PRESENT & PATA_STAT_NOINIT;
return bdev_register(&bdev);
// } else {
// return 0;
// }
}