xref: /openbmc/qemu/hw/ppc/pnv_chiptod.c (revision 0ca94b2f)
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 
pnv_chiptod_xscom_read(void * opaque,hwaddr addr,unsigned size)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 
chiptod_receive_ttype(PnvChipTOD * chiptod,uint32_t trigger)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 
chiptod_power9_broadcast_ttype(PnvChipTOD * sender,uint32_t trigger)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 
chiptod_power10_broadcast_ttype(PnvChipTOD * sender,uint32_t trigger)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 
pnv_chip_get_core_by_xscom_base(PnvChip * chip,uint32_t xscom_base)213cde2ba34SNicholas Piggin static PnvCore *pnv_chip_get_core_by_xscom_base(PnvChip *chip,
214cde2ba34SNicholas Piggin                                                 uint32_t xscom_base)
215cde2ba34SNicholas Piggin {
216cde2ba34SNicholas Piggin     PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
217cde2ba34SNicholas Piggin     int i;
218cde2ba34SNicholas Piggin 
219cde2ba34SNicholas Piggin     for (i = 0; i < chip->nr_cores; i++) {
220cde2ba34SNicholas Piggin         PnvCore *pc = chip->cores[i];
221cde2ba34SNicholas Piggin         CPUCore *cc = CPU_CORE(pc);
222cde2ba34SNicholas Piggin         int core_hwid = cc->core_id;
223cde2ba34SNicholas Piggin 
224cde2ba34SNicholas Piggin         if (pcc->xscom_core_base(chip, core_hwid) == xscom_base) {
225cde2ba34SNicholas Piggin             return pc;
226cde2ba34SNicholas Piggin         }
227cde2ba34SNicholas Piggin     }
228cde2ba34SNicholas Piggin     return NULL;
229cde2ba34SNicholas Piggin }
230cde2ba34SNicholas Piggin 
chiptod_power9_tx_ttype_target(PnvChipTOD * chiptod,uint64_t val)231cde2ba34SNicholas Piggin static PnvCore *chiptod_power9_tx_ttype_target(PnvChipTOD *chiptod,
232cde2ba34SNicholas Piggin                                                uint64_t val)
233cde2ba34SNicholas Piggin {
234cde2ba34SNicholas Piggin     /*
235cde2ba34SNicholas Piggin      * skiboot uses Core ID for P9, though SCOM should work too.
236cde2ba34SNicholas Piggin      */
237cde2ba34SNicholas Piggin     if (val & PPC_BIT(35)) { /* SCOM addressing */
238cde2ba34SNicholas Piggin         uint32_t addr = val >> 32;
239cde2ba34SNicholas Piggin         uint32_t reg = addr & 0xfff;
240cde2ba34SNicholas Piggin 
241cde2ba34SNicholas Piggin         if (reg != PC_TOD) {
242cde2ba34SNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: SCOM addressing: "
243cde2ba34SNicholas Piggin                           "unimplemented slave register 0x%" PRIx32 "\n", reg);
244cde2ba34SNicholas Piggin             return NULL;
245cde2ba34SNicholas Piggin         }
246cde2ba34SNicholas Piggin 
247cde2ba34SNicholas Piggin         return pnv_chip_get_core_by_xscom_base(chiptod->chip, addr & ~0xfff);
248cde2ba34SNicholas Piggin 
249cde2ba34SNicholas Piggin     } else { /* Core ID addressing */
250cde2ba34SNicholas Piggin         uint32_t core_id = GETFIELD(TOD_TX_TTYPE_PIB_SLAVE_ADDR, val) & 0x1f;
251cde2ba34SNicholas Piggin         return pnv_chip_find_core(chiptod->chip, core_id);
252cde2ba34SNicholas Piggin     }
253cde2ba34SNicholas Piggin }
254cde2ba34SNicholas Piggin 
chiptod_power10_tx_ttype_target(PnvChipTOD * chiptod,uint64_t val)255cde2ba34SNicholas Piggin static PnvCore *chiptod_power10_tx_ttype_target(PnvChipTOD *chiptod,
256cde2ba34SNicholas Piggin                                                uint64_t val)
257cde2ba34SNicholas Piggin {
258cde2ba34SNicholas Piggin     /*
259cde2ba34SNicholas Piggin      * skiboot uses SCOM for P10 because Core ID was unable to be made to
260cde2ba34SNicholas Piggin      * work correctly. For this reason only SCOM addressing is implemented.
261cde2ba34SNicholas Piggin      */
262cde2ba34SNicholas Piggin     if (val & PPC_BIT(35)) { /* SCOM addressing */
263cde2ba34SNicholas Piggin         uint32_t addr = val >> 32;
264cde2ba34SNicholas Piggin         uint32_t reg = addr & 0xfff;
265cde2ba34SNicholas Piggin 
266cde2ba34SNicholas Piggin         if (reg != PC_TOD) {
267cde2ba34SNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: SCOM addressing: "
268cde2ba34SNicholas Piggin                           "unimplemented slave register 0x%" PRIx32 "\n", reg);
269cde2ba34SNicholas Piggin             return NULL;
270cde2ba34SNicholas Piggin         }
271cde2ba34SNicholas Piggin 
272cde2ba34SNicholas Piggin         /*
273cde2ba34SNicholas Piggin          * This may not deal with P10 big-core addressing at the moment.
274cde2ba34SNicholas Piggin          * The big-core code in skiboot syncs small cores, but it targets
275cde2ba34SNicholas Piggin          * the even PIR (first small-core) when syncing second small-core.
276cde2ba34SNicholas Piggin          */
277cde2ba34SNicholas Piggin         return pnv_chip_get_core_by_xscom_base(chiptod->chip, addr & ~0xfff);
278cde2ba34SNicholas Piggin 
279cde2ba34SNicholas Piggin     } else { /* Core ID addressing */
280cde2ba34SNicholas Piggin         qemu_log_mask(LOG_UNIMP, "pnv_chiptod: TX TTYPE Core ID "
281cde2ba34SNicholas Piggin                       "addressing is not implemented for POWER10\n");
282cde2ba34SNicholas Piggin         return NULL;
283cde2ba34SNicholas Piggin     }
284cde2ba34SNicholas Piggin }
285cde2ba34SNicholas Piggin 
pnv_chiptod_xscom_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)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 
307cde2ba34SNicholas Piggin     case TOD_TX_TTYPE_CTRL_REG:
308cde2ba34SNicholas Piggin         /*
309cde2ba34SNicholas Piggin          * This register sets the target of the TOD value transfer initiated
310cde2ba34SNicholas Piggin          * by TOD_MOVE_TOD_TO_TB. The TOD is able to send the address to
311cde2ba34SNicholas Piggin          * any target register, though in practice only the PC TOD register
312cde2ba34SNicholas Piggin          * should be used. ChipTOD has a "SCOM addressing" mode which fully
313cde2ba34SNicholas Piggin          * specifies the SCOM address, and a core-ID mode which uses the
314cde2ba34SNicholas Piggin          * core ID to target the PC TOD for a given core.
315cde2ba34SNicholas Piggin          */
316cde2ba34SNicholas Piggin         chiptod->slave_pc_target = pctc->tx_ttype_target(chiptod, val);
317cde2ba34SNicholas Piggin         if (!chiptod->slave_pc_target) {
318cde2ba34SNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
319cde2ba34SNicholas Piggin                           " TOD_TX_TTYPE_CTRL_REG val 0x%" PRIx64
320cde2ba34SNicholas Piggin                           " invalid slave address\n", val);
321cde2ba34SNicholas Piggin         }
322cde2ba34SNicholas 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;
348cde2ba34SNicholas Piggin 
349cde2ba34SNicholas Piggin     case TOD_MOVE_TOD_TO_TB_REG:
350cde2ba34SNicholas Piggin         /*
351cde2ba34SNicholas Piggin          * XXX: it should be a cleaner model to have this drive a SCOM
352cde2ba34SNicholas Piggin          * transaction to the target address, and implement the state machine
353cde2ba34SNicholas Piggin          * in the PnvCore. For now, this hack makes things work.
354cde2ba34SNicholas Piggin          */
355cde2ba34SNicholas Piggin         if (chiptod->tod_state != tod_running) {
356cde2ba34SNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
357cde2ba34SNicholas Piggin                           " TOD_MOVE_TOD_TO_TB_REG in bad state %d\n",
358cde2ba34SNicholas Piggin                           chiptod->tod_state);
359cde2ba34SNicholas Piggin         } else if (!(val & PPC_BIT(0))) {
360cde2ba34SNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
361cde2ba34SNicholas Piggin                           " TOD_MOVE_TOD_TO_TB_REG with bad val 0x%" PRIx64"\n",
362cde2ba34SNicholas Piggin                           val);
363cde2ba34SNicholas Piggin         } else if (chiptod->slave_pc_target == NULL) {
364cde2ba34SNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
365cde2ba34SNicholas Piggin                           " TOD_MOVE_TOD_TO_TB_REG with no slave target\n");
366cde2ba34SNicholas Piggin         } else {
367*0ca94b2fSNicholas Piggin             PnvCore *pc = chiptod->slave_pc_target;
368cde2ba34SNicholas Piggin 
369cde2ba34SNicholas Piggin             /*
370cde2ba34SNicholas Piggin              * Moving TOD to TB will set the TB of all threads in a
371cde2ba34SNicholas Piggin              * core, so skiboot only does this once per thread0, so
372cde2ba34SNicholas Piggin              * that is where we keep the timebase state machine.
373cde2ba34SNicholas Piggin              *
374cde2ba34SNicholas Piggin              * It is likely possible for TBST to be driven from other
375cde2ba34SNicholas Piggin              * threads in the core, but for now we only implement it for
376cde2ba34SNicholas Piggin              * thread 0.
377cde2ba34SNicholas Piggin              */
378cde2ba34SNicholas Piggin 
379*0ca94b2fSNicholas Piggin             if (pc->tod_state.tb_ready_for_tod) {
380*0ca94b2fSNicholas Piggin                 pc->tod_state.tod_sent_to_tb = 1;
381cde2ba34SNicholas Piggin             } else {
382cde2ba34SNicholas Piggin                 qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
383cde2ba34SNicholas Piggin                               " TOD_MOVE_TOD_TO_TB_REG with TB not ready to"
384cde2ba34SNicholas Piggin                               " receive TOD\n");
385cde2ba34SNicholas Piggin             }
386cde2ba34SNicholas Piggin         }
387cde2ba34SNicholas Piggin         break;
3889a69950fSNicholas Piggin     case TOD_START_TOD_REG:
3899a69950fSNicholas Piggin         if (chiptod->tod_state != tod_stopped) {
3909a69950fSNicholas Piggin             qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: LOAD_TOG_REG in "
3919a69950fSNicholas Piggin                           " state %d, should be in 1 (TOD_STOPPED)\n",
3929a69950fSNicholas Piggin                           chiptod->tod_state);
3939a69950fSNicholas Piggin         } else {
3949a69950fSNicholas Piggin             chiptod->tod_state = tod_running;
3959a69950fSNicholas Piggin         }
3969a69950fSNicholas Piggin         break;
3979a69950fSNicholas Piggin     case TOD_TX_TTYPE_4_REG:
3989a69950fSNicholas Piggin     case TOD_TX_TTYPE_5_REG:
3999a69950fSNicholas Piggin         pctc->broadcast_ttype(chiptod, offset);
4009a69950fSNicholas Piggin         break;
4019a69950fSNicholas Piggin     default:
4029a69950fSNicholas Piggin         qemu_log_mask(LOG_UNIMP, "pnv_chiptod: unimplemented register: Ox%"
4039a69950fSNicholas Piggin                       HWADDR_PRIx "\n", addr >> 3);
4049a69950fSNicholas Piggin     }
4059a69950fSNicholas Piggin }
4069a69950fSNicholas Piggin 
4079a69950fSNicholas Piggin static const MemoryRegionOps pnv_chiptod_xscom_ops = {
4089a69950fSNicholas Piggin     .read = pnv_chiptod_xscom_read,
4099a69950fSNicholas Piggin     .write = pnv_chiptod_xscom_write,
4109a69950fSNicholas Piggin     .valid.min_access_size = 8,
4119a69950fSNicholas Piggin     .valid.max_access_size = 8,
4129a69950fSNicholas Piggin     .impl.min_access_size = 8,
4139a69950fSNicholas Piggin     .impl.max_access_size = 8,
4149a69950fSNicholas Piggin     .endianness = DEVICE_BIG_ENDIAN,
4159a69950fSNicholas Piggin };
4169a69950fSNicholas Piggin 
pnv_chiptod_dt_xscom(PnvXScomInterface * dev,void * fdt,int xscom_offset,const char compat[],size_t compat_size)4179a69950fSNicholas Piggin static int pnv_chiptod_dt_xscom(PnvXScomInterface *dev, void *fdt,
4189a69950fSNicholas Piggin                                 int xscom_offset,
4199a69950fSNicholas Piggin                                 const char compat[], size_t compat_size)
4209a69950fSNicholas Piggin {
4219a69950fSNicholas Piggin     PnvChipTOD *chiptod = PNV_CHIPTOD(dev);
4229a69950fSNicholas Piggin     g_autofree char *name = NULL;
4239a69950fSNicholas Piggin     int offset;
4249a69950fSNicholas Piggin     uint32_t chiptod_pcba = PNV9_XSCOM_CHIPTOD_BASE;
4259a69950fSNicholas Piggin     uint32_t reg[] = {
4269a69950fSNicholas Piggin         cpu_to_be32(chiptod_pcba),
4279a69950fSNicholas Piggin         cpu_to_be32(PNV9_XSCOM_CHIPTOD_SIZE)
4289a69950fSNicholas Piggin     };
4299a69950fSNicholas Piggin 
4309a69950fSNicholas Piggin     name = g_strdup_printf("chiptod@%x", chiptod_pcba);
4319a69950fSNicholas Piggin     offset = fdt_add_subnode(fdt, xscom_offset, name);
4329a69950fSNicholas Piggin     _FDT(offset);
4339a69950fSNicholas Piggin 
4349a69950fSNicholas Piggin     if (chiptod->primary) {
4359a69950fSNicholas Piggin         _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0)));
4369a69950fSNicholas Piggin     } else if (chiptod->secondary) {
4379a69950fSNicholas Piggin         _FDT((fdt_setprop(fdt, offset, "secondary", NULL, 0)));
4389a69950fSNicholas Piggin     }
4399a69950fSNicholas Piggin 
4409a69950fSNicholas Piggin     _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
4419a69950fSNicholas Piggin     _FDT((fdt_setprop(fdt, offset, "compatible", compat, compat_size)));
4429a69950fSNicholas Piggin     return 0;
4439a69950fSNicholas Piggin }
4449a69950fSNicholas Piggin 
pnv_chiptod_power9_dt_xscom(PnvXScomInterface * dev,void * fdt,int xscom_offset)4459a69950fSNicholas Piggin static int pnv_chiptod_power9_dt_xscom(PnvXScomInterface *dev, void *fdt,
4469a69950fSNicholas Piggin                              int xscom_offset)
4479a69950fSNicholas Piggin {
4489a69950fSNicholas Piggin     const char compat[] = "ibm,power-chiptod\0ibm,power9-chiptod";
4499a69950fSNicholas Piggin 
4509a69950fSNicholas Piggin     return pnv_chiptod_dt_xscom(dev, fdt, xscom_offset, compat, sizeof(compat));
4519a69950fSNicholas Piggin }
4529a69950fSNicholas Piggin 
4539a69950fSNicholas Piggin static Property pnv_chiptod_properties[] = {
4549a69950fSNicholas Piggin     DEFINE_PROP_BOOL("primary", PnvChipTOD, primary, false),
4559a69950fSNicholas Piggin     DEFINE_PROP_BOOL("secondary", PnvChipTOD, secondary, false),
4569a69950fSNicholas Piggin     DEFINE_PROP_LINK("chip", PnvChipTOD , chip, TYPE_PNV_CHIP, PnvChip *),
4579a69950fSNicholas Piggin     DEFINE_PROP_END_OF_LIST(),
4589a69950fSNicholas Piggin };
4599a69950fSNicholas Piggin 
pnv_chiptod_power9_class_init(ObjectClass * klass,void * data)4609a69950fSNicholas Piggin static void pnv_chiptod_power9_class_init(ObjectClass *klass, void *data)
4619a69950fSNicholas Piggin {
4629a69950fSNicholas Piggin     PnvChipTODClass *pctc = PNV_CHIPTOD_CLASS(klass);
4639a69950fSNicholas Piggin     DeviceClass *dc = DEVICE_CLASS(klass);
4649a69950fSNicholas Piggin     PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
4659a69950fSNicholas Piggin 
4669a69950fSNicholas Piggin     dc->desc = "PowerNV ChipTOD Controller (POWER9)";
4679a69950fSNicholas Piggin     device_class_set_props(dc, pnv_chiptod_properties);
4689a69950fSNicholas Piggin 
4699a69950fSNicholas Piggin     xdc->dt_xscom = pnv_chiptod_power9_dt_xscom;
4709a69950fSNicholas Piggin 
4719a69950fSNicholas Piggin     pctc->broadcast_ttype = chiptod_power9_broadcast_ttype;
472cde2ba34SNicholas Piggin     pctc->tx_ttype_target = chiptod_power9_tx_ttype_target;
4739a69950fSNicholas Piggin 
4749a69950fSNicholas Piggin     pctc->xscom_size = PNV_XSCOM_CHIPTOD_SIZE;
4759a69950fSNicholas Piggin }
4769a69950fSNicholas Piggin 
4779a69950fSNicholas Piggin static const TypeInfo pnv_chiptod_power9_type_info = {
4789a69950fSNicholas Piggin     .name          = TYPE_PNV9_CHIPTOD,
4799a69950fSNicholas Piggin     .parent        = TYPE_PNV_CHIPTOD,
4809a69950fSNicholas Piggin     .instance_size = sizeof(PnvChipTOD),
4819a69950fSNicholas Piggin     .class_init    = pnv_chiptod_power9_class_init,
4829a69950fSNicholas Piggin     .interfaces    = (InterfaceInfo[]) {
4839a69950fSNicholas Piggin         { TYPE_PNV_XSCOM_INTERFACE },
4849a69950fSNicholas Piggin         { }
4859a69950fSNicholas Piggin     }
4869a69950fSNicholas Piggin };
4879a69950fSNicholas Piggin 
pnv_chiptod_power10_dt_xscom(PnvXScomInterface * dev,void * fdt,int xscom_offset)4889a69950fSNicholas Piggin static int pnv_chiptod_power10_dt_xscom(PnvXScomInterface *dev, void *fdt,
4899a69950fSNicholas Piggin                              int xscom_offset)
4909a69950fSNicholas Piggin {
4919a69950fSNicholas Piggin     const char compat[] = "ibm,power-chiptod\0ibm,power10-chiptod";
4929a69950fSNicholas Piggin 
4939a69950fSNicholas Piggin     return pnv_chiptod_dt_xscom(dev, fdt, xscom_offset, compat, sizeof(compat));
4949a69950fSNicholas Piggin }
4959a69950fSNicholas Piggin 
pnv_chiptod_power10_class_init(ObjectClass * klass,void * data)4969a69950fSNicholas Piggin static void pnv_chiptod_power10_class_init(ObjectClass *klass, void *data)
4979a69950fSNicholas Piggin {
4989a69950fSNicholas Piggin     PnvChipTODClass *pctc = PNV_CHIPTOD_CLASS(klass);
4999a69950fSNicholas Piggin     DeviceClass *dc = DEVICE_CLASS(klass);
5009a69950fSNicholas Piggin     PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
5019a69950fSNicholas Piggin 
5029a69950fSNicholas Piggin     dc->desc = "PowerNV ChipTOD Controller (POWER10)";
5039a69950fSNicholas Piggin     device_class_set_props(dc, pnv_chiptod_properties);
5049a69950fSNicholas Piggin 
5059a69950fSNicholas Piggin     xdc->dt_xscom = pnv_chiptod_power10_dt_xscom;
5069a69950fSNicholas Piggin 
5079a69950fSNicholas Piggin     pctc->broadcast_ttype = chiptod_power10_broadcast_ttype;
508cde2ba34SNicholas Piggin     pctc->tx_ttype_target = chiptod_power10_tx_ttype_target;
5099a69950fSNicholas Piggin 
5109a69950fSNicholas Piggin     pctc->xscom_size = PNV_XSCOM_CHIPTOD_SIZE;
5119a69950fSNicholas Piggin }
5129a69950fSNicholas Piggin 
5139a69950fSNicholas Piggin static const TypeInfo pnv_chiptod_power10_type_info = {
5149a69950fSNicholas Piggin     .name          = TYPE_PNV10_CHIPTOD,
5159a69950fSNicholas Piggin     .parent        = TYPE_PNV_CHIPTOD,
5169a69950fSNicholas Piggin     .instance_size = sizeof(PnvChipTOD),
5179a69950fSNicholas Piggin     .class_init    = pnv_chiptod_power10_class_init,
5189a69950fSNicholas Piggin     .interfaces    = (InterfaceInfo[]) {
5199a69950fSNicholas Piggin         { TYPE_PNV_XSCOM_INTERFACE },
5209a69950fSNicholas Piggin         { }
5219a69950fSNicholas Piggin     }
5229a69950fSNicholas Piggin };
5239a69950fSNicholas Piggin 
pnv_chiptod_reset(void * dev)5249a69950fSNicholas Piggin static void pnv_chiptod_reset(void *dev)
5259a69950fSNicholas Piggin {
5269a69950fSNicholas Piggin     PnvChipTOD *chiptod = PNV_CHIPTOD(dev);
5279a69950fSNicholas Piggin 
5289a69950fSNicholas Piggin     chiptod->pss_mss_ctrl_reg = 0;
5299a69950fSNicholas Piggin     if (chiptod->primary) {
5309a69950fSNicholas Piggin         chiptod->pss_mss_ctrl_reg |= PPC_BIT(1); /* TOD is master */
5319a69950fSNicholas Piggin     }
5329a69950fSNicholas Piggin     /* Drawer is master (we do not simulate multi-drawer) */
5339a69950fSNicholas Piggin     chiptod->pss_mss_ctrl_reg |= PPC_BIT(2);
5349a69950fSNicholas Piggin 
5359a69950fSNicholas Piggin     chiptod->tod_error = 0;
5369a69950fSNicholas Piggin     chiptod->tod_state = tod_error;
5379a69950fSNicholas Piggin }
5389a69950fSNicholas Piggin 
pnv_chiptod_realize(DeviceState * dev,Error ** errp)5399a69950fSNicholas Piggin static void pnv_chiptod_realize(DeviceState *dev, Error **errp)
5409a69950fSNicholas Piggin {
5419a69950fSNicholas Piggin     PnvChipTOD *chiptod = PNV_CHIPTOD(dev);
5429a69950fSNicholas Piggin     PnvChipTODClass *pctc = PNV_CHIPTOD_GET_CLASS(chiptod);
5439a69950fSNicholas Piggin 
5449a69950fSNicholas Piggin     /* XScom regions for ChipTOD registers */
5459a69950fSNicholas Piggin     pnv_xscom_region_init(&chiptod->xscom_regs, OBJECT(dev),
5469a69950fSNicholas Piggin                           &pnv_chiptod_xscom_ops, chiptod, "xscom-chiptod",
5479a69950fSNicholas Piggin                           pctc->xscom_size);
5489a69950fSNicholas Piggin 
5499a69950fSNicholas Piggin     qemu_register_reset(pnv_chiptod_reset, chiptod);
5509a69950fSNicholas Piggin }
5519a69950fSNicholas Piggin 
pnv_chiptod_unrealize(DeviceState * dev)5529a69950fSNicholas Piggin static void pnv_chiptod_unrealize(DeviceState *dev)
5539a69950fSNicholas Piggin {
5549a69950fSNicholas Piggin     PnvChipTOD *chiptod = PNV_CHIPTOD(dev);
5559a69950fSNicholas Piggin 
5569a69950fSNicholas Piggin     qemu_unregister_reset(pnv_chiptod_reset, chiptod);
5579a69950fSNicholas Piggin }
5589a69950fSNicholas Piggin 
pnv_chiptod_class_init(ObjectClass * klass,void * data)5599a69950fSNicholas Piggin static void pnv_chiptod_class_init(ObjectClass *klass, void *data)
5609a69950fSNicholas Piggin {
5619a69950fSNicholas Piggin     DeviceClass *dc = DEVICE_CLASS(klass);
5629a69950fSNicholas Piggin 
5639a69950fSNicholas Piggin     dc->realize = pnv_chiptod_realize;
5649a69950fSNicholas Piggin     dc->unrealize = pnv_chiptod_unrealize;
5659a69950fSNicholas Piggin     dc->desc = "PowerNV ChipTOD Controller";
5669a69950fSNicholas Piggin     dc->user_creatable = false;
5679a69950fSNicholas Piggin }
5689a69950fSNicholas Piggin 
5699a69950fSNicholas Piggin static const TypeInfo pnv_chiptod_type_info = {
5709a69950fSNicholas Piggin     .name          = TYPE_PNV_CHIPTOD,
5719a69950fSNicholas Piggin     .parent        = TYPE_DEVICE,
5729a69950fSNicholas Piggin     .instance_size = sizeof(PnvChipTOD),
5739a69950fSNicholas Piggin     .class_init    = pnv_chiptod_class_init,
5749a69950fSNicholas Piggin     .class_size    = sizeof(PnvChipTODClass),
5759a69950fSNicholas Piggin     .abstract      = true,
5769a69950fSNicholas Piggin };
5779a69950fSNicholas Piggin 
pnv_chiptod_register_types(void)5789a69950fSNicholas Piggin static void pnv_chiptod_register_types(void)
5799a69950fSNicholas Piggin {
5809a69950fSNicholas Piggin     type_register_static(&pnv_chiptod_type_info);
5819a69950fSNicholas Piggin     type_register_static(&pnv_chiptod_power9_type_info);
5829a69950fSNicholas Piggin     type_register_static(&pnv_chiptod_power10_type_info);
5839a69950fSNicholas Piggin }
5849a69950fSNicholas Piggin 
5859a69950fSNicholas Piggin type_init(pnv_chiptod_register_types);
586