amdsmn.c revision 355562
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 355562 2019-12-09 17:14:43Z 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 62355562Smav#define PCI_DEVICE_ID_AMD_17H_M30H_ROOT 0x1480 63343323Smav 64343325Smavstruct pciid; 65323184Scemstruct amdsmn_softc { 66323184Scem struct mtx smn_lock; 67343325Smav const struct pciid *smn_pciid; 68323184Scem}; 69323184Scem 70343325Smavstatic const struct pciid { 71343323Smav uint16_t amdsmn_vendorid; 72343323Smav uint16_t amdsmn_deviceid; 73343325Smav uint8_t amdsmn_addr_reg; 74343325Smav uint8_t amdsmn_data_reg; 75323184Scem} amdsmn_ids[] = { 76343325Smav { 77343325Smav .amdsmn_vendorid = CPU_VENDOR_AMD, 78343325Smav .amdsmn_deviceid = PCI_DEVICE_ID_AMD_15H_M60H_ROOT, 79343325Smav .amdsmn_addr_reg = F15H_SMN_ADDR_REG, 80343325Smav .amdsmn_data_reg = F15H_SMN_DATA_REG, 81343325Smav }, 82343325Smav { 83343325Smav .amdsmn_vendorid = CPU_VENDOR_AMD, 84343325Smav .amdsmn_deviceid = PCI_DEVICE_ID_AMD_17H_ROOT, 85343325Smav .amdsmn_addr_reg = F17H_SMN_ADDR_REG, 86343325Smav .amdsmn_data_reg = F17H_SMN_DATA_REG, 87343325Smav }, 88343325Smav { 89343325Smav .amdsmn_vendorid = CPU_VENDOR_AMD, 90343325Smav .amdsmn_deviceid = PCI_DEVICE_ID_AMD_17H_M10H_ROOT, 91343325Smav .amdsmn_addr_reg = F17H_SMN_ADDR_REG, 92343325Smav .amdsmn_data_reg = F17H_SMN_DATA_REG, 93343325Smav }, 94355562Smav { 95355562Smav .amdsmn_vendorid = CPU_VENDOR_AMD, 96355562Smav .amdsmn_deviceid = PCI_DEVICE_ID_AMD_17H_M30H_ROOT, 97355562Smav .amdsmn_addr_reg = F17H_SMN_ADDR_REG, 98355562Smav .amdsmn_data_reg = F17H_SMN_DATA_REG, 99355562Smav }, 100323184Scem}; 101323184Scem 102323184Scem/* 103323184Scem * Device methods. 104323184Scem */ 105323184Scemstatic void amdsmn_identify(driver_t *driver, device_t parent); 106323184Scemstatic int amdsmn_probe(device_t dev); 107323184Scemstatic int amdsmn_attach(device_t dev); 108323184Scemstatic int amdsmn_detach(device_t dev); 109323184Scem 110323184Scemstatic device_method_t amdsmn_methods[] = { 111323184Scem /* Device interface */ 112323184Scem DEVMETHOD(device_identify, amdsmn_identify), 113323184Scem DEVMETHOD(device_probe, amdsmn_probe), 114323184Scem DEVMETHOD(device_attach, amdsmn_attach), 115323184Scem DEVMETHOD(device_detach, amdsmn_detach), 116323184Scem DEVMETHOD_END 117323184Scem}; 118323184Scem 119323184Scemstatic driver_t amdsmn_driver = { 120323184Scem "amdsmn", 121323184Scem amdsmn_methods, 122323184Scem sizeof(struct amdsmn_softc), 123323184Scem}; 124323184Scem 125323184Scemstatic devclass_t amdsmn_devclass; 126323184ScemDRIVER_MODULE(amdsmn, hostb, amdsmn_driver, amdsmn_devclass, NULL, NULL); 127323184ScemMODULE_VERSION(amdsmn, 1); 128323184Scem 129329767Struckmanstatic bool 130343325Smavamdsmn_match(device_t parent, const struct pciid **pciid_out) 131323184Scem{ 132343323Smav uint16_t vendor, device; 133323184Scem size_t i; 134323184Scem 135343323Smav vendor = pci_get_vendor(parent); 136343323Smav device = pci_get_device(parent); 137343323Smav 138343325Smav for (i = 0; i < nitems(amdsmn_ids); i++) { 139343323Smav if (vendor == amdsmn_ids[i].amdsmn_vendorid && 140343325Smav device == amdsmn_ids[i].amdsmn_deviceid) { 141343325Smav if (pciid_out != NULL) 142343325Smav *pciid_out = &amdsmn_ids[i]; 143329767Struckman return (true); 144343325Smav } 145343325Smav } 146329767Struckman return (false); 147329767Struckman} 148323184Scem 149329767Struckmanstatic void 150329767Struckmanamdsmn_identify(driver_t *driver, device_t parent) 151329767Struckman{ 152329767Struckman device_t child; 153329767Struckman 154329767Struckman /* Make sure we're not being doubly invoked. */ 155329767Struckman if (device_find_child(parent, "amdsmn", -1) != NULL) 156323184Scem return; 157343325Smav if (!amdsmn_match(parent, NULL)) 158329767Struckman return; 159323184Scem 160323184Scem child = device_add_child(parent, "amdsmn", -1); 161323184Scem if (child == NULL) 162323184Scem device_printf(parent, "add amdsmn child failed\n"); 163323184Scem} 164323184Scem 165323184Scemstatic int 166323184Scemamdsmn_probe(device_t dev) 167323184Scem{ 168323184Scem uint32_t family; 169343325Smav char buf[64]; 170323184Scem 171323184Scem if (resource_disabled("amdsmn", 0)) 172323184Scem return (ENXIO); 173343325Smav if (!amdsmn_match(device_get_parent(dev), NULL)) 174329767Struckman return (ENXIO); 175323184Scem 176323184Scem family = CPUID_TO_FAMILY(cpu_id); 177323184Scem 178323184Scem switch (family) { 179343325Smav case 0x15: 180323184Scem case 0x17: 181323184Scem break; 182323184Scem default: 183323184Scem return (ENXIO); 184323184Scem } 185343325Smav snprintf(buf, sizeof(buf), "AMD Family %xh System Management Network", 186343325Smav family); 187343325Smav device_set_desc_copy(dev, buf); 188323184Scem 189323184Scem return (BUS_PROBE_GENERIC); 190323184Scem} 191323184Scem 192323184Scemstatic int 193323184Scemamdsmn_attach(device_t dev) 194323184Scem{ 195323184Scem struct amdsmn_softc *sc = device_get_softc(dev); 196323184Scem 197343325Smav if (!amdsmn_match(device_get_parent(dev), &sc->smn_pciid)) 198343325Smav return (ENXIO); 199343325Smav 200323184Scem mtx_init(&sc->smn_lock, "SMN mtx", "SMN", MTX_DEF); 201323184Scem return (0); 202323184Scem} 203323184Scem 204323184Scemint 205323184Scemamdsmn_detach(device_t dev) 206323184Scem{ 207323184Scem struct amdsmn_softc *sc = device_get_softc(dev); 208323184Scem 209323184Scem mtx_destroy(&sc->smn_lock); 210323184Scem return (0); 211323184Scem} 212323184Scem 213323184Scemint 214323184Scemamdsmn_read(device_t dev, uint32_t addr, uint32_t *value) 215323184Scem{ 216323184Scem struct amdsmn_softc *sc = device_get_softc(dev); 217323184Scem device_t parent; 218323184Scem 219323184Scem parent = device_get_parent(dev); 220323184Scem 221323184Scem mtx_lock(&sc->smn_lock); 222343325Smav pci_write_config(parent, sc->smn_pciid->amdsmn_addr_reg, addr, 4); 223343325Smav *value = pci_read_config(parent, sc->smn_pciid->amdsmn_data_reg, 4); 224323184Scem mtx_unlock(&sc->smn_lock); 225323184Scem 226323184Scem return (0); 227323184Scem} 228323184Scem 229323184Scemint 230323184Scemamdsmn_write(device_t dev, uint32_t addr, uint32_t value) 231323184Scem{ 232323184Scem struct amdsmn_softc *sc = device_get_softc(dev); 233323184Scem device_t parent; 234323184Scem 235323184Scem parent = device_get_parent(dev); 236323184Scem 237323184Scem mtx_lock(&sc->smn_lock); 238343325Smav pci_write_config(parent, sc->smn_pciid->amdsmn_addr_reg, addr, 4); 239343325Smav pci_write_config(parent, sc->smn_pciid->amdsmn_data_reg, value, 4); 240323184Scem mtx_unlock(&sc->smn_lock); 241323184Scem 242323184Scem return (0); 243323184Scem} 244