]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/amdsmn/amdsmn.c
MFV r359401: OpenBSM: import ee79d73e8df5: auditreduce: add a zone filter
[FreeBSD/FreeBSD.git] / sys / dev / amdsmn / amdsmn.c
1 /*-
2  * Copyright (c) 2017-2019 Conrad Meyer <cem@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
18  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 /*
28  * Driver for the AMD Family 15h and 17h CPU System Management Network.
29  */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include <sys/param.h>
35 #include <sys/bus.h>
36 #include <sys/conf.h>
37 #include <sys/lock.h>
38 #include <sys/kernel.h>
39 #include <sys/module.h>
40 #include <sys/mutex.h>
41 #include <sys/sysctl.h>
42 #include <sys/systm.h>
43
44 #include <machine/cpufunc.h>
45 #include <machine/cputypes.h>
46 #include <machine/md_var.h>
47 #include <machine/specialreg.h>
48
49 #include <dev/pci/pcivar.h>
50 #include <x86/pci_cfgreg.h>
51
52 #include <dev/amdsmn/amdsmn.h>
53
54 #define F15H_SMN_ADDR_REG       0xb8
55 #define F15H_SMN_DATA_REG       0xbc
56 #define F17H_SMN_ADDR_REG       0x60
57 #define F17H_SMN_DATA_REG       0x64
58
59 #define PCI_DEVICE_ID_AMD_15H_M60H_ROOT         0x1576
60 #define PCI_DEVICE_ID_AMD_17H_ROOT              0x1450
61 #define PCI_DEVICE_ID_AMD_17H_M10H_ROOT         0x15d0
62 #define PCI_DEVICE_ID_AMD_17H_M30H_ROOT         0x1480  /* Also M70H. */
63
64 struct pciid;
65 struct amdsmn_softc {
66         struct mtx smn_lock;
67         const struct pciid *smn_pciid;
68 };
69
70 static const struct pciid {
71         uint16_t        amdsmn_vendorid;
72         uint16_t        amdsmn_deviceid;
73         uint8_t         amdsmn_addr_reg;
74         uint8_t         amdsmn_data_reg;
75 } amdsmn_ids[] = {
76         {
77                 .amdsmn_vendorid = CPU_VENDOR_AMD,
78                 .amdsmn_deviceid = PCI_DEVICE_ID_AMD_15H_M60H_ROOT,
79                 .amdsmn_addr_reg = F15H_SMN_ADDR_REG,
80                 .amdsmn_data_reg = F15H_SMN_DATA_REG,
81         },
82         {
83                 .amdsmn_vendorid = CPU_VENDOR_AMD,
84                 .amdsmn_deviceid = PCI_DEVICE_ID_AMD_17H_ROOT,
85                 .amdsmn_addr_reg = F17H_SMN_ADDR_REG,
86                 .amdsmn_data_reg = F17H_SMN_DATA_REG,
87         },
88         {
89                 .amdsmn_vendorid = CPU_VENDOR_AMD,
90                 .amdsmn_deviceid = PCI_DEVICE_ID_AMD_17H_M10H_ROOT,
91                 .amdsmn_addr_reg = F17H_SMN_ADDR_REG,
92                 .amdsmn_data_reg = F17H_SMN_DATA_REG,
93         },
94         {
95                 .amdsmn_vendorid = CPU_VENDOR_AMD,
96                 .amdsmn_deviceid = PCI_DEVICE_ID_AMD_17H_M30H_ROOT,
97                 .amdsmn_addr_reg = F17H_SMN_ADDR_REG,
98                 .amdsmn_data_reg = F17H_SMN_DATA_REG,
99         },
100 };
101
102 /*
103  * Device methods.
104  */
105 static void     amdsmn_identify(driver_t *driver, device_t parent);
106 static int      amdsmn_probe(device_t dev);
107 static int      amdsmn_attach(device_t dev);
108 static int      amdsmn_detach(device_t dev);
109
110 static device_method_t amdsmn_methods[] = {
111         /* Device interface */
112         DEVMETHOD(device_identify,      amdsmn_identify),
113         DEVMETHOD(device_probe,         amdsmn_probe),
114         DEVMETHOD(device_attach,        amdsmn_attach),
115         DEVMETHOD(device_detach,        amdsmn_detach),
116         DEVMETHOD_END
117 };
118
119 static driver_t amdsmn_driver = {
120         "amdsmn",
121         amdsmn_methods,
122         sizeof(struct amdsmn_softc),
123 };
124
125 static devclass_t amdsmn_devclass;
126 DRIVER_MODULE(amdsmn, hostb, amdsmn_driver, amdsmn_devclass, NULL, NULL);
127 MODULE_VERSION(amdsmn, 1);
128 MODULE_PNP_INFO("U16:vendor;U16:device", pci, amdsmn, amdsmn_ids,
129     nitems(amdsmn_ids));
130
131 static bool
132 amdsmn_match(device_t parent, const struct pciid **pciid_out)
133 {
134         uint16_t vendor, device;
135         size_t i;
136
137         vendor = pci_get_vendor(parent);
138         device = pci_get_device(parent);
139
140         for (i = 0; i < nitems(amdsmn_ids); i++) {
141                 if (vendor == amdsmn_ids[i].amdsmn_vendorid &&
142                     device == amdsmn_ids[i].amdsmn_deviceid) {
143                         if (pciid_out != NULL)
144                                 *pciid_out = &amdsmn_ids[i];
145                         return (true);
146                 }
147         }
148         return (false);
149 }
150
151 static void
152 amdsmn_identify(driver_t *driver, device_t parent)
153 {
154         device_t child;
155
156         /* Make sure we're not being doubly invoked. */
157         if (device_find_child(parent, "amdsmn", -1) != NULL)
158                 return;
159         if (!amdsmn_match(parent, NULL))
160                 return;
161
162         child = device_add_child(parent, "amdsmn", -1);
163         if (child == NULL)
164                 device_printf(parent, "add amdsmn child failed\n");
165 }
166
167 static int
168 amdsmn_probe(device_t dev)
169 {
170         uint32_t family;
171         char buf[64];
172
173         if (resource_disabled("amdsmn", 0))
174                 return (ENXIO);
175         if (!amdsmn_match(device_get_parent(dev), NULL))
176                 return (ENXIO);
177
178         family = CPUID_TO_FAMILY(cpu_id);
179
180         switch (family) {
181         case 0x15:
182         case 0x17:
183                 break;
184         default:
185                 return (ENXIO);
186         }
187         snprintf(buf, sizeof(buf), "AMD Family %xh System Management Network",
188             family);
189         device_set_desc_copy(dev, buf);
190
191         return (BUS_PROBE_GENERIC);
192 }
193
194 static int
195 amdsmn_attach(device_t dev)
196 {
197         struct amdsmn_softc *sc = device_get_softc(dev);
198
199         if (!amdsmn_match(device_get_parent(dev), &sc->smn_pciid))
200                 return (ENXIO);
201
202         mtx_init(&sc->smn_lock, "SMN mtx", "SMN", MTX_DEF);
203         return (0);
204 }
205
206 int
207 amdsmn_detach(device_t dev)
208 {
209         struct amdsmn_softc *sc = device_get_softc(dev);
210
211         mtx_destroy(&sc->smn_lock);
212         return (0);
213 }
214
215 int
216 amdsmn_read(device_t dev, uint32_t addr, uint32_t *value)
217 {
218         struct amdsmn_softc *sc = device_get_softc(dev);
219         device_t parent;
220
221         parent = device_get_parent(dev);
222
223         mtx_lock(&sc->smn_lock);
224         pci_write_config(parent, sc->smn_pciid->amdsmn_addr_reg, addr, 4);
225         *value = pci_read_config(parent, sc->smn_pciid->amdsmn_data_reg, 4);
226         mtx_unlock(&sc->smn_lock);
227
228         return (0);
229 }
230
231 int
232 amdsmn_write(device_t dev, uint32_t addr, uint32_t value)
233 {
234         struct amdsmn_softc *sc = device_get_softc(dev);
235         device_t parent;
236
237         parent = device_get_parent(dev);
238
239         mtx_lock(&sc->smn_lock);
240         pci_write_config(parent, sc->smn_pciid->amdsmn_addr_reg, addr, 4);
241         pci_write_config(parent, sc->smn_pciid->amdsmn_data_reg, value, 4);
242         mtx_unlock(&sc->smn_lock);
243
244         return (0);
245 }