amdsmn.c revision 343325
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 343325 2019-01-22 21:35:25Z 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
62343323Smav
63343325Smavstruct pciid;
64323184Scemstruct amdsmn_softc {
65323184Scem	struct mtx smn_lock;
66343325Smav	const struct pciid *smn_pciid;
67323184Scem};
68323184Scem
69343325Smavstatic const struct pciid {
70343323Smav	uint16_t	amdsmn_vendorid;
71343323Smav	uint16_t	amdsmn_deviceid;
72343325Smav	uint8_t		amdsmn_addr_reg;
73343325Smav	uint8_t		amdsmn_data_reg;
74323184Scem} amdsmn_ids[] = {
75343325Smav	{
76343325Smav		.amdsmn_vendorid = CPU_VENDOR_AMD,
77343325Smav		.amdsmn_deviceid = PCI_DEVICE_ID_AMD_15H_M60H_ROOT,
78343325Smav		.amdsmn_addr_reg = F15H_SMN_ADDR_REG,
79343325Smav		.amdsmn_data_reg = F15H_SMN_DATA_REG,
80343325Smav	},
81343325Smav	{
82343325Smav		.amdsmn_vendorid = CPU_VENDOR_AMD,
83343325Smav		.amdsmn_deviceid = PCI_DEVICE_ID_AMD_17H_ROOT,
84343325Smav		.amdsmn_addr_reg = F17H_SMN_ADDR_REG,
85343325Smav		.amdsmn_data_reg = F17H_SMN_DATA_REG,
86343325Smav	},
87343325Smav	{
88343325Smav		.amdsmn_vendorid = CPU_VENDOR_AMD,
89343325Smav		.amdsmn_deviceid = PCI_DEVICE_ID_AMD_17H_M10H_ROOT,
90343325Smav		.amdsmn_addr_reg = F17H_SMN_ADDR_REG,
91343325Smav		.amdsmn_data_reg = F17H_SMN_DATA_REG,
92343325Smav	},
93323184Scem};
94323184Scem
95323184Scem/*
96323184Scem * Device methods.
97323184Scem */
98323184Scemstatic void 	amdsmn_identify(driver_t *driver, device_t parent);
99323184Scemstatic int	amdsmn_probe(device_t dev);
100323184Scemstatic int	amdsmn_attach(device_t dev);
101323184Scemstatic int	amdsmn_detach(device_t dev);
102323184Scem
103323184Scemstatic device_method_t amdsmn_methods[] = {
104323184Scem	/* Device interface */
105323184Scem	DEVMETHOD(device_identify,	amdsmn_identify),
106323184Scem	DEVMETHOD(device_probe,		amdsmn_probe),
107323184Scem	DEVMETHOD(device_attach,	amdsmn_attach),
108323184Scem	DEVMETHOD(device_detach,	amdsmn_detach),
109323184Scem	DEVMETHOD_END
110323184Scem};
111323184Scem
112323184Scemstatic driver_t amdsmn_driver = {
113323184Scem	"amdsmn",
114323184Scem	amdsmn_methods,
115323184Scem	sizeof(struct amdsmn_softc),
116323184Scem};
117323184Scem
118323184Scemstatic devclass_t amdsmn_devclass;
119323184ScemDRIVER_MODULE(amdsmn, hostb, amdsmn_driver, amdsmn_devclass, NULL, NULL);
120323184ScemMODULE_VERSION(amdsmn, 1);
121323184Scem
122329767Struckmanstatic bool
123343325Smavamdsmn_match(device_t parent, const struct pciid **pciid_out)
124323184Scem{
125343323Smav	uint16_t vendor, device;
126323184Scem	size_t i;
127323184Scem
128343323Smav	vendor = pci_get_vendor(parent);
129343323Smav	device = pci_get_device(parent);
130343323Smav
131343325Smav	for (i = 0; i < nitems(amdsmn_ids); i++) {
132343323Smav		if (vendor == amdsmn_ids[i].amdsmn_vendorid &&
133343325Smav		    device == amdsmn_ids[i].amdsmn_deviceid) {
134343325Smav			if (pciid_out != NULL)
135343325Smav				*pciid_out = &amdsmn_ids[i];
136329767Struckman			return (true);
137343325Smav		}
138343325Smav	}
139329767Struckman	return (false);
140329767Struckman}
141323184Scem
142329767Struckmanstatic void
143329767Struckmanamdsmn_identify(driver_t *driver, device_t parent)
144329767Struckman{
145329767Struckman	device_t child;
146329767Struckman
147329767Struckman	/* Make sure we're not being doubly invoked. */
148329767Struckman	if (device_find_child(parent, "amdsmn", -1) != NULL)
149323184Scem		return;
150343325Smav	if (!amdsmn_match(parent, NULL))
151329767Struckman		return;
152323184Scem
153323184Scem	child = device_add_child(parent, "amdsmn", -1);
154323184Scem	if (child == NULL)
155323184Scem		device_printf(parent, "add amdsmn child failed\n");
156323184Scem}
157323184Scem
158323184Scemstatic int
159323184Scemamdsmn_probe(device_t dev)
160323184Scem{
161323184Scem	uint32_t family;
162343325Smav	char buf[64];
163323184Scem
164323184Scem	if (resource_disabled("amdsmn", 0))
165323184Scem		return (ENXIO);
166343325Smav	if (!amdsmn_match(device_get_parent(dev), NULL))
167329767Struckman		return (ENXIO);
168323184Scem
169323184Scem	family = CPUID_TO_FAMILY(cpu_id);
170323184Scem
171323184Scem	switch (family) {
172343325Smav	case 0x15:
173323184Scem	case 0x17:
174323184Scem		break;
175323184Scem	default:
176323184Scem		return (ENXIO);
177323184Scem	}
178343325Smav	snprintf(buf, sizeof(buf), "AMD Family %xh System Management Network",
179343325Smav	    family);
180343325Smav	device_set_desc_copy(dev, buf);
181323184Scem
182323184Scem	return (BUS_PROBE_GENERIC);
183323184Scem}
184323184Scem
185323184Scemstatic int
186323184Scemamdsmn_attach(device_t dev)
187323184Scem{
188323184Scem	struct amdsmn_softc *sc = device_get_softc(dev);
189323184Scem
190343325Smav	if (!amdsmn_match(device_get_parent(dev), &sc->smn_pciid))
191343325Smav		return (ENXIO);
192343325Smav
193323184Scem	mtx_init(&sc->smn_lock, "SMN mtx", "SMN", MTX_DEF);
194323184Scem	return (0);
195323184Scem}
196323184Scem
197323184Scemint
198323184Scemamdsmn_detach(device_t dev)
199323184Scem{
200323184Scem	struct amdsmn_softc *sc = device_get_softc(dev);
201323184Scem
202323184Scem	mtx_destroy(&sc->smn_lock);
203323184Scem	return (0);
204323184Scem}
205323184Scem
206323184Scemint
207323184Scemamdsmn_read(device_t dev, uint32_t addr, uint32_t *value)
208323184Scem{
209323184Scem	struct amdsmn_softc *sc = device_get_softc(dev);
210323184Scem	device_t parent;
211323184Scem
212323184Scem	parent = device_get_parent(dev);
213323184Scem
214323184Scem	mtx_lock(&sc->smn_lock);
215343325Smav	pci_write_config(parent, sc->smn_pciid->amdsmn_addr_reg, addr, 4);
216343325Smav	*value = pci_read_config(parent, sc->smn_pciid->amdsmn_data_reg, 4);
217323184Scem	mtx_unlock(&sc->smn_lock);
218323184Scem
219323184Scem	return (0);
220323184Scem}
221323184Scem
222323184Scemint
223323184Scemamdsmn_write(device_t dev, uint32_t addr, uint32_t value)
224323184Scem{
225323184Scem	struct amdsmn_softc *sc = device_get_softc(dev);
226323184Scem	device_t parent;
227323184Scem
228323184Scem	parent = device_get_parent(dev);
229323184Scem
230323184Scem	mtx_lock(&sc->smn_lock);
231343325Smav	pci_write_config(parent, sc->smn_pciid->amdsmn_addr_reg, addr, 4);
232343325Smav	pci_write_config(parent, sc->smn_pciid->amdsmn_data_reg, value, 4);
233323184Scem	mtx_unlock(&sc->smn_lock);
234323184Scem
235323184Scem	return (0);
236323184Scem}
237