xref: /freebsd/sys/dev/ipmi/ipmi_pci.c (revision ee8b757a949a9575c7355ea01f0475e0c526b9e5)
137b1ce13SDoug Ambrisko /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3718cf2ccSPedro F. Giffuni  *
437b1ce13SDoug Ambrisko  * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
537b1ce13SDoug Ambrisko  * All rights reserved.
637b1ce13SDoug Ambrisko  *
737b1ce13SDoug Ambrisko  * Redistribution and use in source and binary forms, with or without
837b1ce13SDoug Ambrisko  * modification, are permitted provided that the following conditions
937b1ce13SDoug Ambrisko  * are met:
1037b1ce13SDoug Ambrisko  * 1. Redistributions of source code must retain the above copyright
1137b1ce13SDoug Ambrisko  *    notice, this list of conditions and the following disclaimer.
1237b1ce13SDoug Ambrisko  * 2. Redistributions in binary form must reproduce the above copyright
1337b1ce13SDoug Ambrisko  *    notice, this list of conditions and the following disclaimer in the
1437b1ce13SDoug Ambrisko  *    documentation and/or other materials provided with the distribution.
1537b1ce13SDoug Ambrisko  *
1637b1ce13SDoug Ambrisko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1737b1ce13SDoug Ambrisko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1837b1ce13SDoug Ambrisko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1937b1ce13SDoug Ambrisko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2037b1ce13SDoug Ambrisko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2137b1ce13SDoug Ambrisko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2237b1ce13SDoug Ambrisko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2337b1ce13SDoug Ambrisko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2437b1ce13SDoug Ambrisko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2537b1ce13SDoug Ambrisko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2637b1ce13SDoug Ambrisko  * SUCH DAMAGE.
2737b1ce13SDoug Ambrisko  */
2837b1ce13SDoug Ambrisko 
2937b1ce13SDoug Ambrisko #include <sys/cdefs.h>
3037b1ce13SDoug Ambrisko __FBSDID("$FreeBSD$");
3137b1ce13SDoug Ambrisko 
3237b1ce13SDoug Ambrisko #include <sys/param.h>
3337b1ce13SDoug Ambrisko #include <sys/systm.h>
34d72a0786SJohn Baldwin #include <sys/bus.h>
35d72a0786SJohn Baldwin #include <sys/condvar.h>
36d72a0786SJohn Baldwin #include <sys/eventhandler.h>
3737b1ce13SDoug Ambrisko #include <sys/kernel.h>
3837b1ce13SDoug Ambrisko #include <sys/module.h>
3937b1ce13SDoug Ambrisko #include <sys/rman.h>
40d72a0786SJohn Baldwin #include <sys/selinfo.h>
41*ee8b757aSYinlong Lu #include <sys/efi.h>
4237b1ce13SDoug Ambrisko 
4337b1ce13SDoug Ambrisko #include <dev/pci/pcireg.h>
4437b1ce13SDoug Ambrisko #include <dev/pci/pcivar.h>
4537b1ce13SDoug Ambrisko 
4637b1ce13SDoug Ambrisko #ifdef LOCAL_MODULE
4737b1ce13SDoug Ambrisko #include <ipmivars.h>
4837b1ce13SDoug Ambrisko #else
4937b1ce13SDoug Ambrisko #include <dev/ipmi/ipmivars.h>
5037b1ce13SDoug Ambrisko #endif
5137b1ce13SDoug Ambrisko 
5237b1ce13SDoug Ambrisko static int ipmi_pci_probe(device_t dev);
5337b1ce13SDoug Ambrisko static int ipmi_pci_attach(device_t dev);
5437b1ce13SDoug Ambrisko 
55d72a0786SJohn Baldwin static struct ipmi_ident
5637b1ce13SDoug Ambrisko {
5737b1ce13SDoug Ambrisko 	u_int16_t	vendor;
5837b1ce13SDoug Ambrisko 	u_int16_t	device;
5937b1ce13SDoug Ambrisko 	char		*desc;
6037b1ce13SDoug Ambrisko } ipmi_identifiers[] = {
6137b1ce13SDoug Ambrisko 	{0x1028, 0x000d, "Dell PE2650 SMIC interface"},
6237b1ce13SDoug Ambrisko 	{0, 0, 0}
6337b1ce13SDoug Ambrisko };
6437b1ce13SDoug Ambrisko 
65d72a0786SJohn Baldwin const char *
66d72a0786SJohn Baldwin ipmi_pci_match(uint16_t vendor, uint16_t device)
67d72a0786SJohn Baldwin {
6837b1ce13SDoug Ambrisko 	struct ipmi_ident *m;
6937b1ce13SDoug Ambrisko 
70d72a0786SJohn Baldwin 	for (m = ipmi_identifiers; m->vendor != 0; m++)
71d72a0786SJohn Baldwin 		if (m->vendor == vendor && m->device == device)
72d72a0786SJohn Baldwin 			return (m->desc);
73d72a0786SJohn Baldwin 	return (NULL);
7437b1ce13SDoug Ambrisko }
7537b1ce13SDoug Ambrisko 
7637b1ce13SDoug Ambrisko static int
77d72a0786SJohn Baldwin ipmi_pci_probe(device_t dev)
78d72a0786SJohn Baldwin {
79d72a0786SJohn Baldwin 	const char *desc;
80d72a0786SJohn Baldwin 
81d72a0786SJohn Baldwin 	if (ipmi_attached)
82d72a0786SJohn Baldwin 		return (ENXIO);
83d72a0786SJohn Baldwin 
84d72a0786SJohn Baldwin 	desc = ipmi_pci_match(pci_get_vendor(dev), pci_get_device(dev));
85d72a0786SJohn Baldwin 	if (desc != NULL) {
86d72a0786SJohn Baldwin 		device_set_desc(dev, desc);
87d72a0786SJohn Baldwin 		return (BUS_PROBE_DEFAULT);
88d72a0786SJohn Baldwin 	}
89d72a0786SJohn Baldwin 
90d72a0786SJohn Baldwin 	return (ENXIO);
91d72a0786SJohn Baldwin }
92d72a0786SJohn Baldwin 
93d72a0786SJohn Baldwin static int
94d72a0786SJohn Baldwin ipmi_pci_attach(device_t dev)
95d72a0786SJohn Baldwin {
9637b1ce13SDoug Ambrisko 	struct ipmi_softc *sc = device_get_softc(dev);
97d72a0786SJohn Baldwin 	struct ipmi_get_info info;
98d72a0786SJohn Baldwin 	const char *mode;
99d72a0786SJohn Baldwin 	int error, type;
10037b1ce13SDoug Ambrisko 
101d72a0786SJohn Baldwin 	/* Look for an IPMI entry in the SMBIOS table. */
102d72a0786SJohn Baldwin 	if (!ipmi_smbios_identify(&info))
103d72a0786SJohn Baldwin 		return (ENXIO);
10437b1ce13SDoug Ambrisko 
10537b1ce13SDoug Ambrisko 	sc->ipmi_dev = dev;
10637b1ce13SDoug Ambrisko 
107d72a0786SJohn Baldwin 	switch (info.iface_type) {
108d72a0786SJohn Baldwin 	case KCS_MODE:
109d72a0786SJohn Baldwin 		mode = "KCS";
110d72a0786SJohn Baldwin 		break;
111d72a0786SJohn Baldwin 	case SMIC_MODE:
112d72a0786SJohn Baldwin 		mode = "SMIC";
113d72a0786SJohn Baldwin 		break;
114d72a0786SJohn Baldwin 	case BT_MODE:
115d72a0786SJohn Baldwin 		device_printf(dev, "BT mode is unsupported\n");
116d72a0786SJohn Baldwin 		return (ENXIO);
117d72a0786SJohn Baldwin 	default:
11837b1ce13SDoug Ambrisko 		device_printf(dev, "No IPMI interface found\n");
119d72a0786SJohn Baldwin 		return (ENXIO);
12037b1ce13SDoug Ambrisko 	}
121d72a0786SJohn Baldwin 
122d72a0786SJohn Baldwin 	device_printf(dev, "%s mode found at %s 0x%jx alignment 0x%x on %s\n",
123d72a0786SJohn Baldwin 	    mode, info.io_mode ? "io" : "mem",
124d72a0786SJohn Baldwin 	    (uintmax_t)info.address, info.offset,
125d72a0786SJohn Baldwin 	    device_get_name(device_get_parent(dev)));
126d72a0786SJohn Baldwin 	if (info.io_mode)
127d72a0786SJohn Baldwin 		type = SYS_RES_IOPORT;
128d72a0786SJohn Baldwin 	else
129d72a0786SJohn Baldwin 		type = SYS_RES_MEMORY;
130d72a0786SJohn Baldwin 
131d72a0786SJohn Baldwin 	sc->ipmi_io_rid = PCIR_BAR(0);
132d72a0786SJohn Baldwin 	sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type,
133d72a0786SJohn Baldwin 	    &sc->ipmi_io_rid, RF_ACTIVE);
134d72a0786SJohn Baldwin 	sc->ipmi_io_type = type;
135d72a0786SJohn Baldwin 	sc->ipmi_io_spacing = info.offset;
136d72a0786SJohn Baldwin 
137d72a0786SJohn Baldwin 	if (sc->ipmi_io_res[0] == NULL) {
138d72a0786SJohn Baldwin 		device_printf(dev, "couldn't configure pci io res\n");
139d72a0786SJohn Baldwin 		return (ENXIO);
140d72a0786SJohn Baldwin 	}
14137b1ce13SDoug Ambrisko 
14237b1ce13SDoug Ambrisko 	sc->ipmi_irq_rid = 0;
143d72a0786SJohn Baldwin 	sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
14437b1ce13SDoug Ambrisko 	    &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
14537b1ce13SDoug Ambrisko 
146d72a0786SJohn Baldwin 	switch (info.iface_type) {
147d72a0786SJohn Baldwin 	case KCS_MODE:
148d72a0786SJohn Baldwin 		error = ipmi_kcs_attach(sc);
149d72a0786SJohn Baldwin 		if (error)
150d72a0786SJohn Baldwin 			goto bad;
151d72a0786SJohn Baldwin 		break;
152d72a0786SJohn Baldwin 	case SMIC_MODE:
153d72a0786SJohn Baldwin 		error = ipmi_smic_attach(sc);
154d72a0786SJohn Baldwin 		if (error)
155d72a0786SJohn Baldwin 			goto bad;
156d72a0786SJohn Baldwin 		break;
157d72a0786SJohn Baldwin 	}
158d72a0786SJohn Baldwin 	error = ipmi_attach(dev);
159d72a0786SJohn Baldwin 	if (error)
160d72a0786SJohn Baldwin 		goto bad;
161d72a0786SJohn Baldwin 
162d72a0786SJohn Baldwin 	return (0);
16337b1ce13SDoug Ambrisko bad:
164d72a0786SJohn Baldwin 	ipmi_release_resources(dev);
165d72a0786SJohn Baldwin 	return (error);
16637b1ce13SDoug Ambrisko }
16737b1ce13SDoug Ambrisko 
168d72a0786SJohn Baldwin static device_method_t ipmi_methods[] = {
169d72a0786SJohn Baldwin 	/* Device interface */
170d72a0786SJohn Baldwin 	DEVMETHOD(device_probe,     ipmi_pci_probe),
171d72a0786SJohn Baldwin 	DEVMETHOD(device_attach,    ipmi_pci_attach),
172d72a0786SJohn Baldwin 	DEVMETHOD(device_detach,    ipmi_detach),
173d72a0786SJohn Baldwin 	{ 0, 0 }
174d72a0786SJohn Baldwin };
17537b1ce13SDoug Ambrisko 
17637b1ce13SDoug Ambrisko static driver_t ipmi_pci_driver = {
17737b1ce13SDoug Ambrisko 	"ipmi",
17837b1ce13SDoug Ambrisko 	ipmi_methods,
17937b1ce13SDoug Ambrisko 	sizeof(struct ipmi_softc)
18037b1ce13SDoug Ambrisko };
18137b1ce13SDoug Ambrisko 
182d72a0786SJohn Baldwin DRIVER_MODULE(ipmi_pci, pci, ipmi_pci_driver, ipmi_devclass, 0, 0);
183d72a0786SJohn Baldwin 
184d72a0786SJohn Baldwin /* Native IPMI on PCI driver. */
185d72a0786SJohn Baldwin 
186d72a0786SJohn Baldwin static int
187d72a0786SJohn Baldwin ipmi2_pci_probe(device_t dev)
188d72a0786SJohn Baldwin {
189d72a0786SJohn Baldwin 
190d72a0786SJohn Baldwin 	if (pci_get_class(dev) == PCIC_SERIALBUS &&
191657d9f9fSJohn Baldwin 	    pci_get_subclass(dev) == PCIS_SERIALBUS_IPMI) {
192d72a0786SJohn Baldwin 		device_set_desc(dev, "IPMI System Interface");
193d72a0786SJohn Baldwin 		return (BUS_PROBE_GENERIC);
194d72a0786SJohn Baldwin 	}
195d72a0786SJohn Baldwin 
196d72a0786SJohn Baldwin 	return (ENXIO);
197d72a0786SJohn Baldwin }
198d72a0786SJohn Baldwin 
199d72a0786SJohn Baldwin static int
200d72a0786SJohn Baldwin ipmi2_pci_attach(device_t dev)
201d72a0786SJohn Baldwin {
202d72a0786SJohn Baldwin 	struct ipmi_softc *sc;
203d72a0786SJohn Baldwin 	int error, iface, type;
204d72a0786SJohn Baldwin 
205d72a0786SJohn Baldwin 	sc = device_get_softc(dev);
206d72a0786SJohn Baldwin 	sc->ipmi_dev = dev;
207d72a0786SJohn Baldwin 
208d72a0786SJohn Baldwin 	/* Interface is determined by progif. */
209d72a0786SJohn Baldwin 	switch (pci_get_progif(dev)) {
210657d9f9fSJohn Baldwin 	case PCIP_SERIALBUS_IPMI_SMIC:
211d72a0786SJohn Baldwin 		iface = SMIC_MODE;
212d72a0786SJohn Baldwin 		break;
213657d9f9fSJohn Baldwin 	case PCIP_SERIALBUS_IPMI_KCS:
214d72a0786SJohn Baldwin 		iface = KCS_MODE;
215d72a0786SJohn Baldwin 		break;
216657d9f9fSJohn Baldwin 	case PCIP_SERIALBUS_IPMI_BT:
217d72a0786SJohn Baldwin 		iface = BT_MODE;
218d72a0786SJohn Baldwin 		device_printf(dev, "BT interface unsupported\n");
219d72a0786SJohn Baldwin 		return (ENXIO);
220d72a0786SJohn Baldwin 	default:
221d72a0786SJohn Baldwin 		device_printf(dev, "Unsupported interface: %d\n",
222d72a0786SJohn Baldwin 		    pci_get_progif(dev));
223d72a0786SJohn Baldwin 		return (ENXIO);
224d72a0786SJohn Baldwin 	}
225d72a0786SJohn Baldwin 
2264dc5078fSJohn Baldwin 	/* Check the BAR to determine our resource type. */
227d72a0786SJohn Baldwin 	sc->ipmi_io_rid = PCIR_BAR(0);
2284dc5078fSJohn Baldwin 	if (PCI_BAR_IO(pci_read_config(dev, PCIR_BAR(0), 4)))
229d72a0786SJohn Baldwin 		type = SYS_RES_IOPORT;
230d72a0786SJohn Baldwin 	else
231d72a0786SJohn Baldwin 		type = SYS_RES_MEMORY;
232d72a0786SJohn Baldwin 	sc->ipmi_io_type = type;
233d72a0786SJohn Baldwin 	sc->ipmi_io_spacing = 1;
234d72a0786SJohn Baldwin 	sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type,
235d72a0786SJohn Baldwin 	    &sc->ipmi_io_rid, RF_ACTIVE);
236d72a0786SJohn Baldwin 	if (sc->ipmi_io_res[0] == NULL) {
237d72a0786SJohn Baldwin 		device_printf(dev, "couldn't map ports/memory\n");
238d72a0786SJohn Baldwin 		return (ENXIO);
239d72a0786SJohn Baldwin 	}
240d72a0786SJohn Baldwin 
241d72a0786SJohn Baldwin 	sc->ipmi_irq_rid = 0;
242d72a0786SJohn Baldwin 	sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
243d72a0786SJohn Baldwin 	    &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
244d72a0786SJohn Baldwin 
245d72a0786SJohn Baldwin 	switch (iface) {
246d72a0786SJohn Baldwin 	case KCS_MODE:
247d72a0786SJohn Baldwin 		device_printf(dev, "using KSC interface\n");
248d72a0786SJohn Baldwin 
249d72a0786SJohn Baldwin 		/*
250d72a0786SJohn Baldwin 		 * We have to examine the resource directly to determine the
251d72a0786SJohn Baldwin 		 * alignment.
252d72a0786SJohn Baldwin 		 */
253d72a0786SJohn Baldwin 		if (!ipmi_kcs_probe_align(sc)) {
254d72a0786SJohn Baldwin 			device_printf(dev, "Unable to determine alignment\n");
255d72a0786SJohn Baldwin 			error = ENXIO;
256d72a0786SJohn Baldwin 			goto bad;
257d72a0786SJohn Baldwin 		}
258d72a0786SJohn Baldwin 
259d72a0786SJohn Baldwin 		error = ipmi_kcs_attach(sc);
260d72a0786SJohn Baldwin 		if (error)
261d72a0786SJohn Baldwin 			goto bad;
262d72a0786SJohn Baldwin 		break;
263d72a0786SJohn Baldwin 	case SMIC_MODE:
264d72a0786SJohn Baldwin 		device_printf(dev, "using SMIC interface\n");
265d72a0786SJohn Baldwin 
266d72a0786SJohn Baldwin 		error = ipmi_smic_attach(sc);
267d72a0786SJohn Baldwin 		if (error)
268d72a0786SJohn Baldwin 			goto bad;
269d72a0786SJohn Baldwin 		break;
270d72a0786SJohn Baldwin 	}
271d72a0786SJohn Baldwin 	error = ipmi_attach(dev);
272d72a0786SJohn Baldwin 	if (error)
273d72a0786SJohn Baldwin 		goto bad;
274d72a0786SJohn Baldwin 
275d72a0786SJohn Baldwin 	return (0);
276d72a0786SJohn Baldwin bad:
277d72a0786SJohn Baldwin 	ipmi_release_resources(dev);
278d72a0786SJohn Baldwin 	return (error);
279d72a0786SJohn Baldwin }
280d72a0786SJohn Baldwin 
281d72a0786SJohn Baldwin static device_method_t ipmi2_methods[] = {
282d72a0786SJohn Baldwin 	/* Device interface */
283d72a0786SJohn Baldwin 	DEVMETHOD(device_probe,     ipmi2_pci_probe),
284d72a0786SJohn Baldwin 	DEVMETHOD(device_attach,    ipmi2_pci_attach),
285d72a0786SJohn Baldwin 	DEVMETHOD(device_detach,    ipmi_detach),
286d72a0786SJohn Baldwin 	{ 0, 0 }
287d72a0786SJohn Baldwin };
288d72a0786SJohn Baldwin 
289d72a0786SJohn Baldwin static driver_t ipmi2_pci_driver = {
290d72a0786SJohn Baldwin 	"ipmi",
291d72a0786SJohn Baldwin 	ipmi2_methods,
292d72a0786SJohn Baldwin 	sizeof(struct ipmi_softc)
293d72a0786SJohn Baldwin };
294d72a0786SJohn Baldwin 
295d72a0786SJohn Baldwin DRIVER_MODULE(ipmi2_pci, pci, ipmi2_pci_driver, ipmi_devclass, 0, 0);
296*ee8b757aSYinlong Lu #ifdef ARCH_MAY_USE_EFI
297*ee8b757aSYinlong Lu MODULE_DEPEND(ipmi2_pci, efirt, 1, 1, 1);
298*ee8b757aSYinlong Lu #endif
299