1*0f4c859eSApple OSS Distributions /*
2*0f4c859eSApple OSS Distributions * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
3*0f4c859eSApple OSS Distributions *
4*0f4c859eSApple OSS Distributions * This file contains the low-level serial drivers used on ARM/ARM64 devices.
5*0f4c859eSApple OSS Distributions * The generic serial console code in osfmk/console/serial_console.c will call
6*0f4c859eSApple OSS Distributions * into this code to transmit and receive serial data.
7*0f4c859eSApple OSS Distributions *
8*0f4c859eSApple OSS Distributions * Logging can be performed on multiple serial interfaces at once through a
9*0f4c859eSApple OSS Distributions * method called serial multiplexing. This is implemented by enumerating which
10*0f4c859eSApple OSS Distributions * serial interfaces are available on boot and registering them into a linked
11*0f4c859eSApple OSS Distributions * list of interfaces pointed to by gPESF. When outputting or receiving
12*0f4c859eSApple OSS Distributions * characters, each interface is queried in turn.
13*0f4c859eSApple OSS Distributions *
14*0f4c859eSApple OSS Distributions * Please view doc/arm_serial.md for an in-depth description of these drivers.
15*0f4c859eSApple OSS Distributions */
16*0f4c859eSApple OSS Distributions #include <kern/clock.h>
17*0f4c859eSApple OSS Distributions #include <kern/debug.h>
18*0f4c859eSApple OSS Distributions #include <libkern/OSBase.h>
19*0f4c859eSApple OSS Distributions #include <libkern/section_keywords.h>
20*0f4c859eSApple OSS Distributions #include <mach/mach_time.h>
21*0f4c859eSApple OSS Distributions #include <machine/atomic.h>
22*0f4c859eSApple OSS Distributions #include <machine/machine_routines.h>
23*0f4c859eSApple OSS Distributions #include <pexpert/pexpert.h>
24*0f4c859eSApple OSS Distributions #include <pexpert/protos.h>
25*0f4c859eSApple OSS Distributions #include <pexpert/device_tree.h>
26*0f4c859eSApple OSS Distributions #include <pexpert/arm/consistent_debug.h>
27*0f4c859eSApple OSS Distributions #include <pexpert/arm64/board_config.h>
28*0f4c859eSApple OSS Distributions #include <arm64/proc_reg.h>
29*0f4c859eSApple OSS Distributions #include <pexpert/arm/protos.h>
30*0f4c859eSApple OSS Distributions #include <kern/sched_prim.h>
31*0f4c859eSApple OSS Distributions #if HIBERNATION
32*0f4c859eSApple OSS Distributions #include <machine/pal_hibernate.h>
33*0f4c859eSApple OSS Distributions #endif /* HIBERNATION */
34*0f4c859eSApple OSS Distributions
35*0f4c859eSApple OSS Distributions /**
36*0f4c859eSApple OSS Distributions * Fun and helpful icon to use for announcements regarding serial deprecation,
37*0f4c859eSApple OSS Distributions * disablement. This helps catch people's eye to get them to realize the message
38*0f4c859eSApple OSS Distributions * is something other than normal serial spew. Please don't remove. :)
39*0f4c859eSApple OSS Distributions */
40*0f4c859eSApple OSS Distributions const char hexley[] =
41*0f4c859eSApple OSS Distributions " \n"
42*0f4c859eSApple OSS Distributions " %( \n"
43*0f4c859eSApple OSS Distributions " #% /((( %(& #((( \n"
44*0f4c859eSApple OSS Distributions " *(((( #(((( &(((& %((((((((((&((((((((% \n"
45*0f4c859eSApple OSS Distributions " %(. (% *(( %((((((((((%((((((((((((# \n"
46*0f4c859eSApple OSS Distributions " *(% (( #(( (((@(%((((((((%((#((((((((. \n"
47*0f4c859eSApple OSS Distributions " &(& ((# ((& (( ((((((((((@(%((((((((((% \n"
48*0f4c859eSApple OSS Distributions " &((, ((% &((# & (((((/ %(%(((((((((((((((( \n"
49*0f4c859eSApple OSS Distributions " &((((&#,%((#&%((((/ @((& &((( (&(((((((#((((((( \n"
50*0f4c859eSApple OSS Distributions " %#((((((((%# *## (((((((((@ %@@((&((((((((&((& \n"
51*0f4c859eSApple OSS Distributions " ((/ &##&######%( @&((((((((((((((% \n"
52*0f4c859eSApple OSS Distributions " ((& ###########&#((((&(((((((((& \n"
53*0f4c859eSApple OSS Distributions " #(% /##########%%%(((((((&/ \n"
54*0f4c859eSApple OSS Distributions " &(( &%///&@,##&(((((((( \n"
55*0f4c859eSApple OSS Distributions " (((((( .#&/##@((( ((((% \n"
56*0f4c859eSApple OSS Distributions " #(((%(((, ((((*.....*(((((& (((((% \n"
57*0f4c859eSApple OSS Distributions " %(&&(((((((((#.......((((((((( &#%% \n"
58*0f4c859eSApple OSS Distributions " ((# (((((((@......../(%(((((((( \n"
59*0f4c859eSApple OSS Distributions " %(( (((((@..........((((@(((((( \n"
60*0f4c859eSApple OSS Distributions " &(( ............(((((((((, \n"
61*0f4c859eSApple OSS Distributions " #((# /............(((((# \n"
62*0f4c859eSApple OSS Distributions " ((& .............(((((( \n"
63*0f4c859eSApple OSS Distributions " ((# ((...........#((((((% \n"
64*0f4c859eSApple OSS Distributions " ((( @(((((.......#(((((((( .@&&@/ \n"
65*0f4c859eSApple OSS Distributions " %((. @(((((((#.@((((((((#(@((((((((((#(@ \n"
66*0f4c859eSApple OSS Distributions " &((&@##@@((((((((&((((((((@#(((((((((&. \n"
67*0f4c859eSApple OSS Distributions " *((& ##&( /##(&#((#(& \n"
68*0f4c859eSApple OSS Distributions " %##@&### \n\n";
69*0f4c859eSApple OSS Distributions
70*0f4c859eSApple OSS Distributions struct pe_serial_functions {
71*0f4c859eSApple OSS Distributions /* Initialize the underlying serial hardware. */
72*0f4c859eSApple OSS Distributions void (*init) (void);
73*0f4c859eSApple OSS Distributions
74*0f4c859eSApple OSS Distributions /* Return a non-zero value if the serial interface is ready to send more data. */
75*0f4c859eSApple OSS Distributions unsigned int (*transmit_ready) (void);
76*0f4c859eSApple OSS Distributions
77*0f4c859eSApple OSS Distributions /* Write a single byte of data to serial. */
78*0f4c859eSApple OSS Distributions void (*transmit_data) (uint8_t c);
79*0f4c859eSApple OSS Distributions
80*0f4c859eSApple OSS Distributions /* Return a non-zero value if there's a byte of data available. */
81*0f4c859eSApple OSS Distributions unsigned int (*receive_ready) (void);
82*0f4c859eSApple OSS Distributions
83*0f4c859eSApple OSS Distributions /* Read a single byte from serial. */
84*0f4c859eSApple OSS Distributions uint8_t (*receive_data) (void);
85*0f4c859eSApple OSS Distributions
86*0f4c859eSApple OSS Distributions /* Enables IRQs from this device. */
87*0f4c859eSApple OSS Distributions void (*enable_irq) (void);
88*0f4c859eSApple OSS Distributions
89*0f4c859eSApple OSS Distributions /* Disables IRQs from this device and reports whether IRQs were enabled. */
90*0f4c859eSApple OSS Distributions bool (*disable_irq) (void);
91*0f4c859eSApple OSS Distributions
92*0f4c859eSApple OSS Distributions /* Clears this device's IRQs targeting this agent, returning true if at least one IRQ was cleared. */
93*0f4c859eSApple OSS Distributions bool (*acknowledge_irq) (void);
94*0f4c859eSApple OSS Distributions
95*0f4c859eSApple OSS Distributions /**
96*0f4c859eSApple OSS Distributions * Whether this serial driver can handle irqs. This value should be set by
97*0f4c859eSApple OSS Distributions * querying the device tree to see if the serial device has interrupts
98*0f4c859eSApple OSS Distributions * associated with it.
99*0f4c859eSApple OSS Distributions *
100*0f4c859eSApple OSS Distributions * For a device to support IRQs:
101*0f4c859eSApple OSS Distributions * - enable_irq, disable_irq, and acknowledge_irq must be non-null
102*0f4c859eSApple OSS Distributions * - The AppleSerialShim kext must be able to match to the serial device
103*0f4c859eSApple OSS Distributions * in the IORegistry and call serial_enable_irq with the proper
104*0f4c859eSApple OSS Distributions * serial_device_t
105*0f4c859eSApple OSS Distributions * - The device tree entry for the serial device should have an interrupt
106*0f4c859eSApple OSS Distributions * associated with it.
107*0f4c859eSApple OSS Distributions */
108*0f4c859eSApple OSS Distributions bool has_irq;
109*0f4c859eSApple OSS Distributions
110*0f4c859eSApple OSS Distributions /* enum identifying which serial device these functions belong to. */
111*0f4c859eSApple OSS Distributions serial_device_t device;
112*0f4c859eSApple OSS Distributions
113*0f4c859eSApple OSS Distributions /* Pointer to the next serial interface in the linked-list. */
114*0f4c859eSApple OSS Distributions struct pe_serial_functions *next;
115*0f4c859eSApple OSS Distributions };
116*0f4c859eSApple OSS Distributions
117*0f4c859eSApple OSS Distributions MARK_AS_HIBERNATE_DATA_CONST_LATE static struct pe_serial_functions* gPESF = NULL;
118*0f4c859eSApple OSS Distributions
119*0f4c859eSApple OSS Distributions /**
120*0f4c859eSApple OSS Distributions * Whether uart has been initialized already. This value is kept across a
121*0f4c859eSApple OSS Distributions * sleep/wake cycle so we know we need to reinitialize when serial_init is
122*0f4c859eSApple OSS Distributions * called again after wake.
123*0f4c859eSApple OSS Distributions */
124*0f4c859eSApple OSS Distributions MARK_AS_HIBERNATE_DATA static bool uart_initted = false;
125*0f4c859eSApple OSS Distributions
126*0f4c859eSApple OSS Distributions /* Whether uart should run in simple mode that works during hibernation resume. */
127*0f4c859eSApple OSS Distributions MARK_AS_HIBERNATE_DATA static bool uart_hibernation = false;
128*0f4c859eSApple OSS Distributions
129*0f4c859eSApple OSS Distributions /** Set <=> transmission is authorized.
130*0f4c859eSApple OSS Distributions * Always set, unless SERIALMODE_ON_DEMAND is provided at boot,
131*0f4c859eSApple OSS Distributions * and no data has yet been received.
132*0f4c859eSApple OSS Distributions * Originaly meant to be a per-pe_serial_functions variable,
133*0f4c859eSApple OSS Distributions * but the data protection on the structs prevents it. */
134*0f4c859eSApple OSS Distributions static bool serial_do_transmit = 1;
135*0f4c859eSApple OSS Distributions
136*0f4c859eSApple OSS Distributions /**
137*0f4c859eSApple OSS Distributions * Used to track if all IRQs have been initialized. Each bit of this variable
138*0f4c859eSApple OSS Distributions * represents whether or not a serial device that reports supporting IRQs has
139*0f4c859eSApple OSS Distributions * been initialized yet (1 -> not initialized, 0 -> initialized)
140*0f4c859eSApple OSS Distributions */
141*0f4c859eSApple OSS Distributions static uint32_t serial_irq_status = 0;
142*0f4c859eSApple OSS Distributions
143*0f4c859eSApple OSS Distributions /**
144*0f4c859eSApple OSS Distributions * Set by the 'disable-uart-irq' boot-arg to force serial IRQs into polling mode
145*0f4c859eSApple OSS Distributions * by preventing the serial driver shim kext from registering itself with
146*0f4c859eSApple OSS Distributions * serial_enable_irq.
147*0f4c859eSApple OSS Distributions */
148*0f4c859eSApple OSS Distributions static bool disable_uart_irq = 0;
149*0f4c859eSApple OSS Distributions
150*0f4c859eSApple OSS Distributions /**
151*0f4c859eSApple OSS Distributions * Indicates whether or not a given device's irqs have been set up by calling
152*0f4c859eSApple OSS Distributions * serial_enable_irq for that particular device.
153*0f4c859eSApple OSS Distributions *
154*0f4c859eSApple OSS Distributions * @param device_fns Serial functions for the device that is being checked
155*0f4c859eSApple OSS Distributions * @return Whether or not the irqs have been initialized for that device
156*0f4c859eSApple OSS Distributions */
157*0f4c859eSApple OSS Distributions static bool
irq_initialized(struct pe_serial_functions * device_fns)158*0f4c859eSApple OSS Distributions irq_initialized(struct pe_serial_functions *device_fns)
159*0f4c859eSApple OSS Distributions {
160*0f4c859eSApple OSS Distributions return (serial_irq_status & device_fns->device) == 0;
161*0f4c859eSApple OSS Distributions }
162*0f4c859eSApple OSS Distributions
163*0f4c859eSApple OSS Distributions /**
164*0f4c859eSApple OSS Distributions * Indicates whether or not a given device supports irqs and if they are ready
165*0f4c859eSApple OSS Distributions * to be used.
166*0f4c859eSApple OSS Distributions *
167*0f4c859eSApple OSS Distributions * @param device_fns Serial functions for the device that is being checked
168*0f4c859eSApple OSS Distributions * @return Whether or not the device can and will send IRQs.
169*0f4c859eSApple OSS Distributions */
170*0f4c859eSApple OSS Distributions static bool
irq_available_and_ready(struct pe_serial_functions * device_fns)171*0f4c859eSApple OSS Distributions irq_available_and_ready(struct pe_serial_functions *device_fns)
172*0f4c859eSApple OSS Distributions {
173*0f4c859eSApple OSS Distributions return device_fns->has_irq && irq_initialized(device_fns);
174*0f4c859eSApple OSS Distributions }
175*0f4c859eSApple OSS Distributions
176*0f4c859eSApple OSS Distributions /**
177*0f4c859eSApple OSS Distributions * Searches through the global serial functions list and returns the serial function for a particular device
178*0f4c859eSApple OSS Distributions *
179*0f4c859eSApple OSS Distributions * @param device The device identifier to search for
180*0f4c859eSApple OSS Distributions * @return Serial functions for the specified device
181*0f4c859eSApple OSS Distributions */
182*0f4c859eSApple OSS Distributions static struct pe_serial_functions *
get_serial_functions(serial_device_t device)183*0f4c859eSApple OSS Distributions get_serial_functions(serial_device_t device)
184*0f4c859eSApple OSS Distributions {
185*0f4c859eSApple OSS Distributions struct pe_serial_functions *fns = gPESF;
186*0f4c859eSApple OSS Distributions while (fns != NULL) {
187*0f4c859eSApple OSS Distributions if (fns->device == device) {
188*0f4c859eSApple OSS Distributions return fns;
189*0f4c859eSApple OSS Distributions }
190*0f4c859eSApple OSS Distributions fns = fns->next;
191*0f4c859eSApple OSS Distributions }
192*0f4c859eSApple OSS Distributions return NULL;
193*0f4c859eSApple OSS Distributions }
194*0f4c859eSApple OSS Distributions
195*0f4c859eSApple OSS Distributions /**
196*0f4c859eSApple OSS Distributions * The action to take when polling and waiting for a serial device to be ready
197*0f4c859eSApple OSS Distributions * for output. On ARM64, takes a WFE because the WFE timeout will wake us up in
198*0f4c859eSApple OSS Distributions * the worst case. On ARMv7 devices, we need to hot poll.
199*0f4c859eSApple OSS Distributions */
200*0f4c859eSApple OSS Distributions static inline void
serial_poll(void)201*0f4c859eSApple OSS Distributions serial_poll(void)
202*0f4c859eSApple OSS Distributions {
203*0f4c859eSApple OSS Distributions #if __arm64__
204*0f4c859eSApple OSS Distributions if (!uart_hibernation) {
205*0f4c859eSApple OSS Distributions __builtin_arm_wfe();
206*0f4c859eSApple OSS Distributions }
207*0f4c859eSApple OSS Distributions #endif
208*0f4c859eSApple OSS Distributions }
209*0f4c859eSApple OSS Distributions
210*0f4c859eSApple OSS Distributions /**
211*0f4c859eSApple OSS Distributions * This ensures that if we have a future product that supports hibernation, but
212*0f4c859eSApple OSS Distributions * doesn't support either UART serial or dock-channels, then hibernation will
213*0f4c859eSApple OSS Distributions * gracefully fall back to the serial method that is supported.
214*0f4c859eSApple OSS Distributions */
215*0f4c859eSApple OSS Distributions #if HIBERNATION || defined(APPLE_UART)
216*0f4c859eSApple OSS Distributions MARK_AS_HIBERNATE_DATA static volatile apple_uart_registers_t *apple_uart_registers = 0;
217*0f4c859eSApple OSS Distributions #endif /* HIBERNATION || defined(APPLE_UART) */
218*0f4c859eSApple OSS Distributions
219*0f4c859eSApple OSS Distributions #if HIBERNATION || defined(DOCKCHANNEL_UART)
220*0f4c859eSApple OSS Distributions MARK_AS_HIBERNATE_DATA static vm_offset_t dockchannel_uart_base = 0;
221*0f4c859eSApple OSS Distributions #endif /* HIBERNATION || defined(DOCKCHANNEL_UART) */
222*0f4c859eSApple OSS Distributions
223*0f4c859eSApple OSS Distributions /*****************************************************************************/
224*0f4c859eSApple OSS Distributions
225*0f4c859eSApple OSS Distributions #ifdef APPLE_UART
226*0f4c859eSApple OSS Distributions static int32_t dt_sampling = -1;
227*0f4c859eSApple OSS Distributions static int32_t dt_ubrdiv = -1;
228*0f4c859eSApple OSS Distributions
229*0f4c859eSApple OSS Distributions static void apple_uart_set_baud_rate(uint32_t baud_rate);
230*0f4c859eSApple OSS Distributions
231*0f4c859eSApple OSS Distributions /**
232*0f4c859eSApple OSS Distributions * The Apple UART is configured to use 115200-8-N-1 communication.
233*0f4c859eSApple OSS Distributions */
234*0f4c859eSApple OSS Distributions static void
apple_uart_init(void)235*0f4c859eSApple OSS Distributions apple_uart_init(void)
236*0f4c859eSApple OSS Distributions {
237*0f4c859eSApple OSS Distributions ucon_t ucon = { .raw = 0 };
238*0f4c859eSApple OSS Distributions // Use NCLK (which is constant) instead of PCLK (which is variable).
239*0f4c859eSApple OSS Distributions ucon.clock_selection = UCON_CLOCK_SELECTION_NCLK;
240*0f4c859eSApple OSS Distributions ucon.transmit_mode = UCON_TRANSMIT_MODE_INTERRUPT_OR_POLLING;
241*0f4c859eSApple OSS Distributions ucon.receive_mode = UCON_RECEIVE_MODE_INTERRUPT_OR_POLLING;
242*0f4c859eSApple OSS Distributions apple_uart_registers->ucon = ucon;
243*0f4c859eSApple OSS Distributions
244*0f4c859eSApple OSS Distributions // Configure 8-N-1 communication.
245*0f4c859eSApple OSS Distributions ulcon_t ulcon = { .raw = 0 };
246*0f4c859eSApple OSS Distributions ulcon.word_length = ULCON_WORD_LENGTH_8_BITS;
247*0f4c859eSApple OSS Distributions ulcon.parity_mode = ULCON_PARITY_MODE_NONE;
248*0f4c859eSApple OSS Distributions ulcon.number_of_stop_bits = ULCON_STOP_BITS_1;
249*0f4c859eSApple OSS Distributions apple_uart_registers->ulcon = ulcon;
250*0f4c859eSApple OSS Distributions
251*0f4c859eSApple OSS Distributions apple_uart_set_baud_rate(115200);
252*0f4c859eSApple OSS Distributions
253*0f4c859eSApple OSS Distributions // Enable and reset FIFOs.
254*0f4c859eSApple OSS Distributions ufcon_t ufcon = { .raw = 0 };
255*0f4c859eSApple OSS Distributions ufcon.fifo_enable = 1;
256*0f4c859eSApple OSS Distributions ufcon.tx_fifo_reset = 1;
257*0f4c859eSApple OSS Distributions ufcon.rx_fifo_reset = 1;
258*0f4c859eSApple OSS Distributions apple_uart_registers->ufcon = ufcon;
259*0f4c859eSApple OSS Distributions }
260*0f4c859eSApple OSS Distributions
261*0f4c859eSApple OSS Distributions static void
apple_uart_enable_irq(void)262*0f4c859eSApple OSS Distributions apple_uart_enable_irq(void)
263*0f4c859eSApple OSS Distributions {
264*0f4c859eSApple OSS Distributions // Set the Tx FIFO interrupt trigger level to 0 bytes so interrupts occur when
265*0f4c859eSApple OSS Distributions // the Tx FIFO is completely empty; this leads to higher Tx throughput.
266*0f4c859eSApple OSS Distributions apple_uart_registers->ufcon.tx_fifo_interrupt_trigger_level_dma_watermark = UFCON_TX_FIFO_ITL_0_BYTES;
267*0f4c859eSApple OSS Distributions
268*0f4c859eSApple OSS Distributions // Enable Tx interrupts.
269*0f4c859eSApple OSS Distributions apple_uart_registers->ucon.transmit_interrupt = 1;
270*0f4c859eSApple OSS Distributions }
271*0f4c859eSApple OSS Distributions
272*0f4c859eSApple OSS Distributions static bool
apple_uart_disable_irq(void)273*0f4c859eSApple OSS Distributions apple_uart_disable_irq(void)
274*0f4c859eSApple OSS Distributions {
275*0f4c859eSApple OSS Distributions /* Disables Tx interrupts */
276*0f4c859eSApple OSS Distributions ucon_t ucon = apple_uart_registers->ucon;
277*0f4c859eSApple OSS Distributions const bool irqs_were_enabled = ucon.transmit_interrupt;
278*0f4c859eSApple OSS Distributions
279*0f4c859eSApple OSS Distributions if (irqs_were_enabled) {
280*0f4c859eSApple OSS Distributions ucon.transmit_interrupt = 0;
281*0f4c859eSApple OSS Distributions apple_uart_registers->ucon = ucon;
282*0f4c859eSApple OSS Distributions }
283*0f4c859eSApple OSS Distributions
284*0f4c859eSApple OSS Distributions return irqs_were_enabled;
285*0f4c859eSApple OSS Distributions }
286*0f4c859eSApple OSS Distributions
287*0f4c859eSApple OSS Distributions static bool
apple_uart_ack_irq(void)288*0f4c859eSApple OSS Distributions apple_uart_ack_irq(void)
289*0f4c859eSApple OSS Distributions {
290*0f4c859eSApple OSS Distributions apple_uart_registers->utrstat.transmit_interrupt_status = 1;
291*0f4c859eSApple OSS Distributions return true;
292*0f4c859eSApple OSS Distributions }
293*0f4c859eSApple OSS Distributions
294*0f4c859eSApple OSS Distributions static inline bool
apple_uart_fifo_is_empty(void)295*0f4c859eSApple OSS Distributions apple_uart_fifo_is_empty(void)
296*0f4c859eSApple OSS Distributions {
297*0f4c859eSApple OSS Distributions const ufstat_t ufstat = apple_uart_registers->ufstat;
298*0f4c859eSApple OSS Distributions return !(ufstat.tx_fifo_full || ufstat.tx_fifo_count);
299*0f4c859eSApple OSS Distributions }
300*0f4c859eSApple OSS Distributions
301*0f4c859eSApple OSS Distributions static void
apple_uart_drain_fifo(void)302*0f4c859eSApple OSS Distributions apple_uart_drain_fifo(void)
303*0f4c859eSApple OSS Distributions {
304*0f4c859eSApple OSS Distributions while (!apple_uart_fifo_is_empty()) {
305*0f4c859eSApple OSS Distributions serial_poll();
306*0f4c859eSApple OSS Distributions }
307*0f4c859eSApple OSS Distributions }
308*0f4c859eSApple OSS Distributions
309*0f4c859eSApple OSS Distributions static void
apple_uart_set_baud_rate(uint32_t baud_rate)310*0f4c859eSApple OSS Distributions apple_uart_set_baud_rate(uint32_t baud_rate)
311*0f4c859eSApple OSS Distributions {
312*0f4c859eSApple OSS Distributions uint32_t div = 0;
313*0f4c859eSApple OSS Distributions const uint32_t uart_clock = (uint32_t)gPEClockFrequencyInfo.fix_frequency_hz;
314*0f4c859eSApple OSS Distributions uint32_t sample_rate = 16;
315*0f4c859eSApple OSS Distributions
316*0f4c859eSApple OSS Distributions if (baud_rate < 300) {
317*0f4c859eSApple OSS Distributions baud_rate = 9600;
318*0f4c859eSApple OSS Distributions }
319*0f4c859eSApple OSS Distributions
320*0f4c859eSApple OSS Distributions if (dt_sampling != -1) {
321*0f4c859eSApple OSS Distributions // Use the sampling rate specified in the Device Tree
322*0f4c859eSApple OSS Distributions sample_rate = dt_sampling & 0xf;
323*0f4c859eSApple OSS Distributions }
324*0f4c859eSApple OSS Distributions
325*0f4c859eSApple OSS Distributions if (dt_ubrdiv != -1) {
326*0f4c859eSApple OSS Distributions // Use the ubrdiv specified in the Device Tree
327*0f4c859eSApple OSS Distributions div = dt_ubrdiv & 0xffff;
328*0f4c859eSApple OSS Distributions } else {
329*0f4c859eSApple OSS Distributions // Calculate ubrdiv. UBRDIV = (SourceClock / (BPS * Sample Rate)) - 1
330*0f4c859eSApple OSS Distributions div = uart_clock / (baud_rate * sample_rate);
331*0f4c859eSApple OSS Distributions
332*0f4c859eSApple OSS Distributions uint32_t actual_baud = uart_clock / ((div + 0) * sample_rate);
333*0f4c859eSApple OSS Distributions uint32_t baud_low = uart_clock / ((div + 1) * sample_rate);
334*0f4c859eSApple OSS Distributions
335*0f4c859eSApple OSS Distributions // Adjust div to get the closest target baudrate
336*0f4c859eSApple OSS Distributions if ((baud_rate - baud_low) > (actual_baud - baud_rate)) {
337*0f4c859eSApple OSS Distributions div--;
338*0f4c859eSApple OSS Distributions }
339*0f4c859eSApple OSS Distributions }
340*0f4c859eSApple OSS Distributions
341*0f4c859eSApple OSS Distributions ubrdiv_t ubrdiv = apple_uart_registers->ubrdiv;
342*0f4c859eSApple OSS Distributions ubrdiv.sample_rate = 16 - sample_rate;
343*0f4c859eSApple OSS Distributions ubrdiv.ubr_div = div;
344*0f4c859eSApple OSS Distributions apple_uart_registers->ubrdiv = ubrdiv;
345*0f4c859eSApple OSS Distributions }
346*0f4c859eSApple OSS Distributions
347*0f4c859eSApple OSS Distributions MARK_AS_HIBERNATE_TEXT static unsigned int
apple_uart_transmit_ready(void)348*0f4c859eSApple OSS Distributions apple_uart_transmit_ready(void)
349*0f4c859eSApple OSS Distributions {
350*0f4c859eSApple OSS Distributions return !apple_uart_registers->ufstat.tx_fifo_full;
351*0f4c859eSApple OSS Distributions }
352*0f4c859eSApple OSS Distributions
353*0f4c859eSApple OSS Distributions MARK_AS_HIBERNATE_TEXT static void
apple_uart_transmit_data(uint8_t c)354*0f4c859eSApple OSS Distributions apple_uart_transmit_data(uint8_t c)
355*0f4c859eSApple OSS Distributions {
356*0f4c859eSApple OSS Distributions apple_uart_registers->utxh.txdata = c;
357*0f4c859eSApple OSS Distributions }
358*0f4c859eSApple OSS Distributions
359*0f4c859eSApple OSS Distributions static unsigned int
apple_uart_receive_ready(void)360*0f4c859eSApple OSS Distributions apple_uart_receive_ready(void)
361*0f4c859eSApple OSS Distributions {
362*0f4c859eSApple OSS Distributions const ufstat_t ufstat = apple_uart_registers->ufstat;
363*0f4c859eSApple OSS Distributions return ufstat.rx_fifo_full || ufstat.rx_fifo_count;
364*0f4c859eSApple OSS Distributions }
365*0f4c859eSApple OSS Distributions
366*0f4c859eSApple OSS Distributions static uint8_t
apple_uart_receive_data(void)367*0f4c859eSApple OSS Distributions apple_uart_receive_data(void)
368*0f4c859eSApple OSS Distributions {
369*0f4c859eSApple OSS Distributions return apple_uart_registers->urxh.rxdata;
370*0f4c859eSApple OSS Distributions }
371*0f4c859eSApple OSS Distributions
372*0f4c859eSApple OSS Distributions MARK_AS_HIBERNATE_DATA_CONST_LATE
373*0f4c859eSApple OSS Distributions static struct pe_serial_functions apple_serial_functions =
374*0f4c859eSApple OSS Distributions {
375*0f4c859eSApple OSS Distributions .init = apple_uart_init,
376*0f4c859eSApple OSS Distributions .transmit_ready = apple_uart_transmit_ready,
377*0f4c859eSApple OSS Distributions .transmit_data = apple_uart_transmit_data,
378*0f4c859eSApple OSS Distributions .receive_ready = apple_uart_receive_ready,
379*0f4c859eSApple OSS Distributions .receive_data = apple_uart_receive_data,
380*0f4c859eSApple OSS Distributions .enable_irq = apple_uart_enable_irq,
381*0f4c859eSApple OSS Distributions .disable_irq = apple_uart_disable_irq,
382*0f4c859eSApple OSS Distributions .acknowledge_irq = apple_uart_ack_irq,
383*0f4c859eSApple OSS Distributions .device = SERIAL_APPLE_UART
384*0f4c859eSApple OSS Distributions };
385*0f4c859eSApple OSS Distributions
386*0f4c859eSApple OSS Distributions #endif /* APPLE_UART */
387*0f4c859eSApple OSS Distributions
388*0f4c859eSApple OSS Distributions /*****************************************************************************/
389*0f4c859eSApple OSS Distributions
390*0f4c859eSApple OSS Distributions #ifdef DOCKCHANNEL_UART
391*0f4c859eSApple OSS Distributions #define DOCKCHANNEL_WR_MAX_STALL_US (30*1000)
392*0f4c859eSApple OSS Distributions
393*0f4c859eSApple OSS Distributions static vm_offset_t dock_agent_base;
394*0f4c859eSApple OSS Distributions static uint32_t max_dockchannel_drain_period;
395*0f4c859eSApple OSS Distributions static uint64_t dockchannel_drain_deadline; // Deadline for external agent to drain before a software drain occurs
396*0f4c859eSApple OSS Distributions static bool use_sw_drain;
397*0f4c859eSApple OSS Distributions static uint32_t dock_wstat_mask;
398*0f4c859eSApple OSS Distributions static uint64_t prev_dockchannel_spaces; // Previous w_stat level of the DockChannel.
399*0f4c859eSApple OSS Distributions static uint64_t dockchannel_stall_grace;
400*0f4c859eSApple OSS Distributions MARK_AS_HIBERNATE_DATA static bool use_sw_drain;
401*0f4c859eSApple OSS Distributions MARK_AS_HIBERNATE_DATA static uint32_t dock_wstat_mask;
402*0f4c859eSApple OSS Distributions
403*0f4c859eSApple OSS Distributions // forward reference
404*0f4c859eSApple OSS Distributions static struct pe_serial_functions dockchannel_serial_functions;
405*0f4c859eSApple OSS Distributions
406*0f4c859eSApple OSS Distributions //=======================
407*0f4c859eSApple OSS Distributions // Local funtions
408*0f4c859eSApple OSS Distributions //=======================
409*0f4c859eSApple OSS Distributions
410*0f4c859eSApple OSS Distributions static int
dockchannel_drain_on_stall()411*0f4c859eSApple OSS Distributions dockchannel_drain_on_stall()
412*0f4c859eSApple OSS Distributions {
413*0f4c859eSApple OSS Distributions // Called when DockChannel runs out of spaces.
414*0f4c859eSApple OSS Distributions // Check if the DockChannel reader has stalled. If so, empty the DockChannel ourselves.
415*0f4c859eSApple OSS Distributions // Return number of bytes drained.
416*0f4c859eSApple OSS Distributions
417*0f4c859eSApple OSS Distributions if (mach_absolute_time() >= dockchannel_drain_deadline) {
418*0f4c859eSApple OSS Distributions // It's been more than DOCKCHANEL_WR_MAX_STALL_US and nobody read from the FIFO
419*0f4c859eSApple OSS Distributions // Drop a character.
420*0f4c859eSApple OSS Distributions (void)rDOCKCHANNELS_DOCK_RDATA1(DOCKCHANNEL_UART_CHANNEL);
421*0f4c859eSApple OSS Distributions os_atomic_inc(&prev_dockchannel_spaces, relaxed);
422*0f4c859eSApple OSS Distributions return 1;
423*0f4c859eSApple OSS Distributions }
424*0f4c859eSApple OSS Distributions return 0;
425*0f4c859eSApple OSS Distributions }
426*0f4c859eSApple OSS Distributions
427*0f4c859eSApple OSS Distributions static void
dockchannel_clear_intr(void)428*0f4c859eSApple OSS Distributions dockchannel_clear_intr(void)
429*0f4c859eSApple OSS Distributions {
430*0f4c859eSApple OSS Distributions rDOCKCHANNELS_AGENT_AP_INTR_CTRL &= ~(0x3);
431*0f4c859eSApple OSS Distributions rDOCKCHANNELS_AGENT_AP_INTR_STATUS |= 0x3;
432*0f4c859eSApple OSS Distributions rDOCKCHANNELS_AGENT_AP_ERR_INTR_CTRL &= ~(0x3);
433*0f4c859eSApple OSS Distributions rDOCKCHANNELS_AGENT_AP_ERR_INTR_STATUS |= 0x3;
434*0f4c859eSApple OSS Distributions }
435*0f4c859eSApple OSS Distributions
436*0f4c859eSApple OSS Distributions static bool
dockchannel_disable_irq(void)437*0f4c859eSApple OSS Distributions dockchannel_disable_irq(void)
438*0f4c859eSApple OSS Distributions {
439*0f4c859eSApple OSS Distributions const uint32_t ap_intr_ctrl = rDOCKCHANNELS_AGENT_AP_INTR_CTRL;
440*0f4c859eSApple OSS Distributions const bool irqs_were_enabled = ap_intr_ctrl & 0x1;
441*0f4c859eSApple OSS Distributions if (irqs_were_enabled) {
442*0f4c859eSApple OSS Distributions rDOCKCHANNELS_AGENT_AP_INTR_CTRL = ap_intr_ctrl & ~(0x1);
443*0f4c859eSApple OSS Distributions }
444*0f4c859eSApple OSS Distributions return irqs_were_enabled;
445*0f4c859eSApple OSS Distributions }
446*0f4c859eSApple OSS Distributions
447*0f4c859eSApple OSS Distributions static void
dockchannel_enable_irq(void)448*0f4c859eSApple OSS Distributions dockchannel_enable_irq(void)
449*0f4c859eSApple OSS Distributions {
450*0f4c859eSApple OSS Distributions // set interrupt to be when fifo has 255 empty
451*0f4c859eSApple OSS Distributions rDOCKCHANNELS_DEV_WR_WATERMARK(DOCKCHANNEL_UART_CHANNEL) = 0xFF;
452*0f4c859eSApple OSS Distributions rDOCKCHANNELS_AGENT_AP_INTR_CTRL |= 0x1;
453*0f4c859eSApple OSS Distributions }
454*0f4c859eSApple OSS Distributions
455*0f4c859eSApple OSS Distributions static bool
dockchannel_ack_irq(void)456*0f4c859eSApple OSS Distributions dockchannel_ack_irq(void)
457*0f4c859eSApple OSS Distributions {
458*0f4c859eSApple OSS Distributions /* First check if the IRQ is for the kernel */
459*0f4c859eSApple OSS Distributions if (rDOCKCHANNELS_AGENT_AP_INTR_STATUS & 0x1) {
460*0f4c859eSApple OSS Distributions rDOCKCHANNELS_AGENT_AP_INTR_STATUS |= 0x1;
461*0f4c859eSApple OSS Distributions return true;
462*0f4c859eSApple OSS Distributions }
463*0f4c859eSApple OSS Distributions return false;
464*0f4c859eSApple OSS Distributions }
465*0f4c859eSApple OSS Distributions
466*0f4c859eSApple OSS Distributions MARK_AS_HIBERNATE_TEXT static void
dockchannel_transmit_data(uint8_t c)467*0f4c859eSApple OSS Distributions dockchannel_transmit_data(uint8_t c)
468*0f4c859eSApple OSS Distributions {
469*0f4c859eSApple OSS Distributions rDOCKCHANNELS_DEV_WDATA1(DOCKCHANNEL_UART_CHANNEL) = (unsigned)c;
470*0f4c859eSApple OSS Distributions
471*0f4c859eSApple OSS Distributions if (use_sw_drain && !uart_hibernation) {
472*0f4c859eSApple OSS Distributions os_atomic_dec(&prev_dockchannel_spaces, relaxed); // After writing a byte we have one fewer space than previously expected.
473*0f4c859eSApple OSS Distributions }
474*0f4c859eSApple OSS Distributions }
475*0f4c859eSApple OSS Distributions
476*0f4c859eSApple OSS Distributions static unsigned int
dockchannel_receive_ready(void)477*0f4c859eSApple OSS Distributions dockchannel_receive_ready(void)
478*0f4c859eSApple OSS Distributions {
479*0f4c859eSApple OSS Distributions return rDOCKCHANNELS_DEV_RDATA0(DOCKCHANNEL_UART_CHANNEL) & 0x7f;
480*0f4c859eSApple OSS Distributions }
481*0f4c859eSApple OSS Distributions
482*0f4c859eSApple OSS Distributions static uint8_t
dockchannel_receive_data(void)483*0f4c859eSApple OSS Distributions dockchannel_receive_data(void)
484*0f4c859eSApple OSS Distributions {
485*0f4c859eSApple OSS Distributions return (uint8_t)((rDOCKCHANNELS_DEV_RDATA1(DOCKCHANNEL_UART_CHANNEL) >> 8) & 0xff);
486*0f4c859eSApple OSS Distributions }
487*0f4c859eSApple OSS Distributions
488*0f4c859eSApple OSS Distributions MARK_AS_HIBERNATE_TEXT static unsigned int
dockchannel_transmit_ready(void)489*0f4c859eSApple OSS Distributions dockchannel_transmit_ready(void)
490*0f4c859eSApple OSS Distributions {
491*0f4c859eSApple OSS Distributions uint32_t spaces = rDOCKCHANNELS_DEV_WSTAT(DOCKCHANNEL_UART_CHANNEL) & dock_wstat_mask;
492*0f4c859eSApple OSS Distributions
493*0f4c859eSApple OSS Distributions if (!uart_hibernation) {
494*0f4c859eSApple OSS Distributions if (use_sw_drain) {
495*0f4c859eSApple OSS Distributions if (spaces > prev_dockchannel_spaces) {
496*0f4c859eSApple OSS Distributions // More spaces showed up. That can only mean someone read the FIFO.
497*0f4c859eSApple OSS Distributions // Note that if the DockFIFO is empty we cannot tell if someone is listening,
498*0f4c859eSApple OSS Distributions // we can only give them the benefit of the doubt.
499*0f4c859eSApple OSS Distributions dockchannel_drain_deadline = mach_absolute_time() + dockchannel_stall_grace;
500*0f4c859eSApple OSS Distributions }
501*0f4c859eSApple OSS Distributions prev_dockchannel_spaces = spaces;
502*0f4c859eSApple OSS Distributions return spaces || dockchannel_drain_on_stall();
503*0f4c859eSApple OSS Distributions }
504*0f4c859eSApple OSS Distributions }
505*0f4c859eSApple OSS Distributions
506*0f4c859eSApple OSS Distributions return spaces;
507*0f4c859eSApple OSS Distributions }
508*0f4c859eSApple OSS Distributions
509*0f4c859eSApple OSS Distributions static void
dockchannel_init(void)510*0f4c859eSApple OSS Distributions dockchannel_init(void)
511*0f4c859eSApple OSS Distributions {
512*0f4c859eSApple OSS Distributions if (use_sw_drain) {
513*0f4c859eSApple OSS Distributions nanoseconds_to_absolutetime(DOCKCHANNEL_WR_MAX_STALL_US * NSEC_PER_USEC, &dockchannel_stall_grace);
514*0f4c859eSApple OSS Distributions }
515*0f4c859eSApple OSS Distributions
516*0f4c859eSApple OSS Distributions // Clear all interrupt enable and status bits
517*0f4c859eSApple OSS Distributions dockchannel_clear_intr();
518*0f4c859eSApple OSS Distributions
519*0f4c859eSApple OSS Distributions // Setup DRAIN timer
520*0f4c859eSApple OSS Distributions rDOCKCHANNELS_DEV_DRAIN_CFG(DOCKCHANNEL_UART_CHANNEL) = max_dockchannel_drain_period;
521*0f4c859eSApple OSS Distributions
522*0f4c859eSApple OSS Distributions // Drain timer doesn't get loaded with value from drain period register if fifo
523*0f4c859eSApple OSS Distributions // is already full. Drop a character from the fifo.
524*0f4c859eSApple OSS Distributions rDOCKCHANNELS_DOCK_RDATA1(DOCKCHANNEL_UART_CHANNEL);
525*0f4c859eSApple OSS Distributions }
526*0f4c859eSApple OSS Distributions
527*0f4c859eSApple OSS Distributions MARK_AS_HIBERNATE_DATA_CONST_LATE
528*0f4c859eSApple OSS Distributions static struct pe_serial_functions dockchannel_serial_functions =
529*0f4c859eSApple OSS Distributions {
530*0f4c859eSApple OSS Distributions .init = dockchannel_init,
531*0f4c859eSApple OSS Distributions .transmit_ready = dockchannel_transmit_ready,
532*0f4c859eSApple OSS Distributions .transmit_data = dockchannel_transmit_data,
533*0f4c859eSApple OSS Distributions .receive_ready = dockchannel_receive_ready,
534*0f4c859eSApple OSS Distributions .receive_data = dockchannel_receive_data,
535*0f4c859eSApple OSS Distributions .enable_irq = dockchannel_enable_irq,
536*0f4c859eSApple OSS Distributions .disable_irq = dockchannel_disable_irq,
537*0f4c859eSApple OSS Distributions .acknowledge_irq = dockchannel_ack_irq,
538*0f4c859eSApple OSS Distributions .device = SERIAL_DOCKCHANNEL
539*0f4c859eSApple OSS Distributions };
540*0f4c859eSApple OSS Distributions
541*0f4c859eSApple OSS Distributions #endif /* DOCKCHANNEL_UART */
542*0f4c859eSApple OSS Distributions
543*0f4c859eSApple OSS Distributions /****************************************************************************/
544*0f4c859eSApple OSS Distributions #ifdef PI3_UART
545*0f4c859eSApple OSS Distributions vm_offset_t pi3_gpio_base_vaddr = 0;
546*0f4c859eSApple OSS Distributions vm_offset_t pi3_aux_base_vaddr = 0;
547*0f4c859eSApple OSS Distributions static unsigned int
pi3_uart_tr0(void)548*0f4c859eSApple OSS Distributions pi3_uart_tr0(void)
549*0f4c859eSApple OSS Distributions {
550*0f4c859eSApple OSS Distributions return (unsigned int) BCM2837_GET32(BCM2837_AUX_MU_LSR_REG_V) & 0x20;
551*0f4c859eSApple OSS Distributions }
552*0f4c859eSApple OSS Distributions
553*0f4c859eSApple OSS Distributions static void
pi3_uart_td0(uint8_t c)554*0f4c859eSApple OSS Distributions pi3_uart_td0(uint8_t c)
555*0f4c859eSApple OSS Distributions {
556*0f4c859eSApple OSS Distributions BCM2837_PUT32(BCM2837_AUX_MU_IO_REG_V, (uint32_t) c);
557*0f4c859eSApple OSS Distributions }
558*0f4c859eSApple OSS Distributions
559*0f4c859eSApple OSS Distributions static unsigned int
pi3_uart_rr0(void)560*0f4c859eSApple OSS Distributions pi3_uart_rr0(void)
561*0f4c859eSApple OSS Distributions {
562*0f4c859eSApple OSS Distributions return (unsigned int) BCM2837_GET32(BCM2837_AUX_MU_LSR_REG_V) & 0x01;
563*0f4c859eSApple OSS Distributions }
564*0f4c859eSApple OSS Distributions
565*0f4c859eSApple OSS Distributions static uint8_t
pi3_uart_rd0(void)566*0f4c859eSApple OSS Distributions pi3_uart_rd0(void)
567*0f4c859eSApple OSS Distributions {
568*0f4c859eSApple OSS Distributions return (uint8_t) BCM2837_GET32(BCM2837_AUX_MU_IO_REG_V);
569*0f4c859eSApple OSS Distributions }
570*0f4c859eSApple OSS Distributions
571*0f4c859eSApple OSS Distributions static void
pi3_uart_init(void)572*0f4c859eSApple OSS Distributions pi3_uart_init(void)
573*0f4c859eSApple OSS Distributions {
574*0f4c859eSApple OSS Distributions // Scratch variable
575*0f4c859eSApple OSS Distributions uint32_t i;
576*0f4c859eSApple OSS Distributions
577*0f4c859eSApple OSS Distributions // Reset mini uart registers
578*0f4c859eSApple OSS Distributions BCM2837_PUT32(BCM2837_AUX_ENABLES_V, 1);
579*0f4c859eSApple OSS Distributions BCM2837_PUT32(BCM2837_AUX_MU_CNTL_REG_V, 0);
580*0f4c859eSApple OSS Distributions BCM2837_PUT32(BCM2837_AUX_MU_LCR_REG_V, 3);
581*0f4c859eSApple OSS Distributions BCM2837_PUT32(BCM2837_AUX_MU_MCR_REG_V, 0);
582*0f4c859eSApple OSS Distributions BCM2837_PUT32(BCM2837_AUX_MU_IER_REG_V, 0);
583*0f4c859eSApple OSS Distributions BCM2837_PUT32(BCM2837_AUX_MU_IIR_REG_V, 0xC6);
584*0f4c859eSApple OSS Distributions BCM2837_PUT32(BCM2837_AUX_MU_BAUD_REG_V, 270);
585*0f4c859eSApple OSS Distributions
586*0f4c859eSApple OSS Distributions i = (uint32_t)BCM2837_FSEL_REG(14);
587*0f4c859eSApple OSS Distributions // Configure GPIOs 14 & 15 for alternate function 5
588*0f4c859eSApple OSS Distributions i &= ~(BCM2837_FSEL_MASK(14));
589*0f4c859eSApple OSS Distributions i |= (BCM2837_FSEL_ALT5 << BCM2837_FSEL_OFFS(14));
590*0f4c859eSApple OSS Distributions i &= ~(BCM2837_FSEL_MASK(15));
591*0f4c859eSApple OSS Distributions i |= (BCM2837_FSEL_ALT5 << BCM2837_FSEL_OFFS(15));
592*0f4c859eSApple OSS Distributions
593*0f4c859eSApple OSS Distributions BCM2837_PUT32(BCM2837_FSEL_REG(14), i);
594*0f4c859eSApple OSS Distributions
595*0f4c859eSApple OSS Distributions BCM2837_PUT32(BCM2837_GPPUD_V, 0);
596*0f4c859eSApple OSS Distributions
597*0f4c859eSApple OSS Distributions // Barrier before AP spinning for 150 cycles
598*0f4c859eSApple OSS Distributions __builtin_arm_isb(ISB_SY);
599*0f4c859eSApple OSS Distributions
600*0f4c859eSApple OSS Distributions for (i = 0; i < 150; i++) {
601*0f4c859eSApple OSS Distributions asm volatile ("add x0, x0, xzr");
602*0f4c859eSApple OSS Distributions }
603*0f4c859eSApple OSS Distributions
604*0f4c859eSApple OSS Distributions __builtin_arm_isb(ISB_SY);
605*0f4c859eSApple OSS Distributions
606*0f4c859eSApple OSS Distributions BCM2837_PUT32(BCM2837_GPPUDCLK0_V, (1 << 14) | (1 << 15));
607*0f4c859eSApple OSS Distributions
608*0f4c859eSApple OSS Distributions __builtin_arm_isb(ISB_SY);
609*0f4c859eSApple OSS Distributions
610*0f4c859eSApple OSS Distributions for (i = 0; i < 150; i++) {
611*0f4c859eSApple OSS Distributions asm volatile ("add x0, x0, xzr");
612*0f4c859eSApple OSS Distributions }
613*0f4c859eSApple OSS Distributions
614*0f4c859eSApple OSS Distributions __builtin_arm_isb(ISB_SY);
615*0f4c859eSApple OSS Distributions
616*0f4c859eSApple OSS Distributions BCM2837_PUT32(BCM2837_GPPUDCLK0_V, 0);
617*0f4c859eSApple OSS Distributions
618*0f4c859eSApple OSS Distributions BCM2837_PUT32(BCM2837_AUX_MU_CNTL_REG_V, 3);
619*0f4c859eSApple OSS Distributions }
620*0f4c859eSApple OSS Distributions
621*0f4c859eSApple OSS Distributions SECURITY_READ_ONLY_LATE(static struct pe_serial_functions) pi3_uart_serial_functions =
622*0f4c859eSApple OSS Distributions {
623*0f4c859eSApple OSS Distributions .init = pi3_uart_init,
624*0f4c859eSApple OSS Distributions .transmit_ready = pi3_uart_tr0,
625*0f4c859eSApple OSS Distributions .transmit_data = pi3_uart_td0,
626*0f4c859eSApple OSS Distributions .receive_ready = pi3_uart_rr0,
627*0f4c859eSApple OSS Distributions .receive_data = pi3_uart_rd0,
628*0f4c859eSApple OSS Distributions .device = SERIAL_PI3_UART
629*0f4c859eSApple OSS Distributions };
630*0f4c859eSApple OSS Distributions
631*0f4c859eSApple OSS Distributions #endif /* PI3_UART */
632*0f4c859eSApple OSS Distributions
633*0f4c859eSApple OSS Distributions /*****************************************************************************/
634*0f4c859eSApple OSS Distributions
635*0f4c859eSApple OSS Distributions #ifdef VMAPPLE_UART
636*0f4c859eSApple OSS Distributions
637*0f4c859eSApple OSS Distributions static vm_offset_t vmapple_uart0_base_vaddr = 0;
638*0f4c859eSApple OSS Distributions
639*0f4c859eSApple OSS Distributions #define PL011_LCR_WORD_LENGTH_8 0x60u
640*0f4c859eSApple OSS Distributions #define PL011_LCR_FIFO_DISABLE 0x00u
641*0f4c859eSApple OSS Distributions
642*0f4c859eSApple OSS Distributions #define PL011_LCR_FIFO_ENABLE 0x10u
643*0f4c859eSApple OSS Distributions
644*0f4c859eSApple OSS Distributions #define PL011_LCR_ONE_STOP_BIT 0x00u
645*0f4c859eSApple OSS Distributions #define PL011_LCR_PARITY_DISABLE 0x00u
646*0f4c859eSApple OSS Distributions #define PL011_LCR_BREAK_DISABLE 0x00u
647*0f4c859eSApple OSS Distributions #define PL011_IBRD_DIV_38400 0x27u
648*0f4c859eSApple OSS Distributions #define PL011_FBRD_DIV_38400 0x09u
649*0f4c859eSApple OSS Distributions #define PL011_ICR_CLR_ALL_IRQS 0x07ffu
650*0f4c859eSApple OSS Distributions #define PL011_CR_UART_ENABLE 0x01u
651*0f4c859eSApple OSS Distributions #define PL011_CR_TX_ENABLE 0x100u
652*0f4c859eSApple OSS Distributions #define PL011_CR_RX_ENABLE 0x200u
653*0f4c859eSApple OSS Distributions
654*0f4c859eSApple OSS Distributions #define VMAPPLE_UART0_DR *((volatile uint32_t *) (vmapple_uart0_base_vaddr + 0x00))
655*0f4c859eSApple OSS Distributions #define VMAPPLE_UART0_ECR *((volatile uint32_t *) (vmapple_uart0_base_vaddr + 0x04))
656*0f4c859eSApple OSS Distributions #define VMAPPLE_UART0_FR *((volatile uint32_t *) (vmapple_uart0_base_vaddr + 0x18))
657*0f4c859eSApple OSS Distributions #define VMAPPLE_UART0_IBRD *((volatile uint32_t *) (vmapple_uart0_base_vaddr + 0x24))
658*0f4c859eSApple OSS Distributions #define VMAPPLE_UART0_FBRD *((volatile uint32_t *) (vmapple_uart0_base_vaddr + 0x28))
659*0f4c859eSApple OSS Distributions #define VMAPPLE_UART0_LCR_H *((volatile uint32_t *) (vmapple_uart0_base_vaddr + 0x2c))
660*0f4c859eSApple OSS Distributions #define VMAPPLE_UART0_CR *((volatile uint32_t *) (vmapple_uart0_base_vaddr + 0x30))
661*0f4c859eSApple OSS Distributions #define VMAPPLE_UART0_TIMSC *((volatile uint32_t *) (vmapple_uart0_base_vaddr + 0x38))
662*0f4c859eSApple OSS Distributions #define VMAPPLE_UART0_ICR *((volatile uint32_t *) (vmapple_uart0_base_vaddr + 0x44))
663*0f4c859eSApple OSS Distributions
664*0f4c859eSApple OSS Distributions static unsigned int
vmapple_uart_transmit_ready(void)665*0f4c859eSApple OSS Distributions vmapple_uart_transmit_ready(void)
666*0f4c859eSApple OSS Distributions {
667*0f4c859eSApple OSS Distributions return (unsigned int) !(VMAPPLE_UART0_FR & 0x20);
668*0f4c859eSApple OSS Distributions }
669*0f4c859eSApple OSS Distributions
670*0f4c859eSApple OSS Distributions static void
vmapple_uart_transmit_data(uint8_t c)671*0f4c859eSApple OSS Distributions vmapple_uart_transmit_data(uint8_t c)
672*0f4c859eSApple OSS Distributions {
673*0f4c859eSApple OSS Distributions VMAPPLE_UART0_DR = (uint32_t) c;
674*0f4c859eSApple OSS Distributions }
675*0f4c859eSApple OSS Distributions
676*0f4c859eSApple OSS Distributions static unsigned int
vmapple_uart_receive_ready(void)677*0f4c859eSApple OSS Distributions vmapple_uart_receive_ready(void)
678*0f4c859eSApple OSS Distributions {
679*0f4c859eSApple OSS Distributions return (unsigned int) !(VMAPPLE_UART0_FR & 0x10);
680*0f4c859eSApple OSS Distributions }
681*0f4c859eSApple OSS Distributions
682*0f4c859eSApple OSS Distributions static uint8_t
vmapple_uart_receive_data(void)683*0f4c859eSApple OSS Distributions vmapple_uart_receive_data(void)
684*0f4c859eSApple OSS Distributions {
685*0f4c859eSApple OSS Distributions return (uint8_t) (VMAPPLE_UART0_DR & 0xff);
686*0f4c859eSApple OSS Distributions }
687*0f4c859eSApple OSS Distributions
688*0f4c859eSApple OSS Distributions static void
vmapple_uart_init(void)689*0f4c859eSApple OSS Distributions vmapple_uart_init(void)
690*0f4c859eSApple OSS Distributions {
691*0f4c859eSApple OSS Distributions VMAPPLE_UART0_CR = 0x0;
692*0f4c859eSApple OSS Distributions VMAPPLE_UART0_ECR = 0x0;
693*0f4c859eSApple OSS Distributions VMAPPLE_UART0_LCR_H = (
694*0f4c859eSApple OSS Distributions PL011_LCR_WORD_LENGTH_8 |
695*0f4c859eSApple OSS Distributions PL011_LCR_FIFO_ENABLE |
696*0f4c859eSApple OSS Distributions PL011_LCR_ONE_STOP_BIT |
697*0f4c859eSApple OSS Distributions PL011_LCR_PARITY_DISABLE |
698*0f4c859eSApple OSS Distributions PL011_LCR_BREAK_DISABLE
699*0f4c859eSApple OSS Distributions );
700*0f4c859eSApple OSS Distributions VMAPPLE_UART0_IBRD = PL011_IBRD_DIV_38400;
701*0f4c859eSApple OSS Distributions VMAPPLE_UART0_FBRD = PL011_FBRD_DIV_38400;
702*0f4c859eSApple OSS Distributions VMAPPLE_UART0_TIMSC = 0x0;
703*0f4c859eSApple OSS Distributions VMAPPLE_UART0_ICR = PL011_ICR_CLR_ALL_IRQS;
704*0f4c859eSApple OSS Distributions VMAPPLE_UART0_CR = (
705*0f4c859eSApple OSS Distributions PL011_CR_UART_ENABLE |
706*0f4c859eSApple OSS Distributions PL011_CR_TX_ENABLE |
707*0f4c859eSApple OSS Distributions PL011_CR_RX_ENABLE
708*0f4c859eSApple OSS Distributions );
709*0f4c859eSApple OSS Distributions }
710*0f4c859eSApple OSS Distributions
711*0f4c859eSApple OSS Distributions SECURITY_READ_ONLY_LATE(static struct pe_serial_functions) vmapple_uart_serial_functions =
712*0f4c859eSApple OSS Distributions {
713*0f4c859eSApple OSS Distributions .init = vmapple_uart_init,
714*0f4c859eSApple OSS Distributions .transmit_ready = vmapple_uart_transmit_ready,
715*0f4c859eSApple OSS Distributions .transmit_data = vmapple_uart_transmit_data,
716*0f4c859eSApple OSS Distributions .receive_ready = vmapple_uart_receive_ready,
717*0f4c859eSApple OSS Distributions .receive_data = vmapple_uart_receive_data,
718*0f4c859eSApple OSS Distributions .device = SERIAL_VMAPPLE_UART
719*0f4c859eSApple OSS Distributions };
720*0f4c859eSApple OSS Distributions
721*0f4c859eSApple OSS Distributions #endif /* VMAPPLE_UART */
722*0f4c859eSApple OSS Distributions
723*0f4c859eSApple OSS Distributions /*****************************************************************************/
724*0f4c859eSApple OSS Distributions
725*0f4c859eSApple OSS Distributions /**
726*0f4c859eSApple OSS Distributions * Output @str onto every registered serial interface by polling.
727*0f4c859eSApple OSS Distributions *
728*0f4c859eSApple OSS Distributions * @param str The string to output.
729*0f4c859eSApple OSS Distributions */
730*0f4c859eSApple OSS Distributions static void uart_puts_force_poll(
731*0f4c859eSApple OSS Distributions const char *str);
732*0f4c859eSApple OSS Distributions
733*0f4c859eSApple OSS Distributions /**
734*0f4c859eSApple OSS Distributions * Output @str onto a specific serial interface by polling.
735*0f4c859eSApple OSS Distributions *
736*0f4c859eSApple OSS Distributions * @param str The string to output.
737*0f4c859eSApple OSS Distributions * @param fns The functions to use to output the message.
738*0f4c859eSApple OSS Distributions */
739*0f4c859eSApple OSS Distributions static void uart_puts_force_poll_device(
740*0f4c859eSApple OSS Distributions const char *str,
741*0f4c859eSApple OSS Distributions struct pe_serial_functions *fns);
742*0f4c859eSApple OSS Distributions
743*0f4c859eSApple OSS Distributions static void
register_serial_functions(struct pe_serial_functions * fns)744*0f4c859eSApple OSS Distributions register_serial_functions(struct pe_serial_functions *fns)
745*0f4c859eSApple OSS Distributions {
746*0f4c859eSApple OSS Distributions fns->next = gPESF;
747*0f4c859eSApple OSS Distributions gPESF = fns;
748*0f4c859eSApple OSS Distributions }
749*0f4c859eSApple OSS Distributions
750*0f4c859eSApple OSS Distributions #if HIBERNATION
751*0f4c859eSApple OSS Distributions /**
752*0f4c859eSApple OSS Distributions * Transitions the serial driver into a mode that can be run in the hibernation
753*0f4c859eSApple OSS Distributions * resume context. In this mode, the serial driver runs at a barebones level
754*0f4c859eSApple OSS Distributions * without making sure the serial devices are properly initialized or utilizing
755*0f4c859eSApple OSS Distributions * features such as the software drain timer for dockchannels.
756*0f4c859eSApple OSS Distributions *
757*0f4c859eSApple OSS Distributions * Upon the next call to serial_init (once the hibernation image has been
758*0f4c859eSApple OSS Distributions * loaded), this mode is exited and we return to the normal operation of the
759*0f4c859eSApple OSS Distributions * driver.
760*0f4c859eSApple OSS Distributions */
761*0f4c859eSApple OSS Distributions MARK_AS_HIBERNATE_TEXT void
serial_hibernation_init(void)762*0f4c859eSApple OSS Distributions serial_hibernation_init(void)
763*0f4c859eSApple OSS Distributions {
764*0f4c859eSApple OSS Distributions uart_hibernation = true;
765*0f4c859eSApple OSS Distributions #if defined(APPLE_UART)
766*0f4c859eSApple OSS Distributions apple_uart_registers = (apple_uart_registers_t *)gHibernateGlobals.hibUartRegPhysBase;
767*0f4c859eSApple OSS Distributions #endif /* defined(APPLE_UART) */
768*0f4c859eSApple OSS Distributions #if defined(DOCKCHANNEL_UART)
769*0f4c859eSApple OSS Distributions dockchannel_uart_base = gHibernateGlobals.dockChannelRegPhysBase;
770*0f4c859eSApple OSS Distributions #endif /* defined(DOCKCHANNEL_UART) */
771*0f4c859eSApple OSS Distributions }
772*0f4c859eSApple OSS Distributions
773*0f4c859eSApple OSS Distributions /**
774*0f4c859eSApple OSS Distributions * Transitions the serial driver back to non-hibernation mode so it can resume
775*0f4c859eSApple OSS Distributions * normal operations. Should only be called from serial_init on a hibernation
776*0f4c859eSApple OSS Distributions * resume.
777*0f4c859eSApple OSS Distributions */
778*0f4c859eSApple OSS Distributions MARK_AS_HIBERNATE_TEXT static void
serial_hibernation_cleanup(void)779*0f4c859eSApple OSS Distributions serial_hibernation_cleanup(void)
780*0f4c859eSApple OSS Distributions {
781*0f4c859eSApple OSS Distributions uart_hibernation = false;
782*0f4c859eSApple OSS Distributions #if defined(APPLE_UART)
783*0f4c859eSApple OSS Distributions apple_uart_registers = (apple_uart_registers_t *)gHibernateGlobals.hibUartRegVirtBase;
784*0f4c859eSApple OSS Distributions #endif /* defined(APPLE_UART) */
785*0f4c859eSApple OSS Distributions #if defined(DOCKCHANNEL_UART)
786*0f4c859eSApple OSS Distributions dockchannel_uart_base = gHibernateGlobals.dockChannelRegVirtBase;
787*0f4c859eSApple OSS Distributions #endif /* defined(DOCKCHANNEL_UART) */
788*0f4c859eSApple OSS Distributions }
789*0f4c859eSApple OSS Distributions #endif /* HIBERNATION */
790*0f4c859eSApple OSS Distributions
791*0f4c859eSApple OSS Distributions int
serial_init(void)792*0f4c859eSApple OSS Distributions serial_init(void)
793*0f4c859eSApple OSS Distributions {
794*0f4c859eSApple OSS Distributions DTEntry entryP = NULL;
795*0f4c859eSApple OSS Distributions uint32_t prop_size;
796*0f4c859eSApple OSS Distributions vm_offset_t soc_base;
797*0f4c859eSApple OSS Distributions uintptr_t const *reg_prop;
798*0f4c859eSApple OSS Distributions uint32_t const *prop_value __unused = NULL;
799*0f4c859eSApple OSS Distributions
800*0f4c859eSApple OSS Distributions struct pe_serial_functions *fns = gPESF;
801*0f4c859eSApple OSS Distributions
802*0f4c859eSApple OSS Distributions /**
803*0f4c859eSApple OSS Distributions * Even if the serial devices have already been initialized on cold boot,
804*0f4c859eSApple OSS Distributions * when coming out of a sleep/wake, they'll need to be re-initialized. Since
805*0f4c859eSApple OSS Distributions * the uart_initted value is kept across a sleep/wake, always re-initialize
806*0f4c859eSApple OSS Distributions * to be safe.
807*0f4c859eSApple OSS Distributions */
808*0f4c859eSApple OSS Distributions if (uart_initted) {
809*0f4c859eSApple OSS Distributions #if HIBERNATION
810*0f4c859eSApple OSS Distributions if (uart_hibernation) {
811*0f4c859eSApple OSS Distributions serial_hibernation_cleanup();
812*0f4c859eSApple OSS Distributions }
813*0f4c859eSApple OSS Distributions #endif /* HIBERNATION */
814*0f4c859eSApple OSS Distributions while (fns != NULL) {
815*0f4c859eSApple OSS Distributions fns->init();
816*0f4c859eSApple OSS Distributions fns = fns->next;
817*0f4c859eSApple OSS Distributions }
818*0f4c859eSApple OSS Distributions
819*0f4c859eSApple OSS Distributions return gPESF != NULL;
820*0f4c859eSApple OSS Distributions }
821*0f4c859eSApple OSS Distributions
822*0f4c859eSApple OSS Distributions soc_base = pe_arm_get_soc_base_phys();
823*0f4c859eSApple OSS Distributions
824*0f4c859eSApple OSS Distributions if (soc_base == 0) {
825*0f4c859eSApple OSS Distributions uart_initted = true;
826*0f4c859eSApple OSS Distributions return 0;
827*0f4c859eSApple OSS Distributions }
828*0f4c859eSApple OSS Distributions
829*0f4c859eSApple OSS Distributions PE_parse_boot_argn("disable-uart-irq", &disable_uart_irq, sizeof(disable_uart_irq));
830*0f4c859eSApple OSS Distributions
831*0f4c859eSApple OSS Distributions #ifdef PI3_UART
832*0f4c859eSApple OSS Distributions if (SecureDTFindEntry("name", "gpio", &entryP) == kSuccess) {
833*0f4c859eSApple OSS Distributions SecureDTGetProperty(entryP, "reg", (void const **)®_prop, &prop_size);
834*0f4c859eSApple OSS Distributions pi3_gpio_base_vaddr = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
835*0f4c859eSApple OSS Distributions }
836*0f4c859eSApple OSS Distributions if (SecureDTFindEntry("name", "aux", &entryP) == kSuccess) {
837*0f4c859eSApple OSS Distributions SecureDTGetProperty(entryP, "reg", (void const **)®_prop, &prop_size);
838*0f4c859eSApple OSS Distributions pi3_aux_base_vaddr = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
839*0f4c859eSApple OSS Distributions }
840*0f4c859eSApple OSS Distributions if ((pi3_gpio_base_vaddr != 0) && (pi3_aux_base_vaddr != 0)) {
841*0f4c859eSApple OSS Distributions register_serial_functions(&pi3_uart_serial_functions);
842*0f4c859eSApple OSS Distributions }
843*0f4c859eSApple OSS Distributions #endif /* PI3_UART */
844*0f4c859eSApple OSS Distributions
845*0f4c859eSApple OSS Distributions #ifdef VMAPPLE_UART
846*0f4c859eSApple OSS Distributions if (SecureDTFindEntry("name", "uart0", &entryP) == kSuccess) {
847*0f4c859eSApple OSS Distributions SecureDTGetProperty(entryP, "reg", (void const **)®_prop, &prop_size);
848*0f4c859eSApple OSS Distributions vmapple_uart0_base_vaddr = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
849*0f4c859eSApple OSS Distributions }
850*0f4c859eSApple OSS Distributions
851*0f4c859eSApple OSS Distributions if (vmapple_uart0_base_vaddr != 0) {
852*0f4c859eSApple OSS Distributions register_serial_functions(&vmapple_uart_serial_functions);
853*0f4c859eSApple OSS Distributions }
854*0f4c859eSApple OSS Distributions #endif /* VMAPPLE_UART */
855*0f4c859eSApple OSS Distributions
856*0f4c859eSApple OSS Distributions #ifdef DOCKCHANNEL_UART
857*0f4c859eSApple OSS Distributions uint32_t no_dockchannel_uart = 0;
858*0f4c859eSApple OSS Distributions if (SecureDTFindEntry("name", "dockchannel-uart", &entryP) == kSuccess) {
859*0f4c859eSApple OSS Distributions SecureDTGetProperty(entryP, "reg", (void const **)®_prop, &prop_size);
860*0f4c859eSApple OSS Distributions // Should be two reg entries
861*0f4c859eSApple OSS Distributions if (prop_size / sizeof(uintptr_t) != 4) {
862*0f4c859eSApple OSS Distributions panic("Malformed dockchannel-uart property");
863*0f4c859eSApple OSS Distributions }
864*0f4c859eSApple OSS Distributions dockchannel_uart_base = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
865*0f4c859eSApple OSS Distributions dock_agent_base = ml_io_map(soc_base + *(reg_prop + 2), *(reg_prop + 3));
866*0f4c859eSApple OSS Distributions PE_parse_boot_argn("no-dockfifo-uart", &no_dockchannel_uart, sizeof(no_dockchannel_uart));
867*0f4c859eSApple OSS Distributions // Keep the old name for boot-arg
868*0f4c859eSApple OSS Distributions if (no_dockchannel_uart == 0) {
869*0f4c859eSApple OSS Distributions register_serial_functions(&dockchannel_serial_functions);
870*0f4c859eSApple OSS Distributions SecureDTGetProperty(entryP, "max-aop-clk", (void const **)&prop_value, &prop_size);
871*0f4c859eSApple OSS Distributions max_dockchannel_drain_period = (uint32_t)((prop_value)? (*prop_value * 0.03) : DOCKCHANNEL_DRAIN_PERIOD);
872*0f4c859eSApple OSS Distributions prop_value = NULL;
873*0f4c859eSApple OSS Distributions SecureDTGetProperty(entryP, "enable-sw-drain", (void const **)&prop_value, &prop_size);
874*0f4c859eSApple OSS Distributions use_sw_drain = (prop_value)? *prop_value : 0;
875*0f4c859eSApple OSS Distributions prop_value = NULL;
876*0f4c859eSApple OSS Distributions SecureDTGetProperty(entryP, "dock-wstat-mask", (void const **)&prop_value, &prop_size);
877*0f4c859eSApple OSS Distributions dock_wstat_mask = (prop_value)? *prop_value : 0x1ff;
878*0f4c859eSApple OSS Distributions prop_value = NULL;
879*0f4c859eSApple OSS Distributions SecureDTGetProperty(entryP, "interrupts", (void const **)&prop_value, &prop_size);
880*0f4c859eSApple OSS Distributions if (prop_value) {
881*0f4c859eSApple OSS Distributions dockchannel_serial_functions.has_irq = true;
882*0f4c859eSApple OSS Distributions }
883*0f4c859eSApple OSS Distributions
884*0f4c859eSApple OSS Distributions /* Set sane defaults for dockchannel globals. */
885*0f4c859eSApple OSS Distributions prev_dockchannel_spaces = rDOCKCHANNELS_DEV_WSTAT(DOCKCHANNEL_UART_CHANNEL) & dock_wstat_mask;
886*0f4c859eSApple OSS Distributions dockchannel_drain_deadline = mach_absolute_time() + dockchannel_stall_grace;
887*0f4c859eSApple OSS Distributions } else {
888*0f4c859eSApple OSS Distributions dockchannel_clear_intr();
889*0f4c859eSApple OSS Distributions }
890*0f4c859eSApple OSS Distributions // If no dockchannel-uart is found in the device tree, fall back
891*0f4c859eSApple OSS Distributions // to looking for the traditional UART serial console.
892*0f4c859eSApple OSS Distributions }
893*0f4c859eSApple OSS Distributions
894*0f4c859eSApple OSS Distributions #endif /* DOCKCHANNEL_UART */
895*0f4c859eSApple OSS Distributions
896*0f4c859eSApple OSS Distributions #ifdef APPLE_UART
897*0f4c859eSApple OSS Distributions char const *serial_compat = 0;
898*0f4c859eSApple OSS Distributions uint32_t use_legacy_uart = 1;
899*0f4c859eSApple OSS Distributions
900*0f4c859eSApple OSS Distributions /*
901*0f4c859eSApple OSS Distributions * The boot serial port should have a property named "boot-console".
902*0f4c859eSApple OSS Distributions * If we don't find it there, look for "uart0" and "uart1".
903*0f4c859eSApple OSS Distributions */
904*0f4c859eSApple OSS Distributions if (SecureDTFindEntry("boot-console", NULL, &entryP) == kSuccess) {
905*0f4c859eSApple OSS Distributions SecureDTGetProperty(entryP, "reg", (void const **)®_prop, &prop_size);
906*0f4c859eSApple OSS Distributions apple_uart_registers = (apple_uart_registers_t *)ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
907*0f4c859eSApple OSS Distributions SecureDTGetProperty(entryP, "compatible", (void const **)&serial_compat, &prop_size);
908*0f4c859eSApple OSS Distributions } else if (SecureDTFindEntry("name", "uart0", &entryP) == kSuccess) {
909*0f4c859eSApple OSS Distributions SecureDTGetProperty(entryP, "reg", (void const **)®_prop, &prop_size);
910*0f4c859eSApple OSS Distributions apple_uart_registers = (apple_uart_registers_t *)ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
911*0f4c859eSApple OSS Distributions SecureDTGetProperty(entryP, "compatible", (void const **)&serial_compat, &prop_size);
912*0f4c859eSApple OSS Distributions } else if (SecureDTFindEntry("name", "uart1", &entryP) == kSuccess) {
913*0f4c859eSApple OSS Distributions SecureDTGetProperty(entryP, "reg", (void const **)®_prop, &prop_size);
914*0f4c859eSApple OSS Distributions apple_uart_registers = (apple_uart_registers_t *)ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
915*0f4c859eSApple OSS Distributions SecureDTGetProperty(entryP, "compatible", (void const **)&serial_compat, &prop_size);
916*0f4c859eSApple OSS Distributions }
917*0f4c859eSApple OSS Distributions
918*0f4c859eSApple OSS Distributions if (NULL != entryP) {
919*0f4c859eSApple OSS Distributions prop_value = NULL;
920*0f4c859eSApple OSS Distributions SecureDTGetProperty(entryP, "sampling", (void const **)&prop_value, &prop_size);
921*0f4c859eSApple OSS Distributions if (prop_value) {
922*0f4c859eSApple OSS Distributions dt_sampling = *prop_value;
923*0f4c859eSApple OSS Distributions }
924*0f4c859eSApple OSS Distributions
925*0f4c859eSApple OSS Distributions prop_value = NULL;
926*0f4c859eSApple OSS Distributions SecureDTGetProperty(entryP, "ubrdiv", (void const **)&prop_value, &prop_size);
927*0f4c859eSApple OSS Distributions if (prop_value) {
928*0f4c859eSApple OSS Distributions dt_ubrdiv = *prop_value;
929*0f4c859eSApple OSS Distributions }
930*0f4c859eSApple OSS Distributions
931*0f4c859eSApple OSS Distributions prop_value = NULL;
932*0f4c859eSApple OSS Distributions SecureDTGetProperty(entryP, "interrupts", (void const **)&prop_value, &prop_size);
933*0f4c859eSApple OSS Distributions if (prop_value) {
934*0f4c859eSApple OSS Distributions apple_serial_functions.has_irq = true;
935*0f4c859eSApple OSS Distributions }
936*0f4c859eSApple OSS Distributions
937*0f4c859eSApple OSS Distributions prop_value = NULL;
938*0f4c859eSApple OSS Distributions SecureDTGetProperty(entryP, "disable-legacy-uart", (void const **)&prop_value, &prop_size);
939*0f4c859eSApple OSS Distributions if (prop_value) {
940*0f4c859eSApple OSS Distributions use_legacy_uart = 0;
941*0f4c859eSApple OSS Distributions }
942*0f4c859eSApple OSS Distributions }
943*0f4c859eSApple OSS Distributions
944*0f4c859eSApple OSS Distributions /* Check if we should enable this deprecated serial device. */
945*0f4c859eSApple OSS Distributions PE_parse_boot_argn("use-legacy-uart", &use_legacy_uart, sizeof(use_legacy_uart));
946*0f4c859eSApple OSS Distributions
947*0f4c859eSApple OSS Distributions if (serial_compat && !strcmp(serial_compat, "uart-1,samsung")) {
948*0f4c859eSApple OSS Distributions if (use_legacy_uart) {
949*0f4c859eSApple OSS Distributions register_serial_functions(&apple_serial_functions);
950*0f4c859eSApple OSS Distributions } else {
951*0f4c859eSApple OSS Distributions char legacy_serial_msg[] =
952*0f4c859eSApple OSS Distributions "Expecting more output? Wondering why Clippy turned into a platypus?\n\n"
953*0f4c859eSApple OSS Distributions "UART was manually disabled either via the 'use-legacy-uart=0' boot-arg or via\n"
954*0f4c859eSApple OSS Distributions "the disable-legacy-uart property in the boot-console uart device tree node.\n"
955*0f4c859eSApple OSS Distributions "Serial output over dockchannels is still enabled on devices with support.\n";
956*0f4c859eSApple OSS Distributions apple_serial_functions.init();
957*0f4c859eSApple OSS Distributions uart_puts_force_poll_device(hexley, &apple_serial_functions);
958*0f4c859eSApple OSS Distributions uart_puts_force_poll_device(legacy_serial_msg, &apple_serial_functions);
959*0f4c859eSApple OSS Distributions }
960*0f4c859eSApple OSS Distributions }
961*0f4c859eSApple OSS Distributions #endif /* APPLE_UART */
962*0f4c859eSApple OSS Distributions
963*0f4c859eSApple OSS Distributions fns = gPESF;
964*0f4c859eSApple OSS Distributions while (fns != NULL) {
965*0f4c859eSApple OSS Distributions serial_do_transmit = 1;
966*0f4c859eSApple OSS Distributions fns->init();
967*0f4c859eSApple OSS Distributions if (fns->has_irq) {
968*0f4c859eSApple OSS Distributions serial_irq_status |= fns->device; // serial_device_t is one-hot
969*0f4c859eSApple OSS Distributions }
970*0f4c859eSApple OSS Distributions fns = fns->next;
971*0f4c859eSApple OSS Distributions }
972*0f4c859eSApple OSS Distributions
973*0f4c859eSApple OSS Distributions #if HIBERNATION
974*0f4c859eSApple OSS Distributions /* hibernation needs to know the UART register addresses since it can't directly use this serial driver */
975*0f4c859eSApple OSS Distributions if (dockchannel_uart_base) {
976*0f4c859eSApple OSS Distributions gHibernateGlobals.dockChannelRegPhysBase = ml_vtophys(dockchannel_uart_base);
977*0f4c859eSApple OSS Distributions gHibernateGlobals.dockChannelRegVirtBase = dockchannel_uart_base;
978*0f4c859eSApple OSS Distributions gHibernateGlobals.dockChannelWstatMask = dock_wstat_mask;
979*0f4c859eSApple OSS Distributions }
980*0f4c859eSApple OSS Distributions if (apple_uart_registers) {
981*0f4c859eSApple OSS Distributions gHibernateGlobals.hibUartRegPhysBase = ml_vtophys((vm_offset_t)apple_uart_registers);
982*0f4c859eSApple OSS Distributions gHibernateGlobals.hibUartRegVirtBase = (vm_offset_t)apple_uart_registers;
983*0f4c859eSApple OSS Distributions }
984*0f4c859eSApple OSS Distributions #endif /* HIBERNATION */
985*0f4c859eSApple OSS Distributions
986*0f4c859eSApple OSS Distributions /* Complete. */
987*0f4c859eSApple OSS Distributions uart_initted = true;
988*0f4c859eSApple OSS Distributions return gPESF != NULL;
989*0f4c859eSApple OSS Distributions }
990*0f4c859eSApple OSS Distributions
991*0f4c859eSApple OSS Distributions /**
992*0f4c859eSApple OSS Distributions * Forbid or allow transmission over each serial until they receive data.
993*0f4c859eSApple OSS Distributions */
994*0f4c859eSApple OSS Distributions void
serial_set_on_demand(bool on_demand)995*0f4c859eSApple OSS Distributions serial_set_on_demand(bool on_demand)
996*0f4c859eSApple OSS Distributions {
997*0f4c859eSApple OSS Distributions /* Enable or disable transmission. */
998*0f4c859eSApple OSS Distributions serial_do_transmit = !on_demand;
999*0f4c859eSApple OSS Distributions
1000*0f4c859eSApple OSS Distributions /* If on-demand is enabled, report it. */
1001*0f4c859eSApple OSS Distributions if (on_demand) {
1002*0f4c859eSApple OSS Distributions uart_puts_force_poll(
1003*0f4c859eSApple OSS Distributions "On-demand serial mode selected.\n"
1004*0f4c859eSApple OSS Distributions "Waiting for user input to send logs.\n"
1005*0f4c859eSApple OSS Distributions );
1006*0f4c859eSApple OSS Distributions }
1007*0f4c859eSApple OSS Distributions }
1008*0f4c859eSApple OSS Distributions
1009*0f4c859eSApple OSS Distributions /**
1010*0f4c859eSApple OSS Distributions * Returns a deadline for the longest time the serial driver should wait for an
1011*0f4c859eSApple OSS Distributions * interrupt for. This serves as a timeout for the IRQ to allow for the software
1012*0f4c859eSApple OSS Distributions * drain timer that dockchannels supports.
1013*0f4c859eSApple OSS Distributions *
1014*0f4c859eSApple OSS Distributions * @param fns serial functions representing the device to find the deadline for
1015*0f4c859eSApple OSS Distributions *
1016*0f4c859eSApple OSS Distributions * @returns absolutetime deadline for this device's IRQ.
1017*0f4c859eSApple OSS Distributions */
1018*0f4c859eSApple OSS Distributions static uint64_t
serial_interrupt_deadline(__unused struct pe_serial_functions * fns)1019*0f4c859eSApple OSS Distributions serial_interrupt_deadline(__unused struct pe_serial_functions *fns)
1020*0f4c859eSApple OSS Distributions {
1021*0f4c859eSApple OSS Distributions #if defined(DOCKCHANNEL_UART)
1022*0f4c859eSApple OSS Distributions if (fns->device == SERIAL_DOCKCHANNEL && use_sw_drain) {
1023*0f4c859eSApple OSS Distributions return dockchannel_drain_deadline;
1024*0f4c859eSApple OSS Distributions }
1025*0f4c859eSApple OSS Distributions #endif
1026*0f4c859eSApple OSS Distributions
1027*0f4c859eSApple OSS Distributions /**
1028*0f4c859eSApple OSS Distributions * Default to 1.5ms for all other devices. 1.5ms was chosen as the baudrate
1029*0f4c859eSApple OSS Distributions * of the AppleSerialDevice is 115200, meaning that it should only take
1030*0f4c859eSApple OSS Distributions * ~1.5ms to drain the 16 character buffer completely.
1031*0f4c859eSApple OSS Distributions */
1032*0f4c859eSApple OSS Distributions uint64_t timeout_interval;
1033*0f4c859eSApple OSS Distributions nanoseconds_to_absolutetime(1500 * NSEC_PER_USEC, &timeout_interval);
1034*0f4c859eSApple OSS Distributions return mach_absolute_time() + timeout_interval;
1035*0f4c859eSApple OSS Distributions }
1036*0f4c859eSApple OSS Distributions
1037*0f4c859eSApple OSS Distributions /**
1038*0f4c859eSApple OSS Distributions * Goes to sleep waiting for an interrupt from a specificed serial device.
1039*0f4c859eSApple OSS Distributions *
1040*0f4c859eSApple OSS Distributions * @param fns serial functions representing the device to wait for
1041*0f4c859eSApple OSS Distributions */
1042*0f4c859eSApple OSS Distributions static void
serial_wait_for_interrupt(struct pe_serial_functions * fns)1043*0f4c859eSApple OSS Distributions serial_wait_for_interrupt(struct pe_serial_functions *fns)
1044*0f4c859eSApple OSS Distributions {
1045*0f4c859eSApple OSS Distributions /**
1046*0f4c859eSApple OSS Distributions * This block of code is set up to avoid a race condition in which the IRQ
1047*0f4c859eSApple OSS Distributions * is transmitted and processed by IOKit in between the time we check if the
1048*0f4c859eSApple OSS Distributions * device is ready to transmit and when we call thread_block. If the IRQ
1049*0f4c859eSApple OSS Distributions * fires in that time, thread_wakeup may have already been called in which
1050*0f4c859eSApple OSS Distributions * case we would be blocking and have nothing to wake us up.
1051*0f4c859eSApple OSS Distributions *
1052*0f4c859eSApple OSS Distributions * To avoid this issue, we first call assert_wait_deadline, which prepares
1053*0f4c859eSApple OSS Distributions * the thread to be blocked, but does not actually block the thread. After
1054*0f4c859eSApple OSS Distributions * this point, any call to thread_wakeup from IRQ handler will prevent
1055*0f4c859eSApple OSS Distributions * thread_block from actually blocking. As a performance optimization, we
1056*0f4c859eSApple OSS Distributions * then double check if the device is ready to transmit and if it is, then
1057*0f4c859eSApple OSS Distributions * we cancel the wait and just continue normally.
1058*0f4c859eSApple OSS Distributions */
1059*0f4c859eSApple OSS Distributions assert_wait_deadline(fns, THREAD_UNINT, serial_interrupt_deadline(fns));
1060*0f4c859eSApple OSS Distributions if (!fns->transmit_ready()) {
1061*0f4c859eSApple OSS Distributions fns->enable_irq();
1062*0f4c859eSApple OSS Distributions thread_block(THREAD_CONTINUE_NULL);
1063*0f4c859eSApple OSS Distributions } else {
1064*0f4c859eSApple OSS Distributions clear_wait(current_thread(), THREAD_AWAKENED);
1065*0f4c859eSApple OSS Distributions }
1066*0f4c859eSApple OSS Distributions }
1067*0f4c859eSApple OSS Distributions
1068*0f4c859eSApple OSS Distributions /**
1069*0f4c859eSApple OSS Distributions * Transmit a character over the specified serial output device.
1070*0f4c859eSApple OSS Distributions *
1071*0f4c859eSApple OSS Distributions * @param c Character to send
1072*0f4c859eSApple OSS Distributions * @param poll Whether we should poll or wait for an interrupt.
1073*0f4c859eSApple OSS Distributions * @param force Whether we should force this over the device if output has not been enabled yet.
1074*0f4c859eSApple OSS Distributions * @param fns Functions for the device to output over.
1075*0f4c859eSApple OSS Distributions */
1076*0f4c859eSApple OSS Distributions static inline void
uart_putc_device(char c,bool poll,bool force,struct pe_serial_functions * fns)1077*0f4c859eSApple OSS Distributions uart_putc_device(char c, bool poll, bool force, struct pe_serial_functions *fns)
1078*0f4c859eSApple OSS Distributions {
1079*0f4c859eSApple OSS Distributions if (!(serial_do_transmit || force)) {
1080*0f4c859eSApple OSS Distributions return;
1081*0f4c859eSApple OSS Distributions }
1082*0f4c859eSApple OSS Distributions
1083*0f4c859eSApple OSS Distributions while (!fns->transmit_ready()) {
1084*0f4c859eSApple OSS Distributions if (irq_available_and_ready(fns) && !poll) {
1085*0f4c859eSApple OSS Distributions serial_wait_for_interrupt(fns);
1086*0f4c859eSApple OSS Distributions } else {
1087*0f4c859eSApple OSS Distributions serial_poll();
1088*0f4c859eSApple OSS Distributions }
1089*0f4c859eSApple OSS Distributions }
1090*0f4c859eSApple OSS Distributions fns->transmit_data((uint8_t)c);
1091*0f4c859eSApple OSS Distributions }
1092*0f4c859eSApple OSS Distributions
1093*0f4c859eSApple OSS Distributions /**
1094*0f4c859eSApple OSS Distributions * Output a character onto every registered serial interface whose
1095*0f4c859eSApple OSS Distributions * transmission is enabled..
1096*0f4c859eSApple OSS Distributions *
1097*0f4c859eSApple OSS Distributions * @param c The character to output.
1098*0f4c859eSApple OSS Distributions * @param poll Whether the driver should poll to send the character or if it can
1099*0f4c859eSApple OSS Distributions * wait for an interrupt
1100*0f4c859eSApple OSS Distributions */
1101*0f4c859eSApple OSS Distributions MARK_AS_HIBERNATE_TEXT void
uart_putc_options(char c,bool poll)1102*0f4c859eSApple OSS Distributions uart_putc_options(char c, bool poll)
1103*0f4c859eSApple OSS Distributions {
1104*0f4c859eSApple OSS Distributions struct pe_serial_functions *fns = gPESF;
1105*0f4c859eSApple OSS Distributions
1106*0f4c859eSApple OSS Distributions while (fns != NULL) {
1107*0f4c859eSApple OSS Distributions uart_putc_device(c, poll, false, fns);
1108*0f4c859eSApple OSS Distributions fns = fns->next;
1109*0f4c859eSApple OSS Distributions }
1110*0f4c859eSApple OSS Distributions }
1111*0f4c859eSApple OSS Distributions
1112*0f4c859eSApple OSS Distributions /**
1113*0f4c859eSApple OSS Distributions * Output a character onto every registered serial interface whose
1114*0f4c859eSApple OSS Distributions * transmission is enabled by polling.
1115*0f4c859eSApple OSS Distributions *
1116*0f4c859eSApple OSS Distributions * @param c The character to output.
1117*0f4c859eSApple OSS Distributions */
1118*0f4c859eSApple OSS Distributions void
uart_putc(char c)1119*0f4c859eSApple OSS Distributions uart_putc(char c)
1120*0f4c859eSApple OSS Distributions {
1121*0f4c859eSApple OSS Distributions uart_putc_options(c, true);
1122*0f4c859eSApple OSS Distributions }
1123*0f4c859eSApple OSS Distributions
1124*0f4c859eSApple OSS Distributions /**
1125*0f4c859eSApple OSS Distributions * Output @str onto every registered serial interface by polling.
1126*0f4c859eSApple OSS Distributions *
1127*0f4c859eSApple OSS Distributions * @param str The string to output.
1128*0f4c859eSApple OSS Distributions */
1129*0f4c859eSApple OSS Distributions static void
uart_puts_force_poll(const char * str)1130*0f4c859eSApple OSS Distributions uart_puts_force_poll(
1131*0f4c859eSApple OSS Distributions const char *str)
1132*0f4c859eSApple OSS Distributions {
1133*0f4c859eSApple OSS Distributions struct pe_serial_functions *fns = gPESF;
1134*0f4c859eSApple OSS Distributions while (fns != NULL) {
1135*0f4c859eSApple OSS Distributions uart_puts_force_poll_device(str, fns);
1136*0f4c859eSApple OSS Distributions fns = fns->next;
1137*0f4c859eSApple OSS Distributions }
1138*0f4c859eSApple OSS Distributions }
1139*0f4c859eSApple OSS Distributions
1140*0f4c859eSApple OSS Distributions /**
1141*0f4c859eSApple OSS Distributions * Output @str onto a specific serial interface by polling.
1142*0f4c859eSApple OSS Distributions *
1143*0f4c859eSApple OSS Distributions * @param str The string to output.
1144*0f4c859eSApple OSS Distributions * @param fns The functions to use to output the message.
1145*0f4c859eSApple OSS Distributions */
1146*0f4c859eSApple OSS Distributions static void
uart_puts_force_poll_device(const char * str,struct pe_serial_functions * fns)1147*0f4c859eSApple OSS Distributions uart_puts_force_poll_device(
1148*0f4c859eSApple OSS Distributions const char *str,
1149*0f4c859eSApple OSS Distributions struct pe_serial_functions *fns)
1150*0f4c859eSApple OSS Distributions {
1151*0f4c859eSApple OSS Distributions char c;
1152*0f4c859eSApple OSS Distributions while ((c = *(str++))) {
1153*0f4c859eSApple OSS Distributions uart_putc_device(c, true, true, fns);
1154*0f4c859eSApple OSS Distributions }
1155*0f4c859eSApple OSS Distributions }
1156*0f4c859eSApple OSS Distributions
1157*0f4c859eSApple OSS Distributions /**
1158*0f4c859eSApple OSS Distributions * Read a character from the first registered serial interface that has data
1159*0f4c859eSApple OSS Distributions * available.
1160*0f4c859eSApple OSS Distributions *
1161*0f4c859eSApple OSS Distributions * @return The character if any interfaces have data available, otherwise -1.
1162*0f4c859eSApple OSS Distributions */
1163*0f4c859eSApple OSS Distributions int
uart_getc(void)1164*0f4c859eSApple OSS Distributions uart_getc(void)
1165*0f4c859eSApple OSS Distributions {
1166*0f4c859eSApple OSS Distributions struct pe_serial_functions *fns = gPESF;
1167*0f4c859eSApple OSS Distributions while (fns != NULL) {
1168*0f4c859eSApple OSS Distributions if (fns->receive_ready()) {
1169*0f4c859eSApple OSS Distributions serial_do_transmit = 1;
1170*0f4c859eSApple OSS Distributions return (int)fns->receive_data();
1171*0f4c859eSApple OSS Distributions }
1172*0f4c859eSApple OSS Distributions fns = fns->next;
1173*0f4c859eSApple OSS Distributions }
1174*0f4c859eSApple OSS Distributions return -1;
1175*0f4c859eSApple OSS Distributions }
1176*0f4c859eSApple OSS Distributions
1177*0f4c859eSApple OSS Distributions /**
1178*0f4c859eSApple OSS Distributions * Enables IRQs for a specific serial device and returns whether or not IRQs for
1179*0f4c859eSApple OSS Distributions * that device where enabled successfully. For a serial driver to have irqs
1180*0f4c859eSApple OSS Distributions * enabled, it must have the enable_irq, disable_irq, and acknowledge_irq
1181*0f4c859eSApple OSS Distributions * functions defined and the has_irq flag set.
1182*0f4c859eSApple OSS Distributions *
1183*0f4c859eSApple OSS Distributions * @param device Serial device to enable irqs on
1184*0f4c859eSApple OSS Distributions * @note This function should only be called from the AppleSerialShim kext
1185*0f4c859eSApple OSS Distributions */
1186*0f4c859eSApple OSS Distributions kern_return_t
serial_irq_enable(serial_device_t device)1187*0f4c859eSApple OSS Distributions serial_irq_enable(serial_device_t device)
1188*0f4c859eSApple OSS Distributions {
1189*0f4c859eSApple OSS Distributions struct pe_serial_functions *fns = get_serial_functions(device);
1190*0f4c859eSApple OSS Distributions
1191*0f4c859eSApple OSS Distributions if (!fns || !fns->has_irq || disable_uart_irq) {
1192*0f4c859eSApple OSS Distributions return KERN_FAILURE;
1193*0f4c859eSApple OSS Distributions }
1194*0f4c859eSApple OSS Distributions
1195*0f4c859eSApple OSS Distributions serial_irq_status &= ~device;
1196*0f4c859eSApple OSS Distributions
1197*0f4c859eSApple OSS Distributions return KERN_SUCCESS;
1198*0f4c859eSApple OSS Distributions }
1199*0f4c859eSApple OSS Distributions
1200*0f4c859eSApple OSS Distributions /**
1201*0f4c859eSApple OSS Distributions * Performs any actions needed to handle this IRQ. Wakes up the thread waiting
1202*0f4c859eSApple OSS Distributions * on the interrupt if one exists.
1203*0f4c859eSApple OSS Distributions *
1204*0f4c859eSApple OSS Distributions * @param device Serial device that generated the IRQ.
1205*0f4c859eSApple OSS Distributions * @note Interrupts will have already been cleared and disabled by serial_irq_filter.
1206*0f4c859eSApple OSS Distributions * @note This function should only be called from the AppleSerialShim kext.
1207*0f4c859eSApple OSS Distributions */
1208*0f4c859eSApple OSS Distributions kern_return_t
serial_irq_action(serial_device_t device)1209*0f4c859eSApple OSS Distributions serial_irq_action(serial_device_t device)
1210*0f4c859eSApple OSS Distributions {
1211*0f4c859eSApple OSS Distributions struct pe_serial_functions *fns = get_serial_functions(device);
1212*0f4c859eSApple OSS Distributions
1213*0f4c859eSApple OSS Distributions if (!fns || !fns->has_irq) {
1214*0f4c859eSApple OSS Distributions return KERN_FAILURE;
1215*0f4c859eSApple OSS Distributions }
1216*0f4c859eSApple OSS Distributions
1217*0f4c859eSApple OSS Distributions /**
1218*0f4c859eSApple OSS Distributions * Because IRQs are enabled only when we know a thread is about to sleep, we
1219*0f4c859eSApple OSS Distributions * can call wake up and reasonably expect there to be a thread waiting.
1220*0f4c859eSApple OSS Distributions */
1221*0f4c859eSApple OSS Distributions thread_wakeup(fns);
1222*0f4c859eSApple OSS Distributions
1223*0f4c859eSApple OSS Distributions return KERN_SUCCESS;
1224*0f4c859eSApple OSS Distributions }
1225*0f4c859eSApple OSS Distributions
1226*0f4c859eSApple OSS Distributions /**
1227*0f4c859eSApple OSS Distributions * Returns true if the pending IRQ for device is one that can be handled by the
1228*0f4c859eSApple OSS Distributions * platform serial driver.
1229*0f4c859eSApple OSS Distributions *
1230*0f4c859eSApple OSS Distributions * @param device Serial device that generated the IRQ.
1231*0f4c859eSApple OSS Distributions * @note This function is called from a primary interrupt context and should be
1232*0f4c859eSApple OSS Distributions * kept lightweight.
1233*0f4c859eSApple OSS Distributions * @note This function should only be called from the AppleSerialShim kext
1234*0f4c859eSApple OSS Distributions */
1235*0f4c859eSApple OSS Distributions bool
serial_irq_filter(serial_device_t device)1236*0f4c859eSApple OSS Distributions serial_irq_filter(serial_device_t device)
1237*0f4c859eSApple OSS Distributions {
1238*0f4c859eSApple OSS Distributions struct pe_serial_functions *fns = get_serial_functions(device);
1239*0f4c859eSApple OSS Distributions
1240*0f4c859eSApple OSS Distributions if (!fns || !fns->has_irq) {
1241*0f4c859eSApple OSS Distributions return false;
1242*0f4c859eSApple OSS Distributions }
1243*0f4c859eSApple OSS Distributions
1244*0f4c859eSApple OSS Distributions /**
1245*0f4c859eSApple OSS Distributions * Disable IRQs until next time a thread waits for an interrupt to prevent an interrupt storm.
1246*0f4c859eSApple OSS Distributions */
1247*0f4c859eSApple OSS Distributions const bool had_irqs_enabled = fns->disable_irq();
1248*0f4c859eSApple OSS Distributions const bool was_our_interrupt = fns->acknowledge_irq();
1249*0f4c859eSApple OSS Distributions
1250*0f4c859eSApple OSS Distributions /* Re-enable IRQs if the interrupt wasn't for us. */
1251*0f4c859eSApple OSS Distributions if (had_irqs_enabled && !was_our_interrupt) {
1252*0f4c859eSApple OSS Distributions fns->enable_irq();
1253*0f4c859eSApple OSS Distributions }
1254*0f4c859eSApple OSS Distributions
1255*0f4c859eSApple OSS Distributions return was_our_interrupt;
1256*0f4c859eSApple OSS Distributions }
1257*0f4c859eSApple OSS Distributions
1258*0f4c859eSApple OSS Distributions /**
1259*0f4c859eSApple OSS Distributions * Prepares all serial devices to go to sleep by draining the hardware FIFOs
1260*0f4c859eSApple OSS Distributions * and disabling interrupts.
1261*0f4c859eSApple OSS Distributions */
1262*0f4c859eSApple OSS Distributions void
serial_go_to_sleep(void)1263*0f4c859eSApple OSS Distributions serial_go_to_sleep(void)
1264*0f4c859eSApple OSS Distributions {
1265*0f4c859eSApple OSS Distributions struct pe_serial_functions *fns = gPESF;
1266*0f4c859eSApple OSS Distributions while (fns != NULL) {
1267*0f4c859eSApple OSS Distributions if (irq_available_and_ready(fns)) {
1268*0f4c859eSApple OSS Distributions fns->disable_irq();
1269*0f4c859eSApple OSS Distributions }
1270*0f4c859eSApple OSS Distributions fns = fns->next;
1271*0f4c859eSApple OSS Distributions }
1272*0f4c859eSApple OSS Distributions
1273*0f4c859eSApple OSS Distributions #ifdef APPLE_UART
1274*0f4c859eSApple OSS Distributions /* APPLE_UART needs to drain FIFO before sleeping */
1275*0f4c859eSApple OSS Distributions if (get_serial_functions(SERIAL_APPLE_UART)) {
1276*0f4c859eSApple OSS Distributions apple_uart_drain_fifo();
1277*0f4c859eSApple OSS Distributions }
1278*0f4c859eSApple OSS Distributions #endif /* APPLE_UART */
1279*0f4c859eSApple OSS Distributions }
1280