2 * Copyright (c) 2004-2005 The Regents of The University of Michigan
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.
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.
28 * Authors: Andrew Schultz
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"
44 // clang complains about std::set being overloaded with Packet::set if
45 // we open up the entire namespace std
48 // Bus master IDE registers
55 // PCI config space registers
58 SecondaryTiming
= 0x42,
65 static const uint16_t timeRegWithDecodeEn
= 0x8000;
67 IdeController::Channel::Channel(
68 string newName
, Addr _cmdSize
, Addr _ctrlSize
) :
70 cmdAddr(0), cmdSize(_cmdSize
), ctrlAddr(0), ctrlSize(_ctrlSize
),
71 master(NULL
), slave(NULL
), selected(NULL
)
73 memset(&bmiRegs
, 0, sizeof(bmiRegs
));
74 bmiRegs
.status
.dmaCap0
= 1;
75 bmiRegs
.status
.dmaCap1
= 1;
78 IdeController::Channel::~Channel()
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
)
92 if (params()->disks
.size() > 3)
93 panic("IDE controllers support a maximum of 4 devices attached!\n");
95 // Assign the disks to channels
96 int numDisks
= params()->disks
.size();
98 primary
.master
= params()->disks
[0];
100 primary
.slave
= params()->disks
[1];
102 secondary
.master
= params()->disks
[2];
104 secondary
.slave
= params()->disks
[3];
106 for (int i
= 0; i
< params()->disks
.size(); i
++) {
107 params()->disks
[i
]->setController(this);
109 primary
.select(false);
110 secondary
.select(false);
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];
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];
121 ioEnabled
= (config
.command
& htole(PCI_CMD_IOSE
));
122 bmEnabled
= (config
.command
& htole(PCI_CMD_BME
));
126 IdeController::isDiskSelected(IdeDisk
*diskPtr
)
128 return (primary
.selected
== diskPtr
|| secondary
.selected
== diskPtr
);
132 IdeController::intrPost()
134 primary
.bmiRegs
.status
.intStatus
= 1;
139 IdeController::setDmaComplete(IdeDisk
*disk
)
142 if (disk
== primary
.master
|| disk
== primary
.slave
) {
144 } else if (disk
== secondary
.master
|| disk
== secondary
.slave
) {
145 channel
= &secondary
;
147 panic("Unable to find disk based on pointer %#x\n", disk
);
150 channel
->bmiRegs
.command
.startStop
= 0;
151 channel
->bmiRegs
.status
.active
= 0;
152 channel
->bmiRegs
.status
.intStatus
= 1;
156 IdeController::readConfig(PacketPtr pkt
)
158 int offset
= pkt
->getAddr() & PCI_CONFIG_SIZE
;
159 if (offset
< PCI_DEVICE_SPECIFIC
) {
160 return PciDev::readConfig(pkt
);
165 switch (pkt
->getSize()) {
166 case sizeof(uint8_t):
169 pkt
->set
<uint8_t>(deviceTiming
);
172 pkt
->set
<uint8_t>(udmaControl
);
174 case PrimaryTiming
+ 1:
175 pkt
->set
<uint8_t>(bits(htole(primaryTiming
), 15, 8));
177 case SecondaryTiming
+ 1:
178 pkt
->set
<uint8_t>(bits(htole(secondaryTiming
), 15, 8));
181 pkt
->set
<uint8_t>(bits(htole(ideConfig
), 7, 0));
184 pkt
->set
<uint8_t>(bits(htole(ideConfig
), 15, 8));
187 panic("Invalid PCI configuration read for size 1 at offset: %#x!\n",
190 DPRINTF(IdeCtrl
, "PCI read offset: %#x size: 1 data: %#x\n", offset
,
191 (uint32_t)pkt
->get
<uint8_t>());
193 case sizeof(uint16_t):
196 pkt
->set
<uint16_t>(primaryTiming
);
198 case SecondaryTiming
:
199 pkt
->set
<uint16_t>(secondaryTiming
);
202 pkt
->set
<uint16_t>(udmaTiming
);
205 pkt
->set
<uint16_t>(ideConfig
);
208 panic("Invalid PCI configuration read for size 2 offset: %#x!\n",
211 DPRINTF(IdeCtrl
, "PCI read offset: %#x size: 2 data: %#x\n", offset
,
212 (uint32_t)pkt
->get
<uint16_t>());
214 case sizeof(uint32_t):
215 if (offset
== IDEConfig
)
216 pkt
->set
<uint32_t>(ideConfig
);
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>());
223 panic("invalid access size(?) for PCI configspace!\n");
225 pkt
->makeAtomicResponse();
231 IdeController::writeConfig(PacketPtr pkt
)
233 int offset
= pkt
->getAddr() & PCI_CONFIG_SIZE
;
234 if (offset
< PCI_DEVICE_SPECIFIC
) {
235 PciDev::writeConfig(pkt
);
237 switch (pkt
->getSize()) {
238 case sizeof(uint8_t):
241 deviceTiming
= pkt
->get
<uint8_t>();
244 udmaControl
= pkt
->get
<uint8_t>();
247 replaceBits(ideConfig
, 7, 0, pkt
->get
<uint8_t>());
250 replaceBits(ideConfig
, 15, 8, pkt
->get
<uint8_t>());
253 panic("Invalid PCI configuration write "
254 "for size 1 offset: %#x!\n", offset
);
256 DPRINTF(IdeCtrl
, "PCI write offset: %#x size: 1 data: %#x\n",
257 offset
, (uint32_t)pkt
->get
<uint8_t>());
259 case sizeof(uint16_t):
262 primaryTiming
= pkt
->get
<uint16_t>();
264 case SecondaryTiming
:
265 secondaryTiming
= pkt
->get
<uint16_t>();
268 udmaTiming
= pkt
->get
<uint16_t>();
271 ideConfig
= pkt
->get
<uint16_t>();
274 panic("Invalid PCI configuration write "
275 "for size 2 offset: %#x!\n",
278 DPRINTF(IdeCtrl
, "PCI write offset: %#x size: 2 data: %#x\n",
279 offset
, (uint32_t)pkt
->get
<uint16_t>());
281 case sizeof(uint32_t):
282 if (offset
== IDEConfig
)
283 ideConfig
= pkt
->get
<uint32_t>();
285 panic("Write of unimplemented PCI config. register: %x\n", offset
);
288 panic("invalid access size(?) for PCI configspace!\n");
290 pkt
->makeAtomicResponse();
293 /* Trap command register writes and enable IO/BM as appropriate as well as
296 case PCI0_BASE_ADDR0
:
297 if (BARAddrs
[0] != 0)
298 primary
.cmdAddr
= BARAddrs
[0];
301 case PCI0_BASE_ADDR1
:
302 if (BARAddrs
[1] != 0)
303 primary
.ctrlAddr
= BARAddrs
[1];
306 case PCI0_BASE_ADDR2
:
307 if (BARAddrs
[2] != 0)
308 secondary
.cmdAddr
= BARAddrs
[2];
311 case PCI0_BASE_ADDR3
:
312 if (BARAddrs
[3] != 0)
313 secondary
.ctrlAddr
= BARAddrs
[3];
316 case PCI0_BASE_ADDR4
:
317 if (BARAddrs
[4] != 0)
318 bmiAddr
= BARAddrs
[4];
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
));
331 IdeController::Channel::accessCommand(Addr offset
,
332 int size
, uint8_t *data
, bool read
)
334 const Addr SelectOffset
= 6;
335 const uint8_t SelectDevBit
= 0x10;
337 if (!read
&& offset
== SelectOffset
)
338 select(*data
& SelectDevBit
);
340 if (selected
== NULL
) {
341 assert(size
== sizeof(uint8_t));
344 selected
->readCommand(offset
, size
, data
);
346 selected
->writeCommand(offset
, size
, data
);
351 IdeController::Channel::accessControl(Addr offset
,
352 int size
, uint8_t *data
, bool read
)
354 if (selected
== NULL
) {
355 assert(size
== sizeof(uint8_t));
358 selected
->readControl(offset
, size
, data
);
360 selected
->writeControl(offset
, size
, data
);
365 IdeController::Channel::accessBMI(Addr offset
,
366 int size
, uint8_t *data
, bool read
)
368 assert(offset
+ size
<= sizeof(BMIRegs
));
370 memcpy(data
, (uint8_t *)&bmiRegs
+ offset
, size
);
375 if (size
!= sizeof(uint8_t))
376 panic("Invalid BMIC write size: %x\n", size
);
378 BMICommandReg oldVal
= bmiRegs
.command
;
379 BMICommandReg newVal
= *data
;
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
;
385 if (oldVal
.startStop
!= newVal
.startStop
) {
386 if (selected
== NULL
)
387 panic("DMA start for disk which does not exist\n");
389 if (oldVal
.startStop
) {
390 DPRINTF(IdeCtrl
, "Stopping DMA transfer\n");
391 bmiRegs
.status
.active
= 0;
393 selected
->abortDma();
395 DPRINTF(IdeCtrl
, "Starting DMA transfer\n");
396 bmiRegs
.status
.active
= 1;
398 selected
->startDma(letoh(bmiRegs
.bmidtp
));
402 bmiRegs
.command
= newVal
;
407 if (size
!= sizeof(uint8_t))
408 panic("Invalid BMIS write size: %x\n", size
);
410 BMIStatusReg oldVal
= bmiRegs
.status
;
411 BMIStatusReg newVal
= *data
;
413 // the BMIDEA bit is read only
414 newVal
.active
= oldVal
.active
;
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?
420 newVal
.intStatus
= oldVal
.intStatus
;
421 if (oldVal
.dmaError
&& newVal
.dmaError
)
424 newVal
.dmaError
= oldVal
.dmaError
;
426 bmiRegs
.status
= newVal
;
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);
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
);
444 IdeController::dispatchAccess(PacketPtr pkt
, bool read
)
447 if (pkt
->getSize() != 1 && pkt
->getSize() != 2 && pkt
->getSize() !=4)
448 panic("Bad IDE read size: %d\n", pkt
->getSize());
451 pkt
->makeAtomicResponse();
452 DPRINTF(IdeCtrl
, "io not enabled\n");
456 Addr addr
= pkt
->getAddr();
457 int size
= pkt
->getSize();
458 uint8_t *dataPtr
= pkt
->getPtr
<uint8_t>();
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.
466 primary
.accessCommand(addr
, size
, dataPtr
, read
);
467 } else if (addr
>= primary
.ctrlAddr
&&
468 addr
< (primary
.ctrlAddr
+ primary
.ctrlSize
)) {
469 addr
-= primary
.ctrlAddr
;
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
)
484 if (addr
< sizeof(Channel::BMIRegs
)) {
485 primary
.accessBMI(addr
, size
, dataPtr
, read
);
487 addr
-= sizeof(Channel::BMIRegs
);
488 secondary
.accessBMI(addr
, size
, dataPtr
, read
);
491 panic("IDE controller access to invalid address: %#x\n", addr
);
496 if (pkt
->getSize() == 1)
497 data
= pkt
->get
<uint8_t>();
498 else if (pkt
->getSize() == 2)
499 data
= pkt
->get
<uint16_t>();
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
);
506 pkt
->makeAtomicResponse();
510 IdeController::read(PacketPtr pkt
)
512 dispatchAccess(pkt
, true);
517 IdeController::write(PacketPtr pkt
)
519 dispatchAccess(pkt
, false);
524 IdeController::serialize(std::ostream
&os
)
526 // Serialize the PciDev base class
527 PciDev::serialize(os
);
529 // Serialize channels
530 primary
.serialize("primary", os
);
531 secondary
.serialize("secondary", os
);
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
);
541 // Serialize internal state
542 SERIALIZE_SCALAR(ioEnabled
);
543 SERIALIZE_SCALAR(bmEnabled
);
544 SERIALIZE_SCALAR(bmiAddr
);
545 SERIALIZE_SCALAR(bmiSize
);
549 IdeController::Channel::serialize(const std::string
&base
, std::ostream
&os
)
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
);
566 IdeController::unserialize(Checkpoint
*cp
, const std::string
§ion
)
568 // Unserialize the PciDev base class
569 PciDev::unserialize(cp
, section
);
571 // Unserialize channels
572 primary
.unserialize("primary", cp
, section
);
573 secondary
.unserialize("secondary", cp
, section
);
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
);
583 // Unserialize internal state
584 UNSERIALIZE_SCALAR(ioEnabled
);
585 UNSERIALIZE_SCALAR(bmEnabled
);
586 UNSERIALIZE_SCALAR(bmiAddr
);
587 UNSERIALIZE_SCALAR(bmiSize
);
591 IdeController::Channel::unserialize(const std::string
&base
, Checkpoint
*cp
,
592 const std::string
§ion
)
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
);
599 paramIn(cp
, section
, base
+".bmiRegs.command", command
);
600 bmiRegs
.command
= command
;
601 paramIn(cp
, section
, base
+ ".bmiRegs.reserved0", bmiRegs
.reserved0
);
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
);
612 IdeControllerParams::create()
614 return new IdeController(this);