137b1ce13SDoug Ambrisko /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni *
437b1ce13SDoug Ambrisko * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
537b1ce13SDoug Ambrisko * All rights reserved.
637b1ce13SDoug Ambrisko *
737b1ce13SDoug Ambrisko * Redistribution and use in source and binary forms, with or without
837b1ce13SDoug Ambrisko * modification, are permitted provided that the following conditions
937b1ce13SDoug Ambrisko * are met:
1037b1ce13SDoug Ambrisko * 1. Redistributions of source code must retain the above copyright
1137b1ce13SDoug Ambrisko * notice, this list of conditions and the following disclaimer.
1237b1ce13SDoug Ambrisko * 2. Redistributions in binary form must reproduce the above copyright
1337b1ce13SDoug Ambrisko * notice, this list of conditions and the following disclaimer in the
1437b1ce13SDoug Ambrisko * documentation and/or other materials provided with the distribution.
1537b1ce13SDoug Ambrisko *
1637b1ce13SDoug Ambrisko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1737b1ce13SDoug Ambrisko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1837b1ce13SDoug Ambrisko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1937b1ce13SDoug Ambrisko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2037b1ce13SDoug Ambrisko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2137b1ce13SDoug Ambrisko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2237b1ce13SDoug Ambrisko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2337b1ce13SDoug Ambrisko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2437b1ce13SDoug Ambrisko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2537b1ce13SDoug Ambrisko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2637b1ce13SDoug Ambrisko * SUCH DAMAGE.
2737b1ce13SDoug Ambrisko */
2837b1ce13SDoug Ambrisko
2937b1ce13SDoug Ambrisko #include <sys/param.h>
3037b1ce13SDoug Ambrisko #include <sys/systm.h>
31d72a0786SJohn Baldwin #include <sys/bus.h>
32d72a0786SJohn Baldwin #include <sys/condvar.h>
33d72a0786SJohn Baldwin #include <sys/eventhandler.h>
3437b1ce13SDoug Ambrisko #include <sys/kernel.h>
3537b1ce13SDoug Ambrisko #include <sys/module.h>
3637b1ce13SDoug Ambrisko #include <sys/rman.h>
37d72a0786SJohn Baldwin #include <sys/selinfo.h>
38ee8b757aSYinlong Lu #include <sys/efi.h>
3937b1ce13SDoug Ambrisko
4037b1ce13SDoug Ambrisko #include <dev/pci/pcireg.h>
4137b1ce13SDoug Ambrisko #include <dev/pci/pcivar.h>
4237b1ce13SDoug Ambrisko
4337b1ce13SDoug Ambrisko #ifdef LOCAL_MODULE
4437b1ce13SDoug Ambrisko #include <ipmivars.h>
4537b1ce13SDoug Ambrisko #else
4637b1ce13SDoug Ambrisko #include <dev/ipmi/ipmivars.h>
4737b1ce13SDoug Ambrisko #endif
4837b1ce13SDoug Ambrisko
4937b1ce13SDoug Ambrisko static int ipmi_pci_probe(device_t dev);
5037b1ce13SDoug Ambrisko static int ipmi_pci_attach(device_t dev);
5137b1ce13SDoug Ambrisko
52d72a0786SJohn Baldwin static struct ipmi_ident
5337b1ce13SDoug Ambrisko {
5437b1ce13SDoug Ambrisko u_int16_t vendor;
5537b1ce13SDoug Ambrisko u_int16_t device;
5637b1ce13SDoug Ambrisko char *desc;
5737b1ce13SDoug Ambrisko } ipmi_identifiers[] = {
5837b1ce13SDoug Ambrisko {0x1028, 0x000d, "Dell PE2650 SMIC interface"},
5937b1ce13SDoug Ambrisko {0, 0, 0}
6037b1ce13SDoug Ambrisko };
6137b1ce13SDoug Ambrisko
62d72a0786SJohn Baldwin const char *
ipmi_pci_match(uint16_t vendor,uint16_t device)63d72a0786SJohn Baldwin ipmi_pci_match(uint16_t vendor, uint16_t device)
64d72a0786SJohn Baldwin {
6537b1ce13SDoug Ambrisko struct ipmi_ident *m;
6637b1ce13SDoug Ambrisko
67d72a0786SJohn Baldwin for (m = ipmi_identifiers; m->vendor != 0; m++)
68d72a0786SJohn Baldwin if (m->vendor == vendor && m->device == device)
69d72a0786SJohn Baldwin return (m->desc);
70d72a0786SJohn Baldwin return (NULL);
7137b1ce13SDoug Ambrisko }
7237b1ce13SDoug Ambrisko
7337b1ce13SDoug Ambrisko static int
ipmi_pci_probe(device_t dev)74d72a0786SJohn Baldwin ipmi_pci_probe(device_t dev)
75d72a0786SJohn Baldwin {
76d72a0786SJohn Baldwin const char *desc;
77d72a0786SJohn Baldwin
78d72a0786SJohn Baldwin if (ipmi_attached)
79d72a0786SJohn Baldwin return (ENXIO);
80d72a0786SJohn Baldwin
81d72a0786SJohn Baldwin desc = ipmi_pci_match(pci_get_vendor(dev), pci_get_device(dev));
82d72a0786SJohn Baldwin if (desc != NULL) {
83d72a0786SJohn Baldwin device_set_desc(dev, desc);
84d72a0786SJohn Baldwin return (BUS_PROBE_DEFAULT);
85d72a0786SJohn Baldwin }
86d72a0786SJohn Baldwin
87d72a0786SJohn Baldwin return (ENXIO);
88d72a0786SJohn Baldwin }
89d72a0786SJohn Baldwin
90d72a0786SJohn Baldwin static int
ipmi_pci_attach(device_t dev)91d72a0786SJohn Baldwin ipmi_pci_attach(device_t dev)
92d72a0786SJohn Baldwin {
9337b1ce13SDoug Ambrisko struct ipmi_softc *sc = device_get_softc(dev);
94d72a0786SJohn Baldwin struct ipmi_get_info info;
95d72a0786SJohn Baldwin const char *mode;
96d72a0786SJohn Baldwin int error, type;
9737b1ce13SDoug Ambrisko
98d72a0786SJohn Baldwin /* Look for an IPMI entry in the SMBIOS table. */
99d72a0786SJohn Baldwin if (!ipmi_smbios_identify(&info))
100d72a0786SJohn Baldwin return (ENXIO);
10137b1ce13SDoug Ambrisko
10237b1ce13SDoug Ambrisko sc->ipmi_dev = dev;
10337b1ce13SDoug Ambrisko
104d72a0786SJohn Baldwin switch (info.iface_type) {
105d72a0786SJohn Baldwin case KCS_MODE:
106d72a0786SJohn Baldwin mode = "KCS";
107d72a0786SJohn Baldwin break;
108d72a0786SJohn Baldwin case SMIC_MODE:
109d72a0786SJohn Baldwin mode = "SMIC";
110d72a0786SJohn Baldwin break;
111d72a0786SJohn Baldwin case BT_MODE:
112*1f166509SAndrey V. Elsukov mode = "BT";
113*1f166509SAndrey V. Elsukov break;
114d72a0786SJohn Baldwin default:
11537b1ce13SDoug Ambrisko device_printf(dev, "No IPMI interface found\n");
116d72a0786SJohn Baldwin return (ENXIO);
11737b1ce13SDoug Ambrisko }
118d72a0786SJohn Baldwin
119d72a0786SJohn Baldwin device_printf(dev, "%s mode found at %s 0x%jx alignment 0x%x on %s\n",
120d72a0786SJohn Baldwin mode, info.io_mode ? "io" : "mem",
121d72a0786SJohn Baldwin (uintmax_t)info.address, info.offset,
122d72a0786SJohn Baldwin device_get_name(device_get_parent(dev)));
123d72a0786SJohn Baldwin if (info.io_mode)
124d72a0786SJohn Baldwin type = SYS_RES_IOPORT;
125d72a0786SJohn Baldwin else
126d72a0786SJohn Baldwin type = SYS_RES_MEMORY;
127d72a0786SJohn Baldwin
128d72a0786SJohn Baldwin sc->ipmi_io_rid = PCIR_BAR(0);
129d72a0786SJohn Baldwin sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type,
130d72a0786SJohn Baldwin &sc->ipmi_io_rid, RF_ACTIVE);
131d72a0786SJohn Baldwin sc->ipmi_io_type = type;
132d72a0786SJohn Baldwin sc->ipmi_io_spacing = info.offset;
133d72a0786SJohn Baldwin
134d72a0786SJohn Baldwin if (sc->ipmi_io_res[0] == NULL) {
135d72a0786SJohn Baldwin device_printf(dev, "couldn't configure pci io res\n");
136d72a0786SJohn Baldwin return (ENXIO);
137d72a0786SJohn Baldwin }
13837b1ce13SDoug Ambrisko
13937b1ce13SDoug Ambrisko sc->ipmi_irq_rid = 0;
140d72a0786SJohn Baldwin sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
14137b1ce13SDoug Ambrisko &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
14237b1ce13SDoug Ambrisko
143*1f166509SAndrey V. Elsukov error = ENXIO;
144d72a0786SJohn Baldwin switch (info.iface_type) {
145d72a0786SJohn Baldwin case KCS_MODE:
146d72a0786SJohn Baldwin error = ipmi_kcs_attach(sc);
147d72a0786SJohn Baldwin break;
148d72a0786SJohn Baldwin case SMIC_MODE:
149d72a0786SJohn Baldwin error = ipmi_smic_attach(sc);
150*1f166509SAndrey V. Elsukov break;
151*1f166509SAndrey V. Elsukov case BT_MODE:
152*1f166509SAndrey V. Elsukov error = ipmi_bt_attach(sc);
153d72a0786SJohn Baldwin break;
154d72a0786SJohn Baldwin }
155*1f166509SAndrey V. Elsukov if (error)
156*1f166509SAndrey V. Elsukov goto bad;
157d72a0786SJohn Baldwin error = ipmi_attach(dev);
158d72a0786SJohn Baldwin if (error)
159d72a0786SJohn Baldwin goto bad;
160d72a0786SJohn Baldwin
161d72a0786SJohn Baldwin return (0);
16237b1ce13SDoug Ambrisko bad:
163d72a0786SJohn Baldwin ipmi_release_resources(dev);
164d72a0786SJohn Baldwin return (error);
16537b1ce13SDoug Ambrisko }
16637b1ce13SDoug Ambrisko
167d72a0786SJohn Baldwin static device_method_t ipmi_methods[] = {
168d72a0786SJohn Baldwin /* Device interface */
169d72a0786SJohn Baldwin DEVMETHOD(device_probe, ipmi_pci_probe),
170d72a0786SJohn Baldwin DEVMETHOD(device_attach, ipmi_pci_attach),
171d72a0786SJohn Baldwin DEVMETHOD(device_detach, ipmi_detach),
172d72a0786SJohn Baldwin { 0, 0 }
173d72a0786SJohn Baldwin };
17437b1ce13SDoug Ambrisko
17537b1ce13SDoug Ambrisko static driver_t ipmi_pci_driver = {
17637b1ce13SDoug Ambrisko "ipmi",
17737b1ce13SDoug Ambrisko ipmi_methods,
17837b1ce13SDoug Ambrisko sizeof(struct ipmi_softc)
17937b1ce13SDoug Ambrisko };
18037b1ce13SDoug Ambrisko
181fd773e2bSJohn Baldwin DRIVER_MODULE(ipmi_pci, pci, ipmi_pci_driver, 0, 0);
182d72a0786SJohn Baldwin
183d72a0786SJohn Baldwin /* Native IPMI on PCI driver. */
184d72a0786SJohn Baldwin
185d72a0786SJohn Baldwin static int
ipmi2_pci_probe(device_t dev)186d72a0786SJohn Baldwin ipmi2_pci_probe(device_t dev)
187d72a0786SJohn Baldwin {
188d72a0786SJohn Baldwin
189d72a0786SJohn Baldwin if (pci_get_class(dev) == PCIC_SERIALBUS &&
190657d9f9fSJohn Baldwin pci_get_subclass(dev) == PCIS_SERIALBUS_IPMI) {
191d72a0786SJohn Baldwin device_set_desc(dev, "IPMI System Interface");
192d72a0786SJohn Baldwin return (BUS_PROBE_GENERIC);
193d72a0786SJohn Baldwin }
194d72a0786SJohn Baldwin
195d72a0786SJohn Baldwin return (ENXIO);
196d72a0786SJohn Baldwin }
197d72a0786SJohn Baldwin
198d72a0786SJohn Baldwin static int
ipmi2_pci_attach(device_t dev)199d72a0786SJohn Baldwin ipmi2_pci_attach(device_t dev)
200d72a0786SJohn Baldwin {
201d72a0786SJohn Baldwin struct ipmi_softc *sc;
202d72a0786SJohn Baldwin int error, iface, type;
203d72a0786SJohn Baldwin
204d72a0786SJohn Baldwin sc = device_get_softc(dev);
205d72a0786SJohn Baldwin sc->ipmi_dev = dev;
206d72a0786SJohn Baldwin
207d72a0786SJohn Baldwin /* Interface is determined by progif. */
208d72a0786SJohn Baldwin switch (pci_get_progif(dev)) {
209657d9f9fSJohn Baldwin case PCIP_SERIALBUS_IPMI_SMIC:
210d72a0786SJohn Baldwin iface = SMIC_MODE;
211d72a0786SJohn Baldwin break;
212657d9f9fSJohn Baldwin case PCIP_SERIALBUS_IPMI_KCS:
213d72a0786SJohn Baldwin iface = KCS_MODE;
214d72a0786SJohn Baldwin break;
215657d9f9fSJohn Baldwin case PCIP_SERIALBUS_IPMI_BT:
216d72a0786SJohn Baldwin iface = BT_MODE;
217*1f166509SAndrey V. Elsukov break;
218d72a0786SJohn Baldwin default:
219d72a0786SJohn Baldwin device_printf(dev, "Unsupported interface: %d\n",
220d72a0786SJohn Baldwin pci_get_progif(dev));
221d72a0786SJohn Baldwin return (ENXIO);
222d72a0786SJohn Baldwin }
223d72a0786SJohn Baldwin
2244dc5078fSJohn Baldwin /* Check the BAR to determine our resource type. */
225d72a0786SJohn Baldwin sc->ipmi_io_rid = PCIR_BAR(0);
2264dc5078fSJohn Baldwin if (PCI_BAR_IO(pci_read_config(dev, PCIR_BAR(0), 4)))
227d72a0786SJohn Baldwin type = SYS_RES_IOPORT;
228d72a0786SJohn Baldwin else
229d72a0786SJohn Baldwin type = SYS_RES_MEMORY;
230d72a0786SJohn Baldwin sc->ipmi_io_type = type;
231d72a0786SJohn Baldwin sc->ipmi_io_spacing = 1;
232d72a0786SJohn Baldwin sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type,
233d72a0786SJohn Baldwin &sc->ipmi_io_rid, RF_ACTIVE);
234d72a0786SJohn Baldwin if (sc->ipmi_io_res[0] == NULL) {
235d72a0786SJohn Baldwin device_printf(dev, "couldn't map ports/memory\n");
236d72a0786SJohn Baldwin return (ENXIO);
237d72a0786SJohn Baldwin }
238d72a0786SJohn Baldwin
239d72a0786SJohn Baldwin sc->ipmi_irq_rid = 0;
240d72a0786SJohn Baldwin sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
241d72a0786SJohn Baldwin &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
242d72a0786SJohn Baldwin
243*1f166509SAndrey V. Elsukov error = ENXIO;
244d72a0786SJohn Baldwin switch (iface) {
245d72a0786SJohn Baldwin case KCS_MODE:
246d72a0786SJohn Baldwin device_printf(dev, "using KSC interface\n");
247d72a0786SJohn Baldwin
248d72a0786SJohn Baldwin /*
249d72a0786SJohn Baldwin * We have to examine the resource directly to determine the
250d72a0786SJohn Baldwin * alignment.
251d72a0786SJohn Baldwin */
252d72a0786SJohn Baldwin if (!ipmi_kcs_probe_align(sc)) {
253d72a0786SJohn Baldwin device_printf(dev, "Unable to determine alignment\n");
254d72a0786SJohn Baldwin goto bad;
255d72a0786SJohn Baldwin }
256d72a0786SJohn Baldwin
257d72a0786SJohn Baldwin error = ipmi_kcs_attach(sc);
258d72a0786SJohn Baldwin if (error)
259d72a0786SJohn Baldwin goto bad;
260d72a0786SJohn Baldwin break;
261d72a0786SJohn Baldwin case SMIC_MODE:
262d72a0786SJohn Baldwin device_printf(dev, "using SMIC interface\n");
263d72a0786SJohn Baldwin error = ipmi_smic_attach(sc);
264*1f166509SAndrey V. Elsukov break;
265*1f166509SAndrey V. Elsukov case BT_MODE:
266*1f166509SAndrey V. Elsukov device_printf(dev, "using BT interface\n");
267*1f166509SAndrey V. Elsukov error = ipmi_bt_attach(sc);
268d72a0786SJohn Baldwin break;
269d72a0786SJohn Baldwin }
270*1f166509SAndrey V. Elsukov if (error)
271*1f166509SAndrey V. Elsukov goto bad;
272d72a0786SJohn Baldwin error = ipmi_attach(dev);
273d72a0786SJohn Baldwin if (error)
274d72a0786SJohn Baldwin goto bad;
275d72a0786SJohn Baldwin
276d72a0786SJohn Baldwin return (0);
277d72a0786SJohn Baldwin bad:
278d72a0786SJohn Baldwin ipmi_release_resources(dev);
279d72a0786SJohn Baldwin return (error);
280d72a0786SJohn Baldwin }
281d72a0786SJohn Baldwin
282d72a0786SJohn Baldwin static device_method_t ipmi2_methods[] = {
283d72a0786SJohn Baldwin /* Device interface */
284d72a0786SJohn Baldwin DEVMETHOD(device_probe, ipmi2_pci_probe),
285d72a0786SJohn Baldwin DEVMETHOD(device_attach, ipmi2_pci_attach),
286d72a0786SJohn Baldwin DEVMETHOD(device_detach, ipmi_detach),
287d72a0786SJohn Baldwin { 0, 0 }
288d72a0786SJohn Baldwin };
289d72a0786SJohn Baldwin
290d72a0786SJohn Baldwin static driver_t ipmi2_pci_driver = {
291d72a0786SJohn Baldwin "ipmi",
292d72a0786SJohn Baldwin ipmi2_methods,
293d72a0786SJohn Baldwin sizeof(struct ipmi_softc)
294d72a0786SJohn Baldwin };
295d72a0786SJohn Baldwin
296fd773e2bSJohn Baldwin DRIVER_MODULE(ipmi2_pci, pci, ipmi2_pci_driver, 0, 0);
297ee8b757aSYinlong Lu #ifdef ARCH_MAY_USE_EFI
298ee8b757aSYinlong Lu MODULE_DEPEND(ipmi2_pci, efirt, 1, 1, 1);
299ee8b757aSYinlong Lu #endif
300