1/* 2 * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29/* 30 * file: pe_serial.c 31 * Polled-mode 16x50 UART driver. 32 */ 33 34#include <pexpert/protos.h> 35#include <pexpert/pexpert.h> 36 37/* standard port addresses */ 38enum { 39 COM1_PORT_ADDR = 0x3f8, 40 COM2_PORT_ADDR = 0x2f8 41}; 42 43/* UART register offsets */ 44enum { 45 UART_RBR = 0, /* receive buffer Register (R) */ 46 UART_THR = 0, /* transmit holding register (W) */ 47 UART_DLL = 0, /* DLAB = 1, divisor latch (LSB) */ 48 UART_IER = 1, /* interrupt enable register */ 49 UART_DLM = 1, /* DLAB = 1, divisor latch (MSB) */ 50 UART_IIR = 2, /* interrupt ident register (R) */ 51 UART_FCR = 2, /* fifo control register (W) */ 52 UART_LCR = 3, /* line control register */ 53 UART_MCR = 4, /* modem control register */ 54 UART_LSR = 5, /* line status register */ 55 UART_MSR = 6, /* modem status register */ 56 UART_SCR = 7 /* scratch register */ 57}; 58 59enum { 60 UART_LCR_8BITS = 0x03, 61 UART_LCR_DLAB = 0x80 62}; 63 64enum { 65 UART_MCR_DTR = 0x01, 66 UART_MCR_RTS = 0x02, 67 UART_MCR_OUT1 = 0x04, 68 UART_MCR_OUT2 = 0x08, 69 UART_MCR_LOOP = 0x10 70}; 71 72enum { 73 UART_LSR_DR = 0x01, 74 UART_LSR_OE = 0x02, 75 UART_LSR_PE = 0x04, 76 UART_LSR_FE = 0x08, 77 UART_LSR_THRE = 0x20 78}; 79 80static unsigned uart_baud_rate = 115200; 81#define UART_PORT_ADDR COM1_PORT_ADDR 82 83#define UART_CLOCK 1843200 /* 1.8432 MHz clock */ 84 85#define WRITE(r, v) outb(UART_PORT_ADDR + UART_##r, v) 86#define READ(r) inb(UART_PORT_ADDR + UART_##r) 87#define DELAY(x) { volatile int _d_; for (_d_ = 0; _d_ < (10000*x); _d_++) ; } 88 89static int uart_initted = 0; /* 1 if init'ed */ 90 91static int 92uart_probe( void ) 93{ 94 /* Verify that the Scratch Register is accessible */ 95 96 WRITE( SCR, 0x5a ); 97 if (READ(SCR) != 0x5a) return 0; 98 WRITE( SCR, 0xa5 ); 99 if (READ(SCR) != 0xa5) return 0; 100 return 1; 101} 102 103static void 104uart_set_baud_rate( unsigned long baud_rate ) 105{ 106 const unsigned char lcr = READ( LCR ); 107 unsigned long div; 108 109 if (baud_rate == 0) baud_rate = 9600; 110 div = UART_CLOCK / 16 / baud_rate; 111 WRITE( LCR, lcr | UART_LCR_DLAB ); 112 WRITE( DLM, (unsigned char)(div >> 8) ); 113 WRITE( DLL, (unsigned char) div ); 114 WRITE( LCR, lcr & ~UART_LCR_DLAB); 115} 116 117static void 118uart_putc( char c ) 119{ 120 if (!uart_initted) return; 121 122 /* Wait for THR empty */ 123 while ( !(READ(LSR) & UART_LSR_THRE) ) DELAY(1); 124 125 WRITE( THR, c ); 126} 127 128static int 129uart_getc( void ) 130{ 131 /* 132 * This function returns: 133 * -1 : no data 134 * -2 : receiver error 135 * >0 : character received 136 */ 137 138 unsigned char lsr; 139 140 if (!uart_initted) return -1; 141 142 lsr = READ( LSR ); 143 144 if ( lsr & (UART_LSR_FE | UART_LSR_PE | UART_LSR_OE) ) 145 { 146 READ( RBR ); /* discard */ 147 return -2; 148 } 149 150 if ( lsr & UART_LSR_DR ) 151 { 152 return READ( RBR ); 153 } 154 155 return -1; 156} 157 158int serial_init( void ) 159{ 160 unsigned serial_baud_rate = 0; 161 162 if ( uart_probe() == 0 ) return 0; 163 164 /* Disable hardware interrupts */ 165 166 WRITE( MCR, 0 ); 167 WRITE( IER, 0 ); 168 169 /* Disable FIFO's for 16550 devices */ 170 171 WRITE( FCR, 0 ); 172 173 /* Set for 8-bit, no parity, DLAB bit cleared */ 174 175 WRITE( LCR, UART_LCR_8BITS ); 176 177 /* Set baud rate - use the supplied boot-arg if available */ 178 179 if (PE_parse_boot_argn("serialbaud", &serial_baud_rate, sizeof (serial_baud_rate))) 180 { 181 /* Valid divisor? */ 182 if (!((UART_CLOCK / 16) % serial_baud_rate)) { 183 uart_baud_rate = serial_baud_rate; 184 } 185 } 186 uart_set_baud_rate( uart_baud_rate ); 187 188 /* Assert DTR# and RTS# lines (OUT2?) */ 189 190 WRITE( MCR, UART_MCR_DTR | UART_MCR_RTS ); 191 192 /* Clear any garbage in the input buffer */ 193 194 READ( RBR ); 195 196 uart_initted = 1; 197 198 return 1; 199} 200 201void serial_putc( char c ) 202{ 203 uart_putc(c); 204 if (c == '\n') uart_putc('\r'); 205} 206 207int serial_getc( void ) 208{ 209 return uart_getc(); 210} 211