blob: 314b55cf2aeeed4f51dcccaca0a69702bd8b78d1 [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 <fidl/fuchsia.hardware.registers/cpp/wire.h>
#include <fuchsia/hardware/registers/cpp/banjo.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/ddk/platform-defs.h>
#include <lib/mmio/mmio.h>
#include <zircon/types.h>
#include <map>
#include <optional>
#include <vector>
#include <ddktl/device.h>
#include <ddktl/fidl.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<fuchsia_hardware_registers::Device>::Mixin,
ddk::Unbindable>;
template <typename T>
class RegistersDevice;
template <typename T>
using RegistersDeviceType = ddk::Device<RegistersDevice<T>>;
struct MmioInfo {
fdf::MmioBuffer mmio;
std::vector<fbl::Mutex> locks;
};
using fuchsia_hardware_registers::wire::Metadata;
using fuchsia_hardware_registers::wire::RegistersMetadataEntry;
template <typename T>
class Register : public RegisterType<T>,
public ddk::RegistersProtocol<Register<T>, ddk::base_protocol>,
public fbl::RefCounted<Register<T>> {
using Device = fidl::WireServer<fuchsia_hardware_registers::Device>;
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);
void DdkUnbind(ddk::UnbindTxn txn) {
loop_.Shutdown();
txn.Reply();
}
void DdkRelease() { delete this; }
void RegistersConnect(zx::channel chan);
void ReadRegister8(Device::ReadRegister8RequestView request,
Device::ReadRegister8Completer::Sync& completer) override {
ReadRegister(request->offset, request->mask, completer);
}
void ReadRegister16(Device::ReadRegister16RequestView request,
Device::ReadRegister16Completer::Sync& completer) override {
ReadRegister(request->offset, request->mask, completer);
}
void ReadRegister32(Device::ReadRegister32RequestView request,
Device::ReadRegister32Completer::Sync& completer) override {
ReadRegister(request->offset, request->mask, completer);
}
void ReadRegister64(Device::ReadRegister64RequestView request,
Device::ReadRegister64Completer::Sync& completer) override {
ReadRegister(request->offset, request->mask, completer);
}
void WriteRegister8(Device::WriteRegister8RequestView request,
Device::WriteRegister8Completer::Sync& completer) override {
WriteRegister(request->offset, request->mask, request->value, completer);
}
void WriteRegister16(Device::WriteRegister16RequestView request,
Device::WriteRegister16Completer::Sync& completer) override {
WriteRegister(request->offset, request->mask, request->value, completer);
}
void WriteRegister32(Device::WriteRegister32RequestView request,
Device::WriteRegister32Completer::Sync& completer) override {
WriteRegister(request->offset, request->mask, request->value, completer);
}
void WriteRegister64(Device::WriteRegister64RequestView request,
Device::WriteRegister64Completer::Sync& completer) override {
WriteRegister(request->offset, request->mask, request->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 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
};
extern template class Register<uint8_t>;
extern template class Register<uint16_t>;
extern template class Register<uint32_t>;
extern template class Register<uint64_t>;
} // namespace registers
#endif // SRC_DEVICES_REGISTERS_DRIVERS_REGISTERS_REGISTERS_H_