xref: /freebsd/sys/dev/ipmi/ipmi_pci.c (revision 10b59a9b4add0320d52c15ce057dd697261e7dfc)
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/systm.h>
32  #include <sys/bus.h>
33  #include <sys/condvar.h>
34  #include <sys/eventhandler.h>
35  #include <sys/kernel.h>
36  #include <sys/module.h>
37  #include <sys/rman.h>
38  #include <sys/selinfo.h>
39  
40  #include <dev/pci/pcireg.h>
41  #include <dev/pci/pcivar.h>
42  
43  #ifdef LOCAL_MODULE
44  #include <ipmivars.h>
45  #else
46  #include <dev/ipmi/ipmivars.h>
47  #endif
48  
49  static int ipmi_pci_probe(device_t dev);
50  static int ipmi_pci_attach(device_t dev);
51  
52  static struct ipmi_ident
53  {
54  	u_int16_t	vendor;
55  	u_int16_t	device;
56  	char		*desc;
57  } ipmi_identifiers[] = {
58  	{0x1028, 0x000d, "Dell PE2650 SMIC interface"},
59  	{0, 0, 0}
60  };
61  
62  const char *
63  ipmi_pci_match(uint16_t vendor, uint16_t device)
64  {
65  	struct ipmi_ident *m;
66  
67  	for (m = ipmi_identifiers; m->vendor != 0; m++)
68  		if (m->vendor == vendor && m->device == device)
69  			return (m->desc);
70  	return (NULL);
71  }
72  
73  static int
74  ipmi_pci_probe(device_t dev)
75  {
76  	const char *desc;
77  
78  	if (ipmi_attached)
79  		return (ENXIO);
80  
81  	desc = ipmi_pci_match(pci_get_vendor(dev), pci_get_device(dev));
82  	if (desc != NULL) {
83  		device_set_desc(dev, desc);
84  		return (BUS_PROBE_DEFAULT);
85  	}
86  
87  	return (ENXIO);
88  }
89  
90  static int
91  ipmi_pci_attach(device_t dev)
92  {
93  	struct ipmi_softc *sc = device_get_softc(dev);
94  	struct ipmi_get_info info;
95  	const char *mode;
96  	int error, type;
97  
98  	/* Look for an IPMI entry in the SMBIOS table. */
99  	if (!ipmi_smbios_identify(&info))
100  		return (ENXIO);
101  
102  	sc->ipmi_dev = dev;
103  
104  	switch (info.iface_type) {
105  	case KCS_MODE:
106  		mode = "KCS";
107  		break;
108  	case SMIC_MODE:
109  		mode = "SMIC";
110  		break;
111  	case BT_MODE:
112  		device_printf(dev, "BT mode is unsupported\n");
113  		return (ENXIO);
114  	default:
115  		device_printf(dev, "No IPMI interface found\n");
116  		return (ENXIO);
117  	}
118  
119  	device_printf(dev, "%s mode found at %s 0x%jx alignment 0x%x on %s\n",
120  	    mode, info.io_mode ? "io" : "mem",
121  	    (uintmax_t)info.address, info.offset,
122  	    device_get_name(device_get_parent(dev)));
123  	if (info.io_mode)
124  		type = SYS_RES_IOPORT;
125  	else
126  		type = SYS_RES_MEMORY;
127  
128  	sc->ipmi_io_rid = PCIR_BAR(0);
129  	sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type,
130  	    &sc->ipmi_io_rid, RF_ACTIVE);
131  	sc->ipmi_io_type = type;
132  	sc->ipmi_io_spacing = info.offset;
133  
134  	if (sc->ipmi_io_res[0] == NULL) {
135  		device_printf(dev, "couldn't configure pci io res\n");
136  		return (ENXIO);
137  	}
138  
139  	sc->ipmi_irq_rid = 0;
140  	sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
141  	    &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
142  
143  	switch (info.iface_type) {
144  	case KCS_MODE:
145  		error = ipmi_kcs_attach(sc);
146  		if (error)
147  			goto bad;
148  		break;
149  	case SMIC_MODE:
150  		error = ipmi_smic_attach(sc);
151  		if (error)
152  			goto bad;
153  		break;
154  	}
155  	error = ipmi_attach(dev);
156  	if (error)
157  		goto bad;
158  
159  	return (0);
160  bad:
161  	ipmi_release_resources(dev);
162  	return (error);
163  }
164  
165  static device_method_t ipmi_methods[] = {
166  	/* Device interface */
167  	DEVMETHOD(device_probe,     ipmi_pci_probe),
168  	DEVMETHOD(device_attach,    ipmi_pci_attach),
169  	DEVMETHOD(device_detach,    ipmi_detach),
170  	{ 0, 0 }
171  };
172  
173  static driver_t ipmi_pci_driver = {
174  	"ipmi",
175  	ipmi_methods,
176  	sizeof(struct ipmi_softc)
177  };
178  
179  DRIVER_MODULE(ipmi_pci, pci, ipmi_pci_driver, ipmi_devclass, 0, 0);
180  
181  /* Native IPMI on PCI driver. */
182  
183  static int
184  ipmi2_pci_probe(device_t dev)
185  {
186  
187  	if (pci_get_class(dev) == PCIC_SERIALBUS &&
188  	    pci_get_subclass(dev) == PCIS_SERIALBUS_IPMI) {
189  		device_set_desc(dev, "IPMI System Interface");
190  		return (BUS_PROBE_GENERIC);
191  	}
192  
193  	return (ENXIO);
194  }
195  
196  static int
197  ipmi2_pci_attach(device_t dev)
198  {
199  	struct ipmi_softc *sc;
200  	int error, iface, type;
201  
202  	sc = device_get_softc(dev);
203  	sc->ipmi_dev = dev;
204  
205  	/* Interface is determined by progif. */
206  	switch (pci_get_progif(dev)) {
207  	case PCIP_SERIALBUS_IPMI_SMIC:
208  		iface = SMIC_MODE;
209  		break;
210  	case PCIP_SERIALBUS_IPMI_KCS:
211  		iface = KCS_MODE;
212  		break;
213  	case PCIP_SERIALBUS_IPMI_BT:
214  		iface = BT_MODE;
215  		device_printf(dev, "BT interface unsupported\n");
216  		return (ENXIO);
217  	default:
218  		device_printf(dev, "Unsupported interface: %d\n",
219  		    pci_get_progif(dev));
220  		return (ENXIO);
221  	}
222  
223  	/* Check the BAR to determine our resource type. */
224  	sc->ipmi_io_rid = PCIR_BAR(0);
225  	if (PCI_BAR_IO(pci_read_config(dev, PCIR_BAR(0), 4)))
226  		type = SYS_RES_IOPORT;
227  	else
228  		type = SYS_RES_MEMORY;
229  	sc->ipmi_io_type = type;
230  	sc->ipmi_io_spacing = 1;
231  	sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type,
232  	    &sc->ipmi_io_rid, RF_ACTIVE);
233  	if (sc->ipmi_io_res[0] == NULL) {
234  		device_printf(dev, "couldn't map ports/memory\n");
235  		return (ENXIO);
236  	}
237  
238  	sc->ipmi_irq_rid = 0;
239  	sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
240  	    &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
241  
242  	switch (iface) {
243  	case KCS_MODE:
244  		device_printf(dev, "using KSC interface\n");
245  
246  		/*
247  		 * We have to examine the resource directly to determine the
248  		 * alignment.
249  		 */
250  		if (!ipmi_kcs_probe_align(sc)) {
251  			device_printf(dev, "Unable to determine alignment\n");
252  			error = ENXIO;
253  			goto bad;
254  		}
255  
256  		error = ipmi_kcs_attach(sc);
257  		if (error)
258  			goto bad;
259  		break;
260  	case SMIC_MODE:
261  		device_printf(dev, "using SMIC interface\n");
262  
263  		error = ipmi_smic_attach(sc);
264  		if (error)
265  			goto bad;
266  		break;
267  	}
268  	error = ipmi_attach(dev);
269  	if (error)
270  		goto bad;
271  
272  	return (0);
273  bad:
274  	ipmi_release_resources(dev);
275  	return (error);
276  }
277  
278  static device_method_t ipmi2_methods[] = {
279  	/* Device interface */
280  	DEVMETHOD(device_probe,     ipmi2_pci_probe),
281  	DEVMETHOD(device_attach,    ipmi2_pci_attach),
282  	DEVMETHOD(device_detach,    ipmi_detach),
283  	{ 0, 0 }
284  };
285  
286  static driver_t ipmi2_pci_driver = {
287  	"ipmi",
288  	ipmi2_methods,
289  	sizeof(struct ipmi_softc)
290  };
291  
292  DRIVER_MODULE(ipmi2_pci, pci, ipmi2_pci_driver, ipmi_devclass, 0, 0);
293