/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright 2022 Oxide Computer Company
 */

#ifndef _SYS_PLAT_PCI_PRD_H
#define	_SYS_PLAT_PCI_PRD_H

/*
 * PCI Platform Resource Discovery (PRD)
 *
 * This file forms the platform-specific interfaces that a given platform must
 * implement to support the discovery of PCI resources. In particular:
 *
 *  o Any root complexes that do not show up through the use of normal scanning
 *  o Available resources per root-port including:
 *	+ I/O ports
 *	+ Prefetchable Memory
 *	+ Normal Memory
 *	+ PCI buses
 *  o The naming of slots (the platform uses the PCIe default)
 *
 * These interfaces are all expected to be implemented by a platform's 'pci_prd'
 * module. This is left as a module and not a part of say, unix, so that it can
 * in turn depend on other modules that a platform might require, such as ACPI.
 *
 * In general, unless otherwise indicated, these interfaces will always be
 * called from kernel context, typically during boot. The interfaces will only
 * be called from a single thread at this time and any locking is managed at a
 * layer outside of the pci_prd interfaces. If the subsystem is using some other
 * interfaces that may be used by multiple consumers and needs locking (e.g.
 * ACPI), then that still must be considered in the design and implementation.
 */

#include <sys/types.h>
#include <sys/memlist.h>
#include <sys/sunddi.h>

#ifdef __cplusplus
extern "C" {
#endif

/*
 * Resource types that can be asked after.
 */
typedef enum pci_prd_rsrc {
	PCI_PRD_R_IO,
	PCI_PRD_R_MMIO,
	PCI_PRD_R_PREFETCH,
	PCI_PRD_R_BUS
} pci_prd_rsrc_t;

typedef struct pci_prd_upcalls {
	/*
	 * Return a dev_info_t, if one exists, for this PCI bus.
	 */
	dev_info_t *(*pru_bus2dip_f)(uint32_t);
} pci_prd_upcalls_t;

/*
 * Initialization and teardown functions that will be used by the PCI
 * enumeration code when it attaches and detaches. If all work is done before
 * these come up, there is nothing to do; however, after a call to the _init()
 * function, it is expected that the platform module will be ready to respond to
 * all function calls.
 *
 * Note that the _fini function may never be called as on a typical system, as
 * any PCI(e) devices with attached drivers will result in the PRD consumer
 * remaining loaded.
 */
extern int pci_prd_init(pci_prd_upcalls_t *);
extern void pci_prd_fini(void);

/*
 * Return the maximum PCI bus on this platform that should be searched. This
 * number is the last bus number that should be scanned. e.g. a value of 0x10
 * indicates that we will search buses [0, 0x10]. In general, it is expected
 * that platforms will just return 0xff (PCI_MAX_BUS_NUM - 1) unless for some
 * reason it has other knowledge here.
 */
extern uint32_t pci_prd_max_bus(void);

/*
 * Look up a set of resources that should be assigned to the PCI bus. In
 * general, it is expected that these are only the buses that are assigned to
 * root complexes.
 */
extern struct memlist *pci_prd_find_resource(uint32_t, pci_prd_rsrc_t);

/*
 * Originally when only using BIOS-derived (pre-ACPI) sources on i86pc, the
 * ability to utilize data about multiple buses was considered suspect. As such,
 * this exists as a way to indicate that resources on each root complex are
 * actually valid.
 */
extern boolean_t pci_prd_multi_root_ok(void);

/*
 * This is used to allow the PCI enumeration code to ask the platform about any
 * PCI root complexes that it might know about which might not be discovered
 * through the normal scanning process. One callback will be emitted for each
 * PCI bus via a call to the callback function. The return value of the callback
 * function determines whether we should continue iterating (B_TRUE) or
 * terminate (B_FALSE).
 */
typedef boolean_t (*pci_prd_root_complex_f)(uint32_t, void *);
extern void pci_prd_root_complex_iter(pci_prd_root_complex_f, void *);

/*
 * Give the chance for a platform file to go through and use knowledge that it
 * has (such as the traditional BIOS PCI IRQ routing table) to name the PCI(e)
 * slot.
 */
extern void pci_prd_slot_name(uint32_t, dev_info_t *);

#ifdef __cplusplus
}
#endif

#endif /* _SYS_PLAT_PCI_PRD_H */