2 * Copyright (c) 2013 ARM Limited
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
14 * Copyright (c) 2004-2005 The Regents of The University of Michigan
15 * All rights reserved.
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions are
19 * met: redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer;
21 * redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution;
24 * neither the name of the copyright holders nor the names of its
25 * contributors may be used to endorse or promote products derived from
26 * this software without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 * Authors: Andrew Schultz
45 #include "dev/storage/ide_ctrl.hh"
49 #include "cpu/intr_control.hh"
50 #include "debug/IdeCtrl.hh"
51 #include "dev/storage/ide_disk.hh"
52 #include "mem/packet.hh"
53 #include "mem/packet_access.hh"
54 #include "params/IdeController.hh"
55 #include "sim/byteswap.hh"
57 // clang complains about std::set being overloaded with Packet::set if
58 // we open up the entire namespace std
61 // Bus master IDE registers
68 // PCI config space registers
71 SecondaryTiming
= 0x42,
78 static const uint16_t timeRegWithDecodeEn
= 0x8000;
80 IdeController::Channel::Channel(
81 string newName
, Addr _cmdSize
, Addr _ctrlSize
) :
83 cmdAddr(0), cmdSize(_cmdSize
), ctrlAddr(0), ctrlSize(_ctrlSize
),
84 master(NULL
), slave(NULL
), selected(NULL
)
87 bmiRegs
.status
.dmaCap0
= 1;
88 bmiRegs
.status
.dmaCap1
= 1;
91 IdeController::Channel::~Channel()
95 IdeController::IdeController(Params
*p
)
96 : PciDevice(p
), primary(name() + ".primary", BARSize
[0], BARSize
[1]),
97 secondary(name() + ".secondary", BARSize
[2], BARSize
[3]),
98 bmiAddr(0), bmiSize(BARSize
[4]),
99 primaryTiming(htole(timeRegWithDecodeEn
)),
100 secondaryTiming(htole(timeRegWithDecodeEn
)),
101 deviceTiming(0), udmaControl(0), udmaTiming(0), ideConfig(0),
102 ioEnabled(false), bmEnabled(false),
103 ioShift(p
->io_shift
), ctrlOffset(p
->ctrl_offset
)
106 // Assign the disks to channels
107 for (int i
= 0; i
< params()->disks
.size(); i
++) {
108 if (!params()->disks
[i
])
112 primary
.master
= params()->disks
[0];
115 primary
.slave
= params()->disks
[1];
118 secondary
.master
= params()->disks
[2];
121 secondary
.slave
= params()->disks
[3];
124 panic("IDE controllers support a maximum "
125 "of 4 devices attached!\n");
127 params()->disks
[i
]->setController(this);
130 primary
.select(false);
131 secondary
.select(false);
133 if ((BARAddrs
[0] & ~BAR_IO_MASK
) && (!legacyIO
[0] || ioShift
)) {
134 primary
.cmdAddr
= BARAddrs
[0]; primary
.cmdSize
= BARSize
[0];
135 primary
.ctrlAddr
= BARAddrs
[1]; primary
.ctrlSize
= BARSize
[1];
137 if ((BARAddrs
[2] & ~BAR_IO_MASK
) && (!legacyIO
[2] || ioShift
)) {
138 secondary
.cmdAddr
= BARAddrs
[2]; secondary
.cmdSize
= BARSize
[2];
139 secondary
.ctrlAddr
= BARAddrs
[3]; secondary
.ctrlSize
= BARSize
[3];
142 ioEnabled
= (config
.command
& htole(PCI_CMD_IOSE
));
143 bmEnabled
= (config
.command
& htole(PCI_CMD_BME
));
147 IdeController::isDiskSelected(IdeDisk
*diskPtr
)
149 return (primary
.selected
== diskPtr
|| secondary
.selected
== diskPtr
);
153 IdeController::intrPost()
155 primary
.bmiRegs
.status
.intStatus
= 1;
156 PciDevice::intrPost();
160 IdeController::setDmaComplete(IdeDisk
*disk
)
163 if (disk
== primary
.master
|| disk
== primary
.slave
) {
165 } else if (disk
== secondary
.master
|| disk
== secondary
.slave
) {
166 channel
= &secondary
;
168 panic("Unable to find disk based on pointer %#x\n", disk
);
171 channel
->bmiRegs
.command
.startStop
= 0;
172 channel
->bmiRegs
.status
.active
= 0;
173 channel
->bmiRegs
.status
.intStatus
= 1;
177 IdeController::readConfig(PacketPtr pkt
)
179 int offset
= pkt
->getAddr() & PCI_CONFIG_SIZE
;
180 if (offset
< PCI_DEVICE_SPECIFIC
) {
181 return PciDevice::readConfig(pkt
);
184 switch (pkt
->getSize()) {
185 case sizeof(uint8_t):
188 pkt
->setLE
<uint8_t>(deviceTiming
);
191 pkt
->setLE
<uint8_t>(udmaControl
);
193 case PrimaryTiming
+ 1:
194 pkt
->setLE
<uint8_t>(bits(htole(primaryTiming
), 15, 8));
196 case SecondaryTiming
+ 1:
197 pkt
->setLE
<uint8_t>(bits(htole(secondaryTiming
), 15, 8));
200 pkt
->setLE
<uint8_t>(bits(htole(ideConfig
), 7, 0));
203 pkt
->setLE
<uint8_t>(bits(htole(ideConfig
), 15, 8));
206 panic("Invalid PCI configuration read for size 1 at offset: %#x!\n",
209 DPRINTF(IdeCtrl
, "PCI read offset: %#x size: 1 data: %#x\n", offset
,
210 (uint32_t)pkt
->getLE
<uint8_t>());
212 case sizeof(uint16_t):
215 pkt
->setLE
<uint16_t>(udmaControl
);
218 pkt
->setLE
<uint16_t>(primaryTiming
);
220 case SecondaryTiming
:
221 pkt
->setLE
<uint16_t>(secondaryTiming
);
224 pkt
->setLE
<uint16_t>(udmaTiming
);
227 pkt
->setLE
<uint16_t>(ideConfig
);
230 panic("Invalid PCI configuration read for size 2 offset: %#x!\n",
233 DPRINTF(IdeCtrl
, "PCI read offset: %#x size: 2 data: %#x\n", offset
,
234 (uint32_t)pkt
->getLE
<uint16_t>());
236 case sizeof(uint32_t):
239 pkt
->setLE
<uint32_t>(primaryTiming
);
242 pkt
->setLE
<uint32_t>(ideConfig
);
245 panic("No 32bit reads implemented for this device.");
247 DPRINTF(IdeCtrl
, "PCI read offset: %#x size: 4 data: %#x\n", offset
,
248 (uint32_t)pkt
->getLE
<uint32_t>());
251 panic("invalid access size(?) for PCI configspace!\n");
253 pkt
->makeAtomicResponse();
259 IdeController::writeConfig(PacketPtr pkt
)
261 int offset
= pkt
->getAddr() & PCI_CONFIG_SIZE
;
262 if (offset
< PCI_DEVICE_SPECIFIC
) {
263 PciDevice::writeConfig(pkt
);
265 switch (pkt
->getSize()) {
266 case sizeof(uint8_t):
269 deviceTiming
= pkt
->getLE
<uint8_t>();
272 udmaControl
= pkt
->getLE
<uint8_t>();
275 replaceBits(ideConfig
, 7, 0, pkt
->getLE
<uint8_t>());
278 replaceBits(ideConfig
, 15, 8, pkt
->getLE
<uint8_t>());
281 panic("Invalid PCI configuration write "
282 "for size 1 offset: %#x!\n", offset
);
284 DPRINTF(IdeCtrl
, "PCI write offset: %#x size: 1 data: %#x\n",
285 offset
, (uint32_t)pkt
->getLE
<uint8_t>());
287 case sizeof(uint16_t):
290 udmaControl
= pkt
->getLE
<uint16_t>();
293 primaryTiming
= pkt
->getLE
<uint16_t>();
295 case SecondaryTiming
:
296 secondaryTiming
= pkt
->getLE
<uint16_t>();
299 udmaTiming
= pkt
->getLE
<uint16_t>();
302 ideConfig
= pkt
->getLE
<uint16_t>();
305 panic("Invalid PCI configuration write "
306 "for size 2 offset: %#x!\n",
309 DPRINTF(IdeCtrl
, "PCI write offset: %#x size: 2 data: %#x\n",
310 offset
, (uint32_t)pkt
->getLE
<uint16_t>());
312 case sizeof(uint32_t):
315 primaryTiming
= pkt
->getLE
<uint32_t>();
318 ideConfig
= pkt
->getLE
<uint32_t>();
321 panic("Write of unimplemented PCI config. register: %x\n", offset
);
325 panic("invalid access size(?) for PCI configspace!\n");
327 pkt
->makeAtomicResponse();
330 /* Trap command register writes and enable IO/BM as appropriate as well as
333 case PCI0_BASE_ADDR0
:
334 if (BARAddrs
[0] != 0)
335 primary
.cmdAddr
= BARAddrs
[0];
338 case PCI0_BASE_ADDR1
:
339 if (BARAddrs
[1] != 0)
340 primary
.ctrlAddr
= BARAddrs
[1];
343 case PCI0_BASE_ADDR2
:
344 if (BARAddrs
[2] != 0)
345 secondary
.cmdAddr
= BARAddrs
[2];
348 case PCI0_BASE_ADDR3
:
349 if (BARAddrs
[3] != 0)
350 secondary
.ctrlAddr
= BARAddrs
[3];
353 case PCI0_BASE_ADDR4
:
354 if (BARAddrs
[4] != 0)
355 bmiAddr
= BARAddrs
[4];
359 DPRINTF(IdeCtrl
, "Writing to PCI Command val: %#x\n", config
.command
);
360 ioEnabled
= (config
.command
& htole(PCI_CMD_IOSE
));
361 bmEnabled
= (config
.command
& htole(PCI_CMD_BME
));
368 IdeController::Channel::accessCommand(Addr offset
,
369 int size
, uint8_t *data
, bool read
)
371 const Addr SelectOffset
= 6;
372 const uint8_t SelectDevBit
= 0x10;
374 if (!read
&& offset
== SelectOffset
)
375 select(*data
& SelectDevBit
);
377 if (selected
== NULL
) {
378 assert(size
== sizeof(uint8_t));
381 selected
->readCommand(offset
, size
, data
);
383 selected
->writeCommand(offset
, size
, data
);
388 IdeController::Channel::accessControl(Addr offset
,
389 int size
, uint8_t *data
, bool read
)
391 if (selected
== NULL
) {
392 assert(size
== sizeof(uint8_t));
395 selected
->readControl(offset
, size
, data
);
397 selected
->writeControl(offset
, size
, data
);
402 IdeController::Channel::accessBMI(Addr offset
,
403 int size
, uint8_t *data
, bool read
)
405 assert(offset
+ size
<= sizeof(BMIRegs
));
407 memcpy(data
, (uint8_t *)&bmiRegs
+ offset
, size
);
412 if (size
!= sizeof(uint8_t))
413 panic("Invalid BMIC write size: %x\n", size
);
415 BMICommandReg oldVal
= bmiRegs
.command
;
416 BMICommandReg newVal
= *data
;
418 // if a DMA transfer is in progress, R/W control cannot change
419 if (oldVal
.startStop
&& oldVal
.rw
!= newVal
.rw
)
420 oldVal
.rw
= newVal
.rw
;
422 if (oldVal
.startStop
!= newVal
.startStop
) {
423 if (selected
== NULL
)
424 panic("DMA start for disk which does not exist\n");
426 if (oldVal
.startStop
) {
427 DPRINTF(IdeCtrl
, "Stopping DMA transfer\n");
428 bmiRegs
.status
.active
= 0;
430 selected
->abortDma();
432 DPRINTF(IdeCtrl
, "Starting DMA transfer\n");
433 bmiRegs
.status
.active
= 1;
435 selected
->startDma(letoh(bmiRegs
.bmidtp
));
439 bmiRegs
.command
= newVal
;
444 if (size
!= sizeof(uint8_t))
445 panic("Invalid BMIS write size: %x\n", size
);
447 BMIStatusReg oldVal
= bmiRegs
.status
;
448 BMIStatusReg newVal
= *data
;
450 // the BMIDEA bit is read only
451 newVal
.active
= oldVal
.active
;
453 // to reset (set 0) IDEINTS and IDEDMAE, write 1 to each
454 if ((oldVal
.intStatus
== 1) && (newVal
.intStatus
== 1)) {
455 newVal
.intStatus
= 0; // clear the interrupt?
457 // Assigning two bitunion fields to each other does not
458 // work as intended, so we need to use this temporary variable
459 // to get around the bug.
460 uint8_t tmp
= oldVal
.intStatus
;
461 newVal
.intStatus
= tmp
;
463 if ((oldVal
.dmaError
== 1) && (newVal
.dmaError
== 1)) {
466 uint8_t tmp
= oldVal
.dmaError
;
467 newVal
.dmaError
= tmp
;
470 bmiRegs
.status
= newVal
;
473 case BMIDescTablePtr
:
474 if (size
!= sizeof(uint32_t))
475 panic("Invalid BMIDTP write size: %x\n", size
);
476 bmiRegs
.bmidtp
= htole(*(uint32_t *)data
& ~0x3);
479 if (size
!= sizeof(uint8_t) && size
!= sizeof(uint16_t) &&
480 size
!= sizeof(uint32_t))
481 panic("IDE controller write of invalid write size: %x\n", size
);
482 memcpy((uint8_t *)&bmiRegs
+ offset
, data
, size
);
488 IdeController::dispatchAccess(PacketPtr pkt
, bool read
)
490 if (pkt
->getSize() != 1 && pkt
->getSize() != 2 && pkt
->getSize() !=4)
491 panic("Bad IDE read size: %d\n", pkt
->getSize());
494 pkt
->makeAtomicResponse();
495 DPRINTF(IdeCtrl
, "io not enabled\n");
499 Addr addr
= pkt
->getAddr();
500 int size
= pkt
->getSize();
501 uint8_t *dataPtr
= pkt
->getPtr
<uint8_t>();
503 if (addr
>= primary
.cmdAddr
&&
504 addr
< (primary
.cmdAddr
+ primary
.cmdSize
)) {
505 addr
-= primary
.cmdAddr
;
506 // linux may have shifted the address by ioShift,
507 // here we shift it back, similarly for ctrlOffset.
509 primary
.accessCommand(addr
, size
, dataPtr
, read
);
510 } else if (addr
>= primary
.ctrlAddr
&&
511 addr
< (primary
.ctrlAddr
+ primary
.ctrlSize
)) {
512 addr
-= primary
.ctrlAddr
;
514 primary
.accessControl(addr
, size
, dataPtr
, read
);
515 } else if (addr
>= secondary
.cmdAddr
&&
516 addr
< (secondary
.cmdAddr
+ secondary
.cmdSize
)) {
517 addr
-= secondary
.cmdAddr
;
518 secondary
.accessCommand(addr
, size
, dataPtr
, read
);
519 } else if (addr
>= secondary
.ctrlAddr
&&
520 addr
< (secondary
.ctrlAddr
+ secondary
.ctrlSize
)) {
521 addr
-= secondary
.ctrlAddr
;
522 secondary
.accessControl(addr
, size
, dataPtr
, read
);
523 } else if (addr
>= bmiAddr
&& addr
< (bmiAddr
+ bmiSize
)) {
524 if (!read
&& !bmEnabled
)
527 if (addr
< sizeof(Channel::BMIRegs
)) {
528 primary
.accessBMI(addr
, size
, dataPtr
, read
);
530 addr
-= sizeof(Channel::BMIRegs
);
531 secondary
.accessBMI(addr
, size
, dataPtr
, read
);
534 panic("IDE controller access to invalid address: %#x\n", addr
);
539 if (pkt
->getSize() == 1)
540 data
= pkt
->getLE
<uint8_t>();
541 else if (pkt
->getSize() == 2)
542 data
= pkt
->getLE
<uint16_t>();
544 data
= pkt
->getLE
<uint32_t>();
545 DPRINTF(IdeCtrl
, "%s from offset: %#x size: %#x data: %#x\n",
546 read
? "Read" : "Write", pkt
->getAddr(), pkt
->getSize(), data
);
549 pkt
->makeAtomicResponse();
553 IdeController::read(PacketPtr pkt
)
555 dispatchAccess(pkt
, true);
560 IdeController::write(PacketPtr pkt
)
562 dispatchAccess(pkt
, false);
567 IdeController::serialize(CheckpointOut
&cp
) const
569 // Serialize the PciDevice base class
570 PciDevice::serialize(cp
);
572 // Serialize channels
573 primary
.serialize("primary", cp
);
574 secondary
.serialize("secondary", cp
);
576 // Serialize config registers
577 SERIALIZE_SCALAR(primaryTiming
);
578 SERIALIZE_SCALAR(secondaryTiming
);
579 SERIALIZE_SCALAR(deviceTiming
);
580 SERIALIZE_SCALAR(udmaControl
);
581 SERIALIZE_SCALAR(udmaTiming
);
582 SERIALIZE_SCALAR(ideConfig
);
584 // Serialize internal state
585 SERIALIZE_SCALAR(ioEnabled
);
586 SERIALIZE_SCALAR(bmEnabled
);
587 SERIALIZE_SCALAR(bmiAddr
);
588 SERIALIZE_SCALAR(bmiSize
);
592 IdeController::Channel::serialize(const std::string
&base
,
593 CheckpointOut
&cp
) const
595 paramOut(cp
, base
+ ".cmdAddr", cmdAddr
);
596 paramOut(cp
, base
+ ".cmdSize", cmdSize
);
597 paramOut(cp
, base
+ ".ctrlAddr", ctrlAddr
);
598 paramOut(cp
, base
+ ".ctrlSize", ctrlSize
);
599 uint8_t command
= bmiRegs
.command
;
600 paramOut(cp
, base
+ ".bmiRegs.command", command
);
601 paramOut(cp
, base
+ ".bmiRegs.reserved0", bmiRegs
.reserved0
);
602 uint8_t status
= bmiRegs
.status
;
603 paramOut(cp
, base
+ ".bmiRegs.status", status
);
604 paramOut(cp
, base
+ ".bmiRegs.reserved1", bmiRegs
.reserved1
);
605 paramOut(cp
, base
+ ".bmiRegs.bmidtp", bmiRegs
.bmidtp
);
606 paramOut(cp
, base
+ ".selectBit", selectBit
);
610 IdeController::unserialize(CheckpointIn
&cp
)
612 // Unserialize the PciDevice base class
613 PciDevice::unserialize(cp
);
615 // Unserialize channels
616 primary
.unserialize("primary", cp
);
617 secondary
.unserialize("secondary", cp
);
619 // Unserialize config registers
620 UNSERIALIZE_SCALAR(primaryTiming
);
621 UNSERIALIZE_SCALAR(secondaryTiming
);
622 UNSERIALIZE_SCALAR(deviceTiming
);
623 UNSERIALIZE_SCALAR(udmaControl
);
624 UNSERIALIZE_SCALAR(udmaTiming
);
625 UNSERIALIZE_SCALAR(ideConfig
);
627 // Unserialize internal state
628 UNSERIALIZE_SCALAR(ioEnabled
);
629 UNSERIALIZE_SCALAR(bmEnabled
);
630 UNSERIALIZE_SCALAR(bmiAddr
);
631 UNSERIALIZE_SCALAR(bmiSize
);
635 IdeController::Channel::unserialize(const std::string
&base
, CheckpointIn
&cp
)
637 paramIn(cp
, base
+ ".cmdAddr", cmdAddr
);
638 paramIn(cp
, base
+ ".cmdSize", cmdSize
);
639 paramIn(cp
, base
+ ".ctrlAddr", ctrlAddr
);
640 paramIn(cp
, base
+ ".ctrlSize", ctrlSize
);
642 paramIn(cp
, base
+".bmiRegs.command", command
);
643 bmiRegs
.command
= command
;
644 paramIn(cp
, base
+ ".bmiRegs.reserved0", bmiRegs
.reserved0
);
646 paramIn(cp
, base
+ ".bmiRegs.status", status
);
647 bmiRegs
.status
= status
;
648 paramIn(cp
, base
+ ".bmiRegs.reserved1", bmiRegs
.reserved1
);
649 paramIn(cp
, base
+ ".bmiRegs.bmidtp", bmiRegs
.bmidtp
);
650 paramIn(cp
, base
+ ".selectBit", selectBit
);
655 IdeControllerParams::create()
657 return new IdeController(this);