kmi.cc revision 9806:3f262c18ad5d
17191Sgblack@eecs.umich.edu/*
27191Sgblack@eecs.umich.edu * Copyright (c) 2010 ARM Limited
37191Sgblack@eecs.umich.edu * All rights reserved
47191Sgblack@eecs.umich.edu *
57191Sgblack@eecs.umich.edu * The license below extends only to copyright in the software and shall
67191Sgblack@eecs.umich.edu * not be construed as granting a license to any other intellectual
77191Sgblack@eecs.umich.edu * property including but not limited to intellectual property relating
87191Sgblack@eecs.umich.edu * to a hardware implementation of the functionality of the software
97191Sgblack@eecs.umich.edu * licensed hereunder.  You may use the software subject to the license
107191Sgblack@eecs.umich.edu * terms below provided that you ensure that this notice is replicated
117191Sgblack@eecs.umich.edu * unmodified and in its entirety in all distributions of the software,
127191Sgblack@eecs.umich.edu * modified or unmodified, in source code or in binary form.
137191Sgblack@eecs.umich.edu *
147191Sgblack@eecs.umich.edu * Copyright (c) 2005 The Regents of The University of Michigan
157191Sgblack@eecs.umich.edu * All rights reserved.
167191Sgblack@eecs.umich.edu *
177191Sgblack@eecs.umich.edu * Redistribution and use in source and binary forms, with or without
187191Sgblack@eecs.umich.edu * modification, are permitted provided that the following conditions are
197191Sgblack@eecs.umich.edu * met: redistributions of source code must retain the above copyright
207191Sgblack@eecs.umich.edu * notice, this list of conditions and the following disclaimer;
217191Sgblack@eecs.umich.edu * redistributions in binary form must reproduce the above copyright
227191Sgblack@eecs.umich.edu * notice, this list of conditions and the following disclaimer in the
237191Sgblack@eecs.umich.edu * documentation and/or other materials provided with the distribution;
247191Sgblack@eecs.umich.edu * neither the name of the copyright holders nor the names of its
257191Sgblack@eecs.umich.edu * contributors may be used to endorse or promote products derived from
267191Sgblack@eecs.umich.edu * this software without specific prior written permission.
277191Sgblack@eecs.umich.edu *
287191Sgblack@eecs.umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
297191Sgblack@eecs.umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
307191Sgblack@eecs.umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
317191Sgblack@eecs.umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
327191Sgblack@eecs.umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
337191Sgblack@eecs.umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
347191Sgblack@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
357191Sgblack@eecs.umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
367191Sgblack@eecs.umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
377191Sgblack@eecs.umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
387191Sgblack@eecs.umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
397191Sgblack@eecs.umich.edu *
407191Sgblack@eecs.umich.edu * Authors: Ali Saidi
417191Sgblack@eecs.umich.edu *          William Wang
427191Sgblack@eecs.umich.edu */
437191Sgblack@eecs.umich.edu
447191Sgblack@eecs.umich.edu#include "base/vnc/vncinput.hh"
457191Sgblack@eecs.umich.edu#include "base/trace.hh"
467191Sgblack@eecs.umich.edu#include "debug/Pl050.hh"
477308Sgblack@eecs.umich.edu#include "dev/arm/amba_device.hh"
487191Sgblack@eecs.umich.edu#include "dev/arm/kmi.hh"
497191Sgblack@eecs.umich.edu#include "dev/ps2.hh"
507191Sgblack@eecs.umich.edu#include "mem/packet.hh"
517191Sgblack@eecs.umich.edu#include "mem/packet_access.hh"
527191Sgblack@eecs.umich.edu
537191Sgblack@eecs.umich.eduPl050::Pl050(const Params *p)
547191Sgblack@eecs.umich.edu    : AmbaIntDevice(p), control(0), status(0x43), clkdiv(0), interrupts(0),
557191Sgblack@eecs.umich.edu      rawInterrupts(0), ackNext(false), shiftDown(false), vnc(p->vnc),
567191Sgblack@eecs.umich.edu      driverInitialized(false), intEvent(this)
577191Sgblack@eecs.umich.edu{
587191Sgblack@eecs.umich.edu    pioSize = 0xfff;
597191Sgblack@eecs.umich.edu
607191Sgblack@eecs.umich.edu    if (vnc) {
617248Sgblack@eecs.umich.edu        if (!p->is_mouse)
627191Sgblack@eecs.umich.edu            vnc->setKeyboard(this);
637192Sgblack@eecs.umich.edu        else
647192Sgblack@eecs.umich.edu            vnc->setMouse(this);
657192Sgblack@eecs.umich.edu    }
667192Sgblack@eecs.umich.edu}
677192Sgblack@eecs.umich.edu
687192Sgblack@eecs.umich.eduTick
697192Sgblack@eecs.umich.eduPl050::read(PacketPtr pkt)
707192Sgblack@eecs.umich.edu{
717192Sgblack@eecs.umich.edu    assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize);
727191Sgblack@eecs.umich.edu
737191Sgblack@eecs.umich.edu    Addr daddr = pkt->getAddr() - pioAddr;
747191Sgblack@eecs.umich.edu    pkt->allocate();
757192Sgblack@eecs.umich.edu
767192Sgblack@eecs.umich.edu
777192Sgblack@eecs.umich.edu    uint32_t data = 0;
787192Sgblack@eecs.umich.edu
797192Sgblack@eecs.umich.edu    switch (daddr) {
807192Sgblack@eecs.umich.edu      case kmiCr:
817192Sgblack@eecs.umich.edu        DPRINTF(Pl050, "Read Commmand: %#x\n", (uint32_t)control);
827192Sgblack@eecs.umich.edu        data = control;
837192Sgblack@eecs.umich.edu        break;
847192Sgblack@eecs.umich.edu      case kmiStat:
857192Sgblack@eecs.umich.edu        if (rxQueue.empty())
867192Sgblack@eecs.umich.edu            status.rxfull = 0;
877192Sgblack@eecs.umich.edu        else
887192Sgblack@eecs.umich.edu            status.rxfull = 1;
897192Sgblack@eecs.umich.edu
907192Sgblack@eecs.umich.edu        DPRINTF(Pl050, "Read Status: %#x\n", (uint32_t)status);
917192Sgblack@eecs.umich.edu        data = status;
927192Sgblack@eecs.umich.edu        break;
937192Sgblack@eecs.umich.edu      case kmiData:
947192Sgblack@eecs.umich.edu        if (rxQueue.empty()) {
957191Sgblack@eecs.umich.edu            data = 0;
967191Sgblack@eecs.umich.edu        } else {
977191Sgblack@eecs.umich.edu            data = rxQueue.front();
987191Sgblack@eecs.umich.edu            rxQueue.pop_front();
997191Sgblack@eecs.umich.edu        }
1007191Sgblack@eecs.umich.edu        DPRINTF(Pl050, "Read Data: %#x\n", (uint32_t)data);
1017191Sgblack@eecs.umich.edu        updateIntStatus();
1027191Sgblack@eecs.umich.edu        break;
1037191Sgblack@eecs.umich.edu      case kmiClkDiv:
1047191Sgblack@eecs.umich.edu        data = clkdiv;
1057191Sgblack@eecs.umich.edu        break;
1067191Sgblack@eecs.umich.edu      case kmiISR:
1077191Sgblack@eecs.umich.edu        data = interrupts;
1087191Sgblack@eecs.umich.edu        DPRINTF(Pl050, "Read Interrupts: %#x\n", (uint32_t)interrupts);
1097191Sgblack@eecs.umich.edu        break;
1107191Sgblack@eecs.umich.edu      default:
1117248Sgblack@eecs.umich.edu        if (readId(pkt, ambaId, pioAddr)) {
1127191Sgblack@eecs.umich.edu            // Hack for variable size accesses
1137192Sgblack@eecs.umich.edu            data = pkt->get<uint32_t>();
1147192Sgblack@eecs.umich.edu            break;
1157192Sgblack@eecs.umich.edu        }
1167192Sgblack@eecs.umich.edu
1177192Sgblack@eecs.umich.edu        warn("Tried to read PL050 at offset %#x that doesn't exist\n", daddr);
1187192Sgblack@eecs.umich.edu        break;
1197192Sgblack@eecs.umich.edu    }
1207192Sgblack@eecs.umich.edu
1217192Sgblack@eecs.umich.edu    switch(pkt->getSize()) {
1227192Sgblack@eecs.umich.edu      case 1:
1237192Sgblack@eecs.umich.edu        pkt->set<uint8_t>(data);
1247192Sgblack@eecs.umich.edu        break;
1257192Sgblack@eecs.umich.edu      case 2:
1267192Sgblack@eecs.umich.edu        pkt->set<uint16_t>(data);
1277191Sgblack@eecs.umich.edu        break;
1287192Sgblack@eecs.umich.edu      case 4:
1297192Sgblack@eecs.umich.edu        pkt->set<uint32_t>(data);
1307192Sgblack@eecs.umich.edu        break;
1317192Sgblack@eecs.umich.edu      default:
1327192Sgblack@eecs.umich.edu        panic("KMI read size too big?\n");
1337192Sgblack@eecs.umich.edu        break;
1347192Sgblack@eecs.umich.edu    }
1357192Sgblack@eecs.umich.edu
1367192Sgblack@eecs.umich.edu    pkt->makeAtomicResponse();
1377192Sgblack@eecs.umich.edu    return pioDelay;
1387192Sgblack@eecs.umich.edu}
1397192Sgblack@eecs.umich.edu
1407192Sgblack@eecs.umich.eduTick
1417192Sgblack@eecs.umich.eduPl050::write(PacketPtr pkt)
1427192Sgblack@eecs.umich.edu{
1437192Sgblack@eecs.umich.edu
1447192Sgblack@eecs.umich.edu    assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize);
1457192Sgblack@eecs.umich.edu
1467192Sgblack@eecs.umich.edu    Addr daddr = pkt->getAddr() - pioAddr;
1477192Sgblack@eecs.umich.edu
1487192Sgblack@eecs.umich.edu    assert(pkt->getSize() == sizeof(uint8_t));
1497192Sgblack@eecs.umich.edu
1507192Sgblack@eecs.umich.edu
1517192Sgblack@eecs.umich.edu    switch (daddr) {
1527192Sgblack@eecs.umich.edu      case kmiCr:
1537192Sgblack@eecs.umich.edu        DPRINTF(Pl050, "Write Commmand: %#x\n", (uint32_t)pkt->get<uint8_t>());
1547192Sgblack@eecs.umich.edu        control = pkt->get<uint8_t>();
1557192Sgblack@eecs.umich.edu        updateIntStatus();
1567192Sgblack@eecs.umich.edu        break;
1577192Sgblack@eecs.umich.edu      case kmiData:
1587191Sgblack@eecs.umich.edu        DPRINTF(Pl050, "Write Data: %#x\n", (uint32_t)pkt->get<uint8_t>());
1597191Sgblack@eecs.umich.edu        processCommand(pkt->get<uint8_t>());
1607191Sgblack@eecs.umich.edu        updateIntStatus();
1617191Sgblack@eecs.umich.edu        break;
1627191Sgblack@eecs.umich.edu      case kmiClkDiv:
1637191Sgblack@eecs.umich.edu        clkdiv = pkt->get<uint8_t>();
1647191Sgblack@eecs.umich.edu        break;
1657191Sgblack@eecs.umich.edu      default:
1667191Sgblack@eecs.umich.edu        warn("Tried to write PL050 at offset %#x that doesn't exist\n", daddr);
1677191Sgblack@eecs.umich.edu        break;
1687293Sgblack@eecs.umich.edu    }
1697293Sgblack@eecs.umich.edu    pkt->makeAtomicResponse();
1707293Sgblack@eecs.umich.edu    return pioDelay;
1717293Sgblack@eecs.umich.edu}
1727293Sgblack@eecs.umich.edu
1737293Sgblack@eecs.umich.eduvoid
1747293Sgblack@eecs.umich.eduPl050::processCommand(uint8_t byte)
1757293Sgblack@eecs.umich.edu{
1767293Sgblack@eecs.umich.edu    using namespace Ps2;
1777293Sgblack@eecs.umich.edu
1787293Sgblack@eecs.umich.edu    if (ackNext) {
1797293Sgblack@eecs.umich.edu        ackNext--;
1807293Sgblack@eecs.umich.edu        rxQueue.push_back(Ack);
1817293Sgblack@eecs.umich.edu        updateIntStatus();
1827293Sgblack@eecs.umich.edu        return;
1837293Sgblack@eecs.umich.edu    }
1847293Sgblack@eecs.umich.edu
1857293Sgblack@eecs.umich.edu    switch (byte) {
1867293Sgblack@eecs.umich.edu      case Ps2Reset:
1877293Sgblack@eecs.umich.edu        rxQueue.push_back(Ack);
1887293Sgblack@eecs.umich.edu        rxQueue.push_back(SelfTestPass);
1897293Sgblack@eecs.umich.edu        break;
1907293Sgblack@eecs.umich.edu      case SetResolution:
1917293Sgblack@eecs.umich.edu      case SetRate:
1927293Sgblack@eecs.umich.edu      case SetStatusLed:
1937293Sgblack@eecs.umich.edu      case SetScaling1_1:
1947293Sgblack@eecs.umich.edu      case SetScaling1_2:
1957191Sgblack@eecs.umich.edu        rxQueue.push_back(Ack);
1967191Sgblack@eecs.umich.edu        ackNext = 1;
1977191Sgblack@eecs.umich.edu        break;
1987191Sgblack@eecs.umich.edu      case ReadId:
1997191Sgblack@eecs.umich.edu        rxQueue.push_back(Ack);
2007191Sgblack@eecs.umich.edu        if (params()->is_mouse)
2017191Sgblack@eecs.umich.edu            rxQueue.push_back(MouseId);
2027191Sgblack@eecs.umich.edu        else
2037191Sgblack@eecs.umich.edu            rxQueue.push_back(KeyboardId);
2047191Sgblack@eecs.umich.edu        break;
2057191Sgblack@eecs.umich.edu      case TpReadId:
2067191Sgblack@eecs.umich.edu        if (!params()->is_mouse)
2077191Sgblack@eecs.umich.edu            break;
2087191Sgblack@eecs.umich.edu        // We're not a trackpoint device, this should make the probe go away
2097191Sgblack@eecs.umich.edu        rxQueue.push_back(Ack);
2107191Sgblack@eecs.umich.edu        rxQueue.push_back(0);
2117191Sgblack@eecs.umich.edu        rxQueue.push_back(0);
2127191Sgblack@eecs.umich.edu        // fall through
2137191Sgblack@eecs.umich.edu      case Disable:
2147191Sgblack@eecs.umich.edu      case Enable:
2157191Sgblack@eecs.umich.edu      case SetDefaults:
2167191Sgblack@eecs.umich.edu        rxQueue.push_back(Ack);
2177191Sgblack@eecs.umich.edu        break;
2187191Sgblack@eecs.umich.edu      case StatusRequest:
2197191Sgblack@eecs.umich.edu        rxQueue.push_back(Ack);
2207191Sgblack@eecs.umich.edu        rxQueue.push_back(0);
2217191Sgblack@eecs.umich.edu        rxQueue.push_back(2); // default resolution
2227191Sgblack@eecs.umich.edu        rxQueue.push_back(100); // default sample rate
2237191Sgblack@eecs.umich.edu        break;
2247191Sgblack@eecs.umich.edu      case TouchKitId:
2257191Sgblack@eecs.umich.edu        ackNext = 2;
2267191Sgblack@eecs.umich.edu        rxQueue.push_back(Ack);
2277191Sgblack@eecs.umich.edu        rxQueue.push_back(TouchKitId);
2287191Sgblack@eecs.umich.edu        rxQueue.push_back(1);
2297191Sgblack@eecs.umich.edu        rxQueue.push_back('A');
2307191Sgblack@eecs.umich.edu
2317191Sgblack@eecs.umich.edu        driverInitialized = true;
2327191Sgblack@eecs.umich.edu        break;
2337191Sgblack@eecs.umich.edu      default:
2347191Sgblack@eecs.umich.edu        panic("Unknown byte received: %d\n", byte);
2357191Sgblack@eecs.umich.edu    }
2367191Sgblack@eecs.umich.edu
2377191Sgblack@eecs.umich.edu    updateIntStatus();
2387191Sgblack@eecs.umich.edu}
2397191Sgblack@eecs.umich.edu
2407191Sgblack@eecs.umich.edu
2417191Sgblack@eecs.umich.eduvoid
2427191Sgblack@eecs.umich.eduPl050::updateIntStatus()
2437191Sgblack@eecs.umich.edu{
2447191Sgblack@eecs.umich.edu    if (!rxQueue.empty())
2457191Sgblack@eecs.umich.edu        rawInterrupts.rx = 1;
2467191Sgblack@eecs.umich.edu    else
2477191Sgblack@eecs.umich.edu        rawInterrupts.rx = 0;
2487191Sgblack@eecs.umich.edu
2497192Sgblack@eecs.umich.edu    interrupts.tx = rawInterrupts.tx & control.txint_enable;
2507192Sgblack@eecs.umich.edu    interrupts.rx = rawInterrupts.rx & control.rxint_enable;
2517192Sgblack@eecs.umich.edu
2527192Sgblack@eecs.umich.edu    DPRINTF(Pl050, "rawInterupts=%#x control=%#x interrupts=%#x\n",
2537192Sgblack@eecs.umich.edu            (uint32_t)rawInterrupts, (uint32_t)control, (uint32_t)interrupts);
2547192Sgblack@eecs.umich.edu
2557192Sgblack@eecs.umich.edu    if (interrupts && !intEvent.scheduled())
2567192Sgblack@eecs.umich.edu        schedule(intEvent, curTick() + intDelay);
2577192Sgblack@eecs.umich.edu}
2587192Sgblack@eecs.umich.edu
2597192Sgblack@eecs.umich.eduvoid
2607192Sgblack@eecs.umich.eduPl050::generateInterrupt()
2617293Sgblack@eecs.umich.edu{
2627293Sgblack@eecs.umich.edu
2637293Sgblack@eecs.umich.edu    if (interrupts) {
2647293Sgblack@eecs.umich.edu        gic->sendInt(intNum);
2657293Sgblack@eecs.umich.edu        DPRINTF(Pl050, "Generated interrupt\n");
2667293Sgblack@eecs.umich.edu    }
2677293Sgblack@eecs.umich.edu}
2687293Sgblack@eecs.umich.edu
2697293Sgblack@eecs.umich.eduvoid
2707192Sgblack@eecs.umich.eduPl050::mouseAt(uint16_t x, uint16_t y, uint8_t buttons)
2717191Sgblack@eecs.umich.edu{
272    using namespace Ps2;
273
274    // If the driver hasn't initialized the device yet, no need to try and send
275    // it anything. Similarly we can get vnc mouse events orders of maginture
276    // faster than m5 can process them. Only queue up two sets mouse movements
277    // and don't add more until those are processed.
278    if (!driverInitialized || rxQueue.size() > 10)
279        return;
280
281    // We shouldn't be here unless a vnc server called us in which case
282    // we should have a pointer to it
283    assert(vnc);
284
285    // Convert screen coordinates to touchpad coordinates
286    uint16_t _x = (2047.0/vnc->videoWidth()) * x;
287    uint16_t _y = (2047.0/vnc->videoHeight()) * y;
288
289    rxQueue.push_back(buttons);
290    rxQueue.push_back(_x >> 7);
291    rxQueue.push_back(_x & 0x7f);
292    rxQueue.push_back(_y >> 7);
293    rxQueue.push_back(_y & 0x7f);
294
295    updateIntStatus();
296}
297
298
299void
300Pl050::keyPress(uint32_t key, bool down)
301{
302    using namespace Ps2;
303
304    std::list<uint8_t> keys;
305
306    // convert the X11 keysym into ps2 codes
307    keySymToPs2(key, down, shiftDown, keys);
308
309    // Insert into our queue of charecters
310    rxQueue.splice(rxQueue.end(), keys);
311    updateIntStatus();
312}
313
314void
315Pl050::serialize(std::ostream &os)
316{
317    uint8_t ctrlreg = control;
318    SERIALIZE_SCALAR(ctrlreg);
319
320    uint8_t stsreg = status;
321    SERIALIZE_SCALAR(stsreg);
322    SERIALIZE_SCALAR(clkdiv);
323
324    uint8_t ints = interrupts;
325    SERIALIZE_SCALAR(ints);
326
327    uint8_t raw_ints = rawInterrupts;
328    SERIALIZE_SCALAR(raw_ints);
329
330    SERIALIZE_SCALAR(ackNext);
331    SERIALIZE_SCALAR(shiftDown);
332    SERIALIZE_SCALAR(driverInitialized);
333
334    arrayParamOut(os, "rxQueue", rxQueue);
335}
336
337void
338Pl050::unserialize(Checkpoint *cp, const std::string &section)
339{
340    uint8_t ctrlreg;
341    UNSERIALIZE_SCALAR(ctrlreg);
342    control = ctrlreg;
343
344    uint8_t stsreg;
345    UNSERIALIZE_SCALAR(stsreg);
346    status = stsreg;
347
348    UNSERIALIZE_SCALAR(clkdiv);
349
350    uint8_t ints;
351    UNSERIALIZE_SCALAR(ints);
352    interrupts = ints;
353
354    uint8_t raw_ints;
355    UNSERIALIZE_SCALAR(raw_ints);
356    rawInterrupts = raw_ints;
357
358    UNSERIALIZE_SCALAR(ackNext);
359    UNSERIALIZE_SCALAR(shiftDown);
360    UNSERIALIZE_SCALAR(driverInitialized);
361
362    arrayParamIn(cp, section, "rxQueue", rxQueue);
363}
364
365
366
367Pl050 *
368Pl050Params::create()
369{
370    return new Pl050(this);
371}
372