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