Module Name: src Committed By: skrll Date: Tue Dec 29 15:39:59 UTC 2020
Modified Files: src/sys/dev/pci: pci_map.c pciconf.c Log Message: Add support for Enhanced Allocations as seen in the Cavium ThunderX based GIGABYTE MT30-GS2-00 >From thorpej@. Thanks! To generate a diff of this commit: cvs rdiff -u -r1.41 -r1.42 src/sys/dev/pci/pci_map.c cvs rdiff -u -r1.50 -r1.51 src/sys/dev/pci/pciconf.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/pci/pci_map.c diff -u src/sys/dev/pci/pci_map.c:1.41 src/sys/dev/pci/pci_map.c:1.42 --- src/sys/dev/pci/pci_map.c:1.41 Mon Dec 28 12:38:44 2020 +++ src/sys/dev/pci/pci_map.c Tue Dec 29 15:39:59 2020 @@ -1,7 +1,7 @@ -/* $NetBSD: pci_map.c,v 1.41 2020/12/28 12:38:44 skrll Exp $ */ +/* $NetBSD: pci_map.c,v 1.42 2020/12/29 15:39:59 skrll Exp $ */ /*- - * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. + * Copyright (c) 1998, 2000, 2020 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -34,7 +34,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: pci_map.c,v 1.41 2020/12/28 12:38:44 skrll Exp $"); +__KERNEL_RCSID(0, "$NetBSD: pci_map.c,v 1.42 2020/12/29 15:39:59 skrll Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -244,6 +244,252 @@ pci_mem_find(pci_chipset_tag_t pc, pcita return 0; } +static const char * +bar_type_string(pcireg_t type) +{ + if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO) + return "IO"; + + switch (PCI_MAPREG_MEM_TYPE(type)) { + case PCI_MAPREG_MEM_TYPE_32BIT: + return "MEM32"; + case PCI_MAPREG_MEM_TYPE_32BIT_1M: + return "MEM32-1M"; + case PCI_MAPREG_MEM_TYPE_64BIT: + return "MEM64"; + } + return "<UNKNOWN>"; +} + +enum { + EA_ptr_dw0 = 0, + EA_ptr_base_lower = 1, + EA_ptr_base_upper = 2, + EA_ptr_maxoffset_lower = 3, + EA_ptr_maxoffset_upper = 4, + + EA_PTR_COUNT = 5 +}; + +struct pci_ea_entry { + /* entry field pointers */ + int ea_ptrs[EA_PTR_COUNT]; + + /* Raw register values. */ + pcireg_t dw0; + pcireg_t base_lower; + pcireg_t base_upper; + pcireg_t maxoffset_lower; + pcireg_t maxoffset_upper; + + /* Interesting tidbits derived from them. */ + uint64_t base; + uint64_t maxoffset; + unsigned int bei; + unsigned int props[2]; + bool base_is_64; + bool maxoffset_is_64; + bool enabled; + bool writable; +}; + +static int +pci_ea_lookup(pci_chipset_tag_t pc, pcitag_t tag, int ea_cap_ptr, + int reg, struct pci_ea_entry *entryp) +{ + struct pci_ea_entry entry = { + .ea_ptrs[EA_ptr_dw0] = ea_cap_ptr + 4, + }; + unsigned int i, num_entries; + unsigned int wanted_bei; + pcireg_t val; + + if (reg >= PCI_BAR0 && reg <= PCI_BAR5) + wanted_bei = PCI_EA_BEI_BAR0 + ((reg - PCI_BAR0) / 4); + else if (reg == PCI_MAPREG_ROM) + wanted_bei = PCI_EA_BEI_EXPROM; + else { + /* Invalid BAR. */ + return 1; + } + + val = pci_conf_read(pc, tag, ea_cap_ptr + PCI_EA_CAP1); + num_entries = __SHIFTOUT(val, PCI_EA_CAP1_NUMENTRIES); + + val = pci_conf_read(pc, tag, PCI_BHLC_REG); + if (PCI_HDRTYPE_TYPE(val) == PCI_HDRTYPE_PPB) { + /* Need to skip over PCI_EA_CAP2 on PPBs. */ + entry.ea_ptrs[EA_ptr_dw0] += 4; + } + + for (i = 0; i < num_entries; i++) { + val = pci_conf_read(pc, tag, entry.ea_ptrs[EA_ptr_dw0]); + unsigned int entry_size = __SHIFTOUT(val, PCI_EA_ES); + + entry.bei = __SHIFTOUT(val, PCI_EA_BEI); + entry.props[0] = __SHIFTOUT(val, PCI_EA_PP); + entry.props[1] = __SHIFTOUT(val, PCI_EA_SP); + entry.writable = (val & PCI_EA_W) ? true : false; + entry.enabled = (val & PCI_EA_E) ? true : false; + + if (entry.bei != wanted_bei || entry_size == 0) { + entry.ea_ptrs[EA_ptr_dw0] += 4 * (entry_size + 1); + continue; + } + + entry.ea_ptrs[EA_ptr_base_lower] = + entry.ea_ptrs[EA_ptr_dw0] + 4; + entry.ea_ptrs[EA_ptr_maxoffset_lower] = + entry.ea_ptrs[EA_ptr_dw0] + 8; + + /* Base */ + entry.base_lower = pci_conf_read(pc, tag, + entry.ea_ptrs[EA_ptr_base_lower]); + entry.base_is_64 = + (entry.base_lower & PCI_EA_BASEMAXOFFSET_64BIT) + ? true : false; + if (entry.base_is_64) { + entry.ea_ptrs[EA_ptr_base_upper] = + entry.ea_ptrs[EA_ptr_dw0] + 12; + entry.base_upper = pci_conf_read(pc, tag, + entry.ea_ptrs[EA_ptr_base_upper]); + } else { + entry.ea_ptrs[EA_ptr_base_upper] = 0; + entry.base_upper = 0; + } + + entry.base = (entry.base_lower & PCI_EA_LOWMASK) | + ((uint64_t)entry.base_upper << 32); + + /* MaxOffset */ + entry.maxoffset_lower = pci_conf_read(pc, tag, + entry.ea_ptrs[EA_ptr_maxoffset_lower]); + entry.maxoffset_is_64 = + (entry.maxoffset_lower & PCI_EA_BASEMAXOFFSET_64BIT) + ? true : false; + if (entry.maxoffset_is_64) { + entry.ea_ptrs[EA_ptr_maxoffset_upper] = + entry.ea_ptrs[EA_ptr_dw0] + + (entry.base_is_64 ? 16 : 12); + entry.maxoffset_upper = pci_conf_read(pc, tag, + entry.ea_ptrs[EA_ptr_maxoffset_upper]); + } else { + entry.ea_ptrs[EA_ptr_maxoffset_upper] = 0; + entry.maxoffset_upper = 0; + } + + entry.maxoffset = (entry.maxoffset_lower & PCI_EA_LOWMASK) | + ((uint64_t)entry.maxoffset_upper << 32); + + if (entryp) + *entryp = entry; + return 0; + } + return 1; +} + +static int +pci_ea_find(pci_chipset_tag_t pc, pcitag_t tag, int ea_cap_ptr, + int reg, pcireg_t type, bus_addr_t *basep, bus_size_t *sizep, int *flagsp, + struct pci_ea_entry *entryp) +{ + + struct pci_ea_entry entry; + int rv = pci_ea_lookup(pc, tag, ea_cap_ptr, reg, &entry); + if (rv) + return rv; + + pcireg_t wanted_type; + if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO) + wanted_type = PCI_MAPREG_TYPE_IO; + else { + /* + * This covers ROM as well. We allow any user-specified + * memory type to match an EA memory region with no regard + * for 32 vs. 64. + * + * XXX Should it? + */ + wanted_type = PCI_MAPREG_TYPE_MEM; + } + + /* + * MaxOffset is the last offset where you can issue a + * 32-bit read in the region. Therefore, the size of + * the region is MaxOffset + 4. + */ + uint64_t region_size = entry.maxoffset + 4; + + unsigned int which_prop; + for (which_prop = 0; which_prop < 2; which_prop++) { + int mapflags = 0; + + switch (entry.props[which_prop]) { + case PCI_EA_PROP_MEM_PREF: + mapflags |= BUS_SPACE_MAP_PREFETCHABLE; + /* FALLTHROUGH */ + case PCI_EA_PROP_MEM_NONPREF: + if (PCI_MAPREG_TYPE(wanted_type) != PCI_MAPREG_TYPE_MEM) + goto unexpected_type; + break; + + case PCI_EA_PROP_IO: + if (PCI_MAPREG_TYPE(wanted_type) != PCI_MAPREG_TYPE_IO) + goto unexpected_type; + break; + + case PCI_EA_PROP_MEM_UNAVAIL: + case PCI_EA_PROP_IO_UNAVAIL: + case PCI_EA_PROP_UNAVAIL: + return 1; + + /* XXX Don't support these yet. */ + case PCI_EA_PROP_VF_MEM_PREF: + case PCI_EA_PROP_VF_MEM_NONPREF: + case PCI_EA_PROP_BB_MEM_PREF: + case PCI_EA_PROP_BB_MEM_NONPREF: + case PCI_EA_PROP_BB_IO: + default: + printf("%s: bei %u props[%u]=0x%x\n", + __func__, entry.bei, which_prop, + entry.props[which_prop]); + continue; + continue; + } + + if ((sizeof(uint64_t) > sizeof(bus_addr_t) || + PCI_MAPREG_TYPE(wanted_type) == PCI_MAPREG_TYPE_IO) && + (entry.base + region_size) > 0x100000000ULL) { + goto inaccessible_64bit_region; + } + + *basep = (bus_addr_t)entry.base; + *sizep = (bus_size_t)region_size; + *flagsp = mapflags; + if (entryp) + *entryp = entry; + return 0; + } + + /* BAR not found. */ + return 1; + + unexpected_type: + printf("%s: unexpected type; wanted %s, got 0x%02x\n", + __func__, bar_type_string(wanted_type), entry.props[which_prop]); + return 1; + + inaccessible_64bit_region: + if (PCI_MAPREG_TYPE(wanted_type) == PCI_MAPREG_TYPE_IO) { + printf("%s: 64-bit IO regions are unsupported\n", + __func__); + return 1; + } + printf("%s: 64-bit memory region inaccessible on 32-bit platform\n", + __func__); + return 1; +} + #define _PCI_MAPREG_TYPEBITS(reg) \ (PCI_MAPREG_TYPE(reg) == PCI_MAPREG_TYPE_IO ? \ reg & PCI_MAPREG_TYPE_MASK : \ @@ -295,6 +541,11 @@ int pci_mapreg_info(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t type, bus_addr_t *basep, bus_size_t *sizep, int *flagsp) { + int ea_cap_ptr; + + if (pci_get_capability(pc, tag, PCI_CAP_EA, &ea_cap_ptr, NULL)) + return pci_ea_find(pc, tag, ea_cap_ptr, reg, type, + basep, sizep, flagsp, NULL); if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO) return pci_io_find(pc, tag, reg, type, basep, sizep, Index: src/sys/dev/pci/pciconf.c diff -u src/sys/dev/pci/pciconf.c:1.50 src/sys/dev/pci/pciconf.c:1.51 --- src/sys/dev/pci/pciconf.c:1.50 Tue Oct 20 23:03:30 2020 +++ src/sys/dev/pci/pciconf.c Tue Dec 29 15:39:59 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: pciconf.c,v 1.50 2020/10/20 23:03:30 jmcneill Exp $ */ +/* $NetBSD: pciconf.c,v 1.51 2020/12/29 15:39:59 skrll Exp $ */ /* * Copyright 2001 Wasabi Systems, Inc. @@ -65,7 +65,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: pciconf.c,v 1.50 2020/10/20 23:03:30 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: pciconf.c,v 1.51 2020/12/29 15:39:59 skrll Exp $"); #include "opt_pci.h" @@ -138,6 +138,7 @@ typedef struct _s_pciconf_dev_t { pcitag_t tag; pci_chipset_tag_t pc; struct _s_pciconf_bus_t *ppb; /* I am really a bridge */ + pcireg_t ea_cap_ptr; } pciconf_dev_t; typedef struct _s_pciconf_win_t { @@ -600,12 +601,21 @@ pci_do_device_query(pciconf_bus_t *pb, p pd->tag = tag; pd->ppb = NULL; pd->enable = mode; + pd->ea_cap_ptr = 0; classreg = pci_conf_read(pb->pc, tag, PCI_CLASS_REG); cmd = pci_conf_read(pb->pc, tag, PCI_COMMAND_STATUS_REG); bhlc = pci_conf_read(pb->pc, tag, PCI_BHLC_REG); + if (pci_get_capability(pb->pc, tag, PCI_CAP_EA, &pd->ea_cap_ptr, + NULL)) { + /* XXX Skip devices with EA for now. */ + print_tag(pb->pc, tag); + printf("skipping devices with Enhanced Allocations\n"); + return 0; + } + if (PCI_CLASS(classreg) != PCI_CLASS_BRIDGE && PCI_HDRTYPE_TYPE(bhlc) != PCI_HDRTYPE_PPB) { cmd &= ~(PCI_COMMAND_MASTER_ENABLE |