/* Copyright 2013-2014 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* Handle ChipTOD chip & configure core and CAPP timebases */

#define pr_fmt(fmt)	"CHIPTOD: " fmt

#include <skiboot.h>
#include <xscom.h>
#include <pci.h>
#include <chiptod.h>
#include <chip.h>
#include <io.h>
#include <cpu.h>
#include <timebase.h>
#include <opal-api.h>

/* TOD chip XSCOM addresses */
#define TOD_MASTER_PATH_CTRL		0x00040000 /* Master Path ctrl reg */
#define TOD_PRI_PORT0_CTRL		0x00040001 /* Primary port0 ctrl reg */
#define TOD_PRI_PORT1_CTRL		0x00040002 /* Primary port1 ctrl reg */
#define TOD_SEC_PORT0_CTRL		0x00040003 /* Secondary p0 ctrl reg */
#define TOD_SEC_PORT1_CTRL		0x00040004 /* Secondary p1 ctrl reg */
#define TOD_SLAVE_PATH_CTRL		0x00040005 /* Slave Path ctrl reg */
#define TOD_INTERNAL_PATH_CTRL		0x00040006 /* Internal Path ctrl reg */

/* -- TOD primary/secondary master/slave control register -- */
#define TOD_PSMS_CTRL			0x00040007
#define  TOD_PSMSC_PM_TOD_SELECT	PPC_BIT(1)  /* Primary Master TOD */
#define  TOD_PSMSC_PM_DRAW_SELECT	PPC_BIT(2)  /* Primary Master Drawer */
#define  TOD_PSMSC_SM_TOD_SELECT	PPC_BIT(9)  /* Secondary Master TOD */
#define  TOD_PSMSC_SM_DRAW_SELECT	PPC_BIT(10) /* Secondary Master Draw */

/* -- TOD primary/secondary master/slave status register -- */
#define TOD_STATUS			0x00040008
#define   TOD_ST_TOPOLOGY_SELECT	PPC_BITMASK(0, 2)
#define   TOD_ST_MPATH0_STEP_VALID	PPC_BIT(6)  /* MasterPath0 step valid */
#define   TOD_ST_MPATH1_STEP_VALID	PPC_BIT(7)  /* MasterPath1 step valid */
#define   TOD_ST_SPATH0_STEP_VALID	PPC_BIT(8)  /* SlavePath0 step valid */
#define   TOD_ST_SPATH1_STEP_VALID	PPC_BIT(10) /* SlavePath1 step valid */
/* Primary master/slave path select (0 = PATH_0, 1 = PATH_1) */
#define   TOD_ST_PRI_MPATH_SELECT	PPC_BIT(12) /* Primary MPath Select */
#define   TOD_ST_PRI_SPATH_SELECT	PPC_BIT(15) /* Primary SPath Select */
/* Secondary master/slave path select (0 = PATH_0, 1 = PATH_1) */
#define   TOD_ST_SEC_MPATH_SELECT	PPC_BIT(16) /* Secondary MPath Select */
#define   TOD_ST_SEC_SPATH_SELECT	PPC_BIT(19) /* Secondary SPath Select */
#define   TOD_ST_ACTIVE_MASTER		PPC_BIT(23)
#define   TOD_ST_BACKUP_MASTER		PPC_BIT(24)

/* TOD chip XSCOM addresses */
#define TOD_CHIP_CTRL			0x00040010 /* Chip control register */
#define TOD_TTYPE_0			0x00040011
#define TOD_TTYPE_1			0x00040012 /* PSS switch */
#define TOD_TTYPE_2			0x00040013 /* Enable step checkers */
#define TOD_TTYPE_3			0x00040014 /* Request TOD */
#define TOD_TTYPE_4			0x00040015 /* Send TOD */
#define TOD_TTYPE_5			0x00040016 /* Invalidate TOD */
#define TOD_CHIPTOD_TO_TB		0x00040017
#define TOD_LOAD_TOD_MOD		0x00040018
#define TOD_CHIPTOD_VALUE		0x00040020
#define TOD_CHIPTOD_LOAD_TB		0x00040021
#define TOD_CHIPTOD_FSM			0x00040024

/* -- TOD PIB Master reg -- */
#define TOD_PIB_MASTER			0x00040027
#define   TOD_PIBM_ADDR_CFG_MCAST	PPC_BIT(25)
#define   TOD_PIBM_ADDR_CFG_SLADDR	PPC_BITMASK(26, 31)
#define   TOD_PIBM_TTYPE4_SEND_MODE	PPC_BIT(32)
#define   TOD_PIBM_TTYPE4_SEND_ENBL	PPC_BIT(33)

/* -- TOD Error interrupt register -- */
#define TOD_ERROR			0x00040030
/* SYNC errors */
#define   TOD_ERR_CRMO_PARITY		PPC_BIT(0)
#define   TOD_ERR_OSC0_PARITY		PPC_BIT(1)
#define   TOD_ERR_OSC1_PARITY		PPC_BIT(2)
#define   TOD_ERR_PPORT0_CREG_PARITY	PPC_BIT(3)
#define   TOD_ERR_PPORT1_CREG_PARITY	PPC_BIT(4)
#define   TOD_ERR_SPORT0_CREG_PARITY	PPC_BIT(5)
#define   TOD_ERR_SPORT1_CREG_PARITY	PPC_BIT(6)
#define   TOD_ERR_SPATH_CREG_PARITY	PPC_BIT(7)
#define   TOD_ERR_IPATH_CREG_PARITY	PPC_BIT(8)
#define   TOD_ERR_PSMS_CREG_PARITY	PPC_BIT(9)
#define   TOD_ERR_CRITC_PARITY		PPC_BIT(13)
#define   TOD_ERR_MP0_STEP_CHECK	PPC_BIT(14)
#define   TOD_ERR_MP1_STEP_CHECK	PPC_BIT(15)
#define   TOD_ERR_PSS_HAMMING_DISTANCE	PPC_BIT(18)
#define	  TOD_ERR_DELAY_COMPL_PARITY	PPC_BIT(22)
/* CNTR errors */
#define   TOD_ERR_CTCR_PARITY		PPC_BIT(32)
#define   TOD_ERR_TOD_SYNC_CHECK	PPC_BIT(33)
#define   TOD_ERR_TOD_FSM_PARITY	PPC_BIT(34)
#define   TOD_ERR_TOD_REGISTER_PARITY	PPC_BIT(35)
#define   TOD_ERR_OVERFLOW_YR2042	PPC_BIT(36)
#define   TOD_ERR_TOD_WOF_LSTEP_PARITY	PPC_BIT(37)
#define   TOD_ERR_TTYPE0_RECVD		PPC_BIT(38)
#define   TOD_ERR_TTYPE1_RECVD		PPC_BIT(39)
#define   TOD_ERR_TTYPE2_RECVD		PPC_BIT(40)
#define   TOD_ERR_TTYPE3_RECVD		PPC_BIT(41)
#define   TOD_ERR_TTYPE4_RECVD		PPC_BIT(42)
#define   TOD_ERR_TTYPE5_RECVD		PPC_BIT(43)

/* -- TOD Error interrupt register -- */
#define TOD_ERROR_INJECT		0x00040031

/* Local FIR EH.TPCHIP.TPC.LOCAL_FIR */
#define LOCAL_CORE_FIR		0x0104000C
#define LFIR_SWITCH_COMPLETE	PPC_BIT(18)

/* Number of iterations for the various timeouts */
#define TIMEOUT_LOOPS		20000000

/* TOD active Primary/secondary configuration */
#define TOD_PRI_CONF_IN_USE	0	/* Tod using primary topology*/
#define TOD_SEC_CONF_IN_USE	7	/* Tod using secondary topo */

/* Timebase State Machine error state */
#define TBST_STATE_ERROR	9

static enum chiptod_type {
	chiptod_unknown,
	chiptod_p7,
	chiptod_p8,
	chiptod_p9
} chiptod_type;

enum chiptod_chip_role {
	chiptod_chip_role_UNKNOWN = -1,
	chiptod_chip_role_MDMT = 0,	/* Master Drawer Master TOD */
	chiptod_chip_role_MDST,		/* Master Drawer Slave TOD */
	chiptod_chip_role_SDMT,		/* Slave Drawer Master TOD */
	chiptod_chip_role_SDST,		/* Slave Drawer Slave TOD */
};

enum chiptod_chip_status {
	chiptod_active_master = 0,	/* Chip TOD is Active master */
	chiptod_backup_master = 1,	/* Chip TOD is backup master */
	chiptod_backup_disabled,	/* Chip TOD is backup but disabled */
};

struct chiptod_chip_config_info {
	int32_t id;				/* chip id */
	enum chiptod_chip_role role;		/* Chip role */
	enum chiptod_chip_status status;	/* active/backup/disabled */
};

static int32_t chiptod_primary = -1;
static int32_t chiptod_secondary = -1;
static enum chiptod_topology current_topology = chiptod_topo_unknown;

/*
 * chiptod_topology_info holds primary/secondary chip configuration info.
 * This info is initialized during chiptod_init(). This is an array of two:
 *	[0] = [chiptod_topo_primary] = Primary topology config info
 *	[1] = [chiptod_topo_secondary] = Secondary topology config info
 */
static struct chiptod_chip_config_info chiptod_topology_info[2];

/*
 * Array of TOD control registers that holds last known valid values.
 *
 * Cache chiptod control register values at following instances:
 * 1. Chiptod initialization
 * 2. After topology switch is complete.
 * 3. Upon receiving enable/disable topology request from FSP.
 *
 * Cache following chip TOD control registers:
 *   - Master Path control register (0x00040000)
 *   - Primary Port-0 control register (0x00040001)
 *   - Primary Port-1 control register (0x00040002)
 *   - Secondary Port-0 control register (0x00040003)
 *   - Secondary Port-1 control register (0x00040004)
 *   - Slave Path control register (0x00040005)
 *   - Internal Path control register (0x00040006)
 *   - Primary/secondary master/slave control register (0x00040007)
 *   - Chip control register (0x00040010)
 *
 * This data is used for restoring respective TOD registers to sane values
 * whenever parity errors are reported on these registers (through HMI).
 * The error_bit maps to corresponding bit from TOD error register that
 * reports parity error on respective TOD registers.
 */
static struct chiptod_tod_regs {
	/* error bit from TOD Error reg */
	const uint64_t	error_bit;

	/* xscom address of TOD register to be restored. */
	const uint64_t	xscom_addr;
	/* per chip cached value of TOD control registers to be restored. */
	struct {
		uint64_t	data;
		bool		valid;
	} val[MAX_CHIPS];
} chiptod_tod_regs[] = {
	{ TOD_ERR_CRMO_PARITY, TOD_MASTER_PATH_CTRL, { } },
	{ TOD_ERR_PPORT0_CREG_PARITY, TOD_PRI_PORT0_CTRL,  { } },
	{ TOD_ERR_PPORT1_CREG_PARITY, TOD_PRI_PORT1_CTRL, { } },
	{ TOD_ERR_SPORT0_CREG_PARITY, TOD_SEC_PORT0_CTRL, { } },
	{ TOD_ERR_SPORT1_CREG_PARITY, TOD_SEC_PORT1_CTRL, { } },
	{ TOD_ERR_SPATH_CREG_PARITY, TOD_SLAVE_PATH_CTRL, { } },
	{ TOD_ERR_IPATH_CREG_PARITY, TOD_INTERNAL_PATH_CTRL, { } },
	{ TOD_ERR_PSMS_CREG_PARITY, TOD_PSMS_CTRL, { } },
	{ TOD_ERR_CTCR_PARITY, TOD_CHIP_CTRL, { } },
};

/* The base TFMR value is the same for the whole machine
 * for now as far as I can tell
 */
static uint64_t base_tfmr;

/*
 * For now, we use a global lock for runtime chiptod operations,
 * eventually make this a per-core lock for wakeup rsync and
 * take all of them for RAS cases.
 */
static struct lock chiptod_lock = LOCK_UNLOCKED;
static bool chiptod_unrecoverable;

static void _chiptod_cache_tod_regs(int32_t chip_id)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(chiptod_tod_regs); i++) {
		if (xscom_read(chip_id, chiptod_tod_regs[i].xscom_addr,
			&(chiptod_tod_regs[i].val[chip_id].data))) {
			prerror("XSCOM error reading 0x%08llx reg.\n",
					chiptod_tod_regs[i].xscom_addr);
			/* Invalidate this record and continue */
			chiptod_tod_regs[i].val[chip_id].valid = 0;
			continue;
		}
		chiptod_tod_regs[i].val[chip_id].valid = 1;
	}
}

static void chiptod_cache_tod_registers(void)
{
	struct proc_chip *chip;

	for_each_chip(chip)
		_chiptod_cache_tod_regs(chip->id);
}

static void print_topo_info(enum chiptod_topology topo)
{
	const char *role[] = { "Unknown", "MDMT", "MDST", "SDMT", "SDST" };
	const char *status[] = { "Unknown",
		"Active Master", "Backup Master", "Backup Master Disabled" };

	prlog(PR_DEBUG, "  Chip id: %d, Role: %s, Status: %s\n",
				chiptod_topology_info[topo].id,
				role[chiptod_topology_info[topo].role + 1],
				status[chiptod_topology_info[topo].status + 1]);
}

static void print_topology_info(void)
{
	const char *topo[] = { "Unknown", "Primary", "Secondary" };

	if (current_topology < 0)
		return;

	prlog(PR_DEBUG, "TOD Topology in Use: %s\n",
						topo[current_topology+1]);
	prlog(PR_DEBUG, "  Primary configuration:\n");
	print_topo_info(chiptod_topo_primary);
	prlog(PR_DEBUG, "  Secondary configuration:\n");
	print_topo_info(chiptod_topo_secondary);
}

static enum chiptod_topology query_current_topology(void)
{
	uint64_t tod_status;

	if (xscom_readme(TOD_STATUS, &tod_status)) {
		prerror("XSCOM error reading TOD_STATUS reg\n");
		return chiptod_topo_unknown;
	}

	/*
	 * Tod status register bit [0-2] tells configuration in use.
	 *	000 <= primary configuration in use
	 *	111 <= secondary configuration in use
	 */
	if ((tod_status & TOD_ST_TOPOLOGY_SELECT) == TOD_PRI_CONF_IN_USE)
		return chiptod_topo_primary;
	else
		return chiptod_topo_secondary;
}

static enum chiptod_chip_role
chiptod_get_chip_role(enum chiptod_topology topology, int32_t chip_id)
{
	uint64_t tod_ctrl;
	enum chiptod_chip_role role = chiptod_chip_role_UNKNOWN;

	if (chip_id < 0)
		return role;

	if (xscom_read(chip_id, TOD_PSMS_CTRL, &tod_ctrl)) {
		prerror("XSCOM error reading TOD_PSMS_CTRL\n");
		return chiptod_chip_role_UNKNOWN;
	}

	switch (topology) {
	case chiptod_topo_primary:
		if (tod_ctrl & TOD_PSMSC_PM_DRAW_SELECT) {
			if (tod_ctrl & TOD_PSMSC_PM_TOD_SELECT)
				role = chiptod_chip_role_MDMT;
			else
				role = chiptod_chip_role_MDST;
		} else {
			if (tod_ctrl & TOD_PSMSC_PM_TOD_SELECT)
				role = chiptod_chip_role_SDMT;
			else
				role = chiptod_chip_role_SDST;
		}
		break;
	case chiptod_topo_secondary:
		if (tod_ctrl & TOD_PSMSC_SM_DRAW_SELECT) {
			if (tod_ctrl & TOD_PSMSC_SM_TOD_SELECT)
				role = chiptod_chip_role_MDMT;
			else
				role = chiptod_chip_role_MDST;
		} else {
			if (tod_ctrl & TOD_PSMSC_SM_TOD_SELECT)
				role = chiptod_chip_role_SDMT;
			else
				role = chiptod_chip_role_SDST;
		}
		break;
	case chiptod_topo_unknown:
	default:
		break;
	}
	return role;
}

/*
 * Check and return the status of sync step network for a given
 * topology configuration.
 * Return values:
 *	true:	Sync Step network is running
 *	false:	Sync Step network is not running
 */
static bool chiptod_sync_step_check_running(enum chiptod_topology topology)
{
	uint64_t tod_status;
	enum chiptod_chip_role role;
	bool running = false;
	int32_t chip_id = chiptod_topology_info[topology].id;

	/* Sanity check */
	if (chip_id < 0)
		return false;

	if (xscom_read(chip_id, TOD_STATUS, &tod_status)) {
		prerror("XSCOM error reading TOD_STATUS reg\n");
		return false;
	}

	switch (topology) {
	case chiptod_topo_primary:
		/* Primary configuration */
		role = chiptod_topology_info[topology].role;
		if (role == chiptod_chip_role_MDMT) {
			/*
			 * Chip is using Master path.
			 * Check if it is using path_0/path_1 and then
			 * validity of that path.
			 *
			 * TOD_STATUS[12]: 0 = PATH_0, 1 = PATH_1
			 */
			if (tod_status & TOD_ST_PRI_MPATH_SELECT) {
				if (tod_status & TOD_ST_MPATH1_STEP_VALID)
					running = true;
			} else {
				if (tod_status & TOD_ST_MPATH0_STEP_VALID)
					running = true;
			}
		} else {
			/*
			 * Chip is using Slave path.
			 *
			 * TOD_STATUS[15]: 0 = PATH_0, 1 = PATH_1
			 */
			if (tod_status & TOD_ST_PRI_SPATH_SELECT) {
				if (tod_status & TOD_ST_SPATH1_STEP_VALID)
					running = true;
			} else {
				if (tod_status & TOD_ST_SPATH0_STEP_VALID)
					running = true;
			}
		}
		break;
	case chiptod_topo_secondary:
		/* Secondary configuration */
		role = chiptod_topology_info[topology].role;
		if (role == chiptod_chip_role_MDMT) {
			/*
			 * Chip is using Master path.
			 * Check if it is using path_0/path_1 and then
			 * validity of that path.
			 *
			 * TOD_STATUS[12]: 0 = PATH_0, 1 = PATH_1
			 */
			if (tod_status & TOD_ST_SEC_MPATH_SELECT) {
				if (tod_status & TOD_ST_MPATH1_STEP_VALID)
					running = true;
			} else {
				if (tod_status & TOD_ST_MPATH0_STEP_VALID)
					running = true;
			}
		} else {
			/*
			 * Chip is using Slave path.
			 *
			 * TOD_STATUS[15]: 0 = PATH_0, 1 = PATH_1
			 */
			if (tod_status & TOD_ST_SEC_SPATH_SELECT) {
				if (tod_status & TOD_ST_SPATH1_STEP_VALID)
					running = true;
			} else {
				if (tod_status & TOD_ST_SPATH0_STEP_VALID)
					running = true;
			}
		}
		break;
	default:
		break;
	}
	return running;
}

static enum chiptod_chip_status _chiptod_get_chip_status(int32_t chip_id)
{
	uint64_t tod_status;
	enum chiptod_chip_status status = -1;

	if (chip_id < 0)
		return chiptod_backup_disabled;

	if (xscom_read(chip_id, TOD_STATUS, &tod_status)) {
		prerror("XSCOM error reading TOD_STATUS reg\n");
		return status;
	}

	if (tod_status & TOD_ST_ACTIVE_MASTER)
		status = chiptod_active_master;
	else if (tod_status & TOD_ST_BACKUP_MASTER)
		status = chiptod_backup_master;

	return status;
}

static enum chiptod_chip_status
chiptod_get_chip_status(enum chiptod_topology topology)
{
	return _chiptod_get_chip_status(chiptod_topology_info[topology].id);
}

static void chiptod_update_topology(enum chiptod_topology topo)
{
	int32_t chip_id = chiptod_topology_info[topo].id;

	if (chip_id < 0)
		return;

	chiptod_topology_info[topo].role = chiptod_get_chip_role(topo, chip_id);
	chiptod_topology_info[topo].status = chiptod_get_chip_status(topo);

	/*
	 * If chip TOD on this topology is a backup master then check if
	 * sync/step network is running on this topology. If not,
	 * then mark status as backup not valid.
	 */
	if ((chiptod_topology_info[topo].status == chiptod_backup_master) &&
			!chiptod_sync_step_check_running(topo))
		chiptod_topology_info[topo].status = chiptod_backup_disabled;
}

static void chiptod_setup_base_tfmr(void)
{
	struct dt_node *cpu = this_cpu()->node;
	uint64_t core_freq, tod_freq;
	uint64_t mcbs;

	base_tfmr = SPR_TFMR_TB_ECLIPZ;

	/* Get CPU and TOD freqs in Hz */
	if (dt_has_node_property(cpu, "ibm,extended-clock-frequency", NULL))
		core_freq = dt_prop_get_u64(cpu, "ibm,extended-clock-frequency");
	else
		core_freq = dt_prop_get_u32(cpu, "clock-frequency");
	tod_freq = 32000000;

	/* Calculate the "Max Cycles Between Steps" value according
	 * to the magic formula:
	 *
	 * mcbs = (core_freq * max_jitter_factor) / (4 * tod_freq) / 100;
	 *
	 * The max jitter factor is set to 240 based on what pHyp uses.
	 */
	mcbs = (core_freq * 240) / (4 * tod_freq) / 100;
	prlog(PR_INFO, "Calculated MCBS is 0x%llx"
	      " (Cfreq=%lld Tfreq=%lld)\n",
	      mcbs, core_freq, tod_freq);

	/* Bake that all into TFMR */
	base_tfmr = SETFIELD(SPR_TFMR_MAX_CYC_BET_STEPS, base_tfmr, mcbs);
	base_tfmr = SETFIELD(SPR_TFMR_N_CLKS_PER_STEP, base_tfmr, 0);
	base_tfmr = SETFIELD(SPR_TFMR_SYNC_BIT_SEL, base_tfmr, 4);
}

static bool chiptod_mod_tb(void)
{
	uint64_t tfmr = base_tfmr;
	uint64_t timeout = 0;

	/* Switch timebase to "Not Set" state */
	mtspr(SPR_TFMR, tfmr | SPR_TFMR_LOAD_TOD_MOD);
	do {
		if (++timeout >= (TIMEOUT_LOOPS*2)) {
			prerror("TB \"Not Set\" timeout\n");
			return false;
		}
		tfmr = mfspr(SPR_TFMR);
		if (tfmr & SPR_TFMR_TFMR_CORRUPT) {
			prerror("TB \"Not Set\" TFMR corrupt\n");
			return false;
		}
		if (GETFIELD(SPR_TFMR_TBST_ENCODED, tfmr) == 9) {
			prerror("TB \"Not Set\" TOD in error state\n");
			return false;
		}
	} while (tfmr & SPR_TFMR_LOAD_TOD_MOD);

	return true;
}

static bool chiptod_interrupt_check(void)
{
	uint64_t tfmr;
	uint64_t timeout = 0;

	do {
		if (++timeout >= TIMEOUT_LOOPS) {
			prerror("Interrupt check fail\n");
			return false;
		}
		tfmr = mfspr(SPR_TFMR);
		if (tfmr & SPR_TFMR_TFMR_CORRUPT) {
			prerror("Interrupt check TFMR corrupt !\n");
			return false;
		}
	} while (tfmr & SPR_TFMR_CHIP_TOD_INTERRUPT);

	return true;
}

static bool chiptod_running_check(uint32_t chip_id)
{
	uint64_t tval;

	if (xscom_read(chip_id, TOD_CHIPTOD_FSM, &tval)) {
		prerror("XSCOM error polling run\n");
		return false;
	}
	if (tval & 0x0800000000000000UL)
		return true;
	else
		return false;
}

static bool chiptod_poll_running(void)
{
	uint64_t timeout = 0;
	uint64_t tval;

	/* Chip TOD running check */
	do {
		if (++timeout >= TIMEOUT_LOOPS) {
			prerror("Running check fail timeout\n");
			return false;
		}
		if (xscom_readme(TOD_CHIPTOD_FSM, &tval)) {
			prerror("XSCOM error polling run\n");
			return false;
		}
	} while (!(tval & 0x0800000000000000UL));

	return true;
}

static bool chiptod_to_tb(void)
{
	uint64_t tval, tfmr, tvbits;
	uint64_t timeout = 0;

	/* Tell the ChipTOD about our fabric address
	 *
	 * The pib_master value is calculated from the CPU core ID, given in
	 * the PIR. Because we have different core/thread arrangements in the
	 * PIR between p7 and p8, we need to do the calculation differently.
	 *
	 * p7: 0b00001 || 3-bit core id
	 * p8: 0b0001 || 4-bit core id
	 */

	if (xscom_readme(TOD_PIB_MASTER, &tval)) {
		prerror("XSCOM error reading PIB_MASTER\n");
		return false;
	}
	if (chiptod_type == chiptod_p9) {
		tvbits = (this_cpu()->pir >> 2) & 0x1f;
		tvbits |= 0x20;
	} else if (chiptod_type == chiptod_p8) {
		tvbits = (this_cpu()->pir >> 3) & 0xf;
		tvbits |= 0x10;
	} else {
		tvbits = (this_cpu()->pir >> 2) & 0x7;
		tvbits |= 0x08;
	}
	tval &= ~TOD_PIBM_ADDR_CFG_MCAST;
	tval = SETFIELD(TOD_PIBM_ADDR_CFG_SLADDR, tval, tvbits);
	if (xscom_writeme(TOD_PIB_MASTER, tval)) {
		prerror("XSCOM error writing PIB_MASTER\n");
		return false;
	}

	/* Make us ready to get the TB from the chipTOD */
	mtspr(SPR_TFMR, base_tfmr | SPR_TFMR_MOVE_CHIP_TOD_TO_TB);

	/* Tell the ChipTOD to send it */
	if (xscom_writeme(TOD_CHIPTOD_TO_TB, PPC_BIT(0))) {
		prerror("XSCOM error writing CHIPTOD_TO_TB\n");
		return false;
	}

	/* Wait for it to complete */
	timeout = 0;
	do {
		if (++timeout >= TIMEOUT_LOOPS) {
			prerror("Chip to TB timeout\n");
			return false;
		}
		tfmr = mfspr(SPR_TFMR);
		if (tfmr & SPR_TFMR_TFMR_CORRUPT) {
			prerror("MoveToTB: corrupt TFMR !\n");
			return false;
		}
	} while (tfmr & SPR_TFMR_MOVE_CHIP_TOD_TO_TB);

	return true;
}

static bool chiptod_check_tb_running(void)
{
	/* We used to wait for two SYNC pulses in TFMR but that
	 * doesn't seem to occur in sim, so instead we use a
	 * method similar to what pHyp does which is to check for
	 * TFMR SPR_TFMR_TB_VALID and not SPR_TFMR_TFMR_CORRUPT
	 */
#if 0
	uint64_t tfmr, timeout;
	unsigned int i;

	for (i = 0; i < 2; i++) {
		tfmr = mfspr(SPR_TFMR);
		tfmr &= ~SPR_TFMR_TB_SYNC_OCCURED;
		mtspr(SPR_TFMR, tfmr);
		timeout = 0;
		do {
			if (++timeout >= TIMEOUT_LOOPS) {
				prerror("CHIPTOD: No sync pulses\n");
				return false;
			}
			tfmr = mfspr(SPR_TFMR);
		} while (!(tfmr & SPR_TFMR_TB_SYNC_OCCURED));
	}
#else
	uint64_t tfmr = mfspr(SPR_TFMR);

	return (tfmr & SPR_TFMR_TB_VALID) &&
		!(tfmr & SPR_TFMR_TFMR_CORRUPT);
#endif
	return true;
}

static bool chiptod_reset_tb_errors(void)
{
	uint64_t tfmr;
	unsigned long timeout = 0;

	/* Ask for automatic clear of errors */
	tfmr = base_tfmr | SPR_TFMR_CLEAR_TB_ERRORS;

	/* Additionally pHyp sets these (write-1-to-clear ?) */
	tfmr |= SPR_TFMR_TB_MISSING_SYNC;
	tfmr |= SPR_TFMR_TB_MISSING_STEP;
	tfmr |= SPR_TFMR_TB_RESIDUE_ERR;
	mtspr(SPR_TFMR, tfmr);

	/* We have to write "Clear TB Errors" again */
	tfmr = base_tfmr | SPR_TFMR_CLEAR_TB_ERRORS;
	mtspr(SPR_TFMR, tfmr);

	do {
		if (++timeout >= TIMEOUT_LOOPS) {
			/* Don't actually do anything on error for
			 * now ... not much we can do, panic maybe ?
			 */
			prerror("TB error reset timeout !\n");
			return false;
		}
		tfmr = mfspr(SPR_TFMR);
		if (tfmr & SPR_TFMR_TFMR_CORRUPT) {
			prerror("TB error reset: corrupt TFMR !\n");
			return false;
		}
	} while (tfmr & SPR_TFMR_CLEAR_TB_ERRORS);
	return true;
}

static void chiptod_cleanup_thread_tfmr(void)
{
	uint64_t tfmr = base_tfmr;

	tfmr |= SPR_TFMR_PURR_PARITY_ERR;
	tfmr |= SPR_TFMR_SPURR_PARITY_ERR;
	tfmr |= SPR_TFMR_DEC_PARITY_ERR;
	tfmr |= SPR_TFMR_TFMR_CORRUPT;
	tfmr |= SPR_TFMR_PURR_OVERFLOW;
	tfmr |= SPR_TFMR_SPURR_OVERFLOW;
	mtspr(SPR_TFMR, tfmr);
}

static void chiptod_reset_tod_errors(void)
{
	uint64_t terr;

	/*
	 * At boot, we clear the errors that the firmware is
	 * supposed to handle. List provided by the pHyp folks.
	 */

	terr = TOD_ERR_CRITC_PARITY;
	terr |= TOD_ERR_PSS_HAMMING_DISTANCE;
	terr |= TOD_ERR_DELAY_COMPL_PARITY;
	terr |= TOD_ERR_CTCR_PARITY;
	terr |= TOD_ERR_TOD_SYNC_CHECK;
	terr |= TOD_ERR_TOD_FSM_PARITY;
	terr |= TOD_ERR_TOD_REGISTER_PARITY;

	if (xscom_writeme(TOD_ERROR, terr)) {
		prerror("XSCOM error writing TOD_ERROR !\n");
		/* Not much we can do here ... abort ? */
	}
}

static void chiptod_sync_master(void *data)
{
	uint64_t initial_tb_value;
	bool *result = data;

	prlog(PR_DEBUG, "Master sync on CPU PIR 0x%04x...\n",
	      this_cpu()->pir);

	/* Apply base tfmr */
	mtspr(SPR_TFMR, base_tfmr);

	/* From recipe provided by pHyp folks, reset various errors
	 * before attempting the sync
	 */
	chiptod_reset_tb_errors();

	/* Cleanup thread tfmr bits */
	chiptod_cleanup_thread_tfmr();

	/* Reset errors in the chiptod itself */
	chiptod_reset_tod_errors();

	/* Switch timebase to "Not Set" state */
	if (!chiptod_mod_tb())
		goto error;
	prlog(PR_INSANE, "SYNC MASTER Step 2 TFMR=0x%016lx\n", mfspr(SPR_TFMR));

	/* Chip TOD step checkers enable */
	if (xscom_writeme(TOD_TTYPE_2, PPC_BIT(0))) {
		prerror("XSCOM error enabling steppers\n");
		goto error;
	}

	prlog(PR_INSANE, "SYNC MASTER Step 3 TFMR=0x%016lx\n", mfspr(SPR_TFMR));

	/* Chip TOD interrupt check */
	if (!chiptod_interrupt_check())
		goto error;
	prlog(PR_INSANE, "SYNC MASTER Step 4 TFMR=0x%016lx\n", mfspr(SPR_TFMR));

	/* Switch local chiptod to "Not Set" state */
	if (xscom_writeme(TOD_LOAD_TOD_MOD, PPC_BIT(0))) {
		prerror("XSCOM error sending LOAD_TOD_MOD\n");
		goto error;
	}

	/* Switch all remote chiptod to "Not Set" state */
	if (xscom_writeme(TOD_TTYPE_5, PPC_BIT(0))) {
		prerror("XSCOM error sending TTYPE_5\n");
		goto error;
	}

	/*
	 * Load the master's current timebase value into the Chip TOD
	 * network. This is so we have sane timestamps across the whole
	 * IPL process. The Chip TOD documentation says that the loaded
	 * value needs to be one STEP before a SYNC. In other words,
	 * set the low bits to 0x1ff0.
	 */
	initial_tb_value = (mftb() & ~0x1fff) | 0x1ff0;

	/* Chip TOD load initial value */
	if (xscom_writeme(TOD_CHIPTOD_LOAD_TB, initial_tb_value)) {
		prerror("XSCOM error setting init TB\n");
		goto error;
	}

	prlog(PR_INSANE, "SYNC MASTER Step 5 TFMR=0x%016lx\n", mfspr(SPR_TFMR));

	if (!chiptod_poll_running())
		goto error;
	prlog(PR_INSANE, "SYNC MASTER Step 6 TFMR=0x%016lx\n", mfspr(SPR_TFMR));

	/* Move chiptod value to core TB */
	if (!chiptod_to_tb())
		goto error;
	prlog(PR_INSANE, "SYNC MASTER Step 7 TFMR=0x%016lx\n", mfspr(SPR_TFMR));

	/* Send local chip TOD to all chips TOD */
	if (xscom_writeme(TOD_TTYPE_4, PPC_BIT(0))) {
		prerror("XSCOM error sending TTYPE_4\n");
		goto error;
	}

	/* Check if TB is running */
	if (!chiptod_check_tb_running())
		goto error;

	prlog(PR_INSANE, "Master sync completed, TB=%lx\n", mfspr(SPR_TBRL));

	/*
	 * A little delay to make sure the remote chips get up to
	 * speed before we start syncing them.
	 *
	 * We have to do it here because we know our TB is running
	 * while the boot thread TB might not yet.
	 */
	time_wait_ms(1);

	*result = true;
	return;
 error:
	prerror("Master sync failed! TFMR=0x%016lx\n", mfspr(SPR_TFMR));
	*result = false;
}

static void chiptod_sync_slave(void *data)
{
	bool *result = data;

	/* Only get primaries, not threads */
	if (this_cpu()->is_secondary) {
		/* On secondaries we just cleanup the TFMR */
		chiptod_cleanup_thread_tfmr();
		*result = true;
		return;
	}

	prlog(PR_DEBUG, "Slave sync on CPU PIR 0x%04x...\n",
	      this_cpu()->pir);

	/* Apply base tfmr */
	mtspr(SPR_TFMR, base_tfmr);

	/* From recipe provided by pHyp folks, reset various errors
	 * before attempting the sync
	 */
	chiptod_reset_tb_errors();

	/* Cleanup thread tfmr bits */
	chiptod_cleanup_thread_tfmr();

	/* Switch timebase to "Not Set" state */
	if (!chiptod_mod_tb())
		goto error;
	prlog(PR_INSANE, "SYNC SLAVE Step 2 TFMR=0x%016lx\n", mfspr(SPR_TFMR));

	/* Chip TOD running check */
	if (!chiptod_poll_running())
		goto error;
	prlog(PR_INSANE, "SYNC SLAVE Step 3 TFMR=0x%016lx\n", mfspr(SPR_TFMR));

	/* Chip TOD interrupt check */
	if (!chiptod_interrupt_check())
		goto error;
	prlog(PR_INSANE, "SYNC SLAVE Step 4 TFMR=0x%016lx\n", mfspr(SPR_TFMR));

	/* Move chiptod value to core TB */
	if (!chiptod_to_tb())
		goto error;
	prlog(PR_INSANE, "SYNC SLAVE Step 5 TFMR=0x%016lx\n", mfspr(SPR_TFMR));

	/* Check if TB is running */
	if (!chiptod_check_tb_running())
		goto error;

	prlog(PR_INSANE, "Slave sync completed, TB=%lx\n", mfspr(SPR_TBRL));

	*result = true;
	return;
 error:
	prerror("Slave sync failed ! TFMR=0x%016lx\n", mfspr(SPR_TFMR));
	*result = false;
}

bool chiptod_wakeup_resync(void)
{
	if (chiptod_primary < 0)
		return 0;

	lock(&chiptod_lock);

	/* Apply base tfmr */
	mtspr(SPR_TFMR, base_tfmr);

	/* From recipe provided by pHyp folks, reset various errors
	 * before attempting the sync
	 */
	chiptod_reset_tb_errors();

	/* Cleanup thread tfmr bits */
	chiptod_cleanup_thread_tfmr();

	/* Switch timebase to "Not Set" state */
	if (!chiptod_mod_tb())
		goto error;

	/* Move chiptod value to core TB */
	if (!chiptod_to_tb())
		goto error;

	unlock(&chiptod_lock);

	return true;
 error:
	prerror("Resync failed ! TFMR=0x%16lx\n", mfspr(SPR_TFMR));
	unlock(&chiptod_lock);
	return false;
}

static int __chiptod_recover_tod_errors(void)
{
	uint64_t terr;
	uint64_t treset = 0;
	int i, rc = -1;
	int32_t chip_id = this_cpu()->chip_id;

	/* Read TOD error register */
	if (xscom_readme(TOD_ERROR, &terr)) {
		prerror("XSCOM error reading TOD_ERROR reg\n");
		return 0;
	}
	/* Check for sync check error and recover */
	if ((terr & TOD_ERR_TOD_SYNC_CHECK) ||
		(terr & TOD_ERR_TOD_FSM_PARITY) ||
		(terr & TOD_ERR_CTCR_PARITY) ||
		(terr & TOD_ERR_PSS_HAMMING_DISTANCE) ||
		(terr & TOD_ERR_DELAY_COMPL_PARITY) ||
		(terr & TOD_ERR_TOD_REGISTER_PARITY)) {
		chiptod_reset_tod_errors();
		rc = 1;
	}

	/*
	 * Check for TOD control register parity errors and restore those
	 * registers with last saved valid values.
	 */
	for (i = 0; i < ARRAY_SIZE(chiptod_tod_regs); i++) {
		if (!(terr & chiptod_tod_regs[i].error_bit))
			continue;

		/* Check if we have valid last saved register value. */
		if (!chiptod_tod_regs[i].val[chip_id].valid) {
			prerror("Failed to restore TOD register: %08llx",
					chiptod_tod_regs[i].xscom_addr);
			return 0;
		}

		prlog(PR_DEBUG, "Parity error, Restoring TOD register: "
				"%08llx\n", chiptod_tod_regs[i].xscom_addr);
		if (xscom_writeme(chiptod_tod_regs[i].xscom_addr,
			chiptod_tod_regs[i].val[chip_id].data)) {
			prerror("XSCOM error writing 0x%08llx reg.\n",
					chiptod_tod_regs[i].xscom_addr);
			return 0;
		}
		treset |= chiptod_tod_regs[i].error_bit;
	}

	if (treset && (xscom_writeme(TOD_ERROR, treset))) {
		prerror("XSCOM error writing TOD_ERROR !\n");
		return 0;
	}
	/* We have handled all the TOD errors routed to hypervisor */
	if (treset)
		rc = 1;
	return rc;
}

int chiptod_recover_tod_errors(void)
{
	int rc;

	lock(&chiptod_lock);
	rc = __chiptod_recover_tod_errors();
	unlock(&chiptod_lock);
	return rc;
}

static int32_t chiptod_get_active_master(void)
{
	if (current_topology < 0)
		return -1;

	if (chiptod_topology_info[current_topology].status ==
							chiptod_active_master)
		return chiptod_topology_info[current_topology].id;
	return -1;
}

/* Return true if Active master TOD is running. */
static bool chiptod_master_running(void)
{
	int32_t active_master_chip;

	active_master_chip = chiptod_get_active_master();
	if (active_master_chip != -1) {
		if (chiptod_running_check(active_master_chip))
			return true;
	}
	return false;
}

static bool chiptod_set_ttype4_mode(struct proc_chip *chip, bool enable)
{
	uint64_t tval;

	/* Sanity check */
	if (!chip)
		return false;

	if (xscom_read(chip->id, TOD_PIB_MASTER, &tval)) {
		prerror("XSCOM error reading PIB_MASTER\n");
		return false;
	}

	if (enable) {
		/*
		 * Enable TTYPE4 send mode. This allows TOD to respond to
		 * TTYPE3 request.
		 */
		tval |= TOD_PIBM_TTYPE4_SEND_MODE;
		tval |= TOD_PIBM_TTYPE4_SEND_ENBL;
	} else {
		/* Disable TTYPE4 send mode. */
		tval &= ~TOD_PIBM_TTYPE4_SEND_MODE;
		tval &= ~TOD_PIBM_TTYPE4_SEND_ENBL;
	}

	if (xscom_write(chip->id, TOD_PIB_MASTER, tval)) {
		prerror("XSCOM error writing PIB_MASTER\n");
		return false;
	}
	return true;
}

/* Stop TODs on slave chips in backup topology. */
static void chiptod_stop_slave_tods(void)
{
	struct proc_chip *chip = NULL;
	enum chiptod_topology backup_topo;
	uint64_t terr = 0;

	/* Inject TOD sync check error on salve TODs to stop them. */
	terr |= TOD_ERR_TOD_SYNC_CHECK;

	if (current_topology == chiptod_topo_primary)
		backup_topo = chiptod_topo_secondary;
	else
		backup_topo = chiptod_topo_primary;

	for_each_chip(chip) {
		enum chiptod_chip_role role;

		/* Current chip TOD is already in stooped state */
		if (chip->id == this_cpu()->chip_id)
			continue;

		role = chiptod_get_chip_role(backup_topo, chip->id);

		/* Skip backup master chip TOD. */
		if (role == chiptod_chip_role_MDMT)
			continue;

		if (xscom_write(chip->id, TOD_ERROR_INJECT, terr))
			prerror("XSCOM error writing TOD_ERROR_INJ\n");

		if (chiptod_running_check(chip->id)) {
			prlog(PR_DEBUG,
			"Failed to stop TOD on slave CHIP [%d]\n",
								chip->id);
		}
	}
}

static bool is_topology_switch_required(void)
{
	int32_t active_master_chip;
	uint64_t tod_error;

	active_master_chip = chiptod_get_active_master();

	/* Check if TOD is running on Active master. */
	if (chiptod_master_running())
		return false;

	/*
	 * Check if sync/step network is running.
	 *
	 * If sync/step network is not running on current active topology
	 * then we need switch topology to recover from TOD error.
	 */
	if (!chiptod_sync_step_check_running(current_topology)) {
		prlog(PR_DEBUG, "Sync/Step network not running\n");
		return true;
	}

	/*
	 * Check if there is a step check error reported on
	 * Active master.
	 */
	if (xscom_read(active_master_chip, TOD_ERROR, &tod_error)) {
		prerror("XSCOM error reading TOD_ERROR reg\n");
		/*
		 * Can't do anything here. But we already found that
		 * sync/step network is running. Hence return false.
		 */
		return false;
	}

	if (tod_error & TOD_ERR_MP0_STEP_CHECK) {
		prlog(PR_DEBUG, "TOD step check error\n");
		return true;
	}

	return false;
}

static bool chiptod_backup_valid(void)
{
	enum chiptod_topology backup_topo;

	if (current_topology < 0)
		return false;

	if (current_topology == chiptod_topo_primary)
		backup_topo = chiptod_topo_secondary;
	else
		backup_topo = chiptod_topo_primary;

	if (chiptod_topology_info[backup_topo].status == chiptod_backup_master)
		return chiptod_sync_step_check_running(backup_topo);

	return false;
}

static void chiptod_topology_switch_complete(void)
{
	/*
	 * After the topology switch, we may have a non-functional backup
	 * topology, and we won't be able to recover from future TOD errors
	 * that requires topology switch. Someone needs to either fix it OR
	 * configure new functional backup topology.
	 *
	 * Bit 18 of the Pervasive FIR is used to signal that TOD error
	 * analysis needs to be performed. This allows FSP/PRD to
	 * investigate and re-configure new backup topology if required.
	 * Once new backup topology is configured and ready, FSP sends a
	 * mailbox command xE6, s/c 0x06, mod 0, to enable the backup
	 * topology.
	 *
	 * This isn't documented anywhere. This info is provided by FSP
	 * folks.
	 */
	if (xscom_writeme(LOCAL_CORE_FIR, LFIR_SWITCH_COMPLETE)) {
		prerror("XSCOM error writing LOCAL_CORE_FIR\n");
		return;
	}

	/* Save TOD control registers values. */
	chiptod_cache_tod_registers();

	prlog(PR_DEBUG, "Topology switch complete\n");
	print_topology_info();
}

/*
 * Sync up TOD with other chips and get TOD in running state.
 * Check if current topology is active and running. If not, then
 * trigger a topology switch.
 */
static int chiptod_start_tod(void)
{
	struct proc_chip *chip = NULL;

	/*  Do a topology switch if required. */
	if (is_topology_switch_required()) {
		int32_t mchip = chiptod_get_active_master();

		prlog(PR_DEBUG, "Need topology switch to recover\n");
		/*
		 * There is a failure in StepSync network in current
		 * active topology. TOD is not running on active master chip.
		 * We need to sync with backup master chip TOD.
		 * But before we do that we need to switch topology to make
		 * backup master as the new active master. Once we switch the
		 * topology we can then request TOD value from new active
		 * master. But make sure we move local chiptod to Not Set
		 * before requesting TOD value.
		 *
		 * Before triggering a topology switch, check if backup
		 * is valid and stop all slave TODs in backup topology.
		 */
		if (!chiptod_backup_valid()) {
			prerror("Backup master is not enabled. "
				"Can not do a topology switch.\n");
			goto error_out;
		}

		chiptod_stop_slave_tods();

		if (xscom_write(mchip, TOD_TTYPE_1, PPC_BIT(0))) {
			prerror("XSCOM error switching primary/secondary\n");
			goto error_out;
		}

		/* Update topology info. */
		current_topology = query_current_topology();
		chiptod_update_topology(chiptod_topo_primary);
		chiptod_update_topology(chiptod_topo_secondary);

		/*
		 * We just switched topologies to recover.
		 * Check if new master TOD is running.
		 */
		if (!chiptod_master_running()) {
			prerror("TOD is not running on new master.\n");
			goto error_out;
		}

		/*
		 * Enable step checkers on all Chip TODs
		 *
		 * During topology switch, step checkers are disabled
		 * on all Chip TODs by default. Enable them.
		 */
		if (xscom_writeme(TOD_TTYPE_2, PPC_BIT(0))) {
			prerror("XSCOM error enabling steppers\n");
			goto error_out;
		}

		chiptod_topology_switch_complete();
	}

	if (!chiptod_master_running()) {
		/*
		 * Active Master TOD is not running, which means it won't
		 * respond to TTYPE_3 request.
		 *
		 * Find a chip that has TOD in running state and configure
		 * it to respond to TTYPE_3 request.
		 */
		for_each_chip(chip) {
			if (chiptod_running_check(chip->id)) {
				if (chiptod_set_ttype4_mode(chip, true))
					break;
			}
		}
	}

	/* Switch local chiptod to "Not Set" state */
	if (xscom_writeme(TOD_LOAD_TOD_MOD, PPC_BIT(0))) {
		prerror("XSCOM error sending LOAD_TOD_MOD\n");
		goto error_out;
	}

	/*
	 * Request the current TOD value from another chip.
	 * This will move TOD in running state
	 */
	if (xscom_writeme(TOD_TTYPE_3, PPC_BIT(0))) {
		prerror("XSCOM error sending TTYPE_3\n");
		goto error_out;
	}

	/* Check if chip TOD is running. */
	if (!chiptod_poll_running())
		goto error_out;

	/* Restore the ttype4_mode. */
	chiptod_set_ttype4_mode(chip, false);
	return 1;

error_out:
	chiptod_unrecoverable = true;
	return 0;
}

static bool tfmr_recover_tb_errors(uint64_t tfmr)
{
	uint64_t tfmr_reset_error;
	unsigned long timeout = 0;

	/* Ask for automatic clear of errors */
	tfmr_reset_error = base_tfmr | SPR_TFMR_CLEAR_TB_ERRORS;

	/* Additionally pHyp sets these (write-1-to-clear ?) */
	if (tfmr & SPR_TFMR_TB_MISSING_SYNC)
		tfmr_reset_error |= SPR_TFMR_TB_MISSING_SYNC;

	if (tfmr & SPR_TFMR_TB_MISSING_STEP)
		tfmr_reset_error |= SPR_TFMR_TB_MISSING_STEP;

	/*
	 * write 1 to bit 45 to clear TB residue the error.
	 * TB register has already been reset to zero as part pre-recovery.
	 */
	if (tfmr & SPR_TFMR_TB_RESIDUE_ERR)
		tfmr_reset_error |= SPR_TFMR_TB_RESIDUE_ERR;

	if (tfmr & SPR_TFMR_FW_CONTROL_ERR)
		tfmr_reset_error |= SPR_TFMR_FW_CONTROL_ERR;

	if (tfmr & SPR_TFMR_TBST_CORRUPT)
		tfmr_reset_error |= SPR_TFMR_TBST_CORRUPT;

	mtspr(SPR_TFMR, tfmr_reset_error);

	/* We have to write "Clear TB Errors" again */
	tfmr_reset_error = base_tfmr | SPR_TFMR_CLEAR_TB_ERRORS;
	mtspr(SPR_TFMR, tfmr_reset_error);

	do {
		if (++timeout >= TIMEOUT_LOOPS) {
			prerror("TB error reset timeout !\n");
			return false;
		}
		tfmr = mfspr(SPR_TFMR);
		if (tfmr & SPR_TFMR_TFMR_CORRUPT) {
			prerror("TB error reset: corrupt TFMR !\n");
			return false;
		}
	} while (tfmr & SPR_TFMR_CLEAR_TB_ERRORS);
	return true;
}

bool tfmr_recover_local_errors(uint64_t tfmr)
{
	uint64_t tfmr_reset_errors = 0;

	if (tfmr & SPR_TFMR_DEC_PARITY_ERR) {
		/* Set DEC with all ones */
		mtspr(SPR_DEC, ~0);

		/* set bit 59 to clear TFMR DEC parity error. */
		tfmr_reset_errors |= SPR_TFMR_DEC_PARITY_ERR;
	}

	/*
	* Reset PURR/SPURR to recover. We also need help from KVM
	* layer to handle this change in PURR/SPURR. That needs
	* to be handled in kernel KVM layer. For now, to recover just
	* reset it.
	*/
	if (tfmr & SPR_TFMR_PURR_PARITY_ERR) {
		/* set PURR register with sane value or reset it. */
		mtspr(SPR_PURR, 0);

		/* set bit 57 to clear TFMR PURR parity error. */
		tfmr_reset_errors |= SPR_TFMR_PURR_PARITY_ERR;
	}

	if (tfmr & SPR_TFMR_SPURR_PARITY_ERR) {
		/* set PURR register with sane value or reset it. */
		mtspr(SPR_SPURR, 0);

		/* set bit 58 to clear TFMR PURR parity error. */
		tfmr_reset_errors |= SPR_TFMR_SPURR_PARITY_ERR;
	}

	/* Write TFMR twice to clear the error */
	mtspr(SPR_TFMR, base_tfmr | tfmr_reset_errors);
	mtspr(SPR_TFMR, base_tfmr | tfmr_reset_errors);

	/* Get fresh copy of TFMR */
	tfmr = mfspr(SPR_TFMR);

	/* Check if TFMR non-TB errors still present. */
	if (tfmr & tfmr_reset_errors) {
		prerror("TFMR non-TB error recovery failed! "
			"TFMR=0x%016lx\n", mfspr(SPR_TFMR));
		return false;
	}
	return true;
}

/*
 * TFMR parity error recovery as per pc_workbook:
 *	MT(TFMR) bits 11 and 60 are b’1’
 *	MT(HMER) all bits 1 except for bits 4,5
 */
bool recover_corrupt_tfmr(void)
{
	uint64_t tfmr;

	/* Get the base TFMR */
	tfmr = base_tfmr;

	/* Set bit 60 to clear TFMR parity error. */
	tfmr |= SPR_TFMR_TFMR_CORRUPT;
	mtspr(SPR_TFMR, tfmr);

	/* Write twice to clear the error */
	mtspr(SPR_TFMR, tfmr);

	/* Get fresh copy of TFMR */
	tfmr = mfspr(SPR_TFMR);

	/* Check if TFMR parity error still present. */
	if (tfmr & SPR_TFMR_TFMR_CORRUPT) {
		prerror("TFMR error recovery: corrupt TFMR !\n");
		return false;
	}

	/*
	 * Now that we have sane value in TFMR, check if Timebase machine
	 * state is in ERROR state. If yes, clear TB errors so that
	 * Timebase machine state changes to RESET state. Once in RESET state
	 * then we can then load TB with TOD value.
	 */
	if (GETFIELD(SPR_TFMR_TBST_ENCODED, tfmr) == TBST_STATE_ERROR) {
		if (!chiptod_reset_tb_errors())
			return false;
	}
	return true;
}

void tfmr_cleanup_core_errors(uint64_t tfmr)
{
	/* If HDEC is bad, clean it on all threads before we clear the
	 * error condition.
	 */
	if (tfmr & SPR_TFMR_HDEC_PARITY_ERROR)
		mtspr(SPR_HDEC, 0);

	/* If TB is invalid, clean it on all threads as well, it will be
	 * restored after the next rendez-vous
	 */
	if (!(tfmr & SPR_TFMR_TB_VALID)) {
		mtspr(SPR_TBWU, 0);
		mtspr(SPR_TBWU, 0);
	}
}

int tfmr_clear_core_errors(uint64_t tfmr)
{
	uint64_t tfmr_reset_errors = 0;

	/* return -1 if there is nothing to be fixed. */
	if (!(tfmr & SPR_TFMR_HDEC_PARITY_ERROR))
		return -1;

	tfmr_reset_errors |= SPR_TFMR_HDEC_PARITY_ERROR;

	/* Write TFMR twice to clear the error */
	mtspr(SPR_TFMR, base_tfmr | tfmr_reset_errors);
	mtspr(SPR_TFMR, base_tfmr | tfmr_reset_errors);

	return 1;
}

/*
 * Recover from TB and TOD errors.
 * Timebase register is per core and first thread that gets chance to
 * handle interrupt would fix actual TFAC errors and rest of the threads
 * from same core would see no errors. Return -1 if no errors have been
 * found. The caller (handle_hmi_exception) of this function would not
 * send an HMI event to host if return value is -1.
 *
 * Return values:
 *	0	<= Failed to recover from errors
 *	1	<= Successfully recovered from errors
 *	-1	<= No errors found. Errors are already been fixed.
 */
int chiptod_recover_tb_errors(bool *out_resynced)
{
	uint64_t tfmr;
	int rc = -1;

	*out_resynced = false;

	if (chiptod_primary < 0)
		return 0;

	lock(&chiptod_lock);

	/*
	 * Return if TOD is unrecoverable.
	 * The previous attempt to recover TOD has been failed.
	 */
	if (chiptod_unrecoverable) {
		rc = 0;
		goto error_out;
	}

	/* Get fresh copy of TFMR */
	tfmr = mfspr(SPR_TFMR);

	/*
	 * Check for TB errors.
	 * On Sync check error, bit 44 of TFMR is set. Check for it and
	 * clear it.
	 *
	 * In some rare situations we may have all TB errors already cleared,
	 * but TB stuck in waiting for new value from TOD with TFMR bit 18
	 * set to '1'. This uncertain state of TB would fail the process
	 * of getting TB back into running state. Get TB in clean initial
	 * state by clearing TB errors if TFMR[18] is set.
	 */
	if ((tfmr & SPR_TFMR_TB_MISSING_STEP) ||
		(tfmr & SPR_TFMR_TB_RESIDUE_ERR) ||
		(tfmr & SPR_TFMR_FW_CONTROL_ERR) ||
		(tfmr & SPR_TFMR_TBST_CORRUPT) ||
		(tfmr & SPR_TFMR_MOVE_CHIP_TOD_TO_TB) ||
		(tfmr & SPR_TFMR_TB_MISSING_SYNC)) {
		if (!tfmr_recover_tb_errors(tfmr)) {
			rc = 0;
			goto error_out;
		}
	}

	/*
	 * Check for TOD sync check error.
	 * On TOD errors, bit 51 of TFMR is set. If this bit is on then we
	 * need to fetch TOD error register and recover from TOD errors.
	 * Bit 33 of TOD error register indicates sync check error.
	 */
	if (tfmr & SPR_TFMR_CHIP_TOD_INTERRUPT)
		rc = __chiptod_recover_tod_errors();

	/* Check if TB is running. If not then we need to get it running. */
	if (!(tfmr & SPR_TFMR_TB_VALID)) {
		rc = 0;

		/* Place TB in Notset state. */
		if (!chiptod_mod_tb())
			goto error_out;

		/*
		 * Before we move TOD to core TB check if TOD is running.
		 * If not, then get TOD in running state.
		 */
		if (!chiptod_running_check(this_cpu()->chip_id))
			if (!chiptod_start_tod())
				goto error_out;

		/* Move chiptod value to core TB */
		if (!chiptod_to_tb())
			goto error_out;

		*out_resynced = true;

		/* We have successfully able to get TB running. */
		rc = 1;
	}

error_out:
	unlock(&chiptod_lock);
	return rc;
}

static int64_t opal_resync_timebase(void)
{
	/* Mambo and qemu doesn't simulate the chiptod */
	if (chip_quirk(QUIRK_NO_CHIPTOD))
		return OPAL_SUCCESS;

	if (!chiptod_wakeup_resync()) {
		prerror("OPAL: Resync timebase failed on CPU 0x%04x\n",
			this_cpu()->pir);
		return OPAL_HARDWARE;
	}
	return OPAL_SUCCESS;
}
opal_call(OPAL_RESYNC_TIMEBASE, opal_resync_timebase, 0);

static void chiptod_print_tb(void *data __unused)
{
	prlog(PR_DEBUG, "PIR 0x%04x TB=%lx\n", this_cpu()->pir,
				mfspr(SPR_TBRL));
}

static bool chiptod_probe(void)
{
	struct dt_node *np;

	dt_for_each_compatible(dt_root, np, "ibm,power-chiptod") {
		uint32_t chip;

		/* Old DT has chip-id in chiptod node, newer only in the
		 * parent xscom bridge
		 */
		chip = dt_get_chip_id(np);

		if (dt_has_node_property(np, "primary", NULL)) {
			chiptod_primary = chip;
			if (dt_node_is_compatible(np, "ibm,power7-chiptod"))
				chiptod_type = chiptod_p7;
			if (dt_node_is_compatible(np, "ibm,power8-chiptod"))
				chiptod_type = chiptod_p8;
			if (dt_node_is_compatible(np, "ibm,power9-chiptod"))
				chiptod_type = chiptod_p9;
		}

		if (dt_has_node_property(np, "secondary", NULL))
			chiptod_secondary = chip;

	}

	if (chiptod_type == chiptod_unknown) {
		prerror("Unknown TOD type !\n");
		return false;
	}

	return true;
}

static void chiptod_discover_new_backup(enum chiptod_topology topo)
{
	struct proc_chip *chip = NULL;

	/* Scan through available chips to find new backup master chip */
	for_each_chip(chip) {
		if (_chiptod_get_chip_status(chip->id) == chiptod_backup_master)
			break;
	}

	/* Found new backup master chip. Update the topology info */
	if (chip) {
		prlog(PR_DEBUG, "New backup master: CHIP [%d]\n",
								chip->id);

		if (topo == chiptod_topo_primary)
			chiptod_primary = chip->id;
		else
			chiptod_secondary = chip->id;
		chiptod_topology_info[topo].id = chip->id;
		chiptod_update_topology(topo);

		prlog(PR_DEBUG,
			"Backup topology configuration changed.\n");
		print_topology_info();
	}

	/*
	 * Topology configuration has changed. Save TOD control registers
	 * values.
	 */
	chiptod_cache_tod_registers();
}

/*
 * Enable/disable backup topology.
 * If request is to enable topology, then discover new backup master
 * chip and update the topology configuration info. If the request is
 * to disable topology, then mark the current backup topology as disabled.
 * Return error (-1) if the action is requested on currenlty active
 * topology.
 *
 * Return values:
 *	true	<= Success
 *	false	<= Topology is active and in use.
 */
bool chiptod_adjust_topology(enum chiptod_topology topo, bool enable)
{
	uint8_t rc = true;
	/*
	 * The FSP can only request that the currently inactive topology
	 * be disabled or enabled. If the requested topology is currently
	 * the active topology, then fail this request with a -1 (TOD
	 * topology in use) status as return code.
	 */
	lock(&chiptod_lock);
	if (topo == current_topology) {
		rc = false;
		goto out;
	}

	if (enable)
		chiptod_discover_new_backup(topo);
	else
		chiptod_topology_info[topo].status = chiptod_backup_disabled;
out:
	unlock(&chiptod_lock);
	return rc;
}

static void chiptod_init_topology_info(void)
{
	/* Find and update current topology in use. */
	current_topology = query_current_topology();

	/* Initialized primary topology chip config info */
	chiptod_topology_info[chiptod_topo_primary].id = chiptod_primary;
	chiptod_update_topology(chiptod_topo_primary);

	/* Initialized secondary topology chip config info */
	chiptod_topology_info[chiptod_topo_secondary].id = chiptod_secondary;
	chiptod_update_topology(chiptod_topo_secondary);

	/* Cache TOD control registers values. */
	chiptod_cache_tod_registers();
	print_topology_info();
}

void chiptod_init(void)
{
	struct cpu_thread *cpu0, *cpu;
	bool sres;

	/* Mambo and qemu doesn't simulate the chiptod */
	if (chip_quirk(QUIRK_NO_CHIPTOD))
		return;

	op_display(OP_LOG, OP_MOD_CHIPTOD, 0);

	if (!chiptod_probe()) {
		prerror("Failed ChipTOD detection !\n");
		op_display(OP_FATAL, OP_MOD_CHIPTOD, 0);
		abort();
	}

	op_display(OP_LOG, OP_MOD_CHIPTOD, 1);

	/* Pick somebody on the primary */
	cpu0 = find_cpu_by_chip_id(chiptod_primary);

	/* Calculate the base TFMR value used for everybody */
	chiptod_setup_base_tfmr();

	prlog(PR_DEBUG, "Base TFMR=0x%016llx\n", base_tfmr);

	/* Schedule master sync */
	sres = false;
	cpu_wait_job(cpu_queue_job(cpu0, "chiptod_sync_master",
				   chiptod_sync_master, &sres), true);
	if (!sres) {
		op_display(OP_FATAL, OP_MOD_CHIPTOD, 2);
		abort();
	}

	op_display(OP_LOG, OP_MOD_CHIPTOD, 2);

	/* Schedule slave sync */
	for_each_available_cpu(cpu) {
		/* Skip master */
		if (cpu == cpu0)
			continue;

		/* Queue job */
		sres = false;
		cpu_wait_job(cpu_queue_job(cpu, "chiptod_sync_slave",
					   chiptod_sync_slave, &sres),
			     true);
		if (!sres) {
			op_display(OP_WARN, OP_MOD_CHIPTOD, 3|(cpu->pir << 8));

			/* Disable threads */
			cpu_disable_all_threads(cpu);
		}
		op_display(OP_LOG, OP_MOD_CHIPTOD, 3|(cpu->pir << 8));
	}

	/* Display TBs */
	for_each_available_cpu(cpu) {
		/* Only do primaries, not threads */
		if (cpu->is_secondary)
			continue;
		cpu_wait_job(cpu_queue_job(cpu, "chiptod_print_tb",
					   chiptod_print_tb, NULL), true);
	}

	chiptod_init_topology_info();
	op_display(OP_LOG, OP_MOD_CHIPTOD, 4);
}

/* CAPP timebase sync */

static bool chiptod_capp_reset_tb_errors(uint32_t chip_id,
					 uint32_t tfmr_addr,
					 uint32_t offset)
{
	uint64_t tfmr;
	unsigned long timeout = 0;

	/* Ask for automatic clear of errors */
	tfmr = base_tfmr | SPR_TFMR_CLEAR_TB_ERRORS;

	/* Additionally pHyp sets these (write-1-to-clear ?) */
	tfmr |= SPR_TFMR_TB_MISSING_SYNC;
	tfmr |= SPR_TFMR_TB_MISSING_STEP;
	tfmr |= SPR_TFMR_TB_RESIDUE_ERR;
	tfmr |= SPR_TFMR_TBST_CORRUPT;
	tfmr |= SPR_TFMR_TFMR_CORRUPT;

	/* Write CAPP TFMR */
	xscom_write(chip_id, tfmr_addr + offset, tfmr);

	/* We have to write "Clear TB Errors" again */
	tfmr = base_tfmr | SPR_TFMR_CLEAR_TB_ERRORS;
	/* Write CAPP TFMR */
	xscom_write(chip_id, tfmr_addr + offset, tfmr);

	do {
		if (++timeout >= TIMEOUT_LOOPS) {
			prerror("CAPP: TB error reset timeout !\n");
			return false;
		}
		/* Read CAPP TFMR */
		xscom_read(chip_id, tfmr_addr + offset, &tfmr);
		if (tfmr & SPR_TFMR_TFMR_CORRUPT) {
			prerror("CAPP: TB error reset: corrupt TFMR!\n");
			return false;
		}
	} while (tfmr & SPR_TFMR_CLEAR_TB_ERRORS);
	return true;
}

static bool chiptod_capp_mod_tb(uint32_t chip_id, uint32_t tfmr_addr,
				uint32_t offset)
{
	uint64_t timeout = 0;
	uint64_t tfmr;

	/* Switch CAPP timebase to "Not Set" state */
	tfmr = base_tfmr | SPR_TFMR_LOAD_TOD_MOD;
	xscom_write(chip_id, tfmr_addr + offset, tfmr);
	do {
		if (++timeout >= (TIMEOUT_LOOPS*2)) {
			prerror("CAPP: TB \"Not Set\" timeout\n");
			return false;
		}
		xscom_read(chip_id, tfmr_addr + offset, &tfmr);
		if (tfmr & SPR_TFMR_TFMR_CORRUPT) {
			prerror("CAPP: TB \"Not Set\" TFMR corrupt\n");
			return false;
		}
		if (GETFIELD(SPR_TFMR_TBST_ENCODED, tfmr) == 9) {
			prerror("CAPP: TB \"Not Set\" TOD in error state\n");
			return false;
		}
	} while (tfmr & SPR_TFMR_LOAD_TOD_MOD);

	return true;
}

static bool chiptod_wait_for_chip_sync(void)
{
	uint64_t tfmr;
	uint64_t timeout = 0;

	/* Read core TFMR, mask bit 42, write core TFMR back */
	tfmr = mfspr(SPR_TFMR);
	tfmr &= ~SPR_TFMR_TB_SYNC_OCCURED;
	mtspr(SPR_TFMR, tfmr);

	/* Read core TFMR until the TB sync occurred */
	do {
		if (++timeout >= TIMEOUT_LOOPS) {
			prerror("No sync pulses\n");
			return false;
		}
		tfmr = mfspr(SPR_TFMR);
	} while (!(tfmr & SPR_TFMR_TB_SYNC_OCCURED));
	return true;
}

static bool chiptod_capp_check_tb_running(uint32_t chip_id,
					  uint32_t tfmr_addr,
					  uint32_t offset)
{
	uint64_t tfmr;
	uint64_t timeout = 0;

	/* Read CAPP TFMR until TB becomes valid */
	do {
		if (++timeout >= (TIMEOUT_LOOPS*2)) {
			prerror("CAPP: TB Invalid!\n");
			return false;
		}
		xscom_read(chip_id, tfmr_addr + offset, &tfmr);
		if (tfmr & SPR_TFMR_TFMR_CORRUPT) {
			prerror("CAPP: TFMR corrupt!\n");
			return false;
		}
	} while (!(tfmr & SPR_TFMR_TB_VALID));
	return true;
}

bool chiptod_capp_timebase_sync(unsigned int chip_id, uint32_t tfmr_addr,
				uint32_t tb_addr, uint32_t offset)
{
	uint64_t tfmr;
	uint64_t capp_tb;
	int64_t delta;
	unsigned int retry = 0;

	/* Set CAPP TFMR to base tfmr value */
	xscom_write(chip_id, tfmr_addr + offset, base_tfmr);

	/* Reset CAPP TB errors before attempting the sync */
	if (!chiptod_capp_reset_tb_errors(chip_id, tfmr_addr, offset))
		return false;

	/* Switch CAPP TB to "Not Set" state */
	if (!chiptod_capp_mod_tb(chip_id, tfmr_addr, offset))
		return false;

	/* Sync CAPP TB with core TB, retry while difference > 16usecs */
	do {
		if (retry++ > 5) {
			prerror("CAPP: TB sync: giving up!\n");
			return false;
		}

		/* Make CAPP ready to get the TB, wait for chip sync */
		tfmr = base_tfmr | SPR_TFMR_MOVE_CHIP_TOD_TO_TB;
		xscom_write(chip_id, tfmr_addr + offset, tfmr);
		if (!chiptod_wait_for_chip_sync())
			return false;

		/* Set CAPP TB from core TB */
		xscom_write(chip_id, tb_addr + offset, mftb());

		/* Wait for CAPP TFMR tb_valid bit */
		if (!chiptod_capp_check_tb_running(chip_id, tfmr_addr, offset))
			return false;

		/* Read CAPP TB, read core TB, compare */
		xscom_read(chip_id, tb_addr + offset, &capp_tb);
		delta = mftb() - capp_tb;
		if (delta < 0)
			delta = -delta;
	} while (tb_to_usecs(delta) > 16);

	return true;
}
