1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <stdint.h>
14#include <stdbool.h>
15#include <stdarg.h>
16#include <string.h>
17#include <camkes/gdb/serial.h>
18#include <camkes/gdb/gdb.h>
19#include <sel4/sel4.h>
20#include <utils/util.h>
21
22
23#define BAUD_RATE 115200
24#define MAX_PRINTF_LENGTH 256
25// Register layout. Done by offset from base port
26#define THR_ADDR (0)
27#define RBR_ADDR (0)
28#define LATCH_LOW_ADDR (0)
29#define LATCH_HIGH_ADDR (1)
30#define IER_ADDR (1)
31#define FCR_ADDR (2)
32#define IIR_ADDR (2)
33#define LCR_ADDR (3)
34#define MCR_ADDR (4)
35#define LSR_ADDR (5)
36#define MSR_ADDR (6)
37
38#define IER_RESERVED_MASK (BIT(6) | BIT(7))
39
40#define FCR_ENABLE BIT(0)
41#define FCR_CLEAR_RECEIVE BIT(1)
42#define FCR_CLEAR_TRANSMIT BIT(2)
43#define FCR_TRIGGER_16_1 (0)
44
45#define LCR_DLAB BIT(7)
46
47#define MCR_DTR BIT(0)
48#define MCR_RTS BIT(1)
49#define MCR_AO1 BIT(2)
50#define MCR_AO2 BIT(3)
51
52#define LSR_EMPTY_DHR BIT(6)
53#define LSR_EMPTY_THR BIT(5)
54#define LSR_DATA_READY BIT(0)
55
56#define IIR_FIFO_ENABLED (BIT(6) | BIT(7))
57#define IIR_REASON (BIT(1) | BIT(2) | BIT(3))
58#define IIR_MSR (0)
59#define IIR_THR BIT(1)
60#define IIR_RDA BIT(2)
61#define IIR_TIME (BIT(3) | BIT(2))
62#define IIR_LSR (BIT(2) | BIT(1))
63#define IIR_PENDING BIT(0)
64
65
66
67static void serial_putchar(int c);
68
69// Serial buffer manipulation
70static void initialise_buffer(void);
71static int push_buffer(int c);
72static void clear_buffer(void);
73
74// Register manipulation functions
75static void write_latch(uint16_t val);
76static void write_latch_high(uint8_t val);
77static void write_latch_low(uint8_t val);
78static void write_ier(uint8_t val);
79static uint8_t read_ier(void);
80static void write_lcr(uint8_t val);
81static uint8_t read_lcr(void);
82static void write_fcr(uint8_t val);
83static void write_mcr(uint8_t val);
84static uint8_t read_lsr(void);
85static uint8_t read_rbr(void);
86static void write_thr(uint8_t val);
87static uint8_t read_iir(void);
88static uint8_t read_msr(void);
89
90// Serial config
91static void set_dlab(int v);
92static void disable_interrupt(void);
93static void disable_fifo(void);
94static void set_baud_rate(uint32_t baud);
95static void reset_state(void);
96static void enable_fifo(void);
97static void enable_interrupt(void);
98static void reset_lcr(void);
99static void reset_mcr(void);
100static void wait_for_fifo(void);
101static bool clear_iir(void);
102
103void serial_port_out8_offset(uint16_t offset, uint8_t value);
104uint8_t serial_port_in8_offset(uint16_t offset);
105
106
107int command_wait = false;
108int fifo_depth = 1;
109int fifo_used = 0;
110static gdb_state_t *gdb_state;
111// Serial buffer manipulation
112static void initialise_buffer(void)
113{
114    buf.length = 0;
115    buf.checksum_count = 0;
116    buf.checksum_index = 0;
117}
118
119static int push_buffer(int c)
120{
121    if (buf.length == GETCHAR_BUFSIZ) {
122        return -1;
123    } else {
124        buf.data[buf.length] = c;
125        buf.length++;
126        return 0;
127    }
128}
129
130static void clear_buffer(void)
131{
132    int i;
133    for (i = 0; i < buf.length; i++) {
134        buf.data[i] = 0;
135    }
136    initialise_buffer();
137}
138
139// Register manipulation functions
140static inline void write_latch(uint16_t val)
141{
142    set_dlab(1);
143    write_latch_high(val >> 8);
144    write_latch_low(val & 0xff);
145    set_dlab(0);
146}
147
148static inline void write_latch_high(uint8_t val)
149{
150    serial_port_out8_offset(LATCH_HIGH_ADDR, val);
151}
152
153static inline void write_latch_low(uint8_t val)
154{
155    serial_port_out8_offset(LATCH_LOW_ADDR, val);
156}
157
158static inline void write_ier(uint8_t val)
159{
160    serial_port_out8_offset(IER_ADDR, val);
161}
162static inline uint8_t read_ier()
163{
164    return serial_port_in8_offset(IER_ADDR);
165}
166
167static inline void write_lcr(uint8_t val)
168{
169    serial_port_out8_offset(LCR_ADDR, val);
170}
171static inline uint8_t read_lcr()
172{
173    return serial_port_in8_offset(LCR_ADDR);
174}
175
176static inline void write_fcr(uint8_t val)
177{
178    serial_port_out8_offset(FCR_ADDR, val);
179}
180// you cannot read the fcr
181
182static inline void write_mcr(uint8_t val)
183{
184    serial_port_out8_offset(MCR_ADDR, val);
185}
186
187static inline uint8_t read_lsr()
188{
189    return serial_port_in8_offset(LSR_ADDR);
190}
191
192static inline uint8_t read_rbr()
193{
194    return serial_port_in8_offset(RBR_ADDR);
195}
196
197static inline void write_thr(uint8_t val)
198{
199    serial_port_out8_offset(THR_ADDR, val);
200}
201
202static inline uint8_t read_iir()
203{
204    return serial_port_in8_offset(IIR_ADDR);
205}
206
207static inline uint8_t read_msr()
208{
209    return serial_port_in8_offset(MSR_ADDR);
210}
211
212// Serial config
213
214void serial_init(gdb_state_t *gdb)
215{
216    // Initialize the serial port
217    int UNUSED error;
218    error = serial_lock();
219    gdb_state = gdb;
220    set_dlab(0); // we always assume the dlab is 0 unless we explicitly change it
221    disable_interrupt();
222    disable_fifo();
223    reset_lcr();
224    reset_mcr();
225    clear_iir();
226    set_baud_rate(BAUD_RATE);
227    reset_state();
228    enable_fifo();
229    enable_interrupt();
230    clear_iir();
231    initialise_buffer();
232    error = serial_unlock();
233
234}
235
236static void set_dlab(int v)
237{
238    if (v) {
239        write_lcr(read_lcr() | LCR_DLAB);
240    } else {
241        write_lcr(read_lcr() & ~LCR_DLAB);
242    }
243}
244
245static void disable_interrupt()
246{
247    write_ier(0);
248}
249
250static void disable_fifo()
251{
252    // first attempt to use the clear fifo commands
253    write_fcr(FCR_CLEAR_TRANSMIT | FCR_CLEAR_RECEIVE);
254    // now disable with a 0
255    write_fcr(0);
256}
257
258static void set_baud_rate(uint32_t baud)
259{
260    assert(baud != 0);
261    assert(115200 % baud == 0);
262    uint16_t divisor = 115200 / baud;
263    write_latch(divisor);
264}
265
266static void reset_state(void)
267{
268    // clear internal global state here
269    fifo_used = 0;
270}
271
272static void enable_fifo(void)
273{
274    // check if there is a fifo and how deep it is
275    uint8_t info = read_iir();
276    if ((info & IIR_FIFO_ENABLED) == IIR_FIFO_ENABLED) {
277        fifo_depth = 16;
278        write_fcr(FCR_TRIGGER_16_1 | FCR_ENABLE);
279    } else {
280        fifo_depth = 1;
281    }
282}
283
284static void enable_interrupt(void)
285{
286    write_ier(1);
287}
288
289static void reset_lcr(void)
290{
291    // set 8-n-1
292    write_lcr(3);
293}
294
295static void reset_mcr(void)
296{
297    write_mcr(MCR_DTR | MCR_RTS | MCR_AO1 | MCR_AO2);
298}
299
300static void wait_for_fifo()
301{
302    while (!(read_lsr() & (LSR_EMPTY_DHR | LSR_EMPTY_THR)));
303    fifo_used = 0;
304}
305
306static bool handle_char(void)
307{
308    char c = read_rbr();
309    if (c == '$') {
310        command_wait = true;
311        clear_buffer();
312
313    } else if (buf.checksum_index) {
314        buf.checksum_count++;
315    } else if (c == '#') {
316        buf.checksum_index = buf.length;
317    }
318    push_buffer(c);
319    if (buf.checksum_count == 2 && command_wait) {
320        /* Got a command */
321        return true;
322    }
323    return false;
324}
325
326static bool clear_iir(void)
327{
328    uint8_t iir;
329    while (!((iir = read_iir()) & IIR_PENDING)) {
330        switch (iir & IIR_REASON) {
331        case IIR_RDA:
332        case IIR_TIME:
333            while (read_lsr() & LSR_DATA_READY) {
334                bool result = handle_char();
335                if (result) {
336                    return result;
337                }
338            }
339        default:
340            break;
341        }
342    }
343    return false;
344}
345
346static void serial_output_via_fifo(uint8_t c)
347{
348    // check how much fifo we've used and if we need to drain it
349    if (fifo_used == fifo_depth) {
350        wait_for_fifo();
351    }
352
353    write_thr(c);
354    fifo_used++;
355}
356
357// Serial usage
358static void serial_putchar(int c)
359{
360    if (c == '\n') {
361        serial_output_via_fifo('\r');
362    }
363
364    serial_output_via_fifo((uint8_t)c);
365}
366
367
368void serial_irq_handle(void)
369{
370    int UNUSED error;
371    error = serial_lock();
372    bool got_command = clear_iir();
373    error = serial_irq_acknowledge();
374    error = serial_unlock();
375    if (got_command) {
376        handle_gdb(gdb_state);
377        error = serial_lock();
378        clear_buffer();
379        command_wait = false;
380        error = serial_unlock();
381    }
382}
383
384void gdb_printf(const char *format, ...)
385{
386    va_list arg;
387    char text_buf[MAX_PRINTF_LENGTH];
388    va_start(arg, format);
389    vsnprintf(text_buf, MAX_PRINTF_LENGTH, format, arg);
390    va_end(arg);
391    int UNUSED error;
392    error = serial_lock();
393    for (int i = 0; i < strlen(text_buf); i++) {
394        serial_putchar(text_buf[i]);
395    }
396    error = serial_unlock();
397}
398