18d7fafffSZhi-Jun Robin Fu /*
28d7fafffSZhi-Jun Robin Fu  * CDDL HEADER START
38d7fafffSZhi-Jun Robin Fu  *
48d7fafffSZhi-Jun Robin Fu  * The contents of this file are subject to the terms of the
58d7fafffSZhi-Jun Robin Fu  * Common Development and Distribution License (the "License").
68d7fafffSZhi-Jun Robin Fu  * You may not use this file except in compliance with the License.
78d7fafffSZhi-Jun Robin Fu  *
88d7fafffSZhi-Jun Robin Fu  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98d7fafffSZhi-Jun Robin Fu  * or http://www.opensolaris.org/os/licensing.
108d7fafffSZhi-Jun Robin Fu  * See the License for the specific language governing permissions
118d7fafffSZhi-Jun Robin Fu  * and limitations under the License.
128d7fafffSZhi-Jun Robin Fu  *
138d7fafffSZhi-Jun Robin Fu  * When distributing Covered Code, include this CDDL HEADER in each
148d7fafffSZhi-Jun Robin Fu  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158d7fafffSZhi-Jun Robin Fu  * If applicable, add the following below this CDDL HEADER, with the
168d7fafffSZhi-Jun Robin Fu  * fields enclosed by brackets "[]" replaced with your own identifying
178d7fafffSZhi-Jun Robin Fu  * information: Portions Copyright [yyyy] [name of copyright owner]
188d7fafffSZhi-Jun Robin Fu  *
198d7fafffSZhi-Jun Robin Fu  * CDDL HEADER END
208d7fafffSZhi-Jun Robin Fu  */
218d7fafffSZhi-Jun Robin Fu /*
228d7fafffSZhi-Jun Robin Fu  * Copyright 2010 Advanced Micro Devices, Inc.
238d7fafffSZhi-Jun Robin Fu  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24*1636e047SRobert Mustacchi  * Copyright 2021 Oxide Computer Company
258d7fafffSZhi-Jun Robin Fu  */
268d7fafffSZhi-Jun Robin Fu 
278d7fafffSZhi-Jun Robin Fu /*
288d7fafffSZhi-Jun Robin Fu  * PCI Mechanism 1 low-level routines with ECS support for AMD family >= 0x10
298d7fafffSZhi-Jun Robin Fu  */
308d7fafffSZhi-Jun Robin Fu 
318d7fafffSZhi-Jun Robin Fu #include <sys/controlregs.h>
328d7fafffSZhi-Jun Robin Fu #include <sys/cpuvar.h>
338d7fafffSZhi-Jun Robin Fu #include <sys/types.h>
348d7fafffSZhi-Jun Robin Fu #include <sys/pci.h>
358d7fafffSZhi-Jun Robin Fu #include <sys/pci_impl.h>
368d7fafffSZhi-Jun Robin Fu #include <sys/sunddi.h>
378d7fafffSZhi-Jun Robin Fu #include <sys/pci_cfgspace_impl.h>
388d7fafffSZhi-Jun Robin Fu #include <sys/x86_archext.h>
398d7fafffSZhi-Jun Robin Fu 
408d7fafffSZhi-Jun Robin Fu boolean_t
pci_check_amd_ioecs(void)418d7fafffSZhi-Jun Robin Fu pci_check_amd_ioecs(void)
428d7fafffSZhi-Jun Robin Fu {
438d7fafffSZhi-Jun Robin Fu 	struct cpuid_regs cp;
448d7fafffSZhi-Jun Robin Fu 	int family;
458d7fafffSZhi-Jun Robin Fu 
467417cfdeSKuriakose Kuruvilla 	if (!is_x86_feature(x86_featureset, X86FSET_CPUID))
478d7fafffSZhi-Jun Robin Fu 		return (B_FALSE);
488d7fafffSZhi-Jun Robin Fu 
498d7fafffSZhi-Jun Robin Fu 	/*
508d7fafffSZhi-Jun Robin Fu 	 * Get the CPU vendor string from CPUID.
518d7fafffSZhi-Jun Robin Fu 	 * This PCI mechanism only applies to AMD CPUs.
528d7fafffSZhi-Jun Robin Fu 	 */
538d7fafffSZhi-Jun Robin Fu 	cp.cp_eax = 0;
548d7fafffSZhi-Jun Robin Fu 	(void) __cpuid_insn(&cp);
558d7fafffSZhi-Jun Robin Fu 
568d7fafffSZhi-Jun Robin Fu 	if ((cp.cp_ebx != 0x68747541) || /* Auth */
578d7fafffSZhi-Jun Robin Fu 	    (cp.cp_edx != 0x69746e65) || /* enti */
588d7fafffSZhi-Jun Robin Fu 	    (cp.cp_ecx != 0x444d4163))   /* cAMD */
598d7fafffSZhi-Jun Robin Fu 		return (B_FALSE);
608d7fafffSZhi-Jun Robin Fu 
618d7fafffSZhi-Jun Robin Fu 	/*
628d7fafffSZhi-Jun Robin Fu 	 * Get the CPU family from CPUID.
638d7fafffSZhi-Jun Robin Fu 	 * This PCI mechanism is only available on family 0x10 or higher.
648d7fafffSZhi-Jun Robin Fu 	 */
658d7fafffSZhi-Jun Robin Fu 	cp.cp_eax = 1;
668d7fafffSZhi-Jun Robin Fu 	(void) __cpuid_insn(&cp);
678d7fafffSZhi-Jun Robin Fu 	family = ((cp.cp_eax >> 8) & 0xf) + ((cp.cp_eax >> 20) & 0xff);
688d7fafffSZhi-Jun Robin Fu 
698d7fafffSZhi-Jun Robin Fu 	if (family < 0x10)
708d7fafffSZhi-Jun Robin Fu 		return (B_FALSE);
718d7fafffSZhi-Jun Robin Fu 
728d7fafffSZhi-Jun Robin Fu 	/*
738d7fafffSZhi-Jun Robin Fu 	 * Set the EnableCf8ExtCfg bit in the Northbridge Configuration Register
748d7fafffSZhi-Jun Robin Fu 	 * to enable accessing PCI ECS using in/out instructions.
758d7fafffSZhi-Jun Robin Fu 	 */
768d7fafffSZhi-Jun Robin Fu 	wrmsr(MSR_AMD_NB_CFG, rdmsr(MSR_AMD_NB_CFG) | AMD_GH_NB_CFG_EN_ECS);
778d7fafffSZhi-Jun Robin Fu 	return (B_TRUE);
788d7fafffSZhi-Jun Robin Fu }
798d7fafffSZhi-Jun Robin Fu 
808d7fafffSZhi-Jun Robin Fu /*
818d7fafffSZhi-Jun Robin Fu  * Macro to setup PCI Extended Configuration Space (ECS) address to give to
828d7fafffSZhi-Jun Robin Fu  * "in/out" instructions
838d7fafffSZhi-Jun Robin Fu  */
848d7fafffSZhi-Jun Robin Fu #define	PCI_CADDR1_ECS(b, d, f, r) \
858d7fafffSZhi-Jun Robin Fu 	(PCI_CADDR1((b), (d), (f), (r)) | ((((r) >> 8) & 0xf) << 24))
868d7fafffSZhi-Jun Robin Fu 
878d7fafffSZhi-Jun Robin Fu /*
888d7fafffSZhi-Jun Robin Fu  * Per PCI 2.1 section and PCI-PCI Bridge Architecture 1.0 section
898d7fafffSZhi-Jun Robin Fu  *  dev=31 func=7 reg=0 means a special cycle.  We don't want to
908d7fafffSZhi-Jun Robin Fu  * trigger that by accident, so we pretend that dev 31, func 7 doesn't
918d7fafffSZhi-Jun Robin Fu  * exist.  If we ever want special cycle support, we'll add explicit
928d7fafffSZhi-Jun Robin Fu  * special cycle support.
938d7fafffSZhi-Jun Robin Fu  */
948d7fafffSZhi-Jun Robin Fu 
958d7fafffSZhi-Jun Robin Fu uint8_t
pci_mech1_amd_getb(int bus,int device,int function,int reg)968d7fafffSZhi-Jun Robin Fu pci_mech1_amd_getb(int bus, int device, int function, int reg)
978d7fafffSZhi-Jun Robin Fu {
988d7fafffSZhi-Jun Robin Fu 	uint8_t val;
998d7fafffSZhi-Jun Robin Fu 
1008d7fafffSZhi-Jun Robin Fu 	if (device == PCI_MECH1_SPEC_CYCLE_DEV &&
1018d7fafffSZhi-Jun Robin Fu 	    function == PCI_MECH1_SPEC_CYCLE_FUNC) {
102*1636e047SRobert Mustacchi 		return (PCI_EINVAL8);
103*1636e047SRobert Mustacchi 	}
104*1636e047SRobert Mustacchi 
105*1636e047SRobert Mustacchi 	if (reg > pci_iocfg_max_offset) {
106*1636e047SRobert Mustacchi 		return (PCI_EINVAL8);
1078d7fafffSZhi-Jun Robin Fu 	}
1088d7fafffSZhi-Jun Robin Fu 
1098d7fafffSZhi-Jun Robin Fu 	mutex_enter(&pcicfg_mutex);
1108d7fafffSZhi-Jun Robin Fu 	outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg));
1118d7fafffSZhi-Jun Robin Fu 	val = inb(PCI_CONFDATA | (reg & 0x3));
1128d7fafffSZhi-Jun Robin Fu 	mutex_exit(&pcicfg_mutex);
1138d7fafffSZhi-Jun Robin Fu 	return (val);
1148d7fafffSZhi-Jun Robin Fu }
1158d7fafffSZhi-Jun Robin Fu 
1168d7fafffSZhi-Jun Robin Fu uint16_t
pci_mech1_amd_getw(int bus,int device,int function,int reg)1178d7fafffSZhi-Jun Robin Fu pci_mech1_amd_getw(int bus, int device, int function, int reg)
1188d7fafffSZhi-Jun Robin Fu {
1198d7fafffSZhi-Jun Robin Fu 	uint16_t val;
1208d7fafffSZhi-Jun Robin Fu 
1218d7fafffSZhi-Jun Robin Fu 	if (device == PCI_MECH1_SPEC_CYCLE_DEV &&
1228d7fafffSZhi-Jun Robin Fu 	    function == PCI_MECH1_SPEC_CYCLE_FUNC) {
123*1636e047SRobert Mustacchi 		return (PCI_EINVAL16);
124*1636e047SRobert Mustacchi 	}
125*1636e047SRobert Mustacchi 
126*1636e047SRobert Mustacchi 	if (reg > pci_iocfg_max_offset) {
127*1636e047SRobert Mustacchi 		return (PCI_EINVAL16);
1288d7fafffSZhi-Jun Robin Fu 	}
1298d7fafffSZhi-Jun Robin Fu 
1308d7fafffSZhi-Jun Robin Fu 	mutex_enter(&pcicfg_mutex);
1318d7fafffSZhi-Jun Robin Fu 	outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg));
1328d7fafffSZhi-Jun Robin Fu 	val =  inw(PCI_CONFDATA | (reg & 0x2));
1338d7fafffSZhi-Jun Robin Fu 	mutex_exit(&pcicfg_mutex);
1348d7fafffSZhi-Jun Robin Fu 	return (val);
1358d7fafffSZhi-Jun Robin Fu }
1368d7fafffSZhi-Jun Robin Fu 
1378d7fafffSZhi-Jun Robin Fu uint32_t
pci_mech1_amd_getl(int bus,int device,int function,int reg)1388d7fafffSZhi-Jun Robin Fu pci_mech1_amd_getl(int bus, int device, int function, int reg)
1398d7fafffSZhi-Jun Robin Fu {
1408d7fafffSZhi-Jun Robin Fu 	uint32_t val;
1418d7fafffSZhi-Jun Robin Fu 
1428d7fafffSZhi-Jun Robin Fu 	if (device == PCI_MECH1_SPEC_CYCLE_DEV &&
1438d7fafffSZhi-Jun Robin Fu 	    function == PCI_MECH1_SPEC_CYCLE_FUNC) {
144*1636e047SRobert Mustacchi 		return (PCI_EINVAL32);
145*1636e047SRobert Mustacchi 	}
146*1636e047SRobert Mustacchi 
147*1636e047SRobert Mustacchi 	if (reg > pci_iocfg_max_offset) {
148*1636e047SRobert Mustacchi 		return (PCI_EINVAL32);
1498d7fafffSZhi-Jun Robin Fu 	}
1508d7fafffSZhi-Jun Robin Fu 
1518d7fafffSZhi-Jun Robin Fu 	mutex_enter(&pcicfg_mutex);
1528d7fafffSZhi-Jun Robin Fu 	outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg));
1538d7fafffSZhi-Jun Robin Fu 	val = inl(PCI_CONFDATA);
1548d7fafffSZhi-Jun Robin Fu 	mutex_exit(&pcicfg_mutex);
1558d7fafffSZhi-Jun Robin Fu 	return (val);
1568d7fafffSZhi-Jun Robin Fu }
1578d7fafffSZhi-Jun Robin Fu 
1588d7fafffSZhi-Jun Robin Fu void
pci_mech1_amd_putb(int bus,int device,int function,int reg,uint8_t val)1598d7fafffSZhi-Jun Robin Fu pci_mech1_amd_putb(int bus, int device, int function, int reg, uint8_t val)
1608d7fafffSZhi-Jun Robin Fu {
1618d7fafffSZhi-Jun Robin Fu 	if (device == PCI_MECH1_SPEC_CYCLE_DEV &&
1628d7fafffSZhi-Jun Robin Fu 	    function == PCI_MECH1_SPEC_CYCLE_FUNC) {
1638d7fafffSZhi-Jun Robin Fu 		return;
1648d7fafffSZhi-Jun Robin Fu 	}
1658d7fafffSZhi-Jun Robin Fu 
1668d7fafffSZhi-Jun Robin Fu 	mutex_enter(&pcicfg_mutex);
1678d7fafffSZhi-Jun Robin Fu 	outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg));
1688d7fafffSZhi-Jun Robin Fu 	outb(PCI_CONFDATA | (reg & 0x3), val);
1698d7fafffSZhi-Jun Robin Fu 	mutex_exit(&pcicfg_mutex);
1708d7fafffSZhi-Jun Robin Fu }
1718d7fafffSZhi-Jun Robin Fu 
1728d7fafffSZhi-Jun Robin Fu void
pci_mech1_amd_putw(int bus,int device,int function,int reg,uint16_t val)1738d7fafffSZhi-Jun Robin Fu pci_mech1_amd_putw(int bus, int device, int function, int reg, uint16_t val)
1748d7fafffSZhi-Jun Robin Fu {
1758d7fafffSZhi-Jun Robin Fu 	if (device == PCI_MECH1_SPEC_CYCLE_DEV &&
1768d7fafffSZhi-Jun Robin Fu 	    function == PCI_MECH1_SPEC_CYCLE_FUNC) {
1778d7fafffSZhi-Jun Robin Fu 		return;
1788d7fafffSZhi-Jun Robin Fu 	}
1798d7fafffSZhi-Jun Robin Fu 
1808d7fafffSZhi-Jun Robin Fu 	mutex_enter(&pcicfg_mutex);
1818d7fafffSZhi-Jun Robin Fu 	outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg));
1828d7fafffSZhi-Jun Robin Fu 	outw(PCI_CONFDATA | (reg & 0x2), val);
1838d7fafffSZhi-Jun Robin Fu 	mutex_exit(&pcicfg_mutex);
1848d7fafffSZhi-Jun Robin Fu }
1858d7fafffSZhi-Jun Robin Fu 
1868d7fafffSZhi-Jun Robin Fu void
pci_mech1_amd_putl(int bus,int device,int function,int reg,uint32_t val)1878d7fafffSZhi-Jun Robin Fu pci_mech1_amd_putl(int bus, int device, int function, int reg, uint32_t val)
1888d7fafffSZhi-Jun Robin Fu {
1898d7fafffSZhi-Jun Robin Fu 	if (device == PCI_MECH1_SPEC_CYCLE_DEV &&
1908d7fafffSZhi-Jun Robin Fu 	    function == PCI_MECH1_SPEC_CYCLE_FUNC) {
1918d7fafffSZhi-Jun Robin Fu 		return;
1928d7fafffSZhi-Jun Robin Fu 	}
1938d7fafffSZhi-Jun Robin Fu 
1948d7fafffSZhi-Jun Robin Fu 	mutex_enter(&pcicfg_mutex);
1958d7fafffSZhi-Jun Robin Fu 	outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg));
1968d7fafffSZhi-Jun Robin Fu 	outl(PCI_CONFDATA, val);
1978d7fafffSZhi-Jun Robin Fu 	mutex_exit(&pcicfg_mutex);
1988d7fafffSZhi-Jun Robin Fu }