ipmi_pci.c revision 155517
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: head/sys/dev/ipmi/ipmi_pci.c 155517 2006-02-10 20:51:35Z ambrisko $");
29155517Sambrisko
30155517Sambrisko#include <sys/param.h>
31155517Sambrisko#include <sys/malloc.h>
32155517Sambrisko#include <sys/systm.h>
33155517Sambrisko#include <sys/kernel.h>
34155517Sambrisko#include <sys/module.h>
35155517Sambrisko#include <sys/selinfo.h>
36155517Sambrisko
37155517Sambrisko#include <sys/bus.h>
38155517Sambrisko#include <sys/conf.h>
39155517Sambrisko
40155517Sambrisko#include <machine/bus.h>
41155517Sambrisko#include <machine/resource.h>
42155517Sambrisko#include <sys/rman.h>
43155517Sambrisko
44155517Sambrisko#include <dev/pci/pcireg.h>
45155517Sambrisko#include <dev/pci/pcivar.h>
46155517Sambrisko
47155517Sambrisko#ifdef LOCAL_MODULE
48155517Sambrisko#include <ipmivars.h>
49155517Sambrisko#else
50155517Sambrisko#include <dev/ipmi/ipmivars.h>
51155517Sambrisko#endif
52155517Sambrisko
53155517Sambriskostatic int ipmi_pci_probe(device_t dev);
54155517Sambriskostatic int ipmi_pci_attach(device_t dev);
55155517Sambriskostatic int ipmi_pci_detach(device_t dev);
56155517Sambrisko
57155517Sambriskostatic device_method_t ipmi_methods[] = {
58155517Sambrisko	/* Device interface */
59155517Sambrisko	DEVMETHOD(device_probe,     ipmi_pci_probe),
60155517Sambrisko	DEVMETHOD(device_attach,    ipmi_pci_attach),
61155517Sambrisko	DEVMETHOD(device_detach,    ipmi_pci_detach),
62155517Sambrisko	{ 0, 0 }
63155517Sambrisko};
64155517Sambrisko
65155517Sambriskostruct ipmi_ident
66155517Sambrisko{
67155517Sambrisko	u_int16_t	vendor;
68155517Sambrisko	u_int16_t	device;
69155517Sambrisko	char		*desc;
70155517Sambrisko} ipmi_identifiers[] = {
71155517Sambrisko	{0x1028, 0x000d, "Dell PE2650 SMIC interface"},
72155517Sambrisko	{0, 0, 0}
73155517Sambrisko};
74155517Sambrisko
75155517Sambriskostatic int
76155517Sambriskoipmi_pci_probe(device_t dev) {
77155517Sambrisko	struct ipmi_ident *m;
78155517Sambrisko
79155517Sambrisko	if (ipmi_attached)
80155517Sambrisko		return ENXIO;
81155517Sambrisko
82155517Sambrisko	for (m = ipmi_identifiers; m->vendor != 0; m++) {
83155517Sambrisko		if ((m->vendor == pci_get_vendor(dev)) &&
84155517Sambrisko		    (m->device == pci_get_device(dev))) {
85155517Sambrisko			device_set_desc(dev, m->desc);
86155517Sambrisko			return (BUS_PROBE_DEFAULT);
87155517Sambrisko		}
88155517Sambrisko	}
89155517Sambrisko
90155517Sambrisko	return ENXIO;
91155517Sambrisko}
92155517Sambrisko
93155517Sambriskostatic int
94155517Sambriskoipmi_pci_attach(device_t dev) {
95155517Sambrisko	struct ipmi_softc *sc = device_get_softc(dev);
96155517Sambrisko	device_t parent, smbios_attach_dev = NULL;
97155517Sambrisko	devclass_t dc;
98155517Sambrisko	int status, flags;
99155517Sambrisko	int error;
100155517Sambrisko
101155517Sambrisko
102155517Sambrisko	/*
103155517Sambrisko	 * We need to attach to something that can address the BIOS/
104155517Sambrisko	 * SMBIOS memory range.  This is usually the isa bus however
105155517Sambrisko	 * during a static kernel boot the isa bus is not available
106155517Sambrisko	 * so we run up the tree to the nexus bus.  A module load
107155517Sambrisko	 * will use the isa bus attachment.  If neither work bail
108155517Sambrisko	 * since the SMBIOS defines stuff we need to know to attach to
109155517Sambrisko	 * this device.
110155517Sambrisko	 */
111155517Sambrisko	dc = devclass_find("isa");
112155517Sambrisko	if (dc != NULL) {
113155517Sambrisko		smbios_attach_dev = devclass_get_device(dc, 0);
114155517Sambrisko	}
115155517Sambrisko
116155517Sambrisko	if (smbios_attach_dev == NULL) {
117155517Sambrisko		smbios_attach_dev = dev;
118155517Sambrisko		for (;;) {
119155517Sambrisko			parent = device_get_parent(smbios_attach_dev);
120155517Sambrisko			if (parent == NULL)
121155517Sambrisko				break;
122155517Sambrisko			if (strcmp(device_get_name(smbios_attach_dev),
123155517Sambrisko			    "nexus") == 0)
124155517Sambrisko				break;
125155517Sambrisko			smbios_attach_dev = parent;
126155517Sambrisko		}
127155517Sambrisko	}
128155517Sambrisko
129155517Sambrisko	if (smbios_attach_dev == NULL) {
130155517Sambrisko		device_printf(dev, "Couldn't find isa/nexus device\n");
131155517Sambrisko		goto bad;
132155517Sambrisko	}
133155517Sambrisko	sc->ipmi_smbios_dev = ipmi_smbios_identify(NULL, smbios_attach_dev);
134155517Sambrisko	if (sc->ipmi_smbios_dev == NULL) {
135155517Sambrisko		device_printf(dev, "Couldn't find isa device\n");
136155517Sambrisko		goto bad;
137155517Sambrisko	}
138155517Sambrisko	error = ipmi_smbios_probe(sc->ipmi_smbios_dev);
139155517Sambrisko	if (error != 0) {
140155517Sambrisko		goto bad;
141155517Sambrisko	}
142155517Sambrisko	sc->ipmi_dev = dev;
143155517Sambrisko	error = ipmi_smbios_query(dev);
144155517Sambrisko	device_delete_child(dev, sc->ipmi_smbios_dev);
145155517Sambrisko	if (error != 0)
146155517Sambrisko		goto bad;
147155517Sambrisko
148155517Sambrisko	/* Now we know about the IPMI attachment info. */
149155517Sambrisko	if (sc->ipmi_bios_info.kcs_mode) {
150155517Sambrisko		if (sc->ipmi_bios_info.io_mode)
151155517Sambrisko			device_printf(dev, "KCS mode found at io 0x%llx "
152155517Sambrisko			    "alignment 0x%x on %s\n",
153155517Sambrisko			    (long long)sc->ipmi_bios_info.address,
154155517Sambrisko			    sc->ipmi_bios_info.offset,
155155517Sambrisko			    device_get_name(device_get_parent(sc->ipmi_dev)));
156155517Sambrisko		else
157155517Sambrisko			device_printf(dev, "KCS mode found at mem 0x%llx "
158155517Sambrisko			    "alignment 0x%x on %s\n",
159155517Sambrisko			    (long long)sc->ipmi_bios_info.address,
160155517Sambrisko			    sc->ipmi_bios_info.offset,
161155517Sambrisko			    device_get_name(device_get_parent(sc->ipmi_dev)));
162155517Sambrisko
163155517Sambrisko		sc->ipmi_kcs_status_reg   = sc->ipmi_bios_info.offset;
164155517Sambrisko		sc->ipmi_kcs_command_reg  = sc->ipmi_bios_info.offset;
165155517Sambrisko		sc->ipmi_kcs_data_out_reg = 0;
166155517Sambrisko		sc->ipmi_kcs_data_in_reg  = 0;
167155517Sambrisko
168155517Sambrisko		if (sc->ipmi_bios_info.io_mode) {
169155517Sambrisko			sc->ipmi_io_rid = PCIR_BAR(0);
170155517Sambrisko			sc->ipmi_io_res = bus_alloc_resource(dev,
171155517Sambrisko			    SYS_RES_IOPORT, &sc->ipmi_io_rid,
172155517Sambrisko			    sc->ipmi_bios_info.address,
173155517Sambrisko			    sc->ipmi_bios_info.address +
174155517Sambrisko				(sc->ipmi_bios_info.offset * 2),
175155517Sambrisko			    sc->ipmi_bios_info.offset * 2,
176155517Sambrisko			    RF_ACTIVE);
177155517Sambrisko		} else {
178155517Sambrisko			sc->ipmi_mem_rid = PCIR_BAR(0);
179155517Sambrisko			sc->ipmi_mem_res = bus_alloc_resource(dev,
180155517Sambrisko			    SYS_RES_MEMORY, &sc->ipmi_mem_rid,
181155517Sambrisko			    sc->ipmi_bios_info.address,
182155517Sambrisko			    sc->ipmi_bios_info.address +
183155517Sambrisko			        (sc->ipmi_bios_info.offset * 2),
184155517Sambrisko			    sc->ipmi_bios_info.offset * 2,
185155517Sambrisko			    RF_ACTIVE);
186155517Sambrisko		}
187155517Sambrisko
188155517Sambrisko		if (!sc->ipmi_io_res){
189155517Sambrisko			device_printf(dev, "couldn't configure pci io res\n");
190155517Sambrisko			goto bad;
191155517Sambrisko		}
192155517Sambrisko
193155517Sambrisko		status = INB(sc, sc->ipmi_kcs_status_reg);
194155517Sambrisko		if (status == 0xff) {
195155517Sambrisko			device_printf(dev, "couldn't find it\n");
196155517Sambrisko			goto bad;
197155517Sambrisko		}
198155517Sambrisko		if(status & KCS_STATUS_OBF){
199155517Sambrisko			ipmi_read(dev, NULL, 0);
200155517Sambrisko		}
201155517Sambrisko	} else if (sc->ipmi_bios_info.smic_mode) {
202155517Sambrisko		if (sc->ipmi_bios_info.io_mode)
203155517Sambrisko			device_printf(dev, "SMIC mode found at io 0x%llx "
204155517Sambrisko			    "alignment 0x%x on %s\n",
205155517Sambrisko			    (long long)sc->ipmi_bios_info.address,
206155517Sambrisko			    sc->ipmi_bios_info.offset,
207155517Sambrisko			    device_get_name(device_get_parent(sc->ipmi_dev)));
208155517Sambrisko		else
209155517Sambrisko			device_printf(dev, "SMIC mode found at mem 0x%llx "
210155517Sambrisko			    "alignment 0x%x on %s\n",
211155517Sambrisko			    (long long)sc->ipmi_bios_info.address,
212155517Sambrisko			    sc->ipmi_bios_info.offset,
213155517Sambrisko			    device_get_name(device_get_parent(sc->ipmi_dev)));
214155517Sambrisko
215155517Sambrisko		sc->ipmi_smic_data    = 0;
216155517Sambrisko		sc->ipmi_smic_ctl_sts = sc->ipmi_bios_info.offset;
217155517Sambrisko		sc->ipmi_smic_flags   = sc->ipmi_bios_info.offset * 2;
218155517Sambrisko
219155517Sambrisko		if (sc->ipmi_bios_info.io_mode) {
220155517Sambrisko			sc->ipmi_io_rid = PCIR_BAR(0);
221155517Sambrisko			sc->ipmi_io_res = bus_alloc_resource(dev,
222155517Sambrisko			    SYS_RES_IOPORT, &sc->ipmi_io_rid,
223155517Sambrisko			    sc->ipmi_bios_info.address,
224155517Sambrisko			    sc->ipmi_bios_info.address +
225155517Sambrisko				(sc->ipmi_bios_info.offset * 3),
226155517Sambrisko			    sc->ipmi_bios_info.offset * 3,
227155517Sambrisko			    RF_ACTIVE);
228155517Sambrisko		} else {
229155517Sambrisko			sc->ipmi_mem_rid = PCIR_BAR(0);
230155517Sambrisko			sc->ipmi_mem_res = bus_alloc_resource(dev,
231155517Sambrisko			    SYS_RES_MEMORY, &sc->ipmi_mem_rid,
232155517Sambrisko			    sc->ipmi_bios_info.address,
233155517Sambrisko			    sc->ipmi_bios_info.address +
234155517Sambrisko			        (sc->ipmi_bios_info.offset * 2),
235155517Sambrisko			    sc->ipmi_bios_info.offset * 2,
236155517Sambrisko			    RF_ACTIVE);
237155517Sambrisko		}
238155517Sambrisko
239155517Sambrisko		if (!sc->ipmi_io_res && !sc->ipmi_mem_res){
240155517Sambrisko			device_printf(dev, "couldn't configure pci res\n");
241155517Sambrisko			goto bad;
242155517Sambrisko		}
243155517Sambrisko
244155517Sambrisko		flags = INB(sc, sc->ipmi_smic_flags);
245155517Sambrisko		if (flags == 0xff) {
246155517Sambrisko			device_printf(dev, "couldn't find it\n");
247155517Sambrisko			goto bad;
248155517Sambrisko		}
249155517Sambrisko		if ((flags & SMIC_STATUS_SMS_ATN)
250155517Sambrisko		    && (flags & SMIC_STATUS_RX_RDY)){
251155517Sambrisko			ipmi_read(dev, NULL, 0);
252155517Sambrisko		}
253155517Sambrisko	} else {
254155517Sambrisko		device_printf(dev, "No IPMI interface found\n");
255155517Sambrisko		goto bad;
256155517Sambrisko	}
257155517Sambrisko	ipmi_attach(dev);
258155517Sambrisko
259155517Sambrisko	sc->ipmi_irq_rid = 0;
260155517Sambrisko        sc->ipmi_irq_res = bus_alloc_resource_any(sc->ipmi_dev, SYS_RES_IRQ,
261155517Sambrisko	    &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
262155517Sambrisko	if (sc->ipmi_irq_res == NULL) {
263155517Sambrisko                device_printf(sc->ipmi_dev, "can't allocate interrupt\n");
264155517Sambrisko        } else {
265155517Sambrisko		if (bus_setup_intr(sc->ipmi_dev, sc->ipmi_irq_res,
266155517Sambrisko				   INTR_TYPE_MISC, ipmi_intr,
267155517Sambrisko				   sc->ipmi_dev, &sc->ipmi_irq)) {
268155517Sambrisko			device_printf(sc->ipmi_dev,
269155517Sambrisko			    "can't set up interrupt\n");
270155517Sambrisko			return (EINVAL);
271155517Sambrisko		}
272155517Sambrisko	}
273155517Sambrisko
274155517Sambrisko	return 0;
275155517Sambriskobad:
276155517Sambrisko	return ENXIO;
277155517Sambrisko}
278155517Sambrisko
279155517Sambriskostatic int ipmi_pci_detach(device_t dev) {
280155517Sambrisko	struct ipmi_softc *sc;
281155517Sambrisko
282155517Sambrisko	sc = device_get_softc(dev);
283155517Sambrisko	ipmi_detach(dev);
284155517Sambrisko	if (sc->ipmi_ev_tag)
285155517Sambrisko		EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_ev_tag);
286155517Sambrisko
287155517Sambrisko	if (sc->ipmi_mem_res)
288155517Sambrisko		bus_release_resource(dev, SYS_RES_MEMORY, sc->ipmi_mem_rid,
289155517Sambrisko		    sc->ipmi_mem_res);
290155517Sambrisko	if (sc->ipmi_io_res)
291155517Sambrisko		bus_release_resource(dev, SYS_RES_IOPORT, sc->ipmi_io_rid,
292155517Sambrisko		    sc->ipmi_io_res);
293155517Sambrisko	if (sc->ipmi_irq)
294155517Sambrisko		bus_teardown_intr(sc->ipmi_dev, sc->ipmi_irq_res,
295155517Sambrisko		    sc->ipmi_irq);
296155517Sambrisko	if (sc->ipmi_irq_res)
297155517Sambrisko		bus_release_resource(sc->ipmi_dev, SYS_RES_IRQ,
298155517Sambrisko		    sc->ipmi_irq_rid, sc->ipmi_irq_res);
299155517Sambrisko
300155517Sambrisko	return 0;
301155517Sambrisko}
302155517Sambrisko
303155517Sambriskostatic driver_t ipmi_pci_driver = {
304155517Sambrisko        "ipmi",
305155517Sambrisko        ipmi_methods,
306155517Sambrisko        sizeof(struct ipmi_softc)
307155517Sambrisko};
308155517Sambrisko
309155517SambriskoDRIVER_MODULE(ipmi_foo, pci, ipmi_pci_driver, ipmi_devclass, 0, 0);
310