ipmi_pci.c revision 155517
115177Snate/*-
215284Snate * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
315284Snate * All rights reserved.
415284Snate *
515284Snate * Redistribution and use in source and binary forms, with or without
615284Snate * modification, are permitted provided that the following conditions
715284Snate * are met:
815284Snate * 1. Redistributions of source code must retain the above copyright
915284Snate *    notice, this list of conditions and the following disclaimer.
1015284Snate * 2. Redistributions in binary form must reproduce the above copyright
1115284Snate *    notice, this list of conditions and the following disclaimer in the
1215284Snate *    documentation and/or other materials provided with the distribution.
1315284Snate *
1415284Snate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1515284Snate * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1615284Snate * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1715284Snate * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1815284Snate * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1915284Snate * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2015284Snate * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2115284Snate * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2215284Snate * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2315284Snate * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2415284Snate * SUCH DAMAGE.
2510217Sphk */
2630171Scharnier
2730171Scharnier#include <sys/cdefs.h>
2830171Scharnier__FBSDID("$FreeBSD: head/sys/dev/ipmi/ipmi_pci.c 155517 2006-02-10 20:51:35Z ambrisko $");
2935310Snate
3030171Scharnier#include <sys/param.h>
3130171Scharnier#include <sys/malloc.h>
3230171Scharnier#include <sys/systm.h>
3310217Sphk#include <sys/kernel.h>
3410217Sphk#include <sys/module.h>
3510217Sphk#include <sys/selinfo.h>
3630171Scharnier
3710217Sphk#include <sys/bus.h>
3810217Sphk#include <sys/conf.h>
3931290Snate
4010217Sphk#include <machine/bus.h>
4110217Sphk#include <machine/resource.h>
4210217Sphk#include <sys/rman.h>
4310217Sphk
4416487Snate#include <dev/pci/pcireg.h>
4516487Snate#include <dev/pci/pcivar.h>
4616487Snate
4716487Snate#ifdef LOCAL_MODULE
4816487Snate#include <ipmivars.h>
4916487Snate#else
5016487Snate#include <dev/ipmi/ipmivars.h>
5116487Snate#endif
5216487Snate
5316487Snatestatic int ipmi_pci_probe(device_t dev);
5410217Sphkstatic int ipmi_pci_attach(device_t dev);
5516487Snatestatic int ipmi_pci_detach(device_t dev);
5615177Snate
5715177Snatestatic device_method_t ipmi_methods[] = {
5815177Snate	/* Device interface */
5915177Snate	DEVMETHOD(device_probe,     ipmi_pci_probe),
6015177Snate	DEVMETHOD(device_attach,    ipmi_pci_attach),
6115177Snate	DEVMETHOD(device_detach,    ipmi_pci_detach),
6215177Snate	{ 0, 0 }
6315177Snate};
6415177Snate
6515177Snatestruct ipmi_ident
6615177Snate{
6715177Snate	u_int16_t	vendor;
6815177Snate	u_int16_t	device;
6915177Snate	char		*desc;
7015177Snate} ipmi_identifiers[] = {
7115177Snate	{0x1028, 0x000d, "Dell PE2650 SMIC interface"},
7215177Snate	{0, 0, 0}
7315177Snate};
7415177Snate
7515177Snatestatic int
7615177Snateipmi_pci_probe(device_t dev) {
7715177Snate	struct ipmi_ident *m;
7815177Snate
7915177Snate	if (ipmi_attached)
8015177Snate		return ENXIO;
8115177Snate
8215177Snate	for (m = ipmi_identifiers; m->vendor != 0; m++) {
8315177Snate		if ((m->vendor == pci_get_vendor(dev)) &&
8415177Snate		    (m->device == pci_get_device(dev))) {
8515177Snate			device_set_desc(dev, m->desc);
8615177Snate			return (BUS_PROBE_DEFAULT);
8715177Snate		}
8810217Sphk	}
8910217Sphk
9010217Sphk	return ENXIO;
9110217Sphk}
9210217Sphk
9310217Sphkstatic int
9410217Sphkipmi_pci_attach(device_t dev) {
9515177Snate	struct ipmi_softc *sc = device_get_softc(dev);
9615177Snate	device_t parent, smbios_attach_dev = NULL;
9715177Snate	devclass_t dc;
9810217Sphk	int status, flags;
9910217Sphk	int error;
10010217Sphk
10110217Sphk
10215177Snate	/*
10310217Sphk	 * We need to attach to something that can address the BIOS/
10410217Sphk	 * SMBIOS memory range.  This is usually the isa bus however
10515177Snate	 * during a static kernel boot the isa bus is not available
10610217Sphk	 * so we run up the tree to the nexus bus.  A module load
10715177Snate	 * will use the isa bus attachment.  If neither work bail
10815177Snate	 * since the SMBIOS defines stuff we need to know to attach to
10910217Sphk	 * this device.
11015177Snate	 */
11115177Snate	dc = devclass_find("isa");
11215177Snate	if (dc != NULL) {
11315177Snate		smbios_attach_dev = devclass_get_device(dc, 0);
11415177Snate	}
11515177Snate
11615177Snate	if (smbios_attach_dev == NULL) {
11715177Snate		smbios_attach_dev = dev;
11815177Snate		for (;;) {
11915177Snate			parent = device_get_parent(smbios_attach_dev);
12015177Snate			if (parent == NULL)
12115177Snate				break;
12215177Snate			if (strcmp(device_get_name(smbios_attach_dev),
12315177Snate			    "nexus") == 0)
12415177Snate				break;
12515177Snate			smbios_attach_dev = parent;
12610217Sphk		}
12710217Sphk	}
12815177Snate
12910217Sphk	if (smbios_attach_dev == NULL) {
13015177Snate		device_printf(dev, "Couldn't find isa/nexus device\n");
13110217Sphk		goto bad;
13210217Sphk	}
13310217Sphk	sc->ipmi_smbios_dev = ipmi_smbios_identify(NULL, smbios_attach_dev);
13410217Sphk	if (sc->ipmi_smbios_dev == NULL) {
13510217Sphk		device_printf(dev, "Couldn't find isa device\n");
13610217Sphk		goto bad;
13715177Snate	}
13815177Snate	error = ipmi_smbios_probe(sc->ipmi_smbios_dev);
13915177Snate	if (error != 0) {
14015177Snate		goto bad;
14115177Snate	}
14210217Sphk	sc->ipmi_dev = dev;
14315177Snate	error = ipmi_smbios_query(dev);
14410217Sphk	device_delete_child(dev, sc->ipmi_smbios_dev);
14515177Snate	if (error != 0)
14610217Sphk		goto bad;
14710217Sphk
14810217Sphk	/* Now we know about the IPMI attachment info. */
14910217Sphk	if (sc->ipmi_bios_info.kcs_mode) {
15015177Snate		if (sc->ipmi_bios_info.io_mode)
15110217Sphk			device_printf(dev, "KCS mode found at io 0x%llx "
15215177Snate			    "alignment 0x%x on %s\n",
15310217Sphk			    (long long)sc->ipmi_bios_info.address,
15415177Snate			    sc->ipmi_bios_info.offset,
15510217Sphk			    device_get_name(device_get_parent(sc->ipmi_dev)));
15610217Sphk		else
15715177Snate			device_printf(dev, "KCS mode found at mem 0x%llx "
15815177Snate			    "alignment 0x%x on %s\n",
15910217Sphk			    (long long)sc->ipmi_bios_info.address,
16010217Sphk			    sc->ipmi_bios_info.offset,
16115177Snate			    device_get_name(device_get_parent(sc->ipmi_dev)));
16210217Sphk
16315177Snate		sc->ipmi_kcs_status_reg   = sc->ipmi_bios_info.offset;
16410217Sphk		sc->ipmi_kcs_command_reg  = sc->ipmi_bios_info.offset;
16510217Sphk		sc->ipmi_kcs_data_out_reg = 0;
16615177Snate		sc->ipmi_kcs_data_in_reg  = 0;
16710217Sphk
16810217Sphk		if (sc->ipmi_bios_info.io_mode) {
16910217Sphk			sc->ipmi_io_rid = PCIR_BAR(0);
17016487Snate			sc->ipmi_io_res = bus_alloc_resource(dev,
17110217Sphk			    SYS_RES_IOPORT, &sc->ipmi_io_rid,
17210217Sphk			    sc->ipmi_bios_info.address,
17334699Shosokawa			    sc->ipmi_bios_info.address +
17410217Sphk				(sc->ipmi_bios_info.offset * 2),
17510217Sphk			    sc->ipmi_bios_info.offset * 2,
17634699Shosokawa			    RF_ACTIVE);
17716466Snate		} else {
17815177Snate			sc->ipmi_mem_rid = PCIR_BAR(0);
17934699Shosokawa			sc->ipmi_mem_res = bus_alloc_resource(dev,
18016466Snate			    SYS_RES_MEMORY, &sc->ipmi_mem_rid,
18115177Snate			    sc->ipmi_bios_info.address,
18234699Shosokawa			    sc->ipmi_bios_info.address +
18316466Snate			        (sc->ipmi_bios_info.offset * 2),
18415177Snate			    sc->ipmi_bios_info.offset * 2,
18534699Shosokawa			    RF_ACTIVE);
18616466Snate		}
18710217Sphk
18815177Snate		if (!sc->ipmi_io_res){
18910217Sphk			device_printf(dev, "couldn't configure pci io res\n");
19010217Sphk			goto bad;
19110217Sphk		}
19216487Snate
19310255Sphk		status = INB(sc, sc->ipmi_kcs_status_reg);
19410217Sphk		if (status == 0xff) {
19515177Snate			device_printf(dev, "couldn't find it\n");
19610217Sphk			goto bad;
19710217Sphk		}
19810217Sphk		if(status & KCS_STATUS_OBF){
19910217Sphk			ipmi_read(dev, NULL, 0);
20010217Sphk		}
20115177Snate	} else if (sc->ipmi_bios_info.smic_mode) {
20210217Sphk		if (sc->ipmi_bios_info.io_mode)
20310217Sphk			device_printf(dev, "SMIC mode found at io 0x%llx "
20415177Snate			    "alignment 0x%x on %s\n",
20510217Sphk			    (long long)sc->ipmi_bios_info.address,
20610217Sphk			    sc->ipmi_bios_info.offset,
20715177Snate			    device_get_name(device_get_parent(sc->ipmi_dev)));
20810217Sphk		else
20915177Snate			device_printf(dev, "SMIC mode found at mem 0x%llx "
21010217Sphk			    "alignment 0x%x on %s\n",
21110217Sphk			    (long long)sc->ipmi_bios_info.address,
21210217Sphk			    sc->ipmi_bios_info.offset,
21316487Snate			    device_get_name(device_get_parent(sc->ipmi_dev)));
21410255Sphk
21510217Sphk		sc->ipmi_smic_data    = 0;
21615177Snate		sc->ipmi_smic_ctl_sts = sc->ipmi_bios_info.offset;
21715177Snate		sc->ipmi_smic_flags   = sc->ipmi_bios_info.offset * 2;
21815177Snate
21915177Snate		if (sc->ipmi_bios_info.io_mode) {
22015177Snate			sc->ipmi_io_rid = PCIR_BAR(0);
22115177Snate			sc->ipmi_io_res = bus_alloc_resource(dev,
22210217Sphk			    SYS_RES_IOPORT, &sc->ipmi_io_rid,
22310217Sphk			    sc->ipmi_bios_info.address,
22410217Sphk			    sc->ipmi_bios_info.address +
22510217Sphk				(sc->ipmi_bios_info.offset * 3),
22615177Snate			    sc->ipmi_bios_info.offset * 3,
22710217Sphk			    RF_ACTIVE);
22810217Sphk		} else {
22910217Sphk			sc->ipmi_mem_rid = PCIR_BAR(0);
23010217Sphk			sc->ipmi_mem_res = bus_alloc_resource(dev,
23115177Snate			    SYS_RES_MEMORY, &sc->ipmi_mem_rid,
23210217Sphk			    sc->ipmi_bios_info.address,
23310217Sphk			    sc->ipmi_bios_info.address +
23410217Sphk			        (sc->ipmi_bios_info.offset * 2),
23516487Snate			    sc->ipmi_bios_info.offset * 2,
23610255Sphk			    RF_ACTIVE);
23710217Sphk		}
23815177Snate
23915177Snate		if (!sc->ipmi_io_res && !sc->ipmi_mem_res){
24015177Snate			device_printf(dev, "couldn't configure pci res\n");
24115177Snate			goto bad;
24215177Snate		}
24315177Snate
24415177Snate		flags = INB(sc, sc->ipmi_smic_flags);
24515177Snate		if (flags == 0xff) {
24615177Snate			device_printf(dev, "couldn't find it\n");
24715177Snate			goto bad;
24810217Sphk		}
24910217Sphk		if ((flags & SMIC_STATUS_SMS_ATN)
25015177Snate		    && (flags & SMIC_STATUS_RX_RDY)){
25110217Sphk			ipmi_read(dev, NULL, 0);
25210217Sphk		}
25310217Sphk	} else {
25415177Snate		device_printf(dev, "No IPMI interface found\n");
25510217Sphk		goto bad;
25610217Sphk	}
25710217Sphk	ipmi_attach(dev);
25810217Sphk
25910217Sphk	sc->ipmi_irq_rid = 0;
26010217Sphk        sc->ipmi_irq_res = bus_alloc_resource_any(sc->ipmi_dev, SYS_RES_IRQ,
26110217Sphk	    &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
26215177Snate	if (sc->ipmi_irq_res == NULL) {
26310217Sphk                device_printf(sc->ipmi_dev, "can't allocate interrupt\n");
26410217Sphk        } else {
26510217Sphk		if (bus_setup_intr(sc->ipmi_dev, sc->ipmi_irq_res,
26610217Sphk				   INTR_TYPE_MISC, ipmi_intr,
26710217Sphk				   sc->ipmi_dev, &sc->ipmi_irq)) {
26815177Snate			device_printf(sc->ipmi_dev,
26915177Snate			    "can't set up interrupt\n");
27015177Snate			return (EINVAL);
27110217Sphk		}
27210217Sphk	}
27310217Sphk
27410217Sphk	return 0;
27510217Sphkbad:
27610217Sphk	return ENXIO;
27710217Sphk}
27810217Sphk
27915177Snatestatic int ipmi_pci_detach(device_t dev) {
28015177Snate	struct ipmi_softc *sc;
28110217Sphk
28210217Sphk	sc = device_get_softc(dev);
28315177Snate	ipmi_detach(dev);
28410217Sphk	if (sc->ipmi_ev_tag)
28510217Sphk		EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_ev_tag);
28615177Snate
28710217Sphk	if (sc->ipmi_mem_res)
28810217Sphk		bus_release_resource(dev, SYS_RES_MEMORY, sc->ipmi_mem_rid,
28910217Sphk		    sc->ipmi_mem_res);
29015177Snate	if (sc->ipmi_io_res)
29110217Sphk		bus_release_resource(dev, SYS_RES_IOPORT, sc->ipmi_io_rid,
29210217Sphk		    sc->ipmi_io_res);
29310217Sphk	if (sc->ipmi_irq)
29410217Sphk		bus_teardown_intr(sc->ipmi_dev, sc->ipmi_irq_res,
29510217Sphk		    sc->ipmi_irq);
29610217Sphk	if (sc->ipmi_irq_res)
29710217Sphk		bus_release_resource(sc->ipmi_dev, SYS_RES_IRQ,
29815177Snate		    sc->ipmi_irq_rid, sc->ipmi_irq_res);
29910217Sphk
30010217Sphk	return 0;
30110217Sphk}
30210217Sphk
30310217Sphkstatic driver_t ipmi_pci_driver = {
30410217Sphk        "ipmi",
30510217Sphk        ipmi_methods,
30610217Sphk        sizeof(struct ipmi_softc)
30710217Sphk};
30810217Sphk
30910217SphkDRIVER_MODULE(ipmi_foo, pci, ipmi_pci_driver, ipmi_devclass, 0, 0);
31010217Sphk