uart8250.cc (13342:1ddb43f47325) uart8250.cc (13343:5a7ac3348cd1)
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;
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;
49using namespace TheISA;
50
51void
52Uart8250::processIntrEvent(int intrBit)
53{
54 if (intrBit & IER) {
55 DPRINTF(Uart, "UART InterEvent, interrupting\n");
56 platform->postConsoleInt();
57 status |= intrBit;
58 lastTxInt = curTick();
59 }
60 else
61 DPRINTF(Uart, "UART InterEvent, not interrupting\n");
62
63}
64
65/* The linux serial driver (8250.c about line 1182) loops reading from
66 * the device until the device reports it has no more data to
67 * read. After a maximum of 255 iterations the code prints "serial8250
68 * too much work for irq X," and breaks out of the loop. Since the
69 * simulated system is so much slower than the actual system, if a
70 * user is typing on the keyboard it is very easy for them to provide
71 * input at a fast enough rate to not allow the loop to exit and thus
72 * the error to be printed. This magic number provides a delay between
73 * the time the UART receives a character to send to the simulated
74 * system and the time it actually notifies the system it has a
75 * character to send to alleviate this problem. --Ali
76 */
77void
78Uart8250::scheduleIntr(Event *event)
79{
80 static const Tick interval = 225 * SimClock::Int::ns;
81 DPRINTF(Uart, "Scheduling IER interrupt for %s, at cycle %lld\n",
82 event->name(), curTick() + interval);
83 if (!event->scheduled())
84 schedule(event, curTick() + interval);
85 else
86 reschedule(event, curTick() + interval);
87}
88
89
90Uart8250::Uart8250(const Params *p)
91 : Uart(p, 8), IER(0), DLAB(0), LCR(0), MCR(0), lastTxInt(0),
92 txIntrEvent([this]{ processIntrEvent(TX_INT); }, "TX"),
93 rxIntrEvent([this]{ processIntrEvent(RX_INT); }, "RX")
94{
95}
96
97Tick
98Uart8250::read(PacketPtr pkt)
99{
100 assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize);
101 assert(pkt->getSize() == 1);
102
103 Addr daddr = pkt->getAddr() - pioAddr;
104
105 DPRINTF(Uart, " read register %#x\n", daddr);
106
107 switch (daddr) {
108 case 0x0:
109 if (!(LCR & 0x80)) { // read byte
110 if (device->dataAvailable())
111 pkt->setRaw(device->readData());
112 else {
113 pkt->setRaw((uint8_t)0);
114 // A limited amount of these are ok.
115 DPRINTF(Uart, "empty read of RX register\n");
116 }
117 status &= ~RX_INT;
118 platform->clearConsoleInt();
119
120 if (device->dataAvailable() && (IER & UART_IER_RDI))
121 scheduleIntr(&rxIntrEvent);
122 } else { // dll divisor latch
123 ;
124 }
125 break;
126 case 0x1:
127 if (!(LCR & 0x80)) { // Intr Enable Register(IER)
128 pkt->setRaw(IER);
129 } else { // DLM divisor latch MSB
130 ;
131 }
132 break;
133 case 0x2: // Intr Identification Register (IIR)
134 DPRINTF(Uart, "IIR Read, status = %#x\n", (uint32_t)status);
135
136 if (status & RX_INT) /* Rx data interrupt has a higher priority */
137 pkt->setRaw(IIR_RXID);
138 else if (status & TX_INT) {
139 pkt->setRaw(IIR_TXID);
140 //Tx interrupts are cleared on IIR reads
141 status &= ~TX_INT;
142 } else
143 pkt->setRaw(IIR_NOPEND);
144
145 break;
146 case 0x3: // Line Control Register (LCR)
147 pkt->setRaw(LCR);
148 break;
149 case 0x4: // Modem Control Register (MCR)
150 pkt->setRaw(MCR);
151 break;
152 case 0x5: // Line Status Register (LSR)
153 uint8_t lsr;
154 lsr = 0;
155 // check if there are any bytes to be read
156 if (device->dataAvailable())
157 lsr = UART_LSR_DR;
158 lsr |= UART_LSR_TEMT | UART_LSR_THRE;
159 pkt->setRaw(lsr);
160 break;
161 case 0x6: // Modem Status Register (MSR)
162 pkt->setRaw((uint8_t)0);
163 break;
164 case 0x7: // Scratch Register (SCR)
165 pkt->setRaw((uint8_t)0); // doesn't exist with at 8250.
166 break;
167 default:
168 panic("Tried to access a UART port that doesn't exist\n");
169 break;
170 }
171/* uint32_t d32 = *data;
172 DPRINTF(Uart, "Register read to register %#x returned %#x\n", daddr, d32);
173*/
174 pkt->makeAtomicResponse();
175 return pioDelay;
176}
177
178Tick
179Uart8250::write(PacketPtr pkt)
180{
181
182 assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize);
183 assert(pkt->getSize() == 1);
184
185 Addr daddr = pkt->getAddr() - pioAddr;
186
187 DPRINTF(Uart, " write register %#x value %#x\n", daddr,
188 pkt->getRaw<uint8_t>());
189
190 switch (daddr) {
191 case 0x0:
192 if (!(LCR & 0x80)) { // write byte
193 device->writeData(pkt->getRaw<uint8_t>());
194 platform->clearConsoleInt();
195 status &= ~TX_INT;
196 if (UART_IER_THRI & IER)
197 scheduleIntr(&txIntrEvent);
198 } else { // dll divisor latch
199 ;
200 }
201 break;
202 case 0x1:
203 if (!(LCR & 0x80)) { // Intr Enable Register(IER)
204 IER = pkt->getRaw<uint8_t>();
205 if (UART_IER_THRI & IER)
206 {
207 DPRINTF(Uart,
208 "IER: IER_THRI set, scheduling TX intrrupt\n");
209 if (curTick() - lastTxInt > 225 * SimClock::Int::ns) {
210 DPRINTF(Uart, "-- Interrupting Immediately... %d,%d\n",
211 curTick(), lastTxInt);
212 txIntrEvent.process();
213 } else {
214 DPRINTF(Uart, "-- Delaying interrupt... %d,%d\n",
215 curTick(), lastTxInt);
216 scheduleIntr(&txIntrEvent);
217 }
218 }
219 else
220 {
221 DPRINTF(Uart, "IER: IER_THRI cleared, "
222 "descheduling TX intrrupt\n");
223 if (txIntrEvent.scheduled())
224 deschedule(txIntrEvent);
225 if (status & TX_INT)
226 platform->clearConsoleInt();
227 status &= ~TX_INT;
228 }
229
230 if ((UART_IER_RDI & IER) && device->dataAvailable()) {
231 DPRINTF(Uart,
232 "IER: IER_RDI set, scheduling RX intrrupt\n");
233 scheduleIntr(&rxIntrEvent);
234 } else {
235 DPRINTF(Uart, "IER: IER_RDI cleared, "
236 "descheduling RX intrrupt\n");
237 if (rxIntrEvent.scheduled())
238 deschedule(rxIntrEvent);
239 if (status & RX_INT)
240 platform->clearConsoleInt();
241 status &= ~RX_INT;
242 }
243 } else { // DLM divisor latch MSB
244 ;
245 }
246 break;
247 case 0x2: // FIFO Control Register (FCR)
248 break;
249 case 0x3: // Line Control Register (LCR)
250 LCR = pkt->getRaw<uint8_t>();
251 break;
252 case 0x4: // Modem Control Register (MCR)
253 if (pkt->getRaw<uint8_t>() == (UART_MCR_LOOP | 0x0A))
254 MCR = 0x9A;
255 break;
256 case 0x7: // Scratch Register (SCR)
257 // We are emulating a 8250 so we don't have a scratch reg
258 break;
259 default:
260 panic("Tried to access a UART port that doesn't exist\n");
261 break;
262 }
263 pkt->makeAtomicResponse();
264 return pioDelay;
265}
266
267void
268Uart8250::dataAvailable()
269{
270 // if the kernel wants an interrupt when we have data
271 if (IER & UART_IER_RDI)
272 {
273 platform->postConsoleInt();
274 status |= RX_INT;
275 }
276
277}
278
279AddrRangeList
280Uart8250::getAddrRanges() const
281{
282 AddrRangeList ranges;
283 ranges.push_back(RangeSize(pioAddr, pioSize));
284 return ranges;
285}
286
287void
288Uart8250::serialize(CheckpointOut &cp) const
289{
290 SERIALIZE_SCALAR(status);
291 SERIALIZE_SCALAR(IER);
292 SERIALIZE_SCALAR(DLAB);
293 SERIALIZE_SCALAR(LCR);
294 SERIALIZE_SCALAR(MCR);
295 Tick rxintrwhen;
296 if (rxIntrEvent.scheduled())
297 rxintrwhen = rxIntrEvent.when();
298 else
299 rxintrwhen = 0;
300 Tick txintrwhen;
301 if (txIntrEvent.scheduled())
302 txintrwhen = txIntrEvent.when();
303 else
304 txintrwhen = 0;
305 SERIALIZE_SCALAR(rxintrwhen);
306 SERIALIZE_SCALAR(txintrwhen);
307}
308
309void
310Uart8250::unserialize(CheckpointIn &cp)
311{
312 UNSERIALIZE_SCALAR(status);
313 UNSERIALIZE_SCALAR(IER);
314 UNSERIALIZE_SCALAR(DLAB);
315 UNSERIALIZE_SCALAR(LCR);
316 UNSERIALIZE_SCALAR(MCR);
317 Tick rxintrwhen;
318 Tick txintrwhen;
319 UNSERIALIZE_SCALAR(rxintrwhen);
320 UNSERIALIZE_SCALAR(txintrwhen);
321 if (rxintrwhen != 0)
322 schedule(rxIntrEvent, rxintrwhen);
323 if (txintrwhen != 0)
324 schedule(txIntrEvent, txintrwhen);
325}
326
327Uart8250 *
328Uart8250Params::create()
329{
330 return new Uart8250(this);
331}
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}