]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/ipmi/ipmi_isa.c
contrib/bc: update to version 4.0.2
[FreeBSD/FreeBSD.git] / sys / dev / ipmi / ipmi_isa.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/condvar.h>
36 #include <sys/eventhandler.h>
37 #include <sys/kernel.h>
38 #include <sys/module.h>
39 #include <sys/rman.h>
40 #include <sys/selinfo.h>
41 #include <sys/efi.h>
42
43 #include <machine/pci_cfgreg.h>
44 #include <dev/pci/pcireg.h>
45
46 #include <isa/isavar.h>
47
48 #ifdef LOCAL_MODULE
49 #include <ipmi.h>
50 #include <ipmivars.h>
51 #else
52 #include <sys/ipmi.h>
53 #include <dev/ipmi/ipmivars.h>
54 #endif
55
56 static void
57 ipmi_isa_identify(driver_t *driver, device_t parent)
58 {
59         struct ipmi_get_info info;
60         uint32_t devid;
61
62         if (ipmi_smbios_identify(&info) && info.iface_type != SSIF_MODE &&
63             device_find_child(parent, "ipmi", -1) == NULL) {
64                 /*
65                  * XXX: Hack alert.  On some broken systems, the IPMI
66                  * interface is described via SMBIOS, but the actual
67                  * IO resource is in a PCI device BAR, so we have to let
68                  * the PCI device attach ipmi instead.  In that case don't
69                  * create an isa ipmi device.  For now we hardcode the list
70                  * of bus, device, function tuples.
71                  */
72                 devid = pci_cfgregread(0, 4, 2, PCIR_DEVVENDOR, 4);
73                 if (devid != 0xffffffff &&
74                     ipmi_pci_match(devid & 0xffff, devid >> 16) != NULL)
75                         return;
76                 BUS_ADD_CHILD(parent, 0, "ipmi", -1);
77         }
78 }
79
80 static int
81 ipmi_isa_probe(device_t dev)
82 {
83
84         /*
85          * Give other drivers precedence.  Unfortunately, this doesn't
86          * work if we have an SMBIOS table that duplicates a PCI device
87          * that's later on the bus than the PCI-ISA bridge.
88          */
89         if (ipmi_attached)
90                 return (ENXIO);
91
92         /* Skip any PNP devices. */
93         if (isa_get_logicalid(dev) != 0)
94                 return (ENXIO);
95
96         device_set_desc(dev, "IPMI System Interface");
97         return (BUS_PROBE_DEFAULT);
98 }
99
100 static int
101 ipmi_hint_identify(device_t dev, struct ipmi_get_info *info)
102 {
103         const char *mode, *name;
104         int i, unit, val;
105
106         /* We require at least a "mode" hint. */
107         name = device_get_name(dev);
108         unit = device_get_unit(dev);
109         if (resource_string_value(name, unit, "mode", &mode) != 0)
110                 return (0);
111
112         /* Set the mode and default I/O resources for each mode. */
113         bzero(info, sizeof(struct ipmi_get_info));
114         if (strcasecmp(mode, "KCS") == 0) {
115                 info->iface_type = KCS_MODE;
116                 info->address = 0xca2;
117                 info->io_mode = 1;
118                 info->offset = 1;
119         } else if (strcasecmp(mode, "SMIC") == 0) {
120                 info->iface_type = SMIC_MODE;
121                 info->address = 0xca9;
122                 info->io_mode = 1;
123                 info->offset = 1;
124         } else if (strcasecmp(mode, "BT") == 0) {
125                 info->iface_type = BT_MODE;
126                 info->address = 0xe4;
127                 info->io_mode = 1;
128                 info->offset = 1;
129         } else {
130                 device_printf(dev, "Invalid mode %s\n", mode);
131                 return (0);
132         }
133
134         /*
135          * Kill any resources that isahint.c might have setup for us
136          * since it will conflict with how we do resources.
137          */
138         for (i = 0; i < 2; i++) {
139                 bus_delete_resource(dev, SYS_RES_MEMORY, i);
140                 bus_delete_resource(dev, SYS_RES_IOPORT, i);
141         }
142
143         /* Allow the I/O address to be overriden via hints. */
144         if (resource_int_value(name, unit, "port", &val) == 0 && val != 0) {
145                 info->address = val;
146                 info->io_mode = 1;
147         } else if (resource_int_value(name, unit, "maddr", &val) == 0 &&
148             val != 0) {
149                 info->address = val;
150                 info->io_mode = 0;
151         }
152
153         /* Allow the spacing to be overriden. */
154         if (resource_int_value(name, unit, "spacing", &val) == 0) {
155                 switch (val) {
156                 case 8:
157                         info->offset = 1;
158                         break;
159                 case 16:
160                         info->offset = 2;
161                         break;
162                 case 32:
163                         info->offset = 4;
164                         break;
165                 default:
166                         device_printf(dev, "Invalid register spacing\n");
167                         return (0);
168                 }
169         }
170         return (1);
171 }
172
173 static int
174 ipmi_isa_attach(device_t dev)
175 {
176         struct ipmi_softc *sc = device_get_softc(dev);
177         struct ipmi_get_info info;
178         const char *mode;
179         int count, error, i, type;
180
181         /*
182          * Pull info out of the SMBIOS table.  If that doesn't work, use
183          * hints to enumerate a device.
184          */
185         if (!ipmi_smbios_identify(&info) &&
186             !ipmi_hint_identify(dev, &info))
187                 return (ENXIO);
188
189         switch (info.iface_type) {
190         case KCS_MODE:
191                 count = 2;
192                 mode = "KCS";
193                 break;
194         case SMIC_MODE:
195                 count = 3;
196                 mode = "SMIC";
197                 break;
198         case BT_MODE:
199                 device_printf(dev, "BT mode is unsupported\n");
200                 return (ENXIO);
201         default:
202                 return (ENXIO);
203         }
204         error = 0;
205         sc->ipmi_dev = dev;
206
207         device_printf(dev, "%s mode found at %s 0x%jx alignment 0x%x on %s\n",
208             mode, info.io_mode ? "io" : "mem",
209             (uintmax_t)info.address, info.offset,
210             device_get_name(device_get_parent(dev)));
211         if (info.io_mode)
212                 type = SYS_RES_IOPORT;
213         else
214                 type = SYS_RES_MEMORY;
215
216         sc->ipmi_io_type = type;
217         sc->ipmi_io_spacing = info.offset;
218         if (info.offset == 1) {
219                 sc->ipmi_io_rid = 0;
220                 sc->ipmi_io_res[0] = bus_alloc_resource(dev, type,
221                     &sc->ipmi_io_rid, info.address, info.address + count - 1,
222                     count, RF_ACTIVE);
223                 if (sc->ipmi_io_res[0] == NULL) {
224                         device_printf(dev, "couldn't configure I/O resource\n");
225                         return (ENXIO);
226                 }
227         } else {
228                 for (i = 0; i < count; i++) {
229                         sc->ipmi_io_rid = i;
230                         sc->ipmi_io_res[i] = bus_alloc_resource(dev, type,
231                             &sc->ipmi_io_rid, info.address + i * info.offset,
232                             info.address + i * info.offset, 1, RF_ACTIVE);
233                         if (sc->ipmi_io_res[i] == NULL) {
234                                 device_printf(dev,
235                                     "couldn't configure I/O resource\n");
236                                 error = ENXIO;
237                                 sc->ipmi_io_rid = 0;
238                                 goto bad;
239                         }
240                 }
241                 sc->ipmi_io_rid = 0;
242         }
243
244         if (info.irq != 0) {
245                 sc->ipmi_irq_rid = 0;
246                 sc->ipmi_irq_res = bus_alloc_resource(dev, SYS_RES_IRQ,
247                     &sc->ipmi_irq_rid, info.irq, info.irq, 1,
248                     RF_SHAREABLE | RF_ACTIVE);
249         }
250
251         switch (info.iface_type) {
252         case KCS_MODE:
253                 error = ipmi_kcs_attach(sc);
254                 if (error)
255                         goto bad;
256                 break;
257         case SMIC_MODE:
258                 error = ipmi_smic_attach(sc);
259                 if (error)
260                         goto bad;
261                 break;
262         }
263
264         error = ipmi_attach(dev);
265         if (error)
266                 goto bad;
267
268         return (0);
269 bad:
270         ipmi_release_resources(dev);
271         return (error);
272 }
273
274 static device_method_t ipmi_methods[] = {
275         /* Device interface */
276         DEVMETHOD(device_identify,      ipmi_isa_identify),
277         DEVMETHOD(device_probe,         ipmi_isa_probe),
278         DEVMETHOD(device_attach,        ipmi_isa_attach),
279         DEVMETHOD(device_detach,        ipmi_detach),
280         { 0, 0 }
281 };
282
283 static driver_t ipmi_isa_driver = {
284         "ipmi",
285         ipmi_methods,
286         sizeof(struct ipmi_softc),
287 };
288
289 DRIVER_MODULE(ipmi_isa, isa, ipmi_isa_driver, ipmi_devclass, 0, 0);
290 #ifdef ARCH_MAY_USE_EFI
291 MODULE_DEPEND(ipmi_isa, efirt, 1, 1, 1);
292 #endif