mirror of
https://github.com/genodelabs/genode.git
synced 2025-05-22 18:24:05 +00:00
ahci: new AHCI driver implementation
Supports native command queueing and multiple ports.
This commit is contained in:
parent
33bc63e7c3
commit
7910b5146f
@ -1,4 +1,4 @@
|
|||||||
set mkfs_cmd mkfs.vfat
|
set mkfs_cmd [check_installed mkfs.vfat]
|
||||||
set mkfs_opts "-F32"
|
set mkfs_opts "-F32"
|
||||||
set filesystem ffat
|
set filesystem ffat
|
||||||
|
|
||||||
|
@ -6,9 +6,7 @@
|
|||||||
|
|
||||||
set use_sd_card_drv [expr [have_spec omap4] || [have_spec platform_arndale] || [have_spec pl180]]
|
set use_sd_card_drv [expr [have_spec omap4] || [have_spec platform_arndale] || [have_spec pl180]]
|
||||||
set use_ahci_drv [have_spec x86]
|
set use_ahci_drv [have_spec x86]
|
||||||
|
set mkfs [check_installed mkfs.vfat]
|
||||||
if {[catch { exec which mkfs.vfat } ]} {
|
|
||||||
puts stderr "Error: mkfs.vfat not installed, aborting test"; exit }
|
|
||||||
|
|
||||||
if {[expr [have_spec linux] || [have_spec hw_odroid_xu]]} {
|
if {[expr [have_spec linux] || [have_spec hw_odroid_xu]]} {
|
||||||
puts "Run script does not support this platform"; exit }
|
puts "Run script does not support this platform"; exit }
|
||||||
@ -85,22 +83,30 @@ append_if [have_spec acpi] config {
|
|||||||
<service name="IRQ" />
|
<service name="IRQ" />
|
||||||
</provides>
|
</provides>
|
||||||
<route>
|
<route>
|
||||||
<service name="PCI"> <any-child /> </service>
|
|
||||||
<any-service> <parent/> <any-child /> </any-service>
|
<any-service> <parent/> <any-child /> </any-service>
|
||||||
</route>
|
</route>
|
||||||
|
<config>
|
||||||
|
<policy label="acpi_drv"><pci class="ALL" /></policy>
|
||||||
|
<policy label="ahci_drv"><pci class="AHCI" /></policy>
|
||||||
|
</config>
|
||||||
</start>}
|
</start>}
|
||||||
|
|
||||||
append_if [expr ![have_spec acpi] && [have_spec pci]] config {
|
append_if [expr ![have_spec acpi] && [have_spec pci]] config {
|
||||||
<start name="pci_drv">
|
<start name="pci_drv">
|
||||||
<resource name="RAM" quantum="5M" constrain_phys="yes"/>
|
<resource name="RAM" quantum="5M" constrain_phys="yes"/>
|
||||||
<provides><service name="PCI"/></provides>
|
<provides><service name="PCI"/></provides>
|
||||||
|
<config>
|
||||||
|
<policy label="ahci_drv"/><pci class="AHCI" /></policy>
|
||||||
|
</config>
|
||||||
</start>}
|
</start>}
|
||||||
|
|
||||||
append_if $use_ahci_drv config {
|
append_if $use_ahci_drv config {
|
||||||
<start name="ahci">
|
<start name="ahci_drv">
|
||||||
<resource name="RAM" quantum="1M"/>
|
<resource name="RAM" quantum="1M"/>
|
||||||
<provides> <service name="Block"/> </provides>
|
<provides> <service name="Block"/> </provides>
|
||||||
<config ata="yes" />
|
<config>
|
||||||
|
<policy label="ffat_fs" device="0" />
|
||||||
|
</config>
|
||||||
</start>
|
</start>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +136,7 @@ set boot_modules {
|
|||||||
|
|
||||||
lappend_if [have_spec pci] boot_modules pci_drv
|
lappend_if [have_spec pci] boot_modules pci_drv
|
||||||
lappend_if [have_spec acpi] boot_modules acpi_drv
|
lappend_if [have_spec acpi] boot_modules acpi_drv
|
||||||
lappend_if $use_ahci_drv boot_modules ahci
|
lappend_if $use_ahci_drv boot_modules ahci_drv
|
||||||
lappend_if $use_sd_card_drv boot_modules sd_card_drv
|
lappend_if $use_sd_card_drv boot_modules sd_card_drv
|
||||||
|
|
||||||
build_boot_image $boot_modules
|
build_boot_image $boot_modules
|
||||||
@ -144,7 +150,7 @@ set cmd "dd if=/dev/zero of=$disk_image bs=1024 count=65536"
|
|||||||
puts "creating disk image: $cmd"
|
puts "creating disk image: $cmd"
|
||||||
catch { exec sh -c $cmd }
|
catch { exec sh -c $cmd }
|
||||||
|
|
||||||
set cmd "mkfs.vfat -F32 $disk_image"
|
set cmd "$mkfs -F32 $disk_image"
|
||||||
puts "formating disk image with vfat file system: $cmd"
|
puts "formating disk image with vfat file system: $cmd"
|
||||||
catch { exec sh -c $cmd }
|
catch { exec sh -c $cmd }
|
||||||
|
|
||||||
|
@ -126,16 +126,17 @@ append_if [expr ![have_spec acpi] && [have_spec pci]] config {
|
|||||||
|
|
||||||
append_if $use_ahci config {
|
append_if $use_ahci config {
|
||||||
<start name="ahci">
|
<start name="ahci">
|
||||||
<binary name="ahci" />
|
<binary name="ahci_drv" />
|
||||||
<resource name="RAM" quantum="10M" />
|
<resource name="RAM" quantum="10M" />
|
||||||
<provides><service name="Block" /></provides>
|
<provides><service name="Block" /></provides>
|
||||||
<route>}
|
<route>
|
||||||
|
|
||||||
append_if [expr $use_ahci && [have_spec acpi]] config {
|
|
||||||
<service name="IRQ"><child name="acpi" /></service>}
|
|
||||||
append_if $use_ahci config {
|
|
||||||
<any-service> <parent /> <any-child /></any-service>
|
<any-service> <parent /> <any-child /></any-service>
|
||||||
</route>
|
</route>
|
||||||
|
<config>}
|
||||||
|
append_if $use_ahci config "
|
||||||
|
<policy label=\"test-libc_$filesystem\" device=\"0\" />"
|
||||||
|
append_if $use_ahci config {
|
||||||
|
</config>
|
||||||
</start>}
|
</start>}
|
||||||
|
|
||||||
append_if $use_sd_card_drv config {
|
append_if $use_sd_card_drv config {
|
||||||
@ -167,7 +168,7 @@ append boot_modules libc_$filesystem.lib.so
|
|||||||
|
|
||||||
lappend_if [have_spec pci] boot_modules pci_drv
|
lappend_if [have_spec pci] boot_modules pci_drv
|
||||||
lappend_if [have_spec acpi] boot_modules acpi_drv
|
lappend_if [have_spec acpi] boot_modules acpi_drv
|
||||||
lappend_if $use_ahci boot_modules ahci
|
lappend_if $use_ahci boot_modules ahci_drv
|
||||||
lappend_if $use_sd_card_drv boot_modules sd_card_drv
|
lappend_if $use_sd_card_drv boot_modules sd_card_drv
|
||||||
|
|
||||||
build_boot_image $boot_modules
|
build_boot_image $boot_modules
|
||||||
|
@ -129,6 +129,25 @@ proc drivers_start_nodes { feature_arg } {
|
|||||||
<service name="PCI"/>
|
<service name="PCI"/>
|
||||||
<service name="IRQ" />
|
<service name="IRQ" />
|
||||||
</provides>
|
</provides>
|
||||||
|
<binary name="acpi_drv"/>
|
||||||
|
<provides>
|
||||||
|
<service name="PCI"/>
|
||||||
|
<service name="IRQ" />
|
||||||
|
</provides>
|
||||||
|
<config>
|
||||||
|
<policy label="acpi_drv">
|
||||||
|
<pci class="ALL"/>
|
||||||
|
</policy>
|
||||||
|
<policy label="ahci">
|
||||||
|
<pci class="AHCI"/>
|
||||||
|
</policy>
|
||||||
|
<policy label="ps2_drv">
|
||||||
|
<device name="PS2" />
|
||||||
|
</policy>
|
||||||
|
<policy label="fb_drv">
|
||||||
|
<pci class="VGA" />
|
||||||
|
</policy>
|
||||||
|
</config>
|
||||||
<route>
|
<route>
|
||||||
<service name="PCI"> <any-child /> </service>
|
<service name="PCI"> <any-child /> </service>
|
||||||
<any-service> <parent/> <any-child /> </any-service>
|
<any-service> <parent/> <any-child /> </any-service>
|
||||||
@ -179,6 +198,17 @@ proc drivers_start_nodes { feature_arg } {
|
|||||||
<start name="pci_drv">
|
<start name="pci_drv">
|
||||||
<resource name="RAM" quantum="2M" constrain_phys="yes"/>
|
<resource name="RAM" quantum="2M" constrain_phys="yes"/>
|
||||||
<provides><service name="PCI"/></provides>
|
<provides><service name="PCI"/></provides>
|
||||||
|
<config>
|
||||||
|
<policy label="ahci">
|
||||||
|
<pci class="AHCI"/>
|
||||||
|
</policy>
|
||||||
|
<policy label="ps2_drv">
|
||||||
|
<device name="PS2" />
|
||||||
|
</policy>
|
||||||
|
<policy label="fb_drv">
|
||||||
|
<pci class="VGA" />
|
||||||
|
</policy>
|
||||||
|
</config>
|
||||||
</start>
|
</start>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,9 +51,12 @@ append config [qt5_start_nodes feature]
|
|||||||
|
|
||||||
append_if $use_ahci_driver config {
|
append_if $use_ahci_driver config {
|
||||||
<start name="ahci">
|
<start name="ahci">
|
||||||
<resource name="RAM" quantum="1M"/>
|
<binary name="ahci_drv" />
|
||||||
|
<resource name="RAM" quantum="5M"/>
|
||||||
<provides> <service name="Block"/> </provides>
|
<provides> <service name="Block"/> </provides>
|
||||||
<config ata="yes" />
|
<config>
|
||||||
|
<policy label="ffat_fs" device="0" />
|
||||||
|
</config>
|
||||||
</start>}
|
</start>}
|
||||||
|
|
||||||
append_if $use_sd_card_driver config {
|
append_if $use_sd_card_driver config {
|
||||||
@ -145,7 +148,7 @@ append boot_modules {
|
|||||||
lappend_if [have_spec linux] boot_modules ram_fs
|
lappend_if [have_spec linux] boot_modules ram_fs
|
||||||
lappend_if [expr ![have_spec linux]] boot_modules ffat_fs
|
lappend_if [expr ![have_spec linux]] boot_modules ffat_fs
|
||||||
lappend_if $use_sd_card_driver boot_modules sd_card_drv
|
lappend_if $use_sd_card_driver boot_modules sd_card_drv
|
||||||
lappend_if $use_ahci_driver boot_modules ahci
|
lappend_if $use_ahci_driver boot_modules ahci_drv
|
||||||
|
|
||||||
build_boot_image $boot_modules
|
build_boot_image $boot_modules
|
||||||
|
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
#
|
|
||||||
# \brief Generic toolchain configurations for AHCI
|
|
||||||
# \author Martin Stein <martin.stein@genode-labs.com>
|
|
||||||
# \date 2013-05-17
|
|
||||||
#
|
|
||||||
|
|
||||||
# add C++ sources
|
|
||||||
SRC_CC += main.cc
|
|
||||||
|
|
||||||
# add library dependencies
|
|
||||||
LIBS += base
|
|
||||||
|
|
||||||
# add include directories
|
|
||||||
INC_DIR += $(REP_DIR)/src/drivers/ahci/include
|
|
||||||
|
|
||||||
# declare source paths
|
|
||||||
vpath main.cc $(REP_DIR)/src/drivers/ahci
|
|
@ -1,17 +0,0 @@
|
|||||||
#
|
|
||||||
# \brief Toolchain configurations for AHCI on Exynos
|
|
||||||
# \author Martin Stein <martin.stein@genode-labs.com>
|
|
||||||
# \date 2013-05-17
|
|
||||||
#
|
|
||||||
|
|
||||||
# add C++ sources
|
|
||||||
SRC_CC += ahci_driver.cc
|
|
||||||
|
|
||||||
# add include directories
|
|
||||||
INC_DIR += $(REP_DIR)/src/drivers/ahci/exynos5
|
|
||||||
|
|
||||||
# declare source paths
|
|
||||||
vpath ahci_driver.cc $(REP_DIR)/src/drivers/ahci/exynos5
|
|
||||||
|
|
||||||
# insert configurations that are less specific
|
|
||||||
include $(REP_DIR)/lib/mk/ahci.inc
|
|
6
repos/os/lib/mk/exynos5/ahci_platform.mk
Normal file
6
repos/os/lib/mk/exynos5/ahci_platform.mk
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
SRC_CC = platform.cc
|
||||||
|
|
||||||
|
INC_DIR += $(REP_DIR)/src/drivers/ahci
|
||||||
|
|
||||||
|
vpath platform.cc $(REP_DIR)/src/drivers/ahci/exynos5
|
||||||
|
|
@ -1,11 +0,0 @@
|
|||||||
#
|
|
||||||
# \brief Toolchain configurations for AHCI on X86
|
|
||||||
# \author Martin Stein <martin.stein@genode-labs.com>
|
|
||||||
# \date 2013-05-17
|
|
||||||
#
|
|
||||||
|
|
||||||
# add include directories
|
|
||||||
INC_DIR += $(REP_DIR)/src/drivers/ahci/x86
|
|
||||||
|
|
||||||
# include less specific config
|
|
||||||
include $(REP_DIR)/lib/mk/ahci.inc
|
|
6
repos/os/lib/mk/x86/ahci_platform.mk
Normal file
6
repos/os/lib/mk/x86/ahci_platform.mk
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
SRC_CC = platform.cc
|
||||||
|
|
||||||
|
INC_DIR += $(REP_DIR)/src/drivers/ahci
|
||||||
|
|
||||||
|
vpath platform.cc $(REP_DIR)/src/drivers/ahci/x86
|
||||||
|
|
@ -1,19 +1,27 @@
|
|||||||
This directory contains an implementation of a simple AHCI driver.
|
This directory contains the implementation of Genode's AHCI driver
|
||||||
|
|
||||||
Behavior
|
Behavior
|
||||||
--------
|
--------
|
||||||
|
|
||||||
The server implements Genode's new block-driver API ('os/include/block'), thus
|
The driver supports x86 32/64 bit platforms and the Exynos5 SOC. If
|
||||||
exposing the block-session interface as front-end. AHCI depends on Genode's PCI
|
more than one AHCI controller is present, the first one will be used.
|
||||||
driver as well as the timer server. For a usage example see: 'os/run/ahci.run'.
|
Each active device on each AHCI port will be represented by a Genode
|
||||||
|
block session. The server must be configured via a policy, that states
|
||||||
|
which client can access a certain device:
|
||||||
|
|
||||||
Limitations and known issues
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
Currently, the server scans the PCI bus at startup and retrieves the first available
|
!<start name="ahci">
|
||||||
AHCI controller, scans the controller ports and uses the first non-ATAPI port
|
! <binary name="ahci_drv" />
|
||||||
where a device is present.
|
! <resource name="RAM" quantum="10M" />
|
||||||
|
! <provides><service name="Block" /></provides> }
|
||||||
|
! <route>
|
||||||
|
! <any-service> <parent /> <any-child /> </any-service>
|
||||||
|
! </route>
|
||||||
|
! <config>
|
||||||
|
! <policy label="test-ahci" device="0" />
|
||||||
|
! <policy label="bench" device="1" />
|
||||||
|
! </config>
|
||||||
|
!</start>
|
||||||
|
|
||||||
On real hardware and on kernels taking advantage of I/O APICs (namely NOVA and
|
In the example above, a session request labeled with "test-ahci"
|
||||||
Fiasco.OC) we still lack support for ACPI parsing and thus for interrupts,
|
gains access to device 0, while "bench" gains access to device 1.
|
||||||
leading to a non-working driver.
|
|
||||||
|
208
repos/os/src/drivers/ahci/ahci.cc
Normal file
208
repos/os/src/drivers/ahci/ahci.cc
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
/**
|
||||||
|
* \brief AHCI port controller handling
|
||||||
|
* \author Sebastian Sumpf
|
||||||
|
* \date 2015-04-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <timer_session/connection.h>
|
||||||
|
#include "ata_driver.h"
|
||||||
|
#include "atapi_driver.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct Timer_delayer : Mmio::Delayer, Timer::Connection
|
||||||
|
{
|
||||||
|
void usleep(unsigned us) { Timer::Connection::usleep(us); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Mmio::Delayer &Hba::delayer()
|
||||||
|
{
|
||||||
|
static Timer_delayer d;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct Ahci
|
||||||
|
{
|
||||||
|
/* read device signature */
|
||||||
|
enum Signature {
|
||||||
|
ATA_SIG = 0x101,
|
||||||
|
ATAPI_SIG = 0xeb140101,
|
||||||
|
ATAPI_SIG_QEMU = 0xeb140000, /* will be fixed in Qemu */
|
||||||
|
};
|
||||||
|
|
||||||
|
Ahci_root &root;
|
||||||
|
Platform::Hba &platform_hba = Platform::init(Hba::delayer());
|
||||||
|
Hba hba = { platform_hba };
|
||||||
|
|
||||||
|
enum { MAX_PORTS = 32 };
|
||||||
|
Port_driver *ports[MAX_PORTS];
|
||||||
|
bool port_claimed[MAX_PORTS];
|
||||||
|
|
||||||
|
Signal_rpc_member<Ahci> irq;
|
||||||
|
Signal_rpc_member<Ahci> device_ready;
|
||||||
|
unsigned ready_count = 0;
|
||||||
|
|
||||||
|
Ahci(Ahci_root &root)
|
||||||
|
: root(root), irq(root.entrypoint(), *this, &Ahci::handle_irq),
|
||||||
|
device_ready(root.entrypoint(), *this, &Ahci::ready)
|
||||||
|
{
|
||||||
|
info();
|
||||||
|
|
||||||
|
/* register irq handler */
|
||||||
|
platform_hba.sigh_irq(irq);
|
||||||
|
|
||||||
|
/* initialize HBA (IRQs, memory) */
|
||||||
|
hba.init();
|
||||||
|
|
||||||
|
/* search for devices */
|
||||||
|
scan_ports();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_atapi(unsigned sig)
|
||||||
|
{
|
||||||
|
return sig == ATAPI_SIG_QEMU || sig == ATAPI_SIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forward IRQs to ports
|
||||||
|
*/
|
||||||
|
void handle_irq(unsigned)
|
||||||
|
{
|
||||||
|
unsigned port_list = hba.read<Hba::Is>();
|
||||||
|
while (port_list) {
|
||||||
|
unsigned port = log2(port_list);
|
||||||
|
port_list &= ~(1U << port);
|
||||||
|
|
||||||
|
ports[port]->handle_irq();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clear status register */
|
||||||
|
hba.ack_irq();
|
||||||
|
|
||||||
|
/* ack at interrupt controller */
|
||||||
|
platform_hba.ack_irq();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ready(unsigned)
|
||||||
|
{
|
||||||
|
if (--ready_count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* announce service */
|
||||||
|
root.announce();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void info()
|
||||||
|
{
|
||||||
|
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("\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>();
|
||||||
|
for (unsigned i = 0; i < hba.port_count(); i++) {
|
||||||
|
|
||||||
|
/* check if port is implemented */
|
||||||
|
if (!(avaiable & (1U << i)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Port port(hba, platform_hba, i);
|
||||||
|
bool enabled = port.enable();
|
||||||
|
unsigned sig = port.read<Port::Sig>();
|
||||||
|
|
||||||
|
PINF("\t\t#%u: %s", i, enabled ?
|
||||||
|
(is_atapi(sig) ? "ATAPI" : "ATA") :
|
||||||
|
"off");
|
||||||
|
|
||||||
|
if (!enabled)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
|
||||||
|
switch (sig) {
|
||||||
|
|
||||||
|
case ATA_SIG:
|
||||||
|
ports[i] = new (Genode::env()->heap()) Ata_driver(port, device_ready);
|
||||||
|
ready_count++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ATAPI_SIG:
|
||||||
|
case ATAPI_SIG_QEMU:
|
||||||
|
ports[i] = new (Genode::env()->heap()) Atapi_driver(port, device_ready);
|
||||||
|
ready_count++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
PWRN("Device signature %x unsupported", sig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Block::Driver *claim_port(unsigned port_num)
|
||||||
|
{
|
||||||
|
if (!is_avail(port_num))
|
||||||
|
throw -1;
|
||||||
|
|
||||||
|
port_claimed[port_num] = true;
|
||||||
|
return ports[port_num];
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_port(unsigned port_num)
|
||||||
|
{
|
||||||
|
port_claimed[port_num] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_avail(unsigned port_num)
|
||||||
|
{
|
||||||
|
return port_num < MAX_PORTS && ports[port_num] && !port_claimed[port_num] &&
|
||||||
|
ports[port_num]->ready();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static Ahci *sata_ahci(Ahci *ahci = 0)
|
||||||
|
{
|
||||||
|
static Ahci *a = ahci;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Ahci_driver::init(Ahci_root &root)
|
||||||
|
{
|
||||||
|
static Ahci ahci(root);
|
||||||
|
sata_ahci(&ahci);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Block::Driver *Ahci_driver::claim_port(long device_num)
|
||||||
|
{
|
||||||
|
return sata_ahci()->claim_port(device_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Ahci_driver::free_port(long device_num)
|
||||||
|
{
|
||||||
|
sata_ahci()->free_port(device_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Ahci_driver::is_avail(long device_num)
|
||||||
|
{
|
||||||
|
return sata_ahci()->is_avail(device_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
866
repos/os/src/drivers/ahci/ahci.h
Normal file
866
repos/os/src/drivers/ahci/ahci.h
Normal file
@ -0,0 +1,866 @@
|
|||||||
|
/**
|
||||||
|
* \brief Generic AHCI controller definitions
|
||||||
|
* \author Sebasitan Sumpf
|
||||||
|
* \date 2015-04-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _INCLUDE__AHCI_H_
|
||||||
|
#define _INCLUDE__AHCI_H_
|
||||||
|
|
||||||
|
#include <block/component.h>
|
||||||
|
#include <os/attached_mmio.h>
|
||||||
|
#include <util/volatile_object.h>
|
||||||
|
|
||||||
|
static bool constexpr verbose = false;
|
||||||
|
|
||||||
|
namespace Platform {
|
||||||
|
struct Hba;
|
||||||
|
Hba &init(Genode::Mmio::Delayer &delayer);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Ahci_root
|
||||||
|
{
|
||||||
|
virtual Server::Entrypoint &entrypoint() = 0;
|
||||||
|
virtual void announce() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace Ahci_driver {
|
||||||
|
|
||||||
|
void init(Ahci_root &ep);
|
||||||
|
|
||||||
|
bool is_avail(long device_num);
|
||||||
|
|
||||||
|
Block::Driver *claim_port(long device_num);
|
||||||
|
void free_port(long device_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct Platform::Hba
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Return base address and size of HBA device registers
|
||||||
|
*/
|
||||||
|
virtual Genode::addr_t base() const = 0;
|
||||||
|
virtual Genode::size_t size() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register interrupt signal context
|
||||||
|
*/
|
||||||
|
virtual void sigh_irq(Genode::Signal_context_capability sigh) = 0;
|
||||||
|
virtual void ack_irq() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DMA
|
||||||
|
*/
|
||||||
|
virtual Genode::Ram_dataspace_capability alloc_dma_buffer(Genode::size_t size) = 0;
|
||||||
|
virtual void free_dma_buffer(Genode::Ram_dataspace_capability ds) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HBA definitions
|
||||||
|
*/
|
||||||
|
struct Hba : Genode::Attached_mmio
|
||||||
|
{
|
||||||
|
Hba(Platform::Hba &hba)
|
||||||
|
: Attached_mmio(hba.base(), hba.size()) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Host capabilites
|
||||||
|
*/
|
||||||
|
struct Cap : Register<0x0, 32>
|
||||||
|
{
|
||||||
|
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 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 supports_64bit(){ return !!read<Cap::Sa64>(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic host control
|
||||||
|
*/
|
||||||
|
struct Ghc : Register<0x4, 32>
|
||||||
|
{
|
||||||
|
struct Hr : Bitfield<0, 1> { }; /* hard reset */
|
||||||
|
struct Ie : Bitfield<1, 1> { }; /* interrupt enable */
|
||||||
|
struct Ae : Bitfield<31, 1> { }; /* AHCI enable */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interrupt status
|
||||||
|
*/
|
||||||
|
struct Is : Register<0x8, 32> { };
|
||||||
|
|
||||||
|
void ack_irq() { write<Is>(read<Is>()); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ports implemented
|
||||||
|
*/
|
||||||
|
struct Pi : Register<0xc, 32> { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AHCI version
|
||||||
|
*/
|
||||||
|
struct Version : Register<0x10, 32>
|
||||||
|
{
|
||||||
|
struct Minor : Bitfield<0, 16> { };
|
||||||
|
struct Major : Bitfield<16, 16> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Cap2 : Register<0x24, 32> { };
|
||||||
|
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
/* enable AHCI */
|
||||||
|
write<Ghc::Ae>(1);
|
||||||
|
|
||||||
|
/* enable interrupts */
|
||||||
|
write<Ghc::Ie>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Mmio::Delayer &delayer();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Device_fis : Genode::Mmio
|
||||||
|
{
|
||||||
|
struct Status : Register<0x2, 8>
|
||||||
|
{
|
||||||
|
/* ATAPI bits */
|
||||||
|
struct Device_ready : Bitfield<6, 1> { };
|
||||||
|
};
|
||||||
|
struct Error : Register<0x3, 8> { };
|
||||||
|
|
||||||
|
Device_fis(Genode::addr_t recv_base)
|
||||||
|
: Mmio(recv_base + 0x40) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Command_fis : Genode::Mmio
|
||||||
|
{
|
||||||
|
struct Type : Register<0x0, 8> { }; /* FIS type */
|
||||||
|
struct Bits : Register<0x1, 8, 1>
|
||||||
|
{
|
||||||
|
struct C : Bitfield<7, 1> { }; /* update of command register */
|
||||||
|
};
|
||||||
|
struct Command : Register<0x2, 8, 1> { }; /* ATA command */
|
||||||
|
struct Features0_7 : Register<0x3, 8> { };
|
||||||
|
|
||||||
|
/* big-endian use byte access */
|
||||||
|
struct Lba0_7 : Register<0x4, 8> { };
|
||||||
|
struct Lba8_15 : Register<0x5, 8> { };
|
||||||
|
struct Lba16_23 : Register<0x6, 8> { };
|
||||||
|
struct Lba24 : Genode::Bitset_3<Lba0_7, Lba8_15, Lba16_23> { };
|
||||||
|
struct Device : Register<0x7, 8>
|
||||||
|
{
|
||||||
|
struct Lba : Bitfield<6, 1> { }; /* enable LBA mode */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* big endian */
|
||||||
|
struct Lba24_31 : Register<0x8, 8> { };
|
||||||
|
struct Lba32_39 : Register<0x9, 8> { };
|
||||||
|
struct Lba40_47 : Register<0xa, 8> { };
|
||||||
|
struct Features8_15 : Register<0xb, 8> { };
|
||||||
|
struct Features : Genode::Bitset_2<Features0_7, Features8_15> { };
|
||||||
|
struct Lba48 : Genode::Bitset_3<Lba24_31, Lba32_39, Lba40_47> { };
|
||||||
|
struct Lba : Genode::Bitset_2<Lba24, Lba48> { }; /* LBA 0 - 47 */
|
||||||
|
|
||||||
|
/* big endian */
|
||||||
|
struct Sector0_7 : Register<0xc, 8, 1>
|
||||||
|
{
|
||||||
|
struct Tag : Bitfield<3, 5> { };
|
||||||
|
};
|
||||||
|
struct Sector8_15 : Register<0xd, 8> { };
|
||||||
|
struct Sector : Genode::Bitset_2<Sector0_7, Sector8_15> { }; /* sector count */
|
||||||
|
|
||||||
|
Command_fis(Genode::addr_t base)
|
||||||
|
: Mmio(base)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
|
||||||
|
enum { HOST_TO_DEVICE = 0x27 };
|
||||||
|
write<Type>(HOST_TO_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr Genode::size_t size() { return 0x14; }
|
||||||
|
void clear() { Genode::memset((void *)base, 0, size()); }
|
||||||
|
|
||||||
|
|
||||||
|
/************************
|
||||||
|
** ATA spec commands **
|
||||||
|
************************/
|
||||||
|
|
||||||
|
void identify_device()
|
||||||
|
{
|
||||||
|
write<Bits::C>(1);
|
||||||
|
write<Device::Lba>(0);
|
||||||
|
write<Command>(0xec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dma_ext(bool read, Block::sector_t block_number, Genode::size_t block_count)
|
||||||
|
{
|
||||||
|
write<Bits::C>(1);
|
||||||
|
write<Device::Lba>(1);
|
||||||
|
/* read_dma_ext : write_dma_ext */
|
||||||
|
write<Command>(read ? 0x25 : 0x35);
|
||||||
|
write<Lba>(block_number);
|
||||||
|
write<Sector>(block_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fpdma(bool read, Block::sector_t block_number, Genode::size_t block_count,
|
||||||
|
unsigned slot)
|
||||||
|
{
|
||||||
|
write<Bits::C>(1);
|
||||||
|
write<Device::Lba>(1);
|
||||||
|
/* read_fpdma : write_fpdma */
|
||||||
|
write<Command>(read ? 0x60 : 0x61);
|
||||||
|
write<Lba>(block_number);
|
||||||
|
write<Features>(block_count);
|
||||||
|
write<Sector0_7::Tag>(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void atapi()
|
||||||
|
{
|
||||||
|
write<Bits::C>(1);
|
||||||
|
/* packet command */
|
||||||
|
write<Command>(0xa0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AHCI command list structure header
|
||||||
|
*/
|
||||||
|
struct Command_header : Genode::Mmio
|
||||||
|
{
|
||||||
|
struct Bits : Register<0x0, 16>
|
||||||
|
{
|
||||||
|
struct Cfl : Bitfield<0, 5> { }; /* command FIS length */
|
||||||
|
struct A : Bitfield<5, 1> { }; /* ATAPI command flag */
|
||||||
|
struct W : Bitfield<6, 1> { }; /* write flag */
|
||||||
|
struct P : Bitfield<7, 1> { }; /* prefetchable flag */
|
||||||
|
struct C : Bitfield<10, 1> { }; /* clear busy upon R_OK */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Prdtl : Register<0x2, 16> { }; /* physical region descr. length */
|
||||||
|
struct Prdbc : Register<0x4, 32> { }; /* PRD byte count */
|
||||||
|
struct Ctba0 : Register<0x8, 32> { }; /* command table base addr (low) */
|
||||||
|
struct Ctba0_u0 : Register<0xc, 32> { }; /* command table base addr (upper) */
|
||||||
|
|
||||||
|
Command_header(Genode::addr_t base) : Mmio(base) { }
|
||||||
|
|
||||||
|
void cmd_table_base(Genode::addr_t base_phys)
|
||||||
|
{
|
||||||
|
Genode::uint64_t addr = base_phys;
|
||||||
|
write<Ctba0>(addr);
|
||||||
|
write<Ctba0_u0>(addr >> 32);
|
||||||
|
write<Prdtl>(1);
|
||||||
|
write<Bits::Cfl>(Command_fis::size() / sizeof(unsigned));
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_byte_count()
|
||||||
|
{
|
||||||
|
write<Prdbc>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void atapi_command()
|
||||||
|
{
|
||||||
|
write<Bits::A>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr Genode::size_t size() { return 0x20; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ATAPI packet 12 or 16 bytes
|
||||||
|
*/
|
||||||
|
struct Atapi_command : Genode::Mmio
|
||||||
|
{
|
||||||
|
struct Command : Register<0, 8> { };
|
||||||
|
|
||||||
|
/* LBA32 big endian */
|
||||||
|
struct Lba24_31 : Register<0x2, 8> { };
|
||||||
|
struct Lba16_23 : Register<0x3, 8> { };
|
||||||
|
struct Lba8_15 : Register<0x4, 8> { };
|
||||||
|
struct Lba0_7 : Register<0x5, 8> { };
|
||||||
|
struct Lba24 : Genode::Bitset_3<Lba0_7, Lba8_15, Lba16_23> { };
|
||||||
|
struct Lba : Genode::Bitset_2<Lba24, Lba24_31> { };
|
||||||
|
|
||||||
|
/* sector count big endian */
|
||||||
|
struct Sector8_15 : Register<0x8, 8> { };
|
||||||
|
struct Sector0_7 : Register<0x9, 8> { };
|
||||||
|
struct Sector : Genode::Bitset_2<Sector0_7, Sector8_15> { };
|
||||||
|
|
||||||
|
|
||||||
|
Atapi_command(Genode::addr_t base) : Mmio(base)
|
||||||
|
{
|
||||||
|
Genode::memset((void *)base, 0, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_capacity()
|
||||||
|
{
|
||||||
|
write<Command>(0x25);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_unit_ready()
|
||||||
|
{
|
||||||
|
write<Command>(0x0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_sense()
|
||||||
|
{
|
||||||
|
write<Command>(0x3);
|
||||||
|
write<Lba8_15>(18);
|
||||||
|
}
|
||||||
|
|
||||||
|
void read10(Block::sector_t block_number, Genode::size_t block_count)
|
||||||
|
{
|
||||||
|
write<Command>(0x28);
|
||||||
|
write<Lba>(block_number);
|
||||||
|
write<Sector>(block_count);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Physical region descritpor table
|
||||||
|
*/
|
||||||
|
struct Prdt : Genode::Mmio
|
||||||
|
{
|
||||||
|
struct Dba : Register<0x0, 32> { }; /* data base address */
|
||||||
|
struct Dbau : Register<0x4, 32> { }; /* data base address upper 32 bits */
|
||||||
|
|
||||||
|
struct Bits : Register<0xc, 32>
|
||||||
|
{
|
||||||
|
struct Dbc : Bitfield<0, 22> { }; /* data byte count */
|
||||||
|
struct Irq : Bitfield<31,1> { }; /* interrupt completion */
|
||||||
|
};
|
||||||
|
|
||||||
|
Prdt(Genode::addr_t base, Genode::addr_t phys, Genode::size_t bytes)
|
||||||
|
: Mmio(base)
|
||||||
|
{
|
||||||
|
Genode::uint64_t addr = phys;
|
||||||
|
write<Dba>(addr);
|
||||||
|
write<Dbau>(addr >> 32);
|
||||||
|
write<Bits::Dbc>(bytes > 0 ? bytes - 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr Genode::size_t size() { return 0x10; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Command_table
|
||||||
|
{
|
||||||
|
Command_fis fis;
|
||||||
|
Atapi_command atapi_cmd;
|
||||||
|
|
||||||
|
/* in Genode we only need one PRD (for one packet) */
|
||||||
|
Prdt prdt;
|
||||||
|
|
||||||
|
Command_table(Genode::addr_t base,
|
||||||
|
Genode::addr_t phys,
|
||||||
|
Genode::size_t bytes = 0)
|
||||||
|
: fis(base), atapi_cmd(base + 0x40),
|
||||||
|
prdt(base + 0x80, phys, bytes)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
static constexpr Genode::size_t size() { return 0x100; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
struct Port : Genode::Mmio
|
||||||
|
{
|
||||||
|
Hba &hba;
|
||||||
|
Platform::Hba &platform_hba;
|
||||||
|
unsigned cmd_slots = hba.command_slots();
|
||||||
|
|
||||||
|
Genode::Ram_dataspace_capability device_ds;
|
||||||
|
Genode::Ram_dataspace_capability cmd_ds;
|
||||||
|
Genode::Ram_dataspace_capability device_info_ds;
|
||||||
|
|
||||||
|
Genode::addr_t cmd_list = 0;
|
||||||
|
Genode::addr_t fis_base = 0;
|
||||||
|
Genode::addr_t cmd_table = 0;
|
||||||
|
Genode::addr_t device_info = 0;
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
NONE,
|
||||||
|
STATUS,
|
||||||
|
TEST_READY,
|
||||||
|
IDENTIFY,
|
||||||
|
READY,
|
||||||
|
};
|
||||||
|
|
||||||
|
State state = NONE;
|
||||||
|
|
||||||
|
Port(Hba &hba, Platform::Hba &platform_hba, unsigned number)
|
||||||
|
: Mmio(hba.base + offset() + (number * size())), hba(hba),
|
||||||
|
platform_hba(platform_hba)
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
if (!wait_for<Cmd::Cr>(0, Hba::delayer(), 500, 1000))
|
||||||
|
PERR("failed to stop command list processing");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Port()
|
||||||
|
{
|
||||||
|
if (device_ds.valid()) {
|
||||||
|
Genode::env()->rm_session()->detach((void *)cmd_list);
|
||||||
|
platform_hba.free_dma_buffer(device_ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd_ds.valid()) {
|
||||||
|
Genode::env()->rm_session()->detach((void *)cmd_table);
|
||||||
|
platform_hba.free_dma_buffer(cmd_ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_info_ds.valid()) {
|
||||||
|
Genode::env()->rm_session()->detach((void*)device_info);
|
||||||
|
platform_hba.free_dma_buffer(device_info_ds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr Genode::addr_t offset() { return 0x100; }
|
||||||
|
static constexpr Genode::size_t size() { return 0x80; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command list base (1K length naturally aligned)
|
||||||
|
*/
|
||||||
|
struct Clb : Register<0x0, 32> { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command list base upper 32 bit
|
||||||
|
*/
|
||||||
|
struct Clbu : Register<0x4, 32> { };
|
||||||
|
|
||||||
|
void command_list_base(Genode::addr_t phys)
|
||||||
|
{
|
||||||
|
Genode::uint64_t addr = phys;
|
||||||
|
write<Clb>(addr);
|
||||||
|
write<Clbu>(addr >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIS base address (256 bytes naturally aligned)
|
||||||
|
*/
|
||||||
|
struct Fb : Register<0x8, 32> { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIS base address upper 32 bit
|
||||||
|
*/
|
||||||
|
struct Fbu : Register<0xc, 32> { };
|
||||||
|
|
||||||
|
void fis_rcv_base(Genode::addr_t phys)
|
||||||
|
{
|
||||||
|
Genode::uint64_t addr = phys;
|
||||||
|
write<Fb>(addr);
|
||||||
|
write<Fbu>(addr >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Port interrupt status
|
||||||
|
*/
|
||||||
|
struct Is : Register<0x10, 32, 1>
|
||||||
|
{
|
||||||
|
struct Dhrs : Bitfield<0, 1> { }; /* device to host register FIS */
|
||||||
|
struct Pss : Bitfield<1, 1> { }; /* PIO setup FIS */
|
||||||
|
struct Dss : Bitfield<2, 1> { }; /* DMA setup FIS */
|
||||||
|
struct Sdbs : Bitfield<3, 1> { }; /* Set device bit */
|
||||||
|
struct Pcs : Bitfield<6, 1> { }; /* port connect change status */
|
||||||
|
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 */
|
||||||
|
};
|
||||||
|
|
||||||
|
void ack_irq()
|
||||||
|
{
|
||||||
|
Is::access_t status = read <Is>();
|
||||||
|
|
||||||
|
/* clear Serr.Diag.x */
|
||||||
|
if (Is::Pcs::get(status))
|
||||||
|
write<Serr::Diag::X>(0);
|
||||||
|
|
||||||
|
/* clear Serr.Diag.n */
|
||||||
|
if (Is::Prcs::get(status))
|
||||||
|
write<Serr::Diag::N>(0);
|
||||||
|
|
||||||
|
write<Is>(read<Is>());
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Port interrupt enable
|
||||||
|
*/
|
||||||
|
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 Dse : Bitfield<2, 1> { }; /* DMA setup FIS interrupt */
|
||||||
|
struct Sdbe : Bitfield<3, 1> { }; /* set device bits FIS interrupt (ncg) */
|
||||||
|
struct Ufe : Bitfield<4, 1> { }; /* unknown FIS */
|
||||||
|
struct Dpe : Bitfield<5, 1> { }; /* descriptor processed */
|
||||||
|
struct Ifne : Bitfield<26, 1> { }; /* interface non-fatal error */
|
||||||
|
struct Ife : Bitfield<27, 1> { }; /* interface fatal error */
|
||||||
|
struct Hbde : Bitfield<28, 1> { }; /* host bus data error */
|
||||||
|
struct Hbfe : Bitfield<29, 1> { }; /* host bus fatal error */
|
||||||
|
struct Tfee : Bitfield<30, 1> { }; /* task file error */
|
||||||
|
};
|
||||||
|
|
||||||
|
void interrupt_enable()
|
||||||
|
{
|
||||||
|
Ie::access_t ie = 0;
|
||||||
|
Ie::Dhre::set(ie, 1);
|
||||||
|
Ie::Pse::set(ie, 1);
|
||||||
|
Ie::Dse::set(ie, 1);
|
||||||
|
Ie::Sdbe::set(ie, 1);
|
||||||
|
Ie::Ufe::set(ie, 1);
|
||||||
|
Ie::Dpe::set(ie, 1);
|
||||||
|
Ie::Ifne::set(ie, 1);
|
||||||
|
Ie::Hbde::set(ie, 1);
|
||||||
|
Ie::Hbfe::set(ie, 1);
|
||||||
|
Ie::Tfee::set(ie, 1);
|
||||||
|
write<Ie>(~0U);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Port command
|
||||||
|
*/
|
||||||
|
struct Cmd : Register<0x18, 32>
|
||||||
|
{
|
||||||
|
struct St : Bitfield<0, 1> { }; /* start */
|
||||||
|
struct Sud : Bitfield<1, 1> { }; /* spin-up device */
|
||||||
|
struct Pod : Bitfield<2, 1> { }; /* power-up device */
|
||||||
|
struct Fre : Bitfield<4, 1> { }; /* FIS receive enable */
|
||||||
|
struct Cr : Bitfield<15, 1> { }; /* command list running */
|
||||||
|
struct Atapi : Bitfield<24, 1> { };
|
||||||
|
struct Icc : Bitfield<28, 4> { }; /* interface communication control */
|
||||||
|
};
|
||||||
|
|
||||||
|
void start()
|
||||||
|
{
|
||||||
|
if (read<Cmd::St>())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!wait_for<Tfd::Sts_bsy>(0, hba.delayer(), 500, 1000)) {
|
||||||
|
PERR("HBA busy unable to start command processing.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wait_for<Tfd::Sts_drq>(0, hba.delayer(), 500, 1000)) {
|
||||||
|
PERR("HBA in DRQ unable to start command processing.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
write<Cmd::St>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop()
|
||||||
|
{
|
||||||
|
if (!(read<Ci>() | read<Sact>()))
|
||||||
|
write<Cmd::St>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void power_up()
|
||||||
|
{
|
||||||
|
Cmd::access_t cmd = read<Cmd>();
|
||||||
|
Cmd::Sud::set(cmd, 1);
|
||||||
|
Cmd::Pod::set(cmd, 1);
|
||||||
|
Cmd::Fre::set(cmd, 1);
|
||||||
|
write<Cmd>(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task file data
|
||||||
|
*/
|
||||||
|
struct Tfd : Register<0x20, 32>
|
||||||
|
{
|
||||||
|
struct Sts_drq : Bitfield<3, 1> { }; /* indicates data transfer request */
|
||||||
|
struct Sts_bsy : Bitfield<7, 1> { }; /* interface is busy */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Port signature
|
||||||
|
*/
|
||||||
|
struct Sig : Register<0x24, 32> { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serial ATA status
|
||||||
|
*/
|
||||||
|
struct Ssts : Register<0x28, 32>
|
||||||
|
{
|
||||||
|
struct Dec : Bitfield<0, 4> /* device detection */
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
NONE = 0x0,
|
||||||
|
ESTABLISHED = 0x3
|
||||||
|
};
|
||||||
|
};
|
||||||
|
struct Ipm : Bitfield<8, 4> /* interface power mgmt */
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
ACTIVE = 0x1,
|
||||||
|
SUSPEND = 0x2,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
bool enable()
|
||||||
|
{
|
||||||
|
Ssts::access_t status = read<Ssts>();
|
||||||
|
if (Ssts::Dec::get(status) == Ssts::Dec::NONE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* if in power-mgmt state (partial or slumber) */
|
||||||
|
if (Ssts::Ipm::get(status) & Ssts::Ipm::SUSPEND) {
|
||||||
|
/* try to wake up device */
|
||||||
|
write<Cmd::Icc>(Ssts::Ipm::ACTIVE);
|
||||||
|
|
||||||
|
while ((Ssts::Dec::get(status) != Ssts::Dec::ESTABLISHED) ||
|
||||||
|
!(Ssts::Ipm::get(status) & Ssts::Ipm::ACTIVE))
|
||||||
|
status = read<Ssts>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((Ssts::Dec::get(status) == Ssts::Dec::ESTABLISHED) &&
|
||||||
|
(Ssts::Ipm::get(status) & Ssts::Ipm::ACTIVE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serial ATA control
|
||||||
|
*/
|
||||||
|
struct Sctl : Register<0x2c, 32>
|
||||||
|
{
|
||||||
|
struct Det : Bitfield<0, 4> { }; /* device dectection initialization */
|
||||||
|
struct Ipmt : Bitfield<8, 4> { }; /* allowed power management transitions */
|
||||||
|
};
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
if (read<Cmd::St>())
|
||||||
|
PWRN("CMD.ST bit set during device reset --> unknown behavior");
|
||||||
|
|
||||||
|
write<Sctl::Det>(1);
|
||||||
|
hba.delayer().usleep(1000);
|
||||||
|
write<Sctl::Det>(0);
|
||||||
|
|
||||||
|
if (!wait_for<Ssts::Dec>(Ssts::Dec::ESTABLISHED, hba.delayer()))
|
||||||
|
PWRN("Port reset failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serial ATA error
|
||||||
|
*/
|
||||||
|
struct Serr : Register<0x30, 32>
|
||||||
|
{
|
||||||
|
struct Diag : Register<0x2, 16>
|
||||||
|
{
|
||||||
|
struct N : Bitfield<0, 1> { }; /* PhyRdy change */
|
||||||
|
struct X : Bitfield<10, 1> { }; /* exchanged */
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
void clear_serr() { write<Serr>(read<Serr>()); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serial ATA active
|
||||||
|
*/
|
||||||
|
struct Sact : Register<0x34, 32> { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command issue
|
||||||
|
*/
|
||||||
|
struct Ci : Register<0x38, 32> { };
|
||||||
|
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
/* stop command list processing */
|
||||||
|
stop();
|
||||||
|
|
||||||
|
/* setup command list/table */
|
||||||
|
setup_memory();
|
||||||
|
|
||||||
|
/* disallow all power-management transitions */
|
||||||
|
write<Sctl::Ipmt>(0x3);
|
||||||
|
|
||||||
|
/* power-up device */
|
||||||
|
power_up();
|
||||||
|
|
||||||
|
/* reset port */
|
||||||
|
reset();
|
||||||
|
|
||||||
|
/* clean error register */
|
||||||
|
clear_serr();
|
||||||
|
|
||||||
|
/* enable required interrupts */
|
||||||
|
interrupt_enable();
|
||||||
|
|
||||||
|
/* acknowledge all pending interrupts */
|
||||||
|
ack_irq();
|
||||||
|
hba.ack_irq();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_memory()
|
||||||
|
{
|
||||||
|
using Genode::addr_t;
|
||||||
|
using Genode::size_t;
|
||||||
|
|
||||||
|
device_ds = platform_hba.alloc_dma_buffer(0x1000);
|
||||||
|
|
||||||
|
/* command list 1K */
|
||||||
|
addr_t phys = Genode::Dataspace_client(device_ds).phys_addr();
|
||||||
|
cmd_list = (addr_t)Genode::env()->rm_session()->attach(device_ds);
|
||||||
|
command_list_base(phys);
|
||||||
|
|
||||||
|
/* receive FIS base 256 byte */
|
||||||
|
fis_base = cmd_list + 1024;
|
||||||
|
fis_rcv_base(phys + 1024);
|
||||||
|
|
||||||
|
/* command table */
|
||||||
|
size_t cmd_size = Genode::align_addr(cmd_slots * Command_table::size(), 12);
|
||||||
|
cmd_ds = platform_hba.alloc_dma_buffer(cmd_size);
|
||||||
|
cmd_table = (addr_t)Genode::env()->rm_session()->attach(cmd_ds);
|
||||||
|
phys = (addr_t)Genode::Dataspace_client(cmd_ds).phys_addr();
|
||||||
|
|
||||||
|
/* set command table addresses in command list */
|
||||||
|
for (unsigned i = 0; i < cmd_slots; i++) {
|
||||||
|
Command_header h(cmd_list + (i * Command_header::size()));
|
||||||
|
h.cmd_table_base(phys + (i * Command_table::size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dataspace for device info */
|
||||||
|
device_info_ds = platform_hba.alloc_dma_buffer(0x1000);
|
||||||
|
device_info = Genode::env()->rm_session()->attach(device_info_ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
Genode::addr_t command_table_addr(unsigned slot)
|
||||||
|
{
|
||||||
|
return cmd_table + (slot * Command_table::size());
|
||||||
|
};
|
||||||
|
|
||||||
|
Genode::addr_t command_header_addr(unsigned slot)
|
||||||
|
{
|
||||||
|
return cmd_list + (slot * Command_header::size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute(unsigned slot)
|
||||||
|
{
|
||||||
|
start();
|
||||||
|
write<Ci>(1U << slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ready() { return state == READY; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Port_driver : Port, Block::Driver
|
||||||
|
{
|
||||||
|
Genode::Signal_context_capability state_change_cap;
|
||||||
|
|
||||||
|
Port_driver(Port &port, Genode::Signal_context_capability state_change_cap)
|
||||||
|
: Port(port), state_change_cap(state_change_cap)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
virtual void handle_irq() = 0;
|
||||||
|
|
||||||
|
void state_change() { Genode::Signal_transmitter(state_change_cap).submit(); }
|
||||||
|
|
||||||
|
void sanity_check(Block::sector_t block_number, Genode::size_t count)
|
||||||
|
{
|
||||||
|
/* max. PRDT size is 4MB */
|
||||||
|
if (count * block_size() > 4 * 1024 * 1024) {
|
||||||
|
PERR("error: maximum supported packet size is 4MB");
|
||||||
|
throw Io_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sanity check */
|
||||||
|
if (block_number + count > block_count()) {
|
||||||
|
PERR("error: requested blocks are outside of device");
|
||||||
|
throw Io_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************
|
||||||
|
** Block::Driver **
|
||||||
|
*******************/
|
||||||
|
|
||||||
|
Genode::Ram_dataspace_capability
|
||||||
|
alloc_dma_buffer(Genode::size_t size) override
|
||||||
|
{
|
||||||
|
return platform_hba.alloc_dma_buffer(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_dma_buffer(Genode::Ram_dataspace_capability c) override
|
||||||
|
{
|
||||||
|
platform_hba.free_dma_buffer(c);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _INCLUDE__AHCI_H_ */
|
212
repos/os/src/drivers/ahci/ata_driver.h
Normal file
212
repos/os/src/drivers/ahci/ata_driver.h
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
/**
|
||||||
|
* \brief AHCI-port driver for ATA devices
|
||||||
|
* \author Sebastian Sumpf
|
||||||
|
* \date 2015-04-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ATA_DRIVER_H_
|
||||||
|
#define _ATA_DRIVER_H_
|
||||||
|
|
||||||
|
#include "ahci.h"
|
||||||
|
|
||||||
|
using namespace Genode;
|
||||||
|
|
||||||
|
struct Ata_driver : Port_driver
|
||||||
|
{
|
||||||
|
Genode::Lazy_volatile_object<Identity> info;
|
||||||
|
Block::Packet_descriptor pending[32];
|
||||||
|
|
||||||
|
Ata_driver(Port &port, Signal_context_capability state_change)
|
||||||
|
: Port_driver(port, state_change)
|
||||||
|
{
|
||||||
|
Port::init();
|
||||||
|
identify_device();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned find_free_cmd_slot()
|
||||||
|
{
|
||||||
|
for (unsigned slot = 0; slot < cmd_slots; slot++)
|
||||||
|
if (!pending[slot].valid())
|
||||||
|
return slot;
|
||||||
|
|
||||||
|
throw Block::Driver::Request_congestion();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ack_packets()
|
||||||
|
{
|
||||||
|
unsigned slots = Port::read<Ci>() | Port::read<Sact>();
|
||||||
|
|
||||||
|
for (unsigned slot = 0; slot < cmd_slots; slot++) {
|
||||||
|
if ((slots & (1U << slot)) || !pending[slot].valid())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Block::Packet_descriptor p = pending[slot];
|
||||||
|
pending[slot] = Block::Packet_descriptor();
|
||||||
|
ack_packet(p, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void overlap_check(Block::sector_t block_number,
|
||||||
|
size_t count)
|
||||||
|
{
|
||||||
|
Block::sector_t end = block_number + count - 1;
|
||||||
|
|
||||||
|
for (unsigned slot = 0; slot < cmd_slots; slot++) {
|
||||||
|
if (!pending[slot].valid())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Block::sector_t pending_start = pending[slot].block_number();
|
||||||
|
Block::sector_t pending_end = pending_start + pending[slot].block_count() - 1;
|
||||||
|
/* check if a pending packet overlaps */
|
||||||
|
if ((block_number >= pending_start && block_number <= pending_end) ||
|
||||||
|
(end >= pending_start && end <= pending_end) ||
|
||||||
|
(pending_start >= block_number && pending_start <= end) ||
|
||||||
|
(pending_end >= block_number && pending_end <= end)) {
|
||||||
|
PWRN("Overlap: pending %llu + %zu, request: %llu + %zu", pending[slot].block_number(),
|
||||||
|
pending[slot].block_count(), block_number, count);
|
||||||
|
throw Block::Driver::Request_congestion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void io(bool read,
|
||||||
|
Block::sector_t block_number,
|
||||||
|
size_t count,
|
||||||
|
addr_t phys,
|
||||||
|
Block::Packet_descriptor &packet)
|
||||||
|
{
|
||||||
|
sanity_check(block_number, count);
|
||||||
|
overlap_check(block_number, count);
|
||||||
|
|
||||||
|
unsigned slot = find_free_cmd_slot();
|
||||||
|
pending[slot] = packet;
|
||||||
|
|
||||||
|
/* setup fis */
|
||||||
|
Command_table table(command_table_addr(slot), phys, count * block_size());
|
||||||
|
table.fis.fpdma(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*****************
|
||||||
|
** Port_driver **
|
||||||
|
*****************/
|
||||||
|
|
||||||
|
void handle_irq() override
|
||||||
|
{
|
||||||
|
Is::access_t status = Port::read<Is>();
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
check_device();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
ack_packets();
|
||||||
|
}
|
||||||
|
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
state = READY;
|
||||||
|
state_change();
|
||||||
|
}
|
||||||
|
|
||||||
|
void identify_device()
|
||||||
|
{
|
||||||
|
state = IDENTIFY;
|
||||||
|
addr_t phys = (addr_t)Dataspace_client(device_info_ds).phys_addr();
|
||||||
|
|
||||||
|
Command_table table(command_table_addr(0), phys, 0x1000);
|
||||||
|
table.fis.identify_device();
|
||||||
|
execute(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************
|
||||||
|
** Block::Driver interface **
|
||||||
|
*****************************/
|
||||||
|
|
||||||
|
bool dma_enabled() { return true; };
|
||||||
|
|
||||||
|
Block::Session::Operations ops() override
|
||||||
|
{
|
||||||
|
Block::Session::Operations o;
|
||||||
|
o.set_operation(Block::Packet_descriptor::READ);
|
||||||
|
o.set_operation(Block::Packet_descriptor::WRITE);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_dma(Block::sector_t block_number,
|
||||||
|
size_t block_count,
|
||||||
|
addr_t phys,
|
||||||
|
Block::Packet_descriptor &packet) override
|
||||||
|
{
|
||||||
|
io(true, block_number, block_count, phys, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_dma(Block::sector_t block_number,
|
||||||
|
size_t block_count,
|
||||||
|
addr_t phys,
|
||||||
|
Block::Packet_descriptor &packet) override
|
||||||
|
{
|
||||||
|
io(false, block_number, block_count, phys, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
Genode::size_t block_size() override
|
||||||
|
{
|
||||||
|
Genode::size_t size = 512;
|
||||||
|
|
||||||
|
if (info->read<Identity::Logical_block::Longer_512>())
|
||||||
|
size = info->read<Identity::Logical_words>() / 2;
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block::sector_t block_count() override
|
||||||
|
{
|
||||||
|
return info->read<Identity::Sector_count>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _ATA_DRIVER_H_ */
|
206
repos/os/src/drivers/ahci/atapi_driver.h
Normal file
206
repos/os/src/drivers/ahci/atapi_driver.h
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
/**
|
||||||
|
* \brief AHCI-port driver for ATAPI devices
|
||||||
|
* \author Sebastian Sumpf
|
||||||
|
* \date 2015-04-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ATAPI_DRIVER_H_
|
||||||
|
#define _ATAPI_DRIVER_H_
|
||||||
|
|
||||||
|
#include "ahci.h"
|
||||||
|
#include <util/endian.h>
|
||||||
|
|
||||||
|
using namespace Genode;
|
||||||
|
|
||||||
|
struct Atapi_driver : Port_driver
|
||||||
|
{
|
||||||
|
unsigned sense_tries = 0;
|
||||||
|
Block::Packet_descriptor pending;
|
||||||
|
|
||||||
|
Atapi_driver(Port &port, Signal_context_capability state_change)
|
||||||
|
: Port_driver(port, state_change)
|
||||||
|
{
|
||||||
|
Port::init();
|
||||||
|
Port::write<Cmd::Atapi>(1);
|
||||||
|
read_sense();
|
||||||
|
}
|
||||||
|
|
||||||
|
void atapi_command()
|
||||||
|
{
|
||||||
|
Command_header header(command_header_addr(0));
|
||||||
|
header.atapi_command();
|
||||||
|
header.clear_byte_count();
|
||||||
|
execute(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_unit_ready()
|
||||||
|
{
|
||||||
|
state = TEST_READY;
|
||||||
|
|
||||||
|
Command_table table(command_table_addr(0), 0, 0);
|
||||||
|
table.fis.atapi();
|
||||||
|
table.atapi_cmd.test_unit_ready();
|
||||||
|
|
||||||
|
atapi_command();
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_sense()
|
||||||
|
{
|
||||||
|
state = STATUS;
|
||||||
|
|
||||||
|
if (sense_tries++ >= 3) {
|
||||||
|
PERR("Could not power up device");
|
||||||
|
state_change();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr_t phys = (addr_t)Dataspace_client(device_info_ds).phys_addr();
|
||||||
|
|
||||||
|
Command_table table(command_table_addr(0), phys, 0x1000);
|
||||||
|
table.fis.atapi();
|
||||||
|
table.atapi_cmd.read_sense();
|
||||||
|
|
||||||
|
atapi_command();
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_capacity()
|
||||||
|
{
|
||||||
|
state = IDENTIFY;
|
||||||
|
addr_t phys = (addr_t)Dataspace_client(device_info_ds).phys_addr();
|
||||||
|
|
||||||
|
Command_table table(command_table_addr(0), phys, 0x1000);
|
||||||
|
table.fis.atapi();
|
||||||
|
table.atapi_cmd.read_capacity();
|
||||||
|
|
||||||
|
atapi_command();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ack_packets()
|
||||||
|
{
|
||||||
|
unsigned slots = Port::read<Ci>();
|
||||||
|
|
||||||
|
if (slots & 1 || !pending.valid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Block::Packet_descriptor p = pending;
|
||||||
|
pending = Block::Packet_descriptor();
|
||||||
|
ack_packet(p, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*****************
|
||||||
|
** Port_driver **
|
||||||
|
*****************/
|
||||||
|
|
||||||
|
void handle_irq() override
|
||||||
|
{
|
||||||
|
Is::access_t status = Port::read<Is>();
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
PDBG("irq: is: %x ci: %x state: %u", status, Port::read<Ci>(), state);
|
||||||
|
Device_fis f(fis_base);
|
||||||
|
PDBG("d2h: status: %x error: %x", f.read<Device_fis::Status>(), f.read<Device_fis::Error>());
|
||||||
|
}
|
||||||
|
|
||||||
|
ack_irq();
|
||||||
|
|
||||||
|
if (state == TEST_READY && Port::Is::Dhrs::get(status)) {
|
||||||
|
Device_fis f(fis_base);
|
||||||
|
|
||||||
|
/* check if devic is ready */
|
||||||
|
if (f.read<Device_fis::Status::Device_ready>() && !f.read<Device_fis::Error>())
|
||||||
|
read_capacity();
|
||||||
|
else
|
||||||
|
read_sense();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == READY && Port::Is::Dhrs::get(status)) {
|
||||||
|
ack_packets();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Port::Is::Dss::get(status) || Port::Is::Pss::get(status)) {
|
||||||
|
switch (state) {
|
||||||
|
|
||||||
|
case STATUS:
|
||||||
|
test_unit_ready();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IDENTIFY:
|
||||||
|
state = READY;
|
||||||
|
state_change();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READY:
|
||||||
|
ack_packets();
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************
|
||||||
|
** Block::Driver interface **
|
||||||
|
*****************************/
|
||||||
|
|
||||||
|
bool dma_enabled() { return true; };
|
||||||
|
|
||||||
|
Block::Session::Operations ops() override
|
||||||
|
{
|
||||||
|
Block::Session::Operations o;
|
||||||
|
o.set_operation(Block::Packet_descriptor::READ);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
Genode::size_t block_size() override
|
||||||
|
{
|
||||||
|
return host_to_big_endian(((unsigned *)device_info)[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Block::sector_t block_count() override
|
||||||
|
{
|
||||||
|
return host_to_big_endian(((unsigned *)device_info)[0]) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_dma(Block::sector_t block_number,
|
||||||
|
size_t count,
|
||||||
|
addr_t phys,
|
||||||
|
Block::Packet_descriptor &packet) override
|
||||||
|
{
|
||||||
|
if (pending.valid())
|
||||||
|
throw Block::Driver::Request_congestion();
|
||||||
|
|
||||||
|
sanity_check(block_number, count);
|
||||||
|
|
||||||
|
pending = packet;
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
PDBG("Add packet read %llu count %zu -> %u", block_number, count, 0);
|
||||||
|
|
||||||
|
/* setup fis */
|
||||||
|
Command_table table(command_table_addr(0), phys, count * block_size());
|
||||||
|
table.fis.atapi();
|
||||||
|
|
||||||
|
/* setup atapi command */
|
||||||
|
table.atapi_cmd.read10(block_number, count);
|
||||||
|
|
||||||
|
/* set and clear write flag in command header */
|
||||||
|
Command_header header(command_header_addr(0));
|
||||||
|
header.write<Command_header::Bits::W>(0);
|
||||||
|
header.clear_byte_count();
|
||||||
|
|
||||||
|
/* set pending */
|
||||||
|
execute(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _ATAPI_DRIVER_H_ */
|
@ -1,3 +0,0 @@
|
|||||||
/*
|
|
||||||
* Dummy compilation unit needed to link a valid target.
|
|
||||||
*/
|
|
File diff suppressed because it is too large
Load Diff
@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief AHCI driver declaration
|
|
||||||
* \author Martin Stein <martin.stein@genode-labs.com>
|
|
||||||
* \date 2013-04-10
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2013 Genode Labs GmbH
|
|
||||||
*
|
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
|
||||||
* under the terms of the GNU General Public License version 2.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _AHCI_DRIVER_H_
|
|
||||||
#define _AHCI_DRIVER_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <block/component.h>
|
|
||||||
#include <ram_session/ram_session.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AHCI driver
|
|
||||||
*/
|
|
||||||
class Ahci_driver : public Block::Driver
|
|
||||||
{
|
|
||||||
enum { VERBOSE = 0 };
|
|
||||||
|
|
||||||
/* import Genode symbols */
|
|
||||||
typedef Genode::size_t size_t;
|
|
||||||
typedef Genode::uint64_t uint64_t;
|
|
||||||
typedef Genode::addr_t addr_t;
|
|
||||||
|
|
||||||
int _ncq_command(uint64_t lba, unsigned cnt, addr_t phys, bool w);
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
Ahci_driver();
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************
|
|
||||||
** Block::Driver interface **
|
|
||||||
*****************************/
|
|
||||||
|
|
||||||
Block::Session::Operations ops()
|
|
||||||
{
|
|
||||||
Block::Session::Operations o;
|
|
||||||
o.set_operation(Block::Packet_descriptor::READ);
|
|
||||||
o.set_operation(Block::Packet_descriptor::WRITE);
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t block_size();
|
|
||||||
Block::sector_t block_count();
|
|
||||||
bool dma_enabled() { return true; }
|
|
||||||
|
|
||||||
Genode::Ram_dataspace_capability alloc_dma_buffer(Genode::size_t size) {
|
|
||||||
return Genode::env()->ram_session()->alloc(size, Genode::UNCACHED); }
|
|
||||||
|
|
||||||
void free_dma_buffer(Genode::Ram_dataspace_capability c) {
|
|
||||||
return Genode::env()->ram_session()->free(c); }
|
|
||||||
|
|
||||||
void read_dma(Block::sector_t block_nr, size_t block_cnt, addr_t phys,
|
|
||||||
Block::Packet_descriptor &packet)
|
|
||||||
{
|
|
||||||
if (_ncq_command(block_nr, block_cnt, phys, 0))
|
|
||||||
throw Io_error();
|
|
||||||
ack_packet(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_dma(Block::sector_t block_nr, size_t block_cnt, addr_t phys,
|
|
||||||
Block::Packet_descriptor &packet)
|
|
||||||
{
|
|
||||||
if (_ncq_command(block_nr, block_cnt, phys, 1))
|
|
||||||
throw Io_error();
|
|
||||||
ack_packet(packet);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _AHCI_DRIVER_H_ */
|
|
||||||
|
|
@ -1,198 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief SATA benchmark for Exynos5 platform
|
|
||||||
* \author Martin Stein
|
|
||||||
* \date 2012-06-05
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2012-2013 Genode Labs GmbH
|
|
||||||
*
|
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
|
||||||
* under the terms of the GNU General Public License version 2.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <base/sleep.h>
|
|
||||||
#include <base/printf.h>
|
|
||||||
#include <timer_session/connection.h>
|
|
||||||
#include <os/attached_ram_dataspace.h>
|
|
||||||
#include <dataspace/client.h>
|
|
||||||
|
|
||||||
/* local includes */
|
|
||||||
#include <ahci_driver.h>
|
|
||||||
|
|
||||||
|
|
||||||
struct Operation
|
|
||||||
{
|
|
||||||
virtual void operator () (Block::Driver &driver,
|
|
||||||
Genode::addr_t block_number,
|
|
||||||
Genode::size_t block_count,
|
|
||||||
Genode::addr_t buffer_phys,
|
|
||||||
char *buffer_virt) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
void print_bench_head()
|
|
||||||
{
|
|
||||||
Genode::printf("\n");
|
|
||||||
Genode::printf("bytes/block bytes sec MB/sec\n");
|
|
||||||
Genode::printf("----------------------------------------------\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* \param total_size total number of bytes to read
|
|
||||||
* \param request_size number of bytes per request
|
|
||||||
*/
|
|
||||||
static void run_benchmark(Block::Driver &driver,
|
|
||||||
Timer::Session &timer,
|
|
||||||
char *buffer_virt,
|
|
||||||
Genode::addr_t buffer_phys,
|
|
||||||
Genode::size_t buffer_size,
|
|
||||||
Genode::size_t request_size,
|
|
||||||
Operation &operation)
|
|
||||||
{
|
|
||||||
using namespace Genode;
|
|
||||||
if (request_size > buffer_size) {
|
|
||||||
PERR("undersized buffer %u, need %u", buffer_size, buffer_size);
|
|
||||||
while (1) ;
|
|
||||||
}
|
|
||||||
size_t const block_count = request_size / driver.block_size();
|
|
||||||
/*
|
|
||||||
* The goal is to get 5 test repetitions that took 2 s < time < 2.3 s,
|
|
||||||
* each. Thus we start with count = 32 and then adjust the count
|
|
||||||
* for a retry if the test time is not in range.
|
|
||||||
*/
|
|
||||||
unsigned tmp_bytes = 64 * request_size;;
|
|
||||||
unsigned bytes = 0;
|
|
||||||
unsigned ms = 0;
|
|
||||||
unsigned reps = 0;
|
|
||||||
float sec = 0;
|
|
||||||
float mb_per_sec = 0;
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
size_t num_requests = tmp_bytes / request_size;
|
|
||||||
|
|
||||||
/* do measurement */
|
|
||||||
unsigned const time_before_ms = timer.elapsed_ms();
|
|
||||||
for (unsigned i = 0; i < num_requests; i++)
|
|
||||||
{
|
|
||||||
addr_t const block_number = i * block_count;
|
|
||||||
operation(driver, block_number, block_count,
|
|
||||||
buffer_phys, buffer_virt);
|
|
||||||
}
|
|
||||||
/* read results */
|
|
||||||
unsigned const time_after_ms = timer.elapsed_ms();
|
|
||||||
ms = time_after_ms - time_before_ms;
|
|
||||||
|
|
||||||
/* check if test time in range */
|
|
||||||
if (ms < 2000 || ms >= 2300) {
|
|
||||||
/*
|
|
||||||
* adjust transfer amount according to measured time
|
|
||||||
*
|
|
||||||
* FIXME implement static inertia
|
|
||||||
*/
|
|
||||||
tmp_bytes = (((float) 2150 / ms) * tmp_bytes);
|
|
||||||
tmp_bytes &= 0xfffffe00; /* align to 512 byte blocks */
|
|
||||||
} else {
|
|
||||||
/* if new result is better than the last one do update */
|
|
||||||
float const tmp_mb = ((float)tmp_bytes / 1000) / 1000;
|
|
||||||
float const tmp_sec = (float)ms / 1000;
|
|
||||||
float const tmp_mb_per_sec = (float)tmp_mb / tmp_sec;
|
|
||||||
if (tmp_mb_per_sec > mb_per_sec) {
|
|
||||||
sec = tmp_sec;
|
|
||||||
mb_per_sec = tmp_mb_per_sec;
|
|
||||||
bytes = tmp_bytes;
|
|
||||||
}
|
|
||||||
/* check if we need more repetitions */
|
|
||||||
reps++;
|
|
||||||
if (reps == 5) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unsigned const sec_left = sec;
|
|
||||||
unsigned const sec_right = 1000 * ((float)sec - sec_left);
|
|
||||||
unsigned const mb_per_sec_left = mb_per_sec;
|
|
||||||
unsigned const mb_per_sec_right = 1000 * ((float)mb_per_sec - mb_per_sec_left);
|
|
||||||
PLOG(" %10u %10u %u.%03u %10u.%03u", request_size, bytes, sec_left, sec_right, mb_per_sec_left, mb_per_sec_right);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
using namespace Genode;
|
|
||||||
|
|
||||||
printf("AHCI bench\n");
|
|
||||||
printf("==========\n");
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
static Ahci_driver driver;
|
|
||||||
|
|
||||||
static Timer::Connection timer;
|
|
||||||
|
|
||||||
long const request_sizes[] = {
|
|
||||||
1048576, 262144, 16384, 8192, 4096, 2048, 1024, 512, 0 };
|
|
||||||
|
|
||||||
/* total size of communication buffer */
|
|
||||||
size_t const buffer_size = 1024*1024;
|
|
||||||
|
|
||||||
/* allocate read/write buffer */
|
|
||||||
static Attached_ram_dataspace buffer(env()->ram_session(), buffer_size, Genode::UNCACHED);
|
|
||||||
char * const buffer_virt = buffer.local_addr<char>();
|
|
||||||
addr_t const buffer_phys = Dataspace_client(buffer.cap()).phys_addr();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Benchmark reading from SATA device
|
|
||||||
*/
|
|
||||||
|
|
||||||
printf("read\n");
|
|
||||||
printf("~~~~\n");
|
|
||||||
print_bench_head();
|
|
||||||
|
|
||||||
struct Read : Operation
|
|
||||||
{
|
|
||||||
void operator () (Block::Driver &driver,
|
|
||||||
addr_t number, size_t count, addr_t phys, char *virt)
|
|
||||||
{
|
|
||||||
Block::Packet_descriptor packet;
|
|
||||||
if (driver.dma_enabled())
|
|
||||||
driver.read_dma(number, count, phys, packet);
|
|
||||||
else
|
|
||||||
driver.read(number, count, virt, packet);
|
|
||||||
}
|
|
||||||
} read_operation;
|
|
||||||
|
|
||||||
for (unsigned i = 0; request_sizes[i]; i++)
|
|
||||||
run_benchmark(driver, timer, buffer_virt, buffer_phys, buffer_size,
|
|
||||||
request_sizes[i], read_operation);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Benchmark writing to SATA device
|
|
||||||
*
|
|
||||||
* Attention: Original data will be overridden on target drive
|
|
||||||
*/
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
printf("write\n");
|
|
||||||
printf("~~~~~\n");
|
|
||||||
print_bench_head();
|
|
||||||
|
|
||||||
struct Write : Operation
|
|
||||||
{
|
|
||||||
void operator () (Block::Driver &driver,
|
|
||||||
addr_t number, size_t count, addr_t phys, char *virt)
|
|
||||||
{
|
|
||||||
Block::Packet_descriptor packet;
|
|
||||||
if (driver.dma_enabled())
|
|
||||||
driver.write_dma(number, count, phys, packet);
|
|
||||||
else
|
|
||||||
driver.write(number, count, virt, packet);
|
|
||||||
}
|
|
||||||
} write_operation;
|
|
||||||
|
|
||||||
for (unsigned i = 0; request_sizes[i]; i++)
|
|
||||||
run_benchmark(driver, timer, buffer_virt, buffer_phys, buffer_size,
|
|
||||||
request_sizes[i], write_operation);
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
printf("benchmark finished\n");
|
|
||||||
sleep_forever();
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
TARGET = ahci_bench
|
|
||||||
REQUIRES = exynos5
|
|
||||||
SRC_CC = main.cc ahci_driver.cc
|
|
||||||
LIBS = base
|
|
||||||
INC_DIR += $(PRG_DIR)/.. $(PRG_DIR)/../..
|
|
||||||
vpath ahci_driver.cc $(PRG_DIR)/..
|
|
385
repos/os/src/drivers/ahci/exynos5/platform.cc
Normal file
385
repos/os/src/drivers/ahci/exynos5/platform.cc
Normal file
@ -0,0 +1,385 @@
|
|||||||
|
/**
|
||||||
|
* \brief Driver for Exynos-5250 soc
|
||||||
|
* \author Martin Stein
|
||||||
|
* \author Sebastian Sumpf
|
||||||
|
* \date 2015-05-04
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <drivers/board_base.h>
|
||||||
|
#include <irq_session/connection.h>
|
||||||
|
#include <regulator/consts.h>
|
||||||
|
#include <regulator_session/connection.h>
|
||||||
|
|
||||||
|
#include <ahci.h>
|
||||||
|
|
||||||
|
using namespace Genode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I2C master interface
|
||||||
|
*/
|
||||||
|
struct I2c_interface : Attached_mmio
|
||||||
|
{
|
||||||
|
enum { TX_DELAY_US = 1 };
|
||||||
|
|
||||||
|
/********************************
|
||||||
|
** MMIO structure description **
|
||||||
|
********************************/
|
||||||
|
|
||||||
|
struct Start_msg : Genode::Register<8>
|
||||||
|
{
|
||||||
|
struct Addr : Bitfield<1, 7> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Con : Register<0x0, 8>
|
||||||
|
{
|
||||||
|
struct Tx_prescaler : Bitfield<0, 4> { };
|
||||||
|
struct Irq_pending : Bitfield<4, 1> { };
|
||||||
|
struct Irq_en : Bitfield<5, 1> { };
|
||||||
|
struct Clk_sel : Bitfield<6, 1> { };
|
||||||
|
struct Ack_en : Bitfield<7, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Stat : Register<0x4, 8>
|
||||||
|
{
|
||||||
|
struct Last_bit : Bitfield<0, 1> { };
|
||||||
|
struct Arbitr : Bitfield<3, 1> { };
|
||||||
|
struct Txrx_en : Bitfield<4, 1> { };
|
||||||
|
struct Busy : Bitfield<5, 1> { };
|
||||||
|
struct Mode : Bitfield<6, 2> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Add : Register<0x8, 8>
|
||||||
|
{
|
||||||
|
struct Slave_addr : Bitfield<0, 8> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Ds : Register<0xc, 8> { };
|
||||||
|
|
||||||
|
struct Lc : Register<0x10, 8>
|
||||||
|
{
|
||||||
|
struct Sda_out_delay : Bitfield<0, 2> { };
|
||||||
|
struct Filter_en : Bitfield<2, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
Mmio::Delayer &delayer;
|
||||||
|
/* single-word message that starts a multi-word message transfer */
|
||||||
|
Start_msg::access_t const start_msg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* \param base physical MMIO base
|
||||||
|
* \param slave_addr ID of the targeted slave
|
||||||
|
*/
|
||||||
|
I2c_interface(addr_t base, unsigned slave_addr, Mmio::Delayer &delayer)
|
||||||
|
: Attached_mmio(base, 0x10000), delayer(delayer),
|
||||||
|
start_msg(Start_msg::Addr::bits(slave_addr))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wether acknowledgment for last transaction can be received
|
||||||
|
*/
|
||||||
|
bool ack_received()
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < 3; i++) {
|
||||||
|
if (read<Con::Irq_pending>() && !read<Stat::Last_bit>()) return 1;
|
||||||
|
delayer.usleep(TX_DELAY_US);
|
||||||
|
}
|
||||||
|
PERR("I2C ack not received");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wether arbitration errors occured during the last transaction
|
||||||
|
*/
|
||||||
|
bool arbitration_error()
|
||||||
|
{
|
||||||
|
if (read<Stat::Arbitr>()) {
|
||||||
|
PERR("I2C arbitration failed");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Let I2C master send a message to I2C slave
|
||||||
|
*
|
||||||
|
* \param msg message base
|
||||||
|
* \param msg_size message size
|
||||||
|
*
|
||||||
|
* \retval 0 call was successful
|
||||||
|
* \retval <0 call failed, error code
|
||||||
|
*/
|
||||||
|
int send(uint8_t * msg, size_t msg_size)
|
||||||
|
{
|
||||||
|
/* initiate message transfer */
|
||||||
|
if (!wait_for<Stat::Busy>(0, delayer)) {
|
||||||
|
PERR("I2C busy");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Stat::access_t stat = read<Stat>();
|
||||||
|
Stat::Txrx_en::set(stat, 1);
|
||||||
|
Stat::Mode::set(stat, 3);
|
||||||
|
write<Stat>(stat);
|
||||||
|
write<Ds>(start_msg);
|
||||||
|
delayer.usleep(1000);
|
||||||
|
write<Con::Tx_prescaler>(11);
|
||||||
|
write<Stat::Busy>(1);
|
||||||
|
|
||||||
|
/* transmit message payload */
|
||||||
|
for (unsigned i = 0; i < msg_size; i++) {
|
||||||
|
if (!ack_received()) return -1;
|
||||||
|
write<Ds>(msg[i]);
|
||||||
|
delayer.usleep(TX_DELAY_US);
|
||||||
|
write<Con::Irq_pending>(0);
|
||||||
|
if (arbitration_error()) return -1;
|
||||||
|
}
|
||||||
|
/* end message transfer */
|
||||||
|
if (!ack_received()) return -1;
|
||||||
|
write<Stat::Busy>(0);
|
||||||
|
write<Con::Irq_en>(0);
|
||||||
|
write<Con::Irq_pending>(0); /* FIXME fixup */
|
||||||
|
if (arbitration_error()) return -1;
|
||||||
|
if (!wait_for<Stat::Busy>(0, delayer)) {
|
||||||
|
PERR("I2C end transfer failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I2C control interface of SATA PHY-layer controller
|
||||||
|
*/
|
||||||
|
struct I2c_sataphy : I2c_interface
|
||||||
|
{
|
||||||
|
enum { SLAVE_ADDR = 0x38 };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
I2c_sataphy(Mmio::Delayer &delayer)
|
||||||
|
: I2c_interface(0x121d0000, SLAVE_ADDR, delayer)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the 40-pin interface of the SATA PHY controller
|
||||||
|
*
|
||||||
|
* \retval 0 call was successful
|
||||||
|
* \retval <0 call failed, error code
|
||||||
|
*/
|
||||||
|
int enable_40_pins()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* I2C message
|
||||||
|
*
|
||||||
|
* first byte: set address
|
||||||
|
* second byte: set data
|
||||||
|
*/
|
||||||
|
static uint8_t msg[] = { 0x3a, 0x0b };
|
||||||
|
enum { MSG_SIZE = sizeof(msg)/sizeof(msg[0]) };
|
||||||
|
|
||||||
|
/* send messaage */
|
||||||
|
if (send(msg, MSG_SIZE)) return -1;
|
||||||
|
if (verbose) printf("SATA PHY 40-pin interface enabled\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get I2C interface ready for transmissions
|
||||||
|
*/
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
write<Add::Slave_addr>(SLAVE_ADDR);
|
||||||
|
|
||||||
|
Con::access_t con = read<Con>();
|
||||||
|
Con::Irq_en::set(con, 1);
|
||||||
|
Con::Ack_en::set(con, 1);
|
||||||
|
Con::Clk_sel::set(con, 1);
|
||||||
|
Con::Tx_prescaler::set(con, 9);
|
||||||
|
write<Con>(con);
|
||||||
|
|
||||||
|
Lc::access_t lc = 0;
|
||||||
|
Lc::Sda_out_delay::set(lc, 3);
|
||||||
|
Lc::Filter_en::set(lc, 1);
|
||||||
|
write<Lc>(lc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classical control interface of SATA PHY-layer controller
|
||||||
|
*/
|
||||||
|
struct Sata_phy_ctrl : Attached_mmio
|
||||||
|
{
|
||||||
|
Mmio::Delayer &delayer;
|
||||||
|
I2c_sataphy i2c_sataphy { delayer };
|
||||||
|
|
||||||
|
/********************************
|
||||||
|
** MMIO structure description **
|
||||||
|
********************************/
|
||||||
|
|
||||||
|
struct Reset : Register<0x4, 32>
|
||||||
|
{
|
||||||
|
struct Global : Bitfield<1, 1> { };
|
||||||
|
struct Non_link : Bitfield<0, 8> { };
|
||||||
|
struct Link : Bitfield<16, 4> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Mode0 : Register<0x10, 32>
|
||||||
|
{
|
||||||
|
struct P0_phy_spdmode : Bitfield<0, 2> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Ctrl0 : Register<0x14, 32>
|
||||||
|
{
|
||||||
|
struct P0_phy_calibrated : Bitfield<8, 1> { };
|
||||||
|
struct P0_phy_calibrated_sel : Bitfield<9, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Phctrlm : Register<0xe0, 32>
|
||||||
|
{
|
||||||
|
struct High_speed : Bitfield<0, 1> { };
|
||||||
|
struct Ref_rate : Bitfield<1, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Phstatm : Register<0xf0, 32>
|
||||||
|
{
|
||||||
|
struct Pll_locked : Bitfield<0, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
Sata_phy_ctrl(Mmio::Delayer &delayer)
|
||||||
|
: Attached_mmio(0x12170000, 0x10000), delayer(delayer)
|
||||||
|
{
|
||||||
|
i2c_sataphy.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize parts of SATA PHY that are controlled classically
|
||||||
|
*
|
||||||
|
* \retval 0 call was successful
|
||||||
|
* \retval <0 call failed, error code
|
||||||
|
*/
|
||||||
|
int init()
|
||||||
|
{
|
||||||
|
/* reset */
|
||||||
|
write<Reset>(0);
|
||||||
|
write<Reset::Non_link>(~0);
|
||||||
|
write<Reset::Link>(~0);
|
||||||
|
write<Reset::Global>(~0);
|
||||||
|
|
||||||
|
/* set up SATA phy generation 3 (6 Gb/s) */
|
||||||
|
Phctrlm::access_t phctrlm = read<Phctrlm>();
|
||||||
|
Phctrlm::Ref_rate::set(phctrlm, 0);
|
||||||
|
Phctrlm::High_speed::set(phctrlm, 1);
|
||||||
|
write<Phctrlm>(phctrlm);
|
||||||
|
Ctrl0::access_t ctrl0 = read<Ctrl0>();
|
||||||
|
Ctrl0::P0_phy_calibrated::set(ctrl0, 1);
|
||||||
|
Ctrl0::P0_phy_calibrated_sel::set(ctrl0, 1);
|
||||||
|
write<Ctrl0>(ctrl0);
|
||||||
|
write<Mode0::P0_phy_spdmode>(2);
|
||||||
|
if (i2c_sataphy.enable_40_pins()) return -1;
|
||||||
|
|
||||||
|
/* Release reset */
|
||||||
|
write<Reset::Global>(0);
|
||||||
|
write<Reset::Global>(1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FIXME Linux reads this bit once only and continues
|
||||||
|
* directly, also with zero. So if we get an error
|
||||||
|
* at this point we should study the Linux behavior
|
||||||
|
* in more depth.
|
||||||
|
*/
|
||||||
|
if (!wait_for<Phstatm::Pll_locked>(1, delayer)) {
|
||||||
|
PERR("PLL lock failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (verbose)
|
||||||
|
printf("SATA PHY initialized\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Exynos5_hba : Platform::Hba
|
||||||
|
{
|
||||||
|
Irq_connection irq { Board_base::SATA_IRQ };
|
||||||
|
Regulator::Connection clock_src { Regulator::CLK_SATA };
|
||||||
|
Regulator::Connection power_src { Regulator::PWR_SATA };
|
||||||
|
|
||||||
|
Exynos5_hba(Mmio::Delayer &delayer)
|
||||||
|
{
|
||||||
|
clock_src.state(true);
|
||||||
|
power_src.state(true);
|
||||||
|
|
||||||
|
Sata_phy_ctrl phy(delayer);
|
||||||
|
|
||||||
|
if (phy.init())
|
||||||
|
throw Root::Unavailable();
|
||||||
|
|
||||||
|
/* additionally perform some generic initializations */
|
||||||
|
::Hba hba(*this);
|
||||||
|
|
||||||
|
::Hba::Cap::access_t cap = hba.read< ::Hba::Cap>();
|
||||||
|
::Hba::Cap2::access_t cap2 = hba.read< ::Hba::Cap2>();
|
||||||
|
|
||||||
|
/* reset */
|
||||||
|
hba.write< ::Hba::Ghc::Hr>(1);
|
||||||
|
if (!hba.wait_for< ::Hba::Ghc::Hr>(0, ::Hba::delayer(), 1000, 1000)) {
|
||||||
|
PERR("HBA reset failed");
|
||||||
|
throw Root::Unavailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
hba.write< ::Hba::Cap>(cap);
|
||||||
|
hba.write< ::Hba::Cap2>(cap2);
|
||||||
|
|
||||||
|
/* for exynos set port 0 as implemented, usally set by BIOS */
|
||||||
|
hba.write< ::Hba::Pi>(0x1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************
|
||||||
|
** Hba interface **
|
||||||
|
*******************/
|
||||||
|
|
||||||
|
Genode::addr_t base() const override { return 0x122f0000; }
|
||||||
|
Genode::size_t size() const override { return 0x10000; }
|
||||||
|
|
||||||
|
void sigh_irq(Signal_context_capability sigh) override
|
||||||
|
{
|
||||||
|
irq.sigh(sigh);
|
||||||
|
ack_irq();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ack_irq() override { irq.ack_irq(); }
|
||||||
|
|
||||||
|
Ram_dataspace_capability
|
||||||
|
alloc_dma_buffer(size_t size) override
|
||||||
|
{
|
||||||
|
return env()->ram_session()->alloc(size, UNCACHED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_dma_buffer(Ram_dataspace_capability ds)
|
||||||
|
{
|
||||||
|
env()->ram_session()->free(ds);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Platform::Hba &Platform::init(Mmio::Delayer &delayer)
|
||||||
|
{
|
||||||
|
static Exynos5_hba h(delayer);
|
||||||
|
return h;
|
||||||
|
}
|
@ -1,578 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief Generic base of AHCI device
|
|
||||||
* \author Sebastian Sumpf <Sebastian.Sumpf@genode-labs.com>
|
|
||||||
* \author Martin Stein <Martin.Stein@genode-labs.com>
|
|
||||||
* \date 2011-08-10
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2011-2013 Genode Labs GmbH
|
|
||||||
*
|
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
|
||||||
* under the terms of the GNU General Public License version 2.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _AHCI_DEVICE_BASE_H_
|
|
||||||
#define _AHCI_DEVICE_BASE_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <base/printf.h>
|
|
||||||
#include <base/sleep.h>
|
|
||||||
#include <block/driver.h>
|
|
||||||
#include <block/component.h>
|
|
||||||
#include <cap_session/connection.h>
|
|
||||||
#include <dataspace/client.h>
|
|
||||||
#include <irq_session/connection.h>
|
|
||||||
#include <io_mem_session/connection.h>
|
|
||||||
#include <timer_session/connection.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable for debugging output
|
|
||||||
*/
|
|
||||||
static const bool verbose = false;
|
|
||||||
|
|
||||||
using namespace Genode;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for register access
|
|
||||||
*/
|
|
||||||
class Reg_base
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
|
|
||||||
uint32_t _value(uint32_t offset) { return *(volatile uint32_t *)(this + offset); }
|
|
||||||
void _set(uint32_t offset, uint32_t val) { *(volatile uint32_t *)(this + offset) = val; }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* HBA: Generic Host Control
|
|
||||||
*/
|
|
||||||
class Generic_ctrl : public Reg_base
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/* host capabilities */
|
|
||||||
uint32_t hba_cap() { return _value(0x0); }
|
|
||||||
|
|
||||||
/* return port count from hba_cap */
|
|
||||||
uint32_t port_count() { return (hba_cap() & 0x1f) + 1; }
|
|
||||||
|
|
||||||
/* return command slot count from hba_cap */
|
|
||||||
uint32_t cmd_slots() { return ((hba_cap() >> 8) & 0x1f) + 1; }
|
|
||||||
|
|
||||||
/* global host control */
|
|
||||||
uint32_t hba_ctrl() { return _value(0x4); }
|
|
||||||
void hba_ctrl(uint32_t val) { _set(0x4, val); }
|
|
||||||
|
|
||||||
/* set Interrupt Enable (IE) in hba_ctrl */
|
|
||||||
void global_interrupt_enable()
|
|
||||||
{
|
|
||||||
hba_ctrl(hba_ctrl() | 2);
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
PDBG("HBA %x", hba_ctrl());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set AHCI enable (AE) in hba_ctrl */
|
|
||||||
void global_enable_ahci()
|
|
||||||
{
|
|
||||||
if (!(hba_ctrl() & (1 << 31)))
|
|
||||||
hba_ctrl(hba_ctrl() | (1 << 31));
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
PDBG("AHCI ENABLED: %x", hba_ctrl());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* global interrupt status (contains port interrupts) */
|
|
||||||
uint32_t hba_intr_status() { return _value(0x8); }
|
|
||||||
void hba_intr_status(uint32_t val) { return _set(0x8, val); }
|
|
||||||
|
|
||||||
/* acknowledge global interrupt */
|
|
||||||
void hba_interrupt_ack() { hba_intr_status(hba_intr_status()); }
|
|
||||||
|
|
||||||
/* AHCI version */
|
|
||||||
uint32_t version() { return _value(0x10); }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* AHCI port registers (one set per port)
|
|
||||||
*/
|
|
||||||
class Ahci_port : public Reg_base
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/* command list base */
|
|
||||||
void cmd_list_base(addr_t cmd_base)
|
|
||||||
{
|
|
||||||
uint64_t addr = cmd_base;
|
|
||||||
|
|
||||||
_set(0x0, addr);
|
|
||||||
_set(0x4, addr >> 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* receive FIS base address */
|
|
||||||
void fis_base(addr_t fis_base)
|
|
||||||
{
|
|
||||||
uint64_t addr = fis_base;
|
|
||||||
|
|
||||||
_set(0x8, addr);
|
|
||||||
_set(0xc, addr >> 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* interrupt status */
|
|
||||||
uint32_t intr_status() { return _value(0x10); }
|
|
||||||
void intr_status(uint32_t val) { _set(0x10, val); }
|
|
||||||
|
|
||||||
/* interrupt enable */
|
|
||||||
void intr_enable(uint32_t val) { _set(0x14, val); }
|
|
||||||
|
|
||||||
/* command */
|
|
||||||
uint32_t cmd() { return _value(0x18); }
|
|
||||||
void cmd(uint32_t val) { _set(0x18, val); }
|
|
||||||
|
|
||||||
/* task file data */
|
|
||||||
uint32_t tfd() { return _value(0x20); }
|
|
||||||
|
|
||||||
/* Serial ATA status */
|
|
||||||
uint32_t status() { return _value(0x28); }
|
|
||||||
|
|
||||||
/* Serial ATA control */
|
|
||||||
void sctl(uint32_t val) { _set(0x2c, val); }
|
|
||||||
uint32_t sctl() { return _value(0x2c); }
|
|
||||||
|
|
||||||
/* Serial ATA error */
|
|
||||||
void err(uint32_t val) { _set(0x30, val); }
|
|
||||||
uint32_t err() { return _value(0x30); }
|
|
||||||
|
|
||||||
/* command issue (1 bit per command slot) */
|
|
||||||
void cmd_issue(uint32_t val) { _set(0x38, val); }
|
|
||||||
uint32_t cmd_issue() { return _value(0x38); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if device is active
|
|
||||||
*/
|
|
||||||
bool status_active()
|
|
||||||
{
|
|
||||||
enum {
|
|
||||||
PRESENT_ESTABLISHED = 0x3, /* device is present and connection is established */
|
|
||||||
PM_ACTIVE = 0x100, /* interface is in active power-mngmt. state */
|
|
||||||
PM_PARTIRL = 0x200, /* interface is in partial power-mngmt. state */
|
|
||||||
PM_SLUMBER = 0x600, /* interface is in slumber power-mngmt. state */
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t stat = status();
|
|
||||||
uint32_t pm_stat = stat & 0xf00;
|
|
||||||
|
|
||||||
/* if controller is in sleep state, try to wake up */
|
|
||||||
if (pm_stat == PM_PARTIRL || pm_stat == PM_SLUMBER) {
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
PDBG("Controller is in sleep state, trying to wake up ...");
|
|
||||||
|
|
||||||
cmd(cmd() | (1 << 28));
|
|
||||||
|
|
||||||
while (!(stat & PM_ACTIVE) || (stat & 0xf) != PRESENT_ESTABLISHED) { stat = status(); }
|
|
||||||
}
|
|
||||||
return (((stat & 0xf) == PRESENT_ESTABLISHED) && (stat & PM_ACTIVE));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable CMD.ST to start command list processing
|
|
||||||
*/
|
|
||||||
void hba_enable()
|
|
||||||
{
|
|
||||||
enum {
|
|
||||||
STS_BSY = 0x80, /* device is busy */
|
|
||||||
STS_DRQ = 0x08, /* data transfer requested */
|
|
||||||
};
|
|
||||||
|
|
||||||
while (tfd() & (STS_BSY | STS_DRQ))
|
|
||||||
if (verbose)
|
|
||||||
PDBG("TFD %x", tfd());
|
|
||||||
|
|
||||||
cmd(cmd() | 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disable CMD.ST
|
|
||||||
*/
|
|
||||||
void hba_disable()
|
|
||||||
{
|
|
||||||
if ((cmd() & 1) && !(cmd_issue() & 1))
|
|
||||||
cmd(cmd() & ~1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable port interrupts
|
|
||||||
*/
|
|
||||||
void interrupt_enable() { intr_enable(~0U); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Acknowledge port interrupts
|
|
||||||
*/
|
|
||||||
uint32_t interrupt_ack()
|
|
||||||
{
|
|
||||||
interrupt_pm_ack();
|
|
||||||
|
|
||||||
uint32_t status = intr_status();
|
|
||||||
intr_status(status);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check and handle interrupts due to power mgmt. state transitions
|
|
||||||
*/
|
|
||||||
void interrupt_pm_ack()
|
|
||||||
{
|
|
||||||
enum {
|
|
||||||
INT_PORT_CON_STATUS = 0x40,
|
|
||||||
INT_PHY_RDY_STATUS = 0x400000,
|
|
||||||
};
|
|
||||||
uint32_t status = intr_status();
|
|
||||||
|
|
||||||
if (status & INT_PORT_CON_STATUS)
|
|
||||||
|
|
||||||
/* clear DIAG.x */
|
|
||||||
err(err() & ~(1 << 26));
|
|
||||||
|
|
||||||
if (status & INT_PORT_CON_STATUS)
|
|
||||||
|
|
||||||
/* clear DIAG.n */
|
|
||||||
err(err() & ~(1 << 16));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disable power mgmt. set SCTL.IPM to 3
|
|
||||||
*/
|
|
||||||
void disable_pm() { sctl(sctl() | (3 << 8)); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Power up device
|
|
||||||
*/
|
|
||||||
void get_ready()
|
|
||||||
{
|
|
||||||
enum {
|
|
||||||
SPIN_UP_DEVICE = 0x2,
|
|
||||||
POWER_ON_DEVICE = 0x4,
|
|
||||||
FIS_RECV_ENABLE = 0x10,
|
|
||||||
ENABLE = SPIN_UP_DEVICE | POWER_ON_DEVICE | FIS_RECV_ENABLE
|
|
||||||
};
|
|
||||||
cmd(cmd() | ENABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset this port
|
|
||||||
*/
|
|
||||||
void reset()
|
|
||||||
{
|
|
||||||
/* check for ST bit in command register */
|
|
||||||
if (cmd() & 1)
|
|
||||||
PWRN("CMD.ST bit set during device reset --> unknown behavior");
|
|
||||||
|
|
||||||
/* set device initialization bit for at least 1ms */
|
|
||||||
sctl((sctl() & ~0xf) | 1);
|
|
||||||
|
|
||||||
Timer::Connection timer;
|
|
||||||
timer.msleep(1);
|
|
||||||
|
|
||||||
sctl(sctl() & ~0xf);
|
|
||||||
|
|
||||||
while ((status() & 0xf) != 0x3) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the size of this structure
|
|
||||||
*/
|
|
||||||
static uint32_t size() { return 0x80; }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AHCI command list structure
|
|
||||||
*/
|
|
||||||
struct Command_list
|
|
||||||
{
|
|
||||||
uint8_t cfl:5; /* Command FIS length */
|
|
||||||
uint8_t a:1; /* ATAPI command flag */
|
|
||||||
uint8_t w:1; /* Write flag */
|
|
||||||
uint8_t p:1; /* Prefetchable flag */
|
|
||||||
uint8_t unsused; /* we don't use byte 2 yet */
|
|
||||||
uint16_t prdtl; /* Physical region descr. length */
|
|
||||||
uint32_t prdbc; /* PRD byte count */
|
|
||||||
uint32_t cmd_table_base_l; /* Command table base addr (low) */
|
|
||||||
uint32_t cmd_table_base_u;
|
|
||||||
uint32_t reserved[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AHCI command table structure
|
|
||||||
*/
|
|
||||||
struct Command_table
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Setup FIS and PRD
|
|
||||||
*/
|
|
||||||
void setup_command(uint8_t cmd, uint64_t lba48, uint16_t blk_cnt, addr_t phys_addr)
|
|
||||||
{
|
|
||||||
enum { MAX_BYTES = 1 << 22 }; /* 4MB = one PRD */
|
|
||||||
uint8_t *fis = (uint8_t *)this;
|
|
||||||
|
|
||||||
uint64_t addr = phys_addr;
|
|
||||||
|
|
||||||
/* setup FIS */
|
|
||||||
fis[0] = 0x27; /* type = host to device */
|
|
||||||
fis[1] = 0x80; /* set update command flag */
|
|
||||||
fis[2] = cmd; /* actual command */
|
|
||||||
fis[4] = lba48 & 0xff; /* LBA 0 - 7 */
|
|
||||||
fis[5] = (lba48 >> 8) & 0xff; /* LBA 8 - 15 */
|
|
||||||
fis[6] = (lba48 >> 16) & 0xff; /* LBA 16 - 23 */
|
|
||||||
fis[7] = 0x40; /* LBA mode flag */
|
|
||||||
fis[8] = (lba48 >> 24) & 0xff; /* LBA 24 - 31 */
|
|
||||||
fis[9] = (lba48 >> 32) & 0xff; /* LBA 32 - 39 */
|
|
||||||
fis[10] = (lba48 >> 40) & 0xff; /* LBA 40 - 47 */
|
|
||||||
fis[12] = blk_cnt & 0xff; /* sector count 0 - 7 */
|
|
||||||
fis[13] = (blk_cnt >> 8) & 0xff; /* sector count 8 - 15 */
|
|
||||||
|
|
||||||
/* setup PRD for DMA */
|
|
||||||
uint32_t addr_l = addr;
|
|
||||||
uint32_t addr_u = addr >> 32;
|
|
||||||
memcpy(&fis[0x80], &addr_l, 4); /* DBA: data base address */
|
|
||||||
memcpy(&fis[0x84], &addr_u, 4); /* DBA: data base address upper */
|
|
||||||
uint32_t bytes = (blk_cnt * 512) - 1;
|
|
||||||
|
|
||||||
if (bytes + 1 > MAX_BYTES) {
|
|
||||||
PERR("Unsupported request size %u > %u", bytes, MAX_BYTES);
|
|
||||||
throw Block::Driver::Io_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set byte count for PRD 22 bit */
|
|
||||||
fis[0x8c] = bytes & 0xff;
|
|
||||||
fis[0x8d] = (bytes >> 8) & 0xff;
|
|
||||||
fis[0x8e] = (bytes >> 16) & 0x3f;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generic base of AHCI device
|
|
||||||
*/
|
|
||||||
class Ahci_device_base
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
|
|
||||||
enum {
|
|
||||||
AHCI_PORT_BASE = 0x100,
|
|
||||||
};
|
|
||||||
|
|
||||||
Generic_ctrl *_ctrl; /* generic host control */
|
|
||||||
Ahci_port *_port; /* port base of device */
|
|
||||||
Irq_session_client *_irq; /* device IRQ */
|
|
||||||
Genode::Signal_receiver _irq_rec; /* IRQ signal receiver */
|
|
||||||
Genode::Signal_context _irq_ctx; /* IRQ signal context */
|
|
||||||
size_t _block_cnt; /* number of blocks on device */
|
|
||||||
Command_list *_cmd_list; /* pointer to command list */
|
|
||||||
Command_table *_cmd_table; /* pointer to command table */
|
|
||||||
Ram_dataspace_capability _ds; /* backing-store of internal data structures */
|
|
||||||
Io_mem_session_capability _io_cap; /* I/O mem cap */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find first non-ATAPI device that is ready
|
|
||||||
*/
|
|
||||||
bool _scan_ports()
|
|
||||||
{
|
|
||||||
uint32_t port_cnt = _ctrl->port_count();
|
|
||||||
|
|
||||||
Ahci_port *port = (Ahci_port *)((char *)_ctrl + AHCI_PORT_BASE);
|
|
||||||
for (uint32_t i = 0;
|
|
||||||
i <= port_cnt;
|
|
||||||
i++, port = (Ahci_port *)((char *)port + Ahci_port::size())) {
|
|
||||||
|
|
||||||
bool is_atapi = port->cmd() & (1 << 24); /* check bit 24 */
|
|
||||||
PINF("Port %u: ATAPI %s", i, is_atapi ? "yes" : "no");
|
|
||||||
|
|
||||||
if (is_atapi)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* port status */
|
|
||||||
if (port->status_active()) {
|
|
||||||
PINF("Port %u: Detected interface is active", i);
|
|
||||||
_port = port;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _setup_memory()
|
|
||||||
{
|
|
||||||
_ds = alloc_dma_buffer(0x1000);
|
|
||||||
addr_t phys = Dataspace_client(_ds).phys_addr();
|
|
||||||
uint8_t *virt = (uint8_t *)env()->rm_session()->attach(_ds);
|
|
||||||
|
|
||||||
/* setup command list (size 1k naturally aligned) */
|
|
||||||
_port->cmd_list_base(phys);
|
|
||||||
_cmd_list = (struct Command_list *)(virt);
|
|
||||||
|
|
||||||
/* for now we transfer one PRD with a FIS size of 5 byte */
|
|
||||||
_cmd_list->prdtl = 1;
|
|
||||||
_cmd_list->cfl = 5;
|
|
||||||
virt += 1024; phys += 1024;
|
|
||||||
|
|
||||||
/* setup received FIS base (256 byte naturally aligned) */
|
|
||||||
_port->fis_base(phys);
|
|
||||||
virt += 256; phys += 256;
|
|
||||||
|
|
||||||
uint64_t addr = phys;
|
|
||||||
/* setup command table (128 byte aligned (cache line size)) */
|
|
||||||
_cmd_list->cmd_table_base_l = addr;
|
|
||||||
_cmd_list->cmd_table_base_u = addr >> 32;
|
|
||||||
_cmd_table = (struct Command_table *)(virt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute a prepared command
|
|
||||||
*/
|
|
||||||
void _execute_command()
|
|
||||||
{
|
|
||||||
/* reset byte count */
|
|
||||||
_cmd_list->prdbc = 0;
|
|
||||||
|
|
||||||
/* start HBA command processing */
|
|
||||||
_port->hba_enable();
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
PDBG("Int status: global: %x port: %x error: %x",
|
|
||||||
_ctrl->hba_intr_status(), _port->intr_status(), _port->err());
|
|
||||||
|
|
||||||
/* write CI (command issue) slot 0 */
|
|
||||||
_port->cmd_issue(1);
|
|
||||||
|
|
||||||
uint32_t status = 0;
|
|
||||||
while (!status) {
|
|
||||||
|
|
||||||
/* wait for interrupt */
|
|
||||||
_irq_rec.wait_for_signal();
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
PDBG("Int status (IRQ): global: %x port: %x error: %x",
|
|
||||||
_ctrl->hba_intr_status(), _port->intr_status(), _port->err());
|
|
||||||
|
|
||||||
/* acknowledge interrupt */
|
|
||||||
status = _port->interrupt_ack();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check for error */
|
|
||||||
enum {
|
|
||||||
INT_SETUP_FIS_DMA = 0x4,
|
|
||||||
INT_SETUP_FIS_PIO = 0x2,
|
|
||||||
INT_HOST_REGISTER_FIS = 0x1,
|
|
||||||
INT_OK = INT_SETUP_FIS_DMA | INT_SETUP_FIS_PIO | INT_HOST_REGISTER_FIS
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!(status & INT_OK)) {
|
|
||||||
PERR("Error during SATA request (irq state %x)", status);
|
|
||||||
throw Block::Driver::Io_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* acknowledge global port interrupt */
|
|
||||||
_ctrl->hba_interrupt_ack();
|
|
||||||
|
|
||||||
_irq->ack_irq();
|
|
||||||
|
|
||||||
/* disable hba */
|
|
||||||
_port->hba_disable();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute ATA 'IDENTIFY DEVICE' command
|
|
||||||
*/
|
|
||||||
void _identify_device()
|
|
||||||
{
|
|
||||||
Ram_dataspace_capability ds = alloc_dma_buffer(0x1000);
|
|
||||||
uint16_t *dev_info = (uint16_t *)env()->rm_session()->attach(ds);
|
|
||||||
|
|
||||||
enum { IDENTIFY_DEVICE = 0xec };
|
|
||||||
try {
|
|
||||||
addr_t phys = Dataspace_client(ds).phys_addr();
|
|
||||||
_cmd_table->setup_command(IDENTIFY_DEVICE, 0, 0, phys);
|
|
||||||
_execute_command();
|
|
||||||
|
|
||||||
/* XXX: just read 32 bit for now */
|
|
||||||
_block_cnt = *((size_t *)&dev_info[100]);
|
|
||||||
|
|
||||||
} catch (Block::Driver::Io_error) {
|
|
||||||
PERR("I/O Error: Could not identify device");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
PDBG("Max LBA48 block: %zu", _block_cnt);
|
|
||||||
|
|
||||||
env()->rm_session()->detach(dev_info);
|
|
||||||
env()->ram_session()->free(ds);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ahci_device_base(addr_t base, Io_mem_session_capability io_cap)
|
|
||||||
: _ctrl((Generic_ctrl *)base), _port(0), _irq(0), _cmd_list(0),
|
|
||||||
_cmd_table(0), _io_cap(io_cap) { }
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
virtual ~Ahci_device_base()
|
|
||||||
{
|
|
||||||
/* delete internal data structures */
|
|
||||||
if (_ds.valid()) {
|
|
||||||
env()->rm_session()->detach((void*)_cmd_list);
|
|
||||||
env()->ram_session()->free(_ds);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* close I/O mem session */
|
|
||||||
env()->rm_session()->detach((void *)_ctrl);
|
|
||||||
env()->parent()->close(_io_cap);
|
|
||||||
|
|
||||||
/* XXX release _pci_device */
|
|
||||||
|
|
||||||
/* close IRQ session */
|
|
||||||
destroy(env()->heap(), _irq);
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t block_size() { return 512; }
|
|
||||||
size_t block_count() { return _block_cnt; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issue ATA 'READ_DMA_EXT' command
|
|
||||||
*/
|
|
||||||
void read(Block::sector_t block_number, size_t block_count,
|
|
||||||
addr_t phys)
|
|
||||||
{
|
|
||||||
_cmd_list->w = 0;
|
|
||||||
|
|
||||||
enum { READ_DMA_EXT = 0x25 };
|
|
||||||
_cmd_table->setup_command(READ_DMA_EXT, block_number,
|
|
||||||
block_count, phys);
|
|
||||||
_execute_command();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issue ATA 'WRITE_DMA_EXT' command
|
|
||||||
*/
|
|
||||||
void write(Block::sector_t block_number, size_t block_count,
|
|
||||||
addr_t phys)
|
|
||||||
{
|
|
||||||
_cmd_list->w = 1;
|
|
||||||
|
|
||||||
enum { WRITE_DMA_EXT = 0x35 };
|
|
||||||
_cmd_table->setup_command(WRITE_DMA_EXT, block_number,
|
|
||||||
block_count, phys);
|
|
||||||
_execute_command();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Ram_dataspace_capability alloc_dma_buffer(size_t size) = 0;
|
|
||||||
virtual void free_dma_buffer(Ram_dataspace_capability cap) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _AHCI_DEVICE_BASE_H_ */
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
|||||||
|
|
||||||
/*
|
|
||||||
* \brief Generic base of AHCI driver
|
|
||||||
* \author Sebastian Sumpf <Sebastian.Sumpf@genode-labs.com>
|
|
||||||
* \author Martin Stein <Martin.Stein@genode-labs.com>
|
|
||||||
* \date 2011-08-10
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2011-2013 Genode Labs GmbH
|
|
||||||
*
|
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
|
||||||
* under the terms of the GNU General Public License version 2.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _AHCI_DRIVER_BASE_H_
|
|
||||||
#define _AHCI_DRIVER_BASE_H_
|
|
||||||
|
|
||||||
/* local includes */
|
|
||||||
#include <ahci_device.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation of block driver interface
|
|
||||||
*/
|
|
||||||
class Ahci_driver_base : public Block::Driver
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
|
|
||||||
Ahci_device *_device;
|
|
||||||
|
|
||||||
void _sanity_check(size_t block_number, size_t count)
|
|
||||||
{
|
|
||||||
if (!_device || (block_number + count > block_count()))
|
|
||||||
throw Io_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ahci_driver_base(Ahci_device * const device)
|
|
||||||
: _device(device) { }
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
~Ahci_driver_base()
|
|
||||||
{
|
|
||||||
if (_device)
|
|
||||||
destroy(env()->heap(), _device);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t block_size() { return Ahci_device::block_size(); }
|
|
||||||
Block::sector_t block_count() {
|
|
||||||
return _device ? _device->block_count() : 0; }
|
|
||||||
|
|
||||||
Block::Session::Operations ops()
|
|
||||||
{
|
|
||||||
Block::Session::Operations o;
|
|
||||||
o.set_operation(Block::Packet_descriptor::READ);
|
|
||||||
o.set_operation(Block::Packet_descriptor::WRITE);
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dma_enabled() { return true; }
|
|
||||||
|
|
||||||
void read_dma(Block::sector_t block_number,
|
|
||||||
size_t block_count,
|
|
||||||
addr_t phys,
|
|
||||||
Block::Packet_descriptor &packet)
|
|
||||||
{
|
|
||||||
_sanity_check(block_number, block_count);
|
|
||||||
_device->read(block_number, block_count, phys);
|
|
||||||
ack_packet(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_dma(Block::sector_t block_number,
|
|
||||||
size_t block_count,
|
|
||||||
addr_t phys,
|
|
||||||
Block::Packet_descriptor &packet)
|
|
||||||
{
|
|
||||||
_sanity_check(block_number, block_count);
|
|
||||||
_device->write(block_number, block_count, phys);
|
|
||||||
ack_packet(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ram_dataspace_capability alloc_dma_buffer(size_t size) {
|
|
||||||
return _device->alloc_dma_buffer(size); }
|
|
||||||
|
|
||||||
void free_dma_buffer(Ram_dataspace_capability cap) {
|
|
||||||
return _device->free_dma_buffer(cap); }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _AHCI_DRIVER_BASE_H_ */
|
|
||||||
|
|
@ -1,63 +1,158 @@
|
|||||||
/*
|
/**
|
||||||
* \brief Minimal AHCI-ATA driver
|
* \brief Block driver session creation
|
||||||
* \author Sebastian Sumpf <Sebastian.Sumpf@genode-labs.com>
|
* \author Sebastian Sumpf
|
||||||
* \date 2011-08-10
|
* \date 2015-09-29
|
||||||
*
|
|
||||||
* This driver currently supports only one command slot, one FIS, and one PRD
|
|
||||||
* per FIS, thus limiting the request size to 4MB per request. Since the packet
|
|
||||||
* interface currently only supports a synchronous mode of operation the above
|
|
||||||
* limitations seems reasonable.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2011-2013 Genode Labs GmbH
|
* Copyright (C) 2015 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU General Public License version 2.
|
* under the terms of the GNU General Public License version 2.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <block/component.h>
|
#include <block/component.h>
|
||||||
|
#include <os/config.h>
|
||||||
#include <os/server.h>
|
#include <os/server.h>
|
||||||
|
#include <util/xml_node.h>
|
||||||
|
|
||||||
/* local includes */
|
#include "ahci.h"
|
||||||
#include <ahci_driver.h>
|
|
||||||
|
|
||||||
using namespace Genode;
|
namespace Block {
|
||||||
|
class Factory;
|
||||||
|
class Root_multiple_clients;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct Block::Factory : Driver_factory
|
||||||
|
{
|
||||||
|
long device_num;
|
||||||
|
|
||||||
|
Block::Driver *create()
|
||||||
|
{
|
||||||
|
return Ahci_driver::claim_port(device_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy(Block::Driver *driver)
|
||||||
|
{
|
||||||
|
Ahci_driver::free_port(device_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
Factory(long device_num) : device_num(device_num) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Session_component : public Block::Session_component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
Session_component(Block::Driver_factory &driver_factory,
|
||||||
|
Server::Entrypoint &ep, Genode::size_t buf_size)
|
||||||
|
: Block::Session_component(driver_factory, ep, buf_size) { }
|
||||||
|
|
||||||
|
Block::Driver_factory &factory() { return _driver_factory; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Block::Root_multiple_clients : public Root_component< ::Session_component>,
|
||||||
|
public Ahci_root
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Server::Entrypoint &_ep;
|
||||||
|
|
||||||
|
long _device_num(const char *session_label)
|
||||||
|
{
|
||||||
|
long num = -1;
|
||||||
|
|
||||||
|
try {
|
||||||
|
using namespace Genode;
|
||||||
|
|
||||||
|
Xml_node policy = Genode::config()->xml_node().sub_node("policy");
|
||||||
|
|
||||||
|
for (;; policy = policy.next("policy")) {
|
||||||
|
char label_buf[64];
|
||||||
|
policy.attribute("label").value(label_buf, sizeof(label_buf));
|
||||||
|
|
||||||
|
if (Genode::strcmp(session_label, label_buf))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* read device attribute */
|
||||||
|
policy.attribute("device").value(&num);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (...) {}
|
||||||
|
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
::Session_component *_create_session(const char *args)
|
||||||
|
{
|
||||||
|
size_t tx_buf_size =
|
||||||
|
Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
|
||||||
|
|
||||||
|
/* TODO: build quota check */
|
||||||
|
|
||||||
|
/* Search for configured device */
|
||||||
|
char label_buf[64];
|
||||||
|
Genode::Arg_string::find_arg(args,
|
||||||
|
"label").string(label_buf,
|
||||||
|
sizeof(label_buf),
|
||||||
|
"<unlabeled>");
|
||||||
|
long num = _device_num(label_buf);
|
||||||
|
if (num < 0) {
|
||||||
|
PERR("No confguration found for client: %s", label_buf);
|
||||||
|
throw Root::Invalid_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Ahci_driver::is_avail(num)) {
|
||||||
|
PERR("Device %ld not avaiable", num);
|
||||||
|
throw Root::Unavailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
Factory *factory = new (Genode::env()->heap()) Factory(num);
|
||||||
|
return new (md_alloc()) ::Session_component(*factory,
|
||||||
|
_ep, tx_buf_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _destroy_session(::Session_component *session)
|
||||||
|
{
|
||||||
|
Driver_factory &factory = session->factory();
|
||||||
|
destroy(env()->heap(), session);
|
||||||
|
destroy(env()->heap(), &factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Root_multiple_clients(Server::Entrypoint &ep, Allocator *md_alloc)
|
||||||
|
: Root_component(&ep.rpc_ep(), md_alloc), _ep(ep) { }
|
||||||
|
|
||||||
|
Server::Entrypoint &entrypoint() override { return _ep; }
|
||||||
|
|
||||||
|
void announce() override
|
||||||
|
{
|
||||||
|
Genode::env()->parent()->announce(_ep.manage(*this));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct Main
|
struct Main
|
||||||
{
|
{
|
||||||
Server::Entrypoint &ep;
|
Block::Root_multiple_clients root;
|
||||||
|
|
||||||
struct Factory : Block::Driver_factory
|
|
||||||
{
|
|
||||||
Block::Driver *create() {
|
|
||||||
static Ahci_driver driver;
|
|
||||||
return &driver; }
|
|
||||||
|
|
||||||
void destroy(Block::Driver *driver) { }
|
|
||||||
} factory;
|
|
||||||
|
|
||||||
Block::Root root;
|
|
||||||
|
|
||||||
Main(Server::Entrypoint &ep)
|
Main(Server::Entrypoint &ep)
|
||||||
: ep(ep), root(ep, Genode::env()->heap(), factory)
|
: root(ep, Genode::env()->heap())
|
||||||
{
|
{
|
||||||
printf("--- AHCI driver started ---\n");
|
PINF("--- Starting AHCI driver -> done right .-) --\n");
|
||||||
|
Ahci_driver::init(root);
|
||||||
Genode::env()->parent()->announce(ep.manage(root));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/************
|
|
||||||
** Server **
|
|
||||||
************/
|
|
||||||
|
|
||||||
namespace Server {
|
namespace Server {
|
||||||
char const *name() { return "ahci_ep"; }
|
char const *name() { return "ahci_ep"; }
|
||||||
size_t stack_size() { return 2*1024*sizeof(long); }
|
size_t stack_size() { return 2 * 1024 * sizeof(long); }
|
||||||
void construct(Entrypoint &ep) { static Main server(ep); }
|
void construct(Entrypoint &ep) { static Main server(ep); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
TARGET = ahci
|
TARGET = ahci_drv
|
||||||
LIBS += ahci server
|
SRC_CC = main.cc ahci.cc
|
||||||
SRC_CC += empty.cc
|
LIBS += server base config ahci_platform
|
||||||
|
@ -1,209 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief AHCI device
|
|
||||||
* \author Sebastian Sumpf <Sebastian.Sumpf@genode-labs.com>
|
|
||||||
* \author Martin Stein <Martin.Stein@genode-labs.com>
|
|
||||||
* \date 2011-08-10
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2011-2013 Genode Labs GmbH
|
|
||||||
*
|
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
|
||||||
* under the terms of the GNU General Public License version 2.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _AHCI_DEVICE_H_
|
|
||||||
#define _AHCI_DEVICE_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <pci_session/connection.h>
|
|
||||||
#include <pci_device/client.h>
|
|
||||||
|
|
||||||
/* local includes */
|
|
||||||
#include <ahci_device_base.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AHCI device
|
|
||||||
*/
|
|
||||||
class Ahci_device : public Ahci_device_base
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
enum Pci_config {
|
|
||||||
PCI_CFG_BMIBA_OFF = 0x24, /* offset in PCI config space */
|
|
||||||
CLASS_MASS_STORAGE = 0x10000U,
|
|
||||||
SUBCLASS_AHCI = 0x0600U,
|
|
||||||
CLASS_MASK = 0xffff00U,
|
|
||||||
AHCI_BASE_ID = 0x5, /* resource id of AHCI base addr <BAR 5> */
|
|
||||||
AHCI_INTR_OFF = 0x3c, /* offset of interrupt information in config space */
|
|
||||||
PCI_CFG_CMD_REG = 0x4,
|
|
||||||
};
|
|
||||||
|
|
||||||
::Pci::Connection &_pci;
|
|
||||||
::Pci::Device_client *_pci_device;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return next PCI device
|
|
||||||
*/
|
|
||||||
static Pci::Device_capability _scan_pci(Pci::Connection &pci, Pci::Device_capability prev_device_cap)
|
|
||||||
{
|
|
||||||
Pci::Device_capability device_cap;
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < 2; i++) {
|
|
||||||
try {
|
|
||||||
device_cap = pci.next_device(prev_device_cap,
|
|
||||||
CLASS_MASS_STORAGE | SUBCLASS_AHCI,
|
|
||||||
CLASS_MASK);
|
|
||||||
break;
|
|
||||||
} catch (Pci::Device::Quota_exceeded) {
|
|
||||||
Genode::env()->parent()->upgrade(pci.cap(), "ram_quota=4096");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prev_device_cap.valid())
|
|
||||||
pci.release_device(prev_device_cap);
|
|
||||||
|
|
||||||
return device_cap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize port
|
|
||||||
*/
|
|
||||||
void _init(::Pci::Device_client *pci_device)
|
|
||||||
{
|
|
||||||
uint32_t version = _ctrl->version();
|
|
||||||
PINF("AHCI Version: %x.%04x", (version >> 16), version & 0xffff);
|
|
||||||
|
|
||||||
/* HBA capabilities at offset 0 */
|
|
||||||
uint32_t caps = _ctrl->hba_cap();
|
|
||||||
PINF("CAPs:");
|
|
||||||
PINF("\tPort count: %u", _ctrl->port_count());
|
|
||||||
PINF("\tCommand slots: %u", _ctrl->cmd_slots());
|
|
||||||
PINF("\tAHCI only: %s", (caps & 0x20000) ? "yes" : "no");
|
|
||||||
PINF("\tNative command queuing: %s", (caps & 0x40000000) ? "yes" : "no");
|
|
||||||
PINF("\t64 Bit: %s", (caps & 0x80000000) ? "yes" : "no");
|
|
||||||
|
|
||||||
/* setup up AHCI data structures */
|
|
||||||
_setup_memory();
|
|
||||||
|
|
||||||
/* check and possibly enable AHCI mode */
|
|
||||||
_ctrl->global_enable_ahci();
|
|
||||||
|
|
||||||
/* enable global interrupts */
|
|
||||||
_ctrl->global_interrupt_enable();
|
|
||||||
|
|
||||||
/* disable power mgmt. */
|
|
||||||
_port->disable_pm();
|
|
||||||
|
|
||||||
/* startup device */
|
|
||||||
_port->get_ready();
|
|
||||||
|
|
||||||
/* reset port */
|
|
||||||
_port->reset();
|
|
||||||
|
|
||||||
/* clear error register */
|
|
||||||
_port->err(_port->err());
|
|
||||||
|
|
||||||
/* port interrupt enable */
|
|
||||||
_port->interrupt_enable();
|
|
||||||
|
|
||||||
/* ack all possibly pending interrupts */
|
|
||||||
_port->interrupt_ack();
|
|
||||||
_ctrl->hba_interrupt_ack();
|
|
||||||
|
|
||||||
/* retrieve block count */
|
|
||||||
_identify_device();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Ahci_device(addr_t base, Io_mem_session_capability io_cap,
|
|
||||||
Pci::Connection &pci)
|
|
||||||
: Ahci_device_base(base, io_cap), _pci(pci), _pci_device(0) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Probe PCI-bus for AHCI and ATA-devices
|
|
||||||
*/
|
|
||||||
static Ahci_device *probe(Pci::Connection &pci)
|
|
||||||
{
|
|
||||||
Pci::Device_capability device_cap;
|
|
||||||
Ahci_device *device;
|
|
||||||
|
|
||||||
while ((device_cap = _scan_pci(pci, device_cap)).valid()) {
|
|
||||||
|
|
||||||
::Pci::Device_client * pci_device =
|
|
||||||
new(env()->heap()) ::Pci::Device_client(device_cap);
|
|
||||||
|
|
||||||
PINF("Found AHCI HBA (Vendor ID: %04x Device ID: %04x Class:"
|
|
||||||
" %08x)\n", pci_device->vendor_id(),
|
|
||||||
pci_device->device_id(), pci_device->class_code());
|
|
||||||
|
|
||||||
/* enable Bus Master, Memory Space, I/O Space access by dev */
|
|
||||||
pci_device->config_write(PCI_CFG_CMD_REG, 0x7,
|
|
||||||
::Pci::Device::ACCESS_16BIT);
|
|
||||||
|
|
||||||
/* read and map base address of AHCI controller */
|
|
||||||
Pci::Device::Resource resource = pci_device->resource(AHCI_BASE_ID);
|
|
||||||
|
|
||||||
Io_mem_connection io(resource.base(), resource.size());
|
|
||||||
addr_t addr = (addr_t)env()->rm_session()->attach(io.dataspace());
|
|
||||||
|
|
||||||
/* add possible resource offset */
|
|
||||||
addr += resource.base() & 0xfff;
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
PDBG("resource base: %x virt: %lx", resource.base(), addr);
|
|
||||||
|
|
||||||
/* create and test device */
|
|
||||||
device = new(env()->heap()) Ahci_device(addr, io.cap(), pci);
|
|
||||||
if (device->_scan_ports()) {
|
|
||||||
|
|
||||||
io.on_destruction(Io_mem_connection::KEEP_OPEN);
|
|
||||||
|
|
||||||
/* read IRQ information */
|
|
||||||
unsigned long intr = pci_device->config_read(AHCI_INTR_OFF,
|
|
||||||
::Pci::Device::ACCESS_32BIT);
|
|
||||||
|
|
||||||
if (verbose) {
|
|
||||||
PDBG("Interrupt pin: %lu line: %lu", (intr >> 8) & 0xff, intr & 0xff);
|
|
||||||
|
|
||||||
unsigned char bus, dev, func;
|
|
||||||
pci_device->bus_address(&bus, &dev, &func);
|
|
||||||
PDBG("Bus address: %x:%02x.%u (0x%x)", bus, dev, func, (bus << 8) | ((dev & 0x1f) << 3) | (func & 0x7));
|
|
||||||
}
|
|
||||||
|
|
||||||
device->_irq = new(env()->heap()) Irq_session_client(pci_device->irq(0));
|
|
||||||
Genode::Signal_context_capability cap = device->_irq_rec.manage(&device->_irq_ctx);
|
|
||||||
device->_irq->sigh(cap);
|
|
||||||
device->_irq->ack_irq();
|
|
||||||
|
|
||||||
device->_pci_device = pci_device;
|
|
||||||
/* get device ready */
|
|
||||||
device->_init(pci_device);
|
|
||||||
|
|
||||||
return device;
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy(env()->heap(), pci_device);
|
|
||||||
env()->rm_session()->detach(addr);
|
|
||||||
destroy(env()->heap(), device);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ram_dataspace_capability alloc_dma_buffer(size_t size)
|
|
||||||
{
|
|
||||||
/* transfer quota to pci driver, otherwise we get a exception */
|
|
||||||
char quota[32];
|
|
||||||
Genode::snprintf(quota, sizeof(quota), "ram_quota=%zd", size);
|
|
||||||
Genode::env()->parent()->upgrade(_pci.cap(), quota);
|
|
||||||
|
|
||||||
return _pci.alloc_dma_buffer(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_dma_buffer(Ram_dataspace_capability cap) {
|
|
||||||
return _pci.free_dma_buffer(cap); }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _AHCI_DEVICE_H_ */
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief AHCI driver
|
|
||||||
* \author Sebastian Sumpf <Sebastian.Sumpf@genode-labs.com>
|
|
||||||
* \author Martin Stein <Martin.Stein@genode-labs.com>
|
|
||||||
* \date 2006-08-15
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2006-2013 Genode Labs GmbH
|
|
||||||
*
|
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
|
||||||
* under the terms of the GNU General Public License version 2.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _AHCI_DRIVER_H_
|
|
||||||
#define _AHCI_DRIVER_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <pci_session/connection.h>
|
|
||||||
|
|
||||||
/* local includes */
|
|
||||||
#include <ahci_driver_base.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper class for AHCI driver to ensure specific member-initialization order
|
|
||||||
*/
|
|
||||||
class Ahci_pci_connection {
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
Pci::Connection _pci;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AHCI driver
|
|
||||||
*/
|
|
||||||
class Ahci_driver : public Ahci_pci_connection,
|
|
||||||
public Ahci_driver_base
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
Ahci_driver() : Ahci_driver_base(nullptr) {
|
|
||||||
Ahci_device * device = Ahci_device::probe(_pci);
|
|
||||||
|
|
||||||
if (!device) {
|
|
||||||
PWRN("Could not find ahci device");
|
|
||||||
throw Root::Unavailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
_device = device;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _AHCI_DRIVER_H_ */
|
|
||||||
|
|
137
repos/os/src/drivers/ahci/x86/platform.cc
Normal file
137
repos/os/src/drivers/ahci/x86/platform.cc
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
/**
|
||||||
|
* \brief Driver for PCI-bus platforms
|
||||||
|
* \author Sebastian Sumpf
|
||||||
|
* \date 2015-04-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <irq_session/connection.h>
|
||||||
|
#include <pci_session/connection.h>
|
||||||
|
#include <pci_device/client.h>
|
||||||
|
#include <util/volatile_object.h>
|
||||||
|
|
||||||
|
#include <ahci.h>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace Genode;
|
||||||
|
|
||||||
|
struct X86_hba : Platform::Hba
|
||||||
|
{
|
||||||
|
enum Pci_config {
|
||||||
|
CLASS_MASS_STORAGE = 0x10000u,
|
||||||
|
SUBCLASS_AHCI = 0x600u,
|
||||||
|
CLASS_MASK = 0xffff00u,
|
||||||
|
AHCI_DEVICE = CLASS_MASS_STORAGE | SUBCLASS_AHCI,
|
||||||
|
AHCI_BASE_ID = 0x5, /* resource id of ahci base addr <bar 5> */
|
||||||
|
PCI_CMD = 0x4,
|
||||||
|
};
|
||||||
|
|
||||||
|
Pci::Connection pci;
|
||||||
|
Pci::Device_capability pci_device_cap;
|
||||||
|
Lazy_volatile_object<Pci::Device_client> pci_device;
|
||||||
|
Lazy_volatile_object<Irq_session_client> irq;
|
||||||
|
addr_t res_base;
|
||||||
|
size_t res_size;
|
||||||
|
|
||||||
|
X86_hba()
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < 2; i++)
|
||||||
|
try {
|
||||||
|
if (!(pci_device_cap =
|
||||||
|
pci.next_device(pci_device_cap, AHCI_DEVICE, CLASS_MASK)).valid()) {
|
||||||
|
PERR("No AHCI controller found");
|
||||||
|
throw -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} catch (Pci::Device::Quota_exceeded) {
|
||||||
|
Genode::env()->parent()->upgrade(pci.cap(), "ram_quota=4096");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* construct pci client */
|
||||||
|
pci_device.construct(pci_device_cap);
|
||||||
|
PINF("AHCI found (vendor: %04x device: %04x class:"
|
||||||
|
" %08x)\n", pci_device->vendor_id(),
|
||||||
|
pci_device->device_id(), pci_device->class_code());
|
||||||
|
|
||||||
|
/* read base address of controller */
|
||||||
|
Pci::Device::Resource resource = pci_device->resource(AHCI_BASE_ID);
|
||||||
|
res_base = resource.base();
|
||||||
|
res_size = resource.size();
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
PDBG("base: %lx size: %zx", res_base, res_size);
|
||||||
|
|
||||||
|
/* enable bus master */
|
||||||
|
uint16_t cmd = pci_device->config_read(PCI_CMD, Pci::Device::ACCESS_16BIT);
|
||||||
|
cmd |= 0x4;
|
||||||
|
pci_device->config_write(PCI_CMD, cmd, Pci::Device::ACCESS_16BIT);
|
||||||
|
|
||||||
|
irq.construct(pci_device->irq(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable_msi()
|
||||||
|
{
|
||||||
|
enum { PM_CAP_OFF = 0x34, MSI_CAP = 0x5, MSI_ENABLED = 0x1 };
|
||||||
|
uint8_t cap = pci_device->config_read(PM_CAP_OFF, ::Pci::Device::ACCESS_8BIT);
|
||||||
|
|
||||||
|
/* iterate through cap pointers */
|
||||||
|
for (uint16_t val = 0; cap; cap = val >> 8) {
|
||||||
|
val = pci_device->config_read(cap, ::Pci::Device::ACCESS_16BIT);
|
||||||
|
|
||||||
|
if ((val & 0xff) != MSI_CAP)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
uint16_t msi = pci_device->config_read(cap + 2, ::Pci::Device::ACCESS_16BIT);
|
||||||
|
|
||||||
|
if (msi & MSI_ENABLED) {
|
||||||
|
pci_device->config_write(cap + 2, msi ^ MSI_CAP, ::Pci::Device::ACCESS_8BIT);
|
||||||
|
PINF("Disabled MSIs %x", msi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************
|
||||||
|
** Hba interface **
|
||||||
|
*******************/
|
||||||
|
|
||||||
|
Genode::addr_t base() const override { return res_base; }
|
||||||
|
Genode::size_t size() const override { return res_size; }
|
||||||
|
|
||||||
|
void sigh_irq(Signal_context_capability sigh) override
|
||||||
|
{
|
||||||
|
irq->sigh(sigh);
|
||||||
|
ack_irq();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ack_irq() override { irq->ack_irq(); }
|
||||||
|
|
||||||
|
Ram_dataspace_capability
|
||||||
|
alloc_dma_buffer(Genode::size_t size) override
|
||||||
|
{
|
||||||
|
/* transfer quota to pci driver, otherwise we get a exception */
|
||||||
|
char quota[32];
|
||||||
|
snprintf(quota, sizeof(quota), "ram_quota=%zd", size);
|
||||||
|
env()->parent()->upgrade(pci.cap(), quota);
|
||||||
|
|
||||||
|
return pci.alloc_dma_buffer(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_dma_buffer(Genode::Ram_dataspace_capability ds)
|
||||||
|
{
|
||||||
|
pci.free_dma_buffer(ds);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Platform::Hba &Platform::init(Mmio::Delayer &)
|
||||||
|
{
|
||||||
|
static X86_hba h;
|
||||||
|
return h;
|
||||||
|
}
|
@ -58,7 +58,7 @@ set config {
|
|||||||
</provides>
|
</provides>
|
||||||
<config><policy label="l4linux" uart="0" detect_size="yes"/></config>
|
<config><policy label="l4linux" uart="0" detect_size="yes"/></config>
|
||||||
</start>
|
</start>
|
||||||
<start name="ahci">
|
<start name="ahci_drv">
|
||||||
<resource name="RAM" quantum="1M"/>
|
<resource name="RAM" quantum="1M"/>
|
||||||
<provides><service name="Block"/></provides>
|
<provides><service name="Block"/></provides>
|
||||||
</start>
|
</start>
|
||||||
@ -109,7 +109,7 @@ set boot_modules {
|
|||||||
kdb_uart_drv
|
kdb_uart_drv
|
||||||
l4linux
|
l4linux
|
||||||
initrd.gz
|
initrd.gz
|
||||||
ahci
|
ahci_drv
|
||||||
platform_drv }
|
platform_drv }
|
||||||
|
|
||||||
lappend_if $interpose_part_blk boot_modules part_blk
|
lappend_if $interpose_part_blk boot_modules part_blk
|
||||||
|
@ -168,8 +168,12 @@ append_if [expr !$use_usb] config {
|
|||||||
|
|
||||||
append_if $use_block_sata config {
|
append_if $use_block_sata config {
|
||||||
<start name="ahci" priority="-1">
|
<start name="ahci" priority="-1">
|
||||||
|
<binary name="ahci_drv" />
|
||||||
<resource name="RAM" quantum="1M" />
|
<resource name="RAM" quantum="1M" />
|
||||||
<provides><service name="Block"/></provides>
|
<provides><service name="Block"/></provides>
|
||||||
|
<config>
|
||||||
|
<policy label="seoul" device="0" />
|
||||||
|
</config>
|
||||||
</start>}
|
</start>}
|
||||||
|
|
||||||
append_if $use_genode_iso config {
|
append_if $use_genode_iso config {
|
||||||
@ -422,7 +426,7 @@ lappend_if [have_spec acpi] boot_modules acpi_drv
|
|||||||
lappend_if [have_spec nova] boot_modules pci_device_pd
|
lappend_if [have_spec nova] boot_modules pci_device_pd
|
||||||
lappend_if [expr !$use_usb] boot_modules ps2_drv
|
lappend_if [expr !$use_usb] boot_modules ps2_drv
|
||||||
lappend_if $use_usb boot_modules usb_drv
|
lappend_if $use_usb boot_modules usb_drv
|
||||||
lappend_if $use_block_sata boot_modules ahci
|
lappend_if $use_block_sata boot_modules ahci_drv
|
||||||
lappend_if $use_nic_session boot_modules nic_drv
|
lappend_if $use_nic_session boot_modules nic_drv
|
||||||
lappend_if $use_nic_bridge boot_modules nic_bridge
|
lappend_if $use_nic_bridge boot_modules nic_bridge
|
||||||
lappend_if $use_framebuffer boot_modules fb_drv
|
lappend_if $use_framebuffer boot_modules fb_drv
|
||||||
|
Loading…
x
Reference in New Issue
Block a user