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