diff options
Diffstat (limited to 'usr/src/uts/sun4/io/pcicfg.e.c')
| -rw-r--r-- | usr/src/uts/sun4/io/pcicfg.e.c | 5946 |
1 files changed, 5946 insertions, 0 deletions
diff --git a/usr/src/uts/sun4/io/pcicfg.e.c b/usr/src/uts/sun4/io/pcicfg.e.c new file mode 100644 index 0000000000..aa5fef244e --- /dev/null +++ b/usr/src/uts/sun4/io/pcicfg.e.c @@ -0,0 +1,5946 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * PCI configurator (pcicfg) + */ + +#include <sys/isa_defs.h> + +#include <sys/conf.h> +#include <sys/kmem.h> +#include <sys/debug.h> +#include <sys/modctl.h> +#include <sys/autoconf.h> +#include <sys/hwconf.h> +#include <sys/ddi_impldefs.h> +#include <sys/fcode.h> +#include <sys/pcie.h> +#include <sys/pcie_impl.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/hotplug/pci/pcicfg.h> +#include <sys/ndi_impldefs.h> + +#define PCICFG_DEVICE_TYPE_PCI 1 +#define PCICFG_DEVICE_TYPE_PCIE 2 + +#define EFCODE21554 /* changes for supporting 21554 */ + +static int pcicfg_alloc_resource(dev_info_t *, pci_regspec_t); +static int pcicfg_free_resource(dev_info_t *, pci_regspec_t); +static int pcicfg_remove_assigned_prop(dev_info_t *, pci_regspec_t *); + +#ifdef PCICFG_INTERPRET_FCODE +static int pcicfg_fcode_assign_bars(ddi_acc_handle_t, dev_info_t *, + uint_t, uint_t, uint_t, int32_t, pci_regspec_t *); +#endif /* PCICFG_INTERPRET_FCODE */ + +/* + * ************************************************************************ + * *** Implementation specific local data structures/definitions. *** + * ************************************************************************ + */ + +static int pcicfg_start_devno = 0; /* for Debug only */ + +#define PCICFG_MAX_DEVICE 32 +#define PCICFG_MAX_FUNCTION 8 +#define PCICFG_MAX_REGISTER 64 +#define PCICFG_MAX_BUS_DEPTH 255 + +#define PCICFG_NODEVICE 42 +#define PCICFG_NOMEMORY 43 +#define PCICFG_NOMULTI 44 + +#define PCICFG_HIADDR(n) ((uint32_t)(((uint64_t)(n) & 0xFFFFFFFF00000000)>> 32)) +#define PCICFG_LOADDR(n) ((uint32_t)((uint64_t)(n) & 0x00000000FFFFFFFF)) +#define PCICFG_LADDR(lo, hi) (((uint64_t)(hi) << 32) | (uint32_t)(lo)) + +#define PCICFG_HIWORD(n) ((uint16_t)(((uint32_t)(n) & 0xFFFF0000)>> 16)) +#define PCICFG_LOWORD(n) ((uint16_t)((uint32_t)(n) & 0x0000FFFF)) +#define PCICFG_HIBYTE(n) ((uint8_t)(((uint16_t)(n) & 0xFF00)>> 8)) +#define PCICFG_LOBYTE(n) ((uint8_t)((uint16_t)(n) & 0x00FF)) + +#define PCICFG_ROUND_UP(addr, gran) ((uintptr_t)((gran+addr-1)&(~(gran-1)))) +#define PCICFG_ROUND_DOWN(addr, gran) ((uintptr_t)((addr) & ~(gran-1))) + +#define PCICFG_MEMGRAN 0x100000 +#define PCICFG_IOGRAN 0x1000 +#define PCICFG_4GIG_LIMIT 0xFFFFFFFFUL + +#define PCICFG_MEM_MULT 4 +#define PCICFG_IO_MULT 4 +#define PCICFG_RANGE_LEN 2 /* Number of range entries */ + +static int pcicfg_slot_busnums = 8; +static int pcicfg_slot_memsize = 32 * PCICFG_MEMGRAN; /* 32MB per slot */ +static int pcicfg_slot_iosize = 64 * PCICFG_IOGRAN; /* 64K per slot */ +static int pcicfg_chassis_per_tree = 1; +static int pcicfg_sec_reset_delay = 1000000; + +/* + * The following typedef is used to represent a + * 1275 "bus-range" property of a PCI Bus node. + * DAF - should be in generic include file... + */ + +typedef struct pcicfg_bus_range { + uint32_t lo; + uint32_t hi; +} pcicfg_bus_range_t; + +typedef struct pcicfg_range { + + uint32_t child_hi; + uint32_t child_mid; + uint32_t child_lo; + uint32_t parent_hi; + uint32_t parent_mid; + uint32_t parent_lo; + uint32_t size_hi; + uint32_t size_lo; + +} pcicfg_range_t; + +typedef struct hole hole_t; + +struct hole { + uint64_t start; + uint64_t len; + hole_t *next; +}; + +typedef struct pcicfg_phdl pcicfg_phdl_t; + +struct pcicfg_phdl { + + dev_info_t *dip; /* Associated with the attach point */ + pcicfg_phdl_t *next; + + uint64_t memory_base; /* Memory base for this attach point */ + uint64_t memory_last; + uint64_t memory_len; + uint32_t io_base; /* I/O base for this attach point */ + uint32_t io_last; + uint32_t io_len; + + int error; + uint_t highest_bus; /* Highest bus seen on the probe */ + + hole_t mem_hole; /* Memory hole linked list. */ + hole_t io_hole; /* IO hole linked list */ + + ndi_ra_request_t mem_req; /* allocator request for memory */ + ndi_ra_request_t io_req; /* allocator request for I/O */ +}; + +struct pcicfg_standard_prop_entry { + uchar_t *name; + uint_t config_offset; + uint_t size; +}; + + +struct pcicfg_name_entry { + uint32_t class_code; + char *name; +}; + +struct pcicfg_find_ctrl { + uint_t device; + uint_t function; + dev_info_t *dip; +}; + +typedef struct pcicfg_err_regs { + uint16_t cmd; + uint16_t bcntl; + uint16_t pcie_dev; + uint16_t devctl; + int pcie_cap_off; +} pcicfg_err_regs_t; + +/* + * List of Indirect Config Map Devices. At least the intent of the + * design is to look for a device in this list during the configure + * operation, and if the device is listed here, then it is a nontransparent + * bridge, hence load the driver and avail the config map services from + * the driver. Class and Subclass should be as defined in the PCI specs + * ie. class is 0x6, and subclass is 0x9. + */ +static struct { + uint8_t mem_range_bar_offset; + uint8_t io_range_bar_offset; + uint8_t prefetch_mem_range_bar_offset; +} pcicfg_indirect_map_devs[] = { + PCI_CONF_BASE3, PCI_CONF_BASE2, PCI_CONF_BASE3, + 0, 0, 0, +}; + +#define PCICFG_MAKE_REG_HIGH(busnum, devnum, funcnum, register)\ + (\ + ((ulong_t)(busnum & 0xff) << 16) |\ + ((ulong_t)(devnum & 0x1f) << 11) |\ + ((ulong_t)(funcnum & 0x7) << 8) |\ + ((ulong_t)(register & 0x3f))) + +/* + * debug macros: + */ +#if defined(DEBUG) +extern void prom_printf(const char *, ...); + +/* + * Following values are defined for this debug flag. + * + * 1 = dump configuration header only. + * 2 = dump generic debug data only (no config header dumped) + * 3 = dump everything (both 1 and 2) + */ +int pcicfg_debug = 0; +int pcicfg_dump_fcode = 0; + +static void debug(char *, uintptr_t, uintptr_t, + uintptr_t, uintptr_t, uintptr_t); + +#define DEBUG0(fmt)\ + debug(fmt, 0, 0, 0, 0, 0); +#define DEBUG1(fmt, a1)\ + debug(fmt, (uintptr_t)(a1), 0, 0, 0, 0); +#define DEBUG2(fmt, a1, a2)\ + debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), 0, 0, 0); +#define DEBUG3(fmt, a1, a2, a3)\ + debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2),\ + (uintptr_t)(a3), 0, 0); +#define DEBUG4(fmt, a1, a2, a3, a4)\ + debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2),\ + (uintptr_t)(a3), (uintptr_t)(a4), 0); +#else +#define DEBUG0(fmt) +#define DEBUG1(fmt, a1) +#define DEBUG2(fmt, a1, a2) +#define DEBUG3(fmt, a1, a2, a3) +#define DEBUG4(fmt, a1, a2, a3, a4) +#endif + +#ifdef PCICFG_INTERPRET_FCODE +int pcicfg_dont_interpret = 0; +#else +int pcicfg_dont_interpret = 1; +#endif + +/* + * forward declarations for routines defined in this module (called here) + */ + +static int pcicfg_add_config_reg(dev_info_t *, + uint_t, uint_t, uint_t); +static int pcicfg_probe_children(dev_info_t *, uint_t, uint_t, uint_t, + uint_t *); + +#ifdef PCICFG_INTERPRET_FCODE +static int pcicfg_load_fcode(dev_info_t *, uint_t, uint_t, uint_t, + uint16_t, uint16_t, uchar_t **, int *, int, int); +#endif + +static int pcicfg_fcode_probe(dev_info_t *, uint_t, uint_t, uint_t, uint_t *); +static int pcicfg_probe_bridge(dev_info_t *, ddi_acc_handle_t, uint_t, + uint_t *); +static int pcicfg_free_all_resources(dev_info_t *); +static int pcicfg_alloc_new_resources(dev_info_t *); +static int pcicfg_match_dev(dev_info_t *, void *); +static dev_info_t *pcicfg_devi_find(dev_info_t *, uint_t, uint_t); +static pcicfg_phdl_t *pcicfg_find_phdl(dev_info_t *); +static pcicfg_phdl_t *pcicfg_create_phdl(dev_info_t *); +static int pcicfg_destroy_phdl(dev_info_t *); +static int pcicfg_sum_resources(dev_info_t *, void *); +static int pcicfg_find_resource_end(dev_info_t *, void *); +static int pcicfg_allocate_chunk(dev_info_t *); +static int pcicfg_program_ap(dev_info_t *); +static int pcicfg_device_assign(dev_info_t *); +static int pcicfg_bridge_assign(dev_info_t *, void *); +static int pcicfg_free_resources(dev_info_t *); +static void pcicfg_setup_bridge(pcicfg_phdl_t *, ddi_acc_handle_t); +static void pcicfg_update_bridge(pcicfg_phdl_t *, ddi_acc_handle_t); +static void pcicfg_enable_bridge_probe_err(dev_info_t *dip, + ddi_acc_handle_t h, pcicfg_err_regs_t *regs); +static void pcicfg_disable_bridge_probe_err(dev_info_t *dip, + ddi_acc_handle_t h, pcicfg_err_regs_t *regs); +static int pcicfg_update_assigned_prop(dev_info_t *, pci_regspec_t *); +static void pcicfg_device_on(ddi_acc_handle_t); +static void pcicfg_device_off(ddi_acc_handle_t); +static int pcicfg_set_busnode_props(dev_info_t *, uint8_t); +static int pcicfg_free_bridge_resources(dev_info_t *); +static int pcicfg_free_device_resources(dev_info_t *); +static int pcicfg_teardown_device(dev_info_t *); +static int pcicfg_config_setup(dev_info_t *, ddi_acc_handle_t *); +static void pcicfg_config_teardown(ddi_acc_handle_t *); +static void pcicfg_get_mem(pcicfg_phdl_t *, uint32_t, uint64_t *); +static void pcicfg_get_io(pcicfg_phdl_t *, uint32_t, uint32_t *); +static int pcicfg_update_ranges_prop(dev_info_t *, pcicfg_range_t *); +static int pcicfg_map_phys(dev_info_t *, pci_regspec_t *, caddr_t *, + ddi_device_acc_attr_t *, ddi_acc_handle_t *); +static void pcicfg_unmap_phys(ddi_acc_handle_t *, pci_regspec_t *); +static int pcicfg_dump_assigned(dev_info_t *); +static uint_t pcicfg_configure_ntbridge(dev_info_t *, uint_t, uint_t); +static int pcicfg_indirect_map(dev_info_t *dip); +static uint_t pcicfg_get_ntbridge_child_range(dev_info_t *, uint64_t *, + uint64_t *, uint_t); +static int pcicfg_is_ntbridge(dev_info_t *); +static int pcicfg_ntbridge_allocate_resources(dev_info_t *); +static int pcicfg_ntbridge_configure_done(dev_info_t *); +static int pcicfg_ntbridge_unconfigure(dev_info_t *); +static int pcicfg_ntbridge_unconfigure_child(dev_info_t *, uint_t); +static void pcicfg_free_hole(hole_t *); +static uint64_t pcicfg_alloc_hole(hole_t *, uint64_t *, uint32_t); + +#ifdef DEBUG +static void pcicfg_dump_common_config(ddi_acc_handle_t config_handle); +static void pcicfg_dump_device_config(ddi_acc_handle_t); + +static void pcicfg_dump_bridge_config(ddi_acc_handle_t config_handle); +static uint64_t pcicfg_unused_space(hole_t *, uint32_t *); + +#define PCICFG_DUMP_COMMON_CONFIG(hdl) (void)pcicfg_dump_common_config(hdl) +#define PCICFG_DUMP_DEVICE_CONFIG(hdl) (void)pcicfg_dump_device_config(hdl) +#define PCICFG_DUMP_BRIDGE_CONFIG(hdl) (void)pcicfg_dump_bridge_config(hdl) +#else +#define PCICFG_DUMP_COMMON_CONFIG(handle) +#define PCICFG_DUMP_DEVICE_CONFIG(handle) +#define PCICFG_DUMP_BRIDGE_CONFIG(handle) +#endif + +static kmutex_t pcicfg_list_mutex; /* Protects the probe handle list */ +static pcicfg_phdl_t *pcicfg_phdl_list = NULL; + +#ifndef _DONT_USE_1275_GENERIC_NAMES +/* + * Class code table + */ +static struct pcicfg_name_entry pcicfg_class_lookup [] = { + + { 0x001, "display" }, + { 0x100, "scsi" }, + { 0x101, "ide" }, + { 0x102, "fdc" }, + { 0x103, "ipi" }, + { 0x104, "raid" }, + { 0x200, "ethernet" }, + { 0x201, "token-ring" }, + { 0x202, "fddi" }, + { 0x203, "atm" }, + { 0x300, "display" }, + { 0x400, "video" }, + { 0x401, "sound" }, + { 0x500, "memory" }, + { 0x501, "flash" }, + { 0x600, "host" }, + { 0x601, "isa" }, + { 0x602, "eisa" }, + { 0x603, "mca" }, + { 0x604, "pci" }, + { 0x605, "pcmcia" }, + { 0x606, "nubus" }, + { 0x607, "cardbus" }, + { 0x609, "pci" }, + { 0x700, "serial" }, + { 0x701, "parallel" }, + { 0x800, "interrupt-controller" }, + { 0x801, "dma-controller" }, + { 0x802, "timer" }, + { 0x803, "rtc" }, + { 0x900, "keyboard" }, + { 0x901, "pen" }, + { 0x902, "mouse" }, + { 0xa00, "dock" }, + { 0xb00, "cpu" }, + { 0xc00, "firewire" }, + { 0xc01, "access-bus" }, + { 0xc02, "ssa" }, + { 0xc03, "usb" }, + { 0xc04, "fibre-channel" }, + { 0, 0 } +}; +#endif /* _DONT_USE_1275_GENERIC_NAMES */ + +/* + * Module control operations + */ + +extern struct mod_ops mod_miscops; + +static struct modlmisc modlmisc = { + &mod_miscops, /* Type of module */ + "PCIe/PCI Config (EFCode Enabled) v%I%" +}; + +static struct modlinkage modlinkage = { + MODREV_1, (void *)&modlmisc, NULL +}; + +#ifdef DEBUG + +static void +pcicfg_dump_common_config(ddi_acc_handle_t config_handle) +{ + if ((pcicfg_debug & 1) == 0) + return; + cmn_err(CE_CONT, " Vendor ID = [0x%x]\n", + pci_config_get16(config_handle, PCI_CONF_VENID)); + cmn_err(CE_CONT, " Device ID = [0x%x]\n", + pci_config_get16(config_handle, PCI_CONF_DEVID)); + cmn_err(CE_CONT, " Command REG = [0x%x]\n", + pci_config_get16(config_handle, PCI_CONF_COMM)); + cmn_err(CE_CONT, " Status REG = [0x%x]\n", + pci_config_get16(config_handle, PCI_CONF_STAT)); + cmn_err(CE_CONT, " Revision ID = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_REVID)); + cmn_err(CE_CONT, " Prog Class = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_PROGCLASS)); + cmn_err(CE_CONT, " Dev Class = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_SUBCLASS)); + cmn_err(CE_CONT, " Base Class = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_BASCLASS)); + cmn_err(CE_CONT, " Device ID = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_CACHE_LINESZ)); + cmn_err(CE_CONT, " Header Type = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_HEADER)); + cmn_err(CE_CONT, " BIST = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_BIST)); + cmn_err(CE_CONT, " BASE 0 = [0x%x]\n", + pci_config_get32(config_handle, PCI_CONF_BASE0)); + cmn_err(CE_CONT, " BASE 1 = [0x%x]\n", + pci_config_get32(config_handle, PCI_CONF_BASE1)); + +} + +static void +pcicfg_dump_device_config(ddi_acc_handle_t config_handle) +{ + if ((pcicfg_debug & 1) == 0) + return; + pcicfg_dump_common_config(config_handle); + + cmn_err(CE_CONT, " BASE 2 = [0x%x]\n", + pci_config_get32(config_handle, PCI_CONF_BASE2)); + cmn_err(CE_CONT, " BASE 3 = [0x%x]\n", + pci_config_get32(config_handle, PCI_CONF_BASE3)); + cmn_err(CE_CONT, " BASE 4 = [0x%x]\n", + pci_config_get32(config_handle, PCI_CONF_BASE4)); + cmn_err(CE_CONT, " BASE 5 = [0x%x]\n", + pci_config_get32(config_handle, PCI_CONF_BASE5)); + cmn_err(CE_CONT, " Cardbus CIS = [0x%x]\n", + pci_config_get32(config_handle, PCI_CONF_CIS)); + cmn_err(CE_CONT, " Sub VID = [0x%x]\n", + pci_config_get16(config_handle, PCI_CONF_SUBVENID)); + cmn_err(CE_CONT, " Sub SID = [0x%x]\n", + pci_config_get16(config_handle, PCI_CONF_SUBSYSID)); + cmn_err(CE_CONT, " ROM = [0x%x]\n", + pci_config_get32(config_handle, PCI_CONF_ROM)); + cmn_err(CE_CONT, " I Line = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_ILINE)); + cmn_err(CE_CONT, " I Pin = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_IPIN)); + cmn_err(CE_CONT, " Max Grant = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_MIN_G)); + cmn_err(CE_CONT, " Max Latent = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_MAX_L)); +} + +static void +pcicfg_dump_bridge_config(ddi_acc_handle_t config_handle) +{ + if ((pcicfg_debug & 1) == 0) + return; + + pcicfg_dump_common_config(config_handle); + + cmn_err(CE_CONT, "........................................\n"); + + cmn_err(CE_CONT, " Pri Bus = [0x%x]\n", + pci_config_get8(config_handle, PCI_BCNF_PRIBUS)); + cmn_err(CE_CONT, " Sec Bus = [0x%x]\n", + pci_config_get8(config_handle, PCI_BCNF_SECBUS)); + cmn_err(CE_CONT, " Sub Bus = [0x%x]\n", + pci_config_get8(config_handle, PCI_BCNF_SUBBUS)); + cmn_err(CE_CONT, " Latency = [0x%x]\n", + pci_config_get8(config_handle, PCI_BCNF_LATENCY_TIMER)); + cmn_err(CE_CONT, " I/O Base LO = [0x%x]\n", + pci_config_get8(config_handle, PCI_BCNF_IO_BASE_LOW)); + cmn_err(CE_CONT, " I/O Lim LO = [0x%x]\n", + pci_config_get8(config_handle, PCI_BCNF_IO_LIMIT_LOW)); + cmn_err(CE_CONT, " Sec. Status = [0x%x]\n", + pci_config_get16(config_handle, PCI_BCNF_SEC_STATUS)); + cmn_err(CE_CONT, " Mem Base = [0x%x]\n", + pci_config_get16(config_handle, PCI_BCNF_MEM_BASE)); + cmn_err(CE_CONT, " Mem Limit = [0x%x]\n", + pci_config_get16(config_handle, PCI_BCNF_MEM_LIMIT)); + cmn_err(CE_CONT, " PF Mem Base = [0x%x]\n", + pci_config_get16(config_handle, PCI_BCNF_PF_BASE_LOW)); + cmn_err(CE_CONT, " PF Mem Lim = [0x%x]\n", + pci_config_get16(config_handle, PCI_BCNF_PF_LIMIT_LOW)); + cmn_err(CE_CONT, " PF Base HI = [0x%x]\n", + pci_config_get32(config_handle, PCI_BCNF_PF_BASE_HIGH)); + cmn_err(CE_CONT, " PF Lim HI = [0x%x]\n", + pci_config_get32(config_handle, PCI_BCNF_PF_LIMIT_HIGH)); + cmn_err(CE_CONT, " I/O Base HI = [0x%x]\n", + pci_config_get16(config_handle, PCI_BCNF_IO_BASE_HI)); + cmn_err(CE_CONT, " I/O Lim HI = [0x%x]\n", + pci_config_get16(config_handle, PCI_BCNF_IO_LIMIT_HI)); + cmn_err(CE_CONT, " ROM addr = [0x%x]\n", + pci_config_get32(config_handle, PCI_BCNF_ROM)); + cmn_err(CE_CONT, " Intr Line = [0x%x]\n", + pci_config_get8(config_handle, PCI_BCNF_ILINE)); + cmn_err(CE_CONT, " Intr Pin = [0x%x]\n", + pci_config_get8(config_handle, PCI_BCNF_IPIN)); + cmn_err(CE_CONT, " Bridge Ctrl = [0x%x]\n", + pci_config_get16(config_handle, PCI_BCNF_BCNTRL)); +} + +#endif + + +int +_init() +{ + DEBUG0("PCI configurator installed - Fcode Interpretation/21554\n"); + + mutex_init(&pcicfg_list_mutex, NULL, MUTEX_DRIVER, NULL); + return (mod_install(&modlinkage)); +} + +int +_fini(void) +{ + int error; + + error = mod_remove(&modlinkage); + if (error != 0) { + return (error); + } + mutex_destroy(&pcicfg_list_mutex); + return (0); +} + +int +_info(modinfop) +struct modinfo *modinfop; +{ + return (mod_info(&modlinkage, modinfop)); +} + +/* + * given a cap_id, return its cap_id location in config space + */ +static int +pcicfg_get_cap(ddi_acc_handle_t config_handle, uint8_t cap_id) +{ + uint8_t curcap; + uint_t cap_id_loc; + uint16_t status; + int location = -1; + + /* + * Need to check the Status register for ECP support first. + * Also please note that for type 1 devices, the + * offset could change. Should support type 1 next. + */ + status = pci_config_get16(config_handle, PCI_CONF_STAT); + if (!(status & PCI_STAT_CAP)) { + return (-1); + } + cap_id_loc = pci_config_get8(config_handle, PCI_CONF_CAP_PTR); + + /* Walk the list of capabilities */ + while (cap_id_loc) { + + curcap = pci_config_get8(config_handle, cap_id_loc); + + if (curcap == cap_id) { + location = cap_id_loc; + break; + } + cap_id_loc = pci_config_get8(config_handle, + cap_id_loc + 1); + } + return (location); +} + +/*ARGSUSED*/ +static uint8_t +pcicfg_get_nslots(dev_info_t *dip, ddi_acc_handle_t handle) +{ + int cap_id_loc; + uint8_t num_slots = 0; + + /* just depend on the pcie_cap for now. */ + if ((cap_id_loc = pcicfg_get_cap(handle, PCI_CAP_ID_PCI_E)) + > 0) { + if (pci_config_get16(handle, cap_id_loc + + PCIE_PCIECAP) & + PCIE_PCIECAP_SLOT_IMPL) + num_slots = 1; + } else /* not a PCIe switch/bridge. Must be a PCI-PCI[-X] bridge */ + if ((cap_id_loc = pcicfg_get_cap(handle, PCI_CAP_ID_SLOT_ID)) + > 0) { + uint8_t esr_reg = pci_config_get8(handle, cap_id_loc + + PCI_CAP_ID_REGS_OFF); + num_slots = PCI_CAPSLOT_NSLOTS(esr_reg); + } + /* XXX - need to cover PCI-PCIe bridge with n slots */ + return (num_slots); +} + +/*ARGSUSED*/ +static uint8_t +pcicfg_is_chassis(dev_info_t *dip, ddi_acc_handle_t handle) +{ + int cap_id_loc; + + if ((cap_id_loc = pcicfg_get_cap(handle, PCI_CAP_ID_SLOT_ID)) + > 0) { + uint8_t esr_reg = pci_config_get8(handle, cap_id_loc + 2); + if (PCI_CAPSLOT_FIC(esr_reg)) + return (B_TRUE); + } + return (B_FALSE); +} + +/*ARGSUSED*/ +static int +pcicfg_pcie_dev(dev_info_t *dip, int bus_type, pcicfg_err_regs_t *regs) +{ + /* get parent device's device_type property */ + char *device_type; + int rc = DDI_FAILURE; + dev_info_t *pdip = ddi_get_parent(dip); + + regs->pcie_dev = 0; + if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, + DDI_PROP_DONTPASS, "device_type", &device_type) + != DDI_PROP_SUCCESS) { + DEBUG2("device_type property missing for %s#%d", + ddi_get_name(pdip), ddi_get_instance(pdip)); + return (DDI_FAILURE); + } + switch (bus_type) { + case PCICFG_DEVICE_TYPE_PCIE: + if (strcmp(device_type, "pciex") == 0) { + rc = DDI_SUCCESS; + regs->pcie_dev = 1; + } + break; + case PCICFG_DEVICE_TYPE_PCI: + if (strcmp(device_type, "pci") == 0) + rc = DDI_SUCCESS; + break; + default: + break; + } + ddi_prop_free(device_type); + return (rc); +} + +/*ARGSUSED*/ +static int +pcicfg_pcie_port_type(dev_info_t *dip, ddi_acc_handle_t handle) +{ + int port_type = -1; + int cap_loc; + + if ((cap_loc = pcicfg_get_cap(handle, PCI_CAP_ID_PCI_E)) > 0) + port_type = pci_config_get16(handle, + cap_loc + PCIE_PCIECAP) & PCIE_PCIECAP_DEV_TYPE_MASK; + + return (port_type); +} + +static int +pcicfg_pcie_device_type(dev_info_t *dip, ddi_acc_handle_t handle) +{ + int port_type = pcicfg_pcie_port_type(dip, handle); + + DEBUG1("device port_type = %x\n", port_type); + /* No PCIe CAP regs, we are not PCIe device_type */ + if (port_type < 0) + return (DDI_FAILURE); + + /* check for all PCIe device_types */ + if ((port_type == PCIE_PCIECAP_DEV_TYPE_UP) || + (port_type == PCIE_PCIECAP_DEV_TYPE_DOWN) || + (port_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE)) + return (DDI_SUCCESS); + + return (DDI_FAILURE); + +} + +/* + * In the following functions ndi_devi_enter() without holding the + * parent dip is sufficient. This is because pci dr is driven through + * opens on the nexus which is in the device tree path above the node + * being operated on, and implicitly held due to the open. + */ + +/* + * This entry point is called to configure a device (and + * all its children) on the given bus. It is called when + * a new device is added to the PCI domain. This routine + * will create the device tree and program the devices + * registers. + */ + +int +pcicfg_configure(dev_info_t *devi, uint_t device) +{ + uint_t bus; + int len; + int func; + dev_info_t *new_device; + pcicfg_bus_range_t pci_bus_range; + int rv; + int circ; + uint_t highest_bus = 0; + + /* + * Start probing at the device specified in "device" on the + * "bus" specified. + */ + len = sizeof (pcicfg_bus_range_t); + if (ddi_getlongprop_buf(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, + "bus-range", (caddr_t)&pci_bus_range, &len) != DDI_SUCCESS) { + DEBUG0("no bus-range property\n"); + return (PCICFG_FAILURE); + } + + bus = pci_bus_range.lo; /* primary bus number of this bus node */ + + ndi_devi_enter(devi, &circ); + for (func = 0; func < PCICFG_MAX_FUNCTION; func++) { + + DEBUG3("Configuring [0x%x][0x%x][0x%x]\n", bus, device, func); + + /* + * Try executing fcode if available. + */ + switch (rv = pcicfg_fcode_probe(devi, bus, device, func, + &highest_bus)) { + case PCICFG_FAILURE: + DEBUG2("configure failed: " + "bus [0x%x] device [0x%x]\n", + bus, device); + break; + case PCICFG_NODEVICE: + DEBUG3("no device : bus " + "[0x%x] slot [0x%x] func [0x%x]\n", + bus, device, func); + if (func) + continue; + break; + default: + DEBUG3("configure: bus => [%d] " + "slot => [%d] func => [%d]\n", + bus, device, func); + break; + } + + if (rv != PCICFG_SUCCESS) + break; + + if ((new_device = pcicfg_devi_find(devi, + device, func)) == NULL) { + DEBUG0("Did'nt find device node just created\n"); + goto cleanup; + } + } + ndi_devi_exit(devi, circ); + + if (func == 0) + return (PCICFG_FAILURE); /* probe failed */ + else + return (PCICFG_SUCCESS); + +cleanup: + /* + * Clean up a partially created "probe state" tree. + * There are no resources allocated to the in the + * probe state. + */ + + for (func = 0; func < PCICFG_MAX_FUNCTION; func++) { + if ((new_device = pcicfg_devi_find(devi, + device, func)) == NULL) { + DEBUG0("No more devices to clean up\n"); + continue; + } + + DEBUG2("Cleaning up device [0x%x] function [0x%x]\n", + device, func); + /* + * If this was a bridge device it will have a + * probe handle - if not, no harm in calling this. + */ + (void) pcicfg_destroy_phdl(new_device); + /* + * This will free up the node + */ + (void) ndi_devi_offline(new_device, NDI_DEVI_REMOVE); + } + ndi_devi_exit(devi, circ); + + return (PCICFG_FAILURE); +} + +/* + * configure the child nodes of ntbridge. new_device points to ntbridge itself + */ +/*ARGSUSED*/ +static uint_t +pcicfg_configure_ntbridge(dev_info_t *new_device, uint_t bus, uint_t device) +{ + int bus_range[2], rc = PCICFG_FAILURE, rc1, max_devs = 0; + int devno; + dev_info_t *new_ntbridgechild; + ddi_acc_handle_t config_handle; + uint16_t vid; + uint64_t next_bus; + uint64_t blen; + ndi_ra_request_t req; + uint8_t pcie_device_type = 0; + + /* + * If we need to do indirect config, lets create a property here + * to let the child conf map routine know that it has to + * go through the DDI calls, and not assume the devices are + * mapped directly under the host. + */ + if ((rc = ndi_prop_update_int(DDI_DEV_T_NONE, new_device, + PCI_DEV_CONF_MAP_PROP, (int)DDI_SUCCESS)) + != DDI_SUCCESS) { + + DEBUG0("Cannot create indirect conf map property.\n"); + return ((int)PCICFG_FAILURE); + } + if (pci_config_setup(new_device, &config_handle) != DDI_SUCCESS) + return (PCICFG_FAILURE); + /* check if we are PCIe device */ + if (pcicfg_pcie_device_type(new_device, config_handle) == DDI_SUCCESS) + pcie_device_type = 1; + pci_config_teardown(&config_handle); + + /* create Bus node properties for ntbridge. */ + if (pcicfg_set_busnode_props(new_device, pcie_device_type) != + PCICFG_SUCCESS) { + DEBUG0("Failed to set busnode props\n"); + return (rc); + } + + /* For now: Lets only support one layer of child */ + bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); + req.ra_len = 1; + if (ndi_ra_alloc(ddi_get_parent(new_device), &req, + &next_bus, &blen, NDI_RA_TYPE_PCI_BUSNUM, + NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("ntbridge: Failed to get a bus number\n"); + return (rc); + } + + DEBUG1("ntbridge bus range start ->[%d]\n", next_bus); + + /* + * Following will change, as we detect more bridges + * on the way. + */ + bus_range[0] = (int)next_bus; + bus_range[1] = (int)next_bus; + + if (ndi_prop_update_int_array(DDI_DEV_T_NONE, new_device, + "bus-range", bus_range, 2) != DDI_SUCCESS) { + DEBUG0("Cannot set ntbridge bus-range property"); + return (rc); + } + + /* + * The other interface (away from the host) will be + * initialized by the nexus driver when it loads. + * We just have to set the registers and the nexus driver + * figures out the rest. + */ + + /* + * finally, lets load and attach the driver + * before configuring children of ntbridge. + */ + rc = ndi_devi_online(new_device, NDI_NO_EVENT|NDI_CONFIG); + if (rc != NDI_SUCCESS) { + cmn_err(CE_WARN, + "pcicfg: Fail:cant load nontransparent bridgd driver..\n"); + rc = PCICFG_FAILURE; + return (rc); + } + DEBUG0("pcicfg: Success loading nontransparent bridge nexus driver.."); + + /* Now set aside pci resources for our children. */ + if (pcicfg_ntbridge_allocate_resources(new_device) != + PCICFG_SUCCESS) { + max_devs = 0; + rc = PCICFG_FAILURE; + } else + max_devs = PCICFG_MAX_DEVICE; + + /* Probe devices on 2nd bus */ + for (devno = pcicfg_start_devno; devno < max_devs; devno++) { + + if (ndi_devi_alloc(new_device, DEVI_PSEUDO_NEXNAME, + (pnode_t)DEVI_SID_NODEID, &new_ntbridgechild) + != NDI_SUCCESS) { + + DEBUG0("pcicfg: Failed to alloc test node\n"); + rc = PCICFG_FAILURE; + break; + } + + if (pcicfg_add_config_reg(new_ntbridgechild, next_bus, devno, 0) + != DDI_PROP_SUCCESS) { + cmn_err(CE_WARN, + "Failed to add conf reg for ntbridge child.\n"); + (void) ndi_devi_free(new_ntbridgechild); + rc = PCICFG_FAILURE; + break; + } + + if ((rc = pci_config_setup(new_ntbridgechild, + &config_handle)) != PCICFG_SUCCESS) { + cmn_err(CE_WARN, + "Cannot map ntbridge child %x\n", devno); + (void) ndi_devi_free(new_ntbridgechild); + rc = PCICFG_FAILURE; + break; + } + + /* + * See if there is any PCI HW at this location + * by reading the Vendor ID. If it returns with 0xffff + * then there is no hardware at this location. + */ + vid = pci_config_get16(config_handle, PCI_CONF_VENID); + + pci_config_teardown(&config_handle); + (void) ndi_devi_free(new_ntbridgechild); + if (vid == 0xffff) + continue; + + /* Lets fake attachments points for each child, */ + if (pcicfg_configure(new_device, devno) != PCICFG_SUCCESS) { + int old_dev = pcicfg_start_devno; + + cmn_err(CE_WARN, + "Error configuring ntbridge child dev=%d\n", devno); + + rc = PCICFG_FAILURE; + while (old_dev != devno) { + if (pcicfg_ntbridge_unconfigure_child( + new_device, old_dev) == PCICFG_FAILURE) + + cmn_err(CE_WARN, + "Unconfig Error ntbridge child " + "dev=%d\n", old_dev); + old_dev++; + } + break; + } + } /* devno loop */ + DEBUG1("ntbridge: finish probing 2nd bus, rc=%d\n", rc); + + if (rc != PCICFG_FAILURE) + rc = pcicfg_ntbridge_configure_done(new_device); + else { + pcicfg_phdl_t *entry = pcicfg_find_phdl(new_device); + uint_t *bus; + int k; + + if (ddi_getlongprop(DDI_DEV_T_ANY, new_device, + DDI_PROP_DONTPASS, "bus-range", (caddr_t)&bus, + &k) != DDI_PROP_SUCCESS) { + DEBUG0("Failed to read bus-range property\n"); + rc = PCICFG_FAILURE; + return (rc); + } + + DEBUG2("Need to free bus [%d] range [%d]\n", + bus[0], bus[1] - bus[0] + 1); + + if (ndi_ra_free(ddi_get_parent(new_device), + (uint64_t)bus[0], (uint64_t)(bus[1] - bus[0] + 1), + NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("Failed to free a bus number\n"); + rc = PCICFG_FAILURE; + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)bus, k); + + return (rc); + } + + /* + * Since no memory allocations are done for non transparent + * bridges (but instead we just set the handle with the + * already allocated memory, we just need to reset the + * following values before calling the destroy_phdl() + * function next, otherwise the it will try to free + * memory allocated as in case of a transparent bridge. + */ + entry->memory_len = 0; + entry->io_len = 0; + /* the following will free hole data. */ + (void) pcicfg_destroy_phdl(new_device); + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)bus, k); + } + + /* + * Unload driver just in case child configure failed! + */ + rc1 = ndi_devi_offline(new_device, NDI_NO_EVENT); + DEBUG1("pcicfg: now unloading the ntbridge driver. rc1=%d\n", rc1); + if (rc1 != NDI_SUCCESS) { + cmn_err(CE_WARN, + "pcicfg: cant unload ntbridge driver..children.\n"); + rc = PCICFG_FAILURE; + } + + return (rc); +} + +static int +pcicfg_ntbridge_allocate_resources(dev_info_t *dip) +{ + pcicfg_phdl_t *phdl; + ndi_ra_request_t *mem_request; + ndi_ra_request_t *io_request; + uint64_t boundbase, boundlen; + + phdl = pcicfg_find_phdl(dip); + ASSERT(phdl); + + mem_request = &phdl->mem_req; + io_request = &phdl->io_req; + + phdl->error = PCICFG_SUCCESS; + + /* Set Memory space handle for ntbridge */ + if (pcicfg_get_ntbridge_child_range(dip, &boundbase, &boundlen, + PCI_BASE_SPACE_MEM) != DDI_SUCCESS) { + cmn_err(CE_WARN, + "ntbridge: Mem resource information failure\n"); + phdl->memory_len = 0; + return (PCICFG_FAILURE); + } + mem_request->ra_boundbase = boundbase; + mem_request->ra_boundlen = boundbase + boundlen; + mem_request->ra_len = boundlen; + mem_request->ra_align_mask = + PCICFG_MEMGRAN - 1; /* 1M alignment on memory space */ + mem_request->ra_flags |= NDI_RA_ALLOC_BOUNDED; + + /* + * mem_request->ra_len = + * PCICFG_ROUND_UP(mem_request->ra_len, PCICFG_MEMGRAN); + */ + + phdl->memory_base = phdl->memory_last = boundbase; + phdl->memory_len = boundlen; + phdl->mem_hole.start = phdl->memory_base; + phdl->mem_hole.len = mem_request->ra_len; + phdl->mem_hole.next = (hole_t *)NULL; + + DEBUG2("AP requested [0x%llx], needs [0x%llx] bytes of memory\n", + boundlen, mem_request->ra_len); + + /* set up a memory resource map for NT bridge */ + if (ndi_ra_map_setup(dip, NDI_RA_TYPE_MEM) == NDI_FAILURE) { + DEBUG0("Can not setup ntbridge memory resource map\n"); + return (PCICFG_FAILURE); + } + /* initialize the memory map */ + if (ndi_ra_free(dip, boundbase, boundlen, NDI_RA_TYPE_MEM, + NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("Can not initalize ntbridge memory resource map\n"); + return (PCICFG_FAILURE); + } + /* Set IO space handle for ntbridge */ + if (pcicfg_get_ntbridge_child_range(dip, &boundbase, &boundlen, + PCI_BASE_SPACE_IO) != DDI_SUCCESS) { + cmn_err(CE_WARN, "ntbridge: IO resource information failure\n"); + phdl->io_len = 0; + return (PCICFG_FAILURE); + } + io_request->ra_len = boundlen; + io_request->ra_align_mask = + PCICFG_IOGRAN - 1; /* 4K alignment on I/O space */ + io_request->ra_boundbase = boundbase; + io_request->ra_boundlen = boundbase + boundlen; + io_request->ra_flags |= NDI_RA_ALLOC_BOUNDED; + + /* + * io_request->ra_len = + * PCICFG_ROUND_UP(io_request->ra_len, PCICFG_IOGRAN); + */ + + phdl->io_base = phdl->io_last = (uint32_t)boundbase; + phdl->io_len = (uint32_t)boundlen; + phdl->io_hole.start = phdl->io_base; + phdl->io_hole.len = io_request->ra_len; + phdl->io_hole.next = (hole_t *)NULL; + + DEBUG2("AP requested [0x%llx], needs [0x%llx] bytes of IO\n", + boundlen, io_request->ra_len); + + DEBUG2("MEMORY BASE = [0x%x] length [0x%x]\n", + phdl->memory_base, phdl->memory_len); + DEBUG2("IO BASE = [0x%x] length [0x%x]\n", + phdl->io_base, phdl->io_len); + + /* set up a IO resource map for NT bridge */ + if (ndi_ra_map_setup(dip, NDI_RA_TYPE_IO) == NDI_FAILURE) { + DEBUG0("Can not setup ntbridge memory resource map\n"); + return (PCICFG_FAILURE); + } + /* initialize the IO map */ + if (ndi_ra_free(dip, boundbase, boundlen, NDI_RA_TYPE_IO, + NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("Can not initalize ntbridge memory resource map\n"); + return (PCICFG_FAILURE); + } + + return (PCICFG_SUCCESS); +} + +static int +pcicfg_ntbridge_configure_done(dev_info_t *dip) +{ + pcicfg_range_t range[PCICFG_RANGE_LEN]; + pcicfg_phdl_t *entry; + uint_t len; + pcicfg_bus_range_t bus_range; + int new_bus_range[2]; + + DEBUG1("Configuring children for %llx\n", dip); + + entry = pcicfg_find_phdl(dip); + ASSERT(entry); + + bzero((caddr_t)range, + sizeof (pcicfg_range_t) * PCICFG_RANGE_LEN); + range[1].child_hi = range[1].parent_hi |= + (PCI_REG_REL_M | PCI_ADDR_MEM32); + range[1].child_lo = range[1].parent_lo = (uint32_t)entry->memory_base; + + range[0].child_hi = range[0].parent_hi |= + (PCI_REG_REL_M | PCI_ADDR_IO); + range[0].child_lo = range[0].parent_lo = (uint32_t)entry->io_base; + + len = sizeof (pcicfg_bus_range_t); + if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "bus-range", (caddr_t)&bus_range, (int *)&len) != DDI_SUCCESS) { + DEBUG0("no bus-range property\n"); + return (PCICFG_FAILURE); + } + + new_bus_range[0] = bus_range.lo; /* primary bus number */ + if (entry->highest_bus) { /* secondary bus number */ + if (entry->highest_bus < bus_range.lo) { + cmn_err(CE_WARN, + "ntbridge bus range invalid !(%d,%d)\n", + bus_range.lo, entry->highest_bus); + new_bus_range[1] = bus_range.lo + entry->highest_bus; + } + else + new_bus_range[1] = entry->highest_bus; + } + else + new_bus_range[1] = bus_range.hi; + + DEBUG2("ntbridge: bus range lo=%x, hi=%x\n", + new_bus_range[0], new_bus_range[1]); + + if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, + "bus-range", new_bus_range, 2) != DDI_SUCCESS) { + DEBUG0("Failed to set bus-range property"); + entry->error = PCICFG_FAILURE; + return (PCICFG_FAILURE); + } + +#ifdef DEBUG + { + uint64_t unused; + unused = pcicfg_unused_space(&entry->io_hole, &len); + DEBUG2("ntbridge: Unused IO space %llx bytes over %d holes\n", + unused, len); + } +#endif + + range[0].size_lo = entry->io_len; + if (pcicfg_update_ranges_prop(dip, &range[0])) { + DEBUG0("Failed to update ranges (i/o)\n"); + entry->error = PCICFG_FAILURE; + return (PCICFG_FAILURE); + } + +#ifdef DEBUG + { + uint64_t unused; + unused = pcicfg_unused_space(&entry->mem_hole, &len); + DEBUG2("ntbridge: Unused Mem space %llx bytes over %d holes\n", + unused, len); + } +#endif + + range[1].size_lo = entry->memory_len; + if (pcicfg_update_ranges_prop(dip, &range[1])) { + DEBUG0("Failed to update ranges (memory)\n"); + entry->error = PCICFG_FAILURE; + return (PCICFG_FAILURE); + } + + return (PCICFG_SUCCESS); +} + +static int +pcicfg_ntbridge_unconfigure_child(dev_info_t *new_device, uint_t devno) +{ + + dev_info_t *new_ntbridgechild; + int len, bus; + uint16_t vid; + ddi_acc_handle_t config_handle; + pcicfg_bus_range_t pci_bus_range; + + len = sizeof (pcicfg_bus_range_t); + if (ddi_getlongprop_buf(DDI_DEV_T_ANY, new_device, DDI_PROP_DONTPASS, + "bus-range", (caddr_t)&pci_bus_range, &len) != DDI_SUCCESS) { + DEBUG0("no bus-range property\n"); + return (PCICFG_FAILURE); + } + + bus = pci_bus_range.lo; /* primary bus number of this bus node */ + + if (ndi_devi_alloc(new_device, DEVI_PSEUDO_NEXNAME, + (pnode_t)DEVI_SID_NODEID, &new_ntbridgechild) != NDI_SUCCESS) { + + DEBUG0("pcicfg: Failed to alloc test node\n"); + return (PCICFG_FAILURE); + } + + if (pcicfg_add_config_reg(new_ntbridgechild, bus, devno, 0) + != DDI_PROP_SUCCESS) { + cmn_err(CE_WARN, + "Unconfigure: Failed to add conf reg prop for ntbridge " + "child.\n"); + (void) ndi_devi_free(new_ntbridgechild); + return (PCICFG_FAILURE); + } + + if (pcicfg_config_setup(new_ntbridgechild, &config_handle) + != DDI_SUCCESS) { + cmn_err(CE_WARN, + "pcicfg: Cannot map ntbridge child %x\n", devno); + (void) ndi_devi_free(new_ntbridgechild); + return (PCICFG_FAILURE); + } + + /* + * See if there is any PCI HW at this location + * by reading the Vendor ID. If it returns with 0xffff + * then there is no hardware at this location. + */ + vid = pci_config_get16(config_handle, PCI_CONF_VENID); + + pci_config_teardown(&config_handle); + (void) ndi_devi_free(new_ntbridgechild); + if (vid == 0xffff) + return (PCICFG_NODEVICE); + + return (pcicfg_unconfigure(new_device, devno)); +} + +static int +pcicfg_ntbridge_unconfigure(dev_info_t *dip) +{ + pcicfg_phdl_t *entry = pcicfg_find_phdl(dip); + uint_t *bus; + int k, rc = PCICFG_FAILURE; + + if (entry->memory_len) + if (ndi_ra_map_destroy(dip, NDI_RA_TYPE_MEM) == NDI_FAILURE) { + DEBUG1("cannot destroy ntbridge memory map size=%x\n", + entry->memory_len); + return (PCICFG_FAILURE); + } + if (entry->io_len) + if (ndi_ra_map_destroy(dip, NDI_RA_TYPE_IO) == NDI_FAILURE) { + DEBUG1("cannot destroy ntbridge io map size=%x\n", + entry->io_len); + return (PCICFG_FAILURE); + } + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "bus-range", (caddr_t)&bus, + &k) != DDI_PROP_SUCCESS) { + DEBUG0("ntbridge: Failed to read bus-range property\n"); + return (rc); + } + + DEBUG2("ntbridge: Need to free bus [%d] range [%d]\n", + bus[0], bus[1] - bus[0] + 1); + + if (ndi_ra_free(ddi_get_parent(dip), + (uint64_t)bus[0], (uint64_t)(bus[1] - bus[0] + 1), + NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("ntbridge: Failed to free a bus number\n"); + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)bus, k); + + return (rc); + } + + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)bus, k); + + /* + * Since our resources will be freed at the parent level, + * just reset these values. + */ + entry->memory_len = 0; + entry->io_len = 0; + /* the following will also free hole data. */ + return (pcicfg_destroy_phdl(dip)); + +} + +static int +pcicfg_is_ntbridge(dev_info_t *dip) +{ + ddi_acc_handle_t config_handle; + uint8_t class, subclass; + int rc = DDI_SUCCESS; + + if (pcicfg_config_setup(dip, &config_handle) != DDI_SUCCESS) { + cmn_err(CE_WARN, + "pcicfg: cannot map config space, to get map type\n"); + return (DDI_FAILURE); + } + class = pci_config_get8(config_handle, PCI_CONF_BASCLASS); + subclass = pci_config_get8(config_handle, PCI_CONF_SUBCLASS); + + /* check for class=6, subclass=9, for non transparent bridges. */ + if ((class != PCI_CLASS_BRIDGE) || (subclass != PCI_BRIDGE_STBRIDGE)) + rc = DDI_FAILURE; + + DEBUG3("pcicfg: checking device %x,%x for indirect map. rc=%d\n", + pci_config_get16(config_handle, PCI_CONF_VENID), + pci_config_get16(config_handle, PCI_CONF_DEVID), + rc); + pci_config_teardown(&config_handle); + return (rc); +} + +/* + * this function is called only for SPARC platforms, where we may have + * a mix n' match of direct vs indirectly mapped configuration space. + * On x86, this function does not get called. We always return TRUE + * via a macro for x86. + */ +/*ARGSUSED*/ +static int +pcicfg_indirect_map(dev_info_t *dip) +{ +#if defined(__sparc) + int rc = DDI_FAILURE; + + if (ddi_prop_get_int(DDI_DEV_T_ANY, ddi_get_parent(dip), 0, + PCI_DEV_CONF_MAP_PROP, DDI_FAILURE) != DDI_FAILURE) + rc = DDI_SUCCESS; + else + if (ddi_prop_get_int(DDI_DEV_T_ANY, ddi_get_parent(dip), + 0, PCI_BUS_CONF_MAP_PROP, + DDI_FAILURE) != DDI_FAILURE) + rc = DDI_SUCCESS; + DEBUG1("pci conf map = %d", rc); + return (rc); +#else + return (DDI_SUCCESS); +#endif +} + +static uint_t +pcicfg_get_ntbridge_child_range(dev_info_t *dip, uint64_t *boundbase, + uint64_t *boundlen, uint_t space_type) +{ + int length, found = DDI_FAILURE, acount, i, ibridge; + pci_regspec_t *assigned; + + if ((ibridge = pcicfg_is_ntbridge(dip)) == DDI_FAILURE) + return (found); + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned, + &length) != DDI_PROP_SUCCESS) { + DEBUG1("Failed to get assigned-addresses property %llx\n", dip); + return (found); + } + DEBUG1("pcicfg: ntbridge child range: dip = %s\n", + ddi_driver_name(dip)); + + acount = length / sizeof (pci_regspec_t); + + for (i = 0; i < acount; i++) { + if ((PCI_REG_REG_G(assigned[i].pci_phys_hi) + == pcicfg_indirect_map_devs[ibridge].mem_range_bar_offset) && + (space_type == PCI_BASE_SPACE_MEM)) { + found = DDI_SUCCESS; + break; + } else { + if ((PCI_REG_REG_G(assigned[i].pci_phys_hi) + == pcicfg_indirect_map_devs[ibridge].io_range_bar_offset) && + (space_type == PCI_BASE_SPACE_IO)) { + found = DDI_SUCCESS; + break; + } + } + } + DEBUG3("pcicfg: ntbridge child range: space=%x, base=%lx, len=%lx\n", + space_type, assigned[i].pci_phys_low, assigned[i].pci_size_low); + + if (found == DDI_SUCCESS) { + *boundbase = assigned[i].pci_phys_low; + *boundlen = assigned[i].pci_size_low; + } + + kmem_free(assigned, length); + return (found); +} + +/* + * This will turn resources allocated by pcicfg_configure() + * and remove the device tree from the attachment point + * and below. The routine assumes the devices have their + * drivers detached. + */ +int +pcicfg_unconfigure(dev_info_t *devi, uint_t device) +{ + dev_info_t *child_dip; + int func; + int i; + + /* + * Cycle through devices to make sure none are busy. + * If a single device is busy fail the whole unconfigure. + */ + for (func = 0; func < PCICFG_MAX_FUNCTION; func++) { + if ((child_dip = pcicfg_devi_find(devi, device, func)) == NULL) + continue; + + if (ndi_devi_offline(child_dip, NDI_UNCONFIG) == NDI_SUCCESS) + continue; + /* + * Device function is busy. Before returning we have to + * put all functions back online which were taken + * offline during the process. + */ + DEBUG2("Device [0x%x] function [%x] is busy\n", device, func); + for (i = 0; i < func; i++) { + if ((child_dip = pcicfg_devi_find(devi, device, i)) + == NULL) { + DEBUG0("No more devices to put back on line!!\n"); + /* + * Made it through all functions + */ + continue; + } + if (ndi_devi_online(child_dip, NDI_CONFIG) != NDI_SUCCESS) { + DEBUG0("Failed to put back devices state\n"); + return (PCICFG_FAILURE); + } + } + return (PCICFG_FAILURE); + } + + /* + * Now, tear down all devinfo nodes for this AP. + */ + for (func = 0; func < PCICFG_MAX_FUNCTION; func++) { + if ((child_dip = pcicfg_devi_find(devi, + device, func)) == NULL) { + DEBUG0("No more devices to tear down!\n"); + continue; + } + + DEBUG2("Tearing down device [0x%x] function [0x%x]\n", + device, func); + + if (pcicfg_is_ntbridge(child_dip) != DDI_FAILURE) + if (pcicfg_ntbridge_unconfigure(child_dip) != + PCICFG_SUCCESS) { + cmn_err(CE_WARN, + "ntbridge: unconfigure failed\n"); + return (PCICFG_FAILURE); + } + + if (pcicfg_teardown_device(child_dip) != PCICFG_SUCCESS) { + DEBUG2("Failed to tear down device [0x%x]" + "function [0x%x]\n", + device, func); + return (PCICFG_FAILURE); + } + } + + return (PCICFG_SUCCESS); +} + +static int +pcicfg_teardown_device(dev_info_t *dip) +{ + ddi_acc_handle_t config_handle; + + /* + * Free up resources associated with 'dip' + */ + + if (pcicfg_free_resources(dip) != PCICFG_SUCCESS) { + DEBUG0("Failed to free resources\n"); + return (PCICFG_FAILURE); + } + + /* + * This will disable the device + */ + if (pci_config_setup(dip, &config_handle) != PCICFG_SUCCESS) { + return (PCICFG_FAILURE); + } + + pcicfg_device_off(config_handle); + pci_config_teardown(&config_handle); + + /* + * The framework provides this routine which can + * tear down a sub-tree. + */ + if (ndi_devi_offline(dip, NDI_DEVI_REMOVE) != NDI_SUCCESS) { + DEBUG0("Failed to offline and remove node\n"); + return (PCICFG_FAILURE); + } + + return (PCICFG_SUCCESS); +} + +/* + * BEGIN GENERIC SUPPORT ROUTINES + */ +static pcicfg_phdl_t * +pcicfg_find_phdl(dev_info_t *dip) +{ + pcicfg_phdl_t *entry; + mutex_enter(&pcicfg_list_mutex); + for (entry = pcicfg_phdl_list; entry != NULL; entry = entry->next) { + if (entry->dip == dip) { + mutex_exit(&pcicfg_list_mutex); + return (entry); + } + } + mutex_exit(&pcicfg_list_mutex); + + /* + * Did'nt find entry - create one + */ + return (pcicfg_create_phdl(dip)); +} + +static pcicfg_phdl_t * +pcicfg_create_phdl(dev_info_t *dip) +{ + pcicfg_phdl_t *new; + + new = (pcicfg_phdl_t *)kmem_zalloc(sizeof (pcicfg_phdl_t), + KM_SLEEP); + + new->dip = dip; + mutex_enter(&pcicfg_list_mutex); + new->next = pcicfg_phdl_list; + pcicfg_phdl_list = new; + mutex_exit(&pcicfg_list_mutex); + + return (new); +} + +static int +pcicfg_destroy_phdl(dev_info_t *dip) +{ + pcicfg_phdl_t *entry; + pcicfg_phdl_t *follow = NULL; + + mutex_enter(&pcicfg_list_mutex); + for (entry = pcicfg_phdl_list; entry != NULL; follow = entry, + entry = entry->next) { + if (entry->dip == dip) { + if (entry == pcicfg_phdl_list) { + pcicfg_phdl_list = entry->next; + } else { + follow->next = entry->next; + } + /* + * If this entry has any allocated memory + * or IO space associated with it, that + * must be freed up. + */ + if (entry->memory_len > 0) { + (void) ndi_ra_free(ddi_get_parent(dip), + entry->memory_base, + entry->memory_len, + NDI_RA_TYPE_MEM, NDI_RA_PASS); + } + pcicfg_free_hole(&entry->mem_hole); + + if (entry->io_len > 0) { + (void) ndi_ra_free(ddi_get_parent(dip), + entry->io_base, + entry->io_len, + NDI_RA_TYPE_IO, NDI_RA_PASS); + } + pcicfg_free_hole(&entry->io_hole); + + /* + * Destroy this entry + */ + kmem_free((caddr_t)entry, sizeof (pcicfg_phdl_t)); + mutex_exit(&pcicfg_list_mutex); + return (PCICFG_SUCCESS); + } + } + mutex_exit(&pcicfg_list_mutex); + /* + * Did'nt find the entry + */ + return (PCICFG_FAILURE); +} + +static int +pcicfg_program_ap(dev_info_t *dip) +{ + pcicfg_phdl_t *phdl; + uint8_t header_type; + ddi_acc_handle_t handle; + pcicfg_phdl_t *entry; + + if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) { + DEBUG0("Failed to map config space!\n"); + return (PCICFG_FAILURE); + + } + + header_type = pci_config_get8(handle, PCI_CONF_HEADER); + + (void) pcicfg_config_teardown(&handle); + + if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { + + if (pcicfg_allocate_chunk(dip) != PCICFG_SUCCESS) { + DEBUG0("Not enough memory to hotplug\n"); + (void) pcicfg_destroy_phdl(dip); + return (PCICFG_FAILURE); + } + + phdl = pcicfg_find_phdl(dip); + ASSERT(phdl); + + (void) pcicfg_bridge_assign(dip, (void *)phdl); + + if (phdl->error != PCICFG_SUCCESS) { + DEBUG0("Problem assigning bridge\n"); + (void) pcicfg_destroy_phdl(dip); + return (phdl->error); + } + + /* + * Successfully allocated and assigned + * memory. Set the memory and IO length + * to zero so when the handle is freed up + * it will not de-allocate assigned resources. + */ + entry = (pcicfg_phdl_t *)phdl; + + entry->memory_len = entry->io_len = 0; + + /* + * Free up the "entry" structure. + */ + (void) pcicfg_destroy_phdl(dip); + } else { + if (pcicfg_device_assign(dip) != PCICFG_SUCCESS) { + return (PCICFG_FAILURE); + } + } + return (PCICFG_SUCCESS); +} + +static int +pcicfg_bridge_assign(dev_info_t *dip, void *hdl) +{ + ddi_acc_handle_t handle; + pci_regspec_t *reg; + int length; + int rcount; + int i; + int offset; + uint64_t mem_answer; + uint32_t io_answer; + int count; + uint8_t header_type; + pcicfg_range_t range[PCICFG_RANGE_LEN]; + int bus_range[2]; + + pcicfg_phdl_t *entry = (pcicfg_phdl_t *)hdl; + + DEBUG1("bridge assign: assigning addresses to %s\n", ddi_get_name(dip)); + + if (entry == NULL) { + DEBUG0("Failed to get entry\n"); + return (DDI_WALK_TERMINATE); + } + + entry->error = PCICFG_SUCCESS; + + if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) { + DEBUG0("Failed to map config space!\n"); + entry->error = PCICFG_FAILURE; + return (DDI_WALK_TERMINATE); + } + + header_type = pci_config_get8(handle, PCI_CONF_HEADER); + + if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { + + bzero((caddr_t)range, + sizeof (pcicfg_range_t) * PCICFG_RANGE_LEN); + + (void) pcicfg_setup_bridge(entry, handle); + + range[0].child_hi = range[0].parent_hi |= + (PCI_REG_REL_M | PCI_ADDR_IO); + range[0].child_lo = range[0].parent_lo = + entry->io_last; + range[1].child_hi = range[1].parent_hi |= + (PCI_REG_REL_M | PCI_ADDR_MEM32); + range[1].child_lo = range[1].parent_lo = + entry->memory_last; + + ndi_devi_enter(dip, &count); + ddi_walk_devs(ddi_get_child(dip), + pcicfg_bridge_assign, (void *)entry); + ndi_devi_exit(dip, count); + + (void) pcicfg_update_bridge(entry, handle); + + bus_range[0] = pci_config_get8(handle, PCI_BCNF_SECBUS); + bus_range[1] = pci_config_get8(handle, PCI_BCNF_SUBBUS); + + if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, + "bus-range", bus_range, 2) != DDI_SUCCESS) { + DEBUG0("Failed to set bus-range property"); + entry->error = PCICFG_FAILURE; + return (DDI_WALK_TERMINATE); + } + + if (entry->io_len > 0) { + range[0].size_lo = entry->io_last - entry->io_base; + if (pcicfg_update_ranges_prop(dip, &range[0])) { + DEBUG0("Failed to update ranges (i/o)\n"); + entry->error = PCICFG_FAILURE; + return (DDI_WALK_TERMINATE); + } + } + if (entry->memory_len > 0) { + range[1].size_lo = + entry->memory_last - entry->memory_base; + if (pcicfg_update_ranges_prop(dip, &range[1])) { + DEBUG0("Failed to update ranges (memory)\n"); + entry->error = PCICFG_FAILURE; + return (DDI_WALK_TERMINATE); + } + } + + (void) pcicfg_device_on(handle); + + PCICFG_DUMP_BRIDGE_CONFIG(handle); + + return (DDI_WALK_PRUNECHILD); + } + + /* + * If there is an interrupt pin set program + * interrupt line with default values. + */ + if (pci_config_get8(handle, PCI_CONF_IPIN)) { + pci_config_put8(handle, PCI_CONF_ILINE, 0xf); + } + + /* + * A single device (under a bridge). + * For each "reg" property with a length, allocate memory + * and program the base registers. + */ + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "reg", (caddr_t)®, + &length) != DDI_PROP_SUCCESS) { + DEBUG0("Failed to read reg property\n"); + entry->error = PCICFG_FAILURE; + return (DDI_WALK_TERMINATE); + } + + rcount = length / sizeof (pci_regspec_t); + offset = PCI_CONF_BASE0; + for (i = 0; i < rcount; i++) { + if ((reg[i].pci_size_low != 0)|| + (reg[i].pci_size_hi != 0)) { + + offset = PCI_REG_REG_G(reg[i].pci_phys_hi); + + switch (PCI_REG_ADDR_G(reg[i].pci_phys_hi)) { + case PCI_REG_ADDR_G(PCI_ADDR_MEM64): + + (void) pcicfg_get_mem(entry, + reg[i].pci_size_low, &mem_answer); + pci_config_put64(handle, offset, mem_answer); + DEBUG2("REGISTER off %x (64)LO ----> [0x%x]\n", + offset, + pci_config_get32(handle, offset)); + DEBUG2("REGISTER off %x (64)HI ----> [0x%x]\n", + offset + 4, + pci_config_get32(handle, offset + 4)); + + reg[i].pci_phys_low = PCICFG_HIADDR(mem_answer); + reg[i].pci_phys_mid = + PCICFG_LOADDR(mem_answer); + + break; + + case PCI_REG_ADDR_G(PCI_ADDR_MEM32): + /* allocate memory space from the allocator */ + + (void) pcicfg_get_mem(entry, + reg[i].pci_size_low, &mem_answer); + pci_config_put32(handle, + offset, (uint32_t)mem_answer); + + DEBUG2("REGISTER off %x(32)LO ----> [0x%x]\n", + offset, + pci_config_get32(handle, offset)); + + reg[i].pci_phys_low = (uint32_t)mem_answer; + + break; + case PCI_REG_ADDR_G(PCI_ADDR_IO): + /* allocate I/O space from the allocator */ + + (void) pcicfg_get_io(entry, + reg[i].pci_size_low, &io_answer); + pci_config_put32(handle, offset, io_answer); + + DEBUG2("REGISTER off %x (I/O)LO ----> [0x%x]\n", + offset, + pci_config_get32(handle, offset)); + + reg[i].pci_phys_low = io_answer; + + break; + default: + DEBUG0("Unknown register type\n"); + kmem_free(reg, length); + (void) pcicfg_config_teardown(&handle); + entry->error = PCICFG_FAILURE; + return (DDI_WALK_TERMINATE); + } /* switch */ + + /* + * Now that memory locations are assigned, + * update the assigned address property. + */ + if (pcicfg_update_assigned_prop(dip, + ®[i]) != PCICFG_SUCCESS) { + kmem_free(reg, length); + (void) pcicfg_config_teardown(&handle); + entry->error = PCICFG_FAILURE; + return (DDI_WALK_TERMINATE); + } + } + } + (void) pcicfg_device_on(handle); + + PCICFG_DUMP_DEVICE_CONFIG(handle); + + (void) pcicfg_config_teardown(&handle); + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)reg, length); + + return (DDI_WALK_CONTINUE); +} +static int +pcicfg_device_assign(dev_info_t *dip) +{ + ddi_acc_handle_t handle; + pci_regspec_t *reg; + int length; + int rcount; + int i; + int offset; + ndi_ra_request_t request; + uint64_t answer; + uint64_t alen; + + + DEBUG1("%llx now under configuration\n", dip); + + /* + * XXX Failure here should be noted + */ + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "reg", (caddr_t)®, + &length) != DDI_PROP_SUCCESS) { + DEBUG0("Failed to read reg property\n"); + return (PCICFG_FAILURE); + } + + if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) { + DEBUG0("Failed to map config space!\n"); + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)reg, length); + + return (PCICFG_FAILURE); + } + + /* + * A single device + * + * For each "reg" property with a length, allocate memory + * and program the base registers. + */ + + /* + * If there is an interrupt pin set program + * interrupt line with default values. + */ + if (pci_config_get8(handle, PCI_CONF_IPIN)) { + pci_config_put8(handle, PCI_CONF_ILINE, 0xf); + } + + bzero((caddr_t)&request, sizeof (ndi_ra_request_t)); + + request.ra_flags |= NDI_RA_ALIGN_SIZE; + request.ra_boundbase = 0; + request.ra_boundlen = PCICFG_4GIG_LIMIT; + + rcount = length / sizeof (pci_regspec_t); + offset = PCI_CONF_BASE0; + for (i = 0; i < rcount; i++) { + if ((reg[i].pci_size_low != 0)|| + (reg[i].pci_size_hi != 0)) { + + offset = PCI_REG_REG_G(reg[i].pci_phys_hi); + request.ra_len = reg[i].pci_size_low; + + switch (PCI_REG_ADDR_G(reg[i].pci_phys_hi)) { + case PCI_REG_ADDR_G(PCI_ADDR_MEM64): + request.ra_flags ^= NDI_RA_ALLOC_BOUNDED; + /* allocate memory space from the allocator */ + if (ndi_ra_alloc(ddi_get_parent(dip), + &request, &answer, &alen, + NDI_RA_TYPE_MEM, NDI_RA_PASS) + != NDI_SUCCESS) { + DEBUG0("Failed to allocate 64b mem\n"); + kmem_free(reg, length); + (void) pcicfg_config_teardown(&handle); + return (PCICFG_FAILURE); + } + DEBUG3("64 addr = [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(answer), + PCICFG_LOADDR(answer), + alen); + /* program the low word */ + pci_config_put32(handle, + offset, PCICFG_LOADDR(answer)); + + /* program the high word with value zero */ + pci_config_put32(handle, offset + 4, + PCICFG_HIADDR(answer)); + + reg[i].pci_phys_low = PCICFG_LOADDR(answer); + reg[i].pci_phys_mid = PCICFG_HIADDR(answer); + /* + * currently support 32b address space + * assignments only. + */ + reg[i].pci_phys_hi ^= PCI_ADDR_MEM64 ^ + PCI_ADDR_MEM32; + + offset += 8; + break; + + case PCI_REG_ADDR_G(PCI_ADDR_MEM32): + request.ra_flags |= NDI_RA_ALLOC_BOUNDED; + /* allocate memory space from the allocator */ + if (ndi_ra_alloc(ddi_get_parent(dip), + &request, &answer, &alen, + NDI_RA_TYPE_MEM, NDI_RA_PASS) + != NDI_SUCCESS) { + DEBUG0("Failed to allocate 32b mem\n"); + kmem_free(reg, length); + (void) pcicfg_config_teardown(&handle); + return (PCICFG_FAILURE); + } + DEBUG3("32 addr = [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(answer), + PCICFG_LOADDR(answer), + alen); + /* program the low word */ + pci_config_put32(handle, + offset, PCICFG_LOADDR(answer)); + + reg[i].pci_phys_low = PCICFG_LOADDR(answer); + + offset += 4; + break; + case PCI_REG_ADDR_G(PCI_ADDR_IO): + /* allocate I/O space from the allocator */ + request.ra_flags |= NDI_RA_ALLOC_BOUNDED; + if (ndi_ra_alloc(ddi_get_parent(dip), + &request, &answer, &alen, + NDI_RA_TYPE_IO, NDI_RA_PASS) + != NDI_SUCCESS) { + DEBUG0("Failed to allocate I/O\n"); + kmem_free(reg, length); + (void) pcicfg_config_teardown(&handle); + return (PCICFG_FAILURE); + } + DEBUG3("I/O addr = [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(answer), + PCICFG_LOADDR(answer), + alen); + pci_config_put32(handle, + offset, PCICFG_LOADDR(answer)); + + reg[i].pci_phys_low = PCICFG_LOADDR(answer); + + offset += 4; + break; + default: + DEBUG0("Unknown register type\n"); + kmem_free(reg, length); + (void) pcicfg_config_teardown(&handle); + return (PCICFG_FAILURE); + } /* switch */ + + /* + * Now that memory locations are assigned, + * update the assigned address property. + */ + + if (pcicfg_update_assigned_prop(dip, + ®[i]) != PCICFG_SUCCESS) { + kmem_free(reg, length); + (void) pcicfg_config_teardown(&handle); + return (PCICFG_FAILURE); + } + } + } + + (void) pcicfg_device_on(handle); + kmem_free(reg, length); + + PCICFG_DUMP_DEVICE_CONFIG(handle); + + (void) pcicfg_config_teardown(&handle); + return (PCICFG_SUCCESS); +} + +/* + * The "dip" passed to this routine is assumed to be + * the device at the attachment point. Currently it is + * assumed to be a bridge. + */ +static int +pcicfg_allocate_chunk(dev_info_t *dip) +{ + pcicfg_phdl_t *phdl; + ndi_ra_request_t *mem_request; + ndi_ra_request_t *io_request; + uint64_t mem_answer; + uint64_t io_answer; + int count; + uint64_t alen; + + /* + * This should not find an existing entry - so + * it will create a new one. + */ + phdl = pcicfg_find_phdl(dip); + ASSERT(phdl); + + mem_request = &phdl->mem_req; + io_request = &phdl->io_req; + + /* + * From this point in the tree - walk the devices, + * The function passed in will read and "sum" up + * the memory and I/O requirements and put them in + * structure "phdl". + */ + ndi_devi_enter(ddi_get_parent(dip), &count); + ddi_walk_devs(dip, pcicfg_sum_resources, (void *)phdl); + ndi_devi_exit(ddi_get_parent(dip), count); + + if (phdl->error != PCICFG_SUCCESS) { + DEBUG0("Failure summing resources\n"); + return (phdl->error); + } + + /* + * Call into the memory allocator with the request. + * Record the addresses returned in the phdl + */ + DEBUG1("AP requires [0x%x] bytes of memory space\n", + mem_request->ra_len); + DEBUG1("AP requires [0x%x] bytes of I/O space\n", + io_request->ra_len); + + mem_request->ra_align_mask = + PCICFG_MEMGRAN - 1; /* 1M alignment on memory space */ + io_request->ra_align_mask = + PCICFG_IOGRAN - 1; /* 4K alignment on I/O space */ + io_request->ra_boundbase = 0; + io_request->ra_boundlen = PCICFG_4GIG_LIMIT; + io_request->ra_flags |= NDI_RA_ALLOC_BOUNDED; + + mem_request->ra_len = + PCICFG_ROUND_UP(mem_request->ra_len, PCICFG_MEMGRAN); + + io_request->ra_len = + PCICFG_ROUND_UP(io_request->ra_len, PCICFG_IOGRAN); + + if (ndi_ra_alloc(ddi_get_parent(dip), + mem_request, &mem_answer, &alen, + NDI_RA_TYPE_MEM, NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("Failed to allocate memory\n"); + return (PCICFG_FAILURE); + } + + phdl->memory_base = phdl->memory_last = mem_answer; + phdl->memory_len = alen; + + phdl->mem_hole.start = phdl->memory_base; + phdl->mem_hole.len = phdl->memory_len; + phdl->mem_hole.next = (hole_t *)NULL; + + if (ndi_ra_alloc(ddi_get_parent(dip), io_request, &io_answer, + &alen, NDI_RA_TYPE_IO, NDI_RA_PASS) != NDI_SUCCESS) { + + DEBUG0("Failed to allocate I/O space\n"); + (void) ndi_ra_free(ddi_get_parent(dip), mem_answer, + alen, NDI_RA_TYPE_MEM, NDI_RA_PASS); + phdl->memory_len = phdl->io_len = 0; + return (PCICFG_FAILURE); + } + + phdl->io_base = phdl->io_last = (uint32_t)io_answer; + phdl->io_len = (uint32_t)alen; + + phdl->io_hole.start = phdl->io_base; + phdl->io_hole.len = phdl->io_len; + phdl->io_hole.next = (hole_t *)NULL; + + DEBUG2("MEMORY BASE = [0x%x] length [0x%x]\n", + phdl->memory_base, phdl->memory_len); + DEBUG2("IO BASE = [0x%x] length [0x%x]\n", + phdl->io_base, phdl->io_len); + + return (PCICFG_SUCCESS); +} + +#ifdef DEBUG +/* + * This function is useful in debug mode, where we can measure how + * much memory was wasted/unallocated in bridge device's domain. + */ +static uint64_t +pcicfg_unused_space(hole_t *hole, uint32_t *hole_count) +{ + uint64_t len = 0; + uint32_t count = 0; + + do { + len += hole->len; + hole = hole->next; + count++; + } while (hole); + *hole_count = count; + return (len); +} +#endif + +/* + * This function frees data structures that hold the hole information + * which are allocated in pcicfg_alloc_hole(). This is not freeing + * any memory allocated through NDI calls. + */ +static void +pcicfg_free_hole(hole_t *addr_hole) +{ + hole_t *nhole, *hole = addr_hole->next; + + while (hole) { + nhole = hole->next; + kmem_free(hole, sizeof (hole_t)); + hole = nhole; + } +} + +static uint64_t +pcicfg_alloc_hole(hole_t *addr_hole, uint64_t *alast, uint32_t length) +{ + uint64_t actual_hole_start, ostart, olen; + hole_t *hole = addr_hole, *thole, *nhole; + + do { + actual_hole_start = PCICFG_ROUND_UP(hole->start, length); + if (((actual_hole_start - hole->start) + length) <= hole->len) { + DEBUG3("hole found. start %llx, len %llx, req=%x\n", + hole->start, hole->len, length); + ostart = hole->start; + olen = hole->len; + /* current hole parameters adjust */ + if ((actual_hole_start - hole->start) == 0) { + hole->start += length; + hole->len -= length; + if (hole->start > *alast) + *alast = hole->start; + } else { + hole->len = actual_hole_start - hole->start; + nhole = (hole_t *)kmem_zalloc(sizeof (hole_t), + KM_SLEEP); + nhole->start = actual_hole_start + length; + nhole->len = (ostart + olen) - nhole->start; + nhole->next = NULL; + thole = hole->next; + hole->next = nhole; + nhole->next = thole; + if (nhole->start > *alast) + *alast = nhole->start; + DEBUG2("put new hole to %llx, %llx\n", + nhole->start, nhole->len); + } + DEBUG2("adjust current hole to %llx, %llx\n", + hole->start, hole->len); + break; + } + actual_hole_start = 0; + hole = hole->next; + } while (hole); + + DEBUG1("return hole at %llx\n", actual_hole_start); + return (actual_hole_start); +} + +static void +pcicfg_get_mem(pcicfg_phdl_t *entry, + uint32_t length, uint64_t *ans) +{ + uint64_t new_mem; + + /* See if there is a hole, that can hold this request. */ + new_mem = pcicfg_alloc_hole(&entry->mem_hole, &entry->memory_last, + length); + if (new_mem) { /* if non-zero, found a hole. */ + if (ans != NULL) + *ans = new_mem; + } else + cmn_err(CE_WARN, "No %u bytes memory window for %s\n", + length, ddi_get_name(entry->dip)); +} + +static void +pcicfg_get_io(pcicfg_phdl_t *entry, + uint32_t length, uint32_t *ans) +{ + uint32_t new_io; + uint64_t io_last; + + /* + * See if there is a hole, that can hold this request. + * Pass 64 bit parameters and then truncate to 32 bit. + */ + io_last = entry->io_last; + new_io = (uint32_t)pcicfg_alloc_hole(&entry->io_hole, &io_last, length); + if (new_io) { /* if non-zero, found a hole. */ + entry->io_last = (uint32_t)io_last; + if (ans != NULL) + *ans = new_io; + } else + cmn_err(CE_WARN, "No %u bytes IO space window for %s\n", + length, ddi_get_name(entry->dip)); +} + +static int +pcicfg_sum_resources(dev_info_t *dip, void *hdl) +{ + pcicfg_phdl_t *entry = (pcicfg_phdl_t *)hdl; + pci_regspec_t *pci_rp; + int length; + int rcount; + int i; + ndi_ra_request_t *mem_request; + ndi_ra_request_t *io_request; + uint8_t header_type; + ddi_acc_handle_t handle; + + entry->error = PCICFG_SUCCESS; + + mem_request = &entry->mem_req; + io_request = &entry->io_req; + + if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) { + DEBUG0("Failed to map config space!\n"); + entry->error = PCICFG_FAILURE; + return (DDI_WALK_TERMINATE); + } + + header_type = pci_config_get8(handle, PCI_CONF_HEADER); + + /* + * If its a bridge - just record the highest bus seen + */ + if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { + + if (entry->highest_bus < pci_config_get8(handle, + PCI_BCNF_SECBUS)) { + entry->highest_bus = + pci_config_get8(handle, PCI_BCNF_SECBUS); + } + + (void) pcicfg_config_teardown(&handle); + entry->error = PCICFG_FAILURE; + return (DDI_WALK_CONTINUE); + } else { + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "reg", (caddr_t)&pci_rp, + &length) != DDI_PROP_SUCCESS) { + /* + * If one node in (the subtree of nodes) + * does'nt have a "reg" property fail the + * allocation. + */ + entry->memory_len = 0; + entry->io_len = 0; + entry->error = PCICFG_FAILURE; + return (DDI_WALK_TERMINATE); + } + /* + * For each "reg" property with a length, add that to the + * total memory (or I/O) to allocate. + */ + rcount = length / sizeof (pci_regspec_t); + + for (i = 0; i < rcount; i++) { + + switch (PCI_REG_ADDR_G(pci_rp[i].pci_phys_hi)) { + + case PCI_REG_ADDR_G(PCI_ADDR_MEM32): + mem_request->ra_len = + pci_rp[i].pci_size_low + + PCICFG_ROUND_UP(mem_request->ra_len, + pci_rp[i].pci_size_low); + DEBUG1("ADDING 32 --->0x%x\n", + pci_rp[i].pci_size_low); + + break; + case PCI_REG_ADDR_G(PCI_ADDR_MEM64): + mem_request->ra_len = + pci_rp[i].pci_size_low + + PCICFG_ROUND_UP(mem_request->ra_len, + pci_rp[i].pci_size_low); + DEBUG1("ADDING 64 --->0x%x\n", + pci_rp[i].pci_size_low); + + break; + case PCI_REG_ADDR_G(PCI_ADDR_IO): + io_request->ra_len = + pci_rp[i].pci_size_low + + PCICFG_ROUND_UP(io_request->ra_len, + pci_rp[i].pci_size_low); + DEBUG1("ADDING I/O --->0x%x\n", + pci_rp[i].pci_size_low); + break; + default: + /* Config space register - not included */ + break; + } + } + + /* + * free the memory allocated by ddi_getlongprop + */ + kmem_free(pci_rp, length); + + /* + * continue the walk to the next sibling to sum memory + */ + + (void) pcicfg_config_teardown(&handle); + + return (DDI_WALK_CONTINUE); + } +} + +static int +pcicfg_find_resource_end(dev_info_t *dip, void *hdl) +{ + pcicfg_phdl_t *entry = (pcicfg_phdl_t *)hdl; + pci_regspec_t *pci_ap; + int length; + int rcount; + int i; + + entry->error = PCICFG_SUCCESS; + + if (dip == entry->dip) { + DEBUG0("Don't include parent bridge node\n"); + return (DDI_WALK_CONTINUE); + } else { + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "assigned-addresses", + (caddr_t)&pci_ap, &length) != DDI_PROP_SUCCESS) { + DEBUG0("Node doesn't have assigned-addresses\n"); + return (DDI_WALK_CONTINUE); + } + + rcount = length / sizeof (pci_regspec_t); + + for (i = 0; i < rcount; i++) { + + switch (PCI_REG_ADDR_G(pci_ap[i].pci_phys_hi)) { + + case PCI_REG_ADDR_G(PCI_ADDR_MEM32): + if ((pci_ap[i].pci_phys_low + + pci_ap[i].pci_size_low) > + entry->memory_base) { + entry->memory_base = + pci_ap[i].pci_phys_low + + pci_ap[i].pci_size_low; + } + break; + case PCI_REG_ADDR_G(PCI_ADDR_MEM64): + if ((PCICFG_LADDR(pci_ap[i].pci_phys_low, + pci_ap[i].pci_phys_mid) + + pci_ap[i].pci_size_low) > + entry->memory_base) { + entry->memory_base = PCICFG_LADDR( + pci_ap[i].pci_phys_low, + pci_ap[i].pci_phys_mid) + + pci_ap[i].pci_size_low; + } + break; + case PCI_REG_ADDR_G(PCI_ADDR_IO): + if ((pci_ap[i].pci_phys_low + + pci_ap[i].pci_size_low) > + entry->io_base) { + entry->io_base = + pci_ap[i].pci_phys_low + + pci_ap[i].pci_size_low; + } + break; + } + } + + /* + * free the memory allocated by ddi_getlongprop + */ + kmem_free(pci_ap, length); + + /* + * continue the walk to the next sibling to sum memory + */ + return (DDI_WALK_CONTINUE); + } +} + +static int +pcicfg_free_bridge_resources(dev_info_t *dip) +{ + pcicfg_range_t *ranges; + uint_t *bus; + int k; + int length; + int i; + + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "ranges", (caddr_t)&ranges, + &length) != DDI_PROP_SUCCESS) { + DEBUG0("Failed to read ranges property\n"); + if (ddi_get_child(dip)) { + cmn_err(CE_WARN, "No ranges property found for %s", + ddi_get_name(dip)); + /* + * strictly speaking, we can check for children with + * assigned-addresses but for now it is better to + * be conservative and assume that if there are child + * nodes, then they do consume PCI memory or IO + * resources, Hence return failure. + */ + return (PCICFG_FAILURE); + } + length = 0; + + } + + for (i = 0; i < length / sizeof (pcicfg_range_t); i++) { + if (ranges[i].size_lo != 0 || + ranges[i].size_hi != 0) { + switch (ranges[i].parent_hi & PCI_REG_ADDR_M) { + case PCI_ADDR_IO: + DEBUG2("Free I/O " + "base/length = [0x%x]/[0x%x]\n", + ranges[i].child_lo, + ranges[i].size_lo); + if (ndi_ra_free(ddi_get_parent(dip), + (uint64_t)ranges[i].child_lo, + (uint64_t)ranges[i].size_lo, + NDI_RA_TYPE_IO, NDI_RA_PASS) + != NDI_SUCCESS) { + DEBUG0("Trouble freeing " + "PCI i/o space\n"); + kmem_free(ranges, length); + return (PCICFG_FAILURE); + } + break; + case PCI_ADDR_MEM32: + case PCI_ADDR_MEM64: + DEBUG3("Free Memory base/length = " + "[0x%x.%x]/[0x%x]\n", + ranges[i].child_mid, + ranges[i].child_lo, + ranges[i].size_lo) + if (ndi_ra_free(ddi_get_parent(dip), + PCICFG_LADDR(ranges[i].child_lo, + ranges[i].child_mid), + (uint64_t)ranges[i].size_lo, + NDI_RA_TYPE_MEM, NDI_RA_PASS) + != NDI_SUCCESS) { + DEBUG0("Trouble freeing " + "PCI memory space\n"); + kmem_free(ranges, length); + return (PCICFG_FAILURE); + } + break; + default: + DEBUG0("Unknown memory space\n"); + break; + } + } + } + + if (length) + kmem_free(ranges, length); + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "bus-range", (caddr_t)&bus, + &k) != DDI_PROP_SUCCESS) { + DEBUG0("Failed to read bus-range property\n"); + return (PCICFG_FAILURE); + } + + DEBUG2("Need to free bus [%d] range [%d]\n", + bus[0], bus[1] - bus[0] + 1); + + if (ndi_ra_free(ddi_get_parent(dip), + (uint64_t)bus[0], (uint64_t)(bus[1] - bus[0] + 1), + NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS) != NDI_SUCCESS) { + /*EMPTY*/ + DEBUG0("Failed to free a bus number\n"); + } + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)bus, k); + + return (PCICFG_SUCCESS); +} + +static int +pcicfg_free_device_resources(dev_info_t *dip) +{ + pci_regspec_t *assigned; + + int length; + int acount; + int i; + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned, + &length) != DDI_PROP_SUCCESS) { + DEBUG0("Failed to read assigned-addresses property\n"); + return (PCICFG_FAILURE); + } + + /* + * For each "assigned-addresses" property entry with a length, + * call the memory allocation routines to return the + * resource. + */ + acount = length / sizeof (pci_regspec_t); + for (i = 0; i < acount; i++) { + /* + * Workaround for Devconf (x86) bug to skip extra entries + * beyond the PCI_CONF_BASE5 offset. But we want to free up + * any memory for expansion roms if allocated. + */ + if ((PCI_REG_REG_G(assigned[i].pci_phys_hi) > PCI_CONF_BASE5) && + (PCI_REG_REG_G(assigned[i].pci_phys_hi) != PCI_CONF_ROM)) + break; + + if (pcicfg_free_resource(dip, assigned[i])) { + DEBUG1("pcicfg_free_device_resources - Trouble freeing " + "%x\n", assigned[i].pci_phys_hi); + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)assigned, length); + + return (PCICFG_FAILURE); + } + } + kmem_free(assigned, length); + return (PCICFG_SUCCESS); +} + +static int +pcicfg_free_resources(dev_info_t *dip) +{ + ddi_acc_handle_t handle; + uint8_t header_type; + + if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) { + DEBUG0("Failed to map config space!\n"); + return (PCICFG_FAILURE); + } + + header_type = pci_config_get8(handle, PCI_CONF_HEADER); + + (void) pci_config_teardown(&handle); + + /* + * A different algorithm is used for bridges and leaf devices. + */ + if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { + if (pcicfg_free_bridge_resources(dip) != PCICFG_SUCCESS) { + DEBUG0("Failed freeing up bridge resources\n"); + return (PCICFG_FAILURE); + } + } else { + if (pcicfg_free_device_resources(dip) != PCICFG_SUCCESS) { + DEBUG0("Failed freeing up device resources\n"); + return (PCICFG_FAILURE); + } + } + return (PCICFG_SUCCESS); +} + +#ifndef _DONT_USE_1275_GENERIC_NAMES +static char * +pcicfg_get_class_name(uint32_t classcode) +{ + struct pcicfg_name_entry *ptr; + + for (ptr = &pcicfg_class_lookup[0]; ptr->name != NULL; ptr++) { + if (ptr->class_code == classcode) { + return (ptr->name); + } + } + return (NULL); +} +#endif /* _DONT_USE_1275_GENERIC_NAMES */ + +static dev_info_t * +pcicfg_devi_find(dev_info_t *dip, uint_t device, uint_t function) +{ + struct pcicfg_find_ctrl ctrl; + int count; + + ctrl.device = device; + ctrl.function = function; + ctrl.dip = NULL; + + ndi_devi_enter(dip, &count); + ddi_walk_devs(ddi_get_child(dip), pcicfg_match_dev, (void *)&ctrl); + ndi_devi_exit(dip, count); + + return (ctrl.dip); +} + +static int +pcicfg_match_dev(dev_info_t *dip, void *hdl) +{ + struct pcicfg_find_ctrl *ctrl = (struct pcicfg_find_ctrl *)hdl; + pci_regspec_t *pci_rp; + int length; + int pci_dev; + int pci_func; + + if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, + (uint_t *)&length) != DDI_PROP_SUCCESS) { + ctrl->dip = NULL; + return (DDI_WALK_TERMINATE); + } + + /* get the PCI device address info */ + pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); + pci_func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); + + /* + * free the memory allocated by ddi_prop_lookup_int_array + */ + ddi_prop_free(pci_rp); + + + if ((pci_dev == ctrl->device) && (pci_func == ctrl->function)) { + /* found the match for the specified device address */ + ctrl->dip = dip; + return (DDI_WALK_TERMINATE); + } + + /* + * continue the walk to the next sibling to look for a match. + */ + return (DDI_WALK_PRUNECHILD); +} + +static int +pcicfg_update_assigned_prop(dev_info_t *dip, pci_regspec_t *newone) +{ + int alen; + pci_regspec_t *assigned; + caddr_t newreg; + uint_t status; + + DEBUG0("pcicfg_update_assigned_prop()\n"); + status = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "assigned-addresses", (caddr_t)&assigned, &alen); + switch (status) { + case DDI_PROP_SUCCESS: + break; + case DDI_PROP_NO_MEMORY: + DEBUG0("no memory for assigned-addresses property\n"); + return (PCICFG_FAILURE); + default: + (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, + "assigned-addresses", (int *)newone, + sizeof (*newone)/sizeof (int)); + + (void) pcicfg_dump_assigned(dip); + + return (PCICFG_SUCCESS); + } + + /* + * Allocate memory for the existing + * assigned-addresses(s) plus one and then + * build it. + */ + + newreg = kmem_zalloc(alen+sizeof (*newone), KM_SLEEP); + + bcopy(assigned, newreg, alen); + bcopy(newone, newreg + alen, sizeof (*newone)); + + /* + * Write out the new "assigned-addresses" spec + */ + (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, + "assigned-addresses", (int *)newreg, + (alen + sizeof (*newone))/sizeof (int)); + + kmem_free((caddr_t)newreg, alen+sizeof (*newone)); + + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)assigned, alen); + + (void) pcicfg_dump_assigned(dip); + + return (PCICFG_SUCCESS); +} +static int +pcicfg_update_ranges_prop(dev_info_t *dip, pcicfg_range_t *addition) +{ + int rlen; + pcicfg_range_t *ranges; + caddr_t newreg; + uint_t status; + + status = ddi_getlongprop(DDI_DEV_T_ANY, + dip, DDI_PROP_DONTPASS, "ranges", (caddr_t)&ranges, &rlen); + + + switch (status) { + case DDI_PROP_SUCCESS: + break; + case DDI_PROP_NO_MEMORY: + DEBUG0("ranges present, but unable to get memory\n"); + return (PCICFG_FAILURE); + default: + DEBUG0("no ranges property - creating one\n"); + if (ndi_prop_update_int_array(DDI_DEV_T_NONE, + dip, "ranges", (int *)addition, + sizeof (pcicfg_range_t)/sizeof (int)) + != DDI_SUCCESS) { + DEBUG0("Did'nt create ranges property\n"); + return (PCICFG_FAILURE); + } + return (PCICFG_SUCCESS); + } + + /* + * Allocate memory for the existing reg(s) plus one and then + * build it. + */ + newreg = kmem_zalloc(rlen+sizeof (pcicfg_range_t), KM_SLEEP); + + bcopy(ranges, newreg, rlen); + bcopy(addition, newreg + rlen, sizeof (pcicfg_range_t)); + + /* + * Write out the new "ranges" property + */ + (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, + dip, "ranges", (int *)newreg, + (rlen + sizeof (pcicfg_range_t))/sizeof (int)); + + kmem_free((caddr_t)newreg, rlen+sizeof (pcicfg_range_t)); + + kmem_free((caddr_t)ranges, rlen); + + return (PCICFG_SUCCESS); +} + +static int +pcicfg_update_reg_prop(dev_info_t *dip, uint32_t regvalue, uint_t reg_offset) +{ + int rlen; + pci_regspec_t *reg; + caddr_t newreg; + uint32_t hiword; + pci_regspec_t addition; + uint32_t size; + uint_t status; + + status = ddi_getlongprop(DDI_DEV_T_ANY, + dip, DDI_PROP_DONTPASS, "reg", (caddr_t)®, &rlen); + + switch (status) { + case DDI_PROP_SUCCESS: + break; + case DDI_PROP_NO_MEMORY: + DEBUG0("reg present, but unable to get memory\n"); + return (PCICFG_FAILURE); + default: + DEBUG0("no reg property\n"); + return (PCICFG_FAILURE); + } + + /* + * Allocate memory for the existing reg(s) plus one and then + * build it. + */ + newreg = kmem_zalloc(rlen+sizeof (pci_regspec_t), KM_SLEEP); + + /* + * Build the regspec, then add it to the existing one(s) + */ + + hiword = PCICFG_MAKE_REG_HIGH(PCI_REG_BUS_G(reg->pci_phys_hi), + PCI_REG_DEV_G(reg->pci_phys_hi), + PCI_REG_FUNC_G(reg->pci_phys_hi), reg_offset); + + if (reg_offset == PCI_CONF_ROM) { + size = (~(PCI_BASE_ROM_ADDR_M & regvalue))+1; + hiword |= PCI_ADDR_MEM32; + } else { + size = (~(PCI_BASE_M_ADDR_M & regvalue))+1; + + if ((PCI_BASE_SPACE_M & regvalue) == PCI_BASE_SPACE_MEM) { + if ((PCI_BASE_TYPE_M & regvalue) == PCI_BASE_TYPE_MEM) { + hiword |= PCI_ADDR_MEM32; + } else if ((PCI_BASE_TYPE_M & regvalue) + == PCI_BASE_TYPE_ALL) { + hiword |= PCI_ADDR_MEM64; + } + } else { + hiword |= PCI_ADDR_IO; + } + } + + addition.pci_phys_hi = hiword; + addition.pci_phys_mid = 0; + addition.pci_phys_low = 0; + addition.pci_size_hi = 0; + addition.pci_size_low = size; + + bcopy(reg, newreg, rlen); + bcopy(&addition, newreg + rlen, sizeof (pci_regspec_t)); + + /* + * Write out the new "reg" property + */ + (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, + dip, "reg", (int *)newreg, + (rlen + sizeof (pci_regspec_t))/sizeof (int)); + + kmem_free((caddr_t)newreg, rlen+sizeof (pci_regspec_t)); + kmem_free((caddr_t)reg, rlen); + + return (PCICFG_SUCCESS); +} + +static void +pcicfg_device_on(ddi_acc_handle_t config_handle) +{ + /* + * Enable memory, IO, and bus mastership + * XXX should we enable parity, SERR#, + * fast back-to-back, and addr. stepping? + */ + pci_config_put16(config_handle, PCI_CONF_COMM, + pci_config_get16(config_handle, PCI_CONF_COMM) | 0x7); +} + +static void +pcicfg_device_off(ddi_acc_handle_t config_handle) +{ + /* + * Disable I/O and memory traffic through the bridge + */ + pci_config_put16(config_handle, PCI_CONF_COMM, 0x0); +} + +/* + * Setup the basic 1275 properties based on information found in the config + * header of the PCI device + */ +static int +pcicfg_set_standard_props(dev_info_t *dip, ddi_acc_handle_t config_handle, + uint8_t pcie_dev) +{ + int ret, cap_id_loc; + uint16_t val; + uint32_t wordval; + uint8_t byteval; + + /* These two exists only for non-bridges */ + if (((pci_config_get8(config_handle, PCI_CONF_HEADER) + & PCI_HEADER_TYPE_M) == PCI_HEADER_ZERO) && !pcie_dev) { + byteval = pci_config_get8(config_handle, PCI_CONF_MIN_G); + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "min-grant", byteval)) != DDI_SUCCESS) { + return (ret); + } + + byteval = pci_config_get8(config_handle, PCI_CONF_MAX_L); + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "max-latency", byteval)) != DDI_SUCCESS) { + return (ret); + } + } + + /* + * These should always exist and have the value of the + * corresponding register value + */ + val = pci_config_get16(config_handle, PCI_CONF_VENID); + + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "vendor-id", val)) != DDI_SUCCESS) { + return (ret); + } + val = pci_config_get16(config_handle, PCI_CONF_DEVID); + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "device-id", val)) != DDI_SUCCESS) { + return (ret); + } + byteval = pci_config_get8(config_handle, PCI_CONF_REVID); + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "revision-id", byteval)) != DDI_SUCCESS) { + return (ret); + } + + wordval = (pci_config_get16(config_handle, PCI_CONF_SUBCLASS)<< 8) | + (pci_config_get8(config_handle, PCI_CONF_PROGCLASS)); + + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "class-code", wordval)) != DDI_SUCCESS) { + return (ret); + } + val = (pci_config_get16(config_handle, + PCI_CONF_STAT) & PCI_STAT_DEVSELT); + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "devsel-speed", val)) != DDI_SUCCESS) { + return (ret); + } + + /* + * The next three are bits set in the status register. The property is + * present (but with no value other than its own existence) if the bit + * is set, non-existent otherwise + */ + if ((!pcie_dev) && + (pci_config_get16(config_handle, PCI_CONF_STAT) & + PCI_STAT_FBBC)) { + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "fast-back-to-back", 0)) != DDI_SUCCESS) { + return (ret); + } + } + if ((!pcie_dev) && + (pci_config_get16(config_handle, PCI_CONF_STAT) & + PCI_STAT_66MHZ)) { + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "66mhz-capable", 0)) != DDI_SUCCESS) { + return (ret); + } + } + if (pci_config_get16(config_handle, PCI_CONF_STAT) & PCI_STAT_UDF) { + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "udf-supported", 0)) != DDI_SUCCESS) { + return (ret); + } + } + + /* + * These next three are optional and are not present + * if the corresponding register is zero. If the value + * is non-zero then the property exists with the value + * of the register. + */ + if ((val = pci_config_get16(config_handle, + PCI_CONF_SUBVENID)) != 0) { + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "subsystem-vendor-id", val)) != DDI_SUCCESS) { + return (ret); + } + } + if ((val = pci_config_get16(config_handle, + PCI_CONF_SUBSYSID)) != 0) { + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "subsystem-id", val)) != DDI_SUCCESS) { + return (ret); + } + } + if ((val = pci_config_get16(config_handle, + PCI_CONF_CACHE_LINESZ)) != 0) { + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "cache-line-size", val)) != DDI_SUCCESS) { + return (ret); + } + } + + /* + * If the Interrupt Pin register is non-zero then the + * interrupts property exists + */ + if ((byteval = pci_config_get8(config_handle, PCI_CONF_IPIN)) != 0) { + /* + * If interrupt pin is non-zero, + * record the interrupt line used + */ + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "interrupts", byteval)) != DDI_SUCCESS) { + return (ret); + } + } + if (pcie_dev && (cap_id_loc = pcicfg_get_cap(config_handle, + PCI_CAP_ID_PCI_E)) > 0) { + val = pci_config_get16(config_handle, cap_id_loc + + PCIE_PCIECAP) & PCIE_PCIECAP_SLOT_IMPL; + /* if slot implemented, get physical slot number */ + if (val) { + wordval = (pci_config_get32(config_handle, cap_id_loc + + PCIE_SLOTCAP) >> + PCIE_SLOTCAP_PHY_SLOT_NUM_SHIFT) & + PCIE_SLOTCAP_PHY_SLOT_NUM_MASK; + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, + dip, "physical-slot#", wordval)) + != DDI_SUCCESS) { + return (ret); + } + } + } + return (PCICFG_SUCCESS); +} +static int +pcicfg_set_busnode_props(dev_info_t *dip, uint8_t pcie_device_type) +{ + int ret; + char device_type[8]; + + if (pcie_device_type) + (void) strcpy(device_type, "pciex"); + else + (void) strcpy(device_type, "pci"); + + if ((ret = ndi_prop_update_string(DDI_DEV_T_NONE, dip, + "device_type", device_type)) != DDI_SUCCESS) { + return (ret); + } + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "#address-cells", 3)) != DDI_SUCCESS) { + return (ret); + } + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "#size-cells", 2)) != DDI_SUCCESS) { + return (ret); + } + return (PCICFG_SUCCESS); +} + +static int +pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle, + uint8_t pcie_dev) +{ + + int ret; + char *name; + char buffer[64], pprefix[8]; + uint16_t classcode; + uint8_t revid, pif, pclass, psubclass; + char *compat[24]; + int i; + int n; + uint16_t sub_vid, sub_sid, vid, did; + + /* set the property prefix based on the device type */ + if (pcie_dev) + (void) sprintf(pprefix, "pciex"); + else + (void) sprintf(pprefix, "pci"); + sub_vid = pci_config_get16(config_handle, PCI_CONF_SUBVENID), + sub_sid = pci_config_get16(config_handle, PCI_CONF_SUBSYSID); + vid = pci_config_get16(config_handle, PCI_CONF_VENID), + did = pci_config_get16(config_handle, PCI_CONF_DEVID); + revid = pci_config_get8(config_handle, PCI_CONF_REVID); + pif = pci_config_get8(config_handle, PCI_CONF_PROGCLASS); + classcode = pci_config_get16(config_handle, PCI_CONF_SUBCLASS); + pclass = pci_config_get8(config_handle, PCI_CONF_BASCLASS); + psubclass = pci_config_get8(config_handle, PCI_CONF_SUBCLASS); + + /* + * NOTE: These are for both a child and PCI-PCI bridge node + */ + + /* + * "name" property rule + * -------------------- + * + * + * | \svid | + * | \ | + * | \ | + * | ssid \ | =0 | != 0 | + * |------------|-----------------------|-----------------------| + * | | | | + * | =0 | vid,did | svid,ssid | + * | | | | + * |------------|-----------------------|-----------------------| + * | | | | + * | !=0 | svid,ssid | svid,ssid | + * | | | | + * |------------|-----------------------|-----------------------| + * + * where: + * vid = vendor id + * did = device id + * svid = subsystem vendor id + * ssid = subsystem id + */ + + if ((sub_sid != 0) || (sub_vid != 0)) { + (void) sprintf(buffer, "%s%x,%x", pprefix, sub_vid, sub_sid); + } else { + (void) sprintf(buffer, "%s%x,%x", pprefix, vid, did); + } + + /* + * In some environments, trying to use "generic" 1275 names is + * not the convention. In those cases use the name as created + * above. In all the rest of the cases, check to see if there + * is a generic name first. + */ +#ifdef _DONT_USE_1275_GENERIC_NAMES + name = buffer; +#else + if ((name = pcicfg_get_class_name(classcode)) == NULL) { + /* + * Set name to the above fabricated name + */ + name = buffer; + } +#endif + + /* + * The node name field needs to be filled in with the name + */ + if (ndi_devi_set_nodename(dip, name, 0) != NDI_SUCCESS) { + DEBUG0("Failed to set nodename for node\n"); + return (PCICFG_FAILURE); + } + + /* + * Create the compatible property as an array of pointers + * to strings. Start with the buffer created above. + */ + n = 0; + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + + /* + * Setup 'compatible' as per the PCI2.1 bindings document. + * pci[ex]VVVV,DDDD.SSSS.ssss.RR + * pci[ex]VVVV,DDDD.SSSS.ssss + * pciSSSS.ssss -> not created for PCIe as per PCIe bindings + * pci[ex]VVVV,DDDD.RR + * pci[ex]VVVV,DDDD + * pci[ex]class,CCSSPP + * pci[ex]class,CCSS + */ + + /* pci[ex]VVVV,DDDD.SSSS.ssss.RR */ + (void) sprintf(buffer, "%s%x,%x.%x.%x.%x", pprefix, vid, did, + sub_vid, sub_sid, revid); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + + /* pci[ex]VVVV,DDDD.SSSS.ssss */ + (void) sprintf(buffer, "%s%x,%x.%x.%x", pprefix, vid, did, + sub_vid, sub_sid); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + + /* pciSSSS.ssss -> not created for PCIe as per PCIe bindings */ + if (!pcie_dev) { + (void) sprintf(buffer, "pci%x,%x", sub_vid, sub_sid); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + } + + /* pci[ex]VVVV,DDDD.RR */ + (void) sprintf(buffer, "%s%x,%x.%x", pprefix, vid, did, revid); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + + /* pci[ex]VVVV,DDDD */ + (void) sprintf(buffer, "%s%x,%x", pprefix, vid, did); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + + /* pci[ex]class,CCSSPP */ + (void) sprintf(buffer, "%sclass,%02x%02x%02x", pprefix, + pclass, psubclass, pif); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + + /* pci[ex]class,CCSS */ + (void) sprintf(buffer, "%sclass,%04x", pprefix, classcode); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + + if ((ret = ndi_prop_update_string_array(DDI_DEV_T_NONE, dip, + "compatible", (char **)compat, n)) != DDI_SUCCESS) { + return (ret); + } + + for (i = 0; i < n; i++) { + kmem_free(compat[i], strlen(compat[i]) + 1); + } + + DEBUG1("pcicfg_set_childnode_props - creating name=%s\n", name); + if ((ret = ndi_prop_update_string(DDI_DEV_T_NONE, dip, + "name", name)) != DDI_SUCCESS) { + + DEBUG0("pcicfg_set_childnode_props - Unable to create name " + "property\n"); + + return (ret); + } + + return (PCICFG_SUCCESS); +} + +/* + * Program the bus numbers into the bridge + */ + +static void +pcicfg_set_bus_numbers(ddi_acc_handle_t config_handle, +uint_t primary, uint_t secondary, uint_t subordinate) +{ + DEBUG3("Setting bridge bus-range %d,%d,%d\n", primary, secondary, + subordinate); + /* + * Primary bus# + */ + pci_config_put8(config_handle, PCI_BCNF_PRIBUS, primary); + + /* + * Secondary bus# + */ + pci_config_put8(config_handle, PCI_BCNF_SECBUS, secondary); + + /* + * Subordinate bus# + */ + pci_config_put8(config_handle, PCI_BCNF_SUBBUS, subordinate); +} + +/* + * Put bridge registers into initial state + */ +static void +pcicfg_setup_bridge(pcicfg_phdl_t *entry, + ddi_acc_handle_t handle) +{ + /* + * The highest bus seen during probing is the max-subordinate bus + */ + pci_config_put8(handle, PCI_BCNF_SUBBUS, entry->highest_bus); + + /* + * Reset the secondary bus + */ + pci_config_put16(handle, PCI_BCNF_BCNTRL, + pci_config_get16(handle, PCI_BCNF_BCNTRL) | 0x40); + + drv_usecwait(100); + + pci_config_put16(handle, PCI_BCNF_BCNTRL, + pci_config_get16(handle, PCI_BCNF_BCNTRL) & ~0x40); + + /* + * Program the memory base register with the + * start of the memory range + */ + pci_config_put16(handle, PCI_BCNF_MEM_BASE, + PCICFG_HIWORD(PCICFG_LOADDR(entry->memory_last))); + + /* + * Program the I/O base register with the start of the I/O range + */ + pci_config_put8(handle, PCI_BCNF_IO_BASE_LOW, + PCICFG_HIBYTE(PCICFG_LOWORD(PCICFG_LOADDR(entry->io_last)))); + pci_config_put16(handle, PCI_BCNF_IO_BASE_HI, + PCICFG_HIWORD(PCICFG_LOADDR(entry->io_last))); + + /* + * Clear status bits + */ + pci_config_put16(handle, PCI_BCNF_SEC_STATUS, 0xffff); + + /* + * Turn off prefetchable range + */ + pci_config_put32(handle, PCI_BCNF_PF_BASE_LOW, 0x0000ffff); + pci_config_put32(handle, PCI_BCNF_PF_BASE_HIGH, 0xffffffff); + pci_config_put32(handle, PCI_BCNF_PF_LIMIT_HIGH, 0x0); + + /* + * Needs to be set to this value + */ + pci_config_put8(handle, PCI_CONF_ILINE, 0xf); + + /* + * After a Reset, we need to wait 2^25 clock cycles before the + * first Configuration access. The worst case is 33MHz, which + * is a 1 second wait. + */ + drv_usecwait(pcicfg_sec_reset_delay); + +} + +static void +pcicfg_update_bridge(pcicfg_phdl_t *entry, + ddi_acc_handle_t handle) +{ + uint_t length; + + /* + * Program the memory limit register with the end of the memory range + */ + + DEBUG1("DOWN ROUNDED ===>[0x%x]\n", + PCICFG_ROUND_DOWN(entry->memory_last, + PCICFG_MEMGRAN)); + + pci_config_put16(handle, PCI_BCNF_MEM_LIMIT, + PCICFG_HIWORD(PCICFG_LOADDR( + PCICFG_ROUND_DOWN(entry->memory_last, + PCICFG_MEMGRAN)))); + /* + * Since this is a bridge, the rest of this range will + * be responded to by the bridge. We have to round up + * so no other device claims it. + */ + if ((length = (PCICFG_ROUND_UP(entry->memory_last, + PCICFG_MEMGRAN) - entry->memory_last)) > 0) { + (void) pcicfg_get_mem(entry, length, NULL); + DEBUG1("Added [0x%x]at the top of " + "the bridge (mem)\n", length); + } + + /* + * Program the I/O limit register with the end of the I/O range + */ + pci_config_put8(handle, PCI_BCNF_IO_LIMIT_LOW, + PCICFG_HIBYTE(PCICFG_LOWORD( + PCICFG_LOADDR(PCICFG_ROUND_DOWN(entry->io_last, + PCICFG_IOGRAN))))); + + pci_config_put16(handle, PCI_BCNF_IO_LIMIT_HI, + PCICFG_HIWORD(PCICFG_LOADDR(PCICFG_ROUND_DOWN(entry->io_last, + PCICFG_IOGRAN)))); + + /* + * Same as above for I/O space. Since this is a + * bridge, the rest of this range will be responded + * to by the bridge. We have to round up so no + * other device claims it. + */ + if ((length = (PCICFG_ROUND_UP(entry->io_last, + PCICFG_IOGRAN) - entry->io_last)) > 0) { + (void) pcicfg_get_io(entry, length, NULL); + DEBUG1("Added [0x%x]at the top of " + "the bridge (I/O)\n", length); + } +} + +/*ARGSUSED*/ +static void +pcicfg_disable_bridge_probe_err(dev_info_t *dip, ddi_acc_handle_t h, + pcicfg_err_regs_t *regs) +{ + uint16_t val; + + /* disable SERR generated in the context of Master Aborts. */ + regs->cmd = val = pci_config_get16(h, PCI_CONF_COMM); + val &= ~PCI_COMM_SERR_ENABLE; + pci_config_put16(h, PCI_CONF_COMM, val); + regs->bcntl = val = pci_config_get16(h, PCI_BCNF_BCNTRL); + val &= ~PCI_BCNF_BCNTRL_SERR_ENABLE; + pci_config_put16(h, PCI_BCNF_BCNTRL, val); + /* clear any current pending errors */ + pci_config_put16(h, PCI_CONF_STAT, PCI_STAT_S_TARG_AB| + PCI_STAT_R_TARG_AB | PCI_STAT_R_MAST_AB | PCI_STAT_S_SYSERR); + pci_config_put16(h, PCI_BCNF_SEC_STATUS, PCI_STAT_S_TARG_AB| + PCI_STAT_R_TARG_AB | PCI_STAT_R_MAST_AB | PCI_STAT_S_SYSERR); + /* if we are a PCIe device, disable the generation of UR, CE and NFE */ + if (regs->pcie_dev) { + uint16_t devctl; + int off = pcicfg_get_cap(h, PCI_CAP_ID_PCI_E); + + regs->pcie_cap_off = off; + regs->devctl = devctl = pci_config_get16(h, off + PCIE_DEVCTL); + devctl &= ~(PCIE_DEVCTL_UR_REPORTING_EN | + PCIE_DEVCTL_CE_REPORTING_EN | + PCIE_DEVCTL_NFE_REPORTING_EN | + PCIE_DEVCTL_FE_REPORTING_EN); + pci_config_put16(h, off + PCIE_DEVCTL, devctl); + } +} + +/*ARGSUSED*/ +static void +pcicfg_enable_bridge_probe_err(dev_info_t *dip, ddi_acc_handle_t h, + pcicfg_err_regs_t *regs) +{ + /* clear any pending errors */ + pci_config_put16(h, PCI_CONF_STAT, PCI_STAT_S_TARG_AB| + PCI_STAT_R_TARG_AB | PCI_STAT_R_MAST_AB | PCI_STAT_S_SYSERR); + pci_config_put16(h, PCI_BCNF_SEC_STATUS, PCI_STAT_S_TARG_AB| + PCI_STAT_R_TARG_AB | PCI_STAT_R_MAST_AB | PCI_STAT_S_SYSERR); + + /* restore original settings */ + if (regs->pcie_dev) { + pcie_clear_errors(dip, h); + pci_config_put16(h, regs->pcie_cap_off + PCIE_DEVCTL, + regs->devctl); + } + + pci_config_put16(h, PCI_BCNF_BCNTRL, regs->bcntl); + pci_config_put16(h, PCI_CONF_COMM, regs->cmd); + +} + +static int +pcicfg_probe_children(dev_info_t *parent, uint_t bus, + uint_t device, uint_t func, uint_t *highest_bus) +{ + dev_info_t *new_child; + ddi_acc_handle_t config_handle; + uint8_t header_type, pcie_dev = 0; + + int i; + uint32_t request; + int ret; + pcicfg_err_regs_t regs; + /* + * This node will be put immediately below + * "parent". Allocate a blank device node. It will either + * be filled in or freed up based on further probing. + */ + /* + * Note: in usr/src/uts/common/io/hotplug/pcicfg/pcicfg.c + * ndi_devi_alloc() is called as ndi_devi_alloc_sleep() + */ + if (ndi_devi_alloc(parent, DEVI_PSEUDO_NEXNAME, + (pnode_t)DEVI_SID_NODEID, &new_child) + != NDI_SUCCESS) { + DEBUG0("pcicfg_probe_children(): Failed to alloc child node\n"); + return (PCICFG_FAILURE); + } + + if (pcicfg_add_config_reg(new_child, bus, + device, func) != DDI_SUCCESS) { + DEBUG0("pcicfg_probe_children():" + "Failed to add candidate REG\n"); + goto failedconfig; + } + + if ((ret = pcicfg_config_setup(new_child, &config_handle)) + != PCICFG_SUCCESS) { + if (ret == PCICFG_NODEVICE) { + (void) ndi_devi_free(new_child); + return (ret); + } + DEBUG0("pcicfg_probe_children():" + "Failed to setup config space\n"); + goto failedconfig; + } + + /* + * As soon as we have access to config space, + * turn off device. It will get turned on + * later (after memory is assigned). + */ + (void) pcicfg_device_off(config_handle); + + /* check if we are PCIe device */ + if (pcicfg_pcie_dev(new_child, PCICFG_DEVICE_TYPE_PCIE, ®s) + == DDI_SUCCESS) { + DEBUG0("PCIe device detected\n"); + pcie_dev = 1; + } + + /* + * Set 1275 properties common to all devices + */ + if (pcicfg_set_standard_props(new_child, config_handle, + pcie_dev) != PCICFG_SUCCESS) { + DEBUG0("Failed to set standard properties\n"); + goto failedchild; + } + + /* + * Child node properties NOTE: Both for PCI-PCI bridge and child node + */ + if (pcicfg_set_childnode_props(new_child, config_handle, + pcie_dev) != PCICFG_SUCCESS) { + goto failedchild; + } + + header_type = pci_config_get8(config_handle, PCI_CONF_HEADER); + + /* + * If this is not a multi-function card only probe function zero. + */ + if ((!(header_type & PCI_HEADER_MULTI)) && (func != 0)) { + + (void) pcicfg_config_teardown(&config_handle); + (void) ndi_devi_free(new_child); + return (PCICFG_NODEVICE); + } + + DEBUG1("---Vendor ID = [0x%x]\n", + pci_config_get16(config_handle, PCI_CONF_VENID)); + DEBUG1("---Device ID = [0x%x]\n", + pci_config_get16(config_handle, PCI_CONF_DEVID)); + + /* + * Attach the child to its parent + */ + (void) i_ndi_config_node(new_child, DS_LINKED, 0); + + if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { + + DEBUG3("--Bridge found bus [0x%x] device" + "[0x%x] func [0x%x]\n", bus, device, func); + + if (pcicfg_probe_bridge(new_child, config_handle, + bus, highest_bus) != PCICFG_SUCCESS) { + (void) pcicfg_free_bridge_resources(new_child); + goto failedchild; + } + + } else { + + DEBUG3("--Leaf device found bus [0x%x] device" + "[0x%x] func [0x%x]\n", + bus, device, func); + + i = PCI_CONF_BASE0; + + while (i <= PCI_CONF_BASE5) { + + pci_config_put32(config_handle, i, 0xffffffff); + + request = pci_config_get32(config_handle, i); + /* + * If its a zero length, don't do + * any programming. + */ + if (request != 0) { + /* + * Add to the "reg" property + */ + if (pcicfg_update_reg_prop(new_child, + request, i) != PCICFG_SUCCESS) { + goto failedchild; + } + } else { + DEBUG1("BASE register [0x%x] asks for " + "[0x0]=[0x0](32)\n", i); + i += 4; + continue; + } + + /* + * Increment by eight if it is 64 bit address space + */ + if ((PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_ALL) { + DEBUG3("BASE register [0x%x] asks for " + "[0x%x]=[0x%x] (64)\n", + i, request, + (~(PCI_BASE_M_ADDR_M & request))+1) + i += 8; + } else { + DEBUG3("BASE register [0x%x] asks for " + "[0x%x]=[0x%x](32)\n", + i, request, + (~(PCI_BASE_M_ADDR_M & request))+1) + i += 4; + } + } + + /* + * Get the ROM size and create register for it + */ + pci_config_put32(config_handle, PCI_CONF_ROM, 0xfffffffe); + + request = pci_config_get32(config_handle, PCI_CONF_ROM); + /* + * If its a zero length, don't do + * any programming. + */ + + if (request != 0) { + DEBUG3("BASE register [0x%x] asks for [0x%x]=[0x%x]\n", + PCI_CONF_ROM, request, + (~(PCI_BASE_ROM_ADDR_M & request))+1); + /* + * Add to the "reg" property + */ + if (pcicfg_update_reg_prop(new_child, + request, PCI_CONF_ROM) != PCICFG_SUCCESS) { + goto failedchild; + } + } + + /* now allocate & program the resources */ + if (pcicfg_device_assign(new_child) != PCICFG_SUCCESS) { + (void) pcicfg_free_device_resources(new_child); + goto failedchild; + } + (void) ndi_devi_bind_driver(new_child, 0); + } + + (void) pcicfg_config_teardown(&config_handle); + return (PCICFG_SUCCESS); + +failedchild: + + (void) pcicfg_config_teardown(&config_handle); + +failedconfig: + + (void) ndi_devi_free(new_child); + return (PCICFG_FAILURE); +} + +static int +pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, + uint_t device, uint_t func, uint_t *highest_bus) +{ + dev_info_t *new_child; + int8_t header_type; + int ret; + ddi_acc_handle_t h, ph; + int error = 0; + extern int pcicfg_dont_interpret; + pcicfg_err_regs_t parent_regs, regs; +#ifdef PCICFG_INTERPRET_FCODE + struct pci_ops_bus_args po; + fco_handle_t c; + char unit_address[64]; + int fcode_size = 0; + uchar_t *fcode_addr; + uint64_t mem_answer, mem_alen; + pci_regspec_t p; + int32_t request; + ndi_ra_request_t req; + int16_t vendor_id, device_id; +#endif + + /* + * check if our parent is of type pciex. + * if so, program config space to disable error msgs during probe. + */ + if (pcicfg_pcie_dev(parent, PCICFG_DEVICE_TYPE_PCIE, &parent_regs) + == DDI_SUCCESS) { + DEBUG0("PCI/PCIe parent detected. Disable errors.\n"); + /* + * disable parent generating URs or SERR#s during probing + * alone. + */ + if (pci_config_setup(parent, &ph) != DDI_SUCCESS) + return (DDI_FAILURE); + pcicfg_disable_bridge_probe_err(parent, ph, &parent_regs); + } + + /* + * This node will be put immediately below + * "parent". Allocate a blank device node. It will either + * be filled in or freed up based on further probing. + */ + + if (ndi_devi_alloc(parent, DEVI_PSEUDO_NEXNAME, + (pnode_t)DEVI_SID_NODEID, &new_child) + != NDI_SUCCESS) { + DEBUG0("pcicfg_fcode_probe(): Failed to alloc child node\n"); + /* return (PCICFG_FAILURE); */ + ret = PCICFG_FAILURE; + goto failed2; + } + + /* + * Create a dummy reg property. This will be replaced with + * a real reg property when fcode completes or if we need to + * produce one by hand. + */ + if (pcicfg_add_config_reg(new_child, bus, + device, func) != DDI_SUCCESS) { + ret = PCICFG_FAILURE; + goto failed3; + } +#ifdef EFCODE21554 + if ((ret = pcicfg_config_setup(new_child, &h)) + != PCICFG_SUCCESS) { + DEBUG0("pcicfg_fcode_probe():" + "Failed to setup config space\n"); + ret = PCICFG_NODEVICE; + goto failed3; + } + +#else + p.pci_phys_hi = PCICFG_MAKE_REG_HIGH(bus, device, func, 0); + p.pci_phys_mid = p.pci_phys_low = 0; + p.pci_size_hi = p.pci_size_low = 0; + + /* + * Map in configuration space (temporarily) + */ + acc.devacc_attr_version = DDI_DEVICE_ATTR_V0; + acc.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; + acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC; + + if (pcicfg_map_phys(new_child, &p, &virt, &acc, &h)) { + DEBUG0("pcicfg_fcode_probe():" + "Failed to setup config space\n"); + ret = PCICFG_NODEVICE; + goto failed3; + } + + /* + * First use ddi_peek16 so that if there is not a device there, + * a bus error will not cause a panic. + */ + v = virt + PCI_CONF_VENID; + if (ddi_peek16(new_child, (int16_t *)v, &vendor_id)) { + DEBUG0("Can not read Vendor ID"); + pcicfg_unmap_phys(&h, &p); + ret = PCICFG_NODEVICE; + goto failed3; + } +#endif + DEBUG0("fcode_probe: conf space mapped.\n"); + /* + * As soon as we have access to config space, + * turn off device. It will get turned on + * later (after memory is assigned). + */ + (void) pcicfg_device_off(h); + + /* check if we are PCIe device */ + if (pcicfg_pcie_dev(new_child, PCICFG_DEVICE_TYPE_PCIE, ®s) + == DDI_SUCCESS) { + /*EMPTY*/ + DEBUG0("PCI/PCIe device detected\n"); + } + + /* + * Set 1275 properties common to all devices + */ + if (pcicfg_set_standard_props(new_child, + h, regs.pcie_dev) != PCICFG_SUCCESS) { + DEBUG0("Failed to set standard properties\n"); + goto failed; + } + + /* + * Child node properties NOTE: Both for PCI-PCI bridge and child node + */ + if (pcicfg_set_childnode_props(new_child, + h, regs.pcie_dev) != PCICFG_SUCCESS) { + ret = PCICFG_FAILURE; + goto failed; + } + + header_type = pci_config_get8(h, PCI_CONF_HEADER); + + /* + * If this is not a multi-function card only probe function zero. + */ + if (!(header_type & PCI_HEADER_MULTI) && (func > 0)) { + + ret = PCICFG_NODEVICE; + goto failed; + } + + if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { + + /* + * XXX - Transparent bridges are handled differently + * than other devices with regards to fcode. Since + * no transparent bridge currently ships with fcode, + * there is no reason to try to extract it from its rom + * or call the fcode interpreter to try to load a drop-in. + * If fcode is developed to handle transparent bridges, + * this code will have to change. + */ + + DEBUG3("--Bridge found bus [0x%x] device" + "[0x%x] func [0x%x]\n", bus, device, func); + + if ((ret = pcicfg_probe_bridge(new_child, h, + bus, highest_bus)) != PCICFG_SUCCESS) + (void) pcicfg_free_bridge_resources(new_child); + goto done; + } else { + + DEBUG3("--Leaf device found bus [0x%x] device" + "[0x%x] func [0x%x]\n", + bus, device, func); + + /* + * link in tree, but don't bind driver + * We don't have compatible property yet + */ + (void) i_ndi_config_node(new_child, DS_LINKED, 0); + + if (pci_config_get8(h, PCI_CONF_IPIN)) { + pci_config_put8(h, PCI_CONF_ILINE, 0xf); + } + +#ifdef PCICFG_INTERPRET_FCODE + /* + * Some platforms (x86) don't run fcode, so don't interpret + * fcode that might be in the ROM. + */ + if (pcicfg_dont_interpret == 0) { + + /* This platform supports fcode */ + + vendor_id = pci_config_get16(h, PCI_CONF_VENID); + device_id = pci_config_get16(h, PCI_CONF_DEVID); + + /* + * Get the ROM size and create register for it + */ + pci_config_put32(h, PCI_CONF_ROM, 0xfffffffe); + + request = pci_config_get32(h, PCI_CONF_ROM); + + /* + * If its a zero length, don't do + * any programming. + */ + + if (request != 0) { + /* + * Add resource to assigned-addresses. + */ + if (pcicfg_fcode_assign_bars(h, new_child, + bus, device, func, request, &p) + != PCICFG_SUCCESS) { + DEBUG0("Failed to assign addresses to " + "implemented BARs"); + ret = PCICFG_FAILURE; + goto failed; + } + + /* Turn device on */ + (void) pcicfg_device_on(h); + + /* + * Attempt to load fcode. + */ + (void) pcicfg_load_fcode(new_child, bus, device, + func, vendor_id, device_id, &fcode_addr, + &fcode_size, PCICFG_LOADDR(mem_answer), + (~(PCI_BASE_ROM_ADDR_M & request)) + 1); + + /* Turn device off */ + (void) pcicfg_device_off(h); + + /* + * Free the ROM resources. + */ + (void) pcicfg_free_resource(new_child, p); + + DEBUG2("configure: fcode addr %lx size %x\n", + fcode_addr, fcode_size); + + /* + * Create the fcode-rom-offset property. The + * buffer containing the fcode always starts + * with 0xF1, so the fcode offset is zero. + */ + if (ndi_prop_update_int(DDI_DEV_T_NONE, + new_child, "fcode-rom-offset", 0) + != DDI_SUCCESS) { + DEBUG0("Failed to create " + "fcode-rom-offset property\n"); + ret = PCICFG_FAILURE; + goto failed; + } + } else { + DEBUG0("There is no Expansion ROM\n"); + fcode_addr = NULL; + fcode_size = 0; + } + + /* + * Fill in the bus specific arguments. For + * PCI, it is the config address. + */ + po.config_address = + PCICFG_MAKE_REG_HIGH(bus, device, func, 0); + + DEBUG1("config_address=%x\n", po.config_address); + + /* + * Build unit address. + */ + (void) sprintf(unit_address, "%x,%x", device, func); + + DEBUG3("pci_fc_ops_alloc_handle ap=%lx " + "new device=%lx unit address=%s\n", + parent, new_child, unit_address); + + c = pci_fc_ops_alloc_handle(parent, new_child, + fcode_addr, fcode_size, unit_address, &po); + + DEBUG0("calling fcode_interpreter()\n"); + + DEBUG3("Before int DIP=%lx binding name %s major %d\n", + new_child, ddi_binding_name(new_child), + ddi_name_to_major(ddi_binding_name(new_child))); + + error = fcode_interpreter(parent, &pci_fc_ops, c); + + DEBUG1("returned from fcode_interpreter() - " + "returned %x\n", error); + + pci_fc_ops_free_handle(c); + + DEBUG1("fcode size = %x\n", fcode_size); + /* + * We don't need the fcode anymore. While allocating + * we had rounded up to a page size. + */ + if (fcode_size) { + kmem_free(fcode_addr, ptob(btopr(fcode_size))); + } + } else { + /* This platform does not support fcode */ + + DEBUG0("NOT calling fcode_interpreter()\n"); + } + +#endif /* PCICFG_INTERPRET_FCODE */ + + if ((error == 0) && (pcicfg_dont_interpret == 0)) { + /* + * The interpreter completed successfully. + * We need to redo the resources based on the new reg + * property. + */ + DEBUG3("DIP=%lx binding name %s major %d\n", new_child, + ddi_binding_name(new_child), + ddi_name_to_major(ddi_binding_name(new_child))); + + /* + * Readjust resources specified by reg property. + */ + if (pcicfg_alloc_new_resources(new_child) == + PCICFG_FAILURE) { + ret = PCICFG_FAILURE; + goto failed; + } + + ret = PCICFG_SUCCESS; + /* no fcode, bind driver now */ + (void) ndi_devi_bind_driver(new_child, 0); + + goto done; + } else if ((error != FC_NO_FCODE) && + (pcicfg_dont_interpret == 0)) { + /* + * The interpreter located fcode, but had an error in + * processing. Cleanup and fail the operation. + */ + DEBUG0("Interpreter detected fcode failure\n"); + (void) pcicfg_free_resources(new_child); + ret = PCICFG_FAILURE; + goto failed; + } else { + /* + * Either the interpreter failed with FC_NO_FCODE or we + * chose not to run the interpreter + * (pcicfg_dont_interpret). + * + * If the interpreter failed because there was no + * dropin, then we need to probe the device ourself. + */ + + /* + * Free any resources that may have been assigned + * during fcode loading/execution since we need + * to start over. + */ + (void) pcicfg_free_resources(new_child); + +#ifdef EFCODE21554 + pcicfg_config_teardown(&h); +#else + pcicfg_unmap_phys(&h, &p); +#endif + (void) ndi_devi_free(new_child); + + DEBUG0("No Drop-in Probe device ourself\n"); + + ret = pcicfg_probe_children(parent, bus, device, func, + highest_bus); + + if (ret != PCICFG_SUCCESS) { + DEBUG0("Could not self probe child\n"); + goto failed2; + } + + /* + * We successfully self probed the device. + */ + if ((new_child = pcicfg_devi_find( + parent, device, func)) == NULL) { + DEBUG0("Did'nt find device node " + "just created\n"); + ret = PCICFG_FAILURE; + goto failed2; + } +#ifdef EFCODE21554 + /* + * Till now, we have detected a non transparent bridge + * (ntbridge) as a part of the generic probe code and + * configured only one configuration + * header which is the side facing the host bus. + * Now, configure the other side and create children. + * + * To make the process simpler, lets load the device + * driver for the non transparent bridge as this is a + * Solaris bundled driver, and use its configuration map + * services rather than programming it here. + * If the driver is not bundled into Solaris, it must be + * first loaded and configured before performing any + * hotplug operations. + * + * This not only makes the code simpler but also more + * generic. + * + * So here we go. + */ + if (pcicfg_is_ntbridge(new_child) != DDI_FAILURE) { + + DEBUG0("Found nontransparent bridge.\n"); + + ret = pcicfg_configure_ntbridge(new_child, + bus, device); + } + if (ret != PCICFG_SUCCESS) { + /* + * Bridge configure failed. Free up the self + * probed entry. The bus resource allocation + * maps need to be cleaned up to prevent + * warnings on retries of the failed configure. + */ + (void) pcicfg_ntbridge_unconfigure(new_child); + (void) pcicfg_teardown_device(new_child); + } +#endif + goto done2; + } + } +done: +failed: +#ifdef EFCODE21554 + pcicfg_config_teardown(&h); +#else + pcicfg_unmap_phys(&h, &p); +#endif +failed3: + if (ret != PCICFG_SUCCESS) + (void) ndi_devi_free(new_child); +done2: +failed2: + if (parent_regs.pcie_dev) { + pcicfg_enable_bridge_probe_err(parent, ph, &parent_regs); + pci_config_teardown(&ph); + } + return (ret); +} + +static int +pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, + uint_t *highest_bus) +{ + uint64_t next_bus; + uint_t new_bus, num_slots; + ndi_ra_request_t req; + int rval, i, j; + uint64_t mem_answer, mem_base, mem_alen, mem_size, mem_end; + uint64_t io_answer, io_base, io_alen, io_size, io_end; + uint64_t round_answer, round_len; + pcicfg_range_t range[PCICFG_RANGE_LEN]; + int bus_range[2]; + pcicfg_phdl_t phdl; + int count; + uint64_t pcibus_base, pcibus_alen; + uint64_t max_bus; + uint8_t pcie_device_type = 0; + + bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); + req.ra_flags = (NDI_RA_ALLOC_BOUNDED | NDI_RA_ALLOC_PARTIAL_OK); + req.ra_boundbase = 0; + req.ra_boundlen = PCICFG_MAX_BUS_DEPTH; + req.ra_len = PCICFG_MAX_BUS_DEPTH; + req.ra_align_mask = 0; /* no alignment needed */ + + rval = ndi_ra_alloc(ddi_get_parent(new_child), &req, + &pcibus_base, &pcibus_alen, NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS); + + if (rval != NDI_SUCCESS) { + if (rval == NDI_RA_PARTIAL_REQ) { + /*EMPTY*/ + DEBUG0("NDI_RA_PARTIAL_REQ returned for bus range\n"); + } else { + DEBUG0( + "Failed to allocate bus range for bridge\n"); + return (PCICFG_FAILURE); + } + } + + DEBUG2("Bus Range Allocated [base=%d] [len=%d]\n", + pcibus_base, pcibus_alen); + + if (ndi_ra_map_setup(new_child, NDI_RA_TYPE_PCI_BUSNUM) + == NDI_FAILURE) { + DEBUG0("Can not setup resource map - NDI_RA_TYPE_PCI_BUSNUM\n"); + return (PCICFG_FAILURE); + } + + /* + * Put available bus range into the pool. + * Take the first one for this bridge to use and don't give + * to child. + */ + (void) ndi_ra_free(new_child, pcibus_base+1, pcibus_alen-1, + NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS); + + next_bus = pcibus_base; + max_bus = pcibus_base + pcibus_alen - 1; + + new_bus = next_bus; + + DEBUG1("NEW bus found ->[%d]\n", new_bus); + + /* Keep track of highest bus for subordinate bus programming */ + *highest_bus = new_bus; + + /* + * Allocate Memory Space for Bridge + */ + bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); + req.ra_flags = (NDI_RA_ALLOC_BOUNDED | NDI_RA_ALLOC_PARTIAL_OK); + req.ra_boundbase = 0; + /* + * Note: To support a 32b system, boundlen and len need to be + * 32b quantities + */ + req.ra_boundlen = PCICFG_4GIG_LIMIT + 1; + req.ra_len = PCICFG_4GIG_LIMIT + 1; /* Get as big as possible */ + req.ra_align_mask = + PCICFG_MEMGRAN - 1; /* 1M alignment on memory space */ + + rval = ndi_ra_alloc(ddi_get_parent(new_child), &req, + &mem_answer, &mem_alen, NDI_RA_TYPE_MEM, NDI_RA_PASS); + + if (rval != NDI_SUCCESS) { + if (rval == NDI_RA_PARTIAL_REQ) { + /*EMPTY*/ + DEBUG0("NDI_RA_PARTIAL_REQ returned\n"); + } else { + DEBUG0( + "Failed to allocate memory for bridge\n"); + return (PCICFG_FAILURE); + } + } + + DEBUG3("Bridge Memory Allocated [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(mem_answer), + PCICFG_LOADDR(mem_answer), + mem_alen); + + if (ndi_ra_map_setup(new_child, NDI_RA_TYPE_MEM) == NDI_FAILURE) { + DEBUG0("Can not setup resource map - NDI_RA_TYPE_MEM\n"); + return (PCICFG_FAILURE); + } + + /* + * Put available memory into the pool. + */ + (void) ndi_ra_free(new_child, mem_answer, mem_alen, NDI_RA_TYPE_MEM, + NDI_RA_PASS); + + mem_base = mem_answer; + + /* + * Allocate I/O Space for Bridge + */ + bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); + req.ra_align_mask = PCICFG_IOGRAN - 1; /* 4k alignment */ + req.ra_boundbase = 0; + req.ra_boundlen = PCICFG_4GIG_LIMIT; + req.ra_flags = (NDI_RA_ALLOC_BOUNDED | NDI_RA_ALLOC_PARTIAL_OK); + req.ra_len = PCICFG_4GIG_LIMIT; /* Get as big as possible */ + + rval = ndi_ra_alloc(ddi_get_parent(new_child), &req, &io_answer, + &io_alen, NDI_RA_TYPE_IO, NDI_RA_PASS); + + if (rval != NDI_SUCCESS) { + if (rval == NDI_RA_PARTIAL_REQ) { + /*EMPTY*/ + DEBUG0("NDI_RA_PARTIAL_REQ returned\n"); + } else { + DEBUG0("Failed to allocate io space for bridge\n"); + io_base = io_answer = io_alen = 0; + /* return (PCICFG_FAILURE); */ + } + } + + if (io_alen) { + DEBUG3("Bridge IO Space Allocated [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(io_answer), PCICFG_LOADDR(io_answer), + io_alen); + + if (ndi_ra_map_setup(new_child, NDI_RA_TYPE_IO) == + NDI_FAILURE) { + DEBUG0("Can not setup resource map - NDI_RA_TYPE_IO\n"); + return (PCICFG_FAILURE); + } + + /* + * Put available I/O into the pool. + */ + (void) ndi_ra_free(new_child, io_answer, io_alen, + NDI_RA_TYPE_IO, NDI_RA_PASS); + io_base = io_answer; + } + + (void) pcicfg_set_bus_numbers(h, bus, new_bus, max_bus); + + /* + * Reset the secondary bus + */ + pci_config_put16(h, PCI_BCNF_BCNTRL, + pci_config_get16(h, PCI_BCNF_BCNTRL) | 0x40); + + drv_usecwait(100); + + pci_config_put16(h, PCI_BCNF_BCNTRL, + pci_config_get16(h, PCI_BCNF_BCNTRL) & ~0x40); + + /* + * Program the memory base register with the + * start of the memory range + */ + pci_config_put16(h, PCI_BCNF_MEM_BASE, + PCICFG_HIWORD(PCICFG_LOADDR(mem_answer))); + + /* + * Program the memory limit register with the + * end of the memory range. + */ + + pci_config_put16(h, PCI_BCNF_MEM_LIMIT, + PCICFG_HIWORD(PCICFG_LOADDR( + PCICFG_ROUND_DOWN((mem_answer + mem_alen), PCICFG_MEMGRAN) - 1))); + + /* + * Allocate the chunk of memory (if any) not programmed into the + * bridge because of the round down. + */ + if (PCICFG_ROUND_DOWN((mem_answer + mem_alen), PCICFG_MEMGRAN) + != (mem_answer + mem_alen)) { + DEBUG0("Need to allocate Memory round off chunk\n"); + bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); + req.ra_flags = NDI_RA_ALLOC_SPECIFIED; + req.ra_addr = PCICFG_ROUND_DOWN((mem_answer + mem_alen), + PCICFG_MEMGRAN); + req.ra_len = (mem_answer + mem_alen) - + (PCICFG_ROUND_DOWN((mem_answer + mem_alen), + PCICFG_MEMGRAN)); + + (void) ndi_ra_alloc(new_child, &req, + &round_answer, &round_len, NDI_RA_TYPE_MEM, NDI_RA_PASS); + } + + /* + * Program the I/O Space Base + */ + pci_config_put8(h, PCI_BCNF_IO_BASE_LOW, + PCICFG_HIBYTE(PCICFG_LOWORD( + PCICFG_LOADDR(io_answer)))); + + pci_config_put16(h, PCI_BCNF_IO_BASE_HI, + PCICFG_HIWORD(PCICFG_LOADDR(io_answer))); + + /* + * Program the I/O Space Limit + */ + pci_config_put8(h, PCI_BCNF_IO_LIMIT_LOW, + PCICFG_HIBYTE(PCICFG_LOWORD( + PCICFG_LOADDR(PCICFG_ROUND_DOWN(io_answer + io_alen, + PCICFG_IOGRAN)))) - 1); + + pci_config_put16(h, PCI_BCNF_IO_LIMIT_HI, + PCICFG_HIWORD(PCICFG_LOADDR( + PCICFG_ROUND_DOWN(io_answer + io_alen, PCICFG_IOGRAN))) + - 1); + + /* + * Allocate the chunk of I/O (if any) not programmed into the + * bridge because of the round down. + */ + if (PCICFG_ROUND_DOWN((io_answer + io_alen), PCICFG_IOGRAN) + != (io_answer + io_alen)) { + DEBUG0("Need to allocate I/O round off chunk\n"); + bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); + req.ra_flags = NDI_RA_ALLOC_SPECIFIED; + req.ra_addr = PCICFG_ROUND_DOWN((io_answer + io_alen), + PCICFG_IOGRAN); + req.ra_len = (io_answer + io_alen) - + (PCICFG_ROUND_DOWN((io_answer + io_alen), + PCICFG_IOGRAN)); + + (void) ndi_ra_alloc(new_child, &req, + &round_answer, &round_len, NDI_RA_TYPE_IO, NDI_RA_PASS); + } + + /* + * Clear status bits + */ + pci_config_put16(h, PCI_BCNF_SEC_STATUS, 0xffff); + + /* + * Turn off prefetchable range + */ + pci_config_put32(h, PCI_BCNF_PF_BASE_LOW, 0x0000ffff); + pci_config_put32(h, PCI_BCNF_PF_BASE_HIGH, 0xffffffff); + pci_config_put32(h, PCI_BCNF_PF_LIMIT_HIGH, 0x0); + + /* + * Needs to be set to this value + */ + pci_config_put8(h, PCI_CONF_ILINE, 0xf); + + /* check our device_type as defined by Open Firmware */ + if (pcicfg_pcie_device_type(new_child, h) == DDI_SUCCESS) + pcie_device_type = 1; + + /* + * Set bus properties + */ + if (pcicfg_set_busnode_props(new_child, pcie_device_type) + != PCICFG_SUCCESS) { + DEBUG0("Failed to set busnode props\n"); + return (PCICFG_FAILURE); + } + + (void) pcicfg_device_on(h); + + if (ndi_devi_online(new_child, NDI_NO_EVENT|NDI_CONFIG) + != NDI_SUCCESS) { + DEBUG0("Unable to online bridge\n"); + return (PCICFG_FAILURE); + } + + DEBUG0("Bridge is ONLINE\n"); + + /* + * After a Reset, we need to wait 2^25 clock cycles before the + * first Configuration access. The worst case is 33MHz, which + * is a 1 second wait. + */ + drv_usecwait(pcicfg_sec_reset_delay); + + /* + * Probe all children devices + */ + DEBUG0("Bridge Programming Complete - probe children\n"); + ndi_devi_enter(new_child, &count); + for (i = 0; i < PCICFG_MAX_DEVICE; i++) { + for (j = 0; j < PCICFG_MAX_FUNCTION; j++) { + if ((rval = pcicfg_fcode_probe(new_child, + new_bus, i, j, highest_bus)) + != PCICFG_SUCCESS) { + if (rval == PCICFG_NODEVICE) { + DEBUG3("No Device at bus [0x%x]" + "device [0x%x] " + "func [0x%x]\n", new_bus, i, j); + if (j) + continue; + } else { + DEBUG3("Failed to configure bus " + "[0x%x] device [0x%x] " + "func [0x%x]\n", new_bus, i, j); + rval = PCICFG_FAILURE; + } + + break; + } + } + /* if any function fails to be configured, no need to proceed */ + if (rval != PCICFG_NODEVICE) { + break; + } + } + ndi_devi_exit(new_child, count); + + /* if empty topology underneath, it is still a success. */ + if (rval != PCICFG_FAILURE) + rval = PCICFG_SUCCESS; + + /* + * Offline the bridge to allow reprogramming of resources. + */ + (void) ndi_devi_offline(new_child, NDI_NO_EVENT|NDI_UNCONFIG); + + phdl.dip = new_child; + phdl.memory_base = mem_answer; + phdl.io_base = (uint32_t)io_answer; + phdl.error = PCICFG_SUCCESS; /* in case of empty child tree */ + + ndi_devi_enter(ddi_get_parent(new_child), &count); + ddi_walk_devs(new_child, pcicfg_find_resource_end, (void *)&phdl); + ndi_devi_exit(ddi_get_parent(new_child), count); + + if (phdl.error != PCICFG_SUCCESS) { + DEBUG0("Failure summing resources\n"); + return (PCICFG_FAILURE); + } + + num_slots = pcicfg_get_nslots(new_child, h); + mem_end = PCICFG_ROUND_UP(phdl.memory_base, PCICFG_MEMGRAN); + io_end = PCICFG_ROUND_UP(phdl.io_base, PCICFG_IOGRAN); + + DEBUG3("Start of Unallocated Bridge(%d slots) Resources " + "Mem=0x%lx I/O=0x%lx\n", num_slots, mem_end, io_end); + + /* + * if the bridge a slots, then preallocate. If not, assume static + * configuration. Also check for preallocation limits and spit + * warning messages appropriately (perhaps some can be in debug mode). + */ + if (num_slots) { + uint64_t mem_reqd = mem_answer + (num_slots * + pcicfg_slot_memsize); + uint64_t io_reqd = io_answer + (num_slots * + pcicfg_slot_iosize); + uint8_t highest_bus_reqd = new_bus + (num_slots * + pcicfg_slot_busnums); + + if (mem_end > mem_reqd) + cmn_err(CE_WARN, "Memory space consumed by bridge" + " more than planned for %d slot(s)(%lx, %lx)", + num_slots, mem_answer, mem_end); + if (io_end > io_reqd) + cmn_err(CE_WARN, "IO space consumed by bridge" + " more than planned for %d slot(s)(%lx, %lx)", + num_slots, io_answer, io_end); + if (*highest_bus > highest_bus_reqd) + cmn_err(CE_WARN, "Buses consumed by bridge" + " more than planned for %d slot(s)(%x, %x)", + num_slots, new_bus, *highest_bus); + + if (mem_reqd > (mem_answer + mem_alen)) + cmn_err(CE_WARN, "Memory space required by bridge" + " more than available for %d slot(s)(%lx, %lx)", + num_slots, mem_answer, mem_end); + + if (io_reqd > (io_answer + io_alen)) + cmn_err(CE_WARN, "IO space required by bridge" + " more than available for %d slot(s)(%lx, %lx)", + num_slots, io_answer, io_end); + if (highest_bus_reqd > max_bus) + cmn_err(CE_WARN, "Bus numbers required by bridge" + " more than available for %d slot(s)(%x, %x)", + num_slots, new_bus, *highest_bus); + + mem_end = MAX((MIN(mem_reqd, (mem_answer + mem_alen))), + mem_end); + io_end = MAX((MIN(io_reqd, (io_answer + io_alen))), io_end); + *highest_bus = MAX((MIN(highest_bus_reqd, max_bus)), + *highest_bus); + DEBUG3("mem_end %lx, io_end %lx, highest_bus %x\n", + mem_end, io_end, *highest_bus); + } + + /* + * Give back unused memory space to parent. + */ + (void) ndi_ra_free(ddi_get_parent(new_child), + mem_end, (mem_answer + mem_alen) - mem_end, NDI_RA_TYPE_MEM, + NDI_RA_PASS); + + if (mem_end == mem_answer) { + DEBUG0("No memory resources used\n"); + /* + * To prevent the bridge from forwarding any Memory + * transactions, the Memory Limit will be programmed + * with a smaller value than the Memory Base. + */ + pci_config_put16(h, PCI_BCNF_MEM_BASE, 0xffff); + pci_config_put16(h, PCI_BCNF_MEM_LIMIT, 0); + + mem_size = 0; + } else { + /* + * Reprogram the end of the memory. + */ + pci_config_put16(h, PCI_BCNF_MEM_LIMIT, + PCICFG_HIWORD(mem_end) - 1); + mem_size = mem_end - mem_base; + } + + /* + * Give back unused io space to parent. + */ + (void) ndi_ra_free(ddi_get_parent(new_child), + io_end, (io_answer + io_alen) - io_end, + NDI_RA_TYPE_IO, NDI_RA_PASS); + + if (io_end == io_answer) { + DEBUG0("No IO Space resources used\n"); + + /* + * To prevent the bridge from forwarding any I/O + * transactions, the I/O Limit will be programmed + * with a smaller value than the I/O Base. + */ + pci_config_put8(h, PCI_BCNF_IO_LIMIT_LOW, 0); + pci_config_put16(h, PCI_BCNF_IO_LIMIT_HI, 0); + pci_config_put8(h, PCI_BCNF_IO_BASE_LOW, 0xff); + pci_config_put16(h, PCI_BCNF_IO_BASE_HI, 0); + + io_size = 0; + } else { + /* + * Reprogram the end of the io space. + */ + pci_config_put8(h, PCI_BCNF_IO_LIMIT_LOW, + PCICFG_HIBYTE(PCICFG_LOWORD( + PCICFG_LOADDR(io_end) - 1))); + + pci_config_put16(h, PCI_BCNF_IO_LIMIT_HI, + PCICFG_HIWORD(PCICFG_LOADDR(io_end - 1))); + + io_size = io_end - io_base; + } + + if ((max_bus - *highest_bus) > 0) { + /* + * Give back unused bus numbers + */ + (void) ndi_ra_free(ddi_get_parent(new_child), + *highest_bus+1, max_bus - *highest_bus, + NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS); + } + + /* + * Set bus numbers to ranges encountered during scan + */ + (void) pcicfg_set_bus_numbers(h, bus, new_bus, *highest_bus); + + /* + * Remove the ranges property if it exists since we will create + * a new one. + */ + (void) ndi_prop_remove(DDI_DEV_T_NONE, new_child, "ranges"); + + DEBUG2("Creating Ranges property - Mem Address %lx Mem Size %x\n", + mem_base, mem_size); + DEBUG2(" - I/O Address %lx I/O Size %x\n", + io_base, io_size); + + bzero((caddr_t)range, sizeof (pcicfg_range_t) * PCICFG_RANGE_LEN); + + range[0].child_hi = range[0].parent_hi |= (PCI_REG_REL_M | PCI_ADDR_IO); + range[0].child_lo = range[0].parent_lo = io_base; + range[1].child_hi = range[1].parent_hi |= + (PCI_REG_REL_M | PCI_ADDR_MEM32); + range[1].child_lo = range[1].parent_lo = mem_base; + + if (io_size > 0) { + range[0].size_lo = io_size; + if (pcicfg_update_ranges_prop(new_child, &range[0])) { + DEBUG0("Failed to update ranges (io)\n"); + return (PCICFG_FAILURE); + } + } + if (mem_size > 0) { + range[1].size_lo = mem_size; + if (pcicfg_update_ranges_prop(new_child, &range[1])) { + DEBUG0("Failed to update ranges (memory)\n"); + return (PCICFG_FAILURE); + } + } + + bus_range[0] = pci_config_get8(h, PCI_BCNF_SECBUS); + bus_range[1] = pci_config_get8(h, PCI_BCNF_SUBBUS); + DEBUG1("End of bridge probe: bus_range[0] = %d\n", bus_range[0]); + DEBUG1("End of bridge probe: bus_range[1] = %d\n", bus_range[1]); + + if (ndi_prop_update_int_array(DDI_DEV_T_NONE, new_child, + "bus-range", bus_range, 2) != DDI_SUCCESS) { + DEBUG0("Failed to set bus-range property"); + return (PCICFG_FAILURE); + } + /* + * Remove the resource maps for the bridge since we no longer + * need them. Note that the failure is ignored since the + * ndi_devi_offline above may have already taken care of it via + * driver detach. + * It has been checked that there are no other reasons for + * failure other than map itself being non-existent. So we are Ok. + */ + if (ndi_ra_map_destroy(new_child, NDI_RA_TYPE_MEM) == NDI_FAILURE) { + /*EMPTY*/ + DEBUG0("Can not destroy resource map - NDI_RA_TYPE_MEM\n"); + } + + if (ndi_ra_map_destroy(new_child, NDI_RA_TYPE_IO) == NDI_FAILURE) { + /*EMPTY*/ + DEBUG0("Can not destroy resource map - NDI_RA_TYPE_IO\n"); + } + + if (ndi_ra_map_destroy(new_child, NDI_RA_TYPE_PCI_BUSNUM) + == NDI_FAILURE) { + /*EMPTY*/ + DEBUG0("Can't destroy resource map - NDI_RA_TYPE_PCI_BUSNUM\n"); + } + + return (rval); +} + +/* + * Return PCICFG_SUCCESS if device exists at the specified address. + * Return PCICFG_NODEVICE is no device exists at the specified address. + * + */ +int +pcicfg_config_setup(dev_info_t *dip, ddi_acc_handle_t *handle) +{ + caddr_t virt; + ddi_device_acc_attr_t attr; + int status; + int rlen; + pci_regspec_t *reg; + int ret = DDI_SUCCESS; + int16_t tmp; + /* + * flags = PCICFG_CONF_INDIRECT_MAP if configuration space is indirectly + * mapped, otherwise it is 0. "flags" is introduced in support of any + * non transparent bridges, where configuration space is indirectly + * mapped. + * Indirect mapping is always true on sun4v systems. + */ + int flags = 0; + + + /* + * Get the pci register spec from the node + */ + status = ddi_getlongprop(DDI_DEV_T_ANY, + dip, DDI_PROP_DONTPASS, "reg", (caddr_t)®, &rlen); + + switch (status) { + case DDI_PROP_SUCCESS: + break; + case DDI_PROP_NO_MEMORY: + DEBUG0("reg present, but unable to get memory\n"); + return (PCICFG_FAILURE); + default: + DEBUG0("no reg property\n"); + return (PCICFG_FAILURE); + } + + if (pcicfg_indirect_map(dip) == DDI_SUCCESS) + flags |= PCICFG_CONF_INDIRECT_MAP; + + /* + * Map in configuration space (temporarily) + */ + attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; + attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; + attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; + attr.devacc_attr_access = DDI_CAUTIOUS_ACC; + +#ifdef EFCODE21554 + if (ddi_regs_map_setup(dip, 0, &virt, + 0, 0, &attr, handle) != DDI_SUCCESS) +#else + if (pcicfg_map_phys(dip, reg, &virt, &attr, handle) + != DDI_SUCCESS) +#endif + { + DEBUG0("pcicfg_config_setup():" + "Failed to setup config space\n"); + + kmem_free((caddr_t)reg, rlen); + return (PCICFG_FAILURE); + } + + if (flags & PCICFG_CONF_INDIRECT_MAP) { + /* + * need to use DDI interfaces as the conf space is + * cannot be directly accessed by the host. + */ + tmp = (int16_t)ddi_get16(*handle, (uint16_t *)virt); + } else { + ret = ddi_peek16(dip, (int16_t *)virt, &tmp); + } + + if (ret == DDI_SUCCESS) { + if (tmp == -1) { + DEBUG1("NO DEVICEFOUND, read %x\n", tmp); + ret = PCICFG_NODEVICE; + } else { + /* XXX - Need to check why HV is returning 0 */ + if (tmp == 0) { + DEBUG0("Device Not Ready yet ?"); + ret = PCICFG_NODEVICE; + } else { + DEBUG1("DEVICEFOUND, read %x\n", tmp); + ret = PCICFG_SUCCESS; + } + } + } else { + DEBUG0("ddi_peek failed, must be NODEVICE\n"); + ret = PCICFG_NODEVICE; + } + + /* + * A bug in XMITS 3.0 causes us to miss the Master Abort Split + * Completion message. The result is the error message being + * sent back as part of the config data. If the first two words + * of the config space happen to be the same as the Master Abort + * message, then report back that there is no device there. + */ + if ((ret == PCICFG_SUCCESS) && !(flags & PCICFG_CONF_INDIRECT_MAP)) { + int32_t pcix_scm; + +#define PCICFG_PCIX_SCM 0x10000004 + + pcix_scm = 0; + (void) ddi_peek32(dip, (int32_t *)virt, &pcix_scm); + if (pcix_scm == PCICFG_PCIX_SCM) { + pcix_scm = 0; + (void) ddi_peek32(dip, + (int32_t *)(virt + 4), &pcix_scm); + if (pcix_scm == PCICFG_PCIX_SCM) + ret = PCICFG_NODEVICE; + } + } + + if (ret == PCICFG_NODEVICE) +#ifdef EFCODE21554 + ddi_regs_map_free(handle); +#else + pcicfg_unmap_phys(handle, reg); +#endif + + kmem_free((caddr_t)reg, rlen); + + return (ret); + +} + +static void +pcicfg_config_teardown(ddi_acc_handle_t *handle) +{ + (void) ddi_regs_map_free(handle); +} + +static int +pcicfg_add_config_reg(dev_info_t *dip, + uint_t bus, uint_t device, uint_t func) +{ + int reg[10] = { PCI_ADDR_CONFIG, 0, 0, 0, 0}; + + reg[0] = PCICFG_MAKE_REG_HIGH(bus, device, func, 0); + + return (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, + "reg", reg, 5)); +} + +static int +pcicfg_dump_assigned(dev_info_t *dip) +{ + pci_regspec_t *reg; + int length; + int rcount; + int i; + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)®, + &length) != DDI_PROP_SUCCESS) { + DEBUG0("Failed to read assigned-addresses property\n"); + return (PCICFG_FAILURE); + } + + rcount = length / sizeof (pci_regspec_t); + for (i = 0; i < rcount; i++) { + DEBUG4("pcicfg_dump_assigned - size=%x low=%x mid=%x high=%x\n", + reg[i].pci_size_low, reg[i].pci_phys_low, + reg[i].pci_phys_mid, reg[i].pci_phys_hi); + } + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)reg, length); + + return (PCICFG_SUCCESS); +} + +#ifdef PCICFG_INTERPRET_FCODE +static int +pcicfg_load_fcode(dev_info_t *dip, uint_t bus, uint_t device, uint_t func, + uint16_t vendor_id, uint16_t device_id, uchar_t **fcode_addr, + int *fcode_size, int rom_paddr, int rom_size) +{ + pci_regspec_t p; + int pci_data; + int start_of_fcode; + int image_length; + int code_type; + ddi_acc_handle_t h; + ddi_device_acc_attr_t acc; + uint8_t *addr; + int8_t image_not_found, indicator; + uint16_t vendor_id_img, device_id_img; + int16_t rom_sig; +#ifdef DEBUG + int i; +#endif + + DEBUG4("pcicfg_load_fcode() - " + "bus %x device =%x func=%x rom_paddr=%lx\n", + bus, device, func, rom_paddr); + DEBUG2("pcicfg_load_fcode() - vendor_id=%x device_id=%x\n", + vendor_id, device_id); + + *fcode_size = 0; + *fcode_addr = NULL; + + acc.devacc_attr_version = DDI_DEVICE_ATTR_V0; + acc.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; + acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC; + + p.pci_phys_hi = PCI_ADDR_MEM32 | PCICFG_MAKE_REG_HIGH(bus, device, + func, PCI_CONF_ROM); + + p.pci_phys_mid = 0; + p.pci_phys_low = 0; + + p.pci_size_low = rom_size; + p.pci_size_hi = 0; + + if (pcicfg_map_phys(dip, &p, (caddr_t *)&addr, &acc, &h)) { + DEBUG1("Can Not map in ROM %x\n", p.pci_phys_low); + return (PCICFG_FAILURE); + } + + /* + * Walk the ROM to find the proper image for this device. + */ + image_not_found = 1; + while (image_not_found) { + DEBUG1("Expansion ROM maps to %lx\n", addr); + +#ifdef DEBUG + if (pcicfg_dump_fcode) { + for (i = 0; i < 100; i++) + DEBUG2("ROM 0x%x --> 0x%x\n", i, + ddi_get8(h, (uint8_t *)(addr + i))); + } +#endif + + /* + * Some device say they have an Expansion ROM, but do not, so + * for non-21554 devices use peek so we don't panic due to + * accessing non existent memory. + */ + if (pcicfg_indirect_map(dip) == DDI_SUCCESS) { + rom_sig = ddi_get16(h, + (uint16_t *)(addr + PCI_ROM_SIGNATURE)); + } else { + if (ddi_peek16(dip, + (int16_t *)(addr + PCI_ROM_SIGNATURE), &rom_sig)) { + cmn_err(CE_WARN, + "PCI Expansion ROM is not accessible"); + pcicfg_unmap_phys(&h, &p); + return (PCICFG_FAILURE); + } + } + + /* + * Validate the ROM Signature. + */ + if ((uint16_t)rom_sig != 0xaa55) { + DEBUG1("Invalid ROM Signature %x\n", (uint16_t)rom_sig); + pcicfg_unmap_phys(&h, &p); + return (PCICFG_FAILURE); + } + + DEBUG0("Valid ROM Signature Found\n"); + + start_of_fcode = ddi_get16(h, (uint16_t *)(addr + 2)); + + pci_data = ddi_get16(h, + (uint16_t *)(addr + PCI_ROM_PCI_DATA_STRUCT_PTR)); + + DEBUG2("Pointer To PCI Data Structure %x %x\n", pci_data, + addr); + + /* + * Validate the PCI Data Structure Signature. + * 0x52494350 = "PCIR" + */ + + if (ddi_get8(h, (uint8_t *)(addr + pci_data)) != 0x50) { + DEBUG0("Invalid PCI Data Structure Signature\n"); + pcicfg_unmap_phys(&h, &p); + return (PCICFG_FAILURE); + } + + if (ddi_get8(h, (uint8_t *)(addr + pci_data + 1)) != 0x43) { + DEBUG0("Invalid PCI Data Structure Signature\n"); + pcicfg_unmap_phys(&h, &p); + return (PCICFG_FAILURE); + } + if (ddi_get8(h, (uint8_t *)(addr + pci_data + 2)) != 0x49) { + DEBUG0("Invalid PCI Data Structure Signature\n"); + pcicfg_unmap_phys(&h, &p); + return (PCICFG_FAILURE); + } + if (ddi_get8(h, (uint8_t *)(addr + pci_data + 3)) != 0x52) { + DEBUG0("Invalid PCI Data Structure Signature\n"); + pcicfg_unmap_phys(&h, &p); + return (PCICFG_FAILURE); + } + + /* + * Is this image for this device? + */ + vendor_id_img = ddi_get16(h, + (uint16_t *)(addr + pci_data + PCI_PDS_VENDOR_ID)); + device_id_img = ddi_get16(h, + (uint16_t *)(addr + pci_data + PCI_PDS_DEVICE_ID)); + + DEBUG2("This image is for vendor_id=%x device_id=%x\n", + vendor_id_img, device_id_img); + + code_type = ddi_get8(h, addr + pci_data + PCI_PDS_CODE_TYPE); + + switch (code_type) { + case PCI_PDS_CODE_TYPE_PCAT: + DEBUG0("ROM is of x86/PC-AT Type\n"); + break; + case PCI_PDS_CODE_TYPE_OPEN_FW: + DEBUG0("ROM is of Open Firmware Type\n"); + break; + default: + DEBUG1("ROM is of Unknown Type 0x%x\n", code_type); + break; + } + + if ((vendor_id_img != vendor_id) || + (device_id_img != device_id) || + (code_type != PCI_PDS_CODE_TYPE_OPEN_FW)) { + DEBUG0("Firmware Image is not for this device..." + "goto next image\n"); + /* + * Read indicator byte to see if there is another + * image in the ROM + */ + indicator = ddi_get8(h, + (uint8_t *)(addr + pci_data + PCI_PDS_INDICATOR)); + + if (indicator != 1) { + /* + * There is another image in the ROM. + */ + image_length = ddi_get16(h, (uint16_t *)(addr + + pci_data + PCI_PDS_IMAGE_LENGTH)) * 512; + + addr += image_length; + } else { + /* + * There are no more images. + */ + DEBUG0("There are no more images in the ROM\n"); + pcicfg_unmap_phys(&h, &p); + + return (PCICFG_FAILURE); + } + } else { + DEBUG0("Correct image was found\n"); + image_not_found = 0; /* Image was found */ + } + } + + *fcode_size = (ddi_get8(h, addr + start_of_fcode + 4) << 24) | + (ddi_get8(h, addr + start_of_fcode + 5) << 16) | + (ddi_get8(h, addr + start_of_fcode + 6) << 8) | + (ddi_get8(h, addr + start_of_fcode + 7)); + + DEBUG1("Fcode Size %x\n", *fcode_size); + + /* + * Allocate page aligned buffer space + */ + *fcode_addr = kmem_zalloc(ptob(btopr(*fcode_size)), KM_SLEEP); + + if (*fcode_addr == NULL) { + DEBUG0("kmem_zalloc returned NULL\n"); + pcicfg_unmap_phys(&h, &p); + return (PCICFG_FAILURE); + } + + DEBUG1("Fcode Addr %lx\n", *fcode_addr); + + ddi_rep_get8(h, *fcode_addr, addr + start_of_fcode, *fcode_size, + DDI_DEV_AUTOINCR); + + pcicfg_unmap_phys(&h, &p); + + return (PCICFG_SUCCESS); +} + +static int +pcicfg_fcode_assign_bars(ddi_acc_handle_t h, dev_info_t *dip, uint_t bus, + uint_t device, uint_t func, int32_t fc_request, pci_regspec_t *rom_regspec) +{ + /* + * Assign values to all BARs so that it is safe to turn on the + * device for accessing the fcode on the PROM. On successful + * exit from this function, "assigned-addresses" are created + * for all BARs and ROM BAR is enabled. Also, rom_regspec is + * filled with the values that can be used to free up this + * resource later. + */ + uint32_t request, hiword, size; + pci_regspec_t phys_spec; + ndi_ra_request_t req; + uint64_t mem_answer, mem_alen; + int i; + + DEBUG1("pcicfg_fcode_assign_bars :%s\n", DEVI(dip)->devi_name); + + /* + * Process the BARs. + */ + for (i = PCI_CONF_BASE0; i <= PCI_CONF_BASE5; ) { + pci_config_put32(h, i, 0xffffffff); + request = pci_config_get32(h, i); + /* + * Check if implemented + */ + if (request == 0) { + DEBUG1("pcicfg_fcode_assign_bars :" + "BASE register [0x%x] asks for 0(32)\n", i); + i += 4; + continue; + } + /* + * Build the phys_spec for this BAR + */ + hiword = PCICFG_MAKE_REG_HIGH(bus, device, func, i); + size = (~(PCI_BASE_M_ADDR_M & request)) + 1; + + DEBUG3("pcicfg_fcode_assign_bars :" + "BASE register [0x%x] asks for [0x%x]=[0x%x]\n", + i, request, size); + + if ((PCI_BASE_SPACE_M & request) == PCI_BASE_SPACE_MEM) { + if ((PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_MEM) { + hiword |= PCI_ADDR_MEM32; + } else if ((PCI_BASE_TYPE_M & request) + == PCI_BASE_TYPE_ALL) { + hiword |= PCI_ADDR_MEM64; + } + if (request & PCI_BASE_PREF_M) + hiword |= PCI_REG_PF_M; + } else { + hiword |= PCI_ADDR_IO; + } + phys_spec.pci_phys_hi = hiword; + phys_spec.pci_phys_mid = 0; + phys_spec.pci_phys_low = 0; + phys_spec.pci_size_hi = 0; + phys_spec.pci_size_low = size; + + /* + * The following function + * - allocates address space + * - programs the BAR + * - adds an "assigned-addresses" property + */ + if (pcicfg_alloc_resource(dip, phys_spec)) { + cmn_err(CE_WARN, "failed to allocate %d bytes" + " for dev %s BASE register [0x%x]\n", + size, DEVI(dip)->devi_name, i); + goto failure; + } + if ((PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_ALL) { + /* + * 64 bit, should be in memory space. + */ + i += 8; + } else { + /* + * 32 bit, either memory or I/O space. + */ + i += 4; + } + } + + /* + * Handle ROM BAR. We do not use the common + * resource allocator function because we need to + * return reg spec to the caller. + */ + size = (~(PCI_BASE_ROM_ADDR_M & fc_request)) + 1; + + DEBUG3("BASE register [0x%x] asks for " + "[0x%x]=[0x%x]\n", PCI_CONF_ROM, fc_request, size); + + bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); + + req.ra_boundbase = 0; + req.ra_boundlen = PCICFG_4GIG_LIMIT; + req.ra_len = size; + req.ra_flags |= NDI_RA_ALIGN_SIZE; + req.ra_flags ^= NDI_RA_ALLOC_BOUNDED; + + if (ndi_ra_alloc(ddi_get_parent(dip), + &req, &mem_answer, &mem_alen, + NDI_RA_TYPE_MEM, NDI_RA_PASS)) { + cmn_err(CE_WARN, "failed to allocate %d bytes" + " for dev %s ROM BASE register\n", + size, DEVI(dip)->devi_name); + goto failure; + } + + DEBUG3("ROM addr = [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(mem_answer), + PCICFG_LOADDR(mem_answer), mem_alen); + + /* + * Assign address space and enable ROM. + */ + pci_config_put32(h, PCI_CONF_ROM, + PCICFG_LOADDR(mem_answer) | PCI_BASE_ROM_ENABLE); + + /* + * Add resource to assigned-addresses. + */ + phys_spec.pci_phys_hi = PCICFG_MAKE_REG_HIGH(bus, device, func, \ + PCI_CONF_ROM) | PCI_ADDR_MEM32; + if (fc_request & PCI_BASE_PREF_M) + phys_spec.pci_phys_hi |= PCI_REG_PF_M; + phys_spec.pci_phys_mid = 0; + phys_spec.pci_phys_low = PCICFG_LOADDR(mem_answer); + phys_spec.pci_size_hi = 0; + phys_spec.pci_size_low = size; + + if (pcicfg_update_assigned_prop(dip, &phys_spec) + != PCICFG_SUCCESS) { + cmn_err(CE_WARN, "failed to update" + " assigned-address property for dev %s\n", + DEVI(dip)->devi_name); + goto failure; + } + /* + * Copy out the reg spec. + */ + *rom_regspec = phys_spec; + + return (PCICFG_SUCCESS); + +failure: + /* + * We came in with no "assigned-addresses". + * Free up the resources we may have allocated. + */ + (void) pcicfg_free_device_resources(dip); + + return (PCICFG_FAILURE); +} + +#endif /* PCICFG_INTERPRET_FCODE */ + +static int +pcicfg_free_all_resources(dev_info_t *dip) +{ + pci_regspec_t *assigned; + int assigned_len; + int acount; + int i; + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned, + &assigned_len) != DDI_PROP_SUCCESS) { + DEBUG0("Failed to read assigned-addresses property\n"); + return (PCICFG_FAILURE); + } + + acount = assigned_len / sizeof (pci_regspec_t); + + for (i = 0; i < acount; i++) { + if (pcicfg_free_resource(dip, assigned[i])) { + /* + * Dont forget to free mem from ddi_getlongprop + */ + kmem_free((caddr_t)assigned, assigned_len); + return (PCICFG_FAILURE); + } + } + + /* + * Don't forget to free up memory from ddi_getlongprop + */ + if (assigned_len) + kmem_free((caddr_t)assigned, assigned_len); + + return (PCICFG_SUCCESS); +} +static int +pcicfg_alloc_new_resources(dev_info_t *dip) +{ + pci_regspec_t *assigned, *reg; + int assigned_len, reg_len; + int acount, rcount; + int i, j, alloc_size; + boolean_t alloc; + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "reg", (caddr_t)®, + ®_len) != DDI_PROP_SUCCESS) { + DEBUG0("Failed to read reg property\n"); + return (PCICFG_FAILURE); + } + rcount = reg_len / sizeof (pci_regspec_t); + + DEBUG2("pcicfg_alloc_new_resources() reg size=%x entries=%x\n", + reg_len, rcount); + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned, + &assigned_len) != DDI_PROP_SUCCESS) { + acount = 0; + } else { + acount = assigned_len / sizeof (pci_regspec_t); + } + + DEBUG1("assigned-addresses property len=%x\n", acount); + + /* + * For each address described by reg, search for it in the + * assigned-addresses property. If it does not exist, allocate + * resources for it. If it does exist, check the size in both. + * The size needs to be bigger of the two. + */ + for (i = 1; i < rcount; i++) { + alloc = B_TRUE; + alloc_size = reg[i].pci_size_low; + for (j = 0; j < acount; j++) { + if (assigned[j].pci_phys_hi == reg[i].pci_phys_hi) { + /* + * There is an exact match. Check size. + */ + DEBUG1("pcicfg_alloc_new_resources " + "- %x - MATCH\n", + reg[i].pci_phys_hi); + + if (reg[i].pci_size_low > + assigned[j].pci_size_low) { + /* + * Fcode wants more. + */ + DEBUG3("pcicfg_alloc_new_resources" + " - %x - RESIZE" + " assigned 0x%x reg 0x%x\n", + assigned[j].pci_phys_hi, + assigned[j].pci_size_low, + reg[i].pci_size_low); + + /* + * Free the old resource. + */ + (void) pcicfg_free_resource(dip, + assigned[j]); + } else { + DEBUG3("pcicfg_alloc_new_resources" + " - %x - ENOUGH" + " assigned 0x%x reg 0x%x\n", + assigned[j].pci_phys_hi, + assigned[j].pci_size_low, + reg[i].pci_size_low); + + alloc = B_FALSE; + } + break; + } + /* + * Fcode may have set one or more of the + * NPT bits in phys.hi. + */ + if (PCI_REG_BDFR_G(assigned[j].pci_phys_hi) == + PCI_REG_BDFR_G(reg[i].pci_phys_hi)) { + + DEBUG2("pcicfg_alloc_new_resources " + "- PARTIAL MATCH assigned 0x%x " + "reg 0x%x\n", assigned[j].pci_phys_hi, + reg[i].pci_phys_hi); + /* + * Changing the SS bits is an error + */ + if (PCI_REG_ADDR_G( + assigned[j].pci_phys_hi) != + PCI_REG_ADDR_G(reg[i].pci_phys_hi)) { + + cmn_err(CE_WARN, "Fcode changing" + " SS bits of - 0x%x -" + " on %s\n", reg[i].pci_phys_hi, + DEVI(dip)->devi_name); + + /* + * Dont forget to free mem from + * ddi_getlongprop + */ + if (acount != 0) + kmem_free((caddr_t)assigned, + assigned_len); + kmem_free((caddr_t)reg, reg_len); + return (PCICFG_FAILURE); + } + + + /* + * We are going to allocate new resource. + * Free the old resource. Again, adjust + * the size to be safe. + */ + (void) pcicfg_free_resource(dip, assigned[j]); + + alloc_size = MAX(reg[i].pci_size_low, + assigned[j].pci_size_low); + + break; + } + } + /* + * We are allocating resources for one of three reasons - + * - Fcode wants a larger address space + * - Fcode has set changed/set n, p, t bits. + * - It is a new "reg", it should be only ROM bar, but + * we don't do the checking. + */ + if (alloc == B_TRUE) { + DEBUG1("pcicfg_alloc_new_resources : creating 0x%x\n", + reg[i].pci_phys_hi); + + reg[i].pci_size_low = alloc_size; + if (pcicfg_alloc_resource(dip, reg[i])) { + /* + * Dont forget to free mem from + * ddi_getlongprop + */ + if (acount != 0) + kmem_free((caddr_t)assigned, assigned_len); + kmem_free((caddr_t)reg, reg_len); + return (PCICFG_FAILURE); + } + } + } + + /* + * Don't forget to free up memory from ddi_getlongprop + */ + if (acount != 0) + kmem_free((caddr_t)assigned, assigned_len); + kmem_free((caddr_t)reg, reg_len); + + return (PCICFG_SUCCESS); +} +static int +pcicfg_alloc_resource(dev_info_t *dip, pci_regspec_t phys_spec) +{ + uint64_t answer; + uint64_t alen; + int offset; + pci_regspec_t config; + caddr_t virt, v; + ddi_device_acc_attr_t acc; + ddi_acc_handle_t h; + ndi_ra_request_t request; + pci_regspec_t *assigned; + int assigned_len, entries, i; + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned, + &assigned_len) == DDI_PROP_SUCCESS) { + DEBUG0("pcicfg_alloc_resource - " + "searching assigned-addresses\n"); + + entries = assigned_len / (sizeof (pci_regspec_t)); + + /* + * Walk through the assigned-addresses entries. If there is + * a match, there is no need to allocate the resource. + */ + for (i = 0; i < entries; i++) { + if (assigned[i].pci_phys_hi == phys_spec.pci_phys_hi) { + DEBUG1("pcicfg_alloc_resource - MATCH %x\n", + assigned[i].pci_phys_hi); + kmem_free(assigned, assigned_len); + return (0); + } + } + kmem_free(assigned, assigned_len); + } + + bzero((caddr_t)&request, sizeof (ndi_ra_request_t)); + + config.pci_phys_hi = PCI_CONF_ADDR_MASK & phys_spec.pci_phys_hi; + config.pci_phys_hi &= ~PCI_REG_REG_M; + config.pci_phys_mid = config.pci_phys_low = 0; + config.pci_size_hi = config.pci_size_low = 0; + + /* + * Map in configuration space (temporarily) + */ + acc.devacc_attr_version = DDI_DEVICE_ATTR_V0; + acc.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; + acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC; + + if (pcicfg_map_phys(dip, &config, &virt, &acc, &h)) { + DEBUG0("Can not map in config space\n"); + return (1); + } + + request.ra_flags |= NDI_RA_ALIGN_SIZE; + request.ra_boundbase = 0; + request.ra_boundlen = PCICFG_4GIG_LIMIT; + /* + * Use size stored in phys_spec parameter. + */ + request.ra_len = phys_spec.pci_size_low; + + offset = PCI_REG_REG_G(phys_spec.pci_phys_hi); + + v = virt + offset; + + if (PCI_REG_REG_G(phys_spec.pci_phys_hi) == PCI_CONF_ROM) { + + request.ra_flags ^= NDI_RA_ALLOC_BOUNDED; + + /* allocate memory space from the allocator */ + + if (ndi_ra_alloc(ddi_get_parent(dip), + &request, &answer, &alen, + NDI_RA_TYPE_MEM, NDI_RA_PASS) + != NDI_SUCCESS) { + DEBUG0("(ROM)Failed to allocate 32b mem"); + pcicfg_unmap_phys(&h, &config); + return (1); + } + DEBUG3("ROM addr = [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(answer), + PCICFG_LOADDR(answer), + alen); + + /* program the low word */ + + ddi_put32(h, (uint32_t *)v, (uint32_t)PCICFG_LOADDR(answer)); + + phys_spec.pci_phys_low = PCICFG_LOADDR(answer); + phys_spec.pci_phys_mid = PCICFG_HIADDR(answer); + } else { + + switch (PCI_REG_ADDR_G(phys_spec.pci_phys_hi)) { + case PCI_REG_ADDR_G(PCI_ADDR_MEM64): + request.ra_flags ^= NDI_RA_ALLOC_BOUNDED; + /* allocate memory space from the allocator */ + if (ndi_ra_alloc(ddi_get_parent(dip), + &request, &answer, &alen, + NDI_RA_TYPE_MEM, NDI_RA_PASS) + != NDI_SUCCESS) { + DEBUG0("Failed to allocate 64b mem\n"); + pcicfg_unmap_phys(&h, &config); + return (1); + } + DEBUG3("64 addr = [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(answer), + PCICFG_LOADDR(answer), + alen); + + /* program the low word */ + + ddi_put32(h, (uint32_t *)v, + (uint32_t)PCICFG_LOADDR(answer)); + + /* program the high word with value zero */ + v += 4; + ddi_put32(h, (uint32_t *)v, + (uint32_t)PCICFG_HIADDR(answer)); + + phys_spec.pci_phys_low = PCICFG_LOADDR(answer); + phys_spec.pci_phys_mid = PCICFG_HIADDR(answer); + + break; + + case PCI_REG_ADDR_G(PCI_ADDR_MEM32): + request.ra_flags |= NDI_RA_ALLOC_BOUNDED; + /* allocate memory space from the allocator */ + if (ndi_ra_alloc(ddi_get_parent(dip), + &request, &answer, &alen, + NDI_RA_TYPE_MEM, NDI_RA_PASS) + != NDI_SUCCESS) { + DEBUG0("Failed to allocate 32b mem\n"); + pcicfg_unmap_phys(&h, &config); + return (1); + } + + DEBUG3("32 addr = [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(answer), + PCICFG_LOADDR(answer), + alen); + + /* program the low word */ + + ddi_put32(h, (uint32_t *)v, + (uint32_t)PCICFG_LOADDR(answer)); + + phys_spec.pci_phys_low = PCICFG_LOADDR(answer); + + break; + case PCI_REG_ADDR_G(PCI_ADDR_IO): + /* allocate I/O space from the allocator */ + request.ra_flags |= NDI_RA_ALLOC_BOUNDED; + if (ndi_ra_alloc(ddi_get_parent(dip), + &request, &answer, &alen, + NDI_RA_TYPE_IO, NDI_RA_PASS) + != NDI_SUCCESS) { + DEBUG0("Failed to allocate I/O\n"); + pcicfg_unmap_phys(&h, &config); + return (1); + } + DEBUG3("I/O addr = [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(answer), + PCICFG_LOADDR(answer), + alen); + + ddi_put32(h, (uint32_t *)v, + (uint32_t)PCICFG_LOADDR(answer)); + + phys_spec.pci_phys_low = PCICFG_LOADDR(answer); + + break; + default: + DEBUG0("Unknown register type\n"); + pcicfg_unmap_phys(&h, &config); + return (1); + } /* switch */ + } + + /* + * Now that memory locations are assigned, + * update the assigned address property. + */ + + DEBUG1("updating assigned-addresss for %x\n", phys_spec.pci_phys_hi); + + if (pcicfg_update_assigned_prop(dip, &phys_spec)) { + pcicfg_unmap_phys(&h, &config); + return (1); + } + + pcicfg_unmap_phys(&h, &config); + + return (0); +} + +static int +pcicfg_free_resource(dev_info_t *dip, pci_regspec_t phys_spec) +{ + int offset; + pci_regspec_t config; + caddr_t virt, v; + ddi_device_acc_attr_t acc; + ddi_acc_handle_t h; + ndi_ra_request_t request; + int l; + + bzero((caddr_t)&request, sizeof (ndi_ra_request_t)); + + config.pci_phys_hi = PCI_CONF_ADDR_MASK & phys_spec.pci_phys_hi; + config.pci_phys_hi &= ~PCI_REG_REG_M; + config.pci_phys_mid = config.pci_phys_low = 0; + config.pci_size_hi = config.pci_size_low = 0; + + /* + * Map in configuration space (temporarily) + */ + acc.devacc_attr_version = DDI_DEVICE_ATTR_V0; + acc.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; + acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC; + + if (pcicfg_map_phys(dip, &config, &virt, &acc, &h)) { + DEBUG0("Can not map in config space\n"); + return (1); + } + + offset = PCI_REG_REG_G(phys_spec.pci_phys_hi); + + v = virt + offset; + + /* + * Use size stored in phys_spec parameter. + */ + l = phys_spec.pci_size_low; + + if (PCI_REG_REG_G(phys_spec.pci_phys_hi) == PCI_CONF_ROM) { + + /* free memory back to the allocator */ + if (ndi_ra_free(ddi_get_parent(dip), phys_spec.pci_phys_low, + l, NDI_RA_TYPE_MEM, NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("(ROM)Can not free 32b mem"); + pcicfg_unmap_phys(&h, &config); + return (1); + } + + /* Unmap the BAR by writing a zero */ + + ddi_put32(h, (uint32_t *)v, (uint32_t)0); + } else { + + switch (PCI_REG_ADDR_G(phys_spec.pci_phys_hi)) { + case PCI_REG_ADDR_G(PCI_ADDR_MEM64): + /* free memory back to the allocator */ + if (ndi_ra_free(ddi_get_parent(dip), + PCICFG_LADDR(phys_spec.pci_phys_low, + phys_spec.pci_phys_mid), + l, NDI_RA_TYPE_MEM, + NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("Can not free 64b mem"); + pcicfg_unmap_phys(&h, &config); + return (1); + } + + break; + + case PCI_REG_ADDR_G(PCI_ADDR_MEM32): + /* free memory back to the allocator */ + if (ndi_ra_free(ddi_get_parent(dip), + phys_spec.pci_phys_low, + l, NDI_RA_TYPE_MEM, + NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("Can not free 32b mem"); + pcicfg_unmap_phys(&h, &config); + return (1); + } + + break; + case PCI_REG_ADDR_G(PCI_ADDR_IO): + /* free I/O space back to the allocator */ + if (ndi_ra_free(ddi_get_parent(dip), + phys_spec.pci_phys_low, + l, NDI_RA_TYPE_IO, + NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("Can not free I/O space"); + pcicfg_unmap_phys(&h, &config); + return (1); + } + + break; + default: + DEBUG0("Unknown register type\n"); + pcicfg_unmap_phys(&h, &config); + return (1); + } /* switch */ + } + + /* + * Now that memory locations are assigned, + * update the assigned address property. + */ + + DEBUG1("updating assigned-addresss for %x\n", phys_spec.pci_phys_hi); + + if (pcicfg_remove_assigned_prop(dip, &phys_spec)) { + pcicfg_unmap_phys(&h, &config); + return (1); + } + + pcicfg_unmap_phys(&h, &config); + + return (0); +} + +static int +pcicfg_remove_assigned_prop(dev_info_t *dip, pci_regspec_t *oldone) +{ + int alen, num_entries, i; + pci_regspec_t *assigned, *assigned_copy; + uint_t status; + + status = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "assigned-addresses", (caddr_t)&assigned, &alen); + switch (status) { + case DDI_PROP_SUCCESS: + break; + case DDI_PROP_NO_MEMORY: + DEBUG0("no memory for assigned-addresses property\n"); + return (1); + default: + DEBUG0("assigned-addresses property does not exist\n"); + return (0); + } + + /* + * Make a copy of old assigned-addresses property. + */ + assigned_copy = kmem_alloc(alen, KM_SLEEP); + bcopy(assigned, assigned_copy, alen); + + status = ndi_prop_remove(DDI_DEV_T_NONE, dip, "assigned-addresses"); + + if (status != DDI_PROP_SUCCESS) { + /* + * If "assigned-addresses" is retrieved from PROM, the + * ndi_prop_remove() will fail. + */ + DEBUG1("pcicfg_remove_assigned_prop: 0x%x not removed\n", + oldone->pci_phys_hi); + + /* + * Free up allocated memory + */ + kmem_free(assigned_copy, alen); + kmem_free((caddr_t)assigned, alen); + + return (0); + } + + num_entries = alen / sizeof (pci_regspec_t); + + /* + * Rebuild the assigned-addresses property. + */ + for (i = 0; i < num_entries; i++) { + if (assigned_copy[i].pci_phys_hi != oldone->pci_phys_hi) { + (void) pcicfg_update_assigned_prop(dip, + &assigned_copy[i]); + } + } + + /* + * Free the copy of the original assigned-addresses. + */ + kmem_free(assigned_copy, alen); + + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)assigned, alen); + + return (0); +} + +static int +pcicfg_map_phys(dev_info_t *dip, pci_regspec_t *phys_spec, + caddr_t *addrp, ddi_device_acc_attr_t *accattrp, + ddi_acc_handle_t *handlep) +{ + ddi_map_req_t mr; + ddi_acc_hdl_t *hp; + int result; + + *handlep = impl_acc_hdl_alloc(KM_SLEEP, NULL); + hp = impl_acc_hdl_get(*handlep); + hp->ah_vers = VERS_ACCHDL; + hp->ah_dip = dip; + hp->ah_rnumber = 0; + hp->ah_offset = 0; + hp->ah_len = 0; + hp->ah_acc = *accattrp; + + mr.map_op = DDI_MO_MAP_LOCKED; + mr.map_type = DDI_MT_REGSPEC; + mr.map_obj.rp = (struct regspec *)phys_spec; + mr.map_prot = PROT_READ | PROT_WRITE; + mr.map_flags = DDI_MF_KERNEL_MAPPING; + mr.map_handlep = hp; + mr.map_vers = DDI_MAP_VERSION; + + result = ddi_map(dip, &mr, 0, 0, addrp); + + if (result != DDI_SUCCESS) { + impl_acc_hdl_free(*handlep); + *handlep = (ddi_acc_handle_t)NULL; + } else { + hp->ah_addr = *addrp; + } + + return (result); +} + +void +pcicfg_unmap_phys(ddi_acc_handle_t *handlep, pci_regspec_t *ph) +{ + ddi_map_req_t mr; + ddi_acc_hdl_t *hp; + + hp = impl_acc_hdl_get(*handlep); + ASSERT(hp); + + mr.map_op = DDI_MO_UNMAP; + mr.map_type = DDI_MT_REGSPEC; + mr.map_obj.rp = (struct regspec *)ph; + mr.map_prot = PROT_READ | PROT_WRITE; + mr.map_flags = DDI_MF_KERNEL_MAPPING; + mr.map_handlep = hp; + mr.map_vers = DDI_MAP_VERSION; + + (void) ddi_map(hp->ah_dip, &mr, hp->ah_offset, + hp->ah_len, &hp->ah_addr); + + impl_acc_hdl_free(*handlep); + *handlep = (ddi_acc_handle_t)NULL; +} +#ifdef DEBUG +static void +debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3, + uintptr_t a4, uintptr_t a5) +{ + if (pcicfg_debug == 1) { + prom_printf("pcicfg: "); + prom_printf(fmt, a1, a2, a3, a4, a5); + } else + if (pcicfg_debug) + cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5); +} +#endif |
