mirror of
https://github.com/spicyjpeg/573in1.git
synced 2025-01-22 19:52:05 +01:00
Rewrite IDE driver completely, fix VFS and flash driver
This commit is contained in:
parent
db5eaabe57
commit
ef46aad5b0
@ -106,6 +106,7 @@ addExecutable(
|
||||
src/common/io.cpp
|
||||
src/common/pad.cpp
|
||||
src/common/rom.cpp
|
||||
src/common/romdrivers.cpp
|
||||
src/common/spu.cpp
|
||||
src/common/util.cpp
|
||||
src/libc/crt0.c
|
||||
|
@ -6,8 +6,11 @@
|
||||
|
||||
"App": {
|
||||
"ideInitWorker": {
|
||||
"initDrives": "Initializing IDE devices...\nDo not turn off the 573 or unplug drives.",
|
||||
"initFileIO": "Detecting and mounting filesystems...\nDo not turn off the 573 or unplug drives.",
|
||||
"init": "Initializing IDE devices...\nDo not turn off the 573 or unplug drives."
|
||||
},
|
||||
"fileInitWorker": {
|
||||
"unmount": "Unmounting all mounted filesystems...\nDo not turn off the 573 or unplug drives.",
|
||||
"mount": "Detecting and mounting filesystems...\nDo not turn off the 573 or unplug drives.",
|
||||
"loadResources": "Loading resource pack...\nDo not turn off the 573 or unplug drives.",
|
||||
"autoboot": "Searching for boot executables...\nDo not turn off the 573 or unplug drives."
|
||||
},
|
||||
@ -113,7 +116,7 @@
|
||||
|
||||
"AudioTestScreen": {
|
||||
"title": "{RIGHT_ARROW} Audio output test",
|
||||
"prompt": "Note that the speaker amplifier and analog audio passthrough are disabled by default and will be disabled again once this screen is closed.",
|
||||
"prompt": "Note that the speaker amplifier and analog audio passthrough are disabled by this tool by default.",
|
||||
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back",
|
||||
|
||||
"playLeft": "Play sound on left channel",
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/ide.hpp"
|
||||
#include "common/idedefs.hpp"
|
||||
#include "common/io.hpp"
|
||||
#include "common/util.hpp"
|
||||
#include "ps1/registers573.h"
|
||||
@ -22,13 +23,6 @@
|
||||
|
||||
namespace ide {
|
||||
|
||||
static constexpr int _COMMAND_TIMEOUT = 30000000;
|
||||
static constexpr int _REQUEST_SENSE_TIMEOUT = 500000;
|
||||
static constexpr int _DETECT_DRIVE_TIMEOUT = 500000;
|
||||
|
||||
static constexpr int _DMA_TIMEOUT = 10000;
|
||||
static constexpr int _SRST_DELAY = 5000;
|
||||
|
||||
static const char *const _SENSE_KEY_NAMES[]{
|
||||
"NO_SENSE",
|
||||
"RECOVERED_ERROR",
|
||||
@ -39,25 +33,25 @@ static const char *const _SENSE_KEY_NAMES[]{
|
||||
"UNIT_ATTENTION",
|
||||
"DATA_PROTECT",
|
||||
"BLANK_CHECK",
|
||||
nullptr,
|
||||
nullptr,
|
||||
"UNKNOWN_9",
|
||||
"UNKNOWN_A",
|
||||
"ABORTED_COMMAND",
|
||||
nullptr,
|
||||
nullptr,
|
||||
"UNKNOWN_C",
|
||||
"UNKNOWN_D",
|
||||
"MISCOMPARE",
|
||||
nullptr
|
||||
"UNKNOWN_F"
|
||||
};
|
||||
|
||||
const char *const DEVICE_ERROR_NAMES[]{
|
||||
"NO_ERROR",
|
||||
"UNSUPPORTED_OP",
|
||||
"NO_DRIVE",
|
||||
"NOT_YET_READY",
|
||||
"STATUS_TIMEOUT",
|
||||
"CHECKSUM_MISMATCH",
|
||||
"DRIVE_ERROR",
|
||||
"DISC_ERROR",
|
||||
"DISC_CHANGED",
|
||||
"INCOMPLETE_DATA",
|
||||
"CHECKSUM_MISMATCH"
|
||||
"DISC_CHANGED"
|
||||
};
|
||||
|
||||
/* Utilities */
|
||||
@ -92,6 +86,50 @@ static void _copyString(char *output, const uint16_t *input, size_t length) {
|
||||
}
|
||||
#endif
|
||||
|
||||
static DeviceError _senseDataToError(const SenseData &data) {
|
||||
auto key = data.senseKey & 15;
|
||||
auto asc = data.getPackedASC();
|
||||
auto lba = data.getErrorLBA();
|
||||
|
||||
LOG("%s", _SENSE_KEY_NAMES[key]);
|
||||
LOG("err=0x%02x, key=0x%02x", data.errorCode, data.senseKey);
|
||||
LOG("asc=0x%02x, ascq=0x%02x", data.asc, data.ascQualifier);
|
||||
|
||||
if (lba) {
|
||||
LOG("lba=0x%08x", lba);
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case SENSE_KEY_NO_SENSE:
|
||||
case SENSE_KEY_RECOVERED_ERROR:
|
||||
return NO_ERROR;
|
||||
|
||||
case SENSE_KEY_NOT_READY:
|
||||
return (
|
||||
!asc ||
|
||||
(asc == ASC_NOT_READY) ||
|
||||
(asc == ASC_NOT_READY_IN_PROGRESS)
|
||||
)
|
||||
? NOT_YET_READY
|
||||
: DISC_ERROR;
|
||||
|
||||
case SENSE_KEY_MEDIUM_ERROR:
|
||||
case SENSE_KEY_DATA_PROTECT:
|
||||
return DISC_ERROR;
|
||||
|
||||
case SENSE_KEY_UNIT_ATTENTION:
|
||||
return (asc == ASC_RESET_OCCURRED)
|
||||
? NO_ERROR
|
||||
: DISC_CHANGED;
|
||||
|
||||
case SENSE_KEY_ABORTED_COMMAND:
|
||||
return UNSUPPORTED_OP;
|
||||
|
||||
default:
|
||||
return DRIVE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
bool IdentifyBlock::validateChecksum(void) const {
|
||||
if ((checksum & 0xff) != 0xa5)
|
||||
return true;
|
||||
@ -122,29 +160,129 @@ int IdentifyBlock::getHighestPIOMode(void) const {
|
||||
|
||||
/* Device class */
|
||||
|
||||
static constexpr int _DMA_TIMEOUT = 10000;
|
||||
|
||||
Device devices[2]{ (DEVICE_PRIMARY), (DEVICE_SECONDARY) };
|
||||
|
||||
DeviceError Device::_waitForStatus(
|
||||
uint8_t mask, uint8_t value, int timeout, bool ignoreErrors
|
||||
) {
|
||||
Device::Device(uint32_t flags)
|
||||
: flags(flags), capacity(0), lastStatusReg(0), lastErrorReg(0) {
|
||||
util::clear(lastSenseData);
|
||||
}
|
||||
|
||||
void Device::_readPIO(void *data, size_t length) const {
|
||||
length++;
|
||||
length /= 2;
|
||||
|
||||
util::assertAligned<uint16_t>(data);
|
||||
|
||||
auto ptr = reinterpret_cast<uint16_t *>(data);
|
||||
|
||||
for (; length > 0; length--)
|
||||
*(ptr++) = SYS573_IDE_CS0_BASE[CS0_DATA];
|
||||
}
|
||||
|
||||
void Device::_writePIO(const void *data, size_t length) const {
|
||||
length++;
|
||||
length /= 2;
|
||||
|
||||
util::assertAligned<uint16_t>(data);
|
||||
|
||||
auto ptr = reinterpret_cast<const uint16_t *>(data);
|
||||
|
||||
for (; length > 0; length--)
|
||||
SYS573_IDE_CS0_BASE[CS0_DATA] = *(ptr++);
|
||||
}
|
||||
|
||||
bool Device::_readDMA(void *data, size_t length) const {
|
||||
length += 3;
|
||||
length /= 4;
|
||||
|
||||
util::assertAligned<uint32_t>(data);
|
||||
|
||||
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(data);
|
||||
DMA_BCR (DMA_PIO) = length;
|
||||
DMA_CHCR(DMA_PIO) = 0
|
||||
| DMA_CHCR_READ
|
||||
| DMA_CHCR_MODE_BURST
|
||||
| DMA_CHCR_ENABLE
|
||||
| DMA_CHCR_TRIGGER;
|
||||
|
||||
return waitForDMATransfer(DMA_PIO, _DMA_TIMEOUT);
|
||||
}
|
||||
|
||||
bool Device::_writeDMA(const void *data, size_t length) const {
|
||||
length += 3;
|
||||
length /= 4;
|
||||
|
||||
util::assertAligned<uint32_t>(data);
|
||||
|
||||
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(data);
|
||||
DMA_BCR (DMA_PIO) = length;
|
||||
DMA_CHCR(DMA_PIO) = 0
|
||||
| DMA_CHCR_WRITE
|
||||
| DMA_CHCR_MODE_BURST
|
||||
| DMA_CHCR_ENABLE
|
||||
| DMA_CHCR_TRIGGER;
|
||||
|
||||
return waitForDMATransfer(DMA_PIO, _DMA_TIMEOUT);
|
||||
}
|
||||
|
||||
/* Status and error polling */
|
||||
|
||||
static constexpr int _COMMAND_TIMEOUT = 30000000;
|
||||
static constexpr int _DRQ_TIMEOUT = 30000000;
|
||||
static constexpr int _DETECT_TIMEOUT = 500000;
|
||||
static constexpr int _SRST_DELAY = 5000;
|
||||
|
||||
// Note that ATA drives will always assert DRDY when ready, but ATAPI drives
|
||||
// will not. This is an intentional feature meant to prevent ATA-only drivers
|
||||
// from misdetecting ATAPI drives.
|
||||
DeviceError Device::_waitForIdle(bool drdy, int timeout, bool ignoreError) {
|
||||
if (!timeout)
|
||||
timeout = _COMMAND_TIMEOUT;
|
||||
|
||||
for (; timeout > 0; timeout -= 10) {
|
||||
uint8_t status = _read(CS0_STATUS);
|
||||
auto status = _read(CS0_STATUS);
|
||||
|
||||
if (!ignoreErrors && (status & CS0_STATUS_ERR)) {
|
||||
LOG(
|
||||
"drive %d error, stat=0x%02x, err=0x%02x",
|
||||
(flags / DEVICE_SECONDARY) & 1, _read(CS0_STATUS),
|
||||
_read(CS0_ERROR)
|
||||
);
|
||||
// Only check for errors *after* BSY is cleared.
|
||||
if (!(status & CS0_STATUS_BSY)) {
|
||||
if ((status & CS0_STATUS_ERR) && !ignoreError) {
|
||||
_handleError();
|
||||
return DRIVE_ERROR;
|
||||
}
|
||||
|
||||
_write(CS0_COMMAND, ATA_DEVICE_RESET);
|
||||
return DRIVE_ERROR;
|
||||
if ((status & CS0_STATUS_DRDY) || !drdy)
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
if ((status & mask) == value)
|
||||
delayMicroseconds(10);
|
||||
#ifndef ENABLE_FULL_IDE_DRIVER
|
||||
io::clearWatchdog();
|
||||
#endif
|
||||
}
|
||||
|
||||
LOG("timeout, ignore=%d", ignoreError);
|
||||
_handleTimeout();
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
DeviceError Device::_waitForDRQ(int timeout, bool ignoreError) {
|
||||
if (!timeout)
|
||||
timeout = _DRQ_TIMEOUT;
|
||||
|
||||
for (; timeout > 0; timeout -= 10) {
|
||||
auto status = _read(CS0_STATUS);
|
||||
|
||||
// Check for errors *before* DRQ is set but *after* BSY is cleared.
|
||||
// Confused yet?
|
||||
if (!(status & CS0_STATUS_BSY)) {
|
||||
if ((status & CS0_STATUS_ERR) && !ignoreError) {
|
||||
_handleError();
|
||||
return DRIVE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (status & CS0_STATUS_DRQ)
|
||||
return NO_ERROR;
|
||||
|
||||
delayMicroseconds(10);
|
||||
@ -153,66 +291,48 @@ DeviceError Device::_waitForStatus(
|
||||
#endif
|
||||
}
|
||||
|
||||
LOG(
|
||||
"drive %d timeout, stat=0x%02x, err=0x%02x",
|
||||
(flags / DEVICE_SECONDARY) & 1, _read(CS0_STATUS), _read(CS0_ERROR)
|
||||
);
|
||||
|
||||
_write(CS0_COMMAND, ATA_DEVICE_RESET);
|
||||
LOG("timeout, ignore=%d", ignoreError);
|
||||
_handleTimeout();
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
DeviceError Device::_select(
|
||||
uint8_t devSelFlags, int timeout, bool ignoreErrors
|
||||
) {
|
||||
if (flags & DEVICE_SECONDARY)
|
||||
_write(CS0_DEVICE_SEL, devSelFlags | CS0_DEVICE_SEL_SECONDARY);
|
||||
else
|
||||
_write(CS0_DEVICE_SEL, devSelFlags | CS0_DEVICE_SEL_PRIMARY);
|
||||
void Device::_handleError(void) {
|
||||
lastStatusReg = _read(CS0_STATUS);
|
||||
lastErrorReg = _read(CS0_ERROR);
|
||||
|
||||
return _waitForStatus(CS0_STATUS_BSY, 0, timeout, ignoreErrors);
|
||||
LOG(
|
||||
"drive=%d, st=0x%02x, err=0x%02x", (flags / DEVICE_SECONDARY) & 1,
|
||||
lastStatusReg, lastErrorReg
|
||||
);
|
||||
|
||||
// Issuing a device reset command to an ATAPI drive would result in the
|
||||
// error's sense data being lost.
|
||||
if (!(flags & DEVICE_ATAPI))
|
||||
_write(CS0_COMMAND, ATA_DEVICE_RESET);
|
||||
}
|
||||
|
||||
DeviceError Device::_setLBA(uint64_t lba, size_t count, int timeout) {
|
||||
if (flags & DEVICE_HAS_LBA48) {
|
||||
//assert(lba < (1ULL << 48));
|
||||
//assert(count <= (1 << 16));
|
||||
void Device::_handleTimeout(void) {
|
||||
lastStatusReg = _read(CS0_STATUS);
|
||||
lastErrorReg = _read(CS0_ERROR);
|
||||
|
||||
auto error = _select(CS0_DEVICE_SEL_LBA, timeout);
|
||||
LOG(
|
||||
"drive=%d, st=0x%02x, err=0x%02x", (flags / DEVICE_SECONDARY) & 1,
|
||||
lastStatusReg, lastErrorReg
|
||||
);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
_write(CS0_COUNT, (count >> 8) & 0xff);
|
||||
_write(CS0_SECTOR, (lba >> 24) & 0xff);
|
||||
_write(CS0_CYLINDER_L, (lba >> 32) & 0xff);
|
||||
_write(CS0_CYLINDER_H, (lba >> 40) & 0xff);
|
||||
} else {
|
||||
//assert(lba < (1ULL << 28));
|
||||
//assert(count <= (1 << 8));
|
||||
|
||||
auto error = _select(CS0_DEVICE_SEL_LBA | ((lba >> 24) & 15), timeout);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
_write(CS0_COUNT, (count >> 0) & 0xff);
|
||||
_write(CS0_SECTOR, (lba >> 0) & 0xff);
|
||||
_write(CS0_CYLINDER_L, (lba >> 8) & 0xff);
|
||||
_write(CS0_CYLINDER_H, (lba >> 16) & 0xff);
|
||||
|
||||
return NO_ERROR;
|
||||
_write(CS0_COMMAND, ATA_DEVICE_RESET);
|
||||
}
|
||||
|
||||
DeviceError Device::_detectDrive(void) {
|
||||
DeviceError Device::_resetDrive(void) {
|
||||
// Issue a software reset, which affects both devices on the bus.
|
||||
_write(CS1_DEVICE_CTRL, CS1_DEVICE_CTRL_IEN | CS1_DEVICE_CTRL_SRST);
|
||||
delayMicroseconds(_SRST_DELAY);
|
||||
_write(CS1_DEVICE_CTRL, CS1_DEVICE_CTRL_IEN);
|
||||
delayMicroseconds(_SRST_DELAY);
|
||||
|
||||
if (_select(0, _DETECT_DRIVE_TIMEOUT, true)) {
|
||||
_select(0);
|
||||
|
||||
if (_waitForIdle(false, _DETECT_TIMEOUT, true)) {
|
||||
LOG("drive %d select timeout", (flags / DEVICE_SECONDARY) & 1);
|
||||
return NO_DRIVE;
|
||||
}
|
||||
@ -225,129 +345,62 @@ DeviceError Device::_detectDrive(void) {
|
||||
// the written value. This should not fail even if the drive is busy.
|
||||
uint8_t pattern = 0x55;
|
||||
|
||||
for (int timeout = _DETECT_DRIVE_TIMEOUT; timeout > 0; timeout -= 100) {
|
||||
for (int timeout = _DETECT_TIMEOUT; timeout > 0; timeout -= 10) {
|
||||
_write(CS0_COUNT, pattern);
|
||||
|
||||
// Note that ATA drives will also assert DRDY when ready, but ATAPI
|
||||
// drives will not.
|
||||
if (_read(CS0_COUNT) == pattern)
|
||||
return _waitForStatus(CS0_STATUS_BSY, 0);
|
||||
if (_read(CS0_COUNT) == pattern) {
|
||||
_write(CS0_COUNT, 0);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
pattern >>= 1;
|
||||
if (!(pattern & 1))
|
||||
pattern |= 1 << 7;
|
||||
pattern ^= 0xff;
|
||||
|
||||
delayMicroseconds(100);
|
||||
delayMicroseconds(10);
|
||||
#ifndef ENABLE_FULL_IDE_DRIVER
|
||||
io::clearWatchdog();
|
||||
#endif
|
||||
}
|
||||
|
||||
LOG("drive %d not found", (flags / DEVICE_SECONDARY) & 1);
|
||||
LOG("drive %d not responding", (flags / DEVICE_SECONDARY) & 1);
|
||||
return NO_DRIVE;
|
||||
}
|
||||
|
||||
DeviceError Device::_readPIO(
|
||||
void *data, size_t length, int timeout, bool ignoreErrors
|
||||
) {
|
||||
util::assertAligned<uint16_t>(data);
|
||||
/* ATA-specific function */
|
||||
|
||||
auto error = _waitForStatus(
|
||||
CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout
|
||||
);
|
||||
DeviceError Device::_ataSetLBA(uint64_t lba, size_t count, int timeout) {
|
||||
if (flags & DEVICE_HAS_LBA48) {
|
||||
//assert(lba < (1ULL << 48));
|
||||
//assert(count <= (1 << 16));
|
||||
_select(CS0_DEVICE_SEL_LBA);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
auto error = _waitForIdle(true);
|
||||
|
||||
auto ptr = reinterpret_cast<uint16_t *>(data);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
for (; length; length -= 2)
|
||||
*(ptr++) = SYS573_IDE_CS0_BASE[CS0_DATA];
|
||||
_write(CS0_COUNT, (count >> 8) & 0xff);
|
||||
_write(CS0_SECTOR, (lba >> 24) & 0xff);
|
||||
_write(CS0_CYLINDER_L, (lba >> 32) & 0xff);
|
||||
_write(CS0_CYLINDER_H, (lba >> 40) & 0xff);
|
||||
} else {
|
||||
//assert(lba < (1ULL << 28));
|
||||
//assert(count <= (1 << 8));
|
||||
_select(CS0_DEVICE_SEL_LBA | ((lba >> 24) & 15));
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
auto error = _waitForIdle(true);
|
||||
|
||||
DeviceError Device::_writePIO(
|
||||
const void *data, size_t length, int timeout, bool ignoreErrors
|
||||
) {
|
||||
util::assertAligned<uint16_t>(data);
|
||||
|
||||
auto error = _waitForStatus(
|
||||
CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout
|
||||
);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
auto ptr = reinterpret_cast<const uint16_t *>(data);
|
||||
|
||||
for (; length; length -= 2)
|
||||
SYS573_IDE_CS0_BASE[CS0_DATA] = *(ptr++);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
DeviceError Device::_readDMA(
|
||||
void *data, size_t length, int timeout, bool ignoreErrors
|
||||
) {
|
||||
length /= 4;
|
||||
|
||||
util::assertAligned<uint32_t>(data);
|
||||
|
||||
auto error = _waitForStatus(
|
||||
CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout
|
||||
);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(data);
|
||||
DMA_BCR (DMA_PIO) = length;
|
||||
DMA_CHCR(DMA_PIO) = 0
|
||||
| DMA_CHCR_READ
|
||||
| DMA_CHCR_MODE_BURST
|
||||
| DMA_CHCR_ENABLE
|
||||
| DMA_CHCR_TRIGGER;
|
||||
|
||||
if (!waitForDMATransfer(DMA_PIO, _DMA_TIMEOUT)) {
|
||||
LOG("DMA transfer timeout");
|
||||
return INCOMPLETE_DATA;
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
_write(CS0_COUNT, (count >> 0) & 0xff);
|
||||
_write(CS0_SECTOR, (lba >> 0) & 0xff);
|
||||
_write(CS0_CYLINDER_L, (lba >> 8) & 0xff);
|
||||
_write(CS0_CYLINDER_H, (lba >> 16) & 0xff);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
DeviceError Device::_writeDMA(
|
||||
const void *data, size_t length, int timeout, bool ignoreErrors
|
||||
) {
|
||||
length /= 4;
|
||||
|
||||
util::assertAligned<uint32_t>(data);
|
||||
|
||||
auto error = _waitForStatus(
|
||||
CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout
|
||||
);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(data);
|
||||
DMA_BCR (DMA_PIO) = length;
|
||||
DMA_CHCR(DMA_PIO) = 0
|
||||
| DMA_CHCR_WRITE
|
||||
| DMA_CHCR_MODE_BURST
|
||||
| DMA_CHCR_ENABLE
|
||||
| DMA_CHCR_TRIGGER;
|
||||
|
||||
if (!waitForDMATransfer(DMA_PIO, _DMA_TIMEOUT)) {
|
||||
LOG("DMA transfer timeout");
|
||||
return INCOMPLETE_DATA;
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
DeviceError Device::_ideReadWrite(
|
||||
DeviceError Device::_ataTransfer(
|
||||
uintptr_t ptr, uint64_t lba, size_t count, bool write
|
||||
) {
|
||||
uint8_t cmd;
|
||||
@ -364,7 +417,7 @@ DeviceError Device::_ideReadWrite(
|
||||
while (count) {
|
||||
size_t chunkLength = util::min(count, maxLength);
|
||||
|
||||
auto error = _setLBA(lba, chunkLength);
|
||||
auto error = _ataSetLBA(lba, chunkLength);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
@ -373,41 +426,125 @@ DeviceError Device::_ideReadWrite(
|
||||
|
||||
// Data must be transferred one sector at a time as the drive may
|
||||
// deassert DRQ between sectors.
|
||||
for (size_t i = chunkLength; i; i--) {
|
||||
if (write)
|
||||
error = _writePIO(
|
||||
reinterpret_cast<const void *>(ptr), ATA_SECTOR_SIZE
|
||||
);
|
||||
else
|
||||
error = _readPIO(
|
||||
reinterpret_cast<void *>(ptr), ATA_SECTOR_SIZE
|
||||
);
|
||||
for (size_t i = chunkLength; i; i--, ptr += ATA_SECTOR_SIZE) {
|
||||
auto error = _waitForDRQ();
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
ptr += ATA_SECTOR_SIZE;
|
||||
if (write)
|
||||
_writePIO(reinterpret_cast<const void *>(ptr), ATA_SECTOR_SIZE);
|
||||
else
|
||||
_readPIO(reinterpret_cast<void *>(ptr), ATA_SECTOR_SIZE);
|
||||
}
|
||||
|
||||
error = _waitForStatus(
|
||||
CS0_STATUS_DRDY | CS0_STATUS_BSY, CS0_STATUS_DRDY
|
||||
);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
lba += chunkLength;
|
||||
count -= chunkLength;
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
return _waitForIdle();
|
||||
}
|
||||
|
||||
/* ATAPI-specific functions */
|
||||
|
||||
static constexpr int _ATAPI_READY_TIMEOUT = 30000000;
|
||||
static constexpr int _REQ_SENSE_TIMEOUT = 500000;
|
||||
|
||||
// ATAPI devices will set the CHK (ERR) status flag whenever new sense data is
|
||||
// available in response to a command. In such cases, the error should be
|
||||
// cleared by sending a "request sense" command.
|
||||
DeviceError Device::_atapiRequestSense(void) {
|
||||
Packet packet;
|
||||
|
||||
packet.setRequestSense();
|
||||
_select(0);
|
||||
|
||||
auto error = _waitForIdle(false, _REQ_SENSE_TIMEOUT, true);
|
||||
|
||||
if (!error) {
|
||||
_setCylinder(sizeof(SenseData));
|
||||
_write(CS0_COMMAND, ATA_PACKET);
|
||||
|
||||
error = _waitForDRQ(_REQ_SENSE_TIMEOUT, true);
|
||||
}
|
||||
if (!error) {
|
||||
_writePIO(&packet, getPacketSize());
|
||||
|
||||
error = _waitForDRQ(_REQ_SENSE_TIMEOUT, true);
|
||||
}
|
||||
if (!error) {
|
||||
_readPIO(&lastSenseData, sizeof(SenseData));
|
||||
LOG("sense data ok");
|
||||
} else {
|
||||
// If the request sense command fails, fall back to reading the sense
|
||||
// key from the error register.
|
||||
util::clear(lastSenseData);
|
||||
|
||||
lastSenseData.senseKey = lastErrorReg >> 4;
|
||||
LOG("%s", DEVICE_ERROR_NAMES[error]);
|
||||
}
|
||||
|
||||
return _senseDataToError(lastSenseData);
|
||||
}
|
||||
|
||||
DeviceError Device::_atapiPacket(const Packet &packet, size_t dataLength) {
|
||||
if (!(flags & DEVICE_READY))
|
||||
return NO_DRIVE;
|
||||
if (!(flags & DEVICE_ATAPI))
|
||||
return UNSUPPORTED_OP;
|
||||
|
||||
LOG("cmd=0x%02x, length=0x%x", packet.command, dataLength);
|
||||
|
||||
// Keep resending the command as long as the drive reports it is in progress
|
||||
// of becoming ready (i.e. spinning up).
|
||||
for (int timeout = _ATAPI_READY_TIMEOUT; timeout > 0; timeout -= 10000) {
|
||||
_select(0);
|
||||
|
||||
auto error = _waitForIdle();
|
||||
|
||||
if (!error) {
|
||||
_setCylinder(dataLength);
|
||||
_write(CS0_COMMAND, ATA_PACKET);
|
||||
|
||||
error = _waitForDRQ();
|
||||
}
|
||||
if (!error) {
|
||||
_writePIO(&packet, getPacketSize());
|
||||
|
||||
error = dataLength
|
||||
? _waitForDRQ()
|
||||
: _waitForIdle();
|
||||
}
|
||||
if (!error)
|
||||
return NO_ERROR;
|
||||
|
||||
// If an error occurred, fetch sense data to determine whether to resend
|
||||
// the command.
|
||||
LOG("%s, cmd=0x%02x", DEVICE_ERROR_NAMES[error], packet.command);
|
||||
|
||||
error = _atapiRequestSense();
|
||||
|
||||
if (error && (error != NOT_YET_READY)) {
|
||||
LOG("%s (from sense)", DEVICE_ERROR_NAMES[error]);
|
||||
return error;
|
||||
}
|
||||
|
||||
delayMicroseconds(10000);
|
||||
#ifndef ENABLE_FULL_IDE_DRIVER
|
||||
io::clearWatchdog();
|
||||
#endif
|
||||
}
|
||||
|
||||
LOG("retry timeout, cmd=0x%02x", packet.command);
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
DeviceError Device::_atapiRead(uintptr_t ptr, uint32_t lba, size_t count) {
|
||||
Packet packet;
|
||||
|
||||
packet.setRead(lba, count);
|
||||
auto error = atapiPacket(packet, ATAPI_SECTOR_SIZE);
|
||||
|
||||
auto error = _atapiPacket(packet, ATAPI_SECTOR_SIZE);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
@ -415,19 +552,28 @@ DeviceError Device::_atapiRead(uintptr_t ptr, uint32_t lba, size_t count) {
|
||||
// Data must be transferred one sector at a time as the drive may deassert
|
||||
// DRQ between sectors.
|
||||
for (; count; count--) {
|
||||
if (_readPIO(reinterpret_cast<void *>(ptr), ATAPI_SECTOR_SIZE))
|
||||
return atapiPoll();
|
||||
auto error = _waitForDRQ();
|
||||
|
||||
ptr += ATAPI_SECTOR_SIZE;
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
size_t chunkLength = _getCylinder();
|
||||
|
||||
_readPIO(reinterpret_cast<void *>(ptr), chunkLength);
|
||||
ptr += chunkLength;
|
||||
}
|
||||
|
||||
return _waitForStatus(CS0_STATUS_BSY, 0);
|
||||
return _waitForIdle();
|
||||
}
|
||||
|
||||
/* Public API */
|
||||
|
||||
static constexpr uint16_t _ATAPI_SIGNATURE = 0xeb14;
|
||||
|
||||
DeviceError Device::enumerate(void) {
|
||||
flags &= DEVICE_PRIMARY | DEVICE_SECONDARY;
|
||||
|
||||
auto error = _detectDrive();
|
||||
auto error = _resetDrive();
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
@ -440,17 +586,25 @@ DeviceError Device::enumerate(void) {
|
||||
// to prevent blocking for too long.
|
||||
IdentifyBlock block;
|
||||
|
||||
//_select();
|
||||
|
||||
if ((_read(CS0_CYLINDER_L) == 0x14) && (_read(CS0_CYLINDER_H) == 0xeb)) {
|
||||
if (_getCylinder() == _ATAPI_SIGNATURE) {
|
||||
flags |= DEVICE_ATAPI;
|
||||
|
||||
_write(CS0_COMMAND, ATA_IDENTIFY_PACKET);
|
||||
} else {
|
||||
_write(CS0_COMMAND, ATA_IDENTIFY);
|
||||
}
|
||||
|
||||
if (_readPIO(&block, sizeof(IdentifyBlock), _DETECT_DRIVE_TIMEOUT))
|
||||
return NO_DRIVE;
|
||||
if (!block.validateChecksum())
|
||||
return CHECKSUM_MISMATCH;
|
||||
if (_waitForDRQ(_DETECT_TIMEOUT))
|
||||
return NO_DRIVE;
|
||||
|
||||
_readPIO(&block, sizeof(IdentifyBlock));
|
||||
|
||||
if (!block.validateChecksum())
|
||||
return CHECKSUM_MISMATCH;
|
||||
|
||||
// Parse the identification block.
|
||||
if (flags & DEVICE_ATAPI) {
|
||||
LOG("ATAPI drive found");
|
||||
|
||||
if (
|
||||
(block.deviceFlags & IDENTIFY_DEV_ATAPI_TYPE_BITMASK)
|
||||
@ -463,12 +617,7 @@ DeviceError Device::enumerate(void) {
|
||||
)
|
||||
flags |= DEVICE_HAS_PACKET16;
|
||||
} else {
|
||||
_write(CS0_COMMAND, ATA_IDENTIFY);
|
||||
|
||||
if (_readPIO(&block, sizeof(IdentifyBlock), _DETECT_DRIVE_TIMEOUT))
|
||||
return NO_DRIVE;
|
||||
if (!block.validateChecksum())
|
||||
return CHECKSUM_MISMATCH;
|
||||
LOG("ATA drive found");
|
||||
|
||||
if (block.commandSetFlags[1] & (1 << 10)) {
|
||||
flags |= DEVICE_HAS_LBA48;
|
||||
@ -481,116 +630,42 @@ DeviceError Device::enumerate(void) {
|
||||
}
|
||||
|
||||
#ifdef ENABLE_FULL_IDE_DRIVER
|
||||
_copyString(model, block.model, sizeof(block.model));
|
||||
_copyString(revision, block.revision, sizeof(block.revision));
|
||||
_copyString(model, block.model, sizeof(block.model));
|
||||
_copyString(revision, block.revision, sizeof(block.revision));
|
||||
_copyString(serialNumber, block.serialNumber, sizeof(block.serialNumber));
|
||||
|
||||
LOG("drive %d: %s", (flags / DEVICE_SECONDARY) & 1, model);
|
||||
#endif
|
||||
|
||||
// Find out the fastest PIO transfer mode supported and enable it.
|
||||
int mode = block.getHighestPIOMode();
|
||||
|
||||
_write(CS0_FEATURES, FEATURE_TRANSFER_MODE);
|
||||
_write(CS0_COUNT, (1 << 3) | mode);
|
||||
_write(CS0_COUNT, TRANSFER_MODE_PIO | mode);
|
||||
_write(CS0_COMMAND, ATA_SET_FEATURES);
|
||||
|
||||
error = _waitForStatus(CS0_STATUS_BSY, 0);
|
||||
error = _waitForIdle();
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
LOG("done, stat=0x%02x, mode=PIO%d", _read(CS0_STATUS), mode);
|
||||
LOG("drive %d ready, mode=PIO%d", (flags / DEVICE_SECONDARY) & 1, mode);
|
||||
flags |= DEVICE_READY;
|
||||
return NO_ERROR;
|
||||
|
||||
// Make sure any pending ATAPI sense data is cleared.
|
||||
return poll();
|
||||
}
|
||||
|
||||
DeviceError Device::atapiPacket(const Packet &packet, size_t transferLength) {
|
||||
DeviceError Device::poll(void) {
|
||||
if (!(flags & DEVICE_READY))
|
||||
return NO_DRIVE;
|
||||
if (!(flags & DEVICE_ATAPI))
|
||||
return UNSUPPORTED_OP;
|
||||
|
||||
auto error = _select(0);
|
||||
if (flags & DEVICE_ATAPI) {
|
||||
Packet packet;
|
||||
|
||||
if (error)
|
||||
return atapiPoll();
|
||||
|
||||
_write(CS0_CYLINDER_L, (transferLength >> 0) & 0xff);
|
||||
_write(CS0_CYLINDER_H, (transferLength >> 8) & 0xff);
|
||||
_write(CS0_COMMAND, ATA_PACKET);
|
||||
|
||||
error = _writePIO(&packet, (flags & DEVICE_HAS_PACKET16) ? 16 : 12);
|
||||
|
||||
if (error)
|
||||
return atapiPoll();
|
||||
|
||||
return _waitForStatus(CS0_STATUS_BSY, 0);
|
||||
}
|
||||
|
||||
DeviceError Device::atapiPoll(void) {
|
||||
if (!(flags & DEVICE_READY))
|
||||
return NO_DRIVE;
|
||||
if (!(flags & DEVICE_ATAPI))
|
||||
return UNSUPPORTED_OP;
|
||||
|
||||
Packet packet;
|
||||
SenseData data;
|
||||
|
||||
packet.setRequestSense();
|
||||
|
||||
// If an error occurs, the error flag in the status register will be set but
|
||||
// the drive will still accept a request sense command.
|
||||
auto error = _select(0, _REQUEST_SENSE_TIMEOUT, true);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
_write(CS0_CYLINDER_L, (sizeof(data) >> 0) & 0xff);
|
||||
_write(CS0_CYLINDER_H, (sizeof(data) >> 8) & 0xff);
|
||||
_write(CS0_COMMAND, ATA_PACKET);
|
||||
|
||||
error = _writePIO(
|
||||
&packet, (flags & DEVICE_HAS_PACKET16) ? 16 : 12, _REQUEST_SENSE_TIMEOUT
|
||||
);
|
||||
|
||||
//if (!error)
|
||||
//error = _waitForStatus(CS0_STATUS_BSY, 0, _REQUEST_SENSE_TIMEOUT);
|
||||
if (!error)
|
||||
error = _readPIO(&data, sizeof(data), _REQUEST_SENSE_TIMEOUT);
|
||||
|
||||
int senseKey;
|
||||
|
||||
if (error) {
|
||||
// If the request sense command fails, fall back to reading the sense
|
||||
// key from the IDE error register.
|
||||
senseKey = (_read(CS0_ERROR) >> 4) & 15;
|
||||
LOG("request sense failed");
|
||||
packet.setTestUnitReady();
|
||||
return _atapiPacket(packet);
|
||||
} else {
|
||||
senseKey = data.senseKey & 15;
|
||||
LOG(
|
||||
"key=0x%02x, asc=0x%02x, ascq=0x%02x", data.senseKey, data.asc,
|
||||
data.ascQualifier
|
||||
);
|
||||
}
|
||||
|
||||
LOG("sense key: %s (%d)", _SENSE_KEY_NAMES[senseKey], senseKey);
|
||||
|
||||
switch (senseKey) {
|
||||
case SENSE_KEY_NO_SENSE:
|
||||
case SENSE_KEY_RECOVERED_ERROR:
|
||||
return NO_ERROR;
|
||||
|
||||
case SENSE_KEY_NOT_READY:
|
||||
case SENSE_KEY_MEDIUM_ERROR:
|
||||
case SENSE_KEY_DATA_PROTECT:
|
||||
return DISC_ERROR;
|
||||
|
||||
case SENSE_KEY_UNIT_ATTENTION:
|
||||
return DISC_CHANGED;
|
||||
|
||||
default:
|
||||
return DRIVE_ERROR;
|
||||
_select(CS0_DEVICE_SEL_LBA);
|
||||
return _waitForIdle(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -605,7 +680,7 @@ DeviceError Device::readData(void *data, uint64_t lba, size_t count) {
|
||||
reinterpret_cast<uintptr_t>(data), static_cast<uint32_t>(lba), count
|
||||
);
|
||||
else
|
||||
return _ideReadWrite(
|
||||
return _ataTransfer(
|
||||
reinterpret_cast<uintptr_t>(data), lba, count, false
|
||||
);
|
||||
}
|
||||
@ -618,51 +693,60 @@ DeviceError Device::writeData(const void *data, uint64_t lba, size_t count) {
|
||||
if (flags & (DEVICE_READ_ONLY | DEVICE_ATAPI))
|
||||
return UNSUPPORTED_OP;
|
||||
|
||||
return _ideReadWrite(reinterpret_cast<uintptr_t>(data), lba, count, true);
|
||||
return _ataTransfer(reinterpret_cast<uintptr_t>(data), lba, count, true);
|
||||
}
|
||||
|
||||
DeviceError Device::goIdle(bool standby) {
|
||||
if (!(flags & DEVICE_READY))
|
||||
return NO_DRIVE;
|
||||
if (flags & DEVICE_ATAPI)
|
||||
return startStopUnit(START_STOP_MODE_STOP_DISC);
|
||||
|
||||
if (flags & DEVICE_ATAPI) {
|
||||
Packet packet;
|
||||
_select(CS0_DEVICE_SEL_LBA);
|
||||
|
||||
packet.setStartStopUnit(START_STOP_MODE_STOP_DISC);
|
||||
return atapiPacket(packet);
|
||||
} else {
|
||||
auto error = _select(CS0_DEVICE_SEL_LBA);
|
||||
auto error = _waitForIdle(true);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (standby)
|
||||
_write(CS0_COMMAND, ATA_STANDBY_IMMEDIATE);
|
||||
else
|
||||
_write(CS0_COMMAND, ATA_IDLE_IMMEDIATE);
|
||||
_write(CS0_COMMAND, standby ? ATA_STANDBY_IMMEDIATE : ATA_IDLE_IMMEDIATE);
|
||||
return _waitForIdle();
|
||||
}
|
||||
|
||||
return _waitForStatus(CS0_STATUS_BSY, 0);
|
||||
}
|
||||
DeviceError Device::startStopUnit(ATAPIStartStopMode mode) {
|
||||
if (!(flags & DEVICE_READY))
|
||||
return NO_DRIVE;
|
||||
if (!(flags & DEVICE_ATAPI))
|
||||
return UNSUPPORTED_OP;
|
||||
|
||||
Packet packet;
|
||||
|
||||
packet.setStartStopUnit(mode);
|
||||
return _atapiPacket(packet);
|
||||
}
|
||||
|
||||
DeviceError Device::flushCache(void) {
|
||||
if (!(flags & DEVICE_READY))
|
||||
return NO_DRIVE;
|
||||
if (!(flags & DEVICE_HAS_FLUSH))
|
||||
#if 0
|
||||
return UNSUPPORTED_OP;
|
||||
#else
|
||||
return NO_ERROR;
|
||||
//return UNSUPPORTED_OP;
|
||||
#endif
|
||||
|
||||
auto error = _select(CS0_DEVICE_SEL_LBA);
|
||||
_select(CS0_DEVICE_SEL_LBA);
|
||||
|
||||
auto error = _waitForIdle(true);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (flags & DEVICE_HAS_LBA48)
|
||||
_write(CS0_COMMAND, ATA_FLUSH_CACHE_EXT);
|
||||
else
|
||||
_write(CS0_COMMAND, ATA_FLUSH_CACHE);
|
||||
|
||||
return _waitForStatus(CS0_STATUS_DRDY | CS0_STATUS_BSY, CS0_STATUS_DRDY);
|
||||
_write(
|
||||
CS0_COMMAND,
|
||||
(flags & DEVICE_HAS_LBA48) ? ATA_FLUSH_CACHE_EXT : ATA_FLUSH_CACHE
|
||||
);
|
||||
return _waitForIdle();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,169 +3,15 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/idedefs.hpp"
|
||||
#include "common/util.hpp"
|
||||
#include "ps1/registers573.h"
|
||||
|
||||
namespace ide {
|
||||
|
||||
/* Register definitions */
|
||||
|
||||
enum CS0Register {
|
||||
CS0_DATA = 0,
|
||||
CS0_ERROR = 1,
|
||||
CS0_FEATURES = 1,
|
||||
CS0_COUNT = 2,
|
||||
CS0_SECTOR = 3,
|
||||
CS0_CYLINDER_L = 4,
|
||||
CS0_CYLINDER_H = 5,
|
||||
CS0_DEVICE_SEL = 6,
|
||||
CS0_STATUS = 7,
|
||||
CS0_COMMAND = 7
|
||||
};
|
||||
|
||||
enum CS1Register {
|
||||
CS1_ALT_STATUS = 6,
|
||||
CS1_DEVICE_CTRL = 6
|
||||
};
|
||||
|
||||
enum CS0StatusFlag : uint8_t {
|
||||
CS0_STATUS_ERR = 1 << 0, // Error
|
||||
CS0_STATUS_DRQ = 1 << 3, // Data request
|
||||
CS0_STATUS_DSC = 1 << 4, // Device seek complete (ATA)
|
||||
CS0_STATUS_SERV = 1 << 4, // Service (ATAPI)
|
||||
CS0_STATUS_DF = 1 << 5, // Device fault
|
||||
CS0_STATUS_DRDY = 1 << 6, // Device ready
|
||||
CS0_STATUS_BSY = 1 << 7 // Busy
|
||||
};
|
||||
|
||||
enum CS0DeviceSelectFlag : uint8_t {
|
||||
CS0_DEVICE_SEL_PRIMARY = 10 << 4,
|
||||
CS0_DEVICE_SEL_SECONDARY = 11 << 4,
|
||||
CS0_DEVICE_SEL_LBA = 1 << 6
|
||||
};
|
||||
|
||||
enum CS1DeviceControlFlag : uint8_t {
|
||||
CS1_DEVICE_CTRL_IEN = 1 << 1, // Interrupt enable
|
||||
CS1_DEVICE_CTRL_SRST = 1 << 2, // Software reset
|
||||
CS1_DEVICE_CTRL_HOB = 1 << 7 // High-order bit (LBA48)
|
||||
};
|
||||
|
||||
/* ATA protocol definitions */
|
||||
|
||||
static constexpr size_t ATA_SECTOR_SIZE = 512;
|
||||
|
||||
enum ATACommand : uint8_t {
|
||||
ATA_NOP = 0x00,
|
||||
ATA_DEVICE_RESET = 0x08,
|
||||
ATA_READ_SECTORS = 0x20,
|
||||
ATA_READ_SECTORS_EXT = 0x24,
|
||||
ATA_READ_DMA_EXT = 0x25,
|
||||
ATA_READ_DMA_QUEUED_EXT = 0x26,
|
||||
ATA_WRITE_SECTORS = 0x30,
|
||||
ATA_WRITE_SECTORS_EXT = 0x34,
|
||||
ATA_WRITE_DMA_EXT = 0x35,
|
||||
ATA_WRITE_DMA_QUEUED_EXT = 0x36,
|
||||
ATA_SEEK = 0x70,
|
||||
ATA_EXECUTE_DIAGNOSTIC = 0x90,
|
||||
ATA_PACKET = 0xa0,
|
||||
ATA_IDENTIFY_PACKET = 0xa1,
|
||||
ATA_SERVICE = 0xa2,
|
||||
ATA_DEVICE_CONFIG = 0xb1,
|
||||
ATA_ERASE_SECTORS = 0xc0,
|
||||
ATA_READ_DMA_QUEUED = 0xc7,
|
||||
ATA_READ_DMA = 0xc8,
|
||||
ATA_WRITE_DMA = 0xca,
|
||||
ATA_WRITE_DMA_QUEUED = 0xcc,
|
||||
ATA_STANDBY_IMMEDIATE = 0xe0,
|
||||
ATA_IDLE_IMMEDIATE = 0xe1,
|
||||
ATA_STANDBY = 0xe2,
|
||||
ATA_IDLE = 0xe3,
|
||||
ATA_CHECK_POWER_MODE = 0xe5,
|
||||
ATA_SLEEP = 0xe6,
|
||||
ATA_FLUSH_CACHE = 0xe7,
|
||||
ATA_FLUSH_CACHE_EXT = 0xea,
|
||||
ATA_IDENTIFY = 0xec,
|
||||
ATA_SET_FEATURES = 0xef
|
||||
};
|
||||
|
||||
enum ATAFeature : uint8_t {
|
||||
FEATURE_8BIT_DATA = 0x01,
|
||||
FEATURE_WRITE_CACHE = 0x02,
|
||||
FEATURE_TRANSFER_MODE = 0x03,
|
||||
FEATURE_APM = 0x05,
|
||||
FEATURE_AAM = 0x42,
|
||||
FEATURE_RELEASE_IRQ = 0x5d,
|
||||
FEATURE_SERVICE_IRQ = 0x5e,
|
||||
FEATURE_DISABLE = 0x80
|
||||
};
|
||||
|
||||
/* ATAPI protocol definitions */
|
||||
|
||||
static constexpr size_t ATA_SECTOR_SIZE = 512;
|
||||
static constexpr size_t ATAPI_SECTOR_SIZE = 2048;
|
||||
|
||||
enum ATAPICommand : uint8_t {
|
||||
ATAPI_TEST_UNIT_READY = 0x00,
|
||||
ATAPI_REQUEST_SENSE = 0x03,
|
||||
ATAPI_INQUIRY = 0x12,
|
||||
ATAPI_START_STOP_UNIT = 0x1b,
|
||||
ATAPI_PREVENT_REMOVAL = 0x1e,
|
||||
ATAPI_READ_CAPACITY = 0x25,
|
||||
ATAPI_READ10 = 0x28,
|
||||
ATAPI_SEEK = 0x2b,
|
||||
ATAPI_READ_SUBCHANNEL = 0x42,
|
||||
ATAPI_READ_TOC = 0x43,
|
||||
ATAPI_READ_HEADER = 0x44,
|
||||
ATAPI_PLAY_AUDIO = 0x45,
|
||||
ATAPI_PLAY_AUDIO_MSF = 0x47,
|
||||
ATAPI_PAUSE_RESUME = 0x4b,
|
||||
ATAPI_STOP = 0x4e,
|
||||
ATAPI_MODE_SELECT = 0x55,
|
||||
ATAPI_MODE_SENSE = 0x5a,
|
||||
ATAPI_LOAD_UNLOAD_CD = 0xa6,
|
||||
ATAPI_READ12 = 0xa8,
|
||||
ATAPI_READ_CD_MSF = 0xb9,
|
||||
ATAPI_SCAN = 0xba,
|
||||
ATAPI_SET_CD_SPEED = 0xbb,
|
||||
ATAPI_MECHANISM_STATUS = 0xbd,
|
||||
ATAPI_READ_CD = 0xbe
|
||||
};
|
||||
|
||||
enum ATAPISenseKey : uint8_t {
|
||||
SENSE_KEY_NO_SENSE = 0x0,
|
||||
SENSE_KEY_RECOVERED_ERROR = 0x1,
|
||||
SENSE_KEY_NOT_READY = 0x2,
|
||||
SENSE_KEY_MEDIUM_ERROR = 0x3,
|
||||
SENSE_KEY_HARDWARE_ERROR = 0x4,
|
||||
SENSE_KEY_ILLEGAL_REQUEST = 0x5,
|
||||
SENSE_KEY_UNIT_ATTENTION = 0x6,
|
||||
SENSE_KEY_DATA_PROTECT = 0x7,
|
||||
SENSE_KEY_BLANK_CHECK = 0x8,
|
||||
SENSE_KEY_ABORTED_COMMAND = 0xb,
|
||||
SENSE_KEY_MISCOMPARE = 0xe
|
||||
};
|
||||
|
||||
enum ATAPIStartStopMode : uint8_t {
|
||||
START_STOP_MODE_STOP_DISC = 0,
|
||||
START_STOP_MODE_START_DISC = 1,
|
||||
START_STOP_MODE_OPEN_TRAY = 2,
|
||||
START_STOP_MODE_CLOSE_TRAY = 3
|
||||
};
|
||||
|
||||
enum ATAPIModePage : uint8_t {
|
||||
MODE_PAGE_ERROR_RECOVERY = 0x01,
|
||||
MODE_PAGE_CDROM = 0x0d,
|
||||
MODE_PAGE_CDROM_AUDIO = 0x0e,
|
||||
MODE_PAGE_CDROM_CAPABILITIES = 0x2a,
|
||||
MODE_PAGE_ALL = 0x3f
|
||||
};
|
||||
|
||||
enum ATAPIModePageType : uint8_t {
|
||||
MODE_PAGE_TYPE_CURRENT = 0,
|
||||
MODE_PAGE_TYPE_CHANGEABLE = 1,
|
||||
MODE_PAGE_TYPE_DEFAULT = 2,
|
||||
MODE_PAGE_TYPE_SAVED = 3
|
||||
};
|
||||
|
||||
/* Identification blocks */
|
||||
|
||||
enum IdentifyDeviceFlag : uint16_t {
|
||||
@ -275,6 +121,9 @@ public:
|
||||
| (info[2] << 8)
|
||||
| (info[3] << 0);
|
||||
}
|
||||
inline uint16_t getPackedASC(void) const {
|
||||
return asc | (ascQualifier << 8);
|
||||
}
|
||||
};
|
||||
|
||||
class Packet {
|
||||
@ -283,12 +132,34 @@ public:
|
||||
uint8_t param[11];
|
||||
uint8_t _reserved[4];
|
||||
|
||||
inline void setTestUnitReady(void) {
|
||||
util::clear(*this);
|
||||
|
||||
//command = ATAPI_TEST_UNIT_READY;
|
||||
}
|
||||
inline void setRequestSense(uint8_t additionalLength = 0) {
|
||||
util::clear(*this);
|
||||
|
||||
command = ATAPI_REQUEST_SENSE;
|
||||
param[3] = sizeof(SenseData) + additionalLength;
|
||||
}
|
||||
inline void setStartStopUnit(ATAPIStartStopMode mode) {
|
||||
util::clear(*this);
|
||||
|
||||
command = ATAPI_START_STOP_UNIT;
|
||||
param[3] = mode;
|
||||
}
|
||||
inline void setModeSense(
|
||||
ATAPIModePage page, size_t length,
|
||||
ATAPIModePageType type = MODE_PAGE_TYPE_CURRENT
|
||||
) {
|
||||
util::clear(*this);
|
||||
|
||||
command = ATAPI_MODE_SENSE;
|
||||
param[1] = (page & 0x3f) | (type << 6);
|
||||
param[6] = (length >> 8) & 0xff;
|
||||
param[7] = (length >> 0) & 0xff;
|
||||
}
|
||||
inline void setRead(uint32_t lba, size_t count) {
|
||||
util::clear(*this);
|
||||
|
||||
@ -309,23 +180,6 @@ public:
|
||||
param[1] = (value >> 8) & 0xff;
|
||||
param[2] = (value >> 0) & 0xff;
|
||||
}
|
||||
inline void setRequestSense(uint8_t additionalLength = 0) {
|
||||
util::clear(*this);
|
||||
|
||||
command = ATAPI_REQUEST_SENSE;
|
||||
param[3] = sizeof(SenseData) + additionalLength;
|
||||
}
|
||||
inline void setModeSense(
|
||||
ATAPIModePage page, size_t length,
|
||||
ATAPIModePageType type = MODE_PAGE_TYPE_CURRENT
|
||||
) {
|
||||
util::clear(*this);
|
||||
|
||||
command = ATAPI_MODE_SENSE;
|
||||
param[1] = (page & 0x3f) | (type << 6);
|
||||
param[6] = (length >> 8) & 0xff;
|
||||
param[7] = (length >> 0) & 0xff;
|
||||
}
|
||||
};
|
||||
|
||||
/* Device class */
|
||||
@ -334,12 +188,12 @@ enum DeviceError {
|
||||
NO_ERROR = 0,
|
||||
UNSUPPORTED_OP = 1,
|
||||
NO_DRIVE = 2,
|
||||
STATUS_TIMEOUT = 3,
|
||||
DRIVE_ERROR = 4,
|
||||
DISC_ERROR = 5,
|
||||
DISC_CHANGED = 6,
|
||||
INCOMPLETE_DATA = 7,
|
||||
CHECKSUM_MISMATCH = 8
|
||||
NOT_YET_READY = 3,
|
||||
STATUS_TIMEOUT = 4,
|
||||
CHECKSUM_MISMATCH = 5,
|
||||
DRIVE_ERROR = 6,
|
||||
DISC_ERROR = 7,
|
||||
DISC_CHANGED = 8
|
||||
};
|
||||
|
||||
enum DeviceFlag {
|
||||
@ -357,46 +211,53 @@ enum DeviceFlag {
|
||||
|
||||
class Device {
|
||||
private:
|
||||
inline uint8_t _read(CS0Register reg) {
|
||||
inline uint8_t _read(CS0Register reg) const {
|
||||
return uint8_t(SYS573_IDE_CS0_BASE[reg] & 0xff);
|
||||
}
|
||||
inline void _write(CS0Register reg, uint8_t value) {
|
||||
inline void _write(CS0Register reg, uint8_t value) const {
|
||||
SYS573_IDE_CS0_BASE[reg] = value;
|
||||
}
|
||||
inline uint8_t _read(CS1Register reg) {
|
||||
inline uint8_t _read(CS1Register reg) const {
|
||||
return uint8_t(SYS573_IDE_CS1_BASE[reg] & 0xff);
|
||||
}
|
||||
inline void _write(CS1Register reg, uint8_t value) {
|
||||
inline void _write(CS1Register reg, uint8_t value) const {
|
||||
SYS573_IDE_CS1_BASE[reg] = value;
|
||||
}
|
||||
|
||||
DeviceError _waitForStatus(
|
||||
uint8_t mask, uint8_t value, int timeout = 0, bool ignoreErrors = false
|
||||
);
|
||||
DeviceError _select(
|
||||
uint8_t devSelFlags, int timeout = 0, bool ignoreErrors = false
|
||||
);
|
||||
DeviceError _setLBA(uint64_t lba, size_t count, int timeout = 0);
|
||||
DeviceError _detectDrive(void);
|
||||
inline void _select(uint8_t selFlags) const {
|
||||
if (flags & DEVICE_SECONDARY)
|
||||
_write(CS0_DEVICE_SEL, selFlags | CS0_DEVICE_SEL_SECONDARY);
|
||||
else
|
||||
_write(CS0_DEVICE_SEL, selFlags | CS0_DEVICE_SEL_PRIMARY);
|
||||
}
|
||||
inline void _setCylinder(uint16_t value) const {
|
||||
_write(CS0_CYLINDER_L, (value >> 0) & 0xff);
|
||||
_write(CS0_CYLINDER_H, (value >> 8) & 0xff);
|
||||
}
|
||||
inline uint16_t _getCylinder(void) const {
|
||||
return _read(CS0_CYLINDER_L) | (_read(CS0_CYLINDER_H) << 8);
|
||||
}
|
||||
|
||||
DeviceError _readPIO(
|
||||
void *data, size_t length, int timeout = 0, bool ignoreErrors = false
|
||||
);
|
||||
DeviceError _writePIO(
|
||||
const void *data, size_t length, int timeout = 0,
|
||||
bool ignoreErrors = false
|
||||
);
|
||||
DeviceError _readDMA(
|
||||
void *data, size_t length, int timeout = 0, bool ignoreErrors = false
|
||||
);
|
||||
DeviceError _writeDMA(
|
||||
const void *data, size_t length, int timeout = 0,
|
||||
bool ignoreErrors = false
|
||||
);
|
||||
void _readPIO(void *data, size_t length) const;
|
||||
void _writePIO(const void *data, size_t length) const;
|
||||
bool _readDMA(void *data, size_t length) const;
|
||||
bool _writeDMA(const void *data, size_t length) const;
|
||||
|
||||
DeviceError _ideReadWrite(
|
||||
DeviceError _waitForIdle(
|
||||
bool drdy = false, int timeout = 0, bool ignoreError = false
|
||||
);
|
||||
DeviceError _waitForDRQ(int timeout = 0, bool ignoreError = false);
|
||||
void _handleError(void);
|
||||
void _handleTimeout(void);
|
||||
DeviceError _resetDrive(void);
|
||||
|
||||
DeviceError _ataSetLBA(uint64_t lba, size_t count, int timeout = 0);
|
||||
DeviceError _ataTransfer(
|
||||
uintptr_t ptr, uint64_t lba, size_t count, bool write
|
||||
);
|
||||
|
||||
DeviceError _atapiRequestSense(void);
|
||||
DeviceError _atapiPacket(const Packet &packet, size_t dataLength = 0);
|
||||
DeviceError _atapiRead(uintptr_t ptr, uint32_t lba, size_t count);
|
||||
|
||||
public:
|
||||
@ -407,13 +268,16 @@ public:
|
||||
#endif
|
||||
uint64_t capacity;
|
||||
|
||||
inline Device(uint32_t flags)
|
||||
: flags(flags), capacity(0) {}
|
||||
uint8_t lastStatusReg, lastErrorReg;
|
||||
SenseData lastSenseData;
|
||||
|
||||
inline size_t getSectorSize(void) const {
|
||||
return (flags & DEVICE_ATAPI) ? ATAPI_SECTOR_SIZE : ATA_SECTOR_SIZE;
|
||||
}
|
||||
inline bool isPointerAligned(const void *ptr) {
|
||||
inline size_t getPacketSize(void) const {
|
||||
return (flags & DEVICE_HAS_PACKET16) ? 16 : 12;
|
||||
}
|
||||
inline bool isPointerAligned(const void *ptr) const {
|
||||
// DMA transfers require 4-byte alignment, while PIO transfers require
|
||||
// 2-byte alignment.
|
||||
#if 0
|
||||
@ -423,13 +287,14 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
Device(uint32_t flags);
|
||||
DeviceError enumerate(void);
|
||||
DeviceError atapiPacket(const Packet &packet, size_t transferLength = 0);
|
||||
DeviceError atapiPoll(void);
|
||||
DeviceError poll(void);
|
||||
|
||||
DeviceError readData(void *data, uint64_t lba, size_t count);
|
||||
DeviceError writeData(const void *data, uint64_t lba, size_t count);
|
||||
DeviceError goIdle(bool standby = false);
|
||||
DeviceError startStopUnit(ATAPIStartStopMode mode);
|
||||
DeviceError flushCache(void);
|
||||
};
|
||||
|
||||
|
238
src/common/idedefs.hpp
Normal file
238
src/common/idedefs.hpp
Normal file
@ -0,0 +1,238 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace ide {
|
||||
|
||||
/* Register definitions */
|
||||
|
||||
enum CS0Register {
|
||||
CS0_DATA = 0,
|
||||
CS0_ERROR = 1,
|
||||
CS0_FEATURES = 1,
|
||||
CS0_COUNT = 2,
|
||||
CS0_SECTOR = 3,
|
||||
CS0_CYLINDER_L = 4,
|
||||
CS0_CYLINDER_H = 5,
|
||||
CS0_DEVICE_SEL = 6,
|
||||
CS0_STATUS = 7,
|
||||
CS0_COMMAND = 7
|
||||
};
|
||||
|
||||
enum CS1Register {
|
||||
CS1_ALT_STATUS = 6,
|
||||
CS1_DEVICE_CTRL = 6
|
||||
};
|
||||
|
||||
enum CS0StatusFlag : uint8_t {
|
||||
CS0_STATUS_ERR = 1 << 0, // Error (ATA)
|
||||
CS0_STATUS_CHK = 1 << 0, // Check condition (ATAPI)
|
||||
CS0_STATUS_DRQ = 1 << 3, // Data request
|
||||
CS0_STATUS_DSC = 1 << 4, // Device seek complete (ATA)
|
||||
CS0_STATUS_SERV = 1 << 4, // Service (ATAPI)
|
||||
CS0_STATUS_DF = 1 << 5, // Device fault
|
||||
CS0_STATUS_DRDY = 1 << 6, // Device ready
|
||||
CS0_STATUS_BSY = 1 << 7 // Busy
|
||||
};
|
||||
|
||||
enum CS0DeviceSelectFlag : uint8_t {
|
||||
CS0_DEVICE_SEL_PRIMARY = 10 << 4,
|
||||
CS0_DEVICE_SEL_SECONDARY = 11 << 4,
|
||||
CS0_DEVICE_SEL_LBA = 1 << 6
|
||||
};
|
||||
|
||||
enum CS1DeviceControlFlag : uint8_t {
|
||||
CS1_DEVICE_CTRL_IEN = 1 << 1, // Interrupt enable
|
||||
CS1_DEVICE_CTRL_SRST = 1 << 2, // Software reset
|
||||
CS1_DEVICE_CTRL_HOB = 1 << 7 // High-order bit (LBA48)
|
||||
};
|
||||
|
||||
/* ATA command definitions */
|
||||
|
||||
enum ATACommand : uint8_t {
|
||||
ATA_NOP = 0x00,
|
||||
ATA_DEVICE_RESET = 0x08,
|
||||
ATA_READ_SECTORS = 0x20,
|
||||
ATA_READ_SECTORS_EXT = 0x24,
|
||||
ATA_READ_DMA_EXT = 0x25,
|
||||
ATA_READ_DMA_QUEUED_EXT = 0x26,
|
||||
ATA_WRITE_SECTORS = 0x30,
|
||||
ATA_WRITE_SECTORS_EXT = 0x34,
|
||||
ATA_WRITE_DMA_EXT = 0x35,
|
||||
ATA_WRITE_DMA_QUEUED_EXT = 0x36,
|
||||
ATA_SEEK = 0x70,
|
||||
ATA_EXECUTE_DIAGNOSTIC = 0x90,
|
||||
ATA_PACKET = 0xa0,
|
||||
ATA_IDENTIFY_PACKET = 0xa1,
|
||||
ATA_SERVICE = 0xa2,
|
||||
ATA_DEVICE_CONFIG = 0xb1,
|
||||
ATA_ERASE_SECTORS = 0xc0,
|
||||
ATA_READ_DMA_QUEUED = 0xc7,
|
||||
ATA_READ_DMA = 0xc8,
|
||||
ATA_WRITE_DMA = 0xca,
|
||||
ATA_WRITE_DMA_QUEUED = 0xcc,
|
||||
ATA_STANDBY_IMMEDIATE = 0xe0,
|
||||
ATA_IDLE_IMMEDIATE = 0xe1,
|
||||
ATA_STANDBY = 0xe2,
|
||||
ATA_IDLE = 0xe3,
|
||||
ATA_CHECK_POWER_MODE = 0xe5,
|
||||
ATA_SLEEP = 0xe6,
|
||||
ATA_FLUSH_CACHE = 0xe7,
|
||||
ATA_FLUSH_CACHE_EXT = 0xea,
|
||||
ATA_IDENTIFY = 0xec,
|
||||
ATA_SET_FEATURES = 0xef
|
||||
};
|
||||
|
||||
enum ATAFeature : uint8_t {
|
||||
FEATURE_8BIT_DATA = 0x01,
|
||||
FEATURE_WRITE_CACHE = 0x02,
|
||||
FEATURE_TRANSFER_MODE = 0x03,
|
||||
FEATURE_APM = 0x05,
|
||||
FEATURE_AAM = 0x42,
|
||||
FEATURE_RELEASE_IRQ = 0x5d,
|
||||
FEATURE_SERVICE_IRQ = 0x5e,
|
||||
FEATURE_DISABLE = 0x80
|
||||
};
|
||||
|
||||
enum ATATransferModeFlag : uint8_t {
|
||||
TRANSFER_MODE_PIO_DEFAULT = 0 << 3,
|
||||
TRANSFER_MODE_PIO = 1 << 3,
|
||||
TRANSFER_MODE_DMA = 1 << 5,
|
||||
TRANSFER_MODE_UDMA = 1 << 6
|
||||
};
|
||||
|
||||
/* ATAPI command definitions */
|
||||
|
||||
enum ATAPICommand : uint8_t {
|
||||
ATAPI_TEST_UNIT_READY = 0x00,
|
||||
ATAPI_REQUEST_SENSE = 0x03,
|
||||
ATAPI_INQUIRY = 0x12,
|
||||
ATAPI_START_STOP_UNIT = 0x1b,
|
||||
ATAPI_PREVENT_REMOVAL = 0x1e,
|
||||
ATAPI_READ_CAPACITY = 0x25,
|
||||
ATAPI_READ10 = 0x28,
|
||||
ATAPI_SEEK = 0x2b,
|
||||
ATAPI_READ_SUBCHANNEL = 0x42,
|
||||
ATAPI_READ_TOC = 0x43,
|
||||
ATAPI_READ_HEADER = 0x44,
|
||||
ATAPI_PLAY_AUDIO = 0x45,
|
||||
ATAPI_PLAY_AUDIO_MSF = 0x47,
|
||||
ATAPI_PAUSE_RESUME = 0x4b,
|
||||
ATAPI_STOP = 0x4e,
|
||||
ATAPI_MODE_SELECT = 0x55,
|
||||
ATAPI_MODE_SENSE = 0x5a,
|
||||
ATAPI_LOAD_UNLOAD_CD = 0xa6,
|
||||
ATAPI_READ12 = 0xa8,
|
||||
ATAPI_READ_CD_MSF = 0xb9,
|
||||
ATAPI_SCAN = 0xba,
|
||||
ATAPI_SET_CD_SPEED = 0xbb,
|
||||
ATAPI_MECHANISM_STATUS = 0xbd,
|
||||
ATAPI_READ_CD = 0xbe
|
||||
};
|
||||
|
||||
enum ATAPIModePage : uint8_t {
|
||||
MODE_PAGE_ERROR_RECOVERY = 0x01,
|
||||
MODE_PAGE_CDROM = 0x0d,
|
||||
MODE_PAGE_CDROM_AUDIO = 0x0e,
|
||||
MODE_PAGE_CDROM_CAPABILITIES = 0x2a,
|
||||
MODE_PAGE_ALL = 0x3f
|
||||
};
|
||||
|
||||
enum ATAPIModePageType : uint8_t {
|
||||
MODE_PAGE_TYPE_CURRENT = 0,
|
||||
MODE_PAGE_TYPE_CHANGEABLE = 1,
|
||||
MODE_PAGE_TYPE_DEFAULT = 2,
|
||||
MODE_PAGE_TYPE_SAVED = 3
|
||||
};
|
||||
|
||||
enum ATAPIStartStopMode : uint8_t {
|
||||
START_STOP_MODE_STOP_DISC = 0,
|
||||
START_STOP_MODE_START_DISC = 1,
|
||||
START_STOP_MODE_OPEN_TRAY = 2,
|
||||
START_STOP_MODE_CLOSE_TRAY = 3
|
||||
};
|
||||
|
||||
/* ATAPI sense keys */
|
||||
|
||||
enum ATAPISenseKey : uint8_t {
|
||||
SENSE_KEY_NO_SENSE = 0x0,
|
||||
SENSE_KEY_RECOVERED_ERROR = 0x1,
|
||||
SENSE_KEY_NOT_READY = 0x2,
|
||||
SENSE_KEY_MEDIUM_ERROR = 0x3,
|
||||
SENSE_KEY_HARDWARE_ERROR = 0x4,
|
||||
SENSE_KEY_ILLEGAL_REQUEST = 0x5,
|
||||
SENSE_KEY_UNIT_ATTENTION = 0x6,
|
||||
SENSE_KEY_DATA_PROTECT = 0x7,
|
||||
SENSE_KEY_BLANK_CHECK = 0x8,
|
||||
SENSE_KEY_ABORTED_COMMAND = 0xb,
|
||||
SENSE_KEY_MISCOMPARE = 0xe
|
||||
};
|
||||
|
||||
enum ATAPISenseQualifier : uint16_t {
|
||||
ASC_NO_SENSE_INFO = 0x00 | (0x00 << 8), // "NO ADDITIONAL SENSE INFORMATION"
|
||||
ASC_PLAY_IN_PROGRESS = 0x00 | (0x11 << 8), // "PLAY OPERATION IN PROGRESS"
|
||||
ASC_PLAY_PAUSED = 0x00 | (0x12 << 8), // "PLAY OPERATION PAUSED"
|
||||
ASC_PLAY_COMPLETED = 0x00 | (0x13 << 8), // "PLAY OPERATION SUCCESSFULLY COMPLETED"
|
||||
ASC_PLAY_ERROR = 0x00 | (0x14 << 8), // "PLAY OPERATION STOPPED DUE TO ERROR"
|
||||
ASC_NO_AUDIO_STATUS = 0x00 | (0x15 << 8), // "NO CURRENT AUDIO STATUS TO RETURN"
|
||||
ASC_MECHANICAL_ERROR = 0x01 | (0x00 << 8), // "MECHANICAL POSITIONING OR CHANGER ERROR"
|
||||
ASC_NO_SEEK_COMPLETE = 0x02 | (0x00 << 8), // "NO SEEK COMPLETE"
|
||||
ASC_NOT_READY = 0x04 | (0x00 << 8), // "LOGICAL DRIVE NOT READY - CAUSE NOT REPORTABLE"
|
||||
ASC_NOT_READY_IN_PROGRESS = 0x04 | (0x01 << 8), // "LOGICAL DRIVE NOT READY - IN PROGRESS OF BECOMING READY"
|
||||
ASC_NOT_READY_INIT_REQ = 0x04 | (0x02 << 8), // "LOGICAL DRIVE NOT READY - INITIALIZING COMMAND REQUIRED"
|
||||
ASC_NOT_READY_MANUAL_REQ = 0x04 | (0x03 << 8), // "LOGICAL DRIVE NOT READY - MANUAL INTERVENTION REQUIRED"
|
||||
ASC_LOAD_EJECT_FAILED = 0x05 | (0x01 << 8), // "MEDIA LOAD - EJECT FAILED"
|
||||
ASC_NO_REFERENCE_POSITION = 0x06 | (0x00 << 8), // "NO REFERENCE POSITION FOUND"
|
||||
ASC_TRACK_FOLLOW_ERROR = 0x09 | (0x00 << 8), // "TRACK FOLLOWING ERROR"
|
||||
ASC_TRACK_SERVO_FAILURE = 0x09 | (0x01 << 8), // "TRACKING SERVO FAILURE"
|
||||
ASC_FOCUS_SERVO_FAILURE = 0x09 | (0x02 << 8), // "FOCUS SERVO FAILURE"
|
||||
ASC_SPINDLE_SERVO_FAILURE = 0x09 | (0x03 << 8), // "SPINDLE SERVO FAILURE"
|
||||
ASC_UNRECOVERED_READ_ERROR = 0x11 | (0x00 << 8), // "UNRECOVERED READ ERROR"
|
||||
ASC_CIRC_UNRECOVERED_ERROR = 0x11 | (0x06 << 8), // "CIRC UNRECOVERED ERROR"
|
||||
ASC_POSITIONING_ERROR = 0x15 | (0x00 << 8), // "RANDOM POSITIONING ERROR"
|
||||
ASC_MECHANICAL_ERROR_2 = 0x15 | (0x01 << 8), // "MECHANICAL POSITIONING OR CHANGER ERROR"
|
||||
ASC_POSITIONING_ERROR_2 = 0x15 | (0x02 << 8), // "POSITIONING ERROR DETECTED BY READ OF MEDIUM"
|
||||
ASC_REC_DATA_NO_ECC = 0x17 | (0x00 << 8), // "RECOVERED DATA WITH NO ERROR CORRECTION APPLIED"
|
||||
ASC_REC_DATA_RETRIES = 0x17 | (0x01 << 8), // "RECOVERED DATA WITH RETRIES"
|
||||
ASC_REC_DATA_POS_OFFSET = 0x17 | (0x02 << 8), // "RECOVERED DATA WITH POSITIVE HEAD OFFSET"
|
||||
ASC_REC_DATA_NEG_OFFSET = 0x17 | (0x03 << 8), // "RECOVERED DATA WITH NEGATIVE HEAD OFFSET"
|
||||
ASC_REC_DATA_RETRIES_CIRC = 0x17 | (0x04 << 8), // "RECOVERED DATA WITH RETRIES AND/OR CIRC APPLIED"
|
||||
ASC_REC_DATA_PREV_SECTOR = 0x17 | (0x05 << 8), // "RECOVERED DATA USING PREVIOUS SECTOR ID"
|
||||
ASC_REC_DATA_ECC = 0x18 | (0x00 << 8), // "RECOVERED DATA WITH ERROR CORRECTION APPLIED"
|
||||
ASC_REC_DATA_ECC_RETRIES = 0x18 | (0x01 << 8), // "RECOVERED DATA WITH ERROR CORRECTION & RETRIES APPLIED"
|
||||
ASC_REC_DATA_REALLOCATED = 0x18 | (0x02 << 8), // "RECOVERED DATA - THE DATA WAS AUTO-REALLOCATED"
|
||||
ASC_REC_DATA_CIRC = 0x18 | (0x03 << 8), // "RECOVERED DATA WITH CIRC"
|
||||
ASC_REC_DATA_L_EC = 0x18 | (0x04 << 8), // "RECOVERED DATA WITH L-EC"
|
||||
ASC_PARAM_LENGTH_ERROR = 0x1a | (0x00 << 8), // "PARAMETER LIST LENGTH ERROR"
|
||||
ASC_INVALID_COMMAND = 0x20 | (0x00 << 8), // "INVALID COMMAND OPERATION CODE"
|
||||
ASC_LBA_OUT_OF_RANGE = 0x21 | (0x00 << 8), // "LOGICAL BLOCK ADDRESS OUT OF RANGE"
|
||||
ASC_INVALID_PACKET_FIELD = 0x24 | (0x00 << 8), // "INVALID FIELD IN COMMAND PACKET"
|
||||
ASC_INVALID_PARAM_FIELD = 0x26 | (0x00 << 8), // "INVALID FIELD IN PARAMETER LIST"
|
||||
ASC_PARAM_NOT_SUPPORTED = 0x26 | (0x01 << 8), // "PARAMETER NOT SUPPORTED"
|
||||
ASC_PARAM_VALUE_INVALID = 0x26 | (0x02 << 8), // "PARAMETER VALUE INVALID"
|
||||
ASC_NOT_READY_TO_READY = 0x28 | (0x00 << 8), // "NOT READY TO READY TRANSITION, MEDIUM MAY HAVE CHANGED"
|
||||
ASC_RESET_OCCURRED = 0x29 | (0x00 << 8), // "POWER ON, RESET OR BUS DEVICE RESET OCCURRED"
|
||||
ASC_PARAMS_CHANGED = 0x2a | (0x00 << 8), // "PARAMETERS CHANGED"
|
||||
ASC_MODE_PARAMS_CHANGED = 0x2a | (0x01 << 8), // "MODE PARAMETERS CHANGED"
|
||||
ASC_INCOMPATIBLE_MEDIUM = 0x30 | (0x00 << 8), // "INCOMPATIBLE MEDIUM INSTALLED"
|
||||
ASC_UNKNOWN_FORMAT = 0x30 | (0x01 << 8), // "CANNOT READ MEDIUM - UNKNOWN FORMAT"
|
||||
ASC_INCOMPATIBLE_FORMAT = 0x30 | (0x02 << 8), // "CANNOT READ MEDIUM - INCOMPATIBLE FORMAT"
|
||||
ASC_SAVING_NOT_SUPPORTED = 0x39 | (0x00 << 8), // "SAVING PARAMETERS NOT SUPPORTED"
|
||||
ASC_MEDIUM_NOT_PRESENT = 0x3a | (0x00 << 8), // "MEDIUM NOT PRESENT"
|
||||
ASC_CONDITIONS_CHANGED = 0x3f | (0x00 << 8), // "ATAPI CD-ROM DRIVE OPERATING CONDITIONS HAVE CHANGED"
|
||||
ASC_MICROCODE_CHANGED = 0x3f | (0x01 << 8), // "MICROCODE HAS BEEN CHANGED"
|
||||
ASC_INTERNAL_DRIVE_FAILURE = 0x44 | (0x00 << 8), // "INTERNAL ATAPI CD-ROM DRIVE FAILURE"
|
||||
ASC_OVERLAP_ATTEMPTED = 0x4e | (0x00 << 8), // "OVERLAPPED COMMANDS ATTEMPTED"
|
||||
ASC_LOAD_EJECT_FAILED_2 = 0x53 | (0x00 << 8), // "MEDIA LOAD OR EJECT FAILED"
|
||||
ASC_REMOVAL_PREVENTED = 0x53 | (0x02 << 8), // "MEDIUM REMOVAL PREVENTED"
|
||||
ASC_UNABLE_TO_RECOVER_TOC = 0x57 | (0x00 << 8), // "UNABLE TO RECOVER TABLE OF CONTENTS"
|
||||
ASC_OPERATOR_REQUEST = 0x5a | (0x00 << 8), // "OPERATOR REQUEST OR STATE CHANGE INPUT (UNSPECIFIED)"
|
||||
ASC_REMOVAL_REQUEST = 0x5a | (0x01 << 8), // "OPERATOR MEDIUM REMOVAL REQUEST"
|
||||
ASC_END_OF_USER_AREA = 0x63 | (0x00 << 8), // "END OF USER AREA ENCOUNTERED ON THIS TRACK"
|
||||
ASC_ILLEGAL_TRACK_MODE = 0x64 | (0x00 << 8), // "ILLEGAL MODE FOR THIS TRACK"
|
||||
ASC_PLAY_ABORTED = 0xb9 | (0x00 << 8), // "PLAY OPERATION ABORTED"
|
||||
ASC_LOSS_OF_STREAMING = 0xbf | (0x00 << 8) // "LOSS OF STREAMING"
|
||||
};
|
||||
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
#include <stdint.h>
|
||||
#include "common/io.hpp"
|
||||
#include "common/rom.hpp"
|
||||
#include "common/romdrivers.hpp"
|
||||
#include "common/util.hpp"
|
||||
#include "ps1/registers.h"
|
||||
#include "ps1/registers573.h"
|
||||
@ -165,29 +166,6 @@ uint32_t FlashRegion::zipCRC32(
|
||||
|
||||
/* Flash-specific functions */
|
||||
|
||||
enum JEDECCommand : uint16_t {
|
||||
_JEDEC_RESET = 0xf0f0,
|
||||
_JEDEC_HANDSHAKE1 = 0xaaaa,
|
||||
_JEDEC_HANDSHAKE2 = 0x5555,
|
||||
_JEDEC_GET_ID = 0x9090,
|
||||
_JEDEC_WRITE_BYTE = 0xa0a0,
|
||||
_JEDEC_ERASE_HANDSHAKE = 0x8080,
|
||||
_JEDEC_ERASE_CHIP = 0x1010,
|
||||
_JEDEC_ERASE_SECTOR = 0x3030
|
||||
};
|
||||
|
||||
enum IntelCommand : uint16_t {
|
||||
_INTEL_RESET = 0xffff,
|
||||
_INTEL_GET_ID = 0x9090,
|
||||
_INTEL_WRITE_BYTE = 0x4040,
|
||||
_INTEL_ERASE_SECTOR1 = 0x2020,
|
||||
_INTEL_ERASE_SECTOR2 = 0xd0d0,
|
||||
_INTEL_GET_STATUS = 0x7070,
|
||||
_INTEL_CLEAR_STATUS = 0x5050,
|
||||
_INTEL_SUSPEND = 0xb0b0,
|
||||
_INTEL_RESUME = 0xd0d0
|
||||
};
|
||||
|
||||
enum FlashIdentifier : uint16_t {
|
||||
_ID_AM29F016 = 0x01 | (0xad << 8),
|
||||
_ID_AM29F040 = 0x01 | (0xa4 << 8),
|
||||
@ -234,16 +212,16 @@ uint32_t FlashRegion::getJEDECID(void) const {
|
||||
|
||||
auto _ptr = reinterpret_cast<volatile uint16_t *>(ptr);
|
||||
|
||||
_ptr[0x000] = _JEDEC_RESET;
|
||||
_ptr[0x000] = _INTEL_RESET;
|
||||
_ptr[0x555] = _JEDEC_HANDSHAKE1;
|
||||
_ptr[0x2aa] = _JEDEC_HANDSHAKE2;
|
||||
_ptr[0x555] = _JEDEC_GET_ID; // Same as _INTEL_GET_ID
|
||||
_ptr[0x000] = JEDEC_RESET;
|
||||
_ptr[0x000] = INTEL_RESET;
|
||||
_ptr[0x555] = JEDEC_HANDSHAKE1;
|
||||
_ptr[0x2aa] = JEDEC_HANDSHAKE2;
|
||||
_ptr[0x555] = JEDEC_GET_ID; // Same as INTEL_GET_ID
|
||||
|
||||
uint32_t id = _ptr[0] | (_ptr[1] << 16);
|
||||
|
||||
_ptr[0x000] = _JEDEC_RESET;
|
||||
_ptr[0x000] = _INTEL_RESET;
|
||||
_ptr[0x000] = JEDEC_RESET;
|
||||
_ptr[0x000] = INTEL_RESET;
|
||||
|
||||
return id;
|
||||
}
|
||||
@ -301,356 +279,6 @@ const FlashRegion pcmcia[2]{
|
||||
{ SYS573_BANK_PCMCIA2, 0x4000000, io::JAMMA_PCMCIA_CD2 }
|
||||
};
|
||||
|
||||
/* Data common to all chip drivers */
|
||||
|
||||
static constexpr int _FLASH_WRITE_TIMEOUT = 100000;
|
||||
static constexpr int _FLASH_ERASE_TIMEOUT = 20000000;
|
||||
|
||||
const char *const DRIVER_ERROR_NAMES[]{
|
||||
"NO_ERROR",
|
||||
"UNSUPPORTED_OP",
|
||||
"CHIP_TIMEOUT",
|
||||
"CHIP_ERROR",
|
||||
"VERIFY_MISMATCH",
|
||||
"WRITE_PROTECTED"
|
||||
};
|
||||
|
||||
static const ChipSize _DUMMY_CHIP_SIZE{
|
||||
.chipLength = 0,
|
||||
.eraseSectorLength = 0
|
||||
};
|
||||
|
||||
// The onboard flash and all Konami-supplied flash cards use 2 MB chips with 64
|
||||
// KB sectors and an 8-bit bus.
|
||||
static const ChipSize _STANDARD_CHIP_SIZE{
|
||||
.chipLength = 2 * 0x200000,
|
||||
.eraseSectorLength = 2 * 0x10000
|
||||
};
|
||||
|
||||
static const ChipSize _ALT_CHIP_SIZE{
|
||||
.chipLength = 2 * 0x80000,
|
||||
.eraseSectorLength = 2 * 0x10000
|
||||
};
|
||||
|
||||
const ChipSize &Driver::getChipSize(void) const {
|
||||
return _DUMMY_CHIP_SIZE;
|
||||
}
|
||||
|
||||
/* RTC RAM driver */
|
||||
|
||||
static const ChipSize _RTC_CHIP_SIZE{
|
||||
.chipLength = 0x1ff8,
|
||||
.eraseSectorLength = 0x1ff8
|
||||
};
|
||||
|
||||
void RTCDriver::write(uint32_t offset, uint16_t value) {
|
||||
auto ptr = reinterpret_cast<volatile uint16_t *>(_region.ptr + offset * 2);
|
||||
ptr[0] = value & 0xff;
|
||||
ptr[1] = value >> 8;
|
||||
}
|
||||
|
||||
void RTCDriver::eraseSector(uint32_t offset) {
|
||||
auto ptr = reinterpret_cast<void *>(_region.ptr);
|
||||
|
||||
__builtin_memset(ptr, 0, _region.regionLength * 2);
|
||||
}
|
||||
|
||||
void RTCDriver::eraseChip(uint32_t offset) {
|
||||
eraseSector(offset);
|
||||
}
|
||||
|
||||
DriverError RTCDriver::flushWrite(uint32_t offset, uint16_t value) {
|
||||
auto ptr = reinterpret_cast<volatile uint16_t *>(_region.ptr + offset * 2);
|
||||
|
||||
uint16_t actualValue = (ptr[0] & 0xff) | ((ptr[1] & 0xff) << 8);
|
||||
|
||||
if (value != actualValue) {
|
||||
LOG(
|
||||
"mismatch @ 0x%08x, exp=0x%02x, got=0x%04x", offset, value,
|
||||
actualValue
|
||||
);
|
||||
return VERIFY_MISMATCH;
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
DriverError RTCDriver::flushErase(uint32_t offset) {
|
||||
return flushWrite(offset, 0);
|
||||
}
|
||||
|
||||
const ChipSize &RTCDriver::getChipSize(void) const {
|
||||
return _RTC_CHIP_SIZE;
|
||||
}
|
||||
|
||||
/* AMD AM29F016/017 (Fujitsu MBM29F016A/017A) driver */
|
||||
|
||||
enum JEDECStatusFlag : uint16_t {
|
||||
_JEDEC_STATUS_ERASE_TOGGLE = 1 << 2,
|
||||
_JEDEC_STATUS_ERASE_START = 1 << 3,
|
||||
_JEDEC_STATUS_ERROR = 1 << 5,
|
||||
_JEDEC_STATUS_TOGGLE = 1 << 6,
|
||||
_JEDEC_STATUS_POLL_BIT = 1 << 7
|
||||
};
|
||||
|
||||
DriverError AM29F016Driver::_flush(
|
||||
uint32_t offset, uint16_t value, int timeout
|
||||
) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset & ~1);
|
||||
|
||||
int shift = (offset & 1) * 8;
|
||||
uint8_t byte = (value >> shift) & 0xff;
|
||||
|
||||
uint8_t status, diff;
|
||||
|
||||
for (; timeout > 0; timeout--) {
|
||||
status = (*ptr >> shift) & 0xff;
|
||||
diff = status ^ byte;
|
||||
|
||||
if (!(diff & _JEDEC_STATUS_POLL_BIT))
|
||||
return NO_ERROR;
|
||||
if (status & _JEDEC_STATUS_ERROR)
|
||||
break;
|
||||
}
|
||||
|
||||
// If the error flag was set, make sure an error actually occurred.
|
||||
status = (*ptr >> shift) & 0xff;
|
||||
diff = status ^ byte;
|
||||
|
||||
if (!(diff & _JEDEC_STATUS_POLL_BIT))
|
||||
return NO_ERROR;
|
||||
|
||||
*ptr = _JEDEC_RESET;
|
||||
|
||||
if (status & _JEDEC_STATUS_ERROR) {
|
||||
LOG("error @ 0x%08x, stat=0x%02x", offset, status);
|
||||
return CHIP_ERROR;
|
||||
} else {
|
||||
LOG("timeout @ 0x%08x, stat=0x%02x", offset, status);
|
||||
return CHIP_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
void AM29F016Driver::write(uint32_t offset, uint16_t value) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
||||
offset = (offset % FLASH_BANK_LENGTH) / 2;
|
||||
|
||||
ptr[0x000] = _JEDEC_RESET;
|
||||
ptr[0x555] = _JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aa] = _JEDEC_HANDSHAKE2;
|
||||
ptr[0x555] = _JEDEC_WRITE_BYTE;
|
||||
ptr[offset] = value;
|
||||
}
|
||||
|
||||
void AM29F016Driver::eraseSector(uint32_t offset) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
||||
offset = (offset % FLASH_BANK_LENGTH) / 2;
|
||||
|
||||
ptr[0x000] = _JEDEC_RESET;
|
||||
ptr[0x555] = _JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aa] = _JEDEC_HANDSHAKE2;
|
||||
ptr[0x555] = _JEDEC_ERASE_HANDSHAKE;
|
||||
ptr[0x555] = _JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aa] = _JEDEC_HANDSHAKE2;
|
||||
ptr[offset] = _JEDEC_ERASE_SECTOR;
|
||||
}
|
||||
|
||||
void AM29F016Driver::eraseChip(uint32_t offset) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
||||
|
||||
ptr[0x000] = _JEDEC_RESET;
|
||||
ptr[0x555] = _JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aa] = _JEDEC_HANDSHAKE2;
|
||||
ptr[0x555] = _JEDEC_ERASE_HANDSHAKE;
|
||||
ptr[0x555] = _JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aa] = _JEDEC_HANDSHAKE2;
|
||||
ptr[0x555] = _JEDEC_ERASE_CHIP;
|
||||
}
|
||||
|
||||
DriverError AM29F016Driver::flushWrite(
|
||||
uint32_t offset, uint16_t value
|
||||
) {
|
||||
auto error = _flush(offset, value, _FLASH_WRITE_TIMEOUT);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return _flush(offset + 1, value, _FLASH_WRITE_TIMEOUT);
|
||||
}
|
||||
|
||||
DriverError AM29F016Driver::flushErase(uint32_t offset) {
|
||||
auto error = _flush(offset, 0xffff, _FLASH_ERASE_TIMEOUT);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return _flush(offset + 1, 0xffff, _FLASH_ERASE_TIMEOUT);
|
||||
}
|
||||
|
||||
const ChipSize &AM29F016Driver::getChipSize(void) const {
|
||||
return _STANDARD_CHIP_SIZE;
|
||||
}
|
||||
|
||||
/* AMD AM29F040 (Fujitsu MBM29F040A) driver */
|
||||
|
||||
// Konami's drivers handle this chip pretty much identically to the MBM29F016A,
|
||||
// but using 0x5555/0x2aaa as command addresses instead of 0x555/0x2aa.
|
||||
|
||||
void AM29F040Driver::write(uint32_t offset, uint16_t value) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
||||
offset = (offset % FLASH_BANK_LENGTH) / 2;
|
||||
|
||||
ptr[0x0000] = _JEDEC_RESET;
|
||||
ptr[0x5555] = _JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aaa] = _JEDEC_HANDSHAKE2;
|
||||
ptr[0x5555] = _JEDEC_WRITE_BYTE;
|
||||
ptr[offset] = value;
|
||||
}
|
||||
|
||||
void AM29F040Driver::eraseSector(uint32_t offset) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
||||
offset = (offset % FLASH_BANK_LENGTH) / 2;
|
||||
|
||||
ptr[0x0000] = _JEDEC_RESET;
|
||||
ptr[0x5555] = _JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aaa] = _JEDEC_HANDSHAKE2;
|
||||
ptr[0x5555] = _JEDEC_ERASE_HANDSHAKE;
|
||||
ptr[0x5555] = _JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aaa] = _JEDEC_HANDSHAKE2;
|
||||
ptr[offset] = _JEDEC_ERASE_SECTOR;
|
||||
}
|
||||
|
||||
void AM29F040Driver::eraseChip(uint32_t offset) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
||||
|
||||
ptr[0x0005] = _JEDEC_RESET;
|
||||
ptr[0x5555] = _JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aaa] = _JEDEC_HANDSHAKE2;
|
||||
ptr[0x5555] = _JEDEC_ERASE_HANDSHAKE;
|
||||
ptr[0x5555] = _JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aaa] = _JEDEC_HANDSHAKE2;
|
||||
ptr[0x5555] = _JEDEC_ERASE_CHIP;
|
||||
}
|
||||
|
||||
const ChipSize &AM29F040Driver::getChipSize(void) const {
|
||||
return _ALT_CHIP_SIZE;
|
||||
}
|
||||
|
||||
/* Intel 28F016S5 (Sharp LH28F016S) driver */
|
||||
|
||||
enum IntelStatusFlag : uint16_t {
|
||||
_INTEL_STATUS_DPS = 1 << 1,
|
||||
_INTEL_STATUS_BWSS = 1 << 2,
|
||||
_INTEL_STATUS_VPPS = 1 << 3,
|
||||
_INTEL_STATUS_BWSLBS = 1 << 4,
|
||||
_INTEL_STATUS_ECLBS = 1 << 5,
|
||||
_INTEL_STATUS_ESS = 1 << 6,
|
||||
_INTEL_STATUS_WSMS = 1 << 7
|
||||
};
|
||||
|
||||
DriverError Intel28F016S5Driver::_flush(uint32_t offset, int timeout) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset & ~1);
|
||||
|
||||
int shift = (offset & 1) * 8;
|
||||
uint8_t status = 0;
|
||||
|
||||
// Not required as all write/erase commands already put the chip into status
|
||||
// reading mode.
|
||||
//*ptr = _INTEL_GET_STATUS;
|
||||
|
||||
for (; timeout > 0; timeout--) {
|
||||
status = (*ptr >> shift) & 0xff;
|
||||
|
||||
if (!(status & _INTEL_STATUS_WSMS))
|
||||
continue;
|
||||
|
||||
*ptr = _INTEL_RESET;
|
||||
|
||||
// The datasheet suggests only checking the error flags after WSMS = 1.
|
||||
if (status & (_INTEL_STATUS_DPS | _INTEL_STATUS_VPPS)) {
|
||||
*ptr = _INTEL_CLEAR_STATUS;
|
||||
|
||||
LOG("locked @ 0x%08x, stat=0x%02x", offset, status);
|
||||
return WRITE_PROTECTED;
|
||||
}
|
||||
if (status & (_INTEL_STATUS_BWSLBS | _INTEL_STATUS_ECLBS)) {
|
||||
*ptr = _INTEL_CLEAR_STATUS;
|
||||
|
||||
LOG("error @ 0x%08x, stat=0x%02x", offset, status);
|
||||
return CHIP_ERROR;
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
*ptr = _INTEL_RESET;
|
||||
|
||||
LOG("timeout @ 0x%08x, stat=0x%02x", offset, status);
|
||||
return CHIP_TIMEOUT;
|
||||
}
|
||||
|
||||
void Intel28F016S5Driver::write(uint32_t offset, uint16_t value) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset);
|
||||
|
||||
*ptr = _INTEL_RESET;
|
||||
*ptr = _INTEL_CLEAR_STATUS;
|
||||
*ptr = _INTEL_WRITE_BYTE;
|
||||
*ptr = value;
|
||||
}
|
||||
|
||||
void Intel28F016S5Driver::eraseSector(uint32_t offset) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset);
|
||||
|
||||
*ptr = _INTEL_RESET;
|
||||
*ptr = _INTEL_ERASE_SECTOR1;
|
||||
*ptr = _INTEL_ERASE_SECTOR2;
|
||||
}
|
||||
|
||||
DriverError Intel28F016S5Driver::flushWrite(
|
||||
uint32_t offset, uint16_t value
|
||||
) {
|
||||
auto error = _flush(offset, _FLASH_WRITE_TIMEOUT);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return _flush(offset + 1, _FLASH_WRITE_TIMEOUT);
|
||||
}
|
||||
|
||||
DriverError Intel28F016S5Driver::flushErase(uint32_t offset) {
|
||||
auto error = _flush(offset, _FLASH_ERASE_TIMEOUT);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return _flush(offset + 1, _FLASH_ERASE_TIMEOUT);
|
||||
}
|
||||
|
||||
const ChipSize &Intel28F016S5Driver::getChipSize(void) const {
|
||||
return _STANDARD_CHIP_SIZE;
|
||||
}
|
||||
|
||||
/* Intel 28F640J5 driver */
|
||||
|
||||
static const ChipSize _28F640J5_CHIP_SIZE{
|
||||
.chipLength = 0x800000,
|
||||
.eraseSectorLength = 0x20000
|
||||
};
|
||||
|
||||
DriverError Intel28F640J5Driver::flushWrite(
|
||||
uint32_t offset, uint16_t value
|
||||
) {
|
||||
return _flush(offset, _FLASH_WRITE_TIMEOUT);
|
||||
}
|
||||
|
||||
DriverError Intel28F640J5Driver::flushErase(uint32_t offset) {
|
||||
return _flush(offset, _FLASH_ERASE_TIMEOUT);
|
||||
}
|
||||
|
||||
const ChipSize &Intel28F640J5Driver::getChipSize(void) const {
|
||||
return _28F640J5_CHIP_SIZE;
|
||||
}
|
||||
|
||||
/* BIOS ROM headers */
|
||||
|
||||
static const ShellInfo _SHELL_VERSIONS[]{
|
||||
|
@ -76,116 +76,6 @@ extern const BIOSRegion bios;
|
||||
extern const RTCRegion rtc;
|
||||
extern const FlashRegion flash, pcmcia[2];
|
||||
|
||||
/* Chip drivers */
|
||||
|
||||
enum DriverError {
|
||||
NO_ERROR = 0,
|
||||
UNSUPPORTED_OP = 1,
|
||||
CHIP_TIMEOUT = 2,
|
||||
CHIP_ERROR = 3,
|
||||
VERIFY_MISMATCH = 4,
|
||||
WRITE_PROTECTED = 5
|
||||
};
|
||||
|
||||
struct ChipSize {
|
||||
public:
|
||||
size_t chipLength, eraseSectorLength;
|
||||
};
|
||||
|
||||
class Driver {
|
||||
protected:
|
||||
const Region &_region;
|
||||
|
||||
public:
|
||||
inline Driver(const Region ®ion)
|
||||
: _region(region) {}
|
||||
|
||||
// Note that all offsets must be multiples of 2, as writes are done in
|
||||
// halfwords.
|
||||
virtual ~Driver(void) {}
|
||||
virtual void write(uint32_t offset, uint16_t value) {}
|
||||
virtual void eraseSector(uint32_t offset) {}
|
||||
virtual void eraseChip(uint32_t offset) {}
|
||||
virtual DriverError flushWrite(uint32_t offset, uint16_t value) {
|
||||
return UNSUPPORTED_OP;
|
||||
}
|
||||
virtual DriverError flushErase(uint32_t offset) {
|
||||
return UNSUPPORTED_OP;
|
||||
}
|
||||
virtual const ChipSize &getChipSize(void) const;
|
||||
};
|
||||
|
||||
class RTCDriver : public Driver {
|
||||
public:
|
||||
inline RTCDriver(const RTCRegion ®ion)
|
||||
: Driver(region) {}
|
||||
|
||||
void write(uint32_t offset, uint16_t value);
|
||||
void eraseSector(uint32_t offset);
|
||||
void eraseChip(uint32_t offset);
|
||||
DriverError flushWrite(uint32_t offset, uint16_t value);
|
||||
DriverError flushErase(uint32_t offset);
|
||||
const ChipSize &getChipSize(void) const;
|
||||
};
|
||||
|
||||
class AM29F016Driver : public Driver {
|
||||
protected:
|
||||
DriverError _flush(uint32_t offset, uint16_t value, int timeout);
|
||||
|
||||
public:
|
||||
inline AM29F016Driver(const FlashRegion ®ion)
|
||||
: Driver(region) {}
|
||||
|
||||
virtual void write(uint32_t offset, uint16_t value);
|
||||
virtual void eraseSector(uint32_t offset);
|
||||
virtual void eraseChip(uint32_t offset);
|
||||
DriverError flushWrite(uint32_t offset, uint16_t value);
|
||||
DriverError flushErase(uint32_t offset);
|
||||
const ChipSize &getChipSize(void) const;
|
||||
};
|
||||
|
||||
class AM29F040Driver : public AM29F016Driver {
|
||||
public:
|
||||
inline AM29F040Driver(const FlashRegion ®ion)
|
||||
: AM29F016Driver(region) {}
|
||||
|
||||
void write(uint32_t offset, uint16_t value);
|
||||
void eraseSector(uint32_t offset);
|
||||
void eraseChip(uint32_t offset);
|
||||
const ChipSize &getChipSize(void) const;
|
||||
};
|
||||
|
||||
class Intel28F016S5Driver : public Driver {
|
||||
protected:
|
||||
DriverError _flush(uint32_t offset, int timeout);
|
||||
|
||||
public:
|
||||
inline Intel28F016S5Driver(const FlashRegion ®ion)
|
||||
: Driver(region) {}
|
||||
|
||||
void write(uint32_t offset, uint16_t value);
|
||||
void eraseSector(uint32_t offset);
|
||||
DriverError flushWrite(uint32_t offset, uint16_t value);
|
||||
DriverError flushErase(uint32_t offset);
|
||||
const ChipSize &getChipSize(void) const;
|
||||
};
|
||||
|
||||
class Intel28F640J5Driver : public Intel28F016S5Driver {
|
||||
public:
|
||||
inline Intel28F640J5Driver(const FlashRegion ®ion)
|
||||
: Intel28F016S5Driver(region) {}
|
||||
|
||||
DriverError flushWrite(uint32_t offset, uint16_t value);
|
||||
DriverError flushErase(uint32_t offset);
|
||||
const ChipSize &getChipSize(void) const;
|
||||
};
|
||||
|
||||
extern const char *const DRIVER_ERROR_NAMES[];
|
||||
|
||||
static inline const char *getErrorString(DriverError error) {
|
||||
return DRIVER_ERROR_NAMES[error];
|
||||
}
|
||||
|
||||
/* BIOS ROM headers */
|
||||
|
||||
struct [[gnu::packed]] SonyKernelHeader {
|
||||
|
336
src/common/romdrivers.cpp
Normal file
336
src/common/romdrivers.cpp
Normal file
@ -0,0 +1,336 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/rom.hpp"
|
||||
#include "common/romdrivers.hpp"
|
||||
|
||||
namespace rom {
|
||||
|
||||
/* Data common to all chip drivers */
|
||||
|
||||
static constexpr int _FLASH_WRITE_TIMEOUT = 10000000;
|
||||
static constexpr int _FLASH_ERASE_TIMEOUT = 20000000;
|
||||
|
||||
const char *const DRIVER_ERROR_NAMES[]{
|
||||
"NO_ERROR",
|
||||
"UNSUPPORTED_OP",
|
||||
"CHIP_TIMEOUT",
|
||||
"CHIP_ERROR",
|
||||
"VERIFY_MISMATCH",
|
||||
"WRITE_PROTECTED"
|
||||
};
|
||||
|
||||
static const ChipSize _DUMMY_CHIP_SIZE{
|
||||
.chipLength = 0,
|
||||
.eraseSectorLength = 0
|
||||
};
|
||||
|
||||
// The onboard flash and all Konami-supplied flash cards use 2 MB chips with 64
|
||||
// KB sectors and an 8-bit bus.
|
||||
static const ChipSize _STANDARD_CHIP_SIZE{
|
||||
.chipLength = 2 * 0x200000,
|
||||
.eraseSectorLength = 2 * 0x10000
|
||||
};
|
||||
|
||||
static const ChipSize _ALT_CHIP_SIZE{
|
||||
.chipLength = 2 * 0x80000,
|
||||
.eraseSectorLength = 2 * 0x10000
|
||||
};
|
||||
|
||||
const ChipSize &Driver::getChipSize(void) const {
|
||||
return _DUMMY_CHIP_SIZE;
|
||||
}
|
||||
|
||||
/* RTC RAM driver */
|
||||
|
||||
static const ChipSize _RTC_CHIP_SIZE{
|
||||
.chipLength = 0x1ff8,
|
||||
.eraseSectorLength = 0x1ff8
|
||||
};
|
||||
|
||||
void RTCDriver::write(uint32_t offset, uint16_t value) {
|
||||
auto ptr = reinterpret_cast<volatile uint16_t *>(_region.ptr + offset * 2);
|
||||
ptr[0] = value & 0xff;
|
||||
ptr[1] = value >> 8;
|
||||
}
|
||||
|
||||
void RTCDriver::eraseSector(uint32_t offset) {
|
||||
auto ptr = reinterpret_cast<void *>(_region.ptr);
|
||||
|
||||
__builtin_memset(ptr, 0, _region.regionLength * 2);
|
||||
}
|
||||
|
||||
void RTCDriver::eraseChip(uint32_t offset) {
|
||||
eraseSector(offset);
|
||||
}
|
||||
|
||||
DriverError RTCDriver::flushWrite(uint32_t offset, uint16_t value) {
|
||||
auto ptr = reinterpret_cast<volatile uint16_t *>(_region.ptr + offset * 2);
|
||||
|
||||
uint16_t actualValue = (ptr[0] & 0xff) | ((ptr[1] & 0xff) << 8);
|
||||
|
||||
if (value != actualValue) {
|
||||
LOG("ptr=0x%06x, exp=0x%02x, got=0x%04x", offset, value, actualValue);
|
||||
return VERIFY_MISMATCH;
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
DriverError RTCDriver::flushErase(uint32_t offset) {
|
||||
return flushWrite(offset, 0);
|
||||
}
|
||||
|
||||
const ChipSize &RTCDriver::getChipSize(void) const {
|
||||
return _RTC_CHIP_SIZE;
|
||||
}
|
||||
|
||||
/* AMD AM29F016/017 (Fujitsu MBM29F016A/017A) driver */
|
||||
|
||||
DriverError AM29F016Driver::_flush(
|
||||
uint32_t offset, uint16_t value, int timeout
|
||||
) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset & ~1);
|
||||
|
||||
int shift = (offset & 1) * 8;
|
||||
uint8_t byte = (value >> shift) & 0xff;
|
||||
|
||||
uint8_t status, diff;
|
||||
|
||||
for (; timeout > 0; timeout--) {
|
||||
status = (*ptr >> shift) & 0xff;
|
||||
diff = status ^ byte;
|
||||
|
||||
if (!(diff & JEDEC_STATUS_POLL_BIT))
|
||||
return NO_ERROR;
|
||||
if (status & JEDEC_STATUS_ERROR)
|
||||
break;
|
||||
}
|
||||
|
||||
// If the error flag was set, make sure an error actually occurred.
|
||||
status = (*ptr >> shift) & 0xff;
|
||||
diff = status ^ byte;
|
||||
|
||||
if (!(diff & JEDEC_STATUS_POLL_BIT))
|
||||
return NO_ERROR;
|
||||
|
||||
*ptr = JEDEC_RESET;
|
||||
|
||||
if (status & JEDEC_STATUS_ERROR) {
|
||||
LOG("JEDEC error, ptr=0x%06x, st=0x%02x", offset, status);
|
||||
return CHIP_ERROR;
|
||||
} else {
|
||||
LOG("JEDEC timeout, ptr=0x%06x, st=0x%02x", offset, status);
|
||||
return CHIP_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
void AM29F016Driver::write(uint32_t offset, uint16_t value) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
||||
offset = (offset % FLASH_BANK_LENGTH) / 2;
|
||||
|
||||
ptr[0x000] = JEDEC_RESET;
|
||||
ptr[0x555] = JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aa] = JEDEC_HANDSHAKE2;
|
||||
ptr[0x555] = JEDEC_WRITE_BYTE;
|
||||
ptr[offset] = value;
|
||||
}
|
||||
|
||||
void AM29F016Driver::eraseSector(uint32_t offset) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
||||
offset = (offset % FLASH_BANK_LENGTH) / 2;
|
||||
|
||||
ptr[0x000] = JEDEC_RESET;
|
||||
ptr[0x555] = JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aa] = JEDEC_HANDSHAKE2;
|
||||
ptr[0x555] = JEDEC_ERASE_HANDSHAKE;
|
||||
ptr[0x555] = JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aa] = JEDEC_HANDSHAKE2;
|
||||
ptr[offset] = JEDEC_ERASE_SECTOR;
|
||||
}
|
||||
|
||||
void AM29F016Driver::eraseChip(uint32_t offset) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
||||
|
||||
ptr[0x000] = JEDEC_RESET;
|
||||
ptr[0x555] = JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aa] = JEDEC_HANDSHAKE2;
|
||||
ptr[0x555] = JEDEC_ERASE_HANDSHAKE;
|
||||
ptr[0x555] = JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aa] = JEDEC_HANDSHAKE2;
|
||||
ptr[0x555] = JEDEC_ERASE_CHIP;
|
||||
}
|
||||
|
||||
DriverError AM29F016Driver::flushWrite(
|
||||
uint32_t offset, uint16_t value
|
||||
) {
|
||||
auto error = _flush(offset, value, _FLASH_WRITE_TIMEOUT);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return _flush(offset + 1, value, _FLASH_WRITE_TIMEOUT);
|
||||
}
|
||||
|
||||
DriverError AM29F016Driver::flushErase(uint32_t offset) {
|
||||
auto error = _flush(offset, 0xffff, _FLASH_ERASE_TIMEOUT);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return _flush(offset + 1, 0xffff, _FLASH_ERASE_TIMEOUT);
|
||||
}
|
||||
|
||||
const ChipSize &AM29F016Driver::getChipSize(void) const {
|
||||
return _STANDARD_CHIP_SIZE;
|
||||
}
|
||||
|
||||
/* AMD AM29F040 (Fujitsu MBM29F040A) driver */
|
||||
|
||||
// Konami's drivers handle this chip pretty much identically to the MBM29F016A,
|
||||
// but using 0x5555/0x2aaa as command addresses instead of 0x555/0x2aa.
|
||||
|
||||
void AM29F040Driver::write(uint32_t offset, uint16_t value) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
||||
offset = (offset % FLASH_BANK_LENGTH) / 2;
|
||||
|
||||
ptr[0x0000] = JEDEC_RESET;
|
||||
ptr[0x5555] = JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aaa] = JEDEC_HANDSHAKE2;
|
||||
ptr[0x5555] = JEDEC_WRITE_BYTE;
|
||||
ptr[offset] = value;
|
||||
}
|
||||
|
||||
void AM29F040Driver::eraseSector(uint32_t offset) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
||||
offset = (offset % FLASH_BANK_LENGTH) / 2;
|
||||
|
||||
ptr[0x0000] = JEDEC_RESET;
|
||||
ptr[0x5555] = JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aaa] = JEDEC_HANDSHAKE2;
|
||||
ptr[0x5555] = JEDEC_ERASE_HANDSHAKE;
|
||||
ptr[0x5555] = JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aaa] = JEDEC_HANDSHAKE2;
|
||||
ptr[offset] = JEDEC_ERASE_SECTOR;
|
||||
}
|
||||
|
||||
void AM29F040Driver::eraseChip(uint32_t offset) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
|
||||
|
||||
ptr[0x0005] = JEDEC_RESET;
|
||||
ptr[0x5555] = JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aaa] = JEDEC_HANDSHAKE2;
|
||||
ptr[0x5555] = JEDEC_ERASE_HANDSHAKE;
|
||||
ptr[0x5555] = JEDEC_HANDSHAKE1;
|
||||
ptr[0x2aaa] = JEDEC_HANDSHAKE2;
|
||||
ptr[0x5555] = JEDEC_ERASE_CHIP;
|
||||
}
|
||||
|
||||
const ChipSize &AM29F040Driver::getChipSize(void) const {
|
||||
return _ALT_CHIP_SIZE;
|
||||
}
|
||||
|
||||
/* Intel 28F016S5 (Sharp LH28F016S) driver */
|
||||
|
||||
DriverError Intel28F016S5Driver::_flush(uint32_t offset, int timeout) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset & ~1);
|
||||
|
||||
int shift = (offset & 1) * 8;
|
||||
uint8_t status = 0;
|
||||
|
||||
*ptr = INTEL_GET_STATUS;
|
||||
|
||||
for (; timeout > 0; timeout--) {
|
||||
status = (*ptr >> shift) & 0xff;
|
||||
|
||||
if (!(status & INTEL_STATUS_WSMS))
|
||||
continue;
|
||||
|
||||
*ptr = INTEL_RESET;
|
||||
|
||||
// The datasheet suggests only checking the error flags after WSMS = 1.
|
||||
if (status & (INTEL_STATUS_DPS | INTEL_STATUS_VPPS)) {
|
||||
*ptr = INTEL_CLEAR_STATUS;
|
||||
|
||||
LOG("Intel WP, ptr=0x%06x, st=0x%02x", offset, status);
|
||||
return WRITE_PROTECTED;
|
||||
}
|
||||
if (status & (INTEL_STATUS_BWSLBS | INTEL_STATUS_ECLBS)) {
|
||||
*ptr = INTEL_CLEAR_STATUS;
|
||||
|
||||
LOG("Intel error, ptr=0x%06x, st=0x%02x", offset, status);
|
||||
return CHIP_ERROR;
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
*ptr = INTEL_RESET;
|
||||
|
||||
LOG("Intel timeout, ptr=0x%06x, st=0x%02x", offset, status);
|
||||
return CHIP_TIMEOUT;
|
||||
}
|
||||
|
||||
void Intel28F016S5Driver::write(uint32_t offset, uint16_t value) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset);
|
||||
|
||||
*ptr = INTEL_RESET;
|
||||
*ptr = INTEL_CLEAR_STATUS;
|
||||
*ptr = INTEL_WRITE_BYTE;
|
||||
*ptr = value;
|
||||
}
|
||||
|
||||
void Intel28F016S5Driver::eraseSector(uint32_t offset) {
|
||||
volatile uint16_t *ptr = _region.getRawPtr(offset);
|
||||
|
||||
*ptr = INTEL_RESET;
|
||||
*ptr = INTEL_ERASE_SECTOR1;
|
||||
*ptr = INTEL_ERASE_SECTOR2;
|
||||
}
|
||||
|
||||
DriverError Intel28F016S5Driver::flushWrite(
|
||||
uint32_t offset, uint16_t value
|
||||
) {
|
||||
auto error = _flush(offset, _FLASH_WRITE_TIMEOUT);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return _flush(offset + 1, _FLASH_WRITE_TIMEOUT);
|
||||
}
|
||||
|
||||
DriverError Intel28F016S5Driver::flushErase(uint32_t offset) {
|
||||
auto error = _flush(offset, _FLASH_ERASE_TIMEOUT);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return _flush(offset + 1, _FLASH_ERASE_TIMEOUT);
|
||||
}
|
||||
|
||||
const ChipSize &Intel28F016S5Driver::getChipSize(void) const {
|
||||
return _STANDARD_CHIP_SIZE;
|
||||
}
|
||||
|
||||
/* Intel 28F640J5 driver */
|
||||
|
||||
static const ChipSize _28F640J5_CHIP_SIZE{
|
||||
.chipLength = 0x800000,
|
||||
.eraseSectorLength = 0x20000
|
||||
};
|
||||
|
||||
DriverError Intel28F640J5Driver::flushWrite(
|
||||
uint32_t offset, uint16_t value
|
||||
) {
|
||||
return _flush(offset, _FLASH_WRITE_TIMEOUT);
|
||||
}
|
||||
|
||||
DriverError Intel28F640J5Driver::flushErase(uint32_t offset) {
|
||||
return _flush(offset, _FLASH_ERASE_TIMEOUT);
|
||||
}
|
||||
|
||||
const ChipSize &Intel28F640J5Driver::getChipSize(void) const {
|
||||
return _28F640J5_CHIP_SIZE;
|
||||
}
|
||||
|
||||
}
|
161
src/common/romdrivers.hpp
Normal file
161
src/common/romdrivers.hpp
Normal file
@ -0,0 +1,161 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/rom.hpp"
|
||||
|
||||
namespace rom {
|
||||
|
||||
/* Chip drivers */
|
||||
|
||||
enum JEDECCommand : uint16_t {
|
||||
JEDEC_RESET = 0xf0f0,
|
||||
JEDEC_HANDSHAKE1 = 0xaaaa,
|
||||
JEDEC_HANDSHAKE2 = 0x5555,
|
||||
JEDEC_GET_ID = 0x9090,
|
||||
JEDEC_WRITE_BYTE = 0xa0a0,
|
||||
JEDEC_ERASE_HANDSHAKE = 0x8080,
|
||||
JEDEC_ERASE_CHIP = 0x1010,
|
||||
JEDEC_ERASE_SECTOR = 0x3030
|
||||
};
|
||||
|
||||
enum JEDECStatusFlag : uint16_t {
|
||||
JEDEC_STATUS_ERASE_TOGGLE = 1 << 2,
|
||||
JEDEC_STATUS_ERASE_START = 1 << 3,
|
||||
JEDEC_STATUS_ERROR = 1 << 5,
|
||||
JEDEC_STATUS_TOGGLE = 1 << 6,
|
||||
JEDEC_STATUS_POLL_BIT = 1 << 7
|
||||
};
|
||||
|
||||
enum IntelCommand : uint16_t {
|
||||
INTEL_RESET = 0xffff,
|
||||
INTEL_GET_ID = 0x9090,
|
||||
INTEL_WRITE_BYTE = 0x4040,
|
||||
INTEL_ERASE_SECTOR1 = 0x2020,
|
||||
INTEL_ERASE_SECTOR2 = 0xd0d0,
|
||||
INTEL_GET_STATUS = 0x7070,
|
||||
INTEL_CLEAR_STATUS = 0x5050,
|
||||
INTEL_SUSPEND = 0xb0b0,
|
||||
INTEL_RESUME = 0xd0d0
|
||||
};
|
||||
|
||||
enum IntelStatusFlag : uint16_t {
|
||||
INTEL_STATUS_DPS = 1 << 1,
|
||||
INTEL_STATUS_BWSS = 1 << 2,
|
||||
INTEL_STATUS_VPPS = 1 << 3,
|
||||
INTEL_STATUS_BWSLBS = 1 << 4,
|
||||
INTEL_STATUS_ECLBS = 1 << 5,
|
||||
INTEL_STATUS_ESS = 1 << 6,
|
||||
INTEL_STATUS_WSMS = 1 << 7
|
||||
};
|
||||
|
||||
enum DriverError {
|
||||
NO_ERROR = 0,
|
||||
UNSUPPORTED_OP = 1,
|
||||
CHIP_TIMEOUT = 2,
|
||||
CHIP_ERROR = 3,
|
||||
VERIFY_MISMATCH = 4,
|
||||
WRITE_PROTECTED = 5
|
||||
};
|
||||
|
||||
struct ChipSize {
|
||||
public:
|
||||
size_t chipLength, eraseSectorLength;
|
||||
};
|
||||
|
||||
class Driver {
|
||||
protected:
|
||||
const Region &_region;
|
||||
|
||||
public:
|
||||
inline Driver(const Region ®ion)
|
||||
: _region(region) {}
|
||||
|
||||
// Note that all offsets must be multiples of 2, as writes are done in
|
||||
// halfwords.
|
||||
virtual ~Driver(void) {}
|
||||
virtual void write(uint32_t offset, uint16_t value) {}
|
||||
virtual void eraseSector(uint32_t offset) {}
|
||||
virtual void eraseChip(uint32_t offset) {}
|
||||
virtual DriverError flushWrite(uint32_t offset, uint16_t value) {
|
||||
return UNSUPPORTED_OP;
|
||||
}
|
||||
virtual DriverError flushErase(uint32_t offset) {
|
||||
return UNSUPPORTED_OP;
|
||||
}
|
||||
virtual const ChipSize &getChipSize(void) const;
|
||||
};
|
||||
|
||||
class RTCDriver : public Driver {
|
||||
public:
|
||||
inline RTCDriver(const RTCRegion ®ion)
|
||||
: Driver(region) {}
|
||||
|
||||
void write(uint32_t offset, uint16_t value);
|
||||
void eraseSector(uint32_t offset);
|
||||
void eraseChip(uint32_t offset);
|
||||
DriverError flushWrite(uint32_t offset, uint16_t value);
|
||||
DriverError flushErase(uint32_t offset);
|
||||
const ChipSize &getChipSize(void) const;
|
||||
};
|
||||
|
||||
class AM29F016Driver : public Driver {
|
||||
protected:
|
||||
DriverError _flush(uint32_t offset, uint16_t value, int timeout);
|
||||
|
||||
public:
|
||||
inline AM29F016Driver(const FlashRegion ®ion)
|
||||
: Driver(region) {}
|
||||
|
||||
virtual void write(uint32_t offset, uint16_t value);
|
||||
virtual void eraseSector(uint32_t offset);
|
||||
virtual void eraseChip(uint32_t offset);
|
||||
DriverError flushWrite(uint32_t offset, uint16_t value);
|
||||
DriverError flushErase(uint32_t offset);
|
||||
const ChipSize &getChipSize(void) const;
|
||||
};
|
||||
|
||||
class AM29F040Driver : public AM29F016Driver {
|
||||
public:
|
||||
inline AM29F040Driver(const FlashRegion ®ion)
|
||||
: AM29F016Driver(region) {}
|
||||
|
||||
void write(uint32_t offset, uint16_t value);
|
||||
void eraseSector(uint32_t offset);
|
||||
void eraseChip(uint32_t offset);
|
||||
const ChipSize &getChipSize(void) const;
|
||||
};
|
||||
|
||||
class Intel28F016S5Driver : public Driver {
|
||||
protected:
|
||||
DriverError _flush(uint32_t offset, int timeout);
|
||||
|
||||
public:
|
||||
inline Intel28F016S5Driver(const FlashRegion ®ion)
|
||||
: Driver(region) {}
|
||||
|
||||
void write(uint32_t offset, uint16_t value);
|
||||
void eraseSector(uint32_t offset);
|
||||
DriverError flushWrite(uint32_t offset, uint16_t value);
|
||||
DriverError flushErase(uint32_t offset);
|
||||
const ChipSize &getChipSize(void) const;
|
||||
};
|
||||
|
||||
class Intel28F640J5Driver : public Intel28F016S5Driver {
|
||||
public:
|
||||
inline Intel28F640J5Driver(const FlashRegion ®ion)
|
||||
: Intel28F016S5Driver(region) {}
|
||||
|
||||
DriverError flushWrite(uint32_t offset, uint16_t value);
|
||||
DriverError flushErase(uint32_t offset);
|
||||
const ChipSize &getChipSize(void) const;
|
||||
};
|
||||
|
||||
extern const char *const DRIVER_ERROR_NAMES[];
|
||||
|
||||
static inline const char *getErrorString(DriverError error) {
|
||||
return DRIVER_ERROR_NAMES[error];
|
||||
}
|
||||
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
#include "common/filemisc.hpp"
|
||||
#include "common/filezip.hpp"
|
||||
#include "common/gpu.hpp"
|
||||
#include "common/ide.hpp"
|
||||
#include "common/io.hpp"
|
||||
#include "common/spu.hpp"
|
||||
#include "common/util.hpp"
|
||||
@ -65,7 +66,7 @@ WorkerStatusType WorkerStatus::setStatus(WorkerStatusType value) {
|
||||
/* Filesystem manager class */
|
||||
|
||||
FileIOManager::FileIOManager(void)
|
||||
: _resourceFile(nullptr) {
|
||||
: _resourceFile(nullptr), resourcePtr(nullptr), resourceLength(0) {
|
||||
__builtin_memset(ide, 0, sizeof(ide));
|
||||
|
||||
vfs.mount("resource:", &resource);
|
||||
@ -83,6 +84,9 @@ void FileIOManager::initIDE(void) {
|
||||
auto &dev = ide::devices[i];
|
||||
name[3] = i + '0';
|
||||
|
||||
if (!(dev.flags & ide::DEVICE_READY))
|
||||
continue;
|
||||
|
||||
// Note that calling vfs.mount() multiple times will *not* update any
|
||||
// already mounted device, so if two hard drives or CD-ROMs are present
|
||||
// the hdd:/cdrom: prefix will be assigned to the first one.
|
||||
@ -95,7 +99,7 @@ void FileIOManager::initIDE(void) {
|
||||
}
|
||||
|
||||
ide[i] = iso;
|
||||
vfs.mount("cdrom:", iso, true);
|
||||
vfs.mount("cdrom:", iso);
|
||||
} else {
|
||||
auto fat = new file::FATProvider();
|
||||
|
||||
@ -105,7 +109,7 @@ void FileIOManager::initIDE(void) {
|
||||
}
|
||||
|
||||
ide[i] = fat;
|
||||
vfs.mount("hdd:", fat, true);
|
||||
vfs.mount("hdd:", fat);
|
||||
}
|
||||
|
||||
vfs.mount(name, ide[i], true);
|
||||
@ -113,35 +117,44 @@ void FileIOManager::initIDE(void) {
|
||||
}
|
||||
|
||||
void FileIOManager::closeIDE(void) {
|
||||
for (size_t i = 0; i < util::countOf(ide::devices); i++) {
|
||||
if (!ide[i])
|
||||
continue;
|
||||
char name[6]{ "ide#:" };
|
||||
|
||||
ide[i]->close();
|
||||
delete ide[i];
|
||||
ide[i] = nullptr;
|
||||
for (size_t i = 0; i < util::countOf(ide::devices); i++) {
|
||||
if (ide[i]) {
|
||||
ide[i]->close();
|
||||
delete ide[i];
|
||||
ide[i] = nullptr;
|
||||
}
|
||||
|
||||
name[3] = i + '0';
|
||||
vfs.unmount(name);
|
||||
}
|
||||
}
|
||||
|
||||
bool FileIOManager::loadResourceFile(const char *path) {
|
||||
closeResourceFile();
|
||||
|
||||
_resourceFile = vfs.openFile(path, file::READ);
|
||||
if (path)
|
||||
_resourceFile = vfs.openFile(path, file::READ);
|
||||
|
||||
if (!_resourceFile)
|
||||
return false;
|
||||
// Fall back to the default in-memory resource archive in case of failure.
|
||||
if (_resourceFile) {
|
||||
if (resource.init(_resourceFile))
|
||||
return true;
|
||||
}
|
||||
|
||||
resource.close();
|
||||
return resource.init(_resourceFile);
|
||||
resource.init(resourcePtr, resourceLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
void FileIOManager::closeResourceFile(void) {
|
||||
if (!_resourceFile)
|
||||
return;
|
||||
resource.close();
|
||||
|
||||
_resourceFile->close();
|
||||
delete _resourceFile;
|
||||
_resourceFile = nullptr;
|
||||
if (_resourceFile) {
|
||||
_resourceFile->close();
|
||||
delete _resourceFile;
|
||||
_resourceFile = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void FileIOManager::close(void) {
|
||||
@ -320,9 +333,11 @@ void App::_interruptHandler(void) {
|
||||
LOG("build " VERSION_STRING " (" __DATE__ " " __TIME__ ")");
|
||||
LOG("(C) 2022-2024 spicyjpeg");
|
||||
|
||||
_ctx.screenData = this;
|
||||
_ctx.screenData = this;
|
||||
_fileIO.resourcePtr = resourcePtr;
|
||||
_fileIO.resourceLength = resourceLength;
|
||||
|
||||
_fileIO.resource.init(resourcePtr, resourceLength);
|
||||
_fileIO.loadResourceFile(nullptr);
|
||||
_loadResources();
|
||||
|
||||
char dateString[24];
|
||||
|
@ -54,6 +54,9 @@ private:
|
||||
file::File *_resourceFile;
|
||||
|
||||
public:
|
||||
const void *resourcePtr;
|
||||
size_t resourceLength;
|
||||
|
||||
file::Provider *ide[util::countOf(ide::devices)];
|
||||
file::ZIPProvider resource;
|
||||
#ifdef ENABLE_PCDRV
|
||||
@ -219,6 +222,7 @@ private:
|
||||
|
||||
// miscworkers.cpp
|
||||
bool _ideInitWorker(void);
|
||||
bool _fileInitWorker(void);
|
||||
bool _executableWorker(void);
|
||||
bool _atapiEjectWorker(void);
|
||||
bool _rebootWorker(void);
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "common/defs.hpp"
|
||||
#include "common/file.hpp"
|
||||
#include "common/ide.hpp"
|
||||
#include "common/idedefs.hpp"
|
||||
#include "common/io.hpp"
|
||||
#include "common/util.hpp"
|
||||
#include "main/app/app.hpp"
|
||||
@ -20,29 +21,37 @@ static const char *const _AUTOBOOT_PATHS[][2]{
|
||||
};
|
||||
|
||||
bool App::_ideInitWorker(void) {
|
||||
_workerStatus.update(0, 4, WSTR("App.ideInitWorker.initDrives"));
|
||||
|
||||
for (size_t i = 0; i < util::countOf(ide::devices); i++) {
|
||||
auto &dev = ide::devices[i];
|
||||
|
||||
// Spin down all drives by default.
|
||||
if (dev.flags & ide::DEVICE_READY)
|
||||
continue;
|
||||
if (dev.enumerate())
|
||||
dev.goIdle();
|
||||
|
||||
_workerStatus.update(
|
||||
i, util::countOf(ide::devices), WSTR("App.ideInitWorker.init")
|
||||
);
|
||||
dev.enumerate();
|
||||
}
|
||||
|
||||
_workerStatus.update(1, 4, WSTR("App.ideInitWorker.initFileIO"));
|
||||
return _fileInitWorker();
|
||||
}
|
||||
|
||||
bool App::_fileInitWorker(void) {
|
||||
_workerStatus.update(0, 4, WSTR("App.fileInitWorker.unmount"));
|
||||
_fileIO.closeResourceFile();
|
||||
_fileIO.close();
|
||||
|
||||
_workerStatus.update(1, 4, WSTR("App.fileInitWorker.mount"));
|
||||
_fileIO.initIDE();
|
||||
|
||||
_workerStatus.update(2, 4, WSTR("App.ideInitWorker.loadResources"));
|
||||
_workerStatus.update(2, 4, WSTR("App.fileInitWorker.loadResources"));
|
||||
if (_fileIO.loadResourceFile(EXTERNAL_DATA_DIR "/resource.zip"))
|
||||
_loadResources();
|
||||
|
||||
#ifdef ENABLE_AUTOBOOT
|
||||
// Only try to autoboot if DIP switch 1 is on.
|
||||
if (io::getDIPSwitch(0)) {
|
||||
_workerStatus.update(3, 4, WSTR("App.ideInitWorker.autoboot"));
|
||||
_workerStatus.update(3, 4, WSTR("App.fileInitWorker.autoboot"));
|
||||
|
||||
for (auto path : _AUTOBOOT_PATHS) {
|
||||
file::FileInfo info;
|
||||
@ -193,10 +202,8 @@ bool App::_atapiEjectWorker(void) {
|
||||
if (!(dev.flags & ide::DEVICE_ATAPI))
|
||||
continue;
|
||||
|
||||
ide::Packet packet;
|
||||
|
||||
packet.setStartStopUnit(ide::START_STOP_MODE_OPEN_TRAY);
|
||||
auto error = ide::devices[0].atapiPacket(packet);
|
||||
auto error =
|
||||
ide::devices[0].startStopUnit(ide::START_STOP_MODE_OPEN_TRAY);
|
||||
|
||||
if (error) {
|
||||
_messageScreen.setMessage(
|
||||
|
@ -165,17 +165,15 @@ void FilePickerScreen::setMessage(
|
||||
}
|
||||
|
||||
void FilePickerScreen::reloadAndShow(ui::Context &ctx) {
|
||||
// Check if any disc has been changed and reload all filesystems if
|
||||
// necessary.
|
||||
// Check if any drive has reported a disc change and reload all filesystems
|
||||
// if necessary.
|
||||
for (auto &dev : ide::devices) {
|
||||
if (!(dev.flags & ide::DEVICE_ATAPI))
|
||||
continue;
|
||||
if (!dev.atapiPoll())
|
||||
if (!dev.poll())
|
||||
continue;
|
||||
|
||||
APP->_messageScreen.previousScreens[MESSAGE_ERROR] = this;
|
||||
|
||||
APP->_runWorker(&App::_ideInitWorker, *this, false, true);
|
||||
APP->_runWorker(&App::_fileInitWorker, *this, false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "common/defs.hpp"
|
||||
#include "common/file.hpp"
|
||||
#include "common/rom.hpp"
|
||||
#include "common/romdrivers.hpp"
|
||||
#include "common/util.hpp"
|
||||
#include "main/app/app.hpp"
|
||||
#include "main/app/romactions.hpp"
|
||||
|
@ -235,8 +235,10 @@ void AudioTestScreen::update(ui::Context &ctx) {
|
||||
|
||||
if (ctx.buttons.pressed(ui::BTN_START)) {
|
||||
if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) {
|
||||
#if 0
|
||||
io::setMiscOutput(io::MISC_AMP_ENABLE, false);
|
||||
io::setMiscOutput(io::MISC_CDDA_ENABLE, false);
|
||||
#endif
|
||||
|
||||
ctx.show(APP->_testMenuScreen, true, true);
|
||||
} else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user