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