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>(),
|
||||
hba.read<Hba::Version::Minor>());
|
||||
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");
|
||||
}
|
||||
|
||||
void scan_ports()
|
||||
{
|
||||
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++) {
|
||||
|
||||
/* check if port is implemented */
|
||||
if (!(avaiable & (1U << i)))
|
||||
if (!(available & (1U << i)))
|
||||
continue;
|
||||
|
||||
Port port(hba, platform_hba, i);
|
||||
|
|
|
@ -82,13 +82,13 @@ struct Hba : Genode::Attached_mmio
|
|||
struct Np : Bitfield<0, 4> { }; /* number of ports */
|
||||
struct Ncs : Bitfield<8, 5> { }; /* number of command slots */
|
||||
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 */
|
||||
};
|
||||
|
||||
unsigned port_count() { return read<Cap::Np>() + 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>(); }
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -539,6 +489,12 @@ struct Port : Genode::Mmio
|
|||
struct Prcs : Bitfield<22, 1> { }; /* PhyRdy change status */
|
||||
struct Infs : Bitfield<26, 1> { }; /* interface non-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()
|
||||
|
@ -562,9 +518,9 @@ struct Port : Genode::Mmio
|
|||
struct Ie : Register<0x14, 32, 1>
|
||||
{
|
||||
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 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 Dpe : Bitfield<5, 1> { }; /* descriptor processed */
|
||||
struct Ifne : Bitfield<26, 1> { }; /* interface non-fatal error */
|
||||
|
|
|
@ -18,10 +18,126 @@
|
|||
|
||||
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
|
||||
{
|
||||
Genode::Lazy_volatile_object<Identity> info;
|
||||
Block::Packet_descriptor pending[32];
|
||||
Genode::Lazy_volatile_object<Identity> info;
|
||||
Io_command *io_cmd = nullptr;
|
||||
Block::Packet_descriptor pending[32];
|
||||
|
||||
Ata_driver(Port &port, Signal_context_capability state_change)
|
||||
: Port_driver(port, state_change)
|
||||
|
@ -30,6 +146,12 @@ struct Ata_driver : Port_driver
|
|||
identify_device();
|
||||
}
|
||||
|
||||
~Ata_driver()
|
||||
{
|
||||
if (io_cmd)
|
||||
destroy (Genode::env()->heap(), io_cmd);
|
||||
}
|
||||
|
||||
unsigned find_free_cmd_slot()
|
||||
{
|
||||
for (unsigned slot = 0; slot < cmd_slots; slot++)
|
||||
|
@ -90,15 +212,15 @@ struct Ata_driver : Port_driver
|
|||
|
||||
/* setup fis */
|
||||
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 */
|
||||
Command_header header(command_header_addr(slot));
|
||||
header.write<Command_header::Bits::W>(read ? 0 : 1);
|
||||
header.clear_byte_count();
|
||||
|
||||
/* set pending */
|
||||
Port::write<Sact>(1U << slot);
|
||||
execute(slot);
|
||||
}
|
||||
|
||||
|
@ -114,39 +236,50 @@ struct Ata_driver : Port_driver
|
|||
if (verbose)
|
||||
PDBG("irq: %x state: %u", status, state);
|
||||
|
||||
if ((Port::Is::Dss::get(status) || Port::Is::Pss::get(status)) &&
|
||||
state == IDENTIFY) {
|
||||
info.construct(device_info);
|
||||
if (verbose)
|
||||
info->info();
|
||||
switch (state) {
|
||||
|
||||
check_device();
|
||||
}
|
||||
case IDENTIFY:
|
||||
|
||||
/*
|
||||
* When packets get acked, new ones may be inserted by the block driver
|
||||
* layer, these new packet requests might be finished during 'ack_packets',
|
||||
* so clear the IRQ and re-read after 'ack_packets'
|
||||
*/
|
||||
while (Is::Sdbs::get(status = Port::read<Is>())) {
|
||||
ack_irq();
|
||||
if (Port::Is::Dss::get(status) || Port::Is::Pss::get(status)) {
|
||||
info.construct(device_info);
|
||||
if (verbose)
|
||||
info->info();
|
||||
|
||||
check_device();
|
||||
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();
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
stop();
|
||||
}
|
||||
|
||||
bool ncq_support()
|
||||
{
|
||||
return info->read<Identity::Sata_caps::Ncq_support>() && hba.ncq();
|
||||
}
|
||||
|
||||
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,
|
||||
info->read<Identity::Queue_depth::Max_depth >() + 1);
|
||||
|
||||
/* no native command queueing */
|
||||
if (!ncq_support())
|
||||
cmd_slots = 1;
|
||||
|
||||
state = READY;
|
||||
state_change();
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ class Block::Root_multiple_clients : public Root_component< ::Session_component>
|
|||
}
|
||||
|
||||
if (!Ahci_driver::is_avail(num)) {
|
||||
PERR("Device %ld not avaiable", num);
|
||||
PERR("Device %ld not available", num);
|
||||
throw Root::Unavailable();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue