code:
/*	$OpenBSD: esm.c,v 1.57 2014/12/10 12:27:56 mikeb Exp $ */

/*
 * Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org>
 * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/timeout.h>
#include <sys/queue.h>
#include <sys/sensors.h>

#include <dev/isa/isareg.h>
#include <machine/bus.h>
#include <machine/intr.h>

#include <arch/i386/i386/esmvar.h>
#include <arch/i386/i386/esmreg.h>
#include <arch/i386/isa/isa_machdep.h>

#ifdef ESM_DEBUG
#define DPRINTF(x...)		do { if (esmdebug) printf(x); } while (0)
#define DPRINTFN(n,x...)	do { if (esmdebug > (n)) printf(x); } while (0)
int	esmdebug = 3;
#else
#define DPRINTF(x...)		/* x */
#define DPRINTFN(n,x...)	/* n: x */
#endif

int		esm_match(struct device *, void *, void *);
void		esm_attach(struct device *, struct device *, void *);
int		esm_activate(struct device *, int);

enum esm_sensor_type {
	ESM_S_UNKNOWN, /* XXX */
	ESM_S_INTRUSION,
	ESM_S_TEMP,
	ESM_S_FANRPM,
	ESM_S_VOLTS,
	ESM_S_VOLTSx10,
	ESM_S_AMPS,
	ESM_S_PWRSUP,
	ESM_S_PCISLOT,
	ESM_S_SCSICONN,
	ESM_S_DRIVES, /* argument is the base index of the drive */
	ESM_S_DRIVE,
	ESM_S_HPSLOT,
	ESM_S_ACSWITCH
};

/*
 * map esm sensor types to kernel sensor types.
 * keep this in sync with the esm_sensor_type enum above.
 */
enum sensor_type esm_typemap[] = {
	SENSOR_INTEGER,
	SENSOR_INDICATOR,
	SENSOR_TEMP,
	SENSOR_FANRPM,
	SENSOR_VOLTS_DC,
	SENSOR_VOLTS_DC,
	SENSOR_AMPS,
	SENSOR_INDICATOR,
	SENSOR_INTEGER,
	SENSOR_INDICATOR,
	SENSOR_DRIVE,
	SENSOR_DRIVE,
	SENSOR_INTEGER,
	SENSOR_INDICATOR
};

struct esm_sensor_map {
	enum esm_sensor_type	type;
	int			arg;
	const char		*name;
};

struct esm_sensor {
	u_int8_t		es_dev;
	u_int8_t		es_id;

	enum esm_sensor_type	es_type;

	struct {
		u_int16_t		th_lo_crit;
		u_int16_t		th_lo_warn;
		u_int16_t		th_hi_warn;
		u_int16_t		th_hi_crit;
	}			es_thresholds;

	struct ksensor		*es_sensor;
	TAILQ_ENTRY(esm_sensor)	es_entry;
};

struct esm_softc {
	struct device		sc_dev;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;

	TAILQ_HEAD(, esm_sensor) sc_sensors;
	struct esm_sensor	*sc_nextsensor;
	struct ksensordev	sc_sensordev;
	int			sc_retries;
	volatile int		sc_step;
	struct timeout		sc_timeout;

	int			sc_wdog_period;
	volatile int		sc_wdog_tickle;
};

struct cfattach esm_ca = {
	sizeof(struct esm_softc), esm_match, esm_attach,
	NULL, esm_activate
};

struct cfdriver esm_cd = {
	NULL, "esm", DV_DULL
};

#define DEVNAME(s)	((s)->sc_dev.dv_xname)

#define EREAD(s, r)	bus_space_read_1((s)->sc_iot, (s)->sc_ioh, (r))
#define EWRITE(s, r, v)	bus_space_write_1((s)->sc_iot, (s)->sc_ioh, (r), (v))

#define ECTRLWR(s, v)	EWRITE((s), ESM2_CTRL_REG, (v))
#define EDATARD(s)	EREAD((s), ESM2_DATA_REG)
#define EDATAWR(s, v)	EWRITE((s), ESM2_DATA_REG, (v))

int		esm_watchdog(void *, int);

void		esm_refresh(void *);

int		esm_get_devmap(struct esm_softc *, int, struct esm_devmap *);
void		esm_devmap(struct esm_softc *, struct esm_devmap *);
void		esm_make_sensors(struct esm_softc *, struct esm_devmap *,
		    struct esm_sensor_map *, int);
int		esm_thresholds(struct esm_softc *, struct esm_devmap *,
		    struct esm_sensor *);

int		esm_bmc_ready(struct esm_softc *, int, u_int8_t, u_int8_t, int);
int		esm_cmd(struct esm_softc *, void *, size_t, void *, size_t,
		    int, int);
int		esm_smb_cmd(struct esm_softc *, struct esm_smb_req *,
		    struct esm_smb_resp *, int, int);

int64_t		esm_val2temp(u_int16_t);
int64_t		esm_val2volts(u_int16_t);
int64_t		esm_val2amps(u_int16_t);

/* Determine if this is a Dell server */
int
esm_probe(void *aux)
{
	const char *pdellstr;
	struct dell_sysid *pdellid;
	uint16_t sysid;

	pdellstr = (const char *)ISA_HOLE_VADDR(DELL_SYSSTR_ADDR);
	DPRINTF("Dell String: %s\n", pdellstr);
	if (strncmp(pdellstr, "Dell System", 11))
		return (0);

	pdellid = (struct dell_sysid *)ISA_HOLE_VADDR(DELL_SYSID_ADDR);
	if ((sysid = pdellid->sys_id) == DELL_SYSID_EXT)
		sysid = pdellid->ext_id;
	DPRINTF("SysId: %x\n", sysid);

	switch (sysid) {
	case DELL_SYSID_2300:
	case DELL_SYSID_4300:
	case DELL_SYSID_4350:
	case DELL_SYSID_6300:
	case DELL_SYSID_6350:
	case DELL_SYSID_2400:
	case DELL_SYSID_2450:
	case DELL_SYSID_4400:
	case DELL_SYSID_6400:
	case DELL_SYSID_6450:
	case DELL_SYSID_2500:
	case DELL_SYSID_2550:
	case DELL_SYSID_PV530F:
	case DELL_SYSID_PV735N:
	case DELL_SYSID_PV750N:
	case DELL_SYSID_PV755N:
	case DELL_SYSID_PA200:
		return (1);
	}

	return (0);
}

int
esm_match(struct device *parent, void *match, void *aux)
{
	struct esm_attach_args		*eaa = aux;

	if (strcmp(eaa->eaa_name, esm_cd.cd_name) == 0 && esm_probe(eaa))
		return (1);

	return (0);
}

void
esm_attach(struct device *parent, struct device *self, void *aux)
{
	struct esm_softc		*sc = (struct esm_softc *)self;
	struct esm_attach_args		*eaa = aux;
	u_int8_t			x;

	struct esm_devmap		devmap;
	int				i;

	sc->sc_iot = eaa->eaa_iot;
	TAILQ_INIT(&sc->sc_sensors);

	if (bus_space_map(sc->sc_iot, ESM2_BASE_PORT, 8, 0, &sc->sc_ioh) != 0) {
		printf(": can't map mem space\n");
		return;
	}

	/* turn off interrupts here */
	x = EREAD(sc, ESM2_INTMASK_REG);
	x &= ~(ESM2_TIM_SCI_EN|ESM2_TIM_SMI_EN|ESM2_TIM_NMI2SMI);
	x |= ESM2_TIM_POWER_UP_BITS;
	EWRITE(sc, ESM2_INTMASK_REG, x);

	/* clear event doorbells */
	x = EREAD(sc, ESM2_CTRL_REG);
	x &= ~ESM2_TC_HOSTBUSY;
	x |= ESM2_TC_POWER_UP_BITS;
	EWRITE(sc, ESM2_CTRL_REG, x);

	/* see if card is alive */
	if (esm_bmc_ready(sc, ESM2_CTRL_REG, ESM2_TC_ECBUSY, 0, 1) != 0) {
		printf(": card is not alive\n");
		bus_space_unmap(sc->sc_iot, sc->sc_ioh, 8);
		return;
	}

	sc->sc_wdog_period = 0;
	wdog_register(esm_watchdog, sc);
	printf("\n");

	strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
	    sizeof(sc->sc_sensordev.xname));
	for (i = 0; i <= 0xff; i++) {
		if (esm_get_devmap(sc, i, &devmap) != 0)
			break; /* XXX not continue? */
		esm_devmap(sc, &devmap);
	}

	if (!TAILQ_EMPTY(&sc->sc_sensors)) {
		sensordev_install(&sc->sc_sensordev);
		DPRINTF("%s: starting refresh\n", DEVNAME(sc));
		sc->sc_nextsensor = TAILQ_FIRST(&sc->sc_sensors);
		sc->sc_retries = 0;
		timeout_set(&sc->sc_timeout, esm_refresh, sc);
		timeout_add_sec(&sc->sc_timeout, 1);
	}
}

int
esm_activate(struct device *self, int act)
{
	switch (act) {
	case DVACT_POWERDOWN:
		wdog_shutdown(self);
		break;
	}

	return (0);
}

int
esm_watchdog(void *arg, int period)
{
	struct esm_softc	*sc = arg;
	struct esm_wdog_prop	prop;
	struct esm_wdog_state	state;
	int			s;

	if (sc->sc_wdog_period == period) {
		if (period != 0) {
			s = splclock();
			if (sc->sc_step != 0) {
				/* defer tickling to the sensor refresh */
				sc->sc_wdog_tickle = 1;
			} else {
				/* tickle the watchdog */
				EWRITE(sc, ESM2_CTRL_REG, ESM2_TC_HBDB);
			}
			splx(s);
		}
		return (period);
	}

	/* we're changing the watchdog period */

	memset(&prop, 0, sizeof(prop));
	memset(&state, 0, sizeof(state));

	if (period < 10 && period > 0)
		period = 10;

	s = splclock();

	prop.cmd = ESM2_CMD_HWDC;
	prop.subcmd = ESM2_HWDC_WRITE_PROPERTY;
	prop.action = (period == 0) ? ESM_WDOG_DISABLE : ESM_WDOG_RESET;
	prop.time = period;

	/*
	 * if we're doing a refresh, we need to wait till the hardware is
	 * available again. since period changes only happen via sysctl we
	 * should have a process context we can sleep in.
	 */
	while (sc->sc_step != 0) {
		if (tsleep(sc, PUSER | PCATCH, "esm", 0) == EINTR) {
			splx(s);
			return (sc->sc_wdog_period);
		}
	}

	if (esm_cmd(sc, &prop, sizeof(prop), NULL, 0, 1, 0) != 0) {
		splx(s);
		return (sc->sc_wdog_period);
	}

	state.cmd = ESM2_CMD_HWDC;
	state.subcmd = ESM2_HWDC_WRITE_STATE;
	state.state = (period == 0) ? 0 : 1;

	/* we have the hw, this can't (shouldn't) fail */
	esm_cmd(sc, &state, sizeof(state), NULL, 0, 1, 0);

	splx(s);

	sc->sc_wdog_period = period;
	return (period);
}

void
esm_refresh(void *arg)
{
	struct esm_softc	*sc = arg;
	struct esm_sensor	*es = sc->sc_nextsensor;
	struct esm_smb_req	req;
	struct esm_smb_resp	resp;
	struct esm_smb_resp_val	*val = &resp.resp_val;
	int			nsensors, i, step;

	memset(&req, 0, sizeof(req));
	req.h_cmd = ESM2_CMD_SMB_XMIT_RECV;
	req.h_dev = es->es_dev;
	req.h_txlen = sizeof(req.req_val);
	req.h_rxlen = sizeof(resp.resp_val);
	req.req_val.v_cmd = ESM2_SMB_SENSOR_VALUE;
	req.req_val.v_sensor = es->es_id;

	switch (es->es_type) {
	case ESM_S_DRIVES:
		nsensors = 4;
		break;
	case ESM_S_PWRSUP:
		nsensors = 6;
		break;
	default:
		nsensors = 1;
		break;
	}

	if ((step = esm_smb_cmd(sc, &req, &resp, 0, sc->sc_step)) != 0) {
		sc->sc_step = step;
		if (++sc->sc_retries < 10)
			goto tick;

		for (i = 0; i < nsensors; i++)
			es->es_sensor[i].flags |= SENSOR_FINVALID;
	} else {
		switch (es->es_type) {
		case ESM_S_TEMP:
			es->es_sensor->value = esm_val2temp(val->v_reading);
			break;
		case ESM_S_VOLTS:
			es->es_sensor->value = esm_val2volts(val->v_reading);
			break;
		case ESM_S_VOLTSx10:
			es->es_sensor->value =
			    esm_val2volts(val->v_reading) * 10;
			break;
		case ESM_S_AMPS:
			es->es_sensor->value = esm_val2amps(val->v_reading);
			break;
		case ESM_S_DRIVES:
			for (i = 0; i < nsensors; i++) {
				es->es_sensor[i].value =
				    (val->v_reading >> i * 8) & 0xf;
			}
			break;
		case ESM_S_PWRSUP:
			for (i = 0; i < nsensors; i++) {
				es->es_sensor[i].value =
				    (val->v_reading >> i) & 0x1;
			}
			break;
		default:
			es->es_sensor->value = val->v_reading;
			break;
		}

		switch (es->es_type) {
		case ESM_S_TEMP:
		case ESM_S_FANRPM:
		case ESM_S_VOLTS:
		case ESM_S_AMPS:
			if (val->v_reading >= es->es_thresholds.th_hi_crit ||
			    val->v_reading <= es->es_thresholds.th_lo_crit) {
				es->es_sensor->status = SENSOR_S_CRIT;
				break;
			}

			if (val->v_reading >= es->es_thresholds.th_hi_warn ||
			    val->v_reading <= es->es_thresholds.th_lo_warn) {
				es->es_sensor->status = SENSOR_S_WARN;
				break;
			}

			es->es_sensor->status = SENSOR_S_OK;
			break;

		case ESM_S_PWRSUP:
			if (val->v_status & ESM2_VS_PSU_FAIL) {
				es->es_sensor[3].status = SENSOR_S_CRIT;
				break;
			}

			es->es_sensor[3].status = SENSOR_S_OK;
			break;

		default:
			break;
		}

		for (i = 0; i < nsensors; i++)
			es->es_sensor->flags &= ~SENSOR_FINVALID;
	}

	sc->sc_nextsensor = TAILQ_NEXT(es, es_entry);
	sc->sc_retries = 0;
	sc->sc_step = 0;

	if (sc->sc_wdog_tickle) {
		/*
		 * the controller was busy in a refresh when the watchdog
		 * needed a tickle, so do it now.
		 */
		EWRITE(sc, ESM2_CTRL_REG, ESM2_TC_HBDB);
		sc->sc_wdog_tickle = 0;
	}
	wakeup(sc);

	if (sc->sc_nextsensor == NULL) {
		sc->sc_nextsensor = TAILQ_FIRST(&sc->sc_sensors);
		timeout_add_sec(&sc->sc_timeout, 10);
		return;
	}
tick:
	timeout_add_msec(&sc->sc_timeout, 50);
}

int
esm_get_devmap(struct esm_softc *sc, int dev, struct esm_devmap *devmap)
{
	struct esm_devmap_req	req;
	struct esm_devmap_resp	resp;
#ifdef ESM_DEBUG
	int			i;
#endif

	memset(&req, 0, sizeof(req));
	memset(&resp, 0, sizeof(resp));

	req.cmd = ESM2_CMD_DEVICEMAP;
	req.action = ESM2_DEVICEMAP_READ;
	req.index = dev;
	req.ndev = 1;

	if (esm_cmd(sc, &req, sizeof(req), &resp, sizeof(resp), 1, 0) != 0)
		return (1);

	if (resp.status != 0)
		return (1);

	memcpy(devmap, &resp.devmap[0], sizeof(struct esm_devmap));

#ifdef ESM_DEBUG
	if (esmdebug > 5) {
		printf("\n");
		printf("Device Map(%d) returns:\n", dev);
		printf("  status: %.2x\n", resp.status);
		printf("  #devs : %.2x\n", resp.ndev);
		printf("   index: %.2x\n", resp.devmap[0].index);
		printf("   Type : %.2x.%.2x\n", resp.devmap[0].dev_major,
		    resp.devmap[0].dev_minor);
		printf("   Rev  : %.2x.%.2x\n", resp.devmap[0].rev_major,
		    resp.devmap[0].rev_minor);
		printf("   ROM  : %.2x\n", resp.devmap[0].rev_rom);
		printf("   SMB  : %.2x\n", resp.devmap[0].smb_addr);
		printf("   Stat : %.2x\n", resp.devmap[0].status);
		printf("   MonTy: %.2x\n", resp.devmap[0].monitor_type);
		printf("   Poll : %.2x\n", resp.devmap[0].pollcycle);
		printf("   UUID : ");
		for (i = 0; i < ESM2_UUID_LEN; i++) {
			printf("%02x", resp.devmap[0].uniqueid[i]);
		}
		printf("\n");
	}
#endif /* ESM_DEBUG */

	return (0);
}

struct esm_sensor_map esm_sensors_esm2[] = {
	{ ESM_S_UNKNOWN,	0,		"Motherboard" },
	{ ESM_S_TEMP,		0,		"CPU 1" },
	{ ESM_S_TEMP,		0,		"CPU 2" },
	{ ESM_S_TEMP,		0,		"CPU 3" },
	{ ESM_S_TEMP,		0,		"CPU 4" },

	{ ESM_S_TEMP,		0,		"Mainboard" },
	{ ESM_S_TEMP,		0,		"Ambient" },
	{ ESM_S_VOLTS,		0,		"CPU 1 Core" },
	{ ESM_S_VOLTS,		0,		"CPU 2 Core" },
	{ ESM_S_VOLTS,		0,		"CPU 3 Core" },

	{ ESM_S_VOLTS,		0,		"CPU 4 Core" },
	{ ESM_S_VOLTS,		0,		"Motherboard +5V" },
	{ ESM_S_VOLTS,		0,		"Motherboard +12V" },
	{ ESM_S_VOLTS,		0,		"Motherboard +3.3V" },
	{ ESM_S_VOLTS,		0,		"Motherboard +2.5V" },

	{ ESM_S_VOLTS,		0,		"Motherboard GTL Term" },
	{ ESM_S_VOLTS,		0,		"Motherboard Battery" },
	{ ESM_S_INTRUSION,	0,		"Chassis Intrusion", },
	{ ESM_S_UNKNOWN,	0,		"Chassis Fan Ctrl", },
	{ ESM_S_FANRPM,		0,		"Fan 1" },

	{ ESM_S_FANRPM,		0,		"Fan 2" }, /* 20 */
	{ ESM_S_FANRPM,		0,		"Fan 3" },
	{ ESM_S_FANRPM,		0,		"Power Supply Fan" },
	{ ESM_S_VOLTS,		0,		"CPU 1 cache" },
	{ ESM_S_VOLTS,		0,		"CPU 2 cache" },

	{ ESM_S_VOLTS,		0,		"CPU 3 cache" },
	{ ESM_S_VOLTS,		0,		"CPU 4 cache" },
	{ ESM_S_UNKNOWN,	0,		"Power Ctrl" },
	{ ESM_S_PWRSUP,		0,		"Power Supply 1" },
	{ ESM_S_PWRSUP,		0,		"Power Supply 2" },

	{ ESM_S_VOLTS,		0,		"Mainboard +1.5V" }, /* 30 */
	{ ESM_S_VOLTS,		0,		"Motherboard +2.8V" },
	{ ESM_S_UNKNOWN,	0,		"HotPlug Status" },
	{ ESM_S_PCISLOT,	0,		"PCI Slot 1" },
	{ ESM_S_PCISLOT,	0,		"PCI Slot 2" },

	{ ESM_S_PCISLOT,	0,		"PCI Slot 3" },
	{ ESM_S_PCISLOT,	0,		"PCI Slot 4" },
	{ ESM_S_PCISLOT,	0,		"PCI Slot 5" },
	{ ESM_S_PCISLOT,	0,		"PCI Slot 6" },
	{ ESM_S_PCISLOT,	0,		"PCI Slot 7" },

	{ ESM_S_VOLTS,		0,		"CPU 1 Cartridge" }, /* 40 */
	{ ESM_S_VOLTS,		0,		"CPU 2 Cartridge" },
	{ ESM_S_VOLTS,		0,		"CPU 3 Cartridge" },
	{ ESM_S_VOLTS,		0,		"CPU 4 Cartridge" },
	{ ESM_S_VOLTS,		0,		"Gigabit NIC +1.8V" },

	{ ESM_S_VOLTS,		0,		"Gigabit NIC +2.5V" },
	{ ESM_S_VOLTS,		0,		"Memory +3.3V" },
	{ ESM_S_VOLTS,		0,		"Video +2.5V" },
	{ ESM_S_PWRSUP,		0,		"Power Supply 3" },
	{ ESM_S_FANRPM,		0,		"Fan 4" },

	{ ESM_S_FANRPM,		0,		"Power Supply Fan" }, /* 50 */
	{ ESM_S_FANRPM,		0,		"Power Supply Fan" },
	{ ESM_S_FANRPM,		0,		"Power Supply Fan" },
	{ ESM_S_ACSWITCH,	0,		"A/C Power Switch" },
	{ ESM_S_UNKNOWN,	0,		"PS Over Temp" }
};

struct esm_sensor_map esm_sensors_backplane[] = {
	{ ESM_S_UNKNOWN,	0,		"Backplane" },
	{ ESM_S_UNKNOWN,	0,		"Backplane Control" },
	{ ESM_S_TEMP,		0,		"Backplane Top" },
	{ ESM_S_TEMP,		0,		"Backplane Bottom" },
	{ ESM_S_TEMP,		0,		"Backplane Control Panel" },
	{ ESM_S_VOLTS,		0,		"Backplane Battery" },
	{ ESM_S_VOLTS,		0,		"Backplane +5V" },
	{ ESM_S_VOLTS,		0,		"Backplane +12V" },
	{ ESM_S_VOLTS,		0,		"Backplane Board" },
	{ ESM_S_INTRUSION,	0,		"Backplane Intrusion" },
	{ ESM_S_UNKNOWN,	0,		"Backplane Fan Control" },
	{ ESM_S_FANRPM,		0,		"Backplane Fan 1" },
	{ ESM_S_FANRPM,		0,		"Backplane Fan 2" },
	{ ESM_S_FANRPM,		0,		"Backplane Fan 3" },
	{ ESM_S_SCSICONN,	0,		"Backplane SCSI A Connected" },
	{ ESM_S_VOLTS,		0,		"Backplane SCSI A External" },
	{ ESM_S_VOLTS,		0,		"Backplane SCSI A Internal" },
	{ ESM_S_SCSICONN,	0,		"Backplane SCSI B Connected" },
	{ ESM_S_VOLTS,		0,		"Backplane SCSI B External" },
	{ ESM_S_VOLTS,		0,		"Backplane SCSI B Internal" },
	{ ESM_S_DRIVES,		0,		"Drive" },
	{ ESM_S_DRIVES,		4,		"Drive" },
	{ ESM_S_DRIVE,		0,		"Drive 0" },
	{ ESM_S_DRIVE,		0,		"Drive 1" },
	{ ESM_S_DRIVE,		0,		"Drive 2" },
	{ ESM_S_DRIVE,		0,		"Drive 3" },
	{ ESM_S_DRIVE,		0,		"Drive 4" },
	{ ESM_S_DRIVE,		0,		"Drive 5" },
	{ ESM_S_DRIVE,		0,		"Drive 6" },
	{ ESM_S_DRIVE,		0,		"Drive 7" },
	{ ESM_S_UNKNOWN,	0,		"Backplane Control 2" },
	{ ESM_S_VOLTS,		0,		"Backplane +3.3V" },
};

struct esm_sensor_map esm_sensors_powerunit[] = {
	{ ESM_S_UNKNOWN,	0,		"Power Unit" },
	{ ESM_S_VOLTSx10,	0,		"Power Supply 1 +5V" },
	{ ESM_S_VOLTSx10,	0,		"Power Supply 1 +12V" },
	{ ESM_S_VOLTSx10,	0,		"Power Supply 1 +3.3V" },
	{ ESM_S_VOLTSx10,	0,		"Power Supply 1 -5V" },

	{ ESM_S_VOLTSx10,	0,		"Power Supply 1 -12V" },
	{ ESM_S_VOLTSx10,	0,		"Power Supply 2 +5V" },
	{ ESM_S_VOLTSx10,	0,		"Power Supply 2 +12V" },
	{ ESM_S_VOLTSx10,	0,		"Power Supply 2 +3.3V" },
	{ ESM_S_VOLTSx10,	0,		"Power Supply 2 -5V" },

	{ ESM_S_VOLTSx10,	0,		"Power Supply 2 -12V" },
	{ ESM_S_VOLTSx10,	0,		"Power Supply 3 +5V" },
	{ ESM_S_VOLTSx10,	0,		"Power Supply 3 +12V" },
	{ ESM_S_VOLTSx10,	0,		"Power Supply 3 +3.3V" },
	{ ESM_S_VOLTSx10,	0,		"Power Supply 3 -5V" },

	{ ESM_S_VOLTSx10,	0,		"Power Supply 3 -12V" },
	{ ESM_S_VOLTSx10,	0,		"System Power Supply +5V" },
	{ ESM_S_VOLTSx10,	0,		"System Power Supply +12V" },
	{ ESM_S_VOLTSx10,	0,		"System Power Supply +3.3V" },
	{ ESM_S_VOLTSx10,	0,		"System Power Supply -5V" },

	{ ESM_S_VOLTSx10,	0,		"System Power Supply -12V" },
	{ ESM_S_VOLTSx10,	0,		"System Power Supply +5V aux" },
	{ ESM_S_AMPS,		0,		"Power Supply 1 +5V" },
	{ ESM_S_AMPS,		0,		"Power Supply 1 +12V" },
	{ ESM_S_AMPS,		0,		"Power Supply 1 +3.3V" },

	{ ESM_S_AMPS,		0,		"Power Supply 2 +5V" },
	{ ESM_S_AMPS,		0,		"Power Supply 2 +12V" },
	{ ESM_S_AMPS,		0,		"Power Supply 2 +3.3V" },
	{ ESM_S_AMPS,		0,		"Power Supply 3 +5V" },
	{ ESM_S_AMPS,		0,		"Power Supply 3 +12V" },

	{ ESM_S_AMPS,		0,		"Power Supply 3 +3.3V" },
	{ ESM_S_FANRPM,		0,		"Power Supply 1 Fan" },
	{ ESM_S_FANRPM,		0,		"Power Supply 2 Fan" },
	{ ESM_S_FANRPM,		0,		"Power Supply 3 Fan" },
	{ ESM_S_PWRSUP,		0,		"Power Supply 1" },

	{ ESM_S_PWRSUP,		0,		"Power Supply 2" },
	{ ESM_S_PWRSUP,		0,		"Power Supply 3" },
	{ ESM_S_UNKNOWN,	0,		"PSPB Fan Control" },
	{ ESM_S_FANRPM,		0,		"Fan 1" },
	{ ESM_S_FANRPM,		0,		"Fan 2" },

	{ ESM_S_FANRPM,		0,		"Fan 3" },
	{ ESM_S_FANRPM,		0,		"Fan 4" },
	{ ESM_S_FANRPM,		0,		"Fan 5" },
	{ ESM_S_FANRPM,		0,		"Fan 6" },
	{ ESM_S_UNKNOWN,	0,		"Fan Enclosure" },
};

void
esm_devmap(struct esm_softc *sc, struct esm_devmap *devmap)
{
	struct esm_sensor_map	*sensor_map = NULL;
	const char		*name = NULL, *fname = NULL;
	int			mapsize = 0;

	switch (devmap->dev_major) {
	case ESM2_DEV_ESM2:
		sensor_map = esm_sensors_esm2;

		switch (devmap->dev_minor) {
		case ESM2_DEV_ESM2_2300:
			name = "PowerEdge 2300";
			mapsize = 23;
			break;
		case ESM2_DEV_ESM2_4300:
			name = "PowerEdge 4300";
			mapsize = 27;
			break;
		case ESM2_DEV_ESM2_6300:
			name = "PowerEdge 6300";
			mapsize = 27;
			break;
		case ESM2_DEV_ESM2_6400:
			name = "PowerEdge 6400";
			mapsize = 44;
			break;
		case ESM2_DEV_ESM2_2550:
			name = "PowerEdge 2550";
			mapsize = 48;
			break;
		case ESM2_DEV_ESM2_4350:
			name = "PowerEdge 4350";
			mapsize = 27;
			break;
		case ESM2_DEV_ESM2_6350:
			name = "PowerEdge 6350";
			mapsize = 27;
			break;
		case ESM2_DEV_ESM2_6450:
			name = "PowerEdge 6450";
			mapsize = 44;
			break;
		case ESM2_DEV_ESM2_2400:
			name = "PowerEdge 2400";
			mapsize = 30;
			break;
		case ESM2_DEV_ESM2_4400:
			name = "PowerEdge 4400";
			mapsize = 44;
			break;
		case ESM2_DEV_ESM2_2500:
			name = "PowerEdge 2500";
			mapsize = 55;
			break;
		case ESM2_DEV_ESM2_2450:
			name = "PowerEdge 2450";
			mapsize = 27;
			break;
		case ESM2_DEV_ESM2_2400EX:
			name = "PowerEdge 2400";
			mapsize = 27;
			break;
		case ESM2_DEV_ESM2_2450EX:
			name = "PowerEdge 2450";
			mapsize = 44;
			break;
		default:
			return;
		}

		fname = "Embedded Server Management";
		break;

	case ESM2_DEV_DRACII:
		fname = "Dell Remote Assistance Card II";
		break;

	case ESM2_DEV_FRONT_PANEL:
		fname = "Front Panel";
		break;

	case ESM2_DEV_BACKPLANE2:
		sensor_map = esm_sensors_backplane;
		mapsize = 22;

		fname = "Primary System Backplane";
		break;

	case ESM2_DEV_POWERUNIT2:
		sensor_map = esm_sensors_powerunit;
		mapsize = sizeof(esm_sensors_powerunit) /
		    sizeof(esm_sensors_powerunit[0]);

		fname = "Power Unit";
		break;

	case ESM2_DEV_ENCL2_BACKPLANE:
	case ESM2_DEV_ENCL1_BACKPLANE:
		fname = "Enclosure Backplane";
		break;

	case ESM2_DEV_ENCL2_POWERUNIT:
	case ESM2_DEV_ENCL1_POWERUNIT:
		fname = "Enclosure Powerunit";
		break;

	case ESM2_DEV_HPPCI: /* nfi what this is */
		fname = "HPPCI";
		break;

	case ESM2_DEV_BACKPLANE3:
		sensor_map = esm_sensors_backplane;
		mapsize = sizeof(esm_sensors_backplane) /
		    sizeof(esm_sensors_backplane[0]);

		fname = "Primary System Backplane";
		break;

	default:
		return;
	}

	printf("%s: %s%s%s %d.%d\n", DEVNAME(sc),
	    name ? name : "", name ? " " : "", fname,
	    devmap->rev_major, devmap->rev_minor);

	esm_make_sensors(sc, devmap, sensor_map, mapsize);
}

void
esm_make_sensors(struct esm_softc *sc, struct esm_devmap *devmap,
    struct esm_sensor_map *sensor_map, int mapsize)
{
	struct esm_smb_req	req;
	struct esm_smb_resp	resp;
	struct esm_smb_resp_val	*val = &resp.resp_val;
	struct esm_sensor	*es;
	struct ksensor		*s;
	int			nsensors, i, j;
	const char		*psulabels[] = {
				    "AC", "SW", "OK", "ON", "FFAN", "OTMP"
				};

	memset(&req, 0, sizeof(req));
	req.h_cmd = ESM2_CMD_SMB_XMIT_RECV;
	req.h_dev = devmap->index;
	req.h_txlen = sizeof(req.req_val);
	req.h_rxlen = sizeof(resp.resp_val);

	req.req_val.v_cmd = ESM2_SMB_SENSOR_VALUE;

	for (i = 0; i < mapsize; i++) {
		req.req_val.v_sensor = i;
		if (esm_smb_cmd(sc, &req, &resp, 1, 0) != 0)
			continue;

		DPRINTFN(1, "%s: dev: 0x%02x sensor: %d (%s) "
		    "reading: 0x%04x status: 0x%02x cksum: 0x%02x\n",
		    DEVNAME(sc), devmap->index, i, sensor_map[i].name,
		    val->v_reading, val->v_status, val->v_checksum);

		switch (sensor_map[i].type) {
		case ESM_S_PWRSUP:
			if (val->v_status == 0x00)
				continue;
			break;
		default:
			if (!(val->v_status & ESM2_VS_VALID))
				continue;
			break;
		}

		es = malloc(sizeof(struct esm_sensor), M_DEVBUF,
		    M_NOWAIT|M_ZERO);
		if (es == NULL)
			return;

		es->es_dev = devmap->index;
		es->es_id = i;
		es->es_type = sensor_map[i].type;

		switch (es->es_type) {
		case ESM_S_DRIVES:
			/*
			 * this esm sensor represents 4 kernel sensors, so we
			 * go through these hoops to deal with it.
			 */
			nsensors = 4;
			s = malloc(sizeof(struct ksensor) * nsensors, M_DEVBUF,
			    M_NOWAIT|M_ZERO);
			if (s == NULL) {
				free(es, M_DEVBUF, 0);
				return;
			}

			for (j = 0; j < nsensors; j++) {
				snprintf(s[j].desc, sizeof(s[j].desc), "%s %d",
				    sensor_map[i].name, sensor_map[i].arg + j);
			}
			break;
		case ESM_S_PWRSUP:
			/*
			 * the esm pwrsup sensor has a bitfield for its value,
			 * this expands it out to 6 separate indicators
			 */
			nsensors = 6;
			s = malloc(sizeof(struct ksensor) * nsensors, M_DEVBUF,
			    M_NOWAIT|M_ZERO);
			if (s == NULL) {
				free(es, M_DEVBUF, 0);
				return;
			}

			for (j = 0; j < nsensors; j++) {
				snprintf(s[j].desc, sizeof(s[j].desc), "%s %s",
				    sensor_map[i].name, psulabels[j]);
			}
			break;

		case ESM_S_TEMP:
		case ESM_S_FANRPM:
		case ESM_S_AMPS:
		case ESM_S_VOLTS:
		case ESM_S_VOLTSx10:
			if (esm_thresholds(sc, devmap, es) != 0) {
				free(es, M_DEVBUF, 0);
				continue;
			}
			/* FALLTHROUGH */

		default:
			nsensors = 1;
			s = malloc(sizeof(struct ksensor), M_DEVBUF,
			    M_NOWAIT|M_ZERO);
			if (s == NULL) {
				free(es, M_DEVBUF, 0);
				return;
			}

			strlcpy(s->desc, sensor_map[i].name, sizeof(s->desc));
			break;
		}

		for (j = 0; j < nsensors; j++) {
			s[j].type = esm_typemap[es->es_type];
			sensor_attach(&sc->sc_sensordev, &s[j]);
		}

		es->es_sensor = s;
		TAILQ_INSERT_TAIL(&sc->sc_sensors, es, es_entry);
	}
}

int
esm_thresholds(struct esm_softc *sc, struct esm_devmap *devmap,
    struct esm_sensor *es)
{
	struct esm_smb_req	req;
	struct esm_smb_resp	resp;
	struct esm_smb_resp_thr	*thr = &resp.resp_thr;

	memset(&req, 0, sizeof(req));
	req.h_cmd = ESM2_CMD_SMB_XMIT_RECV;
	req.h_dev = devmap->index;
	req.h_txlen = sizeof(req.req_thr);
	req.h_rxlen = sizeof(resp.resp_thr);

	req.req_thr.t_cmd = ESM2_SMB_SENSOR_THRESHOLDS;
	req.req_thr.t_sensor = es->es_id;

	if (esm_smb_cmd(sc, &req, &resp, 1, 0) != 0)
		return (1);

	DPRINTFN(2, "%s: dev: %d sensor: %d lo fail: %d hi fail: %d "
	    "lo warn: %d hi warn: %d hysterisis: %d checksum: 0x%02x\n",
	    DEVNAME(sc), devmap->index, es->es_id, thr->t_lo_fail,
	    thr->t_hi_fail, thr->t_lo_warn, thr->t_hi_warn, thr->t_hysterisis,
	    thr->t_checksum);

	es->es_thresholds.th_lo_crit = thr->t_lo_fail;
	es->es_thresholds.th_lo_warn = thr->t_lo_warn;
	es->es_thresholds.th_hi_warn = thr->t_hi_warn;
	es->es_thresholds.th_hi_crit = thr->t_hi_fail;

	return (0);
}

int
esm_bmc_ready(struct esm_softc *sc, int port, u_int8_t mask, u_int8_t val,
    int wait)
{
	unsigned int		count = wait ? 0 : 0xfffff;

	do {
		if ((EREAD(sc, port) & mask) == val)
			return (0);
	} while (count++ < 0xfffff);

	return (1);
}

int
esm_cmd(struct esm_softc *sc, void *cmd, size_t cmdlen, void *resp,
    size_t resplen, int wait, int step)
{
	u_int8_t		*tx = (u_int8_t *)cmd;
	u_int8_t		*rx = (u_int8_t *)resp;
	int			i;

	switch (step) {
	case 0:
	case 1:
		/* Wait for card ready */
		if (esm_bmc_ready(sc, ESM2_CTRL_REG, ESM2_TC_READY,
		    0, wait) != 0)
			return (1); /* busy */

		/* Write command data to port */
		ECTRLWR(sc, ESM2_TC_CLR_WPTR);
		for (i = 0; i < cmdlen; i++) {
			DPRINTFN(2, "write: %.2x\n", *tx);
			EDATAWR(sc, *tx);
			tx++;
		}

		/* Ring doorbell... */
		ECTRLWR(sc, ESM2_TC_H2ECDB);
		/* FALLTHROUGH */
	case 2:
		/* ...and wait */
		if (esm_bmc_ready(sc, ESM2_CTRL_REG, ESM2_TC_EC2HDB,
		    ESM2_TC_EC2HDB, wait) != 0)
			return (2);

		/* Set host busy semaphore and clear doorbell */
		ECTRLWR(sc, ESM2_TC_HOSTBUSY);
		ECTRLWR(sc, ESM2_TC_EC2HDB);

		/* Read response data from port */
		ECTRLWR(sc, ESM2_TC_CLR_RPTR);
		for (i = 0; i < resplen; i++) {
			*rx = EDATARD(sc);
			DPRINTFN(2, "read = %.2x\n", *rx);
			rx++;
		}

		/* release semaphore */
		ECTRLWR(sc, ESM2_TC_HOSTBUSY);
		break;
	}

	return (0);
}

int
esm_smb_cmd(struct esm_softc *sc, struct esm_smb_req *req,
    struct esm_smb_resp *resp, int wait, int step)
{
	int			err;

	memset(resp, 0, sizeof(struct esm_smb_resp));

	err = esm_cmd(sc, req, sizeof(req->hdr) + req->h_txlen, resp,
	    sizeof(resp->hdr) + req->h_rxlen, wait, step);
	if (err)
		return (err);

	if (resp->h_status != 0 || resp->h_i2csts != 0) {
		DPRINTFN(3, "%s: dev: 0x%02x error status: 0x%02x "
		    "i2csts: 0x%02x procsts: 0x%02x tx: 0x%02x rx: 0x%02x\n",
		    __func__, req->h_dev, resp->h_status, resp->h_i2csts,
		    resp->h_procsts, resp->h_rx, resp->h_tx);
		return (1);
	}

	return (0);
}

int64_t
esm_val2temp(u_int16_t value)
{
	return (((int64_t)value * 100000) + 273150000);
}

int64_t
esm_val2volts(u_int16_t value)
{
	return ((int64_t)value * 1000);
}

int64_t
esm_val2amps(u_int16_t value)
{
	return ((int64_t)value * 100000);
}


code:
/*	$OpenBSD: esmreg.h,v 1.10 2005/12/13 02:31:45 dlg Exp $ */

/*
 * Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org>
 * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#define ESM2_BASE_PORT		0xe0

#define ESM2_CTRL_REG		4
#define ESM2_DATA_REG		5
#define ESM2_INTMASK_REG	6

#define ESM2_TC_CLR_WPTR	(1L << 0)
#define ESM2_TC_CLR_RPTR	(1L << 1)
#define ESM2_TC_H2ECDB		(1L << 2)
#define ESM2_TC_EC2HDB		(1L << 3)
#define ESM2_TC_EVENTDB		(1L << 4)
#define ESM2_TC_HBDB		(1L << 5)
#define ESM2_TC_HOSTBUSY	(1L << 6)
#define ESM2_TC_ECBUSY		(1L << 7)
#define ESM2_TC_READY		(ESM2_TC_EC2HDB | ESM2_TC_H2ECDB | \
    ESM2_TC_ECBUSY)
#define ESM2_TC_POWER_UP_BITS	(ESM2_TC_CLR_WPTR | ESM2_TC_CLR_RPTR | \
    ESM2_TC_EC2HDB | ESM2_TC_EVENTDB)

#define ESM2_TIM_HIRQ_PEND	(1L << 1)
#define ESM2_TIM_SCI_EN		(1L << 2)
#define ESM2_TIM_SMI_EN		(1L << 3)
#define ESM2_TIM_NMI2SMI	(1L << 4)
#define ESM2_TIM_POWER_UP_BITS	(ESM2_TIM_HIRQ_PEND)

#define ESM2_CMD_NOOP			0x00
#define ESM2_CMD_ECHO			0x01
#define ESM2_CMD_DEVICEMAP		0x03
#define  ESM2_DEVICEMAP_READ			0x00

#define ESM2_CMD_HWDC			0x05 /* Host Watch Dog Control */
#define  ESM2_HWDC_WRITE_STATE			0x01
#define  ESM2_HWDC_READ_PROPERTY		0x02
#define  ESM2_HWDC_WRITE_PROPERTY		0x03

#define ESM2_CMD_SMB_BUF		0x20
#define ESM2_CMD_SMB_BUF_XMIT_RECV	0x21
#define ESM2_CMD_SMB_XMIT_RECV		0x22
#define  ESM2_SMB_SENSOR_VALUE			0x04
#define  ESM2_SMB_SENSOR_THRESHOLDS		0x19

#define ESM2_MAX_CMD_LEN	0x20
#define ESM2_UUID_LEN		0x08

#define DELL_SYSSTR_ADDR	0xFE076L
#define DELL_SYSID_ADDR		0xFE840L

#define DELL_SYSID_2300		0x81
#define DELL_SYSID_4300		0x7C
#define DELL_SYSID_4350		0x84
#define DELL_SYSID_6300		0x7F
#define DELL_SYSID_6350		0x83
#define DELL_SYSID_2400		0x9B
#define DELL_SYSID_2450		0xA6
#define DELL_SYSID_4400		0x9A
#define DELL_SYSID_6400		0x9C
#define DELL_SYSID_6450		0xA2
#define DELL_SYSID_2500		0xD9
#define DELL_SYSID_2550		0xD1
#define DELL_SYSID_PV530F	0xCD
#define DELL_SYSID_PV735N	0xE2
#define DELL_SYSID_PV750N	0xEE
#define DELL_SYSID_PV755N	0xEF
#define DELL_SYSID_PA200	0xCB
#define DELL_SYSID_EXT		0xFE

struct dell_sysid {
	u_int16_t		ext_id;
	u_int8_t		bios_ver[3];
	u_int8_t		sys_id;
} __packed;

struct esm_wdog_prop {
	u_int8_t		cmd;
	u_int8_t		reserved;
	u_int8_t		subcmd;
	u_int8_t		action;
	u_int32_t		time;
} __packed;

#define ESM_WDOG_DISABLE	0x00
#define ESM_WDOG_PWROFF		(1L << 1)
#define ESM_WDOG_PWRCYCLE	(1L << 2)
#define ESM_WDOG_RESET		(1L << 3)
#define ESM_WDOG_NOTIFY		(1L << 4)

struct esm_wdog_state {
	u_int8_t		cmd;
	u_int8_t		reserved;
	u_int8_t		subcmd;
	u_int8_t		state;
} __packed;

struct esm_devmap {
	u_int8_t		index;
	u_int8_t		dev_major;
	u_int8_t		dev_minor;
	u_int8_t		rev_major;
	u_int8_t		rev_minor;
	u_int8_t		rev_rom;
	u_int8_t		smb_addr;
	u_int8_t		status;
	u_int8_t		monitor_type;
	u_int8_t		pollcycle;
	u_int8_t		uniqueid[ESM2_UUID_LEN];
} __packed;

struct esm_devmap_req {
	u_int8_t		cmd;
	u_int8_t		initiator;
	u_int8_t		action;
	u_int8_t		index;
	u_int8_t		ndev;
} __packed;

struct esm_devmap_resp {
	u_int8_t		status;
	u_int8_t		ndev;
	struct esm_devmap	devmap[1]; /* request one map at a time */
} __packed;

/* ESM SMB requests */

struct esm_smb_req_val {
	u_int8_t		v_cmd;
	u_int8_t		v_initiator;
	u_int8_t		v_sensor;
} __packed;

struct esm_smb_req_thr {
	u_int8_t		t_cmd;
	u_int8_t		t_sensor;
} __packed;

struct esm_smb_req {
	struct {
		u_int8_t		_cmd;
		u_int8_t		_dev;
		u_int8_t		_txlen;
		u_int8_t		_rxlen;
	} __packed hdr;
#define h_cmd		hdr._cmd
#define h_dev		hdr._dev
#define h_txlen		hdr._txlen
#define h_rxlen		hdr._rxlen

	union {
		struct esm_smb_req_val	_val;
		struct esm_smb_req_thr	_thr;
	} __packed _;
#define req_val		_._val
#define req_thr		_._thr

} __packed;

/* ESM SMB responses */

struct esm_smb_resp_val {
	u_int16_t		v_reading;
	u_int8_t		v_status;
	u_int8_t		v_checksum;
} __packed;

struct esm_smb_resp_thr {
	u_int8_t		t_sensor;
	u_int16_t		t_lo_fail;
	u_int16_t		t_hi_fail;
	u_int16_t		t_lo_warn;
	u_int16_t		t_hi_warn;
	u_int16_t		t_hysterisis;
	u_int8_t		t_checksum;
} __packed;

struct esm_smb_resp {
	struct {
		u_int8_t		_status;
		u_int8_t		_i2csts;
		u_int8_t		_procsts;
		u_int8_t		_tx;
		u_int8_t		_rx;
	} __packed hdr;
#define h_status	hdr._status
#define h_i2csts	hdr._i2csts
#define h_procsts	hdr._procsts
#define h_tx		hdr._tx
#define h_rx		hdr._rx

	union {
		struct esm_smb_resp_val	_val;
		struct esm_smb_resp_thr	_thr;
	} __packed _;
#define resp_val _._val
#define resp_thr _._thr
} __packed;

/* esm_smb_resp_val drive values */
#define ESM2_V_DRV_EMPTY	1
#define ESM2_V_DRV_READY	2
#define ESM2_V_DRV_POWERUP	3
#define ESM2_V_DRV_ONLINE	4
#define ESM2_V_DRV_IDLE		5
#define ESM2_V_DRV_ACTIVE	6
#define ESM2_V_DRV_REBUILD	7
#define ESM2_V_DRV_POWERDOWN	8
#define ESM2_V_DRV_FAIL		9
#define ESM2_V_DRV_PFAIL	10

/* esm_smb_resp_val powersupply values */
#define ESM2_V_PSU_ID(x)	((x)>>8 & 0xff)
#define ESM2_V_PSU_AC		(1<<0)
#define ESM2_V_PSU_SW		(1<<1)
#define ESM2_V_PSU_OK		(1<<2)
#define ESM2_V_PSU_ON		(1<<3)
#define ESM2_V_PSU_FFAN		(1<<4)
#define ESM2_V_PSU_OTMP		(1<<5)

/* esm_smb_resp_val status */
#define ESM2_VS_VALID		(1<<2)
/* the powersupplies have a special status field */
#define ESM2_VS_PSU_INST	(1<<0)
#define ESM2_VS_PSU_PSON	(1<<1)
#define ESM2_VS_PSU_FAIL	(1<<2)
#define ESM2_VS_PSU_PSDB	(1<<3)

enum esm_dev_type {
	ESM2_DEV_ESM2 = 1,
	ESM2_DEV_DRACII,
	ESM2_DEV_FRONT_PANEL,
	ESM2_DEV_BACKPLANE2,
	ESM2_DEV_POWERUNIT2,
	ESM2_DEV_ENCL2_BACKPLANE,
	ESM2_DEV_ENCL2_POWERUNIT,
	ESM2_DEV_ENCL1_BACKPLANE,
	ESM2_DEV_ENCL1_POWERUNIT,
	ESM2_DEV_HPPCI,
	ESM2_DEV_BACKPLANE3
};

enum esm_dev_esm2_type {
	ESM2_DEV_ESM2_2300 = 0,
	ESM2_DEV_ESM2_4300,
	ESM2_DEV_ESM2_6300,
	ESM2_DEV_ESM2_6400,
	ESM2_DEV_ESM2_2550,
	ESM2_DEV_ESM2_4350,
	ESM2_DEV_ESM2_6350,
	ESM2_DEV_ESM2_6450,
	ESM2_DEV_ESM2_2400,
	ESM2_DEV_ESM2_4400,
	ESM2_DEV_ESM2_R0, /* reserved */
	ESM2_DEV_ESM2_2500,
	ESM2_DEV_ESM2_2450,
	ESM2_DEV_ESM2_R1, /* reserved */
	ESM2_DEV_ESM2_R2, /* reserved */
	ESM2_DEV_ESM2_2400EX,
	ESM2_DEV_ESM2_2450EX
};

license: unknown


code:
/*	$OpenBSD: esmvar.h,v 1.3 2005/11/28 19:59:25 deraadt Exp $ */

/*
 * Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org>
 * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

struct esm_attach_args {
	char			*eaa_name;
	bus_space_tag_t		eaa_iot;
	bus_space_tag_t		eaa_memt;
};

int		esm_probe(void *);


code:
/*	$OpenBSD: est.c,v 1.45 2014/12/09 06:58:28 doug Exp $ */
/*
 * Copyright (c) 2003 Michael Eriksson.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * This is a driver for Intel's Enhanced SpeedStep, as implemented in
 * Pentium M processors.
 *
 * Reference documentation:
 *
 * - IA-32 Intel Architecture Software Developer's Manual, Volume 3:
 *   System Programming Guide.
 *   Section 13.14, Enhanced Intel SpeedStep technology.
 *   Table B-2, MSRs in Pentium M Processors.
 *   http://www.intel.com/design/pentium4/manuals/245472.htm
 *
 * - Intel Pentium M Processor Datasheet.
 *   Table 5, Voltage and Current Specifications.
 *   http://www.intel.com/design/mobile/datashts/252612.htm
 *
 * - Intel Pentium M Processor on 90 nm Process with 2-MB L2 Cache Datasheet
 *   Table 3-4, Voltage and Current Specifications.
 *   http://www.intel.com/design/mobile/datashts/302189.htm
 *
 * - Linux cpufreq patches, speedstep-centrino.c.
 *   Encoding of MSR_PERF_CTL and MSR_PERF_STATUS.
 *   http://www.codemonkey.org.uk/projects/cpufreq/cpufreq-2.4.22-pre6-1.gz
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/malloc.h>

#include <machine/cpu.h>
#include <machine/cpufunc.h>
#include <machine/specialreg.h>
#include <machine/bus.h>

#include "acpicpu.h"

#if NACPICPU > 0
#include <dev/acpi/acpidev.h>
#include <dev/acpi/acpivar.h>
#endif

/* Convert MHz and mV into IDs for passing to the MSR. */
#define ID16(MHz, mV, bus_clk) \
    { ((((MHz * 100 + 50) / bus_clk) << 8) | ((mV ? mV - 700 : 0) >> 4)), \
	 MHz }

struct est_op {
	uint16_t ctrl;
	uint16_t mhz;
};

/* Ultra Low Voltage Intel Pentium M processor 900 MHz */
static struct est_op pm130_900_ulv[] = {
	ID16( 900, 1004, BUS100),
	ID16( 800,  988, BUS100),
	ID16( 600,  844, BUS100),
};

/* Ultra Low Voltage Intel Pentium M processor 1.00 GHz */
static struct est_op pm130_1000_ulv[] = {
	ID16(1000, 1004, BUS100),
	ID16( 900,  988, BUS100),
	ID16( 800,  972, BUS100),
	ID16( 600,  844, BUS100),
};

/* Ultra Low Voltage Intel Pentium M processor 1.10 GHz */
static struct est_op pm130_1100_ulv[] = {
	ID16(1100, 1004, BUS100),
	ID16(1000,  988, BUS100),
	ID16( 900,  972, BUS100),
	ID16( 800,  956, BUS100),
	ID16( 600,  844, BUS100),
};

/* Low Voltage Intel Pentium M processor 1.10 GHz */
static struct est_op pm130_1100_lv[] = {
	ID16(1100, 1180, BUS100),
	ID16(1000, 1164, BUS100),
	ID16( 900, 1100, BUS100),
	ID16( 800, 1020, BUS100),
	ID16( 600,  956, BUS100),
};

/* Low Voltage Intel Pentium M processor 1.20 GHz */
static struct est_op pm130_1200_lv[] = {
	ID16(1200, 1180, BUS100),
	ID16(1100, 1164, BUS100),
	ID16(1000, 1100, BUS100),
	ID16( 900, 1020, BUS100),
	ID16( 800, 1004, BUS100),
	ID16( 600,  956, BUS100),
};

/* Low Voltage Intel Pentium M processor 1.30 GHz */
static struct est_op pm130_1300_lv[] = {
	ID16(1300, 1180, BUS100),
	ID16(1200, 1164, BUS100),
	ID16(1100, 1100, BUS100),
	ID16(1000, 1020, BUS100),
	ID16( 900, 1004, BUS100),
	ID16( 800,  988, BUS100),
	ID16( 600,  956, BUS100),
};

/* Intel Pentium M processor 1.30 GHz */
static struct est_op pm130_1300[] = {
	ID16(1300, 1388, BUS100),
	ID16(1200, 1356, BUS100),
	ID16(1000, 1292, BUS100),
	ID16( 800, 1260, BUS100),
	ID16( 600,  956, BUS100),
};

/* Intel Pentium M processor 1.40 GHz */
static struct est_op pm130_1400[] = {
	ID16(1400, 1484, BUS100),
	ID16(1200, 1436, BUS100),
	ID16(1000, 1308, BUS100),
	ID16( 800, 1180, BUS100),
	ID16( 600,  956, BUS100),
};

/* Intel Pentium M processor 1.50 GHz */
static struct est_op pm130_1500[] = {
	ID16(1500, 1484, BUS100),
	ID16(1400, 1452, BUS100),
	ID16(1200, 1356, BUS100),
	ID16(1000, 1228, BUS100),
	ID16( 800, 1116, BUS100),
	ID16( 600,  956, BUS100),
};

/* Intel Pentium M processor 1.60 GHz */
static struct est_op pm130_1600[] = {
	ID16(1600, 1484, BUS100),
	ID16(1400, 1420, BUS100),
	ID16(1200, 1276, BUS100),
	ID16(1000, 1164, BUS100),
	ID16( 800, 1036, BUS100),
	ID16( 600,  956, BUS100),
};

/* Intel Pentium M processor 1.70 GHz */
static struct est_op pm130_1700[] = {
	ID16(1700, 1484, BUS100),
	ID16(1400, 1308, BUS100),
	ID16(1200, 1228, BUS100),
	ID16(1000, 1116, BUS100),
	ID16( 800, 1004, BUS100),
	ID16( 600,  956, BUS100),
};

/* Intel Pentium M processor 723 1.0 GHz */
static struct est_op pm90_n723[] = {
	ID16(1000,  940, BUS100),
	ID16( 900,  908, BUS100),
	ID16( 800,  876, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 733 1.1 GHz, VID #G */
static struct est_op pm90_n733g[] = {
	ID16(1100,  956, BUS100),
	ID16(1000,  940, BUS100),
	ID16( 900,  908, BUS100),
	ID16( 800,  876, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 733 1.1 GHz, VID #H */
static struct est_op pm90_n733h[] = {
	ID16(1100,  940, BUS100),
	ID16(1000,  924, BUS100),
	ID16( 900,  892, BUS100),
	ID16( 800,  876, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 733 1.1 GHz, VID #I */
static struct est_op pm90_n733i[] = {
	ID16(1100,  924, BUS100),
	ID16(1000,  908, BUS100),
	ID16( 900,  892, BUS100),
	ID16( 800,  860, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 733 1.1 GHz, VID #J */
static struct est_op pm90_n733j[] = {
	ID16(1100,  908, BUS100),
	ID16(1000,  892, BUS100),
	ID16( 900,  876, BUS100),
	ID16( 800,  860, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 733 1.1 GHz, VID #K */
static struct est_op pm90_n733k[] = {
	ID16(1100,  892, BUS100),
	ID16(1000,  876, BUS100),
	ID16( 900,  860, BUS100),
	ID16( 800,  844, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 733 1.1 GHz, VID #L */
static struct est_op pm90_n733l[] = {
	ID16(1100,  876, BUS100),
	ID16(1000,  876, BUS100),
	ID16( 900,  860, BUS100),
	ID16( 800,  844, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 753 1.2 GHz, VID #G */
static struct est_op pm90_n753g[] = {
	ID16(1200,  956, BUS100),
	ID16(1100,  940, BUS100),
	ID16(1000,  908, BUS100),
	ID16( 900,  892, BUS100),
	ID16( 800,  860, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 753 1.2 GHz, VID #H */
static struct est_op pm90_n753h[] = {
	ID16(1200,  940, BUS100),
	ID16(1100,  924, BUS100),
	ID16(1000,  908, BUS100),
	ID16( 900,  876, BUS100),
	ID16( 800,  860, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 753 1.2 GHz, VID #I */
static struct est_op pm90_n753i[] = {
	ID16(1200,  924, BUS100),
	ID16(1100,  908, BUS100),
	ID16(1000,  892, BUS100),
	ID16( 900,  876, BUS100),
	ID16( 800,  860, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 753 1.2 GHz, VID #J */
static struct est_op pm90_n753j[] = {
	ID16(1200,  908, BUS100),
	ID16(1100,  892, BUS100),
	ID16(1000,  876, BUS100),
	ID16( 900,  860, BUS100),
	ID16( 800,  844, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 753 1.2 GHz, VID #K */
static struct est_op pm90_n753k[] = {
	ID16(1200,  892, BUS100),
	ID16(1100,  892, BUS100),
	ID16(1000,  876, BUS100),
	ID16( 900,  860, BUS100),
	ID16( 800,  844, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 753 1.2 GHz, VID #L */
static struct est_op pm90_n753l[] = {
	ID16(1200,  876, BUS100),
	ID16(1100,  876, BUS100),
	ID16(1000,  860, BUS100),
	ID16( 900,  844, BUS100),
	ID16( 800,  844, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 773 1.3 GHz, VID #G */
static struct est_op pm90_n773g[] = {
	ID16(1300,  956, BUS100),
	ID16(1200,  940, BUS100),
	ID16(1100,  924, BUS100),
	ID16(1000,  908, BUS100),
	ID16( 900,  876, BUS100),
	ID16( 800,  860, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 773 1.3 GHz, VID #H */
static struct est_op pm90_n773h[] = {
	ID16(1300,  940, BUS100),
	ID16(1200,  924, BUS100),
	ID16(1100,  908, BUS100),
	ID16(1000,  892, BUS100),
	ID16( 900,  876, BUS100),
	ID16( 800,  860, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 773 1.3 GHz, VID #I */
static struct est_op pm90_n773i[] = {
	ID16(1300,  924, BUS100),
	ID16(1200,  908, BUS100),
	ID16(1100,  892, BUS100),
	ID16(1000,  876, BUS100),
	ID16( 900,  860, BUS100),
	ID16( 800,  844, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 773 1.3 GHz, VID #J */
static struct est_op pm90_n773j[] = {
	ID16(1300,  908, BUS100),
	ID16(1200,  908, BUS100),
	ID16(1100,  892, BUS100),
	ID16(1000,  876, BUS100),
	ID16( 900,  860, BUS100),
	ID16( 800,  844, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 773 1.3 GHz, VID #K */
static struct est_op pm90_n773k[] = {
	ID16(1300,  892, BUS100),
	ID16(1200,  892, BUS100),
	ID16(1100,  876, BUS100),
	ID16(1000,  860, BUS100),
	ID16( 900,  860, BUS100),
	ID16( 800,  844, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 773 1.3 GHz, VID #L */
static struct est_op pm90_n773l[] = {
	ID16(1300,  876, BUS100),
	ID16(1200,  876, BUS100),
	ID16(1100,  860, BUS100),
	ID16(1000,  860, BUS100),
	ID16( 900,  844, BUS100),
	ID16( 800,  844, BUS100),
	ID16( 600,  812, BUS100),
};

/* Intel Pentium M processor 738 1.4 GHz */
static struct est_op pm90_n738[] = {
	ID16(1400, 1116, BUS100),
	ID16(1300, 1116, BUS100),
	ID16(1200, 1100, BUS100),
	ID16(1100, 1068, BUS100),
	ID16(1000, 1052, BUS100),
	ID16( 900, 1036, BUS100),
	ID16( 800, 1020, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 758 1.5 GHz */
static struct est_op pm90_n758[] = {
	ID16(1500, 1116, BUS100),
	ID16(1400, 1116, BUS100),
	ID16(1300, 1100, BUS100),
	ID16(1200, 1084, BUS100),
	ID16(1100, 1068, BUS100),
	ID16(1000, 1052, BUS100),
	ID16( 900, 1036, BUS100),
	ID16( 800, 1020, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 778 1.6 GHz */
static struct est_op pm90_n778[] = {
	ID16(1600, 1116, BUS100),
	ID16(1500, 1116, BUS100),
	ID16(1400, 1100, BUS100),
	ID16(1300, 1184, BUS100),
	ID16(1200, 1068, BUS100),
	ID16(1100, 1052, BUS100),
	ID16(1000, 1052, BUS100),
	ID16( 900, 1036, BUS100),
	ID16( 800, 1020, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 710 1.4 GHz, 533 MHz FSB */
static struct est_op pm90_n710[] = {
	ID16(1400, 1340, BUS133),
	ID16(1200, 1228, BUS133),
	ID16(1000, 1148, BUS133),
	ID16( 800, 1068, BUS133),
	ID16( 600,  998, BUS133),
};

/* Intel Pentium M processor 715 1.5 GHz, VID #A */
static struct est_op pm90_n715a[] = {
	ID16(1500, 1340, BUS100),
	ID16(1200, 1228, BUS100),
	ID16(1000, 1148, BUS100),
	ID16( 800, 1068, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 715 1.5 GHz, VID #B */
static struct est_op pm90_n715b[] = {
	ID16(1500, 1324, BUS100),
	ID16(1200, 1212, BUS100),
	ID16(1000, 1148, BUS100),
	ID16( 800, 1068, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 715 1.5 GHz, VID #C */
static struct est_op pm90_n715c[] = {
	ID16(1500, 1308, BUS100),
	ID16(1200, 1212, BUS100),
	ID16(1000, 1132, BUS100),
	ID16( 800, 1068, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 715 1.5 GHz, VID #D */
static struct est_op pm90_n715d[] = {
	ID16(1500, 1276, BUS100),
	ID16(1200, 1180, BUS100),
	ID16(1000, 1116, BUS100),
	ID16( 800, 1052, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 725 1.6 GHz, VID #A */
static struct est_op pm90_n725a[] = {
	ID16(1600, 1340, BUS100),
	ID16(1400, 1276, BUS100),
	ID16(1200, 1212, BUS100),
	ID16(1000, 1132, BUS100),
	ID16( 800, 1068, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 725 1.6 GHz, VID #B */
static struct est_op pm90_n725b[] = {
	ID16(1600, 1324, BUS100),
	ID16(1400, 1260, BUS100),
	ID16(1200, 1196, BUS100),
	ID16(1000, 1132, BUS100),
	ID16( 800, 1068, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 725 1.6 GHz, VID #C */
static struct est_op pm90_n725c[] = {
	ID16(1600, 1308, BUS100),
	ID16(1400, 1244, BUS100),
	ID16(1200, 1180, BUS100),
	ID16(1000, 1116, BUS100),
	ID16( 800, 1052, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 725 1.6 GHz, VID #D */
static struct est_op pm90_n725d[] = {
	ID16(1600, 1276, BUS100),
	ID16(1400, 1228, BUS100),
	ID16(1200, 1164, BUS100),
	ID16(1000, 1116, BUS100),
	ID16( 800, 1052, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 730 1.6 GHz, 533 MHz FSB */
static struct est_op pm90_n730[] = {
	ID16(1600, 1308, BUS133),
	ID16(1333, 1260, BUS133),
	ID16(1200, 1212, BUS133),
	ID16(1067, 1180, BUS133),
	ID16( 800,  988, BUS133),
};

/* Intel Pentium M processor 735 1.7 GHz, VID #A */
static struct est_op pm90_n735a[] = {
	ID16(1700, 1340, BUS100),
	ID16(1400, 1244, BUS100),
	ID16(1200, 1180, BUS100),
	ID16(1000, 1116, BUS100),
	ID16( 800, 1052, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 735 1.7 GHz, VID #B */
static struct est_op pm90_n735b[] = {
	ID16(1700, 1324, BUS100),
	ID16(1400, 1244, BUS100),
	ID16(1200, 1180, BUS100),
	ID16(1000, 1116, BUS100),
	ID16( 800, 1052, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 735 1.7 GHz, VID #C */
static struct est_op pm90_n735c[] = {
	ID16(1700, 1308, BUS100),
	ID16(1400, 1228, BUS100),
	ID16(1200, 1164, BUS100),
	ID16(1000, 1116, BUS100),
	ID16( 800, 1052, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 735 1.7 GHz, VID #D */
static struct est_op pm90_n735d[] = {
	ID16(1700, 1276, BUS100),
	ID16(1400, 1212, BUS100),
	ID16(1200, 1148, BUS100),
	ID16(1000, 1100, BUS100),
	ID16( 800, 1052, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 740 1.73 GHz, 533 MHz FSB */
static struct est_op pm90_n740[] = {
	ID16(1733, 1356, BUS133),
	ID16(1333, 1212, BUS133),
	ID16(1067, 1100, BUS133),
	ID16( 800,  988, BUS133),
};

/* Intel Pentium M processor 745 1.8 GHz, VID #A */
static struct est_op pm90_n745a[] = {
	ID16(1800, 1340, BUS100),
	ID16(1600, 1292, BUS100),
	ID16(1400, 1228, BUS100),
	ID16(1200, 1164, BUS100),
	ID16(1000, 1116, BUS100),
	ID16( 800, 1052, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 745 1.8 GHz, VID #B */
static struct est_op pm90_n745b[] = {
	ID16(1800, 1324, BUS100),
	ID16(1600, 1276, BUS100),
	ID16(1400, 1212, BUS100),
	ID16(1200, 1164, BUS100),
	ID16(1000, 1116, BUS100),
	ID16( 800, 1052, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 745 1.8 GHz, VID #C */
static struct est_op pm90_n745c[] = {
	ID16(1800, 1308, BUS100),
	ID16(1600, 1260, BUS100),
	ID16(1400, 1212, BUS100),
	ID16(1200, 1148, BUS100),
	ID16(1000, 1100, BUS100),
	ID16( 800, 1052, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 745 1.8 GHz, VID #D */
static struct est_op pm90_n745d[] = {
	ID16(1800, 1276, BUS100),
	ID16(1600, 1228, BUS100),
	ID16(1400, 1180, BUS100),
	ID16(1200, 1132, BUS100),
	ID16(1000, 1084, BUS100),
	ID16( 800, 1036, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 750 1.86 GHz, 533 MHz FSB */
/* values extracted from \_PR\NPSS (via _PSS) SDST ACPI table */
static struct est_op pm90_n750[] = {
	ID16(1867, 1308, BUS133),
	ID16(1600, 1228, BUS133),
	ID16(1333, 1148, BUS133),
	ID16(1067, 1068, BUS133),
	ID16( 800,  988, BUS133),
};

/* Intel Pentium M processor 755 2.0 GHz, VID #A */
static struct est_op pm90_n755a[] = {
	ID16(2000, 1340, BUS100),
	ID16(1800, 1292, BUS100),
	ID16(1600, 1244, BUS100),
	ID16(1400, 1196, BUS100),
	ID16(1200, 1148, BUS100),
	ID16(1000, 1100, BUS100),
	ID16( 800, 1052, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 755 2.0 GHz, VID #B */
static struct est_op pm90_n755b[] = {
	ID16(2000, 1324, BUS100),
	ID16(1800, 1276, BUS100),
	ID16(1600, 1228, BUS100),
	ID16(1400, 1180, BUS100),
	ID16(1200, 1132, BUS100),
	ID16(1000, 1084, BUS100),
	ID16( 800, 1036, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 755 2.0 GHz, VID #C */
static struct est_op pm90_n755c[] = {
	ID16(2000, 1308, BUS100),
	ID16(1800, 1276, BUS100),
	ID16(1600, 1228, BUS100),
	ID16(1400, 1180, BUS100),
	ID16(1200, 1132, BUS100),
	ID16(1000, 1084, BUS100),
	ID16( 800, 1036, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 755 2.0 GHz, VID #D */
static struct est_op pm90_n755d[] = {
	ID16(2000, 1276, BUS100),
	ID16(1800, 1244, BUS100),
	ID16(1600, 1196, BUS100),
	ID16(1400, 1164, BUS100),
	ID16(1200, 1116, BUS100),
	ID16(1000, 1084, BUS100),
	ID16( 800, 1036, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 760 2.0 GHz, 533 MHz FSB */
static struct est_op pm90_n760[] = {
	ID16(2000, 1356, BUS133),
	ID16(1600, 1244, BUS133),
	ID16(1333, 1164, BUS133),
	ID16(1067, 1084, BUS133),
	ID16( 800,  988, BUS133),
};

/* Intel Pentium M processor 765 2.1 GHz, VID #A */
static struct est_op pm90_n765a[] = {
	ID16(2100, 1340, BUS100),
	ID16(1800, 1276, BUS100),
	ID16(1600, 1228, BUS100),
	ID16(1400, 1180, BUS100),
	ID16(1200, 1132, BUS100),
	ID16(1000, 1084, BUS100),
	ID16( 800, 1036, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 765 2.1 GHz, VID #B */
static struct est_op pm90_n765b[] = {
	ID16(2100, 1324, BUS100),
	ID16(1800, 1260, BUS100),
	ID16(1600, 1212, BUS100),
	ID16(1400, 1180, BUS100),
	ID16(1200, 1132, BUS100),
	ID16(1000, 1084, BUS100),
	ID16( 800, 1036, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 765 2.1 GHz, VID #C */
static struct est_op pm90_n765c[] = {
	ID16(2100, 1308, BUS100),
	ID16(1800, 1244, BUS100),
	ID16(1600, 1212, BUS100),
	ID16(1400, 1164, BUS100),
	ID16(1200, 1116, BUS100),
	ID16(1000, 1084, BUS100),
	ID16( 800, 1036, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 765 2.1 GHz, VID #E */
static struct est_op pm90_n765e[] = {
	ID16(2100, 1356, BUS100),
	ID16(1800, 1292, BUS100),
	ID16(1600, 1244, BUS100),
	ID16(1400, 1196, BUS100),
	ID16(1200, 1148, BUS100),
	ID16(1000, 1100, BUS100),
	ID16( 800, 1052, BUS100),
	ID16( 600,  988, BUS100),
};

/* Intel Pentium M processor 770 2.13 GHz */
static struct est_op pm90_n770[] = {
	ID16(2133, 1356, BUS133),
	ID16(1867, 1292, BUS133),
	ID16(1600, 1212, BUS133),
	ID16(1333, 1148, BUS133),
	ID16(1067, 1068, BUS133),
	ID16( 800,  988, BUS133),
};

/*
 * VIA C7-M 500 MHz FSB, 400 MHz FSB, and ULV variants.
 * Data from the "VIA C7-M Processor BIOS Writer's Guide (v2.17)" datasheet.
 */

/* 1.00GHz Centaur C7-M ULV */
static struct est_op C7M_770_ULV[] = {
	ID16(1000,  844, BUS100),
	ID16( 800,  796, BUS100),
	ID16( 600,  796, BUS100),
	ID16( 400,  796, BUS100),
};

/* 1.00GHz Centaur C7-M ULV */
static struct est_op C7M_779_ULV[] = {
	ID16(1000,  796, BUS100),
	ID16( 800,  796, BUS100),
	ID16( 600,  796, BUS100),
	ID16( 400,  796, BUS100),
};

/* 1.20GHz Centaur C7-M ULV */
static struct est_op C7M_772_ULV[] = {
	ID16(1200,  844, BUS100),
	ID16(1000,  844, BUS100),
	ID16( 800,  828, BUS100),
	ID16( 600,  796, BUS100),
	ID16( 400,  796, BUS100),
};

/* 1.50GHz Centaur C7-M ULV */
static struct est_op C7M_775_ULV[] = {
	ID16(1500,  956, BUS100),
	ID16(1400,  940, BUS100),
	ID16(1000,  860, BUS100),
	ID16( 800,  828, BUS100),
	ID16( 600,  796, BUS100),
	ID16( 400,  796, BUS100),
};

/* 1.20GHz Centaur C7-M 400 MHz FSB */
static struct est_op C7M_771[] = {
	ID16(1200,  860, BUS100),
	ID16(1000,  860, BUS100),
	ID16( 800,  844, BUS100),
	ID16( 600,  844, BUS100),
	ID16( 400,  844, BUS100),
};

/* 1.50GHz Centaur C7-M 400 MHz FSB */
static struct est_op C7M_754[] = {
	ID16(1500, 1004, BUS100),
	ID16(1400,  988, BUS100),
	ID16(1000,  940, BUS100),
	ID16( 800,  844, BUS100),
	ID16( 600,  844, BUS100),
	ID16( 400,  844, BUS100),
};

/* 1.60GHz Centaur C7-M 400 MHz FSB */
static struct est_op C7M_764[] = {
	ID16(1600, 1084, BUS100),
	ID16(1400, 1052, BUS100),
	ID16(1000, 1004, BUS100),
	ID16( 800,  844, BUS100),
	ID16( 600,  844, BUS100),
	ID16( 400,  844, BUS100),
};

/* 1.80GHz Centaur C7-M 400 MHz FSB */
static struct est_op C7M_784[] = {
	ID16(1800, 1148, BUS100),
	ID16(1600, 1100, BUS100),
	ID16(1400, 1052, BUS100),
	ID16(1000, 1004, BUS100),
	ID16( 800,  844, BUS100),
	ID16( 600,  844, BUS100),
	ID16( 400,  844, BUS100),
};

/* 2.00GHz Centaur C7-M 400 MHz FSB */
static struct est_op C7M_794[] = {
	ID16(2000, 1148, BUS100),
	ID16(1800, 1132, BUS100),
	ID16(1600, 1100, BUS100),
	ID16(1400, 1052, BUS100),
	ID16(1000, 1004, BUS100),
	ID16( 800,  844, BUS100),
	ID16( 600,  844, BUS100),
	ID16( 400,  844, BUS100),
};

/* 1.60GHz Centaur C7-M 533 MHz FSB */
static struct est_op C7M_765[] = {
	ID16(1600, 1084, BUS133),
	ID16(1467, 1052, BUS133),
	ID16(1200, 1004, BUS133),
	ID16( 800,  844, BUS133),
	ID16( 667,  844, BUS133),
	ID16( 533,  844, BUS133),
};

/* 2.00GHz Centaur C7-M 533 MHz FSB */
static struct est_op C7M_785[] = {
	ID16(1867, 1148, BUS133),
	ID16(1600, 1100, BUS133),
	ID16(1467, 1052, BUS133),
	ID16(1200, 1004, BUS133),
	ID16( 800,  844, BUS133),
	ID16( 667,  844, BUS133),
	ID16( 533,  844, BUS133),
};

/* 2.00GHz Centaur C7-M 533 MHz FSB */
static struct est_op C7M_795[] = {
	ID16(2000, 1148, BUS133),
	ID16(1867, 1132, BUS133),
	ID16(1600, 1100, BUS133),
	ID16(1467, 1052, BUS133),
	ID16(1200, 1004, BUS133),
	ID16( 800,  844, BUS133),
	ID16( 667,  844, BUS133),
	ID16( 533,  844, BUS133),
};

/* 1.00GHz VIA Eden 90nm 'Esther' */
static struct est_op eden90_1000[] = {
	ID16(1000,  844, BUS100),
	ID16( 800,  844, BUS100),
	ID16( 600,  844, BUS100),
	ID16( 400,  844, BUS100),
};

struct fqlist {
	int vendor: 5;
	unsigned bus_clk : 1;
	unsigned n : 5;
	struct est_op *table;
};

#define ENTRY(ven, bus_clk, tab) \
	{ CPUVENDOR_##ven, bus_clk == BUS133 ? 1 : 0, nitems(tab), tab }

#define BUS_CLK(fqp) ((fqp)->bus_clk ? BUS133 : BUS100)

static struct fqlist est_cpus[] = {
	ENTRY(INTEL, BUS100, pm130_900_ulv),
	ENTRY(INTEL, BUS100, pm130_1000_ulv),
	ENTRY(INTEL, BUS100, pm130_1100_ulv),
	ENTRY(INTEL, BUS100, pm130_1100_lv),
	ENTRY(INTEL, BUS100, pm130_1200_lv),
	ENTRY(INTEL, BUS100, pm130_1300_lv),
	ENTRY(INTEL, BUS100, pm130_1300),
	ENTRY(INTEL, BUS100, pm130_1400),
	ENTRY(INTEL, BUS100, pm130_1500),
	ENTRY(INTEL, BUS100, pm130_1600),
	ENTRY(INTEL, BUS100, pm130_1700),

	ENTRY(INTEL, BUS100, pm90_n723),
	ENTRY(INTEL, BUS100, pm90_n733g),
	ENTRY(INTEL, BUS100, pm90_n733h),
	ENTRY(INTEL, BUS100, pm90_n733i),
	ENTRY(INTEL, BUS100, pm90_n733j),
	ENTRY(INTEL, BUS100, pm90_n733k),
	ENTRY(INTEL, BUS100, pm90_n733l),
	ENTRY(INTEL, BUS100, pm90_n753g),
	ENTRY(INTEL, BUS100, pm90_n753h),
	ENTRY(INTEL, BUS100, pm90_n753i),
	ENTRY(INTEL, BUS100, pm90_n753j),
	ENTRY(INTEL, BUS100, pm90_n753k),
	ENTRY(INTEL, BUS100, pm90_n753l),
	ENTRY(INTEL, BUS100, pm90_n773g),
	ENTRY(INTEL, BUS100, pm90_n773h),
	ENTRY(INTEL, BUS100, pm90_n773i),
	ENTRY(INTEL, BUS100, pm90_n773j),
	ENTRY(INTEL, BUS100, pm90_n773k),
	ENTRY(INTEL, BUS100, pm90_n773l),
	ENTRY(INTEL, BUS100, pm90_n738),
	ENTRY(INTEL, BUS100, pm90_n758),
	ENTRY(INTEL, BUS100, pm90_n778),

	ENTRY(INTEL, BUS133, pm90_n710),
	ENTRY(INTEL, BUS100, pm90_n715a),
	ENTRY(INTEL, BUS100, pm90_n715b),
	ENTRY(INTEL, BUS100, pm90_n715c),
	ENTRY(INTEL, BUS100, pm90_n715d),
	ENTRY(INTEL, BUS100, pm90_n725a),
	ENTRY(INTEL, BUS100, pm90_n725b),
	ENTRY(INTEL, BUS100, pm90_n725c),
	ENTRY(INTEL, BUS100, pm90_n725d),
	ENTRY(INTEL, BUS133, pm90_n730),
	ENTRY(INTEL, BUS100, pm90_n735a),
	ENTRY(INTEL, BUS100, pm90_n735b),
	ENTRY(INTEL, BUS100, pm90_n735c),
	ENTRY(INTEL, BUS100, pm90_n735d),
	ENTRY(INTEL, BUS133, pm90_n740),
	ENTRY(INTEL, BUS100, pm90_n745a),
	ENTRY(INTEL, BUS100, pm90_n745b),
	ENTRY(INTEL, BUS100, pm90_n745c),
	ENTRY(INTEL, BUS100, pm90_n745d),
	ENTRY(INTEL, BUS133, pm90_n750),
	ENTRY(INTEL, BUS100, pm90_n755a),
	ENTRY(INTEL, BUS100, pm90_n755b),
	ENTRY(INTEL, BUS100, pm90_n755c),
	ENTRY(INTEL, BUS100, pm90_n755d),
	ENTRY(INTEL, BUS133, pm90_n760),
	ENTRY(INTEL, BUS100, pm90_n765a),
	ENTRY(INTEL, BUS100, pm90_n765b),
	ENTRY(INTEL, BUS100, pm90_n765c),
	ENTRY(INTEL, BUS100, pm90_n765e),
	ENTRY(INTEL, BUS133, pm90_n770),

	ENTRY(VIA,   BUS100, C7M_770_ULV),
	ENTRY(VIA,   BUS100, C7M_779_ULV),
	ENTRY(VIA,   BUS100, C7M_772_ULV),
	ENTRY(VIA,   BUS100, C7M_771),
	ENTRY(VIA,   BUS100, C7M_775_ULV),
	ENTRY(VIA,   BUS100, C7M_754),
	ENTRY(VIA,   BUS100, C7M_764),
	ENTRY(VIA,   BUS133, C7M_765),
	ENTRY(VIA,   BUS100, C7M_784),
	ENTRY(VIA,   BUS133, C7M_785),
	ENTRY(VIA,   BUS100, C7M_794),
	ENTRY(VIA,   BUS133, C7M_795),

	ENTRY(VIA,   BUS100, eden90_1000),
};

#define MSR2MHZ(msr, bus) \
	(((((int) (msr) >> 8) & 0xff) * (bus) + 50) / 100)
#define MSR2MV(msr) \
	(((int) (msr) & 0xff) * 16 + 700)

static struct fqlist *est_fqlist;

extern int setperf_prio;
extern int perflevel;

void p4_get_bus_clock(struct cpu_info *);
void p3_get_bus_clock(struct cpu_info *);
void cyrix3_get_bus_clock(struct cpu_info *);

#if NACPICPU > 0
struct fqlist * est_acpi_init(void);
void est_acpi_pss_changed(struct acpicpu_pss *, int);

struct fqlist *
est_acpi_init()
{
	struct acpicpu_pss *pss;
	struct fqlist *acpilist;
	int nstates, i;

	if ((nstates = acpicpu_fetch_pss(&pss)) == 0)
		goto nolist;

	if ((acpilist = malloc(sizeof(struct fqlist), M_DEVBUF, M_NOWAIT))
	    == NULL)
		goto nolist;

	if ((acpilist->table = mallocarray(nstates, sizeof(struct est_op),
	    M_DEVBUF, M_NOWAIT)) == NULL)
		goto notable;

	acpilist->n = nstates;

	for (i = 0; i < nstates; i++) {
		acpilist->table[i].mhz = pss[i].pss_core_freq;
		acpilist->table[i].ctrl = pss[i].pss_ctrl;
	}

	acpicpu_set_notify(est_acpi_pss_changed);

	return acpilist;

notable:
	free(acpilist, M_DEVBUF, 0);
	acpilist = NULL;
nolist:
	return NULL;
}

void
est_acpi_pss_changed(struct acpicpu_pss *pss, int npss)
{
	struct fqlist *acpilist;
	int needtran = 1, i;
	u_int64_t msr;
	u_int16_t cur;

	msr = rdmsr(MSR_PERF_STATUS);
	cur = msr & 0xffff;

	if ((acpilist = malloc(sizeof(struct fqlist), M_DEVBUF, M_NOWAIT))
	    == NULL) {
		printf("est_acpi_pss_changed: cannot allocate memory for new "
		    "est state");
		return;
	}

	if ((acpilist->table = mallocarray(npss, sizeof(struct est_op),
	    M_DEVBUF, M_NOWAIT)) == NULL) {
		printf("est_acpi_pss_changed: cannot allocate memory for new "
		    "operating points");
		free(acpilist, M_DEVBUF, 0);
		return;
	}

	for (i = 0; i < npss; i++) {
		acpilist->table[i].mhz = pss[i].pss_core_freq;
		acpilist->table[i].ctrl = pss[i].pss_ctrl;
		if (pss[i].pss_ctrl == cur)
			needtran = 0;
	}

	free(est_fqlist->table, M_DEVBUF, 0);
	free(est_fqlist, M_DEVBUF, 0);
	est_fqlist = acpilist;

	if (needtran) {
		est_setperf(perflevel);
	}
}
#endif

void
est_init(struct cpu_info *ci, int vendor)
{
	const char *cpu_device = ci->ci_dev.dv_xname;
	int i, low, high;
	u_int64_t msr;
	u_int16_t idhi, idlo, cur;
	u_int8_t crhi, crlo, crcur;
	struct fqlist *fql;
	struct fqlist *fake_fqlist;
	struct est_op *fake_table;

	if (setperf_prio > 3)
		return;

#if NACPICPU > 0
	est_fqlist = est_acpi_init();
#endif

	/* bus_clock is only used if we can't get values from ACPI */
	if (est_fqlist == NULL) {
		if (vendor == CPUVENDOR_VIA)
			cyrix3_get_bus_clock(ci);
		else if (ci->ci_family == 0xf)
			p4_get_bus_clock(ci);
		else if (ci->ci_family == 6)
			p3_get_bus_clock(ci);
	}

	/*
	 * Interpreting the values of PERF_STATUS is not valid
	 * on recent processors so don't do it on anything unknown
	 */
	if (est_fqlist == NULL && bus_clock != 0) {
		msr = rdmsr(MSR_PERF_STATUS);
		idhi = (msr >> 32) & 0xffff;
		idlo = (msr >> 48) & 0xffff;
		cur = msr & 0xffff;
		crhi = (idhi  >> 8) & 0xff;
		crlo = (idlo  >> 8) & 0xff;
		crcur = (cur >> 8) & 0xff;
		/*
		 * Find an entry which matches (vendor, bus_clock, idhi, idlo)
		 */
		for (i = 0; i < nitems(est_cpus); i++) {
			fql = &est_cpus[i];
			if (vendor == fql->vendor && bus_clock == BUS_CLK(fql)
			    && idhi == fql->table[0].ctrl
			    && idlo == fql->table[fql->n - 1].ctrl) {
				est_fqlist = fql;
				break;
			}
		}
	}

	if (est_fqlist == NULL && bus_clock != 0) {
		if (crhi == 0 || crcur == 0 || crlo > crhi ||
		    crcur < crlo || crcur > crhi) {
			/*
			 * Do complain about other weirdness, because we first
			 * want to know about it, before we decide what to do
			 * with it.
			 */
			printf("%s: EST: strange msr value 0x%016llx\n",
			    cpu_device, msr);
			return;
		}
		if (crlo == 0 || crhi == crlo) {
			/*
			 * Don't complain about these cases, and silently
			 * disable EST: - A lowest clock ratio of 0, which
			 * seems to happen on all Pentium 4's that report EST.
			 * - An equal highest and lowest clock ratio, which
			 * happens on at least the Core 2 Duo X6800, maybe on
			 * newer models too.
			 */
			return;
		}

		printf("%s: unknown Enhanced SpeedStep CPU, msr 0x%016llx\n",
		    cpu_device, msr);
		/*
		 * Generate a fake table with the power states we know.
		 */

		if ((fake_fqlist = malloc(sizeof(struct fqlist), M_DEVBUF,
		    M_NOWAIT)) == NULL) {
			printf("%s: EST: cannot allocate memory for fake "
			    "list\n", cpu_device);
			return;
		}

		if ((fake_table = malloc(sizeof(struct est_op) * 3, M_DEVBUF,
		     M_NOWAIT)) == NULL) {
			free(fake_fqlist, M_DEVBUF, 0);
			printf("%s: EST: cannot allocate memory for fake "
			    "table\n", cpu_device);
			return;
		}
		fake_table[0].ctrl = idhi;
		fake_table[0].mhz = MSR2MHZ(idhi, bus_clock);
		if (cur == idhi || cur == idlo) {
			printf("%s: using only highest and lowest power "
			       "states\n", cpu_device);

			fake_table[1].ctrl = idlo;
			fake_table[1].mhz = MSR2MHZ(idlo, bus_clock);
			fake_fqlist->n = 2;
		} else {
			printf("%s: using only highest, current and lowest "
			    "power states\n", cpu_device);

			fake_table[1].ctrl = cur;
			fake_table[1].mhz = MSR2MHZ(cur, bus_clock);

			fake_table[2].ctrl = idlo;
			fake_table[2].mhz = MSR2MHZ(idlo, bus_clock);
			fake_fqlist->n = 3;
		}

		fake_fqlist->vendor = vendor;
		fake_fqlist->table = fake_table;
		est_fqlist = fake_fqlist;
	}

	if (est_fqlist == NULL)
		return;

	if (est_fqlist->n < 2)
		goto nospeedstep;

	low = est_fqlist->table[est_fqlist->n - 1].mhz;
	high = est_fqlist->table[0].mhz;
	if (low == high)
		goto nospeedstep;

	perflevel = (cpuspeed - low) * 100 / (high - low);

	printf("%s: Enhanced SpeedStep %d MHz", cpu_device, cpuspeed);

	/*
	 * OK, tell the user the available frequencies.
	 */
	printf(": speeds: ");
	for (i = 0; i < est_fqlist->n; i++)
		printf("%d%s", est_fqlist->table[i].mhz, i < est_fqlist->n - 1
		    ?  ", " : " MHz\n");

	cpu_setperf = est_setperf;
	setperf_prio = 3;

	return;

nospeedstep:
	/*
	 * While est_fqlist can point into the static est_cpus[],
	 * it can't fail in that case and therefore can't reach here.
	 */
	free(est_fqlist->table, M_DEVBUF, 0);
	free(est_fqlist, M_DEVBUF, 0);
}

void
est_setperf(int level)
{
	int i;
	uint64_t msr;

	if (est_fqlist == NULL)
		return;

	i = ((level * est_fqlist->n) + 1) / 101;
	if (i >= est_fqlist->n)
		i = est_fqlist->n - 1;
	i = est_fqlist->n - 1 - i;

	msr = rdmsr(MSR_PERF_CTL);
	msr &= ~0xffffULL;
	msr |= est_fqlist->table[i].ctrl;

	wrmsr(MSR_PERF_CTL, msr);
	cpuspeed = est_fqlist->table[i].mhz;
}


code:
/*	$OpenBSD: gdt.c,v 1.35 2015/02/11 05:54:48 dlg Exp $	*/
/*	$NetBSD: gdt.c,v 1.28 2002/12/14 09:38:50 junyoung Exp $	*/

/*-
 * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by John T. Kohl and Charles M. Hannum.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * The GDT handling has two phases.  During the early lifetime of the
 * kernel there is a static gdt which will be stored in bootstrap_gdt.
 * Later, when the virtual memory is initialized, this will be
 * replaced with a maximum sized GDT.
 *
 * The bootstrap GDT area will hold the initial requirement of NGDT
 * descriptors.  The normal GDT will have a statically sized virtual memory
 * area of size MAXGDTSIZ.
 *
 * Every CPU in a system has its own copy of the GDT.  The only real difference
 * between the two are currently that there is a cpu-specific segment holding
 * the struct cpu_info of the processor, for simplicity at getting cpu_info
 * fields from assembly.  The boot processor will actually refer to the global
 * copy of the GDT as pointed to by the gdt variable.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mutex.h>

#include <uvm/uvm_extern.h>

#include <machine/gdt.h>
#include <machine/pcb.h>

union descriptor bootstrap_gdt[NGDT];
union descriptor *gdt = bootstrap_gdt;

int gdt_next;		/* next available slot for sweeping */
int gdt_free;		/* next free slot; terminated with GNULL_SEL */

struct mutex gdt_lock_store = MUTEX_INITIALIZER(IPL_HIGH);

int gdt_get_slot(void);
void gdt_put_slot(int);

/*
 * Lock and unlock the GDT.
 */
#define gdt_lock()	(mtx_enter(&gdt_lock_store))
#define gdt_unlock()	(mtx_leave(&gdt_lock_store))

/* XXX needs spinlocking if we ever mean to go finegrained. */
void
setgdt(int sel, void *base, size_t limit, int type, int dpl, int def32,
    int gran)
{
	struct segment_descriptor *sd = &gdt[sel].sd;
	CPU_INFO_ITERATOR cii;
	struct cpu_info *ci;

	KASSERT(sel < MAXGDTSIZ);

	setsegment(sd, base, limit, type, dpl, def32, gran);
	CPU_INFO_FOREACH(cii, ci)
		if (ci->ci_gdt != NULL && ci->ci_gdt != gdt)
			ci->ci_gdt[sel].sd = *sd;
}

/*
 * Initialize the GDT subsystem.  Called from autoconf().
 */
void
gdt_init()
{
	struct vm_page *pg;
	vaddr_t va;
	struct cpu_info *ci = &cpu_info_primary;

	gdt_next = NGDT;
	gdt_free = GNULL_SEL;

	gdt = (union descriptor *)uvm_km_valloc(kernel_map, MAXGDTSIZ);
	for (va = (vaddr_t)gdt; va < (vaddr_t)gdt + MAXGDTSIZ;
	    va += PAGE_SIZE) {
		pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_ZERO);
		if (pg == NULL)
			panic("gdt_init: no pages");
		pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg),
		    PROT_READ | PROT_WRITE);
	}
	bcopy(bootstrap_gdt, gdt, NGDT * sizeof(union descriptor));
	ci->ci_gdt = gdt;
	setsegment(&ci->ci_gdt[GCPU_SEL].sd, ci, sizeof(struct cpu_info)-1,
	    SDT_MEMRWA, SEL_KPL, 0, 0);

	gdt_init_cpu(ci);
}

#ifdef MULTIPROCESSOR
/*
 * Allocate shadow GDT for a slave cpu.
 */
void
gdt_alloc_cpu(struct cpu_info *ci)
{
	struct vm_page *pg;
	vaddr_t va;

	ci->ci_gdt = (union descriptor *)uvm_km_valloc(kernel_map, MAXGDTSIZ);
	uvm_map_pageable(kernel_map, (vaddr_t)ci->ci_gdt,
	    (vaddr_t)ci->ci_gdt + MAXGDTSIZ, FALSE, FALSE);
	for (va = (vaddr_t)ci->ci_gdt; va < (vaddr_t)ci->ci_gdt + MAXGDTSIZ;
	    va += PAGE_SIZE) {
		pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_ZERO);
		if (pg == NULL)
			panic("gdt_init: no pages");
		pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg),
		    PROT_READ | PROT_WRITE);
	}
	bzero(ci->ci_gdt, MAXGDTSIZ);
	bcopy(gdt, ci->ci_gdt, MAXGDTSIZ);
	setsegment(&ci->ci_gdt[GCPU_SEL].sd, ci, sizeof(struct cpu_info)-1,
	    SDT_MEMRWA, SEL_KPL, 0, 0);
}
#endif	/* MULTIPROCESSOR */

/*
 * Load appropriate gdt descriptor; we better be running on *ci
 * (for the most part, this is how a cpu knows who it is).
 */
void
gdt_init_cpu(struct cpu_info *ci)
{
	struct region_descriptor region;

	setregion(&region, ci->ci_gdt, MAXGDTSIZ - 1);
	lgdt(&region);
}

/*
 * Allocate a GDT slot as follows:
 * 1) If there are entries on the free list, use those.
 * 2) If there are fewer than MAXGDTSIZ entries in use, there are free slots
 *    near the end that we can sweep through.
 */
int
gdt_get_slot()
{
	int slot;

	gdt_lock();

	if (gdt_free != GNULL_SEL) {
		slot = gdt_free;
		gdt_free = gdt[slot].gd.gd_selector;
	} else {
		if (gdt_next >= MAXGDTSIZ)
			panic("gdt_get_slot: out of GDT descriptors");
		slot = gdt_next++;
	}

	gdt_unlock();
	return (slot);
}

/*
 * Deallocate a GDT slot, putting it on the free list.
 */
void
gdt_put_slot(int slot)
{

	gdt_lock();

	gdt[slot].gd.gd_type = SDT_SYSNULL;
	gdt[slot].gd.gd_selector = gdt_free;
	gdt_free = slot;

	gdt_unlock();
}

int
tss_alloc(struct pcb *pcb)
{
	int slot;

	slot = gdt_get_slot();
	setgdt(slot, &pcb->pcb_tss, sizeof(struct pcb) - 1,
	    SDT_SYS386TSS, SEL_KPL, 0, 0);
	return GSEL(slot, SEL_KPL);
}

void
tss_free(int sel)
{

	gdt_put_slot(IDXSEL(sel));
}

#ifdef USER_LDT
/*
 * Caller must have pmap locked for both of these functions.
 */
void
ldt_alloc(struct pmap *pmap, union descriptor *ldt, size_t len)
{
	int slot;

	slot = gdt_get_slot();
	setgdt(slot, ldt, len - 1, SDT_SYSLDT, SEL_KPL, 0, 0);
	pmap->pm_ldt_sel = GSEL(slot, SEL_KPL);
}

void
ldt_free(struct pmap *pmap)
{
	int slot;

	slot = IDXSEL(pmap->pm_ldt_sel);

	gdt_put_slot(slot);
}
#endif /* USER_LDT */


code:
#	$OpenBSD: genassym.cf,v 1.37 2015/04/12 18:37:53 mlarkin Exp $
#
# Copyright (c) 1982, 1990 The Regents of the University of California.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. Neither the name of the University nor the names of its contributors
#    may be used to endorse or promote products derived from this software
#    without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
#	@(#)genassym.c	7.8 (Berkeley) 5/7/91
#

include <sys/param.h>
include <sys/proc.h>
include <sys/resourcevar.h>
include <sys/device.h>
include <sys/user.h>
include <sys/mbuf.h>
include <sys/socketvar.h>
include <netinet/in.h>
include <netinet/ip.h>
include <netinet/ip_var.h>

include <uvm/uvm_extern.h>

include <machine/trap.h>
include <machine/pmap.h>
include <machine/vmparam.h>
include <machine/mutex.h>

if COMPAT_LINUX > 0
include <machine/linux_machdep.h>
endif

if MULTIPROCESSOR
include <machine/cpu.h>
endif

include "isa.h"
if NISA > 0
include <i386/isa/isa_machdep.h>
endif

export	SRUN
export	SONPROC

# values for page tables
export	PDSLOT_KERN
export	PDSLOT_PTE
export	PDSLOT_APTE
export	NKPTP_MIN

# values for virtual memory
export	VM_MAXUSER_ADDRESS

# proc fields and values
struct	proc
member	p_addr
member	p_priority
member	p_stat
member	p_wchan
member	p_vmspace
member	p_flag
member	p_cpu
member	P_MD_ASTPENDING	p_md.md_astpending

export	P_SYSTEM

# interrupt/fault metering
struct	uvmexp		V_
member	TRAP		traps
member	INTR		intrs

# pcb fields
struct	pcb
member	pcb_cr3
member	pcb_ebp
member	pcb_esp
member	pcb_cr0
member	pcb_ldt
member	pcb_ldt_sel
member	pcb_onfault
member	pcb_fpcpu
member	pcb_flags
member	pcb_pmap
export	PCB_SAVECTX

# frame definitions
struct	trapframe
member	tf_cs
member	tf_trapno
member	tf_eflags
member	tf_eip
member	tf_err
member	tf_eax
member	tf_esp
define	FRAMESIZE	sizeof(struct trapframe)

# interrupt frame definitions
struct intrframe
member if_ppl

# signal handling
struct	sigframe	SIGF_
member	HANDLER		sf_handler
member	SC		sf_sc
struct	sigcontext

# mbuf fields
struct	mbuf
member	m_next
member	m_data
member	m_len

# Linux frame offsets
ifdef COMPAT_LINUX
struct	linux_sigframe		LINUX_SIGF_
member	HANDLER			sf_handler
member	SC			sf_sc
struct	linux_sigcontext	LINUX_
endif

# Interrupt metering
if NISA > 0
struct	intrhand
member	ih_fun
member	ih_arg
member	IH_COUNT		ih_count.ec_count
member	ih_next
endif

struct mutex
member	mtx_lock
member	mtx_wantipl
member	mtx_oldipl
member	mtx_owner

define	IP_SRC			offsetof(struct ip, ip_src)
define	IP_DST			offsetof(struct ip, ip_dst)

define	P_MD_TSS_SEL		offsetof(struct proc, p_md.md_tss_sel)

define	CPU_INFO_SELF		offsetof(struct cpu_info, ci_self)
define	CPU_INFO_APICID		offsetof(struct cpu_info, ci_apicid)
define	CPU_INFO_CURPROC	offsetof(struct cpu_info, ci_curproc)
define	CPU_INFO_CURPCB		offsetof(struct cpu_info, ci_curpcb)
define	CPU_INFO_NAME		offsetof(struct cpu_info, ci_dev.dv_xname)
define	CPU_INFO_IDLE_PCB	offsetof(struct cpu_info, ci_idle_pcb)
define	CPU_INFO_IDLE_TSS_SEL	offsetof(struct cpu_info, ci_idle_tss_sel)
define	CPU_INFO_LEVEL		offsetof(struct cpu_info, ci_level)
define	CPU_INFO_VENDOR		offsetof(struct cpu_info, ci_vendor[0])
define	CPU_INFO_SIGNATURE	offsetof(struct cpu_info, ci_signature)
define	CPU_INFO_RESCHED	offsetof(struct cpu_info, ci_want_resched)
define	CPU_INFO_GDT		offsetof(struct cpu_info, ci_gdt)
define	CPU_INFO_IPENDING	offsetof(struct cpu_info, ci_ipending)
define	CPU_INFO_IMASK		offsetof(struct cpu_info, ci_imask)
define	CPU_INFO_IUNMASK	offsetof(struct cpu_info, ci_iunmask)
define	CPU_INFO_ILEVEL		offsetof(struct cpu_info, ci_ilevel)
define	CPU_INFO_IDEPTH		offsetof(struct cpu_info, ci_idepth)
define	CPU_INFO_ISOURCES	offsetof(struct cpu_info, ci_isources)
ifdef DIAGNOSTIC
define	CPU_INFO_MUTEX_LEVEL	offsetof(struct cpu_info, ci_mutex_level)
endif
define	CPU_INFO_CURPMAP	offsetof(struct cpu_info, ci_curpmap)

struct pmap
member pm_pdirpa

define	SIZEOF_CPU_INFO		sizeof(struct cpu_info)


code:
/*	$OpenBSD: hibernate_machdep.c,v 1.46 2015/04/26 11:09:32 kettenis Exp $	*/

/*
 * Copyright (c) 2011 Mike Larkin <mlarkin@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/disk.h>
#include <sys/disklabel.h>
#include <sys/hibernate.h>
#include <sys/timeout.h>
#include <sys/malloc.h>

#include <dev/acpi/acpivar.h>

#include <uvm/uvm_extern.h>
#include <uvm/uvm_pmemrange.h>

#include <machine/hibernate.h>
#include <machine/hibernate_var.h>
#include <machine/kcore.h>
#include <machine/pmap.h>

#ifdef MULTIPROCESSOR
#include <machine/mpbiosvar.h>
#endif /* MULTIPROCESSOR */

#include "acpi.h"
#include "wd.h"
#include "ahci.h"
#include "softraid.h"
#include "sd.h"

/* Hibernate support */
void    hibernate_enter_resume_4k_pte(vaddr_t, paddr_t);
void    hibernate_enter_resume_4k_pde(vaddr_t);
void    hibernate_enter_resume_4m_pde(vaddr_t, paddr_t);

extern	void hibernate_resume_machdep(void);
extern	void hibernate_flush(void);
extern	caddr_t start, end;
extern	int ndumpmem;
extern  struct dumpmem dumpmem[];
extern	bios_memmap_t *bios_memmap;
extern	struct hibernate_state *hibernate_state;

/*
 * Hibernate always uses non-PAE page tables during resume, so
 * redefine masks and pt_entry_t sizes in case PAE is in use.
 */
#define PAGE_MASK_L2    (NBPD - 1)
typedef uint32_t pt_entry_t;

/*
 * i386 MD Hibernate functions
 *
 * see i386 hibernate.h for lowmem layout used during hibernate
 */

/*
 * Returns the hibernate write I/O function to use on this machine
 */
hibio_fn
get_hibernate_io_function(dev_t dev)
{
	char *blkname = findblkname(major(dev));

	if (blkname == NULL)
		return NULL;

#if NWD > 0
	if (strcmp(blkname, "wd") == 0) {
		extern int wd_hibernate_io(dev_t dev, daddr_t blkno,
		    vaddr_t addr, size_t size, int op, void *page);
		return wd_hibernate_io;
	}
#endif
#if NSD > 0
	if (strcmp(blkname, "sd") == 0) {
		extern struct cfdriver sd_cd;
		extern int ahci_hibernate_io(dev_t dev, daddr_t blkno,
		    vaddr_t addr, size_t size, int op, void *page);
		extern int sr_hibernate_io(dev_t dev, daddr_t blkno,
		    vaddr_t addr, size_t size, int op, void *page);
		struct device *dv = disk_lookup(&sd_cd, DISKUNIT(dev));

#if NAHCI > 0
		if (dv && dv->dv_parent && dv->dv_parent->dv_parent &&
		    strcmp(dv->dv_parent->dv_parent->dv_cfdata->cf_driver->cd_name,
		    "ahci") == 0)
			return ahci_hibernate_io;
#endif
#if NSOFTRAID > 0
		if (dv && dv->dv_parent && dv->dv_parent->dv_parent &&
		    strcmp(dv->dv_parent->dv_parent->dv_cfdata->cf_driver->cd_name,
		    "softraid") == 0)
			return sr_hibernate_io;
	}
#endif
#endif /* NSD > 0 */
	return NULL;
}

/*
 * Gather MD-specific data and store into hiber_info
 */
int
get_hibernate_info_md(union hibernate_info *hiber_info)
{
	int i;
	bios_memmap_t *bmp;

	/* Calculate memory ranges */
	hiber_info->nranges = ndumpmem;
	hiber_info->image_size = 0;

	for (i = 0; i < ndumpmem; i++) {
		hiber_info->ranges[i].base = dumpmem[i].start * PAGE_SIZE;
		hiber_info->ranges[i].end = dumpmem[i].end * PAGE_SIZE;
		hiber_info->image_size += hiber_info->ranges[i].end -
		    hiber_info->ranges[i].base;
	}

#if NACPI > 0
	if (hiber_info->nranges >= VM_PHYSSEG_MAX)
		return (1);
	hiber_info->ranges[hiber_info->nranges].base = ACPI_TRAMPOLINE;
	hiber_info->ranges[hiber_info->nranges].end =
	    hiber_info->ranges[hiber_info->nranges].base + PAGE_SIZE;
	hiber_info->image_size += PAGE_SIZE;
	hiber_info->nranges++;
#endif
#ifdef MULTIPROCESSOR
	if (hiber_info->nranges >= VM_PHYSSEG_MAX)
		return (1);
	hiber_info->ranges[hiber_info->nranges].base = MP_TRAMPOLINE;
	hiber_info->ranges[hiber_info->nranges].end =
	    hiber_info->ranges[hiber_info->nranges].base + PAGE_SIZE;
	hiber_info->image_size += PAGE_SIZE;
	hiber_info->nranges++;
#endif

	for (bmp = bios_memmap; bmp->type != BIOS_MAP_END; bmp++) {
		/* Skip non-NVS ranges (already processed) */
		if (bmp->type != BIOS_MAP_NVS)
			continue;
		if (hiber_info->nranges >= VM_PHYSSEG_MAX)
			return (1);

		i = hiber_info->nranges;
		hiber_info->ranges[i].base = round_page(bmp->addr);
		hiber_info->ranges[i].end = trunc_page(bmp->addr + bmp->size);
		hiber_info->image_size += hiber_info->ranges[i].end -
			hiber_info->ranges[i].base;
		hiber_info->nranges++;
	}

	hibernate_sort_ranges(hiber_info);

	return (0);
}

/*
 * Enter a mapping for va->pa in the resume pagetable, using
 * the specified size.
 *
 * size : 0 if a 4KB mapping is desired
 *        1 if a 4MB mapping is desired
 */
void
hibernate_enter_resume_mapping(vaddr_t va, paddr_t pa, int size)
{
	if (size)
		return hibernate_enter_resume_4m_pde(va, pa);
	else
		return hibernate_enter_resume_4k_pte(va, pa);
}

/*
 * Enter a 4MB PDE mapping for the supplied VA/PA into the resume-time pmap
 */
void
hibernate_enter_resume_4m_pde(vaddr_t va, paddr_t pa)
{
	pt_entry_t *pde, npde;

	pde = s4pde_4m(va);
	npde = (pa & HIB_PD_MASK) | PG_RW | PG_V | PG_M | PG_PS;
	*pde = npde;
}

/*
 * Enter a 4KB PTE mapping for the supplied VA/PA into the resume-time pmap.
 */
void
hibernate_enter_resume_4k_pte(vaddr_t va, paddr_t pa)
{
	pt_entry_t *pte, npte;

	pte = s4pte_4k(va);
	npte = (pa & PMAP_PA_MASK) | PG_RW | PG_V | PG_M;
	*pte = npte;
}

/*
 * Enter a 4KB PDE mapping for the supplied VA into the resume-time pmap.
 */
void
hibernate_enter_resume_4k_pde(vaddr_t va)
{
	pt_entry_t *pde, npde;

	pde = s4pde_4k(va);
	npde = (HIBERNATE_PT_PAGE & PMAP_PA_MASK) | PG_RW | PG_V | PG_M;
	*pde = npde;
}

/*
 * Create the resume-time page table. This table maps the image(pig) area,
 * the kernel text area, and various utility pages for use during resume,
 * since we cannot overwrite the resuming kernel's page table during inflate
 * and expect things to work properly.
 */
void
hibernate_populate_resume_pt(union hibernate_info *hib_info,
    paddr_t image_start, paddr_t image_end)
{
	int phys_page_number, i;
	paddr_t pa;
	vaddr_t kern_start_4m_va, kern_end_4m_va, page;
	vaddr_t piglet_start_va, piglet_end_va;
	struct pmap *kpm = pmap_kernel();

	/* Identity map PD, PT, and stack pages */
	pmap_kenter_pa(HIBERNATE_PT_PAGE, HIBERNATE_PT_PAGE, PROT_MASK);
	pmap_kenter_pa(HIBERNATE_PD_PAGE, HIBERNATE_PD_PAGE, PROT_MASK);
	pmap_kenter_pa(HIBERNATE_STACK_PAGE, HIBERNATE_STACK_PAGE, PROT_MASK);
	pmap_activate(curproc);

	bzero((caddr_t)HIBERNATE_PT_PAGE, PAGE_SIZE);
	bzero((caddr_t)HIBERNATE_PD_PAGE, PAGE_SIZE);
	bzero((caddr_t)HIBERNATE_STACK_PAGE, PAGE_SIZE);

	/* PDE for low pages */
	hibernate_enter_resume_4k_pde(0);

	/*
	 * Identity map low physical pages.
	 * See arch/i386/include/hibernate_var.h for page ranges used here.
	 */
	for (i = ACPI_TRAMPOLINE; i <= HIBERNATE_HIBALLOC_PAGE; i += PAGE_SIZE)
		hibernate_enter_resume_mapping(i, i, 0);

	/*
	 * Map current kernel VA range using 4M pages
	 */
	kern_start_4m_va = (paddr_t)&start & ~(PAGE_MASK_L2);
	kern_end_4m_va = (paddr_t)&end & ~(PAGE_MASK_L2);

	/* i386 kernels load at 2MB phys (on the 0th 4mb page) */
	phys_page_number = 0;

	for (page = kern_start_4m_va; page <= kern_end_4m_va;
	    page += NBPD, phys_page_number++) {
		pa = (paddr_t)(phys_page_number * NBPD);
		hibernate_enter_resume_mapping(page, pa, 1);
	}

	/*
	 * Identity map the image (pig) area
	 */
	phys_page_number = image_start / NBPD;
	image_start &= ~(PAGE_MASK_L2);
	image_end &= ~(PAGE_MASK_L2);
	for (page = image_start; page <= image_end ;
	    page += NBPD, phys_page_number++) {
		pa = (paddr_t)(phys_page_number * NBPD);
		hibernate_enter_resume_mapping(page, pa, 1);
	}

	/*
	 * Identity map the piglet using 4MB pages.
	 */
	phys_page_number = hib_info->piglet_pa / NBPD;

	/* VA == PA */
	piglet_start_va = hib_info->piglet_pa;
	piglet_end_va = piglet_start_va + HIBERNATE_CHUNK_SIZE * 4;

	for (page = piglet_start_va; page <= piglet_end_va;
	    page += NBPD, phys_page_number++) {
		pa = (paddr_t)(phys_page_number * NBPD);
		hibernate_enter_resume_mapping(page, pa, 1);
	}

	/*
	 * Fill last 8 slots of the new PD with the PAE PDPTEs of the
	 * kernel pmap, such that we can easily switch back into
	 * non-PAE mode.  If we're running in non-PAE mode, this will
	 * just fill the slots with zeroes.
	 */
	((uint64_t *)HIBERNATE_PD_PAGE)[508] = kpm->pm_pdidx[0];
	((uint64_t *)HIBERNATE_PD_PAGE)[509] = kpm->pm_pdidx[1];
	((uint64_t *)HIBERNATE_PD_PAGE)[510] = kpm->pm_pdidx[2];
	((uint64_t *)HIBERNATE_PD_PAGE)[511] = kpm->pm_pdidx[3];

	/* Unmap MMU pages (stack remains mapped) */
	pmap_kremove(HIBERNATE_PT_PAGE, PAGE_SIZE);
	pmap_kremove(HIBERNATE_PD_PAGE, PAGE_SIZE);
	pmap_activate(curproc);
}

/*
 * During inflate, certain pages that contain our bookkeeping information
 * (eg, the chunk table, scratch pages, etc) need to be skipped over and
 * not inflated into.
 *
 * Returns 1 if the physical page at dest should be skipped, 0 otherwise
 */
int
hibernate_inflate_skip(union hibernate_info *hib_info, paddr_t dest)
{
	if (dest >= hib_info->piglet_pa &&
	    dest <= (hib_info->piglet_pa + 4 * HIBERNATE_CHUNK_SIZE))
		return (1);

	return (0);
}

void
hibernate_enable_intr_machdep(void)
{
	enable_intr();
}

void
hibernate_disable_intr_machdep(void)
{
	disable_intr();
}

#ifdef MULTIPROCESSOR
/*
 * On i386, the APs have not been hatched at the time hibernate resume is
 * called, so there is no need to quiesce them. We do want to make sure
 * however that we are on the BSP.
 */
void
hibernate_quiesce_cpus(void)
{
	KASSERT(CPU_IS_PRIMARY(curcpu()));
}
#endif /* MULTIPROCESSOR */