xref: /openbsd-src/sys/dev/pci/amas.c (revision 8d2c75e49a1f1e140a0efd2b93f23844e8cb679f)
1*8d2c75e4Smpi /*	$OpenBSD: amas.c,v 1.7 2022/03/11 18:00:45 mpi Exp $	*/
264e6b3bbSariane 
364e6b3bbSariane /*
464e6b3bbSariane  * Copyright (c) 2009 Ariane van der Steldt <ariane@stack.nl>
564e6b3bbSariane  *
664e6b3bbSariane  * Permission to use, copy, modify, and distribute this software for any
764e6b3bbSariane  * purpose with or without fee is hereby granted, provided that the above
864e6b3bbSariane  * copyright notice and this permission notice appear in all copies.
964e6b3bbSariane  *
1064e6b3bbSariane  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1164e6b3bbSariane  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1264e6b3bbSariane  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1364e6b3bbSariane  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1464e6b3bbSariane  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1564e6b3bbSariane  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1664e6b3bbSariane  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1764e6b3bbSariane  */
1864e6b3bbSariane 
1964e6b3bbSariane /*
2064e6b3bbSariane  * Device: amas (AMD memory access/address switch).
2164e6b3bbSariane  *
2264e6b3bbSariane  * Driver for the amd athlon/opteron 64 address map.
2364e6b3bbSariane  * This device is integrated in 64-bit Athlon and Opteron cpus
2464e6b3bbSariane  * and contains mappings for memory to processor nodes.
2564e6b3bbSariane  */
2664e6b3bbSariane 
2764e6b3bbSariane #include <dev/pci/amas.h>
2864e6b3bbSariane 
2964e6b3bbSariane #include <sys/param.h>
3064e6b3bbSariane #include <sys/systm.h>
3164e6b3bbSariane #include <sys/device.h>
3264e6b3bbSariane 
3364e6b3bbSariane #include <dev/pci/pcivar.h>
3464e6b3bbSariane #include <dev/pci/pcireg.h>
3564e6b3bbSariane #include <dev/pci/pcidevs.h>
3664e6b3bbSariane 
3764e6b3bbSariane int amas_match(struct device*, void*, void*);
3864e6b3bbSariane void amas_attach(struct device*, struct device*, void*);
3964e6b3bbSariane 
4064e6b3bbSariane /*
4164e6b3bbSariane  * Amas device layout:
4264e6b3bbSariane  *
4364e6b3bbSariane  * - base/limit registers (on 0x0f, 0x10, 0x11)
4464e6b3bbSariane  * - extended base/limit registers (on 0x10)
4564e6b3bbSariane  *
4664e6b3bbSariane  * 0x0f, 0x10 support up to 8 nodes
4764e6b3bbSariane  * 0x11 supports up to 1 nodes
4864e6b3bbSariane  *
4964e6b3bbSariane  * base/limit registers use bits [31..16] to indicate address [39..24]
5064e6b3bbSariane  * extended base/limit registers use bits [7..0] to indicate address [47..40]
5164e6b3bbSariane  * base/limit addresses need to be shifted <<24 for memory address
5264e6b3bbSariane  * extended base/limit addresses need to be shifted <<40 for memory address
5364e6b3bbSariane  */
5464e6b3bbSariane 
5564e6b3bbSariane #define AMAS_REG_BASE(node)	(0x0040 + 0x08 * (node))
5664e6b3bbSariane #define AMAS_REG_LIMIT(node)	(0x0044 + 0x08 * (node))
5764e6b3bbSariane #define AMAS_REG_EXTBASE(node)	(0x0140 + 0x08 * (node))
5864e6b3bbSariane #define AMAS_REG_EXTLIMIT(node)	(0x0144 + 0x08 * (node))
5964e6b3bbSariane 
6064e6b3bbSariane #define AMAS_REG_BL_ADDR(reg)	(((reg) >> 16) & 0xffff)
6164e6b3bbSariane #define AMAS_REG_EBL_ADDR(ereg)	((ereg) & 0xff)
6264e6b3bbSariane 
6364e6b3bbSariane #define AMAS_REG_BL_SHIFT	(24)
6464e6b3bbSariane #define AMAS_REG_EBL_SHIFT	(40)
6564e6b3bbSariane 
6664e6b3bbSariane #define AMAS_REG_BL_PGSHIFT	(AMAS_REG_BL_SHIFT - PAGE_SHIFT)
6764e6b3bbSariane #define AMAS_REG_EBL_PGSHIFT	(AMAS_REG_EBL_SHIFT - PAGE_SHIFT)
6864e6b3bbSariane 
6964e6b3bbSariane /*
7064e6b3bbSariane  * Convert an address in amas to a page number.
7164e6b3bbSariane  *
7264e6b3bbSariane  * The device uses an inclusive mapping, where the upper bound address
7364e6b3bbSariane  * must be all 1's after shifting.
7464e6b3bbSariane  * The device driver uses C-style array indices, hence the +1 in the _LIMIT
7564e6b3bbSariane  * macro.
7664e6b3bbSariane  */
7764e6b3bbSariane #define AMAS_ADDR2PAGE_BASE(base, ebase)				\
7864e6b3bbSariane     (((base) << AMAS_REG_BL_PGSHIFT) | ((ebase) << AMAS_REG_EBL_PGSHIFT))
7964e6b3bbSariane #define AMAS_ADDR2PAGE_LIMIT(base, ebase)				\
8064e6b3bbSariane     (((base + 1) << AMAS_REG_BL_PGSHIFT) | ((ebase) << AMAS_REG_EBL_PGSHIFT))
8164e6b3bbSariane 
8264e6b3bbSariane /*
8364e6b3bbSariane  * Node and interleave description.
8464e6b3bbSariane  * - base contains node selection [10..8] (on 0x0f, 0x10)
8564e6b3bbSariane  * - limit contains node selection bitmask [10..8] (on 0x0f, 0x10)
8664e6b3bbSariane  * - limit contains destination node [2..0] (on 0x0f, 0x10)
8764e6b3bbSariane  */
8864e6b3bbSariane #define AMAS_DST_NODE(base, limit)	((limit) & 0x07)
8964e6b3bbSariane #define AMAS_INTL_ENABLE(base, limit)	(((base) >> 8) & 0x07)
9064e6b3bbSariane #define AMAS_INTL_SELECTOR(base, limit)	(((limit) >> 8) & 0x07)
9164e6b3bbSariane 
9264e6b3bbSariane /*
9364e6b3bbSariane  * Defines for family.
9464e6b3bbSariane  * Corresponds to the amas_feature[] constant below.
9564e6b3bbSariane  */
9664e6b3bbSariane #define AMAS_FAM_0Fh		(0)
9764e6b3bbSariane #define AMAS_FAM_10h		(1)
9864e6b3bbSariane #define AMAS_FAM_11h		(2)
9964e6b3bbSariane 
10064e6b3bbSariane /*
10164e6b3bbSariane  * Feature tests.
10264e6b3bbSariane  *
10364e6b3bbSariane  * 0x11 supports at max 1 node, 0x0f and 0x10 support up to 8 nodes.
10464e6b3bbSariane  * 0x11 has extended address registers.
10564e6b3bbSariane  * 0x0f, 0x10 can interleave memory.
10664e6b3bbSariane  */
10764e6b3bbSariane struct amas_feature_t {
10864e6b3bbSariane 	int maxnodes;
10964e6b3bbSariane 	int can_intl;
11064e6b3bbSariane 	int has_extended_bl;
11164e6b3bbSariane };
11264e6b3bbSariane static const struct amas_feature_t amas_feature[] = {
11364e6b3bbSariane 	/* Family 0x0f */
11464e6b3bbSariane 	{ 8, 1, 0 },
11564e6b3bbSariane 	/* Family 0x10 */
11664e6b3bbSariane 	{ 8, 1, 1 },
11764e6b3bbSariane 	/* Family 0x11 */
11864e6b3bbSariane 	{ 1, 0, 0 },
11964e6b3bbSariane };
12064e6b3bbSariane 
12164e6b3bbSariane /* Probe code. */
122*8d2c75e4Smpi const struct cfattach amas_ca = {
12364e6b3bbSariane 	sizeof(struct amas_softc),
12464e6b3bbSariane 	amas_match,
12564e6b3bbSariane 	amas_attach
12664e6b3bbSariane };
12764e6b3bbSariane 
12864e6b3bbSariane struct cfdriver amas_cd = {
12964e6b3bbSariane 	NULL,
13064e6b3bbSariane 	"amas",
13164e6b3bbSariane 	DV_DULL
13264e6b3bbSariane };
13364e6b3bbSariane 
13464e6b3bbSariane const struct pci_matchid amas_devices[] = {
1353ddeb659Sjsg 	{ PCI_VENDOR_AMD, PCI_PRODUCT_AMD_0F_ADDR },
1363ddeb659Sjsg 	{ PCI_VENDOR_AMD, PCI_PRODUCT_AMD_10_ADDR },
1373ddeb659Sjsg 	{ PCI_VENDOR_AMD, PCI_PRODUCT_AMD_11_ADDR },
13864e6b3bbSariane };
13964e6b3bbSariane 
14064e6b3bbSariane int
amas_match(struct device * parent,void * match,void * aux)14164e6b3bbSariane amas_match(struct device *parent, void *match, void *aux)
14264e6b3bbSariane {
14364e6b3bbSariane 	struct pci_attach_args* pa = aux;
14464e6b3bbSariane 
14564e6b3bbSariane 	if (pci_matchbyid(pa, amas_devices, nitems(amas_devices)))
14664e6b3bbSariane 		return 2; /* override pchb */
14764e6b3bbSariane 	return 0;
14864e6b3bbSariane }
14964e6b3bbSariane 
15064e6b3bbSariane void
amas_attach(struct device * parent,struct device * self,void * aux)15164e6b3bbSariane amas_attach(struct device *parent, struct device *self, void *aux)
15264e6b3bbSariane {
15364e6b3bbSariane 	struct pci_attach_args *pa = aux;
15464e6b3bbSariane 	struct amas_softc *amas = (struct amas_softc*)self;
15564e6b3bbSariane #ifdef DEBUG
15664e6b3bbSariane 	paddr_t start_pg, end_pg;
15764e6b3bbSariane 	int nodes, i;
15864e6b3bbSariane #endif /* DEBUG */
15964e6b3bbSariane 
16064e6b3bbSariane 	amas->pa_tag = pa->pa_tag;
16164e6b3bbSariane 	amas->pa_pc = pa->pa_pc;
16264e6b3bbSariane 
16364e6b3bbSariane 	switch (PCI_PRODUCT(pa->pa_id)) {
1643ddeb659Sjsg 	case PCI_PRODUCT_AMD_0F_ADDR:
16564e6b3bbSariane 		amas->family = AMAS_FAM_0Fh;
16664e6b3bbSariane 		break;
1673ddeb659Sjsg 	case PCI_PRODUCT_AMD_10_ADDR:
16864e6b3bbSariane 		amas->family = AMAS_FAM_10h;
16964e6b3bbSariane 		break;
1703ddeb659Sjsg 	case PCI_PRODUCT_AMD_11_ADDR:
17164e6b3bbSariane 		amas->family = AMAS_FAM_11h;
17264e6b3bbSariane 		break;
17364e6b3bbSariane 	}
17464e6b3bbSariane 
17564e6b3bbSariane #ifdef DEBUG
17664e6b3bbSariane 	nodes = amas_intl_nodes(amas);
17764e6b3bbSariane 
17864e6b3bbSariane 	printf(":");
17964e6b3bbSariane 	if (nodes != 0) {
180ab859becSariane 		printf(" interleaved");
18164e6b3bbSariane 	} else {
18264e6b3bbSariane 		for (i = 0; i < AMAS_MAX_NODES; i++) {
18364e6b3bbSariane 			amas_get_pagerange(amas, i, &start_pg, &end_pg);
18464e6b3bbSariane 
185a54890aaSariane 			if (!(start_pg == 0 && end_pg == 0))
1862f436acaSsf 				printf(" [%#lx, %#lx]", start_pg, end_pg);
18764e6b3bbSariane 		}
18864e6b3bbSariane 	}
18964e6b3bbSariane #endif /* DEBUG */
19064e6b3bbSariane 	printf("\n");
19164e6b3bbSariane 
19264e6b3bbSariane 	return;
19364e6b3bbSariane }
19464e6b3bbSariane 
19564e6b3bbSariane /*
19664e6b3bbSariane  * Returns the number of nodes across which the memory is interleaved.
19764e6b3bbSariane  * Returns 0 if the memory is not interleaved.
19864e6b3bbSariane  */
19964e6b3bbSariane int
amas_intl_nodes(struct amas_softc * amas)20064e6b3bbSariane amas_intl_nodes(struct amas_softc *amas)
20164e6b3bbSariane {
20264e6b3bbSariane 	pcireg_t base_reg, limit_reg;
20364e6b3bbSariane 	int mask;
20464e6b3bbSariane 
20564e6b3bbSariane 	if (!amas_feature[amas->family].can_intl)
20664e6b3bbSariane 		return 0;
20764e6b3bbSariane 
20864e6b3bbSariane 	/*
20964e6b3bbSariane 	 * Use node 0 on amas device to find interleave information.
21064e6b3bbSariane 	 * Node 0 is always present.
21164e6b3bbSariane 	 */
21264e6b3bbSariane 
21364e6b3bbSariane 	base_reg = pci_conf_read(amas->pa_pc, amas->pa_tag, AMAS_REG_BASE(0));
21464e6b3bbSariane 	limit_reg = pci_conf_read(amas->pa_pc, amas->pa_tag, AMAS_REG_LIMIT(0));
21564e6b3bbSariane 	mask = AMAS_INTL_ENABLE(base_reg, limit_reg);
21664e6b3bbSariane 
21764e6b3bbSariane 	return mask == 0 ? 0 : mask + 1;
21864e6b3bbSariane }
21964e6b3bbSariane 
22064e6b3bbSariane /*
22164e6b3bbSariane  * Returns the range of memory that is contained on the given node.
22264e6b3bbSariane  * If the memory is interleaved, the result is undefined.
22364e6b3bbSariane  *
22464e6b3bbSariane  * The range is written in {start,end}_pg_idx.
22564e6b3bbSariane  * Note that these are page numbers and that these use array indices:
22664e6b3bbSariane  * pages are in this range if start <= pg_no < end.
22764e6b3bbSariane  *
22864e6b3bbSariane  * This device supports at most 8 nodes.
22964e6b3bbSariane  */
23064e6b3bbSariane void
amas_get_pagerange(struct amas_softc * amas,int node,paddr_t * start_pg_idx,paddr_t * end_pg_idx)23164e6b3bbSariane amas_get_pagerange(struct amas_softc *amas, int node,
23264e6b3bbSariane     paddr_t *start_pg_idx, paddr_t *end_pg_idx)
23364e6b3bbSariane {
23464e6b3bbSariane 	pcireg_t base, ebase, limit, elimit;
23564e6b3bbSariane 	paddr_t base_addr, ebase_addr, limit_addr, elimit_addr;
23664e6b3bbSariane 
23764e6b3bbSariane 	/* Sanity check: max AMAS_MAX_NODES supported. */
23864e6b3bbSariane 	KASSERT(node >= 0 && node < AMAS_MAX_NODES);
23964e6b3bbSariane 
24064e6b3bbSariane 	if (node >= amas_feature[amas->family].maxnodes) {
24164e6b3bbSariane 		/* Unsupported node: bail out early. */
24264e6b3bbSariane 		*start_pg_idx = 0;
24364e6b3bbSariane 		*end_pg_idx = 0;
24464e6b3bbSariane 		return;
24564e6b3bbSariane 	}
24664e6b3bbSariane 
24764e6b3bbSariane 	base = pci_conf_read(amas->pa_pc, amas->pa_tag,
24864e6b3bbSariane 	    AMAS_REG_BASE(node));
24964e6b3bbSariane 	limit = pci_conf_read(amas->pa_pc, amas->pa_tag,
25064e6b3bbSariane 	    AMAS_REG_LIMIT(node));
25164e6b3bbSariane 	base_addr = AMAS_REG_BL_ADDR(base);
25264e6b3bbSariane 	limit_addr = AMAS_REG_BL_ADDR(limit);
25364e6b3bbSariane 
25464e6b3bbSariane 	ebase = 0;
25564e6b3bbSariane 	elimit = 0;
25664e6b3bbSariane 	ebase_addr = 0;
25764e6b3bbSariane 	elimit_addr = 0;
25864e6b3bbSariane #if 0 /* Needs extended pci registers. */
25964e6b3bbSariane 	if (amas_feature[amas->family].has_extended_bl) {
26064e6b3bbSariane 		ebase = pci_conf_read(amas->pa_pc, amas->pa_tag,
26164e6b3bbSariane 		    AMAS_REG_EXTBASE(node));
26264e6b3bbSariane 		elimit = pci_conf_read(amas->pa_pc, amas->pa_tag,
26364e6b3bbSariane 		    AMAS_REG_EXTLIMIT(node));
26464e6b3bbSariane 		ebase_addr = AMAS_REG_EBL_ADDR(ebase);
26564e6b3bbSariane 		elimit_addr = AMAS_REG_EBL_ADDR(elimit);
26664e6b3bbSariane 	}
26764e6b3bbSariane #endif /* 0 */
26864e6b3bbSariane 
269a4fe1f42Sariane 	if (ebase_addr > elimit_addr ||
270a4fe1f42Sariane 	    (ebase_addr == elimit_addr && base_addr >= limit_addr)) {
27164e6b3bbSariane 		/* no memory present */
27264e6b3bbSariane 		*start_pg_idx = 0;
27364e6b3bbSariane 		*end_pg_idx = 0;
27464e6b3bbSariane 		return;
27564e6b3bbSariane 	}
27664e6b3bbSariane 
27764e6b3bbSariane 	/* Guaranteed by spec. */
27864e6b3bbSariane 	KASSERT(node == AMAS_DST_NODE(base, limit));
27964e6b3bbSariane 
28064e6b3bbSariane 	*start_pg_idx = AMAS_ADDR2PAGE_BASE(base_addr, ebase_addr);
28164e6b3bbSariane 	*end_pg_idx = AMAS_ADDR2PAGE_LIMIT(limit_addr, elimit_addr);
28264e6b3bbSariane 	return;
28364e6b3bbSariane }
284