ahci: support for non-ncq ATA devices
* use '_dma_ext' or '_fdpma' commands * handle interrupts depending on mode of operation * spelling fixes * move ATA 'Idendity' struct to ata header issue #1734
This commit is contained in:
parent
a608d48ddf
commit
2426c58b9f
|
@ -106,18 +106,18 @@ struct Ahci
|
||||||
PINF("\tversion: %x.%04x", hba.read<Hba::Version::Major>(),
|
PINF("\tversion: %x.%04x", hba.read<Hba::Version::Major>(),
|
||||||
hba.read<Hba::Version::Minor>());
|
hba.read<Hba::Version::Minor>());
|
||||||
PINF("\tcommand slots: %u", hba.command_slots());
|
PINF("\tcommand slots: %u", hba.command_slots());
|
||||||
PINF("\tnative command queuing: %s", hba.ncg() ? "yes" : "no");
|
PINF("\tnative command queuing: %s", hba.ncq() ? "yes" : "no");
|
||||||
PINF("\t64 bit support: %s", hba.supports_64bit() ? "yes" : "no");
|
PINF("\t64 bit support: %s", hba.supports_64bit() ? "yes" : "no");
|
||||||
}
|
}
|
||||||
|
|
||||||
void scan_ports()
|
void scan_ports()
|
||||||
{
|
{
|
||||||
PINF("\tnumber of ports: %u pi: %x", hba.port_count(), hba.read<Hba::Pi>());
|
PINF("\tnumber of ports: %u pi: %x", hba.port_count(), hba.read<Hba::Pi>());
|
||||||
unsigned avaiable = hba.read<Hba::Pi>();
|
unsigned available = hba.read<Hba::Pi>();
|
||||||
for (unsigned i = 0; i < hba.port_count(); i++) {
|
for (unsigned i = 0; i < hba.port_count(); i++) {
|
||||||
|
|
||||||
/* check if port is implemented */
|
/* check if port is implemented */
|
||||||
if (!(avaiable & (1U << i)))
|
if (!(available & (1U << i)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Port port(hba, platform_hba, i);
|
Port port(hba, platform_hba, i);
|
||||||
|
|
|
@ -82,13 +82,13 @@ struct Hba : Genode::Attached_mmio
|
||||||
struct Np : Bitfield<0, 4> { }; /* number of ports */
|
struct Np : Bitfield<0, 4> { }; /* number of ports */
|
||||||
struct Ncs : Bitfield<8, 5> { }; /* number of command slots */
|
struct Ncs : Bitfield<8, 5> { }; /* number of command slots */
|
||||||
struct Iss : Bitfield<20, 4> { }; /* interface speed support */
|
struct Iss : Bitfield<20, 4> { }; /* interface speed support */
|
||||||
struct Sncg : Bitfield<30, 1> { }; /* supports native command queuing */
|
struct Sncq : Bitfield<30, 1> { }; /* supports native command queuing */
|
||||||
struct Sa64 : Bitfield<31, 1> { }; /* supports 64 bit addressing */
|
struct Sa64 : Bitfield<31, 1> { }; /* supports 64 bit addressing */
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned port_count() { return read<Cap::Np>() + 1; }
|
unsigned port_count() { return read<Cap::Np>() + 1; }
|
||||||
unsigned command_slots() { return read<Cap::Ncs>() + 1; }
|
unsigned command_slots() { return read<Cap::Ncs>() + 1; }
|
||||||
bool ncg() { return !!read<Cap::Sncg>(); }
|
bool ncq() { return !!read<Cap::Sncq>(); }
|
||||||
bool supports_64bit(){ return !!read<Cap::Sa64>(); }
|
bool supports_64bit(){ return !!read<Cap::Sa64>(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -384,56 +384,6 @@ struct Command_table
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct Identity : Genode::Mmio
|
|
||||||
{
|
|
||||||
Identity(Genode::addr_t base) : Mmio(base) { }
|
|
||||||
|
|
||||||
struct Queue_depth : Register<0x96, 16>
|
|
||||||
{
|
|
||||||
struct Max_depth : Bitfield<0, 5> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Sata_caps : Register<0x98, 16>
|
|
||||||
{
|
|
||||||
struct Ncq_support : Bitfield<8, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Sector_count : Register<0xc8, 64> { };
|
|
||||||
|
|
||||||
struct Logical_block : Register<0xd4, 16>
|
|
||||||
{
|
|
||||||
struct Per_physical : Bitfield<0, 3> { }; /* 2^X logical per physical */
|
|
||||||
struct Longer_512 : Bitfield<12, 1> { };
|
|
||||||
struct Multiple : Bitfield<13, 1> { }; /* multiple logical blocks per physical */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Logical_words : Register<0xea, 32> { }; /* words (16 bit) per logical block */
|
|
||||||
|
|
||||||
struct Alignment : Register<0x1a2, 16>
|
|
||||||
{
|
|
||||||
struct Logical_offset : Bitfield<0, 14> { }; /* offset first logical block in physical */
|
|
||||||
};
|
|
||||||
|
|
||||||
void info()
|
|
||||||
{
|
|
||||||
PLOG("\t\tqueue depth: %u ncg: %u",
|
|
||||||
read<Queue_depth::Max_depth>() + 1,
|
|
||||||
read<Sata_caps::Ncq_support>());
|
|
||||||
PLOG("\t\tnumer of sectors: %llu", read<Sector_count>());
|
|
||||||
PLOG("\t\tmultiple logical blocks per physical: %s",
|
|
||||||
read<Logical_block::Multiple>() ? "yes" : "no");
|
|
||||||
PLOG("\t\tlogical blocks per physical: %u",
|
|
||||||
1U << read<Logical_block::Per_physical>());
|
|
||||||
PLOG("\t\tlogical block size is above 512 byte: %s",
|
|
||||||
read<Logical_block::Longer_512>() ? "yes" : "no");
|
|
||||||
PLOG("\t\twords (16bit) per logical block: %u",
|
|
||||||
read<Logical_words>());
|
|
||||||
PLOG("\t\toffset of first logical block within physical: %u",
|
|
||||||
read<Alignment::Logical_offset>());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AHCI port
|
* AHCI port
|
||||||
*/
|
*/
|
||||||
|
@ -539,6 +489,12 @@ struct Port : Genode::Mmio
|
||||||
struct Prcs : Bitfield<22, 1> { }; /* PhyRdy change status */
|
struct Prcs : Bitfield<22, 1> { }; /* PhyRdy change status */
|
||||||
struct Infs : Bitfield<26, 1> { }; /* interface non-fatal error */
|
struct Infs : Bitfield<26, 1> { }; /* interface non-fatal error */
|
||||||
struct Ifs : Bitfield<27, 1> { }; /* interface fatal error */
|
struct Ifs : Bitfield<27, 1> { }; /* interface fatal error */
|
||||||
|
|
||||||
|
/* ncq irq */
|
||||||
|
struct Fpdma_irq : Sdbs { };
|
||||||
|
|
||||||
|
/* non-ncq irq */
|
||||||
|
struct Dma_ext_irq : Bitfield<0, 3> { };
|
||||||
};
|
};
|
||||||
|
|
||||||
void ack_irq()
|
void ack_irq()
|
||||||
|
@ -562,9 +518,9 @@ struct Port : Genode::Mmio
|
||||||
struct Ie : Register<0x14, 32, 1>
|
struct Ie : Register<0x14, 32, 1>
|
||||||
{
|
{
|
||||||
struct Dhre : Bitfield<0, 1> { }; /* device to host register FIS interrupt */
|
struct Dhre : Bitfield<0, 1> { }; /* device to host register FIS interrupt */
|
||||||
struct Pse : Bitfield<1, 2> { }; /* PIO setup FIS interrupt */
|
struct Pse : Bitfield<1, 1> { }; /* PIO setup FIS interrupt */
|
||||||
struct Dse : Bitfield<2, 1> { }; /* DMA setup FIS interrupt */
|
struct Dse : Bitfield<2, 1> { }; /* DMA setup FIS interrupt */
|
||||||
struct Sdbe : Bitfield<3, 1> { }; /* set device bits FIS interrupt (ncg) */
|
struct Sdbe : Bitfield<3, 1> { }; /* set device bits FIS interrupt (ncq) */
|
||||||
struct Ufe : Bitfield<4, 1> { }; /* unknown FIS */
|
struct Ufe : Bitfield<4, 1> { }; /* unknown FIS */
|
||||||
struct Dpe : Bitfield<5, 1> { }; /* descriptor processed */
|
struct Dpe : Bitfield<5, 1> { }; /* descriptor processed */
|
||||||
struct Ifne : Bitfield<26, 1> { }; /* interface non-fatal error */
|
struct Ifne : Bitfield<26, 1> { }; /* interface non-fatal error */
|
||||||
|
|
|
@ -18,10 +18,126 @@
|
||||||
|
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return data of 'identify_device' ATA command
|
||||||
|
*/
|
||||||
|
struct Identity : Genode::Mmio
|
||||||
|
{
|
||||||
|
Identity(Genode::addr_t base) : Mmio(base) { }
|
||||||
|
|
||||||
|
struct Queue_depth : Register<0x96, 16>
|
||||||
|
{
|
||||||
|
struct Max_depth : Bitfield<0, 5> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Sata_caps : Register<0x98, 16>
|
||||||
|
{
|
||||||
|
struct Ncq_support : Bitfield<8, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Sector_count : Register<0xc8, 64> { };
|
||||||
|
|
||||||
|
struct Logical_block : Register<0xd4, 16>
|
||||||
|
{
|
||||||
|
struct Per_physical : Bitfield<0, 3> { }; /* 2^X logical per physical */
|
||||||
|
struct Longer_512 : Bitfield<12, 1> { };
|
||||||
|
struct Multiple : Bitfield<13, 1> { }; /* multiple logical blocks per physical */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Logical_words : Register<0xea, 32> { }; /* words (16 bit) per logical block */
|
||||||
|
|
||||||
|
struct Alignment : Register<0x1a2, 16>
|
||||||
|
{
|
||||||
|
struct Logical_offset : Bitfield<0, 14> { }; /* offset first logical block in physical */
|
||||||
|
};
|
||||||
|
|
||||||
|
void info()
|
||||||
|
{
|
||||||
|
PLOG("\t\tqueue depth: %u ncq: %u",
|
||||||
|
read<Queue_depth::Max_depth>() + 1,
|
||||||
|
read<Sata_caps::Ncq_support>());
|
||||||
|
PLOG("\t\tnumer of sectors: %llu", read<Sector_count>());
|
||||||
|
PLOG("\t\tmultiple logical blocks per physical: %s",
|
||||||
|
read<Logical_block::Multiple>() ? "yes" : "no");
|
||||||
|
PLOG("\t\tlogical blocks per physical: %u",
|
||||||
|
1U << read<Logical_block::Per_physical>());
|
||||||
|
PLOG("\t\tlogical block size is above 512 byte: %s",
|
||||||
|
read<Logical_block::Longer_512>() ? "yes" : "no");
|
||||||
|
PLOG("\t\twords (16bit) per logical block: %u",
|
||||||
|
read<Logical_words>());
|
||||||
|
PLOG("\t\toffset of first logical block within physical: %u",
|
||||||
|
read<Alignment::Logical_offset>());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commands to distinguish between ncq and non-ncq operation
|
||||||
|
*/
|
||||||
|
struct Io_command
|
||||||
|
{
|
||||||
|
virtual void command(Port &por,
|
||||||
|
Command_table &table,
|
||||||
|
bool read,
|
||||||
|
Block::sector_t block_number,
|
||||||
|
size_t count,
|
||||||
|
unsigned slot) = 0;
|
||||||
|
|
||||||
|
virtual void handle_irq(Port &port, Port::Is::access_t status) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Ncq_command : Io_command
|
||||||
|
{
|
||||||
|
void command(Port &port,
|
||||||
|
Command_table &table,
|
||||||
|
bool read,
|
||||||
|
Block::sector_t block_number,
|
||||||
|
size_t count,
|
||||||
|
unsigned slot) override
|
||||||
|
{
|
||||||
|
table.fis.fpdma(read, block_number, count, slot);
|
||||||
|
/* set pending */
|
||||||
|
port.write<Port::Sact>(1U << slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_irq(Port &port, Port::Is::access_t status) override
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Check for completions of other requests immediately
|
||||||
|
*/
|
||||||
|
while (Port::Is::Sdbs::get(status = port.read<Port::Is>()))
|
||||||
|
port.ack_irq();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dma_ext_command : Io_command
|
||||||
|
{
|
||||||
|
void command(Port &port,
|
||||||
|
Command_table &table,
|
||||||
|
bool read,
|
||||||
|
Block::sector_t block_number,
|
||||||
|
size_t count,
|
||||||
|
unsigned /* slot */) override
|
||||||
|
{
|
||||||
|
table.fis.dma_ext(read, block_number, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_irq(Port &port, Port::Is::access_t status) override
|
||||||
|
{
|
||||||
|
if (Port::Is::Dma_ext_irq::get(status))
|
||||||
|
port.ack_irq();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drivers using ncq- and non-ncq commands
|
||||||
|
*/
|
||||||
struct Ata_driver : Port_driver
|
struct Ata_driver : Port_driver
|
||||||
{
|
{
|
||||||
Genode::Lazy_volatile_object<Identity> info;
|
Genode::Lazy_volatile_object<Identity> info;
|
||||||
Block::Packet_descriptor pending[32];
|
Io_command *io_cmd = nullptr;
|
||||||
|
Block::Packet_descriptor pending[32];
|
||||||
|
|
||||||
Ata_driver(Port &port, Signal_context_capability state_change)
|
Ata_driver(Port &port, Signal_context_capability state_change)
|
||||||
: Port_driver(port, state_change)
|
: Port_driver(port, state_change)
|
||||||
|
@ -30,6 +146,12 @@ struct Ata_driver : Port_driver
|
||||||
identify_device();
|
identify_device();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~Ata_driver()
|
||||||
|
{
|
||||||
|
if (io_cmd)
|
||||||
|
destroy (Genode::env()->heap(), io_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
unsigned find_free_cmd_slot()
|
unsigned find_free_cmd_slot()
|
||||||
{
|
{
|
||||||
for (unsigned slot = 0; slot < cmd_slots; slot++)
|
for (unsigned slot = 0; slot < cmd_slots; slot++)
|
||||||
|
@ -90,15 +212,15 @@ struct Ata_driver : Port_driver
|
||||||
|
|
||||||
/* setup fis */
|
/* setup fis */
|
||||||
Command_table table(command_table_addr(slot), phys, count * block_size());
|
Command_table table(command_table_addr(slot), phys, count * block_size());
|
||||||
table.fis.fpdma(read, block_number, count, slot);
|
|
||||||
|
/* set ATA command */
|
||||||
|
io_cmd->command(*this, table, read, block_number, count, slot);
|
||||||
|
|
||||||
/* set or clear write flag in command header */
|
/* set or clear write flag in command header */
|
||||||
Command_header header(command_header_addr(slot));
|
Command_header header(command_header_addr(slot));
|
||||||
header.write<Command_header::Bits::W>(read ? 0 : 1);
|
header.write<Command_header::Bits::W>(read ? 0 : 1);
|
||||||
header.clear_byte_count();
|
header.clear_byte_count();
|
||||||
|
|
||||||
/* set pending */
|
|
||||||
Port::write<Sact>(1U << slot);
|
|
||||||
execute(slot);
|
execute(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,39 +236,50 @@ struct Ata_driver : Port_driver
|
||||||
if (verbose)
|
if (verbose)
|
||||||
PDBG("irq: %x state: %u", status, state);
|
PDBG("irq: %x state: %u", status, state);
|
||||||
|
|
||||||
if ((Port::Is::Dss::get(status) || Port::Is::Pss::get(status)) &&
|
switch (state) {
|
||||||
state == IDENTIFY) {
|
|
||||||
info.construct(device_info);
|
|
||||||
if (verbose)
|
|
||||||
info->info();
|
|
||||||
|
|
||||||
check_device();
|
case IDENTIFY:
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
if (Port::Is::Dss::get(status) || Port::Is::Pss::get(status)) {
|
||||||
* When packets get acked, new ones may be inserted by the block driver
|
info.construct(device_info);
|
||||||
* layer, these new packet requests might be finished during 'ack_packets',
|
if (verbose)
|
||||||
* so clear the IRQ and re-read after 'ack_packets'
|
info->info();
|
||||||
*/
|
|
||||||
while (Is::Sdbs::get(status = Port::read<Is>())) {
|
check_device();
|
||||||
ack_irq();
|
if (ncq_support())
|
||||||
|
io_cmd = new (Genode::env()->heap()) Ncq_command();
|
||||||
|
else
|
||||||
|
io_cmd = new (Genode::env()->heap()) Dma_ext_command();
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READY:
|
||||||
|
|
||||||
|
io_cmd->handle_irq(*this, status);
|
||||||
ack_packets();
|
ack_packets();
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ncq_support()
|
||||||
|
{
|
||||||
|
return info->read<Identity::Sata_caps::Ncq_support>() && hba.ncq();
|
||||||
|
}
|
||||||
|
|
||||||
void check_device()
|
void check_device()
|
||||||
{
|
{
|
||||||
if (!info->read<Identity::Sata_caps::Ncq_support>() ||
|
|
||||||
!hba.ncg()) {
|
|
||||||
PERR("Device does not support native command queuing: abort");
|
|
||||||
state_change();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_slots = min((int)cmd_slots,
|
cmd_slots = min((int)cmd_slots,
|
||||||
info->read<Identity::Queue_depth::Max_depth >() + 1);
|
info->read<Identity::Queue_depth::Max_depth >() + 1);
|
||||||
|
|
||||||
|
/* no native command queueing */
|
||||||
|
if (!ncq_support())
|
||||||
|
cmd_slots = 1;
|
||||||
|
|
||||||
state = READY;
|
state = READY;
|
||||||
state_change();
|
state_change();
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ class Block::Root_multiple_clients : public Root_component< ::Session_component>
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Ahci_driver::is_avail(num)) {
|
if (!Ahci_driver::is_avail(num)) {
|
||||||
PERR("Device %ld not avaiable", num);
|
PERR("Device %ld not available", num);
|
||||||
throw Root::Unavailable();
|
throw Root::Unavailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue