]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/ipmi/ipmi_isa.c
Initial import from vendor-sys branch of openzfs
[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
42 #include <machine/pci_cfgreg.h>
43 #include <dev/pci/pcireg.h>
44
45 #include <isa/isavar.h>
46
47 #ifdef LOCAL_MODULE
48 #include <ipmi.h>
49 #include <ipmivars.h>
50 #else
51 #include <sys/ipmi.h>
52 #include <dev/ipmi/ipmivars.h>
53 #endif
54
55 static void
56 ipmi_isa_identify(driver_t *driver, device_t parent)
57 {
58         struct ipmi_get_info info;
59         uint32_t devid;
60
61         if (ipmi_smbios_identify(&info) && info.iface_type != SSIF_MODE &&
62             device_find_child(parent, "ipmi", -1) == NULL) {
63                 /*
64                  * XXX: Hack alert.  On some broken systems, the IPMI
65                  * interface is described via SMBIOS, but the actual
66                  * IO resource is in a PCI device BAR, so we have to let
67                  * the PCI device attach ipmi instead.  In that case don't
68                  * create an isa ipmi device.  For now we hardcode the list
69                  * of bus, device, function tuples.
70                  */
71                 devid = pci_cfgregread(0, 4, 2, PCIR_DEVVENDOR, 4);
72                 if (devid != 0xffffffff &&
73                     ipmi_pci_match(devid & 0xffff, devid >> 16) != NULL)
74                         return;
75                 BUS_ADD_CHILD(parent, 0, "ipmi", -1);
76         }
77 }
78
79 static int
80 ipmi_isa_probe(device_t dev)
81 {
82
83         /*
84          * Give other drivers precedence.  Unfortunately, this doesn't
85          * work if we have an SMBIOS table that duplicates a PCI device
86          * that's later on the bus than the PCI-ISA bridge.
87          */
88         if (ipmi_attached)
89                 return (ENXIO);
90
91         /* Skip any PNP devices. */
92         if (isa_get_logicalid(dev) != 0)
93                 return (ENXIO);
94
95         device_set_desc(dev, "IPMI System Interface");
96         return (BUS_PROBE_DEFAULT);
97 }
98
99 static int
100 ipmi_hint_identify(device_t dev, struct ipmi_get_info *info)
101 {
102         const char *mode, *name;
103         int i, unit, val;
104
105         /* We require at least a "mode" hint. */
106         name = device_get_name(dev);
107         unit = device_get_unit(dev);
108         if (resource_string_value(name, unit, "mode", &mode) != 0)
109                 return (0);
110
111         /* Set the mode and default I/O resources for each mode. */
112         bzero(info, sizeof(struct ipmi_get_info));
113         if (strcasecmp(mode, "KCS") == 0) {
114                 info->iface_type = KCS_MODE;
115                 info->address = 0xca2;
116                 info->io_mode = 1;
117                 info->offset = 1;
118         } else if (strcasecmp(mode, "SMIC") == 0) {
119                 info->iface_type = SMIC_MODE;
120                 info->address = 0xca9;
121                 info->io_mode = 1;
122                 info->offset = 1;
123         } else if (strcasecmp(mode, "BT") == 0) {
124                 info->iface_type = BT_MODE;
125                 info->address = 0xe4;
126                 info->io_mode = 1;
127                 info->offset = 1;
128         } else {
129                 device_printf(dev, "Invalid mode %s\n", mode);
130                 return (0);
131         }
132
133         /*
134          * Kill any resources that isahint.c might have setup for us
135          * since it will conflict with how we do resources.
136          */
137         for (i = 0; i < 2; i++) {
138                 bus_delete_resource(dev, SYS_RES_MEMORY, i);
139                 bus_delete_resource(dev, SYS_RES_IOPORT, i);
140         }
141
142         /* Allow the I/O address to be overriden via hints. */
143         if (resource_int_value(name, unit, "port", &val) == 0 && val != 0) {
144                 info->address = val;
145                 info->io_mode = 1;
146         } else if (resource_int_value(name, unit, "maddr", &val) == 0 &&
147             val != 0) {
148                 info->address = val;
149                 info->io_mode = 0;
150         }
151
152         /* Allow the spacing to be overriden. */
153         if (resource_int_value(name, unit, "spacing", &val) == 0) {
154                 switch (val) {
155                 case 8:
156                         info->offset = 1;
157                         break;
158                 case 16:
159                         info->offset = 2;
160                         break;
161                 case 32:
162                         info->offset = 4;
163                         break;
164                 default:
165                         device_printf(dev, "Invalid register spacing\n");
166                         return (0);
167                 }
168         }
169         return (1);
170 }
171
172 static int
173 ipmi_isa_attach(device_t dev)
174 {
175         struct ipmi_softc *sc = device_get_softc(dev);
176         struct ipmi_get_info info;
177         const char *mode;
178         int count, error, i, type;
179
180         /*
181          * Pull info out of the SMBIOS table.  If that doesn't work, use
182          * hints to enumerate a device.
183          */
184         if (!ipmi_smbios_identify(&info) &&
185             !ipmi_hint_identify(dev, &info))
186                 return (ENXIO);
187
188         switch (info.iface_type) {
189         case KCS_MODE:
190                 count = 2;
191                 mode = "KCS";
192                 break;
193         case SMIC_MODE:
194                 count = 3;
195                 mode = "SMIC";
196                 break;
197         case BT_MODE:
198                 device_printf(dev, "BT mode is unsupported\n");
199                 return (ENXIO);
200         default:
201                 return (ENXIO);
202         }
203         error = 0;
204         sc->ipmi_dev = dev;
205
206         device_printf(dev, "%s mode found at %s 0x%jx alignment 0x%x on %s\n",
207             mode, info.io_mode ? "io" : "mem",
208             (uintmax_t)info.address, info.offset,
209             device_get_name(device_get_parent(dev)));
210         if (info.io_mode)
211                 type = SYS_RES_IOPORT;
212         else
213                 type = SYS_RES_MEMORY;
214
215         sc->ipmi_io_type = type;
216         sc->ipmi_io_spacing = info.offset;
217         if (info.offset == 1) {
218                 sc->ipmi_io_rid = 0;
219                 sc->ipmi_io_res[0] = bus_alloc_resource(dev, type,
220                     &sc->ipmi_io_rid, info.address, info.address + count - 1,
221                     count, RF_ACTIVE);
222                 if (sc->ipmi_io_res[0] == NULL) {
223                         device_printf(dev, "couldn't configure I/O resource\n");
224                         return (ENXIO);
225                 }
226         } else {
227                 for (i = 0; i < count; i++) {
228                         sc->ipmi_io_rid = i;
229                         sc->ipmi_io_res[i] = bus_alloc_resource(dev, type,
230                             &sc->ipmi_io_rid, info.address + i * info.offset,
231                             info.address + i * info.offset, 1, RF_ACTIVE);
232                         if (sc->ipmi_io_res[i] == NULL) {
233                                 device_printf(dev,
234                                     "couldn't configure I/O resource\n");
235                                 error = ENXIO;
236                                 sc->ipmi_io_rid = 0;
237                                 goto bad;
238                         }
239                 }
240                 sc->ipmi_io_rid = 0;
241         }
242
243         if (info.irq != 0) {
244                 sc->ipmi_irq_rid = 0;
245                 sc->ipmi_irq_res = bus_alloc_resource(dev, SYS_RES_IRQ,
246                     &sc->ipmi_irq_rid, info.irq, info.irq, 1,
247                     RF_SHAREABLE | RF_ACTIVE);
248         }
249
250         switch (info.iface_type) {
251         case KCS_MODE:
252                 error = ipmi_kcs_attach(sc);
253                 if (error)
254                         goto bad;
255                 break;
256         case SMIC_MODE:
257                 error = ipmi_smic_attach(sc);
258                 if (error)
259                         goto bad;
260                 break;
261         }
262
263         error = ipmi_attach(dev);
264         if (error)
265                 goto bad;
266
267         return (0);
268 bad:
269         ipmi_release_resources(dev);
270         return (error);
271 }
272
273 static device_method_t ipmi_methods[] = {
274         /* Device interface */
275         DEVMETHOD(device_identify,      ipmi_isa_identify),
276         DEVMETHOD(device_probe,         ipmi_isa_probe),
277         DEVMETHOD(device_attach,        ipmi_isa_attach),
278         DEVMETHOD(device_detach,        ipmi_detach),
279         { 0, 0 }
280 };
281
282 static driver_t ipmi_isa_driver = {
283         "ipmi",
284         ipmi_methods,
285         sizeof(struct ipmi_softc),
286 };
287
288 DRIVER_MODULE(ipmi_isa, isa, ipmi_isa_driver, ipmi_devclass, 0, 0);