amdsmn.c revision 343325
1323184Scem/*- 2343325Smav * Copyright (c) 2017-2019 Conrad Meyer <cem@FreeBSD.org> 3323184Scem * All rights reserved. 4323184Scem * 5323184Scem * Redistribution and use in source and binary forms, with or without 6323184Scem * modification, are permitted provided that the following conditions 7323184Scem * are met: 8323184Scem * 1. Redistributions of source code must retain the above copyright 9323184Scem * notice, this list of conditions and the following disclaimer. 10323184Scem * 2. Redistributions in binary form must reproduce the above copyright 11323184Scem * notice, this list of conditions and the following disclaimer in the 12323184Scem * documentation and/or other materials provided with the distribution. 13323184Scem * 14323184Scem * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15323184Scem * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16323184Scem * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17323184Scem * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 18323184Scem * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19323184Scem * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20323184Scem * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21323184Scem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22323184Scem * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 23323184Scem * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24323184Scem * POSSIBILITY OF SUCH DAMAGE. 25323184Scem */ 26323184Scem 27323184Scem/* 28343325Smav * Driver for the AMD Family 15h and 17h CPU System Management Network. 29323184Scem */ 30323184Scem 31323184Scem#include <sys/cdefs.h> 32323184Scem__FBSDID("$FreeBSD: stable/11/sys/dev/amdsmn/amdsmn.c 343325 2019-01-22 21:35:25Z mav $"); 33323184Scem 34323184Scem#include <sys/param.h> 35323184Scem#include <sys/bus.h> 36323184Scem#include <sys/conf.h> 37323184Scem#include <sys/lock.h> 38323184Scem#include <sys/kernel.h> 39323184Scem#include <sys/module.h> 40323184Scem#include <sys/mutex.h> 41323184Scem#include <sys/sysctl.h> 42323184Scem#include <sys/systm.h> 43323184Scem 44323184Scem#include <machine/cpufunc.h> 45343323Smav#include <machine/cputypes.h> 46323184Scem#include <machine/md_var.h> 47323184Scem#include <machine/specialreg.h> 48323184Scem 49323184Scem#include <dev/pci/pcivar.h> 50323184Scem#include <x86/pci_cfgreg.h> 51323184Scem 52323184Scem#include <dev/amdsmn/amdsmn.h> 53323184Scem 54343325Smav#define F15H_SMN_ADDR_REG 0xb8 55343325Smav#define F15H_SMN_DATA_REG 0xbc 56343325Smav#define F17H_SMN_ADDR_REG 0x60 57343325Smav#define F17H_SMN_DATA_REG 0x64 58323184Scem 59343325Smav#define PCI_DEVICE_ID_AMD_15H_M60H_ROOT 0x1576 60343323Smav#define PCI_DEVICE_ID_AMD_17H_ROOT 0x1450 61343323Smav#define PCI_DEVICE_ID_AMD_17H_M10H_ROOT 0x15d0 62343323Smav 63343325Smavstruct pciid; 64323184Scemstruct amdsmn_softc { 65323184Scem struct mtx smn_lock; 66343325Smav const struct pciid *smn_pciid; 67323184Scem}; 68323184Scem 69343325Smavstatic const struct pciid { 70343323Smav uint16_t amdsmn_vendorid; 71343323Smav uint16_t amdsmn_deviceid; 72343325Smav uint8_t amdsmn_addr_reg; 73343325Smav uint8_t amdsmn_data_reg; 74323184Scem} amdsmn_ids[] = { 75343325Smav { 76343325Smav .amdsmn_vendorid = CPU_VENDOR_AMD, 77343325Smav .amdsmn_deviceid = PCI_DEVICE_ID_AMD_15H_M60H_ROOT, 78343325Smav .amdsmn_addr_reg = F15H_SMN_ADDR_REG, 79343325Smav .amdsmn_data_reg = F15H_SMN_DATA_REG, 80343325Smav }, 81343325Smav { 82343325Smav .amdsmn_vendorid = CPU_VENDOR_AMD, 83343325Smav .amdsmn_deviceid = PCI_DEVICE_ID_AMD_17H_ROOT, 84343325Smav .amdsmn_addr_reg = F17H_SMN_ADDR_REG, 85343325Smav .amdsmn_data_reg = F17H_SMN_DATA_REG, 86343325Smav }, 87343325Smav { 88343325Smav .amdsmn_vendorid = CPU_VENDOR_AMD, 89343325Smav .amdsmn_deviceid = PCI_DEVICE_ID_AMD_17H_M10H_ROOT, 90343325Smav .amdsmn_addr_reg = F17H_SMN_ADDR_REG, 91343325Smav .amdsmn_data_reg = F17H_SMN_DATA_REG, 92343325Smav }, 93323184Scem}; 94323184Scem 95323184Scem/* 96323184Scem * Device methods. 97323184Scem */ 98323184Scemstatic void amdsmn_identify(driver_t *driver, device_t parent); 99323184Scemstatic int amdsmn_probe(device_t dev); 100323184Scemstatic int amdsmn_attach(device_t dev); 101323184Scemstatic int amdsmn_detach(device_t dev); 102323184Scem 103323184Scemstatic device_method_t amdsmn_methods[] = { 104323184Scem /* Device interface */ 105323184Scem DEVMETHOD(device_identify, amdsmn_identify), 106323184Scem DEVMETHOD(device_probe, amdsmn_probe), 107323184Scem DEVMETHOD(device_attach, amdsmn_attach), 108323184Scem DEVMETHOD(device_detach, amdsmn_detach), 109323184Scem DEVMETHOD_END 110323184Scem}; 111323184Scem 112323184Scemstatic driver_t amdsmn_driver = { 113323184Scem "amdsmn", 114323184Scem amdsmn_methods, 115323184Scem sizeof(struct amdsmn_softc), 116323184Scem}; 117323184Scem 118323184Scemstatic devclass_t amdsmn_devclass; 119323184ScemDRIVER_MODULE(amdsmn, hostb, amdsmn_driver, amdsmn_devclass, NULL, NULL); 120323184ScemMODULE_VERSION(amdsmn, 1); 121323184Scem 122329767Struckmanstatic bool 123343325Smavamdsmn_match(device_t parent, const struct pciid **pciid_out) 124323184Scem{ 125343323Smav uint16_t vendor, device; 126323184Scem size_t i; 127323184Scem 128343323Smav vendor = pci_get_vendor(parent); 129343323Smav device = pci_get_device(parent); 130343323Smav 131343325Smav for (i = 0; i < nitems(amdsmn_ids); i++) { 132343323Smav if (vendor == amdsmn_ids[i].amdsmn_vendorid && 133343325Smav device == amdsmn_ids[i].amdsmn_deviceid) { 134343325Smav if (pciid_out != NULL) 135343325Smav *pciid_out = &amdsmn_ids[i]; 136329767Struckman return (true); 137343325Smav } 138343325Smav } 139329767Struckman return (false); 140329767Struckman} 141323184Scem 142329767Struckmanstatic void 143329767Struckmanamdsmn_identify(driver_t *driver, device_t parent) 144329767Struckman{ 145329767Struckman device_t child; 146329767Struckman 147329767Struckman /* Make sure we're not being doubly invoked. */ 148329767Struckman if (device_find_child(parent, "amdsmn", -1) != NULL) 149323184Scem return; 150343325Smav if (!amdsmn_match(parent, NULL)) 151329767Struckman return; 152323184Scem 153323184Scem child = device_add_child(parent, "amdsmn", -1); 154323184Scem if (child == NULL) 155323184Scem device_printf(parent, "add amdsmn child failed\n"); 156323184Scem} 157323184Scem 158323184Scemstatic int 159323184Scemamdsmn_probe(device_t dev) 160323184Scem{ 161323184Scem uint32_t family; 162343325Smav char buf[64]; 163323184Scem 164323184Scem if (resource_disabled("amdsmn", 0)) 165323184Scem return (ENXIO); 166343325Smav if (!amdsmn_match(device_get_parent(dev), NULL)) 167329767Struckman return (ENXIO); 168323184Scem 169323184Scem family = CPUID_TO_FAMILY(cpu_id); 170323184Scem 171323184Scem switch (family) { 172343325Smav case 0x15: 173323184Scem case 0x17: 174323184Scem break; 175323184Scem default: 176323184Scem return (ENXIO); 177323184Scem } 178343325Smav snprintf(buf, sizeof(buf), "AMD Family %xh System Management Network", 179343325Smav family); 180343325Smav device_set_desc_copy(dev, buf); 181323184Scem 182323184Scem return (BUS_PROBE_GENERIC); 183323184Scem} 184323184Scem 185323184Scemstatic int 186323184Scemamdsmn_attach(device_t dev) 187323184Scem{ 188323184Scem struct amdsmn_softc *sc = device_get_softc(dev); 189323184Scem 190343325Smav if (!amdsmn_match(device_get_parent(dev), &sc->smn_pciid)) 191343325Smav return (ENXIO); 192343325Smav 193323184Scem mtx_init(&sc->smn_lock, "SMN mtx", "SMN", MTX_DEF); 194323184Scem return (0); 195323184Scem} 196323184Scem 197323184Scemint 198323184Scemamdsmn_detach(device_t dev) 199323184Scem{ 200323184Scem struct amdsmn_softc *sc = device_get_softc(dev); 201323184Scem 202323184Scem mtx_destroy(&sc->smn_lock); 203323184Scem return (0); 204323184Scem} 205323184Scem 206323184Scemint 207323184Scemamdsmn_read(device_t dev, uint32_t addr, uint32_t *value) 208323184Scem{ 209323184Scem struct amdsmn_softc *sc = device_get_softc(dev); 210323184Scem device_t parent; 211323184Scem 212323184Scem parent = device_get_parent(dev); 213323184Scem 214323184Scem mtx_lock(&sc->smn_lock); 215343325Smav pci_write_config(parent, sc->smn_pciid->amdsmn_addr_reg, addr, 4); 216343325Smav *value = pci_read_config(parent, sc->smn_pciid->amdsmn_data_reg, 4); 217323184Scem mtx_unlock(&sc->smn_lock); 218323184Scem 219323184Scem return (0); 220323184Scem} 221323184Scem 222323184Scemint 223323184Scemamdsmn_write(device_t dev, uint32_t addr, uint32_t value) 224323184Scem{ 225323184Scem struct amdsmn_softc *sc = device_get_softc(dev); 226323184Scem device_t parent; 227323184Scem 228323184Scem parent = device_get_parent(dev); 229323184Scem 230323184Scem mtx_lock(&sc->smn_lock); 231343325Smav pci_write_config(parent, sc->smn_pciid->amdsmn_addr_reg, addr, 4); 232343325Smav pci_write_config(parent, sc->smn_pciid->amdsmn_data_reg, value, 4); 233323184Scem mtx_unlock(&sc->smn_lock); 234323184Scem 235323184Scem return (0); 236323184Scem} 237