blob: 1222334e691861194db2cbc8c1c47777b59537bd [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/sync/completion.h>
#include <lib/zx/time.h>
#include <zircon/types.h>
#include <memory>
#include <fbl/condition_variable.h>
#include <fbl/mutex.h>
#include "ahci.h"
#include "bus.h"
#include "port.h"
namespace ahci {
struct ThreadWrapper {
thrd_t thread;
bool created = false;
~ThreadWrapper() { ZX_DEBUG_ASSERT(created == false); }
zx_status_t CreateWithName(thrd_start_t entry, void* arg, const char* name) {
ZX_DEBUG_ASSERT(created == false);
if (thrd_create_with_name(&thread, entry, arg, name) == thrd_success) {
created = true;
return ZX_OK;
void Join() {
if (!created)
thrd_join(thread, nullptr);
created = false;
class Controller {
Controller() {}
// Create a new AHCI Controller.
static zx_status_t Create(zx_device_t* parent, std::unique_ptr<Controller>* con_out);
// Test function: Create a new Controller with a caller-provided host bus interface.
static zx_status_t CreateWithBus(zx_device_t* parent, std::unique_ptr<Bus> bus,
std::unique_ptr<Controller>* con_out);
// Release call for device protocol. Calls Shutdown and deletes this Controller.
static void Release(void* ctx);
// Read or write a 32-bit AHCI controller reg. Endinaness is corrected.
uint32_t RegRead(size_t offset);
zx_status_t RegWrite(size_t offset, uint32_t val);
static int WorkerThread(void* arg) { return static_cast<Controller*>(arg)->WorkerLoop(); }
static int IrqThread(void* arg) { return static_cast<Controller*>(arg)->IrqLoop(); }
static int InitThread(void* arg) { return static_cast<Controller*>(arg)->InitScan(); }
// Create worker, irq, and watchdog threads.
zx_status_t LaunchThreads();
// Release all resources.
// Not used in DDK lifecycle where Release() is called.
void Shutdown() __TA_EXCLUDES(lock_);
zx_status_t HbaReset();
void AhciEnable();
zx_status_t SetDevInfo(uint32_t portnr, sata_devinfo_t* devinfo);
void Queue(uint32_t portnr, sata_txn_t* txn);
void SignalWorker() { sync_completion_signal(&worker_completion_); }
Bus* bus() { return bus_.get(); }
zx_device_t** zxdev_ptr() { return &zxdev_; }
int WorkerLoop();
int WatchdogLoop();
int IrqLoop();
int InitScan();
bool ShouldExit() __TA_EXCLUDES(lock_);
zx_device_t* zxdev_ = nullptr;
uint32_t cap_ = 0;
fbl::Mutex lock_;
bool threads_should_exit_ __TA_GUARDED(lock_) = false;
ThreadWrapper irq_thread_;
ThreadWrapper worker_thread_;
sync_completion_t worker_completion_;
std::unique_ptr<Bus> bus_;
Port ports_[AHCI_MAX_PORTS];
} // namespace ahci