xref: /onnv-gate/usr/src/uts/intel/io/hotplug/pcicfg/pcicfg.c (revision 12622:e018bcda4475)
13470Sanish /*
23470Sanish  * CDDL HEADER START
33470Sanish  *
43470Sanish  * The contents of this file are subject to the terms of the
53470Sanish  * Common Development and Distribution License (the "License").
63470Sanish  * You may not use this file except in compliance with the License.
73470Sanish  *
83470Sanish  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93470Sanish  * or http://www.opensolaris.org/os/licensing.
103470Sanish  * See the License for the specific language governing permissions
113470Sanish  * and limitations under the License.
123470Sanish  *
133470Sanish  * When distributing Covered Code, include this CDDL HEADER in each
143470Sanish  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153470Sanish  * If applicable, add the following below this CDDL HEADER, with the
163470Sanish  * fields enclosed by brackets "[]" replaced with your own identifying
173470Sanish  * information: Portions Copyright [yyyy] [name of copyright owner]
183470Sanish  *
193470Sanish  * CDDL HEADER END
203470Sanish  */
213470Sanish /*
22*12622SAlan.Adamson@Sun.COM  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
233470Sanish  */
243470Sanish 
253470Sanish /*
263470Sanish  *     PCI configurator (pcicfg)
273470Sanish  */
283470Sanish 
293470Sanish #include <sys/sysmacros.h>
303470Sanish #include <sys/conf.h>
313470Sanish #include <sys/kmem.h>
323470Sanish #include <sys/debug.h>
333470Sanish #include <sys/modctl.h>
343470Sanish #include <sys/autoconf.h>
353470Sanish #include <sys/hwconf.h>
363470Sanish #include <sys/pcie.h>
3710923SEvan.Yan@Sun.COM #include <sys/pcie_impl.h>
387987SErwin.Tsaur@Sun.COM #include <sys/pci_cap.h>
393470Sanish #include <sys/ddi.h>
403470Sanish #include <sys/sunndi.h>
413470Sanish #include <sys/hotplug/pci/pcicfg.h>
423470Sanish #include <sys/ndi_impldefs.h>
4311245SZhijun.Fu@Sun.COM #include <sys/pci_cfgacc.h>
4411245SZhijun.Fu@Sun.COM #include <sys/pcie_impl.h>
453470Sanish 
463470Sanish /*
473470Sanish  * ************************************************************************
483470Sanish  * *** Implementation specific local data structures/definitions.	***
493470Sanish  * ************************************************************************
503470Sanish  */
513470Sanish 
523470Sanish static	int	pcicfg_start_devno = 0;	/* for Debug only */
533470Sanish 
5410923SEvan.Yan@Sun.COM #define	PCICFG_MAX_ARI_FUNCTION 256
5510923SEvan.Yan@Sun.COM 
563470Sanish #define	PCICFG_NODEVICE 42
573470Sanish #define	PCICFG_NOMEMORY 43
583470Sanish #define	PCICFG_NOMULTI	44
595149Sjveta #define	PCICFG_NORESRC	45
603470Sanish 
613470Sanish #define	PCICFG_HIADDR(n) ((uint32_t)(((uint64_t)(n) & \
623470Sanish 	0xFFFFFFFF00000000ULL)>> 32))
633470Sanish #define	PCICFG_LOADDR(n) ((uint32_t)((uint64_t)(n) & 0x00000000FFFFFFFF))
643470Sanish #define	PCICFG_LADDR(lo, hi)	(((uint64_t)(hi) << 32) | (uint32_t)(lo))
653470Sanish 
663470Sanish #define	PCICFG_HIWORD(n) ((uint16_t)(((uint32_t)(n) & 0xFFFF0000)>> 16))
673470Sanish #define	PCICFG_LOWORD(n) ((uint16_t)((uint32_t)(n) & 0x0000FFFF))
683470Sanish #define	PCICFG_HIBYTE(n) ((uint8_t)(((uint16_t)(n) & 0xFF00)>> 8))
693470Sanish #define	PCICFG_LOBYTE(n) ((uint8_t)((uint16_t)(n) & 0x00FF))
703470Sanish 
713470Sanish #define	PCICFG_ROUND_UP(addr, gran) ((uintptr_t)((gran+addr-1)&(~(gran-1))))
723470Sanish #define	PCICFG_ROUND_DOWN(addr, gran) ((uintptr_t)((addr) & ~(gran-1)))
733470Sanish 
743470Sanish #define	PCICFG_MEMGRAN 0x100000
753470Sanish #define	PCICFG_IOGRAN 0x1000
763470Sanish #define	PCICFG_4GIG_LIMIT 0xFFFFFFFFUL
773470Sanish 
783470Sanish #define	PCICFG_MEM_MULT 4
793470Sanish #define	PCICFG_IO_MULT 4
803470Sanish #define	PCICFG_RANGE_LEN 3 /* Number of range entries */
813470Sanish 
823470Sanish static int pcicfg_slot_busnums = 8;
833470Sanish static int pcicfg_slot_memsize = 32 * PCICFG_MEMGRAN; /* 32MB per slot */
843470Sanish static int pcicfg_slot_pf_memsize = 32 * PCICFG_MEMGRAN; /* 32MB per slot */
853470Sanish static int pcicfg_slot_iosize = 64 * PCICFG_IOGRAN; /* 64K per slot */
863470Sanish static int pcicfg_sec_reset_delay = 3000000;
873470Sanish static int pcicfg_do_legacy_props = 1;	/* create legacy compatible prop */
883470Sanish 
893470Sanish typedef struct hole hole_t;
903470Sanish 
913470Sanish struct hole {
923470Sanish 	uint64_t	start;
933470Sanish 	uint64_t	len;
943470Sanish 	hole_t		*next;
953470Sanish };
963470Sanish 
973470Sanish typedef struct pcicfg_phdl pcicfg_phdl_t;
983470Sanish 
993470Sanish struct pcicfg_phdl {
1003470Sanish 
1013470Sanish 	dev_info_t	*dip;		/* Associated with the bridge */
1023470Sanish 	dev_info_t	*top_dip;	/* top node of the attach point */
1033470Sanish 	pcicfg_phdl_t	*next;
1043470Sanish 
1053470Sanish 	/* non-prefetchable memory space */
1063470Sanish 	uint64_t	memory_base;	/* Memory base for this attach point */
1073470Sanish 	uint64_t	memory_last;
1083470Sanish 	uint64_t	memory_len;
1093470Sanish 
1103470Sanish 	/* prefetchable memory space */
11110923SEvan.Yan@Sun.COM 	uint64_t	pf_memory_base;	/* PF Memory base for this Connection */
1123470Sanish 	uint64_t	pf_memory_last;
1133470Sanish 	uint64_t	pf_memory_len;
1143470Sanish 
1153470Sanish 	/* io space */
1163470Sanish 	uint32_t	io_base;	/* I/O base for this attach point */
1173470Sanish 	uint32_t	io_last;
1183470Sanish 	uint32_t	io_len;
1193470Sanish 
1203470Sanish 	int		error;
1213470Sanish 	uint_t		highest_bus;	/* Highest bus seen on the probe */
1223470Sanish 
1233470Sanish 	hole_t		mem_hole;	/* Memory hole linked list. */
1243470Sanish 	hole_t		pf_mem_hole;	/* PF Memory hole linked list. */
1253470Sanish 	hole_t		io_hole;	/* IO hole linked list */
1263470Sanish 
1273470Sanish 	ndi_ra_request_t mem_req;	/* allocator request for memory */
1283470Sanish 	ndi_ra_request_t pf_mem_req;	/* allocator request for PF memory */
1293470Sanish 	ndi_ra_request_t io_req;	/* allocator request for I/O */
1303470Sanish };
1313470Sanish 
1323470Sanish struct pcicfg_standard_prop_entry {
1333470Sanish     uchar_t *name;
1343470Sanish     uint_t  config_offset;
1353470Sanish     uint_t  size;
1363470Sanish };
1373470Sanish 
1383470Sanish 
1393470Sanish struct pcicfg_name_entry {
1403470Sanish     uint32_t class_code;
1413470Sanish     char  *name;
1423470Sanish };
1433470Sanish 
1443470Sanish struct pcicfg_find_ctrl {
1453470Sanish 	uint_t		device;
1463470Sanish 	uint_t		function;
1473470Sanish 	dev_info_t	*dip;
1483470Sanish };
1493470Sanish 
1503470Sanish /*
1513470Sanish  * List of Indirect Config Map Devices. At least the intent of the
1523470Sanish  * design is to look for a device in this list during the configure
1533470Sanish  * operation, and if the device is listed here, then it is a nontransparent
1543470Sanish  * bridge, hence load the driver and avail the config map services from
1553470Sanish  * the driver. Class and Subclass should be as defined in the PCI specs
1563470Sanish  * ie. class is 0x6, and subclass is 0x9.
1573470Sanish  */
1583470Sanish static struct {
1593470Sanish 	uint8_t		mem_range_bar_offset;
1603470Sanish 	uint8_t		io_range_bar_offset;
1613470Sanish 	uint8_t		prefetch_mem_range_bar_offset;
1623470Sanish } pcicfg_indirect_map_devs[] = {
1633470Sanish 	PCI_CONF_BASE3, PCI_CONF_BASE2, PCI_CONF_BASE3,
1643470Sanish 	0,	0,	0,
1653470Sanish };
1663470Sanish 
1673470Sanish #define	PCICFG_MAKE_REG_HIGH(busnum, devnum, funcnum, register)\
1683470Sanish 	(\
1693470Sanish 	((ulong_t)(busnum & 0xff) << 16)    |\
1703470Sanish 	((ulong_t)(devnum & 0x1f) << 11)    |\
1713470Sanish 	((ulong_t)(funcnum & 0x7) <<  8)    |\
1723470Sanish 	((ulong_t)(register & 0x3f)))
1733470Sanish 
1743470Sanish /*
1753470Sanish  * debug macros:
1763470Sanish  */
1773470Sanish #if	defined(DEBUG)
1783470Sanish extern void prom_printf(const char *, ...);
1793470Sanish 
1803470Sanish /*
1813470Sanish  * Following values are defined for this debug flag.
1823470Sanish  *
1833470Sanish  * 1 = dump configuration header only.
1843470Sanish  * 2 = dump generic debug data only (no config header dumped)
1853470Sanish  * 3 = dump everything (both 1 and 2)
1863470Sanish  */
1873470Sanish int pcicfg_debug = 0;
1883470Sanish 
1893470Sanish static void debug(char *, uintptr_t, uintptr_t,
1903470Sanish 	uintptr_t, uintptr_t, uintptr_t);
1913470Sanish 
1923470Sanish #define	DEBUG0(fmt)\
1933470Sanish 	debug(fmt, 0, 0, 0, 0, 0);
1943470Sanish #define	DEBUG1(fmt, a1)\
1953470Sanish 	debug(fmt, (uintptr_t)(a1), 0, 0, 0, 0);
1963470Sanish #define	DEBUG2(fmt, a1, a2)\
1973470Sanish 	debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), 0, 0, 0);
1983470Sanish #define	DEBUG3(fmt, a1, a2, a3)\
1993470Sanish 	debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2),\
2003470Sanish 		(uintptr_t)(a3), 0, 0);
2013470Sanish #define	DEBUG4(fmt, a1, a2, a3, a4)\
2023470Sanish 	debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2),\
2033470Sanish 		(uintptr_t)(a3), (uintptr_t)(a4), 0);
2043470Sanish #define	DEBUG5(fmt, a1, a2, a3, a4, a5)\
2053470Sanish 	debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2),\
2063470Sanish 		(uintptr_t)(a3), (uintptr_t)(a4), (uintptr_t)(a5));
2073470Sanish #else
2083470Sanish #define	DEBUG0(fmt)
2093470Sanish #define	DEBUG1(fmt, a1)
2103470Sanish #define	DEBUG2(fmt, a1, a2)
2113470Sanish #define	DEBUG3(fmt, a1, a2, a3)
2123470Sanish #define	DEBUG4(fmt, a1, a2, a3, a4)
2133470Sanish #define	DEBUG5(fmt, a1, a2, a3, a4, a5)
2143470Sanish #endif
2153470Sanish 
2163470Sanish /*
2173470Sanish  * forward declarations for routines defined in this module (called here)
2183470Sanish  */
2193470Sanish 
2203470Sanish static int pcicfg_add_config_reg(dev_info_t *,
22110923SEvan.Yan@Sun.COM     uint_t, uint_t, uint_t);
2223470Sanish static int pcicfg_probe_children(dev_info_t *, uint_t, uint_t, uint_t,
22311245SZhijun.Fu@Sun.COM     uint_t *, pcicfg_flags_t, boolean_t);
2243470Sanish static int pcicfg_match_dev(dev_info_t *, void *);
2253470Sanish static dev_info_t *pcicfg_devi_find(dev_info_t *, uint_t, uint_t);
2263470Sanish static pcicfg_phdl_t *pcicfg_find_phdl(dev_info_t *);
2273470Sanish static pcicfg_phdl_t *pcicfg_create_phdl(dev_info_t *);
2283470Sanish static int pcicfg_destroy_phdl(dev_info_t *);
2293470Sanish static int pcicfg_sum_resources(dev_info_t *, void *);
2303470Sanish static int pcicfg_device_assign(dev_info_t *);
2313470Sanish static int pcicfg_bridge_assign(dev_info_t *, void *);
23210923SEvan.Yan@Sun.COM static int pcicfg_device_assign_readonly(dev_info_t *);
23310923SEvan.Yan@Sun.COM static int pcicfg_free_resources(dev_info_t *, pcicfg_flags_t);
2343470Sanish static void pcicfg_setup_bridge(pcicfg_phdl_t *, ddi_acc_handle_t);
2353470Sanish static void pcicfg_update_bridge(pcicfg_phdl_t *, ddi_acc_handle_t);
2363470Sanish static int pcicfg_update_assigned_prop(dev_info_t *, pci_regspec_t *);
2373470Sanish static void pcicfg_device_on(ddi_acc_handle_t);
2383470Sanish static void pcicfg_device_off(ddi_acc_handle_t);
2393470Sanish static int pcicfg_set_busnode_props(dev_info_t *, uint8_t);
2403470Sanish static int pcicfg_free_bridge_resources(dev_info_t *);
2413470Sanish static int pcicfg_free_device_resources(dev_info_t *);
24211245SZhijun.Fu@Sun.COM static int pcicfg_teardown_device(dev_info_t *, pcicfg_flags_t, boolean_t);
2433470Sanish static void pcicfg_reparent_node(dev_info_t *, dev_info_t *);
2443470Sanish static int pcicfg_config_setup(dev_info_t *, ddi_acc_handle_t *);
2453470Sanish static void pcicfg_config_teardown(ddi_acc_handle_t *);
2463470Sanish static void pcicfg_get_mem(pcicfg_phdl_t *, uint32_t, uint64_t *);
2473470Sanish static void pcicfg_get_pf_mem(pcicfg_phdl_t *, uint32_t, uint64_t *);
2483470Sanish static void pcicfg_get_io(pcicfg_phdl_t *, uint32_t, uint32_t *);
2493470Sanish static int pcicfg_update_ranges_prop(dev_info_t *, ppb_ranges_t *);
2503470Sanish static int pcicfg_configure_ntbridge(dev_info_t *, uint_t, uint_t);
2513470Sanish static uint_t pcicfg_ntbridge_child(dev_info_t *);
2523470Sanish static uint_t pcicfg_get_ntbridge_child_range(dev_info_t *, uint64_t *,
25310923SEvan.Yan@Sun.COM     uint64_t *, uint_t);
2543470Sanish static int pcicfg_is_ntbridge(dev_info_t *);
2553470Sanish static int pcicfg_ntbridge_allocate_resources(dev_info_t *);
2563470Sanish static int pcicfg_ntbridge_configure_done(dev_info_t *);
2573470Sanish static int pcicfg_ntbridge_program_child(dev_info_t *);
2583470Sanish static uint_t pcicfg_ntbridge_unconfigure(dev_info_t *);
2593470Sanish static int pcicfg_ntbridge_unconfigure_child(dev_info_t *, uint_t);
2603470Sanish static void pcicfg_free_hole(hole_t *);
2613470Sanish static uint64_t pcicfg_alloc_hole(hole_t *, uint64_t *, uint32_t);
2623470Sanish static int pcicfg_device_type(dev_info_t *, ddi_acc_handle_t *);
2633470Sanish static void pcicfg_update_phdl(dev_info_t *, uint8_t, uint8_t);
2643470Sanish static int pcicfg_get_cap(ddi_acc_handle_t, uint8_t);
2653470Sanish static uint8_t pcicfg_get_nslots(dev_info_t *, ddi_acc_handle_t);
2663470Sanish static int pcicfg_pcie_dev(dev_info_t *, ddi_acc_handle_t);
2673470Sanish static int pcicfg_pcie_device_type(dev_info_t *, ddi_acc_handle_t);
2683470Sanish static int pcicfg_pcie_port_type(dev_info_t *, ddi_acc_handle_t);
2693470Sanish static int pcicfg_probe_bridge(dev_info_t *, ddi_acc_handle_t, uint_t,
27011245SZhijun.Fu@Sun.COM 	uint_t *, boolean_t);
2713470Sanish static int pcicfg_find_resource_end(dev_info_t *, void *);
27211245SZhijun.Fu@Sun.COM static boolean_t is_pcie_fabric(dev_info_t *);
2733470Sanish 
27410923SEvan.Yan@Sun.COM static int pcicfg_populate_reg_props(dev_info_t *, ddi_acc_handle_t);
27510923SEvan.Yan@Sun.COM static int pcicfg_populate_props_from_bar(dev_info_t *, ddi_acc_handle_t);
27610923SEvan.Yan@Sun.COM static int pcicfg_update_assigned_prop_value(dev_info_t *, uint32_t,
27710923SEvan.Yan@Sun.COM     uint32_t, uint32_t, uint_t);
27810923SEvan.Yan@Sun.COM static int pcicfg_ari_configure(dev_info_t *);
27910923SEvan.Yan@Sun.COM 
2803470Sanish #ifdef DEBUG
2813470Sanish static void pcicfg_dump_common_config(ddi_acc_handle_t config_handle);
2823470Sanish static void pcicfg_dump_device_config(ddi_acc_handle_t);
2833470Sanish static void pcicfg_dump_bridge_config(ddi_acc_handle_t config_handle);
2843470Sanish static uint64_t pcicfg_unused_space(hole_t *, uint32_t *);
2853470Sanish 
2863470Sanish #define	PCICFG_DUMP_COMMON_CONFIG(hdl) (void)pcicfg_dump_common_config(hdl)
2873470Sanish #define	PCICFG_DUMP_DEVICE_CONFIG(hdl) (void)pcicfg_dump_device_config(hdl)
2883470Sanish #define	PCICFG_DUMP_BRIDGE_CONFIG(hdl) (void)pcicfg_dump_bridge_config(hdl)
2893470Sanish #else
2903470Sanish #define	PCICFG_DUMP_COMMON_CONFIG(handle)
2913470Sanish #define	PCICFG_DUMP_DEVICE_CONFIG(handle)
2923470Sanish #define	PCICFG_DUMP_BRIDGE_CONFIG(handle)
2933470Sanish #endif
2943470Sanish 
2953470Sanish static kmutex_t pcicfg_list_mutex; /* Protects the probe handle list */
2963470Sanish static pcicfg_phdl_t *pcicfg_phdl_list = NULL;
2973470Sanish 
2983470Sanish #ifndef _DONT_USE_1275_GENERIC_NAMES
2993470Sanish /*
3003470Sanish  * Class code table
3013470Sanish  */
3023470Sanish static struct pcicfg_name_entry pcicfg_class_lookup [] = {
3033470Sanish 
3043470Sanish 	{ 0x001, "display" },
3053470Sanish 	{ 0x100, "scsi" },
3063470Sanish 	{ 0x101, "ide" },
3073470Sanish 	{ 0x102, "fdc" },
3083470Sanish 	{ 0x103, "ipi" },
3093470Sanish 	{ 0x104, "raid" },
3103470Sanish 	{ 0x105, "ata" },
3113470Sanish 	{ 0x106, "sata" },
3123470Sanish 	{ 0x200, "ethernet" },
3133470Sanish 	{ 0x201, "token-ring" },
3143470Sanish 	{ 0x202, "fddi" },
3153470Sanish 	{ 0x203, "atm" },
3163470Sanish 	{ 0x204, "isdn" },
3173470Sanish 	{ 0x206, "mcd" },
3183470Sanish 	{ 0x300, "display" },
3193470Sanish 	{ 0x400, "video" },
3203470Sanish 	{ 0x401, "sound" },
3213470Sanish 	{ 0x500, "memory" },
3223470Sanish 	{ 0x501, "flash" },
3233470Sanish 	{ 0x600, "host" },
3243470Sanish 	{ 0x601, "isa" },
3253470Sanish 	{ 0x602, "eisa" },
3263470Sanish 	{ 0x603, "mca" },
3273470Sanish 	{ 0x604, "pci" },
3283470Sanish 	{ 0x605, "pcmcia" },
3293470Sanish 	{ 0x606, "nubus" },
3303470Sanish 	{ 0x607, "cardbus" },
3313470Sanish 	{ 0x609, "pci" },
3323470Sanish 	{ 0x60a, "ib-pci" },
3333470Sanish 	{ 0x700, "serial" },
3343470Sanish 	{ 0x701, "parallel" },
3353470Sanish 	{ 0x800, "interrupt-controller" },
3363470Sanish 	{ 0x801, "dma-controller" },
3373470Sanish 	{ 0x802, "timer" },
3383470Sanish 	{ 0x803, "rtc" },
3393470Sanish 	{ 0x900, "keyboard" },
3403470Sanish 	{ 0x901, "pen" },
3413470Sanish 	{ 0x902, "mouse" },
3423470Sanish 	{ 0xa00, "dock" },
3433470Sanish 	{ 0xb00, "cpu" },
3443470Sanish 	{ 0xb01, "cpu" },
3453470Sanish 	{ 0xb02, "cpu" },
3463470Sanish 	{ 0xb10, "cpu" },
3473470Sanish 	{ 0xb20, "cpu" },
3483470Sanish 	{ 0xb30, "cpu" },
3493470Sanish 	{ 0xb40, "coproc" },
3503470Sanish 	{ 0xc00, "firewire" },
3513470Sanish 	{ 0xc01, "access-bus" },
3523470Sanish 	{ 0xc02, "ssa" },
3533470Sanish 	{ 0xc03, "usb" },
3543470Sanish 	{ 0xc04, "fibre-channel" },
3553470Sanish 	{ 0xc05, "smbus" },
3563470Sanish 	{ 0xc06, "ib" },
3573470Sanish 	{ 0xd00, "irda" },
3583470Sanish 	{ 0xd01, "ir" },
3593470Sanish 	{ 0xd10, "rf" },
3603470Sanish 	{ 0xd11, "btooth" },
3613470Sanish 	{ 0xd12, "brdband" },
3623470Sanish 	{ 0xd20, "802.11a" },
3633470Sanish 	{ 0xd21, "802.11b" },
3643470Sanish 	{ 0xe00, "i2o" },
3653470Sanish 	{ 0xf01, "tv" },
3663470Sanish 	{ 0xf02, "audio" },
3673470Sanish 	{ 0xf03, "voice" },
3683470Sanish 	{ 0xf04, "data" },
3693470Sanish 	{ 0, 0 }
3703470Sanish };
3713470Sanish #endif /* _DONT_USE_1275_GENERIC_NAMES */
3723470Sanish 
3733470Sanish /*
3743470Sanish  * Module control operations
3753470Sanish  */
3763470Sanish 
3773470Sanish extern struct mod_ops mod_miscops;
3783470Sanish 
3793470Sanish static struct modlmisc modlmisc = {
3803470Sanish 	&mod_miscops, /* Type of module */
3817542SRichard.Bean@Sun.COM 	"PCI configurator"
3823470Sanish };
3833470Sanish 
3843470Sanish static struct modlinkage modlinkage = {
3853470Sanish 	MODREV_1, (void *)&modlmisc, NULL
3863470Sanish };
3873470Sanish 
3883470Sanish 
3893470Sanish #ifdef DEBUG
3903470Sanish 
3913470Sanish static void
pcicfg_dump_common_config(ddi_acc_handle_t config_handle)3923470Sanish pcicfg_dump_common_config(ddi_acc_handle_t config_handle)
3933470Sanish {
3943470Sanish 	if ((pcicfg_debug & 1) == 0)
3953470Sanish 		return;
3963470Sanish 	prom_printf(" Vendor ID   = [0x%x]\n",
39710923SEvan.Yan@Sun.COM 	    pci_config_get16(config_handle, PCI_CONF_VENID));
3983470Sanish 	prom_printf(" Device ID   = [0x%x]\n",
39910923SEvan.Yan@Sun.COM 	    pci_config_get16(config_handle, PCI_CONF_DEVID));
4003470Sanish 	prom_printf(" Command REG = [0x%x]\n",
40110923SEvan.Yan@Sun.COM 	    pci_config_get16(config_handle, PCI_CONF_COMM));
4023470Sanish 	prom_printf(" Status  REG = [0x%x]\n",
40310923SEvan.Yan@Sun.COM 	    pci_config_get16(config_handle, PCI_CONF_STAT));
4043470Sanish 	prom_printf(" Revision ID = [0x%x]\n",
40510923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_CONF_REVID));
4063470Sanish 	prom_printf(" Prog Class  = [0x%x]\n",
40710923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_CONF_PROGCLASS));
4083470Sanish 	prom_printf(" Dev Class   = [0x%x]\n",
40910923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_CONF_SUBCLASS));
4103470Sanish 	prom_printf(" Base Class  = [0x%x]\n",
41110923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_CONF_BASCLASS));
4123470Sanish 	prom_printf(" Device ID   = [0x%x]\n",
41310923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_CONF_CACHE_LINESZ));
4143470Sanish 	prom_printf(" Header Type = [0x%x]\n",
41510923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_CONF_HEADER));
4163470Sanish 	prom_printf(" BIST        = [0x%x]\n",
41710923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_CONF_BIST));
4183470Sanish 	prom_printf(" BASE 0      = [0x%x]\n",
41910923SEvan.Yan@Sun.COM 	    pci_config_get32(config_handle, PCI_CONF_BASE0));
4203470Sanish 	prom_printf(" BASE 1      = [0x%x]\n",
42110923SEvan.Yan@Sun.COM 	    pci_config_get32(config_handle, PCI_CONF_BASE1));
4223470Sanish 
4233470Sanish }
4243470Sanish 
4253470Sanish static void
pcicfg_dump_device_config(ddi_acc_handle_t config_handle)4263470Sanish pcicfg_dump_device_config(ddi_acc_handle_t config_handle)
4273470Sanish {
4283470Sanish 	if ((pcicfg_debug & 1) == 0)
4293470Sanish 		return;
4303470Sanish 	pcicfg_dump_common_config(config_handle);
4313470Sanish 
4323470Sanish 	prom_printf(" BASE 2      = [0x%x]\n",
43310923SEvan.Yan@Sun.COM 	    pci_config_get32(config_handle, PCI_CONF_BASE2));
4343470Sanish 	prom_printf(" BASE 3      = [0x%x]\n",
43510923SEvan.Yan@Sun.COM 	    pci_config_get32(config_handle, PCI_CONF_BASE3));
4363470Sanish 	prom_printf(" BASE 4      = [0x%x]\n",
43710923SEvan.Yan@Sun.COM 	    pci_config_get32(config_handle, PCI_CONF_BASE4));
4383470Sanish 	prom_printf(" BASE 5      = [0x%x]\n",
43910923SEvan.Yan@Sun.COM 	    pci_config_get32(config_handle, PCI_CONF_BASE5));
4403470Sanish 	prom_printf(" Cardbus CIS = [0x%x]\n",
44110923SEvan.Yan@Sun.COM 	    pci_config_get32(config_handle, PCI_CONF_CIS));
4423470Sanish 	prom_printf(" Sub VID     = [0x%x]\n",
44310923SEvan.Yan@Sun.COM 	    pci_config_get16(config_handle, PCI_CONF_SUBVENID));
4443470Sanish 	prom_printf(" Sub SID     = [0x%x]\n",
44510923SEvan.Yan@Sun.COM 	    pci_config_get16(config_handle, PCI_CONF_SUBSYSID));
4463470Sanish 	prom_printf(" ROM         = [0x%x]\n",
44710923SEvan.Yan@Sun.COM 	    pci_config_get32(config_handle, PCI_CONF_ROM));
4483470Sanish 	prom_printf(" I Line      = [0x%x]\n",
44910923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_CONF_ILINE));
4503470Sanish 	prom_printf(" I Pin       = [0x%x]\n",
45110923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_CONF_IPIN));
4523470Sanish 	prom_printf(" Max Grant   = [0x%x]\n",
45310923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_CONF_MIN_G));
4543470Sanish 	prom_printf(" Max Latent  = [0x%x]\n",
45510923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_CONF_MAX_L));
4563470Sanish }
4573470Sanish 
4583470Sanish static void
pcicfg_dump_bridge_config(ddi_acc_handle_t config_handle)4593470Sanish pcicfg_dump_bridge_config(ddi_acc_handle_t config_handle)
4603470Sanish {
4613470Sanish 	if ((pcicfg_debug & 1) == 0)
4623470Sanish 		return;
4633470Sanish 	pcicfg_dump_common_config(config_handle);
4643470Sanish 
4653470Sanish 	prom_printf("........................................\n");
4663470Sanish 
4673470Sanish 	prom_printf(" Pri Bus     = [0x%x]\n",
46810923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_BCNF_PRIBUS));
4693470Sanish 	prom_printf(" Sec Bus     = [0x%x]\n",
47010923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_BCNF_SECBUS));
4713470Sanish 	prom_printf(" Sub Bus     = [0x%x]\n",
47210923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_BCNF_SUBBUS));
4733470Sanish 	prom_printf(" Latency     = [0x%x]\n",
47410923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_BCNF_LATENCY_TIMER));
4753470Sanish 	prom_printf(" I/O Base LO = [0x%x]\n",
47610923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_BCNF_IO_BASE_LOW));
4773470Sanish 	prom_printf(" I/O Lim LO  = [0x%x]\n",
47810923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_BCNF_IO_LIMIT_LOW));
4793470Sanish 	prom_printf(" Sec. Status = [0x%x]\n",
48010923SEvan.Yan@Sun.COM 	    pci_config_get16(config_handle, PCI_BCNF_SEC_STATUS));
4813470Sanish 	prom_printf(" Mem Base    = [0x%x]\n",
48210923SEvan.Yan@Sun.COM 	    pci_config_get16(config_handle, PCI_BCNF_MEM_BASE));
4833470Sanish 	prom_printf(" Mem Limit   = [0x%x]\n",
48410923SEvan.Yan@Sun.COM 	    pci_config_get16(config_handle, PCI_BCNF_MEM_LIMIT));
4853470Sanish 	prom_printf(" PF Mem Base = [0x%x]\n",
48610923SEvan.Yan@Sun.COM 	    pci_config_get16(config_handle, PCI_BCNF_PF_BASE_LOW));
4873470Sanish 	prom_printf(" PF Mem Lim  = [0x%x]\n",
48810923SEvan.Yan@Sun.COM 	    pci_config_get16(config_handle, PCI_BCNF_PF_LIMIT_LOW));
4893470Sanish 	prom_printf(" PF Base HI  = [0x%x]\n",
49010923SEvan.Yan@Sun.COM 	    pci_config_get32(config_handle, PCI_BCNF_PF_BASE_HIGH));
4913470Sanish 	prom_printf(" PF Lim  HI  = [0x%x]\n",
49210923SEvan.Yan@Sun.COM 	    pci_config_get32(config_handle, PCI_BCNF_PF_LIMIT_HIGH));
4933470Sanish 	prom_printf(" I/O Base HI = [0x%x]\n",
49410923SEvan.Yan@Sun.COM 	    pci_config_get16(config_handle, PCI_BCNF_IO_BASE_HI));
4953470Sanish 	prom_printf(" I/O Lim HI  = [0x%x]\n",
49610923SEvan.Yan@Sun.COM 	    pci_config_get16(config_handle, PCI_BCNF_IO_LIMIT_HI));
4973470Sanish 	prom_printf(" ROM addr    = [0x%x]\n",
49810923SEvan.Yan@Sun.COM 	    pci_config_get32(config_handle, PCI_BCNF_ROM));
4993470Sanish 	prom_printf(" Intr Line   = [0x%x]\n",
50010923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_BCNF_ILINE));
5013470Sanish 	prom_printf(" Intr Pin    = [0x%x]\n",
50210923SEvan.Yan@Sun.COM 	    pci_config_get8(config_handle, PCI_BCNF_IPIN));
5033470Sanish 	prom_printf(" Bridge Ctrl = [0x%x]\n",
50410923SEvan.Yan@Sun.COM 	    pci_config_get16(config_handle, PCI_BCNF_BCNTRL));
5053470Sanish }
5063470Sanish #endif
5073470Sanish 
5083470Sanish int
_init()5093470Sanish _init()
5103470Sanish {
5113470Sanish 	DEBUG0(" PCI configurator installed\n");
5123470Sanish 	mutex_init(&pcicfg_list_mutex, NULL, MUTEX_DRIVER, NULL);
5133470Sanish 	return (mod_install(&modlinkage));
5143470Sanish }
5153470Sanish 
5163470Sanish int
_fini(void)5173470Sanish _fini(void)
5183470Sanish {
5193470Sanish 	int error;
5203470Sanish 
5213470Sanish 	error = mod_remove(&modlinkage);
5223470Sanish 	if (error != 0) {
5233470Sanish 		return (error);
5243470Sanish 	}
5253470Sanish 	mutex_destroy(&pcicfg_list_mutex);
5263470Sanish 	return (0);
5273470Sanish }
5283470Sanish 
5293470Sanish int
_info(struct modinfo * modinfop)5303470Sanish _info(struct modinfo *modinfop)
5313470Sanish {
5323470Sanish 	return (mod_info(&modlinkage, modinfop));
5333470Sanish }
5343470Sanish 
5353470Sanish /*
5363470Sanish  * In the following functions ndi_devi_enter() without holding the
5373470Sanish  * parent dip is sufficient. This is because  pci dr is driven through
5383470Sanish  * opens on the nexus which is in the device tree path above the node
5393470Sanish  * being operated on, and implicitly held due to the open.
5403470Sanish  */
5413470Sanish 
5423470Sanish /*
5433470Sanish  * This entry point is called to configure a device (and
5443470Sanish  * all its children) on the given bus. It is called when
5453470Sanish  * a new device is added to the PCI domain.  This routine
5463470Sanish  * will create the device tree and program the devices
5473470Sanish  * registers.
5483470Sanish  */
5493470Sanish int
pcicfg_configure(dev_info_t * devi,uint_t device,uint_t function,pcicfg_flags_t flags)55010923SEvan.Yan@Sun.COM pcicfg_configure(dev_info_t *devi, uint_t device, uint_t function,
55110923SEvan.Yan@Sun.COM     pcicfg_flags_t flags)
5523470Sanish {
5533470Sanish 	uint_t bus;
5543470Sanish 	int len;
5553470Sanish 	int func;
5563470Sanish 	dev_info_t *attach_point;
5573470Sanish 	pci_bus_range_t pci_bus_range;
5583470Sanish 	int rv;
5593470Sanish 	int circ;
5603470Sanish 	uint_t highest_bus;
56110923SEvan.Yan@Sun.COM 	int ari_mode = B_FALSE;
56210923SEvan.Yan@Sun.COM 	int max_function = PCI_MAX_FUNCTIONS;
56310923SEvan.Yan@Sun.COM 	int trans_device;
56410923SEvan.Yan@Sun.COM 	dev_info_t *new_device;
56511245SZhijun.Fu@Sun.COM 	boolean_t is_pcie;
56610923SEvan.Yan@Sun.COM 
56710923SEvan.Yan@Sun.COM 	if (flags == PCICFG_FLAG_ENABLE_ARI)
56810923SEvan.Yan@Sun.COM 		return (pcicfg_ari_configure(devi));
5693470Sanish 
5703470Sanish 	/*
5713470Sanish 	 * Start probing at the device specified in "device" on the
5723470Sanish 	 * "bus" specified.
5733470Sanish 	 */
5743470Sanish 	len = sizeof (pci_bus_range_t);
5753470Sanish 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, devi, 0, "bus-range",
5763470Sanish 	    (caddr_t)&pci_bus_range, &len) != DDI_SUCCESS) {
5773470Sanish 		DEBUG0("no bus-range property\n");
5783470Sanish 		return (PCICFG_FAILURE);
5793470Sanish 	}
5803470Sanish 
5813470Sanish 	bus = pci_bus_range.lo; /* primary bus number of this bus node */
5823470Sanish 
5833470Sanish 	attach_point = devi;
5843470Sanish 
58511245SZhijun.Fu@Sun.COM 	is_pcie = is_pcie_fabric(devi);
58611245SZhijun.Fu@Sun.COM 
5873470Sanish 	ndi_devi_enter(devi, &circ);
58810923SEvan.Yan@Sun.COM 	for (func = 0; func < max_function; ) {
58910923SEvan.Yan@Sun.COM 
59010923SEvan.Yan@Sun.COM 		if ((function != PCICFG_ALL_FUNC) && (function != func))
59110923SEvan.Yan@Sun.COM 			goto next;
59210923SEvan.Yan@Sun.COM 
59310923SEvan.Yan@Sun.COM 		if (ari_mode)
59410923SEvan.Yan@Sun.COM 			trans_device = func >> 3;
59510923SEvan.Yan@Sun.COM 		else
59610923SEvan.Yan@Sun.COM 			trans_device = device;
5973470Sanish 
5983470Sanish 		switch (rv = pcicfg_probe_children(attach_point,
59911245SZhijun.Fu@Sun.COM 		    bus, trans_device, func & 7, &highest_bus,
60011245SZhijun.Fu@Sun.COM 		    flags, is_pcie)) {
6015149Sjveta 			case PCICFG_NORESRC:
6023470Sanish 			case PCICFG_FAILURE:
60310923SEvan.Yan@Sun.COM 				DEBUG2("configure failed: bus [0x%x] device "
60410923SEvan.Yan@Sun.COM 				    "[0x%x]\n", bus, trans_device);
6053470Sanish 				goto cleanup;
6063470Sanish 			case PCICFG_NODEVICE:
6073470Sanish 				DEBUG3("no device : bus "
60810923SEvan.Yan@Sun.COM 				    "[0x%x] slot [0x%x] func [0x%x]\n",
60910923SEvan.Yan@Sun.COM 				    bus, trans_device, func &7);
61010923SEvan.Yan@Sun.COM 
611*12622SAlan.Adamson@Sun.COM 				/*
612*12622SAlan.Adamson@Sun.COM 				 * When walking the list of ARI functions
613*12622SAlan.Adamson@Sun.COM 				 * we don't expect to see a non-present
614*12622SAlan.Adamson@Sun.COM 				 * function, so we will stop walking
615*12622SAlan.Adamson@Sun.COM 				 * the function list.
616*12622SAlan.Adamson@Sun.COM 				 */
617*12622SAlan.Adamson@Sun.COM 				if (ari_mode == B_TRUE)
618*12622SAlan.Adamson@Sun.COM 					break;
619*12622SAlan.Adamson@Sun.COM 
62010923SEvan.Yan@Sun.COM 				if (func)
62110923SEvan.Yan@Sun.COM 					goto next;
62210923SEvan.Yan@Sun.COM 				break;
6233470Sanish 			default:
6243470Sanish 				DEBUG3("configure: bus => [%d] "
62510923SEvan.Yan@Sun.COM 				    "slot => [%d] func => [%d]\n",
62610923SEvan.Yan@Sun.COM 				    bus, trans_device, func & 7);
6273470Sanish 			break;
6283470Sanish 		}
6293470Sanish 
6303470Sanish 		if (rv != PCICFG_SUCCESS)
6313470Sanish 			break;
6323470Sanish 
6333470Sanish 		if ((new_device = pcicfg_devi_find(attach_point,
63410923SEvan.Yan@Sun.COM 		    trans_device, func & 7)) == NULL) {
6353470Sanish 			DEBUG0("Did'nt find device node just created\n");
6363470Sanish 			goto cleanup;
6373470Sanish 		}
6383470Sanish 
6393470Sanish 		/*
6403470Sanish 		 * Up until now, we have detected a non transparent bridge
6413470Sanish 		 * (ntbridge) as a part of the generic probe code and
6423470Sanish 		 * configured only one configuration
6433470Sanish 		 * header which is the side facing the host bus.
6443470Sanish 		 * Now, configure the other side and create children.
6453470Sanish 		 *
6463470Sanish 		 * In order to make the process simpler, lets load the device
6473470Sanish 		 * driver for the non transparent bridge as this is a
6483470Sanish 		 * Solaris bundled driver, and use its configuration map
6493470Sanish 		 * services rather than programming it here.
6503470Sanish 		 * If the driver is not bundled into Solaris, it must be
6513470Sanish 		 * first loaded and configured before performing any
6523470Sanish 		 * hotplug operations.
6533470Sanish 		 *
6543470Sanish 		 * This not only makes the code here simpler but also more
6553470Sanish 		 * generic.
6563470Sanish 		 *
6573470Sanish 		 * So here we go.
6583470Sanish 		 */
6593470Sanish 
6603470Sanish 		/*
6613470Sanish 		 * check if this is a bridge in nontransparent mode
6623470Sanish 		 */
6633470Sanish 		if (pcicfg_is_ntbridge(new_device) != DDI_FAILURE) {
6643470Sanish 			DEBUG0("pcicfg: Found nontransparent bridge.\n");
6653470Sanish 
66610923SEvan.Yan@Sun.COM 			rv = pcicfg_configure_ntbridge(new_device, bus,
66710923SEvan.Yan@Sun.COM 			    trans_device);
6685149Sjveta 			if (rv != PCICFG_SUCCESS)
6693470Sanish 				goto cleanup;
6703470Sanish 		}
67110923SEvan.Yan@Sun.COM 
67210923SEvan.Yan@Sun.COM next:
67310923SEvan.Yan@Sun.COM 		/*
67410923SEvan.Yan@Sun.COM 		 * Determine if ARI Forwarding should be enabled.
67510923SEvan.Yan@Sun.COM 		 */
67610923SEvan.Yan@Sun.COM 		if (func == 0) {
67710923SEvan.Yan@Sun.COM 			if ((pcie_ari_supported(devi)
67810923SEvan.Yan@Sun.COM 			    == PCIE_ARI_FORW_SUPPORTED) &&
67910923SEvan.Yan@Sun.COM 			    (pcie_ari_device(new_device) == PCIE_ARI_DEVICE)) {
68010923SEvan.Yan@Sun.COM 				if (pcie_ari_enable(devi) == DDI_SUCCESS) {
68110923SEvan.Yan@Sun.COM 					(void) ddi_prop_create(DDI_DEV_T_NONE,
68210923SEvan.Yan@Sun.COM 					    devi,  DDI_PROP_CANSLEEP,
68310923SEvan.Yan@Sun.COM 					    "ari-enabled", NULL, 0);
68410923SEvan.Yan@Sun.COM 
68510923SEvan.Yan@Sun.COM 					ari_mode = B_TRUE;
68610923SEvan.Yan@Sun.COM 					max_function = PCICFG_MAX_ARI_FUNCTION;
68710923SEvan.Yan@Sun.COM 				}
68810923SEvan.Yan@Sun.COM 			}
68910923SEvan.Yan@Sun.COM 		}
69010923SEvan.Yan@Sun.COM 		if (ari_mode == B_TRUE) {
69110923SEvan.Yan@Sun.COM 			int next_function;
69210923SEvan.Yan@Sun.COM 
69310923SEvan.Yan@Sun.COM 			DEBUG0("Next Function - ARI Device\n");
69410923SEvan.Yan@Sun.COM 			if (pcie_ari_get_next_function(new_device,
69510923SEvan.Yan@Sun.COM 			    &next_function) != DDI_SUCCESS)
69610923SEvan.Yan@Sun.COM 				goto cleanup;
69710923SEvan.Yan@Sun.COM 
69810923SEvan.Yan@Sun.COM 			/*
69910923SEvan.Yan@Sun.COM 			 * Check if there are more fucntions to probe.
70010923SEvan.Yan@Sun.COM 			 */
70110923SEvan.Yan@Sun.COM 			if (next_function == 0) {
70210923SEvan.Yan@Sun.COM 				DEBUG0("Next Function - "
70310923SEvan.Yan@Sun.COM 				    "No more ARI Functions\n");
70410923SEvan.Yan@Sun.COM 				break;
70510923SEvan.Yan@Sun.COM 			}
70610923SEvan.Yan@Sun.COM 			func = next_function;
70710923SEvan.Yan@Sun.COM 		} else {
70810923SEvan.Yan@Sun.COM 			func++;
70910923SEvan.Yan@Sun.COM 		}
71010923SEvan.Yan@Sun.COM 		DEBUG1("Next Function - %x\n", func);
7113470Sanish 	}
7123470Sanish 
7133470Sanish 	ndi_devi_exit(devi, circ);
7143470Sanish 
7153470Sanish 	if (func == 0)
7163470Sanish 		return (PCICFG_FAILURE);	/* probe failed */
7173470Sanish 	else
7183470Sanish 		return (PCICFG_SUCCESS);
7193470Sanish 
7203470Sanish cleanup:
7213470Sanish 	/*
7223470Sanish 	 * Clean up a partially created "probe state" tree.
7233470Sanish 	 * There are no resources allocated to the in the
7243470Sanish 	 * probe state.
7253470Sanish 	 */
7263470Sanish 
7273470Sanish 	for (func = 0; func < PCI_MAX_FUNCTIONS; func++) {
72810923SEvan.Yan@Sun.COM 		if ((function != PCICFG_ALL_FUNC) && (function != func))
72910923SEvan.Yan@Sun.COM 			continue;
73010923SEvan.Yan@Sun.COM 
73110923SEvan.Yan@Sun.COM 		if ((new_device = pcicfg_devi_find(devi, device, func))
73210923SEvan.Yan@Sun.COM 		    == NULL) {
7333470Sanish 			continue;
7343470Sanish 		}
7353470Sanish 
7363470Sanish 		DEBUG2("Cleaning up device [0x%x] function [0x%x]\n",
73710923SEvan.Yan@Sun.COM 		    device, func);
7383470Sanish 		/*
7393470Sanish 		 * If this was a bridge device it will have a
7403470Sanish 		 * probe handle - if not, no harm in calling this.
7413470Sanish 		 */
7423470Sanish 		(void) pcicfg_destroy_phdl(new_device);
74311245SZhijun.Fu@Sun.COM 		if (is_pcie) {
74411245SZhijun.Fu@Sun.COM 			/*
74511245SZhijun.Fu@Sun.COM 			 * free pcie_bus_t for the sub-tree
74611245SZhijun.Fu@Sun.COM 			 */
74711245SZhijun.Fu@Sun.COM 			if (ddi_get_child(new_device) != NULL)
74811245SZhijun.Fu@Sun.COM 				pcie_fab_fini_bus(new_device, PCIE_BUS_ALL);
74911245SZhijun.Fu@Sun.COM 
75011245SZhijun.Fu@Sun.COM 			pcie_fini_bus(new_device, PCIE_BUS_ALL);
75111245SZhijun.Fu@Sun.COM 		}
7523470Sanish 		/*
7533470Sanish 		 * This will free up the node
7543470Sanish 		 */
7553470Sanish 		(void) ndi_devi_offline(new_device, NDI_DEVI_REMOVE);
7563470Sanish 	}
7573470Sanish 	ndi_devi_exit(devi, circ);
7583470Sanish 
7595149Sjveta 	/*
7605149Sjveta 	 * Use private return codes to help identify issues without debugging
7615149Sjveta 	 * enabled.  Resource limitations and mis-configurations are
7625149Sjveta 	 * probably the most likely caue of configuration failures on x86.
7635149Sjveta 	 * Convert return code back to values expected by the external
7645149Sjveta 	 * consumer before returning so we will warn only once on the first
7655149Sjveta 	 * encountered failure.
7665149Sjveta 	 */
7675149Sjveta 	if (rv == PCICFG_NORESRC) {
7685149Sjveta 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
7695149Sjveta 
7705149Sjveta 		(void) ddi_pathname(devi, path);
7715149Sjveta 		cmn_err(CE_CONT, "?Not enough PCI resources to "
7725149Sjveta 		    "configure: %s\n", path);
7735149Sjveta 
7745149Sjveta 		kmem_free(path, MAXPATHLEN);
7755149Sjveta 		rv = PCICFG_FAILURE;
7765149Sjveta 	}
7775149Sjveta 
7785149Sjveta 	return (rv);
7793470Sanish }
7803470Sanish 
7813470Sanish /*
7823470Sanish  * configure the child nodes of ntbridge. new_device points to ntbridge itself
7833470Sanish  */
7843470Sanish /*ARGSUSED*/
7853470Sanish static int
pcicfg_configure_ntbridge(dev_info_t * new_device,uint_t bus,uint_t device)7863470Sanish pcicfg_configure_ntbridge(dev_info_t *new_device, uint_t bus, uint_t device)
7873470Sanish {
7883470Sanish 	int bus_range[2], rc = PCICFG_FAILURE, rc1, max_devs = 0;
7893470Sanish 	int			devno;
7903470Sanish 	dev_info_t		*new_ntbridgechild;
7913470Sanish 	ddi_acc_handle_t	config_handle;
7923470Sanish 	uint16_t		vid;
7933470Sanish 	uint64_t		next_bus;
7943470Sanish 	uint64_t		blen;
7953470Sanish 	ndi_ra_request_t	req;
7963470Sanish 	uint8_t			pcie_device_type = 0;
7973470Sanish 
7983470Sanish 	/*
7993470Sanish 	 * If we need to do indirect config, lets create a property here
8003470Sanish 	 * to let the child conf map routine know that it has to
8013470Sanish 	 * go through the DDI calls, and not assume the devices are
8023470Sanish 	 * mapped directly under the host.
8033470Sanish 	 */
8043470Sanish 	if ((rc = ndi_prop_update_int(DDI_DEV_T_NONE, new_device,
80510923SEvan.Yan@Sun.COM 	    PCI_DEV_CONF_MAP_PROP, (int)DDI_SUCCESS)) != DDI_SUCCESS) {
8063470Sanish 		DEBUG0("Cannot create indirect conf map property.\n");
8073470Sanish 		return ((int)PCICFG_FAILURE);
8083470Sanish 	}
8093470Sanish 
8103470Sanish 	if (pci_config_setup(new_device, &config_handle) != DDI_SUCCESS)
8113470Sanish 		return (PCICFG_FAILURE);
8123470Sanish 	/* check if we are PCIe device */
8133470Sanish 	if (pcicfg_pcie_device_type(new_device, config_handle) == DDI_SUCCESS) {
8143470Sanish 		DEBUG0("PCIe device detected\n");
8153470Sanish 		pcie_device_type = 1;
8163470Sanish 	}
8173470Sanish 	pci_config_teardown(&config_handle);
8183470Sanish 	/* create Bus node properties for ntbridge. */
8193470Sanish 	if (pcicfg_set_busnode_props(new_device, pcie_device_type)
82010923SEvan.Yan@Sun.COM 	    != PCICFG_SUCCESS) {
8213470Sanish 		DEBUG0("Failed to set busnode props\n");
8223470Sanish 		return (rc);
8233470Sanish 	}
8243470Sanish 
8253470Sanish 	/* For now: Lets only support one layer of child */
8263470Sanish 	bzero((caddr_t)&req, sizeof (ndi_ra_request_t));
8273470Sanish 	req.ra_len = 1;
82810923SEvan.Yan@Sun.COM 	if (ndi_ra_alloc(ddi_get_parent(new_device), &req, &next_bus, &blen,
82910923SEvan.Yan@Sun.COM 	    NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS) != NDI_SUCCESS) {
8303470Sanish 		DEBUG0("ntbridge: Failed to get a bus number\n");
8315149Sjveta 		return (PCICFG_NORESRC);
8323470Sanish 	}
8333470Sanish 
8343470Sanish 	DEBUG1("ntbridge bus range start  ->[%d]\n", next_bus);
8353470Sanish 
8363470Sanish 	/*
8373470Sanish 	 * Following will change, as we detect more bridges
8383470Sanish 	 * on the way.
8393470Sanish 	 */
8403470Sanish 	bus_range[0] = (int)next_bus;
8413470Sanish 	bus_range[1] = (int)next_bus;
8423470Sanish 
84310923SEvan.Yan@Sun.COM 	if (ndi_prop_update_int_array(DDI_DEV_T_NONE, new_device, "bus-range",
84410923SEvan.Yan@Sun.COM 	    bus_range, 2) != DDI_SUCCESS) {
8453470Sanish 		DEBUG0("Cannot set ntbridge bus-range property");
8463470Sanish 		return (rc);
8473470Sanish 	}
8483470Sanish 
8493470Sanish 	/*
8503470Sanish 	 * The other interface (away from the host) will be
8513470Sanish 	 * initialized by the nexus driver when it loads.
8523470Sanish 	 * We just have to set the registers and the nexus driver
8533470Sanish 	 * figures out the rest.
8543470Sanish 	 */
8553470Sanish 
8563470Sanish 	/*
8573470Sanish 	 * finally, lets load and attach the driver
8583470Sanish 	 * before configuring children of ntbridge.
8593470Sanish 	 */
8603470Sanish 	rc = ndi_devi_online(new_device, NDI_ONLINE_ATTACH|NDI_CONFIG);
8613470Sanish 	if (rc != NDI_SUCCESS) {
8623470Sanish 		cmn_err(CE_WARN,
8633470Sanish 		"pcicfg: Fail:cant load nontransparent bridgd driver..\n");
8643470Sanish 		rc = PCICFG_FAILURE;
8653470Sanish 		return (rc);
8663470Sanish 	}
8673470Sanish 	DEBUG0("pcicfg: Success loading nontransparent bridge nexus driver..");
8683470Sanish 
8695149Sjveta 	/* Now set aside pci resource allocation requests for our children */
87010923SEvan.Yan@Sun.COM 	if (pcicfg_ntbridge_allocate_resources(new_device) != PCICFG_SUCCESS) {
8713470Sanish 		max_devs = 0;
8723470Sanish 		rc = PCICFG_FAILURE;
8733470Sanish 	} else
8743470Sanish 		max_devs = PCI_MAX_DEVICES;
8753470Sanish 
8763470Sanish 	/* Probe devices on 2nd bus */
8775149Sjveta 	rc = PCICFG_SUCCESS;
8783470Sanish 	for (devno = pcicfg_start_devno; devno < max_devs; devno++) {
8793470Sanish 
8803470Sanish 		ndi_devi_alloc_sleep(new_device, DEVI_PSEUDO_NEXNAME,
8813470Sanish 		    (pnode_t)DEVI_SID_NODEID, &new_ntbridgechild);
8823470Sanish 
8833470Sanish 		if (pcicfg_add_config_reg(new_ntbridgechild, next_bus, devno, 0)
88410923SEvan.Yan@Sun.COM 		    != DDI_PROP_SUCCESS) {
8853470Sanish 			cmn_err(CE_WARN,
88610923SEvan.Yan@Sun.COM 			    "Failed to add conf reg for ntbridge child.\n");
8873470Sanish 			(void) ndi_devi_free(new_ntbridgechild);
8883470Sanish 			rc = PCICFG_FAILURE;
8893470Sanish 			break;
8903470Sanish 		}
8913470Sanish 
8923470Sanish 		if (pci_config_setup(new_ntbridgechild, &config_handle)
89310923SEvan.Yan@Sun.COM 		    != DDI_SUCCESS) {
8943470Sanish 			cmn_err(CE_WARN,
89510923SEvan.Yan@Sun.COM 			    "Cannot map ntbridge child %x\n", devno);
8963470Sanish 			(void) ndi_devi_free(new_ntbridgechild);
8973470Sanish 			rc = PCICFG_FAILURE;
8983470Sanish 			break;
8993470Sanish 		}
9003470Sanish 
9013470Sanish 		/*
9023470Sanish 		 * See if there is any PCI HW at this location
9033470Sanish 		 * by reading the Vendor ID.  If it returns with 0xffff
9043470Sanish 		 * then there is no hardware at this location.
9053470Sanish 		 */
9063470Sanish 		vid = pci_config_get16(config_handle, PCI_CONF_VENID);
9073470Sanish 
9083470Sanish 		pci_config_teardown(&config_handle);
9093470Sanish 		(void) ndi_devi_free(new_ntbridgechild);
9103470Sanish 		if (vid	== 0xffff)
9113470Sanish 			continue;
9123470Sanish 
9133470Sanish 		/* Lets fake attachments points for each child, */
91410923SEvan.Yan@Sun.COM 		rc = pcicfg_configure(new_device, devno, PCICFG_ALL_FUNC, 0);
9155149Sjveta 		if (rc != PCICFG_SUCCESS) {
9163470Sanish 			int old_dev = pcicfg_start_devno;
9173470Sanish 
9183470Sanish 			cmn_err(CE_WARN,
91910923SEvan.Yan@Sun.COM 			    "Error configuring ntbridge child dev=%d\n", devno);
9203470Sanish 
9213470Sanish 			while (old_dev != devno) {
9223470Sanish 				if (pcicfg_ntbridge_unconfigure_child(
92310923SEvan.Yan@Sun.COM 				    new_device, old_dev) == PCICFG_FAILURE)
92410923SEvan.Yan@Sun.COM 					cmn_err(CE_WARN, "Unconfig Error "
92510923SEvan.Yan@Sun.COM 					    "ntbridge child dev=%d\n", old_dev);
9263470Sanish 				old_dev++;
9273470Sanish 			}
9283470Sanish 			break;
9293470Sanish 		}
9303470Sanish 	} /* devno loop */
9313470Sanish 	DEBUG1("ntbridge: finish probing 2nd bus, rc=%d\n", rc);
9323470Sanish 
9335149Sjveta 	if (rc == PCICFG_SUCCESS)
9343470Sanish 		rc = pcicfg_ntbridge_configure_done(new_device);
9353470Sanish 	else {
9363470Sanish 		pcicfg_phdl_t *entry = pcicfg_find_phdl(new_device);
9373470Sanish 		uint_t			*bus;
9383470Sanish 		int			k;
9393470Sanish 
9403470Sanish 		if (ddi_getlongprop(DDI_DEV_T_ANY, new_device,
94110923SEvan.Yan@Sun.COM 		    DDI_PROP_DONTPASS, "bus-range", (caddr_t)&bus, &k)
94210923SEvan.Yan@Sun.COM 		    != DDI_PROP_SUCCESS) {
9433470Sanish 			DEBUG0("Failed to read bus-range property\n");
9443470Sanish 			rc = PCICFG_FAILURE;
9453470Sanish 			return (rc);
9463470Sanish 		}
9473470Sanish 
9483470Sanish 		DEBUG2("Need to free bus [%d] range [%d]\n",
94910923SEvan.Yan@Sun.COM 		    bus[0], bus[1] - bus[0] + 1);
95010923SEvan.Yan@Sun.COM 
95110923SEvan.Yan@Sun.COM 		if (ndi_ra_free(ddi_get_parent(new_device), (uint64_t)bus[0],
95210923SEvan.Yan@Sun.COM 		    (uint64_t)(bus[1] - bus[0] + 1), NDI_RA_TYPE_PCI_BUSNUM,
95310923SEvan.Yan@Sun.COM 		    NDI_RA_PASS) != NDI_SUCCESS) {
9543470Sanish 			DEBUG0("Failed to free a bus number\n");
9553470Sanish 			rc = PCICFG_FAILURE;
9563735Sprasad 			kmem_free(bus, k);
9573470Sanish 			return (rc);
9583470Sanish 		}
9593470Sanish 
9603470Sanish 		/*
9613470Sanish 		 * Since no memory allocations are done for non transparent
9623470Sanish 		 * bridges (but instead we just set the handle with the
9633470Sanish 		 * already allocated memory, we just need to reset the
9643470Sanish 		 * following values before calling the destroy_phdl()
9653470Sanish 		 * function next, otherwise the it will try to free
9663470Sanish 		 * memory allocated as in case of a transparent bridge.
9673470Sanish 		 */
9683470Sanish 		entry->memory_len = 0;
9693470Sanish 		entry->pf_memory_len = 0;
9703470Sanish 		entry->io_len = 0;
9713735Sprasad 		kmem_free(bus, k);
9723470Sanish 		/* the following will free hole data. */
9733470Sanish 		(void) pcicfg_destroy_phdl(new_device);
9743470Sanish 	}
9753470Sanish 
9763470Sanish 	/*
9773470Sanish 	 * Unload driver just in case child configure failed!
9783470Sanish 	 */
9793470Sanish 	rc1 = ndi_devi_offline(new_device, 0);
9803470Sanish 	DEBUG1("pcicfg: now unloading the ntbridge driver. rc1=%d\n", rc1);
9813470Sanish 	if (rc1 != NDI_SUCCESS) {
9823470Sanish 		cmn_err(CE_WARN,
9833470Sanish 		"pcicfg: cant unload ntbridge driver..children.\n");
9843470Sanish 		rc = PCICFG_FAILURE;
9853470Sanish 	}
9863470Sanish 
9873470Sanish 	return (rc);
9883470Sanish }
9893470Sanish 
9903470Sanish static int
pcicfg_ntbridge_allocate_resources(dev_info_t * dip)9913470Sanish pcicfg_ntbridge_allocate_resources(dev_info_t *dip)
9923470Sanish {
9933470Sanish 	pcicfg_phdl_t		*phdl;
9943470Sanish 	ndi_ra_request_t	*mem_request;
9953470Sanish 	ndi_ra_request_t	*pf_mem_request;
9963470Sanish 	ndi_ra_request_t	*io_request;
9973470Sanish 	uint64_t		boundbase, boundlen;
9983470Sanish 
9993470Sanish 	phdl = pcicfg_find_phdl(dip);
10003470Sanish 	ASSERT(phdl);
10013470Sanish 
10023470Sanish 	mem_request = &phdl->mem_req;
10033470Sanish 	pf_mem_request = &phdl->pf_mem_req;
10043470Sanish 	io_request  = &phdl->io_req;
10053470Sanish 
10063470Sanish 	phdl->error = PCICFG_SUCCESS;
10073470Sanish 
10083470Sanish 	/* Set Memory space handle for ntbridge */
10093470Sanish 	if (pcicfg_get_ntbridge_child_range(dip, &boundbase, &boundlen,
101010923SEvan.Yan@Sun.COM 	    PCI_BASE_SPACE_MEM) != DDI_SUCCESS) {
10113470Sanish 		cmn_err(CE_WARN,
101210923SEvan.Yan@Sun.COM 		    "ntbridge: Mem resource information failure\n");
10133470Sanish 		phdl->memory_len  = 0;
10143470Sanish 		return (PCICFG_FAILURE);
10153470Sanish 	}
10163470Sanish 	mem_request->ra_boundbase = boundbase;
10173470Sanish 	mem_request->ra_boundlen = boundbase + boundlen;
10183470Sanish 	mem_request->ra_len = boundlen;
10193470Sanish 	mem_request->ra_align_mask =
102010923SEvan.Yan@Sun.COM 	    PCICFG_MEMGRAN - 1; /* 1M alignment on memory space */
10213470Sanish 	mem_request->ra_flags |= NDI_RA_ALLOC_BOUNDED;
10223470Sanish 
10233470Sanish 	/*
10243470Sanish 	 * mem_request->ra_len =
10253470Sanish 	 * PCICFG_ROUND_UP(mem_request->ra_len, PCICFG_MEMGRAN);
10263470Sanish 	 */
10273470Sanish 
10283470Sanish 	phdl->memory_base = phdl->memory_last = boundbase;
10293470Sanish 	phdl->memory_len  = boundlen;
10303470Sanish 	phdl->mem_hole.start = phdl->memory_base;
10313470Sanish 	phdl->mem_hole.len = mem_request->ra_len;
10323470Sanish 	phdl->mem_hole.next = (hole_t *)NULL;
10333470Sanish 
103410923SEvan.Yan@Sun.COM 	DEBUG2("Connector requested [0x%llx], needs [0x%llx] bytes of memory\n",
103510923SEvan.Yan@Sun.COM 	    boundlen, mem_request->ra_len);
10363470Sanish 
10373470Sanish 	/* Set IO space handle for ntbridge */
10383470Sanish 	if (pcicfg_get_ntbridge_child_range(dip, &boundbase, &boundlen,
103910923SEvan.Yan@Sun.COM 	    PCI_BASE_SPACE_IO) != DDI_SUCCESS) {
10403470Sanish 		cmn_err(CE_WARN, "ntbridge: IO resource information failure\n");
10413470Sanish 		phdl->io_len  = 0;
10423470Sanish 		return (PCICFG_FAILURE);
10433470Sanish 	}
10443470Sanish 	io_request->ra_len = boundlen;
10453470Sanish 	io_request->ra_align_mask =
104610923SEvan.Yan@Sun.COM 	    PCICFG_IOGRAN - 1;   /* 4K alignment on I/O space */
10473470Sanish 	io_request->ra_boundbase = boundbase;
10483470Sanish 	io_request->ra_boundlen = boundbase + boundlen;
10493470Sanish 	io_request->ra_flags |= NDI_RA_ALLOC_BOUNDED;
10503470Sanish 
10513470Sanish 	/*
10523470Sanish 	 * io_request->ra_len =
10533470Sanish 	 * PCICFG_ROUND_UP(io_request->ra_len, PCICFG_IOGRAN);
10543470Sanish 	 */
10553470Sanish 
10563470Sanish 	phdl->io_base = phdl->io_last = (uint32_t)boundbase;
10573470Sanish 	phdl->io_len  = (uint32_t)boundlen;
10583470Sanish 	phdl->io_hole.start = phdl->io_base;
10593470Sanish 	phdl->io_hole.len = io_request->ra_len;
10603470Sanish 	phdl->io_hole.next = (hole_t *)NULL;
10613470Sanish 
106210923SEvan.Yan@Sun.COM 	DEBUG2("Connector requested [0x%llx], needs [0x%llx] bytes of IO\n",
106310923SEvan.Yan@Sun.COM 	    boundlen, io_request->ra_len);
10643470Sanish 
10653470Sanish 	/* Set Prefetchable Memory space handle for ntbridge */
10663470Sanish 	if (pcicfg_get_ntbridge_child_range(dip, &boundbase, &boundlen,
106710923SEvan.Yan@Sun.COM 	    PCI_BASE_SPACE_MEM | PCI_BASE_PREF_M) != DDI_SUCCESS) {
10683470Sanish 		cmn_err(CE_WARN,
106910923SEvan.Yan@Sun.COM 		    "ntbridge: PF Mem resource information failure\n");
10703470Sanish 		phdl->pf_memory_len  = 0;
10713470Sanish 		return (PCICFG_FAILURE);
10723470Sanish 	}
10733470Sanish 	pf_mem_request->ra_boundbase = boundbase;
10743470Sanish 	pf_mem_request->ra_boundlen = boundbase + boundlen;
10753470Sanish 	pf_mem_request->ra_len = boundlen;
10763470Sanish 	pf_mem_request->ra_align_mask =
107710923SEvan.Yan@Sun.COM 	    PCICFG_MEMGRAN - 1; /* 1M alignment on memory space */
10783470Sanish 	pf_mem_request->ra_flags |= NDI_RA_ALLOC_BOUNDED;
10793470Sanish 
10803470Sanish 	/*
10813470Sanish 	 * pf_mem_request->ra_len =
10823470Sanish 	 * PCICFG_ROUND_UP(pf_mem_request->ra_len, PCICFG_MEMGRAN);
10833470Sanish 	 */
10843470Sanish 
10853470Sanish 	phdl->pf_memory_base = phdl->pf_memory_last = boundbase;
10863470Sanish 	phdl->pf_memory_len  = boundlen;
10873470Sanish 	phdl->pf_mem_hole.start = phdl->pf_memory_base;
10883470Sanish 	phdl->pf_mem_hole.len = pf_mem_request->ra_len;
10893470Sanish 	phdl->pf_mem_hole.next = (hole_t *)NULL;
10903470Sanish 
109110923SEvan.Yan@Sun.COM 	DEBUG2("Connector requested [0x%llx], needs [0x%llx] bytes of PF "
109210923SEvan.Yan@Sun.COM 	    "memory\n", boundlen, pf_mem_request->ra_len);
10933470Sanish 
10943470Sanish 	DEBUG2("MEMORY BASE = [0x%lx] length [0x%lx]\n",
109510923SEvan.Yan@Sun.COM 	    phdl->memory_base, phdl->memory_len);
10963470Sanish 	DEBUG2("IO     BASE = [0x%x] length [0x%x]\n",
109710923SEvan.Yan@Sun.COM 	    phdl->io_base, phdl->io_len);
10983470Sanish 	DEBUG2("PF MEMORY BASE = [0x%lx] length [0x%lx]\n",
109910923SEvan.Yan@Sun.COM 	    phdl->pf_memory_base, phdl->pf_memory_len);
11003470Sanish 
11013470Sanish 	return (PCICFG_SUCCESS);
11023470Sanish }
11033470Sanish 
11043470Sanish static int
pcicfg_ntbridge_configure_done(dev_info_t * dip)11053470Sanish pcicfg_ntbridge_configure_done(dev_info_t *dip)
11063470Sanish {
11073470Sanish 	ppb_ranges_t range[PCICFG_RANGE_LEN];
11083470Sanish 	pcicfg_phdl_t		*entry;
11093470Sanish 	uint_t			len;
11103470Sanish 	pci_bus_range_t		bus_range;
11113470Sanish 	int			new_bus_range[2];
11123470Sanish 
11133470Sanish 	DEBUG1("Configuring children for %p\n", dip);
11143470Sanish 
11153470Sanish 	entry = pcicfg_find_phdl(dip);
11163470Sanish 	ASSERT(entry);
11173470Sanish 
111810923SEvan.Yan@Sun.COM 	bzero((caddr_t)range, sizeof (ppb_ranges_t) * PCICFG_RANGE_LEN);
11193470Sanish 	range[1].child_high = range[1].parent_high |=
112010923SEvan.Yan@Sun.COM 	    (PCI_REG_REL_M | PCI_ADDR_MEM32);
11213470Sanish 	range[1].child_low = range[1].parent_low = (uint32_t)entry->memory_base;
11223470Sanish 
11233470Sanish 	range[0].child_high = range[0].parent_high |=
112410923SEvan.Yan@Sun.COM 	    (PCI_REG_REL_M | PCI_ADDR_IO);
11253470Sanish 	range[0].child_low = range[0].parent_low = (uint32_t)entry->io_base;
11263470Sanish 
11273470Sanish 	range[2].child_high = range[2].parent_high |=
112810923SEvan.Yan@Sun.COM 	    (PCI_REG_REL_M | PCI_ADDR_MEM32 | PCI_REG_PF_M);
11293470Sanish 	range[2].child_low = range[2].parent_low =
113010923SEvan.Yan@Sun.COM 	    (uint32_t)entry->pf_memory_base;
11313470Sanish 
11323470Sanish 	len = sizeof (pci_bus_range_t);
11333470Sanish 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
113410923SEvan.Yan@Sun.COM 	    "bus-range", (caddr_t)&bus_range, (int *)&len) != DDI_SUCCESS) {
11353470Sanish 		DEBUG0("no bus-range property\n");
11363470Sanish 		return (PCICFG_FAILURE);
11373470Sanish 	}
11383470Sanish 
11393470Sanish 	new_bus_range[0] = bus_range.lo;	/* primary bus number */
11403470Sanish 	if (entry->highest_bus) {	/* secondary bus number */
11413470Sanish 		if (entry->highest_bus < bus_range.lo) {
11423470Sanish 			cmn_err(CE_WARN,
114310923SEvan.Yan@Sun.COM 			    "ntbridge bus range invalid !(%d,%d)\n",
114410923SEvan.Yan@Sun.COM 			    bus_range.lo, entry->highest_bus);
11453470Sanish 			new_bus_range[1] = bus_range.lo + entry->highest_bus;
11463470Sanish 		}
11473470Sanish 		else
11483470Sanish 			new_bus_range[1] = entry->highest_bus;
11493470Sanish 	}
11503470Sanish 	else
11513470Sanish 		new_bus_range[1] = bus_range.hi;
11523470Sanish 
115310923SEvan.Yan@Sun.COM 	DEBUG2("ntbridge: bus range lo=%x, hi=%x\n", new_bus_range[0],
115410923SEvan.Yan@Sun.COM 	    new_bus_range[1]);
115510923SEvan.Yan@Sun.COM 
115610923SEvan.Yan@Sun.COM 	if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "bus-range",
115710923SEvan.Yan@Sun.COM 	    new_bus_range, 2) != DDI_SUCCESS) {
11583470Sanish 		DEBUG0("Failed to set bus-range property");
11593470Sanish 		entry->error = PCICFG_FAILURE;
11603470Sanish 		return (PCICFG_FAILURE);
11613470Sanish 	}
11623470Sanish 
11633470Sanish #ifdef DEBUG
11643470Sanish 	{
11653470Sanish 		uint64_t	unused;
11663470Sanish 		unused = pcicfg_unused_space(&entry->io_hole, &len);
11673470Sanish 		DEBUG2("ntbridge: Unused IO space %llx bytes over %d holes\n",
116810923SEvan.Yan@Sun.COM 		    unused, len);
11693470Sanish 	}
11703470Sanish #endif
11713470Sanish 
11723470Sanish 	range[0].size_low = entry->io_len;
11733470Sanish 	if (pcicfg_update_ranges_prop(dip, &range[0])) {
11743470Sanish 		DEBUG0("Failed to update ranges (i/o)\n");
11753470Sanish 		entry->error = PCICFG_FAILURE;
11763470Sanish 		return (PCICFG_FAILURE);
11773470Sanish 	}
11783470Sanish 
11793470Sanish #ifdef DEBUG
11803470Sanish 	{
11813470Sanish 		uint64_t	unused;
11823470Sanish 		unused = pcicfg_unused_space(&entry->mem_hole, &len);
11833470Sanish 		DEBUG2("ntbridge: Unused Mem space %llx bytes over %d holes\n",
118410923SEvan.Yan@Sun.COM 		    unused, len);
11853470Sanish 	}
11863470Sanish #endif
11873470Sanish 
11883470Sanish 	range[1].size_low = entry->memory_len;
11893470Sanish 	if (pcicfg_update_ranges_prop(dip, &range[1])) {
11903470Sanish 		DEBUG0("Failed to update ranges (memory)\n");
11913470Sanish 		entry->error = PCICFG_FAILURE;
11923470Sanish 		return (PCICFG_FAILURE);
11933470Sanish 	}
11943470Sanish 
11953470Sanish #ifdef DEBUG
11963470Sanish 	{
11973470Sanish 		uint64_t	unused;
11983470Sanish 		unused = pcicfg_unused_space(&entry->pf_mem_hole, &len);
11993470Sanish 		DEBUG2("ntbridge: Unused PF Mem space %llx bytes over"
120010923SEvan.Yan@Sun.COM 		    " %d holes\n", unused, len);
12013470Sanish 	}
12023470Sanish #endif
12033470Sanish 
12043470Sanish 	range[2].size_low = entry->pf_memory_len;
12053470Sanish 	if (pcicfg_update_ranges_prop(dip, &range[2])) {
12063470Sanish 		DEBUG0("Failed to update ranges (PF memory)\n");
12073470Sanish 		entry->error = PCICFG_FAILURE;
12083470Sanish 		return (PCICFG_FAILURE);
12093470Sanish 	}
12103470Sanish 
12113470Sanish 	return (PCICFG_SUCCESS);
12123470Sanish }
12133470Sanish 
12143470Sanish static int
pcicfg_ntbridge_program_child(dev_info_t * dip)12153470Sanish pcicfg_ntbridge_program_child(dev_info_t *dip)
12163470Sanish {
121710923SEvan.Yan@Sun.COM 	pcicfg_phdl_t	*entry;
121810923SEvan.Yan@Sun.COM 	int		rc = PCICFG_SUCCESS;
12193470Sanish 	dev_info_t	*anode = dip;
12203470Sanish 
122110923SEvan.Yan@Sun.COM 	/* Find the Hotplug Connection (CN) node */
122210923SEvan.Yan@Sun.COM 	while ((anode != NULL) &&
122310923SEvan.Yan@Sun.COM 	    (strcmp(ddi_binding_name(anode), "hp_attachment") != 0)) {
12243470Sanish 		anode = ddi_get_parent(anode);
12253470Sanish 	}
12263470Sanish 
12273470Sanish 	if (anode == NULL) {
12283470Sanish 		DEBUG0("ntbridge child tree not in PROBE state\n");
12293470Sanish 		return (PCICFG_FAILURE);
12303470Sanish 	}
12313470Sanish 	entry = pcicfg_find_phdl(ddi_get_parent(anode));
12323470Sanish 	ASSERT(entry);
12333470Sanish 
12343470Sanish 	if (pcicfg_bridge_assign(dip, entry) == DDI_WALK_TERMINATE) {
12353470Sanish 		cmn_err(CE_WARN,
123610923SEvan.Yan@Sun.COM 		    "ntbridge: Error assigning range for child %s\n",
123710923SEvan.Yan@Sun.COM 		    ddi_get_name(dip));
12383470Sanish 		rc = PCICFG_FAILURE;
12393470Sanish 	}
12403470Sanish 	return (rc);
12413470Sanish }
12423470Sanish 
12433470Sanish static int
pcicfg_ntbridge_unconfigure_child(dev_info_t * new_device,uint_t devno)12443470Sanish pcicfg_ntbridge_unconfigure_child(dev_info_t *new_device, uint_t devno)
12453470Sanish {
12463470Sanish 
12473470Sanish 	dev_info_t	*new_ntbridgechild;
12483470Sanish 	int 		len, bus;
12493470Sanish 	uint16_t	vid;
12503470Sanish 	ddi_acc_handle_t	config_handle;
12513470Sanish 	pci_bus_range_t pci_bus_range;
12523470Sanish 
12533470Sanish 	len = sizeof (pci_bus_range_t);
12543470Sanish 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, new_device, DDI_PROP_DONTPASS,
125510923SEvan.Yan@Sun.COM 	    "bus-range", (caddr_t)&pci_bus_range, &len) != DDI_SUCCESS) {
12563470Sanish 		DEBUG0("no bus-range property\n");
12573470Sanish 		return (PCICFG_FAILURE);
12583470Sanish 	}
12593470Sanish 
12603470Sanish 	bus = pci_bus_range.lo; /* primary bus number of this bus node */
12613470Sanish 
12623470Sanish 	ndi_devi_alloc_sleep(new_device, DEVI_PSEUDO_NEXNAME,
12633470Sanish 	    (pnode_t)DEVI_SID_NODEID, &new_ntbridgechild);
12643470Sanish 
12653470Sanish 	if (pcicfg_add_config_reg(new_ntbridgechild, bus, devno, 0)
126610923SEvan.Yan@Sun.COM 	    != DDI_PROP_SUCCESS) {
126710923SEvan.Yan@Sun.COM 		cmn_err(CE_WARN, "Unconfigure: Failed to add conf reg prop for "
126810923SEvan.Yan@Sun.COM 		    "ntbridge child.\n");
12693470Sanish 		(void) ndi_devi_free(new_ntbridgechild);
12703470Sanish 		return (PCICFG_FAILURE);
12713470Sanish 	}
12723470Sanish 
12733470Sanish 	if (pci_config_setup(new_ntbridgechild, &config_handle)
127410923SEvan.Yan@Sun.COM 	    != DDI_SUCCESS) {
127510923SEvan.Yan@Sun.COM 		cmn_err(CE_WARN, "pcicfg: Cannot map ntbridge child %x\n",
127610923SEvan.Yan@Sun.COM 		    devno);
12773470Sanish 		(void) ndi_devi_free(new_ntbridgechild);
12783470Sanish 		return (PCICFG_FAILURE);
12793470Sanish 	}
12803470Sanish 
12813470Sanish 	/*
12823470Sanish 	 * See if there is any PCI HW at this location
12833470Sanish 	 * by reading the Vendor ID.  If it returns with 0xffff
12843470Sanish 	 * then there is no hardware at this location.
12853470Sanish 	 */
12863470Sanish 	vid = pci_config_get16(config_handle, PCI_CONF_VENID);
12873470Sanish 
12883470Sanish 	pci_config_teardown(&config_handle);
12893470Sanish 	(void) ndi_devi_free(new_ntbridgechild);
12903470Sanish 	if (vid	== 0xffff)
12913470Sanish 		return (PCICFG_NODEVICE);
12923470Sanish 
129310923SEvan.Yan@Sun.COM 	return (pcicfg_unconfigure(new_device, devno, PCICFG_ALL_FUNC, 0));
12943470Sanish }
12953470Sanish 
12963470Sanish static uint_t
pcicfg_ntbridge_unconfigure(dev_info_t * dip)12973470Sanish pcicfg_ntbridge_unconfigure(dev_info_t *dip)
12983470Sanish {
12993470Sanish 	pcicfg_phdl_t *entry = pcicfg_find_phdl(dip);
13003470Sanish 	uint_t			*bus;
13013470Sanish 	int			k, rc = DDI_FAILURE;
13023470Sanish 
130310923SEvan.Yan@Sun.COM 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "bus-range",
130410923SEvan.Yan@Sun.COM 	    (caddr_t)&bus, &k) != DDI_PROP_SUCCESS) {
13053470Sanish 		DEBUG0("ntbridge: Failed to read bus-range property\n");
13063470Sanish 		return (rc);
13073470Sanish 	}
13083470Sanish 
13093470Sanish 	DEBUG2("ntbridge: Need to free bus [%d] range [%d]\n",
131010923SEvan.Yan@Sun.COM 	    bus[0], bus[1] - bus[0] + 1);
131110923SEvan.Yan@Sun.COM 
131210923SEvan.Yan@Sun.COM 	if (ndi_ra_free(ddi_get_parent(dip), (uint64_t)bus[0],
131310923SEvan.Yan@Sun.COM 	    (uint64_t)(bus[1] - bus[0] + 1),
131410923SEvan.Yan@Sun.COM 	    NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS) != NDI_SUCCESS) {
13153470Sanish 		DEBUG0("ntbridge: Failed to free a bus number\n");
13163470Sanish 		kmem_free(bus, k);
13173470Sanish 		return (rc);
13183470Sanish 	}
13193470Sanish 
13203470Sanish 	/*
13213470Sanish 	 * Since our resources will be freed at the parent level,
13223470Sanish 	 * just reset these values.
13233470Sanish 	 */
13243470Sanish 	entry->memory_len = 0;
13253470Sanish 	entry->io_len = 0;
13263470Sanish 	entry->pf_memory_len = 0;
13273470Sanish 
13283470Sanish 	kmem_free(bus, k);
13293470Sanish 
13303470Sanish 	/* the following will also free hole data. */
13313470Sanish 	return (pcicfg_destroy_phdl(dip));
13323470Sanish 
13333470Sanish }
13343470Sanish 
13353470Sanish static int
pcicfg_is_ntbridge(dev_info_t * dip)13363470Sanish pcicfg_is_ntbridge(dev_info_t *dip)
13373470Sanish {
13383470Sanish 	ddi_acc_handle_t	config_handle;
13393470Sanish 	uint8_t		class, subclass;
13403470Sanish 	int		rc = DDI_SUCCESS;
13413470Sanish 
13423470Sanish 	if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
13433470Sanish 		cmn_err(CE_WARN,
134410923SEvan.Yan@Sun.COM 		    "pcicfg: cannot map config space, to get map type\n");
13453470Sanish 		return (DDI_FAILURE);
13463470Sanish 	}
13473470Sanish 	class = pci_config_get8(config_handle, PCI_CONF_BASCLASS);
13483470Sanish 	subclass = pci_config_get8(config_handle, PCI_CONF_SUBCLASS);
13493470Sanish 
13503470Sanish 	/* check for class=6, subclass=9, for non transparent bridges.  */
13513470Sanish 	if ((class != PCI_CLASS_BRIDGE) || (subclass != PCI_BRIDGE_STBRIDGE))
13523470Sanish 		rc = DDI_FAILURE;
13533470Sanish 
13543470Sanish 	DEBUG3("pcicfg: checking device %x,%x for indirect map. rc=%d\n",
135510923SEvan.Yan@Sun.COM 	    pci_config_get16(config_handle, PCI_CONF_VENID),
135610923SEvan.Yan@Sun.COM 	    pci_config_get16(config_handle, PCI_CONF_DEVID),
135710923SEvan.Yan@Sun.COM 	    rc);
13583470Sanish 	pci_config_teardown(&config_handle);
13593470Sanish 	return (rc);
13603470Sanish }
13613470Sanish 
13623470Sanish static uint_t
pcicfg_ntbridge_child(dev_info_t * dip)13633470Sanish pcicfg_ntbridge_child(dev_info_t *dip)
13643470Sanish {
13653470Sanish 	int 		len, val, rc = DDI_FAILURE;
13663470Sanish 	dev_info_t	*anode = dip;
13673470Sanish 
13683470Sanish 	/*
136910923SEvan.Yan@Sun.COM 	 * Find the Hotplug Connection (CN) node
13703470Sanish 	 */
13713470Sanish 	while ((anode != NULL) && (strcmp(ddi_binding_name(anode),
137210923SEvan.Yan@Sun.COM 	    "hp_attachment") != 0)) {
13733470Sanish 		anode = ddi_get_parent(anode);
13743470Sanish 	}
13753470Sanish 
13763470Sanish 	if (anode == NULL) {
13773470Sanish 		DEBUG0("ntbridge child tree not in PROBE state\n");
13783470Sanish 		return (rc);
13793470Sanish 	}
13803470Sanish 	len = sizeof (int);
13813470Sanish 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ddi_get_parent(anode),
138210923SEvan.Yan@Sun.COM 	    DDI_PROP_DONTPASS, PCI_DEV_CONF_MAP_PROP, (caddr_t)&val, &len)
138310923SEvan.Yan@Sun.COM 	    != DDI_SUCCESS) {
13843470Sanish 
13853470Sanish 		DEBUG1("ntbridge child: no \"%s\" property\n",
138610923SEvan.Yan@Sun.COM 		    PCI_DEV_CONF_MAP_PROP);
13873470Sanish 		return (rc);
13883470Sanish 	}
13893470Sanish 	DEBUG0("ntbridge child: success\n");
13903470Sanish 	return (DDI_SUCCESS);
13913470Sanish }
13923470Sanish 
13933470Sanish static uint_t
pcicfg_get_ntbridge_child_range(dev_info_t * dip,uint64_t * boundbase,uint64_t * boundlen,uint_t space_type)13943470Sanish pcicfg_get_ntbridge_child_range(dev_info_t *dip, uint64_t *boundbase,
13953470Sanish 				uint64_t *boundlen, uint_t space_type)
13963470Sanish {
13973470Sanish 	int		length, found = DDI_FAILURE, acount, i, ibridge;
13983470Sanish 	pci_regspec_t	*assigned;
13993470Sanish 
14003470Sanish 	if ((ibridge = pcicfg_is_ntbridge(dip)) == DDI_FAILURE)
14013470Sanish 		return (found);
14023470Sanish 
140310923SEvan.Yan@Sun.COM 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
140410923SEvan.Yan@Sun.COM 	    "assigned-addresses", (caddr_t)&assigned, &length)
140510923SEvan.Yan@Sun.COM 	    != DDI_PROP_SUCCESS) {
14063470Sanish 		DEBUG1("Failed to get assigned-addresses property %llx\n", dip);
14073470Sanish 		return (found);
14083470Sanish 	}
14093470Sanish 	DEBUG1("pcicfg: ntbridge child range: dip = %s\n",
141010923SEvan.Yan@Sun.COM 	    ddi_driver_name(dip));
14113470Sanish 
14123470Sanish 	acount = length / sizeof (pci_regspec_t);
14133470Sanish 
14143470Sanish 	for (i = 0; i < acount; i++) {
141510923SEvan.Yan@Sun.COM 		if ((PCI_REG_REG_G(assigned[i].pci_phys_hi) ==
141610923SEvan.Yan@Sun.COM 		    pcicfg_indirect_map_devs[ibridge].mem_range_bar_offset) &&
141710923SEvan.Yan@Sun.COM 		    (space_type == PCI_BASE_SPACE_MEM)) {
14183470Sanish 			found = DDI_SUCCESS;
14193470Sanish 			break;
142010923SEvan.Yan@Sun.COM 		} else if ((PCI_REG_REG_G(assigned[i].pci_phys_hi) ==
142110923SEvan.Yan@Sun.COM 		    pcicfg_indirect_map_devs[ibridge].io_range_bar_offset) &&
142210923SEvan.Yan@Sun.COM 		    (space_type == PCI_BASE_SPACE_IO)) {
142310923SEvan.Yan@Sun.COM 			found = DDI_SUCCESS;
142410923SEvan.Yan@Sun.COM 			break;
142510923SEvan.Yan@Sun.COM 		} else if ((PCI_REG_REG_G(assigned[i].pci_phys_hi) ==
142610923SEvan.Yan@Sun.COM 		    pcicfg_indirect_map_devs[ibridge].
142710923SEvan.Yan@Sun.COM 		    prefetch_mem_range_bar_offset) &&
142810923SEvan.Yan@Sun.COM 		    (space_type == (PCI_BASE_SPACE_MEM |
142910923SEvan.Yan@Sun.COM 		    PCI_BASE_PREF_M))) {
143010923SEvan.Yan@Sun.COM 			found = DDI_SUCCESS;
143110923SEvan.Yan@Sun.COM 			break;
14323470Sanish 		}
14333470Sanish 	}
14343470Sanish 	DEBUG3("pcicfg: ntbridge child range: space=%x, base=%lx, len=%lx\n",
143510923SEvan.Yan@Sun.COM 	    space_type, assigned[i].pci_phys_low, assigned[i].pci_size_low);
14363470Sanish 
14373470Sanish 	if (found == DDI_SUCCESS)  {
14383470Sanish 		*boundbase = assigned[i].pci_phys_low;
14393470Sanish 		*boundlen = assigned[i].pci_size_low;
14403470Sanish 	}
14413470Sanish 
14423470Sanish 	kmem_free(assigned, length);
14433470Sanish 	return (found);
14443470Sanish }
14453470Sanish 
14463470Sanish /*
14473470Sanish  * This will turn  resources allocated by pcicfg_configure()
144810923SEvan.Yan@Sun.COM  * and remove the device tree from the Hotplug Connection (CN)
14493470Sanish  * and below.  The routine assumes the devices have their
14503470Sanish  * drivers detached.
14513470Sanish  */
14523470Sanish int
pcicfg_unconfigure(dev_info_t * devi,uint_t device,uint_t function,pcicfg_flags_t flags)145310923SEvan.Yan@Sun.COM pcicfg_unconfigure(dev_info_t *devi, uint_t device, uint_t function,
145410923SEvan.Yan@Sun.COM     pcicfg_flags_t flags)
14553470Sanish {
14563470Sanish 	dev_info_t *child_dip;
14573470Sanish 	int func;
14583470Sanish 	int i;
145910923SEvan.Yan@Sun.COM 	int max_function, trans_device;
146011245SZhijun.Fu@Sun.COM 	int circ;
146111245SZhijun.Fu@Sun.COM 	boolean_t is_pcie;
146210923SEvan.Yan@Sun.COM 
146310923SEvan.Yan@Sun.COM 	if (pcie_ari_is_enabled(devi) == PCIE_ARI_FORW_ENABLED)
146410923SEvan.Yan@Sun.COM 		max_function = PCICFG_MAX_ARI_FUNCTION;
146510923SEvan.Yan@Sun.COM 	else
146610923SEvan.Yan@Sun.COM 		max_function = PCI_MAX_FUNCTIONS;
14673470Sanish 
14683470Sanish 	/*
14693470Sanish 	 * Cycle through devices to make sure none are busy.
14703470Sanish 	 * If a single device is busy fail the whole unconfigure.
14713470Sanish 	 */
147211245SZhijun.Fu@Sun.COM 	is_pcie = is_pcie_fabric(devi);
147311245SZhijun.Fu@Sun.COM 
147411245SZhijun.Fu@Sun.COM 	ndi_devi_enter(devi, &circ);
147510923SEvan.Yan@Sun.COM 	for (func = 0; func < max_function; func++) {
147610923SEvan.Yan@Sun.COM 		if ((function != PCICFG_ALL_FUNC) && (function != func))
147710923SEvan.Yan@Sun.COM 			continue;
147810923SEvan.Yan@Sun.COM 
147910923SEvan.Yan@Sun.COM 		if (max_function == PCICFG_MAX_ARI_FUNCTION)
148010923SEvan.Yan@Sun.COM 			trans_device = func >> 3; /* ARI Device */
148110923SEvan.Yan@Sun.COM 		else
148210923SEvan.Yan@Sun.COM 			trans_device = device;
148310923SEvan.Yan@Sun.COM 
148410923SEvan.Yan@Sun.COM 		if ((child_dip = pcicfg_devi_find(devi, trans_device,
148510923SEvan.Yan@Sun.COM 		    func & 7)) == NULL)
14863470Sanish 			continue;
14873470Sanish 
14883470Sanish 		if (ndi_devi_offline(child_dip, NDI_UNCONFIG) == NDI_SUCCESS)
148910923SEvan.Yan@Sun.COM 			continue;
149010923SEvan.Yan@Sun.COM 
14913470Sanish 		/*
14923470Sanish 		 * Device function is busy. Before returning we have to
14933470Sanish 		 * put all functions back online which were taken
14943470Sanish 		 * offline during the process.
14953470Sanish 		 */
149610923SEvan.Yan@Sun.COM 		DEBUG2("Device [0x%x] function [0x%x] is busy\n",
149710923SEvan.Yan@Sun.COM 		    trans_device, func & 7);
149810923SEvan.Yan@Sun.COM 		/*
149910923SEvan.Yan@Sun.COM 		 * If we are only asked to offline one specific function,
150010923SEvan.Yan@Sun.COM 		 * and that fails, we just simply return.
150110923SEvan.Yan@Sun.COM 		 */
150210923SEvan.Yan@Sun.COM 		if (function != PCICFG_ALL_FUNC)
150310923SEvan.Yan@Sun.COM 			return (PCICFG_FAILURE);
150410923SEvan.Yan@Sun.COM 
15053470Sanish 		for (i = 0; i < func; i++) {
150610923SEvan.Yan@Sun.COM 			if (max_function == PCICFG_MAX_ARI_FUNCTION)
150710923SEvan.Yan@Sun.COM 				trans_device = i >> 3;
150810923SEvan.Yan@Sun.COM 
150910923SEvan.Yan@Sun.COM 			if ((child_dip = pcicfg_devi_find(devi, trans_device,
151010923SEvan.Yan@Sun.COM 			    i & 7)) == NULL) {
151110923SEvan.Yan@Sun.COM 				DEBUG0("No more devices to put back "
151210923SEvan.Yan@Sun.COM 				    "on line!!\n");
151310923SEvan.Yan@Sun.COM 				/*
151410923SEvan.Yan@Sun.COM 				 * Made it through all functions
151510923SEvan.Yan@Sun.COM 				 */
151610923SEvan.Yan@Sun.COM 				continue;
151710923SEvan.Yan@Sun.COM 			}
151810923SEvan.Yan@Sun.COM 			if (ndi_devi_online(child_dip, NDI_CONFIG)
151910923SEvan.Yan@Sun.COM 			    != NDI_SUCCESS) {
152010923SEvan.Yan@Sun.COM 				DEBUG0("Failed to put back devices state\n");
152111245SZhijun.Fu@Sun.COM 				goto fail;
152210923SEvan.Yan@Sun.COM 			}
15233470Sanish 		}
152411245SZhijun.Fu@Sun.COM 		goto fail;
15253470Sanish 	}
15263470Sanish 
15273470Sanish 	/*
152810923SEvan.Yan@Sun.COM 	 * Now, tear down all devinfo nodes for this Connector.
15293470Sanish 	 */
153010923SEvan.Yan@Sun.COM 	for (func = 0; func < max_function; func++) {
153110923SEvan.Yan@Sun.COM 		if ((function != PCICFG_ALL_FUNC) && (function != func))
153210923SEvan.Yan@Sun.COM 			continue;
153310923SEvan.Yan@Sun.COM 
153410923SEvan.Yan@Sun.COM 		if (max_function == PCICFG_MAX_ARI_FUNCTION)
153510923SEvan.Yan@Sun.COM 			trans_device = func >> 3; /* ARI Device */
153610923SEvan.Yan@Sun.COM 		else
153710923SEvan.Yan@Sun.COM 			trans_device = device;
153810923SEvan.Yan@Sun.COM 
153910923SEvan.Yan@Sun.COM 		if ((child_dip = pcicfg_devi_find(devi, trans_device, func & 7))
154010923SEvan.Yan@Sun.COM 		    == NULL) {
154110923SEvan.Yan@Sun.COM 			DEBUG2("No device at %x,%x\n", trans_device, func & 7);
15423470Sanish 			continue;
15433470Sanish 		}
15443470Sanish 
15453470Sanish 		DEBUG2("Tearing down device [0x%x] function [0x%x]\n",
154610923SEvan.Yan@Sun.COM 		    trans_device, func & 7);
15473470Sanish 
15483470Sanish 		if (pcicfg_is_ntbridge(child_dip) != DDI_FAILURE)
15493470Sanish 			if (pcicfg_ntbridge_unconfigure(child_dip) !=
155010923SEvan.Yan@Sun.COM 			    PCICFG_SUCCESS) {
15513470Sanish 				cmn_err(CE_WARN,
155210923SEvan.Yan@Sun.COM 				    "ntbridge: unconfigure failed\n");
155311245SZhijun.Fu@Sun.COM 				goto fail;
15543470Sanish 			}
15553470Sanish 
155611245SZhijun.Fu@Sun.COM 		if (pcicfg_teardown_device(child_dip, flags, is_pcie)
155710923SEvan.Yan@Sun.COM 		    != PCICFG_SUCCESS) {
15583470Sanish 			DEBUG2("Failed to tear down device [0x%x]"
155910923SEvan.Yan@Sun.COM 			    "function [0x%x]\n", trans_device, func & 7);
156011245SZhijun.Fu@Sun.COM 			goto fail;
15613470Sanish 		}
15623470Sanish 	}
156310923SEvan.Yan@Sun.COM 
156410923SEvan.Yan@Sun.COM 	if (pcie_ari_is_enabled(devi) == PCIE_ARI_FORW_ENABLED) {
156510923SEvan.Yan@Sun.COM 		(void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "ari-enabled");
156610923SEvan.Yan@Sun.COM 		(void) pcie_ari_disable(devi);
156710923SEvan.Yan@Sun.COM 	}
156810923SEvan.Yan@Sun.COM 
156911245SZhijun.Fu@Sun.COM 	ndi_devi_exit(devi, circ);
15703470Sanish 	return (PCICFG_SUCCESS);
157111245SZhijun.Fu@Sun.COM 
157211245SZhijun.Fu@Sun.COM fail:
157311245SZhijun.Fu@Sun.COM 	ndi_devi_exit(devi, circ);
157411245SZhijun.Fu@Sun.COM 	return (PCICFG_FAILURE);
15753470Sanish }
15763470Sanish 
15773470Sanish static int
pcicfg_teardown_device(dev_info_t * dip,pcicfg_flags_t flags,boolean_t is_pcie)157811245SZhijun.Fu@Sun.COM pcicfg_teardown_device(dev_info_t *dip, pcicfg_flags_t flags, boolean_t is_pcie)
15793470Sanish {
15803470Sanish 	ddi_acc_handle_t	handle;
15813470Sanish 
15823470Sanish 	/*
15833470Sanish 	 * Free up resources associated with 'dip'
15843470Sanish 	 */
158510923SEvan.Yan@Sun.COM 	if (pcicfg_free_resources(dip, flags) != PCICFG_SUCCESS) {
15863470Sanish 		DEBUG0("Failed to free resources\n");
15873470Sanish 		return (PCICFG_FAILURE);
15883470Sanish 	}
15893470Sanish 
15903470Sanish 	/*
15913470Sanish 	 * disable the device
15923470Sanish 	 */
15933470Sanish 	if (pcicfg_config_setup(dip, &handle) != PCICFG_SUCCESS)
15943470Sanish 		return (PCICFG_FAILURE);
15953470Sanish 	pcicfg_device_off(handle);
15963470Sanish 	pcicfg_config_teardown(&handle);
15973470Sanish 
159811245SZhijun.Fu@Sun.COM 	if (is_pcie) {
159911245SZhijun.Fu@Sun.COM 		/*
160011245SZhijun.Fu@Sun.COM 		 * free pcie_bus_t for the sub-tree
160111245SZhijun.Fu@Sun.COM 		 */
160211245SZhijun.Fu@Sun.COM 		if (ddi_get_child(dip) != NULL)
160311245SZhijun.Fu@Sun.COM 			pcie_fab_fini_bus(dip, PCIE_BUS_ALL);
160411245SZhijun.Fu@Sun.COM 
160511245SZhijun.Fu@Sun.COM 		pcie_fini_bus(dip, PCIE_BUS_ALL);
160611245SZhijun.Fu@Sun.COM 	}
160711245SZhijun.Fu@Sun.COM 
16083470Sanish 	/*
16093470Sanish 	 * The framework provides this routine which can
16103470Sanish 	 * tear down a sub-tree.
16113470Sanish 	 */
16123470Sanish 	if (ndi_devi_offline(dip, NDI_DEVI_REMOVE) != NDI_SUCCESS) {
16133470Sanish 		DEBUG0("Failed to offline and remove node\n");
16143470Sanish 		return (PCICFG_FAILURE);
16153470Sanish 	}
16163470Sanish 
16173470Sanish 	return (PCICFG_SUCCESS);
16183470Sanish }
16193470Sanish 
16203470Sanish /*
16213470Sanish  * BEGIN GENERIC SUPPORT ROUTINES
16223470Sanish  */
16233470Sanish static pcicfg_phdl_t *
pcicfg_find_phdl(dev_info_t * dip)16243470Sanish pcicfg_find_phdl(dev_info_t *dip)
16253470Sanish {
16263470Sanish 	pcicfg_phdl_t *entry;
16273470Sanish 	mutex_enter(&pcicfg_list_mutex);
16283470Sanish 	for (entry = pcicfg_phdl_list; entry != NULL; entry = entry->next) {
16293470Sanish 		if (entry->dip == dip) {
16303470Sanish 			mutex_exit(&pcicfg_list_mutex);
16313470Sanish 			return (entry);
16323470Sanish 		}
16333470Sanish 	}
16343470Sanish 	mutex_exit(&pcicfg_list_mutex);
16353470Sanish 
16363470Sanish 	/*
16373470Sanish 	 * Did'nt find entry - create one
16383470Sanish 	 */
16393470Sanish 	return (pcicfg_create_phdl(dip));
16403470Sanish }
16413470Sanish 
16423470Sanish static pcicfg_phdl_t *
pcicfg_create_phdl(dev_info_t * dip)16433470Sanish pcicfg_create_phdl(dev_info_t *dip)
16443470Sanish {
16453470Sanish 	pcicfg_phdl_t *new;
16463470Sanish 
164710923SEvan.Yan@Sun.COM 	new = (pcicfg_phdl_t *)kmem_zalloc(sizeof (pcicfg_phdl_t), KM_SLEEP);
16483470Sanish 
16493470Sanish 	new->dip = dip;
16503470Sanish 	mutex_enter(&pcicfg_list_mutex);
16513470Sanish 	new->next = pcicfg_phdl_list;
16523470Sanish 	pcicfg_phdl_list = new;
16533470Sanish 	mutex_exit(&pcicfg_list_mutex);
16543470Sanish 
16553470Sanish 	return (new);
16563470Sanish }
16573470Sanish 
16583470Sanish static int
pcicfg_destroy_phdl(dev_info_t * dip)16593470Sanish pcicfg_destroy_phdl(dev_info_t *dip)
16603470Sanish {
16613470Sanish 	pcicfg_phdl_t *entry;
16623470Sanish 	pcicfg_phdl_t *follow = NULL;
16633470Sanish 
16643470Sanish 	mutex_enter(&pcicfg_list_mutex);
16653470Sanish 	for (entry = pcicfg_phdl_list; entry != NULL; follow = entry,
166610923SEvan.Yan@Sun.COM 	    entry = entry->next) {
16673470Sanish 		if (entry->dip == dip) {
16683470Sanish 			if (entry == pcicfg_phdl_list) {
16693470Sanish 				pcicfg_phdl_list = entry->next;
16703470Sanish 			} else {
16713470Sanish 				follow->next = entry->next;
16723470Sanish 			}
16733470Sanish 			/*
16743470Sanish 			 * If this entry has any allocated memory
16753470Sanish 			 * or IO space associated with it, that
16763470Sanish 			 * must be freed up.
16773470Sanish 			 */
16783470Sanish 			if (entry->memory_len > 0) {
16793470Sanish 				(void) ndi_ra_free(ddi_get_parent(dip),
168010923SEvan.Yan@Sun.COM 				    entry->memory_base, entry->memory_len,
168110923SEvan.Yan@Sun.COM 				    NDI_RA_TYPE_MEM, NDI_RA_PASS);
16823470Sanish 			}
16833470Sanish 			pcicfg_free_hole(&entry->mem_hole);
16843470Sanish 
16853470Sanish 			if (entry->io_len > 0) {
16863470Sanish 				(void) ndi_ra_free(ddi_get_parent(dip),
168710923SEvan.Yan@Sun.COM 				    entry->io_base, entry->io_len,
168810923SEvan.Yan@Sun.COM 				    NDI_RA_TYPE_IO, NDI_RA_PASS);
16893470Sanish 			}
16903470Sanish 			pcicfg_free_hole(&entry->io_hole);
16913470Sanish 
16923470Sanish 			if (entry->pf_memory_len > 0) {
16933470Sanish 				(void) ndi_ra_free(ddi_get_parent(dip),
169410923SEvan.Yan@Sun.COM 				    entry->pf_memory_base, entry->pf_memory_len,
169510923SEvan.Yan@Sun.COM 				    NDI_RA_TYPE_PCI_PREFETCH_MEM, NDI_RA_PASS);
16963470Sanish 			}
16973470Sanish 			pcicfg_free_hole(&entry->pf_mem_hole);
16983470Sanish 
16993470Sanish 			/*
17003470Sanish 			 * Destroy this entry
17013470Sanish 			 */
17023470Sanish 			kmem_free((caddr_t)entry, sizeof (pcicfg_phdl_t));
17033470Sanish 			mutex_exit(&pcicfg_list_mutex);
17043470Sanish 			return (PCICFG_SUCCESS);
17053470Sanish 		}
17063470Sanish 	}
17073470Sanish 	mutex_exit(&pcicfg_list_mutex);
17083470Sanish 	/*
17093470Sanish 	 * Did'nt find the entry
17103470Sanish 	 */
17113470Sanish 	return (PCICFG_FAILURE);
17123470Sanish }
17133470Sanish 
17143470Sanish static int
pcicfg_bridge_assign(dev_info_t * dip,void * hdl)17153470Sanish pcicfg_bridge_assign(dev_info_t *dip, void *hdl)
17163470Sanish {
17173470Sanish 	ddi_acc_handle_t handle;
17183470Sanish 	pci_regspec_t *reg;
17193470Sanish 	int length;
17203470Sanish 	int rcount;
17213470Sanish 	int i;
17223470Sanish 	int offset;
17233470Sanish 	uint64_t mem_answer;
17243470Sanish 	uint32_t io_answer;
17253470Sanish 	int count;
17263470Sanish 	uint8_t header_type;
17273470Sanish 	ppb_ranges_t range[PCICFG_RANGE_LEN];
17283470Sanish 	int bus_range[2];
17293470Sanish 	uint64_t mem_residual;
17303470Sanish 	uint64_t pf_mem_residual;
17313470Sanish 	uint64_t io_residual;
17323470Sanish 
17333470Sanish 	pcicfg_phdl_t *entry = (pcicfg_phdl_t *)hdl;
17343470Sanish 
173510923SEvan.Yan@Sun.COM 	DEBUG1("bridge assign: assigning addresses to %s\n", ddi_get_name(dip));
17363470Sanish 
17373470Sanish 	entry->error = PCICFG_SUCCESS;
17383470Sanish 
17393470Sanish 	if (entry == NULL) {
17403470Sanish 		DEBUG0("Failed to get entry\n");
17413470Sanish 		entry->error = PCICFG_FAILURE;
17423470Sanish 		return (DDI_WALK_TERMINATE);
17433470Sanish 	}
17443470Sanish 
174510923SEvan.Yan@Sun.COM 	if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) {
17463470Sanish 		DEBUG0("Failed to map config space!\n");
17473470Sanish 		entry->error = PCICFG_FAILURE;
17483470Sanish 		return (DDI_WALK_TERMINATE);
17493470Sanish 	}
17503470Sanish 
17513470Sanish 	header_type = pci_config_get8(handle, PCI_CONF_HEADER);
17523470Sanish 
17533470Sanish 	if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) {
17543470Sanish 
175510923SEvan.Yan@Sun.COM 		bzero((caddr_t)range, sizeof (ppb_ranges_t) * PCICFG_RANGE_LEN);
17563470Sanish 
17573470Sanish 		(void) pcicfg_setup_bridge(entry, handle);
17583470Sanish 
17593470Sanish 		range[0].child_high = range[0].parent_high |=
176010923SEvan.Yan@Sun.COM 		    (PCI_REG_REL_M | PCI_ADDR_IO);
176110923SEvan.Yan@Sun.COM 		range[0].child_low = range[0].parent_low = entry->io_last;
17623470Sanish 		range[1].child_high = range[1].parent_high |=
176310923SEvan.Yan@Sun.COM 		    (PCI_REG_REL_M | PCI_ADDR_MEM32);
17643470Sanish 		range[1].child_low = range[1].parent_low =
176510923SEvan.Yan@Sun.COM 		    entry->memory_last;
17663470Sanish 		range[2].child_high = range[2].parent_high |=
176710923SEvan.Yan@Sun.COM 		    (PCI_REG_REL_M | PCI_ADDR_MEM32 | PCI_REG_PF_M);
17683470Sanish 		range[2].child_low = range[2].parent_low =
176910923SEvan.Yan@Sun.COM 		    entry->pf_memory_last;
17703470Sanish 
17713470Sanish 		ndi_devi_enter(dip, &count);
17723470Sanish 		ddi_walk_devs(ddi_get_child(dip),
177310923SEvan.Yan@Sun.COM 		    pcicfg_bridge_assign, (void *)entry);
17743470Sanish 		ndi_devi_exit(dip, count);
17753470Sanish 
17763470Sanish 		(void) pcicfg_update_bridge(entry, handle);
17773470Sanish 
17783470Sanish 		bus_range[0] = pci_config_get8(handle, PCI_BCNF_SECBUS);
17793470Sanish 		bus_range[1] = pci_config_get8(handle, PCI_BCNF_SUBBUS);
17803470Sanish 
17813470Sanish 		if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
178210923SEvan.Yan@Sun.COM 		    "bus-range", bus_range, 2) != DDI_SUCCESS) {
17833470Sanish 			DEBUG0("Failed to set bus-range property");
17843470Sanish 			entry->error = PCICFG_FAILURE;
17853735Sprasad 			(void) pcicfg_config_teardown(&handle);
17863470Sanish 			return (DDI_WALK_TERMINATE);
17873470Sanish 		}
17883470Sanish 
17893470Sanish 		/*
17903470Sanish 		 * Put back memory and I/O space not allocated
17913470Sanish 		 * under the bridge.
17923470Sanish 		 */
17933470Sanish 		mem_residual = entry->memory_len -
179410923SEvan.Yan@Sun.COM 		    (entry->memory_last - entry->memory_base);
17953470Sanish 		if (mem_residual > 0) {
17963470Sanish 			(void) ndi_ra_free(ddi_get_parent(dip),
179710923SEvan.Yan@Sun.COM 			    entry->memory_last, mem_residual,
179810923SEvan.Yan@Sun.COM 			    NDI_RA_TYPE_MEM, NDI_RA_PASS);
17993470Sanish 		}
18003470Sanish 
180110923SEvan.Yan@Sun.COM 		io_residual = entry->io_len - (entry->io_last - entry->io_base);
18023470Sanish 		if (io_residual > 0) {
180310923SEvan.Yan@Sun.COM 			(void) ndi_ra_free(ddi_get_parent(dip), entry->io_last,
180410923SEvan.Yan@Sun.COM 			    io_residual, NDI_RA_TYPE_IO, NDI_RA_PASS);
18053470Sanish 		}
18063470Sanish 
18073470Sanish 		pf_mem_residual = entry->pf_memory_len -
180810923SEvan.Yan@Sun.COM 		    (entry->pf_memory_last - entry->pf_memory_base);
18093470Sanish 		if (pf_mem_residual > 0) {
18103470Sanish 			(void) ndi_ra_free(ddi_get_parent(dip),
181110923SEvan.Yan@Sun.COM 			    entry->pf_memory_last, pf_mem_residual,
181210923SEvan.Yan@Sun.COM 			    NDI_RA_TYPE_PCI_PREFETCH_MEM, NDI_RA_PASS);
18133470Sanish 		}
18143470Sanish 
18153470Sanish 		if (entry->io_len > 0) {
18163470Sanish 			range[0].size_low = entry->io_last - entry->io_base;
18173470Sanish 			if (pcicfg_update_ranges_prop(dip, &range[0])) {
18183470Sanish 				DEBUG0("Failed to update ranges (i/o)\n");
18193470Sanish 				entry->error = PCICFG_FAILURE;
18203735Sprasad 				(void) pcicfg_config_teardown(&handle);
18213470Sanish 				return (DDI_WALK_TERMINATE);
18223470Sanish 			}
18233470Sanish 		}
18243470Sanish 		if (entry->memory_len > 0) {
18253470Sanish 			range[1].size_low =
182610923SEvan.Yan@Sun.COM 			    entry->memory_last - entry->memory_base;
18273470Sanish 			if (pcicfg_update_ranges_prop(dip, &range[1])) {
18283470Sanish 				DEBUG0("Failed to update ranges (memory)\n");
18293470Sanish 				entry->error = PCICFG_FAILURE;
18303735Sprasad 				(void) pcicfg_config_teardown(&handle);
18313470Sanish 				return (DDI_WALK_TERMINATE);
18323470Sanish 			}
18333470Sanish 		}
18343470Sanish 		if (entry->pf_memory_len > 0) {
18353470Sanish 			range[2].size_low =
183610923SEvan.Yan@Sun.COM 			    entry->pf_memory_last - entry->pf_memory_base;
18373470Sanish 			if (pcicfg_update_ranges_prop(dip, &range[2])) {
18383470Sanish 				DEBUG0("Failed to update ranges (PF memory)\n");
18393470Sanish 				entry->error = PCICFG_FAILURE;
18403735Sprasad 				(void) pcicfg_config_teardown(&handle);
18413470Sanish 				return (DDI_WALK_TERMINATE);
18423470Sanish 			}
18433470Sanish 		}
18443470Sanish 
18453470Sanish 		(void) pcicfg_device_on(handle);
18463470Sanish 
18473470Sanish 		PCICFG_DUMP_BRIDGE_CONFIG(handle);
18483470Sanish 
18493735Sprasad 		(void) pcicfg_config_teardown(&handle);
18503735Sprasad 
18513470Sanish 		return (DDI_WALK_PRUNECHILD);
18523470Sanish 	}
18533470Sanish 
18543470Sanish 	/*
18553470Sanish 	 * If there is an interrupt pin set program
18563470Sanish 	 * interrupt line with default values.
18573470Sanish 	 */
18583470Sanish 	if (pci_config_get8(handle, PCI_CONF_IPIN)) {
18593470Sanish 		pci_config_put8(handle, PCI_CONF_ILINE, 0xf);
18603470Sanish 	}
18613470Sanish 
18623470Sanish 	/*
18633470Sanish 	 * A single device (under a bridge).
18643470Sanish 	 * For each "reg" property with a length, allocate memory
18653470Sanish 	 * and program the base registers.
18663470Sanish 	 */
186710923SEvan.Yan@Sun.COM 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
186810923SEvan.Yan@Sun.COM 	    (caddr_t)&reg, &length) != DDI_PROP_SUCCESS) {
18693470Sanish 		DEBUG0("Failed to read reg property\n");
18703470Sanish 		entry->error = PCICFG_FAILURE;
18713735Sprasad 		(void) pcicfg_config_teardown(&handle);
18723470Sanish 		return (DDI_WALK_TERMINATE);
18733470Sanish 	}
18743470Sanish 
18753470Sanish 	rcount = length / sizeof (pci_regspec_t);
18763470Sanish 	offset = PCI_CONF_BASE0;
18773470Sanish 	for (i = 0; i < rcount; i++) {
187810923SEvan.Yan@Sun.COM 		if ((reg[i].pci_size_low != 0) || (reg[i].pci_size_hi != 0)) {
18793470Sanish 
18803470Sanish 			offset = PCI_REG_REG_G(reg[i].pci_phys_hi);
18813470Sanish 
18823470Sanish 			switch (PCI_REG_ADDR_G(reg[i].pci_phys_hi)) {
18833470Sanish 			case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
18843470Sanish 
18853470Sanish 				if (reg[i].pci_phys_hi & PCI_REG_PF_M) {
18863470Sanish 					/* allocate prefetchable memory */
18873470Sanish 					pcicfg_get_pf_mem(entry,
18883470Sanish 					    reg[i].pci_size_low, &mem_answer);
18893470Sanish 				} else { /* get non prefetchable memory */
18903470Sanish 					pcicfg_get_mem(entry,
189110923SEvan.Yan@Sun.COM 					    reg[i].pci_size_low, &mem_answer);
18923470Sanish 				}
18933470Sanish 				pci_config_put64(handle, offset, mem_answer);
18943470Sanish 				DEBUG2("REGISTER off %x (64)LO ----> [0x%x]\n",
189510923SEvan.Yan@Sun.COM 				    offset, pci_config_get32(handle, offset));
18963470Sanish 				DEBUG2("REGISTER off %x (64)HI ----> [0x%x]\n",
189710923SEvan.Yan@Sun.COM 				    offset + 4,
189810923SEvan.Yan@Sun.COM 				    pci_config_get32(handle, offset + 4));
18993470Sanish 
19003470Sanish 				reg[i].pci_phys_hi |= PCI_REG_REL_M;
19013470Sanish 				reg[i].pci_phys_low = PCICFG_LOADDR(mem_answer);
190210923SEvan.Yan@Sun.COM 				reg[i].pci_phys_mid = PCICFG_HIADDR(mem_answer);
19033470Sanish 				break;
19043470Sanish 
19053470Sanish 			case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
19063470Sanish 				if (reg[i].pci_phys_hi & PCI_REG_PF_M) {
19073470Sanish 					/* allocate prefetchable memory */
19083470Sanish 					pcicfg_get_pf_mem(entry,
19093470Sanish 					    reg[i].pci_size_low, &mem_answer);
19103470Sanish 				} else {
19113470Sanish 					/* get non prefetchable memory */
19123470Sanish 					pcicfg_get_mem(entry,
191310923SEvan.Yan@Sun.COM 					    reg[i].pci_size_low, &mem_answer);
19143470Sanish 				}
19153470Sanish 
191610923SEvan.Yan@Sun.COM 				pci_config_put32(handle, offset,
191710923SEvan.Yan@Sun.COM 				    (uint32_t)mem_answer);
19183470Sanish 
19193470Sanish 				DEBUG2("REGISTER off %x(32)LO ----> [0x%x]\n",
192010923SEvan.Yan@Sun.COM 				    offset, pci_config_get32(handle, offset));
19213470Sanish 
19223470Sanish 				reg[i].pci_phys_hi |= PCI_REG_REL_M;
19233470Sanish 				reg[i].pci_phys_low = (uint32_t)mem_answer;
19243470Sanish 
19253470Sanish 				break;
19263470Sanish 			case PCI_REG_ADDR_G(PCI_ADDR_IO):
19273470Sanish 				/* allocate I/O space from the allocator */
19283470Sanish 
192910923SEvan.Yan@Sun.COM 				(void) pcicfg_get_io(entry, reg[i].pci_size_low,
193010923SEvan.Yan@Sun.COM 				    &io_answer);
19313470Sanish 				pci_config_put32(handle, offset, io_answer);
19323470Sanish 
19333470Sanish 				DEBUG2("REGISTER off %x (I/O)LO ----> [0x%x]\n",
193410923SEvan.Yan@Sun.COM 				    offset, pci_config_get32(handle, offset));
19353470Sanish 
19363470Sanish 				reg[i].pci_phys_hi |= PCI_REG_REL_M;
19373470Sanish 				reg[i].pci_phys_low = io_answer;
19383470Sanish 
19393470Sanish 				break;
19403470Sanish 			default:
19413470Sanish 				DEBUG0("Unknown register type\n");
19423470Sanish 				kmem_free(reg, length);
19433470Sanish 				(void) pcicfg_config_teardown(&handle);
19443470Sanish 				entry->error = PCICFG_FAILURE;
19453470Sanish 				return (DDI_WALK_TERMINATE);
19463470Sanish 			} /* switch */
19473470Sanish 
19483470Sanish 			/*
19493470Sanish 			 * Now that memory locations are assigned,
19503470Sanish 			 * update the assigned address property.
19513470Sanish 			 */
195210923SEvan.Yan@Sun.COM 			if (pcicfg_update_assigned_prop(dip, &reg[i])
195310923SEvan.Yan@Sun.COM 			    != PCICFG_SUCCESS) {
19543470Sanish 				kmem_free(reg, length);
19553470Sanish 				(void) pcicfg_config_teardown(&handle);
19563470Sanish 				entry->error = PCICFG_FAILURE;
19573470Sanish 				return (DDI_WALK_TERMINATE);
19583470Sanish 			}
19593470Sanish 		}
19603470Sanish 	}
19613470Sanish 	(void) pcicfg_device_on(handle);
19623470Sanish 
19633470Sanish 	PCICFG_DUMP_DEVICE_CONFIG(handle);
19643470Sanish 
19653470Sanish 	(void) pcicfg_config_teardown(&handle);
19663470Sanish 	kmem_free((caddr_t)reg, length);
19673470Sanish 	return (DDI_WALK_CONTINUE);
19683470Sanish }
19693470Sanish 
19703470Sanish static int
pcicfg_device_assign(dev_info_t * dip)19713470Sanish pcicfg_device_assign(dev_info_t *dip)
19723470Sanish {
19733470Sanish 	ddi_acc_handle_t	handle;
19743470Sanish 	pci_regspec_t		*reg;
19753470Sanish 	int			length;
19763470Sanish 	int			rcount;
19773470Sanish 	int			i;
19783470Sanish 	int			offset;
19793470Sanish 	ndi_ra_request_t	request;
19803470Sanish 	uint64_t		answer;
19813470Sanish 	uint64_t		alen;
19823470Sanish 
19833470Sanish 	DEBUG1("%llx now under configuration\n", dip);
19843470Sanish 
19853470Sanish 	/* request.ra_len = PCICFG_ROUND_UP(request.ra_len, PCICFG_IOGRAN); */
19863470Sanish 	if (pcicfg_ntbridge_child(dip) == DDI_SUCCESS) {
19873470Sanish 
19883470Sanish 		return (pcicfg_ntbridge_program_child(dip));
19893470Sanish 	}
19903470Sanish 	/*
19913470Sanish 	 * XXX Failure here should be noted
19923470Sanish 	 */
199310923SEvan.Yan@Sun.COM 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
199410923SEvan.Yan@Sun.COM 	    (caddr_t)&reg, &length) != DDI_PROP_SUCCESS) {
19953470Sanish 		DEBUG0("Failed to read reg property\n");
19963470Sanish 		return (PCICFG_FAILURE);
19973470Sanish 	}
19983470Sanish 
19993470Sanish 	if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) {
20003470Sanish 		DEBUG0("Failed to map config space!\n");
20013735Sprasad 		kmem_free(reg, length);
20023470Sanish 		return (PCICFG_FAILURE);
20033470Sanish 	}
20043470Sanish 
20053470Sanish 	/*
20063470Sanish 	 * A single device
20073470Sanish 	 *
20083470Sanish 	 * For each "reg" property with a length, allocate memory
20093470Sanish 	 * and program the base registers.
20103470Sanish 	 */
20113470Sanish 
20123470Sanish 	/*
20133470Sanish 	 * If there is an interrupt pin set program
20143470Sanish 	 * interrupt line with default values.
20153470Sanish 	 */
20163470Sanish 	if (pci_config_get8(handle, PCI_CONF_IPIN)) {
20173470Sanish 		pci_config_put8(handle, PCI_CONF_ILINE, 0xf);
20183470Sanish 	}
20193470Sanish 
20203470Sanish 	bzero((caddr_t)&request, sizeof (ndi_ra_request_t));
20213470Sanish 
20223470Sanish 	/*
20233470Sanish 	 * Note: Both non-prefetchable and prefetchable memory space
20243470Sanish 	 * allocations are made within 32bit space. Currently, BIOSs
20253470Sanish 	 * allocate device memory for PCI devices within the 32bit space
20263470Sanish 	 * so this will not be a problem.
20273470Sanish 	 */
20283470Sanish 	request.ra_flags |= NDI_RA_ALIGN_SIZE | NDI_RA_ALLOC_BOUNDED;
20293470Sanish 	request.ra_boundbase = 0;
20303470Sanish 	request.ra_boundlen = PCICFG_4GIG_LIMIT;
20313470Sanish 
20323470Sanish 	rcount = length / sizeof (pci_regspec_t);
20333470Sanish 	offset = PCI_CONF_BASE0;
20343470Sanish 	for (i = 0; i < rcount; i++) {
20353470Sanish 		char	*mem_type;
20363470Sanish 
203710923SEvan.Yan@Sun.COM 		if ((reg[i].pci_size_low != 0)|| (reg[i].pci_size_hi != 0)) {
20383470Sanish 
20393470Sanish 			offset = PCI_REG_REG_G(reg[i].pci_phys_hi);
20403470Sanish 			request.ra_len = reg[i].pci_size_low;
20413470Sanish 
20423470Sanish 			switch (PCI_REG_ADDR_G(reg[i].pci_phys_hi)) {
20433470Sanish 			case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
20443470Sanish 				if (reg[i].pci_phys_hi & PCI_REG_PF_M) {
204510923SEvan.Yan@Sun.COM 					mem_type = NDI_RA_TYPE_PCI_PREFETCH_MEM;
20463470Sanish 				} else {
204710923SEvan.Yan@Sun.COM 					mem_type = NDI_RA_TYPE_MEM;
20483470Sanish 				}
20493470Sanish 				/* allocate memory space from the allocator */
205010923SEvan.Yan@Sun.COM 				if (ndi_ra_alloc(ddi_get_parent(dip), &request,
205110923SEvan.Yan@Sun.COM 				    &answer, &alen, mem_type, NDI_RA_PASS)
205210923SEvan.Yan@Sun.COM 				    != NDI_SUCCESS) {
20533470Sanish 					DEBUG0("Failed to allocate 64b mem\n");
20543470Sanish 					kmem_free(reg, length);
20553470Sanish 					(void) pcicfg_config_teardown(&handle);
20565149Sjveta 					return (PCICFG_NORESRC);
20573470Sanish 				}
20583470Sanish 				DEBUG3("64 addr = [0x%x.0x%x] len [0x%x]\n",
205910923SEvan.Yan@Sun.COM 				    PCICFG_HIADDR(answer),
206010923SEvan.Yan@Sun.COM 				    PCICFG_LOADDR(answer), alen);
20613470Sanish 				/* program the low word */
206210923SEvan.Yan@Sun.COM 				pci_config_put32(handle, offset,
206310923SEvan.Yan@Sun.COM 				    PCICFG_LOADDR(answer));
20643470Sanish 				/* program the high word */
20653470Sanish 				pci_config_put32(handle, offset + 4,
206610923SEvan.Yan@Sun.COM 				    PCICFG_HIADDR(answer));
20673470Sanish 
20683470Sanish 				reg[i].pci_phys_hi |= PCI_REG_REL_M;
20693470Sanish 				reg[i].pci_phys_low = PCICFG_LOADDR(answer);
20703470Sanish 				reg[i].pci_phys_mid = PCICFG_HIADDR(answer);
20713470Sanish 				/*
20723470Sanish 				 * currently support 32b address space
20733470Sanish 				 * assignments only.
20743470Sanish 				 */
207510923SEvan.Yan@Sun.COM 				reg[i].pci_phys_hi ^=
207610923SEvan.Yan@Sun.COM 				    PCI_ADDR_MEM64 ^ PCI_ADDR_MEM32;
20773470Sanish 
20783470Sanish 				offset += 8;
20793470Sanish 				break;
20803470Sanish 
20813470Sanish 			case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
20823470Sanish 				if (reg[i].pci_phys_hi & PCI_REG_PF_M)
20833470Sanish 					mem_type = NDI_RA_TYPE_PCI_PREFETCH_MEM;
20843470Sanish 				else
20853470Sanish 					mem_type = NDI_RA_TYPE_MEM;
20863470Sanish 				/* allocate memory space from the allocator */
208710923SEvan.Yan@Sun.COM 				if (ndi_ra_alloc(ddi_get_parent(dip), &request,
208810923SEvan.Yan@Sun.COM 				    &answer, &alen, mem_type, NDI_RA_PASS)
208910923SEvan.Yan@Sun.COM 				    != NDI_SUCCESS) {
20903470Sanish 					DEBUG0("Failed to allocate 32b mem\n");
20913470Sanish 					kmem_free(reg, length);
20923470Sanish 					(void) pcicfg_config_teardown(&handle);
20935149Sjveta 					return (PCICFG_NORESRC);
20943470Sanish 				}
20953470Sanish 				DEBUG3("32 addr = [0x%x.0x%x] len [0x%x]\n",
209610923SEvan.Yan@Sun.COM 				    PCICFG_HIADDR(answer),
209710923SEvan.Yan@Sun.COM 				    PCICFG_LOADDR(answer),
209810923SEvan.Yan@Sun.COM 				    alen);
20993470Sanish 				/* program the low word */
210010923SEvan.Yan@Sun.COM 				pci_config_put32(handle, offset,
210110923SEvan.Yan@Sun.COM 				    PCICFG_LOADDR(answer));
21023470Sanish 
21033470Sanish 				reg[i].pci_phys_hi |= PCI_REG_REL_M;
21043470Sanish 				reg[i].pci_phys_low = PCICFG_LOADDR(answer);
21053470Sanish 				reg[i].pci_phys_mid = 0;
21063470Sanish 
21073470Sanish 				offset += 4;
21083470Sanish 				break;
21093470Sanish 			case PCI_REG_ADDR_G(PCI_ADDR_IO):
211011437SLipeng.Sang@Sun.COM 				/*
211111437SLipeng.Sang@Sun.COM 				 * Try to allocate I/O space. If it fails,
211211437SLipeng.Sang@Sun.COM 				 * continue here instead of returning failure
211311437SLipeng.Sang@Sun.COM 				 * so that the hotplug for drivers that don't
211411437SLipeng.Sang@Sun.COM 				 * use I/O space can succeed, For drivers
211511437SLipeng.Sang@Sun.COM 				 * that need to use I/O space, the hotplug
211611437SLipeng.Sang@Sun.COM 				 * will still fail later during driver attach.
211711437SLipeng.Sang@Sun.COM 				 */
211810923SEvan.Yan@Sun.COM 				if (ndi_ra_alloc(ddi_get_parent(dip), &request,
211910923SEvan.Yan@Sun.COM 				    &answer, &alen, NDI_RA_TYPE_IO, NDI_RA_PASS)
212010923SEvan.Yan@Sun.COM 				    != NDI_SUCCESS) {
21213470Sanish 					DEBUG0("Failed to allocate I/O\n");
212211437SLipeng.Sang@Sun.COM 					continue;
21233470Sanish 				}
21243470Sanish 				DEBUG3("I/O addr = [0x%x.0x%x] len [0x%x]\n",
212510923SEvan.Yan@Sun.COM 				    PCICFG_HIADDR(answer),
212610923SEvan.Yan@Sun.COM 				    PCICFG_LOADDR(answer), alen);
212710923SEvan.Yan@Sun.COM 				pci_config_put32(handle, offset,
212810923SEvan.Yan@Sun.COM 				    PCICFG_LOADDR(answer));
21293470Sanish 
21303470Sanish 				reg[i].pci_phys_hi |= PCI_REG_REL_M;
21313470Sanish 				reg[i].pci_phys_low = PCICFG_LOADDR(answer);
21323470Sanish 
21333470Sanish 				offset += 4;
21343470Sanish 				break;
21353470Sanish 			default:
21363470Sanish 				DEBUG0("Unknown register type\n");
21373470Sanish 				kmem_free(reg, length);
21383470Sanish 				(void) pcicfg_config_teardown(&handle);
21393470Sanish 				return (PCICFG_FAILURE);
21403470Sanish 			} /* switch */
21413470Sanish 
21423470Sanish 			/*
21433470Sanish 			 * Now that memory locations are assigned,
21443470Sanish 			 * update the assigned address property.
21453470Sanish 			 */
21463470Sanish 
214710923SEvan.Yan@Sun.COM 			if (pcicfg_update_assigned_prop(dip, &reg[i])
214810923SEvan.Yan@Sun.COM 			    != PCICFG_SUCCESS) {
21493470Sanish 				kmem_free(reg, length);
21503470Sanish 				(void) pcicfg_config_teardown(&handle);
21513470Sanish 				return (PCICFG_FAILURE);
21523470Sanish 			}
21533470Sanish 		}
21543470Sanish 	}
21553470Sanish 
21563470Sanish 	(void) pcicfg_device_on(handle);
21573470Sanish 	kmem_free(reg, length);
21583470Sanish 
21593470Sanish 	PCICFG_DUMP_DEVICE_CONFIG(handle);
21603470Sanish 
21613470Sanish 	(void) pcicfg_config_teardown(&handle);
21623470Sanish 	return (PCICFG_SUCCESS);
21633470Sanish }
21643470Sanish 
216510923SEvan.Yan@Sun.COM static int
pcicfg_device_assign_readonly(dev_info_t * dip)216610923SEvan.Yan@Sun.COM pcicfg_device_assign_readonly(dev_info_t *dip)
216710923SEvan.Yan@Sun.COM {
216810923SEvan.Yan@Sun.COM 	ddi_acc_handle_t	handle;
216910923SEvan.Yan@Sun.COM 	pci_regspec_t		*assigned;
217010923SEvan.Yan@Sun.COM 	int			length;
217110923SEvan.Yan@Sun.COM 	int			acount;
217210923SEvan.Yan@Sun.COM 	int			i;
217310923SEvan.Yan@Sun.COM 	ndi_ra_request_t	request;
217410923SEvan.Yan@Sun.COM 	uint64_t		answer;
217510923SEvan.Yan@Sun.COM 	uint64_t		alen;
217610923SEvan.Yan@Sun.COM 
217710923SEvan.Yan@Sun.COM 	DEBUG1("%llx now under configuration\n", dip);
217810923SEvan.Yan@Sun.COM 
217910923SEvan.Yan@Sun.COM 	/*
218010923SEvan.Yan@Sun.COM 	 * we don't support ntbridges for readonly probe.
218110923SEvan.Yan@Sun.COM 	 */
218210923SEvan.Yan@Sun.COM 	if (pcicfg_ntbridge_child(dip) == DDI_SUCCESS) {
218310923SEvan.Yan@Sun.COM 		return (PCICFG_FAILURE);
218410923SEvan.Yan@Sun.COM 	}
218510923SEvan.Yan@Sun.COM 
218610923SEvan.Yan@Sun.COM 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
218710923SEvan.Yan@Sun.COM 	    DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned,
218810923SEvan.Yan@Sun.COM 	    &length) != DDI_PROP_SUCCESS) {
218910923SEvan.Yan@Sun.COM 		DEBUG0("Failed to read assigned-addresses property\n");
219010923SEvan.Yan@Sun.COM 		return (PCICFG_FAILURE);
219110923SEvan.Yan@Sun.COM 	}
219210923SEvan.Yan@Sun.COM 
219310923SEvan.Yan@Sun.COM 	if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) {
219410923SEvan.Yan@Sun.COM 		DEBUG0("Failed to map config space!\n");
219510923SEvan.Yan@Sun.COM 		kmem_free(assigned, length);
219610923SEvan.Yan@Sun.COM 		return (PCICFG_FAILURE);
219710923SEvan.Yan@Sun.COM 	}
219810923SEvan.Yan@Sun.COM 
219910923SEvan.Yan@Sun.COM 	/*
220010923SEvan.Yan@Sun.COM 	 * If there is an interrupt pin set program
220110923SEvan.Yan@Sun.COM 	 * interrupt line with default values.
220210923SEvan.Yan@Sun.COM 	 */
220310923SEvan.Yan@Sun.COM 	if (pci_config_get8(handle, PCI_CONF_IPIN)) {
220410923SEvan.Yan@Sun.COM 		pci_config_put8(handle, PCI_CONF_ILINE, 0xf);
220510923SEvan.Yan@Sun.COM 	}
220610923SEvan.Yan@Sun.COM 	/*
220710923SEvan.Yan@Sun.COM 	 * Note: Both non-prefetchable and prefetchable memory space
220810923SEvan.Yan@Sun.COM 	 * allocations are made within 32bit space. Currently, BIOSs
220910923SEvan.Yan@Sun.COM 	 * allocate device memory for PCI devices within the 32bit space
221010923SEvan.Yan@Sun.COM 	 * so this will not be a problem.
221110923SEvan.Yan@Sun.COM 	 */
221210923SEvan.Yan@Sun.COM 	bzero((caddr_t)&request, sizeof (ndi_ra_request_t));
221310923SEvan.Yan@Sun.COM 
221410923SEvan.Yan@Sun.COM 	request.ra_flags = NDI_RA_ALLOC_SPECIFIED;  /* specified addr */
221510923SEvan.Yan@Sun.COM 	request.ra_boundbase = 0;
221610923SEvan.Yan@Sun.COM 	request.ra_boundlen = PCICFG_4GIG_LIMIT;
221710923SEvan.Yan@Sun.COM 
221810923SEvan.Yan@Sun.COM 	acount = length / sizeof (pci_regspec_t);
221910923SEvan.Yan@Sun.COM 	for (i = 0; i < acount; i++) {
222010923SEvan.Yan@Sun.COM 		char	*mem_type;
222110923SEvan.Yan@Sun.COM 
222210923SEvan.Yan@Sun.COM 		if ((assigned[i].pci_size_low != 0)||
222310923SEvan.Yan@Sun.COM 		    (assigned[i].pci_size_hi != 0)) {
222410923SEvan.Yan@Sun.COM 
222510923SEvan.Yan@Sun.COM 			request.ra_len = assigned[i].pci_size_low;
222610923SEvan.Yan@Sun.COM 
222710923SEvan.Yan@Sun.COM 			switch (PCI_REG_ADDR_G(assigned[i].pci_phys_hi)) {
222810923SEvan.Yan@Sun.COM 			case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
222910923SEvan.Yan@Sun.COM 				request.ra_addr = (uint64_t)PCICFG_LADDR(
223010923SEvan.Yan@Sun.COM 				    assigned[i].pci_phys_low,
223110923SEvan.Yan@Sun.COM 				    assigned[i].pci_phys_mid);
223210923SEvan.Yan@Sun.COM 
223310923SEvan.Yan@Sun.COM 				if (assigned[i].pci_phys_hi & PCI_REG_PF_M) {
223410923SEvan.Yan@Sun.COM 					mem_type = NDI_RA_TYPE_PCI_PREFETCH_MEM;
223510923SEvan.Yan@Sun.COM 				} else {
223610923SEvan.Yan@Sun.COM 					mem_type = NDI_RA_TYPE_MEM;
223710923SEvan.Yan@Sun.COM 				}
223810923SEvan.Yan@Sun.COM 				/* allocate memory space from the allocator */
223910923SEvan.Yan@Sun.COM 				if (ndi_ra_alloc(ddi_get_parent(dip), &request,
224010923SEvan.Yan@Sun.COM 				    &answer, &alen, mem_type, NDI_RA_PASS)
224110923SEvan.Yan@Sun.COM 				    != NDI_SUCCESS) {
224210923SEvan.Yan@Sun.COM 					DEBUG0("Failed to allocate 64b mem\n");
224310923SEvan.Yan@Sun.COM 					kmem_free(assigned, length);
224410923SEvan.Yan@Sun.COM 					return (PCICFG_NORESRC);
224510923SEvan.Yan@Sun.COM 				}
224610923SEvan.Yan@Sun.COM 
224710923SEvan.Yan@Sun.COM 				break;
224810923SEvan.Yan@Sun.COM 			case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
224910923SEvan.Yan@Sun.COM 				request.ra_addr = (uint64_t)
225010923SEvan.Yan@Sun.COM 				    assigned[i].pci_phys_low;
225110923SEvan.Yan@Sun.COM 
225210923SEvan.Yan@Sun.COM 				if (assigned[i].pci_phys_hi & PCI_REG_PF_M)
225310923SEvan.Yan@Sun.COM 					mem_type = NDI_RA_TYPE_PCI_PREFETCH_MEM;
225410923SEvan.Yan@Sun.COM 				else
225510923SEvan.Yan@Sun.COM 					mem_type = NDI_RA_TYPE_MEM;
225610923SEvan.Yan@Sun.COM 				/* allocate memory space from the allocator */
225710923SEvan.Yan@Sun.COM 				if (ndi_ra_alloc(ddi_get_parent(dip), &request,
225810923SEvan.Yan@Sun.COM 				    &answer, &alen, mem_type, NDI_RA_PASS)
225910923SEvan.Yan@Sun.COM 				    != NDI_SUCCESS) {
226010923SEvan.Yan@Sun.COM 					DEBUG0("Failed to allocate 32b mem\n");
226110923SEvan.Yan@Sun.COM 					kmem_free(assigned, length);
226210923SEvan.Yan@Sun.COM 					return (PCICFG_NORESRC);
226310923SEvan.Yan@Sun.COM 				}
226410923SEvan.Yan@Sun.COM 
226510923SEvan.Yan@Sun.COM 				break;
226610923SEvan.Yan@Sun.COM 			case PCI_REG_ADDR_G(PCI_ADDR_IO):
226710923SEvan.Yan@Sun.COM 				request.ra_addr = (uint64_t)
226810923SEvan.Yan@Sun.COM 				    assigned[i].pci_phys_low;
226910923SEvan.Yan@Sun.COM 
227010923SEvan.Yan@Sun.COM 				/* allocate I/O space from the allocator */
227110923SEvan.Yan@Sun.COM 				if (ndi_ra_alloc(ddi_get_parent(dip), &request,
227210923SEvan.Yan@Sun.COM 				    &answer, &alen, NDI_RA_TYPE_IO, NDI_RA_PASS)
227310923SEvan.Yan@Sun.COM 				    != NDI_SUCCESS) {
227410923SEvan.Yan@Sun.COM 					DEBUG0("Failed to allocate I/O\n");
227510923SEvan.Yan@Sun.COM 					kmem_free(assigned, length);
227610923SEvan.Yan@Sun.COM 					return (PCICFG_NORESRC);
227710923SEvan.Yan@Sun.COM 				}
227810923SEvan.Yan@Sun.COM 
227910923SEvan.Yan@Sun.COM 				break;
228010923SEvan.Yan@Sun.COM 			default:
228110923SEvan.Yan@Sun.COM 				DEBUG0("Unknown register type\n");
228210923SEvan.Yan@Sun.COM 				kmem_free(assigned, length);
228310923SEvan.Yan@Sun.COM 				return (PCICFG_FAILURE);
228410923SEvan.Yan@Sun.COM 			} /* switch */
228510923SEvan.Yan@Sun.COM 		}
228610923SEvan.Yan@Sun.COM 	}
228710923SEvan.Yan@Sun.COM 
228810923SEvan.Yan@Sun.COM 	(void) pcicfg_device_on(handle);
228910923SEvan.Yan@Sun.COM 	kmem_free(assigned, length);
229010923SEvan.Yan@Sun.COM 
229110923SEvan.Yan@Sun.COM 	PCICFG_DUMP_DEVICE_CONFIG(handle);
229210923SEvan.Yan@Sun.COM 
229310923SEvan.Yan@Sun.COM 	(void) pcicfg_config_teardown(&handle);
229410923SEvan.Yan@Sun.COM 	return (PCICFG_SUCCESS);
229510923SEvan.Yan@Sun.COM }
229610923SEvan.Yan@Sun.COM 
22973470Sanish #ifdef	DEBUG
22983470Sanish /*
22993470Sanish  * This function is useful in debug mode, where we can measure how
23003470Sanish  * much memory was wasted/unallocated in bridge device's domain.
23013470Sanish  */
23023470Sanish static uint64_t
pcicfg_unused_space(hole_t * hole,uint32_t * hole_count)23033470Sanish pcicfg_unused_space(hole_t *hole, uint32_t *hole_count)
23043470Sanish {
23053470Sanish 	uint64_t len = 0;
23063470Sanish 	uint32_t count = 0;
23073470Sanish 
23083470Sanish 	do {
23093470Sanish 		len += hole->len;
23103470Sanish 		hole = hole->next;
23113470Sanish 		count++;
23123470Sanish 	} while (hole);
23133470Sanish 	*hole_count = count;
23143470Sanish 	return (len);
23153470Sanish }
23163470Sanish #endif
23173470Sanish 
23183470Sanish /*
23193470Sanish  * This function frees data structures that hold the hole information
23203470Sanish  * which are allocated in pcicfg_alloc_hole(). This is not freeing
23213470Sanish  * any memory allocated through NDI calls.
23223470Sanish  */
23233470Sanish static void
pcicfg_free_hole(hole_t * addr_hole)23243470Sanish pcicfg_free_hole(hole_t *addr_hole)
23253470Sanish {
23263470Sanish 	hole_t *nhole, *hole = addr_hole->next;
23273470Sanish 
23283470Sanish 	while (hole) {
23293470Sanish 		nhole = hole->next;
23303470Sanish 		kmem_free(hole, sizeof (hole_t));
23313470Sanish 		hole = nhole;
23323470Sanish 	}
23333470Sanish }
23343470Sanish 
23353470Sanish static uint64_t
pcicfg_alloc_hole(hole_t * addr_hole,uint64_t * alast,uint32_t length)23363470Sanish pcicfg_alloc_hole(hole_t *addr_hole, uint64_t *alast, uint32_t length)
23373470Sanish {
23383470Sanish 	uint64_t actual_hole_start, ostart, olen;
23393470Sanish 	hole_t	*hole = addr_hole, *thole, *nhole;
23403470Sanish 
23413470Sanish 	do {
23423470Sanish 		actual_hole_start = PCICFG_ROUND_UP(hole->start, length);
23433470Sanish 		if (((actual_hole_start - hole->start) + length) <= hole->len) {
23443470Sanish 			DEBUG3("hole found. start %llx, len %llx, req=0x%x\n",
234510923SEvan.Yan@Sun.COM 			    hole->start, hole->len, length);
23463470Sanish 			ostart = hole->start;
23473470Sanish 			olen = hole->len;
23483470Sanish 			/* current hole parameters adjust */
23493470Sanish 			if ((actual_hole_start - hole->start) == 0) {
23503470Sanish 				hole->start += length;
23513470Sanish 				hole->len -= length;
23523470Sanish 				if (hole->start > *alast)
23533470Sanish 					*alast = hole->start;
23543470Sanish 			} else {
23553470Sanish 				hole->len = actual_hole_start - hole->start;
23563470Sanish 				nhole = (hole_t *)kmem_zalloc(sizeof (hole_t),
235710923SEvan.Yan@Sun.COM 				    KM_SLEEP);
23583470Sanish 				nhole->start = actual_hole_start + length;
23593470Sanish 				nhole->len = (ostart + olen) - nhole->start;
23603470Sanish 				nhole->next = NULL;
23613470Sanish 				thole = hole->next;
23623470Sanish 				hole->next = nhole;
23633470Sanish 				nhole->next = thole;
23643470Sanish 				if (nhole->start > *alast)
23653470Sanish 					*alast = nhole->start;
23663470Sanish 				DEBUG2("put new hole to %llx, %llx\n",
236710923SEvan.Yan@Sun.COM 				    nhole->start, nhole->len);
23683470Sanish 			}
23693470Sanish 			DEBUG2("adjust current hole to %llx, %llx\n",
237010923SEvan.Yan@Sun.COM 			    hole->start, hole->len);
23713470Sanish 			break;
23723470Sanish 		}
23733470Sanish 		actual_hole_start = 0;
23743470Sanish 		hole = hole->next;
23753470Sanish 	} while (hole);
23763470Sanish 
23773470Sanish 	DEBUG1("return hole at %llx\n", actual_hole_start);
23783470Sanish 	return (actual_hole_start);
23793470Sanish }
23803470Sanish 
23813470Sanish static void
pcicfg_get_mem(pcicfg_phdl_t * entry,uint32_t length,uint64_t * ans)238210923SEvan.Yan@Sun.COM pcicfg_get_mem(pcicfg_phdl_t *entry, uint32_t length, uint64_t *ans)
23833470Sanish {
23843470Sanish 	uint64_t new_mem;
23853470Sanish 
23863470Sanish 	/* See if there is a hole, that can hold this request. */
23873470Sanish 	new_mem = pcicfg_alloc_hole(&entry->mem_hole, &entry->memory_last,
238810923SEvan.Yan@Sun.COM 	    length);
23893470Sanish 	if (new_mem) {	/* if non-zero, found a hole. */
23903470Sanish 		if (ans != NULL)
23913470Sanish 			*ans = new_mem;
23923470Sanish 	} else
23933470Sanish 		cmn_err(CE_WARN, "No %u bytes memory window for %s\n",
239410923SEvan.Yan@Sun.COM 		    length, ddi_get_name(entry->dip));
23953470Sanish }
23963470Sanish 
23973470Sanish static void
pcicfg_get_io(pcicfg_phdl_t * entry,uint32_t length,uint32_t * ans)23983470Sanish pcicfg_get_io(pcicfg_phdl_t *entry,
23993470Sanish 	uint32_t length, uint32_t *ans)
24003470Sanish {
24013470Sanish 	uint32_t new_io;
24023470Sanish 	uint64_t io_last;
24033470Sanish 
24043470Sanish 	/*
24053470Sanish 	 * See if there is a hole, that can hold this request.
24063470Sanish 	 * Pass 64 bit parameters and then truncate to 32 bit.
24073470Sanish 	 */
24083470Sanish 	io_last = entry->io_last;
24093470Sanish 	new_io = (uint32_t)pcicfg_alloc_hole(&entry->io_hole, &io_last, length);
24103470Sanish 	if (new_io) {	/* if non-zero, found a hole. */
24113470Sanish 		entry->io_last = (uint32_t)io_last;
24123470Sanish 		if (ans != NULL)
24133470Sanish 			*ans = new_io;
24143470Sanish 	} else
24153470Sanish 		cmn_err(CE_WARN, "No %u bytes IO space window for %s\n",
241610923SEvan.Yan@Sun.COM 		    length, ddi_get_name(entry->dip));
24173470Sanish }
24183470Sanish 
24193470Sanish static void
pcicfg_get_pf_mem(pcicfg_phdl_t * entry,uint32_t length,uint64_t * ans)242010923SEvan.Yan@Sun.COM pcicfg_get_pf_mem(pcicfg_phdl_t *entry, uint32_t length, uint64_t *ans)
24213470Sanish {
24223470Sanish 	uint64_t new_mem;
24233470Sanish 
24243470Sanish 	/* See if there is a hole, that can hold this request. */
24253470Sanish 	new_mem = pcicfg_alloc_hole(&entry->pf_mem_hole, &entry->pf_memory_last,
242610923SEvan.Yan@Sun.COM 	    length);
24273470Sanish 	if (new_mem) {	/* if non-zero, found a hole. */
24283470Sanish 		if (ans != NULL)
24293470Sanish 			*ans = new_mem;
24303470Sanish 	} else
24313470Sanish 		cmn_err(CE_WARN, "No %u bytes PF memory window for %s\n",
243210923SEvan.Yan@Sun.COM 		    length, ddi_get_name(entry->dip));
24333470Sanish }
24343470Sanish 
24353470Sanish static int
pcicfg_sum_resources(dev_info_t * dip,void * hdl)24363470Sanish pcicfg_sum_resources(dev_info_t *dip, void *hdl)
24373470Sanish {
24383470Sanish 	pcicfg_phdl_t *entry = (pcicfg_phdl_t *)hdl;
24393470Sanish 	pci_regspec_t *pci_rp;
24403470Sanish 	int length;
24413470Sanish 	int rcount;
24423470Sanish 	int i;
24433470Sanish 	ndi_ra_request_t *pf_mem_request;
24443470Sanish 	ndi_ra_request_t *mem_request;
24453470Sanish 	ndi_ra_request_t *io_request;
24463470Sanish 	uint8_t header_type;
24473470Sanish 	ddi_acc_handle_t handle;
24483470Sanish 
24493470Sanish 	entry->error = PCICFG_SUCCESS;
24503470Sanish 
24513470Sanish 	pf_mem_request = &entry->pf_mem_req;
24523470Sanish 	mem_request = &entry->mem_req;
24533470Sanish 	io_request =  &entry->io_req;
24543470Sanish 
24553470Sanish 	if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) {
24563470Sanish 		DEBUG0("Failed to map config space!\n");
24573470Sanish 		entry->error = PCICFG_FAILURE;
24583470Sanish 		return (DDI_WALK_TERMINATE);
24593470Sanish 	}
24603470Sanish 
24613470Sanish 	header_type = pci_config_get8(handle, PCI_CONF_HEADER);
24623470Sanish 
24633470Sanish 	/*
24643470Sanish 	 * If its a bridge - just record the highest bus seen
24653470Sanish 	 */
24663470Sanish 	if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) {
24673470Sanish 
24683470Sanish 		if (entry->highest_bus < pci_config_get8(handle,
246910923SEvan.Yan@Sun.COM 		    PCI_BCNF_SECBUS)) {
24703470Sanish 			entry->highest_bus =
247110923SEvan.Yan@Sun.COM 			    pci_config_get8(handle, PCI_BCNF_SECBUS);
24723470Sanish 		}
24733470Sanish 		(void) pcicfg_config_teardown(&handle);
24743470Sanish 		entry->error = PCICFG_FAILURE;
24753470Sanish 		return (DDI_WALK_CONTINUE);
24763470Sanish 	} else {
247710923SEvan.Yan@Sun.COM 		if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
247810923SEvan.Yan@Sun.COM 		    "reg", (caddr_t)&pci_rp, &length) != DDI_PROP_SUCCESS) {
24793470Sanish 			/*
24803470Sanish 			 * If one node in (the subtree of nodes)
24813470Sanish 			 * doesn't have a "reg" property fail the
24823470Sanish 			 * allocation.
24833470Sanish 			 */
24843470Sanish 			entry->memory_len = 0;
24853470Sanish 			entry->io_len = 0;
24863470Sanish 			entry->pf_memory_len = 0;
24873470Sanish 			entry->error = PCICFG_FAILURE;
24883735Sprasad 			(void) pcicfg_config_teardown(&handle);
24893470Sanish 			return (DDI_WALK_TERMINATE);
24903470Sanish 		}
24913470Sanish 		/*
24923470Sanish 		 * For each "reg" property with a length, add that to the
24933470Sanish 		 * total memory (or I/O) to allocate.
24943470Sanish 		 */
24953470Sanish 		rcount = length / sizeof (pci_regspec_t);
24963470Sanish 
24973470Sanish 		for (i = 0; i < rcount; i++) {
24983470Sanish 
24993470Sanish 			switch (PCI_REG_ADDR_G(pci_rp[i].pci_phys_hi)) {
25003470Sanish 
25013470Sanish 			case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
25023470Sanish 				if (pci_rp[i].pci_phys_hi & PCI_REG_PF_M) {
250310923SEvan.Yan@Sun.COM 					pf_mem_request->ra_len =
250410923SEvan.Yan@Sun.COM 					    pci_rp[i].pci_size_low +
250510923SEvan.Yan@Sun.COM 					    PCICFG_ROUND_UP(
250610923SEvan.Yan@Sun.COM 					    pf_mem_request->ra_len,
250710923SEvan.Yan@Sun.COM 					    pci_rp[i].pci_size_low);
250810923SEvan.Yan@Sun.COM 					DEBUG1("ADDING 32 --->0x%x\n",
250910923SEvan.Yan@Sun.COM 					    pci_rp[i].pci_size_low);
25103470Sanish 				} else {
251110923SEvan.Yan@Sun.COM 					mem_request->ra_len =
251210923SEvan.Yan@Sun.COM 					    pci_rp[i].pci_size_low +
251310923SEvan.Yan@Sun.COM 					    PCICFG_ROUND_UP(mem_request->ra_len,
251410923SEvan.Yan@Sun.COM 					    pci_rp[i].pci_size_low);
251510923SEvan.Yan@Sun.COM 					DEBUG1("ADDING 32 --->0x%x\n",
251610923SEvan.Yan@Sun.COM 					    pci_rp[i].pci_size_low);
25173470Sanish 				}
25183470Sanish 
251910923SEvan.Yan@Sun.COM 				break;
25203470Sanish 			case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
25213470Sanish 				if (pci_rp[i].pci_phys_hi & PCI_REG_PF_M) {
252210923SEvan.Yan@Sun.COM 					pf_mem_request->ra_len =
252310923SEvan.Yan@Sun.COM 					    pci_rp[i].pci_size_low +
252410923SEvan.Yan@Sun.COM 					    PCICFG_ROUND_UP(
252510923SEvan.Yan@Sun.COM 					    pf_mem_request->ra_len,
252610923SEvan.Yan@Sun.COM 					    pci_rp[i].pci_size_low);
252710923SEvan.Yan@Sun.COM 					DEBUG1("ADDING 64 --->0x%x\n",
252810923SEvan.Yan@Sun.COM 					    pci_rp[i].pci_size_low);
25293470Sanish 				} else {
253010923SEvan.Yan@Sun.COM 					mem_request->ra_len =
253110923SEvan.Yan@Sun.COM 					    pci_rp[i].pci_size_low +
253210923SEvan.Yan@Sun.COM 					    PCICFG_ROUND_UP(mem_request->ra_len,
253310923SEvan.Yan@Sun.COM 					    pci_rp[i].pci_size_low);
253410923SEvan.Yan@Sun.COM 					DEBUG1("ADDING 64 --->0x%x\n",
253510923SEvan.Yan@Sun.COM 					    pci_rp[i].pci_size_low);
25363470Sanish 				}
25373470Sanish 
253810923SEvan.Yan@Sun.COM 				break;
25393470Sanish 			case PCI_REG_ADDR_G(PCI_ADDR_IO):
25403470Sanish 				io_request->ra_len =
254110923SEvan.Yan@Sun.COM 				    pci_rp[i].pci_size_low +
254210923SEvan.Yan@Sun.COM 				    PCICFG_ROUND_UP(io_request->ra_len,
254310923SEvan.Yan@Sun.COM 				    pci_rp[i].pci_size_low);
25443470Sanish 				DEBUG1("ADDING I/O --->0x%x\n",
254510923SEvan.Yan@Sun.COM 				    pci_rp[i].pci_size_low);
254610923SEvan.Yan@Sun.COM 				break;
25473470Sanish 			default:
254810923SEvan.Yan@Sun.COM 				/* Config space register - not included */
254910923SEvan.Yan@Sun.COM 				break;
25503470Sanish 			}
25513470Sanish 		}
25523470Sanish 
25533470Sanish 		/*
25543470Sanish 		 * free the memory allocated by ddi_getlongprop
25553470Sanish 		 */
25563470Sanish 		kmem_free(pci_rp, length);
25573470Sanish 
25583470Sanish 		/*
25593470Sanish 		 * continue the walk to the next sibling to sum memory
25603470Sanish 		 */
25613470Sanish 
25623470Sanish 		(void) pcicfg_config_teardown(&handle);
25633470Sanish 
25643470Sanish 		return (DDI_WALK_CONTINUE);
25653470Sanish 	}
25663470Sanish }
25673470Sanish 
25683470Sanish static int
pcicfg_free_bridge_resources(dev_info_t * dip)25693470Sanish pcicfg_free_bridge_resources(dev_info_t *dip)
25703470Sanish {
25713470Sanish 	ppb_ranges_t		*ranges;
25723470Sanish 	uint_t			*bus;
25733470Sanish 	int			k;
25743735Sprasad 	int			length = 0;
25753470Sanish 	int			i;
25763470Sanish 
25773470Sanish 
257810923SEvan.Yan@Sun.COM 	if ((i = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
257910923SEvan.Yan@Sun.COM 	    "ranges", (caddr_t)&ranges, &length)) != DDI_PROP_SUCCESS) {
25803470Sanish 		DEBUG0("Failed to read ranges property\n");
25813470Sanish 		if (ddi_get_child(dip)) {
25823470Sanish 			cmn_err(CE_WARN, "No ranges property found for %s",
258310923SEvan.Yan@Sun.COM 			    ddi_get_name(dip));
25843470Sanish 			/*
25853470Sanish 			 * strictly speaking, we can check for children with
25863470Sanish 			 * assigned-addresses but for now it is better to
25873470Sanish 			 * be conservative and assume that if there are child
25883470Sanish 			 * nodes, then they do consume PCI memory or IO
25893470Sanish 			 * resources, Hence return failure.
25903470Sanish 			 */
25913470Sanish 			return (PCICFG_FAILURE);
25923470Sanish 		}
25933470Sanish 		length = 0;
25943470Sanish 	}
25953470Sanish 
25963470Sanish 	for (i = 0; i < length / sizeof (ppb_ranges_t); i++) {
25973470Sanish 		char *mem_type;
25983470Sanish 
259910923SEvan.Yan@Sun.COM 		if (ranges[i].size_low != 0 || ranges[i].size_high != 0) {
26003470Sanish 			switch (ranges[i].parent_high & PCI_REG_ADDR_M) {
260110923SEvan.Yan@Sun.COM 			case PCI_ADDR_IO:
260210923SEvan.Yan@Sun.COM 				DEBUG2("Free I/O    base/length = "
260310923SEvan.Yan@Sun.COM 				    "[0x%x]/[0x%x]\n", ranges[i].child_low,
260410923SEvan.Yan@Sun.COM 				    ranges[i].size_low);
260510923SEvan.Yan@Sun.COM 				if (ndi_ra_free(ddi_get_parent(dip),
260610923SEvan.Yan@Sun.COM 				    (uint64_t)ranges[i].child_low,
260710923SEvan.Yan@Sun.COM 				    (uint64_t)ranges[i].size_low,
260810923SEvan.Yan@Sun.COM 				    NDI_RA_TYPE_IO, NDI_RA_PASS)
260910923SEvan.Yan@Sun.COM 				    != NDI_SUCCESS) {
261010923SEvan.Yan@Sun.COM 					DEBUG0("Trouble freeing "
261110923SEvan.Yan@Sun.COM 					    "PCI i/o space\n");
261210923SEvan.Yan@Sun.COM 					kmem_free(ranges, length);
261310923SEvan.Yan@Sun.COM 					return (PCICFG_FAILURE);
261410923SEvan.Yan@Sun.COM 				}
26153470Sanish 				break;
261610923SEvan.Yan@Sun.COM 			case PCI_ADDR_MEM32:
261710923SEvan.Yan@Sun.COM 			case PCI_ADDR_MEM64:
261810923SEvan.Yan@Sun.COM 				if (ranges[i].parent_high & PCI_REG_PF_M) {
261910923SEvan.Yan@Sun.COM 					DEBUG3("Free PF Memory base/length = "
262010923SEvan.Yan@Sun.COM 					    "[0x%x.0x%x]/[0x%x]\n",
262110923SEvan.Yan@Sun.COM 					    ranges[i].child_mid,
262210923SEvan.Yan@Sun.COM 					    ranges[i].child_low,
262310923SEvan.Yan@Sun.COM 					    ranges[i].size_low);
26243470Sanish 					mem_type = NDI_RA_TYPE_PCI_PREFETCH_MEM;
262510923SEvan.Yan@Sun.COM 				} else {
26263470Sanish 					DEBUG3("Free Memory base/length"
262710923SEvan.Yan@Sun.COM 					    " = [0x%x.0x%x]/[0x%x]\n",
262810923SEvan.Yan@Sun.COM 					    ranges[i].child_mid,
262910923SEvan.Yan@Sun.COM 					    ranges[i].child_low,
263010923SEvan.Yan@Sun.COM 					    ranges[i].size_low)
26313470Sanish 					mem_type = NDI_RA_TYPE_MEM;
263210923SEvan.Yan@Sun.COM 				}
263310923SEvan.Yan@Sun.COM 				if (ndi_ra_free(ddi_get_parent(dip),
263410923SEvan.Yan@Sun.COM 				    PCICFG_LADDR(ranges[i].child_low,
263510923SEvan.Yan@Sun.COM 				    ranges[i].child_mid),
263610923SEvan.Yan@Sun.COM 				    (uint64_t)ranges[i].size_low,
263710923SEvan.Yan@Sun.COM 				    mem_type, NDI_RA_PASS) != NDI_SUCCESS) {
263810923SEvan.Yan@Sun.COM 					DEBUG0("Trouble freeing "
263910923SEvan.Yan@Sun.COM 					    "PCI memory space\n");
264010923SEvan.Yan@Sun.COM 					kmem_free(ranges, length);
264110923SEvan.Yan@Sun.COM 					return (PCICFG_FAILURE);
264210923SEvan.Yan@Sun.COM 				}
26433470Sanish 				break;
264410923SEvan.Yan@Sun.COM 			default:
264510923SEvan.Yan@Sun.COM 				DEBUG0("Unknown memory space\n");
26463470Sanish 				break;
26473470Sanish 			}
26483470Sanish 		}
26493470Sanish 	}
26503470Sanish 
26513470Sanish 	if (length)
26523470Sanish 		kmem_free(ranges, length);
26533470Sanish 
265410923SEvan.Yan@Sun.COM 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
265510923SEvan.Yan@Sun.COM 	    "bus-range", (caddr_t)&bus, &k) != DDI_PROP_SUCCESS) {
26563470Sanish 		DEBUG0("Failed to read bus-range property\n");
26573470Sanish 		return (PCICFG_FAILURE);
26583470Sanish 	}
26593470Sanish 
26603470Sanish 	DEBUG2("Need to free bus [%d] range [%d]\n",
266110923SEvan.Yan@Sun.COM 	    bus[0], bus[1] - bus[0] + 1);
266210923SEvan.Yan@Sun.COM 
266310923SEvan.Yan@Sun.COM 	if (ndi_ra_free(ddi_get_parent(dip), (uint64_t)bus[0],
266410923SEvan.Yan@Sun.COM 	    (uint64_t)(bus[1] - bus[0] + 1), NDI_RA_TYPE_PCI_BUSNUM,
266510923SEvan.Yan@Sun.COM 	    NDI_RA_PASS) != NDI_SUCCESS) {
26663470Sanish 		DEBUG0("Failed to free a bus number\n");
26673735Sprasad 		kmem_free(bus, k);
26683470Sanish 		return (PCICFG_FAILURE);
26693470Sanish 	}
26703735Sprasad 
26713735Sprasad 	kmem_free(bus, k);
26723470Sanish 	return (PCICFG_SUCCESS);
26733470Sanish }
26743470Sanish 
26753470Sanish static int
pcicfg_free_device_resources(dev_info_t * dip)26763470Sanish pcicfg_free_device_resources(dev_info_t *dip)
26773470Sanish {
26783470Sanish 	pci_regspec_t *assigned;
26793470Sanish 
26803470Sanish 	int length;
26813470Sanish 	int acount;
26823470Sanish 	int i;
26833470Sanish 
268410923SEvan.Yan@Sun.COM 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
268510923SEvan.Yan@Sun.COM 	    "assigned-addresses", (caddr_t)&assigned, &length)
268610923SEvan.Yan@Sun.COM 	    != DDI_PROP_SUCCESS) {
26873470Sanish 		DEBUG0("Failed to read assigned-addresses property\n");
26883470Sanish 		return (PCICFG_FAILURE);
26893470Sanish 	}
26903470Sanish 
26913470Sanish 	/*
26923470Sanish 	 * For each "assigned-addresses" property entry with a length,
26933470Sanish 	 * call the memory allocation routines to return the
26943470Sanish 	 * resource.
26953470Sanish 	 */
26963470Sanish 	acount = length / sizeof (pci_regspec_t);
26973470Sanish 	for (i = 0; i < acount; i++) {
26983470Sanish 		char *mem_type;
26993470Sanish 
27003470Sanish 		/*
27013470Sanish 		 * Free the resource if the size of it is not zero.
27023470Sanish 		 */
27033470Sanish 		if ((assigned[i].pci_size_low != 0)||
270410923SEvan.Yan@Sun.COM 		    (assigned[i].pci_size_hi != 0)) {
27053470Sanish 			switch (PCI_REG_ADDR_G(assigned[i].pci_phys_hi)) {
27063470Sanish 			case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
27073470Sanish 				/*
27083470Sanish 				 * Check the assigned address for zero.
27093470Sanish 				 * (Workaround for Devconf (x86) bug to
27103470Sanish 				 * skip bogus entry for ROM base address
27113470Sanish 				 * register. If the assigned address is
27123470Sanish 				 * zero then ignore the entry
27133470Sanish 				 * (see bugid 4281306)).
27143470Sanish 				 */
27153470Sanish 				if (assigned[i].pci_phys_low == 0)
27163470Sanish 					break; /* ignore the entry */
27173470Sanish 
27183470Sanish 				if (assigned[i].pci_phys_hi & PCI_REG_PF_M)
27193470Sanish 					mem_type = NDI_RA_TYPE_PCI_PREFETCH_MEM;
27203470Sanish 				else
27213470Sanish 					mem_type = NDI_RA_TYPE_MEM;
27223470Sanish 
27233470Sanish 				if (ndi_ra_free(ddi_get_parent(dip),
27243735Sprasad 				    (uint64_t)assigned[i].pci_phys_low,
27253735Sprasad 				    (uint64_t)assigned[i].pci_size_low,
27263735Sprasad 				    mem_type, NDI_RA_PASS) != NDI_SUCCESS) {
27273735Sprasad 					DEBUG0("Trouble freeing "
272810923SEvan.Yan@Sun.COM 					    "PCI memory space\n");
27293735Sprasad 					kmem_free(assigned, length);
27303735Sprasad 					return (PCICFG_FAILURE);
27313470Sanish 				}
27323470Sanish 
27333470Sanish 				DEBUG4("Returned 0x%x of 32 bit %s space"
273410923SEvan.Yan@Sun.COM 				    " @ 0x%x from register 0x%x\n",
273510923SEvan.Yan@Sun.COM 				    assigned[i].pci_size_low, mem_type,
273610923SEvan.Yan@Sun.COM 				    assigned[i].pci_phys_low,
273710923SEvan.Yan@Sun.COM 				    PCI_REG_REG_G(assigned[i].pci_phys_hi));
27383470Sanish 
27393470Sanish 			break;
27403470Sanish 			case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
27413470Sanish 				if (assigned[i].pci_phys_hi & PCI_REG_PF_M)
27423470Sanish 					mem_type = NDI_RA_TYPE_PCI_PREFETCH_MEM;
27433470Sanish 				else
27443470Sanish 					mem_type = NDI_RA_TYPE_MEM;
27453470Sanish 
27463470Sanish 				if (ndi_ra_free(ddi_get_parent(dip),
27473735Sprasad 				    PCICFG_LADDR(assigned[i].pci_phys_low,
27483735Sprasad 				    assigned[i].pci_phys_mid),
27493735Sprasad 				    (uint64_t)assigned[i].pci_size_low,
27503735Sprasad 				    mem_type, NDI_RA_PASS) != NDI_SUCCESS) {
27513735Sprasad 					DEBUG0("Trouble freeing "
275210923SEvan.Yan@Sun.COM 					    "PCI memory space\n");
27533735Sprasad 					kmem_free(assigned, length);
27543735Sprasad 					return (PCICFG_FAILURE);
27553470Sanish 				}
27563470Sanish 
27573470Sanish 				DEBUG5("Returned 0x%x of 64 bit %s space"
275810923SEvan.Yan@Sun.COM 				    " @ 0x%x.0x%x from register 0x%x\n",
275910923SEvan.Yan@Sun.COM 				    assigned[i].pci_size_low,
276010923SEvan.Yan@Sun.COM 				    mem_type, assigned[i].pci_phys_mid,
276110923SEvan.Yan@Sun.COM 				    assigned[i].pci_phys_low,
276210923SEvan.Yan@Sun.COM 				    PCI_REG_REG_G(assigned[i].pci_phys_hi));
27633470Sanish 
27643470Sanish 			break;
27653470Sanish 			case PCI_REG_ADDR_G(PCI_ADDR_IO):
27663470Sanish 				if (ndi_ra_free(ddi_get_parent(dip),
27673735Sprasad 				    (uint64_t)assigned[i].pci_phys_low,
27683735Sprasad 				    (uint64_t)assigned[i].pci_size_low,
27693735Sprasad 				    NDI_RA_TYPE_IO, NDI_RA_PASS) !=
27703735Sprasad 				    NDI_SUCCESS) {
27713735Sprasad 					DEBUG0("Trouble freeing "
277210923SEvan.Yan@Sun.COM 					    "PCI IO space\n");
27733735Sprasad 					kmem_free(assigned, length);
27743735Sprasad 					return (PCICFG_FAILURE);
27753470Sanish 				}
277610923SEvan.Yan@Sun.COM 				DEBUG3("Returned 0x%x of IO space @ 0x%x from "
277710923SEvan.Yan@Sun.COM 				    "register 0x%x\n", assigned[i].pci_size_low,
277810923SEvan.Yan@Sun.COM 				    assigned[i].pci_phys_low,
277910923SEvan.Yan@Sun.COM 				    PCI_REG_REG_G(assigned[i].pci_phys_hi));
27803470Sanish 			break;
27813470Sanish 			default:
27823470Sanish 				DEBUG0("Unknown register type\n");
27833470Sanish 				kmem_free(assigned, length);
27843470Sanish 				return (PCICFG_FAILURE);
27853470Sanish 			} /* switch */
27863470Sanish 		}
27873470Sanish 	}
27883470Sanish 	kmem_free(assigned, length);
27893470Sanish 	return (PCICFG_SUCCESS);
27903470Sanish }
27913470Sanish 
27923470Sanish static int
pcicfg_free_resources(dev_info_t * dip,pcicfg_flags_t flags)279310923SEvan.Yan@Sun.COM pcicfg_free_resources(dev_info_t *dip, pcicfg_flags_t flags)
27943470Sanish {
27953470Sanish 	ddi_acc_handle_t handle;
27963470Sanish 	uint8_t header_type;
27973470Sanish 
27983470Sanish 	if (pci_config_setup(dip, &handle) != DDI_SUCCESS) {
27993470Sanish 		DEBUG0("Failed to map config space!\n");
28003470Sanish 		return (PCICFG_FAILURE);
28013470Sanish 	}
28023470Sanish 
28033470Sanish 	header_type = pci_config_get8(handle, PCI_CONF_HEADER);
28043470Sanish 
28053470Sanish 	(void) pci_config_teardown(&handle);
28063470Sanish 
28073470Sanish 	/*
28083470Sanish 	 * A different algorithm is used for bridges and leaf devices.
28093470Sanish 	 */
28103470Sanish 	if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) {
281110923SEvan.Yan@Sun.COM 		/*
281210923SEvan.Yan@Sun.COM 		 * We only support readonly probing for leaf devices.
281310923SEvan.Yan@Sun.COM 		 */
281410923SEvan.Yan@Sun.COM 		if (flags & PCICFG_FLAG_READ_ONLY)
281510923SEvan.Yan@Sun.COM 			return (PCICFG_FAILURE);
281610923SEvan.Yan@Sun.COM 
28173470Sanish 		if (pcicfg_free_bridge_resources(dip) != PCICFG_SUCCESS) {
28183470Sanish 			DEBUG0("Failed freeing up bridge resources\n");
28193470Sanish 			return (PCICFG_FAILURE);
28203470Sanish 		}
28213470Sanish 	} else {
28223470Sanish 		if (pcicfg_free_device_resources(dip) != PCICFG_SUCCESS) {
28233470Sanish 			DEBUG0("Failed freeing up device resources\n");
28243470Sanish 			return (PCICFG_FAILURE);
28253470Sanish 		}
28263470Sanish 	}
28273470Sanish 
28283470Sanish 	return (PCICFG_SUCCESS);
28293470Sanish }
28303470Sanish 
28313470Sanish #ifndef _DONT_USE_1275_GENERIC_NAMES
28323470Sanish static char *
pcicfg_get_class_name(uint32_t classcode)28333470Sanish pcicfg_get_class_name(uint32_t classcode)
28343470Sanish {
28353470Sanish 	struct pcicfg_name_entry *ptr;
28363470Sanish 
28373470Sanish 	for (ptr = &pcicfg_class_lookup[0]; ptr->name != NULL; ptr++) {
28383470Sanish 		if (ptr->class_code == classcode) {
28393470Sanish 			return (ptr->name);
28403470Sanish 		}
28413470Sanish 	}
28423470Sanish 	return (NULL);
28433470Sanish }
28443470Sanish #endif /* _DONT_USE_1275_GENERIC_NAMES */
28453470Sanish 
28463470Sanish static dev_info_t *
pcicfg_devi_find(dev_info_t * dip,uint_t device,uint_t function)28473470Sanish pcicfg_devi_find(dev_info_t *dip, uint_t device, uint_t function)
28483470Sanish {
28493470Sanish 	struct pcicfg_find_ctrl ctrl;
28503470Sanish 	int count;
28513470Sanish 
28523470Sanish 	ctrl.device = device;
28533470Sanish 	ctrl.function = function;
28543470Sanish 	ctrl.dip = NULL;
28553470Sanish 
28563470Sanish 	ndi_devi_enter(dip, &count);
28573470Sanish 	ddi_walk_devs(ddi_get_child(dip), pcicfg_match_dev, (void *)&ctrl);
28583470Sanish 	ndi_devi_exit(dip, count);
28593470Sanish 
28603470Sanish 	return (ctrl.dip);
28613470Sanish }
28623470Sanish 
28633470Sanish static int
pcicfg_match_dev(dev_info_t * dip,void * hdl)28643470Sanish pcicfg_match_dev(dev_info_t *dip, void *hdl)
28653470Sanish {
28663470Sanish 	struct pcicfg_find_ctrl *ctrl = (struct pcicfg_find_ctrl *)hdl;
28673470Sanish 	pci_regspec_t *pci_rp;
28683470Sanish 	int length;
28693470Sanish 	int pci_dev;
28703470Sanish 	int pci_func;
28713470Sanish 
287210923SEvan.Yan@Sun.COM 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
287310923SEvan.Yan@Sun.COM 	    "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
28743470Sanish 		ctrl->dip = NULL;
28753470Sanish 		return (DDI_WALK_TERMINATE);
28763470Sanish 	}
28773470Sanish 
28783470Sanish 	/* get the PCI device address info */
28793470Sanish 	pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
28803470Sanish 	pci_func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
28813470Sanish 
28823470Sanish 	/*
28833470Sanish 	 * free the memory allocated by ddi_prop_lookup_int_array
28843470Sanish 	 */
28853470Sanish 	ddi_prop_free(pci_rp);
28863470Sanish 
28873470Sanish 
28883470Sanish 	if ((pci_dev == ctrl->device) && (pci_func == ctrl->function)) {
28893470Sanish 		/* found the match for the specified device address */
28903470Sanish 		ctrl->dip = dip;
28913470Sanish 		return (DDI_WALK_TERMINATE);
28923470Sanish 	}
28933470Sanish 
28943470Sanish 	/*
28953470Sanish 	 * continue the walk to the next sibling to look for a match.
28963470Sanish 	 */
28973470Sanish 	return (DDI_WALK_PRUNECHILD);
28983470Sanish }
28993470Sanish 
29003470Sanish static int
pcicfg_update_assigned_prop(dev_info_t * dip,pci_regspec_t * newone)29013470Sanish pcicfg_update_assigned_prop(dev_info_t *dip, pci_regspec_t *newone)
29023470Sanish {
29033470Sanish 	int		alen;
29043470Sanish 	pci_regspec_t	*assigned;
29053470Sanish 	caddr_t		newreg;
29063470Sanish 	uint_t		status;
29073470Sanish 
29083470Sanish 	status = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
290910923SEvan.Yan@Sun.COM 	    "assigned-addresses", (caddr_t)&assigned, &alen);
29103470Sanish 	switch (status) {
29113470Sanish 		case DDI_PROP_SUCCESS:
29123470Sanish 		break;
29133470Sanish 		case DDI_PROP_NO_MEMORY:
29143470Sanish 			DEBUG0("no memory for assigned-addresses property\n");
29153470Sanish 			return (PCICFG_FAILURE);
29163470Sanish 		default:
29173470Sanish 			(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
291810923SEvan.Yan@Sun.COM 			    "assigned-addresses", (int *)newone,
291910923SEvan.Yan@Sun.COM 			    sizeof (*newone)/sizeof (int));
29203470Sanish 			return (PCICFG_SUCCESS);
29213470Sanish 	}
29223470Sanish 
29233470Sanish 	/*
29243470Sanish 	 * Allocate memory for the existing
29253470Sanish 	 * assigned-addresses(s) plus one and then
29263470Sanish 	 * build it.
29273470Sanish 	 */
29283470Sanish 
29293470Sanish 	newreg = kmem_zalloc(alen+sizeof (*newone), KM_SLEEP);
29303470Sanish 
29313470Sanish 	bcopy(assigned, newreg, alen);
29323470Sanish 	bcopy(newone, newreg + alen, sizeof (*newone));
29333470Sanish 
29343470Sanish 	/*
29353470Sanish 	 * Write out the new "assigned-addresses" spec
29363470Sanish 	 */
29373470Sanish 	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
293810923SEvan.Yan@Sun.COM 	    "assigned-addresses", (int *)newreg,
293910923SEvan.Yan@Sun.COM 	    (alen + sizeof (*newone))/sizeof (int));
29403470Sanish 
29413470Sanish 	kmem_free((caddr_t)newreg, alen+sizeof (*newone));
29423470Sanish 	kmem_free(assigned, alen);
29433470Sanish 
29443470Sanish 	return (PCICFG_SUCCESS);
29453470Sanish }
29463470Sanish 
29473470Sanish static int
pcicfg_update_ranges_prop(dev_info_t * dip,ppb_ranges_t * addition)29483470Sanish pcicfg_update_ranges_prop(dev_info_t *dip, ppb_ranges_t *addition)
29493470Sanish {
29503470Sanish 	int		rlen;
29513470Sanish 	ppb_ranges_t	*ranges;
29523470Sanish 	caddr_t		newreg;
29533470Sanish 	uint_t		status;
29543470Sanish 
295510923SEvan.Yan@Sun.COM 	status = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
295610923SEvan.Yan@Sun.COM 	    "ranges", (caddr_t)&ranges, &rlen);
29573470Sanish 
29583470Sanish 
29593470Sanish 	switch (status) {
29603470Sanish 		case DDI_PROP_SUCCESS:
29613470Sanish 			break;
29623470Sanish 		case DDI_PROP_NO_MEMORY:
29633470Sanish 			DEBUG0("ranges present, but unable to get memory\n");
29643470Sanish 			return (PCICFG_FAILURE);
29653470Sanish 		default:
29663470Sanish 			DEBUG0("no ranges property - creating one\n");
29673470Sanish 			if (ndi_prop_update_int_array(DDI_DEV_T_NONE,
296810923SEvan.Yan@Sun.COM 			    dip, "ranges", (int *)addition,
296910923SEvan.Yan@Sun.COM 			    sizeof (ppb_ranges_t)/sizeof (int))
297010923SEvan.Yan@Sun.COM 			    != DDI_SUCCESS) {
29713470Sanish 				DEBUG0("Did'nt create ranges property\n");
29723470Sanish 				return (PCICFG_FAILURE);
29733470Sanish 			}
29743470Sanish 			return (PCICFG_SUCCESS);
29753470Sanish 	}
29763470Sanish 
29773470Sanish 	/*
29783470Sanish 	 * Allocate memory for the existing ranges plus one and then
29793470Sanish 	 * build it.
29803470Sanish 	 */
29813470Sanish 	newreg = kmem_zalloc(rlen+sizeof (ppb_ranges_t), KM_SLEEP);
29823470Sanish 
29833470Sanish 	bcopy(ranges, newreg, rlen);
29843470Sanish 	bcopy(addition, newreg + rlen, sizeof (ppb_ranges_t));
29853470Sanish 
29863470Sanish 	/*
29873470Sanish 	 * Write out the new "ranges" property
29883470Sanish 	 */
298910923SEvan.Yan@Sun.COM 	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "ranges",
299010923SEvan.Yan@Sun.COM 	    (int *)newreg, (rlen + sizeof (ppb_ranges_t))/sizeof (int));
29913470Sanish 
29923470Sanish 	DEBUG1("Updating ranges property for %d entries",
299310923SEvan.Yan@Sun.COM 	    rlen / sizeof (ppb_ranges_t) + 1);
29943470Sanish 
29953470Sanish 	kmem_free((caddr_t)newreg, rlen+sizeof (ppb_ranges_t));
29963470Sanish 
29973470Sanish 	kmem_free((caddr_t)ranges, rlen);
29983470Sanish 
29993470Sanish 	return (PCICFG_SUCCESS);
30003470Sanish }
30013470Sanish 
30023470Sanish static int
pcicfg_update_reg_prop(dev_info_t * dip,uint32_t regvalue,uint_t reg_offset)30033470Sanish pcicfg_update_reg_prop(dev_info_t *dip, uint32_t regvalue, uint_t reg_offset)
30043470Sanish {
30053470Sanish 	int		rlen;
30063470Sanish 	pci_regspec_t	*reg;
30073470Sanish 	caddr_t		newreg;
30083470Sanish 	uint32_t	hiword;
30093470Sanish 	pci_regspec_t	addition;
30103470Sanish 	uint32_t	size;
30113470Sanish 	uint_t		status;
30123470Sanish 
30133470Sanish 	status = ddi_getlongprop(DDI_DEV_T_ANY,
301410923SEvan.Yan@Sun.COM 	    dip, DDI_PROP_DONTPASS, "reg", (caddr_t)&reg, &rlen);
30153470Sanish 
30163470Sanish 	switch (status) {
30173470Sanish 		case DDI_PROP_SUCCESS:
30183470Sanish 		break;
30193470Sanish 		case DDI_PROP_NO_MEMORY:
30203470Sanish 			DEBUG0("reg present, but unable to get memory\n");
30213470Sanish 			return (PCICFG_FAILURE);
30223470Sanish 		default:
30233470Sanish 			DEBUG0("no reg property\n");
30243470Sanish 			return (PCICFG_FAILURE);
30253470Sanish 	}
30263470Sanish 
30273470Sanish 	/*
30283470Sanish 	 * Allocate memory for the existing reg(s) plus one and then
30293470Sanish 	 * build it.
30303470Sanish 	 */
30313470Sanish 	newreg = kmem_zalloc(rlen+sizeof (pci_regspec_t), KM_SLEEP);
30323470Sanish 
30333470Sanish 	/*
30343470Sanish 	 * Build the regspec, then add it to the existing one(s)
30353470Sanish 	 */
30363470Sanish 
30373470Sanish 	hiword = PCICFG_MAKE_REG_HIGH(PCI_REG_BUS_G(reg->pci_phys_hi),
30383470Sanish 	    PCI_REG_DEV_G(reg->pci_phys_hi),
30393470Sanish 	    PCI_REG_FUNC_G(reg->pci_phys_hi), reg_offset);
30403470Sanish 
30413470Sanish 	if (reg_offset == PCI_CONF_ROM) {
30423470Sanish 		size = (~(PCI_BASE_ROM_ADDR_M & regvalue))+1;
30433470Sanish 		hiword |= PCI_ADDR_MEM32;
30443470Sanish 	} else {
30453470Sanish 		size = (~(PCI_BASE_M_ADDR_M & regvalue))+1;
30463470Sanish 
30473470Sanish 		if ((PCI_BASE_SPACE_M & regvalue) == PCI_BASE_SPACE_MEM) {
30483470Sanish 			if ((PCI_BASE_TYPE_M & regvalue) == PCI_BASE_TYPE_MEM) {
30493470Sanish 				hiword |= PCI_ADDR_MEM32;
30503470Sanish 			} else if ((PCI_BASE_TYPE_M & regvalue)
305110923SEvan.Yan@Sun.COM 			    == PCI_BASE_TYPE_ALL) {
30523470Sanish 				hiword |= PCI_ADDR_MEM64;
30533470Sanish 			}
30543470Sanish 			if (regvalue & PCI_BASE_PREF_M)
30553470Sanish 				hiword |= PCI_REG_PF_M;
30563470Sanish 		} else {
30573470Sanish 			hiword |= PCI_ADDR_IO;
30583470Sanish 		}
30593470Sanish 	}
30603470Sanish 
30613470Sanish 	addition.pci_phys_hi = hiword;
30623470Sanish 	addition.pci_phys_mid = 0;
30633470Sanish 	addition.pci_phys_low = 0;
30643470Sanish 	addition.pci_size_hi = 0;
30653470Sanish 	addition.pci_size_low = size;
30663470Sanish 
30673470Sanish 	bcopy(reg, newreg, rlen);
30683470Sanish 	bcopy(&addition, newreg + rlen, sizeof (pci_regspec_t));
30693470Sanish 
30703470Sanish 	DEBUG3("updating BAR@off %x with %x,%x\n", reg_offset, hiword, size);
30713470Sanish 	/*
30723470Sanish 	 * Write out the new "reg" property
30733470Sanish 	 */
307410923SEvan.Yan@Sun.COM 	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "reg",
307510923SEvan.Yan@Sun.COM 	    (int *)newreg, (rlen + sizeof (pci_regspec_t))/sizeof (int));
30763470Sanish 
30773470Sanish 	kmem_free((caddr_t)newreg, rlen+sizeof (pci_regspec_t));
30783470Sanish 	kmem_free((caddr_t)reg, rlen);
30793470Sanish 
30803470Sanish 	return (PCICFG_SUCCESS);
30813470Sanish }
30823470Sanish 
308310923SEvan.Yan@Sun.COM static int
pcicfg_update_assigned_prop_value(dev_info_t * dip,uint32_t size,uint32_t base,uint32_t base_hi,uint_t reg_offset)308410923SEvan.Yan@Sun.COM pcicfg_update_assigned_prop_value(dev_info_t *dip, uint32_t size,
308510923SEvan.Yan@Sun.COM     uint32_t base, uint32_t base_hi, uint_t reg_offset)
308610923SEvan.Yan@Sun.COM {
308710923SEvan.Yan@Sun.COM 	int		rlen;
308810923SEvan.Yan@Sun.COM 	pci_regspec_t	*reg;
308910923SEvan.Yan@Sun.COM 	uint32_t	hiword;
309010923SEvan.Yan@Sun.COM 	pci_regspec_t	addition;
309110923SEvan.Yan@Sun.COM 	uint_t		status;
309210923SEvan.Yan@Sun.COM 
309310923SEvan.Yan@Sun.COM 	status = ddi_getlongprop(DDI_DEV_T_ANY,
309410923SEvan.Yan@Sun.COM 	    dip, DDI_PROP_DONTPASS, "reg", (caddr_t)&reg, &rlen);
309510923SEvan.Yan@Sun.COM 
309610923SEvan.Yan@Sun.COM 	switch (status) {
309710923SEvan.Yan@Sun.COM 		case DDI_PROP_SUCCESS:
309810923SEvan.Yan@Sun.COM 		break;
309910923SEvan.Yan@Sun.COM 		case DDI_PROP_NO_MEMORY:
310010923SEvan.Yan@Sun.COM 			DEBUG0("reg present, but unable to get memory\n");
310110923SEvan.Yan@Sun.COM 			return (PCICFG_FAILURE);
310210923SEvan.Yan@Sun.COM 		default:
310310923SEvan.Yan@Sun.COM 			/*
310410923SEvan.Yan@Sun.COM 			 * Since the config space "reg" entry should have been
310510923SEvan.Yan@Sun.COM 			 * created, we expect a "reg" property already
310610923SEvan.Yan@Sun.COM 			 * present here.
310710923SEvan.Yan@Sun.COM 			 */
310810923SEvan.Yan@Sun.COM 			DEBUG0("no reg property\n");
310910923SEvan.Yan@Sun.COM 			return (PCICFG_FAILURE);
311010923SEvan.Yan@Sun.COM 	}
311110923SEvan.Yan@Sun.COM 
311210923SEvan.Yan@Sun.COM 	/*
311310923SEvan.Yan@Sun.COM 	 * Build the regspec, then add it to the existing one(s)
311410923SEvan.Yan@Sun.COM 	 */
311510923SEvan.Yan@Sun.COM 
311610923SEvan.Yan@Sun.COM 	hiword = PCICFG_MAKE_REG_HIGH(PCI_REG_BUS_G(reg->pci_phys_hi),
311710923SEvan.Yan@Sun.COM 	    PCI_REG_DEV_G(reg->pci_phys_hi),
311810923SEvan.Yan@Sun.COM 	    PCI_REG_FUNC_G(reg->pci_phys_hi), reg_offset);
311910923SEvan.Yan@Sun.COM 
312010923SEvan.Yan@Sun.COM 	hiword |= PCI_REG_REL_M;
312110923SEvan.Yan@Sun.COM 
312210923SEvan.Yan@Sun.COM 	if (reg_offset == PCI_CONF_ROM) {
312310923SEvan.Yan@Sun.COM 		hiword |= PCI_ADDR_MEM32;
312410923SEvan.Yan@Sun.COM 
312510923SEvan.Yan@Sun.COM 		base = PCI_BASE_ROM_ADDR_M & base;
312610923SEvan.Yan@Sun.COM 	} else {
312710923SEvan.Yan@Sun.COM 		if ((PCI_BASE_SPACE_M & base) == PCI_BASE_SPACE_MEM) {
312810923SEvan.Yan@Sun.COM 			if ((PCI_BASE_TYPE_M & base) == PCI_BASE_TYPE_MEM) {
312910923SEvan.Yan@Sun.COM 				hiword |= PCI_ADDR_MEM32;
313010923SEvan.Yan@Sun.COM 			} else if ((PCI_BASE_TYPE_M & base)
313110923SEvan.Yan@Sun.COM 			    == PCI_BASE_TYPE_ALL) {
313210923SEvan.Yan@Sun.COM 				hiword |= PCI_ADDR_MEM64;
313310923SEvan.Yan@Sun.COM 			}
313410923SEvan.Yan@Sun.COM 			if (base & PCI_BASE_PREF_M)
313510923SEvan.Yan@Sun.COM 				hiword |= PCI_REG_PF_M;
313610923SEvan.Yan@Sun.COM 
313710923SEvan.Yan@Sun.COM 			base = PCI_BASE_M_ADDR_M & base;
313810923SEvan.Yan@Sun.COM 		} else {
313910923SEvan.Yan@Sun.COM 			hiword |= PCI_ADDR_IO;
314010923SEvan.Yan@Sun.COM 
314110923SEvan.Yan@Sun.COM 			base = PCI_BASE_IO_ADDR_M & base;
314210923SEvan.Yan@Sun.COM 			base_hi = 0;
314310923SEvan.Yan@Sun.COM 		}
314410923SEvan.Yan@Sun.COM 	}
314510923SEvan.Yan@Sun.COM 
314610923SEvan.Yan@Sun.COM 	addition.pci_phys_hi = hiword;
314710923SEvan.Yan@Sun.COM 	addition.pci_phys_mid = base_hi;
314810923SEvan.Yan@Sun.COM 	addition.pci_phys_low = base;
314910923SEvan.Yan@Sun.COM 	addition.pci_size_hi = 0;
315010923SEvan.Yan@Sun.COM 	addition.pci_size_low = size;
315110923SEvan.Yan@Sun.COM 
315210923SEvan.Yan@Sun.COM 	DEBUG3("updating BAR@off %x with %x,%x\n", reg_offset, hiword, size);
315310923SEvan.Yan@Sun.COM 
315410923SEvan.Yan@Sun.COM 	kmem_free((caddr_t)reg, rlen);
315510923SEvan.Yan@Sun.COM 
315610923SEvan.Yan@Sun.COM 	return (pcicfg_update_assigned_prop(dip, &addition));
315710923SEvan.Yan@Sun.COM }
315810923SEvan.Yan@Sun.COM 
31593470Sanish static void
pcicfg_device_on(ddi_acc_handle_t config_handle)31603470Sanish pcicfg_device_on(ddi_acc_handle_t config_handle)
31613470Sanish {
31623470Sanish 	/*
31633470Sanish 	 * Enable memory, IO, and bus mastership
31643470Sanish 	 * XXX should we enable parity, SERR#,
31653470Sanish 	 * fast back-to-back, and addr. stepping?
31663470Sanish 	 */
31673470Sanish 	pci_config_put16(config_handle, PCI_CONF_COMM,
316810923SEvan.Yan@Sun.COM 	    pci_config_get16(config_handle, PCI_CONF_COMM) | 0x7);
31693470Sanish }
31703470Sanish 
31713470Sanish static void
pcicfg_device_off(ddi_acc_handle_t config_handle)31723470Sanish pcicfg_device_off(ddi_acc_handle_t config_handle)
31733470Sanish {
31743470Sanish 	/*
31753470Sanish 	 * Disable I/O and memory traffic through the bridge
31763470Sanish 	 */
31773470Sanish 	pci_config_put16(config_handle, PCI_CONF_COMM, 0x0);
31783470Sanish }
31793470Sanish 
31803470Sanish /*
31813470Sanish  * Setup the basic 1275 properties based on information found in the config
31823470Sanish  * header of the PCI device
31833470Sanish  */
31843470Sanish static int
pcicfg_set_standard_props(dev_info_t * dip,ddi_acc_handle_t config_handle,uint8_t pcie_dev)31853470Sanish pcicfg_set_standard_props(dev_info_t *dip, ddi_acc_handle_t config_handle,
31863470Sanish 	uint8_t pcie_dev)
31873470Sanish {
31887987SErwin.Tsaur@Sun.COM 	int ret;
31897987SErwin.Tsaur@Sun.COM 	uint16_t cap_id_loc, val;
31903470Sanish 	uint32_t wordval;
31913470Sanish 	uint8_t byteval;
31923470Sanish 
31933470Sanish 	/* These two exists only for non-bridges */
319410923SEvan.Yan@Sun.COM 	if (((pci_config_get8(config_handle, PCI_CONF_HEADER) &
319510923SEvan.Yan@Sun.COM 	    PCI_HEADER_TYPE_M) == PCI_HEADER_ZERO) && !pcie_dev) {
31963470Sanish 		byteval = pci_config_get8(config_handle, PCI_CONF_MIN_G);
31973470Sanish 		if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
319810923SEvan.Yan@Sun.COM 		    "min-grant", byteval)) != DDI_SUCCESS) {
31993470Sanish 			return (ret);
32003470Sanish 		}
32013470Sanish 
32023470Sanish 		byteval = pci_config_get8(config_handle, PCI_CONF_MAX_L);
32033470Sanish 		if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
320410923SEvan.Yan@Sun.COM 		    "max-latency", byteval)) != DDI_SUCCESS) {
32053470Sanish 			return (ret);
32063470Sanish 		}
32073470Sanish 	}
32083470Sanish 
32093470Sanish 	/*
32103470Sanish 	 * These should always exist and have the value of the
32113470Sanish 	 * corresponding register value
32123470Sanish 	 */
32133470Sanish 	val = pci_config_get16(config_handle, PCI_CONF_VENID);
32143470Sanish 
321510923SEvan.Yan@Sun.COM 	if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, "vendor-id", val))
321610923SEvan.Yan@Sun.COM 	    != DDI_SUCCESS) {
32173470Sanish 		return (ret);
32183470Sanish 	}
32193470Sanish 	val = pci_config_get16(config_handle, PCI_CONF_DEVID);
322010923SEvan.Yan@Sun.COM 	if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, "device-id", val))
322110923SEvan.Yan@Sun.COM 	    != DDI_SUCCESS) {
32223470Sanish 		return (ret);
32233470Sanish 	}
32243470Sanish 	byteval = pci_config_get8(config_handle, PCI_CONF_REVID);
32253470Sanish 	if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
322610923SEvan.Yan@Sun.COM 	    "revision-id", byteval)) != DDI_SUCCESS) {
32273470Sanish 		return (ret);
32283470Sanish 	}
32293470Sanish 
32303470Sanish 	wordval = (pci_config_get16(config_handle, PCI_CONF_SUBCLASS)<< 8) |
323110923SEvan.Yan@Sun.COM 	    (pci_config_get8(config_handle, PCI_CONF_PROGCLASS));
32323470Sanish 
32333470Sanish 	if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
323410923SEvan.Yan@Sun.COM 	    "class-code", wordval)) != DDI_SUCCESS) {
32353470Sanish 		return (ret);
32363470Sanish 	}
323710923SEvan.Yan@Sun.COM 	val = (pci_config_get16(config_handle, PCI_CONF_STAT) &
323810923SEvan.Yan@Sun.COM 	    PCI_STAT_DEVSELT);
32393470Sanish 	if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
324010923SEvan.Yan@Sun.COM 	    "devsel-speed", val)) != DDI_SUCCESS) {
32413470Sanish 		return (ret);
32423470Sanish 	}
32433470Sanish 
32443470Sanish 	/*
32453470Sanish 	 * The next three are bits set in the status register.  The property is
32463470Sanish 	 * present (but with no value other than its own existence) if the bit
32473470Sanish 	 * is set, non-existent otherwise
32483470Sanish 	 */
32493470Sanish 	if ((!pcie_dev) &&
325010923SEvan.Yan@Sun.COM 	    (pci_config_get16(config_handle, PCI_CONF_STAT) & PCI_STAT_FBBC)) {
32513470Sanish 		if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
325210923SEvan.Yan@Sun.COM 		    "fast-back-to-back", 0)) != DDI_SUCCESS) {
32533470Sanish 			return (ret);
32543470Sanish 		}
32553470Sanish 	}
32563470Sanish 	if ((!pcie_dev) &&
325710923SEvan.Yan@Sun.COM 	    (pci_config_get16(config_handle, PCI_CONF_STAT) & PCI_STAT_66MHZ)) {
32583470Sanish 		if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
325910923SEvan.Yan@Sun.COM 		    "66mhz-capable", 0)) != DDI_SUCCESS) {
32603470Sanish 			return (ret);
32613470Sanish 		}
32623470Sanish 	}
32633470Sanish 	if (pci_config_get16(config_handle, PCI_CONF_STAT) & PCI_STAT_UDF) {
32643470Sanish 		if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
326510923SEvan.Yan@Sun.COM 		    "udf-supported", 0)) != DDI_SUCCESS) {
32663470Sanish 			return (ret);
32673470Sanish 		}
32683470Sanish 	}
32693470Sanish 
32703470Sanish 	/*
32713470Sanish 	 * These next three are optional and are not present
32723470Sanish 	 * if the corresponding register is zero.  If the value
32733470Sanish 	 * is non-zero then the property exists with the value
32743470Sanish 	 * of the register.
32753470Sanish 	 */
327610923SEvan.Yan@Sun.COM 	if ((val = pci_config_get16(config_handle, PCI_CONF_SUBVENID)) != 0) {
32773470Sanish 		if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
327810923SEvan.Yan@Sun.COM 		    "subsystem-vendor-id", val)) != DDI_SUCCESS) {
32793470Sanish 			return (ret);
32803470Sanish 		}
32813470Sanish 	}
328210923SEvan.Yan@Sun.COM 	if ((val = pci_config_get16(config_handle, PCI_CONF_SUBSYSID)) != 0) {
32833470Sanish 		if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
328410923SEvan.Yan@Sun.COM 		    "subsystem-id", val)) != DDI_SUCCESS) {
32853470Sanish 			return (ret);
32863470Sanish 		}
32873470Sanish 	}
328810923SEvan.Yan@Sun.COM 	if ((val = pci_config_get16(config_handle, PCI_CONF_CACHE_LINESZ))
328910923SEvan.Yan@Sun.COM 	    != 0) {
32903470Sanish 		if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
329110923SEvan.Yan@Sun.COM 		    "cache-line-size", val)) != DDI_SUCCESS) {
32923470Sanish 			return (ret);
32933470Sanish 		}
32943470Sanish 	}
32953470Sanish 
32963470Sanish 	/*
32973470Sanish 	 * If the Interrupt Pin register is non-zero then the
32983470Sanish 	 * interrupts property exists
32993470Sanish 	 */
33003470Sanish 	if ((byteval = pci_config_get8(config_handle, PCI_CONF_IPIN)) != 0) {
33013470Sanish 		/*
33023470Sanish 		 * If interrupt pin is non-zero,
33033470Sanish 		 * record the interrupt line used
33043470Sanish 		 */
33053470Sanish 		if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
330610923SEvan.Yan@Sun.COM 		    "interrupts", byteval)) != DDI_SUCCESS) {
33073470Sanish 			return (ret);
33083470Sanish 		}
33093470Sanish 	}
33107987SErwin.Tsaur@Sun.COM 	(void) PCI_CAP_LOCATE(config_handle, PCI_CAP_ID_PCI_E, &cap_id_loc);
33117987SErwin.Tsaur@Sun.COM 	if (pcie_dev && cap_id_loc != PCI_CAP_NEXT_PTR_NULL) {
331210923SEvan.Yan@Sun.COM 		val = pci_config_get16(config_handle, cap_id_loc + PCIE_PCIECAP)
331310923SEvan.Yan@Sun.COM 		    & PCIE_PCIECAP_SLOT_IMPL;
33143470Sanish 		/* if slot implemented, get physical slot number */
33153470Sanish 		if (val) {
33163470Sanish 			wordval = pci_config_get32(config_handle, cap_id_loc +
331710923SEvan.Yan@Sun.COM 			    PCIE_SLOTCAP);
33183470Sanish 			/* create the property only if slotnum set correctly? */
33193470Sanish 			if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
33203470Sanish 			    "physical-slot#", PCIE_SLOTCAP_PHY_SLOT_NUM(
33213470Sanish 			    wordval))) != DDI_SUCCESS) {
33223470Sanish 				return (ret);
33233470Sanish 			}
33243470Sanish 		}
33253470Sanish 	}
33263470Sanish 
33273470Sanish 	return (PCICFG_SUCCESS);
33283470Sanish }
33293470Sanish 
33303470Sanish static int
pcicfg_set_busnode_props(dev_info_t * dip,uint8_t pcie_device_type)33313470Sanish pcicfg_set_busnode_props(dev_info_t *dip, uint8_t pcie_device_type)
33323470Sanish {
33333470Sanish 	int ret;
33343470Sanish 	char device_type[8];
33353470Sanish 
33363470Sanish 	if (pcie_device_type)
33373470Sanish 		(void) strcpy(device_type, "pciex");
33383470Sanish 	else
33393470Sanish 		(void) strcpy(device_type, "pci");
33403470Sanish 
33413470Sanish 	if ((ret = ndi_prop_update_string(DDI_DEV_T_NONE, dip,
334210923SEvan.Yan@Sun.COM 	    "device_type", device_type)) != DDI_SUCCESS) {
33433470Sanish 		return (ret);
33443470Sanish 	}
33453470Sanish 	if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
334610923SEvan.Yan@Sun.COM 	    "#address-cells", 3)) != DDI_SUCCESS) {
33473470Sanish 		return (ret);
33483470Sanish 	}
334910923SEvan.Yan@Sun.COM 	if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, "#size-cells", 2))
335010923SEvan.Yan@Sun.COM 	    != DDI_SUCCESS) {
33513470Sanish 		return (ret);
33523470Sanish 	}
33533470Sanish 	return (PCICFG_SUCCESS);
33543470Sanish }
33553470Sanish 
33563470Sanish static int
pcicfg_set_childnode_props(dev_info_t * dip,ddi_acc_handle_t config_handle,uint8_t pcie_dev)33573470Sanish pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle,
33583470Sanish 		uint8_t pcie_dev)
33593470Sanish {
33603470Sanish 
33613470Sanish 	int		ret;
33623470Sanish 	char		*name;
33633470Sanish 	char		buffer[64], pprefix[8], nprefix[8];
33643470Sanish 	uint16_t	classcode;
33653470Sanish 	uint8_t		revid, pif, pclass, psubclass;
33663470Sanish 	char		*compat[24];
33673470Sanish 	int		i;
33683470Sanish 	int		n;
33693470Sanish 	uint16_t		sub_vid, sub_sid, vid, did;
33703470Sanish 	/* set the property prefix based on the device type */
33713470Sanish 	if (pcie_dev) {
33723470Sanish 		(void) sprintf(pprefix, "pciex");
33733470Sanish 	} else
33743470Sanish 		(void) sprintf(pprefix, "pci");
33753470Sanish 
33763470Sanish 	/* set the prefix right for name property */
33773470Sanish 	/* x86 platforms need to go with pci for upgrade purposes */
33783470Sanish 	(void) sprintf(nprefix, "pci");
33793470Sanish 
33803470Sanish 	/*
33813470Sanish 	 * NOTE: These are for both a child and PCI-PCI bridge node
33823470Sanish 	 */
338310923SEvan.Yan@Sun.COM 	sub_vid = pci_config_get16(config_handle, PCI_CONF_SUBVENID);
33843470Sanish 	sub_sid = pci_config_get16(config_handle, PCI_CONF_SUBSYSID);
338510923SEvan.Yan@Sun.COM 	vid = pci_config_get16(config_handle, PCI_CONF_VENID);
33863470Sanish 	did = pci_config_get16(config_handle, PCI_CONF_DEVID);
33873470Sanish 	revid = pci_config_get8(config_handle, PCI_CONF_REVID);
33883470Sanish 	pif = pci_config_get8(config_handle, PCI_CONF_PROGCLASS);
33893470Sanish 	classcode = pci_config_get16(config_handle, PCI_CONF_SUBCLASS);
33903470Sanish 	pclass = pci_config_get8(config_handle, PCI_CONF_BASCLASS);
33913470Sanish 	psubclass = pci_config_get8(config_handle, PCI_CONF_SUBCLASS);
33923470Sanish 
33934463Sprasad 	if (!sub_vid)
33943470Sanish 		(void) sprintf(buffer, "%s%x,%x", nprefix, vid, did);
33953470Sanish 	else
33963470Sanish 		(void) sprintf(buffer, "%s%x,%x", nprefix, sub_vid, sub_sid);
33973470Sanish 
33983470Sanish 	/*
33993470Sanish 	 * In some environments, trying to use "generic" 1275 names is
34003470Sanish 	 * not the convention.  In those cases use the name as created
34013470Sanish 	 * above.  In all the rest of the cases, check to see if there
34023470Sanish 	 * is a generic name first.
34033470Sanish 	 */
34043470Sanish #ifdef _DONT_USE_1275_GENERIC_NAMES
34053470Sanish 	name = buffer;
34063470Sanish #else
34073470Sanish 	if ((name = pcicfg_get_class_name(classcode)) == NULL) {
34083470Sanish 		/*
34093470Sanish 		 * Set name to the above fabricated name
34103470Sanish 		 */
34113470Sanish 		name = buffer;
34123470Sanish 	}
34133470Sanish #endif
34143470Sanish 
34153470Sanish 	/*
34163470Sanish 	 * The node name field needs to be filled in with the name
34173470Sanish 	 */
34183470Sanish 	if (ndi_devi_set_nodename(dip, name, 0) != NDI_SUCCESS) {
34193470Sanish 		DEBUG0("Failed to set nodename for node\n");
34203470Sanish 		return (PCICFG_FAILURE);
34213470Sanish 	}
34223470Sanish 
34233470Sanish 	/*
34243470Sanish 	 * Create the compatible property as an array of pointers
34253470Sanish 	 * to strings.  Start with the buffer created above.
34263470Sanish 	 */
34273470Sanish 	n = 0;
34283470Sanish 
34293470Sanish 	/*
34303470Sanish 	 * Setup 'compatible' as per the PCI2.1 bindings document.
34313470Sanish 	 *	pci[ex]VVVV,DDDD.SSSS.ssss.RR
34323470Sanish 	 *	pci[ex]VVVV,DDDD.SSSS.ssss
34333470Sanish 	 *	pciSSSS.ssss  -> not created for PCIe as per PCIe bindings
34343470Sanish 	 *	pci[ex]VVVV,DDDD.RR
34353470Sanish 	 *	pci[ex]VVVV,DDDD
34363470Sanish 	 *	pci[ex]class,CCSSPP
34373470Sanish 	 *	pci[ex]class,CCSS
34383470Sanish 	 * Add legacy entries for compatibility with legacy devices and OS
34393470Sanish 	 * for x86.
34403470Sanish 	 *	pciVVVV,DDDD.SSSS.ssss.RR
34413470Sanish 	 *	pciVVVV,DDDD.SSSS.ssss
34423470Sanish 	 *	pciSSSS.ssss
34433470Sanish 	 *	pciVVVV,DDDD.RR
34443470Sanish 	 *	pciVVVV,DDDD
34453470Sanish 	 *	pciclass,CCSSPP
34463470Sanish 	 *	pciclass,CCSS
34473470Sanish 	 */
34483470Sanish 
34493470Sanish 	do {
34504463Sprasad 		if (sub_vid) {
345110923SEvan.Yan@Sun.COM 			/* pci[ex]VVVV,DDDD.SSSS.ssss.RR */
345210923SEvan.Yan@Sun.COM 			(void) sprintf(buffer, "%s%x,%x.%x.%x.%x", pprefix, vid,
345310923SEvan.Yan@Sun.COM 			    did, sub_vid, sub_sid, revid);
345410923SEvan.Yan@Sun.COM 			compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP);
345510923SEvan.Yan@Sun.COM 			(void) strcpy(compat[n++], buffer);
345610923SEvan.Yan@Sun.COM 
345710923SEvan.Yan@Sun.COM 			/* pci[ex]VVVV,DDDD.SSSS.ssss */
345810923SEvan.Yan@Sun.COM 			(void) sprintf(buffer, "%s%x,%x.%x.%x", pprefix,  vid,
345910923SEvan.Yan@Sun.COM 			    did, sub_vid, sub_sid);
34603470Sanish 			compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP);
34613470Sanish 			(void) strcpy(compat[n++], buffer);
346210923SEvan.Yan@Sun.COM 
346310923SEvan.Yan@Sun.COM 			/* pciSSSS.ssss  -> not created for PCIe as per PCIe */
346410923SEvan.Yan@Sun.COM 			/* binding to IEEE 1275 spec.			 */
346510923SEvan.Yan@Sun.COM 			if (!pcie_dev && pcicfg_do_legacy_props) {
346610923SEvan.Yan@Sun.COM 				(void) sprintf(buffer, "pci%x,%x", sub_vid,
346710923SEvan.Yan@Sun.COM 				    sub_sid);
346810923SEvan.Yan@Sun.COM 				compat[n] = kmem_alloc(strlen(buffer) + 1,
346910923SEvan.Yan@Sun.COM 				    KM_SLEEP);
347010923SEvan.Yan@Sun.COM 				(void) strcpy(compat[n++], buffer);
347110923SEvan.Yan@Sun.COM 			}
34723470Sanish 		}
34733470Sanish 
34743470Sanish 		/* pci[ex]VVVV,DDDD.RR */
34753470Sanish 		(void) sprintf(buffer, "%s%x,%x.%x", pprefix,  vid, did, revid);
34763470Sanish 		compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP);
34773470Sanish 		(void) strcpy(compat[n++], buffer);
34783470Sanish 
34793470Sanish 		/* pci[ex]VVVV,DDDD */
34803470Sanish 		(void) sprintf(buffer, "%s%x,%x", pprefix, vid, did);
34813470Sanish 		compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP);
34823470Sanish 		(void) strcpy(compat[n++], buffer);
34833470Sanish 
34843470Sanish 		/* pci[ex]class,CCSSPP */
348510923SEvan.Yan@Sun.COM 		(void) sprintf(buffer, "%sclass,%02x%02x%02x", pprefix, pclass,
348610923SEvan.Yan@Sun.COM 		    psubclass, pif);
34873470Sanish 		compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP);
34883470Sanish 		(void) strcpy(compat[n++], buffer);
34893470Sanish 
34903470Sanish 		/* pci[ex]class,CCSS */
34913470Sanish 		(void) sprintf(buffer, "%sclass,%04x", pprefix, classcode);
34923470Sanish 		compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP);
34933470Sanish 		(void) strcpy(compat[n++], buffer);
34943470Sanish 
34953470Sanish 		if (!pcie_dev)
34963470Sanish 			break;
34973470Sanish 
34983470Sanish 		/* also add compatible names using "pci" prefix */
34993470Sanish 		(void) sprintf(pprefix, "pci");
35003470Sanish 		pcie_dev = 0;
35013470Sanish 
35023470Sanish 	} while (pcicfg_do_legacy_props);
35033470Sanish 
350410923SEvan.Yan@Sun.COM 	ret = ndi_prop_update_string_array(DDI_DEV_T_NONE, dip, "compatible",
350510923SEvan.Yan@Sun.COM 	    (char **)compat, n);
35063470Sanish 
35073470Sanish 	for (i = 0; i < n; i++) {
35083470Sanish 		kmem_free(compat[i], strlen(compat[i]) + 1);
35093470Sanish 	}
35103470Sanish 
35113735Sprasad 	return (ret);
35123470Sanish }
35133470Sanish 
35143470Sanish /*
35153470Sanish  * Program the bus numbers into the bridge
35163470Sanish  */
35173470Sanish static void
pcicfg_set_bus_numbers(ddi_acc_handle_t config_handle,uint_t primary,uint_t secondary,uint_t subordinate)35183470Sanish pcicfg_set_bus_numbers(ddi_acc_handle_t config_handle,
35193470Sanish uint_t primary, uint_t secondary, uint_t subordinate)
35203470Sanish {
35213470Sanish 	DEBUG3("Setting bridge bus-range %d,%d,%d\n", primary, secondary,
352210923SEvan.Yan@Sun.COM 	    subordinate);
35233470Sanish 	/*
35243470Sanish 	 * Primary bus#
35253470Sanish 	 */
35263470Sanish 	pci_config_put8(config_handle, PCI_BCNF_PRIBUS, primary);
35273470Sanish 
35283470Sanish 	/*
35293470Sanish 	 * Secondary bus#
35303470Sanish 	 */
35313470Sanish 	pci_config_put8(config_handle, PCI_BCNF_SECBUS, secondary);
35323470Sanish 
35333470Sanish 	/*
35343470Sanish 	 * Set the subordinate bus number to ff in order to pass through any
35353470Sanish 	 * type 1 cycle with a bus number higher than the secondary bus#
35363470Sanish 	 */
35373470Sanish 	pci_config_put8(config_handle, PCI_BCNF_SUBBUS, subordinate);
35383470Sanish }
35393470Sanish 
35403470Sanish /*
35413470Sanish  * Put bridge registers into initial state
35423470Sanish  */
35433470Sanish static void
pcicfg_setup_bridge(pcicfg_phdl_t * entry,ddi_acc_handle_t handle)35443470Sanish pcicfg_setup_bridge(pcicfg_phdl_t *entry,
35453470Sanish 	ddi_acc_handle_t handle)
35463470Sanish {
35473470Sanish 	/*
35483470Sanish 	 * The highest bus seen during probing is the max-subordinate bus
35493470Sanish 	 */
35503470Sanish 	pci_config_put8(handle, PCI_BCNF_SUBBUS, entry->highest_bus);
35513470Sanish 
35523470Sanish 	/*
35533470Sanish 	 * Reset the secondary bus
35543470Sanish 	 */
35553470Sanish 	pci_config_put16(handle, PCI_BCNF_BCNTRL,
355610923SEvan.Yan@Sun.COM 	    pci_config_get16(handle, PCI_BCNF_BCNTRL) | 0x40);
35573470Sanish 	drv_usecwait(1000);
35583470Sanish 	pci_config_put16(handle, PCI_BCNF_BCNTRL,
355910923SEvan.Yan@Sun.COM 	    pci_config_get16(handle, PCI_BCNF_BCNTRL) & ~0x40);
35603470Sanish 	drv_usecwait(1000);
35613470Sanish 
35623470Sanish 	/*
35633470Sanish 	 * Program the memory base register with the
35643470Sanish 	 * start of the memory range
35653470Sanish 	 */
35663470Sanish 	pci_config_put16(handle, PCI_BCNF_MEM_BASE,
356710923SEvan.Yan@Sun.COM 	    PCICFG_HIWORD(PCICFG_LOADDR(entry->memory_last)));
35683470Sanish 
35693470Sanish 	/*
35703470Sanish 	 * Program the I/O base register with the start of the I/O range
35713470Sanish 	 */
35723470Sanish 	pci_config_put8(handle, PCI_BCNF_IO_BASE_LOW,
357310923SEvan.Yan@Sun.COM 	    PCICFG_HIBYTE(PCICFG_LOWORD(PCICFG_LOADDR(entry->io_last))));
35743470Sanish 	pci_config_put16(handle, PCI_BCNF_IO_BASE_HI,
357510923SEvan.Yan@Sun.COM 	    PCICFG_HIWORD(PCICFG_LOADDR(entry->io_last)));
35763470Sanish 
35773470Sanish 	/*
35783470Sanish 	 * Program the PF memory base register with the start of
35793470Sanish 	 * PF memory range
35803470Sanish 	 */
35813470Sanish 	pci_config_put16(handle, PCI_BCNF_PF_BASE_LOW,
358210923SEvan.Yan@Sun.COM 	    PCICFG_HIWORD(PCICFG_LOADDR(entry->pf_memory_last)));
35833470Sanish 	pci_config_put32(handle, PCI_BCNF_PF_BASE_HIGH,
358410923SEvan.Yan@Sun.COM 	    PCICFG_HIADDR(entry->pf_memory_last));
35853470Sanish 
35863470Sanish 	/*
35873470Sanish 	 * Clear status bits
35883470Sanish 	 */
35893470Sanish 	pci_config_put16(handle, PCI_BCNF_SEC_STATUS, 0xffff);
35903470Sanish 
35913470Sanish 	/*
35923470Sanish 	 * Needs to be set to this value
35933470Sanish 	 */
35943470Sanish 	pci_config_put8(handle, PCI_CONF_ILINE, 0xf);
35953470Sanish 
35963470Sanish 	/*
35973470Sanish 	 * XXX - may be delay should be used since noone configures
35983470Sanish 	 * devices in the interrupt context
35993470Sanish 	 */
36003470Sanish 	drv_usecwait(pcicfg_sec_reset_delay);	/* 1 sec wait */
36013470Sanish }
36023470Sanish 
36033470Sanish static void
pcicfg_update_bridge(pcicfg_phdl_t * entry,ddi_acc_handle_t handle)36043470Sanish pcicfg_update_bridge(pcicfg_phdl_t *entry,
36053470Sanish 	ddi_acc_handle_t handle)
36063470Sanish {
36073470Sanish 	uint_t length;
36083470Sanish 
36093470Sanish 	/*
36103470Sanish 	 * Program the memory limit register with the end of the memory range
36113470Sanish 	 */
36123470Sanish 
36133470Sanish 	DEBUG1("DOWN ROUNDED ===>[0x%x]\n",
361410923SEvan.Yan@Sun.COM 	    PCICFG_ROUND_DOWN(entry->memory_last, PCICFG_MEMGRAN));
36153470Sanish 
36163470Sanish 	pci_config_put16(handle, PCI_BCNF_MEM_LIMIT,
361710923SEvan.Yan@Sun.COM 	    PCICFG_HIWORD(PCICFG_LOADDR(
361810923SEvan.Yan@Sun.COM 	    PCICFG_ROUND_DOWN(entry->memory_last, PCICFG_MEMGRAN))));
36193470Sanish 	/*
36203470Sanish 	 * Since this is a bridge, the rest of this range will
36213470Sanish 	 * be responded to by the bridge.  We have to round up
36223470Sanish 	 * so no other device claims it.
36233470Sanish 	 */
362410923SEvan.Yan@Sun.COM 	if ((length = (PCICFG_ROUND_UP(entry->memory_last, PCICFG_MEMGRAN)
362510923SEvan.Yan@Sun.COM 	    - entry->memory_last)) > 0) {
36263470Sanish 		(void) pcicfg_get_mem(entry, length, NULL);
362710923SEvan.Yan@Sun.COM 		DEBUG1("Added [0x%x]at the top of the bridge (mem)\n", length);
36283470Sanish 	}
36293470Sanish 
36303470Sanish 	/*
36313470Sanish 	 * Program the PF memory limit register with the end of the memory range
36323470Sanish 	 */
36333470Sanish 
36343470Sanish 	DEBUG1("DOWN ROUNDED ===>[0x%x]\n",
363510923SEvan.Yan@Sun.COM 	    PCICFG_ROUND_DOWN(entry->pf_memory_last, PCICFG_MEMGRAN));
36363470Sanish 
36373470Sanish 	pci_config_put16(handle, PCI_BCNF_PF_LIMIT_LOW,
363810923SEvan.Yan@Sun.COM 	    PCICFG_HIWORD(PCICFG_LOADDR(PCICFG_ROUND_DOWN(
363910923SEvan.Yan@Sun.COM 	    entry->pf_memory_last, PCICFG_MEMGRAN))));
364010923SEvan.Yan@Sun.COM 	pci_config_put32(handle, PCI_BCNF_PF_LIMIT_HIGH, PCICFG_HIADDR(
364110923SEvan.Yan@Sun.COM 	    PCICFG_ROUND_DOWN(entry->pf_memory_last, PCICFG_MEMGRAN)));
364210923SEvan.Yan@Sun.COM 	if ((length = (PCICFG_ROUND_UP(entry->pf_memory_last, PCICFG_MEMGRAN)
364310923SEvan.Yan@Sun.COM 	    - entry->pf_memory_last)) > 0) {
36443470Sanish 		(void) pcicfg_get_pf_mem(entry, length, NULL);
364510923SEvan.Yan@Sun.COM 		DEBUG1("Added [0x%x]at the top of the bridge (PF mem)\n",
364610923SEvan.Yan@Sun.COM 		    length);
36473470Sanish 	}
36483470Sanish 
36493470Sanish 	/*
36503470Sanish 	 * Program the I/O limit register with the end of the I/O range
36513470Sanish 	 */
36523470Sanish 	pci_config_put8(handle, PCI_BCNF_IO_LIMIT_LOW,
365310923SEvan.Yan@Sun.COM 	    PCICFG_HIBYTE(PCICFG_LOWORD(
365410923SEvan.Yan@Sun.COM 	    PCICFG_LOADDR(PCICFG_ROUND_DOWN(entry->io_last, PCICFG_IOGRAN)))));
365510923SEvan.Yan@Sun.COM 
365610923SEvan.Yan@Sun.COM 	pci_config_put16(handle, PCI_BCNF_IO_LIMIT_HI, PCICFG_HIWORD(
365710923SEvan.Yan@Sun.COM 	    PCICFG_LOADDR(PCICFG_ROUND_DOWN(entry->io_last, PCICFG_IOGRAN))));
36583470Sanish 
36593470Sanish 	/*
36603470Sanish 	 * Same as above for I/O space. Since this is a
36613470Sanish 	 * bridge, the rest of this range will be responded
36623470Sanish 	 * to by the bridge.  We have to round up so no
36633470Sanish 	 * other device claims it.
36643470Sanish 	 */
366510923SEvan.Yan@Sun.COM 	if ((length = (PCICFG_ROUND_UP(entry->io_last, PCICFG_IOGRAN)
366610923SEvan.Yan@Sun.COM 	    - entry->io_last)) > 0) {
36673470Sanish 		(void) pcicfg_get_io(entry, length, NULL);
366810923SEvan.Yan@Sun.COM 		DEBUG1("Added [0x%x]at the top of the bridge (I/O)\n", length);
36693470Sanish 	}
36703470Sanish }
36713470Sanish 
36723470Sanish static int
pcicfg_probe_children(dev_info_t * parent,uint_t bus,uint_t device,uint_t func,uint_t * highest_bus,pcicfg_flags_t flags,boolean_t is_pcie)367310923SEvan.Yan@Sun.COM pcicfg_probe_children(dev_info_t *parent, uint_t bus, uint_t device,
367411245SZhijun.Fu@Sun.COM     uint_t func, uint_t *highest_bus, pcicfg_flags_t flags, boolean_t is_pcie)
36753470Sanish {
36763470Sanish 	dev_info_t		*new_child;
36773470Sanish 	ddi_acc_handle_t	config_handle;
36783470Sanish 	uint8_t			header_type, pcie_dev = 0;
36795149Sjveta 	int			ret = PCICFG_FAILURE;
36803470Sanish 
36813470Sanish 	/*
36823470Sanish 	 * This node will be put immediately below
36833470Sanish 	 * "parent". Allocate a blank device node.  It will either
36843470Sanish 	 * be filled in or freed up based on further probing.
36853470Sanish 	 */
36863470Sanish 
36873470Sanish 	ndi_devi_alloc_sleep(parent, DEVI_PSEUDO_NEXNAME,
368810923SEvan.Yan@Sun.COM 	    (pnode_t)DEVI_SID_NODEID, &new_child);
368910923SEvan.Yan@Sun.COM 
369010923SEvan.Yan@Sun.COM 	if (pcicfg_add_config_reg(new_child, bus, device, func)
369110923SEvan.Yan@Sun.COM 	    != DDI_SUCCESS) {
369210923SEvan.Yan@Sun.COM 		DEBUG0("pcicfg_probe_children():Failed to add candidate REG\n");
36933470Sanish 		goto failedconfig;
36943470Sanish 	}
36953470Sanish 
36963470Sanish 	if ((ret = pcicfg_config_setup(new_child, &config_handle))
369710923SEvan.Yan@Sun.COM 	    != PCICFG_SUCCESS) {
36983470Sanish 		if (ret == PCICFG_NODEVICE) {
36993470Sanish 			(void) ndi_devi_free(new_child);
37003470Sanish 			return (ret);
37013470Sanish 		}
37023470Sanish 		DEBUG0("pcicfg_probe_children():"
37033470Sanish 		"Failed to setup config space\n");
37043470Sanish 		goto failedconfig;
37053470Sanish 	}
37063470Sanish 
370711245SZhijun.Fu@Sun.COM 	if (is_pcie)
370811245SZhijun.Fu@Sun.COM 		(void) pcie_init_bus(new_child, PCI_GETBDF(bus, device, func),
370911245SZhijun.Fu@Sun.COM 		    PCIE_BUS_INITIAL);
371011245SZhijun.Fu@Sun.COM 
37113470Sanish 	/*
37123470Sanish 	 * As soon as we have access to config space,
37133470Sanish 	 * turn off device. It will get turned on
37143470Sanish 	 * later (after memory is assigned).
37153470Sanish 	 */
37163470Sanish 	(void) pcicfg_device_off(config_handle);
37173470Sanish 
37183470Sanish 	/* check if we are PCIe device */
37193470Sanish 	if (pcicfg_pcie_dev(new_child, config_handle) == DDI_SUCCESS) {
37203470Sanish 		DEBUG0("PCIe device detected\n");
37213470Sanish 		pcie_dev = 1;
37223470Sanish 	}
37233470Sanish 
37243470Sanish 	/*
37253470Sanish 	 * Set 1275 properties common to all devices
37263470Sanish 	 */
372710923SEvan.Yan@Sun.COM 	if (pcicfg_set_standard_props(new_child, config_handle, pcie_dev)
372810923SEvan.Yan@Sun.COM 	    != PCICFG_SUCCESS) {
37293470Sanish 		DEBUG0("Failed to set standard properties\n");
37303470Sanish 		goto failedchild;
37313470Sanish 	}
37323470Sanish 
37333470Sanish 	/*
37343470Sanish 	 * Child node properties  NOTE: Both for PCI-PCI bridge and child node
37353470Sanish 	 */
373610923SEvan.Yan@Sun.COM 	if (pcicfg_set_childnode_props(new_child, config_handle, pcie_dev)
373710923SEvan.Yan@Sun.COM 	    != PCICFG_SUCCESS) {
37383470Sanish 		goto failedchild;
37393470Sanish 	}
37403470Sanish 
37413470Sanish 	header_type = pci_config_get8(config_handle, PCI_CONF_HEADER);
37423470Sanish 
37433470Sanish 	/*
37443470Sanish 	 * If this is not a multi-function card only probe function zero.
37453470Sanish 	 */
37463470Sanish 	if ((!(header_type & PCI_HEADER_MULTI)) && (func != 0)) {
37473470Sanish 
374811245SZhijun.Fu@Sun.COM 		ret = PCICFG_NODEVICE;
374911245SZhijun.Fu@Sun.COM 		goto failedchild;
37503470Sanish 	}
37513470Sanish 
37523470Sanish 	/*
37533470Sanish 	 * Attach the child to its parent
37543470Sanish 	 */
37553470Sanish 	(void) i_ndi_config_node(new_child, DS_LINKED, 0);
37563470Sanish 
375711600SVikram.Hegde@Sun.COM 	DEVI_SET_PCI(new_child);
375811600SVikram.Hegde@Sun.COM 
37593470Sanish 	if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) {
37603470Sanish 
376110923SEvan.Yan@Sun.COM 		DEBUG3("--Bridge found bus [0x%x] device[0x%x] func [0x%x]\n",
376210923SEvan.Yan@Sun.COM 		    bus, device, func);
376310923SEvan.Yan@Sun.COM 
376410923SEvan.Yan@Sun.COM 		/* Only support read-only probe for leaf device */
376510923SEvan.Yan@Sun.COM 		if (flags & PCICFG_FLAG_READ_ONLY)
376610923SEvan.Yan@Sun.COM 			goto failedchild;
37673470Sanish 
37685149Sjveta 		ret = pcicfg_probe_bridge(new_child, config_handle, bus,
376911245SZhijun.Fu@Sun.COM 		    highest_bus, is_pcie);
37705149Sjveta 		if (ret != PCICFG_SUCCESS) {
37713470Sanish 			(void) pcicfg_free_bridge_resources(new_child);
37723470Sanish 			goto failedchild;
37733470Sanish 		}
37743470Sanish 
37753470Sanish 	} else {
37763470Sanish 
37773470Sanish 		DEBUG3("--Leaf device found bus [0x%x] device"
377810923SEvan.Yan@Sun.COM 		    "[0x%x] func [0x%x]\n", bus, device, func);
377910923SEvan.Yan@Sun.COM 
378010923SEvan.Yan@Sun.COM 		if (flags & PCICFG_FLAG_READ_ONLY) {
37813470Sanish 			/*
378210923SEvan.Yan@Sun.COM 			 * with read-only probe, don't do any resource
378310923SEvan.Yan@Sun.COM 			 * allocation, just read the BARs and update props.
37843470Sanish 			 */
378510923SEvan.Yan@Sun.COM 			ret = pcicfg_populate_props_from_bar(new_child,
378610923SEvan.Yan@Sun.COM 			    config_handle);
378710923SEvan.Yan@Sun.COM 			if (ret != PCICFG_SUCCESS)
378810923SEvan.Yan@Sun.COM 				goto failedchild;
37893470Sanish 
37903470Sanish 			/*
379110923SEvan.Yan@Sun.COM 			 * now allocate the resources, just remove the
379210923SEvan.Yan@Sun.COM 			 * resources from the parent busra pool.
37933470Sanish 			 */
379410923SEvan.Yan@Sun.COM 			ret = pcicfg_device_assign_readonly(new_child);
379510923SEvan.Yan@Sun.COM 			if (ret != PCICFG_SUCCESS) {
379610923SEvan.Yan@Sun.COM 				(void) pcicfg_free_device_resources(new_child);
379710923SEvan.Yan@Sun.COM 				goto failedchild;
37983470Sanish 			}
379910923SEvan.Yan@Sun.COM 
380010923SEvan.Yan@Sun.COM 		} else {
38013470Sanish 			/*
380210923SEvan.Yan@Sun.COM 			 * update "reg" property by sizing the BARs.
38033470Sanish 			 */
380410923SEvan.Yan@Sun.COM 			ret = pcicfg_populate_reg_props(new_child,
380510923SEvan.Yan@Sun.COM 			    config_handle);
380610923SEvan.Yan@Sun.COM 			if (ret != PCICFG_SUCCESS)
380710923SEvan.Yan@Sun.COM 				goto failedchild;
380810923SEvan.Yan@Sun.COM 
380910923SEvan.Yan@Sun.COM 			/* now allocate & program the resources */
381010923SEvan.Yan@Sun.COM 			ret = pcicfg_device_assign(new_child);
381110923SEvan.Yan@Sun.COM 			if (ret != PCICFG_SUCCESS) {
381210923SEvan.Yan@Sun.COM 				(void) pcicfg_free_device_resources(new_child);
38133470Sanish 				goto failedchild;
38143470Sanish 			}
38153470Sanish 		}
38163470Sanish 
38173470Sanish 		(void) ndi_devi_bind_driver(new_child, 0);
38183470Sanish 	}
38193470Sanish 
38203470Sanish 	(void) pcicfg_config_teardown(&config_handle);
38213470Sanish 
382211245SZhijun.Fu@Sun.COM 	/*
382311245SZhijun.Fu@Sun.COM 	 * Properties have been setted up, so initialize the remaining
382411245SZhijun.Fu@Sun.COM 	 * bus_t fields
382511245SZhijun.Fu@Sun.COM 	 */
382611245SZhijun.Fu@Sun.COM 	if (is_pcie)
382711387SSurya.Prakki@Sun.COM 		(void) pcie_init_bus(new_child, 0, PCIE_BUS_FINAL);
382811245SZhijun.Fu@Sun.COM 
38293470Sanish 	return (PCICFG_SUCCESS);
38303470Sanish 
38313470Sanish failedchild:
38323470Sanish 	/*
38333470Sanish 	 * XXX check if it should be taken offline (if online)
38343470Sanish 	 */
38353470Sanish 	(void) pcicfg_config_teardown(&config_handle);
38363470Sanish 
383711245SZhijun.Fu@Sun.COM 	if (is_pcie)
383811245SZhijun.Fu@Sun.COM 		pcie_fini_bus(new_child, PCIE_BUS_FINAL);
383911245SZhijun.Fu@Sun.COM 
38403470Sanish failedconfig:
38413470Sanish 
38423470Sanish 	(void) ndi_devi_free(new_child);
38435149Sjveta 	return (ret);
38443470Sanish }
38453470Sanish 
384610923SEvan.Yan@Sun.COM /*
384710923SEvan.Yan@Sun.COM  * Sizing the BARs and update "reg" property
384810923SEvan.Yan@Sun.COM  */
384910923SEvan.Yan@Sun.COM static int
pcicfg_populate_reg_props(dev_info_t * new_child,ddi_acc_handle_t config_handle)385010923SEvan.Yan@Sun.COM pcicfg_populate_reg_props(dev_info_t *new_child,
385110923SEvan.Yan@Sun.COM     ddi_acc_handle_t config_handle)
385210923SEvan.Yan@Sun.COM {
385310923SEvan.Yan@Sun.COM 	int		i;
385410923SEvan.Yan@Sun.COM 	uint32_t 	request;
385510923SEvan.Yan@Sun.COM 
385610923SEvan.Yan@Sun.COM 	i = PCI_CONF_BASE0;
385710923SEvan.Yan@Sun.COM 
385810923SEvan.Yan@Sun.COM 	while (i <= PCI_CONF_BASE5) {
385910923SEvan.Yan@Sun.COM 
386010923SEvan.Yan@Sun.COM 		pci_config_put32(config_handle, i, 0xffffffff);
386110923SEvan.Yan@Sun.COM 
386210923SEvan.Yan@Sun.COM 		request = pci_config_get32(config_handle, i);
386310923SEvan.Yan@Sun.COM 		/*
386410923SEvan.Yan@Sun.COM 		 * If its a zero length, don't do
386510923SEvan.Yan@Sun.COM 		 * any programming.
386610923SEvan.Yan@Sun.COM 		 */
386710923SEvan.Yan@Sun.COM 		if (request != 0) {
386810923SEvan.Yan@Sun.COM 			/*
386910923SEvan.Yan@Sun.COM 			 * Add to the "reg" property
387010923SEvan.Yan@Sun.COM 			 */
387110923SEvan.Yan@Sun.COM 			if (pcicfg_update_reg_prop(new_child,
387210923SEvan.Yan@Sun.COM 			    request, i) != PCICFG_SUCCESS) {
387310923SEvan.Yan@Sun.COM 				goto failedchild;
387410923SEvan.Yan@Sun.COM 			}
387510923SEvan.Yan@Sun.COM 		} else {
387610923SEvan.Yan@Sun.COM 			DEBUG1("BASE register [0x%x] asks for "
387710923SEvan.Yan@Sun.COM 			    "[0x0]=[0x0](32)\n", i);
387810923SEvan.Yan@Sun.COM 			i += 4;
387910923SEvan.Yan@Sun.COM 			continue;
388010923SEvan.Yan@Sun.COM 		}
388110923SEvan.Yan@Sun.COM 
388210923SEvan.Yan@Sun.COM 		/*
388310923SEvan.Yan@Sun.COM 		 * Increment by eight if it is 64 bit address space
388410923SEvan.Yan@Sun.COM 		 */
388510923SEvan.Yan@Sun.COM 		if ((PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_ALL) {
388610923SEvan.Yan@Sun.COM 			DEBUG3("BASE register [0x%x] asks for "
388710923SEvan.Yan@Sun.COM 			    "[0x%x]=[0x%x] (64)\n",
388810923SEvan.Yan@Sun.COM 			    i, request, (~(PCI_BASE_M_ADDR_M & request))+1);
388910923SEvan.Yan@Sun.COM 			i += 8;
389010923SEvan.Yan@Sun.COM 		} else {
389110923SEvan.Yan@Sun.COM 			DEBUG3("BASE register [0x%x] asks for "
389210923SEvan.Yan@Sun.COM 			    "[0x%x]=[0x%x](32)\n",
389310923SEvan.Yan@Sun.COM 			    i, request, (~(PCI_BASE_M_ADDR_M & request))+1);
389410923SEvan.Yan@Sun.COM 			i += 4;
389510923SEvan.Yan@Sun.COM 		}
389610923SEvan.Yan@Sun.COM 	}
389710923SEvan.Yan@Sun.COM 
389810923SEvan.Yan@Sun.COM 	/*
389910923SEvan.Yan@Sun.COM 	 * Get the ROM size and create register for it
390010923SEvan.Yan@Sun.COM 	 */
390110923SEvan.Yan@Sun.COM 	pci_config_put32(config_handle, PCI_CONF_ROM, 0xfffffffe);
390210923SEvan.Yan@Sun.COM 
390310923SEvan.Yan@Sun.COM 	request = pci_config_get32(config_handle, PCI_CONF_ROM);
390410923SEvan.Yan@Sun.COM 	/*
390510923SEvan.Yan@Sun.COM 	 * If its a zero length, don't do
390610923SEvan.Yan@Sun.COM 	 * any programming.
390710923SEvan.Yan@Sun.COM 	 */
390810923SEvan.Yan@Sun.COM 
390910923SEvan.Yan@Sun.COM 	if (request != 0) {
391010923SEvan.Yan@Sun.COM 		DEBUG3("BASE register [0x%x] asks for [0x%x]=[0x%x]\n",
391110923SEvan.Yan@Sun.COM 		    PCI_CONF_ROM, request,
391210923SEvan.Yan@Sun.COM 		    (~(PCI_BASE_ROM_ADDR_M & request)) + 1);
391310923SEvan.Yan@Sun.COM 		/*
391410923SEvan.Yan@Sun.COM 		 * Add to the "reg" property
391510923SEvan.Yan@Sun.COM 		 */
391610923SEvan.Yan@Sun.COM 		if (pcicfg_update_reg_prop(new_child, request, PCI_CONF_ROM)
391710923SEvan.Yan@Sun.COM 		    != PCICFG_SUCCESS) {
391810923SEvan.Yan@Sun.COM 			goto failedchild;
391910923SEvan.Yan@Sun.COM 		}
392010923SEvan.Yan@Sun.COM 	}
392110923SEvan.Yan@Sun.COM 
392210923SEvan.Yan@Sun.COM 	return (PCICFG_SUCCESS);
392310923SEvan.Yan@Sun.COM 
392410923SEvan.Yan@Sun.COM failedchild:
392510923SEvan.Yan@Sun.COM 	return (PCICFG_FAILURE);
392610923SEvan.Yan@Sun.COM }
392710923SEvan.Yan@Sun.COM 
392810923SEvan.Yan@Sun.COM /*
392910923SEvan.Yan@Sun.COM  * Read the BARs and update properties. Used in virtual hotplug.
393010923SEvan.Yan@Sun.COM  */
393110923SEvan.Yan@Sun.COM static int
pcicfg_populate_props_from_bar(dev_info_t * new_child,ddi_acc_handle_t config_handle)393210923SEvan.Yan@Sun.COM pcicfg_populate_props_from_bar(dev_info_t *new_child,
393310923SEvan.Yan@Sun.COM     ddi_acc_handle_t config_handle)
393410923SEvan.Yan@Sun.COM {
393510923SEvan.Yan@Sun.COM 	uint32_t request, base, base_hi, size;
393610923SEvan.Yan@Sun.COM 	int i;
393710923SEvan.Yan@Sun.COM 
393810923SEvan.Yan@Sun.COM 	i = PCI_CONF_BASE0;
393910923SEvan.Yan@Sun.COM 
394010923SEvan.Yan@Sun.COM 	while (i <= PCI_CONF_BASE5) {
394110923SEvan.Yan@Sun.COM 		/*
394210923SEvan.Yan@Sun.COM 		 * determine the size of the address space
394310923SEvan.Yan@Sun.COM 		 */
394410923SEvan.Yan@Sun.COM 		base = pci_config_get32(config_handle, i);
394510923SEvan.Yan@Sun.COM 		pci_config_put32(config_handle, i, 0xffffffff);
394610923SEvan.Yan@Sun.COM 		request = pci_config_get32(config_handle, i);
394710923SEvan.Yan@Sun.COM 		pci_config_put32(config_handle, i, base);
394810923SEvan.Yan@Sun.COM 
394910923SEvan.Yan@Sun.COM 		/*
395010923SEvan.Yan@Sun.COM 		 * If its a zero length, don't do any programming.
395110923SEvan.Yan@Sun.COM 		 */
395210923SEvan.Yan@Sun.COM 		if (request != 0) {
395310923SEvan.Yan@Sun.COM 			/*
395410923SEvan.Yan@Sun.COM 			 * Add to the "reg" property
395510923SEvan.Yan@Sun.COM 			 */
395610923SEvan.Yan@Sun.COM 			if (pcicfg_update_reg_prop(new_child,
395710923SEvan.Yan@Sun.COM 			    request, i) != PCICFG_SUCCESS) {
395810923SEvan.Yan@Sun.COM 				goto failedchild;
395910923SEvan.Yan@Sun.COM 			}
396010923SEvan.Yan@Sun.COM 
396110923SEvan.Yan@Sun.COM 			if ((PCI_BASE_SPACE_IO & request) == 0 &&
396210923SEvan.Yan@Sun.COM 			    (PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_ALL) {
396310923SEvan.Yan@Sun.COM 				base_hi = pci_config_get32(config_handle, i+4);
396410923SEvan.Yan@Sun.COM 			} else {
396510923SEvan.Yan@Sun.COM 				base_hi = 0;
396610923SEvan.Yan@Sun.COM 			}
396710923SEvan.Yan@Sun.COM 			/*
396810923SEvan.Yan@Sun.COM 			 * Add to "assigned-addresses" property
396910923SEvan.Yan@Sun.COM 			 */
397010923SEvan.Yan@Sun.COM 			size = (~(PCI_BASE_M_ADDR_M & request))+1;
397110923SEvan.Yan@Sun.COM 			if (pcicfg_update_assigned_prop_value(new_child,
397210923SEvan.Yan@Sun.COM 			    size, base, base_hi, i) != PCICFG_SUCCESS) {
397310923SEvan.Yan@Sun.COM 				goto failedchild;
397410923SEvan.Yan@Sun.COM 			}
397510923SEvan.Yan@Sun.COM 		} else {
397610923SEvan.Yan@Sun.COM 			DEBUG1("BASE register [0x%x] asks for [0x0]=[0x0]"
397710923SEvan.Yan@Sun.COM 			    "(32)\n", i);
397810923SEvan.Yan@Sun.COM 			i += 4;
397910923SEvan.Yan@Sun.COM 			continue;
398010923SEvan.Yan@Sun.COM 		}
398110923SEvan.Yan@Sun.COM 
398210923SEvan.Yan@Sun.COM 		/*
398310923SEvan.Yan@Sun.COM 		 * Increment by eight if it is 64 bit address space
398410923SEvan.Yan@Sun.COM 		 */
398510923SEvan.Yan@Sun.COM 		if ((PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_ALL) {
398610923SEvan.Yan@Sun.COM 			DEBUG3("BASE register [0x%x] asks for [0x%x]=[0x%x]"
398710923SEvan.Yan@Sun.COM 			    "(64)\n", i, request,
398810923SEvan.Yan@Sun.COM 			    (~(PCI_BASE_M_ADDR_M & request)) + 1);
398910923SEvan.Yan@Sun.COM 			i += 8;
399010923SEvan.Yan@Sun.COM 		} else {
399110923SEvan.Yan@Sun.COM 			DEBUG3("BASE register [0x%x] asks for [0x%x]=[0x%x]"
399210923SEvan.Yan@Sun.COM 			    "(32)\n", i, request,
399310923SEvan.Yan@Sun.COM 			    (~(PCI_BASE_M_ADDR_M & request)) + 1);
399410923SEvan.Yan@Sun.COM 			i += 4;
399510923SEvan.Yan@Sun.COM 		}
399610923SEvan.Yan@Sun.COM 	}
399710923SEvan.Yan@Sun.COM 
399810923SEvan.Yan@Sun.COM 	/*
399910923SEvan.Yan@Sun.COM 	 * Get the ROM size and create register for it
400010923SEvan.Yan@Sun.COM 	 */
400110923SEvan.Yan@Sun.COM 	base = pci_config_get32(config_handle, PCI_CONF_ROM);
400210923SEvan.Yan@Sun.COM 	pci_config_put32(config_handle, PCI_CONF_ROM, 0xfffffffe);
400310923SEvan.Yan@Sun.COM 	request = pci_config_get32(config_handle, PCI_CONF_ROM);
400410923SEvan.Yan@Sun.COM 	pci_config_put32(config_handle, PCI_CONF_ROM, base);
400510923SEvan.Yan@Sun.COM 
400610923SEvan.Yan@Sun.COM 	/*
400710923SEvan.Yan@Sun.COM 	 * If its a zero length, don't do
400810923SEvan.Yan@Sun.COM 	 * any programming.
400910923SEvan.Yan@Sun.COM 	 */
401010923SEvan.Yan@Sun.COM 	if (request != 0) {
401110923SEvan.Yan@Sun.COM 		DEBUG3("BASE register [0x%x] asks for [0x%x]=[0x%x]\n",
401210923SEvan.Yan@Sun.COM 		    PCI_CONF_ROM, request,
401310923SEvan.Yan@Sun.COM 		    (~(PCI_BASE_ROM_ADDR_M & request)) + 1);
401410923SEvan.Yan@Sun.COM 		/*
401510923SEvan.Yan@Sun.COM 		 * Add to the "reg" property
401610923SEvan.Yan@Sun.COM 		 */
401710923SEvan.Yan@Sun.COM 		if (pcicfg_update_reg_prop(new_child, request, PCI_CONF_ROM)
401810923SEvan.Yan@Sun.COM 		    != PCICFG_SUCCESS) {
401910923SEvan.Yan@Sun.COM 			goto failedchild;
402010923SEvan.Yan@Sun.COM 		}
402110923SEvan.Yan@Sun.COM 		/*
402210923SEvan.Yan@Sun.COM 		 * Add to "assigned-addresses" property
402310923SEvan.Yan@Sun.COM 		 */
402410923SEvan.Yan@Sun.COM 		size = (~(PCI_BASE_ROM_ADDR_M & request))+1;
402510923SEvan.Yan@Sun.COM 		if (pcicfg_update_assigned_prop_value(new_child, size,
402610923SEvan.Yan@Sun.COM 		    base, 0, PCI_CONF_ROM) != PCICFG_SUCCESS) {
402710923SEvan.Yan@Sun.COM 			goto failedchild;
402810923SEvan.Yan@Sun.COM 		}
402910923SEvan.Yan@Sun.COM 	}
403010923SEvan.Yan@Sun.COM 
403110923SEvan.Yan@Sun.COM 	return (PCICFG_SUCCESS);
403210923SEvan.Yan@Sun.COM 
403310923SEvan.Yan@Sun.COM failedchild:
403410923SEvan.Yan@Sun.COM 	return (PCICFG_FAILURE);
403510923SEvan.Yan@Sun.COM }
403610923SEvan.Yan@Sun.COM 
40373470Sanish static int
pcicfg_probe_bridge(dev_info_t * new_child,ddi_acc_handle_t h,uint_t bus,uint_t * highest_bus,boolean_t is_pcie)40383470Sanish pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus,
403911245SZhijun.Fu@Sun.COM     uint_t *highest_bus, boolean_t is_pcie)
40403470Sanish {
40413470Sanish 	uint64_t next_bus;
40423470Sanish 	uint_t new_bus, num_slots;
40433470Sanish 	ndi_ra_request_t req;
40443470Sanish 	int rval, i, j;
40453470Sanish 	uint64_t mem_answer, io_answer, mem_base, io_base, mem_alen, io_alen;
40463470Sanish 	uint64_t pf_mem_answer, pf_mem_base, pf_mem_alen;
40473470Sanish 	uint64_t mem_size, io_size, pf_mem_size;
40483470Sanish 	uint64_t mem_end, pf_mem_end, io_end;
40493470Sanish 	uint64_t round_answer, round_len;
40503470Sanish 	ppb_ranges_t range[PCICFG_RANGE_LEN];
40513470Sanish 	int bus_range[2];
40523470Sanish 	pcicfg_phdl_t phdl;
40533470Sanish 	int count;
40543470Sanish 	uint64_t pcibus_base, pcibus_alen;
40553470Sanish 	uint64_t max_bus;
40563470Sanish 	uint8_t pcie_device_type = 0;
40573470Sanish 	uint_t pf_mem_supported = 0;
405810923SEvan.Yan@Sun.COM 	dev_info_t *new_device;
405910923SEvan.Yan@Sun.COM 	int trans_device;
406010923SEvan.Yan@Sun.COM 	int ari_mode = B_FALSE;
406110923SEvan.Yan@Sun.COM 	int max_function = PCI_MAX_FUNCTIONS;
40623470Sanish 
40633470Sanish 	io_answer = io_base = io_alen = io_size = 0;
40643470Sanish 	pf_mem_answer = pf_mem_base = pf_mem_size = pf_mem_alen = 0;
40653470Sanish 
40663470Sanish 	/*
406710923SEvan.Yan@Sun.COM 	 * Set "device_type" to "pci", the actual type will be set later
406810923SEvan.Yan@Sun.COM 	 * by pcicfg_set_busnode_props() below. This is needed as the
406910923SEvan.Yan@Sun.COM 	 * pcicfg_ra_free() below would update "available" property based
407010923SEvan.Yan@Sun.COM 	 * on "device_type".
407110923SEvan.Yan@Sun.COM 	 *
407210923SEvan.Yan@Sun.COM 	 * This code can be removed later after PCI configurator is changed
407310923SEvan.Yan@Sun.COM 	 * to use PCIRM, which automatically update properties upon allocation
407410923SEvan.Yan@Sun.COM 	 * and free, at that time we'll be able to remove the code inside
407510923SEvan.Yan@Sun.COM 	 * ndi_ra_alloc/free() which currently updates "available" property
407610923SEvan.Yan@Sun.COM 	 * for pci/pcie devices in pcie fabric.
407710923SEvan.Yan@Sun.COM 	 */
407810923SEvan.Yan@Sun.COM 	if (ndi_prop_update_string(DDI_DEV_T_NONE, new_child,
407910923SEvan.Yan@Sun.COM 	    "device_type", "pci") != DDI_SUCCESS) {
408010923SEvan.Yan@Sun.COM 		DEBUG0("Failed to set \"device_type\" props\n");
408110923SEvan.Yan@Sun.COM 		return (PCICFG_FAILURE);
408210923SEvan.Yan@Sun.COM 	}
408310923SEvan.Yan@Sun.COM 
408410923SEvan.Yan@Sun.COM 	/*
40853470Sanish 	 * setup resource maps for the bridge node
40863470Sanish 	 */
40873470Sanish 	if (ndi_ra_map_setup(new_child, NDI_RA_TYPE_PCI_BUSNUM)
408810923SEvan.Yan@Sun.COM 	    == NDI_FAILURE) {
40893470Sanish 		DEBUG0("Can not setup resource map - NDI_RA_TYPE_PCI_BUSNUM\n");
40903470Sanish 		rval = PCICFG_FAILURE;
40913470Sanish 		goto cleanup;
40923470Sanish 	}
40933470Sanish 	if (ndi_ra_map_setup(new_child, NDI_RA_TYPE_MEM) == NDI_FAILURE) {
40943470Sanish 		DEBUG0("Can not setup resource map - NDI_RA_TYPE_MEM\n");
40953470Sanish 		rval = PCICFG_FAILURE;
40963470Sanish 		goto cleanup;
40973470Sanish 	}
40983470Sanish 	if (ndi_ra_map_setup(new_child, NDI_RA_TYPE_IO) == NDI_FAILURE) {
40993470Sanish 		DEBUG0("Can not setup resource map - NDI_RA_TYPE_IO\n");
41003470Sanish 		rval = PCICFG_FAILURE;
41013470Sanish 		goto cleanup;
41023470Sanish 	}
41033470Sanish 	if (ndi_ra_map_setup(new_child, NDI_RA_TYPE_PCI_PREFETCH_MEM) ==
410410923SEvan.Yan@Sun.COM 	    NDI_FAILURE) {
41053470Sanish 		DEBUG0("Can not setup resource map -"
410610923SEvan.Yan@Sun.COM 		    " NDI_RA_TYPE_PCI_PREFETCH_MEM\n");
41073470Sanish 		rval = PCICFG_FAILURE;
41083470Sanish 		goto cleanup;
41093470Sanish 	}
41103470Sanish 
41113470Sanish 	/*
41123470Sanish 	 * Allocate bus range pool for the bridge.
41133470Sanish 	 */
41143470Sanish 	bzero((caddr_t)&req, sizeof (ndi_ra_request_t));
41153470Sanish 	req.ra_flags = (NDI_RA_ALLOC_BOUNDED | NDI_RA_ALLOC_PARTIAL_OK);
41163470Sanish 	req.ra_boundbase = 0;
41173470Sanish 	req.ra_boundlen = req.ra_len = (PCI_MAX_BUS_NUM -1);
41183470Sanish 	req.ra_align_mask = 0;  /* no alignment needed */
41193470Sanish 
41203470Sanish 	rval = ndi_ra_alloc(ddi_get_parent(new_child), &req,
41213470Sanish 	    &pcibus_base, &pcibus_alen, NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS);
41223470Sanish 
41233470Sanish 	if (rval != NDI_SUCCESS) {
41243470Sanish 		if (rval == NDI_RA_PARTIAL_REQ) {
41253470Sanish 			/*EMPTY*/
41263470Sanish 			DEBUG0("NDI_RA_PARTIAL_REQ returned for bus range\n");
41273470Sanish 		} else {
41283470Sanish 			DEBUG0(
41293470Sanish 			    "Failed to allocate bus range for bridge\n");
41305149Sjveta 			rval = PCICFG_NORESRC;
41313470Sanish 			goto cleanup;
41323470Sanish 		}
41333470Sanish 	}
41343470Sanish 
41353470Sanish 	DEBUG2("Bus Range Allocated [base=%d] [len=%d]\n",
413610923SEvan.Yan@Sun.COM 	    pcibus_base, pcibus_alen);
41373470Sanish 
41383470Sanish 	/*
41393470Sanish 	 * Put available bus range into the pool.
41403470Sanish 	 * Take the first one for this bridge to use and don't give
41413470Sanish 	 * to child.
41423470Sanish 	 */
41433470Sanish 	(void) ndi_ra_free(new_child, pcibus_base+1, pcibus_alen-1,
414410923SEvan.Yan@Sun.COM 	    NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS);
41453470Sanish 
41463470Sanish 	next_bus = pcibus_base;
41473470Sanish 	max_bus = pcibus_base + pcibus_alen - 1;
41483470Sanish 
41493470Sanish 	new_bus = next_bus;
41503470Sanish 
41513470Sanish 	DEBUG1("NEW bus found  ->[%d]\n", new_bus);
41523470Sanish 
41533470Sanish 	/* Keep track of highest bus for subordinate bus programming */
41543470Sanish 	*highest_bus = new_bus;
41553470Sanish 
41563470Sanish 	/*
41573470Sanish 	 * Allocate (non-prefetchable) Memory Space for Bridge
41583470Sanish 	 */
41593470Sanish 	bzero((caddr_t)&req, sizeof (ndi_ra_request_t));
41603470Sanish 	req.ra_flags = (NDI_RA_ALLOC_BOUNDED | NDI_RA_ALLOC_PARTIAL_OK);
41613470Sanish 	req.ra_boundbase = 0;
41623470Sanish 	/*
41633470Sanish 	 * limit the boundlen,len to a 32b quantity. It should be Ok to
41643470Sanish 	 * lose alignment-based-size of resource due to this.
41653470Sanish 	 */
41663470Sanish 	req.ra_boundlen = PCICFG_4GIG_LIMIT;
41673470Sanish 	req.ra_len = PCICFG_4GIG_LIMIT; /* Get as big as possible */
41683470Sanish 	req.ra_align_mask =
41693470Sanish 	    PCICFG_MEMGRAN - 1; /* 1M alignment on memory space */
41703470Sanish 
41713470Sanish 	rval = ndi_ra_alloc(ddi_get_parent(new_child), &req,
41723470Sanish 	    &mem_answer, &mem_alen,  NDI_RA_TYPE_MEM, NDI_RA_PASS);
41733470Sanish 
41743470Sanish 	if (rval != NDI_SUCCESS) {
41753470Sanish 		if (rval == NDI_RA_PARTIAL_REQ) {
41763470Sanish 			/*EMPTY*/
41773470Sanish 			DEBUG0("NDI_RA_PARTIAL_REQ returned\n");
41783470Sanish 		} else {
41793470Sanish 			DEBUG0(
41803470Sanish 			    "Failed to allocate memory for bridge\n");
41815149Sjveta 			rval = PCICFG_NORESRC;
41823470Sanish 			goto cleanup;
41833470Sanish 		}
41843470Sanish 	}
41853470Sanish 
41863470Sanish 	DEBUG3("Bridge Memory Allocated [0x%x.%x] len [0x%x]\n",
41873470Sanish 	    PCICFG_HIADDR(mem_answer),
41883470Sanish 	    PCICFG_LOADDR(mem_answer),
41893470Sanish 	    mem_alen);
41903470Sanish 
41913470Sanish 	/*
41923470Sanish 	 * Put available memory into the pool.
41933470Sanish 	 */
41943470Sanish 	(void) ndi_ra_free(new_child, mem_answer, mem_alen, NDI_RA_TYPE_MEM,
41953470Sanish 	    NDI_RA_PASS);
41963470Sanish 
41973470Sanish 	mem_base = mem_answer;
41983470Sanish 
41993470Sanish 	/*
42003470Sanish 	 * Allocate I/O Space for Bridge
42013470Sanish 	 */
42023470Sanish 	bzero((caddr_t)&req, sizeof (ndi_ra_request_t));
42033470Sanish 	req.ra_align_mask = PCICFG_IOGRAN - 1; /* 4k alignment */
42043470Sanish 	req.ra_boundbase = 0;
42053470Sanish 	req.ra_boundlen = PCICFG_4GIG_LIMIT;
42063470Sanish 	req.ra_flags = (NDI_RA_ALLOC_BOUNDED | NDI_RA_ALLOC_PARTIAL_OK);
42073470Sanish 	req.ra_len = PCICFG_4GIG_LIMIT; /* Get as big as possible */
42083470Sanish 
42093470Sanish 	rval = ndi_ra_alloc(ddi_get_parent(new_child), &req, &io_answer,
42103470Sanish 	    &io_alen, NDI_RA_TYPE_IO, NDI_RA_PASS);
42113470Sanish 
42123470Sanish 	if (rval != NDI_SUCCESS) {
42133470Sanish 		if (rval == NDI_RA_PARTIAL_REQ) {
42143470Sanish 			/*EMPTY*/
42153470Sanish 			DEBUG0("NDI_RA_PARTIAL_REQ returned\n");
42163470Sanish 		} else {
42173470Sanish 			DEBUG0("Failed to allocate io space for bridge\n");
42183470Sanish 			/* i/o space is an optional requirement so continue */
42193470Sanish 		}
42203470Sanish 	}
42213470Sanish 
42223470Sanish 	DEBUG3("Bridge IO Space Allocated [0x%x.%x] len [0x%x]\n",
42233470Sanish 	    PCICFG_HIADDR(io_answer), PCICFG_LOADDR(io_answer), io_alen);
42243470Sanish 
42253470Sanish 	/*
42263470Sanish 	 * Put available I/O into the pool.
42273470Sanish 	 */
42283470Sanish 	(void) ndi_ra_free(new_child, io_answer, io_alen, NDI_RA_TYPE_IO,
42293470Sanish 	    NDI_RA_PASS);
42303470Sanish 
42313470Sanish 	io_base = io_answer;
42323470Sanish 
42333470Sanish 	/*
42343470Sanish 	 * Check if the bridge supports Prefetchable memory range.
42353470Sanish 	 * If it does, then we setup PF memory range for the bridge.
42363470Sanish 	 * Otherwise, we skip the step of setting up PF memory
42373470Sanish 	 * range for it. This could cause config operation to
42383470Sanish 	 * fail if any devices under the bridge need PF memory.
42393470Sanish 	 */
42403470Sanish 	/* write a non zero value to the PF BASE register */
42413470Sanish 	pci_config_put16(h, PCI_BCNF_PF_BASE_LOW, 0xfff0);
42423470Sanish 	/* if the read returns zero then PF range is not supported */
42433470Sanish 	if (pci_config_get16(h, PCI_BCNF_PF_BASE_LOW) == 0) {
42443470Sanish 		/* bridge doesn't support PF memory range */
42453470Sanish 		goto pf_setup_end;
42463470Sanish 	} else {
42473470Sanish 		pf_mem_supported = 1;
42483470Sanish 		/* reset the PF BASE register */
42493470Sanish 		pci_config_put16(h, PCI_BCNF_PF_BASE_LOW, 0);
42503470Sanish 	}
42513470Sanish 
42523470Sanish 	/*
42533470Sanish 	 * Bridge supports PF mem range; Allocate PF Memory Space for it.
42543470Sanish 	 *
42553470Sanish 	 * Note: Both non-prefetchable and prefetchable memory space
42563470Sanish 	 * allocations are made within 32bit space. Currently, BIOSs
42573470Sanish 	 * allocate device memory for PCI devices within the 32bit space
42583470Sanish 	 * so this will not be a problem.
42593470Sanish 	 */
42603470Sanish 	bzero((caddr_t)&req, sizeof (ndi_ra_request_t));
42613470Sanish 	req.ra_flags = NDI_RA_ALLOC_PARTIAL_OK | NDI_RA_ALLOC_BOUNDED;
42623470Sanish 	req.ra_boundbase = 0;
42633470Sanish 	req.ra_len = PCICFG_4GIG_LIMIT; /* Get as big as possible */
42643470Sanish 	req.ra_align_mask =
42653470Sanish 	    PCICFG_MEMGRAN - 1; /* 1M alignment on memory space */
42663470Sanish 
42673470Sanish 	rval = ndi_ra_alloc(ddi_get_parent(new_child), &req,
42683470Sanish 	    &pf_mem_answer, &pf_mem_alen,  NDI_RA_TYPE_PCI_PREFETCH_MEM,
42693470Sanish 	    NDI_RA_PASS);
42703470Sanish 
42713470Sanish 	if (rval != NDI_SUCCESS) {
42723470Sanish 		if (rval == NDI_RA_PARTIAL_REQ) {
42733470Sanish 			/*EMPTY*/
42743470Sanish 			DEBUG0("NDI_RA_PARTIAL_REQ returned\n");
42753470Sanish 		} else {
42763470Sanish 			DEBUG0(
42773470Sanish 			    "Failed to allocate PF memory for bridge\n");
42783470Sanish 			/* PF mem is an optional requirement so continue */
42793470Sanish 		}
42803470Sanish 	}
42813470Sanish 
42823470Sanish 	DEBUG3("Bridge PF Memory Allocated [0x%x.%x] len [0x%x]\n",
42833470Sanish 	    PCICFG_HIADDR(pf_mem_answer),
42843470Sanish 	    PCICFG_LOADDR(pf_mem_answer),
42853470Sanish 	    pf_mem_alen);
42863470Sanish 
42873470Sanish 	/*
42883470Sanish 	 * Put available PF memory into the pool.
42893470Sanish 	 */
42903470Sanish 	(void) ndi_ra_free(new_child, pf_mem_answer, pf_mem_alen,
42913470Sanish 	    NDI_RA_TYPE_PCI_PREFETCH_MEM, NDI_RA_PASS);
42923470Sanish 
42933470Sanish 	pf_mem_base = pf_mem_answer;
42943470Sanish 
42953470Sanish 	/*
42963470Sanish 	 * Program the PF memory base register with the
42973470Sanish 	 * start of the memory range
42983470Sanish 	 */
42993470Sanish 	pci_config_put16(h, PCI_BCNF_PF_BASE_LOW,
43003470Sanish 	    PCICFG_HIWORD(PCICFG_LOADDR(pf_mem_answer)));
43013470Sanish 	pci_config_put32(h, PCI_BCNF_PF_BASE_HIGH,
43023470Sanish 	    PCICFG_HIADDR(pf_mem_answer));
43033470Sanish 
43043470Sanish 	/*
43053470Sanish 	 * Program the PF memory limit register with the
43063470Sanish 	 * end of the memory range.
43073470Sanish 	 */
43083470Sanish 	pci_config_put16(h, PCI_BCNF_PF_LIMIT_LOW,
43093470Sanish 	    PCICFG_HIWORD(PCICFG_LOADDR(
43103470Sanish 	    PCICFG_ROUND_DOWN((pf_mem_answer + pf_mem_alen),
43113470Sanish 	    PCICFG_MEMGRAN) - 1)));
43123470Sanish 	pci_config_put32(h, PCI_BCNF_PF_LIMIT_HIGH,
43133470Sanish 	    PCICFG_HIADDR(PCICFG_ROUND_DOWN((pf_mem_answer + pf_mem_alen),
43143470Sanish 	    PCICFG_MEMGRAN) - 1));
43153470Sanish 
43163470Sanish 	/*
43173470Sanish 	 * Allocate the chunk of PF memory (if any) not programmed into the
43183470Sanish 	 * bridge because of the round down.
43193470Sanish 	 */
43203470Sanish 	if (PCICFG_ROUND_DOWN((pf_mem_answer + pf_mem_alen), PCICFG_MEMGRAN)
43213470Sanish 	    != (pf_mem_answer + pf_mem_alen)) {
43223470Sanish 		DEBUG0("Need to allocate Memory round off chunk\n");
43233470Sanish 		bzero((caddr_t)&req, sizeof (ndi_ra_request_t));
43243470Sanish 		req.ra_flags = NDI_RA_ALLOC_SPECIFIED;
43253470Sanish 		req.ra_addr = PCICFG_ROUND_DOWN((pf_mem_answer + pf_mem_alen),
43263470Sanish 		    PCICFG_MEMGRAN);
43273470Sanish 		req.ra_len =  (pf_mem_answer + pf_mem_alen) -
43283470Sanish 		    (PCICFG_ROUND_DOWN((pf_mem_answer + pf_mem_alen),
43293470Sanish 		    PCICFG_MEMGRAN));
43303470Sanish 
43313470Sanish 		(void) ndi_ra_alloc(new_child, &req,
43323470Sanish 		    &round_answer, &round_len,  NDI_RA_TYPE_PCI_PREFETCH_MEM,
43333470Sanish 		    NDI_RA_PASS);
43343470Sanish 	}
43353470Sanish 
43363470Sanish pf_setup_end:
43373470Sanish 
43383470Sanish 	/*
43393470Sanish 	 * Program the memory base register with the
43403470Sanish 	 * start of the memory range
43413470Sanish 	 */
43423470Sanish 	pci_config_put16(h, PCI_BCNF_MEM_BASE,
43433470Sanish 	    PCICFG_HIWORD(PCICFG_LOADDR(mem_answer)));
43443470Sanish 
43453470Sanish 	/*
43463470Sanish 	 * Program the memory limit register with the
43473470Sanish 	 * end of the memory range.
43483470Sanish 	 */
43493470Sanish 
43503470Sanish 	pci_config_put16(h, PCI_BCNF_MEM_LIMIT,
43513470Sanish 	    PCICFG_HIWORD(PCICFG_LOADDR(
43523470Sanish 	    PCICFG_ROUND_DOWN((mem_answer + mem_alen), PCICFG_MEMGRAN) - 1)));
43533470Sanish 
43543470Sanish 	/*
43553470Sanish 	 * Allocate the chunk of memory (if any) not programmed into the
43563470Sanish 	 * bridge because of the round down.
43573470Sanish 	 */
43583470Sanish 	if (PCICFG_ROUND_DOWN((mem_answer + mem_alen), PCICFG_MEMGRAN)
43593470Sanish 	    != (mem_answer + mem_alen)) {
43603470Sanish 		DEBUG0("Need to allocate Memory round off chunk\n");
43613470Sanish 		bzero((caddr_t)&req, sizeof (ndi_ra_request_t));
43623470Sanish 		req.ra_flags = NDI_RA_ALLOC_SPECIFIED;
43633470Sanish 		req.ra_addr = PCICFG_ROUND_DOWN((mem_answer + mem_alen),
43643470Sanish 		    PCICFG_MEMGRAN);
43653470Sanish 		req.ra_len =  (mem_answer + mem_alen) -
43663470Sanish 		    (PCICFG_ROUND_DOWN((mem_answer + mem_alen),
43673470Sanish 		    PCICFG_MEMGRAN));
43683470Sanish 
43693470Sanish 		(void) ndi_ra_alloc(new_child, &req,
43703470Sanish 		    &round_answer, &round_len,  NDI_RA_TYPE_MEM, NDI_RA_PASS);
43713470Sanish 	}
43723470Sanish 
43733470Sanish 	/*
43743470Sanish 	 * Program the I/O Space Base
43753470Sanish 	 */
43763470Sanish 	pci_config_put8(h, PCI_BCNF_IO_BASE_LOW,
43773470Sanish 	    PCICFG_HIBYTE(PCICFG_LOWORD(
43783470Sanish 	    PCICFG_LOADDR(io_answer))));
43793470Sanish 
43803470Sanish 	pci_config_put16(h, PCI_BCNF_IO_BASE_HI,
43813470Sanish 	    PCICFG_HIWORD(PCICFG_LOADDR(io_answer)));
43823470Sanish 
43833470Sanish 	/*
43843470Sanish 	 * Program the I/O Space Limit
43853470Sanish 	 */
43863470Sanish 	pci_config_put8(h, PCI_BCNF_IO_LIMIT_LOW,
43873470Sanish 	    PCICFG_HIBYTE(PCICFG_LOWORD(
43883470Sanish 	    PCICFG_LOADDR(PCICFG_ROUND_DOWN(io_answer + io_alen,
43893470Sanish 	    PCICFG_IOGRAN)))) - 1);
43903470Sanish 
43913470Sanish 	pci_config_put16(h, PCI_BCNF_IO_LIMIT_HI,
43923470Sanish 	    PCICFG_HIWORD(PCICFG_LOADDR(
43933470Sanish 	    PCICFG_ROUND_DOWN(io_answer + io_alen, PCICFG_IOGRAN)))
43943470Sanish 	    - 1);
43953470Sanish 
43963470Sanish 	/*
43973470Sanish 	 * Allocate the chunk of I/O (if any) not programmed into the
43983470Sanish 	 * bridge because of the round down.
43993470Sanish 	 */
44003470Sanish 	if (PCICFG_ROUND_DOWN((io_answer + io_alen), PCICFG_IOGRAN)
44013470Sanish 	    != (io_answer + io_alen)) {
44023470Sanish 		DEBUG0("Need to allocate I/O round off chunk\n");
44033470Sanish 		bzero((caddr_t)&req, sizeof (ndi_ra_request_t));
44043470Sanish 		req.ra_flags = NDI_RA_ALLOC_SPECIFIED;
44053470Sanish 		req.ra_addr = PCICFG_ROUND_DOWN((io_answer + io_alen),
44063470Sanish 		    PCICFG_IOGRAN);
44073470Sanish 		req.ra_len =  (io_answer + io_alen) -
44083470Sanish 		    (PCICFG_ROUND_DOWN((io_answer + io_alen),
44093470Sanish 		    PCICFG_IOGRAN));
44103470Sanish 
44113470Sanish 		(void) ndi_ra_alloc(new_child, &req,
44123470Sanish 		    &round_answer, &round_len,  NDI_RA_TYPE_IO, NDI_RA_PASS);
44133470Sanish 	}
44143470Sanish 
44153470Sanish 	(void) pcicfg_set_bus_numbers(h, bus, new_bus, max_bus);
44163470Sanish 
44173470Sanish 	/*
44189556SZhijun.Fu@Sun.COM 	 * Setup "ranges" and "bus-range" properties before onlining
44199556SZhijun.Fu@Sun.COM 	 * the bridge.
44209556SZhijun.Fu@Sun.COM 	 */
44219556SZhijun.Fu@Sun.COM 	bzero((caddr_t)range, sizeof (ppb_ranges_t) * PCICFG_RANGE_LEN);
44229556SZhijun.Fu@Sun.COM 
44239556SZhijun.Fu@Sun.COM 	range[0].child_high = range[0].parent_high |= (PCI_REG_REL_M |
44249556SZhijun.Fu@Sun.COM 	    PCI_ADDR_IO);
44259556SZhijun.Fu@Sun.COM 	range[0].child_low = range[0].parent_low = io_base;
44269556SZhijun.Fu@Sun.COM 	range[1].child_high = range[1].parent_high |=
44279556SZhijun.Fu@Sun.COM 	    (PCI_REG_REL_M | PCI_ADDR_MEM32);
44289556SZhijun.Fu@Sun.COM 	range[1].child_low = range[1].parent_low = mem_base;
44299556SZhijun.Fu@Sun.COM 	range[2].child_high = range[2].parent_high |=
44309556SZhijun.Fu@Sun.COM 	    (PCI_REG_REL_M | PCI_ADDR_MEM64 | PCI_REG_PF_M);
44319556SZhijun.Fu@Sun.COM 	range[2].child_low = range[2].parent_low = pf_mem_base;
44329556SZhijun.Fu@Sun.COM 
44339556SZhijun.Fu@Sun.COM 	range[0].size_low = io_alen;
44349556SZhijun.Fu@Sun.COM 	(void) pcicfg_update_ranges_prop(new_child, &range[0]);
44359556SZhijun.Fu@Sun.COM 	range[1].size_low = mem_alen;
44369556SZhijun.Fu@Sun.COM 	(void) pcicfg_update_ranges_prop(new_child, &range[1]);
44379556SZhijun.Fu@Sun.COM 	range[2].size_low = pf_mem_alen;
44389556SZhijun.Fu@Sun.COM 	(void) pcicfg_update_ranges_prop(new_child, &range[2]);
44399556SZhijun.Fu@Sun.COM 
44409556SZhijun.Fu@Sun.COM 	bus_range[0] = new_bus;
44419556SZhijun.Fu@Sun.COM 	bus_range[1] = max_bus;
44429556SZhijun.Fu@Sun.COM 	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, new_child,
44439556SZhijun.Fu@Sun.COM 	    "bus-range", bus_range, 2);
44449556SZhijun.Fu@Sun.COM 
44459556SZhijun.Fu@Sun.COM 	/*
44463470Sanish 	 * Reset the secondary bus
44473470Sanish 	 */
44483470Sanish 	pci_config_put16(h, PCI_BCNF_BCNTRL,
44493470Sanish 	    pci_config_get16(h, PCI_BCNF_BCNTRL) | 0x40);
44503470Sanish 
44513470Sanish 	drv_usecwait(100);
44523470Sanish 
44533470Sanish 	pci_config_put16(h, PCI_BCNF_BCNTRL,
44543470Sanish 	    pci_config_get16(h, PCI_BCNF_BCNTRL) & ~0x40);
44553470Sanish 
44563470Sanish 	/*
44573470Sanish 	 * Clear status bits
44583470Sanish 	 */
44593470Sanish 	pci_config_put16(h, PCI_BCNF_SEC_STATUS, 0xffff);
44603470Sanish 
44613470Sanish 	/*
44623470Sanish 	 * Needs to be set to this value
44633470Sanish 	 */
44643470Sanish 	pci_config_put8(h, PCI_CONF_ILINE, 0xf);
44653470Sanish 
44663470Sanish 	/* check our device_type as defined by Open Firmware */
44673470Sanish 	if (pcicfg_pcie_device_type(new_child, h) == DDI_SUCCESS)
44683470Sanish 		pcie_device_type = 1;
44693470Sanish 
44703470Sanish 	/*
44713470Sanish 	 * Set bus properties
44723470Sanish 	 */
44733470Sanish 	if (pcicfg_set_busnode_props(new_child, pcie_device_type)
447410923SEvan.Yan@Sun.COM 	    != PCICFG_SUCCESS) {
44753470Sanish 		DEBUG0("Failed to set busnode props\n");
44763470Sanish 		rval = PCICFG_FAILURE;
44773470Sanish 		goto cleanup;
44783470Sanish 	}
44793470Sanish 
44803470Sanish 	(void) pcicfg_device_on(h);
44813470Sanish 
448211245SZhijun.Fu@Sun.COM 	if (is_pcie)
448311387SSurya.Prakki@Sun.COM 		(void) pcie_init_bus(new_child, 0, PCIE_BUS_FINAL);
44843470Sanish 	if (ndi_devi_online(new_child, NDI_NO_EVENT|NDI_CONFIG)
44853470Sanish 	    != NDI_SUCCESS) {
44863470Sanish 		DEBUG0("Unable to online bridge\n");
44873470Sanish 		rval = PCICFG_FAILURE;
44883470Sanish 		goto cleanup;
44893470Sanish 	}
44903470Sanish 
44913470Sanish 	DEBUG0("Bridge is ONLINE\n");
44923470Sanish 
44933470Sanish 	/*
44943470Sanish 	 * After a Reset, we need to wait 2^25 clock cycles before the
44953470Sanish 	 * first Configuration access.  The worst case is 33MHz, which
44963470Sanish 	 * is a 1 second wait.
44973470Sanish 	 */
44983470Sanish 	drv_usecwait(pcicfg_sec_reset_delay);
44993470Sanish 
45003470Sanish 	/*
45013470Sanish 	 * Probe all children devices
45023470Sanish 	 */
45033470Sanish 	DEBUG0("Bridge Programming Complete - probe children\n");
45043470Sanish 	ndi_devi_enter(new_child, &count);
450510923SEvan.Yan@Sun.COM 	for (i = 0; ((i < PCI_MAX_DEVICES) && (ari_mode == B_FALSE));
450610923SEvan.Yan@Sun.COM 	    i++) {
450710923SEvan.Yan@Sun.COM 		for (j = 0; j < max_function; ) {
450810923SEvan.Yan@Sun.COM 			if (ari_mode)
450910923SEvan.Yan@Sun.COM 				trans_device = j >> 3;
451010923SEvan.Yan@Sun.COM 			else
451110923SEvan.Yan@Sun.COM 				trans_device = i;
451210923SEvan.Yan@Sun.COM 
45133470Sanish 			if ((rval = pcicfg_probe_children(new_child,
451411245SZhijun.Fu@Sun.COM 			    new_bus, trans_device, j & 7, highest_bus,
451511245SZhijun.Fu@Sun.COM 			    0, is_pcie)) != PCICFG_SUCCESS) {
45163470Sanish 				if (rval == PCICFG_NODEVICE) {
45173470Sanish 					DEBUG3("No Device at bus [0x%x]"
451810923SEvan.Yan@Sun.COM 					    "device [0x%x] "
451910923SEvan.Yan@Sun.COM 					    "func [0x%x]\n", new_bus,
452010923SEvan.Yan@Sun.COM 					    trans_device, j & 7);
452110923SEvan.Yan@Sun.COM 
45223470Sanish 					if (j)
452310923SEvan.Yan@Sun.COM 						goto next;
45243470Sanish 				} else
45253470Sanish 					/*EMPTY*/
45263470Sanish 					DEBUG3("Failed to configure bus "
452710923SEvan.Yan@Sun.COM 					    "[0x%x] device [0x%x] "
452810923SEvan.Yan@Sun.COM 					    "func [0x%x]\n", new_bus,
452910923SEvan.Yan@Sun.COM 					    trans_device, j & 7);
45303470Sanish 				break;
45313470Sanish 			}
453210923SEvan.Yan@Sun.COM next:
453310923SEvan.Yan@Sun.COM 			new_device = pcicfg_devi_find(new_child, trans_device,
453410923SEvan.Yan@Sun.COM 			    (j & 7));
453510923SEvan.Yan@Sun.COM 
453610923SEvan.Yan@Sun.COM 			/*
453710923SEvan.Yan@Sun.COM 			 * Determine if ARI Forwarding should be enabled.
453810923SEvan.Yan@Sun.COM 			 */
453910923SEvan.Yan@Sun.COM 			if (j == 0) {
454010923SEvan.Yan@Sun.COM 				if (new_device == NULL)
454110923SEvan.Yan@Sun.COM 					break;
454210923SEvan.Yan@Sun.COM 
454310923SEvan.Yan@Sun.COM 				if ((pcie_ari_supported(new_child) ==
454410923SEvan.Yan@Sun.COM 				    PCIE_ARI_FORW_SUPPORTED) &&
454510923SEvan.Yan@Sun.COM 				    (pcie_ari_device(new_device) ==
454610923SEvan.Yan@Sun.COM 				    PCIE_ARI_DEVICE)) {
454710923SEvan.Yan@Sun.COM 					if (pcie_ari_enable(new_child) ==
454810923SEvan.Yan@Sun.COM 					    DDI_SUCCESS) {
454910923SEvan.Yan@Sun.COM 						(void) ddi_prop_create(
455010923SEvan.Yan@Sun.COM 						    DDI_DEV_T_NONE,
455110923SEvan.Yan@Sun.COM 						    new_child,
455210923SEvan.Yan@Sun.COM 						    DDI_PROP_CANSLEEP,
455310923SEvan.Yan@Sun.COM 						    "ari-enabled", NULL, 0);
455410923SEvan.Yan@Sun.COM 						ari_mode = B_TRUE;
455510923SEvan.Yan@Sun.COM 						max_function =
455610923SEvan.Yan@Sun.COM 						    PCICFG_MAX_ARI_FUNCTION;
455710923SEvan.Yan@Sun.COM 					}
455810923SEvan.Yan@Sun.COM 				}
455910923SEvan.Yan@Sun.COM 			}
456010923SEvan.Yan@Sun.COM 			if (ari_mode == B_TRUE) {
456110923SEvan.Yan@Sun.COM 				int next_function;
456210923SEvan.Yan@Sun.COM 
456310923SEvan.Yan@Sun.COM 				if (new_device == NULL)
456410923SEvan.Yan@Sun.COM 					break;
456510923SEvan.Yan@Sun.COM 
456610923SEvan.Yan@Sun.COM 				if (pcie_ari_get_next_function(new_device,
456710923SEvan.Yan@Sun.COM 				    &next_function) != DDI_SUCCESS)
456810923SEvan.Yan@Sun.COM 					break;
456910923SEvan.Yan@Sun.COM 
457010923SEvan.Yan@Sun.COM 				j = next_function;
457110923SEvan.Yan@Sun.COM 
457210923SEvan.Yan@Sun.COM 				if (next_function == 0)
457310923SEvan.Yan@Sun.COM 					break;
457410923SEvan.Yan@Sun.COM 			} else
457510923SEvan.Yan@Sun.COM 				j++;
457610923SEvan.Yan@Sun.COM 
45773470Sanish 		}
45783470Sanish 		/* if any function fails to be configured, no need to proceed */
45793470Sanish 		if (rval != PCICFG_NODEVICE)
45803470Sanish 			break;
45813470Sanish 	}
45823470Sanish 	ndi_devi_exit(new_child, count);
45833470Sanish 
45843470Sanish 	/*
45853470Sanish 	 * Offline the bridge to allow reprogramming of resources.
458610923SEvan.Yan@Sun.COM 	 *
458710923SEvan.Yan@Sun.COM 	 * This should always succeed since nobody else has started to
458810923SEvan.Yan@Sun.COM 	 * use it yet, failing to detach the driver would indicate a bug.
458910923SEvan.Yan@Sun.COM 	 * Also in that case it's better just panic than allowing the
459010923SEvan.Yan@Sun.COM 	 * configurator to proceed with BAR reprogramming without bridge
459110923SEvan.Yan@Sun.COM 	 * driver detached.
45923470Sanish 	 */
459310923SEvan.Yan@Sun.COM 	VERIFY(ndi_devi_offline(new_child, NDI_NO_EVENT|NDI_UNCONFIG)
459410923SEvan.Yan@Sun.COM 	    == NDI_SUCCESS);
459511245SZhijun.Fu@Sun.COM 	if (is_pcie)
459611245SZhijun.Fu@Sun.COM 		pcie_fini_bus(new_child, PCIE_BUS_INITIAL);
45973470Sanish 
45983470Sanish 	phdl.dip = new_child;
45993470Sanish 	phdl.memory_base = mem_answer;
46003470Sanish 	phdl.io_base = io_answer;
46013470Sanish 	phdl.pf_memory_base = pf_mem_answer;
46023470Sanish 	phdl.error = PCICFG_SUCCESS;	/* in case of empty child tree */
46033470Sanish 
46043470Sanish 	ndi_devi_enter(ddi_get_parent(new_child), &count);
46053470Sanish 	ddi_walk_devs(new_child, pcicfg_find_resource_end, (void *)&phdl);
46063470Sanish 	ndi_devi_exit(ddi_get_parent(new_child), count);
46073470Sanish 
46083470Sanish 	num_slots = pcicfg_get_nslots(new_child, h);
46093470Sanish 	mem_end = PCICFG_ROUND_UP(phdl.memory_base, PCICFG_MEMGRAN);
46103470Sanish 	io_end = PCICFG_ROUND_UP(phdl.io_base, PCICFG_IOGRAN);
46113470Sanish 	pf_mem_end = PCICFG_ROUND_UP(phdl.pf_memory_base, PCICFG_MEMGRAN);
46123470Sanish 
461310923SEvan.Yan@Sun.COM 	DEBUG4("Start of Unallocated Bridge(%d slots) Resources Mem=0x%lx "
461410923SEvan.Yan@Sun.COM 	    "I/O=0x%lx PF_mem=%x%lx\n", num_slots, mem_end, io_end, pf_mem_end);
461510923SEvan.Yan@Sun.COM 
461610923SEvan.Yan@Sun.COM 	/*
461710923SEvan.Yan@Sun.COM 	 * Before probing the children we've allocated maximum MEM/IO
461810923SEvan.Yan@Sun.COM 	 * resources from parent, and updated "available" property
461910923SEvan.Yan@Sun.COM 	 * accordingly. Later we'll be giving up unused resources to
462010923SEvan.Yan@Sun.COM 	 * the parent, thus we need to destroy "available" property
462110923SEvan.Yan@Sun.COM 	 * here otherwise it will be out-of-sync with the actual free
462210923SEvan.Yan@Sun.COM 	 * resources this bridge has. This property will be rebuilt below
462310923SEvan.Yan@Sun.COM 	 * with the actual free resources reserved for hotplug slots
462410923SEvan.Yan@Sun.COM 	 * (if any).
462510923SEvan.Yan@Sun.COM 	 */
462610923SEvan.Yan@Sun.COM 	(void) ndi_prop_remove(DDI_DEV_T_NONE, new_child, "available");
46273470Sanish 	/*
46283470Sanish 	 * if the bridge a slots, then preallocate. If not, assume static
46293470Sanish 	 * configuration. Also check for preallocation limits and spit
46303470Sanish 	 * warning messages appropriately (perhaps some can be in debug mode).
46313470Sanish 	 */
46323470Sanish 	if (num_slots) {
463310923SEvan.Yan@Sun.COM 		uint64_t mem_reqd = mem_answer +
463410923SEvan.Yan@Sun.COM 		    (num_slots * pcicfg_slot_memsize);
463510923SEvan.Yan@Sun.COM 		uint64_t io_reqd = io_answer +
463610923SEvan.Yan@Sun.COM 		    (num_slots * pcicfg_slot_iosize);
463710923SEvan.Yan@Sun.COM 		uint64_t pf_mem_reqd = pf_mem_answer +
463810923SEvan.Yan@Sun.COM 		    (num_slots * pcicfg_slot_pf_memsize);
463910923SEvan.Yan@Sun.COM 		uint8_t highest_bus_reqd = new_bus +
464010923SEvan.Yan@Sun.COM 		    (num_slots * pcicfg_slot_busnums);
46416116Sarutz #ifdef DEBUG
46423470Sanish 		if (mem_end > mem_reqd)
46436116Sarutz 			DEBUG3("Memory space consumed by bridge more "
46443470Sanish 			    "than planned for %d slot(s)(%" PRIx64 ",%"
46453470Sanish 			    PRIx64 ")", num_slots, mem_answer, mem_end);
46463470Sanish 		if (io_end > io_reqd)
46476116Sarutz 			DEBUG3("IO space consumed by bridge more than"
46483470Sanish 			    " planned for %d slot(s)(%" PRIx64 ",%" PRIx64 ")",
46493470Sanish 			    num_slots, io_answer, io_end);
46503470Sanish 		if (pf_mem_end > pf_mem_reqd)
46516116Sarutz 			DEBUG3("PF Memory space consumed by bridge"
46523470Sanish 			    " more than planned for %d slot(s)(%" PRIx64 ",%"
46533470Sanish 			    PRIx64 ")", num_slots, pf_mem_answer, pf_mem_end);
46543470Sanish 		if (*highest_bus > highest_bus_reqd)
46556116Sarutz 			DEBUG3("Buses consumed by bridge more "
46563470Sanish 			    "than planned for %d slot(s)(%x, %x)",
46573470Sanish 			    num_slots, new_bus, *highest_bus);
46583470Sanish 
46593470Sanish 		if (mem_reqd > (mem_answer + mem_alen))
46606116Sarutz 			DEBUG3("Memory space required by bridge more "
46613470Sanish 			    "than available for %d slot(s)(%" PRIx64 ",%"
46623470Sanish 			    PRIx64 ")", num_slots, mem_answer, mem_end);
46633470Sanish 		if (io_reqd > (io_answer + io_alen))
46646116Sarutz 			DEBUG3("IO space required by bridge more than"
46653470Sanish 			    "available for %d slot(s)(%" PRIx64 ",%" PRIx64 ")",
46663470Sanish 			    num_slots, io_answer, io_end);
46673470Sanish 		if (pf_mem_reqd > (pf_mem_answer + pf_mem_alen))
46686116Sarutz 			DEBUG3("PF Memory space required by bridge"
46693470Sanish 			    " more than available for %d slot(s)(%" PRIx64 ",%"
46703470Sanish 			    PRIx64 ")", num_slots, pf_mem_answer, pf_mem_end);
46713470Sanish 		if (highest_bus_reqd > max_bus)
46726116Sarutz 			DEBUG3("Bus numbers required by bridge more "
46733470Sanish 			    "than available for %d slot(s)(%x, %x)",
46743470Sanish 			    num_slots, new_bus, *highest_bus);
46756116Sarutz #endif
46763470Sanish 		mem_end = MAX((MIN(mem_reqd, (mem_answer + mem_alen))),
467710923SEvan.Yan@Sun.COM 		    mem_end);
46783470Sanish 		io_end = MAX((MIN(io_reqd, (io_answer + io_alen))), io_end);
46793470Sanish 		pf_mem_end = MAX((MIN(pf_mem_reqd, (pf_mem_answer +
468010923SEvan.Yan@Sun.COM 		    pf_mem_alen))), pf_mem_end);
46813470Sanish 		*highest_bus = MAX((MIN(highest_bus_reqd, max_bus)),
468210923SEvan.Yan@Sun.COM 		    *highest_bus);
46833470Sanish 		DEBUG4("mem_end %lx, io_end %lx, pf_mem_end %lx"
468410923SEvan.Yan@Sun.COM 		    " highest_bus %x\n", mem_end, io_end, pf_mem_end,
468510923SEvan.Yan@Sun.COM 		    *highest_bus);
46863470Sanish 	}
46873470Sanish 
46883470Sanish 	/*
46893470Sanish 	 * Give back unused memory space to parent.
46903470Sanish 	 */
469110923SEvan.Yan@Sun.COM 	(void) ndi_ra_free(ddi_get_parent(new_child), mem_end,
469210923SEvan.Yan@Sun.COM 	    (mem_answer + mem_alen) - mem_end, NDI_RA_TYPE_MEM, NDI_RA_PASS);
46933470Sanish 
46943470Sanish 	if (mem_end == mem_answer) {
46953470Sanish 		DEBUG0("No memory resources used\n");
46963470Sanish 		/*
46973470Sanish 		 * To prevent the bridge from forwarding any Memory
46983470Sanish 		 * transactions, the Memory Limit will be programmed
46993470Sanish 		 * with a smaller value than the Memory Base.
47003470Sanish 		 */
47013470Sanish 		pci_config_put16(h, PCI_BCNF_MEM_BASE, 0xffff);
47023470Sanish 		pci_config_put16(h, PCI_BCNF_MEM_LIMIT, 0);
47033470Sanish 
47043470Sanish 		mem_size = 0;
47053470Sanish 	} else {
47063470Sanish 		/*
47073470Sanish 		 * Reprogram the end of the memory.
47083470Sanish 		 */
47093470Sanish 		pci_config_put16(h, PCI_BCNF_MEM_LIMIT,
47103470Sanish 		    PCICFG_HIWORD(mem_end) - 1);
47113470Sanish 		mem_size = mem_end - mem_base;
47123470Sanish 	}
47133470Sanish 
47143470Sanish 	/*
47153470Sanish 	 * Give back unused io space to parent.
47163470Sanish 	 */
47173470Sanish 	(void) ndi_ra_free(ddi_get_parent(new_child),
47183470Sanish 	    io_end, (io_answer + io_alen) - io_end,
47193470Sanish 	    NDI_RA_TYPE_IO, NDI_RA_PASS);
47203470Sanish 
47213470Sanish 	if (io_end == io_answer) {
47223470Sanish 		DEBUG0("No IO Space resources used\n");
47233470Sanish 
47243470Sanish 		/*
47253470Sanish 		 * To prevent the bridge from forwarding any I/O
47263470Sanish 		 * transactions, the I/O Limit will be programmed
47273470Sanish 		 * with a smaller value than the I/O Base.
47283470Sanish 		 */
47293470Sanish 		pci_config_put8(h, PCI_BCNF_IO_LIMIT_LOW, 0);
47303470Sanish 		pci_config_put16(h, PCI_BCNF_IO_LIMIT_HI, 0);
47313470Sanish 		pci_config_put8(h, PCI_BCNF_IO_BASE_LOW, 0xff);
47323470Sanish 		pci_config_put16(h, PCI_BCNF_IO_BASE_HI, 0);
47333470Sanish 
47343470Sanish 		io_size = 0;
47353470Sanish 	} else {
47363470Sanish 		/*
47373470Sanish 		 * Reprogram the end of the io space.
47383470Sanish 		 */
47393470Sanish 		pci_config_put8(h, PCI_BCNF_IO_LIMIT_LOW,
47403470Sanish 		    PCICFG_HIBYTE(PCICFG_LOWORD(
47413470Sanish 		    PCICFG_LOADDR(io_end) - 1)));
47423470Sanish 
47433470Sanish 		pci_config_put16(h, PCI_BCNF_IO_LIMIT_HI,
47443470Sanish 		    PCICFG_HIWORD(PCICFG_LOADDR(io_end - 1)));
47453470Sanish 
47463470Sanish 		io_size = io_end - io_base;
47473470Sanish 	}
47483470Sanish 
47493470Sanish 	/*
47503470Sanish 	 * Give back unused PF memory space to parent.
47513470Sanish 	 */
47523470Sanish 	if (pf_mem_supported) {
475310923SEvan.Yan@Sun.COM 		(void) ndi_ra_free(ddi_get_parent(new_child),
475410923SEvan.Yan@Sun.COM 		    pf_mem_end, (pf_mem_answer + pf_mem_alen) - pf_mem_end,
475510923SEvan.Yan@Sun.COM 		    NDI_RA_TYPE_PCI_PREFETCH_MEM, NDI_RA_PASS);
475610923SEvan.Yan@Sun.COM 
475710923SEvan.Yan@Sun.COM 		if (pf_mem_end == pf_mem_answer) {
475810923SEvan.Yan@Sun.COM 			DEBUG0("No PF memory resources used\n");
475910923SEvan.Yan@Sun.COM 			/*
476010923SEvan.Yan@Sun.COM 			 * To prevent the bridge from forwarding any PF Memory
476110923SEvan.Yan@Sun.COM 			 * transactions, the PF Memory Limit will be programmed
476210923SEvan.Yan@Sun.COM 			 * with a smaller value than the Memory Base.
476310923SEvan.Yan@Sun.COM 			 */
476410923SEvan.Yan@Sun.COM 			pci_config_put16(h, PCI_BCNF_PF_BASE_LOW, 0xfff0);
476510923SEvan.Yan@Sun.COM 			pci_config_put32(h, PCI_BCNF_PF_BASE_HIGH, 0xffffffff);
476610923SEvan.Yan@Sun.COM 			pci_config_put16(h, PCI_BCNF_PF_LIMIT_LOW, 0);
476710923SEvan.Yan@Sun.COM 			pci_config_put32(h, PCI_BCNF_PF_LIMIT_HIGH, 0);
476810923SEvan.Yan@Sun.COM 
476910923SEvan.Yan@Sun.COM 			pf_mem_size = 0;
477010923SEvan.Yan@Sun.COM 		} else {
477110923SEvan.Yan@Sun.COM 			/*
477210923SEvan.Yan@Sun.COM 			 * Reprogram the end of the PF memory range.
477310923SEvan.Yan@Sun.COM 			 */
477410923SEvan.Yan@Sun.COM 			pci_config_put16(h, PCI_BCNF_PF_LIMIT_LOW,
477510923SEvan.Yan@Sun.COM 			    PCICFG_HIWORD(PCICFG_LOADDR(pf_mem_end - 1)));
477610923SEvan.Yan@Sun.COM 			pci_config_put32(h, PCI_BCNF_PF_LIMIT_HIGH,
477710923SEvan.Yan@Sun.COM 			    PCICFG_HIADDR(pf_mem_end - 1));
477810923SEvan.Yan@Sun.COM 			pf_mem_size = pf_mem_end - pf_mem_base;
477910923SEvan.Yan@Sun.COM 		}
47803470Sanish 	}
47813470Sanish 
47823470Sanish 	if ((max_bus - *highest_bus) > 0) {
47833470Sanish 		/*
47843470Sanish 		 * Give back unused bus numbers
47853470Sanish 		 */
47863470Sanish 		(void) ndi_ra_free(ddi_get_parent(new_child),
478710923SEvan.Yan@Sun.COM 		    *highest_bus+1, max_bus - *highest_bus,
478810923SEvan.Yan@Sun.COM 		    NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS);
47893470Sanish 	}
47903470Sanish 
47913470Sanish 	/*
47923470Sanish 	 * Set bus numbers to ranges encountered during scan
47933470Sanish 	 */
47943470Sanish 	(void) pcicfg_set_bus_numbers(h, bus, new_bus, *highest_bus);
47953470Sanish 
47963470Sanish 	/*
47973470Sanish 	 * Remove the ranges property if it exists since we will create
47983470Sanish 	 * a new one.
47993470Sanish 	 */
48003470Sanish 	(void) ndi_prop_remove(DDI_DEV_T_NONE, new_child, "ranges");
48013470Sanish 
48023470Sanish 	DEBUG2("Creating Ranges property - Mem Address %lx Mem Size %x\n",
48033470Sanish 	    mem_base, mem_size);
48043470Sanish 	DEBUG2("                         - I/O Address %lx I/O Size %x\n",
48053470Sanish 	    io_base, io_size);
48063470Sanish 	DEBUG2("                         - PF Mem address %lx PF Mem Size %x\n",
48073470Sanish 	    pf_mem_base, pf_mem_size);
48083470Sanish 
48093470Sanish 	bzero((caddr_t)range, sizeof (ppb_ranges_t) * PCICFG_RANGE_LEN);
48103470Sanish 
48113470Sanish 	range[0].child_high = range[0].parent_high |= (PCI_REG_REL_M |
48123470Sanish 	    PCI_ADDR_IO);
48133470Sanish 	range[0].child_low = range[0].parent_low = io_base;
48143470Sanish 	range[1].child_high = range[1].parent_high |=
48153470Sanish 	    (PCI_REG_REL_M | PCI_ADDR_MEM32);
48163470Sanish 	range[1].child_low = range[1].parent_low = mem_base;
48173470Sanish 	range[2].child_high = range[2].parent_high |=
48183470Sanish 	    (PCI_REG_REL_M | PCI_ADDR_MEM64 | PCI_REG_PF_M);
48193470Sanish 	range[2].child_low = range[2].parent_low = pf_mem_base;
48203470Sanish 
48213470Sanish 	if (io_size > 0) {
48223470Sanish 		range[0].size_low = io_size;
48233470Sanish 		(void) pcicfg_update_ranges_prop(new_child, &range[0]);
48243470Sanish 	}
48253470Sanish 	if (mem_size > 0) {
48263470Sanish 		range[1].size_low = mem_size;
48273470Sanish 		(void) pcicfg_update_ranges_prop(new_child, &range[1]);
48283470Sanish 	}
48293470Sanish 	if (pf_mem_size > 0) {
48303470Sanish 		range[2].size_low = pf_mem_size;
48313470Sanish 		(void) pcicfg_update_ranges_prop(new_child, &range[2]);
48323470Sanish 	}
48333470Sanish 
48343470Sanish 	bus_range[0] = pci_config_get8(h, PCI_BCNF_SECBUS);
48353470Sanish 	bus_range[1] = pci_config_get8(h, PCI_BCNF_SUBBUS);
48363470Sanish 	DEBUG1("End of bridge probe: bus_range[0] =  %d\n", bus_range[0]);
48373470Sanish 	DEBUG1("End of bridge probe: bus_range[1] =  %d\n", bus_range[1]);
48383470Sanish 
48393470Sanish 	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, new_child,
48403470Sanish 	    "bus-range", bus_range, 2);
48413470Sanish 
48423470Sanish 	rval = PCICFG_SUCCESS;
48433470Sanish 
48443470Sanish 	PCICFG_DUMP_BRIDGE_CONFIG(h);
48453470Sanish 
48463470Sanish cleanup:
48473470Sanish 	/* free up resources (for error return case only) */
48483470Sanish 	if (rval != PCICFG_SUCCESS) {
484910923SEvan.Yan@Sun.COM 		if (mem_alen)
485010923SEvan.Yan@Sun.COM 			(void) ndi_ra_free(ddi_get_parent(new_child), mem_base,
485110923SEvan.Yan@Sun.COM 			    mem_alen, NDI_RA_TYPE_MEM, NDI_RA_PASS);
485210923SEvan.Yan@Sun.COM 		if (io_alen)
485310923SEvan.Yan@Sun.COM 			(void) ndi_ra_free(ddi_get_parent(new_child), io_base,
485410923SEvan.Yan@Sun.COM 			    io_alen, NDI_RA_TYPE_IO, NDI_RA_PASS);
485510923SEvan.Yan@Sun.COM 		if (pf_mem_alen)
485610923SEvan.Yan@Sun.COM 			(void) ndi_ra_free(ddi_get_parent(new_child),
485710923SEvan.Yan@Sun.COM 			    pf_mem_base, pf_mem_alen,
485810923SEvan.Yan@Sun.COM 			    NDI_RA_TYPE_PCI_PREFETCH_MEM, NDI_RA_PASS);
485910923SEvan.Yan@Sun.COM 		if (pcibus_alen)
486010923SEvan.Yan@Sun.COM 			(void) ndi_ra_free(ddi_get_parent(new_child),
486110923SEvan.Yan@Sun.COM 			    pcibus_base, pcibus_alen, NDI_RA_TYPE_PCI_BUSNUM,
486210923SEvan.Yan@Sun.COM 			    NDI_RA_PASS);
48633470Sanish 	}
48643470Sanish 
48653470Sanish 	/* free up any resource maps setup for the bridge node */
48663470Sanish 	(void) ndi_ra_map_destroy(new_child, NDI_RA_TYPE_PCI_BUSNUM);
48673470Sanish 	(void) ndi_ra_map_destroy(new_child, NDI_RA_TYPE_IO);
48683470Sanish 	(void) ndi_ra_map_destroy(new_child, NDI_RA_TYPE_MEM);
48693470Sanish 	(void) ndi_ra_map_destroy(new_child, NDI_RA_TYPE_PCI_PREFETCH_MEM);
48703470Sanish 
48713470Sanish 	return (rval);
48723470Sanish }
48733470Sanish 
48743470Sanish static int
pcicfg_find_resource_end(dev_info_t * dip,void * hdl)48753470Sanish pcicfg_find_resource_end(dev_info_t *dip, void *hdl)
48763470Sanish {
48773470Sanish 	pcicfg_phdl_t *entry = (pcicfg_phdl_t *)hdl;
48783470Sanish 	pci_regspec_t *pci_ap;
48793470Sanish 	int length;
48803470Sanish 	int rcount;
48813470Sanish 	int i;
48823470Sanish 
48833470Sanish 	entry->error = PCICFG_SUCCESS;
48843470Sanish 
48853470Sanish 	if (dip == entry->dip) {
48863470Sanish 		DEBUG0("Don't include parent bridge node\n");
48873470Sanish 		return (DDI_WALK_CONTINUE);
48883470Sanish 	} else {
48893470Sanish 		if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
48903470Sanish 		    DDI_PROP_DONTPASS, "assigned-addresses",
48913470Sanish 		    (caddr_t)&pci_ap,  &length) != DDI_PROP_SUCCESS) {
48923470Sanish 			DEBUG0("Node doesn't have assigned-addresses\n");
48933470Sanish 			return (DDI_WALK_CONTINUE);
48943470Sanish 		}
48953470Sanish 
48963470Sanish 		rcount = length / sizeof (pci_regspec_t);
48973470Sanish 
48983470Sanish 		for (i = 0; i < rcount; i++) {
48993470Sanish 
49003470Sanish 			switch (PCI_REG_ADDR_G(pci_ap[i].pci_phys_hi)) {
49013470Sanish 
49023470Sanish 			case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
490310923SEvan.Yan@Sun.COM 				if (pci_ap[i].pci_phys_hi & PCI_REG_PF_M) {
490410923SEvan.Yan@Sun.COM 					if ((pci_ap[i].pci_phys_low +
490510923SEvan.Yan@Sun.COM 					    pci_ap[i].pci_size_low) >
490610923SEvan.Yan@Sun.COM 					    entry->pf_memory_base) {
490710923SEvan.Yan@Sun.COM 						entry->pf_memory_base =
490810923SEvan.Yan@Sun.COM 						    pci_ap[i].pci_phys_low +
490910923SEvan.Yan@Sun.COM 						    pci_ap[i].pci_size_low;
491010923SEvan.Yan@Sun.COM 					}
491110923SEvan.Yan@Sun.COM 				} else {
491210923SEvan.Yan@Sun.COM 					if ((pci_ap[i].pci_phys_low +
491310923SEvan.Yan@Sun.COM 					    pci_ap[i].pci_size_low) >
491410923SEvan.Yan@Sun.COM 					    entry->memory_base) {
491510923SEvan.Yan@Sun.COM 						entry->memory_base =
491610923SEvan.Yan@Sun.COM 						    pci_ap[i].pci_phys_low +
491710923SEvan.Yan@Sun.COM 						    pci_ap[i].pci_size_low;
491810923SEvan.Yan@Sun.COM 					}
49193470Sanish 				}
492010923SEvan.Yan@Sun.COM 				break;
49213470Sanish 			case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
492210923SEvan.Yan@Sun.COM 				if (pci_ap[i].pci_phys_hi & PCI_REG_PF_M) {
492310923SEvan.Yan@Sun.COM 					if ((PCICFG_LADDR(
49243470Sanish 					    pci_ap[i].pci_phys_low,
49253470Sanish 					    pci_ap[i].pci_phys_mid) +
492610923SEvan.Yan@Sun.COM 					    pci_ap[i].pci_size_low) >
492710923SEvan.Yan@Sun.COM 					    entry->pf_memory_base) {
492810923SEvan.Yan@Sun.COM 						entry->pf_memory_base =
492910923SEvan.Yan@Sun.COM 						    PCICFG_LADDR(
493010923SEvan.Yan@Sun.COM 						    pci_ap[i].pci_phys_low,
493110923SEvan.Yan@Sun.COM 						    pci_ap[i].pci_phys_mid) +
493210923SEvan.Yan@Sun.COM 						    pci_ap[i].pci_size_low;
493310923SEvan.Yan@Sun.COM 					}
493410923SEvan.Yan@Sun.COM 				} else {
493510923SEvan.Yan@Sun.COM 					if ((PCICFG_LADDR(
49363470Sanish 					    pci_ap[i].pci_phys_low,
49373470Sanish 					    pci_ap[i].pci_phys_mid) +
493810923SEvan.Yan@Sun.COM 					    pci_ap[i].pci_size_low) >
493910923SEvan.Yan@Sun.COM 					    entry->memory_base) {
494010923SEvan.Yan@Sun.COM 						entry->memory_base =
494110923SEvan.Yan@Sun.COM 						    PCICFG_LADDR(
494210923SEvan.Yan@Sun.COM 						    pci_ap[i].pci_phys_low,
494310923SEvan.Yan@Sun.COM 						    pci_ap[i].pci_phys_mid) +
494410923SEvan.Yan@Sun.COM 						    pci_ap[i].pci_size_low;
494510923SEvan.Yan@Sun.COM 					}
49463470Sanish 				}
494710923SEvan.Yan@Sun.COM 				break;
49483470Sanish 			case PCI_REG_ADDR_G(PCI_ADDR_IO):
49493470Sanish 				if ((pci_ap[i].pci_phys_low +
49503470Sanish 				    pci_ap[i].pci_size_low) >
49513470Sanish 				    entry->io_base) {
49523470Sanish 					entry->io_base =
49533470Sanish 					    pci_ap[i].pci_phys_low +
49543470Sanish 					    pci_ap[i].pci_size_low;
49553470Sanish 				}
495610923SEvan.Yan@Sun.COM 				break;
49573470Sanish 			}
49583470Sanish 		}
49593470Sanish 
49603470Sanish 		/*
49613470Sanish 		 * free the memory allocated by ddi_getlongprop
49623470Sanish 		 */
49633470Sanish 		kmem_free(pci_ap, length);
49643470Sanish 
49653470Sanish 		/*
49663470Sanish 		 * continue the walk to the next sibling to sum memory
49673470Sanish 		 */
49683470Sanish 		return (DDI_WALK_CONTINUE);
49693470Sanish 	}
49703470Sanish }
49713470Sanish 
49723470Sanish /*
49733470Sanish  * Make "parent" be the parent of the "child" dip
49743470Sanish  */
49753470Sanish static void
pcicfg_reparent_node(dev_info_t * child,dev_info_t * parent)49763470Sanish pcicfg_reparent_node(dev_info_t *child, dev_info_t *parent)
49773470Sanish {
49783470Sanish 	int circ;
49793470Sanish 	dev_info_t *opdip;
49803470Sanish 
49813470Sanish 	ASSERT(i_ddi_node_state(child) <= DS_LINKED);
49823470Sanish 	/*
49833470Sanish 	 * Unlink node from tree before reparenting
49843470Sanish 	 */
49853470Sanish 	opdip = ddi_get_parent(child);
49863470Sanish 	ndi_devi_enter(opdip, &circ);
49873470Sanish 	(void) i_ndi_unconfig_node(child, DS_PROTO, 0);
49883470Sanish 	ndi_devi_exit(opdip, circ);
49893470Sanish 
49903470Sanish 	DEVI(child)->devi_parent = DEVI(parent);
49913470Sanish 	DEVI(child)->devi_bus_ctl = DEVI(parent);
49923470Sanish 	(void) ndi_devi_bind_driver(child, 0);
49933470Sanish }
49943470Sanish 
49953470Sanish /*
49963470Sanish  * Return PCICFG_SUCCESS if device exists at the specified address.
49973470Sanish  * Return PCICFG_NODEVICE is no device exists at the specified address.
49983470Sanish  */
49993470Sanish int
pcicfg_config_setup(dev_info_t * dip,ddi_acc_handle_t * handle)50003470Sanish pcicfg_config_setup(dev_info_t *dip, ddi_acc_handle_t *handle)
50013470Sanish {
50023470Sanish 	caddr_t	cfgaddr;
50033470Sanish 	ddi_device_acc_attr_t attr;
50043470Sanish 	dev_info_t *anode;
50053470Sanish 	int status;
50063470Sanish 	int		rlen;
50073470Sanish 	pci_regspec_t	*reg;
50083470Sanish 	int		ret = DDI_SUCCESS;
50093470Sanish 	int16_t		tmp;
50103470Sanish 
50113470Sanish 	/*
50123470Sanish 	 * Get the pci register spec from the node
50133470Sanish 	 */
501410923SEvan.Yan@Sun.COM 	status = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
501510923SEvan.Yan@Sun.COM 	    (caddr_t)&reg, &rlen);
50163470Sanish 
50173470Sanish 	switch (status) {
50183470Sanish 		case DDI_PROP_SUCCESS:
501910923SEvan.Yan@Sun.COM 			break;
50203470Sanish 		case DDI_PROP_NO_MEMORY:
50213470Sanish 			DEBUG0("reg present, but unable to get memory\n");
50223470Sanish 			return (PCICFG_FAILURE);
50233470Sanish 		default:
50243470Sanish 			DEBUG0("no reg property\n");
50253470Sanish 			return (PCICFG_FAILURE);
50263470Sanish 	}
50273470Sanish 
50283470Sanish 	anode = dip;
50293470Sanish 	DEBUG2("conf_map: dip=%p, anode=%p\n", dip, anode);
50303470Sanish 
50313470Sanish 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
50323470Sanish 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
50333470Sanish 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
50343470Sanish 
503510923SEvan.Yan@Sun.COM 	if (ddi_regs_map_setup(anode, 0, &cfgaddr, 0, 0, &attr, handle)
503610923SEvan.Yan@Sun.COM 	    != DDI_SUCCESS) {
50373470Sanish 		DEBUG0("Failed to setup registers\n");
50383470Sanish 		kmem_free((caddr_t)reg, rlen);
50393470Sanish 		return (PCICFG_FAILURE);
50403470Sanish 	}
50413470Sanish 
50423470Sanish 	/*
50433470Sanish 	 * need to use DDI interfaces as the conf space is
50443470Sanish 	 * cannot be directly accessed by the host.
50453470Sanish 	 */
50463470Sanish 	tmp = (int16_t)ddi_get16(*handle, (uint16_t *)cfgaddr);
50473470Sanish 	if ((tmp == (int16_t)0xffff) || (tmp == -1)) {
50483470Sanish 		DEBUG1("NO DEVICEFOUND, read %x\n", tmp);
50493470Sanish 		ret = PCICFG_NODEVICE;
50503470Sanish 	} else {
50513470Sanish 		if (tmp == 0) {
50523470Sanish 			DEBUG0("Device Not Ready yet ?");
50533470Sanish 			ret = PCICFG_NODEVICE;
50543470Sanish 		} else {
50553470Sanish 			DEBUG1("DEVICEFOUND, read %x\n", tmp);
50563470Sanish 			ret = PCICFG_SUCCESS;
50573470Sanish 		}
50583470Sanish 	}
50593470Sanish 
50603470Sanish 	if (ret == PCICFG_NODEVICE)
50613470Sanish 		ddi_regs_map_free(handle);
50623470Sanish 	kmem_free((caddr_t)reg, rlen);
50633470Sanish 
50643470Sanish 	return (ret);
50653470Sanish 
50663470Sanish }
50673470Sanish 
50683470Sanish static void
pcicfg_config_teardown(ddi_acc_handle_t * handle)50693470Sanish pcicfg_config_teardown(ddi_acc_handle_t *handle)
50703470Sanish {
50713470Sanish 	(void) ddi_regs_map_free(handle);
50723470Sanish }
50733470Sanish 
50743470Sanish static int
pcicfg_add_config_reg(dev_info_t * dip,uint_t bus,uint_t device,uint_t func)50753470Sanish pcicfg_add_config_reg(dev_info_t *dip,
50763470Sanish 	uint_t bus, uint_t device, uint_t func)
50773470Sanish {
50783470Sanish 	int reg[10] = { PCI_ADDR_CONFIG, 0, 0, 0, 0};
50793470Sanish 
50803470Sanish 	reg[0] = PCICFG_MAKE_REG_HIGH(bus, device, func, 0);
50813470Sanish 
508210923SEvan.Yan@Sun.COM 	return (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "reg", reg, 5));
50833470Sanish }
50843470Sanish 
508510923SEvan.Yan@Sun.COM static int
pcicfg_ari_configure(dev_info_t * dip)508610923SEvan.Yan@Sun.COM pcicfg_ari_configure(dev_info_t *dip)
508710923SEvan.Yan@Sun.COM {
508810923SEvan.Yan@Sun.COM 	if (pcie_ari_supported(dip) == PCIE_ARI_FORW_NOT_SUPPORTED)
508910923SEvan.Yan@Sun.COM 		return (DDI_FAILURE);
509010923SEvan.Yan@Sun.COM 
509110923SEvan.Yan@Sun.COM 	/*
509210923SEvan.Yan@Sun.COM 	 * Until we have resource balancing, dynamically configure
509310923SEvan.Yan@Sun.COM 	 * ARI functions without firmware assistamce.
509410923SEvan.Yan@Sun.COM 	 */
509510923SEvan.Yan@Sun.COM 	return (DDI_FAILURE);
509610923SEvan.Yan@Sun.COM }
509710923SEvan.Yan@Sun.COM 
509810923SEvan.Yan@Sun.COM 
50993470Sanish #ifdef DEBUG
51003470Sanish static void
debug(char * fmt,uintptr_t a1,uintptr_t a2,uintptr_t a3,uintptr_t a4,uintptr_t a5)51013470Sanish debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3,
51023470Sanish 	uintptr_t a4, uintptr_t a5)
51033470Sanish {
51043470Sanish 	if (pcicfg_debug > 1) {
51053470Sanish 		prom_printf("pcicfg: ");
51063470Sanish 		prom_printf(fmt, a1, a2, a3, a4, a5);
51073470Sanish 	}
51083470Sanish }
51093470Sanish #endif
51103470Sanish 
51113470Sanish /*ARGSUSED*/
51123470Sanish static uint8_t
pcicfg_get_nslots(dev_info_t * dip,ddi_acc_handle_t handle)51133470Sanish pcicfg_get_nslots(dev_info_t *dip, ddi_acc_handle_t handle)
51143470Sanish {
51157987SErwin.Tsaur@Sun.COM 	uint16_t cap_id_loc, slot_id_loc;
51163470Sanish 	uint8_t num_slots = 0;
51173470Sanish 
51183470Sanish 	/* just depend on the pcie_cap for now. */
51197987SErwin.Tsaur@Sun.COM 	(void) PCI_CAP_LOCATE(handle, PCI_CAP_ID_PCI_E, &cap_id_loc);
51207987SErwin.Tsaur@Sun.COM 	(void) PCI_CAP_LOCATE(handle, PCI_CAP_ID_SLOT_ID, &slot_id_loc);
51217987SErwin.Tsaur@Sun.COM 	if (cap_id_loc != PCI_CAP_NEXT_PTR_NULL) {
512210923SEvan.Yan@Sun.COM 		if (pci_config_get8(handle, cap_id_loc + PCI_CAP_ID_REGS_OFF) &
512310923SEvan.Yan@Sun.COM 		    PCIE_PCIECAP_SLOT_IMPL)
51243470Sanish 			num_slots = 1;
51253470Sanish 	} else /* not a PCIe switch/bridge. Must be a PCI-PCI[-X] bridge */
51267987SErwin.Tsaur@Sun.COM 	if (slot_id_loc != PCI_CAP_NEXT_PTR_NULL) {
51277987SErwin.Tsaur@Sun.COM 		uint8_t esr_reg = pci_config_get8(handle, slot_id_loc + 2);
51283470Sanish 		num_slots = PCI_CAPSLOT_NSLOTS(esr_reg);
51293470Sanish 	}
51303470Sanish 	/* XXX - need to cover PCI-PCIe bridge with n slots */
51313470Sanish 	return (num_slots);
51323470Sanish }
51333470Sanish 
51343470Sanish /*ARGSUSED*/
51353470Sanish static int
pcicfg_pcie_dev(dev_info_t * dip,ddi_acc_handle_t handle)51363470Sanish pcicfg_pcie_dev(dev_info_t *dip, ddi_acc_handle_t handle)
51373470Sanish {
51383470Sanish 	/* get parent device's device_type property */
51393470Sanish 	char *device_type;
51403470Sanish 	int val;
51413470Sanish 	dev_info_t *pdip = ddi_get_parent(dip);
51423470Sanish 
514310923SEvan.Yan@Sun.COM 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS,
514410923SEvan.Yan@Sun.COM 	    "device_type", &device_type) != DDI_PROP_SUCCESS) {
51453470Sanish 		DEBUG2("device_type property missing for %s#%d",
514610923SEvan.Yan@Sun.COM 		    ddi_get_name(pdip), ddi_get_instance(pdip));
51473470Sanish 		return (DDI_FAILURE);
51483470Sanish 	}
51493470Sanish 	DEBUG1("device_type=<%s>\n", device_type);
51503470Sanish 
51513470Sanish 	val = DDI_FAILURE;
51523470Sanish 	if (strcmp(device_type, "pciex") == 0)
51533470Sanish 		val = DDI_SUCCESS;
51543470Sanish 	ddi_prop_free(device_type);
51553470Sanish 	return (val);
51563470Sanish }
51573470Sanish 
51583470Sanish static int
pcicfg_pcie_device_type(dev_info_t * dip,ddi_acc_handle_t handle)51593470Sanish pcicfg_pcie_device_type(dev_info_t *dip, ddi_acc_handle_t handle)
51603470Sanish {
51613470Sanish 	int port_type = pcicfg_pcie_port_type(dip, handle);
51623470Sanish 
51633470Sanish 	DEBUG1("device port_type = %x\n", port_type);
51643470Sanish 	/* No PCIe CAP regs, we are not PCIe device_type */
51653470Sanish 	if (port_type < 0)
51663470Sanish 		return (DDI_FAILURE);
51673470Sanish 
51683470Sanish 	/* check for all PCIe device_types */
51693470Sanish 	if ((port_type == PCIE_PCIECAP_DEV_TYPE_UP) ||
517010923SEvan.Yan@Sun.COM 	    (port_type == PCIE_PCIECAP_DEV_TYPE_DOWN) ||
517110923SEvan.Yan@Sun.COM 	    (port_type == PCIE_PCIECAP_DEV_TYPE_ROOT) ||
517210923SEvan.Yan@Sun.COM 	    (port_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE))
51733470Sanish 		return (DDI_SUCCESS);
51743470Sanish 
51753470Sanish 	return (DDI_FAILURE);
51763470Sanish 
51773470Sanish }
51783470Sanish 
51793470Sanish /*ARGSUSED*/
51803470Sanish static int
pcicfg_pcie_port_type(dev_info_t * dip,ddi_acc_handle_t handle)51813470Sanish pcicfg_pcie_port_type(dev_info_t *dip, ddi_acc_handle_t handle)
51823470Sanish {
51833470Sanish 	int port_type = -1;
51847987SErwin.Tsaur@Sun.COM 	uint16_t cap_loc;
51853470Sanish 
51863470Sanish 	/* Note: need to look at the port type information here */
51877987SErwin.Tsaur@Sun.COM 	(void) PCI_CAP_LOCATE(handle, PCI_CAP_ID_PCI_E, &cap_loc);
51887987SErwin.Tsaur@Sun.COM 	if (cap_loc != PCI_CAP_NEXT_PTR_NULL)
51893470Sanish 		port_type = pci_config_get16(handle,
519010923SEvan.Yan@Sun.COM 		    cap_loc + PCIE_PCIECAP) & PCIE_PCIECAP_DEV_TYPE_MASK;
51913470Sanish 
51923470Sanish 	return (port_type);
51933470Sanish }
519411245SZhijun.Fu@Sun.COM 
519511245SZhijun.Fu@Sun.COM /*
519611245SZhijun.Fu@Sun.COM  * Return true if the devinfo node is in a PCI Express hierarchy.
519711245SZhijun.Fu@Sun.COM  */
519811245SZhijun.Fu@Sun.COM static boolean_t
is_pcie_fabric(dev_info_t * dip)519911245SZhijun.Fu@Sun.COM is_pcie_fabric(dev_info_t *dip)
520011245SZhijun.Fu@Sun.COM {
520111245SZhijun.Fu@Sun.COM 	dev_info_t *root = ddi_root_node();
520211245SZhijun.Fu@Sun.COM 	dev_info_t *pdip;
520311245SZhijun.Fu@Sun.COM 	boolean_t found = B_FALSE;
520411245SZhijun.Fu@Sun.COM 	char *bus;
520511245SZhijun.Fu@Sun.COM 
520611245SZhijun.Fu@Sun.COM 	/*
520711245SZhijun.Fu@Sun.COM 	 * Does this device reside in a pcie fabric ?
520811245SZhijun.Fu@Sun.COM 	 */
520911245SZhijun.Fu@Sun.COM 	for (pdip = dip; pdip && (pdip != root) && !found;
521011245SZhijun.Fu@Sun.COM 	    pdip = ddi_get_parent(pdip)) {
521111245SZhijun.Fu@Sun.COM 		if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip,
521211245SZhijun.Fu@Sun.COM 		    DDI_PROP_DONTPASS, "device_type", &bus) !=
521311245SZhijun.Fu@Sun.COM 		    DDI_PROP_SUCCESS)
521411245SZhijun.Fu@Sun.COM 			break;
521511245SZhijun.Fu@Sun.COM 
521611245SZhijun.Fu@Sun.COM 		if (strcmp(bus, "pciex") == 0)
521711245SZhijun.Fu@Sun.COM 			found = B_TRUE;
521811245SZhijun.Fu@Sun.COM 
521911245SZhijun.Fu@Sun.COM 		ddi_prop_free(bus);
522011245SZhijun.Fu@Sun.COM 	}
522111245SZhijun.Fu@Sun.COM 
522211245SZhijun.Fu@Sun.COM 	return (found);
522311245SZhijun.Fu@Sun.COM }
5224