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