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