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.
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.
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]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright (c) 2011 Bayard G. Bell. All rights reserved.
28 #include <sys/types.h>
31 #include <sys/modctl.h>
32 #include <sys/promif.h>
34 #include <sys/ddi_impldefs.h>
36 #include <sys/sunddi.h>
38 #include <sys/acpi/acpi.h>
39 #include <sys/acpica.h>
40 #include <sys/psm_types.h>
43 * ACPI Power Management Driver
45 * acpippm deals with those bits of ppm functionality that
46 * must be mediated by ACPI
48 * The routines in this driver is referenced by Platform
49 * Power Management driver of X86 workstation systems.
50 * acpippm driver is loaded because it is listed as a platform driver
51 * It is initially configured as a pseudo driver.
53 extern void pc_tod_set_rtc_offsets(ACPI_TABLE_FADT
*);
54 extern int acpica_use_safe_delay
;
57 * Configuration Function prototypes and data structures
59 static int appm_attach(dev_info_t
*, ddi_attach_cmd_t
);
60 static int appm_detach(dev_info_t
*, ddi_detach_cmd_t
);
61 static int appm_getinfo(dev_info_t
*, ddi_info_cmd_t
, void *, void **);
62 static int appm_open(dev_t
*dev_p
, int flag
, int otyp
, cred_t
*cred_p
);
63 static int appm_close(dev_t dev
, int flag
, int otyp
, cred_t
*cred_p
);
64 static int appm_ioctl(dev_t
, int, intptr_t, int, cred_t
*, int *);
67 * Configuration data structures
69 static struct cb_ops appm_cbops
= {
71 appm_close
, /* close */
77 appm_ioctl
, /* ioctl */
81 nochpoll
, /* chpoll */
82 ddi_prop_op
, /* prop_op */
84 D_MP
| D_NEW
, /* flag */
90 static struct dev_ops appm_ops
= {
91 DEVO_REV
, /* devo_rev */
93 appm_getinfo
, /* getinfo */
94 nulldev
, /* identify */
96 appm_attach
, /* attach */
97 appm_detach
, /* detach */
99 &appm_cbops
, /* cb_ops */
102 ddi_quiesce_not_needed
, /* quiesce */
105 extern struct mod_ops mod_driverops
;
107 static struct modldrv modldrv
= {
113 static struct modlinkage modlinkage
= {
120 * Driver state structure
124 ddi_acc_handle_t devid_hndl
;
125 ddi_acc_handle_t estar_hndl
;
126 int lyropen
; /* ref count */
130 * Driver global variables
132 * appm_lock synchronize the access of lyr handle to each appm
133 * minor device, therefore write to tomatillo device is
134 * sequentialized. Lyr protocol requires pairing up lyr open
135 * and close, so only a single reference is allowed per minor node.
137 static void *appm_statep
;
138 static kmutex_t appm_lock
;
143 extern int acpi_enter_sleepstate(s3a_t
*);
144 extern int acpi_exit_sleepstate(s3a_t
*);
152 if ((error
= ddi_soft_state_init(&appm_statep
,
153 sizeof (appm_unit
), 0)) != DDI_SUCCESS
) {
157 mutex_init(&appm_lock
, NULL
, MUTEX_DRIVER
, NULL
);
159 if ((error
= mod_install(&modlinkage
)) != DDI_SUCCESS
) {
160 mutex_destroy(&appm_lock
);
161 ddi_soft_state_fini(&appm_statep
);
173 if ((error
= mod_remove(&modlinkage
)) == DDI_SUCCESS
) {
174 mutex_destroy(&appm_lock
);
175 ddi_soft_state_fini(&appm_statep
);
183 _info(struct modinfo
*modinfop
)
185 return (mod_info(&modlinkage
, modinfop
));
191 * Driver attach(9e) entry point
194 appm_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
196 char *str
= "appm_attach";
199 ACPI_TABLE_FADT
*fadt
= NULL
;
200 int rv
= DDI_SUCCESS
;
206 return (DDI_SUCCESS
);
208 cmn_err(CE_WARN
, "%s: cmd %d unsupported.\n", str
, cmd
);
209 return (DDI_FAILURE
);
212 instance
= ddi_get_instance(dip
);
213 rv
= ddi_soft_state_zalloc(appm_statep
, instance
);
214 if (rv
!= DDI_SUCCESS
) {
215 cmn_err(CE_WARN
, "%s: failed alloc for dev(%s@%s)",
216 str
, ddi_binding_name(dip
),
217 ddi_get_name_addr(dip
) ? ddi_get_name_addr(dip
) : " ");
221 if ((unitp
= ddi_get_soft_state(appm_statep
, instance
)) == NULL
) {
227 * Export "ddi-kernel-ioctl" property - prepared to support
228 * kernel ioctls (driver layering).
229 * XXX is this still needed?
230 * XXXX (RSF) Not that I am aware of.
232 rv
= ddi_prop_create(DDI_DEV_T_NONE
, dip
, DDI_PROP_CANSLEEP
,
233 DDI_KERNEL_IOCTL
, NULL
, 0);
234 if (rv
!= DDI_PROP_SUCCESS
)
241 * XXX here we would do whatever we need to to determine if the
242 * XXX platform supports ACPI, and fail the attach if not.
243 * XXX If it does, we do whatever setup is needed to get access to
244 * XXX ACPI register space.
250 * create minor node for kernel_ioctl calls
252 rv
= ddi_create_minor_node(dip
, "acpi-ppm", S_IFCHR
, instance
, 0, 0);
253 if (rv
!= DDI_SUCCESS
)
257 if (AcpiGetTable(ACPI_SIG_FADT
, 1,
258 (ACPI_TABLE_HEADER
**)&fadt
) != AE_OK
)
261 /* Init the RTC offsets */
263 pc_tod_set_rtc_offsets(fadt
);
269 if (ddi_prop_exists(DDI_DEV_T_ANY
, dip
, DDI_PROP_DONTPASS
|
270 DDI_PROP_NOTPROM
, DDI_KERNEL_IOCTL
))
271 ddi_prop_remove_all(dip
);
273 ddi_soft_state_free(appm_statep
, instance
);
280 * Driver getinfo(9e) entry routine
284 appm_getinfo(dev_info_t
*dip
, ddi_info_cmd_t cmd
, void *arg
, void **result
)
290 case DDI_INFO_DEVT2DEVINFO
:
291 instance
= getminor((dev_t
)arg
);
292 unitp
= ddi_get_soft_state(appm_statep
, instance
);
294 return (DDI_FAILURE
);
296 *result
= (void *) unitp
->dip
;
297 return (DDI_SUCCESS
);
299 case DDI_INFO_DEVT2INSTANCE
:
300 instance
= getminor((dev_t
)arg
);
301 *result
= (void *)(uintptr_t)instance
;
302 return (DDI_SUCCESS
);
305 return (DDI_FAILURE
);
315 appm_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
317 char *str
= "appm_detach";
321 return (DDI_FAILURE
);
323 return (DDI_SUCCESS
);
325 cmn_err(CE_WARN
, "%s: cmd %d unsupported", str
, cmd
);
326 return (DDI_FAILURE
);
333 appm_open(dev_t
*dev_p
, int flag
, int otyp
, cred_t
*cred_p
)
337 /* not intended to allow sysadmin level root process to open it */
338 if (drv_priv(cred_p
) != DDI_SUCCESS
)
341 if ((unitp
= ddi_get_soft_state(
342 appm_statep
, getminor(*dev_p
))) == NULL
) {
343 cmn_err(CE_WARN
, "appm_open: failed to get soft state!");
344 return (DDI_FAILURE
);
347 mutex_enter(&appm_lock
);
348 if (unitp
->lyropen
!= 0) {
349 mutex_exit(&appm_lock
);
353 mutex_exit(&appm_lock
);
355 return (DDI_SUCCESS
);
361 appm_close(dev_t dev
, int flag
, int otyp
, cred_t
*cred_p
)
366 ddi_get_soft_state(appm_statep
, getminor(dev
))) == NULL
)
367 return (DDI_FAILURE
);
369 mutex_enter(&appm_lock
);
371 mutex_exit(&appm_lock
);
373 return (DDI_SUCCESS
);
378 * must match ppm.conf
380 #define APPMIOC ('A' << 8)
381 #define APPMIOC_ENTER_S3 (APPMIOC | 1) /* arg *s3a_t */
382 #define APPMIOC_EXIT_S3 (APPMIOC | 2) /* arg *s3a_t */
386 appm_ioctl(dev_t dev
, int cmd
, intptr_t arg
, int flag
,
387 cred_t
*cred_p
, int *rval_p
)
389 static boolean_t acpi_initted
= B_FALSE
;
390 char *str
= "appm_ioctl";
392 s3a_t
*s3ap
= (s3a_t
*)arg
;
394 PMD(PMD_SX
, ("%s: called with %x\n", str
, cmd
))
396 if (drv_priv(cred_p
) != 0) {
397 PMD(PMD_SX
, ("%s: EPERM\n", str
))
401 if (ddi_get_soft_state(appm_statep
, getminor(dev
)) == NULL
) {
402 PMD(PMD_SX
, ("%s: no soft state: EIO\n", str
))
407 PMD(PMD_SX
, ("%s: !acpi_initted\n", str
))
408 if (acpica_init() == 0) {
409 acpi_initted
= B_TRUE
;
411 if (rval_p
!= NULL
) {
414 PMD(PMD_SX
, ("%s: EINVAL\n", str
))
419 PMD(PMD_SX
, ("%s: looking for cmd %x\n", str
, cmd
))
421 case APPMIOC_ENTER_S3
:
423 * suspend to RAM (ie S3)
425 PMD(PMD_SX
, ("%s: cmd %x, arg %p\n", str
, cmd
, (void *)arg
))
426 acpica_use_safe_delay
= 1;
427 ret
= acpi_enter_sleepstate(s3ap
);
430 case APPMIOC_EXIT_S3
:
434 PMD(PMD_SX
, ("%s: cmd %x, arg %p\n", str
, cmd
, (void *)arg
))
435 ret
= acpi_exit_sleepstate(s3ap
);
436 acpica_use_safe_delay
= 0;
440 PMD(PMD_SX
, ("%s: cmd %x unrecognized: ENOTTY\n", str
, cmd
))
445 * upon failure return EINVAL
448 if (rval_p
!= NULL
) {