1/* 2 * Copyright (c) 2012-2014 ARM Limited 3 * All rights reserved 4 * 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. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are 16 * met: redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer; 18 * redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution; 21 * neither the name of the copyright holders nor the names of its 22 * contributors may be used to endorse or promote products derived from 23 * this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * Authors: Vasileios Spiliopoulos 38 * Akash Bagdia 39 * Stephan Diestelhorst 40 */ 41 42#include "dev/arm/energy_ctrl.hh" 43 44#include "debug/EnergyCtrl.hh" 45#include "mem/packet.hh" 46#include "mem/packet_access.hh" 47#include "params/EnergyCtrl.hh" 48#include "sim/dvfs_handler.hh" 49 50EnergyCtrl::EnergyCtrl(const Params *p) 51 : BasicPioDevice(p, PIO_NUM_FIELDS * 4), // each field is 32 bit 52 dvfsHandler(p->dvfs_handler), 53 domainID(0), 54 domainIDIndexToRead(0), 55 perfLevelAck(0), 56 perfLevelToRead(0), 57 updateAckEvent([this]{ updatePLAck(); }, name()) 58{ 59 fatal_if(!p->dvfs_handler, "EnergyCtrl: Needs a DVFSHandler for a " 60 "functioning system.\n"); 61} 62 63Tick 64EnergyCtrl::read(PacketPtr pkt) 65{ 66 assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); 67 assert(pkt->getSize() == 4); 68 69 Addr daddr = pkt->getAddr() - pioAddr; 70 assert((daddr & 3) == 0); 71 Registers reg = Registers(daddr / 4); 72 73 if (!dvfsHandler->isEnabled()) { 74 // NB: Zero is a good response if the handler is disabled 75 pkt->setLE<uint32_t>(0); 76 warn_once("EnergyCtrl: Disabled handler, ignoring read from reg %i\n", 77 reg); 78 DPRINTF(EnergyCtrl, "dvfs handler disabled, return 0 for read from "\ 79 "reg %i\n", reg); 80 pkt->makeAtomicResponse(); 81 return pioDelay; 82 } 83 84 uint32_t result = 0; 85 Tick period; 86 double voltage; 87 88 switch(reg) { 89 case DVFS_HANDLER_STATUS: 90 result = 1; 91 DPRINTF(EnergyCtrl, "dvfs handler enabled\n"); 92 break; 93 case DVFS_NUM_DOMAINS: 94 result = dvfsHandler->numDomains(); 95 DPRINTF(EnergyCtrl, "reading number of domains %d\n", result); 96 break; 97 case DVFS_DOMAINID_AT_INDEX: 98 result = dvfsHandler->domainID(domainIDIndexToRead); 99 DPRINTF(EnergyCtrl, "reading domain id at index %d as %d\n", 100 domainIDIndexToRead, result); 101 break; 102 case DVFS_HANDLER_TRANS_LATENCY: 103 // Return transition latency in nanoseconds 104 result = dvfsHandler->transLatency() / SimClock::Int::ns; 105 DPRINTF(EnergyCtrl, "reading dvfs handler trans latency %d ns\n", 106 result); 107 break; 108 case DOMAIN_ID: 109 result = domainID; 110 DPRINTF(EnergyCtrl, "reading domain id:%d\n", result); 111 break; 112 case PERF_LEVEL: 113 result = dvfsHandler->perfLevel(domainID); 114 DPRINTF(EnergyCtrl, "reading domain %d perf level: %d\n", 115 domainID, result); 116 break; 117 case PERF_LEVEL_ACK: 118 result = perfLevelAck; 119 DPRINTF(EnergyCtrl, "reading ack:%d\n", result); 120 // Signal is set for a single read only 121 if (result == 1) 122 perfLevelAck = 0; 123 break; 124 case NUM_OF_PERF_LEVELS: 125 result = dvfsHandler->numPerfLevels(domainID); 126 DPRINTF(EnergyCtrl, "reading num of perf level:%d\n", result); 127 break; 128 case FREQ_AT_PERF_LEVEL: 129 period = dvfsHandler->clkPeriodAtPerfLevel(domainID, perfLevelToRead); 130 result = ticksTokHz(period); 131 DPRINTF(EnergyCtrl, "reading freq %d KHz at perf level: %d\n", 132 result, perfLevelToRead); 133 break; 134 case VOLT_AT_PERF_LEVEL: 135 voltage = dvfsHandler->voltageAtPerfLevel(domainID, perfLevelToRead); 136 result = toMicroVolt(voltage); 137 DPRINTF(EnergyCtrl, "reading voltage %d u-volt at perf level: %d\n", 138 result, perfLevelToRead); 139 break; 140 default: 141 panic("Tried to read EnergyCtrl at offset %#x / reg %i\n", daddr, 142 reg); 143 } 144 pkt->setLE<uint32_t>(result); 145 pkt->makeAtomicResponse(); 146 return pioDelay; 147} 148 149Tick 150EnergyCtrl::write(PacketPtr pkt) 151{ 152 assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); 153 assert(pkt->getSize() == 4); 154 155 uint32_t data; 156 data = pkt->getLE<uint32_t>(); 157 158 Addr daddr = pkt->getAddr() - pioAddr; 159 assert((daddr & 3) == 0); 160 Registers reg = Registers(daddr / 4); 161 162 if (!dvfsHandler->isEnabled()) { 163 // Ignore writes to a disabled controller 164 warn_once("EnergyCtrl: Disabled handler, ignoring write %u to "\ 165 "reg %i\n", data, reg); 166 DPRINTF(EnergyCtrl, "dvfs handler disabled, ignoring write %u to "\ 167 "reg %i\n", data, reg); 168 pkt->makeAtomicResponse(); 169 return pioDelay; 170 } 171 172 switch(reg) { 173 case DVFS_DOMAINID_AT_INDEX: 174 domainIDIndexToRead = data; 175 DPRINTF(EnergyCtrl, "writing domain id index:%d\n", 176 domainIDIndexToRead); 177 break; 178 case DOMAIN_ID: 179 // Extra check to ensure that a valid domain ID is being queried 180 if (dvfsHandler->validDomainID(data)) { 181 domainID = data; 182 DPRINTF(EnergyCtrl, "writing domain id:%d\n", domainID); 183 } else { 184 DPRINTF(EnergyCtrl, "invalid domain id:%d\n", domainID); 185 } 186 break; 187 case PERF_LEVEL: 188 if (dvfsHandler->perfLevel(domainID, data)) { 189 if (updateAckEvent.scheduled()) { 190 // The OS driver is trying to change the perf level while 191 // another change is in flight. This is fine, but only a 192 // single acknowledgment will be sent. 193 DPRINTF(EnergyCtrl, "descheduling previous pending ack "\ 194 "event\n"); 195 deschedule(updateAckEvent); 196 } 197 schedule(updateAckEvent, curTick() + dvfsHandler->transLatency()); 198 DPRINTF(EnergyCtrl, "writing domain %d perf level: %d\n", 199 domainID, data); 200 } else { 201 DPRINTF(EnergyCtrl, "invalid / ineffective perf level:%d for "\ 202 "domain:%d\n", data, domainID); 203 } 204 break; 205 case PERF_LEVEL_TO_READ: 206 perfLevelToRead = data; 207 DPRINTF(EnergyCtrl, "writing perf level to read opp at: %d\n", 208 data); 209 break; 210 default: 211 panic("Tried to write EnergyCtrl at offset %#x\n", daddr); 212 break; 213 } 214 215 pkt->makeAtomicResponse(); 216 return pioDelay; 217} 218 219void 220EnergyCtrl::serialize(CheckpointOut &cp) const 221{ 222 SERIALIZE_SCALAR(domainID); 223 SERIALIZE_SCALAR(domainIDIndexToRead); 224 SERIALIZE_SCALAR(perfLevelToRead); 225 SERIALIZE_SCALAR(perfLevelAck); 226 227 Tick next_event = updateAckEvent.scheduled() ? updateAckEvent.when() : 0; 228 SERIALIZE_SCALAR(next_event); 229} 230 231void 232EnergyCtrl::unserialize(CheckpointIn &cp) 233{ 234 UNSERIALIZE_SCALAR(domainID); 235 UNSERIALIZE_SCALAR(domainIDIndexToRead); 236 UNSERIALIZE_SCALAR(perfLevelToRead); 237 UNSERIALIZE_SCALAR(perfLevelAck); 238 Tick next_event = 0; 239 UNSERIALIZE_SCALAR(next_event); 240 241 // restore scheduled events 242 if (next_event != 0) { 243 schedule(updateAckEvent, next_event); 244 } 245} 246 247EnergyCtrl * EnergyCtrlParams::create() 248{ 249 return new EnergyCtrl(this); 250} 251 252void 253EnergyCtrl::startup() 254{ 255 if (!dvfsHandler->isEnabled()) { 256 warn("Existing EnergyCtrl, but no enabled DVFSHandler found.\n"); 257 } 258} 259 260void 261EnergyCtrl::init() 262{ 263 BasicPioDevice::init(); 264} 265