1010#include < lib/driver/compat/cpp/logging.h> // nogncheck
1111#endif
1212
13+ #include < lib/zx/clock.h>
1314#include < threads.h>
1415#include < zircon/syscalls-next.h>
1516#include < zircon/threads.h>
@@ -48,19 +49,88 @@ fit::closure DriverTransportWriteOperation::MakeCallback(zx_status_t status) {
4849
4950} // namespace internal
5051
51- AmlUart::AmlUart (ddk::PDevFidl pdev,
52- const fuchsia_hardware_serial::wire::SerialPortInfo& serial_port_info,
53- fdf::MmioBuffer mmio, fdf::UnownedSynchronizedDispatcher irq_dispatcher,
54- std::optional<fidl::ClientEnd<fuchsia_power_broker::Lessor>> lessor_client_end)
52+ AmlUart::AmlUart (
53+ ddk::PDevFidl pdev, const fuchsia_hardware_serial::wire::SerialPortInfo& serial_port_info,
54+ fdf::MmioBuffer mmio, fdf::UnownedSynchronizedDispatcher irq_dispatcher,
55+ std::optional<fdf::UnownedSynchronizedDispatcher> timer_dispatcher,
56+ std::optional<fidl::ClientEnd<fuchsia_power_broker::Lessor>> lessor_client_end,
57+ std::optional<fidl::ClientEnd<fuchsia_power_broker::CurrentLevel>> current_level_client_end,
58+ std::optional<fidl::ClientEnd<fuchsia_power_broker::RequiredLevel>> required_level_client_end,
59+ std::optional<fidl::ClientEnd<fuchsia_power_broker::ElementControl>> element_control_client_end)
5560 : pdev_(std::move(pdev)),
5661 serial_port_info_ (serial_port_info),
5762 mmio_(std::move(mmio)),
5863 irq_dispatcher_(std::move(irq_dispatcher)) {
64+ if (timer_dispatcher != std::nullopt ) {
65+ timer_dispatcher_ = std::move (*timer_dispatcher);
66+ // Use ZX_TIMER_SLACK_LATE so that the timer will never fire earlier than the deadline.
67+ zx::timer::create (ZX_TIMER_SLACK_LATE, ZX_CLOCK_MONOTONIC, &lease_timer_);
68+ timer_waiter_.set_object (lease_timer_.get ());
69+ timer_waiter_.set_trigger (ZX_TIMER_SIGNALED);
70+ }
71+
5972 if (lessor_client_end != std::nullopt ) {
6073 lessor_client_.Bind (std::move (*lessor_client_end));
6174 } else {
6275 zxlogf (INFO, " No lessor client end gets passed into AmlUart during initialization." );
6376 }
77+
78+ if (required_level_client_end != std::nullopt ) {
79+ required_level_client_.Bind (std::move (*required_level_client_end),
80+ fdf::Dispatcher::GetCurrent ()->async_dispatcher ());
81+ } else {
82+ zxlogf (INFO, " No required level client end gets passed into AmlUart during initialization." );
83+ }
84+
85+ if (current_level_client_end != std::nullopt ) {
86+ current_level_client_.Bind (std::move (*current_level_client_end));
87+ } else {
88+ zxlogf (INFO, " No current level client end gets passed into AmlUart during initialization." );
89+ }
90+
91+ if (element_control_client_end != std::nullopt ) {
92+ element_control_client_end_ = std::move (element_control_client_end);
93+ WatchRequiredLevel ();
94+ } else {
95+ zxlogf (INFO, " No element control client end gets passed into AmlUart during initialization." );
96+ }
97+ }
98+
99+ // Keep watching the required level, since the driver doesn't toggle the hardware power state
100+ // according to power element levels, report the current level to power broker immediately with the
101+ // required level value received from power broker.
102+ void AmlUart::WatchRequiredLevel () {
103+ if (!required_level_client_.is_valid ()) {
104+ zxlogf (ERROR, " RequiredLevel not valid, can't monitor it" );
105+ element_control_client_end_.reset ();
106+ return ;
107+ }
108+ required_level_client_->Watch ().Then (
109+ [this ](fidl::Result<fuchsia_power_broker::RequiredLevel::Watch>& result) {
110+ if (result.is_error ()) {
111+ if (result.error_value ().is_framework_error () &&
112+ result.error_value ().framework_error ().status () != ZX_ERR_CANCELED) {
113+ zxlogf (ERROR, " Power level required call failed: %s. Stop monitoring required level" ,
114+ result.error_value ().FormatDescription ().c_str ());
115+ }
116+ element_control_client_end_.reset ();
117+ return ;
118+ }
119+ zxlogf (DEBUG, " RequiredLevel : %u" , static_cast <uint8_t >(result->required_level ()));
120+ if (!current_level_client_.is_valid ()) {
121+ zxlogf (ERROR, " CurrentLevel not valid. Stop monitoring required level" );
122+ element_control_client_end_.reset ();
123+ return ;
124+ }
125+ auto update_result = current_level_client_->Update (result->required_level ());
126+ if (update_result.is_error ()) {
127+ zxlogf (ERROR, " CurrentLevel call failed: %s. Stop monitoring required level" ,
128+ update_result.error_value ().FormatDescription ().c_str ());
129+ element_control_client_end_.reset ();
130+ return ;
131+ }
132+ WatchRequiredLevel ();
133+ });
64134}
65135
66136constexpr auto kMinBaudRate = 2 ;
@@ -200,6 +270,10 @@ void AmlUart::EnableLocked(bool enable) {
200270 }
201271}
202272
273+ fidl::ClientEnd<fuchsia_power_broker::ElementControl>& AmlUart::element_control_for_testing () {
274+ return *element_control_client_end_;
275+ }
276+
203277void AmlUart::HandleTXRaceForTest () {
204278 {
205279 fbl::AutoLock al (&enable_lock_);
@@ -220,6 +294,12 @@ void AmlUart::HandleRXRaceForTest() {
220294 HandleRX ();
221295}
222296
297+ void AmlUart::InjectTimerForTest (zx_handle_t handle) {
298+ lease_timer_.reset (handle);
299+ timer_waiter_.set_object (lease_timer_.get ());
300+ timer_waiter_.set_trigger (ZX_TIMER_SIGNALED);
301+ }
302+
223303zx_status_t AmlUart::Enable (bool enable) {
224304 fbl::AutoLock al (&enable_lock_);
225305
@@ -388,11 +468,60 @@ void AmlUart::handle_unknown_method(
388468 zxlogf (WARNING, " handle_unknown_method in fuchsia_hardware_serialimpl::Device server." );
389469}
390470
471+ zx::result<fidl::ClientEnd<fuchsia_power_broker::LeaseControl>> AmlUart::AcquireLease () {
472+ auto lease_result = lessor_client_->Lease (kPowerLevelHandling );
473+ if (lease_result.is_error ()) {
474+ zxlogf (ERROR, " Failed to aquire lease: %s" ,
475+ lease_result.error_value ().FormatDescription ().c_str ());
476+ return zx::error (ZX_ERR_BAD_STATE);
477+ }
478+ if (!lease_result->lease_control ().is_valid ()) {
479+ zxlogf (ERROR, " Lease returned invalid lease control client end." );
480+ return zx::error (ZX_ERR_BAD_STATE);
481+ }
482+ // Wait until the lease is actually granted.
483+ fidl::SyncClient<fuchsia_power_broker::LeaseControl> lease_control_client (
484+ std::move (lease_result->lease_control ()));
485+ fuchsia_power_broker::LeaseStatus current_lease_status =
486+ fuchsia_power_broker::LeaseStatus::kUnknown ;
487+ do {
488+ auto watch_result = lease_control_client->WatchStatus (current_lease_status);
489+ if (watch_result.is_error ()) {
490+ zxlogf (ERROR, " WatchStatus returned error: %s" ,
491+ watch_result.error_value ().FormatDescription ().c_str ());
492+ return zx::error (ZX_ERR_BAD_STATE);
493+ }
494+ current_lease_status = watch_result->status ();
495+ } while (current_lease_status != fuchsia_power_broker::LeaseStatus::kSatisfied );
496+ return zx::ok (lease_control_client.TakeClientEnd ());
497+ }
498+
391499void AmlUart::HandleIrq (async_dispatcher_t * dispatcher, async::IrqBase* irq, zx_status_t status,
392500 const zx_packet_interrupt_t * interrupt) {
393501 if (status != ZX_OK) {
394502 return ;
395503 }
504+ // If the element control client end is not available, it means that power management is not
505+ // enabled for this driver, the driver will not take a wake lease and set timer in this case.
506+ if (element_control_client_end_) {
507+ fbl::AutoLock lock (&timer_lock_);
508+
509+ if (lease_control_client_end_.is_valid ()) {
510+ // Cancel the timer and set it again if the last one hasn't expired.
511+ lease_timer_.cancel ();
512+ } else {
513+ // Take the wake lease and keep the lease control client.
514+ auto lease_control_result = AcquireLease ();
515+ if (lease_control_result.is_error ()) {
516+ return ;
517+ }
518+ lease_control_client_end_ = std::move (*lease_control_result);
519+ }
520+
521+ timeout_ = zx::deadline_after (zx::msec (kPowerLeaseTimeoutMs ));
522+ timer_waiter_.Begin (timer_dispatcher_->async_dispatcher ());
523+ lease_timer_.set (timeout_, zx::duration ());
524+ }
396525
397526 auto uart_status = Status::Get ().ReadFrom (&mmio_);
398527 if (!uart_status.rx_empty ()) {
@@ -405,4 +534,19 @@ void AmlUart::HandleIrq(async_dispatcher_t* dispatcher, async::IrqBase* irq, zx_
405534 irq_.ack ();
406535}
407536
537+ void AmlUart::HandleLeaseTimer (async_dispatcher_t * dispatcher, async::WaitBase* wait,
538+ zx_status_t status, const zx_packet_signal_t * signal) {
539+ fbl::AutoLock lock (&timer_lock_);
540+ if (status == ZX_ERR_CANCELED) {
541+ // Do nothing if the handler is triggered by the destroy of the dispatcher.
542+ return ;
543+ }
544+ if (zx::clock::get_monotonic () < timeout_) {
545+ // If the current time is earlier than timeout, it means that this handler is triggered after
546+ // |HandleIrq| holds the lock, and when this handler get the lock, the timer has been reset, so
547+ // don't drop the lease in this case.
548+ return ;
549+ }
550+ lease_control_client_end_.reset ();
551+ }
408552} // namespace serial
0 commit comments