Hand merge
[gem5.git] / dev / uart.cc
1 /*
2 * Copyright (c) 2004 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
29 /* @file
30 * Implements a 8250 UART
31 */
32
33 #include <string>
34 #include <vector>
35
36 #include "base/inifile.hh"
37 #include "base/str.hh" // for to_number
38 #include "base/trace.hh"
39 #include "dev/simconsole.hh"
40 #include "dev/uart.hh"
41 #include "dev/platform.hh"
42 #include "mem/bus/bus.hh"
43 #include "mem/bus/pio_interface.hh"
44 #include "mem/bus/pio_interface_impl.hh"
45 #include "mem/functional_mem/memory_control.hh"
46 #include "sim/builder.hh"
47
48 using namespace std;
49
50 Uart::IntrEvent::IntrEvent(Uart *u, int bit)
51 : Event(&mainEventQueue), uart(u)
52 {
53 DPRINTF(Uart, "UART Interrupt Event Initilizing\n");
54 intrBit = bit;
55 }
56
57 const char *
58 Uart::IntrEvent::description()
59 {
60 return "uart interrupt delay event";
61 }
62
63 void
64 Uart::IntrEvent::process()
65 {
66 if (intrBit & uart->IER) {
67 DPRINTF(Uart, "UART InterEvent, interrupting\n");
68 uart->platform->postConsoleInt();
69 uart->status |= intrBit;
70 }
71 else
72 DPRINTF(Uart, "UART InterEvent, not interrupting\n");
73
74 }
75
76 /* The linux serial driver (8250.c about line 1182) loops reading from
77 * the device until the device reports it has no more data to
78 * read. After a maximum of 255 iterations the code prints "serial8250
79 * too much work for irq X," and breaks out of the loop. Since the
80 * simulated system is so much slower than the actual system, if a
81 * user is typing on the keyboard it is very easy for them to provide
82 * input at a fast enough rate to not allow the loop to exit and thus
83 * the error to be printed. This magic number provides a delay between
84 * the time the UART receives a character to send to the simulated
85 * system and the time it actually notifies the system it has a
86 * character to send to alleviate this problem. --Ali
87 */
88 void
89 Uart::IntrEvent::scheduleIntr()
90 {
91 static const Tick interval = (Tick)((Clock::Float::s / 2e9) * 450);
92 DPRINTF(Uart, "Scheduling IER interrupt for %#x, at cycle %lld\n", intrBit,
93 curTick + interval);
94 if (!scheduled())
95 schedule(curTick + interval);
96 else
97 reschedule(curTick + interval);
98 }
99
100 Uart::Uart(const string &name, SimConsole *c, MemoryController *mmu, Addr a,
101 Addr s, HierParams *hier, Bus *bus, Tick pio_latency, Platform *p)
102 : PioDevice(name, p), addr(a), size(s), cons(c),
103 txIntrEvent(this, TX_INT), rxIntrEvent(this, RX_INT)
104 {
105 mmu->add_child(this, RangeSize(addr, size));
106
107
108 if (bus) {
109 pioInterface = newPioInterface(name, hier, bus, this,
110 &Uart::cacheAccess);
111 pioInterface->addAddrRange(RangeSize(addr, size));
112 pioLatency = pio_latency * bus->clockRatio;
113 }
114
115 readAddr = 0;
116 IER = 0;
117 DLAB = 0;
118 LCR = 0;
119 MCR = 0;
120 status = 0;
121
122 // set back pointers
123 cons->uart = this;
124 platform->uart = this;
125
126 }
127
128 Fault
129 Uart::read(MemReqPtr &req, uint8_t *data)
130 {
131 Addr daddr = req->paddr - (addr & EV5::PAddrImplMask);
132 DPRINTF(Uart, " read register %#x\n", daddr);
133
134
135
136 #ifdef ALPHA_TLASER
137
138 switch (req->size) {
139 case sizeof(uint64_t):
140 *(uint64_t *)data = 0;
141 break;
142 case sizeof(uint32_t):
143 *(uint32_t *)data = 0;
144 break;
145 case sizeof(uint16_t):
146 *(uint16_t *)data = 0;
147 break;
148 case sizeof(uint8_t):
149 *(uint8_t *)data = 0;
150 break;
151 }
152
153 switch (daddr) {
154 case 0x80: // Status Register
155 if (readAddr == 3) {
156 readAddr = 0;
157 if (status & TX_INT)
158 *data = (1 << 4);
159 else if (status & RX_INT)
160 *data = (1 << 5);
161 else
162 DPRINTF(Uart, "spurious read\n");
163
164 } else {
165 *data = (1 << 2);
166 if (status & RX_INT)
167 *data |= (1 << 0);
168 }
169 break;
170
171 case 0xc0: // Data register (RX)
172 if (!cons->dataAvailable())
173 panic("No data to read");
174
175 cons->in(*data);
176
177 if (!cons->dataAvailable()) {
178 platform->clearConsoleInt();
179 status &= ~RX_INT;
180 }
181
182 DPRINTF(Uart, "read data register \'%c\' %2x\n",
183 isprint(*data) ? *data : ' ', *data);
184 break;
185 }
186
187
188 #else
189
190
191 assert(req->size == 1);
192
193 switch (daddr) {
194 case 0x0:
195 if (!(LCR & 0x80)) { // read byte
196 if (cons->dataAvailable())
197 cons->in(*data);
198 else {
199 *(uint8_t*)data = 0;
200 // A limited amount of these are ok.
201 DPRINTF(Uart, "empty read of RX register\n");
202 }
203 status &= ~RX_INT;
204 platform->clearConsoleInt();
205
206 if (cons->dataAvailable() && (IER & UART_IER_RDI))
207 rxIntrEvent.scheduleIntr();
208 } else { // dll divisor latch
209 ;
210 }
211 break;
212 case 0x1:
213 if (!(LCR & 0x80)) { // Intr Enable Register(IER)
214 *(uint8_t*)data = IER;
215 } else { // DLM divisor latch MSB
216 ;
217 }
218 break;
219 case 0x2: // Intr Identification Register (IIR)
220 DPRINTF(Uart, "IIR Read, status = %#x\n", (uint32_t)status);
221 if (status)
222 *(uint8_t*)data = 0;
223 else
224 *(uint8_t*)data = 1;
225 break;
226 case 0x3: // Line Control Register (LCR)
227 *(uint8_t*)data = LCR;
228 break;
229 case 0x4: // Modem Control Register (MCR)
230 break;
231 case 0x5: // Line Status Register (LSR)
232 uint8_t lsr;
233 lsr = 0;
234 // check if there are any bytes to be read
235 if (cons->dataAvailable())
236 lsr = UART_LSR_DR;
237 lsr |= UART_LSR_TEMT | UART_LSR_THRE;
238 *(uint8_t*)data = lsr;
239 break;
240 case 0x6: // Modem Status Register (MSR)
241 *(uint8_t*)data = 0;
242 break;
243 case 0x7: // Scratch Register (SCR)
244 *(uint8_t*)data = 0; // doesn't exist with at 8250.
245 break;
246 default:
247 panic("Tried to access a UART port that doesn't exist\n");
248 break;
249 }
250
251 #endif
252 return No_Fault;
253
254 }
255
256 Fault
257 Uart::write(MemReqPtr &req, const uint8_t *data)
258 {
259 Addr daddr = req->paddr - (addr & EV5::PAddrImplMask);
260
261 DPRINTF(Uart, " write register %#x value %#x\n", daddr, *(uint8_t*)data);
262
263 #ifdef ALPHA_TLASER
264
265 switch (daddr) {
266 case 0x80:
267 readAddr = *data;
268 switch (*data) {
269 case 0x28: // Ack of TX
270 if ((status & TX_INT) == 0)
271 panic("Ack of transmit, though there was no interrupt");
272
273 status &= ~TX_INT;
274 platform->clearConsoleInt();
275 break;
276 case 0x00:
277 case 0x01:
278 case 0x03: // going to read RR3
279 case 0x12:
280 break;
281 default:
282 DPRINTF(Uart, "writing status register %#x \n",
283 *(uint64_t *)data);
284 break;
285 }
286 break;
287
288 case 0xc0: // Data register (TX)
289 cons->out(*(uint64_t *)data);
290 platform->postConsoleInt();
291 status |= TX_INT;
292 break;
293 }
294
295
296 #else
297 switch (daddr) {
298 case 0x0:
299 if (!(LCR & 0x80)) { // write byte
300 cons->out(*(uint8_t *)data);
301 platform->clearConsoleInt();
302 status &= ~TX_INT;
303 if (UART_IER_THRI & IER)
304 txIntrEvent.scheduleIntr();
305 } else { // dll divisor latch
306 ;
307 }
308 break;
309 case 0x1:
310 if (!(LCR & 0x80)) { // Intr Enable Register(IER)
311 IER = *(uint8_t*)data;
312 if (UART_IER_THRI & IER)
313 {
314 DPRINTF(Uart, "IER: IER_THRI set, scheduling TX intrrupt\n");
315 txIntrEvent.scheduleIntr();
316 }
317 else
318 {
319 DPRINTF(Uart, "IER: IER_THRI cleared, descheduling TX intrrupt\n");
320 if (txIntrEvent.scheduled())
321 txIntrEvent.deschedule();
322 if (status & TX_INT)
323 platform->clearConsoleInt();
324 status &= ~TX_INT;
325 }
326
327 if ((UART_IER_RDI & IER) && cons->dataAvailable()) {
328 DPRINTF(Uart, "IER: IER_RDI set, scheduling RX intrrupt\n");
329 rxIntrEvent.scheduleIntr();
330 } else {
331 DPRINTF(Uart, "IER: IER_RDI cleared, descheduling RX intrrupt\n");
332 if (rxIntrEvent.scheduled())
333 rxIntrEvent.deschedule();
334 if (status & RX_INT)
335 platform->clearConsoleInt();
336 status &= ~RX_INT;
337 }
338 } else { // DLM divisor latch MSB
339 ;
340 }
341 break;
342 case 0x2: // FIFO Control Register (FCR)
343 break;
344 case 0x3: // Line Control Register (LCR)
345 LCR = *(uint8_t*)data;
346 break;
347 case 0x4: // Modem Control Register (MCR)
348 if (*(uint8_t*)data == (UART_MCR_LOOP | 0x0A))
349 MCR = 0x9A;
350 break;
351 case 0x7: // Scratch Register (SCR)
352 // We are emulating a 8250 so we don't have a scratch reg
353 break;
354 default:
355 panic("Tried to access a UART port that doesn't exist\n");
356 break;
357 }
358 #endif
359
360 return No_Fault;
361 }
362
363 void
364 Uart::dataAvailable()
365 {
366 #ifdef ALPHA_TLASER
367 platform->postConsoleInt();
368 status |= RX_INT;
369 #else
370
371 // if the kernel wants an interrupt when we have data
372 if (IER & UART_IER_RDI)
373 {
374 platform->postConsoleInt();
375 status |= RX_INT;
376 }
377
378 #endif
379 }
380
381 Tick
382 Uart::cacheAccess(MemReqPtr &req)
383 {
384 return curTick + pioLatency;
385 }
386
387 void
388 Uart::serialize(ostream &os)
389 {
390 #ifdef ALPHA_TLASER
391 SERIALIZE_SCALAR(readAddr);
392 SERIALIZE_SCALAR(status);
393 #else
394 SERIALIZE_SCALAR(status);
395 SERIALIZE_SCALAR(IER);
396 SERIALIZE_SCALAR(DLAB);
397 SERIALIZE_SCALAR(LCR);
398 SERIALIZE_SCALAR(MCR);
399 Tick rxintrwhen;
400 if (rxIntrEvent.scheduled())
401 rxintrwhen = rxIntrEvent.when();
402 else
403 rxintrwhen = 0;
404 Tick txintrwhen;
405 if (txIntrEvent.scheduled())
406 txintrwhen = txIntrEvent.when();
407 else
408 txintrwhen = 0;
409 SERIALIZE_SCALAR(rxintrwhen);
410 SERIALIZE_SCALAR(txintrwhen);
411 #endif
412 }
413
414 void
415 Uart::unserialize(Checkpoint *cp, const std::string &section)
416 {
417 #ifdef ALPHA_TLASER
418 UNSERIALIZE_SCALAR(readAddr);
419 UNSERIALIZE_SCALAR(status);
420 #else
421 UNSERIALIZE_SCALAR(status);
422 UNSERIALIZE_SCALAR(IER);
423 UNSERIALIZE_SCALAR(DLAB);
424 UNSERIALIZE_SCALAR(LCR);
425 UNSERIALIZE_SCALAR(MCR);
426 Tick rxintrwhen;
427 Tick txintrwhen;
428 UNSERIALIZE_SCALAR(rxintrwhen);
429 UNSERIALIZE_SCALAR(txintrwhen);
430 if (rxintrwhen != 0)
431 rxIntrEvent.schedule(rxintrwhen);
432 if (txintrwhen != 0)
433 txIntrEvent.schedule(txintrwhen);
434 #endif
435
436 }
437
438 BEGIN_DECLARE_SIM_OBJECT_PARAMS(Uart)
439
440 SimObjectParam<SimConsole *> console;
441 SimObjectParam<MemoryController *> mmu;
442 SimObjectParam<Platform *> platform;
443 Param<Addr> addr;
444 Param<Addr> size;
445 SimObjectParam<Bus*> io_bus;
446 Param<Tick> pio_latency;
447 SimObjectParam<HierParams *> hier;
448
449
450 END_DECLARE_SIM_OBJECT_PARAMS(Uart)
451
452 BEGIN_INIT_SIM_OBJECT_PARAMS(Uart)
453
454 INIT_PARAM(console, "The console"),
455 INIT_PARAM(mmu, "Memory Controller"),
456 INIT_PARAM(platform, "Pointer to platfrom"),
457 INIT_PARAM(addr, "Device Address"),
458 INIT_PARAM_DFLT(size, "Device size", 0x8),
459 INIT_PARAM_DFLT(io_bus, "The IO Bus to attach to", NULL),
460 INIT_PARAM_DFLT(pio_latency, "Programmed IO latency in bus cycles", 1),
461 INIT_PARAM_DFLT(hier, "Hierarchy global variables", &defaultHierParams)
462
463 END_INIT_SIM_OBJECT_PARAMS(Uart)
464
465 CREATE_SIM_OBJECT(Uart)
466 {
467 return new Uart(getInstanceName(), console, mmu, addr, size, hier, io_bus,
468 pio_latency, platform);
469 }
470
471 REGISTER_SIM_OBJECT("Uart", Uart)