arm: Add support for programmable oscillators

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.
This commit is contained in:
Andreas Sandberg
2015-08-07 09:59:25 +01:00
parent cd098a7e84
commit 598edaae05
3 changed files with 253 additions and 86 deletions

View 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);
}