xref: /freebsd/sys/dev/ipmi/ipmi_pci.c (revision d72a078647de20e31ab09295e671f1b8b4ad89b2)
137b1ce13SDoug Ambrisko /*-
237b1ce13SDoug Ambrisko  * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
337b1ce13SDoug Ambrisko  * All rights reserved.
437b1ce13SDoug Ambrisko  *
537b1ce13SDoug Ambrisko  * Redistribution and use in source and binary forms, with or without
637b1ce13SDoug Ambrisko  * modification, are permitted provided that the following conditions
737b1ce13SDoug Ambrisko  * are met:
837b1ce13SDoug Ambrisko  * 1. Redistributions of source code must retain the above copyright
937b1ce13SDoug Ambrisko  *    notice, this list of conditions and the following disclaimer.
1037b1ce13SDoug Ambrisko  * 2. Redistributions in binary form must reproduce the above copyright
1137b1ce13SDoug Ambrisko  *    notice, this list of conditions and the following disclaimer in the
1237b1ce13SDoug Ambrisko  *    documentation and/or other materials provided with the distribution.
1337b1ce13SDoug Ambrisko  *
1437b1ce13SDoug Ambrisko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1537b1ce13SDoug Ambrisko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1637b1ce13SDoug Ambrisko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1737b1ce13SDoug Ambrisko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1837b1ce13SDoug Ambrisko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1937b1ce13SDoug Ambrisko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2037b1ce13SDoug Ambrisko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2137b1ce13SDoug Ambrisko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2237b1ce13SDoug Ambrisko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2337b1ce13SDoug Ambrisko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2437b1ce13SDoug Ambrisko  * SUCH DAMAGE.
2537b1ce13SDoug Ambrisko  */
2637b1ce13SDoug Ambrisko 
2737b1ce13SDoug Ambrisko #include <sys/cdefs.h>
2837b1ce13SDoug Ambrisko __FBSDID("$FreeBSD$");
2937b1ce13SDoug Ambrisko 
3037b1ce13SDoug Ambrisko #include <sys/param.h>
3137b1ce13SDoug Ambrisko #include <sys/systm.h>
32d72a0786SJohn Baldwin #include <sys/bus.h>
33d72a0786SJohn Baldwin #include <sys/condvar.h>
34d72a0786SJohn Baldwin #include <sys/eventhandler.h>
3537b1ce13SDoug Ambrisko #include <sys/kernel.h>
3637b1ce13SDoug Ambrisko #include <sys/module.h>
3737b1ce13SDoug Ambrisko #include <sys/rman.h>
38d72a0786SJohn Baldwin #include <sys/selinfo.h>
3937b1ce13SDoug Ambrisko 
4037b1ce13SDoug Ambrisko #include <dev/pci/pcireg.h>
4137b1ce13SDoug Ambrisko #include <dev/pci/pcivar.h>
4237b1ce13SDoug Ambrisko 
4337b1ce13SDoug Ambrisko #ifdef LOCAL_MODULE
4437b1ce13SDoug Ambrisko #include <ipmivars.h>
4537b1ce13SDoug Ambrisko #else
4637b1ce13SDoug Ambrisko #include <dev/ipmi/ipmivars.h>
4737b1ce13SDoug Ambrisko #endif
4837b1ce13SDoug Ambrisko 
4937b1ce13SDoug Ambrisko static int ipmi_pci_probe(device_t dev);
5037b1ce13SDoug Ambrisko static int ipmi_pci_attach(device_t dev);
5137b1ce13SDoug Ambrisko 
52d72a0786SJohn Baldwin static struct ipmi_ident
5337b1ce13SDoug Ambrisko {
5437b1ce13SDoug Ambrisko 	u_int16_t	vendor;
5537b1ce13SDoug Ambrisko 	u_int16_t	device;
5637b1ce13SDoug Ambrisko 	char		*desc;
5737b1ce13SDoug Ambrisko } ipmi_identifiers[] = {
5837b1ce13SDoug Ambrisko 	{0x1028, 0x000d, "Dell PE2650 SMIC interface"},
5937b1ce13SDoug Ambrisko 	{0, 0, 0}
6037b1ce13SDoug Ambrisko };
6137b1ce13SDoug Ambrisko 
62d72a0786SJohn Baldwin const char *
63d72a0786SJohn Baldwin ipmi_pci_match(uint16_t vendor, uint16_t device)
64d72a0786SJohn Baldwin {
6537b1ce13SDoug Ambrisko 	struct ipmi_ident *m;
6637b1ce13SDoug Ambrisko 
67d72a0786SJohn Baldwin 	for (m = ipmi_identifiers; m->vendor != 0; m++)
68d72a0786SJohn Baldwin 		if (m->vendor == vendor && m->device == device)
69d72a0786SJohn Baldwin 			return (m->desc);
70d72a0786SJohn Baldwin 	return (NULL);
7137b1ce13SDoug Ambrisko }
7237b1ce13SDoug Ambrisko 
7337b1ce13SDoug Ambrisko static int
74d72a0786SJohn Baldwin ipmi_pci_probe(device_t dev)
75d72a0786SJohn Baldwin {
76d72a0786SJohn Baldwin 	const char *desc;
77d72a0786SJohn Baldwin 
78d72a0786SJohn Baldwin 	if (ipmi_attached)
79d72a0786SJohn Baldwin 		return (ENXIO);
80d72a0786SJohn Baldwin 
81d72a0786SJohn Baldwin 	desc = ipmi_pci_match(pci_get_vendor(dev), pci_get_device(dev));
82d72a0786SJohn Baldwin 	if (desc != NULL) {
83d72a0786SJohn Baldwin 		device_set_desc(dev, desc);
84d72a0786SJohn Baldwin 		return (BUS_PROBE_DEFAULT);
85d72a0786SJohn Baldwin 	}
86d72a0786SJohn Baldwin 
87d72a0786SJohn Baldwin 	return (ENXIO);
88d72a0786SJohn Baldwin }
89d72a0786SJohn Baldwin 
90d72a0786SJohn Baldwin static int
91d72a0786SJohn Baldwin ipmi_pci_attach(device_t dev)
92d72a0786SJohn Baldwin {
9337b1ce13SDoug Ambrisko 	struct ipmi_softc *sc = device_get_softc(dev);
94d72a0786SJohn Baldwin 	struct ipmi_get_info info;
95d72a0786SJohn Baldwin 	const char *mode;
96d72a0786SJohn Baldwin 	int error, type;
9737b1ce13SDoug Ambrisko 
98d72a0786SJohn Baldwin 	/* Look for an IPMI entry in the SMBIOS table. */
99d72a0786SJohn Baldwin 	if (!ipmi_smbios_identify(&info))
100d72a0786SJohn Baldwin 		return (ENXIO);
10137b1ce13SDoug Ambrisko 
10237b1ce13SDoug Ambrisko 	sc->ipmi_dev = dev;
10337b1ce13SDoug Ambrisko 
104d72a0786SJohn Baldwin 	switch (info.iface_type) {
105d72a0786SJohn Baldwin 	case KCS_MODE:
106d72a0786SJohn Baldwin 		mode = "KCS";
107d72a0786SJohn Baldwin 		break;
108d72a0786SJohn Baldwin 	case SMIC_MODE:
109d72a0786SJohn Baldwin 		mode = "SMIC";
110d72a0786SJohn Baldwin 		break;
111d72a0786SJohn Baldwin 	case BT_MODE:
112d72a0786SJohn Baldwin 		device_printf(dev, "BT mode is unsupported\n");
113d72a0786SJohn Baldwin 		return (ENXIO);
114d72a0786SJohn Baldwin 	default:
11537b1ce13SDoug Ambrisko 		device_printf(dev, "No IPMI interface found\n");
116d72a0786SJohn Baldwin 		return (ENXIO);
11737b1ce13SDoug Ambrisko 	}
118d72a0786SJohn Baldwin 
119d72a0786SJohn Baldwin 	device_printf(dev, "%s mode found at %s 0x%jx alignment 0x%x on %s\n",
120d72a0786SJohn Baldwin 	    mode, info.io_mode ? "io" : "mem",
121d72a0786SJohn Baldwin 	    (uintmax_t)info.address, info.offset,
122d72a0786SJohn Baldwin 	    device_get_name(device_get_parent(dev)));
123d72a0786SJohn Baldwin 	if (info.io_mode)
124d72a0786SJohn Baldwin 		type = SYS_RES_IOPORT;
125d72a0786SJohn Baldwin 	else
126d72a0786SJohn Baldwin 		type = SYS_RES_MEMORY;
127d72a0786SJohn Baldwin 
128d72a0786SJohn Baldwin 	sc->ipmi_io_rid = PCIR_BAR(0);
129d72a0786SJohn Baldwin 	sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type,
130d72a0786SJohn Baldwin 	    &sc->ipmi_io_rid, RF_ACTIVE);
131d72a0786SJohn Baldwin 	sc->ipmi_io_type = type;
132d72a0786SJohn Baldwin 	sc->ipmi_io_spacing = info.offset;
133d72a0786SJohn Baldwin 
134d72a0786SJohn Baldwin 	if (sc->ipmi_io_res[0] == NULL) {
135d72a0786SJohn Baldwin 		device_printf(dev, "couldn't configure pci io res\n");
136d72a0786SJohn Baldwin 		return (ENXIO);
137d72a0786SJohn Baldwin 	}
13837b1ce13SDoug Ambrisko 
13937b1ce13SDoug Ambrisko 	sc->ipmi_irq_rid = 0;
140d72a0786SJohn Baldwin 	sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
14137b1ce13SDoug Ambrisko 	    &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
14237b1ce13SDoug Ambrisko 
143d72a0786SJohn Baldwin 	switch (info.iface_type) {
144d72a0786SJohn Baldwin 	case KCS_MODE:
145d72a0786SJohn Baldwin 		error = ipmi_kcs_attach(sc);
146d72a0786SJohn Baldwin 		if (error)
147d72a0786SJohn Baldwin 			goto bad;
148d72a0786SJohn Baldwin 		break;
149d72a0786SJohn Baldwin 	case SMIC_MODE:
150d72a0786SJohn Baldwin 		error = ipmi_smic_attach(sc);
151d72a0786SJohn Baldwin 		if (error)
152d72a0786SJohn Baldwin 			goto bad;
153d72a0786SJohn Baldwin 		break;
154d72a0786SJohn Baldwin 	}
155d72a0786SJohn Baldwin 	error = ipmi_attach(dev);
156d72a0786SJohn Baldwin 	if (error)
157d72a0786SJohn Baldwin 		goto bad;
158d72a0786SJohn Baldwin 
159d72a0786SJohn Baldwin 	return (0);
16037b1ce13SDoug Ambrisko bad:
161d72a0786SJohn Baldwin 	ipmi_release_resources(dev);
162d72a0786SJohn Baldwin 	return (error);
16337b1ce13SDoug Ambrisko }
16437b1ce13SDoug Ambrisko 
165d72a0786SJohn Baldwin static device_method_t ipmi_methods[] = {
166d72a0786SJohn Baldwin 	/* Device interface */
167d72a0786SJohn Baldwin 	DEVMETHOD(device_probe,     ipmi_pci_probe),
168d72a0786SJohn Baldwin 	DEVMETHOD(device_attach,    ipmi_pci_attach),
169d72a0786SJohn Baldwin 	DEVMETHOD(device_detach,    ipmi_detach),
170d72a0786SJohn Baldwin 	{ 0, 0 }
171d72a0786SJohn Baldwin };
17237b1ce13SDoug Ambrisko 
17337b1ce13SDoug Ambrisko static driver_t ipmi_pci_driver = {
17437b1ce13SDoug Ambrisko 	"ipmi",
17537b1ce13SDoug Ambrisko 	ipmi_methods,
17637b1ce13SDoug Ambrisko 	sizeof(struct ipmi_softc)
17737b1ce13SDoug Ambrisko };
17837b1ce13SDoug Ambrisko 
179d72a0786SJohn Baldwin DRIVER_MODULE(ipmi_pci, pci, ipmi_pci_driver, ipmi_devclass, 0, 0);
180d72a0786SJohn Baldwin 
181d72a0786SJohn Baldwin /* Native IPMI on PCI driver. */
182d72a0786SJohn Baldwin 
183d72a0786SJohn Baldwin static int
184d72a0786SJohn Baldwin ipmi2_pci_probe(device_t dev)
185d72a0786SJohn Baldwin {
186d72a0786SJohn Baldwin 
187d72a0786SJohn Baldwin 	if (pci_get_class(dev) == PCIC_SERIALBUS &&
188d72a0786SJohn Baldwin 	    pci_get_subclass(dev) == 0x07) {
189d72a0786SJohn Baldwin 		device_set_desc(dev, "IPMI System Interface");
190d72a0786SJohn Baldwin 		return (BUS_PROBE_GENERIC);
191d72a0786SJohn Baldwin 	}
192d72a0786SJohn Baldwin 
193d72a0786SJohn Baldwin 	return (ENXIO);
194d72a0786SJohn Baldwin }
195d72a0786SJohn Baldwin 
196d72a0786SJohn Baldwin static int
197d72a0786SJohn Baldwin ipmi2_pci_attach(device_t dev)
198d72a0786SJohn Baldwin {
199d72a0786SJohn Baldwin 	struct ipmi_softc *sc;
200d72a0786SJohn Baldwin 	int error, iface, type;
201d72a0786SJohn Baldwin 
202d72a0786SJohn Baldwin 	sc = device_get_softc(dev);
203d72a0786SJohn Baldwin 	sc->ipmi_dev = dev;
204d72a0786SJohn Baldwin 
205d72a0786SJohn Baldwin 	/* Interface is determined by progif. */
206d72a0786SJohn Baldwin 	switch (pci_get_progif(dev)) {
207d72a0786SJohn Baldwin 	case 0:
208d72a0786SJohn Baldwin 		iface = SMIC_MODE;
209d72a0786SJohn Baldwin 		break;
210d72a0786SJohn Baldwin 	case 1:
211d72a0786SJohn Baldwin 		iface = KCS_MODE;
212d72a0786SJohn Baldwin 		break;
213d72a0786SJohn Baldwin 	case 2:
214d72a0786SJohn Baldwin 		iface = BT_MODE;
215d72a0786SJohn Baldwin 		device_printf(dev, "BT interface unsupported\n");
216d72a0786SJohn Baldwin 		return (ENXIO);
217d72a0786SJohn Baldwin 	default:
218d72a0786SJohn Baldwin 		device_printf(dev, "Unsupported interface: %d\n",
219d72a0786SJohn Baldwin 		    pci_get_progif(dev));
220d72a0786SJohn Baldwin 		return (ENXIO);
221d72a0786SJohn Baldwin 	}
222d72a0786SJohn Baldwin 
223d72a0786SJohn Baldwin 	/*
224d72a0786SJohn Baldwin 	 * Bottom bit of bar indicates resouce type.  There should be
225d72a0786SJohn Baldwin 	 * constants in pcireg.h for fields in a BAR.
226d72a0786SJohn Baldwin 	 */
227d72a0786SJohn Baldwin 	sc->ipmi_io_rid = PCIR_BAR(0);
228d72a0786SJohn Baldwin 	if (pci_read_config(dev, PCIR_BAR(0), 4) & 0x1)
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