0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * CDDL HEADER START
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * The contents of this file are subject to the terms of the
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Common Development and Distribution License (the "License").
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * You may not use this file except in compliance with the License.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * or http://www.opensolaris.org/os/licensing.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * See the License for the specific language governing permissions
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * and limitations under the License.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * When distributing Covered Code, include this CDDL HEADER in each
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * If applicable, add the following below this CDDL HEADER, with the
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * fields enclosed by brackets "[]" replaced with your own identifying
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * information: Portions Copyright [yyyy] [name of copyright owner]
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * CDDL HEADER END
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
5cd376e8b7030707d78315f63adb4bb2b4d9963eJimmy Vetayases * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe#include <sys/hpet_acpi.h>
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe#include <sys/hpet.h>
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe#include <sys/bitmap.h>
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe#include <sys/inttypes.h>
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe#include <sys/time.h>
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe#include <sys/sunddi.h>
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe#include <sys/ksynch.h>
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe#include <sys/apic.h>
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe#include <sys/callb.h>
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe#include <sys/clock.h>
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe#include <sys/archsystm.h>
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe#include <sys/cpupart.h>
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic int hpet_init_proxy(int *hpet_vect, iflag_t *hpet_flags);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic boolean_t hpet_install_proxy(void);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic boolean_t hpet_callback(int code);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic boolean_t hpet_cpr(int code);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic boolean_t hpet_resume(void);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic void hpet_cst_callback(uint32_t code);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic boolean_t hpet_deep_idle_config(int code);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic int hpet_validate_table(ACPI_TABLE_HPET *hpet_table);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic boolean_t hpet_checksum_table(unsigned char *table, unsigned int len);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic void *hpet_memory_map(ACPI_TABLE_HPET *hpet_table);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic int hpet_start_main_counter(hpet_info_t *hip);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic int hpet_stop_main_counter(hpet_info_t *hip);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic uint64_t hpet_read_main_counter_value(hpet_info_t *hip);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic uint64_t hpet_set_leg_rt_cnf(hpet_info_t *hip, uint32_t new_value);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic uint64_t hpet_read_gen_cap(hpet_info_t *hip);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic uint64_t hpet_read_gen_config(hpet_info_t *hip);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic uint64_t hpet_read_gen_intrpt_stat(hpet_info_t *hip);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic uint64_t hpet_read_timer_N_config(hpet_info_t *hip, uint_t n);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic hpet_TN_conf_cap_t hpet_convert_timer_N_config(uint64_t conf);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic void hpet_write_gen_config(hpet_info_t *hip, uint64_t l);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic void hpet_write_gen_intrpt_stat(hpet_info_t *hip, uint64_t l);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic void hpet_write_timer_N_config(hpet_info_t *hip, uint_t n, uint64_t l);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic void hpet_write_timer_N_comp(hpet_info_t *hip, uint_t n, uint64_t l);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic void hpet_disable_timer(hpet_info_t *hip, uint32_t timer_n);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic void hpet_enable_timer(hpet_info_t *hip, uint32_t timer_n);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic int hpet_get_IOAPIC_intr_capable_timer(hpet_info_t *hip);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic int hpet_timer_available(uint32_t allocated_timers, uint32_t n);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic void hpet_timer_alloc(uint32_t *allocated_timers, uint32_t n);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic void hpet_timer_set_up(hpet_info_t *hip, uint32_t timer_n,
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayases uint32_t interrupt);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic uint_t hpet_isr(char *arg);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic uint32_t hpet_install_interrupt_handler(uint_t (*func)(char *),
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayases int vector);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic void hpet_uninstall_interrupt_handler(void);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic void hpet_expire_all(void);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic boolean_t hpet_guaranteed_schedule(hrtime_t required_wakeup_time);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic boolean_t hpet_use_hpet_timer(hrtime_t *expire);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic void hpet_use_lapic_timer(hrtime_t expire);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesstatic void hpet_init_proxy_data(void);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayases
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * hpet_state_lock is used to synchronize disabling/enabling deep c-states
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * and to synchronize suspend/resume.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic kmutex_t hpet_state_lock;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic struct hpet_state {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe boolean_t proxy_installed; /* CBE proxy interrupt setup */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe boolean_t cpr; /* currently in CPR */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe boolean_t cpu_deep_idle; /* user enable/disable */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe boolean_t uni_cstate; /* disable if only one cstate */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe} hpet_state = { B_FALSE, B_FALSE, B_TRUE, B_TRUE};
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxeuint64_t hpet_spin_check = HPET_SPIN_CHECK;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxeuint64_t hpet_spin_timeout = HPET_SPIN_TIMEOUT;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxeuint64_t hpet_idle_spin_timeout = HPET_SPIN_TIMEOUT;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxeuint64_t hpet_isr_spin_timeout = HPET_SPIN_TIMEOUT;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic kmutex_t hpet_proxy_lock; /* lock for lAPIC proxy data */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * hpet_proxy_users is a per-cpu array.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic hpet_proxy_t *hpet_proxy_users; /* one per CPU */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric SaxeACPI_TABLE_HPET *hpet_table; /* ACPI HPET table */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_info_t hpet_info; /* Human readable Information */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Provide HPET access from unix.so.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Set up pointers to access symbols in pcplusmp.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic void
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_establish_hooks(void)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet.install_proxy = &hpet_install_proxy;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet.callback = &hpet_callback;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet.use_hpet_timer = &hpet_use_hpet_timer;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet.use_lapic_timer = &hpet_use_lapic_timer;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Get the ACPI "HPET" table.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * acpi_probe() calls this function from mp_startup before drivers are loaded.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * acpi_probe() verified the system is using ACPI before calling this.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * There may be more than one ACPI HPET table (Itanium only?).
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Intel's HPET spec defines each timer block to have up to 32 counters and
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * be 1024 bytes long. There can be more than one timer block of 32 counters.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Each timer block would have an additional ACPI HPET table.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Typical x86 systems today only have 1 HPET with 3 counters.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * On x86 we only consume HPET table "1" for now.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxeint
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_acpi_init(int *hpet_vect, iflag_t *hpet_flags)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe extern hrtime_t tsc_read(void);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe extern int idle_cpu_no_deep_c;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe extern int cpuid_deep_cstates_supported(void);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe void *la;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint64_t ret;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint_t num_timers;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint_t ti;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe (void) memset(&hpet_info, 0, sizeof (hpet_info));
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet.supported = HPET_NO_SUPPORT;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (idle_cpu_no_deep_c)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (DDI_FAILURE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (!cpuid_deep_cstates_supported())
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (DDI_FAILURE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_establish_hooks();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Get HPET ACPI table 1.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (ACPI_FAILURE(AcpiGetTable(ACPI_SIG_HPET, HPET_TABLE_1,
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe (ACPI_TABLE_HEADER **)&hpet_table))) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_NOTE, "!hpet_acpi: unable to get ACPI HPET table");
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (DDI_FAILURE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet_validate_table(hpet_table) != AE_OK) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_NOTE, "!hpet_acpi: invalid HPET table");
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (DDI_FAILURE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe la = hpet_memory_map(hpet_table);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (la == NULL) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_NOTE, "!hpet_acpi: memory map HPET failed");
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (DDI_FAILURE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.logical_address = la;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ret = hpet_read_gen_cap(&hpet_info);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.gen_cap.counter_clk_period = HPET_GCAP_CNTR_CLK_PERIOD(ret);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.gen_cap.vendor_id = HPET_GCAP_VENDOR_ID(ret);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.gen_cap.leg_route_cap = HPET_GCAP_LEG_ROUTE_CAP(ret);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.gen_cap.count_size_cap = HPET_GCAP_CNT_SIZE_CAP(ret);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Hardware contains the last timer's number.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Add 1 to get the number of timers.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.gen_cap.num_tim_cap = HPET_GCAP_NUM_TIM_CAP(ret) + 1;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.gen_cap.rev_id = HPET_GCAP_REV_ID(ret);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet_info.gen_cap.counter_clk_period > HPET_MAX_CLK_PERIOD) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_NOTE, "!hpet_acpi: COUNTER_CLK_PERIOD 0x%lx > 0x%lx",
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe (long)hpet_info.gen_cap.counter_clk_period,
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe (long)HPET_MAX_CLK_PERIOD);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (DDI_FAILURE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe num_timers = (uint_t)hpet_info.gen_cap.num_tim_cap;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if ((num_timers < 3) || (num_timers > 32)) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_NOTE, "!hpet_acpi: invalid number of HPET timers "
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe "%lx", (long)num_timers);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (DDI_FAILURE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.timer_n_config = (hpet_TN_conf_cap_t *)kmem_zalloc(
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe num_timers * sizeof (uint64_t), KM_SLEEP);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ret = hpet_read_gen_config(&hpet_info);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.gen_config.leg_rt_cnf = HPET_GCFR_LEG_RT_CNF_BITX(ret);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.gen_config.enable_cnf = HPET_GCFR_ENABLE_CNF_BITX(ret);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Solaris does not use the HPET Legacy Replacement Route capabilities.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * This feature has been off by default on test systems.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * The HPET spec does not specify if Legacy Replacement Route is
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * on or off by default, so we explicitely set it off here.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * It should not matter which mode the HPET is in since we use
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * the first available non-legacy replacement timer: timer 2.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe (void) hpet_set_leg_rt_cnf(&hpet_info, 0);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ret = hpet_read_gen_config(&hpet_info);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.gen_config.leg_rt_cnf = HPET_GCFR_LEG_RT_CNF_BITX(ret);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.gen_config.enable_cnf = HPET_GCFR_ENABLE_CNF_BITX(ret);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.gen_intrpt_stat = hpet_read_gen_intrpt_stat(&hpet_info);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.main_counter_value = hpet_read_main_counter_value(&hpet_info);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe for (ti = 0; ti < num_timers; ++ti) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ret = hpet_read_timer_N_config(&hpet_info, ti);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Make sure no timers are enabled (think fast reboot or
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * virtual hardware).
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (ret & HPET_TIMER_N_INT_ENB_CNF_BIT) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_disable_timer(&hpet_info, ti);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ret &= ~HPET_TIMER_N_INT_ENB_CNF_BIT;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.timer_n_config[ti] = hpet_convert_timer_N_config(ret);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Be aware the Main Counter may need to be initialized in the future
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * if it is used for more than just Deep C-State support.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * The HPET's Main Counter does not need to be initialize to a specific
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * value before starting it for use to wake up CPUs from Deep C-States.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet_start_main_counter(&hpet_info) != AE_OK) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_NOTE, "!hpet_acpi: hpet_start_main_counter failed");
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (DDI_FAILURE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.period = hpet_info.gen_cap.counter_clk_period;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Read main counter twice to record HPET latency for debugging.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.tsc[0] = tsc_read();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.hpet_main_counter_reads[0] =
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_read_main_counter_value(&hpet_info);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.tsc[1] = tsc_read();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.hpet_main_counter_reads[1] =
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_read_main_counter_value(&hpet_info);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.tsc[2] = tsc_read();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ret = hpet_read_gen_config(&hpet_info);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.gen_config.leg_rt_cnf = HPET_GCFR_LEG_RT_CNF_BITX(ret);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.gen_config.enable_cnf = HPET_GCFR_ENABLE_CNF_BITX(ret);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * HPET main counter reads are supported now.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet.supported = HPET_TIMER_SUPPORT;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (hpet_init_proxy(hpet_vect, hpet_flags));
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxevoid
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_acpi_fini(void)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet.supported == HPET_NO_SUPPORT)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet.supported >= HPET_TIMER_SUPPORT)
9aa01d984654bcf5acc197ab08e46d4b6d939871Bill Holler (void) hpet_stop_main_counter(&hpet_info);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet.supported > HPET_TIMER_SUPPORT)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_disable_timer(&hpet_info, hpet_info.cstate_timer.timer);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Do initial setup to use a HPET timer as a proxy for Deep C-state stalled
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * LAPIC Timers. Get a free HPET timer that supports I/O APIC routed interrupt.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Setup data to handle the timer's ISR, and add the timer's interrupt.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * The ddi cannot be use to allocate the HPET timer's interrupt.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * ioapic_init_intr() in mp_platform_common() later sets up the I/O APIC
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * to handle the HPET timer's interrupt.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Note: FSB (MSI) interrupts are not currently supported by Intel HPETs as of
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * ICH9. The HPET spec allows for MSI. In the future MSI may be prefered.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic int
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_init_proxy(int *hpet_vect, iflag_t *hpet_flags)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet_get_IOAPIC_intr_capable_timer(&hpet_info) == -1) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_WARN, "!hpet_acpi: get ioapic intr failed.");
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (DDI_FAILURE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_init_proxy_data();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet_install_interrupt_handler(&hpet_isr,
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.cstate_timer.intr) != AE_OK) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_WARN, "!hpet_acpi: install interrupt failed.");
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (DDI_FAILURE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *hpet_vect = hpet_info.cstate_timer.intr;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_flags->intr_el = INTR_EL_LEVEL;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_flags->intr_po = INTR_PO_ACTIVE_HIGH;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_flags->bustype = BUS_PCI; /* we *do* conform to PCI */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Avoid a possibly stuck interrupt by programing the HPET's timer here
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * before the I/O APIC is programmed to handle this interrupt.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_timer_set_up(&hpet_info, hpet_info.cstate_timer.timer,
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.cstate_timer.intr);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * All HPET functionality is supported.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet.supported = HPET_FULL_SUPPORT;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (DDI_SUCCESS);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Called by kernel if it can support Deep C-States.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic boolean_t
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_install_proxy(void)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet_state.proxy_installed == B_TRUE)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (B_TRUE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet.supported != HPET_FULL_SUPPORT)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (B_FALSE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_enable_timer(&hpet_info, hpet_info.cstate_timer.timer);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_state.proxy_installed = B_TRUE;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (B_TRUE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Remove the interrupt that was added with add_avintr() in
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * hpet_install_interrupt_handler().
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic void
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_uninstall_interrupt_handler(void)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe rem_avintr(NULL, CBE_HIGH_PIL, (avfunc)&hpet_isr,
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.cstate_timer.intr);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic int
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_validate_table(ACPI_TABLE_HPET *hpet_table)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ACPI_TABLE_HEADER *table_header = (ACPI_TABLE_HEADER *)hpet_table;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (table_header->Length != sizeof (ACPI_TABLE_HPET)) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_WARN, "!hpet_validate_table: Length %lx != sizeof ("
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe "ACPI_TABLE_HPET) %lx.",
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe (unsigned long)((ACPI_TABLE_HEADER *)hpet_table)->Length,
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe (unsigned long)sizeof (ACPI_TABLE_HPET));
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (AE_ERROR);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (!ACPI_COMPARE_NAME(table_header->Signature, ACPI_SIG_HPET)) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_WARN, "!hpet_validate_table: Invalid HPET table "
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe "signature");
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (AE_ERROR);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (!hpet_checksum_table((unsigned char *)hpet_table,
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe (unsigned int)table_header->Length)) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_WARN, "!hpet_validate_table: Invalid HPET checksum");
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (AE_ERROR);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Sequence should be table number - 1. We are using table 1.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet_table->Sequence != HPET_TABLE_1 - 1) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_WARN, "!hpet_validate_table: Invalid Sequence %lx",
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe (long)hpet_table->Sequence);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (AE_ERROR);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (AE_OK);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic boolean_t
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_checksum_table(unsigned char *table, unsigned int length)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe unsigned char checksum = 0;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe int i;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe for (i = 0; i < length; ++i, ++table)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe checksum += *table;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (checksum == 0);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic void *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_memory_map(ACPI_TABLE_HPET *hpet_table)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (AcpiOsMapMemory(hpet_table->Address.Address, HPET_SIZE));
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic int
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_start_main_counter(hpet_info_t *hip)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint64_t *gcr_ptr;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint64_t gcr;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe gcr_ptr = (uint64_t *)HPET_GEN_CONFIG_ADDRESS(hip->logical_address);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe gcr = *gcr_ptr;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe gcr |= HPET_GCFR_ENABLE_CNF;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *gcr_ptr = gcr;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe gcr = *gcr_ptr;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (gcr & HPET_GCFR_ENABLE_CNF ? AE_OK : ~AE_OK);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic int
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_stop_main_counter(hpet_info_t *hip)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint64_t *gcr_ptr;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint64_t gcr;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe gcr_ptr = (uint64_t *)HPET_GEN_CONFIG_ADDRESS(hip->logical_address);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe gcr = *gcr_ptr;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe gcr &= ~HPET_GCFR_ENABLE_CNF;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *gcr_ptr = gcr;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe gcr = *gcr_ptr;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (gcr & HPET_GCFR_ENABLE_CNF ? ~AE_OK : AE_OK);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Set the Legacy Replacement Route bit.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * This should be called before setting up timers.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * The HPET specification is silent regarding setting this after timers are
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * programmed.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic uint64_t
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_set_leg_rt_cnf(hpet_info_t *hip, uint32_t new_value)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint64_t gen_conf = hpet_read_gen_config(hip);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe switch (new_value) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe case 0:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe gen_conf &= ~HPET_GCFR_LEG_RT_CNF;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe break;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe case HPET_GCFR_LEG_RT_CNF:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe gen_conf |= HPET_GCFR_LEG_RT_CNF;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe break;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe default:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ASSERT(new_value == 0 || new_value == HPET_GCFR_LEG_RT_CNF);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe break;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_write_gen_config(hip, gen_conf);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (gen_conf);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic uint64_t
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_read_gen_cap(hpet_info_t *hip)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (*(uint64_t *)HPET_GEN_CAP_ADDRESS(hip->logical_address));
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic uint64_t
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_read_gen_config(hpet_info_t *hip)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (*(uint64_t *)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe HPET_GEN_CONFIG_ADDRESS(hip->logical_address));
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic uint64_t
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_read_gen_intrpt_stat(hpet_info_t *hip)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hip->gen_intrpt_stat = *(uint64_t *)HPET_GEN_INTR_STAT_ADDRESS(
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hip->logical_address);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (hip->gen_intrpt_stat);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic uint64_t
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_read_timer_N_config(hpet_info_t *hip, uint_t n)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint64_t conf = *(uint64_t *)HPET_TIMER_N_CONF_ADDRESS(
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hip->logical_address, n);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hip->timer_n_config[n] = hpet_convert_timer_N_config(conf);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (conf);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic hpet_TN_conf_cap_t
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_convert_timer_N_config(uint64_t conf)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_TN_conf_cap_t cc = { 0 };
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cc.int_route_cap = HPET_TIMER_N_INT_ROUTE_CAP(conf);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cc.fsb_int_del_cap = HPET_TIMER_N_FSB_INT_DEL_CAP(conf);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cc.fsb_int_en_cnf = HPET_TIMER_N_FSB_EN_CNF(conf);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cc.int_route_cnf = HPET_TIMER_N_INT_ROUTE_CNF(conf);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cc.mode32_cnf = HPET_TIMER_N_MODE32_CNF(conf);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cc.val_set_cnf = HPET_TIMER_N_VAL_SET_CNF(conf);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cc.size_cap = HPET_TIMER_N_SIZE_CAP(conf);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cc.per_int_cap = HPET_TIMER_N_PER_INT_CAP(conf);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cc.type_cnf = HPET_TIMER_N_TYPE_CNF(conf);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cc.int_enb_cnf = HPET_TIMER_N_INT_ENB_CNF(conf);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cc.int_type_cnf = HPET_TIMER_N_INT_TYPE_CNF(conf);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (cc);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic uint64_t
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_read_main_counter_value(hpet_info_t *hip)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint64_t value;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint32_t *counter;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint32_t high1, high2, low;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe counter = (uint32_t *)HPET_MAIN_COUNTER_ADDRESS(hip->logical_address);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * 32-bit main counters
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hip->gen_cap.count_size_cap == 0) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe value = (uint64_t)*counter;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hip->main_counter_value = value;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (value);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * HPET spec claims a 64-bit read can be split into two 32-bit reads
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * by the hardware connection to the HPET.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe high2 = counter[1];
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe do {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe high1 = high2;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe low = counter[0];
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe high2 = counter[1];
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe } while (high2 != high1);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe value = ((uint64_t)high1 << 32) | low;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hip->main_counter_value = value;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (value);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic void
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_write_gen_config(hpet_info_t *hip, uint64_t l)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *(uint64_t *)HPET_GEN_CONFIG_ADDRESS(hip->logical_address) = l;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic void
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_write_gen_intrpt_stat(hpet_info_t *hip, uint64_t l)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *(uint64_t *)HPET_GEN_INTR_STAT_ADDRESS(hip->logical_address) = l;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic void
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_write_timer_N_config(hpet_info_t *hip, uint_t n, uint64_t l)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hip->timer_n_config[n].size_cap == 1)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *(uint64_t *)HPET_TIMER_N_CONF_ADDRESS(
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hip->logical_address, n) = l;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe else
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *(uint32_t *)HPET_TIMER_N_CONF_ADDRESS(
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hip->logical_address, n) = (uint32_t)(0xFFFFFFFF & l);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic void
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_write_timer_N_comp(hpet_info_t *hip, uint_t n, uint64_t l)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *(uint64_t *)HPET_TIMER_N_COMP_ADDRESS(hip->logical_address, n) = l;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic void
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_disable_timer(hpet_info_t *hip, uint32_t timer_n)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint64_t l;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe l = hpet_read_timer_N_config(hip, timer_n);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe l &= ~HPET_TIMER_N_INT_ENB_CNF_BIT;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_write_timer_N_config(hip, timer_n, l);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic void
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_enable_timer(hpet_info_t *hip, uint32_t timer_n)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint64_t l;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe l = hpet_read_timer_N_config(hip, timer_n);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe l |= HPET_TIMER_N_INT_ENB_CNF_BIT;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_write_timer_N_config(hip, timer_n, l);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Add the interrupt handler for I/O APIC interrupt number (interrupt line).
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * The I/O APIC line (vector) is programmed in ioapic_init_intr() called
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * from apic_picinit() psm_ops apic_ops entry point after we return from
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * apic_init() psm_ops entry point.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic uint32_t
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_install_interrupt_handler(uint_t (*func)(char *), int vector)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint32_t retval;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe retval = add_avintr(NULL, CBE_HIGH_PIL, (avfunc)func, "HPET Timer",
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe vector, NULL, NULL, NULL, NULL);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (retval == 0) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_WARN, "!hpet_acpi: add_avintr() failed");
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (AE_BAD_PARAMETER);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (AE_OK);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * The HPET timers specify which I/O APIC interrupts they can be routed to.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Find the first available non-legacy-replacement timer and its I/O APIC irq.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Supported I/O APIC IRQs are specified in the int_route_cap bitmap in each
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * timer's timer_n_config register.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic int
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_get_IOAPIC_intr_capable_timer(hpet_info_t *hip)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe int timer;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe int intr;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe for (timer = HPET_FIRST_NON_LEGACY_TIMER;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe timer < hip->gen_cap.num_tim_cap; ++timer) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (!hpet_timer_available(hip->allocated_timers, timer))
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe continue;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe intr = lowbit(hip->timer_n_config[timer].int_route_cap) - 1;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (intr >= 0) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_timer_alloc(&hip->allocated_timers, timer);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hip->cstate_timer.timer = timer;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hip->cstate_timer.intr = intr;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (timer);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (-1);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Mark this timer as used.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic void
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_timer_alloc(uint32_t *allocated_timers, uint32_t n)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *allocated_timers |= 1 << n;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Check if this timer is available.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * No mutual exclusion because only one thread uses this.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic int
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_timer_available(uint32_t allocated_timers, uint32_t n)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return ((allocated_timers & (1 << n)) == 0);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Setup timer N to route its interrupt to I/O APIC.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic void
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_timer_set_up(hpet_info_t *hip, uint32_t timer_n, uint32_t interrupt)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint64_t conf;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe conf = hpet_read_timer_N_config(hip, timer_n);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Caller is required to verify this interrupt route is supported.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ASSERT(HPET_TIMER_N_INT_ROUTE_CAP(conf) & (1 << interrupt));
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe conf &= ~HPET_TIMER_N_FSB_EN_CNF_BIT; /* use IOAPIC */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe conf |= HPET_TIMER_N_INT_ROUTE_SHIFT(interrupt);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe conf &= ~HPET_TIMER_N_TYPE_CNF_BIT; /* non periodic */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe conf &= ~HPET_TIMER_N_INT_ENB_CNF_BIT; /* disabled */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe conf |= HPET_TIMER_N_INT_TYPE_CNF_BIT; /* Level Triggered */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_write_timer_N_config(hip, timer_n, conf);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * The HPET's Main Counter is not stopped before programming an HPET timer.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * This will allow the HPET to be used as a time source.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * The programmed timer interrupt may occur before this function returns.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Callers must block interrupts before calling this function if they must
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * guarantee the interrupt is handled after this function returns.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Return 0 if main counter is less than timer after enabling timer.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * The interrupt was programmed, but it may fire before this returns.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Return !0 if main counter is greater than timer after enabling timer.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * In other words: the timer will not fire, and we do not know if it did fire.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * delta is in HPET ticks.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Writing a 64-bit value to a 32-bit register will "wrap around".
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * A 32-bit HPET timer will wrap around in a little over 5 minutes.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxeint
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_timer_program(hpet_info_t *hip, uint32_t timer, uint64_t delta)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint64_t time, program;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe program = hpet_read_main_counter_value(hip);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe program += delta;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_write_timer_N_comp(hip, timer, program);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe time = hpet_read_main_counter_value(hip);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (time < program)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (AE_OK);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (AE_TIME);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * CPR and power policy-change callback entry point.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxeboolean_t
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_callback(int code)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe switch (code) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe case PM_DEFAULT_CPU_DEEP_IDLE:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*FALLTHROUGH*/
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe case PM_ENABLE_CPU_DEEP_IDLE:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*FALLTHROUGH*/
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe case PM_DISABLE_CPU_DEEP_IDLE:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (hpet_deep_idle_config(code));
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe case CB_CODE_CPR_RESUME:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*FALLTHROUGH*/
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe case CB_CODE_CPR_CHKPT:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (hpet_cpr(code));
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe case CST_EVENT_MULTIPLE_CSTATES:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_cst_callback(CST_EVENT_MULTIPLE_CSTATES);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (B_TRUE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe case CST_EVENT_ONE_CSTATE:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_cst_callback(CST_EVENT_ONE_CSTATE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (B_TRUE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe default:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_NOTE, "!hpet_callback: invalid code %d\n", code);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (B_FALSE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * According to the HPET spec 1.0a: the Operating System must save and restore
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * HPET event timer hardware context through ACPI sleep state transitions.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Timer registers (including the main counter) may not be preserved through
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * ACPI S3, S4, or S5 sleep states. This code does not not support S1 nor S2.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Current HPET state is already in hpet.supported and
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * hpet_state.proxy_installed. hpet_info contains the proxy interrupt HPET
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Timer state.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Future projects beware: the HPET Main Counter is undefined after ACPI S3 or
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * S4, and it is not saved/restored here. Future projects cannot expect the
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Main Counter to be monotomically (or accurately) increasing across CPR.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Note: the CPR Checkpoint path later calls pause_cpus() which ensures all
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * CPUs are awake and in a spin loop before the system suspends. The HPET is
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * not needed for Deep C-state wakeup when CPUs are in cpu_pause().
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * It is safe to leave the HPET running as the system suspends; we just
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * disable the timer from generating interrupts here.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic boolean_t
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_cpr(int code)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ulong_t intr, dead_count = 0;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hrtime_t dead = gethrtime() + hpet_spin_timeout;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe boolean_t ret = B_TRUE;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe mutex_enter(&hpet_state_lock);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe switch (code) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe case CB_CODE_CPR_CHKPT:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet_state.proxy_installed == B_FALSE)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe break;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_state.cpr = B_TRUE;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe intr = intr_clear();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe while (!mutex_tryenter(&hpet_proxy_lock)) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * spin
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe intr_restore(intr);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (dead_count++ > hpet_spin_check) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe dead_count = 0;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (gethrtime() > dead) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_state.cpr = B_FALSE;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe mutex_exit(&hpet_state_lock);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_NOTE, "!hpet_cpr: deadman");
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (B_FALSE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe intr = intr_clear();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_expire_all();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe mutex_exit(&hpet_proxy_lock);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe intr_restore(intr);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_disable_timer(&hpet_info, hpet_info.cstate_timer.timer);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe break;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe case CB_CODE_CPR_RESUME:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet_resume() == B_TRUE)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_state.cpr = B_FALSE;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe else
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_NOTE, "!hpet_resume failed.");
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe break;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe default:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_NOTE, "!hpet_cpr: invalid code %d\n", code);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ret = B_FALSE;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe break;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe mutex_exit(&hpet_state_lock);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (ret);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Assume the HPET stopped in Suspend state and timer state was lost.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic boolean_t
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_resume(void)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet.supported != HPET_TIMER_SUPPORT)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (B_TRUE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * The HPET spec does not specify if Legacy Replacement Route is
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * on or off by default, so we set it off here.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe (void) hpet_set_leg_rt_cnf(&hpet_info, 0);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet_start_main_counter(&hpet_info) != AE_OK) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_NOTE, "!hpet_resume: start main counter failed");
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet.supported = HPET_NO_SUPPORT;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet_state.proxy_installed == B_TRUE) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_state.proxy_installed = B_FALSE;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_uninstall_interrupt_handler();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (B_FALSE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet_state.proxy_installed == B_FALSE)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (B_TRUE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_timer_set_up(&hpet_info, hpet_info.cstate_timer.timer,
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_info.cstate_timer.intr);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet_state.cpu_deep_idle == B_TRUE)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_enable_timer(&hpet_info, hpet_info.cstate_timer.timer);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (B_TRUE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Callback to enable/disable Deep C-States based on power.conf setting.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic boolean_t
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_deep_idle_config(int code)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ulong_t intr, dead_count = 0;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hrtime_t dead = gethrtime() + hpet_spin_timeout;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe boolean_t ret = B_TRUE;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe mutex_enter(&hpet_state_lock);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe switch (code) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe case PM_DEFAULT_CPU_DEEP_IDLE:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*FALLTHROUGH*/
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe case PM_ENABLE_CPU_DEEP_IDLE:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet_state.cpu_deep_idle == B_TRUE)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe break;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet_state.proxy_installed == B_FALSE) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ret = B_FALSE; /* Deep C-States not supported */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe break;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_enable_timer(&hpet_info, hpet_info.cstate_timer.timer);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_state.cpu_deep_idle = B_TRUE;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe break;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe case PM_DISABLE_CPU_DEEP_IDLE:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if ((hpet_state.cpu_deep_idle == B_FALSE) ||
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe (hpet_state.proxy_installed == B_FALSE))
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe break;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * The order of these operations is important to avoid
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * lost wakeups: Set a flag to refuse all future LAPIC Timer
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * proxy requests, then wake up all CPUs from deep C-state,
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * and finally disable the HPET interrupt-generating timer.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_state.cpu_deep_idle = B_FALSE;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe intr = intr_clear();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe while (!mutex_tryenter(&hpet_proxy_lock)) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * spin
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe intr_restore(intr);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (dead_count++ > hpet_spin_check) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe dead_count = 0;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (gethrtime() > dead) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_state.cpu_deep_idle = B_TRUE;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe mutex_exit(&hpet_state_lock);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_NOTE,
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe "!hpet_deep_idle_config: deadman");
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (B_FALSE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe intr = intr_clear();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_expire_all();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe mutex_exit(&hpet_proxy_lock);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe intr_restore(intr);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_disable_timer(&hpet_info, hpet_info.cstate_timer.timer);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe break;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe default:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_NOTE, "!hpet_deep_idle_config: invalid code %d\n",
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe code);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ret = B_FALSE;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe break;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe mutex_exit(&hpet_state_lock);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (ret);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Callback for _CST c-state change notifications.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic void
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_cst_callback(uint32_t code)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ulong_t intr, dead_count = 0;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hrtime_t dead = gethrtime() + hpet_spin_timeout;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe switch (code) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe case CST_EVENT_ONE_CSTATE:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_state.uni_cstate = B_TRUE;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe intr = intr_clear();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe while (!mutex_tryenter(&hpet_proxy_lock)) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * spin
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe intr_restore(intr);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (dead_count++ > hpet_spin_check) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe dead_count = 0;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (gethrtime() > dead) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_expire_all();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_NOTE,
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe "!hpet_cst_callback: deadman");
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe intr = intr_clear();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_expire_all();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe mutex_exit(&hpet_proxy_lock);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe intr_restore(intr);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe break;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe case CST_EVENT_MULTIPLE_CSTATES:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_state.uni_cstate = B_FALSE;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe break;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe default:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cmn_err(CE_NOTE, "!hpet_cst_callback: invalid code %d\n", code);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe break;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Interrupt Service Routine for HPET I/O-APIC-generated interrupts.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Used to wakeup CPUs from Deep C-state when their Local APIC Timer stops.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * This ISR runs on one CPU which pokes other CPUs out of Deep C-state as
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * needed.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/* ARGSUSED */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic uint_t
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_isr(char *arg)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint64_t timer_status;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint64_t timer_mask;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ulong_t intr, dead_count = 0;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hrtime_t dead = gethrtime() + hpet_isr_spin_timeout;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe timer_mask = HPET_INTR_STATUS_MASK(hpet_info.cstate_timer.timer);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * We are using a level-triggered interrupt.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * HPET sets timer's General Interrupt Status Register bit N.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * ISR checks this bit to see if it needs servicing.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * ISR then clears this bit by writing 1 to that bit.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe timer_status = hpet_read_gen_intrpt_stat(&hpet_info);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (!(timer_status & timer_mask))
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (DDI_INTR_UNCLAIMED);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_write_gen_intrpt_stat(&hpet_info, timer_mask);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Do not touch ISR data structures before checking the HPET's General
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Interrupt Status register. The General Interrupt Status register
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * will not be set by hardware until after timer interrupt generation
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * is enabled by software. Software allocates necessary data
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * structures before enabling timer interrupts. ASSERT the software
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * data structures required to handle this interrupt are initialized.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ASSERT(hpet_proxy_users != NULL);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * CPUs in deep c-states do not enable interrupts until after
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * performing idle cleanup which includes descheduling themselves from
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * the HPET. The CPU running this ISR will NEVER find itself in the
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * proxy list. A lost wakeup may occur if this is false.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ASSERT(hpet_proxy_users[CPU->cpu_id] == HPET_INFINITY);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Higher level interrupts may deadlock with CPUs going idle if this
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * ISR is prempted while holding hpet_proxy_lock.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe intr = intr_clear();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe while (!mutex_tryenter(&hpet_proxy_lock)) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * spin
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe intr_restore(intr);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (dead_count++ > hpet_spin_check) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe dead_count = 0;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (gethrtime() > dead) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_expire_all();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (DDI_INTR_CLAIMED);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe intr = intr_clear();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe (void) hpet_guaranteed_schedule(HPET_INFINITY);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe mutex_exit(&hpet_proxy_lock);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe intr_restore(intr);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (DDI_INTR_CLAIMED);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Used when disabling the HPET Timer interrupt. CPUs in Deep C-state must be
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * woken up because they can no longer rely on the HPET's Timer to wake them.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * We do not need to wait for CPUs to wakeup.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic void
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_expire_all(void)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe processorid_t id;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
22bbd95e3be77066d326fc29c95eb88028ee9d2baubrey.li@intel.com for (id = 0; id < max_ncpus; ++id) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet_proxy_users[id] != HPET_INFINITY) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_proxy_users[id] = HPET_INFINITY;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (id != CPU->cpu_id)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe poke_cpu(id);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * To avoid missed wakeups this function must guarantee either the HPET timer
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * was successfully programmed to the next expire time or there are no waiting
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * CPUs.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Callers cannot enter C2 or deeper if the HPET could not be programmed to
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * generate its next interrupt to happen at required_wakeup_time or sooner.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Returns B_TRUE if the HPET was programmed to interrupt by
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * required_wakeup_time, B_FALSE if not.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic boolean_t
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_guaranteed_schedule(hrtime_t required_wakeup_time)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hrtime_t now, next_proxy_time;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe processorid_t id, next_proxy_id;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe int proxy_timer = hpet_info.cstate_timer.timer;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe boolean_t done = B_FALSE;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ASSERT(mutex_owned(&hpet_proxy_lock));
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Loop until we successfully program the HPET,
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * or no CPUs are scheduled to use the HPET as a proxy.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe do {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Wake all CPUs that expired before now.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Find the next CPU to wake up and next HPET program time.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe now = gethrtime();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe next_proxy_time = HPET_INFINITY;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe next_proxy_id = CPU->cpu_id;
22bbd95e3be77066d326fc29c95eb88028ee9d2baubrey.li@intel.com for (id = 0; id < max_ncpus; ++id) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet_proxy_users[id] < now) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_proxy_users[id] = HPET_INFINITY;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (id != CPU->cpu_id)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe poke_cpu(id);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe } else if (hpet_proxy_users[id] < next_proxy_time) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe next_proxy_time = hpet_proxy_users[id];
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe next_proxy_id = id;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (next_proxy_time == HPET_INFINITY) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe done = B_TRUE;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * There are currently no CPUs using the HPET's Timer
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * as a proxy for their LAPIC Timer. The HPET's Timer
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * does not need to be programmed.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Letting the HPET timer wrap around to the current
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * time is the longest possible timeout.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * A 64-bit timer will wrap around in ~ 2^44 seconds.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * A 32-bit timer will wrap around in ~ 2^12 seconds.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Disabling the HPET's timer interrupt requires a
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * (relatively expensive) write to the HPET.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Instead we do nothing.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * We are gambling some CPU will attempt to enter a
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * deep c-state before the timer wraps around.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * We assume one spurious interrupt in a little over an
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * hour has less performance impact than writing to the
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * HPET's timer disable bit every time all CPUs wakeup
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * from deep c-state.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe } else {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Idle CPUs disable interrupts before programming the
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * HPET to prevent a lost wakeup if the HPET
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * interrupts the idle cpu before it can enter a
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Deep C-State.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hpet_timer_program(&hpet_info, proxy_timer,
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe HRTIME_TO_HPET_TICKS(next_proxy_time - gethrtime()))
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe != AE_OK) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * We could not program the HPET to wakeup the
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * next CPU. We must wake the CPU ourself to
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * avoid a lost wakeup.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_proxy_users[next_proxy_id] = HPET_INFINITY;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (next_proxy_id != CPU->cpu_id)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe poke_cpu(next_proxy_id);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe } else {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe done = B_TRUE;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe } while (!done);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (next_proxy_time <= required_wakeup_time);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Use an HPET timer to act as this CPU's proxy local APIC timer.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Used in deep c-states C2 and above while the CPU's local APIC timer stalls.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Called by the idle thread with interrupts enabled.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Always returns with interrupts disabled.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * There are 3 possible outcomes from this function:
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * 1. The Local APIC Timer was already disabled before this function was called.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * LAPIC TIMER : disabled
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * HPET : not scheduled to wake this CPU
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * *lapic_expire : (hrtime_t)HPET_INFINITY
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Returns : B_TRUE
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * 2. Successfully programmed the HPET to act as a LAPIC Timer proxy.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * LAPIC TIMER : disabled
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * HPET : scheduled to wake this CPU
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * *lapic_expire : hrtime_t when LAPIC timer would have expired
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Returns : B_TRUE
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * 3. Failed to programmed the HPET to act as a LAPIC Timer proxy.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * LAPIC TIMER : enabled
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * HPET : not scheduled to wake this CPU
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * *lapic_expire : (hrtime_t)HPET_INFINITY
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Returns : B_FALSE
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * The idle thread cannot enter Deep C-State in case 3.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * The idle thread must re-enable & re-program the LAPIC_TIMER in case 2.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic boolean_t
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_use_hpet_timer(hrtime_t *lapic_expire)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe extern hrtime_t apic_timer_stop_count(void);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe extern void apic_timer_restart(hrtime_t);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hrtime_t now, expire, dead;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe uint64_t lapic_count, dead_count;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cpupart_t *cpu_part;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe processorid_t cpu_sid;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe processorid_t cpu_id = CPU->cpu_id;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe processorid_t id;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe boolean_t rslt;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe boolean_t hset_update;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cpu_part = CPU->cpu_part;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cpu_sid = CPU->cpu_seqid;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ASSERT(CPU->cpu_thread == CPU->cpu_idle_thread);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * A critical section exists between when the HPET is programmed
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * to interrupt the CPU and when this CPU enters an idle state.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Interrupts must be blocked during that time to prevent lost
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * CBE wakeup interrupts from either LAPIC or HPET.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Must block interrupts before acquiring hpet_proxy_lock to prevent
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * a deadlock with the ISR if the ISR runs on this CPU after the
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * idle thread acquires the mutex but before it clears interrupts.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
cef70d2c52b5ed31a487790e4584f648812210a9Bill Holler ASSERT(!interrupts_enabled());
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe lapic_count = apic_timer_stop_count();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe now = gethrtime();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe dead = now + hpet_idle_spin_timeout;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *lapic_expire = expire = now + lapic_count;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (lapic_count == (hrtime_t)-1) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * LAPIC timer is currently disabled.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Will not use the HPET as a LAPIC Timer proxy.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *lapic_expire = (hrtime_t)HPET_INFINITY;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (B_TRUE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Serialize hpet_proxy data structure manipulation.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe dead_count = 0;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe while (!mutex_tryenter(&hpet_proxy_lock)) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * spin
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe apic_timer_restart(expire);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe sti();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe cli();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (dead_count++ > hpet_spin_check) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe dead_count = 0;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hset_update = (((CPU->cpu_flags & CPU_OFFLINE) == 0) &&
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe (ncpus > 1));
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (hset_update &&
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe !bitset_in_set(&cpu_part->cp_haltset, cpu_sid)) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *lapic_expire = (hrtime_t)HPET_INFINITY;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (B_FALSE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe lapic_count = apic_timer_stop_count();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe now = gethrtime();
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *lapic_expire = expire = now + lapic_count;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (lapic_count == (hrtime_t)-1) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * LAPIC timer is currently disabled.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Will not use the HPET as a LAPIC Timer proxy.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *lapic_expire = (hrtime_t)HPET_INFINITY;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (B_TRUE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (now > dead) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe apic_timer_restart(expire);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *lapic_expire = (hrtime_t)HPET_INFINITY;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (B_FALSE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if ((hpet_state.cpr == B_TRUE) ||
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe (hpet_state.cpu_deep_idle == B_FALSE) ||
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe (hpet_state.proxy_installed == B_FALSE) ||
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe (hpet_state.uni_cstate == B_TRUE)) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe mutex_exit(&hpet_proxy_lock);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe apic_timer_restart(expire);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *lapic_expire = (hrtime_t)HPET_INFINITY;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (B_FALSE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_proxy_users[cpu_id] = expire;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * We are done if another cpu is scheduled on the HPET with an
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * expire time before us. The next HPET interrupt has been programmed
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * to fire before our expire time.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
22bbd95e3be77066d326fc29c95eb88028ee9d2baubrey.li@intel.com for (id = 0; id < max_ncpus; ++id) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if ((hpet_proxy_users[id] <= expire) && (id != cpu_id)) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe mutex_exit(&hpet_proxy_lock);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (B_TRUE);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * We are the next lAPIC to expire.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Program the HPET with our expire time.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe rslt = hpet_guaranteed_schedule(expire);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe mutex_exit(&hpet_proxy_lock);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (rslt == B_FALSE) {
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe apic_timer_restart(expire);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *lapic_expire = (hrtime_t)HPET_INFINITY;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe }
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe return (rslt);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Called by the idle thread when waking up from Deep C-state before enabling
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * interrupts. With an array data structure it is faster to always remove
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * ourself from the array without checking if the HPET ISR already removed.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe *
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * We use a lazy algorithm for removing CPUs from the HPET's schedule.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * We do not reprogram the HPET here because this CPU has real work to do.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * On a idle system the CPU was probably woken up by the HPET's ISR.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * On a heavily loaded system CPUs are not going into Deep C-state.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * On a moderately loaded system another CPU will usually enter Deep C-state
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * and reprogram the HPET before the HPET fires with our wakeup.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic void
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_use_lapic_timer(hrtime_t expire)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe extern void apic_timer_restart(hrtime_t);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe processorid_t cpu_id = CPU->cpu_id;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ASSERT(CPU->cpu_thread == CPU->cpu_idle_thread);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe ASSERT(!interrupts_enabled());
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_proxy_users[cpu_id] = HPET_INFINITY;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Do not enable a LAPIC Timer that was initially disabled.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe if (expire != HPET_INFINITY)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe apic_timer_restart(expire);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe/*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Initialize data structure to keep track of CPUs using HPET as a proxy for
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * their stalled local APIC timer. For now this is just an array.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxestatic void
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxehpet_init_proxy_data(void)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe{
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe processorid_t id;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
22bbd95e3be77066d326fc29c95eb88028ee9d2baubrey.li@intel.com * Use max_ncpus for hot plug compliance.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
22bbd95e3be77066d326fc29c95eb88028ee9d2baubrey.li@intel.com hpet_proxy_users = kmem_zalloc(max_ncpus * sizeof (*hpet_proxy_users),
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe KM_SLEEP);
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe /*
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe * Unused entries always contain HPET_INFINITY.
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe */
22bbd95e3be77066d326fc29c95eb88028ee9d2baubrey.li@intel.com for (id = 0; id < max_ncpus; ++id)
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe hpet_proxy_users[id] = HPET_INFINITY;
0e7515250c8395f368aa45fb9acae7c4f8f8b786Eric Saxe}