qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

tco.c (7514B)


      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 
     18 enum {
     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 
     33 static 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 
     43 static inline void tco_timer_stop(TCOIORegs *tr)
     44 {
     45     tr->expire_time = -1;
     46     timer_del(tr->tco_timer);
     47 }
     48 
     49 static 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 */
     81 static inline int can_start_tco_timer(TCOIORegs *tr)
     82 {
     83     return !(tr->tco.cnt1 & TCO_TMR_HLT) && tr->tco.tmr > 1;
     84 }
     85 
     86 static 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 
    126 static 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 
    189 static 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 
    195 static 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 
    202 static 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 
    212 void 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 
    238 const VMStateDescription vmstate_tco_io_sts = {
    239     .name = "tco io device status",
    240     .version_id = 1,
    241     .minimum_version_id = 1,
    242     .fields      = (VMStateField[]) {
    243         VMSTATE_UINT16(tco.rld, TCOIORegs),
    244         VMSTATE_UINT8(tco.din, TCOIORegs),
    245         VMSTATE_UINT8(tco.dout, TCOIORegs),
    246         VMSTATE_UINT16(tco.sts1, TCOIORegs),
    247         VMSTATE_UINT16(tco.sts2, TCOIORegs),
    248         VMSTATE_UINT16(tco.cnt1, TCOIORegs),
    249         VMSTATE_UINT16(tco.cnt2, TCOIORegs),
    250         VMSTATE_UINT8(tco.msg1, TCOIORegs),
    251         VMSTATE_UINT8(tco.msg2, TCOIORegs),
    252         VMSTATE_UINT8(tco.wdcnt, TCOIORegs),
    253         VMSTATE_UINT16(tco.tmr, TCOIORegs),
    254         VMSTATE_UINT8(sw_irq_gen, TCOIORegs),
    255         VMSTATE_TIMER_PTR(tco_timer, TCOIORegs),
    256         VMSTATE_INT64(expire_time, TCOIORegs),
    257         VMSTATE_UINT8(timeouts_no, TCOIORegs),
    258         VMSTATE_END_OF_LIST()
    259     }
    260 };