/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#ifndef	_SYS_PCIEV_H
#define	_SYS_PCIEV_H

#ifdef	__cplusplus
extern "C" {
#endif

typedef struct pcie_eh_data {
	uint16_t minor_ver;	/* Minor data packet version, added data */
	uint16_t major_ver;	/* Major data packet version, struct change */
	uint16_t pci_err_status;	/* pci status register */
	uint16_t pci_bdg_sec_stat;	/* PCI secondary status reg */
	uint32_t pcix_status;		/* pcix status register */
	uint16_t pcix_bdg_sec_stat;	/* pcix bridge secondary status reg */
	uint32_t pcix_bdg_stat;		/* pcix bridge status reg */
	uint16_t pcix_ecc_control_0;	/* pcix ecc control status reg */
	uint16_t pcix_ecc_status_0;	/* pcix ecc control status reg */
	uint32_t pcix_ecc_fst_addr_0;	/* pcix ecc first address reg */
	uint32_t pcix_ecc_sec_addr_0;	/* pcix ecc second address reg */
	uint32_t pcix_ecc_attr_0;	/* pcix ecc attributes reg */
	uint16_t pcix_ecc_control_1;	/* pcix ecc control status reg */
	uint16_t pcix_ecc_status_1;	/* pcix ecc control status reg */
	uint32_t pcix_ecc_fst_addr_1;	/* pcix ecc first address reg */
	uint32_t pcix_ecc_sec_addr_1;	/* pcix ecc second address reg */
	uint32_t pcix_ecc_attr_1;	/* pcix ecc attributes reg */
	uint16_t pcie_err_status;	/* pcie device status register */
	uint32_t pcie_ue_status;	/* pcie ue error status reg */
	uint32_t pcie_ue_hdr[4];	/* pcie ue header log */
	uint32_t pcie_ce_status;	/* pcie ce error status reg */
	uint32_t pcie_sue_status;	/* pcie bridge secondary ue status */
	uint32_t pcie_sue_hdr[4];	/* pcie bridge secondary ue hdr log */
	uint16_t pcie_rp_ctl;		/* root port control register */
	uint32_t pcie_rp_err_status;	/* pcie root port error status reg */
	uint32_t pcie_rp_err_cmd;	/* pcie root port error cmd reg */
	uint16_t pcie_rp_ce_src_id;	/* pcie root port ce sourpe id */
	uint16_t pcie_rp_ue_src_id;	/* pcie root port ue sourpe id */
} pcie_eh_data_t;

typedef struct pcie_domains {
	uint_t domain_id;
	uint_t cached_count;	/* Reference Count of cached dom id list */
	uint_t faulty_count;	/* Reference Count of faulty dom id list */
	struct pcie_domains *cached_next; /* Next on cached dom id list */
	struct pcie_domains *faulty_prev; /* Prev on faulty dom id list */
	struct pcie_domains *faulty_next; /* Next on faulty dom id list */
} pcie_domains_t;

typedef struct pcie_req_id_list {
	pcie_req_id_t		bdf;
	struct pcie_req_id_list	*next;
} pcie_req_id_list_t;

typedef struct pcie_child_domains {
	pcie_domains_t *ids;
	pcie_req_id_list_t *bdfs;
} pcie_child_domains_t;

/*
 * IOV data structure:
 * This data strucutre is now statically allocated during bus_p
 * initializing time. Do we need to have this data structure for
 * non-root domains? If not, is there a way to differentiate root
 * domain and non-root domain so that we do the initialization for
 * root domain only?
 */
typedef struct pcie_domain {
	/*
	 * Bridges:
	 * Cache the domain/channel id and bdfs of all it's children.
	 *
	 * Leaves:
	 * Cache just the domain/channel id of self.
	 * Bridges will contain 0 <= N <= NumChild
	 *
	 * Note:
	 * there is no lock to protect the access to
	 * pcie_domains_t data struture. Currently we don't see
	 * the need for lock. But we need to pay attention if there
	 * might be issues when hotplug is enabled.
	 */
	union {
		pcie_child_domains_t ids;
		pcie_domains_t id;
	} domain;

	/*
	 * Reference count of the domain type for this device and it's children.
	 * For leaf devices, fmadom + nfma + root = 1
	 * For bridges, the sum of the counts = number of LEAF children.
	 *
	 * All devices start with a count of 1 for either nfmadom or rootdom.
	 */
	uint_t		fmadom_count;	/* FMA channel capable domain */
	uint_t		nfmadom_count;	/* Non-FMA channel domain */
	uint_t		rootdom_count;	/* Root domain */

	/* flag if the affected dev will cause guest domains to panic */
	boolean_t	nfma_panic;
} pcie_domain_t;

extern void pcie_domain_list_add(uint_t, pcie_domains_t **);
extern void pcie_domain_list_remove(uint_t, pcie_domains_t *);
extern void pcie_save_domain_id(pcie_domains_t *);
extern void pcie_init_dom(dev_info_t *);
extern void pcie_fini_dom(dev_info_t *);

#define	PCIE_ASSIGNED_TO_FMA_DOM(bus_p)	\
	(!PCIE_IS_BDG(bus_p) && PCIE_BUS2DOM(bus_p)->fmadom_count > 0)
#define	PCIE_ASSIGNED_TO_NFMA_DOM(bus_p)	\
	(!PCIE_IS_BDG(bus_p) && PCIE_BUS2DOM(bus_p)->nfmadom_count > 0)
#define	PCIE_ASSIGNED_TO_ROOT_DOM(bus_p)			\
	(PCIE_IS_BDG(bus_p) || PCIE_BUS2DOM(bus_p)->rootdom_count > 0)
#define	PCIE_BDG_HAS_CHILDREN_FMA_DOM(bus_p)			\
	(PCIE_IS_BDG(bus_p) && PCIE_BUS2DOM(bus_p)->fmadom_count > 0)
#define	PCIE_BDG_HAS_CHILDREN_NFMA_DOM(bus_p)			\
	(PCIE_IS_BDG(bus_p) && PCIE_BUS2DOM(bus_p)->nfmadom_count > 0)
#define	PCIE_BDG_HAS_CHILDREN_ROOT_DOM(bus_p)			\
	(PCIE_IS_BDG(bus_p) && PCIE_BUS2DOM(bus_p)->rootdom_count > 0)
#define	PCIE_IS_ASSIGNED(bus_p)	\
	(!PCIE_ASSIGNED_TO_ROOT_DOM(bus_p))
#define	PCIE_BDG_IS_UNASSIGNED(bus_p)	\
	(PCIE_IS_BDG(bus_p) &&		\
	(!PCIE_BDG_HAS_CHILDREN_NFMA_DOM(bus_p)) &&	\
	(!PCIE_BDG_HAS_CHILDREN_FMA_DOM(bus_p)))


#define	PCIE_IN_DOMAIN(bus_p, id) (pcie_in_domain((bus_p), (id)))

/* Following macros are only valid for leaf devices */
#define	PCIE_DOMAIN_ID_GET(bus_p) \
	((uint_t)(PCIE_IS_ASSIGNED(bus_p)			\
	    ? PCIE_BUS2DOM(bus_p)->domain.id.domain_id : 0))
#define	PCIE_DOMAIN_ID_SET(bus_p, new_id) \
	if (!PCIE_IS_BDG(bus_p)) \
		PCIE_BUS2DOM(bus_p)->domain.id.domain_id = (uint_t)(new_id)
#define	PCIE_DOMAIN_ID_INCR_REF_COUNT(bus_p)	\
	if (!PCIE_IS_BDG(bus_p))	\
		PCIE_BUS2DOM(bus_p)->domain.id.cached_count = 1;
#define	PCIE_DOMAIN_ID_DECR_REF_COUNT(bus_p)	\
	if (!PCIE_IS_BDG(bus_p))	\
		PCIE_BUS2DOM(bus_p)->domain.id.cached_count = 0;

/* Following macros are only valid for bridges */
#define	PCIE_DOMAIN_LIST_GET(bus_p) \
	((pcie_domains_t *)(PCIE_IS_BDG(bus_p) ?	\
	    PCIE_BUS2DOM(bus_p)->domain.ids.ids : NULL))
#define	PCIE_DOMAIN_LIST_ADD(bus_p, domain_id) \
	if (PCIE_IS_BDG(bus_p)) \
	    pcie_domain_list_add(domain_id, \
		&PCIE_BUS2DOM(bus_p)->domain.ids.ids)
#define	PCIE_DOMAIN_LIST_REMOVE(bus_p, domain_id) \
	if (PCIE_IS_BDG(bus_p)) \
	    pcie_domain_list_remove(domain_id, \
		PCIE_BUS2DOM(bus_p)->domain.ids.ids)

#define	PCIE_BDF_LIST_GET(bus_p) \
	((pcie_req_id_list_t *)(PCIE_IS_BDG(bus_p) ? \
	    PCIE_BUS2DOM(bus_p)->domain.ids.bdfs : NULL))
#define	PCIE_BDF_LIST_ADD(bus_p, bdf) \
	if (PCIE_IS_BDG(bus_p)) \
		pcie_bdf_list_add(bdf, &PCIE_BUS2DOM(bus_p)->domain.ids.bdfs)
#define	PCIE_BDF_LIST_REMOVE(bus_p, bdf) \
	if (PCIE_IS_BDG(bus_p)) \
		pcie_bdf_list_remove(bdf, &PCIE_BUS2DOM(bus_p)->domain.ids.bdfs)

#ifdef	__cplusplus
}
#endif

#endif	/* _SYS_PCIEV_H */