112077Sgedare@rtems.org/*
212077Sgedare@rtems.org * Copyright (c) 2017 Gedare Bloom
312077Sgedare@rtems.org * Copyright (c) 2010 ARM Limited
412077Sgedare@rtems.org * All rights reserved
512077Sgedare@rtems.org *
612077Sgedare@rtems.org * The license below extends only to copyright in the software and shall
712077Sgedare@rtems.org * not be construed as granting a license to any other intellectual
812077Sgedare@rtems.org * property including but not limited to intellectual property relating
912077Sgedare@rtems.org * to a hardware implementation of the functionality of the software
1012077Sgedare@rtems.org * licensed hereunder.  You may use the software subject to the license
1112077Sgedare@rtems.org * terms below provided that you ensure that this notice is replicated
1212077Sgedare@rtems.org * unmodified and in its entirety in all distributions of the software,
1312077Sgedare@rtems.org * modified or unmodified, in source code or in binary form.
1412077Sgedare@rtems.org *
1512077Sgedare@rtems.org * Redistribution and use in source and binary forms, with or without
1612077Sgedare@rtems.org * modification, are permitted provided that the following conditions are
1712077Sgedare@rtems.org * met: redistributions of source code must retain the above copyright
1812077Sgedare@rtems.org * notice, this list of conditions and the following disclaimer;
1912077Sgedare@rtems.org * redistributions in binary form must reproduce the above copyright
2012077Sgedare@rtems.org * notice, this list of conditions and the following disclaimer in the
2112077Sgedare@rtems.org * documentation and/or other materials provided with the distribution;
2212077Sgedare@rtems.org * neither the name of the copyright holders nor the names of its
2312077Sgedare@rtems.org * contributors may be used to endorse or promote products derived from
2412077Sgedare@rtems.org * this software without specific prior written permission.
2512077Sgedare@rtems.org *
2612077Sgedare@rtems.org * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2712077Sgedare@rtems.org * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2812077Sgedare@rtems.org * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2912077Sgedare@rtems.org * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3012077Sgedare@rtems.org * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3112077Sgedare@rtems.org * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3212077Sgedare@rtems.org * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3312077Sgedare@rtems.org * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3412077Sgedare@rtems.org * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3512077Sgedare@rtems.org * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3612077Sgedare@rtems.org * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3712077Sgedare@rtems.org *
3812077Sgedare@rtems.org * Authors: Ali Saidi
3912077Sgedare@rtems.org *          Gedare Bloom
4012077Sgedare@rtems.org */
4112077Sgedare@rtems.org
4212077Sgedare@rtems.org#include "dev/arm/timer_a9global.hh"
4312077Sgedare@rtems.org
4412077Sgedare@rtems.org#include "base/intmath.hh"
4512077Sgedare@rtems.org#include "base/trace.hh"
4612077Sgedare@rtems.org#include "debug/Checkpoint.hh"
4712077Sgedare@rtems.org#include "debug/Timer.hh"
4812077Sgedare@rtems.org#include "dev/arm/base_gic.hh"
4912077Sgedare@rtems.org#include "mem/packet.hh"
5012077Sgedare@rtems.org#include "mem/packet_access.hh"
5112077Sgedare@rtems.org
5212077Sgedare@rtems.orgA9GlobalTimer::A9GlobalTimer(Params *p)
5312077Sgedare@rtems.org    : BasicPioDevice(p, 0x1C), gic(p->gic),
5412077Sgedare@rtems.org      global_timer(name() + ".globaltimer", this, p->int_num)
5512077Sgedare@rtems.org{
5612077Sgedare@rtems.org}
5712077Sgedare@rtems.org
5812077Sgedare@rtems.orgA9GlobalTimer::Timer::Timer(std::string __name, A9GlobalTimer *_parent,
5912077Sgedare@rtems.org    int int_num)
6012077Sgedare@rtems.org    : _name(__name), parent(_parent), intNum(int_num), control(0x0),
6112077Sgedare@rtems.org      rawInt(false), pendingInt(false), autoIncValue(0x0), cmpValEvent(this)
6212077Sgedare@rtems.org{
6312077Sgedare@rtems.org}
6412077Sgedare@rtems.org
6512077Sgedare@rtems.orgTick
6612077Sgedare@rtems.orgA9GlobalTimer::read(PacketPtr pkt)
6712077Sgedare@rtems.org{
6812077Sgedare@rtems.org    assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize);
6912077Sgedare@rtems.org    assert(pkt->getSize() == 4);
7012077Sgedare@rtems.org    Addr daddr = pkt->getAddr() - pioAddr;
7112077Sgedare@rtems.org
7212077Sgedare@rtems.org    if (daddr < Timer::Size)
7312077Sgedare@rtems.org        global_timer.read(pkt, daddr);
7412077Sgedare@rtems.org    else
7512077Sgedare@rtems.org        panic("Tried to read A9GlobalTimer at offset %#x that doesn't exist\n",
7612077Sgedare@rtems.org                daddr);
7712077Sgedare@rtems.org    pkt->makeAtomicResponse();
7812077Sgedare@rtems.org    return pioDelay;
7912077Sgedare@rtems.org}
8012077Sgedare@rtems.org
8112077Sgedare@rtems.orguint64_t
8212077Sgedare@rtems.orgA9GlobalTimer::Timer::getTimeCounterFromTicks(Tick ticks)
8312077Sgedare@rtems.org{
8412077Sgedare@rtems.org  return ticks / parent->clockPeriod() / (control.prescalar + 1) - 1;
8512077Sgedare@rtems.org}
8612077Sgedare@rtems.org
8712077Sgedare@rtems.orgvoid
8812077Sgedare@rtems.orgA9GlobalTimer::Timer::read(PacketPtr pkt, Addr daddr)
8912077Sgedare@rtems.org{
9012077Sgedare@rtems.org    DPRINTF(Timer, "Reading from A9GlobalTimer at offset: %#x\n", daddr);
9112077Sgedare@rtems.org    uint64_t time;
9212077Sgedare@rtems.org
9312077Sgedare@rtems.org    switch(daddr) {
9412077Sgedare@rtems.org      case CounterRegLow32:
9512077Sgedare@rtems.org        time = getTimeCounterFromTicks(curTick());
9612077Sgedare@rtems.org        DPRINTF(Timer, "-- returning lower 32-bits of counter: %u\n", time);
9713230Sgabeblack@google.com        pkt->setLE<uint32_t>(time);
9812077Sgedare@rtems.org        break;
9912077Sgedare@rtems.org      case CounterRegHigh32:
10012077Sgedare@rtems.org        time = getTimeCounterFromTicks(curTick());
10112077Sgedare@rtems.org        time >>= 32;
10212077Sgedare@rtems.org        DPRINTF(Timer, "-- returning upper 32-bits of counter: %u\n", time);
10313230Sgabeblack@google.com        pkt->setLE<uint32_t>(time);
10412077Sgedare@rtems.org        break;
10512077Sgedare@rtems.org      case ControlReg:
10613230Sgabeblack@google.com        pkt->setLE<uint32_t>(control);
10712077Sgedare@rtems.org        break;
10812077Sgedare@rtems.org      case IntStatusReg:
10913230Sgabeblack@google.com        pkt->setLE<uint32_t>(rawInt);
11012077Sgedare@rtems.org        break;
11112077Sgedare@rtems.org      case CmpValRegLow32:
11212077Sgedare@rtems.org        DPRINTF(Timer, "Event schedule for %d, clock=%d, prescale=%d\n",
11312077Sgedare@rtems.org                cmpValEvent.when(), parent->clockPeriod(), control.prescalar);
11412077Sgedare@rtems.org        if (cmpValEvent.scheduled()) {
11512077Sgedare@rtems.org          time = getTimeCounterFromTicks(cmpValEvent.when() - curTick());
11612077Sgedare@rtems.org        } else {
11712077Sgedare@rtems.org          time = 0;
11812077Sgedare@rtems.org        }
11912077Sgedare@rtems.org        DPRINTF(Timer, "-- returning lower 32-bits of comparator: %u\n", time);
12013230Sgabeblack@google.com        pkt->setLE<uint32_t>(time);
12112077Sgedare@rtems.org        break;
12212077Sgedare@rtems.org      case CmpValRegHigh32:
12312077Sgedare@rtems.org        DPRINTF(Timer, "Event schedule for %d, clock=%d, prescale=%d\n",
12412077Sgedare@rtems.org                cmpValEvent.when(), parent->clockPeriod(), control.prescalar);
12512077Sgedare@rtems.org        if (cmpValEvent.scheduled()) {
12612077Sgedare@rtems.org          time = getTimeCounterFromTicks(cmpValEvent.when() - curTick());
12712077Sgedare@rtems.org          time >>= 32;
12812077Sgedare@rtems.org        } else {
12912077Sgedare@rtems.org          time = 0;
13012077Sgedare@rtems.org        }
13112077Sgedare@rtems.org        DPRINTF(Timer, "-- returning upper 32-bits of comparator: %u\n", time);
13213230Sgabeblack@google.com        pkt->setLE<uint32_t>(time);
13312077Sgedare@rtems.org        break;
13412077Sgedare@rtems.org      case AutoIncrementReg:
13513230Sgabeblack@google.com        pkt->setLE<uint32_t>(autoIncValue);
13612077Sgedare@rtems.org        break;
13712077Sgedare@rtems.org      default:
13812077Sgedare@rtems.org        panic("Tried to read A9GlobalTimer at offset %#x\n", daddr);
13912077Sgedare@rtems.org        break;
14012077Sgedare@rtems.org    }
14112077Sgedare@rtems.org    DPRINTF(Timer, "Reading %#x from A9GlobalTimer at offset: %#x\n",
14213230Sgabeblack@google.com             pkt->getLE<uint32_t>(), daddr);
14312077Sgedare@rtems.org}
14412077Sgedare@rtems.org
14512077Sgedare@rtems.orgTick
14612077Sgedare@rtems.orgA9GlobalTimer::write(PacketPtr pkt)
14712077Sgedare@rtems.org{
14812077Sgedare@rtems.org    assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize);
14912077Sgedare@rtems.org    assert(pkt->getSize() == 4);
15012077Sgedare@rtems.org    Addr daddr = pkt->getAddr() - pioAddr;
15112077Sgedare@rtems.org    DPRINTF(Timer, "Writing to A9GlobalTimer at offset: %#x\n", daddr);
15212077Sgedare@rtems.org
15312077Sgedare@rtems.org    warn_once("A9 Global Timer doesn't support banked per-cpu registers\n");
15412077Sgedare@rtems.org
15512077Sgedare@rtems.org    if (daddr < Timer::Size)
15612077Sgedare@rtems.org        global_timer.write(pkt, daddr);
15712077Sgedare@rtems.org    else
15812077Sgedare@rtems.org        panic("Tried to write A9GlobalTimer at offset %#x doesn't exist\n",
15912077Sgedare@rtems.org                daddr);
16012077Sgedare@rtems.org    pkt->makeAtomicResponse();
16112077Sgedare@rtems.org    return pioDelay;
16212077Sgedare@rtems.org}
16312077Sgedare@rtems.org
16412077Sgedare@rtems.orgvoid
16512077Sgedare@rtems.orgA9GlobalTimer::Timer::write(PacketPtr pkt, Addr daddr)
16612077Sgedare@rtems.org{
16712077Sgedare@rtems.org    DPRINTF(Timer, "Writing %#x to A9GlobalTimer at offset: %#x\n",
16813230Sgabeblack@google.com            pkt->getLE<uint32_t>(), daddr);
16912077Sgedare@rtems.org    switch (daddr) {
17012077Sgedare@rtems.org     case CounterRegLow32:
17112077Sgedare@rtems.org     case CounterRegHigh32:
17212077Sgedare@rtems.org        DPRINTF(Timer, "Ignoring unsupported write to Global Timer Counter\n");
17312077Sgedare@rtems.org        break;
17412077Sgedare@rtems.org      case ControlReg:
17512077Sgedare@rtems.org        bool old_enable;
17612077Sgedare@rtems.org        bool old_cmpEnable;
17712077Sgedare@rtems.org        old_enable = control.enable;
17812077Sgedare@rtems.org        old_cmpEnable = control.cmpEnable;
17913230Sgabeblack@google.com        control = pkt->getLE<uint32_t>();
18012077Sgedare@rtems.org        if ((old_enable == 0) && control.enable)
18112077Sgedare@rtems.org            restartCounter();
18212077Sgedare@rtems.org        if ((old_cmpEnable == 0) && control.cmpEnable)
18312077Sgedare@rtems.org            restartCounter();
18412077Sgedare@rtems.org        break;
18512077Sgedare@rtems.org      case IntStatusReg:
18612077Sgedare@rtems.org        /* TODO: should check that '1' was written. */
18712077Sgedare@rtems.org        rawInt = false;
18812077Sgedare@rtems.org        if (pendingInt) {
18912077Sgedare@rtems.org            pendingInt = false;
19012077Sgedare@rtems.org            DPRINTF(Timer, "Clearing interrupt\n");
19112077Sgedare@rtems.org            parent->gic->clearInt(intNum);
19212077Sgedare@rtems.org        }
19312077Sgedare@rtems.org        break;
19412077Sgedare@rtems.org      case CmpValRegLow32:
19512077Sgedare@rtems.org        cmpVal &= 0xFFFFFFFF00000000ULL;
19613230Sgabeblack@google.com        cmpVal |= (uint64_t)pkt->getLE<uint32_t>();
19712077Sgedare@rtems.org        break;
19812077Sgedare@rtems.org      case CmpValRegHigh32:
19912077Sgedare@rtems.org        cmpVal &= 0x00000000FFFFFFFFULL;
20013230Sgabeblack@google.com        cmpVal |= ((uint64_t)pkt->getLE<uint32_t>() << 32);
20112077Sgedare@rtems.org        break;
20212077Sgedare@rtems.org      case AutoIncrementReg:
20313230Sgabeblack@google.com        autoIncValue = pkt->getLE<uint32_t>();
20412077Sgedare@rtems.org        break;
20512077Sgedare@rtems.org      default:
20612077Sgedare@rtems.org        panic("Tried to write A9GlobalTimer at offset %#x\n", daddr);
20712077Sgedare@rtems.org        break;
20812077Sgedare@rtems.org    }
20912077Sgedare@rtems.org}
21012077Sgedare@rtems.org
21112077Sgedare@rtems.orgvoid
21212077Sgedare@rtems.orgA9GlobalTimer::Timer::restartCounter()
21312077Sgedare@rtems.org{
21412077Sgedare@rtems.org    if (!control.enable)
21512077Sgedare@rtems.org        return;
21612077Sgedare@rtems.org    DPRINTF(Timer, "Restarting counter with value %#x\n", cmpVal);
21712077Sgedare@rtems.org
21812077Sgedare@rtems.org    Tick time = parent->clockPeriod() * (control.prescalar + 1) * (cmpVal + 1);
21912077Sgedare@rtems.org
22012077Sgedare@rtems.org    if (time < curTick()) {
22112077Sgedare@rtems.org        DPRINTF(Timer, "-- Event time %#x < curTick %#x\n", time, curTick());
22212077Sgedare@rtems.org        return;
22312077Sgedare@rtems.org    }
22412077Sgedare@rtems.org    if (cmpValEvent.scheduled()) {
22512077Sgedare@rtems.org        DPRINTF(Timer, "-- Event was already schedule, de-scheduling\n");
22612077Sgedare@rtems.org        parent->deschedule(cmpValEvent);
22712077Sgedare@rtems.org    }
22812077Sgedare@rtems.org    parent->schedule(cmpValEvent, time);
22912077Sgedare@rtems.org    DPRINTF(Timer, "-- Scheduling new event for: %d\n", time);
23012077Sgedare@rtems.org}
23112077Sgedare@rtems.org
23212077Sgedare@rtems.orgvoid
23312077Sgedare@rtems.orgA9GlobalTimer::Timer::counterAtCmpVal()
23412077Sgedare@rtems.org{
23512077Sgedare@rtems.org    if (!control.enable)
23612077Sgedare@rtems.org        return;
23712077Sgedare@rtems.org
23812077Sgedare@rtems.org    DPRINTF(Timer, "Counter reached cmpVal\n");
23912077Sgedare@rtems.org
24012077Sgedare@rtems.org    rawInt = true;
24112077Sgedare@rtems.org    bool old_pending = pendingInt;
24212077Sgedare@rtems.org    if (control.intEnable)
24312077Sgedare@rtems.org        pendingInt = true;
24412077Sgedare@rtems.org    if (pendingInt && !old_pending) {
24512077Sgedare@rtems.org        DPRINTF(Timer, "-- Causing interrupt\n");
24612077Sgedare@rtems.org        parent->gic->sendPPInt(intNum, 0); /* FIXME: cpuNum */
24712077Sgedare@rtems.org    }
24812077Sgedare@rtems.org
24912077Sgedare@rtems.org    if (control.autoIncrement == 0) // one-shot
25012077Sgedare@rtems.org        return;
25112077Sgedare@rtems.org
25212077Sgedare@rtems.org    cmpVal += (uint64_t)autoIncValue;
25312077Sgedare@rtems.org    restartCounter();
25412077Sgedare@rtems.org}
25512077Sgedare@rtems.org
25612077Sgedare@rtems.orgvoid
25712077Sgedare@rtems.orgA9GlobalTimer::Timer::serialize(CheckpointOut &cp) const
25812077Sgedare@rtems.org{
25912077Sgedare@rtems.org    DPRINTF(Checkpoint, "Serializing Arm A9GlobalTimer\n");
26012077Sgedare@rtems.org
26112077Sgedare@rtems.org    uint32_t control_serial = control;
26212077Sgedare@rtems.org    SERIALIZE_SCALAR(control_serial);
26312077Sgedare@rtems.org
26412077Sgedare@rtems.org    SERIALIZE_SCALAR(rawInt);
26512077Sgedare@rtems.org    SERIALIZE_SCALAR(pendingInt);
26612077Sgedare@rtems.org    SERIALIZE_SCALAR(cmpVal);
26712077Sgedare@rtems.org    SERIALIZE_SCALAR(autoIncValue);
26812077Sgedare@rtems.org
26912077Sgedare@rtems.org    bool is_in_event = cmpValEvent.scheduled();
27012077Sgedare@rtems.org    SERIALIZE_SCALAR(is_in_event);
27112077Sgedare@rtems.org
27212077Sgedare@rtems.org    Tick event_time;
27312077Sgedare@rtems.org    if (is_in_event){
27412077Sgedare@rtems.org        event_time = cmpValEvent.when();
27512077Sgedare@rtems.org        SERIALIZE_SCALAR(event_time);
27612077Sgedare@rtems.org    }
27712077Sgedare@rtems.org}
27812077Sgedare@rtems.org
27912077Sgedare@rtems.orgvoid
28012077Sgedare@rtems.orgA9GlobalTimer::Timer::unserialize(CheckpointIn &cp)
28112077Sgedare@rtems.org{
28212077Sgedare@rtems.org    DPRINTF(Checkpoint, "Unserializing Arm A9GlobalTimer\n");
28312077Sgedare@rtems.org
28412077Sgedare@rtems.org    uint32_t control_serial;
28512077Sgedare@rtems.org    UNSERIALIZE_SCALAR(control_serial);
28612077Sgedare@rtems.org    control = control_serial;
28712077Sgedare@rtems.org
28812077Sgedare@rtems.org    UNSERIALIZE_SCALAR(rawInt);
28912077Sgedare@rtems.org    UNSERIALIZE_SCALAR(pendingInt);
29012077Sgedare@rtems.org    UNSERIALIZE_SCALAR(cmpVal);
29112077Sgedare@rtems.org    UNSERIALIZE_SCALAR(autoIncValue);
29212077Sgedare@rtems.org
29312077Sgedare@rtems.org    bool is_in_event;
29412077Sgedare@rtems.org    UNSERIALIZE_SCALAR(is_in_event);
29512077Sgedare@rtems.org
29612077Sgedare@rtems.org    Tick event_time;
29712077Sgedare@rtems.org    if (is_in_event){
29812077Sgedare@rtems.org        UNSERIALIZE_SCALAR(event_time);
29912077Sgedare@rtems.org        parent->schedule(cmpValEvent, event_time);
30012077Sgedare@rtems.org    }
30112077Sgedare@rtems.org}
30212077Sgedare@rtems.org
30312077Sgedare@rtems.orgvoid
30412077Sgedare@rtems.orgA9GlobalTimer::serialize(CheckpointOut &cp) const
30512077Sgedare@rtems.org{
30612077Sgedare@rtems.org    global_timer.serialize(cp);
30712077Sgedare@rtems.org}
30812077Sgedare@rtems.org
30912077Sgedare@rtems.orgvoid
31012077Sgedare@rtems.orgA9GlobalTimer::unserialize(CheckpointIn &cp)
31112077Sgedare@rtems.org{
31212077Sgedare@rtems.org    global_timer.unserialize(cp);
31312077Sgedare@rtems.org}
31412077Sgedare@rtems.org
31512077Sgedare@rtems.orgA9GlobalTimer *
31612077Sgedare@rtems.orgA9GlobalTimerParams::create()
31712077Sgedare@rtems.org{
31812077Sgedare@rtems.org    return new A9GlobalTimer(this);
31912077Sgedare@rtems.org}
320