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" 2632cad1ffSPhilippe Mathieu-Daudé #include "system/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 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 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 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 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 { 3670ca94b2fSNicholas 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 3790ca94b2fSNicholas Piggin if (pc->tod_state.tb_ready_for_tod) { 3800ca94b2fSNicholas 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 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 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 45390f5755eSRichard Henderson static const 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 }; 4589a69950fSNicholas Piggin 459*12d1a768SPhilippe Mathieu-Daudé static void pnv_chiptod_power9_class_init(ObjectClass *klass, const void *data) 4609a69950fSNicholas Piggin { 4619a69950fSNicholas Piggin PnvChipTODClass *pctc = PNV_CHIPTOD_CLASS(klass); 4629a69950fSNicholas Piggin DeviceClass *dc = DEVICE_CLASS(klass); 4639a69950fSNicholas Piggin PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); 4649a69950fSNicholas Piggin 4659a69950fSNicholas Piggin dc->desc = "PowerNV ChipTOD Controller (POWER9)"; 4669a69950fSNicholas Piggin device_class_set_props(dc, pnv_chiptod_properties); 4679a69950fSNicholas Piggin 4689a69950fSNicholas Piggin xdc->dt_xscom = pnv_chiptod_power9_dt_xscom; 4699a69950fSNicholas Piggin 4709a69950fSNicholas Piggin pctc->broadcast_ttype = chiptod_power9_broadcast_ttype; 471cde2ba34SNicholas Piggin pctc->tx_ttype_target = chiptod_power9_tx_ttype_target; 4729a69950fSNicholas Piggin 4739a69950fSNicholas Piggin pctc->xscom_size = PNV_XSCOM_CHIPTOD_SIZE; 4749a69950fSNicholas Piggin } 4759a69950fSNicholas Piggin 4769a69950fSNicholas Piggin static const TypeInfo pnv_chiptod_power9_type_info = { 4779a69950fSNicholas Piggin .name = TYPE_PNV9_CHIPTOD, 4789a69950fSNicholas Piggin .parent = TYPE_PNV_CHIPTOD, 4799a69950fSNicholas Piggin .instance_size = sizeof(PnvChipTOD), 4809a69950fSNicholas Piggin .class_init = pnv_chiptod_power9_class_init, 4819a69950fSNicholas Piggin .interfaces = (InterfaceInfo[]) { 4829a69950fSNicholas Piggin { TYPE_PNV_XSCOM_INTERFACE }, 4839a69950fSNicholas Piggin { } 4849a69950fSNicholas Piggin } 4859a69950fSNicholas Piggin }; 4869a69950fSNicholas Piggin 4879a69950fSNicholas Piggin static int pnv_chiptod_power10_dt_xscom(PnvXScomInterface *dev, void *fdt, 4889a69950fSNicholas Piggin int xscom_offset) 4899a69950fSNicholas Piggin { 4909a69950fSNicholas Piggin const char compat[] = "ibm,power-chiptod\0ibm,power10-chiptod"; 4919a69950fSNicholas Piggin 4929a69950fSNicholas Piggin return pnv_chiptod_dt_xscom(dev, fdt, xscom_offset, compat, sizeof(compat)); 4939a69950fSNicholas Piggin } 4949a69950fSNicholas Piggin 495*12d1a768SPhilippe Mathieu-Daudé static void pnv_chiptod_power10_class_init(ObjectClass *klass, const void *data) 4969a69950fSNicholas Piggin { 4979a69950fSNicholas Piggin PnvChipTODClass *pctc = PNV_CHIPTOD_CLASS(klass); 4989a69950fSNicholas Piggin DeviceClass *dc = DEVICE_CLASS(klass); 4999a69950fSNicholas Piggin PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); 5009a69950fSNicholas Piggin 5019a69950fSNicholas Piggin dc->desc = "PowerNV ChipTOD Controller (POWER10)"; 5029a69950fSNicholas Piggin device_class_set_props(dc, pnv_chiptod_properties); 5039a69950fSNicholas Piggin 5049a69950fSNicholas Piggin xdc->dt_xscom = pnv_chiptod_power10_dt_xscom; 5059a69950fSNicholas Piggin 5069a69950fSNicholas Piggin pctc->broadcast_ttype = chiptod_power10_broadcast_ttype; 507cde2ba34SNicholas Piggin pctc->tx_ttype_target = chiptod_power10_tx_ttype_target; 5089a69950fSNicholas Piggin 5099a69950fSNicholas Piggin pctc->xscom_size = PNV_XSCOM_CHIPTOD_SIZE; 5109a69950fSNicholas Piggin } 5119a69950fSNicholas Piggin 5129a69950fSNicholas Piggin static const TypeInfo pnv_chiptod_power10_type_info = { 5139a69950fSNicholas Piggin .name = TYPE_PNV10_CHIPTOD, 5149a69950fSNicholas Piggin .parent = TYPE_PNV_CHIPTOD, 5159a69950fSNicholas Piggin .instance_size = sizeof(PnvChipTOD), 5169a69950fSNicholas Piggin .class_init = pnv_chiptod_power10_class_init, 5179a69950fSNicholas Piggin .interfaces = (InterfaceInfo[]) { 5189a69950fSNicholas Piggin { TYPE_PNV_XSCOM_INTERFACE }, 5199a69950fSNicholas Piggin { } 5209a69950fSNicholas Piggin } 5219a69950fSNicholas Piggin }; 5229a69950fSNicholas Piggin 5239a69950fSNicholas Piggin static void pnv_chiptod_reset(void *dev) 5249a69950fSNicholas Piggin { 5259a69950fSNicholas Piggin PnvChipTOD *chiptod = PNV_CHIPTOD(dev); 5269a69950fSNicholas Piggin 5279a69950fSNicholas Piggin chiptod->pss_mss_ctrl_reg = 0; 5289a69950fSNicholas Piggin if (chiptod->primary) { 5299a69950fSNicholas Piggin chiptod->pss_mss_ctrl_reg |= PPC_BIT(1); /* TOD is master */ 5309a69950fSNicholas Piggin } 5319a69950fSNicholas Piggin /* Drawer is master (we do not simulate multi-drawer) */ 5329a69950fSNicholas Piggin chiptod->pss_mss_ctrl_reg |= PPC_BIT(2); 5339a69950fSNicholas Piggin 5349a69950fSNicholas Piggin chiptod->tod_error = 0; 5359a69950fSNicholas Piggin chiptod->tod_state = tod_error; 5369a69950fSNicholas Piggin } 5379a69950fSNicholas Piggin 5389a69950fSNicholas Piggin static void pnv_chiptod_realize(DeviceState *dev, Error **errp) 5399a69950fSNicholas Piggin { 5409a69950fSNicholas Piggin PnvChipTOD *chiptod = PNV_CHIPTOD(dev); 5419a69950fSNicholas Piggin PnvChipTODClass *pctc = PNV_CHIPTOD_GET_CLASS(chiptod); 5429a69950fSNicholas Piggin 5439a69950fSNicholas Piggin /* XScom regions for ChipTOD registers */ 5449a69950fSNicholas Piggin pnv_xscom_region_init(&chiptod->xscom_regs, OBJECT(dev), 5459a69950fSNicholas Piggin &pnv_chiptod_xscom_ops, chiptod, "xscom-chiptod", 5469a69950fSNicholas Piggin pctc->xscom_size); 5479a69950fSNicholas Piggin 5489a69950fSNicholas Piggin qemu_register_reset(pnv_chiptod_reset, chiptod); 5499a69950fSNicholas Piggin } 5509a69950fSNicholas Piggin 5519a69950fSNicholas Piggin static void pnv_chiptod_unrealize(DeviceState *dev) 5529a69950fSNicholas Piggin { 5539a69950fSNicholas Piggin PnvChipTOD *chiptod = PNV_CHIPTOD(dev); 5549a69950fSNicholas Piggin 5559a69950fSNicholas Piggin qemu_unregister_reset(pnv_chiptod_reset, chiptod); 5569a69950fSNicholas Piggin } 5579a69950fSNicholas Piggin 558*12d1a768SPhilippe Mathieu-Daudé static void pnv_chiptod_class_init(ObjectClass *klass, const void *data) 5599a69950fSNicholas Piggin { 5609a69950fSNicholas Piggin DeviceClass *dc = DEVICE_CLASS(klass); 5619a69950fSNicholas Piggin 5629a69950fSNicholas Piggin dc->realize = pnv_chiptod_realize; 5639a69950fSNicholas Piggin dc->unrealize = pnv_chiptod_unrealize; 5649a69950fSNicholas Piggin dc->desc = "PowerNV ChipTOD Controller"; 5659a69950fSNicholas Piggin dc->user_creatable = false; 5669a69950fSNicholas Piggin } 5679a69950fSNicholas Piggin 5689a69950fSNicholas Piggin static const TypeInfo pnv_chiptod_type_info = { 5699a69950fSNicholas Piggin .name = TYPE_PNV_CHIPTOD, 5709a69950fSNicholas Piggin .parent = TYPE_DEVICE, 5719a69950fSNicholas Piggin .instance_size = sizeof(PnvChipTOD), 5729a69950fSNicholas Piggin .class_init = pnv_chiptod_class_init, 5739a69950fSNicholas Piggin .class_size = sizeof(PnvChipTODClass), 5749a69950fSNicholas Piggin .abstract = true, 5759a69950fSNicholas Piggin }; 5769a69950fSNicholas Piggin 5779a69950fSNicholas Piggin static void pnv_chiptod_register_types(void) 5789a69950fSNicholas Piggin { 5799a69950fSNicholas Piggin type_register_static(&pnv_chiptod_type_info); 5809a69950fSNicholas Piggin type_register_static(&pnv_chiptod_power9_type_info); 5819a69950fSNicholas Piggin type_register_static(&pnv_chiptod_power10_type_info); 5829a69950fSNicholas Piggin } 5839a69950fSNicholas Piggin 5849a69950fSNicholas Piggin type_init(pnv_chiptod_register_types); 585