1#include <barrelfish/barrelfish.h>
2#include "serial.h"
3#include <barrelfish/inthandler.h>
4#include <driverkit/driverkit.h>
5
6#include <dev/omap/omap44xx_uart3_dev.h>
7#include <arch/arm/omap44xx/device_registers.h>
8#include <maps/omap44xx_map.h>
9
10/* XXX */
11#define UART_IRQ (32+74)
12
13#define DEFAULT_MEMBASE OMAP44XX_MAP_L4_PER_UART3
14
15static omap44xx_uart3_t port;
16
17static void serial_poll(omap44xx_uart3_t *uart)
18{
19    // Read while we can
20    while(omap44xx_uart3_lsr_rx_fifo_e_rdf(uart)) {
21        char c = omap44xx_uart3_rhr_rhr_rdf(uart);
22        serial_input(&c, 1);
23    }
24}
25
26static void serial_interrupt(void *arg)
27{
28    // get type
29    omap44xx_uart3_iir_t iir= omap44xx_uart3_iir_rd(&port);
30
31    if (omap44xx_uart3_iir_it_pending_extract(iir) == 0) {
32        omap44xx_uart3_it_type_status_t it_type=
33            omap44xx_uart3_iir_it_type_extract(iir);
34        switch(it_type) {
35            case omap44xx_uart3_it_modem:
36                omap44xx_uart3_msr_rd(&port);
37                break;
38            case omap44xx_uart3_it_rxtimeout:
39            case omap44xx_uart3_it_rhr:
40                serial_poll(&port);
41                break;
42            default:
43                debug_printf("serial_interrupt: unhandled irq: %d\n", it_type);
44                break;
45        }
46    }
47}
48
49static bool convert_rx_simple(uint8_t *trig)
50{
51    switch(*trig) {
52        case 8:
53            *trig = 0;
54            return true;
55        case 16:
56            *trig = 1;
57            return true;
58        case 56:
59            *trig = 2;
60            return true;
61        case 60:
62            *trig = 3;
63            return true;
64        default:
65            return false;
66    }
67}
68
69static bool convert_tx_simple(uint8_t *trig)
70{
71    switch(*trig) {
72        case 8:
73            *trig = 0;
74            return true;
75        case 16:
76            *trig = 1;
77            return true;
78        case 32:
79            *trig = 2;
80            return true;
81        case 56:
82            *trig = 3;
83            return true;
84        default:
85            return false;
86    }
87}
88
89/*
90 * Initialzie OMAP UART with interrupt
91 * UART TRM 23.3
92 */
93static void omap44xx_uart3_init(omap44xx_uart3_t *uart, lvaddr_t base)
94{
95    // XXX: test this with other values
96    // rx and tx FIFO threshold values (1 -- 63)
97    uint8_t rx_trig = 1; // amount of characters in fifo
98    uint8_t tx_trig = 63; // amount of free spaces in fifo
99    bool need_rx_1b = convert_rx_simple(&rx_trig);
100    bool need_tx_1b = convert_tx_simple(&tx_trig);
101
102    omap44xx_uart3_initialize(uart, (mackerel_addr_t) base);
103    // do soft reset -- not the best idea if we rely on the same UART for
104    // debug output
105    //omap44xx_uart3_sysc_softreset_wrf(uart, 0x1);
106    //while (!omap44xx_uart3_syss_resetdone_rdf(uart)); // poll for reset completion
107
108    // configure FIFOs according to TRM (section 25.3.5.1.1.2)
109    //1 switch to config mode B (access to efr register): set lcr to 0xbf
110    omap44xx_uart3_lcr_t old_lcr = omap44xx_uart3_lcr_rd(uart);
111    omap44xx_uart3_lcr_wr(uart, 0xbf);
112    // 1.1 disable baud clock so we can write to FCR[0] and FCR[3]
113    omap44xx_uart3_dll_clock_lsb_wrf(uart, 0x0);
114    omap44xx_uart3_dlh_clock_msb_wrf(uart, 0x0);
115    //2 enable register submode tlr to access tlr register
116    omap44xx_uart3_enhanced_en_status_t old_enhanced_en =
117        omap44xx_uart3_efr_enhanced_en_rdf(uart);
118    omap44xx_uart3_efr_enhanced_en_wrf(uart, 1);
119    //3 switch to config mode A (access to mcr register): set lcr to: 0x80
120    omap44xx_uart3_lcr_wr(uart, 0x80);
121    //4 enable register submode tlr to access tlr register
122    omap44xx_uart3_tcr_tlr_status_t old_tcr_tlr =
123        omap44xx_uart3_mcr_tcr_tlr_rdf(uart);
124    omap44xx_uart3_mcr_tcr_tlr_wrf(uart, 1);
125    //5 enable FIFO, load FIFO triggers, load DMA mode (part1)
126    omap44xx_uart3_fcr_t fcr = omap44xx_uart3_fcr_default;
127    // set trigger lvls of rx and tx FIFOs (defined above)
128    fcr = omap44xx_uart3_fcr_rx_fifo_trig_insert(fcr, rx_trig&0x3);
129    fcr = omap44xx_uart3_fcr_tx_fifo_trig_insert(fcr, tx_trig&0x3);
130    fcr = omap44xx_uart3_fcr_dma_mode_insert(fcr, 0); // no DMA
131    fcr = omap44xx_uart3_fcr_fifo_en_insert(fcr, 0); // enable FIFOs
132    omap44xx_uart3_fcr_wr(uart, fcr);
133    //6 switch to config mode B
134    omap44xx_uart3_lcr_wr(uart, 0xbf);
135    //7 load FIFO triggers (DMA only?, part2)
136    omap44xx_uart3_tlr_t tlr = omap44xx_uart3_tlr_default;
137    omap44xx_uart3_tlr_rx_fifo_trig_dma_insert(tlr, rx_trig>>2);
138    omap44xx_uart3_tlr_tx_fifo_trig_dma_insert(tlr, tx_trig>>2);
139    omap44xx_uart3_tlr_wr(uart, tlr);
140    //8 load new FIFO triggers & new DMA mode (part3)
141    omap44xx_uart3_scr_t scr = omap44xx_uart3_scr_default;
142    // make FIFO trigger levels byte granularity
143    // --> lvl = { *_fifo_trig_dma : *_fifo_trig }
144    scr = omap44xx_uart3_scr_rx_trig_granu1_insert(scr, need_rx_1b);
145    scr = omap44xx_uart3_scr_tx_trig_granu1_insert(scr, need_tx_1b);
146    scr = omap44xx_uart3_scr_dma_mode_2_insert(scr, 0); // no DMA
147    scr = omap44xx_uart3_scr_dma_mode_ctl_insert(scr, 0); // shouldn't matter w/ DMA off
148    omap44xx_uart3_scr_wr(uart, scr);
149    //8b clear fifo queues
150    omap44xx_uart3_fcr_rx_fifo_clear_wrf(uart, 1);
151    omap44xx_uart3_fcr_tx_fifo_clear_wrf(uart, 1);
152    //9 restore enhanced_en
153    omap44xx_uart3_efr_enhanced_en_wrf(uart, old_enhanced_en);
154    //10 switch to config mode A
155    omap44xx_uart3_lcr_wr(uart, 0x80);
156    //11 restore TCR_TLR
157    omap44xx_uart3_mcr_tcr_tlr_wrf(uart, old_tcr_tlr);
158    //12 restore LCR
159    omap44xx_uart3_lcr_wr(uart, old_lcr);
160
161    // configure protocol, baud rate and irq according to trm (section
162    // 23.3.5.1.1.3)
163    //1 disable UART access to DLL and DLH regs
164    omap44xx_uart3_mdr1_mode_select_wrf(uart, 0x7);
165    //2 switch to register config mode B
166    omap44xx_uart3_lcr_wr(uart, 0xbf);
167    //3 enable access to IER bit field
168    old_enhanced_en = omap44xx_uart3_efr_enhanced_en_rdf(uart);
169    omap44xx_uart3_efr_enhanced_en_wrf(uart, 1);
170    //4 switch to reg operational mode to access IER register
171    omap44xx_uart3_lcr_wr(uart, 0);
172    //5 clear IER register
173    omap44xx_uart3_ier_wr(uart, 0);
174    //6 config mode B
175    omap44xx_uart3_lcr_wr(uart, 0xbf);
176    //7 new divisor value --> 115200 baud == 0x00, 0x1A (dlh, dll)
177    omap44xx_uart3_dll_clock_lsb_wrf(uart, 0x1a);
178    omap44xx_uart3_dlh_clock_msb_wrf(uart, 0x0);
179    //8 register operational mode
180    omap44xx_uart3_lcr_wr(uart, 0);
181    //9 load irq config --> only rhr irq for now
182    omap44xx_uart3_ier_rhr_it_wrf(uart, 1);
183    //10 register config mode B
184    omap44xx_uart3_lcr_wr(uart, 0xbf);
185    //11 restore efr.enhanced_en
186    omap44xx_uart3_efr_enhanced_en_wrf(uart, old_enhanced_en);
187    //12 load protocol formatting --> 8N1
188    omap44xx_uart3_lcr_t lcr = omap44xx_uart3_lcr_default;
189    lcr = omap44xx_uart3_lcr_parity_en_insert(lcr, 0);       // No parity
190    lcr = omap44xx_uart3_lcr_nb_stop_insert(lcr, 0);         // 1 stop bit
191    lcr = omap44xx_uart3_lcr_char_length_insert(lcr, omap44xx_uart3_cl8); // 8 data bits
192    omap44xx_uart3_lcr_wr(uart, lcr);
193    //13 load UART mode
194    omap44xx_uart3_mdr1_mode_select_wrf(uart, 0x0);
195    // DONE
196}
197
198
199static
200errval_t real_init(uint32_t membase) {
201    // XXX: TODO: figure this out --> kaluga magic?
202    errval_t err;
203    lvaddr_t vbase;
204    err = map_device_register(membase, BASE_PAGE_SIZE, &vbase);
205    if (err_is_fail(err)) {
206        USER_PANIC_ERR(err, "map_device_register failed\n");
207        return err;
208    }
209    assert(vbase);
210
211    // paging_map_device returns an address pointing to the beginning of
212    // a section, need to add the offset for within the section again
213    debug_printf("omap serial_init base = 0x%"PRIxLVADDR"\n", vbase);
214    omap44xx_uart3_init(&port, vbase);
215    debug_printf("omap serial_init[%d]: done.\n", port);
216    return SYS_ERR_OK;
217}
218
219errval_t serial_init(struct serial_params *params)
220{
221    uint32_t membase= DEFAULT_MEMBASE;
222    if(params->membase != SERIAL_MEMBASE_INVALID)
223        membase= (uint32_t)params->membase;
224
225    uint8_t irq= UART_IRQ;
226    if(params->irq != SERIAL_IRQ_INVALID)
227        irq= params->irq;
228
229    // initialize hardware
230    errval_t err = real_init(membase);
231    if (err_is_fail(err)) {
232        USER_PANIC_ERR(err, "serial_init failed\n");
233        return -1;
234    }
235
236    // register interrupt
237    err = inthandler_setup_arm(serial_interrupt, NULL, irq);
238    if (err_is_fail(err)) {
239        USER_PANIC_ERR(err, "interrupt setup failed.");
240    }
241
242    // offer service now we're up
243    start_service();
244    return SYS_ERR_OK;
245}
246
247/** output a single character */
248static void serial_putchar(char c)
249{
250    // Wait until FIFO can hold more characters
251    while (!omap44xx_uart3_lsr_tx_fifo_e_rdf(&port));
252    // Write character
253    omap44xx_uart3_thr_thr_wrf(&port, c);
254}
255
256/** write string to serial port */
257void serial_write(const char *c, size_t len)
258{
259    for (int i = 0; i < len; i++) {
260        serial_putchar(c[i]);
261    }
262}
263