xref: /illumos-gate/usr/src/uts/intel/io/pciex/pcie_acpi.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 #include <sys/sysmacros.h>
26 #include <sys/types.h>
27 #include <sys/kmem.h>
28 #include <sys/ddi.h>
29 #include <sys/sunddi.h>
30 #include <sys/sunndi.h>
31 #include <sys/promif.h>
32 #include <sys/pcie.h>
33 #include <sys/pci_cap.h>
34 #include <sys/pcie_impl.h>
35 #include <sys/pcie_acpi.h>
36 #include <sys/acpi/acpi.h>
37 #include <sys/acpica.h>
38 
39 ACPI_STATUS pcie_acpi_eval_osc(dev_info_t *dip, ACPI_HANDLE osc_hdl,
40 	uint32_t *osc_flags);
41 static ACPI_STATUS pcie_acpi_find_osc(ACPI_HANDLE busobj,
42 	ACPI_HANDLE *osc_hdlp);
43 
44 #ifdef DEBUG
45 static void pcie_dump_acpi_obj(ACPI_HANDLE pcibus_obj);
46 static ACPI_STATUS pcie_walk_obj_namespace(ACPI_HANDLE hdl, uint32_t nl,
47 	void *context, void **ret);
48 static ACPI_STATUS pcie_print_acpi_name(ACPI_HANDLE hdl, uint32_t nl,
49 	void *context, void **ret);
50 #endif /* DEBUG */
51 
52 void
53 pcie_init_plat(dev_info_t *dip)
54 {
55 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
56 	bus_p->bus_plat_private =
57 	    (pcie_x86_priv_t *)kmem_zalloc(sizeof (pcie_x86_priv_t), KM_SLEEP);
58 }
59 
60 void
61 pcie_fini_plat(dev_info_t *dip)
62 {
63 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
64 
65 	kmem_free(bus_p->bus_plat_private, sizeof (pcie_x86_priv_t));
66 }
67 
68 int
69 pcie_acpi_osc(dev_info_t *dip, uint32_t *osc_flags)
70 {
71 	ACPI_HANDLE pcibus_obj;
72 	int status = AE_ERROR;
73 	ACPI_HANDLE osc_hdl;
74 	pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
75 	pcie_x86_priv_t *osc_p = (pcie_x86_priv_t *)bus_p->bus_plat_private;
76 
77 	/* Mark this so we know _OSC has been called for this device */
78 	osc_p->bus_osc = B_TRUE;
79 
80 	/*
81 	 * (1)  Find the ACPI device node for this bus node.
82 	 */
83 	status = acpica_get_handle(dip, &pcibus_obj);
84 	if (status != AE_OK) {
85 		PCIE_DBG("No ACPI device found (dip %p)\n", (void *)dip);
86 		return (DDI_FAILURE);
87 	}
88 
89 	/*
90 	 * (2)	Check if _OSC method is present.
91 	 */
92 	if (pcie_acpi_find_osc(pcibus_obj, &osc_hdl) != AE_OK) {
93 		/* no _OSC method present */
94 		PCIE_DBG("no _OSC method present for dip %p\n",
95 		    (void *)dip);
96 		return (DDI_FAILURE);
97 	}
98 
99 	/*
100 	 * (3)	_OSC method exists; evaluate _OSC.
101 	 */
102 	if (pcie_acpi_eval_osc(dip, osc_hdl, osc_flags) != AE_OK) {
103 		PCIE_DBG("Failed to evaluate _OSC method for dip 0x%p\n",
104 		    (void *)dip);
105 		return (DDI_FAILURE);
106 	}
107 
108 	osc_p->bus_osc_hp = (*osc_flags & OSC_CONTROL_PCIE_NAT_HP) ?
109 	    B_TRUE : B_FALSE;
110 	osc_p->bus_osc_aer = (*osc_flags & OSC_CONTROL_PCIE_ADV_ERR) ?
111 	    B_TRUE : B_FALSE;
112 
113 #ifdef DEBUG
114 	if (pcie_debug_flags > 1)
115 		pcie_dump_acpi_obj(pcibus_obj);
116 #endif /* DEBUG */
117 
118 	return (DDI_SUCCESS);
119 }
120 
121 static ACPI_STATUS
122 pcie_acpi_find_osc(ACPI_HANDLE busobj, ACPI_HANDLE *osc_hdlp)
123 {
124 	ACPI_HANDLE parentobj = busobj;
125 	ACPI_STATUS status = AE_NOT_FOUND;
126 
127 	*osc_hdlp = NULL;
128 
129 	/*
130 	 * Walk up the ACPI device tree looking for _OSC method.
131 	 */
132 	do {
133 		busobj = parentobj;
134 		if ((status = AcpiGetHandle(busobj, "_OSC", osc_hdlp)) == AE_OK)
135 			break;
136 	} while (AcpiGetParent(busobj, &parentobj) == AE_OK);
137 
138 	if (*osc_hdlp == NULL)
139 		status = AE_NOT_FOUND;
140 
141 	return (status);
142 }
143 
144 /* UUID for for PCI/PCI-X/PCI-Exp hierarchy as defined in PCI fw ver 3.0 */
145 static uint8_t pcie_uuid[16] =
146 	{0x5b, 0x4d, 0xdb, 0x33, 0xf7, 0x1f, 0x1c, 0x40,
147 	0x96, 0x57, 0x74, 0x41, 0xc0, 0x3d, 0xd7, 0x66};
148 
149 /*
150  * Evaluate _OSC method.
151  */
152 ACPI_STATUS
153 pcie_acpi_eval_osc(dev_info_t *dip, ACPI_HANDLE osc_hdl, uint32_t *osc_flags)
154 {
155 	ACPI_STATUS		status;
156 	ACPI_OBJECT_LIST	arglist;
157 	ACPI_OBJECT		args[4];
158 	UINT32			caps_buffer[3];
159 	ACPI_BUFFER		rb;
160 	UINT32			*rbuf;
161 	UINT32			tmp_ctrl;
162 
163 	/* construct argument list */
164 	arglist.Count = 4;
165 	arglist.Pointer = args;
166 
167 	/* arg0 - UUID */
168 	args[0].Type = ACPI_TYPE_BUFFER;
169 	args[0].Buffer.Length = 16; /* size of UUID string */
170 	args[0].Buffer.Pointer = pcie_uuid;
171 
172 	/* arg1 - Revision ID */
173 	args[1].Type = ACPI_TYPE_INTEGER;
174 	args[1].Integer.Value = PCIE_OSC_REVISION_ID;
175 
176 	/* arg2 - Count */
177 	args[2].Type = ACPI_TYPE_INTEGER;
178 	args[2].Integer.Value = 3; /* no. of DWORDS in caps_buffer */
179 
180 	/* arg3 - Capabilities Buffer */
181 	args[3].Type = ACPI_TYPE_BUFFER;
182 	args[3].Buffer.Length = 12;
183 	args[3].Buffer.Pointer = (void *)caps_buffer;
184 
185 	/* Initialize Capabilities Buffer */
186 
187 	/* DWORD1: no query flag set */
188 	caps_buffer[0] = 0;
189 	/* DWORD2: Support Field */
190 	caps_buffer[1] = OSC_SUPPORT_FIELD_INIT;
191 	/* DWORD3: Control Field */
192 	caps_buffer[2] = OSC_CONTROL_FIELD_INIT;
193 
194 	/* If hotplug is supported add the corresponding control fields */
195 	if (*osc_flags & OSC_CONTROL_PCIE_NAT_HP)
196 		caps_buffer[2] |= (OSC_CONTROL_PCIE_NAT_HP |
197 		    OSC_CONTROL_PCIE_NAT_PM);
198 
199 	tmp_ctrl = caps_buffer[2];
200 	rb.Length = ACPI_ALLOCATE_BUFFER;
201 	rb.Pointer = NULL;
202 
203 	status = AcpiEvaluateObjectTyped(osc_hdl, NULL, &arglist, &rb,
204 	    ACPI_TYPE_BUFFER);
205 	if (status != AE_OK) {
206 		PCIE_DBG("Failed to execute _OSC method (status %d)\n",
207 		    status);
208 		return (status);
209 	}
210 
211 	/* LINTED pointer alignment */
212 	rbuf = (UINT32 *)((ACPI_OBJECT *)rb.Pointer)->Buffer.Pointer;
213 
214 	/* check the STATUS word in the capability buffer */
215 	if (rbuf[0] & OSC_STATUS_ERRORS) {
216 		PCIE_DBG("_OSC method failed (STATUS %d)\n", rbuf[0]);
217 		AcpiOsFree(rb.Pointer);
218 		return (AE_ERROR);
219 	}
220 
221 	*osc_flags = rbuf[2];
222 
223 	PCIE_DBG("_OSC method evaluation completed for 0x%p: "
224 	    "STATUS 0x%x SUPPORT 0x%x CONTROL req 0x%x, CONTROL ret 0x%x\n",
225 	    (void *)dip, rbuf[0], rbuf[1], tmp_ctrl, rbuf[2]);
226 
227 	AcpiOsFree(rb.Pointer);
228 
229 	return (AE_OK);
230 }
231 
232 /*
233  * Checks if _OSC method has been called for this device.
234  */
235 boolean_t
236 pcie_is_osc(dev_info_t *dip)
237 {
238 	pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
239 	pcie_x86_priv_t *osc_p = (pcie_x86_priv_t *)bus_p->bus_plat_private;
240 	return (osc_p->bus_osc);
241 }
242 
243 #ifdef DEBUG
244 static void
245 pcie_dump_acpi_obj(ACPI_HANDLE pcibus_obj)
246 {
247 	int status;
248 	ACPI_BUFFER retbuf;
249 
250 	if (pcibus_obj == NULL)
251 		return;
252 
253 	/* print the full path name */
254 	retbuf.Pointer = NULL;
255 	retbuf.Length = ACPI_ALLOCATE_BUFFER;
256 	status = AcpiGetName(pcibus_obj, ACPI_FULL_PATHNAME, &retbuf);
257 	if (status != AE_OK)
258 		return;
259 	PCIE_DBG("PCIE BUS PATHNAME: %s\n", (char *)retbuf.Pointer);
260 	AcpiOsFree(retbuf.Pointer);
261 
262 	/* dump all the methods for this bus node */
263 	PCIE_DBG("  METHODS: \n");
264 	status = AcpiWalkNamespace(ACPI_TYPE_METHOD, pcibus_obj, 1,
265 	    pcie_print_acpi_name, "  ", NULL);
266 	/* dump all the child devices */
267 	status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, pcibus_obj, 1,
268 	    pcie_walk_obj_namespace, NULL, NULL);
269 }
270 
271 /*ARGSUSED*/
272 static ACPI_STATUS
273 pcie_walk_obj_namespace(ACPI_HANDLE hdl, uint32_t nl, void *context,
274 	void **ret)
275 {
276 	int status;
277 	ACPI_BUFFER retbuf;
278 	char buf[32];
279 
280 	/* print the full path name */
281 	retbuf.Pointer = NULL;
282 	retbuf.Length = ACPI_ALLOCATE_BUFFER;
283 	status = AcpiGetName(hdl, ACPI_FULL_PATHNAME, &retbuf);
284 	if (status != AE_OK)
285 		return (status);
286 	buf[0] = 0;
287 	while (nl--)
288 		(void) strcat(buf, "  ");
289 	PCIE_DBG("%sDEVICE: %s\n", buf, (char *)retbuf.Pointer);
290 	AcpiOsFree(retbuf.Pointer);
291 
292 	/* dump all the methods for this device */
293 	PCIE_DBG("%s  METHODS: \n", buf);
294 	status = AcpiWalkNamespace(ACPI_TYPE_METHOD, hdl, 1,
295 	    pcie_print_acpi_name, (void *)buf, NULL);
296 	return (status);
297 }
298 
299 /*ARGSUSED*/
300 static ACPI_STATUS
301 pcie_print_acpi_name(ACPI_HANDLE hdl, uint32_t nl, void *context, void **ret)
302 {
303 	int status;
304 	ACPI_BUFFER retbuf;
305 	char name[16];
306 
307 	retbuf.Pointer = name;
308 	retbuf.Length = 16;
309 	status = AcpiGetName(hdl, ACPI_SINGLE_NAME, &retbuf);
310 	if (status == AE_OK)
311 		PCIE_DBG("%s    %s \n", (char *)context, name);
312 	return (AE_OK);
313 }
314 #endif /* DEBUG */
315