text: /*
   * Copyright (c) 2004-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
   *          Andrew Schultz
   */
  
  /** @file
   * Tsunami PChip (pci)
   */
  #include "dev/alpha/tsunami_pchip.hh"
  
  #include <deque>
  #include <string>
  #include <vector>
  
  #include "base/trace.hh"
  #include "debug/Tsunami.hh"
  #include "dev/alpha/tsunami.hh"
  #include "dev/alpha/tsunami_cchip.hh"
  #include "dev/alpha/tsunamireg.h"
  #include "dev/pci/device.hh"
  #include "mem/packet.hh"
  #include "mem/packet_access.hh"
  #include "sim/system.hh"
  
  using namespace std;
  
  TsunamiPChip::TsunamiPChip(const Params *p)
      : GenericPciHost(p),
        pioRange(RangeSize(p->pio_addr, 0x1000)),
        pioDelay(p->pio_latency)
  {
      for (int i = 0; i < 4; i++) {
          wsba[i] = 0;
          wsm[i] = 0;
          tba[i] = 0;
      }
  
      // initialize pchip control register
      pctl = (ULL(0x1) << 20) | (ULL(0x1) << 32) | (ULL(0x2) << 36);
  
      //Set back pointer in tsunami
      p->tsunami->pchip = this;
  }
  
  Tick
  TsunamiPChip::read(PacketPtr pkt)
  {
      // We only need to handle our own configuration registers, pass
      // unknown addresses to the generic code.
      if (!pioRange.contains(pkt->getAddr()))
          return GenericPciHost::read(pkt);
  
      Addr daddr = (pkt->getAddr() - pioRange.start()) >> 6;;
      assert(pkt->getSize() == sizeof(uint64_t));
  
  
      DPRINTF(Tsunami, "read  va=%#x size=%d\n", pkt->getAddr(), pkt->getSize());
  
      switch(daddr) {
        case TSDEV_PC_WSBA0:
              pkt->setLE(wsba[0]);
              break;
        case TSDEV_PC_WSBA1:
              pkt->setLE(wsba[1]);
              break;
        case TSDEV_PC_WSBA2:
              pkt->setLE(wsba[2]);
              break;
        case TSDEV_PC_WSBA3:
              pkt->setLE(wsba[3]);
              break;
        case TSDEV_PC_WSM0:
              pkt->setLE(wsm[0]);
              break;
        case TSDEV_PC_WSM1:
              pkt->setLE(wsm[1]);
              break;
        case TSDEV_PC_WSM2:
              pkt->setLE(wsm[2]);
              break;
        case TSDEV_PC_WSM3:
              pkt->setLE(wsm[3]);
              break;
        case TSDEV_PC_TBA0:
              pkt->setLE(tba[0]);
              break;
        case TSDEV_PC_TBA1:
              pkt->setLE(tba[1]);
              break;
        case TSDEV_PC_TBA2:
              pkt->setLE(tba[2]);
              break;
        case TSDEV_PC_TBA3:
              pkt->setLE(tba[3]);
              break;
        case TSDEV_PC_PCTL:
              pkt->setLE(pctl);
              break;
        case TSDEV_PC_PLAT:
              panic("PC_PLAT not implemented\n");
        case TSDEV_PC_RES:
              panic("PC_RES not implemented\n");
        case TSDEV_PC_PERROR:
              pkt->setLE((uint64_t)0x00);
              break;
        case TSDEV_PC_PERRMASK:
              pkt->setLE((uint64_t)0x00);
              break;
        case TSDEV_PC_PERRSET:
              panic("PC_PERRSET not implemented\n");
        case TSDEV_PC_TLBIV:
              panic("PC_TLBIV not implemented\n");
        case TSDEV_PC_TLBIA:
              pkt->setLE((uint64_t)0x00); // shouldn't be readable, but linux
              break;
        case TSDEV_PC_PMONCTL:
              panic("PC_PMONCTL not implemented\n");
        case TSDEV_PC_PMONCNT:
              panic("PC_PMONCTN not implemented\n");
        default:
            panic("Default in PChip Read reached reading 0x%x\n", daddr);
      }
  
      pkt->makeAtomicResponse();
      return pioDelay;
  
  }
  
  Tick
  TsunamiPChip::write(PacketPtr pkt)
  {
      // We only need to handle our own configuration registers, pass
      // unknown addresses to the generic code.
      if (!pioRange.contains(pkt->getAddr()))
          return GenericPciHost::write(pkt);
  
      Addr daddr = (pkt->getAddr() - pioRange.start()) >> 6;
  
      assert(pkt->getSize() == sizeof(uint64_t));
  
      DPRINTF(Tsunami, "write - va=%#x size=%d \n", pkt->getAddr(), pkt->getSize());
  
      switch(daddr) {
          case TSDEV_PC_WSBA0:
                wsba[0] = pkt->getLE<uint64_t>();
                break;
          case TSDEV_PC_WSBA1:
                wsba[1] = pkt->getLE<uint64_t>();
                break;
          case TSDEV_PC_WSBA2:
                wsba[2] = pkt->getLE<uint64_t>();
                break;
          case TSDEV_PC_WSBA3:
                wsba[3] = pkt->getLE<uint64_t>();
                break;
          case TSDEV_PC_WSM0:
                wsm[0] = pkt->getLE<uint64_t>();
                break;
          case TSDEV_PC_WSM1:
                wsm[1] = pkt->getLE<uint64_t>();
                break;
          case TSDEV_PC_WSM2:
                wsm[2] = pkt->getLE<uint64_t>();
                break;
          case TSDEV_PC_WSM3:
                wsm[3] = pkt->getLE<uint64_t>();
                break;
          case TSDEV_PC_TBA0:
                tba[0] = pkt->getLE<uint64_t>();
                break;
          case TSDEV_PC_TBA1:
                tba[1] = pkt->getLE<uint64_t>();
                break;
          case TSDEV_PC_TBA2:
                tba[2] = pkt->getLE<uint64_t>();
                break;
          case TSDEV_PC_TBA3:
                tba[3] = pkt->getLE<uint64_t>();
                break;
          case TSDEV_PC_PCTL:
                pctl = pkt->getLE<uint64_t>();
                break;
          case TSDEV_PC_PLAT:
                panic("PC_PLAT not implemented\n");
          case TSDEV_PC_RES:
                panic("PC_RES not implemented\n");
          case TSDEV_PC_PERROR:
                break;
          case TSDEV_PC_PERRMASK:
                panic("PC_PERRMASK not implemented\n");
          case TSDEV_PC_PERRSET:
                panic("PC_PERRSET not implemented\n");
          case TSDEV_PC_TLBIV:
                panic("PC_TLBIV not implemented\n");
          case TSDEV_PC_TLBIA:
                break; // value ignored, supposted to invalidate SG TLB
          case TSDEV_PC_PMONCTL:
                panic("PC_PMONCTL not implemented\n");
          case TSDEV_PC_PMONCNT:
                panic("PC_PMONCTN not implemented\n");
          default:
              panic("Default in PChip write reached reading 0x%x\n", daddr);
  
      } // uint64_t
  
      pkt->makeAtomicResponse();
      return pioDelay;
  }
  
  
  AddrRangeList
  TsunamiPChip::getAddrRanges() const
  {
      return AddrRangeList({
              RangeSize(confBase, confSize),
              pioRange
          });
  }
  
  
  #define DMA_ADDR_MASK ULL(0x3ffffffff)
  
  Addr
  TsunamiPChip::dmaAddr(const PciBusAddr &dev, Addr busAddr) const
  {
      // compare the address to the window base registers
      uint64_t tbaMask = 0;
      uint64_t baMask = 0;
  
      uint64_t windowMask = 0;
      uint64_t windowBase = 0;
  
      uint64_t pteEntry = 0;
  
      Addr pteAddr;
      Addr dmaAddr;
  
      for (int i = 0; i < 4; i++) {
  
          windowBase = wsba[i];
          windowMask = ~wsm[i] & (ULL(0xfff) << 20);
  
          if ((busAddr & windowMask) == (windowBase & windowMask)) {
  
              if (wsba[i] & 0x1) {   // see if enabled
                  if (wsba[i] & 0x2) { // see if SG bit is set
                      /** @todo
                          This currently is faked by just doing a direct
                          read from memory, however, to be realistic, this
                          needs to actually do a bus transaction.  The process
                          is explained in the tsunami documentation on page
                          10-12 and basically munges the address to look up a
                          PTE from a table in memory and then uses that mapping
                          to create an address for the SG page
                      */
  
                      tbaMask = ~(((wsm[i] & (ULL(0xfff) << 20)) >> 10) |
                                  ULL(0x3ff));
                      baMask = (wsm[i] & (ULL(0xfff) << 20)) | (ULL(0x7f) << 13);
                      pteAddr = (tba[i] & tbaMask) | ((busAddr & baMask) >> 10);
  
                      sys->physProxy.readBlob(pteAddr, &pteEntry,
                                              sizeof(uint64_t));
  
                      dmaAddr = ((pteEntry & ~ULL(0x1)) << 12) |
                                (busAddr & ULL(0x1fff));
  
                  } else {
                      baMask = (wsm[i] & (ULL(0xfff) << 20)) | ULL(0xfffff);
                      tbaMask = ~baMask;
                      dmaAddr = (tba[i] & tbaMask) | (busAddr & baMask);
                  }
  
                  return (dmaAddr & DMA_ADDR_MASK);
              }
          }
      }
  
      // if no match was found, then return the original address
      return busAddr;
  }
  
  void
  TsunamiPChip::serialize(CheckpointOut &cp) const
  {
      SERIALIZE_SCALAR(pctl);
      SERIALIZE_ARRAY(wsba, 4);
      SERIALIZE_ARRAY(wsm, 4);
      SERIALIZE_ARRAY(tba, 4);
  }
  
  void
  TsunamiPChip::unserialize(CheckpointIn &cp)
  {
      UNSERIALIZE_SCALAR(pctl);
      UNSERIALIZE_ARRAY(wsba, 4);
      UNSERIALIZE_ARRAY(wsm, 4);
      UNSERIALIZE_ARRAY(tba, 4);
  }
  
  
  TsunamiPChip *
  TsunamiPChipParams::create()
  {
      return new TsunamiPChip(this);
  }
  