Home | History | Annotate | Download | only in core
      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 
     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 
     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 
    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 
    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 
    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 
    200 void sirq_cleanup(void)
    201 {
    202 	sirq_cleanup_nowipe();
    203 	memcpy(SerialHead, 0x0, serial_buf_size);
    204 }
    205