1 /*
2  * -----------------------------------------------------------------------
3  *
4  *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9  *   Boston MA 02111-1307, USA; either version 2 of the License, or
10  *   (at your option) any later version; incorporated herein by reference.
11  *
12  * -----------------------------------------------------------------------
13  *
14  * serirq.c
15  *
16  * Serial port IRQ code
17  *
18  * We don't know what IRQ, if any, we have, so map all of them...
19  */
20 #include <sys/io.h>
21 #include <string.h>
22 
23 #include <fs.h>
24 #include "bios.h"
25 
26 static char serial_buf[serial_buf_size];
27 
28 static unsigned short SerialIRQPort; /* Serial port w IRQ service */
29 char *SerialHead = serial_buf;    /* Head of serial port rx buffer */
30 char *SerialTail = serial_buf;    /* Tail of serial port rx buffer */
31 
32 static unsigned char IRQMask[2];	     /* PIC IRQ mask status */
33 
34 static unsigned int oldirq[16];
35 
36 typedef void (*irqhandler_t)(void);
37 
38 void sirq_cleanup(void);
39 
irq_common(unsigned short old_irq)40 static void irq_common(unsigned short old_irq)
41 {
42 	char *dst;
43 	irqhandler_t next;
44 	char val;
45 
46 	dst = SerialHead;
47 	next = (irqhandler_t)oldirq[old_irq];
48 
49 	/* LSR */
50 	val = inb(SerialPort + 5);
51 
52 	/* Received data */
53 	while (val & 1) {
54 		/* RDR */
55 		*dst++ = inb(SerialPort);
56 		/* LSR */
57 		val = inb(SerialPort + 5);
58 		if ((val & FlowIgnore) == FlowIgnore) {
59 			/* Wrap around if necessary */
60 			dst = (char *)((unsigned long)dst & (serial_buf_size - 1));
61 
62 			/* Would this cause overflow? */
63 			if (dst != SerialTail)
64 				SerialHead = dst;
65 		}
66 	}
67 
68 	/* Chain to next handler */
69 	next();
70 }
71 
72 #define SERIAL_IRQ_HANDLER(n) \
73 	static void serstub_irq##n(void)	\
74 	{					\
75 		irq_common(n);			\
76 	}
77 
78 SERIAL_IRQ_HANDLER(0);
79 SERIAL_IRQ_HANDLER(1);
80 SERIAL_IRQ_HANDLER(2);
81 SERIAL_IRQ_HANDLER(3);
82 SERIAL_IRQ_HANDLER(4);
83 SERIAL_IRQ_HANDLER(5);
84 SERIAL_IRQ_HANDLER(6);
85 SERIAL_IRQ_HANDLER(7);
86 SERIAL_IRQ_HANDLER(8);
87 SERIAL_IRQ_HANDLER(9);
88 SERIAL_IRQ_HANDLER(10);
89 SERIAL_IRQ_HANDLER(11);
90 SERIAL_IRQ_HANDLER(12);
91 SERIAL_IRQ_HANDLER(13);
92 SERIAL_IRQ_HANDLER(14);
93 SERIAL_IRQ_HANDLER(15);
94 
save_irq_vectors(uint32_t * src,uint32_t * dst)95 static inline void save_irq_vectors(uint32_t *src, uint32_t *dst)
96 {
97 	int i;
98 
99 	for (i = 0; i < 8; i++)
100 		*dst++ = *src++;
101 }
102 
install_irq_vectors(uint32_t * dst,int first)103 static inline void install_irq_vectors(uint32_t *dst, int first)
104 {
105 	if (first) {
106 		*dst++ = (uint32_t)serstub_irq0;
107 		*dst++ = (uint32_t)serstub_irq1;
108 		*dst++ = (uint32_t)serstub_irq2;
109 		*dst++ = (uint32_t)serstub_irq3;
110 		*dst++ = (uint32_t)serstub_irq4;
111 		*dst++ = (uint32_t)serstub_irq5;
112 		*dst++ = (uint32_t)serstub_irq6;
113 		*dst++ = (uint32_t)serstub_irq7;
114 	} else {
115 		*dst++ = (uint32_t)serstub_irq8;
116 		*dst++ = (uint32_t)serstub_irq9;
117 		*dst++ = (uint32_t)serstub_irq10;
118 		*dst++ = (uint32_t)serstub_irq11;
119 		*dst++ = (uint32_t)serstub_irq12;
120 		*dst++ = (uint32_t)serstub_irq13;
121 		*dst++ = (uint32_t)serstub_irq14;
122 		*dst++ = (uint32_t)serstub_irq15;
123 	}
124 }
125 
sirq_install(void)126 __export void sirq_install(void)
127 {
128 	char val, val2;
129 
130 	sirq_cleanup();
131 
132 	save_irq_vectors((uint32_t *)(4 * 0x8), oldirq);
133 	save_irq_vectors((uint32_t *)(4 * 0x70), &oldirq[8]);
134 
135 	install_irq_vectors((uint32_t *)(4 * 0x8), 1);
136 	install_irq_vectors((uint32_t *)(4 * 0x70), 0);
137 
138 	SerialIRQPort = SerialPort;
139 
140 	/* Clear DLAB (should already be...) */
141 	outb(0x3, SerialIRQPort + 5);
142 	io_delay();
143 
144 	/* Enable receive interrupt */
145 	outb(0x1, SerialIRQPort + 1);
146 	io_delay();
147 
148 	/*
149 	 * Enable all the interrupt lines at the PIC. Some BIOSes only
150 	 * enable the timer interrupts and other interrupts actively
151 	 * in use by the BIOS.
152 	 */
153 
154 	/* Secondary PIC mask register */
155 	val = inb(0xA1);
156 	val2 = inb(0x21);
157 	IRQMask[0] = val;
158 	IRQMask[1] = val2;
159 
160 	io_delay();
161 
162 	/* Remove all interrupt masks */
163 	outb(0x21, 0);
164 	outb(0xA1, 0);
165 }
166 
sirq_cleanup_nowipe(void)167 __export void sirq_cleanup_nowipe(void)
168 {
169 	uint32_t *dst;
170 	int i;
171 
172 	if (!SerialIRQPort)
173 		return;
174 
175 	/* Clear DLAB */
176 	outb(0x3, SerialIRQPort + 5);
177 	io_delay();
178 
179 	/* Clear IER */
180 	outb(0x0, SerialIRQPort + 1);
181 	io_delay();
182 
183 	/* Restore PIC masks */
184 	outb(IRQMask[0], 0x21);
185 	outb(IRQMask[1], 0xA1);
186 
187 	/* Restore the original interrupt vectors */
188 	dst = (uint32_t *)(4 * 0x8);
189 	for (i = 0; i < 8; i++)
190 		*dst++ = oldirq[i];
191 
192 	dst = (uint32_t *)(4 * 0x70);
193 	for (i = 8; i < 16; i++)
194 		*dst++ = oldirq[i];
195 
196 	/* No active interrupt system */
197 	SerialIRQPort = 0;
198 }
199 
sirq_cleanup(void)200 void sirq_cleanup(void)
201 {
202 	sirq_cleanup_nowipe();
203 	memcpy(SerialHead, 0x0, serial_buf_size);
204 }
205