dev/arm: get rid of AmbaDev namespace
[gem5.git] / src / dev / ide_ctrl.cc
1 /*
2 * Copyright (c) 2004-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: Andrew Schultz
29 * Ali Saidi
30 * Miguel Serrano
31 */
32
33 #include <string>
34
35 #include "cpu/intr_control.hh"
36 #include "debug/IdeCtrl.hh"
37 #include "dev/ide_ctrl.hh"
38 #include "dev/ide_disk.hh"
39 #include "mem/packet.hh"
40 #include "mem/packet_access.hh"
41 #include "params/IdeController.hh"
42 #include "sim/byteswap.hh"
43
44 // clang complains about std::set being overloaded with Packet::set if
45 // we open up the entire namespace std
46 using std::string;
47
48 // Bus master IDE registers
49 enum BMIRegOffset {
50 BMICommand = 0x0,
51 BMIStatus = 0x2,
52 BMIDescTablePtr = 0x4
53 };
54
55 // PCI config space registers
56 enum ConfRegOffset {
57 PrimaryTiming = 0x40,
58 SecondaryTiming = 0x42,
59 DeviceTiming = 0x44,
60 UDMAControl = 0x48,
61 UDMATiming = 0x4A,
62 IDEConfig = 0x54
63 };
64
65 static const uint16_t timeRegWithDecodeEn = 0x8000;
66
67 IdeController::Channel::Channel(
68 string newName, Addr _cmdSize, Addr _ctrlSize) :
69 _name(newName),
70 cmdAddr(0), cmdSize(_cmdSize), ctrlAddr(0), ctrlSize(_ctrlSize),
71 master(NULL), slave(NULL), selected(NULL)
72 {
73 memset(&bmiRegs, 0, sizeof(bmiRegs));
74 bmiRegs.status.dmaCap0 = 1;
75 bmiRegs.status.dmaCap1 = 1;
76 }
77
78 IdeController::Channel::~Channel()
79 {
80 }
81
82 IdeController::IdeController(Params *p)
83 : PciDev(p), primary(name() + ".primary", BARSize[0], BARSize[1]),
84 secondary(name() + ".secondary", BARSize[2], BARSize[3]),
85 bmiAddr(0), bmiSize(BARSize[4]),
86 primaryTiming(htole(timeRegWithDecodeEn)),
87 secondaryTiming(htole(timeRegWithDecodeEn)),
88 deviceTiming(0), udmaControl(0), udmaTiming(0), ideConfig(0),
89 ioEnabled(false), bmEnabled(false),
90 ioShift(p->io_shift), ctrlOffset(p->ctrl_offset)
91 {
92 if (params()->disks.size() > 3)
93 panic("IDE controllers support a maximum of 4 devices attached!\n");
94
95 // Assign the disks to channels
96 int numDisks = params()->disks.size();
97 if (numDisks > 0)
98 primary.master = params()->disks[0];
99 if (numDisks > 1)
100 primary.slave = params()->disks[1];
101 if (numDisks > 2)
102 secondary.master = params()->disks[2];
103 if (numDisks > 3)
104 secondary.slave = params()->disks[3];
105
106 for (int i = 0; i < params()->disks.size(); i++) {
107 params()->disks[i]->setController(this);
108 }
109 primary.select(false);
110 secondary.select(false);
111
112 if ((BARAddrs[0] & ~BAR_IO_MASK) && (!legacyIO[0] || ioShift)) {
113 primary.cmdAddr = BARAddrs[0]; primary.cmdSize = BARSize[0];
114 primary.ctrlAddr = BARAddrs[1]; primary.ctrlSize = BARAddrs[1];
115 }
116 if ((BARAddrs[2] & ~BAR_IO_MASK) && (!legacyIO[2] || ioShift)) {
117 secondary.cmdAddr = BARAddrs[2]; secondary.cmdSize = BARSize[2];
118 secondary.ctrlAddr = BARAddrs[3]; secondary.ctrlSize = BARAddrs[3];
119 }
120
121 ioEnabled = (config.command & htole(PCI_CMD_IOSE));
122 bmEnabled = (config.command & htole(PCI_CMD_BME));
123 }
124
125 bool
126 IdeController::isDiskSelected(IdeDisk *diskPtr)
127 {
128 return (primary.selected == diskPtr || secondary.selected == diskPtr);
129 }
130
131 void
132 IdeController::intrPost()
133 {
134 primary.bmiRegs.status.intStatus = 1;
135 PciDev::intrPost();
136 }
137
138 void
139 IdeController::setDmaComplete(IdeDisk *disk)
140 {
141 Channel *channel;
142 if (disk == primary.master || disk == primary.slave) {
143 channel = &primary;
144 } else if (disk == secondary.master || disk == secondary.slave) {
145 channel = &secondary;
146 } else {
147 panic("Unable to find disk based on pointer %#x\n", disk);
148 }
149
150 channel->bmiRegs.command.startStop = 0;
151 channel->bmiRegs.status.active = 0;
152 channel->bmiRegs.status.intStatus = 1;
153 }
154
155 Tick
156 IdeController::readConfig(PacketPtr pkt)
157 {
158 int offset = pkt->getAddr() & PCI_CONFIG_SIZE;
159 if (offset < PCI_DEVICE_SPECIFIC) {
160 return PciDev::readConfig(pkt);
161 }
162
163 pkt->allocate();
164
165 switch (pkt->getSize()) {
166 case sizeof(uint8_t):
167 switch (offset) {
168 case DeviceTiming:
169 pkt->set<uint8_t>(deviceTiming);
170 break;
171 case UDMAControl:
172 pkt->set<uint8_t>(udmaControl);
173 break;
174 case PrimaryTiming + 1:
175 pkt->set<uint8_t>(bits(htole(primaryTiming), 15, 8));
176 break;
177 case SecondaryTiming + 1:
178 pkt->set<uint8_t>(bits(htole(secondaryTiming), 15, 8));
179 break;
180 case IDEConfig:
181 pkt->set<uint8_t>(bits(htole(ideConfig), 7, 0));
182 break;
183 case IDEConfig + 1:
184 pkt->set<uint8_t>(bits(htole(ideConfig), 15, 8));
185 break;
186 default:
187 panic("Invalid PCI configuration read for size 1 at offset: %#x!\n",
188 offset);
189 }
190 DPRINTF(IdeCtrl, "PCI read offset: %#x size: 1 data: %#x\n", offset,
191 (uint32_t)pkt->get<uint8_t>());
192 break;
193 case sizeof(uint16_t):
194 switch (offset) {
195 case PrimaryTiming:
196 pkt->set<uint16_t>(primaryTiming);
197 break;
198 case SecondaryTiming:
199 pkt->set<uint16_t>(secondaryTiming);
200 break;
201 case UDMATiming:
202 pkt->set<uint16_t>(udmaTiming);
203 break;
204 case IDEConfig:
205 pkt->set<uint16_t>(ideConfig);
206 break;
207 default:
208 panic("Invalid PCI configuration read for size 2 offset: %#x!\n",
209 offset);
210 }
211 DPRINTF(IdeCtrl, "PCI read offset: %#x size: 2 data: %#x\n", offset,
212 (uint32_t)pkt->get<uint16_t>());
213 break;
214 case sizeof(uint32_t):
215 if (offset == IDEConfig)
216 pkt->set<uint32_t>(ideConfig);
217 else
218 panic("No 32bit reads implemented for this device.");
219 DPRINTF(IdeCtrl, "PCI read offset: %#x size: 4 data: %#x\n", offset,
220 (uint32_t)pkt->get<uint32_t>());
221 break;
222 default:
223 panic("invalid access size(?) for PCI configspace!\n");
224 }
225 pkt->makeAtomicResponse();
226 return configDelay;
227 }
228
229
230 Tick
231 IdeController::writeConfig(PacketPtr pkt)
232 {
233 int offset = pkt->getAddr() & PCI_CONFIG_SIZE;
234 if (offset < PCI_DEVICE_SPECIFIC) {
235 PciDev::writeConfig(pkt);
236 } else {
237 switch (pkt->getSize()) {
238 case sizeof(uint8_t):
239 switch (offset) {
240 case DeviceTiming:
241 deviceTiming = pkt->get<uint8_t>();
242 break;
243 case UDMAControl:
244 udmaControl = pkt->get<uint8_t>();
245 break;
246 case IDEConfig:
247 replaceBits(ideConfig, 7, 0, pkt->get<uint8_t>());
248 break;
249 case IDEConfig + 1:
250 replaceBits(ideConfig, 15, 8, pkt->get<uint8_t>());
251 break;
252 default:
253 panic("Invalid PCI configuration write "
254 "for size 1 offset: %#x!\n", offset);
255 }
256 DPRINTF(IdeCtrl, "PCI write offset: %#x size: 1 data: %#x\n",
257 offset, (uint32_t)pkt->get<uint8_t>());
258 break;
259 case sizeof(uint16_t):
260 switch (offset) {
261 case PrimaryTiming:
262 primaryTiming = pkt->get<uint16_t>();
263 break;
264 case SecondaryTiming:
265 secondaryTiming = pkt->get<uint16_t>();
266 break;
267 case UDMATiming:
268 udmaTiming = pkt->get<uint16_t>();
269 break;
270 case IDEConfig:
271 ideConfig = pkt->get<uint16_t>();
272 break;
273 default:
274 panic("Invalid PCI configuration write "
275 "for size 2 offset: %#x!\n",
276 offset);
277 }
278 DPRINTF(IdeCtrl, "PCI write offset: %#x size: 2 data: %#x\n",
279 offset, (uint32_t)pkt->get<uint16_t>());
280 break;
281 case sizeof(uint32_t):
282 if (offset == IDEConfig)
283 ideConfig = pkt->get<uint32_t>();
284 else
285 panic("Write of unimplemented PCI config. register: %x\n", offset);
286 break;
287 default:
288 panic("invalid access size(?) for PCI configspace!\n");
289 }
290 pkt->makeAtomicResponse();
291 }
292
293 /* Trap command register writes and enable IO/BM as appropriate as well as
294 * BARs. */
295 switch(offset) {
296 case PCI0_BASE_ADDR0:
297 if (BARAddrs[0] != 0)
298 primary.cmdAddr = BARAddrs[0];
299 break;
300
301 case PCI0_BASE_ADDR1:
302 if (BARAddrs[1] != 0)
303 primary.ctrlAddr = BARAddrs[1];
304 break;
305
306 case PCI0_BASE_ADDR2:
307 if (BARAddrs[2] != 0)
308 secondary.cmdAddr = BARAddrs[2];
309 break;
310
311 case PCI0_BASE_ADDR3:
312 if (BARAddrs[3] != 0)
313 secondary.ctrlAddr = BARAddrs[3];
314 break;
315
316 case PCI0_BASE_ADDR4:
317 if (BARAddrs[4] != 0)
318 bmiAddr = BARAddrs[4];
319 break;
320
321 case PCI_COMMAND:
322 DPRINTF(IdeCtrl, "Writing to PCI Command val: %#x\n", config.command);
323 ioEnabled = (config.command & htole(PCI_CMD_IOSE));
324 bmEnabled = (config.command & htole(PCI_CMD_BME));
325 break;
326 }
327 return configDelay;
328 }
329
330 void
331 IdeController::Channel::accessCommand(Addr offset,
332 int size, uint8_t *data, bool read)
333 {
334 const Addr SelectOffset = 6;
335 const uint8_t SelectDevBit = 0x10;
336
337 if (!read && offset == SelectOffset)
338 select(*data & SelectDevBit);
339
340 if (selected == NULL) {
341 assert(size == sizeof(uint8_t));
342 *data = 0;
343 } else if (read) {
344 selected->readCommand(offset, size, data);
345 } else {
346 selected->writeCommand(offset, size, data);
347 }
348 }
349
350 void
351 IdeController::Channel::accessControl(Addr offset,
352 int size, uint8_t *data, bool read)
353 {
354 if (selected == NULL) {
355 assert(size == sizeof(uint8_t));
356 *data = 0;
357 } else if (read) {
358 selected->readControl(offset, size, data);
359 } else {
360 selected->writeControl(offset, size, data);
361 }
362 }
363
364 void
365 IdeController::Channel::accessBMI(Addr offset,
366 int size, uint8_t *data, bool read)
367 {
368 assert(offset + size <= sizeof(BMIRegs));
369 if (read) {
370 memcpy(data, (uint8_t *)&bmiRegs + offset, size);
371 } else {
372 switch (offset) {
373 case BMICommand:
374 {
375 if (size != sizeof(uint8_t))
376 panic("Invalid BMIC write size: %x\n", size);
377
378 BMICommandReg oldVal = bmiRegs.command;
379 BMICommandReg newVal = *data;
380
381 // if a DMA transfer is in progress, R/W control cannot change
382 if (oldVal.startStop && oldVal.rw != newVal.rw)
383 oldVal.rw = newVal.rw;
384
385 if (oldVal.startStop != newVal.startStop) {
386 if (selected == NULL)
387 panic("DMA start for disk which does not exist\n");
388
389 if (oldVal.startStop) {
390 DPRINTF(IdeCtrl, "Stopping DMA transfer\n");
391 bmiRegs.status.active = 0;
392
393 selected->abortDma();
394 } else {
395 DPRINTF(IdeCtrl, "Starting DMA transfer\n");
396 bmiRegs.status.active = 1;
397
398 selected->startDma(letoh(bmiRegs.bmidtp));
399 }
400 }
401
402 bmiRegs.command = newVal;
403 }
404 break;
405 case BMIStatus:
406 {
407 if (size != sizeof(uint8_t))
408 panic("Invalid BMIS write size: %x\n", size);
409
410 BMIStatusReg oldVal = bmiRegs.status;
411 BMIStatusReg newVal = *data;
412
413 // the BMIDEA bit is read only
414 newVal.active = oldVal.active;
415
416 // to reset (set 0) IDEINTS and IDEDMAE, write 1 to each
417 if (oldVal.intStatus && newVal.intStatus)
418 newVal.intStatus = 0; // clear the interrupt?
419 else
420 newVal.intStatus = oldVal.intStatus;
421 if (oldVal.dmaError && newVal.dmaError)
422 newVal.dmaError = 0;
423 else
424 newVal.dmaError = oldVal.dmaError;
425
426 bmiRegs.status = newVal;
427 }
428 break;
429 case BMIDescTablePtr:
430 if (size != sizeof(uint32_t))
431 panic("Invalid BMIDTP write size: %x\n", size);
432 bmiRegs.bmidtp = htole(*(uint32_t *)data & ~0x3);
433 break;
434 default:
435 if (size != sizeof(uint8_t) && size != sizeof(uint16_t) &&
436 size != sizeof(uint32_t))
437 panic("IDE controller write of invalid write size: %x\n", size);
438 memcpy((uint8_t *)&bmiRegs + offset, data, size);
439 }
440 }
441 }
442
443 void
444 IdeController::dispatchAccess(PacketPtr pkt, bool read)
445 {
446 pkt->allocate();
447 if (pkt->getSize() != 1 && pkt->getSize() != 2 && pkt->getSize() !=4)
448 panic("Bad IDE read size: %d\n", pkt->getSize());
449
450 if (!ioEnabled) {
451 pkt->makeAtomicResponse();
452 DPRINTF(IdeCtrl, "io not enabled\n");
453 return;
454 }
455
456 Addr addr = pkt->getAddr();
457 int size = pkt->getSize();
458 uint8_t *dataPtr = pkt->getPtr<uint8_t>();
459
460 if (addr >= primary.cmdAddr &&
461 addr < (primary.cmdAddr + primary.cmdSize)) {
462 addr -= primary.cmdAddr;
463 // linux may have shifted the address by ioShift,
464 // here we shift it back, similarly for ctrlOffset.
465 addr >>= ioShift;
466 primary.accessCommand(addr, size, dataPtr, read);
467 } else if (addr >= primary.ctrlAddr &&
468 addr < (primary.ctrlAddr + primary.ctrlSize)) {
469 addr -= primary.ctrlAddr;
470 addr += ctrlOffset;
471 primary.accessControl(addr, size, dataPtr, read);
472 } else if (addr >= secondary.cmdAddr &&
473 addr < (secondary.cmdAddr + secondary.cmdSize)) {
474 addr -= secondary.cmdAddr;
475 secondary.accessCommand(addr, size, dataPtr, read);
476 } else if (addr >= secondary.ctrlAddr &&
477 addr < (secondary.ctrlAddr + secondary.ctrlSize)) {
478 addr -= secondary.ctrlAddr;
479 secondary.accessControl(addr, size, dataPtr, read);
480 } else if (addr >= bmiAddr && addr < (bmiAddr + bmiSize)) {
481 if (!read && !bmEnabled)
482 return;
483 addr -= bmiAddr;
484 if (addr < sizeof(Channel::BMIRegs)) {
485 primary.accessBMI(addr, size, dataPtr, read);
486 } else {
487 addr -= sizeof(Channel::BMIRegs);
488 secondary.accessBMI(addr, size, dataPtr, read);
489 }
490 } else {
491 panic("IDE controller access to invalid address: %#x\n", addr);
492 }
493
494 #ifndef NDEBUG
495 uint32_t data;
496 if (pkt->getSize() == 1)
497 data = pkt->get<uint8_t>();
498 else if (pkt->getSize() == 2)
499 data = pkt->get<uint16_t>();
500 else
501 data = pkt->get<uint32_t>();
502 DPRINTF(IdeCtrl, "%s from offset: %#x size: %#x data: %#x\n",
503 read ? "Read" : "Write", pkt->getAddr(), pkt->getSize(), data);
504 #endif
505
506 pkt->makeAtomicResponse();
507 }
508
509 Tick
510 IdeController::read(PacketPtr pkt)
511 {
512 dispatchAccess(pkt, true);
513 return pioDelay;
514 }
515
516 Tick
517 IdeController::write(PacketPtr pkt)
518 {
519 dispatchAccess(pkt, false);
520 return pioDelay;
521 }
522
523 void
524 IdeController::serialize(std::ostream &os)
525 {
526 // Serialize the PciDev base class
527 PciDev::serialize(os);
528
529 // Serialize channels
530 primary.serialize("primary", os);
531 secondary.serialize("secondary", os);
532
533 // Serialize config registers
534 SERIALIZE_SCALAR(primaryTiming);
535 SERIALIZE_SCALAR(secondaryTiming);
536 SERIALIZE_SCALAR(deviceTiming);
537 SERIALIZE_SCALAR(udmaControl);
538 SERIALIZE_SCALAR(udmaTiming);
539 SERIALIZE_SCALAR(ideConfig);
540
541 // Serialize internal state
542 SERIALIZE_SCALAR(ioEnabled);
543 SERIALIZE_SCALAR(bmEnabled);
544 SERIALIZE_SCALAR(bmiAddr);
545 SERIALIZE_SCALAR(bmiSize);
546 }
547
548 void
549 IdeController::Channel::serialize(const std::string &base, std::ostream &os)
550 {
551 paramOut(os, base + ".cmdAddr", cmdAddr);
552 paramOut(os, base + ".cmdSize", cmdSize);
553 paramOut(os, base + ".ctrlAddr", ctrlAddr);
554 paramOut(os, base + ".ctrlSize", ctrlSize);
555 uint8_t command = bmiRegs.command;
556 paramOut(os, base + ".bmiRegs.command", command);
557 paramOut(os, base + ".bmiRegs.reserved0", bmiRegs.reserved0);
558 uint8_t status = bmiRegs.status;
559 paramOut(os, base + ".bmiRegs.status", status);
560 paramOut(os, base + ".bmiRegs.reserved1", bmiRegs.reserved1);
561 paramOut(os, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp);
562 paramOut(os, base + ".selectBit", selectBit);
563 }
564
565 void
566 IdeController::unserialize(Checkpoint *cp, const std::string &section)
567 {
568 // Unserialize the PciDev base class
569 PciDev::unserialize(cp, section);
570
571 // Unserialize channels
572 primary.unserialize("primary", cp, section);
573 secondary.unserialize("secondary", cp, section);
574
575 // Unserialize config registers
576 UNSERIALIZE_SCALAR(primaryTiming);
577 UNSERIALIZE_SCALAR(secondaryTiming);
578 UNSERIALIZE_SCALAR(deviceTiming);
579 UNSERIALIZE_SCALAR(udmaControl);
580 UNSERIALIZE_SCALAR(udmaTiming);
581 UNSERIALIZE_SCALAR(ideConfig);
582
583 // Unserialize internal state
584 UNSERIALIZE_SCALAR(ioEnabled);
585 UNSERIALIZE_SCALAR(bmEnabled);
586 UNSERIALIZE_SCALAR(bmiAddr);
587 UNSERIALIZE_SCALAR(bmiSize);
588 }
589
590 void
591 IdeController::Channel::unserialize(const std::string &base, Checkpoint *cp,
592 const std::string &section)
593 {
594 paramIn(cp, section, base + ".cmdAddr", cmdAddr);
595 paramIn(cp, section, base + ".cmdSize", cmdSize);
596 paramIn(cp, section, base + ".ctrlAddr", ctrlAddr);
597 paramIn(cp, section, base + ".ctrlSize", ctrlSize);
598 uint8_t command;
599 paramIn(cp, section, base +".bmiRegs.command", command);
600 bmiRegs.command = command;
601 paramIn(cp, section, base + ".bmiRegs.reserved0", bmiRegs.reserved0);
602 uint8_t status;
603 paramIn(cp, section, base + ".bmiRegs.status", status);
604 bmiRegs.status = status;
605 paramIn(cp, section, base + ".bmiRegs.reserved1", bmiRegs.reserved1);
606 paramIn(cp, section, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp);
607 paramIn(cp, section, base + ".selectBit", selectBit);
608 select(selectBit);
609 }
610
611 IdeController *
612 IdeControllerParams::create()
613 {
614 return new IdeController(this);
615 }