code:
/*	$OpenBSD: ldc.c,v 1.12 2015/03/21 18:02:58 kettenis Exp $	*/
/*
 * Copyright (c) 2009 Mark Kettenis
 *
 * 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/malloc.h>
#include <sys/systm.h>

#include <machine/bus.h>
#include <machine/hypervisor.h>

#include <sparc64/dev/ldcvar.h>

#ifdef LDC_DEBUG
#define DPRINTF(x)	printf x
#else
#define DPRINTF(x)
#endif

void	ldc_rx_ctrl_vers(struct ldc_conn *, struct ldc_pkt *);
void	ldc_rx_ctrl_rtr(struct ldc_conn *, struct ldc_pkt *);
void	ldc_rx_ctrl_rts(struct ldc_conn *, struct ldc_pkt *);
void	ldc_rx_ctrl_rdx(struct ldc_conn *, struct ldc_pkt *);

void	ldc_send_ack(struct ldc_conn *);
void	ldc_send_rtr(struct ldc_conn *);
void	ldc_send_rts(struct ldc_conn *);
void	ldc_send_rdx(struct ldc_conn *);

void
ldc_rx_ctrl(struct ldc_conn *lc, struct ldc_pkt *lp)
{
	switch (lp->ctrl) {
	case LDC_VERS:
		ldc_rx_ctrl_vers(lc, lp);
		break;

	case LDC_RTS:
		ldc_rx_ctrl_rts(lc, lp);
		break;

	case LDC_RTR:
		ldc_rx_ctrl_rtr(lc, lp);
		break;

	case LDC_RDX:
		ldc_rx_ctrl_rdx(lc, lp);
		break;

	default:
		DPRINTF(("CTRL/0x%02x/0x%02x\n", lp->stype, lp->ctrl));
		ldc_reset(lc);
		break;
	}
}

void
ldc_rx_ctrl_vers(struct ldc_conn *lc, struct ldc_pkt *lp)
{
	switch (lp->stype) {
	case LDC_INFO:
		DPRINTF(("CTRL/INFO/VERS\n"));
		if (lp->major == LDC_VERSION_MAJOR &&
		    lp->minor == LDC_VERSION_MINOR)
			ldc_send_ack(lc);
		else
			/* XXX do nothing for now. */
			;
		break;

	case LDC_ACK:
		if (lc->lc_state != LDC_SND_VERS) {
			DPRINTF(("Spurious CTRL/ACK/VERS: state %d\n",
			    lc->lc_state));
			ldc_reset(lc);
			return;
		}
		DPRINTF(("CTRL/ACK/VERS\n"));
		ldc_send_rts(lc);
		break;

	case LDC_NACK:
		DPRINTF(("CTRL/NACK/VERS\n"));
		ldc_reset(lc);
		break;

	default:
		DPRINTF(("CTRL/0x%02x/VERS\n", lp->stype));
		ldc_reset(lc);
		break;
	}
}

void
ldc_rx_ctrl_rts(struct ldc_conn *lc, struct ldc_pkt *lp)
{
	switch (lp->stype) {
	case LDC_INFO:
		if (lc->lc_state != LDC_RCV_VERS) {
			DPRINTF(("Spurious CTRL/INFO/RTS: state %d\n",
			    lc->lc_state));
			ldc_reset(lc);
			return;
		}
		DPRINTF(("CTRL/INFO/RTS\n"));
		ldc_send_rtr(lc);
		break;

	case LDC_ACK:
		DPRINTF(("CTRL/ACK/RTS\n"));
		ldc_reset(lc);
		break;

	case LDC_NACK:
		DPRINTF(("CTRL/NACK/RTS\n"));
		ldc_reset(lc);
		break;

	default:
		DPRINTF(("CTRL/0x%02x/RTS\n", lp->stype));
		ldc_reset(lc);
		break;
	}
}

void
ldc_rx_ctrl_rtr(struct ldc_conn *lc, struct ldc_pkt *lp)
{
	switch (lp->stype) {
	case LDC_INFO:
		if (lc->lc_state != LDC_SND_RTS) {
			DPRINTF(("Spurious CTRL/INFO/RTR: state %d\n",
			    lc->lc_state));
			ldc_reset(lc);
			return;
		}
		DPRINTF(("CTRL/INFO/RTR\n"));
		ldc_send_rdx(lc);
		lc->lc_start(lc);
		break;

	case LDC_ACK:
		DPRINTF(("CTRL/ACK/RTR\n"));
		ldc_reset(lc);
		break;

	case LDC_NACK:
		DPRINTF(("CTRL/NACK/RTR\n"));
		ldc_reset(lc);
		break;

	default:
		DPRINTF(("CTRL/0x%02x/RTR\n", lp->stype));
		ldc_reset(lc);
		break;
	}
}

void
ldc_rx_ctrl_rdx(struct ldc_conn *lc, struct ldc_pkt *lp)
{
	switch (lp->stype) {
	case LDC_INFO:
		if (lc->lc_state != LDC_SND_RTR) {
			DPRINTF(("Spurious CTRL/INFO/RTR: state %d\n",
			    lc->lc_state));
			ldc_reset(lc);
			return;
		}
		DPRINTF(("CTRL/INFO/RDX\n"));
		lc->lc_start(lc);
		break;

	case LDC_ACK:
		DPRINTF(("CTRL/ACK/RDX\n"));
		ldc_reset(lc);
		break;

	case LDC_NACK:
		DPRINTF(("CTRL/NACK/RDX\n"));
		ldc_reset(lc);
		break;

	default:
		DPRINTF(("CTRL/0x%02x/RDX\n", lp->stype));
		ldc_reset(lc);
		break;
	}
}

void
ldc_rx_data(struct ldc_conn *lc, struct ldc_pkt *lp)
{
	size_t len;

	if (lp->stype != LDC_INFO) {
		DPRINTF(("DATA/0x%02x\n", lp->stype));
		ldc_reset(lc);
		return;
	}

	if (lc->lc_state != LDC_SND_RTR &&
	    lc->lc_state != LDC_SND_RDX) {
		DPRINTF(("Spurious DATA/INFO: state %d\n", lc->lc_state));
		ldc_reset(lc);
		return;
	}

	if (lp->env & LDC_FRAG_START) {
		lc->lc_len = (lp->env & LDC_LEN_MASK) + 8;
		KASSERT(lc->lc_len <= sizeof(lc->lc_msg));
		memcpy((uint8_t *)lc->lc_msg, lp, lc->lc_len);
	} else {
		len = (lp->env & LDC_LEN_MASK);
		if (lc->lc_len + len > sizeof(lc->lc_msg)) {
			DPRINTF(("Buffer overrun\n"));
			ldc_reset(lc);
			return;
		}
		memcpy(((uint8_t *)lc->lc_msg) + lc->lc_len, &lp->major, len);
		lc->lc_len += len;
	}

	if (lp->env & LDC_FRAG_STOP)
		lc->lc_rx_data(lc, (struct ldc_pkt *)lc->lc_msg);
}

void
ldc_send_vers(struct ldc_conn *lc)
{
	struct ldc_pkt *lp;
	uint64_t tx_head, tx_tail, tx_state;
	int err;

	mtx_enter(&lc->lc_txq->lq_mtx);
	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
	if (err != H_EOK || tx_state != LDC_CHANNEL_UP) {
		mtx_leave(&lc->lc_txq->lq_mtx);
		return;
	}

	lp = (struct ldc_pkt *)(lc->lc_txq->lq_va + tx_tail);
	bzero(lp, sizeof(struct ldc_pkt));
	lp->type = LDC_CTRL;
	lp->stype = LDC_INFO;
	lp->ctrl = LDC_VERS;
	lp->major = 1;
	lp->minor = 0;

	tx_tail += sizeof(*lp);
	tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*lp)) - 1);
	err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail);
	if (err != H_EOK) {
		printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
		mtx_leave(&lc->lc_txq->lq_mtx);
		return;
	}

	lc->lc_state = LDC_SND_VERS;
	mtx_leave(&lc->lc_txq->lq_mtx);
}

void
ldc_send_ack(struct ldc_conn *lc)
{
	struct ldc_pkt *lp;
	uint64_t tx_head, tx_tail, tx_state;
	int err;

	mtx_enter(&lc->lc_txq->lq_mtx);
	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
	if (err != H_EOK || tx_state != LDC_CHANNEL_UP) {
		mtx_leave(&lc->lc_txq->lq_mtx);
		return;
	}

	lp = (struct ldc_pkt *)(lc->lc_txq->lq_va + tx_tail);
	bzero(lp, sizeof(struct ldc_pkt));
	lp->type = LDC_CTRL;
	lp->stype = LDC_ACK;
	lp->ctrl = LDC_VERS;
	lp->major = 1;
	lp->minor = 0;

	tx_tail += sizeof(*lp);
	tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*lp)) - 1);
	err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail);
	if (err != H_EOK) {
		printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
		mtx_leave(&lc->lc_txq->lq_mtx);
		return;
	}

	lc->lc_state = LDC_RCV_VERS;
	mtx_leave(&lc->lc_txq->lq_mtx);
}

void
ldc_send_rts(struct ldc_conn *lc)
{
	struct ldc_pkt *lp;
	uint64_t tx_head, tx_tail, tx_state;
	int err;

	mtx_enter(&lc->lc_txq->lq_mtx);
	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
	if (err != H_EOK || tx_state != LDC_CHANNEL_UP) {
		mtx_leave(&lc->lc_txq->lq_mtx);
		return;
	}

	lp = (struct ldc_pkt *)(lc->lc_txq->lq_va + tx_tail);
	bzero(lp, sizeof(struct ldc_pkt));
	lp->type = LDC_CTRL;
	lp->stype = LDC_INFO;
	lp->ctrl = LDC_RTS;
	lp->env = LDC_MODE_UNRELIABLE;
	lp->seqid = lc->lc_tx_seqid++;

	tx_tail += sizeof(*lp);
	tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*lp)) - 1);
	err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail);
	if (err != H_EOK) {
		printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
		mtx_leave(&lc->lc_txq->lq_mtx);
		return;
	}

	lc->lc_state = LDC_SND_RTS;
	mtx_leave(&lc->lc_txq->lq_mtx);
}

void
ldc_send_rtr(struct ldc_conn *lc)
{
	struct ldc_pkt *lp;
	uint64_t tx_head, tx_tail, tx_state;
	int err;

	mtx_enter(&lc->lc_txq->lq_mtx);
	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
	if (err != H_EOK || tx_state != LDC_CHANNEL_UP) {
		mtx_leave(&lc->lc_txq->lq_mtx);
		return;
	}

	lp = (struct ldc_pkt *)(lc->lc_txq->lq_va + tx_tail);
	bzero(lp, sizeof(struct ldc_pkt));
	lp->type = LDC_CTRL;
	lp->stype = LDC_INFO;
	lp->ctrl = LDC_RTR;
	lp->env = LDC_MODE_UNRELIABLE;
	lp->seqid = lc->lc_tx_seqid++;

	tx_tail += sizeof(*lp);
	tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*lp)) - 1);
	err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail);
	if (err != H_EOK) {
		printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
		mtx_leave(&lc->lc_txq->lq_mtx);
		return;
	}

	lc->lc_state = LDC_SND_RTR;
	mtx_leave(&lc->lc_txq->lq_mtx);
}

void
ldc_send_rdx(struct ldc_conn *lc)
{
	struct ldc_pkt *lp;
	uint64_t tx_head, tx_tail, tx_state;
	int err;

	mtx_enter(&lc->lc_txq->lq_mtx);
	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
	if (err != H_EOK || tx_state != LDC_CHANNEL_UP) {
		mtx_leave(&lc->lc_txq->lq_mtx);
		return;
	}

	lp = (struct ldc_pkt *)(lc->lc_txq->lq_va + tx_tail);
	bzero(lp, sizeof(struct ldc_pkt));
	lp->type = LDC_CTRL;
	lp->stype = LDC_INFO;
	lp->ctrl = LDC_RDX;
	lp->env = LDC_MODE_UNRELIABLE;
	lp->seqid = lc->lc_tx_seqid++;

	tx_tail += sizeof(*lp);
	tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*lp)) - 1);
	err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail);
	if (err != H_EOK) {
		printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
		mtx_leave(&lc->lc_txq->lq_mtx);
		return;
	}

	lc->lc_state = LDC_SND_RDX;
	mtx_leave(&lc->lc_txq->lq_mtx);
}

int
ldc_send_unreliable(struct ldc_conn *lc, void *msg, size_t len)
{
	struct ldc_pkt *lp;
	uint64_t tx_head, tx_tail, tx_state;
	uint64_t tx_avail;
	uint8_t *p = msg;
	int err;

	mtx_enter(&lc->lc_txq->lq_mtx);
	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
	if (err != H_EOK || tx_state != LDC_CHANNEL_UP) {
		mtx_leave(&lc->lc_txq->lq_mtx);
		return (EIO);
	}

	tx_avail = (tx_head - tx_tail) / sizeof(*lp) +
	    lc->lc_txq->lq_nentries - 1;
	tx_avail %= lc->lc_txq->lq_nentries;
	if (len > tx_avail * LDC_PKT_PAYLOAD) {
		mtx_leave(&lc->lc_txq->lq_mtx);
		return (EWOULDBLOCK);
	}

	while (len > 0) {
		lp = (struct ldc_pkt *)(lc->lc_txq->lq_va + tx_tail);
		bzero(lp, sizeof(struct ldc_pkt));
		lp->type = LDC_DATA;
		lp->stype = LDC_INFO;
		lp->env = min(len, LDC_PKT_PAYLOAD);
		if (p == msg)
			lp->env |= LDC_FRAG_START;
		if (len <= LDC_PKT_PAYLOAD)
			lp->env |= LDC_FRAG_STOP;
		lp->seqid = lc->lc_tx_seqid++;
		bcopy(p, &lp->major, min(len, LDC_PKT_PAYLOAD));

		tx_tail += sizeof(*lp);
		tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*lp)) - 1);
		err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail);
		if (err != H_EOK) {
			printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
			mtx_leave(&lc->lc_txq->lq_mtx);
			return (EIO);
		}
		p += min(len, LDC_PKT_PAYLOAD);
		len -= min(len, LDC_PKT_PAYLOAD);
	}

	mtx_leave(&lc->lc_txq->lq_mtx);
	return (0);
}

void
ldc_reset(struct ldc_conn *lc)
{
	int err;

	DPRINTF(("Resetting connection\n"));

	mtx_enter(&lc->lc_txq->lq_mtx);
	err = hv_ldc_tx_qconf(lc->lc_id,
	    lc->lc_txq->lq_map->dm_segs[0].ds_addr, lc->lc_txq->lq_nentries);
	if (err != H_EOK)
		printf("%s: hv_ldc_tx_qconf %d\n", __func__, err);

	err = hv_ldc_rx_qconf(lc->lc_id,
	    lc->lc_rxq->lq_map->dm_segs[0].ds_addr, lc->lc_rxq->lq_nentries);
	if (err != H_EOK)
		printf("%s: hv_ldc_rx_qconf %d\n", __func__, err);

	lc->lc_tx_seqid = 0;
	lc->lc_state = 0;
	lc->lc_tx_state = lc->lc_rx_state = LDC_CHANNEL_DOWN;
	mtx_leave(&lc->lc_txq->lq_mtx);

	lc->lc_reset(lc);
}

struct ldc_queue *
ldc_queue_alloc(bus_dma_tag_t t, int nentries)
{
	struct ldc_queue *lq;
	bus_size_t size;
	caddr_t va;
	int nsegs;

	lq = malloc(sizeof(struct ldc_queue), M_DEVBUF, M_NOWAIT);
	if (lq == NULL)
		return NULL;

	mtx_init(&lq->lq_mtx, IPL_TTY);

	size = roundup(nentries * sizeof(struct ldc_pkt), PAGE_SIZE);

	if (bus_dmamap_create(t, size, 1, size, 0,
	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &lq->lq_map) != 0)
		return (NULL);

	if (bus_dmamem_alloc(t, size, PAGE_SIZE, 0, &lq->lq_seg, 1,
	    &nsegs, BUS_DMA_NOWAIT) != 0)
		goto destroy;

	if (bus_dmamem_map(t, &lq->lq_seg, 1, size, &va,
	    BUS_DMA_NOWAIT) != 0)
		goto free;

	if (bus_dmamap_load(t, lq->lq_map, va, size, NULL,
	    BUS_DMA_NOWAIT) != 0)
		goto unmap;

	lq->lq_va = va;
	lq->lq_nentries = nentries;
	return (lq);

unmap:
	bus_dmamem_unmap(t, va, size);
free:
	bus_dmamem_free(t, &lq->lq_seg, 1);
destroy:
	bus_dmamap_destroy(t, lq->lq_map);

	return (NULL);
}

void
ldc_queue_free(bus_dma_tag_t t, struct ldc_queue *lq)
{
	bus_size_t size;

	size = roundup(lq->lq_nentries * sizeof(struct ldc_pkt), PAGE_SIZE);

	bus_dmamap_unload(t, lq->lq_map);
	bus_dmamem_unmap(t, lq->lq_va, size);
	bus_dmamem_free(t, &lq->lq_seg, 1);
	bus_dmamap_destroy(t, lq->lq_map);
	free(lq, M_DEVBUF, 0);
}

struct ldc_map *
ldc_map_alloc(bus_dma_tag_t t, int nentries)
{
	struct ldc_map *lm;
	bus_size_t size;
	caddr_t va;
	int nsegs;

	lm = malloc(sizeof(struct ldc_map), M_DEVBUF, M_NOWAIT);
	if (lm == NULL)
		return NULL;

	size = roundup(nentries * sizeof(struct ldc_map_slot), PAGE_SIZE);

	if (bus_dmamap_create(t, size, 1, size, 0,
	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &lm->lm_map) != 0)
		return (NULL);

	if (bus_dmamem_alloc(t, size, PAGE_SIZE, 0, &lm->lm_seg, 1,
	    &nsegs, BUS_DMA_NOWAIT) != 0)
		goto destroy;

	if (bus_dmamem_map(t, &lm->lm_seg, 1, size, &va,
	    BUS_DMA_NOWAIT) != 0)
		goto free;

	if (bus_dmamap_load(t, lm->lm_map, va, size, NULL,
	    BUS_DMA_NOWAIT) != 0)
		goto unmap;

	lm->lm_slot = (struct ldc_map_slot *)va;
	lm->lm_nentries = nentries;
	bzero(lm->lm_slot, nentries * sizeof(struct ldc_map_slot));
	return (lm);

unmap:
	bus_dmamem_unmap(t, va, size);
free:
	bus_dmamem_free(t, &lm->lm_seg, 1);
destroy:
	bus_dmamap_destroy(t, lm->lm_map);

	return (NULL);
}

void
ldc_map_free(bus_dma_tag_t t, struct ldc_map *lm)
{
	bus_size_t size;

	size = lm->lm_nentries * sizeof(struct ldc_map_slot);
	size = roundup(size, PAGE_SIZE);

	bus_dmamap_unload(t, lm->lm_map);
	bus_dmamem_unmap(t, (caddr_t)lm->lm_slot, size);
	bus_dmamem_free(t, &lm->lm_seg, 1);
	bus_dmamap_destroy(t, lm->lm_map);
	free(lm, M_DEVBUF, 0);
}


code:
/*	$OpenBSD: ldcvar.h,v 1.6 2014/09/29 17:43:29 kettenis Exp $	*/
/*
 * Copyright (c) 2009 Mark Kettenis
 *
 * 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.
 */

/*
 * LDC queues.
 */

#include <sys/mutex.h>

struct ldc_queue {
	struct mutex	lq_mtx;
	bus_dmamap_t	lq_map;
	bus_dma_segment_t lq_seg;
	caddr_t		lq_va;
	int		lq_nentries;
};

struct ldc_queue *ldc_queue_alloc(bus_dma_tag_t, int);
void	ldc_queue_free(bus_dma_tag_t, struct ldc_queue *);

/*
 * LDC virtual link layer protocol.
 */

#define LDC_VERSION_MAJOR	1
#define LDC_VERSION_MINOR	0

#define LDC_PKT_PAYLOAD		56

struct ldc_pkt {
	uint8_t		type;
	uint8_t		stype;
	uint8_t		ctrl;
	uint8_t		env;
	uint32_t	seqid;

	uint16_t	major;
	uint16_t	minor;
	uint32_t	_reserved[13];
};

/* Packet types. */
#define LDC_CTRL	0x01
#define LDC_DATA	0x02
#define LDC_ERR		0x10

/* Packet subtypes. */
#define LDC_INFO	0x01
#define LDC_ACK		0x02
#define LDC_NACK	0x04

/* Control info values. */
#define LDC_VERS	0x01
#define LDC_RTS		0x02
#define LDC_RTR		0x03
#define LDC_RDX		0x04

/* Packet envelope. */
#define LDC_MODE_RAW		0x00
#define LDC_MODE_UNRELIABLE	0x01
#define LDC_MODE_RELIABLE	0x03

#define LDC_LEN_MASK	0x3f
#define LDC_FRAG_MASK	0xc0
#define LDC_FRAG_START	0x40
#define LDC_FRAG_STOP	0x80

/*
 * XXX Get rid of the +8 once we no longer need to store the header of
 * the first packet.
 */
#define LDC_MSG_MAX	(128 + 8)

struct ldc_conn {
	uint64_t	lc_id;

	struct ldc_queue *lc_txq;
	struct ldc_queue *lc_rxq;
	uint64_t	lc_tx_state;
	uint64_t	lc_rx_state;

	uint32_t	lc_tx_seqid;
	uint8_t		lc_state;
#define LDC_SND_VERS	1
#define LDC_RCV_VERS	2
#define LDC_SND_RTS	3
#define LDC_SND_RTR	4
#define LDC_SND_RDX	5

	uint64_t	lc_msg[LDC_MSG_MAX / 8];
	size_t		lc_len;

	void		*lc_sc;
	void		(*lc_reset)(struct ldc_conn *);
	void		(*lc_start)(struct ldc_conn *);
	void		(*lc_rx_data)(struct ldc_conn *, struct ldc_pkt *);
};

void	ldc_rx_ctrl(struct ldc_conn *, struct ldc_pkt *);
void	ldc_rx_data(struct ldc_conn *, struct ldc_pkt *);

void	ldc_send_vers(struct ldc_conn *);
int	ldc_send_unreliable(struct ldc_conn *, void *, size_t);

void	ldc_reset(struct ldc_conn *);

/*
 * LDC map tables.
 */

struct ldc_map_slot {
	uint64_t	entry;
	uint64_t	cookie;
};

#define LDC_MTE_R	0x0000000000000010ULL
#define LDC_MTE_W	0x0000000000000020ULL
#define LDC_MTE_X	0x0000000000000040ULL
#define LDC_MTE_IOR	0x0000000000000080ULL
#define LDC_MTE_IOW	0x0000000000000100ULL
#define LDC_MTE_CPR	0x0000000000000200ULL
#define LDC_MTE_CPW	0x0000000000000400ULL
#define LDC_MTE_RA_MASK	0x007fffffffffe000ULL

struct ldc_map {
	bus_dmamap_t		lm_map;
	bus_dma_segment_t	lm_seg;
	struct ldc_map_slot	*lm_slot;
	int			lm_nentries;
	int			lm_next;
	int			lm_count;
};

struct ldc_map *ldc_map_alloc(bus_dma_tag_t, int);
void	ldc_map_free(bus_dma_tag_t, struct ldc_map *);

struct ldc_cookie {
	uint64_t	addr;
	uint64_t	size;
};

license: unknown


code:
/*	$OpenBSD: led.c,v 1.1 2007/05/29 04:08:02 kettenis Exp $	*/

/*
 * Copyright (c) 2007 Mark Kettenis
 *
 * 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.
 */

/*
 * Driver for leds on Fire V215/V245.
 */

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

#include <machine/autoconf.h>
#include <machine/bus.h>
#include <machine/cpu.h>

#include <sparc64/dev/ebusreg.h>
#include <sparc64/dev/ebusvar.h>

/*
 * Register access is indirect, through an address and data port.
 */

#define	EPIC_DATA			0x40
#define	EPIC_ADDR			0x41
#define	EPIC_WRITE_MASK			0x80

#define	EPIC_FW_VERSION			0x05
#define	EPIC_LED_STATE0			0x06

#define	EPIC_ALERT_LED_MASK		0x0C
#define	EPIC_ALERT_LED_OFF		0x00
#define	EPIC_ALERT_LED_ON		0x04

#define	EPIC_POWER_LED_MASK		0x30
#define	EPIC_POWER_LED_OFF		0x00
#define	EPIC_POWER_LED_ON		0x10
#define	EPIC_POWER_LED_SB_BLINK		0x20
#define EPIC_POWER_LED_FAST_BLINK	0x30

struct led_softc {
	struct device		sc_dv;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;

	struct timeout		sc_to;
	int			sc_on;
	struct blink_led	sc_blink;
};

int	led_match(struct device *, void *, void *);
void	led_attach(struct device *, struct device *, void *);

struct cfattach led_ca = {
	sizeof(struct led_softc), led_match, led_attach
};

struct cfdriver led_cd = {
	NULL, "led", DV_DULL
};

void led_blink(void *, int);
void led_blink_finish(void *);

int
led_match(struct device *parent, void *cf, void *aux)
{
	struct ebus_attach_args *ea = aux;
	char *str;

	if (strcmp("env-monitor", ea->ea_name) != 0)
		return (0);

	str = getpropstring(ea->ea_node, "compatible");
	if (strcmp(str, "epic") == 0)
		return (1);

	return (0);
}

void
led_attach(struct device *parent, struct device *self, void *aux)
{
	struct led_softc *sc = (void *)self;
	struct ebus_attach_args *ea = aux;
	int rev;

	if (ebus_bus_map(ea->ea_iotag, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
		sc->sc_iot = ea->ea_iotag;
	} else if (ebus_bus_map(ea->ea_memtag, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
		sc->sc_iot = ea->ea_memtag;
	} else {
		printf("%s: can't map register space\n", self->dv_xname);
		return;
	}

	bus_space_write_1(sc->sc_iot, sc->sc_ioh, EPIC_ADDR, EPIC_FW_VERSION);
	delay(10000);
	rev = bus_space_read_1(sc->sc_iot, sc->sc_ioh, EPIC_DATA);
	printf(": rev 0x%02x\n", rev);

	/* Turn off the alert LED. */
	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
	    EPIC_WRITE_MASK, EPIC_ALERT_LED_MASK);
	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
	    EPIC_ADDR, EPIC_LED_STATE0);
	delay(10000);
	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
	    EPIC_DATA, EPIC_ALERT_LED_OFF);

	timeout_set(&sc->sc_to, led_blink_finish, sc);

	sc->sc_blink.bl_func = led_blink;
	sc->sc_blink.bl_arg = sc;
	blink_led_register(&sc->sc_blink);
}

void
led_blink(void *v, int on)
{
	struct led_softc *sc = v;

	sc->sc_on = on;
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, EPIC_ADDR, EPIC_LED_STATE0);
	timeout_add(&sc->sc_to, max(1, hz / 100));
}

void
led_blink_finish(void *v)
{
	struct led_softc *sc = v;
	u_int8_t reg;

	if (sc->sc_on)
		reg = EPIC_ALERT_LED_ON;
	else
		reg = EPIC_ALERT_LED_OFF;

	bus_space_write_1(sc->sc_iot, sc->sc_ioh, EPIC_DATA, reg);
}


code:
/*	$OpenBSD: lom.c,v 1.25 2014/12/10 12:27:57 mikeb Exp $	*/
/*
 * Copyright (c) 2009 Mark Kettenis
 *
 * 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/device.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/sensors.h>
#include <sys/systm.h>
#include <sys/timeout.h>

#include <machine/autoconf.h>
#include <machine/openfirm.h>

#include <sparc64/dev/ebusreg.h>
#include <sparc64/dev/ebusvar.h>

/*
 * LOMlite is a so far unidentified microcontroller.
 */
#define LOM1_STATUS		0x00	/* R */
#define  LOM1_STATUS_BUSY	0x80
#define LOM1_CMD		0x00	/* W */
#define LOM1_DATA		0x01	/* R/W */

/*
 * LOMlite2 is implemented as a H8/3437 microcontroller which has its
 * on-chip host interface hooked up to EBus.
 */
#define LOM2_DATA		0x00	/* R/W */
#define LOM2_CMD		0x01	/* W */
#define LOM2_STATUS		0x01	/* R */
#define  LOM2_STATUS_OBF	0x01	/* Output Buffer Full */
#define  LOM2_STATUS_IBF	0x02	/* Input Buffer Full  */

#define LOM_IDX_CMD		0x00
#define  LOM_IDX_CMD_GENERIC	0x00
#define  LOM_IDX_CMD_TEMP	0x04
#define  LOM_IDX_CMD_FAN	0x05

#define LOM_IDX_FW_REV		0x01	/* Firmware revision  */

#define LOM_IDX_FAN1		0x04	/* Fan speed */
#define LOM_IDX_FAN2		0x05
#define LOM_IDX_FAN3		0x06
#define LOM_IDX_FAN4		0x07
#define LOM_IDX_PSU1		0x08	/* PSU status */
#define LOM_IDX_PSU2		0x09
#define LOM_IDX_PSU3		0x0a
#define  LOM_PSU_INPUTA		0x01
#define  LOM_PSU_INPUTB		0x02
#define  LOM_PSU_OUTPUT		0x04
#define  LOM_PSU_PRESENT	0x08
#define  LOM_PSU_STANDBY	0x10

#define LOM_IDX_TEMP1		0x18	/* Temperature */
#define LOM_IDX_TEMP2		0x19
#define LOM_IDX_TEMP3		0x1a
#define LOM_IDX_TEMP4		0x1b
#define LOM_IDX_TEMP5		0x1c
#define LOM_IDX_TEMP6		0x1d
#define LOM_IDX_TEMP7		0x1e
#define LOM_IDX_TEMP8		0x1f

#define LOM_IDX_LED1		0x25

#define LOM_IDX_ALARM		0x30
#define LOM_IDX_WDOG_CTL	0x31
#define  LOM_WDOG_ENABLE	0x01
#define  LOM_WDOG_RESET		0x02
#define  LOM_WDOG_AL3_WDOG	0x04
#define  LOM_WDOG_AL3_FANPSU	0x08
#define LOM_IDX_WDOG_TIME	0x32
#define  LOM_WDOG_TIME_MAX	126

#define LOM1_IDX_HOSTNAME1	0x33
#define LOM1_IDX_HOSTNAME2	0x34
#define LOM1_IDX_HOSTNAME3	0x35
#define LOM1_IDX_HOSTNAME4	0x36
#define LOM1_IDX_HOSTNAME5	0x37
#define LOM1_IDX_HOSTNAME6	0x38
#define LOM1_IDX_HOSTNAME7	0x39
#define LOM1_IDX_HOSTNAME8	0x3a
#define LOM1_IDX_HOSTNAME9	0x3b
#define LOM1_IDX_HOSTNAME10	0x3c
#define LOM1_IDX_HOSTNAME11	0x3d
#define LOM1_IDX_HOSTNAME12	0x3e

#define LOM2_IDX_HOSTNAMELEN	0x38
#define LOM2_IDX_HOSTNAME	0x39

#define LOM_IDX_CONFIG		0x5d
#define LOM_IDX_FAN1_CAL	0x5e
#define LOM_IDX_FAN2_CAL	0x5f
#define LOM_IDX_FAN3_CAL	0x60
#define LOM_IDX_FAN4_CAL	0x61
#define LOM_IDX_FAN1_LOW	0x62
#define LOM_IDX_FAN2_LOW	0x63
#define LOM_IDX_FAN3_LOW	0x64
#define LOM_IDX_FAN4_LOW	0x65

#define LOM_IDX_CONFIG2		0x66
#define LOM_IDX_CONFIG3		0x67

#define LOM_IDX_PROBE55		0x7e	/* Always returns 0x55 */
#define LOM_IDX_PROBEAA		0x7f	/* Always returns 0xaa */

#define LOM_IDX_WRITE		0x80

#define LOM_IDX4_TEMP_NAME_START	0x40
#define LOM_IDX4_TEMP_NAME_END		0xff

#define LOM_IDX5_FAN_NAME_START		0x40
#define LOM_IDX5_FAN_NAME_END		0xff

#define LOM_MAX_FAN	4
#define LOM_MAX_PSU	3
#define LOM_MAX_TEMP	8

struct lom_cmd {
	uint8_t			lc_cmd;
	uint8_t			lc_data;

	TAILQ_ENTRY(lom_cmd)	lc_next;
};

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

	int			sc_type;
#define LOM_LOMLITE		0
#define LOM_LOMLITE2		2
	int			sc_space;

	struct ksensor		sc_fan[LOM_MAX_FAN];
	struct ksensor		sc_psu[LOM_MAX_PSU];
	struct ksensor		sc_temp[LOM_MAX_TEMP];
	struct ksensordev	sc_sensordev;

	int			sc_num_fan;
	int			sc_num_psu;
	int			sc_num_temp;

	uint8_t			sc_fan_cal[LOM_MAX_FAN];
	uint8_t			sc_fan_low[LOM_MAX_FAN];

	char			sc_hostname[MAXHOSTNAMELEN];

	struct timeout		sc_wdog_to;
	int			sc_wdog_period;
	uint8_t			sc_wdog_ctl;
	struct lom_cmd		sc_wdog_pat;

	TAILQ_HEAD(, lom_cmd)	sc_queue;
	struct mutex		sc_queue_mtx;
	struct timeout		sc_state_to;
	int			sc_state;
#define LOM_STATE_IDLE		0
#define LOM_STATE_CMD		1
#define LOM_STATE_DATA		2
	int			sc_retry;
};

int	lom_match(struct device *, void *, void *);
void	lom_attach(struct device *, struct device *, void *);
int	lom_activate(struct device *, int);

struct cfattach lom_ca = {
	sizeof(struct lom_softc), lom_match, lom_attach,
	NULL, lom_activate
};

struct cfdriver lom_cd = {
	NULL, "lom", DV_DULL
};

int	lom_read(struct lom_softc *, uint8_t, uint8_t *);
int	lom_write(struct lom_softc *, uint8_t, uint8_t);
void	lom_queue_cmd(struct lom_softc *, struct lom_cmd *);
void	lom_dequeue_cmd(struct lom_softc *, struct lom_cmd *);
int	lom1_read(struct lom_softc *, uint8_t, uint8_t *);
int	lom1_write(struct lom_softc *, uint8_t, uint8_t);
int	lom1_read_polled(struct lom_softc *, uint8_t, uint8_t *);
int	lom1_write_polled(struct lom_softc *, uint8_t, uint8_t);
void	lom1_queue_cmd(struct lom_softc *, struct lom_cmd *);
void	lom1_process_queue(void *);
void	lom1_process_queue_locked(struct lom_softc *);
int	lom2_read(struct lom_softc *, uint8_t, uint8_t *);
int	lom2_write(struct lom_softc *, uint8_t, uint8_t);
int	lom2_read_polled(struct lom_softc *, uint8_t, uint8_t *);
int	lom2_write_polled(struct lom_softc *, uint8_t, uint8_t);
void	lom2_queue_cmd(struct lom_softc *, struct lom_cmd *);
int	lom2_intr(void *);

int	lom_init_desc(struct lom_softc *sc);
void	lom_refresh(void *);
void	lom1_write_hostname(struct lom_softc *);
void	lom2_write_hostname(struct lom_softc *);

void	lom_wdog_pat(void *);
int	lom_wdog_cb(void *, int);

void	lom_shutdown(void *);

int
lom_match(struct device *parent, void *match, void *aux)
{
	struct ebus_attach_args *ea = aux;

	if (strcmp(ea->ea_name, "SUNW,lom") == 0 ||
	    strcmp(ea->ea_name, "SUNW,lomh") == 0)
		return (1);

	return (0);
}

void
lom_attach(struct device *parent, struct device *self, void *aux)
{
	struct lom_softc *sc = (void *)self;
	struct ebus_attach_args *ea = aux;
	uint8_t reg, fw_rev, config, config2, config3;
	uint8_t cal, low;
	int i;

	if (strcmp(ea->ea_name, "SUNW,lomh") == 0) {
		if (ea->ea_nintrs < 1) {
			printf(": no interrupt\n");
			return;
		}
		sc->sc_type = LOM_LOMLITE2;
	}

	if (ebus_bus_map(ea->ea_iotag, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
		sc->sc_iot = ea->ea_iotag;
	} else if (ebus_bus_map(ea->ea_memtag, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
		sc->sc_iot = ea->ea_memtag;
	} else {
		printf(": can't map register space\n");
                return;
	}

	if (sc->sc_type < LOM_LOMLITE2) {
		/* XXX Magic */
		bus_space_read_1(sc->sc_iot, sc->sc_ioh, 0);
		bus_space_write_1(sc->sc_iot, sc->sc_ioh, 3, 0xca);
	}

	if (lom_read(sc, LOM_IDX_PROBE55, &reg) || reg != 0x55 ||
	    lom_read(sc, LOM_IDX_PROBEAA, &reg) || reg != 0xaa ||
	    lom_read(sc, LOM_IDX_FW_REV, &fw_rev) ||
	    lom_read(sc, LOM_IDX_CONFIG, &config))
	{
		printf(": not responding\n");
		return;
	}

	TAILQ_INIT(&sc->sc_queue);
	mtx_init(&sc->sc_queue_mtx, IPL_BIO);

	config2 = config3 = 0;
	if (sc->sc_type < LOM_LOMLITE2) {
		/*
		 * LOMlite doesn't do interrupts so we limp along on
		 * timeouts.
		 */
		timeout_set(&sc->sc_state_to, lom1_process_queue, sc);
	} else {
		lom_read(sc, LOM_IDX_CONFIG2, &config2);
		lom_read(sc, LOM_IDX_CONFIG3, &config3);

		bus_intr_establish(sc->sc_iot, ea->ea_intrs[0],
		    IPL_BIO, 0, lom2_intr, sc, self->dv_xname);
	}

	sc->sc_num_fan = min((config >> 5) & 0x7, LOM_MAX_FAN);
	sc->sc_num_psu = min((config >> 3) & 0x3, LOM_MAX_PSU);
	sc->sc_num_temp = min((config2 >> 4) & 0xf, LOM_MAX_TEMP);

	for (i = 0; i < sc->sc_num_fan; i++) {
		if (lom_read(sc, LOM_IDX_FAN1_CAL + i, &cal) ||
		    lom_read(sc, LOM_IDX_FAN1_LOW + i, &low)) {
			printf(": can't read fan information\n");
			return;
		}
		sc->sc_fan_cal[i] = cal;
		sc->sc_fan_low[i] = low;
	}

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));
	for (i = 0; i < sc->sc_num_fan; i++) {
		sc->sc_fan[i].type = SENSOR_FANRPM;
		sensor_attach(&sc->sc_sensordev, &sc->sc_fan[i]);
		snprintf(sc->sc_fan[i].desc, sizeof(sc->sc_fan[i].desc),
		    "fan%d", i + 1);
	}
	for (i = 0; i < sc->sc_num_psu; i++) {
		sc->sc_psu[i].type = SENSOR_INDICATOR;
		sensor_attach(&sc->sc_sensordev, &sc->sc_psu[i]);
		snprintf(sc->sc_psu[i].desc, sizeof(sc->sc_psu[i].desc),
		    "PSU%d", i + 1);
	}
	for (i = 0; i < sc->sc_num_temp; i++) {
		sc->sc_temp[i].type = SENSOR_TEMP;
		sensor_attach(&sc->sc_sensordev, &sc->sc_temp[i]);
	}
	if (lom_init_desc(sc)) {
		printf(": can't read sensor names\n");
		return;
	}

	if (sensor_task_register(sc, lom_refresh, 5) == NULL) {
		printf(": unable to register update task\n");
		return;
	}

	sensordev_install(&sc->sc_sensordev);

	/*
	 * We configure the watchdog to turn on the fault LED when the
	 * watchdog timer expires.  We run our own timeout to pat it
	 * such that this won't happen unless the kernel hangs.  When
	 * the watchdog is explicitly configured using sysctl(8), we
	 * reconfigure it to reset the machine and let the standard
	 * watchdog(4) machinery take over.
	 */
	lom_write(sc, LOM_IDX_WDOG_TIME, LOM_WDOG_TIME_MAX);
	lom_read(sc, LOM_IDX_WDOG_CTL, &sc->sc_wdog_ctl);
	sc->sc_wdog_ctl &= ~LOM_WDOG_RESET;
	sc->sc_wdog_ctl |= LOM_WDOG_ENABLE;
	lom_write(sc, LOM_IDX_WDOG_CTL, sc->sc_wdog_ctl);
	timeout_set(&sc->sc_wdog_to, lom_wdog_pat, sc);
	timeout_add_sec(&sc->sc_wdog_to, LOM_WDOG_TIME_MAX / 2);

	wdog_register(lom_wdog_cb, sc);

	printf(": %s rev %d.%d\n",
	    sc->sc_type < LOM_LOMLITE2 ? "LOMlite" : "LOMlite2",
	    fw_rev >> 4, fw_rev & 0x0f);
}

int
lom_activate(struct device *self, int act)
{
	int ret = 0;

	switch (act) {
	case DVACT_POWERDOWN:
		wdog_shutdown(self);
		lom_shutdown(self);
		break;
	}

	return (ret);
}

int
lom_read(struct lom_softc *sc, uint8_t reg, uint8_t *val)
{
	if (sc->sc_type < LOM_LOMLITE2)
		return lom1_read(sc, reg, val);
	else
		return lom2_read(sc, reg, val);
}

int
lom_write(struct lom_softc *sc, uint8_t reg, uint8_t val)
{
	if (sc->sc_type < LOM_LOMLITE2)
		return lom1_write(sc, reg, val);
	else
		return lom2_write(sc, reg, val);
}

void
lom_queue_cmd(struct lom_softc *sc, struct lom_cmd *lc)
{
	if (sc->sc_type < LOM_LOMLITE2)
		return lom1_queue_cmd(sc, lc);
	else
		return lom2_queue_cmd(sc, lc);
}

void
lom_dequeue_cmd(struct lom_softc *sc, struct lom_cmd *lc)
{
	struct lom_cmd *lcp;

	mtx_enter(&sc->sc_queue_mtx);
	TAILQ_FOREACH(lcp, &sc->sc_queue, lc_next) {
		if (lcp == lc) {
			TAILQ_REMOVE(&sc->sc_queue, lc, lc_next);
			break;
		}
	}
	mtx_leave(&sc->sc_queue_mtx);
}

int
lom1_read(struct lom_softc *sc, uint8_t reg, uint8_t *val)
{
	struct lom_cmd lc;
	int error;

	if (cold)
		return lom1_read_polled(sc, reg, val);

	lc.lc_cmd = reg;
	lc.lc_data = 0xff;
	lom1_queue_cmd(sc, &lc);

	error = tsleep(&lc, PZERO, "lomrd", hz);
	if (error)
		lom_dequeue_cmd(sc, &lc);

	*val = lc.lc_data;

	return (error);
}

int
lom1_write(struct lom_softc *sc, uint8_t reg, uint8_t val)
{
	struct lom_cmd lc;
	int error;

	if (cold)
		return lom1_write_polled(sc, reg, val);

	lc.lc_cmd = reg | LOM_IDX_WRITE;
	lc.lc_data = val;
	lom1_queue_cmd(sc, &lc);

	error = tsleep(&lc, PZERO, "lomwr", 2 * hz);
	if (error)
		lom_dequeue_cmd(sc, &lc);

	return (error);
}

int
lom1_read_polled(struct lom_softc *sc, uint8_t reg, uint8_t *val)
{
	uint8_t str;
	int i;

	/* Wait for input buffer to become available. */
	for (i = 30; i > 0; i--) {
		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
		delay(1000);
		if ((str & LOM1_STATUS_BUSY) == 0)
			break;
	}
	if (i == 0)
		return (ETIMEDOUT);

	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_CMD, reg);

	/* Wait until the microcontroller fills output buffer. */
	for (i = 30; i > 0; i--) {
		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
		delay(1000);
		if ((str & LOM1_STATUS_BUSY) == 0)
			break;
	}
	if (i == 0)
		return (ETIMEDOUT);

	*val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA);
	return (0);
}

int
lom1_write_polled(struct lom_softc *sc, uint8_t reg, uint8_t val)
{
	uint8_t str;
	int i;

	/* Wait for input buffer to become available. */
	for (i = 30; i > 0; i--) {
		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
		delay(1000);
		if ((str & LOM1_STATUS_BUSY) == 0)
			break;
	}
	if (i == 0)
		return (ETIMEDOUT);

	reg |= LOM_IDX_WRITE;
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_CMD, reg);

	/* Wait until the microcontroller fills output buffer. */
	for (i = 30; i > 0; i--) {
		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
		delay(1000);
		if ((str & LOM1_STATUS_BUSY) == 0)
			break;
	}
	if (i == 0)
		return (ETIMEDOUT);

	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA, val);

	return (0);
}

void
lom1_queue_cmd(struct lom_softc *sc, struct lom_cmd *lc)
{
	mtx_enter(&sc->sc_queue_mtx);
	TAILQ_INSERT_TAIL(&sc->sc_queue, lc, lc_next);
	if (sc->sc_state == LOM_STATE_IDLE) {
		sc->sc_state = LOM_STATE_CMD;
		lom1_process_queue_locked(sc);
	}
	mtx_leave(&sc->sc_queue_mtx);
}

void
lom1_process_queue(void *arg)
{
	struct lom_softc *sc = arg;

	mtx_enter(&sc->sc_queue_mtx);
	lom1_process_queue_locked(sc);
	mtx_leave(&sc->sc_queue_mtx);
}

void
lom1_process_queue_locked(struct lom_softc *sc)
{
	struct lom_cmd *lc;
	uint8_t str;

	lc = TAILQ_FIRST(&sc->sc_queue);
	if (lc == NULL) {
		sc->sc_state = LOM_STATE_IDLE;
		return;
	}

	str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
	if (str & LOM1_STATUS_BUSY) {
		if (sc->sc_retry++ < 30) {
			timeout_add_msec(&sc->sc_state_to, 1);
			return;
		}

		/*
		 * Looks like the microcontroller got wedged.  Unwedge
		 * it by writing this magic value.  Give it some time
		 * to recover.
		 */
		bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA, 0xac);
		timeout_add_msec(&sc->sc_state_to, 1000);
		sc->sc_state = LOM_STATE_CMD;
		return;
	}

	sc->sc_retry = 0;

	if (sc->sc_state == LOM_STATE_CMD) {
		bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_CMD, lc->lc_cmd);
		sc->sc_state = LOM_STATE_DATA;
		timeout_add_msec(&sc->sc_state_to, 250);
		return;
	}

	KASSERT(sc->sc_state == LOM_STATE_DATA);
	if ((lc->lc_cmd & LOM_IDX_WRITE) == 0)
		lc->lc_data = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA);
	else
		bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA, lc->lc_data);

	TAILQ_REMOVE(&sc->sc_queue, lc, lc_next);

	wakeup(lc);

	if (!TAILQ_EMPTY(&sc->sc_queue)) {
		sc->sc_state = LOM_STATE_CMD;
		timeout_add_msec(&sc->sc_state_to, 1);
		return;
	}

	sc->sc_state = LOM_STATE_IDLE;
}

int
lom2_read(struct lom_softc *sc, uint8_t reg, uint8_t *val)
{
	struct lom_cmd lc;
	int error;

	if (cold)
		return lom2_read_polled(sc, reg, val);

	lc.lc_cmd = reg;
	lc.lc_data = 0xff;
	lom2_queue_cmd(sc, &lc);

	error = tsleep(&lc, PZERO, "lom2rd", hz);
	if (error)
		lom_dequeue_cmd(sc, &lc);

	*val = lc.lc_data;

	return (error);
}

int
lom2_read_polled(struct lom_softc *sc, uint8_t reg, uint8_t *val)
{
	uint8_t str;
	int i;

	/* Wait for input buffer to become available. */
	for (i = 1000; i > 0; i--) {
		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
		delay(10);
		if ((str & LOM2_STATUS_IBF) == 0)
			break;
	}
	if (i == 0)
		return (ETIMEDOUT);

	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM2_CMD, reg);

	/* Wait until the microcontroller fills output buffer. */
	for (i = 1000; i > 0; i--) {
		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
		delay(10);
		if (str & LOM2_STATUS_OBF)
			break;
	}
	if (i == 0)
		return (ETIMEDOUT);

	*val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA);
	return (0);
}

int
lom2_write(struct lom_softc *sc, uint8_t reg, uint8_t val)
{
	struct lom_cmd lc;
	int error;

	if (cold)
		return lom2_write_polled(sc, reg, val);

	lc.lc_cmd = reg | LOM_IDX_WRITE;
	lc.lc_data = val;
	lom2_queue_cmd(sc, &lc);

	error = tsleep(&lc, PZERO, "lom2wr", hz);
	if (error)
		lom_dequeue_cmd(sc, &lc);

	return (error);
}

int
lom2_write_polled(struct lom_softc *sc, uint8_t reg, uint8_t val)
{
	uint8_t str;
	int i;

	/* Wait for input buffer to become available. */
	for (i = 1000; i > 0; i--) {
		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
		delay(10);
		if ((str & LOM2_STATUS_IBF) == 0)
			break;
	}
	if (i == 0)
		return (ETIMEDOUT);

	if (sc->sc_space == LOM_IDX_CMD_GENERIC && reg != LOM_IDX_CMD)
		reg |= LOM_IDX_WRITE;

	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM2_CMD, reg);

	/* Wait until the microcontroller fills output buffer. */
	for (i = 1000; i > 0; i--) {
		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
		delay(10);
		if (str & LOM2_STATUS_OBF)
			break;
	}
	if (i == 0)
		return (ETIMEDOUT);

	bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA);

	/* Wait for input buffer to become available. */
	for (i = 1000; i > 0; i--) {
		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
		delay(10);
		if ((str & LOM2_STATUS_IBF) == 0)
			break;
	}
	if (i == 0)
		return (ETIMEDOUT);

	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA, val);

	/* Wait until the microcontroller fills output buffer. */
	for (i = 1000; i > 0; i--) {
		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
		delay(10);
		if (str & LOM2_STATUS_OBF)
			break;
	}
	if (i == 0)
		return (ETIMEDOUT);

	bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA);

	/* If we switched spaces, remember the one we're in now. */
	if (reg == LOM_IDX_CMD)
		sc->sc_space = val;

	return (0);
}

void
lom2_queue_cmd(struct lom_softc *sc, struct lom_cmd *lc)
{
	uint8_t str;

	mtx_enter(&sc->sc_queue_mtx);
	TAILQ_INSERT_TAIL(&sc->sc_queue, lc, lc_next);
	if (sc->sc_state == LOM_STATE_IDLE) {
		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
		if ((str & LOM2_STATUS_IBF) == 0) {
			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
			    LOM2_CMD, lc->lc_cmd);
			sc->sc_state = LOM_STATE_DATA;
		}
	}
	mtx_leave(&sc->sc_queue_mtx);
}

int
lom2_intr(void *arg)
{
	struct lom_softc *sc = arg;
	struct lom_cmd *lc;
	uint8_t str, obr;

	mtx_enter(&sc->sc_queue_mtx);

	str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
	obr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA);

	lc = TAILQ_FIRST(&sc->sc_queue);
	if (lc == NULL) {
		mtx_leave(&sc->sc_queue_mtx);
		return (0);
	}

	if (lc->lc_cmd & LOM_IDX_WRITE) {
		bus_space_write_1(sc->sc_iot, sc->sc_ioh,
		    LOM2_DATA, lc->lc_data);
		lc->lc_cmd &= ~LOM_IDX_WRITE;
		mtx_leave(&sc->sc_queue_mtx);
		return (1);
	}

	KASSERT(sc->sc_state = LOM_STATE_DATA);
	lc->lc_data = obr;

	TAILQ_REMOVE(&sc->sc_queue, lc, lc_next);

	wakeup(lc);

	sc->sc_state = LOM_STATE_IDLE;

	if (!TAILQ_EMPTY(&sc->sc_queue)) {
		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
		if ((str & LOM2_STATUS_IBF) == 0) {
			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
			    LOM2_CMD, lc->lc_cmd);
			sc->sc_state = LOM_STATE_DATA;
		}
	}

	mtx_leave(&sc->sc_queue_mtx);

	return (1);
}

int
lom_init_desc(struct lom_softc *sc)
{
	uint8_t val;
	int i, j, k;
	int error;

	/* LOMlite doesn't provide sensor descriptions. */
	if (sc->sc_type < LOM_LOMLITE2)
		return (0);

	/*
	 * Read temperature sensor names.
	 */
	error = lom_write(sc, LOM_IDX_CMD, LOM_IDX_CMD_TEMP);
	if (error)
		return (error);

	i = 0;
	j = 0;
	k = LOM_IDX4_TEMP_NAME_START;
	while (k <= LOM_IDX4_TEMP_NAME_END) {
		error = lom_read(sc, k++, &val);
		if (error)
			goto fail;

		if (val == 0xff)
			break;

		if (j < sizeof (sc->sc_temp[i].desc) - 1)
			sc->sc_temp[i].desc[j++] = val;

		if (val == '\0') {
			i++;
			j = 0;
			if (i < sc->sc_num_temp)
				continue;

			break;
		}
	}

	/*
	 * Read fan names.
	 */
	error = lom_write(sc, LOM_IDX_CMD, LOM_IDX_CMD_FAN);
	if (error)
		return (error);

	i = 0;
	j = 0;
	k = LOM_IDX5_FAN_NAME_START;
	while (k <= LOM_IDX5_FAN_NAME_END) {
		error = lom_read(sc, k++, &val);
		if (error)
			goto fail;

		if (val == 0xff)
			break;

		if (j < sizeof (sc->sc_fan[i].desc) - 1)
			sc->sc_fan[i].desc[j++] = val;

		if (val == '\0') {
			i++;
			j = 0;
			if (i < sc->sc_num_fan)
				continue;

			break;
		}
	}

fail:
	lom_write(sc, LOM_IDX_CMD, LOM_IDX_CMD_GENERIC);
	return (error);
}

void
lom_refresh(void *arg)
{
	struct lom_softc *sc = arg;
	uint8_t val;
	int i;

	for (i = 0; i < sc->sc_num_fan; i++) {
		if (lom_read(sc, LOM_IDX_FAN1 + i, &val)) {
			sc->sc_fan[i].flags |= SENSOR_FINVALID;
			continue;
		}

		sc->sc_fan[i].value = (60 * sc->sc_fan_cal[i] * val) / 100;
		if (val < sc->sc_fan_low[i])
			sc->sc_fan[i].status = SENSOR_S_CRIT;
		else
			sc->sc_fan[i].status = SENSOR_S_OK;
		sc->sc_fan[i].flags &= ~SENSOR_FINVALID;
	}

	for (i = 0; i < sc->sc_num_psu; i++) {
		if (lom_read(sc, LOM_IDX_PSU1 + i, &val) ||
		    !ISSET(val, LOM_PSU_PRESENT)) {
			sc->sc_psu[i].flags |= SENSOR_FINVALID;
			continue;
		}

		if (val & LOM_PSU_STANDBY) {
			sc->sc_psu[i].value = 0;
			sc->sc_psu[i].status = SENSOR_S_UNSPEC;
		} else {
			sc->sc_psu[i].value = 1;
			if (ISSET(val, LOM_PSU_INPUTA) &&
			    ISSET(val, LOM_PSU_INPUTB) &&
			    ISSET(val, LOM_PSU_OUTPUT))
				sc->sc_psu[i].status = SENSOR_S_OK;
			else
				sc->sc_psu[i].status = SENSOR_S_CRIT;
		}
		sc->sc_psu[i].flags &= ~SENSOR_FINVALID;
	}

	for (i = 0; i < sc->sc_num_temp; i++) {
		if (lom_read(sc, LOM_IDX_TEMP1 + i, &val)) {
			sc->sc_temp[i].flags |= SENSOR_FINVALID;
			continue;
		}

		sc->sc_temp[i].value = val * 1000000 + 273150000;
		sc->sc_temp[i].flags &= ~SENSOR_FINVALID;
	}

	/*
	 * If our hostname is set and differs from what's stored in
	 * the LOM, write the new hostname back to the LOM.  Note that
	 * we include the terminating NUL when writing the hostname
	 * back to the LOM, otherwise the LOM will print any trailing
	 * garbage.
	 */
	if (hostnamelen > 0 &&
	    strncmp(sc->sc_hostname, hostname, sizeof(hostname)) != 0) {
		if (sc->sc_type < LOM_LOMLITE2)
			lom1_write_hostname(sc);
		else
			lom2_write_hostname(sc);
		strlcpy(sc->sc_hostname, hostname, sizeof(hostname));
	}
}

void
lom1_write_hostname(struct lom_softc *sc)
{
	char name[(LOM1_IDX_HOSTNAME12 - LOM1_IDX_HOSTNAME1 + 1) + 1];
	char *p;
	int i;

	/*
	 * LOMlite generally doesn't have enough space to store the
	 * fully qualified hostname.  If the hostname is too long,
	 * strip off the domain name.
	 */
	strlcpy(name, hostname, sizeof(name));
	if (hostnamelen >= sizeof(name)) {
		p = strchr(name, '.');
		if (p)
			*p = '\0';
	}

	for (i = 0; i < strlen(name) + 1; i++)
		if (lom_write(sc, LOM1_IDX_HOSTNAME1 + i, name[i]))
			break;
}

void
lom2_write_hostname(struct lom_softc *sc)
{
	int i;

	lom_write(sc, LOM2_IDX_HOSTNAMELEN, hostnamelen + 1);
	for (i = 0; i < hostnamelen + 1; i++)
		lom_write(sc, LOM2_IDX_HOSTNAME, hostname[i]);
}

void
lom_wdog_pat(void *arg)
{
	struct lom_softc *sc = arg;

	/* Pat the dog. */
	sc->sc_wdog_pat.lc_cmd = LOM_IDX_WDOG_CTL | LOM_IDX_WRITE;
	sc->sc_wdog_pat.lc_data = sc->sc_wdog_ctl;
	lom_queue_cmd(sc, &sc->sc_wdog_pat);

	timeout_add_sec(&sc->sc_wdog_to, LOM_WDOG_TIME_MAX / 2);
}

int
lom_wdog_cb(void *arg, int period)
{
	struct lom_softc *sc = arg;

	if (period > LOM_WDOG_TIME_MAX)
		period = LOM_WDOG_TIME_MAX;
	else if (period < 0)
		period = 0;

	if (period == 0) {
		if (sc->sc_wdog_period != 0) {
			/* Stop watchdog from resetting the machine. */
			sc->sc_wdog_ctl &= ~LOM_WDOG_RESET;
			lom_write(sc, LOM_IDX_WDOG_CTL, sc->sc_wdog_ctl);

			lom_write(sc, LOM_IDX_WDOG_TIME, LOM_WDOG_TIME_MAX);
			timeout_add_sec(&sc->sc_wdog_to, LOM_WDOG_TIME_MAX / 2);
		}
	} else {
		if (sc->sc_wdog_period != period) {
			/* Set new timeout. */
			lom_write(sc, LOM_IDX_WDOG_TIME, period);
		}
		if (sc->sc_wdog_period == 0) {
			/* Make watchdog reset the machine. */
			sc->sc_wdog_ctl |= LOM_WDOG_RESET;
			lom_write(sc, LOM_IDX_WDOG_CTL, sc->sc_wdog_ctl);

			timeout_del(&sc->sc_wdog_to);
		} else {
			/* Pat the dog. */
			lom_dequeue_cmd(sc, &sc->sc_wdog_pat);
			sc->sc_wdog_pat.lc_cmd = LOM_IDX_WDOG_CTL | LOM_IDX_WRITE;
			sc->sc_wdog_pat.lc_data = sc->sc_wdog_ctl;
			lom_queue_cmd(sc, &sc->sc_wdog_pat);
		}
	}
	sc->sc_wdog_period = period;

	return (period);
}

void
lom_shutdown(void *arg)
{
	struct lom_softc *sc = arg;

	sc->sc_wdog_ctl &= ~LOM_WDOG_ENABLE;
	lom_write(sc, LOM_IDX_WDOG_CTL, sc->sc_wdog_ctl);
}


code:
/*	$OpenBSD: lpt_ebus.c,v 1.8 2003/12/16 15:04:56 jason Exp $	*/
/*	$NetBSD: lpt_ebus.c,v 1.8 2002/03/01 11:51:00 martin Exp $	*/

/*
 * Copyright (c) 1999, 2000 Matthew R. Green
 * 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.
 */

/*
 * NS Super I/O PC87332VLJ "lpt" to ebus attachment
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/tty.h>

#include <machine/bus.h>

#include <sparc64/dev/ebusreg.h>
#include <sparc64/dev/ebusvar.h>

#include <dev/ic/lptvar.h>

struct lpt_ebus_softc {
	struct lpt_softc sc_lpt;
	bus_space_handle_t sc_ctrl;
};

int	lpt_ebus_match(struct device *, void *, void *);
void	lpt_ebus_attach(struct device *, struct device *, void *);

struct cfattach lpt_ebus_ca = {
	sizeof(struct lpt_ebus_softc), lpt_ebus_match, lpt_ebus_attach
};

int
lpt_ebus_match(parent, match, aux)
	struct device *parent;
	void *match;
	void *aux;
{
	struct ebus_attach_args *ea = aux;

	if (!strcmp(ea->ea_name, "ecpp") ||
	    !strcmp(ea->ea_name, "parallel"))
		return (1);
	return (0);
}

void
lpt_ebus_attach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct lpt_ebus_softc *sc = (void *)self;
	struct ebus_attach_args *ea = aux;

	if (ebus_bus_map(ea->ea_memtag, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), ea->ea_regs[0].size,
	    0, 0, &sc->sc_lpt.sc_ioh) == 0) {
		sc->sc_lpt.sc_iot = ea->ea_memtag;
	} else if (ebus_bus_map(ea->ea_iotag, 0,
		    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), ea->ea_regs[0].size,
		    0, 0, &sc->sc_lpt.sc_ioh) == 0) {
		sc->sc_lpt.sc_iot = ea->ea_iotag;
	} else {
		printf(": can't map register space\n");
		return;
	}

	if (ebus_bus_map(sc->sc_lpt.sc_iot, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[1]), ea->ea_regs[1].size, 0, 0,
	    &sc->sc_ctrl) != 0) {
		printf(": can't map control space\n");
		bus_space_unmap(sc->sc_lpt.sc_iot, sc->sc_lpt.sc_ioh,
		    ea->ea_regs[0].size);
		return;
	}

	sc->sc_lpt.sc_flags |= LPT_POLLED;
	printf(": polled");

	lpt_attach_common(&sc->sc_lpt);
}


code:
/*	$OpenBSD: machfb.c,v 1.10 2014/07/28 15:00:27 jsg Exp $	*/

/*
 * Copyright (c) 2009 Mark Kettenis.
 *
 * 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/device.h>
#include <sys/pciio.h>
#include <sys/systm.h>

#include <machine/autoconf.h>
#include <machine/bus.h>
#include <machine/openfirm.h>

#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>

#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>

#include <dev/rasops/rasops.h>

#include <machine/fbvar.h>

#define M64_PCI_MEM		0x10
#define M64_PCI_MMIO		0x18

#define M64_REG_OFF		0x007ffc00
#define M64_REG_SIZE		0x0400

#define M64_CRTC_INT_CNTL	0x0018

#define M64_BUS_CNTL		0x00a0
#define  M64_BUS_FIFO_ERR_ACK	0x00200000
#define  M64_BUS_HOST_ERR_ACK	0x00800000
#define  M64_BUS_APER_REG_DIS	0x00000010

#define M64_DAC_WINDEX		0x00c0
#define M64_DAC_DATA		0x00c1
#define M64_DAC_MASK		0x00c2
#define M64_DAC_RINDEX		0x00c3
#define M64_DAC_CNTL		0x00c4
#define  M64_DAC_8BIT_EN		0x00000100

#define M64_GEN_TEST_CNTL	0x00d0
#define  M64_GEN_GUI_EN			0x00000100

#define M64_DST_OFF_PITCH	0x0100
#define M64_DST_X		0x0104
#define M64_DST_Y		0x0108
#define M64_DST_Y_X		0x010c
#define M64_DST_WIDTH		0x0110
#define M64_DST_HEIGHT		0x0114
#define M64_DST_HEIGHT_WIDTH	0x0118
#define M64_DST_X_WIDTH		0x011c
#define M64_DST_BRES_LNTH	0x0120
#define M64_DST_BRES_ERR	0x0124
#define M64_DST_BRES_INC	0x0128
#define M64_DST_BRES_DEC	0x012c
#define M64_DST_CNTL		0x0130
#define  M64_DST_X_RIGHT_TO_LEFT	0x00000000
#define  M64_DST_X_LEFT_TO_RIGHT	0x00000001
#define  M64_DST_Y_BOTTOM_TO_TOP	0x00000000
#define  M64_DST_Y_TOP_TO_BOTTOM	0x00000002
#define  M64_DST_X_MAJOR		0x00000000
#define  M64_DST_Y_MAJOR		0x00000004
#define  M64_DST_X_TILE			0x00000008
#define  M64_DST_Y_TILE			0x00000010
#define  M64_DST_LAST_PEL		0x00000020
#define  M64_DST_POLYGON_EN		0x00000040
#define  M64_DST_24_ROT_EN		0x00000080

#define M64_SRC_OFF_PITCH	0x0180
#define M64_SRC_X		0x0184
#define M64_SRC_Y		0x0188
#define M64_SRC_Y_X		0x018c
#define M64_SRC_WIDTH1		0x0190
#define M64_SRC_HEIGHT1		0x0194
#define M64_SRC_HEIGHT1_WIDTH1	0x0198
#define M64_SRC_X_START		0x019c
#define M64_SRC_Y_START		0x01a0
#define M64_SRC_Y_X_START	0x01a4
#define M64_SRC_WIDTH2		0x01a8
#define M64_SRC_HEIGHT2		0x01ac
#define M64_SRC_HEIGHT2_WIDTH2	0x01b0
#define M64_SRC_CNTL		0x01b4
#define  M64_SRC_PATT_EN		0x00000001
#define  M64_SRC_PATT_ROT_EN		0x00000002
#define  M64_SRC_LINEAR_EN		0x00000004
#define  M64_SRC_BYTE_ALIGN 		0x00000008
#define  M64_SRC_LINE_X_RIGHT_TO_LEFT	0x00000000
#define  M64_SRC_LINE_X_LEFT_TO_RIGHT	0x00000010

#define M64_HOST_CNTL		0x0240

#define M64_PAT_REG0		0x0280
#define M64_PAT_REG1		0x0284
#define M64_PAT_CNTL		0x0288

#define M64_SC_LEFT		0x02a0
#define M64_SC_RIGHT		0x02a4
#define M64_SC_LEFT_RIGHT	0x02a8
#define M64_SC_TOP		0x02ac
#define M64_SC_BOTTOM		0x02b0
#define M64_SC_TOP_BOTTOM	0x02b4

#define M64_DP_BKGD_CLR		0x02c0
#define M64_DP_FRGD_CLR		0x02c4
#define M64_DP_WRITE_MASK	0x02c8

#define M64_DP_CHAIN_MASK	0x02cc
#define  M64_DP_CHAIN_8BPP		0x00008080
#define M64_DP_PIX_WIDTH	0x02d0
#define  M64_DST_8BPP			0x00000002
#define  M64_SRC_8BPP			0x00000200
#define  M64_HOST_8BPP			0x00020000
#define M64_DP_MIX		0x02d4
#define  M64_MIX_DST			0x00000003
#define  M64_MIX_SRC			0x00000007
#define M64_DP_SRC           0x02d8
#define  M64_BKGD_SRC_BKGD_CLR		0x00000000
#define  M64_BKGD_SRC_FRGD_CLR		0x00000001
#define  M64_BKGD_SRC_HOST		0x00000002
#define  M64_BKGD_SRC_BLIT		0x00000003
#define  M64_BKGD_SRC_PATTERN		0x00000004
#define  M64_FRGD_SRC_BKGD_CLR		0x00000000
#define  M64_FRGD_SRC_FRGD_CLR		0x00000100
#define  M64_FRGD_SRC_HOST		0x00000200
#define  M64_FRGD_SRC_BLIT		0x00000300
#define  M64_FRGD_SRC_PATTERN		0x00000400
#define  M64_MONO_SRC_ONE		0x00000000
#define  M64_MONO_SRC_PATTERN		0x00010000
#define  M64_MONO_SRC_HOST		0x00020000
#define  M64_MONO_SRC_BLIT		0x00030000

#define M64_CLR_CMP_CLR		0x0300
#define M64_CLR_CMP_MASK	0x0304
#define M64_CLR_CMP_CNTL	0x0308

#define M64_FIFO_STAT		0x0310
#define  M64_FIFO_STAT_MASK		0x0000ffff

#define M64_CONTEXT_MASK	0x0320

#define M64_GUI_TRAJ_CNTL	0x0330
#define M64_GUI_STAT		0x0338
#define  M64_GUI_ACTIVE		 0x00000001

#define M64_COORDS(x, y)	((x << 16) | (y))

#ifdef APERTURE
extern int allowaperture;
#endif

struct machfb_softc {
	struct sunfb	sc_sunfb;

	bus_space_tag_t sc_memt;
	bus_space_handle_t sc_memh;
	bus_addr_t	sc_membase;
	bus_size_t	sc_memsize;

	bus_space_tag_t	sc_regt;
	bus_space_handle_t sc_regh;

	bus_space_tag_t	sc_mmiot;
	bus_space_handle_t sc_mmioh;
	bus_addr_t	sc_mmiobase;
	bus_size_t	sc_mmiosize;

	pcitag_t	sc_pcitag;

	int		sc_mode;
	u_int8_t	sc_cmap_red[256];
	u_int8_t	sc_cmap_green[256];
	u_int8_t	sc_cmap_blue[256];
};

int	machfb_ioctl(void *, u_long, caddr_t, int, struct proc *);
paddr_t	machfb_mmap(void *, off_t, int);

struct wsdisplay_accessops machfb_accessops = {
	.ioctl = machfb_ioctl,
	.mmap = machfb_mmap
};

int	machfb_match(struct device *, void *, void *);
void	machfb_attach(struct device *, struct device *, void *);

struct cfattach machfb_ca = {
	sizeof(struct machfb_softc), machfb_match, machfb_attach
};

struct cfdriver machfb_cd = {
	NULL, "machfb", DV_DULL
};

int	machfb_is_console(int);
int	machfb_getcmap(struct machfb_softc *, struct wsdisplay_cmap *);
int	machfb_putcmap(struct machfb_softc *, struct wsdisplay_cmap *);
void	machfb_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t);

int	machfb_copycols(void *, int, int, int, int);
int	machfb_erasecols(void *, int, int, int, long);
int	machfb_copyrows(void *, int, int, int);
int	machfb_eraserows(void *, int, int, long);

void	machfb_init(struct machfb_softc *);
int	machfb_wait_fifo(struct machfb_softc *, int);
int	machfb_wait(struct machfb_softc *);
void	machfb_copyrect(struct machfb_softc *, int, int, int, int, int, int);
void	machfb_fillrect(struct machfb_softc *, int, int, int, int, int);

int
machfb_match(struct device *parent, void *cf, void *aux)
{
	struct pci_attach_args *pa = aux;
	char buf[32];
	int node;

	node = PCITAG_NODE(pa->pa_tag);
	OF_getprop(node, "name", buf, sizeof(buf));
	if (strcmp(buf, "SUNW,m64B") == 0)
		return (10);

	if (OF_getprop(node, "compatible", buf, sizeof(buf)) > 0 &&
	    strcmp(buf, "SUNW,m64B") == 0)
		return (10);

	return (0);
}

void
machfb_attach(struct device *parent, struct device *self, void *aux)
{
	struct machfb_softc *sc = (struct machfb_softc *)self;
	struct pci_attach_args *pa = aux;
	struct rasops_info *ri;
	int node, console;
	char *model;

	sc->sc_pcitag = pa->pa_tag;

	node = PCITAG_NODE(pa->pa_tag);
	console = machfb_is_console(node);

	printf("\n");

	model = getpropstring(node, "model");
	printf("%s: %s", self->dv_xname, model);

	if (pci_mapreg_map(pa, M64_PCI_MEM, PCI_MAPREG_TYPE_MEM,
	    BUS_SPACE_MAP_LINEAR, &sc->sc_memt, &sc->sc_memh,
	    &sc->sc_membase, &sc->sc_memsize, 0)) {
		printf("\n%s: can't map video memory\n", self->dv_xname);
		return;
	}

	sc->sc_regt = sc->sc_memt;
	if (bus_space_subregion(sc->sc_memt, sc->sc_memh,
	    M64_REG_OFF, M64_REG_SIZE, &sc->sc_regh)) {
		printf("\n%s: can't map registers\n", self->dv_xname);
		return;
	}

	if (pci_mapreg_map(pa, M64_PCI_MMIO, PCI_MAPREG_TYPE_MEM, 0,
	    &sc->sc_mmiot, &sc->sc_mmioh, &sc->sc_mmiobase,
	    &sc->sc_mmiosize, 0)) {
		printf("\n%s: can't map registers\n", self->dv_xname);
		return;
	}

	fb_setsize(&sc->sc_sunfb, 8, 1152, 900, node, 0);
	if (sc->sc_sunfb.sf_depth == 24) {
		sc->sc_sunfb.sf_depth = 32;
		sc->sc_sunfb.sf_linebytes =
		    (sc->sc_sunfb.sf_depth / 8) * sc->sc_sunfb.sf_width;
		sc->sc_sunfb.sf_fbsize =
		    sc->sc_sunfb.sf_height * sc->sc_sunfb.sf_linebytes;
	}

	printf(", %dx%d\n", sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height);

#ifdef RAMDISK_HOOKS
	printf("%s: aperture needed\n", self->dv_xname);
#endif

	ri = &sc->sc_sunfb.sf_ro;
	ri->ri_bits = bus_space_vaddr(sc->sc_memt, sc->sc_memh);
	ri->ri_hw = sc;

	fbwscons_init(&sc->sc_sunfb, RI_BSWAP, console);
	fbwscons_setcolormap(&sc->sc_sunfb, machfb_setcolor);
	sc->sc_mode = WSDISPLAYIO_MODE_EMUL;

	machfb_init(sc);
	ri->ri_ops.copyrows = machfb_copyrows;
	ri->ri_ops.copycols = machfb_copycols;
	ri->ri_ops.eraserows = machfb_eraserows;
	ri->ri_ops.erasecols = machfb_erasecols;

	if (console)
		fbwscons_console_init(&sc->sc_sunfb, -1);
	fbwscons_attach(&sc->sc_sunfb, &machfb_accessops, console);
}

int
machfb_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p)
{
	struct machfb_softc *sc = v;
	struct wsdisplay_fbinfo *wdf;
	struct pcisel *sel;

	switch (cmd) {
	case WSDISPLAYIO_GTYPE:
		*(u_int *)data = WSDISPLAY_TYPE_MACHFB;
		break;
	case WSDISPLAYIO_SMODE:
		sc->sc_mode = *(u_int *)data;
		if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) {
			struct rasops_info *ri = &sc->sc_sunfb.sf_ro;

			/* Restore colormap. */
			fbwscons_setcolormap(&sc->sc_sunfb, machfb_setcolor);

			/* Clear screen. */
			machfb_fillrect(sc, 0, 0, ri->ri_width, ri->ri_height,
			    ri->ri_devcmap[WSCOL_WHITE]);
		}
		break;
	case WSDISPLAYIO_GINFO:
		wdf = (void *)data;
		wdf->height = sc->sc_sunfb.sf_height;
		wdf->width  = sc->sc_sunfb.sf_width;
		wdf->depth  = sc->sc_sunfb.sf_depth;
		wdf->cmsize = 256;
		break;
	case WSDISPLAYIO_LINEBYTES:
		*(u_int *)data = sc->sc_sunfb.sf_linebytes;
		break;

	case WSDISPLAYIO_GETCMAP:
		return machfb_getcmap(sc, (struct wsdisplay_cmap *)data);
	case WSDISPLAYIO_PUTCMAP:
		return machfb_putcmap(sc, (struct wsdisplay_cmap *)data);

	case WSDISPLAYIO_GPCIID:
		sel = (struct pcisel *)data;
		sel->pc_bus = PCITAG_BUS(sc->sc_pcitag);
		sel->pc_dev = PCITAG_DEV(sc->sc_pcitag);
		sel->pc_func = PCITAG_FUN(sc->sc_pcitag);
		break;

	case WSDISPLAYIO_SVIDEO:
	case WSDISPLAYIO_GVIDEO:
		break;

	case WSDISPLAYIO_GCURPOS:
	case WSDISPLAYIO_SCURPOS:
	case WSDISPLAYIO_GCURMAX:
	case WSDISPLAYIO_GCURSOR:
	case WSDISPLAYIO_SCURSOR:
	default:
		return -1; /* not supported yet */
        }

	return (0);
}

paddr_t
machfb_mmap(void *v, off_t off, int prot)
{
	struct machfb_softc *sc = v;

	if (off & PGOFSET)
		return (-1);

	switch (sc->sc_mode) {
	case WSDISPLAYIO_MODE_MAPPED:
#ifdef APERTURE
		if (allowaperture == 0)
			return (-1);
#endif

		if (sc->sc_mmiosize == 0)
			return (-1);

		if (off >= sc->sc_membase &&
		    off < (sc->sc_membase + sc->sc_memsize))
			return (bus_space_mmap(sc->sc_memt,
			    sc->sc_membase, off - sc->sc_membase,
			    prot, BUS_SPACE_MAP_LINEAR));

		if (off >= sc->sc_mmiobase &&
		    off < (sc->sc_mmiobase + sc->sc_mmiosize))
			return (bus_space_mmap(sc->sc_mmiot,
			    sc->sc_mmiobase, off - sc->sc_mmiobase,
			    prot, BUS_SPACE_MAP_LINEAR));
		break;

	case WSDISPLAYIO_MODE_DUMBFB:
		if (off >= 0 && off < sc->sc_memsize)
			return (bus_space_mmap(sc->sc_memt, sc->sc_membase,
			    off, prot, BUS_SPACE_MAP_LINEAR));
		break;
	}

	return (-1);
}

int
machfb_is_console(int node)
{
	extern int fbnode;

	return (fbnode == node);
}

int
machfb_getcmap(struct machfb_softc *sc, struct wsdisplay_cmap *cm)
{
	u_int index = cm->index;
	u_int count = cm->count;
	int error;

	if (index >= 256 || count > 256 - index)
		return (EINVAL);

	error = copyout(&sc->sc_cmap_red[index], cm->red, count);
	if (error)
		return (error);
	error = copyout(&sc->sc_cmap_green[index], cm->green, count);
	if (error)
		return (error);
	error = copyout(&sc->sc_cmap_blue[index], cm->blue, count);
	if (error)
		return (error);
	return (0);
}

int
machfb_putcmap(struct machfb_softc *sc, struct wsdisplay_cmap *cm)
{
	u_int index = cm->index;
	u_int count = cm->count;
	u_int i;
	int error;
	u_char *r, *g, *b;

	if (index >= 256 || count > 256 - index)
		return (EINVAL);

	if ((error = copyin(cm->red, &sc->sc_cmap_red[index], count)) != 0)
		return (error);
	if ((error = copyin(cm->green, &sc->sc_cmap_green[index], count)) != 0)
		return (error);
	if ((error = copyin(cm->blue, &sc->sc_cmap_blue[index], count)) != 0)
		return (error);

	r = &sc->sc_cmap_red[index];
	g = &sc->sc_cmap_green[index];
	b = &sc->sc_cmap_blue[index];

	bus_space_write_1(sc->sc_regt, sc->sc_regh, M64_DAC_MASK, 0xff);
	bus_space_write_1(sc->sc_regt, sc->sc_regh, M64_DAC_WINDEX, index);
	for (i = 0; i < count; i++) {
		bus_space_write_1(sc->sc_regt, sc->sc_regh,
		    M64_DAC_DATA, *r);
		bus_space_write_1(sc->sc_regt, sc->sc_regh,
		    M64_DAC_DATA, *g);
		bus_space_write_1(sc->sc_regt, sc->sc_regh,
		    M64_DAC_DATA, *b);
		r++, g++, b++;
	}
	return (0);
}

void
machfb_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b)
{
	struct machfb_softc *sc = v;

	sc->sc_cmap_red[index] = r;
	sc->sc_cmap_green[index] = g;
	sc->sc_cmap_blue[index] = b;

	bus_space_write_1(sc->sc_regt, sc->sc_regh, M64_DAC_MASK, 0xff);
	bus_space_write_1(sc->sc_regt, sc->sc_regh, M64_DAC_WINDEX, index);
	bus_space_write_1(sc->sc_regt, sc->sc_regh, M64_DAC_DATA, r);
	bus_space_write_1(sc->sc_regt, sc->sc_regh, M64_DAC_DATA, g);
	bus_space_write_1(sc->sc_regt, sc->sc_regh, M64_DAC_DATA, b);
}

/*
 * Accelerated routines.
 */

int
machfb_copycols(void *cookie, int row, int src, int dst, int num)
{
	struct rasops_info *ri = cookie;
	struct machfb_softc *sc = ri->ri_hw;

	num *= ri->ri_font->fontwidth;
	src *= ri->ri_font->fontwidth;
	dst *= ri->ri_font->fontwidth;
	row *= ri->ri_font->fontheight;

	machfb_copyrect(sc, ri->ri_xorigin + src, ri->ri_yorigin + row,
	    ri->ri_xorigin + dst, ri->ri_yorigin + row,
	    num, ri->ri_font->fontheight);

	return 0;
}

int
machfb_erasecols(void *cookie, int row, int col, int num, long attr)
{
	struct rasops_info *ri = cookie;
	struct machfb_softc *sc = ri->ri_hw;
	int bg, fg;

	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);

	row *= ri->ri_font->fontheight;
	col *= ri->ri_font->fontwidth;
	num *= ri->ri_font->fontwidth;

	machfb_fillrect(sc, ri->ri_xorigin + col, ri->ri_yorigin + row,
	    num, ri->ri_font->fontheight, ri->ri_devcmap[bg]);

	return 0;
}

int
machfb_copyrows(void *cookie, int src, int dst, int num)
{
	struct rasops_info *ri = cookie;
	struct machfb_softc *sc = ri->ri_hw;

	num *= ri->ri_font->fontheight;
	src *= ri->ri_font->fontheight;
	dst *= ri->ri_font->fontheight;

	machfb_copyrect(sc, ri->ri_xorigin, ri->ri_yorigin + src,
	    ri->ri_xorigin, ri->ri_yorigin + dst, ri->ri_emuwidth, num);

	return 0;
}

int
machfb_eraserows(void *cookie, int row, int num, long attr)
{
	struct rasops_info *ri = cookie;
	struct machfb_softc *sc = ri->ri_hw;
	int bg, fg;
	int x, y, w;

	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);

	if ((num == ri->ri_rows) && ISSET(ri->ri_flg, RI_FULLCLEAR)) {
		num = ri->ri_height;
		x = y = 0;
		w = ri->ri_width;
	} else {
		num *= ri->ri_font->fontheight;
		x = ri->ri_xorigin;
		y = ri->ri_yorigin + row * ri->ri_font->fontheight;
		w = ri->ri_emuwidth;
	}
	machfb_fillrect(sc, x, y, w, num, ri->ri_devcmap[bg]);

	return 0;
}

void
machfb_init(struct machfb_softc *sc)
{
	uint32_t reg;

        /* Reset engine. */
	reg = bus_space_read_4(sc->sc_regt, sc->sc_regh, M64_GEN_TEST_CNTL);
	reg &= ~M64_GEN_GUI_EN;
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_GEN_TEST_CNTL, reg);

        /* Enable engine. */
	reg = bus_space_read_4(sc->sc_regt, sc->sc_regh, M64_GEN_TEST_CNTL);
	reg &= M64_GEN_GUI_EN;
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_GEN_TEST_CNTL, reg);

        /* Clearing any FIFO or host errors. */
	reg = bus_space_read_4(sc->sc_regt, sc->sc_regh, M64_BUS_CNTL);
	reg |= M64_BUS_HOST_ERR_ACK | M64_BUS_FIFO_ERR_ACK;
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_BUS_CNTL, reg);

	machfb_wait_fifo(sc, 14);

	bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_CONTEXT_MASK, 0xffffffff);

	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_DST_OFF_PITCH,
	    (sc->sc_sunfb.sf_linebytes / 8) << 22);

	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_DST_Y_X, 0);
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_DST_HEIGHT, 0);
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_DST_BRES_ERR, 0);
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_DST_BRES_INC, 0);
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_DST_BRES_DEC, 0);

	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_DST_CNTL,
	    M64_DST_LAST_PEL | M64_DST_X_LEFT_TO_RIGHT |
	    M64_DST_Y_TOP_TO_BOTTOM);

	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_SRC_OFF_PITCH,
	    (sc->sc_sunfb.sf_linebytes / 8) << 22);

	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_SRC_Y_X, 0);
	bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_SRC_HEIGHT1_WIDTH1, 1);
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_SRC_Y_X_START, 0);
	bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_SRC_HEIGHT2_WIDTH2, 1);

	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_SRC_CNTL,
	    M64_SRC_LINE_X_LEFT_TO_RIGHT);

	machfb_wait_fifo(sc, 13);

	/* Host attributes. */
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_HOST_CNTL, 0);

	/* Pattern attributes. */
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_PAT_REG0, 0);
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_PAT_REG1, 0);
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_PAT_CNTL, 0);

	/* Scissors. */
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_SC_LEFT, 0);
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_SC_TOP, 0);
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_SC_BOTTOM,
	    sc->sc_sunfb.sf_height - 1);
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_SC_RIGHT,
	    sc->sc_sunfb.sf_linebytes - 1);

	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_DP_BKGD_CLR, 0);
	bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_DP_FRGD_CLR, 0xffffffff);
	bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_DP_WRITE_MASK, 0xffffffff);
	bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_DP_MIX, (M64_MIX_SRC << 16) | M64_MIX_DST);

	bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_DP_SRC, M64_FRGD_SRC_FRGD_CLR);

	machfb_wait_fifo(sc, 3);

	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_CLR_CMP_CLR, 0);
	bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_CLR_CMP_MASK, 0xffffffff);
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_CLR_CMP_CNTL, 0);

	machfb_wait_fifo(sc, 3);

	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_DP_PIX_WIDTH,
	    M64_HOST_8BPP | M64_SRC_8BPP | M64_DST_8BPP);
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_DP_CHAIN_MASK,
	    M64_DP_CHAIN_8BPP);

	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_GUI_TRAJ_CNTL,
	    M64_DST_X_LEFT_TO_RIGHT | M64_DST_Y_TOP_TO_BOTTOM);

	machfb_wait(sc);
}

int
machfb_wait_fifo(struct machfb_softc *sc, int v)
{
	int i;

	for (i = 1000000; i != 0; i--) {
		if ((bus_space_read_4(sc->sc_regt, sc->sc_regh,
		    M64_FIFO_STAT) & M64_FIFO_STAT_MASK) <= (0x8000 >> v))
			break;
		DELAY(1);
	}

	return i;
}

int
machfb_wait(struct machfb_softc *sc)
{
	int i;

	machfb_wait_fifo(sc, 16);
	for (i = 1000000; i != 0; i--) {
		if ((bus_space_read_4(sc->sc_regt, sc->sc_regh,
		    M64_GUI_STAT) & M64_GUI_ACTIVE) == 0)
			break;
		DELAY(1);
	}

	return i;
}

void
machfb_copyrect(struct machfb_softc *sc, int sx, int sy, int dx, int dy,
    int w, int h)
{
	uint32_t dest_ctl = 0;

	machfb_wait_fifo(sc, 10);

	bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_DP_WRITE_MASK, 0xff);
	bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_DP_SRC, M64_FRGD_SRC_BLIT);
	bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_DP_MIX, M64_MIX_SRC << 16);
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_CLR_CMP_CNTL, 0);
	if (dy < sy) {
		dest_ctl = M64_DST_Y_TOP_TO_BOTTOM;
	} else {
		sy += h - 1;
		dy += h - 1;
		dest_ctl = M64_DST_Y_BOTTOM_TO_TOP;
	}
	if (dx < sx) {
		dest_ctl |= M64_DST_X_LEFT_TO_RIGHT;
		bus_space_write_4(sc->sc_regt, sc->sc_regh,
		    M64_SRC_CNTL, M64_SRC_LINE_X_LEFT_TO_RIGHT);
	} else {
		dest_ctl |= M64_DST_X_RIGHT_TO_LEFT;
		sx += w - 1;
		dx += w - 1;
		bus_space_write_4(sc->sc_regt, sc->sc_regh,
		    M64_SRC_CNTL, M64_SRC_LINE_X_RIGHT_TO_LEFT);
	}
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_DST_CNTL, dest_ctl);

	bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_SRC_Y_X, M64_COORDS(sx, sy));
	bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_SRC_WIDTH1, w);
	bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_DST_Y_X, M64_COORDS(dx, dy));
	bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_DST_HEIGHT_WIDTH, M64_COORDS(w, h));

	machfb_wait(sc);
}

void
machfb_fillrect(struct machfb_softc *sc, int x, int y, int w, int h, int color)
{
	machfb_wait_fifo(sc, 11);

        bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_DP_WRITE_MASK, 0xff);
        bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_DP_FRGD_CLR, color);
        bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_DP_SRC, M64_FRGD_SRC_FRGD_CLR);
        bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_DP_MIX, M64_MIX_SRC << 16);
        bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_CLR_CMP_CNTL, 0);
        bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_SRC_CNTL, M64_SRC_LINE_X_LEFT_TO_RIGHT);
        bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_DST_CNTL, M64_DST_X_LEFT_TO_RIGHT | M64_DST_Y_TOP_TO_BOTTOM);

        bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_SRC_Y_X, M64_COORDS(x, y));
        bus_space_write_4(sc->sc_regt, sc->sc_regh, M64_SRC_WIDTH1, w);
        bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_DST_Y_X, M64_COORDS(x, y));
        bus_space_write_4(sc->sc_regt, sc->sc_regh,
	    M64_DST_HEIGHT_WIDTH, M64_COORDS(w, h));

        machfb_wait(sc);
}


code:
/*	$OpenBSD: mgiic.c,v 1.3 2015/05/07 00:00:52 jsg Exp $	*/
/*
 * Copyright (c) 2008 Theo de Raadt <deraadt@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/device.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/rwlock.h>

#include <uvm/uvm_extern.h>

#include <machine/bus.h>
#include <machine/autoconf.h>
#include <machine/openfirm.h>

#include <dev/i2c/i2cvar.h>
#include <sparc64/dev/ofwi2cvar.h>

#define MGSLAVEADDR		0x00
#define MGSLAVEXADDR		0x08
#define MGDATA			0x10
#define MGCONTROL			0x18
#define  MGCONTROL_IEN			0x80
#define  MGCONTROL_ENAB			0x40
#define  MGCONTROL_STA			0x20
#define  MGCONTROL_STP			0x10
#define  MGCONTROL_IFLG			0x08
#define  MGCONTROL_AAK			0x04
#define MGSTATUS		0x20
#define  MGSTATUS_BUSERR		0x00
#define  MGSTATUS_STARTSENT		0x08
#define	 MGSTATUS_REPEATSTART		0x10
#define  MGSTATUS_ADDR_W_ACKR		0x18
#define  MGSTATUS_ADDR_W_NOACKR		0x20
#define  MGSTATUS_MDATA_ACKR		0x28
#define  MGSTATUS_MDATA_NOACKR		0x30
#define  MGSTATUS_ARBLOST		0x38
#define  MGSTATUS_ADDR_R_ACKR		0x40
#define  MGSTATUS_ADDR_R_NOACKR		0x48
#define  MGSTATUS_MDATA_ACKT		0x50
#define  MGSTATUS_MDATA_NOACKT		0x58
#define  MGSTATUS_SADDR_W_ACKT		0x60
#define  MGSTATUS_ARBLOST_SLW_ACKT	0x68
#define  MGSTATUS_GC_TACK		0x70
#define  MGSTATUS_ARBLOST_GC_ACKT	0x78
#define  MGSTATUS_IDLE			0xf8
#define MGCLOCKCONTROL		0x28
#define MGSOFTRESET		0x30

struct mgiic_softc {
	struct device sc_dev;

	bus_space_tag_t		sc_bt;
	bus_space_handle_t	sc_regh;

	int			sc_poll;

	struct i2c_controller	sc_i2c;
	struct rwlock		sc_lock;
};

int			mgiic_match(struct device *, void *, void *);
void			mgiic_attach(struct device *, struct device *, void *);

struct cfdriver mgiic_cd = {
        NULL, "mgiic", DV_DULL
};

struct cfattach mgiic_ca = {
        sizeof(struct mgiic_softc), mgiic_match, mgiic_attach
};

int			mgiic_i2c_acquire_bus(void *, int);
void			mgiic_i2c_release_bus(void *, int);
int			mgiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
			    size_t, void *, size_t, int);

int			mgiic_xmit(struct mgiic_softc *, u_int8_t, const u_int8_t *,
			    size_t);
int			mgiic_recv(struct mgiic_softc *, u_int8_t, u_int8_t *, size_t);
volatile u_int8_t	mgiic_read(struct mgiic_softc *, bus_size_t);
volatile void		mgiic_write(struct mgiic_softc *, bus_size_t, u_int8_t);
volatile void		mgiic_control(struct mgiic_softc *, u_int8_t, u_int8_t);
int			mgiic_poll(struct mgiic_softc *);

int
mgiic_match(struct device *parent, void *match, void *aux)
{
	struct mainbus_attach_args *ma = aux;
	char compat[32];

	if (strcmp(ma->ma_name, "i2c") != 0)
		return (0);
	if (OF_getprop(ma->ma_node, "compatible", compat, sizeof(compat)) == -1)
		return (0);
	if (strcmp(compat, "fire-i2c") == 0)
		return (1);
	return (0);
}

void
mgiic_attach(struct device *parent, struct device *self, void *aux)
{
	struct mgiic_softc *sc = (struct mgiic_softc *)self;
	struct mainbus_attach_args *ma = aux;
	struct i2cbus_attach_args iba;

	sc->sc_bt = ma->ma_bustag;

	if (bus_space_map(sc->sc_bt, ma->ma_reg[0].ur_paddr,
	    ma->ma_reg[0].ur_len, 0, &sc->sc_regh)) {
		printf(": failed to map preg\n");
		return;
	}

	rw_init(&sc->sc_lock, "iiclk");
	sc->sc_i2c.ic_cookie = sc;
	sc->sc_i2c.ic_acquire_bus = mgiic_i2c_acquire_bus;
	sc->sc_i2c.ic_release_bus = mgiic_i2c_release_bus;
	sc->sc_i2c.ic_exec = mgiic_i2c_exec;

	printf("\n");

	bzero(&iba, sizeof(iba));
	iba.iba_name = "iic";
	iba.iba_tag = &sc->sc_i2c;
	iba.iba_bus_scan = ofwiic_scan;
	iba.iba_bus_scan_arg = &ma->ma_node;
	config_found(&sc->sc_dev, &iba, iicbus_print);
}

int
mgiic_i2c_acquire_bus(void *arg, int flags)
{
	struct mgiic_softc     *sc = arg;

	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
		return (0);

	return (rw_enter(&sc->sc_lock, RW_WRITE | RW_INTR));
}

void
mgiic_i2c_release_bus(void *arg, int flags)
{
	struct mgiic_softc     *sc = arg;

	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
		return;

	rw_exit(&sc->sc_lock);
}

int
mgiic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr,
    const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
{
	struct mgiic_softc	*sc = arg;
	int			ret = 0;

	if (addr & ~0x7f)
		return (1);

	if (cold || sc->sc_poll)
		flags |= I2C_F_POLL;

	if (cmdlen > 0) {
		ret = mgiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen);
		if (ret != 0)
			goto done;
	}

	if (len > 0) {
		if (I2C_OP_WRITE_P(op))
			ret = mgiic_xmit(sc, addr & 0x7f, buf, len);
		else
			ret = mgiic_recv(sc, addr & 0x7f, buf, len);
	}
done:
	printf("e%d\n", ret);
	return (ret);
}

int
mgiic_xmit(struct mgiic_softc *sc, u_int8_t addr, const u_int8_t *buf,
    size_t len)
{
	int err = 1, i = 0;

top:
	printf("xmit s%02x STA ", mgiic_read(sc, MGSTATUS));
	mgiic_control(sc, MGCONTROL_STA, MGCONTROL_IFLG);

	if (mgiic_poll(sc))
		goto bail;
	printf("s%02x ", mgiic_read(sc, MGSTATUS));
	if (mgiic_read(sc, MGSTATUS) != MGSTATUS_STARTSENT)
		goto bail;

	mgiic_write(sc, MGDATA, addr << 1);
	printf("a%02x ", addr << 1);
	mgiic_control(sc, 0, MGCONTROL_IFLG);

	while (i < len) {
		if (mgiic_poll(sc))
			 goto bail;
		printf("s%02x ", mgiic_read(sc, MGSTATUS));
		switch (mgiic_read(sc, MGSTATUS)) {
		case MGSTATUS_ADDR_W_ACKR:
		case MGSTATUS_MDATA_ACKR:
			mgiic_write(sc, MGDATA, buf[i]);
			printf("w%02x ", buf[i]);
			i++;
			mgiic_control(sc, 0, MGCONTROL_IFLG);
			break;
		case MGSTATUS_ADDR_W_NOACKR:
		case MGSTATUS_MDATA_NOACKR:
			mgiic_write(sc, MGDATA, buf[i]);
			printf("w%02x ", buf[i]);
			mgiic_control(sc, 0, MGCONTROL_IFLG);
			break;
		case MGSTATUS_BUSERR:
			mgiic_control(sc, MGCONTROL_STP, MGCONTROL_IFLG);
			i = 0;
			if (mgiic_poll(sc))
				goto bail;
			goto top;
		case MGSTATUS_IDLE:
		default:
			err = 1;
			goto bail;
		}
	}
	printf("OK ");
	err = 0;
bail:
	if (err)
		printf("BAIL STP s%02x\n", mgiic_read(sc, MGSTATUS));
	mgiic_control(sc, MGCONTROL_STP, MGCONTROL_IFLG);
	while (mgiic_read(sc, MGSTATUS) != MGSTATUS_IDLE)
		;
	printf("s%02x\n", mgiic_read(sc, MGSTATUS));
	return (err);
}

int
mgiic_recv(struct mgiic_softc *sc, u_int8_t addr, u_int8_t *buf, size_t len)
{
	int err = 1, i = 0;

	printf("recv s%02x ", mgiic_read(sc, MGSTATUS));
	mgiic_control(sc, MGCONTROL_STA, MGCONTROL_IFLG);
	if (mgiic_poll(sc))
		goto bail;

	printf("s%02x ", mgiic_read(sc, MGSTATUS));
	if (mgiic_read(sc, MGSTATUS) != MGSTATUS_STARTSENT)
		goto bail;

re_address:
	mgiic_write(sc, MGDATA, (addr << 1) | 0x01);
	printf("a%02x ", (addr << 1) | 0x01);
	mgiic_control(sc, 0, MGCONTROL_IFLG);

	while (i < len) {
		if (mgiic_poll(sc))
			goto bail;
		printf("s%02x ", mgiic_read(sc, MGSTATUS));
		switch (mgiic_read(sc, MGSTATUS)) {
		case MGSTATUS_ADDR_R_ACKR:
			if (len - i > 1)
				mgiic_control(sc, MGCONTROL_AAK, MGCONTROL_IFLG);
			else
				mgiic_control(sc, 0, MGCONTROL_IFLG);
			break;
		case MGSTATUS_ADDR_R_NOACKR:
			mgiic_control(sc, MGCONTROL_STA, MGCONTROL_IFLG);
			break;
		case MGSTATUS_REPEATSTART:
			goto re_address;
		case MGSTATUS_MDATA_ACKT:
			buf[i] = mgiic_read(sc, MGDATA);
			printf("r%02x ", buf[i]);
			i++;
			if (len - i > 1)
				mgiic_control(sc, MGCONTROL_AAK, MGCONTROL_IFLG);
			else
				mgiic_control(sc, 0, MGCONTROL_IFLG|MGCONTROL_AAK);
			break;
		case MGSTATUS_MDATA_NOACKT:
			buf[i] = mgiic_read(sc, MGDATA);
			printf("r%02x ", buf[i]);
			i++;
			if (len == i) {
				printf("DONE ");
				err = 0;
				goto bail;
			}
			printf("SHORT ");
			goto bail;
			break;
		default:
			printf("BAD");
			goto bail;
		}
	}
	printf("OK ");
	err = 0;
bail:
	if (err)
		printf("BAIL STP s%02x\n", mgiic_read(sc, MGSTATUS));
	mgiic_control(sc, MGCONTROL_STP, MGCONTROL_IFLG | MGCONTROL_AAK);
	while (mgiic_read(sc, MGSTATUS) != MGSTATUS_IDLE)
		;
	printf("s%02x\n", mgiic_read(sc, MGSTATUS));
	return (err);
}

volatile u_int8_t
mgiic_read(struct mgiic_softc *sc, bus_size_t r)
{
	bus_space_barrier(sc->sc_bt, sc->sc_regh, r, 8,
	    BUS_SPACE_BARRIER_READ);
	return (bus_space_read_8(sc->sc_bt, sc->sc_regh, r)) & 0xff;
}

volatile void
mgiic_write(struct mgiic_softc *sc, bus_size_t r, u_int8_t v)
{
	u_int64_t val = v;

	bus_space_write_8(sc->sc_bt, sc->sc_regh, r, val);
	bus_space_barrier(sc->sc_bt, sc->sc_regh, r, 8,
	    BUS_SPACE_BARRIER_WRITE);
}

volatile void
mgiic_control(struct mgiic_softc *sc, u_int8_t on, u_int8_t off)
{
	u_int8_t val;

	val = (mgiic_read(sc, MGCONTROL) | on) & ~off;
	mgiic_write(sc, MGCONTROL, val);
}

int
mgiic_poll(struct mgiic_softc *sc)
{
	int		i;

	for (i = 0; i < 1000; i++) {
		if (mgiic_read(sc, MGCONTROL) & MGCONTROL_IFLG)
			return (0);
		delay(100);
	}
	return (1);
}


code:
/*	$OpenBSD: msi.c,v 1.3 2014/11/24 13:16:27 kettenis Exp $	*/
/*
 * Copyright (c) 2011 Mark Kettenis <kettenis@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/device.h>
#include <sys/malloc.h>

#include <machine/bus.h>

#include <sparc64/dev/msivar.h>

struct msi_msg {
	uint64_t mm_data[8];
};

struct msi_eq *
msi_eq_alloc(bus_dma_tag_t t, int msi_eq_size)
{
	struct msi_eq *meq;
	bus_size_t size;
	caddr_t va;
	int nsegs;

	meq = malloc(sizeof(struct msi_eq), M_DEVBUF, M_NOWAIT);
	if (meq == NULL)
		return NULL;

	size = roundup(msi_eq_size * sizeof(struct msi_msg), PAGE_SIZE);

	if (bus_dmamap_create(t, size, 1, size, 0,
	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &meq->meq_map) != 0)
		return (NULL);

	if (bus_dmamem_alloc(t, size, size, 0, &meq->meq_seg, 1,
	    &nsegs, BUS_DMA_NOWAIT) != 0)
		goto destroy;

	if (bus_dmamem_map(t, &meq->meq_seg, 1, size, &va,
	    BUS_DMA_NOWAIT) != 0)
		goto free;

	if (bus_dmamap_load(t, meq->meq_map, va, size, NULL,
	    BUS_DMA_NOWAIT) != 0)
		goto unmap;

	meq->meq_va = va;
	meq->meq_nentries = msi_eq_size;
	return (meq);

unmap:
	bus_dmamem_unmap(t, va, size);
free:
	bus_dmamem_free(t, &meq->meq_seg, 1);
destroy:
	bus_dmamap_destroy(t, meq->meq_map);

	return (NULL);
}

void
msi_eq_free(bus_dma_tag_t t, struct msi_eq *meq)
{
	bus_size_t size;

	size = roundup(meq->meq_nentries * sizeof(struct msi_msg), PAGE_SIZE);

	bus_dmamap_unload(t, meq->meq_map);
	bus_dmamem_unmap(t, meq->meq_va, size);
	bus_dmamem_free(t, &meq->meq_seg, 1);
	bus_dmamap_destroy(t, meq->meq_map);
	free(meq, M_DEVBUF, 0);
}


code:
/*	$OpenBSD: msivar.h,v 1.1 2011/07/06 05:35:53 kettenis Exp $	*/
/*
 * Copyright (c) 2011 Mark Kettenis <kettenis@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 msi_eq *msi_eq_alloc(bus_dma_tag_t, int);
void msi_eq_free(bus_dma_tag_t t, struct msi_eq *);

struct msi_eq {
	bus_dmamap_t	meq_map;
	bus_dma_segment_t meq_seg;
	caddr_t		meq_va;
	int		meq_nentries;
};


code:
/*	$OpenBSD: ofwi2c.c,v 1.9 2014/02/21 21:28:26 deraadt Exp $	*/

/*
 * Copyright (c) 2006 Theo de Raadt
 *
 * 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 <dev/i2c/i2cvar.h>
#include <dev/ofw/openfirm.h>

#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <sparc64/pci_machdep.h>

#include <arch/sparc64/dev/ofwi2cvar.h>

void
ofwiic_pci_scan(struct device *self, struct i2cbus_attach_args *iba, void *aux)
{
	struct pci_attach_args *pa = aux;
	pcitag_t tag = pa->pa_tag;
	int iba_node = PCITAG_NODE(tag);
	char name[32];
	int node;

	for (node = OF_child(iba_node); node; node = OF_peer(node)) {
		memset(name, 0, sizeof(name));

		if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
			continue;
		if (name[0] == '\0')
			continue;

		if (strcmp(name, "i2c-smbus") == 0 ||
		    strcmp(name, "i2c") == 0)
			ofwiic_scan(self, iba, &node);
	}
}

void
ofwiic_scan(struct device *self, struct i2cbus_attach_args *iba, void *aux)
{
	int iba_node = *(int *)aux;
	extern int iic_print(void *, const char *);
	struct i2c_attach_args ia;
	char name[32];
	u_int32_t reg[2];
	int node;

	for (node = OF_child(iba_node); node; node = OF_peer(node)) {
		memset(name, 0, sizeof(name));
		memset(reg, 0, sizeof(reg));

		if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
			continue;
		if (name[0] == '\0')
			continue;

		if (OF_getprop(node, "reg", reg, sizeof(reg)) == -1)
			continue;

		memset(&ia, 0, sizeof(ia));
		ia.ia_tag = iba->iba_tag;
		ia.ia_addr = (reg[0] << 7) | (reg[1] >> 1);
		ia.ia_name = name;
		ia.ia_cookie = &node;

		if (strncmp(ia.ia_name, "i2c-", strlen("i2c-")) == 0)
			ia.ia_name += strlen("i2c-");

		/* Skip non-SPD EEPROMs.  */
		if (strcmp(ia.ia_name, "at24c64") == 0 ||
		    strcmp(ia.ia_name, "at34c02") == 0) {
			if (OF_getprop(node, "name", name, sizeof(name)) == -1)
				continue;
			if (strcmp(name, "dimm") == 0 ||
			    strcmp(name, "dimm-spd") == 0)
				ia.ia_name = "spd";
			else
				continue;
		}

		/*
		 * XXX alipm crashes on some machines for an unknown reason
		 * when doing the periodic i2c accesses things like sensors
		 * need.  However, devices accessed only at boot are fine.
		 */
		if (strcmp(self->dv_parent->dv_xname, "alipm0") == 0 &&
		    (ia.ia_addr < 0x50 || ia.ia_addr > 0x57)) {
			iic_print(&ia, self->dv_parent->dv_xname);
			printf(" skipped due to %s bugs\n",
			    self->dv_parent->dv_xname);
			continue;
		}

		config_found(self, &ia, iic_print);
	}
}


code:
/*	$OpenBSD: ofwi2cvar.h,v 1.2 2006/02/09 12:16:25 dlg Exp $	*/

/*
 * Copyright (c) 2006 Theo de Raadt
 *
 * 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 <dev/i2c/i2cvar.h>

void	ofwiic_pci_scan(struct device *, struct i2cbus_attach_args *, void *);
void	ofwiic_scan(struct device *, struct i2cbus_attach_args *, void *);


code:
/*	$OpenBSD: pcf8591_envctrl.c,v 1.6 2007/10/25 21:17:20 kettenis Exp $ */

/*
 * Copyright (c) 2006 Damien Miller <djm@openbsd.org>
 * Copyright (c) 2007 Mark Kettenis <kettenis@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/sensors.h>

#include <dev/ofw/openfirm.h>
#include <dev/i2c/i2cvar.h>

#define PCF8591_CHANNELS	4

#define PCF8591_CTRL_CH0	0x00
#define PCF8591_CTRL_CH1	0x01
#define PCF8591_CTRL_CH2	0x02
#define PCF8591_CTRL_CH3	0x03
#define PCF8591_CTRL_AUTOINC	0x04
#define PCF8591_CTRL_OSCILLATOR	0x40

struct ecadc_channel {
	u_int		chan_num;
	struct ksensor	chan_sensor;
	u_char		*chan_xlate;
	int64_t		chan_factor;
	int64_t		chan_min;
	int64_t		chan_warn;
	int64_t		chan_crit;
};

struct ecadc_softc {
	struct device		sc_dev;
	i2c_tag_t		sc_tag;
	i2c_addr_t		sc_addr;
	u_char			sc_ps_xlate[256];
	u_char			sc_cpu_xlate[256];
	u_int			sc_nchan;
	struct ecadc_channel	sc_channels[PCF8591_CHANNELS];
	struct ksensordev	sc_sensordev;
};

int	ecadc_match(struct device *, void *, void *);
void	ecadc_attach(struct device *, struct device *, void *);
void	ecadc_refresh(void *);

struct cfattach ecadc_ca = {
	sizeof(struct ecadc_softc), ecadc_match, ecadc_attach
};

struct cfdriver ecadc_cd = {
	NULL, "ecadc", DV_DULL
};

int
ecadc_match(struct device *parent, void *match, void *aux)
{
	struct i2c_attach_args *ia = aux;

	if (strcmp(ia->ia_name, "ecadc") != 0)
		return (0);

	return (1);
}

void
ecadc_attach(struct device *parent, struct device *self, void *aux)
{
	struct ecadc_softc *sc = (struct ecadc_softc *)self;
	u_char term[256];
	u_char *cp, *desc;
	int64_t min, warn, crit, num, den;
	u_int8_t junk[PCF8591_CHANNELS + 1];
	struct i2c_attach_args *ia = aux;
	struct ksensor *sensor;
	int len, addr, chan, node = *(int *)ia->ia_cookie;
	u_int i;

	if ((len = OF_getprop(node, "thermisters", term,
	    sizeof(term))) < 0) {
		printf(": couldn't find \"thermisters\" property\n");
		return;
	}

	if (OF_getprop(node, "cpu-temp-factors", &sc->sc_cpu_xlate[2],
	    sizeof(sc->sc_cpu_xlate) - 2) < 0) {
		printf(": couldn't find \"cpu-temp-factors\" property\n");
		return;
	}
	sc->sc_cpu_xlate[0] = sc->sc_cpu_xlate[1] = sc->sc_cpu_xlate[2];

	/* Only the Sun Enterprise 450 has these. */
	OF_getprop(node, "ps-temp-factors", &sc->sc_ps_xlate[2],
	    sizeof(sc->sc_ps_xlate) - 2);
	sc->sc_ps_xlate[0] = sc->sc_ps_xlate[1] = sc->sc_ps_xlate[2];

	cp = term;
	while (cp < term + len) {
		addr = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
		chan = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
		min = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
		warn = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
		crit = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
		num = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
		den = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
		desc = cp;
		while (cp < term + len && *cp++);

		if (addr != (ia->ia_addr << 1))
			continue;

		if (num == 0 || den == 0)
			num = den = 1;

		sc->sc_channels[sc->sc_nchan].chan_num = chan;

		sensor = &sc->sc_channels[sc->sc_nchan].chan_sensor;
		sensor->type = SENSOR_TEMP;
		strlcpy(sensor->desc, desc, sizeof(sensor->desc));

		if (strncmp(desc, "CPU", 3) == 0)
			sc->sc_channels[sc->sc_nchan].chan_xlate =
			    sc->sc_cpu_xlate;
		else if (strncmp(desc, "PS", 2) == 0)
			sc->sc_channels[sc->sc_nchan].chan_xlate =
			    sc->sc_ps_xlate;
		else
			sc->sc_channels[sc->sc_nchan].chan_factor =
			    (1000000 * num) / den;
		sc->sc_channels[sc->sc_nchan].chan_min =
		    273150000 + 1000000 * min;
		sc->sc_channels[sc->sc_nchan].chan_warn =
		    273150000 + 1000000 * warn;
		sc->sc_channels[sc->sc_nchan].chan_crit =
		    273150000 + 1000000 * crit;
		sc->sc_nchan++;
	}

	sc->sc_tag = ia->ia_tag;
	sc->sc_addr = ia->ia_addr;

	iic_acquire_bus(sc->sc_tag, 0);

	/* Try a read now, so we can fail if it doesn't work */
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    NULL, 0, junk, sc->sc_nchan + 1, 0)) {
		printf(": read failed\n");
		iic_release_bus(sc->sc_tag, 0);
		return;
	}

	iic_release_bus(sc->sc_tag, 0);

	/* Initialize sensor data. */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));

	for (i = 0; i < sc->sc_nchan; i++)
		sensor_attach(&sc->sc_sensordev,
		    &sc->sc_channels[i].chan_sensor);

	if (sensor_task_register(sc, ecadc_refresh, 5) == NULL) {
		printf(": unable to register update task\n");
		return;
	}

	sensordev_install(&sc->sc_sensordev);

	printf("\n");
}

void
ecadc_refresh(void *arg)
{
	struct ecadc_softc *sc = arg;
	u_int i;
	u_int8_t data[PCF8591_CHANNELS + 1];
	u_int8_t ctrl = PCF8591_CTRL_CH0 | PCF8591_CTRL_AUTOINC |
	    PCF8591_CTRL_OSCILLATOR;

	iic_acquire_bus(sc->sc_tag, 0);
	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
	    &ctrl, 1, NULL, 0, 0)) {
		iic_release_bus(sc->sc_tag, 0);
		return;
	}
	/* NB: first byte out is stale, so read num_channels + 1 */
	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
	    NULL, 0, data, PCF8591_CHANNELS + 1, 0)) {
		iic_release_bus(sc->sc_tag, 0);
		return;
	}
	iic_release_bus(sc->sc_tag, 0);

	/* We only support temperature channels. */
	for (i = 0; i < sc->sc_nchan; i++) {
		struct ecadc_channel *chp = &sc->sc_channels[i];

		if (chp->chan_xlate)
			chp->chan_sensor.value = 273150000 + 1000000 *
			    chp->chan_xlate[data[1 + chp->chan_num]];
		else
			chp->chan_sensor.value = 273150000 +
			    chp->chan_factor * data[1 + chp->chan_num];

		chp->chan_sensor.status = SENSOR_S_OK;
		if (chp->chan_sensor.value < chp->chan_min)
			chp->chan_sensor.status = SENSOR_S_UNKNOWN;
		if (chp->chan_sensor.value > chp->chan_warn)
			chp->chan_sensor.status = SENSOR_S_WARN;
		if (chp->chan_sensor.value > chp->chan_crit)
			chp->chan_sensor.status = SENSOR_S_CRIT;
	}
}