code:
/*	$OpenBSD: pm_direct.c,v 1.25 2013/04/23 07:38:05 mpi Exp $	*/
/*	$NetBSD: pm_direct.c,v 1.9 2000/06/08 22:10:46 tsubai Exp $	*/

/*
 * Copyright (C) 1997 Takashi Hamada
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *  This product includes software developed by Takashi Hamada
 * 4. 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.
 */

#ifdef DEBUG
#ifndef ADB_DEBUG
#define ADB_DEBUG
#endif
#endif

/* #define	PM_GRAB_SI	1 */

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

#include <machine/cpu.h>

#include <dev/adb/adb.h>
#include <macppc/dev/adbvar.h>
#include <macppc/dev/pm_direct.h>
#include <macppc/dev/viareg.h>

/* hardware dependent values */
#define ADBDelay 100		/* XXX */

/* useful macros */
#define PM_SR()			read_via_reg(VIA1, vSR)
#define PM_VIA_INTR_ENABLE()	write_via_reg(VIA1, vIER, 0x90)
#define PM_VIA_INTR_DISABLE()	write_via_reg(VIA1, vIER, 0x10)
#define PM_VIA_CLR_INTR()	write_via_reg(VIA1, vIFR, 0x90)
#if 0
#define PM_SET_STATE_ACKON()	via_reg_or(VIA2, vBufB, 0x04)
#define PM_SET_STATE_ACKOFF()	via_reg_and(VIA2, vBufB, ~0x04)
#define PM_IS_ON		(0x02 == (read_via_reg(VIA2, vBufB) & 0x02))
#define PM_IS_OFF		(0x00 == (read_via_reg(VIA2, vBufB) & 0x02))
#else
#define PM_SET_STATE_ACKON()	via_reg_or(VIA2, vBufB, 0x10)
#define PM_SET_STATE_ACKOFF()	via_reg_and(VIA2, vBufB, ~0x10)
#define PM_IS_ON		(0x08 == (read_via_reg(VIA2, vBufB) & 0x08))
#define PM_IS_OFF		(0x00 == (read_via_reg(VIA2, vBufB) & 0x08))
#endif

/* these values shows that number of data returned after 'send' cmd is sent */
signed char pm_send_cmd_type[] = {
	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
	0x00, 0x00,   -1,   -1,   -1,   -1,   -1, 0x00,
	  -1, 0x00, 0x02, 0x01, 0x01,   -1,   -1,   -1,
	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
	0x04, 0x14,   -1, 0x03,   -1,   -1,   -1,   -1,
	0x00, 0x00, 0x02, 0x02,   -1,   -1,   -1,   -1,
	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
	0x00, 0x00,   -1,   -1, 0x01,   -1,   -1,   -1,
	0x01, 0x00, 0x02, 0x02,   -1, 0x01, 0x03, 0x01,
	0x00, 0x01, 0x00, 0x00, 0x00,   -1,   -1,   -1,
	0x02,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   -1,   -1,
	0x01, 0x01, 0x01,   -1,   -1,   -1,   -1,   -1,
	0x00, 0x00,   -1,   -1,   -1,   -1, 0x04, 0x04,
	0x04,   -1, 0x00,   -1,   -1,   -1,   -1,   -1,
	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
	0x01, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
	0x00, 0x00,   -1,   -1,   -1,   -1,   -1,   -1,
	0x02, 0x02, 0x02, 0x04,   -1, 0x00,   -1,   -1,
	0x01, 0x01, 0x03, 0x02,   -1,   -1,   -1,   -1,
	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
	0x01, 0x01,   -1,   -1, 0x00, 0x00,   -1,   -1,
	  -1, 0x04, 0x00,   -1,   -1,   -1,   -1,   -1,
	0x03,   -1, 0x00,   -1, 0x00,   -1,   -1, 0x00,
	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1
};

/* these values shows that number of data returned after 'receive' cmd is sent */
signed char pm_receive_cmd_type[] = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x02, 0x02,   -1,   -1,   -1,   -1,   -1, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x05, 0x15,   -1, 0x02,   -1,   -1,   -1,   -1,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x02, 0x00, 0x03, 0x03,   -1,   -1,   -1,   -1,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x04, 0x04, 0x03, 0x09,   -1,   -1,   -1,   -1,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  -1,   -1,   -1,   -1,   -1,   -1, 0x01, 0x01,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x06,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x02, 0x00, 0x00, 0x00,   -1,   -1,   -1,   -1,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x02, 0x02,   -1,   -1, 0x02,   -1,   -1,   -1,
	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
	  -1,   -1, 0x02,   -1,   -1,   -1,   -1, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
};

/*
 * Define the private functions
 */

/* for debugging */
#ifdef ADB_DEBUG
void	pm_printerr(char *, int, int, char *);
#endif

int	pm_wait_busy(int);
int	pm_wait_free(int);
int	pm_receive(u_char *);
int	pm_send(u_char);

/* these functions also use the variables of adb_direct.c */
void	pm_adb_get_TALK_result(PMData *);
void	pm_adb_get_ADB_data(PMData *);

/*
 * These variables are in adb_direct.c.
 */
extern u_char	*adbBuffer;	/* pointer to user data area */
extern void	*adbCompRout;	/* pointer to the completion routine */
extern void	*adbCompData;	/* pointer to the completion routine data */
extern int	adbWaiting;	/* waiting for return data from the device */
extern int	adbWaitingCmd;	/* ADB command we are waiting for */
extern int	adbStarting;	/* doing ADB reinit, so do "polling" differently */

#define	ADB_MAX_MSG_LENGTH	16
#define	ADB_MAX_HDR_LENGTH	8
struct adbCommand {
	u_char	header[ADB_MAX_HDR_LENGTH];	/* not used yet */
	u_char	data[ADB_MAX_MSG_LENGTH];	/* packet data only */
	u_char	*saveBuf;	/* where to save result */
	u_char	*compRout;	/* completion routine pointer */
	u_char	*compData;	/* completion routine data pointer */
	u_int	cmd;		/* the original command for this data */
	u_int	unsol;		/* 1 if packet was unsolicited */
	u_int	ack_only;	/* 1 for no special processing */
};
extern	void	adb_pass_up(struct adbCommand *);

#ifdef ADB_DEBUG
/*
 * This function dumps contents of the PMData
 */
void
pm_printerr(ttl, rval, num, data)
	char *ttl;
	int rval;
	int num;
	char *data;
{
	int i;

	printf("pm: %s:%04x %02x ", ttl, rval, num);
	for (i = 0; i < num; i++)
		printf("%02x ", data[i]);
	printf("\n");
}
#endif

/*
 * Wait until PM IC is busy
 */
int
pm_wait_busy(int delay)
{
	while (PM_IS_ON) {
#ifdef PM_GRAB_SI
		(void)intr_dispatch(0x70);
#endif
		if ((--delay) < 0)
			return 1;	/* timeout */
	}
	return 0;
}

/*
 * Wait until PM IC is free
 */
int
pm_wait_free(int delay)
{
	while (PM_IS_OFF) {
#ifdef PM_GRAB_SI
		(void)intr_dispatch(0x70);
#endif
		if ((--delay) < 0)
			return 0;	/* timeout */
	}
	return 1;
}

/*
 * Functions for the PB Duo series and the PB 5XX series
 */

/*
 * Receive data from PM for the PB Duo series and the PB 5XX series
 */
int
pm_receive(u_char *data)
{
	int i;
	int rval;

	rval = 0xffffcd34;

	switch (1) {
	default:
		/* set VIA SR to input mode */
		via_reg_or(VIA1, vACR, 0x0c);
		via_reg_and(VIA1, vACR, ~0x10);
		i = PM_SR();

		PM_SET_STATE_ACKOFF();
		if (pm_wait_busy((int)ADBDelay*32) != 0)
			break;		/* timeout */

		PM_SET_STATE_ACKON();
		rval = 0xffffcd33;
		if (pm_wait_free((int)ADBDelay*32) == 0)
			break;		/* timeout */

		*data = PM_SR();
		rval = 0;

		break;
	}

	PM_SET_STATE_ACKON();
	via_reg_or(VIA1, vACR, 0x1c);

	return rval;
}

/*
 * Send data to PM for the PB Duo series and the PB 5XX series
 */
int
pm_send(data)
	u_char data;
{
	int rval;

	via_reg_or(VIA1, vACR, 0x1c);
	write_via_reg(VIA1, vSR, data);	/* PM_SR() = data; */

	PM_SET_STATE_ACKOFF();
	rval = 0xffffcd36;
	if (pm_wait_busy((int)ADBDelay*32) != 0) {
		PM_SET_STATE_ACKON();
		via_reg_or(VIA1, vACR, 0x1c);
		return rval;
	}

	PM_SET_STATE_ACKON();
	rval = 0xffffcd35;
	if (pm_wait_free((int)ADBDelay*32) != 0)
		rval = 0;

	PM_SET_STATE_ACKON();
	via_reg_or(VIA1, vACR, 0x1c);

	return rval;
}

/*
 * My PMgrOp routine for the PB Duo series and the PB 5XX series
 */
int
pmgrop(PMData *pmdata)
{
	int i;
	int s;
	u_char via1_vIER;
	int rval = 0;
	int num_pm_data = 0;
	u_char pm_cmd;
	short pm_num_rx_data;
	u_char pm_data;
	u_char *pm_buf;

	s = splhigh();

	/* disable all interrupts but PM */
	via1_vIER = 0x10;
	via1_vIER &= read_via_reg(VIA1, vIER);
	write_via_reg(VIA1, vIER, via1_vIER);
	if (via1_vIER != 0x0)
		via1_vIER |= 0x80;

	switch (pmdata->command) {
	default:
		/* wait until PM is free */
		pm_cmd = (u_char)(pmdata->command & 0xff);
		rval = 0xcd38;
		if (pm_wait_free(ADBDelay * 4) == 0)
			break;			/* timeout */

		/* send PM command */
		if ((rval = pm_send((u_char)(pm_cmd & 0xff))))
			break;				/* timeout */

		/* send number of PM data */
		num_pm_data = pmdata->num_data;
		if (pm_send_cmd_type[pm_cmd] < 0) {
			if ((rval = pm_send((u_char)(num_pm_data & 0xff))) != 0)
				break;		/* timeout */
			pmdata->command = 0;
		}
		/* send PM data */
		pm_buf = (u_char *)pmdata->s_buf;
		for (i = 0 ; i < num_pm_data; i++)
			if ((rval = pm_send(pm_buf[i])) != 0)
				break;			/* timeout */
		if (i != num_pm_data)
			break;				/* timeout */

		/* check if PM will send me data  */
		pm_num_rx_data = pm_receive_cmd_type[pm_cmd];
		pmdata->num_data = pm_num_rx_data;
		if (pm_num_rx_data == 0) {
			rval = 0;
			break;				/* no return data */
		}

		/* receive PM command */
		pm_data = pmdata->command;
		pm_num_rx_data--;
		if (pm_num_rx_data == 0)
			if ((rval = pm_receive(&pm_data)) != 0) {
				rval = 0xffffcd37;
				break;
			}
		pmdata->command = pm_data;

		/* receive number of PM data */
		if (pm_num_rx_data < 0) {
			if ((rval = pm_receive(&pm_data)) != 0)
				break;		/* timeout */
			num_pm_data = pm_data;
		} else
			num_pm_data = pm_num_rx_data;
		pmdata->num_data = num_pm_data;

		/* receive PM data */
		pm_buf = (u_char *)pmdata->r_buf;
		for (i = 0; i < num_pm_data; i++) {
			if ((rval = pm_receive(&pm_data)) != 0)
				break;			/* timeout */
			pm_buf[i] = pm_data;
		}

		rval = 0;
	}

	/* restore former value */
	write_via_reg(VIA1, vIER, via1_vIER);
	splx(s);

	return rval;
}

/*
 * My PM interrupt routine for the PB Duo series and the PB 5XX series
 */
void
pm_intr()
{
	int s;
	int rval;
	PMData pmdata;

	s = splhigh();

	PM_VIA_CLR_INTR();			/* clear VIA1 interrupt */
						/* ask PM what happend */
	pmdata.command = 0x78;
	pmdata.num_data = 0;
	pmdata.s_buf = &pmdata.data[2];
	pmdata.r_buf = &pmdata.data[2];
	rval = pmgrop(&pmdata);
	if (rval != 0) {
#ifdef ADB_DEBUG
		if (adb_debug)
			printf("pm: PM is not ready. error code: %08x\n", rval);
#endif
		splx(s);
		return;
	}

	switch ((u_int)(pmdata.data[2] & 0xff)) {
	case 0x00:		/* 1 sec interrupt? */
		break;
	case PMU_INT_TICK:	/* 1 sec interrupt? */
		break;
	case PMU_INT_SNDBRT:	/* Brightness/Contrast button on LCD panel */
		break;
	case PMU_INT_ADB:	/* ADB data requested by TALK command */
	case PMU_INT_ADB|PMU_INT_ADB_AUTO:
		pm_adb_get_TALK_result(&pmdata);
		break;
	case 0x16:		/* ADB device event */
	case 0x18:
	case 0x1e:
	case PMU_INT_WAKEUP:
		pm_adb_get_ADB_data(&pmdata);
		break;
	default:
#ifdef ADB_DEBUG
		if (adb_debug)
			pm_printerr("driver does not support this event.",
			    pmdata.data[2], pmdata.num_data,
			    pmdata.data);
#endif
		break;
	}

	splx(s);
}

/*
 * Synchronous ADBOp routine for the Power Manager
 */
int
pm_adb_op(u_char *buffer, void *compRout, void *data, int command)
{
	int i;
	int s;
	int rval;
	int ndelay;
	int waitfor;	/* interrupts to poll for */
	int ifr;
#ifdef ADB_DEBUG
	int oldifr;
#endif
	PMData pmdata;
	struct adbCommand packet;
	extern int adbempty;

	if (adbWaiting == 1)
		return 1;

	s = splhigh();
	write_via_reg(VIA1, vIER, 0x10);

	adbBuffer = buffer;
	adbCompRout = compRout;
	adbCompData = data;

	pmdata.command = 0x20;
	pmdata.s_buf = pmdata.data;
	pmdata.r_buf = pmdata.data;

	/*
	 * if the command is LISTEN,
	 * add number of ADB data to number of PM data
	 */
	if ((command & 0xc) == 0x8) {
		if (buffer != (u_char *)0)
			pmdata.num_data = buffer[0] + 3;
	} else
		pmdata.num_data = 3;

	/*
	 * Resetting adb on several models, such as
	 * - PowerBook3,*
	 * - PowerBook5,*
	 * - PowerMac10,1
	 * causes several pmu interrupts with ifr set to PMU_INT_SNDBRT.
	 * Not processing them prevents us from seeing the adb devices
	 * afterwards, so we have to expect it unless we know the adb
	 * bus is empty.
	 */
	if (command == PMU_RESET_ADB) {
		waitfor = PMU_INT_ADB_AUTO | PMU_INT_ADB;
		if (adbempty == 0)
			waitfor |= PMU_INT_SNDBRT;
	} else
		waitfor = PMU_INT_ALL;

	pmdata.data[0] = (u_char)(command & 0xff);
	pmdata.data[1] = 0;
	/* if the command is LISTEN, copy ADB data to PM buffer */
	if ((command & 0xc) == 0x8) {
		if ((buffer != (u_char *)0) && (buffer[0] <= 24)) {
			pmdata.data[2] = buffer[0];	/* number of data */
			for (i = 0; i < buffer[0]; i++)
				pmdata.data[3 + i] = buffer[1 + i];
		} else
			pmdata.data[2] = 0;
	} else
		pmdata.data[2] = 0;

	if ((command & 0xc) != 0xc) {	/* if the command is not TALK */
		/* set up stuff for adb_pass_up */
		packet.data[0] = 1 + pmdata.data[2];
		packet.data[1] = command;
		for (i = 0; i < pmdata.data[2]; i++)
			packet.data[i+2] = pmdata.data[i+3];
		packet.saveBuf = adbBuffer;
		packet.compRout = adbCompRout;
		packet.compData = adbCompData;
		packet.cmd = command;
		packet.unsol = 0;
		packet.ack_only = 1;
		adb_polling = 1;
		adb_pass_up(&packet);
		adb_polling = 0;
	}

	rval = pmgrop(&pmdata);
	if (rval != 0) {
		splx(s);
		return 1;
	}

	delay (1000);

	adbWaiting = 1;
	adbWaitingCmd = command;

	PM_VIA_INTR_ENABLE();

	/* wait until the PM interrupt is occurred */
	ndelay = 0x8000;
#ifdef ADB_DEBUG
	oldifr = 0;
#endif
	while (adbWaiting == 1) {
		ifr = read_via_reg(VIA1, vIFR);
		if (ifr & waitfor) {
			pm_intr();
#ifdef PM_GRAB_SI
			(void)intr_dispatch(0x70);
#endif
#ifdef ADB_DEBUG
		} else if (ifr != oldifr) {
			if (adb_debug)
				printf("pm_adb_op: ignoring ifr %02x"
				    ", expecting %02x\n",
				    (u_int)ifr, (u_int)waitfor);
			oldifr = ifr;
#endif
		}
		if ((--ndelay) < 0) {
			splx(s);
			return 1;
		}
		delay(10);
	}

	/* this command enables the interrupt by operating ADB devices */
	pmdata.command = 0x20;
	pmdata.num_data = 4;
	pmdata.s_buf = pmdata.data;
	pmdata.r_buf = pmdata.data;
	pmdata.data[0] = 0x00;
	pmdata.data[1] = 0x86;	/* magic spell for awaking the PM */
	pmdata.data[2] = 0x00;
	pmdata.data[3] = 0x0c;	/* each bit may express the existent ADB device */
	rval = pmgrop(&pmdata);

	splx(s);
	return rval;
}

void
pm_adb_get_TALK_result(PMData *pmdata)
{
	int i;
	struct adbCommand packet;

	/* set up data for adb_pass_up */
	packet.data[0] = pmdata->num_data-1;
	packet.data[1] = pmdata->data[3];
	for (i = 0; i <packet.data[0]-1; i++)
		packet.data[i+2] = pmdata->data[i+4];

	packet.saveBuf = adbBuffer;
	packet.compRout = adbCompRout;
	packet.compData = adbCompData;
	packet.unsol = 0;
	packet.ack_only = 0;
	adb_polling = 1;
	adb_pass_up(&packet);
	adb_polling = 0;

	adbWaiting = 0;
	adbBuffer = (long)0;
	adbCompRout = (long)0;
	adbCompData = (long)0;
}

void
pm_adb_get_ADB_data(PMData *pmdata)
{
	int i;
	struct adbCommand packet;

	/* set up data for adb_pass_up */
	packet.data[0] = pmdata->num_data-1;	/* number of raw data */
	packet.data[1] = pmdata->data[3];	/* ADB command */
	for (i = 0; i <packet.data[0]-1; i++)
		packet.data[i+2] = pmdata->data[i+4];
	packet.unsol = 1;
	packet.ack_only = 0;
	adb_pass_up(&packet);
}

void
pm_adb_restart()
{
	PMData p;

	p.command = PMU_RESET_CPU;
	p.num_data = 0;
	p.s_buf = p.data;
	p.r_buf = p.data;
	pmgrop(&p);
}

void
pm_adb_poweroff()
{
	PMData p;

	bzero(&p, sizeof p);
	p.command = PMU_POWER_OFF;
	p.num_data = 4;
	p.s_buf = p.data;
	p.r_buf = p.data;
	strlcpy(p.data, "MATT", sizeof p.data);
	pmgrop(&p);
}

void
pm_read_date_time(time_t *time)
{
	PMData p;
	u_int32_t t;

	p.command = PMU_READ_RTC;
	p.num_data = 0;
	p.s_buf = p.data;
	p.r_buf = p.data;
	pmgrop(&p);

	bcopy(p.data, &t, sizeof(t));
	*time = (time_t)t;
}

void
pm_set_date_time(time_t time)
{
	PMData p;
	u_int32_t t = time;		/* XXX eventually truncates */

	p.command = PMU_SET_RTC;
	p.num_data = sizeof(t);
	p.s_buf = p.r_buf = p.data;
	bcopy(&t, p.data, sizeof(t));
	pmgrop(&p);
}

#if 0
void
pm_eject_pcmcia(int slot)
{
	PMData p;

	if (slot != 0 && slot != 1)
		return;

	p.command = PMU_EJECT_PCMCIA;
	p.num_data = 1;
	p.s_buf = p.r_buf = p.data;
	p.data[0] = 5 + slot;	/* XXX */
	pmgrop(&p);
}
#endif

/*
 * Thanks to Paul Mackerras and Fabio Riccardi's Linux implementation
 * for a clear description of the PMU results.
 */

int
pm_battery_info(int battery, struct pmu_battery_info *info)
{
	PMData p;

	p.command = PMU_SMART_BATTERY_STATE;
	p.num_data = 1;
	p.s_buf = p.r_buf = p.data;
	p.data[0] = battery + 1;
	pmgrop(&p);

	info->flags = p.data[1];

	switch (p.data[0]) {
	case 3:
	case 4:
		info->cur_charge = p.data[2];
		info->max_charge = p.data[3];
		info->draw = *((signed char *)&p.data[4]);
		info->voltage = p.data[5];
		break;
	case 5:
		info->cur_charge = ((p.data[2] << 8) | (p.data[3]));
		info->max_charge = ((p.data[4] << 8) | (p.data[5]));
		info->draw = *((signed short *)&p.data[6]);
		info->voltage = ((p.data[8] << 8) | (p.data[7]));
		break;
	default:
		/* XXX - Error condition */
		info->cur_charge = 0;
		info->max_charge = 0;
		info->draw = 0;
		info->voltage = 0;
		break;
	}

	return 1;
}

void
pmu_fileserver_mode(int on)
{
	PMData p;

	p.command = PMU_POWER_EVENTS;
	p.num_data = 1;
	p.s_buf = p.r_buf = p.data;
	p.data[0] = PMU_PWR_GET_POWERUP_EVENTS;
	pmgrop(&p);

	p.command = PMU_POWER_EVENTS;
	p.num_data = 3;
	p.s_buf = p.r_buf = p.data;
	p.data[1] = p.data[0];   /* result from the get */
	if (on) {
		p.data[0] = PMU_PWR_SET_POWERUP_EVENTS;
		p.data[2] = PMU_WAKE_AC_LOSS;
	} else {
		p.data[0] = PMU_PWR_CLR_POWERUP_EVENTS;
		p.data[2] = PMU_WAKE_AC_LOSS;
	}
	pmgrop(&p);
}


code:
/*	$OpenBSD: pm_direct.h,v 1.13 2012/09/02 08:16:40 mpi Exp $	*/
/*	$NetBSD: pm_direct.h,v 1.7 2005/01/07 04:59:58 briggs Exp $	*/

/*
 * Copyright (C) 1997 Takashi Hamada
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *  This product includes software developed by Takashi Hamada.
 * 4. 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.
 */
/* From: pm_direct.h 1.0 01/02/97 Takashi Hamada */
#ifndef _PM_DIRECT_H_
#define _PM_DIRECT_H_

/*
 * Public declarations that other routines may need.
 */

/* data structure of the command of the Power Manager */
typedef	struct	{
	short	command;	/* command of the Power Manager 	*/
	short	num_data;	/* number of data			*/
	char	*s_buf;		/* pointer to buffer for sending 	*/
	char	*r_buf;		/* pointer to buffer for receiving	*/
	char	data[128];	/* data buffer (is it too much?)	*/
				/* null command seen w/ 120 data bytes  */
}	PMData;

int	pmgrop(PMData *);
int	pm_adb_op(u_char *, void *, void *, int);
void	pm_adb_restart(void);
void	pm_adb_poweroff(void);
void	pm_intr(void);
void	pm_read_date_time(time_t *);
void	pm_set_date_time(time_t);

struct pmu_battery_info
{
	unsigned int flags;
	unsigned int cur_charge;
	unsigned int max_charge;
	signed   int draw;
	unsigned int voltage;
};

int pm_battery_info(int, struct pmu_battery_info *);

void pm_eject_pcmcia(int);
void pmu_fileserver_mode(int);

/* PMU commands */
#define PMU_RESET_ADB		0x00	/* Reset ADB */
#define PMU_POWER_OFF		0x7e	/* Turn Power off */
#define PMU_RESET_CPU		0xd0	/* Reset CPU */

#define PMU_SET_RTC		0x30	/* Set realtime clock */
#define PMU_READ_RTC		0x38	/* Read realtime clock */

#define PMU_WRITE_PRAM		0x32	/* Write PRAM */
#define PMU_READ_PRAM		0x3a	/* Read PRAM */

#define PMU_WRITE_NVRAM		0x33	/* Write NVRAM */
#define PMU_READ_NVRAM		0x3b	/* Read NVRAM */

#define PMU_EJECT_PCMCIA	0x4c	/* Eject PCMCIA slot */

#define PMU_SET_BRIGHTNESS	0x41	/* Set backlight brightness */
#define PMU_READ_BRIGHTNESS	0xd9	/* Read brightness button position */

#define PMU_POWER_EVENTS        0x8f    /* Send power-event commands to PMU */
#define PMU_SYSTEM_READY        0xdf    /* tell PMU we are awake */

#define PMU_SMART_BATTERY_STATE	0x6f	/* Read battery state */

#define PMU_I2C			0x9a	/* I2C */

/* Bits in PMU interrupt and interrupt mask bytes */
#define PMU_INT_ADB_AUTO	0x04	/* ADB autopoll, when PMU_INT_ADB */
#define PMU_INT_PCEJECT		0x04	/* PC-card eject buttons */
#define PMU_INT_SNDBRT		0x08	/* sound/brightness up/down buttons */
#define PMU_INT_ADB		0x10	/* ADB autopoll or reply data */
#define PMU_INT_BATTERY		0x20
#define PMU_INT_WAKEUP		0x40
#define PMU_INT_TICK		0x80	/* 1-second tick interrupt */
#define PMU_INT_ALL		0xff	/* Mask of all interrupts */

/* Bits to use with the PMU_POWER_CTRL0 command */
#define PMU_POW0_ON		0x80	/* OR this to power ON the device */
#define PMU_POW0_OFF		0x00	/* leave bit 7 to 0 to power it OFF */

/* Bits to use with the PMU_POWER_CTRL command */
#define PMU_POW_ON		0x80	/* OR this to power ON the device */
#define PMU_POW_OFF		0x00	/* leave bit 7 to 0 to power it OFF */
#define PMU_POW_BACKLIGHT	0x01	/* backlight power */
#define PMU_POW_CHARGER		0x02	/* battery charger power */
#define PMU_POW_IRLED		0x04	/* IR led power (on wallstreet) */
#define PMU_POW_MEDIABAY	0x08	/* media bay power (wallstreet/lombard ?) */

/* PMU PMU_POWER_EVENTS commands */
enum {
	PMU_PWR_GET_POWERUP_EVENTS      = 0x00,
	PMU_PWR_SET_POWERUP_EVENTS      = 0x01,
	PMU_PWR_CLR_POWERUP_EVENTS      = 0x02,
	PMU_PWR_GET_WAKEUP_EVENTS       = 0x03,
	PMU_PWR_SET_WAKEUP_EVENTS       = 0x04,
	PMU_PWR_CLR_WAKEUP_EVENTS       = 0x05,
};

/* PMU WAKE ON EVENTS */

#define PMU_WAKE_KEYB		0x01
#define PMU_WAKE_AC_LOSS	0x02
#define PMU_WAKE_AC_CHG		0x04
#define PMU_WAKE_LID		0x08
#define PMU_WAKE_RING		0x10

/* PMU Power Information */

#define PMU_PWR_AC_PRESENT	(1 << 0)
#define PMU_PWR_BATT_PRESENT	(1 << 2)

/* PMU I2C */
#define PMU_I2C_SIMPLE		0x00
#define PMU_I2C_NORMAL		0x01
#define PMU_I2C_COMBINED	0x02

#endif

license: unknown


code:
/*	$OpenBSD: smu.c,v 1.27 2015/06/04 18:01:44 kettenis Exp $	*/

/*
 * Copyright (c) 2005 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/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/rwlock.h>
#include <sys/proc.h>
#include <sys/sensors.h>

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

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

#include <macppc/dev/maci2cvar.h>
#include <macppc/pci/macobio.h>

int     smu_match(struct device *, void *, void *);
void    smu_attach(struct device *, struct device *, void *);

#define SMU_MAXFANS	8

struct smu_fan {
	u_int8_t	reg;
	u_int16_t	min_rpm;
	u_int16_t	max_rpm;
	u_int16_t	unmanaged_rpm;
	struct ksensor	sensor;
};

#define SMU_MAXSENSORS	4

struct smu_sensor {
	u_int8_t	reg;
	struct ksensor	sensor;
};

struct smu_softc {
        struct device   sc_dev;

	/* SMU command buffer. */
        bus_dma_tag_t   sc_dmat;
        bus_dmamap_t    sc_cmdmap;
        bus_dma_segment_t sc_cmdseg[1];
        caddr_t         sc_cmd;
	struct rwlock	sc_lock;

	/* Doorbell and mailbox. */
	struct ppc_bus_space sc_mem_bus_space;
	bus_space_tag_t sc_memt;
	bus_space_handle_t sc_gpioh;
	bus_space_handle_t sc_buffh;

	struct smu_fan	sc_fans[SMU_MAXFANS];
	int		sc_num_fans;

	struct smu_sensor sc_sensors[SMU_MAXSENSORS];
	int		sc_num_sensors;

	struct ksensordev sc_sensordev;

	u_int16_t	sc_cpu_diode_scale;
	int16_t		sc_cpu_diode_offset;
	u_int16_t	sc_cpu_volt_scale;
	int16_t		sc_cpu_volt_offset;
	u_int16_t	sc_cpu_curr_scale;
	int16_t		sc_cpu_curr_offset;

	u_int16_t	sc_slots_pow_scale;
	int16_t		sc_slots_pow_offset;

	struct i2c_controller sc_i2c_tag;
};

struct cfattach smu_ca = {
        sizeof(struct smu_softc), smu_match, smu_attach
};

struct cfdriver smu_cd = {
        NULL, "smu", DV_DULL,
};

/* SMU command */
struct smu_cmd {
        u_int8_t        cmd;
        u_int8_t        len;
        u_int8_t        data[254];
};
#define SMU_CMDSZ       sizeof(struct smu_cmd)

/* RTC */
#define SMU_RTC			0x8e
#define SMU_RTC_SET_DATETIME	0x80
#define SMU_RTC_GET_DATETIME	0x81

/* ADC */
#define SMU_ADC			0xd8

/* Fan control */
#define SMU_FAN			0x4a

/* Data partitions */
#define SMU_PARTITION		0x3e
#define SMU_PARTITION_LATEST	0x01
#define SMU_PARTITION_BASE	0x02
#define SMU_PARTITION_UPDATE	0x03

/* I2C */
#define SMU_I2C			0x9a
#define SMU_I2C_SIMPLE		0x00
#define SMU_I2C_NORMAL		0x01
#define SMU_I2C_COMBINED	0x02

/* Power Management */
#define SMU_POWER		0xaa

/* Miscellaneous */
#define SMU_MISC		0xee
#define SMU_MISC_GET_DATA	0x02

int	smu_intr(void *);

int	smu_do_cmd(struct smu_softc *, int);
int	smu_time_read(time_t *);
int	smu_time_write(time_t);
int	smu_get_datablock(struct smu_softc *sc, u_int8_t, u_int8_t *, size_t);
int	smu_fan_set_rpm(struct smu_softc *, struct smu_fan *, u_int16_t);
int	smu_fan_refresh(struct smu_softc *, struct smu_fan *);
int	smu_sensor_refresh(struct smu_softc *, struct smu_sensor *);
void	smu_refresh_sensors(void *);

int	smu_i2c_acquire_bus(void *, int);
void	smu_i2c_release_bus(void *, int);
int	smu_i2c_exec(void *, i2c_op_t, i2c_addr_t,
	    const void *, size_t, void *buf, size_t, int);

void	smu_slew_voltage(u_int);

int
smu_match(struct device *parent, void *cf, void *aux)
{
        struct confargs *ca = aux;

        if (strcmp(ca->ca_name, "smu") == 0)
                return (1);
        return (0);
}

/* XXX */
extern struct powerpc_bus_dma_tag pci_bus_dma_tag;

void
smu_attach(struct device *parent, struct device *self, void *aux)
{
        struct smu_softc *sc = (struct smu_softc *)self;
	struct confargs *ca = aux;
	struct i2cbus_attach_args iba;
	struct smu_fan *fan;
	struct smu_sensor *sensor;
	int nseg, node;
	char type[32], loc[32];
	u_int32_t reg, intr, gpio, val;
#ifndef SMALL_KERNEL
	u_int8_t data[12];
#endif

	/* XXX */
	sc->sc_mem_bus_space.bus_base = 0x80000000;
	sc->sc_mem_bus_space.bus_size = 0;
	sc->sc_mem_bus_space.bus_io = 0;
	sc->sc_memt = &sc->sc_mem_bus_space;

	/* Map smu-doorbell gpio. */
	if (OF_getprop(ca->ca_node, "platform-doorbell-ack",
	        &node, sizeof node) <= 0 ||
	    OF_getprop(node, "reg", &reg, sizeof reg) <= 0 ||
	    OF_getprop(node, "interrupts", &intr, sizeof intr) <= 0 ||
	    OF_getprop(OF_parent(node), "reg", &gpio, sizeof gpio) <= 0) {
		printf(": cannot find smu-doorbell gpio\n");
		return;
	}
	if (bus_space_map(sc->sc_memt, gpio + reg, 1, 0, &sc->sc_gpioh)) {
		printf(": cannot map smu-doorbell gpio\n");
		return;
	}

	/* XXX Should get this from OF. */
	if (bus_space_map(sc->sc_memt, 0x860c, 4, 0, &sc->sc_buffh)) {
		printf(": cannot map smu-doorbell buffer\n");
		return;
	}

	/* XXX */
        sc->sc_dmat = &pci_bus_dma_tag;

	/* Allocate and map SMU command buffer.  */
	if (bus_dmamem_alloc(sc->sc_dmat, SMU_CMDSZ, 0, 0,
            sc->sc_cmdseg, 1, &nseg, BUS_DMA_NOWAIT)) {
                printf(": cannot allocate cmd buffer\n");
                return;
        }
        if (bus_dmamem_map(sc->sc_dmat, sc->sc_cmdseg, nseg,
            SMU_CMDSZ, &sc->sc_cmd, BUS_DMA_NOWAIT)) {
                printf(": cannot map cmd buffer\n");
                bus_dmamem_free(sc->sc_dmat, sc->sc_cmdseg, 1);
                return;
        }
        if (bus_dmamap_create(sc->sc_dmat, SMU_CMDSZ, 1, SMU_CMDSZ, 0,
            BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &sc->sc_cmdmap)) {
                printf(": cannot create cmd dmamap\n");
                bus_dmamem_unmap(sc->sc_dmat, sc->sc_cmd, SMU_CMDSZ);
                bus_dmamem_free(sc->sc_dmat, sc->sc_cmdseg, 1);
                return;
        }
        if (bus_dmamap_load(sc->sc_dmat, sc->sc_cmdmap, sc->sc_cmd,
            SMU_CMDSZ, NULL, BUS_DMA_NOWAIT)) {
                printf(": cannot load cmd dmamap\n");
                bus_dmamap_destroy(sc->sc_dmat, sc->sc_cmdmap);
                bus_dmamem_unmap(sc->sc_dmat, sc->sc_cmd, SMU_CMDSZ);
                bus_dmamem_free(sc->sc_dmat, sc->sc_cmdseg, nseg);
                return;
        }

	rw_init(&sc->sc_lock, sc->sc_dev.dv_xname);

	/* Establish smu-doorbell interrupt. */
	mac_intr_establish(parent, intr, IST_EDGE, IPL_BIO,
	    smu_intr, sc, sc->sc_dev.dv_xname);

	/* Initialize global variables that control RTC functionality. */
	time_read = smu_time_read;
	time_write = smu_time_write;

	/* Fans */
	node = OF_getnodebyname(ca->ca_node, "rpm-fans");
	if (node == 0)
		node = OF_getnodebyname(ca->ca_node, "fans");
	for (node = OF_child(node); node; node = OF_peer(node)) {
		if (OF_getprop(node, "reg", &reg, sizeof reg) <= 0 ||
		    OF_getprop(node, "device_type", type, sizeof type) <= 0)
			continue;

		if (strcmp(type, "fan-rpm-control") != 0) {
			printf(": unsupported fan type: %s\n", type);
			return;
		}

		if (sc->sc_num_fans >= SMU_MAXFANS) {
			printf(": too many fans\n");
			return;
		}

		fan = &sc->sc_fans[sc->sc_num_fans++];
		fan->sensor.type = SENSOR_FANRPM;
		fan->sensor.flags = SENSOR_FINVALID;
		fan->reg = reg;

		if (OF_getprop(node, "min-value", &val, sizeof val) <= 0)
			val = 0;
		fan->min_rpm = val;
		if (OF_getprop(node, "max-value", &val, sizeof val) <= 0)
			val = 0xffff;
		fan->max_rpm = val;
		if (OF_getprop(node, "unmanage-value", &val, sizeof val) <= 0)
			val = fan->max_rpm;
		fan->unmanaged_rpm = val;

		if (OF_getprop(node, "location", loc, sizeof loc) <= 0)
			strlcpy(loc, "Unknown", sizeof loc);
		strlcpy(fan->sensor.desc, loc, sizeof sensor->sensor.desc);

		/* Start running fans at their "unmanaged" speed. */
		smu_fan_set_rpm(sc, fan, fan->unmanaged_rpm);

#ifndef SMALL_KERNEL
		sensor_attach(&sc->sc_sensordev, &fan->sensor);
#endif
	}

	/*
	 * Bail out if we didn't find any fans.  If we don't set the
	 * fans to a safe speed, but tickle the SMU periodically by
	 * reading sensors, the fans will never spin up and the
	 * machine might overheat.
	 */
	if (sc->sc_num_fans == 0) {
		printf(": no fans\n");
		return;
	}

#ifndef SMALL_KERNEL
	/* Sensors */
	node = OF_getnodebyname(ca->ca_node, "sensors");
	for (node = OF_child(node); node; node = OF_peer(node)) {
		if (OF_getprop(node, "reg", &val, sizeof val) <= 0 ||
		    OF_getprop(node, "device_type", type, sizeof type) <= 0)
			continue;

		if (sc->sc_num_sensors >= SMU_MAXSENSORS) {
			printf(": too many sensors\n");
			return;
		}

		sensor = &sc->sc_sensors[sc->sc_num_sensors++];
		sensor->sensor.flags = SENSOR_FINVALID;
		sensor->reg = val;

		if (strcmp(type, "current-sensor") == 0) {
			sensor->sensor.type = SENSOR_AMPS;
		} else if (strcmp(type, "temp-sensor") == 0) {
			sensor->sensor.type = SENSOR_TEMP;
		} else if (strcmp(type, "voltage-sensor") == 0) {
			sensor->sensor.type = SENSOR_VOLTS_DC;
		} else if (strcmp(type, "power-sensor") == 0) {
			sensor->sensor.type = SENSOR_WATTS;
		} else {
			sensor->sensor.type = SENSOR_INTEGER;
		}

		if (OF_getprop(node, "location", loc, sizeof loc) <= 0)
			strlcpy(loc, "Unknown", sizeof loc);
		strlcpy(sensor->sensor.desc, loc, sizeof sensor->sensor.desc);

		sensor_attach(&sc->sc_sensordev, &sensor->sensor);
	}

	/* Register sensor device with sysctl */
	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));
	sensordev_install(&sc->sc_sensordev);

	/* CPU temperature diode calibration */
	smu_get_datablock(sc, 0x18, data, sizeof data);
	sc->sc_cpu_diode_scale = (data[4] << 8) + data[5];
	sc->sc_cpu_diode_offset = (data[6] << 8) + data[7];

	/* CPU power (voltage and current) calibration */
	smu_get_datablock(sc, 0x21, data, sizeof data);
	sc->sc_cpu_volt_scale = (data[4] << 8) + data[5];
	sc->sc_cpu_volt_offset = (data[6] << 8) + data[7];
	sc->sc_cpu_curr_scale = (data[8] << 8) + data[9];
	sc->sc_cpu_curr_offset = (data[10] << 8) + data[11];

	/* Slots power calibration */
	smu_get_datablock(sc, 0x78, data, sizeof data);
	sc->sc_slots_pow_scale = (data[4] << 8) + data[5];
	sc->sc_slots_pow_offset = (data[6] << 8) + data[7];

	sensor_task_register(sc, smu_refresh_sensors, 5);
#endif /* !SMALL_KERNEL */
	printf("\n");

	ppc64_slew_voltage = smu_slew_voltage;

	sc->sc_i2c_tag.ic_cookie = sc;
	sc->sc_i2c_tag.ic_acquire_bus = smu_i2c_acquire_bus;
	sc->sc_i2c_tag.ic_release_bus = smu_i2c_release_bus;
	sc->sc_i2c_tag.ic_exec = smu_i2c_exec;

	/*
	 * Early versions of the SMU have the i2c bus node directly
	 * below the "smu" node, while later models have an
	 * intermediate "smu-i2c-control" node.
	 */
	node = OF_getnodebyname(ca->ca_node, "smu-i2c-control");
	if (node)
		node = OF_child(node);
	else
		node = OF_getnodebyname(ca->ca_node, "i2c");

	bzero(&iba, sizeof iba);
	iba.iba_name = "iic";
	iba.iba_tag = &sc->sc_i2c_tag;
	iba.iba_bus_scan = maciic_scan;
	iba.iba_bus_scan_arg = &node;
	config_found(&sc->sc_dev, &iba, NULL);
}

int
smu_intr(void *arg)
{
	wakeup(arg);
	return 1;
}

int
smu_do_cmd(struct smu_softc *sc, int timo)
{
	struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
	u_int8_t gpio, ack = ~cmd->cmd;
	int error;

	/* Write to mailbox.  */
	bus_space_write_4(sc->sc_memt, sc->sc_buffh, 0,
	    sc->sc_cmdmap->dm_segs->ds_addr);

	/* Flush to RAM. */
	asm volatile ("dcbst 0,%0; sync" :: "r"(sc->sc_cmd): "memory");

	/* Ring doorbell.  */
	bus_space_write_1(sc->sc_memt, sc->sc_gpioh, 0, GPIO_DDR_OUTPUT);

	do {
		error = tsleep(sc, PWAIT, "smu", (timo * hz) / 1000);
		if (error)
			return (error);
		gpio = bus_space_read_1(sc->sc_memt, sc->sc_gpioh, 0);
	} while (!(gpio & (GPIO_DATA)));

	/* CPU might have brought back the cache line. */
	asm volatile ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");

	if (cmd->cmd != ack)
		return (EIO);
	return (0);
}

int
smu_time_read(time_t *secs)
{
	struct smu_softc *sc = smu_cd.cd_devs[0];
	struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
	struct clock_ymdhms dt;
	int error;

	rw_enter_write(&sc->sc_lock);

	cmd->cmd = SMU_RTC;
	cmd->len = 1;
	cmd->data[0] = SMU_RTC_GET_DATETIME;
	error = smu_do_cmd(sc, 800);
	if (error) {
		rw_exit_write(&sc->sc_lock);

		*secs = 0;
		return (error);
	}

	dt.dt_year = 2000 + FROMBCD(cmd->data[6]);
	dt.dt_mon = FROMBCD(cmd->data[5]);
	dt.dt_day = FROMBCD(cmd->data[4]);
	dt.dt_hour = FROMBCD(cmd->data[2]);
	dt.dt_min = FROMBCD(cmd->data[1]);
	dt.dt_sec = FROMBCD(cmd->data[0]);

	rw_exit_write(&sc->sc_lock);

	*secs = clock_ymdhms_to_secs(&dt);
	return (0);
}

int
smu_time_write(time_t secs)
{
	struct smu_softc *sc = smu_cd.cd_devs[0];
	struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
	struct clock_ymdhms dt;
	int error;

	clock_secs_to_ymdhms(secs, &dt);

	rw_enter_write(&sc->sc_lock);

	cmd->cmd = SMU_RTC;
	cmd->len = 8;
	cmd->data[0] = SMU_RTC_SET_DATETIME;
	cmd->data[1] = TOBCD(dt.dt_sec);
	cmd->data[2] = TOBCD(dt.dt_min);
	cmd->data[3] = TOBCD(dt.dt_hour);
	cmd->data[4] = TOBCD(dt.dt_wday);
	cmd->data[5] = TOBCD(dt.dt_day);
	cmd->data[6] = TOBCD(dt.dt_mon);
	cmd->data[7] = TOBCD(dt.dt_year - 2000);
	error = smu_do_cmd(sc, 800);

	rw_exit_write(&sc->sc_lock);

	return (error);
}

int
smu_get_datablock(struct smu_softc *sc, u_int8_t id, u_int8_t *buf, size_t len)
{
	struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
	u_int8_t addr[4];
	int error;

	cmd->cmd = SMU_PARTITION;
	cmd->len = 2;
	cmd->data[0] = SMU_PARTITION_LATEST;
	cmd->data[1] = id;
	error = smu_do_cmd(sc, 800);
	if (error)
		return (error);

	addr[0] = 0x00;
	addr[1] = 0x00;
	addr[2] = cmd->data[0];
	addr[3] = cmd->data[1];

	cmd->cmd = SMU_MISC;
	cmd->len = 7;
	cmd->data[0] = SMU_MISC_GET_DATA;
	cmd->data[1] = sizeof(u_int32_t);
	cmd->data[2] = addr[0];
	cmd->data[3] = addr[1];
	cmd->data[4] = addr[2];
	cmd->data[5] = addr[3];
	cmd->data[6] = len;
	error = smu_do_cmd(sc, 800);
	if (error)
		return (error);

	memcpy(buf, cmd->data, len);
	return (0);
}

int
smu_fan_set_rpm(struct smu_softc *sc, struct smu_fan *fan, u_int16_t rpm)
{
	struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;

	/*
	 * On the PowerMac8,2 this command expects the requested fan
	 * speed at a different location in the command block than on
	 * the PowerMac8,1.  We simply store the value at both
	 * locations.
	 */
	cmd->cmd = SMU_FAN;
	cmd->len = 14;
	cmd->data[0] = 0x00;	/* fan-rpm-control */
	cmd->data[1] = 0x01 << fan->reg;
	cmd->data[2] = cmd->data[2 + fan->reg * 2] = (rpm >> 8) & 0xff;
	cmd->data[3] = cmd->data[3 + fan->reg * 2] = (rpm & 0xff);
	return smu_do_cmd(sc, 800);
}

int
smu_fan_refresh(struct smu_softc *sc, struct smu_fan *fan)
{
	struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
	int error;

	cmd->cmd = SMU_FAN;
	cmd->len = 2;
	cmd->data[0] = 0x01;	/* fan-rpm-control */
	cmd->data[1] = 0x01 << fan->reg;
	error = smu_do_cmd(sc, 800);
	if (error) {
		fan->sensor.flags = SENSOR_FINVALID;
		return (error);
	}
	fan->sensor.value = (cmd->data[1] << 8) + cmd->data[2];
	fan->sensor.flags = 0;
	return (0);
}

int
smu_sensor_refresh(struct smu_softc *sc, struct smu_sensor *sensor)
{
	struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
	int64_t value;
	int error;

	cmd->cmd = SMU_ADC;
	cmd->len = 1;
	cmd->data[0] = sensor->reg;
	error = smu_do_cmd(sc, 800);
	if (error) {
		sensor->sensor.flags = SENSOR_FINVALID;
		return (error);
	}
	value = (cmd->data[0] << 8) + cmd->data[1];
	switch (sensor->sensor.type) {
	case SENSOR_TEMP:
		value *= sc->sc_cpu_diode_scale;
		value >>= 3;
		value += ((int64_t)sc->sc_cpu_diode_offset) << 9;
		value <<= 1;

		/* Convert from 16.16 fixed point degC into muK. */
		value *= 15625;
		value /= 1024;
		value += 273150000;
		break;

	case SENSOR_VOLTS_DC:
		value *= sc->sc_cpu_volt_scale;
		value += sc->sc_cpu_volt_offset;
		value <<= 4;

		/* Convert from 16.16 fixed point V into muV. */
		value *= 15625;
		value /= 1024;
		break;

	case SENSOR_AMPS:
		value *= sc->sc_cpu_curr_scale;
		value += sc->sc_cpu_curr_offset;
		value <<= 4;

		/* Convert from 16.16 fixed point A into muA. */
		value *= 15625;
		value /= 1024;
		break;

	case SENSOR_WATTS:
		value *= sc->sc_slots_pow_scale;
		value += sc->sc_slots_pow_offset;
		value <<= 4;

		/* Convert from 16.16 fixed point W into muW. */
		value *= 15625;
		value /= 1024;
		break;

	default:
		break;
	}
	sensor->sensor.value = value;
	sensor->sensor.flags = 0;
	return (0);
}

void
smu_refresh_sensors(void *arg)
{
	struct smu_softc *sc = arg;
	int i;

	rw_enter_write(&sc->sc_lock);
	for (i = 0; i < sc->sc_num_sensors; i++)
		smu_sensor_refresh(sc, &sc->sc_sensors[i]);
	for (i = 0; i < sc->sc_num_fans; i++)
		smu_fan_refresh(sc, &sc->sc_fans[i]);
	rw_exit_write(&sc->sc_lock);
}

int
smu_i2c_acquire_bus(void *cookie, int flags)
{
	struct smu_softc *sc = cookie;

	if (flags & I2C_F_POLL)
		return (0);

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

void
smu_i2c_release_bus(void *cookie, int flags)
{
	struct smu_softc *sc = cookie;

        if (flags & I2C_F_POLL)
                return;

	rw_exit(&sc->sc_lock);
}

int
smu_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
    const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
{
	struct smu_softc *sc = cookie;
	struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
	u_int8_t smu_op = SMU_I2C_NORMAL;
	int error, retries = 10;

	if (!I2C_OP_STOP_P(op) || cmdlen > 3 || len > 5)
		return (EINVAL);

	if(cmdlen == 0)
		smu_op = SMU_I2C_SIMPLE;
	else if (I2C_OP_READ_P(op))
		smu_op = SMU_I2C_COMBINED;

	cmd->cmd = SMU_I2C;
	cmd->len = 9 + len;
	cmd->data[0] = 0xb;
	cmd->data[1] = smu_op;
	cmd->data[2] = addr << 1;
	cmd->data[3] = cmdlen;
	memcpy (&cmd->data[4], cmdbuf, cmdlen);
	cmd->data[7] = addr << 1 | I2C_OP_READ_P(op);
	cmd->data[8] = len;
	memcpy(&cmd->data[9], buf, len);

	error = smu_do_cmd(sc, 250);
	if (error)
		return error;

	while (retries--) {
		cmd->cmd = SMU_I2C;
		cmd->len = 1;
		cmd->data[0] = 0;
		memset(&cmd->data[1], 0xff, len);

		error = smu_do_cmd(sc, 250);
		if (error)
			return error;

		if ((cmd->data[0] & 0x80) == 0)
			break;
		if (cmd->data[0] == 0xfd)
			break;

		DELAY(15 * 1000);
	}

	if (cmd->data[0] & 0x80)
		return (EIO);

	if (I2C_OP_READ_P(op))
		memcpy(buf, &cmd->data[1], len);
	return (0);
}

void
smu_slew_voltage(u_int freq_scale)
{
	struct smu_softc *sc = smu_cd.cd_devs[0];
	struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;

	rw_enter_write(&sc->sc_lock);

	cmd->cmd = SMU_POWER;
	cmd->len = 8;
	memcpy(cmd->data, "VSLEW", 5);
	cmd->data[5] = 0xff;
	cmd->data[6] = 1;
	cmd->data[7] = freq_scale;

	smu_do_cmd(sc, 250);

	rw_exit_write(&sc->sc_lock);
}


code:
/*	$OpenBSD: snapper.c,v 1.35 2009/10/26 20:17:27 deraadt Exp $	*/
/*	$NetBSD: snapper.c,v 1.1 2003/12/27 02:19:34 grant Exp $	*/

/*-
 * Copyright (c) 2002 Tsubai Masanari.  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.
 */

/*
 * Datasheet is available from
 * http://focus.ti.com/docs/prod/folders/print/tas3004.html
 */

#include <sys/param.h>
#include <sys/audioio.h>
#include <sys/device.h>
#include <sys/systm.h>

#include <dev/audio_if.h>
#include <dev/ofw/openfirm.h>
#include <macppc/dev/dbdma.h>

#include <machine/autoconf.h>

#include <macppc/dev/i2svar.h>

#ifdef SNAPPER_DEBUG
# define DPRINTF printf
#else
# define DPRINTF while (0) printf
#endif

/* XXX */
#define snapper_softc i2s_softc

/* XXX */
int kiic_write(struct device *, int, int, const void *, int);
int kiic_writereg(struct device *, int, u_int);

void snapper_init(struct snapper_softc *);
int snapper_getdev(void *, struct audio_device *);
int snapper_match(struct device *, void *, void *);
void snapper_attach(struct device *, struct device *, void *);
void snapper_defer(struct device *);
void snapper_set_volume(struct snapper_softc *, int, int);
void snapper_set_bass(struct snapper_softc *, int);
void snapper_set_treble(struct snapper_softc *, int);
void snapper_set_input(struct snapper_softc *, int);
void snapper_get_default_params(void *, int, struct audio_params *);

int tas3004_write(struct snapper_softc *, u_int, const void *);
int tas3004_init(struct snapper_softc *);

struct cfattach snapper_ca = {
	sizeof(struct snapper_softc), snapper_match, snapper_attach
};
struct cfdriver snapper_cd = {
	NULL, "snapper", DV_DULL
};

struct audio_hw_if snapper_hw_if = {
	i2s_open,
	i2s_close,
	NULL,
	i2s_query_encoding,
	i2s_set_params,
	i2s_round_blocksize,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	i2s_halt_output,
	i2s_halt_input,
	NULL,
	snapper_getdev,
	NULL,
	i2s_set_port,
	i2s_get_port,
	i2s_query_devinfo,
	i2s_allocm,		/* allocm */
	NULL,
	i2s_round_buffersize,
	i2s_mappage,
	i2s_get_props,
	i2s_trigger_output,
	i2s_trigger_input,
	snapper_get_default_params
};

struct audio_device snapper_device = {
	"SNAPPER",
	"",
	"snapper"
};

const uint8_t snapper_trebletab[] = {
	0x96,	/* -18dB */
	0x94,	/* -17dB */
	0x92,	/* -16dB */
	0x90,	/* -15dB */
	0x8e,	/* -14dB */
	0x8c,	/* -13dB */
	0x8a,	/* -12dB */
	0x88,	/* -11dB */
	0x86,	/* -10dB */
	0x84,	/* -9dB */
	0x82,	/* -8dB */
	0x80,	/* -7dB */
	0x7e,	/* -6dB */
	0x7c,	/* -5dB */
	0x7a,	/* -4dB */
	0x78,	/* -3dB */
	0x76,	/* -2dB */
	0x74,	/* -1dB */
	0x72,	/* 0dB */
	0x70,	/* 1dB */
	0x6d,	/* 2dB */
	0x6b,	/* 3dB */
	0x68,	/* 4dB */
	0x65,	/* 5dB */
	0x62,	/* 6dB */
	0x5d,	/* 7dB */
	0x59,	/* 8dB */
	0x53,	/* 9dB */
	0x4d,	/* 10dB */
	0x47,	/* 11dB */
	0x3f,	/* 12dB */
	0x36,	/* 13dB */
	0x2c,	/* 14dB */
	0x20,	/* 15dB */
	0x13,	/* 16dB */
	0x04,	/* 17dB */
	0x01,	/* 18dB */
};

const uint8_t snapper_basstab[] = {
	0x96,	/* -18dB */
	0x94,	/* -17dB */
	0x92,	/* -16dB */
	0x90,	/* -15dB */
	0x8e,	/* -14dB */
	0x8c,	/* -13dB */
	0x8a,	/* -12dB */
	0x88,	/* -11dB */
	0x86,	/* -10dB */
	0x84,	/* -9dB */
	0x82,	/* -8dB */
	0x80,	/* -7dB */
	0x7e,	/* -6dB */
	0x7c,	/* -5dB */
	0x7a,	/* -4dB */
	0x78,	/* -3dB */
	0x76,	/* -2dB */
	0x74,	/* -1dB */
	0x72,	/* 0dB */
	0x6f,	/* 1dB */
	0x6d,	/* 2dB */
	0x6a,	/* 3dB */
	0x67,	/* 4dB */
	0x65,	/* 5dB */
	0x62,	/* 6dB */
	0x5f,	/* 7dB */
	0x5b,	/* 8dB */
	0x55,	/* 9dB */
	0x4f,	/* 10dB */
	0x49,	/* 11dB */
	0x43,	/* 12dB */
	0x3b,	/* 13dB */
	0x33,	/* 14dB */
	0x29,	/* 15dB */
	0x1e,	/* 16dB */
	0x11,	/* 17dB */
	0x01,	/* 18dB */
};

struct {
	int high, mid, low;
} snapper_volumetab[] = {
	{ 0x07, 0xF1, 0x7B }, /* 18.0 */
	{ 0x07, 0x7F, 0xBB }, /* 17.5 */
	{ 0x07, 0x14, 0x57 }, /* 17.0 */
	{ 0x06, 0xAE, 0xF6 }, /* 16.5 */
	{ 0x06, 0x4F, 0x40 }, /* 16.0 */
	{ 0x05, 0xF4, 0xE5 }, /* 15.5 */
	{ 0x05, 0x9F, 0x98 }, /* 15.0 */
	{ 0x05, 0x4F, 0x10 }, /* 14.5 */
	{ 0x05, 0x03, 0x0A }, /* 14.0 */
	{ 0x04, 0xBB, 0x44 }, /* 13.5 */
	{ 0x04, 0x77, 0x83 }, /* 13.0 */
	{ 0x04, 0x37, 0x8B }, /* 12.5 */
	{ 0x03, 0xFB, 0x28 }, /* 12.0 */
	{ 0x03, 0xC2, 0x25 }, /* 11.5 */
	{ 0x03, 0x8C, 0x53 }, /* 11.0 */
	{ 0x03, 0x59, 0x83 }, /* 10.5 */
	{ 0x03, 0x29, 0x8B }, /* 10.0 */
	{ 0x02, 0xFC, 0x42 }, /* 9.5 */
	{ 0x02, 0xD1, 0x82 }, /* 9.0 */
	{ 0x02, 0xA9, 0x25 }, /* 8.5 */
	{ 0x02, 0x83, 0x0B }, /* 8.0 */
	{ 0x02, 0x5F, 0x12 }, /* 7.5 */
	{ 0x02, 0x3D, 0x1D }, /* 7.0 */
	{ 0x02, 0x1D, 0x0E }, /* 6.5 */
	{ 0x01, 0xFE, 0xCA }, /* 6.0 */
	{ 0x01, 0xE2, 0x37 }, /* 5.5 */
	{ 0x01, 0xC7, 0x3D }, /* 5.0 */
	{ 0x01, 0xAD, 0xC6 }, /* 4.5 */
	{ 0x01, 0x95, 0xBC }, /* 4.0 */
	{ 0x01, 0x7F, 0x09 }, /* 3.5 */
	{ 0x01, 0x69, 0x9C }, /* 3.0 */
	{ 0x01, 0x55, 0x62 }, /* 2.5 */
	{ 0x01, 0x42, 0x49 }, /* 2.0 */
	{ 0x01, 0x30, 0x42 }, /* 1.5 */
	{ 0x01, 0x1F, 0x3D }, /* 1.0 */
	{ 0x01, 0x0F, 0x2B }, /* 0.5 */
	{ 0x01, 0x00, 0x00 }, /* 0.0 */
	{ 0x00, 0xF1, 0xAE }, /* -0.5 */
	{ 0x00, 0xE4, 0x29 }, /* -1.0 */
	{ 0x00, 0xD7, 0x66 }, /* -1.5 */
	{ 0x00, 0xCB, 0x59 }, /* -2.0 */
	{ 0x00, 0xBF, 0xF9 }, /* -2.5 */
	{ 0x00, 0xB5, 0x3C }, /* -3.0 */
	{ 0x00, 0xAB, 0x19 }, /* -3.5 */
	{ 0x00, 0xA1, 0x86 }, /* -4.0 */
	{ 0x00, 0x98, 0x7D }, /* -4.5 */
	{ 0x00, 0x8F, 0xF6 }, /* -5.0 */
	{ 0x00, 0x87, 0xE8 }, /* -5.5 */
	{ 0x00, 0x80, 0x4E }, /* -6.0 */
	{ 0x00, 0x79, 0x20 }, /* -6.5 */
	{ 0x00, 0x72, 0x5A }, /* -7.0 */
	{ 0x00, 0x6B, 0xF4 }, /* -7.5 */
	{ 0x00, 0x65, 0xEA }, /* -8.0 */
	{ 0x00, 0x60, 0x37 }, /* -8.5 */
	{ 0x00, 0x5A, 0xD5 }, /* -9.0 */
	{ 0x00, 0x55, 0xC0 }, /* -9.5 */
	{ 0x00, 0x50, 0xF4 }, /* -10.0 */
	{ 0x00, 0x4C, 0x6D }, /* -10.5 */
	{ 0x00, 0x48, 0x27 }, /* -11.0 */
	{ 0x00, 0x44, 0x1D }, /* -11.5 */
	{ 0x00, 0x40, 0x4E }, /* -12.0 */
	{ 0x00, 0x3C, 0xB5 }, /* -12.5 */
	{ 0x00, 0x39, 0x50 }, /* -13.0 */
	{ 0x00, 0x36, 0x1B }, /* -13.5 */
	{ 0x00, 0x33, 0x14 }, /* -14.0 */
	{ 0x00, 0x30, 0x39 }, /* -14.5 */
	{ 0x00, 0x2D, 0x86 }, /* -15.0 */
	{ 0x00, 0x2A, 0xFA }, /* -15.5 */
	{ 0x00, 0x28, 0x93 }, /* -16.0 */
	{ 0x00, 0x26, 0x4E }, /* -16.5 */
	{ 0x00, 0x24, 0x29 }, /* -17.0 */
	{ 0x00, 0x22, 0x23 }, /* -17.5 */
	{ 0x00, 0x20, 0x3A }, /* -18.0 */
	{ 0x00, 0x1E, 0x6D }, /* -18.5 */
	{ 0x00, 0x1C, 0xB9 }, /* -19.0 */
	{ 0x00, 0x1B, 0x1E }, /* -19.5 */
	{ 0x00, 0x19, 0x9A }, /* -20.0 */
	{ 0x00, 0x18, 0x2B }, /* -20.5 */
	{ 0x00, 0x16, 0xD1 }, /* -21.0 */
	{ 0x00, 0x15, 0x8A }, /* -21.5 */
	{ 0x00, 0x14, 0x56 }, /* -22.0 */
	{ 0x00, 0x13, 0x33 }, /* -22.5 */
	{ 0x00, 0x12, 0x20 }, /* -23.0 */
	{ 0x00, 0x11, 0x1C }, /* -23.5 */
	{ 0x00, 0x10, 0x27 }, /* -24.0 */
	{ 0x00, 0x0F, 0x40 }, /* -24.5 */
	{ 0x00, 0x0E, 0x65 }, /* -25.0 */
	{ 0x00, 0x0D, 0x97 }, /* -25.5 */
	{ 0x00, 0x0C, 0xD5 }, /* -26.0 */
	{ 0x00, 0x0C, 0x1D }, /* -26.5 */
	{ 0x00, 0x0B, 0x6F }, /* -27.0 */
	{ 0x00, 0x0A, 0xCC }, /* -27.5 */
	{ 0x00, 0x0A, 0x31 }, /* -28.0 */
	{ 0x00, 0x09, 0x9F }, /* -28.5 */
	{ 0x00, 0x09, 0x15 }, /* -29.0 */
	{ 0x00, 0x08, 0x93 }, /* -29.5 */
	{ 0x00, 0x08, 0x18 }, /* -30.0 */
	{ 0x00, 0x07, 0xA5 }, /* -30.5 */
	{ 0x00, 0x07, 0x37 }, /* -31.0 */
	{ 0x00, 0x06, 0xD0 }, /* -31.5 */
	{ 0x00, 0x06, 0x6E }, /* -32.0 */
	{ 0x00, 0x06, 0x12 }, /* -32.5 */
	{ 0x00, 0x05, 0xBB }, /* -33.0 */
	{ 0x00, 0x05, 0x69 }, /* -33.5 */
	{ 0x00, 0x05, 0x1C }, /* -34.0 */
	{ 0x00, 0x04, 0xD2 }, /* -34.5 */
	{ 0x00, 0x04, 0x8D }, /* -35.0 */
	{ 0x00, 0x04, 0x4C }, /* -35.5 */
	{ 0x00, 0x04, 0x0F }, /* -36.0 */
	{ 0x00, 0x03, 0xD5 }, /* -36.5 */
	{ 0x00, 0x03, 0x9E }, /* -37.0 */
	{ 0x00, 0x03, 0x6A }, /* -37.5 */
	{ 0x00, 0x03, 0x39 }, /* -38.0 */
	{ 0x00, 0x03, 0x0B }, /* -38.5 */
	{ 0x00, 0x02, 0xDF }, /* -39.0 */
	{ 0x00, 0x02, 0xB6 }, /* -39.5 */
	{ 0x00, 0x02, 0x8F }, /* -40.0 */
	{ 0x00, 0x02, 0x6B }, /* -40.5 */
	{ 0x00, 0x02, 0x48 }, /* -41.0 */
	{ 0x00, 0x02, 0x27 }, /* -41.5 */
	{ 0x00, 0x02, 0x09 }, /* -42.0 */
	{ 0x00, 0x01, 0xEB }, /* -42.5 */
	{ 0x00, 0x01, 0xD0 }, /* -43.0 */
	{ 0x00, 0x01, 0xB6 }, /* -43.5 */
	{ 0x00, 0x01, 0x9E }, /* -44.0 */
	{ 0x00, 0x01, 0x86 }, /* -44.5 */
	{ 0x00, 0x01, 0x71 }, /* -45.0 */
	{ 0x00, 0x01, 0x5C }, /* -45.5 */
	{ 0x00, 0x01, 0x48 }, /* -46.0 */
	{ 0x00, 0x01, 0x36 }, /* -46.5 */
	{ 0x00, 0x01, 0x25 }, /* -47.0 */
	{ 0x00, 0x01, 0x14 }, /* -47.5 */
	{ 0x00, 0x01, 0x05 }, /* -48.0 */
	{ 0x00, 0x00, 0xF6 }, /* -48.5 */
	{ 0x00, 0x00, 0xE9 }, /* -49.0 */
	{ 0x00, 0x00, 0xDC }, /* -49.5 */
	{ 0x00, 0x00, 0xCF }, /* -50.0 */
	{ 0x00, 0x00, 0xC4 }, /* -50.5 */
	{ 0x00, 0x00, 0xB9 }, /* -51.0 */
	{ 0x00, 0x00, 0xAE }, /* -51.5 */
	{ 0x00, 0x00, 0xA5 }, /* -52.0 */
	{ 0x00, 0x00, 0x9B }, /* -52.5 */
	{ 0x00, 0x00, 0x93 }, /* -53.0 */
	{ 0x00, 0x00, 0x8B }, /* -53.5 */
	{ 0x00, 0x00, 0x83 }, /* -54.0 */
	{ 0x00, 0x00, 0x7B }, /* -54.5 */
	{ 0x00, 0x00, 0x75 }, /* -55.0 */
	{ 0x00, 0x00, 0x6E }, /* -55.5 */
	{ 0x00, 0x00, 0x68 }, /* -56.0 */
	{ 0x00, 0x00, 0x62 }, /* -56.5 */
	{ 0x00, 0x00, 0x0 } /* Mute? */

};

/* TAS3004 registers */
#define DEQ_MCR1	0x01	/* Main control register 1 (1byte) */
#define DEQ_DRC		0x02	/* Dynamic range compression (6bytes?) */
#define DEQ_VOLUME	0x04	/* Volume (6bytes) */
#define DEQ_TREBLE	0x05	/* Treble control (1byte) */
#define DEQ_BASS	0x06	/* Bass control (1byte) */
#define DEQ_MIXER_L	0x07	/* Mixer left gain (9bytes) */
#define DEQ_MIXER_R	0x08	/* Mixer right gain (9bytes) */
#define DEQ_LB0		0x0a	/* Left biquad 0 (15bytes) */
#define DEQ_LB1		0x0b	/* Left biquad 1 (15bytes) */
#define DEQ_LB2		0x0c	/* Left biquad 2 (15bytes) */
#define DEQ_LB3		0x0d	/* Left biquad 3 (15bytes) */
#define DEQ_LB4		0x0e	/* Left biquad 4 (15bytes) */
#define DEQ_LB5		0x0f	/* Left biquad 5 (15bytes) */
#define DEQ_LB6		0x10	/* Left biquad 6 (15bytes) */
#define DEQ_RB0		0x13	/* Right biquad 0 (15bytes) */
#define DEQ_RB1		0x14	/* Right biquad 1 (15bytes) */
#define DEQ_RB2		0x15	/* Right biquad 2 (15bytes) */
#define DEQ_RB3		0x16	/* Right biquad 3 (15bytes) */
#define DEQ_RB4		0x17	/* Right biquad 4 (15bytes) */
#define DEQ_RB5		0x18	/* Right biquad 5 (15bytes) */
#define DEQ_RB6		0x19	/* Right biquad 6 (15bytes) */
#define DEQ_LLB		0x21	/* Left loudness biquad (15bytes) */
#define DEQ_RLB		0x22	/* Right loudness biquad (15bytes) */
#define DEQ_LLB_GAIN	0x23	/* Left loudness biquad gain (3bytes) */
#define DEQ_RLB_GAIN	0x24	/* Right loudness biquad gain (3bytes) */
#define DEQ_ACR		0x40	/* Analog control register (1byte) */
#define DEQ_MCR2	0x43	/* Main control register 2 (1byte) */

#define DEQ_MCR1_FL	0x80	/* Fast load */
#define DEQ_MCR1_SC	0x40	/* SCLK frequency */
#define  DEQ_MCR1_SC_32	0x00	/*  32fs */
#define  DEQ_MCR1_SC_64	0x40	/*  64fs */
#define DEQ_MCR1_SM	0x30	/* Output serial port mode */
#define  DEQ_MCR1_SM_L	0x00	/*  Left justified */
#define  DEQ_MCR1_SM_R	0x10	/*  Right justified */
#define  DEQ_MCR1_SM_I2S 0x20	/*  I2S */
#define DEQ_MCR1_W	0x03	/* Serial port word length */
#define  DEQ_MCR1_W_16	0x00	/*  16 bit */
#define  DEQ_MCR1_W_18	0x01	/*  18 bit */
#define  DEQ_MCR1_W_20	0x02	/*  20 bit */

#define DEQ_MCR2_DL	0x80	/* Download */
#define DEQ_MCR2_AP	0x02	/* All pass mode */

#define DEQ_ACR_ADM	0x80	/* ADC output mode */
#define DEQ_ACR_LRB	0x40	/* Select B input */
#define DEQ_ACR_DM	0x0c	/* De-emphasis control */
#define  DEQ_ACR_DM_OFF	0x00	/*  off */
#define  DEQ_ACR_DM_48	0x04	/*  fs = 48kHz */
#define  DEQ_ACR_DM_44	0x08	/*  fs = 44.1kHz */
#define DEQ_ACR_INP	0x02	/* Analog input select */
#define  DEQ_ACR_INP_A	0x00	/*  A */
#define  DEQ_ACR_INP_B	0x02	/*  B */
#define DEQ_ACR_APD	0x01	/* Analog power down */

struct tas3004_reg {
	u_char MCR1[1];
	u_char DRC[6];
	u_char VOLUME[6];
	u_char TREBLE[1];
	u_char BASS[1];
	u_char MIXER_L[9];
	u_char MIXER_R[9];
	u_char LB0[15];
	u_char LB1[15];
	u_char LB2[15];
	u_char LB3[15];
	u_char LB4[15];
	u_char LB5[15];
	u_char LB6[15];
	u_char RB0[15];
	u_char RB1[15];
	u_char RB2[15];
	u_char RB3[15];
	u_char RB4[15];
	u_char RB5[15];
	u_char RB6[15];
	u_char LLB[15];
	u_char RLB[15];
	u_char LLB_GAIN[3];
	u_char RLB_GAIN[3];
	u_char ACR[1];
	u_char MCR2[1];
};

int
snapper_match(struct device *parent, void *match, void *aux)
{
	struct confargs *ca = aux;
	int soundbus, soundchip, soundcodec;
	char compat[32];

	if (strcmp(ca->ca_name, "i2s") != 0)
		return (0);

	if ((soundbus = OF_child(ca->ca_node)) == 0 ||
	    (soundchip = OF_child(soundbus)) == 0)
		return (0);

	bzero(compat, sizeof compat);
	OF_getprop(soundchip, "compatible", compat, sizeof compat);

	if (strcmp(compat, "snapper") == 0)
		return (1);

	if (OF_getprop(soundchip, "platform-tas-codec-ref",
	    &soundcodec, sizeof soundcodec) == sizeof soundcodec)
		return (1);

	return (0);
}

void
snapper_attach(struct device *parent, struct device *self, void *aux)
{
	struct snapper_softc *sc = (struct snapper_softc *)self;

	sc->sc_setvolume = snapper_set_volume;
	sc->sc_setbass = snapper_set_bass;
	sc->sc_settreble = snapper_set_treble;
	sc->sc_setinput = snapper_set_input;

	i2s_attach(parent, sc, aux);
	config_defer(self, snapper_defer);
}

void
snapper_defer(struct device *dev)
{
	struct snapper_softc *sc = (struct snapper_softc *)dev;
	struct device *dv;

	TAILQ_FOREACH(dv, &alldevs, dv_list)
		if (strcmp(dv->dv_cfdata->cf_driver->cd_name, "kiic") == 0 &&
		    strcmp(dv->dv_parent->dv_cfdata->cf_driver->cd_name, "macobio") == 0)
			sc->sc_i2c = dv;
	if (sc->sc_i2c == NULL) {
		printf("%s: unable to find i2c\n", sc->sc_dev.dv_xname);
		return;
	}

	/* XXX If i2c has failed to attach, what should we do? */

	audio_attach_mi(&snapper_hw_if, sc, &sc->sc_dev);

	/* kiic_setmode(sc->sc_i2c, I2C_STDSUBMODE); */
	snapper_init(sc);
}

void
snapper_set_volume(struct snapper_softc *sc, int left, int right)
{
	u_char vol[6];
	int nentries = sizeof(snapper_volumetab) / sizeof(snapper_volumetab[0]);
	int l, r;

	sc->sc_vol_l = left;
	sc->sc_vol_r = right;

	l = nentries - (left * nentries / 256);
	r = nentries - (right * nentries / 256);

	DPRINTF(" left %d vol %d %d, right %d vol %d %d\n",
		left, l, nentries,
		right, r, nentries);
	if (l >= nentries)
		l = nentries-1;
	if (r >= nentries)
		r = nentries-1;

	vol[0] = snapper_volumetab[l].high;
	vol[1] = snapper_volumetab[l].mid;
	vol[2] = snapper_volumetab[l].low;
	vol[3] = snapper_volumetab[r].high;
	vol[4] = snapper_volumetab[r].mid;
	vol[5] = snapper_volumetab[r].low;

	tas3004_write(sc, DEQ_VOLUME, vol);
}

void
snapper_set_treble(struct snapper_softc *sc, int value)
{
	uint8_t reg;

	if ((value >= 0) && (value <= 255) && (value != sc->sc_treble)) {
		reg = snapper_trebletab[(value >> 3) + 2];
		if (tas3004_write(sc, DEQ_TREBLE, &reg) < 0)
			return;
		sc->sc_treble = value;
	}
}

void
snapper_set_bass(struct snapper_softc *sc, int value)
{
	uint8_t reg;

	if ((value >= 0) && (value <= 255) && (value != sc->sc_bass)) {
		reg = snapper_basstab[(value >> 3) + 2];
		if (tas3004_write(sc, DEQ_BASS, &reg) < 0)
			return;
		sc->sc_bass = value;
	}
}

void
snapper_set_input(struct snapper_softc *sc, int mask)
{
	uint8_t val = 0;

	switch (mask) {
	case    1 << 0: /* microphone */
		val = DEQ_ACR_ADM | DEQ_ACR_LRB | DEQ_ACR_INP_B;
		break;
	case    1 << 1: /* line in */
		val = 0;
		break;
	}
	tas3004_write(sc, DEQ_ACR, &val);
}

const struct tas3004_reg tas3004_initdata = {
	{ DEQ_MCR1_SC_64 | DEQ_MCR1_SM_I2S | DEQ_MCR1_W_20 },	/* MCR1 */
	{ 1, 0, 0, 0, 0, 0 },					/* DRC */
	{ 0x00, 0xd7, 0x66, 0x00, 0xd7, 0x66 },			/* VOLUME */
	{ 0x72 },						/* TREBLE */
	{ 0x72 },						/* BASS */
	{ 0x10, 0x00, 0x00, 0, 0, 0, 0, 0, 0 },			/* MIXER_L */
	{ 0x10, 0x00, 0x00, 0, 0, 0, 0, 0, 0 },			/* MIXER_R */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0, 0, 0 },						/* LLB_GAIN */
	{ 0, 0, 0 },						/* RLB_GAIN */
	{ DEQ_ACR_ADM | DEQ_ACR_LRB | DEQ_ACR_INP_B },		/* ACR */
	{ 0 }							/* MCR2 */
};

const char tas3004_regsize[] = {
	0,					/* 0x00 */
	sizeof tas3004_initdata.MCR1,		/* 0x01 */
	sizeof tas3004_initdata.DRC,		/* 0x02 */
	0,					/* 0x03 */
	sizeof tas3004_initdata.VOLUME,		/* 0x04 */
	sizeof tas3004_initdata.TREBLE,		/* 0x05 */
	sizeof tas3004_initdata.BASS,		/* 0x06 */
	sizeof tas3004_initdata.MIXER_L,	/* 0x07 */
	sizeof tas3004_initdata.MIXER_R,	/* 0x08 */
	0,					/* 0x09 */
	sizeof tas3004_initdata.LB0,		/* 0x0a */
	sizeof tas3004_initdata.LB1,		/* 0x0b */
	sizeof tas3004_initdata.LB2,		/* 0x0c */
	sizeof tas3004_initdata.LB3,		/* 0x0d */
	sizeof tas3004_initdata.LB4,		/* 0x0e */
	sizeof tas3004_initdata.LB5,		/* 0x0f */
	sizeof tas3004_initdata.LB6,		/* 0x10 */
	0,					/* 0x11 */
	0,					/* 0x12 */
	sizeof tas3004_initdata.RB0,		/* 0x13 */
	sizeof tas3004_initdata.RB1,		/* 0x14 */
	sizeof tas3004_initdata.RB2,		/* 0x15 */
	sizeof tas3004_initdata.RB3,		/* 0x16 */
	sizeof tas3004_initdata.RB4,		/* 0x17 */
	sizeof tas3004_initdata.RB5,		/* 0x18 */
	sizeof tas3004_initdata.RB6,		/* 0x19 */
	0,0,0,0, 0,0,
	0,					/* 0x20 */
	sizeof tas3004_initdata.LLB,		/* 0x21 */
	sizeof tas3004_initdata.RLB,		/* 0x22 */
	sizeof tas3004_initdata.LLB_GAIN,	/* 0x23 */
	sizeof tas3004_initdata.RLB_GAIN,	/* 0x24 */
	0,0,0,0, 0,0,0,0, 0,0,0,
	0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
	sizeof tas3004_initdata.ACR,		/* 0x40 */
	0,					/* 0x41 */
	0,					/* 0x42 */
	sizeof tas3004_initdata.MCR2		/* 0x43 */
};

#define DEQaddr 0x6a

int
tas3004_write(struct snapper_softc *sc, u_int reg, const void *data)
{
	int size;

	KASSERT(reg < sizeof tas3004_regsize);
	size = tas3004_regsize[reg];
	KASSERT(size > 0);

	if (kiic_write(sc->sc_i2c, DEQaddr, reg, data, size))
		return (-1);

	return (0);
}

#define DEQ_WRITE(sc, reg, addr) \
	if (tas3004_write(sc, reg, addr)) goto err

int
tas3004_init(struct snapper_softc *sc)
{
	deq_reset(sc);

	DEQ_WRITE(sc, DEQ_LB0, tas3004_initdata.LB0);
	DEQ_WRITE(sc, DEQ_LB1, tas3004_initdata.LB1);
	DEQ_WRITE(sc, DEQ_LB2, tas3004_initdata.LB2);
	DEQ_WRITE(sc, DEQ_LB3, tas3004_initdata.LB3);
	DEQ_WRITE(sc, DEQ_LB4, tas3004_initdata.LB4);
	DEQ_WRITE(sc, DEQ_LB5, tas3004_initdata.LB5);
	DEQ_WRITE(sc, DEQ_LB6, tas3004_initdata.LB6);
	DEQ_WRITE(sc, DEQ_RB0, tas3004_initdata.RB0);
	DEQ_WRITE(sc, DEQ_RB1, tas3004_initdata.RB1);
	DEQ_WRITE(sc, DEQ_RB1, tas3004_initdata.RB1);
	DEQ_WRITE(sc, DEQ_RB2, tas3004_initdata.RB2);
	DEQ_WRITE(sc, DEQ_RB3, tas3004_initdata.RB3);
	DEQ_WRITE(sc, DEQ_RB4, tas3004_initdata.RB4);
	DEQ_WRITE(sc, DEQ_RB5, tas3004_initdata.RB5);
	DEQ_WRITE(sc, DEQ_RB6, tas3004_initdata.RB6);
	DEQ_WRITE(sc, DEQ_MCR1, tas3004_initdata.MCR1);
	DEQ_WRITE(sc, DEQ_MCR2, tas3004_initdata.MCR2);
	DEQ_WRITE(sc, DEQ_DRC, tas3004_initdata.DRC);
	DEQ_WRITE(sc, DEQ_VOLUME, tas3004_initdata.VOLUME);
	DEQ_WRITE(sc, DEQ_TREBLE, tas3004_initdata.TREBLE);
	DEQ_WRITE(sc, DEQ_BASS, tas3004_initdata.BASS);
	DEQ_WRITE(sc, DEQ_MIXER_L, tas3004_initdata.MIXER_L);
	DEQ_WRITE(sc, DEQ_MIXER_R, tas3004_initdata.MIXER_R);
	DEQ_WRITE(sc, DEQ_LLB, tas3004_initdata.LLB);
	DEQ_WRITE(sc, DEQ_RLB, tas3004_initdata.RLB);
	DEQ_WRITE(sc, DEQ_LLB_GAIN, tas3004_initdata.LLB_GAIN);
	DEQ_WRITE(sc, DEQ_RLB_GAIN, tas3004_initdata.RLB_GAIN);
	DEQ_WRITE(sc, DEQ_ACR, tas3004_initdata.ACR);

	return (0);
err:
	printf("%s: tas3004_init failed\n", sc->sc_dev.dv_xname);
	return (-1);
}

void
snapper_init(struct snapper_softc *sc)
{

	/* "sample-rates" (44100, 48000) */
	i2s_set_rate(sc, 44100);

#if 1
	/* Enable I2C interrupts. */
#define IER 4
#define I2C_INT_DATA 0x01
#define I2C_INT_ADDR 0x02
#define I2C_INT_STOP 0x04
	kiic_writereg(sc->sc_i2c, IER,I2C_INT_DATA|I2C_INT_ADDR|I2C_INT_STOP);
#endif

	if (tas3004_init(sc))
		return;

	snapper_set_volume(sc, 190, 190);
	snapper_set_treble(sc, 128); /* 0 dB */
	snapper_set_bass(sc, 128); /* 0 dB */

	/* Mic in, reflects tas3004_initdata.ACR */
	sc->sc_record_source = 1 << 1;
	snapper_set_input(sc, sc->sc_record_source);
}

int
snapper_getdev(void *h, struct audio_device *retp)
{
	*retp = snapper_device;
	return (0);
}

void
snapper_get_default_params(void *addr, int mode, struct audio_params *params)
{
	i2s_get_default_params(params);
}


code:
/*	$OpenBSD: sysbutton.c,v 1.4 2008/06/13 00:31:09 krw Exp $	*/
/*
 * Copyright (c) 2007 Gordon Willem Klok <gwk@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/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/device.h>

#include <ddb/db_var.h>
#include <dev/ofw/openfirm.h>

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

struct sysbutton_softc {
	struct device	sc_dev;
	int		sc_node;
	int 		sc_intr;
};

int sysbutton_match(struct device *, void *, void *);
void sysbutton_attach(struct device *, struct device *, void *);
int sysbutton_intr(void *);

struct cfattach sysbutton_ca = {
	sizeof(struct sysbutton_softc), sysbutton_match,
	sysbutton_attach
};

struct cfdriver sysbutton_cd = {
	NULL, "sysbutton", DV_DULL
};

int
sysbutton_match(struct device *parent, void *arg, void *aux)
{
	struct confargs *ca = aux;

	if (strcmp(ca->ca_name, "indicatorSwitch-gpio") == 0)
		return 1;

	return 0;
}

void
sysbutton_attach(struct device *parent, struct device *self, void *aux)
{
	struct sysbutton_softc *sc = (struct sysbutton_softc *)self;
	struct confargs *ca = aux;
	int intr[2];

	sc->sc_node = ca->ca_node;

	OF_getprop(sc->sc_node, "interrupts", intr, sizeof(intr));
	sc->sc_intr = intr[0];

	printf(": irq %d\n", sc->sc_intr);

	mac_intr_establish(parent, sc->sc_intr, IST_EDGE,
	    IPL_NONE, sysbutton_intr, sc, sc->sc_dev.dv_xname);
}

int
sysbutton_intr(void *v)
{

	/*
	 * XXX: Holding this button causes an interrupt storm if
	 * ddb.console=0.
	 */
#ifdef DDB
	if (db_console)
		Debugger();
#endif

	return 1;
}


code:
/*	$OpenBSD: tumbler.c,v 1.7 2009/10/26 20:17:27 deraadt Exp $	*/

/*-
 * Copyright (c) 2001,2003 Tsubai Masanari.  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.
 */

/*
 * Datasheet is available from
 * http://focus.ti.com/docs/prod/folders/print/tas3001.html
 */

#include <sys/param.h>
#include <sys/audioio.h>
#include <sys/device.h>
#include <sys/systm.h>

#include <dev/audio_if.h>
#include <dev/ofw/openfirm.h>
#include <macppc/dev/dbdma.h>

#include <machine/autoconf.h>

#include <macppc/dev/i2svar.h>

#ifdef TUMBLER_DEBUG
# define DPRINTF printf
#else
# define DPRINTF while (0) printf
#endif

/* XXX */
#define tumbler_softc i2s_softc

/* XXX */
int kiic_write(struct device *, int, int, const void *, int);
int kiic_writereg(struct device *, int, u_int);

void tumbler_init(struct tumbler_softc *);
int tumbler_getdev(void *, struct audio_device *);
int tumbler_match(struct device *, void *, void *);
void tumbler_attach(struct device *, struct device *, void *);
void tumbler_defer(struct device *);
void tumbler_set_volume(struct tumbler_softc *, int, int);
void tumbler_set_bass(struct tumbler_softc *, int);
void tumbler_set_treble(struct tumbler_softc *, int);
void tumbler_get_default_params(void *, int, struct audio_params *);

int tas3001_write(struct tumbler_softc *, u_int, const void *);
int tas3001_init(struct tumbler_softc *);

struct cfattach tumbler_ca = {
	sizeof(struct tumbler_softc), tumbler_match, tumbler_attach
};
struct cfdriver tumbler_cd = {
	NULL, "tumbler", DV_DULL
};

struct audio_hw_if tumbler_hw_if = {
	i2s_open,
	i2s_close,
	NULL,
	i2s_query_encoding,
	i2s_set_params,
	i2s_round_blocksize,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	i2s_halt_output,
	i2s_halt_input,
	NULL,
	tumbler_getdev,
	NULL,
	i2s_set_port,
	i2s_get_port,
	i2s_query_devinfo,
	i2s_allocm,		/* allocm */
	NULL,
	i2s_round_buffersize,
	i2s_mappage,
	i2s_get_props,
	i2s_trigger_output,
	i2s_trigger_input,
	tumbler_get_default_params
};

struct audio_device tumbler_device = {
	"TUMBLER",
	"",
	"tumbler"
};

const uint8_t tumbler_trebletab[] = {
	0x96,	/* -18dB */
	0x94,	/* -17dB */
	0x92,	/* -16dB */
	0x90,	/* -15dB */
	0x8e,	/* -14dB */
	0x8c,	/* -13dB */
	0x8a,	/* -12dB */
	0x88,	/* -11dB */
	0x86,	/* -10dB */
	0x84,	/* -9dB */
	0x82,	/* -8dB */
	0x80,	/* -7dB */
	0x7e,	/* -6dB */
	0x7c,	/* -5dB */
	0x7a,	/* -4dB */
	0x78,	/* -3dB */
	0x76,	/* -2dB */
	0x74,	/* -1dB */
	0x72,	/* 0dB */
	0x70,	/* 1dB */
	0x6d,	/* 2dB */
	0x6b,	/* 3dB */
	0x68,	/* 4dB */
	0x65,	/* 5dB */
	0x62,	/* 6dB */
	0x5e,	/* 7dB */
	0x59,	/* 8dB */
	0x5a,	/* 9dB */
	0x4f,	/* 10dB */
	0x49,	/* 11dB */
	0x42,	/* 12dB */
	0x3a,	/* 13dB */
	0x32,	/* 14dB */
	0x28,	/* 15dB */
	0x1c,	/* 16dB */
	0x10,	/* 17dB */
	0x01,	/* 18dB */
};

const uint8_t tumbler_basstab[] = {
	0x86,	/* -18dB */
	0x7f,	/* -17dB */
	0x7a,	/* -16dB */
	0x76,	/* -15dB */
	0x72,	/* -14dB */
	0x6e,	/* -13dB */
	0x6b,	/* -12dB */
	0x66,	/* -11dB */
	0x61,	/* -10dB */
	0x5d,	/* -9dB */
	0x5a,	/* -8dB */
	0x58,	/* -7dB */
	0x55,	/* -6dB */
	0x53,	/* -5dB */
	0x4f,	/* -4dB */
	0x4b,	/* -3dB */
	0x46,	/* -2dB */
	0x42,	/* -1dB */
	0x3e,	/* 0dB */
	0x3b,	/* 1dB */
	0x38,	/* 2dB */
	0x35,	/* 3dB */
	0x31,	/* 4dB */
	0x2e,	/* 5dB */
	0x2b,	/* 6dB */
	0x28,	/* 7dB */
	0x25,	/* 8dB */
	0x21,	/* 9dB */
	0x1c,	/* 10dB */
	0x18,	/* 11dB */
	0x16,	/* 12dB */
	0x13,	/* 13dB */
	0x10,	/* 14dB */
	0x0d,	/* 15dB */
	0x0a,	/* 16dB */
	0x06,	/* 17dB */
	0x01,	/* 18dB */
};

/* TAS3001 registers */
#define DEQ_MCR		0x01	/* Main Control Register (1byte) */
#define DEQ_DRC		0x02	/* Dynamic Range Compression (2bytes) */
#define DEQ_VOLUME	0x04	/* Volume (6bytes) */
#define DEQ_TREBLE	0x05	/* Treble Control Register (1byte) */
#define DEQ_BASS	0x06	/* Bass Control Register (1byte) */
#define DEQ_MIXER1	0x07	/* Mixer 1 (3bytes) */
#define DEQ_MIXER2	0x08	/* Mixer 2 (3bytes) */
#define DEQ_LB0		0x0a	/* Left Biquad 0 (15bytes) */
#define DEQ_LB1		0x0b	/* Left Biquad 1 (15bytes) */
#define DEQ_LB2		0x0c	/* Left Biquad 2 (15bytes) */
#define DEQ_LB3		0x0d	/* Left Biquad 3 (15bytes) */
#define DEQ_LB4		0x0e	/* Left Biquad 4 (15bytes) */
#define DEQ_LB5		0x0f	/* Left Biquad 5 (15bytes) */
#define DEQ_RB0		0x13	/* Right Biquad 0 (15bytes) */
#define DEQ_RB1		0x14	/* Right Biquad 1 (15bytes) */
#define DEQ_RB2		0x15	/* Right Biquad 2 (15bytes) */
#define DEQ_RB3		0x16	/* Right Biquad 3 (15bytes) */
#define DEQ_RB4		0x17	/* Right Biquad 4 (15bytes) */
#define DEQ_RB5		0x18	/* Right Biquad 5 (15bytes) */

#define DEQ_MCR_FL	0x80	/* Fast load */
#define DEQ_MCR_SC	0x40	/* SCLK frequency */
#define  DEQ_MCR_SC_32	0x00	/*  32fs */
#define  DEQ_MCR_SC_64	0x40	/*  64fs */
#define DEQ_MCR_OM	0x30	/* Output serial port mode */
#define  DEQ_MCR_OM_L	0x00	/*  Left justified */
#define  DEQ_MCR_OM_R	0x10	/*  Right justified */
#define  DEQ_MCR_OM_I2S	0x20	/*  I2S */
#define DEQ_MCR_IM	0x0c	/* Input serial port mode */
#define  DEQ_MCR_IM_L	0x00	/*  Left justified */
#define  DEQ_MCR_IM_R	0x04	/*  Right justified */
#define  DEQ_MCR_IM_I2S	0x08	/*  I2S */
#define DEQ_MCR_W	0x03	/* Serial port word length */
#define  DEQ_MCR_W_16	0x00	/*  16 bit */
#define  DEQ_MCR_W_18	0x01	/*  18 bit */
#define  DEQ_MCR_W_20	0x02	/*  20 bit */

#define DEQ_DRC_CR	0xc0	/* Compression ratio */
#define  DEQ_DRC_CR_31	0xc0	/*  3:1 */
#define DEQ_DRC_EN	0x01	/* Enable DRC */

#define DEQ_MCR_I2S	(DEQ_MCR_OM_I2S | DEQ_MCR_IM_I2S)

struct tas3001_reg {
	u_char MCR[1];
	u_char DRC[2];
	u_char VOLUME[6];
	u_char TREBLE[1];
	u_char BASS[1];
	u_char MIXER1[3];
	u_char MIXER2[3];
	u_char LB0[15];
	u_char LB1[15];
	u_char LB2[15];
	u_char LB3[15];
	u_char LB4[15];
	u_char LB5[15];
	u_char RB0[15];
	u_char RB1[15];
	u_char RB2[15];
	u_char RB3[15];
	u_char RB4[15];
	u_char RB5[15];
};

int
tumbler_match(struct device *parent, void *match, void *aux)
{
	struct confargs *ca = aux;
	int soundbus, soundchip;
	char compat[32];

	if (strcmp(ca->ca_name, "i2s") != 0)
		return (0);

	if ((soundbus = OF_child(ca->ca_node)) == 0 ||
	    (soundchip = OF_child(soundbus)) == 0)
		return (0);

	bzero(compat, sizeof compat);
	OF_getprop(soundchip, "compatible", compat, sizeof compat);

	if (strcmp(compat, "tumbler") != 0)
		return (0);

	return (1);
}

void
tumbler_attach(struct device *parent, struct device *self, void *aux)
{
	struct tumbler_softc *sc = (struct tumbler_softc *)self;

	sc->sc_setvolume = tumbler_set_volume;
	sc->sc_setbass = tumbler_set_bass;
	sc->sc_settreble = tumbler_set_treble;

	i2s_attach(parent, sc, aux);
	config_defer(self, tumbler_defer);
}

void
tumbler_defer(struct device *dev)
{
	struct tumbler_softc *sc = (struct tumbler_softc *)dev;
	struct device *dv;

	TAILQ_FOREACH(dv, &alldevs, dv_list)
		if (strcmp(dv->dv_cfdata->cf_driver->cd_name, "kiic") == 0 &&
		    strcmp(dv->dv_parent->dv_cfdata->cf_driver->cd_name, "macobio") == 0)
			sc->sc_i2c = dv;
	if (sc->sc_i2c == NULL) {
		printf("%s: unable to find i2c\n", sc->sc_dev.dv_xname);
		return;
	}

	/* XXX If i2c has failed to attach, what should we do? */

	audio_attach_mi(&tumbler_hw_if, sc, &sc->sc_dev);

	tumbler_init(sc);
}

void
tumbler_set_volume(struct tumbler_softc *sc, int left, int right)
{
	u_char vol[6];

	sc->sc_vol_l = left;
	sc->sc_vol_r = right;

	left <<= 6;	/* XXX for now */
	right <<= 6;

	vol[0] = left >> 16;
	vol[1] = left >> 8;
	vol[2] = left;
	vol[3] = right >> 16;
	vol[4] = right >> 8;
	vol[5] = right;

	tas3001_write(sc, DEQ_VOLUME, vol);
}

void
tumbler_set_treble(struct tumbler_softc *sc, int value)
{
	uint8_t reg;

	if ((value >= 0) && (value <= 255) && (value != sc->sc_treble)) {
		reg = tumbler_trebletab[(value >> 3) + 2];
		if (tas3001_write(sc, DEQ_TREBLE, &reg) < 0)
			return;
		sc->sc_treble = value;
	}
}

void
tumbler_set_bass(struct tumbler_softc *sc, int value)
{
	uint8_t reg;

	if ((value >= 0) && (value <= 255) && (value != sc->sc_bass)) {
		reg = tumbler_basstab[(value >> 3) + 2];
		if (tas3001_write(sc, DEQ_BASS, &reg) < 0)
			return;
		sc->sc_bass = value;
	}
}

const struct tas3001_reg tas3001_initdata = {
	{ DEQ_MCR_SC_64 | DEQ_MCR_I2S | DEQ_MCR_W_20 },		/* MCR */
	{ DEQ_DRC_CR_31, 0xa0 },				/* DRC */
	{ 0, 0, 0, 0, 0, 0 },					/* VOLUME */
	{ 0x72 },						/* TREBLE */
	{ 0x3e },						/* BASS */
	{ 0x10, 0x00, 0x00 },					/* MIXER1 */
	{ 0x00, 0x00, 0x00 },					/* MIXER2 */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
	{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },	/* BIQUAD */
};

const char tas3001_regsize[] = {
	0,					/* 0x00 */
	sizeof tas3001_initdata.MCR,		/* 0x01 */
	sizeof tas3001_initdata.DRC,		/* 0x02 */
	0,					/* 0x03 */
	sizeof tas3001_initdata.VOLUME,		/* 0x04 */
	sizeof tas3001_initdata.TREBLE,		/* 0x05 */
	sizeof tas3001_initdata.BASS,		/* 0x06 */
	sizeof tas3001_initdata.MIXER1,		/* 0x07 */
	sizeof tas3001_initdata.MIXER2,		/* 0x08 */
	0,					/* 0x09 */
	sizeof tas3001_initdata.LB0,		/* 0x0a */
	sizeof tas3001_initdata.LB1,		/* 0x0b */
	sizeof tas3001_initdata.LB2,		/* 0x0c */
	sizeof tas3001_initdata.LB3,		/* 0x0d */
	sizeof tas3001_initdata.LB4,		/* 0x0e */
	sizeof tas3001_initdata.LB5,		/* 0x0f */
	0,					/* 0x10 */
	0,					/* 0x11 */
	0,					/* 0x12 */
	sizeof tas3001_initdata.RB0,		/* 0x13 */
	sizeof tas3001_initdata.RB1,		/* 0x14 */
	sizeof tas3001_initdata.RB2,		/* 0x15 */
	sizeof tas3001_initdata.RB3,		/* 0x16 */
	sizeof tas3001_initdata.RB4,		/* 0x17 */
	sizeof tas3001_initdata.RB5		/* 0x18 */
};

#define DEQaddr 0x68

int
tas3001_write(struct tumbler_softc *sc, u_int reg, const void *data)
{
	int size;

	KASSERT(reg < sizeof tas3001_regsize);
	size = tas3001_regsize[reg];
	KASSERT(size > 0);

	if (kiic_write(sc->sc_i2c, DEQaddr, reg, data, size))
		return (-1);

	return (0);
}

#define DEQ_WRITE(sc, reg, addr) \
	if (tas3001_write(sc, reg, addr)) goto err

int
tas3001_init(struct tumbler_softc *sc)
{
	deq_reset(sc);

	/* Initialize TAS3001 registers. */
	DEQ_WRITE(sc, DEQ_LB0, tas3001_initdata.LB0);
	DEQ_WRITE(sc, DEQ_LB1, tas3001_initdata.LB1);
	DEQ_WRITE(sc, DEQ_LB2, tas3001_initdata.LB2);
	DEQ_WRITE(sc, DEQ_LB3, tas3001_initdata.LB3);
	DEQ_WRITE(sc, DEQ_LB4, tas3001_initdata.LB4);
	DEQ_WRITE(sc, DEQ_LB5, tas3001_initdata.LB5);
	DEQ_WRITE(sc, DEQ_RB0, tas3001_initdata.RB0);
	DEQ_WRITE(sc, DEQ_RB1, tas3001_initdata.RB1);
	DEQ_WRITE(sc, DEQ_RB1, tas3001_initdata.RB1);
	DEQ_WRITE(sc, DEQ_RB2, tas3001_initdata.RB2);
	DEQ_WRITE(sc, DEQ_RB3, tas3001_initdata.RB3);
	DEQ_WRITE(sc, DEQ_RB4, tas3001_initdata.RB4);
	DEQ_WRITE(sc, DEQ_MCR, tas3001_initdata.MCR);
	DEQ_WRITE(sc, DEQ_DRC, tas3001_initdata.DRC);
	DEQ_WRITE(sc, DEQ_VOLUME, tas3001_initdata.VOLUME);
	DEQ_WRITE(sc, DEQ_TREBLE, tas3001_initdata.TREBLE);
	DEQ_WRITE(sc, DEQ_BASS, tas3001_initdata.BASS);
	DEQ_WRITE(sc, DEQ_MIXER1, tas3001_initdata.MIXER1);
	DEQ_WRITE(sc, DEQ_MIXER2, tas3001_initdata.MIXER2);

	return (0);
err:
	printf("%s: tas3001_init: error\n", sc->sc_dev.dv_xname);
	return (-1);
}

void
tumbler_init(struct tumbler_softc *sc)
{

	/* "sample-rates" (44100, 48000) */
	i2s_set_rate(sc, 44100);

#if 1
	/* Enable I2C interrupts. */
#define IER 4
#define I2C_INT_DATA 0x01
#define I2C_INT_ADDR 0x02
#define I2C_INT_STOP 0x04
	kiic_writereg(sc->sc_i2c, IER,I2C_INT_DATA|I2C_INT_ADDR|I2C_INT_STOP);
#endif

	if (tas3001_init(sc))
		return;

	tumbler_set_volume(sc, 80, 80);
}

int
tumbler_getdev(void *h, struct audio_device *retp)
{
	*retp = tumbler_device;
	return (0);
}

void
tumbler_get_default_params(void *addr, int mode, struct audio_params *params)
{
	i2s_get_default_params(params);
}


code:
/*	$OpenBSD: uni_n.c,v 1.17 2015/03/30 13:45:02 mpi Exp $	*/

/*
 * Copyright (c) 2013 Martin Pieuchot
 * Copyright (c) 1998-2001 Dale Rahn.
 * 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.
 *
 * 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.
 */

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

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

#include <dev/ofw/openfirm.h>

#define UNINORTH_CLK_OFFSET	0x20
#define UNINORTH_POW_OFFSET	0x30
#define UNINORTH_STA_OFFSET	0x70
#define UNINORTH_MPIC_OFFSET	0xe0

#define UNINORTH_PCICLOCK_CTL	0x01
#define UNINORTH_ETHERNET_CTL	0x02
#define UNINORTH_FIREWIRE_CTL	0x04

#define UNINORTH_POW_NORMAL	0x00
#define UNINORTH_POW_IDLE	0x01
#define UNINORTH_POW_SLEEP	0x02

#define UNINORTH_MPIC_RESET	0x02
#define UNINORTH_MPIC_ENABLE	0x04

#define UNINORTH_SLEEPING	0x01
#define UNINORTH_RUNNING	0x02

struct memc_softc {
	struct device sc_dev;
	struct ppc_bus_space sc_membus_space;

	uint8_t *sc_baseaddr;
};

int	memcmatch(struct device *, void *, void *);
void	memcattach(struct device *, struct device *, void *);
void	memc_attach_children(struct memc_softc *sc, int memc_node);
int	memc_print(void *aux, const char *name);

struct cfdriver memc_cd = {
	NULL, "memc", DV_DULL
};

const struct cfattach memc_ca = {
	sizeof(struct memc_softc), memcmatch, memcattach
};

void memc_sleep(void);
void memc_resume(void);
uint32_t memc_read(struct memc_softc *sc, int);
void memc_write(struct memc_softc *sc, int, uint32_t);
void memc_enable(struct memc_softc *, int, uint32_t);
void memc_disable(struct memc_softc *, int, uint32_t);

int
memcmatch(struct device *parent, void *cf, void *aux)
{
	struct confargs *ca = aux;

	if (strcmp(ca->ca_name, "memc") != 0)
		return (0);

	return (1);
}

void
memcattach(struct device *parent, struct device *self, void *aux)
{
	struct memc_softc *sc = (struct memc_softc *)self;
	struct confargs *ca = aux;
	uint32_t rev, reg[2];
	char name[32];
	int len;

	OF_getprop(ca->ca_node, "reg", &reg, sizeof(reg));

	len = OF_getprop(ca->ca_node, "name", name, sizeof(name));
	if (len > 0)
		name[len] = 0;

	/* Map the first page in order to access the registers */
	if (strcmp(name, "u3") == 0 || strcmp(name, "u4") == 0)
		sc->sc_baseaddr = mapiodev(reg[1], PAGE_SIZE);
	else
		sc->sc_baseaddr = mapiodev(reg[0], PAGE_SIZE);

	/* Enable the ethernet clock */
	memc_enable(sc, UNINORTH_CLK_OFFSET, UNINORTH_ETHERNET_CTL);
	len = OF_getprop(ca->ca_node, "device-rev", &rev, sizeof(rev));
	if (len < 0)
		rev = 0;

	printf (": %s rev 0x%x\n", name, rev);

	memc_attach_children(sc, ca->ca_node);
}

void
memc_attach_children(struct memc_softc *sc, int memc_node)
{
	struct confargs ca;
	int node, namelen;
	u_int32_t reg[20];
	int32_t intr[8];
	char	name[32];

	ca.ca_iot = &sc->sc_membus_space;
	ca.ca_dmat = 0; /* XXX */
	ca.ca_baseaddr = 0; /* XXX */
	sc->sc_membus_space.bus_base = ca.ca_baseaddr;

        for (node = OF_child(memc_node); node; node = OF_peer(node)) {
		namelen = OF_getprop(node, "name", name, sizeof(name));
		if (namelen < 0)
			continue;
		if (namelen >= sizeof(name))
			continue;
		name[namelen] = 0;

		ca.ca_name = name;
		ca.ca_node = node;
		ca.ca_nreg = OF_getprop(node, "reg", reg, sizeof(reg));
		ca.ca_reg = reg;
		ca.ca_nintr = OF_getprop(node, "AAPL,interrupts", intr,
				sizeof(intr));
		if (ca.ca_nintr == -1)
			ca.ca_nintr = OF_getprop(node, "interrupts", intr,
					sizeof(intr));
		ca.ca_intr = intr;

		if (strcmp(ca.ca_name, "mpic") == 0)
			memc_enable(sc, UNINORTH_MPIC_OFFSET,
			    UNINORTH_MPIC_RESET|UNINORTH_MPIC_ENABLE);

		config_found((struct device *)sc, &ca, memc_print);
	}
}

int
memc_print(void *aux, const char *name)
{
	struct confargs *ca = aux;
	/* we dont want extra stuff printing */
	if (name)
		printf("\"%s\" at %s", ca->ca_name, name);
	if (ca->ca_nreg > 0)
		printf(" offset 0x%x", ca->ca_reg[0]);
	return UNCONF;
}

void
memc_sleep(void)
{
	struct memc_softc *sc = memc_cd.cd_devs[0];

	memc_write(sc, UNINORTH_STA_OFFSET, UNINORTH_SLEEPING);
	DELAY(10);
	memc_write(sc, UNINORTH_POW_OFFSET, UNINORTH_POW_SLEEP);
	DELAY(10);
}

void
memc_resume(void)
{
	struct memc_softc *sc = memc_cd.cd_devs[0];

	memc_write(sc, UNINORTH_POW_OFFSET, UNINORTH_POW_NORMAL);
	DELAY(10);
	memc_write(sc, UNINORTH_STA_OFFSET, UNINORTH_RUNNING);
	DELAY(100); /* XXX */
}

uint32_t
memc_read(struct memc_softc *sc, int offset)
{
	return in32(sc->sc_baseaddr + offset);
}

void
memc_write(struct memc_softc *sc, int offset, uint32_t value)
{
	out32(sc->sc_baseaddr + offset, value);
}

void
memc_enable(struct memc_softc *sc, int offset, uint32_t bits)
{
	bits |= memc_read(sc, offset);
	memc_write(sc, offset, bits);
}

void
memc_disable(struct memc_softc *sc, int offset, uint32_t bits)
{
	bits = memc_read(sc, offset) & ~bits;
	memc_write(sc, offset, bits);
}


code:
/*	$OpenBSD: viareg.h,v 1.4 2002/06/07 07:14:48 miod Exp $	*/
/*	$NetBSD: viareg.h,v 1.2 1998/10/20 14:56:30 tsubai Exp $	*/

/*-
 * Copyright (C) 1993	Allen K. Briggs, Chris P. Caputo,
 *			Michael L. Finch, Bradley A. Grantham, and
 *			Lawrence A. Kesteloot
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the Alice Group.
 * 4. The names of the Alice Group or any of its members may not be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``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 ALICE GROUP 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.
 *
 */
/*

	Prototype VIA control definitions

	06/04/92,22:33:57 BG Let's see what I can do.

*/

extern volatile unsigned char *Via1Base;
#define VIA1_addr	Via1Base	/* at PA 0x50f00000 */

#define VIA1		0
#define VIA2		0

	/* VIA interface registers */
#define vBufA		0x1e00	/* register A */
#define vBufB		0	/* register B */
#define vDirA		0x0600	/* data direction register */
#define vDirB		0x0400	/* data direction register */
#define vT1C		0x0800
#define vT1CH		0x0a00
#define vT1L		0x0c00
#define vT1LH		0x0e00
#define vT2C		0x1000
#define vT2CH		0x1200
#define vSR		0x1400	/* shift register */
#define vACR		0x1600	/* aux control register */
#define vIFR		0x1a00	/* interrupt flag register */
#define vIER		0x1c00	/* interrupt enable register */

#define via_reg(v, r) (*(Via1Base + (r)))

#include <machine/pio.h>

static __inline void
via_reg_and(int ign, int reg, int val)
{
	volatile unsigned char *addr = Via1Base + reg;

	out8(addr, in8(addr) & val);
}

static __inline void
via_reg_or(int ign, int reg, int val)
{
	volatile unsigned char *addr = Via1Base + reg;

	out8(addr, in8(addr) | val);
}

static __inline void
via_reg_xor(int ign, int reg, int val)
{
	volatile unsigned char *addr = Via1Base + reg;

	out8(addr, in8(addr) ^ val);
}

static __inline int
read_via_reg(int ign, int reg)
{
	volatile unsigned char *addr = Via1Base + reg;

	return in8(addr);
}

static __inline void
write_via_reg(int ign, int reg, int val)
{
	volatile unsigned char *addr = Via1Base + reg;

	out8(addr, val);
}