1/* 2 * Copyright (c) 2005 The Regents of The University of Michigan 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer; 9 * redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution; 12 * neither the name of the copyright holders nor the names of its 13 * contributors may be used to endorse or promote products derived from 14 * this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * Authors: Ali Saidi 29 */ 30 31/** @file 32 * Implements a 8250 UART 33 */ 34 35#include "dev/serial/uart8250.hh" 36 37#include <string> 38#include <vector> 39 40#include "base/inifile.hh" 41#include "base/trace.hh" 42#include "config/the_isa.hh" 43#include "debug/Uart.hh" 44#include "dev/platform.hh" 45#include "mem/packet.hh" 46#include "mem/packet_access.hh" 47 48using namespace std; 49 50void 51Uart8250::processIntrEvent(int intrBit) 52{ 53 if (intrBit & IER) { 54 DPRINTF(Uart, "UART InterEvent, interrupting\n"); 55 platform->postConsoleInt(); 56 status |= intrBit; 57 lastTxInt = curTick(); 58 } 59 else 60 DPRINTF(Uart, "UART InterEvent, not interrupting\n"); 61 62} 63 64/* The linux serial driver (8250.c about line 1182) loops reading from 65 * the device until the device reports it has no more data to 66 * read. After a maximum of 255 iterations the code prints "serial8250 67 * too much work for irq X," and breaks out of the loop. Since the 68 * simulated system is so much slower than the actual system, if a 69 * user is typing on the keyboard it is very easy for them to provide 70 * input at a fast enough rate to not allow the loop to exit and thus 71 * the error to be printed. This magic number provides a delay between 72 * the time the UART receives a character to send to the simulated 73 * system and the time it actually notifies the system it has a 74 * character to send to alleviate this problem. --Ali 75 */ 76void 77Uart8250::scheduleIntr(Event *event) 78{ 79 static const Tick interval = 225 * SimClock::Int::ns; 80 DPRINTF(Uart, "Scheduling IER interrupt for %s, at cycle %lld\n", 81 event->name(), curTick() + interval); 82 if (!event->scheduled()) 83 schedule(event, curTick() + interval); 84 else 85 reschedule(event, curTick() + interval); 86} 87 88 89Uart8250::Uart8250(const Params *p) 90 : Uart(p, 8), IER(0), DLAB(0), LCR(0), MCR(0), lastTxInt(0), 91 txIntrEvent([this]{ processIntrEvent(TX_INT); }, "TX"), 92 rxIntrEvent([this]{ processIntrEvent(RX_INT); }, "RX") 93{ 94} 95 96Tick 97Uart8250::read(PacketPtr pkt) 98{ 99 assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); 100 assert(pkt->getSize() == 1); 101 102 Addr daddr = pkt->getAddr() - pioAddr; 103 104 DPRINTF(Uart, " read register %#x\n", daddr); 105 106 switch (daddr) { 107 case 0x0: 108 if (!(LCR & 0x80)) { // read byte 109 if (device->dataAvailable()) 110 pkt->setRaw(device->readData()); 111 else { 112 pkt->setRaw((uint8_t)0); 113 // A limited amount of these are ok. 114 DPRINTF(Uart, "empty read of RX register\n"); 115 } 116 status &= ~RX_INT; 117 platform->clearConsoleInt(); 118 119 if (device->dataAvailable() && (IER & UART_IER_RDI)) 120 scheduleIntr(&rxIntrEvent); 121 } else { // dll divisor latch 122 ; 123 } 124 break; 125 case 0x1: 126 if (!(LCR & 0x80)) { // Intr Enable Register(IER) 127 pkt->setRaw(IER); 128 } else { // DLM divisor latch MSB 129 ; 130 } 131 break; 132 case 0x2: // Intr Identification Register (IIR) 133 DPRINTF(Uart, "IIR Read, status = %#x\n", (uint32_t)status); 134 135 if (status & RX_INT) /* Rx data interrupt has a higher priority */ 136 pkt->setRaw(IIR_RXID); 137 else if (status & TX_INT) { 138 pkt->setRaw(IIR_TXID); 139 //Tx interrupts are cleared on IIR reads 140 status &= ~TX_INT; 141 } else 142 pkt->setRaw(IIR_NOPEND); 143 144 break; 145 case 0x3: // Line Control Register (LCR) 146 pkt->setRaw(LCR); 147 break; 148 case 0x4: // Modem Control Register (MCR) 149 pkt->setRaw(MCR); 150 break; 151 case 0x5: // Line Status Register (LSR) 152 uint8_t lsr; 153 lsr = 0; 154 // check if there are any bytes to be read 155 if (device->dataAvailable()) 156 lsr = UART_LSR_DR; 157 lsr |= UART_LSR_TEMT | UART_LSR_THRE; 158 pkt->setRaw(lsr); 159 break; 160 case 0x6: // Modem Status Register (MSR) 161 pkt->setRaw((uint8_t)0); 162 break; 163 case 0x7: // Scratch Register (SCR) 164 pkt->setRaw((uint8_t)0); // doesn't exist with at 8250. 165 break; 166 default: 167 panic("Tried to access a UART port that doesn't exist\n"); 168 break; 169 } 170/* uint32_t d32 = *data; 171 DPRINTF(Uart, "Register read to register %#x returned %#x\n", daddr, d32); 172*/ 173 pkt->makeAtomicResponse(); 174 return pioDelay; 175} 176 177Tick 178Uart8250::write(PacketPtr pkt) 179{ 180 181 assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); 182 assert(pkt->getSize() == 1); 183 184 Addr daddr = pkt->getAddr() - pioAddr; 185 186 DPRINTF(Uart, " write register %#x value %#x\n", daddr, 187 pkt->getRaw<uint8_t>()); 188 189 switch (daddr) { 190 case 0x0: 191 if (!(LCR & 0x80)) { // write byte 192 device->writeData(pkt->getRaw<uint8_t>()); 193 platform->clearConsoleInt(); 194 status &= ~TX_INT; 195 if (UART_IER_THRI & IER) 196 scheduleIntr(&txIntrEvent); 197 } else { // dll divisor latch 198 ; 199 } 200 break; 201 case 0x1: 202 if (!(LCR & 0x80)) { // Intr Enable Register(IER) 203 IER = pkt->getRaw<uint8_t>(); 204 if (UART_IER_THRI & IER) 205 { 206 DPRINTF(Uart, 207 "IER: IER_THRI set, scheduling TX intrrupt\n"); 208 if (curTick() - lastTxInt > 225 * SimClock::Int::ns) { 209 DPRINTF(Uart, "-- Interrupting Immediately... %d,%d\n", 210 curTick(), lastTxInt); 211 txIntrEvent.process(); 212 } else { 213 DPRINTF(Uart, "-- Delaying interrupt... %d,%d\n", 214 curTick(), lastTxInt); 215 scheduleIntr(&txIntrEvent); 216 } 217 } 218 else 219 { 220 DPRINTF(Uart, "IER: IER_THRI cleared, " 221 "descheduling TX intrrupt\n"); 222 if (txIntrEvent.scheduled()) 223 deschedule(txIntrEvent); 224 if (status & TX_INT) 225 platform->clearConsoleInt(); 226 status &= ~TX_INT; 227 } 228 229 if ((UART_IER_RDI & IER) && device->dataAvailable()) { 230 DPRINTF(Uart, 231 "IER: IER_RDI set, scheduling RX intrrupt\n"); 232 scheduleIntr(&rxIntrEvent); 233 } else { 234 DPRINTF(Uart, "IER: IER_RDI cleared, " 235 "descheduling RX intrrupt\n"); 236 if (rxIntrEvent.scheduled()) 237 deschedule(rxIntrEvent); 238 if (status & RX_INT) 239 platform->clearConsoleInt(); 240 status &= ~RX_INT; 241 } 242 } else { // DLM divisor latch MSB 243 ; 244 } 245 break; 246 case 0x2: // FIFO Control Register (FCR) 247 break; 248 case 0x3: // Line Control Register (LCR) 249 LCR = pkt->getRaw<uint8_t>(); 250 break; 251 case 0x4: // Modem Control Register (MCR) 252 if (pkt->getRaw<uint8_t>() == (UART_MCR_LOOP | 0x0A)) 253 MCR = 0x9A; 254 break; 255 case 0x7: // Scratch Register (SCR) 256 // We are emulating a 8250 so we don't have a scratch reg 257 break; 258 default: 259 panic("Tried to access a UART port that doesn't exist\n"); 260 break; 261 } 262 pkt->makeAtomicResponse(); 263 return pioDelay; 264} 265 266void 267Uart8250::dataAvailable() 268{ 269 // if the kernel wants an interrupt when we have data 270 if (IER & UART_IER_RDI) 271 { 272 platform->postConsoleInt(); 273 status |= RX_INT; 274 } 275 276} 277 278AddrRangeList 279Uart8250::getAddrRanges() const 280{ 281 AddrRangeList ranges; 282 ranges.push_back(RangeSize(pioAddr, pioSize)); 283 return ranges; 284} 285 286void 287Uart8250::serialize(CheckpointOut &cp) const 288{ 289 SERIALIZE_SCALAR(status); 290 SERIALIZE_SCALAR(IER); 291 SERIALIZE_SCALAR(DLAB); 292 SERIALIZE_SCALAR(LCR); 293 SERIALIZE_SCALAR(MCR); 294 Tick rxintrwhen; 295 if (rxIntrEvent.scheduled()) 296 rxintrwhen = rxIntrEvent.when(); 297 else 298 rxintrwhen = 0; 299 Tick txintrwhen; 300 if (txIntrEvent.scheduled()) 301 txintrwhen = txIntrEvent.when(); 302 else 303 txintrwhen = 0; 304 SERIALIZE_SCALAR(rxintrwhen); 305 SERIALIZE_SCALAR(txintrwhen); 306} 307 308void 309Uart8250::unserialize(CheckpointIn &cp) 310{ 311 UNSERIALIZE_SCALAR(status); 312 UNSERIALIZE_SCALAR(IER); 313 UNSERIALIZE_SCALAR(DLAB); 314 UNSERIALIZE_SCALAR(LCR); 315 UNSERIALIZE_SCALAR(MCR); 316 Tick rxintrwhen; 317 Tick txintrwhen; 318 UNSERIALIZE_SCALAR(rxintrwhen); 319 UNSERIALIZE_SCALAR(txintrwhen); 320 if (rxintrwhen != 0) 321 schedule(rxIntrEvent, rxintrwhen); 322 if (txintrwhen != 0) 323 schedule(txIntrEvent, txintrwhen); 324} 325 326Uart8250 * 327Uart8250Params::create() 328{ 329 return new Uart8250(this); 330} 331