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 
tco_timer_reload(TCOIORegs * tr)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 
tco_timer_stop(TCOIORegs * tr)43 static inline void tco_timer_stop(TCOIORegs *tr)
44 {
45     tr->expire_time = -1;
46     timer_del(tr->tco_timer);
47 }
48 
tco_timer_expired(void * opaque)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 */
can_start_tco_timer(TCOIORegs * tr)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 
tco_ioport_readw(TCOIORegs * tr,uint32_t addr)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 
tco_ioport_writew(TCOIORegs * tr,uint32_t addr,uint32_t val)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 
tco_io_readw(void * opaque,hwaddr addr,unsigned width)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 
tco_io_writew(void * opaque,hwaddr addr,uint64_t val,unsigned width)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 
acpi_pm_tco_init(TCOIORegs * tr,MemoryRegion * parent)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     .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