1a3114836SGerry Liu /*
2a3114836SGerry Liu * CDDL HEADER START
3a3114836SGerry Liu *
4a3114836SGerry Liu * The contents of this file are subject to the terms of the
5a3114836SGerry Liu * Common Development and Distribution License (the "License").
6a3114836SGerry Liu * You may not use this file except in compliance with the License.
7a3114836SGerry Liu *
8a3114836SGerry Liu * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9a3114836SGerry Liu * or http://www.opensolaris.org/os/licensing.
10a3114836SGerry Liu * See the License for the specific language governing permissions
11a3114836SGerry Liu * and limitations under the License.
12a3114836SGerry Liu *
13a3114836SGerry Liu * When distributing Covered Code, include this CDDL HEADER in each
14a3114836SGerry Liu * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15a3114836SGerry Liu * If applicable, add the following below this CDDL HEADER, with the
16a3114836SGerry Liu * fields enclosed by brackets "[]" replaced with your own identifying
17a3114836SGerry Liu * information: Portions Copyright [yyyy] [name of copyright owner]
18a3114836SGerry Liu *
19a3114836SGerry Liu * CDDL HEADER END
20a3114836SGerry Liu */
21a3114836SGerry Liu
22a3114836SGerry Liu /*
23*26f3cdf0SGordon Ross * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
24a3114836SGerry Liu * Copyright (c) 2010, Intel Corporation.
25a3114836SGerry Liu * All rights reserved.
26a3114836SGerry Liu */
27a3114836SGerry Liu
28a3114836SGerry Liu #include <sys/types.h>
29a3114836SGerry Liu #include <sys/atomic.h>
30a3114836SGerry Liu #include <sys/bitmap.h>
31a3114836SGerry Liu #include <sys/cmn_err.h>
32a3114836SGerry Liu #include <sys/note.h>
33a3114836SGerry Liu #include <sys/sunndi.h>
34a3114836SGerry Liu #include <sys/fastboot_impl.h>
35a3114836SGerry Liu #include <sys/sysevent.h>
36a3114836SGerry Liu #include <sys/sysevent/dr.h>
37a3114836SGerry Liu #include <sys/sysevent/eventdefs.h>
38a3114836SGerry Liu #include <sys/acpi/acpi.h>
39a3114836SGerry Liu #include <sys/acpica.h>
40a3114836SGerry Liu #include <sys/acpidev.h>
41a3114836SGerry Liu #include <sys/acpidev_dr.h>
42a3114836SGerry Liu #include <sys/acpinex.h>
43a3114836SGerry Liu
44a3114836SGerry Liu int acpinex_event_support_remove = 0;
45a3114836SGerry Liu
46a3114836SGerry Liu static volatile uint_t acpinex_dr_event_cnt = 0;
47a3114836SGerry Liu static ulong_t acpinex_object_type_mask[BT_BITOUL(ACPI_TYPE_NS_NODE_MAX + 1)];
48a3114836SGerry Liu
49a3114836SGerry Liu /*
50a3114836SGerry Liu * Generate DR_REQ event to syseventd.
51a3114836SGerry Liu * Please refer to sys/sysevent/dr.h for message definition.
52a3114836SGerry Liu */
53a3114836SGerry Liu static int
acpinex_event_generate_event(dev_info_t * dip,ACPI_HANDLE hdl,int req,int event,char * objname)54a3114836SGerry Liu acpinex_event_generate_event(dev_info_t *dip, ACPI_HANDLE hdl, int req,
55a3114836SGerry Liu int event, char *objname)
56a3114836SGerry Liu {
57a3114836SGerry Liu int rv = 0;
58a3114836SGerry Liu sysevent_id_t eid;
59a3114836SGerry Liu sysevent_value_t evnt_val;
60a3114836SGerry Liu sysevent_attr_list_t *evnt_attr_list = NULL;
61a3114836SGerry Liu char *attach_pnt;
62a3114836SGerry Liu char event_type[32];
63a3114836SGerry Liu
64a3114836SGerry Liu /* Add "attachment point" attribute. */
65a3114836SGerry Liu attach_pnt = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
66a3114836SGerry Liu if (ACPI_FAILURE(acpidev_dr_get_attachment_point(hdl,
67a3114836SGerry Liu attach_pnt, MAXPATHLEN))) {
68a3114836SGerry Liu cmn_err(CE_WARN,
69a3114836SGerry Liu "!acpinex: failed to generate AP name for %s.", objname);
70a3114836SGerry Liu kmem_free(attach_pnt, MAXPATHLEN);
71a3114836SGerry Liu return (-1);
72a3114836SGerry Liu }
73a3114836SGerry Liu ASSERT(attach_pnt[0] != '\0');
74a3114836SGerry Liu evnt_val.value_type = SE_DATA_TYPE_STRING;
75a3114836SGerry Liu evnt_val.value.sv_string = attach_pnt;
76a3114836SGerry Liu rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val, KM_SLEEP);
77a3114836SGerry Liu if (rv != 0) {
78a3114836SGerry Liu cmn_err(CE_WARN,
79a3114836SGerry Liu "!acpinex: failed to add attr [%s] for %s event.",
80a3114836SGerry Liu DR_AP_ID, EC_DR);
81a3114836SGerry Liu kmem_free(attach_pnt, MAXPATHLEN);
82a3114836SGerry Liu return (rv);
83a3114836SGerry Liu }
84a3114836SGerry Liu
85a3114836SGerry Liu /* Add "request type" attribute. */
86a3114836SGerry Liu evnt_val.value_type = SE_DATA_TYPE_STRING;
87a3114836SGerry Liu evnt_val.value.sv_string = SE_REQ2STR(req);
88a3114836SGerry Liu rv = sysevent_add_attr(&evnt_attr_list, DR_REQ_TYPE, &evnt_val,
89a3114836SGerry Liu KM_SLEEP);
90a3114836SGerry Liu if (rv != 0) {
91a3114836SGerry Liu cmn_err(CE_WARN,
92a3114836SGerry Liu "!acpinex: failed to add attr [%s] for %s event.",
93a3114836SGerry Liu DR_REQ_TYPE, EC_DR);
94a3114836SGerry Liu sysevent_free_attr(evnt_attr_list);
95a3114836SGerry Liu kmem_free(attach_pnt, MAXPATHLEN);
96a3114836SGerry Liu return (rv);
97a3114836SGerry Liu }
98a3114836SGerry Liu
99a3114836SGerry Liu /* Add "acpi-event-type" attribute. */
100a3114836SGerry Liu switch (event) {
101a3114836SGerry Liu case ACPI_NOTIFY_BUS_CHECK:
102a3114836SGerry Liu (void) snprintf(event_type, sizeof (event_type),
103a3114836SGerry Liu ACPIDEV_EVENT_TYPE_BUS_CHECK);
104a3114836SGerry Liu break;
105a3114836SGerry Liu case ACPI_NOTIFY_DEVICE_CHECK:
106a3114836SGerry Liu (void) snprintf(event_type, sizeof (event_type),
107a3114836SGerry Liu ACPIDEV_EVENT_TYPE_DEVICE_CHECK);
108a3114836SGerry Liu break;
109a3114836SGerry Liu case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
110a3114836SGerry Liu (void) snprintf(event_type, sizeof (event_type),
111a3114836SGerry Liu ACPIDEV_EVENT_TYPE_DEVICE_CHECK_LIGHT);
112a3114836SGerry Liu break;
113a3114836SGerry Liu case ACPI_NOTIFY_EJECT_REQUEST:
114a3114836SGerry Liu (void) snprintf(event_type, sizeof (event_type),
115a3114836SGerry Liu ACPIDEV_EVENT_TYPE_EJECT_REQUEST);
116a3114836SGerry Liu break;
117a3114836SGerry Liu default:
118a3114836SGerry Liu cmn_err(CE_WARN,
119a3114836SGerry Liu "!acpinex: unknown ACPI event type %d.", event);
120a3114836SGerry Liu sysevent_free_attr(evnt_attr_list);
121a3114836SGerry Liu kmem_free(attach_pnt, MAXPATHLEN);
122a3114836SGerry Liu return (-1);
123a3114836SGerry Liu }
124a3114836SGerry Liu evnt_val.value_type = SE_DATA_TYPE_STRING;
125a3114836SGerry Liu evnt_val.value.sv_string = event_type;
126a3114836SGerry Liu rv = sysevent_add_attr(&evnt_attr_list, ACPIDEV_EVENT_TYPE_ATTR_NAME,
127a3114836SGerry Liu &evnt_val, KM_SLEEP);
128a3114836SGerry Liu if (rv != 0) {
129a3114836SGerry Liu cmn_err(CE_WARN,
130a3114836SGerry Liu "!acpinex: failed to add attr [%s] for %s event.",
131a3114836SGerry Liu ACPIDEV_EVENT_TYPE_ATTR_NAME, EC_DR);
132a3114836SGerry Liu sysevent_free_attr(evnt_attr_list);
133a3114836SGerry Liu kmem_free(attach_pnt, MAXPATHLEN);
134a3114836SGerry Liu return (rv);
135a3114836SGerry Liu }
136a3114836SGerry Liu
137a3114836SGerry Liu rv = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, EC_DR, ESC_DR_REQ,
138a3114836SGerry Liu evnt_attr_list, &eid, KM_SLEEP);
139a3114836SGerry Liu if (rv != DDI_SUCCESS) {
140a3114836SGerry Liu cmn_err(CE_WARN,
141a3114836SGerry Liu "!acpinex: failed to log DR_REQ event for %s.", objname);
142a3114836SGerry Liu rv = -1;
143a3114836SGerry Liu }
144a3114836SGerry Liu
145a3114836SGerry Liu nvlist_free(evnt_attr_list);
146a3114836SGerry Liu kmem_free(attach_pnt, MAXPATHLEN);
147a3114836SGerry Liu
148a3114836SGerry Liu return (rv);
149a3114836SGerry Liu }
150a3114836SGerry Liu
151a3114836SGerry Liu /*
152a3114836SGerry Liu * Event handler for ACPI EJECT_REQUEST notifications.
153a3114836SGerry Liu * EJECT_REQUEST notifications should be generated on the device to be ejected,
154a3114836SGerry Liu * so no need to scan subtree of it.
155a3114836SGerry Liu * It also invokes ACPI _OST method to update event status if call_ost is true.
156a3114836SGerry Liu */
157a3114836SGerry Liu static void
acpinex_event_handle_eject_request(ACPI_HANDLE hdl,acpinex_softstate_t * sp,boolean_t call_ost)158a3114836SGerry Liu acpinex_event_handle_eject_request(ACPI_HANDLE hdl, acpinex_softstate_t *sp,
159a3114836SGerry Liu boolean_t call_ost)
160a3114836SGerry Liu {
161a3114836SGerry Liu int code;
162a3114836SGerry Liu char *objname;
163a3114836SGerry Liu
164a3114836SGerry Liu ASSERT(hdl != NULL);
165a3114836SGerry Liu objname = acpidev_get_object_name(hdl);
166a3114836SGerry Liu
167a3114836SGerry Liu ASSERT(sp != NULL);
168a3114836SGerry Liu ASSERT(sp->ans_dip != NULL && sp->ans_hdl != NULL);
169a3114836SGerry Liu if (sp == NULL || sp->ans_dip == NULL || sp->ans_hdl == NULL) {
170a3114836SGerry Liu if (call_ost) {
171a3114836SGerry Liu (void) acpidev_eval_ost(hdl, ACPI_NOTIFY_EJECT_REQUEST,
172a3114836SGerry Liu ACPI_OST_STA_FAILURE, NULL, 0);
173a3114836SGerry Liu }
174a3114836SGerry Liu ACPINEX_DEBUG(CE_WARN,
175a3114836SGerry Liu "!acpinex: softstate data structure is invalid.");
176a3114836SGerry Liu cmn_err(CE_WARN,
177a3114836SGerry Liu "!acpinex: failed to handle EJECT_REQUEST event from %s.",
178a3114836SGerry Liu objname);
179a3114836SGerry Liu acpidev_free_object_name(objname);
180a3114836SGerry Liu return;
181a3114836SGerry Liu }
182a3114836SGerry Liu
183a3114836SGerry Liu if (acpinex_event_support_remove == 0) {
184a3114836SGerry Liu cmn_err(CE_WARN,
185a3114836SGerry Liu "!acpinex: hot-removing of device %s is unsupported.",
186a3114836SGerry Liu objname);
187a3114836SGerry Liu code = ACPI_OST_STA_EJECT_NOT_SUPPORT;
188a3114836SGerry Liu } else if (acpinex_event_generate_event(sp->ans_dip, hdl,
189a3114836SGerry Liu SE_OUTGOING_RES, ACPI_NOTIFY_EJECT_REQUEST, objname) != 0) {
190a3114836SGerry Liu cmn_err(CE_WARN, "!acpinex: failed to generate ESC_DR_REQ "
191a3114836SGerry Liu "event for device eject request from %s.", objname);
192a3114836SGerry Liu code = ACPI_OST_STA_FAILURE;
193a3114836SGerry Liu } else {
194a3114836SGerry Liu cmn_err(CE_NOTE, "!acpinex: generate ESC_DR_REQ event for "
195a3114836SGerry Liu "device eject request from %s.", objname);
196a3114836SGerry Liu code = ACPI_OST_STA_EJECT_IN_PROGRESS;
197a3114836SGerry Liu }
198a3114836SGerry Liu if (call_ost) {
199a3114836SGerry Liu (void) acpidev_eval_ost(hdl, ACPI_NOTIFY_EJECT_REQUEST,
200a3114836SGerry Liu code, NULL, 0);
201a3114836SGerry Liu }
202a3114836SGerry Liu
203a3114836SGerry Liu acpidev_free_object_name(objname);
204a3114836SGerry Liu }
205a3114836SGerry Liu
206a3114836SGerry Liu struct acpinex_event_check_arg {
207a3114836SGerry Liu acpinex_softstate_t *softstatep;
208a3114836SGerry Liu int event_type;
209a3114836SGerry Liu uint32_t device_insert;
210a3114836SGerry Liu uint32_t device_remove;
211a3114836SGerry Liu uint32_t device_fail;
212a3114836SGerry Liu };
213a3114836SGerry Liu
214a3114836SGerry Liu static ACPI_STATUS
acpinex_event_handle_check_one(ACPI_HANDLE hdl,UINT32 lvl,void * ctx,void ** retval)215a3114836SGerry Liu acpinex_event_handle_check_one(ACPI_HANDLE hdl, UINT32 lvl, void *ctx,
216a3114836SGerry Liu void **retval)
217a3114836SGerry Liu {
218a3114836SGerry Liu _NOTE(ARGUNUSED(lvl, retval));
219a3114836SGerry Liu
220a3114836SGerry Liu char *objname;
221a3114836SGerry Liu int status, psta, csta;
222a3114836SGerry Liu acpidev_data_handle_t dhdl;
223a3114836SGerry Liu struct acpinex_event_check_arg *argp;
224a3114836SGerry Liu
225a3114836SGerry Liu ASSERT(hdl != NULL);
226a3114836SGerry Liu ASSERT(ctx != NULL);
227a3114836SGerry Liu argp = (struct acpinex_event_check_arg *)ctx;
228a3114836SGerry Liu
229a3114836SGerry Liu dhdl = acpidev_data_get_handle(hdl);
230a3114836SGerry Liu if (dhdl == NULL) {
231a3114836SGerry Liu /* Skip subtree if failed to get the data handle. */
232a3114836SGerry Liu ACPINEX_DEBUG(CE_NOTE,
233a3114836SGerry Liu "!acpinex: failed to get data associated with %p.", hdl);
234a3114836SGerry Liu return (AE_CTRL_DEPTH);
235a3114836SGerry Liu } else if (!acpidev_data_dr_capable(dhdl)) {
236a3114836SGerry Liu return (AE_OK);
237a3114836SGerry Liu }
238a3114836SGerry Liu
239a3114836SGerry Liu objname = acpidev_get_object_name(hdl);
240a3114836SGerry Liu
241a3114836SGerry Liu status = 0;
242a3114836SGerry Liu /* Query previous device status. */
243a3114836SGerry Liu psta = acpidev_data_get_status(dhdl);
244a3114836SGerry Liu if (acpidev_check_device_enabled(psta)) {
245a3114836SGerry Liu status |= 0x1;
246a3114836SGerry Liu }
247a3114836SGerry Liu /* Query current device status. */
248a3114836SGerry Liu csta = acpidev_query_device_status(hdl);
249a3114836SGerry Liu if (acpidev_check_device_enabled(csta)) {
250a3114836SGerry Liu status |= 0x2;
251a3114836SGerry Liu }
252a3114836SGerry Liu
253a3114836SGerry Liu switch (status) {
254a3114836SGerry Liu case 0x0:
255a3114836SGerry Liu /*FALLTHROUGH*/
256a3114836SGerry Liu case 0x3:
257a3114836SGerry Liu /* No status changes, keep on walking. */
258a3114836SGerry Liu acpidev_free_object_name(objname);
259a3114836SGerry Liu return (AE_OK);
260a3114836SGerry Liu
261a3114836SGerry Liu case 0x1:
262a3114836SGerry Liu /* Surprising removal. */
263a3114836SGerry Liu cmn_err(CE_WARN,
264a3114836SGerry Liu "!acpinex: device %s has been surprisingly removed.",
265a3114836SGerry Liu objname);
266a3114836SGerry Liu if (argp->event_type == ACPI_NOTIFY_BUS_CHECK) {
267a3114836SGerry Liu /*
268a3114836SGerry Liu * According to ACPI spec, BUS_CHECK notification
269a3114836SGerry Liu * should be triggered for hot-adding events only.
270a3114836SGerry Liu */
271a3114836SGerry Liu ACPINEX_DEBUG(CE_WARN,
272a3114836SGerry Liu "!acpinex: device %s has been surprisingly removed "
273a3114836SGerry Liu "when handling BUS_CHECK event.", objname);
274a3114836SGerry Liu }
275a3114836SGerry Liu acpidev_free_object_name(objname);
276a3114836SGerry Liu argp->device_remove++;
277a3114836SGerry Liu return (AE_CTRL_DEPTH);
278a3114836SGerry Liu
279a3114836SGerry Liu case 0x2:
280a3114836SGerry Liu /* Hot-adding. */
281a3114836SGerry Liu ACPINEX_DEBUG(CE_NOTE,
282a3114836SGerry Liu "!acpinex: device %s has been inserted.", objname);
283a3114836SGerry Liu argp->device_insert++;
284a3114836SGerry Liu if (acpinex_event_generate_event(argp->softstatep->ans_dip, hdl,
285a3114836SGerry Liu SE_INCOMING_RES, argp->event_type, objname) != 0) {
286a3114836SGerry Liu cmn_err(CE_WARN,
287a3114836SGerry Liu "!acpinex: failed to generate ESC_DR_REQ event for "
288a3114836SGerry Liu "device insert request from %s.", objname);
289a3114836SGerry Liu argp->device_fail++;
290a3114836SGerry Liu } else {
291a3114836SGerry Liu cmn_err(CE_NOTE, "!acpinex: generate ESC_DR_REQ event "
292a3114836SGerry Liu "for device insert request from %s.", objname);
293a3114836SGerry Liu }
294a3114836SGerry Liu acpidev_free_object_name(objname);
295a3114836SGerry Liu return (AE_OK);
296a3114836SGerry Liu
297a3114836SGerry Liu default:
298a3114836SGerry Liu ASSERT(0);
299a3114836SGerry Liu break;
300a3114836SGerry Liu }
301a3114836SGerry Liu
302a3114836SGerry Liu return (AE_ERROR);
303a3114836SGerry Liu }
304a3114836SGerry Liu
305a3114836SGerry Liu /*
306a3114836SGerry Liu * Event handler for BUS_CHECK/DEVICE_CHECK/DEVICE_CHECK_LIGHT notifications.
307a3114836SGerry Liu * These events may be signaled on parent/ancestor of devices to be hot-added,
308a3114836SGerry Liu * so need to scan ACPI namespace to figure out devices in question.
309a3114836SGerry Liu * It also invokes ACPI _OST method to update event status if call_ost is true.
310a3114836SGerry Liu */
311a3114836SGerry Liu static void
acpinex_event_handle_check_request(int event,ACPI_HANDLE hdl,acpinex_softstate_t * sp,boolean_t call_ost)312a3114836SGerry Liu acpinex_event_handle_check_request(int event, ACPI_HANDLE hdl,
313a3114836SGerry Liu acpinex_softstate_t *sp, boolean_t call_ost)
314a3114836SGerry Liu {
315a3114836SGerry Liu ACPI_STATUS rv;
316a3114836SGerry Liu int code;
317a3114836SGerry Liu char *objname;
318a3114836SGerry Liu struct acpinex_event_check_arg arg;
319a3114836SGerry Liu
320a3114836SGerry Liu ASSERT(hdl != NULL);
321a3114836SGerry Liu objname = acpidev_get_object_name(hdl);
322a3114836SGerry Liu
323a3114836SGerry Liu ASSERT(sp != NULL);
324a3114836SGerry Liu ASSERT(sp->ans_dip != NULL && sp->ans_hdl != NULL);
325a3114836SGerry Liu if (sp == NULL || sp->ans_dip == NULL || sp->ans_hdl == NULL) {
326a3114836SGerry Liu if (call_ost) {
327a3114836SGerry Liu (void) acpidev_eval_ost(hdl, event,
328a3114836SGerry Liu ACPI_OST_STA_FAILURE, NULL, 0);
329a3114836SGerry Liu }
330a3114836SGerry Liu ACPINEX_DEBUG(CE_WARN,
331a3114836SGerry Liu "!acpinex: softstate data structure is invalid.");
332a3114836SGerry Liu cmn_err(CE_WARN, "!acpinex: failed to handle "
333a3114836SGerry Liu "BUS/DEVICE_CHECK event from %s.", objname);
334a3114836SGerry Liu acpidev_free_object_name(objname);
335a3114836SGerry Liu return;
336a3114836SGerry Liu }
337a3114836SGerry Liu
338a3114836SGerry Liu bzero(&arg, sizeof (arg));
339a3114836SGerry Liu arg.event_type = event;
340a3114836SGerry Liu arg.softstatep = sp;
341a3114836SGerry Liu rv = acpinex_event_handle_check_one(hdl, 0, &arg, NULL);
342a3114836SGerry Liu if (ACPI_SUCCESS(rv)) {
343a3114836SGerry Liu rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, hdl,
344a3114836SGerry Liu ACPIDEV_MAX_ENUM_LEVELS,
345a3114836SGerry Liu &acpinex_event_handle_check_one, NULL, &arg, NULL);
346a3114836SGerry Liu }
347a3114836SGerry Liu
348a3114836SGerry Liu if (ACPI_FAILURE(rv)) {
349a3114836SGerry Liu /* Failed to scan the ACPI namespace. */
350a3114836SGerry Liu cmn_err(CE_WARN, "!acpinex: failed to handle event %d from %s.",
351a3114836SGerry Liu event, objname);
352a3114836SGerry Liu code = ACPI_OST_STA_FAILURE;
353a3114836SGerry Liu } else if (arg.device_remove != 0) {
354a3114836SGerry Liu /* Surprising removal happened. */
355a3114836SGerry Liu ACPINEX_DEBUG(CE_WARN,
356a3114836SGerry Liu "!acpinex: some devices have been surprisingly removed.");
357a3114836SGerry Liu code = ACPI_OST_STA_NOT_SUPPORT;
358a3114836SGerry Liu } else if (arg.device_fail != 0) {
359a3114836SGerry Liu /* Failed to handle some devices. */
360a3114836SGerry Liu ACPINEX_DEBUG(CE_WARN,
361a3114836SGerry Liu "!acpinex: failed to check status of some devices.");
362a3114836SGerry Liu code = ACPI_OST_STA_FAILURE;
363a3114836SGerry Liu } else if (arg.device_insert == 0) {
364a3114836SGerry Liu /* No hot-added devices found. */
365a3114836SGerry Liu cmn_err(CE_WARN,
366a3114836SGerry Liu "!acpinex: no hot-added devices under %s found.", objname);
367a3114836SGerry Liu code = ACPI_OST_STA_FAILURE;
368a3114836SGerry Liu } else {
369a3114836SGerry Liu code = ACPI_OST_STA_INSERT_IN_PROGRESS;
370a3114836SGerry Liu }
371a3114836SGerry Liu if (call_ost) {
372a3114836SGerry Liu (void) acpidev_eval_ost(hdl, event, code, NULL, 0);
373a3114836SGerry Liu }
374a3114836SGerry Liu
375a3114836SGerry Liu acpidev_free_object_name(objname);
376a3114836SGerry Liu }
377a3114836SGerry Liu
378a3114836SGerry Liu static void
acpinex_event_system_handler(ACPI_HANDLE hdl,UINT32 type,void * arg)379a3114836SGerry Liu acpinex_event_system_handler(ACPI_HANDLE hdl, UINT32 type, void *arg)
380a3114836SGerry Liu {
381a3114836SGerry Liu acpinex_softstate_t *sp;
382a3114836SGerry Liu
383a3114836SGerry Liu ASSERT(hdl != NULL);
384a3114836SGerry Liu ASSERT(arg != NULL);
385a3114836SGerry Liu sp = (acpinex_softstate_t *)arg;
386a3114836SGerry Liu
387a3114836SGerry Liu acpidev_dr_lock_all();
388a3114836SGerry Liu mutex_enter(&sp->ans_lock);
389a3114836SGerry Liu
390a3114836SGerry Liu switch (type) {
391a3114836SGerry Liu case ACPI_NOTIFY_BUS_CHECK:
392a3114836SGerry Liu /*
393a3114836SGerry Liu * Bus Check. This notification is performed on a device object
394a3114836SGerry Liu * to indicate to OSPM that it needs to perform the Plug and
395a3114836SGerry Liu * Play re-enumeration operation on the device tree starting
396a3114836SGerry Liu * from the point where it has been notified. OSPM will only
397a3114836SGerry Liu * perform this operation at boot, and when notified. It is
398a3114836SGerry Liu * the responsibility of the ACPI AML code to notify OSPM at
399a3114836SGerry Liu * any other times that this operation is required. The more
400a3114836SGerry Liu * accurately and closer to the actual device tree change the
401a3114836SGerry Liu * notification can be done, the more efficient the operating
402a3114836SGerry Liu * system response will be; however, it can also be an issue
403a3114836SGerry Liu * when a device change cannot be confirmed. For example, if
404a3114836SGerry Liu * the hardware cannot notice a device change for a particular
405a3114836SGerry Liu * location during a system sleeping state, it issues a Bus
406a3114836SGerry Liu * Check notification on wake to inform OSPM that it needs to
407a3114836SGerry Liu * check the configuration for a device change.
408a3114836SGerry Liu */
409a3114836SGerry Liu /*FALLTHROUGH*/
410a3114836SGerry Liu case ACPI_NOTIFY_DEVICE_CHECK:
411a3114836SGerry Liu /*
412a3114836SGerry Liu * Device Check. Used to notify OSPM that the device either
413a3114836SGerry Liu * appeared or disappeared. If the device has appeared, OSPM
414a3114836SGerry Liu * will re-enumerate from the parent. If the device has
415a3114836SGerry Liu * disappeared, OSPM will invalidate the state of the device.
416a3114836SGerry Liu * OSPM may optimize out re-enumeration. If _DCK is present,
417a3114836SGerry Liu * then Notify(object,1) is assumed to indicate an undock
418a3114836SGerry Liu * request.
419a3114836SGerry Liu */
420a3114836SGerry Liu /*FALLTHROUGH*/
421a3114836SGerry Liu case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
422a3114836SGerry Liu /*
423a3114836SGerry Liu * Device Check Light. Used to notify OSPM that the device
424a3114836SGerry Liu * either appeared or disappeared. If the device has appeared,
425a3114836SGerry Liu * OSPM will re-enumerate from the device itself, not the
426a3114836SGerry Liu * parent. If the device has disappeared, OSPM will invalidate
427a3114836SGerry Liu * the state of the device.
428a3114836SGerry Liu */
429a3114836SGerry Liu atomic_inc_uint(&acpinex_dr_event_cnt);
430a3114836SGerry Liu acpinex_event_handle_check_request(type, hdl, sp, B_TRUE);
431a3114836SGerry Liu break;
432a3114836SGerry Liu
433a3114836SGerry Liu case ACPI_NOTIFY_EJECT_REQUEST:
434a3114836SGerry Liu /*
435a3114836SGerry Liu * Eject Request. Used to notify OSPM that the device should
436a3114836SGerry Liu * be ejected, and that OSPM needs to perform the Plug and Play
437a3114836SGerry Liu * ejection operation. OSPM will run the _EJx method.
438a3114836SGerry Liu */
439a3114836SGerry Liu atomic_inc_uint(&acpinex_dr_event_cnt);
440a3114836SGerry Liu acpinex_event_handle_eject_request(hdl, sp, B_TRUE);
441a3114836SGerry Liu break;
442a3114836SGerry Liu
443a3114836SGerry Liu default:
444a3114836SGerry Liu ACPINEX_DEBUG(CE_NOTE,
445a3114836SGerry Liu "!acpinex: unhandled event(%d) on hdl %p under %s.",
446a3114836SGerry Liu type, hdl, sp->ans_path);
447a3114836SGerry Liu (void) acpidev_eval_ost(hdl, type, ACPI_OST_STA_NOT_SUPPORT,
448a3114836SGerry Liu NULL, 0);
449a3114836SGerry Liu break;
450a3114836SGerry Liu }
451a3114836SGerry Liu
452a3114836SGerry Liu if (acpinex_dr_event_cnt != 0) {
453a3114836SGerry Liu /*
454a3114836SGerry Liu * Disable fast reboot if a CPU/MEM/IOH hotplug event happens.
455a3114836SGerry Liu * Note: this is a temporary solution and will be revised when
456a3114836SGerry Liu * fast reboot can support CPU/MEM/IOH DR operations in the
457a3114836SGerry Liu * future.
458a3114836SGerry Liu *
459a3114836SGerry Liu * ACPI BIOS generates some static ACPI tables, such as MADT,
460a3114836SGerry Liu * SRAT and SLIT, to describe the system hardware configuration
461a3114836SGerry Liu * on power-on. When a CPU/MEM/IOH hotplug event happens, those
462a3114836SGerry Liu * static tables won't be updated and will become stale.
463a3114836SGerry Liu *
464a3114836SGerry Liu * If we reset the system by fast reboot, BIOS will have no
465a3114836SGerry Liu * chance to regenerate those staled static tables. Fast reboot
466a3114836SGerry Liu * can't tolerate such inconsistency between staled ACPI tables
467a3114836SGerry Liu * and real hardware configuration yet.
468a3114836SGerry Liu *
469a3114836SGerry Liu * A temporary solution is introduced to disable fast reboot if
470a3114836SGerry Liu * CPU/MEM/IOH hotplug event happens. This solution should be
471a3114836SGerry Liu * revised when fast reboot is enhanced to support CPU/MEM/IOH
472a3114836SGerry Liu * DR operations.
473a3114836SGerry Liu */
474a3114836SGerry Liu fastreboot_disable(FBNS_HOTPLUG);
475a3114836SGerry Liu }
476a3114836SGerry Liu
477a3114836SGerry Liu mutex_exit(&sp->ans_lock);
478a3114836SGerry Liu acpidev_dr_unlock_all();
479a3114836SGerry Liu }
480a3114836SGerry Liu
481a3114836SGerry Liu /*
482a3114836SGerry Liu * Install event handler for ACPI system events.
483a3114836SGerry Liu * Acpinex driver handles ACPI system events for its children,
484a3114836SGerry Liu * device specific events will be handled by device drivers.
485a3114836SGerry Liu * Return DDI_SUCCESS on success, and DDI_FAILURE on failure.
486a3114836SGerry Liu */
487a3114836SGerry Liu static int
acpinex_event_install_handler(ACPI_HANDLE hdl,void * arg,ACPI_DEVICE_INFO * infop,acpidev_data_handle_t dhdl)488a3114836SGerry Liu acpinex_event_install_handler(ACPI_HANDLE hdl, void *arg,
489a3114836SGerry Liu ACPI_DEVICE_INFO *infop, acpidev_data_handle_t dhdl)
490a3114836SGerry Liu {
491*26f3cdf0SGordon Ross ACPI_STATUS status;
492a3114836SGerry Liu int rc = DDI_SUCCESS;
493a3114836SGerry Liu
494a3114836SGerry Liu ASSERT(hdl != NULL);
495a3114836SGerry Liu ASSERT(dhdl != NULL);
496a3114836SGerry Liu ASSERT(infop != NULL);
497a3114836SGerry Liu
498a3114836SGerry Liu /*
499a3114836SGerry Liu * Check whether the event handler has already been installed on the
500a3114836SGerry Liu * device object. With the introduction of ACPI Alias objects, which are
501a3114836SGerry Liu * similar to symlinks in file systems, there may be multiple name
502a3114836SGerry Liu * objects in the ACPI namespace pointing to the same underlying device
503a3114836SGerry Liu * object. Those Alias objects need to be filtered out, otherwise
504a3114836SGerry Liu * it will attempt to install the event handler multiple times on the
505a3114836SGerry Liu * same device object which will fail.
506a3114836SGerry Liu */
507a3114836SGerry Liu if (acpidev_data_get_flag(dhdl, ACPIDEV_DATA_HANDLER_READY)) {
508a3114836SGerry Liu return (DDI_SUCCESS);
509a3114836SGerry Liu }
510*26f3cdf0SGordon Ross status = AcpiInstallNotifyHandler(hdl, ACPI_SYSTEM_NOTIFY,
511*26f3cdf0SGordon Ross acpinex_event_system_handler, arg);
512*26f3cdf0SGordon Ross if (status == AE_OK || status == AE_ALREADY_EXISTS) {
513a3114836SGerry Liu acpidev_data_set_flag(dhdl, ACPIDEV_DATA_HANDLER_READY);
514a3114836SGerry Liu } else {
515a3114836SGerry Liu char *objname;
516a3114836SGerry Liu
517a3114836SGerry Liu objname = acpidev_get_object_name(hdl);
518a3114836SGerry Liu cmn_err(CE_WARN,
519a3114836SGerry Liu "!acpinex: failed to install system event handler for %s.",
520a3114836SGerry Liu objname);
521a3114836SGerry Liu acpidev_free_object_name(objname);
522a3114836SGerry Liu rc = DDI_FAILURE;
523a3114836SGerry Liu }
524a3114836SGerry Liu
525a3114836SGerry Liu return (rc);
526a3114836SGerry Liu }
527a3114836SGerry Liu
528a3114836SGerry Liu /*
529a3114836SGerry Liu * Uninstall event handler for ACPI system events.
530a3114836SGerry Liu * Return DDI_SUCCESS on success, and DDI_FAILURE on failure.
531a3114836SGerry Liu */
532a3114836SGerry Liu static int
acpinex_event_uninstall_handler(ACPI_HANDLE hdl,ACPI_DEVICE_INFO * infop,acpidev_data_handle_t dhdl)533a3114836SGerry Liu acpinex_event_uninstall_handler(ACPI_HANDLE hdl, ACPI_DEVICE_INFO *infop,
534a3114836SGerry Liu acpidev_data_handle_t dhdl)
535a3114836SGerry Liu {
536a3114836SGerry Liu ASSERT(hdl != NULL);
537a3114836SGerry Liu ASSERT(dhdl != NULL);
538a3114836SGerry Liu ASSERT(infop != NULL);
539a3114836SGerry Liu
540a3114836SGerry Liu if (!acpidev_data_get_flag(dhdl, ACPIDEV_DATA_HANDLER_READY)) {
541a3114836SGerry Liu return (DDI_SUCCESS);
542a3114836SGerry Liu }
543a3114836SGerry Liu if (ACPI_SUCCESS(AcpiRemoveNotifyHandler(hdl, ACPI_SYSTEM_NOTIFY,
544a3114836SGerry Liu acpinex_event_system_handler))) {
545a3114836SGerry Liu acpidev_data_clear_flag(dhdl, ACPIDEV_DATA_HANDLER_READY);
546a3114836SGerry Liu } else {
547a3114836SGerry Liu char *objname;
548a3114836SGerry Liu
549a3114836SGerry Liu objname = acpidev_get_object_name(hdl);
550a3114836SGerry Liu cmn_err(CE_WARN, "!acpinex: failed to uninstall system event "
551a3114836SGerry Liu "handler for %s.", objname);
552a3114836SGerry Liu acpidev_free_object_name(objname);
553a3114836SGerry Liu return (DDI_FAILURE);
554a3114836SGerry Liu }
555a3114836SGerry Liu
556a3114836SGerry Liu return (DDI_SUCCESS);
557a3114836SGerry Liu }
558a3114836SGerry Liu
559a3114836SGerry Liu /*
560a3114836SGerry Liu * Install/uninstall ACPI system event handler for child objects of hdl.
561a3114836SGerry Liu * Return DDI_SUCCESS on success, and DDI_FAILURE on failure.
562a3114836SGerry Liu */
563a3114836SGerry Liu static int
acpinex_event_walk(boolean_t init,acpinex_softstate_t * sp,ACPI_HANDLE hdl)564a3114836SGerry Liu acpinex_event_walk(boolean_t init, acpinex_softstate_t *sp, ACPI_HANDLE hdl)
565a3114836SGerry Liu {
566a3114836SGerry Liu int rc;
567a3114836SGerry Liu int retval = DDI_SUCCESS;
568a3114836SGerry Liu dev_info_t *dip;
569a3114836SGerry Liu ACPI_HANDLE child = NULL;
570a3114836SGerry Liu ACPI_OBJECT_TYPE type;
571a3114836SGerry Liu ACPI_DEVICE_INFO *infop;
572a3114836SGerry Liu acpidev_data_handle_t dhdl;
573a3114836SGerry Liu
574a3114836SGerry Liu /* Walk all child objects. */
575a3114836SGerry Liu ASSERT(hdl != NULL);
576a3114836SGerry Liu while (ACPI_SUCCESS(AcpiGetNextObject(ACPI_TYPE_ANY, hdl, child,
577a3114836SGerry Liu &child))) {
578a3114836SGerry Liu /* Skip unwanted object types. */
579a3114836SGerry Liu if (ACPI_FAILURE(AcpiGetType(child, &type)) ||
580a3114836SGerry Liu type > ACPI_TYPE_NS_NODE_MAX ||
581a3114836SGerry Liu BT_TEST(acpinex_object_type_mask, type) == 0) {
582a3114836SGerry Liu continue;
583a3114836SGerry Liu }
584a3114836SGerry Liu
585a3114836SGerry Liu /* Get data associated with the object. Skip it if fails. */
586a3114836SGerry Liu dhdl = acpidev_data_get_handle(child);
587a3114836SGerry Liu if (dhdl == NULL) {
588a3114836SGerry Liu ACPINEX_DEBUG(CE_NOTE, "!acpinex: failed to get data "
589a3114836SGerry Liu "associated with %p, skip.", child);
590a3114836SGerry Liu continue;
591a3114836SGerry Liu }
592a3114836SGerry Liu
593a3114836SGerry Liu /* Query ACPI object info for the object. */
594a3114836SGerry Liu if (ACPI_FAILURE(AcpiGetObjectInfo(child, &infop))) {
595a3114836SGerry Liu cmn_err(CE_WARN,
596a3114836SGerry Liu "!acpidnex: failed to get object info for %p.",
597a3114836SGerry Liu child);
598a3114836SGerry Liu continue;
599a3114836SGerry Liu }
600a3114836SGerry Liu
601a3114836SGerry Liu if (init) {
602a3114836SGerry Liu rc = acpinex_event_install_handler(child, sp, infop,
603a3114836SGerry Liu dhdl);
604a3114836SGerry Liu if (rc != DDI_SUCCESS) {
605a3114836SGerry Liu ACPINEX_DEBUG(CE_WARN, "!acpinex: failed to "
606a3114836SGerry Liu "install handler for child %p of %s.",
607a3114836SGerry Liu child, sp->ans_path);
608a3114836SGerry Liu retval = DDI_FAILURE;
609a3114836SGerry Liu /*
610a3114836SGerry Liu * Try to handle descendants if both of the
611a3114836SGerry Liu * following two conditions are true:
612a3114836SGerry Liu * 1) Device corresponding to the current object is
613a3114836SGerry Liu * enabled. If the device is absent/disabled,
614a3114836SGerry Liu * no notification should be generated from
615a3114836SGerry Liu * descendant objects of it.
616a3114836SGerry Liu * 2) No Solaris device node has been created for the
617a3114836SGerry Liu * current object yet. If the device node has been
618a3114836SGerry Liu * created for the current object, notification
619a3114836SGerry Liu * events from child objects should be handled by
620a3114836SGerry Liu * the corresponding driver.
621a3114836SGerry Liu */
622a3114836SGerry Liu } else if (acpidev_check_device_enabled(
623a3114836SGerry Liu acpidev_data_get_status(dhdl)) &&
624a3114836SGerry Liu ACPI_FAILURE(acpica_get_devinfo(child, &dip))) {
625a3114836SGerry Liu rc = acpinex_event_walk(B_TRUE, sp, child);
626a3114836SGerry Liu if (rc != DDI_SUCCESS) {
627a3114836SGerry Liu ACPINEX_DEBUG(CE_WARN,
628a3114836SGerry Liu "!acpinex: failed to install "
629a3114836SGerry Liu "handler for descendants of %s.",
630a3114836SGerry Liu sp->ans_path);
631a3114836SGerry Liu retval = DDI_FAILURE;
632a3114836SGerry Liu }
633a3114836SGerry Liu }
634a3114836SGerry Liu } else {
635a3114836SGerry Liu rc = DDI_SUCCESS;
636a3114836SGerry Liu /* Uninstall handler for descendants if needed. */
637a3114836SGerry Liu if (ACPI_FAILURE(acpica_get_devinfo(child, &dip))) {
638a3114836SGerry Liu rc = acpinex_event_walk(B_FALSE, sp, child);
639a3114836SGerry Liu }
640a3114836SGerry Liu if (rc == DDI_SUCCESS) {
641a3114836SGerry Liu rc = acpinex_event_uninstall_handler(child,
642a3114836SGerry Liu infop, dhdl);
643a3114836SGerry Liu }
644a3114836SGerry Liu /* Undo will be done by caller in case of failure. */
645a3114836SGerry Liu if (rc != DDI_SUCCESS) {
646a3114836SGerry Liu ACPINEX_DEBUG(CE_WARN, "!acpinex: failed to "
647a3114836SGerry Liu "uninstall handler for descendants of %s.",
648a3114836SGerry Liu sp->ans_path);
649a3114836SGerry Liu AcpiOsFree(infop);
650a3114836SGerry Liu retval = DDI_FAILURE;
651a3114836SGerry Liu break;
652a3114836SGerry Liu }
653a3114836SGerry Liu }
654a3114836SGerry Liu
655a3114836SGerry Liu /* Release cached resources. */
656a3114836SGerry Liu AcpiOsFree(infop);
657a3114836SGerry Liu }
658a3114836SGerry Liu
659a3114836SGerry Liu return (retval);
660a3114836SGerry Liu }
661a3114836SGerry Liu
662a3114836SGerry Liu int
acpinex_event_scan(acpinex_softstate_t * sp,boolean_t init)663a3114836SGerry Liu acpinex_event_scan(acpinex_softstate_t *sp, boolean_t init)
664a3114836SGerry Liu {
665a3114836SGerry Liu int rc;
666a3114836SGerry Liu
667a3114836SGerry Liu ASSERT(sp != NULL);
668a3114836SGerry Liu ASSERT(sp->ans_hdl != NULL);
669a3114836SGerry Liu ASSERT(sp->ans_dip != NULL);
670a3114836SGerry Liu if (sp == NULL || sp->ans_hdl == NULL || sp->ans_dip == NULL) {
671a3114836SGerry Liu ACPINEX_DEBUG(CE_WARN,
672a3114836SGerry Liu "!acpinex: invalid parameter to acpinex_event_scan().");
673a3114836SGerry Liu return (DDI_FAILURE);
674a3114836SGerry Liu }
675a3114836SGerry Liu
676a3114836SGerry Liu /* Lock current device node and walk all child device nodes of it. */
677a3114836SGerry Liu mutex_enter(&sp->ans_lock);
678a3114836SGerry Liu
679a3114836SGerry Liu rc = acpinex_event_walk(init, sp, sp->ans_hdl);
680a3114836SGerry Liu if (rc != DDI_SUCCESS) {
681a3114836SGerry Liu if (init) {
682a3114836SGerry Liu ACPINEX_DEBUG(CE_WARN, "!acpinex: failed to "
683a3114836SGerry Liu "configure child objects of %s.", sp->ans_path);
684a3114836SGerry Liu rc = DDI_FAILURE;
685a3114836SGerry Liu } else {
686a3114836SGerry Liu ACPINEX_DEBUG(CE_WARN, "!acpinex: failed to "
687a3114836SGerry Liu "unconfigure child objects of %s.", sp->ans_path);
688a3114836SGerry Liu /* Undo in case of errors */
689a3114836SGerry Liu (void) acpinex_event_walk(B_TRUE, sp, sp->ans_hdl);
690a3114836SGerry Liu rc = DDI_FAILURE;
691a3114836SGerry Liu }
692a3114836SGerry Liu }
693a3114836SGerry Liu
694a3114836SGerry Liu mutex_exit(&sp->ans_lock);
695a3114836SGerry Liu
696a3114836SGerry Liu return (rc);
697a3114836SGerry Liu }
698a3114836SGerry Liu
699a3114836SGerry Liu void
acpinex_event_init(void)700a3114836SGerry Liu acpinex_event_init(void)
701a3114836SGerry Liu {
702a3114836SGerry Liu /*
703a3114836SGerry Liu * According to ACPI specifications, notification is only supported on
704a3114836SGerry Liu * Device, Processor and ThermalZone. Currently we only need to handle
705a3114836SGerry Liu * Device and Processor objects.
706a3114836SGerry Liu */
707a3114836SGerry Liu BT_SET(acpinex_object_type_mask, ACPI_TYPE_PROCESSOR);
708a3114836SGerry Liu BT_SET(acpinex_object_type_mask, ACPI_TYPE_DEVICE);
709a3114836SGerry Liu }
710a3114836SGerry Liu
711a3114836SGerry Liu void
acpinex_event_fini(void)712a3114836SGerry Liu acpinex_event_fini(void)
713a3114836SGerry Liu {
714a3114836SGerry Liu bzero(acpinex_object_type_mask, sizeof (acpinex_object_type_mask));
715a3114836SGerry Liu }
716