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