xref: /freebsd/sys/dev/ipmi/ipmi_pci.c (revision 4f29da19bd44f0e99f021510460a81bf754c21d2)
1 /*-
2  * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/param.h>
31 #include <sys/malloc.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/module.h>
35 #include <sys/selinfo.h>
36 
37 #include <sys/bus.h>
38 #include <sys/conf.h>
39 
40 #include <machine/bus.h>
41 #include <machine/resource.h>
42 #include <sys/rman.h>
43 
44 #include <dev/pci/pcireg.h>
45 #include <dev/pci/pcivar.h>
46 
47 #ifdef LOCAL_MODULE
48 #include <ipmivars.h>
49 #else
50 #include <dev/ipmi/ipmivars.h>
51 #endif
52 
53 static int ipmi_pci_probe(device_t dev);
54 static int ipmi_pci_attach(device_t dev);
55 static int ipmi_pci_detach(device_t dev);
56 
57 static device_method_t ipmi_methods[] = {
58 	/* Device interface */
59 	DEVMETHOD(device_probe,     ipmi_pci_probe),
60 	DEVMETHOD(device_attach,    ipmi_pci_attach),
61 	DEVMETHOD(device_detach,    ipmi_pci_detach),
62 	{ 0, 0 }
63 };
64 
65 struct ipmi_ident
66 {
67 	u_int16_t	vendor;
68 	u_int16_t	device;
69 	char		*desc;
70 } ipmi_identifiers[] = {
71 	{0x1028, 0x000d, "Dell PE2650 SMIC interface"},
72 	{0, 0, 0}
73 };
74 
75 static int
76 ipmi_pci_probe(device_t dev) {
77 	struct ipmi_ident *m;
78 
79 	if (ipmi_attached)
80 		return ENXIO;
81 
82 	for (m = ipmi_identifiers; m->vendor != 0; m++) {
83 		if ((m->vendor == pci_get_vendor(dev)) &&
84 		    (m->device == pci_get_device(dev))) {
85 			device_set_desc(dev, m->desc);
86 			return (BUS_PROBE_DEFAULT);
87 		}
88 	}
89 
90 	return ENXIO;
91 }
92 
93 static int
94 ipmi_pci_attach(device_t dev) {
95 	struct ipmi_softc *sc = device_get_softc(dev);
96 	device_t parent, smbios_attach_dev = NULL;
97 	devclass_t dc;
98 	int status, flags;
99 	int error;
100 
101 
102 	/*
103 	 * We need to attach to something that can address the BIOS/
104 	 * SMBIOS memory range.  This is usually the isa bus however
105 	 * during a static kernel boot the isa bus is not available
106 	 * so we run up the tree to the nexus bus.  A module load
107 	 * will use the isa bus attachment.  If neither work bail
108 	 * since the SMBIOS defines stuff we need to know to attach to
109 	 * this device.
110 	 */
111 	dc = devclass_find("isa");
112 	if (dc != NULL) {
113 		smbios_attach_dev = devclass_get_device(dc, 0);
114 	}
115 
116 	if (smbios_attach_dev == NULL) {
117 		smbios_attach_dev = dev;
118 		for (;;) {
119 			parent = device_get_parent(smbios_attach_dev);
120 			if (parent == NULL)
121 				break;
122 			if (strcmp(device_get_name(smbios_attach_dev),
123 			    "nexus") == 0)
124 				break;
125 			smbios_attach_dev = parent;
126 		}
127 	}
128 
129 	if (smbios_attach_dev == NULL) {
130 		device_printf(dev, "Couldn't find isa/nexus device\n");
131 		goto bad;
132 	}
133 	sc->ipmi_smbios_dev = ipmi_smbios_identify(NULL, smbios_attach_dev);
134 	if (sc->ipmi_smbios_dev == NULL) {
135 		device_printf(dev, "Couldn't find isa device\n");
136 		goto bad;
137 	}
138 	error = ipmi_smbios_probe(sc->ipmi_smbios_dev);
139 	if (error != 0) {
140 		goto bad;
141 	}
142 	sc->ipmi_dev = dev;
143 	error = ipmi_smbios_query(dev);
144 	device_delete_child(dev, sc->ipmi_smbios_dev);
145 	if (error != 0)
146 		goto bad;
147 
148 	/* Now we know about the IPMI attachment info. */
149 	if (sc->ipmi_bios_info.kcs_mode) {
150 		if (sc->ipmi_bios_info.io_mode)
151 			device_printf(dev, "KCS mode found at io 0x%llx "
152 			    "alignment 0x%x on %s\n",
153 			    (long long)sc->ipmi_bios_info.address,
154 			    sc->ipmi_bios_info.offset,
155 			    device_get_name(device_get_parent(sc->ipmi_dev)));
156 		else
157 			device_printf(dev, "KCS mode found at mem 0x%llx "
158 			    "alignment 0x%x on %s\n",
159 			    (long long)sc->ipmi_bios_info.address,
160 			    sc->ipmi_bios_info.offset,
161 			    device_get_name(device_get_parent(sc->ipmi_dev)));
162 
163 		sc->ipmi_kcs_status_reg   = sc->ipmi_bios_info.offset;
164 		sc->ipmi_kcs_command_reg  = sc->ipmi_bios_info.offset;
165 		sc->ipmi_kcs_data_out_reg = 0;
166 		sc->ipmi_kcs_data_in_reg  = 0;
167 
168 		if (sc->ipmi_bios_info.io_mode) {
169 			sc->ipmi_io_rid = PCIR_BAR(0);
170 			sc->ipmi_io_res = bus_alloc_resource(dev,
171 			    SYS_RES_IOPORT, &sc->ipmi_io_rid,
172 			    sc->ipmi_bios_info.address,
173 			    sc->ipmi_bios_info.address +
174 				(sc->ipmi_bios_info.offset * 2),
175 			    sc->ipmi_bios_info.offset * 2,
176 			    RF_ACTIVE);
177 		} else {
178 			sc->ipmi_mem_rid = PCIR_BAR(0);
179 			sc->ipmi_mem_res = bus_alloc_resource(dev,
180 			    SYS_RES_MEMORY, &sc->ipmi_mem_rid,
181 			    sc->ipmi_bios_info.address,
182 			    sc->ipmi_bios_info.address +
183 			        (sc->ipmi_bios_info.offset * 2),
184 			    sc->ipmi_bios_info.offset * 2,
185 			    RF_ACTIVE);
186 		}
187 
188 		if (!sc->ipmi_io_res){
189 			device_printf(dev, "couldn't configure pci io res\n");
190 			goto bad;
191 		}
192 
193 		status = INB(sc, sc->ipmi_kcs_status_reg);
194 		if (status == 0xff) {
195 			device_printf(dev, "couldn't find it\n");
196 			goto bad;
197 		}
198 		if(status & KCS_STATUS_OBF){
199 			ipmi_read(dev, NULL, 0);
200 		}
201 	} else if (sc->ipmi_bios_info.smic_mode) {
202 		if (sc->ipmi_bios_info.io_mode)
203 			device_printf(dev, "SMIC mode found at io 0x%llx "
204 			    "alignment 0x%x on %s\n",
205 			    (long long)sc->ipmi_bios_info.address,
206 			    sc->ipmi_bios_info.offset,
207 			    device_get_name(device_get_parent(sc->ipmi_dev)));
208 		else
209 			device_printf(dev, "SMIC mode found at mem 0x%llx "
210 			    "alignment 0x%x on %s\n",
211 			    (long long)sc->ipmi_bios_info.address,
212 			    sc->ipmi_bios_info.offset,
213 			    device_get_name(device_get_parent(sc->ipmi_dev)));
214 
215 		sc->ipmi_smic_data    = 0;
216 		sc->ipmi_smic_ctl_sts = sc->ipmi_bios_info.offset;
217 		sc->ipmi_smic_flags   = sc->ipmi_bios_info.offset * 2;
218 
219 		if (sc->ipmi_bios_info.io_mode) {
220 			sc->ipmi_io_rid = PCIR_BAR(0);
221 			sc->ipmi_io_res = bus_alloc_resource(dev,
222 			    SYS_RES_IOPORT, &sc->ipmi_io_rid,
223 			    sc->ipmi_bios_info.address,
224 			    sc->ipmi_bios_info.address +
225 				(sc->ipmi_bios_info.offset * 3),
226 			    sc->ipmi_bios_info.offset * 3,
227 			    RF_ACTIVE);
228 		} else {
229 			sc->ipmi_mem_rid = PCIR_BAR(0);
230 			sc->ipmi_mem_res = bus_alloc_resource(dev,
231 			    SYS_RES_MEMORY, &sc->ipmi_mem_rid,
232 			    sc->ipmi_bios_info.address,
233 			    sc->ipmi_bios_info.address +
234 			        (sc->ipmi_bios_info.offset * 2),
235 			    sc->ipmi_bios_info.offset * 2,
236 			    RF_ACTIVE);
237 		}
238 
239 		if (!sc->ipmi_io_res && !sc->ipmi_mem_res){
240 			device_printf(dev, "couldn't configure pci res\n");
241 			goto bad;
242 		}
243 
244 		flags = INB(sc, sc->ipmi_smic_flags);
245 		if (flags == 0xff) {
246 			device_printf(dev, "couldn't find it\n");
247 			goto bad;
248 		}
249 		if ((flags & SMIC_STATUS_SMS_ATN)
250 		    && (flags & SMIC_STATUS_RX_RDY)){
251 			ipmi_read(dev, NULL, 0);
252 		}
253 	} else {
254 		device_printf(dev, "No IPMI interface found\n");
255 		goto bad;
256 	}
257 	ipmi_attach(dev);
258 
259 	sc->ipmi_irq_rid = 0;
260         sc->ipmi_irq_res = bus_alloc_resource_any(sc->ipmi_dev, SYS_RES_IRQ,
261 	    &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
262 	if (sc->ipmi_irq_res == NULL) {
263                 device_printf(sc->ipmi_dev, "can't allocate interrupt\n");
264         } else {
265 		if (bus_setup_intr(sc->ipmi_dev, sc->ipmi_irq_res,
266 				   INTR_TYPE_MISC, ipmi_intr,
267 				   sc->ipmi_dev, &sc->ipmi_irq)) {
268 			device_printf(sc->ipmi_dev,
269 			    "can't set up interrupt\n");
270 			return (EINVAL);
271 		}
272 	}
273 
274 	return 0;
275 bad:
276 	return ENXIO;
277 }
278 
279 static int ipmi_pci_detach(device_t dev) {
280 	struct ipmi_softc *sc;
281 
282 	sc = device_get_softc(dev);
283 	ipmi_detach(dev);
284 	if (sc->ipmi_ev_tag)
285 		EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_ev_tag);
286 
287 	if (sc->ipmi_mem_res)
288 		bus_release_resource(dev, SYS_RES_MEMORY, sc->ipmi_mem_rid,
289 		    sc->ipmi_mem_res);
290 	if (sc->ipmi_io_res)
291 		bus_release_resource(dev, SYS_RES_IOPORT, sc->ipmi_io_rid,
292 		    sc->ipmi_io_res);
293 	if (sc->ipmi_irq)
294 		bus_teardown_intr(sc->ipmi_dev, sc->ipmi_irq_res,
295 		    sc->ipmi_irq);
296 	if (sc->ipmi_irq_res)
297 		bus_release_resource(sc->ipmi_dev, SYS_RES_IRQ,
298 		    sc->ipmi_irq_rid, sc->ipmi_irq_res);
299 
300 	return 0;
301 }
302 
303 static driver_t ipmi_pci_driver = {
304         "ipmi",
305         ipmi_methods,
306         sizeof(struct ipmi_softc)
307 };
308 
309 DRIVER_MODULE(ipmi_foo, pci, ipmi_pci_driver, ipmi_devclass, 0, 0);
310