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