xref: /illumos-gate/usr/src/uts/i86pc/io/fipe/fipe_pm.c (revision 90b1de135fcfa7ce4adc9138a885aa94bbcef04f)
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
fipe_mc_change(int throttle)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
fipe_mc_restore(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
fipe_mc_init(dev_info_t * dip)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
fipe_mc_fini(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
fipe_search_ioat_dev(dev_info_t * dip,void * arg)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
fipe_ioat_trigger(void)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
fipe_ioat_cancel(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
fipe_ioat_alloc(void * arg)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
fipe_ioat_free(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
fipe_ioat_trigger(void)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
fipe_ioat_cancel(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
fipe_ioat_alloc(void * arg)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
fipe_ioat_free(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
fipe_ioat_init(void)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
fipe_ioat_fini(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
fipe_idle_start(void)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
fipe_idle_stop(void)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
fipe_kstat_update(kstat_t * ksp,int rw)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
fipe_init(dev_info_t * dip)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
fipe_fini(void)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
fipe_start(void)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
fipe_stop(void)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
fipe_suspend(void)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
fipe_resume(void)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
fipe_get_pmpolicy(void)1339eca2601cSRandy Fishel fipe_get_pmpolicy(void)
1340eca2601cSRandy Fishel {
1341eca2601cSRandy Fishel 	return (fipe_pm_policy);
1342eca2601cSRandy Fishel }
1343eca2601cSRandy Fishel 
1344eca2601cSRandy Fishel int
fipe_set_pmpolicy(fipe_pm_policy_t policy)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
fipe_enable(int throttle,cpu_idle_check_wakeup_t check_func,void * check_arg)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
fipe_disable(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
fipe_check_cpu(struct fipe_cpu_state * sp,cpu_idle_callback_context_t ctx,hrtime_t ts)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
fipe_idle_enter(void * arg,cpu_idle_callback_context_t ctx,cpu_idle_check_wakeup_t check_func,void * check_arg)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
fipe_idle_exit(void * arg,cpu_idle_callback_context_t ctx,int flags)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