/* 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.
 */

#ifndef __XSCOM_H
#define __XSCOM_H

#include <stdint.h>
#include <processor.h>
#include <cpu.h>

/*
 * SCOM "partID" definitions:
 *
 * All Ids are 32-bits long, top nibble is reserved for the
 * 'type' field:
 *     0x0 = Processor Chip
 *     0x8 = Memory Buffer (Centaur) Chip
 *     0x4 = EX/Core Chiplet
 *
 * Processor Chip = Logical Fabric Id = PIR>>7
 *     0b0000.0000.0000.0000.0000.0000.00NN.NCCC
 *     N=Node, C=Chip
 * Centaur Chip = Associated Processor Chip with memory channel
 * appended and flag set
 *     0b1000.0000.0000.0000.0000.00NN.NCCC.MMMM
 *     N=Node, C=Chip, M=Memory Channel
 * Processor EX/Core chiplet = PIR >> 3 with flag set.
 * On P8:
 *     0b0100.0000.0000.0000.0000.00NN.NCCC.PPPP
 * On P9:
 *     0b0100.0000.0000.0000.0000.0NNN.CCCP.PPPP
 *     N=Node, C=Chip, P=Processor core
 */

/*
 * SCOM Address definition extracted from HWPs for documentation
 * purposes
 *
 * "Normal" (legacy) format
 *
 *            111111 11112222 22222233 33333333 44444444 44555555 55556666
 * 01234567 89012345 67890123 45678901 23456789 01234567 89012345 67890123
 * -------- -------- -------- -------- -------- -------- -------- --------
 * 00000000 00000000 00000000 00000000 0MCCCCCC ????PPPP 00LLLLLL LLLLLLLL
 *                                      ||          |    |
 *                                      ||          |    `-> Local Address*
 *                                      ||          |
 *                                      ||          `-> Port
 *                                      ||
 *                                      |`-> Chiplet ID**
 *                                      |
 *                                      `-> Multicast bit
 *
 *  * Local address is composed of "00" + 4-bit ring + 10-bit ID
 *    The 10-bit ID is usually 4-bit sat_id and 6-bit reg_id
 *
 * ** Chiplet ID turns into multicast operation type and group number
 *    if the multicast bit is set
 *
 * "Indirect" format
 *
 *
 *            111111 11112222 22222233 33333333 44444444 44555555 55556666
 * 01234567 89012345 67890123 45678901 23456789 01234567 89012345 67890123
 * -------- -------- -------- -------- -------- -------- -------- --------
 * 10000000 0000IIII IIIIIGGG GGGLLLLL 0MCCCCCC ????PPPP 00LLLLLL LLLLLLLL
 *              |         |      |      ||          |    |
 *              |         |      |      ||          |    `-> Local Address*
 *              |         |      |      ||          |
 *              |         |      |      ||          `-> Port
 *              |         |      |      ||
 *              |         |      |      |`-> Chiplet ID**
 *              |         |      |      |
 *              |         |      |      `-> Multicast bit
 *              |         |      |
 *              |         |      `-> Lane ID
 *              |         |
 *              |         `-> RX or TX Group ID
 *              |
 *              `-> Indirect Register Address
 *
 *  * Local address is composed of "00" + 4-bit ring + 4-bit sat_id + "111111"
 *
 * ** Chiplet ID turns into multicast operation type and group number
 *    if the multicast bit is set
 */

/*
 * Generate a local address from a given ring/satellite/offset
 * combination:
 *
 *     Ring    Satellite     offset
 *  +---------+---------+-------------+
 *  |    4    |    4    |     6       |
 *  +---------+---------+-------------+
 */
#define XSCOM_SAT(_r, _s, _o)	\
	(((_r) << 10) | ((_s) << 6) | (_o))

/*
 * Additional useful definitions for P8
 */
#define P8_EX_PCB_SLAVE_BASE	0x100F0000

#define XSCOM_ADDR_P8_EX_SLAVE(core, offset) \
     (P8_EX_PCB_SLAVE_BASE | (((core) & 0xF) << 24) | ((offset) & 0xFFFF))

#define XSCOM_ADDR_P8_EX(core, addr) \
		((((core) & 0xF) << 24) | (addr))

/*
 * Additional useful definitions for P9
 */

/* An EQ is a quad (also named an EP) */
#define XSCOM_ADDR_P9_EP(core, addr) \
	(((((core) & 0x1c) + 0x40) << 22) | (addr))
#define XSCOM_ADDR_P9_EP_SLAVE(core, addr) \
	XSCOM_ADDR_P9_EP(core, (addr) | 0xf0000)

/* An EX is a pair of cores. They are accessed via their corresponding EQs
 * with bit 0x400 indicating which of the 2 EX to address
 */
#define XSCOM_ADDR_P9_EX(core, addr) \
	(XSCOM_ADDR_P9_EP(core, addr | (((core) & 2) << 9)))

/* An EC is an individual core and has its own XSCOM addressing */
#define XSCOM_ADDR_P9_EC(core, addr) \
	(((((core) & 0x1F) + 0x20) << 24) | (addr))
#define XSCOM_ADDR_P9_EC_SLAVE(core, addr) \
	XSCOM_ADDR_P9_EC(core, (addr) | 0xf0000)

/* Power 9 EC slave per-core power mgt slave registers */
#define EC_PPM_SPECIAL_WKUP_OTR		0x010A
#define EC_PPM_SPECIAL_WKUP_FSP		0x010B
#define EC_PPM_SPECIAL_WKUP_OCC		0x010C
#define EC_PPM_SPECIAL_WKUP_HYP		0x010D

/************* XXXX Move these P8 only registers elswhere !!! ****************/

/* Per core power mgt registers */
#define PM_OHA_MODE_REG		0x1002000D
#define L2_FIR_ACTION1		0x10012807

/* EX slave per-core power mgt slave regisers */
#define EX_PM_GP0			0x0100
#define EX_PM_GP1			0x0103
#define EX_PM_CLEAR_GP1			0x0104 /* AND SCOM */
#define EX_PM_SET_GP1			0x0105 /* OR SCOM */
#define EX_PM_SPECIAL_WAKEUP_FSP	0x010B
#define EX_PM_SPECIAL_WAKEUP_OCC	0x010C
#define EX_PM_SPECIAL_WAKEUP_PHYP	0x010D
#define EX_PM_IDLE_STATE_HISTORY_PHYP	0x0110
#define EX_PM_IDLE_STATE_HISTORY_FSP	0x0111
#define EX_PM_IDLE_STATE_HISTORY_OCC	0x0112
#define EX_PM_IDLE_STATE_HISTORY_PERF	0x0113
#define EX_PM_CORE_PFET_VRET		0x0130
#define EX_PM_CORE_ECO_VRET		0x0150
#define EX_PM_PPMSR			0x0153
#define EX_PM_PPMCR			0x0159

/* Power mgt bits in GP0 */
#define EX_PM_GP0_SPECIAL_WAKEUP_DONE		PPC_BIT(31)

/* Power mgt settings in GP1 */
#define EX_PM_SETUP_GP1_FAST_SLEEP		0xD800000000000000ULL
#define EX_PM_SETUP_GP1_DEEP_SLEEP		0x2400000000000000ULL
#define EX_PM_SETUP_GP1_FAST_SLEEP_DEEP_WINKLE	0xC400000000000000ULL
#define EX_PM_GP1_SLEEP_WINKLE_MASK		0xFC00000000000000ULL
#define EX_PM_SETUP_GP1_PM_SPR_OVERRIDE_EN	0x0010000000000000ULL
#define EX_PM_SETUP_GP1_DPLL_FREQ_OVERRIDE_EN	0x0020000000000000ULL

/* Fields in history regs */
#define EX_PM_IDLE_ST_HIST_PM_STATE_MASK	PPC_BITMASK(0, 2)
#define EX_PM_IDLE_ST_HIST_PM_STATE_LSH		PPC_BITLSHIFT(2)

/***************************************************************************/

/* Definitions relating to indirect XSCOMs shared with centaur */
#define XSCOM_ADDR_IND_FLAG		PPC_BIT(0)
#define XSCOM_ADDR_IND_ADDR		PPC_BITMASK(12,31)
#define XSCOM_ADDR_IND_DATA		PPC_BITMASK(48,63)

#define XSCOM_DATA_IND_READ		PPC_BIT(0)
#define XSCOM_DATA_IND_COMPLETE		PPC_BIT(32)
#define XSCOM_DATA_IND_ERR		PPC_BITMASK(33,35)
#define XSCOM_DATA_IND_DATA		PPC_BITMASK(48,63)
#define XSCOM_DATA_IND_FORM1_DATA	PPC_BITMASK(12,63)

/* HB folks say: try 10 time for now */
#define XSCOM_IND_MAX_RETRIES		10

/* Max number of retries when XSCOM remains busy */
#define XSCOM_BUSY_MAX_RETRIES		3000

/* Max number of retries for xscom clearing recovery. */
#define XSCOM_CLEAR_MAX_RETRIES		10

/* xscom clear address range/mask */
#define XSCOM_CLEAR_RANGE_START		0x20010A00
#define XSCOM_CLEAR_RANGE_END		0x20010ABF
#define XSCOM_CLEAR_RANGE_MASK		0x200FFBFF

/* Retry count after which to reset XSCOM, if still busy */
#define XSCOM_BUSY_RESET_THRESHOLD	1000

/*
 * Error handling:
 *
 * Error codes TBD, 0 = success
 */

/* Use only in select places where multiple SCOMs are time/latency sensitive */
extern void _xscom_lock(void);
extern int _xscom_read(uint32_t partid, uint64_t pcb_addr, uint64_t *val, bool take_lock);
extern int _xscom_write(uint32_t partid, uint64_t pcb_addr, uint64_t val, bool take_lock);
extern void _xscom_unlock(void);


/* Targeted SCOM access */
static inline int xscom_read(uint32_t partid, uint64_t pcb_addr, uint64_t *val)
{
	return _xscom_read(partid, pcb_addr, val, true);
}
static inline int xscom_write(uint32_t partid, uint64_t pcb_addr, uint64_t val) {
	return _xscom_write(partid, pcb_addr, val, true);
}
extern int xscom_write_mask(uint32_t partid, uint64_t pcb_addr, uint64_t val, uint64_t mask);

/* This chip SCOM access */
extern int xscom_readme(uint64_t pcb_addr, uint64_t *val);
extern int xscom_writeme(uint64_t pcb_addr, uint64_t val);
extern void xscom_init(void);

/* Mark XSCOM lock as being in console path */
extern void xscom_used_by_console(void);

/* Returns true if XSCOM can be used. Typically this returns false if
 * the current CPU holds the XSCOM lock (to avoid re-entrancy from error path).
 */
extern bool xscom_ok(void);

extern int64_t xscom_read_cfam_chipid(uint32_t partid, uint32_t *chip_id);
extern int64_t xscom_trigger_xstop(void);

#endif /* __XSCOM_H */
