/* 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 __P7IOC_H
#define __P7IOC_H

#include <cec.h>
#include <pci.h>

#include <ccan/container_of/container_of.h>

/*
 * Memory windows and BUID assignment
 *
 * - GX BAR assignment
 *
 *   I don't know of any spec here, so we're going to mimmic what
 *   OPAL seems to be doing:
 *
 *     - BAR 0 :   32M, disabled. We just leave it alone.
 *     - BAR 1 :    8G, enabled. Appears to correspond to the MMIO
 *                      space of the IOC itself and the PCI IO space
 *     - BAR 2:   128G,
 *     - BAR 3:   128G,
 *     - BAR 4:   128G, all 3 contiguous, forming a single 368G region
 *                      and is used for M32 and M64 PHB windows.
 *
 * - Memory map
 *
 *    MWIN1 = BAR1 (8G)
 *    MWIN2 = BAR2,3,4 (384G)
 *
 *    MWIN2 is divided into 6 * 4G regions for use by M32's (*) and
 *    6 * 32G regions for use by M64's.
 *
 * (*) The M32 will typically be configured to only 2G or so, however
 *     the OS is in control of that setting, and since we have to reserve
 *     a power of two, we reserve the whole 4G.
 *
 *    - RGC registers: MWIN1 + 0x00000000
 *    - PHBn IO space: MWIN1 + 0x01000000 + n * 0x00800000 (8M each)
 *    - PHBn M32     : MWIN2 + n * 0x1_00000000 (4G each)
 *    - PHBn M64     : MWIN2 + (n + 1) * 0x8_00000000 (32G each)
 *
 * - BUID map. The RGC has interrupts, each PHB has then its own
 *             interrupts (errors etc...), 4 LSIs and 256 LSIs so
 *             respectively 1 BUID for self, 1 for LSIs and 16 for LSIs
 *
 *   We keep all BUIDs below 0x10 reserved. They will be used for things
 *   like the PSI controller, the NX unit, etc.. in the P7 chip.
 *
 *    RGC	: 0x010
 *    PHBn LSI	: 0x040 + n * 0x40 (   1 BUID)
 *    PHBn MSI  : 0x060 + n * 0x40 (0x10 BUIDs)
 *
 * -> For routing, each PHB gets a block of 0x40 BUIDs:
 *
 *	from 0x40 * (n + 1) to 0x7f * (n + 1)
 */

/* Some definitions resulting from the above description
 *
 * Note: A better approach might be to read the GX BAR content
 *       and isolate the biggest contiguous windows. From there
 *       we could divide things algorithmically and thus be
 *       less sensitive to a change in the memory map by the FSP
 */
#define MWIN1_SIZE	0x200000000ul	/* MWIN1 is 8G */
#define MWIN2_SIZE     0x6000000000ul	/* MWIN2 is 384G */
#define PHB_IO_OFFSET	 0x01000000ul	/* Offset of PHB IO space in MWIN1 */
#define PHB_IO_SIZE	 0x00800000ul
#define PHB_M32_OFFSET	        0x0ul	/* Offset of PHB M32 space in MWIN2 */
#define PHB_M32_SIZE	0x100000000ul
#define PHB_M64_OFFSET	0x800000000ul	/* Offset of PHB M64 space in MWIN2 */
#define PHB_M64_SIZE	0x800000000ul
#define RGC_BUID_OFFSET		0x10	/* Offset of RGC BUID */
#define PHB_BUID_OFFSET		0x40	/* Offset of PHB BUID blocks */
#define PHB_BUID_SIZE		0x40	/* Size of PHB BUID blocks */
#define PHB_BUID_LSI_OFFSET	0x00	/* Offset of LSI in PHB BUID block */
#define PHB_BUID_MSI_OFFSET	0x20	/* Offset of MSI in PHB BUID block */
#define PHB_BUID_MSI_SIZE	0x10	/* Size of PHB MSI BUID block */

#define PHBn_IO_BASE(n)		(PHB_IO_OFFSET + (n) * PHB_IO_SIZE)
#define PHBn_M32_BASE(n)	(PHB_M32_OFFSET + (n) * PHB_M32_SIZE)
#define PHBn_M64_BASE(n)	(PHB_M64_OFFSET + (n) * PHB_M64_SIZE)
#define PHBn_BUID_BASE(n)	(PHB_BUID_OFFSET + (n) * PHB_BUID_SIZE)

#define BUID_TO_PHB(buid)	(((buid) - PHB_BUID_OFFSET) / PHB_BUID_SIZE)

/* p7ioc has 6 PHBs */
#define P7IOC_NUM_PHBS		6

/* M32 window setting at boot:
 *
 * To allow for DMA, we need to split the 32-bit PCI address space between
 * MMIO and DMA. For now, we use a 2G/2G split with MMIO at the top.
 *
 * Note: The top 64K of the M32 space are used by MSIs. This is not
 * visible here but need to be conveyed to the OS one way or another
 *
 * Note2: The space reserved in the system address space for M32 is always
 * 4G. That we chose to use a smaller portion of it is not relevant to
 * the upper levels. To keep things consistent, the offset we apply to
 * the window start is also applied on the host side.
 */
#define M32_PCI_START	0x80000000
#define M32_PCI_SIZE	0x80000000

/* PHB registers exist in both a hard coded space and a programmable
 * AIB space. We program the latter to the values recommended in the
 * documentation:
 *
 *	0x80000 + n * 0x10000
 */
#define PHBn_ASB_BASE(n)	(((n) << 16))
#define PHBn_ASB_SIZE		0x10000ul
#define PHBn_AIB_BASE(n)	(0x80000ul + ((n) << 16))
#define PHBn_AIB_SIZE		0x10000ul

/*
 * LSI interrupts
 *
 * The LSI interrupt block supports 8 interrupts. 4 of them are the
 * standard PCIe INTA..INTB. The rest is for additional functions
 * of the PHB
 */
#define PHB_LSI_PCIE_INTA		0
#define PHB_LSI_PCIE_INTB		1
#define PHB_LSI_PCIE_INTC		2
#define PHB_LSI_PCIE_INTD		3
#define PHB_LSI_PCIE_HOTPLUG		4
#define PHB_LSI_PCIE_PERFCTR		5
#define PHB_LSI_PCIE_UNUSED		6
#define PHB_LSI_PCIE_ERROR		7

/* P7IOC PHB slot states */
#define P7IOC_SLOT_NORMAL		PCI_SLOT_STATE_NORMAL
#define P7IOC_SLOT_LINK			PCI_SLOT_STATE_LINK
#define   P7IOC_SLOT_LINK_START		(P7IOC_SLOT_LINK + 1)
#define   P7IOC_SLOT_LINK_WAIT		(P7IOC_SLOT_LINK + 2)
#define P7IOC_SLOT_HRESET		PCI_SLOT_STATE_HRESET
#define   P7IOC_SLOT_HRESET_START	(P7IOC_SLOT_HRESET + 1)
#define   P7IOC_SLOT_HRESET_TRAINING	(P7IOC_SLOT_HRESET + 2)
#define   P7IOC_SLOT_HRESET_DELAY	(P7IOC_SLOT_HRESET + 3)
#define   P7IOC_SLOT_HRESET_DELAY2	(P7IOC_SLOT_HRESET + 4)
#define P7IOC_SLOT_FRESET		PCI_SLOT_STATE_FRESET
#define   P7IOC_SLOT_FRESET_START	(P7IOC_SLOT_FRESET + 1)
#define   P7IOC_SLOT_FRESET_TRAINING	(P7IOC_SLOT_FRESET + 2)
#define   P7IOC_SLOT_FRESET_POWER_OFF	(P7IOC_SLOT_FRESET + 3)
#define   P7IOC_SLOT_FRESET_POWER_ON	(P7IOC_SLOT_FRESET + 4)
#define   P7IOC_SLOT_FRESET_ASSERT	(P7IOC_SLOT_FRESET + 5)
#define   P7IOC_SLOT_FRESET_DEASSERT	(P7IOC_SLOT_FRESET + 6)
#define P7IOC_SLOT_CRESET		PCI_SLOT_STATE_CRESET
#define   P7IOC_SLOT_CRESET_START	(P7IOC_SLOT_CRESET + 1)

/*
 * In order to support error detection and recovery on different
 * types of IOCs (e.g. P5IOC, P7IOC, P8IOC), the best bet would
 * be make the implementation to be 2 layers: OPAL layer and IOC
 * layer. The OPAL layer just handles the general information and
 * IOC layer should process much more detailed information, which
 * is sensitive to itself.
 */
#define P7IOC_ERR_SRC_NONE	0
#define P7IOC_ERR_SRC_EI	1
#define P7IOC_ERR_SRC_RGC	2
#define P7IOC_ERR_SRC_BI_UP	3
#define P7IOC_ERR_SRC_BI_DOWN	4
#define P7IOC_ERR_SRC_CI_P0	5
#define P7IOC_ERR_SRC_CI_P1	6
#define P7IOC_ERR_SRC_CI_P2	7
#define P7IOC_ERR_SRC_CI_P3	8
#define P7IOC_ERR_SRC_CI_P4	9
#define P7IOC_ERR_SRC_CI_P5	10
#define P7IOC_ERR_SRC_CI_P6	11
#define P7IOC_ERR_SRC_CI_P7	12
#define P7IOC_ERR_SRC_PHB0	13
#define P7IOC_ERR_SRC_PHB1	14
#define P7IOC_ERR_SRC_PHB2	15
#define P7IOC_ERR_SRC_PHB3	16
#define P7IOC_ERR_SRC_PHB4	17
#define P7IOC_ERR_SRC_PHB5	18
#define P7IOC_ERR_SRC_MISC	19
#define P7IOC_ERR_SRC_I2C	20
#define P7IOC_ERR_SRC_LAST	21

#define P7IOC_ERR_CLASS_NONE	0
#define P7IOC_ERR_CLASS_GXE	1
#define P7IOC_ERR_CLASS_PLL	2
#define P7IOC_ERR_CLASS_RGA	3
#define P7IOC_ERR_CLASS_PHB	4
#define P7IOC_ERR_CLASS_ER	5
#define P7IOC_ERR_CLASS_INF	6
#define P7IOC_ERR_CLASS_MAL	7
#define P7IOC_ERR_CLASS_LAST	8

/*
 * P7IOC error descriptor. For errors from PHB and PE, they
 * will be cached to the corresponding PHBs. However, the
 * left errors (e.g. EI, CI Port0/1) will be cached to the
 * IOC directly.
 */
struct p7ioc_err {
	uint32_t err_src;
	uint32_t err_class;
	uint32_t err_bit;
};

struct p7ioc;

#define P7IOC_PHB_CFG_USE_ASB	0x00000001 /* ASB to access PCI-CFG     */
#define P7IOC_PHB_CFG_BLOCKED	0x00000002 /* PCI-CFG blocked except 0	*/

struct p7ioc_phb {
	uint8_t				index;	/* 0..5 index inside p7ioc */
	uint8_t				gen;
	uint32_t			flags;
	bool				broken;
#define P7IOC_REV_DD10	0x00a20001
#define P7IOC_REV_DD11	0x00a20002
	uint32_t			rev;	/* Both major and minor have 2 bytes */
	void				*regs_asb;
	void				*regs;	/* AIB regs */
	uint32_t			buid_lsi;
	uint32_t			buid_msi;
	uint64_t			io_base;
	uint64_t			m32_base;
	uint64_t			m64_base;
	int64_t				ecap;	/* cached PCI-E cap offset */
	int64_t				aercap; /* cached AER ecap offset */
	uint64_t			lxive_cache[8];
	uint64_t			mxive_cache[256];
	uint64_t			mve_cache[256];
	uint64_t			peltm_cache[128];
	uint64_t			peltv_lo_cache[128];
	uint64_t			peltv_hi_cache[128];
	uint64_t			tve_lo_cache[128];
	uint64_t			tve_hi_cache[128];
	uint64_t			iod_cache[128];
	uint64_t			m32d_cache[128];
	uint64_t			m64b_cache[16];
	uint64_t			m64d_cache[128];
	bool				err_pending;
	struct p7ioc_err		err;
	struct p7ioc			*ioc;
	struct phb			phb;
};

static inline struct p7ioc_phb *phb_to_p7ioc_phb(struct phb *phb)
{
	return container_of(phb, struct p7ioc_phb, phb);
}

static inline bool p7ioc_phb_err_pending(struct p7ioc_phb *p)
{
	return p->err_pending;
}

static inline void p7ioc_phb_set_err_pending(struct p7ioc_phb *p, bool pending)
{
	if (!pending) {
		p->err.err_src   = P7IOC_ERR_SRC_NONE;
		p->err.err_class = P7IOC_ERR_CLASS_NONE;
		p->err.err_bit   = -1;
	}

	p->err_pending = pending;
}

/*
 * State structure for P7IOC IO HUB
 */
struct p7ioc {
	/* Device node */
	struct dt_node			*dt_node;

	/* MMIO regs */
	void				*regs;

	/* Main MMIO window from GX for registers & PCI IO space */
	uint64_t			mmio1_win_start;
	uint64_t			mmio1_win_size;

	/* Secondary MMIO window for PCI MMIO space */
	uint64_t			mmio2_win_start;
	uint64_t			mmio2_win_size;

	/* BUID base for the PHB. This does include the top bits
	 * (chip, GX bus ID, etc...). This is initialized from the
	 * SPIRA. It does not contain the offset 0x10 for RGC
	 * interrupts.
	 *
	 * The OPAL-defined "interrupt-base" property will contain
	 * the RGC BUID, not this base value, since this is the real
	 * starting point of interrupts for the IOC and we don't want
	 * to cover the BUID 0..f gap which is reserved for P7 on-chip
	 * interrupt sources.
	 */
	uint32_t			buid_base;
	uint32_t			rgc_buid;

	/* XIVT cache for RGC interrupts */
	uint64_t			xive_cache[16];
	bool				err_pending;
	struct p7ioc_err		err;

	/* PHB array & presence detect */
	struct p7ioc_phb		phbs[P7IOC_NUM_PHBS];
	uint8_t				phb_pdt;
	   
	struct io_hub			hub;
};

static inline struct p7ioc *iohub_to_p7ioc(struct io_hub *hub)
{
	return container_of(hub, struct p7ioc, hub);
}

static inline bool p7ioc_err_pending(struct p7ioc *ioc)
{
	return ioc->err_pending;
}

static inline void p7ioc_set_err_pending(struct p7ioc *ioc, bool pending)
{
	if (!pending) {
		ioc->err.err_src   = P7IOC_ERR_SRC_NONE;
		ioc->err.err_class = P7IOC_ERR_CLASS_NONE;
		ioc->err.err_bit   = -1;
	}

	ioc->err_pending = pending;
}

static inline bool p7ioc_phb_enabled(struct p7ioc *ioc, unsigned int phb)
{
	return !!(ioc->phb_pdt & (0x80 >> phb));
}

extern int64_t p7ioc_inits(struct p7ioc *ioc);

extern void p7ioc_phb_setup(struct p7ioc *ioc, uint8_t index);
extern int64_t p7ioc_phb_init(struct p7ioc_phb *p);

extern bool p7ioc_check_LEM(struct p7ioc *ioc, uint16_t *pci_error_type,
			    uint16_t *severity);
extern int64_t p7ioc_phb_get_xive(struct p7ioc_phb *p, uint32_t isn,
				  uint16_t *server, uint8_t *prio);
extern int64_t p7ioc_phb_set_xive(struct p7ioc_phb *p, uint32_t isn,
				  uint16_t server, uint8_t prio);
extern void p7ioc_reset(struct io_hub *hub);
extern void p7ioc_phb_reset(struct phb *phb);

#endif /* __P7IOC_H */
