diff options
author | randyf <none@none> | 2007-10-20 16:00:42 -0700 |
---|---|---|
committer | randyf <none@none> | 2007-10-20 16:00:42 -0700 |
commit | 2df1fe9ca32bb227b9158c67f5c00b54c20b10fd (patch) | |
tree | 358c576f885c00d42a760d9e35e5b66e77209fe2 | |
parent | 10b3fbf593a6678eec9b50a01903ef4eb73111e4 (diff) | |
download | illumos-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
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)®list, ®len) != 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)®list, ®len) != DDI_PROP_SUCCESS) { + "reg", (caddr_t)®list, ®len) != 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, - ®_offset); + ®_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, - ®_offset); + PCI_REG_ADDR_M|PCI_REG_REL_M, + PCI_ADDR_IO|PCI_RELOCAT_B, VGA_REG_ADDR, + ®_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)®, &length) != DDI_PROP_SUCCESS) { + "reg", (caddr_t)®, &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)®, &length) != DDI_PROP_SUCCESS) { + "reg", (caddr_t)®, &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, - ®len) != DDI_PROP_SUCCESS) { + DDI_PROP_DONTPASS, "reg", (int **)&drv_regp, + ®len) != 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, ®size); 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, ®size); 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, ®size); 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, ®len) != DDI_SUCCESS) + DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg", + (caddr_t)&drv_regp, ®len) != 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, - ®_offset); + ®_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, - ®_offset); + PCI_REG_ADDR_M|PCI_REG_REL_M, + PCI_ADDR_IO|PCI_RELOCAT_B, VGA_REG_ADDR, + ®_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)®, &length) != DDI_PROP_SUCCESS) { + "reg", (caddr_t)®, &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)®, &length) != DDI_PROP_SUCCESS) { + "reg", (caddr_t)®, &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 */ |