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