18311733cSPierre-Alain TORET /*-
22a07801cSSascha Wildner * Copyright (c) 2017-2020 Conrad Meyer <cem@FreeBSD.org>
38311733cSPierre-Alain TORET * All rights reserved.
48311733cSPierre-Alain TORET *
58311733cSPierre-Alain TORET * Redistribution and use in source and binary forms, with or without
68311733cSPierre-Alain TORET * modification, are permitted provided that the following conditions
78311733cSPierre-Alain TORET * are met:
88311733cSPierre-Alain TORET * 1. Redistributions of source code must retain the above copyright
98311733cSPierre-Alain TORET * notice, this list of conditions and the following disclaimer.
108311733cSPierre-Alain TORET * 2. Redistributions in binary form must reproduce the above copyright
118311733cSPierre-Alain TORET * notice, this list of conditions and the following disclaimer in the
128311733cSPierre-Alain TORET * documentation and/or other materials provided with the distribution.
138311733cSPierre-Alain TORET *
148311733cSPierre-Alain TORET * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
158311733cSPierre-Alain TORET * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
168311733cSPierre-Alain TORET * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
178311733cSPierre-Alain TORET * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
188311733cSPierre-Alain TORET * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
198311733cSPierre-Alain TORET * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
208311733cSPierre-Alain TORET * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
218311733cSPierre-Alain TORET * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
228311733cSPierre-Alain TORET * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
238311733cSPierre-Alain TORET * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
248311733cSPierre-Alain TORET * POSSIBILITY OF SUCH DAMAGE.
2523be8282SSascha Wildner *
262a07801cSSascha Wildner * $FreeBSD: head/sys/dev/amdsmn/amdsmn.c 366136 2020-09-25 04:16:28Z cem $
278311733cSPierre-Alain TORET */
288311733cSPierre-Alain TORET
298311733cSPierre-Alain TORET /*
30*4970ada5SMatthew Dillon * Driver for the AMD Family 15h, 17h, 19h CPU System Management Network.
318311733cSPierre-Alain TORET */
328311733cSPierre-Alain TORET
338311733cSPierre-Alain TORET #include <sys/param.h>
348311733cSPierre-Alain TORET #include <sys/bus.h>
358311733cSPierre-Alain TORET #include <sys/conf.h>
368311733cSPierre-Alain TORET #include <sys/lock.h>
378311733cSPierre-Alain TORET #include <sys/kernel.h>
388311733cSPierre-Alain TORET #include <sys/module.h>
398311733cSPierre-Alain TORET #include <sys/mutex.h>
408311733cSPierre-Alain TORET #include <sys/sysctl.h>
418311733cSPierre-Alain TORET #include <sys/systm.h>
428311733cSPierre-Alain TORET
438311733cSPierre-Alain TORET #include <machine/cpufunc.h>
448311733cSPierre-Alain TORET #include <machine/cputypes.h>
458311733cSPierre-Alain TORET #include <machine/md_var.h>
468311733cSPierre-Alain TORET #include <machine/specialreg.h>
478311733cSPierre-Alain TORET
488311733cSPierre-Alain TORET #include <bus/pci/pcivar.h>
498311733cSPierre-Alain TORET #include <bus/pci/pci_cfgreg.h>
508311733cSPierre-Alain TORET
518311733cSPierre-Alain TORET #include <dev/powermng/amdsmn/amdsmn.h>
528311733cSPierre-Alain TORET
538311733cSPierre-Alain TORET #define F15H_SMN_ADDR_REG 0xb8
548311733cSPierre-Alain TORET #define F15H_SMN_DATA_REG 0xbc
55*4970ada5SMatthew Dillon
568311733cSPierre-Alain TORET #define F17H_SMN_ADDR_REG 0x60
578311733cSPierre-Alain TORET #define F17H_SMN_DATA_REG 0x64
588311733cSPierre-Alain TORET
59*4970ada5SMatthew Dillon #define F19H_SMN_ADDR_REG 0x60
60*4970ada5SMatthew Dillon #define F19H_SMN_DATA_REG 0x64
61*4970ada5SMatthew Dillon
628311733cSPierre-Alain TORET #define PCI_DEVICE_ID_AMD_15H_M60H_ROOT 0x1576
638311733cSPierre-Alain TORET #define PCI_DEVICE_ID_AMD_17H_ROOT 0x1450
648311733cSPierre-Alain TORET #define PCI_DEVICE_ID_AMD_17H_M10H_ROOT 0x15d0
658311733cSPierre-Alain TORET #define PCI_DEVICE_ID_AMD_17H_M30H_ROOT 0x1480 /* Also M70H. */
662a07801cSSascha Wildner #define PCI_DEVICE_ID_AMD_17H_M60H_ROOT 0x1630
67*4970ada5SMatthew Dillon #define PCI_DEVICE_ID_AMD_17H_MA0H_ROOT 0x14b5
68*4970ada5SMatthew Dillon
69*4970ada5SMatthew Dillon #if 0
70*4970ada5SMatthew Dillon #define PCI_DEVICE_ID_AMD_19H_M10H_ROOT 0x14b1
71*4970ada5SMatthew Dillon #define PCI_DEVICE_ID_AMD_19H_M40H_ROOT 0x14b5
72*4970ada5SMatthew Dillon #define PCI_DEVICE_ID_AMD_19H_M50H_ROOT 0x166e
73*4970ada5SMatthew Dillon #define PCI_DEVICE_ID_AMD_19H_M60H_ROOT 0x14e4
74*4970ada5SMatthew Dillon #define PCI_DEVICE_ID_AMD_19H_M70H_ROOT 0x14f4
75*4970ada5SMatthew Dillon #endif
76*4970ada5SMatthew Dillon #define PCI_DEVICE_ID_AMD_19H_M10H_ROOT 0x14a4
77*4970ada5SMatthew Dillon #define PCI_DEVICE_ID_AMD_19H_M60H_ROOT 0x14d8
78*4970ada5SMatthew Dillon #define PCI_DEVICE_ID_AMD_19H_M70H_ROOT 0x14e8
798311733cSPierre-Alain TORET
808311733cSPierre-Alain TORET struct pciid;
818311733cSPierre-Alain TORET struct amdsmn_softc {
828311733cSPierre-Alain TORET struct lock smn_lock;
838311733cSPierre-Alain TORET const struct pciid *smn_pciid;
848311733cSPierre-Alain TORET };
858311733cSPierre-Alain TORET
868311733cSPierre-Alain TORET static const struct pciid {
878311733cSPierre-Alain TORET uint16_t amdsmn_vendorid;
888311733cSPierre-Alain TORET uint16_t amdsmn_deviceid;
898311733cSPierre-Alain TORET uint8_t amdsmn_addr_reg;
908311733cSPierre-Alain TORET uint8_t amdsmn_data_reg;
918311733cSPierre-Alain TORET } amdsmn_ids[] = {
928311733cSPierre-Alain TORET {
938311733cSPierre-Alain TORET .amdsmn_vendorid = CPU_VENDOR_AMD,
948311733cSPierre-Alain TORET .amdsmn_deviceid = PCI_DEVICE_ID_AMD_15H_M60H_ROOT,
958311733cSPierre-Alain TORET .amdsmn_addr_reg = F15H_SMN_ADDR_REG,
968311733cSPierre-Alain TORET .amdsmn_data_reg = F15H_SMN_DATA_REG,
978311733cSPierre-Alain TORET },
988311733cSPierre-Alain TORET {
998311733cSPierre-Alain TORET .amdsmn_vendorid = CPU_VENDOR_AMD,
1008311733cSPierre-Alain TORET .amdsmn_deviceid = PCI_DEVICE_ID_AMD_17H_ROOT,
1018311733cSPierre-Alain TORET .amdsmn_addr_reg = F17H_SMN_ADDR_REG,
1028311733cSPierre-Alain TORET .amdsmn_data_reg = F17H_SMN_DATA_REG,
1038311733cSPierre-Alain TORET },
1048311733cSPierre-Alain TORET {
1058311733cSPierre-Alain TORET .amdsmn_vendorid = CPU_VENDOR_AMD,
1068311733cSPierre-Alain TORET .amdsmn_deviceid = PCI_DEVICE_ID_AMD_17H_M10H_ROOT,
1078311733cSPierre-Alain TORET .amdsmn_addr_reg = F17H_SMN_ADDR_REG,
1088311733cSPierre-Alain TORET .amdsmn_data_reg = F17H_SMN_DATA_REG,
1098311733cSPierre-Alain TORET },
1108311733cSPierre-Alain TORET {
1118311733cSPierre-Alain TORET .amdsmn_vendorid = CPU_VENDOR_AMD,
1128311733cSPierre-Alain TORET .amdsmn_deviceid = PCI_DEVICE_ID_AMD_17H_M30H_ROOT,
1138311733cSPierre-Alain TORET .amdsmn_addr_reg = F17H_SMN_ADDR_REG,
1148311733cSPierre-Alain TORET .amdsmn_data_reg = F17H_SMN_DATA_REG,
1158311733cSPierre-Alain TORET },
1162a07801cSSascha Wildner {
1172a07801cSSascha Wildner .amdsmn_vendorid = CPU_VENDOR_AMD,
1182a07801cSSascha Wildner .amdsmn_deviceid = PCI_DEVICE_ID_AMD_17H_M60H_ROOT,
1192a07801cSSascha Wildner .amdsmn_addr_reg = F17H_SMN_ADDR_REG,
1202a07801cSSascha Wildner .amdsmn_data_reg = F17H_SMN_DATA_REG,
1212a07801cSSascha Wildner },
122*4970ada5SMatthew Dillon {
123*4970ada5SMatthew Dillon .amdsmn_vendorid = CPU_VENDOR_AMD,
124*4970ada5SMatthew Dillon .amdsmn_deviceid = PCI_DEVICE_ID_AMD_17H_MA0H_ROOT,
125*4970ada5SMatthew Dillon .amdsmn_addr_reg = F17H_SMN_ADDR_REG,
126*4970ada5SMatthew Dillon .amdsmn_data_reg = F17H_SMN_DATA_REG,
127*4970ada5SMatthew Dillon },
128*4970ada5SMatthew Dillon {
129*4970ada5SMatthew Dillon .amdsmn_vendorid = CPU_VENDOR_AMD,
130*4970ada5SMatthew Dillon .amdsmn_deviceid = PCI_DEVICE_ID_AMD_19H_M10H_ROOT,
131*4970ada5SMatthew Dillon .amdsmn_addr_reg = F19H_SMN_ADDR_REG,
132*4970ada5SMatthew Dillon .amdsmn_data_reg = F19H_SMN_DATA_REG,
133*4970ada5SMatthew Dillon },
134*4970ada5SMatthew Dillon #if 0
135*4970ada5SMatthew Dillon {
136*4970ada5SMatthew Dillon .amdsmn_vendorid = CPU_VENDOR_AMD,
137*4970ada5SMatthew Dillon .amdsmn_deviceid = PCI_DEVICE_ID_AMD_19H_M40H_ROOT,
138*4970ada5SMatthew Dillon .amdsmn_addr_reg = F19H_SMN_ADDR_REG,
139*4970ada5SMatthew Dillon .amdsmn_data_reg = F19H_SMN_DATA_REG,
140*4970ada5SMatthew Dillon },
141*4970ada5SMatthew Dillon {
142*4970ada5SMatthew Dillon .amdsmn_vendorid = CPU_VENDOR_AMD,
143*4970ada5SMatthew Dillon .amdsmn_deviceid = PCI_DEVICE_ID_AMD_19H_M50H_ROOT,
144*4970ada5SMatthew Dillon .amdsmn_addr_reg = F19H_SMN_ADDR_REG,
145*4970ada5SMatthew Dillon .amdsmn_data_reg = F19H_SMN_DATA_REG,
146*4970ada5SMatthew Dillon },
147*4970ada5SMatthew Dillon #endif
148*4970ada5SMatthew Dillon {
149*4970ada5SMatthew Dillon .amdsmn_vendorid = CPU_VENDOR_AMD,
150*4970ada5SMatthew Dillon .amdsmn_deviceid = PCI_DEVICE_ID_AMD_19H_M60H_ROOT,
151*4970ada5SMatthew Dillon .amdsmn_addr_reg = F19H_SMN_ADDR_REG,
152*4970ada5SMatthew Dillon .amdsmn_data_reg = F19H_SMN_DATA_REG,
153*4970ada5SMatthew Dillon },
154*4970ada5SMatthew Dillon {
155*4970ada5SMatthew Dillon .amdsmn_vendorid = CPU_VENDOR_AMD,
156*4970ada5SMatthew Dillon .amdsmn_deviceid = PCI_DEVICE_ID_AMD_19H_M70H_ROOT,
157*4970ada5SMatthew Dillon .amdsmn_addr_reg = F19H_SMN_ADDR_REG,
158*4970ada5SMatthew Dillon .amdsmn_data_reg = F19H_SMN_DATA_REG,
159*4970ada5SMatthew Dillon },
1608311733cSPierre-Alain TORET };
1618311733cSPierre-Alain TORET
1628311733cSPierre-Alain TORET /*
1638311733cSPierre-Alain TORET * Device methods.
1648311733cSPierre-Alain TORET */
1658311733cSPierre-Alain TORET static void amdsmn_identify(driver_t *driver, device_t parent);
1668311733cSPierre-Alain TORET static int amdsmn_probe(device_t dev);
1678311733cSPierre-Alain TORET static int amdsmn_attach(device_t dev);
1688311733cSPierre-Alain TORET static int amdsmn_detach(device_t dev);
1698311733cSPierre-Alain TORET
1708311733cSPierre-Alain TORET static device_method_t amdsmn_methods[] = {
1718311733cSPierre-Alain TORET /* Device interface */
1728311733cSPierre-Alain TORET DEVMETHOD(device_identify, amdsmn_identify),
1738311733cSPierre-Alain TORET DEVMETHOD(device_probe, amdsmn_probe),
1748311733cSPierre-Alain TORET DEVMETHOD(device_attach, amdsmn_attach),
1758311733cSPierre-Alain TORET DEVMETHOD(device_detach, amdsmn_detach),
1768311733cSPierre-Alain TORET DEVMETHOD_END
1778311733cSPierre-Alain TORET };
1788311733cSPierre-Alain TORET
1798311733cSPierre-Alain TORET static driver_t amdsmn_driver = {
1808311733cSPierre-Alain TORET "amdsmn",
1818311733cSPierre-Alain TORET amdsmn_methods,
1828311733cSPierre-Alain TORET sizeof(struct amdsmn_softc),
1838311733cSPierre-Alain TORET };
1848311733cSPierre-Alain TORET
1858311733cSPierre-Alain TORET static devclass_t amdsmn_devclass;
186*4970ada5SMatthew Dillon DRIVER_MODULE_ORDERED(amdsmn, hostb, amdsmn_driver,
187*4970ada5SMatthew Dillon &amdsmn_devclass, NULL, NULL, SI_ORDER_EARLIER);
1888311733cSPierre-Alain TORET MODULE_VERSION(amdsmn, 1);
1898311733cSPierre-Alain TORET #if !defined(__DragonFly__)
1908311733cSPierre-Alain TORET MODULE_PNP_INFO("U16:vendor;U16:device", pci, amdsmn, amdsmn_ids,
1918311733cSPierre-Alain TORET nitems(amdsmn_ids));
1928311733cSPierre-Alain TORET #endif
1938311733cSPierre-Alain TORET
1948311733cSPierre-Alain TORET static bool
amdsmn_match(device_t parent,const struct pciid ** pciid_out)1958311733cSPierre-Alain TORET amdsmn_match(device_t parent, const struct pciid **pciid_out)
1968311733cSPierre-Alain TORET {
1978311733cSPierre-Alain TORET uint16_t vendor, device;
1988311733cSPierre-Alain TORET size_t i;
1998311733cSPierre-Alain TORET
2008311733cSPierre-Alain TORET vendor = pci_get_vendor(parent);
2018311733cSPierre-Alain TORET device = pci_get_device(parent);
2028311733cSPierre-Alain TORET
2038311733cSPierre-Alain TORET for (i = 0; i < nitems(amdsmn_ids); i++) {
2048311733cSPierre-Alain TORET if (vendor == amdsmn_ids[i].amdsmn_vendorid &&
2058311733cSPierre-Alain TORET device == amdsmn_ids[i].amdsmn_deviceid) {
2068311733cSPierre-Alain TORET if (pciid_out != NULL)
2078311733cSPierre-Alain TORET *pciid_out = &amdsmn_ids[i];
2088311733cSPierre-Alain TORET return (true);
2098311733cSPierre-Alain TORET }
2108311733cSPierre-Alain TORET }
2118311733cSPierre-Alain TORET return (false);
2128311733cSPierre-Alain TORET }
2138311733cSPierre-Alain TORET
2148311733cSPierre-Alain TORET static void
amdsmn_identify(driver_t * driver,device_t parent)2158311733cSPierre-Alain TORET amdsmn_identify(driver_t *driver, device_t parent)
2168311733cSPierre-Alain TORET {
2178311733cSPierre-Alain TORET device_t child;
2188311733cSPierre-Alain TORET
2198311733cSPierre-Alain TORET /* Make sure we're not being doubly invoked. */
2208311733cSPierre-Alain TORET if (device_find_child(parent, "amdsmn", -1) != NULL)
2218311733cSPierre-Alain TORET return;
2228311733cSPierre-Alain TORET if (!amdsmn_match(parent, NULL))
2238311733cSPierre-Alain TORET return;
2248311733cSPierre-Alain TORET
2258311733cSPierre-Alain TORET child = device_add_child(parent, "amdsmn", -1);
2268311733cSPierre-Alain TORET if (child == NULL)
2278311733cSPierre-Alain TORET device_printf(parent, "add amdsmn child failed\n");
2288311733cSPierre-Alain TORET }
2298311733cSPierre-Alain TORET
2308311733cSPierre-Alain TORET static int
amdsmn_probe(device_t dev)2318311733cSPierre-Alain TORET amdsmn_probe(device_t dev)
2328311733cSPierre-Alain TORET {
2338311733cSPierre-Alain TORET uint32_t family;
2348311733cSPierre-Alain TORET char buf[64];
2358311733cSPierre-Alain TORET
2368311733cSPierre-Alain TORET if (resource_disabled("amdsmn", 0))
2378311733cSPierre-Alain TORET return (ENXIO);
2388311733cSPierre-Alain TORET if (!amdsmn_match(device_get_parent(dev), NULL))
2398311733cSPierre-Alain TORET return (ENXIO);
2408311733cSPierre-Alain TORET
2418311733cSPierre-Alain TORET family = CPUID_TO_FAMILY(cpu_id);
2428311733cSPierre-Alain TORET
2438311733cSPierre-Alain TORET switch (family) {
2448311733cSPierre-Alain TORET case 0x15:
2458311733cSPierre-Alain TORET case 0x17:
246d834e1dcSMatthew Dillon case 0x19:
2478311733cSPierre-Alain TORET break;
2488311733cSPierre-Alain TORET default:
2498311733cSPierre-Alain TORET return (ENXIO);
2508311733cSPierre-Alain TORET }
2518311733cSPierre-Alain TORET ksnprintf(buf, sizeof(buf), "AMD Family %xh System Management Network",
2528311733cSPierre-Alain TORET family);
2538311733cSPierre-Alain TORET device_set_desc_copy(dev, buf);
2548311733cSPierre-Alain TORET
2558311733cSPierre-Alain TORET return (BUS_PROBE_GENERIC);
2568311733cSPierre-Alain TORET }
2578311733cSPierre-Alain TORET
2588311733cSPierre-Alain TORET static int
amdsmn_attach(device_t dev)2598311733cSPierre-Alain TORET amdsmn_attach(device_t dev)
2608311733cSPierre-Alain TORET {
2618311733cSPierre-Alain TORET struct amdsmn_softc *sc = device_get_softc(dev);
2628311733cSPierre-Alain TORET
2638311733cSPierre-Alain TORET if (!amdsmn_match(device_get_parent(dev), &sc->smn_pciid))
2648311733cSPierre-Alain TORET return (ENXIO);
2658311733cSPierre-Alain TORET
2668311733cSPierre-Alain TORET lockinit(&sc->smn_lock, device_get_nameunit(dev), 0, 0);
2678311733cSPierre-Alain TORET return (0);
2688311733cSPierre-Alain TORET }
2698311733cSPierre-Alain TORET
2708311733cSPierre-Alain TORET int
amdsmn_detach(device_t dev)2718311733cSPierre-Alain TORET amdsmn_detach(device_t dev)
2728311733cSPierre-Alain TORET {
2738311733cSPierre-Alain TORET struct amdsmn_softc *sc = device_get_softc(dev);
2748311733cSPierre-Alain TORET
2758311733cSPierre-Alain TORET lockuninit(&sc->smn_lock);
2768311733cSPierre-Alain TORET return (0);
2778311733cSPierre-Alain TORET }
2788311733cSPierre-Alain TORET
2798311733cSPierre-Alain TORET int
amdsmn_read(device_t dev,uint32_t addr,uint32_t * value)2808311733cSPierre-Alain TORET amdsmn_read(device_t dev, uint32_t addr, uint32_t *value)
2818311733cSPierre-Alain TORET {
2828311733cSPierre-Alain TORET struct amdsmn_softc *sc = device_get_softc(dev);
2838311733cSPierre-Alain TORET device_t parent;
2848311733cSPierre-Alain TORET
2858311733cSPierre-Alain TORET parent = device_get_parent(dev);
2868311733cSPierre-Alain TORET
2878311733cSPierre-Alain TORET lockmgr(&sc->smn_lock, LK_EXCLUSIVE);
2888311733cSPierre-Alain TORET pci_write_config(parent, sc->smn_pciid->amdsmn_addr_reg, addr, 4);
2898311733cSPierre-Alain TORET *value = pci_read_config(parent, sc->smn_pciid->amdsmn_data_reg, 4);
2908311733cSPierre-Alain TORET lockmgr(&sc->smn_lock, LK_RELEASE);
2918311733cSPierre-Alain TORET
2928311733cSPierre-Alain TORET return (0);
2938311733cSPierre-Alain TORET }
2948311733cSPierre-Alain TORET
2958311733cSPierre-Alain TORET int
amdsmn_write(device_t dev,uint32_t addr,uint32_t value)2968311733cSPierre-Alain TORET amdsmn_write(device_t dev, uint32_t addr, uint32_t value)
2978311733cSPierre-Alain TORET {
2988311733cSPierre-Alain TORET struct amdsmn_softc *sc = device_get_softc(dev);
2998311733cSPierre-Alain TORET device_t parent;
3008311733cSPierre-Alain TORET
3018311733cSPierre-Alain TORET parent = device_get_parent(dev);
3028311733cSPierre-Alain TORET
3038311733cSPierre-Alain TORET lockmgr(&sc->smn_lock, LK_EXCLUSIVE);
3048311733cSPierre-Alain TORET pci_write_config(parent, sc->smn_pciid->amdsmn_addr_reg, addr, 4);
3058311733cSPierre-Alain TORET pci_write_config(parent, sc->smn_pciid->amdsmn_data_reg, value, 4);
3068311733cSPierre-Alain TORET lockmgr(&sc->smn_lock, LK_RELEASE);
3078311733cSPierre-Alain TORET
3088311733cSPierre-Alain TORET return (0);
3098311733cSPierre-Alain TORET }
310