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