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)®, &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, ®[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)®, &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, ®[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)®, &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)®, &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)®, &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