From 2426c58b9f68d19a745ba41073d567827cb38c32 Mon Sep 17 00:00:00 2001 From: Sebastian Sumpf Date: Tue, 6 Oct 2015 15:36:34 +0200 Subject: [PATCH] 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 --- repos/os/src/drivers/ahci/ahci.cc | 6 +- repos/os/src/drivers/ahci/ahci.h | 64 ++------- repos/os/src/drivers/ahci/ata_driver.h | 185 +++++++++++++++++++++---- repos/os/src/drivers/ahci/main.cc | 2 +- 4 files changed, 173 insertions(+), 84 deletions(-) diff --git a/repos/os/src/drivers/ahci/ahci.cc b/repos/os/src/drivers/ahci/ahci.cc index b6262bb36..5b6d40a5d 100644 --- a/repos/os/src/drivers/ahci/ahci.cc +++ b/repos/os/src/drivers/ahci/ahci.cc @@ -106,18 +106,18 @@ struct Ahci PINF("\tversion: %x.%04x", hba.read(), hba.read()); 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()); - unsigned avaiable = hba.read(); + unsigned available = hba.read(); 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); diff --git a/repos/os/src/drivers/ahci/ahci.h b/repos/os/src/drivers/ahci/ahci.h index 357d2519a..15b019e79 100644 --- a/repos/os/src/drivers/ahci/ahci.h +++ b/repos/os/src/drivers/ahci/ahci.h @@ -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() + 1; } unsigned command_slots() { return read() + 1; } - bool ncg() { return !!read(); } + bool ncq() { return !!read(); } bool supports_64bit(){ return !!read(); } /** @@ -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() + 1, - read()); - PLOG("\t\tnumer of sectors: %llu", read()); - PLOG("\t\tmultiple logical blocks per physical: %s", - read() ? "yes" : "no"); - PLOG("\t\tlogical blocks per physical: %u", - 1U << read()); - PLOG("\t\tlogical block size is above 512 byte: %s", - read() ? "yes" : "no"); - PLOG("\t\twords (16bit) per logical block: %u", - read()); - PLOG("\t\toffset of first logical block within physical: %u", - read()); - } -}; - - /** * 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 */ diff --git a/repos/os/src/drivers/ahci/ata_driver.h b/repos/os/src/drivers/ahci/ata_driver.h index 44ed911d9..fff3bb4a7 100644 --- a/repos/os/src/drivers/ahci/ata_driver.h +++ b/repos/os/src/drivers/ahci/ata_driver.h @@ -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() + 1, + read()); + PLOG("\t\tnumer of sectors: %llu", read()); + PLOG("\t\tmultiple logical blocks per physical: %s", + read() ? "yes" : "no"); + PLOG("\t\tlogical blocks per physical: %u", + 1U << read()); + PLOG("\t\tlogical block size is above 512 byte: %s", + read() ? "yes" : "no"); + PLOG("\t\twords (16bit) per logical block: %u", + read()); + PLOG("\t\toffset of first logical block within physical: %u", + read()); + } +}; + +/** + * 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(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.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 info; - Block::Packet_descriptor pending[32]; + Genode::Lazy_volatile_object 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(read ? 0 : 1); header.clear_byte_count(); - /* set pending */ - Port::write(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())) { - 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() && hba.ncq(); + } + void check_device() { - if (!info->read() || - !hba.ncg()) { - PERR("Device does not support native command queuing: abort"); - state_change(); - return; - } - cmd_slots = min((int)cmd_slots, info->read() + 1); + + /* no native command queueing */ + if (!ncq_support()) + cmd_slots = 1; + state = READY; state_change(); } diff --git a/repos/os/src/drivers/ahci/main.cc b/repos/os/src/drivers/ahci/main.cc index 296ec09a8..02c411a61 100644 --- a/repos/os/src/drivers/ahci/main.cc +++ b/repos/os/src/drivers/ahci/main.cc @@ -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(); }