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.
248d7fafffSZhi-Jun Robin Fu  */
258d7fafffSZhi-Jun Robin Fu 
268d7fafffSZhi-Jun Robin Fu /*
278d7fafffSZhi-Jun Robin Fu  * PCI Mechanism 1 low-level routines with ECS support for AMD family >= 0x10
288d7fafffSZhi-Jun Robin Fu  */
298d7fafffSZhi-Jun Robin Fu 
308d7fafffSZhi-Jun Robin Fu #include <sys/controlregs.h>
318d7fafffSZhi-Jun Robin Fu #include <sys/cpuvar.h>
328d7fafffSZhi-Jun Robin Fu #include <sys/types.h>
338d7fafffSZhi-Jun Robin Fu #include <sys/pci.h>
348d7fafffSZhi-Jun Robin Fu #include <sys/pci_impl.h>
358d7fafffSZhi-Jun Robin Fu #include <sys/sunddi.h>
368d7fafffSZhi-Jun Robin Fu #include <sys/pci_cfgspace_impl.h>
378d7fafffSZhi-Jun Robin Fu #include <sys/x86_archext.h>
388d7fafffSZhi-Jun Robin Fu 
398d7fafffSZhi-Jun Robin Fu boolean_t
408d7fafffSZhi-Jun Robin Fu pci_check_amd_ioecs(void)
418d7fafffSZhi-Jun Robin Fu {
428d7fafffSZhi-Jun Robin Fu 	struct cpuid_regs cp;
438d7fafffSZhi-Jun Robin Fu 	int family;
448d7fafffSZhi-Jun Robin Fu 
45*7417cfdeSKuriakose Kuruvilla 	if (!is_x86_feature(x86_featureset, X86FSET_CPUID))
468d7fafffSZhi-Jun Robin Fu 		return (B_FALSE);
478d7fafffSZhi-Jun Robin Fu 
488d7fafffSZhi-Jun Robin Fu 	/*
498d7fafffSZhi-Jun Robin Fu 	 * Get the CPU vendor string from CPUID.
508d7fafffSZhi-Jun Robin Fu 	 * This PCI mechanism only applies to AMD CPUs.
518d7fafffSZhi-Jun Robin Fu 	 */
528d7fafffSZhi-Jun Robin Fu 	cp.cp_eax = 0;
538d7fafffSZhi-Jun Robin Fu 	(void) __cpuid_insn(&cp);
548d7fafffSZhi-Jun Robin Fu 
558d7fafffSZhi-Jun Robin Fu 	if ((cp.cp_ebx != 0x68747541) || /* Auth */
568d7fafffSZhi-Jun Robin Fu 	    (cp.cp_edx != 0x69746e65) || /* enti */
578d7fafffSZhi-Jun Robin Fu 	    (cp.cp_ecx != 0x444d4163))   /* cAMD */
588d7fafffSZhi-Jun Robin Fu 		return (B_FALSE);
598d7fafffSZhi-Jun Robin Fu 
608d7fafffSZhi-Jun Robin Fu 	/*
618d7fafffSZhi-Jun Robin Fu 	 * Get the CPU family from CPUID.
628d7fafffSZhi-Jun Robin Fu 	 * This PCI mechanism is only available on family 0x10 or higher.
638d7fafffSZhi-Jun Robin Fu 	 */
648d7fafffSZhi-Jun Robin Fu 	cp.cp_eax = 1;
658d7fafffSZhi-Jun Robin Fu 	(void) __cpuid_insn(&cp);
668d7fafffSZhi-Jun Robin Fu 	family = ((cp.cp_eax >> 8) & 0xf) + ((cp.cp_eax >> 20) & 0xff);
678d7fafffSZhi-Jun Robin Fu 
688d7fafffSZhi-Jun Robin Fu 	if (family < 0x10)
698d7fafffSZhi-Jun Robin Fu 		return (B_FALSE);
708d7fafffSZhi-Jun Robin Fu 
718d7fafffSZhi-Jun Robin Fu 	/*
728d7fafffSZhi-Jun Robin Fu 	 * Set the EnableCf8ExtCfg bit in the Northbridge Configuration Register
738d7fafffSZhi-Jun Robin Fu 	 * to enable accessing PCI ECS using in/out instructions.
748d7fafffSZhi-Jun Robin Fu 	 */
758d7fafffSZhi-Jun Robin Fu 	wrmsr(MSR_AMD_NB_CFG, rdmsr(MSR_AMD_NB_CFG) | AMD_GH_NB_CFG_EN_ECS);
768d7fafffSZhi-Jun Robin Fu 	return (B_TRUE);
778d7fafffSZhi-Jun Robin Fu }
788d7fafffSZhi-Jun Robin Fu 
798d7fafffSZhi-Jun Robin Fu /*
808d7fafffSZhi-Jun Robin Fu  * Macro to setup PCI Extended Configuration Space (ECS) address to give to
818d7fafffSZhi-Jun Robin Fu  * "in/out" instructions
828d7fafffSZhi-Jun Robin Fu  */
838d7fafffSZhi-Jun Robin Fu #define	PCI_CADDR1_ECS(b, d, f, r) \
848d7fafffSZhi-Jun Robin Fu 	(PCI_CADDR1((b), (d), (f), (r)) | ((((r) >> 8) & 0xf) << 24))
858d7fafffSZhi-Jun Robin Fu 
868d7fafffSZhi-Jun Robin Fu /*
878d7fafffSZhi-Jun Robin Fu  * Per PCI 2.1 section 3.7.4.1 and PCI-PCI Bridge Architecture 1.0 section
888d7fafffSZhi-Jun Robin Fu  * 5.3.1.2:  dev=31 func=7 reg=0 means a special cycle.  We don't want to
898d7fafffSZhi-Jun Robin Fu  * trigger that by accident, so we pretend that dev 31, func 7 doesn't
908d7fafffSZhi-Jun Robin Fu  * exist.  If we ever want special cycle support, we'll add explicit
918d7fafffSZhi-Jun Robin Fu  * special cycle support.
928d7fafffSZhi-Jun Robin Fu  */
938d7fafffSZhi-Jun Robin Fu 
948d7fafffSZhi-Jun Robin Fu uint8_t
958d7fafffSZhi-Jun Robin Fu pci_mech1_amd_getb(int bus, int device, int function, int reg)
968d7fafffSZhi-Jun Robin Fu {
978d7fafffSZhi-Jun Robin Fu 	uint8_t val;
988d7fafffSZhi-Jun Robin Fu 
998d7fafffSZhi-Jun Robin Fu 	if (device == PCI_MECH1_SPEC_CYCLE_DEV &&
1008d7fafffSZhi-Jun Robin Fu 	    function == PCI_MECH1_SPEC_CYCLE_FUNC) {
1018d7fafffSZhi-Jun Robin Fu 		return (0xff);
1028d7fafffSZhi-Jun Robin Fu 	}
1038d7fafffSZhi-Jun Robin Fu 
1048d7fafffSZhi-Jun Robin Fu 	mutex_enter(&pcicfg_mutex);
1058d7fafffSZhi-Jun Robin Fu 	outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg));
1068d7fafffSZhi-Jun Robin Fu 	val = inb(PCI_CONFDATA | (reg & 0x3));
1078d7fafffSZhi-Jun Robin Fu 	mutex_exit(&pcicfg_mutex);
1088d7fafffSZhi-Jun Robin Fu 	return (val);
1098d7fafffSZhi-Jun Robin Fu }
1108d7fafffSZhi-Jun Robin Fu 
1118d7fafffSZhi-Jun Robin Fu uint16_t
1128d7fafffSZhi-Jun Robin Fu pci_mech1_amd_getw(int bus, int device, int function, int reg)
1138d7fafffSZhi-Jun Robin Fu {
1148d7fafffSZhi-Jun Robin Fu 	uint16_t val;
1158d7fafffSZhi-Jun Robin Fu 
1168d7fafffSZhi-Jun Robin Fu 	if (device == PCI_MECH1_SPEC_CYCLE_DEV &&
1178d7fafffSZhi-Jun Robin Fu 	    function == PCI_MECH1_SPEC_CYCLE_FUNC) {
1188d7fafffSZhi-Jun Robin Fu 		return (0xffff);
1198d7fafffSZhi-Jun Robin Fu 	}
1208d7fafffSZhi-Jun Robin Fu 
1218d7fafffSZhi-Jun Robin Fu 	mutex_enter(&pcicfg_mutex);
1228d7fafffSZhi-Jun Robin Fu 	outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg));
1238d7fafffSZhi-Jun Robin Fu 	val =  inw(PCI_CONFDATA | (reg & 0x2));
1248d7fafffSZhi-Jun Robin Fu 	mutex_exit(&pcicfg_mutex);
1258d7fafffSZhi-Jun Robin Fu 	return (val);
1268d7fafffSZhi-Jun Robin Fu }
1278d7fafffSZhi-Jun Robin Fu 
1288d7fafffSZhi-Jun Robin Fu uint32_t
1298d7fafffSZhi-Jun Robin Fu pci_mech1_amd_getl(int bus, int device, int function, int reg)
1308d7fafffSZhi-Jun Robin Fu {
1318d7fafffSZhi-Jun Robin Fu 	uint32_t val;
1328d7fafffSZhi-Jun Robin Fu 
1338d7fafffSZhi-Jun Robin Fu 	if (device == PCI_MECH1_SPEC_CYCLE_DEV &&
1348d7fafffSZhi-Jun Robin Fu 	    function == PCI_MECH1_SPEC_CYCLE_FUNC) {
1358d7fafffSZhi-Jun Robin Fu 		return (0xffffffffu);
1368d7fafffSZhi-Jun Robin Fu 	}
1378d7fafffSZhi-Jun Robin Fu 
1388d7fafffSZhi-Jun Robin Fu 	mutex_enter(&pcicfg_mutex);
1398d7fafffSZhi-Jun Robin Fu 	outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg));
1408d7fafffSZhi-Jun Robin Fu 	val = inl(PCI_CONFDATA);
1418d7fafffSZhi-Jun Robin Fu 	mutex_exit(&pcicfg_mutex);
1428d7fafffSZhi-Jun Robin Fu 	return (val);
1438d7fafffSZhi-Jun Robin Fu }
1448d7fafffSZhi-Jun Robin Fu 
1458d7fafffSZhi-Jun Robin Fu void
1468d7fafffSZhi-Jun Robin Fu pci_mech1_amd_putb(int bus, int device, int function, int reg, uint8_t val)
1478d7fafffSZhi-Jun Robin Fu {
1488d7fafffSZhi-Jun Robin Fu 	if (device == PCI_MECH1_SPEC_CYCLE_DEV &&
1498d7fafffSZhi-Jun Robin Fu 	    function == PCI_MECH1_SPEC_CYCLE_FUNC) {
1508d7fafffSZhi-Jun Robin Fu 		return;
1518d7fafffSZhi-Jun Robin Fu 	}
1528d7fafffSZhi-Jun Robin Fu 
1538d7fafffSZhi-Jun Robin Fu 	mutex_enter(&pcicfg_mutex);
1548d7fafffSZhi-Jun Robin Fu 	outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg));
1558d7fafffSZhi-Jun Robin Fu 	outb(PCI_CONFDATA | (reg & 0x3), val);
1568d7fafffSZhi-Jun Robin Fu 	mutex_exit(&pcicfg_mutex);
1578d7fafffSZhi-Jun Robin Fu }
1588d7fafffSZhi-Jun Robin Fu 
1598d7fafffSZhi-Jun Robin Fu void
1608d7fafffSZhi-Jun Robin Fu pci_mech1_amd_putw(int bus, int device, int function, int reg, uint16_t val)
1618d7fafffSZhi-Jun Robin Fu {
1628d7fafffSZhi-Jun Robin Fu 	if (device == PCI_MECH1_SPEC_CYCLE_DEV &&
1638d7fafffSZhi-Jun Robin Fu 	    function == PCI_MECH1_SPEC_CYCLE_FUNC) {
1648d7fafffSZhi-Jun Robin Fu 		return;
1658d7fafffSZhi-Jun Robin Fu 	}
1668d7fafffSZhi-Jun Robin Fu 
1678d7fafffSZhi-Jun Robin Fu 	mutex_enter(&pcicfg_mutex);
1688d7fafffSZhi-Jun Robin Fu 	outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg));
1698d7fafffSZhi-Jun Robin Fu 	outw(PCI_CONFDATA | (reg & 0x2), val);
1708d7fafffSZhi-Jun Robin Fu 	mutex_exit(&pcicfg_mutex);
1718d7fafffSZhi-Jun Robin Fu }
1728d7fafffSZhi-Jun Robin Fu 
1738d7fafffSZhi-Jun Robin Fu void
1748d7fafffSZhi-Jun Robin Fu pci_mech1_amd_putl(int bus, int device, int function, int reg, uint32_t val)
1758d7fafffSZhi-Jun Robin Fu {
1768d7fafffSZhi-Jun Robin Fu 	if (device == PCI_MECH1_SPEC_CYCLE_DEV &&
1778d7fafffSZhi-Jun Robin Fu 	    function == PCI_MECH1_SPEC_CYCLE_FUNC) {
1788d7fafffSZhi-Jun Robin Fu 		return;
1798d7fafffSZhi-Jun Robin Fu 	}
1808d7fafffSZhi-Jun Robin Fu 
1818d7fafffSZhi-Jun Robin Fu 	mutex_enter(&pcicfg_mutex);
1828d7fafffSZhi-Jun Robin Fu 	outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg));
1838d7fafffSZhi-Jun Robin Fu 	outl(PCI_CONFDATA, val);
1848d7fafffSZhi-Jun Robin Fu 	mutex_exit(&pcicfg_mutex);
1858d7fafffSZhi-Jun Robin Fu }
186