xref: /xnu-10002.61.3/pexpert/arm/pe_serial.c (revision 0f4c859e951fba394238ab619495c4e1d54d0f34)
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 **)&reg_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 **)&reg_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 **)&reg_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 **)&reg_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 **)&reg_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 **)&reg_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 **)&reg_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