qemu/hw/acpi/tco.c
<<
>>
Prefs
   1/*
   2 * QEMU ICH9 TCO emulation
   3 *
   4 * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
   5 *
   6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
   7 * See the COPYING file in the top-level directory.
   8 */
   9
  10#include "qemu/osdep.h"
  11#include "sysemu/watchdog.h"
  12#include "hw/i386/ich9.h"
  13#include "migration/vmstate.h"
  14
  15#include "hw/acpi/tco.h"
  16#include "trace.h"
  17
  18enum {
  19    TCO_RLD_DEFAULT         = 0x0000,
  20    TCO_DAT_IN_DEFAULT      = 0x00,
  21    TCO_DAT_OUT_DEFAULT     = 0x00,
  22    TCO1_STS_DEFAULT        = 0x0000,
  23    TCO2_STS_DEFAULT        = 0x0000,
  24    TCO1_CNT_DEFAULT        = 0x0000,
  25    TCO2_CNT_DEFAULT        = 0x0008,
  26    TCO_MESSAGE1_DEFAULT    = 0x00,
  27    TCO_MESSAGE2_DEFAULT    = 0x00,
  28    TCO_WDCNT_DEFAULT       = 0x00,
  29    TCO_TMR_DEFAULT         = 0x0004,
  30    SW_IRQ_GEN_DEFAULT      = 0x03,
  31};
  32
  33static inline void tco_timer_reload(TCOIORegs *tr)
  34{
  35    int ticks = tr->tco.tmr & TCO_TMR_MASK;
  36    int64_t nsec = (int64_t)ticks * TCO_TICK_NSEC;
  37
  38    trace_tco_timer_reload(ticks, nsec / 1000000);
  39    tr->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + nsec;
  40    timer_mod(tr->tco_timer, tr->expire_time);
  41}
  42
  43static inline void tco_timer_stop(TCOIORegs *tr)
  44{
  45    tr->expire_time = -1;
  46    timer_del(tr->tco_timer);
  47}
  48
  49static void tco_timer_expired(void *opaque)
  50{
  51    TCOIORegs *tr = opaque;
  52    ICH9LPCPMRegs *pm = container_of(tr, ICH9LPCPMRegs, tco_regs);
  53    ICH9LPCState *lpc = container_of(pm, ICH9LPCState, pm);
  54    uint32_t gcs = pci_get_long(lpc->chip_config + ICH9_CC_GCS);
  55
  56    trace_tco_timer_expired(tr->timeouts_no,
  57                            lpc->pin_strap.spkr_hi,
  58                            !!(gcs & ICH9_CC_GCS_NO_REBOOT));
  59    tr->tco.rld = 0;
  60    tr->tco.sts1 |= TCO_TIMEOUT;
  61    if (++tr->timeouts_no == 2) {
  62        tr->tco.sts2 |= TCO_SECOND_TO_STS;
  63        tr->tco.sts2 |= TCO_BOOT_STS;
  64        tr->timeouts_no = 0;
  65
  66        if (!lpc->pin_strap.spkr_hi && !(gcs & ICH9_CC_GCS_NO_REBOOT)) {
  67            watchdog_perform_action();
  68            tco_timer_stop(tr);
  69            return;
  70        }
  71    }
  72
  73    if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) {
  74        ich9_generate_smi();
  75    }
  76    tr->tco.rld = tr->tco.tmr;
  77    tco_timer_reload(tr);
  78}
  79
  80/* NOTE: values of 0 or 1 will be ignored by ICH */
  81static inline int can_start_tco_timer(TCOIORegs *tr)
  82{
  83    return !(tr->tco.cnt1 & TCO_TMR_HLT) && tr->tco.tmr > 1;
  84}
  85
  86static uint32_t tco_ioport_readw(TCOIORegs *tr, uint32_t addr)
  87{
  88    uint16_t rld;
  89
  90    switch (addr) {
  91    case TCO_RLD:
  92        if (tr->expire_time != -1) {
  93            int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
  94            int64_t elapsed = (tr->expire_time - now) / TCO_TICK_NSEC;
  95            rld = (uint16_t)elapsed | (tr->tco.rld & ~TCO_RLD_MASK);
  96        } else {
  97            rld = tr->tco.rld;
  98        }
  99        return rld;
 100    case TCO_DAT_IN:
 101        return tr->tco.din;
 102    case TCO_DAT_OUT:
 103        return tr->tco.dout;
 104    case TCO1_STS:
 105        return tr->tco.sts1;
 106    case TCO2_STS:
 107        return tr->tco.sts2;
 108    case TCO1_CNT:
 109        return tr->tco.cnt1;
 110    case TCO2_CNT:
 111        return tr->tco.cnt2;
 112    case TCO_MESSAGE1:
 113        return tr->tco.msg1;
 114    case TCO_MESSAGE2:
 115        return tr->tco.msg2;
 116    case TCO_WDCNT:
 117        return tr->tco.wdcnt;
 118    case TCO_TMR:
 119        return tr->tco.tmr;
 120    case SW_IRQ_GEN:
 121        return tr->sw_irq_gen;
 122    }
 123    return 0;
 124}
 125
 126static void tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val)
 127{
 128    switch (addr) {
 129    case TCO_RLD:
 130        tr->timeouts_no = 0;
 131        if (can_start_tco_timer(tr)) {
 132            tr->tco.rld = tr->tco.tmr;
 133            tco_timer_reload(tr);
 134        } else {
 135            tr->tco.rld = val;
 136        }
 137        break;
 138    case TCO_DAT_IN:
 139        tr->tco.din = val;
 140        tr->tco.sts1 |= SW_TCO_SMI;
 141        ich9_generate_smi();
 142        break;
 143    case TCO_DAT_OUT:
 144        tr->tco.dout = val;
 145        tr->tco.sts1 |= TCO_INT_STS;
 146        /* TODO: cause an interrupt, as selected by the TCO_INT_SEL bits */
 147        break;
 148    case TCO1_STS:
 149        tr->tco.sts1 = val & TCO1_STS_MASK;
 150        break;
 151    case TCO2_STS:
 152        tr->tco.sts2 = val & TCO2_STS_MASK;
 153        break;
 154    case TCO1_CNT:
 155        val &= TCO1_CNT_MASK;
 156        /*
 157         * once TCO_LOCK bit is set, it can not be cleared by software. a reset
 158         * is required to change this bit from 1 to 0 -- it defaults to 0.
 159         */
 160        tr->tco.cnt1 = val | (tr->tco.cnt1 & TCO_LOCK);
 161        if (can_start_tco_timer(tr)) {
 162            tr->tco.rld = tr->tco.tmr;
 163            tco_timer_reload(tr);
 164        } else {
 165            tco_timer_stop(tr);
 166        }
 167        break;
 168    case TCO2_CNT:
 169        tr->tco.cnt2 = val;
 170        break;
 171    case TCO_MESSAGE1:
 172        tr->tco.msg1 = val;
 173        break;
 174    case TCO_MESSAGE2:
 175        tr->tco.msg2 = val;
 176        break;
 177    case TCO_WDCNT:
 178        tr->tco.wdcnt = val;
 179        break;
 180    case TCO_TMR:
 181        tr->tco.tmr = val;
 182        break;
 183    case SW_IRQ_GEN:
 184        tr->sw_irq_gen = val;
 185        break;
 186    }
 187}
 188
 189static uint64_t tco_io_readw(void *opaque, hwaddr addr, unsigned width)
 190{
 191    TCOIORegs *tr = opaque;
 192    return tco_ioport_readw(tr, addr);
 193}
 194
 195static void tco_io_writew(void *opaque, hwaddr addr, uint64_t val,
 196                          unsigned width)
 197{
 198    TCOIORegs *tr = opaque;
 199    tco_ioport_writew(tr, addr, val);
 200}
 201
 202static const MemoryRegionOps tco_io_ops = {
 203    .read = tco_io_readw,
 204    .write = tco_io_writew,
 205    .valid.min_access_size = 1,
 206    .valid.max_access_size = 4,
 207    .impl.min_access_size = 1,
 208    .impl.max_access_size = 2,
 209    .endianness = DEVICE_LITTLE_ENDIAN,
 210};
 211
 212void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent)
 213{
 214    *tr = (TCOIORegs) {
 215        .tco = {
 216            .rld      = TCO_RLD_DEFAULT,
 217            .din      = TCO_DAT_IN_DEFAULT,
 218            .dout     = TCO_DAT_OUT_DEFAULT,
 219            .sts1     = TCO1_STS_DEFAULT,
 220            .sts2     = TCO2_STS_DEFAULT,
 221            .cnt1     = TCO1_CNT_DEFAULT,
 222            .cnt2     = TCO2_CNT_DEFAULT,
 223            .msg1     = TCO_MESSAGE1_DEFAULT,
 224            .msg2     = TCO_MESSAGE2_DEFAULT,
 225            .wdcnt    = TCO_WDCNT_DEFAULT,
 226            .tmr      = TCO_TMR_DEFAULT,
 227        },
 228        .sw_irq_gen    = SW_IRQ_GEN_DEFAULT,
 229        .tco_timer     = timer_new_ns(QEMU_CLOCK_VIRTUAL, tco_timer_expired, tr),
 230        .expire_time   = -1,
 231        .timeouts_no   = 0,
 232    };
 233    memory_region_init_io(&tr->io, memory_region_owner(parent),
 234                          &tco_io_ops, tr, "sm-tco", ICH9_PMIO_TCO_LEN);
 235    memory_region_add_subregion(parent, ICH9_PMIO_TCO_RLD, &tr->io);
 236}
 237
 238const VMStateDescription vmstate_tco_io_sts = {
 239    .name = "tco io device status",
 240    .version_id = 1,
 241    .minimum_version_id = 1,
 242    .minimum_version_id_old = 1,
 243    .fields      = (VMStateField[]) {
 244        VMSTATE_UINT16(tco.rld, TCOIORegs),
 245        VMSTATE_UINT8(tco.din, TCOIORegs),
 246        VMSTATE_UINT8(tco.dout, TCOIORegs),
 247        VMSTATE_UINT16(tco.sts1, TCOIORegs),
 248        VMSTATE_UINT16(tco.sts2, TCOIORegs),
 249        VMSTATE_UINT16(tco.cnt1, TCOIORegs),
 250        VMSTATE_UINT16(tco.cnt2, TCOIORegs),
 251        VMSTATE_UINT8(tco.msg1, TCOIORegs),
 252        VMSTATE_UINT8(tco.msg2, TCOIORegs),
 253        VMSTATE_UINT8(tco.wdcnt, TCOIORegs),
 254        VMSTATE_UINT16(tco.tmr, TCOIORegs),
 255        VMSTATE_UINT8(sw_irq_gen, TCOIORegs),
 256        VMSTATE_TIMER_PTR(tco_timer, TCOIORegs),
 257        VMSTATE_INT64(expire_time, TCOIORegs),
 258        VMSTATE_UINT8(timeouts_no, TCOIORegs),
 259        VMSTATE_END_OF_LIST()
 260    }
 261};
 262