arm: Add support for programmable oscillators
authorAndreas Sandberg <Andreas.Sandberg@ARM.com>
Fri, 7 Aug 2015 08:59:25 +0000 (09:59 +0100)
committerAndreas Sandberg <Andreas.Sandberg@ARM.com>
Fri, 7 Aug 2015 08:59:25 +0000 (09:59 +0100)
Add support for oscillators that can be programmed using the RealView
/ Versatile Express configuration interface. These oscillators are
typically used for things like the pixel clock in the display
controller.

The default configurations support the oscillators from a Versatile
Express motherboard (V2M-P1) with a CoreTile Express A15x2.

src/dev/arm/RealView.py
src/dev/arm/rv_ctrl.cc
src/dev/arm/rv_ctrl.hh

index 5365ac9de8067dde76276682a03d795ecb01433f..a9b9dede21139fff910ae59209248eadccd4e988 100644 (file)
@@ -42,6 +42,8 @@
 
 from m5.params import *
 from m5.proxy import *
+from ClockDomain import ClockDomain
+from VoltageDomain import VoltageDomain
 from Device import BasicPioDevice, PioDevice, IsaFake, BadAddr, DmaDevice
 from Pci import PciConfigAll
 from Ethernet import NSGigE, IGbE_igb, IGbE_e1000
@@ -89,6 +91,49 @@ class RealViewCtrl(BasicPioDevice):
     proc_id1 = Param.UInt32(0x0C000222, "Processor ID, SYS_PROCID1")
     idreg = Param.UInt32(0x00000000, "ID Register, SYS_ID")
 
+class RealViewOsc(ClockDomain):
+    type = 'RealViewOsc'
+    cxx_header = "dev/arm/rv_ctrl.hh"
+
+    parent = Param.RealViewCtrl(Parent.any, "RealView controller")
+
+    # TODO: We currently don't have the notion of a clock source,
+    # which means we have to associate oscillators with a voltage
+    # source.
+    voltage_domain = Param.VoltageDomain(Parent.voltage_domain,
+                                         "Voltage domain")
+
+    # See ARM DUI 0447J (ARM Motherboard Express uATX -- V2M-P1) and
+    # the individual core/logic tile reference manuals for details
+    # about the site/position/dcc/device allocation.
+    site = Param.UInt8("Board Site")
+    position = Param.UInt8("Position in device stack")
+    dcc = Param.UInt8("Daughterboard Configuration Controller")
+    device = Param.UInt8("Device ID")
+
+    freq = Param.Clock("Default frequency")
+
+class VExpressCoreTileCtrl(RealViewCtrl):
+    class MotherBoardOsc(RealViewOsc):
+        site, position, dcc = (0, 0, 0)
+
+    class CoreTileOsc(RealViewOsc):
+        site, position, dcc = (1, 0, 0)
+
+    # See ARM DUI 0447J (ARM Motherboard Express uATX -- V2M-P1)
+    osc_mcc = MotherBoardOsc(device=0, freq="50MHz")
+    osc_clcd = MotherBoardOsc(device=1, freq="23.75MHz")
+    osc_peripheral = MotherBoardOsc(device=2, freq="24MHz")
+    osc_system_bus = MotherBoardOsc(device=4, freq="24MHz")
+
+    # See Table 2.8 in ARM DUI 0604E (CoreTile Express A15x2 TRM).
+    osc_cpu = CoreTileOsc(device=0, freq="60MHz")
+    osc_hsbm = CoreTileOsc(device=4, freq="40MHz")
+    osc_pxl = CoreTileOsc(device=5, freq="23.75MHz")
+    osc_smb = CoreTileOsc(device=6, freq="50MHz")
+    osc_sys = CoreTileOsc(device=7, freq="60MHz")
+    osc_ddr = CoreTileOsc(device=8, freq="40MHz")
+
 class VGic(PioDevice):
     type = 'VGic'
     cxx_header = "dev/arm/vgic.hh"
@@ -227,7 +272,7 @@ class RealView(Platform):
 # Chapter 4: Programmer's Reference
 class RealViewPBX(RealView):
     uart = Pl011(pio_addr=0x10009000, int_num=44)
-    realview_io = RealViewCtrl(pio_addr=0x10000000)
+    realview_io = VExpressCoreTileCtrl(pio_addr=0x10000000)
     gic = Pl390()
     timer0 = Sp804(int_num0=36, int_num1=36, pio_addr=0x10011000)
     timer1 = Sp804(int_num0=37, int_num1=37, pio_addr=0x10012000)
@@ -354,7 +399,7 @@ class RealViewPBX(RealView):
 # Chapter 4: Programmer's Reference
 class RealViewEB(RealView):
     uart = Pl011(pio_addr=0x10009000, int_num=44)
-    realview_io = RealViewCtrl(pio_addr=0x10000000, idreg=0x01400500)
+    realview_io = VExpressCoreTileCtrl(pio_addr=0x10000000, idreg=0x01400500)
     gic = Pl390(dist_addr=0x10041000, cpu_addr=0x10040000)
     timer0 = Sp804(int_num0=36, int_num1=36, pio_addr=0x10011000)
     timer1 = Sp804(int_num0=37, int_num1=37, pio_addr=0x10012000)
@@ -464,8 +509,9 @@ class VExpress_EMM(RealView):
     _mem_regions = [(Addr('2GB'), Addr('2GB'))]
     pci_cfg_base = 0x30000000
     uart = Pl011(pio_addr=0x1c090000, int_num=37)
-    realview_io = RealViewCtrl(proc_id0=0x14000000, proc_id1=0x14000000, \
-                               idreg=0x02250000, pio_addr=0x1C010000)
+    realview_io = VExpressCoreTileCtrl(
+        proc_id0=0x14000000, proc_id1=0x14000000,
+        idreg=0x02250000, pio_addr=0x1C010000)
     gic = Pl390(dist_addr=0x2C001000, cpu_addr=0x2C002000)
     local_cpu_timer = CpuLocalTimer(int_num_timer=29, int_num_watchdog=30, pio_addr=0x2C080000)
     generic_timer = GenericTimer(int_phys=29, int_virt=27)
index 27ba2e8a6886b4372a075a755d6ac120eb7fa1d4..507def0c14a7c957f489bb5394b25cbc233fb93e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010,2013 ARM Limited
+ * Copyright (c) 2010,2013,2015 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -42,6 +42,7 @@
 #include "dev/arm/rv_ctrl.hh"
 #include "mem/packet.hh"
 #include "mem/packet_access.hh"
+#include "sim/voltage_domain.hh"
 
 RealViewCtrl::RealViewCtrl(Params *p)
     : BasicPioDevice(p, 0xD4), flags(0), scData(0)
@@ -155,86 +156,33 @@ RealViewCtrl::write(PacketPtr pkt)
           // A request is being submitted to read/write the system control
           // registers.  See
           // http://infocenter.arm.com/help/topic/com.arm.doc.dui0447h/CACDEFGH.html
-          // For now, model as much of the OSC regs (can't find docs) as Linux
-          // seems to require (can't find docs); some clocks are deemed to be 0,
-          // giving all kinds of /0 problems booting Linux 3.9.  Return a
-          // vaguely plausible number within the range the device trees state:
-          uint32_t data = pkt->get<uint32_t>();
-          uint16_t dev = bits(data, 11, 0);
-          uint8_t pos = bits(data, 15, 12);
-          uint8_t site = bits(data, 17, 16);
-          uint8_t func = bits(data, 25, 20);
-          uint8_t dcc = bits(data, 29, 26);
-          bool wr = bits(data, 30);
-          bool start = bits(data, 31);
+          CfgCtrlReg req = pkt->get<uint32_t>();
+          if (!req.start) {
+              DPRINTF(RVCTRL, "SCReg: write %#x to ctrl but not starting\n",
+                      req);
+              break;
+          }
+
+          auto it_dev(devices.find(req & CFG_CTRL_ADDR_MASK));
+          if (it_dev == devices.end()) {
+              warn_once("SCReg: Access to unknown device "
+                        "dcc%d:site%d:pos%d:fn%d:dev%d\n",
+                        req.dcc, req.site, req.pos, req.func, req.dev);
+              break;
+          }
+
+          // Service the request as a read or write depending on the
+          // wr bit in the control register.
+          Device &dev(*it_dev->second);
+          if (req.wr) {
+              DPRINTF(RVCTRL, "SCReg: Writing %#x (ctrlWr %#x)\n",
+                      scData, req);
+              dev.write(scData);
 
-          if (start) {
-              if (wr) {
-                  warn_once("SCReg: Writing %#x to dcc%d:site%d:pos%d:fn%d:dev%d\n",
-                          scData, dcc, site, pos, func, dev);
-                  // Only really support reading, for now!
-              } else {
-                  // Only deal with function 1 (oscillators) so far!
-                  if (dcc != 0 || pos != 0 || func != 1) {
-                      warn("SCReg: read from unknown area "
-                           "(dcc %d:site%d:pos%d:fn%d:dev%d)\n",
-                           dcc, site, pos, func, dev);
-                  } else {
-                      switch (site) {
-                        case 0: { // Motherboard regs
-                            switch(dev) {
-                              case 0: // MCC clk
-                                scData = 25000000;
-                                break;
-                              case 1: // CLCD clk
-                                scData = 25000000;
-                                break;
-                              case 2: // PeriphClk 24MHz
-                                scData = 24000000;
-                                break;
-                              default:
-                                scData = 0;
-                                warn("SCReg: read from unknown dev %d "
-                                     "(site%d:pos%d:fn%d)\n",
-                                     dev, site, pos, func);
-                            }
-                        } break;
-                        case 1: { // Coretile 1 regs
-                            switch(dev) {
-                              case 0: // CPU PLL ref
-                                scData = 50000000;
-                                break;
-                              case 4: // Muxed AXI master clock
-                                scData = 40000000;
-                                break;
-                              case 5: // HDLCD clk
-                                scData = 50000000;
-                                break;
-                              case 6: // SMB clock
-                                scData = 35000000;
-                                break;
-                              case 7: // SYS PLL (also used for pl011 UART!)
-                                scData = 40000000;
-                                break;
-                              case 8: // DDR PLL 40MHz fixed
-                                scData = 40000000;
-                                break;
-                              default:
-                                scData = 0;
-                                warn("SCReg: read from unknown dev %d "
-                                     "(site%d:pos%d:fn%d)\n",
-                                     dev, site, pos, func);
-                            }
-                        } break;
-                        default:
-                          warn("SCReg: Read from unknown site %d (pos%d:fn%d:dev%d)\n",
-                               site, pos, func, dev);
-                      }
-                      DPRINTF(RVCTRL, "SCReg: Will read %#x (ctrlWr %#x)\n", scData, data);
-                  }
-              }
           } else {
-              DPRINTF(RVCTRL, "SCReg: write %#x to ctrl but not starting\n", data);
+              scData = dev.read();
+              DPRINTF(RVCTRL, "SCReg: Reading %#x (ctrlRd %#x)\n",
+                      scData, req);
           }
       } break;
       case CfgStat:     // Weird to write this
@@ -259,8 +207,102 @@ RealViewCtrl::unserialize(CheckpointIn &cp)
     UNSERIALIZE_SCALAR(flags);
 }
 
+void
+RealViewCtrl::registerDevice(DeviceFunc func, uint8_t site, uint8_t pos,
+                             uint8_t dcc, uint16_t dev,
+                             Device *handler)
+{
+    CfgCtrlReg addr = 0;
+    addr.func = func;
+    addr.site = site;
+    addr.pos = pos;
+    addr.dcc = dcc;
+    addr.dev = dev;
+
+    if (devices.find(addr) != devices.end()) {
+        fatal("Platform device dcc%d:site%d:pos%d:fn%d:dev%d "
+              "already registered.",
+              addr.dcc, addr.site, addr.pos, addr.func, addr.dev);
+    }
+
+    devices[addr] = handler;
+}
+
+
+RealViewOsc::RealViewOsc(RealViewOscParams *p)
+    : ClockDomain(p, p->voltage_domain),
+      RealViewCtrl::Device(*p->parent, RealViewCtrl::FUNC_OSC,
+                           p->site, p->position, p->dcc, p->device)
+{
+    if (SimClock::Float::s  / p->freq > UINT32_MAX) {
+        fatal("Oscillator frequency out of range: %f\n",
+            SimClock::Float::s  / p->freq / 1E6);
+    }
+
+    _clockPeriod = p->freq;
+}
+
+void
+RealViewOsc::startup()
+{
+    // Tell dependent object to set their clock frequency
+    for (auto m : members)
+        m->updateClockPeriod();
+}
+
+void
+RealViewOsc::serialize(CheckpointOut &cp) const
+{
+    SERIALIZE_SCALAR(_clockPeriod);
+}
+
+void
+RealViewOsc::unserialize(CheckpointIn &cp)
+{
+    UNSERIALIZE_SCALAR(_clockPeriod);
+}
+
+void
+RealViewOsc::clockPeriod(Tick clock_period)
+{
+    panic_if(clock_period == 0, "%s has a clock period of zero\n", name());
+
+    // Align all members to the current tick
+    for (auto m : members)
+        m->updateClockPeriod();
+
+    _clockPeriod = clock_period;
+
+    // inform any derived clocks they need to updated their period
+    for (auto m : children)
+        m->updateClockPeriod();
+}
+
+uint32_t
+RealViewOsc::read() const
+{
+    const uint32_t freq(SimClock::Float::s / _clockPeriod);
+    DPRINTF(RVCTRL, "Reading OSC frequency: %f MHz\n", freq / 1E6);
+    return freq;
+}
+
+void
+RealViewOsc::write(uint32_t freq)
+{
+    DPRINTF(RVCTRL, "Setting new OSC frequency: %f MHz\n", freq / 1E6);
+    clockPeriod(SimClock::Float::s / freq);
+}
+
+
+
 RealViewCtrl *
 RealViewCtrlParams::create()
 {
     return new RealViewCtrl(this);
 }
+
+RealViewOsc *
+RealViewOscParams::create()
+{
+    return new RealViewOsc(this);
+}
index cae5e2e2c905a205828608a19fd9f0fcf3932e76..905fe14d9cf2292a92f02e29c3b3800022db1945 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010,2013 ARM Limited
+ * Copyright (c) 2010,2013,2015 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -43,6 +43,7 @@
 #include "base/bitunion.hh"
 #include "dev/io_device.hh"
 #include "params/RealViewCtrl.hh"
+#include "params/RealViewOsc.hh"
 
 /** @file
  * This implements the simple real view registers on a PBXA9
 
 class RealViewCtrl : public BasicPioDevice
 {
+  public:
+    enum DeviceFunc {
+        FUNC_OSC      = 1,
+        FUNC_VOLT     = 2,
+        FUNC_AMP      = 3,
+        FUNC_TEMP     = 4,
+        FUNC_RESET    = 5,
+        FUNC_SCC      = 6,
+        FUNC_MUXFPGA  = 7,
+        FUNC_SHUTDOWN = 8,
+        FUNC_REBOOT   = 9,
+        FUNC_DVIMODE  = 11,
+        FUNC_POWER    = 12,
+        FUNC_ENERGY   = 13,
+    };
+
+    class Device
+    {
+      public:
+        Device(RealViewCtrl &parent, DeviceFunc func,
+               uint8_t site, uint8_t pos, uint8_t dcc, uint16_t dev)
+        {
+            parent.registerDevice(func, site, pos, dcc, dev,  this);
+        }
+
+        virtual ~Device() {}
+
+        virtual uint32_t read() const = 0;
+        virtual void write(uint32_t value) = 0;
+    };
+
   protected:
     enum {
         IdReg      = 0x00,
@@ -96,6 +128,18 @@ class RealViewCtrl : public BasicPioDevice
         Bitfield<16> locked;
     EndBitUnion(SysLockReg)
 
+    BitUnion32(CfgCtrlReg)
+        Bitfield<11, 0> dev;
+        Bitfield<15, 12> pos;
+        Bitfield<17, 16> site;
+        Bitfield<25, 20> func;
+        Bitfield<29, 26> dcc;
+        Bitfield<30> wr;
+        Bitfield<31> start;
+    EndBitUnion(CfgCtrlReg)
+
+    static const uint32_t CFG_CTRL_ADDR_MASK = 0x3fffffffUL;
+
     SysLockReg sysLock;
 
     /** This register is used for smp booting.
@@ -127,17 +171,52 @@ class RealViewCtrl : public BasicPioDevice
      * @param pkt The memory request.
      * @param data Where to put the data.
      */
-    virtual Tick read(PacketPtr pkt);
+    Tick read(PacketPtr pkt) M5_ATTR_OVERRIDE;
 
     /**
      * All writes are simply ignored.
      * @param pkt The memory request.
      * @param data the data
      */
-    virtual Tick write(PacketPtr pkt);
+    Tick write(PacketPtr pkt) M5_ATTR_OVERRIDE;
 
     void serialize(CheckpointOut &cp) const M5_ATTR_OVERRIDE;
     void unserialize(CheckpointIn &cp) M5_ATTR_OVERRIDE;
+
+  public:
+    void registerDevice(DeviceFunc func, uint8_t site, uint8_t pos,
+                        uint8_t dcc, uint16_t dev,
+                        Device *handler);
+
+  protected:
+    std::map<uint32_t, Device *> devices;
+};
+
+/**
+ * This is an implementation of a programmable oscillator on the that
+ * can be configured through the RealView/Versatile Express
+ * configuration interface.
+ *
+ * See ARM DUI 0447J (ARM  Motherboard Express uATX -- V2M-P1).
+ */
+class RealViewOsc
+    : public ClockDomain, RealViewCtrl::Device
+{
+  public:
+    RealViewOsc(RealViewOscParams *p);
+    virtual ~RealViewOsc() {};
+
+    void startup() M5_ATTR_OVERRIDE;
+
+    void serialize(CheckpointOut &cp) const M5_ATTR_OVERRIDE;
+    void unserialize(CheckpointIn &cp) M5_ATTR_OVERRIDE;
+
+  public: // RealViewCtrl::Device interface
+    uint32_t read() const M5_ATTR_OVERRIDE;
+    void write(uint32_t freq) M5_ATTR_OVERRIDE;
+
+  protected:
+    void clockPeriod(Tick clock_period);
 };