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