]>
Commit | Line | Data |
---|---|---|
55e303ae | 1 | /* |
2d21ac55 | 2 | * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. |
55e303ae | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
0a7de745 | 5 | * |
2d21ac55 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
0a7de745 | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
0a7de745 | 17 | * |
2d21ac55 A |
18 | * The Original Code and all software distributed under the License are |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
0a7de745 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
55e303ae A |
27 | */ |
28 | ||
29 | /* | |
30 | * file: pe_serial.c | |
31 | * Polled-mode 16x50 UART driver. | |
32 | */ | |
33 | ||
813fb2f6 | 34 | #include <machine/machine_routines.h> |
55e303ae A |
35 | #include <pexpert/protos.h> |
36 | #include <pexpert/pexpert.h> | |
37 | ||
813fb2f6 | 38 | struct pe_serial_functions { |
0a7de745 A |
39 | void (*uart_init) (void); |
40 | void (*uart_set_baud_rate) (int unit, uint32_t baud_rate); | |
41 | int (*tr0) (void); | |
42 | void (*td0) (int c); | |
43 | int (*rr0) (void); | |
44 | int (*rd0) (void); | |
813fb2f6 A |
45 | }; |
46 | ||
47 | static struct pe_serial_functions *gPESF; | |
48 | ||
49 | static int uart_initted = 0; /* 1 if init'ed */ | |
50 | ||
a39ff7e2 A |
51 | static unsigned int legacy_uart_enabled = 0; /* 1 Legacy IO based UART is supported on platform */ |
52 | ||
53 | static boolean_t lpss_uart_supported = 0; /* 1 if LPSS UART is supported on platform */ | |
54 | static unsigned int lpss_uart_enabled = 0; /* 1 if it is LPSS UART is in D0 state */ | |
0a7de745 | 55 | static void lpss_uart_re_init(void); |
a39ff7e2 | 56 | |
d9a64523 A |
57 | static boolean_t pcie_uart_enabled = 0; /* 1 if PCIe UART is supported on platform */ |
58 | ||
813fb2f6 A |
59 | #define DEFAULT_UART_BAUD_RATE 115200 |
60 | ||
61 | static unsigned uart_baud_rate = DEFAULT_UART_BAUD_RATE; | |
62 | ||
63 | // ============================================================================= | |
64 | // Legacy UART support using IO transactions to COM1 or COM2 | |
65 | // ============================================================================= | |
66 | ||
67 | #define LEGACY_UART_PORT_ADDR COM1_PORT_ADDR | |
68 | #define LEGACY_UART_CLOCK 1843200 /* 1.8432 MHz clock */ | |
69 | ||
70 | #define IO_WRITE(r, v) outb(LEGACY_UART_PORT_ADDR + UART_##r, v) | |
71 | #define IO_READ(r) inb(LEGACY_UART_PORT_ADDR + UART_##r) | |
72 | ||
55e303ae | 73 | enum { |
0a7de745 A |
74 | COM1_PORT_ADDR = 0x3f8, |
75 | COM2_PORT_ADDR = 0x2f8 | |
55e303ae A |
76 | }; |
77 | ||
55e303ae | 78 | enum { |
0a7de745 A |
79 | UART_RBR = 0, /* receive buffer Register (R) */ |
80 | UART_THR = 0, /* transmit holding register (W) */ | |
81 | UART_DLL = 0, /* DLAB = 1, divisor latch (LSB) */ | |
82 | UART_IER = 1, /* interrupt enable register */ | |
83 | UART_DLM = 1, /* DLAB = 1, divisor latch (MSB) */ | |
84 | UART_IIR = 2, /* interrupt ident register (R) */ | |
85 | UART_FCR = 2, /* fifo control register (W) */ | |
86 | UART_LCR = 3, /* line control register */ | |
87 | UART_MCR = 4, /* modem control register */ | |
88 | UART_LSR = 5, /* line status register */ | |
89 | UART_MSR = 6, /* modem status register */ | |
90 | UART_SCR = 7 /* scratch register */ | |
55e303ae A |
91 | }; |
92 | ||
93 | enum { | |
0a7de745 A |
94 | UART_LCR_8BITS = 0x03, |
95 | UART_LCR_DLAB = 0x80 | |
55e303ae A |
96 | }; |
97 | ||
98 | enum { | |
0a7de745 A |
99 | UART_MCR_DTR = 0x01, |
100 | UART_MCR_RTS = 0x02, | |
101 | UART_MCR_OUT1 = 0x04, | |
102 | UART_MCR_OUT2 = 0x08, | |
103 | UART_MCR_LOOP = 0x10 | |
55e303ae A |
104 | }; |
105 | ||
106 | enum { | |
0a7de745 A |
107 | UART_LSR_DR = 0x01, |
108 | UART_LSR_OE = 0x02, | |
109 | UART_LSR_PE = 0x04, | |
110 | UART_LSR_FE = 0x08, | |
111 | UART_LSR_THRE = 0x20 | |
55e303ae A |
112 | }; |
113 | ||
a39ff7e2 A |
114 | enum { |
115 | UART_CLK_125M_1 = 0x60002, | |
116 | UART_CLK_125M_2 = 0x80060003, | |
117 | }; | |
118 | ||
55e303ae | 119 | static int |
813fb2f6 | 120 | legacy_uart_probe( void ) |
55e303ae | 121 | { |
0a7de745 | 122 | /* Verify that the Scratch Register is accessible */ |
593a1d5f | 123 | |
0a7de745 A |
124 | IO_WRITE( SCR, 0x5a ); |
125 | if (IO_READ(SCR) != 0x5a) { | |
126 | return 0; | |
127 | } | |
128 | IO_WRITE( SCR, 0xa5 ); | |
129 | if (IO_READ(SCR) != 0xa5) { | |
130 | return 0; | |
131 | } | |
132 | return 1; | |
55e303ae A |
133 | } |
134 | ||
135 | static void | |
813fb2f6 | 136 | legacy_uart_set_baud_rate( __unused int unit, uint32_t baud_rate ) |
55e303ae | 137 | { |
0a7de745 A |
138 | const unsigned char lcr = IO_READ( LCR ); |
139 | unsigned long div; | |
140 | ||
141 | if (baud_rate == 0) { | |
142 | baud_rate = 9600; | |
143 | } | |
144 | div = LEGACY_UART_CLOCK / 16 / baud_rate; | |
145 | IO_WRITE( LCR, lcr | UART_LCR_DLAB ); | |
146 | IO_WRITE( DLM, (unsigned char)(div >> 8)); | |
147 | IO_WRITE( DLL, (unsigned char) div ); | |
148 | IO_WRITE( LCR, lcr & ~UART_LCR_DLAB); | |
813fb2f6 A |
149 | } |
150 | ||
151 | static int | |
152 | legacy_uart_tr0( void ) | |
153 | { | |
0a7de745 | 154 | return IO_READ(LSR) & UART_LSR_THRE; |
813fb2f6 A |
155 | } |
156 | ||
157 | static void | |
158 | legacy_uart_td0( int c ) | |
159 | { | |
0a7de745 | 160 | IO_WRITE( THR, c ); |
55e303ae A |
161 | } |
162 | ||
163 | static void | |
813fb2f6 | 164 | legacy_uart_init( void ) |
55e303ae | 165 | { |
0a7de745 | 166 | /* Disable hardware interrupts */ |
813fb2f6 | 167 | |
0a7de745 A |
168 | IO_WRITE( MCR, 0 ); |
169 | IO_WRITE( IER, 0 ); | |
813fb2f6 | 170 | |
0a7de745 | 171 | /* Disable FIFO's for 16550 devices */ |
813fb2f6 | 172 | |
0a7de745 | 173 | IO_WRITE( FCR, 0 ); |
813fb2f6 | 174 | |
0a7de745 | 175 | /* Set for 8-bit, no parity, DLAB bit cleared */ |
813fb2f6 | 176 | |
0a7de745 | 177 | IO_WRITE( LCR, UART_LCR_8BITS ); |
813fb2f6 | 178 | |
0a7de745 | 179 | /* Set baud rate */ |
55e303ae | 180 | |
0a7de745 | 181 | gPESF->uart_set_baud_rate( 0, uart_baud_rate ); |
55e303ae | 182 | |
0a7de745 | 183 | /* Assert DTR# and RTS# lines (OUT2?) */ |
813fb2f6 | 184 | |
0a7de745 | 185 | IO_WRITE( MCR, UART_MCR_DTR | UART_MCR_RTS ); |
813fb2f6 | 186 | |
0a7de745 | 187 | /* Clear any garbage in the input buffer */ |
813fb2f6 | 188 | |
0a7de745 | 189 | IO_READ( RBR ); |
813fb2f6 | 190 | |
0a7de745 | 191 | uart_initted = 1; |
55e303ae A |
192 | } |
193 | ||
0c530ab8 | 194 | static int |
0a7de745 | 195 | legacy_uart_rr0( void ) |
0c530ab8 | 196 | { |
0a7de745 | 197 | unsigned char lsr; |
0c530ab8 | 198 | |
0a7de745 | 199 | lsr = IO_READ( LSR ); |
0c530ab8 | 200 | |
0a7de745 A |
201 | if (lsr & (UART_LSR_FE | UART_LSR_PE | UART_LSR_OE)) { |
202 | IO_READ( RBR ); /* discard */ | |
203 | return 0; | |
204 | } | |
0c530ab8 | 205 | |
0a7de745 | 206 | return lsr & UART_LSR_DR; |
813fb2f6 A |
207 | } |
208 | ||
209 | static int | |
0a7de745 | 210 | legacy_uart_rd0( void ) |
813fb2f6 | 211 | { |
0a7de745 | 212 | return IO_READ( RBR ); |
813fb2f6 A |
213 | } |
214 | ||
215 | static struct pe_serial_functions legacy_uart_serial_functions = { | |
0a7de745 A |
216 | .uart_init = legacy_uart_init, |
217 | .uart_set_baud_rate = legacy_uart_set_baud_rate, | |
218 | .tr0 = legacy_uart_tr0, | |
219 | .td0 = legacy_uart_td0, | |
220 | .rr0 = legacy_uart_rr0, | |
221 | .rd0 = legacy_uart_rd0 | |
813fb2f6 A |
222 | }; |
223 | ||
224 | // ============================================================================= | |
225 | // MMIO UART (using PCH LPSS UART2) | |
226 | // ============================================================================= | |
227 | ||
a39ff7e2 A |
228 | #define MMIO_UART2_BASE_LEGACY 0xFE034000 /* Legacy MMIO Config space */ |
229 | #define MMIO_UART2_BASE 0xFE036000 /* MMIO Config space */ | |
230 | #define PCI_UART2 0xFE037000 /* PCI Config Space */ | |
813fb2f6 A |
231 | |
232 | #define MMIO_WRITE(r, v) ml_phys_write_word(mmio_uart_base + MMIO_UART_##r, v) | |
233 | #define MMIO_READ(r) ml_phys_read_word(mmio_uart_base + MMIO_UART_##r) | |
234 | ||
235 | enum { | |
0a7de745 A |
236 | MMIO_UART_RBR = 0x0, /* receive buffer Register (R) */ |
237 | MMIO_UART_THR = 0x0, /* transmit holding register (W) */ | |
238 | MMIO_UART_DLL = 0x0, /* DLAB = 1, divisor latch (LSB) */ | |
239 | MMIO_UART_IER = 0x4, /* interrupt enable register */ | |
240 | MMIO_UART_DLM = 0x4, /* DLAB = 1, divisor latch (MSB) */ | |
241 | MMIO_UART_FCR = 0x8, /* fifo control register (W) */ | |
242 | MMIO_UART_LCR = 0xc, /* line control register */ | |
243 | MMIO_UART_MCR = 0x10, /* modem control register */ | |
244 | MMIO_UART_LSR = 0x14, /* line status register */ | |
245 | MMIO_UART_SCR = 0x1c, /* scratch register */ | |
246 | MMIO_UART_CLK = 0x200, /* clocks register */ | |
247 | MMIO_UART_RST = 0x204 /* Reset register */ | |
813fb2f6 A |
248 | }; |
249 | ||
250 | static vm_offset_t mmio_uart_base = 0; | |
0a7de745 | 251 | |
813fb2f6 A |
252 | static int |
253 | mmio_uart_present( void ) | |
254 | { | |
0a7de745 A |
255 | MMIO_WRITE( SCR, 0x5a ); |
256 | if (MMIO_READ(SCR) != 0x5a) { | |
257 | return 0; | |
258 | } | |
259 | MMIO_WRITE( SCR, 0xa5 ); | |
260 | if (MMIO_READ(SCR) != 0xa5) { | |
261 | return 0; | |
262 | } | |
263 | return 1; | |
813fb2f6 A |
264 | } |
265 | ||
266 | static int | |
267 | mmio_uart_probe( void ) | |
268 | { | |
0a7de745 A |
269 | unsigned new_mmio_uart_base = 0; |
270 | ||
271 | // if specified, mmio_uart overrides all probing | |
272 | if (PE_parse_boot_argn("mmio_uart", &new_mmio_uart_base, sizeof(new_mmio_uart_base))) { | |
273 | // mmio_uart=0 will disable mmio_uart support | |
274 | if (new_mmio_uart_base == 0) { | |
275 | return 0; | |
276 | } | |
277 | ||
278 | mmio_uart_base = new_mmio_uart_base; | |
279 | return 1; | |
280 | } | |
281 | ||
282 | // probe the two possible MMIO_UART2 addresses | |
283 | mmio_uart_base = MMIO_UART2_BASE; | |
284 | if (mmio_uart_present()) { | |
285 | return 1; | |
286 | } | |
287 | ||
288 | mmio_uart_base = MMIO_UART2_BASE_LEGACY; | |
289 | if (mmio_uart_present()) { | |
290 | return 1; | |
291 | } | |
292 | ||
293 | // no mmio uart found | |
294 | return 0; | |
813fb2f6 A |
295 | } |
296 | ||
297 | static void | |
298 | mmio_uart_set_baud_rate( __unused int unit, __unused uint32_t baud_rate ) | |
299 | { | |
0a7de745 A |
300 | const unsigned char lcr = MMIO_READ( LCR ); |
301 | unsigned long div; | |
813fb2f6 | 302 | |
0a7de745 A |
303 | if (baud_rate == 0) { |
304 | baud_rate = 9600; | |
305 | } | |
306 | div = LEGACY_UART_CLOCK / 16 / baud_rate; | |
813fb2f6 | 307 | |
0a7de745 A |
308 | MMIO_WRITE( LCR, lcr | UART_LCR_DLAB ); |
309 | MMIO_WRITE( DLM, (unsigned char)(div >> 8)); | |
310 | MMIO_WRITE( DLL, (unsigned char) div ); | |
311 | MMIO_WRITE( LCR, lcr & ~UART_LCR_DLAB); | |
813fb2f6 A |
312 | } |
313 | ||
314 | static int | |
315 | mmio_uart_tr0( void ) | |
316 | { | |
0a7de745 | 317 | return MMIO_READ(LSR) & UART_LSR_THRE; |
0c530ab8 A |
318 | } |
319 | ||
813fb2f6 A |
320 | static void |
321 | mmio_uart_td0( int c ) | |
55e303ae | 322 | { |
0a7de745 | 323 | MMIO_WRITE( THR, c ); |
813fb2f6 | 324 | } |
55e303ae | 325 | |
813fb2f6 A |
326 | static void |
327 | mmio_uart_init( void ) | |
328 | { | |
0a7de745 | 329 | /* Disable hardware interrupts */ |
55e303ae | 330 | |
0a7de745 A |
331 | MMIO_WRITE( MCR, 0 ); |
332 | MMIO_WRITE( IER, 0 ); | |
55e303ae | 333 | |
0a7de745 | 334 | /* Disable FIFO's for 16550 devices */ |
55e303ae | 335 | |
0a7de745 | 336 | MMIO_WRITE( FCR, 0 ); |
55e303ae | 337 | |
0a7de745 | 338 | /* Set for 8-bit, no parity, DLAB bit cleared */ |
55e303ae | 339 | |
0a7de745 | 340 | MMIO_WRITE( LCR, UART_LCR_8BITS ); |
55e303ae | 341 | |
0a7de745 | 342 | /* Leave baud rate as set by firmware unless serialbaud boot-arg overrides */ |
55e303ae | 343 | |
0a7de745 A |
344 | if (uart_baud_rate != DEFAULT_UART_BAUD_RATE) { |
345 | gPESF->uart_set_baud_rate( 0, uart_baud_rate ); | |
346 | } | |
55e303ae | 347 | |
0a7de745 | 348 | /* Assert DTR# and RTS# lines (OUT2?) */ |
55e303ae | 349 | |
0a7de745 | 350 | MMIO_WRITE( MCR, UART_MCR_DTR | UART_MCR_RTS ); |
55e303ae | 351 | |
0a7de745 | 352 | /* Clear any garbage in the input buffer */ |
55e303ae | 353 | |
0a7de745 | 354 | MMIO_READ( RBR ); |
55e303ae | 355 | |
0a7de745 | 356 | uart_initted = 1; |
813fb2f6 | 357 | } |
55e303ae | 358 | |
813fb2f6 | 359 | static int |
0a7de745 | 360 | mmio_uart_rr0( void ) |
813fb2f6 | 361 | { |
0a7de745 A |
362 | unsigned char lsr; |
363 | ||
364 | lsr = MMIO_READ( LSR ); | |
813fb2f6 | 365 | |
0a7de745 A |
366 | if (lsr & (UART_LSR_FE | UART_LSR_PE | UART_LSR_OE)) { |
367 | MMIO_READ( RBR ); /* discard */ | |
368 | return 0; | |
369 | } | |
813fb2f6 | 370 | |
0a7de745 | 371 | return lsr & UART_LSR_DR; |
813fb2f6 A |
372 | } |
373 | ||
0a7de745 A |
374 | void |
375 | lpss_uart_enable( boolean_t on_off ) | |
a39ff7e2 A |
376 | { |
377 | unsigned int pmcs_reg; | |
378 | ||
379 | if (!lpss_uart_supported) { | |
380 | return; | |
381 | } | |
382 | ||
0a7de745 | 383 | pmcs_reg = ml_phys_read_byte(PCI_UART2 + 0x84); |
a39ff7e2 A |
384 | if (on_off == FALSE) { |
385 | pmcs_reg |= 0x03; | |
386 | lpss_uart_enabled = 0; | |
387 | } else { | |
388 | pmcs_reg &= ~(0x03); | |
389 | } | |
390 | ||
0a7de745 A |
391 | ml_phys_write_byte(PCI_UART2 + 0x84, pmcs_reg); |
392 | pmcs_reg = ml_phys_read_byte(PCI_UART2 + 0x84); | |
393 | ||
a39ff7e2 A |
394 | if (on_off == TRUE) { |
395 | lpss_uart_re_init(); | |
396 | lpss_uart_enabled = 1; | |
397 | } | |
398 | } | |
399 | ||
0a7de745 A |
400 | static void |
401 | lpss_uart_re_init( void ) | |
a39ff7e2 A |
402 | { |
403 | uint32_t register_read; | |
a39ff7e2 | 404 | |
0a7de745 A |
405 | MMIO_WRITE(RST, 0x7); /* LPSS UART2 controller out ot reset */ |
406 | register_read = MMIO_READ(RST); | |
a39ff7e2 | 407 | |
0a7de745 A |
408 | MMIO_WRITE(LCR, UART_LCR_DLAB); /* Set DLAB bit to enable reading/writing of DLL, DLH */ |
409 | register_read = MMIO_READ(LCR); | |
a39ff7e2 | 410 | |
0a7de745 A |
411 | MMIO_WRITE(DLL, 1); /* Divisor Latch Low Register */ |
412 | register_read = MMIO_READ(DLL); | |
a39ff7e2 | 413 | |
0a7de745 A |
414 | MMIO_WRITE(DLM, 0); /* Divisor Latch High Register */ |
415 | register_read = MMIO_READ(DLM); | |
a39ff7e2 | 416 | |
0a7de745 A |
417 | MMIO_WRITE(FCR, 1); /* Enable FIFO */ |
418 | register_read = MMIO_READ(FCR); | |
a39ff7e2 | 419 | |
0a7de745 A |
420 | MMIO_WRITE(LCR, UART_LCR_8BITS); /* Set 8 bits, clear DLAB */ |
421 | register_read = MMIO_READ(LCR); | |
a39ff7e2 | 422 | |
0a7de745 A |
423 | MMIO_WRITE(MCR, UART_MCR_RTS); /* Request to send */ |
424 | register_read = MMIO_READ(MCR); | |
a39ff7e2 | 425 | |
0a7de745 A |
426 | MMIO_WRITE(CLK, UART_CLK_125M_1); /* 1.25M Clock speed */ |
427 | register_read = MMIO_READ(CLK); | |
428 | ||
429 | MMIO_WRITE(CLK, UART_CLK_125M_2); /* 1.25M Clock speed */ | |
430 | register_read = MMIO_READ(CLK); | |
a39ff7e2 A |
431 | } |
432 | ||
813fb2f6 | 433 | static int |
0a7de745 | 434 | mmio_uart_rd0( void ) |
813fb2f6 | 435 | { |
0a7de745 | 436 | return MMIO_READ( RBR ); |
813fb2f6 A |
437 | } |
438 | ||
439 | static struct pe_serial_functions mmio_uart_serial_functions = { | |
0a7de745 A |
440 | .uart_init = mmio_uart_init, |
441 | .uart_set_baud_rate = mmio_uart_set_baud_rate, | |
442 | .tr0 = mmio_uart_tr0, | |
443 | .td0 = mmio_uart_td0, | |
444 | .rr0 = mmio_uart_rr0, | |
445 | .rd0 = mmio_uart_rd0 | |
813fb2f6 A |
446 | }; |
447 | ||
d9a64523 | 448 | // ============================================================================= |
0a7de745 | 449 | // PCIE_MMIO UART |
d9a64523 A |
450 | // ============================================================================= |
451 | ||
452 | #define PCIE_MMIO_UART_BASE 0xFE410000 | |
453 | ||
454 | #define PCIE_MMIO_WRITE(r, v) ml_phys_write_byte(pcie_mmio_uart_base + PCIE_MMIO_UART_##r, v) | |
455 | #define PCIE_MMIO_READ(r) ml_phys_read_byte(pcie_mmio_uart_base + PCIE_MMIO_UART_##r) | |
456 | ||
457 | enum { | |
0a7de745 A |
458 | PCIE_MMIO_UART_RBR = 0x0, /* receive buffer Register (R) */ |
459 | PCIE_MMIO_UART_THR = 0x0, /* transmit holding register (W) */ | |
460 | PCIE_MMIO_UART_IER = 0x1, /* interrupt enable register */ | |
461 | PCIE_MMIO_UART_FCR = 0x2, /* fifo control register (W) */ | |
462 | PCIE_MMIO_UART_LCR = 0x4, /* line control register */ | |
463 | PCIE_MMIO_UART_MCR = 0x4, /* modem control register */ | |
464 | PCIE_MMIO_UART_LSR = 0x5, /* line status register */ | |
465 | PCIE_MMIO_UART_DLL = 0x8, /* DLAB = 1, divisor latch (LSB) */ | |
466 | PCIE_MMIO_UART_DLM = 0x9, /* DLAB = 1, divisor latch (MSB) */ | |
467 | PCIE_MMIO_UART_SCR = 0x30, /* scratch register */ | |
d9a64523 A |
468 | }; |
469 | ||
470 | static vm_offset_t pcie_mmio_uart_base = 0; | |
0a7de745 | 471 | |
d9a64523 A |
472 | static int |
473 | pcie_mmio_uart_present( void ) | |
474 | { | |
0a7de745 A |
475 | PCIE_MMIO_WRITE( SCR, 0x5a ); |
476 | if (PCIE_MMIO_READ(SCR) != 0x5a) { | |
477 | return 0; | |
478 | } | |
479 | PCIE_MMIO_WRITE( SCR, 0xa5 ); | |
480 | if (PCIE_MMIO_READ(SCR) != 0xa5) { | |
481 | return 0; | |
482 | } | |
d9a64523 | 483 | |
0a7de745 | 484 | return 1; |
d9a64523 A |
485 | } |
486 | ||
487 | static int | |
488 | pcie_mmio_uart_probe( void ) | |
489 | { | |
0a7de745 A |
490 | unsigned new_pcie_mmio_uart_base = 0; |
491 | ||
492 | // if specified, pcie_mmio_uart overrides all probing | |
493 | if (PE_parse_boot_argn("pcie_mmio_uart", &new_pcie_mmio_uart_base, sizeof(new_pcie_mmio_uart_base))) { | |
494 | // pcie_mmio_uart=0 will disable pcie_mmio_uart support | |
495 | if (new_pcie_mmio_uart_base == 0) { | |
496 | return 0; | |
497 | } | |
498 | pcie_mmio_uart_base = new_pcie_mmio_uart_base; | |
499 | return 1; | |
500 | } | |
501 | ||
502 | pcie_mmio_uart_base = PCIE_MMIO_UART_BASE; | |
503 | if (pcie_mmio_uart_present()) { | |
504 | return 1; | |
505 | } | |
506 | ||
507 | // no pcie_mmio uart found | |
508 | return 0; | |
d9a64523 A |
509 | } |
510 | ||
511 | static void | |
512 | pcie_mmio_uart_set_baud_rate( __unused int unit, __unused uint32_t baud_rate ) | |
513 | { | |
0a7de745 A |
514 | const unsigned char lcr = PCIE_MMIO_READ( LCR ); |
515 | unsigned long div; | |
d9a64523 | 516 | |
0a7de745 A |
517 | if (baud_rate == 0) { |
518 | baud_rate = 9600; | |
519 | } | |
520 | div = LEGACY_UART_CLOCK / 16 / baud_rate; | |
d9a64523 | 521 | |
0a7de745 A |
522 | PCIE_MMIO_WRITE( LCR, lcr | UART_LCR_DLAB ); |
523 | PCIE_MMIO_WRITE( DLM, (unsigned char)(div >> 8)); | |
524 | PCIE_MMIO_WRITE( DLL, (unsigned char) div ); | |
525 | PCIE_MMIO_WRITE( LCR, lcr & ~UART_LCR_DLAB); | |
d9a64523 A |
526 | } |
527 | ||
528 | static int | |
529 | pcie_mmio_uart_tr0( void ) | |
530 | { | |
0a7de745 | 531 | return PCIE_MMIO_READ(LSR) & UART_LSR_THRE; |
d9a64523 A |
532 | } |
533 | ||
534 | static void | |
535 | pcie_mmio_uart_td0( int c ) | |
536 | { | |
0a7de745 | 537 | PCIE_MMIO_WRITE( THR, c ); |
d9a64523 A |
538 | } |
539 | ||
540 | static void | |
541 | pcie_mmio_uart_init( void ) | |
542 | { | |
0a7de745 | 543 | uart_initted = 1; |
d9a64523 A |
544 | } |
545 | ||
546 | static int | |
0a7de745 | 547 | pcie_mmio_uart_rr0( void ) |
d9a64523 | 548 | { |
0a7de745 A |
549 | unsigned char lsr; |
550 | ||
551 | lsr = PCIE_MMIO_READ( LSR ); | |
d9a64523 | 552 | |
0a7de745 A |
553 | if (lsr & (UART_LSR_FE | UART_LSR_PE | UART_LSR_OE)) { |
554 | PCIE_MMIO_READ( RBR ); /* discard */ | |
555 | return 0; | |
556 | } | |
d9a64523 | 557 | |
0a7de745 | 558 | return lsr & UART_LSR_DR; |
d9a64523 A |
559 | } |
560 | ||
561 | static int | |
0a7de745 | 562 | pcie_mmio_uart_rd0( void ) |
d9a64523 | 563 | { |
0a7de745 | 564 | return PCIE_MMIO_READ( RBR ); |
d9a64523 A |
565 | } |
566 | ||
567 | static struct pe_serial_functions pcie_mmio_uart_serial_functions = { | |
0a7de745 A |
568 | .uart_init = pcie_mmio_uart_init, |
569 | .uart_set_baud_rate = pcie_mmio_uart_set_baud_rate, | |
570 | .tr0 = pcie_mmio_uart_tr0, | |
571 | .td0 = pcie_mmio_uart_td0, | |
572 | .rr0 = pcie_mmio_uart_rr0, | |
573 | .rd0 = pcie_mmio_uart_rd0 | |
d9a64523 A |
574 | }; |
575 | ||
813fb2f6 A |
576 | // ============================================================================= |
577 | // Generic serial support below | |
578 | // ============================================================================= | |
579 | ||
580 | int | |
581 | serial_init( void ) | |
582 | { | |
0a7de745 | 583 | unsigned new_uart_baud_rate = 0; |
813fb2f6 | 584 | |
0a7de745 A |
585 | if (PE_parse_boot_argn("serialbaud", &new_uart_baud_rate, sizeof(new_uart_baud_rate))) { |
586 | /* Valid divisor? */ | |
587 | if (!((LEGACY_UART_CLOCK / 16) % new_uart_baud_rate)) { | |
588 | uart_baud_rate = new_uart_baud_rate; | |
589 | } | |
590 | } | |
591 | ||
592 | if (mmio_uart_probe()) { | |
593 | gPESF = &mmio_uart_serial_functions; | |
594 | gPESF->uart_init(); | |
595 | lpss_uart_supported = 1; | |
596 | lpss_uart_enabled = 1; | |
597 | return 1; | |
598 | } else if (legacy_uart_probe()) { | |
599 | gPESF = &legacy_uart_serial_functions; | |
600 | gPESF->uart_init(); | |
601 | legacy_uart_enabled = 1; | |
602 | return 1; | |
603 | } else if (pcie_mmio_uart_probe()) { | |
604 | gPESF = &pcie_mmio_uart_serial_functions; | |
605 | gPESF->uart_init(); | |
606 | pcie_uart_enabled = 1; | |
607 | return 1; | |
608 | } else { | |
609 | return 0; | |
610 | } | |
813fb2f6 A |
611 | } |
612 | ||
613 | static void | |
614 | uart_putc(char c) | |
615 | { | |
d9a64523 | 616 | if (uart_initted && (legacy_uart_enabled || lpss_uart_enabled || pcie_uart_enabled)) { |
0a7de745 A |
617 | while (!gPESF->tr0()) { |
618 | ; /* Wait until THR is empty. */ | |
619 | } | |
620 | gPESF->td0(c); | |
621 | } | |
813fb2f6 A |
622 | } |
623 | ||
624 | static int | |
625 | uart_getc(void) | |
626 | { | |
0a7de745 A |
627 | if (uart_initted && (legacy_uart_enabled || lpss_uart_enabled || pcie_uart_enabled)) { |
628 | if (!gPESF->rr0()) { | |
629 | return -1; | |
630 | } | |
631 | return gPESF->rd0(); | |
632 | } | |
633 | return -1; | |
55e303ae A |
634 | } |
635 | ||
813fb2f6 A |
636 | void |
637 | serial_putc( char c ) | |
55e303ae | 638 | { |
0a7de745 | 639 | uart_putc(c); |
55e303ae A |
640 | } |
641 | ||
813fb2f6 A |
642 | int |
643 | serial_getc( void ) | |
55e303ae | 644 | { |
0a7de745 | 645 | return uart_getc(); |
55e303ae | 646 | } |