diff options
Diffstat (limited to 'usr/src/uts/i86pc/io')
-rw-r--r-- | usr/src/uts/i86pc/io/acpippm.conf | 28 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/cbe.c | 50 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/consplat.c | 3 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/gfx_private/gfxp_vgatext.c | 320 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/mc/mcamd_drv.c | 20 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/mp_platform_common.c | 182 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/pci/pci.c | 68 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/pciex/npe.c | 61 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/pcplusmp/apic.c | 11 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/ppm.conf | 46 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/ppm/acpippm.c | 443 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/ppm/acpippm.h | 41 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/ppm/acpisleep.c | 214 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/psm/psm_common.c | 38 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/psm/uppc.c | 2 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/todpc_subr.c | 229 |
16 files changed, 1610 insertions, 146 deletions
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 }; |