1eca2601cSRandy Fishel /* 2eca2601cSRandy Fishel * CDDL HEADER START 3eca2601cSRandy Fishel * 4eca2601cSRandy Fishel * The contents of this file are subject to the terms of the 5eca2601cSRandy Fishel * Common Development and Distribution License (the "License"). 6eca2601cSRandy Fishel * You may not use this file except in compliance with the License. 7eca2601cSRandy Fishel * 8eca2601cSRandy Fishel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9eca2601cSRandy Fishel * or http://www.opensolaris.org/os/licensing. 10eca2601cSRandy Fishel * See the License for the specific language governing permissions 11eca2601cSRandy Fishel * and limitations under the License. 12eca2601cSRandy Fishel * 13eca2601cSRandy Fishel * When distributing Covered Code, include this CDDL HEADER in each 14eca2601cSRandy Fishel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15eca2601cSRandy Fishel * If applicable, add the following below this CDDL HEADER, with the 16eca2601cSRandy Fishel * fields enclosed by brackets "[]" replaced with your own identifying 17eca2601cSRandy Fishel * information: Portions Copyright [yyyy] [name of copyright owner] 18eca2601cSRandy Fishel * 19eca2601cSRandy Fishel * CDDL HEADER END 20eca2601cSRandy Fishel */ 21eca2601cSRandy Fishel /* 22eca2601cSRandy Fishel * Copyright (c) 2009, Intel Corporation. 23eca2601cSRandy Fishel * All rights reserved. 24eca2601cSRandy Fishel */ 25eca2601cSRandy Fishel 26eca2601cSRandy Fishel #include <sys/atomic.h> 27eca2601cSRandy Fishel #include <sys/cpuvar.h> 28eca2601cSRandy Fishel #include <sys/cpu.h> 29eca2601cSRandy Fishel #include <sys/cpu_event.h> 30eca2601cSRandy Fishel #include <sys/cmn_err.h> 31eca2601cSRandy Fishel #include <sys/ddi.h> 32eca2601cSRandy Fishel #include <sys/kmem.h> 33eca2601cSRandy Fishel #include <sys/kstat.h> 34eca2601cSRandy Fishel #include <sys/pci.h> 35eca2601cSRandy Fishel #include <sys/sunddi.h> 36eca2601cSRandy Fishel #include <sys/sunndi.h> 37eca2601cSRandy Fishel #include <sys/synch.h> 38eca2601cSRandy Fishel #include <sys/sysmacros.h> 39eca2601cSRandy Fishel #include <sys/fipe.h> 40eca2601cSRandy Fishel #include <vm/hat.h> 41eca2601cSRandy Fishel 42eca2601cSRandy Fishel /* Current PM policy, configurable through /etc/system and fipe.conf. */ 43eca2601cSRandy Fishel fipe_pm_policy_t fipe_pm_policy = FIPE_PM_POLICY_BALANCE; 44eca2601cSRandy Fishel int fipe_pm_throttle_level = 1; 45eca2601cSRandy Fishel 46eca2601cSRandy Fishel /* Enable kstat support. */ 47eca2601cSRandy Fishel #define FIPE_KSTAT_SUPPORT 1 48eca2601cSRandy Fishel 49eca2601cSRandy Fishel /* Enable performance relative statistics. */ 50eca2601cSRandy Fishel #define FIPE_KSTAT_DETAIL 1 51eca2601cSRandy Fishel 52eca2601cSRandy Fishel /* Enable builtin IOAT driver if no IOAT driver is available. */ 53eca2601cSRandy Fishel #define FIPE_IOAT_BUILTIN 0 54eca2601cSRandy Fishel #if defined(FIPE_IOAT_BUILTIN) && (FIPE_IOAT_BUILTIN == 0) 55eca2601cSRandy Fishel #undef FIPE_IOAT_BUILTIN 56eca2601cSRandy Fishel #endif 57eca2601cSRandy Fishel 58eca2601cSRandy Fishel #ifdef FIPE_IOAT_BUILTIN 59eca2601cSRandy Fishel /* Use IOAT channel 3 to generate memory transactions. */ 60eca2601cSRandy Fishel #define FIPE_IOAT_CHAN_CTRL 0x200 61eca2601cSRandy Fishel #define FIPE_IOAT_CHAN_STS_LO 0x204 62eca2601cSRandy Fishel #define FIPE_IOAT_CHAN_STS_HI 0x208 63eca2601cSRandy Fishel #define FIPE_IOAT_CHAN_ADDR_LO 0x20C 64eca2601cSRandy Fishel #define FIPE_IOAT_CHAN_ADDR_HI 0x210 65eca2601cSRandy Fishel #define FIPE_IOAT_CHAN_CMD 0x214 66eca2601cSRandy Fishel #define FIPE_IOAT_CHAN_ERR 0x228 67eca2601cSRandy Fishel #else /* FIPE_IOAT_BUILTIN */ 68eca2601cSRandy Fishel #include <sys/dcopy.h> 69eca2601cSRandy Fishel #endif /* FIPE_IOAT_BUILTIN */ 70eca2601cSRandy Fishel 71eca2601cSRandy Fishel /* Memory controller relative PCI configuration constants. */ 72eca2601cSRandy Fishel #define FIPE_MC_GBLACT 0x60 73eca2601cSRandy Fishel #define FIPE_MC_THRTLOW 0x64 74eca2601cSRandy Fishel #define FIPE_MC_THRTCTRL 0x67 75eca2601cSRandy Fishel #define FIPE_MC_THRTCTRL_HUNT 0x1 76eca2601cSRandy Fishel 77eca2601cSRandy Fishel /* Hardware recommended values. */ 78eca2601cSRandy Fishel #define FIPE_MC_MEMORY_OFFSET 1024 79eca2601cSRandy Fishel #define FIPE_MC_MEMORY_SIZE 128 80eca2601cSRandy Fishel 81eca2601cSRandy Fishel /* Number of IOAT commands posted when entering idle. */ 82eca2601cSRandy Fishel #define FIPE_IOAT_CMD_NUM 2 83eca2601cSRandy Fishel 84eca2601cSRandy Fishel /* Resource allocation retry interval in microsecond. */ 85eca2601cSRandy Fishel #define FIPE_IOAT_RETRY_INTERVAL (15 * 1000 * 1000) 86eca2601cSRandy Fishel 87eca2601cSRandy Fishel /* Statistics update interval in nanosecond. */ 88eca2601cSRandy Fishel #define FIPE_STAT_INTERVAL (10 * 1000 * 1000) 89eca2601cSRandy Fishel 90eca2601cSRandy Fishel /* Configuration profile support. */ 91eca2601cSRandy Fishel #define FIPE_PROFILE_FIELD(field) (fipe_profile_curr->field) 92eca2601cSRandy Fishel #define FIPE_PROF_IDLE_COUNT FIPE_PROFILE_FIELD(idle_count) 93eca2601cSRandy Fishel #define FIPE_PROF_BUSY_THRESHOLD FIPE_PROFILE_FIELD(busy_threshold) 94eca2601cSRandy Fishel #define FIPE_PROF_INTR_THRESHOLD FIPE_PROFILE_FIELD(intr_threshold) 95eca2601cSRandy Fishel #define FIPE_PROF_INTR_BUSY_THRESHOLD FIPE_PROFILE_FIELD(intr_busy_threshold) 96eca2601cSRandy Fishel #define FIPE_PROF_INTR_BUSY_THROTTLE FIPE_PROFILE_FIELD(intr_busy_throttle) 97eca2601cSRandy Fishel 98eca2601cSRandy Fishel /* Priority assigned to FIPE memory power management driver on x86. */ 99eca2601cSRandy Fishel #define CPU_IDLE_CB_PRIO_FIPE (CPU_IDLE_CB_PRIO_LOW_BASE + 0x4000000) 100eca2601cSRandy Fishel 101eca2601cSRandy Fishel /* Structure to support power management profile. */ 102*90b1de13SRichard PALO #pragma align CPU_CACHE_COHERENCE_SIZE(fipe_profiles) 103eca2601cSRandy Fishel static struct fipe_profile { 104eca2601cSRandy Fishel uint32_t idle_count; 105eca2601cSRandy Fishel uint32_t busy_threshold; 106eca2601cSRandy Fishel uint32_t intr_threshold; 107eca2601cSRandy Fishel uint32_t intr_busy_threshold; 108eca2601cSRandy Fishel uint32_t intr_busy_throttle; 109eca2601cSRandy Fishel } fipe_profiles[FIPE_PM_POLICY_MAX] = { 110eca2601cSRandy Fishel { 0, 0, 0, 0, 0 }, 111eca2601cSRandy Fishel { 5, 30, 20, 50, 5 }, 112eca2601cSRandy Fishel { 10, 40, 40, 75, 4 }, 113eca2601cSRandy Fishel { 15, 50, 60, 100, 2 }, 114eca2601cSRandy Fishel }; 115eca2601cSRandy Fishel 116eca2601cSRandy Fishel /* Structure to store memory controller relative data. */ 117*90b1de13SRichard PALO #pragma align CPU_CACHE_COHERENCE_SIZE(fipe_mc_ctrl) 118eca2601cSRandy Fishel static struct fipe_mc_ctrl { 119eca2601cSRandy Fishel ddi_acc_handle_t mc_pci_hdl; 120eca2601cSRandy Fishel unsigned char mc_thrtctrl; 121eca2601cSRandy Fishel unsigned char mc_thrtlow; 122eca2601cSRandy Fishel unsigned char mc_gblact; 123eca2601cSRandy Fishel dev_info_t *mc_dip; 124eca2601cSRandy Fishel boolean_t mc_initialized; 125eca2601cSRandy Fishel } fipe_mc_ctrl; 126eca2601cSRandy Fishel 127eca2601cSRandy Fishel /* Structure to store IOAT relative information. */ 128*90b1de13SRichard PALO #pragma align CPU_CACHE_COHERENCE_SIZE(fipe_ioat_ctrl) 129eca2601cSRandy Fishel static struct fipe_ioat_control { 130eca2601cSRandy Fishel kmutex_t ioat_lock; 131eca2601cSRandy Fishel boolean_t ioat_ready; 132eca2601cSRandy Fishel #ifdef FIPE_IOAT_BUILTIN 133eca2601cSRandy Fishel boolean_t ioat_reg_mapped; 134eca2601cSRandy Fishel ddi_acc_handle_t ioat_reg_handle; 135eca2601cSRandy Fishel uint8_t *ioat_reg_addr; 136eca2601cSRandy Fishel uint64_t ioat_cmd_physaddr; 137eca2601cSRandy Fishel #else /* FIPE_IOAT_BUILTIN */ 138eca2601cSRandy Fishel dcopy_cmd_t ioat_cmds[FIPE_IOAT_CMD_NUM + 1]; 139eca2601cSRandy Fishel dcopy_handle_t ioat_handle; 140eca2601cSRandy Fishel #endif /* FIPE_IOAT_BUILTIN */ 141eca2601cSRandy Fishel dev_info_t *ioat_dev_info; 142eca2601cSRandy Fishel uint64_t ioat_buf_physaddr; 143eca2601cSRandy Fishel char *ioat_buf_virtaddr; 144eca2601cSRandy Fishel char *ioat_buf_start; 145eca2601cSRandy Fishel size_t ioat_buf_size; 146eca2601cSRandy Fishel timeout_id_t ioat_timerid; 147eca2601cSRandy Fishel boolean_t ioat_failed; 148eca2601cSRandy Fishel boolean_t ioat_cancel; 149eca2601cSRandy Fishel boolean_t ioat_try_alloc; 150eca2601cSRandy Fishel } fipe_ioat_ctrl; 151eca2601cSRandy Fishel 152*90b1de13SRichard PALO #pragma align CPU_CACHE_COHERENCE_SIZE(fipe_idle_ctrl) 153eca2601cSRandy Fishel static struct fipe_idle_ctrl { 154eca2601cSRandy Fishel boolean_t idle_ready; 155eca2601cSRandy Fishel cpu_idle_callback_handle_t cb_handle; 156eca2601cSRandy Fishel cpu_idle_prop_handle_t prop_enter; 157eca2601cSRandy Fishel cpu_idle_prop_handle_t prop_exit; 158eca2601cSRandy Fishel cpu_idle_prop_handle_t prop_busy; 159eca2601cSRandy Fishel cpu_idle_prop_handle_t prop_idle; 160eca2601cSRandy Fishel cpu_idle_prop_handle_t prop_intr; 161eca2601cSRandy Fishel 162eca2601cSRandy Fishel /* Put here for cache efficiency, it should be in fipe_global_ctrl. */ 163eca2601cSRandy Fishel hrtime_t tick_interval; 164eca2601cSRandy Fishel } fipe_idle_ctrl; 165eca2601cSRandy Fishel 166eca2601cSRandy Fishel /* 167eca2601cSRandy Fishel * Global control structure. 168eca2601cSRandy Fishel * Solaris idle thread has no reentrance issue, so it's enough to count CPUs 169eca2601cSRandy Fishel * in idle state. Otherwise cpuset_t bitmap should be used to track idle CPUs. 170eca2601cSRandy Fishel */ 171*90b1de13SRichard PALO #pragma align CPU_CACHE_COHERENCE_SIZE(fipe_gbl_ctrl) 172eca2601cSRandy Fishel static struct fipe_global_ctrl { 173eca2601cSRandy Fishel kmutex_t lock; 174eca2601cSRandy Fishel boolean_t pm_enabled; 175eca2601cSRandy Fishel volatile boolean_t pm_active; 176eca2601cSRandy Fishel volatile uint32_t cpu_count; 177eca2601cSRandy Fishel volatile uint64_t io_waiters; 178eca2601cSRandy Fishel hrtime_t enter_ts; 179eca2601cSRandy Fishel hrtime_t time_in_pm; 180eca2601cSRandy Fishel size_t state_size; 181eca2601cSRandy Fishel char *state_buf; 182eca2601cSRandy Fishel #ifdef FIPE_KSTAT_SUPPORT 183eca2601cSRandy Fishel kstat_t *fipe_kstat; 184eca2601cSRandy Fishel #endif /* FIPE_KSTAT_SUPPORT */ 185eca2601cSRandy Fishel } fipe_gbl_ctrl; 186eca2601cSRandy Fishel 187eca2601cSRandy Fishel #define FIPE_CPU_STATE_PAD (128 - \ 188eca2601cSRandy Fishel 2 * sizeof (boolean_t) - 4 * sizeof (hrtime_t) - \ 189eca2601cSRandy Fishel 2 * sizeof (uint64_t) - 2 * sizeof (uint32_t)) 190eca2601cSRandy Fishel 191eca2601cSRandy Fishel /* Per-CPU status. */ 192eca2601cSRandy Fishel #pragma pack(1) 193eca2601cSRandy Fishel typedef struct fipe_cpu_state { 194eca2601cSRandy Fishel boolean_t cond_ready; 195eca2601cSRandy Fishel boolean_t state_ready; 196eca2601cSRandy Fishel uint32_t idle_count; 197eca2601cSRandy Fishel uint32_t throttle_cnt; 198eca2601cSRandy Fishel hrtime_t throttle_ts; 199eca2601cSRandy Fishel hrtime_t next_ts; 200eca2601cSRandy Fishel hrtime_t last_busy; 201eca2601cSRandy Fishel hrtime_t last_idle; 202eca2601cSRandy Fishel uint64_t last_intr; 203eca2601cSRandy Fishel uint64_t last_iowait; 204eca2601cSRandy Fishel char pad1[FIPE_CPU_STATE_PAD]; 205eca2601cSRandy Fishel } fipe_cpu_state_t; 206eca2601cSRandy Fishel #pragma pack() 207eca2601cSRandy Fishel 208eca2601cSRandy Fishel #ifdef FIPE_KSTAT_SUPPORT 209*90b1de13SRichard PALO #pragma align CPU_CACHE_COHERENCE_SIZE(fipe_kstat) 210eca2601cSRandy Fishel static struct fipe_kstat_s { 211eca2601cSRandy Fishel kstat_named_t fipe_enabled; 212eca2601cSRandy Fishel kstat_named_t fipe_policy; 213eca2601cSRandy Fishel kstat_named_t fipe_pm_time; 214eca2601cSRandy Fishel #ifdef FIPE_KSTAT_DETAIL 215eca2601cSRandy Fishel kstat_named_t ioat_ready; 216eca2601cSRandy Fishel kstat_named_t pm_tryenter_cnt; 217eca2601cSRandy Fishel kstat_named_t pm_success_cnt; 218eca2601cSRandy Fishel kstat_named_t pm_race_cnt; 219eca2601cSRandy Fishel kstat_named_t cpu_loop_cnt; 220eca2601cSRandy Fishel kstat_named_t cpu_busy_cnt; 221eca2601cSRandy Fishel kstat_named_t cpu_idle_cnt; 222eca2601cSRandy Fishel kstat_named_t cpu_intr_busy_cnt; 223eca2601cSRandy Fishel kstat_named_t cpu_intr_throttle_cnt; 224eca2601cSRandy Fishel kstat_named_t bio_busy_cnt; 225eca2601cSRandy Fishel kstat_named_t ioat_start_fail_cnt; 226eca2601cSRandy Fishel kstat_named_t ioat_stop_fail_cnt; 227eca2601cSRandy Fishel #endif /* FIPE_KSTAT_DETAIL */ 228eca2601cSRandy Fishel } fipe_kstat = { 229eca2601cSRandy Fishel { "fipe_enabled", KSTAT_DATA_INT32 }, 230eca2601cSRandy Fishel { "fipe_policy", KSTAT_DATA_INT32 }, 231eca2601cSRandy Fishel { "fipe_pm_time", KSTAT_DATA_UINT64 }, 232eca2601cSRandy Fishel #ifdef FIPE_KSTAT_DETAIL 233eca2601cSRandy Fishel { "ioat_ready", KSTAT_DATA_INT32 }, 234eca2601cSRandy Fishel { "pm_tryenter_cnt", KSTAT_DATA_UINT64 }, 235eca2601cSRandy Fishel { "pm_success_cnt", KSTAT_DATA_UINT64 }, 236eca2601cSRandy Fishel { "pm_race_cnt", KSTAT_DATA_UINT64 }, 237eca2601cSRandy Fishel { "cpu_loop_cnt", KSTAT_DATA_UINT64 }, 238eca2601cSRandy Fishel { "cpu_busy_cnt", KSTAT_DATA_UINT64 }, 239eca2601cSRandy Fishel { "cpu_idle_cnt", KSTAT_DATA_UINT64 }, 240eca2601cSRandy Fishel { "cpu_intr_busy_cnt", KSTAT_DATA_UINT64 }, 241eca2601cSRandy Fishel { "cpu_intr_thrt_cnt", KSTAT_DATA_UINT64 }, 242eca2601cSRandy Fishel { "bio_busy_cnt", KSTAT_DATA_UINT64 }, 243eca2601cSRandy Fishel { "ioat_start_fail_cnt", KSTAT_DATA_UINT64 }, 244eca2601cSRandy Fishel { "ioat_stop_fail_cnt", KSTAT_DATA_UINT64 } 245eca2601cSRandy Fishel #endif /* FIPE_KSTAT_DETAIL */ 246eca2601cSRandy Fishel }; 247eca2601cSRandy Fishel 248eca2601cSRandy Fishel #define FIPE_KSTAT_INC(v) \ 249eca2601cSRandy Fishel atomic_inc_64(&fipe_kstat.v.value.ui64) 250eca2601cSRandy Fishel #ifdef FIPE_KSTAT_DETAIL 251eca2601cSRandy Fishel #define FIPE_KSTAT_DETAIL_INC(v) \ 252eca2601cSRandy Fishel atomic_inc_64(&fipe_kstat.v.value.ui64) 253eca2601cSRandy Fishel #else /* FIPE_KSTAT_DETAIL */ 254eca2601cSRandy Fishel #define FIPE_KSTAT_DETAIL_INC(v) 255eca2601cSRandy Fishel #endif /* FIPE_KSTAT_DETAIL */ 256eca2601cSRandy Fishel 257eca2601cSRandy Fishel #else /* FIPE_KSTAT_SUPPORT */ 258eca2601cSRandy Fishel 259eca2601cSRandy Fishel #define FIPE_KSTAT_INC(v) 260eca2601cSRandy Fishel #define FIPE_KSTAT_DETAIL_INC(v) 261eca2601cSRandy Fishel 262eca2601cSRandy Fishel #endif /* FIPE_KSTAT_SUPPORT */ 263eca2601cSRandy Fishel 264eca2601cSRandy Fishel /* Save current power management profile during suspend/resume. */ 265eca2601cSRandy Fishel static fipe_pm_policy_t fipe_pm_policy_saved = FIPE_PM_POLICY_BALANCE; 266eca2601cSRandy Fishel static fipe_cpu_state_t *fipe_cpu_states = NULL; 267eca2601cSRandy Fishel 268eca2601cSRandy Fishel /* 269eca2601cSRandy Fishel * There is no lock to protect fipe_profile_curr, so fipe_profile_curr 270eca2601cSRandy Fishel * could change on threads in fipe_idle_enter. This is not an issue, 271eca2601cSRandy Fishel * as it always points to a valid profile, and though it might make 272eca2601cSRandy Fishel * an incorrect choice for the new profile, it will still be a valid 273eca2601cSRandy Fishel * selection, and would do the correct operation for the new profile on 274eca2601cSRandy Fishel * next cpu_idle_enter cycle. Since the selections would always be 275eca2601cSRandy Fishel * valid for some profile, the overhead for the lock is not wasted. 276eca2601cSRandy Fishel */ 277eca2601cSRandy Fishel static struct fipe_profile *fipe_profile_curr = NULL; 278eca2601cSRandy Fishel 279eca2601cSRandy Fishel static void fipe_idle_enter(void *arg, cpu_idle_callback_context_t ctx, 280eca2601cSRandy Fishel cpu_idle_check_wakeup_t check_func, void* check_arg); 281eca2601cSRandy Fishel static void fipe_idle_exit(void* arg, cpu_idle_callback_context_t ctx, 282eca2601cSRandy Fishel int flags); 283eca2601cSRandy Fishel static cpu_idle_callback_t fipe_idle_cb = { 284eca2601cSRandy Fishel CPU_IDLE_CALLBACK_VER0, 285eca2601cSRandy Fishel fipe_idle_enter, 286eca2601cSRandy Fishel fipe_idle_exit, 287eca2601cSRandy Fishel }; 288eca2601cSRandy Fishel 289eca2601cSRandy Fishel /* 290eca2601cSRandy Fishel * Configure memory controller into power saving mode: 291eca2601cSRandy Fishel * 1) OLTT activation limit is set to unlimited 292eca2601cSRandy Fishel * 2) MC works in S-CLTT mode 293eca2601cSRandy Fishel */ 294eca2601cSRandy Fishel static int 295eca2601cSRandy Fishel fipe_mc_change(int throttle) 296eca2601cSRandy Fishel { 297eca2601cSRandy Fishel /* Enable OLTT/disable S-CLTT mode */ 298eca2601cSRandy Fishel pci_config_put8(fipe_mc_ctrl.mc_pci_hdl, FIPE_MC_THRTCTRL, 299eca2601cSRandy Fishel fipe_mc_ctrl.mc_thrtctrl & ~FIPE_MC_THRTCTRL_HUNT); 300eca2601cSRandy Fishel /* Set OLTT activation limit to unlimited */ 301eca2601cSRandy Fishel pci_config_put8(fipe_mc_ctrl.mc_pci_hdl, FIPE_MC_GBLACT, 0); 302eca2601cSRandy Fishel /* 303eca2601cSRandy Fishel * Set S-CLTT low throttling to desired value. The lower value, 304eca2601cSRandy Fishel * the more power saving and the less available memory bandwidth. 305eca2601cSRandy Fishel */ 306eca2601cSRandy Fishel pci_config_put8(fipe_mc_ctrl.mc_pci_hdl, FIPE_MC_THRTLOW, throttle); 307eca2601cSRandy Fishel /* Enable S-CLTT/disable OLTT mode */ 308eca2601cSRandy Fishel pci_config_put8(fipe_mc_ctrl.mc_pci_hdl, FIPE_MC_THRTCTRL, 309eca2601cSRandy Fishel fipe_mc_ctrl.mc_thrtctrl | FIPE_MC_THRTCTRL_HUNT); 310eca2601cSRandy Fishel 311eca2601cSRandy Fishel return (0); 312eca2601cSRandy Fishel } 313eca2601cSRandy Fishel 314eca2601cSRandy Fishel /* 315eca2601cSRandy Fishel * Restore memory controller's original configuration. 316eca2601cSRandy Fishel */ 317eca2601cSRandy Fishel static void 318eca2601cSRandy Fishel fipe_mc_restore(void) 319eca2601cSRandy Fishel { 320eca2601cSRandy Fishel pci_config_put8(fipe_mc_ctrl.mc_pci_hdl, FIPE_MC_THRTCTRL, 321eca2601cSRandy Fishel fipe_mc_ctrl.mc_thrtctrl & ~FIPE_MC_THRTCTRL_HUNT); 322eca2601cSRandy Fishel pci_config_put8(fipe_mc_ctrl.mc_pci_hdl, FIPE_MC_GBLACT, 323eca2601cSRandy Fishel fipe_mc_ctrl.mc_gblact); 324eca2601cSRandy Fishel pci_config_put8(fipe_mc_ctrl.mc_pci_hdl, FIPE_MC_THRTLOW, 325eca2601cSRandy Fishel fipe_mc_ctrl.mc_thrtlow); 326eca2601cSRandy Fishel pci_config_put8(fipe_mc_ctrl.mc_pci_hdl, FIPE_MC_THRTCTRL, 327eca2601cSRandy Fishel fipe_mc_ctrl.mc_thrtctrl); 328eca2601cSRandy Fishel } 329eca2601cSRandy Fishel 330eca2601cSRandy Fishel /* 331eca2601cSRandy Fishel * Initialize memory controller's data structure and status. 332eca2601cSRandy Fishel */ 333eca2601cSRandy Fishel static int 334eca2601cSRandy Fishel fipe_mc_init(dev_info_t *dip) 335eca2601cSRandy Fishel { 336eca2601cSRandy Fishel ddi_acc_handle_t handle; 337eca2601cSRandy Fishel 338eca2601cSRandy Fishel bzero(&fipe_mc_ctrl, sizeof (fipe_mc_ctrl)); 339eca2601cSRandy Fishel 340eca2601cSRandy Fishel /* Hold one reference count and will be released in fipe_mc_fini. */ 341eca2601cSRandy Fishel ndi_hold_devi(dip); 342eca2601cSRandy Fishel 343eca2601cSRandy Fishel /* Setup pci configuration handler. */ 344eca2601cSRandy Fishel if (pci_config_setup(dip, &handle) != DDI_SUCCESS) { 345eca2601cSRandy Fishel cmn_err(CE_WARN, 346eca2601cSRandy Fishel "!fipe: failed to setup pcicfg handler in mc_init."); 347eca2601cSRandy Fishel ndi_rele_devi(dip); 348eca2601cSRandy Fishel return (-1); 349eca2601cSRandy Fishel } 350eca2601cSRandy Fishel 351eca2601cSRandy Fishel /* Save original configuration. */ 352eca2601cSRandy Fishel fipe_mc_ctrl.mc_thrtctrl = pci_config_get8(handle, FIPE_MC_THRTCTRL); 353eca2601cSRandy Fishel fipe_mc_ctrl.mc_thrtlow = pci_config_get8(handle, FIPE_MC_THRTLOW); 354eca2601cSRandy Fishel fipe_mc_ctrl.mc_gblact = pci_config_get8(handle, FIPE_MC_GBLACT); 355eca2601cSRandy Fishel fipe_mc_ctrl.mc_dip = dip; 356eca2601cSRandy Fishel fipe_mc_ctrl.mc_pci_hdl = handle; 357eca2601cSRandy Fishel fipe_mc_ctrl.mc_initialized = B_TRUE; 358eca2601cSRandy Fishel 359eca2601cSRandy Fishel return (0); 360eca2601cSRandy Fishel } 361eca2601cSRandy Fishel 362eca2601cSRandy Fishel /* 363eca2601cSRandy Fishel * Restore memory controller's configuration and release resources. 364eca2601cSRandy Fishel */ 365eca2601cSRandy Fishel static void 366eca2601cSRandy Fishel fipe_mc_fini(void) 367eca2601cSRandy Fishel { 368eca2601cSRandy Fishel if (fipe_mc_ctrl.mc_initialized) { 369eca2601cSRandy Fishel fipe_mc_restore(); 370eca2601cSRandy Fishel pci_config_teardown(&fipe_mc_ctrl.mc_pci_hdl); 371eca2601cSRandy Fishel ndi_rele_devi(fipe_mc_ctrl.mc_dip); 372eca2601cSRandy Fishel fipe_mc_ctrl.mc_initialized = B_FALSE; 373eca2601cSRandy Fishel } 374eca2601cSRandy Fishel bzero(&fipe_mc_ctrl, sizeof (fipe_mc_ctrl)); 375eca2601cSRandy Fishel } 376eca2601cSRandy Fishel 377eca2601cSRandy Fishel /* Search device with specific pci ids. */ 378eca2601cSRandy Fishel struct fipe_pci_ioat_id { 379eca2601cSRandy Fishel uint16_t venid; 380eca2601cSRandy Fishel uint16_t devid; 381eca2601cSRandy Fishel uint16_t subvenid; 382eca2601cSRandy Fishel uint16_t subsysid; 383eca2601cSRandy Fishel char *unitaddr; 384eca2601cSRandy Fishel }; 385eca2601cSRandy Fishel 386eca2601cSRandy Fishel static struct fipe_pci_ioat_id fipe_pci_ioat_ids[] = { 387eca2601cSRandy Fishel { 0x8086, 0x1a38, 0xffff, 0xffff, NULL }, 388eca2601cSRandy Fishel { 0x8086, 0x360b, 0xffff, 0xffff, NULL }, 389eca2601cSRandy Fishel }; 390eca2601cSRandy Fishel 391eca2601cSRandy Fishel /*ARGSUSED*/ 392eca2601cSRandy Fishel static int 393eca2601cSRandy Fishel fipe_search_ioat_dev(dev_info_t *dip, void *arg) 394eca2601cSRandy Fishel { 395eca2601cSRandy Fishel char *unit; 396eca2601cSRandy Fishel struct fipe_pci_ioat_id *id; 397eca2601cSRandy Fishel int i, max, venid, devid, subvenid, subsysid; 398eca2601cSRandy Fishel 399eca2601cSRandy Fishel /* Query PCI id properties. */ 400eca2601cSRandy Fishel venid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 401eca2601cSRandy Fishel "vendor-id", 0xffffffff); 402eca2601cSRandy Fishel if (venid == 0xffffffff) { 403eca2601cSRandy Fishel return (DDI_WALK_CONTINUE); 404eca2601cSRandy Fishel } 405eca2601cSRandy Fishel devid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 406eca2601cSRandy Fishel "device-id", 0xffffffff); 407eca2601cSRandy Fishel if (devid == 0xffffffff) { 408eca2601cSRandy Fishel return (DDI_WALK_CONTINUE); 409eca2601cSRandy Fishel } 410eca2601cSRandy Fishel subvenid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 411eca2601cSRandy Fishel "subsystem-vendor-id", 0xffffffff); 412eca2601cSRandy Fishel if (subvenid == 0xffffffff) { 413eca2601cSRandy Fishel return (DDI_WALK_CONTINUE); 414eca2601cSRandy Fishel } 415eca2601cSRandy Fishel subsysid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 416eca2601cSRandy Fishel "subsystem-id", 0xffffffff); 417eca2601cSRandy Fishel if (subvenid == 0xffffffff) { 418eca2601cSRandy Fishel return (DDI_WALK_CONTINUE); 419eca2601cSRandy Fishel } 420eca2601cSRandy Fishel if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 421eca2601cSRandy Fishel "unit-address", &unit) != DDI_PROP_SUCCESS) { 422eca2601cSRandy Fishel return (DDI_WALK_CONTINUE); 423eca2601cSRandy Fishel } 424eca2601cSRandy Fishel 425eca2601cSRandy Fishel max = sizeof (fipe_pci_ioat_ids) / sizeof (fipe_pci_ioat_ids[0]); 426eca2601cSRandy Fishel for (i = 0; i < max; i++) { 427eca2601cSRandy Fishel id = &fipe_pci_ioat_ids[i]; 428eca2601cSRandy Fishel if ((id->venid == 0xffffu || id->venid == venid) && 429eca2601cSRandy Fishel (id->devid == 0xffffu || id->devid == devid) && 430eca2601cSRandy Fishel (id->subvenid == 0xffffu || id->subvenid == subvenid) && 431eca2601cSRandy Fishel (id->subsysid == 0xffffu || id->subsysid == subsysid) && 432eca2601cSRandy Fishel (id->unitaddr == NULL || strcmp(id->unitaddr, unit) == 0)) { 433eca2601cSRandy Fishel break; 434eca2601cSRandy Fishel } 435eca2601cSRandy Fishel } 436eca2601cSRandy Fishel ddi_prop_free(unit); 437eca2601cSRandy Fishel if (i >= max) { 438eca2601cSRandy Fishel return (DDI_WALK_CONTINUE); 439eca2601cSRandy Fishel } 440eca2601cSRandy Fishel 441eca2601cSRandy Fishel /* Found IOAT device, hold one reference count. */ 442eca2601cSRandy Fishel ndi_hold_devi(dip); 443eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_dev_info = dip; 444eca2601cSRandy Fishel 445eca2601cSRandy Fishel return (DDI_WALK_TERMINATE); 446eca2601cSRandy Fishel } 447eca2601cSRandy Fishel 448eca2601cSRandy Fishel /* 449eca2601cSRandy Fishel * To enable FBDIMM idle power enhancement mechanism, IOAT will be used to 450eca2601cSRandy Fishel * generate enough memory traffic to trigger memory controller thermal throttle 451eca2601cSRandy Fishel * circuitry. 452eca2601cSRandy Fishel * If dcopy/ioat is available, we will use dcopy interface to communicate 453eca2601cSRandy Fishel * with IOAT. Otherwise the built-in driver will directly talk to IOAT 454eca2601cSRandy Fishel * hardware. 455eca2601cSRandy Fishel */ 456eca2601cSRandy Fishel #ifdef FIPE_IOAT_BUILTIN 457eca2601cSRandy Fishel static int 458eca2601cSRandy Fishel fipe_ioat_trigger(void) 459eca2601cSRandy Fishel { 460eca2601cSRandy Fishel uint16_t ctrl; 461eca2601cSRandy Fishel uint32_t err; 462eca2601cSRandy Fishel uint8_t *addr = fipe_ioat_ctrl.ioat_reg_addr; 463eca2601cSRandy Fishel ddi_acc_handle_t handle = fipe_ioat_ctrl.ioat_reg_handle; 464eca2601cSRandy Fishel 465eca2601cSRandy Fishel /* Check channel in use flag. */ 466eca2601cSRandy Fishel ctrl = ddi_get16(handle, (uint16_t *)(addr + FIPE_IOAT_CHAN_CTRL)); 467eca2601cSRandy Fishel if (ctrl & 0x100) { 468eca2601cSRandy Fishel /* 469eca2601cSRandy Fishel * Channel is in use by somebody else. IOAT driver may have 470eca2601cSRandy Fishel * been loaded, forbid fipe from accessing IOAT hardware 471eca2601cSRandy Fishel * anymore. 472eca2601cSRandy Fishel */ 473eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_ready = B_FALSE; 474eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_failed = B_TRUE; 475eca2601cSRandy Fishel FIPE_KSTAT_INC(ioat_start_fail_cnt); 476eca2601cSRandy Fishel return (-1); 477eca2601cSRandy Fishel } else { 478eca2601cSRandy Fishel /* Set channel in use flag. */ 479eca2601cSRandy Fishel ddi_put16(handle, 480eca2601cSRandy Fishel (uint16_t *)(addr + FIPE_IOAT_CHAN_CTRL), 0x100); 481eca2601cSRandy Fishel } 482eca2601cSRandy Fishel 483eca2601cSRandy Fishel /* Write command address. */ 484eca2601cSRandy Fishel ddi_put32(handle, 485eca2601cSRandy Fishel (uint32_t *)(addr + FIPE_IOAT_CHAN_ADDR_LO), 486eca2601cSRandy Fishel (uint32_t)fipe_ioat_ctrl.ioat_cmd_physaddr); 487eca2601cSRandy Fishel ddi_put32(handle, (uint32_t *)(addr + FIPE_IOAT_CHAN_ADDR_HI), 488eca2601cSRandy Fishel (uint32_t)(fipe_ioat_ctrl.ioat_cmd_physaddr >> 32)); 489eca2601cSRandy Fishel 490eca2601cSRandy Fishel /* Check and clear error flags. */ 491eca2601cSRandy Fishel err = ddi_get32(handle, (uint32_t *)(addr + FIPE_IOAT_CHAN_ERR)); 492eca2601cSRandy Fishel if (err != 0) { 493eca2601cSRandy Fishel ddi_put32(handle, (uint32_t *)(addr + FIPE_IOAT_CHAN_ERR), err); 494eca2601cSRandy Fishel } 495eca2601cSRandy Fishel 496eca2601cSRandy Fishel /* Start channel. */ 497eca2601cSRandy Fishel ddi_put8(handle, (uint8_t *)(addr + FIPE_IOAT_CHAN_CMD), 0x1); 498eca2601cSRandy Fishel 499eca2601cSRandy Fishel return (0); 500eca2601cSRandy Fishel } 501eca2601cSRandy Fishel 502eca2601cSRandy Fishel static void 503eca2601cSRandy Fishel fipe_ioat_cancel(void) 504eca2601cSRandy Fishel { 505eca2601cSRandy Fishel uint32_t status; 506eca2601cSRandy Fishel uint8_t *addr = fipe_ioat_ctrl.ioat_reg_addr; 507eca2601cSRandy Fishel ddi_acc_handle_t handle = fipe_ioat_ctrl.ioat_reg_handle; 508eca2601cSRandy Fishel 509eca2601cSRandy Fishel /* 510eca2601cSRandy Fishel * Reset channel. Sometimes reset is not reliable, 511eca2601cSRandy Fishel * so check completion or abort status after reset. 512eca2601cSRandy Fishel */ 513eca2601cSRandy Fishel /* LINTED: constant in conditional context */ 514eca2601cSRandy Fishel while (1) { 515eca2601cSRandy Fishel /* Issue reset channel command. */ 516eca2601cSRandy Fishel ddi_put8(handle, (uint8_t *)(addr + FIPE_IOAT_CHAN_CMD), 0x20); 517eca2601cSRandy Fishel 518eca2601cSRandy Fishel /* Query command status. */ 519eca2601cSRandy Fishel status = ddi_get32(handle, 520eca2601cSRandy Fishel (uint32_t *)(addr + FIPE_IOAT_CHAN_STS_LO)); 521eca2601cSRandy Fishel if (status & 0x1) { 522eca2601cSRandy Fishel /* Reset channel completed. */ 523eca2601cSRandy Fishel break; 524eca2601cSRandy Fishel } else { 525eca2601cSRandy Fishel SMT_PAUSE(); 526eca2601cSRandy Fishel } 527eca2601cSRandy Fishel } 528eca2601cSRandy Fishel 529eca2601cSRandy Fishel /* Put channel into "not in use" state. */ 530eca2601cSRandy Fishel ddi_put16(handle, (uint16_t *)(addr + FIPE_IOAT_CHAN_CTRL), 0); 531eca2601cSRandy Fishel } 532eca2601cSRandy Fishel 533eca2601cSRandy Fishel /*ARGSUSED*/ 534eca2601cSRandy Fishel static void 535eca2601cSRandy Fishel fipe_ioat_alloc(void *arg) 536eca2601cSRandy Fishel { 537eca2601cSRandy Fishel int rc = 0, nregs; 538eca2601cSRandy Fishel dev_info_t *dip; 539eca2601cSRandy Fishel ddi_device_acc_attr_t attr; 540eca2601cSRandy Fishel boolean_t fatal = B_FALSE; 541eca2601cSRandy Fishel 542eca2601cSRandy Fishel mutex_enter(&fipe_ioat_ctrl.ioat_lock); 543eca2601cSRandy Fishel /* 544eca2601cSRandy Fishel * fipe_ioat_alloc() is called in DEVICE ATTACH context when loaded. 545eca2601cSRandy Fishel * In DEVICE ATTACH context, it can't call ddi_walk_devs(), so just 546eca2601cSRandy Fishel * schedule a timer and exit. 547eca2601cSRandy Fishel */ 548eca2601cSRandy Fishel if (fipe_ioat_ctrl.ioat_try_alloc == B_FALSE) { 549eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_try_alloc = B_TRUE; 550eca2601cSRandy Fishel goto out_error; 551eca2601cSRandy Fishel } 552eca2601cSRandy Fishel 553eca2601cSRandy Fishel /* Check whether has been initialized or encountered permanent error. */ 554eca2601cSRandy Fishel if (fipe_ioat_ctrl.ioat_ready || fipe_ioat_ctrl.ioat_failed || 555eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_cancel) { 556eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_timerid = 0; 557eca2601cSRandy Fishel mutex_exit(&fipe_ioat_ctrl.ioat_lock); 558eca2601cSRandy Fishel return; 559eca2601cSRandy Fishel } 560eca2601cSRandy Fishel 561eca2601cSRandy Fishel if (fipe_ioat_ctrl.ioat_dev_info == NULL) { 562eca2601cSRandy Fishel /* Find dev_info_t for IOAT engine. */ 563eca2601cSRandy Fishel ddi_walk_devs(ddi_root_node(), fipe_search_ioat_dev, NULL); 564eca2601cSRandy Fishel if (fipe_ioat_ctrl.ioat_dev_info == NULL) { 565eca2601cSRandy Fishel cmn_err(CE_NOTE, 566eca2601cSRandy Fishel "!fipe: no IOAT hardware found, disable pm."); 567eca2601cSRandy Fishel fatal = B_TRUE; 568eca2601cSRandy Fishel goto out_error; 569eca2601cSRandy Fishel } 570eca2601cSRandy Fishel } 571eca2601cSRandy Fishel 572eca2601cSRandy Fishel /* Map in IOAT control register window. */ 573eca2601cSRandy Fishel ASSERT(fipe_ioat_ctrl.ioat_dev_info != NULL); 574eca2601cSRandy Fishel ASSERT(fipe_ioat_ctrl.ioat_reg_mapped == B_FALSE); 575eca2601cSRandy Fishel dip = fipe_ioat_ctrl.ioat_dev_info; 576eca2601cSRandy Fishel if (ddi_dev_nregs(dip, &nregs) != DDI_SUCCESS || nregs < 2) { 577eca2601cSRandy Fishel cmn_err(CE_WARN, "!fipe: ioat has not enough register bars."); 578eca2601cSRandy Fishel fatal = B_TRUE; 579eca2601cSRandy Fishel goto out_error; 580eca2601cSRandy Fishel } 581eca2601cSRandy Fishel attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 582eca2601cSRandy Fishel attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC; 583eca2601cSRandy Fishel attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 584eca2601cSRandy Fishel rc = ddi_regs_map_setup(dip, 1, 585eca2601cSRandy Fishel (caddr_t *)&fipe_ioat_ctrl.ioat_reg_addr, 586eca2601cSRandy Fishel 0, 0, &attr, &fipe_ioat_ctrl.ioat_reg_handle); 587eca2601cSRandy Fishel if (rc != DDI_SUCCESS) { 588eca2601cSRandy Fishel cmn_err(CE_WARN, "!fipe: failed to map IOAT registeres."); 589eca2601cSRandy Fishel fatal = B_TRUE; 590eca2601cSRandy Fishel goto out_error; 591eca2601cSRandy Fishel } 592eca2601cSRandy Fishel 593eca2601cSRandy Fishel /* Mark IOAT status. */ 594eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_reg_mapped = B_TRUE; 595eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_ready = B_TRUE; 596eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_failed = B_FALSE; 597eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_timerid = 0; 598eca2601cSRandy Fishel mutex_exit(&fipe_ioat_ctrl.ioat_lock); 599eca2601cSRandy Fishel 600eca2601cSRandy Fishel return; 601eca2601cSRandy Fishel 602eca2601cSRandy Fishel out_error: 603eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_timerid = 0; 604eca2601cSRandy Fishel if (!fipe_ioat_ctrl.ioat_ready && !fipe_ioat_ctrl.ioat_cancel) { 605eca2601cSRandy Fishel if (fatal) { 606eca2601cSRandy Fishel /* Mark permanent error and give up. */ 607eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_failed = B_TRUE; 608eca2601cSRandy Fishel /* Release reference count hold by ddi_find_devinfo. */ 609eca2601cSRandy Fishel if (fipe_ioat_ctrl.ioat_dev_info != NULL) { 610eca2601cSRandy Fishel ndi_rele_devi(fipe_ioat_ctrl.ioat_dev_info); 611eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_dev_info = NULL; 612eca2601cSRandy Fishel } 613eca2601cSRandy Fishel } else { 614eca2601cSRandy Fishel /* 615eca2601cSRandy Fishel * Schedule another timer to keep on trying. 616eca2601cSRandy Fishel * timeout() should always succeed, no need to check 617eca2601cSRandy Fishel * return. 618eca2601cSRandy Fishel */ 619eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_timerid = timeout(fipe_ioat_alloc, 620eca2601cSRandy Fishel NULL, drv_usectohz(FIPE_IOAT_RETRY_INTERVAL)); 621eca2601cSRandy Fishel } 622eca2601cSRandy Fishel } 623eca2601cSRandy Fishel mutex_exit(&fipe_ioat_ctrl.ioat_lock); 624eca2601cSRandy Fishel } 625eca2601cSRandy Fishel 626eca2601cSRandy Fishel static void 627eca2601cSRandy Fishel fipe_ioat_free(void) 628eca2601cSRandy Fishel { 629eca2601cSRandy Fishel mutex_enter(&fipe_ioat_ctrl.ioat_lock); 630eca2601cSRandy Fishel /* Cancel timeout to avoid race condition. */ 631eca2601cSRandy Fishel if (fipe_ioat_ctrl.ioat_timerid != 0) { 632eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_cancel = B_TRUE; 633eca2601cSRandy Fishel mutex_exit(&fipe_ioat_ctrl.ioat_lock); 634eca2601cSRandy Fishel (void) untimeout(fipe_ioat_ctrl.ioat_timerid); 635eca2601cSRandy Fishel mutex_enter(&fipe_ioat_ctrl.ioat_lock); 636eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_timerid = 0; 637eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_cancel = B_FALSE; 638eca2601cSRandy Fishel } 639eca2601cSRandy Fishel 640eca2601cSRandy Fishel if (fipe_ioat_ctrl.ioat_reg_mapped) { 641eca2601cSRandy Fishel ddi_regs_map_free(&fipe_ioat_ctrl.ioat_reg_handle); 642eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_reg_mapped = B_FALSE; 643eca2601cSRandy Fishel } 644eca2601cSRandy Fishel 645eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_ready = B_FALSE; 646eca2601cSRandy Fishel mutex_exit(&fipe_ioat_ctrl.ioat_lock); 647eca2601cSRandy Fishel } 648eca2601cSRandy Fishel 649eca2601cSRandy Fishel #else /* FIPE_IOAT_BUILTIN */ 650eca2601cSRandy Fishel 651eca2601cSRandy Fishel /* 652eca2601cSRandy Fishel * Trigger IOAT memory copy operation when entering power saving state. 653eca2601cSRandy Fishel * A group of commands will be posted to IOAT driver and those commands 654eca2601cSRandy Fishel * will be placed into an IOAT ring buffer. 655eca2601cSRandy Fishel */ 656eca2601cSRandy Fishel static int 657eca2601cSRandy Fishel fipe_ioat_trigger(void) 658eca2601cSRandy Fishel { 659eca2601cSRandy Fishel int idx; 660eca2601cSRandy Fishel dcopy_cmd_t *cmds = fipe_ioat_ctrl.ioat_cmds; 661eca2601cSRandy Fishel 662eca2601cSRandy Fishel for (idx = FIPE_IOAT_CMD_NUM; idx > 0; idx--) { 663eca2601cSRandy Fishel if (dcopy_cmd_post(cmds[idx]) == DCOPY_SUCCESS) { 664eca2601cSRandy Fishel continue; 665eca2601cSRandy Fishel } else { 666eca2601cSRandy Fishel /* 667eca2601cSRandy Fishel * Don't rollback on failure, it doesn't hurt much more 668eca2601cSRandy Fishel * than some small memory copy operations. 669eca2601cSRandy Fishel */ 670eca2601cSRandy Fishel FIPE_KSTAT_DETAIL_INC(ioat_start_fail_cnt); 671eca2601cSRandy Fishel return (-1); 672eca2601cSRandy Fishel } 673eca2601cSRandy Fishel } 674eca2601cSRandy Fishel 675eca2601cSRandy Fishel return (0); 676eca2601cSRandy Fishel } 677eca2601cSRandy Fishel 678eca2601cSRandy Fishel /* 679eca2601cSRandy Fishel * Cancel the memory copy operations posted by fipe_ioat_trigger. 680eca2601cSRandy Fishel * It's achieved by posting a new command which will break the ring 681eca2601cSRandy Fishel * created by fipe_ioat_trigger. If it fails, the best way to recover 682eca2601cSRandy Fishel * is to just let it go. IOAT will recover when posting next command 683eca2601cSRandy Fishel * on the same channel. 684eca2601cSRandy Fishel */ 685eca2601cSRandy Fishel static void 686eca2601cSRandy Fishel fipe_ioat_cancel(void) 687eca2601cSRandy Fishel { 688eca2601cSRandy Fishel if (dcopy_cmd_post(fipe_ioat_ctrl.ioat_cmds[0]) != DCOPY_SUCCESS) { 689eca2601cSRandy Fishel FIPE_KSTAT_DETAIL_INC(ioat_stop_fail_cnt); 690eca2601cSRandy Fishel } 691eca2601cSRandy Fishel } 692eca2601cSRandy Fishel 693eca2601cSRandy Fishel /* 694eca2601cSRandy Fishel * This function will be called from allocate IOAT resources. 695eca2601cSRandy Fishel * Allocation may fail due to following reasons: 696eca2601cSRandy Fishel * 1) IOAT driver hasn't been loaded yet. Keep on trying in this case. 697eca2601cSRandy Fishel * 2) IOAT resources are temporarily unavailable. Keep on trying in this case. 698eca2601cSRandy Fishel * 3) Other no recoverable reasons. Disable power management function. 699eca2601cSRandy Fishel */ 700eca2601cSRandy Fishel /*ARGSUSED*/ 701eca2601cSRandy Fishel static void 702eca2601cSRandy Fishel fipe_ioat_alloc(void *arg) 703eca2601cSRandy Fishel { 704eca2601cSRandy Fishel int idx, flags, rc = 0; 705eca2601cSRandy Fishel uint64_t physaddr; 706eca2601cSRandy Fishel boolean_t fatal = B_FALSE; 707eca2601cSRandy Fishel dcopy_query_t info; 708eca2601cSRandy Fishel dcopy_handle_t handle; 709eca2601cSRandy Fishel dcopy_cmd_t cmds[FIPE_IOAT_CMD_NUM + 1]; 710eca2601cSRandy Fishel 711eca2601cSRandy Fishel mutex_enter(&fipe_ioat_ctrl.ioat_lock); 712eca2601cSRandy Fishel /* 713eca2601cSRandy Fishel * fipe_ioat_alloc() is called in DEVICE ATTACH context when loaded. 714eca2601cSRandy Fishel * In DEVICE ATTACH context, it can't call ddi_walk_devs(), so just 715eca2601cSRandy Fishel * schedule a timer and exit. 716eca2601cSRandy Fishel */ 717eca2601cSRandy Fishel if (fipe_ioat_ctrl.ioat_try_alloc == B_FALSE) { 718eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_try_alloc = B_TRUE; 719eca2601cSRandy Fishel mutex_exit(&fipe_ioat_ctrl.ioat_lock); 720eca2601cSRandy Fishel goto out_error; 721eca2601cSRandy Fishel } 722eca2601cSRandy Fishel 723eca2601cSRandy Fishel /* 724eca2601cSRandy Fishel * Check whether device has been initialized or if it encountered 725eca2601cSRandy Fishel * some permanent error. 726eca2601cSRandy Fishel */ 727eca2601cSRandy Fishel if (fipe_ioat_ctrl.ioat_ready || fipe_ioat_ctrl.ioat_failed || 728eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_cancel) { 729eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_timerid = 0; 730eca2601cSRandy Fishel mutex_exit(&fipe_ioat_ctrl.ioat_lock); 731eca2601cSRandy Fishel return; 732eca2601cSRandy Fishel } 733eca2601cSRandy Fishel 734eca2601cSRandy Fishel if (fipe_ioat_ctrl.ioat_dev_info == NULL) { 735eca2601cSRandy Fishel /* Find dev_info_t for IOAT engine. */ 736eca2601cSRandy Fishel ddi_walk_devs(ddi_root_node(), fipe_search_ioat_dev, NULL); 737eca2601cSRandy Fishel if (fipe_ioat_ctrl.ioat_dev_info == NULL) { 738eca2601cSRandy Fishel cmn_err(CE_NOTE, 739eca2601cSRandy Fishel "!fipe: no IOAT hardware found, disable pm."); 740eca2601cSRandy Fishel mutex_exit(&fipe_ioat_ctrl.ioat_lock); 741eca2601cSRandy Fishel fatal = B_TRUE; 742eca2601cSRandy Fishel goto out_error; 743eca2601cSRandy Fishel } 744eca2601cSRandy Fishel } 745eca2601cSRandy Fishel mutex_exit(&fipe_ioat_ctrl.ioat_lock); 746eca2601cSRandy Fishel 747eca2601cSRandy Fishel /* Check, allocate and initialize IOAT resources with lock released. */ 748eca2601cSRandy Fishel dcopy_query(&info); 749eca2601cSRandy Fishel if (info.dq_version < DCOPY_QUERY_V0) { 750eca2601cSRandy Fishel /* Permanent error, give up. */ 751eca2601cSRandy Fishel cmn_err(CE_WARN, "!fipe: IOAT driver version mismatch."); 752eca2601cSRandy Fishel fatal = B_TRUE; 753eca2601cSRandy Fishel goto out_error; 754eca2601cSRandy Fishel } else if (info.dq_num_channels == 0) { 755eca2601cSRandy Fishel /* IOAT driver hasn't been loaded, keep trying. */ 756eca2601cSRandy Fishel goto out_error; 757eca2601cSRandy Fishel } 758eca2601cSRandy Fishel 759eca2601cSRandy Fishel /* Allocate IOAT channel. */ 760eca2601cSRandy Fishel rc = dcopy_alloc(DCOPY_NOSLEEP, &handle); 761eca2601cSRandy Fishel if (rc == DCOPY_NORESOURCES) { 762eca2601cSRandy Fishel /* Resource temporarily not available, keep trying. */ 763eca2601cSRandy Fishel goto out_error; 764eca2601cSRandy Fishel } else if (rc != DCOPY_SUCCESS) { 765eca2601cSRandy Fishel /* Permanent error, give up. */ 766eca2601cSRandy Fishel cmn_err(CE_WARN, "!fipe: failed to allocate IOAT channel."); 767eca2601cSRandy Fishel fatal = B_TRUE; 768eca2601cSRandy Fishel goto out_error; 769eca2601cSRandy Fishel } 770eca2601cSRandy Fishel 771eca2601cSRandy Fishel /* 772eca2601cSRandy Fishel * Allocate multiple IOAT commands and organize them into a ring to 773eca2601cSRandy Fishel * loop forever. Commands number is determined by IOAT descriptor size 774eca2601cSRandy Fishel * and memory interleave pattern. 775eca2601cSRandy Fishel * cmd[0] is used break the loop and disable IOAT operation. 776eca2601cSRandy Fishel * cmd[1, FIPE_IOAT_CMD_NUM] are grouped into a ring and cmd[1] is the 777eca2601cSRandy Fishel * list head. 778eca2601cSRandy Fishel */ 779eca2601cSRandy Fishel bzero(cmds, sizeof (cmds)); 780eca2601cSRandy Fishel physaddr = fipe_ioat_ctrl.ioat_buf_physaddr; 781eca2601cSRandy Fishel for (idx = FIPE_IOAT_CMD_NUM; idx >= 0; idx--) { 782eca2601cSRandy Fishel /* Allocate IOAT commands. */ 783eca2601cSRandy Fishel if (idx == 0 || idx == FIPE_IOAT_CMD_NUM) { 784eca2601cSRandy Fishel flags = DCOPY_NOSLEEP; 785eca2601cSRandy Fishel } else { 786eca2601cSRandy Fishel /* 787eca2601cSRandy Fishel * To link commands into a list, the initial value of 788eca2601cSRandy Fishel * cmd need to be set to next cmd on list. 789eca2601cSRandy Fishel */ 790eca2601cSRandy Fishel flags = DCOPY_NOSLEEP | DCOPY_ALLOC_LINK; 791eca2601cSRandy Fishel cmds[idx] = cmds[idx + 1]; 792eca2601cSRandy Fishel } 793eca2601cSRandy Fishel rc = dcopy_cmd_alloc(handle, flags, &cmds[idx]); 794eca2601cSRandy Fishel if (rc == DCOPY_NORESOURCES) { 795eca2601cSRandy Fishel goto out_freecmd; 796eca2601cSRandy Fishel } else if (rc != DCOPY_SUCCESS) { 797eca2601cSRandy Fishel /* Permanent error, give up. */ 798eca2601cSRandy Fishel cmn_err(CE_WARN, 799eca2601cSRandy Fishel "!fipe: failed to allocate IOAT command."); 800eca2601cSRandy Fishel fatal = B_TRUE; 801eca2601cSRandy Fishel goto out_freecmd; 802eca2601cSRandy Fishel } 803eca2601cSRandy Fishel 804eca2601cSRandy Fishel /* Disable src/dst snoop to improve CPU cache efficiency. */ 805eca2601cSRandy Fishel cmds[idx]->dp_flags = DCOPY_CMD_NOSRCSNP | DCOPY_CMD_NODSTSNP; 806eca2601cSRandy Fishel /* Specially handle commands on the list. */ 807eca2601cSRandy Fishel if (idx != 0) { 808eca2601cSRandy Fishel /* Disable IOAT status. */ 809eca2601cSRandy Fishel cmds[idx]->dp_flags |= DCOPY_CMD_NOSTAT; 810eca2601cSRandy Fishel /* Disable waiting for resources. */ 811eca2601cSRandy Fishel cmds[idx]->dp_flags |= DCOPY_CMD_NOWAIT; 812eca2601cSRandy Fishel if (idx == 1) { 813eca2601cSRandy Fishel /* The list head, chain command into loop. */ 814eca2601cSRandy Fishel cmds[idx]->dp_flags |= DCOPY_CMD_LOOP; 815eca2601cSRandy Fishel } else { 816eca2601cSRandy Fishel /* Queue all other commands except head. */ 817eca2601cSRandy Fishel cmds[idx]->dp_flags |= DCOPY_CMD_QUEUE; 818eca2601cSRandy Fishel } 819eca2601cSRandy Fishel } 820eca2601cSRandy Fishel cmds[idx]->dp_cmd = DCOPY_CMD_COPY; 821eca2601cSRandy Fishel cmds[idx]->dp.copy.cc_source = physaddr; 822eca2601cSRandy Fishel cmds[idx]->dp.copy.cc_dest = physaddr + FIPE_MC_MEMORY_OFFSET; 823eca2601cSRandy Fishel if (idx == 0) { 824eca2601cSRandy Fishel /* 825eca2601cSRandy Fishel * Command 0 is used to cancel memory copy by breaking 826eca2601cSRandy Fishel * the ring created in fipe_ioat_trigger(). 827eca2601cSRandy Fishel * For efficiency, use the smallest memory copy size. 828eca2601cSRandy Fishel */ 829eca2601cSRandy Fishel cmds[idx]->dp.copy.cc_size = 1; 830eca2601cSRandy Fishel } else { 831eca2601cSRandy Fishel cmds[idx]->dp.copy.cc_size = FIPE_MC_MEMORY_SIZE; 832eca2601cSRandy Fishel } 833eca2601cSRandy Fishel } 834eca2601cSRandy Fishel 835eca2601cSRandy Fishel /* Update IOAT control status if it hasn't been initialized yet. */ 836eca2601cSRandy Fishel mutex_enter(&fipe_ioat_ctrl.ioat_lock); 837eca2601cSRandy Fishel if (!fipe_ioat_ctrl.ioat_ready && !fipe_ioat_ctrl.ioat_cancel) { 838eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_handle = handle; 839eca2601cSRandy Fishel for (idx = 0; idx <= FIPE_IOAT_CMD_NUM; idx++) { 840eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_cmds[idx] = cmds[idx]; 841eca2601cSRandy Fishel } 842eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_ready = B_TRUE; 843eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_failed = B_FALSE; 844eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_timerid = 0; 845eca2601cSRandy Fishel mutex_exit(&fipe_ioat_ctrl.ioat_lock); 846eca2601cSRandy Fishel return; 847eca2601cSRandy Fishel } 848eca2601cSRandy Fishel mutex_exit(&fipe_ioat_ctrl.ioat_lock); 849eca2601cSRandy Fishel /* Initialized by another thread, fall through to free resources. */ 850eca2601cSRandy Fishel 851eca2601cSRandy Fishel out_freecmd: 852eca2601cSRandy Fishel if (cmds[0] != NULL) { 853eca2601cSRandy Fishel dcopy_cmd_free(&cmds[0]); 854eca2601cSRandy Fishel } 855eca2601cSRandy Fishel /* Only need to free head, dcopy will free all commands on the list. */ 856eca2601cSRandy Fishel for (idx = 1; idx <= FIPE_IOAT_CMD_NUM; idx++) { 857eca2601cSRandy Fishel if (cmds[idx] != NULL) { 858eca2601cSRandy Fishel dcopy_cmd_free(&cmds[idx]); 859eca2601cSRandy Fishel break; 860eca2601cSRandy Fishel } 861eca2601cSRandy Fishel } 862eca2601cSRandy Fishel dcopy_free(&handle); 863eca2601cSRandy Fishel 864eca2601cSRandy Fishel out_error: 865eca2601cSRandy Fishel mutex_enter(&fipe_ioat_ctrl.ioat_lock); 866eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_timerid = 0; 867eca2601cSRandy Fishel if (!fipe_ioat_ctrl.ioat_ready && !fipe_ioat_ctrl.ioat_cancel) { 868eca2601cSRandy Fishel if (fatal) { 869eca2601cSRandy Fishel /* Mark permanent error and give up. */ 870eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_failed = B_TRUE; 871eca2601cSRandy Fishel /* Release reference count hold by ddi_find_devinfo. */ 872eca2601cSRandy Fishel if (fipe_ioat_ctrl.ioat_dev_info != NULL) { 873eca2601cSRandy Fishel ndi_rele_devi(fipe_ioat_ctrl.ioat_dev_info); 874eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_dev_info = NULL; 875eca2601cSRandy Fishel } 876eca2601cSRandy Fishel } else { 877eca2601cSRandy Fishel /* 878eca2601cSRandy Fishel * Schedule another timer to keep on trying. 879eca2601cSRandy Fishel * timeout() should always success, no need to check. 880eca2601cSRandy Fishel */ 881eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_timerid = timeout(fipe_ioat_alloc, 882eca2601cSRandy Fishel NULL, drv_usectohz(FIPE_IOAT_RETRY_INTERVAL)); 883eca2601cSRandy Fishel } 884eca2601cSRandy Fishel } 885eca2601cSRandy Fishel mutex_exit(&fipe_ioat_ctrl.ioat_lock); 886eca2601cSRandy Fishel } 887eca2601cSRandy Fishel 888eca2601cSRandy Fishel /* 889eca2601cSRandy Fishel * Free resources allocated in fipe_ioat_alloc. 890eca2601cSRandy Fishel */ 891eca2601cSRandy Fishel static void 892eca2601cSRandy Fishel fipe_ioat_free(void) 893eca2601cSRandy Fishel { 894eca2601cSRandy Fishel int idx = 0; 895eca2601cSRandy Fishel dcopy_cmd_t *cmds = fipe_ioat_ctrl.ioat_cmds; 896eca2601cSRandy Fishel 897eca2601cSRandy Fishel mutex_enter(&fipe_ioat_ctrl.ioat_lock); 898eca2601cSRandy Fishel 899eca2601cSRandy Fishel /* Cancel timeout to avoid race condition. */ 900eca2601cSRandy Fishel if (fipe_ioat_ctrl.ioat_timerid != 0) { 901eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_cancel = B_TRUE; 902eca2601cSRandy Fishel mutex_exit(&fipe_ioat_ctrl.ioat_lock); 903eca2601cSRandy Fishel (void) untimeout(fipe_ioat_ctrl.ioat_timerid); 904eca2601cSRandy Fishel mutex_enter(&fipe_ioat_ctrl.ioat_lock); 905eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_timerid = 0; 906eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_cancel = B_FALSE; 907eca2601cSRandy Fishel } 908eca2601cSRandy Fishel 909eca2601cSRandy Fishel /* Free ioat resources. */ 910eca2601cSRandy Fishel if (fipe_ioat_ctrl.ioat_ready) { 911eca2601cSRandy Fishel if (cmds[0] != NULL) { 912eca2601cSRandy Fishel dcopy_cmd_free(&cmds[0]); 913eca2601cSRandy Fishel } 914eca2601cSRandy Fishel for (idx = 1; idx <= FIPE_IOAT_CMD_NUM; idx++) { 915eca2601cSRandy Fishel if (cmds[idx] != NULL) { 916eca2601cSRandy Fishel dcopy_cmd_free(&cmds[idx]); 917eca2601cSRandy Fishel break; 918eca2601cSRandy Fishel } 919eca2601cSRandy Fishel } 920eca2601cSRandy Fishel bzero(fipe_ioat_ctrl.ioat_cmds, 921eca2601cSRandy Fishel sizeof (fipe_ioat_ctrl.ioat_cmds)); 922eca2601cSRandy Fishel dcopy_free(&fipe_ioat_ctrl.ioat_handle); 923eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_handle = NULL; 924eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_ready = B_FALSE; 925eca2601cSRandy Fishel } 926eca2601cSRandy Fishel 927eca2601cSRandy Fishel /* Release reference count hold by ddi_find_devinfo. */ 928eca2601cSRandy Fishel if (fipe_ioat_ctrl.ioat_dev_info != NULL) { 929eca2601cSRandy Fishel ndi_rele_devi(fipe_ioat_ctrl.ioat_dev_info); 930eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_dev_info = NULL; 931eca2601cSRandy Fishel } 932eca2601cSRandy Fishel 933eca2601cSRandy Fishel mutex_exit(&fipe_ioat_ctrl.ioat_lock); 934eca2601cSRandy Fishel } 935eca2601cSRandy Fishel #endif /* FIPE_IOAT_BUILTIN */ 936eca2601cSRandy Fishel 937eca2601cSRandy Fishel /* 938eca2601cSRandy Fishel * Initialize IOAT relative resources. 939eca2601cSRandy Fishel */ 940eca2601cSRandy Fishel static int 941eca2601cSRandy Fishel fipe_ioat_init(void) 942eca2601cSRandy Fishel { 943eca2601cSRandy Fishel char *buf; 944eca2601cSRandy Fishel size_t size; 945eca2601cSRandy Fishel 946eca2601cSRandy Fishel bzero(&fipe_ioat_ctrl, sizeof (fipe_ioat_ctrl)); 947eca2601cSRandy Fishel mutex_init(&fipe_ioat_ctrl.ioat_lock, NULL, MUTEX_DRIVER, NULL); 948eca2601cSRandy Fishel 949eca2601cSRandy Fishel /* 950eca2601cSRandy Fishel * Allocate memory for IOAT memory copy operation. 951eca2601cSRandy Fishel * The allocated memory should be page aligned to achieve better power 952eca2601cSRandy Fishel * savings. 953eca2601cSRandy Fishel * Don't use ddi_dma_mem_alloc here to keep thing simple. This also 954eca2601cSRandy Fishel * makes quiesce easier. 955eca2601cSRandy Fishel */ 956eca2601cSRandy Fishel size = PAGESIZE; 957eca2601cSRandy Fishel buf = kmem_zalloc(size, KM_SLEEP); 958eca2601cSRandy Fishel if ((intptr_t)buf & PAGEOFFSET) { 959eca2601cSRandy Fishel kmem_free(buf, PAGESIZE); 960eca2601cSRandy Fishel size <<= 1; 961eca2601cSRandy Fishel buf = kmem_zalloc(size, KM_SLEEP); 962eca2601cSRandy Fishel } 963eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_buf_size = size; 964eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_buf_start = buf; 965eca2601cSRandy Fishel buf = (char *)P2ROUNDUP((intptr_t)buf, PAGESIZE); 966eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_buf_virtaddr = buf; 967eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_buf_physaddr = hat_getpfnum(kas.a_hat, buf); 968eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_buf_physaddr <<= PAGESHIFT; 969eca2601cSRandy Fishel 970eca2601cSRandy Fishel #ifdef FIPE_IOAT_BUILTIN 971eca2601cSRandy Fishel { 972eca2601cSRandy Fishel uint64_t bufpa; 973eca2601cSRandy Fishel /* IOAT descriptor data structure copied from ioat.h. */ 974eca2601cSRandy Fishel struct fipe_ioat_cmd_desc { 975eca2601cSRandy Fishel uint32_t dd_size; 976eca2601cSRandy Fishel uint32_t dd_ctrl; 977eca2601cSRandy Fishel uint64_t dd_src_paddr; 978eca2601cSRandy Fishel uint64_t dd_dest_paddr; 979eca2601cSRandy Fishel uint64_t dd_next_desc; 980eca2601cSRandy Fishel uint64_t dd_res4; 981eca2601cSRandy Fishel uint64_t dd_res5; 982eca2601cSRandy Fishel uint64_t dd_res6; 983eca2601cSRandy Fishel uint64_t dd_res7; 984eca2601cSRandy Fishel } *desc; 985eca2601cSRandy Fishel 986eca2601cSRandy Fishel /* 987eca2601cSRandy Fishel * Build two IOAT command descriptors and chain them into ring. 988eca2601cSRandy Fishel * Control flags as below: 989eca2601cSRandy Fishel * 0x2: disable source snoop 990eca2601cSRandy Fishel * 0x4: disable destination snoop 991eca2601cSRandy Fishel * 0x0 << 24: memory copy operation 992eca2601cSRandy Fishel * The layout for command descriptors and memory buffers are 993eca2601cSRandy Fishel * organized for power saving effect, please don't change it. 994eca2601cSRandy Fishel */ 995eca2601cSRandy Fishel buf = fipe_ioat_ctrl.ioat_buf_virtaddr; 996eca2601cSRandy Fishel bufpa = fipe_ioat_ctrl.ioat_buf_physaddr; 997eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_cmd_physaddr = bufpa; 998eca2601cSRandy Fishel 999eca2601cSRandy Fishel /* First command descriptor. */ 1000eca2601cSRandy Fishel desc = (struct fipe_ioat_cmd_desc *)(buf); 1001eca2601cSRandy Fishel desc->dd_size = 128; 1002eca2601cSRandy Fishel desc->dd_ctrl = 0x6; 1003eca2601cSRandy Fishel desc->dd_src_paddr = bufpa + 2048; 1004eca2601cSRandy Fishel desc->dd_dest_paddr = bufpa + 3072; 1005eca2601cSRandy Fishel /* Point to second descriptor. */ 1006eca2601cSRandy Fishel desc->dd_next_desc = bufpa + 64; 1007eca2601cSRandy Fishel 1008eca2601cSRandy Fishel /* Second command descriptor. */ 1009eca2601cSRandy Fishel desc = (struct fipe_ioat_cmd_desc *)(buf + 64); 1010eca2601cSRandy Fishel desc->dd_size = 128; 1011eca2601cSRandy Fishel desc->dd_ctrl = 0x6; 1012eca2601cSRandy Fishel desc->dd_src_paddr = bufpa + 2048; 1013eca2601cSRandy Fishel desc->dd_dest_paddr = bufpa + 3072; 1014eca2601cSRandy Fishel /* Point to first descriptor. */ 1015eca2601cSRandy Fishel desc->dd_next_desc = bufpa; 1016eca2601cSRandy Fishel } 1017eca2601cSRandy Fishel #endif /* FIPE_IOAT_BUILTIN */ 1018eca2601cSRandy Fishel 1019eca2601cSRandy Fishel return (0); 1020eca2601cSRandy Fishel } 1021eca2601cSRandy Fishel 1022eca2601cSRandy Fishel static void 1023eca2601cSRandy Fishel fipe_ioat_fini(void) 1024eca2601cSRandy Fishel { 1025eca2601cSRandy Fishel /* Release reference count hold by ddi_find_devinfo. */ 1026eca2601cSRandy Fishel if (fipe_ioat_ctrl.ioat_dev_info != NULL) { 1027eca2601cSRandy Fishel ndi_rele_devi(fipe_ioat_ctrl.ioat_dev_info); 1028eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_dev_info = NULL; 1029eca2601cSRandy Fishel } 1030eca2601cSRandy Fishel 1031eca2601cSRandy Fishel if (fipe_ioat_ctrl.ioat_buf_start != NULL) { 1032eca2601cSRandy Fishel ASSERT(fipe_ioat_ctrl.ioat_buf_size != 0); 1033eca2601cSRandy Fishel kmem_free(fipe_ioat_ctrl.ioat_buf_start, 1034eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_buf_size); 1035eca2601cSRandy Fishel } 1036eca2601cSRandy Fishel 1037eca2601cSRandy Fishel mutex_destroy(&fipe_ioat_ctrl.ioat_lock); 1038eca2601cSRandy Fishel bzero(&fipe_ioat_ctrl, sizeof (fipe_ioat_ctrl)); 1039eca2601cSRandy Fishel } 1040eca2601cSRandy Fishel 1041eca2601cSRandy Fishel static int 1042eca2601cSRandy Fishel fipe_idle_start(void) 1043eca2601cSRandy Fishel { 1044eca2601cSRandy Fishel int rc; 1045eca2601cSRandy Fishel 1046eca2601cSRandy Fishel if (fipe_idle_ctrl.idle_ready) { 1047eca2601cSRandy Fishel return (0); 1048eca2601cSRandy Fishel } 1049eca2601cSRandy Fishel 1050eca2601cSRandy Fishel if (cpu_idle_prop_create_handle(CPU_IDLE_PROP_ENTER_TIMESTAMP, 1051eca2601cSRandy Fishel &fipe_idle_ctrl.prop_enter) != 0) { 1052eca2601cSRandy Fishel cmn_err(CE_WARN, "!fipe: failed to get enter_ts property."); 1053eca2601cSRandy Fishel return (-1); 1054eca2601cSRandy Fishel } 1055eca2601cSRandy Fishel if (cpu_idle_prop_create_handle(CPU_IDLE_PROP_EXIT_TIMESTAMP, 1056eca2601cSRandy Fishel &fipe_idle_ctrl.prop_exit) != 0) { 1057eca2601cSRandy Fishel cmn_err(CE_WARN, "!fipe: failed to get exit_ts property."); 1058eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_enter); 1059eca2601cSRandy Fishel return (-1); 1060eca2601cSRandy Fishel } 1061eca2601cSRandy Fishel if (cpu_idle_prop_create_handle(CPU_IDLE_PROP_TOTAL_IDLE_TIME, 1062eca2601cSRandy Fishel &fipe_idle_ctrl.prop_idle) != 0) { 1063eca2601cSRandy Fishel cmn_err(CE_WARN, "!fipe: failed to get idle_time property."); 1064eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_exit); 1065eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_enter); 1066eca2601cSRandy Fishel return (-1); 1067eca2601cSRandy Fishel } 1068eca2601cSRandy Fishel if (cpu_idle_prop_create_handle(CPU_IDLE_PROP_TOTAL_BUSY_TIME, 1069eca2601cSRandy Fishel &fipe_idle_ctrl.prop_busy) != 0) { 1070eca2601cSRandy Fishel cmn_err(CE_WARN, "!fipe: failed to get busy_time property."); 1071eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_idle); 1072eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_exit); 1073eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_enter); 1074eca2601cSRandy Fishel return (-1); 1075eca2601cSRandy Fishel } 1076eca2601cSRandy Fishel if (cpu_idle_prop_create_handle(CPU_IDLE_PROP_INTERRUPT_COUNT, 1077eca2601cSRandy Fishel &fipe_idle_ctrl.prop_intr) != 0) { 1078eca2601cSRandy Fishel cmn_err(CE_WARN, "!fipe: failed to get intr_count property."); 1079eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_busy); 1080eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_idle); 1081eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_exit); 1082eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_enter); 1083eca2601cSRandy Fishel return (-1); 1084eca2601cSRandy Fishel } 1085eca2601cSRandy Fishel 1086eca2601cSRandy Fishel /* Register idle state notification callback. */ 1087eca2601cSRandy Fishel rc = cpu_idle_register_callback(CPU_IDLE_CB_PRIO_FIPE, &fipe_idle_cb, 1088eca2601cSRandy Fishel NULL, &fipe_idle_ctrl.cb_handle); 1089eca2601cSRandy Fishel if (rc != 0) { 1090eca2601cSRandy Fishel cmn_err(CE_WARN, "!fipe: failed to register cpuidle callback."); 1091eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_intr); 1092eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_busy); 1093eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_idle); 1094eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_exit); 1095eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_enter); 1096eca2601cSRandy Fishel return (-1); 1097eca2601cSRandy Fishel } 1098eca2601cSRandy Fishel 1099eca2601cSRandy Fishel fipe_idle_ctrl.idle_ready = B_TRUE; 1100eca2601cSRandy Fishel 1101eca2601cSRandy Fishel return (0); 1102eca2601cSRandy Fishel } 1103eca2601cSRandy Fishel 1104eca2601cSRandy Fishel static int 1105eca2601cSRandy Fishel fipe_idle_stop(void) 1106eca2601cSRandy Fishel { 1107eca2601cSRandy Fishel int rc; 1108eca2601cSRandy Fishel 1109eca2601cSRandy Fishel if (fipe_idle_ctrl.idle_ready == B_FALSE) { 1110eca2601cSRandy Fishel return (0); 1111eca2601cSRandy Fishel } 1112eca2601cSRandy Fishel 1113eca2601cSRandy Fishel rc = cpu_idle_unregister_callback(fipe_idle_ctrl.cb_handle); 1114eca2601cSRandy Fishel if (rc != 0) { 1115eca2601cSRandy Fishel cmn_err(CE_WARN, 1116eca2601cSRandy Fishel "!fipe: failed to unregister cpuidle callback."); 1117eca2601cSRandy Fishel return (-1); 1118eca2601cSRandy Fishel } 1119eca2601cSRandy Fishel 1120eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_intr); 1121eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_busy); 1122eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_idle); 1123eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_exit); 1124eca2601cSRandy Fishel (void) cpu_idle_prop_destroy_handle(fipe_idle_ctrl.prop_enter); 1125eca2601cSRandy Fishel 1126eca2601cSRandy Fishel fipe_idle_ctrl.idle_ready = B_FALSE; 1127eca2601cSRandy Fishel 1128eca2601cSRandy Fishel return (0); 1129eca2601cSRandy Fishel } 1130eca2601cSRandy Fishel 1131eca2601cSRandy Fishel #ifdef FIPE_KSTAT_SUPPORT 1132eca2601cSRandy Fishel static int 1133eca2601cSRandy Fishel fipe_kstat_update(kstat_t *ksp, int rw) 1134eca2601cSRandy Fishel { 1135eca2601cSRandy Fishel struct fipe_kstat_s *sp; 1136eca2601cSRandy Fishel hrtime_t hrt; 1137eca2601cSRandy Fishel 1138eca2601cSRandy Fishel if (rw == KSTAT_WRITE) { 1139eca2601cSRandy Fishel return (EACCES); 1140eca2601cSRandy Fishel } 1141eca2601cSRandy Fishel 1142eca2601cSRandy Fishel sp = ksp->ks_data; 1143eca2601cSRandy Fishel sp->fipe_enabled.value.i32 = fipe_gbl_ctrl.pm_enabled ? 1 : 0; 1144eca2601cSRandy Fishel sp->fipe_policy.value.i32 = fipe_pm_policy; 1145eca2601cSRandy Fishel 1146eca2601cSRandy Fishel hrt = fipe_gbl_ctrl.time_in_pm; 1147eca2601cSRandy Fishel scalehrtime(&hrt); 1148eca2601cSRandy Fishel sp->fipe_pm_time.value.ui64 = (uint64_t)hrt; 1149eca2601cSRandy Fishel 1150eca2601cSRandy Fishel #ifdef FIPE_KSTAT_DETAIL 1151eca2601cSRandy Fishel sp->ioat_ready.value.i32 = fipe_ioat_ctrl.ioat_ready ? 1 : 0; 1152eca2601cSRandy Fishel #endif /* FIPE_KSTAT_DETAIL */ 1153eca2601cSRandy Fishel 1154eca2601cSRandy Fishel return (0); 1155eca2601cSRandy Fishel } 1156eca2601cSRandy Fishel #endif /* FIPE_KSTAT_SUPPORT */ 1157eca2601cSRandy Fishel 1158eca2601cSRandy Fishel /* 1159eca2601cSRandy Fishel * Initialize memory power management subsystem. 1160eca2601cSRandy Fishel * Note: This function should only be called from ATTACH. 1161eca2601cSRandy Fishel * Note: caller must ensure exclusive access to all fipe_xxx interfaces. 1162eca2601cSRandy Fishel */ 1163eca2601cSRandy Fishel int 1164eca2601cSRandy Fishel fipe_init(dev_info_t *dip) 1165eca2601cSRandy Fishel { 1166eca2601cSRandy Fishel size_t nsize; 1167eca2601cSRandy Fishel hrtime_t hrt; 1168eca2601cSRandy Fishel 1169eca2601cSRandy Fishel /* Initialize global control structure. */ 1170eca2601cSRandy Fishel bzero(&fipe_gbl_ctrl, sizeof (fipe_gbl_ctrl)); 1171eca2601cSRandy Fishel mutex_init(&fipe_gbl_ctrl.lock, NULL, MUTEX_DRIVER, NULL); 1172eca2601cSRandy Fishel 1173eca2601cSRandy Fishel /* Query power management policy from device property. */ 1174eca2601cSRandy Fishel fipe_pm_policy = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, 1175eca2601cSRandy Fishel FIPE_PROP_PM_POLICY, fipe_pm_policy); 1176eca2601cSRandy Fishel if (fipe_pm_policy < 0 || fipe_pm_policy >= FIPE_PM_POLICY_MAX) { 1177eca2601cSRandy Fishel cmn_err(CE_CONT, 1178eca2601cSRandy Fishel "?fipe: invalid power management policy %d.\n", 1179eca2601cSRandy Fishel fipe_pm_policy); 1180eca2601cSRandy Fishel fipe_pm_policy = FIPE_PM_POLICY_BALANCE; 1181eca2601cSRandy Fishel } 1182eca2601cSRandy Fishel fipe_profile_curr = &fipe_profiles[fipe_pm_policy]; 1183eca2601cSRandy Fishel 1184eca2601cSRandy Fishel /* 1185eca2601cSRandy Fishel * Compute unscaled hrtime value corresponding to FIPE_STAT_INTERVAL. 1186eca2601cSRandy Fishel * (1 << 36) should be big enough here. 1187eca2601cSRandy Fishel */ 1188eca2601cSRandy Fishel hrt = 1ULL << 36; 1189eca2601cSRandy Fishel scalehrtime(&hrt); 1190eca2601cSRandy Fishel fipe_idle_ctrl.tick_interval = FIPE_STAT_INTERVAL * (1ULL << 36) / hrt; 1191eca2601cSRandy Fishel 1192eca2601cSRandy Fishel if (fipe_mc_init(dip) != 0) { 1193eca2601cSRandy Fishel cmn_err(CE_WARN, "!fipe: failed to initialize mc state."); 1194eca2601cSRandy Fishel goto out_mc_error; 1195eca2601cSRandy Fishel } 1196eca2601cSRandy Fishel if (fipe_ioat_init() != 0) { 1197eca2601cSRandy Fishel cmn_err(CE_NOTE, "!fipe: failed to initialize ioat state."); 1198eca2601cSRandy Fishel goto out_ioat_error; 1199eca2601cSRandy Fishel } 1200eca2601cSRandy Fishel 1201eca2601cSRandy Fishel /* Allocate per-CPU structure. */ 1202eca2601cSRandy Fishel nsize = max_ncpus * sizeof (fipe_cpu_state_t); 1203eca2601cSRandy Fishel nsize += CPU_CACHE_COHERENCE_SIZE; 1204eca2601cSRandy Fishel fipe_gbl_ctrl.state_buf = kmem_zalloc(nsize, KM_SLEEP); 1205eca2601cSRandy Fishel fipe_gbl_ctrl.state_size = nsize; 1206eca2601cSRandy Fishel fipe_cpu_states = (fipe_cpu_state_t *)P2ROUNDUP( 1207eca2601cSRandy Fishel (intptr_t)fipe_gbl_ctrl.state_buf, CPU_CACHE_COHERENCE_SIZE); 1208eca2601cSRandy Fishel 1209eca2601cSRandy Fishel #ifdef FIPE_KSTAT_SUPPORT 1210eca2601cSRandy Fishel fipe_gbl_ctrl.fipe_kstat = kstat_create("fipe", 0, "fipe-pm", "misc", 1211eca2601cSRandy Fishel KSTAT_TYPE_NAMED, sizeof (fipe_kstat) / sizeof (kstat_named_t), 1212eca2601cSRandy Fishel KSTAT_FLAG_VIRTUAL); 1213eca2601cSRandy Fishel if (fipe_gbl_ctrl.fipe_kstat == NULL) { 1214eca2601cSRandy Fishel cmn_err(CE_CONT, "?fipe: failed to create kstat object.\n"); 1215eca2601cSRandy Fishel } else { 1216eca2601cSRandy Fishel fipe_gbl_ctrl.fipe_kstat->ks_lock = &fipe_gbl_ctrl.lock; 1217eca2601cSRandy Fishel fipe_gbl_ctrl.fipe_kstat->ks_data = &fipe_kstat; 1218eca2601cSRandy Fishel fipe_gbl_ctrl.fipe_kstat->ks_update = fipe_kstat_update; 1219eca2601cSRandy Fishel kstat_install(fipe_gbl_ctrl.fipe_kstat); 1220eca2601cSRandy Fishel } 1221eca2601cSRandy Fishel #endif /* FIPE_KSTAT_SUPPORT */ 1222eca2601cSRandy Fishel 1223eca2601cSRandy Fishel return (0); 1224eca2601cSRandy Fishel 1225eca2601cSRandy Fishel out_ioat_error: 1226eca2601cSRandy Fishel fipe_mc_fini(); 1227eca2601cSRandy Fishel out_mc_error: 1228eca2601cSRandy Fishel mutex_destroy(&fipe_gbl_ctrl.lock); 1229eca2601cSRandy Fishel bzero(&fipe_gbl_ctrl, sizeof (fipe_gbl_ctrl)); 1230eca2601cSRandy Fishel 1231eca2601cSRandy Fishel return (-1); 1232eca2601cSRandy Fishel } 1233eca2601cSRandy Fishel 1234eca2601cSRandy Fishel /* 1235eca2601cSRandy Fishel * Destroy memory power management subsystem. 1236eca2601cSRandy Fishel * Note: This function should only be called from DETACH. 1237eca2601cSRandy Fishel * Note: caller must ensure exclusive access to all fipe_xxx interfaces. 1238eca2601cSRandy Fishel */ 1239eca2601cSRandy Fishel int 1240eca2601cSRandy Fishel fipe_fini(void) 1241eca2601cSRandy Fishel { 1242eca2601cSRandy Fishel if (fipe_gbl_ctrl.pm_enabled) { 1243eca2601cSRandy Fishel cmn_err(CE_NOTE, "!fipe: call fipe_fini without stopping PM."); 1244eca2601cSRandy Fishel return (EBUSY); 1245eca2601cSRandy Fishel } 1246eca2601cSRandy Fishel 1247eca2601cSRandy Fishel ASSERT(!fipe_gbl_ctrl.pm_active); 1248eca2601cSRandy Fishel fipe_ioat_fini(); 1249eca2601cSRandy Fishel fipe_mc_fini(); 1250eca2601cSRandy Fishel 1251eca2601cSRandy Fishel #ifdef FIPE_KSTAT_SUPPORT 1252eca2601cSRandy Fishel if (fipe_gbl_ctrl.fipe_kstat != NULL) { 1253eca2601cSRandy Fishel kstat_delete(fipe_gbl_ctrl.fipe_kstat); 1254eca2601cSRandy Fishel fipe_gbl_ctrl.fipe_kstat = NULL; 1255eca2601cSRandy Fishel } 1256eca2601cSRandy Fishel #endif /* FIPE_KSTAT_SUPPORT */ 1257eca2601cSRandy Fishel 1258eca2601cSRandy Fishel if (fipe_gbl_ctrl.state_buf != NULL) { 1259eca2601cSRandy Fishel ASSERT(fipe_gbl_ctrl.state_size != 0); 1260eca2601cSRandy Fishel kmem_free(fipe_gbl_ctrl.state_buf, fipe_gbl_ctrl.state_size); 1261eca2601cSRandy Fishel fipe_cpu_states = NULL; 1262eca2601cSRandy Fishel } 1263eca2601cSRandy Fishel 1264eca2601cSRandy Fishel fipe_profile_curr = NULL; 1265eca2601cSRandy Fishel mutex_destroy(&fipe_gbl_ctrl.lock); 1266eca2601cSRandy Fishel bzero(&fipe_gbl_ctrl, sizeof (fipe_gbl_ctrl)); 1267eca2601cSRandy Fishel 1268eca2601cSRandy Fishel return (0); 1269eca2601cSRandy Fishel } 1270eca2601cSRandy Fishel 1271eca2601cSRandy Fishel /* 1272eca2601cSRandy Fishel * Start memory power management subsystem. 1273eca2601cSRandy Fishel * Note: caller must ensure exclusive access to all fipe_xxx interfaces. 1274eca2601cSRandy Fishel */ 1275eca2601cSRandy Fishel int 1276eca2601cSRandy Fishel fipe_start(void) 1277eca2601cSRandy Fishel { 1278eca2601cSRandy Fishel if (fipe_gbl_ctrl.pm_enabled == B_TRUE) { 1279eca2601cSRandy Fishel return (0); 1280eca2601cSRandy Fishel } 1281eca2601cSRandy Fishel 1282eca2601cSRandy Fishel bzero(fipe_cpu_states, max_ncpus * sizeof (fipe_cpu_states[0])); 1283eca2601cSRandy Fishel fipe_ioat_alloc(NULL); 1284eca2601cSRandy Fishel if (fipe_idle_start() != 0) { 1285eca2601cSRandy Fishel cmn_err(CE_NOTE, "!fipe: failed to start PM subsystem."); 1286eca2601cSRandy Fishel fipe_ioat_free(); 1287eca2601cSRandy Fishel return (-1); 1288eca2601cSRandy Fishel } 1289eca2601cSRandy Fishel 1290eca2601cSRandy Fishel fipe_gbl_ctrl.pm_enabled = B_TRUE; 1291eca2601cSRandy Fishel 1292eca2601cSRandy Fishel return (0); 1293eca2601cSRandy Fishel } 1294eca2601cSRandy Fishel 1295eca2601cSRandy Fishel /* 1296eca2601cSRandy Fishel * Stop memory power management subsystem. 1297eca2601cSRandy Fishel * Note: caller must ensure exclusive access to all fipe_xxx interfaces. 1298eca2601cSRandy Fishel */ 1299eca2601cSRandy Fishel int 1300eca2601cSRandy Fishel fipe_stop(void) 1301eca2601cSRandy Fishel { 1302eca2601cSRandy Fishel if (fipe_gbl_ctrl.pm_enabled) { 1303eca2601cSRandy Fishel if (fipe_idle_stop() != 0) { 1304eca2601cSRandy Fishel cmn_err(CE_NOTE, 1305eca2601cSRandy Fishel "!fipe: failed to stop PM subsystem."); 1306eca2601cSRandy Fishel return (-1); 1307eca2601cSRandy Fishel } 1308eca2601cSRandy Fishel fipe_ioat_free(); 1309eca2601cSRandy Fishel fipe_gbl_ctrl.pm_enabled = B_FALSE; 1310eca2601cSRandy Fishel } 1311eca2601cSRandy Fishel ASSERT(!fipe_gbl_ctrl.pm_active); 1312eca2601cSRandy Fishel 1313eca2601cSRandy Fishel return (0); 1314eca2601cSRandy Fishel } 1315eca2601cSRandy Fishel 1316eca2601cSRandy Fishel int 1317eca2601cSRandy Fishel fipe_suspend(void) 1318eca2601cSRandy Fishel { 1319eca2601cSRandy Fishel /* Save current power management policy. */ 1320eca2601cSRandy Fishel fipe_pm_policy_saved = fipe_pm_policy; 1321eca2601cSRandy Fishel /* Disable PM by setting profile to FIPE_PM_POLICY_DISABLE. */ 1322eca2601cSRandy Fishel fipe_pm_policy = FIPE_PM_POLICY_DISABLE; 1323eca2601cSRandy Fishel fipe_profile_curr = &fipe_profiles[fipe_pm_policy]; 1324eca2601cSRandy Fishel 1325eca2601cSRandy Fishel return (0); 1326eca2601cSRandy Fishel } 1327eca2601cSRandy Fishel 1328eca2601cSRandy Fishel int 1329eca2601cSRandy Fishel fipe_resume(void) 1330eca2601cSRandy Fishel { 1331eca2601cSRandy Fishel /* Restore saved power management policy. */ 1332eca2601cSRandy Fishel fipe_pm_policy = fipe_pm_policy_saved; 1333eca2601cSRandy Fishel fipe_profile_curr = &fipe_profiles[fipe_pm_policy]; 1334eca2601cSRandy Fishel 1335eca2601cSRandy Fishel return (0); 1336eca2601cSRandy Fishel } 1337eca2601cSRandy Fishel 1338eca2601cSRandy Fishel fipe_pm_policy_t 1339eca2601cSRandy Fishel fipe_get_pmpolicy(void) 1340eca2601cSRandy Fishel { 1341eca2601cSRandy Fishel return (fipe_pm_policy); 1342eca2601cSRandy Fishel } 1343eca2601cSRandy Fishel 1344eca2601cSRandy Fishel int 1345eca2601cSRandy Fishel fipe_set_pmpolicy(fipe_pm_policy_t policy) 1346eca2601cSRandy Fishel { 1347eca2601cSRandy Fishel if (policy < 0 || policy >= FIPE_PM_POLICY_MAX) { 1348eca2601cSRandy Fishel return (EINVAL); 1349eca2601cSRandy Fishel } 1350eca2601cSRandy Fishel fipe_pm_policy = policy; 1351eca2601cSRandy Fishel fipe_profile_curr = &fipe_profiles[fipe_pm_policy]; 1352eca2601cSRandy Fishel 1353eca2601cSRandy Fishel return (0); 1354eca2601cSRandy Fishel } 1355eca2601cSRandy Fishel 1356eca2601cSRandy Fishel /* 1357eca2601cSRandy Fishel * Check condition (fipe_gbl_ctrl.cpu_cnt == ncpus) to make sure that 1358eca2601cSRandy Fishel * there is other CPU trying to wake up system from memory power saving state. 1359eca2601cSRandy Fishel * If a CPU is waking up system, fipe_disable() will set 1360eca2601cSRandy Fishel * fipe_gbl_ctrl.pm_active to false as soon as possible and allow other CPU's 1361eca2601cSRandy Fishel * to continue, and it will take the responsibility to recover system from 1362eca2601cSRandy Fishel * memory power saving state. 1363eca2601cSRandy Fishel */ 1364eca2601cSRandy Fishel static void 1365eca2601cSRandy Fishel fipe_enable(int throttle, cpu_idle_check_wakeup_t check_func, void* check_arg) 1366eca2601cSRandy Fishel { 1367eca2601cSRandy Fishel extern void membar_sync(void); 1368eca2601cSRandy Fishel 1369eca2601cSRandy Fishel FIPE_KSTAT_DETAIL_INC(pm_tryenter_cnt); 1370eca2601cSRandy Fishel 1371eca2601cSRandy Fishel /* 1372eca2601cSRandy Fishel * Check CPU wakeup events. 1373eca2601cSRandy Fishel */ 1374eca2601cSRandy Fishel if (check_func != NULL) { 1375eca2601cSRandy Fishel (*check_func)(check_arg); 1376eca2601cSRandy Fishel } 1377eca2601cSRandy Fishel 1378eca2601cSRandy Fishel /* 1379eca2601cSRandy Fishel * Try to acquire mutex, which also implicitly has the same effect 1380eca2601cSRandy Fishel * of calling membar_sync(). 1381eca2601cSRandy Fishel * If mutex_tryenter fails, that means other CPU is waking up. 1382eca2601cSRandy Fishel */ 1383eca2601cSRandy Fishel if (mutex_tryenter(&fipe_gbl_ctrl.lock) == 0) { 1384eca2601cSRandy Fishel FIPE_KSTAT_DETAIL_INC(pm_race_cnt); 1385eca2601cSRandy Fishel /* 1386eca2601cSRandy Fishel * Handle a special race condition for the case that a CPU wakes 1387eca2601cSRandy Fishel * and then enters into idle state within a short period. 1388eca2601cSRandy Fishel * This case can't be reliably detected by cpu_count mechanism. 1389eca2601cSRandy Fishel */ 1390eca2601cSRandy Fishel } else if (fipe_gbl_ctrl.pm_active) { 1391eca2601cSRandy Fishel FIPE_KSTAT_DETAIL_INC(pm_race_cnt); 1392eca2601cSRandy Fishel mutex_exit(&fipe_gbl_ctrl.lock); 1393eca2601cSRandy Fishel } else { 1394eca2601cSRandy Fishel fipe_gbl_ctrl.pm_active = B_TRUE; 1395eca2601cSRandy Fishel membar_sync(); 1396eca2601cSRandy Fishel if (fipe_gbl_ctrl.cpu_count != ncpus) { 1397eca2601cSRandy Fishel FIPE_KSTAT_DETAIL_INC(pm_race_cnt); 1398eca2601cSRandy Fishel fipe_gbl_ctrl.pm_active = B_FALSE; 1399eca2601cSRandy Fishel } else if (fipe_ioat_trigger() != 0) { 1400eca2601cSRandy Fishel fipe_gbl_ctrl.pm_active = B_FALSE; 1401eca2601cSRandy Fishel } else if (fipe_gbl_ctrl.cpu_count != ncpus || 1402eca2601cSRandy Fishel fipe_mc_change(throttle) != 0) { 1403eca2601cSRandy Fishel fipe_gbl_ctrl.pm_active = B_FALSE; 1404eca2601cSRandy Fishel fipe_ioat_cancel(); 1405eca2601cSRandy Fishel if (fipe_gbl_ctrl.cpu_count != ncpus) { 1406eca2601cSRandy Fishel FIPE_KSTAT_DETAIL_INC(pm_race_cnt); 1407eca2601cSRandy Fishel } 1408eca2601cSRandy Fishel } else if (fipe_gbl_ctrl.cpu_count != ncpus) { 1409eca2601cSRandy Fishel fipe_gbl_ctrl.pm_active = B_FALSE; 1410eca2601cSRandy Fishel fipe_mc_restore(); 1411eca2601cSRandy Fishel fipe_ioat_cancel(); 1412eca2601cSRandy Fishel FIPE_KSTAT_DETAIL_INC(pm_race_cnt); 1413eca2601cSRandy Fishel } else { 1414eca2601cSRandy Fishel FIPE_KSTAT_DETAIL_INC(pm_success_cnt); 1415eca2601cSRandy Fishel } 1416eca2601cSRandy Fishel mutex_exit(&fipe_gbl_ctrl.lock); 1417eca2601cSRandy Fishel } 1418eca2601cSRandy Fishel } 1419eca2601cSRandy Fishel 1420eca2601cSRandy Fishel static void 1421eca2601cSRandy Fishel fipe_disable(void) 1422eca2601cSRandy Fishel { 1423eca2601cSRandy Fishel /* 1424eca2601cSRandy Fishel * Try to acquire lock, which also implicitly has the same effect 1425eca2601cSRandy Fishel * of calling membar_sync(). 1426eca2601cSRandy Fishel */ 1427eca2601cSRandy Fishel while (mutex_tryenter(&fipe_gbl_ctrl.lock) == 0) { 1428eca2601cSRandy Fishel /* 1429eca2601cSRandy Fishel * If power saving is inactive, just return and all dirty 1430eca2601cSRandy Fishel * house-keeping work will be handled in fipe_enable(). 1431eca2601cSRandy Fishel */ 1432eca2601cSRandy Fishel if (fipe_gbl_ctrl.pm_active == B_FALSE) { 1433eca2601cSRandy Fishel return; 1434eca2601cSRandy Fishel } else { 1435eca2601cSRandy Fishel (void) SMT_PAUSE(); 1436eca2601cSRandy Fishel } 1437eca2601cSRandy Fishel } 1438eca2601cSRandy Fishel 1439eca2601cSRandy Fishel /* Disable power saving if it's active. */ 1440eca2601cSRandy Fishel if (fipe_gbl_ctrl.pm_active) { 1441eca2601cSRandy Fishel /* 1442eca2601cSRandy Fishel * Set pm_active to FALSE as soon as possible to prevent 1443eca2601cSRandy Fishel * other CPUs from waiting on pm_active flag. 1444eca2601cSRandy Fishel */ 1445eca2601cSRandy Fishel fipe_gbl_ctrl.pm_active = B_FALSE; 1446eca2601cSRandy Fishel membar_producer(); 1447eca2601cSRandy Fishel fipe_mc_restore(); 1448eca2601cSRandy Fishel fipe_ioat_cancel(); 1449eca2601cSRandy Fishel } 1450eca2601cSRandy Fishel 1451eca2601cSRandy Fishel mutex_exit(&fipe_gbl_ctrl.lock); 1452eca2601cSRandy Fishel } 1453eca2601cSRandy Fishel 1454eca2601cSRandy Fishel /*ARGSUSED*/ 1455eca2601cSRandy Fishel static boolean_t 1456eca2601cSRandy Fishel fipe_check_cpu(struct fipe_cpu_state *sp, cpu_idle_callback_context_t ctx, 1457eca2601cSRandy Fishel hrtime_t ts) 1458eca2601cSRandy Fishel { 1459eca2601cSRandy Fishel if (cpu_flagged_offline(CPU->cpu_flags)) { 1460eca2601cSRandy Fishel /* Treat CPU in offline state as ready. */ 1461eca2601cSRandy Fishel sp->cond_ready = B_TRUE; 1462eca2601cSRandy Fishel return (B_TRUE); 1463eca2601cSRandy Fishel } else if (sp->next_ts <= ts) { 1464eca2601cSRandy Fishel uint64_t intr; 1465eca2601cSRandy Fishel hrtime_t idle, busy, diff; 1466eca2601cSRandy Fishel cpu_idle_prop_value_t val; 1467eca2601cSRandy Fishel 1468eca2601cSRandy Fishel /* Set default value. */ 1469eca2601cSRandy Fishel sp->cond_ready = B_TRUE; 1470eca2601cSRandy Fishel sp->idle_count = 0; 1471eca2601cSRandy Fishel 1472eca2601cSRandy Fishel /* Calculate idle percent. */ 1473eca2601cSRandy Fishel idle = sp->last_idle; 1474eca2601cSRandy Fishel sp->last_idle = cpu_idle_prop_get_hrtime( 1475eca2601cSRandy Fishel fipe_idle_ctrl.prop_idle, ctx); 1476eca2601cSRandy Fishel idle = sp->last_idle - idle; 1477eca2601cSRandy Fishel busy = sp->last_busy; 1478eca2601cSRandy Fishel sp->last_busy = cpu_idle_prop_get_hrtime( 1479eca2601cSRandy Fishel fipe_idle_ctrl.prop_busy, ctx); 1480eca2601cSRandy Fishel busy = sp->last_busy - busy; 1481eca2601cSRandy Fishel /* Check idle condition. */ 1482eca2601cSRandy Fishel if (idle > 0 && busy > 0) { 1483eca2601cSRandy Fishel if (busy * (100 - FIPE_PROF_BUSY_THRESHOLD) > 1484eca2601cSRandy Fishel idle * FIPE_PROF_BUSY_THRESHOLD) { 1485eca2601cSRandy Fishel FIPE_KSTAT_DETAIL_INC(cpu_busy_cnt); 1486eca2601cSRandy Fishel sp->cond_ready = B_FALSE; 1487eca2601cSRandy Fishel } else { 1488eca2601cSRandy Fishel FIPE_KSTAT_DETAIL_INC(cpu_idle_cnt); 1489eca2601cSRandy Fishel } 1490eca2601cSRandy Fishel } else { 1491eca2601cSRandy Fishel FIPE_KSTAT_DETAIL_INC(cpu_busy_cnt); 1492eca2601cSRandy Fishel sp->cond_ready = B_FALSE; 1493eca2601cSRandy Fishel } 1494eca2601cSRandy Fishel 1495eca2601cSRandy Fishel /* Calculate interrupt count. */ 1496eca2601cSRandy Fishel diff = sp->next_ts; 1497eca2601cSRandy Fishel sp->next_ts = ts + fipe_idle_ctrl.tick_interval; 1498eca2601cSRandy Fishel diff = sp->next_ts - diff; 1499eca2601cSRandy Fishel intr = sp->last_intr; 1500eca2601cSRandy Fishel if (cpu_idle_prop_get_value(fipe_idle_ctrl.prop_intr, ctx, 1501eca2601cSRandy Fishel &val) == 0) { 1502eca2601cSRandy Fishel sp->last_intr = val.cipv_uint64; 1503eca2601cSRandy Fishel intr = sp->last_intr - intr; 1504eca2601cSRandy Fishel if (diff != 0) { 1505eca2601cSRandy Fishel intr = intr * fipe_idle_ctrl.tick_interval; 1506eca2601cSRandy Fishel intr /= diff; 1507eca2601cSRandy Fishel } else { 1508eca2601cSRandy Fishel intr = FIPE_PROF_INTR_THRESHOLD; 1509eca2601cSRandy Fishel } 1510eca2601cSRandy Fishel } else { 1511eca2601cSRandy Fishel intr = FIPE_PROF_INTR_THRESHOLD; 1512eca2601cSRandy Fishel } 1513eca2601cSRandy Fishel 1514eca2601cSRandy Fishel /* 1515eca2601cSRandy Fishel * System is busy with interrupts, so disable all PM 1516eca2601cSRandy Fishel * status checks for INTR_BUSY_THROTTLE ticks. 1517eca2601cSRandy Fishel * Interrupts are disabled when FIPE callbacks are called, 1518eca2601cSRandy Fishel * so this optimization will help to reduce interrupt 1519eca2601cSRandy Fishel * latency. 1520eca2601cSRandy Fishel */ 1521eca2601cSRandy Fishel if (intr >= FIPE_PROF_INTR_BUSY_THRESHOLD) { 1522eca2601cSRandy Fishel FIPE_KSTAT_DETAIL_INC(cpu_intr_busy_cnt); 1523eca2601cSRandy Fishel sp->throttle_ts = ts + FIPE_PROF_INTR_BUSY_THROTTLE * 1524eca2601cSRandy Fishel fipe_idle_ctrl.tick_interval; 1525eca2601cSRandy Fishel sp->cond_ready = B_FALSE; 1526eca2601cSRandy Fishel } else if (intr >= FIPE_PROF_INTR_THRESHOLD) { 1527eca2601cSRandy Fishel FIPE_KSTAT_DETAIL_INC(cpu_intr_throttle_cnt); 1528eca2601cSRandy Fishel sp->cond_ready = B_FALSE; 1529eca2601cSRandy Fishel } 1530eca2601cSRandy Fishel } else if (++sp->idle_count >= FIPE_PROF_IDLE_COUNT) { 1531eca2601cSRandy Fishel /* Too many idle enter/exit in this tick. */ 1532eca2601cSRandy Fishel FIPE_KSTAT_DETAIL_INC(cpu_loop_cnt); 1533eca2601cSRandy Fishel sp->throttle_ts = sp->next_ts + fipe_idle_ctrl.tick_interval; 1534eca2601cSRandy Fishel sp->idle_count = 0; 1535eca2601cSRandy Fishel sp->cond_ready = B_FALSE; 1536eca2601cSRandy Fishel return (B_FALSE); 1537eca2601cSRandy Fishel } 1538eca2601cSRandy Fishel 1539eca2601cSRandy Fishel return (sp->cond_ready); 1540eca2601cSRandy Fishel } 1541eca2601cSRandy Fishel 1542eca2601cSRandy Fishel /*ARGSUSED*/ 1543eca2601cSRandy Fishel static void 1544eca2601cSRandy Fishel fipe_idle_enter(void *arg, cpu_idle_callback_context_t ctx, 1545eca2601cSRandy Fishel cpu_idle_check_wakeup_t check_func, void* check_arg) 1546eca2601cSRandy Fishel { 1547eca2601cSRandy Fishel hrtime_t ts; 1548eca2601cSRandy Fishel uint32_t cnt; 1549eca2601cSRandy Fishel uint64_t iowait; 1550eca2601cSRandy Fishel cpu_t *cp = CPU; 1551eca2601cSRandy Fishel struct fipe_cpu_state *sp; 1552eca2601cSRandy Fishel 1553eca2601cSRandy Fishel sp = &fipe_cpu_states[cp->cpu_id]; 1554eca2601cSRandy Fishel ts = cpu_idle_prop_get_hrtime(fipe_idle_ctrl.prop_enter, ctx); 1555eca2601cSRandy Fishel 1556eca2601cSRandy Fishel if (fipe_pm_policy != FIPE_PM_POLICY_DISABLE && 1557eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_ready && 1558eca2601cSRandy Fishel sp->state_ready && sp->throttle_ts <= ts) { 1559eca2601cSRandy Fishel /* Adjust iowait count for local CPU. */ 1560eca2601cSRandy Fishel iowait = CPU_STATS(cp, sys.iowait); 1561eca2601cSRandy Fishel if (iowait != sp->last_iowait) { 1562eca2601cSRandy Fishel atomic_add_64(&fipe_gbl_ctrl.io_waiters, 1563eca2601cSRandy Fishel iowait - sp->last_iowait); 1564eca2601cSRandy Fishel sp->last_iowait = iowait; 1565eca2601cSRandy Fishel } 1566eca2601cSRandy Fishel 1567eca2601cSRandy Fishel /* Check current CPU status. */ 1568eca2601cSRandy Fishel if (fipe_check_cpu(sp, ctx, ts)) { 1569eca2601cSRandy Fishel /* Increase count of CPU ready for power saving. */ 1570eca2601cSRandy Fishel do { 1571eca2601cSRandy Fishel cnt = fipe_gbl_ctrl.cpu_count; 1572eca2601cSRandy Fishel ASSERT(cnt < ncpus); 1573eca2601cSRandy Fishel } while (atomic_cas_32(&fipe_gbl_ctrl.cpu_count, 1574eca2601cSRandy Fishel cnt, cnt + 1) != cnt); 1575eca2601cSRandy Fishel 1576eca2601cSRandy Fishel /* 1577eca2601cSRandy Fishel * Enable power saving if all CPUs are idle. 1578eca2601cSRandy Fishel */ 1579eca2601cSRandy Fishel if (cnt + 1 == ncpus) { 1580eca2601cSRandy Fishel if (fipe_gbl_ctrl.io_waiters == 0) { 1581eca2601cSRandy Fishel fipe_gbl_ctrl.enter_ts = ts; 1582eca2601cSRandy Fishel fipe_enable(fipe_pm_throttle_level, 1583eca2601cSRandy Fishel check_func, check_arg); 1584eca2601cSRandy Fishel /* There are ongoing block io operations. */ 1585eca2601cSRandy Fishel } else { 1586eca2601cSRandy Fishel FIPE_KSTAT_DETAIL_INC(bio_busy_cnt); 1587eca2601cSRandy Fishel } 1588eca2601cSRandy Fishel } 1589eca2601cSRandy Fishel } 1590eca2601cSRandy Fishel } else if (fipe_pm_policy == FIPE_PM_POLICY_DISABLE || 1591eca2601cSRandy Fishel fipe_ioat_ctrl.ioat_ready == B_FALSE) { 1592eca2601cSRandy Fishel if (sp->cond_ready == B_TRUE) { 1593eca2601cSRandy Fishel sp->cond_ready = B_FALSE; 1594eca2601cSRandy Fishel } 1595eca2601cSRandy Fishel } else if (sp->state_ready == B_FALSE) { 1596eca2601cSRandy Fishel sp->cond_ready = B_FALSE; 1597eca2601cSRandy Fishel sp->state_ready = B_TRUE; 1598eca2601cSRandy Fishel sp->throttle_ts = 0; 1599eca2601cSRandy Fishel sp->next_ts = ts + fipe_idle_ctrl.tick_interval; 1600eca2601cSRandy Fishel sp->last_busy = cpu_idle_prop_get_hrtime( 1601eca2601cSRandy Fishel fipe_idle_ctrl.prop_busy, ctx); 1602eca2601cSRandy Fishel sp->last_idle = cpu_idle_prop_get_hrtime( 1603eca2601cSRandy Fishel fipe_idle_ctrl.prop_idle, ctx); 1604eca2601cSRandy Fishel sp->last_intr = cpu_idle_prop_get_hrtime( 1605eca2601cSRandy Fishel fipe_idle_ctrl.prop_intr, ctx); 1606eca2601cSRandy Fishel sp->idle_count = 0; 1607eca2601cSRandy Fishel } 1608eca2601cSRandy Fishel } 1609eca2601cSRandy Fishel 1610eca2601cSRandy Fishel /*ARGSUSED*/ 1611eca2601cSRandy Fishel static void 1612eca2601cSRandy Fishel fipe_idle_exit(void* arg, cpu_idle_callback_context_t ctx, int flags) 1613eca2601cSRandy Fishel { 1614eca2601cSRandy Fishel uint32_t cnt; 1615eca2601cSRandy Fishel hrtime_t ts; 1616eca2601cSRandy Fishel struct fipe_cpu_state *sp; 1617eca2601cSRandy Fishel 1618eca2601cSRandy Fishel sp = &fipe_cpu_states[CPU->cpu_id]; 1619eca2601cSRandy Fishel if (sp->cond_ready) { 1620eca2601cSRandy Fishel do { 1621eca2601cSRandy Fishel cnt = fipe_gbl_ctrl.cpu_count; 1622eca2601cSRandy Fishel ASSERT(cnt > 0); 1623eca2601cSRandy Fishel } while (atomic_cas_32(&fipe_gbl_ctrl.cpu_count, 1624eca2601cSRandy Fishel cnt, cnt - 1) != cnt); 1625eca2601cSRandy Fishel 1626eca2601cSRandy Fishel /* 1627eca2601cSRandy Fishel * Try to disable power saving state. 1628eca2601cSRandy Fishel * Only the first CPU waking from idle state will try to 1629eca2601cSRandy Fishel * disable power saving state, all other CPUs will just go 1630eca2601cSRandy Fishel * on and not try to wait for memory to recover from power 1631eca2601cSRandy Fishel * saving state. 1632eca2601cSRandy Fishel * So there are possible periods during which some CPUs are in 1633eca2601cSRandy Fishel * active state but memory is in power saving state. 1634eca2601cSRandy Fishel * This is OK, since it is an uncommon case, and it is 1635eca2601cSRandy Fishel * better for performance to let them continue as their 1636eca2601cSRandy Fishel * blocking latency is smaller than a mutex, and is only 1637eca2601cSRandy Fishel * hit in the uncommon condition. 1638eca2601cSRandy Fishel */ 1639eca2601cSRandy Fishel if (cnt == ncpus) { 1640eca2601cSRandy Fishel fipe_disable(); 1641eca2601cSRandy Fishel ts = cpu_idle_prop_get_hrtime(fipe_idle_ctrl.prop_exit, 1642eca2601cSRandy Fishel ctx); 1643eca2601cSRandy Fishel fipe_gbl_ctrl.time_in_pm += ts - fipe_gbl_ctrl.enter_ts; 1644eca2601cSRandy Fishel } 1645eca2601cSRandy Fishel } 1646eca2601cSRandy Fishel } 1647