xref: /dflybsd-src/sys/dev/misc/ecc/ecc_e5.c (revision b8c69b44c3d7472d7f129a92321e58f0edde9723)
150193170SSepherosa Ziehau /*
250193170SSepherosa Ziehau  * Copyright (c) 2015 The DragonFly Project.  All rights reserved.
350193170SSepherosa Ziehau  *
450193170SSepherosa Ziehau  * This code is derived from software contributed to The DragonFly Project
550193170SSepherosa Ziehau  * by Sepherosa Ziehau <sepherosa@gmail.com>
650193170SSepherosa Ziehau  *
750193170SSepherosa Ziehau  * Redistribution and use in source and binary forms, with or without
850193170SSepherosa Ziehau  * modification, are permitted provided that the following conditions
950193170SSepherosa Ziehau  * are met:
1050193170SSepherosa Ziehau  *
1150193170SSepherosa Ziehau  * 1. Redistributions of source code must retain the above copyright
1250193170SSepherosa Ziehau  *    notice, this list of conditions and the following disclaimer.
1350193170SSepherosa Ziehau  * 2. Redistributions in binary form must reproduce the above copyright
1450193170SSepherosa Ziehau  *    notice, this list of conditions and the following disclaimer in
1550193170SSepherosa Ziehau  *    the documentation and/or other materials provided with the
1650193170SSepherosa Ziehau  *    distribution.
1750193170SSepherosa Ziehau  * 3. Neither the name of The DragonFly Project nor the names of its
1850193170SSepherosa Ziehau  *    contributors may be used to endorse or promote products derived
1950193170SSepherosa Ziehau  *    from this software without specific, prior written permission.
2050193170SSepherosa Ziehau  *
2150193170SSepherosa Ziehau  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2250193170SSepherosa Ziehau  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2350193170SSepherosa Ziehau  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2450193170SSepherosa Ziehau  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
2550193170SSepherosa Ziehau  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2650193170SSepherosa Ziehau  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
2750193170SSepherosa Ziehau  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2850193170SSepherosa Ziehau  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2950193170SSepherosa Ziehau  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3050193170SSepherosa Ziehau  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3150193170SSepherosa Ziehau  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3250193170SSepherosa Ziehau  * SUCH DAMAGE.
3350193170SSepherosa Ziehau  */
3450193170SSepherosa Ziehau 
3550193170SSepherosa Ziehau #include <sys/param.h>
3650193170SSepherosa Ziehau #include <sys/systm.h>
3723832f75SSepherosa Ziehau #include <sys/bitops.h>
3850193170SSepherosa Ziehau #include <sys/bus.h>
39e9a2e4daSSepherosa Ziehau #include <sys/cpu_topology.h>
4050193170SSepherosa Ziehau #include <sys/kernel.h>
4150193170SSepherosa Ziehau #include <sys/malloc.h>
4223832f75SSepherosa Ziehau #include <sys/queue.h>
4323832f75SSepherosa Ziehau #include <sys/sensors.h>
4450193170SSepherosa Ziehau 
4550193170SSepherosa Ziehau #include <bus/pci/pcivar.h>
4650193170SSepherosa Ziehau #include <bus/pci/pcireg.h>
4750193170SSepherosa Ziehau #include <bus/pci/pci_cfgreg.h>
4850193170SSepherosa Ziehau #include <bus/pci/pcib_private.h>
4950193170SSepherosa Ziehau 
5050193170SSepherosa Ziehau #include "pcib_if.h"
5150193170SSepherosa Ziehau 
5223832f75SSepherosa Ziehau #include <dev/misc/dimm/dimm.h>
530c543cddSSepherosa Ziehau #include <dev/misc/ecc/e5_imc_reg.h>
540c543cddSSepherosa Ziehau #include <dev/misc/ecc/e5_imc_var.h>
5550193170SSepherosa Ziehau 
5623832f75SSepherosa Ziehau struct ecc_e5_dimm {
5723832f75SSepherosa Ziehau 	TAILQ_ENTRY(ecc_e5_dimm) dimm_link;
5823832f75SSepherosa Ziehau 	struct dimm_softc	*dimm_softc;
5923832f75SSepherosa Ziehau 	struct ksensor		dimm_sensor;
6023832f75SSepherosa Ziehau };
6123832f75SSepherosa Ziehau 
6250193170SSepherosa Ziehau struct ecc_e5_rank {
6323832f75SSepherosa Ziehau 	struct ecc_e5_dimm *rank_dimm_sc;
6450193170SSepherosa Ziehau };
6550193170SSepherosa Ziehau 
6650193170SSepherosa Ziehau struct ecc_e5_softc {
6750193170SSepherosa Ziehau 	device_t		ecc_dev;
680c543cddSSepherosa Ziehau 	const struct e5_imc_chan *ecc_chan;
6950193170SSepherosa Ziehau 	int			ecc_node;
7050193170SSepherosa Ziehau 	int			ecc_rank_cnt;
7150193170SSepherosa Ziehau 	struct ecc_e5_rank	ecc_rank[PCI_E5_IMC_ERROR_RANK_MAX];
72e9a2e4daSSepherosa Ziehau 	struct sensor_task	*ecc_senstask;
7323832f75SSepherosa Ziehau 	TAILQ_HEAD(, ecc_e5_dimm) ecc_dimm;
7450193170SSepherosa Ziehau };
7550193170SSepherosa Ziehau 
7650193170SSepherosa Ziehau #define ecc_printf(sc, fmt, arg...) \
7750193170SSepherosa Ziehau 	device_printf((sc)->ecc_dev, fmt , ##arg)
7850193170SSepherosa Ziehau 
7950193170SSepherosa Ziehau static int	ecc_e5_probe(device_t);
8050193170SSepherosa Ziehau static int	ecc_e5_attach(device_t);
8150193170SSepherosa Ziehau static int	ecc_e5_detach(device_t);
8250193170SSepherosa Ziehau static void	ecc_e5_shutdown(device_t);
8350193170SSepherosa Ziehau 
8423832f75SSepherosa Ziehau static void	ecc_e5_sensor_task(void *);
8550193170SSepherosa Ziehau 
860c543cddSSepherosa Ziehau #define ECC_E5_CHAN(v, imc, c, c_ext)				\
8750193170SSepherosa Ziehau {								\
880c543cddSSepherosa Ziehau 	.did		= PCI_E5V##v##_IMC##imc##_ERROR_CHN##c##_DID_ID, \
890c543cddSSepherosa Ziehau 	.slot		= PCISLOT_E5V##v##_IMC##imc##_ERROR_CHN##c, \
900c543cddSSepherosa Ziehau 	.func		= PCIFUNC_E5V##v##_IMC##imc##_ERROR_CHN##c, \
910c543cddSSepherosa Ziehau 	.desc		= "Intel E5 v" #v " ECC",		\
920c543cddSSepherosa Ziehau 								\
930c543cddSSepherosa Ziehau 	E5_IMC_CHAN_FIELDS(v, imc, c, c_ext)			\
9450193170SSepherosa Ziehau }
9550193170SSepherosa Ziehau 
960c543cddSSepherosa Ziehau #define ECC_E5_CHAN_V2(c)		ECC_E5_CHAN(2, 0, c, c)
97acbc630dSSepherosa Ziehau #define ECC_E5_CHAN_IMC0_V3(c)		ECC_E5_CHAN(3, 0, c, c)
98acbc630dSSepherosa Ziehau #define ECC_E5_CHAN_IMC1_V3(c, c_ext)	ECC_E5_CHAN(3, 1, c, c_ext)
990c543cddSSepherosa Ziehau #define ECC_E5_CHAN_END			E5_IMC_CHAN_END
10050193170SSepherosa Ziehau 
1010c543cddSSepherosa Ziehau static const struct e5_imc_chan ecc_e5_chans[] = {
1020c543cddSSepherosa Ziehau 	ECC_E5_CHAN_V2(0),
1030c543cddSSepherosa Ziehau 	ECC_E5_CHAN_V2(1),
1040c543cddSSepherosa Ziehau 	ECC_E5_CHAN_V2(2),
1050c543cddSSepherosa Ziehau 	ECC_E5_CHAN_V2(3),
10650193170SSepherosa Ziehau 
107acbc630dSSepherosa Ziehau 	ECC_E5_CHAN_IMC0_V3(0),
108acbc630dSSepherosa Ziehau 	ECC_E5_CHAN_IMC0_V3(1),
109acbc630dSSepherosa Ziehau 	ECC_E5_CHAN_IMC0_V3(2),
110acbc630dSSepherosa Ziehau 	ECC_E5_CHAN_IMC0_V3(3),
111acbc630dSSepherosa Ziehau 	ECC_E5_CHAN_IMC1_V3(0, 2),	/* IMC1 chan0 -> channel2 */
112acbc630dSSepherosa Ziehau 	ECC_E5_CHAN_IMC1_V3(1, 3),	/* IMC1 chan1 -> channel3 */
113acbc630dSSepherosa Ziehau 
1140c543cddSSepherosa Ziehau 	ECC_E5_CHAN_END
11550193170SSepherosa Ziehau };
11650193170SSepherosa Ziehau 
1170c543cddSSepherosa Ziehau #undef ECC_E5_CHAN_END
1180c543cddSSepherosa Ziehau #undef ECC_E5_CHAN_V2
1190c543cddSSepherosa Ziehau #undef ECC_E5_CHAN
12050193170SSepherosa Ziehau 
12150193170SSepherosa Ziehau static device_method_t ecc_e5_methods[] = {
12250193170SSepherosa Ziehau 	/* Device interface */
12350193170SSepherosa Ziehau 	DEVMETHOD(device_probe,		ecc_e5_probe),
12450193170SSepherosa Ziehau 	DEVMETHOD(device_attach,	ecc_e5_attach),
12550193170SSepherosa Ziehau 	DEVMETHOD(device_detach,	ecc_e5_detach),
12650193170SSepherosa Ziehau 	DEVMETHOD(device_shutdown,	ecc_e5_shutdown),
12750193170SSepherosa Ziehau 	DEVMETHOD(device_suspend,	bus_generic_suspend),
12850193170SSepherosa Ziehau 	DEVMETHOD(device_resume,	bus_generic_resume),
12950193170SSepherosa Ziehau 	DEVMETHOD_END
13050193170SSepherosa Ziehau };
13150193170SSepherosa Ziehau 
13250193170SSepherosa Ziehau static driver_t ecc_e5_driver = {
13350193170SSepherosa Ziehau 	"ecc",
13450193170SSepherosa Ziehau 	ecc_e5_methods,
13550193170SSepherosa Ziehau 	sizeof(struct ecc_e5_softc)
13650193170SSepherosa Ziehau };
13750193170SSepherosa Ziehau static devclass_t ecc_devclass;
13850193170SSepherosa Ziehau DRIVER_MODULE(ecc_e5, pci, ecc_e5_driver, ecc_devclass, NULL, NULL);
13950193170SSepherosa Ziehau MODULE_DEPEND(ecc_e5, pci, 1, 1, 1);
14023832f75SSepherosa Ziehau MODULE_DEPEND(ecc_e5, dimm, 1, 1, 1);
141*b8c69b44SSascha Wildner MODULE_VERSION(ecc_e5, 1);
14250193170SSepherosa Ziehau 
14350193170SSepherosa Ziehau static int
ecc_e5_probe(device_t dev)14450193170SSepherosa Ziehau ecc_e5_probe(device_t dev)
14550193170SSepherosa Ziehau {
1460c543cddSSepherosa Ziehau 	const struct e5_imc_chan *c;
14750193170SSepherosa Ziehau 	uint16_t vid, did;
14850193170SSepherosa Ziehau 	int slot, func;
14950193170SSepherosa Ziehau 
15050193170SSepherosa Ziehau 	vid = pci_get_vendor(dev);
1510c543cddSSepherosa Ziehau 	if (vid != PCI_E5_IMC_VID_ID)
15250193170SSepherosa Ziehau 		return ENXIO;
15350193170SSepherosa Ziehau 
15450193170SSepherosa Ziehau 	did = pci_get_device(dev);
15550193170SSepherosa Ziehau 	slot = pci_get_slot(dev);
15650193170SSepherosa Ziehau 	func = pci_get_function(dev);
15750193170SSepherosa Ziehau 
1580c543cddSSepherosa Ziehau 	for (c = ecc_e5_chans; c->desc != NULL; ++c) {
1590c543cddSSepherosa Ziehau 		if (c->did == did && c->slot == slot && c->func == func) {
16050193170SSepherosa Ziehau 			struct ecc_e5_softc *sc = device_get_softc(dev);
1610c543cddSSepherosa Ziehau 			int node;
16250193170SSepherosa Ziehau 
1630c543cddSSepherosa Ziehau 			node = e5_imc_node_probe(dev, c);
1640c543cddSSepherosa Ziehau 			if (node < 0)
16550193170SSepherosa Ziehau 				break;
16650193170SSepherosa Ziehau 
16723832f75SSepherosa Ziehau 			device_set_desc(dev, c->desc);
16850193170SSepherosa Ziehau 
1690c543cddSSepherosa Ziehau 			sc->ecc_chan = c;
17050193170SSepherosa Ziehau 			sc->ecc_node = node;
17150193170SSepherosa Ziehau 			return 0;
17250193170SSepherosa Ziehau 		}
17350193170SSepherosa Ziehau 	}
17450193170SSepherosa Ziehau 	return ENXIO;
17550193170SSepherosa Ziehau }
17650193170SSepherosa Ziehau 
17750193170SSepherosa Ziehau static int
ecc_e5_attach(device_t dev)17850193170SSepherosa Ziehau ecc_e5_attach(device_t dev)
17950193170SSepherosa Ziehau {
18050193170SSepherosa Ziehau 	struct ecc_e5_softc *sc = device_get_softc(dev);
181e9a2e4daSSepherosa Ziehau 	int dimm, rank, error, cpuid;
182e9a2e4daSSepherosa Ziehau 	const cpu_node_t *node;
18350193170SSepherosa Ziehau 	uint32_t mcmtr;
18450193170SSepherosa Ziehau 
18523832f75SSepherosa Ziehau 	TAILQ_INIT(&sc->ecc_dimm);
18650193170SSepherosa Ziehau 	sc->ecc_dev = dev;
18750193170SSepherosa Ziehau 
1880c543cddSSepherosa Ziehau 	mcmtr = IMC_CPGC_READ_4(sc->ecc_dev, sc->ecc_chan,
1890c543cddSSepherosa Ziehau 	    PCI_E5_IMC_CPGC_MCMTR);
19050193170SSepherosa Ziehau 	if (bootverbose) {
1910c543cddSSepherosa Ziehau 		if (sc->ecc_chan->ver == E5_IMC_CHAN_VER3 &&
1920c543cddSSepherosa Ziehau 		    (mcmtr & PCI_E5V3_IMC_CPGC_MCMTR_DDR4))
193d3b628e2SSepherosa Ziehau 			ecc_printf(sc, "DDR4\n");
19450193170SSepherosa Ziehau 		if (__SHIFTOUT(mcmtr, PCI_E5_IMC_CPGC_MCMTR_IMC_MODE) ==
1950c543cddSSepherosa Ziehau 		    PCI_E5_IMC_CPGC_MCMTR_IMC_MODE_DDR3) {
196d3b628e2SSepherosa Ziehau 			ecc_printf(sc, "native %s\n",
1970c543cddSSepherosa Ziehau 			    sc->ecc_chan->ver == E5_IMC_CHAN_VER2 ?
1980c543cddSSepherosa Ziehau 			    "DDR3" : "DDR");
1990c543cddSSepherosa Ziehau 		}
20050193170SSepherosa Ziehau 	}
20150193170SSepherosa Ziehau 
20250193170SSepherosa Ziehau 	rank = 0;
2030c543cddSSepherosa Ziehau 	for (dimm = 0; dimm < PCI_E5_IMC_CHN_DIMM_MAX; ++dimm) {
20423832f75SSepherosa Ziehau 		struct ecc_e5_dimm *dimm_sc;
20523832f75SSepherosa Ziehau 		struct ksensor *sens;
20650193170SSepherosa Ziehau 		const char *width;
207e4a417edSSepherosa Ziehau 		uint32_t dimmmtr;
20850193170SSepherosa Ziehau 		int rank_cnt, r;
20950193170SSepherosa Ziehau 		int density;
21050193170SSepherosa Ziehau 		int val;
21150193170SSepherosa Ziehau 
212e4a417edSSepherosa Ziehau 		dimmmtr = IMC_CTAD_READ_4(sc->ecc_dev, sc->ecc_chan,
21350193170SSepherosa Ziehau 		    PCI_E5_IMC_CTAD_DIMMMTR(dimm));
21450193170SSepherosa Ziehau 
215e4a417edSSepherosa Ziehau 		if ((dimmmtr & PCI_E5_IMC_CTAD_DIMMMTR_DIMM_POP) == 0)
21650193170SSepherosa Ziehau 			continue;
21750193170SSepherosa Ziehau 
218e4a417edSSepherosa Ziehau 		val = __SHIFTOUT(dimmmtr, PCI_E5_IMC_CTAD_DIMMMTR_RANK_CNT);
21950193170SSepherosa Ziehau 		switch (val) {
22050193170SSepherosa Ziehau 		case PCI_E5_IMC_CTAD_DIMMMTR_RANK_CNT_SR:
22150193170SSepherosa Ziehau 			rank_cnt = 1;
22250193170SSepherosa Ziehau 			break;
22350193170SSepherosa Ziehau 		case PCI_E5_IMC_CTAD_DIMMMTR_RANK_CNT_DR:
22450193170SSepherosa Ziehau 			rank_cnt = 2;
22550193170SSepherosa Ziehau 			break;
22650193170SSepherosa Ziehau 		case PCI_E5_IMC_CTAD_DIMMMTR_RANK_CNT_QR:
22750193170SSepherosa Ziehau 			rank_cnt = 4;
22850193170SSepherosa Ziehau 			break;
2290c543cddSSepherosa Ziehau 		case PCI_E5V3_IMC_CTAD_DIMMMTR_RANK_CNT_8R:
2300c543cddSSepherosa Ziehau 			if (sc->ecc_chan->ver >= E5_IMC_CHAN_VER3) {
2310c543cddSSepherosa Ziehau 				rank_cnt = 8;
2320c543cddSSepherosa Ziehau 				break;
2330c543cddSSepherosa Ziehau 			}
2340c543cddSSepherosa Ziehau 			/* FALL THROUGH */
23550193170SSepherosa Ziehau 		default:
23650193170SSepherosa Ziehau 			ecc_printf(sc, "unknown rank count 0x%x\n", val);
23744ee2fbeSSepherosa Ziehau 			error = ENXIO;
23844ee2fbeSSepherosa Ziehau 			goto failed;
23950193170SSepherosa Ziehau 		}
24050193170SSepherosa Ziehau 
241e4a417edSSepherosa Ziehau 		val = __SHIFTOUT(dimmmtr, PCI_E5_IMC_CTAD_DIMMMTR_DDR3_WIDTH);
24250193170SSepherosa Ziehau 		switch (val) {
24350193170SSepherosa Ziehau 		case PCI_E5_IMC_CTAD_DIMMMTR_DDR3_WIDTH_4:
24450193170SSepherosa Ziehau 			width = "x4";
24550193170SSepherosa Ziehau 			break;
24650193170SSepherosa Ziehau 		case PCI_E5_IMC_CTAD_DIMMMTR_DDR3_WIDTH_8:
24750193170SSepherosa Ziehau 			width = "x8";
24850193170SSepherosa Ziehau 			break;
24950193170SSepherosa Ziehau 		case PCI_E5_IMC_CTAD_DIMMMTR_DDR3_WIDTH_16:
25050193170SSepherosa Ziehau 			width = "x16";
25150193170SSepherosa Ziehau 			break;
25250193170SSepherosa Ziehau 		default:
25350193170SSepherosa Ziehau 			ecc_printf(sc, "unknown ddr3 width 0x%x\n", val);
25444ee2fbeSSepherosa Ziehau 			error = ENXIO;
25544ee2fbeSSepherosa Ziehau 			goto failed;
25650193170SSepherosa Ziehau 		}
25750193170SSepherosa Ziehau 
258e4a417edSSepherosa Ziehau 		val = __SHIFTOUT(dimmmtr, PCI_E5_IMC_CTAD_DIMMMTR_DDR3_DNSTY);
25950193170SSepherosa Ziehau 		switch (val) {
26050193170SSepherosa Ziehau 		case PCI_E5_IMC_CTAD_DIMMMTR_DDR3_DNSTY_2G:
26150193170SSepherosa Ziehau 			density = 2;
26250193170SSepherosa Ziehau 			break;
26350193170SSepherosa Ziehau 		case PCI_E5_IMC_CTAD_DIMMMTR_DDR3_DNSTY_4G:
26450193170SSepherosa Ziehau 			density = 4;
26550193170SSepherosa Ziehau 			break;
26650193170SSepherosa Ziehau 		case PCI_E5_IMC_CTAD_DIMMMTR_DDR3_DNSTY_8G:
26750193170SSepherosa Ziehau 			density = 8;
26850193170SSepherosa Ziehau 			break;
2690c543cddSSepherosa Ziehau 		case PCI_E5_IMC_CTAD_DIMMMTR_DDR3_DNSTY_1G:
2700c543cddSSepherosa Ziehau 			if (sc->ecc_chan->ver < E5_IMC_CHAN_VER3) {
2710c543cddSSepherosa Ziehau 				density = 1;
2720c543cddSSepherosa Ziehau 				break;
2730c543cddSSepherosa Ziehau 			}
2740c543cddSSepherosa Ziehau 			/* FALL THROUGH */
27550193170SSepherosa Ziehau 		default:
27650193170SSepherosa Ziehau 			ecc_printf(sc, "unknown ddr3 density 0x%x\n", val);
27744ee2fbeSSepherosa Ziehau 			error = ENXIO;
27844ee2fbeSSepherosa Ziehau 			goto failed;
27950193170SSepherosa Ziehau 		}
28050193170SSepherosa Ziehau 
28150193170SSepherosa Ziehau 		if (bootverbose) {
28250193170SSepherosa Ziehau 			ecc_printf(sc, "DIMM%d %dGB, %d%s, density %dGB\n",
28350193170SSepherosa Ziehau 			    dimm, density * rank_cnt * 2,
28450193170SSepherosa Ziehau 			    rank_cnt, width, density);
28550193170SSepherosa Ziehau 		}
28650193170SSepherosa Ziehau 
28723832f75SSepherosa Ziehau 		dimm_sc = kmalloc(sizeof(*dimm_sc), M_DEVBUF,
28823832f75SSepherosa Ziehau 		    M_WAITOK | M_ZERO);
28923832f75SSepherosa Ziehau 		dimm_sc->dimm_softc =
29023832f75SSepherosa Ziehau 		    dimm_create(sc->ecc_node, sc->ecc_chan->chan_ext, dimm);
29123832f75SSepherosa Ziehau 
29223832f75SSepherosa Ziehau 		sens = &dimm_sc->dimm_sensor;
29323832f75SSepherosa Ziehau 		ksnprintf(sens->desc, sizeof(sens->desc),
29423832f75SSepherosa Ziehau 		    "node%d chan%d DIMM%d ecc",
29523832f75SSepherosa Ziehau 		    sc->ecc_node, sc->ecc_chan->chan_ext, dimm);
29610cf3bfcSSepherosa Ziehau 		sens->type = SENSOR_ECC;
2977273d7b8SSepherosa Ziehau 		sensor_set(sens, 0, SENSOR_S_OK);
29823832f75SSepherosa Ziehau 		dimm_sensor_attach(dimm_sc->dimm_softc, sens);
29923832f75SSepherosa Ziehau 
30023832f75SSepherosa Ziehau 		TAILQ_INSERT_TAIL(&sc->ecc_dimm, dimm_sc, dimm_link);
30123832f75SSepherosa Ziehau 
30250193170SSepherosa Ziehau 		for (r = 0; r < rank_cnt; ++r) {
30350193170SSepherosa Ziehau 			struct ecc_e5_rank *rk;
30450193170SSepherosa Ziehau 
30550193170SSepherosa Ziehau 			if (rank >= PCI_E5_IMC_ERROR_RANK_MAX) {
30650193170SSepherosa Ziehau 				ecc_printf(sc, "too many ranks\n");
30744ee2fbeSSepherosa Ziehau 				error = ENXIO;
30844ee2fbeSSepherosa Ziehau 				goto failed;
30950193170SSepherosa Ziehau 			}
310adb2b6d6SSepherosa Ziehau 
31150193170SSepherosa Ziehau 			rk = &sc->ecc_rank[rank];
31223832f75SSepherosa Ziehau 			rk->rank_dimm_sc = dimm_sc;
31350193170SSepherosa Ziehau 			++rank;
31450193170SSepherosa Ziehau 		}
31550193170SSepherosa Ziehau 	}
31650193170SSepherosa Ziehau 	sc->ecc_rank_cnt = rank;
31750193170SSepherosa Ziehau 
31850193170SSepherosa Ziehau 	if ((mcmtr & PCI_E5_IMC_CPGC_MCMTR_ECC_EN) == 0) {
31950193170SSepherosa Ziehau 		ecc_printf(sc, "ECC is not enabled\n");
32050193170SSepherosa Ziehau 		return 0;
32150193170SSepherosa Ziehau 	}
32250193170SSepherosa Ziehau 
32350193170SSepherosa Ziehau 	for (rank = 0; rank < sc->ecc_rank_cnt; ++rank) {
32450193170SSepherosa Ziehau 		const struct ecc_e5_rank *rk = &sc->ecc_rank[rank];
32550193170SSepherosa Ziehau 		uint32_t thr, mask;
32650193170SSepherosa Ziehau 		int ofs;
32750193170SSepherosa Ziehau 
32850193170SSepherosa Ziehau 		ofs = PCI_E5_IMC_ERROR_COR_ERR_TH(rank / 2);
32950193170SSepherosa Ziehau 		if (rank & 1)
33050193170SSepherosa Ziehau 			mask = PCI_E5_IMC_ERROR_COR_ERR_TH_HI;
33150193170SSepherosa Ziehau 		else
3320c543cddSSepherosa Ziehau 			mask = PCI_E5_IMC_ERROR_COR_ERR_TH_LO;
33350193170SSepherosa Ziehau 
33450193170SSepherosa Ziehau 		thr = pci_read_config(sc->ecc_dev, ofs, 4);
3357ee0de79SSepherosa Ziehau 		dimm_set_ecc_thresh(rk->rank_dimm_sc->dimm_softc,
33650193170SSepherosa Ziehau 		    __SHIFTOUT(thr, mask));
33750193170SSepherosa Ziehau 	}
33850193170SSepherosa Ziehau 
339e9a2e4daSSepherosa Ziehau 	cpuid = -1;
340e9a2e4daSSepherosa Ziehau 	node = get_cpu_node_by_chipid(sc->ecc_node);
341e9a2e4daSSepherosa Ziehau 	if (node != NULL && node->child_no > 0) {
342e9a2e4daSSepherosa Ziehau 		cpuid = BSRCPUMASK(node->members);
343e9a2e4daSSepherosa Ziehau 		if (bootverbose) {
344e9a2e4daSSepherosa Ziehau 			device_printf(dev, "node%d chan%d -> cpu%d\n",
345e9a2e4daSSepherosa Ziehau 			    sc->ecc_node, sc->ecc_chan->chan_ext, cpuid);
346e9a2e4daSSepherosa Ziehau 		}
347e9a2e4daSSepherosa Ziehau 	}
348e9a2e4daSSepherosa Ziehau 	sc->ecc_senstask = sensor_task_register2(sc, ecc_e5_sensor_task,
349e9a2e4daSSepherosa Ziehau 	    1, cpuid);
350e9a2e4daSSepherosa Ziehau 
35150193170SSepherosa Ziehau 	return 0;
35244ee2fbeSSepherosa Ziehau failed:
35344ee2fbeSSepherosa Ziehau 	ecc_e5_detach(dev);
35444ee2fbeSSepherosa Ziehau 	return error;
35550193170SSepherosa Ziehau }
35650193170SSepherosa Ziehau 
35750193170SSepherosa Ziehau static void
ecc_e5_sensor_task(void * xsc)35823832f75SSepherosa Ziehau ecc_e5_sensor_task(void *xsc)
35950193170SSepherosa Ziehau {
36050193170SSepherosa Ziehau 	struct ecc_e5_softc *sc = xsc;
36150193170SSepherosa Ziehau 	uint32_t err_ranks, val;
36250193170SSepherosa Ziehau 
36350193170SSepherosa Ziehau 	val = pci_read_config(sc->ecc_dev, PCI_E5_IMC_ERROR_COR_ERR_STAT, 4);
36450193170SSepherosa Ziehau 
36550193170SSepherosa Ziehau 	err_ranks = (val & PCI_E5_IMC_ERROR_COR_ERR_STAT_RANKS);
36650193170SSepherosa Ziehau 	while (err_ranks != 0) {
36750193170SSepherosa Ziehau 		int rank;
36850193170SSepherosa Ziehau 
36950193170SSepherosa Ziehau 		rank = ffs(err_ranks) - 1;
37050193170SSepherosa Ziehau 		err_ranks &= ~(1 << rank);
37150193170SSepherosa Ziehau 
37250193170SSepherosa Ziehau 		if (rank < sc->ecc_rank_cnt) {
37350193170SSepherosa Ziehau 			const struct ecc_e5_rank *rk = &sc->ecc_rank[rank];
37423832f75SSepherosa Ziehau 			struct ecc_e5_dimm *dimm_sc = rk->rank_dimm_sc;
37550193170SSepherosa Ziehau 			uint32_t err, mask;
37623832f75SSepherosa Ziehau 			int ofs, ecc_cnt;
37750193170SSepherosa Ziehau 
37850193170SSepherosa Ziehau 			ofs = PCI_E5_IMC_ERROR_COR_ERR_CNT(rank / 2);
37950193170SSepherosa Ziehau 			if (rank & 1)
38050193170SSepherosa Ziehau 				mask = PCI_E5_IMC_ERROR_COR_ERR_CNT_HI;
38150193170SSepherosa Ziehau 			else
38250193170SSepherosa Ziehau 				mask = PCI_E5_IMC_ERROR_COR_ERR_CNT_LO;
38350193170SSepherosa Ziehau 
38450193170SSepherosa Ziehau 			err = pci_read_config(sc->ecc_dev, ofs, 4);
38523832f75SSepherosa Ziehau 			ecc_cnt = __SHIFTOUT(err, mask);
38623832f75SSepherosa Ziehau 
38723832f75SSepherosa Ziehau 			dimm_sensor_ecc_set(dimm_sc->dimm_softc,
38823832f75SSepherosa Ziehau 			    &dimm_sc->dimm_sensor, ecc_cnt, TRUE);
3898d0c1205SSepherosa Ziehau 		} else {
3908d0c1205SSepherosa Ziehau 			ecc_printf(sc, "channel%d rank%d critical error\n",
3918d0c1205SSepherosa Ziehau 			    sc->ecc_chan->chan_ext, rank);
39250193170SSepherosa Ziehau 		}
39350193170SSepherosa Ziehau 	}
39450193170SSepherosa Ziehau 
39550193170SSepherosa Ziehau 	if (val & PCI_E5_IMC_ERROR_COR_ERR_STAT_RANKS) {
39650193170SSepherosa Ziehau 		pci_write_config(sc->ecc_dev, PCI_E5_IMC_ERROR_COR_ERR_STAT,
39750193170SSepherosa Ziehau 		    val, 4);
39850193170SSepherosa Ziehau 	}
39950193170SSepherosa Ziehau }
40050193170SSepherosa Ziehau 
40150193170SSepherosa Ziehau static void
ecc_e5_stop(device_t dev)40250193170SSepherosa Ziehau ecc_e5_stop(device_t dev)
40350193170SSepherosa Ziehau {
40450193170SSepherosa Ziehau 	struct ecc_e5_softc *sc = device_get_softc(dev);
40550193170SSepherosa Ziehau 
406e9a2e4daSSepherosa Ziehau 	if (sc->ecc_senstask != NULL) {
407e9a2e4daSSepherosa Ziehau 		sensor_task_unregister2(sc->ecc_senstask);
408e9a2e4daSSepherosa Ziehau 		sc->ecc_senstask = NULL;
409e9a2e4daSSepherosa Ziehau 	}
41050193170SSepherosa Ziehau }
41150193170SSepherosa Ziehau 
41250193170SSepherosa Ziehau static int
ecc_e5_detach(device_t dev)41350193170SSepherosa Ziehau ecc_e5_detach(device_t dev)
41450193170SSepherosa Ziehau {
41523832f75SSepherosa Ziehau 	struct ecc_e5_softc *sc = device_get_softc(dev);
41623832f75SSepherosa Ziehau 	struct ecc_e5_dimm *dimm_sc;
41723832f75SSepherosa Ziehau 
41850193170SSepherosa Ziehau 	ecc_e5_stop(dev);
41923832f75SSepherosa Ziehau 
42023832f75SSepherosa Ziehau 	while ((dimm_sc = TAILQ_FIRST(&sc->ecc_dimm)) != NULL) {
42123832f75SSepherosa Ziehau 		TAILQ_REMOVE(&sc->ecc_dimm, dimm_sc, dimm_link);
42223832f75SSepherosa Ziehau 		dimm_sensor_detach(dimm_sc->dimm_softc, &dimm_sc->dimm_sensor);
42323832f75SSepherosa Ziehau 		dimm_destroy(dimm_sc->dimm_softc);
42423832f75SSepherosa Ziehau 
42523832f75SSepherosa Ziehau 		kfree(dimm_sc, M_DEVBUF);
42623832f75SSepherosa Ziehau 	}
42750193170SSepherosa Ziehau 	return 0;
42850193170SSepherosa Ziehau }
42950193170SSepherosa Ziehau 
43050193170SSepherosa Ziehau static void
ecc_e5_shutdown(device_t dev)43150193170SSepherosa Ziehau ecc_e5_shutdown(device_t dev)
43250193170SSepherosa Ziehau {
43350193170SSepherosa Ziehau 	ecc_e5_stop(dev);
43450193170SSepherosa Ziehau }
435