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 "aml-uart.h"
6 
7 #include <stdint.h>
8 #include <string.h>
9 
10 #include <bits/limits.h>
11 #include <ddk/binding.h>
12 #include <ddk/debug.h>
13 #include <ddk/device.h>
14 #include <ddk/metadata.h>
15 #include <ddk/protocol/platform/bus.h>
16 #include <ddktl/device.h>
17 #include <hw/reg.h>
18 
19 #include <fbl/alloc_checker.h>
20 #include <fbl/auto_call.h>
21 #include <fbl/auto_lock.h>
22 #include <hwreg/mmio.h>
23 #include <zircon/device/serial.h>
24 #include <zircon/threads.h>
25 #include <zircon/types.h>
26 
27 #include "registers.h"
28 
29 namespace serial {
30 
Create(zx_device_t * parent)31 zx_status_t AmlUart::Create(zx_device_t* parent) {
32     zx_status_t status;
33 
34     pdev_protocol_t pdev;
35     if ((status = device_get_protocol(parent, ZX_PROTOCOL_PDEV, &pdev)) != ZX_OK) {
36         zxlogf(ERROR, "%s: ZX_PROTOCOL_PDEV not available\n", __func__);
37         return status;
38     }
39 
40     serial_port_info_t info;
41     size_t actual;
42     status = device_get_metadata(parent, DEVICE_METADATA_SERIAL_PORT_INFO, &info, sizeof(info),
43                                  &actual);
44     if (status != ZX_OK) {
45         zxlogf(ERROR, "%s: device_get_metadata failed %d\n", __func__, status);
46         return status;
47     }
48     if (actual < sizeof(info)) {
49         zxlogf(ERROR, "%s: serial_port_info_t metadata too small\n", __func__);
50         return ZX_ERR_INTERNAL;
51     }
52 
53     mmio_buffer_t mmio;
54     status = pdev_map_mmio_buffer2(&pdev, 0, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio);
55     if (status != ZX_OK) {
56         zxlogf(ERROR, "%s: pdev_map_&mmio__buffer failed %d\n", __func__, status);
57         return status;
58     }
59 
60     fbl::AllocChecker ac;
61     auto* uart = new (&ac) AmlUart(parent, pdev, info, ddk::MmioBuffer(mmio));
62     if (!ac.check()) {
63         return ZX_ERR_NO_MEMORY;
64     }
65 
66     auto cleanup = fbl::MakeAutoCall([&uart]() { uart->DdkRelease(); });
67 
68     // Default configuration for the case that serial_impl_config is not called.
69     constexpr uint32_t kDefaultBaudRate = 115200;
70     constexpr uint32_t kDefaultConfig =
71         SERIAL_DATA_BITS_8 | SERIAL_STOP_BITS_1 | SERIAL_PARITY_NONE;
72     uart->SerialImplConfig(kDefaultBaudRate, kDefaultConfig);
73 
74     status = uart->DdkAdd("aml-uart");
75     if (status != ZX_OK) {
76         zxlogf(ERROR, "%s: DdkDeviceAdd failed\n", __func__);
77         return status;
78     }
79 
80     cleanup.cancel();
81     return ZX_OK;
82 }
83 
ReadStateAndNotify()84 uint32_t AmlUart::ReadStateAndNotify() {
85     fbl::AutoLock al(&status_lock_);
86 
87     auto status = Status::Get().ReadFrom(&mmio_);
88 
89     uint32_t state = 0;
90     if (!status.rx_empty()) {
91         state |= SERIAL_STATE_READABLE;
92     }
93     if (!status.tx_full()) {
94         state |= SERIAL_STATE_WRITABLE;
95     }
96     const bool notify = (state != state_);
97     state_ = state;
98 
99     if (notify && notify_cb_) {
100         notify_cb_(state);
101     }
102 
103     return state;
104 }
105 
IrqThread()106 int AmlUart::IrqThread() {
107     zxlogf(INFO, "%s start\n", __func__);
108 
109     while (1) {
110         zx_status_t status;
111         status = irq_.wait(nullptr);
112         if (status != ZX_OK) {
113             zxlogf(ERROR, "%s: irq.wait() got %d\n", __func__, status);
114             break;
115         }
116         // This will call the notify_cb if the serial state has changed.
117         ReadStateAndNotify();
118     }
119 
120     return 0;
121 }
122 
SerialImplGetInfo(serial_port_info_t * info)123 zx_status_t AmlUart::SerialImplGetInfo(serial_port_info_t* info) {
124     memcpy(info, &serial_port_info_, sizeof(*info));
125     return ZX_OK;
126 }
127 
SerialImplConfig(uint32_t baud_rate,uint32_t flags)128 zx_status_t AmlUart::SerialImplConfig(uint32_t baud_rate, uint32_t flags) {
129     // Control register is determined completely by this logic, so start with a clean slate.
130     auto ctrl = Control::Get().FromValue(0);
131 
132     if ((flags & SERIAL_SET_BAUD_RATE_ONLY) == 0) {
133         switch (flags & SERIAL_DATA_BITS_MASK) {
134         case SERIAL_DATA_BITS_5:
135             ctrl.set_xmit_len(Control::kXmitLength5);
136             break;
137         case SERIAL_DATA_BITS_6:
138             ctrl.set_xmit_len(Control::kXmitLength6);
139             break;
140         case SERIAL_DATA_BITS_7:
141             ctrl.set_xmit_len(Control::kXmitLength7);
142             break;
143         case SERIAL_DATA_BITS_8:
144             ctrl.set_xmit_len(Control::kXmitLength8);
145             break;
146         default:
147             return ZX_ERR_INVALID_ARGS;
148         }
149 
150         switch (flags & SERIAL_STOP_BITS_MASK) {
151         case SERIAL_STOP_BITS_1:
152             ctrl.set_stop_len(Control::kStopLen1);
153             break;
154         case SERIAL_STOP_BITS_2:
155             ctrl.set_stop_len(Control::kStopLen2);
156             break;
157         default:
158             return ZX_ERR_INVALID_ARGS;
159         }
160 
161         switch (flags & SERIAL_PARITY_MASK) {
162         case SERIAL_PARITY_NONE:
163             ctrl.set_parity(Control::kParityNone);
164             break;
165         case SERIAL_PARITY_EVEN:
166             ctrl.set_parity(Control::kParityEven);
167             break;
168         case SERIAL_PARITY_ODD:
169             ctrl.set_parity(Control::kParityOdd);
170             break;
171         default:
172             return ZX_ERR_INVALID_ARGS;
173         }
174 
175         switch (flags & SERIAL_FLOW_CTRL_MASK) {
176         case SERIAL_FLOW_CTRL_NONE:
177             ctrl.set_two_wire(1);
178             break;
179         case SERIAL_FLOW_CTRL_CTS_RTS:
180             // CTS/RTS is on by default
181             break;
182         default:
183             return ZX_ERR_INVALID_ARGS;
184         }
185     }
186 
187     // Configure baud rate based on crystal clock speed.
188     // See meson_uart_change_speed() in drivers/amlogic/uart/uart/meson_uart.c.
189     constexpr uint32_t kCrystalClockSpeed = 24000000;
190     uint32_t baud_bits = (kCrystalClockSpeed / 3) / baud_rate - 1;
191     if (baud_bits & (~AML_UART_REG5_NEW_BAUD_RATE_MASK)) {
192         zxlogf(ERROR, "%s: baud rate %u too large\n", __func__, baud_rate);
193         return ZX_ERR_OUT_OF_RANGE;
194     }
195     auto baud = Reg5::Get()
196                     .FromValue(0)
197                     .set_new_baud_rate(baud_bits)
198                     .set_use_xtal_clk(1)
199                     .set_use_new_baud_rate(1);
200 
201     fbl::AutoLock al(&enable_lock_);
202 
203     if ((flags & SERIAL_SET_BAUD_RATE_ONLY) == 0) {
204         // Invert our RTS if we are we are not enabled and configured for flow control.
205         if (!enabled_ && (ctrl.two_wire() == 0)) {
206             ctrl.set_inv_rts(1);
207         }
208         ctrl.WriteTo(&mmio_);
209     }
210 
211     baud.WriteTo(&mmio_);
212 
213     return ZX_OK;
214 }
215 
EnableLocked(bool enable)216 void AmlUart::EnableLocked(bool enable) {
217     auto ctrl = Control::Get().ReadFrom(&mmio_);
218 
219     if (enable) {
220         // Reset the port.
221         ctrl.set_rst_rx(1)
222             .set_rst_tx(1)
223             .set_clear_error(1)
224             .WriteTo(&mmio_);
225 
226         ctrl.set_rst_rx(0)
227             .set_rst_tx(0)
228             .set_clear_error(0)
229             .WriteTo(&mmio_);
230 
231         // Enable rx and tx.
232         ctrl.set_tx_enable(1)
233             .set_rx_enable(1)
234             .set_tx_interrupt_enable(1)
235             .set_rx_interrupt_enable(1)
236             // Clear our RTS.
237             .set_inv_rts(0)
238             .WriteTo(&mmio_);
239 
240         // Set interrupt thresholds.
241         // Generate interrupt if TX buffer drops below half full.
242         constexpr uint32_t kTransmitIrqCount = 32;
243         // Generate interrupt as soon as we receive any data.
244         constexpr uint32_t kRecieveIrqCount = 1;
245         Misc::Get()
246             .FromValue(0)
247             .set_xmit_irq_count(kTransmitIrqCount)
248             .set_recv_irq_count(kRecieveIrqCount)
249             .WriteTo(&mmio_);
250     } else {
251         ctrl.set_tx_enable(0)
252             .set_rx_enable(0)
253             // Invert our RTS if we are configured for flow control.
254             .set_inv_rts(!ctrl.two_wire())
255             .WriteTo(&mmio_);
256     }
257 }
258 
SerialImplEnable(bool enable)259 zx_status_t AmlUart::SerialImplEnable(bool enable) {
260     fbl::AutoLock al(&enable_lock_);
261 
262     if (enable && !enabled_) {
263         zx_status_t status = pdev_map_interrupt(&pdev_, 0, irq_.reset_and_get_address());
264         if (status != ZX_OK) {
265             zxlogf(ERROR, "%s: pdev_map_interrupt failed %d\n", __func__, status);
266             return status;
267         }
268 
269         EnableLocked(true);
270 
271         auto start_thread = [](void* arg) { return static_cast<AmlUart*>(arg)->IrqThread(); };
272         int rc = thrd_create_with_name(&irq_thread_, start_thread, this, "aml_uart_irq_thread");
273         if (rc != thrd_success) {
274             EnableLocked(false);
275             return thrd_status_to_zx_status(rc);
276         }
277     } else if (!enable && enabled_) {
278         irq_.destroy();
279         thrd_join(irq_thread_, nullptr);
280         EnableLocked(false);
281     }
282 
283     enabled_ = enable;
284     return ZX_OK;
285 }
286 
SerialImplRead(void * buf,size_t length,size_t * out_actual)287 zx_status_t AmlUart::SerialImplRead(void* buf, size_t length, size_t* out_actual) {
288     auto* bufptr = static_cast<uint8_t*>(buf);
289     const uint8_t* const end = bufptr + length;
290     while (bufptr < end && (ReadStateAndNotify() & SERIAL_STATE_READABLE)) {
291         uint32_t val = mmio_.Read32(AML_UART_RFIFO);
292         *bufptr++ = static_cast<uint8_t>(val);
293     }
294 
295     const size_t read = reinterpret_cast<uintptr_t>(bufptr) - reinterpret_cast<uintptr_t>(buf);
296     *out_actual = read;
297     if (read == 0) {
298         return ZX_ERR_SHOULD_WAIT;
299     }
300     return ZX_OK;
301 }
302 
SerialImplWrite(const void * buf,size_t length,size_t * out_actual)303 zx_status_t AmlUart::SerialImplWrite(const void* buf, size_t length, size_t* out_actual) {
304     const auto* bufptr = static_cast<const uint8_t*>(buf);
305     const uint8_t* const end = bufptr + length;
306     while (bufptr < end && (ReadStateAndNotify() & SERIAL_STATE_WRITABLE)) {
307         mmio_.Write32(*bufptr++, AML_UART_WFIFO);
308     }
309 
310     const size_t written = reinterpret_cast<uintptr_t>(bufptr) - reinterpret_cast<uintptr_t>(buf);
311     *out_actual = written;
312     if (written == 0) {
313         return ZX_ERR_SHOULD_WAIT;
314     }
315     return ZX_OK;
316 }
317 
SerialImplSetNotifyCallback(const serial_notify_t * cb)318 zx_status_t AmlUart::SerialImplSetNotifyCallback(const serial_notify_t* cb) {
319     {
320         fbl::AutoLock al(&enable_lock_);
321 
322         if (enabled_) {
323             zxlogf(ERROR, "%s called when driver is enabled\n", __func__);
324             return ZX_ERR_BAD_STATE;
325         }
326 
327         fbl::AutoLock al2(&status_lock_);
328         serial_notify_t notify_cb = *cb;
329         notify_cb_ = [=](uint32_t state) { notify_cb.callback(notify_cb.ctx, state); };
330     }
331 
332     // This will trigger notifying current state.
333     ReadStateAndNotify();
334 
335     return ZX_OK;
336 }
337 
338 } // namespace serial
339 
aml_uart_bind(void * ctx,zx_device_t * parent)340 extern "C" zx_status_t aml_uart_bind(void* ctx, zx_device_t* parent) {
341     return serial::AmlUart::Create(parent);
342 }
343