1155517Sambrisko/*-
2155517Sambrisko * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
3155517Sambrisko * All rights reserved.
4155517Sambrisko *
5155517Sambrisko * Redistribution and use in source and binary forms, with or without
6155517Sambrisko * modification, are permitted provided that the following conditions
7155517Sambrisko * are met:
8155517Sambrisko * 1. Redistributions of source code must retain the above copyright
9155517Sambrisko *    notice, this list of conditions and the following disclaimer.
10155517Sambrisko * 2. Redistributions in binary form must reproduce the above copyright
11155517Sambrisko *    notice, this list of conditions and the following disclaimer in the
12155517Sambrisko *    documentation and/or other materials provided with the distribution.
13155517Sambrisko *
14155517Sambrisko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15155517Sambrisko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16155517Sambrisko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17155517Sambrisko * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18155517Sambrisko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19155517Sambrisko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20155517Sambrisko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21155517Sambrisko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22155517Sambrisko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23155517Sambrisko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24155517Sambrisko * SUCH DAMAGE.
25155517Sambrisko */
26155517Sambrisko
27155517Sambrisko#include <sys/cdefs.h>
28155517Sambrisko__FBSDID("$FreeBSD$");
29155517Sambrisko
30155517Sambrisko#include <sys/param.h>
31155517Sambrisko#include <sys/systm.h>
32162562Sjhb#include <sys/bus.h>
33162562Sjhb#include <sys/condvar.h>
34162562Sjhb#include <sys/eventhandler.h>
35155517Sambrisko#include <sys/kernel.h>
36155517Sambrisko#include <sys/module.h>
37162562Sjhb#include <sys/rman.h>
38155517Sambrisko#include <sys/selinfo.h>
39155517Sambrisko
40155517Sambrisko#include <dev/pci/pcireg.h>
41155517Sambrisko#include <dev/pci/pcivar.h>
42155517Sambrisko
43155517Sambrisko#ifdef LOCAL_MODULE
44155517Sambrisko#include <ipmivars.h>
45155517Sambrisko#else
46155517Sambrisko#include <dev/ipmi/ipmivars.h>
47155517Sambrisko#endif
48155517Sambrisko
49155517Sambriskostatic int ipmi_pci_probe(device_t dev);
50155517Sambriskostatic int ipmi_pci_attach(device_t dev);
51155517Sambrisko
52162562Sjhbstatic struct ipmi_ident
53155517Sambrisko{
54155517Sambrisko	u_int16_t	vendor;
55155517Sambrisko	u_int16_t	device;
56155517Sambrisko	char		*desc;
57155517Sambrisko} ipmi_identifiers[] = {
58155517Sambrisko	{0x1028, 0x000d, "Dell PE2650 SMIC interface"},
59155517Sambrisko	{0, 0, 0}
60155517Sambrisko};
61155517Sambrisko
62162562Sjhbconst char *
63162562Sjhbipmi_pci_match(uint16_t vendor, uint16_t device)
64162562Sjhb{
65155517Sambrisko	struct ipmi_ident *m;
66155517Sambrisko
67162562Sjhb	for (m = ipmi_identifiers; m->vendor != 0; m++)
68162562Sjhb		if (m->vendor == vendor && m->device == device)
69162562Sjhb			return (m->desc);
70162562Sjhb	return (NULL);
71162562Sjhb}
72162562Sjhb
73162562Sjhbstatic int
74162562Sjhbipmi_pci_probe(device_t dev)
75162562Sjhb{
76162562Sjhb	const char *desc;
77162562Sjhb
78155517Sambrisko	if (ipmi_attached)
79162562Sjhb		return (ENXIO);
80155517Sambrisko
81162562Sjhb	desc = ipmi_pci_match(pci_get_vendor(dev), pci_get_device(dev));
82162562Sjhb	if (desc != NULL) {
83162562Sjhb		device_set_desc(dev, desc);
84162562Sjhb		return (BUS_PROBE_DEFAULT);
85155517Sambrisko	}
86155517Sambrisko
87162562Sjhb	return (ENXIO);
88155517Sambrisko}
89155517Sambrisko
90155517Sambriskostatic int
91162562Sjhbipmi_pci_attach(device_t dev)
92162562Sjhb{
93155517Sambrisko	struct ipmi_softc *sc = device_get_softc(dev);
94162562Sjhb	struct ipmi_get_info info;
95162562Sjhb	const char *mode;
96162562Sjhb	int error, type;
97155517Sambrisko
98162562Sjhb	/* Look for an IPMI entry in the SMBIOS table. */
99162562Sjhb	if (!ipmi_smbios_identify(&info))
100162562Sjhb		return (ENXIO);
101155517Sambrisko
102162562Sjhb	sc->ipmi_dev = dev;
103162562Sjhb
104162562Sjhb	switch (info.iface_type) {
105162562Sjhb	case KCS_MODE:
106162562Sjhb		mode = "KCS";
107162562Sjhb		break;
108162562Sjhb	case SMIC_MODE:
109162562Sjhb		mode = "SMIC";
110162562Sjhb		break;
111162562Sjhb	case BT_MODE:
112162562Sjhb		device_printf(dev, "BT mode is unsupported\n");
113162562Sjhb		return (ENXIO);
114162562Sjhb	default:
115162562Sjhb		device_printf(dev, "No IPMI interface found\n");
116162562Sjhb		return (ENXIO);
117155517Sambrisko	}
118155517Sambrisko
119162562Sjhb	device_printf(dev, "%s mode found at %s 0x%jx alignment 0x%x on %s\n",
120162562Sjhb	    mode, info.io_mode ? "io" : "mem",
121162562Sjhb	    (uintmax_t)info.address, info.offset,
122162562Sjhb	    device_get_name(device_get_parent(dev)));
123162562Sjhb	if (info.io_mode)
124162562Sjhb		type = SYS_RES_IOPORT;
125162562Sjhb	else
126162562Sjhb		type = SYS_RES_MEMORY;
127162562Sjhb
128162562Sjhb	sc->ipmi_io_rid = PCIR_BAR(0);
129162562Sjhb	sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type,
130162562Sjhb	    &sc->ipmi_io_rid, RF_ACTIVE);
131162562Sjhb	sc->ipmi_io_type = type;
132162562Sjhb	sc->ipmi_io_spacing = info.offset;
133162562Sjhb
134162562Sjhb	if (sc->ipmi_io_res[0] == NULL) {
135162562Sjhb		device_printf(dev, "couldn't configure pci io res\n");
136162562Sjhb		return (ENXIO);
137155517Sambrisko	}
138162562Sjhb
139162562Sjhb	sc->ipmi_irq_rid = 0;
140162562Sjhb	sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
141162562Sjhb	    &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
142162562Sjhb
143162562Sjhb	switch (info.iface_type) {
144162562Sjhb	case KCS_MODE:
145162562Sjhb		error = ipmi_kcs_attach(sc);
146162562Sjhb		if (error)
147162562Sjhb			goto bad;
148162562Sjhb		break;
149162562Sjhb	case SMIC_MODE:
150162562Sjhb		error = ipmi_smic_attach(sc);
151162562Sjhb		if (error)
152162562Sjhb			goto bad;
153162562Sjhb		break;
154155517Sambrisko	}
155162562Sjhb	error = ipmi_attach(dev);
156162562Sjhb	if (error)
157155517Sambrisko		goto bad;
158155517Sambrisko
159162562Sjhb	return (0);
160162562Sjhbbad:
161162562Sjhb	ipmi_release_resources(dev);
162162562Sjhb	return (error);
163162562Sjhb}
164155517Sambrisko
165162562Sjhbstatic device_method_t ipmi_methods[] = {
166162562Sjhb	/* Device interface */
167162562Sjhb	DEVMETHOD(device_probe,     ipmi_pci_probe),
168162562Sjhb	DEVMETHOD(device_attach,    ipmi_pci_attach),
169162562Sjhb	DEVMETHOD(device_detach,    ipmi_detach),
170162562Sjhb	{ 0, 0 }
171162562Sjhb};
172155517Sambrisko
173162562Sjhbstatic driver_t ipmi_pci_driver = {
174162562Sjhb	"ipmi",
175162562Sjhb	ipmi_methods,
176162562Sjhb	sizeof(struct ipmi_softc)
177162562Sjhb};
178155517Sambrisko
179162562SjhbDRIVER_MODULE(ipmi_pci, pci, ipmi_pci_driver, ipmi_devclass, 0, 0);
180155517Sambrisko
181162562Sjhb/* Native IPMI on PCI driver. */
182155517Sambrisko
183162562Sjhbstatic int
184162562Sjhbipmi2_pci_probe(device_t dev)
185162562Sjhb{
186155517Sambrisko
187162562Sjhb	if (pci_get_class(dev) == PCIC_SERIALBUS &&
188168157Sjhb	    pci_get_subclass(dev) == PCIS_SERIALBUS_IPMI) {
189162562Sjhb		device_set_desc(dev, "IPMI System Interface");
190162562Sjhb		return (BUS_PROBE_GENERIC);
191162562Sjhb	}
192155517Sambrisko
193162562Sjhb	return (ENXIO);
194162562Sjhb}
195155517Sambrisko
196162562Sjhbstatic int
197162562Sjhbipmi2_pci_attach(device_t dev)
198162562Sjhb{
199162562Sjhb	struct ipmi_softc *sc;
200162562Sjhb	int error, iface, type;
201162562Sjhb
202162562Sjhb	sc = device_get_softc(dev);
203162562Sjhb	sc->ipmi_dev = dev;
204162562Sjhb
205162562Sjhb	/* Interface is determined by progif. */
206162562Sjhb	switch (pci_get_progif(dev)) {
207168157Sjhb	case PCIP_SERIALBUS_IPMI_SMIC:
208162562Sjhb		iface = SMIC_MODE;
209162562Sjhb		break;
210168157Sjhb	case PCIP_SERIALBUS_IPMI_KCS:
211162562Sjhb		iface = KCS_MODE;
212162562Sjhb		break;
213168157Sjhb	case PCIP_SERIALBUS_IPMI_BT:
214162562Sjhb		iface = BT_MODE;
215162562Sjhb		device_printf(dev, "BT interface unsupported\n");
216162562Sjhb		return (ENXIO);
217162562Sjhb	default:
218162562Sjhb		device_printf(dev, "Unsupported interface: %d\n",
219162562Sjhb		    pci_get_progif(dev));
220162562Sjhb		return (ENXIO);
221155517Sambrisko	}
222155517Sambrisko
223168162Sjhb	/* Check the BAR to determine our resource type. */
224162562Sjhb	sc->ipmi_io_rid = PCIR_BAR(0);
225168162Sjhb	if (PCI_BAR_IO(pci_read_config(dev, PCIR_BAR(0), 4)))
226162562Sjhb		type = SYS_RES_IOPORT;
227162562Sjhb	else
228162562Sjhb		type = SYS_RES_MEMORY;
229162562Sjhb	sc->ipmi_io_type = type;
230162562Sjhb	sc->ipmi_io_spacing = 1;
231162562Sjhb	sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type,
232162562Sjhb	    &sc->ipmi_io_rid, RF_ACTIVE);
233162562Sjhb	if (sc->ipmi_io_res[0] == NULL) {
234162562Sjhb		device_printf(dev, "couldn't map ports/memory\n");
235162562Sjhb		return (ENXIO);
236162562Sjhb	}
237162562Sjhb
238155517Sambrisko	sc->ipmi_irq_rid = 0;
239162562Sjhb	sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
240155517Sambrisko	    &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
241155517Sambrisko
242162562Sjhb	switch (iface) {
243162562Sjhb	case KCS_MODE:
244162562Sjhb		device_printf(dev, "using KSC interface\n");
245155517Sambrisko
246162562Sjhb		/*
247162562Sjhb		 * We have to examine the resource directly to determine the
248162562Sjhb		 * alignment.
249162562Sjhb		 */
250162562Sjhb		if (!ipmi_kcs_probe_align(sc)) {
251162562Sjhb			device_printf(dev, "Unable to determine alignment\n");
252162562Sjhb			error = ENXIO;
253162562Sjhb			goto bad;
254162562Sjhb		}
255155517Sambrisko
256162562Sjhb		error = ipmi_kcs_attach(sc);
257162562Sjhb		if (error)
258162562Sjhb			goto bad;
259162562Sjhb		break;
260162562Sjhb	case SMIC_MODE:
261162562Sjhb		device_printf(dev, "using SMIC interface\n");
262155517Sambrisko
263162562Sjhb		error = ipmi_smic_attach(sc);
264162562Sjhb		if (error)
265162562Sjhb			goto bad;
266162562Sjhb		break;
267162562Sjhb	}
268162562Sjhb	error = ipmi_attach(dev);
269162562Sjhb	if (error)
270162562Sjhb		goto bad;
271155517Sambrisko
272162562Sjhb	return (0);
273162562Sjhbbad:
274162562Sjhb	ipmi_release_resources(dev);
275162562Sjhb	return (error);
276155517Sambrisko}
277155517Sambrisko
278162562Sjhbstatic device_method_t ipmi2_methods[] = {
279162562Sjhb	/* Device interface */
280162562Sjhb	DEVMETHOD(device_probe,     ipmi2_pci_probe),
281162562Sjhb	DEVMETHOD(device_attach,    ipmi2_pci_attach),
282162562Sjhb	DEVMETHOD(device_detach,    ipmi_detach),
283162562Sjhb	{ 0, 0 }
284155517Sambrisko};
285155517Sambrisko
286162562Sjhbstatic driver_t ipmi2_pci_driver = {
287162562Sjhb	"ipmi",
288162562Sjhb	ipmi2_methods,
289162562Sjhb	sizeof(struct ipmi_softc)
290162562Sjhb};
291162562Sjhb
292162562SjhbDRIVER_MODULE(ipmi2_pci, pci, ipmi2_pci_driver, ipmi_devclass, 0, 0);
293