blob: 241882625e2f50a771554282e50bde1ec004cafa [file] [log] [blame]
// Copyright 2020 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.
#ifndef SRC_DEVICES_REGISTERS_DRIVERS_REGISTERS_REGISTERS_H_
#define SRC_DEVICES_REGISTERS_DRIVERS_REGISTERS_REGISTERS_H_
#include <fuchsia/hardware/registers/llcpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/mmio/mmio.h>
#include <zircon/types.h>
#include <map>
#include <optional>
#include <vector>
#include <ddk/platform-defs.h>
#include <ddktl/device.h>
#include <ddktl/fidl.h>
#include <ddktl/protocol/registers.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
#include <fbl/ref_counted.h>
namespace registers {
template <typename T>
class Register;
template <typename T>
using RegisterType = ddk::Device<Register<T>, ddk::Messageable, ddk::Unbindable>;
template <typename T>
class RegistersDevice;
template <typename T>
using RegistersDeviceType = ddk::Device<RegistersDevice<T>, ddk::Unbindable>;
struct MmioInfo {
ddk::MmioBuffer mmio;
std::vector<fbl::Mutex> locks;
};
using ::llcpp::fuchsia::hardware::registers::Metadata;
using ::llcpp::fuchsia::hardware::registers::RegistersMetadataEntry;
template <typename T>
class Register : public ::llcpp::fuchsia::hardware::registers::Device::Interface,
public RegisterType<T>,
public ddk::RegistersProtocol<Register<T>, ddk::base_protocol>,
public fbl::RefCounted<Register<T>> {
public:
explicit Register(zx_device_t* device, std::shared_ptr<MmioInfo> mmio)
: RegisterType<T>(device), mmio_(mmio), loop_(&kAsyncLoopConfigNoAttachToCurrentThread) {}
zx_status_t Init(const RegistersMetadataEntry& config);
zx_status_t DdkMessage(fidl_incoming_msg_t* msg, fidl_txn_t* txn) {
DdkTransaction transaction(txn);
::llcpp::fuchsia::hardware::registers::Device::Dispatch(this, msg, &transaction);
return transaction.Status();
}
void DdkUnbind(ddk::UnbindTxn txn) {
loop_.Shutdown();
txn.Reply();
}
void DdkRelease() { delete this; }
void RegistersConnect(zx::channel chan);
void ReadRegister8(uint64_t offset, uint8_t mask, ReadRegister8Completer::Sync& completer) {
ReadRegister(offset, mask, completer);
}
void ReadRegister16(uint64_t offset, uint16_t mask, ReadRegister16Completer::Sync& completer) {
ReadRegister(offset, mask, completer);
}
void ReadRegister32(uint64_t offset, uint32_t mask, ReadRegister32Completer::Sync& completer) {
ReadRegister(offset, mask, completer);
}
void ReadRegister64(uint64_t offset, uint64_t mask, ReadRegister64Completer::Sync& completer) {
ReadRegister(offset, mask, completer);
}
void WriteRegister8(uint64_t offset, uint8_t mask, uint8_t value,
WriteRegister8Completer::Sync& completer) {
WriteRegister(offset, mask, value, completer);
}
void WriteRegister16(uint64_t offset, uint16_t mask, uint16_t value,
WriteRegister16Completer::Sync& completer) {
WriteRegister(offset, mask, value, completer);
}
void WriteRegister32(uint64_t offset, uint32_t mask, uint32_t value,
WriteRegister32Completer::Sync& completer) {
WriteRegister(offset, mask, value, completer);
}
void WriteRegister64(uint64_t offset, uint64_t mask, uint64_t value,
WriteRegister64Completer::Sync& completer) {
WriteRegister(offset, mask, value, completer);
}
private:
template <typename Ty, typename Completer>
void ReadRegister(uint64_t offset, Ty mask, Completer& completer) {
if constexpr (!std::is_same_v<T, Ty>) {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
return;
}
T val;
// Need cast to compile
auto status = ReadRegister(offset, static_cast<T>(mask), &val);
if (status == ZX_OK) {
// Need cast to compile
completer.ReplySuccess(static_cast<Ty>(val));
} else {
completer.ReplyError(status);
}
}
template <typename Ty, typename Completer>
void WriteRegister(uint64_t offset, Ty mask, Ty value, Completer& completer) {
if constexpr (!std::is_same_v<T, Ty>) {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
return;
}
// Need cast to compile
auto status = WriteRegister(offset, static_cast<T>(mask), static_cast<T>(value));
if (status == ZX_OK) {
completer.ReplySuccess();
} else {
completer.ReplyError(status);
}
}
zx_status_t ReadRegister(uint64_t offset, T mask, T* out_value);
zx_status_t WriteRegister(uint64_t offset, T mask, T value);
bool VerifyMask(T mask, uint64_t register_offset);
std::shared_ptr<MmioInfo> mmio_;
uint64_t id_;
std::map<uint64_t, std::pair<T, uint32_t>> masks_; // base_address to (mask, reg_count)
async::Loop loop_;
bool loop_started_ = false;
};
template <typename T>
class RegistersDevice : public RegistersDeviceType<T> {
public:
static zx_status_t Create(zx_device_t* parent, Metadata metadata);
void DdkUnbind(ddk::UnbindTxn txn) { txn.Reply(); }
void DdkRelease() { delete this; }
private:
template <typename U>
friend class FakeRegistersDevice;
explicit RegistersDevice(zx_device_t* parent) : RegistersDeviceType<T>(parent) {}
zx_status_t Init(zx_device_t* parent, Metadata metadata);
// For unit tests
zx_status_t Init(std::map<uint32_t, std::shared_ptr<MmioInfo>> mmios) {
mmios_ = std::move(mmios);
return ZX_OK;
}
std::map<uint32_t, std::shared_ptr<MmioInfo>> mmios_; // MMIO ID to MmioInfo
};
} // namespace registers
#endif // SRC_DEVICES_REGISTERS_DRIVERS_REGISTERS_REGISTERS_H_