blob: 6f526ae0ad0ce51f156a633bbfe67004fa3d6a78 [file] [log] [blame]
// Copyright 2024 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_HRTIMER_DRIVERS_AML_HRTIMER_AML_HRTIMER_SERVER_H_
#define SRC_DEVICES_HRTIMER_DRIVERS_AML_HRTIMER_AML_HRTIMER_SERVER_H_
#include <fidl/fuchsia.hardware.hrtimer/cpp/fidl.h>
#include <fidl/fuchsia.power.broker/cpp/fidl.h>
#include <fidl/fuchsia.power.system/cpp/fidl.h>
#include <lib/async/cpp/irq.h>
#include <lib/driver/mmio/cpp/mmio-buffer.h>
#include <lib/fit/result.h>
#include <lib/inspect/cpp/inspect.h>
#include <lib/inspect/cpp/inspector.h>
#include <lib/zx/interrupt.h>
#include <optional>
#include <variant>
#include "lib/inspect/component/cpp/component.h"
namespace hrtimer {
constexpr size_t kTimersAll[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
constexpr size_t kTimersSupportWait[] = {0, 1, 2, 3, 5, 6, 7, 8};
static constexpr size_t kNumberOfTimers = std::size(kTimersAll);
class AmlHrtimerServer : public fidl::Server<fuchsia_hardware_hrtimer::Device> {
public:
// Cast is needed because PowerLevel and BinaryPowerLevel are distinct types.
static const fuchsia_power_broker::PowerLevel kWakeHandlingLeaseOn =
static_cast<fuchsia_power_broker::PowerLevel>(fuchsia_power_broker::BinaryPowerLevel::kOn);
AmlHrtimerServer(async_dispatcher_t* dispatcher, fdf::MmioBuffer mmio,
std::optional<fidl::SyncClient<fuchsia_power_system::ActivityGovernor>> sag,
zx::interrupt irq_a, zx::interrupt irq_b, zx::interrupt irq_c,
zx::interrupt irq_d, zx::interrupt irq_f, zx::interrupt irq_g,
zx::interrupt irq_h, zx::interrupt irq_i, inspect::ComponentInspector& inspect);
void ShutDown();
// For unit testing.
static size_t GetNumberOfTimers() { return kNumberOfTimers; }
bool HasWaitCompleter(size_t timer_index) {
ZX_ASSERT(timer_index < kNumberOfTimers);
return !std::holds_alternative<std::monostate>(
timers_[timer_index].power_enabled_wait_completer);
}
bool StartTicksLeftFitInHardware(size_t timer_index) {
ZX_ASSERT(timer_index < kNumberOfTimers);
// This unit testing method is only meant to be used when extending max ticks.
ZX_ASSERT(timers_[timer_index].properties.extend_max_ticks);
return timers_[timer_index].start_ticks_left <= std::numeric_limits<uint16_t>::max();
}
protected:
// FIDL natural C++ methods for fuchsia.hardware.hrtimer.
void Start(StartRequest& request, StartCompleter::Sync& completer) override;
void Stop(StopRequest& request, StopCompleter::Sync& completer) override;
void ReadTimer(ReadTimerRequest& request, ReadTimerCompleter::Sync& completer) override;
void ReadClock(ReadClockRequest& request, ReadClockCompleter::Sync& completer) override;
void GetTicksLeft(GetTicksLeftRequest& request, GetTicksLeftCompleter::Sync& completer) override;
void SetEvent(SetEventRequest& request, SetEventCompleter::Sync& completer) override;
void StartAndWait(StartAndWaitRequest& request, StartAndWaitCompleter::Sync& completer) override;
void StartAndWait2(StartAndWait2Request& request,
StartAndWait2Completer::Sync& completer) override;
void GetProperties(GetPropertiesCompleter::Sync& completer) override;
void handle_unknown_method(fidl::UnknownMethodMetadata<fuchsia_hardware_hrtimer::Device> metadata,
fidl::UnknownMethodCompleter::Sync& completer) override;
private:
static constexpr size_t kMaxInspectEvents = 256; // Arbitrary.
enum class MaxTicks : uint8_t {
k16Bit,
k64Bit,
};
enum class EventType : uint8_t {
None,
Start,
StartAndWait,
StartAndWait2,
StartHardware,
RetriggerIrq,
TriggerIrqWait,
TriggerIrqWait2,
TriggerIrq,
Stop,
StopWait,
StopWait2,
};
struct TimersProperties {
uint64_t id;
bool supports_notifications;
bool supports_system_clock;
bool supports_1usec;
bool supports_10usecs;
bool supports_100usecs;
bool supports_1msec;
MaxTicks max_ticks_support;
bool always_on_domain;
bool watchdog;
bool extend_max_ticks;
};
struct Timer {
Timer(AmlHrtimerServer& server, TimersProperties& props);
void HandleIrq(async_dispatcher_t* dispatcher, async::IrqBase* irq, zx_status_t status,
const zx_packet_interrupt_t* interrupt);
AmlHrtimerServer& parent;
TimersProperties& properties;
uint64_t resolution_nsecs;
std::optional<zx::event> event;
zx::interrupt irq;
async::IrqMethod<Timer, &Timer::HandleIrq> irq_handler{this};
// Completer saved to reply to a StartAndWait power aware FIDL call.
std::variant<std::monostate, StartAndWaitCompleter::Async, StartAndWait2Completer::Async>
power_enabled_wait_completer;
uint64_t start_ticks_left = 0;
uint64_t last_ticks = 0;
};
static size_t TimerIndexFromId(uint64_t id) { return id; }
inspect::UintProperty& IrqEntries() { return irq_entries_; }
inspect::UintProperty& IrqExits() { return irq_exits_; }
bool IsTimerStarted(size_t id);
fit::result<const fuchsia_hardware_hrtimer::DriverError> StartHardware(size_t timer_index);
void RecordEvent(int64_t now, uint64_t id, EventType type, uint64_t data);
TimersProperties timers_properties_[kNumberOfTimers] = {
// clang-format off
// id| notif|system| 1us| 10us| 100us| 1ms| max ticks| AOdom| WDT|extend|
{ 0, true, false, true, true, true, true, MaxTicks::k16Bit, false, false, false}, // A.
{ 1, true, false, true, true, true, true, MaxTicks::k16Bit, false, false, false}, // B.
{ 2, true, false, true, true, true, true, MaxTicks::k16Bit, false, false, false}, // C.
{ 3, true, false, true, true, true, true, MaxTicks::k16Bit, false, false, false}, // D.
{ 4, false, true, true, true, true, false, MaxTicks::k64Bit, false, false, false}, // E.
{ 5, true, false, true, true, true, true, MaxTicks::k16Bit, false, false, true }, // F.
{ 6, true, false, true, true, true, true, MaxTicks::k16Bit, false, false, true }, // G.
{ 7, true, false, true, true, true, true, MaxTicks::k16Bit, false, false, true }, // H.
{ 8, true, false, true, true, true, true, MaxTicks::k16Bit, false, false, true }, // I.
// The timers below are available in the hardware but not supported by this driver.
// Timer id 9 is a WDT 24MHz.
// { 9, true, false, false, false, false, false, MaxTicks::k16Bit,false, true , false},
// { 10, true, false, true, true, true, false, MaxTicks::k16Bit, true, false, false}, // AO_A.
// { 11, true, false, true, true, true, false, MaxTicks::k16Bit, true, false, false}, // AO_B.
// { 12, false, false, true, true, true, false, MaxTicks::k16Bit, true, false, false}, // AO_C.
// // There is no AO_D.
// { 13, false, true, false, false, false, false, MaxTicks::k64Bit, true, false, false}, // AO_E.
// { 14, false, true, false, false, false, false, MaxTicks::k64Bit, true, false, false}, // AO_F.
// { 15, false, true, false, false, false, false, MaxTicks::k64Bit, true, false, false}, // AO_G.
// Timer id 16 is a AO_WDT.
// { 16, true, true, false, false, false, false, MaxTicks::k16Bit, true, true , false},
// clang-format on
};
std::array<Timer, kNumberOfTimers> timers_ = {
Timer(*this, timers_properties_[0]), Timer(*this, timers_properties_[1]),
Timer(*this, timers_properties_[2]), Timer(*this, timers_properties_[3]),
Timer(*this, timers_properties_[4]), Timer(*this, timers_properties_[5]),
Timer(*this, timers_properties_[6]), Timer(*this, timers_properties_[7]),
Timer(*this, timers_properties_[8])};
std::optional<fdf::MmioBuffer> mmio_;
// FIDL client used to request wake leases directly from SAG.
std::optional<fidl::SyncClient<fuchsia_power_system::ActivityGovernor>> sag_;
async_dispatcher_t* dispatcher_;
inspect::Node inspect_node_;
struct Event {
int64_t timestamp;
uint64_t id;
uint64_t data;
EventType type;
} events_[kMaxInspectEvents] = {};
size_t event_index_ = 0;
// TODO(b/369886005): These inspect properties exist to help diagnose b/369886005
// and can probably be safely removed once that bug is resolved.
inspect::UintProperty lease_requests_;
inspect::UintProperty lease_replies_;
inspect::UintProperty update_requests_;
inspect::UintProperty update_replies_;
inspect::UintProperty irq_entries_;
inspect::UintProperty irq_exits_;
};
} // namespace hrtimer
#endif // SRC_DEVICES_HRTIMER_DRIVERS_AML_HRTIMER_AML_HRTIMER_SERVER_H_