]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/amdsmn/amdsmn.c
Merge LLVM libunwind trunk r351319, from just before upstream's
[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
63 struct pciid;
64 struct amdsmn_softc {
65         struct mtx smn_lock;
66         const struct pciid *smn_pciid;
67 };
68
69 static const struct pciid {
70         uint16_t        amdsmn_vendorid;
71         uint16_t        amdsmn_deviceid;
72         uint8_t         amdsmn_addr_reg;
73         uint8_t         amdsmn_data_reg;
74 } amdsmn_ids[] = {
75         {
76                 .amdsmn_vendorid = CPU_VENDOR_AMD,
77                 .amdsmn_deviceid = PCI_DEVICE_ID_AMD_15H_M60H_ROOT,
78                 .amdsmn_addr_reg = F15H_SMN_ADDR_REG,
79                 .amdsmn_data_reg = F15H_SMN_DATA_REG,
80         },
81         {
82                 .amdsmn_vendorid = CPU_VENDOR_AMD,
83                 .amdsmn_deviceid = PCI_DEVICE_ID_AMD_17H_ROOT,
84                 .amdsmn_addr_reg = F17H_SMN_ADDR_REG,
85                 .amdsmn_data_reg = F17H_SMN_DATA_REG,
86         },
87         {
88                 .amdsmn_vendorid = CPU_VENDOR_AMD,
89                 .amdsmn_deviceid = PCI_DEVICE_ID_AMD_17H_M10H_ROOT,
90                 .amdsmn_addr_reg = F17H_SMN_ADDR_REG,
91                 .amdsmn_data_reg = F17H_SMN_DATA_REG,
92         },
93 };
94
95 /*
96  * Device methods.
97  */
98 static void     amdsmn_identify(driver_t *driver, device_t parent);
99 static int      amdsmn_probe(device_t dev);
100 static int      amdsmn_attach(device_t dev);
101 static int      amdsmn_detach(device_t dev);
102
103 static device_method_t amdsmn_methods[] = {
104         /* Device interface */
105         DEVMETHOD(device_identify,      amdsmn_identify),
106         DEVMETHOD(device_probe,         amdsmn_probe),
107         DEVMETHOD(device_attach,        amdsmn_attach),
108         DEVMETHOD(device_detach,        amdsmn_detach),
109         DEVMETHOD_END
110 };
111
112 static driver_t amdsmn_driver = {
113         "amdsmn",
114         amdsmn_methods,
115         sizeof(struct amdsmn_softc),
116 };
117
118 static devclass_t amdsmn_devclass;
119 DRIVER_MODULE(amdsmn, hostb, amdsmn_driver, amdsmn_devclass, NULL, NULL);
120 MODULE_VERSION(amdsmn, 1);
121 MODULE_PNP_INFO("U16:vendor;U16:device", pci, amdsmn, amdsmn_ids,
122     nitems(amdsmn_ids));
123
124 static bool
125 amdsmn_match(device_t parent, const struct pciid **pciid_out)
126 {
127         uint16_t vendor, device;
128         size_t i;
129
130         vendor = pci_get_vendor(parent);
131         device = pci_get_device(parent);
132
133         for (i = 0; i < nitems(amdsmn_ids); i++) {
134                 if (vendor == amdsmn_ids[i].amdsmn_vendorid &&
135                     device == amdsmn_ids[i].amdsmn_deviceid) {
136                         if (pciid_out != NULL)
137                                 *pciid_out = &amdsmn_ids[i];
138                         return (true);
139                 }
140         }
141         return (false);
142 }
143
144 static void
145 amdsmn_identify(driver_t *driver, device_t parent)
146 {
147         device_t child;
148
149         /* Make sure we're not being doubly invoked. */
150         if (device_find_child(parent, "amdsmn", -1) != NULL)
151                 return;
152         if (!amdsmn_match(parent, NULL))
153                 return;
154
155         child = device_add_child(parent, "amdsmn", -1);
156         if (child == NULL)
157                 device_printf(parent, "add amdsmn child failed\n");
158 }
159
160 static int
161 amdsmn_probe(device_t dev)
162 {
163         uint32_t family;
164         char buf[64];
165
166         if (resource_disabled("amdsmn", 0))
167                 return (ENXIO);
168         if (!amdsmn_match(device_get_parent(dev), NULL))
169                 return (ENXIO);
170
171         family = CPUID_TO_FAMILY(cpu_id);
172
173         switch (family) {
174         case 0x15:
175         case 0x17:
176                 break;
177         default:
178                 return (ENXIO);
179         }
180         snprintf(buf, sizeof(buf), "AMD Family %xh System Management Network",
181             family);
182         device_set_desc_copy(dev, buf);
183
184         return (BUS_PROBE_GENERIC);
185 }
186
187 static int
188 amdsmn_attach(device_t dev)
189 {
190         struct amdsmn_softc *sc = device_get_softc(dev);
191
192         if (!amdsmn_match(device_get_parent(dev), &sc->smn_pciid))
193                 return (ENXIO);
194
195         mtx_init(&sc->smn_lock, "SMN mtx", "SMN", MTX_DEF);
196         return (0);
197 }
198
199 int
200 amdsmn_detach(device_t dev)
201 {
202         struct amdsmn_softc *sc = device_get_softc(dev);
203
204         mtx_destroy(&sc->smn_lock);
205         return (0);
206 }
207
208 int
209 amdsmn_read(device_t dev, uint32_t addr, uint32_t *value)
210 {
211         struct amdsmn_softc *sc = device_get_softc(dev);
212         device_t parent;
213
214         parent = device_get_parent(dev);
215
216         mtx_lock(&sc->smn_lock);
217         pci_write_config(parent, sc->smn_pciid->amdsmn_addr_reg, addr, 4);
218         *value = pci_read_config(parent, sc->smn_pciid->amdsmn_data_reg, 4);
219         mtx_unlock(&sc->smn_lock);
220
221         return (0);
222 }
223
224 int
225 amdsmn_write(device_t dev, uint32_t addr, uint32_t value)
226 {
227         struct amdsmn_softc *sc = device_get_softc(dev);
228         device_t parent;
229
230         parent = device_get_parent(dev);
231
232         mtx_lock(&sc->smn_lock);
233         pci_write_config(parent, sc->smn_pciid->amdsmn_addr_reg, addr, 4);
234         pci_write_config(parent, sc->smn_pciid->amdsmn_data_reg, value, 4);
235         mtx_unlock(&sc->smn_lock);
236
237         return (0);
238 }