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