1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <threads.h>
6 
7 #include <ddk/protocol/platform/device.h>
8 #include <ddk/protocol/platform-device-lib.h>
9 #include <ddk/protocol/serialimpl.h>
10 #include <ddk/protocol/serial.h>
11 #include <ddktl/device.h>
12 #include <ddktl/mmio.h>
13 #include <ddktl/protocol/serialimpl.h>
14 
15 #include <fbl/function.h>
16 #include <fbl/mutex.h>
17 #include <lib/zx/interrupt.h>
18 #include <zircon/thread_annotations.h>
19 #include <zircon/types.h>
20 
21 #include <utility>
22 
23 namespace serial {
24 
25 class AmlUart;
26 using DeviceType = ddk::Device<AmlUart, ddk::Unbindable>;
27 
28 class AmlUart : public DeviceType,
29                 public ddk::SerialImplProtocol<AmlUart, ddk::base_protocol> {
30 public:
31     // Spawns device node.
32     static zx_status_t Create(zx_device_t* parent);
33 
34     // Device protocol implementation.
DdkUnbind()35     void DdkUnbind() {
36         DdkRemove();
37     }
DdkRelease()38     void DdkRelease() {
39         SerialImplEnable(false);
40         delete this;
41     }
42 
43     // Serial protocol implementation.
44     zx_status_t SerialImplGetInfo(serial_port_info_t* info);
45     zx_status_t SerialImplConfig(uint32_t baud_rate, uint32_t flags);
46     zx_status_t SerialImplEnable(bool enable);
47     zx_status_t SerialImplRead(void* buf, size_t length, size_t* out_actual);
48     zx_status_t SerialImplWrite(const void* buf, size_t length, size_t* out_actual);
49     zx_status_t SerialImplSetNotifyCallback(const serial_notify_t* cb);
50 
51 private:
52     using Callback = fbl::Function<void(uint32_t)>;
53 
AmlUart(zx_device_t * parent,const pdev_protocol_t & pdev,const serial_port_info_t & serial_port_info,ddk::MmioBuffer mmio)54     explicit AmlUart(zx_device_t* parent, const pdev_protocol_t& pdev,
55                      const serial_port_info_t& serial_port_info,
56                      ddk::MmioBuffer mmio)
57         : DeviceType(parent), pdev_(pdev), serial_port_info_(serial_port_info),
58           mmio_(std::move(mmio)) {}
59 
60     // Reads the current state from the status register and calls notify_cb if it has changed.
61     uint32_t ReadStateAndNotify();
62     void EnableLocked(bool enable) TA_REQ(enable_lock_);
63     int IrqThread();
64 
65     const pdev_protocol_t pdev_;
66     const serial_port_info_t serial_port_info_;
67     ddk::MmioBuffer mmio_;
68     zx::interrupt irq_;
69 
70     thrd_t irq_thread_ TA_GUARDED(enable_lock_);
71     bool enabled_ TA_GUARDED(enable_lock_) = false;
72 
73     Callback notify_cb_ TA_GUARDED(status_lock_) = nullptr;
74     // Last state we sent to notify_cb.
75     uint32_t state_ TA_GUARDED(status_lock_) = 0;
76 
77     // Protects enabling/disabling lifecycle.
78     fbl::Mutex enable_lock_;
79     // Protects status register and notify_cb.
80     fbl::Mutex status_lock_;
81 };
82 
83 } // namespace serial
84