xref: /qemu/hw/ppc/pnv_chiptod.c (revision cde2ba34a951997f01c184acf6e3a29eb6a81e79)
19a69950fSNicholas Piggin /*
29a69950fSNicholas Piggin  * QEMU PowerPC PowerNV Emulation of some ChipTOD behaviour
39a69950fSNicholas Piggin  *
49a69950fSNicholas Piggin  * Copyright (c) 2022-2023, IBM Corporation.
59a69950fSNicholas Piggin  *
69a69950fSNicholas Piggin  * SPDX-License-Identifier: GPL-2.0-or-later
79a69950fSNicholas Piggin  *
89a69950fSNicholas Piggin  * ChipTOD (aka TOD) is a facility implemented in the nest / pervasive. The
99a69950fSNicholas Piggin  * purpose is to keep time-of-day across chips and cores.
109a69950fSNicholas Piggin  *
119a69950fSNicholas Piggin  * There is a master chip TOD, which sends signals to slave chip TODs to
129a69950fSNicholas Piggin  * keep them synchronized. There are two sets of configuration registers
139a69950fSNicholas Piggin  * called primary and secondary, which can be used fail over.
149a69950fSNicholas Piggin  *
159a69950fSNicholas Piggin  * The chip TOD also distributes synchronisation signals to the timebase
169a69950fSNicholas Piggin  * facility in each of the cores on the chip. In particular there is a
179a69950fSNicholas Piggin  * feature that can move the TOD value in the ChipTOD to and from the TB.
189a69950fSNicholas Piggin  *
199a69950fSNicholas Piggin  * Initialisation typically brings all ChipTOD into sync (see tod_state),
209a69950fSNicholas Piggin  * and then brings each core TB into sync with the ChipTODs (see timebase
219a69950fSNicholas Piggin  * state and TFMR). This model is a very basic simulation of the init sequence
229a69950fSNicholas Piggin  * performed by skiboot.
239a69950fSNicholas Piggin  */
249a69950fSNicholas Piggin 
259a69950fSNicholas Piggin #include "qemu/osdep.h"
269a69950fSNicholas Piggin #include "sysemu/reset.h"
279a69950fSNicholas Piggin #include "target/ppc/cpu.h"
289a69950fSNicholas Piggin #include "qapi/error.h"
299a69950fSNicholas Piggin #include "qemu/log.h"
309a69950fSNicholas Piggin #include "qemu/module.h"
319a69950fSNicholas Piggin #include "hw/irq.h"
329a69950fSNicholas Piggin #include "hw/qdev-properties.h"
339a69950fSNicholas Piggin #include "hw/ppc/fdt.h"
349a69950fSNicholas Piggin #include "hw/ppc/ppc.h"
359a69950fSNicholas Piggin #include "hw/ppc/pnv.h"
369a69950fSNicholas Piggin #include "hw/ppc/pnv_chip.h"
379a69950fSNicholas Piggin #include "hw/ppc/pnv_core.h"
389a69950fSNicholas Piggin #include "hw/ppc/pnv_xscom.h"
399a69950fSNicholas Piggin #include "hw/ppc/pnv_chiptod.h"
409a69950fSNicholas Piggin #include "trace.h"
419a69950fSNicholas Piggin 
429a69950fSNicholas Piggin #include <libfdt.h>
439a69950fSNicholas Piggin 
449a69950fSNicholas Piggin /* TOD chip XSCOM addresses */
459a69950fSNicholas Piggin #define TOD_M_PATH_CTRL_REG             0x00000000 /* Master Path ctrl reg */
469a69950fSNicholas Piggin #define TOD_PRI_PORT_0_CTRL_REG         0x00000001 /* Primary port0 ctrl reg */
479a69950fSNicholas Piggin #define TOD_PRI_PORT_1_CTRL_REG         0x00000002 /* Primary port1 ctrl reg */
489a69950fSNicholas Piggin #define TOD_SEC_PORT_0_CTRL_REG         0x00000003 /* Secondary p0 ctrl reg */
499a69950fSNicholas Piggin #define TOD_SEC_PORT_1_CTRL_REG         0x00000004 /* Secondary p1 ctrl reg */
509a69950fSNicholas Piggin #define TOD_S_PATH_CTRL_REG             0x00000005 /* Slave Path ctrl reg */
519a69950fSNicholas Piggin #define TOD_I_PATH_CTRL_REG             0x00000006 /* Internal Path ctrl reg */
529a69950fSNicholas Piggin 
539a69950fSNicholas Piggin /* -- TOD primary/secondary master/slave control register -- */
549a69950fSNicholas Piggin #define TOD_PSS_MSS_CTRL_REG            0x00000007
559a69950fSNicholas Piggin 
569a69950fSNicholas Piggin /* -- TOD primary/secondary master/slave status register -- */
579a69950fSNicholas Piggin #define TOD_PSS_MSS_STATUS_REG          0x00000008
589a69950fSNicholas Piggin 
599a69950fSNicholas Piggin /* TOD chip XSCOM addresses */
609a69950fSNicholas Piggin #define TOD_CHIP_CTRL_REG               0x00000010 /* Chip control reg */
619a69950fSNicholas Piggin 
629a69950fSNicholas Piggin #define TOD_TX_TTYPE_0_REG              0x00000011
639a69950fSNicholas Piggin #define TOD_TX_TTYPE_1_REG              0x00000012 /* PSS switch reg */
649a69950fSNicholas Piggin #define TOD_TX_TTYPE_2_REG              0x00000013 /* Enable step checkers */
659a69950fSNicholas Piggin #define TOD_TX_TTYPE_3_REG              0x00000014 /* Request TOD reg */
669a69950fSNicholas Piggin #define TOD_TX_TTYPE_4_REG              0x00000015 /* Send TOD reg */
679a69950fSNicholas Piggin #define TOD_TX_TTYPE_5_REG              0x00000016 /* Invalidate TOD reg */
689a69950fSNicholas Piggin 
699a69950fSNicholas Piggin #define TOD_MOVE_TOD_TO_TB_REG          0x00000017
709a69950fSNicholas Piggin #define TOD_LOAD_TOD_MOD_REG            0x00000018
719a69950fSNicholas Piggin #define TOD_LOAD_TOD_REG                0x00000021
729a69950fSNicholas Piggin #define TOD_START_TOD_REG               0x00000022
739a69950fSNicholas Piggin #define TOD_FSM_REG                     0x00000024
749a69950fSNicholas Piggin 
759a69950fSNicholas Piggin #define TOD_TX_TTYPE_CTRL_REG           0x00000027 /* TX TTYPE Control reg */
769a69950fSNicholas Piggin #define   TOD_TX_TTYPE_PIB_SLAVE_ADDR      PPC_BITMASK(26, 31)
779a69950fSNicholas Piggin 
789a69950fSNicholas Piggin /* -- TOD Error interrupt register -- */
799a69950fSNicholas Piggin #define TOD_ERROR_REG                   0x00000030
809a69950fSNicholas Piggin 
819a69950fSNicholas Piggin /* PC unit PIB address which recieves the timebase transfer from TOD */
829a69950fSNicholas Piggin #define   PC_TOD                        0x4A3
839a69950fSNicholas Piggin 
849a69950fSNicholas Piggin /*
859a69950fSNicholas Piggin  * The TOD FSM:
869a69950fSNicholas Piggin  * - The reset state is 0 error.
879a69950fSNicholas Piggin  * - A hardware error detected will transition to state 0 from any state.
889a69950fSNicholas Piggin  * - LOAD_TOD_MOD and TTYPE5 will transition to state 7 from any state.
899a69950fSNicholas Piggin  *
909a69950fSNicholas Piggin  * | state      | action                       | new |
919a69950fSNicholas Piggin  * |------------+------------------------------+-----|
929a69950fSNicholas Piggin  * | 0 error    | LOAD_TOD_MOD                 |  7  |
939a69950fSNicholas Piggin  * | 0 error    | Recv TTYPE5 (invalidate TOD) |  7  |
949a69950fSNicholas Piggin  * | 7 not_set  | LOAD_TOD (bit-63 = 0)        |  2  |
959a69950fSNicholas Piggin  * | 7 not_set  | LOAD_TOD (bit-63 = 1)        |  1  |
969a69950fSNicholas Piggin  * | 7 not_set  | Recv TTYPE4 (send TOD)       |  2  |
979a69950fSNicholas Piggin  * | 2 running  |                              |     |
989a69950fSNicholas Piggin  * | 1 stopped  | START_TOD                    |  2  |
999a69950fSNicholas Piggin  *
1009a69950fSNicholas Piggin  * Note the hardware has additional states but they relate to the sending
1019a69950fSNicholas Piggin  * and receiving and waiting on synchronisation signals between chips and
1029a69950fSNicholas Piggin  * are not described or modeled here.
1039a69950fSNicholas Piggin  */
1049a69950fSNicholas Piggin 
1059a69950fSNicholas Piggin static uint64_t pnv_chiptod_xscom_read(void *opaque, hwaddr addr,
1069a69950fSNicholas Piggin                                           unsigned size)
1079a69950fSNicholas Piggin {
1089a69950fSNicholas Piggin     PnvChipTOD *chiptod = PNV_CHIPTOD(opaque);
1099a69950fSNicholas Piggin     uint32_t offset = addr >> 3;
1109a69950fSNicholas Piggin     uint64_t val = 0;
1119a69950fSNicholas Piggin 
1129a69950fSNicholas Piggin     switch (offset) {
1139a69950fSNicholas Piggin     case TOD_PSS_MSS_STATUS_REG:
1149a69950fSNicholas Piggin         /*
1159a69950fSNicholas Piggin          * ChipTOD does not support configurations other than primary
1169a69950fSNicholas Piggin          * master, does not support errors, etc.
1179a69950fSNicholas Piggin          */
1189a69950fSNicholas Piggin         val |= PPC_BITMASK(6, 10); /* STEP checker validity */
1199a69950fSNicholas Piggin         val |= PPC_BIT(12); /* Primary config master path select */
1209a69950fSNicholas Piggin         if (chiptod->tod_state == tod_running) {
1219a69950fSNicholas Piggin             val |= PPC_BIT(20); /* Is running */
1229a69950fSNicholas Piggin         }
1239a69950fSNicholas Piggin         val |= PPC_BIT(21); /* Is using primary config */
1249a69950fSNicholas Piggin         val |= PPC_BIT(26); /* Is using master path select */
1259a69950fSNicholas Piggin 
1269a69950fSNicholas Piggin         if (chiptod->primary) {
1279a69950fSNicholas Piggin             val |= PPC_BIT(23); /* Is active master */
1289a69950fSNicholas Piggin         } else if (chiptod->secondary) {
1299a69950fSNicholas Piggin             val |= PPC_BIT(24); /* Is backup master */
1309a69950fSNicholas Piggin         } else {
1319a69950fSNicholas Piggin             val |= PPC_BIT(25); /* Is slave (should backup master set this?) */
1329a69950fSNicholas Piggin         }
1339a69950fSNicholas Piggin         break;
1349a69950fSNicholas Piggin     case TOD_PSS_MSS_CTRL_REG:
1359a69950fSNicholas Piggin         val = chiptod->pss_mss_ctrl_reg;
1369a69950fSNicholas Piggin         break;
1379a69950fSNicholas Piggin     case TOD_TX_TTYPE_CTRL_REG:
1389a69950fSNicholas Piggin         val = 0;
1399a69950fSNicholas Piggin         break;
1409a69950fSNicholas Piggin     case TOD_ERROR_REG:
1419a69950fSNicholas Piggin         val = chiptod->tod_error;
1429a69950fSNicholas Piggin         break;
1439a69950fSNicholas Piggin     case TOD_FSM_REG:
1449a69950fSNicholas Piggin         if (chiptod->tod_state == tod_running) {
1459a69950fSNicholas Piggin             val |= PPC_BIT(4);
1469a69950fSNicholas Piggin         }
1479a69950fSNicholas Piggin         break;
1489a69950fSNicholas Piggin     default:
1499a69950fSNicholas Piggin         qemu_log_mask(LOG_UNIMP, "pnv_chiptod: unimplemented register: Ox%"
1509a69950fSNicholas Piggin                       HWADDR_PRIx "\n", addr >> 3);
1519a69950fSNicholas Piggin     }
1529a69950fSNicholas Piggin 
1539a69950fSNicholas Piggin     trace_pnv_chiptod_xscom_read(addr >> 3, val);
1549a69950fSNicholas Piggin 
1559a69950fSNicholas Piggin     return val;
1569a69950fSNicholas Piggin }
1579a69950fSNicholas Piggin 
1589a69950fSNicholas Piggin static void chiptod_receive_ttype(PnvChipTOD *chiptod, uint32_t trigger)
1599a69950fSNicholas Piggin {
1609a69950fSNicholas Piggin     switch (trigger) {
1619a69950fSNicholas Piggin     case TOD_TX_TTYPE_4_REG:
1629a69950fSNicholas Piggin         if (chiptod->tod_state != tod_not_set) {
1639a69950fSNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: received TTYPE4 in "
1649a69950fSNicholas Piggin                           " state %d, should be in 7 (TOD_NOT_SET)\n",
1659a69950fSNicholas Piggin                           chiptod->tod_state);
1669a69950fSNicholas Piggin         } else {
1679a69950fSNicholas Piggin             chiptod->tod_state = tod_running;
1689a69950fSNicholas Piggin         }
1699a69950fSNicholas Piggin         break;
1709a69950fSNicholas Piggin     case TOD_TX_TTYPE_5_REG:
1719a69950fSNicholas Piggin         /* Works from any state */
1729a69950fSNicholas Piggin         chiptod->tod_state = tod_not_set;
1739a69950fSNicholas Piggin         break;
1749a69950fSNicholas Piggin     default:
1759a69950fSNicholas Piggin         qemu_log_mask(LOG_UNIMP, "pnv_chiptod: received unimplemented "
1769a69950fSNicholas Piggin                       " TTYPE %u\n", trigger);
1779a69950fSNicholas Piggin         break;
1789a69950fSNicholas Piggin     }
1799a69950fSNicholas Piggin }
1809a69950fSNicholas Piggin 
1819a69950fSNicholas Piggin static void chiptod_power9_broadcast_ttype(PnvChipTOD *sender,
1829a69950fSNicholas Piggin                                             uint32_t trigger)
1839a69950fSNicholas Piggin {
1849a69950fSNicholas Piggin     PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
1859a69950fSNicholas Piggin     int i;
1869a69950fSNicholas Piggin 
1879a69950fSNicholas Piggin     for (i = 0; i < pnv->num_chips; i++) {
1889a69950fSNicholas Piggin         Pnv9Chip *chip9 = PNV9_CHIP(pnv->chips[i]);
1899a69950fSNicholas Piggin         PnvChipTOD *chiptod = &chip9->chiptod;
1909a69950fSNicholas Piggin 
1919a69950fSNicholas Piggin         if (chiptod != sender) {
1929a69950fSNicholas Piggin             chiptod_receive_ttype(chiptod, trigger);
1939a69950fSNicholas Piggin         }
1949a69950fSNicholas Piggin     }
1959a69950fSNicholas Piggin }
1969a69950fSNicholas Piggin 
1979a69950fSNicholas Piggin static void chiptod_power10_broadcast_ttype(PnvChipTOD *sender,
1989a69950fSNicholas Piggin                                             uint32_t trigger)
1999a69950fSNicholas Piggin {
2009a69950fSNicholas Piggin     PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
2019a69950fSNicholas Piggin     int i;
2029a69950fSNicholas Piggin 
2039a69950fSNicholas Piggin     for (i = 0; i < pnv->num_chips; i++) {
2049a69950fSNicholas Piggin         Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]);
2059a69950fSNicholas Piggin         PnvChipTOD *chiptod = &chip10->chiptod;
2069a69950fSNicholas Piggin 
2079a69950fSNicholas Piggin         if (chiptod != sender) {
2089a69950fSNicholas Piggin             chiptod_receive_ttype(chiptod, trigger);
2099a69950fSNicholas Piggin         }
2109a69950fSNicholas Piggin     }
2119a69950fSNicholas Piggin }
2129a69950fSNicholas Piggin 
213*cde2ba34SNicholas Piggin static PnvCore *pnv_chip_get_core_by_xscom_base(PnvChip *chip,
214*cde2ba34SNicholas Piggin                                                 uint32_t xscom_base)
215*cde2ba34SNicholas Piggin {
216*cde2ba34SNicholas Piggin     PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
217*cde2ba34SNicholas Piggin     int i;
218*cde2ba34SNicholas Piggin 
219*cde2ba34SNicholas Piggin     for (i = 0; i < chip->nr_cores; i++) {
220*cde2ba34SNicholas Piggin         PnvCore *pc = chip->cores[i];
221*cde2ba34SNicholas Piggin         CPUCore *cc = CPU_CORE(pc);
222*cde2ba34SNicholas Piggin         int core_hwid = cc->core_id;
223*cde2ba34SNicholas Piggin 
224*cde2ba34SNicholas Piggin         if (pcc->xscom_core_base(chip, core_hwid) == xscom_base) {
225*cde2ba34SNicholas Piggin             return pc;
226*cde2ba34SNicholas Piggin         }
227*cde2ba34SNicholas Piggin     }
228*cde2ba34SNicholas Piggin     return NULL;
229*cde2ba34SNicholas Piggin }
230*cde2ba34SNicholas Piggin 
231*cde2ba34SNicholas Piggin static PnvCore *chiptod_power9_tx_ttype_target(PnvChipTOD *chiptod,
232*cde2ba34SNicholas Piggin                                                uint64_t val)
233*cde2ba34SNicholas Piggin {
234*cde2ba34SNicholas Piggin     /*
235*cde2ba34SNicholas Piggin      * skiboot uses Core ID for P9, though SCOM should work too.
236*cde2ba34SNicholas Piggin      */
237*cde2ba34SNicholas Piggin     if (val & PPC_BIT(35)) { /* SCOM addressing */
238*cde2ba34SNicholas Piggin         uint32_t addr = val >> 32;
239*cde2ba34SNicholas Piggin         uint32_t reg = addr & 0xfff;
240*cde2ba34SNicholas Piggin 
241*cde2ba34SNicholas Piggin         if (reg != PC_TOD) {
242*cde2ba34SNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: SCOM addressing: "
243*cde2ba34SNicholas Piggin                           "unimplemented slave register 0x%" PRIx32 "\n", reg);
244*cde2ba34SNicholas Piggin             return NULL;
245*cde2ba34SNicholas Piggin         }
246*cde2ba34SNicholas Piggin 
247*cde2ba34SNicholas Piggin         return pnv_chip_get_core_by_xscom_base(chiptod->chip, addr & ~0xfff);
248*cde2ba34SNicholas Piggin 
249*cde2ba34SNicholas Piggin     } else { /* Core ID addressing */
250*cde2ba34SNicholas Piggin         uint32_t core_id = GETFIELD(TOD_TX_TTYPE_PIB_SLAVE_ADDR, val) & 0x1f;
251*cde2ba34SNicholas Piggin         return pnv_chip_find_core(chiptod->chip, core_id);
252*cde2ba34SNicholas Piggin     }
253*cde2ba34SNicholas Piggin }
254*cde2ba34SNicholas Piggin 
255*cde2ba34SNicholas Piggin static PnvCore *chiptod_power10_tx_ttype_target(PnvChipTOD *chiptod,
256*cde2ba34SNicholas Piggin                                                uint64_t val)
257*cde2ba34SNicholas Piggin {
258*cde2ba34SNicholas Piggin     /*
259*cde2ba34SNicholas Piggin      * skiboot uses SCOM for P10 because Core ID was unable to be made to
260*cde2ba34SNicholas Piggin      * work correctly. For this reason only SCOM addressing is implemented.
261*cde2ba34SNicholas Piggin      */
262*cde2ba34SNicholas Piggin     if (val & PPC_BIT(35)) { /* SCOM addressing */
263*cde2ba34SNicholas Piggin         uint32_t addr = val >> 32;
264*cde2ba34SNicholas Piggin         uint32_t reg = addr & 0xfff;
265*cde2ba34SNicholas Piggin 
266*cde2ba34SNicholas Piggin         if (reg != PC_TOD) {
267*cde2ba34SNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: SCOM addressing: "
268*cde2ba34SNicholas Piggin                           "unimplemented slave register 0x%" PRIx32 "\n", reg);
269*cde2ba34SNicholas Piggin             return NULL;
270*cde2ba34SNicholas Piggin         }
271*cde2ba34SNicholas Piggin 
272*cde2ba34SNicholas Piggin         /*
273*cde2ba34SNicholas Piggin          * This may not deal with P10 big-core addressing at the moment.
274*cde2ba34SNicholas Piggin          * The big-core code in skiboot syncs small cores, but it targets
275*cde2ba34SNicholas Piggin          * the even PIR (first small-core) when syncing second small-core.
276*cde2ba34SNicholas Piggin          */
277*cde2ba34SNicholas Piggin         return pnv_chip_get_core_by_xscom_base(chiptod->chip, addr & ~0xfff);
278*cde2ba34SNicholas Piggin 
279*cde2ba34SNicholas Piggin     } else { /* Core ID addressing */
280*cde2ba34SNicholas Piggin         qemu_log_mask(LOG_UNIMP, "pnv_chiptod: TX TTYPE Core ID "
281*cde2ba34SNicholas Piggin                       "addressing is not implemented for POWER10\n");
282*cde2ba34SNicholas Piggin         return NULL;
283*cde2ba34SNicholas Piggin     }
284*cde2ba34SNicholas Piggin }
285*cde2ba34SNicholas Piggin 
2869a69950fSNicholas Piggin static void pnv_chiptod_xscom_write(void *opaque, hwaddr addr,
2879a69950fSNicholas Piggin                                     uint64_t val, unsigned size)
2889a69950fSNicholas Piggin {
2899a69950fSNicholas Piggin     PnvChipTOD *chiptod = PNV_CHIPTOD(opaque);
2909a69950fSNicholas Piggin     PnvChipTODClass *pctc = PNV_CHIPTOD_GET_CLASS(chiptod);
2919a69950fSNicholas Piggin     uint32_t offset = addr >> 3;
2929a69950fSNicholas Piggin 
2939a69950fSNicholas Piggin     trace_pnv_chiptod_xscom_write(addr >> 3, val);
2949a69950fSNicholas Piggin 
2959a69950fSNicholas Piggin     switch (offset) {
2969a69950fSNicholas Piggin     case TOD_PSS_MSS_CTRL_REG:
2979a69950fSNicholas Piggin         /* Is this correct? */
2989a69950fSNicholas Piggin         if (chiptod->primary) {
2999a69950fSNicholas Piggin             val |= PPC_BIT(1); /* TOD is master */
3009a69950fSNicholas Piggin         } else {
3019a69950fSNicholas Piggin             val &= ~PPC_BIT(1);
3029a69950fSNicholas Piggin         }
3039a69950fSNicholas Piggin         val |= PPC_BIT(2); /* Drawer is master (don't simulate multi-drawer) */
3049a69950fSNicholas Piggin         chiptod->pss_mss_ctrl_reg = val & PPC_BITMASK(0, 31);
3059a69950fSNicholas Piggin         break;
3069a69950fSNicholas Piggin 
307*cde2ba34SNicholas Piggin     case TOD_TX_TTYPE_CTRL_REG:
308*cde2ba34SNicholas Piggin         /*
309*cde2ba34SNicholas Piggin          * This register sets the target of the TOD value transfer initiated
310*cde2ba34SNicholas Piggin          * by TOD_MOVE_TOD_TO_TB. The TOD is able to send the address to
311*cde2ba34SNicholas Piggin          * any target register, though in practice only the PC TOD register
312*cde2ba34SNicholas Piggin          * should be used. ChipTOD has a "SCOM addressing" mode which fully
313*cde2ba34SNicholas Piggin          * specifies the SCOM address, and a core-ID mode which uses the
314*cde2ba34SNicholas Piggin          * core ID to target the PC TOD for a given core.
315*cde2ba34SNicholas Piggin          */
316*cde2ba34SNicholas Piggin         chiptod->slave_pc_target = pctc->tx_ttype_target(chiptod, val);
317*cde2ba34SNicholas Piggin         if (!chiptod->slave_pc_target) {
318*cde2ba34SNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
319*cde2ba34SNicholas Piggin                           " TOD_TX_TTYPE_CTRL_REG val 0x%" PRIx64
320*cde2ba34SNicholas Piggin                           " invalid slave address\n", val);
321*cde2ba34SNicholas Piggin         }
322*cde2ba34SNicholas Piggin         break;
3239a69950fSNicholas Piggin     case TOD_ERROR_REG:
3249a69950fSNicholas Piggin         chiptod->tod_error &= ~val;
3259a69950fSNicholas Piggin         break;
3269a69950fSNicholas Piggin     case TOD_LOAD_TOD_MOD_REG:
3279a69950fSNicholas Piggin         if (!(val & PPC_BIT(0))) {
3289a69950fSNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
3299a69950fSNicholas Piggin                           " TOD_LOAD_TOD_MOD_REG with bad val 0x%" PRIx64"\n",
3309a69950fSNicholas Piggin                           val);
3319a69950fSNicholas Piggin         } else {
3329a69950fSNicholas Piggin             chiptod->tod_state = tod_not_set;
3339a69950fSNicholas Piggin         }
3349a69950fSNicholas Piggin         break;
3359a69950fSNicholas Piggin     case TOD_LOAD_TOD_REG:
3369a69950fSNicholas Piggin         if (chiptod->tod_state != tod_not_set) {
3379a69950fSNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: LOAD_TOG_REG in "
3389a69950fSNicholas Piggin                           " state %d, should be in 7 (TOD_NOT_SET)\n",
3399a69950fSNicholas Piggin                           chiptod->tod_state);
3409a69950fSNicholas Piggin         } else {
3419a69950fSNicholas Piggin             if (val & PPC_BIT(63)) {
3429a69950fSNicholas Piggin                 chiptod->tod_state = tod_stopped;
3439a69950fSNicholas Piggin             } else {
3449a69950fSNicholas Piggin                 chiptod->tod_state = tod_running;
3459a69950fSNicholas Piggin             }
3469a69950fSNicholas Piggin         }
3479a69950fSNicholas Piggin         break;
348*cde2ba34SNicholas Piggin 
349*cde2ba34SNicholas Piggin     case TOD_MOVE_TOD_TO_TB_REG:
350*cde2ba34SNicholas Piggin         /*
351*cde2ba34SNicholas Piggin          * XXX: it should be a cleaner model to have this drive a SCOM
352*cde2ba34SNicholas Piggin          * transaction to the target address, and implement the state machine
353*cde2ba34SNicholas Piggin          * in the PnvCore. For now, this hack makes things work.
354*cde2ba34SNicholas Piggin          */
355*cde2ba34SNicholas Piggin         if (chiptod->tod_state != tod_running) {
356*cde2ba34SNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
357*cde2ba34SNicholas Piggin                           " TOD_MOVE_TOD_TO_TB_REG in bad state %d\n",
358*cde2ba34SNicholas Piggin                           chiptod->tod_state);
359*cde2ba34SNicholas Piggin         } else if (!(val & PPC_BIT(0))) {
360*cde2ba34SNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
361*cde2ba34SNicholas Piggin                           " TOD_MOVE_TOD_TO_TB_REG with bad val 0x%" PRIx64"\n",
362*cde2ba34SNicholas Piggin                           val);
363*cde2ba34SNicholas Piggin         } else if (chiptod->slave_pc_target == NULL) {
364*cde2ba34SNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
365*cde2ba34SNicholas Piggin                           " TOD_MOVE_TOD_TO_TB_REG with no slave target\n");
366*cde2ba34SNicholas Piggin         } else {
367*cde2ba34SNicholas Piggin             PowerPCCPU *cpu = chiptod->slave_pc_target->threads[0];
368*cde2ba34SNicholas Piggin             CPUPPCState *env = &cpu->env;
369*cde2ba34SNicholas Piggin 
370*cde2ba34SNicholas Piggin             /*
371*cde2ba34SNicholas Piggin              * Moving TOD to TB will set the TB of all threads in a
372*cde2ba34SNicholas Piggin              * core, so skiboot only does this once per thread0, so
373*cde2ba34SNicholas Piggin              * that is where we keep the timebase state machine.
374*cde2ba34SNicholas Piggin              *
375*cde2ba34SNicholas Piggin              * It is likely possible for TBST to be driven from other
376*cde2ba34SNicholas Piggin              * threads in the core, but for now we only implement it for
377*cde2ba34SNicholas Piggin              * thread 0.
378*cde2ba34SNicholas Piggin              */
379*cde2ba34SNicholas Piggin 
380*cde2ba34SNicholas Piggin             if (env->pnv_tod_tbst.tb_ready_for_tod) {
381*cde2ba34SNicholas Piggin                 env->pnv_tod_tbst.tod_sent_to_tb = 1;
382*cde2ba34SNicholas Piggin             } else {
383*cde2ba34SNicholas Piggin                 qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
384*cde2ba34SNicholas Piggin                               " TOD_MOVE_TOD_TO_TB_REG with TB not ready to"
385*cde2ba34SNicholas Piggin                               " receive TOD\n");
386*cde2ba34SNicholas Piggin             }
387*cde2ba34SNicholas Piggin         }
388*cde2ba34SNicholas Piggin         break;
3899a69950fSNicholas Piggin     case TOD_START_TOD_REG:
3909a69950fSNicholas Piggin         if (chiptod->tod_state != tod_stopped) {
3919a69950fSNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: LOAD_TOG_REG in "
3929a69950fSNicholas Piggin                           " state %d, should be in 1 (TOD_STOPPED)\n",
3939a69950fSNicholas Piggin                           chiptod->tod_state);
3949a69950fSNicholas Piggin         } else {
3959a69950fSNicholas Piggin             chiptod->tod_state = tod_running;
3969a69950fSNicholas Piggin         }
3979a69950fSNicholas Piggin         break;
3989a69950fSNicholas Piggin     case TOD_TX_TTYPE_4_REG:
3999a69950fSNicholas Piggin     case TOD_TX_TTYPE_5_REG:
4009a69950fSNicholas Piggin         pctc->broadcast_ttype(chiptod, offset);
4019a69950fSNicholas Piggin         break;
4029a69950fSNicholas Piggin     default:
4039a69950fSNicholas Piggin         qemu_log_mask(LOG_UNIMP, "pnv_chiptod: unimplemented register: Ox%"
4049a69950fSNicholas Piggin                       HWADDR_PRIx "\n", addr >> 3);
4059a69950fSNicholas Piggin     }
4069a69950fSNicholas Piggin }
4079a69950fSNicholas Piggin 
4089a69950fSNicholas Piggin static const MemoryRegionOps pnv_chiptod_xscom_ops = {
4099a69950fSNicholas Piggin     .read = pnv_chiptod_xscom_read,
4109a69950fSNicholas Piggin     .write = pnv_chiptod_xscom_write,
4119a69950fSNicholas Piggin     .valid.min_access_size = 8,
4129a69950fSNicholas Piggin     .valid.max_access_size = 8,
4139a69950fSNicholas Piggin     .impl.min_access_size = 8,
4149a69950fSNicholas Piggin     .impl.max_access_size = 8,
4159a69950fSNicholas Piggin     .endianness = DEVICE_BIG_ENDIAN,
4169a69950fSNicholas Piggin };
4179a69950fSNicholas Piggin 
4189a69950fSNicholas Piggin static int pnv_chiptod_dt_xscom(PnvXScomInterface *dev, void *fdt,
4199a69950fSNicholas Piggin                                 int xscom_offset,
4209a69950fSNicholas Piggin                                 const char compat[], size_t compat_size)
4219a69950fSNicholas Piggin {
4229a69950fSNicholas Piggin     PnvChipTOD *chiptod = PNV_CHIPTOD(dev);
4239a69950fSNicholas Piggin     g_autofree char *name = NULL;
4249a69950fSNicholas Piggin     int offset;
4259a69950fSNicholas Piggin     uint32_t chiptod_pcba = PNV9_XSCOM_CHIPTOD_BASE;
4269a69950fSNicholas Piggin     uint32_t reg[] = {
4279a69950fSNicholas Piggin         cpu_to_be32(chiptod_pcba),
4289a69950fSNicholas Piggin         cpu_to_be32(PNV9_XSCOM_CHIPTOD_SIZE)
4299a69950fSNicholas Piggin     };
4309a69950fSNicholas Piggin 
4319a69950fSNicholas Piggin     name = g_strdup_printf("chiptod@%x", chiptod_pcba);
4329a69950fSNicholas Piggin     offset = fdt_add_subnode(fdt, xscom_offset, name);
4339a69950fSNicholas Piggin     _FDT(offset);
4349a69950fSNicholas Piggin 
4359a69950fSNicholas Piggin     if (chiptod->primary) {
4369a69950fSNicholas Piggin         _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0)));
4379a69950fSNicholas Piggin     } else if (chiptod->secondary) {
4389a69950fSNicholas Piggin         _FDT((fdt_setprop(fdt, offset, "secondary", NULL, 0)));
4399a69950fSNicholas Piggin     }
4409a69950fSNicholas Piggin 
4419a69950fSNicholas Piggin     _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
4429a69950fSNicholas Piggin     _FDT((fdt_setprop(fdt, offset, "compatible", compat, compat_size)));
4439a69950fSNicholas Piggin     return 0;
4449a69950fSNicholas Piggin }
4459a69950fSNicholas Piggin 
4469a69950fSNicholas Piggin static int pnv_chiptod_power9_dt_xscom(PnvXScomInterface *dev, void *fdt,
4479a69950fSNicholas Piggin                              int xscom_offset)
4489a69950fSNicholas Piggin {
4499a69950fSNicholas Piggin     const char compat[] = "ibm,power-chiptod\0ibm,power9-chiptod";
4509a69950fSNicholas Piggin 
4519a69950fSNicholas Piggin     return pnv_chiptod_dt_xscom(dev, fdt, xscom_offset, compat, sizeof(compat));
4529a69950fSNicholas Piggin }
4539a69950fSNicholas Piggin 
4549a69950fSNicholas Piggin static Property pnv_chiptod_properties[] = {
4559a69950fSNicholas Piggin     DEFINE_PROP_BOOL("primary", PnvChipTOD, primary, false),
4569a69950fSNicholas Piggin     DEFINE_PROP_BOOL("secondary", PnvChipTOD, secondary, false),
4579a69950fSNicholas Piggin     DEFINE_PROP_LINK("chip", PnvChipTOD , chip, TYPE_PNV_CHIP, PnvChip *),
4589a69950fSNicholas Piggin     DEFINE_PROP_END_OF_LIST(),
4599a69950fSNicholas Piggin };
4609a69950fSNicholas Piggin 
4619a69950fSNicholas Piggin static void pnv_chiptod_power9_class_init(ObjectClass *klass, void *data)
4629a69950fSNicholas Piggin {
4639a69950fSNicholas Piggin     PnvChipTODClass *pctc = PNV_CHIPTOD_CLASS(klass);
4649a69950fSNicholas Piggin     DeviceClass *dc = DEVICE_CLASS(klass);
4659a69950fSNicholas Piggin     PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
4669a69950fSNicholas Piggin 
4679a69950fSNicholas Piggin     dc->desc = "PowerNV ChipTOD Controller (POWER9)";
4689a69950fSNicholas Piggin     device_class_set_props(dc, pnv_chiptod_properties);
4699a69950fSNicholas Piggin 
4709a69950fSNicholas Piggin     xdc->dt_xscom = pnv_chiptod_power9_dt_xscom;
4719a69950fSNicholas Piggin 
4729a69950fSNicholas Piggin     pctc->broadcast_ttype = chiptod_power9_broadcast_ttype;
473*cde2ba34SNicholas Piggin     pctc->tx_ttype_target = chiptod_power9_tx_ttype_target;
4749a69950fSNicholas Piggin 
4759a69950fSNicholas Piggin     pctc->xscom_size = PNV_XSCOM_CHIPTOD_SIZE;
4769a69950fSNicholas Piggin }
4779a69950fSNicholas Piggin 
4789a69950fSNicholas Piggin static const TypeInfo pnv_chiptod_power9_type_info = {
4799a69950fSNicholas Piggin     .name          = TYPE_PNV9_CHIPTOD,
4809a69950fSNicholas Piggin     .parent        = TYPE_PNV_CHIPTOD,
4819a69950fSNicholas Piggin     .instance_size = sizeof(PnvChipTOD),
4829a69950fSNicholas Piggin     .class_init    = pnv_chiptod_power9_class_init,
4839a69950fSNicholas Piggin     .interfaces    = (InterfaceInfo[]) {
4849a69950fSNicholas Piggin         { TYPE_PNV_XSCOM_INTERFACE },
4859a69950fSNicholas Piggin         { }
4869a69950fSNicholas Piggin     }
4879a69950fSNicholas Piggin };
4889a69950fSNicholas Piggin 
4899a69950fSNicholas Piggin static int pnv_chiptod_power10_dt_xscom(PnvXScomInterface *dev, void *fdt,
4909a69950fSNicholas Piggin                              int xscom_offset)
4919a69950fSNicholas Piggin {
4929a69950fSNicholas Piggin     const char compat[] = "ibm,power-chiptod\0ibm,power10-chiptod";
4939a69950fSNicholas Piggin 
4949a69950fSNicholas Piggin     return pnv_chiptod_dt_xscom(dev, fdt, xscom_offset, compat, sizeof(compat));
4959a69950fSNicholas Piggin }
4969a69950fSNicholas Piggin 
4979a69950fSNicholas Piggin static void pnv_chiptod_power10_class_init(ObjectClass *klass, void *data)
4989a69950fSNicholas Piggin {
4999a69950fSNicholas Piggin     PnvChipTODClass *pctc = PNV_CHIPTOD_CLASS(klass);
5009a69950fSNicholas Piggin     DeviceClass *dc = DEVICE_CLASS(klass);
5019a69950fSNicholas Piggin     PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
5029a69950fSNicholas Piggin 
5039a69950fSNicholas Piggin     dc->desc = "PowerNV ChipTOD Controller (POWER10)";
5049a69950fSNicholas Piggin     device_class_set_props(dc, pnv_chiptod_properties);
5059a69950fSNicholas Piggin 
5069a69950fSNicholas Piggin     xdc->dt_xscom = pnv_chiptod_power10_dt_xscom;
5079a69950fSNicholas Piggin 
5089a69950fSNicholas Piggin     pctc->broadcast_ttype = chiptod_power10_broadcast_ttype;
509*cde2ba34SNicholas Piggin     pctc->tx_ttype_target = chiptod_power10_tx_ttype_target;
5109a69950fSNicholas Piggin 
5119a69950fSNicholas Piggin     pctc->xscom_size = PNV_XSCOM_CHIPTOD_SIZE;
5129a69950fSNicholas Piggin }
5139a69950fSNicholas Piggin 
5149a69950fSNicholas Piggin static const TypeInfo pnv_chiptod_power10_type_info = {
5159a69950fSNicholas Piggin     .name          = TYPE_PNV10_CHIPTOD,
5169a69950fSNicholas Piggin     .parent        = TYPE_PNV_CHIPTOD,
5179a69950fSNicholas Piggin     .instance_size = sizeof(PnvChipTOD),
5189a69950fSNicholas Piggin     .class_init    = pnv_chiptod_power10_class_init,
5199a69950fSNicholas Piggin     .interfaces    = (InterfaceInfo[]) {
5209a69950fSNicholas Piggin         { TYPE_PNV_XSCOM_INTERFACE },
5219a69950fSNicholas Piggin         { }
5229a69950fSNicholas Piggin     }
5239a69950fSNicholas Piggin };
5249a69950fSNicholas Piggin 
5259a69950fSNicholas Piggin static void pnv_chiptod_reset(void *dev)
5269a69950fSNicholas Piggin {
5279a69950fSNicholas Piggin     PnvChipTOD *chiptod = PNV_CHIPTOD(dev);
5289a69950fSNicholas Piggin 
5299a69950fSNicholas Piggin     chiptod->pss_mss_ctrl_reg = 0;
5309a69950fSNicholas Piggin     if (chiptod->primary) {
5319a69950fSNicholas Piggin         chiptod->pss_mss_ctrl_reg |= PPC_BIT(1); /* TOD is master */
5329a69950fSNicholas Piggin     }
5339a69950fSNicholas Piggin     /* Drawer is master (we do not simulate multi-drawer) */
5349a69950fSNicholas Piggin     chiptod->pss_mss_ctrl_reg |= PPC_BIT(2);
5359a69950fSNicholas Piggin 
5369a69950fSNicholas Piggin     chiptod->tod_error = 0;
5379a69950fSNicholas Piggin     chiptod->tod_state = tod_error;
5389a69950fSNicholas Piggin }
5399a69950fSNicholas Piggin 
5409a69950fSNicholas Piggin static void pnv_chiptod_realize(DeviceState *dev, Error **errp)
5419a69950fSNicholas Piggin {
5429a69950fSNicholas Piggin     PnvChipTOD *chiptod = PNV_CHIPTOD(dev);
5439a69950fSNicholas Piggin     PnvChipTODClass *pctc = PNV_CHIPTOD_GET_CLASS(chiptod);
5449a69950fSNicholas Piggin 
5459a69950fSNicholas Piggin     /* XScom regions for ChipTOD registers */
5469a69950fSNicholas Piggin     pnv_xscom_region_init(&chiptod->xscom_regs, OBJECT(dev),
5479a69950fSNicholas Piggin                           &pnv_chiptod_xscom_ops, chiptod, "xscom-chiptod",
5489a69950fSNicholas Piggin                           pctc->xscom_size);
5499a69950fSNicholas Piggin 
5509a69950fSNicholas Piggin     qemu_register_reset(pnv_chiptod_reset, chiptod);
5519a69950fSNicholas Piggin }
5529a69950fSNicholas Piggin 
5539a69950fSNicholas Piggin static void pnv_chiptod_unrealize(DeviceState *dev)
5549a69950fSNicholas Piggin {
5559a69950fSNicholas Piggin     PnvChipTOD *chiptod = PNV_CHIPTOD(dev);
5569a69950fSNicholas Piggin 
5579a69950fSNicholas Piggin     qemu_unregister_reset(pnv_chiptod_reset, chiptod);
5589a69950fSNicholas Piggin }
5599a69950fSNicholas Piggin 
5609a69950fSNicholas Piggin static void pnv_chiptod_class_init(ObjectClass *klass, void *data)
5619a69950fSNicholas Piggin {
5629a69950fSNicholas Piggin     DeviceClass *dc = DEVICE_CLASS(klass);
5639a69950fSNicholas Piggin 
5649a69950fSNicholas Piggin     dc->realize = pnv_chiptod_realize;
5659a69950fSNicholas Piggin     dc->unrealize = pnv_chiptod_unrealize;
5669a69950fSNicholas Piggin     dc->desc = "PowerNV ChipTOD Controller";
5679a69950fSNicholas Piggin     dc->user_creatable = false;
5689a69950fSNicholas Piggin }
5699a69950fSNicholas Piggin 
5709a69950fSNicholas Piggin static const TypeInfo pnv_chiptod_type_info = {
5719a69950fSNicholas Piggin     .name          = TYPE_PNV_CHIPTOD,
5729a69950fSNicholas Piggin     .parent        = TYPE_DEVICE,
5739a69950fSNicholas Piggin     .instance_size = sizeof(PnvChipTOD),
5749a69950fSNicholas Piggin     .class_init    = pnv_chiptod_class_init,
5759a69950fSNicholas Piggin     .class_size    = sizeof(PnvChipTODClass),
5769a69950fSNicholas Piggin     .abstract      = true,
5779a69950fSNicholas Piggin };
5789a69950fSNicholas Piggin 
5799a69950fSNicholas Piggin static void pnv_chiptod_register_types(void)
5809a69950fSNicholas Piggin {
5819a69950fSNicholas Piggin     type_register_static(&pnv_chiptod_type_info);
5829a69950fSNicholas Piggin     type_register_static(&pnv_chiptod_power9_type_info);
5839a69950fSNicholas Piggin     type_register_static(&pnv_chiptod_power10_type_info);
5849a69950fSNicholas Piggin }
5859a69950fSNicholas Piggin 
5869a69950fSNicholas Piggin type_init(pnv_chiptod_register_types);
587