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:
Sebastian Sumpf 2015-10-06 15:36:34 +02:00 committed by Christian Helmuth
parent a608d48ddf
commit 2426c58b9f
4 changed files with 173 additions and 84 deletions

View File

@ -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);

View File

@ -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 */

View File

@ -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();
}

View File

@ -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();
}