summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrandyf <none@none>2007-10-20 16:00:42 -0700
committerrandyf <none@none>2007-10-20 16:00:42 -0700
commit2df1fe9ca32bb227b9158c67f5c00b54c20b10fd (patch)
tree358c576f885c00d42a760d9e35e5b66e77209fe2
parent10b3fbf593a6678eec9b50a01903ef4eb73111e4 (diff)
downloadillumos-gate-2df1fe9ca32bb227b9158c67f5c00b54c20b10fd.tar.gz
PSARC/2005/469 X86 Energy Star compliance
PSARC/2006/632 PSMI extension for state save and restore 6330209 nge needs to support DDI_SUSPEND/DDI_RESUME 6381827 Suspend to RAM on x86 6393154 audio810 needs to support DDI_SUSPEND/DDI_RESUME 6397047 fd, fdc needs to support Suspend/Resume 6401974 cannot enter S3 with ohci PME enable set on Tyan 2865 with Sun or Tyan 2.01 BIOS 6422613 memscrubber doesn't re-acquire lock before CALLB_CPR_EXIT 6455736 ata/dadk/cmdk should support DDI_SUSPEND/DDI_RESUME 6511370 CPR on SPARC regression 6586018 TODOP Macros in i86pc/sys/machclock.h not in sun4u/sun4v equivilent (Sparc only) 6610124 It takes more than 3 minutes after printing "pci_pre_resume nv_sata:0" 6617143 powerd/pmconfig emits a different default message for an existing on or off action. --HG-- rename : usr/src/cmd/power/power.conf => usr/src/cmd/power/power.conf.sparc
-rw-r--r--usr/src/cmd/devfsadm/i386/misc_link_i386.c23
-rw-r--r--usr/src/cmd/mdb/i86pc/modules/pcplusmp/apic.c117
-rw-r--r--usr/src/cmd/power/Makefile22
-rw-r--r--usr/src/cmd/power/conf.c25
-rw-r--r--usr/src/cmd/power/handlers.c311
-rw-r--r--usr/src/cmd/power/parse.c54
-rw-r--r--usr/src/cmd/power/pmconfig.h9
-rw-r--r--usr/src/cmd/power/power.conf.i38635
-rw-r--r--usr/src/cmd/power/power.conf.sparc (renamed from usr/src/cmd/power/power.conf)9
-rw-r--r--usr/src/cmd/power/powerd.c172
-rw-r--r--usr/src/cmd/uadmin/uadmin.c5
-rw-r--r--usr/src/pkgdefs/Makefile1
-rw-r--r--usr/src/pkgdefs/SUNWcakr.i/prototype_com5
-rw-r--r--usr/src/pkgdefs/SUNWcpr.i/Makefile37
-rw-r--r--usr/src/pkgdefs/SUNWcpr.i/pkginfo.tmpl52
-rw-r--r--usr/src/pkgdefs/SUNWcpr.i/postinstall41
-rw-r--r--usr/src/pkgdefs/SUNWcpr.i/postremove60
-rw-r--r--usr/src/pkgdefs/SUNWcpr.i/prototype_com50
-rw-r--r--usr/src/pkgdefs/SUNWcpr.i/prototype_i38660
-rw-r--r--usr/src/pkgdefs/etc/exception_list_i3865
-rw-r--r--usr/src/pkgdefs/etc/exception_list_sparc5
-rw-r--r--usr/src/uts/common/Makefile.files1
-rw-r--r--usr/src/uts/common/cpr/cpr_driver.c131
-rw-r--r--usr/src/uts/common/cpr/cpr_dump.c51
-rw-r--r--usr/src/uts/common/cpr/cpr_main.c692
-rw-r--r--usr/src/uts/common/cpr/cpr_misc.c32
-rw-r--r--usr/src/uts/common/cpr/cpr_mod.c191
-rw-r--r--usr/src/uts/common/cpr/cpr_stat.c25
-rw-r--r--usr/src/uts/common/cpr/cpr_uthread.c36
-rw-r--r--usr/src/uts/common/io/asy.c871
-rw-r--r--usr/src/uts/common/io/audio/sada/drv/audio810/audio810.c258
-rw-r--r--usr/src/uts/common/io/fdc.c111
-rw-r--r--usr/src/uts/common/io/i8042.c72
-rw-r--r--usr/src/uts/common/io/kb8042/kb8042.c90
-rw-r--r--usr/src/uts/common/io/kb8042/kb8042.h12
-rw-r--r--usr/src/uts/common/io/pci-ide/pci-ide.c74
-rw-r--r--usr/src/uts/common/io/pm.c359
-rw-r--r--usr/src/uts/common/io/ppm/ppm.c175
-rw-r--r--usr/src/uts/common/io/ppm/ppm_subr.c74
-rw-r--r--usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.c59
-rwxr-xr-xusr/src/uts/common/io/srn.c563
-rwxr-xr-xusr/src/uts/common/io/srn.conf27
-rw-r--r--usr/src/uts/common/io/usb/hcd/ehci/ehci.c7
-rw-r--r--usr/src/uts/common/io/usb/hcd/ehci/ehci_util.c3
-rw-r--r--usr/src/uts/common/io/usb/hcd/openhci/ohci.c4
-rw-r--r--usr/src/uts/common/os/callb.c11
-rw-r--r--usr/src/uts/common/os/cpu.c21
-rw-r--r--usr/src/uts/common/os/sunpci.c283
-rw-r--r--usr/src/uts/common/os/sunpm.c193
-rw-r--r--usr/src/uts/common/sys/Makefile1
-rw-r--r--usr/src/uts/common/sys/asy.h18
-rw-r--r--usr/src/uts/common/sys/audio/impl/audio810_impl.h14
-rw-r--r--usr/src/uts/common/sys/cpr.h49
-rw-r--r--usr/src/uts/common/sys/cpuvar.h1
-rw-r--r--usr/src/uts/common/sys/dktp/cmdk.h24
-rw-r--r--usr/src/uts/common/sys/dktp/dadk.h6
-rw-r--r--usr/src/uts/common/sys/epm.h118
-rw-r--r--usr/src/uts/common/sys/pm.h38
-rw-r--r--usr/src/uts/common/sys/ppmvar.h13
-rw-r--r--usr/src/uts/common/sys/rtc.h12
-rw-r--r--usr/src/uts/common/sys/srn.h79
-rw-r--r--usr/src/uts/common/sys/sunddi.h6
-rw-r--r--usr/src/uts/common/sys/uadmin.h17
-rw-r--r--usr/src/uts/common/syscall/uadmin.c12
-rw-r--r--usr/src/uts/i86pc/Makefile.files8
-rw-r--r--usr/src/uts/i86pc/Makefile.i86pc.shared11
-rw-r--r--usr/src/uts/i86pc/Makefile.rules13
-rw-r--r--usr/src/uts/i86pc/acpippm/Makefile91
-rw-r--r--usr/src/uts/i86pc/cpr/Makefile98
-rw-r--r--usr/src/uts/i86pc/genassym/Makefile2
-rw-r--r--usr/src/uts/i86pc/gfx_private/Makefile4
-rw-r--r--usr/src/uts/i86pc/io/acpippm.conf28
-rw-r--r--usr/src/uts/i86pc/io/cbe.c50
-rw-r--r--usr/src/uts/i86pc/io/consplat.c3
-rw-r--r--usr/src/uts/i86pc/io/gfx_private/gfxp_vgatext.c320
-rw-r--r--usr/src/uts/i86pc/io/mc/mcamd_drv.c20
-rw-r--r--usr/src/uts/i86pc/io/mp_platform_common.c182
-rw-r--r--usr/src/uts/i86pc/io/pci/pci.c68
-rw-r--r--usr/src/uts/i86pc/io/pciex/npe.c61
-rw-r--r--usr/src/uts/i86pc/io/pcplusmp/apic.c11
-rw-r--r--usr/src/uts/i86pc/io/ppm.conf46
-rw-r--r--usr/src/uts/i86pc/io/ppm/acpippm.c443
-rw-r--r--usr/src/uts/i86pc/io/ppm/acpippm.h41
-rw-r--r--usr/src/uts/i86pc/io/ppm/acpisleep.c214
-rw-r--r--usr/src/uts/i86pc/io/psm/psm_common.c38
-rw-r--r--usr/src/uts/i86pc/io/psm/uppc.c2
-rw-r--r--usr/src/uts/i86pc/io/todpc_subr.c229
-rw-r--r--usr/src/uts/i86pc/ml/cpr_wakecode.s1033
-rw-r--r--usr/src/uts/i86pc/ml/offsets.in50
-rw-r--r--usr/src/uts/i86pc/os/acpi_stubs.c52
-rw-r--r--usr/src/uts/i86pc/os/cpr_impl.c1096
-rw-r--r--usr/src/uts/i86pc/os/ddi_impl.c18
-rw-r--r--usr/src/uts/i86pc/os/memscrub.c26
-rw-r--r--usr/src/uts/i86pc/os/mp_implfuncs.c2
-rw-r--r--usr/src/uts/i86pc/os/mp_machdep.c19
-rw-r--r--usr/src/uts/i86pc/os/mp_pc.c18
-rw-r--r--usr/src/uts/i86pc/os/mp_startup.c2
-rw-r--r--usr/src/uts/i86pc/os/timestamp.c170
-rw-r--r--usr/src/uts/i86pc/sys/apic.h1
-rw-r--r--usr/src/uts/i86pc/sys/cpr_impl.h72
-rw-r--r--usr/src/uts/i86pc/sys/cpr_wakecode.h153
-rw-r--r--usr/src/uts/i86pc/sys/machclock.h18
-rw-r--r--usr/src/uts/i86pc/sys/machsystm.h2
-rw-r--r--usr/src/uts/i86pc/sys/psm_common.h2
-rw-r--r--usr/src/uts/i86pc/sys/psm_types.h47
-rw-r--r--usr/src/uts/i86pc/sys/smp_impldefs.h2
-rw-r--r--usr/src/uts/i86xpv/Makefile.files1
-rw-r--r--usr/src/uts/intel/Makefile.files3
-rw-r--r--usr/src/uts/intel/Makefile.intel.shared1
-rw-r--r--usr/src/uts/intel/ia32/ml/modstubs.s2
-rw-r--r--usr/src/uts/intel/io/acpica/acpica.c211
-rw-r--r--usr/src/uts/intel/io/agpgart/amd64_gart.c24
-rw-r--r--usr/src/uts/intel/io/dktp/controller/ata/ata_cmd.h5
-rw-r--r--usr/src/uts/intel/io/dktp/controller/ata/ata_common.c632
-rw-r--r--usr/src/uts/intel/io/dktp/controller/ata/ata_common.h15
-rw-r--r--usr/src/uts/intel/io/dktp/dcdev/dadk.c162
-rw-r--r--usr/src/uts/intel/io/dktp/disk/cmdk.c225
-rw-r--r--usr/src/uts/intel/io/dktp/hba/ghd/ghd_debug.c21
-rw-r--r--usr/src/uts/intel/io/pci/pci_pci.c56
-rw-r--r--usr/src/uts/intel/io/vgatext/vgatext.c219
-rw-r--r--usr/src/uts/intel/os/cpr_intel.c75
-rw-r--r--usr/src/uts/intel/os/name_to_major2
-rw-r--r--usr/src/uts/intel/promif/prom_env.c23
-rwxr-xr-xusr/src/uts/intel/srn/Makefile84
-rw-r--r--usr/src/uts/intel/sys/acpica.h2
-rw-r--r--usr/src/uts/intel/sys/promif.h10
-rw-r--r--usr/src/uts/sparc/os/cpr_sparc.c9
-rw-r--r--usr/src/uts/sun4u/os/cpr_impl.c120
-rw-r--r--usr/src/uts/sun4u/sys/machclock.h13
129 files changed, 11254 insertions, 1359 deletions
diff --git a/usr/src/cmd/devfsadm/i386/misc_link_i386.c b/usr/src/cmd/devfsadm/i386/misc_link_i386.c
index 566d2c88a9..22d36d2fc9 100644
--- a/usr/src/cmd/devfsadm/i386/misc_link_i386.c
+++ b/usr/src/cmd/devfsadm/i386/misc_link_i386.c
@@ -49,6 +49,7 @@ static int agp_process(di_minor_t minor, di_node_t node);
static int drm_node(di_minor_t minor, di_node_t node);
static int mc_node(di_minor_t minor, di_node_t node);
static int xsvc(di_minor_t minor, di_node_t node);
+static int srn(di_minor_t minor, di_node_t node);
static int ucode(di_minor_t minor, di_node_t node);
static devfsadm_create_t misc_cbt[] = {
@@ -94,6 +95,9 @@ static devfsadm_create_t misc_cbt[] = {
{ "pseudo", "ddi_pseudo", NULL,
TYPE_EXACT, ILEVEL_0, xsvc
},
+ { "pseudo", "ddi_pseudo", NULL,
+ TYPE_EXACT, ILEVEL_0, srn
+ },
{ "memory-controller", "ddi_mem_ctrl", NULL,
TYPE_EXACT, ILEVEL_0, mc_node
},
@@ -586,6 +590,25 @@ xsvc(di_minor_t minor, di_node_t node)
}
/*
+ * Creates \M0 devlink for srn device
+ */
+static int
+srn(di_minor_t minor, di_node_t node)
+{
+ char *mn;
+
+ if (strcmp(di_node_name(node), "srn") != 0)
+ return (DEVFSADM_CONTINUE);
+
+ mn = di_minor_name(minor);
+ if (mn == NULL)
+ return (DEVFSADM_CONTINUE);
+
+ (void) devfsadm_mklink(mn, node, minor, 0);
+ return (DEVFSADM_CONTINUE);
+}
+
+/*
* /dev/ucode -> /devices/pseudo/ucode@0:ucode
*/
static int
diff --git a/usr/src/cmd/mdb/i86pc/modules/pcplusmp/apic.c b/usr/src/cmd/mdb/i86pc/modules/pcplusmp/apic.c
index fb8824855a..2e048223ea 100644
--- a/usr/src/cmd/mdb/i86pc/modules/pcplusmp/apic.c
+++ b/usr/src/cmd/mdb/i86pc/modules/pcplusmp/apic.c
@@ -89,6 +89,121 @@ interrupt_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
return (DCMD_OK);
}
+/* Macros for reading/writing the IOAPIC RDT entries */
+#define READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic_ix, ipin) \
+ ioapic_read(ioapic_ix, APIC_RDT_CMD + (2 * (ipin)))
+
+#define READ_IOAPIC_RDT_ENTRY_HIGH_DWORD(ioapic_ix, ipin) \
+ ioapic_read(ioapic_ix, APIC_RDT_CMD2 + (2 * (ipin)))
+
+static uint32_t *ioapic_adr[MAX_IO_APIC];
+
+uint32_t
+ioapic_read(int ioapic_ix, uint32_t reg)
+{
+ volatile uint32_t *ioapic;
+
+ ioapic = ioapic_adr[ioapic_ix];
+ ioapic[APIC_IO_REG] = reg;
+ return (ioapic[APIC_IO_DATA]);
+}
+
+/*
+ * ioapic dcmd - Print out the ioapic registers, nicely formatted.
+ */
+/*ARGSUSED*/
+static int
+ioapic(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uint32_t apic_io_max;
+ int reg;
+ int reg_max;
+ int i;
+
+
+ if ((flags & DCMD_ADDRSPEC) || argc != 0)
+ return (DCMD_USAGE);
+
+ if (mdb_readvar(&ioapic_adr, "apicioadr") == -1) {
+ /*
+ * If the mdb_warn string does not end in a \n, mdb will
+ * automatically append the reason for the failure.
+ */
+ mdb_warn("failed to read ioapicadr");
+ return (DCMD_ERR);
+ }
+
+ if (mdb_readvar(&apic_io_max, "apic_io_max") == -1) {
+ /*
+ * If the mdb_warn string does not end in a \n, mdb will
+ * automatically append the reason for the failure.
+ */
+ mdb_warn("failed to read apic_io_max");
+ return (DCMD_ERR);
+ }
+
+ mdb_printf("ioapicadr\t%p\n", ioapic_adr);
+
+ for (i = 0; i < apic_io_max; i++) {
+ /* Bits 23-16 define the maximum redirection entries */
+ reg_max = ioapic_read(i, APIC_VERS_CMD);
+ reg_max = (reg_max >> 16) & 0xff;
+
+ mdb_printf("%4s %8s %8s\n", "reg", "high", " low");
+ for (reg = 0; reg <= reg_max; reg++) {
+ uint32_t high, low;
+
+ high = READ_IOAPIC_RDT_ENTRY_HIGH_DWORD(i, reg);
+ low = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(i, reg);
+
+ mdb_printf("%2d %8x %8x\n", reg, high, low);
+ }
+
+ mdb_printf("\n");
+
+ }
+
+ return (DCMD_OK);
+}
+
+
+/*
+ * apic dcmd - Print out the apic registers, nicely formatted.
+ */
+/*ARGSUSED*/
+static int
+apic(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uint32_t *papic;
+
+ if ((flags & DCMD_ADDRSPEC) || argc != 0)
+ return (DCMD_USAGE);
+
+ if (mdb_readvar(&papic, "apicadr") == -1) {
+ /*
+ * If the mdb_warn string does not end in a \n, mdb will
+ * automatically append the reason for the failure.
+ */
+ mdb_warn("failed to read apicadr");
+ return (DCMD_ERR);
+ }
+
+ mdb_printf("apicadr\t%p\n", papic);
+ mdb_printf("as_task_reg\t%x\n", papic[APIC_TASK_REG]);
+ mdb_printf("as_dest_reg\t%x\n", papic[APIC_DEST_REG]);
+ mdb_printf("as_format_reg\t%x\n", papic[APIC_FORMAT_REG]);
+ mdb_printf("as_local_timer\t%x\n", papic[APIC_LOCAL_TIMER]);
+ mdb_printf("as_pcint_vect\t%x\n", papic[APIC_PCINT_VECT]);
+ mdb_printf("as_int_vect0\t%x\n", papic[APIC_INT_VECT0]);
+ mdb_printf("as_int_vect1\t%x\n", papic[APIC_INT_VECT1]);
+ mdb_printf("as_err_vect\t%x\n", papic[APIC_ERR_VECT]);
+ mdb_printf("as_init_count\t%x\n", papic[APIC_INIT_COUNT]);
+ mdb_printf("as_divide_reg\t%x\n", papic[APIC_DIVIDE_REG]);
+ mdb_printf("as_spur_int_reg\t%x\n", papic[APIC_SPUR_INT_REG]);
+
+ return (DCMD_OK);
+}
+
/*
* MDB module linkage information:
@@ -101,6 +216,8 @@ static const mdb_dcmd_t dcmds[] = {
interrupt_help},
{ "softint", "?[-d]", "print soft interrupts", soft_interrupt_dump,
soft_interrupt_help},
+ { "apic", NULL, "print apic register contents", apic },
+ { "ioapic", NULL, "print ioapic register contents", ioapic },
{ NULL }
};
diff --git a/usr/src/cmd/power/Makefile b/usr/src/cmd/power/Makefile
index 9dd5337471..6fc647efc5 100644
--- a/usr/src/cmd/power/Makefile
+++ b/usr/src/cmd/power/Makefile
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -38,7 +38,8 @@ OBJS = $(SRCS:%.c=%.o)
SCRIPTS = sysidpm.sh
SYSIDPM = sysidpm
PROG = $(DAEMON) $(PMCFG) $(SYSIDPM)
-ETCFILES = power.conf
+POWERCONF= power.conf
+ETCFILES = $(POWERCONF)
POWERPERM = power
DEFAULTFILES = power.dfl
@@ -58,13 +59,14 @@ POFILES= $(PMCFG_SRCS:%.c=%.po)
# SCCS control here.
OPOFILES= sysidpm.po
-#
-# we only need libdevinfo on sparc
+# pmconfig only needs libdevinfo on sparc
sparc_LDEVINFO= -ldevinfo
i386_LDEVINFO=
-DAEMON_LDLIBS = $(LDLIBS.cmd) -lkstat $($(MACH)_LDEVINFO)
-PMCFG_LDLIBS = $(LDLIBS.cmd) $($(MACH)_LDEVINFO)
+LDEVINFO= -ldevinfo
+
+DAEMON_LDLIBS = $(LDLIBS.cmd) -lkstat $(LDEVINFO)
+PMCFG_LDLIBS = $(LDLIBS.cmd) -lcmd -lsmbios -lkstat $($(MACH)_LDEVINFO)
OWNER= root
ROOTUSRSBINPMCFG= $(PMCFG:%=$(ROOTUSRSBIN)/%)
@@ -98,6 +100,8 @@ all: $(PROG) $(POWERPERM).dfl $(ETCFILES) $(SCRIPTS)
install clean:
+$(POWERCONF): $(POWERCONF).$(MACH)
+
$(DAEMON_OBJS): $(DAEMON_SRCS)
$(CC) $(CFLAGS) -D_REENTRANT $(CPPFLAGS) -o $@ -c $<
$(PROCESS_COMMENT) $@
@@ -134,7 +138,7 @@ $(POFILE): $(POFILES) $(OPOFILES)
check: $(CHKMANIFEST)
clean:
- $(RM) $(OBJS) $(SYSIDPM)
+ $(RM) $(OBJS) $(SYSIDPM) $(POWERCONF)
$(RM) $(POFILE) $(POFILES)
lint := LINTFLAGS=-auxn
@@ -145,4 +149,8 @@ lint:
cstyle:
$(CSTYLE) $(SRCS)
+%: %.$(MACH)
+ $(RM) $@
+ cat $< > $@
+
include ../Makefile.targ
diff --git a/usr/src/cmd/power/conf.c b/usr/src/cmd/power/conf.c
index 5bea3f08c8..d84e3cd014 100644
--- a/usr/src/cmd/power/conf.c
+++ b/usr/src/cmd/power/conf.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -83,6 +82,13 @@ static int fflag, rflag;
int pm_fd;
uid_t ruid;
int def_src;
+/*
+ * Until we get more graphics driver support, we only enable autopm,
+ * S3 support and autoS3 by default on X86 systems that are on our whitelist.
+ */
+int whitelist_only = 1;
+
+int verify = 0;
static void
@@ -302,7 +308,10 @@ restart_powerd(void)
(void) setreuid(0, 0);
(void) setregid(0, 0);
(void) setgroups(0, NULL);
- (void) execle(powerd, powerd, NULL, NULL);
+ if (debug)
+ (void) execle(powerd, powerd, "-d", NULL, NULL);
+ else
+ (void) execle(powerd, powerd, NULL, NULL);
exit(1);
} else {
do {
@@ -503,6 +512,12 @@ main(int cnt, char **vec)
case 'r':
rflag = 1;
break;
+ case 'W':
+ whitelist_only = 0;
+ break;
+ case 'v':
+ verify = 1;
+ break;
default:
usage();
break;
diff --git a/usr/src/cmd/power/handlers.c b/usr/src/cmd/power/handlers.c
index 44df1f5c30..0754af4d6d 100644
--- a/usr/src/cmd/power/handlers.c
+++ b/usr/src/cmd/power/handlers.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -32,6 +32,9 @@
#include <sys/mnttab.h>
#include <syslog.h>
#include <stdlib.h>
+#include <sys/pm.h>
+#include <kstat.h>
+#include <sys/smbios.h>
#define STRCPYLIM(dst, src, str) strcpy_limit(dst, src, sizeof (dst), str)
@@ -44,6 +47,7 @@ static char bad_thresh_fmt[] = "bad threshold(s)\n";
static char stat_fmt[] = "cannot stat \"%s\", %s\n";
static char always_on[] = "always-on";
+#define PM_DEFAULT_ALGORITHM -1
/*
* When lines in a config file (usually "/etc/power.conf") start with
* a recognized keyword, a "handler" routine is called for specific
@@ -54,6 +58,59 @@ static char always_on[] = "always-on";
*/
+static char pm_cmd_string[32];
+
+static char *
+pm_map(int cmd)
+{
+ pm_req_t req;
+
+ req.value = cmd;
+ req.data = (void *)pm_cmd_string;
+ req.datasize = sizeof (pm_cmd_string);
+
+ if (ioctl(pm_fd, PM_GET_CMD_NAME, &req) < 0) {
+ perror("PM cmd name lookup:");
+ return ("??");
+ }
+ return (pm_cmd_string);
+}
+
+static int
+isonlist(char *listname, const char *man, const char *prod)
+{
+ pm_searchargs_t sl;
+ int ret;
+
+ sl.pms_listname = listname;
+ sl.pms_manufacturer = (char *)man;
+ sl.pms_product = (char *)prod;
+ ret = ioctl(pm_fd, PM_SEARCH_LIST, &sl);
+ mesg(MDEBUG, "PM_SEARCH_LIST %s for %s,%s returns %d\n",
+ listname, man, prod, ret);
+ return (ret == 0);
+}
+
+static int
+do_ioctl(int ioctl_cmd, char *keyword, char *behavior, int suppress)
+{
+ mesg(MDEBUG, "doing ioctl %s for %s ", pm_map(ioctl_cmd), keyword);
+ if (ioctl(pm_fd, ioctl_cmd, NULL) == -1) {
+ int suppressed = suppress == -1 || suppress == errno;
+ mesg(MDEBUG, "%s failed, %s (%ssuppressed)\n", behavior,
+ strerror(errno), (suppressed ? "" : "not "));
+ if (!suppressed) {
+ mesg(MERR, "%s %s failed, %s\n", keyword, behavior,
+ strerror(errno));
+ return (NOUP);
+ } else {
+ return (OKUP);
+ }
+ }
+ mesg(MDEBUG, "succeeded\n");
+ return (OKUP);
+}
+
/*
* Check for valid cpupm behavior and communicate it to the kernel.
*/
@@ -91,6 +148,231 @@ cpupm(void)
/*
+ * Two decisions are identical except for the list names and ioctl commands
+ * inputs: whitelist, blacklist, yes, no
+ * if (! ("S3" kstat exists))
+ * return (no)
+ * if (SystemInformation.Manufacturer == "Sun Microsystems" &&
+ * (Pref_PM_Profile == Workstation || Pref_PM_Profile == Desktop)) {
+ * if (platform on blacklist)
+ * return (no)
+ * return (yes)
+ * } else {
+ * if (platform on whitelist)
+ * return (yes)
+ * return (no)
+ * }
+ */
+
+int
+S3_helper(char *whitelist, char *blacklist, int yes, int no, char *keyword,
+ char *behavior, int *didyes, int suppress)
+{
+ int oflags = SMB_O_NOCKSUM | SMB_O_NOVERS;
+ smbios_hdl_t *shp;
+ smbios_system_t sys;
+ id_t id;
+ int ret;
+ kstat_ctl_t *kc;
+ kstat_t *ksp;
+ kstat_named_t *dp;
+ smbios_info_t info;
+ int preferred_pm_profile = 0;
+ char yesstr[32], nostr[32]; /* DEBUG */
+
+ *didyes = 0;
+
+ strncpy(yesstr, pm_map(yes), sizeof (yesstr));
+ strncpy(nostr, pm_map(no), sizeof (nostr));
+ mesg(MDEBUG, "S3_helper(%s, %s, %s, %s, %s, %s)\n", whitelist,
+ blacklist, yesstr, nostr, keyword, behavior);
+ if ((kc = kstat_open()) == NULL) {
+ mesg(MDEBUG, "kstat_open failed\n");
+ return (OKUP);
+ }
+ ksp = kstat_lookup(kc, "acpi", -1, "acpi");
+ if (ksp == NULL) {
+ mesg(MDEBUG, "kstat_lookup 'acpi', -1, 'acpi' failed\n");
+ kstat_close(kc);
+ return (OKUP);
+ }
+ (void) kstat_read(kc, ksp, NULL);
+ dp = kstat_data_lookup(ksp, "S3");
+ if (dp == NULL || dp->value.l == 0) {
+ mesg(MDEBUG, "kstat_data_lookup 'S3' fails\n");
+ if (dp != NULL)
+ mesg(MDEBUG, "value.l %lx\n", dp->value.l);
+ kstat_close(kc);
+ return (do_ioctl(no, keyword, behavior, suppress));
+ }
+ mesg(MDEBUG, "kstat indicates S3 support (%lx)\n", dp->value.l);
+
+ if (!whitelist_only) {
+ /*
+ * We still have an ACPI ksp, search it again for
+ * 'preferred_pm_profile' (needs to be valid if we don't
+ * aren't only using a whitelist).
+ */
+ dp = kstat_data_lookup(ksp, "preferred_pm_profile");
+ if (dp == NULL) {
+ mesg(MDEBUG, "kstat_data_lookup 'ppmp fails\n");
+ kstat_close(kc);
+ return (do_ioctl(no, keyword, behavior, suppress));
+ }
+ mesg(MDEBUG, "kstat indicates preffered_pm_profile is %lx\n",
+ dp->value.l);
+ preferred_pm_profile = dp->value.l;
+ }
+ kstat_close(kc);
+
+ if ((shp = smbios_open(NULL,
+ SMB_VERSION, oflags, &ret)) == NULL) {
+ /* we promised not to complain */
+ /* we bail leaving it to the kernel default */
+ mesg(MDEBUG, "smbios_open failed %d\n", errno);
+ return (OKUP);
+ }
+ if ((id = smbios_info_system(shp, &sys)) == SMB_ERR) {
+ mesg(MDEBUG, "smbios_info_system failed %d\n", errno);
+ smbios_close(shp);
+ return (OKUP);
+ }
+ if (smbios_info_common(shp, id, &info) == SMB_ERR) {
+ mesg(MDEBUG, "smbios_info_common failed %d\n", errno);
+ smbios_close(shp);
+ return (OKUP);
+ }
+ mesg(MDEBUG, "Manufacturer: %s\n", info.smbi_manufacturer);
+ mesg(MDEBUG, "Product: %s\n", info.smbi_product);
+ smbios_close(shp);
+
+ if (!whitelist_only) {
+#define PPP_DESKTOP 1
+#define PPP_WORKSTATION 3
+ if (strcmp(info.smbi_manufacturer, "Sun Microsystems") == 0 &&
+ (preferred_pm_profile == PPP_DESKTOP ||
+ preferred_pm_profile == PPP_WORKSTATION)) {
+ if (isonlist(blacklist,
+ info.smbi_manufacturer, info.smbi_product)) {
+ return (do_ioctl(no, keyword, behavior,
+ suppress));
+ } else {
+ ret = do_ioctl(yes, keyword, behavior,
+ suppress);
+ *didyes = (ret == OKUP);
+ return (ret);
+ }
+ }
+ }
+ if (isonlist(whitelist,
+ info.smbi_manufacturer, info.smbi_product)) {
+ ret = do_ioctl(yes, keyword, behavior, suppress);
+ *didyes = (ret == OKUP);
+ return (ret);
+ } else {
+ return (do_ioctl(no, keyword, behavior, suppress));
+ }
+}
+
+int
+S3sup(void) /* S3-support keyword handler */
+{
+ struct btoc {
+ char *behavior;
+ int cmd;
+ };
+ static struct btoc blist[] = {
+ "default", PM_DEFAULT_ALGORITHM,
+ "enable", PM_ENABLE_S3,
+ "disable", PM_DISABLE_S3,
+ NULL, 0
+ };
+ struct btoc *bp;
+ char *behavior;
+ int dontcare;
+
+ for (behavior = LINEARG(1), bp = blist; bp->cmd; bp++) {
+ if (strcmp(behavior, bp->behavior) == 0)
+ break;
+ }
+ if (bp->cmd == 0) {
+ mesg(MERR, "invalid S3-support behavior \"%s\"\n", behavior);
+ return (NOUP);
+ }
+
+
+ switch (bp->cmd) {
+
+ case PM_ENABLE_S3:
+ case PM_DISABLE_S3:
+ return (do_ioctl(bp->cmd, "S3-support", behavior, EBUSY));
+
+ case PM_DEFAULT_ALGORITHM:
+ /*
+ * we suppress errors in the "default" case because we
+ * already did an invisible default call, so we know we'll
+ * get EBUSY
+ */
+ return (S3_helper("S3-support-enable", "S3-support-disable",
+ PM_ENABLE_S3, PM_DISABLE_S3, "S3-support", behavior,
+ &dontcare, EBUSY));
+
+ default:
+ mesg(MERR, "S3-support %s failed, %s\n", behavior,
+ strerror(errno));
+ return (NOUP);
+ }
+}
+
+/*
+ * Check for valid autoS3 behavior and save after ioctl success.
+ */
+int
+autoS3(void)
+{
+ struct btoc {
+ char *behavior;
+ int cmd;
+ };
+ static struct btoc blist[] = {
+ "default", PM_DEFAULT_ALGORITHM,
+ "disable", PM_STOP_AUTOS3,
+ "enable", PM_START_AUTOS3,
+ NULL, 0
+ };
+ struct btoc *bp;
+ char *behavior;
+ int dontcare;
+
+ for (behavior = LINEARG(1), bp = blist; bp->cmd; bp++) {
+ if (strcmp(behavior, bp->behavior) == 0)
+ break;
+ }
+ if (bp->cmd == 0) {
+ mesg(MERR, "invalid autoS3 behavior \"%s\"\n", behavior);
+ return (NOUP);
+ }
+
+ switch (bp->cmd) {
+ default:
+ mesg(MERR, "autoS3 %s failed, %s\n",
+ behavior, strerror(errno));
+ mesg(MDEBUG, "unknown command\n", bp->cmd);
+ return (OKUP);
+
+ case PM_STOP_AUTOS3:
+ case PM_START_AUTOS3:
+ return (do_ioctl(bp->cmd, "autoS3", behavior, EBUSY));
+
+ case PM_DEFAULT_ALGORITHM:
+ return (S3_helper("S3-autoenable", "S3-autodisable",
+ PM_START_AUTOS3, PM_STOP_AUTOS3, "autoS3", behavior,
+ &dontcare, EBUSY));
+ }
+}
+
+
+/*
* Check for valid autopm behavior and save after ioctl success.
*/
int
@@ -101,7 +383,7 @@ autopm(void)
int cmd, Errno, isdef;
};
static struct btoc blist[] = {
- "default", PM_START_PM, EBUSY, 1,
+ "default", PM_START_PM, -1, 1,
"disable", PM_STOP_PM, EINVAL, 0,
"enable", PM_START_PM, EBUSY, 0,
NULL, 0, 0, 0,
@@ -121,6 +403,7 @@ autopm(void)
/*
* for "default" behavior, do not enable autopm if not ESTAR_V3
*/
+#if defined(__sparc)
if (!bp->isdef || (estar_vers == ESTAR_V3)) {
if (ioctl(pm_fd, bp->cmd, NULL) == -1 && errno != bp->Errno) {
mesg(MERR, "autopm %s failed, %s\n",
@@ -130,6 +413,30 @@ autopm(void)
}
(void) strcpy(new_cc.apm_behavior, behavior);
return (OKUP);
+#endif
+#if defined(__x86)
+ if (!bp->isdef) {
+ if (ioctl(pm_fd, bp->cmd, NULL) == -1 && errno != bp->Errno) {
+ mesg(MERR, "autopm %s failed, %s\n",
+ behavior, strerror(errno));
+ return (NOUP);
+ }
+ mesg(MDEBUG, "autopm %s succeeded\n", behavior);
+
+ return (OKUP);
+ } else {
+ int didenable;
+ int ret = S3_helper("autopm-enable", "autopm-disable",
+ PM_START_PM, PM_STOP_PM, "autopm", behavior, &didenable,
+ bp->Errno);
+ if (didenable) {
+ /* tell powerd to attach all devices */
+ new_cc.is_autopm_default = 1;
+ (void) strcpy(new_cc.apm_behavior, behavior);
+ }
+ return (ret);
+ }
+#endif
}
diff --git a/usr/src/cmd/power/parse.c b/usr/src/cmd/power/parse.c
index 650e2154cb..e7adff4d18 100644
--- a/usr/src/cmd/power/parse.c
+++ b/usr/src/cmd/power/parse.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -53,6 +53,8 @@ prmup_t pm_status = { 0, OKUP, "pm" };
* must appear before a substring like "dev".
*/
static cinfo_t conftab[] = {
+ "S3-support", S3sup, &pm_status, NULL, 2, 0, 1,
+ "autoS3", autoS3, &pm_status, NULL, 2, 0, 1,
"autopm", autopm, &pm_status, NULL, 2, 0, 1,
"autoshutdown", autosd, &cpr_status, as_cmt, 5, 0, 1,
"cpu-threshold", cputhr, &pm_status, NULL, 2, 0, 1,
@@ -373,6 +375,14 @@ parse_conf_file(char *name, vact_t action)
cinfo_t *cip;
int linc, cnt;
size_t llen;
+ int dontcare;
+
+ /*
+ * Do the "implied default" for autoS3
+ */
+ (void) S3_helper("S3-support-enable", "S3-support-disable",
+ PM_ENABLE_S3, PM_DISABLE_S3, "S3-support", "default",
+ &dontcare, -1);
file_buf = get_conf_data(name);
mesg(MDEBUG, "\nnow parsing \"%s\"...\n", name);
@@ -446,4 +456,46 @@ parse_conf_file(char *name, vact_t action)
lineno = 0;
free(file_buf);
+
+ if (verify) {
+ int ret = ioctl(pm_fd, PM_GET_PM_STATE, NULL);
+ if (ret < 0) {
+ mesg(MDEBUG, "Cannot get PM state: %s\n",
+ strerror(errno));
+ }
+ switch (ret) {
+ case PM_SYSTEM_PM_ENABLED:
+ mesg(MDEBUG, "Autopm Enabled\n");
+ break;
+ case PM_SYSTEM_PM_DISABLED:
+ mesg(MDEBUG, "Autopm Disabled\n");
+ break;
+ }
+ ret = ioctl(pm_fd, PM_GET_S3_SUPPORT_STATE, NULL);
+ if (ret < 0) {
+ mesg(MDEBUG, "Cannot get PM state: %s\n",
+ strerror(errno));
+ }
+ switch (ret) {
+ case PM_S3_SUPPORT_ENABLED:
+ mesg(MDEBUG, "S3 support Enabled\n");
+ break;
+ case PM_S3_SUPPORT_DISABLED:
+ mesg(MDEBUG, "S3 support Disabled\n");
+ break;
+ }
+ ret = ioctl(pm_fd, PM_GET_AUTOS3_STATE, NULL);
+ if (ret < 0) {
+ mesg(MDEBUG, "Cannot get PM state: %s\n",
+ strerror(errno));
+ }
+ switch (ret) {
+ case PM_AUTOS3_ENABLED:
+ mesg(MDEBUG, "AutoS3 Enabled\n");
+ break;
+ case PM_AUTOS3_DISABLED:
+ mesg(MDEBUG, "AutoS3 Disabled\n");
+ break;
+ }
+ }
}
diff --git a/usr/src/cmd/power/pmconfig.h b/usr/src/cmd/power/pmconfig.h
index e48c9a0289..33f26b63df 100644
--- a/usr/src/cmd/power/pmconfig.h
+++ b/usr/src/cmd/power/pmconfig.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -92,6 +92,9 @@ typedef struct cinfo cinfo_t;
typedef void (*vact_t)(char *, size_t, cinfo_t *);
+/* Suspend/Resume flags */
+extern int whitelist_only;
+extern int verify;
/*
* "conf.c"
@@ -105,7 +108,6 @@ extern uid_t ruid;
extern int def_src;
extern void mesg(int, char *, ...);
-
/*
* "parse.c"
*/
@@ -118,6 +120,9 @@ extern void parse_conf_file(char *, vact_t);
/*
* handlers.c
*/
+extern int S3_helper(char *, char *, int, int, char *, char *, int *, int);
+extern int S3sup(void);
+extern int autoS3(void);
extern int autopm(void);
extern int autosd(void);
extern int cpupm(void);
diff --git a/usr/src/cmd/power/power.conf.i386 b/usr/src/cmd/power/power.conf.i386
new file mode 100644
index 0000000000..0c1d37132b
--- /dev/null
+++ b/usr/src/cmd/power/power.conf.i386
@@ -0,0 +1,35 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# Power Management Configuration File
+#
+# This entry keeps removable media from being powered down unless the
+# console framebuffer and monitor are powered down
+# (See removable-media(9P))
+
+device-dependency-property removable-media /dev/fb
+
+autopm default
+autoS3 default
diff --git a/usr/src/cmd/power/power.conf b/usr/src/cmd/power/power.conf.sparc
index 47068b41f9..17064e630f 100644
--- a/usr/src/cmd/power/power.conf
+++ b/usr/src/cmd/power/power.conf.sparc
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,8 @@
#
# CDDL HEADER END
#
-# Copyright (c) 1996 - 2001 by Sun Microsystems, Inc.
-# All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
#
#pragma ident "%Z%%M% %I% %E% SMI"
#
diff --git a/usr/src/cmd/power/powerd.c b/usr/src/cmd/power/powerd.c
index 19e137ce02..a559041d0a 100644
--- a/usr/src/cmd/power/powerd.c
+++ b/usr/src/cmd/power/powerd.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
@@ -53,6 +53,7 @@
#include <sys/stropts.h> /* for INFTIM */
#include <sys/pbio.h>
#include <sys/cpr.h>
+#include <sys/srn.h>
#include <stdarg.h>
#include "powerd.h"
@@ -73,6 +74,7 @@ extern int last_nfs_activity(hrtime_t *, int);
#define TOD "/dev/tod"
#define PROM "/dev/openprom"
#define PB "/dev/power_button"
+#define SRN "/dev/srn"
#define LOGFILE "./powerd.log"
#define PBM_THREAD 0
@@ -108,9 +110,7 @@ static int autoshutdown_en;
static int do_idlecheck;
static int got_sighup;
static int estar_v2_prop;
-#ifdef sparc
static int estar_v3_prop;
-#endif
static int log_power_cycles_error = 0;
static int log_system_board_date_error = 0;
static int log_no_autoshutdown_warning = 0;
@@ -126,6 +126,11 @@ static char *power_button_cmd[] = {
"-h", "-d", ":0", NULL
};
+static char *autoS3_cmd[] = {
+ "/usr/openwin/bin/sys-suspend",
+ "-n", "-d", ":0", NULL
+};
+
static char pidpath[] = PIDPATH;
static char scratch[PATH_MAX];
static char *prog;
@@ -148,11 +153,10 @@ static int open_pidfile(char *);
static int write_pidfile(int, pid_t);
static int read_cpr_config(void);
static void system_activity_monitor(void);
-#ifdef sparc
+static void autos3_monitor(void);
static void do_attach(void);
static void *attach_devices(void *);
-#endif
-
+static int powerd_debug;
/* PRINTFLIKE1 */
static void
@@ -205,8 +209,11 @@ main(int argc, char *argv[])
* Process options
*/
broadcast = 1;
- while ((c = getopt(argc, argv, "n")) != EOF) {
+ while ((c = getopt(argc, argv, "nd")) != EOF) {
switch (c) {
+ case 'd':
+ powerd_debug = 1;
+ break;
case 'n':
broadcast = 0;
break;
@@ -230,7 +237,7 @@ main(int argc, char *argv[])
*/
if (mutex_init(&poweroff_mutex, USYNC_THREAD, NULL) != 0) {
(void) fprintf(stderr,
- "%s: Unable to initialize mutex lock\n", prog);
+ "%s: Unable to initialize mutex lock\n", prog);
exit(EXIT_FAILURE);
}
@@ -296,6 +303,8 @@ main(int argc, char *argv[])
* thread to monitor the power button.
*/
if ((pb_fd = open(PB, O_RDONLY)) != -1) {
+ if (powerd_debug)
+ logerror("powerd starting power button monitor.");
if (thr_create(NULL, NULL,
(void *(*)(void *))power_button_monitor, NULL,
THR_DAEMON, NULL) != 0) {
@@ -303,14 +312,14 @@ main(int argc, char *argv[])
}
}
-#ifdef sparc
do_attach();
-#endif
/*
* Create a new thread to monitor system activity and suspend
* system if idle.
*/
+ if (powerd_debug)
+ logerror("powerd starting system activity monitor.");
if (thr_create(NULL, NULL,
(void *(*)(void *))system_activity_monitor, NULL,
THR_DAEMON, NULL) != 0) {
@@ -318,6 +327,16 @@ main(int argc, char *argv[])
}
/*
+ * Create a new thread to handle autos3 trigger
+ */
+ if (powerd_debug)
+ logerror("powerd starting autos3 monitor.");
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))autos3_monitor, NULL, THR_DAEMON, NULL) != 0) {
+ logerror("Unable to create thread to monitor autos3 activity.");
+ }
+
+ /*
* Block until we receive an explicit terminate signal
*/
(void) sigsuspend(&sigmask);
@@ -370,6 +389,68 @@ system_activity_monitor(void)
} while (errno == EINTR);
}
+static void
+autos3_monitor(void)
+{
+ struct pollfd poll_fd;
+ srn_event_info_t srn_event; /* contains suspend type */
+ int fd, ret;
+
+ fd = open(SRN, O_RDWR|O_EXCL|O_NDELAY);
+ if (fd == -1) {
+ logerror("Unable to open %s: %s", SRN, strerror(errno));
+ thr_exit((void *) errno);
+ }
+ logerror("Able to open %s", SRN);
+
+ /*
+ * Tell device we want the special sauce
+ */
+ ret = ioctl(fd, SRN_IOC_AUTOSX, NULL);
+ if (ret == -1) {
+ logerror("Ioctl SRN_IOC_AUTOSX failed: %s", strerror(errno));
+ close(fd);
+ thr_exit((void *) errno);
+ }
+ poll_fd.fd = fd;
+ /*CONSTCOND*/
+ while (1) {
+ poll_fd.revents = 0;
+ poll_fd.events = POLLIN;
+ if (poll(&poll_fd, 1, -1) < 0) {
+ switch (errno) {
+ case EINTR:
+ case EAGAIN:
+ continue;
+ default:
+ logerror("Poll error: %s", strerror(errno));
+ close(fd);
+ thr_exit((void *) errno);
+ }
+ }
+
+ ret = ioctl(fd, SRN_IOC_NEXTEVENT, &srn_event);
+ if (ret == -1) {
+ logerror("ioctl error: %s", strerror(errno));
+ close(fd);
+ thr_exit((void *) errno);
+ }
+ switch (srn_event.ae_type) {
+ case 3: /* S3 */
+ if (powerd_debug)
+ logerror("ioctl returns type: %d",
+ srn_event.ae_type);
+ break;
+ default:
+ logerror("Unsupported target state %d",
+ srn_event.ae_type);
+ continue;
+ }
+ (void) poweroff("AutoS3", autoS3_cmd);
+ continue;
+ }
+}
+
static int
read_cpr_config(void)
{
@@ -482,7 +563,7 @@ work_handler(int sig)
} else if (strcmp(asinfo.as_behavior, "default") == 0) {
info->pd_autoshutdown = estar_v2_prop;
} else if (strcmp(asinfo.as_behavior, "shutdown") == 0 ||
- strcmp(asinfo.as_behavior, "autowakeup") == 0) {
+ strcmp(asinfo.as_behavior, "autowakeup") == 0) {
info->pd_autoshutdown = asinfo.is_cpr_capable;
} else {
logerror("autoshutdown behavior \"%s\" unrecognized.",
@@ -500,14 +581,15 @@ work_handler(int sig)
(strcmp(asinfo.as_behavior, "autowakeup") == 0) ? 1 : 0;
}
autoshutdown_en = (asinfo.as_idle >= 0 && info->pd_autoshutdown)
- ? 1 : 0;
+ ? 1 : 0;
#ifdef DEBUG
(void) fprintf(stderr, "autoshutdown_en = %d, as_idle = %d, "
- "pd_autoresume = %d\n",
- autoshutdown_en, asinfo.as_idle, info->pd_autoresume);
+ "pd_autoresume = %d\n",
+ autoshutdown_en, asinfo.as_idle, info->pd_autoresume);
+
(void) fprintf(stderr, " pd_start_time=%d, pd_finish_time=%d\n",
- info->pd_start_time, info->pd_finish_time);
+ info->pd_start_time, info->pd_finish_time);
#endif
got_sighup = 1;
@@ -574,11 +656,10 @@ check_shutdown(time_t *now, hrtime_t *hr_now)
start_calc = 1;
time_since_last_resume = time(NULL) - last_resume;
next_time = info->pd_idle_time * 60 -
- MIN(least_idle, time_since_last_resume);
+ MIN(least_idle, time_since_last_resume);
#ifdef DEBUG
- fprintf(stderr, " check_shutdown: next_time=%d\n",
- next_time);
+ fprintf(stderr, " check_shutdown: next_time=%d\n", next_time);
#endif
/*
@@ -591,7 +672,7 @@ check_shutdown(time_t *now, hrtime_t *hr_now)
start_calc = 1;
time_since_last_resume = time(NULL) - last_resume;
next_time = info->pd_idle_time * 60 -
- MIN(least_idle, time_since_last_resume);
+ MIN(least_idle, time_since_last_resume);
}
/*
@@ -602,8 +683,8 @@ check_shutdown(time_t *now, hrtime_t *hr_now)
got_sighup = 0;
idlecheck_time = run_idlecheck();
next_time = info->pd_idle_time * 60 -
- MIN(idlecheck_time, MIN(least_idle,
- time_since_last_resume));
+ MIN(idlecheck_time, MIN(least_idle,
+ time_since_last_resume));
/*
* If we have caught SIGTHAW or SIGHUP, need to
* recalculate.
@@ -613,10 +694,10 @@ check_shutdown(time_t *now, hrtime_t *hr_now)
got_sighup = 0;
idlecheck_time = run_idlecheck();
time_since_last_resume = time(NULL) -
- last_resume;
+ last_resume;
next_time = info->pd_idle_time * 60 -
- MIN(idlecheck_time, MIN(least_idle,
- time_since_last_resume));
+ MIN(idlecheck_time, MIN(least_idle,
+ time_since_last_resume));
}
}
@@ -631,7 +712,7 @@ check_shutdown(time_t *now, hrtime_t *hr_now)
tod_fd = open(TOD, O_RDWR);
if (info->pd_autoresume && tod_fd != -1) {
wakeup_time = (*now < f) ? f :
- (f + DAYS_TO_SECS);
+ (f + DAYS_TO_SECS);
/*
* A software fix for hardware
* bug 1217415.
@@ -645,10 +726,9 @@ check_shutdown(time_t *now, hrtime_t *hr_now)
return;
}
if (ioctl(tod_fd, TOD_SET_ALARM,
- &wakeup_time) == -1) {
- logerror("Unable to program "
- "TOD alarm for "
- "autowakeup.");
+ &wakeup_time) == -1) {
+ logerror("Unable to program TOD"
+ " alarm for autowakeup.");
close(tod_fd);
return;
}
@@ -659,7 +739,7 @@ check_shutdown(time_t *now, hrtime_t *hr_now)
if (info->pd_autoresume && tod_fd != -1) {
if (ioctl(tod_fd, TOD_CLEAR_ALARM,
- NULL) == -1)
+ NULL) == -1)
logerror("Unable to clear "
"alarm in TOD device.");
close(tod_fd);
@@ -668,8 +748,8 @@ check_shutdown(time_t *now, hrtime_t *hr_now)
(void) time(now);
/* wait at least 5 mins */
shutdown_time = *now +
- ((info->pd_idle_time * 60) > 300 ?
- (info->pd_idle_time * 60) : 300);
+ ((info->pd_idle_time * 60) > 300 ?
+ (info->pd_idle_time * 60) : 300);
} else {
/* wait 5 mins */
shutdown_time = *now + 300;
@@ -698,8 +778,8 @@ is_ok2shutdown(time_t *now)
/* CONSTCOND */
while (1) {
if ((prom_fd = open(PROM, O_RDWR)) == -1 &&
- (errno == EAGAIN))
- continue;
+ (errno == EAGAIN))
+ continue;
break;
}
@@ -760,15 +840,14 @@ is_ok2shutdown(time_t *now)
* 7-year life span instead of (lifetime - date free_cycles ended).
*/
scaled_cycles = (int)(((float)life_passed / (float)LIFETIME_SECS) *
- (power_cycle_limit - free_cycles));
+ (power_cycle_limit - free_cycles));
if (no_power_cycles)
goto ckdone;
#ifdef DEBUG
(void) fprintf(stderr, "Actual power_cycles = %d\t"
- "Scaled power_cycles = %d\n",
- power_cycles, scaled_cycles);
+ "Scaled power_cycles = %d\n", power_cycles, scaled_cycles);
#endif
if (power_cycles > scaled_cycles) {
if (log_no_autoshutdown_warning == 0) {
@@ -807,13 +886,13 @@ check_idleness(time_t *now, hrtime_t *hr_now)
#ifdef DEBUG
(void) fprintf(stderr, "Idle ttychars for %d secs.\n",
- info->pd_ttychars_idle);
+ info->pd_ttychars_idle);
(void) fprintf(stderr, "Idle loadaverage for %d secs.\n",
- info->pd_loadaverage_idle);
+ info->pd_loadaverage_idle);
(void) fprintf(stderr, "Idle diskreads for %d secs.\n",
- info->pd_diskreads_idle);
+ info->pd_diskreads_idle);
(void) fprintf(stderr, "Idle nfsreqs for %d secs.\n",
- info->pd_nfsreqs_idle);
+ info->pd_nfsreqs_idle);
#endif
checkidle_time = *now + IDLECHK_INTERVAL;
@@ -848,7 +927,8 @@ run_idlecheck()
/*
* Reap any child process which has been left over.
*/
- while (waitpid((pid_t)-1, &status, WNOHANG) > 0);
+ while (waitpid((pid_t)-1, &status, WNOHANG) > 0)
+ ;
/*
* Execute the user's idlecheck script and set variable PM_IDLETIME.
@@ -856,7 +936,7 @@ run_idlecheck()
*/
if ((child = fork1()) == 0) {
(void) sprintf(pm_variable, "PM_IDLETIME=%d",
- info->pd_idle_time);
+ info->pd_idle_time);
(void) putenv(pm_variable);
cp = strrchr(asinfo.idlecheck_path, '/');
if (cp == NULL)
@@ -1136,7 +1216,6 @@ power_button_monitor(void *arg)
}
}
-#ifdef sparc
static void
do_attach(void)
{
@@ -1151,6 +1230,8 @@ do_attach(void)
estar_v3_prop = asinfo.is_autopm_default;
if ((strcmp(asinfo.apm_behavior, "enable") == 0) ||
(estar_v3_prop && strcmp(asinfo.apm_behavior, "default") == 0)) {
+ if (powerd_debug)
+ logerror("powerd starting device attach thread.");
if (thr_create(NULL, NULL, attach_devices, NULL,
THR_DAEMON, NULL) != 0) {
logerror("Unable to create thread to attach devices.");
@@ -1179,7 +1260,6 @@ attach_devices(void *arg)
return (NULL);
}
-#endif
/*
@@ -1199,7 +1279,7 @@ open_pidfile(char *me)
const char *e3 = "%s: Cannot open /proc for pid %ld: ";
const char *e4 = "%s: Cannot read /proc for pid %ld: ";
const char *e5 = "%s: Another instance (pid %ld) is trying to exit"
- "and may be hung. Please contact sysadmin.\n";
+ "and may be hung. Please contact sysadmin.\n";
const char *e6 = "%s: Another daemon is running\n";
const char *e7 = "%s: Cannot create pid file: ";
diff --git a/usr/src/cmd/uadmin/uadmin.c b/usr/src/cmd/uadmin/uadmin.c
index 6c5ed10f9f..1161bd992b 100644
--- a/usr/src/cmd/uadmin/uadmin.c
+++ b/usr/src/cmd/uadmin/uadmin.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -64,7 +64,8 @@ main(int argc, char *argv[])
cmd = atoi(argv[1]);
fcn = atoi(argv[2]);
if (argc == 4) { /* mdep argument given */
- if (cmd != A_REBOOT && cmd != A_SHUTDOWN && cmd != A_DUMP) {
+ if (cmd != A_REBOOT && cmd != A_SHUTDOWN && cmd != A_DUMP &&
+ cmd != A_FREEZE) {
(void) fprintf(stderr, "%s: mdep argument not "
"allowed for this cmd value\n", argv[0]);
(void) fprintf(stderr, Usage, argv[0]);
diff --git a/usr/src/pkgdefs/Makefile b/usr/src/pkgdefs/Makefile
index f98abc3fa9..7843cd9174 100644
--- a/usr/src/pkgdefs/Makefile
+++ b/usr/src/pkgdefs/Makefile
@@ -117,6 +117,7 @@ i386_SUBDIRS= \
SUNWcar.i \
SUNWcarx.i \
SUNWcpc.i \
+ SUNWcpr.i \
SUNWdfb.i \
SUNWdrmr \
SUNWgrub \
diff --git a/usr/src/pkgdefs/SUNWcakr.i/prototype_com b/usr/src/pkgdefs/SUNWcakr.i/prototype_com
index 11eae3f62d..10f820f3dd 100644
--- a/usr/src/pkgdefs/SUNWcakr.i/prototype_com
+++ b/usr/src/pkgdefs/SUNWcakr.i/prototype_com
@@ -77,6 +77,10 @@ d none platform/i86pc/kernel/dacf/amd64 755 root sys
f none platform/i86pc/kernel/dacf/amd64/consconfig_dacf 755 root sys
d none platform/i86pc/kernel/drv 755 root sys
d none platform/i86pc/kernel/drv/amd64 755 root sys
+f none platform/i86pc/kernel/drv/amd64/acpippm 755 root sys
+f none platform/i86pc/kernel/drv/acpippm 755 root sys
+f none platform/i86pc/kernel/drv/acpippm.conf 644 root sys
+f none platform/i86pc/kernel/drv/amd64/ppm 755 root sys
f none platform/i86pc/kernel/drv/amd64/isa 755 root sys
f none platform/i86pc/kernel/drv/amd64/mc-amd 755 root sys
f none platform/i86pc/kernel/drv/amd64/npe 755 root sys
@@ -114,4 +118,3 @@ f none platform/i86pc/multiboot 755 root sys
d none platform/i86pc/ucode 755 root sys
v none platform/i86pc/ucode/intel-ucode.txt 444 root sys
f none platform/i86pc/kernel/drv/amd64/cpudrv 755 root sys
-f none platform/i86pc/kernel/drv/amd64/ppm 755 root sys
diff --git a/usr/src/pkgdefs/SUNWcpr.i/Makefile b/usr/src/pkgdefs/SUNWcpr.i/Makefile
new file mode 100644
index 0000000000..5a3782f224
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWcpr.i/Makefile
@@ -0,0 +1,37 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+DATAFILES += depend
+
+.KEEP_STATE:
+
+all: $(FILES)
+install: all pkg
+
+include ../Makefile.targ
diff --git a/usr/src/pkgdefs/SUNWcpr.i/pkginfo.tmpl b/usr/src/pkgdefs/SUNWcpr.i/pkginfo.tmpl
new file mode 100644
index 0000000000..e682d41925
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWcpr.i/pkginfo.tmpl
@@ -0,0 +1,52 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWcpr"
+NAME="Suspend, Resume package"
+ARCH="i386.i86pc"
+VERSION="ONVERS,REV=0.0.0"
+CATEGORY="system"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+DESC="Suspend, Resume package"
+BASEDIR=/
+SUNW_PKGVERS="1.0"
+SUNW_PKGTYPE="root"
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+MAXINST="1000"
+CLASSES="none"
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="true"
+SUNW_PKG_THISZONE="false"
diff --git a/usr/src/pkgdefs/SUNWcpr.i/postinstall b/usr/src/pkgdefs/SUNWcpr.i/postinstall
new file mode 100644
index 0000000000..37cf851cd3
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWcpr.i/postinstall
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# SUNWcpr postinstall script
+
+#
+# Unload old copy of cpr from system
+#
+
+if [ "$BASEDIR" = "/" ] ; then
+ old_cpr=`/usr/sbin/modinfo | grep -w "cpr" | awk '{print $1}'`
+ if [ "X${old_cpr}" != "X" ] ; then
+ /usr/sbin/modunload -i ${old_cpr}
+ fi
+fi
+
+exit 0
diff --git a/usr/src/pkgdefs/SUNWcpr.i/postremove b/usr/src/pkgdefs/SUNWcpr.i/postremove
new file mode 100644
index 0000000000..09f00bf613
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWcpr.i/postremove
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# SUNWcpr postremove script
+
+#
+# Unload old copy of cpr from system
+# In the current version of x86, none of the files to be removed will exist
+#
+
+CPRLOWER="${PKG_INSTALL_ROOT}/.cpr_generic_info \
+ ${PKG_INSTALL_ROOT}/.cpr_turbo_info \
+ ${PKG_INSTALL_ROOT}/.cpr_defaultboot_info \
+ ${PKG_INSTALL_ROOT}/.cpr_config \
+ ${PKG_INSTALL_ROOT}/.cpr_default"
+CPRFILES="${CPRLOWER} ${PKG_INSTALL_ROOT}/.CPR_TB ${PKG_INSTALL_ROOT}/.CPR"
+
+if [ "$BASEDIR" = "/" ] ; then
+ old_cpr=`/usr/sbin/modinfo | grep -w "cpr" | awk '{print $1}'`
+ if [ "X${old_cpr}" != "X" ] ; then
+ /usr/sbin/modunload -i ${old_cpr}
+ fi
+
+ if [ -s ${PKG_INSTALL_ROOT}/etc/power.conf ]; then
+ state_filesystem=`/usr/bin/grep "^[ ]*statefile[ ]" \
+ ${PKG_INSTALL_ROOT}/etc/power.conf`
+ if [ "X${state_filesystem}" != "X" ] ; then
+ set - ${state_filesystem}
+ CPRSF=${PKG_INSTALL_ROOT}/"$2"
+ fi
+ fi
+
+ rm -f ${CPRFILES} ${CPRSF}
+fi
+
+exit 0
diff --git a/usr/src/pkgdefs/SUNWcpr.i/prototype_com b/usr/src/pkgdefs/SUNWcpr.i/prototype_com
new file mode 100644
index 0000000000..f3cba0b721
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWcpr.i/prototype_com
@@ -0,0 +1,50 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+
+# packaging files
+i pkginfo
+i copyright
+i depend
+#
+i postremove
+i postinstall
+# source locations relative to the prototype file
+#
+# SUNWcpr.i
+#
+d none kernel 0755 root sys
+d none kernel/drv 0755 root sys
+d none platform 0755 root sys
diff --git a/usr/src/pkgdefs/SUNWcpr.i/prototype_i386 b/usr/src/pkgdefs/SUNWcpr.i/prototype_i386
new file mode 100644
index 0000000000..063a56632a
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWcpr.i/prototype_i386
@@ -0,0 +1,60 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are x86 specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWcpr.i
+#
+f none kernel/drv/srn 755 root sys
+f none kernel/drv/srn.conf 644 root sys
+d none kernel/drv/amd64 0755 root sys
+f none kernel/drv/amd64/srn 755 root sys
+d none platform/i86pc 0755 root sys
+d none platform/i86pc/kernel 0755 root sys
+d none platform/i86pc/kernel/misc 0755 root sys
+f none platform/i86pc/kernel/misc/cpr 0755 root sys
+d none platform/i86pc/kernel/misc/amd64 0755 root sys
+f none platform/i86pc/kernel/misc/amd64/cpr 0755 root sys
diff --git a/usr/src/pkgdefs/etc/exception_list_i386 b/usr/src/pkgdefs/etc/exception_list_i386
index 175f5276c8..58fc7ddd64 100644
--- a/usr/src/pkgdefs/etc/exception_list_i386
+++ b/usr/src/pkgdefs/etc/exception_list_i386
@@ -894,3 +894,8 @@ usr/include/sys/kiconv_latin1.h i386
# At this time, the ttydefs.cleanup file is only useful on sun4u systems
#
etc/flash/postdeployment/ttydefs.cleanup i386
+#
+# This header file is shared only between the power commands and
+# ppm/srn modules # and should not be in any package
+#
+usr/include/sys/srn.h i386
diff --git a/usr/src/pkgdefs/etc/exception_list_sparc b/usr/src/pkgdefs/etc/exception_list_sparc
index 5fe6ef0e3e..cc929f34b0 100644
--- a/usr/src/pkgdefs/etc/exception_list_sparc
+++ b/usr/src/pkgdefs/etc/exception_list_sparc
@@ -977,3 +977,8 @@ usr/include/sqlite/sqlite.h sparc
usr/include/sys/kiconv_emea1.h sparc
usr/include/sys/kiconv_emea2.h sparc
usr/include/sys/kiconv_latin1.h sparc
+#
+# This header file is shared only between the power commands and
+# ppm/srn modules # and should not be in any package
+#
+usr/include/sys/srn.h sparc
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index 1575dd888f..21071a1742 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -616,6 +616,7 @@ OPTIONS_OBJS += options.o
WINLOCK_OBJS += winlockio.o
PM_OBJS += pm.o
+SRN_OBJS += srn.o
PSEUDO_OBJS += pseudonex.o
diff --git a/usr/src/uts/common/cpr/cpr_driver.c b/usr/src/uts/common/cpr/cpr_driver.c
index a23a9cbf7c..442473c7ca 100644
--- a/usr/src/uts/common/cpr/cpr_driver.c
+++ b/usr/src/uts/common/cpr/cpr_driver.c
@@ -45,6 +45,18 @@ extern int devi_attach(dev_info_t *, int);
static char *devi_string(dev_info_t *, char *);
static int cpr_is_real_device(dev_info_t *);
+/*
+ * Xen uses this code to suspend _all_ drivers quickly and easily.
+ * Suspend and Resume uses it for the same reason, but also has
+ * to contend with some platform specific code that Xen does not.
+ * it is also used as a test entry point for developers/testers to
+ * execute code without going through a complete suspend. So additions
+ * that have platform implications shall need #if[n]def's.
+ */
+#ifndef __xpv
+extern void i_cpr_save_configuration(dev_info_t *);
+extern void i_cpr_restore_configuration(dev_info_t *);
+#endif
/*
* Traverse the dev info tree:
@@ -70,22 +82,52 @@ cpr_suspend_devices(dev_info_t *dip)
devi_string(dip, buf));
ASSERT((DEVI(dip)->devi_cpr_flags & DCF_CPR_SUSPENDED) == 0);
- if (!i_ddi_devi_attached(dip))
+#ifndef __xpv
+ i_cpr_save_configuration(dip);
+#endif
+
+
+ if (!i_ddi_devi_attached(dip)) {
error = DDI_FAILURE;
- else
- error = devi_detach(dip, DDI_SUSPEND);
+ } else {
+#ifndef __xpv
+ if (cpr_test_point != DEVICE_SUSPEND_TO_RAM ||
+ (cpr_test_point == DEVICE_SUSPEND_TO_RAM &&
+ cpr_device == ddi_driver_major(dip))) {
+#endif
+ error = devi_detach(dip, DDI_SUSPEND);
+#ifndef __xpv
+ } else {
+ error = DDI_SUCCESS;
+ }
+#endif
+ }
- if (error == DDI_SUCCESS)
+ if (error == DDI_SUCCESS) {
DEVI(dip)->devi_cpr_flags |= DCF_CPR_SUSPENDED;
+ }
+
else {
CPR_DEBUG(CPR_DEBUG2,
"WARNING: Unable to suspend device %s\n",
devi_string(dip, buf));
cpr_err(CE_WARN, "Unable to suspend device %s.",
- devi_string(dip, buf));
+ devi_string(dip, buf));
cpr_err(CE_WARN, "Device is busy or does not "
- "support suspend/resume.");
- return (ENXIO);
+ "support suspend/resume.");
+#ifndef __xpv
+ /*
+ * the device has failed to suspend however,
+ * if cpr_test_point == FORCE_SUSPEND_TO_RAM
+ * after putting out the warning message above,
+ * we carry on as if suspending the device had
+ * been successful
+ */
+ if (cpr_test_point == FORCE_SUSPEND_TO_RAM)
+ DEVI(dip)->devi_cpr_flags |= DCF_CPR_SUSPENDED;
+ else
+#endif
+ return (ENXIO);
}
}
return (0);
@@ -124,13 +166,27 @@ cpr_resume_devices(dev_info_t *start, int resume_failed)
DEVI(dip)->devi_cpr_flags &= ~DCF_CPR_SUSPENDED;
/*
+ * Always attempt to restore device configuration before
+ * attempting resume
+ */
+#ifndef __xpv
+ i_cpr_restore_configuration(dip);
+#endif
+
+ /*
* There may be background attaches happening on devices
* that were not originally suspended by cpr, so resume
* only devices that were suspended by cpr. Also, stop
* resuming after the first resume failure, but traverse
- * the entire tree to clear the suspend flag.
+ * the entire tree to clear the suspend flag unless the
+ * FORCE_SUSPEND_TO_RAM test point is set.
*/
+#ifndef __xpv
+ if (did_suspend && (!error ||
+ cpr_test_point == FORCE_SUSPEND_TO_RAM)) {
+#else
if (did_suspend && !error) {
+#endif
CPR_DEBUG(CPR_DEBUG2, "Resuming device %s\n",
devi_string(dip, buf));
/*
@@ -146,17 +202,28 @@ cpr_resume_devices(dev_info_t *start, int resume_failed)
cpr_err(CE_WARN, "Skipping %s, device "
"not ready for resume",
devi_string(dip, buf));
- } else if (devi_attach(dip, DDI_RESUME) !=
- DDI_SUCCESS) {
- CPR_DEBUG(CPR_DEBUG2,
- "WARNING: Unable to resume device %s\n",
- devi_string(dip, buf));
- cpr_err(CE_WARN, "Unable to resume device %s",
- devi_string(dip, buf));
- error = ENXIO;
+#ifndef __xpv
+ } else if (cpr_test_point != DEVICE_SUSPEND_TO_RAM ||
+ (cpr_test_point == DEVICE_SUSPEND_TO_RAM &&
+ cpr_device == ddi_driver_major(dip))) {
+#else
+ } else {
+#endif
+ if (devi_attach(dip, DDI_RESUME) !=
+ DDI_SUCCESS) {
+ error = ENXIO;
+ }
}
}
+ if (error == ENXIO) {
+ CPR_DEBUG(CPR_DEBUG2,
+ "WARNING: Unable to resume device %s\n",
+ devi_string(dip, buf));
+ cpr_err(CE_WARN, "Unable to resume device %s",
+ devi_string(dip, buf));
+ }
+
error = cpr_resume_devices(ddi_get_child(dip), error);
last = dip;
}
@@ -176,10 +243,8 @@ devi_string(dev_info_t *devi, char *buf)
name = ddi_node_name(devi);
address = ddi_get_name_addr(devi);
- size = (name == NULL) ?
- strlen("<null name>") : strlen(name);
- size += (address == NULL) ?
- strlen("<null>") : strlen(address);
+ size = (name == NULL) ? strlen("<null name>") : strlen(name);
+ size += (address == NULL) ? strlen("<null>") : strlen(address);
/*
* Make sure that we don't over-run the buffer.
@@ -237,29 +302,3 @@ cpr_is_real_device(dev_info_t *dip)
return (1);
}
}
-
-/*
- * Power down the system.
- */
-void
-cpr_power_down(void)
-{
-#if defined(__sparc)
- /*
- * XXX This platform firmware implementation dependency
- * doesn't belong in common code!
- */
- int is_defined = 0;
- char *wordexists = "p\" power-off\" find nip swap l! ";
- char *req = "power-off";
-
- /*
- * is_defined has value -1 when defined
- */
- prom_interpret(wordexists, (uintptr_t)&is_defined, 0, 0, 0, 0);
- if (is_defined) {
- CPR_DEBUG(CPR_DEBUG1, "\ncpr: %s...\n", req);
- prom_interpret(req, 0, 0, 0, 0, 0);
- }
-#endif
-}
diff --git a/usr/src/uts/common/cpr/cpr_dump.c b/usr/src/uts/common/cpr/cpr_dump.c
index 99f5cea43d..28fee49bf9 100644
--- a/usr/src/uts/common/cpr/cpr_dump.c
+++ b/usr/src/uts/common/cpr/cpr_dump.c
@@ -54,17 +54,20 @@
#include <sys/ddi.h>
#include <sys/panic.h>
#include <sys/thread.h>
+#include <sys/note.h>
/* Local defines and variables */
#define BTOb(bytes) ((bytes) << 3) /* Bytes to bits, log2(NBBY) */
#define bTOB(bits) ((bits) >> 3) /* bits to Bytes, log2(NBBY) */
+#if defined(__sparc)
static uint_t cpr_pages_tobe_dumped;
static uint_t cpr_regular_pgs_dumped;
-
static int cpr_dump_regular_pages(vnode_t *);
static int cpr_count_upages(int, bitfunc_t);
static int cpr_compress_and_write(vnode_t *, uint_t, pfn_t, pgcnt_t);
+#endif
+
int cpr_flush_write(vnode_t *);
int cpr_contig_pages(vnode_t *, int);
@@ -75,6 +78,8 @@ extern size_t cpr_get_devsize(dev_t);
extern int i_cpr_dump_setup(vnode_t *);
extern int i_cpr_blockzero(char *, char **, int *, vnode_t *);
extern int cpr_test_mode;
+int cpr_setbit(pfn_t, int);
+int cpr_clrbit(pfn_t, int);
ctrm_t cpr_term;
@@ -87,13 +92,16 @@ int cpr_nbitmaps;
char *cpr_pagedata; /* page buffer for compression / tmp copy */
size_t cpr_pagedata_size; /* page buffer size in bytes */
+#if defined(__sparc)
static char *cpr_wptr; /* keep track of where to write to next */
static int cpr_file_bn; /* cpr state-file block offset */
static int cpr_disk_writes_ok;
static size_t cpr_dev_space = 0;
+#endif
char cpr_pagecopy[CPR_MAXCONTIG * MMU_PAGESIZE];
+#if defined(__sparc)
/*
* On some platforms bcopy may modify the thread structure
* during bcopy (eg, to prevent cpu migration). If the
@@ -194,6 +202,7 @@ cpr_write_header(vnode_t *vp)
struct cpr_dump_desc cdump;
pgcnt_t bitmap_pages;
pgcnt_t kpages, vpages, upages;
+ pgcnt_t cpr_count_kpages(int mapflag, bitfunc_t bitfunc);
cdump.cdd_magic = (uint_t)CPR_DUMP_MAGIC;
cdump.cdd_version = CPR_VERSION;
@@ -237,19 +246,20 @@ cpr_write_header(vnode_t *vp)
* Roundup will be done in the file allocation code.
*/
STAT->cs_nocomp_statefsz = sizeof (cdd_t) + sizeof (cmd_t) +
- (sizeof (cbd_t) * cdump.cdd_bitmaprec) +
- (sizeof (cpd_t) * cdump.cdd_dumppgsize) +
- mmu_ptob(cdump.cdd_dumppgsize + bitmap_pages);
+ (sizeof (cbd_t) * cdump.cdd_bitmaprec) +
+ (sizeof (cpd_t) * cdump.cdd_dumppgsize) +
+ mmu_ptob(cdump.cdd_dumppgsize + bitmap_pages);
/*
* If the estimated statefile is not big enough,
* go retry now to save un-necessary operations.
*/
if (!(CPR->c_flags & C_COMPRESSING) &&
- (STAT->cs_nocomp_statefsz > STAT->cs_est_statefsz)) {
+ (STAT->cs_nocomp_statefsz > STAT->cs_est_statefsz)) {
if (cpr_debug & (CPR_DEBUG1 | CPR_DEBUG7))
- prom_printf("cpr_write_header: STAT->cs_nocomp_statefsz > "
- "STAT->cs_est_statefsz\n");
+ prom_printf("cpr_write_header: "
+ "STAT->cs_nocomp_statefsz > "
+ "STAT->cs_est_statefsz\n");
return (ENOSPC);
}
@@ -272,10 +282,10 @@ cpr_write_terminator(vnode_t *vp)
/* count the last one (flush) */
cpr_term.real_statef_size = STAT->cs_real_statefsz +
- btod(cpr_wptr - cpr_buf) * DEV_BSIZE;
+ btod(cpr_wptr - cpr_buf) * DEV_BSIZE;
CPR_DEBUG(CPR_DEBUG9, "cpr_dump: Real Statefile Size: %ld\n",
- STAT->cs_real_statefsz);
+ STAT->cs_real_statefsz);
cpr_tod_get(&cpr_term.tm_shutdown);
@@ -382,6 +392,7 @@ cpr_write_statefile(vnode_t *vp)
return (error);
}
+#endif
/*
@@ -393,9 +404,13 @@ cpr_write_statefile(vnode_t *vp)
* - writes the remaining user pages
* - writes the kernel pages
*/
+#if defined(__x86)
+ _NOTE(ARGSUSED(0))
+#endif
int
cpr_dump(vnode_t *vp)
{
+#if defined(__sparc)
int error;
if (cpr_buf == NULL) {
@@ -484,11 +499,13 @@ cpr_dump(vnode_t *vp)
if (error = i_cpr_blockzero(cpr_buf, &cpr_wptr, &cpr_file_bn, vp))
return (error);
+#endif
return (0);
}
+#if defined(__sparc)
/*
* cpr_xwalk() is called many 100x with a range within kvseg or kvseg_reloc;
* a page-count from each range is accumulated at arg->pages.
@@ -633,7 +650,8 @@ cpr_sparse_seg_check(struct seg *seg)
for (; ste->st_seg; ste++) {
tseg = (ste->st_addrtype == KSEG_PTR_ADDR) ?
- *ste->st_seg : (struct seg *)ste->st_seg;
+ *ste->st_seg : (struct seg *)ste->st_seg;
+
if (seg == tseg)
return (ste);
}
@@ -690,7 +708,8 @@ cpr_count_kpages(int mapflag, bitfunc_t bitfunc)
CPR_DEBUG(CPR_DEBUG9, "cpr_count_kpages: kas_cnt=%ld\n", kas_cnt);
CPR_DEBUG(CPR_DEBUG7, "\ncpr_count_kpages: %ld pages, 0x%lx bytes\n",
- kas_cnt, mmu_ptob(kas_cnt));
+ kas_cnt, mmu_ptob(kas_cnt));
+
return (kas_cnt);
}
@@ -796,7 +815,7 @@ cpr_count_upages(int mapflag, bitfunc_t bitfunc)
extern struct vnode prom_ppages;
if (pp->p_vnode == NULL || PP_ISKAS(pp) ||
pp->p_vnode == &prom_ppages ||
- PP_ISFREE(pp) && PP_ISAGED(pp))
+ PP_ISFREE(pp) && PP_ISAGED(pp))
#else
if (pp->p_vnode == NULL || PP_ISKAS(pp) ||
PP_ISFREE(pp) && PP_ISAGED(pp))
@@ -813,9 +832,10 @@ cpr_count_upages(int mapflag, bitfunc_t bitfunc)
STAT->cs_upage2statef = dcnt;
CPR_DEBUG(CPR_DEBUG9, "cpr_count_upages: dirty=%ld total=%ld\n",
- dcnt, tcnt);
+ dcnt, tcnt);
CPR_DEBUG(CPR_DEBUG7, "cpr_count_upages: %ld pages, 0x%lx bytes\n",
- dcnt, mmu_ptob(dcnt));
+ dcnt, mmu_ptob(dcnt));
+
return (dcnt);
}
@@ -907,7 +927,7 @@ cpr_compress_and_write(vnode_t *vp, uint_t va, pfn_t pfn, pgcnt_t npg)
i_cpr_mapin(CPR->c_mapping_area, npg, pfn);
CPR_DEBUG(CPR_DEBUG3, "mapped-in %ld pages, vaddr 0x%p, pfn 0x%lx\n",
- npg, CPR->c_mapping_area, pfn);
+ npg, CPR->c_mapping_area, pfn);
/*
* Fill cpr page descriptor.
@@ -1181,3 +1201,4 @@ cpr_dump_regular_pages(vnode_t *vp)
CPR_DEBUG(CPR_DEBUG7, "cpr_dump_regular_pages() done.\n");
return (error);
}
+#endif
diff --git a/usr/src/uts/common/cpr/cpr_main.c b/usr/src/uts/common/cpr/cpr_main.c
index 6669469681..65e911cb11 100644
--- a/usr/src/uts/common/cpr/cpr_main.c
+++ b/usr/src/uts/common/cpr/cpr_main.c
@@ -25,7 +25,6 @@
#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* This module contains the guts of checkpoint-resume mechanism.
* All code in this module is platform independent.
@@ -51,6 +50,10 @@
#include <sys/reboot.h>
#include <sys/kdi.h>
#include <sys/promif.h>
+#include <sys/srn.h>
+#include <sys/cpr_impl.h>
+
+#define PPM(dip) ((dev_info_t *)DEVI(dip)->devi_pm_ppm)
extern struct cpr_terminator cpr_term;
@@ -63,18 +66,47 @@ extern void cpr_set_bitmap_size(void);
extern void cpr_stat_init();
extern void cpr_statef_close(void);
extern void flush_windows(void);
+extern void (*srn_signal)(int, int);
+extern void init_cpu_syscall(struct cpu *);
+extern void i_cpr_pre_resume_cpus();
+extern void i_cpr_post_resume_cpus();
extern int pm_powering_down;
-
-static int cpr_suspend(void);
-static int cpr_resume(void);
-static void cpr_suspend_init(void);
+extern kmutex_t srn_clone_lock;
+extern int srn_inuse;
+
+static int cpr_suspend(int);
+static int cpr_resume(int);
+static void cpr_suspend_init(int);
+#if defined(__x86)
+static int cpr_suspend_cpus(void);
+static void cpr_resume_cpus(void);
+#endif
+static int cpr_all_online(void);
+static void cpr_restore_offline(void);
cpr_time_t wholecycle_tv;
int cpr_suspend_succeeded;
pfn_t curthreadpfn;
int curthreadremapped;
+extern cpuset_t cpu_ready_set;
+extern void *(*cpu_pause_func)(void *);
+
+extern processorid_t i_cpr_bootcpuid(void);
+extern cpu_t *i_cpr_bootcpu(void);
+extern void tsc_adjust_delta(hrtime_t tdelta);
+extern void tsc_resume(void);
+extern int tsc_resume_in_cyclic;
+
+/*
+ * Set this variable to 1, to have device drivers resume in an
+ * uniprocessor environment. This is to allow drivers that assume
+ * that they resume on a UP machine to continue to work. Should be
+ * deprecated once the broken drivers are fixed
+ */
+int cpr_resume_uniproc = 0;
+
/*
* save or restore abort_enable; this prevents a drop
* to kadb or prom during cpr_resume_devices() when
@@ -101,23 +133,73 @@ cpr_sae(int stash)
* returned back to here and it then calls the resume routine.
*/
int
-cpr_main(void)
+cpr_main(int sleeptype)
{
- label_t saveq = ttolwp(curthread)->lwp_qsav;
- int rc;
+ int rc, rc2;
+ label_t saveq;
+ klwp_t *tlwp = ttolwp(curthread);
- if (rc = cpr_default_setup(1))
- return (rc);
+ if (sleeptype == CPR_TODISK) {
+ if ((rc = cpr_default_setup(1)) != 0)
+ return (rc);
+ ASSERT(tlwp);
+ saveq = tlwp->lwp_qsav;
+ }
+
+ if (sleeptype == CPR_TORAM) {
+ rc = cpr_suspend(sleeptype);
+ PMD(PMD_SX, ("cpr_suspend rets %x\n", rc))
+ if (rc == 0) {
+ int i_cpr_power_down(int sleeptype);
+
+ /*
+ * From this point on, we should be at a high
+ * spl, interrupts disabled, and all but one
+ * cpu's paused (effectively UP/single threaded).
+ * So this is were we want to put ASSERTS()
+ * to let us know otherwise.
+ */
+ ASSERT(cpus_paused());
+ /*
+ * Now do the work of actually putting this
+ * machine to sleep!
+ */
+ rc = i_cpr_power_down(sleeptype);
+ if (rc == 0) {
+ PMD(PMD_SX, ("back from succssful suspend\n"))
+ }
+ /*
+ * We do care about the return value from cpr_resume
+ * at this point, as it will tell us if one of the
+ * resume functions failed (cpr_resume_devices())
+ * However, for this to return and _not_ panic, means
+ * that we must be in one of the test functions. So
+ * check for that and return an appropriate message.
+ */
+ rc2 = cpr_resume(sleeptype);
+ if (rc2 != 0) {
+ ASSERT(cpr_test_point > 0);
+ cmn_err(CE_NOTE,
+ "cpr_resume returned non-zero: %d\n", rc2);
+ PMD(PMD_SX, ("cpr_resume rets %x\n", rc2))
+ }
+ ASSERT(!cpus_paused());
+ } else {
+ PMD(PMD_SX, ("failed suspend, resuming\n"))
+ rc = cpr_resume(sleeptype);
+ }
+ return (rc);
+ }
/*
- * Remember where we are for resume
+ * Remember where we are for resume after reboot
*/
- if (!setjmp(&ttolwp(curthread)->lwp_qsav)) {
+ if (!setjmp(&tlwp->lwp_qsav)) {
/*
* try to checkpoint the system, if failed return back
* to userland, otherwise power off.
*/
- rc = cpr_suspend();
+ rc = cpr_suspend(sleeptype);
if (rc || cpr_reusable_mode) {
/*
* We don't really want to go down, or
@@ -125,22 +207,28 @@ cpr_main(void)
* to put the system back to an operable state then
* return back to userland.
*/
- (void) cpr_resume();
+ PMD(PMD_SX, ("failed suspend, resuming\n"))
+ (void) cpr_resume(sleeptype);
+ PMD(PMD_SX, ("back from failed suspend resume\n"))
}
} else {
/*
* This is the resumed side of longjmp, restore the previous
* longjmp pointer if there is one so this will be transparent
* to the world.
+ * This path is only for CPR_TODISK, where we reboot
*/
- ttolwp(curthread)->lwp_qsav = saveq;
+ ASSERT(sleeptype == CPR_TODISK);
+ tlwp->lwp_qsav = saveq;
CPR->c_flags &= ~C_SUSPENDING;
CPR->c_flags |= C_RESUMING;
/*
* resume the system back to the original state
*/
- rc = cpr_resume();
+ rc = cpr_resume(sleeptype);
+ PMD(PMD_SX, ("back from successful suspend; resume rets %x\n",
+ rc))
}
(void) cpr_default_setup(0);
@@ -149,6 +237,8 @@ cpr_main(void)
}
+#if defined(__sparc)
+
/*
* check/disable or re-enable UFS logging
*/
@@ -180,8 +270,7 @@ cpr_log_status(int enable, int *svstat, vnode_t *vp)
*svstat = status;
if (cpr_debug & CPR_DEBUG5) {
mntpt = vfs_getmntpoint(vp->v_vfsp);
- CPR_DEBUG(CPR_DEBUG5,
- "%s: \"%s\", logging status = %d\n",
+ errp("%s: \"%s\", logging status = %d\n",
str, refstr_value(mntpt), status);
refstr_rele(mntpt);
};
@@ -207,11 +296,10 @@ cpr_log_status(int enable, int *svstat, vnode_t *vp)
} else {
if (cpr_debug & CPR_DEBUG5) {
mntpt = vfs_getmntpoint(vp->v_vfsp);
- CPR_DEBUG(CPR_DEBUG5,
- "%s: \"%s\", logging is now %sd\n",
+ errp("%s: \"%s\", logging is now %sd\n",
str, refstr_value(mntpt), able);
refstr_rele(mntpt);
- }
+ };
}
}
@@ -223,7 +311,6 @@ cpr_log_status(int enable, int *svstat, vnode_t *vp)
*svstat = -1;
}
-
/*
* enable/disable UFS logging on filesystems containing cpr_default_path
* and cpr statefile. since the statefile can be on any fs, that fs
@@ -234,6 +321,7 @@ cpr_log_status(int enable, int *svstat, vnode_t *vp)
* file outside of rootfs would cause errors during cprboot, plus cpr and
* fsck problems with the new fs if logging were enabled.
*/
+
static int
cpr_ufs_logging(int enable)
{
@@ -274,6 +362,7 @@ cpr_ufs_logging(int enable)
return (0);
}
+#endif
/*
@@ -288,6 +377,54 @@ cpr_lock_mgr(void (*service)(void))
(*service)();
}
+int
+cpr_suspend_cpus(void)
+{
+ cpu_t *bootcpu;
+ int ret = 0;
+ extern void *i_cpr_save_context(void *arg);
+
+ mutex_enter(&cpu_lock);
+
+ /*
+ * if bootcpu is offline bring it back online
+ */
+ bootcpu = i_cpr_bootcpu();
+
+ /*
+ * the machine could not have booted without a bootcpu
+ */
+ ASSERT(bootcpu != NULL);
+
+ /*
+ * bring all the offline cpus online
+ */
+ if ((ret = cpr_all_online())) {
+ mutex_exit(&cpu_lock);
+ return (ret);
+ }
+
+ /*
+ * Set the affinity to be the boot processor
+ * This is cleared in either cpr_resume_cpus() or cpr_unpause_cpus()
+ */
+ affinity_set(i_cpr_bootcpuid());
+
+ ASSERT(CPU->cpu_id == 0);
+
+ PMD(PMD_SX, ("curthread running on bootcpu\n"))
+
+ /*
+ * pause all other running CPUs and save the CPU state at the sametime
+ */
+ cpu_pause_func = i_cpr_save_context;
+ pause_cpus(NULL);
+
+ mutex_exit(&cpu_lock);
+
+ return (0);
+}
+
/*
* Take the system down to a checkpointable state and write
* the state file, the following are sequentially executed:
@@ -301,41 +438,69 @@ cpr_lock_mgr(void (*service)(void))
* - suspend all devices
* - block intrpts
* - dump system state and memory to state file
+ * - SPARC code will not be called with CPR_TORAM, caller filters
*/
static int
-cpr_suspend(void)
+cpr_suspend(int sleeptype)
{
- int sf_realloc, rc, skt_rc, nverr;
+#if defined(__sparc)
+ int sf_realloc, nverr;
+#endif
+ int rc = 0;
+ int skt_rc = 0;
+ PMD(PMD_SX, ("cpr_suspend %x\n", sleeptype))
cpr_set_substate(C_ST_SUSPEND_BEGIN);
- cpr_suspend_init();
+ cpr_suspend_init(sleeptype);
cpr_save_time();
cpr_tod_get(&wholecycle_tv);
CPR_STAT_EVENT_START("Suspend Total");
+ i_cpr_alloc_cpus();
+
+#if defined(__sparc)
+ ASSERT(sleeptype == CPR_TODISK);
if (!cpr_reusable_mode) {
/*
- * We need to validate default file before fs functionality
- * is disabled.
+ * We need to validate default file before fs
+ * functionality is disabled.
*/
if (rc = cpr_validate_definfo(0))
return (rc);
}
-
i_cpr_save_machdep_info();
+#endif
+ PMD(PMD_SX, ("cpr_suspend: stop scans\n"))
/* Stop PM scans ASAP */
(void) callb_execute_class(CB_CL_CPR_PM, CB_CODE_CPR_CHKPT);
pm_dispatch_to_dep_thread(PM_DEP_WK_CPR_SUSPEND,
NULL, NULL, PM_DEP_WAIT, NULL, 0);
+#if defined(__sparc)
+ ASSERT(sleeptype == CPR_TODISK);
cpr_set_substate(C_ST_MP_OFFLINE);
if (rc = cpr_mp_offline())
return (rc);
+#endif
+ /*
+ * Ask Xorg to suspend the frame buffer, and wait for it to happen
+ */
+ mutex_enter(&srn_clone_lock);
+ if (srn_signal) {
+ PMD(PMD_SX, ("cpr_suspend: (*srn_signal)(..., "
+ "SRN_SUSPEND_REQ)\n"))
+ srn_inuse = 1; /* because *(srn_signal) cv_waits */
+ (*srn_signal)(SRN_TYPE_APM, SRN_SUSPEND_REQ);
+ srn_inuse = 0;
+ } else {
+ PMD(PMD_SX, ("cpr_suspend: srn_signal NULL\n"))
+ }
+ mutex_exit(&srn_clone_lock);
/*
* Ask the user threads to stop by themselves, but
@@ -346,11 +511,13 @@ cpr_suspend(void)
CPR_DEBUG(CPR_DEBUG1, "\nstopping user threads...");
CPR_STAT_EVENT_START(" stop users");
cpr_set_substate(C_ST_STOP_USER_THREADS);
+ PMD(PMD_SX, ("cpr_suspend: stop user threads\n"))
if (rc = cpr_stop_user_threads())
return (rc);
CPR_STAT_EVENT_END(" stop users");
CPR_DEBUG(CPR_DEBUG1, "done\n");
+ PMD(PMD_SX, ("cpr_suspend: save direct levels\n"))
pm_save_direct_levels();
/*
@@ -360,10 +527,14 @@ cpr_suspend(void)
*/
(void) callb_execute_class(CB_CL_CPR_PROMPRINTF, CB_CODE_CPR_CHKPT);
+ PMD(PMD_SX, ("cpr_suspend: send notice\n"))
+#ifndef DEBUG
cpr_send_notice();
if (cpr_debug)
prom_printf("\n");
+#endif
+ PMD(PMD_SX, ("cpr_suspend: POST USER callback\n"))
(void) callb_execute_class(CB_CL_CPR_POST_USER, CB_CODE_CPR_CHKPT);
/*
@@ -373,9 +544,12 @@ cpr_suspend(void)
* a kernel thread.
*/
cpr_set_substate(C_ST_PM_REATTACH_NOINVOL);
+ PMD(PMD_SX, ("cpr_suspend: reattach noinvol\n"))
if (!pm_reattach_noinvol())
return (ENXIO);
+#if defined(__sparc)
+ ASSERT(sleeptype == CPR_TODISK);
/*
* if ufs logging is enabled, we need to disable before
* stopping kernel threads so that ufs delete and roll
@@ -398,8 +572,8 @@ cpr_suspend(void)
alloc_statefile:
/*
- * If our last state was C_ST_DUMP_NOSPC, we're trying to realloc
- * the statefile, otherwise this is the first attempt.
+ * If our last state was C_ST_DUMP_NOSPC, we're trying to
+ * realloc the statefile, otherwise this is the first attempt.
*/
sf_realloc = (CPR->c_substate == C_ST_DUMP_NOSPC) ? 1 : 0;
@@ -407,7 +581,7 @@ alloc_statefile:
cpr_set_substate(C_ST_STATEF_ALLOC);
if (rc = cpr_alloc_statefile(sf_realloc)) {
if (sf_realloc)
- prom_printf("realloc failed\n");
+ errp("realloc failed\n");
return (rc);
}
CPR_STAT_EVENT_END(" alloc statefile");
@@ -415,9 +589,10 @@ alloc_statefile:
/*
* Sync the filesystem to preserve its integrity.
*
- * This sync is also used to flush out all B_DELWRI buffers (fs cache)
- * which are mapped and neither dirty nor referenced before
- * cpr_invalidate_pages destroys them. fsflush does similar thing.
+ * This sync is also used to flush out all B_DELWRI buffers
+ * (fs cache) which are mapped and neither dirty nor referenced
+ * before cpr_invalidate_pages destroys them.
+ * fsflush does similar thing.
*/
sync();
@@ -425,16 +600,18 @@ alloc_statefile:
* destroy all clean file mapped kernel pages
*/
CPR_STAT_EVENT_START(" clean pages");
- CPR_DEBUG(CPR_DEBUG1, "cleaning up mapped pages...");
+ CPR_DEBUG(CPR_DEBUG1, ("cleaning up mapped pages..."));
(void) callb_execute_class(CB_CL_CPR_VM, CB_CODE_CPR_CHKPT);
- CPR_DEBUG(CPR_DEBUG1, "done\n");
+ CPR_DEBUG(CPR_DEBUG1, ("done\n"));
CPR_STAT_EVENT_END(" clean pages");
+#endif
/*
* Hooks needed by lock manager prior to suspending.
* Refer to code for more comments.
*/
+ PMD(PMD_SX, ("cpr_suspend: lock mgr\n"))
cpr_lock_mgr(lm_cprsuspend);
/*
@@ -444,6 +621,7 @@ alloc_statefile:
CPR_DEBUG(CPR_DEBUG1, "suspending drivers...");
cpr_set_substate(C_ST_SUSPEND_DEVICES);
pm_powering_down = 1;
+ PMD(PMD_SX, ("cpr_suspend: suspending devices\n"))
rc = cpr_suspend_devices(ddi_root_node());
pm_powering_down = 0;
if (rc)
@@ -455,36 +633,61 @@ alloc_statefile:
* Stop all daemon activities
*/
cpr_set_substate(C_ST_STOP_KERNEL_THREADS);
+ PMD(PMD_SX, ("cpr_suspend: stopping kernel threads\n"))
if (skt_rc = cpr_stop_kernel_threads())
return (skt_rc);
+ PMD(PMD_SX, ("cpr_suspend: POST KERNEL callback\n"))
(void) callb_execute_class(CB_CL_CPR_POST_KERNEL, CB_CODE_CPR_CHKPT);
+ PMD(PMD_SX, ("cpr_suspend: reattach noinvol fini\n"))
pm_reattach_noinvol_fini();
cpr_sae(1);
+ PMD(PMD_SX, ("cpr_suspend: CPR CALLOUT callback\n"))
(void) callb_execute_class(CB_CL_CPR_CALLOUT, CB_CODE_CPR_CHKPT);
- /*
- * It's safer to do tod_get before we disable all intr.
- */
- CPR_STAT_EVENT_START(" write statefile");
+ if (sleeptype == CPR_TODISK) {
+ /*
+ * It's safer to do tod_get before we disable all intr.
+ */
+ CPR_STAT_EVENT_START(" write statefile");
+ }
/*
* it's time to ignore the outside world, stop the real time
* clock and disable any further intrpt activity.
*/
+ PMD(PMD_SX, ("cpr_suspend: handle xc\n"))
i_cpr_handle_xc(1); /* turn it on to disable xc assertion */
mutex_enter(&cpu_lock);
+ PMD(PMD_SX, ("cpr_suspend: cyclic suspend\n"))
cyclic_suspend();
mutex_exit(&cpu_lock);
- mon_clock_stop();
- mon_clock_unshare();
- mon_clock_start();
+ /*
+ * Due to the different methods of resuming the system between
+ * CPR_TODISK (boot cprboot on SPARC, which reloads kernel image)
+ * and CPR_TORAM (restart via reset into existing kernel image)
+ * cpus are not suspended and restored in the SPARC case, since it
+ * is necessary to restart the cpus and pause them before restoring
+ * the OBP image
+ */
+
+#if defined(__x86)
+
+ /* pause aux cpus */
+ PMD(PMD_SX, ("pause aux cpus\n"))
+
+ cpr_set_substate(C_ST_MP_PAUSED);
+ if ((rc = cpr_suspend_cpus()) != 0)
+ return (rc);
+#endif
+
+ PMD(PMD_SX, ("cpr_suspend: stop intr\n"))
i_cpr_stop_intr();
CPR_DEBUG(CPR_DEBUG1, "interrupt is stopped\n");
@@ -494,16 +697,28 @@ alloc_statefile:
* it must be up now.
*/
ASSERT(pm_cfb_is_up());
+ PMD(PMD_SX, ("cpr_suspend: prom suspend prepost\n"))
prom_suspend_prepost();
+#if defined(__sparc)
/*
* getting ready to write ourself out, flush the register
* windows to make sure that our stack is good when we
* come back on the resume side.
*/
flush_windows();
+#endif
/*
+ * For S3, we're done
+ */
+ if (sleeptype == CPR_TORAM) {
+ PMD(PMD_SX, ("cpr_suspend rets %x\n", rc))
+ cpr_set_substate(C_ST_NODUMP);
+ return (rc);
+ }
+#if defined(__sparc)
+ /*
* FATAL: NO MORE MEMORY ALLOCATION ALLOWED AFTER THIS POINT!!!
*
* The system is quiesced at this point, we are ready to either dump
@@ -535,7 +750,7 @@ alloc_statefile:
if (rc == ENOSPC) {
cpr_set_substate(C_ST_DUMP_NOSPC);
- (void) cpr_resume();
+ (void) cpr_resume(sleeptype);
goto alloc_statefile;
} else if (rc == 0) {
if (cpr_reusable_mode) {
@@ -544,9 +759,97 @@ alloc_statefile:
} else
rc = cpr_set_properties(1);
}
+#endif
+ PMD(PMD_SX, ("cpr_suspend: return %d\n", rc))
return (rc);
}
+void
+cpr_resume_cpus(void)
+{
+ /*
+ * this is a cut down version of start_other_cpus()
+ * just do the initialization to wake the other cpus
+ */
+
+#if defined(__x86)
+ /*
+ * Initialize our syscall handlers
+ */
+ init_cpu_syscall(CPU);
+
+#endif
+
+ i_cpr_pre_resume_cpus();
+
+ /*
+ * Restart the paused cpus
+ */
+ mutex_enter(&cpu_lock);
+ start_cpus();
+ mutex_exit(&cpu_lock);
+
+ /*
+ * clear the affinity set in cpr_suspend_cpus()
+ */
+ affinity_clear();
+
+ i_cpr_post_resume_cpus();
+
+ mutex_enter(&cpu_lock);
+ /*
+ * Restore this cpu to use the regular cpu_pause(), so that
+ * online and offline will work correctly
+ */
+ cpu_pause_func = NULL;
+
+ /*
+ * offline all the cpus that were brought online during suspend
+ */
+ cpr_restore_offline();
+
+ /*
+ * clear the affinity set in cpr_suspend_cpus()
+ */
+ affinity_clear();
+
+ mutex_exit(&cpu_lock);
+}
+
+void
+cpr_unpause_cpus(void)
+{
+ /*
+ * Now restore the system back to what it was before we suspended
+ */
+
+ PMD(PMD_SX, ("cpr_unpause_cpus: restoring system\n"))
+
+ mutex_enter(&cpu_lock);
+
+ /*
+ * Restore this cpu to use the regular cpu_pause(), so that
+ * online and offline will work correctly
+ */
+ cpu_pause_func = NULL;
+
+ /*
+ * Restart the paused cpus
+ */
+ start_cpus();
+
+ /*
+ * offline all the cpus that were brought online during suspend
+ */
+ cpr_restore_offline();
+
+ /*
+ * clear the affinity set in cpr_suspend_cpus()
+ */
+ affinity_clear();
+
+ mutex_exit(&cpu_lock);
+}
/*
* Bring the system back up from a checkpoint, at this point
@@ -559,7 +862,7 @@ alloc_statefile:
* - put all threads back on run queue
*/
static int
-cpr_resume(void)
+cpr_resume(int sleeptype)
{
cpr_time_t pwron_tv, *ctp;
char *str;
@@ -570,6 +873,7 @@ cpr_resume(void)
* that was suspended to a different level.
*/
CPR_DEBUG(CPR_DEBUG1, "\nEntering cpr_resume...\n");
+ PMD(PMD_SX, ("cpr_resume %x\n", sleeptype))
/*
* Note:
@@ -584,12 +888,14 @@ cpr_resume(void)
* and the one that caused the failure, if necessary."
*/
switch (CPR->c_substate) {
+#if defined(__sparc)
case C_ST_DUMP:
/*
* This is most likely a full-fledged cpr_resume after
* a complete and successful cpr suspend. Just roll back
* everything.
*/
+ ASSERT(sleeptype == CPR_TODISK);
break;
case C_ST_REUSABLE:
@@ -605,46 +911,60 @@ cpr_resume(void)
* is possible that a need for roll back of a state
* change arises between these exit points.
*/
+ ASSERT(sleeptype == CPR_TODISK);
goto rb_dump;
+#endif
+
+ case C_ST_NODUMP:
+ PMD(PMD_SX, ("cpr_resume: NODUMP\n"))
+ goto rb_nodump;
case C_ST_STOP_KERNEL_THREADS:
+ PMD(PMD_SX, ("cpr_resume: STOP_KERNEL_THREADS\n"))
goto rb_stop_kernel_threads;
case C_ST_SUSPEND_DEVICES:
+ PMD(PMD_SX, ("cpr_resume: SUSPEND_DEVICES\n"))
goto rb_suspend_devices;
+#if defined(__sparc)
case C_ST_STATEF_ALLOC:
+ ASSERT(sleeptype == CPR_TODISK);
goto rb_statef_alloc;
case C_ST_DISABLE_UFS_LOGGING:
+ ASSERT(sleeptype == CPR_TODISK);
goto rb_disable_ufs_logging;
+#endif
case C_ST_PM_REATTACH_NOINVOL:
+ PMD(PMD_SX, ("cpr_resume: REATTACH_NOINVOL\n"))
goto rb_pm_reattach_noinvol;
case C_ST_STOP_USER_THREADS:
+ PMD(PMD_SX, ("cpr_resume: STOP_USER_THREADS\n"))
goto rb_stop_user_threads;
+#if defined(__sparc)
case C_ST_MP_OFFLINE:
+ PMD(PMD_SX, ("cpr_resume: MP_OFFLINE\n"))
goto rb_mp_offline;
+#endif
+
+#if defined(__x86)
+ case C_ST_MP_PAUSED:
+ PMD(PMD_SX, ("cpr_resume: MP_PAUSED\n"))
+ goto rb_mp_paused;
+#endif
+
default:
+ PMD(PMD_SX, ("cpr_resume: others\n"))
goto rb_others;
}
rb_all:
/*
- * setup debugger trapping.
- */
- if (cpr_suspend_succeeded)
- i_cpr_set_tbr();
-
- /*
- * tell prom to monitor keys before the kernel comes alive
- */
- mon_clock_start();
-
- /*
* perform platform-dependent initialization
*/
if (cpr_suspend_succeeded)
@@ -659,33 +979,65 @@ rb_dump:
*
* DO NOT ADD ANY INITIALIZATION STEP BEFORE THIS POINT!!
*/
+rb_nodump:
+ /*
+ * If we did suspend to RAM, we didn't generate a dump
+ */
+ PMD(PMD_SX, ("cpr_resume: CPR DMA callback\n"))
(void) callb_execute_class(CB_CL_CPR_DMA, CB_CODE_CPR_RESUME);
- if (cpr_suspend_succeeded)
+ if (cpr_suspend_succeeded) {
+ PMD(PMD_SX, ("cpr_resume: CPR RPC callback\n"))
(void) callb_execute_class(CB_CL_CPR_RPC, CB_CODE_CPR_RESUME);
+ }
prom_resume_prepost();
+#if !defined(__sparc)
+ /*
+ * Need to sync the software clock with the hardware clock.
+ * On Sparc, this occurs in the sparc-specific cbe. However
+ * on x86 this needs to be handled _before_ we bring other cpu's
+ * back online. So we call a resume function in timestamp.c
+ */
+ if (tsc_resume_in_cyclic == 0)
+ tsc_resume();
+#endif
+
+#if defined(__sparc)
if (cpr_suspend_succeeded && (boothowto & RB_DEBUG))
kdi_dvec_cpr_restart();
+#endif
+
+
+#if defined(__x86)
+rb_mp_paused:
+ PT(PT_RMPO);
+ PMD(PMD_SX, ("resume aux cpus\n"))
+
+ if (cpr_suspend_succeeded) {
+ cpr_resume_cpus();
+ } else {
+ cpr_unpause_cpus();
+ }
+#endif
/*
* let the tmp callout catch up.
*/
+ PMD(PMD_SX, ("cpr_resume: CPR CALLOUT callback\n"))
(void) callb_execute_class(CB_CL_CPR_CALLOUT, CB_CODE_CPR_RESUME);
i_cpr_enable_intr();
- mon_clock_stop();
- mon_clock_share();
-
mutex_enter(&cpu_lock);
+ PMD(PMD_SX, ("cpr_resume: cyclic resume\n"))
cyclic_resume();
mutex_exit(&cpu_lock);
- mon_clock_start();
-
+ PMD(PMD_SX, ("cpr_resume: handle xc\n"))
i_cpr_handle_xc(0); /* turn it off to allow xc assertion */
+ PMD(PMD_SX, ("cpr_resume: CPR POST KERNEL callback\n"))
(void) callb_execute_class(CB_CL_CPR_POST_KERNEL, CB_CODE_CPR_RESUME);
/*
@@ -701,7 +1053,8 @@ rb_dump:
cpr_convert_promtime(&pwron_tv);
ctp = &cpr_term.tm_shutdown;
- CPR_STAT_EVENT_END_TMZ(" write statefile", ctp);
+ if (sleeptype == CPR_TODISK)
+ CPR_STAT_EVENT_END_TMZ(" write statefile", ctp);
CPR_STAT_EVENT_END_TMZ("Suspend Total", ctp);
CPR_STAT_EVENT_START_TMZ("Resume Total", &pwron_tv);
@@ -726,62 +1079,116 @@ rb_stop_kernel_threads:
* disabled before starting kernel threads, we don't want
* modunload thread to start changing device tree underneath.
*/
+ PMD(PMD_SX, ("cpr_resume: modunload disable\n"))
modunload_disable();
+ PMD(PMD_SX, ("cpr_resume: start kernel threads\n"))
cpr_start_kernel_threads();
rb_suspend_devices:
CPR_DEBUG(CPR_DEBUG1, "resuming devices...");
CPR_STAT_EVENT_START(" start drivers");
+ PMD(PMD_SX,
+ ("cpr_resume: rb_suspend_devices: cpr_resume_uniproc = %d\n",
+ cpr_resume_uniproc))
+
+#if defined(__x86)
+ /*
+ * If cpr_resume_uniproc is set, then pause all the other cpus
+ * apart from the current cpu, so that broken drivers that think
+ * that they are on a uniprocessor machine will resume
+ */
+ if (cpr_resume_uniproc) {
+ mutex_enter(&cpu_lock);
+ pause_cpus(NULL);
+ mutex_exit(&cpu_lock);
+ }
+#endif
+
/*
* The policy here is to continue resume everything we can if we did
* not successfully finish suspend; and panic if we are coming back
* from a fully suspended system.
*/
+ PMD(PMD_SX, ("cpr_resume: resume devices\n"))
rc = cpr_resume_devices(ddi_root_node(), 0);
cpr_sae(0);
str = "Failed to resume one or more devices.";
- if (rc && CPR->c_substate == C_ST_DUMP)
- cpr_err(CE_PANIC, str);
- else if (rc)
- cpr_err(CE_WARN, str);
+
+ if (rc) {
+ if (CPR->c_substate == C_ST_DUMP ||
+ (sleeptype == CPR_TORAM &&
+ CPR->c_substate == C_ST_NODUMP)) {
+ if (cpr_test_point == FORCE_SUSPEND_TO_RAM) {
+ PMD(PMD_SX, ("cpr_resume: resume device "
+ "warn\n"))
+ cpr_err(CE_WARN, str);
+ } else {
+ PMD(PMD_SX, ("cpr_resume: resume device "
+ "panic\n"))
+ cpr_err(CE_PANIC, str);
+ }
+ } else {
+ PMD(PMD_SX, ("cpr_resume: resume device warn\n"))
+ cpr_err(CE_WARN, str);
+ }
+ }
+
CPR_STAT_EVENT_END(" start drivers");
CPR_DEBUG(CPR_DEBUG1, "done\n");
+#if defined(__x86)
+ /*
+ * If cpr_resume_uniproc is set, then unpause all the processors
+ * that were paused before resuming the drivers
+ */
+ if (cpr_resume_uniproc) {
+ mutex_enter(&cpu_lock);
+ start_cpus();
+ mutex_exit(&cpu_lock);
+ }
+#endif
+
/*
* If we had disabled modunloading in this cpr resume cycle (i.e. we
* resumed from a state earlier than C_ST_SUSPEND_DEVICES), re-enable
* modunloading now.
*/
- if (CPR->c_substate != C_ST_SUSPEND_DEVICES)
+ if (CPR->c_substate != C_ST_SUSPEND_DEVICES) {
+ PMD(PMD_SX, ("cpr_resume: modload enable\n"))
modunload_enable();
+ }
/*
* Hooks needed by lock manager prior to resuming.
* Refer to code for more comments.
*/
+ PMD(PMD_SX, ("cpr_resume: lock mgr\n"))
cpr_lock_mgr(lm_cprresume);
+#if defined(__sparc)
/*
* This is a partial (half) resume during cpr suspend, we
* haven't yet given up on the suspend. On return from here,
* cpr_suspend() will try to reallocate and retry the suspend.
*/
if (CPR->c_substate == C_ST_DUMP_NOSPC) {
- mon_clock_stop();
return (0);
}
+ if (sleeptype == CPR_TODISK) {
rb_statef_alloc:
- cpr_statef_close();
+ cpr_statef_close();
rb_disable_ufs_logging:
- /*
- * if ufs logging was disabled, re-enable
- */
- (void) cpr_ufs_logging(1);
+ /*
+ * if ufs logging was disabled, re-enable
+ */
+ (void) cpr_ufs_logging(1);
+ }
+#endif
rb_pm_reattach_noinvol:
/*
@@ -795,44 +1202,64 @@ rb_pm_reattach_noinvol:
CPR->c_substate == C_ST_STATEF_ALLOC ||
CPR->c_substate == C_ST_SUSPEND_DEVICES ||
CPR->c_substate == C_ST_STOP_KERNEL_THREADS) {
+ PMD(PMD_SX, ("cpr_resume: reattach noinvol fini\n"))
pm_reattach_noinvol_fini();
}
+ PMD(PMD_SX, ("cpr_resume: CPR POST USER callback\n"))
(void) callb_execute_class(CB_CL_CPR_POST_USER, CB_CODE_CPR_RESUME);
+ PMD(PMD_SX, ("cpr_resume: CPR PROMPRINTF callback\n"))
(void) callb_execute_class(CB_CL_CPR_PROMPRINTF, CB_CODE_CPR_RESUME);
+ PMD(PMD_SX, ("cpr_resume: restore direct levels\n"))
pm_restore_direct_levels();
rb_stop_user_threads:
CPR_DEBUG(CPR_DEBUG1, "starting user threads...");
+ PMD(PMD_SX, ("cpr_resume: starting user threads\n"))
cpr_start_user_threads();
CPR_DEBUG(CPR_DEBUG1, "done\n");
+ /*
+ * Ask Xorg to resume the frame buffer, and wait for it to happen
+ */
+ mutex_enter(&srn_clone_lock);
+ if (srn_signal) {
+ PMD(PMD_SX, ("cpr_suspend: (*srn_signal)(..., "
+ "SRN_NORMAL_RESUME)\n"))
+ srn_inuse = 1; /* because (*srn_signal) cv_waits */
+ (*srn_signal)(SRN_TYPE_APM, SRN_NORMAL_RESUME);
+ srn_inuse = 0;
+ } else {
+ PMD(PMD_SX, ("cpr_suspend: srn_signal NULL\n"))
+ }
+ mutex_exit(&srn_clone_lock);
+#if defined(__sparc)
rb_mp_offline:
if (cpr_mp_online())
cpr_err(CE_WARN, "Failed to online all the processors.");
+#endif
rb_others:
- pm_dispatch_to_dep_thread(PM_DEP_WK_CPR_RESUME, NULL, NULL, PM_DEP_WAIT,
- NULL, 0);
+ PMD(PMD_SX, ("cpr_resume: dep thread\n"))
+ pm_dispatch_to_dep_thread(PM_DEP_WK_CPR_RESUME, NULL, NULL,
+ PM_DEP_WAIT, NULL, 0);
+ PMD(PMD_SX, ("cpr_resume: CPR PM callback\n"))
(void) callb_execute_class(CB_CL_CPR_PM, CB_CODE_CPR_RESUME);
- /*
- * now that all the drivers are going, kernel kbd driver can
- * take over, turn off prom monitor clock
- */
- mon_clock_stop();
-
if (cpr_suspend_succeeded) {
- cpr_restore_time();
cpr_stat_record_events();
}
- if (!cpr_reusable_mode)
+#if defined(__sparc)
+ if (sleeptype == CPR_TODISK && !cpr_reusable_mode)
cpr_clear_definfo();
+#endif
+ i_cpr_free_cpus();
CPR_DEBUG(CPR_DEBUG1, "Sending SIGTHAW...");
+ PMD(PMD_SX, ("cpr_resume: SIGTHAW\n"))
cpr_signal_user(SIGTHAW);
CPR_DEBUG(CPR_DEBUG1, "done\n");
@@ -854,11 +1281,12 @@ rb_others:
CPR_STAT_EVENT_PRINT();
#endif /* CPR_STAT */
+ PMD(PMD_SX, ("cpr_resume returns %x\n", rc))
return (rc);
}
static void
-cpr_suspend_init(void)
+cpr_suspend_init(int sleeptype)
{
cpr_time_t *ctp;
@@ -880,15 +1308,93 @@ cpr_suspend_init(void)
ctp = &cpr_term.tm_cprboot_end;
bzero(ctp, sizeof (*ctp));
+ if (sleeptype == CPR_TODISK) {
+ /*
+ * Lookup the physical address of our thread structure.
+ * This should never be invalid and the entire thread structure
+ * is expected to reside within the same pfn.
+ */
+ curthreadpfn = hat_getpfnum(kas.a_hat, (caddr_t)curthread);
+ ASSERT(curthreadpfn != PFN_INVALID);
+ ASSERT(curthreadpfn == hat_getpfnum(kas.a_hat,
+ (caddr_t)curthread + sizeof (kthread_t) - 1));
+ }
+
+ cpr_suspend_succeeded = 0;
+}
+
+/*
+ * bring all the offline cpus online
+ */
+static int
+cpr_all_online(void)
+{
+ int rc = 0;
+
+#ifdef __sparc
/*
- * Lookup the physical address of our thread structure. This should
- * never be invalid and the entire thread structure is expected
- * to reside within the same pfn.
+ * do nothing
*/
- curthreadpfn = hat_getpfnum(kas.a_hat, (caddr_t)curthread);
- ASSERT(curthreadpfn != PFN_INVALID);
- ASSERT(curthreadpfn == hat_getpfnum(kas.a_hat,
- (caddr_t)curthread + sizeof (kthread_t) - 1));
+#else
+
+ cpu_t *cp;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ cp = cpu_list;
+ do {
+ cp->cpu_cpr_flags &= ~CPU_CPR_ONLINE;
+ if (!CPU_ACTIVE(cp)) {
+ if ((rc = cpu_online(cp)) != 0)
+ break;
+ CPU_SET_CPR_FLAGS(cp, CPU_CPR_ONLINE);
+ }
+ } while ((cp = cp->cpu_next) != cpu_list);
+
+ if (rc) {
+ /*
+ * an online operation failed so offline the cpus
+ * that were onlined above to restore the system
+ * to its original state
+ */
+ cpr_restore_offline();
+ }
+#endif
+ return (rc);
+}
+
+/*
+ * offline all the cpus that were brought online by cpr_all_online()
+ */
+static void
+cpr_restore_offline(void)
+{
+
+#ifdef __sparc
+ /*
+ * do nothing
+ */
+#else
+
+ cpu_t *cp;
+ int rc = 0;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ cp = cpu_list;
+ do {
+ if (CPU_CPR_IS_ONLINE(cp)) {
+ rc = cpu_offline(cp, 0);
+ /*
+ * this offline should work, since the cpu was
+ * offline originally and was successfully onlined
+ * by cpr_all_online()
+ */
+ ASSERT(rc == 0);
+ cp->cpu_cpr_flags &= ~CPU_CPR_ONLINE;
+ }
+ } while ((cp = cp->cpu_next) != cpu_list);
+
+#endif
- cpr_suspend_succeeded = 0;
}
diff --git a/usr/src/uts/common/cpr/cpr_misc.c b/usr/src/uts/common/cpr/cpr_misc.c
index 936e3e9565..1ec0452c81 100644
--- a/usr/src/uts/common/cpr/cpr_misc.c
+++ b/usr/src/uts/common/cpr/cpr_misc.c
@@ -38,6 +38,7 @@
#include <sys/kmem.h>
#include <sys/cpr.h>
#include <sys/conf.h>
+#include <sys/machclock.h>
/*
* CPR miscellaneous support routines
@@ -61,11 +62,14 @@ extern char *cpr_pagedata;
extern int cpr_bufs_allocated;
extern int cpr_bitmaps_allocated;
+#if defined(__sparc)
static struct cprconfig cprconfig;
static int cprconfig_loaded = 0;
static int cpr_statefile_ok(vnode_t *, int);
static int cpr_p_online(cpu_t *, int);
static void cpr_save_mp_state(void);
+#endif
+
int cpr_is_ufs(struct vfs *);
char cpr_default_path[] = CPR_DEFAULT;
@@ -112,6 +116,10 @@ cpr_init(int fcn)
CPR->c_flags |= C_REUSABLE;
else
CPR->c_flags |= C_SUSPENDING;
+ if (fcn == AD_SUSPEND_TO_RAM || fcn == DEV_SUSPEND_TO_RAM) {
+ return (0);
+ }
+#if defined(__sparc)
if (fcn != AD_CPR_NOCOMPRESS && fcn != AD_CPR_TESTNOZ)
CPR->c_flags |= C_COMPRESSING;
/*
@@ -126,6 +134,7 @@ cpr_init(int fcn)
if (cpr_debug & CPR_DEBUG3)
cpr_err(CE_CONT, "Reserved virtual range from 0x%p for writing "
"kas\n", (void *)CPR->c_mapping_area);
+#endif
return (0);
}
@@ -157,6 +166,7 @@ cpr_done(void)
}
+#if defined(__sparc)
/*
* reads config data into cprconfig
*/
@@ -815,6 +825,7 @@ cpr_get_reusable_mode(void)
return (0);
}
+#endif
/*
* clock/time related routines
@@ -828,7 +839,7 @@ cpr_tod_get(cpr_time_t *ctp)
timestruc_t ts;
mutex_enter(&tod_lock);
- ts = tod_get();
+ ts = TODOP_GET(tod_ops);
mutex_exit(&tod_lock);
ctp->tv_sec = (time32_t)ts.tv_sec;
ctp->tv_nsec = (int32_t)ts.tv_nsec;
@@ -857,6 +868,7 @@ cpr_restore_time(void)
clkset(cpr_time_stamp);
}
+#if defined(__sparc)
/*
* CPU ONLINE/OFFLINE CODE
*/
@@ -1104,20 +1116,20 @@ cpr_reusable_mount_check(void)
}
/*
- * Force a fresh read of the cprinfo per uadmin 3 call
+ * return statefile offset in DEV_BSIZE units
*/
-void
-cpr_forget_cprconfig(void)
+int
+cpr_statefile_offset(void)
{
- cprconfig_loaded = 0;
+ return (cpr_statefile_is_spec() ? btod(CPR_SPEC_OFFSET) : 0);
}
-
/*
- * return statefile offset in DEV_BSIZE units
+ * Force a fresh read of the cprinfo per uadmin 3 call
*/
-int
-cpr_statefile_offset(void)
+void
+cpr_forget_cprconfig(void)
{
- return (cpr_statefile_is_spec() ? btod(CPR_SPEC_OFFSET) : 0);
+ cprconfig_loaded = 0;
}
+#endif
diff --git a/usr/src/uts/common/cpr/cpr_mod.c b/usr/src/uts/common/cpr/cpr_mod.c
index 365f102a2b..9358a6ab3a 100644
--- a/usr/src/uts/common/cpr/cpr_mod.c
+++ b/usr/src/uts/common/cpr/cpr_mod.c
@@ -42,16 +42,23 @@
#include <sys/autoconf.h>
#include <sys/machsystm.h>
-extern int i_cpr_is_supported(void);
+extern int i_cpr_is_supported(int sleeptype);
extern int cpr_is_ufs(struct vfs *);
extern int cpr_check_spec_statefile(void);
extern int cpr_reusable_mount_check(void);
-extern void cpr_forget_cprconfig(void);
extern int i_cpr_reusable_supported(void);
extern int i_cpr_reusefini(void);
-
extern struct mod_ops mod_miscops;
+extern int cpr_init(int);
+extern void cpr_done(void);
+extern void i_cpr_stop_other_cpus(void);
+extern int i_cpr_power_down();
+
+#if defined(__sparc)
+extern void cpr_forget_cprconfig(void);
+#endif
+
static struct modlmisc modlmisc = {
&mod_miscops, "checkpoint resume"
};
@@ -68,6 +75,9 @@ kmutex_t cpr_slock; /* cpr serial lock */
cpr_t cpr_state;
int cpr_debug;
int cpr_test_mode; /* true if called via uadmin testmode */
+int cpr_test_point = LOOP_BACK_NONE; /* cpr test point */
+int cpr_mp_enable = 0; /* set to 1 to enable MP suspend */
+major_t cpr_device = 0; /* major number for S3 on one device */
/*
* All the loadable module related code follows
@@ -100,9 +110,25 @@ _info(struct modinfo *modinfop)
return (mod_info(&modlinkage, modinfop));
}
+static
+int
+atoi(char *p)
+{
+ int i;
+
+ i = (*p++ - '0');
+
+ while (*p != '\0')
+ i = 10 * i + (*p++ - '0');
+
+ return (i);
+}
+
int
-cpr(int fcn)
+cpr(int fcn, void *mdep)
{
+
+#if defined(__sparc)
static const char noswapstr[] = "reusable statefile requires "
"that no swap area be configured.\n";
static const char blockstr[] = "reusable statefile must be "
@@ -112,11 +138,71 @@ cpr(int fcn)
"use uadmin A_FREEZE AD_REUSEFINI (uadmin %d %d) "
"to exit reusable statefile mode.\n";
static const char modefmt[] = "%s in reusable mode.\n";
+#endif
register int rc = 0;
- extern int cpr_init(int);
- extern void cpr_done(void);
+ int cpr_sleeptype;
/*
+ * First, reject commands that we don't (yet) support on this arch.
+ * This is easier to understand broken out like this than grotting
+ * through the second switch below.
+ */
+
+ switch (fcn) {
+#if defined(__sparc)
+ case AD_CHECK_SUSPEND_TO_RAM:
+ case AD_SUSPEND_TO_RAM:
+ return (ENOTSUP);
+ case AD_CHECK_SUSPEND_TO_DISK:
+ case AD_SUSPEND_TO_DISK:
+ case AD_CPR_REUSEINIT:
+ case AD_CPR_NOCOMPRESS:
+ case AD_CPR_FORCE:
+ case AD_CPR_REUSABLE:
+ case AD_CPR_REUSEFINI:
+ case AD_CPR_TESTZ:
+ case AD_CPR_TESTNOZ:
+ case AD_CPR_TESTHALT:
+ case AD_CPR_SUSP_DEVICES:
+ cpr_sleeptype = CPR_TODISK;
+ break;
+#endif
+#if defined(__x86)
+ case AD_CHECK_SUSPEND_TO_DISK:
+ case AD_SUSPEND_TO_DISK:
+ case AD_CPR_REUSEINIT:
+ case AD_CPR_NOCOMPRESS:
+ case AD_CPR_FORCE:
+ case AD_CPR_REUSABLE:
+ case AD_CPR_REUSEFINI:
+ case AD_CPR_TESTZ:
+ case AD_CPR_TESTNOZ:
+ case AD_CPR_TESTHALT:
+ case AD_CPR_PRINT:
+ return (ENOTSUP);
+ /* The DEV_* values need to be removed after sys-syspend is fixed */
+ case DEV_CHECK_SUSPEND_TO_RAM:
+ case DEV_SUSPEND_TO_RAM:
+ case AD_CPR_SUSP_DEVICES:
+ case AD_CHECK_SUSPEND_TO_RAM:
+ case AD_SUSPEND_TO_RAM:
+ case AD_LOOPBACK_SUSPEND_TO_RAM_PASS:
+ case AD_LOOPBACK_SUSPEND_TO_RAM_FAIL:
+ case AD_FORCE_SUSPEND_TO_RAM:
+ case AD_DEVICE_SUSPEND_TO_RAM:
+ /*
+ * if MP then do not support suspend to RAM, however override
+ * the MP restriction if cpr_mp_enable has been set
+ */
+ if (ncpus > 1 && cpr_mp_enable == 0)
+ return (ENOTSUP);
+ else
+ cpr_sleeptype = CPR_TORAM;
+ break;
+#endif
+ }
+#if defined(__sparc)
+ /*
* Need to know if we're in reusable mode, but we will likely have
* rebooted since REUSEINIT, so we have to get the info from the
* file system
@@ -125,8 +211,11 @@ cpr(int fcn)
cpr_reusable_mode = cpr_get_reusable_mode();
cpr_forget_cprconfig();
+#endif
+
switch (fcn) {
+#if defined(__sparc)
case AD_CPR_REUSEINIT:
if (!i_cpr_reusable_supported())
return (ENOTSUP);
@@ -188,7 +277,7 @@ cpr(int fcn)
break;
case AD_CPR_CHECK:
- if (!i_cpr_is_supported() || cpr_reusable_mode)
+ if (!i_cpr_is_supported(cpr_sleeptype) || cpr_reusable_mode)
return (ENOTSUP);
return (0);
@@ -196,6 +285,7 @@ cpr(int fcn)
CPR_STAT_EVENT_END("POST CPR DELAY");
cpr_stat_event_print();
return (0);
+#endif
case AD_CPR_DEBUG0:
cpr_debug = 0;
@@ -215,13 +305,55 @@ cpr(int fcn)
cpr_debug |= CPR_DEBUG6;
return (0);
+ /* The DEV_* values need to be removed after sys-syspend is fixed */
+ case DEV_CHECK_SUSPEND_TO_RAM:
+ case DEV_SUSPEND_TO_RAM:
+ case AD_CHECK_SUSPEND_TO_RAM:
+ case AD_SUSPEND_TO_RAM:
+ cpr_test_point = LOOP_BACK_NONE;
+ break;
+
+ case AD_LOOPBACK_SUSPEND_TO_RAM_PASS:
+ cpr_test_point = LOOP_BACK_PASS;
+ break;
+
+ case AD_LOOPBACK_SUSPEND_TO_RAM_FAIL:
+ cpr_test_point = LOOP_BACK_FAIL;
+ break;
+
+ case AD_FORCE_SUSPEND_TO_RAM:
+ cpr_test_point = FORCE_SUSPEND_TO_RAM;
+ break;
+
+ case AD_DEVICE_SUSPEND_TO_RAM:
+ cpr_test_point = DEVICE_SUSPEND_TO_RAM;
+ cpr_device = (major_t)atoi((char *)mdep);
+ break;
+
+ case AD_CPR_SUSP_DEVICES:
+ cpr_test_point = FORCE_SUSPEND_TO_RAM;
+ if (cpr_suspend_devices(ddi_root_node()) != DDI_SUCCESS)
+ cmn_err(CE_WARN,
+ "Some devices did not suspend "
+ "and may be unusable");
+ (void) cpr_resume_devices(ddi_root_node(), 0);
+ return (0);
+
default:
return (ENOTSUP);
}
- if (!i_cpr_is_supported() || !cpr_is_ufs(rootvfs))
+ if (!i_cpr_is_supported(cpr_sleeptype) ||
+ (cpr_sleeptype == CPR_TODISK && !cpr_is_ufs(rootvfs)))
return (ENOTSUP);
+ if (fcn == AD_CHECK_SUSPEND_TO_RAM ||
+ fcn == DEV_CHECK_SUSPEND_TO_RAM) {
+ ASSERT(i_cpr_is_supported(cpr_sleeptype));
+ return (0);
+ }
+
+#if defined(__sparc)
if (fcn == AD_CPR_REUSEINIT) {
if (mutex_tryenter(&cpr_slock) == 0)
return (EBUSY);
@@ -247,6 +379,7 @@ cpr(int fcn)
mutex_exit(&cpr_slock);
return (rc);
}
+#endif
/*
* acquire cpr serial lock and init cpr state structure.
@@ -254,23 +387,39 @@ cpr(int fcn)
if (rc = cpr_init(fcn))
return (rc);
+#if defined(__sparc)
if (fcn == AD_CPR_REUSABLE) {
if ((rc = i_cpr_check_cprinfo()) != 0) {
mutex_exit(&cpr_slock);
return (rc);
}
}
+#endif
/*
* Call the main cpr routine. If we are successful, we will be coming
* down from the resume side, otherwise we are still in suspend.
*/
cpr_err(CE_CONT, "System is being suspended");
- if (rc = cpr_main()) {
+ if (rc = cpr_main(cpr_sleeptype)) {
CPR->c_flags |= C_ERROR;
+ PMD(PMD_SX, ("cpr: Suspend operation failed.\n"))
cpr_err(CE_NOTE, "Suspend operation failed.");
} else if (CPR->c_flags & C_SUSPENDING) {
- extern void cpr_power_down();
+
+ /*
+ * In the suspend to RAM case, by the time we get
+ * control back we're already resumed
+ */
+ if (cpr_sleeptype == CPR_TORAM) {
+ PMD(PMD_SX, ("cpr: cpr CPR_TORAM done\n"))
+ cpr_done();
+ return (rc);
+ }
+
+#if defined(__sparc)
+
+ PMD(PMD_SX, ("cpr: Suspend operation succeeded.\n"))
/*
* Back from a successful checkpoint
*/
@@ -280,6 +429,7 @@ cpr(int fcn)
}
/* make sure there are no more changes to the device tree */
+ PMD(PMD_SX, ("cpr: dev tree freeze\n"))
devtree_freeze();
/*
@@ -288,7 +438,9 @@ cpr(int fcn)
* for us to be preempted, we're essentially single threaded
* from here on out.
*/
- stop_other_cpus();
+ PMD(PMD_SX, ("cpr: stop other cpus\n"))
+ i_cpr_stop_other_cpus();
+ PMD(PMD_SX, ("cpr: spl6\n"))
(void) spl6();
/*
@@ -296,24 +448,27 @@ cpr(int fcn)
* be called when there are no other threads that could be
* accessing devices
*/
+ PMD(PMD_SX, ("cpr: reset leaves\n"))
reset_leaves();
/*
- * If cpr_power_down() succeeds, it'll not return.
+ * If i_cpr_power_down() succeeds, it'll not return
*
* Drives with write-cache enabled need to flush
* their cache.
*/
- if (fcn != AD_CPR_TESTHALT)
- cpr_power_down();
-
+ if (fcn != AD_CPR_TESTHALT) {
+ PMD(PMD_SX, ("cpr: power down\n"))
+ (void) i_cpr_power_down(cpr_sleeptype);
+ }
+ ASSERT(cpr_sleeptype == CPR_TODISK);
+ /* currently CPR_TODISK comes back via a boot path */
CPR_DEBUG(CPR_DEBUG1, "(Done. Please Switch Off)\n");
halt(NULL);
/* NOTREACHED */
+#endif
}
- /*
- * For resuming: release resources and the serial lock.
- */
+ PMD(PMD_SX, ("cpr: cpr done\n"))
cpr_done();
return (rc);
}
diff --git a/usr/src/uts/common/cpr/cpr_stat.c b/usr/src/uts/common/cpr/cpr_stat.c
index 264bb4c9c7..9992f23c82 100644
--- a/usr/src/uts/common/cpr/cpr_stat.c
+++ b/usr/src/uts/common/cpr/cpr_stat.c
@@ -28,7 +28,6 @@
#include <sys/types.h>
#include <sys/ddi.h>
#include <sys/pte.h>
-#include <sys/intreg.h>
#include <sys/cpr.h>
/*
@@ -111,7 +110,7 @@ cpr_stat_event_end(char *name, cpr_time_t *ctp)
cep->ce_sec.etime = tv.tv_sec;
cep->ce_sec.ltime = cep->ce_sec.etime - cep->ce_sec.stime;
cep->ce_sec.mtime = ((cep->ce_sec.mtime * (cep->ce_ntests - 1)) +
- cep->ce_sec.ltime) / cep->ce_ntests;
+ cep->ce_sec.ltime) / cep->ce_ntests;
/*
* calculate 100*milliseconds
@@ -158,10 +157,10 @@ cpr_stat_record_events()
STAT->cs_real_statefsz = cpr_term.real_statef_size;
cur_comprate = ((longlong_t)((longlong_t)
- STAT->cs_nocomp_statefsz*100)/
- STAT->cs_real_statefsz);
+ STAT->cs_nocomp_statefsz*100)/
+ STAT->cs_real_statefsz);
if (STAT->cs_min_comprate == 0 ||
- (STAT->cs_min_comprate > cur_comprate))
+ (STAT->cs_min_comprate > cur_comprate))
STAT->cs_min_comprate = cur_comprate;
}
}
@@ -203,25 +202,25 @@ cpr_stat_event_print()
*/
printf("\nMISCELLANEOUS STATISTICS INFORMATION (units in KBytes)\n\n");
printf("\tUser Pages w/o Swapspace:\t%8lu (%lu pages)\n",
- cp->cs_nosw_pages*PAGESIZE/1000, cp->cs_nosw_pages);
+ cp->cs_nosw_pages*PAGESIZE/1000, cp->cs_nosw_pages);
printf("\tTotal Upages Saved to Statefile:%8d (%d pages)\n",
- cp->cs_upage2statef*PAGESIZE/1000, cp->cs_upage2statef);
+ cp->cs_upage2statef*PAGESIZE/1000, cp->cs_upage2statef);
if (cp->cs_mclustsz)
printf("\tAverage Cluster Size:\t\t%8d (%d.%1d%1d pages)\n\n",
- cp->cs_mclustsz/1000, cp->cs_mclustsz/PAGESIZE,
- ((cp->cs_mclustsz%PAGESIZE)*10/PAGESIZE),
- ((cp->cs_mclustsz%PAGESIZE)*100/PAGESIZE)%10);
+ cp->cs_mclustsz/1000, cp->cs_mclustsz/PAGESIZE,
+ ((cp->cs_mclustsz%PAGESIZE)*10/PAGESIZE),
+ ((cp->cs_mclustsz%PAGESIZE)*100/PAGESIZE)%10);
printf("\tKernel Memory Size:\t\t%8lu\n", cp->cs_nocomp_statefsz/1000);
printf("\tEstimated Statefile Size:\t%8lu\n", cp->cs_est_statefsz/1000);
printf("\tActual Statefile Size:\t\t%8lu\n", cp->cs_real_statefsz/1000);
if (cp->cs_real_statefsz) {
int min = cp->cs_min_comprate;
int new = ((longlong_t)((longlong_t)
- cp->cs_nocomp_statefsz*100)/cp->cs_real_statefsz);
+ cp->cs_nocomp_statefsz*100)/cp->cs_real_statefsz);
printf("\tCompression Ratio:\t\t%5d.%1d%1d (worst %d.%1d%1d)\n",
- new/100, (new%100)/10, new%10,
- min/100, (min%100)/10, min%10);
+ new/100, (new%100)/10, new%10,
+ min/100, (min%100)/10, min%10);
}
}
diff --git a/usr/src/uts/common/cpr/cpr_uthread.c b/usr/src/uts/common/cpr/cpr_uthread.c
index 49ea1dfb1f..e2da80d5b8 100644
--- a/usr/src/uts/common/cpr/cpr_uthread.c
+++ b/usr/src/uts/common/cpr/cpr_uthread.c
@@ -59,7 +59,7 @@ cpr_signal_user(int sig)
for (p = practive; p; p = p->p_next) {
/* only user threads */
if (p->p_exec == NULL || p->p_stat == SZOMB ||
- p == proc_init || p == ttoproc(curthread))
+ p == proc_init || p == ttoproc(curthread))
continue;
mutex_enter(&p->p_lock);
@@ -87,7 +87,7 @@ cpr_stop_user_threads()
return (ESRCH);
cpr_stop_user(count * count * CPR_UTSTOP_WAIT);
} while (cpr_check_user_threads() &&
- (count < CPR_UTSTOP_RETRY || CPR->c_fcn != AD_CPR_FORCE));
+ (count < CPR_UTSTOP_RETRY || CPR->c_fcn != AD_CPR_FORCE));
return (0);
}
@@ -194,11 +194,11 @@ cpr_check_user_threads()
CPR_DEBUG(CPR_DEBUG1, "Suspend failed: "
"cannot stop uthread\n");
cpr_err(CE_WARN, "Suspend cannot stop "
- "process %s (%p:%x).",
- ttoproc(tp)->p_user.u_psargs, (void *)tp,
- tp->t_state);
+ "process %s (%p:%x).",
+ ttoproc(tp)->p_user.u_psargs, (void *)tp,
+ tp->t_state);
cpr_err(CE_WARN, "Process may be waiting for"
- " network request, please try again.");
+ " network request, please try again.");
}
CPR_DEBUG(CPR_DEBUG2, "cant stop t=%p state=%x pfg=%x "
@@ -284,8 +284,6 @@ int
cpr_stop_kernel_threads(void)
{
caddr_t name;
- kthread_id_t tp;
- proc_t *p;
callb_lock_table(); /* Note: we unlock the table in resume. */
@@ -298,6 +296,25 @@ cpr_stop_kernel_threads(void)
return (EBUSY);
}
+ CPR_DEBUG(CPR_DEBUG1, ("done\n"));
+ return (0);
+}
+
+/*
+ * Check to see that kernel threads are stopped.
+ * This should be called while CPU's are paused, and the caller is
+ * effectively running single user, or else we are virtually guaranteed
+ * to fail. The routine should not ASSERT on the paused state or spl
+ * level, as there may be a use for this to verify that things are running
+ * again.
+ */
+int
+cpr_threads_are_stopped(void)
+{
+ caddr_t name;
+ kthread_id_t tp;
+ proc_t *p;
+
/*
* We think we stopped all the kernel threads. Just in case
* someone is not playing by the rules, take a spin through
@@ -320,8 +337,7 @@ cpr_stop_kernel_threads(void)
return (EBUSY);
}
} while ((tp = tp->t_next) != curthread);
- mutex_exit(&pidlock);
- CPR_DEBUG(CPR_DEBUG1, "done\n");
+ mutex_exit(&pidlock);
return (0);
}
diff --git a/usr/src/uts/common/io/asy.c b/usr/src/uts/common/io/asy.c
index 1062cd28f8..12ff96c905 100644
--- a/usr/src/uts/common/io/asy.c
+++ b/usr/src/uts/common/io/asy.c
@@ -230,6 +230,15 @@ static const int standard_com_ports[] = {
static int *com_ports;
static uint_t num_com_ports;
+#ifdef DEBUG
+/*
+ * Set this to true to make the driver pretend to do a suspend. Useful
+ * for debugging suspend/resume code with a serial debugger.
+ */
+boolean_t asy_nosuspend = B_FALSE;
+#endif
+
+
/*
* Baud rate table. Indexed by #defines found in sys/termios.h
*/
@@ -272,6 +281,7 @@ ushort_t asyspdtab[] = {
static int asyrsrv(queue_t *q);
static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr);
static int asyclose(queue_t *q, int flag, cred_t *credp);
+static int asywputdo(queue_t *q, mblk_t *mp, boolean_t);
static int asywput(queue_t *q, mblk_t *mp);
struct module_info asy_info = {
@@ -392,7 +402,7 @@ _fini(void)
asy_addedsoft = 0;
/* free "motherboard-serial-ports" property if allocated */
if (com_ports != NULL && com_ports != (int *)standard_com_ports)
- ddi_prop_free(com_ports);
+ ddi_prop_free(com_ports);
com_ports = NULL;
mutex_destroy(&asy_soft_lock);
ddi_soft_state_fini(&asy_soft_state);
@@ -406,6 +416,59 @@ _info(struct modinfo *modinfop)
return (mod_info(&modlinkage, modinfop));
}
+void
+async_put_suspq(struct asycom *asy, mblk_t *mp)
+{
+ struct asyncline *async = asy->asy_priv;
+
+ ASSERT(mutex_owned(&asy->asy_excl));
+
+ if (async->async_suspqf == NULL)
+ async->async_suspqf = mp;
+ else
+ async->async_suspqb->b_next = mp;
+
+ async->async_suspqb = mp;
+}
+
+static mblk_t *
+async_get_suspq(struct asycom *asy)
+{
+ struct asyncline *async = asy->asy_priv;
+ mblk_t *mp;
+
+ ASSERT(mutex_owned(&asy->asy_excl));
+
+ if ((mp = async->async_suspqf) != NULL) {
+ async->async_suspqf = mp->b_next;
+ mp->b_next = NULL;
+ } else {
+ async->async_suspqb = NULL;
+ }
+ return (mp);
+}
+
+static void
+async_process_suspq(struct asycom *asy)
+{
+ struct asyncline *async = asy->asy_priv;
+ mblk_t *mp;
+
+ ASSERT(mutex_owned(&asy->asy_excl));
+
+ while ((mp = async_get_suspq(asy)) != NULL) {
+ queue_t *q;
+
+ q = async->async_ttycommon.t_writeq;
+ ASSERT(q != NULL);
+ mutex_exit(&asy->asy_excl);
+ (void) asywputdo(q, mp, B_FALSE);
+ mutex_enter(&asy->asy_excl);
+ }
+ async->async_flags &= ~ASYNC_DDI_SUSPENDED;
+ cv_broadcast(&async->async_flags_cv);
+}
+
static int
asy_get_bus_type(dev_info_t *devinfo)
{
@@ -494,7 +557,7 @@ asy_get_io_regnum_isa(dev_info_t *devi, struct asycom *asy)
if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
"reg", (caddr_t)&reglist, &reglen) != DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "asy_get_io_regnum: reg property not found "
- "in devices property list");
+ "in devices property list");
return (-1);
}
@@ -545,9 +608,6 @@ asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd)
struct asycom *asy;
struct asyncline *async;
- if (cmd != DDI_DETACH)
- return (DDI_FAILURE);
-
instance = ddi_get_instance(devi); /* find out which unit */
asy = ddi_get_soft_state(asy_soft_state, instance);
@@ -555,25 +615,104 @@ asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd)
return (DDI_FAILURE);
async = asy->asy_priv;
- DEBUGNOTE2(ASY_DEBUG_INIT, "asy%d: %s shutdown.",
- instance, asy_hw_name(asy));
+ switch (cmd) {
+ case DDI_DETACH:
+ DEBUGNOTE2(ASY_DEBUG_INIT, "asy%d: %s shutdown.",
+ instance, asy_hw_name(asy));
- /* cancel DTR hold timeout */
- if (async->async_dtrtid != 0) {
- (void) untimeout(async->async_dtrtid);
- async->async_dtrtid = 0;
- }
+ /* cancel DTR hold timeout */
+ if (async->async_dtrtid != 0) {
+ (void) untimeout(async->async_dtrtid);
+ async->async_dtrtid = 0;
+ }
+
+ /* remove all minor device node(s) for this device */
+ ddi_remove_minor_node(devi, NULL);
+
+ mutex_destroy(&asy->asy_excl);
+ mutex_destroy(&asy->asy_excl_hi);
+ cv_destroy(&async->async_flags_cv);
+ ddi_remove_intr(devi, 0, asy->asy_iblock);
+ ddi_regs_map_free(&asy->asy_iohandle);
+ asy_soft_state_free(asy);
+ DEBUGNOTE1(ASY_DEBUG_INIT, "asy%d: shutdown complete",
+ instance);
+ break;
+ case DDI_SUSPEND:
+ {
+ unsigned i;
+ uchar_t lsr;
+
+#ifdef DEBUG
+ if (asy_nosuspend)
+ return (DDI_SUCCESS);
+#endif
+ mutex_enter(&asy->asy_excl);
+
+ ASSERT(async->async_ops >= 0);
+ while (async->async_ops > 0)
+ cv_wait(&async->async_ops_cv, &asy->asy_excl);
+
+ async->async_flags |= ASYNC_DDI_SUSPENDED;
+
+ /* Wait for timed break and delay to complete */
+ while ((async->async_flags & (ASYNC_BREAK|ASYNC_DELAY))) {
+ if (cv_wait_sig(&async->async_flags_cv, &asy->asy_excl)
+ == 0) {
+ async_process_suspq(asy);
+ mutex_exit(&asy->asy_excl);
+ return (DDI_FAILURE);
+ }
+ }
+
+ /* Clear untimed break */
+ if (async->async_flags & ASYNC_OUT_SUSPEND)
+ async_resume_utbrk(async);
+
+ mutex_exit(&asy->asy_excl);
+
+ mutex_enter(&asy->asy_soft_sr);
+ mutex_enter(&asy->asy_excl);
+ if (async->async_wbufcid != 0) {
+ bufcall_id_t bcid = async->async_wbufcid;
+ async->async_wbufcid = 0;
+ async->async_flags |= ASYNC_RESUME_BUFCALL;
+ mutex_exit(&asy->asy_excl);
+ unbufcall(bcid);
+ mutex_enter(&asy->asy_excl);
+ }
+ mutex_enter(&asy->asy_excl_hi);
- /* remove all minor device node(s) for this device */
- ddi_remove_minor_node(devi, NULL);
+ /* Disable interrupts from chip */
+ ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
+ asy->asy_flags |= ASY_DDI_SUSPENDED;
+
+ /* Process remaining RX characters and RX errors, if any */
+ lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR);
+ async_rxint(asy, lsr);
+
+ /* Wait for TX to drain */
+ for (i = 1000; i > 0; i--) {
+ lsr = ddi_get8(asy->asy_iohandle,
+ asy->asy_ioaddr + LSR);
+ if ((lsr & (XSRE | XHRE)) == (XSRE | XHRE))
+ break;
+ delay(drv_usectohz(10000));
+ }
+ if (i == 0)
+ cmn_err(CE_WARN,
+ "asy: transmitter wasn't drained before "
+ "driver was suspended");
+
+ mutex_exit(&asy->asy_excl_hi);
+ mutex_exit(&asy->asy_excl);
+ mutex_exit(&asy->asy_soft_sr);
+ break;
+ }
+ default:
+ return (DDI_FAILURE);
+ }
- mutex_destroy(&asy->asy_excl);
- mutex_destroy(&asy->asy_excl_hi);
- cv_destroy(&async->async_flags_cv);
- ddi_remove_intr(devi, 0, asy->asy_iblock);
- ddi_regs_map_free(&asy->asy_iohandle);
- asy_soft_state_free(asy);
- DEBUGNOTE1(ASY_DEBUG_INIT, "asy%d: shutdown complete", instance);
return (DDI_SUCCESS);
}
@@ -610,10 +749,73 @@ asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
DDI_STRICTORDER_ACC,
};
- if (cmd != DDI_ATTACH)
+ instance = ddi_get_instance(devi); /* find out which unit */
+
+ switch (cmd) {
+ case DDI_ATTACH:
+ break;
+ case DDI_RESUME:
+ {
+ struct asyncline *async;
+
+#ifdef DEBUG
+ if (asy_nosuspend)
+ return (DDI_SUCCESS);
+#endif
+ asy = ddi_get_soft_state(asy_soft_state, instance);
+ if (asy == NULL)
+ return (DDI_FAILURE);
+
+ mutex_enter(&asy->asy_soft_sr);
+ mutex_enter(&asy->asy_excl);
+ mutex_enter(&asy->asy_excl_hi);
+
+ async = asy->asy_priv;
+ /* Disable interrupts */
+ ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
+ if (asy_identify_chip(devi, asy) != DDI_SUCCESS) {
+ mutex_exit(&asy->asy_excl_hi);
+ mutex_exit(&asy->asy_excl);
+ mutex_exit(&asy->asy_soft_sr);
+ cmn_err(CE_WARN, "Cannot identify UART chip at %p\n",
+ (void *)asy->asy_ioaddr);
+ return (DDI_FAILURE);
+ }
+ asy->asy_flags &= ~ASY_DDI_SUSPENDED;
+ if (async->async_flags & ASYNC_ISOPEN) {
+ asy_program(asy, ASY_INIT);
+ /* Kick off output */
+ if (async->async_ocnt > 0) {
+ async_resume(async);
+ } else {
+ mutex_exit(&asy->asy_excl_hi);
+ if (async->async_xmitblk)
+ freeb(async->async_xmitblk);
+ async->async_xmitblk = NULL;
+ async_start(async);
+ mutex_enter(&asy->asy_excl_hi);
+ }
+ ASYSETSOFT(asy);
+ }
+ mutex_exit(&asy->asy_excl_hi);
+ mutex_exit(&asy->asy_excl);
+ mutex_exit(&asy->asy_soft_sr);
+
+ mutex_enter(&asy->asy_excl);
+ if (async->async_flags & ASYNC_RESUME_BUFCALL) {
+ async->async_wbufcid = bufcall(async->async_wbufcds,
+ BPRI_HI, (void (*)(void *)) async_reioctl,
+ (void *)(intptr_t)async->async_common->asy_unit);
+ async->async_flags &= ~ASYNC_RESUME_BUFCALL;
+ }
+ async_process_suspq(asy);
+ mutex_exit(&asy->asy_excl);
+ return (DDI_SUCCESS);
+ }
+ default:
return (DDI_FAILURE);
+ }
- instance = ddi_get_instance(devi); /* find out which unit */
ret = ddi_soft_state_zalloc(asy_soft_state, instance);
if (ret != DDI_SUCCESS)
return (DDI_FAILURE);
@@ -773,7 +975,8 @@ asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
*/
mutex_init(&asy->asy_excl, NULL, MUTEX_DRIVER, asy_soft_iblock);
mutex_init(&asy->asy_excl_hi, NULL, MUTEX_DRIVER,
- (void *)asy->asy_iblock);
+ (void *)asy->asy_iblock);
+ mutex_init(&asy->asy_soft_sr, NULL, MUTEX_DRIVER, asy_soft_iblock);
mutex_enter(&asy->asy_excl);
mutex_enter(&asy->asy_excl_hi);
@@ -783,6 +986,7 @@ asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
mutex_exit(&asy->asy_excl);
mutex_destroy(&asy->asy_excl);
mutex_destroy(&asy->asy_excl_hi);
+ mutex_destroy(&asy->asy_soft_sr);
ddi_regs_map_free(&asy->asy_iohandle);
cmn_err(CE_CONT, "Cannot identify UART chip at %p\n",
(void *)asy->asy_ioaddr);
@@ -796,11 +1000,10 @@ asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, DLAB);
/* Set the baud rate to 9600 */
ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + (DAT+DLL),
- asyspdtab[asy->asy_bidx] & 0xff);
+ asyspdtab[asy->asy_bidx] & 0xff);
ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + (DAT+DLH),
- (asyspdtab[asy->asy_bidx] >> 8) & 0xff);
- ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
- asy->asy_lcr);
+ (asyspdtab[asy->asy_bidx] >> 8) & 0xff);
+ ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, asy->asy_lcr);
ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr);
mutex_exit(&asy->asy_excl_hi);
@@ -821,12 +1024,12 @@ asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
ddi_regs_map_free(&asy->asy_iohandle);
mutex_exit(&asy_glob_lock);
cmn_err(CE_CONT,
- "Can not set soft interrupt for ASY driver\n");
+ "Can not set soft interrupt for ASY driver\n");
asy_soft_state_free(asy);
return (DDI_FAILURE);
}
mutex_init(&asy_soft_lock, NULL, MUTEX_DRIVER,
- (void *)asy->asy_iblock);
+ (void *)asy->asy_iblock);
asy_addedsoft++;
}
mutex_exit(&asy_glob_lock);
@@ -845,7 +1048,7 @@ asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
mutex_destroy(&asy->asy_excl_hi);
ddi_regs_map_free(&asy->asy_iohandle);
cmn_err(CE_CONT,
- "Can not set device interrupt for ASY driver\n");
+ "Can not set device interrupt for ASY driver\n");
asy_soft_state_free(asy);
return (DDI_FAILURE);
}
@@ -959,20 +1162,17 @@ asy_getproperty(dev_info_t *devi, struct asycom *asy, const char *property)
if (ret != DDI_PROP_SUCCESS) {
(void) sprintf(name, "com%c-%s", number, property);
len = sizeof (val);
- ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val,
- &len);
+ ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
}
if (ret != DDI_PROP_SUCCESS) {
(void) sprintf(name, "tty0%c-%s", number, property);
len = sizeof (val);
- ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val,
- &len);
+ ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
}
if (ret != DDI_PROP_SUCCESS) {
(void) sprintf(name, "port-%c-%s", letter, property);
len = sizeof (val);
- ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val,
- &len);
+ ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
}
if (ret != DDI_PROP_SUCCESS)
return (-1); /* property non-existant */
@@ -1375,7 +1575,7 @@ again:
kmem_free(termiosp, len);
} else
cmn_err(CE_WARN,
- "asy: couldn't get ttymodes property!");
+ "asy: couldn't get ttymodes property!");
mutex_enter(&asy->asy_excl_hi);
/* eeprom mode support - respect properties */
@@ -1394,8 +1594,9 @@ again:
async->async_startc = CSTART;
async->async_stopc = CSTOP;
asy_program(asy, ASY_INIT);
- } else if ((async->async_ttycommon.t_flags & TS_XCLUDE) &&
- secpolicy_excl_open(cr) != 0) {
+ } else
+ if ((async->async_ttycommon.t_flags & TS_XCLUDE) &&
+ secpolicy_excl_open(cr) != 0) {
mutex_exit(&asy->asy_excl_hi);
mutex_exit(&asy->asy_excl);
return (EBUSY);
@@ -1427,17 +1628,18 @@ again:
mcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR);
ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
- mcr|(asy->asy_mcr&DTR));
+ mcr|(asy->asy_mcr&DTR));
DEBUGCONT3(ASY_DEBUG_INIT,
- "asy%dopen: \"Raise DTR on every open\": make mcr = %x, "
- "make TS_SOFTCAR = %s\n",
- unit, mcr|(asy->asy_mcr&DTR),
- (asy->asy_flags & ASY_IGNORE_CD) ? "ON" : "OFF");
+ "asy%dopen: \"Raise DTR on every open\": make mcr = %x, "
+ "make TS_SOFTCAR = %s\n",
+ unit, mcr|(asy->asy_mcr&DTR),
+ (asy->asy_flags & ASY_IGNORE_CD) ? "ON" : "OFF");
+
if (asy->asy_flags & ASY_IGNORE_CD) {
DEBUGCONT1(ASY_DEBUG_MODEM,
- "asy%dopen: ASY_IGNORE_CD set, set TS_SOFTCAR\n",
- unit);
+ "asy%dopen: ASY_IGNORE_CD set, set TS_SOFTCAR\n",
+ unit);
async->async_ttycommon.t_flags |= TS_SOFTCAR;
}
else
@@ -1448,10 +1650,11 @@ again:
*/
asy->asy_msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR);
DEBUGCONT3(ASY_DEBUG_INIT, "asy%dopen: TS_SOFTCAR is %s, "
- "MSR & DCD is %s\n",
- unit,
- (async->async_ttycommon.t_flags & TS_SOFTCAR) ? "set" : "clear",
- (asy->asy_msr & DCD) ? "set" : "clear");
+ "MSR & DCD is %s\n",
+ unit,
+ (async->async_ttycommon.t_flags & TS_SOFTCAR) ? "set" : "clear",
+ (asy->asy_msr & DCD) ? "set" : "clear");
+
if (asy->asy_msr & DCD)
async->async_flags |= ASYNC_CARR_ON;
else
@@ -1671,33 +1874,34 @@ nodrain:
* If line has HUPCL set or is incompletely opened fix up the modem
* lines.
*/
- DEBUGCONT1(ASY_DEBUG_MODEM,
- "asy%dclose: next check HUPCL flag\n", instance);
+ DEBUGCONT1(ASY_DEBUG_MODEM, "asy%dclose: next check HUPCL flag\n",
+ instance);
mutex_enter(&asy->asy_excl_hi);
if ((async->async_ttycommon.t_cflag & HUPCL) ||
(async->async_flags & ASYNC_WOPEN)) {
DEBUGCONT3(ASY_DEBUG_MODEM,
- "asy%dclose: HUPCL flag = %x, ASYNC_WOPEN flag = %x\n",
- instance,
- async->async_ttycommon.t_cflag & HUPCL,
- async->async_ttycommon.t_cflag & ASYNC_WOPEN);
+ "asy%dclose: HUPCL flag = %x, ASYNC_WOPEN flag = %x\n",
+ instance,
+ async->async_ttycommon.t_cflag & HUPCL,
+ async->async_ttycommon.t_cflag & ASYNC_WOPEN);
async->async_flags |= ASYNC_DTR_DELAY;
/* turn off DTR, RTS but NOT interrupt to 386 */
if (asy->asy_flags & (ASY_IGNORE_CD|ASY_RTS_DTR_OFF)) {
DEBUGCONT3(ASY_DEBUG_MODEM,
- "asy%dclose: ASY_IGNORE_CD flag = %x, "
- "ASY_RTS_DTR_OFF flag = %x\n",
- instance,
- asy->asy_flags & ASY_IGNORE_CD,
- asy->asy_flags & ASY_RTS_DTR_OFF);
- ddi_put8(asy->asy_iohandle,
- asy->asy_ioaddr + MCR, asy->asy_mcr|OUT2);
+ "asy%dclose: ASY_IGNORE_CD flag = %x, "
+ "ASY_RTS_DTR_OFF flag = %x\n",
+ instance,
+ asy->asy_flags & ASY_IGNORE_CD,
+ asy->asy_flags & ASY_RTS_DTR_OFF);
+
+ ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
+ asy->asy_mcr|OUT2);
} else {
DEBUGCONT1(ASY_DEBUG_MODEM,
"asy%dclose: Dropping DTR and RTS\n", instance);
- ddi_put8(asy->asy_iohandle,
- asy->asy_ioaddr + MCR, OUT2);
+ ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
+ OUT2);
}
async->async_dtrtid =
timeout((void (*)())async_dtr_free,
@@ -1707,10 +1911,9 @@ nodrain:
* If nobody's using it now, turn off receiver interrupts.
*/
if ((async->async_flags & (ASYNC_WOPEN|ASYNC_ISOPEN)) == 0) {
- icr = ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + ICR);
+ icr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ICR);
ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR,
- (icr & ~RIEN));
+ (icr & ~RIEN));
}
mutex_exit(&asy->asy_excl_hi);
out:
@@ -1750,9 +1953,12 @@ asy_isbusy(struct asycom *asy)
async = asy->asy_priv;
ASSERT(mutex_owned(&asy->asy_excl));
ASSERT(mutex_owned(&asy->asy_excl_hi));
+/*
+ * XXXX this should be recoded
+ */
return ((async->async_ocnt > 0) ||
- ((ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + LSR) & (XSRE|XHRE)) == 0));
+ ((ddi_get8(asy->asy_iohandle,
+ asy->asy_ioaddr + LSR) & (XSRE|XHRE)) == 0));
}
static void
@@ -1826,7 +2032,7 @@ asy_program(struct asycom *asy, int mode)
#ifdef DEBUG
instance = UNIT(async->async_dev);
DEBUGCONT2(ASY_DEBUG_PROCS,
- "asy%d_program: mode = 0x%08X, enter\n", instance, mode);
+ "asy%d_program: mode = 0x%08X, enter\n", instance, mode);
#endif
baudrate = BAUDINDEX(async->async_ttycommon.t_cflag);
@@ -1836,15 +2042,15 @@ asy_program(struct asycom *asy, int mode)
if (baudrate > CBAUD) {
async->async_ttycommon.t_cflag |= CIBAUDEXT;
async->async_ttycommon.t_cflag |=
- (((baudrate - CBAUD - 1) << IBSHIFT) & CIBAUD);
+ (((baudrate - CBAUD - 1) << IBSHIFT) & CIBAUD);
} else {
async->async_ttycommon.t_cflag &= ~CIBAUDEXT;
async->async_ttycommon.t_cflag |=
- ((baudrate << IBSHIFT) & CIBAUD);
+ ((baudrate << IBSHIFT) & CIBAUD);
}
c_flag = async->async_ttycommon.t_cflag &
- (CLOCAL|CREAD|CSTOPB|CSIZE|PARENB|PARODD|CBAUD|CBAUDEXT);
+ (CLOCAL|CREAD|CSTOPB|CSIZE|PARENB|PARODD|CBAUD|CBAUDEXT);
/* disable interrupts */
ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
@@ -1855,7 +2061,7 @@ asy_program(struct asycom *asy, int mode)
(void) ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ISR);
(void) ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR);
asy->asy_msr = flush_reg = ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + MSR);
+ asy->asy_ioaddr + MSR);
/*
* The device is programmed in the open sequence, if we
* have to hardware handshake, then this is a good time
@@ -1892,17 +2098,16 @@ asy_program(struct asycom *asy, int mode)
if (asy->asy_use_fifo == FIFO_ON) {
for (flush_reg = asy->asy_fifo_buf; flush_reg-- > 0; ) {
(void) ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + DAT);
+ asy->asy_ioaddr + DAT);
}
} else {
flush_reg = ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + DAT);
+ asy->asy_ioaddr + DAT);
}
if (ocflags != (c_flag & ~CLOCAL) || mode == ASY_INIT) {
/* Set line control */
- lcr = ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + LCR);
+ lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR);
lcr &= ~(WLS0|WLS1|STB|PEN|EPS);
if (c_flag & CSTOPB)
@@ -1930,13 +2135,13 @@ asy_program(struct asycom *asy, int mode)
}
/* set the baud rate, unless it is "0" */
- ddi_put8(asy->asy_iohandle,
- asy->asy_ioaddr + LCR, DLAB);
+ ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, DLAB);
+
if (baudrate != 0) {
ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT,
- asyspdtab[baudrate] & 0xff);
+ asyspdtab[baudrate] & 0xff);
ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR,
- (asyspdtab[baudrate] >> 8) & 0xff);
+ (asyspdtab[baudrate] >> 8) & 0xff);
}
/* set the line control modes */
ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, lcr);
@@ -1957,10 +2162,10 @@ asy_program(struct asycom *asy, int mode)
if (baudrate == 0)
ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
- (asy->asy_mcr & RTS) | OUT2);
+ (asy->asy_mcr & RTS) | OUT2);
else
ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
- asy->asy_mcr | OUT2);
+ asy->asy_mcr | OUT2);
/*
* Call the modem status interrupt handler to check for the carrier
@@ -1971,10 +2176,10 @@ asy_program(struct asycom *asy, int mode)
/* Set interrupt control */
DEBUGCONT3(ASY_DEBUG_MODM2,
- "asy%d_program: c_flag & CLOCAL = %x t_cflag & CRTSCTS = %x\n",
- instance,
- c_flag & CLOCAL,
- async->async_ttycommon.t_cflag & CRTSCTS);
+ "asy%d_program: c_flag & CLOCAL = %x t_cflag & CRTSCTS = %x\n",
+ instance, c_flag & CLOCAL,
+ async->async_ttycommon.t_cflag & CRTSCTS);
+
if ((c_flag & CLOCAL) && !(async->async_ttycommon.t_cflag & CRTSCTS))
/*
* direct-wired line ignores DCD, so we don't enable modem
@@ -2026,10 +2231,11 @@ asyintr(caddr_t argasy)
uchar_t interrupt_id, lsr;
interrupt_id = ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + ISR) & 0x0F;
+ asy->asy_ioaddr + ISR) & 0x0F;
async = asy->asy_priv;
+
if ((async == NULL) || asy_addedsoft == 0 ||
- !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) {
+ !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) {
if (interrupt_id & NOINTERRUPT)
return (DDI_INTR_UNCLAIMED);
else {
@@ -2040,30 +2246,31 @@ asyintr(caddr_t argasy)
* reading modem status
*/
(void) ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + LSR);
+ asy->asy_ioaddr + LSR);
(void) ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + DAT);
+ asy->asy_ioaddr + DAT);
asy->asy_msr = ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + MSR);
+ asy->asy_ioaddr + MSR);
return (DDI_INTR_CLAIMED);
}
}
+
mutex_enter(&asy->asy_excl_hi);
/*
* We will loop until the interrupt line is pulled low. asy
* interrupt is edge triggered.
*/
/* CSTYLED */
- for (;; interrupt_id = (ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + ISR) & 0x0F)) {
+ for (;; interrupt_id =
+ (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ISR) & 0x0F)) {
+
if (interrupt_id & NOINTERRUPT)
break;
ret_status = DDI_INTR_CLAIMED;
- DEBUGCONT1(ASY_DEBUG_INTR,
- "asyintr: interrupt_id = 0x%d\n", interrupt_id);
- lsr = ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + LSR);
+ DEBUGCONT1(ASY_DEBUG_INTR, "asyintr: interrupt_id = 0x%d\n",
+ interrupt_id);
+ lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR);
switch (interrupt_id) {
case RxRDY:
case RSTATUS:
@@ -2218,9 +2425,9 @@ async_rxint(struct asycom *asy, uchar_t lsr)
if (!(tp->t_cflag & CREAD)) {
while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) {
(void) (ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + DAT) & 0xff);
+ asy->asy_ioaddr + DAT) & 0xff);
lsr = ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + LSR);
+ asy->asy_ioaddr + LSR);
if (looplim-- < 0) /* limit loop */
break;
}
@@ -2232,7 +2439,7 @@ async_rxint(struct asycom *asy, uchar_t lsr)
s = 0; /* reset error status */
if (lsr & RCA) {
c = ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + DAT) & 0xff;
+ asy->asy_ioaddr + DAT) & 0xff;
/*
* We handle XON/XOFF char if IXON is set,
@@ -2319,8 +2526,7 @@ async_rxint(struct asycom *asy, uchar_t lsr)
else
async->async_sw_overrun = 1;
check_looplim:
- lsr = ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + LSR);
+ lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR);
if (looplim-- < 0) /* limit loop */
break;
}
@@ -2355,19 +2561,19 @@ async_msint_retry:
/* this resets the interrupt */
msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR);
DEBUGCONT10(ASY_DEBUG_STATE,
- "async%d_msint call #%d:\n"
- " transition: %3s %3s %3s %3s\n"
- "current state: %3s %3s %3s %3s\n",
- instance,
- ++(asy->asy_msint_cnt),
- (msr & DCTS) ? "DCTS" : " ",
- (msr & DDSR) ? "DDSR" : " ",
- (msr & DRI) ? "DRI " : " ",
- (msr & DDCD) ? "DDCD" : " ",
- (msr & CTS) ? "CTS " : " ",
- (msr & DSR) ? "DSR " : " ",
- (msr & RI) ? "RI " : " ",
- (msr & DCD) ? "DCD " : " ");
+ "async%d_msint call #%d:\n"
+ " transition: %3s %3s %3s %3s\n"
+ "current state: %3s %3s %3s %3s\n",
+ instance,
+ ++(asy->asy_msint_cnt),
+ (msr & DCTS) ? "DCTS" : " ",
+ (msr & DDSR) ? "DDSR" : " ",
+ (msr & DRI) ? "DRI " : " ",
+ (msr & DDCD) ? "DDCD" : " ",
+ (msr & CTS) ? "CTS " : " ",
+ (msr & DSR) ? "DSR " : " ",
+ (msr & RI) ? "RI " : " ",
+ (msr & DCD) ? "DCD " : " ");
/* If CTS status is changed, do H/W output flow control */
if ((t_cflag & CRTSCTS) && (((asy->asy_msr ^ msr) & CTS) != 0))
@@ -2489,17 +2695,16 @@ begin:
async->async_ext = 0;
/* check for carrier up */
DEBUGCONT3(ASY_DEBUG_MODM2,
- "async%d_softint: asy_msr & DCD = %x, "
- "tp->t_flags & TS_SOFTCAR = %x\n",
- instance,
- asy->asy_msr & DCD,
- tp->t_flags & TS_SOFTCAR);
+ "async%d_softint: asy_msr & DCD = %x, "
+ "tp->t_flags & TS_SOFTCAR = %x\n",
+ instance, asy->asy_msr & DCD, tp->t_flags & TS_SOFTCAR);
+
if (asy->asy_msr & DCD) {
/* carrier present */
if ((async->async_flags & ASYNC_CARR_ON) == 0) {
DEBUGCONT1(ASY_DEBUG_MODM2,
- "async%d_softint: set ASYNC_CARR_ON\n",
- instance);
+ "async%d_softint: set ASYNC_CARR_ON\n",
+ instance);
async->async_flags |= ASYNC_CARR_ON;
if (async->async_flags & ASYNC_ISOPEN) {
mutex_exit(&asy->asy_excl_hi);
@@ -2517,9 +2722,9 @@ begin:
int flushflag;
DEBUGCONT1(ASY_DEBUG_MODEM,
- "async%d_softint: carrier dropped, "
- "so drop DTR\n",
- instance);
+ "async%d_softint: carrier dropped, "
+ "so drop DTR\n",
+ instance);
/*
* Carrier went away.
* Drop DTR, abort any output in
@@ -2528,60 +2733,62 @@ begin:
* notification upstream.
*/
val = ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + MCR);
+ asy->asy_ioaddr + MCR);
ddi_put8(asy->asy_iohandle,
asy->asy_ioaddr + MCR, (val & ~DTR));
+
if (async->async_flags & ASYNC_BUSY) {
- DEBUGCONT0(ASY_DEBUG_BUSY,
+ DEBUGCONT0(ASY_DEBUG_BUSY,
"async_softint: "
"Carrier dropped. "
"Clearing async_ocnt\n");
- async->async_ocnt = 0;
+ async->async_ocnt = 0;
} /* if */
async->async_flags &= ~ASYNC_STOPPED;
if (async->async_flags & ASYNC_ISOPEN) {
- mutex_exit(&asy->asy_excl_hi);
- mutex_exit(&asy->asy_excl);
- (void) putctl(q, M_HANGUP);
- mutex_enter(&asy->asy_excl);
- DEBUGCONT1(ASY_DEBUG_MODEM,
- "async%d_softint: "
- "putctl(q, M_HANGUP)\n",
- instance);
- /*
- * Flush FIFO buffers
- * Any data left in there is invalid now
- */
- if (asy->asy_use_fifo == FIFO_ON)
- asy_reset_fifo(asy, FIFOTXFLSH);
- /*
- * Flush our write queue if we have one.
- *
- * If we're in the midst of close, then flush
- * everything. Don't leave stale ioctls lying
- * about.
- */
- flushflag = (async->async_flags &
- ASYNC_CLOSING) ? FLUSHALL : FLUSHDATA;
- flushq(tp->t_writeq, flushflag);
-
- bp = async->async_xmitblk; /* active msg */
- if (bp != NULL) {
- freeb(bp);
- async->async_xmitblk = NULL;
- }
+ mutex_exit(&asy->asy_excl_hi);
+ mutex_exit(&asy->asy_excl);
+ (void) putctl(q, M_HANGUP);
+ mutex_enter(&asy->asy_excl);
+ DEBUGCONT1(ASY_DEBUG_MODEM,
+ "async%d_softint: "
+ "putctl(q, M_HANGUP)\n",
+ instance);
+ /*
+ * Flush FIFO buffers
+ * Any data left in there is invalid now
+ */
+ if (asy->asy_use_fifo == FIFO_ON)
+ asy_reset_fifo(asy, FIFOTXFLSH);
+ /*
+ * Flush our write queue if we have one.
+ * If we're in the midst of close, then
+ * flush everything. Don't leave stale
+ * ioctls lying about.
+ */
+ flushflag = (async->async_flags &
+ ASYNC_CLOSING) ? FLUSHALL :
+ FLUSHDATA;
+ flushq(tp->t_writeq, flushflag);
+
+ /* active msg */
+ bp = async->async_xmitblk;
+ if (bp != NULL) {
+ freeb(bp);
+ async->async_xmitblk = NULL;
+ }
- mutex_enter(&asy->asy_excl_hi);
- async->async_flags &= ~ASYNC_BUSY;
- /*
- * This message warns of Carrier loss
- * with data left to transmit can hang the
- * system.
- */
- DEBUGCONT0(ASY_DEBUG_MODEM,
- "async_softint: Flushing to "
- "prevent HUPCL hanging\n");
+ mutex_enter(&asy->asy_excl_hi);
+ async->async_flags &= ~ASYNC_BUSY;
+ /*
+ * This message warns of Carrier loss
+ * with data left to transmit can hang
+ * the system.
+ */
+ DEBUGCONT0(ASY_DEBUG_MODEM,
+ "async_softint: Flushing to "
+ "prevent HUPCL hanging\n");
} /* if (ASYNC_ISOPEN) */
} /* if (ASYNC_CARR_ON && CLOCAL) */
async->async_flags &= ~ASYNC_CARR_ON;
@@ -2625,8 +2832,10 @@ begin:
IN_FLOW_STREAMS);
mutex_exit(&asy->asy_excl_hi);
}
- DEBUGCONT2(ASY_DEBUG_INPUT,
- "async%d_softint: %d char(s) in queue.\n", instance, cc);
+
+ DEBUGCONT2(ASY_DEBUG_INPUT, "async%d_softint: %d char(s) in queue.\n",
+ instance, cc);
+
if (!(bp = allocb(cc, BPRI_MED))) {
mutex_exit(&asy->asy_excl);
ttycommon_qfull(&async->async_ttycommon, q);
@@ -2648,7 +2857,7 @@ begin:
if (bp->b_wptr > bp->b_rptr) {
if (!canput(q)) {
asyerror(CE_NOTE, "asy%d: local queue full",
- instance);
+ instance);
freemsg(bp);
} else
(void) putq(q, bp);
@@ -2732,7 +2941,7 @@ rv:
mutex_exit(&asy->asy_excl_hi);
mutex_exit(&asy->asy_excl);
asyerror(CE_NOTE, "asy%d: ring buffer overflow",
- instance);
+ instance);
mutex_enter(&asy->asy_excl);
mutex_enter(&asy->asy_excl_hi);
}
@@ -2775,10 +2984,9 @@ async_restart(void *arg)
if ((async->async_flags & ASYNC_BREAK) &&
!(async->async_flags & ASYNC_OUT_SUSPEND)) {
mutex_enter(&asy->asy_excl_hi);
- lcr = ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + LCR);
+ lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR);
ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
- (lcr & ~SETBREAK));
+ (lcr & ~SETBREAK));
mutex_exit(&asy->asy_excl_hi);
}
async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK);
@@ -2831,9 +3039,9 @@ async_nstart(struct asyncline *async, int mode)
*/
if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY)) {
DEBUGCONT2((mode? ASY_DEBUG_OUT : 0),
- "async%d_nstart: start %s.\n",
- instance,
- async->async_flags & ASYNC_BREAK ? "break" : "busy");
+ "async%d_nstart: start %s.\n",
+ instance,
+ async->async_flags & ASYNC_BREAK ? "break" : "busy");
return;
}
@@ -2851,13 +3059,13 @@ async_nstart(struct asyncline *async, int mode)
*/
if (async->async_flags & ASYNC_DELAY) {
DEBUGCONT1((mode? ASY_DEBUG_OUT : 0),
- "async%d_nstart: start ASYNC_DELAY.\n", instance);
+ "async%d_nstart: start ASYNC_DELAY.\n", instance);
return;
}
if ((q = async->async_ttycommon.t_writeq) == NULL) {
DEBUGCONT1((mode? ASY_DEBUG_OUT : 0),
- "async%d_nstart: start writeq is null.\n", instance);
+ "async%d_nstart: start writeq is null.\n", instance);
return; /* not attached to a stream */
}
@@ -2882,9 +3090,9 @@ async_nstart(struct asyncline *async, int mode)
*/
mutex_enter(&asy->asy_excl_hi);
val = ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + LCR);
- ddi_put8(asy->asy_iohandle,
- asy->asy_ioaddr + LCR, (val | SETBREAK));
+ asy->asy_ioaddr + LCR);
+ ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
+ (val | SETBREAK));
mutex_exit(&asy->asy_excl_hi);
async->async_flags |= ASYNC_BREAK;
(void) timeout(async_restart, (caddr_t)async,
@@ -2977,9 +3185,8 @@ async_nstart(struct asyncline *async, int mode)
if (didsome)
async->async_flags |= ASYNC_PROGRESS;
DEBUGCONT2(ASY_DEBUG_BUSY,
- "async%d_nstart: Set ASYNC_BUSY. async_ocnt=%d\n",
- instance,
- async->async_ocnt);
+ "async%d_nstart: Set ASYNC_BUSY. async_ocnt=%d\n",
+ instance, async->async_ocnt);
async->async_flags |= ASYNC_BUSY;
mutex_exit(&asy->asy_excl_hi);
}
@@ -3123,12 +3330,13 @@ async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp)
* ioctls, so keep the others safe too.
*/
DEBUGCONT2(ASY_DEBUG_IOCTL, "async%d_ioctl: %s\n",
- instance,
- iocp->ioc_cmd == TIOCMGET ? "TIOCMGET" :
- iocp->ioc_cmd == TIOCMSET ? "TIOCMSET" :
- iocp->ioc_cmd == TIOCMBIS ? "TIOCMBIS" :
- iocp->ioc_cmd == TIOCMBIC ? "TIOCMBIC" :
- "other");
+ instance,
+ iocp->ioc_cmd == TIOCMGET ? "TIOCMGET" :
+ iocp->ioc_cmd == TIOCMSET ? "TIOCMSET" :
+ iocp->ioc_cmd == TIOCMBIS ? "TIOCMBIS" :
+ iocp->ioc_cmd == TIOCMBIC ? "TIOCMBIC" :
+ "other");
+
switch (iocp->ioc_cmd) {
case TIOCMGET:
case TIOCGPPS:
@@ -3330,14 +3538,15 @@ async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp)
* clock / (baud * 16) * 16 * 2.
*/
index = BAUDINDEX(
- async->async_ttycommon.t_cflag);
+ async->async_ttycommon.t_cflag);
async->async_flags |= ASYNC_BREAK;
+
while ((ddi_get8(asy->asy_iohandle,
asy->asy_ioaddr + LSR) & XSRE) == 0) {
mutex_exit(&asy->asy_excl_hi);
mutex_exit(&asy->asy_excl);
drv_usecwait(
- 32*asyspdtab[index] & 0xfff);
+ 32*asyspdtab[index] & 0xfff);
mutex_enter(&asy->asy_excl);
mutex_enter(&asy->asy_excl_hi);
}
@@ -3348,23 +3557,23 @@ async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp)
* "async_start" to grab the next message.
*/
val = ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + LCR);
+ asy->asy_ioaddr + LCR);
ddi_put8(asy->asy_iohandle,
- asy->asy_ioaddr + LCR,
- (val | SETBREAK));
+ asy->asy_ioaddr + LCR,
+ (val | SETBREAK));
mutex_exit(&asy->asy_excl_hi);
(void) timeout(async_restart, (caddr_t)async,
drv_usectohz(1000000)/4);
} else {
DEBUGCONT1(ASY_DEBUG_OUT,
- "async%d_ioctl: wait for flush.\n",
- instance);
+ "async%d_ioctl: wait for flush.\n",
+ instance);
mutex_enter(&asy->asy_excl_hi);
asy_waiteot(asy);
mutex_exit(&asy->asy_excl_hi);
DEBUGCONT1(ASY_DEBUG_OUT,
- "async%d_ioctl: ldterm satisfied.\n",
- instance);
+ "async%d_ioctl: ldterm satisfied.\n",
+ instance);
}
break;
@@ -3409,7 +3618,7 @@ async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp)
case TIOCMBIC:
if (iocp->ioc_count != TRANSPARENT) {
DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: "
- "non-transparent\n", instance);
+ "non-transparent\n", instance);
error = miocpullup(mp, sizeof (int));
if (error != 0)
@@ -3417,14 +3626,14 @@ async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp)
mutex_enter(&asy->asy_excl_hi);
(void) asymctl(asy,
- dmtoasy(*(int *)mp->b_cont->b_rptr),
- iocp->ioc_cmd);
+ dmtoasy(*(int *)mp->b_cont->b_rptr),
+ iocp->ioc_cmd);
mutex_exit(&asy->asy_excl_hi);
iocp->ioc_error = 0;
mp->b_datap->db_type = M_IOCACK;
} else {
DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: "
- "transparent\n", instance);
+ "transparent\n", instance);
mcopyin(mp, NULL, sizeof (int), NULL);
}
break;
@@ -3442,12 +3651,11 @@ async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp)
if (iocp->ioc_count == TRANSPARENT) {
DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: "
- "transparent\n", instance);
- mcopyout(mp, NULL, sizeof (int), NULL,
- datamp);
+ "transparent\n", instance);
+ mcopyout(mp, NULL, sizeof (int), NULL, datamp);
} else {
DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: "
- "non-transparent\n", instance);
+ "non-transparent\n", instance);
mioc2ack(mp, datamp, sizeof (int), 0);
}
break;
@@ -3458,7 +3666,7 @@ async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp)
break;
*(struct cons_polledio **)mp->b_cont->b_rptr =
- &asy->polledio;
+ &asy->polledio;
mp->b_datap->db_type = M_IOCACK;
break;
@@ -3498,7 +3706,7 @@ async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp)
*/
mcopyout(mp, NULL, sizeof (boolean_t), NULL, NULL);
*(boolean_t *)mp->b_cont->b_rptr =
- (asy->asy_flags & ASY_CONSOLE) != 0;
+ (asy->asy_flags & ASY_CONSOLE) != 0;
break;
default:
@@ -3534,7 +3742,16 @@ asyrsrv(queue_t *q)
}
/*
- * Put procedure for write queue.
+ * The ASYWPUTDO_NOT_SUSP macro indicates to asywputdo() whether it should
+ * handle messages as though the driver is operating normally or is
+ * suspended. In the suspended case, some or all of the processing may have
+ * to be delayed until the driver is resumed.
+ */
+#define ASYWPUTDO_NOT_SUSP(async, wput) \
+ !((wput) && ((async)->async_flags & ASYNC_DDI_SUSPENDED))
+
+/*
+ * Processing for write queue put procedure.
* Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
* set the flow control character for M_STOPI and M_STARTI messages;
* queue up M_BREAK, M_DELAY, and M_DATA messages for processing
@@ -3545,7 +3762,7 @@ asyrsrv(queue_t *q)
* as we do in ldterm.
*/
static int
-asywput(queue_t *q, mblk_t *mp)
+asywputdo(queue_t *q, mblk_t *mp, boolean_t wput)
{
struct asyncline *async;
struct asycom *asy;
@@ -3555,6 +3772,7 @@ asywput(queue_t *q, mblk_t *mp)
int error;
async = (struct asyncline *)q->q_ptr;
+
#ifdef DEBUG
instance = UNIT(async->async_dev);
#endif
@@ -3577,17 +3795,19 @@ asywput(queue_t *q, mblk_t *mp)
mutex_enter(&asy->asy_excl);
if (async->async_flags & ASYNC_STOPPED) {
async->async_flags &= ~ASYNC_STOPPED;
- /*
- * If an output operation is in progress,
- * resume it. Otherwise, prod the start
- * routine.
- */
- if (async->async_ocnt > 0) {
- mutex_enter(&asy->asy_excl_hi);
- async_resume(async);
- mutex_exit(&asy->asy_excl_hi);
- } else {
- async_start(async);
+ if (ASYWPUTDO_NOT_SUSP(async, wput)) {
+ /*
+ * If an output operation is in progress,
+ * resume it. Otherwise, prod the start
+ * routine.
+ */
+ if (async->async_ocnt > 0) {
+ mutex_enter(&asy->asy_excl_hi);
+ async_resume(async);
+ mutex_exit(&asy->asy_excl_hi);
+ } else {
+ async_start(async);
+ }
}
}
mutex_exit(&asy->asy_excl);
@@ -3606,19 +3826,21 @@ asywput(queue_t *q, mblk_t *mp)
if (*(int *)mp->b_cont->b_rptr != 0) {
DEBUGCONT1(ASY_DEBUG_OUT,
- "async%d_ioctl: flush request.\n",
- instance);
+ "async%d_ioctl: flush request.\n",
+ instance);
(void) putq(q, mp);
- mutex_enter(&asy->asy_excl);
- /*
- * If an TIOCSBRK is in progress,
- * clean it as TIOCCBRK does,
- * then kick off output.
- * If TIOCSBRK is not in progress,
- * just kick off output.
- */
- async_resume_utbrk(async);
+ mutex_enter(&asy->asy_excl);
+ if (ASYWPUTDO_NOT_SUSP(async, wput)) {
+ /*
+ * If an TIOCSBRK is in progress,
+ * clean it as TIOCCBRK does,
+ * then kick off output.
+ * If TIOCSBRK is not in progress,
+ * just kick off output.
+ */
+ async_resume_utbrk(async);
+ }
mutex_exit(&asy->asy_excl);
break;
}
@@ -3636,16 +3858,18 @@ asywput(queue_t *q, mblk_t *mp)
* start routine, just in case.
*/
(void) putq(q, mp);
- mutex_enter(&asy->asy_excl);
- /*
- * If an TIOCSBRK is in progress,
- * clean it as TIOCCBRK does.
- * then kick off output.
- * If TIOCSBRK is not in progress,
- * just kick off output.
- */
- async_resume_utbrk(async);
+ mutex_enter(&asy->asy_excl);
+ if (ASYWPUTDO_NOT_SUSP(async, wput)) {
+ /*
+ * If an TIOCSBRK is in progress,
+ * clean it as TIOCCBRK does.
+ * then kick off output.
+ * If TIOCSBRK is not in progress,
+ * just kick off output.
+ */
+ async_resume_utbrk(async);
+ }
mutex_exit(&asy->asy_excl);
break;
@@ -3653,7 +3877,14 @@ asywput(queue_t *q, mblk_t *mp)
/*
* Do it now.
*/
- async_ioctl(async, q, mp);
+ mutex_enter(&asy->asy_excl);
+ if (ASYWPUTDO_NOT_SUSP(async, wput)) {
+ mutex_exit(&asy->asy_excl);
+ async_ioctl(async, q, mp);
+ break;
+ }
+ async_put_suspq(asy, mp);
+ mutex_exit(&asy->asy_excl);
break;
}
break;
@@ -3667,13 +3898,20 @@ asywput(queue_t *q, mblk_t *mp)
*/
mutex_enter(&asy->asy_excl_hi);
if (async->async_flags & ASYNC_BUSY) {
- DEBUGCONT1(ASY_DEBUG_BUSY, "asy%dwput: "
+ DEBUGCONT1(ASY_DEBUG_BUSY, "asy%dwput: "
"Clearing async_ocnt, "
"leaving ASYNC_BUSY set\n",
instance);
async->async_ocnt = 0;
async->async_flags &= ~ASYNC_BUSY;
} /* if */
+
+ if (ASYWPUTDO_NOT_SUSP(async, wput)) {
+ /* Flush FIFO buffers */
+ if (asy->asy_use_fifo == FIFO_ON) {
+ asy_reset_fifo(asy, FIFOTXFLSH);
+ }
+ }
mutex_exit(&asy->asy_excl_hi);
/* Flush FIFO buffers */
@@ -3693,9 +3931,11 @@ asywput(queue_t *q, mblk_t *mp)
*mp->b_rptr &= ~FLUSHW; /* it has been flushed */
}
if (*mp->b_rptr & FLUSHR) {
- /* Flush FIFO buffers */
- if (asy->asy_use_fifo == FIFO_ON) {
- asy_reset_fifo(asy, FIFORXFLSH);
+ if (ASYWPUTDO_NOT_SUSP(async, wput)) {
+ /* Flush FIFO buffers */
+ if (asy->asy_use_fifo == FIFO_ON) {
+ asy_reset_fifo(asy, FIFORXFLSH);
+ }
}
flushq(RD(q), FLUSHDATA);
qreply(q, mp); /* give the read queues a crack at it */
@@ -3707,9 +3947,11 @@ asywput(queue_t *q, mblk_t *mp)
* We must make sure we process messages that survive the
* write-side flush.
*/
- mutex_enter(&asy->asy_excl);
- async_start(async);
- mutex_exit(&asy->asy_excl);
+ if (ASYWPUTDO_NOT_SUSP(async, wput)) {
+ mutex_enter(&asy->asy_excl);
+ async_start(async);
+ mutex_exit(&asy->asy_excl);
+ }
break;
case M_BREAK:
@@ -3720,44 +3962,64 @@ asywput(queue_t *q, mblk_t *mp)
* and poke the start routine.
*/
(void) putq(q, mp);
- mutex_enter(&asy->asy_excl);
- async_start(async);
- mutex_exit(&asy->asy_excl);
+ if (ASYWPUTDO_NOT_SUSP(async, wput)) {
+ mutex_enter(&asy->asy_excl);
+ async_start(async);
+ mutex_exit(&asy->asy_excl);
+ }
break;
case M_STOPI:
mutex_enter(&asy->asy_excl);
- mutex_enter(&asy->asy_excl_hi);
- if (!(async->async_inflow_source & IN_FLOW_USER)) {
- async_flowcontrol_hw_input(asy, FLOW_STOP,
- IN_FLOW_USER);
- (void) async_flowcontrol_sw_input(asy, FLOW_STOP,
- IN_FLOW_USER);
+ if (ASYWPUTDO_NOT_SUSP(async, wput)) {
+ mutex_enter(&asy->asy_excl_hi);
+ if (!(async->async_inflow_source & IN_FLOW_USER)) {
+ async_flowcontrol_hw_input(asy, FLOW_STOP,
+ IN_FLOW_USER);
+ (void) async_flowcontrol_sw_input(asy,
+ FLOW_STOP, IN_FLOW_USER);
+ }
+ mutex_exit(&asy->asy_excl_hi);
+ mutex_exit(&asy->asy_excl);
+ freemsg(mp);
+ break;
}
- mutex_exit(&asy->asy_excl_hi);
+ async_put_suspq(asy, mp);
mutex_exit(&asy->asy_excl);
- freemsg(mp);
break;
case M_STARTI:
mutex_enter(&asy->asy_excl);
- mutex_enter(&asy->asy_excl_hi);
- if (async->async_inflow_source & IN_FLOW_USER) {
- async_flowcontrol_hw_input(asy, FLOW_START,
- IN_FLOW_USER);
- (void) async_flowcontrol_sw_input(asy, FLOW_START,
- IN_FLOW_USER);
+ if (ASYWPUTDO_NOT_SUSP(async, wput)) {
+ mutex_enter(&asy->asy_excl_hi);
+ if (async->async_inflow_source & IN_FLOW_USER) {
+ async_flowcontrol_hw_input(asy, FLOW_START,
+ IN_FLOW_USER);
+ (void) async_flowcontrol_sw_input(asy,
+ FLOW_START, IN_FLOW_USER);
+ }
+ mutex_exit(&asy->asy_excl_hi);
+ mutex_exit(&asy->asy_excl);
+ freemsg(mp);
+ break;
}
- mutex_exit(&asy->asy_excl_hi);
+ async_put_suspq(asy, mp);
mutex_exit(&asy->asy_excl);
- freemsg(mp);
break;
case M_CTL:
if (MBLKL(mp) >= sizeof (struct iocblk) &&
((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) {
- ((struct iocblk *)mp->b_rptr)->ioc_cmd = MC_HAS_POSIX;
- qreply(q, mp);
+ mutex_enter(&asy->asy_excl);
+ if (ASYWPUTDO_NOT_SUSP(async, wput)) {
+ ((struct iocblk *)mp->b_rptr)->ioc_cmd =
+ MC_HAS_POSIX;
+ mutex_exit(&asy->asy_excl);
+ qreply(q, mp);
+ break;
+ } else {
+ async_put_suspq(asy, mp);
+ }
} else {
/*
* These MC_SERVICE type messages are used by upper
@@ -3784,7 +4046,14 @@ asywput(queue_t *q, mblk_t *mp)
break;
case M_IOCDATA:
- async_iocdata(q, mp);
+ mutex_enter(&asy->asy_excl);
+ if (ASYWPUTDO_NOT_SUSP(async, wput)) {
+ mutex_exit(&asy->asy_excl);
+ async_iocdata(q, mp);
+ break;
+ }
+ async_put_suspq(asy, mp);
+ mutex_exit(&asy->asy_excl);
break;
default:
@@ -3794,6 +4063,12 @@ asywput(queue_t *q, mblk_t *mp)
return (0);
}
+static int
+asywput(queue_t *q, mblk_t *mp)
+{
+ return (asywputdo(q, mp, B_TRUE));
+}
+
/*
* Retry an "ioctl", now that "bufcall" claims we may be able to allocate
* the buffer we need.
@@ -3853,11 +4128,11 @@ async_iocdata(queue_t *q, mblk_t *mp)
mutex_enter(&asy->asy_excl);
DEBUGCONT2(ASY_DEBUG_MODEM, "async%d_iocdata: case %s\n",
- instance,
- csp->cp_cmd == TIOCMGET ? "TIOCMGET" :
- csp->cp_cmd == TIOCMSET ? "TIOCMSET" :
- csp->cp_cmd == TIOCMBIS ? "TIOCMBIS" :
- "TIOCMBIC");
+ instance,
+ csp->cp_cmd == TIOCMGET ? "TIOCMGET" :
+ csp->cp_cmd == TIOCMSET ? "TIOCMSET" :
+ csp->cp_cmd == TIOCMBIS ? "TIOCMBIS" :
+ "TIOCMBIC");
switch (csp->cp_cmd) {
case TIOCMGET:
@@ -3876,9 +4151,8 @@ async_iocdata(queue_t *q, mblk_t *mp)
case TIOCMBIS:
case TIOCMBIC:
mutex_enter(&asy->asy_excl_hi);
- (void) asymctl(asy,
- dmtoasy(*(int *)mp->b_cont->b_rptr),
- csp->cp_cmd);
+ (void) asymctl(asy, dmtoasy(*(int *)mp->b_cont->b_rptr),
+ csp->cp_cmd);
mutex_exit(&asy->asy_excl_hi);
mioc2ack(mp, NULL, 0, 0);
break;
@@ -3927,8 +4201,8 @@ asyischar(cons_polledio_arg_t arg)
{
struct asycom *asy = (struct asycom *)arg;
- return ((ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + LSR) & RCA) != 0);
+ return ((ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & RCA)
+ != 0);
}
/*
@@ -3941,8 +4215,7 @@ asygetchar(cons_polledio_arg_t arg)
while (!asyischar(arg))
drv_usecwait(10);
- return (ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + DAT));
+ return (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + DAT));
}
/*
@@ -3964,19 +4237,19 @@ asymctl(struct asycom *asy, int bits, int how)
case TIOCMSET:
DEBUGCONT2(ASY_DEBUG_MODEM,
- "asy%dmctl: TIOCMSET, bits = %x\n", instance, bits);
+ "asy%dmctl: TIOCMSET, bits = %x\n", instance, bits);
mcr_r = bits; /* Set bits */
break;
case TIOCMBIS:
DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dmctl: TIOCMBIS, bits = %x\n",
- instance, bits);
+ instance, bits);
mcr_r |= bits; /* Mask in bits */
break;
case TIOCMBIC:
DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dmctl: TIOCMBIC, bits = %x\n",
- instance, bits);
+ instance, bits);
mcr_r &= ~bits; /* Mask out bits */
break;
@@ -3990,17 +4263,17 @@ asymctl(struct asycom *asy, int bits, int how)
asy->asy_ioaddr + ICR) & MIEN) {
msr_r = asy->asy_msr;
DEBUGCONT2(ASY_DEBUG_MODEM,
- "asy%dmctl: TIOCMGET, read msr_r = %x\n",
- instance, msr_r);
+ "asy%dmctl: TIOCMGET, read msr_r = %x\n",
+ instance, msr_r);
} else {
msr_r = ddi_get8(asy->asy_iohandle,
- asy->asy_ioaddr + MSR);
+ asy->asy_ioaddr + MSR);
DEBUGCONT2(ASY_DEBUG_MODEM,
- "asy%dmctl: TIOCMGET, read MSR = %x\n",
- instance, msr_r);
+ "asy%dmctl: TIOCMGET, read MSR = %x\n",
+ instance, msr_r);
}
DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dtodm: modem_lines = %x\n",
- instance, asytodm(mcr_r, msr_r));
+ instance, asytodm(mcr_r, msr_r));
return (asytodm(mcr_r, msr_r));
}
diff --git a/usr/src/uts/common/io/audio/sada/drv/audio810/audio810.c b/usr/src/uts/common/io/audio/sada/drv/audio810/audio810.c
index 19cc147773..5001635a43 100644
--- a/usr/src/uts/common/io/audio/sada/drv/audio810/audio810.c
+++ b/usr/src/uts/common/io/audio/sada/drv/audio810/audio810.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -160,6 +160,8 @@ static uint_t audio810_intr(caddr_t);
/*
* Local Routine Prototypes
*/
+static void audio810_set_busy(audio810_state_t *);
+static void audio810_set_idle(audio810_state_t *);
static int audio810_codec_sync(audio810_state_t *);
static int audio810_write_ac97(audio810_state_t *, int, uint16_t);
static int audio810_read_ac97(audio810_state_t *, int, uint16_t *);
@@ -569,17 +571,49 @@ audio810_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
switch (cmd) {
case DDI_ATTACH:
break;
-
- /*
- * now, no suspend/resume supported. we'll do it in the future.
- */
case DDI_RESUME:
ATRACE("I810_attach() DDI_RESUME", NULL);
- audio_sup_log(NULL, CE_WARN,
- "%s%d: i810_attach() resume is not supported yet",
- audio810_name, instance);
- return (DDI_FAILURE);
+ if ((statep = ddi_get_soft_state(audio810_statep, instance)) ==
+ NULL) {
+ audio_sup_log(NULL, CE_WARN,
+ "!attach() DDI_RESUME get soft state failed");
+ return (DDI_FAILURE);
+ }
+
+ ASSERT(dip == statep->dip);
+
+ mutex_enter(&statep->inst_lock);
+
+ ASSERT(statep->i810_suspended == I810_SUSPENDED);
+
+ statep->i810_suspended = I810_NOT_SUSPENDED;
+
+ /* Restore the audio810 chip's state */
+ if (audio810_chip_init(statep, I810_INIT_RESTORE) !=
+ AUDIO_SUCCESS) {
+ audio_sup_log(statep->audio_handle, CE_WARN,
+ "!attach() DDI_RESUME failed to init chip");
+ mutex_exit(&statep->inst_lock);
+ return (DDI_FAILURE);
+ }
+
+ mutex_exit(&statep->inst_lock);
+
+ /* Resume playing and recording, if required */
+ if (audio_sup_restore_state(statep->audio_handle,
+ AUDIO_ALL_DEVICES, AUDIO_BOTH) == AUDIO_FAILURE) {
+ audio_sup_log(statep->audio_handle, CE_WARN,
+ "!attach() DDI_RESUME audio restart failed");
+ }
+
+ mutex_enter(&statep->inst_lock);
+ cv_broadcast(&statep->i810_cv); /* let entry points continue */
+ mutex_exit(&statep->inst_lock);
+
+ ATRACE("audio810_attach() DDI_RESUME done", NULL);
+
+ return (DDI_SUCCESS);
default:
audio_sup_log(NULL, CE_WARN,
"!%s%d: attach() unknown command: 0x%x",
@@ -638,7 +672,7 @@ audio810_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
/* set PCI command register */
cmdreg = pci_config_get16(statep->pci_conf_handle, PCI_CONF_COMM);
pci_config_put16(statep->pci_conf_handle, PCI_CONF_COMM,
- cmdreg | PCI_COMM_IO | PCI_COMM_MAE | PCI_COMM_ME);
+ cmdreg | PCI_COMM_IO | PCI_COMM_MAE | PCI_COMM_ME);
if ((audio810_alloc_sample_buf(statep, I810_DMA_PCM_OUT,
statep->play_buf_size) == AUDIO_FAILURE) ||
@@ -700,6 +734,7 @@ error_unmap:
error_destroy:
ATRACE("audio810_attach() error_destroy", statep);
mutex_destroy(&statep->inst_lock);
+ cv_destroy(&statep->i810_cv);
error_audiosup:
ATRACE("audio810_attach() error_audiosup", statep);
@@ -751,17 +786,33 @@ audio810_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
switch (cmd) {
case DDI_DETACH:
break;
-
- /*
- * now, no suspend/resume supported. we'll do it in the future.
- */
case DDI_SUSPEND:
ATRACE("i810_detach() SUSPEND", statep);
- audio_sup_log(NULL, CE_WARN,
- "%s%d: i810_detach() suspend is not supported yet",
- audio810_name, instance);
- return (DDI_FAILURE);
+ mutex_enter(&statep->inst_lock);
+
+ ASSERT(statep->i810_suspended == I810_NOT_SUSPENDED);
+
+ statep->i810_suspended = I810_SUSPENDED; /* stop new ops */
+
+ /* wait for current operations to complete */
+ while (statep->i810_busy_cnt != 0)
+ cv_wait(&statep->i810_cv, &statep->inst_lock);
+
+ /* stop DMA engines */
+ audio810_stop_dma(statep);
+
+ if (audio_sup_save_state(statep->audio_handle,
+ AUDIO_ALL_DEVICES, AUDIO_BOTH) == AUDIO_FAILURE) {
+ audio_sup_log(statep->audio_handle, CE_WARN,
+ "!detach() DDI_SUSPEND audio save failed");
+ }
+
+ mutex_exit(&statep->inst_lock);
+
+ ATRACE("audio810_detach() DDI_SUSPEND successful", statep);
+
+ return (DDI_SUCCESS);
default:
ATRACE("i810_detach() unknown command", cmd);
audio_sup_log(statep->audio_handle, CE_WARN,
@@ -796,6 +847,7 @@ audio810_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
(void) audio_sup_unregister(statep->audio_handle);
mutex_destroy(&statep->inst_lock);
+ cv_destroy(&statep->i810_cv);
audio810_unmap_regs(statep);
@@ -835,6 +887,13 @@ audio810_intr(caddr_t arg)
statep = (audio810_state_t *)arg;
mutex_enter(&statep->inst_lock);
+
+ if (statep->i810_suspended == I810_SUSPENDED) {
+ ATRACE("audio810_intr() device suspended", NULL);
+ mutex_exit(&statep->inst_lock);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
gsr = I810_BM_GET32(I810_REG_GSR);
/* check if device is interrupting */
@@ -883,6 +942,97 @@ audio810_intr(caddr_t arg)
} /* audio810_intr() */
+/*
+ * audio810_set_busy()
+ *
+ * Description:
+ * This routine is called whenever a routine needs to guarantee
+ * that it will not be suspended. It will also block any routine
+ * while a suspend is going on.
+ *
+ * CAUTION: This routine cannot be called by routines that will
+ * block. Otherwise DDI_SUSPEND will be blocked for a
+ * long time. And that is the wrong thing to do.
+ *
+ * Arguments:
+ * audio810_state_t *statep The device's state structure
+ *
+ * Returns:
+ * void
+ */
+static void
+audio810_set_busy(audio810_state_t *statep)
+{
+ ATRACE("in audio810_set_busy()", statep);
+
+ ASSERT(!mutex_owned(&statep->inst_lock));
+
+ /* get the lock so we are safe */
+ mutex_enter(&statep->inst_lock);
+
+ /* block if we are suspended */
+ while (statep->i810_suspended == I810_SUSPENDED) {
+ cv_wait(&statep->i810_cv, &statep->inst_lock);
+ }
+
+ /*
+ * Okay, we aren't suspended, so mark as busy.
+ * This will keep us from being suspended when we release the lock.
+ */
+ ASSERT(statep->i810_busy_cnt >= 0);
+ statep->i810_busy_cnt++;
+
+ mutex_exit(&statep->inst_lock);
+
+ ATRACE("audio810_set_busy() done", statep);
+
+ ASSERT(!mutex_owned(&statep->inst_lock));
+
+} /* audio810_set_busy() */
+
+/*
+ * audio810_set_idle()
+ *
+ * Description:
+ * This routine reduces the busy count. It then does a cv_broadcast()
+ * if the count is 0 so a waiting DDI_SUSPEND will continue forward.
+ *
+ * Arguments:
+ * audio810_state_t *state The device's state structure
+ *
+ * Returns:
+ * void
+ */
+static void
+audio810_set_idle(audio810_state_t *statep)
+{
+ ATRACE("in audio810_set_idle()", statep);
+
+ ASSERT(!mutex_owned(&statep->inst_lock));
+
+ /* get the lock so we are safe */
+ mutex_enter(&statep->inst_lock);
+
+ ASSERT(statep->i810_suspended == I810_NOT_SUSPENDED);
+
+ /* decrement the busy count */
+ ASSERT(statep->i810_busy_cnt > 0);
+ statep->i810_busy_cnt--;
+
+ /* if no longer busy, then we wake up a waiting SUSPEND */
+ if (statep->i810_busy_cnt == 0) {
+ cv_broadcast(&statep->i810_cv);
+ }
+
+ /* we're done, so unlock */
+ mutex_exit(&statep->inst_lock);
+
+ ATRACE("audio810_set_idle() done", statep);
+
+ ASSERT(!mutex_owned(&statep->inst_lock));
+
+} /* audio810_set_idle() */
+
/* *********************** Mixer Entry Point Routines ******************* */
/*
* audio810_ad_set_config()
@@ -926,6 +1076,8 @@ audio810_ad_set_config(audiohdl_t ahandle, int stream, int command,
statep = audio_sup_get_private(ahandle);
ASSERT(statep);
+ audio810_set_busy(statep);
+
mutex_enter(&statep->inst_lock);
switch (command) {
case AM_SET_GAIN:
@@ -1030,6 +1182,8 @@ audio810_ad_set_config(audiohdl_t ahandle, int stream, int command,
}
mutex_exit(&statep->inst_lock);
+ audio810_set_idle(statep);
+
ATRACE_32("i810_ad_set_config() returning", rc);
return (rc);
@@ -1063,6 +1217,7 @@ audio810_ad_set_format(audiohdl_t ahandle, int stream, int dir,
{
audio810_state_t *statep;
uint16_t val;
+ int rc = AUDIO_FAILURE;
ASSERT(precision == AUDIO_PRECISION_16);
ASSERT(channels == AUDIO_CHANNELS_STEREO);
@@ -1083,6 +1238,8 @@ audio810_ad_set_format(audiohdl_t ahandle, int stream, int dir,
statep = audio_sup_get_private(ahandle);
ASSERT(statep);
+ audio810_set_busy(statep);
+
mutex_enter(&statep->inst_lock);
if (statep->var_sr == B_FALSE) {
@@ -1092,8 +1249,7 @@ audio810_ad_set_format(audiohdl_t ahandle, int stream, int dir,
audio_sup_log(statep->audio_handle, CE_NOTE,
"!ad_set_format() bad sample rate %d\n",
sample_rate);
- mutex_exit(&statep->inst_lock);
- return (AUDIO_FAILURE);
+ goto done;
}
} else {
switch (sample_rate) {
@@ -1111,8 +1267,7 @@ audio810_ad_set_format(audiohdl_t ahandle, int stream, int dir,
case I810_SAMPR48000: break;
default:
ATRACE_32("i810_ad_set_format() bad SR", sample_rate);
- mutex_exit(&statep->inst_lock);
- return (AUDIO_FAILURE);
+ goto done;
}
}
@@ -1140,8 +1295,7 @@ audio810_ad_set_format(audiohdl_t ahandle, int stream, int dir,
audio_sup_log(statep->audio_handle, CE_NOTE,
"!set_format() bad output sample rate %d",
sample_rate);
- mutex_exit(&statep->inst_lock);
- return (AUDIO_FAILURE);
+ goto done;
}
}
@@ -1170,8 +1324,7 @@ audio810_ad_set_format(audiohdl_t ahandle, int stream, int dir,
audio_sup_log(statep->audio_handle, CE_NOTE,
"!set_format() bad input sample rate %d",
sample_rate);
- mutex_exit(&statep->inst_lock);
- return (AUDIO_FAILURE);
+ goto done;
}
}
@@ -1180,12 +1333,15 @@ audio810_ad_set_format(audiohdl_t ahandle, int stream, int dir,
statep->i810_cprecision = precision;
}
+ rc = AUDIO_SUCCESS;
done:
mutex_exit(&statep->inst_lock);
- ATRACE_32("i810_ad_set_format() returning success", 0);
+ audio810_set_idle(statep);
- return (AUDIO_SUCCESS);
+ ATRACE_32("i810_ad_set_format() returning", rc);
+
+ return (rc);
} /* audio810_ad_set_format() */
@@ -1215,6 +1371,8 @@ audio810_ad_start_play(audiohdl_t ahandle, int stream)
statep = audio_sup_get_private(ahandle);
ASSERT(statep);
+ audio810_set_busy(statep);
+
mutex_enter(&statep->inst_lock);
if (statep->flags & I810_DMA_PLAY_PAUSED) {
@@ -1239,6 +1397,9 @@ audio810_ad_start_play(audiohdl_t ahandle, int stream)
done:
mutex_exit(&statep->inst_lock);
+
+ audio810_set_idle(statep);
+
return (rc);
} /* audio810_ad_start_play() */
@@ -1269,17 +1430,20 @@ audio810_ad_pause_play(audiohdl_t ahandle, int stream)
ATRACE("audio810_ad_pause_play() ", ahandle);
ATRACE_32("i810_ad_pause_play() stream", stream);
+ audio810_set_busy(statep);
+
mutex_enter(&statep->inst_lock);
- if ((statep->flags & I810_DMA_PLAY_STARTED) == 0) {
- mutex_exit(&statep->inst_lock);
- return;
- }
+ if ((statep->flags & I810_DMA_PLAY_STARTED) == 0)
+ goto done;
cr = I810_BM_GET8(I810_PCM_OUT_CR);
cr &= ~I810_BM_CR_RUN;
I810_BM_PUT8(I810_PCM_OUT_CR, cr);
statep->flags |= I810_DMA_PLAY_PAUSED;
+done:
mutex_exit(&statep->inst_lock);
+ audio810_set_idle(statep);
+
} /* audio810_ad_pause_play() */
/*
@@ -1308,6 +1472,8 @@ audio810_ad_stop_play(audiohdl_t ahandle, int stream)
statep = audio_sup_get_private(ahandle);
ASSERT(statep);
+ audio810_set_busy(statep);
+
mutex_enter(&statep->inst_lock);
/* pause bus master */
@@ -1323,6 +1489,8 @@ audio810_ad_stop_play(audiohdl_t ahandle, int stream)
mutex_exit(&statep->inst_lock);
+ audio810_set_idle(statep);
+
} /* audio810_ad_stop_play() */
/*
@@ -1344,25 +1512,28 @@ static int
audio810_ad_start_record(audiohdl_t ahandle, int stream)
{
audio810_state_t *statep;
- int rc;
+ int rc = AUDIO_SUCCESS;
ATRACE("audio810_ad_start_record() ", ahandle);
ATRACE_32("i810_ad_start_record() stream", stream);
statep = audio_sup_get_private(ahandle);
ASSERT(statep);
+ audio810_set_busy(statep);
+
mutex_enter(&statep->inst_lock);
- if (statep->flags & I810_DMA_RECD_STARTED) {
- mutex_exit(&statep->inst_lock);
- return (AUDIO_SUCCESS);
- }
+ if (statep->flags & I810_DMA_RECD_STARTED)
+ goto done;
rc = audio810_prepare_record_buf(statep);
if (rc == AUDIO_SUCCESS) {
statep->flags |= I810_DMA_RECD_STARTED;
}
+done:
mutex_exit(&statep->inst_lock);
+ audio810_set_idle(statep);
+
return (rc);
} /* audio810_ad_start_record() */
@@ -1393,6 +1564,8 @@ audio810_ad_stop_record(audiohdl_t ahandle, int stream)
statep = audio_sup_get_private(ahandle);
ASSERT(statep);
+ audio810_set_busy(statep);
+
mutex_enter(&statep->inst_lock);
statep->flags &= ~I810_DMA_RECD_STARTED;
@@ -1407,6 +1580,8 @@ audio810_ad_stop_record(audiohdl_t ahandle, int stream)
mutex_exit(&statep->inst_lock);
+ audio810_set_idle(statep);
+
} /* audio810_ad_stop_record() */
/* *********************** Local Routines *************************** */
@@ -1585,6 +1760,7 @@ audio810_init_state(audio810_state_t *statep, dev_info_t *dip)
return (AUDIO_FAILURE);
}
mutex_init(&statep->inst_lock, NULL, MUTEX_DRIVER, statep->intr_iblock);
+ cv_init(&statep->i810_cv, NULL, CV_DRIVER, NULL);
/* fill in device info strings */
(void) strcpy(statep->i810_dev_info.name, I810_DEV_NAME);
@@ -2419,6 +2595,14 @@ audio810_stop_dma(audio810_state_t *statep)
statep->flags = 0;
+/*
+ * XXXX Not sure what these declarations are for, but I brought them from
+ * the PM gate.
+ */
+ statep->play_buf.io_started = B_FALSE;
+
+ statep->record_buf.io_started = B_FALSE;
+
} /* audio810_stop_dma() */
/*
diff --git a/usr/src/uts/common/io/fdc.c b/usr/src/uts/common/io/fdc.c
index ab8d9b9c18..0e111658e0 100644
--- a/usr/src/uts/common/io/fdc.c
+++ b/usr/src/uts/common/io/fdc.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -472,7 +471,7 @@ fdc_probe(dev_info_t *dip)
}
FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_probe: dip %p",
- (void*)dip));
+ (void*)dip));
if (get_ioaddr(dip, &ioaddr) != DDI_SUCCESS)
return (DDI_PROBE_FAILURE);
@@ -497,7 +496,7 @@ fdc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
char name[MAXNAMELEN];
FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_attach: dip %p",
- (void*)dip));
+ (void*)dip));
switch (cmd) {
case DDI_ATTACH:
@@ -600,6 +599,10 @@ fdc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
ddi_report_dev(dip);
return (DDI_SUCCESS);
+ case DDI_RESUME:
+ return (DDI_SUCCESS);
+ /* break; */
+
default:
return (DDI_FAILURE);
}
@@ -826,11 +829,12 @@ static int
fdc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
struct fdcntlr *fcp;
+ struct fcu_obj *fjp;
int unit;
int rval = 0;
FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_detach: dip %p",
- (void*)dip));
+ (void*)dip));
fcp = ddi_get_driver_private(dip);
@@ -847,8 +851,8 @@ fdc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
if (ddi_dmae_release(fcp->c_dip, fcp->c_dmachan) !=
DDI_SUCCESS)
cmn_err(CE_WARN, "fdc_detach: dma release failed, "
- "dip %p, dmachan %x\n",
- (void*)fcp->c_dip, fcp->c_dmachan);
+ "dip %p, dmachan %x\n",
+ (void*)fcp->c_dip, fcp->c_dmachan);
ddi_prop_remove_all(fcp->c_dip);
ddi_set_driver_private(fcp->c_dip, NULL);
@@ -858,6 +862,35 @@ fdc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
sema_destroy(&fcp->c_selsem);
ddi_soft_state_free(fdc_state_head, ddi_get_instance(dip));
break;
+
+ case DDI_SUSPEND:
+ /*
+ * Following code causes the fdc (floppy controller)
+ * to suspend as long as there are no floppy drives
+ * attached to it.
+ * At present the floppy driver does not support
+ * SUSPEND/RESUME.
+ *
+ * Check if any FD units are attached
+ *
+ * For now, SUSPEND/RESUME is not supported
+ * if a floppy drive is present.
+ * So if any FD unit is attached return DDI_FAILURE
+ */
+ for (unit = 0; unit < NFDUN; unit++) {
+ fjp = fcp->c_unit[unit];
+ if (fjp->fj_flags & FUNIT_DRVATCH) {
+ cmn_err(CE_WARN,
+ "fdc_detach: fd attached, failing SUSPEND");
+ return (DDI_FAILURE);
+ }
+ }
+
+ cmn_err(CE_NOTE, "fdc_detach: SUSPEND fdc");
+
+ rval = DDI_SUCCESS;
+ break;
+
default:
rval = EINVAL;
break;
@@ -892,9 +925,9 @@ fdc_abort(struct fcu_obj *fjp)
if (ddi_dmae_stop(fcp->c_dip, fcp->c_dmachan) !=
DDI_SUCCESS)
cmn_err(CE_WARN,
- "fdc_detach: dma release failed, "
- "dip %p, dmachan %x\n",
- (void*)fcp->c_dip, fcp->c_dmachan);
+ "fdc_detach: dma release failed, "
+ "dip %p, dmachan %x\n",
+ (void*)fcp->c_dip, fcp->c_dmachan);
}
mutex_exit(&fcp->c_lock);
drv_usecwait(500);
@@ -923,7 +956,7 @@ fdc_dkinfo(struct fcu_obj *fjp, struct dk_cinfo *dcp)
struct fdcntlr *fcp = fjp->fj_fdc;
(void) strncpy((char *)&dcp->dki_cname, ddi_get_name(fcp->c_dip),
- DK_DEVLEN);
+ DK_DEVLEN);
dcp->dki_ctype = DKC_UNKNOWN; /* no code for generic PC/AT fdc */
dcp->dki_flags = DKI_FMTTRK;
dcp->dki_addr = fcp->c_regbase;
@@ -931,7 +964,7 @@ fdc_dkinfo(struct fcu_obj *fjp, struct dk_cinfo *dcp)
dcp->dki_prio = fcp->c_intprio;
dcp->dki_vec = fcp->c_intvec;
(void) strncpy((char *)&dcp->dki_dname, ddi_driver_name(fjp->fj_dip),
- DK_DEVLEN);
+ DK_DEVLEN);
dcp->dki_slave = fjp->fj_unit & 3;
dcp->dki_maxtransfer = maxphys / DEV_BSIZE;
return (DDI_SUCCESS);
@@ -1149,7 +1182,7 @@ fdrw(struct fcu_obj *fjp, int funit, int rw, int cyl, int head,
dmar_flags |= (DDI_DMA_STREAMING | DDI_DMA_PARTIAL);
if (ddi_dma_alloc_handle(fcp->c_dip, &fdc_dma_attr, DDI_DMA_SLEEP,
- 0, &csb->csb_dmahandle) != DDI_SUCCESS) {
+ 0, &csb->csb_dmahandle) != DDI_SUCCESS) {
rval = EINVAL;
goto out;
}
@@ -1181,15 +1214,14 @@ fdrw(struct fcu_obj *fjp, int funit, int rw, int cyl, int head,
} else if (rval == DDI_DMA_PARTIAL_MAP) {
csb->csb_handle_bound = 1;
if (ddi_dma_numwin(csb->csb_dmahandle, &csb->csb_dmawincnt) !=
- DDI_SUCCESS) {
+ DDI_SUCCESS) {
cmn_err(CE_WARN, "fdrw: dma numwin failed\n");
rval = EINVAL;
goto out;
}
} else {
cmn_err(CE_WARN,
- "fdrw: dma addr bind handle failed, rval = %d\n",
- rval);
+ "fdrw: dma addr bind handle failed, rval = %d\n", rval);
rval = EINVAL;
goto out;
}
@@ -1269,7 +1301,7 @@ fdtrkformat(struct fcu_obj *fjp, int funit, int cyl, int head, int filldata)
fmdatlen = 4 * numsctr;
if (ddi_dma_alloc_handle(fcp->c_dip, &fdc_dma_attr, DDI_DMA_SLEEP,
- 0, &csb->csb_dmahandle) != DDI_SUCCESS) {
+ 0, &csb->csb_dmahandle) != DDI_SUCCESS) {
rval = EINVAL;
goto out;
}
@@ -1310,15 +1342,15 @@ fdtrkformat(struct fcu_obj *fjp, int funit, int cyl, int head, int filldata)
} else if (rval == DDI_DMA_PARTIAL_MAP) {
csb->csb_handle_bound = 1;
if (ddi_dma_numwin(csb->csb_dmahandle, &csb->csb_dmawincnt) !=
- DDI_SUCCESS) {
+ DDI_SUCCESS) {
cmn_err(CE_WARN, "fdtrkformat: dma numwin failed\n");
rval = EINVAL;
goto out;
}
} else {
cmn_err(CE_WARN,
- "fdtrkformat: dma buf bind handle failed, rval = %d\n",
- rval);
+ "fdtrkformat: dma buf bind handle failed, rval = %d\n",
+ rval);
rval = EINVAL;
goto out;
}
@@ -1579,15 +1611,15 @@ fdcquiesce(struct fdcntlr *fcp)
int unit;
FCERRPRINT(FDEP_L2, FDEM_RESE, (CE_NOTE, "fdcquiesce fcp %p",
- (void*)fcp));
+ (void*)fcp));
ASSERT(MUTEX_HELD(&fcp->c_lock));
mutex_enter(&fcp->c_dorlock);
if (ddi_dmae_stop(fcp->c_dip, fcp->c_dmachan) != DDI_SUCCESS)
cmn_err(CE_WARN, "fdcquiesce: dmae stop failed, "
- "dip %p, dmachan %x\n",
- (void*)fcp->c_dip, fcp->c_dmachan);
+ "dip %p, dmachan %x\n",
+ (void*)fcp->c_dip, fcp->c_dmachan);
fcp->c_digout = (fcp->c_digout & (FD_DMTREN | FD_DRSEL)) | FD_ENABLE;
outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
@@ -2158,7 +2190,7 @@ retry:
*/
mutex_enter(&fcp->c_dorlock);
(void) fdc_motorsm(fjp, FMI_RSTARTCMD,
- fjp->fj_drive->fdd_motoron);
+ fjp->fj_drive->fdd_motoron);
/*
* Return value ignored - fdcmotort deals with failure.
*/
@@ -2202,8 +2234,8 @@ retry:
if (ddi_dmae_prog(fcp->c_dip, &dmaereq, &csb->csb_dmacookie,
fcp->c_dmachan) != DDI_SUCCESS)
cmn_err(CE_WARN, "fdc_exec: dmae prog failed, "
- "dip %p, dmachan %x\n",
- (void*)fcp->c_dip, fcp->c_dmachan);
+ "dip %p, dmachan %x\n",
+ (void*)fcp->c_dip, fcp->c_dmachan);
}
if ((fdc_statemach(fcp) == FXS_DOWT) && !sleep) {
@@ -2400,7 +2432,9 @@ fdrecover(struct fdcntlr *fcp)
(void*)fcp->c_dip, fcp->c_dmachan,
residual);
FCERRPRINT(FDEP_L2, FDEM_RECO,
- (CE_NOTE, "fd unit %d: %s error: dma count=0x%lx residual=0x%x",
+ (CE_NOTE,
+ "fd unit %d: %s error: "
+ "dma count=0x%lx residual=0x%x",
csb->csb_drive,
fdcmds[*csb->csb_cmd & 0x1f].cmdname,
csb->csb_dmacookie.dmac_size, residual));
@@ -2412,8 +2446,10 @@ fdrecover(struct fdcntlr *fcp)
*/
if (++csb->csb_ourtrys <= OURUN_TRIES) {
FCERRPRINT(FDEP_L2, FDEM_RECO,
-(CE_NOTE, "fd unit %d: %s error: over/under-run",
-csb->csb_drive, fdcmds[*csb->csb_cmd & 0x1f].cmdname));
+ (CE_NOTE,
+ "fd unit %d: %s error: over/under-run",
+ csb->csb_drive,
+ fdcmds[*csb->csb_cmd & 0x1f].cmdname));
return (0);
} else
/*
@@ -2445,9 +2481,12 @@ csb->csb_drive, fdcmds[*csb->csb_cmd & 0x1f].cmdname));
if (csb->csb_opflags &
(CSB_OFDMARD | CSB_OFDMAWT)) {
FCERRPRINT(FDEP_L4, FDEM_RECO,
-(CE_WARN, "fd unit %d: %s error: st0=0x%x st1=0x%x st2=0x%x",
+ (CE_WARN,
+ "fd unit %d: %s error: "
+ "st0=0x%x st1=0x%x st2=0x%x",
csb->csb_drive,
-fdcmds[*csb->csb_cmd & 0x1f].cmdname,
+ fdcmds[*csb->csb_cmd &
+ 0x1f].cmdname,
*csb->csb_rslt, csb->csb_rslt[1],
csb->csb_rslt[2]));
}
@@ -2681,8 +2720,8 @@ fdwatch(void *arg)
if (fcp->c_flags & FCFLG_WAITING) {
if (ddi_dmae_stop(fcp->c_dip, fcp->c_dmachan) != DDI_SUCCESS)
cmn_err(CE_WARN, "fdwatch: dmae stop failed, "
- "dip %p, dmachan %x\n",
- (void*)fcp->c_dip, fcp->c_dmachan);
+ "dip %p, dmachan %x\n",
+ (void*)fcp->c_dip, fcp->c_dmachan);
csb = &fcp->c_csb;
FCERRPRINT(FDEP_L3, FDEM_WATC,
(CE_WARN, "fdcwatch unit %d: xstate = %d",
@@ -3168,7 +3207,7 @@ get_ioaddr(dev_info_t *dip, int *ioaddr)
} *reglist;
if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
- "reg", (caddr_t)&reglist, &reglen) != DDI_PROP_SUCCESS) {
+ "reg", (caddr_t)&reglist, &reglen) != DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "fdc: reg property not found");
return (DDI_FAILURE);
}
diff --git a/usr/src/uts/common/io/i8042.c b/usr/src/uts/common/io/i8042.c
index 42bcbb8d2a..9c9e568abc 100644
--- a/usr/src/uts/common/io/i8042.c
+++ b/usr/src/uts/common/io/i8042.c
@@ -382,6 +382,23 @@ static void i8042_send(struct i8042 *global, int reg, unsigned char cmd);
unsigned int i8042_unclaimed_interrupts = 0;
+static void
+i8042_discard_junk_data(struct i8042 *global)
+{
+ /* Discard any junk data that may have been left around */
+ for (;;) {
+ unsigned char stat;
+
+ stat = ddi_get8(global->io_handle,
+ global->io_addr + I8042_STAT);
+ if (! (stat & I8042_STAT_OUTBF))
+ break;
+ (void) ddi_get8(global->io_handle,
+ global->io_addr + I8042_DATA);
+
+ }
+}
+
static int
i8042_cleanup(struct i8042 *global)
{
@@ -508,7 +525,7 @@ i8042_purge_outbuf(struct i8042 *global)
if (i8042_wait_obf(global))
break;
(void) ddi_get8(global->io_handle,
- global->io_addr + I8042_DATA);
+ global->io_addr + I8042_DATA);
}
/*
@@ -537,10 +554,9 @@ i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
switch (cmd) {
case DDI_RESUME:
-#ifdef __sparc
global = (struct i8042 *)ddi_get_driver_private(dip);
+ i8042_discard_junk_data(global);
i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL);
-#endif
return (DDI_SUCCESS);
case DDI_ATTACH:
@@ -628,7 +644,7 @@ i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
global->iblock_cookies = NULL;
mutex_init(&global->i8042_mutex, NULL, MUTEX_DRIVER,
- (global->nintrs > 0) ? global->iblock_cookies[0] : NULL);
+ (global->nintrs > 0) ? global->iblock_cookies[0] : NULL);
mutex_init(&global->i8042_out_mutex, NULL, MUTEX_DRIVER, NULL);
@@ -816,8 +832,8 @@ i8042_map(
}
if (rnumber < 0 || rnumber >= iprop_len) {
cmn_err(CE_WARN, "%s #%d: bad map request for %s@%s",
- DRIVER_NAME(dip), ddi_get_instance(dip),
- ddi_node_name(rdip), ddi_get_name_addr(rdip));
+ DRIVER_NAME(dip), ddi_get_instance(dip),
+ ddi_node_name(rdip), ddi_get_name_addr(rdip));
return (DDI_FAILURE);
}
#endif
@@ -838,9 +854,9 @@ i8042_map(
default:
#if defined(DEBUG)
cmn_err(CE_WARN, "%s #%d: unknown map type %d for %s@%s",
- DRIVER_NAME(dip), ddi_get_instance(dip),
- mp->map_type,
- ddi_node_name(rdip), ddi_get_name_addr(rdip));
+ DRIVER_NAME(dip), ddi_get_instance(dip),
+ mp->map_type,
+ ddi_node_name(rdip), ddi_get_name_addr(rdip));
#endif
return (DDI_FAILURE);
}
@@ -848,9 +864,9 @@ i8042_map(
#if defined(DEBUG)
if (offset != 0 || len != 0) {
cmn_err(CE_WARN,
- "%s #%d: partial mapping attempt for %s@%s ignored",
- DRIVER_NAME(dip), ddi_get_instance(dip),
- ddi_node_name(rdip), ddi_get_name_addr(rdip));
+ "%s #%d: partial mapping attempt for %s@%s ignored",
+ DRIVER_NAME(dip), ddi_get_instance(dip),
+ ddi_node_name(rdip), ddi_get_name_addr(rdip));
}
#endif
@@ -901,7 +917,7 @@ i8042_map(
default:
cmn_err(CE_WARN, "%s: map operation %d not supported",
- DRIVER_NAME(dip), mp->map_op);
+ DRIVER_NAME(dip), mp->map_op);
return (DDI_FAILURE);
}
}
@@ -986,7 +1002,7 @@ i8042_intr(caddr_t arg)
#if defined(DEBUG)
if (port->overruns % 50 == 1) {
cmn_err(CE_WARN, "i8042/%d: %d overruns\n",
- which_port, port->overruns);
+ which_port, port->overruns);
}
#endif
mutex_exit(&global->i8042_mutex);
@@ -1039,7 +1055,7 @@ i8042_send(struct i8042 *global, int reg, unsigned char val)
/*CONSTANTCONDITION*/
while (1) {
stat = ddi_get8(global->io_handle,
- global->io_addr + I8042_STAT);
+ global->io_addr + I8042_STAT);
if ((stat & I8042_STAT_INBF) == 0) {
ddi_put8(global->io_handle, global->io_addr+reg, val);
@@ -1113,7 +1129,7 @@ i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr)
} else {
#if defined(DEBUG)
cmn_err(CE_WARN,
- "i8042: Tried to read from empty buffer");
+ "i8042: Tried to read from empty buffer");
#endif
ret = 0;
}
@@ -1127,7 +1143,7 @@ i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr)
case I8042_INT_OUTPUT_DATA:
case I8042_POLL_OUTPUT_DATA:
cmn_err(CE_WARN, "i8042: read of write-only register 0x%p",
- (void *)addr);
+ (void *)addr);
ret = 0;
break;
#endif
@@ -1137,7 +1153,7 @@ i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr)
return (B_TRUE);
for (;;) {
stat = ddi_get8(global->io_handle,
- global->io_addr + I8042_STAT);
+ global->io_addr + I8042_STAT);
if ((stat & I8042_STAT_OUTBF) == 0)
return (B_FALSE);
switch (port->which) {
@@ -1151,13 +1167,13 @@ i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr)
break;
default:
cmn_err(CE_WARN, "data from unknown port: %d",
- port->which);
+ port->which);
}
/*
* Data for wrong port pending; discard it.
*/
(void) ddi_get8(global->io_handle,
- global->io_addr + I8042_DATA);
+ global->io_addr + I8042_DATA);
}
/* NOTREACHED */
@@ -1170,7 +1186,7 @@ i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr)
}
stat = ddi_get8(global->io_handle,
- global->io_addr + I8042_STAT);
+ global->io_addr + I8042_STAT);
if ((stat & I8042_STAT_OUTBF) == 0) {
#if defined(DEBUG)
prom_printf("I8042_POLL_INPUT_DATA: no data!\n");
@@ -1178,7 +1194,7 @@ i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr)
return (0);
}
ret = ddi_get8(global->io_handle,
- global->io_addr + I8042_DATA);
+ global->io_addr + I8042_DATA);
switch (port->which) {
case MAIN_PORT:
if ((stat & I8042_STAT_AUXBF) == 0)
@@ -1197,7 +1213,7 @@ i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr)
default:
#if defined(DEBUG)
cmn_err(CE_WARN, "i8042: read of undefined register 0x%p",
- (void *)addr);
+ (void *)addr);
#endif
ret = 0;
break;
@@ -1240,12 +1256,12 @@ i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value)
case I8042_POLL_INPUT_AVAIL:
case I8042_POLL_INPUT_DATA:
cmn_err(CE_WARN, "i8042: write of read-only register 0x%p",
- (void *)addr);
+ (void *)addr);
break;
default:
cmn_err(CE_WARN, "i8042: read of undefined register 0x%p",
- (void *)addr);
+ (void *)addr);
break;
#endif
}
@@ -1391,7 +1407,7 @@ i8042_ctlops(dev_info_t *dip, dev_info_t *rdip,
(void) sprintf(name, "%d", which_port);
ddi_set_name_addr(child, name);
ddi_set_parent_data(child,
- (caddr_t)&global->i8042_ports[which_port]);
+ (caddr_t)&global->i8042_ports[which_port]);
return (DDI_SUCCESS);
case DDI_CTLOPS_UNINITCHILD:
@@ -1402,8 +1418,8 @@ i8042_ctlops(dev_info_t *dip, dev_info_t *rdip,
case DDI_CTLOPS_REPORTDEV:
cmn_err(CE_CONT, "?8042 device: %s@%s, %s # %d\n",
- ddi_node_name(rdip), ddi_get_name_addr(rdip),
- DRIVER_NAME(rdip), ddi_get_instance(rdip));
+ ddi_node_name(rdip), ddi_get_name_addr(rdip),
+ DRIVER_NAME(rdip), ddi_get_instance(rdip));
return (DDI_SUCCESS);
default:
diff --git a/usr/src/uts/common/io/kb8042/kb8042.c b/usr/src/uts/common/io/kb8042/kb8042.c
index 6799e6d75a..0e8369b076 100644
--- a/usr/src/uts/common/io/kb8042/kb8042.c
+++ b/usr/src/uts/common/io/kb8042/kb8042.c
@@ -431,6 +431,14 @@ kb8042_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
kb8042->w_init = 0;
kb8042_init(kb8042, B_TRUE);
kb8042_setled(kb8042, leds, B_FALSE);
+ mutex_enter(&kb8042->w_hw_mutex);
+ kb8042->suspended = B_FALSE;
+ if (kb8042->w_qp != NULL) {
+ enableok(WR(kb8042->w_qp));
+ qenable(WR(kb8042->w_qp));
+ }
+ cv_broadcast(&kb8042->suspend_cv);
+ mutex_exit(&kb8042->w_hw_mutex);
return (DDI_SUCCESS);
case DDI_ATTACH:
@@ -480,7 +488,8 @@ kb8042_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
}
mutex_init(&kb8042->w_hw_mutex, NULL, MUTEX_DRIVER, kb8042->w_iblock);
-
+ cv_init(&kb8042->ops_cv, NULL, CV_DRIVER, NULL);
+ cv_init(&kb8042->suspend_cv, NULL, CV_DRIVER, NULL);
kb8042->init_state |= KB8042_HW_MUTEX_INITTED;
kb8042_init(kb8042, B_FALSE);
@@ -552,6 +561,12 @@ kb8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
switch (cmd) {
case DDI_SUSPEND:
+ mutex_enter(&kb8042->w_hw_mutex);
+ ASSERT(kb8042->ops >= 0);
+ while (kb8042->ops > 0)
+ cv_wait(&kb8042->ops_cv, &kb8042->w_hw_mutex);
+ kb8042->suspended = B_TRUE;
+ mutex_exit(&kb8042->w_hw_mutex);
return (DDI_SUCCESS);
case DDI_DETACH:
@@ -606,8 +621,11 @@ kb8042_cleanup(struct kb8042 *kb8042)
{
ASSERT(kb8042_dip != NULL);
- if (kb8042->init_state & KB8042_HW_MUTEX_INITTED)
+ if (kb8042->init_state & KB8042_HW_MUTEX_INITTED) {
+ cv_destroy(&kb8042->suspend_cv);
+ cv_destroy(&kb8042->ops_cv);
mutex_destroy(&kb8042->w_hw_mutex);
+ }
if (kb8042->init_state & KB8042_INTR_ADDED)
ddi_remove_intr(kb8042_dip, 0, kb8042->w_iblock);
@@ -660,9 +678,19 @@ kb8042_open(queue_t *qp, dev_t *devp, int flag, int sflag, cred_t *credp)
kb8042 = &Kdws;
+ mutex_enter(&kb8042->w_hw_mutex);
+ while (kb8042->suspended) {
+ if (cv_wait_sig(&kb8042->suspend_cv, &kb8042->w_hw_mutex) ==
+ 0) {
+ mutex_exit(&kb8042->w_hw_mutex);
+ return (EINTR);
+ }
+ }
+
kb8042->w_dev = *devp;
if (qp->q_ptr) {
+ mutex_exit(&kb8042->w_hw_mutex);
return (0);
}
qp->q_ptr = (caddr_t)kb8042;
@@ -670,6 +698,10 @@ kb8042_open(queue_t *qp, dev_t *devp, int flag, int sflag, cred_t *credp)
if (!kb8042->w_qp)
kb8042->w_qp = qp;
+ ASSERT(kb8042->ops >= 0);
+ kb8042->ops++;
+ mutex_exit(&kb8042->w_hw_mutex);
+
kb8042_get_initial_leds(kb8042, &initial_leds, &initial_led_mask);
err = kbtrans_streams_init(qp, sflag, credp,
(struct kbtrans_hardware *)kb8042, &kb8042_callbacks,
@@ -700,6 +732,13 @@ kb8042_open(queue_t *qp, dev_t *devp, int flag, int sflag, cred_t *credp)
kbtrans_streams_enable(kb8042->hw_kbtrans);
+ mutex_enter(&kb8042->w_hw_mutex);
+ ASSERT(kb8042->ops > 0);
+ kb8042->ops--;
+ if (kb8042->ops == 0)
+ cv_broadcast(&kb8042->ops_cv);
+ mutex_exit(&kb8042->w_hw_mutex);
+
return (0);
}
@@ -714,11 +753,31 @@ kb8042_close(queue_t *qp, int flag, cred_t *credp)
kb8042 = (struct kb8042 *)qp->q_ptr;
+ mutex_enter(&kb8042->w_hw_mutex);
+ while (kb8042->suspended) {
+ if (cv_wait_sig(&kb8042->suspend_cv, &kb8042->w_hw_mutex) ==
+ 0) {
+ mutex_exit(&kb8042->w_hw_mutex);
+ return (EINTR);
+ }
+ }
+
+ ASSERT(kb8042->ops >= 0);
+ kb8042->ops++;
+ mutex_exit(&kb8042->w_hw_mutex);
+
(void) kbtrans_streams_fini(kb8042->hw_kbtrans);
kb8042->w_qp = (queue_t *)NULL;
qprocsoff(qp);
+ mutex_enter(&kb8042->w_hw_mutex);
+ ASSERT(kb8042->ops > 0);
+ kb8042->ops--;
+ if (kb8042->ops == 0)
+ cv_broadcast(&kb8042->ops_cv);
+ mutex_exit(&kb8042->w_hw_mutex);
+
return (0);
}
@@ -728,10 +787,27 @@ kb8042_wsrv(queue_t *qp)
struct kb8042 *kb8042;
mblk_t *mp;
+ boolean_t suspended;
kb8042 = (struct kb8042 *)qp->q_ptr;
+ mutex_enter(&kb8042->w_hw_mutex);
+ suspended = kb8042->suspended;
+ ASSERT(kb8042->ops >= 0);
+ if (!suspended)
+ kb8042->ops++;
+ mutex_exit(&kb8042->w_hw_mutex);
+
+#ifdef NO_KB_DEBUG
+ while (!suspended && (mp = getq(qp)) != NULL) {
+#else
+ /*
+ * Not taking keyboard input while suspending can make debugging
+ * difficult. However, we still do the ops counting so that we
+ * don't suspend at a bad time.
+ */
while ((mp = getq(qp))) {
+#endif
switch (kbtrans_streams_message(kb8042->hw_kbtrans, mp)) {
case KBTRANS_MESSAGE_HANDLED:
continue;
@@ -765,6 +841,16 @@ kb8042_wsrv(queue_t *qp)
continue;
}
}
+
+ mutex_enter(&kb8042->w_hw_mutex);
+ if (!suspended) {
+ ASSERT(kb8042->ops > 0);
+ kb8042->ops--;
+ if (kb8042->ops == 0)
+ cv_broadcast(&kb8042->ops_cv);
+ }
+ mutex_exit(&kb8042->w_hw_mutex);
+
return (0);
}
diff --git a/usr/src/uts/common/io/kb8042/kb8042.h b/usr/src/uts/common/io/kb8042/kb8042.h
index 33d849669e..6232d38279 100644
--- a/usr/src/uts/common/io/kb8042/kb8042.h
+++ b/usr/src/uts/common/io/kb8042/kb8042.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,12 +18,13 @@
*
* CDDL HEADER END
*/
+
/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
/* All Rights Reserved */
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -111,6 +111,10 @@ struct kb8042 {
int simulated_kbd_type;
uint32_t init_state;
int break_received;
+ boolean_t suspended;
+ int ops;
+ kcondvar_t suspend_cv;
+ kcondvar_t ops_cv;
};
#define KB_COMMAND_STATE_IDLE 0
diff --git a/usr/src/uts/common/io/pci-ide/pci-ide.c b/usr/src/uts/common/io/pci-ide/pci-ide.c
index c47157dd31..924aec38d7 100644
--- a/usr/src/uts/common/io/pci-ide/pci-ide.c
+++ b/usr/src/uts/common/io/pci-ide/pci-ide.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -47,6 +47,7 @@
#include <sys/pci_intr_lib.h>
int pciide_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+int pciide_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
#define PCIIDE_NATIVE_MODE(dip) \
(!ddi_prop_exists(DDI_DEV_T_ANY, (dip), DDI_PROP_DONTPASS, \
@@ -154,7 +155,7 @@ struct dev_ops pciide_ops = {
nulldev, /* identify */
nulldev, /* probe */
pciide_attach, /* attach */
- nodev, /* detach */
+ pciide_detach, /* detach */
nodev, /* reset */
(struct cb_ops *)0, /* driver operations */
&pciide_bus_ops /* bus operations */
@@ -203,9 +204,8 @@ pciide_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
ddi_acc_handle_t conf_hdl = NULL;
int rc;
-
- if (cmd == DDI_ATTACH) {
-
+ switch (cmd) {
+ case DDI_ATTACH:
/*
* Make sure bus-mastering is enabled, even if
* BIOS didn't.
@@ -225,13 +225,55 @@ pciide_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
cmdreg | PCI_COMM_ME);
}
pci_config_teardown(&conf_hdl);
+ return (DDI_SUCCESS);
+ case DDI_RESUME:
+ /* Restore our PCI configuration header */
+ if (pci_restore_config_regs(dip) != DDI_SUCCESS) {
+ /*
+ * XXXX
+ * This is a pretty bad thing. However, for some
+ * reason it always happens. To further complicate
+ * things, it appears if we just ignore this, we
+ * properly resume. For now, all I want to do is
+ * to generate this message so that it doesn't get
+ * forgotten.
+ */
+ cmn_err(CE_WARN,
+ "Couldn't restore PCI config regs for %s(%p)",
+ ddi_node_name(dip), (void *) dip);
+ }
+#ifdef DEBUG
+ /* Bus mastering should still be enabled */
+ if (pci_config_setup(dip, &conf_hdl) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ cmdreg = pci_config_get16(conf_hdl, PCI_CONF_COMM);
+ ASSERT((cmdreg & PCI_COMM_ME) != 0);
+ pci_config_teardown(&conf_hdl);
+#endif
return (DDI_SUCCESS);
- } else {
- return (DDI_FAILURE);
}
+
+ return (DDI_FAILURE);
}
+/*ARGSUSED*/
+int
+pciide_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_DETACH:
+ return (DDI_SUCCESS);
+ case DDI_SUSPEND:
+ /* Save our PCI configuration header */
+ if (pci_save_config_regs(dip) != DDI_SUCCESS) {
+ /* Don't suspend if we cannot save config regs */
+ return (DDI_FAILURE);
+ }
+ return (DDI_SUCCESS);
+ }
+ return (DDI_FAILURE);
+}
/*ARGSUSED*/
static int
@@ -295,9 +337,9 @@ pciide_ddi_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
old_rnumber = rnumber;
new_rnumber
- = pciide_pre26_rnumber_map(dip, old_rnumber);
+ = pciide_pre26_rnumber_map(dip, old_rnumber);
PDBG(("pciide rnumber old %d new %d\n",
- old_rnumber, new_rnumber));
+ old_rnumber, new_rnumber));
rnumber = new_rnumber;
}
@@ -454,7 +496,7 @@ pciide_initchild(dev_info_t *mydip, dev_info_t *cdip)
* property in the ata.conf file.
*/
vec = ddi_prop_get_int(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
- "interrupts", -1);
+ "interrupts", -1);
if (vec == -1) {
/* setup compatibility mode interrupts */
if (dev == 0) {
@@ -530,7 +572,7 @@ pciide_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
old_rnumber = mp->map_obj.rnumber;
new_rnumber = pciide_pre26_rnumber_map(dip, old_rnumber);
PDBG(("pciide rnumber old %d new %d\n",
- old_rnumber, new_rnumber));
+ old_rnumber, new_rnumber));
mp->map_obj.rnumber = new_rnumber;
}
@@ -545,7 +587,7 @@ pciide_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
*/
pdip = ddi_get_parent(dip);
rc = ((*(DEVI(pdip)->devi_ops->devo_bus_ops->bus_map))
- (pdip, dip, mp, offset, len, vaddrp));
+ (pdip, dip, mp, offset, len, vaddrp));
PDBG(("pciide_bus_map %s\n", rc == DDI_SUCCESS ? "okay" : "!ok"));
@@ -751,7 +793,7 @@ pciide_compat_setup(dev_info_t *mydip, dev_info_t *cdip, int dev)
if ((dev == 0 && !(class_code & PCI_IDE_IF_NATIVE_PRI)) ||
(dev == 1 && !(class_code & PCI_IDE_IF_NATIVE_SEC))) {
rc = ddi_prop_update_int(DDI_DEV_T_NONE, cdip,
- "compatibility-mode", 1);
+ "compatibility-mode", 1);
if (rc != DDI_PROP_SUCCESS)
cmn_err(CE_WARN,
"pciide prop error %d compat-mode", rc);
@@ -765,9 +807,9 @@ pciide_compat_setup(dev_info_t *mydip, dev_info_t *cdip, int dev)
*/
class_code &= 0x00ffff00;
class_code |= PCI_IDE_IF_BM_CAP_MASK |
- PCI_IDE_IF_NATIVE_PRI | PCI_IDE_IF_NATIVE_SEC;
+ PCI_IDE_IF_NATIVE_PRI | PCI_IDE_IF_NATIVE_SEC;
rc = ddi_prop_update_int(DDI_DEV_T_NONE, mydip,
- "class-code", class_code);
+ "class-code", class_code);
if (rc != DDI_PROP_SUCCESS)
cmn_err(CE_WARN,
"pciide prop error %d class-code", rc);
@@ -783,7 +825,7 @@ pciide_pre26_rnumber_map(dev_info_t *mydip, int rnumber)
int class_code;
class_code = ddi_prop_get_int(DDI_DEV_T_ANY, mydip, DDI_PROP_DONTPASS,
- "class-code", 0);
+ "class-code", 0);
pri_native = (class_code & PCI_IDE_IF_NATIVE_PRI) ? TRUE : FALSE;
sec_native = (class_code & PCI_IDE_IF_NATIVE_SEC) ? TRUE : FALSE;
diff --git a/usr/src/uts/common/io/pm.c b/usr/src/uts/common/io/pm.c
index d4cb7f0da0..42847eecf7 100644
--- a/usr/src/uts/common/io/pm.c
+++ b/usr/src/uts/common/io/pm.c
@@ -56,10 +56,10 @@
#include <sys/policy.h>
/*
- * Minor number is instance<<8 + clone minor from range 1-255; (0 reserved
- * for "original"
+ * Minor number is instance<<8 + clone minor from range 1-254; (0 reserved
+ * for "original")
*/
-#define PM_MINOR_TO_CLONE(minor) ((minor) & (PM_MAX_CLONE - 1))
+#define PM_MINOR_TO_CLONE(minor) ((minor) & (PM_MAX_CLONE -1))
#define PM_NUMCMPTS(dip) (DEVI(dip)->devi_pm_num_components)
#define PM_IS_CFB(dip) (DEVI(dip)->devi_pm_flags & PMC_CONSOLE_FB)
@@ -67,6 +67,8 @@
#define PM_RELE(dip) ddi_release_devi(dip)
#define PM_IDLEDOWN_TIME 10
+#define MAXSMBIOSSTRLEN 64 /* from SMBIOS spec */
+#define MAXCOPYBUF (MAXSMBIOSSTRLEN + 1)
extern kmutex_t pm_scan_lock; /* protects autopm_enable, pm_scans_disabled */
extern kmutex_t pm_clone_lock; /* protects pm_clones array */
@@ -77,6 +79,19 @@ extern int pm_system_idle_threshold;
extern int pm_cpu_idle_threshold;
extern kcondvar_t pm_clones_cv[PM_MAX_CLONE];
extern uint_t pm_poll_cnt[PM_MAX_CLONE];
+extern int autoS3_enabled;
+extern void pm_record_thresh(pm_thresh_rec_t *);
+extern void pm_register_watcher(int, dev_info_t *);
+extern int pm_get_current_power(dev_info_t *, int, int *);
+extern int pm_interest_registered(int);
+extern void pm_all_to_default_thresholds(void);
+extern int pm_current_threshold(dev_info_t *, int, int *);
+extern void pm_deregister_watcher(int, dev_info_t *);
+extern void pm_unrecord_threshold(char *);
+extern int pm_S3_enabled;
+extern int pm_ppm_searchlist(pm_searchargs_t *);
+extern psce_t *pm_psc_clone_to_direct(int);
+extern psce_t *pm_psc_clone_to_interest(int);
/*
* The soft state of the power manager. Since there will only
@@ -181,7 +196,7 @@ pm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
pmstp->pm_instance = ddi_get_instance(dip);
if (ddi_create_minor_node(dip, "pm", S_IFCHR,
(pmstp->pm_instance << 8) + 0,
- DDI_PSEUDO, 0) != DDI_SUCCESS) {
+ DDI_PSEUDO, 0) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
pmstp->pm_dip = dip; /* pm_init and getinfo depend on it */
@@ -271,6 +286,7 @@ pm_close_direct_pm_device(dev_info_t *dip, void *arg)
#define NODEP 5
#define DEP 6
#define PM_PSC 7
+#define PM_SRCH 8
#define CHECKPERMS 0x001
#define SU 0x002
@@ -405,6 +421,8 @@ static struct pm_cmd_info pmci[] = {
{PM_RESET_DEVICE_THRESHOLD, "PM_RESET_DEVICE_THRESHOLD", 1, PM_REQ,
INWHO, DIP, NODEP, SU},
{PM_GET_PM_STATE, "PM_GET_PM_STATE", 1, NOSTRUCT},
+ {PM_GET_AUTOS3_STATE, "PM_GET_AUTOS3_STATE", 1, NOSTRUCT},
+ {PM_GET_S3_SUPPORT_STATE, "PM_GET_S3_SUPPORT_STATE", 1, NOSTRUCT},
{PM_GET_DEVICE_TYPE, "PM_GET_DEVICE_TYPE", 1, PM_REQ, INWHO,
DIP, NODEP},
{PM_SET_COMPONENT_THRESHOLDS, "PM_SET_COMPONENT_THRESHOLDS", 1, PM_REQ,
@@ -431,6 +449,14 @@ static struct pm_cmd_info pmci[] = {
{PM_SET_CPU_THRESHOLD, "PM_SET_CPU_THRESHOLD", 1, NOSTRUCT,
0, 0, 0, SU},
{PM_GET_CPUPM_STATE, "PM_GET_CPUPM_STATE", 1, NOSTRUCT},
+ {PM_START_AUTOS3, "PM_START_AUTOS3", 1, NOSTRUCT, 0, 0, 0, SU},
+ {PM_STOP_AUTOS3, "PM_STOP_AUTOS3", 1, NOSTRUCT, 0, 0, 0, SU},
+ {PM_ENABLE_S3, "PM_ENABLE_S3", 1, NOSTRUCT, 0, 0, 0, SU},
+ {PM_DISABLE_S3, "PM_DISABLE_S3", 1, NOSTRUCT, 0, 0, 0, SU},
+ {PM_ENTER_S3, "PM_ENTER_S3", 1, NOSTRUCT, 0, 0, 0, SU},
+ {PM_SEARCH_LIST, "PM_SEARCH_LIST", 1, PM_SRCH, 0, 0, 0, SU},
+ {PM_GET_CMD_NAME, "PM_GET_CMD_NAME", 1, PM_REQ, INDATAOUT, NODIP,
+ NODEP, 0},
{0, NULL}
};
@@ -729,8 +755,6 @@ static void
pm_discard_entries(int clone)
{
psce_t *pscep;
- psce_t *pm_psc_clone_to_direct(int);
- psce_t *pm_psc_clone_to_interest(int);
int direct = 0;
mutex_enter(&pm_clone_lock);
@@ -901,26 +925,21 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
size_t wholen; /* copyinstr length */
size_t deplen = MAXNAMELEN;
char *dep, i_dep_buf[MAXNAMELEN];
- char *pathbuf;
+ char pathbuf[MAXNAMELEN];
struct pm_component *cp;
#ifdef _MULTI_DATAMODEL
pm_state_change32_t *pscp32;
pm_state_change32_t psc32;
+ pm_searchargs32_t psa32;
size_t copysize32;
#endif
pm_state_change_t *pscp;
pm_state_change_t psc;
+ pm_searchargs_t psa;
+ char listname[MAXCOPYBUF];
+ char manufacturer[MAXCOPYBUF];
+ char product[MAXCOPYBUF];
size_t copysize;
- extern void pm_record_thresh(pm_thresh_rec_t *);
- psce_t *pm_psc_clone_to_direct(int);
- psce_t *pm_psc_clone_to_interest(int);
- extern void pm_register_watcher(int, dev_info_t *);
- extern int pm_get_current_power(dev_info_t *, int, int *);
- extern int pm_interest_registered(int);
- extern void pm_all_to_default_thresholds(void);
- extern int pm_current_threshold(dev_info_t *, int, int *);
- extern void pm_deregister_watcher(int, dev_info_t *);
- extern void pm_unrecord_threshold(char *);
PMD(PMD_IOCTL, ("ioctl: %s: begin\n", cmdstr))
@@ -955,6 +974,7 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
}
switch (pcip->str_type) {
case PM_REQ:
+ {
#ifdef _MULTI_DATAMODEL
if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
pm_req32_t req32;
@@ -979,9 +999,9 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
break;
}
req.physpath = who;
+ PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n",
+ cmdstr, req.physpath))
}
- PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n", cmdstr,
- req.physpath))
if (pcip->inargs & INDATA) {
req.data = (void *)(uintptr_t)req32.data;
req.datasize = req32.datasize;
@@ -1053,9 +1073,8 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
ASSERT(!(pcip->inargs & INDATAINT));
ASSERT(pcip->deptype == DEP);
if (req32.data != NULL) {
- size_t dummy;
if (copyinstr((void *)(uintptr_t)
- req32.data, dep, deplen, &dummy)) {
+ req32.data, dep, deplen, NULL)) {
PMD(PMD_ERROR, ("ioctl: %s: "
"0x%p dep size %lx, EFAULT"
"\n", cmdstr,
@@ -1096,9 +1115,9 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
break;
}
req.physpath = who;
+ PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n",
+ cmdstr, req.physpath))
}
- PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n", cmdstr,
- req.physpath))
if (!(pcip->inargs & INDATA)) {
req.data = NULL;
req.datasize = 0;
@@ -1154,9 +1173,8 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
ASSERT(!(pcip->inargs & INDATAINT));
ASSERT(pcip->deptype == DEP);
if (req.data != NULL) {
- size_t dummy;
if (copyinstr((caddr_t)req.data,
- dep, deplen, &dummy)) {
+ dep, deplen, NULL)) {
PMD(PMD_ERROR, ("ioctl: %s: "
"0x%p dep size %lu, "
"EFAULT\n", cmdstr,
@@ -1222,6 +1240,7 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
}
case PM_GET_DEVICE_THRESHOLD:
+ {
PM_LOCK_DIP(dip);
if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip)) {
PM_UNLOCK_DIP(dip);
@@ -1234,6 +1253,7 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
PM_UNLOCK_DIP(dip);
ret = 0;
break;
+ }
case PM_DIRECT_PM:
{
@@ -1248,11 +1268,9 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
* Check to see if we are there is a dependency on
* this kept device, if so, return EBUSY.
*/
- pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
(void) ddi_pathname(dip, pathbuf);
pm_dispatch_to_dep_thread(PM_DEP_WK_CHECK_KEPT,
NULL, pathbuf, PM_DEP_WAIT, &has_dep, 0);
- kmem_free(pathbuf, MAXPATHLEN);
if (has_dep) {
PMD(PMD_ERROR | PMD_DPM, ("%s EBUSY\n",
cmdstr))
@@ -1301,11 +1319,9 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
info->pmi_dev_pm_state &= ~PM_DIRECT;
PM_UNLOCK_DIP(dip);
/* Bring ourselves up if there is a keeper. */
- pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
(void) ddi_pathname(dip, pathbuf);
pm_dispatch_to_dep_thread(PM_DEP_WK_BRINGUP_SELF,
NULL, pathbuf, PM_DEP_WAIT, NULL, 0);
- kmem_free(pathbuf, MAXPATHLEN);
pm_discard_entries(clone);
pm_deregister_watcher(clone, dip);
/*
@@ -1426,6 +1442,7 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
}
case PM_GET_CURRENT_POWER:
+ {
if (pm_get_current_power(dip, req.component,
rval_p) != DDI_SUCCESS) {
PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s "
@@ -1440,6 +1457,7 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
else
ret = 0;
break;
+ }
case PM_GET_TIME_IDLE:
{
@@ -1629,11 +1647,14 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
}
case PM_GET_NUM_COMPONENTS:
+ {
ret = 0;
*rval_p = PM_NUMCMPTS(dip);
break;
+ }
case PM_GET_DEVICE_TYPE:
+ {
ret = 0;
if ((info = PM_GET_PM_INFO(dip)) == NULL) {
PMD(PMD_ERROR, ("ioctl: %s: "
@@ -1647,6 +1668,7 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
*rval_p = PM_AUTOPM;
}
break;
+ }
case PM_SET_COMPONENT_THRESHOLDS:
{
@@ -1981,6 +2003,8 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
cmdstr, PM_DEVICE(dip),
(void *)req.data))
ASSERT(!dipheld);
+ kmem_free(timestamp,
+ comps * sizeof (time_t));
return (EFAULT);
}
rvaddr = (caddr_t)req.data;
@@ -1994,7 +2018,24 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
break;
}
+ case PM_GET_CMD_NAME:
+ {
+ PMD(PMD_IOCTL, ("%s: %s\n", cmdstr,
+ pm_decode_cmd(req.value)))
+ if (ret = copyoutstr(pm_decode_cmd(req.value),
+ (char *)req.data, req.datasize, &lencopied)) {
+ PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
+ "copyoutstr %p failed--EFAULT\n", cmdstr,
+ PM_DEVICE(dip), (void *)req.data))
+ break;
+ }
+ *rval_p = lencopied;
+ ret = 0;
+ break;
+ }
+
case PM_GET_COMPONENT_NAME:
+ {
ASSERT(dip);
if (!e_pm_valid_comp(dip, req.component, &cp)) {
PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
@@ -2014,6 +2055,7 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
*rval_p = lencopied;
ret = 0;
break;
+ }
case PM_GET_POWER_NAME:
{
@@ -2118,6 +2160,7 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
case PM_GET_NUM_POWER_LEVELS:
+ {
if (!e_pm_valid_comp(dip, req.component, &cp)) {
PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) "
"component %d > numcmpts - 1 %d--EINVAL\n",
@@ -2129,8 +2172,10 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
*rval_p = cp->pmc_comp.pmc_numlevels;
ret = 0;
break;
+ }
case PM_GET_DEVICE_THRESHOLD_BASIS:
+ {
ret = 0;
PM_LOCK_DIP(dip);
if ((info = PM_GET_PM_INFO(dip)) == NULL) {
@@ -2172,9 +2217,23 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
PM_UNLOCK_DIP(dip);
break;
}
+ default:
+ /*
+ * Internal error, invalid ioctl description
+ * force debug entry even if pm_debug not set
+ */
+#ifdef DEBUG
+ pm_log("invalid diptype %d for cmd %d (%s)\n",
+ pcip->diptype, cmd, pcip->name);
+#endif
+ ASSERT(0);
+ return (EIO);
+ }
break;
+ }
case PM_PSC:
+ {
/*
* Commands that require pm_state_change_t as arg
*/
@@ -2461,7 +2520,7 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
if (ddi_copyout(&psc32.component,
&pscp32->component, copysize32, mode)
- != 0) {
+ != 0) {
PMD(PMD_ERROR, ("ioctl: %s: copyout "
"failed--EFAULT\n", cmdstr))
ret = EFAULT;
@@ -2482,14 +2541,128 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
break;
}
default:
+ /*
+ * Internal error, invalid ioctl description
+ * force debug entry even if pm_debug not set
+ */
+#ifdef DEBUG
+ pm_log("invalid diptype %d for cmd %d (%s)\n",
+ pcip->diptype, cmd, pcip->name);
+#endif
ASSERT(0);
+ return (EIO);
}
break;
+ }
+
+ case PM_SRCH: /* command that takes a pm_searchargs_t arg */
+ {
+ /*
+ * If no ppm, then there is nothing to search.
+ */
+ if (DEVI(ddi_root_node())->devi_pm_ppm == NULL) {
+ ret = ENODEV;
+ break;
+ }
+
+#ifdef _MULTI_DATAMODEL
+ if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) {
+ if (ddi_copyin((caddr_t)arg, &psa32,
+ sizeof (psa32), mode) != 0) {
+ PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
+ "EFAULT\n\n", cmdstr))
+ return (EFAULT);
+ }
+ if (copyinstr((void *)(uintptr_t)psa32.pms_listname,
+ listname, MAXCOPYBUF, NULL)) {
+ PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
+ "%d, " "EFAULT\n", cmdstr,
+ (void *)(uintptr_t)psa32.pms_listname,
+ MAXCOPYBUF))
+ ret = EFAULT;
+ break;
+ }
+ if (copyinstr((void *)(uintptr_t)psa32.pms_manufacturer,
+ manufacturer, MAXCOPYBUF, NULL)) {
+ PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
+ "%d, " "EFAULT\n", cmdstr,
+ (void *)(uintptr_t)psa32.pms_manufacturer,
+ MAXCOPYBUF))
+ ret = EFAULT;
+ break;
+ }
+ if (copyinstr((void *)(uintptr_t)psa32.pms_product,
+ product, MAXCOPYBUF, NULL)) {
+ PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
+ "%d, " "EFAULT\n", cmdstr,
+ (void *)(uintptr_t)psa32.pms_product,
+ MAXCOPYBUF))
+ ret = EFAULT;
+ break;
+ }
+ } else
+#endif /* _MULTI_DATAMODEL */
+ {
+ if (ddi_copyin((caddr_t)arg, &psa,
+ sizeof (psa), mode) != 0) {
+ PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin "
+ "EFAULT\n\n", cmdstr))
+ return (EFAULT);
+ }
+ if (copyinstr(psa.pms_listname,
+ listname, MAXCOPYBUF, NULL)) {
+ PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
+ "%d, " "EFAULT\n", cmdstr,
+ (void *)psa.pms_listname, MAXCOPYBUF))
+ ret = EFAULT;
+ break;
+ }
+ if (copyinstr(psa.pms_manufacturer,
+ manufacturer, MAXCOPYBUF, NULL)) {
+ PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
+ "%d, " "EFAULT\n", cmdstr,
+ (void *)psa.pms_manufacturer, MAXCOPYBUF))
+ ret = EFAULT;
+ break;
+ }
+ if (copyinstr(psa.pms_product,
+ product, MAXCOPYBUF, NULL)) {
+ PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF "
+ "%d, " "EFAULT\n", cmdstr,
+ (void *)psa.pms_product, MAXCOPYBUF))
+ ret = EFAULT;
+ break;
+ }
+ }
+ psa.pms_listname = listname;
+ psa.pms_manufacturer = manufacturer;
+ psa.pms_product = product;
+ switch (cmd) {
+ case PM_SEARCH_LIST:
+ ret = pm_ppm_searchlist(&psa);
+ break;
+
+ default:
+ /*
+ * Internal error, invalid ioctl description
+ * force debug entry even if pm_debug not set
+ */
+#ifdef DEBUG
+ pm_log("invalid diptype %d for cmd %d (%s)\n",
+ pcip->diptype, cmd, pcip->name);
+#endif
+ ASSERT(0);
+ return (EIO);
+ }
+ break;
+ }
case NOSTRUCT:
+ {
switch (cmd) {
case PM_START_PM:
case PM_START_CPUPM:
+ {
mutex_enter(&pm_scan_lock);
if ((cmd == PM_START_PM && autopm_enabled) ||
(cmd == PM_START_CPUPM && PM_CPUPM_ENABLED)) {
@@ -2500,13 +2673,14 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
break;
}
if (cmd == PM_START_PM)
- autopm_enabled = 1;
+ autopm_enabled = 1;
else
- cpupm = PM_CPUPM_ENABLE;
+ cpupm = PM_CPUPM_ENABLE;
mutex_exit(&pm_scan_lock);
ddi_walk_devs(ddi_root_node(), pm_start_pm_walk, &cmd);
ret = 0;
break;
+ }
case PM_RESET_PM:
case PM_STOP_PM:
@@ -2523,13 +2697,16 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
ret = EINVAL;
break;
}
- if (cmd == PM_STOP_PM)
- autopm_enabled = 0;
- else if (cmd == PM_STOP_CPUPM)
- cpupm = PM_CPUPM_DISABLE;
- else {
- autopm_enabled = 0;
- cpupm = PM_CPUPM_NOTSET;
+ if (cmd == PM_STOP_PM) {
+ autopm_enabled = 0;
+ pm_S3_enabled = 0;
+ autoS3_enabled = 0;
+ } else if (cmd == PM_STOP_CPUPM) {
+ cpupm = PM_CPUPM_DISABLE;
+ } else {
+ autopm_enabled = 0;
+ autoS3_enabled = 0;
+ cpupm = PM_CPUPM_NOTSET;
}
mutex_exit(&pm_scan_lock);
@@ -2553,22 +2730,29 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
}
case PM_GET_SYSTEM_THRESHOLD:
+ {
*rval_p = pm_system_idle_threshold;
ret = 0;
break;
+ }
case PM_GET_DEFAULT_SYSTEM_THRESHOLD:
+ {
*rval_p = pm_default_idle_threshold;
ret = 0;
break;
+ }
case PM_GET_CPU_THRESHOLD:
+ {
*rval_p = pm_cpu_idle_threshold;
ret = 0;
break;
+ }
case PM_SET_SYSTEM_THRESHOLD:
case PM_SET_CPU_THRESHOLD:
+ {
if ((int)arg < 0) {
PMD(PMD_ERROR, ("ioctl: %s: arg 0x%x < 0"
"--EINVAL\n", cmdstr, (int)arg))
@@ -2583,20 +2767,24 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
pm_cpu_idle_threshold = (int)arg;
}
ddi_walk_devs(ddi_root_node(), pm_set_idle_thresh_walk,
- (void *) &cmd);
+ (void *) &cmd);
ret = 0;
break;
+ }
case PM_IDLE_DOWN:
+ {
if (pm_timeout_idledown() != 0) {
ddi_walk_devs(ddi_root_node(),
pm_start_idledown, (void *)PMID_IOC);
}
ret = 0;
break;
+ }
case PM_GET_PM_STATE:
+ {
if (autopm_enabled) {
*rval_p = PM_SYSTEM_PM_ENABLED;
} else {
@@ -2604,8 +2792,10 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
}
ret = 0;
break;
+ }
case PM_GET_CPUPM_STATE:
+ {
if (PM_CPUPM_ENABLED)
*rval_p = PM_CPU_PM_ENABLED;
else if (PM_CPUPM_DISABLED)
@@ -2615,7 +2805,96 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
ret = 0;
break;
}
+
+ case PM_GET_AUTOS3_STATE:
+ {
+ if (autoS3_enabled) {
+ *rval_p = PM_AUTOS3_ENABLED;
+ } else {
+ *rval_p = PM_AUTOS3_DISABLED;
+ }
+ ret = 0;
+ break;
+ }
+
+ case PM_GET_S3_SUPPORT_STATE:
+ {
+ if (pm_S3_enabled) {
+ *rval_p = PM_S3_SUPPORT_ENABLED;
+ } else {
+ *rval_p = PM_S3_SUPPORT_DISABLED;
+ }
+ ret = 0;
+ break;
+ }
+
+ /*
+ * pmconfig tells us if the platform supports S3
+ */
+ case PM_ENABLE_S3:
+ {
+ mutex_enter(&pm_scan_lock);
+ if (pm_S3_enabled) {
+ mutex_exit(&pm_scan_lock);
+ PMD(PMD_ERROR, ("ioctl: %s: EBUSY\n",
+ cmdstr))
+ ret = EBUSY;
+ break;
+ }
+ pm_S3_enabled = 1;
+ mutex_exit(&pm_scan_lock);
+ ret = 0;
+ break;
+ }
+
+ case PM_DISABLE_S3:
+ {
+ mutex_enter(&pm_scan_lock);
+ pm_S3_enabled = 0;
+ mutex_exit(&pm_scan_lock);
+ ret = 0;
+ break;
+ }
+
+ case PM_START_AUTOS3:
+ {
+ mutex_enter(&pm_scan_lock);
+ if (autoS3_enabled) {
+ mutex_exit(&pm_scan_lock);
+ PMD(PMD_ERROR, ("ioctl: %s: EBUSY\n",
+ cmdstr))
+ ret = EBUSY;
+ break;
+ }
+ autoS3_enabled = 1;
+ mutex_exit(&pm_scan_lock);
+ ret = 0;
+ break;
+ }
+
+ case PM_STOP_AUTOS3:
+ {
+ mutex_enter(&pm_scan_lock);
+ autoS3_enabled = 0;
+ mutex_exit(&pm_scan_lock);
+ ret = 0;
+ break;
+ }
+
+ default:
+ /*
+ * Internal error, invalid ioctl description
+ * force debug entry even if pm_debug not set
+ */
+#ifdef DEBUG
+ pm_log("invalid diptype %d for cmd %d (%s)\n",
+ pcip->diptype, cmd, pcip->name);
+#endif
+ ASSERT(0);
+ return (EIO);
+ }
break;
+ }
default:
/*
diff --git a/usr/src/uts/common/io/ppm/ppm.c b/usr/src/uts/common/io/ppm/ppm.c
index b35a2e369d..e52ff63b78 100644
--- a/usr/src/uts/common/io/ppm/ppm.c
+++ b/usr/src/uts/common/io/ppm/ppm.c
@@ -206,8 +206,10 @@ int
_init(void)
{
if (ddi_soft_state_init(
- &ppm_statep, sizeof (ppm_unit_t), 1) != DDI_SUCCESS)
+ &ppm_statep, sizeof (ppm_unit_t), 1) != DDI_SUCCESS) {
+ PPMD(D_INIT, ("ppm: soft state init\n"))
return (DDI_FAILURE);
+ }
if (mod_install(&modlinkage) != DDI_SUCCESS) {
ddi_soft_state_fini(&ppm_statep);
@@ -220,7 +222,12 @@ _init(void)
int
_fini(void)
{
- return (mod_remove(&modlinkage));
+ int error;
+
+ if ((error = mod_remove(&modlinkage)) == DDI_SUCCESS)
+ ddi_soft_state_fini(&ppm_statep);
+
+ return (error);
}
@@ -679,7 +686,7 @@ err_bydom:
STRUCT_INIT(norm, mode);
ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(norm),
- STRUCT_SIZE(norm), mode);
+ STRUCT_SIZE(norm), mode);
if (ret != 0)
return (EFAULT);
@@ -755,6 +762,10 @@ ppm_ctlops(dev_info_t *dip, dev_info_t *rdip,
ppm_owned_t *owned;
int mode;
int ret = DDI_SUCCESS;
+ static int ppm_manage_sx(s3a_t *, int);
+ static int ppm_search_list(pm_searchargs_t *);
+ int *res = (int *)result;
+ s3a_t s3args;
#ifdef DEBUG
char *str = "ppm_ctlops";
@@ -765,8 +776,9 @@ ppm_ctlops(dev_info_t *dip, dev_info_t *rdip,
str, ddi_binding_name(rdip), ctlstr))
#endif
- if (ctlop != DDI_CTLOPS_POWER)
+ if (ctlop != DDI_CTLOPS_POWER) {
return (DDI_FAILURE);
+ }
unitp = (ppm_unit_t *)ddi_get_soft_state(ppm_statep, ppm_inst);
@@ -779,8 +791,6 @@ ppm_ctlops(dev_info_t *dip, dev_info_t *rdip,
ppm_manage_led(PPM_LED_BLINKING);
else
ppm_manage_led(PPM_LED_SOLIDON);
- PPMD(D_LOWEST, ("%s: %sall devices are at lowest power \n",
- str, mode ? "" : "not "))
return (DDI_SUCCESS);
/* undo the claiming of 'rdip' at attach time */
@@ -984,9 +994,37 @@ ppm_ctlops(dev_info_t *dip, dev_info_t *rdip,
return (DDI_FAILURE);
}
+ case PMR_PPM_ENTER_SX:
+ case PMR_PPM_EXIT_SX:
+ s3args.s3a_state = reqp->req.ppm_power_enter_sx_req.sx_state;
+ s3args.s3a_test_point =
+ reqp->req.ppm_power_enter_sx_req.test_point;
+ s3args.s3a_wakephys = reqp->req.ppm_power_enter_sx_req.wakephys;
+ s3args.s3a_psr = reqp->req.ppm_power_enter_sx_req.psr;
+ ret = ppm_manage_sx(&s3args,
+ reqp->request_type == PMR_PPM_ENTER_SX);
+ if (ret) {
+ PPMD(D_CPR, ("ppm_manage_sx returns %d\n", ret))
+ return (DDI_FAILURE);
+ } else {
+ return (DDI_SUCCESS);
+ }
+
+ case PMR_PPM_SEARCH_LIST:
+ ret = ppm_search_list(reqp->req.ppm_search_list_req.searchlist);
+ reqp->req.ppm_search_list_req.result = ret;
+ *res = ret;
+ if (ret) {
+ PPMD(D_CPR, ("ppm_search_list returns %d\n", ret))
+ return (DDI_FAILURE);
+ } else {
+ PPMD(D_CPR, ("ppm_search_list returns %d\n", ret))
+ return (DDI_SUCCESS);
+ }
+
default:
cmn_err(CE_WARN, "ppm_ctlops: unrecognized ctlops req(%d)",
- reqp->request_type);
+ reqp->request_type);
return (DDI_FAILURE);
}
}
@@ -1246,7 +1284,7 @@ ppm_bringup_domains()
}
mutex_exit(&domp->lock);
}
- PPMD(D_CPR, ("%s[%d]: exit, ret=%d\n", str, ppmbringup, ret))
+ PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmbringup))
return (ret);
}
@@ -1275,6 +1313,15 @@ ppm_sync_bookkeeping()
mutex_exit(&domp->lock);
continue;
}
+
+ /*
+ * skip NULL .devlist slot, for some may host pci device
+ * that can not tolerate clock off or not even participate
+ * in PM.
+ */
+ if (domp->devlist == NULL)
+ continue;
+
switch (domp->model) {
case PPMD_FET:
ret = ppm_fetset(domp, PPMD_OFF);
@@ -1291,7 +1338,7 @@ ppm_sync_bookkeeping()
}
mutex_exit(&domp->lock);
}
- PPMD(D_CPR, ("%s[%d]: exit, ret=%d\n", str, ppmsyncbp, ret))
+ PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmsyncbp))
return (ret);
}
@@ -1655,14 +1702,14 @@ ppm_fetset(ppm_domain_t *domp, uint8_t value)
* we might wait for longer than required
*/
PPMD(D_FET, ("%s : waiting %lu micro seconds "
- "before on\n", domp->name,
- delay - temp))
+ "before on\n", domp->name,
+ delay - temp));
drv_usecwait(delay - temp);
}
}
}
switch (dc->method) {
-#if !defined(__x86)
+#ifdef sun4u
case PPMDC_I2CKIO: {
i2c_gpio_t i2c_req;
i2c_req.reg_mask = dc->m_un.i2c.mask;
@@ -1739,7 +1786,7 @@ ppm_fetget(ppm_domain_t *domp, uint8_t *lvl)
}
switch (dc->method) {
-#if !defined(__x86)
+#ifdef sun4u
case PPMDC_I2CKIO: {
i2c_gpio_t i2c_req;
i2c_req.reg_mask = dc->m_un.i2c.mask;
@@ -1773,7 +1820,7 @@ ppm_fetget(ppm_domain_t *domp, uint8_t *lvl)
}
off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.kio.val :
- dc->next->m_un.kio.val;
+ dc->next->m_un.kio.val;
*lvl = (kio_val == off_val) ? PPMD_OFF : PPMD_ON;
PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name,
@@ -2187,7 +2234,7 @@ ppm_gpioset(ppm_domain_t *domp, int key)
}
switch (dc->method) {
-#if !defined(__x86)
+#ifdef sun4u
case PPMDC_I2CKIO: {
i2c_gpio_t i2c_req;
ppm_dev_t *pdev;
@@ -2223,6 +2270,7 @@ ppm_gpioset(ppm_domain_t *domp, int key)
break;
}
#endif
+
case PPMDC_KIO:
ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
(intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred,
@@ -2666,3 +2714,100 @@ ppm_power_down_domain(dev_info_t *dip)
mutex_exit(&domp->lock);
return (ret);
}
+
+static int
+ppm_manage_sx(s3a_t *s3ap, int enter)
+{
+ ppm_domain_t *domp = ppm_lookup_domain("domain_estar");
+ ppm_dc_t *dc;
+ int ret = 0;
+
+ if (domp == NULL) {
+ PPMD(D_CPR, ("ppm_manage_sx: can't find estar domain\n"))
+ return (ENODEV);
+ }
+ PPMD(D_CPR, ("ppm_manage_sx %x, enter %d\n", s3ap->s3a_state,
+ enter))
+ switch (s3ap->s3a_state) {
+ case S3:
+ if (enter) {
+ dc = ppm_lookup_dc(domp, PPMDC_ENTER_S3);
+ } else {
+ dc = ppm_lookup_dc(domp, PPMDC_EXIT_S3);
+ }
+ ASSERT(dc && dc->method == PPMDC_KIO);
+ PPMD(D_CPR,
+ ("ppm_manage_sx: calling acpi driver (handle %p)"
+ " with %x\n", (void *)dc->lh, dc->m_un.kio.iowr))
+ ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
+ (intptr_t)s3ap, FWRITE | FKIOCTL, kcred, NULL);
+ break;
+
+ case S4:
+ /* S4 is not supported yet */
+ return (EINVAL);
+ default:
+ ASSERT(0);
+ }
+ return (ret);
+}
+
+/*
+ * Search enable/disable lists, which are encoded in ppm.conf as an array
+ * of char strings.
+ */
+static int
+ppm_search_list(pm_searchargs_t *sl)
+{
+ int i;
+ int flags = DDI_PROP_DONTPASS;
+ ppm_unit_t *unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
+ char **pp;
+ char *starp;
+ uint_t nelements;
+ char *manuf = sl->pms_manufacturer;
+ char *prod = sl->pms_product;
+
+ if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, unitp->dip, flags,
+ sl->pms_listname, &pp, &nelements) != DDI_PROP_SUCCESS) {
+ PPMD(D_CPR, ("ppm_search_list prop lookup %s failed--EINVAL\n",
+ sl->pms_listname))
+ return (EINVAL);
+ }
+ ASSERT((nelements & 1) == 0); /* must be even */
+
+ PPMD(D_CPR, ("ppm_search_list looking for %s, %s\n", manuf, prod))
+
+ for (i = 0; i < nelements; i += 2) {
+ PPMD(D_CPR, ("checking %s, %s", pp[i], pp[i+1]))
+ /* we support only a trailing '*' pattern match */
+ if ((starp = strchr(pp[i], '*')) != NULL && *(starp + 1) == 0) {
+ /* LINTED - ptrdiff overflow */
+ if (strncmp(manuf, pp[i], (starp - pp[i])) != 0) {
+ PPMD(D_CPR, (" no match %s with %s\n",
+ manuf, pp[i + 1]))
+ continue;
+ }
+ }
+ if ((starp = strchr(pp[i + 1], '*')) != NULL &&
+ *(starp + 1) == 0) {
+ if (strncmp(prod,
+ /* LINTED - ptrdiff overflow */
+ pp[i + 1], (starp - pp[i + 1])) != 0) {
+ PPMD(D_CPR, (" no match %s with %s\n",
+ prod, pp[i + 1]))
+ continue;
+ }
+ }
+ if (strcmp(manuf, pp[i]) == 0 &&
+ (strcmp(prod, pp[i + 1]) == 0)) {
+ PPMD(D_CPR, (" match\n"))
+ ddi_prop_free(pp);
+ return (0);
+ }
+ PPMD(D_CPR, (" no match %s with %s or %s with %s\n",
+ manuf, pp[i], prod, pp[i + 1]))
+ }
+ ddi_prop_free(pp);
+ return (ENODEV);
+}
diff --git a/usr/src/uts/common/io/ppm/ppm_subr.c b/usr/src/uts/common/io/ppm/ppm_subr.c
index 4bcd24c877..617ac3a380 100644
--- a/usr/src/uts/common/io/ppm/ppm_subr.c
+++ b/usr/src/uts/common/io/ppm/ppm_subr.c
@@ -207,6 +207,7 @@ ppm_lookup_hndl(int model, ppm_dc_t *key_dc)
#define PPM_CTRL_PROP_SUFFIX "-control"
struct ppm_domit ppm_domit_data[] = {
+ "SX", PPMD_SX, 0, PPMD_ON,
"CPU", PPMD_CPU, PPMD_LOCK_ALL, PPMD_ON,
"FET", PPMD_FET, PPMD_LOCK_ONE, PPMD_ON,
"PCI", PPMD_PCI, PPMD_LOCK_ONE, PPMD_ON,
@@ -401,11 +402,19 @@ ppm_lookup_dev(dev_info_t *dip)
PPM_GET_PATHNAME(dip, path);
for (domp = ppm_domain_p; domp; domp = domp->next) {
- if (PPM_DOMAIN_UP(domp))
+ if (PPM_DOMAIN_UP(domp)) {
for (dbp = domp->conflist; dbp; dbp = dbp->next) {
+ /*
+ * allow claiming root without knowing
+ * its full name
+ */
+ if (dip == ddi_root_node() &&
+ strcmp(dbp->name, "/") == 0)
+ return (domp);
if (ppm_match_devs(path, dbp) == 0)
return (domp);
}
+ }
}
return (NULL);
@@ -514,7 +523,8 @@ ppm_match_devs(char *dev_path, ppm_db_t *dbp)
/* "<exact match>*" */
if (dbp->name[dbp->wcpos[0] + 1] == 0) {
cp = path + dbp->wcpos[0];
- while (*cp && (*cp++ != '/'));
+ while (*cp && (*cp++ != '/'))
+ ;
return ((*cp == 0) ? 0 : -1);
}
@@ -761,6 +771,22 @@ ppm_init_cb(dev_info_t *dip)
for (domp = ppm_domain_p; domp != NULL; domp = domp->next) {
for (dc = domp->dc; dc; dc = dc->next) {
+ /*
+ * Warning: This code is rather confusing.
+ *
+ * It intends to ensure that ppm_init_lyr() is only
+ * called ONCE for a device that may be associated
+ * with more than one domain control.
+ * So, what it does is first to check to see if
+ * there is a handle, and then if not it goes on
+ * to call the init_lyr() routine.
+ *
+ * The non-obvious thing is that the ppm_init_lyr()
+ * routine, in addition to opening the device
+ * associated with the dc (domain control) in
+ * question, has the side-effect of creating the
+ * handle for that dc as well.
+ */
if (ppm_lookup_hndl(domp->model, dc) != NULL)
continue;
@@ -979,6 +1005,8 @@ struct ppm_confdefs {
char *sym;
int val;
} ppm_confdefs_table[] = {
+ "ENTER_S3", PPMDC_ENTER_S3,
+ "EXIT_S3", PPMDC_EXIT_S3,
"CPU_NEXT", PPMDC_CPU_NEXT,
"PRE_CHNG", PPMDC_PRE_CHNG,
"CPU_GO", PPMDC_CPU_GO,
@@ -991,7 +1019,9 @@ struct ppm_confdefs {
"LED_OFF", PPMDC_LED_OFF,
"KIO", PPMDC_KIO,
"VCORE", PPMDC_VCORE,
+#ifdef sun4u
"I2CKIO", PPMDC_I2CKIO,
+#endif
"CPUSPEEDKIO", PPMDC_CPUSPEEDKIO,
"PRE_PWR_OFF", PPMDC_PRE_PWR_OFF,
"PRE_PWR_ON", PPMDC_PRE_PWR_ON,
@@ -1103,14 +1133,20 @@ ppm_parse_dc(char **dc_namep, ppm_dc_t *dc)
(void) ppm_stoi(dclist[i], &dc->m_un.cpu.speeds);
continue;
}
+#ifdef sun4u
if (strstr(dclist[i], "mask=")) {
(void) ppm_stoi(dclist[i], &dc->m_un.i2c.mask);
continue;
}
+#endif
/* This must be before the if statement for delay */
if (strstr(dclist[i], "post_delay=")) {
+#ifdef sun4u
ASSERT(dc->method == PPMDC_KIO ||
dc->method == PPMDC_I2CKIO);
+#else
+ ASSERT(dc->method == PPMDC_KIO);
+#endif
/*
* all delays are uint_t type instead of clock_t.
* If the delay is too long, it might get truncated.
@@ -1119,13 +1155,15 @@ ppm_parse_dc(char **dc_namep, ppm_dc_t *dc)
switch (dc->method) {
case PPMDC_KIO:
(void) ppm_stoi(dclist[i],
- &dc->m_un.kio.post_delay);
+ &dc->m_un.kio.post_delay);
break;
+#ifdef sun4u
case PPMDC_I2CKIO:
(void) ppm_stoi(dclist[i],
- &dc->m_un.i2c.post_delay);
+ &dc->m_un.i2c.post_delay);
break;
+#endif
default:
break;
@@ -1133,9 +1171,14 @@ ppm_parse_dc(char **dc_namep, ppm_dc_t *dc)
continue;
}
if (strstr(dclist[i], "delay=")) {
+#ifdef sun4u
+ ASSERT(dc->method == PPMDC_VCORE ||
+ dc->method == PPMDC_KIO ||
+ dc->method == PPMDC_I2CKIO);
+#else
ASSERT(dc->method == PPMDC_VCORE ||
- dc->method == PPMDC_KIO ||
- dc->method == PPMDC_I2CKIO);
+ dc->method == PPMDC_KIO);
+#endif
/*
* all delays are uint_t type instead of clock_t.
@@ -1148,9 +1191,11 @@ ppm_parse_dc(char **dc_namep, ppm_dc_t *dc)
(void) ppm_stoi(dclist[i], &dc->m_un.kio.delay);
break;
+#ifdef sun4u
case PPMDC_I2CKIO:
(void) ppm_stoi(dclist[i], &dc->m_un.i2c.delay);
break;
+#endif
case PPMDC_VCORE:
(void) ppm_stoi(dclist[i], &dc->m_un.cpu.delay);
@@ -1227,6 +1272,8 @@ ppm_lookup_dc(ppm_domain_t *domp, int cmd)
case PPMDC_PWR_ON:
case PPMDC_RESET_OFF:
case PPMDC_RESET_ON:
+ case PPMDC_ENTER_S3:
+ case PPMDC_EXIT_S3:
break;
default:
PPMD(D_PPMDC, ("%s: cmd(%d) unrecognized\n", str, cmd))
@@ -1234,9 +1281,11 @@ ppm_lookup_dc(ppm_domain_t *domp, int cmd)
}
for (dc = domp->dc; dc; dc = dc->next) {
- if (dc->cmd == cmd)
+ if (dc->cmd == cmd) {
return (dc);
+ }
}
+
return (NULL);
}
@@ -1315,6 +1364,7 @@ ppm_get_ctlstr(int ctlop, uint_t mask)
FLINTSTR(D_LOCKS, PMR_PPM_UNLOCK_POWER),
FLINTSTR(D_LOCKS, PMR_PPM_TRY_LOCK_POWER),
FLINTSTR(D_LOCKS, PMR_PPM_POWER_LOCK_OWNER),
+ FLINTSTR(D_CTLOPS1 | D_CTLOPS2, PMR_PPM_ENTER_SX),
FLINTSTR(D_CTLOPS1 | D_CTLOPS2, PMR_UNKNOWN),
};
@@ -1334,13 +1384,15 @@ ppm_print_dc(ppm_dc_t *dc)
PPMD(D_PPMDC, ("\nAdds ppm_dc: path(%s),\n cmd(%x), "
"method(%x), ", d->path, d->cmd, d->method))
- if (d->method == PPMDC_I2CKIO) {
+ if (d->method == PPMDC_KIO) {
+ PPMD(D_PPMDC, ("kio.iowr(%x), kio.val(0x%X)",
+ d->m_un.kio.iowr, d->m_un.kio.val))
+#ifdef sun4u
+ } else if (d->method == PPMDC_I2CKIO) {
PPMD(D_PPMDC, ("i2c.iowr(%x), i2c.val(0x%X), "
"i2c.mask(0x%X)", d->m_un.i2c.iowr,
d->m_un.i2c.val, d->m_un.i2c.mask))
- } else if (d->method == PPMDC_KIO) {
- PPMD(D_PPMDC, ("kio.iowr(%x), kio.val(0x%X)",
- d->m_un.kio.iowr, d->m_un.kio.val))
+#endif
} else if (d->method == PPMDC_VCORE) {
PPMD(D_PPMDC, ("cpu: .iord(%x), .iowr(%x), .val(0x%X), "
".delay(0x%x)",
diff --git a/usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.c b/usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.c
index fa303ca9b5..f6d5870c5c 100644
--- a/usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.c
+++ b/usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.c
@@ -138,6 +138,7 @@ static void nv_read_signature(nv_port_t *nvp);
static void mcp55_set_intr(nv_port_t *nvp, int flag);
static void mcp04_set_intr(nv_port_t *nvp, int flag);
static void nv_resume(nv_port_t *nvp);
+static void nv_suspend(nv_port_t *nvp);
static int nv_start_sync(nv_port_t *nvp, sata_pkt_t *spkt);
static int nv_abort_active(nv_port_t *nvp, sata_pkt_t *spkt, int abort_reason);
static void nv_copy_registers(nv_port_t *nvp, sata_device_t *sd,
@@ -400,7 +401,7 @@ nv_get8(ddi_acc_handle_t handle, uint8_t *dev_addr)
static int
nv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
- int status, attach_state, intr_types, bar, i;
+ int status, attach_state, intr_types, bar, i, command;
int inst = ddi_get_instance(dip);
ddi_acc_handle_t pci_conf_handle;
nv_ctl_t *nvc;
@@ -439,6 +440,20 @@ nv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
attach_state |= ATTACH_PROGRESS_CONF_HANDLE;
+ /*
+ * If a device is attached after a suspend/resume, sometimes
+ * the command register is zero, as it might not be set by
+ * BIOS or a parent. Set it again here.
+ */
+ command = pci_config_get16(pci_conf_handle, PCI_CONF_COMM);
+
+ if (command == 0) {
+ cmn_err(CE_WARN, "nv_sata%d: restoring PCI command"
+ " register", inst);
+ pci_config_put16(pci_conf_handle, PCI_CONF_COMM,
+ PCI_COMM_IO|PCI_COMM_MAE|PCI_COMM_ME);
+ }
+
subclass = pci_config_get8(pci_conf_handle, PCI_CONF_SUBCLASS);
if (subclass & PCI_MASS_RAID) {
@@ -585,7 +600,6 @@ nv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
NVLOG((NVDBG_INIT, nvc, NULL,
"nv_attach(): DDI_RESUME inst %d", inst));
-
nvc->nvc_state &= ~NV_CTRL_SUSPEND;
for (i = 0; i < NV_MAX_PORTS(nvc); i++) {
@@ -716,6 +730,11 @@ nv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
* the current state.
*/
NVLOG((NVDBG_INIT, nvc, NULL, "nv_detach: DDI_SUSPEND"));
+
+ for (i = 0; i < NV_MAX_PORTS(nvc); i++) {
+ nv_suspend(&(nvc->nvc_port[i]));
+ }
+
nvc->nvc_state |= NV_CTRL_SUSPEND;
return (DDI_SUCCESS);
@@ -1226,7 +1245,7 @@ nv_start_sync(nv_port_t *nvp, sata_pkt_t *spkt)
(*(nvc->nvc_set_intr))(nvp, NV_INTR_ENABLE);
NVLOG((NVDBG_SYNC, nvp->nvp_ctlp, nvp, "nv_sata_satapkt_sync:"
- " done % reason %d", ret));
+ " done % reason %d", ret));
return (ret);
}
@@ -2725,7 +2744,7 @@ mcp55_dma_setup_intr(nv_ctl_t *nvc, nv_port_t *nvp)
MCP_SATA_AE_NCQ_SDEV_DMA_SETUP_TAG_SHIFT};
nv_cmn_err(CE_PANIC, nvc, nvp,
- "this is should not be executed at all until NCQ");
+ "this is should not be executed at all until NCQ");
mutex_enter(&nvp->nvp_mutex);
@@ -4576,6 +4595,38 @@ nv_resume(nv_port_t *nvp)
* nv_reset(nvp);
*/
+ nv_reset(nvp);
+
+ mutex_exit(&nvp->nvp_mutex);
+}
+
+/*
+ * The PM functions for suspend and resume are incomplete and need additional
+ * work. It may or may not work in the current state.
+ */
+static void
+nv_suspend(nv_port_t *nvp)
+{
+ NVLOG((NVDBG_INIT, nvp->nvp_ctlp, nvp, "nv_suspend()"));
+
+ mutex_enter(&nvp->nvp_mutex);
+
+ if (nvp->nvp_state & NV_PORT_INACTIVE) {
+ mutex_exit(&nvp->nvp_mutex);
+
+ return;
+ }
+
+ (*(nvp->nvp_ctlp->nvc_set_intr))(nvp, NV_INTR_DISABLE);
+
+ /*
+ * power may have been removed to the port and the
+ * drive, and/or a drive may have been added or removed.
+ * Force a reset which will cause a probe and re-establish
+ * any state needed on the drive.
+ * nv_reset(nvp);
+ */
+
mutex_exit(&nvp->nvp_mutex);
}
diff --git a/usr/src/uts/common/io/srn.c b/usr/src/uts/common/io/srn.c
new file mode 100755
index 0000000000..cb2888871d
--- /dev/null
+++ b/usr/src/uts/common/io/srn.c
@@ -0,0 +1,563 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * srn Provide apm-like interfaces to Xorg
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/modctl.h>
+#include <sys/conf.h> /* driver flags and functions */
+#include <sys/open.h> /* OTYP_CHR definition */
+#include <sys/stat.h> /* S_IFCHR definition */
+#include <sys/pathname.h> /* name -> dev_info xlation */
+#include <sys/kmem.h> /* memory alloc stuff */
+#include <sys/debug.h>
+#include <sys/pm.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/epm.h>
+#include <sys/vfs.h>
+#include <sys/mode.h>
+#include <sys/mkdev.h>
+#include <sys/promif.h>
+#include <sys/consdev.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/poll.h>
+#include <sys/note.h>
+#include <sys/taskq.h>
+#include <sys/policy.h>
+#include <sys/srn.h>
+
+/*
+ * Minor number is instance<<8 + clone minor from range 1-255;
+ * But only one will be allocated
+ */
+#define SRN_MINOR_TO_CLONE(minor) ((minor) & (SRN_MAX_CLONE - 1))
+#define SU 0x002
+#define SG 0x004
+
+extern kmutex_t srn_clone_lock; /* protects srn_clones array */
+extern kcondvar_t srn_clones_cv[SRN_MAX_CLONE];
+extern uint_t srn_poll_cnt[SRN_MAX_CLONE];
+
+/*
+ * The soft state of the srn driver. Since there will only be
+ * one of these, just reference it through a static struct.
+ */
+static struct srnstate {
+ dev_info_t *srn_dip; /* ptr to our dev_info node */
+ int srn_instance; /* for ddi_get_instance() */
+ uchar_t srn_clones[SRN_MAX_CLONE]; /* unique opens */
+ struct cred *srn_cred[SRN_MAX_CLONE]; /* cred for each open */
+ int srn_type[SRN_MAX_CLONE]; /* type of handshake */
+ int srn_delivered[SRN_MAX_CLONE];
+ srn_event_info_t srn_pending[SRN_MAX_CLONE];
+} srn = { NULL, -1};
+typedef struct srnstate *srn_state_t;
+
+kcondvar_t srn_clones_cv[SRN_MAX_CLONE];
+uint_t srn_poll_cnt[SRN_MAX_CLONE]; /* count of events for poll */
+int srn_apm_count;
+int srn_autosx_count;
+struct pollhead srn_pollhead[SRN_MAX_CLONE];
+
+static int srn_open(dev_t *, int, int, cred_t *);
+static int srn_close(dev_t, int, int, cred_t *);
+static int srn_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+static int srn_chpoll(dev_t, short, int, short *, struct pollhead **);
+
+static struct cb_ops srn_cb_ops = {
+ srn_open, /* open */
+ srn_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ srn_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ srn_chpoll, /* poll */
+ ddi_prop_op, /* prop_op */
+ NULL, /* streamtab */
+ D_NEW | D_MP /* driver compatibility flag */
+};
+
+static int srn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
+ void **result);
+static int srn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+static int srn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+static void srn_notify(int type, int event);
+
+static struct dev_ops srn_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* refcnt */
+ srn_getinfo, /* info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ srn_attach, /* attach */
+ srn_detach, /* detach */
+ nodev, /* reset */
+ &srn_cb_ops, /* driver operations */
+ NULL, /* bus operations */
+ NULL /* power */
+};
+
+static struct modldrv modldrv = {
+ &mod_driverops,
+ "srn driver v1.4",
+ &srn_ops
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, &modldrv, 0
+};
+
+/* Local functions */
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_fini(void)
+{
+ return (mod_remove(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+static int
+srn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int i;
+ extern void (*srn_signal)(int, int);
+
+ switch (cmd) {
+
+ case DDI_ATTACH:
+ if (srn.srn_instance != -1) /* Only allow one instance */
+ return (DDI_FAILURE);
+ srn.srn_instance = ddi_get_instance(dip);
+ if (ddi_create_minor_node(dip, "srn", S_IFCHR,
+ (srn.srn_instance << 8) + 0, DDI_PSEUDO, 0)
+ != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+ srn.srn_dip = dip; /* srn_init and getinfo depend on it */
+
+ for (i = 0; i < SRN_MAX_CLONE; i++)
+ cv_init(&srn_clones_cv[i], NULL, CV_DEFAULT, NULL);
+
+ srn.srn_instance = ddi_get_instance(dip);
+ mutex_enter(&srn_clone_lock);
+ srn_signal = srn_notify;
+ mutex_exit(&srn_clone_lock);
+ ddi_report_dev(dip);
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+/* ARGSUSED */
+static int
+srn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ int i;
+ extern int srn_inuse;
+ extern void (*srn_signal)(int, int);
+
+ switch (cmd) {
+ case DDI_DETACH:
+
+ mutex_enter(&srn_clone_lock);
+ while (srn_inuse) {
+ mutex_exit(&srn_clone_lock);
+ delay(1);
+ mutex_enter(&srn_clone_lock);
+ }
+ srn_signal = NULL;
+ mutex_exit(&srn_clone_lock);
+
+ for (i = 0; i < SRN_MAX_CLONE; i++)
+ cv_destroy(&srn_clones_cv[i]);
+
+ ddi_remove_minor_node(dip, NULL);
+ srn.srn_instance = -1;
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+
+#ifdef DEBUG
+char *srn_cmd_string;
+int srn_cmd;
+#endif
+
+/*
+ * Returns true if permission granted by credentials
+ * XXX
+ */
+static int
+srn_perms(int perm, cred_t *cr)
+{
+ if ((perm & SU) && secpolicy_power_mgmt(cr) == 0) /* privileged? */
+ return (1);
+ if ((perm & SG) && (crgetgid(cr) == 0)) /* group 0 is ok */
+ return (1);
+ return (0);
+}
+
+static int
+srn_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
+ struct pollhead **phpp)
+{
+ extern struct pollhead srn_pollhead[]; /* common/os/sunpm.c */
+ int clone;
+
+ clone = SRN_MINOR_TO_CLONE(getminor(dev));
+ if ((events & (POLLIN | POLLRDNORM)) && srn_poll_cnt[clone]) {
+ *reventsp |= (POLLIN | POLLRDNORM);
+ } else {
+ *reventsp = 0;
+ if (!anyyet) {
+ *phpp = &srn_pollhead[clone];
+ }
+ }
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+srn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ dev_t dev;
+ int instance;
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ if (srn.srn_instance == -1)
+ return (DDI_FAILURE);
+ *result = srn.srn_dip;
+ return (DDI_SUCCESS);
+
+ case DDI_INFO_DEVT2INSTANCE:
+ dev = (dev_t)arg;
+ instance = getminor(dev) >> 8;
+ *result = (void *)(uintptr_t)instance;
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+
+/*ARGSUSED1*/
+static int
+srn_open(dev_t *devp, int flag, int otyp, cred_t *cr)
+{
+ int clone;
+
+ if (otyp != OTYP_CHR)
+ return (EINVAL);
+
+ mutex_enter(&srn_clone_lock);
+ for (clone = 1; clone < SRN_MAX_CLONE - 1; clone++)
+ if (!srn.srn_clones[clone])
+ break;
+
+ if (clone == SRN_MAX_CLONE) {
+ mutex_exit(&srn_clone_lock);
+ return (ENXIO);
+ }
+ srn.srn_cred[clone] = cr;
+ ASSERT(srn_apm_count >= 0);
+ srn_apm_count++;
+ srn.srn_type[clone] = SRN_TYPE_APM;
+ crhold(cr);
+
+ *devp = makedevice(getmajor(*devp), (srn.srn_instance << 8) +
+ clone);
+ srn.srn_clones[clone] = 1;
+ srn.srn_cred[clone] = cr;
+ crhold(cr);
+ mutex_exit(&srn_clone_lock);
+ PMD(PMD_SX, ("srn open OK\n"))
+ return (0);
+}
+
+/*ARGSUSED1*/
+static int
+srn_close(dev_t dev, int flag, int otyp, cred_t *cr)
+{
+ int clone;
+
+ if (otyp != OTYP_CHR)
+ return (EINVAL);
+
+ clone = SRN_MINOR_TO_CLONE(getminor(dev));
+ PMD(PMD_SX, ("srn_close: minor %x, clone %x\n", getminor(dev),
+ clone))
+ mutex_enter(&srn_clone_lock);
+ crfree(srn.srn_cred[clone]);
+ srn.srn_cred[clone] = 0;
+ srn_poll_cnt[clone] = 0;
+ if (srn.srn_pending[clone].ae_type || srn.srn_delivered[clone]) {
+ srn.srn_pending[clone].ae_type = 0;
+ srn.srn_delivered[clone] = 0;
+ cv_signal(&srn_clones_cv[clone]);
+ }
+ switch (srn.srn_type[clone]) {
+ case SRN_TYPE_AUTOSX:
+ ASSERT(srn_autosx_count);
+ srn_autosx_count--;
+ break;
+ case SRN_TYPE_APM:
+ ASSERT(srn_apm_count);
+ srn_apm_count--;
+ break;
+ default:
+ ASSERT(0);
+ return (EINVAL);
+ }
+ srn.srn_clones[clone] = 0;
+ mutex_exit(&srn_clone_lock);
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+srn_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
+{
+ int clone = SRN_MINOR_TO_CLONE(getminor(dev));
+
+ PMD(PMD_SX, ("ioctl: %x: begin\n", cmd))
+
+ switch (cmd) {
+ case SRN_IOC_NEXTEVENT:
+ case SRN_IOC_SUSPEND:
+ case SRN_IOC_RESUME:
+ case SRN_IOC_AUTOSX:
+ break;
+ default:
+ return (ENOTTY);
+ }
+
+ if (!srn_perms(SU | SG, srn.srn_cred[clone])) {
+ return (EPERM);
+ }
+ switch (cmd) {
+ case SRN_IOC_AUTOSX:
+ PMD(PMD_SX, ("SRN_IOC_AUTOSX entered\n"))
+ mutex_enter(&srn_clone_lock);
+ if (!srn.srn_clones[clone]) {
+ PMD(PMD_SX, (" ioctl !srn_clones--EINVAL\n"))
+ mutex_exit(&srn_clone_lock);
+ return (EINVAL);
+ }
+ if (srn.srn_pending[clone].ae_type) {
+ PMD(PMD_SX, ("AUTOSX while pending--EBUSY\n"))
+ mutex_exit(&srn_clone_lock);
+ return (EBUSY);
+ }
+ if (srn.srn_type[clone] == SRN_TYPE_AUTOSX) {
+ PMD(PMD_SX, ("AUTOSX already--EBUSY\n"))
+ mutex_exit(&srn_clone_lock);
+ return (EBUSY);
+ }
+ ASSERT(srn.srn_type[clone] == SRN_TYPE_APM);
+ srn.srn_type[clone] = SRN_TYPE_AUTOSX;
+ srn_apm_count--;
+ ASSERT(srn_apm_count >= 0);
+ ASSERT(srn_autosx_count >= 0);
+ srn_autosx_count++;
+ mutex_exit(&srn_clone_lock);
+ PMD(PMD_SX, ("SRN_IOC_AUTOSX returns success\n"))
+ return (0);
+
+ case SRN_IOC_NEXTEVENT:
+ /*
+ * return the next suspend or resume event; there should
+ * be one, cause we only get called if we've signalled a
+ * poll data completion
+ * then wake up the kernel thread sleeping for the delivery
+ */
+ PMD(PMD_SX, ("SRN_IOC_NEXTEVENT entered\n"))
+ mutex_enter(&srn_clone_lock);
+ if (srn_poll_cnt[clone] == 0) {
+ mutex_exit(&srn_clone_lock);
+ PMD(PMD_SX, ("SRN_IOC_NEXTEVENT clone %d "
+ "EWOULDBLOCK\n", clone))
+ return (EWOULDBLOCK);
+ }
+ ASSERT(srn.srn_pending[clone].ae_type);
+ if (ddi_copyout(&srn.srn_pending[clone], (void *)arg,
+ sizeof (srn_event_info_t), mode) != 0) {
+ mutex_exit(&srn_clone_lock);
+ PMD(PMD_SX, ("SRN_IOC_NEXTEVENT clone %d EFAULT\n",
+ clone))
+ return (EFAULT);
+ }
+ if (srn.srn_type[clone] == SRN_TYPE_APM)
+ srn.srn_delivered[clone] =
+ srn.srn_pending[clone].ae_type;
+ PMD(PMD_SX, ("SRN_IOC_NEXTEVENT clone %d delivered %x\n",
+ clone, srn.srn_pending[clone].ae_type))
+ srn_poll_cnt[clone] = 0;
+ mutex_exit(&srn_clone_lock);
+ return (0);
+
+ case SRN_IOC_SUSPEND:
+ /* ack suspend */
+ PMD(PMD_SX, ("SRN_IOC_SUSPEND entered clone %d\n", clone))
+ mutex_enter(&srn_clone_lock);
+ if (srn.srn_delivered[clone] != SRN_SUSPEND_REQ) {
+ mutex_exit(&srn_clone_lock);
+ PMD(PMD_SX, ("SRN_IOC_SUSPEND EINVAL\n"))
+ return (EINVAL);
+ }
+ srn.srn_delivered[clone] = 0;
+ srn.srn_pending[clone].ae_type = 0;
+ /* notify the kernel suspend thread to continue */
+ PMD(PMD_SX, ("SRN_IOC_SUSPEND clone %d ok\n", clone))
+ cv_signal(&srn_clones_cv[clone]);
+ mutex_exit(&srn_clone_lock);
+ return (0);
+
+ case SRN_IOC_RESUME:
+ /* ack resume */
+ PMD(PMD_SX, ("SRN_IOC_RESUME entered clone %d\n", clone))
+ mutex_enter(&srn_clone_lock);
+ if (srn.srn_delivered[clone] != SRN_NORMAL_RESUME) {
+ mutex_exit(&srn_clone_lock);
+ PMD(PMD_SX, ("SRN_IOC_RESUME EINVAL\n"))
+ return (EINVAL);
+ }
+ srn.srn_delivered[clone] = 0;
+ srn.srn_pending[clone].ae_type = 0;
+ /* notify the kernel resume thread to continue */
+ PMD(PMD_SX, ("SRN_IOC_RESUME ok for clone %d\n", clone))
+ cv_signal(&srn_clones_cv[clone]);
+ mutex_exit(&srn_clone_lock);
+ return (0);
+
+ default:
+ PMD(PMD_SX, ("srn_ioctl unknown cmd EINVAL\n"))
+ return (EINVAL);
+ }
+}
+/*
+ * A very simple handshake with the srn driver,
+ * only one outstanding event at a time.
+ * The OS delivers the event and depending on type,
+ * either blocks waiting for the ack, or drives on
+ */
+void
+srn_notify(int type, int event)
+{
+ int clone, count;
+ PMD(PMD_SX, ("srn_notify entered with type %d, event 0x%x\n",
+ type, event));
+ ASSERT(mutex_owned(&srn_clone_lock));
+ switch (type) {
+ case SRN_TYPE_APM:
+ if (srn_apm_count == 0) {
+ PMD(PMD_SX, ("no apm types\n"))
+ return;
+ }
+ count = srn_apm_count;
+ break;
+ case SRN_TYPE_AUTOSX:
+ if (srn_autosx_count == 0) {
+ PMD(PMD_SX, ("no autosx types\n"))
+ return;
+ }
+ count = srn_autosx_count;
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+ ASSERT(count > 0);
+ PMD(PMD_SX, ("count %d\n", count))
+ for (clone = 0; clone < SRN_MAX_CLONE; clone++) {
+ if (srn.srn_type[clone] == type) {
+ if (type == SRN_TYPE_APM) {
+ ASSERT(srn.srn_pending[clone].ae_type == 0);
+ ASSERT(srn_poll_cnt[clone] == 0);
+ ASSERT(srn.srn_delivered[clone] == 0);
+ }
+ srn.srn_pending[clone].ae_type = event;
+ srn_poll_cnt[clone] = 1;
+ PMD(PMD_SX, ("pollwake %d\n", clone))
+ pollwakeup(&srn_pollhead[clone], (POLLRDNORM | POLLIN));
+ count--;
+ if (count == 0)
+ break;
+ }
+ }
+ if (type == SRN_TYPE_AUTOSX) { /* we don't wait */
+ PMD(PMD_SX, ("Not waiting for AUTOSX ack\n"))
+ return;
+ }
+ ASSERT(type == SRN_TYPE_APM);
+ /* otherwise wait for acks */
+restart:
+ /*
+ * We wait untill all of the pending events are cleared.
+ * We have to start over every time we do a cv_wait because
+ * we give up the mutex and can be re-entered
+ */
+ for (clone = 1; clone < SRN_MAX_CLONE; clone++) {
+ if (srn.srn_clones[clone] == 0 ||
+ srn.srn_type[clone] != SRN_TYPE_APM)
+ continue;
+ if (srn.srn_pending[clone].ae_type) {
+ PMD(PMD_SX, ("srn_notify waiting for ack for clone %d, "
+ "event %x\n", clone, event))
+ cv_wait(&srn_clones_cv[clone], &srn_clone_lock);
+ goto restart;
+ }
+ }
+ PMD(PMD_SX, ("srn_notify done with %x\n", event))
+}
diff --git a/usr/src/uts/common/io/srn.conf b/usr/src/uts/common/io/srn.conf
new file mode 100755
index 0000000000..7db6545647
--- /dev/null
+++ b/usr/src/uts/common/io/srn.conf
@@ -0,0 +1,27 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+name="srn" parent="pseudo" instance=0;
diff --git a/usr/src/uts/common/io/usb/hcd/ehci/ehci.c b/usr/src/uts/common/io/usb/hcd/ehci/ehci.c
index 3b986e1723..93b7815a7d 100644
--- a/usr/src/uts/common/io/usb/hcd/ehci/ehci.c
+++ b/usr/src/uts/common/io/usb/hcd/ehci/ehci.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -65,6 +65,11 @@ uint_t ehci_errmask = (uint_t)PRINT_MASK_ALL;
uint_t ehci_errlevel = USB_LOG_L2;
uint_t ehci_instance_debug = (uint_t)-1;
+/*
+ * Tunable to ensure host controller goes off even if a keyboard is attached.
+ */
+int force_ehci_off = 1;
+
/* Enable all workarounds for VIA VT62x2 */
uint_t ehci_vt62x2_workaround = EHCI_VIA_WORKAROUNDS;
diff --git a/usr/src/uts/common/io/usb/hcd/ehci/ehci_util.c b/usr/src/uts/common/io/usb/hcd/ehci/ehci_util.c
index bc760adfc8..44e6688a4b 100644
--- a/usr/src/uts/common/io/usb/hcd/ehci/ehci_util.c
+++ b/usr/src/uts/common/io/usb/hcd/ehci/ehci_util.c
@@ -54,6 +54,7 @@ extern void *ehci_statep;
extern void ehci_handle_endpoint_reclaimation(ehci_state_t *);
extern uint_t ehci_vt62x2_workaround;
+extern int force_ehci_off;
/* Adjustable variables for the size of the pools */
int ehci_qh_pool_size = EHCI_QH_POOL_SIZE;
@@ -2006,7 +2007,7 @@ ehci_cpr_suspend(ehci_state_t *ehcip)
* Stop the ehci host controller
* if usb keyboard is not connected.
*/
- if (ehcip->ehci_polled_kbd_count == 0) {
+ if (ehcip->ehci_polled_kbd_count == 0 || force_ehci_off != 0) {
Set_OpReg(ehci_command,
Get_OpReg(ehci_command) & ~EHCI_CMD_HOST_CTRL_RUN);
}
diff --git a/usr/src/uts/common/io/usb/hcd/openhci/ohci.c b/usr/src/uts/common/io/usb/hcd/openhci/ohci.c
index 2cb62f57ee..bbfef3dff2 100644
--- a/usr/src/uts/common/io/usb/hcd/openhci/ohci.c
+++ b/usr/src/uts/common/io/usb/hcd/openhci/ohci.c
@@ -47,6 +47,8 @@
/* Pointer to the state structure */
static void *ohci_statep;
+int force_ohci_off = 1;
+
/* Number of instances */
#define OHCI_INSTS 1
@@ -2353,7 +2355,7 @@ ohci_cpr_suspend(ohci_state_t *ohcip)
* Suspend the ohci host controller
* if usb keyboard is not connected.
*/
- if (ohcip->ohci_polled_kbd_count == 0) {
+ if (ohcip->ohci_polled_kbd_count == 0 || force_ohci_off != 0) {
Set_OpReg(hcr_control, HCR_CONTROL_SUSPD);
}
diff --git a/usr/src/uts/common/os/callb.c b/usr/src/uts/common/os/callb.c
index 819851e7b9..b0a9264762 100644
--- a/usr/src/uts/common/os/callb.c
+++ b/usr/src/uts/common/os/callb.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -255,7 +254,7 @@ callb_execute_class(int class, int code)
#ifdef CALLB_DEBUG
printf("callb_execute: name=%s func=%p arg=%p\n",
- cp->c_name, (void *)cp->c_func, (void *)cp->c_arg);
+ cp->c_name, (void *)cp->c_func, (void *)cp->c_arg);
#endif /* CALLB_DEBUG */
mutex_exit(&ct->ct_lock);
@@ -294,12 +293,14 @@ callb_generic_cpr(void *arg, int code)
switch (code) {
case CB_CODE_CPR_CHKPT:
cp->cc_events |= CALLB_CPR_START;
+#ifdef CPR_NOT_THREAD_SAFE
while (!(cp->cc_events & CALLB_CPR_SAFE))
/* cv_timedwait() returns -1 if it times out. */
if ((ret = cv_timedwait(&cp->cc_callb_cv,
cp->cc_lockp,
lbolt + callb_timeout_sec * hz)) == -1)
break;
+#endif
break;
case CB_CODE_CPR_RESUME:
diff --git a/usr/src/uts/common/os/cpu.c b/usr/src/uts/common/os/cpu.c
index d99d9403cd..ef306f2979 100644
--- a/usr/src/uts/common/os/cpu.c
+++ b/usr/src/uts/common/os/cpu.c
@@ -161,6 +161,9 @@ static struct _cpu_pause_info {
static kmutex_t pause_free_mutex;
static kcondvar_t pause_free_cv;
+void *(*cpu_pause_func)(void *) = NULL;
+
+
static struct cpu_sys_stats_ks_data {
kstat_named_t cpu_ticks_idle;
kstat_named_t cpu_ticks_user;
@@ -738,10 +741,12 @@ weakbinding_start(void)
* which is a good trade off.
*/
static void
-cpu_pause(volatile char *safe)
+cpu_pause(int index)
{
int s;
struct _cpu_pause_info *cpi = &cpu_pause_info;
+ volatile char *safe = &safe_list[index];
+ long lindex = index;
ASSERT((curthread->t_bound_cpu != NULL) || (*safe == PAUSE_DIE));
@@ -766,6 +771,16 @@ cpu_pause(volatile char *safe)
* setbackdq/setfrontdq.
*/
s = splhigh();
+ /*
+ * if cpu_pause_func() has been set then call it using
+ * index as the argument, currently only used by
+ * cpr_suspend_cpus(). This function is used as the
+ * code to execute on the "paused" cpu's when a machine
+ * comes out of a sleep state and CPU's were powered off.
+ * (could also be used for hotplugging CPU's).
+ */
+ if (cpu_pause_func != NULL)
+ (*cpu_pause_func)((void *)lindex);
mach_cpu_pause(safe);
@@ -809,13 +824,13 @@ static void
cpu_pause_alloc(cpu_t *cp)
{
kthread_id_t t;
- int cpun = cp->cpu_id;
+ long cpun = cp->cpu_id;
/*
* Note, v.v_nglobpris will not change value as long as I hold
* cpu_lock.
*/
- t = thread_create(NULL, 0, cpu_pause, (caddr_t)&safe_list[cpun],
+ t = thread_create(NULL, 0, cpu_pause, (void *)cpun,
0, &p0, TS_STOPPED, v.v_nglobpris - 1);
thread_lock(t);
t->t_bound_cpu = cp;
diff --git a/usr/src/uts/common/os/sunpci.c b/usr/src/uts/common/os/sunpci.c
index ff89c017d6..5bc4c71474 100644
--- a/usr/src/uts/common/os/sunpci.c
+++ b/usr/src/uts/common/os/sunpci.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -334,10 +334,12 @@ pci_save_config_regs(dev_info_t *dip)
off_t offset = 0;
uint8_t cap_ptr, cap_id;
int pcie = 0;
+ PMD(PMD_SX, ("pci_save_config_regs %s:%d\n", ddi_driver_name(dip),
+ ddi_get_instance(dip)))
if (pci_config_setup(dip, &confhdl) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s%d can't get config handle",
- ddi_driver_name(dip), ddi_get_instance(dip));
+ ddi_driver_name(dip), ddi_get_instance(dip));
return (DDI_FAILURE);
}
@@ -364,7 +366,7 @@ pci_save_config_regs(dev_info_t *dip)
if (pcie) {
/* PCI express device. Can have data in all 4k space */
regbuf = (uint32_t *)kmem_zalloc((size_t)PCIE_CONF_HDR_SIZE,
- KM_SLEEP);
+ KM_SLEEP);
p = regbuf;
/*
* Allocate space for mask.
@@ -406,12 +408,12 @@ pci_save_config_regs(dev_info_t *dip)
kmem_free(regbuf, (size_t)PCIE_CONF_HDR_SIZE);
} else {
regbuf = (uint32_t *)kmem_zalloc((size_t)PCI_CONF_HDR_SIZE,
- KM_SLEEP);
+ KM_SLEEP);
chsp = (pci_config_header_state_t *)regbuf;
chsp->chs_command = pci_config_get16(confhdl, PCI_CONF_COMM);
chsp->chs_header_type = pci_config_get8(confhdl,
- PCI_CONF_HEADER);
+ PCI_CONF_HEADER);
if ((chsp->chs_header_type & PCI_HEADER_TYPE_M) ==
PCI_HEADER_ONE)
chsp->chs_bridge_control =
@@ -767,3 +769,274 @@ restoreconfig_err:
pci_config_teardown(&confhdl);
return (DDI_FAILURE);
}
+
+/*ARGSUSED*/
+static int
+pci_lookup_pmcap(dev_info_t *dip, ddi_acc_handle_t conf_hdl,
+ uint16_t *pmcap_offsetp)
+{
+ uint8_t cap_ptr;
+ uint8_t cap_id;
+ uint8_t header_type;
+ uint16_t status;
+
+ header_type = pci_config_get8(conf_hdl, PCI_CONF_HEADER);
+ header_type &= PCI_HEADER_TYPE_M;
+
+ /* we don't deal with bridges, etc here */
+ if (header_type != PCI_HEADER_ZERO) {
+ return (DDI_FAILURE);
+ }
+
+ status = pci_config_get16(conf_hdl, PCI_CONF_STAT);
+ if ((status & PCI_STAT_CAP) == 0) {
+ return (DDI_FAILURE);
+ }
+
+ cap_ptr = pci_config_get8(conf_hdl, PCI_CONF_CAP_PTR);
+
+ /*
+ * Walk the capabilities searching for a PM entry.
+ */
+ while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) {
+ cap_id = pci_config_get8(conf_hdl, cap_ptr + PCI_CAP_ID);
+ if (cap_id == PCI_CAP_ID_PM) {
+ break;
+ }
+ cap_ptr = pci_config_get8(conf_hdl,
+ cap_ptr + PCI_CAP_NEXT_PTR);
+ }
+
+ if (cap_ptr == PCI_CAP_NEXT_PTR_NULL) {
+ return (DDI_FAILURE);
+ }
+ *pmcap_offsetp = cap_ptr;
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Do common pci-specific suspend actions:
+ * - enable wakeup if appropriate for the device
+ * - put device in lowest D-state that supports wakeup, or D3 if none
+ * - turn off bus mastering in control register
+ * For lack of per-dip storage (parent private date is pretty busy)
+ * we use properties to store the necessary context
+ * To avoid grotting through pci config space on every suspend,
+ * we leave the prop in existence after resume, cause we know that
+ * the detach framework code will dispose of it for us.
+ */
+
+typedef struct pci_pm_context {
+ int ppc_flags;
+ uint16_t ppc_cap_offset; /* offset in config space to pm cap */
+ uint16_t ppc_pmcsr; /* need this too */
+ uint16_t ppc_suspend_level;
+} pci_pm_context_t;
+
+#define SAVED_PM_CONTEXT "pci-pm-context"
+
+/* values for ppc_flags */
+#define PPCF_NOPMCAP 1
+
+/*
+ * Handle pci-specific suspend processing
+ * PM CSR and PCI CMD are saved by pci_save_config_regs().
+ * If device can wake up system via PME, enable it to do so
+ * Set device power level to lowest that can generate PME, or D3 if none can
+ * Turn off bus master enable in pci command register
+ */
+#if defined(__x86)
+extern int acpi_ddi_setwake(dev_info_t *dip, int level);
+#endif
+
+int
+pci_post_suspend(dev_info_t *dip)
+{
+ pci_pm_context_t *p;
+ uint16_t pmcap, pmcsr, pcicmd;
+ uint_t length;
+ int ret;
+ int fromprop = 1; /* source of memory *p */
+ ddi_acc_handle_t hdl;
+
+ PMD(PMD_SX, ("pci_post_suspend %s:%d\n",
+ ddi_driver_name(dip), ddi_get_instance(dip)))
+
+ if (pci_save_config_regs(dip) != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+
+ if (pci_config_setup(dip, &hdl) != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
+ SAVED_PM_CONTEXT, (uchar_t **)&p, &length) != DDI_PROP_SUCCESS) {
+ p = (pci_pm_context_t *)kmem_zalloc(sizeof (*p), KM_SLEEP);
+ fromprop = 0;
+ if (pci_lookup_pmcap(dip, hdl,
+ &p->ppc_cap_offset) != DDI_SUCCESS) {
+ p->ppc_flags |= PPCF_NOPMCAP;
+ ret = ndi_prop_update_byte_array(DDI_DEV_T_NONE, dip,
+ SAVED_PM_CONTEXT, (uchar_t *)p,
+ sizeof (pci_pm_context_t));
+ if (ret != DDI_PROP_SUCCESS) {
+ (void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
+ SAVED_PM_CONTEXT);
+ ret = DDI_FAILURE;
+ } else {
+ ret = DDI_SUCCESS;
+ }
+ kmem_free(p, sizeof (*p));
+ pci_config_teardown(&hdl);
+ return (DDI_SUCCESS);
+ }
+ /*
+ * Upon suspend, set the power level to the lowest that can
+ * wake the system. If none can, then set to lowest.
+ * XXX later we will need to check policy to see if this
+ * XXX device has had wakeup disabled
+ */
+ pmcap = pci_config_get16(hdl, p->ppc_cap_offset + PCI_PMCAP);
+ if ((pmcap & PCI_PMCAP_D3COLD_PME) != 0)
+ p->ppc_suspend_level =
+ (PCI_PMCSR_PME_EN | PCI_PMCSR_D3HOT);
+ else if ((pmcap & (PCI_PMCAP_D3HOT_PME | PCI_PMCAP_D2_PME)) !=
+ 0)
+ p->ppc_suspend_level = PCI_PMCSR_PME_EN | PCI_PMCSR_D2;
+ else if ((pmcap & PCI_PMCAP_D1_PME) != 0)
+ p->ppc_suspend_level = PCI_PMCSR_PME_EN | PCI_PMCSR_D1;
+ else if ((pmcap & PCI_PMCAP_D0_PME) != 0)
+ p->ppc_suspend_level = PCI_PMCSR_PME_EN | PCI_PMCSR_D0;
+ else
+ p->ppc_suspend_level = PCI_PMCSR_D3HOT;
+
+ /*
+ * we defer updating the property to catch the saved
+ * register values as well
+ */
+ }
+ /* If we set this in kmem_zalloc'd memory, we already returned above */
+ if ((p->ppc_flags & PPCF_NOPMCAP) != 0) {
+ ddi_prop_free(p);
+ pci_config_teardown(&hdl);
+ return (DDI_SUCCESS);
+ }
+
+
+ /*
+ * Turn off (Bus) Master Enable, since acpica will be turning off
+ * bus master aribitration
+ */
+ pcicmd = pci_config_get16(hdl, PCI_CONF_COMM);
+ pcicmd &= ~PCI_COMM_ME;
+ pci_config_put16(hdl, PCI_CONF_COMM, pcicmd);
+
+ /*
+ * set pm csr
+ */
+ pmcsr = pci_config_get16(hdl, p->ppc_cap_offset + PCI_PMCSR);
+ p->ppc_pmcsr = pmcsr;
+ pmcsr &= (PCI_PMCSR_STATE_MASK);
+ pmcsr |= (PCI_PMCSR_PME_STAT | p->ppc_suspend_level);
+ pci_config_put16(hdl, p->ppc_cap_offset + PCI_PMCSR, pmcsr);
+
+#if defined(__x86)
+ /*
+ * Arrange for platform wakeup enabling
+ */
+ if ((p->ppc_suspend_level & PCI_PMCSR_PME_EN) != 0) {
+ int retval;
+
+ retval = acpi_ddi_setwake(dip, 3); /* XXX 3 for now */
+ if (retval) {
+ PMD(PMD_SX, ("pci_post_suspend, setwake %s@%s rets "
+ "%x\n", PM_NAME(dip), PM_ADDR(dip), retval));
+ }
+ }
+#endif
+
+ /*
+ * Push out saved register values
+ */
+ ret = ndi_prop_update_byte_array(DDI_DEV_T_NONE, dip, SAVED_PM_CONTEXT,
+ (uchar_t *)p, sizeof (pci_pm_context_t));
+ if (ret == DDI_PROP_SUCCESS) {
+ if (fromprop)
+ ddi_prop_free(p);
+ else
+ kmem_free(p, sizeof (*p));
+ pci_config_teardown(&hdl);
+ return (DDI_SUCCESS);
+ }
+ /* Failed; put things back the way we found them */
+ (void) pci_restore_config_regs(dip);
+ if (fromprop)
+ ddi_prop_free(p);
+ else
+ kmem_free(p, sizeof (*p));
+ (void) ddi_prop_remove(DDI_DEV_T_NONE, dip, SAVED_PM_CONTEXT);
+ pci_config_teardown(&hdl);
+ return (DDI_FAILURE);
+}
+
+/*
+ * The inverse of pci_post_suspend; handle pci-specific resume processing
+ * First, turn device back on, then restore config space.
+ */
+
+int
+pci_pre_resume(dev_info_t *dip)
+{
+ ddi_acc_handle_t hdl;
+ pci_pm_context_t *p;
+ /* E_FUNC_SET_NOT_USED */
+ uint16_t pmcap, pmcsr;
+ int flags;
+ uint_t length;
+ clock_t drv_usectohz(clock_t microsecs);
+#if defined(__x86)
+ uint16_t suspend_level;
+#endif
+
+ PMD(PMD_SX, ("pci_pre_resume %s:%d\n", ddi_driver_name(dip),
+ ddi_get_instance(dip)))
+ if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
+ SAVED_PM_CONTEXT, (uchar_t **)&p, &length) != DDI_PROP_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+ flags = p->ppc_flags;
+ pmcap = p->ppc_cap_offset;
+ pmcsr = p->ppc_pmcsr;
+#if defined(__x86)
+ suspend_level = p->ppc_suspend_level;
+#endif
+ ddi_prop_free(p);
+ if ((flags & PPCF_NOPMCAP) != 0) {
+ return (DDI_SUCCESS);
+ }
+#if defined(__x86)
+ /*
+ * Turn platform wake enable back off
+ */
+ if ((suspend_level & PCI_PMCSR_PME_EN) != 0) {
+ int retval;
+
+ retval = acpi_ddi_setwake(dip, 0); /* 0 for now */
+ if (retval) {
+ PMD(PMD_SX, ("pci_pre_resume, setwake %s@%s rets "
+ "%x\n", PM_NAME(dip), PM_ADDR(dip), retval));
+ }
+ }
+#endif
+ if (pci_config_setup(dip, &hdl) != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+ pci_config_put16(hdl, pmcap + PCI_PMCSR, pmcsr);
+ delay(drv_usectohz(10000)); /* PCI PM spec D3->D0 (10ms) */
+ pci_config_teardown(&hdl);
+ (void) pci_restore_config_regs(dip); /* fudges D-state! */
+ return (DDI_SUCCESS);
+}
diff --git a/usr/src/uts/common/os/sunpm.c b/usr/src/uts/common/os/sunpm.c
index 9c89cf3637..40338e4fcf 100644
--- a/usr/src/uts/common/os/sunpm.c
+++ b/usr/src/uts/common/os/sunpm.c
@@ -174,6 +174,11 @@
#include <sys/disp.h>
#include <sys/sobject.h>
#include <sys/sunmdi.h>
+#include <sys/systm.h>
+#include <sys/cpuvar.h>
+#include <sys/cyclic.h>
+#include <sys/uadmin.h>
+#include <sys/srn.h>
/*
@@ -341,6 +346,37 @@ int autopm_enabled;
pm_cpupm_t cpupm = PM_CPUPM_NOTSET;
/*
+ * AutoS3 depends on autopm being enabled, and must be enabled by
+ * PM_START_AUTOS3 command.
+ */
+int autoS3_enabled;
+
+#if !defined(__sparc)
+/*
+ * on sparc these live in fillsysinfo.c
+ *
+ * If this variable is non-zero, cpr should return "not supported" when
+ * it is queried even though it would normally be supported on this platform.
+ */
+int cpr_supported_override;
+
+/*
+ * Some platforms may need to support CPR even in the absence of
+ * having the correct platform id information. If this
+ * variable is non-zero, cpr should proceed even in the absence
+ * of otherwise being qualified.
+ */
+int cpr_platform_enable = 0;
+
+#endif
+
+/*
+ * pm_S3_enabled indicates that we believe the platform can support S3,
+ * which we get from pmconfig(1M)
+ */
+int pm_S3_enabled;
+
+/*
* This flag is true while processes are stopped for a checkpoint/resume.
* Controlling processes of direct pm'd devices are not available to
* participate in power level changes, so we bypass them when this is set.
@@ -352,6 +388,7 @@ static int pm_processes_stopped;
/*
* see common/sys/epm.h for PMD_* values
*/
+
uint_t pm_debug = 0;
/*
@@ -364,6 +401,7 @@ uint_t pm_debug = 0;
* deadlocks and decremented at the end of pm_set_power()
*/
uint_t pm_divertdebug = 1;
+volatile uint_t pm_debug_to_console = 0;
kmutex_t pm_debug_lock; /* protects pm_divertdebug */
void prdeps(char *);
@@ -410,6 +448,13 @@ uint_t pm_poll_cnt[PM_MAX_CLONE]; /* count of events for poll */
unsigned char pm_interest[PM_MAX_CLONE];
struct pollhead pm_pollhead;
+/*
+ * Data structures shared with common/io/srn.c
+ */
+kmutex_t srn_clone_lock; /* protects srn_signal, srn_inuse */
+void (*srn_signal)(int type, int event);
+int srn_inuse; /* stop srn detach */
+
extern int hz;
extern char *platform_module_list[];
@@ -447,7 +492,6 @@ pscc_t *pm_pscc_direct;
#define PM_IS_NEXUS(dip) NEXUS_DRV(devopsp[PM_MAJOR(dip)])
#define POWERING_ON(old, new) ((old) == 0 && (new) != 0)
#define POWERING_OFF(old, new) ((old) != 0 && (new) == 0)
-#define PPM(dip) ((dev_info_t *)DEVI(dip)->devi_pm_ppm)
#define PM_INCR_NOTLOWEST(dip) { \
mutex_enter(&pm_compcnt_lock); \
@@ -510,14 +554,14 @@ typedef struct lock_loan {
static lock_loan_t lock_loan_head; /* list head is a dummy element */
#ifdef DEBUG
-#ifdef PMDDEBUG
+#ifdef PMDDEBUG
#define PMD_FUNC(func, name) char *(func) = (name);
-#else
+#else /* !PMDDEBUG */
#define PMD_FUNC(func, name)
-#endif
-#else
+#endif /* PMDDEBUG */
+#else /* !DEBUG */
#define PMD_FUNC(func, name)
-#endif
+#endif /* DEBUG */
/*
@@ -607,7 +651,7 @@ static boolean_t
pm_halt_callb(void *arg, int code)
{
_NOTE(ARGUNUSED(arg, code))
- return (B_TRUE); /* XXX for now */
+ return (B_TRUE);
}
/*
@@ -2057,6 +2101,25 @@ pm_ppm_notify_all_lowest(dev_info_t *dip, int mode)
(void) pm_ctlops((dev_info_t *)ppmcp->ppmc_dip, dip,
DDI_CTLOPS_POWER, &power_req, &result);
mutex_exit(&ppm_lock);
+ if (mode == PM_ALL_LOWEST) {
+ if (autoS3_enabled) {
+ PMD(PMD_SX, ("pm_ppm_notify_all_lowest triggering "
+ "autos3\n"))
+ mutex_enter(&srn_clone_lock);
+ if (srn_signal) {
+ srn_inuse++;
+ PMD(PMD_SX, ("(*srn_signal)(AUTOSX, 3)\n"))
+ (*srn_signal)(SRN_TYPE_AUTOSX, 3);
+ srn_inuse--;
+ } else {
+ PMD(PMD_SX, ("srn_signal NULL\n"))
+ }
+ mutex_exit(&srn_clone_lock);
+ } else {
+ PMD(PMD_SX, ("pm_ppm_notify_all_lowest autos3 "
+ "disabled\n"));
+ }
+ }
}
static void
@@ -3161,10 +3224,11 @@ pm_register_ppm(int (*func)(dev_info_t *), dev_info_t *dip)
if (i >= MAX_PPM_HANDLERS)
return (DDI_FAILURE);
while ((dip = ddi_get_parent(dip)) != NULL) {
- if (PM_GET_PM_INFO(dip) == NULL)
+ if (dip != ddi_root_node() && PM_GET_PM_INFO(dip) == NULL)
continue;
pm_ppm_claim(dip);
- if (pm_ppm_claimed(dip)) {
+ /* don't bother with the not power-manageable nodes */
+ if (pm_ppm_claimed(dip) && PM_GET_PM_INFO(dip)) {
/*
* Tell ppm about this.
*/
@@ -7549,7 +7613,7 @@ pm_cfb_setup(const char *stdout_path)
* IF console is fb and is power managed, don't do prom_printfs from
* pm debug macro
*/
- if (pm_cfb_enabled) {
+ if (pm_cfb_enabled && !pm_debug_to_console) {
if (pm_debug)
prom_printf("pm debug output will be to log only\n");
pm_divertdebug++;
@@ -7652,14 +7716,16 @@ pm_cfb_setup_intr(void)
extern void prom_set_outfuncs(void (*)(void), void (*)(void));
void pm_cfb_check_and_powerup(void);
+ mutex_init(&pm_cfb_lock, NULL, MUTEX_SPIN, (void *)ipltospl(SPL8));
+#ifdef PMDDEBUG
+ mutex_init(&pm_debug_lock, NULL, MUTEX_SPIN, (void *)ipltospl(SPL8));
+#endif
+
if (!stdout_is_framebuffer) {
PMD(PMD_CFB, ("%s: console not fb\n", pmf))
return;
}
- mutex_init(&pm_cfb_lock, NULL, MUTEX_SPIN, (void *)ipltospl(SPL8));
-#ifdef DEBUG
- mutex_init(&pm_debug_lock, NULL, MUTEX_SPIN, (void *)ipltospl(SPL8));
-#endif
+
/*
* setup software interrupt handler
*/
@@ -7811,14 +7877,26 @@ pm_path_to_major(char *path)
}
#ifdef DEBUG
+#ifndef sparc
+clock_t pt_sleep = 1;
+#endif
-char *pm_msgp;
-char *pm_bufend;
-char *pm_msgbuf = NULL;
-int pm_logpages = 2;
+char *pm_msgp;
+char *pm_bufend;
+char *pm_msgbuf = NULL;
+int pm_logpages = 0x100;
+#include <sys/sunldi.h>
+#include <sys/uio.h>
+clock_t pm_log_sleep = 1000;
+int pm_extra_cr = 1;
+volatile int pm_tty = 1;
#define PMLOGPGS pm_logpages
+#if defined(__x86)
+void pm_printf(char *s);
+#endif
+
/*PRINTFLIKE1*/
void
pm_log(const char *fmt, ...)
@@ -7841,15 +7919,30 @@ pm_log(const char *fmt, ...)
(void) vsnprintf(pm_msgbuf, size, fmt, adx);
if (!pm_divertdebug)
prom_printf("%s", pm_msgp);
+#if defined(__x86)
+ if (pm_tty) {
+ pm_printf(pm_msgp);
+ if (pm_extra_cr)
+ pm_printf("\r");
+ }
+#endif
pm_msgp = pm_msgbuf + size;
} else {
(void) vsnprintf(pm_msgp, size, fmt, adx);
+#if defined(__x86)
+ if (pm_tty) {
+ pm_printf(pm_msgp);
+ if (pm_extra_cr)
+ pm_printf("\r");
+ }
+#endif
if (!pm_divertdebug)
prom_printf("%s", pm_msgp);
pm_msgp += size;
}
va_end(adx);
mutex_exit(&pm_debug_lock);
+ drv_usecwait((clock_t)pm_log_sleep);
}
#endif /* DEBUG */
@@ -9108,16 +9201,19 @@ pm_desc_pwrchk_walk(dev_info_t *dip, void *arg)
PMD_FUNC(pmf, "desc_pwrchk")
pm_desc_pwrchk_t *pdpchk = (pm_desc_pwrchk_t *)arg;
pm_info_t *info = PM_GET_PM_INFO(dip);
- int i, curpwr, ce_level;
+ int i;
+ /* LINTED */
+ int curpwr, ce_level;
if (!info)
return (DDI_WALK_CONTINUE);
PMD(PMD_SET, ("%s: %s@%s(%s#%d)\n", pmf, PM_DEVICE(dip)))
for (i = 0; i < PM_NUMCMPTS(dip); i++) {
- curpwr = PM_CURPOWER(dip, i);
- if (curpwr == 0)
+ /* LINTED */
+ if ((curpwr = PM_CURPOWER(dip, i)) == 0)
continue;
+ /* E_FUNC_SET_NOT_USED */
ce_level = (pdpchk->pdpc_par_involved == 0) ? CE_PANIC :
CE_WARN;
PMD(PMD_SET, ("%s: %s@%s(%s#%d) is powered off while desc "
@@ -9170,3 +9266,58 @@ pm_return_lock(void)
mutex_exit(&pm_loan_lock);
kmem_free(cur, sizeof (*cur));
}
+
+#if defined(__x86)
+
+#define CPR_RXR 0x1
+#define CPR_TXR 0x20
+#define CPR_DATAREG 0x3f8
+#define CPR_LSTAT 0x3fd
+#define CPR_INTRCTL 0x3f9
+
+char
+pm_getchar(void)
+{
+ while ((inb(CPR_LSTAT) & CPR_RXR) != CPR_RXR)
+ drv_usecwait(10);
+
+ return (inb(CPR_DATAREG));
+
+}
+
+void
+pm_putchar(char c)
+{
+ while ((inb(CPR_LSTAT) & CPR_TXR) == 0)
+ drv_usecwait(10);
+
+ outb(CPR_DATAREG, c);
+}
+
+void
+pm_printf(char *s)
+{
+ while (*s) {
+ pm_putchar(*s++);
+ }
+}
+
+#endif
+
+int
+pm_ppm_searchlist(pm_searchargs_t *sp)
+{
+ power_req_t power_req;
+ int result = 0;
+ /* LINTED */
+ int ret;
+
+ power_req.request_type = PMR_PPM_SEARCH_LIST;
+ power_req.req.ppm_search_list_req.searchlist = sp;
+ ASSERT(DEVI(ddi_root_node())->devi_pm_ppm);
+ ret = pm_ctlops((dev_info_t *)DEVI(ddi_root_node())->devi_pm_ppm,
+ ddi_root_node(), DDI_CTLOPS_POWER, &power_req, &result);
+ PMD(PMD_SX, ("pm_ppm_searchlist returns %d, result %d\n",
+ ret, result))
+ return (result);
+}
diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile
index 75060a7a15..4cd97b5c3a 100644
--- a/usr/src/uts/common/sys/Makefile
+++ b/usr/src/uts/common/sys/Makefile
@@ -459,6 +459,7 @@ CHKHDRS= \
sockio.h \
squeue.h \
squeue_impl.h \
+ srn.h \
sservice.h \
stat.h \
statfs.h \
diff --git a/usr/src/uts/common/sys/asy.h b/usr/src/uts/common/sys/asy.h
index 491a222cd0..3b3d0c2a63 100644
--- a/usr/src/uts/common/sys/asy.h
+++ b/usr/src/uts/common/sys/asy.h
@@ -275,6 +275,15 @@ struct asycom {
ddi_iblock_cookie_t asy_iblock;
kmutex_t asy_excl; /* asy adaptive mutex */
kmutex_t asy_excl_hi; /* asy spinlock mutex */
+
+ /*
+ * The asy_soft_sr mutex should only be taken by the soft interrupt
+ * handler and the driver DDI_SUSPEND/DDI_RESUME code. It
+ * shouldn't be taken by any code that may get called indirectly
+ * by the soft interrupt handler (e.g. as a result of a put or
+ * putnext call).
+ */
+ kmutex_t asy_soft_sr; /* soft int suspend/resume mutex */
uchar_t asy_msr; /* saved modem status */
uchar_t asy_mcr; /* soft carrier bits */
uchar_t asy_lcr; /* console lcr bits */
@@ -300,11 +309,13 @@ struct asycom {
struct asyncline {
int async_flags; /* random flags */
kcondvar_t async_flags_cv; /* condition variable for flags */
+ kcondvar_t async_ops_cv; /* condition variable for async_ops */
dev_t async_dev; /* device major/minor numbers */
mblk_t *async_xmitblk; /* transmit: active msg block */
struct asycom *async_common; /* device common data */
tty_common_t async_ttycommon; /* tty driver common data */
bufcall_id_t async_wbufcid; /* id for pending write-side bufcall */
+ size_t async_wbufcds; /* Buffer size requested in bufcall */
timeout_id_t async_polltid; /* softint poll timeout id */
timeout_id_t async_dtrtid; /* delaying DTR turn on */
timeout_id_t async_utbrktid; /* hold minimum untimed break time id */
@@ -343,6 +354,10 @@ struct asyncline {
short async_ext; /* modem status change count */
short async_work; /* work to do flag */
timeout_id_t async_timer; /* close drain progress timer */
+
+ mblk_t *async_suspqf; /* front of suspend queue */
+ mblk_t *async_suspqb; /* back of suspend queue */
+ int async_ops; /* active operations counter */
};
/* definitions for async_flags field */
@@ -372,6 +387,8 @@ struct asyncline {
#define ASYNC_OUT_FLW_RESUME 0x00100000 /* output need to be resumed */
/* because of transition of flow */
/* control from stop to start */
+#define ASYNC_DDI_SUSPENDED 0x00200000 /* suspended by DDI */
+#define ASYNC_RESUME_BUFCALL 0x00400000 /* call bufcall when resumed by DDI */
/* asy_hwtype definitions */
#define ASY8250A 0x2 /* 8250A or 16450 */
@@ -389,6 +406,7 @@ struct asyncline {
#define ASY_RTS_DTR_OFF 0x00000020
#define ASY_IGNORE_CD 0x00000040
#define ASY_CONSOLE 0x00000080
+#define ASY_DDI_SUSPENDED 0x00000100 /* suspended by DDI */
/* definitions for asy_flags2 field */
#define ASY2_NO_LOOPBACK 0x00000001 /* Device doesn't support loopback */
diff --git a/usr/src/uts/common/sys/audio/impl/audio810_impl.h b/usr/src/uts/common/sys/audio/impl/audio810_impl.h
index 7846685221..05baf89564 100644
--- a/usr/src/uts/common/sys/audio/impl/audio810_impl.h
+++ b/usr/src/uts/common/sys/audio/impl/audio810_impl.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -64,6 +64,9 @@ extern "C" {
#define I810_BSIZE (8*1024)
+#define I810_NOT_SUSPENDED (0)
+#define I810_SUSPENDED (~I810_NOT_SUSPENDED)
+
#define I810_MAX_CHANNELS (200) /* force max # chs */
#define I810_MAX_HW_CHANNELS (32)
#define I810_MAX_IN_CHANNELS (1)
@@ -279,6 +282,9 @@ struct audio810_state {
int i810_psamples; /* pcm-out samples/intr */
uint32_t i810_res_flags; /* resource flags */
+ int i810_suspended; /* suspend/resume state */
+ int i810_busy_cnt; /* device busy count */
+ kcondvar_t i810_cv; /* suspend/resume cond. var */
};
typedef struct audio810_state audio810_state_t;
diff --git a/usr/src/uts/common/sys/cpr.h b/usr/src/uts/common/sys/cpr.h
index 337cbf9986..6fd5438cc0 100644
--- a/usr/src/uts/common/sys/cpr.h
+++ b/usr/src/uts/common/sys/cpr.h
@@ -178,6 +178,9 @@ struct cprconfig {
extern int cpr_debug;
+#define errp prom_printf
+#define DPRINT
+
/*
* CPR_DEBUG1 displays the main flow of CPR. Use it to identify which
* sub-module of CPR causes problems.
@@ -393,6 +396,7 @@ typedef struct cpr_terminator ctrm_t;
#define AD_CPR_TESTZ 8 /* test mode, auto-restart compress */
#define AD_CPR_PRINT 9 /* print out stats */
#define AD_CPR_NOCOMPRESS 10 /* store state file uncompressed */
+#define AD_CPR_SUSP_DEVICES 11 /* Only suspend resume devices */
#define AD_CPR_DEBUG0 100 /* clear debug flag */
#define AD_CPR_DEBUG1 101 /* display CPR main flow via prom */
#define AD_CPR_DEBUG2 102 /* misc small/mid size loops */
@@ -404,6 +408,31 @@ typedef struct cpr_terminator ctrm_t;
#define AD_CPR_DEBUG9 109 /* display stat data on console */
/*
+ * Suspend to RAM test points.
+ * Probably belong above, but are placed here for now.
+ */
+/* S3 leave hardware on and return success */
+#define AD_LOOPBACK_SUSPEND_TO_RAM_PASS 22
+
+/* S3 leave hardware on and return failure */
+#define AD_LOOPBACK_SUSPEND_TO_RAM_FAIL 23
+
+/* S3 ignored devices that fail to suspend */
+#define AD_FORCE_SUSPEND_TO_RAM 24
+
+/* S3 on a specified device */
+#define AD_DEVICE_SUSPEND_TO_RAM 25
+
+
+
+/*
+ * Temporary definition of the Suspend to RAM development subcommands
+ * so that non-ON apps will work after initial integration.
+ */
+#define DEV_SUSPEND_TO_RAM 200
+#define DEV_CHECK_SUSPEND_TO_RAM 201
+
+/*
* cprboot related information and definitions.
* The statefile names are hardcoded for now.
*/
@@ -506,6 +535,8 @@ extern cpr_t cpr_state;
#define C_ST_SETPROPS_0 10
#define C_ST_DUMP_NOSPC 11
#define C_ST_REUSABLE 12
+#define C_ST_NODUMP 13
+#define C_ST_MP_PAUSED 14
#define cpr_set_substate(a) (CPR->c_substate = (a))
@@ -547,18 +578,25 @@ struct cpr_walkinfo {
*/
#define DCF_CPR_SUSPENDED 0x1 /* device went through cpr_suspend */
+/*
+ * Values used to differentiate between suspend to disk and suspend to ram
+ * in cpr_suspend and cpr_resume
+ */
+
+#define CPR_TORAM 3
+#define CPR_TODISK 4
+
#ifndef _ASM
extern char *cpr_build_statefile_path(void);
extern char *cpr_enumerate_promprops(char **, size_t *);
extern char *cpr_get_statefile_prom_path(void);
-extern int cpr_clrbit(pfn_t, int);
extern int cpr_contig_pages(vnode_t *, int);
extern int cpr_default_setup(int);
extern int cpr_dump(vnode_t *);
extern int cpr_get_reusable_mode(void);
extern int cpr_isset(pfn_t, int);
-extern int cpr_main(void);
+extern int cpr_main(int);
extern int cpr_mp_offline(void);
extern int cpr_mp_online(void);
extern int cpr_nobit(pfn_t, int);
@@ -570,10 +608,10 @@ extern int cpr_read_phys_page(int, uint_t, int *);
extern int cpr_read_terminator(int, ctrm_t *, caddr_t);
extern int cpr_resume_devices(dev_info_t *, int);
extern int cpr_set_properties(int);
-extern int cpr_setbit(pfn_t, int);
extern int cpr_statefile_is_spec(void);
extern int cpr_statefile_offset(void);
extern int cpr_stop_kernel_threads(void);
+extern int cpr_threads_are_stopped(void);
extern int cpr_stop_user_threads(void);
extern int cpr_suspend_devices(dev_info_t *);
extern int cpr_validate_definfo(int);
@@ -585,9 +623,7 @@ extern int i_cpr_dump_sensitive_kpages(vnode_t *);
extern int i_cpr_save_sensitive_kpages(void);
extern pgcnt_t cpr_count_kpages(int, bitfunc_t);
extern pgcnt_t cpr_count_pages(caddr_t, size_t, int, bitfunc_t, int);
-extern pgcnt_t cpr_count_seg_pages(int, bitfunc_t);
extern pgcnt_t cpr_count_volatile_pages(int, bitfunc_t);
-extern pgcnt_t cpr_scan_kvseg(int, bitfunc_t, struct seg *);
extern pgcnt_t i_cpr_count_sensitive_kpages(int, bitfunc_t);
extern pgcnt_t i_cpr_count_special_kpages(int, bitfunc_t);
extern pgcnt_t i_cpr_count_storage_pages(int, bitfunc_t);
@@ -607,6 +643,9 @@ extern void cpr_stat_record_events(void);
extern void cpr_tod_get(cpr_time_t *ctp);
extern void cpr_tod_fault_reset(void);
extern void i_cpr_bitmap_cleanup(void);
+extern void i_cpr_stop_other_cpus(void);
+extern void i_cpr_alloc_cpus(void);
+extern void i_cpr_free_cpus(void);
/*PRINTFLIKE2*/
extern void cpr_err(int, const char *, ...) __KPRINTFLIKE(2);
diff --git a/usr/src/uts/common/sys/cpuvar.h b/usr/src/uts/common/sys/cpuvar.h
index 3093034ceb..4785796781 100644
--- a/usr/src/uts/common/sys/cpuvar.h
+++ b/usr/src/uts/common/sys/cpuvar.h
@@ -512,6 +512,7 @@ extern cpuset_t cpu_seqid_inuse;
#define CPU_CPR_OFFLINE 0x0
#define CPU_CPR_ONLINE 0x1
#define CPU_CPR_IS_OFFLINE(cpu) (((cpu)->cpu_cpr_flags & CPU_CPR_ONLINE) == 0)
+#define CPU_CPR_IS_ONLINE(cpu) ((cpu)->cpu_cpr_flags & CPU_CPR_ONLINE)
#define CPU_SET_CPR_FLAGS(cpu, flag) ((cpu)->cpu_cpr_flags |= flag)
#if defined(_KERNEL) || defined(_KMEMUSER)
diff --git a/usr/src/uts/common/sys/dktp/cmdk.h b/usr/src/uts/common/sys/dktp/cmdk.h
index 05dff32d9a..11b1bd9d78 100644
--- a/usr/src/uts/common/sys/dktp/cmdk.h
+++ b/usr/src/uts/common/sys/dktp/cmdk.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -73,10 +73,26 @@ struct cmdk {
uint32_t dk_altused; /* num entries in V_ALTSCTR */
uint32_t *dk_slc_cnt; /* entries per slice */
struct alts_ent **dk_slc_ent; /* link to remap data */
+
+ /*
+ * for power management
+ */
+ kmutex_t dk_pm_mutex;
+ kcondvar_t dk_suspend_cv;
+ uint32_t dk_pm_level;
+ uint32_t dk_pm_is_enabled;
};
+/*
+ * Power Management definitions
+ */
+#define CMDK_SPINDLE_UNINIT ((uint_t)(-1))
+#define CMDK_SPINDLE_OFF 0x0
+#define CMDK_SPINDLE_ON 0x1
+
/* common disk flags definitions */
#define CMDK_OPEN 0x1
+#define CMDK_SUSPEND 0x2
#define CMDK_TGDK_OPEN 0x4
#define CMDKUNIT(dev) (getminor((dev)) >> CMDK_UNITSHF)
diff --git a/usr/src/uts/common/sys/dktp/dadk.h b/usr/src/uts/common/sys/dktp/dadk.h
index 0f07c5a89f..f5c990e7c0 100644
--- a/usr/src/uts/common/sys/dktp/dadk.h
+++ b/usr/src/uts/common/sys/dktp/dadk.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -63,6 +63,8 @@ struct dadk {
uchar_t dad_thread_cnt; /* reference count on removable */
/* - disk state watcher thread */
kstat_t *dad_errstats; /* error stats */
+ kmutex_t dad_cmd_mutex;
+ int dad_cmd_count;
};
#define DAD_SECSIZ dad_phyg.g_secsiz
@@ -132,6 +134,8 @@ static void dadk_watch_thread(struct dadk *dadkp);
int dadk_inquiry(opaque_t objp, opaque_t *inqpp);
void dadk_cleanup(struct tgdk_obj *dkobjp);
+int dadk_getcmds(opaque_t objp);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/sys/epm.h b/usr/src/uts/common/sys/epm.h
index 5443eee3e2..35b656409b 100644
--- a/usr/src/uts/common/sys/epm.h
+++ b/usr/src/uts/common/sys/epm.h
@@ -34,6 +34,12 @@
#include <sys/ddi_impldefs.h>
#include <sys/taskq.h>
+/*
+ * XXXX
+ * Do we really need this include? It may be leftover from early CPUPM code.
+ * #include <sys/processor.h>
+ */
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -225,6 +231,8 @@ typedef enum pm_cpupm
PM_CPUPM_DISABLE /* do not power manage CPU devices */
} pm_cpupm_t;
+#define PPM(dip) ((dev_info_t *)DEVI(dip)->devi_pm_ppm)
+
/*
* The power request struct uses for the DDI_CTLOPS_POWER busctl.
*
@@ -258,7 +266,10 @@ typedef enum {
PMR_PPM_TRY_LOCK_POWER, /* ppm try lock power */
PMR_PPM_INIT_CHILD, /* ppm init child notify */
PMR_PPM_UNINIT_CHILD, /* ppm uninit child notify */
- PMR_PPM_POWER_LOCK_OWNER /* ppm power lock owner's address */
+ PMR_PPM_POWER_LOCK_OWNER, /* ppm power lock owner's address */
+ PMR_PPM_ENTER_SX, /* ppm: enter ACPI S[2-4] state */
+ PMR_PPM_EXIT_SX, /* ppm: enter ACPI S[2-4] state */
+ PMR_PPM_SEARCH_LIST /* ppm: search tuple list */
} pm_request_type;
/*
@@ -386,9 +397,47 @@ typedef struct power_req {
dev_info_t *who;
kthread_t *owner;
} ppm_power_lock_owner_req;
+ /*
+ * PMR_PPM_POWER_ENTER_SX
+ */
+ struct ppm_power_enter_sx_req {
+ int sx_state; /* S3, S4 */
+ int test_point; /* test point */
+ uint64_t wakephys; /* restart vector phys addr */
+ void *psr; /* PSM (apic) state buffer */
+ } ppm_power_enter_sx_req;
+ /*
+ * PMR_PPM_SEARCH_LIST
+ */
+ struct ppm_search_list {
+ pm_searchargs_t *searchlist;
+ int result;
+ } ppm_search_list_req;
} req;
} power_req_t;
+#define S3 3
+#define S4 4
+
+extern int cpr_test_point;
+extern major_t cpr_device;
+
+#define LOOP_BACK_NONE 0
+#define LOOP_BACK_PASS 1
+#define LOOP_BACK_FAIL 2
+#define FORCE_SUSPEND_TO_RAM 3
+#define DEVICE_SUSPEND_TO_RAM 4
+
+/*
+ * Struct passed as arg to appm_ioctl
+ */
+typedef struct s3_args {
+ int s3a_state; /* S3, S4 */
+ int s3a_test_point; /* test point */
+ uint64_t s3a_wakephys; /* restart vector physical addr */
+ void *s3a_psr; /* apic state save buffer */
+} s3a_t;
+
/*
* Structure used by the following bus_power operations:
*
@@ -624,25 +673,86 @@ typedef struct pm_thresh_rec {
#define PMD_PIL 0x20000000 /* print out PIL when calling power */
#define PMD_PHC 0x40000000 /* pm_power_has_changed messages */
#define PMD_LOCK 0x80000000
+#define PMD_SX 0x80000000 /* ACPI S[1234] states */
+#define PMD_PROTO PMD_SX /* and other Prototype stuff */
extern uint_t pm_debug;
extern uint_t pm_divertdebug;
/*PRINTFLIKE1*/
extern void pm_log(const char *fmt, ...) __KPRINTFLIKE(1);
+#if !defined(__sparc)
+/*
+ * On non-sparc machines, PMDDEBUG isn't as big a deal as Sparc, so we
+ * define PMDDEUG here for use on non-sparc platforms.
+ */
+#define PMDDEBUG
+#endif /* !__sparc */
+
#ifdef PMDDEBUG
#define PMD(level, arglist) { \
if (pm_debug & (level)) { \
pm_log arglist; \
} \
}
-#else
+#else /* !PMDDEBUG */
#define PMD(level, arglist) ((void)0);
+#endif /* PMDDEBUG */
+#ifndef sparc
+extern clock_t pt_sleep;
+/* code is char hex number to display on POST LED */
+#define PT(code) {outb(0x80, (char)code); drv_usecwait(pt_sleep); }
+#else
+#define PT(code)
#endif
-
#else
#define PMD(level, arglist)
+#define PT(code)
#endif
+/*
+ * Code Value Indication
+ *
+ */
+#define PT_SPL7 0x01 /* pm_suspend spl7 */
+#define PT_PMSRET 0x02 /* pm_suspend returns */
+#define PT_PPMCTLOP 0x03 /* invoking ppm_ctlops */
+#define PT_ACPISDEV 0x04 /* acpi suspend devices */
+#define PT_IC 0x05 /* acpi intr_clear */
+#define PT_1to1 0x06 /* 1:1 mapping */
+#define PT_SC 0x07 /* save context */
+#define PT_SWV 0x08 /* set waking vector */
+#define PT_SWV_FAIL 0x09 /* set waking vector failed */
+#define PT_EWE 0x0a /* enable wake events */
+#define PT_EWE_FAIL 0x0b /* enable wake events failed */
+#define PT_RTCW 0x0c /* setting rtc wakeup */
+#define PT_RTCW_FAIL 0x0d /* setting rtc wakeup failed */
+#define PT_TOD 0x0e /* setting tod */
+#define PT_SXP 0x0f /* sx prep */
+#define PT_SXE 0x10 /* sx enter */
+#define PT_SXE_FAIL 0x11 /* sx enter failed */
+#define PT_INSOM 0x12 /* insomnia label */
+#define PT_WOKE 0x20 /* woke up */
+#define PT_UNDO1to1 0x21 /* Undo 1:1 mapping */
+#define PT_LSS 0x22 /* leave sleep state */
+#define PT_LSS_FAIL 0x23 /* leave sleep state failed */
+#define PT_DPB 0x24 /* disable power button */
+#define PT_DPB_FAIL 0x25 /* disable power button failed */
+#define PT_DRTC_FAIL 0x26 /* disable rtc fails */
+#define PT_ACPIREINIT 0x27 /* reinit apic */
+#define PT_ACPIRESTORE 0x28 /* restore apic */
+#define PT_INTRRESTORE 0x28 /* restore interrupts */
+#define PT_RESDEV 0x2a /* ressume acpi devices */
+#define PT_CPU 0x2b /* init_cpu_syscall */
+#define PT_PRESUME 0x30 /* pm_resume entered */
+#define PT_RSUS 0x31 /* pm_resume "suspended" */
+#define PT_RKERN 0x32 /* pm_resume "kernel" */
+#define PT_RDRV 0x33 /* pm_resume "driver" */
+#define PT_RDRV_FAIL 0x34 /* pm_resume "driver" failed */
+#define PT_RRNOINVOL 0x35 /* pm_resume "reattach_noinvol" */
+#define PT_RUSER 0x36 /* pm_resume "user" */
+#define PT_RAPMSIG 0x37 /* pm_resume APM/SRN signal */
+#define PT_RMPO 0x38 /* pm_resume "mp_online" */
+#define PT_RDONE 0x39 /* pm_resume done */
extern void pm_detaching(dev_info_t *);
extern void pm_detach_failed(dev_info_t *);
@@ -721,7 +831,7 @@ extern int pm_is_cfb(dev_info_t *);
extern int pm_cfb_is_up(void);
#endif
-#ifdef DEBUG
+#ifdef DIPLOCKDEBUG
#define PM_LOCK_DIP(dip) { PMD(PMD_LOCK, ("dip lock %s@%s(%s#%d) " \
"%s %d\n", PM_DEVICE(dip), \
__FILE__, __LINE__)) \
diff --git a/usr/src/uts/common/sys/pm.h b/usr/src/uts/common/sys/pm.h
index a65075b3a5..8be171fef1 100644
--- a/usr/src/uts/common/sys/pm.h
+++ b/usr/src/uts/common/sys/pm.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -95,7 +95,16 @@ typedef enum {
PM_STOP_CPUPM,
PM_GET_CPU_THRESHOLD,
PM_SET_CPU_THRESHOLD,
- PM_GET_CPUPM_STATE
+ PM_GET_CPUPM_STATE,
+ PM_ENABLE_S3, /* allow pm to go to S3 state */
+ PM_DISABLE_S3, /* do not allow pm to go to S3 state */
+ PM_ENTER_S3, /* obsolete, not supported */
+ PM_START_AUTOS3,
+ PM_STOP_AUTOS3,
+ PM_SEARCH_LIST, /* search S3 enable/disable list */
+ PM_GET_AUTOS3_STATE,
+ PM_GET_S3_SUPPORT_STATE,
+ PM_GET_CMD_NAME
} pm_cmds;
/*
@@ -129,6 +138,17 @@ typedef struct pm_req {
} pm_req_t;
/*
+ * PM_SEARCH_LIST requires a list name, manufacturer and product name
+ * Searches the named list for a matching tuple.
+ * NOTE: This structure may be removed in a later release.
+ */
+typedef struct pm_searchargs {
+ char *pms_listname; /* name of list to search */
+ char *pms_manufacturer; /* 1st elment of tuple */
+ char *pms_product; /* 2nd elment of tuple */
+} pm_searchargs_t;
+
+/*
* Use these for PM_ADD_DEPENDENT and PM_ADD_DEPENDENT_PROPERTY
*/
#define pmreq_keeper physpath /* keeper in the physpath field */
@@ -208,6 +228,13 @@ typedef struct pm_state_change32 {
size32_t size; /* size of buffer physpath points to */
} pm_state_change32_t;
+typedef struct pm_searchargs32_t {
+ caddr32_t pms_listname;
+ caddr32_t pms_manufacturer;
+ caddr32_t pms_product;
+} pm_searchargs32_t;
+
+
#endif
/*
@@ -228,10 +255,13 @@ typedef enum {
PM_CPU_THRESHOLD,
PM_CPU_PM_ENABLED,
PM_CPU_PM_DISABLED,
- PM_CPU_PM_NOTSET
+ PM_CPU_PM_NOTSET,
+ PM_AUTOS3_ENABLED,
+ PM_AUTOS3_DISABLED,
+ PM_S3_SUPPORT_ENABLED,
+ PM_S3_SUPPORT_DISABLED
} pm_states;
-
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/sys/ppmvar.h b/usr/src/uts/common/sys/ppmvar.h
index 87d2a68086..1b8562a423 100644
--- a/usr/src/uts/common/sys/ppmvar.h
+++ b/usr/src/uts/common/sys/ppmvar.h
@@ -135,6 +135,12 @@ struct ppm_dc {
ldi_handle_t lh; /* layered (ldi) handle */
char *path; /* control device prom pathname */
uint_t cmd; /* search key: op to be performed */
+ /* one of: PPMDC_CPU_NEXT */
+ /* PPMDC_CPU_GO, PPMDC_FET_ON, */
+ /* PPMDC_FET_OFF, PPMDC_LED_ON, */
+ /* PPMDC_LED_OFF, PPMDC_PCI_ON, */
+ /* PPMDC_ENTER_S3, PPMDC_PCI_OFF */
+ /* PPMDC_EXIT_S3 commands */
uint_t method; /* control method / union selector */
/* one of PPMDC_KIO, PPMDC_I2CKIO, */
/* PPMDC_CPUSPEEDKIO */
@@ -157,6 +163,7 @@ struct ppm_dc {
uint_t post_delay; /* post delay, if any */
} kio;
+#ifdef sun4u
/* PPMDC_I2CKIO: KIO requires 'arg' as struct i2c_gpio */
/* (defined in i2c_client.h) */
struct m_i2ckio {
@@ -169,6 +176,7 @@ struct ppm_dc {
/* operation can be carried out */
uint_t post_delay; /* post delay, if any */
} i2c;
+#endif
/* PPMDC_CPUSPEEDKIO, PPMDC_VCORE: cpu estar related */
/* simple KIO */
@@ -203,6 +211,8 @@ typedef struct ppm_dc ppm_dc_t;
#define PPMDC_PWR_ON 16
#define PPMDC_RESET_OFF 17
#define PPMDC_RESET_ON 18
+#define PPMDC_ENTER_S3 19
+#define PPMDC_EXIT_S3 20
/*
* ppm_dc.method field - select union element
@@ -210,7 +220,9 @@ typedef struct ppm_dc ppm_dc_t;
#define PPMDC_KIO 1 /* simple ioctl with val as arg */
#define PPMDC_CPUSPEEDKIO 2 /* ioctl with speed index arg */
#define PPMDC_VCORE 3 /* CPU Vcore change operation */
+#ifdef sun4u
#define PPMDC_I2CKIO 4 /* ioctl with i2c_gpio_t as arg */
+#endif
/*
* devices that are powered by the same source
@@ -245,6 +257,7 @@ typedef struct ppm_domain ppm_domain_t;
#define PPMD_PCI 4 /* PCI pm model */
#define PPMD_PCI_PROP 5 /* PCI_PROP pm model */
#define PPMD_PCIE 6 /* PCI Express pm model */
+#define PPMD_SX 7 /* ACPI Sx pm model */
#define PPMD_IS_PCI(model) \
((model) == PPMD_PCI || (model) == PPMD_PCI_PROP)
diff --git a/usr/src/uts/common/sys/rtc.h b/usr/src/uts/common/sys/rtc.h
index 330f41ee69..1db2925542 100644
--- a/usr/src/uts/common/sys/rtc.h
+++ b/usr/src/uts/common/sys/rtc.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 1999 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -90,7 +90,7 @@ extern "C" {
#define RTC_VRT 0x80 /* Valid RAM and time bit */
#define RTC_NREG 0x0e /* number of RTC registers */
-#define RTC_NREGP 0x0a /* number of RTC registers to set time */
+#define RTC_NREGP 0x0c /* number of RTC registers to set time */
#define RTC_CENTURY 0x32 /* not included in RTC_NREG(P) */
/*
@@ -117,6 +117,8 @@ struct rtc_t { /* registers 0x0 to 0xD, 0x32 */
unsigned char rtc_statusc;
unsigned char rtc_statusd;
unsigned char rtc_century; /* register 0x32 */
+ unsigned char rtc_adom; /* ACPI-provided day alarm */
+ unsigned char rtc_amon; /* ACPI-provided mon alarm */
};
#ifdef __cplusplus
diff --git a/usr/src/uts/common/sys/srn.h b/usr/src/uts/common/sys/srn.h
new file mode 100644
index 0000000000..6145a98108
--- /dev/null
+++ b/usr/src/uts/common/sys/srn.h
@@ -0,0 +1,79 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_SRN_H
+#define _SYS_SRN_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The following ioctl commands and structures may not exist
+ * or may have a different interpretation in a future release.
+ */
+
+
+#define SRN_STANDBY_REQ 0xa01
+#define SRN_SUSPEND_REQ 0xa02
+#define SRN_NORMAL_RESUME 0xa03
+#define SRN_CRIT_RESUME 0xa04
+#define SRN_BATTERY_LOW 0xa05
+#define SRN_POWER_CHANGE 0xa06
+#define SRN_UPDATE_TIME 0xa07
+#define SRN_CRIT_SUSPEND_REQ 0xa08
+#define SRN_USER_STANDBY_REQ 0xa09
+#define SRN_USER_SUSPEND_REQ 0xa0a
+#define SRN_SYS_STANDBY_RESUME 0xa0b
+#define SRN_IOC_NEXTEVENT 0xa0c
+#define SRN_IOC_RESUME 0xa0d
+#define SRN_IOC_SUSPEND 0xa0e
+#define SRN_IOC_STANDBY 0xa0f
+#define SRN_IOC_AUTOSX 0xa10 /* change behavior of driver */
+
+typedef struct srn_event_info
+{
+ int ae_type;
+
+} srn_event_info_t;
+
+#ifdef _KERNEL
+
+#define SRN_MAX_CLONE 8 /* only two consumer known */
+
+#define SRN_TYPE_APM 1
+#define SRN_TYPE_AUTOSX 2
+
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_SRN_H */
diff --git a/usr/src/uts/common/sys/sunddi.h b/usr/src/uts/common/sys/sunddi.h
index 351a75bcd4..82cb130bd9 100644
--- a/usr/src/uts/common/sys/sunddi.h
+++ b/usr/src/uts/common/sys/sunddi.h
@@ -1919,6 +1919,12 @@ pci_target_enqueue(uint64_t, char *, char *, uint64_t);
void
pci_targetq_init(void);
+int
+pci_post_suspend(dev_info_t *dip);
+
+int
+pci_pre_resume(dev_info_t *dip);
+
/*
* the prototype for the C Language Type Model inquiry.
*/
diff --git a/usr/src/uts/common/sys/uadmin.h b/usr/src/uts/common/sys/uadmin.h
index d88f247a93..e307462896 100644
--- a/usr/src/uts/common/sys/uadmin.h
+++ b/usr/src/uts/common/sys/uadmin.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -58,12 +58,27 @@ extern "C" {
#define AD_POWEROFF 6 /* software poweroff */
#define AD_NOSYNC 7 /* do not sync filesystems on next A_DUMP */
+
/*
* Functions reserved for A_FREEZE (may not be available on all platforms)
+ * Note: AD_COMPRESS, AD_CHECK and AD_FORCE are now obsolete
+ * The first two are succeeded by AD_SUSPEND_TO_DISK and
+ * AD_CHECK_SUSPEND_TO_DISK respectively.
+ * AD_FORCE should not be used by any new application
+ *
+ * We maintain compatibility with the earlier interfaces:
+ * AD_COMPRESS and AD_CHECK, by preserving those values
+ * in the corresponding new interfaces
*/
+
#define AD_COMPRESS 0 /* store state file compressed during CPR */
#define AD_FORCE 1 /* force to do AD_COMPRESS */
#define AD_CHECK 2 /* test if CPR module is available */
+#define AD_SUSPEND_TO_DISK AD_COMPRESS /* A_FREEZE, CPR or ACPI S4 */
+#define AD_CHECK_SUSPEND_TO_DISK AD_CHECK /* A_FREEZE, CPR/S4 capable? */
+#define AD_SUSPEND_TO_RAM 20 /* A_FREEZE, S3 */
+#define AD_CHECK_SUSPEND_TO_RAM 21 /* A_FREEZE, S3 capable? */
+
/*
* NOTE: the following defines comprise an Unstable interface. Their semantics
* may change or they may be removed completely in a later release
diff --git a/usr/src/uts/common/syscall/uadmin.c b/usr/src/uts/common/syscall/uadmin.c
index df00b4e9d0..a5f92268f2 100644
--- a/usr/src/uts/common/syscall/uadmin.c
+++ b/usr/src/uts/common/syscall/uadmin.c
@@ -309,12 +309,15 @@ kadmin(int cmd, int fcn, void *mdep, cred_t *credp)
case A_FREEZE:
{
- /* XXX: declare in some header file */
- extern int cpr(int);
+ /*
+ * This is the entrypoint for all suspend/resume actions.
+ */
+ extern int cpr(int, void *);
if (modload("misc", "cpr") == -1)
return (ENOTSUP);
- error = cpr(fcn);
+ /* Let the CPR module decide what to do with mdep */
+ error = cpr(fcn, mdep);
break;
}
@@ -387,7 +390,8 @@ uadmin(int cmd, int fcn, uintptr_t mdep)
* a boot string. We pull that in as bootargs, if applicable.
*/
if (mdep != NULL &&
- (cmd == A_SHUTDOWN || cmd == A_REBOOT || cmd == A_DUMP)) {
+ (cmd == A_SHUTDOWN || cmd == A_REBOOT || cmd == A_DUMP ||
+ cmd == A_FREEZE)) {
bootargs = kmem_zalloc(BOOTARGS_MAX, KM_SLEEP);
if ((error = copyinstr((const char *)mdep, bootargs,
BOOTARGS_MAX, &nbytes)) != 0) {
diff --git a/usr/src/uts/i86pc/Makefile.files b/usr/src/uts/i86pc/Makefile.files
index 270dcdfd7b..48e880943a 100644
--- a/usr/src/uts/i86pc/Makefile.files
+++ b/usr/src/uts/i86pc/Makefile.files
@@ -34,6 +34,7 @@
# object lists
#
CORE_OBJS += \
+ acpi_stubs.o \
biosdisk.o \
bios_call.o \
cbe.o \
@@ -179,6 +180,8 @@ MCAMD_OBJS += \
CPUDRV_OBJS += cpudrv.o cpudrv_plat.o cpu_acpi.o speedstep.o
PPM_OBJS += ppm_subr.o ppm.o ppm_plat.o
+ACPIPPM_OBJS += acpippm.o acpisleep.o
+
ROOTNEX_OBJS += rootnex.o
TZMON_OBJS += tzmon.o
UPPC_OBJS += uppc.o psm_common.o
@@ -213,7 +216,10 @@ ASSYM_DEPS += \
sseblk.o \
swtch.o \
syscall_asm.o \
- syscall_asm_amd64.o
+ syscall_asm_amd64.o \
+ cpr_wakecode.o
+
+CPR_IMPL_OBJS = cpr_impl.o cpr_wakecode.o
$(KDI_ASSYM_DEPS:%=$(OBJS_DIR)/%): $(DSF_DIR)/$(OBJS_DIR)/kdi_assym.h
diff --git a/usr/src/uts/i86pc/Makefile.i86pc.shared b/usr/src/uts/i86pc/Makefile.i86pc.shared
index 145001e1da..311e8ee50b 100644
--- a/usr/src/uts/i86pc/Makefile.i86pc.shared
+++ b/usr/src/uts/i86pc/Makefile.i86pc.shared
@@ -255,7 +255,11 @@ DRV_KMODS += tzmon
DRV_KMODS += battery
DRV_KMODS += cpudrv
-DRV_KMODS += ppm
+
+#
+# Platform Power Modules
+#
+DRV_KMODS += ppm acpippm
$(CLOSED_BUILD)CLOSED_DRV_KMODS += memtest
@@ -307,3 +311,8 @@ DACF_KMODS += consconfig_dacf
# 'Mach' Modules (/kernel/mach):
#
MACH_KMODS += uppc
+
+#
+# CPR Misc Module.
+#
+MISC_KMODS += cpr
diff --git a/usr/src/uts/i86pc/Makefile.rules b/usr/src/uts/i86pc/Makefile.rules
index 07ee7dfe6c..8ca64e2fcb 100644
--- a/usr/src/uts/i86pc/Makefile.rules
+++ b/usr/src/uts/i86pc/Makefile.rules
@@ -92,6 +92,13 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/pcplusmp/%.c
$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/pcplusmp/%.s
$(COMPILE.s) -o $@ $<
+$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/ppm/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/ppm/%.s
+ $(COMPILE.s) -o $@ $<
+
$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/psm/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -264,6 +271,12 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/pcplusmp/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/pcplusmp/%.s
@($(LHEAD) $(LINT.s) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/ppm/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/ppm/%.s
+ @($(LHEAD) $(LINT.s) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/psm/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/i86pc/acpippm/Makefile b/usr/src/uts/i86pc/acpippm/Makefile
new file mode 100644
index 0000000000..2731469730
--- /dev/null
+++ b/usr/src/uts/i86pc/acpippm/Makefile
@@ -0,0 +1,91 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# uts/i86pc/acpippm/Makefile
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of the power managment
+# module for the ACPI subsystem
+#
+# i86pc implementation architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = acpippm
+OBJECTS = $(ACPIPPM_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(ACPIPPM_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_PSM_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/i86pc/io
+INC_PATH += -I$(UTSBASE)/i86pc/sys/acpi
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/i86pc/Makefile.i86pc
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# lint pass one enforcement
+#
+CFLAGS += $(CCVERBOSE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS) $(CONF_INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/i86pc/Makefile.targ
diff --git a/usr/src/uts/i86pc/cpr/Makefile b/usr/src/uts/i86pc/cpr/Makefile
new file mode 100644
index 0000000000..115b4d0800
--- /dev/null
+++ b/usr/src/uts/i86pc/cpr/Makefile
@@ -0,0 +1,98 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# uts/i86pc/cpr/Makefile
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of the cpr misc kernel module.
+#
+# i86pc implementation architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = cpr
+#
+#
+OBJECTS = $(CPR_IMPL_OBJS:%=$(OBJS_DIR)/%) \
+ $(CPR_OBJS:%=$(OBJS_DIR)/%) \
+ $(CPR_INTEL_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(CPR_OBJS:%.o=$(LINTS_DIR)/%.ln) \
+ $(CPR_IMPL_OBJS:%.o=$(LINTS_DIR)/%.ln) \
+ $(CPR_INTEL_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/i86pc/Makefile.i86pc
+
+#
+# Override defaults
+#
+LDFLAGS += -dy -N misc/acpica
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# lint pass one enforcement
+#
+CFLAGS += $(CCVERBOSE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/i86pc/Makefile.targ
diff --git a/usr/src/uts/i86pc/genassym/Makefile b/usr/src/uts/i86pc/genassym/Makefile
index 55425ce673..4d164a896d 100644
--- a/usr/src/uts/i86pc/genassym/Makefile
+++ b/usr/src/uts/i86pc/genassym/Makefile
@@ -78,6 +78,8 @@ clean.lint:
install: def
+CPPFLAGS += -I../../i86pc/io/ppm
+
#
# Create assym.h
#
diff --git a/usr/src/uts/i86pc/gfx_private/Makefile b/usr/src/uts/i86pc/gfx_private/Makefile
index 86381e7a48..e3b23a0f57 100644
--- a/usr/src/uts/i86pc/gfx_private/Makefile
+++ b/usr/src/uts/i86pc/gfx_private/Makefile
@@ -20,7 +20,7 @@
#
#
# uts/i86pc/gfx_private/Makefile
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#ident "%Z%%M% %I% %E% SMI"
@@ -45,6 +45,8 @@ LINTS = $(GFX_PRIVATE_OBJS:%.o=$(LINTS_DIR)/%.ln)
ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE)
VGATEXT_FONT = 8859-1
VGATEXT_SRC = $(UTSBASE)/intel/io/vgatext
+GFXP_VGATEXT = gfxp_vgatext
+GFXP_VGATEXT_SRC = $(UTSBASE)/i86pc/io/gfx_private
#
# dependency
diff --git a/usr/src/uts/i86pc/io/acpippm.conf b/usr/src/uts/i86pc/io/acpippm.conf
new file mode 100644
index 0000000000..53112bd96d
--- /dev/null
+++ b/usr/src/uts/i86pc/io/acpippm.conf
@@ -0,0 +1,28 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+
+name="acpippm" parent="pseudo" instance=0;
diff --git a/usr/src/uts/i86pc/io/cbe.c b/usr/src/uts/i86pc/io/cbe.c
index 7667bb2219..6bf01fa283 100644
--- a/usr/src/uts/i86pc/io/cbe.c
+++ b/usr/src/uts/i86pc/io/cbe.c
@@ -38,6 +38,7 @@
#include <sys/psm.h>
#include <sys/atomic.h>
#include <sys/clock.h>
+#include <sys/x86_archext.h>
#include <sys/ddi_impldefs.h>
#include <sys/ddi_intr.h>
#include <sys/avintr.h>
@@ -58,6 +59,8 @@ static ddi_softint_hdl_impl_t cbe_clock_hdl =
cyclic_id_t cbe_hres_cyclic;
int cbe_psm_timer_mode = TIMER_ONESHOT;
+extern int tsc_gethrtime_enable;
+
void cbe_hres_tick(void);
int
@@ -200,6 +203,43 @@ cbe_configure(cpu_t *cpu)
return (cpu);
}
+#ifndef __xpv
+/*
+ * declarations needed for time adjustment
+ */
+extern void tsc_suspend(void);
+extern void tsc_resume(void);
+/*
+ * Call the resume function in the cyclic, instead of inline in the
+ * resume path.
+ */
+extern int tsc_resume_in_cyclic;
+#endif
+
+/*ARGSUSED*/
+static void
+cbe_suspend(cyb_arg_t arg)
+{
+#ifndef __xpv
+ /*
+ * This is an x86 backend, so let the tsc_suspend
+ * that is specific to x86 platforms do the work.
+ */
+ tsc_suspend();
+#endif
+}
+
+/*ARGSUSED*/
+static void
+cbe_resume(cyb_arg_t arg)
+{
+#ifndef __xpv
+ if (tsc_resume_in_cyclic) {
+ tsc_resume();
+ }
+#endif
+}
+
void
cbe_enable(void *arg)
{
@@ -209,7 +249,11 @@ cbe_enable(void *arg)
if ((cbe_psm_timer_mode != TIMER_ONESHOT) && (me == 0))
return;
- ASSERT(!CPU_IN_SET(cbe_enabled, me));
+ /*
+ * Added (me == 0) to the ASSERT because the timer isn't
+ * disabled on CPU 0, and cbe_enable is called when we resume.
+ */
+ ASSERT((me == 0) || !CPU_IN_SET(cbe_enabled, me));
CPUSET_ADD(cbe_enabled, me);
if (cbe_psm_timer_mode == TIMER_ONESHOT)
(*psm_timer_enable)();
@@ -268,8 +312,8 @@ cbe_init(void)
cbe_set_level, /* cyb_set_level */
cbe_restore_level, /* cyb_restore_level */
cbe_xcall, /* cyb_xcall */
- NULL, /* cyb_suspend */
- NULL /* cyb_resume */
+ cbe_suspend, /* cyb_suspend */
+ cbe_resume /* cyb_resume */
};
hrtime_t resolution;
cyc_handler_t hdlr;
diff --git a/usr/src/uts/i86pc/io/consplat.c b/usr/src/uts/i86pc/io/consplat.c
index cfb0f5e861..feecc2d3d5 100644
--- a/usr/src/uts/i86pc/io/consplat.c
+++ b/usr/src/uts/i86pc/io/consplat.c
@@ -54,7 +54,8 @@ static char *
gfxdrv_name[] = {
"vgatext",
"i915",
- "nvidia"
+ "atiatom",
+ "nvidia",
};
int
diff --git a/usr/src/uts/i86pc/io/gfx_private/gfxp_vgatext.c b/usr/src/uts/i86pc/io/gfx_private/gfxp_vgatext.c
index 543349655c..a9d3c3827e 100644
--- a/usr/src/uts/i86pc/io/gfx_private/gfxp_vgatext.c
+++ b/usr/src/uts/i86pc/io/gfx_private/gfxp_vgatext.c
@@ -49,6 +49,9 @@
#include <sys/kd.h>
#include <sys/ddi_impldefs.h>
+
+
+
#include "gfx_private.h"
#define MYNAME "gfxp_vgatext"
@@ -151,6 +154,12 @@ static struct fbgattr vgatext_attr = {
#define GFXP_FLAG_CONSOLE 0x00000001
#define GFXP_IS_CONSOLE(softc) ((softc)->flags & GFXP_FLAG_CONSOLE)
+/*
+ * Global name used to write the softc pointer in, for the
+ * data wrapper vgatext_return_pointers()
+ */
+
+
int gfxp_vgatext_detach(dev_info_t *devi, ddi_detach_cmd_t cmd,
gfxp_vgatext_softc_ptr_t ptr);
static int vgatext_devinit(struct vgatext_softc *, struct vis_devinit *data);
@@ -168,9 +177,16 @@ static void vgatext_polled_cursor(struct vis_polledio_arg *,
struct vis_conscursor *);
static void vgatext_init(struct vgatext_softc *);
static void vgatext_set_text(struct vgatext_softc *);
+
+static void vgatext_save_text(struct vgatext_softc *softc);
+static void vgatext_restore_textmode(struct vgatext_softc *softc);
+static int vgatext_suspend(struct vgatext_softc *softc);
+static void vgatext_resume(struct vgatext_softc *softc);
+
#if defined(USE_BORDERS)
static void vgatext_init_graphics(struct vgatext_softc *);
#endif
+
static int vgatext_kdsetmode(struct vgatext_softc *softc, int mode);
static void vgatext_setfont(struct vgatext_softc *softc);
static void vgatext_get_cursor(struct vgatext_softc *softc,
@@ -220,7 +236,8 @@ gfxp_check_for_console(dev_info_t *devi, struct vgatext_softc *softc,
if (pci_config_setup(devi, &pci_conf) != DDI_SUCCESS) {
cmn_err(CE_WARN,
- MYNAME ": can't get PCI conf handle");
+ MYNAME
+ ": can't get PCI conf handle");
return;
}
@@ -294,25 +311,30 @@ gfxp_vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd,
int pci_pcie_bus = 0;
+
switch (cmd) {
case DDI_ATTACH:
- break;
+ break;
case DDI_RESUME:
- return (DDI_SUCCESS);
+ vgatext_resume(softc);
+ return (DDI_SUCCESS);
+
default:
- return (DDI_FAILURE);
+ return (DDI_FAILURE);
}
/* DDI_ATTACH */
+ softc->devi = devi; /* Copy and init DEVI */
+
softc->polledio.arg = (struct vis_polledio_arg *)softc;
softc->polledio.display = vgatext_polled_display;
softc->polledio.copy = vgatext_polled_copy;
softc->polledio.cursor = vgatext_polled_cursor;
error = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_get_parent(devi),
- DDI_PROP_DONTPASS, "device_type", &parent_type);
+ DDI_PROP_DONTPASS, "device_type", &parent_type);
if (error != DDI_SUCCESS) {
cmn_err(CE_WARN, MYNAME ": can't determine parent type.");
goto fail;
@@ -321,46 +343,50 @@ gfxp_vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd,
/* Not enable AGP and DRM by default */
if (STREQ(parent_type, "isa") || STREQ(parent_type, "eisa")) {
reg_rnumber = vgatext_get_isa_reg_index(devi, 1, VGA_REG_ADDR,
- &reg_offset);
+ &reg_offset);
if (reg_rnumber < 0) {
cmn_err(CE_WARN,
- MYNAME ": can't find reg entry for registers");
+ MYNAME
+ ": can't find reg entry for registers");
error = DDI_FAILURE;
goto fail;
}
softc->fb_regno = vgatext_get_isa_reg_index(devi, 0,
- VGA_MEM_ADDR, &mem_offset);
+ VGA_MEM_ADDR, &mem_offset);
if (softc->fb_regno < 0) {
cmn_err(CE_WARN,
- MYNAME ": can't find reg entry for memory");
+ MYNAME
+ ": can't find reg entry for memory");
error = DDI_FAILURE;
goto fail;
}
} else if (STREQ(parent_type, "pci") || STREQ(parent_type, "pciex")) {
pci_pcie_bus = 1;
reg_rnumber = vgatext_get_pci_reg_index(devi,
- PCI_REG_ADDR_M|PCI_REG_REL_M,
- PCI_ADDR_IO|PCI_RELOCAT_B, VGA_REG_ADDR,
- &reg_offset);
+ PCI_REG_ADDR_M|PCI_REG_REL_M,
+ PCI_ADDR_IO|PCI_RELOCAT_B, VGA_REG_ADDR,
+ &reg_offset);
if (reg_rnumber < 0) {
cmn_err(CE_WARN,
- MYNAME ": can't find reg entry for registers");
+ MYNAME
+ ": can't find reg entry for registers");
error = DDI_FAILURE;
goto fail;
}
softc->fb_regno = vgatext_get_pci_reg_index(devi,
- PCI_REG_ADDR_M|PCI_REG_REL_M,
- PCI_ADDR_MEM32|PCI_RELOCAT_B, VGA_MEM_ADDR,
- &mem_offset);
+ PCI_REG_ADDR_M|PCI_REG_REL_M,
+ PCI_ADDR_MEM32|PCI_RELOCAT_B, VGA_MEM_ADDR,
+ &mem_offset);
if (softc->fb_regno < 0) {
cmn_err(CE_WARN,
- MYNAME ": can't find reg entry for memory");
+ MYNAME
+ ": can't find reg entry for memory");
error = DDI_FAILURE;
goto fail;
}
} else {
cmn_err(CE_WARN, MYNAME ": unknown parent type \"%s\".",
- parent_type);
+ parent_type);
error = DDI_FAILURE;
goto fail;
}
@@ -368,8 +394,8 @@ gfxp_vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd,
parent_type = NULL;
error = ddi_regs_map_setup(devi, reg_rnumber,
- (caddr_t *)&softc->regs.addr, reg_offset, VGA_REG_SIZE,
- &dev_attr, &softc->regs.handle);
+ (caddr_t *)&softc->regs.addr, reg_offset, VGA_REG_SIZE,
+ &dev_attr, &softc->regs.handle);
if (error != DDI_SUCCESS)
goto fail;
softc->regs.mapped = B_TRUE;
@@ -377,9 +403,9 @@ gfxp_vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd,
softc->fb_size = VGA_MEM_SIZE;
error = ddi_regs_map_setup(devi, softc->fb_regno,
- (caddr_t *)&softc->fb.addr,
- mem_offset, softc->fb_size,
- &dev_attr, &softc->fb.handle);
+ (caddr_t *)&softc->fb.addr,
+ mem_offset, softc->fb_size,
+ &dev_attr, &softc->fb.handle);
if (error != DDI_SUCCESS)
goto fail;
softc->fb.mapped = B_TRUE;
@@ -397,7 +423,7 @@ gfxp_vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd,
goto fail;
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
- DDI_PROP_DONTPASS, "console", &cons) == DDI_SUCCESS) {
+ DDI_PROP_DONTPASS, "console", &cons) == DDI_SUCCESS) {
if (strcmp(cons, "graphics") == 0) {
happyface_boot = 1;
vgatext_silent = 1;
@@ -429,7 +455,15 @@ gfxp_vgatext_detach(dev_info_t *devi, ddi_detach_cmd_t cmd,
{
struct vgatext_softc *softc = (struct vgatext_softc *)ptr;
+
+
+
+
switch (cmd) {
+
+ case DDI_SUSPEND:
+ return (vgatext_suspend(softc));
+ /* break; */
case DDI_DETACH:
if (softc->fb.mapped)
ddi_regs_map_free(&softc->fb.handle);
@@ -439,7 +473,7 @@ gfxp_vgatext_detach(dev_info_t *devi, ddi_detach_cmd_t cmd,
default:
cmn_err(CE_WARN, "gfxp_vgatext_detach: unknown cmd 0x%x\n",
- cmd);
+ cmd);
return (DDI_FAILURE);
}
}
@@ -478,7 +512,7 @@ gfxp_vgatext_ioctl(
{
struct vgatext_softc *softc = (struct vgatext_softc *)ptr;
static char kernel_only[] =
- "gfxp_vgatext_ioctl: %s is a kernel only ioctl";
+ "gfxp_vgatext_ioctl: %s is a kernel only ioctl";
int err;
int kd_mode;
@@ -494,58 +528,59 @@ gfxp_vgatext_ioctl(
case VIS_DEVINIT:
- if (!(mode & FKIOCTL)) {
- cmn_err(CE_CONT, kernel_only, "VIS_DEVINIT");
- return (ENXIO);
- }
+ if (!(mode & FKIOCTL)) {
+ cmn_err(CE_CONT, kernel_only, "VIS_DEVINIT");
+ return (ENXIO);
+ }
- err = vgatext_devinit(softc, (struct vis_devinit *)data);
- if (err != 0) {
- cmn_err(CE_WARN,
- "gfxp_vgatext_ioctl: could not initialize console");
- return (err);
- }
- break;
+ err = vgatext_devinit(softc, (struct vis_devinit *)data);
+ if (err != 0) {
+ cmn_err(CE_WARN,
+ "gfxp_vgatext_ioctl: could not"
+ " initialize console");
+ return (err);
+ }
+ break;
case VIS_CONSCOPY: /* move */
{
- struct vis_conscopy pma;
+ struct vis_conscopy pma;
- if (ddi_copyin((void *)data, &pma,
- sizeof (struct vis_conscopy), mode))
- return (EFAULT);
+ if (ddi_copyin((void *)data, &pma,
+ sizeof (struct vis_conscopy), mode))
+ return (EFAULT);
- vgatext_cons_copy(softc, &pma);
- break;
+ vgatext_cons_copy(softc, &pma);
+ break;
}
case VIS_CONSDISPLAY: /* display */
{
- struct vis_consdisplay display_request;
+ struct vis_consdisplay display_request;
- if (ddi_copyin((void *)data, &display_request,
- sizeof (display_request), mode))
- return (EFAULT);
+ if (ddi_copyin((void *)data, &display_request,
+ sizeof (display_request), mode))
+ return (EFAULT);
- vgatext_cons_display(softc, &display_request);
- break;
+ vgatext_cons_display(softc, &display_request);
+ break;
}
case VIS_CONSCURSOR:
{
- struct vis_conscursor cursor_request;
+ struct vis_conscursor cursor_request;
- if (ddi_copyin((void *)data, &cursor_request,
- sizeof (cursor_request), mode))
- return (EFAULT);
+ if (ddi_copyin((void *)data, &cursor_request,
+ sizeof (cursor_request), mode))
+ return (EFAULT);
- vgatext_cons_cursor(softc, &cursor_request);
+ vgatext_cons_cursor(softc, &cursor_request);
- if (cursor_request.action == VIS_GET_CURSOR &&
- ddi_copyout(&cursor_request, (void *)data,
- sizeof (cursor_request), mode))
- return (EFAULT);
- break;
+ if (cursor_request.action == VIS_GET_CURSOR &&
+ ddi_copyout(&cursor_request, (void *)data,
+ sizeof (cursor_request), mode))
+ return (EFAULT);
+ break;
}
case VIS_GETCMAP:
@@ -576,6 +611,92 @@ gfxp_vgatext_ioctl(
return (0);
}
+/*
+ * vgatext_save_text
+ * vgatext_restore_textmode
+ * vgatext_suspend
+ * vgatext_resume
+ *
+ * Routines to save and restore contents of the VGA text area
+ * Mostly, this is to support Suspend/Resume operation for graphics
+ * device drivers. Here in the VGAtext common code, we simply squirrel
+ * away the contents of the hardware's text area during Suspend and then
+ * put it back during Resume
+ */
+static void
+vgatext_save_text(struct vgatext_softc *softc)
+{
+ unsigned i;
+
+ for (i = 0; i < sizeof (softc->shadow); i++)
+ softc->shadow[i] = softc->text_base[i];
+}
+
+static void
+vgatext_restore_textmode(struct vgatext_softc *softc)
+{
+ unsigned i;
+
+ vgatext_init(softc);
+ for (i = 0; i < sizeof (softc->shadow); i++) {
+ softc->text_base[i] = softc->shadow[i];
+ }
+ if (softc->cursor.visible) {
+ vgatext_set_cursor(softc,
+ softc->cursor.row, softc->cursor.col);
+ }
+ vgatext_restore_colormap(softc);
+}
+
+static int
+vgatext_suspend(struct vgatext_softc *softc)
+{
+ switch (softc->mode) {
+ case KD_TEXT:
+ vgatext_save_text(softc);
+ break;
+
+ case KD_GRAPHICS:
+ break;
+
+ default:
+ cmn_err(CE_WARN, MYNAME ": unknown mode in vgatext_suspend.");
+ return (DDI_FAILURE);
+ }
+ return (DDI_SUCCESS);
+}
+
+static void
+vgatext_resume(struct vgatext_softc *softc)
+{
+
+ switch (softc->mode) {
+ case KD_TEXT:
+ vgatext_restore_textmode(softc);
+ break;
+
+ case KD_GRAPHICS:
+
+ /*
+ * Upon RESUME, the graphics device will always actually
+ * be in TEXT mode even though the Xorg server did not
+ * make that mode change itself (the suspend code did).
+ * We want first, therefore, to restore textmode
+ * operation fully, and then the Xorg server will
+ * do the rest to restore the device to its
+ * (hi resolution) graphics mode
+ */
+ vgatext_restore_textmode(softc);
+#if defined(USE_BORDERS)
+ vgatext_init_graphics(softc);
+#endif
+ break;
+ default:
+ cmn_err(CE_WARN, MYNAME ": unknown mode in vgatext_resume.");
+ break;
+ }
+}
+
static int
vgatext_kdsetmode(struct vgatext_softc *softc, int mode)
{
@@ -593,21 +714,23 @@ vgatext_kdsetmode(struct vgatext_softc *softc, int mode)
softc->current_base = softc->text_base;
if (softc->cursor.visible) {
vgatext_set_cursor(softc,
- softc->cursor.row, softc->cursor.col);
+ softc->cursor.row, softc->cursor.col);
}
vgatext_restore_colormap(softc);
break;
case KD_GRAPHICS:
+
+
if (vgatext_silent == 1) {
extern void progressbar_stop(void);
vgatext_silent = 0;
progressbar_stop();
}
- for (i = 0; i < sizeof (softc->shadow); i++) {
- softc->shadow[i] = softc->text_base[i];
- }
+ vgatext_save_text(softc);
+
+
softc->current_base = softc->shadow;
#if defined(USE_BORDERS)
vgatext_init_graphics(softc);
@@ -637,7 +760,7 @@ gfxp_vgatext_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
}
if (!(off >= VGA_MMAP_FB_BASE &&
- off < VGA_MMAP_FB_BASE + softc->fb_size)) {
+ off < VGA_MMAP_FB_BASE + softc->fb_size)) {
cmn_err(CE_WARN, "vgatext: Can't map offset 0x%llx", off);
return (-1);
}
@@ -647,9 +770,9 @@ gfxp_vgatext_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
else
length = len;
- if ((err = devmap_devmem_setup(dhp, softc->devi, NULL, softc->fb_regno,
- off - VGA_MMAP_FB_BASE,
- length, PROT_ALL, 0, &dev_attr)) < 0) {
+ if ((err = devmap_devmem_setup(dhp, softc->devi,
+ NULL, softc->fb_regno, off - VGA_MMAP_FB_BASE,
+ length, PROT_ALL, 0, &dev_attr)) < 0) {
return (err);
}
@@ -709,10 +832,10 @@ vgatext_cons_display(struct vgatext_softc *softc, struct vis_consdisplay *da)
* system startup graphics.
*/
attr = (solaris_color_to_pc_color[da->bg_color & 0xf] << 4)
- | solaris_color_to_pc_color[da->fg_color & 0xf];
+ | solaris_color_to_pc_color[da->fg_color & 0xf];
string = da->data;
addr = (struct cgatext *)softc->current_base
- + (da->row * TEXT_COLS + da->col);
+ + (da->row * TEXT_COLS + da->col);
for (i = 0; i < da->width; i++) {
addr->ch = string[i];
addr->attr = attr;
@@ -915,20 +1038,20 @@ vgatext_set_text(struct vgatext_softc *softc)
/* set sequencer registers */
vga_set_seq(&softc->regs, VGA_SEQ_RST_SYN,
- (vga_get_seq(&softc->regs, VGA_SEQ_RST_SYN) &
- ~VGA_SEQ_RST_SYN_NO_SYNC_RESET));
+ (vga_get_seq(&softc->regs, VGA_SEQ_RST_SYN) &
+ ~VGA_SEQ_RST_SYN_NO_SYNC_RESET));
for (i = 1; i < NUM_SEQ_REG; i++) {
vga_set_seq(&softc->regs, i, VGA_SEQ_TEXT[i]);
}
vga_set_seq(&softc->regs, VGA_SEQ_RST_SYN,
- (vga_get_seq(&softc->regs, VGA_SEQ_RST_SYN) |
- VGA_SEQ_RST_SYN_NO_ASYNC_RESET |
- VGA_SEQ_RST_SYN_NO_SYNC_RESET));
+ (vga_get_seq(&softc->regs, VGA_SEQ_RST_SYN) |
+ VGA_SEQ_RST_SYN_NO_ASYNC_RESET |
+ VGA_SEQ_RST_SYN_NO_SYNC_RESET));
/* set crt controller registers */
vga_set_crtc(&softc->regs, VGA_CRTC_VRE,
- (vga_get_crtc(&softc->regs, VGA_CRTC_VRE) &
- ~VGA_CRTC_VRE_LOCK));
+ (vga_get_crtc(&softc->regs, VGA_CRTC_VRE) &
+ ~VGA_CRTC_VRE_LOCK));
for (i = 0; i < NUM_CRTC_REG; i++) {
vga_set_crtc(&softc->regs, i, VGA_CRTC_TEXT[i]);
}
@@ -946,8 +1069,8 @@ vgatext_set_text(struct vgatext_softc *softc)
/* set palette */
for (i = 0; i < VGA_TEXT_CMAP_ENTRIES; i++) {
vga_put_cmap(&softc->regs, i, VGA_TEXT_PALETTES[i][0] << 2,
- VGA_TEXT_PALETTES[i][1] << 2,
- VGA_TEXT_PALETTES[i][2] << 2);
+ VGA_TEXT_PALETTES[i][1] << 2,
+ VGA_TEXT_PALETTES[i][2] << 2);
}
for (i = VGA_TEXT_CMAP_ENTRIES; i < VGA8_CMAP_ENTRIES; i++) {
vga_put_cmap(&softc->regs, i, 0, 0, 0);
@@ -970,10 +1093,10 @@ vgatext_init(struct vgatext_softc *softc)
vga_set_atr(&softc->regs, VGA_ATR_MODE, atr_mode);
#if defined(USE_BORDERS)
vga_set_atr(&softc->regs, VGA_ATR_BDR_CLR,
- vga_get_atr(&softc->regs, VGA_BRIGHT_WHITE));
+ vga_get_atr(&softc->regs, VGA_BRIGHT_WHITE));
#else
vga_set_atr(&softc->regs, VGA_ATR_BDR_CLR,
- vga_get_atr(&softc->regs, VGA_BLACK));
+ vga_get_atr(&softc->regs, VGA_BLACK));
#endif
vgatext_setfont(softc); /* need selectable font? */
}
@@ -983,7 +1106,7 @@ static void
vgatext_init_graphics(struct vgatext_softc *softc)
{
vga_set_atr(&softc->regs, VGA_ATR_BDR_CLR,
- vga_get_atr(&softc->regs, VGA_BLACK));
+ vga_get_atr(&softc->regs, VGA_BLACK));
}
#endif
@@ -1044,9 +1167,9 @@ vgatext_save_colormap(struct vgatext_softc *softc)
}
for (i = 0; i < VGA8_CMAP_ENTRIES; i++) {
vga_get_cmap(&softc->regs, i,
- &softc->colormap[i].red,
- &softc->colormap[i].green,
- &softc->colormap[i].blue);
+ &softc->colormap[i].red,
+ &softc->colormap[i].green,
+ &softc->colormap[i].blue);
}
}
@@ -1060,9 +1183,9 @@ vgatext_restore_colormap(struct vgatext_softc *softc)
}
for (i = 0; i < VGA8_CMAP_ENTRIES; i++) {
vga_put_cmap(&softc->regs, i,
- softc->colormap[i].red,
- softc->colormap[i].green,
- softc->colormap[i].blue);
+ softc->colormap[i].red,
+ softc->colormap[i].green,
+ softc->colormap[i].blue);
}
}
@@ -1097,7 +1220,7 @@ vgatext_get_pci_reg_index(
pci_regspec_t *reg;
if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
- "reg", (caddr_t)&reg, &length) != DDI_PROP_SUCCESS) {
+ "reg", (caddr_t)&reg, &length) != DDI_PROP_SUCCESS) {
return (-1);
}
@@ -1156,7 +1279,7 @@ vgatext_get_isa_reg_index(
struct regspec *reg;
if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
- "reg", (caddr_t)&reg, &length) != DDI_PROP_SUCCESS) {
+ "reg", (caddr_t)&reg, &length) != DDI_PROP_SUCCESS) {
return (-1);
}
@@ -1176,3 +1299,22 @@ vgatext_get_isa_reg_index(
return (-1);
}
+
+/*
+ * This vgatext function is used to return the fb, and reg pointers
+ * and handles for peer graphics drivers.
+ */
+
+void
+vgatext_return_pointers(struct vgatext_softc *softc,
+ struct vgaregmap *fbs,
+ struct vgaregmap *regss)
+{
+
+ fbs->addr = softc->fb.addr;
+ fbs->handle = softc->fb.handle;
+ fbs->mapped = softc->fb.mapped;
+ regss->addr = softc->regs.addr;
+ regss->handle = softc->regs.handle;
+ regss->mapped = softc->regs.mapped;
+}
diff --git a/usr/src/uts/i86pc/io/mc/mcamd_drv.c b/usr/src/uts/i86pc/io/mc/mcamd_drv.c
index d7c9371795..284de242a9 100644
--- a/usr/src/uts/i86pc/io/mc/mcamd_drv.c
+++ b/usr/src/uts/i86pc/io/mc/mcamd_drv.c
@@ -1491,6 +1491,17 @@ mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
int chipid, rc;
mc_t *mc;
+ /*
+ * This driver has no hardware state, but does
+ * claim to have a reg property, so it will be
+ * called on suspend. It is probably better to
+ * make sure it doesn't get called on suspend,
+ * but it is just as easy to make sure we just
+ * return DDI_SUCCESS if called.
+ */
+ if (cmd == DDI_RESUME)
+ return (DDI_SUCCESS);
+
if (cmd != DDI_ATTACH || mc_no_attach != 0)
return (DDI_FAILURE);
@@ -1672,7 +1683,14 @@ mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
static int
mc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
- return (DDI_FAILURE);
+ /*
+ * See the comment about suspend in
+ * mc_attach().
+ */
+ if (cmd == DDI_SUSPEND)
+ return (DDI_SUCCESS);
+ else
+ return (DDI_FAILURE);
}
static struct dev_ops mc_ops = {
diff --git a/usr/src/uts/i86pc/io/mp_platform_common.c b/usr/src/uts/i86pc/io/mp_platform_common.c
index 7acbca94a8..563b6b7cdd 100644
--- a/usr/src/uts/i86pc/io/mp_platform_common.c
+++ b/usr/src/uts/i86pc/io/mp_platform_common.c
@@ -30,8 +30,9 @@
* PSMI 1.2 extensions are supported only in 2.7 and later versions.
* PSMI 1.3 and 1.4 extensions are supported in Solaris 10.
* PSMI 1.5 extensions are supported in Solaris Nevada.
+ * PSMI 1.6 extensions are supported in Solaris Nevada.
*/
-#define PSMI_1_5
+#define PSMI_1_6
#include <sys/processor.h>
#include <sys/time.h>
@@ -3989,3 +3990,182 @@ apic_is_ioapic_AMD_813x(uint32_t physaddr)
ndi_rele_devi(ioapicsnode);
return (rv);
}
+
+struct apic_state {
+ int32_t as_task_reg;
+ int32_t as_dest_reg;
+ int32_t as_format_reg;
+ int32_t as_local_timer;
+ int32_t as_pcint_vect;
+ int32_t as_int_vect0;
+ int32_t as_int_vect1;
+ int32_t as_err_vect;
+ int32_t as_init_count;
+ int32_t as_divide_reg;
+ int32_t as_spur_int_reg;
+ int32_t as_ioapic[6][24]; /* spec says 23 */
+};
+
+
+static void
+apic_save_state(struct apic_state *sp)
+{
+ int i;
+
+ PMD(PMD_SX, ("apic_save_state %p\n", (void *)sp))
+ /*
+ * First the local APIC.
+ */
+ sp->as_task_reg = apicadr[APIC_TASK_REG];
+ sp->as_dest_reg = apicadr[APIC_DEST_REG];
+ sp->as_format_reg = apicadr[APIC_FORMAT_REG];
+ sp->as_local_timer = apicadr[APIC_LOCAL_TIMER];
+ sp->as_pcint_vect = apicadr[APIC_PCINT_VECT];
+ sp->as_int_vect0 = apicadr[APIC_INT_VECT0];
+ sp->as_int_vect1 = apicadr[APIC_INT_VECT1];
+ sp->as_err_vect = apicadr[APIC_ERR_VECT];
+ sp->as_init_count = apicadr[APIC_INIT_COUNT];
+ sp->as_divide_reg = apicadr[APIC_DIVIDE_REG];
+ sp->as_spur_int_reg = apicadr[APIC_SPUR_INT_REG];
+
+ /*
+ * if on the boot processor then save the IO APICs.
+ */
+ if (psm_get_cpu_id() == 0) {
+ for (i = 0; i < apic_io_max; i++) {
+ volatile uint32_t *ioapic = apicioadr[i];
+ int intin_max, j;
+
+ /* Bits 23-16 define the maximum redirection entries */
+ ioapic[APIC_IO_REG] = APIC_VERS_CMD;
+ intin_max = (ioapic[APIC_IO_DATA] >> 16) & 0xff;
+#if 0 /* debug */
+ prom_printf("\nIOAPIC %d (%d redirs):\n",
+ i, intin_max+1);
+#endif /* debug */
+ for (j = 0; j <= intin_max; j++) {
+ ioapic[APIC_IO_REG] = APIC_RDT_CMD + 2*j;
+ sp->as_ioapic[i][j] = ioapic[APIC_IO_DATA];
+#if 0 /* debug */
+ prom_printf("\t%d: %x\n", j, as_ioapic[i][j]);
+#endif /* debug */
+ }
+ }
+ }
+}
+
+static void
+apic_restore_state(struct apic_state *sp)
+{
+ int i;
+ int iflag;
+ apic_irq_t *irqp;
+ int rv;
+ int retval = 0;
+
+ /*
+ * First the local APIC.
+ */
+ apicadr[APIC_TASK_REG] = sp->as_task_reg;
+ apicadr[APIC_DEST_REG] = sp->as_dest_reg;
+ apicadr[APIC_FORMAT_REG] = sp->as_format_reg;
+ apicadr[APIC_LOCAL_TIMER] = sp->as_local_timer;
+ apicadr[APIC_PCINT_VECT] = sp->as_pcint_vect;
+ apicadr[APIC_INT_VECT0] = sp->as_int_vect0;
+ apicadr[APIC_INT_VECT1] = sp->as_int_vect1;
+ apicadr[APIC_ERR_VECT] = sp->as_err_vect;
+ apicadr[APIC_INIT_COUNT] = sp->as_init_count;
+ apicadr[APIC_DIVIDE_REG] = sp->as_divide_reg;
+ apicadr[APIC_SPUR_INT_REG] = sp->as_spur_int_reg;
+
+ /*
+ * the following only needs to be done once, so we do it on the
+ * boot processor, since we know that we only have one of those
+ */
+ if (psm_get_cpu_id() == 0) {
+ /*
+ * regenerate the IO APICs.
+ */
+
+ iflag = intr_clear();
+ lock_set(&apic_ioapic_lock);
+
+ for (i = apic_min_device_irq; i < apic_max_device_irq; i++) {
+ if ((irqp = apic_irq_table[i]) == NULL)
+ continue;
+ for (; irqp; irqp = irqp->airq_next) {
+ if (irqp->airq_mps_intr_index == FREE_INDEX)
+ continue;
+ if (irqp->airq_temp_cpu != IRQ_UNINIT) {
+ rv = apic_setup_io_intr(irqp, i,
+ B_FALSE);
+ if (rv) {
+ PMD(PMD_SX,
+ ("apic_setup_io_intr(%p, "
+ "%d) %d\n", (void *)irqp,
+ i, rv));
+ }
+ retval |= rv;
+ }
+ }
+ }
+
+ PMD(PMD_SX, ("apic_restore_state retval %x\n", retval))
+
+ lock_clear(&apic_ioapic_lock);
+ intr_restore(iflag);
+
+
+ /*
+ * restore acpi link device mappings
+ */
+ acpi_restore_link_devices();
+ }
+}
+
+/*
+ * Returns 0 on success
+ */
+int
+apic_state(psm_state_request_t *rp)
+{
+ PMD(PMD_SX, ("apic_state "))
+ switch (rp->psr_cmd) {
+ case PSM_STATE_ALLOC:
+ rp->req.psm_state_req.psr_state =
+ kmem_zalloc(sizeof (struct apic_state), KM_NOSLEEP);
+ if (rp->req.psm_state_req.psr_state == NULL)
+ return (ENOMEM);
+ rp->req.psm_state_req.psr_state_size =
+ sizeof (struct apic_state);
+ PMD(PMD_SX, (":STATE_ALLOC: state %p, size %lx\n",
+ rp->req.psm_state_req.psr_state,
+ rp->req.psm_state_req.psr_state_size))
+ return (0);
+
+ case PSM_STATE_FREE:
+ kmem_free(rp->req.psm_state_req.psr_state,
+ rp->req.psm_state_req.psr_state_size);
+ PMD(PMD_SX, (" STATE_FREE: state %p, size %lx\n",
+ rp->req.psm_state_req.psr_state,
+ rp->req.psm_state_req.psr_state_size))
+ return (0);
+
+ case PSM_STATE_SAVE:
+ PMD(PMD_SX, (" STATE_SAVE: state %p, size %lx\n",
+ rp->req.psm_state_req.psr_state,
+ rp->req.psm_state_req.psr_state_size))
+ apic_save_state(rp->req.psm_state_req.psr_state);
+ return (0);
+
+ case PSM_STATE_RESTORE:
+ apic_restore_state(rp->req.psm_state_req.psr_state);
+ PMD(PMD_SX, (" STATE_RESTORE: state %p, size %lx\n",
+ rp->req.psm_state_req.psr_state,
+ rp->req.psm_state_req.psr_state_size))
+ return (0);
+
+ default:
+ return (EINVAL);
+ }
+}
diff --git a/usr/src/uts/i86pc/io/pci/pci.c b/usr/src/uts/i86pc/io/pci/pci.c
index 1af5ca17fb..7cd0b38d4e 100644
--- a/usr/src/uts/i86pc/io/pci/pci.c
+++ b/usr/src/uts/i86pc/io/pci/pci.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -221,6 +221,16 @@ pci_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
*/
int instance = ddi_get_instance(devi);
pci_state_t *pcip = NULL;
+ switch (cmd) {
+ case DDI_ATTACH:
+ break;
+
+ case DDI_RESUME:
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, "device_type", "pci")
!= DDI_PROP_SUCCESS) {
@@ -285,23 +295,30 @@ pci_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
pci_state_t *pcip;
pcip = ddi_get_soft_state(pci_statep, ddi_get_instance(devi));
- if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE) {
- ddi_fm_handler_unregister(devi);
- pci_ereport_teardown(devi);
- }
- mutex_destroy(&pcip->pci_peek_poke_mutex);
- mutex_destroy(&pcip->pci_err_mutex);
- ddi_fm_fini(devi);
- /* Uninitialize pcitool support. */
- pcitool_uninit(devi);
- /* Uninitialize hotplug support on this bus. */
- (void) pcihp_uninit(devi);
+ switch (cmd) {
+ case DDI_DETACH:
+ if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE) {
+ ddi_fm_handler_unregister(devi);
+ pci_ereport_teardown(devi);
+ }
+ mutex_destroy(&pcip->pci_peek_poke_mutex);
+ mutex_destroy(&pcip->pci_err_mutex);
+ ddi_fm_fini(devi); /* Uninitialize pcitool support. */
+ pcitool_uninit(devi);
- ddi_soft_state_free(pci_statep, instance);
+ /* Uninitialize hotplug support on this bus. */
+ (void) pcihp_uninit(devi);
- return (DDI_SUCCESS);
+ ddi_soft_state_free(pci_statep, instance);
+
+ return (DDI_SUCCESS);
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
}
static int
@@ -510,6 +527,7 @@ pci_ctlops(dev_info_t *dip, dev_info_t *rdip,
int rn;
int totreg;
pci_state_t *pcip;
+ struct attachspec *asp;
switch (ctlop) {
case DDI_CTLOPS_REPORTDEV:
@@ -537,8 +555,8 @@ pci_ctlops(dev_info_t *dip, dev_info_t *rdip,
*(int *)result = 0;
if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
- DDI_PROP_DONTPASS, "reg", (int **)&drv_regp,
- &reglen) != DDI_PROP_SUCCESS) {
+ DDI_PROP_DONTPASS, "reg", (int **)&drv_regp,
+ &reglen) != DDI_PROP_SUCCESS) {
return (DDI_FAILURE);
}
@@ -578,6 +596,21 @@ pci_ctlops(dev_info_t *dip, dev_info_t *rdip,
pci_common_peekpoke, &pcip->pci_err_mutex,
&pcip->pci_peek_poke_mutex));
+ /* for now only X86 systems support PME wakeup from suspended state */
+ case DDI_CTLOPS_ATTACH:
+ asp = (struct attachspec *)arg;
+ if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE)
+ if (pci_pre_resume(rdip) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ return (ddi_ctlops(dip, rdip, ctlop, arg, result));
+
+ case DDI_CTLOPS_DETACH:
+ asp = (struct attachspec *)arg;
+ if (asp->cmd == DDI_SUSPEND && asp->when == DDI_POST)
+ if (pci_post_suspend(rdip) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ return (ddi_ctlops(dip, rdip, ctlop, arg, result));
+
default:
return (ddi_ctlops(dip, rdip, ctlop, arg, result));
}
@@ -679,8 +712,7 @@ pci_initchild(dev_info_t *child)
* Support for the "command-preserve" property.
*/
command_preserve = ddi_prop_get_int(DDI_DEV_T_ANY, child,
- DDI_PROP_DONTPASS,
- "command-preserve", 0);
+ DDI_PROP_DONTPASS, "command-preserve", 0);
command = pci_config_get16(config_handle, PCI_CONF_COMM);
command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB);
command |= (pci_command_default & ~command_preserve);
diff --git a/usr/src/uts/i86pc/io/pciex/npe.c b/usr/src/uts/i86pc/io/pciex/npe.c
index 80ff196f44..fde4276cc7 100644
--- a/usr/src/uts/i86pc/io/pciex/npe.c
+++ b/usr/src/uts/i86pc/io/pciex/npe.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -228,6 +228,9 @@ npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
int instance = ddi_get_instance(devi);
pci_state_t *pcip = NULL;
+ if (cmd == DDI_RESUME)
+ return (DDI_SUCCESS);
+
if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, "device_type",
"pciex") != DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "npe: 'device_type' prop create failed");
@@ -282,20 +285,29 @@ npe_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
pcip = ddi_get_soft_state(npe_statep, ddi_get_instance(devi));
- /* Uninitialize pcitool support. */
- pcitool_uninit(devi);
+ switch (cmd) {
+ case DDI_DETACH:
- /*
- * Uninitialize hotplug support on this bus.
- */
- (void) pcihp_uninit(devi);
+ /* Uninitialize pcitool support. */
+ pcitool_uninit(devi);
- if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE)
- ddi_fm_handler_unregister(devi);
+ /*
+ * Uninitialize hotplug support on this bus.
+ */
+ (void) pcihp_uninit(devi);
- ddi_fm_fini(devi);
- ddi_soft_state_free(npe_statep, instance);
- return (DDI_SUCCESS);
+ if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE)
+ ddi_fm_handler_unregister(devi);
+
+ ddi_fm_fini(devi);
+ ddi_soft_state_free(npe_statep, instance);
+ return (DDI_SUCCESS);
+
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
}
@@ -598,6 +610,7 @@ npe_ctlops(dev_info_t *dip, dev_info_t *rdip,
int totreg;
uint_t reglen;
pci_regspec_t *drv_regp;
+ struct attachspec *asp;
switch (ctlop) {
case DDI_CTLOPS_REPORTDEV:
@@ -662,6 +675,30 @@ npe_ctlops(dev_info_t *dip, dev_info_t *rdip,
case DDI_CTLOPS_POKE:
return (pci_common_peekpoke(dip, rdip, ctlop, arg, result));
+ /* X86 systems support PME wakeup from suspended state */
+ case DDI_CTLOPS_ATTACH:
+ asp = (struct attachspec *)arg;
+ /* only do this for immediate children */
+ if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE &&
+ ddi_get_parent(rdip) == dip)
+ if (pci_pre_resume(rdip) != DDI_SUCCESS) {
+ /* Not good, better stop now. */
+ cmn_err(CE_PANIC,
+ "Couldn't pre-resume device %p",
+ (void *) dip);
+ /* NOTREACHED */
+ }
+ return (ddi_ctlops(dip, rdip, ctlop, arg, result));
+
+ case DDI_CTLOPS_DETACH:
+ asp = (struct attachspec *)arg;
+ /* only do this for immediate children */
+ if (asp->cmd == DDI_SUSPEND && asp->when == DDI_POST &&
+ ddi_get_parent(rdip) == dip)
+ if (pci_post_suspend(rdip) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ return (ddi_ctlops(dip, rdip, ctlop, arg, result));
+
default:
break;
}
diff --git a/usr/src/uts/i86pc/io/pcplusmp/apic.c b/usr/src/uts/i86pc/io/pcplusmp/apic.c
index f31aa2dbcc..e63f2d596a 100644
--- a/usr/src/uts/i86pc/io/pcplusmp/apic.c
+++ b/usr/src/uts/i86pc/io/pcplusmp/apic.c
@@ -31,8 +31,9 @@
* PSMI 1.2 extensions are supported only in 2.7 and later versions.
* PSMI 1.3 and 1.4 extensions are supported in Solaris 10.
* PSMI 1.5 extensions are supported in Solaris Nevada.
+ * PSMI 1.6 extensions are supported in Solaris Nevada.
*/
-#define PSMI_1_5
+#define PSMI_1_6
#include <sys/processor.h>
#include <sys/time.h>
@@ -238,12 +239,13 @@ static struct psm_ops apic_ops = {
apic_timer_disable,
apic_post_cyclic_setup,
apic_preshutdown,
- apic_intr_ops /* Advanced DDI Interrupt framework */
+ apic_intr_ops, /* Advanced DDI Interrupt framework */
+ apic_state, /* save, restore apic state for S3 */
};
static struct psm_info apic_psm_info = {
- PSM_INFO_VER01_5, /* version */
+ PSM_INFO_VER01_6, /* version */
PSM_OWN_EXCLUSIVE, /* ownership */
(struct psm_ops *)&apic_ops, /* operation */
APIC_PCPLUSMP_NAME, /* machine name */
@@ -1371,6 +1373,9 @@ apic_preshutdown(int cmd, int fcn)
APIC_VERBOSE_POWEROFF(("apic_preshutdown(%d,%d); m=%d a=%d\n",
cmd, fcn, apic_poweroff_method, apic_enable_acpi));
+ if ((cmd != A_SHUTDOWN) || (fcn != AD_POWEROFF)) {
+ return;
+ }
}
static void
diff --git a/usr/src/uts/i86pc/io/ppm.conf b/usr/src/uts/i86pc/io/ppm.conf
index 830e94a28a..ea1f555526 100644
--- a/usr/src/uts/i86pc/io/ppm.conf
+++ b/usr/src/uts/i86pc/io/ppm.conf
@@ -34,6 +34,7 @@ name="ppm" parent="pseudo" instance=0;
# the nature of the domain;
#
# "domain_xxx-model" - PM model: CPU
+# "domain_xxx-model" - PM model: SX
#
# "domain_xxx-propname" - a property name that is exported by device in
# a domain. Currently, it is used by PCI_PROP model to identify devices
@@ -58,7 +59,7 @@ name="ppm" parent="pseudo" instance=0;
#
# which keywords apply depends on cmd. There are two sets as shown below.
# Here is the first:
-# cmd=[PCI_ON | PCI_OFF]
+# cmd=[ENTER_S3 | ENTER_S4]
# path=<prompath> - control device's prom pathname (includes minor)
# method=[KIO|I2CKIO] This selects a method which may be
# an ioctl that sets a single value or an i2c ioctl that
@@ -69,7 +70,48 @@ name="ppm" parent="pseudo" instance=0;
# the relevant bits of a register will be set
# mask=<integer> - which bits of val are relevant (if method is I2CKIO)
#
+ppm-domains="domain_cpu" , "domain_estar";
-ppm-domains="domain_cpu";
+#
+# CPU domain
+#
domain_cpu-devices="/cpus/cpu@*";
domain_cpu-model="CPU";
+
+#
+# Estar domain
+# 0x4101 is APPMIOC_ENTER_S3 (('A' << 8) | 1)
+# 0x4102 is APPMIOC_EXIT_S3 (('A' << 8) | 2)
+#
+domain_estar-devices="/";
+domain_estar-model="SX";
+domain_estar-control=
+ "cmd=ENTER_S3 path=/pseudo/acpippm@0:acpi-ppm method=KIO iowr=0x4101",
+ "cmd=EXIT_S3 path=/pseudo/acpippm@0:acpi-ppm method=KIO iowr=0x4102";
+
+#
+# S3-enable whitelist
+#
+S3-support-enable =
+ "Sun Microsystems", "Sun Ultra 40 Workstation",
+ "Sun Microsystems", "Sun Ultra 20 Workstation";
+
+S3-support-disable =
+ "Sun Microsystems", "Sun Blade x8400 Server Module",
+ "Sun Microsystems", "Sun Fire*";
+
+S3-autoenable =
+ "Sun Microsystems", "Sun Ultra 40 Workstation",
+ "Sun Microsystems", "Sun Ultra 20 Workstation";
+
+S3-autodisable =
+ "Sun Microsystems", "Sun Blade x8400 Server Module",
+ "Sun Microsystems", "Sun Fire*";
+
+autopm-enable =
+ "Sun Microsystems", "Sun Ultra 40 Workstation",
+ "Sun Microsystems", "Sun Ultra 20 Workstation";
+
+autopm-disable =
+ "Sun Microsystems", "Sun Blade x8400 Server Module",
+ "Sun Microsystems", "Sun Fire*";
diff --git a/usr/src/uts/i86pc/io/ppm/acpippm.c b/usr/src/uts/i86pc/io/ppm/acpippm.c
new file mode 100644
index 0000000000..a8e5019e50
--- /dev/null
+++ b/usr/src/uts/i86pc/io/ppm/acpippm.c
@@ -0,0 +1,443 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/open.h>
+#include <sys/modctl.h>
+#include <sys/promif.h>
+#include <sys/stat.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/epm.h>
+#include <sys/acpi/acpi.h>
+#include <sys/acpica.h>
+#include <sys/psm_types.h>
+
+/*
+ * ACPI Power Management Driver
+ *
+ * acpippm deals with those bits of ppm functionality that
+ * must be mediated by ACPI
+ *
+ * The routines in this driver is referenced by Platform
+ * Power Management driver of X86 workstation systems.
+ * acpippm driver is loaded because it is listed as a platform driver
+ * It is initially configured as a pseudo driver.
+ */
+
+/*
+ * Configuration Function prototypes and data structures
+ */
+static int appm_attach(dev_info_t *, ddi_attach_cmd_t);
+static int appm_detach(dev_info_t *, ddi_detach_cmd_t);
+static int appm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
+static int appm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
+static int appm_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
+static int appm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+
+/*
+ * Configuration data structures
+ */
+static struct cb_ops appm_cbops = {
+ appm_open, /* open */
+ appm_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ appm_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* chpoll */
+ ddi_prop_op, /* prop_op */
+ NULL, /* stream */
+ D_MP | D_NEW, /* flag */
+ CB_REV, /* rev */
+ nodev, /* aread */
+ nodev, /* awrite */
+};
+
+static struct dev_ops appm_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* refcnt */
+ appm_getinfo, /* getinfo */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ appm_attach, /* attach */
+ appm_detach, /* detach */
+ nodev, /* reset */
+ &appm_cbops, /* cb_ops */
+ NULL, /* bus_ops */
+ NULL /* power */
+};
+
+extern struct mod_ops mod_driverops;
+
+static struct modldrv modldrv = {
+ &mod_driverops,
+ "ACPI ppm driver v1.8",
+ &appm_ops,
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ &modldrv,
+ NULL
+};
+
+/*
+ * Driver state structure
+ */
+typedef struct {
+ dev_info_t *dip;
+ ddi_acc_handle_t devid_hndl;
+ ddi_acc_handle_t estar_hndl;
+ int lyropen; /* ref count */
+} appm_unit;
+
+/*
+ * Driver global variables
+ *
+ * appm_lock synchronize the access of lyr handle to each appm
+ * minor device, therefore write to tomatillo device is
+ * sequentialized. Lyr protocol requires pairing up lyr open
+ * and close, so only a single reference is allowed per minor node.
+ */
+static void *appm_statep;
+static kmutex_t appm_lock;
+
+/*
+ * S3 stuff:
+ */
+char _depends_on[] = "misc/acpica";
+
+extern int acpi_enter_sleepstate(s3a_t *);
+extern int acpi_exit_sleepstate(s3a_t *);
+
+
+int
+_init(void)
+{
+ int error;
+
+ if ((error = ddi_soft_state_init(&appm_statep,
+ sizeof (appm_unit), 0)) != DDI_SUCCESS) {
+ return (error);
+ }
+
+ mutex_init(&appm_lock, NULL, MUTEX_DRIVER, NULL);
+
+ if ((error = mod_install(&modlinkage)) != DDI_SUCCESS) {
+ mutex_destroy(&appm_lock);
+ ddi_soft_state_fini(&appm_statep);
+ return (error);
+ }
+
+ return (error);
+}
+
+int
+_fini(void)
+{
+ int error;
+
+ if ((error = mod_remove(&modlinkage)) == DDI_SUCCESS) {
+ mutex_destroy(&appm_lock);
+ ddi_soft_state_fini(&appm_statep);
+ }
+
+ return (error);
+
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+
+
+/*
+ * Driver attach(9e) entry point
+ */
+static int
+appm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ char *str = "appm_attach";
+ int instance;
+ appm_unit *unitp;
+ int rv = DDI_SUCCESS;
+
+ switch (cmd) {
+ case DDI_ATTACH:
+ break;
+ case DDI_RESUME:
+ return (DDI_SUCCESS);
+ default:
+ cmn_err(CE_WARN, "%s: cmd %d unsupported.\n", str, cmd);
+ return (DDI_FAILURE);
+ }
+
+ instance = ddi_get_instance(dip);
+ rv = ddi_soft_state_zalloc(appm_statep, instance);
+ if (rv != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s: failed alloc for dev(%s@%s)",
+ str, ddi_binding_name(dip),
+ ddi_get_name_addr(dip) ? ddi_get_name_addr(dip) : " ");
+ return (rv);
+ }
+
+ if ((unitp = ddi_get_soft_state(appm_statep, instance)) == NULL) {
+ rv = DDI_FAILURE;
+ goto doerrs;
+ }
+
+ /*
+ * Export "ddi-kernel-ioctl" property - prepared to support
+ * kernel ioctls (driver layering).
+ * XXX is this still needed?
+ * XXXX (RSF) Not that I am aware of.
+ */
+ rv = ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
+ DDI_KERNEL_IOCTL, NULL, 0);
+ if (rv != DDI_PROP_SUCCESS)
+ goto doerrs;
+
+ ddi_report_dev(dip);
+ unitp->dip = dip;
+
+ /*
+ * XXX here we would do whatever we need to to determine if the
+ * XXX platform supports ACPI, and fail the attach if not.
+ * XXX If it does, we do whatever setup is needed to get access to
+ * XXX ACPI register space.
+ */
+
+ unitp->lyropen = 0;
+
+ /*
+ * create minor node for kernel_ioctl calls
+ */
+ rv = ddi_create_minor_node(dip, "acpi-ppm", S_IFCHR, instance, 0, 0);
+ if (rv != DDI_SUCCESS)
+ goto doerrs;
+
+ return (rv);
+
+doerrs:
+
+ if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS |
+ DDI_PROP_NOTPROM, DDI_KERNEL_IOCTL))
+ ddi_prop_remove_all(dip);
+
+ ddi_soft_state_free(appm_statep, instance);
+
+ return (rv);
+}
+
+
+/*
+ * Driver getinfo(9e) entry routine
+ */
+/* ARGSUSED */
+static int
+appm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
+{
+ appm_unit *unitp;
+ int instance;
+
+ switch (cmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ instance = getminor((dev_t)arg);
+ unitp = ddi_get_soft_state(appm_statep, instance);
+ if (unitp == NULL) {
+ return (DDI_FAILURE);
+ }
+ *result = (void *) unitp->dip;
+ return (DDI_SUCCESS);
+
+ case DDI_INFO_DEVT2INSTANCE:
+ instance = getminor((dev_t)arg);
+ *result = (void *)(uintptr_t)instance;
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+
+/*
+ * detach(9e)
+ */
+/* ARGSUSED */
+static int
+appm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ char *str = "appm_detach";
+
+ switch (cmd) {
+ case DDI_DETACH:
+ return (DDI_FAILURE);
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+ default:
+ cmn_err(CE_WARN, "%s: cmd %d unsupported", str, cmd);
+ return (DDI_FAILURE);
+ }
+}
+
+
+/* ARGSUSED */
+static int
+appm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
+{
+ appm_unit *unitp;
+
+ /* not intended to allow sysadmin level root process to open it */
+ if (drv_priv(cred_p) != DDI_SUCCESS)
+ return (EPERM);
+
+ if ((unitp = ddi_get_soft_state(
+ appm_statep, getminor(*dev_p))) == NULL) {
+ cmn_err(CE_WARN, "appm_open: failed to get soft state!");
+ return (DDI_FAILURE);
+ }
+
+ mutex_enter(&appm_lock);
+ if (unitp->lyropen != 0) {
+ mutex_exit(&appm_lock);
+ return (EBUSY);
+ }
+ unitp->lyropen++;
+ mutex_exit(&appm_lock);
+
+ return (DDI_SUCCESS);
+}
+
+
+/* ARGSUSED */
+static int
+appm_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
+{
+ appm_unit *unitp;
+
+ if ((unitp =
+ ddi_get_soft_state(appm_statep, getminor(dev))) == NULL)
+ return (DDI_FAILURE);
+
+ mutex_enter(&appm_lock);
+ unitp->lyropen = 0;
+ mutex_exit(&appm_lock);
+
+ return (DDI_SUCCESS);
+}
+
+
+/*
+ * must match ppm.conf
+ */
+#define APPMIOC ('A' << 8)
+#define APPMIOC_ENTER_S3 (APPMIOC | 1) /* arg *s3a_t */
+#define APPMIOC_EXIT_S3 (APPMIOC | 2) /* arg *s3a_t */
+
+/* ARGSUSED3 */
+static int
+appm_ioctl(dev_t dev, int cmd, intptr_t arg, int flag,
+ cred_t *cred_p, int *rval_p)
+{
+ static boolean_t acpi_initted = B_FALSE;
+ char *str = "appm_ioctl";
+ int ret;
+ s3a_t *s3ap = (s3a_t *)arg;
+
+ PMD(PMD_SX, ("%s: called with %x\n", str, cmd))
+
+ if (drv_priv(cred_p) != 0) {
+ PMD(PMD_SX, ("%s: EPERM\n", str))
+ return (EPERM);
+ }
+
+ if (ddi_get_soft_state(appm_statep, getminor(dev)) == NULL) {
+ PMD(PMD_SX, ("%s: no soft state: EIO\n", str))
+ return (EIO);
+ }
+
+ if (!acpi_initted) {
+ PMD(PMD_SX, ("%s: !acpi_initted\n", str))
+ if (acpica_init() == 0) {
+ acpi_initted = B_TRUE;
+ } else {
+ if (rval_p != NULL) {
+ *rval_p = EINVAL;
+ }
+ PMD(PMD_SX, ("%s: EINVAL\n", str))
+ return (EINVAL);
+ }
+ }
+
+ PMD(PMD_SX, ("%s: looking for cmd %x\n", str, cmd))
+ switch (cmd) {
+ case APPMIOC_ENTER_S3:
+ /*
+ * suspend to RAM (ie S3)
+ */
+ PMD(PMD_SX, ("%s: cmd %x, arg %p\n", str, cmd, (void *)arg))
+ ret = acpi_enter_sleepstate(s3ap);
+ break;
+
+ case APPMIOC_EXIT_S3:
+ /*
+ * return from S3
+ */
+ PMD(PMD_SX, ("%s: cmd %x, arg %p\n", str, cmd, (void *)arg))
+ ret = acpi_exit_sleepstate(s3ap);
+ break;
+
+ default:
+ PMD(PMD_SX, ("%s: cmd %x unrecognized: ENOTTY\n", str, cmd))
+ return (ENOTTY);
+ }
+
+ /*
+ * upon failure return EINVAL
+ */
+ if (ret != 0) {
+ if (rval_p != NULL) {
+ *rval_p = EINVAL;
+ }
+ return (EINVAL);
+ }
+
+ return (0);
+}
diff --git a/usr/src/uts/i86pc/io/ppm/acpippm.h b/usr/src/uts/i86pc/io/ppm/acpippm.h
new file mode 100644
index 0000000000..c60aedfc15
--- /dev/null
+++ b/usr/src/uts/i86pc/io/ppm/acpippm.h
@@ -0,0 +1,41 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_ACPIPPM_H
+#define _SYS_ACPIPPM_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_ACPIPPM_H */
diff --git a/usr/src/uts/i86pc/io/ppm/acpisleep.c b/usr/src/uts/i86pc/io/ppm/acpisleep.c
new file mode 100644
index 0000000000..d1ab6c5c34
--- /dev/null
+++ b/usr/src/uts/i86pc/io/ppm/acpisleep.c
@@ -0,0 +1,214 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/smp_impldefs.h>
+#include <sys/promif.h>
+
+#include <sys/kmem.h>
+#include <sys/archsystm.h>
+#include <sys/cpuvar.h>
+#include <sys/pte.h>
+#include <vm/seg_kmem.h>
+#include <sys/epm.h>
+#include <sys/machsystm.h>
+#include <sys/clock.h>
+
+#include <sys/cpr_wakecode.h>
+#include <sys/acpi/acpi.h>
+
+#ifdef OLDPMCODE
+#include "acpi.h"
+#endif
+
+#include <sys/x86_archext.h>
+#include <sys/reboot.h>
+#include <sys/cpu_module.h>
+#include <sys/kdi.h>
+
+/*
+ * S3 stuff
+ */
+
+int acpi_rtc_wake = 0x0; /* wake in N seconds */
+
+#if 0 /* debug */
+static uint8_t branchbuf[64 * 1024]; /* for the HDT branch trace stuff */
+#endif /* debug */
+
+extern int boothowto;
+
+#define BOOTCPU 0 /* cpu 0 is always the boot cpu */
+
+extern void kernel_wc_code(void);
+extern tod_ops_t *tod_ops;
+extern int flushes_require_xcalls;
+extern int tsc_gethrtime_enable;
+
+extern cpuset_t cpu_ready_set;
+extern void *(*cpu_pause_func)(void *);
+
+/*
+ * This probably belong in apic.c, along with the save/restore stuff.
+ */
+static void
+reinit_picmode(void)
+{
+ ACPI_OBJECT_LIST arglist;
+ ACPI_OBJECT arg;
+ ACPI_STATUS status;
+
+ /* Setup parameter object */
+ arglist.Count = 1;
+ arglist.Pointer = &arg;
+ arg.Type = ACPI_TYPE_INTEGER;
+ arg.Integer.Value = 1;
+
+ status = AcpiEvaluateObject(NULL, "\\_PIC", &arglist, NULL);
+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+ PMD(PMD_SX, ("Method _PIC failed, %d\n", status))
+ }
+}
+
+
+/*
+ * This is what we've all been waiting for!
+ */
+int
+acpi_enter_sleepstate(s3a_t *s3ap)
+{
+ ACPI_PHYSICAL_ADDRESS wakephys = s3ap->s3a_wakephys;
+ caddr_t wakevirt = rm_platter_va;
+ /*LINTED*/
+ wakecode_t *wp = (wakecode_t *)wakevirt;
+ uint_t Sx = s3ap->s3a_state;
+
+ PT(PT_SWV);
+ /* Set waking vector */
+ if (AcpiSetFirmwareWakingVector(wakephys) != AE_OK) {
+ PT(PT_SWV_FAIL);
+ PMD(PMD_SX, ("Can't SetFirmwareWakingVector(%lx)\n",
+ (long)wakephys))
+ goto insomnia;
+ }
+
+ PT(PT_EWE);
+ /* Enable wake events */
+ if (AcpiEnableEvent(ACPI_EVENT_POWER_BUTTON, 0) != AE_OK) {
+ PT(PT_EWE_FAIL);
+ PMD(PMD_SX, ("Can't EnableEvent(POWER_BUTTON)\n"))
+ }
+ if (acpi_rtc_wake > 0) {
+ PT(PT_RTCW);
+ if (AcpiEnableEvent(ACPI_EVENT_RTC, 0) != AE_OK) {
+ PT(PT_RTCW_FAIL);
+ PMD(PMD_SX, ("Can't EnableEvent(RTC)\n"))
+ }
+
+ /*
+ * Set RTC to wake us in a wee while.
+ */
+ mutex_enter(&tod_lock);
+ PT(PT_TOD);
+ TODOP_SETWAKE(tod_ops, acpi_rtc_wake);
+ mutex_exit(&tod_lock);
+ }
+
+ /*
+ * Prepare for sleep ... could've done this earlier?
+ */
+ PT(PT_SXP);
+ PMD(PMD_SX, ("Calling AcpiEnterSleepStatePrep(%d) ...\n", Sx))
+ if (AcpiEnterSleepStatePrep(Sx) != AE_OK) {
+ PMD(PMD_SX, ("... failed\n!"))
+ goto insomnia;
+ }
+
+ switch (s3ap->s3a_test_point) {
+ case DEVICE_SUSPEND_TO_RAM:
+ case LOOP_BACK_PASS:
+ return (0);
+ case LOOP_BACK_FAIL:
+ return (1);
+ default:
+ ASSERT(s3ap->s3a_test_point == LOOP_BACK_NONE ||
+ s3ap->s3a_test_point == FORCE_SUSPEND_TO_RAM);
+ }
+
+ /*
+ * Tell the hardware to sleep.
+ */
+ PT(PT_SXE);
+ PMD(PMD_SX, ("Calling AcpiEnterSleepState(%d) ...\n", Sx))
+ if (AcpiEnterSleepState(Sx) != AE_OK) {
+ PT(PT_SXE_FAIL);
+ PMD(PMD_SX, ("... failed!\n"))
+ }
+
+insomnia:
+ PT(PT_INSOM);
+ /* cleanup is done in the caller */
+ return (1);
+}
+
+int
+acpi_exit_sleepstate(s3a_t *s3ap)
+{
+ int Sx = s3ap->s3a_state;
+
+ PT(PT_WOKE);
+ PMD(PMD_SX, ("!We woke up!\n"))
+
+ PT(PT_LSS);
+ if (AcpiLeaveSleepState(Sx) != AE_OK) {
+ PT(PT_LSS_FAIL);
+ PMD(PMD_SX, ("Problem with LeaveSleepState!\n"))
+ }
+
+ PT(PT_DPB);
+ if (AcpiDisableEvent(ACPI_EVENT_POWER_BUTTON, 0) != AE_OK) {
+ PT(PT_DPB_FAIL);
+ PMD(PMD_SX, ("Problem w/ DisableEvent(POWER_BUTTON)\n"))
+ }
+ if (acpi_rtc_wake > 0 &&
+ AcpiDisableEvent(ACPI_EVENT_RTC, 0) != AE_OK) {
+ PT(PT_DRTC_FAIL);
+ PMD(PMD_SX, ("Problem w/ DisableEvent(RTC)\n"))
+ }
+
+ PMD(PMD_SX, ("Restore state of APICs\n"))
+
+ /* Restore state of APICs */
+ PT(PT_ACPIREINIT);
+ reinit_picmode();
+ PT(PT_ACPIRESTORE);
+
+ PMD(PMD_SX, ("Exiting acpi_sleepstate() => 0\n"))
+
+ return (0);
+}
diff --git a/usr/src/uts/i86pc/io/psm/psm_common.c b/usr/src/uts/i86pc/io/psm/psm_common.c
index fd92327093..bfbcfcb1be 100644
--- a/usr/src/uts/i86pc/io/psm/psm_common.c
+++ b/usr/src/uts/i86pc/io/psm/psm_common.c
@@ -623,9 +623,9 @@ acpi_get_current_irq_resource(acpi_psm_lnk_t *acpipsmlnkp, int *pci_irqp,
}
intr_flagp->intr_el = psm_acpi_edgelevel(
- rp->Data.Irq.Triggering);
+ rp->Data.Irq.Triggering);
intr_flagp->intr_po = psm_acpi_po(
- rp->Data.Irq.Polarity);
+ rp->Data.Irq.Polarity);
irq = rp->Data.Irq.Interrupts[0];
status = ACPI_PSM_SUCCESS;
} else if (rp->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
@@ -644,9 +644,9 @@ acpi_get_current_irq_resource(acpi_psm_lnk_t *acpipsmlnkp, int *pci_irqp,
}
intr_flagp->intr_el = psm_acpi_edgelevel(
- rp->Data.ExtendedIrq.Triggering);
+ rp->Data.ExtendedIrq.Triggering);
intr_flagp->intr_po = psm_acpi_po(
- rp->Data.ExtendedIrq.Polarity);
+ rp->Data.ExtendedIrq.Polarity);
irq = rp->Data.ExtendedIrq.Interrupts[0];
status = ACPI_PSM_SUCCESS;
}
@@ -799,7 +799,7 @@ acpi_get_possible_irq_resources(acpi_psm_lnk_t *acpipsmlnkp,
/* NEEDSWORK: move this into add_irqlist_entry someday */
irqlist = kmem_zalloc(irqlist_len * sizeof (*irqlist),
- KM_SLEEP);
+ KM_SLEEP);
for (i = 0; i < irqlist_len; i++)
if (resp->Type == ACPI_RESOURCE_TYPE_IRQ)
irqlist[i] = ((uint8_t *)tmplist)[i];
@@ -808,7 +808,7 @@ acpi_get_possible_irq_resources(acpi_psm_lnk_t *acpipsmlnkp,
intr_flags.intr_el = psm_acpi_edgelevel(el);
intr_flags.intr_po = psm_acpi_po(po);
acpi_add_irqlist_entry(irqlistp, irqlist, irqlist_len,
- &intr_flags);
+ &intr_flags);
}
AcpiOsFree(rsb.Pointer);
@@ -925,6 +925,32 @@ acpi_get_irq_lnk_cache_ent(ACPI_HANDLE lnkobj, int *pci_irqp,
return (ret);
}
+/*
+ * Walk the irq_cache_table and re-configure the link device to
+ * the saved state.
+ */
+void
+acpi_restore_link_devices(void)
+{
+ irq_cache_t *irqcachep;
+ acpi_psm_lnk_t psmlnk;
+ int i, status;
+
+ /* XXX: may not need to hold this mutex */
+ mutex_enter(&acpi_irq_cache_mutex);
+ for (irqcachep = irq_cache_table, i = 0; i < irq_cache_valid;
+ irqcachep++, i++) {
+ /* only field used from psmlnk in set_irq is lnkobj */
+ psmlnk.lnkobj = irqcachep->lnkobj;
+ status = acpi_set_irq_resource(&psmlnk, irqcachep->irq);
+ /* warn if set_irq failed; soldier on */
+ if (status != ACPI_PSM_SUCCESS)
+ cmn_err(CE_WARN, "restore_link failed for IRQ 0x%x\n",
+ irqcachep->irq);
+ }
+ mutex_exit(&acpi_irq_cache_mutex);
+}
+
int
acpi_poweroff(void)
{
diff --git a/usr/src/uts/i86pc/io/psm/uppc.c b/usr/src/uts/i86pc/io/psm/uppc.c
index 51181b0415..2fd3f860c5 100644
--- a/usr/src/uts/i86pc/io/psm/uppc.c
+++ b/usr/src/uts/i86pc/io/psm/uppc.c
@@ -25,7 +25,7 @@
#pragma ident "%Z%%M% %I% %E% SMI"
-#define PSMI_1_5
+#define PSMI_1_6
#include <sys/mutex.h>
#include <sys/types.h>
diff --git a/usr/src/uts/i86pc/io/todpc_subr.c b/usr/src/uts/i86pc/io/todpc_subr.c
index 7e55876af3..5b92cb9077 100644
--- a/usr/src/uts/i86pc/io/todpc_subr.c
+++ b/usr/src/uts/i86pc/io/todpc_subr.c
@@ -47,13 +47,79 @@
#include <sys/stat.h>
#include <sys/sunddi.h>
+#include <sys/acpi/acpi.h>
+#include <sys/acpica.h>
+
static int todpc_rtcget(unsigned char *buf);
static void todpc_rtcput(unsigned char *buf);
+#define CLOCK_RES 1000 /* 1 microsec in nanosecs */
+
+int clock_res = CLOCK_RES;
+
+/*
+ * The minimum sleep time till an alarm can be fired.
+ * This can be tuned in /etc/system, but if the value is too small,
+ * there is a danger that it will be missed if it takes too long to
+ * get from the set point to sleep. Or that it can fire quickly, and
+ * generate a power spike on the hardware. And small values are
+ * probably only usefull for test setups.
+ */
+int clock_min_alarm = 4;
+
/*
* Machine-dependent clock routines.
*/
+extern long gmt_lag;
+
+struct rtc_offset {
+ int8_t loaded;
+ uint8_t day_alrm;
+ uint8_t mon_alrm;
+ uint8_t century;
+};
+
+static struct rtc_offset pc_rtc_offset = {0, 0, 0, 0};
+
+
+/*
+ * Entry point for ACPI to pass RTC or other clock values that
+ * are useful to TOD.
+ */
+void
+pc_tod_set_rtc_offsets(FADT_DESCRIPTOR *fadt) {
+ int ok = 0;
+
+ /*
+ * ASSERT is for debugging, but we don't want the machine
+ * falling over because for some reason we didn't get a valid
+ * pointer.
+ */
+ ASSERT(fadt);
+ if (fadt == NULL) {
+ return;
+ }
+
+ if (fadt->DayAlrm) {
+ pc_rtc_offset.day_alrm = fadt->DayAlrm;
+ ok = 1;
+ }
+
+ if (fadt->MonAlrm) {
+ pc_rtc_offset.mon_alrm = fadt->MonAlrm;
+ ok = 1;
+ }
+
+ if (fadt->Century) {
+ pc_rtc_offset.century = fadt->Century;
+ ok = 1;
+ }
+
+ pc_rtc_offset.loaded = ok;
+}
+
+
/*
* Write the specified time into the clock chip.
* Must be called with tod_lock held.
@@ -123,7 +189,7 @@ todpc_get(tod_ops_t *top)
if (tod.tod_year < 69) {
if (range_warn && tod.tod_year > 38) {
cmn_err(CE_WARN, "hardware real-time clock is out "
- "of range -- time needs to be reset");
+ "of range -- time needs to be reset");
range_warn = 0;
}
tod.tod_year += 100 + YRBASE; /* 20xx year */
@@ -135,12 +201,12 @@ todpc_get(tod_ops_t *top)
}
if (century_warn && BCD_TO_BYTE(rtc.rtc_century) != compute_century) {
cmn_err(CE_NOTE,
- "The hardware real-time clock appears to have the "
- "wrong century: %d.\nSolaris will still operate "
- "correctly, but other OS's/firmware agents may "
- "not.\nUse date(1) to set the date to the current "
- "time to correct the RTC.",
- BCD_TO_BYTE(rtc.rtc_century));
+ "The hardware real-time clock appears to have the "
+ "wrong century: %d.\nSolaris will still operate "
+ "correctly, but other OS's/firmware agents may "
+ "not.\nUse date(1) to set the date to the current "
+ "time to correct the RTC.",
+ BCD_TO_BYTE(rtc.rtc_century));
century_warn = 0;
}
tod.tod_month = BCD_TO_BYTE(rtc.rtc_mon);
@@ -156,6 +222,109 @@ todpc_get(tod_ops_t *top)
return (ts);
}
+#include <sys/promif.h>
+/*
+ * Write the specified wakeup alarm into the clock chip.
+ * Must be called with tod_lock held.
+ */
+void
+/*ARGSUSED*/
+todpc_setalarm(tod_ops_t *top, int nsecs)
+{
+ struct rtc_t rtc;
+ int delta, asec, amin, ahr, adom, amon;
+ int day_alrm = pc_rtc_offset.day_alrm;
+ int mon_alrm = pc_rtc_offset.mon_alrm;
+
+ ASSERT(MUTEX_HELD(&tod_lock));
+
+ /* A delay of zero is not allowed */
+ if (nsecs == 0)
+ return;
+
+ /* Make sure that we delay no less than the minimum time */
+ if (nsecs < clock_min_alarm)
+ nsecs = clock_min_alarm;
+
+ if (todpc_rtcget((unsigned char *)&rtc))
+ return;
+
+ /*
+ * Compute alarm secs, mins and hrs, and where appropriate, dom
+ * and mon. rtc bytes are in binary-coded decimal, so we have
+ * to convert.
+ */
+ delta = nsecs + BCD_TO_BYTE(rtc.rtc_sec);
+ asec = delta % 60;
+
+ delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_min);
+ amin = delta % 60;
+
+ delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_hr);
+ ahr = delta % 24;
+
+ if (day_alrm == 0 && delta >= 24) {
+ prom_printf("No day alarm - set to end of today!\n");
+ asec = 59;
+ amin = 59;
+ ahr = 23;
+ } else {
+ int mon = BCD_TO_BYTE(rtc.rtc_mon);
+ static int dpm[] =
+ {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+ adom = (delta / 24) + BCD_TO_BYTE(rtc.rtc_dom);
+
+ if (mon_alrm == 0) {
+ if (adom > dpm[mon]) {
+ prom_printf("No mon alarm - "
+ "set to end of current month!\n");
+ asec = 59;
+ amin = 59;
+ ahr = 23;
+ adom = dpm[mon];
+ }
+ } else {
+ for (amon = mon;
+ amon <= 12 && adom > dpm[amon]; amon++) {
+ adom -= dpm[amon];
+ }
+ if (amon > 12) {
+ prom_printf("Alarm too far in future - "
+ "set to end of current year!\n");
+ asec = 59;
+ amin = 59;
+ ahr = 23;
+ adom = dpm[12];
+ amon = 12;
+ }
+ rtc.rtc_amon = BYTE_TO_BCD(amon);
+ }
+
+ rtc.rtc_adom = BYTE_TO_BCD(adom);
+ }
+
+ rtc.rtc_asec = BYTE_TO_BCD(asec);
+ rtc.rtc_amin = BYTE_TO_BCD(amin);
+ rtc.rtc_ahr = BYTE_TO_BCD(ahr);
+
+ rtc.rtc_statusb |= RTC_AIE; /* Enable alarm interrupt */
+
+ todpc_rtcput((unsigned char *)&rtc);
+}
+
+/*
+ * Clear an alarm. This is effectively setting an alarm of 0.
+ */
+void
+/*ARGSUSED*/
+todpc_clralarm(tod_ops_t *top)
+{
+ mutex_enter(&tod_lock);
+ todpc_setalarm(top, 0);
+ mutex_exit(&tod_lock);
+}
+
/*
* Routine to read contents of real time clock to the specified buffer.
* Returns ENXIO if clock not valid, or EAGAIN if clock data cannot be read
@@ -176,9 +345,18 @@ todpc_rtcget(unsigned char *buf)
int i;
int retries = 256;
unsigned char *rawp;
+ unsigned char century = RTC_CENTURY;
+ unsigned char day_alrm;
+ unsigned char mon_alrm;
ASSERT(MUTEX_HELD(&tod_lock));
+ day_alrm = pc_rtc_offset.day_alrm;
+ mon_alrm = pc_rtc_offset.mon_alrm;
+ if (pc_rtc_offset.century != 0) {
+ century = pc_rtc_offset.century;
+ }
+
outb(RTC_ADDR, RTC_D); /* check if clock valid */
reg = inb(RTC_DATA);
if ((reg & RTC_VRT) == 0)
@@ -198,8 +376,18 @@ checkuip:
outb(RTC_ADDR, i);
*rawp++ = inb(RTC_DATA);
}
- outb(RTC_ADDR, RTC_CENTURY); /* do century */
+ outb(RTC_ADDR, century); /* do century */
((struct rtc_t *)buf)->rtc_century = inb(RTC_DATA);
+
+ if (day_alrm > 0) {
+ outb(RTC_ADDR, day_alrm);
+ ((struct rtc_t *)buf)->rtc_adom = inb(RTC_DATA) & 0x3f;
+ }
+ if (mon_alrm > 0) {
+ outb(RTC_ADDR, mon_alrm);
+ ((struct rtc_t *)buf)->rtc_amon = inb(RTC_DATA);
+ }
+
outb(RTC_ADDR, 0); /* re-read Seconds register */
reg = inb(RTC_DATA);
if (reg != ((struct rtc_t *)buf)->rtc_sec ||
@@ -221,6 +409,13 @@ todpc_rtcput(unsigned char *buf)
{
unsigned char reg;
int i;
+ unsigned char century = RTC_CENTURY;
+ unsigned char day_alrm = pc_rtc_offset.day_alrm;
+ unsigned char mon_alrm = pc_rtc_offset.mon_alrm;
+
+ if (pc_rtc_offset.century != 0) {
+ century = pc_rtc_offset.century;
+ }
outb(RTC_ADDR, RTC_B);
reg = inb(RTC_DATA);
@@ -230,8 +425,20 @@ todpc_rtcput(unsigned char *buf)
outb(RTC_ADDR, i);
outb(RTC_DATA, buf[i]);
}
- outb(RTC_ADDR, RTC_CENTURY); /* do century */
+ outb(RTC_ADDR, century); /* do century */
outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_century);
+
+ if (day_alrm > 0) {
+ outb(RTC_ADDR, day_alrm);
+ outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_adom);
+ }
+ if (mon_alrm > 0) {
+ outb(RTC_ADDR, mon_alrm);
+ outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_amon);
+ }
+
+ outb(RTC_ADDR, RTC_B);
+ reg = inb(RTC_DATA);
outb(RTC_ADDR, RTC_B);
outb(RTC_DATA, reg & ~RTC_SET); /* allow time update */
}
@@ -240,6 +447,10 @@ static tod_ops_t todpc_ops = {
TOD_OPS_VERSION,
todpc_get,
todpc_set,
+ NULL,
+ NULL,
+ todpc_setalarm,
+ todpc_clralarm,
NULL
};
diff --git a/usr/src/uts/i86pc/ml/cpr_wakecode.s b/usr/src/uts/i86pc/ml/cpr_wakecode.s
new file mode 100644
index 0000000000..93dffd9c51
--- /dev/null
+++ b/usr/src/uts/i86pc/ml/cpr_wakecode.s
@@ -0,0 +1,1033 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/asm_linkage.h>
+#include <sys/asm_misc.h>
+#include <sys/regset.h>
+#include <sys/privregs.h>
+#include <sys/x86_archext.h>
+#include <sys/cpr_wakecode.h>
+
+#if !defined(__lint)
+#include <sys/segments.h>
+#include "assym.h"
+#endif
+
+/*
+ * This file contains the low level routines involved in getting
+ * into and out of ACPI S3, including those needed for restarting
+ * the non-boot cpus.
+ *
+ * Our assumptions:
+ *
+ * Our actions:
+ *
+ */
+
+#if defined(lint) || defined(__lint)
+
+/*ARGSUSED*/
+int
+wc_save_context(wc_cpu_t *pcpu)
+{ return 0; }
+
+#else /* lint */
+
+#if defined(__GNU_AS__)
+
+ NOTHING AT ALL YET!
+
+#else /* !defined(__GNU_AS__) */
+
+#if defined(__amd64)
+
+ ENTRY_NP(wc_save_context)
+
+ movq (%rsp), %rdx / return address
+ movq %rdx, WC_RETADDR(%rdi)
+
+/*
+ * C calling convention with no local variables, just use 1st arg ie %rdi
+ * and the registers. Therefore push caller's fp, set out fp to be sp and
+ * push %r12, %r13 %r14. At function end unwind this by: popping %r14, %r13
+ * %r14, restore the sp from fp and pop caller's fp.
+ */
+
+ pushq %rbp
+ movq %rsp,%rbp
+ pushq %r12
+ pushq %r13
+ pushq %r14
+
+ movq %rdi, WC_VIRTADDR(%rdi)
+ movq %rdi, WC_RDI(%rdi)
+
+ movq %rdx, WC_RDX(%rdi)
+
+/ stash everything else we need
+ sgdt WC_GDT(%rdi)
+ sidt WC_IDT(%rdi)
+ sldt WC_LDT(%rdi)
+ str WC_TR(%rdi)
+
+ movq %cr0, %rdx
+ movq %rdx, WC_CR0(%rdi)
+ movq %cr3, %rdx
+ movq %rdx, WC_CR3(%rdi)
+ movq %cr4, %rdx
+ movq %rdx, WC_CR4(%rdi)
+ movq %cr8, %rdx
+ movq %rdx, WC_CR8(%rdi)
+
+ movq %r8, WC_R8(%rdi)
+ movq %r9, WC_R9(%rdi)
+ movq %r10, WC_R10(%rdi)
+ movq %r11, WC_R11(%rdi)
+ movq %r12, WC_R12(%rdi)
+ movq %r13, WC_R13(%rdi)
+ movq %r14, WC_R14(%rdi)
+ movq %r15, WC_R15(%rdi)
+ movq %rax, WC_RAX(%rdi)
+ movq %rbp, WC_RBP(%rdi)
+ movq %rbx, WC_RBX(%rdi)
+ movq %rcx, WC_RCX(%rdi)
+ movq %rsi, WC_RSI(%rdi)
+ movq %rsp, WC_RSP(%rdi)
+
+ movw %ss, WC_SS(%rdi)
+ movw %cs, WC_CS(%rdi)
+ movw %ds, WC_DS(%rdi)
+ movw %es, WC_ES(%rdi)
+
+ movq $0, %rcx / save %fs register
+ movw %fs, %cx
+ movq %rcx, WC_FS(%rdi)
+
+ movl $MSR_AMD_FSBASE, %ecx
+ rdmsr
+ movl %eax, WC_FSBASE(%rdi)
+ movl %edx, WC_FSBASE+4(%rdi)
+
+ movq $0, %rcx / save %gs register
+ movw %gs, %cx
+ movq %rcx, WC_GS(%rdi)
+
+ movl $MSR_AMD_GSBASE, %ecx / save gsbase msr
+ rdmsr
+ movl %eax, WC_GSBASE(%rdi)
+ movl %edx, WC_GSBASE+4(%rdi)
+
+ movl $MSR_AMD_KGSBASE, %ecx / save kgsbase msr
+ rdmsr
+ movl %eax, WC_KGSBASE(%rdi)
+ movl %edx, WC_KGSBASE+4(%rdi)
+
+ pushfq
+ popq WC_EFLAGS(%rdi)
+
+/*
+ * call save_stack(cpup)
+ * NB %rdi is the first arguemnt to both wc_save_context() and save_stack()
+ * so it must not be modified during either of these calls.
+ * The pushq will decrement the value of %rsp
+ * we need to push the %rbp because it is the frame pointer and we need
+ * to use the C calling convention
+ */
+
+ pushq %rbp
+ call *save_stack_func
+ popq %rbp
+
+ wbinvd / flush the cache
+
+ movq $1, %rax / at suspend return 1
+
+/ see comment at function enter
+ popq %r14
+ popq %r13
+ popq %r12
+ leave
+
+ ret
+
+ SET_SIZE(wc_save_context)
+
+#elif defined(__i386)
+
+ ENTRY_NP(wc_save_context)
+
+ movl 4(%esp), %eax / wc_cpu_t *
+ movl %eax, WC_VIRTADDR(%eax)
+
+ movl (%esp), %edx / return address
+ movl %edx, WC_RETADDR(%eax)
+
+ str WC_TR(%eax) / stash everything else we need
+ sgdt WC_GDT(%eax)
+ sldt WC_LDT(%eax)
+ sidt WC_IDT(%eax)
+
+ movl %cr0, %edx
+ movl %edx, WC_CR0(%eax)
+ movl %cr3, %edx
+ movl %edx, WC_CR3(%eax)
+ movl %cr4, %edx
+ movl %edx, WC_CR4(%eax)
+
+ movl %ebx, WC_EBX(%eax)
+ movl %edi, WC_EDI(%eax)
+ movl %esi, WC_ESI(%eax)
+ movl %ebp, WC_EBP(%eax)
+ movl %esp, WC_ESP(%eax)
+
+ movw %ss, WC_SS(%eax)
+ movw %cs, WC_CS(%eax)
+ movw %ds, WC_DS(%eax)
+ movw %es, WC_ES(%eax)
+ movw %fs, WC_FS(%eax)
+ movw %gs, WC_GS(%eax)
+
+ pushfl
+ popl WC_EFLAGS(%eax)
+
+ wbinvd / flush the cache
+
+ movl $1, %eax / at suspend return 1
+ ret
+
+ SET_SIZE(wc_save_context)
+
+#endif /* __amd64 */
+
+#endif /* __GNU_AS__ */
+
+#endif /* lint */
+
+
+/*
+ * Our assumptions:
+ * - We are running in real mode.
+ * - Interrupts are disabled.
+ *
+ * Our actions:
+ * - We start using our GDT by loading correct values in the
+ * selector registers (cs=KCS_SEL, ds=es=ss=KDS_SEL, fs=KFS_SEL,
+ * gs=KGS_SEL).
+ * - We change over to using our IDT.
+ * - We load the default LDT into the hardware LDT register.
+ * - We load the default TSS into the hardware task register.
+ * - We restore registers
+ * - We return to original caller (a la setjmp)
+ */
+
+#if defined(lint) || defined(__lint)
+
+void
+wc_rm_start(void)
+{}
+
+void
+wc_rm_end(void)
+{}
+
+#else /* lint */
+
+#if defined(__GNU_AS__)
+
+ NOTHING AT ALL YET!
+
+#else /* __GNU_AS__ */
+
+#if defined(__amd64)
+
+ ENTRY_NP(wc_rm_start)
+
+ /*
+ * For vulcan as we need to do a .code32 and mentally invert the
+ * meaning of the addr16 and data16 prefixes to get 32-bit access when
+ * generating code to be executed in 16-bit mode (sigh...)
+ */
+
+ .code32
+
+ cli
+ movw %cs, %ax
+ movw %ax, %ds / establish ds ...
+ movw %ax, %ss / ... and ss:esp
+ D16 movl $WC_STKSTART, %esp
+/ using the following value blows up machines! - DO NOT USE
+/ D16 movl 0xffc, %esp
+
+#define LED 1
+#define SERIAL 1
+
+#if LED
+ D16 movl $0x80, %edx
+ D16 movb $0xd1, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x61, %al
+ outb (%dx)
+#endif
+
+ D16 call cominit
+
+ /*
+ * Enable protected-mode, write protect, and alignment mask
+ * %cr0 has already been initialsed to zero
+ */
+ movl %cr0, %eax
+ D16 orl $[CR0_PE|CR0_WP|CR0_AM], %eax
+ movl %eax, %cr0
+
+ /*
+ * Do a jmp immediately after writing to cr0 when enabling protected
+ * mode to clear the real mode prefetch queue (per Intel's docs)
+ */
+ jmp pestart
+pestart:
+
+#if LED
+ D16 movl $0x80, %edx
+ D16 movb $0xd2, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x62, %al
+ outb (%dx)
+#endif
+
+ /*
+ * 16-bit protected mode is now active, so prepare to turn on long
+ * mode
+ */
+
+#if LED
+ D16 movl $0x80, %edx
+ D16 movb $0xd3, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x63, %al
+ outb (%dx)
+#endif
+
+ /*
+ * Add any initial cr4 bits
+ */
+ movl %cr4, %eax
+ A16 D16 orl CR4OFF, %eax
+
+ /*
+ * Enable PAE mode (CR4.PAE)
+ */
+ D16 orl $CR4_PAE, %eax
+ movl %eax, %cr4
+
+#if LED
+ D16 movl $0x80, %edx
+ D16 movb $0xd4, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x64, %al
+ outb (%dx)
+#endif
+
+ /*
+ * Point cr3 to the 64-bit long mode page tables.
+ *
+ * Note that these MUST exist in 32-bit space, as we don't have
+ * a way to load %cr3 with a 64-bit base address for the page tables
+ * until the CPU is actually executing in 64-bit long mode.
+ */
+ A16 D16 movl CR3OFF, %eax
+ movl %eax, %cr3
+
+ /*
+ * Set long mode enable in EFER (EFER.LME = 1)
+ */
+ D16 movl $MSR_AMD_EFER, %ecx
+ rdmsr
+
+ D16 orl $AMD_EFER_LME, %eax
+ wrmsr
+
+#if LED
+ D16 movl $0x80, %edx
+ D16 movb $0xd5, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x65, %al
+ outb (%dx)
+#endif
+
+ /*
+ * Finally, turn on paging (CR0.PG = 1) to activate long mode.
+ */
+ movl %cr0, %eax
+ D16 orl $CR0_PG, %eax
+ movl %eax, %cr0
+
+ /*
+ * The instruction after enabling paging in CR0 MUST be a branch.
+ */
+ jmp long_mode_active
+
+long_mode_active:
+
+#if LED
+ D16 movl $0x80, %edx
+ D16 movb $0xd6, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x66, %al
+ outb (%dx)
+#endif
+
+ /*
+ * Long mode is now active but since we're still running with the
+ * original 16-bit CS we're actually in 16-bit compatability mode.
+ *
+ * We have to load an intermediate GDT and IDT here that we know are
+ * in 32-bit space before we can use the kernel's GDT and IDT, which
+ * may be in the 64-bit address space, and since we're in compatability
+ * mode, we only have access to 16 and 32-bit instructions at the
+ * moment.
+ */
+ A16 D16 lgdt TEMPGDTOFF /* load temporary GDT */
+ A16 D16 lidt TEMPIDTOFF /* load temporary IDT */
+
+
+ /*
+ * Do a far transfer to 64-bit mode. Set the CS selector to a 64-bit
+ * long mode selector (CS.L=1) in the temporary 32-bit GDT and jump
+ * to the real mode platter address of wc_long_mode_64 as until the
+ * 64-bit CS is in place we don't have access to 64-bit instructions
+ * and thus can't reference a 64-bit %rip.
+ */
+
+#if LED
+ D16 movl $0x80, %edx
+ D16 movb $0xd7, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x67, %al
+ outb (%dx)
+#endif
+
+ D16 pushl $TEMP_CS64_SEL
+ A16 D16 pushl LM64OFF
+
+ D16 lret
+
+
+/*
+ * Support routine to re-initialize VGA subsystem
+ */
+vgainit:
+ D16 ret
+
+/*
+ * Support routine to re-initialize keyboard (which is USB - help!)
+ */
+kbdinit:
+ D16 ret
+
+/*
+ * Support routine to re-initialize COM ports to something sane
+ */
+cominit:
+ / init COM1 & COM2
+ xorl %edx, %edx / select COM1
+ D16 movl $0xe3, %eax / ah=0; al=(9600bd|8_bit|nopar)
+ int $0x14
+ D16 movl $1, %edx / select COM2
+ D16 movl $0xe3, %eax / ah=0; al=(9600bd|8_bit|nopar)
+ int $0x14
+ D16 ret
+
+ .code64
+/*
+ * Support routine to re-initialize COM ports to something sane
+ */
+cominit64:
+ / init COM1 & COM2
+ xorq %rdx, %rdx / select COM1
+ movq $0xe3, %rax / ah=0; al=(9600bd|8_bit|nopar)
+ int $0x14
+ movq $1, %rdx / select COM2
+ movq $0xe3, %rax / ah=0; al=(9600bd|8_bit|nopar)
+ int $0x14
+ ret
+
+ .globl wc_long_mode_64
+wc_long_mode_64:
+
+#if LED
+ movw $0x80, %dx
+ movb $0xd8, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ movw $0x3f8, %dx
+ movb $0x68, %al
+ outb (%dx)
+#endif
+
+ /*
+ * We are now running in long mode with a 64-bit CS (EFER.LMA=1,
+ * CS.L=1) so we now have access to 64-bit instructions.
+ *
+ * First, set the 64-bit GDT base.
+ */
+ .globl rm_platter_pa
+ movl rm_platter_pa, %eax
+
+ lgdtq GDTROFF(%rax) /* load 64-bit GDT */
+
+ /*
+ * Save the CPU number in %r11; get the value here since it's saved in
+ * the real mode platter.
+ */
+/ JAN
+/ the following is wrong! need to figure out MP systems
+/ movl CPUNOFF(%rax), %r11d
+
+ /*
+ * Add rm_platter_pa to %rsp to point it to the same location as seen
+ * from 64-bit mode.
+ */
+ addq %rax, %rsp
+
+ /*
+ * Now do an lretq to load CS with the appropriate selector for the
+ * kernel's 64-bit GDT and to start executing 64-bit setup code at the
+ * virtual address where boot originally loaded this code rather than
+ * the copy in the real mode platter's rm_code array as we've been
+ * doing so far.
+ */
+
+#if LED
+ movw $0x80, %dx
+ movb $0xd9, %al
+ outb (%dx)
+#endif
+
+/ JAN this should produce 'i' but we get 'g' instead ???
+#if SERIAL
+ movw $0x3f8, %dx
+ movb $0x69, %al
+ outb (%dx)
+#endif
+
+ pushq $KCS_SEL
+ pushq $kernel_wc_code
+ lretq
+
+ .globl kernel_wc_code
+kernel_wc_code:
+
+#if LED
+ movw $0x80, %dx
+ movb $0xda, %al
+ outb (%dx)
+#endif
+
+/ JAN this should produce 'j' but we get 'g' instead ???
+#if SERIAL
+ movw $0x3f8, %dx
+ movb $0x6a, %al
+ outb (%dx)
+#endif
+
+ /*
+ * Complete the balance of the setup we need to before executing
+ * 64-bit kernel code (namely init rsp, TSS, LGDT, FS and GS).
+ */
+ .globl rm_platter_va
+ movq rm_platter_va, %rbx
+ addq $WC_CPU, %rbx
+
+#if LED
+ movw $0x80, %dx
+ movb $0xdb, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ movw $0x3f8, %dx
+ movw $0x6b, %ax
+ outb (%dx)
+#endif
+
+ /*
+ * restore the rest of the registers
+ */
+
+ lidtq WC_IDT(%rbx)
+
+#if LED
+ movw $0x80, %dx
+ movb $0xdc, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ movw $0x3f8, %dx
+ movw $0x6c, %ax
+ outb (%dx)
+#endif
+
+ /*
+ * restore the rest of the registers
+ */
+
+ movw $KDS_SEL, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %ss
+
+ /*
+ * Before proceeding, enable usage of the page table NX bit if
+ * that's how the page tables are set up.
+ */
+ movl x86_feature, %ecx
+ andl $X86_NX, %ecx
+ jz 1f
+ movl $MSR_AMD_EFER, %ecx
+ rdmsr
+ orl $AMD_EFER_NXE, %eax
+ wrmsr
+1:
+
+ movq WC_CR4(%rbx), %rax / restore full cr4 (with Global Enable)
+ movq %rax, %cr4
+
+ lldt WC_LDT(%rbx)
+ movzwq WC_TR(%rbx), %rax / clear TSS busy bit
+ addq WC_GDT+2(%rbx), %rax
+ andl $0xfffffdff, 4(%rax)
+ movq 4(%rax), %rcx
+ ltr WC_TR(%rbx)
+
+#if LED
+ movw $0x80, %dx
+ movb $0xdd, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ movw $0x3f8, %dx
+ movw $0x6d, %ax
+ outb (%dx)
+#endif
+
+/ restore %fsbase %gsbase %kgbase registers using wrmsr instruction
+
+ movq WC_FS(%rbx), %rcx / restore fs register
+ movw %cx, %fs
+
+ movl $MSR_AMD_FSBASE, %ecx
+ movl WC_FSBASE(%rbx), %eax
+ movl WC_FSBASE+4(%rbx), %edx
+ wrmsr
+
+ movq WC_GS(%rbx), %rcx / restore gs register
+ movw %cx, %gs
+
+ movl $MSR_AMD_GSBASE, %ecx / restore gsbase msr
+ movl WC_GSBASE(%rbx), %eax
+ movl WC_GSBASE+4(%rbx), %edx
+ wrmsr
+
+ movl $MSR_AMD_KGSBASE, %ecx / restore kgsbase msr
+ movl WC_KGSBASE(%rbx), %eax
+ movl WC_KGSBASE+4(%rbx), %edx
+ wrmsr
+
+ movq WC_CR0(%rbx), %rdx
+ movq %rdx, %cr0
+ movq WC_CR3(%rbx), %rdx
+ movq %rdx, %cr3
+ movq WC_CR8(%rbx), %rdx
+ movq %rdx, %cr8
+
+#if LED
+ movw $0x80, %dx
+ movb $0xde, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ movw $0x3f8, %dx
+ movb $0x6e, %al
+ outb (%dx)
+#endif
+
+/ dummy up a stck so we can make C function calls
+ movq WC_RSP(%rbx), %rsp
+
+ /*
+ * APIC initialization (we dummy up a stack so we can make this call)
+ */
+ pushq $0 /* null frame pointer terminates stack trace */
+ movq %rsp, %rbp /* stack aligned on 16-byte boundary */
+
+ call *ap_mlsetup
+
+ call *cpr_start_cpu_func
+
+/ restore %rbx to the value it ahd before we called the functions above
+ movq rm_platter_va, %rbx
+ addq $WC_CPU, %rbx
+
+ movq WC_R8(%rbx), %r8
+ movq WC_R9(%rbx), %r9
+ movq WC_R10(%rbx), %r10
+ movq WC_R11(%rbx), %r11
+ movq WC_R12(%rbx), %r12
+ movq WC_R13(%rbx), %r13
+ movq WC_R14(%rbx), %r14
+ movq WC_R15(%rbx), %r15
+/ movq WC_RAX(%rbx), %rax
+ movq WC_RBP(%rbx), %rbp
+ movq WC_RCX(%rbx), %rcx
+/ movq WC_RDX(%rbx), %rdx
+ movq WC_RDI(%rbx), %rdi
+ movq WC_RSI(%rbx), %rsi
+
+
+/ assume that %cs does not need to be restored
+/ %ds, %es & %ss are ignored in 64bit mode
+ movw WC_SS(%rbx), %ss
+ movw WC_DS(%rbx), %ds
+ movw WC_ES(%rbx), %es
+
+#if LED
+ movw $0x80, %dx
+ movb $0xdf, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ movw $0x3f8, %dx
+ movb $0x6f, %al
+ outb (%dx)
+#endif
+
+
+ movq WC_RBP(%rbx), %rbp
+ movq WC_RSP(%rbx), %rsp
+
+#if LED
+ movw $0x80, %dx
+ movb $0xe0, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ movw $0x3f8, %dx
+ movb $0x70, %al
+ outb (%dx)
+#endif
+
+
+ movq WC_RCX(%rbx), %rcx
+
+ pushq WC_EFLAGS(%rbx) / restore flags
+ popfq
+
+#if LED
+ movw $0x80, %dx
+ movb $0xe1, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ movw $0x3f8, %dx
+ movb $0x71, %al
+ outb (%dx)
+#endif
+
+/*
+ * can not use outb after this point, because doing so would mean using
+ * %dx which would modify %rdx which is restored here
+ */
+
+ movq %rbx, %rax
+ movq WC_RDX(%rax), %rdx
+ movq WC_RBX(%rax), %rbx
+
+ popq %r14
+ popq %r13
+ popq %r12
+ leave
+
+ movq WC_RETADDR(%rax), %rax
+ movq %rax, (%rsp) / return to caller of wc_save_context
+
+ xorl %eax, %eax / at wakeup return 0
+ ret
+
+
+ SET_SIZE(wc_rm_start)
+
+ ENTRY_NP(asmspin)
+
+ movl %edi, %ecx
+A1:
+ loop A1
+
+ SET_SIZE(asmspin)
+
+ .globl wc_rm_end
+wc_rm_end:
+ nop
+
+#elif defined(__i386)
+
+ ENTRY_NP(wc_rm_start)
+
+/entry: jmp entry / stop here for HDT
+
+ cli
+ movw %cs, %ax
+ movw %ax, %ds / establish ds ...
+ movw %ax, %ss / ... and ss:esp
+ D16 movl $WC_STKSTART, %esp
+
+#define LED 1
+#define SERIAL 1
+
+#if LED
+ D16 movl $0x80, %edx
+ D16 movb $0xd1, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x61, %al
+ outb (%dx)
+#endif
+
+
+ D16 call vgainit
+ D16 call kbdinit
+ D16 call cominit
+
+#if LED
+ D16 movl $0x80, %edx
+ D16 movb $0xd2, %al
+ outb (%dx)
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x62, %al
+ outb (%dx)
+#endif
+
+ D16 A16 movl $WC_CPU, %ebx / base add of wc_cpu_t
+
+#if LED
+ D16 movb $0xd3, %al
+ outb $0x80
+#endif
+
+#if SERIAL
+ D16 movl $0x3f8, %edx
+ D16 movb $0x63, %al
+ outb (%dx)
+#endif
+
+ D16 A16 movl %cs:WC_DS(%ebx), %edx / %ds post prot/paging transit
+
+ D16 movb $0xd4, %al
+ outb $0x80
+
+ D16 A16 lgdt %cs:WC_GDT(%ebx) / restore gdt and idtr
+ D16 A16 lidt %cs:WC_IDT(%ebx)
+
+ D16 movb $0xd5, %al
+ outb $0x80
+
+ D16 A16 movl %cs:WC_CR4(%ebx), %eax / restore cr4
+ D16 andl $-1!CR4_PGE, %eax / don't set Global Enable yet
+ movl %eax, %cr4
+
+ D16 movb $0xd6, %al
+ outb $0x80
+
+ D16 A16 movl %cs:WC_CR3(%ebx), %eax / set PDPT
+ movl %eax, %cr3
+
+ D16 movb $0xd7, %al
+ outb $0x80
+
+ D16 A16 movl %cs:WC_CR0(%ebx), %eax / enable prot/paging, etc.
+ movl %eax, %cr0
+
+ D16 movb $0xd8, %al
+ outb $0x80
+
+ D16 A16 movl %cs:WC_VIRTADDR(%ebx), %ebx / virtaddr of wc_cpu_t
+
+ D16 movb $0xd9, %al
+ outb $0x80
+
+ D16 movb $0xda, %al
+ outb $0x80
+ jmp flush / flush prefetch queue
+flush:
+ D16 pushl $KCS_SEL
+ D16 pushl $kernel_wc_code
+ D16 lret / re-appear at kernel_wc_code
+
+
+/*
+ * Support routine to re-initialize VGA subsystem
+ */
+vgainit:
+ D16 ret
+
+/*
+ * Support routine to re-initialize keyboard (which is USB - help!)
+ */
+kbdinit:
+ D16 ret
+
+/*
+ * Support routine to re-initialize COM ports to something sane for debug output
+ */
+cominit:
+ / init COM1 & COM2
+ xorl %edx, %edx / select COM1
+ D16 movl $0xe3, %eax / ah=0; al=(9600bd|8_bit|nopar)
+ int $0x14
+ D16 movl $1, %edx / select COM2
+ D16 movl $0xe3, %eax / ah=0; al=(9600bd|8_bit|nopar)
+ int $0x14
+ D16 ret
+
+ .globl wc_rm_end
+wc_rm_end:
+ nop
+
+ .globl kernel_wc_code
+kernel_wc_code:
+ / At this point we are with kernel's cs and proper eip.
+ / We will be executing not from the copy in real mode platter,
+ / but from the original code where boot loaded us.
+ / By this time GDT and IDT are loaded as is cr0, cr3 and cr4.
+ / %ebx is wc_cpu
+ / %dx is our ds
+
+ D16 movb $0xdb, %al
+ outb $0x80
+
+/ got here OK
+
+ movw %dx, %ds / $KDS_SEL
+ movb $0xdc, %al
+ outb $0x80
+
+ movl $MSR_AMD_EFER, %ecx / re-enable NX bit
+ rdmsr
+ orl $AMD_EFER_NXE, %eax
+ wrmsr
+
+ movl WC_CR4(%ebx), %eax / restore full cr4 (with Global Enable)
+ movl %eax, %cr4
+
+
+ lldt WC_LDT(%ebx) / $LDT_SEL
+
+ movzwl WC_TR(%ebx), %eax / clear TSS busy bit
+ addl WC_GDT+2(%ebx), %eax
+ andl $-1!0x200, 4(%eax)
+ ltr WC_TR(%ebx) / $UTSS_SEL
+
+ movw WC_SS(%ebx), %ss / lssl WC_ESP(%ebx), %esp
+ movl WC_ESP(%ebx), %esp / ^ don't use, asm busted!
+
+ movl WC_RETADDR(%ebx), %eax / return to caller of wc_save_context
+ movl %eax, (%esp)
+
+ movw WC_ES(%ebx), %es / restore segment registers
+ movw WC_FS(%ebx), %fs
+ movw WC_GS(%ebx), %gs
+
+ /*
+ * APIC initialization
+ */
+ call *ap_mlsetup
+
+ call *cpr_start_cpu_func
+
+ pushl WC_EFLAGS(%ebx) / restore flags
+ popfl
+
+ movl WC_EDI(%ebx), %edi / restore general registers
+ movl WC_ESI(%ebx), %esi
+ movl WC_EBP(%ebx), %ebp
+ movl WC_EBX(%ebx), %ebx
+
+/exit: jmp exit / stop here for HDT
+
+ xorl %eax, %eax / at wakeup return 0
+ ret
+
+ SET_SIZE(wc_rm_start)
+
+
+#endif /* defined(__amd64) */
+
+#endif /* !defined(__GNU_AS__) */
+
+#endif /* lint */
+
diff --git a/usr/src/uts/i86pc/ml/offsets.in b/usr/src/uts/i86pc/ml/offsets.in
index 7219b7a02f..eff25dbcc2 100644
--- a/usr/src/uts/i86pc/ml/offsets.in
+++ b/usr/src/uts/i86pc/ml/offsets.in
@@ -60,6 +60,7 @@
#include <sys/lgrp.h>
#include <sys/dtrace.h>
#include <sys/brand.h>
+#include <sys/cpr_wakecode.h>
proc PROCSIZE
p_link
@@ -387,3 +388,52 @@ brand
zone
zone_brand_data
+
+wc_cpu WC_CPU_SIZE
+ wc_retaddr
+ wc_virtaddr
+ wc_cr0
+ wc_cr3
+ wc_cr4
+ wc_cr8
+ wc_fs
+ wc_fsbase
+ wc_gs
+ wc_gsbase
+ wc_kgsbase
+ wc_r8
+ wc_r9
+ wc_r10
+ wc_r11
+ wc_r12
+ wc_r13
+ wc_r14
+ wc_r15
+ wc_rax
+ wc_rbp
+ wc_rbx
+ wc_rcx
+ wc_rdi
+ wc_rdx
+ wc_rsi
+ wc_rsp
+ wc_gdt_limit WC_GDT
+ wc_gdt_base
+ wc_idt_limit WC_IDT
+ wc_idt_base
+ wc_tr
+ wc_ldt
+ wc_eflags
+ wc_ebx
+ wc_edi
+ wc_esi
+ wc_ebp
+ wc_esp
+ wc_esp
+ wc_ss
+ wc_cs
+ wc_ds
+ wc_es
+
+wc_wakecode
+ wc_cpu
diff --git a/usr/src/uts/i86pc/os/acpi_stubs.c b/usr/src/uts/i86pc/os/acpi_stubs.c
new file mode 100644
index 0000000000..5f3f71556f
--- /dev/null
+++ b/usr/src/uts/i86pc/os/acpi_stubs.c
@@ -0,0 +1,52 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+
+#include <sys/acpi/acpi.h>
+#include <sys/acpica.h>
+
+/*
+ * This file contains ACPI functions that are needed by the kernel before
+ * the ACPI module is loaded. Any functions or definitions need to be
+ * able to deal with the possibility that ACPI doesn't get loaded, or
+ * doesn't contain the required method.
+ */
+
+int (*acpi_fp_setwake)();
+
+/*
+ *
+ */
+int
+acpi_ddi_setwake(dev_info_t *dip, int level)
+{
+ if (acpi_fp_setwake == NULL)
+ return (AE_ERROR);
+
+ return ((*acpi_fp_setwake)(dip, level));
+}
diff --git a/usr/src/uts/i86pc/os/cpr_impl.c b/usr/src/uts/i86pc/os/cpr_impl.c
new file mode 100644
index 0000000000..939df37716
--- /dev/null
+++ b/usr/src/uts/i86pc/os/cpr_impl.c
@@ -0,0 +1,1096 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Platform specific implementation code
+ * Currently only suspend to RAM is supported (ACPI S3)
+ */
+
+#define SUNDDI_IMPL
+
+#include <sys/types.h>
+#include <sys/promif.h>
+#include <sys/prom_isa.h>
+#include <sys/prom_plat.h>
+#include <sys/cpuvar.h>
+#include <sys/pte.h>
+#include <vm/hat.h>
+#include <vm/page.h>
+#include <vm/as.h>
+#include <sys/cpr.h>
+#include <sys/kmem.h>
+#include <sys/clock.h>
+#include <sys/kmem.h>
+#include <sys/panic.h>
+#include <vm/seg_kmem.h>
+#include <sys/cpu_module.h>
+#include <sys/callb.h>
+#include <sys/machsystm.h>
+#include <sys/vmsystm.h>
+#include <sys/systm.h>
+#include <sys/archsystm.h>
+#include <sys/stack.h>
+#include <sys/fs/ufs_fs.h>
+#include <sys/memlist.h>
+#include <sys/bootconf.h>
+#include <sys/thread.h>
+#include <sys/x_call.h>
+#include <sys/smp_impldefs.h>
+#include <vm/vm_dep.h>
+#include <sys/psm.h>
+#include <sys/epm.h>
+#include <sys/cpr_wakecode.h>
+#include <sys/x86_archext.h>
+#include <sys/reboot.h>
+#include <sys/acpi/acpi.h>
+#include <sys/acpica.h>
+
+#define AFMT "%lx"
+
+extern int flushes_require_xcalls;
+extern cpuset_t cpu_ready_set;
+
+#if defined(__amd64)
+extern void *wc_long_mode_64(void);
+#endif /* __amd64 */
+extern int tsc_gethrtime_enable;
+extern void i_cpr_start_cpu(void);
+
+ushort_t cpr_mach_type = CPR_MACHTYPE_X86;
+void (*cpr_start_cpu_func)(void) = i_cpr_start_cpu;
+
+static wc_cpu_t *wc_other_cpus = NULL;
+static cpuset_t procset = 1;
+
+static void
+init_real_mode_platter(int cpun, uint32_t offset, uint_t cr4, wc_desctbr_t gdt);
+
+static int i_cpr_platform_alloc(psm_state_request_t *req);
+static void i_cpr_platform_free(psm_state_request_t *req);
+static int i_cpr_save_apic(psm_state_request_t *req);
+static int i_cpr_restore_apic(psm_state_request_t *req);
+
+#if defined(__amd64)
+static void restore_stack(wc_cpu_t *cpup);
+static void save_stack(wc_cpu_t *cpup);
+void (*save_stack_func)(wc_cpu_t *) = save_stack;
+#endif /* __amd64 */
+
+/*
+ * restart paused slave cpus
+ */
+void
+i_cpr_machdep_setup(void)
+{
+ if (ncpus > 1) {
+ CPR_DEBUG(CPR_DEBUG1, ("MP restarted...\n"));
+ mutex_enter(&cpu_lock);
+ start_cpus();
+ mutex_exit(&cpu_lock);
+ }
+}
+
+
+/*
+ * Stop all interrupt activities in the system
+ */
+void
+i_cpr_stop_intr(void)
+{
+ (void) spl7();
+}
+
+/*
+ * Set machine up to take interrupts
+ */
+void
+i_cpr_enable_intr(void)
+{
+ (void) spl0();
+}
+
+/*
+ * Save miscellaneous information which needs to be written to the
+ * state file. This information is required to re-initialize
+ * kernel/prom handshaking.
+ */
+void
+i_cpr_save_machdep_info(void)
+{
+ int notcalled = 0;
+ ASSERT(notcalled);
+}
+
+
+void
+i_cpr_set_tbr(void)
+{
+}
+
+
+processorid_t
+i_cpr_bootcpuid(void)
+{
+ return (0);
+}
+
+/*
+ * cpu0 should contain bootcpu info
+ */
+cpu_t *
+i_cpr_bootcpu(void)
+{
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ return (cpu_get(i_cpr_bootcpuid()));
+}
+
+/*
+ * Save context for the specified CPU
+ */
+void *
+i_cpr_save_context(void *arg)
+{
+ long index = (long)arg;
+ psm_state_request_t *papic_state;
+ int resuming;
+ int ret;
+
+ PMD(PMD_SX, ("i_cpr_save_context() index = %ld\n", index))
+
+ ASSERT(index < NCPU);
+
+ papic_state = &(wc_other_cpus + index)->wc_apic_state;
+
+ ret = i_cpr_platform_alloc(papic_state);
+ ASSERT(ret == 0);
+
+ ret = i_cpr_save_apic(papic_state);
+ ASSERT(ret == 0);
+
+ /*
+ * wc_save_context returns twice, once when susending and
+ * once when resuming, wc_save_context() returns 0 when
+ * suspending and non-zero upon resume
+ */
+ resuming = (wc_save_context(wc_other_cpus + index) == 0);
+
+ PMD(PMD_SX, ("i_cpr_save_context: wc_save_context returns %d\n",
+ resuming))
+
+ /*
+ * do NOT call any functions after this point, because doing so
+ * will modify the stack that we are running on
+ */
+
+ if (resuming) {
+
+ ret = i_cpr_restore_apic(papic_state);
+ ASSERT(ret == 0);
+
+ i_cpr_platform_free(papic_state);
+
+ /*
+ * Setting the bit in cpu_ready_set must be the last operation
+ * in processor initialization; the boot CPU will continue to
+ * boot once it sees this bit set for all active CPUs.
+ */
+ CPUSET_ATOMIC_ADD(cpu_ready_set, CPU->cpu_id);
+
+ PMD(PMD_SX,
+ ("cpu_release() cpu_ready_set = %lx, CPU->cpu_id = %d\n",
+ cpu_ready_set, CPU->cpu_id))
+ }
+ return (NULL);
+}
+
+static ushort_t *warm_reset_vector = NULL;
+
+static ushort_t *
+map_warm_reset_vector()
+{
+ /*LINTED*/
+ if (!(warm_reset_vector = (ushort_t *)psm_map_phys(WARM_RESET_VECTOR,
+ sizeof (ushort_t *), PROT_READ|PROT_WRITE)))
+ return (NULL);
+
+ /*
+ * setup secondary cpu bios boot up vector
+ */
+ *warm_reset_vector = (ushort_t)((caddr_t)
+ /*LINTED*/
+ ((struct rm_platter *)rm_platter_va)->rm_code - rm_platter_va
+ + ((ulong_t)rm_platter_va & 0xf));
+ warm_reset_vector++;
+ *warm_reset_vector = (ushort_t)(rm_platter_pa >> 4);
+
+ --warm_reset_vector;
+ return (warm_reset_vector);
+}
+
+void
+i_cpr_pre_resume_cpus()
+{
+ /*
+ * this is a cut down version of start_other_cpus()
+ * just do the initialization to wake the other cpus
+ */
+ unsigned who;
+ int cpuid = i_cpr_bootcpuid();
+ int started_cpu;
+ uint32_t code_length = 0;
+ caddr_t wakevirt = rm_platter_va;
+ /*LINTED*/
+ wakecode_t *wp = (wakecode_t *)wakevirt;
+ char *str = "i_cpr_pre_resume_cpus";
+ extern int get_tsc_ready();
+ int err;
+
+ /*LINTED*/
+ rm_platter_t *real_mode_platter = (rm_platter_t *)rm_platter_va;
+
+ /*
+ * Copy the real mode code at "real_mode_start" to the
+ * page at rm_platter_va.
+ */
+ warm_reset_vector = map_warm_reset_vector();
+ if (warm_reset_vector == NULL) {
+ PMD(PMD_SX, ("i_cpr_pre_resume_cpus() returning #2\n"))
+ return;
+ }
+
+ flushes_require_xcalls = 1;
+
+ /*
+ * We lock our affinity to the master CPU to ensure that all slave CPUs
+ * do their TSC syncs with the same CPU.
+ */
+
+ affinity_set(CPU_CURRENT);
+
+ cpu_ready_set = 0;
+
+ for (who = 0; who < ncpus; who++) {
+
+ wc_cpu_t *cpup = wc_other_cpus + who;
+ wc_desctbr_t gdt;
+
+ if (who == cpuid)
+ continue;
+
+ if (!CPU_IN_SET(mp_cpus, who))
+ continue;
+
+ PMD(PMD_SX, ("%s() waking up %d cpu\n", str, who))
+
+ bcopy(cpup, &(wp->wc_cpu), sizeof (wc_cpu_t));
+
+ gdt.base = cpup->wc_gdt_base;
+ gdt.limit = cpup->wc_gdt_limit;
+
+#if defined(__amd64)
+ code_length = (uint32_t)wc_long_mode_64 - (uint32_t)wc_rm_start;
+#else
+ code_length = 0;
+#endif
+
+ init_real_mode_platter(who, code_length, cpup->wc_cr4, gdt);
+
+ started_cpu = 1;
+
+ if ((err = mach_cpuid_start(who, rm_platter_va)) != 0) {
+ cmn_err(CE_WARN, "cpu%d: failed to start during "
+ "suspend/resume error %d", who, err);
+ continue;
+ }
+
+ PMD(PMD_SX, ("%s() #1 waiting for procset 0x%lx\n", str,
+ (ulong_t)procset))
+
+/*
+ * This conditional compile only affects the MP case.
+ */
+#ifdef MP_PM
+ for (delays = 0; !CPU_IN_SET(procset, who); delays++) {
+ if (delays == 500) {
+ /*
+ * After five seconds, things are probably
+ * looking a bit bleak - explain the hang.
+ */
+ cmn_err(CE_NOTE, "cpu%d: started, "
+ "but not running in the kernel yet", who);
+ PMD(PMD_SX, ("%s() %d cpu started "
+ "but not running in the kernel yet\n",
+ str, who))
+ } else if (delays > 2000) {
+ /*
+ * We waited at least 20 seconds, bail ..
+ */
+ cmn_err(CE_WARN, "cpu%d: timed out", who);
+ PMD(PMD_SX, ("%s() %d cpu timed out\n",
+ str, who))
+ started_cpu = 0;
+ }
+
+ /*
+ * wait at least 10ms, then check again..
+ */
+ delay(USEC_TO_TICK_ROUNDUP(10000));
+ }
+#else
+ while (!CPU_IN_SET(procset, who)) {
+ ;
+ }
+
+#endif /* MP_PM */
+
+ PMD(PMD_SX, ("%s() %d cpu started\n", str, who))
+
+ if (!started_cpu)
+ continue;
+
+ PMD(PMD_SX, ("%s() tsc_ready = %d\n", str,
+ get_tsc_ready()))
+
+ if (tsc_gethrtime_enable) {
+ PMD(PMD_SX, ("%s() calling tsc_sync_master\n", str))
+ tsc_sync_master(who);
+ }
+
+
+ PMD(PMD_SX, ("%s() waiting for cpu_ready_set %ld\n", str,
+ cpu_ready_set))
+ /*
+ * Wait for cpu to declare that it is ready, we want the
+ * cpus to start serially instead of in parallel, so that
+ * they do not contend with each other in wc_rm_start()
+ */
+ while (!CPU_IN_SET(cpu_ready_set, who)) {
+ PMD(PMD_SX, ("%s() waiting for "
+ "cpu_ready_set %ld\n", str, cpu_ready_set))
+ ;
+ }
+
+ /*
+ * do not need to re-initialize dtrace using dtrace_cpu_init
+ * function
+ */
+ PMD(PMD_SX, ("%s() cpu %d now ready\n", str, who))
+ }
+
+ affinity_clear();
+
+ PMD(PMD_SX, ("%s() all cpus now ready\n", str))
+}
+
+static void
+unmap_warm_reset_vector(ushort_t *warm_reset_vector)
+{
+ psm_unmap_phys((caddr_t)warm_reset_vector, sizeof (ushort_t *));
+}
+
+/*
+ * We need to setup a 1:1 (virtual to physical) mapping for the
+ * page containing the wakeup code.
+ */
+static struct as *save_as; /* when switching to kas */
+
+static void
+unmap_wakeaddr_1to1(uint64_t wakephys)
+{
+ uintptr_t wp = (uintptr_t)wakephys;
+ hat_setup(save_as->a_hat, 0); /* switch back from kernel hat */
+ hat_unload(kas.a_hat, (caddr_t)wp, PAGESIZE, HAT_UNLOAD);
+}
+
+void
+i_cpr_post_resume_cpus()
+{
+ uint64_t wakephys = rm_platter_pa;
+
+ if (warm_reset_vector != NULL)
+ unmap_warm_reset_vector(warm_reset_vector);
+
+ hat_unload(kas.a_hat, (caddr_t)(uintptr_t)rm_platter_pa, MMU_PAGESIZE,
+ HAT_UNLOAD);
+
+ /*
+ * cmi_post_mpstartup() is only required upon boot not upon
+ * resume from RAM
+ */
+
+ PT(PT_UNDO1to1);
+ /* Tear down 1:1 mapping for wakeup code */
+ unmap_wakeaddr_1to1(wakephys);
+}
+
+/* ARGSUSED */
+void
+i_cpr_handle_xc(int flag)
+{
+}
+
+int
+i_cpr_reusable_supported(void)
+{
+ return (0);
+}
+static void
+map_wakeaddr_1to1(uint64_t wakephys)
+{
+ uintptr_t wp = (uintptr_t)wakephys;
+ hat_devload(kas.a_hat, (caddr_t)wp, PAGESIZE, btop(wakephys),
+ (PROT_READ|PROT_WRITE|PROT_EXEC|HAT_STORECACHING_OK|HAT_NOSYNC),
+ HAT_LOAD);
+ save_as = curthread->t_procp->p_as;
+ hat_setup(kas.a_hat, 0); /* switch to kernel-only hat */
+}
+
+
+void
+prt_other_cpus()
+{
+ int who;
+
+ if (ncpus == 1) {
+ PMD(PMD_SX, ("prt_other_cpus() other cpu table empty for "
+ "uniprocessor machine\n"))
+ return;
+ }
+
+ for (who = 0; who < ncpus; who++) {
+
+ wc_cpu_t *cpup = wc_other_cpus + who;
+
+ PMD(PMD_SX, ("prt_other_cpus() who = %d, gdt=%p:%x, "
+ "idt=%p:%x, ldt=%lx, tr=%lx, kgsbase="
+ AFMT ", sp=%lx\n", who,
+ (void *)cpup->wc_gdt_base, cpup->wc_gdt_limit,
+ (void *)cpup->wc_idt_base, cpup->wc_idt_limit,
+ (long)cpup->wc_ldt, (long)cpup->wc_tr,
+ (long)cpup->wc_kgsbase, (long)cpup->wc_rsp))
+ }
+}
+
+/*
+ * Power down the system.
+ */
+int
+i_cpr_power_down(int sleeptype)
+{
+ caddr_t wakevirt = rm_platter_va;
+ uint64_t wakephys = rm_platter_pa;
+ uint_t saved_intr;
+ uint32_t code_length = 0;
+ wc_desctbr_t gdt;
+ /*LINTED*/
+ wakecode_t *wp = (wakecode_t *)wakevirt;
+ /*LINTED*/
+ rm_platter_t *wcpp = (rm_platter_t *)wakevirt;
+ wc_cpu_t *cpup = &(wp->wc_cpu);
+ dev_info_t *ppm;
+ int ret = 0;
+ power_req_t power_req;
+ char *str = "i_cpr_power_down";
+#if defined(__amd64)
+ /*LINTED*/
+ rm_platter_t *real_mode_platter = (rm_platter_t *)rm_platter_va;
+#endif
+ extern int cpr_suspend_succeeded;
+ extern void kernel_wc_code();
+ extern ulong_t intr_clear(void);
+ extern void intr_restore(ulong_t);
+
+ ASSERT(sleeptype == CPR_TORAM);
+ ASSERT(CPU->cpu_id == 0);
+
+ if ((ppm = PPM(ddi_root_node())) == NULL) {
+ PMD(PMD_SX, ("%s: root node not claimed\n", str))
+ return (ENOTTY);
+ }
+
+ PMD(PMD_SX, ("Entering %s()\n", str))
+
+ PT(PT_IC);
+ saved_intr = intr_clear();
+
+ PT(PT_1to1);
+ /* Setup 1:1 mapping for wakeup code */
+ map_wakeaddr_1to1(wakephys);
+
+ PMD(PMD_SX, ("ncpus=%d\n", ncpus))
+
+ PMD(PMD_SX, ("wc_rm_end - wc_rm_start=%lx WC_CODESIZE=%x\n",
+ ((size_t)((uint_t)wc_rm_end - (uint_t)wc_rm_start)), WC_CODESIZE))
+
+ PMD(PMD_SX, ("wakevirt=%p, wakephys=%x\n",
+ (void *)wakevirt, (uint_t)wakephys))
+
+ ASSERT(((size_t)((uint_t)wc_rm_end - (uint_t)wc_rm_start)) <
+ WC_CODESIZE);
+
+ bzero(wakevirt, PAGESIZE);
+
+ /* Copy code to rm_platter */
+ bcopy((caddr_t)wc_rm_start, wakevirt,
+ (size_t)((uint_t)wc_rm_end - (uint_t)wc_rm_start));
+
+ prt_other_cpus();
+
+#if defined(__amd64)
+
+ PMD(PMD_SX, ("real_mode_platter->rm_cr4=%lx, getcr4()=%lx\n",
+ (ulong_t)real_mode_platter->rm_cr4, (ulong_t)getcr4()))
+ PMD(PMD_SX, ("real_mode_platter->rm_pdbr=%lx, getcr3()=%lx\n",
+ (ulong_t)real_mode_platter->rm_pdbr, getcr3()))
+
+ real_mode_platter->rm_cr4 = getcr4();
+ real_mode_platter->rm_pdbr = getcr3();
+
+ rmp_gdt_init(real_mode_platter);
+
+ /*
+ * Since the CPU needs to jump to protected mode using an identity
+ * mapped address, we need to calculate it here.
+ */
+ real_mode_platter->rm_longmode64_addr = rm_platter_pa +
+ ((uint32_t)wc_long_mode_64 - (uint32_t)wc_rm_start);
+
+ PMD(PMD_SX, ("real_mode_platter->rm_cr4=%lx, getcr4()=%lx\n",
+ (ulong_t)real_mode_platter->rm_cr4, getcr4()))
+
+ PMD(PMD_SX, ("real_mode_platter->rm_pdbr=%lx, getcr3()=%lx\n",
+ (ulong_t)real_mode_platter->rm_pdbr, getcr3()))
+
+ PMD(PMD_SX, ("real_mode_platter->rm_longmode64_addr=%lx\n",
+ (ulong_t)real_mode_platter->rm_longmode64_addr))
+
+#endif
+
+ PMD(PMD_SX, ("mp_cpus=%lx\n", (ulong_t)mp_cpus))
+
+ PT(PT_SC);
+ if (wc_save_context(cpup)) {
+
+ ret = i_cpr_platform_alloc(&(wc_other_cpus->wc_apic_state));
+ if (ret != 0)
+ return (ret);
+
+ ret = i_cpr_save_apic(&(wc_other_cpus->wc_apic_state));
+ PMD(PMD_SX, ("%s: i_cpr_save_apic() returned %d\n", str, ret))
+ if (ret != 0)
+ return (ret);
+
+ PMD(PMD_SX, ("wakephys=%x, kernel_wc_code=%p\n",
+ (uint_t)wakephys, (void *)&kernel_wc_code))
+ PMD(PMD_SX, ("virtaddr=%lx, retaddr=%lx\n",
+ (long)cpup->wc_virtaddr, (long)cpup->wc_retaddr))
+ PMD(PMD_SX, ("ebx=%x, edi=%x, esi=%x, ebp=%x, esp=%x\n",
+ cpup->wc_ebx, cpup->wc_edi, cpup->wc_esi, cpup->wc_ebp,
+ cpup->wc_esp))
+ PMD(PMD_SX, ("cr0=%lx, cr3=%lx, cr4=%lx\n",
+ (long)cpup->wc_cr0, (long)cpup->wc_cr3,
+ (long)cpup->wc_cr4))
+ PMD(PMD_SX, ("cs=%x, ds=%x, es=%x, ss=%x, fs=%lx, gs=%lx, "
+ "flgs=%lx\n", cpup->wc_cs, cpup->wc_ds, cpup->wc_es,
+ cpup->wc_ss, (long)cpup->wc_fs, (long)cpup->wc_gs,
+ (long)cpup->wc_eflags))
+
+ PMD(PMD_SX, ("gdt=%p:%x, idt=%p:%x, ldt=%lx, tr=%lx, "
+ "kgbase=%lx\n", (void *)cpup->wc_gdt_base,
+ cpup->wc_gdt_limit, (void *)cpup->wc_idt_base,
+ cpup->wc_idt_limit, (long)cpup->wc_ldt,
+ (long)cpup->wc_tr, (long)cpup->wc_kgsbase))
+
+ gdt.base = cpup->wc_gdt_base;
+ gdt.limit = cpup->wc_gdt_limit;
+
+#if defined(__amd64)
+ code_length = (uint32_t)wc_long_mode_64 -
+ (uint32_t)wc_rm_start;
+#else
+ code_length = 0;
+#endif
+
+ init_real_mode_platter(0, code_length, cpup->wc_cr4, gdt);
+
+#if defined(__amd64)
+ PMD(PMD_SX, ("real_mode_platter->rm_cr4=%lx, getcr4()=%lx\n",
+ (ulong_t)wcpp->rm_cr4, getcr4()))
+
+ PMD(PMD_SX, ("real_mode_platter->rm_pdbr=%lx, getcr3()=%lx\n",
+ (ulong_t)wcpp->rm_pdbr, getcr3()))
+
+ PMD(PMD_SX, ("real_mode_platter->rm_longmode64_addr=%lx\n",
+ (ulong_t)wcpp->rm_longmode64_addr))
+
+ PMD(PMD_SX,
+ ("real_mode_platter->rm_temp_gdt[TEMPGDT_KCODE64]=%lx\n",
+ (ulong_t)wcpp->rm_temp_gdt[TEMPGDT_KCODE64]))
+#endif
+
+ PMD(PMD_SX, ("gdt=%p:%x, idt=%p:%x, ldt=%lx, tr=%lx, "
+ "kgsbase=%lx\n", (void *)wcpp->rm_gdt_base,
+ wcpp->rm_gdt_lim, (void *)wcpp->rm_idt_base,
+ wcpp->rm_idt_lim, (long)cpup->wc_ldt, (long)cpup->wc_tr,
+ (long)cpup->wc_kgsbase))
+
+ power_req.request_type = PMR_PPM_ENTER_SX;
+ power_req.req.ppm_power_enter_sx_req.sx_state = S3;
+ power_req.req.ppm_power_enter_sx_req.test_point =
+ cpr_test_point;
+ power_req.req.ppm_power_enter_sx_req.wakephys = wakephys;
+
+ PMD(PMD_SX, ("%s: pm_ctlops PMR_PPM_ENTER_SX\n", str))
+ PT(PT_PPMCTLOP);
+ (void) pm_ctlops(ppm, ddi_root_node(), DDI_CTLOPS_POWER,
+ &power_req, &ret);
+ PMD(PMD_SX, ("%s: returns %d\n", str, ret))
+
+ /*
+ * If it works, we get control back to the else branch below
+ * If we get control back here, it didn't work.
+ * XXX return EINVAL here?
+ */
+
+ unmap_wakeaddr_1to1(wakephys);
+ intr_restore(saved_intr);
+
+ return (ret);
+ } else {
+ cpr_suspend_succeeded = 1;
+
+ power_req.request_type = PMR_PPM_EXIT_SX;
+ power_req.req.ppm_power_enter_sx_req.sx_state = S3;
+
+ PMD(PMD_SX, ("%s: pm_ctlops PMR_PPM_EXIT_SX\n", str))
+ PT(PT_PPMCTLOP);
+ (void) pm_ctlops(ppm, ddi_root_node(), DDI_CTLOPS_POWER,
+ &power_req, &ret);
+ PMD(PMD_SX, ("%s: returns %d\n", str, ret))
+
+ ret = i_cpr_restore_apic(&(wc_other_cpus->wc_apic_state));
+ /*
+ * the restore should never fail, if the saved suceeded
+ */
+ ASSERT(ret == 0);
+
+ i_cpr_platform_free(&(wc_other_cpus->wc_apic_state));
+
+ PT(PT_INTRRESTORE);
+ intr_restore(saved_intr);
+ PT(PT_CPU);
+
+ return (ret);
+ }
+}
+
+/*
+ * Stop all other cpu's before halting or rebooting. We pause the cpu's
+ * instead of sending a cross call.
+ * Stolen from sun4/os/mp_states.c
+ */
+
+static int cpu_are_paused; /* sic */
+
+void
+i_cpr_stop_other_cpus(void)
+{
+ mutex_enter(&cpu_lock);
+ if (cpu_are_paused) {
+ mutex_exit(&cpu_lock);
+ return;
+ }
+ pause_cpus(NULL);
+ cpu_are_paused = 1;
+
+ mutex_exit(&cpu_lock);
+}
+
+int
+i_cpr_is_supported(int sleeptype)
+{
+ extern int cpr_supported_override;
+ extern int cpr_platform_enable;
+ extern int pm_S3_enabled;
+
+ if (sleeptype != CPR_TORAM)
+ return (0);
+
+ /*
+ * The next statement tests if a specific platform has turned off
+ * cpr support.
+ */
+ if (cpr_supported_override)
+ return (0);
+
+ /*
+ * If a platform has specifically turned on cpr support ...
+ */
+ if (cpr_platform_enable)
+ return (1);
+
+ return (pm_S3_enabled);
+}
+
+void
+i_cpr_bitmap_cleanup(void)
+{
+}
+
+void
+i_cpr_free_memory_resources(void)
+{
+}
+
+/*
+ * Needed only for S3 so far
+ */
+static int
+i_cpr_platform_alloc(psm_state_request_t *req)
+{
+ char *str = "i_cpr_platform_alloc";
+
+ PMD(PMD_SX, ("cpu = %d, %s(%p) \n", CPU->cpu_id, str, (void *)req))
+
+ if (ncpus == 1) {
+ PMD(PMD_SX, ("%s() : ncpus == 1\n", str))
+ return (0);
+ }
+
+ req->psr_cmd = PSM_STATE_ALLOC;
+ return ((*psm_state)(req));
+}
+
+/*
+ * Needed only for S3 so far
+ */
+static void
+i_cpr_platform_free(psm_state_request_t *req)
+{
+ char *str = "i_cpr_platform_free";
+
+ PMD(PMD_SX, ("cpu = %d, %s(%p) \n", CPU->cpu_id, str, (void *)req))
+
+ if (ncpus == 1) {
+ PMD(PMD_SX, ("%s() : ncpus == 1\n", str))
+ }
+
+ req->psr_cmd = PSM_STATE_FREE;
+ (void) (*psm_state)(req);
+}
+
+static int
+i_cpr_save_apic(psm_state_request_t *req)
+{
+ char *str = "i_cpr_save_apic";
+
+ if (ncpus == 1) {
+ PMD(PMD_SX, ("%s() : ncpus == 1\n", str))
+ return (0);
+ }
+
+ req->psr_cmd = PSM_STATE_SAVE;
+ return ((*psm_state)(req));
+}
+
+static int
+i_cpr_restore_apic(psm_state_request_t *req)
+{
+ char *str = "i_cpr_restore_apic";
+
+ if (ncpus == 1) {
+ PMD(PMD_SX, ("%s() : ncpus == 1\n", str))
+ return (0);
+ }
+
+ req->psr_cmd = PSM_STATE_RESTORE;
+ return ((*psm_state)(req));
+}
+
+
+/* stop lint complaining about offset not being used in 32bit mode */
+#if !defined(__amd64)
+/*ARGSUSED*/
+#endif
+static void
+init_real_mode_platter(int cpun, uint32_t offset, uint_t cr4, wc_desctbr_t gdt)
+{
+ /*LINTED*/
+ rm_platter_t *real_mode_platter = (rm_platter_t *)rm_platter_va;
+
+ /*
+ * Fill up the real mode platter to make it easy for real mode code to
+ * kick it off. This area should really be one passed by boot to kernel
+ * and guaranteed to be below 1MB and aligned to 16 bytes. Should also
+ * have identical physical and virtual address in paged mode.
+ */
+
+ real_mode_platter->rm_pdbr = getcr3();
+ real_mode_platter->rm_cpu = cpun;
+ real_mode_platter->rm_cr4 = cr4;
+
+ real_mode_platter->rm_gdt_base = gdt.base;
+ real_mode_platter->rm_gdt_lim = gdt.limit;
+
+#if defined(__amd64)
+ real_mode_platter->rm_x86feature = x86_feature;
+
+ if (getcr3() > 0xffffffffUL)
+ panic("Cannot initialize CPUs; kernel's 64-bit page tables\n"
+ "located above 4G in physical memory (@ 0x%llx).",
+ (unsigned long long)getcr3());
+
+ /*
+ * Setup pseudo-descriptors for temporary GDT and IDT for use ONLY
+ * by code in real_mode_start():
+ *
+ * GDT[0]: NULL selector
+ * GDT[1]: 64-bit CS: Long = 1, Present = 1, bits 12, 11 = 1
+ *
+ * Clear the IDT as interrupts will be off and a limit of 0 will cause
+ * the CPU to triple fault and reset on an NMI, seemingly as reasonable
+ * a course of action as any other, though it may cause the entire
+ * platform to reset in some cases...
+ */
+ real_mode_platter->rm_temp_gdt[0] = 0ULL;
+ real_mode_platter->rm_temp_gdt[TEMPGDT_KCODE64] = 0x20980000000000ULL;
+
+ real_mode_platter->rm_temp_gdt_lim = (ushort_t)
+ (sizeof (real_mode_platter->rm_temp_gdt) - 1);
+ real_mode_platter->rm_temp_gdt_base = rm_platter_pa +
+ (uint32_t)(&((rm_platter_t *)0)->rm_temp_gdt);
+
+ real_mode_platter->rm_temp_idt_lim = 0;
+ real_mode_platter->rm_temp_idt_base = 0;
+
+ /*
+ * Since the CPU needs to jump to protected mode using an identity
+ * mapped address, we need to calculate it here.
+ */
+ real_mode_platter->rm_longmode64_addr = rm_platter_pa + offset;
+#endif /* __amd64 */
+
+ /* return; */
+}
+
+void
+i_cpr_start_cpu(void)
+{
+
+ struct cpu *cp = CPU;
+
+ char *str = "i_cpr_start_cpu";
+ extern void init_cpu_syscall(struct cpu *cp);
+
+#if defined(__amd64)
+ wc_cpu_t *cpup = wc_other_cpus + cp->cpu_id;
+#endif /* __amd64 */
+
+ PMD(PMD_SX, ("%s() called\n", str))
+
+ PMD(PMD_SX, ("%s() #0 cp->cpu_base_spl %d\n", str,
+ cp->cpu_base_spl))
+
+ mutex_enter(&cpu_lock);
+ if (cp == i_cpr_bootcpu()) {
+ mutex_exit(&cpu_lock);
+ PMD(PMD_SX,
+ ("%s() called on bootcpu nothing to do!\n", str))
+ return;
+ }
+ mutex_exit(&cpu_lock);
+
+ /*
+ * We need to Sync PAT with cpu0's PAT. We have to do
+ * this with interrupts disabled.
+ */
+ if (x86_feature & X86_PAT)
+ pat_sync();
+
+ /*
+ * Initialize this CPU's syscall handlers
+ */
+ init_cpu_syscall(cp);
+
+ PMD(PMD_SX, ("%s() #1 cp->cpu_base_spl %d\n", str, cp->cpu_base_spl))
+
+ /*
+ * Do not need to call cpuid_pass2(), cpuid_pass3(), cpuid_pass4() or
+ * init_cpu_info(), since the work that they do is only needed to
+ * be done once at boot time
+ */
+
+
+ mutex_enter(&cpu_lock);
+
+#if defined(__amd64)
+ restore_stack(cpup);
+#endif /* __amd64 */
+
+ CPUSET_ADD(procset, cp->cpu_id);
+ mutex_exit(&cpu_lock);
+
+ PMD(PMD_SX, ("%s() #2 cp->cpu_base_spl %d\n", str,
+ cp->cpu_base_spl))
+
+ /* XXX remove before integration */
+ PMD(PMD_SX, ("%s() procset 0x%lx\n", str, (ulong_t)procset))
+
+ if (tsc_gethrtime_enable) {
+ PMD(PMD_SX, ("%s() calling tsc_sync_slave\n", str))
+ tsc_sync_slave();
+ }
+
+ PMD(PMD_SX, ("%s() cp->cpu_id %d, cp->cpu_intr_actv %d\n", str,
+ cp->cpu_id, cp->cpu_intr_actv))
+ PMD(PMD_SX, ("%s() #3 cp->cpu_base_spl %d\n", str,
+ cp->cpu_base_spl))
+
+ (void) spl0(); /* enable interrupts */
+
+ PMD(PMD_SX, ("%s() #4 cp->cpu_base_spl %d\n", str,
+ cp->cpu_base_spl))
+
+ /*
+ * Set up the CPU module for this CPU. This can't be done before
+ * this CPU is made CPU_READY, because we may (in heterogeneous systems)
+ * need to go load another CPU module. The act of attempting to load
+ * a module may trigger a cross-call, which will ASSERT unless this
+ * cpu is CPU_READY.
+ */
+
+ /*
+ * cmi already been init'd (during boot), so do not need to do it again
+ */
+#ifdef PM_REINITMCAONRESUME
+ if (x86_feature & X86_MCA)
+ cmi_mca_init();
+#endif
+
+ PMD(PMD_SX, ("%s() returning\n", str))
+
+ /* return; */
+}
+
+#if defined(__amd64)
+/*
+ * we only need to do this for amd64!
+ */
+
+/*
+ * save the stack
+ */
+void
+save_stack(wc_cpu_t *cpup)
+{
+ char *str = "save_stack";
+ caddr_t base = curthread->t_stk;
+ caddr_t sp = (caddr_t)cpup->wc_rsp;
+
+
+ PMD(PMD_SX, ("%s() CPU->cpu_id %d\n", str, CPU->cpu_id))
+ PMD(PMD_SX, ("save_stack() curthread->t_stk = %p, sp = %p\n",
+ (void *)base, (void *)sp))
+
+ ASSERT(base > sp);
+ /*LINTED*/
+ bcopy(sp, cpup->wc_stack, base - sp);
+
+}
+
+/*
+ * restore the stack
+ */
+static void
+restore_stack(wc_cpu_t *cpup)
+{
+ /*
+ * we only need to do this for amd64!
+ */
+
+ char *str = "restore_stack";
+ caddr_t base = curthread->t_stk;
+ caddr_t sp = (caddr_t)cpup->wc_rsp;
+
+ PMD(PMD_SX, ("%s() CPU->cpu_id %d\n", str, CPU->cpu_id))
+ PMD(PMD_SX, ("%s() curthread->t_stk = %p, sp = %p\n", str,
+ (void *)base, (void *)sp))
+
+ ASSERT(base > sp);
+ /*LINTED*/
+ bcopy(cpup->wc_stack, sp, base - sp);
+
+}
+
+#endif /* __amd64 */
+
+
+void
+i_cpr_alloc_cpus(void)
+{
+ char *str = "i_cpr_alloc_cpus";
+
+ PMD(PMD_SX, ("%s() CPU->cpu_id %d\n", str, CPU->cpu_id))
+ /*
+ * we allocate this only when we actually need it to save on
+ * kernel memory
+ */
+
+ if (wc_other_cpus == NULL) {
+ wc_other_cpus = kmem_zalloc(ncpus * sizeof (wc_cpu_t),
+ KM_SLEEP);
+ }
+
+}
+
+void
+i_cpr_free_cpus(void)
+{
+ if (wc_other_cpus != NULL) {
+ kmem_free((void *) wc_other_cpus, ncpus * sizeof (wc_cpu_t));
+ wc_other_cpus = NULL;
+ }
+}
+
+/*
+ * wrapper for acpica_ddi_save_resources()
+ */
+void
+i_cpr_save_configuration(dev_info_t *dip)
+{
+ acpica_ddi_save_resources(dip);
+}
+
+/*
+ * wrapper for acpica_ddi_restore_resources()
+ */
+void
+i_cpr_restore_configuration(dev_info_t *dip)
+{
+ acpica_ddi_restore_resources(dip);
+}
diff --git a/usr/src/uts/i86pc/os/ddi_impl.c b/usr/src/uts/i86pc/os/ddi_impl.c
index ce92a564ec..1ddc6aa0f7 100644
--- a/usr/src/uts/i86pc/os/ddi_impl.c
+++ b/usr/src/uts/i86pc/os/ddi_impl.c
@@ -73,9 +73,10 @@
*/
/*
- * No platform drivers on this platform
+ * Platform drivers on this platform
*/
char *platform_module_list[] = {
+ "acpippm",
"ppm",
(char *)0
};
@@ -1947,9 +1948,18 @@ get_vga_properties(void)
char property_val[50];
void *bop_staging_area;
- major = ddi_name_to_major("vgatext");
- if (major == (major_t)-1)
- return;
+ /*
+ * XXXX Hack Allert!
+ * There really needs to be a better way for identifying various
+ * console framebuffers and their related issues. Till then,
+ * check for this one as a replacement to vgatext.
+ */
+ major = ddi_name_to_major("ragexl");
+ if (major == (major_t)-1) {
+ major = ddi_name_to_major("vgatext");
+ if (major == (major_t)-1)
+ return;
+ }
devi = devnamesp[major].dn_head;
if (devi == NULL)
return;
diff --git a/usr/src/uts/i86pc/os/memscrub.c b/usr/src/uts/i86pc/os/memscrub.c
index 322573ff91..b38cd4ecfb 100644
--- a/usr/src/uts/i86pc/os/memscrub.c
+++ b/usr/src/uts/i86pc/os/memscrub.c
@@ -98,6 +98,7 @@
#include <vm/seg_kmem.h>
#include <vm/seg_kpm.h>
#include <vm/hat_i86.h>
+#include <sys/callb.h> /* CPR callback */
static caddr_t memscrub_window;
static hat_mempte_t memscrub_pte;
@@ -252,7 +253,7 @@ compute_interval_sec()
return (memscrub_period_sec);
else
return (memscrub_period_sec/
- (memscrub_phys_pages/memscrub_span_pages));
+ (memscrub_phys_pages/memscrub_span_pages));
}
void
@@ -266,6 +267,12 @@ memscrubber()
struct memlist *mlp;
extern void scan_memory(caddr_t, size_t);
+ callb_cpr_t cprinfo;
+
+ /*
+ * notify CPR of our existence
+ */
+ CALLB_CPR_INIT(&cprinfo, &memscrub_lock, callb_generic_cpr, "memscrub");
if (memscrub_memlist == NULL) {
cmn_err(CE_WARN, "memscrub_memlist not initialized.");
@@ -314,6 +321,12 @@ memscrubber()
}
/*
+ * it is safe from our standpoint for CPR to
+ * suspend the system
+ */
+ CALLB_CPR_SAFE_BEGIN(&cprinfo);
+
+ /*
* hit the snooze bar
*/
(void) timeout(memscrub_wakeup, NULL, interval_sec * hz);
@@ -323,6 +336,9 @@ memscrubber()
*/
cv_wait(&memscrub_cv, &memscrub_lock);
+ /* we need to goto work */
+ CALLB_CPR_SAFE_END(&cprinfo, &memscrub_lock);
+
mutex_exit(&memscrub_lock);
do {
@@ -393,6 +409,12 @@ memscrub_exit:
if (!disable_memscrub_quietly)
cmn_err(CE_NOTE, "memory scrubber exiting.");
+ /*
+ * We are about to bail, but don't have the memscrub_lock,
+ * and it is needed for CALLB_CPR_EXIT.
+ */
+ mutex_enter(&memscrub_lock);
+ CALLB_CPR_EXIT(&cprinfo);
cv_destroy(&memscrub_cv);
@@ -448,7 +470,7 @@ memscrub_add_span(uint64_t start, uint64_t bytes)
memscrub_printmemlist("memscrub_memlist before", memscrub_memlist);
cmn_err(CE_CONT, "memscrub_phys_pages: 0x%x\n", memscrub_phys_pages);
cmn_err(CE_CONT, "memscrub_add_span: address: 0x%llx"
- " size: 0x%llx\n", start, bytes);
+ " size: 0x%llx\n", start, bytes);
#endif /* MEMSCRUB_DEBUG */
/*
diff --git a/usr/src/uts/i86pc/os/mp_implfuncs.c b/usr/src/uts/i86pc/os/mp_implfuncs.c
index ac3e03ff45..5ffbd7502a 100644
--- a/usr/src/uts/i86pc/os/mp_implfuncs.c
+++ b/usr/src/uts/i86pc/os/mp_implfuncs.c
@@ -30,7 +30,7 @@
#include <vm/hat.h>
#include <sys/modctl.h>
#include <vm/seg_kmem.h>
-#define PSMI_1_5
+#define PSMI_1_6
#include <sys/psm.h>
#include <sys/psm_modctl.h>
#include <sys/smp_impldefs.h>
diff --git a/usr/src/uts/i86pc/os/mp_machdep.c b/usr/src/uts/i86pc/os/mp_machdep.c
index 1d4fef5395..93332f1382 100644
--- a/usr/src/uts/i86pc/os/mp_machdep.c
+++ b/usr/src/uts/i86pc/os/mp_machdep.c
@@ -25,7 +25,7 @@
#pragma ident "%Z%%M% %I% %E% SMI"
-#define PSMI_1_5
+#define PSMI_1_6
#include <sys/smp_impldefs.h>
#include <sys/psm.h>
#include <sys/psm_modctl.h>
@@ -136,6 +136,8 @@ void (*psm_timer_disable)(void) = NULL;
void (*psm_post_cyclic_setup)(void *arg) = NULL;
int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, psm_intr_op_t,
int *) = mach_intr_ops;
+int (*psm_state)(psm_state_request_t *) = (int (*)(psm_state_request_t *))
+ return_instr;
void (*notify_error)(int, char *) = (void (*)(int, char *))return_instr;
void (*hrtime_tick)(void) = return_instr;
@@ -923,6 +925,9 @@ mach_smpinit(void)
if (pops->psm_post_cyclic_setup)
psm_post_cyclic_setup = pops->psm_post_cyclic_setup;
+ if (pops->psm_state)
+ psm_state = pops->psm_state;
+
/* check for multiple cpu's */
if (cnt < 2)
return;
@@ -1314,6 +1319,18 @@ mach_cpu_start(struct cpu *cp, void *ctx)
return ((*pops->psm_cpu_start)(id, ctx));
}
+int
+mach_cpuid_start(processorid_t id, void *ctx)
+{
+ struct psm_ops *pops = mach_set[0];
+
+#ifdef DEBUG
+ if (CPU_IN_SET(cpufailset, id))
+ return (0);
+#endif
+ return ((*pops->psm_cpu_start)(id, ctx));
+}
+
/*ARGSUSED*/
static int
mach_translate_irq(dev_info_t *dip, int irqno)
diff --git a/usr/src/uts/i86pc/os/mp_pc.c b/usr/src/uts/i86pc/os/mp_pc.c
index eb07c7bd4f..7746818a8f 100644
--- a/usr/src/uts/i86pc/os/mp_pc.c
+++ b/usr/src/uts/i86pc/os/mp_pc.c
@@ -47,6 +47,9 @@
extern void real_mode_start(void);
extern void real_mode_end(void);
+extern void *(*cpu_pause_func)(void *);
+
+void rmp_gdt_init(rm_platter_t *);
/*
* Fill up the real mode platter to make it easy for real mode code to
@@ -68,8 +71,8 @@ mach_cpucontext_init(void)
* setup secondary cpu bios boot up vector
*/
*vec = (ushort_t)((caddr_t)
- ((struct rm_platter *)rm_platter_va)->rm_code - rm_platter_va
- + ((ulong_t)rm_platter_va & 0xf));
+ ((struct rm_platter *)rm_platter_va)->rm_code - rm_platter_va
+ + ((ulong_t)rm_platter_va & 0xf));
vec[1] = (ushort_t)(rm_platter_pa >> 4);
warm_reset_vector = vec;
@@ -162,6 +165,16 @@ mach_cpucontext_alloc(struct cpu *cp)
rm->rm_x86feature = x86_feature;
rm->rm_cr4 = getcr4();
+ rmp_gdt_init(rm);
+
+ return (ct);
+}
+
+/*ARGSUSED*/
+void
+rmp_gdt_init(rm_platter_t *rm)
+{
+
#if defined(__amd64)
if (getcr3() > 0xffffffffUL)
@@ -197,7 +210,6 @@ mach_cpucontext_alloc(struct cpu *cp)
((uint32_t)long_mode_64 - (uint32_t)real_mode_start);
#endif /* __amd64 */
- return (ct);
}
/*ARGSUSED*/
diff --git a/usr/src/uts/i86pc/os/mp_startup.c b/usr/src/uts/i86pc/os/mp_startup.c
index 87f945811a..4feca10ed7 100644
--- a/usr/src/uts/i86pc/os/mp_startup.c
+++ b/usr/src/uts/i86pc/os/mp_startup.c
@@ -148,7 +148,7 @@ init_cpu_info(struct cpu *cp)
* Configure syscall support on this CPU.
*/
/*ARGSUSED*/
-static void
+void
init_cpu_syscall(struct cpu *cp)
{
kpreempt_disable();
diff --git a/usr/src/uts/i86pc/os/timestamp.c b/usr/src/uts/i86pc/os/timestamp.c
index fe49b90f2a..23d9957d13 100644
--- a/usr/src/uts/i86pc/os/timestamp.c
+++ b/usr/src/uts/i86pc/os/timestamp.c
@@ -160,6 +160,7 @@ static hrtime_t shadow_tsc_hrtime_base;
static hrtime_t shadow_tsc_last;
static uint_t shadow_nsec_scale;
static uint32_t shadow_hres_lock;
+int get_tsc_ready();
hrtime_t
tsc_gethrtime(void)
@@ -409,7 +410,8 @@ tsc_digest(processorid_t target)
if ((tdelta > max) || ((tdelta >= 0) && update)) {
TSC_CONVERT_AND_ADD(tdelta, hdelta, nsec_scale);
tsc_sync_delta[target] = tsc_sync_delta[source] - hdelta;
- tsc_sync_tick_delta[target] = -tdelta;
+ tsc_sync_tick_delta[target] = tsc_sync_tick_delta[source]
+ -tdelta;
gethrtimef = tsc_gethrtime_delta;
gethrtimeunscaledf = tsc_gethrtimeunscaled_delta;
return;
@@ -419,7 +421,8 @@ tsc_digest(processorid_t target)
if ((tdelta > max) || update) {
TSC_CONVERT_AND_ADD(tdelta, hdelta, nsec_scale);
tsc_sync_delta[target] = tsc_sync_delta[source] + hdelta;
- tsc_sync_tick_delta[target] = tdelta;
+ tsc_sync_tick_delta[target] = tsc_sync_tick_delta[source]
+ + tdelta;
gethrtimef = tsc_gethrtime_delta;
gethrtimeunscaledf = tsc_gethrtimeunscaled_delta;
}
@@ -621,3 +624,166 @@ tsc_hrtimeinit(uint64_t cpu_freq_hz)
hrtime_tick = tsc_tick;
gethrtime_hires = 1;
}
+
+int
+get_tsc_ready()
+{
+ return (tsc_ready);
+}
+
+/*
+ * Adjust all the deltas by adding the passed value to the array.
+ * Then use the "delt" versions of the the gethrtime functions.
+ * Note that 'tdelta' _could_ be a negative number, which should
+ * reduce the values in the array (used, for example, if the Solaris
+ * instance was moved by a virtual manager to a machine with a higher
+ * value of tsc).
+ */
+void
+tsc_adjust_delta(hrtime_t tdelta)
+{
+ int i;
+ hrtime_t hdelta = 0;
+
+ TSC_CONVERT(tdelta, hdelta, nsec_scale);
+
+ for (i = 0; i < NCPU; i++) {
+ tsc_sync_delta[i] += hdelta;
+ tsc_sync_tick_delta[i] += tdelta;
+ }
+
+ gethrtimef = tsc_gethrtime_delta;
+ gethrtimeunscaledf = tsc_gethrtimeunscaled_delta;
+}
+
+/*
+ * Functions to manage TSC and high-res time on suspend and resume.
+ */
+
+/*
+ * declarations needed for time adjustment
+ */
+extern void rtcsync(void);
+extern tod_ops_t *tod_ops;
+/* There must be a better way than exposing nsec_scale! */
+extern uint_t nsec_scale;
+static uint64_t tsc_saved_tsc = 0; /* 1 in 2^64 chance this'll screw up! */
+static timestruc_t tsc_saved_ts;
+static int tsc_needs_resume = 0; /* We only want to do this once. */
+int tsc_delta_onsuspend = 0;
+int tsc_adjust_seconds = 1;
+int tsc_suspend_count = 0;
+int tsc_resume_in_cyclic = 0;
+
+/*
+ * Let timestamp.c know that we are suspending. It needs to take
+ * snapshots of the current time, and do any pre-suspend work.
+ */
+void
+tsc_suspend(void)
+{
+/*
+ * What we need to do here, is to get the time we suspended, so that we
+ * know how much we should add to the resume.
+ * This routine is called by each CPU, so we need to handle reentry.
+ */
+ if (tsc_gethrtime_enable) {
+ /*
+ * We put the tsc_read() inside the lock as it
+ * as no locking constraints, and it puts the
+ * aquired value closer to the time stamp (in
+ * case we delay getting the lock).
+ */
+ mutex_enter(&tod_lock);
+ tsc_saved_tsc = tsc_read();
+ tsc_saved_ts = TODOP_GET(tod_ops);
+ mutex_exit(&tod_lock);
+ /* We only want to do this once. */
+ if (tsc_needs_resume == 0) {
+ if (tsc_delta_onsuspend) {
+ tsc_adjust_delta(tsc_saved_tsc);
+ } else {
+ tsc_adjust_delta(nsec_scale);
+ }
+ tsc_suspend_count++;
+ }
+ }
+
+ invalidate_cache();
+ tsc_needs_resume = 1;
+}
+
+/*
+ * Restore all timestamp state based on the snapshots taken at
+ * suspend time.
+ */
+void
+tsc_resume(void)
+{
+ /*
+ * We only need to (and want to) do this once. So let the first
+ * caller handle this (we are locked by the cpu lock), as it
+ * is preferential that we get the earliest sync.
+ */
+ if (tsc_needs_resume) {
+ /*
+ * If using the TSC, adjust the delta based on how long
+ * we were sleeping (or away). We also adjust for
+ * migration and a grown TSC.
+ */
+ if (tsc_saved_tsc != 0) {
+ timestruc_t ts;
+ hrtime_t now, sleep_tsc = 0;
+ int sleep_sec;
+ extern void tsc_tick(void);
+ extern uint64_t cpu_freq_hz;
+
+ /* tsc_read() MUST be before TODOP_GET() */
+ mutex_enter(&tod_lock);
+ now = tsc_read();
+ ts = TODOP_GET(tod_ops);
+ mutex_exit(&tod_lock);
+
+ /* Compute seconds of sleep time */
+ sleep_sec = ts.tv_sec - tsc_saved_ts.tv_sec;
+
+ /*
+ * If the saved sec is less that or equal to
+ * the current ts, then there is likely a
+ * problem with the clock. Assume at least
+ * one second has passed, so that time goes forward.
+ */
+ if (sleep_sec <= 0) {
+ sleep_sec = 1;
+ }
+
+ /* How many TSC's should have occured while sleeping */
+ if (tsc_adjust_seconds)
+ sleep_tsc = sleep_sec * cpu_freq_hz;
+
+ /*
+ * We also want to subtract from the "sleep_tsc"
+ * the current value of tsc_read(), so that our
+ * adjustment accounts for the amount of time we
+ * have been resumed _or_ an adjustment based on
+ * the fact that we didn't actually power off the
+ * CPU (migration is another issue, but _should_
+ * also comply with this calculation). If the CPU
+ * never powered off, then:
+ * 'now == sleep_tsc + saved_tsc'
+ * and the delta will effectively be "0".
+ */
+ sleep_tsc -= now;
+ if (tsc_delta_onsuspend) {
+ tsc_adjust_delta(sleep_tsc);
+ } else {
+ tsc_adjust_delta(tsc_saved_tsc + sleep_tsc);
+ }
+ tsc_saved_tsc = 0;
+
+ tsc_tick();
+ }
+ tsc_needs_resume = 0;
+ }
+
+}
diff --git a/usr/src/uts/i86pc/sys/apic.h b/usr/src/uts/i86pc/sys/apic.h
index 0e692d954b..f4f57e1059 100644
--- a/usr/src/uts/i86pc/sys/apic.h
+++ b/usr/src/uts/i86pc/sys/apic.h
@@ -699,6 +699,7 @@ extern int apic_rebind_all(apic_irq_t *irq_ptr, int bind_cpu);
extern int apic_introp_xlate(dev_info_t *dip, struct intrspec *ispec, int type);
extern int apic_intr_ops(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp,
psm_intr_op_t intr_op, int *result);
+extern int apic_state(psm_state_request_t *);
extern boolean_t apic_cpu_in_range(int cpu);
extern int apic_check_msi_support();
extern apic_irq_t *apic_find_irq(dev_info_t *dip, struct intrspec *ispec,
diff --git a/usr/src/uts/i86pc/sys/cpr_impl.h b/usr/src/uts/i86pc/sys/cpr_impl.h
new file mode 100644
index 0000000000..72b76f42d4
--- /dev/null
+++ b/usr/src/uts/i86pc/sys/cpr_impl.h
@@ -0,0 +1,72 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_CPR_IMPL_H
+#define _SYS_CPR_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifndef _ASM
+
+#include <sys/processor.h>
+#include <sys/machparam.h>
+#include <sys/vnode.h>
+#include <sys/pte.h>
+
+/*
+ * This file contains machine dependent information for CPR
+ */
+#define CPR_MACHTYPE_X86 0x5856 /* 'X'0t86 */
+typedef uint64_t cpr_ptr;
+typedef uint64_t cpr_ext;
+
+
+/*
+ * processor info
+ */
+struct i86pc_cpu_info {
+ pnode_t node;
+ processorid_t cpu_id;
+};
+
+extern void i_cpr_machdep_setup(void);
+extern void i_cpr_enable_intr(void);
+extern void i_cpr_stop_intr(void);
+extern void i_cpr_handle_xc(int);
+extern int i_cpr_check_cprinfo(void);
+extern int i_cpr_reusable_supported(void);
+
+#endif /* _ASM */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_CPR_IMPL_H */
diff --git a/usr/src/uts/i86pc/sys/cpr_wakecode.h b/usr/src/uts/i86pc/sys/cpr_wakecode.h
new file mode 100644
index 0000000000..7f55c9dcd6
--- /dev/null
+++ b/usr/src/uts/i86pc/sys/cpr_wakecode.h
@@ -0,0 +1,153 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _CPR_WC_H
+#define _CPR_WC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define WC_CODESIZE 0x400
+
+#if ! defined(_ASM)
+
+#include <sys/rm_platter.h>
+#include <sys/psm_types.h>
+
+typedef struct wc_cpu {
+ uint64_t wc_retaddr;
+ uint64_t wc_virtaddr;
+ uint64_t wc_cr0;
+ uint64_t wc_cr3;
+ uint64_t wc_cr4;
+ uint64_t wc_cr8;
+ uint64_t wc_fs;
+ uint64_t wc_fsbase;
+ uint64_t wc_gs;
+ uint64_t wc_gsbase;
+ uint64_t wc_kgsbase;
+ uint64_t wc_r8;
+ uint64_t wc_r9;
+ uint64_t wc_r10;
+ uint64_t wc_r11;
+ uint64_t wc_r12;
+ uint64_t wc_r13;
+ uint64_t wc_r14;
+ uint64_t wc_r15;
+ uint64_t wc_rax;
+ uint64_t wc_rbp;
+ uint64_t wc_rbx;
+ uint64_t wc_rcx;
+ uint64_t wc_rdi;
+ uint64_t wc_rdx;
+ uint64_t wc_rsi;
+ uint64_t wc_rsp;
+
+#if defined(__amd64)
+ /*
+ * The compiler will want to 64-bit align the 64-bit rm_gdt_base
+ * pointer, so we need to add an extra four bytes of padding here to
+ * make sure rm_gdt_lim and rm_gdt_base will align to create a proper
+ * ten byte GDT pseudo-descriptor.
+ */
+uint32_t wc_gdt_pad1;
+#endif
+ ushort_t wc_gdt_pad2;
+ ushort_t wc_gdt_limit;
+ user_desc_t *wc_gdt_base;
+
+#if defined(__amd64)
+ /*
+ * The compiler will want to 64-bit align the 64-bit rm_idt_base
+ * pointer, so we need to add an extra four bytes of padding here to
+ * make sure rm_idt_lim and rm_idt_base will align to create a proper
+ * ten byte IDT pseudo-descriptor.
+ */
+uint32_t wc_idt_pad1;
+#endif
+ ushort_t wc_idt_pad2;
+ ushort_t wc_idt_limit;
+ user_desc_t *wc_idt_base;
+
+#if defined(__amd64)
+ uint64_t wc_tr;
+ uint64_t wc_ldt;
+ uint64_t wc_eflags;
+#else
+ uint32_t wc_tr;
+ uint32_t wc_ldt;
+ uint32_t wc_eflags;
+#endif
+
+ uint32_t wc_ebx;
+ uint32_t wc_edi;
+ uint32_t wc_esi;
+ uint32_t wc_ebp;
+ uint32_t wc_esp;
+ uint16_t wc_ss;
+ uint16_t wc_cs;
+ uint16_t wc_ds;
+ uint16_t wc_es;
+ char wc_stack[400];
+ psm_state_request_t wc_apic_state;
+
+
+ /* temp stack grows down to here */
+} wc_cpu_t;
+
+typedef struct wc_wakecode {
+ rm_platter_t wc_platter;
+ wc_cpu_t wc_cpu;
+
+ /* temp stack grows down to here */
+} wakecode_t;
+
+/*
+ * this is NOT correctly aligned, see description of idt & gdt, limit and
+ * base in wc_cpu_t above
+ */
+typedef struct wc_desctbr {
+ ushort_t limit;
+ void *base;
+} wc_desctbr_t;
+
+extern int wc_save_context(wc_cpu_t *);
+extern void wc_rm_start(void);
+extern void wc_rm_end(void);
+extern void (*cpr_start_cpu_func)(void);
+
+#endif /* ! defined(_ASM) */
+
+#define WC_STKSTART 0x7fc /* end of rm_platter page */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CPR_WC_H */
diff --git a/usr/src/uts/i86pc/sys/machclock.h b/usr/src/uts/i86pc/sys/machclock.h
index 6b3686b95c..a214ada1bc 100644
--- a/usr/src/uts/i86pc/sys/machclock.h
+++ b/usr/src/uts/i86pc/sys/machclock.h
@@ -40,14 +40,20 @@ extern "C" {
struct tod_ops;
typedef struct tod_ops tod_ops_t;
+/*
+ * TOD Ops.
+ * The only functions that _must_ be defined are the tod_get() and
+ * tod_set() functions. All others may be unused, and need to be
+ * checked for NULL before using.
+ */
struct tod_ops {
int tod_version;
timestruc_t (*tod_get)(tod_ops_t *);
void (*tod_set)(tod_ops_t *, timestruc_t);
- /*
- * On SPARC, additional operations include setting
- * and clearing a watchdog timer, as well as power alarms.
- */
+ uint_t (*tod_set_watchdog_timer)(tod_ops_t *, int);
+ uint_t (*tod_clear_watchdog_timer)(tod_ops_t *);
+ void (*tod_set_wake_alarm)(tod_ops_t *, int);
+ void (*tod_clear_wake_alarm)(tod_ops_t *);
struct tod_ops *tod_next;
};
@@ -58,6 +64,10 @@ extern char *tod_module_name;
#define TODOP_GET(top) ((top)->tod_get(top))
#define TODOP_SET(top, ts) ((top)->tod_set(top, ts))
+#define TODOP_SETWD(top, nsec) ((top)->tod_set_watchdog_timer(top, nsec))
+#define TODOP_CLRWD(top) ((top)->tod_clear_watchdog_timer(top))
+#define TODOP_SETWAKE(top, nsec) ((top)->tod_set_wake_alarm(top, nsec))
+#define TODOP_CLRWAKE(top) ((top)->tod_clear_wake_alarm(top))
#ifdef __cplusplus
}
diff --git a/usr/src/uts/i86pc/sys/machsystm.h b/usr/src/uts/i86pc/sys/machsystm.h
index fdaa21d218..7f5cb22437 100644
--- a/usr/src/uts/i86pc/sys/machsystm.h
+++ b/usr/src/uts/i86pc/sys/machsystm.h
@@ -56,6 +56,7 @@ extern "C" {
extern void mach_cpu_idle(void);
extern void mach_cpu_halt(char *);
extern int mach_cpu_start(cpu_t *, void *);
+extern int mach_cpuid_start(processorid_t, void *);
extern int Cpudelay;
extern void setcpudelay(void);
@@ -116,6 +117,7 @@ extern int mach_cpucontext_init(void);
extern void mach_cpucontext_fini(void);
extern void *mach_cpucontext_alloc(struct cpu *);
extern void mach_cpucontext_free(struct cpu *, void *, int);
+extern void rmp_gdt_init(rm_platter_t *);
extern uintptr_t hole_start, hole_end;
diff --git a/usr/src/uts/i86pc/sys/psm_common.h b/usr/src/uts/i86pc/sys/psm_common.h
index ef68cb9f07..b244f186c5 100644
--- a/usr/src/uts/i86pc/sys/psm_common.h
+++ b/usr/src/uts/i86pc/sys/psm_common.h
@@ -135,6 +135,8 @@ extern void acpi_new_irq_cache_ent(int bus, int dev, int ipin, int pci_irq,
extern int acpi_get_irq_cache_ent(uchar_t bus, uchar_t dev, int ipin,
int *pci_irqp, iflag_t *intr_flagp);
+extern void acpi_restore_link_devices(void);
+
extern int acpi_poweroff(void);
extern void psm_set_elcr(int vecno, int val);
diff --git a/usr/src/uts/i86pc/sys/psm_types.h b/usr/src/uts/i86pc/sys/psm_types.h
index 70b49ed3dc..17e9db17b1 100644
--- a/usr/src/uts/i86pc/sys/psm_types.h
+++ b/usr/src/uts/i86pc/sys/psm_types.h
@@ -63,7 +63,32 @@ typedef enum psm_intr_op_e {
PSM_INTR_OP_APIC_TYPE /* 15. Returns APIC type */
} psm_intr_op_t;
-struct psm_ops {
+/*
+ * PSM_STATE definitions
+ */
+typedef enum psm_state_op_e {
+ PSM_STATE_ALLOC = 1,
+ PSM_STATE_FREE,
+ PSM_STATE_SAVE,
+ PSM_STATE_RESTORE
+} psm_state_op_t;
+
+typedef struct psm_state_req {
+ psm_state_op_t psr_cmd;
+ union psm_req {
+ /*
+ * PSM_STATE_ALLOC, PSM_STATE_FREE, PSM_STATE_SAVE,
+ * PSM_STATE_RESTORE all use the same struct,
+ * but union for later expansion
+ */
+ struct {
+ void *psr_state;
+ size_t psr_state_size;
+ } psm_state_req;
+ } req;
+} psm_state_request_t;
+
+struct psm_ops {
int (*psm_probe)(void);
void (*psm_softinit)(void);
@@ -80,7 +105,8 @@ struct psm_ops {
void (*psm_set_idlecpu)(processorid_t cpun);
void (*psm_unset_idlecpu)(processorid_t cpun);
-#if defined(PSMI_1_3) || defined(PSMI_1_4) || defined(PSMI_1_5)
+#if defined(PSMI_1_3) || defined(PSMI_1_4) || defined(PSMI_1_5) || \
+ defined(PSMI_1_6)
int (*psm_clkinit)(int hertz);
#else
void (*psm_clkinit)(int hertz);
@@ -91,14 +117,14 @@ struct psm_ops {
hrtime_t (*psm_gethrtime)(void);
processorid_t (*psm_get_next_processorid)(processorid_t cpu_id);
-#if defined(PSMI_1_5)
+#if defined(PSMI_1_5) || defined(PSMI_1_6)
int (*psm_cpu_start)(processorid_t cpun, caddr_t ctxt);
#else
void (*psm_cpu_start)(processorid_t cpun, caddr_t rm_code);
#endif
int (*psm_post_cpu_start)(void);
#if defined(PSMI_1_2) || defined(PSMI_1_3) || defined(PSMI_1_4) || \
- defined(PSMI_1_5)
+ defined(PSMI_1_5) || defined(PSMI_1_6)
void (*psm_shutdown)(int cmd, int fcn);
#else
void (*psm_shutdown)(void);
@@ -114,22 +140,26 @@ struct psm_ops {
#endif
void (*psm_notify_error)(int level, char *errmsg);
#if defined(PSMI_1_2) || defined(PSMI_1_3) || defined(PSMI_1_4) || \
- defined(PSMI_1_5)
+ defined(PSMI_1_5) || defined(PSMI_1_6)
void (*psm_notify_func)(int msg);
#endif
-#if defined(PSMI_1_3) || defined(PSMI_1_4) || defined(PSMI_1_5)
+#if defined(PSMI_1_3) || defined(PSMI_1_4) || defined(PSMI_1_5) || \
+ defined(PSMI_1_6)
void (*psm_timer_reprogram)(hrtime_t time);
void (*psm_timer_enable)(void);
void (*psm_timer_disable)(void);
void (*psm_post_cyclic_setup)(void *arg);
#endif
-#if defined(PSMI_1_4) || defined(PSMI_1_5)
+#if defined(PSMI_1_4) || defined(PSMI_1_5) || defined(PSMI_1_6)
void (*psm_preshutdown)(int cmd, int fcn);
#endif
-#if defined(PSMI_1_5)
+#if defined(PSMI_1_5) || defined(PSMI_1_6)
int (*psm_intr_ops)(dev_info_t *dip, ddi_intr_handle_impl_t *handle,
psm_intr_op_t op, int *result);
#endif
+#if defined(PSMI_1_6)
+ int (*psm_state)(psm_state_request_t *request);
+#endif
};
@@ -153,6 +183,7 @@ struct psm_info {
#define PSM_INFO_VER01_3 0x8604
#define PSM_INFO_VER01_4 0x8605
#define PSM_INFO_VER01_5 0x8606
+#define PSM_INFO_VER01_6 0x8706
#define PSM_INFO_VER01_X (PSM_INFO_VER01_1 & 0xFFF0) /* ver 1.X */
/*
diff --git a/usr/src/uts/i86pc/sys/smp_impldefs.h b/usr/src/uts/i86pc/sys/smp_impldefs.h
index 266c0e43a1..6c45d46072 100644
--- a/usr/src/uts/i86pc/sys/smp_impldefs.h
+++ b/usr/src/uts/i86pc/sys/smp_impldefs.h
@@ -35,6 +35,7 @@
#include <sys/avintr.h>
#include <sys/pic.h>
#include <sys/xc_levels.h>
+#include <sys/psm_types.h>
#ifdef __cplusplus
extern "C" {
@@ -70,6 +71,7 @@ extern void (*psm_timer_reprogram)(hrtime_t); /* timer reprogram */
extern void (*psm_timer_enable)(void); /* timer enable */
extern void (*psm_timer_disable)(void); /* timer disable */
extern void (*psm_post_cyclic_setup)(void *arg); /* psm cyclic setup */
+extern int (*psm_state)(psm_state_request_t *); /* psm state save/restore */
extern int (*slvltovect)(int); /* ipl interrupt priority level */
extern int (*setlvl)(int, int *); /* set intr pri represented by vect */
diff --git a/usr/src/uts/i86xpv/Makefile.files b/usr/src/uts/i86xpv/Makefile.files
index e0393e231c..221c580e2c 100644
--- a/usr/src/uts/i86xpv/Makefile.files
+++ b/usr/src/uts/i86xpv/Makefile.files
@@ -34,6 +34,7 @@
# object lists
#
CORE_OBJS += \
+ acpi_stubs.o \
balloon.o \
biosdisk.o \
cbe.o \
diff --git a/usr/src/uts/intel/Makefile.files b/usr/src/uts/intel/Makefile.files
index c84c1608c3..e4b136dd18 100644
--- a/usr/src/uts/intel/Makefile.files
+++ b/usr/src/uts/intel/Makefile.files
@@ -274,3 +274,6 @@ BOOTDEV_OBJS += \
bootdev.o
INC_PATH += -I$(UTSBASE)/intel
+
+
+CPR_INTEL_OBJS += cpr_intel.o
diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared
index 53267390d6..4602092e15 100644
--- a/usr/src/uts/intel/Makefile.intel.shared
+++ b/usr/src/uts/intel/Makefile.intel.shared
@@ -208,6 +208,7 @@ DRV_KMODS += ahci
DRV_KMODS += amd64_gart
DRV_KMODS += amr
DRV_KMODS += agpgart
+DRV_KMODS += srn
DRV_KMODS += agptarget
DRV_KMODS += arp
DRV_KMODS += asy
diff --git a/usr/src/uts/intel/ia32/ml/modstubs.s b/usr/src/uts/intel/ia32/ml/modstubs.s
index d6d1bc3d58..d6f06c6e1f 100644
--- a/usr/src/uts/intel/ia32/ml/modstubs.s
+++ b/usr/src/uts/intel/ia32/ml/modstubs.s
@@ -596,6 +596,8 @@ fcnname/**/_info: \
NO_UNLOAD_STUB(klmmod, lm_svc, nomod_zero);
NO_UNLOAD_STUB(klmmod, lm_shutdown, nomod_zero);
NO_UNLOAD_STUB(klmmod, lm_unexport, nomod_zero);
+ NO_UNLOAD_STUB(klmmod, lm_cprresume, nomod_zero);
+ NO_UNLOAD_STUB(klmmod, lm_cprsuspend, nomod_zero);
NO_UNLOAD_STUB(klmmod, lm_safelock, nomod_zero);
NO_UNLOAD_STUB(klmmod, lm_safemap, nomod_zero);
NO_UNLOAD_STUB(klmmod, lm_has_sleep, nomod_zero);
diff --git a/usr/src/uts/intel/io/acpica/acpica.c b/usr/src/uts/intel/io/acpica/acpica.c
index b743d46504..98044e8e3d 100644
--- a/usr/src/uts/intel/io/acpica/acpica.c
+++ b/usr/src/uts/intel/io/acpica/acpica.c
@@ -38,6 +38,7 @@
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/esunddi.h>
+#include <sys/kstat.h>
#include <sys/acpi/acpi.h>
#include <sys/acpica.h>
@@ -57,10 +58,17 @@ static struct modlinkage modlinkage = {
};
/*
+ * Local prototypes
+ */
+
+static void acpica_init_kstats(void);
+
+/*
* Local data
*/
static kmutex_t acpica_module_lock;
+static kstat_t *acpica_ksp;
/*
* State of acpica subsystem
@@ -99,17 +107,23 @@ int acpica_muzzle_debug_output = 0;
int acpica_muzzle_debug_output = 1;
#endif
+/*
+ * ACPI DDI hooks
+ */
+static int acpica_ddi_setwake(dev_info_t *dip, int level);
int
_init(void)
{
int error = EBUSY;
int status;
+ extern int (*acpi_fp_setwake)();
mutex_init(&acpica_module_lock, NULL, MUTEX_DRIVER, NULL);
if ((error = mod_install(&modlinkage)) != 0) {
mutex_destroy(&acpica_module_lock);
+ goto load_error;
}
AcpiGbl_EnableInterpreterSlack = (acpica_enable_interpreter_slack != 0);
@@ -118,6 +132,9 @@ _init(void)
cmn_err(CE_WARN, "!acpica: error pre-init:1:%d", status);
}
+ acpi_fp_setwake = acpica_ddi_setwake;
+
+load_error:
return (error);
}
@@ -388,6 +405,7 @@ acpica_init()
acpica_ec_init();
acpica_init_state = ACPICA_INITIALIZED;
+ acpica_init_kstats();
error:
if (acpica_init_state != ACPICA_INITIALIZED) {
cmn_err(CE_NOTE, "!failed to initialize"
@@ -487,3 +505,196 @@ acpica_get_sci(int *sci_irq, iflag_t *sci_flags)
return (AE_OK);
}
+
+/*
+ * Sets ACPI wake state for device referenced by dip.
+ * If level is S0 (0), disables wake event; otherwise,
+ * enables wake event which will wake system from level.
+ */
+static int
+acpica_ddi_setwake(dev_info_t *dip, int level)
+{
+ ACPI_STATUS status;
+ ACPI_HANDLE devobj, gpeobj;
+ ACPI_OBJECT *prw, *gpe;
+ ACPI_BUFFER prw_buf;
+ int gpebit, pwr_res_count, prw_level, rv;
+
+ /*
+ * initialize these early so we can use a common
+ * exit point below
+ */
+ prw_buf.Pointer = NULL;
+ prw_buf.Length = ACPI_ALLOCATE_BUFFER;
+ rv = 0;
+
+ /*
+ * Attempt to get a handle to a corresponding ACPI object.
+ * If no object is found, return quietly, since not all
+ * devices have corresponding ACPI objects.
+ */
+ status = acpica_get_handle(dip, &devobj);
+ if (ACPI_FAILURE(status)) {
+ char pathbuf[MAXPATHLEN];
+ ddi_pathname(dip, pathbuf);
+#ifdef DEBUG
+ cmn_err(CE_NOTE, "!acpica_ddi_setwake: could not get"
+ " handle for %s, %s:%d", pathbuf, ddi_driver_name(dip),
+ ddi_get_instance(dip));
+#endif
+ goto done;
+ }
+
+ /*
+ * Attempt to evaluate _PRW object.
+ * If no valid object is found, return quietly, since not all
+ * devices have _PRW objects.
+ */
+ status = AcpiEvaluateObject(devobj, "_PRW", NULL, &prw_buf);
+ prw = prw_buf.Pointer;
+ if (ACPI_FAILURE(status) || prw == NULL ||
+ prw->Type != ACPI_TYPE_PACKAGE || prw->Package.Count < 2 ||
+ prw->Package.Elements[1].Type != ACPI_TYPE_INTEGER) {
+ cmn_err(CE_NOTE, "acpica_ddi_setwake: could not "
+ " evaluate _PRW");
+ goto done;
+ }
+
+ /* fetch the lowest wake level from the _PRW */
+ prw_level = prw->Package.Elements[1].Integer.Value;
+
+ /*
+ * process the GPE description
+ */
+ switch (prw->Package.Elements[0].Type) {
+ case ACPI_TYPE_INTEGER:
+ gpeobj = NULL;
+ gpebit = prw->Package.Elements[0].Integer.Value;
+ break;
+ case ACPI_TYPE_PACKAGE:
+ gpe = &prw->Package.Elements[0];
+ if (gpe->Package.Count != 2 ||
+ gpe->Package.Elements[1].Type != ACPI_TYPE_INTEGER)
+ goto done;
+ gpeobj = gpe->Package.Elements[0].Reference.Handle;
+ gpebit = gpe->Package.Elements[1].Integer.Value;
+ if (gpeobj == NULL)
+ goto done;
+ default:
+ goto done;
+ }
+
+ rv = -1;
+ if (level == 0) {
+ if (ACPI_FAILURE(AcpiDisableGpe(gpeobj, gpebit, ACPI_NOT_ISR)))
+ goto done;
+ } else if (prw_level <= level) {
+ if (ACPI_SUCCESS(
+ AcpiSetGpeType(gpeobj, gpebit, ACPI_GPE_TYPE_WAKE)))
+ if (ACPI_FAILURE(
+ AcpiEnableGpe(gpeobj, gpebit, ACPI_NOT_ISR)))
+ goto done;
+ }
+ rv = 0;
+done:
+ if (prw_buf.Pointer != NULL)
+ AcpiOsFree(prw_buf.Pointer);
+ return (rv);
+}
+
+/*
+ * kstat access to a limited set of ACPI propertis
+ */
+static void
+acpica_init_kstats()
+{
+ ACPI_HANDLE s3handle;
+ ACPI_STATUS status;
+ FADT_DESCRIPTOR *fadt;
+ kstat_named_t *knp;
+
+ /*
+ * Create a small set of named kstats; just return in the rare
+ * case of a failure, * in which case, the kstats won't be present.
+ */
+ if ((acpica_ksp = kstat_create("acpi", 0, "acpi", "misc",
+ KSTAT_TYPE_NAMED, 2, 0)) == NULL)
+ return;
+
+ /*
+ * initialize kstat 'S3' to reflect the presence of \_S3 in
+ * the ACPI namespace (1 = present, 0 = not present)
+ */
+ knp = acpica_ksp->ks_data;
+ knp->value.l = (AcpiGetHandle(NULL, "\\_S3", &s3handle) == AE_OK);
+ kstat_named_init(knp, "S3", KSTAT_DATA_LONG);
+ knp++; /* advance to next named kstat */
+
+ /*
+ * initialize kstat 'preferred_pm_profile' to the value
+ * contained in the (always present) FADT
+ */
+ status = AcpiGetFirmwareTable(FADT_SIG, 1, ACPI_LOGICAL_ADDRESSING,
+ (ACPI_TABLE_HEADER **)&fadt);
+ knp->value.l = (status == AE_OK) ? fadt->Prefer_PM_Profile : -1;
+ kstat_named_init(knp, "preferred_pm_profile", KSTAT_DATA_LONG);
+
+ /*
+ * install the named kstats
+ */
+ kstat_install(acpica_ksp);
+}
+
+/*
+ * Attempt to save the current ACPI settings (_CRS) for the device
+ * which corresponds to the supplied devinfo node. The settings are
+ * saved as a property on the dip. If no ACPI object is found to be
+ * associated with the devinfo node, no action is taken and no error
+ * is reported.
+ */
+void
+acpica_ddi_save_resources(dev_info_t *dip)
+{
+ ACPI_HANDLE devobj;
+ ACPI_BUFFER resbuf;
+ int ret;
+
+ resbuf.Length = ACPI_ALLOCATE_BUFFER;
+ if (ACPI_FAILURE(acpica_get_handle(dip, &devobj)) ||
+ ACPI_FAILURE(AcpiGetCurrentResources(devobj, &resbuf)))
+ return;
+
+ ret = ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
+ "acpi-crs", resbuf.Pointer, resbuf.Length);
+
+ ASSERT(ret == DDI_PROP_SUCCESS);
+
+ AcpiOsFree(resbuf.Pointer);
+}
+
+/*
+ * If the supplied devinfo node has an ACPI settings property attached,
+ * restore them to the associated ACPI device using _SRS. The property
+ * is deleted from the devinfo node afterward.
+ */
+void
+acpica_ddi_restore_resources(dev_info_t *dip)
+{
+ ACPI_HANDLE devobj;
+ ACPI_BUFFER resbuf;
+ uchar_t *propdata;
+ uint_t proplen;
+
+ if (ACPI_FAILURE(acpica_get_handle(dip, &devobj)))
+ return;
+
+ if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "acpi-crs", &propdata, &proplen) != DDI_PROP_SUCCESS)
+ return;
+
+ resbuf.Pointer = propdata;
+ resbuf.Length = proplen;
+ (void) AcpiSetCurrentResources(devobj, &resbuf);
+ ddi_prop_free(propdata);
+ (void) ddi_prop_remove(DDI_DEV_T_ANY, dip, "acpi-crs");
+}
diff --git a/usr/src/uts/intel/io/agpgart/amd64_gart.c b/usr/src/uts/intel/io/agpgart/amd64_gart.c
index fbcebf781d..22d6ef3994 100644
--- a/usr/src/uts/intel/io/agpgart/amd64_gart.c
+++ b/usr/src/uts/intel/io/agpgart/amd64_gart.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -173,9 +173,18 @@ amd64_gart_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
int status;
char buf[80];
- if (cmd != DDI_ATTACH)
+ switch (cmd) {
+ default:
return (DDI_FAILURE);
+ case DDI_RESUME:
+ /* Nothing special is needed for resume. */
+ return (DDI_SUCCESS);
+
+ case DDI_ATTACH:
+ break;
+ }
+
instance = ddi_get_instance(dip);
if (ddi_soft_state_zalloc(amd64_gart_glob_soft_handle, instance) !=
@@ -209,9 +218,18 @@ amd64_gart_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
amd64_gart_softstate_t *sc;
char buf[80];
- if (cmd != DDI_DETACH)
+ switch (cmd) {
+ default:
return (DDI_FAILURE);
+ case DDI_SUSPEND:
+ /* Nothing special is needed for suspend */
+ return (DDI_SUCCESS);
+
+ case DDI_DETACH:
+ break;
+ }
+
instance = ddi_get_instance(dip);
sc = ddi_get_soft_state(amd64_gart_glob_soft_handle, instance);
diff --git a/usr/src/uts/intel/io/dktp/controller/ata/ata_cmd.h b/usr/src/uts/intel/io/dktp/controller/ata/ata_cmd.h
index 9483f1e9b0..3607ff1cda 100644
--- a/usr/src/uts/intel/io/dktp/controller/ata/ata_cmd.h
+++ b/usr/src/uts/intel/io/dktp/controller/ata/ata_cmd.h
@@ -2,7 +2,7 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
+ * Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 1996 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -45,6 +45,7 @@ extern "C" {
#define ATC_DOOR_LOCK 0xde /* door lock */
#define ATC_DOOR_UNLOCK 0xdf /* door unlock */
#define ATC_IDLE 0xe3 /* idle */
+#define ATC_SLEEP 0xe6 /* sleep */
/*
* ATA/ATAPI-4 disk commands.
diff --git a/usr/src/uts/intel/io/dktp/controller/ata/ata_common.c b/usr/src/uts/intel/io/dktp/controller/ata/ata_common.c
index 17294569fb..7fb8da9ff0 100644
--- a/usr/src/uts/intel/io/dktp/controller/ata/ata_common.c
+++ b/usr/src/uts/intel/io/dktp/controller/ata/ata_common.c
@@ -99,6 +99,12 @@ static int ata_check_revert_to_defaults(ata_drv_t *ata_drvp);
static void ata_show_transfer_mode(ata_ctl_t *, ata_drv_t *);
static int ata_spec_init_controller(dev_info_t *dip);
+static void ata_init_pm(dev_info_t *);
+static int ata_suspend(dev_info_t *);
+static int ata_resume(dev_info_t *);
+static int ata_power(dev_info_t *, int, int);
+static int ata_change_power(dev_info_t *, uint8_t);
+static int ata_is_pci(dev_info_t *);
/*
* Local static data
@@ -114,6 +120,21 @@ int ata_reset_bus_watchdog = 1000;
/*
+ * Use local or framework power management
+ */
+
+#ifdef ATA_USE_AUTOPM
+#define ATA_BUSY_COMPONENT(d, c) ((void)pm_busy_component(d, c))
+#define ATA_IDLE_COMPONENT(d, c) ((void)pm_idle_component(d, c))
+#define ATA_RAISE_POWER(d, c, l) pm_raise_power(d, c, l)
+#define ATA_LOWER_POWER(d, c, l) pm_lower_power(d, c, l)
+#else
+#define ATA_BUSY_COMPONENT(d, c)
+#define ATA_IDLE_COMPONENT(d, c)
+#define ATA_RAISE_POWER(d, c, l) ata_power(d, c, l)
+#define ATA_LOWER_POWER(d, c, l) ata_power(d, c, l)
+#endif
+/*
* number of seconds to wait during various operations
*/
int ata_flush_delay = 5 * 1000000;
@@ -228,7 +249,8 @@ ata_devo_reset(
if ((ata_drvp->ad_flags & AD_DISK) != 0 &&
((ata_drvp->ad_flags & AD_NORVRT) == 0)) {
/* Enable revert to defaults when reset */
- (void) ata_set_feature(ata_ctlp, ata_drvp, 0xCC, 0);
+ (void) ata_set_feature(ata_ctlp, ata_drvp,
+ ATSF_ENA_REVPOD, 0);
}
/*
@@ -245,7 +267,7 @@ ata_devo_reset(
*/
rc = ata_flush_cache(ata_ctlp, ata_drvp);
ADBG_WARN(("ata_flush_cache %s\n",
- rc ? "okay" : "failed"));
+ rc ? "okay" : "failed"));
if (!rc)
flush_okay = FALSE;
@@ -297,7 +319,8 @@ static struct dev_ops ata_ops = {
ata_detach, /* detach */
ata_devo_reset, /* reset */
&ata_cb_ops, /* driver operations */
- NULL /* bus operations */
+ NULL, /* bus operations */
+ ata_power /* power */
};
/* driver loadable module wrapper */
@@ -419,8 +442,14 @@ ata_attach(
debug_enter("\nATA_ATTACH\n\n");
#endif
- if (cmd != DDI_ATTACH)
+ switch (cmd) {
+ case DDI_ATTACH:
+ break;
+ case DDI_RESUME:
+ return (ata_resume(dip));
+ default:
return (DDI_FAILURE);
+ }
/* initialize controller */
ata_ctlp = ata_init_controller(dip);
@@ -476,14 +505,14 @@ ata_attach(
* confused by non-existent drives.
*/
ddi_put8(ata_ctlp->ac_iohandle1, ata_ctlp->ac_drvhd,
- first_drvp->ad_drive_bits);
+ first_drvp->ad_drive_bits);
ata_nsecwait(400);
/*
* make certain the drive selected
*/
if (!ata_wait(ata_ctlp->ac_iohandle2, ata_ctlp->ac_ioaddr2,
- 0, ATS_BSY, 5000000)) {
+ 0, ATS_BSY, 5000000)) {
ADBG_ERROR(("ata_attach: select failed\n"));
}
@@ -525,6 +554,8 @@ ata_attach(
ata_ctlp->ac_flags |= AC_ATTACHED;
mutex_exit(&ata_ctlp->ac_ccc.ccc_hba_mutex);
+ ata_init_pm(dip);
+
ddi_report_dev(dip);
return (DDI_SUCCESS);
@@ -550,8 +581,14 @@ ata_detach(
ADBG_TRACE(("ata_detach entered\n"));
- if (cmd != DDI_DETACH)
+ switch (cmd) {
+ case DDI_DETACH:
+ break;
+ case DDI_SUSPEND:
+ return (ata_suspend(dip));
+ default:
return (DDI_FAILURE);
+ }
instance = ddi_get_instance(dip);
ata_ctlp = ddi_get_soft_state(ata_state, instance);
@@ -559,6 +596,17 @@ ata_detach(
if (!ata_ctlp)
return (DDI_SUCCESS);
+ if (ata_ctlp->ac_pm_support) {
+ ATA_BUSY_COMPONENT(dip, 0);
+ if (ata_ctlp->ac_pm_level != PM_LEVEL_D0) {
+ if (ATA_RAISE_POWER(dip, 0, PM_LEVEL_D0) !=
+ DDI_SUCCESS) {
+ ATA_IDLE_COMPONENT(dip, 0);
+ return (DDI_FAILURE);
+ }
+ }
+ (void) ddi_prop_remove(DDI_DEV_T_NONE, dip, "pm-components");
+ }
ata_ctlp->ac_flags &= ~AC_ATTACHED;
/* destroy ata module */
@@ -641,8 +689,8 @@ ata_bus_ctl(
/* These ops shouldn't be called by a target driver */
ADBG_ERROR(("ata_bus_ctl: %s%d: invalid op (%d) from %s%d\n",
- ddi_driver_name(d), ddi_get_instance(d), o,
- ddi_driver_name(r), ddi_get_instance(r)));
+ ddi_driver_name(d), ddi_get_instance(d), o,
+ ddi_driver_name(r), ddi_get_instance(r)));
return (DDI_FAILURE);
@@ -683,7 +731,7 @@ ata_bus_ctl(
target_type = ATA_DEV_ATAPI;
else {
ADBG_WARN(("ata_bus_ctl: invalid target class %s\n",
- bufp));
+ bufp));
ddi_prop_free(bufp);
return (DDI_FAILURE);
}
@@ -710,17 +758,17 @@ ata_bus_ctl(
/* get (target,lun) of child device */
targ = ddi_prop_get_int(DDI_DEV_T_ANY, tdip, DDI_PROP_DONTPASS,
- "target", -1);
+ "target", -1);
if (targ == -1) {
ADBG_WARN(("ata_bus_ctl: failed to get targ num\n"));
return (DDI_FAILURE);
}
lun = ddi_prop_get_int(DDI_DEV_T_ANY, tdip, DDI_PROP_DONTPASS,
- "lun", 0);
+ "lun", 0);
if ((targ < 0) || (targ >= ATA_MAXTARG) ||
- (lun < 0) || (lun >= ATA_MAXLUN)) {
+ (lun < 0) || (lun >= ATA_MAXLUN)) {
return (DDI_FAILURE);
}
@@ -743,7 +791,7 @@ ata_bus_ctl(
if (strcmp(ddi_get_name(tdip), "cmdk") == 0) {
if ((target_type == ATA_DEV_DISK) &&
- (target_type != drive_type))
+ (target_type != drive_type))
return (DDI_FAILURE);
target_type = drive_type;
@@ -757,9 +805,9 @@ ata_bus_ctl(
if (ndi_prop_update_string(DDI_DEV_T_NONE, tdip,
"disk", disk_prop) != DDI_PROP_SUCCESS) {
ADBG_WARN(("ata_bus_ctl: failed to "
- "create disk prop\n"));
+ "create disk prop\n"));
return (DDI_FAILURE);
- }
+ }
}
if (ndi_prop_update_string(DDI_DEV_T_NONE, tdip,
@@ -820,7 +868,7 @@ ata_hba_complete(
ata_pktp = GCMD2APKT(gcmdp);
if (ata_pktp->ap_complete)
(*ata_pktp->ap_complete)(ata_drvp, ata_pktp,
- do_callback);
+ do_callback);
}
/* GHD ccc_timeout_func callback */
@@ -919,7 +967,7 @@ ata_init_controller(
if (ata_ctlp == NULL) {
ADBG_WARN(("ata_init_controller: failed to find "
- "controller struct\n"));
+ "controller struct\n"));
return (NULL);
}
@@ -933,14 +981,14 @@ ata_init_controller(
* map the device registers
*/
if (!ata_setup_ioaddr(dip, &ata_ctlp->ac_iohandle1, &ioaddr1,
- &ata_ctlp->ac_iohandle2, &ioaddr2,
- &ata_ctlp->ac_bmhandle, &ata_ctlp->ac_bmaddr)) {
+ &ata_ctlp->ac_iohandle2, &ioaddr2,
+ &ata_ctlp->ac_bmhandle, &ata_ctlp->ac_bmaddr)) {
(void) ata_detach(dip, DDI_DETACH);
return (NULL);
}
ADBG_INIT(("ata_init_controller: ioaddr1 = 0x%p, ioaddr2 = 0x%p\n",
- ioaddr1, ioaddr2));
+ ioaddr1, ioaddr2));
/*
* Do ARQ setup
@@ -980,12 +1028,12 @@ ata_init_controller(
* drop after a resume.
*/
ata_ctlp->ac_timing_flags = ddi_prop_get_int(DDI_DEV_T_ANY,
- dip, DDI_PROP_DONTPASS, "timing_flags", 0);
+ dip, DDI_PROP_DONTPASS, "timing_flags", 0);
/*
* get max transfer size, default to 256 sectors
*/
ata_ctlp->ac_max_transfer = ddi_prop_get_int(DDI_DEV_T_ANY,
- dip, DDI_PROP_DONTPASS, "max_transfer", 0x100);
+ dip, DDI_PROP_DONTPASS, "max_transfer", 0x100);
if (ata_ctlp->ac_max_transfer < 1)
ata_ctlp->ac_max_transfer = 1;
if (ata_ctlp->ac_max_transfer > 0x100)
@@ -995,7 +1043,7 @@ ata_init_controller(
* Get the standby timer value
*/
ata_ctlp->ac_standby_time = ddi_prop_get_int(DDI_DEV_T_ANY,
- dip, DDI_PROP_DONTPASS, "standby", -1);
+ dip, DDI_PROP_DONTPASS, "standby", -1);
/*
* If this is a /pci/pci-ide instance check to see if
@@ -1011,9 +1059,9 @@ ata_init_controller(
return (NULL);
}
(void) sprintf(prop_buf, "SUNW-ata-%04x-isa",
- addr1);
+ addr1);
if (ddi_prop_exists(DDI_DEV_T_ANY, ddi_root_node(),
- DDI_PROP_DONTPASS, prop_buf)) {
+ DDI_PROP_DONTPASS, prop_buf)) {
(void) ata_detach(dip, DDI_DETACH);
return (NULL);
}
@@ -1029,11 +1077,11 @@ ata_init_controller(
GHD_WAITQ_INIT(&ata_ctlp->ac_ccc.ccc_waitq, NULL, 1);
if (!ghd_register("ata", &ata_ctlp->ac_ccc, dip, 0, ata_ctlp,
- atapi_ccballoc, atapi_ccbfree,
- ata_pciide_dma_sg_func, ata_hba_start,
- ata_hba_complete, ata_intr,
- ata_get_status, ata_process_intr, ata_timeout_func,
- &ata_timer_conf, NULL)) {
+ atapi_ccballoc, atapi_ccbfree,
+ ata_pciide_dma_sg_func, ata_hba_start,
+ ata_hba_complete, ata_intr,
+ ata_get_status, ata_process_intr, ata_timeout_func,
+ &ata_timer_conf, NULL)) {
(void) ata_detach(dip, DDI_DETACH);
return (NULL);
}
@@ -1094,7 +1142,7 @@ ata_init_drive(
int valid_version = 0;
ADBG_TRACE(("ata_init_drive entered, targ = %d, lun = %d\n",
- targ, lun));
+ targ, lun));
/* check if device already exists */
@@ -1114,7 +1162,7 @@ ata_init_drive(
ata_drvp->ad_ctlp = ata_ctlp;
ata_drvp->ad_targ = targ;
ata_drvp->ad_drive_bits =
- (ata_drvp->ad_targ == 0 ? ATDH_DRIVE0 : ATDH_DRIVE1);
+ (ata_drvp->ad_targ == 0 ? ATDH_DRIVE0 : ATDH_DRIVE1);
/*
* Add the LUN for SFF-8070i support
*/
@@ -1127,11 +1175,11 @@ ata_init_drive(
*/
drive_type = ata_drive_type(ata_drvp->ad_drive_bits,
- ata_ctlp->ac_iohandle1,
- ata_ctlp->ac_ioaddr1,
- ata_ctlp->ac_iohandle2,
- ata_ctlp->ac_ioaddr2,
- aidp);
+ ata_ctlp->ac_iohandle1,
+ ata_ctlp->ac_ioaddr1,
+ ata_ctlp->ac_iohandle2,
+ ata_ctlp->ac_ioaddr2,
+ aidp);
switch (drive_type) {
case ATA_DEV_NONE:
@@ -1150,11 +1198,11 @@ ata_init_drive(
*/
if (!ata_strncmp(nec_260, aidp->ai_model, sizeof (aidp->ai_model))) {
swab(aidp->ai_drvser, aidp->ai_drvser,
- sizeof (aidp->ai_drvser));
+ sizeof (aidp->ai_drvser));
swab(aidp->ai_fw, aidp->ai_fw,
- sizeof (aidp->ai_fw));
+ sizeof (aidp->ai_fw));
swab(aidp->ai_model, aidp->ai_model,
- sizeof (aidp->ai_model));
+ sizeof (aidp->ai_model));
}
/*
@@ -1177,8 +1225,8 @@ ata_init_drive(
buf[i] = '\0';
ATAPRT(("?\t%s device at targ %d, lun %d lastlun 0x%x\n",
- (ATAPIDRV(ata_drvp) ? "ATAPI":"IDE"),
- ata_drvp->ad_targ, ata_drvp->ad_lun, aidp->ai_lastlun));
+ (ATAPIDRV(ata_drvp) ? "ATAPI":"IDE"),
+ ata_drvp->ad_targ, ata_drvp->ad_lun, aidp->ai_lastlun));
ATAPRT(("?\tmodel %s\n", buf));
@@ -1191,21 +1239,21 @@ ata_init_drive(
}
ATAPRT((
"?\tATA/ATAPI-%d supported, majver 0x%x minver 0x%x\n",
- valid_version,
- aidp->ai_majorversion,
- aidp->ai_minorversion));
+ valid_version,
+ aidp->ai_majorversion,
+ aidp->ai_minorversion));
}
if (ata_capability_data) {
ATAPRT(("?\t\tstat %x, err %x\n",
- ddi_get8(ata_ctlp->ac_iohandle2,
- ata_ctlp->ac_altstatus),
- ddi_get8(ata_ctlp->ac_iohandle1, ata_ctlp->ac_error)));
+ ddi_get8(ata_ctlp->ac_iohandle2,
+ ata_ctlp->ac_altstatus),
+ ddi_get8(ata_ctlp->ac_iohandle1, ata_ctlp->ac_error)));
ATAPRT(("?\t\tcfg 0x%x, cap 0x%x\n",
- aidp->ai_config,
- aidp->ai_cap));
+ aidp->ai_config,
+ aidp->ai_cap));
/*
* Be aware that ATA-6 and later drives may not provide valid
@@ -1220,34 +1268,34 @@ ata_init_drive(
* Supported version less then ATA-6
*/
ATAPRT(("?\t\tcyl %d, hd %d, sec/trk %d\n",
- aidp->ai_fixcyls,
- aidp->ai_heads,
- aidp->ai_sectors));
+ aidp->ai_fixcyls,
+ aidp->ai_heads,
+ aidp->ai_sectors));
}
ATAPRT(("?\t\tmult1 0x%x, mult2 0x%x\n",
- aidp->ai_mult1,
- aidp->ai_mult2));
+ aidp->ai_mult1,
+ aidp->ai_mult2));
if (valid_version && aidp->ai_majorversion < ATAC_MAJVER_4) {
ATAPRT((
"?\t\tpiomode 0x%x, dmamode 0x%x, advpiomode 0x%x\n",
- aidp->ai_piomode,
- aidp->ai_dmamode,
- aidp->ai_advpiomode));
+ aidp->ai_piomode,
+ aidp->ai_dmamode,
+ aidp->ai_advpiomode));
} else {
ATAPRT(("?\t\tadvpiomode 0x%x\n",
- aidp->ai_advpiomode));
+ aidp->ai_advpiomode));
}
ATAPRT(("?\t\tminpio %d, minpioflow %d\n",
- aidp->ai_minpio,
- aidp->ai_minpioflow));
+ aidp->ai_minpio,
+ aidp->ai_minpioflow));
if (valid_version && aidp->ai_majorversion >= ATAC_MAJVER_4 &&
(aidp->ai_validinfo & ATAC_VALIDINFO_83)) {
ATAPRT(("?\t\tdwdma 0x%x, ultradma 0x%x\n",
- aidp->ai_dworddma,
- aidp->ai_ultradma));
+ aidp->ai_dworddma,
+ aidp->ai_ultradma));
} else {
ATAPRT(("?\t\tdwdma 0x%x\n",
- aidp->ai_dworddma));
+ aidp->ai_dworddma));
}
}
@@ -1268,7 +1316,7 @@ ata_init_drive(
* lock the drive's current settings in case I have to
* reset the drive due to some sort of error
*/
- (void) ata_set_feature(ata_ctlp, ata_drvp, 0x66, 0);
+ (void) ata_set_feature(ata_ctlp, ata_drvp, ATSF_DIS_REVPOD, 0);
return (ata_drvp);
@@ -1299,14 +1347,14 @@ ata_uninit_drive(
* Select the correct drive
*/
ddi_put8(ata_ctlp->ac_iohandle1, ata_ctlp->ac_drvhd,
- ata_drvp->ad_drive_bits);
+ ata_drvp->ad_drive_bits);
ata_nsecwait(400);
/*
* Disable interrupts from the drive
*/
ddi_put8(ata_ctlp->ac_iohandle2, ata_ctlp->ac_devctl,
- (ATDC_D3 | ATDC_NIEN));
+ (ATDC_D3 | ATDC_NIEN));
#endif
/* interface specific clean-ups */
@@ -1352,7 +1400,7 @@ ata_drive_type(
* make certain the drive is selected, and wait for not busy
*/
(void) ata_wait3(io_hdl2, ioaddr2, 0, ATS_BSY, 0x7f, 0, 0x7f, 0,
- 5 * 1000000);
+ 5 * 1000000);
status = ddi_get8(io_hdl2, (uchar_t *)ioaddr2 + AT_ALTSTATUS);
@@ -1481,12 +1529,12 @@ ata_wait3(
* check for error conditions
*/
if ((val & failure_onbits2) == failure_onbits2 &&
- (val & failure_offbits2) == 0) {
+ (val & failure_offbits2) == 0) {
return (FALSE);
}
if ((val & failure_onbits3) == failure_onbits3 &&
- (val & failure_offbits3) == 0) {
+ (val & failure_offbits3) == 0) {
return (FALSE);
}
@@ -1564,7 +1612,7 @@ ata_id_common(
* make sure we give them enough time to respond.
*/
(void) ata_wait3(io_hdl2, ioaddr2, 0, ATS_BSY,
- ATS_ERR, ATS_BSY, 0x7f, 0, 5 * 1000000);
+ ATS_ERR, ATS_BSY, 0x7f, 0, 5 * 1000000);
/*
* read the status byte and clear the pending interrupt
@@ -1581,8 +1629,8 @@ ata_id_common(
if (status & ATS_BSY) {
ADBG_ERROR(("ata_id_common: BUSY status 0x%x error 0x%x\n",
- ddi_get8(io_hdl2, (uchar_t *)ioaddr2 +AT_ALTSTATUS),
- ddi_get8(io_hdl1, (uchar_t *)ioaddr1 + AT_ERROR)));
+ ddi_get8(io_hdl2, (uchar_t *)ioaddr2 +AT_ALTSTATUS),
+ ddi_get8(io_hdl1, (uchar_t *)ioaddr1 + AT_ERROR)));
return (FALSE);
}
@@ -1596,8 +1644,8 @@ ata_id_common(
*/
if (!ata_wait(io_hdl2, ioaddr2, ATS_DRQ, ATS_BSY, 1000000)) {
ADBG_WARN(("ata_id_common: !DRQ status 0x%x error 0x%x\n",
- ddi_get8(io_hdl2, (uchar_t *)ioaddr2 +AT_ALTSTATUS),
- ddi_get8(io_hdl1, (uchar_t *)ioaddr1 + AT_ERROR)));
+ ddi_get8(io_hdl2, (uchar_t *)ioaddr2 +AT_ALTSTATUS),
+ ddi_get8(io_hdl1, (uchar_t *)ioaddr1 + AT_ERROR)));
return (FALSE);
}
}
@@ -1606,7 +1654,7 @@ ata_id_common(
* transfer the data
*/
ddi_rep_get16(io_hdl1, (ushort_t *)aidp, (ushort_t *)ioaddr1 + AT_DATA,
- NBPSCTR >> 1, DDI_DEV_NO_AUTOINCR);
+ NBPSCTR >> 1, DDI_DEV_NO_AUTOINCR);
/* wait for the busy bit to settle */
ata_nsecwait(400);
@@ -1624,10 +1672,10 @@ ata_id_common(
*
*/
if (!ata_wait(io_hdl2, ioaddr2, (uchar_t)(expect_drdy ? ATS_DRDY : 0),
- (ATS_BSY | ATS_DRQ), 1000000)) {
+ (ATS_BSY | ATS_DRQ), 1000000)) {
ADBG_WARN(("ata_id_common: bad status 0x%x error 0x%x\n",
- ddi_get8(io_hdl2, (uchar_t *)ioaddr2 + AT_ALTSTATUS),
- ddi_get8(io_hdl1, (uchar_t *)ioaddr1 + AT_ERROR)));
+ ddi_get8(io_hdl2, (uchar_t *)ioaddr2 + AT_ALTSTATUS),
+ ddi_get8(io_hdl1, (uchar_t *)ioaddr1 + AT_ERROR)));
return (FALSE);
}
@@ -1639,8 +1687,8 @@ ata_id_common(
*/
if (status & (ATS_DF | ATS_ERR)) {
ADBG_WARN(("ata_id_common: status 0x%x error 0x%x \n",
- ddi_get8(io_hdl2, (uchar_t *)ioaddr2 + AT_ALTSTATUS),
- ddi_get8(io_hdl1, (uchar_t *)ioaddr1 + AT_ERROR)));
+ ddi_get8(io_hdl2, (uchar_t *)ioaddr2 + AT_ALTSTATUS),
+ ddi_get8(io_hdl1, (uchar_t *)ioaddr1 + AT_ERROR)));
return (FALSE);
}
return (TRUE);
@@ -1677,13 +1725,13 @@ ata_command(
/* make certain the drive selected */
if (!ata_wait(io_hdl2, ata_ctlp->ac_ioaddr2,
- (uchar_t)(expect_drdy ? ATS_DRDY : 0),
- ATS_BSY, busy_wait)) {
+ (uchar_t)(expect_drdy ? ATS_DRDY : 0),
+ ATS_BSY, busy_wait)) {
ADBG_ERROR(("ata_command: select failed "
- "DRDY 0x%x CMD 0x%x F 0x%x N 0x%x "
- "S 0x%x H 0x%x CL 0x%x CH 0x%x\n",
- expect_drdy, cmd, feature, count,
- sector, head, cyl_low, cyl_hi));
+ "DRDY 0x%x CMD 0x%x F 0x%x N 0x%x "
+ "S 0x%x H 0x%x CL 0x%x CH 0x%x\n",
+ expect_drdy, cmd, feature, count,
+ sector, head, cyl_low, cyl_hi));
return (FALSE);
}
@@ -1706,10 +1754,10 @@ ata_command(
/* wait for not busy */
if (!ata_wait(io_hdl2, ata_ctlp->ac_ioaddr2, 0, ATS_BSY, busy_wait)) {
ADBG_ERROR(("ata_command: BSY too long!"
- "DRDY 0x%x CMD 0x%x F 0x%x N 0x%x "
- "S 0x%x H 0x%x CL 0x%x CH 0x%x\n",
- expect_drdy, cmd, feature, count,
- sector, head, cyl_low, cyl_hi));
+ "DRDY 0x%x CMD 0x%x F 0x%x N 0x%x "
+ "S 0x%x H 0x%x CL 0x%x CH 0x%x\n",
+ expect_drdy, cmd, feature, count,
+ sector, head, cyl_low, cyl_hi));
return (FALSE);
}
@@ -1717,10 +1765,10 @@ ata_command(
* wait for DRDY before continuing
*/
(void) ata_wait3(io_hdl2, ata_ctlp->ac_ioaddr2,
- ATS_DRDY, ATS_BSY, /* okay */
- ATS_ERR, ATS_BSY, /* cmd failed */
- ATS_DF, ATS_BSY, /* drive failed */
- busy_wait);
+ ATS_DRDY, ATS_BSY, /* okay */
+ ATS_ERR, ATS_BSY, /* cmd failed */
+ ATS_DF, ATS_BSY, /* drive failed */
+ busy_wait);
/* read status to clear IRQ, and check for error */
status = ddi_get8(io_hdl1, ata_ctlp->ac_status);
@@ -1730,12 +1778,12 @@ ata_command(
if (!silent) {
ADBG_ERROR(("ata_command status 0x%x error 0x%x "
- "DRDY 0x%x CMD 0x%x F 0x%x N 0x%x "
- "S 0x%x H 0x%x CL 0x%x CH 0x%x\n",
- ddi_get8(io_hdl1, ata_ctlp->ac_status),
- ddi_get8(io_hdl1, ata_ctlp->ac_error),
- expect_drdy, cmd, feature, count,
- sector, head, cyl_low, cyl_hi));
+ "DRDY 0x%x CMD 0x%x F 0x%x N 0x%x "
+ "S 0x%x H 0x%x CL 0x%x CH 0x%x\n",
+ ddi_get8(io_hdl1, ata_ctlp->ac_status),
+ ddi_get8(io_hdl1, ata_ctlp->ac_error),
+ expect_drdy, cmd, feature, count,
+ sector, head, cyl_low, cyl_hi));
}
return (FALSE);
}
@@ -1758,8 +1806,8 @@ ata_set_feature(
int rc;
rc = ata_command(ata_ctlp, ata_drvp, TRUE, TRUE, ata_set_feature_wait,
- ATC_SET_FEAT, feature, value, 0, 0, 0, 0);
- /* feature, count, sector, head, cyl_low, cyl_hi */
+ ATC_SET_FEAT, feature, value, 0, 0, 0, 0);
+ /* feature, count, sector, head, cyl_low, cyl_hi */
if (rc) {
return (TRUE);
@@ -1784,8 +1832,8 @@ ata_flush_cache(
{
/* this command is optional so fail silently */
return (ata_command(ata_ctlp, ata_drvp, TRUE, TRUE,
- ata_flush_cache_wait,
- ATC_FLUSH_CACHE, 0, 0, 0, 0, 0, 0));
+ ata_flush_cache_wait,
+ ATC_FLUSH_CACHE, 0, 0, 0, 0, 0, 0));
}
/*
@@ -1812,7 +1860,6 @@ ata_setup_ioaddr(
caddr_t *bm_addrp)
{
ddi_device_acc_attr_t dev_attr;
- char *bufp;
int rnumber;
int rc;
off_t regsize;
@@ -1824,14 +1871,14 @@ ata_setup_ioaddr(
rc = ddi_dev_regsize(dip, 0, &regsize);
if (rc != DDI_SUCCESS || regsize <= AT_CMD) {
ADBG_INIT(("ata_setup_ioaddr(1): rc %d regsize %lld\n",
- rc, (long long)regsize));
+ rc, (long long)regsize));
return (FALSE);
}
rc = ddi_dev_regsize(dip, 1, &regsize);
if (rc != DDI_SUCCESS || regsize <= AT_ALTSTATUS) {
ADBG_INIT(("ata_setup_ioaddr(2): rc %d regsize %lld\n",
- rc, (long long)regsize));
+ rc, (long long)regsize));
return (FALSE);
}
@@ -1859,24 +1906,15 @@ ata_setup_ioaddr(
/* else, it's ISA or PCI-IDE, check further */
rnumber = 0;
- rc = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_get_parent(dip),
- DDI_PROP_DONTPASS, "device_type", &bufp);
- if (rc != DDI_PROP_SUCCESS) {
- ADBG_ERROR(("ata_setup_ioaddr !device_type\n"));
- goto not_pciide;
- }
-
- if (strcmp(bufp, "pci-ide") != 0) {
+ if (!ata_is_pci(dip)) {
/*
* If it's not a PCI-IDE, there are only two reg tuples
* and the first one contains the I/O base (170 or 1f0)
* rather than the controller instance number.
*/
ADBG_TRACE(("ata_setup_ioaddr !pci-ide\n"));
- ddi_prop_free(bufp);
goto not_pciide;
}
- ddi_prop_free(bufp);
/*
@@ -1889,7 +1927,7 @@ ata_setup_ioaddr(
rc = ddi_dev_regsize(dip, 2, &regsize);
if (rc != DDI_SUCCESS || regsize < 8) {
ADBG_INIT(("ata_setup_ioaddr(3): rc %d regsize %lld\n",
- rc, (long long)regsize));
+ rc, (long long)regsize));
goto not_pciide;
}
@@ -1898,7 +1936,7 @@ ata_setup_ioaddr(
if (rc != DDI_SUCCESS) {
/* map failed, try to use in non-pci-ide mode */
ADBG_WARN(("ata_setup_ioaddr bus master map failed, rc=0x%x\n",
- rc));
+ rc));
*bm_hdlp = NULL;
}
@@ -1908,7 +1946,7 @@ not_pciide:
*/
rc = ddi_regs_map_setup(dip, rnumber, addr1p, 0, 0, &dev_attr,
- handle1p);
+ handle1p);
if (rc != DDI_SUCCESS) {
cmn_err(CE_WARN, "ata: reg tuple 0 map failed, rc=0x%x\n", rc);
@@ -1930,7 +1968,7 @@ not_pciide:
* map the upper control block registers
*/
rc = ddi_regs_map_setup(dip, rnumber + 1, addr2p, 0, 0, &dev_attr,
- handle2p);
+ handle2p);
if (rc == DDI_SUCCESS)
return (TRUE);
@@ -2010,7 +2048,7 @@ ata_init_pciide(
if (ata_check_pciide_blacklist(dip, ATA_BL_NODMA)) {
ata_ctlp->ac_pciide_bm = FALSE;
ata_cntrl_DMA_sel_msg =
- "cntrl blacklisted/DMA engine broken";
+ "cntrl blacklisted/DMA engine broken";
return;
}
@@ -2023,11 +2061,11 @@ ata_init_pciide(
*/
class_code = ddi_prop_get_int(DDI_DEV_T_ANY, ddi_get_parent(dip),
- DDI_PROP_DONTPASS, "class-code", 0);
+ DDI_PROP_DONTPASS, "class-code", 0);
if ((class_code & PCIIDE_BM_CAP_MASK) != PCIIDE_BM_CAP_MASK) {
ata_ctlp->ac_pciide_bm = FALSE;
ata_cntrl_DMA_sel_msg =
- "cntrl not Bus Master DMA capable";
+ "cntrl not Bus Master DMA capable";
return;
}
@@ -2036,7 +2074,7 @@ ata_init_pciide(
* between channels
*/
status = ddi_get8(ata_ctlp->ac_bmhandle,
- (uchar_t *)ata_ctlp->ac_bmaddr + PCIIDE_BMISX_REG);
+ (uchar_t *)ata_ctlp->ac_bmaddr + PCIIDE_BMISX_REG);
/*
* Some motherboards have CSB5's that are wired "to emulate CSB4 mode".
* In such a mode, the simplex bit is asserted, but in fact testing
@@ -2138,7 +2176,7 @@ ata_init_drive_pcidma(
}
ata_options = ddi_prop_get_int(DDI_DEV_T_ANY, ata_ctlp->ac_dip,
- 0, "ata-options", 0);
+ 0, "ata-options", 0);
if (!(ata_options & ATA_OPTIONS_DMA)) {
/*
@@ -2146,7 +2184,7 @@ ata_init_drive_pcidma(
* DMA is not enabled by this property
*/
ata_dev_DMA_sel_msg =
- "disabled by \"ata-options\" property";
+ "disabled by \"ata-options\" property";
return (ATA_DMA_OFF);
}
@@ -2178,13 +2216,13 @@ ata_init_drive_pcidma(
}
dma = ata_prop_lookup_int(DDI_DEV_T_ANY, tdip,
- 0, "ata-dma-enabled", TRUE);
+ 0, "ata-dma-enabled", TRUE);
disk_dma = ata_prop_lookup_int(DDI_DEV_T_ANY, tdip,
- 0, "ata-disk-dma-enabled", TRUE);
+ 0, "ata-disk-dma-enabled", TRUE);
cd_dma = ata_prop_lookup_int(DDI_DEV_T_ANY, tdip,
- 0, "atapi-cd-dma-enabled", FALSE);
+ 0, "atapi-cd-dma-enabled", FALSE);
atapi_dma = ata_prop_lookup_int(DDI_DEV_T_ANY, tdip,
- 0, "atapi-other-dma-enabled", TRUE);
+ 0, "atapi-other-dma-enabled", TRUE);
if (dma == FALSE) {
cmn_err(CE_CONT, "?ata_init_drive_pcidma: "
@@ -2288,7 +2326,7 @@ ata_prop_create(
if (strcmp("atapi", name) == 0) {
rc = ndi_prop_update_string(DDI_DEV_T_NONE, tgt_dip,
- "variant", name);
+ "variant", name);
if (rc != DDI_PROP_SUCCESS)
return (FALSE);
}
@@ -2297,7 +2335,7 @@ ata_prop_create(
return (TRUE);
rc = ndi_prop_update_byte_array(DDI_DEV_T_NONE, tgt_dip, name,
- (uchar_t *)&ata_drvp->ad_id, sizeof (ata_drvp->ad_id));
+ (uchar_t *)&ata_drvp->ad_id, sizeof (ata_drvp->ad_id));
if (rc != DDI_PROP_SUCCESS) {
ADBG_ERROR(("ata_prop_create failed, rc=%d\n", rc));
}
@@ -2469,7 +2507,7 @@ ata_ctlr_fsm(
* Start ARQ pkt if necessary
*/
if ((ata_pktp->ap_flags & AP_ARQ_NEEDED) == AP_ARQ_NEEDED &&
- (ata_pktp->ap_status & ATS_ERR)) {
+ (ata_pktp->ap_status & ATS_ERR)) {
/* set controller state back to active */
ata_ctlp->ac_state = current_state;
@@ -2552,7 +2590,7 @@ ata_start_arq(
arq_pktp->ap_resid = senselen;
arq_pktp->ap_flags = AP_ATAPI | AP_READ;
arq_pktp->ap_cdb_pad =
- ((unsigned)(ata_drvp->ad_cdb_len - arq_pktp->ap_cdb_len)) >> 1;
+ ((unsigned)(ata_drvp->ad_cdb_len - arq_pktp->ap_cdb_len)) >> 1;
bytes = min(senselen, ATAPI_MAX_BYTES_PER_DRQ);
arq_pktp->ap_hicyl = (uchar_t)(bytes >> 8);
@@ -2591,7 +2629,7 @@ ata_reset_bus(
fsm_func = ATA_FSM_RESET;
for (watchdog = ata_reset_bus_watchdog; watchdog > 0; watchdog--) {
switch (ata_ctlr_fsm(fsm_func, ata_ctlp, NULL, NULL,
- &DoneFlg)) {
+ &DoneFlg)) {
case ATA_FSM_RC_OKAY:
rc = TRUE;
goto fsm_done;
@@ -2737,7 +2775,7 @@ wait_for_not_busy:
*/
usecs_left = (deadline - gethrtime()) / 1000;
(void) ata_wait3(io_hdl2, ata_ctlp->ac_ioaddr2, 0, ATS_BSY,
- ATS_ERR, ATS_BSY, ATS_DF, ATS_BSY, usecs_left);
+ ATS_ERR, ATS_BSY, ATS_DF, ATS_BSY, usecs_left);
return (TRUE);
}
@@ -2987,7 +3025,7 @@ ata_hba_start(
request_started = FALSE;
for (watchdog = ata_hba_start_watchdog; watchdog > 0; watchdog--) {
switch (ata_ctlr_fsm(fsm_func, ata_ctlp, ata_drvp, ata_pktp,
- NULL)) {
+ NULL)) {
case ATA_FSM_RC_OKAY:
request_started = TRUE;
goto fsm_done;
@@ -3037,15 +3075,15 @@ ata_check_pciide_blacklist(
vendorid = ddi_prop_get_int(DDI_DEV_T_ANY, ddi_get_parent(dip),
- DDI_PROP_DONTPASS, "vendor-id", 0);
+ DDI_PROP_DONTPASS, "vendor-id", 0);
deviceid = ddi_prop_get_int(DDI_DEV_T_ANY, ddi_get_parent(dip),
- DDI_PROP_DONTPASS, "device-id", 0);
+ DDI_PROP_DONTPASS, "device-id", 0);
/*
* first check for a match in the "pci-ide-blacklist" property
*/
rc = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 0,
- "pci-ide-blacklist", &propp, &count);
+ "pci-ide-blacklist", &propp, &count);
if (rc == DDI_PROP_SUCCESS) {
count = (count * sizeof (uint_t)) / sizeof (pcibl_t);
@@ -3053,12 +3091,12 @@ ata_check_pciide_blacklist(
while (count--) {
/* check for matching ID */
if ((vendorid & blp->b_vmask)
- != (blp->b_vendorid & blp->b_vmask)) {
+ != (blp->b_vendorid & blp->b_vmask)) {
blp++;
continue;
}
if ((deviceid & blp->b_dmask)
- != (blp->b_deviceid & blp->b_dmask)) {
+ != (blp->b_deviceid & blp->b_dmask)) {
blp++;
continue;
}
@@ -3099,7 +3137,7 @@ ata_check_drive_blacklist(
for (blp = ata_drive_blacklist; blp->b_model; blp++) {
if (!ata_strncmp(blp->b_model, aidp->ai_model,
- sizeof (aidp->ai_model)))
+ sizeof (aidp->ai_model)))
continue;
if (blp->b_flags & flags)
return (TRUE);
@@ -3152,7 +3190,7 @@ ata_queue_cmd(
* ap_start function is called.
*/
rc = ghd_transport(&ata_ctlp->ac_ccc, gcmdp, gcmdp->cmd_gtgtp,
- 0, TRUE, NULL);
+ 0, TRUE, NULL);
if (rc != TRAN_ACCEPT) {
/* this should never, ever happen */
@@ -3230,7 +3268,7 @@ ata_check_revert_to_defaults(
/* look for a disk-specific "revert" property" */
propval = ddi_getprop(DDI_DEV_T_ANY, ata_ctlp->ac_dip,
- DDI_PROP_DONTPASS, prop_buf, -1);
+ DDI_PROP_DONTPASS, prop_buf, -1);
if (propval == 0)
return (FALSE);
else if (propval != -1)
@@ -3238,7 +3276,7 @@ ata_check_revert_to_defaults(
/* look for a global "revert" property" */
propval = ddi_getprop(DDI_DEV_T_ANY, ata_ctlp->ac_dip,
- 0, ATA_REVERT_PROP_GLOBAL, -1);
+ 0, ATA_REVERT_PROP_GLOBAL, -1);
if (propval == 0)
return (FALSE);
else if (propval != -1)
@@ -3262,7 +3300,7 @@ ata_show_transfer_mode(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp)
}
ATAPRT(("?\tPIO mode %d selected\n",
(ata_drvp->ad_id.ai_advpiomode & ATAC_ADVPIO_4_SUP) ==
- ATAC_ADVPIO_4_SUP ? 4 : 3));
+ ATAC_ADVPIO_4_SUP ? 4 : 3));
} else {
/* Using DMA */
if (ata_drvp->ad_id.ai_dworddma & ATAC_MDMA_SEL_MASK) {
@@ -3271,10 +3309,10 @@ ata_show_transfer_mode(ata_ctl_t *ata_ctlp, ata_drv_t *ata_drvp)
* selected, not both.
*/
ATAPRT(("?\tMultiwordDMA mode %d selected\n",
- (ata_drvp->ad_id.ai_dworddma & ATAC_MDMA_2_SEL) ==
+ (ata_drvp->ad_id.ai_dworddma & ATAC_MDMA_2_SEL) ==
ATAC_MDMA_2_SEL ? 2 :
(ata_drvp->ad_id.ai_dworddma & ATAC_MDMA_1_SEL) ==
- ATAC_MDMA_1_SEL ? 1 : 0));
+ ATAC_MDMA_1_SEL ? 1 : 0));
} else {
for (i = 0; i <= 6; i++) {
if (ata_drvp->ad_id.ai_ultradma &
@@ -3330,9 +3368,9 @@ ata_spec_init_controller(dev_info_t *dip)
struct ata_ctl_spec *ctlsp;
vendor_id = ddi_prop_get_int(DDI_DEV_T_ANY, ddi_get_parent(dip),
- DDI_PROP_DONTPASS, "vendor-id", 0);
+ DDI_PROP_DONTPASS, "vendor-id", 0);
device_id = ddi_prop_get_int(DDI_DEV_T_ANY, ddi_get_parent(dip),
- DDI_PROP_DONTPASS, "device-id", 0);
+ DDI_PROP_DONTPASS, "device-id", 0);
/* Locate controller specific ops, if they exist */
ctlsp = ata_cntrls_spec;
@@ -3375,7 +3413,7 @@ ata_prop_lookup_int(dev_t match_dev, dev_info_t *dip,
int proprc;
proprc = ddi_prop_lookup_string(match_dev, dip,
- flags, name, &bufp);
+ flags, name, &bufp);
if (proprc == DDI_PROP_SUCCESS) {
cp = bufp;
@@ -3390,3 +3428,287 @@ ata_prop_lookup_int(dev_t match_dev, dev_info_t *dip,
return (rc);
}
+
+/*
+ * Initialize the power management components
+ */
+static void
+ata_init_pm(dev_info_t *dip)
+{
+ char pmc_name[16];
+ char *pmc[] = {
+ NULL,
+ "0=Sleep (PCI D3 State)",
+ "3=PowerOn (PCI D0 State)",
+ NULL
+ };
+ int instance;
+ ata_ctl_t *ata_ctlp;
+
+
+ instance = ddi_get_instance(dip);
+ ata_ctlp = ddi_get_soft_state(ata_state, instance);
+ ata_ctlp->ac_pm_support = 0;
+
+ /* check PCI capabilities */
+ if (!ata_is_pci(dip))
+ return;
+
+ (void) sprintf(pmc_name, "NAME=ata%d", instance);
+ pmc[0] = pmc_name;
+
+#ifdef ATA_USE_AUTOPM
+ if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
+ "pm-components", pmc, 3) != DDI_PROP_SUCCESS) {
+ return;
+ }
+#endif
+
+ ata_ctlp->ac_pm_support = 1;
+ ata_ctlp->ac_pm_level = PM_LEVEL_D0;
+
+ ATA_BUSY_COMPONENT(dip, 0);
+ if (ATA_RAISE_POWER(dip, 0, PM_LEVEL_D0) != DDI_SUCCESS) {
+ (void) ddi_prop_remove(DDI_DEV_T_NONE, dip, "pm-components");
+ }
+ ATA_IDLE_COMPONENT(dip, 0);
+}
+
+/*
+ * resume the hard drive
+ */
+static void
+ata_resume_drive(ata_drv_t *ata_drvp)
+{
+ ata_ctl_t *ata_ctlp = ata_drvp->ad_ctlp;
+ int drive_type;
+ struct ata_id id;
+ uint8_t udma;
+
+ ADBG_TRACE(("ata_resume_drive entered\n"));
+
+ drive_type = ata_drive_type(ata_drvp->ad_drive_bits,
+ ata_ctlp->ac_iohandle1, ata_ctlp->ac_ioaddr1,
+ ata_ctlp->ac_iohandle2, ata_ctlp->ac_ioaddr2,
+ &id);
+ if (drive_type == ATA_DEV_NONE)
+ return;
+
+ /* Reset Ultra DMA mode */
+ udma = ATACM_UDMA_SEL(&ata_drvp->ad_id);
+ if (udma != 0) {
+ uint8_t mode;
+ for (mode = 0; mode < 8; mode++)
+ if (((1 << mode) & udma) != 0)
+ break;
+ ASSERT(mode != 8);
+
+ mode |= ATF_XFRMOD_UDMA;
+
+ if (!ata_set_feature(ata_ctlp, ata_drvp, ATSF_SET_XFRMOD, mode))
+ return;
+ }
+
+ if (!ATAPIDRV(ata_drvp)) {
+ if (!ata_disk_setup_parms(ata_ctlp, ata_drvp))
+ return;
+ (void) ata_set_feature(ata_ctlp, ata_drvp, ATSF_DIS_REVPOD, 0);
+ }
+}
+
+/*
+ * resume routine, it will be run when get the command
+ * DDI_RESUME at attach(9E) from system power management
+ */
+static int
+ata_resume(dev_info_t *dip)
+{
+ int instance;
+ ata_ctl_t *ata_ctlp;
+ ddi_acc_handle_t io_hdl2;
+ caddr_t ioaddr2;
+
+ instance = ddi_get_instance(dip);
+ ata_ctlp = ddi_get_soft_state(ata_state, instance);
+
+ if (!ata_ctlp->ac_pm_support)
+ return (DDI_FAILURE);
+ if (ata_ctlp->ac_pm_level == PM_LEVEL_D0)
+ return (DDI_SUCCESS);
+
+ ATA_BUSY_COMPONENT(dip, 0);
+ if (ATA_RAISE_POWER(dip, 0, PM_LEVEL_D0) == DDI_FAILURE)
+ return (DDI_FAILURE);
+ ATA_IDLE_COMPONENT(dip, 0);
+
+ /* enable interrupts from the device */
+ io_hdl2 = ata_ctlp->ac_iohandle2;
+ ioaddr2 = ata_ctlp->ac_ioaddr2;
+ ddi_put8(io_hdl2, (uchar_t *)ioaddr2 + AT_DEVCTL, ATDC_D3);
+ ata_ctlp->ac_pm_level = PM_LEVEL_D0;
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * suspend routine, it will be run when get the command
+ * DDI_SUSPEND at detach(9E) from system power management
+ */
+static int
+ata_suspend(dev_info_t *dip)
+{
+ int instance;
+ ata_ctl_t *ata_ctlp;
+ ddi_acc_handle_t io_hdl2;
+
+ instance = ddi_get_instance(dip);
+ ata_ctlp = ddi_get_soft_state(ata_state, instance);
+
+ if (!ata_ctlp->ac_pm_support)
+ return (DDI_FAILURE);
+ if (ata_ctlp->ac_pm_level == PM_LEVEL_D3)
+ return (DDI_SUCCESS);
+
+ /* disable interrupts and turn the software reset bit on */
+ io_hdl2 = ata_ctlp->ac_iohandle2;
+ ddi_put8(io_hdl2, ata_ctlp->ac_devctl, (ATDC_D3 | ATDC_SRST));
+
+ (void) ata_reset_bus(ata_ctlp);
+ (void) ata_change_power(dip, ATC_SLEEP);
+ ata_ctlp->ac_pm_level = PM_LEVEL_D3;
+ return (DDI_SUCCESS);
+}
+
+int ata_save_pci_config = 0;
+/*
+ * ata specific power management entry point, it was
+ * used to change the power management component
+ */
+static int
+ata_power(dev_info_t *dip, int component, int level)
+{
+ int instance;
+ ata_ctl_t *ata_ctlp;
+ uint8_t cmd;
+
+ ADBG_TRACE(("ata_power entered, component = %d, level = %d\n",
+ component, level));
+
+ instance = ddi_get_instance(dip);
+ ata_ctlp = ddi_get_soft_state(ata_state, instance);
+ if (ata_ctlp == NULL || component != 0)
+ return (DDI_FAILURE);
+
+ if (!ata_ctlp->ac_pm_support)
+ return (DDI_FAILURE);
+
+ switch (level) {
+ case PM_LEVEL_D0:
+ if (ata_save_pci_config)
+ (void) pci_restore_config_regs(dip);
+ ata_ctlp->ac_pm_level = PM_LEVEL_D0;
+ cmd = ATC_STANDBY_IM;
+ break;
+ case PM_LEVEL_D3:
+ if (ata_save_pci_config)
+ (void) pci_save_config_regs(dip);
+ ata_ctlp->ac_pm_level = PM_LEVEL_D3;
+ cmd = ATC_SLEEP;
+ break;
+ default:
+ return (DDI_FAILURE);
+ }
+ return (ata_change_power(dip, cmd));
+}
+
+/*
+ * sent commands to ata controller to change the power level
+ */
+static int
+ata_change_power(dev_info_t *dip, uint8_t cmd)
+{
+ int instance;
+ ata_ctl_t *ata_ctlp;
+ ata_drv_t *ata_drvp;
+ uchar_t targ;
+ struct ata_id id;
+ uchar_t lun;
+ uchar_t lastlun;
+
+ ADBG_TRACE(("ata_change_power entered, cmd = %d\n", cmd));
+
+ instance = ddi_get_instance(dip);
+ ata_ctlp = ddi_get_soft_state(ata_state, instance);
+ /*
+ * Issue command on each disk device on the bus.
+ */
+ for (targ = 0; targ < ATA_MAXTARG; targ++) {
+ ata_drvp = CTL2DRV(ata_ctlp, targ, 0);
+ if (ata_drvp == NULL)
+ continue;
+ if (ata_drive_type(ata_drvp->ad_drive_bits,
+ ata_ctlp->ac_iohandle1, ata_ctlp->ac_ioaddr1,
+ ata_ctlp->ac_iohandle2, ata_ctlp->ac_ioaddr2,
+ &id) != ATA_DEV_DISK)
+ continue;
+ (void) ata_flush_cache(ata_ctlp, ata_drvp);
+ if (!ata_command(ata_ctlp, ata_drvp, TRUE, TRUE, 5 * 1000000,
+ cmd, 0, 0, 0, 0, 0, 0)) {
+ cmn_err(CE_WARN, "!ata_controller - Can not put "
+ "drive %d in to power mode %u", targ, cmd);
+ (void) ata_devo_reset(dip, DDI_RESET_FORCE);
+ return (DDI_FAILURE);
+ }
+ }
+
+ if (cmd == ATC_SLEEP)
+ return (DDI_SUCCESS);
+
+ for (targ = 0; targ < ATA_MAXTARG; targ++) {
+ ata_drvp = CTL2DRV(ata_ctlp, targ, 0);
+ if ((ata_drvp == NULL) || !(ata_drvp->ad_flags & AD_DISK))
+ continue;
+ ata_resume_drive(ata_drvp);
+
+ if (ATAPIDRV(ata_drvp))
+ lastlun = ata_drvp->ad_id.ai_lastlun;
+ else
+ lastlun = 0;
+ if (!ata_enable_atapi_luns)
+ lastlun = 0;
+ for (lun = 1; lun <= lastlun && lun < ATA_MAXLUN; lun++) {
+ ata_drvp = CTL2DRV(ata_ctlp, targ, lun);
+ if (ata_drvp != NULL)
+ ata_resume_drive(ata_drvp);
+ }
+ (void) ata_software_reset(ata_ctlp);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * return 1 when ata controller is a pci device,
+ * otherwise return 0
+ */
+static int
+ata_is_pci(dev_info_t *dip)
+{
+ int rc;
+ char *bufp;
+ int ispci;
+
+ rc = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_get_parent(dip),
+ DDI_PROP_DONTPASS, "device_type", &bufp);
+
+ if (rc != DDI_PROP_SUCCESS) {
+ ADBG_ERROR(("ata_is_pci !device_type\n"));
+ return (0);
+ }
+
+ ispci = (strcmp(bufp, "pci-ide") == 0);
+
+ ddi_prop_free(bufp);
+
+ return (ispci);
+}
diff --git a/usr/src/uts/intel/io/dktp/controller/ata/ata_common.h b/usr/src/uts/intel/io/dktp/controller/ata/ata_common.h
index 1df1252b55..22aef7c97e 100644
--- a/usr/src/uts/intel/io/dktp/controller/ata/ata_common.h
+++ b/usr/src/uts/intel/io/dktp/controller/ata/ata_common.h
@@ -177,6 +177,15 @@ extern "C" {
* Feature register bits
*/
#define ATF_ATAPI_DMA 0x01 /* ATAPI DMA enable bit */
+#define ATF_XFRMOD_UDMA 0x40 /* Ultra DMA mode */
+#define ATACM_UDMA_SEL(id) (((id)->ai_ultradma >> 8) & 0x7f)
+
+/*
+ * Set feature register definitions.
+ */
+#define ATSF_SET_XFRMOD 0X03 /* Set transfer mode */
+#define ATSF_DIS_REVPOD 0x66 /* Disable reverting to power on defaults */
+#define ATSF_ENA_REVPOD 0xcc /* Enable reverting to power on defaults */
/*
* common bits and options for set features (ATC_SET_FEAT)
@@ -281,6 +290,12 @@ typedef struct ata_ctl {
struct ata_pkt *ac_arq_pktp; /* pkt for performing ATAPI ARQ */
struct ata_pkt *ac_fault_pktp; /* pkt that caused ARQ */
uchar_t ac_arq_cdb[6];
+
+ /*
+ * Power Management
+ */
+ int ac_pm_support;
+ int ac_pm_level;
} ata_ctl_t;
/* ac_flags (per-controller) */
diff --git a/usr/src/uts/intel/io/dktp/dcdev/dadk.c b/usr/src/uts/intel/io/dktp/dcdev/dadk.c
index eaf5c28db0..5b99559cc3 100644
--- a/usr/src/uts/intel/io/dktp/dcdev/dadk.c
+++ b/usr/src/uts/intel/io/dktp/dcdev/dadk.c
@@ -73,6 +73,7 @@ static struct cmpkt *dadk_pktprep(struct dadk *dadkp, struct cmpkt *in_pktp,
static int dadk_pkt(opaque_t com_data, struct buf *bp, int (*func)(caddr_t),
caddr_t arg);
static void dadk_transport(opaque_t com_data, struct buf *bp);
+static int dadk_ctl_ioctl(struct dadk *, uint32_t, uintptr_t, int);
struct tgcom_objops dadk_com_ops = {
nodev,
@@ -336,6 +337,8 @@ dadk_init(opaque_t objp, opaque_t devp, opaque_t flcobjp, opaque_t queobjp,
BBH_INIT(bbhobjp);
dadkp->dad_flcobjp = flcobjp;
+ mutex_init(&dadkp->dad_cmd_mutex, NULL, MUTEX_DRIVER, NULL);
+ dadkp->dad_cmd_count = 0;
return (FLC_INIT(flcobjp, &(dadkp->dad_com), queobjp, lkarg));
}
@@ -364,6 +367,7 @@ dadk_cleanup(struct tgdk_obj *dkobjp)
FLC_FREE(dadkp->dad_flcobjp);
dadkp->dad_flcobjp = NULL;
}
+ mutex_destroy(&dadkp->dad_cmd_mutex);
}
/* ARGSUSED */
@@ -376,7 +380,7 @@ dadk_probe(opaque_t objp, int kmsflg)
devp = dadkp->dad_sd;
if (!devp->sd_inq || (devp->sd_inq->inq_dtype == DTYPE_NOTPRESENT) ||
- (devp->sd_inq->inq_dtype == DTYPE_UNKNOWN)) {
+ (devp->sd_inq->inq_dtype == DTYPE_UNKNOWN)) {
return (DDI_PROBE_FAILURE);
}
@@ -454,21 +458,23 @@ dadk_open(opaque_t objp, int flag)
return (DDI_SUCCESS);
}
} else {
- mutex_enter(&dadkp->dad_mutex);
- dadkp->dad_iostate = DKIO_NONE;
- cv_broadcast(&dadkp->dad_state_cv);
- mutex_exit(&dadkp->dad_mutex);
-
- if (dadk_rmb_ioctl(dadkp, DCMD_START_MOTOR, 0, 0, DADK_SILENT) ||
- dadk_rmb_ioctl(dadkp, DCMD_LOCK, 0, 0, DADK_SILENT) ||
- dadk_rmb_ioctl(dadkp, DCMD_UPDATE_GEOM, 0, 0, DADK_SILENT)) {
- return (DDI_FAILURE);
- }
-
- mutex_enter(&dadkp->dad_mutex);
- dadkp->dad_iostate = DKIO_INSERTED;
- cv_broadcast(&dadkp->dad_state_cv);
- mutex_exit(&dadkp->dad_mutex);
+ mutex_enter(&dadkp->dad_mutex);
+ dadkp->dad_iostate = DKIO_NONE;
+ cv_broadcast(&dadkp->dad_state_cv);
+ mutex_exit(&dadkp->dad_mutex);
+
+ if (dadk_rmb_ioctl(dadkp, DCMD_START_MOTOR, 0, 0,
+ DADK_SILENT) ||
+ dadk_rmb_ioctl(dadkp, DCMD_LOCK, 0, 0, DADK_SILENT) ||
+ dadk_rmb_ioctl(dadkp, DCMD_UPDATE_GEOM, 0, 0,
+ DADK_SILENT)) {
+ return (DDI_FAILURE);
+ }
+
+ mutex_enter(&dadkp->dad_mutex);
+ dadkp->dad_iostate = DKIO_INSERTED;
+ cv_broadcast(&dadkp->dad_state_cv);
+ mutex_exit(&dadkp->dad_mutex);
}
/*
@@ -482,20 +488,20 @@ dadk_open(opaque_t objp, int flag)
* is added to the driver to change WCE, dad_wce
* must be updated appropriately.
*/
- error = CTL_IOCTL(dadkp->dad_ctlobjp, DIOCTL_GETWCE,
+ error = dadk_ctl_ioctl(dadkp, DIOCTL_GETWCE,
(uintptr_t)&wce, FKIOCTL | FNATIVE);
mutex_enter(&dadkp->dad_mutex);
dadkp->dad_wce = (error != 0) || (wce != 0);
mutex_exit(&dadkp->dad_mutex);
/* logical disk geometry */
- CTL_IOCTL(dadkp->dad_ctlobjp, DIOCTL_GETGEOM,
+ (void) dadk_ctl_ioctl(dadkp, DIOCTL_GETGEOM,
(uintptr_t)&dadkp->dad_logg, FKIOCTL | FNATIVE);
if (dadkp->dad_logg.g_cap == 0)
return (DDI_FAILURE);
/* get physical disk geometry */
- CTL_IOCTL(dadkp->dad_ctlobjp, DIOCTL_GETPHYGEOM,
+ (void) dadk_ctl_ioctl(dadkp, DIOCTL_GETPHYGEOM,
(uintptr_t)&dadkp->dad_phyg, FKIOCTL | FNATIVE);
if (dadkp->dad_phyg.g_cap == 0)
return (DDI_FAILURE);
@@ -507,7 +513,7 @@ dadk_open(opaque_t objp, int flag)
/* start profiling */
FLC_START_KSTAT(dadkp->dad_flcobjp, "disk",
- ddi_get_instance(CTL_DIP_DEV(dadkp->dad_ctlobjp)));
+ ddi_get_instance(CTL_DIP_DEV(dadkp->dad_ctlobjp)));
return (DDI_SUCCESS);
}
@@ -534,7 +540,8 @@ dadk_setcap(struct dadk *dadkp)
/* set sec,block shift factor - (512->0, 1024->1, 2048->2, etc.) */
totsize >>= SCTRSHFT;
- for (i = 0; totsize != 1; i++, totsize >>= 1);
+ for (i = 0; totsize != 1; i++, totsize >>= 1)
+ ;
dadkp->dad_blkshf = i;
dadkp->dad_secshf = i + SCTRSHFT;
}
@@ -594,14 +601,14 @@ dadk_create_errstats(struct dadk *dadkp, int instance)
dep->dadk_model.value.c[0] = 0;
dadk_ioc_string.is_buf = &dep->dadk_model.value.c[0];
dadk_ioc_string.is_size = sizeof (dep->dadk_model.value.c);
- CTL_IOCTL(dadkp->dad_ctlobjp, DIOCTL_GETMODEL,
+ (void) dadk_ctl_ioctl(dadkp, DIOCTL_GETMODEL,
(uintptr_t)&dadk_ioc_string, FKIOCTL | FNATIVE);
/* get serial */
dep->dadk_serial.value.c[0] = 0;
dadk_ioc_string.is_buf = &dep->dadk_serial.value.c[0];
dadk_ioc_string.is_size = sizeof (dep->dadk_serial.value.c);
- CTL_IOCTL(dadkp->dad_ctlobjp, DIOCTL_GETSERIAL,
+ (void) dadk_ctl_ioctl(dadkp, DIOCTL_GETSERIAL,
(uintptr_t)&dadk_ioc_string, FKIOCTL | FNATIVE);
/* Get revision */
@@ -659,6 +666,9 @@ dadk_strategy(opaque_t objp, struct buf *bp)
}
SET_BP_SEC(bp, (LBLK2SEC(GET_BP_SEC(bp), dadkp->dad_blkshf)));
+ mutex_enter(&dadkp->dad_cmd_mutex);
+ dadkp->dad_cmd_count++;
+ mutex_exit(&dadkp->dad_cmd_mutex);
FLC_ENQUE(dadkp->dad_flcobjp, bp);
return (DDI_SUCCESS);
@@ -715,7 +725,7 @@ dadk_ioctl(opaque_t objp, dev_t dev, int cmd, intptr_t arg, int flag,
switch (cmd) {
case DKIOCGETDEF:
- {
+ {
struct buf *bp;
int err, head;
unsigned char *secbuf;
@@ -752,6 +762,9 @@ dadk_ioctl(opaque_t objp, dev_t dev, int cmd, intptr_t arg, int flag,
bp->b_forw = (struct buf *)dadkp;
bp->b_back = (struct buf *)DCMD_GETDEF;
+ mutex_enter(&dadkp->dad_cmd_mutex);
+ dadkp->dad_cmd_count++;
+ mutex_exit(&dadkp->dad_cmd_mutex);
FLC_ENQUE(dadkp->dad_flcobjp, bp);
err = biowait(bp);
if (!err) {
@@ -762,9 +775,9 @@ dadk_ioctl(opaque_t objp, dev_t dev, int cmd, intptr_t arg, int flag,
kmem_free(secbuf, NBPSCTR);
freerbuf(bp);
return (err);
- }
+ }
case DIOCTL_RWCMD:
- {
+ {
struct dadkio_rwcmd *rwcmdp;
int status, rw;
@@ -794,8 +807,8 @@ dadk_ioctl(opaque_t objp, dev_t dev, int cmd, intptr_t arg, int flag,
return (status);
default:
return (EINVAL);
+ }
}
- }
case DKIOC_UPDATEFW:
/*
@@ -805,7 +818,7 @@ dadk_ioctl(opaque_t objp, dev_t dev, int cmd, intptr_t arg, int flag,
if (PRIV_POLICY(cred_p, PRIV_ALL, B_FALSE, EPERM, NULL) != 0)
return (EPERM);
else
- return (CTL_IOCTL(dadkp->dad_ctlobjp, cmd, arg, flag));
+ return (dadk_ctl_ioctl(dadkp, cmd, arg, flag));
case DKIOCFLUSHWRITECACHE:
{
@@ -884,6 +897,9 @@ dadk_ioctl(opaque_t objp, dev_t dev, int cmd, intptr_t arg, int flag,
CTL_IOSETUP(dadkp->dad_ctlobjp, pktp);
+ mutex_enter(&dadkp->dad_cmd_mutex);
+ dadkp->dad_cmd_count++;
+ mutex_exit(&dadkp->dad_cmd_mutex);
FLC_ENQUE(dadkp->dad_flcobjp, bp);
if (is_sync) {
@@ -894,16 +910,16 @@ dadk_ioctl(opaque_t objp, dev_t dev, int cmd, intptr_t arg, int flag,
}
default:
if (!dadkp->dad_rmb)
- return (CTL_IOCTL(dadkp->dad_ctlobjp, cmd, arg, flag));
+ return (dadk_ctl_ioctl(dadkp, cmd, arg, flag));
}
switch (cmd) {
case CDROMSTOP:
return (dadk_rmb_ioctl(dadkp, DCMD_STOP_MOTOR, 0,
- 0, DADK_SILENT));
+ 0, DADK_SILENT));
case CDROMSTART:
return (dadk_rmb_ioctl(dadkp, DCMD_START_MOTOR, 0,
- 0, DADK_SILENT));
+ 0, DADK_SILENT));
case DKIOCLOCK:
return (dadk_rmb_ioctl(dadkp, DCMD_LOCK, 0, 0, DADK_SILENT));
case DKIOCUNLOCK:
@@ -914,11 +930,11 @@ dadk_ioctl(opaque_t objp, dev_t dev, int cmd, intptr_t arg, int flag,
int ret;
if (ret = dadk_rmb_ioctl(dadkp, DCMD_UNLOCK, 0, 0,
- DADK_SILENT)) {
+ DADK_SILENT)) {
return (ret);
}
if (ret = dadk_rmb_ioctl(dadkp, DCMD_EJECT, 0, 0,
- DADK_SILENT)) {
+ DADK_SILENT)) {
return (ret);
}
mutex_enter(&dadkp->dad_mutex);
@@ -1036,7 +1052,7 @@ dadk_iob_alloc(opaque_t objp, daddr_t blkno, ssize_t xfer, int kmsflg)
iobp->b_psec = LBLK2SEC(blkno, dadkp->dad_blkshf);
iobp->b_pbyteoff = (blkno & ((1<<dadkp->dad_blkshf) - 1)) << SCTRSHFT;
iobp->b_pbytecnt = ((iobp->b_pbyteoff + xfer + dadkp->DAD_SECSIZ - 1)
- >> dadkp->dad_secshf) << dadkp->dad_secshf;
+ >> dadkp->dad_secshf) << dadkp->dad_secshf;
bp->b_un.b_addr = 0;
/*
@@ -1107,6 +1123,9 @@ dadk_iob_xfer(opaque_t objp, struct tgdk_iob *iobp, int rw)
bp->b_resid = 0;
/* call flow control */
+ mutex_enter(&dadkp->dad_cmd_mutex);
+ dadkp->dad_cmd_count++;
+ mutex_exit(&dadkp->dad_cmd_mutex);
FLC_ENQUE(dadkp->dad_flcobjp, bp);
err = biowait(bp);
@@ -1253,16 +1272,15 @@ dadk_ioretry(struct cmpkt *pktp, int action)
if (pktp->cp_retry++ < DADK_RETRY_COUNT) {
CTL_IOSETUP(dadkp->dad_ctlobjp, pktp);
if (CTL_TRANSPORT(dadkp->dad_ctlobjp, pktp) ==
- CTL_SEND_SUCCESS) {
+ CTL_SEND_SUCCESS) {
return (JUST_RETURN);
}
gda_log(dadkp->dad_sd->sd_dev, dadk_name,
- CE_WARN,
- "transport of command fails\n");
+ CE_WARN, "transport of command fails\n");
} else
gda_log(dadkp->dad_sd->sd_dev,
- dadk_name, CE_WARN,
- "exceeds maximum number of retries\n");
+ dadk_name, CE_WARN,
+ "exceeds maximum number of retries\n");
bioerror(pktp->cp_bp, ENXIO);
/*FALLTHROUGH*/
case COMMAND_DONE_ERROR:
@@ -1378,7 +1396,7 @@ dadk_chkerr(struct cmpkt *pktp)
if (pktp->cp_retry) {
err_blkno = pktp->cp_srtsec + ((pktp->cp_bytexfer -
- pktp->cp_resid) >> dadkp->dad_secshf);
+ pktp->cp_resid) >> dadkp->dad_secshf);
} else
err_blkno = -1;
@@ -1468,11 +1486,10 @@ dadk_recorderr(struct cmpkt *pktp, struct dadkio_rwcmd *rwcmdp)
rwcmdp->status.failed_blk = rwcmdp->blkaddr +
- ((pktp->cp_bytexfer -
- pktp->cp_resid) >> dadkp->dad_secshf);
+ ((pktp->cp_bytexfer - pktp->cp_resid) >> dadkp->dad_secshf);
rwcmdp->status.resid = pktp->cp_bp->b_resid +
- pktp->cp_byteleft - pktp->cp_bytexfer + pktp->cp_resid;
+ pktp->cp_byteleft - pktp->cp_bytexfer + pktp->cp_resid;
switch ((int)(* (char *)pktp->cp_scbp)) {
case DERR_AMNF:
case DERR_ABORT:
@@ -1504,14 +1521,22 @@ dadk_recorderr(struct cmpkt *pktp, struct dadkio_rwcmd *rwcmdp)
if (rwcmdp->flags & DADKIO_FLAG_SILENT)
return;
gda_errmsg(dadkp->dad_sd, pktp, dadk_name, dadk_errtab[scb].d_severity,
- rwcmdp->blkaddr, rwcmdp->status.failed_blk,
- dadk_cmds, dadk_sense);
+ rwcmdp->blkaddr, rwcmdp->status.failed_blk,
+ dadk_cmds, dadk_sense);
}
/*ARGSUSED*/
static void
dadk_polldone(struct buf *bp)
{
+ struct cmpkt *pktp;
+ struct dadk *dadkp;
+
+ pktp = GDA_BP_PKT(bp);
+ dadkp = PKT2DADK(pktp);
+ mutex_enter(&dadkp->dad_cmd_mutex);
+ dadkp->dad_cmd_count--;
+ mutex_exit(&dadkp->dad_cmd_mutex);
}
static void
@@ -1544,6 +1569,9 @@ dadk_iodone(struct buf *bp)
if (pktp->cp_private)
BBH_FREEHANDLE(dadkp->dad_bbhobjp, pktp->cp_private);
gda_free(dadkp->dad_ctlobjp, pktp, NULL);
+ mutex_enter(&dadkp->dad_cmd_mutex);
+ dadkp->dad_cmd_count--;
+ mutex_exit(&dadkp->dad_cmd_mutex);
biodone(bp);
}
@@ -1558,7 +1586,7 @@ dadk_check_media(opaque_t objp, int *state)
#ifdef DADK_DEBUG
if (dadk_debug & DSTATE)
PRF("dadk_check_media: user state %x disk state %x\n",
- *state, dadkp->dad_iostate);
+ *state, dadkp->dad_iostate);
#endif
/*
* If state already changed just return
@@ -1676,7 +1704,7 @@ dadk_rmb_ioctl(struct dadk *dadkp, int cmd, intptr_t arg, int flags, int silent)
bp->b_forw = (struct buf *)dadkp->dad_flcobjp;
pktp->cp_passthru = (opaque_t)(intptr_t)silent;
- err = CTL_IOCTL(dadkp->dad_ctlobjp, cmd, (uintptr_t)pktp, flags);
+ err = dadk_ctl_ioctl(dadkp, cmd, (uintptr_t)pktp, flags);
freerbuf(bp);
gda_free(dadkp->dad_ctlobjp, pktp, NULL);
return (err);
@@ -1698,6 +1726,9 @@ dadk_rmb_iodone(struct buf *bp)
/* Start next one */
FLC_DEQUE(dadkp->dad_flcobjp, bp);
+ mutex_enter(&dadkp->dad_cmd_mutex);
+ dadkp->dad_cmd_count--;
+ mutex_exit(&dadkp->dad_cmd_mutex);
biodone(bp);
}
@@ -1769,5 +1800,42 @@ dadk_dk(struct dadk *dadkp, struct dadkio_rwcmd *rwcmdp, struct buf *bp)
(void) dadk_ioprep(dadkp, pktp);
+ mutex_enter(&dadkp->dad_cmd_mutex);
+ dadkp->dad_cmd_count++;
+ mutex_exit(&dadkp->dad_cmd_mutex);
FLC_ENQUE(dadkp->dad_flcobjp, bp);
}
+
+/*
+ * There is no existing way to notify cmdk module
+ * when the command completed, so add this function
+ * to calculate how many on-going commands.
+ */
+int
+dadk_getcmds(opaque_t objp)
+{
+ struct dadk *dadkp = (struct dadk *)objp;
+ int count;
+
+ mutex_enter(&dadkp->dad_cmd_mutex);
+ count = dadkp->dad_cmd_count;
+ mutex_exit(&dadkp->dad_cmd_mutex);
+ return (count);
+}
+
+/*
+ * this function was used to calc the cmd for CTL_IOCTL
+ */
+static int
+dadk_ctl_ioctl(struct dadk *dadkp, uint32_t cmd, uintptr_t arg, int flag)
+{
+ int error;
+ mutex_enter(&dadkp->dad_cmd_mutex);
+ dadkp->dad_cmd_count++;
+ mutex_exit(&dadkp->dad_cmd_mutex);
+ error = CTL_IOCTL(dadkp->dad_ctlobjp, cmd, arg, flag);
+ mutex_enter(&dadkp->dad_cmd_mutex);
+ dadkp->dad_cmd_count--;
+ mutex_exit(&dadkp->dad_cmd_mutex);
+ return (error);
+}
diff --git a/usr/src/uts/intel/io/dktp/disk/cmdk.c b/usr/src/uts/intel/io/dktp/disk/cmdk.c
index 4ce5a60250..d14946bcf2 100644
--- a/usr/src/uts/intel/io/dktp/disk/cmdk.c
+++ b/usr/src/uts/intel/io/dktp/disk/cmdk.c
@@ -163,6 +163,11 @@ static int cmdkprobe(dev_info_t *dip);
static int cmdkattach(dev_info_t *dip, ddi_attach_cmd_t cmd);
static int cmdkdetach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+static void cmdk_setup_pm(dev_info_t *dip, struct cmdk *dkp);
+static int cmdkresume(dev_info_t *dip);
+static int cmdksuspend(dev_info_t *dip);
+static int cmdkpower(dev_info_t *dip, int component, int level);
+
struct dev_ops cmdk_ops = {
DEVO_REV, /* devo_rev, */
0, /* refcnt */
@@ -173,7 +178,8 @@ struct dev_ops cmdk_ops = {
cmdkdetach, /* detach */
nodev, /* reset */
&cmdk_cb_ops, /* driver operations */
- (struct bus_ops *)0 /* bus operations */
+ (struct bus_ops *)0, /* bus operations */
+ cmdkpower /* power */
};
/*
@@ -322,13 +328,22 @@ cmdkattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
struct cmdk *dkp;
char *node_type;
- if (cmd != DDI_ATTACH)
+ switch (cmd) {
+ case DDI_ATTACH:
+ break;
+ case DDI_RESUME:
+ return (cmdkresume(dip));
+ default:
return (DDI_FAILURE);
+ }
instance = ddi_get_instance(dip);
if (!(dkp = ddi_get_soft_state(cmdk_state, instance)))
return (DDI_FAILURE);
+ dkp->dk_pm_level = CMDK_SPINDLE_UNINIT;
+ mutex_init(&dkp->dk_mutex, NULL, MUTEX_DRIVER, NULL);
+
mutex_enter(&dkp->dk_mutex);
/* dadk_attach is an empty function that only returns SUCCESS */
@@ -386,6 +401,13 @@ cmdkattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
DDI_KERNEL_IOCTL, NULL, 0);
ddi_report_dev(dip);
+ /*
+ * Initialize power management
+ */
+ mutex_init(&dkp->dk_pm_mutex, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&dkp->dk_suspend_cv, NULL, CV_DRIVER, NULL);
+ cmdk_setup_pm(dip, dkp);
+
return (DDI_SUCCESS);
fail1:
@@ -408,7 +430,13 @@ cmdkdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
int instance;
int max_instance;
- if (cmd != DDI_DETACH) {
+ switch (cmd) {
+ case DDI_DETACH:
+ /* return (DDI_FAILURE); */
+ break;
+ case DDI_SUSPEND:
+ return (cmdksuspend(dip));
+ default:
#ifdef CMDK_DEBUG
if (cmdk_debug & DIO) {
PRF("cmdkdetach: cmd = %d unknown\n", cmd);
@@ -454,6 +482,8 @@ cmdkdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
mutex_exit(&dkp->dk_mutex);
mutex_destroy(&dkp->dk_mutex);
rw_destroy(&dkp->dk_bbh_mutex);
+ mutex_destroy(&dkp->dk_pm_mutex);
+ cv_destroy(&dkp->dk_suspend_cv);
ddi_soft_state_free(cmdk_state, instance);
return (DDI_SUCCESS);
@@ -490,6 +520,145 @@ cmdkinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
return (DDI_SUCCESS);
}
+/*
+ * Initialize the power management components
+ */
+static void
+cmdk_setup_pm(dev_info_t *dip, struct cmdk *dkp)
+{
+ char *pm_comp[] = { "NAME=cmdk", "0=off", "1=on", NULL };
+
+ /*
+ * Since the cmdk device does not the 'reg' property,
+ * cpr will not call its DDI_SUSPEND/DDI_RESUME entries.
+ * The following code is to tell cpr that this device
+ * DOES need to be suspended and resumed.
+ */
+ (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
+ "pm-hardware-state", "needs-suspend-resume");
+
+ if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
+ "pm-components", pm_comp, 3) == DDI_PROP_SUCCESS) {
+ if (pm_raise_power(dip, 0, CMDK_SPINDLE_ON) == DDI_SUCCESS) {
+ mutex_enter(&dkp->dk_pm_mutex);
+ dkp->dk_pm_level = CMDK_SPINDLE_ON;
+ dkp->dk_pm_is_enabled = 1;
+ mutex_exit(&dkp->dk_pm_mutex);
+ } else {
+ mutex_enter(&dkp->dk_pm_mutex);
+ dkp->dk_pm_level = CMDK_SPINDLE_OFF;
+ dkp->dk_pm_is_enabled = 0;
+ mutex_exit(&dkp->dk_pm_mutex);
+ }
+ } else {
+ mutex_enter(&dkp->dk_pm_mutex);
+ dkp->dk_pm_level = CMDK_SPINDLE_UNINIT;
+ dkp->dk_pm_is_enabled = 0;
+ mutex_exit(&dkp->dk_pm_mutex);
+ }
+}
+
+/*
+ * suspend routine, it will be run when get the command
+ * DDI_SUSPEND at detach(9E) from system power management
+ */
+static int
+cmdksuspend(dev_info_t *dip)
+{
+ struct cmdk *dkp;
+ int instance;
+ clock_t count = 0;
+
+ instance = ddi_get_instance(dip);
+ if (!(dkp = ddi_get_soft_state(cmdk_state, instance)))
+ return (DDI_FAILURE);
+ mutex_enter(&dkp->dk_mutex);
+ if (dkp->dk_flag & CMDK_SUSPEND) {
+ mutex_exit(&dkp->dk_mutex);
+ return (DDI_SUCCESS);
+ }
+ dkp->dk_flag |= CMDK_SUSPEND;
+
+ /* need to wait a while */
+ while (dadk_getcmds(DKTP_DATA) != 0) {
+ delay(drv_usectohz(1000000));
+ if (count > 60) {
+ dkp->dk_flag &= ~CMDK_SUSPEND;
+ cv_broadcast(&dkp->dk_suspend_cv);
+ mutex_exit(&dkp->dk_mutex);
+ return (DDI_FAILURE);
+ }
+ count++;
+ }
+ mutex_exit(&dkp->dk_mutex);
+ return (DDI_SUCCESS);
+}
+
+/*
+ * resume routine, it will be run when get the command
+ * DDI_RESUME at attach(9E) from system power management
+ */
+static int
+cmdkresume(dev_info_t *dip)
+{
+ struct cmdk *dkp;
+ int instance;
+
+ instance = ddi_get_instance(dip);
+ if (!(dkp = ddi_get_soft_state(cmdk_state, instance)))
+ return (DDI_FAILURE);
+ mutex_enter(&dkp->dk_mutex);
+ if (!(dkp->dk_flag & CMDK_SUSPEND)) {
+ mutex_exit(&dkp->dk_mutex);
+ return (DDI_FAILURE);
+ }
+ dkp->dk_pm_level = CMDK_SPINDLE_ON;
+ dkp->dk_flag &= ~CMDK_SUSPEND;
+ cv_broadcast(&dkp->dk_suspend_cv);
+ mutex_exit(&dkp->dk_mutex);
+ return (DDI_SUCCESS);
+
+}
+
+/*
+ * power management entry point, it was used to
+ * change power management component.
+ * Actually, the real hard drive suspend/resume
+ * was handled in ata, so this function is not
+ * doing any real work other than verifying that
+ * the disk is idle.
+ */
+static int
+cmdkpower(dev_info_t *dip, int component, int level)
+{
+ struct cmdk *dkp;
+ int instance;
+
+ instance = ddi_get_instance(dip);
+ if (!(dkp = ddi_get_soft_state(cmdk_state, instance)) ||
+ component != 0 || level > CMDK_SPINDLE_ON ||
+ level < CMDK_SPINDLE_OFF) {
+ return (DDI_FAILURE);
+ }
+
+ mutex_enter(&dkp->dk_pm_mutex);
+ if (dkp->dk_pm_is_enabled && dkp->dk_pm_level == level) {
+ mutex_exit(&dkp->dk_pm_mutex);
+ return (DDI_SUCCESS);
+ }
+ mutex_exit(&dkp->dk_pm_mutex);
+
+ if ((level == CMDK_SPINDLE_OFF) &&
+ (dadk_getcmds(DKTP_DATA) != 0)) {
+ return (DDI_FAILURE);
+ }
+
+ mutex_enter(&dkp->dk_pm_mutex);
+ dkp->dk_pm_level = level;
+ mutex_exit(&dkp->dk_pm_mutex);
+ return (DDI_SUCCESS);
+}
+
static int
cmdk_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int mod_flags,
char *name, caddr_t valuep, int *lengthp)
@@ -677,6 +846,12 @@ cmdkioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp, int *rvalp)
if (!(dkp = ddi_get_soft_state(cmdk_state, instance)))
return (ENXIO);
+ mutex_enter(&dkp->dk_mutex);
+ while (dkp->dk_flag & CMDK_SUSPEND) {
+ cv_wait(&dkp->dk_suspend_cv, &dkp->dk_mutex);
+ }
+ mutex_exit(&dkp->dk_mutex);
+
bzero(data, sizeof (data));
switch (cmd) {
@@ -873,6 +1048,10 @@ cmdkclose(dev_t dev, int flag, int otyp, cred_t *credp)
return (ENXIO);
}
+ while (dkp->dk_flag & CMDK_SUSPEND) {
+ cv_wait(&dkp->dk_suspend_cv, &dkp->dk_mutex);
+ }
+
part = CMDKPART(dev);
partbit = 1 << part;
@@ -926,6 +1105,12 @@ cmdkopen(dev_t *dev_p, int flag, int otyp, cred_t *credp)
if (otyp >= OTYPCNT)
return (EINVAL);
+ mutex_enter(&dkp->dk_mutex);
+ while (dkp->dk_flag & CMDK_SUSPEND) {
+ cv_wait(&dkp->dk_suspend_cv, &dkp->dk_mutex);
+ }
+ mutex_exit(&dkp->dk_mutex);
+
part = CMDKPART(dev);
partbit = 1 << part;
nodelay = (flag & (FNDELAY | FNONBLOCK));
@@ -1040,12 +1225,38 @@ cmdkmin(struct buf *bp)
static int
cmdkrw(dev_t dev, struct uio *uio, int flag)
{
+ int instance;
+ struct cmdk *dkp;
+
+ instance = CMDKUNIT(dev);
+ if (!(dkp = ddi_get_soft_state(cmdk_state, instance)))
+ return (ENXIO);
+
+ mutex_enter(&dkp->dk_mutex);
+ while (dkp->dk_flag & CMDK_SUSPEND) {
+ cv_wait(&dkp->dk_suspend_cv, &dkp->dk_mutex);
+ }
+ mutex_exit(&dkp->dk_mutex);
+
return (physio(cmdkstrategy, (struct buf *)0, dev, flag, cmdkmin, uio));
}
static int
cmdkarw(dev_t dev, struct aio_req *aio, int flag)
{
+ int instance;
+ struct cmdk *dkp;
+
+ instance = CMDKUNIT(dev);
+ if (!(dkp = ddi_get_soft_state(cmdk_state, instance)))
+ return (ENXIO);
+
+ mutex_enter(&dkp->dk_mutex);
+ while (dkp->dk_flag & CMDK_SUSPEND) {
+ cv_wait(&dkp->dk_suspend_cv, &dkp->dk_mutex);
+ }
+ mutex_exit(&dkp->dk_mutex);
+
return (aphysio(cmdkstrategy, anocancel, dev, flag, cmdkmin, aio));
}
@@ -1070,6 +1281,12 @@ cmdkstrategy(struct buf *bp)
return (0);
}
+ mutex_enter(&dkp->dk_mutex);
+ while (dkp->dk_flag & CMDK_SUSPEND) {
+ cv_wait(&dkp->dk_suspend_cv, &dkp->dk_mutex);
+ }
+ mutex_exit(&dkp->dk_mutex);
+
bp->b_flags &= ~(B_DONE|B_ERROR);
bp->b_resid = 0;
bp->av_back = NULL;
@@ -1895,7 +2112,7 @@ cmdk_bbh_gethandle(opaque_t bbh_data, struct buf *bp)
/* at least one bad sector in our section. break it. */
/* CASE 5: */
if ((lastsec >= altp->bad_start) &&
- (lastsec <= altp->bad_end)) {
+ (lastsec <= altp->bad_end)) {
ckp[idx+1].ck_seclen = lastsec - altp->bad_start + 1;
ckp[idx].ck_seclen -= ckp[idx+1].ck_seclen;
ckp[idx+1].ck_sector = altp->good_start;
diff --git a/usr/src/uts/intel/io/dktp/hba/ghd/ghd_debug.c b/usr/src/uts/intel/io/dktp/hba/ghd/ghd_debug.c
index 068dbd9f76..ae591ae1b8 100644
--- a/usr/src/uts/intel/io/dktp/hba/ghd/ghd_debug.c
+++ b/usr/src/uts/intel/io/dktp/hba/ghd/ghd_debug.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -56,28 +56,29 @@ ghd_err(const char *fmt, ...)
}
#if defined(GHD_DEBUG)
+#include <sys/promif.h>
#define PRF prom_printf
static void
ghd_dump_ccc(ccc_t *P)
{
PRF("nextp 0x%p tmrp 0x%p label 0x%p &mutex 0x%p\n",
- P->ccc_nextp, P->ccc_tmrp, P->ccc_label, &P->ccc_activel_mutex);
+ P->ccc_nextp, P->ccc_tmrp, P->ccc_label, &P->ccc_activel_mutex);
PRF("&activel 0x%p dip 0x%p iblock 0x%p\n",
- &P->ccc_activel, P->ccc_hba_dip, P->ccc_iblock);
+ &P->ccc_activel, P->ccc_hba_dip, P->ccc_iblock);
PRF("softid 0x%p &hba_mutext 0x%p\n poll 0x%p\n",
- P->ccc_soft_id, &P->ccc_hba_mutex, &P->ccc_hba_pollmode);
+ P->ccc_soft_id, &P->ccc_hba_mutex, &P->ccc_hba_pollmode);
PRF("&devs 0x%p &waitq_mutex 0x%p &waitq 0x%p\n",
- &P->ccc_devs, &P->ccc_waitq_mutex, &P->ccc_waitq);
+ &P->ccc_devs, &P->ccc_waitq_mutex, &P->ccc_waitq);
PRF("waitq_freezetime 0x%p waitq_freezedelay %p\n",
- &P->ccc_waitq_freezetime, &P->ccc_waitq_freezedelay);
+ &P->ccc_waitq_freezetime, &P->ccc_waitq_freezedelay);
PRF("dq softid 0x%p &dq_mutex 0x%p &doneq 0x%p\n",
- P->ccc_doneq_softid, &P->ccc_doneq_mutex, &P->ccc_doneq);
+ P->ccc_doneq_softid, &P->ccc_doneq_mutex, &P->ccc_doneq);
PRF("handle 0x%p &ccballoc 0x%p\n",
- P->ccc_hba_handle, &P->ccc_ccballoc);
+ P->ccc_hba_handle, &P->ccc_ccballoc);
PRF("hba_reset_notify_callback 0x%p notify_list 0x%p mutex 0x%p\n",
- P->ccc_hba_reset_notify_callback, &P->ccc_reset_notify_list,
- &P->ccc_reset_notify_mutex);
+ P->ccc_hba_reset_notify_callback, &P->ccc_reset_notify_list,
+ &P->ccc_reset_notify_mutex);
}
diff --git a/usr/src/uts/intel/io/pci/pci_pci.c b/usr/src/uts/intel/io/pci/pci_pci.c
index b6dbe7f1b9..753c04d8f8 100644
--- a/usr/src/uts/intel/io/pci/pci_pci.c
+++ b/usr/src/uts/intel/io/pci/pci_pci.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -319,7 +319,7 @@ ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
DDI_FM_EREPORT_CAPABLE))
pci_ereport_setup(devi);
if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
- ddi_fm_handler_register(devi, ppb_fm_callback, NULL);
+ ddi_fm_handler_register(devi, ppb_fm_callback, NULL);
if (pci_config_setup(devi, &config_handle) != DDI_SUCCESS) {
if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
@@ -348,7 +348,8 @@ ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
* to this bus.
*/
if (pcihp_init(devi) != DDI_SUCCESS)
- cmn_err(CE_WARN, "pci: Failed to setup hotplug framework");
+ cmn_err(CE_WARN,
+ "pci: Failed to setup hotplug framework");
ddi_report_dev(devi);
return (DDI_SUCCESS);
@@ -419,7 +420,7 @@ ppb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
pdip = (dev_info_t *)DEVI(dip)->devi_parent;
return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip,
- rdip, mp, offset, len, vaddrp));
+ rdip, mp, offset, len, vaddrp));
}
/*ARGSUSED*/
@@ -432,6 +433,7 @@ ppb_ctlops(dev_info_t *dip, dev_info_t *rdip,
int rn;
int totreg;
ppb_devstate_t *ppb;
+ struct attachspec *asp;
switch (ctlop) {
case DDI_CTLOPS_REPORTDEV:
@@ -459,6 +461,22 @@ ppb_ctlops(dev_info_t *dip, dev_info_t *rdip,
return (DDI_FAILURE);
break;
+ /* X86 systems support PME wakeup from suspend */
+ case DDI_CTLOPS_ATTACH:
+ asp = (struct attachspec *)arg;
+ if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE)
+ if (pci_pre_resume(rdip) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ return (ddi_ctlops(dip, rdip, ctlop, arg, result));
+
+
+ case DDI_CTLOPS_DETACH:
+ asp = (struct attachspec *)arg;
+ if (asp->cmd == DDI_SUSPEND && asp->when == DDI_POST)
+ if (pci_post_suspend(rdip) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ return (ddi_ctlops(dip, rdip, ctlop, arg, result));
+
case DDI_CTLOPS_PEEK:
case DDI_CTLOPS_POKE:
ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(dip));
@@ -474,8 +492,8 @@ ppb_ctlops(dev_info_t *dip, dev_info_t *rdip,
*(int *)result = 0;
if (ddi_getlongprop(DDI_DEV_T_ANY, rdip,
- DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg",
- (caddr_t)&drv_regp, &reglen) != DDI_SUCCESS)
+ DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg",
+ (caddr_t)&drv_regp, &reglen) != DDI_SUCCESS)
return (DDI_FAILURE);
totreg = reglen / sizeof (pci_regspec_t);
@@ -596,8 +614,8 @@ ppb_initchild(dev_info_t *child)
}
/* transfer select properties from PROM to kernel */
- if (ddi_getprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS, "interrupts",
- -1) != -1) {
+ if (ddi_getprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS,
+ "interrupts", -1) != -1) {
pdptr = kmem_zalloc((sizeof (struct ddi_parent_private_data) +
sizeof (struct intrspec)), KM_SLEEP);
pdptr->par_intr = (struct intrspec *)(pdptr + 1);
@@ -660,20 +678,20 @@ ppb_save_config_regs(ppb_devstate_t *ppb_p)
ddi_acc_handle_t config_handle;
for (i = 0, dip = ddi_get_child(ppb_p->dip); dip != NULL;
- i++, dip = ddi_get_next_sibling(dip)) {
+ i++, dip = ddi_get_next_sibling(dip)) {
if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
- ddi_driver_name(ppb_p->dip),
- ddi_get_instance(ppb_p->dip),
- ddi_driver_name(dip),
- ddi_get_instance(dip));
+ ddi_driver_name(ppb_p->dip),
+ ddi_get_instance(ppb_p->dip),
+ ddi_driver_name(dip),
+ ddi_get_instance(dip));
continue;
}
ppb_p->config_state[i].dip = dip;
ppb_p->config_state[i].command =
- pci_config_get16(config_handle, PCI_CONF_COMM);
+ pci_config_get16(config_handle, PCI_CONF_COMM);
pci_config_teardown(&config_handle);
}
ppb_p->config_state_index = i;
@@ -701,14 +719,14 @@ ppb_restore_config_regs(ppb_devstate_t *ppb_p)
dip = ppb_p->config_state[i].dip;
if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
- ddi_driver_name(ppb_p->dip),
- ddi_get_instance(ppb_p->dip),
- ddi_driver_name(dip),
- ddi_get_instance(dip));
+ ddi_driver_name(ppb_p->dip),
+ ddi_get_instance(ppb_p->dip),
+ ddi_driver_name(dip),
+ ddi_get_instance(dip));
continue;
}
pci_config_put16(config_handle, PCI_CONF_COMM,
- ppb_p->config_state[i].command);
+ ppb_p->config_state[i].command);
pci_config_teardown(&config_handle);
}
}
diff --git a/usr/src/uts/intel/io/vgatext/vgatext.c b/usr/src/uts/intel/io/vgatext/vgatext.c
index 7835b63ecd..4e85cd7fd9 100644
--- a/usr/src/uts/intel/io/vgatext/vgatext.c
+++ b/usr/src/uts/intel/io/vgatext/vgatext.c
@@ -86,6 +86,15 @@
#define VGA_MMAP_FB_BASE VGA_MEM_ADDR
+/*
+ * This variable allows for this driver to suspend even if it
+ * shouldn't. Note that by setting it, the framebuffer will probably
+ * not come back. So use it with a serial console, or with serial
+ * line debugging (say, for example, if this driver is being modified
+ * to support _some_ hardware doing suspend and resume).
+ */
+int vgatext_force_suspend = 0;
+
static int vgatext_open(dev_t *, int, int, cred_t *);
static int vgatext_close(dev_t, int, int, cred_t *);
static int vgatext_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
@@ -271,7 +280,7 @@ _init(void)
e = mod_install(&modlinkage);
if (e) {
- ddi_soft_state_fini(&vgatext_softc_head);
+ ddi_soft_state_fini(&vgatext_softc_head);
}
return (e);
}
@@ -282,7 +291,7 @@ _fini(void)
int e;
if ((e = mod_remove(&modlinkage)) != 0)
- return (e);
+ return (e);
ddi_soft_state_fini(&vgatext_softc_head);
@@ -334,7 +343,7 @@ vgatext_check_for_console(dev_info_t *devi, struct vgatext_softc *softc,
if (pci_config_setup(devi, &pci_conf) != DDI_SUCCESS) {
cmn_err(CE_WARN,
- MYNAME ": can't get PCI conf handle");
+ MYNAME ": can't get PCI conf handle");
return;
}
@@ -410,12 +419,18 @@ vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
switch (cmd) {
case DDI_ATTACH:
- break;
+ break;
case DDI_RESUME:
- return (DDI_SUCCESS);
+ /*
+ * Though vgatext doesn't really know how to resume
+ * on a generic framebuffer, we should succeed, as
+ * it is far better to have no console, than potentiall
+ * have no machine.
+ */
+ return (DDI_SUCCESS);
default:
- return (DDI_FAILURE);
+ return (DDI_FAILURE);
}
/* DDI_ATTACH */
@@ -436,7 +451,7 @@ vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
softc->polledio.cursor = vgatext_polled_cursor;
error = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_get_parent(devi),
- DDI_PROP_DONTPASS, "device_type", &parent_type);
+ DDI_PROP_DONTPASS, "device_type", &parent_type);
if (error != DDI_SUCCESS) {
cmn_err(CE_WARN, MYNAME ": can't determine parent type.");
goto fail;
@@ -444,47 +459,47 @@ vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
if (STREQ(parent_type, "isa") || STREQ(parent_type, "eisa")) {
reg_rnumber = vgatext_get_isa_reg_index(devi, 1, VGA_REG_ADDR,
- &reg_offset);
+ &reg_offset);
if (reg_rnumber < 0) {
cmn_err(CE_WARN,
- MYNAME ": can't find reg entry for registers");
+ MYNAME ": can't find reg entry for registers");
error = DDI_FAILURE;
goto fail;
}
softc->fb_regno = vgatext_get_isa_reg_index(devi, 0,
- VGA_MEM_ADDR, &mem_offset);
+ VGA_MEM_ADDR, &mem_offset);
if (softc->fb_regno < 0) {
cmn_err(CE_WARN,
- MYNAME ": can't find reg entry for memory");
+ MYNAME ": can't find reg entry for memory");
error = DDI_FAILURE;
goto fail;
}
} else if (STREQ(parent_type, "pci") || STREQ(parent_type, "pciex")) {
pci_pcie_bus = 1;
reg_rnumber = vgatext_get_pci_reg_index(devi,
- PCI_REG_ADDR_M|PCI_REG_REL_M,
- PCI_ADDR_IO|PCI_RELOCAT_B, VGA_REG_ADDR,
- &reg_offset);
+ PCI_REG_ADDR_M|PCI_REG_REL_M,
+ PCI_ADDR_IO|PCI_RELOCAT_B, VGA_REG_ADDR,
+ &reg_offset);
if (reg_rnumber < 0) {
cmn_err(CE_WARN,
- MYNAME ": can't find reg entry for registers");
+ MYNAME ": can't find reg entry for registers");
error = DDI_FAILURE;
goto fail;
}
softc->fb_regno = vgatext_get_pci_reg_index(devi,
- PCI_REG_ADDR_M|PCI_REG_REL_M,
- PCI_ADDR_MEM32|PCI_RELOCAT_B, VGA_MEM_ADDR,
- &mem_offset);
+ PCI_REG_ADDR_M|PCI_REG_REL_M,
+ PCI_ADDR_MEM32|PCI_RELOCAT_B, VGA_MEM_ADDR,
+ &mem_offset);
if (softc->fb_regno < 0) {
cmn_err(CE_WARN,
- MYNAME ": can't find reg entry for memory");
+ MYNAME ": can't find reg entry for memory");
error = DDI_FAILURE;
goto fail;
}
agpm = 1; /* should have AGP master support */
} else {
cmn_err(CE_WARN, MYNAME ": unknown parent type \"%s\".",
- parent_type);
+ parent_type);
error = DDI_FAILURE;
goto fail;
}
@@ -492,8 +507,8 @@ vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
parent_type = NULL;
error = ddi_regs_map_setup(devi, reg_rnumber,
- (caddr_t *)&softc->regs.addr, reg_offset, VGA_REG_SIZE,
- &dev_attr, &softc->regs.handle);
+ (caddr_t *)&softc->regs.addr, reg_offset, VGA_REG_SIZE,
+ &dev_attr, &softc->regs.handle);
if (error != DDI_SUCCESS)
goto fail;
softc->regs.mapped = B_TRUE;
@@ -501,9 +516,9 @@ vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
softc->fb_size = VGA_MEM_SIZE;
error = ddi_regs_map_setup(devi, softc->fb_regno,
- (caddr_t *)&softc->fb.addr,
- mem_offset, softc->fb_size,
- &dev_attr, &softc->fb.handle);
+ (caddr_t *)&softc->fb.addr,
+ mem_offset, softc->fb_size,
+ &dev_attr, &softc->fb.handle);
if (error != DDI_SUCCESS)
goto fail;
softc->fb.mapped = B_TRUE;
@@ -527,7 +542,7 @@ vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
goto fail;
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
- DDI_PROP_DONTPASS, "console", &cons) == DDI_SUCCESS) {
+ DDI_PROP_DONTPASS, "console", &cons) == DDI_SUCCESS) {
if (strcmp(cons, "graphics") == 0) {
happyface_boot = 1;
vgatext_silent = 1;
@@ -589,6 +604,28 @@ vgatext_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
(void) ddi_soft_state_free(vgatext_softc_head, instance);
return (DDI_SUCCESS);
+ case DDI_SUSPEND:
+ /*
+ * This is a generic VGA file, and therefore, cannot
+ * understand how to deal with suspend and resume on
+ * a generic interface. So we fail any attempt to
+ * suspend. At some point in the future, we might use
+ * this as an entrypoint for display drivers and this
+ * assumption may change.
+ *
+ * However, from a platform development perspective,
+ * it is important that this driver suspend if a
+ * developer is using a serial console and/or working
+ * on a framebuffer driver that will support suspend
+ * and resume. Therefore, we have this module tunable
+ * (purposely using a long name) that will allow for
+ * suspend it it is set. Otherwise we fail.
+ */
+ if (vgatext_force_suspend != 0)
+ return (DDI_SUCCESS);
+ else
+ return (DDI_FAILURE);
+
default:
cmn_err(CE_WARN, "vgatext_detach: unknown cmd 0x%x\n", cmd);
return (DDI_FAILURE);
@@ -676,59 +713,59 @@ do_gfx_ioctl(int cmd, intptr_t data, int mode, struct vgatext_softc *softc)
case VIS_DEVINIT:
- if (!(mode & FKIOCTL)) {
- cmn_err(CE_CONT, kernel_only, "VIS_DEVINIT");
- return (ENXIO);
- }
+ if (!(mode & FKIOCTL)) {
+ cmn_err(CE_CONT, kernel_only, "VIS_DEVINIT");
+ return (ENXIO);
+ }
- err = vgatext_devinit(softc, (struct vis_devinit *)data);
- if (err != 0) {
- cmn_err(CE_WARN,
- "vgatext_ioctl: could not initialize console");
- return (err);
- }
- break;
+ err = vgatext_devinit(softc, (struct vis_devinit *)data);
+ if (err != 0) {
+ cmn_err(CE_WARN,
+ "vgatext_ioctl: could not initialize console");
+ return (err);
+ }
+ break;
case VIS_CONSCOPY: /* move */
- {
- struct vis_conscopy pma;
+ {
+ struct vis_conscopy pma;
- if (ddi_copyin((void *)data, &pma,
- sizeof (struct vis_conscopy), mode))
- return (EFAULT);
+ if (ddi_copyin((void *)data, &pma,
+ sizeof (struct vis_conscopy), mode))
+ return (EFAULT);
- vgatext_cons_copy(softc, &pma);
- break;
- }
+ vgatext_cons_copy(softc, &pma);
+ break;
+ }
case VIS_CONSDISPLAY: /* display */
- {
- struct vis_consdisplay display_request;
+ {
+ struct vis_consdisplay display_request;
- if (ddi_copyin((void *)data, &display_request,
- sizeof (display_request), mode))
- return (EFAULT);
+ if (ddi_copyin((void *)data, &display_request,
+ sizeof (display_request), mode))
+ return (EFAULT);
- vgatext_cons_display(softc, &display_request);
- break;
- }
+ vgatext_cons_display(softc, &display_request);
+ break;
+ }
case VIS_CONSCURSOR:
- {
- struct vis_conscursor cursor_request;
+ {
+ struct vis_conscursor cursor_request;
- if (ddi_copyin((void *)data, &cursor_request,
- sizeof (cursor_request), mode))
- return (EFAULT);
+ if (ddi_copyin((void *)data, &cursor_request,
+ sizeof (cursor_request), mode))
+ return (EFAULT);
- vgatext_cons_cursor(softc, &cursor_request);
+ vgatext_cons_cursor(softc, &cursor_request);
- if (cursor_request.action == VIS_GET_CURSOR &&
- ddi_copyout(&cursor_request, (void *)data,
- sizeof (cursor_request), mode))
- return (EFAULT);
- break;
- }
+ if (cursor_request.action == VIS_GET_CURSOR &&
+ ddi_copyout(&cursor_request, (void *)data,
+ sizeof (cursor_request), mode))
+ return (EFAULT);
+ break;
+ }
case VIS_GETCMAP:
case VIS_PUTCMAP:
@@ -807,7 +844,7 @@ vgatext_kdsetmode(struct vgatext_softc *softc, int mode)
softc->current_base = softc->text_base;
if (softc->cursor.visible) {
vgatext_set_cursor(softc,
- softc->cursor.row, softc->cursor.col);
+ softc->cursor.row, softc->cursor.col);
}
vgatext_restore_colormap(softc);
break;
@@ -852,7 +889,7 @@ vgatext_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
}
if (!(off >= VGA_MMAP_FB_BASE &&
- off < VGA_MMAP_FB_BASE + softc->fb_size)) {
+ off < VGA_MMAP_FB_BASE + softc->fb_size)) {
cmn_err(CE_WARN, "vgatext: Can't map offset 0x%llx", off);
return (-1);
}
@@ -863,8 +900,8 @@ vgatext_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
length = len;
if ((err = devmap_devmem_setup(dhp, softc->devi, NULL, softc->fb_regno,
- off - VGA_MMAP_FB_BASE,
- length, PROT_ALL, 0, &dev_attr)) < 0) {
+ off - VGA_MMAP_FB_BASE,
+ length, PROT_ALL, 0, &dev_attr)) < 0) {
return (err);
}
@@ -924,10 +961,10 @@ vgatext_cons_display(struct vgatext_softc *softc, struct vis_consdisplay *da)
* system startup graphics.
*/
attr = (solaris_color_to_pc_color[da->bg_color & 0xf] << 4)
- | solaris_color_to_pc_color[da->fg_color & 0xf];
+ | solaris_color_to_pc_color[da->fg_color & 0xf];
string = da->data;
addr = (struct cgatext *)softc->current_base
- + (da->row * TEXT_COLS + da->col);
+ + (da->row * TEXT_COLS + da->col);
for (i = 0; i < da->width; i++) {
addr->ch = string[i];
addr->attr = attr;
@@ -1130,20 +1167,20 @@ vgatext_set_text(struct vgatext_softc *softc)
/* set sequencer registers */
vga_set_seq(&softc->regs, VGA_SEQ_RST_SYN,
- (vga_get_seq(&softc->regs, VGA_SEQ_RST_SYN) &
- ~VGA_SEQ_RST_SYN_NO_SYNC_RESET));
+ (vga_get_seq(&softc->regs, VGA_SEQ_RST_SYN) &
+ ~VGA_SEQ_RST_SYN_NO_SYNC_RESET));
for (i = 1; i < NUM_SEQ_REG; i++) {
vga_set_seq(&softc->regs, i, VGA_SEQ_TEXT[i]);
}
vga_set_seq(&softc->regs, VGA_SEQ_RST_SYN,
- (vga_get_seq(&softc->regs, VGA_SEQ_RST_SYN) |
- VGA_SEQ_RST_SYN_NO_ASYNC_RESET |
- VGA_SEQ_RST_SYN_NO_SYNC_RESET));
+ (vga_get_seq(&softc->regs, VGA_SEQ_RST_SYN) |
+ VGA_SEQ_RST_SYN_NO_ASYNC_RESET |
+ VGA_SEQ_RST_SYN_NO_SYNC_RESET));
/* set crt controller registers */
vga_set_crtc(&softc->regs, VGA_CRTC_VRE,
- (vga_get_crtc(&softc->regs, VGA_CRTC_VRE) &
- ~VGA_CRTC_VRE_LOCK));
+ (vga_get_crtc(&softc->regs, VGA_CRTC_VRE) &
+ ~VGA_CRTC_VRE_LOCK));
for (i = 0; i < NUM_CRTC_REG; i++) {
vga_set_crtc(&softc->regs, i, VGA_CRTC_TEXT[i]);
}
@@ -1161,8 +1198,8 @@ vgatext_set_text(struct vgatext_softc *softc)
/* set palette */
for (i = 0; i < VGA_TEXT_CMAP_ENTRIES; i++) {
vga_put_cmap(&softc->regs, i, VGA_TEXT_PALETTES[i][0] << 2,
- VGA_TEXT_PALETTES[i][1] << 2,
- VGA_TEXT_PALETTES[i][2] << 2);
+ VGA_TEXT_PALETTES[i][1] << 2,
+ VGA_TEXT_PALETTES[i][2] << 2);
}
for (i = VGA_TEXT_CMAP_ENTRIES; i < VGA8_CMAP_ENTRIES; i++) {
vga_put_cmap(&softc->regs, i, 0, 0, 0);
@@ -1185,10 +1222,10 @@ vgatext_init(struct vgatext_softc *softc)
vga_set_atr(&softc->regs, VGA_ATR_MODE, atr_mode);
#if defined(USE_BORDERS)
vga_set_atr(&softc->regs, VGA_ATR_BDR_CLR,
- vga_get_atr(&softc->regs, VGA_BRIGHT_WHITE));
+ vga_get_atr(&softc->regs, VGA_BRIGHT_WHITE));
#else
vga_set_atr(&softc->regs, VGA_ATR_BDR_CLR,
- vga_get_atr(&softc->regs, VGA_BLACK));
+ vga_get_atr(&softc->regs, VGA_BLACK));
#endif
vgatext_setfont(softc); /* need selectable font? */
}
@@ -1198,7 +1235,7 @@ static void
vgatext_init_graphics(struct vgatext_softc *softc)
{
vga_set_atr(&softc->regs, VGA_ATR_BDR_CLR,
- vga_get_atr(&softc->regs, VGA_BLACK));
+ vga_get_atr(&softc->regs, VGA_BLACK));
}
#endif
@@ -1301,9 +1338,9 @@ vgatext_save_colormap(struct vgatext_softc *softc)
}
for (i = 0; i < VGA8_CMAP_ENTRIES; i++) {
vga_get_cmap(&softc->regs, i,
- &softc->colormap[i].red,
- &softc->colormap[i].green,
- &softc->colormap[i].blue);
+ &softc->colormap[i].red,
+ &softc->colormap[i].green,
+ &softc->colormap[i].blue);
}
}
@@ -1317,9 +1354,9 @@ vgatext_restore_colormap(struct vgatext_softc *softc)
}
for (i = 0; i < VGA8_CMAP_ENTRIES; i++) {
vga_put_cmap(&softc->regs, i,
- softc->colormap[i].red,
- softc->colormap[i].green,
- softc->colormap[i].blue);
+ softc->colormap[i].red,
+ softc->colormap[i].green,
+ softc->colormap[i].blue);
}
}
@@ -1354,7 +1391,7 @@ vgatext_get_pci_reg_index(
pci_regspec_t *reg;
if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
- "reg", (caddr_t)&reg, &length) != DDI_PROP_SUCCESS) {
+ "reg", (caddr_t)&reg, &length) != DDI_PROP_SUCCESS) {
return (-1);
}
@@ -1413,7 +1450,7 @@ vgatext_get_isa_reg_index(
struct regspec *reg;
if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
- "reg", (caddr_t)&reg, &length) != DDI_PROP_SUCCESS) {
+ "reg", (caddr_t)&reg, &length) != DDI_PROP_SUCCESS) {
return (-1);
}
diff --git a/usr/src/uts/intel/os/cpr_intel.c b/usr/src/uts/intel/os/cpr_intel.c
new file mode 100644
index 0000000000..b34041386b
--- /dev/null
+++ b/usr/src/uts/intel/os/cpr_intel.c
@@ -0,0 +1,75 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * cpr functions for supported sparc platforms
+ */
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/cpr.h>
+#include <sys/kmem.h>
+#include <sys/errno.h>
+
+/*
+ * setup the original and new sets of property names/values
+ * Not relevant to S3, which is all we support for now.
+ */
+/*ARGSUSED*/
+int
+cpr_default_setup(int alloc)
+{
+ return (0);
+}
+
+void
+cpr_send_notice(void)
+{
+ static char cstr[] = "\014" "\033[1P" "\033[18;21H";
+
+ prom_printf(cstr);
+ prom_printf("Saving System State. Please Wait... ");
+}
+
+void
+cpr_spinning_bar(void)
+{
+ static char *spin_strings[] = { "|\b", "/\b", "-\b", "\\\b" };
+ static int idx;
+
+ prom_printf(spin_strings[idx]);
+ if (++idx == 4)
+ idx = 0;
+}
+
+void
+cpr_resume_notice(void)
+{
+ static char cstr[] = "\014" "\033[1P" "\033[18;21H";
+
+ prom_printf(cstr);
+ prom_printf("Restoring System State. Please Wait... ");
+}
diff --git a/usr/src/uts/intel/os/name_to_major b/usr/src/uts/intel/os/name_to_major
index 9d983ec052..28810d57b0 100644
--- a/usr/src/uts/intel/os/name_to_major
+++ b/usr/src/uts/intel/os/name_to_major
@@ -127,6 +127,8 @@ evtchn 197
xdb 199
domcaps 200
balloon 201
+acpippm 202
+srn 203
did 239
lx_ptm 240
lx_systrace 241
diff --git a/usr/src/uts/intel/promif/prom_env.c b/usr/src/uts/intel/promif/prom_env.c
index 4b43f678ef..73b2a08af6 100644
--- a/usr/src/uts/intel/promif/prom_env.c
+++ b/usr/src/uts/intel/promif/prom_env.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -47,6 +47,7 @@ static promif_owrap_t nullwrapper =
static promif_owrap_t *wrapper = &nullwrapper;
static promif_owrap_t pmwrapper;
+static promif_owrap_t *saved_wrapper;
promif_owrap_t
*promif_preout(void)
@@ -69,3 +70,17 @@ prom_set_outfuncs(void (*pref)(void), void (*postf)(void))
pmwrapper.postout = postf;
wrapper = &pmwrapper;
}
+
+void
+prom_suspend_prepost(void)
+{
+ saved_wrapper = wrapper;
+ wrapper = &nullwrapper;
+}
+
+void
+prom_resume_prepost(void)
+{
+ wrapper = saved_wrapper;
+ saved_wrapper = NULL;
+}
diff --git a/usr/src/uts/intel/srn/Makefile b/usr/src/uts/intel/srn/Makefile
new file mode 100755
index 0000000000..8146241688
--- /dev/null
+++ b/usr/src/uts/intel/srn/Makefile
@@ -0,0 +1,84 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# uts/intel/srn/Makefile
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of the srn driver
+#
+# intel architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = srn
+OBJECTS = $(SRN_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(SRN_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/io
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/sys/acpica.h b/usr/src/uts/intel/sys/acpica.h
index b760afcb97..40e830fd05 100644
--- a/usr/src/uts/intel/sys/acpica.h
+++ b/usr/src/uts/intel/sys/acpica.h
@@ -128,6 +128,8 @@ extern ACPI_STATUS acpica_get_handle(dev_info_t *, ACPI_HANDLE *);
extern ACPI_STATUS acpica_eval_int(ACPI_HANDLE, char *, int *);
extern void acpica_map_cpu(processorid_t, MADT_PROCESSOR_APIC *);
extern void acpica_build_processor_map();
+extern void acpica_ddi_save_resources(dev_info_t *);
+extern void acpica_ddi_restore_resources(dev_info_t *);
#ifdef __cplusplus
}
diff --git a/usr/src/uts/intel/sys/promif.h b/usr/src/uts/intel/sys/promif.h
index 20ce5c2db1..ee7e138f1e 100644
--- a/usr/src/uts/intel/sys/promif.h
+++ b/usr/src/uts/intel/sys/promif.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -219,6 +218,9 @@ typedef struct promif_owrap {
void (*postout)(void);
} promif_owrap_t;
+extern void prom_suspend_prepost(void);
+extern void prom_resume_prepost(void);
+
/*
* WAN boot key storage interface
*/
diff --git a/usr/src/uts/sparc/os/cpr_sparc.c b/usr/src/uts/sparc/os/cpr_sparc.c
index ebef17c4fa..3dd44bb9e6 100644
--- a/usr/src/uts/sparc/os/cpr_sparc.c
+++ b/usr/src/uts/sparc/os/cpr_sparc.c
@@ -337,6 +337,15 @@ cpr_spinning_bar(void)
idx = 0;
}
+void
+cpr_resume_notice(void)
+{
+ static char cstr[] = "\014" "\033[1P" "\033[18;21H";
+
+ prom_printf(cstr);
+ prom_printf("Restoring System State. Please Wait... ");
+}
+
/*
* Convert a full device path to its shortest unambiguous equivalent.
* For example, a path which starts out /iommu@x,y/sbus@i,j/espdma . . .
diff --git a/usr/src/uts/sun4u/os/cpr_impl.c b/usr/src/uts/sun4u/os/cpr_impl.c
index 4c5b415b0a..e0c6a4b2d8 100644
--- a/usr/src/uts/sun4u/os/cpr_impl.c
+++ b/usr/src/uts/sun4u/os/cpr_impl.c
@@ -65,6 +65,10 @@
#include <vm/vm_dep.h>
extern void cpr_clear_bitmaps(void);
+extern int cpr_setbit(pfn_t ppn, int mapflag);
+extern int cpr_clrbit(pfn_t ppn, int mapflag);
+extern pgcnt_t cpr_scan_kvseg(int mapflag, bitfunc_t bitfunc, struct seg *seg);
+extern pgcnt_t cpr_count_seg_pages(int mapflag, bitfunc_t bitfunc);
extern void dtlb_wr_entry(uint_t, tte_t *, uint64_t *);
extern void itlb_wr_entry(uint_t, tte_t *, uint64_t *);
@@ -641,12 +645,6 @@ i_cpr_save_machdep_info(void)
}
-void
-i_cpr_set_tbr(void)
-{
-}
-
-
/*
* cpu0 should contain bootcpu info
*/
@@ -656,6 +654,11 @@ i_cpr_bootcpu(void)
return (&cpu0);
}
+processorid_t
+i_cpr_bootcpuid(void)
+{
+ return (0);
+}
/*
* Return the virtual address of the mapping area
@@ -1761,7 +1764,7 @@ i_cpr_dump_setup(vnode_t *vp)
int
-i_cpr_is_supported(void)
+i_cpr_is_supported(int sleeptype)
{
char es_prop[] = "energystar-v2";
pnode_t node;
@@ -1769,6 +1772,9 @@ i_cpr_is_supported(void)
extern int cpr_supported_override;
extern int cpr_platform_enable;
+ if (sleeptype != CPR_TODISK)
+ return (0);
+
/*
* The next statement tests if a specific platform has turned off
* cpr support.
@@ -1924,3 +1930,103 @@ i_cpr_alloc_bitmaps(void)
i_cpr_bitmap_cleanup();
return (err);
}
+
+
+
+/*
+ * Power down the system.
+ */
+int
+i_cpr_power_down(int sleeptype)
+{
+ int is_defined = 0;
+ char *wordexists = "p\" power-off\" find nip swap l! ";
+ char *req = "power-off";
+
+ ASSERT(sleeptype == CPR_TODISK);
+
+ /*
+ * is_defined has value -1 when defined
+ */
+ prom_interpret(wordexists, (uintptr_t)&is_defined, 0, 0, 0, 0);
+ if (is_defined) {
+ CPR_DEBUG(CPR_DEBUG1, "\ncpr: %s...\n", req);
+ prom_interpret(req, 0, 0, 0, 0, 0);
+ }
+ /*
+ * Only returns if failed
+ */
+ return (EIO);
+}
+
+void
+i_cpr_stop_other_cpus(void)
+{
+ stop_other_cpus();
+}
+
+/*
+ * Save context for the specified CPU
+ */
+/* ARGSUSED */
+void *
+i_cpr_save_context(void *arg)
+{
+ /*
+ * Not yet
+ */
+ ASSERT(0);
+ return (NULL);
+}
+
+void
+i_cpr_pre_resume_cpus(void)
+{
+ /*
+ * Not yet
+ */
+ ASSERT(0);
+}
+
+void
+i_cpr_post_resume_cpus(void)
+{
+ /*
+ * Not yet
+ */
+ ASSERT(0);
+}
+
+/*
+ * nothing to do
+ */
+void
+i_cpr_alloc_cpus(void)
+{
+}
+
+/*
+ * nothing to do
+ */
+void
+i_cpr_free_cpus(void)
+{
+}
+
+/* ARGSUSED */
+void
+i_cpr_save_configuration(dev_info_t *dip)
+{
+ /*
+ * this is a no-op on sparc
+ */
+}
+
+/* ARGSUSED */
+void
+i_cpr_restore_configuration(dev_info_t *dip)
+{
+ /*
+ * this is a no-op on sparc
+ */
+}
diff --git a/usr/src/uts/sun4u/sys/machclock.h b/usr/src/uts/sun4u/sys/machclock.h
index 3bd3ad7e7b..e0bc501de7 100644
--- a/usr/src/uts/sun4u/sys/machclock.h
+++ b/usr/src/uts/sun4u/sys/machclock.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -104,6 +104,17 @@ struct tod_ops {
extern struct tod_ops tod_ops;
extern char *tod_module_name;
+/*
+ * These defines allow common code to use TOD functions independant
+ * of hardware platform.
+ */
+#define TODOP_GET(top) ((top).tod_get())
+#define TODOP_SET(top, ts) ((top).tod_set(ts))
+#define TODOP_SETWD(top, nsec) ((top).tod_set_watchdog_timer(nsec))
+#define TODOP_CLRWD(top) ((top).tod_clear_watchdog_timer())
+#define TODOP_SETWAKE(top, ts) ((top).tod_set_power_alarm(ts))
+#define TODOP_CLRWAKE(top) ((top).tod_clear_power_alarm())
+
#endif /* _KERNEL */
#endif /* _ASM */