1162562Sjhb/*-
2162562Sjhb * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
3162562Sjhb * All rights reserved.
4162562Sjhb *
5162562Sjhb * Redistribution and use in source and binary forms, with or without
6162562Sjhb * modification, are permitted provided that the following conditions
7162562Sjhb * are met:
8162562Sjhb * 1. Redistributions of source code must retain the above copyright
9162562Sjhb *    notice, this list of conditions and the following disclaimer.
10162562Sjhb * 2. Redistributions in binary form must reproduce the above copyright
11162562Sjhb *    notice, this list of conditions and the following disclaimer in the
12162562Sjhb *    documentation and/or other materials provided with the distribution.
13162562Sjhb *
14162562Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15162562Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16162562Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17162562Sjhb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18162562Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19162562Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20162562Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21162562Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22162562Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23162562Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24162562Sjhb * SUCH DAMAGE.
25162562Sjhb */
26162562Sjhb
27162562Sjhb#include <sys/cdefs.h>
28162562Sjhb__FBSDID("$FreeBSD$");
29162562Sjhb
30162562Sjhb#include <sys/param.h>
31162562Sjhb#include <sys/systm.h>
32162562Sjhb#include <sys/bus.h>
33162562Sjhb#include <sys/condvar.h>
34162562Sjhb#include <sys/kernel.h>
35162562Sjhb#include <sys/module.h>
36162562Sjhb#include <sys/rman.h>
37162562Sjhb#include <sys/selinfo.h>
38162562Sjhb
39193530Sjkim#include <contrib/dev/acpica/include/acpi.h>
40193530Sjkim
41162562Sjhb#include <dev/acpica/acpivar.h>
42162562Sjhb
43162562Sjhb/* Hooks for the ACPI CA debugging infrastructure */
44162562Sjhb#define _COMPONENT	ACPI_BUTTON
45162562SjhbACPI_MODULE_NAME("IPMI")
46162562Sjhb
47162562Sjhb#ifdef LOCAL_MODULE
48162562Sjhb#include <ipmi.h>
49162562Sjhb#include <ipmivars.h>
50162562Sjhb#else
51162562Sjhb#include <sys/ipmi.h>
52162562Sjhb#include <dev/ipmi/ipmivars.h>
53162562Sjhb#endif
54162562Sjhb
55162562Sjhbstatic int ipmi_acpi_probe(device_t);
56162562Sjhbstatic int ipmi_acpi_attach(device_t);
57162562Sjhb
58162562Sjhbint
59162562Sjhbipmi_acpi_probe(device_t dev)
60162562Sjhb{
61162562Sjhb	static char *ipmi_ids[] = {"IPI0001", NULL};
62162562Sjhb
63162562Sjhb	if (ipmi_attached)
64162562Sjhb		return (EBUSY);
65162562Sjhb
66162562Sjhb	if (acpi_disabled("ipmi") ||
67162562Sjhb	    ACPI_ID_PROBE(device_get_parent(dev), dev, ipmi_ids) == NULL)
68162562Sjhb		return (ENXIO);
69162562Sjhb
70162562Sjhb	device_set_desc(dev, "IPMI System Interface");
71162562Sjhb
72162562Sjhb	return (0);
73162562Sjhb}
74162562Sjhb
75162562Sjhbstatic int
76162562Sjhbipmi_acpi_attach(device_t dev)
77162562Sjhb{
78162562Sjhb	ACPI_HANDLE devh;
79162562Sjhb	const char *mode;
80162562Sjhb	struct ipmi_get_info info;
81162562Sjhb	struct ipmi_softc *sc = device_get_softc(dev);
82162562Sjhb	int count, error, flags, i, type;
83162562Sjhb	int interface_type = 0, interface_version = 0;
84162562Sjhb
85162562Sjhb	error = 0;
86162562Sjhb	devh = acpi_get_handle(dev);
87162562Sjhb	if (ACPI_FAILURE(acpi_GetInteger(devh, "_IFT", &interface_type)))
88162562Sjhb		return (ENXIO);
89162562Sjhb
90162562Sjhb	if (ACPI_FAILURE(acpi_GetInteger(devh, "_SRV", &interface_version)))
91162562Sjhb		return (ENXIO);
92162562Sjhb
93162562Sjhb	switch (interface_type) {
94162562Sjhb	case KCS_MODE:
95162562Sjhb		count = 2;
96162562Sjhb		mode = "KCS";
97162562Sjhb		break;
98162562Sjhb	case SMIC_MODE:
99162562Sjhb		count = 3;
100162562Sjhb		mode = "SMIC";
101162562Sjhb		break;
102162562Sjhb	case BT_MODE:
103162562Sjhb		device_printf(dev, "BT interface not supported\n");
104162562Sjhb		return (ENXIO);
105162562Sjhb	case SSIF_MODE:
106162562Sjhb		if (ACPI_FAILURE(acpi_GetInteger(devh, "_ADR", &flags)))
107162562Sjhb			return (ENXIO);
108188078Sjhb		info.address = flags;
109162562Sjhb		device_printf(dev, "SSIF interface not supported on ACPI\n");
110162562Sjhb		return (0);
111162562Sjhb	default:
112162562Sjhb		return (ENXIO);
113162562Sjhb	}
114162562Sjhb
115162562Sjhb	if (bus_get_resource(dev, SYS_RES_IOPORT, 0, NULL, NULL) == 0)
116162562Sjhb		type = SYS_RES_IOPORT;
117162562Sjhb	else if (bus_get_resource(dev, SYS_RES_MEMORY, 0, NULL, NULL) == 0)
118162562Sjhb		type = SYS_RES_MEMORY;
119162562Sjhb	else {
120162562Sjhb		device_printf(dev, "unknown resource type\n");
121162562Sjhb		return (ENXIO);
122162562Sjhb	}
123162562Sjhb
124162562Sjhb	sc->ipmi_io_rid = 0;
125162562Sjhb	sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type,
126162562Sjhb	    &sc->ipmi_io_rid, RF_ACTIVE);
127162562Sjhb	sc->ipmi_io_type = type;
128162562Sjhb	sc->ipmi_io_spacing = 1;
129162562Sjhb	if (sc->ipmi_io_res[0] == NULL) {
130162562Sjhb		device_printf(dev, "couldn't configure I/O resource\n");
131162562Sjhb		return (ENXIO);
132162562Sjhb	}
133162562Sjhb
134162562Sjhb	/* If we have multiple resources, allocate up to MAX_RES. */
135162562Sjhb	for (i = 1; i < MAX_RES; i++) {
136162562Sjhb		sc->ipmi_io_rid = i;
137162562Sjhb		sc->ipmi_io_res[i] = bus_alloc_resource_any(dev, type,
138162562Sjhb		    &sc->ipmi_io_rid, RF_ACTIVE);
139162562Sjhb		if (sc->ipmi_io_res[i] == NULL)
140162562Sjhb			break;
141162562Sjhb	}
142162562Sjhb	sc->ipmi_io_rid = 0;
143162562Sjhb
144162562Sjhb	/* If we have multiple resources, make sure we have enough of them. */
145162562Sjhb	if (sc->ipmi_io_res[1] != NULL && sc->ipmi_io_res[count - 1] == NULL) {
146162562Sjhb		device_printf(dev, "too few I/O resources\n");
147162562Sjhb		error = ENXIO;
148162562Sjhb		goto bad;
149162562Sjhb	}
150162562Sjhb
151162562Sjhb	device_printf(dev, "%s mode found at %s 0x%jx on %s\n",
152162562Sjhb	    mode, type == SYS_RES_IOPORT ? "io" : "mem",
153162562Sjhb	    (uintmax_t)rman_get_start(sc->ipmi_io_res[0]),
154162562Sjhb	    device_get_name(device_get_parent(dev)));
155162562Sjhb
156162562Sjhb	sc->ipmi_dev = dev;
157162562Sjhb
158162562Sjhb	/*
159162562Sjhb	 * Setup an interrupt if we have an interrupt resource.  We
160162562Sjhb	 * don't support GPE interrupts via _GPE yet.
161162562Sjhb	 */
162162562Sjhb	sc->ipmi_irq_rid = 0;
163162562Sjhb	sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
164162562Sjhb	    &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
165162562Sjhb
166162562Sjhb	/* Warn if _GPE exists. */
167162562Sjhb	if (ACPI_SUCCESS(AcpiEvaluateObject(devh, "_GPE", NULL, NULL)))
168162562Sjhb		device_printf(dev, "_GPE support not implemented\n");
169162562Sjhb
170162562Sjhb	/*
171162562Sjhb	 * We assume an alignment of 1 byte as currently the IPMI spec
172162562Sjhb	 * doesn't provide any way to determine the alignment via ACPI.
173162562Sjhb	 */
174162562Sjhb	switch (interface_type) {
175162562Sjhb	case KCS_MODE:
176162562Sjhb		error = ipmi_kcs_attach(sc);
177162562Sjhb		if (error)
178162562Sjhb			goto bad;
179162562Sjhb		break;
180162562Sjhb	case SMIC_MODE:
181162562Sjhb		error = ipmi_smic_attach(sc);
182162562Sjhb		if (error)
183162562Sjhb			goto bad;
184162562Sjhb		break;
185162562Sjhb	}
186162562Sjhb	error = ipmi_attach(dev);
187162562Sjhb	if (error)
188162562Sjhb		goto bad;
189162562Sjhb
190162562Sjhb	return (0);
191162562Sjhbbad:
192162562Sjhb	ipmi_release_resources(dev);
193162562Sjhb	return (error);
194162562Sjhb}
195162562Sjhb
196162562Sjhbstatic device_method_t ipmi_methods[] = {
197162562Sjhb	/* Device interface */
198162562Sjhb	DEVMETHOD(device_probe,		ipmi_acpi_probe),
199162562Sjhb	DEVMETHOD(device_attach,	ipmi_acpi_attach),
200162562Sjhb	DEVMETHOD(device_detach,	ipmi_detach),
201162562Sjhb	{ 0, 0 }
202162562Sjhb};
203162562Sjhb
204162562Sjhbstatic driver_t ipmi_acpi_driver = {
205162562Sjhb	"ipmi",
206162562Sjhb	ipmi_methods,
207162562Sjhb	sizeof(struct ipmi_softc),
208162562Sjhb};
209162562Sjhb
210162562SjhbDRIVER_MODULE(ipmi_acpi, acpi, ipmi_acpi_driver, ipmi_devclass, 0, 0);
211162562SjhbMODULE_DEPEND(ipmi_acpi, acpi, 1, 1, 1);
212