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