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