1 // Copyright 2018 The Fuchsia Authors
2 // Use of this source code is governed by a MIT-style
3 // license that can be found in the LICENSE file or at
4 // https://opensource.org/licenses/MIT
5 
6 #include <arch/arm64/periphmap.h>
7 #include <dev/interrupt.h>
8 #include <dev/uart.h>
9 #include <kernel/thread.h>
10 #include <lib/cbuf.h>
11 #include <lib/debuglog.h>
12 #include <pdev/driver.h>
13 #include <pdev/uart.h>
14 #include <platform/debug.h>
15 #include <reg.h>
16 #include <stdio.h>
17 #include <trace.h>
18 #include <zircon/boot/driver-config.h>
19 
20 // clang-format off
21 
22 /* Registers */
23 #define MX8_URXD                    (0x00)
24 #define MX8_UTXD                    (0x40)
25 #define MX8_UCR1                    (0x80)
26 #define MX8_UCR2                    (0x84)
27 #define MX8_UCR3                    (0x88)
28 #define MX8_UCR4                    (0x8C)
29 #define MX8_UFCR                    (0x90)
30 #define MX8_USR1                    (0x94)
31 #define MX8_USR2                    (0x98)
32 #define MX8_UTS                     (0xB4)
33 
34 /* UCR1 Bit Definition */
35 #define UCR1_TRDYEN                 (1 << 13)
36 #define UCR1_RRDYEN                 (1 << 9)
37 #define UCR1_UARTEN                 (1 << 0)
38 
39 /* UCR2 Bit Definition */
40 #define UCR2_TXEN                   (1 << 2)
41 #define UCR2_RXEN                   (1 << 1)
42 #define UCR2_SRST                   (1 << 0)
43 
44 /* UFCR Bit Definition */
45 #define UFCR_TXTL(x)                (x << 10)
46 #define UFCR_RXTL(x)                (x << 0)
47 #define UFCR_MASK                   (0x3f)
48 
49 /* USR1 Bit Definition */
50 #define USR1_TRDY                   (1 << 13)
51 #define USR1_RRDY                   (1 << 9)
52 
53 /* USR2 Bit Definition */
54 #define USR2_TXFE                   (1 << 14)
55 
56 /* UTS Bit Definition */
57 #define UTS_TXEMPTY                 (1 << 6)
58 #define UTS_RXEMPTY                 (1 << 5)
59 #define UTS_TXFULL                  (1 << 4)
60 #define UTS_RXFULL                  (1 << 3)
61 
62 #define RXBUF_SIZE 32
63 
64 // clang-format on
65 
66 // values read from zbi
67 static bool initialized = false;
68 static vaddr_t uart_base = 0;
69 static uint32_t uart_irq = 0;
70 static cbuf_t uart_rx_buf;
71 // static cbuf_t uart_tx_buf;
72 
73 static bool uart_tx_irq_enabled = false;
74 static event_t uart_dputc_event = EVENT_INITIAL_VALUE(uart_dputc_event,
75                                                       true,
76                                                       EVENT_FLAG_AUTOUNSIGNAL);
77 
78 static spin_lock_t uart_spinlock = SPIN_LOCK_INITIAL_VALUE;
79 
80 #define UARTREG(reg) (*(volatile uint32_t*)((uart_base) + (reg)))
81 
uart_irq_handler(void * arg)82 static interrupt_eoi uart_irq_handler(void* arg) {
83     /* read interrupt status and mask */
84     while ((UARTREG(MX8_USR1) & USR1_RRDY)) {
85         if (cbuf_space_avail(&uart_rx_buf) == 0) {
86             break;
87         }
88         char c = UARTREG(MX8_URXD) & 0xFF;
89         cbuf_write_char(&uart_rx_buf, c);
90     }
91 
92     /* Signal if anyone is waiting to TX */
93     if (UARTREG(MX8_UCR1) & UCR1_TRDYEN) {
94         spin_lock(&uart_spinlock);
95         if (!(UARTREG(MX8_USR2) & UTS_TXFULL)) {
96             // signal
97             event_signal(&uart_dputc_event, true);
98         }
99         spin_unlock(&uart_spinlock);
100     }
101 
102     return IRQ_EOI_DEACTIVATE;
103 }
104 
105 /* panic-time getc/putc */
imx_uart_pputc(char c)106 static int imx_uart_pputc(char c) {
107     if (!uart_base) {
108         return -1;
109     }
110 
111     /* spin while fifo is full */
112     while (UARTREG(MX8_UTS) & UTS_TXFULL)
113         ;
114     UARTREG(MX8_UTXD) = c;
115 
116     return 1;
117 }
118 
imx_uart_pgetc()119 static int imx_uart_pgetc() {
120     if (!uart_base) {
121         return ZX_ERR_NOT_SUPPORTED;
122     }
123 
124     if ((UARTREG(MX8_UTS) & UTS_RXEMPTY)) {
125         return ZX_ERR_INTERNAL;
126     }
127 
128     return UARTREG(MX8_URXD);
129 }
130 
imx_uart_getc(bool wait)131 static int imx_uart_getc(bool wait) {
132     if (!uart_base) {
133         return ZX_ERR_NOT_SUPPORTED;
134     }
135 
136     if (initialized) {
137         char c;
138         if (cbuf_read_char(&uart_rx_buf, &c, wait) == 1) {
139             return c;
140         }
141         return ZX_ERR_INTERNAL;
142     } else {
143         // Interrupts are not enabled yet. Use panic calls for now
144         return imx_uart_pgetc();
145     }
146 }
147 
imx_dputs(const char * str,size_t len,bool block,bool map_NL)148 static void imx_dputs(const char* str, size_t len,
149                       bool block, bool map_NL) {
150     spin_lock_saved_state_t state;
151     bool copied_CR = false;
152 
153     if (!uart_base) {
154         return;
155     }
156     if (!uart_tx_irq_enabled) {
157         block = false;
158     }
159     spin_lock_irqsave(&uart_spinlock, state);
160 
161     while (len > 0) {
162         // is FIFO full?
163         while ((UARTREG(MX8_UTS) & UTS_TXFULL)) {
164             spin_unlock_irqrestore(&uart_spinlock, state);
165             if (block) {
166                 event_wait(&uart_dputc_event);
167             } else {
168                 arch_spinloop_pause();
169             }
170             spin_lock_irqsave(&uart_spinlock, state);
171         }
172         if (*str == '\n' && map_NL && !copied_CR) {
173             copied_CR = true;
174             imx_uart_pputc('\r');
175         } else {
176             copied_CR = false;
177             imx_uart_pputc(*str++);
178             len--;
179         }
180     }
181     spin_unlock_irqrestore(&uart_spinlock, state);
182 }
183 
imx_start_panic()184 static void imx_start_panic() {
185     uart_tx_irq_enabled = false;
186 }
187 
188 static const struct pdev_uart_ops uart_ops = {
189     .getc = imx_uart_getc,
190     .pputc = imx_uart_pputc,
191     .pgetc = imx_uart_pgetc,
192     .start_panic = imx_start_panic,
193     .dputs = imx_dputs,
194 };
195 
imx_uart_init(const void * driver_data,uint32_t length)196 static void imx_uart_init(const void* driver_data, uint32_t length) {
197     uint32_t regVal;
198 
199     // create circular buffer to hold received data
200     cbuf_initialize(&uart_rx_buf, RXBUF_SIZE);
201 
202     // register uart irq
203     register_int_handler(uart_irq, &uart_irq_handler, NULL);
204 
205     // set rx fifo threshold to 1 character
206     regVal = UARTREG(MX8_UFCR);
207     regVal &= ~UFCR_RXTL(UFCR_MASK);
208     regVal &= ~UFCR_TXTL(UFCR_MASK);
209     regVal |= UFCR_RXTL(1);
210     regVal |= UFCR_TXTL(0x2);
211     UARTREG(MX8_UFCR) = regVal;
212 
213     // enable rx interrupt
214     regVal = UARTREG(MX8_UCR1);
215     regVal |= UCR1_RRDYEN;
216     if (dlog_bypass() == false) {
217         // enable tx interrupt
218         regVal |= UCR1_TRDYEN;
219     }
220     UARTREG(MX8_UCR1) = regVal;
221 
222     // enable rx and tx transmisster
223     regVal = UARTREG(MX8_UCR2);
224     regVal |= UCR2_RXEN | UCR2_TXEN;
225     UARTREG(MX8_UCR2) = regVal;
226 
227     if (dlog_bypass() == true) {
228         uart_tx_irq_enabled = false;
229     } else {
230         /* start up tx driven output */
231         printf("UART: started IRQ driven TX\n");
232         uart_tx_irq_enabled = true;
233     }
234 
235     initialized = true;
236 
237     // enable interrupts
238     unmask_interrupt(uart_irq);
239 }
240 
imx_uart_init_early(const void * driver_data,uint32_t length)241 static void imx_uart_init_early(const void* driver_data, uint32_t length) {
242     ASSERT(length >= sizeof(dcfg_simple_t));
243     auto driver = static_cast<const dcfg_simple_t*>(driver_data);
244     ASSERT(driver->mmio_phys && driver->irq);
245 
246     uart_base = periph_paddr_to_vaddr(driver->mmio_phys);
247     ASSERT(uart_base);
248     uart_irq = driver->irq;
249 
250     pdev_register_uart(&uart_ops);
251 }
252 
253 LK_PDEV_INIT(imx_uart_init_early, KDRV_NXP_IMX_UART, imx_uart_init_early, LK_INIT_LEVEL_PLATFORM_EARLY);
254 LK_PDEV_INIT(imx_uart_init, KDRV_NXP_IMX_UART, imx_uart_init, LK_INIT_LEVEL_PLATFORM);
255