Kernel: Improve ATA device initialization
This does better detection of empty ports and fixes floating bus read-value from 0x00 to 0xFF.
This commit is contained in:
parent
627ca432cc
commit
1de50a2a94
|
@ -35,11 +35,10 @@ namespace Kernel
|
||||||
{}
|
{}
|
||||||
BAN::ErrorOr<void> initialize();
|
BAN::ErrorOr<void> initialize();
|
||||||
|
|
||||||
void select_device(bool secondary);
|
void select_device(bool is_secondary);
|
||||||
BAN::ErrorOr<DeviceType> identify(bool secondary, BAN::Span<uint16_t> buffer);
|
BAN::ErrorOr<DeviceType> identify(bool is_secondary, BAN::Span<uint16_t> buffer);
|
||||||
|
|
||||||
void block_until_irq();
|
BAN::ErrorOr<void> block_until_irq();
|
||||||
//uint8_t device_index(const ATADevice&) const;
|
|
||||||
|
|
||||||
uint8_t io_read(uint16_t);
|
uint8_t io_read(uint16_t);
|
||||||
void io_write(uint16_t, uint8_t);
|
void io_write(uint16_t, uint8_t);
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace Kernel
|
||||||
if (bus_ptr == nullptr)
|
if (bus_ptr == nullptr)
|
||||||
return BAN::Error::from_errno(ENOMEM);
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
auto bus = BAN::RefPtr<ATABus>::adopt(bus_ptr);
|
auto bus = BAN::RefPtr<ATABus>::adopt(bus_ptr);
|
||||||
if (bus->io_read(ATA_PORT_STATUS) == 0x00)
|
if (bus->io_read(ATA_PORT_STATUS) == 0xFF)
|
||||||
{
|
{
|
||||||
dprintln("Floating ATA bus on IO port 0x{H}", base);
|
dprintln("Floating ATA bus on IO port 0x{H}", base);
|
||||||
return BAN::Error::from_errno(ENODEV);
|
return BAN::Error::from_errno(ENODEV);
|
||||||
|
@ -69,94 +69,83 @@ namespace Kernel
|
||||||
SystemTimer::get().sleep_ns(400);
|
SystemTimer::get().sleep_ns(400);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ATABus::select_device(bool secondary)
|
void ATABus::select_device(bool is_secondary)
|
||||||
{
|
{
|
||||||
io_write(ATA_PORT_DRIVE_SELECT, 0xA0 | ((uint8_t)secondary << 4));
|
io_write(ATA_PORT_DRIVE_SELECT, 0xA0 | ((uint8_t)is_secondary << 4));
|
||||||
select_delay();
|
select_delay();
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<ATABus::DeviceType> ATABus::identify(bool secondary, BAN::Span<uint16_t> buffer)
|
BAN::ErrorOr<ATABus::DeviceType> ATABus::identify(bool is_secondary, BAN::Span<uint16_t> buffer)
|
||||||
{
|
{
|
||||||
// Try to detect whether port contains device
|
select_device(is_secondary);
|
||||||
uint8_t status = io_read(ATA_PORT_STATUS);
|
|
||||||
if (status & ATA_STATUS_BSY)
|
|
||||||
{
|
|
||||||
uint64_t timeout = SystemTimer::get().ms_since_boot() + s_ata_timeout_ms;
|
|
||||||
while ((status = io_read(ATA_PORT_STATUS)) & ATA_STATUS_BSY)
|
|
||||||
{
|
|
||||||
if (SystemTimer::get().ms_since_boot() >= timeout)
|
|
||||||
{
|
|
||||||
dprintln("BSY flag clear timeout, assuming no drive on port");
|
|
||||||
return BAN::Error::from_errno(ETIMEDOUT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (__builtin_popcount(status) >= 4)
|
|
||||||
{
|
|
||||||
dprintln("STATUS contains garbage, assuming no drive on port");
|
|
||||||
return BAN::Error::from_errno(EINVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
select_device(secondary);
|
|
||||||
|
|
||||||
// Disable interrupts
|
// Disable interrupts
|
||||||
io_write(ATA_PORT_CONTROL, ATA_CONTROL_nIEN);
|
io_write(ATA_PORT_CONTROL, ATA_CONTROL_nIEN);
|
||||||
|
|
||||||
|
io_write(ATA_PORT_SECTOR_COUNT, 0);
|
||||||
|
io_write(ATA_PORT_LBA0, 0);
|
||||||
|
io_write(ATA_PORT_LBA1, 0);
|
||||||
|
io_write(ATA_PORT_LBA2, 0);
|
||||||
io_write(ATA_PORT_COMMAND, ATA_COMMAND_IDENTIFY);
|
io_write(ATA_PORT_COMMAND, ATA_COMMAND_IDENTIFY);
|
||||||
SystemTimer::get().sleep_ms(1);
|
SystemTimer::get().sleep_ms(1);
|
||||||
|
|
||||||
// No device on port
|
// No device on port
|
||||||
if (io_read(ATA_PORT_STATUS) == 0)
|
if (io_read(ATA_PORT_STATUS) == 0)
|
||||||
return BAN::Error::from_errno(EINVAL);
|
return BAN::Error::from_errno(ENODEV);
|
||||||
|
|
||||||
DeviceType type = DeviceType::ATA;
|
TRY(wait(false));
|
||||||
|
|
||||||
if (wait(true).is_error())
|
const uint8_t lba1 = io_read(ATA_PORT_LBA1);
|
||||||
|
const uint8_t lba2 = io_read(ATA_PORT_LBA2);
|
||||||
|
|
||||||
|
auto device_type = DeviceType::ATA;
|
||||||
|
if (lba1 || lba2)
|
||||||
{
|
{
|
||||||
uint8_t lba1 = io_read(ATA_PORT_LBA1);
|
|
||||||
uint8_t lba2 = io_read(ATA_PORT_LBA2);
|
|
||||||
|
|
||||||
if (lba1 == 0x14 && lba2 == 0xEB)
|
if (lba1 == 0x14 && lba2 == 0xEB)
|
||||||
type = DeviceType::ATAPI;
|
device_type = DeviceType::ATAPI;
|
||||||
else if (lba1 == 0x69 && lba2 == 0x96)
|
else if (lba1 == 0x69 && lba2 == 0x96)
|
||||||
type = DeviceType::ATAPI;
|
device_type = DeviceType::ATAPI;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
dprintln("Unsupported device type");
|
dprintln("Unsupported device type {2H} {2H}", lba1, lba2);
|
||||||
return BAN::Error::from_errno(EINVAL);
|
return BAN::Error::from_errno(EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
io_write(ATA_PORT_COMMAND, ATA_COMMAND_IDENTIFY_PACKET);
|
io_write(ATA_PORT_COMMAND, ATA_COMMAND_IDENTIFY_PACKET);
|
||||||
SystemTimer::get().sleep_ms(1);
|
SystemTimer::get().sleep_ms(1);
|
||||||
|
|
||||||
if (auto res = wait(true); res.is_error())
|
|
||||||
{
|
|
||||||
dprintln("Fatal error: {}", res.error());
|
|
||||||
return BAN::Error::from_errno(EINVAL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TRY(wait(true));
|
||||||
|
|
||||||
ASSERT(buffer.size() >= 256);
|
ASSERT(buffer.size() >= 256);
|
||||||
read_buffer(ATA_PORT_DATA, buffer.data(), 256);
|
read_buffer(ATA_PORT_DATA, buffer.data(), 256);
|
||||||
return type;
|
return device_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ATABus::handle_irq()
|
void ATABus::handle_irq()
|
||||||
{
|
{
|
||||||
ASSERT(!m_has_got_irq);
|
|
||||||
if (io_read(ATA_PORT_STATUS) & ATA_STATUS_ERR)
|
if (io_read(ATA_PORT_STATUS) & ATA_STATUS_ERR)
|
||||||
dprintln("ATA Error: {}", error());
|
dprintln("ATA Error: {}", error());
|
||||||
m_has_got_irq.store(true);
|
|
||||||
|
bool expected { false };
|
||||||
|
[[maybe_unused]] bool success = m_has_got_irq.compare_exchange(expected, true);
|
||||||
|
ASSERT(success);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ATABus::block_until_irq()
|
BAN::ErrorOr<void> ATABus::block_until_irq()
|
||||||
{
|
{
|
||||||
|
const uint64_t timeout_ms = SystemTimer::get().ms_since_boot() + s_ata_timeout_ms;
|
||||||
|
|
||||||
bool expected { true };
|
bool expected { true };
|
||||||
while (!m_has_got_irq.compare_exchange(expected, false))
|
while (!m_has_got_irq.compare_exchange(expected, false))
|
||||||
{
|
{
|
||||||
|
if (SystemTimer::get().ms_since_boot() >= timeout_ms)
|
||||||
|
return BAN::Error::from_errno(ETIMEDOUT);
|
||||||
Processor::pause();
|
Processor::pause();
|
||||||
expected = true;
|
expected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t ATABus::io_read(uint16_t port)
|
uint8_t ATABus::io_read(uint16_t port)
|
||||||
|
|
Loading…
Reference in New Issue