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