1#include <linux/module.h>
2#include <linux/slab.h>
3
4#include "mce_amd.h"
5
6static struct amd_decoder_ops *fam_ops;
7
8static u8 xec_mask	 = 0xf;
9static u8 nb_err_cpumask = 0xf;
10
11static bool report_gart_errors;
12static void (*nb_bus_decoder)(int node_id, struct mce *m);
13
14void amd_report_gart_errors(bool v)
15{
16	report_gart_errors = v;
17}
18EXPORT_SYMBOL_GPL(amd_report_gart_errors);
19
20void amd_register_ecc_decoder(void (*f)(int, struct mce *))
21{
22	nb_bus_decoder = f;
23}
24EXPORT_SYMBOL_GPL(amd_register_ecc_decoder);
25
26void amd_unregister_ecc_decoder(void (*f)(int, struct mce *))
27{
28	if (nb_bus_decoder) {
29		WARN_ON(nb_bus_decoder != f);
30
31		nb_bus_decoder = NULL;
32	}
33}
34EXPORT_SYMBOL_GPL(amd_unregister_ecc_decoder);
35
36/*
37 * string representation for the different MCA reported error types, see F3x48
38 * or MSR0000_0411.
39 */
40
41/* transaction type */
42const char * const tt_msgs[] = { "INSN", "DATA", "GEN", "RESV" };
43EXPORT_SYMBOL_GPL(tt_msgs);
44
45/* cache level */
46const char * const ll_msgs[] = { "RESV", "L1", "L2", "L3/GEN" };
47EXPORT_SYMBOL_GPL(ll_msgs);
48
49/* memory transaction type */
50const char * const rrrr_msgs[] = {
51       "GEN", "RD", "WR", "DRD", "DWR", "IRD", "PRF", "EV", "SNP"
52};
53EXPORT_SYMBOL_GPL(rrrr_msgs);
54
55/* participating processor */
56const char * const pp_msgs[] = { "SRC", "RES", "OBS", "GEN" };
57EXPORT_SYMBOL_GPL(pp_msgs);
58
59/* request timeout */
60const char * const to_msgs[] = { "no timeout", "timed out" };
61EXPORT_SYMBOL_GPL(to_msgs);
62
63/* memory or i/o */
64const char * const ii_msgs[] = { "MEM", "RESV", "IO", "GEN" };
65EXPORT_SYMBOL_GPL(ii_msgs);
66
67static const char * const f15h_ic_mce_desc[] = {
68	"UC during a demand linefill from L2",
69	"Parity error during data load from IC",
70	"Parity error for IC valid bit",
71	"Main tag parity error",
72	"Parity error in prediction queue",
73	"PFB data/address parity error",
74	"Parity error in the branch status reg",
75	"PFB promotion address error",
76	"Tag error during probe/victimization",
77	"Parity error for IC probe tag valid bit",
78	"PFB non-cacheable bit parity error",
79	"PFB valid bit parity error",			/* xec = 0xd */
80	"Microcode Patch Buffer",			/* xec = 010 */
81	"uop queue",
82	"insn buffer",
83	"predecode buffer",
84	"fetch address FIFO"
85};
86
87static const char * const f15h_cu_mce_desc[] = {
88	"Fill ECC error on data fills",			/* xec = 0x4 */
89	"Fill parity error on insn fills",
90	"Prefetcher request FIFO parity error",
91	"PRQ address parity error",
92	"PRQ data parity error",
93	"WCC Tag ECC error",
94	"WCC Data ECC error",
95	"WCB Data parity error",
96	"VB Data ECC or parity error",
97	"L2 Tag ECC error",				/* xec = 0x10 */
98	"Hard L2 Tag ECC error",
99	"Multiple hits on L2 tag",
100	"XAB parity error",
101	"PRB address parity error"
102};
103
104static const char * const nb_mce_desc[] = {
105	"DRAM ECC error detected on the NB",
106	"CRC error detected on HT link",
107	"Link-defined sync error packets detected on HT link",
108	"HT Master abort",
109	"HT Target abort",
110	"Invalid GART PTE entry during GART table walk",
111	"Unsupported atomic RMW received from an IO link",
112	"Watchdog timeout due to lack of progress",
113	"DRAM ECC error detected on the NB",
114	"SVM DMA Exclusion Vector error",
115	"HT data error detected on link",
116	"Protocol error (link, L3, probe filter)",
117	"NB internal arrays parity error",
118	"DRAM addr/ctl signals parity error",
119	"IO link transmission error",
120	"L3 data cache ECC error",			/* xec = 0x1c */
121	"L3 cache tag error",
122	"L3 LRU parity bits error",
123	"ECC Error in the Probe Filter directory"
124};
125
126static const char * const fr_ex_mce_desc[] = {
127	"CPU Watchdog timer expire",
128	"Wakeup array dest tag",
129	"AG payload array",
130	"EX payload array",
131	"IDRF array",
132	"Retire dispatch queue",
133	"Mapper checkpoint array",
134	"Physical register file EX0 port",
135	"Physical register file EX1 port",
136	"Physical register file AG0 port",
137	"Physical register file AG1 port",
138	"Flag register file",
139	"DE error occurred"
140};
141
142static bool f12h_dc_mce(u16 ec, u8 xec)
143{
144	bool ret = false;
145
146	if (MEM_ERROR(ec)) {
147		u8 ll = LL(ec);
148		ret = true;
149
150		if (ll == LL_L2)
151			pr_cont("during L1 linefill from L2.\n");
152		else if (ll == LL_L1)
153			pr_cont("Data/Tag %s error.\n", R4_MSG(ec));
154		else
155			ret = false;
156	}
157	return ret;
158}
159
160static bool f10h_dc_mce(u16 ec, u8 xec)
161{
162	if (R4(ec) == R4_GEN && LL(ec) == LL_L1) {
163		pr_cont("during data scrub.\n");
164		return true;
165	}
166	return f12h_dc_mce(ec, xec);
167}
168
169static bool k8_dc_mce(u16 ec, u8 xec)
170{
171	if (BUS_ERROR(ec)) {
172		pr_cont("during system linefill.\n");
173		return true;
174	}
175
176	return f10h_dc_mce(ec, xec);
177}
178
179static bool f14h_dc_mce(u16 ec, u8 xec)
180{
181	u8 r4	 = R4(ec);
182	bool ret = true;
183
184	if (MEM_ERROR(ec)) {
185
186		if (TT(ec) != TT_DATA || LL(ec) != LL_L1)
187			return false;
188
189		switch (r4) {
190		case R4_DRD:
191		case R4_DWR:
192			pr_cont("Data/Tag parity error due to %s.\n",
193				(r4 == R4_DRD ? "load/hw prf" : "store"));
194			break;
195		case R4_EVICT:
196			pr_cont("Copyback parity error on a tag miss.\n");
197			break;
198		case R4_SNOOP:
199			pr_cont("Tag parity error during snoop.\n");
200			break;
201		default:
202			ret = false;
203		}
204	} else if (BUS_ERROR(ec)) {
205
206		if ((II(ec) != II_MEM && II(ec) != II_IO) || LL(ec) != LL_LG)
207			return false;
208
209		pr_cont("System read data error on a ");
210
211		switch (r4) {
212		case R4_RD:
213			pr_cont("TLB reload.\n");
214			break;
215		case R4_DWR:
216			pr_cont("store.\n");
217			break;
218		case R4_DRD:
219			pr_cont("load.\n");
220			break;
221		default:
222			ret = false;
223		}
224	} else {
225		ret = false;
226	}
227
228	return ret;
229}
230
231static bool f15h_dc_mce(u16 ec, u8 xec)
232{
233	bool ret = true;
234
235	if (MEM_ERROR(ec)) {
236
237		switch (xec) {
238		case 0x0:
239			pr_cont("Data Array access error.\n");
240			break;
241
242		case 0x1:
243			pr_cont("UC error during a linefill from L2/NB.\n");
244			break;
245
246		case 0x2:
247		case 0x11:
248			pr_cont("STQ access error.\n");
249			break;
250
251		case 0x3:
252			pr_cont("SCB access error.\n");
253			break;
254
255		case 0x10:
256			pr_cont("Tag error.\n");
257			break;
258
259		case 0x12:
260			pr_cont("LDQ access error.\n");
261			break;
262
263		default:
264			ret = false;
265		}
266	} else if (BUS_ERROR(ec)) {
267
268		if (!xec)
269			pr_cont("System Read Data Error.\n");
270		else
271			pr_cont(" Internal error condition type %d.\n", xec);
272	} else
273		ret = false;
274
275	return ret;
276}
277
278static void amd_decode_dc_mce(struct mce *m)
279{
280	u16 ec = EC(m->status);
281	u8 xec = XEC(m->status, xec_mask);
282
283	pr_emerg(HW_ERR "Data Cache Error: ");
284
285	/* TLB error signatures are the same across families */
286	if (TLB_ERROR(ec)) {
287		if (TT(ec) == TT_DATA) {
288			pr_cont("%s TLB %s.\n", LL_MSG(ec),
289				((xec == 2) ? "locked miss"
290					    : (xec ? "multimatch" : "parity")));
291			return;
292		}
293	} else if (fam_ops->dc_mce(ec, xec))
294		;
295	else
296		pr_emerg(HW_ERR "Corrupted DC MCE info?\n");
297}
298
299static bool k8_ic_mce(u16 ec, u8 xec)
300{
301	u8 ll	 = LL(ec);
302	bool ret = true;
303
304	if (!MEM_ERROR(ec))
305		return false;
306
307	if (ll == 0x2)
308		pr_cont("during a linefill from L2.\n");
309	else if (ll == 0x1) {
310		switch (R4(ec)) {
311		case R4_IRD:
312			pr_cont("Parity error during data load.\n");
313			break;
314
315		case R4_EVICT:
316			pr_cont("Copyback Parity/Victim error.\n");
317			break;
318
319		case R4_SNOOP:
320			pr_cont("Tag Snoop error.\n");
321			break;
322
323		default:
324			ret = false;
325			break;
326		}
327	} else
328		ret = false;
329
330	return ret;
331}
332
333static bool f14h_ic_mce(u16 ec, u8 xec)
334{
335	u8 r4    = R4(ec);
336	bool ret = true;
337
338	if (MEM_ERROR(ec)) {
339		if (TT(ec) != 0 || LL(ec) != 1)
340			ret = false;
341
342		if (r4 == R4_IRD)
343			pr_cont("Data/tag array parity error for a tag hit.\n");
344		else if (r4 == R4_SNOOP)
345			pr_cont("Tag error during snoop/victimization.\n");
346		else
347			ret = false;
348	}
349	return ret;
350}
351
352static bool f15h_ic_mce(u16 ec, u8 xec)
353{
354	bool ret = true;
355
356	if (!MEM_ERROR(ec))
357		return false;
358
359	switch (xec) {
360	case 0x0 ... 0xa:
361		pr_cont("%s.\n", f15h_ic_mce_desc[xec]);
362		break;
363
364	case 0xd:
365		pr_cont("%s.\n", f15h_ic_mce_desc[xec-2]);
366		break;
367
368	case 0x10:
369		pr_cont("%s.\n", f15h_ic_mce_desc[xec-4]);
370		break;
371
372	case 0x11 ... 0x14:
373		pr_cont("Decoder %s parity error.\n", f15h_ic_mce_desc[xec-4]);
374		break;
375
376	default:
377		ret = false;
378	}
379	return ret;
380}
381
382static void amd_decode_ic_mce(struct mce *m)
383{
384	u16 ec = EC(m->status);
385	u8 xec = XEC(m->status, xec_mask);
386
387	pr_emerg(HW_ERR "Instruction Cache Error: ");
388
389	if (TLB_ERROR(ec))
390		pr_cont("%s TLB %s.\n", LL_MSG(ec),
391			(xec ? "multimatch" : "parity error"));
392	else if (BUS_ERROR(ec)) {
393		bool k8 = (boot_cpu_data.x86 == 0xf && (m->status & BIT_64(58)));
394
395		pr_cont("during %s.\n", (k8 ? "system linefill" : "NB data read"));
396	} else if (fam_ops->ic_mce(ec, xec))
397		;
398	else
399		pr_emerg(HW_ERR "Corrupted IC MCE info?\n");
400}
401
402static void amd_decode_bu_mce(struct mce *m)
403{
404	u16 ec = EC(m->status);
405	u8 xec = XEC(m->status, xec_mask);
406
407	pr_emerg(HW_ERR "Bus Unit Error");
408
409	if (xec == 0x1)
410		pr_cont(" in the write data buffers.\n");
411	else if (xec == 0x3)
412		pr_cont(" in the victim data buffers.\n");
413	else if (xec == 0x2 && MEM_ERROR(ec))
414		pr_cont(": %s error in the L2 cache tags.\n", R4_MSG(ec));
415	else if (xec == 0x0) {
416		if (TLB_ERROR(ec))
417			pr_cont(": %s error in a Page Descriptor Cache or "
418				"Guest TLB.\n", TT_MSG(ec));
419		else if (BUS_ERROR(ec))
420			pr_cont(": %s/ECC error in data read from NB: %s.\n",
421				R4_MSG(ec), PP_MSG(ec));
422		else if (MEM_ERROR(ec)) {
423			u8 r4 = R4(ec);
424
425			if (r4 >= 0x7)
426				pr_cont(": %s error during data copyback.\n",
427					R4_MSG(ec));
428			else if (r4 <= 0x1)
429				pr_cont(": %s parity/ECC error during data "
430					"access from L2.\n", R4_MSG(ec));
431			else
432				goto wrong_bu_mce;
433		} else
434			goto wrong_bu_mce;
435	} else
436		goto wrong_bu_mce;
437
438	return;
439
440wrong_bu_mce:
441	pr_emerg(HW_ERR "Corrupted BU MCE info?\n");
442}
443
444static void amd_decode_cu_mce(struct mce *m)
445{
446	u16 ec = EC(m->status);
447	u8 xec = XEC(m->status, xec_mask);
448
449	pr_emerg(HW_ERR "Combined Unit Error: ");
450
451	if (TLB_ERROR(ec)) {
452		if (xec == 0x0)
453			pr_cont("Data parity TLB read error.\n");
454		else if (xec == 0x1)
455			pr_cont("Poison data provided for TLB fill.\n");
456		else
457			goto wrong_cu_mce;
458	} else if (BUS_ERROR(ec)) {
459		if (xec > 2)
460			goto wrong_cu_mce;
461
462		pr_cont("Error during attempted NB data read.\n");
463	} else if (MEM_ERROR(ec)) {
464		switch (xec) {
465		case 0x4 ... 0xc:
466			pr_cont("%s.\n", f15h_cu_mce_desc[xec - 0x4]);
467			break;
468
469		case 0x10 ... 0x14:
470			pr_cont("%s.\n", f15h_cu_mce_desc[xec - 0x7]);
471			break;
472
473		default:
474			goto wrong_cu_mce;
475		}
476	}
477
478	return;
479
480wrong_cu_mce:
481	pr_emerg(HW_ERR "Corrupted CU MCE info?\n");
482}
483
484static void amd_decode_ls_mce(struct mce *m)
485{
486	u16 ec = EC(m->status);
487	u8 xec = XEC(m->status, xec_mask);
488
489	if (boot_cpu_data.x86 >= 0x14) {
490		pr_emerg("You shouldn't be seeing an LS MCE on this cpu family,"
491			 " please report on LKML.\n");
492		return;
493	}
494
495	pr_emerg(HW_ERR "Load Store Error");
496
497	if (xec == 0x0) {
498		u8 r4 = R4(ec);
499
500		if (!BUS_ERROR(ec) || (r4 != R4_DRD && r4 != R4_DWR))
501			goto wrong_ls_mce;
502
503		pr_cont(" during %s.\n", R4_MSG(ec));
504	} else
505		goto wrong_ls_mce;
506
507	return;
508
509wrong_ls_mce:
510	pr_emerg(HW_ERR "Corrupted LS MCE info?\n");
511}
512
513void amd_decode_nb_mce(struct mce *m)
514{
515	struct cpuinfo_x86 *c = &boot_cpu_data;
516	int node_id = amd_get_nb_id(m->extcpu);
517	u16 ec = EC(m->status);
518	u8 xec = XEC(m->status, 0x1f);
519	u8 offset = 0;
520
521	pr_emerg(HW_ERR "Northbridge Error (node %d): ", node_id);
522
523	switch (xec) {
524	case 0x0 ... 0xe:
525
526		/* special handling for DRAM ECCs */
527		if (xec == 0x0 || xec == 0x8) {
528			/* no ECCs on F11h */
529			if (c->x86 == 0x11)
530				goto wrong_nb_mce;
531
532			pr_cont("%s.\n", nb_mce_desc[xec]);
533
534			if (nb_bus_decoder)
535				nb_bus_decoder(node_id, m);
536			return;
537		}
538		break;
539
540	case 0xf:
541		if (TLB_ERROR(ec))
542			pr_cont("GART Table Walk data error.\n");
543		else if (BUS_ERROR(ec))
544			pr_cont("DMA Exclusion Vector Table Walk error.\n");
545		else
546			goto wrong_nb_mce;
547		return;
548
549	case 0x19:
550		if (boot_cpu_data.x86 == 0x15)
551			pr_cont("Compute Unit Data Error.\n");
552		else
553			goto wrong_nb_mce;
554		return;
555
556	case 0x1c ... 0x1f:
557		offset = 13;
558		break;
559
560	default:
561		goto wrong_nb_mce;
562	}
563
564	pr_cont("%s.\n", nb_mce_desc[xec - offset]);
565	return;
566
567wrong_nb_mce:
568	pr_emerg(HW_ERR "Corrupted NB MCE info?\n");
569}
570EXPORT_SYMBOL_GPL(amd_decode_nb_mce);
571
572static void amd_decode_fr_mce(struct mce *m)
573{
574	struct cpuinfo_x86 *c = &boot_cpu_data;
575	u8 xec = XEC(m->status, xec_mask);
576
577	if (c->x86 == 0xf || c->x86 == 0x11)
578		goto wrong_fr_mce;
579
580	pr_emerg(HW_ERR "%s Error: ",
581		 (c->x86 == 0x15 ? "Execution Unit" : "FIROB"));
582
583	if (xec == 0x0 || xec == 0xc)
584		pr_cont("%s.\n", fr_ex_mce_desc[xec]);
585	else if (xec < 0xd)
586		pr_cont("%s parity error.\n", fr_ex_mce_desc[xec]);
587	else
588		goto wrong_fr_mce;
589
590	return;
591
592wrong_fr_mce:
593	pr_emerg(HW_ERR "Corrupted FR MCE info?\n");
594}
595
596static void amd_decode_fp_mce(struct mce *m)
597{
598	u8 xec = XEC(m->status, xec_mask);
599
600	pr_emerg(HW_ERR "Floating Point Unit Error: ");
601
602	switch (xec) {
603	case 0x1:
604		pr_cont("Free List");
605		break;
606
607	case 0x2:
608		pr_cont("Physical Register File");
609		break;
610
611	case 0x3:
612		pr_cont("Retire Queue");
613		break;
614
615	case 0x4:
616		pr_cont("Scheduler table");
617		break;
618
619	case 0x5:
620		pr_cont("Status Register File");
621		break;
622
623	default:
624		goto wrong_fp_mce;
625		break;
626	}
627
628	pr_cont(" parity error.\n");
629
630	return;
631
632wrong_fp_mce:
633	pr_emerg(HW_ERR "Corrupted FP MCE info?\n");
634}
635
636static inline void amd_decode_err_code(u16 ec)
637{
638
639	pr_emerg(HW_ERR "cache level: %s", LL_MSG(ec));
640
641	if (BUS_ERROR(ec))
642		pr_cont(", mem/io: %s", II_MSG(ec));
643	else
644		pr_cont(", tx: %s", TT_MSG(ec));
645
646	if (MEM_ERROR(ec) || BUS_ERROR(ec)) {
647		pr_cont(", mem-tx: %s", R4_MSG(ec));
648
649		if (BUS_ERROR(ec))
650			pr_cont(", part-proc: %s (%s)", PP_MSG(ec), TO_MSG(ec));
651	}
652
653	pr_cont("\n");
654}
655
656/*
657 * Filter out unwanted MCE signatures here.
658 */
659static bool amd_filter_mce(struct mce *m)
660{
661	u8 xec = (m->status >> 16) & 0x1f;
662
663	/*
664	 * NB GART TLB error reporting is disabled by default.
665	 */
666	if (m->bank == 4 && xec == 0x5 && !report_gart_errors)
667		return true;
668
669	return false;
670}
671
672int amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data)
673{
674	struct mce *m = (struct mce *)data;
675	struct cpuinfo_x86 *c = &boot_cpu_data;
676	int ecc;
677
678	if (amd_filter_mce(m))
679		return NOTIFY_STOP;
680
681	pr_emerg(HW_ERR "CPU:%d\tMC%d_STATUS[%s|%s|%s|%s|%s",
682		m->extcpu, m->bank,
683		((m->status & MCI_STATUS_OVER)	? "Over"  : "-"),
684		((m->status & MCI_STATUS_UC)	? "UE"	  : "CE"),
685		((m->status & MCI_STATUS_MISCV)	? "MiscV" : "-"),
686		((m->status & MCI_STATUS_PCC)	? "PCC"	  : "-"),
687		((m->status & MCI_STATUS_ADDRV)	? "AddrV" : "-"));
688
689	if (c->x86 == 0x15)
690		pr_cont("|%s|%s",
691			((m->status & BIT_64(44)) ? "Deferred" : "-"),
692			((m->status & BIT_64(43)) ? "Poison"   : "-"));
693
694	/* do the two bits[14:13] together */
695	ecc = (m->status >> 45) & 0x3;
696	if (ecc)
697		pr_cont("|%sECC", ((ecc == 2) ? "C" : "U"));
698
699	pr_cont("]: 0x%016llx\n", m->status);
700
701	if (m->status & MCI_STATUS_ADDRV)
702		pr_emerg(HW_ERR "\tMC%d_ADDR: 0x%016llx\n", m->bank, m->addr);
703
704	switch (m->bank) {
705	case 0:
706		amd_decode_dc_mce(m);
707		break;
708
709	case 1:
710		amd_decode_ic_mce(m);
711		break;
712
713	case 2:
714		if (c->x86 == 0x15)
715			amd_decode_cu_mce(m);
716		else
717			amd_decode_bu_mce(m);
718		break;
719
720	case 3:
721		amd_decode_ls_mce(m);
722		break;
723
724	case 4:
725		amd_decode_nb_mce(m);
726		break;
727
728	case 5:
729		amd_decode_fr_mce(m);
730		break;
731
732	case 6:
733		amd_decode_fp_mce(m);
734		break;
735
736	default:
737		break;
738	}
739
740	amd_decode_err_code(m->status & 0xffff);
741
742	return NOTIFY_STOP;
743}
744EXPORT_SYMBOL_GPL(amd_decode_mce);
745
746static struct notifier_block amd_mce_dec_nb = {
747	.notifier_call	= amd_decode_mce,
748};
749
750static int __init mce_amd_init(void)
751{
752	struct cpuinfo_x86 *c = &boot_cpu_data;
753
754	if (c->x86_vendor != X86_VENDOR_AMD)
755		return 0;
756
757	if (c->x86 < 0xf || c->x86 > 0x15)
758		return 0;
759
760	fam_ops = kzalloc(sizeof(struct amd_decoder_ops), GFP_KERNEL);
761	if (!fam_ops)
762		return -ENOMEM;
763
764	switch (c->x86) {
765	case 0xf:
766		fam_ops->dc_mce = k8_dc_mce;
767		fam_ops->ic_mce = k8_ic_mce;
768		break;
769
770	case 0x10:
771		fam_ops->dc_mce = f10h_dc_mce;
772		fam_ops->ic_mce = k8_ic_mce;
773		break;
774
775	case 0x11:
776		fam_ops->dc_mce = k8_dc_mce;
777		fam_ops->ic_mce = k8_ic_mce;
778		break;
779
780	case 0x12:
781		fam_ops->dc_mce = f12h_dc_mce;
782		fam_ops->ic_mce = k8_ic_mce;
783		break;
784
785	case 0x14:
786		nb_err_cpumask  = 0x3;
787		fam_ops->dc_mce = f14h_dc_mce;
788		fam_ops->ic_mce = f14h_ic_mce;
789		break;
790
791	case 0x15:
792		xec_mask = 0x1f;
793		fam_ops->dc_mce = f15h_dc_mce;
794		fam_ops->ic_mce = f15h_ic_mce;
795		break;
796
797	default:
798		printk(KERN_WARNING "Huh? What family is it: 0x%x?!\n", c->x86);
799		kfree(fam_ops);
800		return -EINVAL;
801	}
802
803	pr_info("MCE: In-kernel MCE decoding enabled.\n");
804
805	mce_register_decode_chain(&amd_mce_dec_nb);
806
807	return 0;
808}
809early_initcall(mce_amd_init);
810
811#ifdef MODULE
812static void __exit mce_amd_exit(void)
813{
814	mce_unregister_decode_chain(&amd_mce_dec_nb);
815	kfree(fam_ops);
816}
817
818MODULE_DESCRIPTION("AMD MCE decoder");
819MODULE_ALIAS("edac-mce-amd");
820MODULE_LICENSE("GPL");
821module_exit(mce_amd_exit);
822#endif
823