diff --git a/repos/libports/run/libc_ffat.run b/repos/libports/run/libc_ffat.run
index 2cf795f00d..ec69eeef32 100644
--- a/repos/libports/run/libc_ffat.run
+++ b/repos/libports/run/libc_ffat.run
@@ -1,4 +1,4 @@
-set mkfs_cmd mkfs.vfat
+set mkfs_cmd [check_installed mkfs.vfat]
set mkfs_opts "-F32"
set filesystem ffat
diff --git a/repos/libports/run/libc_ffat_fs.run b/repos/libports/run/libc_ffat_fs.run
index ad05cd9eb0..c6f24eecea 100644
--- a/repos/libports/run/libc_ffat_fs.run
+++ b/repos/libports/run/libc_ffat_fs.run
@@ -6,9 +6,7 @@
set use_sd_card_drv [expr [have_spec omap4] || [have_spec platform_arndale] || [have_spec pl180]]
set use_ahci_drv [have_spec x86]
-
-if {[catch { exec which mkfs.vfat } ]} {
- puts stderr "Error: mkfs.vfat not installed, aborting test"; exit }
+set mkfs [check_installed mkfs.vfat]
if {[expr [have_spec linux] || [have_spec hw_odroid_xu]]} {
puts "Run script does not support this platform"; exit }
@@ -85,22 +83,30 @@ append_if [have_spec acpi] config {
-
+
+
+
+
}
append_if [expr ![have_spec acpi] && [have_spec pci]] config {
+
+
+
}
append_if $use_ahci_drv config {
-
+
-
+
+
+
}
@@ -130,7 +136,7 @@ set boot_modules {
lappend_if [have_spec pci] boot_modules pci_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
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"
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"
catch { exec sh -c $cmd }
diff --git a/repos/libports/run/libc_filesystem_test.inc b/repos/libports/run/libc_filesystem_test.inc
index 1804b43df6..7ac7931714 100644
--- a/repos/libports/run/libc_filesystem_test.inc
+++ b/repos/libports/run/libc_filesystem_test.inc
@@ -126,16 +126,17 @@ append_if [expr ![have_spec acpi] && [have_spec pci]] config {
append_if $use_ahci config {
-
+
- }
-
-append_if [expr $use_ahci && [have_spec acpi]] config {
- }
-append_if $use_ahci config {
+
+ }
+append_if $use_ahci config "
+ "
+append_if $use_ahci 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 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
build_boot_image $boot_modules
diff --git a/repos/libports/run/qt5_drivers.inc b/repos/libports/run/qt5_drivers.inc
index 6acce9033e..d8bbd81f62 100644
--- a/repos/libports/run/qt5_drivers.inc
+++ b/repos/libports/run/qt5_drivers.inc
@@ -129,6 +129,25 @@ proc drivers_start_nodes { feature_arg } {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -179,6 +198,17 @@ proc drivers_start_nodes { feature_arg } {
+
+
+
+
+
+
+
+
+
+
+
}
diff --git a/repos/libports/run/qt5_textedit.run b/repos/libports/run/qt5_textedit.run
index f3249918c1..ca55f7f966 100644
--- a/repos/libports/run/qt5_textedit.run
+++ b/repos/libports/run/qt5_textedit.run
@@ -51,9 +51,12 @@ append config [qt5_start_nodes feature]
append_if $use_ahci_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 [expr ![have_spec linux]] boot_modules ffat_fs
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
diff --git a/repos/os/lib/mk/ahci.inc b/repos/os/lib/mk/ahci.inc
deleted file mode 100644
index a3ceab080c..0000000000
--- a/repos/os/lib/mk/ahci.inc
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# \brief Generic toolchain configurations for AHCI
-# \author Martin Stein
-# \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
diff --git a/repos/os/lib/mk/exynos5/ahci.mk b/repos/os/lib/mk/exynos5/ahci.mk
deleted file mode 100644
index dd45bd76e0..0000000000
--- a/repos/os/lib/mk/exynos5/ahci.mk
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# \brief Toolchain configurations for AHCI on Exynos
-# \author Martin Stein
-# \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
diff --git a/repos/os/lib/mk/exynos5/ahci_platform.mk b/repos/os/lib/mk/exynos5/ahci_platform.mk
new file mode 100644
index 0000000000..fa416556ac
--- /dev/null
+++ b/repos/os/lib/mk/exynos5/ahci_platform.mk
@@ -0,0 +1,6 @@
+SRC_CC = platform.cc
+
+INC_DIR += $(REP_DIR)/src/drivers/ahci
+
+vpath platform.cc $(REP_DIR)/src/drivers/ahci/exynos5
+
diff --git a/repos/os/lib/mk/x86/ahci.mk b/repos/os/lib/mk/x86/ahci.mk
deleted file mode 100644
index 85524346af..0000000000
--- a/repos/os/lib/mk/x86/ahci.mk
+++ /dev/null
@@ -1,11 +0,0 @@
-#
-# \brief Toolchain configurations for AHCI on X86
-# \author Martin Stein
-# \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
diff --git a/repos/os/lib/mk/x86/ahci_platform.mk b/repos/os/lib/mk/x86/ahci_platform.mk
new file mode 100644
index 0000000000..e92501687e
--- /dev/null
+++ b/repos/os/lib/mk/x86/ahci_platform.mk
@@ -0,0 +1,6 @@
+SRC_CC = platform.cc
+
+INC_DIR += $(REP_DIR)/src/drivers/ahci
+
+vpath platform.cc $(REP_DIR)/src/drivers/ahci/x86
+
diff --git a/repos/os/src/drivers/ahci/README b/repos/os/src/drivers/ahci/README
index 7934602600..3ab8e528fc 100644
--- a/repos/os/src/drivers/ahci/README
+++ b/repos/os/src/drivers/ahci/README
@@ -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
--------
-The server implements Genode's new block-driver API ('os/include/block'), thus
-exposing the block-session interface as front-end. AHCI depends on Genode's PCI
-driver as well as the timer server. For a usage example see: 'os/run/ahci.run'.
+The driver supports x86 32/64 bit platforms and the Exynos5 SOC. If
+more than one AHCI controller is present, the first one will be used.
+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
-AHCI controller, scans the controller ports and uses the first non-ATAPI port
-where a device is present.
+!
+!
+!
+! }
+!
+!
+!
+!
+!
+!
+!
+!
-On real hardware and on kernels taking advantage of I/O APICs (namely NOVA and
-Fiasco.OC) we still lack support for ACPI parsing and thus for interrupts,
-leading to a non-working driver.
+In the example above, a session request labeled with "test-ahci"
+gains access to device 0, while "bench" gains access to device 1.
diff --git a/repos/os/src/drivers/ahci/ahci.cc b/repos/os/src/drivers/ahci/ahci.cc
new file mode 100644
index 0000000000..b6262bb36a
--- /dev/null
+++ b/repos/os/src/drivers/ahci/ahci.cc
@@ -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
+#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 irq;
+ Signal_rpc_member 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();
+ 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.read());
+ 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());
+ unsigned avaiable = hba.read();
+ 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();
+
+ 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);
+}
+
+
+
diff --git a/repos/os/src/drivers/ahci/ahci.h b/repos/os/src/drivers/ahci/ahci.h
new file mode 100644
index 0000000000..357d2519a1
--- /dev/null
+++ b/repos/os/src/drivers/ahci/ahci.h
@@ -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
+#include
+#include
+
+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() + 1; }
+ unsigned command_slots() { return read() + 1; }
+ bool ncg() { return !!read(); }
+ bool supports_64bit(){ return !!read(); }
+
+ /**
+ * 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(read()); }
+
+ /**
+ * 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(1);
+
+ /* enable interrupts */
+ write(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 { };
+ 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 { };
+ struct Lba48 : Genode::Bitset_3 { };
+ struct Lba : Genode::Bitset_2 { }; /* 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 { }; /* sector count */
+
+ Command_fis(Genode::addr_t base)
+ : Mmio(base)
+ {
+ clear();
+
+ enum { HOST_TO_DEVICE = 0x27 };
+ write(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(1);
+ write(0);
+ write(0xec);
+ }
+
+ void dma_ext(bool read, Block::sector_t block_number, Genode::size_t block_count)
+ {
+ write(1);
+ write(1);
+ /* read_dma_ext : write_dma_ext */
+ write(read ? 0x25 : 0x35);
+ write(block_number);
+ write(block_count);
+ }
+
+ void fpdma(bool read, Block::sector_t block_number, Genode::size_t block_count,
+ unsigned slot)
+ {
+ write(1);
+ write(1);
+ /* read_fpdma : write_fpdma */
+ write(read ? 0x60 : 0x61);
+ write(block_number);
+ write(block_count);
+ write(slot);
+ }
+
+ void atapi()
+ {
+ write(1);
+ /* packet command */
+ write(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(addr);
+ write(addr >> 32);
+ write(1);
+ write(Command_fis::size() / sizeof(unsigned));
+ }
+
+ void clear_byte_count()
+ {
+ write(0);
+ }
+
+ void atapi_command()
+ {
+ write(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 { };
+ struct Lba : Genode::Bitset_2 { };
+
+ /* sector count big endian */
+ struct Sector8_15 : Register<0x8, 8> { };
+ struct Sector0_7 : Register<0x9, 8> { };
+ struct Sector : Genode::Bitset_2 { };
+
+
+ Atapi_command(Genode::addr_t base) : Mmio(base)
+ {
+ Genode::memset((void *)base, 0, 16);
+ }
+
+ void read_capacity()
+ {
+ write(0x25);
+ }
+
+ void test_unit_ready()
+ {
+ write(0x0);
+ }
+
+ void read_sense()
+ {
+ write(0x3);
+ write(18);
+ }
+
+ void read10(Block::sector_t block_number, Genode::size_t block_count)
+ {
+ write(0x28);
+ write(block_number);
+ write(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(addr);
+ write(addr >> 32);
+ write(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() + 1,
+ read());
+ PLOG("\t\tnumer of sectors: %llu", read());
+ PLOG("\t\tmultiple logical blocks per physical: %s",
+ read() ? "yes" : "no");
+ PLOG("\t\tlogical blocks per physical: %u",
+ 1U << read());
+ PLOG("\t\tlogical block size is above 512 byte: %s",
+ read() ? "yes" : "no");
+ PLOG("\t\twords (16bit) per logical block: %u",
+ read());
+ PLOG("\t\toffset of first logical block within physical: %u",
+ read());
+ }
+};
+
+
+/**
+ * AHCI port
+ */
+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(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(addr);
+ write(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(addr);
+ write(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 ();
+
+ /* clear Serr.Diag.x */
+ if (Is::Pcs::get(status))
+ write(0);
+
+ /* clear Serr.Diag.n */
+ if (Is::Prcs::get(status))
+ write(0);
+
+ write(read());
+ };
+
+ /**
+ * 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(~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())
+ return;
+
+ if (!wait_for(0, hba.delayer(), 500, 1000)) {
+ PERR("HBA busy unable to start command processing.");
+ return;
+ }
+
+ if (!wait_for(0, hba.delayer(), 500, 1000)) {
+ PERR("HBA in DRQ unable to start command processing.");
+ return;
+ }
+
+ write(1);
+ }
+
+ void stop()
+ {
+ if (!(read() | read()))
+ write(0);
+ }
+
+ void power_up()
+ {
+ Cmd::access_t cmd = read();
+ Cmd::Sud::set(cmd, 1);
+ Cmd::Pod::set(cmd, 1);
+ Cmd::Fre::set(cmd, 1);
+ write(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();
+ 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(Ssts::Ipm::ACTIVE);
+
+ while ((Ssts::Dec::get(status) != Ssts::Dec::ESTABLISHED) ||
+ !(Ssts::Ipm::get(status) & Ssts::Ipm::ACTIVE))
+ status = read();
+ }
+
+ 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())
+ PWRN("CMD.ST bit set during device reset --> unknown behavior");
+
+ write(1);
+ hba.delayer().usleep(1000);
+ write(0);
+
+ if (!wait_for(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(read()); }
+
+ /**
+ * 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(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(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_ */
diff --git a/repos/os/src/drivers/ahci/ata_driver.h b/repos/os/src/drivers/ahci/ata_driver.h
new file mode 100644
index 0000000000..44ed911d92
--- /dev/null
+++ b/repos/os/src/drivers/ahci/ata_driver.h
@@ -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 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() | Port::read();
+
+ 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(read ? 0 : 1);
+ header.clear_byte_count();
+
+ /* set pending */
+ Port::write(1U << slot);
+ execute(slot);
+ }
+
+
+ /*****************
+ ** Port_driver **
+ *****************/
+
+ void handle_irq() override
+ {
+ Is::access_t status = Port::read();
+
+ 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())) {
+ ack_irq();
+ ack_packets();
+ }
+
+ stop();
+ }
+
+ void check_device()
+ {
+ if (!info->read() ||
+ !hba.ncg()) {
+ PERR("Device does not support native command queuing: abort");
+ state_change();
+ return;
+ }
+
+ cmd_slots = min((int)cmd_slots,
+ info->read() + 1);
+ 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())
+ size = info->read() / 2;
+
+ return size;
+ }
+
+ Block::sector_t block_count() override
+ {
+ return info->read();
+ }
+};
+
+
+#endif /* _ATA_DRIVER_H_ */
diff --git a/repos/os/src/drivers/ahci/atapi_driver.h b/repos/os/src/drivers/ahci/atapi_driver.h
new file mode 100644
index 0000000000..45aa67b2f8
--- /dev/null
+++ b/repos/os/src/drivers/ahci/atapi_driver.h
@@ -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
+
+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(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();
+
+ 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();
+
+ if (verbose) {
+ PDBG("irq: is: %x ci: %x state: %u", status, Port::read(), state);
+ Device_fis f(fis_base);
+ PDBG("d2h: status: %x error: %x", f.read(), f.read());
+ }
+
+ ack_irq();
+
+ if (state == TEST_READY && Port::Is::Dhrs::get(status)) {
+ Device_fis f(fis_base);
+
+ /* check if devic is ready */
+ if (f.read() && !f.read())
+ 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(0);
+ header.clear_byte_count();
+
+ /* set pending */
+ execute(0);
+ }
+};
+
+#endif /* _ATAPI_DRIVER_H_ */
diff --git a/repos/os/src/drivers/ahci/empty.cc b/repos/os/src/drivers/ahci/empty.cc
deleted file mode 100644
index 066c1bf825..0000000000
--- a/repos/os/src/drivers/ahci/empty.cc
+++ /dev/null
@@ -1,3 +0,0 @@
-/*
- * Dummy compilation unit needed to link a valid target.
- */
diff --git a/repos/os/src/drivers/ahci/exynos5/ahci_driver.cc b/repos/os/src/drivers/ahci/exynos5/ahci_driver.cc
deleted file mode 100644
index 15ad874b6b..0000000000
--- a/repos/os/src/drivers/ahci/exynos5/ahci_driver.cc
+++ /dev/null
@@ -1,1960 +0,0 @@
-/*
- * \brief AHCI driver implementation
- * \author Martin Stein
- * \date 2013-05-17
- */
-
-/*
- * 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.
- */
-
-/* local includes */
-#include
-
-/* Genode includes */
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-using namespace Genode;
-
-/**
- * Delayer for MMIO polling
- */
-struct Timer_delayer : Mmio::Delayer, Timer::Connection
-{
- void usleep(unsigned us) { Timer::Connection::usleep(us); }
-};
-
-static Mmio::Delayer * delayer() {
- static Timer_delayer s;
- return &s;
-}
-
-/**
- * Compose a physical region descriptor
- *
- * \param prd_addr destination
- * \param phys physical region base
- * \param size physical region size
- */
-void write_prd(addr_t prd_addr, uint64_t phys, unsigned size)
-{
- struct Bits : Register<32>
- {
- struct Dbc : Bitfield<0, 22> { }; /* data byte count */
- struct I : Bitfield<31, 1> { }; /* IRQ on completion */
- };
- struct Prd
- {
- uint64_t dba; /* data base address */
- uint32_t zero;
- uint32_t bits;
- };
- Bits::access_t bits = 0;
- Bits::Dbc::set(bits, size - 1);
-
- Prd volatile * prd = (Prd volatile *)prd_addr;
- prd->dba = phys;
- prd->zero = 0;
- prd->bits = bits;
-}
-
-/**
- * Compose a command slot
- *
- * \param slot_addr destination
- * \param ct_phys physical command table base address
- * \param w writes 1: host-to-device 0: device-to-host
- * \param reset wether it is a soft reset command
- * \param pmp port multiplier port
- * \param prdtl physical region descriptor table length in entries
- */
-void write_cmd_slot(addr_t slot_addr, uint64_t ct_phys, bool w,
- bool reset, uint8_t pmp, uint16_t prdtl)
-{
- struct Bits : Register<32>
- {
- struct Cfl : Bitfield<0, 5> { }; /* command FIS length */
- struct A : Bitfield<5, 1> { }; /* ATAPI command */
- struct W : Bitfield<6, 1> { }; /* write (1: H2D, 0: D2H) */
- struct P : Bitfield<7, 1> { }; /* prefetchable */
- struct R : Bitfield<8, 1> { }; /* reset */
- struct B : Bitfield<9, 1> { }; /* BIST */
- struct C : Bitfield<10, 1> { }; /* clear busy upon R_OK */
- struct Pmp : Bitfield<12, 4> { }; /* port multiplier port */
- struct Prdtl : Bitfield<16, 16> { }; /* PRD-table length in entries */
- };
- struct Slot
- {
- uint32_t bits;
- uint32_t prdbc; /* PRD byte count */
- uint64_t ctba; /* command table descriptor base address */
- uint32_t zero;
- };
- Bits::access_t bits = 0;
- Bits::Cfl::set(bits, 5);
- Bits::W::set(bits, w);
- Bits::R::set(bits, reset);
- Bits::C::set(bits, reset);
- Bits::Pmp::set(bits, pmp);
- Bits::Prdtl::set(bits, prdtl);
-
- Slot volatile * slot = (Slot volatile *)slot_addr;
- slot->bits = bits;
- slot->prdbc = 0;
- slot->ctba = ct_phys;
- slot->zero = 0;
-}
-
-/**
- * Frame information structure
- */
-struct Fis
-{
- /* FIS payload */
- uint8_t volatile byte[20];
-
- void _init()
- {
- for (unsigned i = 0; i < sizeof(byte)/sizeof(byte[0]); i++)
- byte[i] = 0;
- }
-
- void _reg_h2d()
- {
- byte[0] = 0x27; /* type */
- byte[15] = 0x08; /* control */
- }
-
- void _cmd_h2d()
- {
- _reg_h2d();
-
- struct Flags : Register<8>
- {
- struct Cmd : Bitfield<7, 1> { }; /* 1: command 0: control */
- };
- Flags::access_t flags = 0;
- Flags::Cmd::set(flags, 1);
-
- byte[1] = flags;
- }
-
- void _obsolete_device()
- {
- byte[7] = 0xa0;
- }
-
- void _lba(uint64_t lba)
- {
- byte[4] = lba & 0xff;
- byte[5] = (lba >> 8) & 0xff;
- byte[6] = (lba >> 16) & 0xff;
- byte[8] = (lba >> 24) & 0xff;
- byte[9] = (lba >> 32) & 0xff;
- byte[10] = (lba >> 40) & 0xff;
- }
-
- void _feature(uint16_t ft)
- {
- byte[3] = ft & 0xff;
- byte[11] = (ft >> 8) & 0xff;
- }
-
- void _count(uint16_t cnt)
- {
- byte[12] = cnt & 0xff;
- byte[13] = (cnt >> 8) & 0xff;
- }
-
- public:
-
- /**
- * Read PIO-setup transfer count
- */
- uint16_t transfer_cnt()
- {
- uint16_t ret = 0;
- ret |= (uint16_t)byte[16];
- ret |= (uint16_t)byte[17] << 8;
- return ret;
- }
-
- /**
- * Read count
- */
- uint16_t count()
- {
- uint16_t ret = 0;
- ret |= (uint16_t)byte[12];
- ret |= (uint16_t)byte[13] << 8;
- return ret;
- }
-
- /**
- * Read logical block address
- */
- uint64_t lba()
- {
- uint64_t ret = 0;
- ret |= (uint64_t)byte[4];
- ret |= (uint64_t)byte[5] << 8;
- ret |= (uint64_t)byte[6] << 16;
- ret |= (uint64_t)byte[8] << 24;
- ret |= (uint64_t)byte[9] << 32;
- ret |= (uint64_t)byte[10] << 40;
- return ret;
- }
-
- /**
- * FIS to clear device-to-host receive area
- */
- void clear_d2h_rx()
- {
- _init();
- _reg_h2d();
- _obsolete_device();
-
- byte[2] = 0x80; /* command */
- }
-
- /**
- * Command FIS for ATA command 'identify device'
- */
- void identify_device()
- {
- _init();
- _cmd_h2d();
- _obsolete_device();
-
- byte[2] = 0xec; /* command */
- }
-
- /**
- * Command FIS for ATA command 'read native max addr'
- */
- void read_native_max_addr()
- {
- _init();
- _cmd_h2d();
- _obsolete_device();
-
- byte[2] = 0x27; /* command */
- byte[7] |= 0x40; /* device */
- }
-
- /**
- * Command FIS for ATA command 'set features' with feature 'set transfer mode'
- *
- * \param transfer_mode ID of targeted mode
- */
- void set_transfer_mode(uint8_t transfer_mode)
- {
- _init();
- _cmd_h2d();
- _obsolete_device();
- _feature(3);
- _count(transfer_mode);
-
- byte[2] = 0xef; /* command */
- }
-
- /**
- * Command FIS for ATA command 'read / write FPDMA queued'
- *
- * \param w 1: do write FPDMA queued 0: do read FPDMA queued
- * \param block_nr logical block address (LBA)
- * \param block_cnt blocks to be read / write
- * \param tag command slot ID
- */
- void fpdma_queued(bool w, uint64_t block_nr,
- uint16_t block_cnt, unsigned tag)
- {
- _init();
- _cmd_h2d();
- _feature(block_cnt);
- _lba(block_nr);
-
- struct Count : Register<16>
- {
- struct Tag : Bitfield<3, 5> { };
- };
- Count::access_t cnt = 0;
- Count::Tag::set(cnt, tag);
- _count(cnt);
-
- byte[2] = w ? 0x61 : 0x60; /* command */
-
- struct Device : Register<8>
- {
- struct Lba_mode : Bitfield<6, 1> { };
- };
- Device::access_t dev = byte[7];
- Device::Lba_mode::set(dev, 1);
- byte[7] = dev;
- }
-
- /**
- * First and second soft reset FIS
- *
- * \param second if this is the second soft reset FIS or the first
- * \param pmp port multiplier port
- */
- void soft_reset(bool second, uint8_t pmp)
- {
- _init();
- _reg_h2d();
- _obsolete_device();
-
- struct Flags : Register<8>
- {
- struct Pmp : Bitfield<0, 4> { }; /* port multiplier port */
- };
- Flags::access_t flags = byte[1];
- Flags::Pmp::set(flags, pmp);
- byte[1] = flags;
-
- struct Control : Register<8>
- {
- struct Softreset : Bitfield<2, 1> { };
- };
- Control::access_t ctl = byte[15];
- Control::Softreset::set(ctl, !second);
- byte[15] = ctl;
- }
-
- /**
- * Wether a PIO setup FIS was sucessfully received
- *
- * \param transfer_size size of transfered data
- * \param block_nr LBA of transfered data (0 if it has no LBA)
- */
- bool is_pio_setup(uint16_t transfer_size, uint64_t block_nr)
- {
- struct Flags : Register<8>
- {
- struct Pmp : Bitfield<0,4> { }; // port multiplier port
- struct D : Bitfield<5,1> { }; // data transfer direction, 1: D2H
- struct I : Bitfield<6,1> { }; // interrupt bit
- };
- Flags::access_t flags = 0;
- Flags::D::set(flags, 1);
- Flags::I::set(flags, 1);
-
- /**
- * ATA device register
- */
- struct Device : Register<8>
- {
- struct Lba28_27_24 : Bitfield<0, 4> { }; // LBA[27..24] if LBA28 is used
- struct Slave : Bitfield<4, 1> { }; // 0: master 1: slave
- struct Obsolete_0 : Bitfield<5, 1> { }; // = 1
- struct Lba : Bitfield<6, 1> { }; // FIXME: LBA flag does what?
- struct Obsolete_1 : Bitfield<7, 1> { }; // = 1
- };
-
- /*
- * FIXME
- * The count register is set differently for different
- * drives and i've no idea what it means in this context
- * but as long as all works fine i ignore it simply.
- * (WD2500BEVS: 0xff, SAMSUNG840PRO128GB: 0x1)
- *
- * FIXME
- * LBA flag in device register is 1 for at least
- * OCZ Agility 3 120 GB but normally it is 0. Hopefully
- * ignoring it becomes not a problem in future.
- */
- return byte[0] == 0x5f && /* type */
- byte[1] == flags &&
- byte[2] == 0x58 && /* old status */
- byte[3] == 0 && /* error */
- lba() == block_nr &&
- Device::Lba28_27_24::get(byte[7]) == 0 &&
- Device::Slave::get(byte[7]) == 0 &&
- Device::Obsolete_0::get(byte[7]) == 1 &&
- Device::Obsolete_1::get(byte[7]) == 1 &&
- byte[15] == 0x50 && /* new status */
- transfer_cnt() == transfer_size;
- }
-
- /**
- * Print out FIS content in three lines with two spaces indent
- */
- void print()
- {
- printf(" 0: 0x%02x", byte[ 0]);
- printf(" 1: 0x%02x", byte[ 1]);
- printf(" 2: 0x%02x", byte[ 2]);
- printf(" 3: 0x%02x", byte[ 3]);
- printf(" 4: 0x%02x", byte[ 4]);
- printf(" 5: 0x%02x", byte[ 5]);
- printf(" 6: 0x%02x", byte[ 6]);
- printf(" 7: 0x%02x\n", byte[ 7]);
- printf(" 8: 0x%02x", byte[ 8]);
- printf(" 9: 0x%02x", byte[ 9]);
- printf(" 10: 0x%02x", byte[10]);
- printf(" 11: 0x%02x", byte[11]);
- printf(" 12: 0x%02x", byte[12]);
- printf(" 13: 0x%02x", byte[13]);
- printf(" 14: 0x%02x", byte[14]);
- printf(" 15: 0x%02x\n", byte[15]);
- printf(" lba: %llu", lba());
- printf(" cnt: %u\n", count());
- }
-
- /**
- * Wether reply for 'set transfer mode' was successfully received
- *
- * \param transfer_mode ID of transfer mode that should be set
- */
- bool is_set_transfer_mode_reply(uint8_t transfer_mode)
- {
- /*
- * FIXME
- * I've no idea what most of these values stand for and
- * interpreting Linux seems to be the only way to change this.
- */
- bool result = 0;
- result = byte[0] == 0x34 && /* type */
- byte[1] == 0x40 &&
- byte[2] == 0x50 &&
- byte[3] == 0 &&
- lba() == 0 &&
- byte[7] == 0xa0 && /* device */
- byte[11] == 0 &&
- byte[14] == 0 &&
- byte[15] == 0;
-
- /*
- * FIXME
- * Sometimes count is 0 and sometimes it equals the transfer
- * mode that was set but both seems to work.
- */
- if (count() == 0)
- printf("cleared transfer mode in reconfiguration reply\n");
- else if (count() != transfer_mode)
- result = 0;
-
- return result;
- }
-};
-
-/**
- * I2C master interface
- */
-struct I2c_interface : Attached_mmio
-{
- enum { VERBOSE = 0 };
-
- 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> { };
- };
-
- /* 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)
- : Attached_mmio(base, 0x10000),
- 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() && !read()) 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()) {
- 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(0, *delayer())) {
- PERR("I2C busy");
- return -1;
- }
- Stat::access_t stat = read();
- Stat::Txrx_en::set(stat, 1);
- Stat::Mode::set(stat, 3);
- write(stat);
- write(start_msg);
- delayer()->usleep(1000);
- write(11);
- write(1);
-
- /* transmit message payload */
- for (unsigned i = 0; i < msg_size; i++) {
- if (!ack_received()) return -1;
- write(msg[i]);
- delayer()->usleep(TX_DELAY_US);
- write(0);
- if (arbitration_error()) return -1;
- }
- /* end message transfer */
- if (!ack_received()) return -1;
- write(0);
- write(0);
- write(0); /* FIXME fixup */
- if (arbitration_error()) return -1;
- if (!wait_for(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() : I2c_interface(0x121d0000, SLAVE_ADDR) { }
-
- /**
- * 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(SLAVE_ADDR);
-
- Con::access_t con = read();
- 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);
-
- Lc::access_t lc = 0;
- Lc::Sda_out_delay::set(lc, 3);
- Lc::Filter_en::set(lc, 1);
- write(lc);
- }
-};
-
-static I2c_sataphy * i2c_sataphy() {
- static I2c_sataphy i2c_sataphy;
- return &i2c_sataphy;
-}
-
-/**
- * Classical control interface of SATA PHY-layer controller
- */
-struct Sata_phy_ctrl : Attached_mmio
-{
- enum { VERBOSE = 0 };
-
- /********************************
- ** 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() : Attached_mmio(0x12170000, 0x10000) { }
-
- /**
- * Initialize parts of SATA PHY that are controlled classically
- *
- * \retval 0 call was successful
- * \retval <0 call failed, error code
- */
- int init()
- {
- /* reset */
- write(0);
- write(~0);
- write(~0);
- write(~0);
-
- /* set up SATA phy generation 3 (6 Gb/s) */
- Phctrlm::access_t phctrlm = read();
- Phctrlm::Ref_rate::set(phctrlm, 0);
- Phctrlm::High_speed::set(phctrlm, 1);
- write(phctrlm);
- Ctrl0::access_t ctrl0 = read();
- Ctrl0::P0_phy_calibrated::set(ctrl0, 1);
- Ctrl0::P0_phy_calibrated_sel::set(ctrl0, 1);
- write(ctrl0);
- write(2);
- if (i2c_sataphy()->enable_40_pins()) return -1;
-
- /* Release reset */
- write(0);
- write(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(1, *delayer())) {
- PERR("PLL lock failed");
- return -1;
- }
- if (VERBOSE) printf("SATA PHY initialized\n");
- return 0;
- }
-};
-
-static Sata_phy_ctrl * sata_phy_ctrl() {
- static Sata_phy_ctrl sata_phy_ctrl;
- return &sata_phy_ctrl;
-}
-
-/**
- * SATA AHCI interface
- */
-struct Sata_ahci : Attached_mmio
-{
- enum { VERBOSE = 0 };
-
- /* general config */
- enum {
- /* FIXME only with port multiplier support (sata_srst_pmp in Linux) */
- SOFT_RESET_PMP = 15,
- BLOCK_SIZE = 512,
- BLOCKS_PER_LOG = 1,
- BYTES_PER_PRD = 1 << 22,
- };
-
- /* DMA structure */
- enum {
- CMD_LIST_SIZE = 0x400,
- CMD_SLOT_SIZE = 0x20,
- FIS_AREA_SIZE = 0x100,
- CMD_TABLE_SIZE = 0xb00,
- CMD_TABLE_HEAD_SIZE = 0x80,
- PRD_SIZE = 0x10,
- };
-
- /* FIS RX area structure */
- enum {
- REG_D2H_FIS_OFFSET = 0x40,
- PIO_SETUP_FIS_OFFSET = 0x20,
- };
-
- /* debouncing settings */
- enum {
- FAST_DBC_TRIAL_US = 5000,
- SLOW_DBC_TRIAL_US = 25000,
- };
-
- /* modes when doing 'set features' with feature 'set transfer mode' */
- enum { UDMA_133 = 0x46, };
-
- /********************************
- ** MMIO structure description **
- ********************************/
-
- struct Cap : Register<0x0, 32>
- {
- struct Np : Bitfield<0, 4> { };
- struct Ems : Bitfield<6, 1> { };
- struct Ncs : Bitfield<8, 5> { };
- struct Iss : Bitfield<20, 4> { };
- };
- struct Ghc : Register<0x4, 32>
- {
- struct Hr : Bitfield<0, 1> { };
- struct Ie : Bitfield<1, 1> { };
- struct Ae : Bitfield<31, 1> { };
- };
- struct Is : Register<0x8, 32, 1>
- {
- struct Ips : Bitfield<0, 1> { };
- };
- struct Pi : Register<0xc, 32> { };
- struct Vs : Register<0x10, 32>
- {
- struct Mnr : Bitfield<0, 16> { };
- struct Mjr : Bitfield<16, 16> { };
- };
- struct Cap2 : Register<0x24, 32> { };
- struct P0clb : Register<0x100, 32>
- {
- struct Clb : Bitfield<10, 22> { };
- };
- struct P0fb : Register<0x108, 32>
- {
- struct Fb : Bitfield<8, 24> { };
- };
- struct P0is : Register<0x110, 32, 1>
- {
- struct Dhrs : Bitfield<0, 1> { };
- struct Pss : Bitfield<1, 1> { };
- struct Sdbs : Bitfield<3, 1> { };
- struct Infs : Bitfield<26, 1> { };
- struct Ifs : Bitfield<27, 1> { };
- };
- struct P0ie : Register<0x114, 32>
- {
- struct Dhre : Bitfield<0, 1> { };
- struct Pse : Bitfield<1, 1> { };
- struct Dse : Bitfield<2, 1> { };
- struct Sdbe : Bitfield<3, 1> { };
- struct Ufe : Bitfield<4, 1> { };
- struct Dpe : Bitfield<5, 1> { };
- struct Pce : Bitfield<6, 1> { };
- struct Prce : Bitfield<22, 1> { };
- struct Ife : Bitfield<27, 1> { };
- struct Hbde : Bitfield<28, 1> { };
- struct Hbfe : Bitfield<29, 1> { };
- struct Tfee : Bitfield<30, 1> { };
- };
- struct P0cmd : Register<0x118, 32>
- {
- struct St : Bitfield<0, 1> { };
- struct Sud : Bitfield<1, 1> { };
- struct Pod : Bitfield<2, 1> { };
- struct Fre : Bitfield<4, 1> { };
- struct Fr : Bitfield<14, 1> { };
- struct Cr : Bitfield<15, 1> { };
- struct Pma : Bitfield<17, 1> { };
- struct Atapi : Bitfield<24, 4> { };
- struct Icc : Bitfield<28, 4> { };
- };
- struct P0tfd : Register<0x120, 32>
- {
- struct Sts_bsy : Bitfield<7, 1> { };
- };
- struct P0sig : Register<0x124, 32>
- {
- struct Lba_8_15 : Bitfield<16, 8> { };
- struct Lba_16_31 : Bitfield<24, 8> { };
- };
- struct P0ssts : Register<0x128, 32>
- {
- struct Det : Bitfield<0, 4> { };
- struct Spd : Bitfield<4, 4> { };
- struct Ipm : Bitfield<8, 4> { };
- };
- struct P0sctl : Register<0x12c, 32>
- {
- struct Det : Bitfield<0, 4> { };
- struct Spd : Bitfield<4, 4> { };
- struct Ipm : Bitfield<8, 4> { };
- };
- struct P0serr : Register<0x130, 32>
- {
- struct Err_c : Bitfield<9, 1> { };
- struct Err_p : Bitfield<10, 1> { };
- struct Diag_n : Bitfield<16, 1> { };
- struct Diag_b : Bitfield<19, 1> { };
- struct Diag_c : Bitfield<21, 1> { };
- struct Diag_h : Bitfield<22, 1> { };
- struct Diag_x : Bitfield<26, 1> { };
- };
- struct P0sact : Register<0x134, 32, 1> { };
- struct P0ci : Register<0x138, 32, 1> { };
- struct P0sntf : Register<0x13c, 32, 1>
- {
- struct Pmn : Bitfield<0, 16> { };
- };
-
- /* device settings */
- uint64_t block_cnt;
-
- /* working-DMA structure */
- Dataspace_capability ds;
- addr_t cl_phys; /* command list */
- addr_t cl_virt;
- addr_t fb_phys; /* FIS receive area */
- addr_t fb_virt;
- addr_t ct_phys; /* command table */
- addr_t ct_virt;
-
- /* debouncing settings */
- unsigned dbc_trial_us;
- unsigned dbc_trials;
- unsigned dbc_stable_trials;
-
- /* port 0 settings */
- unsigned p0_speed;
- Irq_connection p0_irq;
- Genode::Signal_receiver p0_irq_rec;
- Genode::Signal_context p0_irq_ctx;
-
- enum { SATA_3_MAX_SPEED = 3 };
-
- /**
- * Constructor
- */
- Sata_ahci()
- : Attached_mmio(0x122f0000, 0x10000),
- ds(env()->ram_session()->alloc(0x20000, Genode::UNCACHED)),
- cl_phys(Dataspace_client(ds).phys_addr()),
- cl_virt(env()->rm_session()->attach(ds)),
- fb_phys(cl_phys + CMD_LIST_SIZE),
- fb_virt(cl_virt + CMD_LIST_SIZE),
- ct_phys(fb_phys + FIS_AREA_SIZE),
- ct_virt(fb_virt + FIS_AREA_SIZE),
- dbc_trial_us(FAST_DBC_TRIAL_US),
- dbc_trials(50),
- dbc_stable_trials(5),
- p0_speed(SATA_3_MAX_SPEED),
- p0_irq(Genode::Board_base::SATA_IRQ)
- {
- p0_irq.sigh(p0_irq_rec.manage(&p0_irq_ctx));
- p0_irq.ack_irq();
- }
-
- /**
- * Clear all interrupts at port 0
- *
- * \return value of P0IS before it was cleared
- */
- P0is::access_t p0_clear_irqs()
- {
- P0is::access_t p0is = read();
- write(p0is);
- return p0is;
- }
-
- /**
- * Get port back ready after port IRQs were raised
- *
- * \param lba holds current drive LBA if call returns 1
- *
- * \retval 0 no errors were detected during IRQ handling
- * \retval 1 port has been recovered from errors, lba denotes error point
- * \retval -1 errors occured and port couln't be recovered
- */
- int p0_handle_irqs(uint64_t & lba)
- {
- /* ack interrupts and errors */
- P0is::access_t p0is = p0_clear_irqs();
- P0serr::access_t p0serr = p0_clear_errors();
-
- /* leave if interrupts are just as expected */
- if (p0is == P0is::Sdbs::bits(1)) return 0;
- if (p0is == P0is::Dhrs::bits(1)) return 0;
-
- /* interpret unexpected interrupts */
- bool interface_err = 0;
- bool fatal = 0;
- if (P0is::Ifs::get(p0is)) {
- interface_err = 1;
- if (VERBOSE) fatal = 1;
- } else if (P0is::Infs::get(p0is))
- interface_err = 1;
-
- /* print and handle known errors */
- if (interface_err)
- {
- /* print errors */
- if(VERBOSE) {
- printf("handle");
- if (fatal) printf(" fatal");
- else printf(" non-fatal");
- printf(" interface errors:\n");
- if (P0serr::Diag_b::get(p0serr))
- printf(" 10 B to 8 B decode error\n");
- if (P0serr::Err_p::get(p0serr))
- printf(" protocol error\n");
- if (P0serr::Diag_c::get(p0serr))
- printf(" CRC error\n");
- if (P0serr::Err_c::get(p0serr))
- printf(" non-recovered persistent communication error\n");
- if (P0serr::Diag_h::get(p0serr))
- printf(" handshake error\n");
- }
- /* get error LBA */
- Fis * fis = (Fis *)(fb_virt + REG_D2H_FIS_OFFSET);
- lba = fis->lba();
-
- /* print reply FIS */
- if (VERBOSE) {
- printf("error report that was sent by the drive:\n");
- fis->print();
- }
- /* handle errors */
- return p0_error_recovery() ? -1 : 1;
- }
- /* complain about unkown errors */
- PERR("unknown error (P0IS 0x%x P0SERR 0x%x)\n", p0is, p0serr);
- return -1;
- }
-
- /**
- * Get the AHCI controller ready for port initializations
- *
- * \retval 0 call was successful
- * \retval <0 call failed, error code
- */
- int init()
- {
- /* enable AHCI */
- write(1);
- if (!read()) {
- PERR("SATA AHCI failed to enable AHCI");
- return -1;
- }
- /* save HBA config */
- Cap::access_t cap = read();
- Pi::access_t pi = read();
- Vs::access_t vs = read();
- Cap2::access_t cap2 = read();
-
- /* check port number and mask */
- unsigned ports = 0;
- for (unsigned i = 0; i < Pi::ACCESS_WIDTH; i++) if (pi & (1 << i)) ports++;
- if (ports != Cap::Np::get(cap) + 1) {
- ports = Cap::Np::get(cap) + 1;
- pi = (1 << ports) - 1;
- }
- if (ports != 1 || pi != 1) {
- PERR("SATA AHCI driver proved with port 0 only");
- return -1;
- }
- /* check enclosure management support */
- if (Cap::Ems::get(cap)) {
- PERR("SATA AHCI driver proved without EMS only");
- return -1;
- }
- /* check AHCI revision */
- unsigned rev_mjr = Vs::Mjr::get(vs);
- unsigned rev_mnr = Vs::Mnr::get(vs);
- if (rev_mjr != 0x1 || rev_mnr != 0x300) {
- PERR("SATA AHCI driver proved with AHCI rev 1.3 only");
- return -1;
- }
- /* check interface speed */
- char const * speed;
- switch(Cap::Iss::get(cap)) {
- case 1:
- speed = "1.5";
- break;
- case 2:
- speed = "3";
- break;
- case 3:
- speed = "6";
- break;
- default:
- PERR("SATA AHCI failed to get controller speed");
- return -1;
- }
- /* check number of command slots */
- unsigned slots = Cap::Ncs::get(cap) + 1;
- if (slots != 32) {
- PERR("SATA AHCI driver proved with 32 slots only");
- return -1;
- }
- /* reset */
- write(1);
- if (!wait_for(0, *delayer(), 1000, 1000)) {
- PERR("SATA AHCI reset failed");
- return -1;
- }
- /* enable AHCI */
- write(1);
- if (!read()) {
- PERR("SATA AHCI failed to enable AHCI");
- return -1;
- }
- /* restore HBA config */
- write(cap);
- write(cap2);
- write(pi);
- if (VERBOSE)
- printf("SATA AHCI initialized, AHCI rev %x.%x, "
- "%s Gbps, %u slots, %u port%c\n",
- rev_mjr, rev_mnr, speed, slots,
- ports, ports > 1 ? 's' : ' ');
- return 0;
- }
-
- /**
- * Stop processing commands at port 0
- *
- * \retval 0 call was successful
- * \retval <0 call failed, error code
- */
- int p0_disable_cmd_processing()
- {
- P0cmd::access_t p0cmd = read();
- if (P0cmd::St::get(p0cmd) || P0cmd::Cr::get(p0cmd)) {
- write(0);
- if (!wait_for(0, *delayer(), 500, 1000)) {
- PERR("PORT0 failed to stop HBA processing");
- return -1;
- }
- }
- return 0;
- }
-
- /**
- * Start processing commands at port 0
- */
- void p0_enable_cmd_processing()
- {
- write(1);
- read(); /* flush */
- }
-
- /**
- * Stop and restart processing commands at port 0
- *
- * \retval 0 call was successful
- * \retval <0 call failed, error code
- */
- int p0_restart_cmd_processing()
- {
- if (p0_disable_cmd_processing()) return -1;
- p0_enable_cmd_processing();
- return 0;
- }
-
- /**
- * Execute prepared command, wait for completion and acknowledge at port
- *
- * \param P0IS_BIT state bit of the interrupt that's expected to be raised
- * \param tag command slot ID
- *
- * \retval 0 call was successful
- * \retval -1 call failed
- */
- template
- int p0_issue_cmd(unsigned tag)
- {
- typedef typename P0IS_BIT::Bitfield_base P0is_bit;
- write(1 << tag);
- p0_irq_rec.wait_for_signal();
- if (!read()) {
- PERR("ATA0 no IRQ raised");
- return -1;
- }
- if (read() != P0is_bit::bits(1)) {
- PERR("ATA0 expected P0IS to be %x (is %x)",
- P0is_bit::bits(1), read());
- return -1;
- }
- write(1);
- if (read()) {
- PERR("ATA0 unfinished IRQ after command");
- return -1;
- }
- return 0;
- }
-
- /**
- * Request and read out the identification data of the port 0 device
- *
- * \retval 0 call was successful
- * \retval <0 call failed, error code
- */
- int p0_identify_device()
- {
- /**
- * Device identification data
- */
- struct Device_id
- {
- enum {
- /* FIXME use register framework to do shifts */
- UDMA_133_SUPPORTED = 1 << 6,
- UDMA_133_ACTIVE = 1 << 14,
- SIZE = 0x200,
- };
-
- uint16_t na_0[23]; /* word 0.. 22 */
- char revision[8]; /* word 23.. 26 */
- char model_nr[40]; /* word 27.. 46 */
- uint16_t na_1[28]; /* word 47.. 74 */
- uint16_t queue_depth; /* word 75 */
- uint16_t sata_caps; /* word 76 */
- uint16_t na_2[11]; /* word 77.. 87 */
- uint16_t udma; /* word 88 */
- uint16_t na_3[11]; /* word 89.. 99 */
- uint64_t total_lba_sectors; /* word 100 */
-
- /**
- * Helper to print interchanged char arrays
- */
- void print(char const * src, size_t size) {
- for(unsigned i = 0; i < size; i+=2)
- {
- if (!src[i+1] || !src[i]) return;
- if (src[i+1] == 0x20 && src[i] == 0x20) return;
- printf("%c%c", src[i+1], src[i]);
- }
- }
-
- /**
- * Print model name and firmware revision of the device
- */
- void print_label()
- {
- print(model_nr, sizeof(model_nr)/sizeof(model_nr[0]));
- printf(" rev ");
- print(revision, sizeof(revision)/sizeof(revision[0]));
- }
-
- /**
- * Wether device supports native command queueing (NCQ)
- */
- bool supports_ncq() { return sata_caps & (1 << 8); }
- };
-
- /* create receive buffer DMA */
- Ram_dataspace_capability dev_id_ds = env()->ram_session()->alloc(0x1000, Genode::UNCACHED);;
- addr_t dev_id_virt = (addr_t)env()->rm_session()->attach(dev_id_ds);
- addr_t dev_id_phys = Dataspace_client(dev_id_ds).phys_addr();
-
- /* do command 'identify device' */
- unsigned tag = 31;
- addr_t cmd_table = ct_virt + tag * CMD_TABLE_SIZE;
- Fis * fis = (Fis *)cmd_table;
- fis->identify_device();
- unsigned prd_id = 0;
- addr_t prd = cmd_table + CMD_TABLE_HEAD_SIZE + prd_id * PRD_SIZE;
- write_prd(prd, dev_id_phys, Device_id::SIZE);
- addr_t cmd_slot = cl_virt + tag * CMD_SLOT_SIZE;
- write_cmd_slot(cmd_slot, ct_phys + tag * CMD_TABLE_SIZE, 0, 0, 0, 1);
- if (p0_issue_cmd(tag)) return -1;
-
- /* check if we received the requested data */
- fis = (Fis *)(fb_virt + PIO_SETUP_FIS_OFFSET);
- if (!fis->is_pio_setup(Device_id::SIZE, 0)) {
- PERR("Invalid PIO setup FIS");
- fis->print();
- return -1;
- }
- /* interpret device ID */
- Device_id * dev_id = (Device_id *)dev_id_virt;
- block_cnt = dev_id->total_lba_sectors;
- if (VERBOSE) {
- printf("ATA0 ");
- dev_id->print_label();
- printf(", %llu blocks, %llu GB\n", block_cnt,
- ((uint64_t)block_cnt * BLOCK_SIZE) / 1000000000);
- }
- /* get command mode */
- if (!dev_id->supports_ncq()) {
- PERR("ATA0 driver not proved with modes other than NCQ");
- return -1;
- }
- /* get transfer mode */
- if (!(dev_id->udma & Device_id::UDMA_133_SUPPORTED)) {
- PERR("ATA0 driver not proved with other modes than UDMA133");
- return -1;
- }
- if (VERBOSE)
- printf("ATA0 supports UDMA-133 and NCQ with queue depth %u\n",
- dev_id->queue_depth + 1);
- write(1);
- p0_irq.ack_irq();
-
- /* destroy receive buffer DMA */
- env()->rm_session()->detach(dev_id_virt);
- env()->ram_session()->free(dev_id_ds);;
- return 0;
- }
-
- /**
- * Wether the port 0 device hides blocks via the HPA feature
- *
- * \retval 1 hides blocks
- * \retval 0 doesn't hide blocks
- * \retval -1 failed to determine
- */
- int p0_hides_blocks()
- {
- /* do command 'read native max addr' */
- unsigned tag = 31;
- addr_t cmd_table = ct_virt + tag * CMD_TABLE_SIZE;
- Fis * fis = (Fis *)cmd_table;
- fis->read_native_max_addr();
- addr_t cmd_slot = cl_virt + tag * CMD_SLOT_SIZE;
- write_cmd_slot(cmd_slot, ct_phys + tag * CMD_TABLE_SIZE, 0, 0, 0, 0);
- if (p0_issue_cmd(tag)) return -1;
-
- /* read received address */
- fis = (Fis *)(fb_virt + REG_D2H_FIS_OFFSET);
- uint64_t max_native_addr = fis->lba();
-
- /* end command */
- write(1);
- p0_irq.ack_irq();
-
- /* check for hidden blocks */
- return max_native_addr + 1 != block_cnt;
- }
-
- /**
- * Clear all port errors at port 0
- *
- * \return value of P0SERR before it was cleared
- */
- P0serr::access_t p0_clear_errors()
- {
- P0serr::access_t const p0serr = read();
- write(p0serr);
- return p0serr;
- }
-
- /**
- * Tell port 0 device wich transfer mode to use
- *
- * \param mode ID of targeted transfer mode
- *
- * \retval 0 call was successful
- * \retval <0 call failed, error code
- */
- int p0_transfer_mode(uint8_t mode)
- {
- /* do command 'set features' with feature 'set transfer mode' */
- unsigned tag = 31;
- addr_t cmd_table = ct_virt + tag * CMD_TABLE_SIZE;
- addr_t cmd_slot = cl_virt + tag * CMD_SLOT_SIZE;
- Fis * fis = (Fis *)cmd_table;
- fis->set_transfer_mode(mode);
- write_cmd_slot(cmd_slot, ct_phys + tag * CMD_TABLE_SIZE, 0, 0, 0, 0);
- if (p0_issue_cmd(tag)) return -1;
-
- /* check answer */
- fis = (Fis *)(fb_virt + REG_D2H_FIS_OFFSET);
- if (!fis->is_set_transfer_mode_reply(mode)) {
- PERR("Invalid reply after set up transfer mode");
- return -1;
- }
- /* end command */
- write(1);
- p0_irq.ack_irq();
- return 0;
- }
-
- /**
- * Enable interrupt reception for port 0
- */
- void p0_enable_irqs()
- {
- enum { PORT = 0 };
-
- /* clear IRQs */
- p0_clear_irqs();
- write(1 << PORT);
-
- /* enable all IRQs we need */
- P0ie::access_t p0ie = 0;
- P0ie::Dhre::set(p0ie, 1);
- P0ie::Pse::set(p0ie, 1);
- P0ie::Dse::set(p0ie, 1);
- P0ie::Sdbe::set(p0ie, 1);
- P0ie::Ufe::set(p0ie, 1);
- P0ie::Dpe::set(p0ie, 1);
- P0ie::Pce::set(p0ie, 1);
- P0ie::Prce::set(p0ie, 1);
- P0ie::Ife::set(p0ie, 1);
- P0ie::Hbde::set(p0ie, 1);
- P0ie::Hbfe::set(p0ie, 1);
- P0ie::Tfee::set(p0ie, 1);
- write(p0ie);
- }
-
- /**
- * Soft reset link at port 0
- *
- * \retval 0 call was successful
- * \retval <0 call failed, error code
- */
- int p0_soft_reset()
- {
- /* first soft reset FIS */
- if (p0_restart_cmd_processing()) return -1;
- Fis * fis = (Fis *)ct_virt;
- fis->soft_reset(0, SOFT_RESET_PMP);
- write_cmd_slot(cl_virt, ct_phys, 0, 1, SOFT_RESET_PMP, 0);
-
- /* we can't do p0_issue_cmd here - IRQ gets not triggered */
- write(1);
- if (!wait_for(0, *delayer(), 500, 1000)) {
- PERR("ATA0 failed to issue first soft-reset command");
- return -1;
- }
- delayer()->usleep(5); /* according to spec wait at least 5 us */
-
- /* second soft reset FIS */
- fis->soft_reset(1, SOFT_RESET_PMP);
- write_cmd_slot(cl_virt, ct_phys, 0, 0, SOFT_RESET_PMP, 0);
- write(1);
- read(); /* this time simply flush because dynamic waiting not needed */
-
- /* old devices might need 150 ms but newer specs say 2 ms */
- if (!wait_for(0, *delayer(), 150, 1000)) {
- PERR("ATA0 drive hangs in soft reset");
- return -1;
- }
- return 0;
- }
-
- /**
- * Hard reset link at port 0
- *
- * \retval 0 call was successful
- * \retval <0 call failed, error code
- */
- int p0_hard_reset(bool const set_speed = 0,
- P0sctl::access_t const speed = 0)
- {
- enum { IPM = 3 };
- if (set_speed) {
- /*
- * SATA spec doesn't provide much information about speed
- * reconfig. So turn off PHY meanwhile to be on the safe side.
- */
- P0sctl::access_t p0sctl = read();
- P0sctl::Ipm::set(p0sctl, IPM);
- P0sctl::Det::set(p0sctl, 4);
- write(p0sctl);
-
- /* reconfigure speed */
- p0sctl = read();
- P0sctl::Spd::set(p0sctl, speed);
- write(p0sctl);
- }
- /* request for reset via P0SCTL */
- P0sctl::access_t p0sctl = read();
- P0sctl::Ipm::set(p0sctl, IPM);
- P0sctl::Det::set(p0sctl, 1);
- write(p0sctl);
- read(); /* flush */
-
- /* wait until reset is done and end operation */
- delayer()->usleep(1000);
- unsigned trials = 100;
- for (; trials; trials--)
- {
- write(0);
-
- /*
- * FIXME
- * Some PHY controllers need much time at this point.
- * Thus normally we should wait at least 200 ms to avoid
- * bad behaviour but as long as exynos5 does fine
- * we do it faster.
- */
- delayer()->usleep(1000);
- p0sctl = read();
-
- if (P0sctl::Det::get(p0sctl) == 0 &&
- P0sctl::Ipm::get(p0sctl) == 3) break;
- }
- if (!trials) {
- PERR("PORT0 resume after hard reset failed");
- return -1;
- }
- return 0;
- }
-
- /**
- * Debounce link at port 0
- *
- * \param trials total amount of debouncing trials
- * \param trial_us time to wait between two trials
- * \param stable targeted amount of consecutive stable trials
- *
- * \retval 0 call was successful
- * \retval <0 call failed, error code
- *
- * We give the port some time in order that the P0SSTS becomes stable
- * over multiple reads. The call is successful if the register gets
- * stable in time and with P0SSTS.DET saying "connection established".
- */
- int p0_debounce(unsigned const trials, unsigned const trial_us,
- unsigned const stable)
- {
- unsigned t = 0; /* current trial */
- unsigned s = 0; /* current amount of stable trials */
- P0ssts::access_t old_det = read();
- for (; t < trials; t++) {
- delayer()->usleep(trial_us);
- P0ssts::access_t new_det = read();
- if (new_det == 3 && new_det == old_det) {
- s++;
- if (s >= stable) break;
- } else s = 0;
- old_det = new_det;
- }
- if (t >= trials) {
- if (VERBOSE) printf("PORT0 failed debouncing\n");
- return -1;
- }
- return 0;
- }
-
- /**
- * Disable interrupt reception for port 0
- */
- void p0_disable_irqs() { write(0); }
-
- /**
- * Get port 0 and its device ready for NCQ commands
- *
- * \retval 0 call was successful
- * \retval <0 call failed, error code
- */
- int p0_init()
- {
- /* disable command processing and FIS reception */
- p0_disable_cmd_processing();
- write(0);
- if (!wait_for(0, *delayer(), 500, 1000)) {
- PERR("PORT0 failed to stop FIS reception");
- return -1;
- }
- /* clear all S-errors and interrupts */
- p0_clear_errors();
- write(read());
- write(1);
-
- /* activate */
- write(1);
- read();
- P0cmd::access_t p0cmd = read();
- P0cmd::Sud::set(p0cmd, 1);
- P0cmd::Pod::set(p0cmd, 1);
- P0cmd::Icc::set(p0cmd, 1);
- write(p0cmd);
-
- /* set up command-list- and FIS-DMA */
- write(P0clb::Clb::masked(cl_phys));
- write(P0fb::Fb::masked(fb_phys));
-
- /* enable FIS reception and command processing */
- write(1);
- read();
- p0_enable_cmd_processing();
-
- /* disable port multiplier */
- write(0);
-
- /* freeze AHCI */
- p0_disable_irqs();
- p0_disable_cmd_processing();
-
- /* clear D2H receive area */
- Fis * fis = (Fis *)(fb_virt + REG_D2H_FIS_OFFSET);
- fis->clear_d2h_rx();
-
- if (p0_hard_reset(1, p0_speed)) return -1;
- if (p0_dynamic_debounce()) return -1;
-
- /* check if device is ready */
- if (!wait_for(0, *delayer())) {
- PERR("PORT0 device not ready");
- return -1;
- }
- p0_enable_cmd_processing();
-
- if (p0_soft_reset()) return -1;
- p0_enable_irqs();
- p0_clear_errors();
-
- /* set ATAPI bit appropriatly */
- write(0);
- read(); /* flush */
-
- /* check device type (LBA[31:8] = 0 means ATA device) */
- P0sig::access_t p0sig = read();
- if (P0sig::Lba_8_15::get(p0sig) || P0sig::Lba_16_31::get(p0sig)) {
- PERR("PORT0 driver not proved with non-ATA devices");
- return -1;
- }
- /* check device speed */
- char const * speed;
- P0ssts::access_t p0ssts = read();
- switch(P0ssts::Spd::get(p0ssts)) {
- case 1:
- speed = "1.5";
- break;
- case 2:
- speed = "3";
- break;
- case 3:
- speed = "6";
- break;
- default:
- PERR("PORT0 failed to get port speed");
- return -1;
- }
- /* check PM state of device */
- if (P0ssts::Ipm::get(p0ssts) != 1) {
- PERR("PORT0 device not in active PM state");
- return -1;
- }
- if (VERBOSE) printf("PORT0 connected, ATA device, %s Gbps\n", speed);
-
- if (p0_identify_device()) return -1;
- if (p0_hides_blocks()) {
- PERR("ATA0 drive hides blocks via HPA");
- return -1;
- }
-
- /*
- * FIXME
- * At this point Linux normally reads out the parameters of the
- * SATA DevSlp feature but the values are used only when it comes
- * to LPM wich wasn't needed at all in our use cases. Look for
- * 'ata_dev_configure' and 'ATA_LOG_DEVSLP_*' in Linux if you want
- * to add this feature.
- */
-
- if (p0_transfer_mode(UDMA_133)) return -1;
-
- if (p0_clear_errors()) {
- PERR("ATA0 errors after initialization");
- return -1;
- }
- delayer()->usleep(10000);
- return 0;
- }
-
- /**
- * Do a NCQ command, wait until it is finished, and end it
- *
- * \param lba Logical block address of first block.
- * Holds current error LBA if call returns 1.
- * \param cnt blocks to transfer
- * \param phys physical adress of receive/send buffer DMA
- * \param w 1: write 0: read
- *
- * \retval 0 command finished without errors
- * \retval 1 port has been recovered from errors, lba denotes error point
- * \retval -1 errors occured and port couln't be recovered
- */
- int ncq_command(uint64_t & lba, size_t cnt, addr_t phys, bool w)
- {
- /* set up command table entry */
- unsigned tag = 0;
- Fis * fis = (Fis *)(ct_virt + tag * CMD_TABLE_SIZE);
- fis->fpdma_queued(w, lba, cnt, tag);
-
- /* set up scatter/gather list */
- addr_t prd_list = ct_virt + tag * CMD_TABLE_SIZE + CMD_TABLE_HEAD_SIZE;
- uint8_t prdtl = 0;
- if (write_prd_list(prd_list, phys, cnt, prdtl)) {
- PERR("failed to set up scatter/gather list");
- return -1;
- }
- /* set up command list entry */
- addr_t cmd_slot = cl_virt + tag * CMD_SLOT_SIZE;
- addr_t cmd_table = ct_phys + tag * CMD_TABLE_SIZE;
- write_cmd_slot(cmd_slot, cmd_table, w, 0, 0, prdtl);
-
- /* issue command and wait for completion */
- write(1 << tag);
- write(1 << tag);
- p0_irq_rec.wait_for_signal();
-
- /* get port back ready and deteremine command state */
- int ret = p0_handle_irqs(lba);
- if (ret >= 0) {
- P0sntf::access_t pmn = read();
- if (pmn) {
- write(pmn);
- PERR("ATA0 PM notification after NCQ command");
- return -1;
- }
- if (read()) {
- PERR("ATA0 outstanding commands after NCQ command");
- return -1;
- }
- write(1);
- p0_irq.ack_irq();
- }
- return ret;
- }
-
- /**
- * Try debouncing, if it fails lower settings one by one till it succeeds
- *
- * \retval 0 debouncing succeeded with settings stored in member vars
- * \retval -1 failed to do successful debouncing
- */
- int p0_dynamic_debounce()
- {
- unsigned const initial_p0_speed = p0_speed;
-
- /* try debouncing with presettings first */
- while (p0_debounce(dbc_trials, dbc_trial_us, dbc_stable_trials))
- {
- /* recover from debouncing error */
- p0_clear_errors();
- delayer()->usleep(10000);
- if (read()) {
- p0_clear_irqs();
- write(read());
- }
- p0_clear_errors();
- if (read()) {
- PERR("PORT0 failed to recover from debouncing error %x", read());
- return -1;
- }
-
- /*
- * FIXME
- * Linux cleared D2H FIS again at this point but it seemed not
- * to be necessary as all works fine without.
- */
-
- /* try to lower settings and retry debouncing */
- if (dbc_trial_us == SLOW_DBC_TRIAL_US && p0_speed == 1) {
- PERR("PORT0 debouncing failed with lowest settings");
- return -1;
- } else if (p0_speed != 1) {
- /*
- * If no speed limit is set, go to the most generous limit,
- * otherwise choose the next harder limit.
- */
- if (VERBOSE) printf("PORT0 lower port speed\n");
- if (p0_speed == 0) p0_speed = 3;
- else p0_speed--;
- if (p0_hard_reset(1, p0_speed)) return -1;
- } else {
- /*
- * Reset port speed and redo dynamic debouncing
- * but do it more gently this time.
- */
- if (VERBOSE) printf("PORT0 retry debouncing more gently\n");
- dbc_trial_us = SLOW_DBC_TRIAL_US;
- p0_speed = initial_p0_speed;
- if (p0_hard_reset(1, p0_speed)) return -1;
- }
- }
- p0_clear_errors();
- return 0;
- }
-
- /**
- * Rescue port 0 from an error that occured after port initialization
- *
- * \retval 0 call was successful
- * \retval <0 call failed, error code
- *
- * FIXME
- * This function is merely a trimmed version of 'p0_init' to keep
- * implementation costs low. Implement specialized methods to speed up
- * recovery.
- */
- int p0_error_recovery()
- {
- /* disable command processing and FIS reception */
- p0_disable_cmd_processing();
- write(0);
- if (!wait_for