xref: /freebsd/sys/dev/ipmi/ipmi_isa.c (revision ee8b757a949a9575c7355ea01f0475e0c526b9e5)
1d72a0786SJohn Baldwin /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3718cf2ccSPedro F. Giffuni  *
4d72a0786SJohn Baldwin  * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
5d72a0786SJohn Baldwin  * All rights reserved.
6d72a0786SJohn Baldwin  *
7d72a0786SJohn Baldwin  * Redistribution and use in source and binary forms, with or without
8d72a0786SJohn Baldwin  * modification, are permitted provided that the following conditions
9d72a0786SJohn Baldwin  * are met:
10d72a0786SJohn Baldwin  * 1. Redistributions of source code must retain the above copyright
11d72a0786SJohn Baldwin  *    notice, this list of conditions and the following disclaimer.
12d72a0786SJohn Baldwin  * 2. Redistributions in binary form must reproduce the above copyright
13d72a0786SJohn Baldwin  *    notice, this list of conditions and the following disclaimer in the
14d72a0786SJohn Baldwin  *    documentation and/or other materials provided with the distribution.
15d72a0786SJohn Baldwin  *
16d72a0786SJohn Baldwin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17d72a0786SJohn Baldwin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18d72a0786SJohn Baldwin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19d72a0786SJohn Baldwin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20d72a0786SJohn Baldwin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21d72a0786SJohn Baldwin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22d72a0786SJohn Baldwin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23d72a0786SJohn Baldwin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24d72a0786SJohn Baldwin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25d72a0786SJohn Baldwin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26d72a0786SJohn Baldwin  * SUCH DAMAGE.
27d72a0786SJohn Baldwin  */
28d72a0786SJohn Baldwin 
29d72a0786SJohn Baldwin #include <sys/cdefs.h>
30d72a0786SJohn Baldwin __FBSDID("$FreeBSD$");
31d72a0786SJohn Baldwin 
32d72a0786SJohn Baldwin #include <sys/param.h>
33d72a0786SJohn Baldwin #include <sys/systm.h>
34d72a0786SJohn Baldwin #include <sys/bus.h>
35d72a0786SJohn Baldwin #include <sys/condvar.h>
36d72a0786SJohn Baldwin #include <sys/eventhandler.h>
37d72a0786SJohn Baldwin #include <sys/kernel.h>
38d72a0786SJohn Baldwin #include <sys/module.h>
39d72a0786SJohn Baldwin #include <sys/rman.h>
40d72a0786SJohn Baldwin #include <sys/selinfo.h>
41*ee8b757aSYinlong Lu #include <sys/efi.h>
42d72a0786SJohn Baldwin 
43d72a0786SJohn Baldwin #include <machine/pci_cfgreg.h>
44d72a0786SJohn Baldwin #include <dev/pci/pcireg.h>
45d72a0786SJohn Baldwin 
46d72a0786SJohn Baldwin #include <isa/isavar.h>
47d72a0786SJohn Baldwin 
48d72a0786SJohn Baldwin #ifdef LOCAL_MODULE
49d72a0786SJohn Baldwin #include <ipmi.h>
50d72a0786SJohn Baldwin #include <ipmivars.h>
51d72a0786SJohn Baldwin #else
52d72a0786SJohn Baldwin #include <sys/ipmi.h>
53d72a0786SJohn Baldwin #include <dev/ipmi/ipmivars.h>
54d72a0786SJohn Baldwin #endif
55d72a0786SJohn Baldwin 
56d72a0786SJohn Baldwin static void
57d72a0786SJohn Baldwin ipmi_isa_identify(driver_t *driver, device_t parent)
58d72a0786SJohn Baldwin {
59d72a0786SJohn Baldwin 	struct ipmi_get_info info;
60d72a0786SJohn Baldwin 	uint32_t devid;
61d72a0786SJohn Baldwin 
629b256674SSean Bruno 	if (ipmi_smbios_identify(&info) && info.iface_type != SSIF_MODE &&
639b256674SSean Bruno 	    device_find_child(parent, "ipmi", -1) == NULL) {
64d72a0786SJohn Baldwin 		/*
65d72a0786SJohn Baldwin 		 * XXX: Hack alert.  On some broken systems, the IPMI
66d72a0786SJohn Baldwin 		 * interface is described via SMBIOS, but the actual
67d72a0786SJohn Baldwin 		 * IO resource is in a PCI device BAR, so we have to let
68d72a0786SJohn Baldwin 		 * the PCI device attach ipmi instead.  In that case don't
69d72a0786SJohn Baldwin 		 * create an isa ipmi device.  For now we hardcode the list
70d72a0786SJohn Baldwin 		 * of bus, device, function tuples.
71d72a0786SJohn Baldwin 		 */
72d72a0786SJohn Baldwin 		devid = pci_cfgregread(0, 4, 2, PCIR_DEVVENDOR, 4);
73d72a0786SJohn Baldwin 		if (devid != 0xffffffff &&
74d72a0786SJohn Baldwin 		    ipmi_pci_match(devid & 0xffff, devid >> 16) != NULL)
75d72a0786SJohn Baldwin 			return;
76d72a0786SJohn Baldwin 		BUS_ADD_CHILD(parent, 0, "ipmi", -1);
77d72a0786SJohn Baldwin 	}
78d72a0786SJohn Baldwin }
79d72a0786SJohn Baldwin 
80d72a0786SJohn Baldwin static int
81d72a0786SJohn Baldwin ipmi_isa_probe(device_t dev)
82d72a0786SJohn Baldwin {
83d72a0786SJohn Baldwin 
84ef3103bbSSean Bruno 	/*
85ef3103bbSSean Bruno 	 * Give other drivers precedence.  Unfortunately, this doesn't
86ef3103bbSSean Bruno 	 * work if we have an SMBIOS table that duplicates a PCI device
87ef3103bbSSean Bruno 	 * that's later on the bus than the PCI-ISA bridge.
88ef3103bbSSean Bruno 	 */
89ef3103bbSSean Bruno 	if (ipmi_attached)
90ef3103bbSSean Bruno 		return (ENXIO);
91ef3103bbSSean Bruno 
92d72a0786SJohn Baldwin 	/* Skip any PNP devices. */
93d72a0786SJohn Baldwin 	if (isa_get_logicalid(dev) != 0)
94d72a0786SJohn Baldwin 		return (ENXIO);
95d72a0786SJohn Baldwin 
96d72a0786SJohn Baldwin 	device_set_desc(dev, "IPMI System Interface");
97d72a0786SJohn Baldwin 	return (BUS_PROBE_DEFAULT);
98d72a0786SJohn Baldwin }
99d72a0786SJohn Baldwin 
100d72a0786SJohn Baldwin static int
10172d73315SDoug Ambrisko ipmi_hint_identify(device_t dev, struct ipmi_get_info *info)
10272d73315SDoug Ambrisko {
10372d73315SDoug Ambrisko 	const char *mode, *name;
10472d73315SDoug Ambrisko 	int i, unit, val;
10572d73315SDoug Ambrisko 
10672d73315SDoug Ambrisko 	/* We require at least a "mode" hint. */
10772d73315SDoug Ambrisko 	name = device_get_name(dev);
10872d73315SDoug Ambrisko 	unit = device_get_unit(dev);
10972d73315SDoug Ambrisko 	if (resource_string_value(name, unit, "mode", &mode) != 0)
11072d73315SDoug Ambrisko 		return (0);
11172d73315SDoug Ambrisko 
11272d73315SDoug Ambrisko 	/* Set the mode and default I/O resources for each mode. */
11372d73315SDoug Ambrisko 	bzero(info, sizeof(struct ipmi_get_info));
11472d73315SDoug Ambrisko 	if (strcasecmp(mode, "KCS") == 0) {
11572d73315SDoug Ambrisko 		info->iface_type = KCS_MODE;
11672d73315SDoug Ambrisko 		info->address = 0xca2;
11772d73315SDoug Ambrisko 		info->io_mode = 1;
11872d73315SDoug Ambrisko 		info->offset = 1;
11972d73315SDoug Ambrisko 	} else if (strcasecmp(mode, "SMIC") == 0) {
12072d73315SDoug Ambrisko 		info->iface_type = SMIC_MODE;
12172d73315SDoug Ambrisko 		info->address = 0xca9;
12272d73315SDoug Ambrisko 		info->io_mode = 1;
12372d73315SDoug Ambrisko 		info->offset = 1;
12472d73315SDoug Ambrisko 	} else if (strcasecmp(mode, "BT") == 0) {
12572d73315SDoug Ambrisko 		info->iface_type = BT_MODE;
12672d73315SDoug Ambrisko 		info->address = 0xe4;
12772d73315SDoug Ambrisko 		info->io_mode = 1;
12872d73315SDoug Ambrisko 		info->offset = 1;
12972d73315SDoug Ambrisko 	} else {
13072d73315SDoug Ambrisko 		device_printf(dev, "Invalid mode %s\n", mode);
13172d73315SDoug Ambrisko 		return (0);
13272d73315SDoug Ambrisko 	}
13372d73315SDoug Ambrisko 
13472d73315SDoug Ambrisko 	/*
13572d73315SDoug Ambrisko 	 * Kill any resources that isahint.c might have setup for us
13672d73315SDoug Ambrisko 	 * since it will conflict with how we do resources.
13772d73315SDoug Ambrisko 	 */
13872d73315SDoug Ambrisko 	for (i = 0; i < 2; i++) {
13972d73315SDoug Ambrisko 		bus_delete_resource(dev, SYS_RES_MEMORY, i);
14072d73315SDoug Ambrisko 		bus_delete_resource(dev, SYS_RES_IOPORT, i);
14172d73315SDoug Ambrisko 	}
14272d73315SDoug Ambrisko 
14372d73315SDoug Ambrisko 	/* Allow the I/O address to be overriden via hints. */
14472d73315SDoug Ambrisko 	if (resource_int_value(name, unit, "port", &val) == 0 && val != 0) {
14572d73315SDoug Ambrisko 		info->address = val;
14672d73315SDoug Ambrisko 		info->io_mode = 1;
14772d73315SDoug Ambrisko 	} else if (resource_int_value(name, unit, "maddr", &val) == 0 &&
14872d73315SDoug Ambrisko 	    val != 0) {
14972d73315SDoug Ambrisko 		info->address = val;
15072d73315SDoug Ambrisko 		info->io_mode = 0;
15172d73315SDoug Ambrisko 	}
15272d73315SDoug Ambrisko 
15372d73315SDoug Ambrisko 	/* Allow the spacing to be overriden. */
15472d73315SDoug Ambrisko 	if (resource_int_value(name, unit, "spacing", &val) == 0) {
15572d73315SDoug Ambrisko 		switch (val) {
15672d73315SDoug Ambrisko 		case 8:
15772d73315SDoug Ambrisko 			info->offset = 1;
15872d73315SDoug Ambrisko 			break;
15972d73315SDoug Ambrisko 		case 16:
16072d73315SDoug Ambrisko 			info->offset = 2;
16172d73315SDoug Ambrisko 			break;
16272d73315SDoug Ambrisko 		case 32:
16372d73315SDoug Ambrisko 			info->offset = 4;
16472d73315SDoug Ambrisko 			break;
16572d73315SDoug Ambrisko 		default:
16672d73315SDoug Ambrisko 			device_printf(dev, "Invalid register spacing\n");
16772d73315SDoug Ambrisko 			return (0);
16872d73315SDoug Ambrisko 		}
16972d73315SDoug Ambrisko 	}
17072d73315SDoug Ambrisko 	return (1);
17172d73315SDoug Ambrisko }
17272d73315SDoug Ambrisko 
17372d73315SDoug Ambrisko static int
174d72a0786SJohn Baldwin ipmi_isa_attach(device_t dev)
175d72a0786SJohn Baldwin {
176d72a0786SJohn Baldwin 	struct ipmi_softc *sc = device_get_softc(dev);
177d72a0786SJohn Baldwin 	struct ipmi_get_info info;
178d72a0786SJohn Baldwin 	const char *mode;
179d72a0786SJohn Baldwin 	int count, error, i, type;
180d72a0786SJohn Baldwin 
18172d73315SDoug Ambrisko 	/*
18272d73315SDoug Ambrisko 	 * Pull info out of the SMBIOS table.  If that doesn't work, use
18372d73315SDoug Ambrisko 	 * hints to enumerate a device.
18472d73315SDoug Ambrisko 	 */
18572d73315SDoug Ambrisko 	if (!ipmi_smbios_identify(&info) &&
18672d73315SDoug Ambrisko 	    !ipmi_hint_identify(dev, &info))
187d72a0786SJohn Baldwin 		return (ENXIO);
188d72a0786SJohn Baldwin 
189d72a0786SJohn Baldwin 	switch (info.iface_type) {
190d72a0786SJohn Baldwin 	case KCS_MODE:
191d72a0786SJohn Baldwin 		count = 2;
192d72a0786SJohn Baldwin 		mode = "KCS";
193d72a0786SJohn Baldwin 		break;
194d72a0786SJohn Baldwin 	case SMIC_MODE:
195d72a0786SJohn Baldwin 		count = 3;
196d72a0786SJohn Baldwin 		mode = "SMIC";
197d72a0786SJohn Baldwin 		break;
198d72a0786SJohn Baldwin 	case BT_MODE:
199d72a0786SJohn Baldwin 		device_printf(dev, "BT mode is unsupported\n");
200d72a0786SJohn Baldwin 		return (ENXIO);
201d72a0786SJohn Baldwin 	default:
202d72a0786SJohn Baldwin 		return (ENXIO);
203d72a0786SJohn Baldwin 	}
204d72a0786SJohn Baldwin 	error = 0;
205d72a0786SJohn Baldwin 	sc->ipmi_dev = dev;
206d72a0786SJohn Baldwin 
207d72a0786SJohn Baldwin 	device_printf(dev, "%s mode found at %s 0x%jx alignment 0x%x on %s\n",
208d72a0786SJohn Baldwin 	    mode, info.io_mode ? "io" : "mem",
209d72a0786SJohn Baldwin 	    (uintmax_t)info.address, info.offset,
210d72a0786SJohn Baldwin 	    device_get_name(device_get_parent(dev)));
211d72a0786SJohn Baldwin 	if (info.io_mode)
212d72a0786SJohn Baldwin 		type = SYS_RES_IOPORT;
213d72a0786SJohn Baldwin 	else
214d72a0786SJohn Baldwin 		type = SYS_RES_MEMORY;
215d72a0786SJohn Baldwin 
216d72a0786SJohn Baldwin 	sc->ipmi_io_type = type;
217d72a0786SJohn Baldwin 	sc->ipmi_io_spacing = info.offset;
218d72a0786SJohn Baldwin 	if (info.offset == 1) {
219d72a0786SJohn Baldwin 		sc->ipmi_io_rid = 0;
220d72a0786SJohn Baldwin 		sc->ipmi_io_res[0] = bus_alloc_resource(dev, type,
221d72a0786SJohn Baldwin 		    &sc->ipmi_io_rid, info.address, info.address + count - 1,
222d72a0786SJohn Baldwin 		    count, RF_ACTIVE);
223d72a0786SJohn Baldwin 		if (sc->ipmi_io_res[0] == NULL) {
224d72a0786SJohn Baldwin 			device_printf(dev, "couldn't configure I/O resource\n");
225d72a0786SJohn Baldwin 			return (ENXIO);
226d72a0786SJohn Baldwin 		}
227d72a0786SJohn Baldwin 	} else {
228d72a0786SJohn Baldwin 		for (i = 0; i < count; i++) {
229d72a0786SJohn Baldwin 			sc->ipmi_io_rid = i;
230d72a0786SJohn Baldwin 			sc->ipmi_io_res[i] = bus_alloc_resource(dev, type,
231d72a0786SJohn Baldwin 			    &sc->ipmi_io_rid, info.address + i * info.offset,
232d72a0786SJohn Baldwin 			    info.address + i * info.offset, 1, RF_ACTIVE);
233d72a0786SJohn Baldwin 			if (sc->ipmi_io_res[i] == NULL) {
234d72a0786SJohn Baldwin 				device_printf(dev,
235d72a0786SJohn Baldwin 				    "couldn't configure I/O resource\n");
236d72a0786SJohn Baldwin 				error = ENXIO;
237d72a0786SJohn Baldwin 				sc->ipmi_io_rid = 0;
238d72a0786SJohn Baldwin 				goto bad;
239d72a0786SJohn Baldwin 			}
240d72a0786SJohn Baldwin 		}
241d72a0786SJohn Baldwin 		sc->ipmi_io_rid = 0;
242d72a0786SJohn Baldwin 	}
243d72a0786SJohn Baldwin 
244d72a0786SJohn Baldwin 	if (info.irq != 0) {
245d72a0786SJohn Baldwin 		sc->ipmi_irq_rid = 0;
246d72a0786SJohn Baldwin 		sc->ipmi_irq_res = bus_alloc_resource(dev, SYS_RES_IRQ,
247d72a0786SJohn Baldwin 		    &sc->ipmi_irq_rid, info.irq, info.irq, 1,
248d72a0786SJohn Baldwin 		    RF_SHAREABLE | RF_ACTIVE);
249d72a0786SJohn Baldwin 	}
250d72a0786SJohn Baldwin 
251d72a0786SJohn Baldwin 	switch (info.iface_type) {
252d72a0786SJohn Baldwin 	case KCS_MODE:
253d72a0786SJohn Baldwin 		error = ipmi_kcs_attach(sc);
254d72a0786SJohn Baldwin 		if (error)
255d72a0786SJohn Baldwin 			goto bad;
256d72a0786SJohn Baldwin 		break;
257d72a0786SJohn Baldwin 	case SMIC_MODE:
258d72a0786SJohn Baldwin 		error = ipmi_smic_attach(sc);
259d72a0786SJohn Baldwin 		if (error)
260d72a0786SJohn Baldwin 			goto bad;
261d72a0786SJohn Baldwin 		break;
262d72a0786SJohn Baldwin 	}
263d72a0786SJohn Baldwin 
264d72a0786SJohn Baldwin 	error = ipmi_attach(dev);
265d72a0786SJohn Baldwin 	if (error)
266d72a0786SJohn Baldwin 		goto bad;
267d72a0786SJohn Baldwin 
268d72a0786SJohn Baldwin 	return (0);
269d72a0786SJohn Baldwin bad:
270d72a0786SJohn Baldwin 	ipmi_release_resources(dev);
271d72a0786SJohn Baldwin 	return (error);
272d72a0786SJohn Baldwin }
273d72a0786SJohn Baldwin 
274d72a0786SJohn Baldwin static device_method_t ipmi_methods[] = {
275d72a0786SJohn Baldwin 	/* Device interface */
276d72a0786SJohn Baldwin 	DEVMETHOD(device_identify,      ipmi_isa_identify),
277d72a0786SJohn Baldwin 	DEVMETHOD(device_probe,		ipmi_isa_probe),
278d72a0786SJohn Baldwin 	DEVMETHOD(device_attach,	ipmi_isa_attach),
279d72a0786SJohn Baldwin 	DEVMETHOD(device_detach,	ipmi_detach),
280d72a0786SJohn Baldwin 	{ 0, 0 }
281d72a0786SJohn Baldwin };
282d72a0786SJohn Baldwin 
283d72a0786SJohn Baldwin static driver_t ipmi_isa_driver = {
284d72a0786SJohn Baldwin 	"ipmi",
285d72a0786SJohn Baldwin 	ipmi_methods,
286d72a0786SJohn Baldwin 	sizeof(struct ipmi_softc),
287d72a0786SJohn Baldwin };
288d72a0786SJohn Baldwin 
289d72a0786SJohn Baldwin DRIVER_MODULE(ipmi_isa, isa, ipmi_isa_driver, ipmi_devclass, 0, 0);
290*ee8b757aSYinlong Lu #ifdef ARCH_MAY_USE_EFI
291*ee8b757aSYinlong Lu MODULE_DEPEND(ipmi_isa, efirt, 1, 1, 1);
292*ee8b757aSYinlong Lu #endif
293