]>
Commit | Line | Data |
---|---|---|
d4413732 | 1 | /* |
6852fd9b | 2 | * @file op_model_amd.c |
bd87f1f0 | 3 | * athlon / K7 / K8 / Family 10h model-specific MSR operations |
1da177e4 | 4 | * |
ae735e99 | 5 | * @remark Copyright 2002-2009 OProfile authors |
1da177e4 LT |
6 | * @remark Read the file COPYING |
7 | * | |
8 | * @author John Levon | |
9 | * @author Philippe Elie | |
10 | * @author Graydon Hoare | |
adf5ec0b | 11 | * @author Robert Richter <robert.richter@amd.com> |
56784f11 | 12 | * @author Barry Kasindorf |
ae735e99 | 13 | */ |
1da177e4 LT |
14 | |
15 | #include <linux/oprofile.h> | |
56784f11 BK |
16 | #include <linux/device.h> |
17 | #include <linux/pci.h> | |
18 | ||
1da177e4 LT |
19 | #include <asm/ptrace.h> |
20 | #include <asm/msr.h> | |
3e4ff115 | 21 | #include <asm/nmi.h> |
d4413732 | 22 | |
1da177e4 LT |
23 | #include "op_x86_model.h" |
24 | #include "op_counter.h" | |
25 | ||
4c168eaf RR |
26 | #define NUM_COUNTERS 4 |
27 | #define NUM_CONTROLS 4 | |
3370d358 | 28 | #define OP_EVENT_MASK 0x0FFF |
42399adb | 29 | #define OP_CTR_OVERFLOW (1ULL<<31) |
3370d358 RR |
30 | |
31 | #define MSR_AMD_EVENTSEL_RESERVED ((0xFFFFFCF0ULL<<32)|(1ULL<<21)) | |
1da177e4 | 32 | |
852402cc RR |
33 | static unsigned long reset_value[NUM_COUNTERS]; |
34 | ||
35 | #ifdef CONFIG_OPROFILE_IBS | |
36 | ||
87f0bacc | 37 | /* IbsFetchCtl bits/masks */ |
c572ae4e RR |
38 | #define IBS_FETCH_RAND_EN (1ULL<<57) |
39 | #define IBS_FETCH_VAL (1ULL<<49) | |
40 | #define IBS_FETCH_ENABLE (1ULL<<48) | |
41 | #define IBS_FETCH_CNT_MASK 0xFFFF0000ULL | |
56784f11 | 42 | |
87f0bacc | 43 | /*IbsOpCtl bits */ |
c572ae4e RR |
44 | #define IBS_OP_CNT_CTL (1ULL<<19) |
45 | #define IBS_OP_VAL (1ULL<<18) | |
46 | #define IBS_OP_ENABLE (1ULL<<17) | |
56784f11 | 47 | |
c572ae4e RR |
48 | #define IBS_FETCH_SIZE 6 |
49 | #define IBS_OP_SIZE 12 | |
56784f11 | 50 | |
fc81be8c | 51 | static int has_ibs; /* AMD Family10h and later */ |
56784f11 BK |
52 | |
53 | struct op_ibs_config { | |
54 | unsigned long op_enabled; | |
55 | unsigned long fetch_enabled; | |
56 | unsigned long max_cnt_fetch; | |
57 | unsigned long max_cnt_op; | |
58 | unsigned long rand_en; | |
59 | unsigned long dispatched_ops; | |
60 | }; | |
61 | ||
62 | static struct op_ibs_config ibs_config; | |
d4413732 | 63 | |
852402cc RR |
64 | #endif |
65 | ||
6657fe4f | 66 | /* functions for op_amd_spec */ |
dfa15428 | 67 | |
6657fe4f | 68 | static void op_amd_fill_in_addresses(struct op_msrs * const msrs) |
1da177e4 | 69 | { |
cb9c448c DZ |
70 | int i; |
71 | ||
d4413732 | 72 | for (i = 0; i < NUM_COUNTERS; i++) { |
4c168eaf RR |
73 | if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i)) |
74 | msrs->counters[i].addr = MSR_K7_PERFCTR0 + i; | |
cb9c448c DZ |
75 | else |
76 | msrs->counters[i].addr = 0; | |
77 | } | |
78 | ||
d4413732 | 79 | for (i = 0; i < NUM_CONTROLS; i++) { |
4c168eaf RR |
80 | if (reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i)) |
81 | msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i; | |
cb9c448c DZ |
82 | else |
83 | msrs->controls[i].addr = 0; | |
84 | } | |
1da177e4 LT |
85 | } |
86 | ||
ef8828dd RR |
87 | static void op_amd_setup_ctrs(struct op_x86_model_spec const *model, |
88 | struct op_msrs const * const msrs) | |
1da177e4 | 89 | { |
3370d358 | 90 | u64 val; |
1da177e4 | 91 | int i; |
d4413732 | 92 | |
1da177e4 | 93 | /* clear all counters */ |
4c168eaf | 94 | for (i = 0 ; i < NUM_CONTROLS; ++i) { |
217d3cfb | 95 | if (unlikely(!msrs->controls[i].addr)) |
cb9c448c | 96 | continue; |
3370d358 RR |
97 | rdmsrl(msrs->controls[i].addr, val); |
98 | val &= model->reserved; | |
99 | wrmsrl(msrs->controls[i].addr, val); | |
1da177e4 | 100 | } |
cb9c448c | 101 | |
1da177e4 | 102 | /* avoid a false detection of ctr overflows in NMI handler */ |
4c168eaf | 103 | for (i = 0; i < NUM_COUNTERS; ++i) { |
217d3cfb | 104 | if (unlikely(!msrs->counters[i].addr)) |
cb9c448c | 105 | continue; |
bbc5986d | 106 | wrmsrl(msrs->counters[i].addr, -1LL); |
1da177e4 LT |
107 | } |
108 | ||
109 | /* enable active counters */ | |
4c168eaf | 110 | for (i = 0; i < NUM_COUNTERS; ++i) { |
217d3cfb | 111 | if (counter_config[i].enabled && msrs->counters[i].addr) { |
4c168eaf | 112 | reset_value[i] = counter_config[i].count; |
bbc5986d RR |
113 | wrmsrl(msrs->counters[i].addr, |
114 | -(s64)counter_config[i].count); | |
3370d358 RR |
115 | rdmsrl(msrs->controls[i].addr, val); |
116 | val &= model->reserved; | |
117 | val |= op_x86_get_ctrl(model, &counter_config[i]); | |
118 | wrmsrl(msrs->controls[i].addr, val); | |
4c168eaf RR |
119 | } else { |
120 | reset_value[i] = 0; | |
1da177e4 LT |
121 | } |
122 | } | |
123 | } | |
124 | ||
852402cc RR |
125 | #ifdef CONFIG_OPROFILE_IBS |
126 | ||
7939d2bf RR |
127 | static inline int |
128 | op_amd_handle_ibs(struct pt_regs * const regs, | |
129 | struct op_msrs const * const msrs) | |
1da177e4 | 130 | { |
c572ae4e | 131 | u64 val, ctl; |
1acda878 | 132 | struct op_entry entry; |
1da177e4 | 133 | |
fc81be8c | 134 | if (!has_ibs) |
21e70878 | 135 | return 0; |
1da177e4 | 136 | |
7939d2bf | 137 | if (ibs_config.fetch_enabled) { |
c572ae4e RR |
138 | rdmsrl(MSR_AMD64_IBSFETCHCTL, ctl); |
139 | if (ctl & IBS_FETCH_VAL) { | |
140 | rdmsrl(MSR_AMD64_IBSFETCHLINAD, val); | |
141 | oprofile_write_reserve(&entry, regs, val, | |
14f0ca8e | 142 | IBS_FETCH_CODE, IBS_FETCH_SIZE); |
51563a0e RR |
143 | oprofile_add_data64(&entry, val); |
144 | oprofile_add_data64(&entry, ctl); | |
c572ae4e | 145 | rdmsrl(MSR_AMD64_IBSFETCHPHYSAD, val); |
51563a0e | 146 | oprofile_add_data64(&entry, val); |
14f0ca8e | 147 | oprofile_write_commit(&entry); |
56784f11 | 148 | |
fd13f6c8 | 149 | /* reenable the IRQ */ |
c572ae4e RR |
150 | ctl &= ~(IBS_FETCH_VAL | IBS_FETCH_CNT_MASK); |
151 | ctl |= IBS_FETCH_ENABLE; | |
152 | wrmsrl(MSR_AMD64_IBSFETCHCTL, ctl); | |
56784f11 BK |
153 | } |
154 | } | |
155 | ||
7939d2bf | 156 | if (ibs_config.op_enabled) { |
c572ae4e RR |
157 | rdmsrl(MSR_AMD64_IBSOPCTL, ctl); |
158 | if (ctl & IBS_OP_VAL) { | |
159 | rdmsrl(MSR_AMD64_IBSOPRIP, val); | |
160 | oprofile_write_reserve(&entry, regs, val, | |
14f0ca8e | 161 | IBS_OP_CODE, IBS_OP_SIZE); |
51563a0e | 162 | oprofile_add_data64(&entry, val); |
c572ae4e | 163 | rdmsrl(MSR_AMD64_IBSOPDATA, val); |
51563a0e | 164 | oprofile_add_data64(&entry, val); |
c572ae4e | 165 | rdmsrl(MSR_AMD64_IBSOPDATA2, val); |
51563a0e | 166 | oprofile_add_data64(&entry, val); |
c572ae4e | 167 | rdmsrl(MSR_AMD64_IBSOPDATA3, val); |
51563a0e | 168 | oprofile_add_data64(&entry, val); |
c572ae4e | 169 | rdmsrl(MSR_AMD64_IBSDCLINAD, val); |
51563a0e | 170 | oprofile_add_data64(&entry, val); |
c572ae4e | 171 | rdmsrl(MSR_AMD64_IBSDCPHYSAD, val); |
51563a0e | 172 | oprofile_add_data64(&entry, val); |
14f0ca8e | 173 | oprofile_write_commit(&entry); |
56784f11 BK |
174 | |
175 | /* reenable the IRQ */ | |
c572ae4e RR |
176 | ctl &= ~IBS_OP_VAL & 0xFFFFFFFF; |
177 | ctl |= IBS_OP_ENABLE; | |
178 | wrmsrl(MSR_AMD64_IBSOPCTL, ctl); | |
56784f11 BK |
179 | } |
180 | } | |
181 | ||
1da177e4 LT |
182 | return 1; |
183 | } | |
184 | ||
90637595 RR |
185 | static inline void op_amd_start_ibs(void) |
186 | { | |
c572ae4e | 187 | u64 val; |
90637595 | 188 | if (has_ibs && ibs_config.fetch_enabled) { |
c572ae4e RR |
189 | val = (ibs_config.max_cnt_fetch >> 4) & 0xFFFF; |
190 | val |= ibs_config.rand_en ? IBS_FETCH_RAND_EN : 0; | |
191 | val |= IBS_FETCH_ENABLE; | |
192 | wrmsrl(MSR_AMD64_IBSFETCHCTL, val); | |
90637595 RR |
193 | } |
194 | ||
195 | if (has_ibs && ibs_config.op_enabled) { | |
c572ae4e RR |
196 | val = (ibs_config.max_cnt_op >> 4) & 0xFFFF; |
197 | val |= ibs_config.dispatched_ops ? IBS_OP_CNT_CTL : 0; | |
198 | val |= IBS_OP_ENABLE; | |
199 | wrmsrl(MSR_AMD64_IBSOPCTL, val); | |
90637595 RR |
200 | } |
201 | } | |
202 | ||
203 | static void op_amd_stop_ibs(void) | |
204 | { | |
c572ae4e | 205 | if (has_ibs && ibs_config.fetch_enabled) |
90637595 | 206 | /* clear max count and enable */ |
c572ae4e | 207 | wrmsrl(MSR_AMD64_IBSFETCHCTL, 0); |
90637595 | 208 | |
c572ae4e | 209 | if (has_ibs && ibs_config.op_enabled) |
90637595 | 210 | /* clear max count and enable */ |
c572ae4e | 211 | wrmsrl(MSR_AMD64_IBSOPCTL, 0); |
90637595 RR |
212 | } |
213 | ||
214 | #else | |
215 | ||
216 | static inline int op_amd_handle_ibs(struct pt_regs * const regs, | |
21e70878 JSR |
217 | struct op_msrs const * const msrs) |
218 | { | |
219 | return 0; | |
220 | } | |
90637595 RR |
221 | static inline void op_amd_start_ibs(void) { } |
222 | static inline void op_amd_stop_ibs(void) { } | |
223 | ||
852402cc RR |
224 | #endif |
225 | ||
7939d2bf RR |
226 | static int op_amd_check_ctrs(struct pt_regs * const regs, |
227 | struct op_msrs const * const msrs) | |
228 | { | |
42399adb | 229 | u64 val; |
7939d2bf RR |
230 | int i; |
231 | ||
4c168eaf RR |
232 | for (i = 0 ; i < NUM_COUNTERS; ++i) { |
233 | if (!reset_value[i]) | |
7939d2bf | 234 | continue; |
42399adb RR |
235 | rdmsrl(msrs->counters[i].addr, val); |
236 | /* bit is clear if overflowed: */ | |
237 | if (val & OP_CTR_OVERFLOW) | |
238 | continue; | |
239 | oprofile_add_sample(regs, i); | |
bbc5986d | 240 | wrmsrl(msrs->counters[i].addr, -(s64)reset_value[i]); |
7939d2bf RR |
241 | } |
242 | ||
243 | op_amd_handle_ibs(regs, msrs); | |
244 | ||
245 | /* See op_model_ppro.c */ | |
246 | return 1; | |
247 | } | |
d4413732 | 248 | |
6657fe4f | 249 | static void op_amd_start(struct op_msrs const * const msrs) |
1da177e4 | 250 | { |
dea3766c | 251 | u64 val; |
1da177e4 | 252 | int i; |
4c168eaf RR |
253 | for (i = 0 ; i < NUM_COUNTERS ; ++i) { |
254 | if (reset_value[i]) { | |
dea3766c RR |
255 | rdmsrl(msrs->controls[i].addr, val); |
256 | val |= ARCH_PERFMON_EVENTSEL0_ENABLE; | |
257 | wrmsrl(msrs->controls[i].addr, val); | |
1da177e4 LT |
258 | } |
259 | } | |
852402cc | 260 | |
90637595 | 261 | op_amd_start_ibs(); |
1da177e4 LT |
262 | } |
263 | ||
6657fe4f | 264 | static void op_amd_stop(struct op_msrs const * const msrs) |
1da177e4 | 265 | { |
dea3766c | 266 | u64 val; |
1da177e4 LT |
267 | int i; |
268 | ||
fd13f6c8 RR |
269 | /* |
270 | * Subtle: stop on all counters to avoid race with setting our | |
271 | * pm callback | |
272 | */ | |
4c168eaf RR |
273 | for (i = 0 ; i < NUM_COUNTERS ; ++i) { |
274 | if (!reset_value[i]) | |
cb9c448c | 275 | continue; |
dea3766c RR |
276 | rdmsrl(msrs->controls[i].addr, val); |
277 | val &= ~ARCH_PERFMON_EVENTSEL0_ENABLE; | |
278 | wrmsrl(msrs->controls[i].addr, val); | |
1da177e4 | 279 | } |
56784f11 | 280 | |
90637595 | 281 | op_amd_stop_ibs(); |
1da177e4 LT |
282 | } |
283 | ||
6657fe4f | 284 | static void op_amd_shutdown(struct op_msrs const * const msrs) |
cb9c448c DZ |
285 | { |
286 | int i; | |
287 | ||
4c168eaf | 288 | for (i = 0 ; i < NUM_COUNTERS ; ++i) { |
217d3cfb | 289 | if (msrs->counters[i].addr) |
cb9c448c DZ |
290 | release_perfctr_nmi(MSR_K7_PERFCTR0 + i); |
291 | } | |
4c168eaf | 292 | for (i = 0 ; i < NUM_CONTROLS ; ++i) { |
217d3cfb | 293 | if (msrs->controls[i].addr) |
cb9c448c DZ |
294 | release_evntsel_nmi(MSR_K7_EVNTSEL0 + i); |
295 | } | |
296 | } | |
1da177e4 | 297 | |
9fa6812d | 298 | #ifdef CONFIG_OPROFILE_IBS |
a4c408a4 | 299 | |
7d77f2dc RR |
300 | static u8 ibs_eilvt_off; |
301 | ||
56784f11 BK |
302 | static inline void apic_init_ibs_nmi_per_cpu(void *arg) |
303 | { | |
7d77f2dc | 304 | ibs_eilvt_off = setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0); |
56784f11 BK |
305 | } |
306 | ||
307 | static inline void apic_clear_ibs_nmi_per_cpu(void *arg) | |
308 | { | |
309 | setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1); | |
310 | } | |
311 | ||
fe615cbf | 312 | static int init_ibs_nmi(void) |
7d77f2dc RR |
313 | { |
314 | #define IBSCTL_LVTOFFSETVAL (1 << 8) | |
315 | #define IBSCTL 0x1cc | |
316 | struct pci_dev *cpu_cfg; | |
317 | int nodes; | |
318 | u32 value = 0; | |
319 | ||
320 | /* per CPU setup */ | |
ebb535de | 321 | on_each_cpu(apic_init_ibs_nmi_per_cpu, NULL, 1); |
7d77f2dc RR |
322 | |
323 | nodes = 0; | |
324 | cpu_cfg = NULL; | |
325 | do { | |
326 | cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD, | |
327 | PCI_DEVICE_ID_AMD_10H_NB_MISC, | |
328 | cpu_cfg); | |
329 | if (!cpu_cfg) | |
330 | break; | |
331 | ++nodes; | |
332 | pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off | |
333 | | IBSCTL_LVTOFFSETVAL); | |
334 | pci_read_config_dword(cpu_cfg, IBSCTL, &value); | |
335 | if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) { | |
83bd9243 | 336 | pci_dev_put(cpu_cfg); |
7d77f2dc RR |
337 | printk(KERN_DEBUG "Failed to setup IBS LVT offset, " |
338 | "IBSCTL = 0x%08x", value); | |
339 | return 1; | |
340 | } | |
341 | } while (1); | |
342 | ||
343 | if (!nodes) { | |
344 | printk(KERN_DEBUG "No CPU node configured for IBS"); | |
345 | return 1; | |
346 | } | |
347 | ||
348 | #ifdef CONFIG_NUMA | |
349 | /* Sanity check */ | |
350 | /* Works only for 64bit with proper numa implementation. */ | |
351 | if (nodes != num_possible_nodes()) { | |
352 | printk(KERN_DEBUG "Failed to setup CPU node(s) for IBS, " | |
353 | "found: %d, expected %d", | |
354 | nodes, num_possible_nodes()); | |
355 | return 1; | |
356 | } | |
357 | #endif | |
358 | return 0; | |
359 | } | |
360 | ||
fe615cbf RR |
361 | /* uninitialize the APIC for the IBS interrupts if needed */ |
362 | static void clear_ibs_nmi(void) | |
363 | { | |
fc81be8c | 364 | if (has_ibs) |
fe615cbf RR |
365 | on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1); |
366 | } | |
367 | ||
fd13f6c8 | 368 | /* initialize the APIC for the IBS interrupts if available */ |
fe615cbf | 369 | static void ibs_init(void) |
56784f11 | 370 | { |
fc81be8c | 371 | has_ibs = boot_cpu_has(X86_FEATURE_IBS); |
56784f11 | 372 | |
fc81be8c | 373 | if (!has_ibs) |
56784f11 BK |
374 | return; |
375 | ||
fe615cbf | 376 | if (init_ibs_nmi()) { |
fc81be8c | 377 | has_ibs = 0; |
852402cc RR |
378 | return; |
379 | } | |
380 | ||
381 | printk(KERN_INFO "oprofile: AMD IBS detected\n"); | |
56784f11 BK |
382 | } |
383 | ||
fe615cbf | 384 | static void ibs_exit(void) |
56784f11 | 385 | { |
fc81be8c | 386 | if (!has_ibs) |
fe615cbf RR |
387 | return; |
388 | ||
389 | clear_ibs_nmi(); | |
56784f11 BK |
390 | } |
391 | ||
25ad2913 | 392 | static int (*create_arch_files)(struct super_block *sb, struct dentry *root); |
270d3e1a | 393 | |
25ad2913 | 394 | static int setup_ibs_files(struct super_block *sb, struct dentry *root) |
56784f11 | 395 | { |
56784f11 | 396 | struct dentry *dir; |
270d3e1a RR |
397 | int ret = 0; |
398 | ||
399 | /* architecture specific files */ | |
400 | if (create_arch_files) | |
401 | ret = create_arch_files(sb, root); | |
402 | ||
403 | if (ret) | |
404 | return ret; | |
56784f11 | 405 | |
fc81be8c | 406 | if (!has_ibs) |
270d3e1a RR |
407 | return ret; |
408 | ||
409 | /* model specific files */ | |
56784f11 BK |
410 | |
411 | /* setup some reasonable defaults */ | |
412 | ibs_config.max_cnt_fetch = 250000; | |
413 | ibs_config.fetch_enabled = 0; | |
414 | ibs_config.max_cnt_op = 250000; | |
415 | ibs_config.op_enabled = 0; | |
416 | ibs_config.dispatched_ops = 1; | |
2d55a478 RR |
417 | |
418 | dir = oprofilefs_mkdir(sb, root, "ibs_fetch"); | |
56784f11 | 419 | oprofilefs_create_ulong(sb, dir, "enable", |
2d55a478 | 420 | &ibs_config.fetch_enabled); |
56784f11 | 421 | oprofilefs_create_ulong(sb, dir, "max_count", |
2d55a478 RR |
422 | &ibs_config.max_cnt_fetch); |
423 | oprofilefs_create_ulong(sb, dir, "rand_enable", | |
424 | &ibs_config.rand_en); | |
425 | ||
ccd755c2 | 426 | dir = oprofilefs_mkdir(sb, root, "ibs_op"); |
56784f11 | 427 | oprofilefs_create_ulong(sb, dir, "enable", |
2d55a478 | 428 | &ibs_config.op_enabled); |
56784f11 | 429 | oprofilefs_create_ulong(sb, dir, "max_count", |
2d55a478 | 430 | &ibs_config.max_cnt_op); |
56784f11 | 431 | oprofilefs_create_ulong(sb, dir, "dispatched_ops", |
2d55a478 | 432 | &ibs_config.dispatched_ops); |
fc2bd734 RR |
433 | |
434 | return 0; | |
56784f11 BK |
435 | } |
436 | ||
adf5ec0b RR |
437 | static int op_amd_init(struct oprofile_operations *ops) |
438 | { | |
fe615cbf | 439 | ibs_init(); |
270d3e1a RR |
440 | create_arch_files = ops->create_files; |
441 | ops->create_files = setup_ibs_files; | |
adf5ec0b RR |
442 | return 0; |
443 | } | |
444 | ||
445 | static void op_amd_exit(void) | |
446 | { | |
fe615cbf | 447 | ibs_exit(); |
adf5ec0b RR |
448 | } |
449 | ||
9fa6812d RR |
450 | #else |
451 | ||
452 | /* no IBS support */ | |
453 | ||
454 | static int op_amd_init(struct oprofile_operations *ops) | |
455 | { | |
456 | return 0; | |
457 | } | |
458 | ||
459 | static void op_amd_exit(void) {} | |
460 | ||
461 | #endif /* CONFIG_OPROFILE_IBS */ | |
a4c408a4 | 462 | |
6657fe4f | 463 | struct op_x86_model_spec const op_amd_spec = { |
c92960fc RR |
464 | .num_counters = NUM_COUNTERS, |
465 | .num_controls = NUM_CONTROLS, | |
3370d358 RR |
466 | .reserved = MSR_AMD_EVENTSEL_RESERVED, |
467 | .event_mask = OP_EVENT_MASK, | |
468 | .init = op_amd_init, | |
469 | .exit = op_amd_exit, | |
c92960fc RR |
470 | .fill_in_addresses = &op_amd_fill_in_addresses, |
471 | .setup_ctrs = &op_amd_setup_ctrs, | |
472 | .check_ctrs = &op_amd_check_ctrs, | |
473 | .start = &op_amd_start, | |
474 | .stop = &op_amd_stop, | |
3370d358 | 475 | .shutdown = &op_amd_shutdown, |
1da177e4 | 476 | }; |