diff options
Diffstat (limited to 'src/dev/arm/pl011.cc')
-rw-r--r-- | src/dev/arm/pl011.cc | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/src/dev/arm/pl011.cc b/src/dev/arm/pl011.cc new file mode 100644 index 000000000..2afc9977d --- /dev/null +++ b/src/dev/arm/pl011.cc @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2010 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Copyright (c) 2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Ali Saidi + */ + +#include "base/trace.hh" +#include "dev/arm/gic.hh" +#include "dev/arm/pl011.hh" +#include "dev/terminal.hh" +#include "mem/packet.hh" +#include "mem/packet_access.hh" +#include "sim/sim_exit.hh" + +Pl011::Pl011(const Params *p) + : Uart(p), control(0x300), fbrd(0), ibrd(0), lcrh(0), ifls(0x12), imsc(0), + rawInt(0), maskInt(0), intNum(p->int_num), gic(p->gic), + endOnEOT(p->end_on_eot), intDelay(p->int_delay), intEvent(this) +{ + pioSize = 0xfff; +} + +Tick +Pl011::read(PacketPtr pkt) +{ + assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); + + Addr daddr = pkt->getAddr() - pioAddr; + pkt->allocate(); + + DPRINTF(Uart, " read register %#x size=%d\n", daddr, pkt->getSize()); + + // use a temporary data since the uart registers are read/written with + // different size operations + // + uint32_t data = 0; + + switch(daddr) { + case UART_DR: + data = 0; + if (term->dataAvailable()) + data = term->in(); + break; + case UART_FR: + // For now we're infintely fast, so TX is never full, always empty, + // always clear to send + data = UART_FR_TXFE | UART_FR_CTS; + if (!term->dataAvailable()) + data |= UART_FR_RXFE; + DPRINTF(Uart, "Reading FR register as %#x rawInt=0x%x imsc=0x%x maskInt=0x%x\n", + data, rawInt, imsc, maskInt); + break; + case UART_CR: + data = control; + break; + case UART_IBRD: + data = ibrd; + break; + case UART_FBRD: + data = fbrd; + break; + case UART_LCRH: + data = lcrh; + break; + case UART_IFLS: + data = ifls; + break; + case UART_IMSC: + data = imsc; + break; + case UART_RIS: + data = rawInt; + DPRINTF(Uart, "Reading Raw Int status as 0x%x\n", rawInt); + break; + case UART_MIS: + DPRINTF(Uart, "Reading Masked Int status as 0x%x\n", rawInt); + data = maskInt; + break; + default: + if (daddr >= UART_PER_ID0 && daddr <= UART_CEL_ID3) { + // AMBA ID information + int byte; + byte = (daddr - UART_PER_ID0) << 1; + DPRINTF(AMBA, "--daddr=%#x shift=%d val=%#x\n", daddr, byte, + (ULL(0xb105f00d00341011) >> byte) & 0xFF); + data = (ULL(0xb105f00d00341011) >> byte) & 0xFF; + break; + } + panic("Tried to read PL011 at offset %#x that doesn't exist\n", daddr); + break; + } + + switch(pkt->getSize()) { + case 1: + pkt->set<uint8_t>(data); + break; + case 2: + pkt->set<uint16_t>(data); + break; + case 4: + pkt->set<uint32_t>(data); + break; + default: + panic("Uart read size too big?\n"); + break; + } + + + pkt->makeAtomicResponse(); + return pioDelay; +} + +Tick +Pl011::write(PacketPtr pkt) +{ + + assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); + + Addr daddr = pkt->getAddr() - pioAddr; + + DPRINTF(Uart, " write register %#x value %#x size=%d\n", daddr, + pkt->get<uint8_t>(), pkt->getSize()); + + // use a temporary data since the uart registers are read/written with + // different size operations + // + uint32_t data = 0; + + switch(pkt->getSize()) { + case 1: + data = pkt->get<uint8_t>(); + break; + case 2: + data = pkt->get<uint16_t>(); + break; + case 4: + data = pkt->get<uint32_t>(); + break; + default: + panic("Uart write size too big?\n"); + break; + } + + + switch (daddr) { + case UART_DR: + if ((data & 0xFF) == 0x04 && endOnEOT) + exitSimLoop("UART received EOT", 0); + + term->out(data & 0xFF); + + if (imsc.txim) { + DPRINTF(Uart, "TX int enabled, scheduling interruptt\n"); + rawInt.txim = 1; + if (!intEvent.scheduled()) + schedule(intEvent, curTick + intDelay); + } + + break; + case UART_CR: + control = data; + break; + case UART_IBRD: + ibrd = data; + break; + case UART_FBRD: + fbrd = data; + break; + case UART_LCRH: + lcrh = data; + break; + case UART_IFLS: + ifls = data; + break; + case UART_IMSC: + imsc = data; + + if (imsc.rimim || imsc.ctsmim || imsc.dcdmim || imsc.dsrmim + || imsc.feim || imsc.peim || imsc.beim || imsc.oeim || imsc.rsvd) + panic("Unknown interrupt enabled\n"); + + if (imsc.txim) { + DPRINTF(Uart, "Writing to IMSC: TX int enabled, scheduling interruptt\n"); + rawInt.txim = 1; + if (!intEvent.scheduled()) + schedule(intEvent, curTick + intDelay); + } + + break; + + case UART_ICR: + DPRINTF(Uart, "Clearing interrupts 0x%x\n", data); + rawInt = rawInt & ~data; + maskInt = rawInt & imsc; + + DPRINTF(Uart, " -- Masked interrupts 0x%x\n", maskInt); + + if (!maskInt) + gic->clearInt(intNum); + + break; + default: + panic("Tried to write PL011 at offset %#x that doesn't exist\n", daddr); + break; + } + pkt->makeAtomicResponse(); + return pioDelay; +} + +void +Pl011::dataAvailable() +{ + /*@todo ignore the fifo, just say we have data now + * We might want to fix this, or we might not care */ + rawInt.rxim = 1; + rawInt.rtim = 1; + + DPRINTF(Uart, "Data available, scheduling interrupt\n"); + + if (!intEvent.scheduled()) + schedule(intEvent, curTick + intDelay); +} + +void +Pl011::generateInterrupt() +{ + DPRINTF(Uart, "Generate Interrupt: imsc=0x%x rawInt=0x%x maskInt=0x%x\n", + imsc, rawInt, maskInt); + maskInt = imsc & rawInt; + + if (maskInt.rxim || maskInt.rtim || maskInt.txim) { + gic->sendInt(intNum); + DPRINTF(Uart, " -- Generated\n"); + } + +} + + + +void +Pl011::serialize(std::ostream &os) +{ + panic("Need to implement serialization\n"); +} + +void +Pl011::unserialize(Checkpoint *cp, const std::string §ion) +{ + panic("Need to implement serialization\n"); +} + +Pl011 * +Pl011Params::create() +{ + return new Pl011(this); +} |