Updated copyright on Tsunami and kern/linux files.
[gem5.git] / dev / tsunami_uart.cc
1 /* $Id$ */
2
3 /* @file
4 * Tsunami UART
5 */
6
7 /*
8 * Copyright (C) 1998 by the Board of Trustees
9 * of Leland Stanford Junior University.
10 * Copyright (C) 1998 Digital Equipment Corporation
11 *
12 * This file is part of the SimOS distribution.
13 * See LICENSE file for terms of the license.
14 *
15 */
16
17 #include <string>
18 #include <vector>
19
20 #include "base/inifile.hh"
21 #include "base/str.hh" // for to_number
22 #include "base/trace.hh"
23 #include "dev/console.hh"
24 #include "dev/tsunami_uart.hh"
25 #include "mem/bus/bus.hh"
26 #include "mem/bus/pio_interface.hh"
27 #include "mem/bus/pio_interface_impl.hh"
28 #include "mem/functional_mem/memory_control.hh"
29 #include "sim/builder.hh"
30 #include "targetarch/ev5.hh"
31
32 using namespace std;
33
34 #define CONS_INT_TX 0x01 // interrupt enable / state bits
35 #define CONS_INT_RX 0x02
36
37
38 TsunamiUart::IntrEvent::IntrEvent(TsunamiUart *u)
39 : Event(&mainEventQueue), uart(u)
40 {
41 DPRINTF(TsunamiUart, "UART Interrupt Event Initilizing\n");
42 }
43
44 const char *
45 TsunamiUart::IntrEvent::description()
46 {
47 return "tsunami uart interrupt delay event";
48 }
49
50 void
51 TsunamiUart::IntrEvent::process()
52 {
53 if (UART_IER_THRI & uart->IER) {
54 DPRINTF(TsunamiUart, "UART InterEvent, interrupting\n");
55 uart->cons->raiseInt(CONS_INT_TX);
56 }
57 else
58 DPRINTF(TsunamiUart, "UART InterEvent, not interrupting\n");
59
60 }
61
62 void
63 TsunamiUart::IntrEvent::scheduleIntr()
64 {
65 DPRINTF(TsunamiUart, "Scheduling IER interrupt\n");
66 if (!scheduled())
67 schedule(curTick + 300);
68 else
69 reschedule(curTick + 300);
70 }
71
72
73
74 TsunamiUart::TsunamiUart(const string &name, SimConsole *c,
75 MemoryController *mmu, Addr a,
76 HierParams *hier, Bus *bus)
77 : PioDevice(name), addr(a), cons(c), status_store(0), valid_char(false),
78 intrEvent(this)
79 {
80 mmu->add_child(this, Range<Addr>(addr, addr + size));
81
82 if (bus) {
83 pioInterface = newPioInterface(name, hier, bus, this,
84 &TsunamiUart::cacheAccess);
85 pioInterface->addAddrRange(addr, addr + size - 1);
86 }
87
88 IER = 0;
89 }
90
91 Fault
92 TsunamiUart::read(MemReqPtr &req, uint8_t *data)
93 {
94 Addr daddr = req->paddr - (addr & PA_IMPL_MASK);
95 DPRINTF(TsunamiUart, " read register %#x\n", daddr);
96
97 switch (req->size) {
98 case sizeof(uint64_t):
99 *(uint64_t *)data = 0;
100 break;
101 case sizeof(uint32_t):
102 *(uint32_t *)data = 0;
103 break;
104 case sizeof(uint16_t):
105 *(uint16_t *)data = 0;
106 break;
107 case sizeof(uint8_t):
108 *(uint8_t *)data = 0;
109 break;
110 }
111
112
113 switch(daddr) {
114 case 0x5: // Status Register
115 {
116 int status = cons->intStatus();
117 if (!valid_char) {
118 valid_char = cons->in(next_char);
119 if (!valid_char)
120 status &= ~CONS_INT_RX;
121 } else {
122 status |= CONS_INT_RX;
123 }
124
125 if (status_store == 3) {
126 // RR3 stuff? Don't really understand it, btw
127 status_store = 0;
128 if (status & CONS_INT_TX) {
129 *data = (1 << 4);
130 return No_Fault;
131 } else if (status & CONS_INT_RX) {
132 *data = (1 << 5);
133 return No_Fault;
134 } else {
135 DPRINTF(TsunamiUart, "spurious read\n");
136 return No_Fault;
137 }
138 } else {
139 int reg = (1 << 2) | (1 << 5) | (1 << 6);
140 if (status & CONS_INT_RX)
141 reg |= (1 << 0);
142 *data = reg;
143 return No_Fault;
144 }
145 break;
146 }
147
148 case 0x0: // Data register (RX)
149 DPRINTF(TsunamiUart, "read data register \'%c\' %#02x\n",
150 isprint(next_char) ? next_char : ' ', next_char);
151
152 *data = next_char;
153 valid_char = false;
154 return No_Fault;
155
156 case 0x1: // Interrupt Enable Register
157 // This is the lovely way linux checks there is actually a serial
158 // port at the desired address
159 if (IER == 0)
160 *data = 0;
161 else if (IER == 0x0F)
162 *data = 0x0F;
163 else
164 *data = 0;
165 return No_Fault;
166 case 0x2:
167 // High two bits need to be clear for an 8250 (simple) serial port
168 // Low bit of IIR is 0 for a pending interrupt, 1 otherwise.
169 int status = cons->intStatus();
170 status = (status & 0x1) | (status >> 1);
171 *data = (~status) & 0x1 ;
172 return No_Fault;
173 }
174 *data = 0;
175 // panic("%s: read daddr=%#x type=read *data=%#x\n", name(), daddr, *data);
176
177 return No_Fault;
178 }
179
180 Fault
181 TsunamiUart::write(MemReqPtr &req, const uint8_t *data)
182 {
183 Addr daddr = req->paddr - (addr & PA_IMPL_MASK);
184
185 DPRINTF(TsunamiUart, " write register %#x value %#x\n", daddr, *(uint8_t*)data);
186
187 switch (daddr) {
188 case 0x3:
189 status_store = *data;
190 switch (*data) {
191 case 0x03: // going to read RR3
192 return No_Fault;
193
194 case 0x28: // Ack of TX
195 {
196 if ((cons->intStatus() & CONS_INT_TX) == 0)
197 panic("Ack of transmit, though there was no interrupt");
198
199 cons->clearInt(CONS_INT_TX);
200 return No_Fault;
201 }
202
203 case 0x00:
204 case 0x01:
205 case 0x12:
206 // going to write data???
207 return No_Fault;
208
209 default:
210 DPRINTF(TsunamiUart, "writing status register %#x \n",
211 *(uint8_t *)data);
212 return No_Fault;
213 }
214
215 case 0x0: // Data register (TX)
216 char ourchar;
217 ourchar = *(uint64_t *)data;
218 if ((isprint(ourchar) || iscntrl(ourchar)) && (ourchar != 0x0C))
219 cons->out(ourchar);
220 cons->clearInt(CONS_INT_TX);
221 intrEvent.scheduleIntr();
222 return No_Fault;
223 break;
224 case 0x1: // IER
225 IER = *(uint8_t*)data;
226 DPRINTF(TsunamiUart, "writing to IER [%#x]\n", IER);
227 if (UART_IER_THRI & IER)
228 cons->raiseInt(CONS_INT_TX);
229 else {
230 cons->clearInt(CONS_INT_TX);
231 if (intrEvent.scheduled())
232 intrEvent.deschedule();
233 }
234 return No_Fault;
235 break;
236 case 0x4: // MCR
237 DPRINTF(TsunamiUart, "writing to MCR %#x\n", *(uint8_t*)data);
238 return No_Fault;
239
240 }
241
242 return No_Fault;
243 }
244
245 Tick
246 TsunamiUart::cacheAccess(MemReqPtr &req)
247 {
248 return curTick + 1000;
249 }
250
251 void
252 TsunamiUart::serialize(ostream &os)
253 {
254 SERIALIZE_SCALAR(status_store);
255 SERIALIZE_SCALAR(next_char);
256 SERIALIZE_SCALAR(valid_char);
257 SERIALIZE_SCALAR(IER);
258 }
259
260 void
261 TsunamiUart::unserialize(Checkpoint *cp, const std::string &section)
262 {
263 UNSERIALIZE_SCALAR(status_store);
264 UNSERIALIZE_SCALAR(next_char);
265 UNSERIALIZE_SCALAR(valid_char);
266 UNSERIALIZE_SCALAR(IER);
267 }
268
269 BEGIN_DECLARE_SIM_OBJECT_PARAMS(TsunamiUart)
270
271 SimObjectParam<SimConsole *> console;
272 SimObjectParam<MemoryController *> mmu;
273 Param<Addr> addr;
274 SimObjectParam<Bus*> io_bus;
275 SimObjectParam<HierParams *> hier;
276
277
278 END_DECLARE_SIM_OBJECT_PARAMS(TsunamiUart)
279
280 BEGIN_INIT_SIM_OBJECT_PARAMS(TsunamiUart)
281
282 INIT_PARAM(console, "The console"),
283 INIT_PARAM(mmu, "Memory Controller"),
284 INIT_PARAM(addr, "Device Address"),
285 INIT_PARAM_DFLT(io_bus, "The IO Bus to attach to", NULL),
286 INIT_PARAM_DFLT(hier, "Hierarchy global variables", &defaultHierParams)
287
288 END_INIT_SIM_OBJECT_PARAMS(TsunamiUart)
289
290 CREATE_SIM_OBJECT(TsunamiUart)
291 {
292 return new TsunamiUart(getInstanceName(), console, mmu, addr, hier, io_bus);
293 }
294
295 REGISTER_SIM_OBJECT("TsunamiUart", TsunamiUart)