1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26/*
27 *	Library file that has code for PCIe booting
28 */
29
30#include <sys/conf.h>
31#include <sys/pci.h>
32#include <sys/sunndi.h>
33#include <sys/pcie.h>
34#include <sys/pcie_impl.h>
35#include <sys/pci_cfgspace.h>
36#include <io/pciex/pcie_nvidia.h>
37
38/*
39 * PCI Configuration (Nvidia chipsets, PCIe) related library functions
40 */
41
42/* Globals */
43extern int pci_boot_debug;
44
45extern uint64_t mcfg_mem_base;
46
47boolean_t
48check_if_device_is_pciex(dev_info_t *cdip, uchar_t bus, uchar_t dev,
49    uchar_t func, boolean_t *slot_valid, ushort_t *slot_number,
50    ushort_t *is_pci_bridge)
51{
52	boolean_t found_pciex = B_FALSE;
53	ushort_t cap;
54	ushort_t capsp;
55	ushort_t cap_count = PCI_CAP_MAX_PTR;
56	ushort_t status;
57	uint32_t slot_cap;
58
59	*slot_valid = B_FALSE;
60
61	status = (*pci_getw_func)(bus, dev, func, PCI_CONF_STAT);
62	if (!(status & PCI_STAT_CAP))
63		return (B_FALSE);
64
65	capsp = (*pci_getb_func)(bus, dev, func, PCI_CONF_CAP_PTR);
66	while (cap_count-- && capsp >= PCI_CAP_PTR_OFF) {
67		capsp &= PCI_CAP_PTR_MASK;
68		cap = (*pci_getb_func)(bus, dev, func, capsp);
69
70		if (cap == PCI_CAP_ID_PCI_E) {
71#ifdef	DEBUG
72			if (pci_boot_debug)
73				cmn_err(CE_CONT, "PCI-Express (%x,%x,%x) "
74				    "capability found\n", bus, dev, func);
75#endif	/* DEBUG */
76
77			status = (*pci_getw_func)(bus, dev, func, capsp + 2);
78			/*
79			 * See section 7.8.2 of PCI-Express Base Spec v1.0a
80			 * for Device/Port Type.
81			 * PCIE_PCIECAP_DEV_TYPE_PCIE2PCI implies that the
82			 * device is a PCIe2PCI bridge
83			 */
84			*is_pci_bridge =
85			    ((status & PCIE_PCIECAP_DEV_TYPE_MASK) ==
86			    PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) ? 1 : 0;
87
88			/*
89			 * Check for "Slot  Implemented" bit
90			 * PCIE_PCIECAP_SLOT_IMPL implies that.
91			 */
92			if (status & PCIE_PCIECAP_SLOT_IMPL) {
93				/* offset 14h is Slot Cap Register */
94				slot_cap = (*pci_getl_func)(bus, dev, func,
95				    capsp + PCIE_SLOTCAP);
96				*slot_valid = B_TRUE;
97				*slot_number =
98				    PCIE_SLOTCAP_PHY_SLOT_NUM(slot_cap);
99
100				/* Is PCI Express HotPlug capability set? */
101				if (cdip &&
102				    (slot_cap & PCIE_SLOTCAP_HP_CAPABLE)) {
103					(void) ndi_prop_update_int(
104					    DDI_DEV_T_NONE, cdip,
105					    "pci-hotplug-type",
106					    INBAND_HPC_PCIE);
107				}
108			}
109
110			found_pciex = B_TRUE;
111		}
112
113		if (cdip && (cap == PCI_CAP_ID_PCI_HOTPLUG)) {
114			(void) ndi_prop_update_int(DDI_DEV_T_NONE, cdip,
115			    "pci-hotplug-type", INBAND_HPC_SHPC);
116		}
117
118		capsp = (*pci_getb_func)(bus, dev, func,
119		    capsp + PCI_CAP_NEXT_PTR);
120	}
121
122	return (found_pciex);
123}
124
125
126/*
127 * scan all buses, devices, functions to look for any
128 * PCI-Express device in the system.
129 * If found, return B_TRUE else B_FALSE
130 */
131boolean_t
132look_for_any_pciex_device(uchar_t bus)
133{
134	uchar_t dev, func;
135	uchar_t nfunc, header;
136	ushort_t venid, slot_num, is_pci_bridge = 0;
137	boolean_t slot_valid;
138
139	for (dev = 0; dev < 32; dev++) {
140		nfunc = 1;
141		for (func = 0; func < nfunc; func++) {
142#ifdef	DEBUG
143			if (pci_boot_debug)
144				cmn_err(CE_NOTE, "pciex dev 0x%x, func 0x%x",
145				    dev, func);
146#endif	/* DEBUG */
147
148			venid = (*pci_getw_func)(bus, dev, func,
149			    PCI_CONF_VENID);
150			/* no function at this address */
151			if ((venid == 0xffff) || (venid == 0))
152				continue;
153
154			header = (*pci_getb_func)(bus, dev, func,
155			    PCI_CONF_HEADER);
156			if (header == 0xff)
157				continue; /* illegal value */
158
159			/*
160			 * according to some mail from Microsoft posted to
161			 * the pci-drivers alias, their only requirement for
162			 * a multifunction device is for the 1st function to
163			 * have to PCI_HEADER_MULTI bit set.
164			 */
165			if ((func == 0) && (header & PCI_HEADER_MULTI))
166				nfunc = 8;
167
168			if (check_if_device_is_pciex(NULL, bus, dev, func,
169			    &slot_valid, &slot_num, &is_pci_bridge) == B_TRUE)
170				return (B_TRUE);
171		} /* end of func */
172	} /* end of dev */
173
174	return (B_FALSE);
175}
176
177boolean_t
178create_pcie_root_bus(uchar_t bus, dev_info_t *dip)
179{
180	pcie_bus_t *bus_p;
181
182	/*
183	 * Currently this is being hard-coded.
184	 * We need to figure out if the root bus does indeed
185	 * have PCI-Ex in the path by looking for MCFG in
186	 * the ACPI tables
187	 */
188	if (look_for_any_pciex_device(bus) == B_FALSE)
189		return (B_FALSE);
190
191#ifdef	DEBUG
192	if (pci_boot_debug)
193		cmn_err(CE_CONT, "Found PCI-Ex in the system\n");
194#endif	/* DEBUG */
195	(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
196	    "device_type", "pciex");
197	(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
198	    "compatible", "pciex_root_complex");
199
200	pcie_rc_init_bus(dip);
201
202	/* save base addr in bus_t for pci_cfgacc_xxx() */
203	bus_p = PCIE_DIP2BUS(dip);
204	bus_p->bus_cfgacc_base = mcfg_mem_base;
205
206	return (B_TRUE);
207}
208
209
210/*
211 * add_nvidia_isa_bridge_props():
212 *	To enable native hotplug; we need to map in two I/O BARs
213 *	from ISA bridge's config space
214 *
215 * NOTE: For now, this function is only used for Nvidia's CrushK 8-04 chipsets.
216 */
217void
218add_nvidia_isa_bridge_props(dev_info_t *dip, uchar_t bus, uchar_t dev,
219    uchar_t func)
220{
221	uint_t devloc, base;
222	pci_regspec_t regs[2] = {{0}};
223	pci_regspec_t assigned[2] = {{0}};
224
225	devloc = (uint_t)bus << PCI_REG_BUS_SHIFT |
226	    (uint_t)dev << PCI_REG_DEV_SHIFT |
227	    (uint_t)func << PCI_REG_FUNC_SHIFT;
228	regs[0].pci_phys_hi = devloc;
229
230	/* System Control BAR i/o space */
231	base = (*pci_getl_func)(bus, dev, func,
232	    NVIDIA_CK804_ISA_SYSCTRL_BAR_OFF);
233	regs[0].pci_size_low = assigned[0].pci_size_low = PCI_CONF_HDR_SIZE;
234	assigned[0].pci_phys_hi = regs[0].pci_phys_hi = (PCI_RELOCAT_B |
235	    PCI_ADDR_IO | devloc | NVIDIA_CK804_ISA_SYSCTRL_BAR_OFF);
236	assigned[0].pci_phys_low = regs[0].pci_phys_low =
237	    base & PCI_BASE_IO_ADDR_M;
238
239	/* Analog BAR i/o space */
240	base = (*pci_getl_func)(bus, dev, func,
241	    NVIDIA_CK804_ISA_ANALOG_BAR_OFF);
242	regs[1].pci_size_low = assigned[1].pci_size_low = PCI_CONF_HDR_SIZE;
243	assigned[1].pci_phys_hi = regs[1].pci_phys_hi = (PCI_RELOCAT_B |
244	    PCI_ADDR_IO | devloc | NVIDIA_CK804_ISA_ANALOG_BAR_OFF);
245	assigned[1].pci_phys_low = regs[1].pci_phys_low =
246	    base & PCI_BASE_IO_ADDR_M;
247
248	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "reg",
249	    (int *)regs, 2 * sizeof (pci_regspec_t) / sizeof (int));
250	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
251	    "assigned-addresses",
252	    (int *)assigned, 2 * sizeof (pci_regspec_t) / sizeof (int));
253}
254