xref: /qemu/target/ppc/timebase_helper.c (revision 7cef6d686309e2792186504ae17cf4f3eb57ef68)
16de673d4SBlue Swirl /*
26de673d4SBlue Swirl  *  PowerPC emulation helpers for QEMU.
36de673d4SBlue Swirl  *
46de673d4SBlue Swirl  *  Copyright (c) 2003-2007 Jocelyn Mayer
56de673d4SBlue Swirl  *
66de673d4SBlue Swirl  * This library is free software; you can redistribute it and/or
76de673d4SBlue Swirl  * modify it under the terms of the GNU Lesser General Public
86de673d4SBlue Swirl  * License as published by the Free Software Foundation; either
96bd039cdSChetan Pant  * version 2.1 of the License, or (at your option) any later version.
106de673d4SBlue Swirl  *
116de673d4SBlue Swirl  * This library is distributed in the hope that it will be useful,
126de673d4SBlue Swirl  * but WITHOUT ANY WARRANTY; without even the implied warranty of
136de673d4SBlue Swirl  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
146de673d4SBlue Swirl  * Lesser General Public License for more details.
156de673d4SBlue Swirl  *
166de673d4SBlue Swirl  * You should have received a copy of the GNU Lesser General Public
176de673d4SBlue Swirl  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
186de673d4SBlue Swirl  */
190d75590dSPeter Maydell #include "qemu/osdep.h"
206de673d4SBlue Swirl #include "cpu.h"
21d8c14411SNicholas Piggin #include "hw/ppc/ppc.h"
222ef6175aSRichard Henderson #include "exec/helper-proto.h"
2363c91552SPaolo Bonzini #include "qemu/log.h"
24235352eeSPeter Maydell #include "qemu/main-loop.h"
256de673d4SBlue Swirl 
266de673d4SBlue Swirl /*****************************************************************************/
276de673d4SBlue Swirl /* SPR accesses */
286de673d4SBlue Swirl 
helper_load_tbl(CPUPPCState * env)29d0f1562dSBlue Swirl target_ulong helper_load_tbl(CPUPPCState *env)
306de673d4SBlue Swirl {
316de673d4SBlue Swirl     return (target_ulong)cpu_ppc_load_tbl(env);
326de673d4SBlue Swirl }
336de673d4SBlue Swirl 
helper_load_tbu(CPUPPCState * env)34d0f1562dSBlue Swirl target_ulong helper_load_tbu(CPUPPCState *env)
356de673d4SBlue Swirl {
366de673d4SBlue Swirl     return cpu_ppc_load_tbu(env);
376de673d4SBlue Swirl }
386de673d4SBlue Swirl 
helper_load_atbl(CPUPPCState * env)39d0f1562dSBlue Swirl target_ulong helper_load_atbl(CPUPPCState *env)
406de673d4SBlue Swirl {
416de673d4SBlue Swirl     return (target_ulong)cpu_ppc_load_atbl(env);
426de673d4SBlue Swirl }
436de673d4SBlue Swirl 
helper_load_atbu(CPUPPCState * env)44d0f1562dSBlue Swirl target_ulong helper_load_atbu(CPUPPCState *env)
456de673d4SBlue Swirl {
466de673d4SBlue Swirl     return cpu_ppc_load_atbu(env);
476de673d4SBlue Swirl }
486de673d4SBlue Swirl 
helper_load_vtb(CPUPPCState * env)495d62725bSSuraj Jitindar Singh target_ulong helper_load_vtb(CPUPPCState *env)
505d62725bSSuraj Jitindar Singh {
515d62725bSSuraj Jitindar Singh     return cpu_ppc_load_vtb(env);
525d62725bSSuraj Jitindar Singh }
535d62725bSSuraj Jitindar Singh 
546de673d4SBlue Swirl #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
helper_load_purr(CPUPPCState * env)55d0f1562dSBlue Swirl target_ulong helper_load_purr(CPUPPCState *env)
566de673d4SBlue Swirl {
576de673d4SBlue Swirl     return (target_ulong)cpu_ppc_load_purr(env);
586de673d4SBlue Swirl }
595cc7e69fSSuraj Jitindar Singh 
helper_store_purr(CPUPPCState * env,target_ulong val)605cc7e69fSSuraj Jitindar Singh void helper_store_purr(CPUPPCState *env, target_ulong val)
615cc7e69fSSuraj Jitindar Singh {
62a21d89b5SNicholas Piggin     CPUState *cs = env_cpu(env);
63a21d89b5SNicholas Piggin     CPUState *ccs;
64a21d89b5SNicholas Piggin 
6550d8cfb9SNicholas Piggin     if (ppc_cpu_lpar_single_threaded(cs)) {
665cc7e69fSSuraj Jitindar Singh         cpu_ppc_store_purr(env, val);
67a21d89b5SNicholas Piggin         return;
68a21d89b5SNicholas Piggin     }
69a21d89b5SNicholas Piggin 
70a21d89b5SNicholas Piggin     THREAD_SIBLING_FOREACH(cs, ccs) {
71a21d89b5SNicholas Piggin         CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
72a21d89b5SNicholas Piggin         cpu_ppc_store_purr(cenv, val);
73a21d89b5SNicholas Piggin     }
745cc7e69fSSuraj Jitindar Singh }
756de673d4SBlue Swirl #endif
766de673d4SBlue Swirl 
776de673d4SBlue Swirl #if !defined(CONFIG_USER_ONLY)
helper_store_tbl(CPUPPCState * env,target_ulong val)78d0f1562dSBlue Swirl void helper_store_tbl(CPUPPCState *env, target_ulong val)
796de673d4SBlue Swirl {
80a21d89b5SNicholas Piggin     CPUState *cs = env_cpu(env);
81a21d89b5SNicholas Piggin     CPUState *ccs;
82a21d89b5SNicholas Piggin 
8350d8cfb9SNicholas Piggin     if (ppc_cpu_lpar_single_threaded(cs)) {
846de673d4SBlue Swirl         cpu_ppc_store_tbl(env, val);
85a21d89b5SNicholas Piggin         return;
86a21d89b5SNicholas Piggin     }
87a21d89b5SNicholas Piggin 
88a21d89b5SNicholas Piggin     THREAD_SIBLING_FOREACH(cs, ccs) {
89a21d89b5SNicholas Piggin         CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
90a21d89b5SNicholas Piggin         cpu_ppc_store_tbl(cenv, val);
91a21d89b5SNicholas Piggin     }
926de673d4SBlue Swirl }
936de673d4SBlue Swirl 
helper_store_tbu(CPUPPCState * env,target_ulong val)94d0f1562dSBlue Swirl void helper_store_tbu(CPUPPCState *env, target_ulong val)
956de673d4SBlue Swirl {
96a21d89b5SNicholas Piggin     CPUState *cs = env_cpu(env);
97a21d89b5SNicholas Piggin     CPUState *ccs;
98a21d89b5SNicholas Piggin 
9950d8cfb9SNicholas Piggin     if (ppc_cpu_lpar_single_threaded(cs)) {
1006de673d4SBlue Swirl         cpu_ppc_store_tbu(env, val);
101a21d89b5SNicholas Piggin         return;
102a21d89b5SNicholas Piggin     }
103a21d89b5SNicholas Piggin 
104a21d89b5SNicholas Piggin     THREAD_SIBLING_FOREACH(cs, ccs) {
105a21d89b5SNicholas Piggin         CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
106a21d89b5SNicholas Piggin         cpu_ppc_store_tbu(cenv, val);
107a21d89b5SNicholas Piggin     }
1086de673d4SBlue Swirl }
1096de673d4SBlue Swirl 
helper_store_atbl(CPUPPCState * env,target_ulong val)110d0f1562dSBlue Swirl void helper_store_atbl(CPUPPCState *env, target_ulong val)
1116de673d4SBlue Swirl {
1126de673d4SBlue Swirl     cpu_ppc_store_atbl(env, val);
1136de673d4SBlue Swirl }
1146de673d4SBlue Swirl 
helper_store_atbu(CPUPPCState * env,target_ulong val)115d0f1562dSBlue Swirl void helper_store_atbu(CPUPPCState *env, target_ulong val)
1166de673d4SBlue Swirl {
1176de673d4SBlue Swirl     cpu_ppc_store_atbu(env, val);
1186de673d4SBlue Swirl }
1196de673d4SBlue Swirl 
helper_load_decr(CPUPPCState * env)120d0f1562dSBlue Swirl target_ulong helper_load_decr(CPUPPCState *env)
1216de673d4SBlue Swirl {
1226de673d4SBlue Swirl     return cpu_ppc_load_decr(env);
1236de673d4SBlue Swirl }
1246de673d4SBlue Swirl 
helper_store_decr(CPUPPCState * env,target_ulong val)125d0f1562dSBlue Swirl void helper_store_decr(CPUPPCState *env, target_ulong val)
1266de673d4SBlue Swirl {
1276de673d4SBlue Swirl     cpu_ppc_store_decr(env, val);
1286de673d4SBlue Swirl }
1296de673d4SBlue Swirl 
helper_load_hdecr(CPUPPCState * env)1304b236b62SBenjamin Herrenschmidt target_ulong helper_load_hdecr(CPUPPCState *env)
1314b236b62SBenjamin Herrenschmidt {
1324b236b62SBenjamin Herrenschmidt     return cpu_ppc_load_hdecr(env);
1334b236b62SBenjamin Herrenschmidt }
1344b236b62SBenjamin Herrenschmidt 
helper_store_hdecr(CPUPPCState * env,target_ulong val)1354b236b62SBenjamin Herrenschmidt void helper_store_hdecr(CPUPPCState *env, target_ulong val)
1364b236b62SBenjamin Herrenschmidt {
137a21d89b5SNicholas Piggin     CPUState *cs = env_cpu(env);
138a21d89b5SNicholas Piggin     CPUState *ccs;
139a21d89b5SNicholas Piggin 
14050d8cfb9SNicholas Piggin     if (ppc_cpu_lpar_single_threaded(cs)) {
1414b236b62SBenjamin Herrenschmidt         cpu_ppc_store_hdecr(env, val);
142a21d89b5SNicholas Piggin         return;
143a21d89b5SNicholas Piggin     }
144a21d89b5SNicholas Piggin 
145a21d89b5SNicholas Piggin     THREAD_SIBLING_FOREACH(cs, ccs) {
146a21d89b5SNicholas Piggin         CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
147a21d89b5SNicholas Piggin         cpu_ppc_store_hdecr(cenv, val);
148a21d89b5SNicholas Piggin     }
1494b236b62SBenjamin Herrenschmidt }
1504b236b62SBenjamin Herrenschmidt 
helper_store_vtb(CPUPPCState * env,target_ulong val)1515d62725bSSuraj Jitindar Singh void helper_store_vtb(CPUPPCState *env, target_ulong val)
1525d62725bSSuraj Jitindar Singh {
153a21d89b5SNicholas Piggin     CPUState *cs = env_cpu(env);
154a21d89b5SNicholas Piggin     CPUState *ccs;
155a21d89b5SNicholas Piggin 
15650d8cfb9SNicholas Piggin     if (ppc_cpu_lpar_single_threaded(cs)) {
1575d62725bSSuraj Jitindar Singh         cpu_ppc_store_vtb(env, val);
158a21d89b5SNicholas Piggin         return;
159a21d89b5SNicholas Piggin     }
160a21d89b5SNicholas Piggin 
161a21d89b5SNicholas Piggin     THREAD_SIBLING_FOREACH(cs, ccs) {
162a21d89b5SNicholas Piggin         CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
163a21d89b5SNicholas Piggin         cpu_ppc_store_vtb(cenv, val);
164a21d89b5SNicholas Piggin     }
1655d62725bSSuraj Jitindar Singh }
1665d62725bSSuraj Jitindar Singh 
helper_store_tbu40(CPUPPCState * env,target_ulong val)167f0ec31b1SSuraj Jitindar Singh void helper_store_tbu40(CPUPPCState *env, target_ulong val)
168f0ec31b1SSuraj Jitindar Singh {
169a21d89b5SNicholas Piggin     CPUState *cs = env_cpu(env);
170a21d89b5SNicholas Piggin     CPUState *ccs;
171a21d89b5SNicholas Piggin 
17250d8cfb9SNicholas Piggin     if (ppc_cpu_lpar_single_threaded(cs)) {
173f0ec31b1SSuraj Jitindar Singh         cpu_ppc_store_tbu40(env, val);
174a21d89b5SNicholas Piggin         return;
175a21d89b5SNicholas Piggin     }
176a21d89b5SNicholas Piggin 
177a21d89b5SNicholas Piggin     THREAD_SIBLING_FOREACH(cs, ccs) {
178a21d89b5SNicholas Piggin         CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
179a21d89b5SNicholas Piggin         cpu_ppc_store_tbu40(cenv, val);
180a21d89b5SNicholas Piggin     }
181f0ec31b1SSuraj Jitindar Singh }
182f0ec31b1SSuraj Jitindar Singh 
helper_load_40x_pit(CPUPPCState * env)183d0f1562dSBlue Swirl target_ulong helper_load_40x_pit(CPUPPCState *env)
1846de673d4SBlue Swirl {
1856de673d4SBlue Swirl     return load_40x_pit(env);
1866de673d4SBlue Swirl }
1876de673d4SBlue Swirl 
helper_store_40x_pit(CPUPPCState * env,target_ulong val)188d0f1562dSBlue Swirl void helper_store_40x_pit(CPUPPCState *env, target_ulong val)
1896de673d4SBlue Swirl {
1906de673d4SBlue Swirl     store_40x_pit(env, val);
1916de673d4SBlue Swirl }
1926de673d4SBlue Swirl 
helper_store_40x_tcr(CPUPPCState * env,target_ulong val)193cbd8f17dSCédric Le Goater void helper_store_40x_tcr(CPUPPCState *env, target_ulong val)
194cbd8f17dSCédric Le Goater {
195cbd8f17dSCédric Le Goater     store_40x_tcr(env, val);
196cbd8f17dSCédric Le Goater }
197cbd8f17dSCédric Le Goater 
helper_store_40x_tsr(CPUPPCState * env,target_ulong val)198cbd8f17dSCédric Le Goater void helper_store_40x_tsr(CPUPPCState *env, target_ulong val)
199cbd8f17dSCédric Le Goater {
200cbd8f17dSCédric Le Goater     store_40x_tsr(env, val);
201cbd8f17dSCédric Le Goater }
202cbd8f17dSCédric Le Goater 
helper_store_booke_tcr(CPUPPCState * env,target_ulong val)203d0f1562dSBlue Swirl void helper_store_booke_tcr(CPUPPCState *env, target_ulong val)
2046de673d4SBlue Swirl {
2056de673d4SBlue Swirl     store_booke_tcr(env, val);
2066de673d4SBlue Swirl }
2076de673d4SBlue Swirl 
helper_store_booke_tsr(CPUPPCState * env,target_ulong val)208d0f1562dSBlue Swirl void helper_store_booke_tsr(CPUPPCState *env, target_ulong val)
2096de673d4SBlue Swirl {
2106de673d4SBlue Swirl     store_booke_tsr(env, val);
2116de673d4SBlue Swirl }
2126de673d4SBlue Swirl 
2130ca94b2fSNicholas Piggin #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
2140ca94b2fSNicholas Piggin /*
2150ca94b2fSNicholas Piggin  * qemu-user breaks with pnv headers, so they go under ifdefs for now.
2160ca94b2fSNicholas Piggin  * A clean up may be to move powernv specific registers and helpers into
2170ca94b2fSNicholas Piggin  * target/ppc/pnv_helper.c
2180ca94b2fSNicholas Piggin  */
2190ca94b2fSNicholas Piggin #include "hw/ppc/pnv_core.h"
220*78be3218SNicholas Piggin #include "hw/ppc/pnv_chip.h"
221d8c14411SNicholas Piggin /*
222d8c14411SNicholas Piggin  * POWER processor Timebase Facility
223d8c14411SNicholas Piggin  */
224d8c14411SNicholas Piggin 
225d8c14411SNicholas Piggin /*
226d8c14411SNicholas Piggin  * The TBST is the timebase state machine, which is a per-core machine that
227d8c14411SNicholas Piggin  * is used to synchronize the core TB with the ChipTOD. States 3,4,5 are
228d8c14411SNicholas Piggin  * not used in POWER8/9/10.
229d8c14411SNicholas Piggin  *
230d8c14411SNicholas Piggin  * The state machine gets driven by writes to TFMR SPR from the core, and
231d8c14411SNicholas Piggin  * by signals from the ChipTOD. The state machine table for common
232d8c14411SNicholas Piggin  * transitions is as follows (according to hardware specs, not necessarily
233d8c14411SNicholas Piggin  * this implementation):
234d8c14411SNicholas Piggin  *
235d8c14411SNicholas Piggin  * | Cur            | Event                            | New |
236d8c14411SNicholas Piggin  * +----------------+----------------------------------+-----+
237d8c14411SNicholas Piggin  * | 0 RESET        | TFMR |= LOAD_TOD_MOD             | 1   |
238d8c14411SNicholas Piggin  * | 1 SEND_TOD_MOD | "immediate transition"           | 2   |
239d8c14411SNicholas Piggin  * | 2 NOT_SET      | mttbu/mttbu40/mttbl              | 2   |
240d8c14411SNicholas Piggin  * | 2 NOT_SET      | TFMR |= MOVE_CHIP_TOD_TO_TB      | 6   |
241d8c14411SNicholas Piggin  * | 6 SYNC_WAIT    | "sync pulse from ChipTOD"        | 7   |
242d8c14411SNicholas Piggin  * | 7 GET_TOD      | ChipTOD xscom MOVE_TOD_TO_TB_REG | 8   |
243d8c14411SNicholas Piggin  * | 8 TB_RUNNING   | mttbu/mttbu40                    | 8   |
244d8c14411SNicholas Piggin  * | 8 TB_RUNNING   | TFMR |= LOAD_TOD_MOD             | 1   |
245d8c14411SNicholas Piggin  * | 8 TB_RUNNING   | mttbl                            | 9   |
246d8c14411SNicholas Piggin  * | 9 TB_ERROR     | TFMR |= CLEAR_TB_ERRORS          | 0   |
247d8c14411SNicholas Piggin  *
248d8c14411SNicholas Piggin  * - LOAD_TOD_MOD will also move states 2,6 to state 1, omitted from table
249d8c14411SNicholas Piggin  *   because it's not a typical init flow.
250d8c14411SNicholas Piggin  *
251d8c14411SNicholas Piggin  * - The ERROR state can be entered from most/all other states on invalid
252d8c14411SNicholas Piggin  *   states (e.g., if some TFMR control bit is set from a state where it's
253d8c14411SNicholas Piggin  *   not listed to cause a transition away from), omitted to avoid clutter.
254d8c14411SNicholas Piggin  *
255d8c14411SNicholas Piggin  * Note: mttbl causes a timebase error because this inevitably causes
256d8c14411SNicholas Piggin  * ticks to be lost and TB to become unsynchronized, whereas TB can be
257d8c14411SNicholas Piggin  * adjusted using mttbu* without losing ticks. mttbl behaviour is not
258d8c14411SNicholas Piggin  * modelled.
259d8c14411SNicholas Piggin  *
260d8c14411SNicholas Piggin  * Note: the TB state machine does not actually cause any real TB adjustment!
261d8c14411SNicholas Piggin  * TB starts out synchronized across all vCPUs (hardware threads) in
262d8c14411SNicholas Piggin  * QMEU, so for now the purpose of the TBST and ChipTOD model is simply
263d8c14411SNicholas Piggin  * to step through firmware initialisation sequences.
264d8c14411SNicholas Piggin  */
tfmr_get_tb_state(uint64_t tfmr)265d8c14411SNicholas Piggin static unsigned int tfmr_get_tb_state(uint64_t tfmr)
266d8c14411SNicholas Piggin {
267d8c14411SNicholas Piggin     return (tfmr & TFMR_TBST_ENCODED) >> (63 - 31);
268d8c14411SNicholas Piggin }
269d8c14411SNicholas Piggin 
tfmr_new_tb_state(uint64_t tfmr,unsigned int tbst)270d8c14411SNicholas Piggin static uint64_t tfmr_new_tb_state(uint64_t tfmr, unsigned int tbst)
271d8c14411SNicholas Piggin {
272d8c14411SNicholas Piggin     tfmr &= ~TFMR_TBST_LAST;
273d8c14411SNicholas Piggin     tfmr |= (tfmr & TFMR_TBST_ENCODED) >> 4; /* move state to last state */
274d8c14411SNicholas Piggin     tfmr &= ~TFMR_TBST_ENCODED;
275d8c14411SNicholas Piggin     tfmr |= (uint64_t)tbst << (63 - 31); /* move new state to state */
276d8c14411SNicholas Piggin 
277d8c14411SNicholas Piggin     if (tbst == TBST_TB_RUNNING) {
278d8c14411SNicholas Piggin         tfmr |= TFMR_TB_VALID;
279d8c14411SNicholas Piggin     } else {
280d8c14411SNicholas Piggin         tfmr &= ~TFMR_TB_VALID;
281d8c14411SNicholas Piggin     }
282d8c14411SNicholas Piggin 
283d8c14411SNicholas Piggin     return tfmr;
284d8c14411SNicholas Piggin }
285d8c14411SNicholas Piggin 
write_tfmr(CPUPPCState * env,target_ulong val)286a21d89b5SNicholas Piggin static void write_tfmr(CPUPPCState *env, target_ulong val)
287a21d89b5SNicholas Piggin {
288a21d89b5SNicholas Piggin     CPUState *cs = env_cpu(env);
289a21d89b5SNicholas Piggin 
29050d8cfb9SNicholas Piggin     if (ppc_cpu_core_single_threaded(cs)) {
291a21d89b5SNicholas Piggin         env->spr[SPR_TFMR] = val;
292a21d89b5SNicholas Piggin     } else {
293a21d89b5SNicholas Piggin         CPUState *ccs;
294a21d89b5SNicholas Piggin         THREAD_SIBLING_FOREACH(cs, ccs) {
295a21d89b5SNicholas Piggin             CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
296a21d89b5SNicholas Piggin             cenv->spr[SPR_TFMR] = val;
297a21d89b5SNicholas Piggin         }
298a21d89b5SNicholas Piggin     }
299a21d89b5SNicholas Piggin }
300a21d89b5SNicholas Piggin 
cpu_get_tbst(PowerPCCPU * cpu)3010ca94b2fSNicholas Piggin static PnvCoreTODState *cpu_get_tbst(PowerPCCPU *cpu)
3020ca94b2fSNicholas Piggin {
3030ca94b2fSNicholas Piggin     PnvCore *pc = pnv_cpu_state(cpu)->pnv_core;
3040ca94b2fSNicholas Piggin 
305*78be3218SNicholas Piggin     if (pc->big_core && pc->tod_state.big_core_quirk) {
306*78be3218SNicholas Piggin         /* Must operate on the even small core */
307*78be3218SNicholas Piggin         int core_id = CPU_CORE(pc)->core_id;
308*78be3218SNicholas Piggin         if (core_id & 1) {
309*78be3218SNicholas Piggin             pc = pc->chip->cores[core_id & ~1];
310*78be3218SNicholas Piggin         }
311*78be3218SNicholas Piggin     }
312*78be3218SNicholas Piggin 
3130ca94b2fSNicholas Piggin     return &pc->tod_state;
3140ca94b2fSNicholas Piggin }
3150ca94b2fSNicholas Piggin 
tb_state_machine_step(CPUPPCState * env)316d8c14411SNicholas Piggin static void tb_state_machine_step(CPUPPCState *env)
317d8c14411SNicholas Piggin {
3180ca94b2fSNicholas Piggin     PowerPCCPU *cpu = env_archcpu(env);
3190ca94b2fSNicholas Piggin     PnvCoreTODState *tod_state = cpu_get_tbst(cpu);
320d8c14411SNicholas Piggin     uint64_t tfmr = env->spr[SPR_TFMR];
321d8c14411SNicholas Piggin     unsigned int tbst = tfmr_get_tb_state(tfmr);
322d8c14411SNicholas Piggin 
323d8c14411SNicholas Piggin     if (!(tfmr & TFMR_TB_ECLIPZ) || tbst == TBST_TB_ERROR) {
324d8c14411SNicholas Piggin         return;
325d8c14411SNicholas Piggin     }
326d8c14411SNicholas Piggin 
3270ca94b2fSNicholas Piggin     if (tod_state->tb_sync_pulse_timer) {
3280ca94b2fSNicholas Piggin         tod_state->tb_sync_pulse_timer--;
329d8c14411SNicholas Piggin     } else {
330d8c14411SNicholas Piggin         tfmr |= TFMR_TB_SYNC_OCCURED;
331a21d89b5SNicholas Piggin         write_tfmr(env, tfmr);
332d8c14411SNicholas Piggin     }
333d8c14411SNicholas Piggin 
3340ca94b2fSNicholas Piggin     if (tod_state->tb_state_timer) {
3350ca94b2fSNicholas Piggin         tod_state->tb_state_timer--;
336d8c14411SNicholas Piggin         return;
337d8c14411SNicholas Piggin     }
338d8c14411SNicholas Piggin 
339d8c14411SNicholas Piggin     if (tfmr & TFMR_LOAD_TOD_MOD) {
340d8c14411SNicholas Piggin         tfmr &= ~TFMR_LOAD_TOD_MOD;
341d8c14411SNicholas Piggin         if (tbst == TBST_GET_TOD) {
342d8c14411SNicholas Piggin             tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR);
343d8c14411SNicholas Piggin             tfmr |= TFMR_FIRMWARE_CONTROL_ERROR;
344d8c14411SNicholas Piggin         } else {
345d8c14411SNicholas Piggin             tfmr = tfmr_new_tb_state(tfmr, TBST_SEND_TOD_MOD);
346d8c14411SNicholas Piggin             /* State seems to transition immediately */
347d8c14411SNicholas Piggin             tfmr = tfmr_new_tb_state(tfmr, TBST_NOT_SET);
348d8c14411SNicholas Piggin         }
349d8c14411SNicholas Piggin     } else if (tfmr & TFMR_MOVE_CHIP_TOD_TO_TB) {
350d8c14411SNicholas Piggin         if (tbst == TBST_SYNC_WAIT) {
351d8c14411SNicholas Piggin             tfmr = tfmr_new_tb_state(tfmr, TBST_GET_TOD);
3520ca94b2fSNicholas Piggin             tod_state->tb_state_timer = 3;
353d8c14411SNicholas Piggin         } else if (tbst == TBST_GET_TOD) {
3540ca94b2fSNicholas Piggin             if (tod_state->tod_sent_to_tb) {
355d8c14411SNicholas Piggin                 tfmr = tfmr_new_tb_state(tfmr, TBST_TB_RUNNING);
356d8c14411SNicholas Piggin                 tfmr &= ~TFMR_MOVE_CHIP_TOD_TO_TB;
3570ca94b2fSNicholas Piggin                 tod_state->tb_ready_for_tod = 0;
3580ca94b2fSNicholas Piggin                 tod_state->tod_sent_to_tb = 0;
359d8c14411SNicholas Piggin             }
360d8c14411SNicholas Piggin         } else {
361d8c14411SNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: MOVE_CHIP_TOD_TO_TB "
362d8c14411SNicholas Piggin                           "state machine in invalid state 0x%x\n", tbst);
363d8c14411SNicholas Piggin             tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR);
364d8c14411SNicholas Piggin             tfmr |= TFMR_FIRMWARE_CONTROL_ERROR;
3650ca94b2fSNicholas Piggin             tod_state->tb_ready_for_tod = 0;
366d8c14411SNicholas Piggin         }
367d8c14411SNicholas Piggin     }
368d8c14411SNicholas Piggin 
369a21d89b5SNicholas Piggin     write_tfmr(env, tfmr);
370d8c14411SNicholas Piggin }
371d8c14411SNicholas Piggin 
helper_load_tfmr(CPUPPCState * env)372b25f2ffaSNicholas Piggin target_ulong helper_load_tfmr(CPUPPCState *env)
373b25f2ffaSNicholas Piggin {
374d8c14411SNicholas Piggin     tb_state_machine_step(env);
375d8c14411SNicholas Piggin 
376d8c14411SNicholas Piggin     return env->spr[SPR_TFMR] | TFMR_TB_ECLIPZ;
377b25f2ffaSNicholas Piggin }
378b25f2ffaSNicholas Piggin 
helper_store_tfmr(CPUPPCState * env,target_ulong val)379b25f2ffaSNicholas Piggin void helper_store_tfmr(CPUPPCState *env, target_ulong val)
380b25f2ffaSNicholas Piggin {
3810ca94b2fSNicholas Piggin     PowerPCCPU *cpu = env_archcpu(env);
3820ca94b2fSNicholas Piggin     PnvCoreTODState *tod_state = cpu_get_tbst(cpu);
383d8c14411SNicholas Piggin     uint64_t tfmr = env->spr[SPR_TFMR];
384d8c14411SNicholas Piggin     uint64_t clear_on_write;
385d8c14411SNicholas Piggin     unsigned int tbst = tfmr_get_tb_state(tfmr);
386d8c14411SNicholas Piggin 
387d8c14411SNicholas Piggin     if (!(val & TFMR_TB_ECLIPZ)) {
388d8c14411SNicholas Piggin         qemu_log_mask(LOG_UNIMP, "TFMR non-ECLIPZ mode not implemented\n");
389d8c14411SNicholas Piggin         tfmr &= ~TFMR_TBST_ENCODED;
390d8c14411SNicholas Piggin         tfmr &= ~TFMR_TBST_LAST;
391d8c14411SNicholas Piggin         goto out;
392d8c14411SNicholas Piggin     }
393d8c14411SNicholas Piggin 
394d8c14411SNicholas Piggin     /* Update control bits */
395d8c14411SNicholas Piggin     tfmr = (tfmr & ~TFMR_CONTROL_MASK) | (val & TFMR_CONTROL_MASK);
396d8c14411SNicholas Piggin 
397d8c14411SNicholas Piggin     /* Several bits are clear-on-write, only one is implemented so far */
398d8c14411SNicholas Piggin     clear_on_write = val & TFMR_FIRMWARE_CONTROL_ERROR;
399d8c14411SNicholas Piggin     tfmr &= ~clear_on_write;
400d8c14411SNicholas Piggin 
401d8c14411SNicholas Piggin     /*
402d8c14411SNicholas Piggin      * mtspr always clears this. The sync pulse timer makes it come back
403d8c14411SNicholas Piggin      * after the second mfspr.
404d8c14411SNicholas Piggin      */
405d8c14411SNicholas Piggin     tfmr &= ~TFMR_TB_SYNC_OCCURED;
4060ca94b2fSNicholas Piggin     tod_state->tb_sync_pulse_timer = 1;
407d8c14411SNicholas Piggin 
408d8c14411SNicholas Piggin     if (((tfmr | val) & (TFMR_LOAD_TOD_MOD | TFMR_MOVE_CHIP_TOD_TO_TB)) ==
409d8c14411SNicholas Piggin                         (TFMR_LOAD_TOD_MOD | TFMR_MOVE_CHIP_TOD_TO_TB)) {
410d8c14411SNicholas Piggin         qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: LOAD_TOD_MOD and "
411d8c14411SNicholas Piggin                                        "MOVE_CHIP_TOD_TO_TB both set\n");
412d8c14411SNicholas Piggin         tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR);
413d8c14411SNicholas Piggin         tfmr |= TFMR_FIRMWARE_CONTROL_ERROR;
4140ca94b2fSNicholas Piggin         tod_state->tb_ready_for_tod = 0;
415d8c14411SNicholas Piggin         goto out;
416d8c14411SNicholas Piggin     }
417d8c14411SNicholas Piggin 
418d8c14411SNicholas Piggin     if (tfmr & TFMR_CLEAR_TB_ERRORS) {
419d8c14411SNicholas Piggin         /*
420d8c14411SNicholas Piggin          * Workbook says TFMR_CLEAR_TB_ERRORS should be written twice.
421d8c14411SNicholas Piggin          * This is not simulated/required here.
422d8c14411SNicholas Piggin          */
423d8c14411SNicholas Piggin         tfmr = tfmr_new_tb_state(tfmr, TBST_RESET);
424d8c14411SNicholas Piggin         tfmr &= ~TFMR_CLEAR_TB_ERRORS;
425d8c14411SNicholas Piggin         tfmr &= ~TFMR_LOAD_TOD_MOD;
426d8c14411SNicholas Piggin         tfmr &= ~TFMR_MOVE_CHIP_TOD_TO_TB;
427d8c14411SNicholas Piggin         tfmr &= ~TFMR_FIRMWARE_CONTROL_ERROR; /* XXX: should this be cleared? */
4280ca94b2fSNicholas Piggin         tod_state->tb_ready_for_tod = 0;
4290ca94b2fSNicholas Piggin         tod_state->tod_sent_to_tb = 0;
430d8c14411SNicholas Piggin         goto out;
431d8c14411SNicholas Piggin     }
432d8c14411SNicholas Piggin 
433d8c14411SNicholas Piggin     if (tbst == TBST_TB_ERROR) {
434d8c14411SNicholas Piggin         qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: mtspr TFMR in TB_ERROR"
435d8c14411SNicholas Piggin                                        " state\n");
436d8c14411SNicholas Piggin         tfmr |= TFMR_FIRMWARE_CONTROL_ERROR;
437d8c14411SNicholas Piggin         return;
438d8c14411SNicholas Piggin     }
439d8c14411SNicholas Piggin 
440d8c14411SNicholas Piggin     if (tfmr & TFMR_LOAD_TOD_MOD) {
441d8c14411SNicholas Piggin         /* Wait for an arbitrary 3 mfspr until the next state transition. */
4420ca94b2fSNicholas Piggin         tod_state->tb_state_timer = 3;
443d8c14411SNicholas Piggin     } else if (tfmr & TFMR_MOVE_CHIP_TOD_TO_TB) {
444d8c14411SNicholas Piggin         if (tbst == TBST_NOT_SET) {
445d8c14411SNicholas Piggin             tfmr = tfmr_new_tb_state(tfmr, TBST_SYNC_WAIT);
4460ca94b2fSNicholas Piggin             tod_state->tb_ready_for_tod = 1;
4470ca94b2fSNicholas Piggin             tod_state->tb_state_timer = 3; /* arbitrary */
448d8c14411SNicholas Piggin         } else {
449d8c14411SNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: MOVE_CHIP_TOD_TO_TB "
450d8c14411SNicholas Piggin                                            "not in TB not set state 0x%x\n",
451d8c14411SNicholas Piggin                                            tbst);
452d8c14411SNicholas Piggin             tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR);
453d8c14411SNicholas Piggin             tfmr |= TFMR_FIRMWARE_CONTROL_ERROR;
4540ca94b2fSNicholas Piggin             tod_state->tb_ready_for_tod = 0;
455d8c14411SNicholas Piggin         }
456d8c14411SNicholas Piggin     }
457d8c14411SNicholas Piggin 
458d8c14411SNicholas Piggin out:
459a21d89b5SNicholas Piggin     write_tfmr(env, tfmr);
460b25f2ffaSNicholas Piggin }
461b25f2ffaSNicholas Piggin #endif
462b25f2ffaSNicholas Piggin 
4636de673d4SBlue Swirl /*****************************************************************************/
4646de673d4SBlue Swirl /* Embedded PowerPC specific helpers */
4656de673d4SBlue Swirl 
4666de673d4SBlue Swirl /* XXX: to be improved to check access rights when in user-mode */
helper_load_dcr(CPUPPCState * env,target_ulong dcrn)467d0f1562dSBlue Swirl target_ulong helper_load_dcr(CPUPPCState *env, target_ulong dcrn)
4686de673d4SBlue Swirl {
4696de673d4SBlue Swirl     uint32_t val = 0;
4706de673d4SBlue Swirl 
4716de673d4SBlue Swirl     if (unlikely(env->dcr_env == NULL)) {
47248880da6SPaolo Bonzini         qemu_log_mask(LOG_GUEST_ERROR, "No DCR environment\n");
473a13f0a9bSBenjamin Herrenschmidt         raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM,
4746de673d4SBlue Swirl                                POWERPC_EXCP_INVAL |
475a13f0a9bSBenjamin Herrenschmidt                                POWERPC_EXCP_INVAL_INVAL, GETPC());
476235352eeSPeter Maydell     } else {
477235352eeSPeter Maydell         int ret;
478235352eeSPeter Maydell 
479195801d7SStefan Hajnoczi         bql_lock();
480235352eeSPeter Maydell         ret = ppc_dcr_read(env->dcr_env, (uint32_t)dcrn, &val);
481195801d7SStefan Hajnoczi         bql_unlock();
482235352eeSPeter Maydell         if (unlikely(ret != 0)) {
48348880da6SPaolo Bonzini             qemu_log_mask(LOG_GUEST_ERROR, "DCR read error %d %03x\n",
48448880da6SPaolo Bonzini                           (uint32_t)dcrn, (uint32_t)dcrn);
485a13f0a9bSBenjamin Herrenschmidt             raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM,
486a13f0a9bSBenjamin Herrenschmidt                                    POWERPC_EXCP_INVAL |
487e8985179SMatheus Ferst                                    POWERPC_EXCP_INVAL_INVAL, GETPC());
4886de673d4SBlue Swirl         }
489235352eeSPeter Maydell     }
4906de673d4SBlue Swirl     return val;
4916de673d4SBlue Swirl }
4926de673d4SBlue Swirl 
helper_store_dcr(CPUPPCState * env,target_ulong dcrn,target_ulong val)493d0f1562dSBlue Swirl void helper_store_dcr(CPUPPCState *env, target_ulong dcrn, target_ulong val)
4946de673d4SBlue Swirl {
4956de673d4SBlue Swirl     if (unlikely(env->dcr_env == NULL)) {
49648880da6SPaolo Bonzini         qemu_log_mask(LOG_GUEST_ERROR, "No DCR environment\n");
497a13f0a9bSBenjamin Herrenschmidt         raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM,
4986de673d4SBlue Swirl                                POWERPC_EXCP_INVAL |
499a13f0a9bSBenjamin Herrenschmidt                                POWERPC_EXCP_INVAL_INVAL, GETPC());
500235352eeSPeter Maydell     } else {
501235352eeSPeter Maydell         int ret;
502195801d7SStefan Hajnoczi         bql_lock();
503235352eeSPeter Maydell         ret = ppc_dcr_write(env->dcr_env, (uint32_t)dcrn, (uint32_t)val);
504195801d7SStefan Hajnoczi         bql_unlock();
505235352eeSPeter Maydell         if (unlikely(ret != 0)) {
50648880da6SPaolo Bonzini             qemu_log_mask(LOG_GUEST_ERROR, "DCR write error %d %03x\n",
50748880da6SPaolo Bonzini                           (uint32_t)dcrn, (uint32_t)dcrn);
508a13f0a9bSBenjamin Herrenschmidt             raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM,
509a13f0a9bSBenjamin Herrenschmidt                                    POWERPC_EXCP_INVAL |
510e8985179SMatheus Ferst                                    POWERPC_EXCP_INVAL_INVAL, GETPC());
5116de673d4SBlue Swirl         }
5126de673d4SBlue Swirl     }
513235352eeSPeter Maydell }
514e8985179SMatheus Ferst #endif
515