code:
/*	$OpenBSD: pxa2x0_apm.c,v 1.43 2014/09/20 09:28:24 kettenis Exp $	*/

/*-
 * Copyright (c) 2001 Alexander Guy.  All rights reserved.
 * Copyright (c) 1998-2001 Michael Shalayeff. All rights reserved.
 * Copyright (c) 1995 John T. Kohl.  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 University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF MIND, 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/systm.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/rwlock.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/device.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/reboot.h>
#include <sys/event.h>

#include <machine/conf.h>
#include <machine/cpu.h>
#include <machine/apmvar.h>

#include <arm/xscale/pxa2x0reg.h>
#include <arm/xscale/pxa2x0var.h>
#include <arm/xscale/pxa2x0_apm.h>
#include <arm/xscale/pxa2x0_gpio.h>
#include <dev/wscons/wsdisplayvar.h>

#include "wsdisplay.h"

#if defined(APMDEBUG)
#define DPRINTF(x)	printf x
#else
#define	DPRINTF(x)	/**/
#endif

#define APM_LOCK(sc)    rw_enter_write(&(sc)->sc_lock);
#define APM_UNLOCK(sc)  rw_exit_write(&(sc)->sc_lock);

struct cfdriver apm_cd = {
	NULL, "apm", DV_DULL
};

#define	APMUNIT(dev)	(minor(dev)&0xf0)
#define	APMDEV(dev)	(minor(dev)&0x0f)
#define APMDEV_NORMAL	0
#define APMDEV_CTL	8

int	apm_userstandbys;
int	apm_suspends;
int	apm_battlow;

/* battery percentage at which we get verbose in our warnings.  This
   value can be changed using sysctl(8), value machdep.apmwarn.
   Setting it to zero kills all warnings */
int	cpu_apmwarn = 10;

void	apm_power_print(struct pxa2x0_apm_softc *, struct apm_power_info *);
void	apm_power_info(struct pxa2x0_apm_softc *, struct apm_power_info *);
void	apm_suspend(struct pxa2x0_apm_softc *);
void	apm_resume(struct pxa2x0_apm_softc *);
int	apm_get_event(struct pxa2x0_apm_softc *, u_int *);
int	apm_handle_event(struct pxa2x0_apm_softc *, u_int);
void	apm_thread_create(void *);
void	apm_thread(void *);

extern int perflevel;
int	freq;
void	pxa2x0_setperf(int speed);
int	pxa2x0_cpuspeed(int *speed);

int	apm_record_event(struct pxa2x0_apm_softc *, u_int);
void	filt_apmrdetach(struct knote *kn);
int	filt_apmread(struct knote *kn, long hint);
int	apmkqfilter(dev_t dev, struct knote *kn);

struct filterops apmread_filtops =
	{ 1, NULL, filt_apmrdetach, filt_apmread};

/*
 * Flags to control kernel display
 *	SCFLAG_NOPRINT:		do not output APM power messages due to
 *				a power change event.
 *
 *	SCFLAG_PCTPRINT:	do not output APM power messages due to
 *				to a power change event unless the battery
 *				percentage changes.
 */

#define SCFLAG_NOPRINT	0x0008000
#define SCFLAG_PCTPRINT	0x0004000
#define SCFLAG_PRINT	(SCFLAG_NOPRINT|SCFLAG_PCTPRINT)

#define	SCFLAG_OREAD 	(1 << 0)
#define	SCFLAG_OWRITE	(1 << 1)
#define	SCFLAG_OPEN	(SCFLAG_OREAD|SCFLAG_OWRITE)

/* This structure must be kept in sync with pxa2x0_apm_asm.S. */
struct pxa2x0_memcfg {
	/* SDRAM refresh */
	u_int32_t mdrefr_high;		/* 0x00 */
	u_int32_t mdrefr_low;		/* 0x04 */
	u_int32_t mdrefr_low2;		/* 0x08 */
	/* Synchronous, static, or VLIO interfaces */
	u_int32_t msc_high[3];		/* 0x0c */
	u_int32_t msc_low[3];		/* 0x18 */
	/* XXX move up */
	u_int32_t mdrefr_91;		/* 0x24 */
};

/* XXX */
#define MDREFR_C3000	(MDREFR_K0DB2 | MDREFR_E1PIN | MDREFR_K1RUN | \
			 MDREFR_K1DB2 | MDREFR_K2DB2 | MDREFR_APD)
#define MSC0_HIGH \
		( 7 << MSC_RRR_SHIFT << 16) | \
		(15 << MSC_RDN_SHIFT << 16) | \
		(15 << MSC_RDF_SHIFT << 16) | \
		(MSC_RT_NONBURST     << 16) | \
		( 2 << MSC_RRR_SHIFT)       | \
		(13 << MSC_RDN_SHIFT)       | \
		(13 << MSC_RDF_SHIFT)       | \
		MSC_RBW	/* PXA271 */        | \
		MSC_RT_NONBURST
#define MSC1_HIGH \
		( 7 << MSC_RRR_SHIFT << 16) | \
		(15 << MSC_RDN_SHIFT << 16) | \
		(15 << MSC_RDF_SHIFT << 16) | \
		(MSC_RT_VLIO         << 16) | \
		( 3 << MSC_RRR_SHIFT)       | \
		( 4 << MSC_RDN_SHIFT)       | \
		(13 << MSC_RDF_SHIFT)       | \
		MSC_RT_VLIO
#define MSC2_HIGH \
		( 7 << MSC_RRR_SHIFT << 16) | \
		(15 << MSC_RDN_SHIFT << 16) | \
		(15 << MSC_RDF_SHIFT << 16) | \
		(MSC_RT_NONBURST     << 16) | \
		( 3 << MSC_RRR_SHIFT)       | \
		( 4 << MSC_RDN_SHIFT)       | \
		(13 << MSC_RDF_SHIFT)       | \
		MSC_RT_VLIO
#define MSC0_LOW \
		( 7 << MSC_RRR_SHIFT << 16) | \
		(15 << MSC_RDN_SHIFT << 16) | \
		(15 << MSC_RDF_SHIFT << 16) | \
		(MSC_RT_NONBURST     << 16) | \
		( 1 << MSC_RRR_SHIFT)       | \
		( 8 << MSC_RDN_SHIFT)       | \
		( 8 << MSC_RDF_SHIFT)       | \
		MSC_RBW	/* PXA271 */        | \
		MSC_RT_NONBURST
#define MSC1_LOW \
		( 7 << MSC_RRR_SHIFT << 16) | \
		(15 << MSC_RDN_SHIFT << 16) | \
		(15 << MSC_RDF_SHIFT << 16) | \
		(MSC_RT_VLIO         << 16) | \
		( 1 << MSC_RRR_SHIFT)       | \
		( 2 << MSC_RDN_SHIFT)       | \
		( 6 << MSC_RDF_SHIFT)       | \
		MSC_RT_VLIO
#define MSC2_LOW \
		( 7 << MSC_RRR_SHIFT << 16) | \
		(15 << MSC_RDN_SHIFT << 16) | \
		(15 << MSC_RDF_SHIFT << 16) | \
		(MSC_RT_NONBURST     << 16) | \
		( 1 << MSC_RRR_SHIFT)       | \
		( 2 << MSC_RDN_SHIFT)       | \
		( 6 << MSC_RDF_SHIFT)       | \
		MSC_RT_VLIO
struct pxa2x0_memcfg pxa2x0_memcfg = {
	(MDREFR_C3000 | 0x030),
	(MDREFR_C3000 | 0x00b),
	(MDREFR_C3000 | 0x017),
	{ MSC0_HIGH, MSC1_HIGH, MSC2_HIGH },
	{ MSC1_LOW, MSC1_LOW, MSC2_LOW },
	(MDREFR_C3000 | 0x013)
};

#define PI2C_RETRY_COUNT	10
/* XXX varies depending on voltage regulator IC. */
#define PI2C_VOLTAGE_LOW	0x13	/* 1.00V */
#define PI2C_VOLTAGE_HIGH	0x1a	/* 1.35V */

void	pxa2x0_pi2c_open(bus_space_tag_t, bus_space_handle_t);
void	pxa2x0_pi2c_close(bus_space_tag_t, bus_space_handle_t);
int	pxa2x0_pi2c_read(bus_space_tag_t, bus_space_handle_t, u_char, u_char *);
int	pxa2x0_pi2c_write(bus_space_tag_t, bus_space_handle_t, u_char, u_char);
int	pxa2x0_pi2c_getvoltage(bus_space_tag_t, bus_space_handle_t, u_char *);
int	pxa2x0_pi2c_setvoltage(bus_space_tag_t, bus_space_handle_t, u_char);
#if 0
void	pxa2x0_pi2c_print(struct pxa2x0_apm_softc *);
#endif

/* XXX used in pxa2x0_apm_asm.S */
bus_space_handle_t pxa2x0_gpio_ioh;
bus_space_handle_t pxa2x0_clkman_ioh;
bus_space_handle_t pxa2x0_memctl_ioh;

/* pxa2x0_apm_asm.S */
void	pxa27x_run_mode(void);
void	pxa27x_fastbus_run_mode(int, u_int32_t);
void	pxa27x_frequency_change(int, int, struct pxa2x0_memcfg *);
void	pxa2x0_cpu_suspend(void);
void	pxa2x0_cpu_resume(void);
void	pxa27x_cpu_speed_high(void);
void	pxa27x_cpu_speed_low(void);
void	pxa27x_cpu_speed_91(void);
void	pxa27x_cpu_speed_208(void);

void
apm_power_print(struct pxa2x0_apm_softc *sc, struct apm_power_info *powerp)
{

	if (powerp->battery_life != APM_BATT_LIFE_UNKNOWN)
		printf("%s: battery life expectancy %d%%\n",
		    sc->sc_dev.dv_xname, powerp->battery_life);

	printf("%s: AC ", sc->sc_dev.dv_xname);
	switch (powerp->ac_state) {
	case APM_AC_OFF:
		printf("off,");
		break;
	case APM_AC_ON:
		printf("on,");
		break;
	case APM_AC_BACKUP:
		printf("backup power,");
		break;
	default:
	case APM_AC_UNKNOWN:
		printf("unknown,");
		break;
	}

	printf(" battery is ");
	switch (powerp->battery_state) {
	case APM_BATT_HIGH:
		printf("high");
		break;
	case APM_BATT_LOW:
		printf("low");
		break;
	case APM_BATT_CRITICAL:
		printf("CRITICAL");
		break;
	case APM_BATT_CHARGING:
		printf("charging");
		break;
	case APM_BATT_UNKNOWN:
		printf("unknown");
		break;
	default:
		printf("undecoded (%x)", powerp->battery_state);
		break;
	}

	printf("\n");
}

void
apm_power_info(struct pxa2x0_apm_softc *sc,
    struct apm_power_info *power)
{

	power->ac_state = APM_AC_UNKNOWN;
	power->battery_state = APM_BATT_UNKNOWN;
	power->battery_life = 0 /* APM_BATT_LIFE_UNKNOWN */;
	power->minutes_left = 0;

	if (sc->sc_power_info != NULL)
		sc->sc_power_info(sc, power);
}

void
apm_suspend(struct pxa2x0_apm_softc *sc)
{
	int s;

#if NWSDISPLAY > 0
	wsdisplay_suspend();
#endif /* NWSDISPLAY > 0 */

	resettodr();

	if (sc->sc_suspend == NULL)
		pxa2x0_wakeup_config(PXA2X0_WAKEUP_ALL, 1);
	else
		sc->sc_suspend(sc);

	s = splhigh();
	config_suspend_all(DVACT_SUSPEND);

	/* XXX
	 * Flag to disk drivers that they should "power down" the disk
	 * when we get to DVACT_POWERDOWN.
	 */
	boothowto |= RB_POWERDOWN;
	config_suspend_all(DVACT_POWERDOWN);
	boothowto &= ~RB_POWERDOWN;

	splx(s);

	pxa2x0_apm_sleep(sc);
}

void
apm_resume(struct pxa2x0_apm_softc *sc)
{
	int s;

	s = splhigh();
	config_suspend_all(DVACT_RESUME);
	splx(s);

	inittodr(0);

	/*
	 * Clear the OTG Peripheral hold after running the pxaudc and pxaohci
	 * ca_activate to re-enable their operation. See 3.8.1.2
	 */
	/* XXX ifdef NPXAUDC > 0 */
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PSSR, PSSR_OTGPH);

	bufq_restart();

	config_suspend_all(DVACT_WAKEUP);

#if NWSDISPLAY > 0
	wsdisplay_resume();
#endif /* NWSDISPLAY > 0 */
}

int
apm_get_event(struct pxa2x0_apm_softc *sc, u_int *typep)
{

	if (sc->sc_get_event != NULL)
		return (sc->sc_get_event(sc, typep));

	*typep = APM_NOEVENT;
	return (1);
}

int
apm_handle_event(struct pxa2x0_apm_softc *sc, u_int type)
{
	struct	apm_power_info power;
	int	ret = 0;

	switch (type) {
	case APM_NOEVENT:
		ret = 1;
		break;
	case APM_CRIT_SUSPEND_REQ:
		DPRINTF(("suspend required immediately\n"));
#if 0
		/* XXX apmd would make us suspend again after resume. */
		(void)apm_record_event(sc, type);
#endif
		/*
		 * We ignore APM_CRIT_RESUME and just suspend here as usual
		 * to simplify the actual apm_get_event() implementation.
		 */
		apm_suspends++;
		ret = 1;
		break;
	case APM_USER_SUSPEND_REQ:
	case APM_SUSPEND_REQ:
		DPRINTF(("suspend requested\n"));
		if (apm_record_event(sc, type)) {
			DPRINTF(("suspend ourselves\n"));
			apm_suspends++;
		}
		break;
	case APM_POWER_CHANGE:
		DPRINTF(("power status change\n"));
		apm_power_info(sc, &power);
		if (power.battery_life != APM_BATT_LIFE_UNKNOWN &&
		    power.battery_life < cpu_apmwarn &&
		    (sc->sc_flags & SCFLAG_PRINT) != SCFLAG_NOPRINT &&
		    ((sc->sc_flags & SCFLAG_PRINT) != SCFLAG_PCTPRINT ||
		    sc->sc_batt_life != power.battery_life)) {
			sc->sc_batt_life = power.battery_life;
			apm_power_print(sc, &power);
		}
		apm_record_event(sc, type);
		break;
	case APM_BATTERY_LOW:
		DPRINTF(("Battery low!\n"));
		apm_battlow++;
		apm_record_event(sc, type);
		break;
	default:
		DPRINTF(("apm_handle_event: unsupported event, code %d\n",
		    type));
	}

	return (ret);
}

void
apm_thread_create(void *v)
{
	struct pxa2x0_apm_softc *sc = v;

	if (kthread_create(apm_thread, sc, &sc->sc_thread,
	    sc->sc_dev.dv_xname)) {
		/* apm_disconnect(sc); */
		printf("%s: failed to create kernel thread, disabled",
		    sc->sc_dev.dv_xname);
	}
}

void
apm_thread(void *v)
{
	struct pxa2x0_apm_softc *sc = v;
	u_int	type;

	for (;;) {
		APM_LOCK(sc);

		while (1) {
			if (apm_get_event(sc, &type) != 0)
				break;
			if (apm_handle_event(sc, type) != 0)
				break;
		}
		if (apm_suspends || apm_userstandbys /* || apm_battlow*/) {
			apm_suspend(sc);
			apm_resume(sc);
		}
		apm_battlow = apm_suspends = apm_userstandbys = 0;

		APM_UNLOCK(sc);
		tsleep(&lbolt, PWAIT, "apmev", 0);
	}
}

int
apmopen(dev_t dev, int flag, int mode, struct proc *p)
{
	struct pxa2x0_apm_softc *sc;
	int error = 0;

	/* apm0 only */
	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
		return (ENXIO);

	DPRINTF(("apmopen: dev %d pid %d flag %x mode %x\n",
	    APMDEV(dev), p->p_pid, flag, mode));

	switch (APMDEV(dev)) {
	case APMDEV_CTL:
		if (!(flag & FWRITE)) {
			error = EINVAL;
			break;
		}
		if (sc->sc_flags & SCFLAG_OWRITE) {
			error = EBUSY;
			break;
		}
		sc->sc_flags |= SCFLAG_OWRITE;
		break;
	case APMDEV_NORMAL:
		if (!(flag & FREAD) || (flag & FWRITE)) {
			error = EINVAL;
			break;
		}
		sc->sc_flags |= SCFLAG_OREAD;
		break;
	default:
		error = ENXIO;
		break;
	}
	return (error);
}

int
apmclose(dev_t dev, int flag, int mode, struct proc *p)
{
	struct pxa2x0_apm_softc *sc;

	/* apm0 only */
	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
		return (ENXIO);

	DPRINTF(("apmclose: pid %d flag %x mode %x\n", p->p_pid, flag, mode));

	switch (APMDEV(dev)) {
	case APMDEV_CTL:
		sc->sc_flags &= ~SCFLAG_OWRITE;
		break;
	case APMDEV_NORMAL:
		sc->sc_flags &= ~SCFLAG_OREAD;
		break;
	}
	return (0);
}

int
apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	struct pxa2x0_apm_softc *sc;
	struct apm_power_info *power;
	int error = 0;

	/* apm0 only */
	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
		return (ENXIO);

	switch (cmd) {
		/* some ioctl names from linux */
	case APM_IOC_STANDBY:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		else
			apm_userstandbys++;
		break;
	case APM_IOC_SUSPEND:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		else
			apm_suspends++;	/* XXX */
		break;
	case APM_IOC_PRN_CTL:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		else {
			int flag = *(int *)data;
			DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag ));
			switch (flag) {
			case APM_PRINT_ON:	/* enable printing */
				sc->sc_flags &= ~SCFLAG_PRINT;
				break;
			case APM_PRINT_OFF: /* disable printing */
				sc->sc_flags &= ~SCFLAG_PRINT;
				sc->sc_flags |= SCFLAG_NOPRINT;
				break;
			case APM_PRINT_PCT: /* disable some printing */
				sc->sc_flags &= ~SCFLAG_PRINT;
				sc->sc_flags |= SCFLAG_PCTPRINT;
				break;
			default:
				error = EINVAL;
				break;
			}
		}
		break;
	case APM_IOC_DEV_CTL:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		break;
	case APM_IOC_GETPOWER:
	        power = (struct apm_power_info *)data;
		apm_power_info(sc, power);
		break;
	case APM_IOC_STANDBY_REQ:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		else if (apm_record_event(sc, APM_USER_STANDBY_REQ))
			error = EINVAL; /* ? */
		break;
	case APM_IOC_SUSPEND_REQ:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		else if (apm_record_event(sc, APM_USER_SUSPEND_REQ))
			error = EINVAL; /* ? */
	default:
		error = ENOTTY;
	}

	return (error);
}

int
apm_record_event(struct pxa2x0_apm_softc *sc, u_int type)
{
	static int apm_evindex;

	/* skip if no user waiting */
	if ((sc->sc_flags & SCFLAG_OPEN) == 0)
		return (1);

	apm_evindex++;
	KNOTE(&sc->sc_note, APM_EVENT_COMPOSE(type, apm_evindex));

	return (0);
}

void
filt_apmrdetach(struct knote *kn)
{
	struct pxa2x0_apm_softc *sc =
	    (struct pxa2x0_apm_softc *)kn->kn_hook;

	SLIST_REMOVE(&sc->sc_note, kn, knote, kn_selnext);
}

int
filt_apmread(struct knote *kn, long hint)
{
	/* XXX weird kqueue_scan() semantics */
	if (hint && !kn->kn_data)
		kn->kn_data = (int)hint;

	return (1);
}

int
apmkqfilter(dev_t dev, struct knote *kn)
{
	struct pxa2x0_apm_softc *sc;

	/* apm0 only */
	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
		return (ENXIO);

	switch (kn->kn_filter) {
	case EVFILT_READ:
		kn->kn_fop = &apmread_filtops;
		break;
	default:
		return (EINVAL);
	}

	kn->kn_hook = (caddr_t)sc;
	SLIST_INSERT_HEAD(&sc->sc_note, kn, kn_selnext);

	return (0);
}

void
pxa2x0_apm_attach_sub(struct pxa2x0_apm_softc *sc)
{

	sc->sc_iot = &pxa2x0_bs_tag;

	if (bus_space_map(sc->sc_iot, PXA2X0_POWMAN_BASE,
	    PXA2X0_POWMAN_SIZE, 0, &sc->sc_pm_ioh)) {
		printf("pxa2x0_apm_attach_sub: failed to map POWMAN\n");
		return;
	}

	rw_init(&sc->sc_lock, "apmlk");

	kthread_create_deferred(apm_thread_create, sc);

	printf("\n");

	if (bus_space_map(sc->sc_iot, PXA2X0_CLKMAN_BASE, PXA2X0_CLKMAN_SIZE,
	    0, &pxa2x0_clkman_ioh)) {
		printf("%s: failed to map CLKMAN\n", sc->sc_dev.dv_xname);
		return;
	}

	if (bus_space_map(sc->sc_iot, PXA2X0_MEMCTL_BASE, PXA2X0_MEMCTL_SIZE,
	    0, &pxa2x0_memctl_ioh)) {
		printf("%s: failed to map MEMCTL\n", sc->sc_dev.dv_xname);
		return;
	}
	sc->sc_memctl_ioh = pxa2x0_memctl_ioh;

	if (bus_space_map(sc->sc_iot, PXA2X0_GPIO_BASE, PXA2X0_GPIO_SIZE,
	    0, &pxa2x0_gpio_ioh)) {
		printf("%s: can't map GPIO\n", sc->sc_dev.dv_xname);
		return;
	}

	/* Clear all reset status flags. */
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_RCSR,
	    RCSR_GPR | RCSR_SMR | RCSR_WDR | RCSR_HWR);
}

void
pxa2x0_wakeup_config(u_int wsrc, int enable)
{
	struct pxa2x0_apm_softc *sc;
	u_int32_t prer;
	u_int32_t pfer;
	u_int32_t pkwr;

	if (apm_cd.cd_ndevs < 1 || apm_cd.cd_devs[0] == NULL)
		return;
	sc = apm_cd.cd_devs[0];

	prer = pfer = pkwr = 0;

	if ((wsrc & PXA2X0_WAKEUP_POWERON) != 0) {
		prer |= (1<<0);
		pfer |= (1<<0);
		pkwr |= (1<<12); /* XXX */
	}

	if ((wsrc & PXA2X0_WAKEUP_GPIORST) != 0)
		pfer |= (1<<1);
	if ((wsrc & PXA2X0_WAKEUP_SD) != 0)
		prer |= (1<<9);
	if ((wsrc & PXA2X0_WAKEUP_RC) != 0)
		prer |= (1<<13);
	if ((wsrc & PXA2X0_WAKEUP_SYNC) != 0)
		pkwr |= (1<<1);
	if ((wsrc & PXA2X0_WAKEUP_KEYNS0) != 0)
		prer |= (1<<12);
	if ((wsrc & PXA2X0_WAKEUP_KEYNS1) != 0)
		pkwr |= (1<<2);
	if ((wsrc & PXA2X0_WAKEUP_KEYNS2) != 0)
		pkwr |= (1<<9);
	if ((wsrc & PXA2X0_WAKEUP_KEYNS3) != 0)
		pkwr |= (1<<3);
	if ((wsrc & PXA2X0_WAKEUP_KEYNS4) != 0)
		pkwr |= (1<<4);
	if ((wsrc & PXA2X0_WAKEUP_KEYNS5) != 0)
		pkwr |= (1<<6);
	if ((wsrc & PXA2X0_WAKEUP_KEYNS6) != 0)
		pkwr |= (1<<7);
	if ((wsrc & PXA2X0_WAKEUP_CF0) != 0)
		pkwr |= (1<<11);
	if ((wsrc & PXA2X0_WAKEUP_CF1) != 0)
		pkwr |= (1<<10);
	if ((wsrc & PXA2X0_WAKEUP_USBD) != 0)
		prer |= (1<<24);

	if ((wsrc & PXA2X0_WAKEUP_LOCKSW) != 0) {
		prer |= (1<<15);
		pfer |= (1<<15);
	}

	if ((wsrc & PXA2X0_WAKEUP_JACKIN) != 0) {
		prer |= (1<<23);
		pfer |= (1<<23);
	}

	if ((wsrc & PXA2X0_WAKEUP_CHRGFULL) != 0)
		pkwr |= (1<<18);
	if ((wsrc & PXA2X0_WAKEUP_RTC) != 0)
		prer |= (1<<31);

	if (enable) {
		sc->sc_wakeon |= wsrc;
		prer |= bus_space_read_4(sc->sc_iot, sc->sc_pm_ioh,
		    POWMAN_PRER);
		pfer |= bus_space_read_4(sc->sc_iot, sc->sc_pm_ioh,
		    POWMAN_PFER);
		pkwr |= bus_space_read_4(sc->sc_iot, sc->sc_pm_ioh,
		    POWMAN_PKWR);
	} else {
		sc->sc_wakeon &= ~wsrc;
		prer = bus_space_read_4(sc->sc_iot, sc->sc_pm_ioh,
		    POWMAN_PRER) & ~prer;
		pfer = bus_space_read_4(sc->sc_iot, sc->sc_pm_ioh,
		    POWMAN_PFER) & ~pfer;
		pkwr = bus_space_read_4(sc->sc_iot, sc->sc_pm_ioh,
		    POWMAN_PKWR) & ~pkwr;
	}

	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PKWR, pkwr);
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PRER, prer);
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PFER, pfer);
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PWER,
	    prer | pfer);
}

u_int
pxa2x0_wakeup_status(void)
{
	struct pxa2x0_apm_softc *sc;
	u_int32_t rv;
	u_int	wsrc;

	if (apm_cd.cd_ndevs < 1 || apm_cd.cd_devs[0] == NULL)
		return (0);

	sc = apm_cd.cd_devs[0];
	wsrc = 0;

	rv = bus_space_read_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PEDR);
	if ((rv & (1<<0)) != 0)
		wsrc |= PXA2X0_WAKEUP_POWERON;
	if ((rv & (1<<1)) != 0)
		wsrc |= PXA2X0_WAKEUP_GPIORST;
	if ((rv & (1<<9)) != 0)
		wsrc |= PXA2X0_WAKEUP_SD;
	if ((rv & (1<<12)) != 0)
		wsrc |= PXA2X0_WAKEUP_KEYNS0;
	if ((rv & (1<<13)) != 0)
		wsrc |= PXA2X0_WAKEUP_RC;
	if ((rv & (1<<15)) != 0)
		wsrc |= PXA2X0_WAKEUP_LOCKSW;
	if ((rv & (1<<23)) != 0)
		wsrc |= PXA2X0_WAKEUP_JACKIN;
	if ((rv & (1<<24)) != 0)
		wsrc |= PXA2X0_WAKEUP_USBD;
	if ((rv & (1<<31)) != 0)
		wsrc |= PXA2X0_WAKEUP_RTC;

	rv = bus_space_read_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PKSR);
	if ((rv & (1<<1)) != 0)
		wsrc |= PXA2X0_WAKEUP_SYNC;
	if ((rv & (1<<2)) != 0)
		wsrc |= PXA2X0_WAKEUP_KEYNS1;
	if ((rv & (1<<9)) != 0)
		wsrc |= PXA2X0_WAKEUP_KEYNS2;
	if ((rv & (1<<3)) != 0)
		wsrc |= PXA2X0_WAKEUP_KEYNS3;
	if ((rv & (1<<4)) != 0)
		wsrc |= PXA2X0_WAKEUP_KEYNS4;
	if ((rv & (1<<6)) != 0)
		wsrc |= PXA2X0_WAKEUP_KEYNS5;
	if ((rv & (1<<7)) != 0)
		wsrc |= PXA2X0_WAKEUP_KEYNS6;
	if ((rv & (1<<10)) != 0)
		wsrc |= PXA2X0_WAKEUP_CF1;
	if ((rv & (1<<11)) != 0)
		wsrc |= PXA2X0_WAKEUP_CF0;
	if ((rv & (1<<12)) != 0)
		wsrc |= PXA2X0_WAKEUP_POWERON;
	if ((rv & (1<<18)) != 0)
		wsrc |= PXA2X0_WAKEUP_CHRGFULL;

	return (wsrc);
}

struct pxa2x0_sleep_data {
	/* OS timer registers */
	u_int32_t sd_osmr0, sd_osmr1, sd_osmr2, sd_osmr3;
	u_int32_t sd_oscr0;
	u_int32_t sd_osmr4, sd_osmr5;
	u_int32_t sd_oscr4;
	u_int32_t sd_omcr4, sd_omcr5;
	u_int32_t sd_oier;
	/* GPIO registers */
	u_int32_t sd_gpdr0, sd_gpdr1, sd_gpdr2, sd_gpdr3;
	u_int32_t sd_grer0, sd_grer1, sd_grer2, sd_grer3;
	u_int32_t sd_gfer0, sd_gfer1, sd_gfer2, sd_gfer3;
	u_int32_t sd_gafr0_l, sd_gafr1_l, sd_gafr2_l, sd_gafr3_l;
	u_int32_t sd_gafr0_u, sd_gafr1_u, sd_gafr2_u, sd_gafr3_u;
	u_int32_t sd_gplr0, sd_gplr1, sd_gplr2, sd_gplr3;
	/* Interrupt controller registers */
	u_int32_t sd_iclr;
	u_int32_t sd_icmr;
	u_int32_t sd_iccr;
	/* Memory controller registers */
	u_int32_t sd_mecr;
	u_int32_t sd_mcmem0, sd_mcmem1;
	u_int32_t sd_mcatt0, sd_mcatt1;
	u_int32_t sd_mcio0, sd_mcio1;
	/* Clocks manager registers */
	u_int32_t sd_cken;
};

void
pxa2x0_apm_sleep(struct pxa2x0_apm_softc *sc)
{
	struct pxa2x0_sleep_data sd;
	bus_space_handle_t ost_ioh;
	int save;
	u_int32_t rv;

	ost_ioh = (bus_space_handle_t)0;
	if (bus_space_map(sc->sc_iot, PXA2X0_OST_BASE, PXA2X0_OST_SIZE, 0,
	    &ost_ioh)) {
		printf("pxa2x0_apm_sleep: can't map OST\n");
		goto out;
	}

	save = disable_interrupts(I32_bit|F32_bit);

	sd.sd_oscr0 = bus_space_read_4(sc->sc_iot, ost_ioh, OST_OSCR0);
	sd.sd_oscr4 = bus_space_read_4(sc->sc_iot, ost_ioh, OST_OSCR4);
	sd.sd_omcr4 = bus_space_read_4(sc->sc_iot, ost_ioh, OST_OMCR4);
	sd.sd_omcr5 = bus_space_read_4(sc->sc_iot, ost_ioh, OST_OMCR5);
	sd.sd_osmr0 = bus_space_read_4(sc->sc_iot, ost_ioh, OST_OSMR0);
	sd.sd_osmr1 = bus_space_read_4(sc->sc_iot, ost_ioh, OST_OSMR1);
	sd.sd_osmr2 = bus_space_read_4(sc->sc_iot, ost_ioh, OST_OSMR2);
	sd.sd_osmr3 = bus_space_read_4(sc->sc_iot, ost_ioh, OST_OSMR3);
	sd.sd_osmr4 = bus_space_read_4(sc->sc_iot, ost_ioh, OST_OSMR4);
	sd.sd_osmr5 = bus_space_read_4(sc->sc_iot, ost_ioh, OST_OSMR5);
	sd.sd_oier = bus_space_read_4(sc->sc_iot, ost_ioh, OST_OIER);

	/* Bring the PXA27x into 416MHz turbo mode. */
        if ((cputype & ~CPU_ID_XSCALE_COREREV_MASK) == CPU_ID_PXA27X &&
	    bus_space_read_4(sc->sc_iot, pxa2x0_clkman_ioh, CLKMAN_CCCR) !=
	    (CCCR_A | CCCR_TURBO_X2 | CCCR_RUN_X16)) {
#if 0
		pxa27x_cpu_speed_high();
#else
#define CLKCFG_T		(1<<0)	/* turbo */
#define CLKCFG_F		(1<<1)	/* frequency change */
#define CLKCFG_B		(1<<3)	/* fast-bus */
		pxa27x_frequency_change(CCCR_A | CCCR_TURBO_X2 |
		    CCCR_RUN_X16, CLKCFG_B | CLKCFG_F | CLKCFG_T,
		    &pxa2x0_memcfg);
#endif
		delay(500000); /* XXX */
	}

suspend_again:
	/* Clear wake-up status. */
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PEDR,
	    0xffffffff);
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PKSR,
	    0xffffffff);

	/* XXX control battery charging in sleep mode. */

	/* XXX schedule RTC alarm to check the battery, or schedule
	   XXX wake-up shortly before an already programmed alarm? */

	pxa27x_run_mode();
#define MDREFR_LOW		(MDREFR_C3000 | 0x00b)
	pxa27x_fastbus_run_mode(0, MDREFR_LOW);
	delay(1);
#if 1
	pxa27x_cpu_speed_91();
#else
	pxa27x_frequency_change(CCCR_TURBO_X1 | CCCR_RUN_X7, CLKCFG_F,
	    &pxa2x0_memcfg);
#endif
	pxa2x0_pi2c_setvoltage(sc->sc_iot, sc->sc_pm_ioh, PI2C_VOLTAGE_LOW);

	sd.sd_gpdr0 = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPDR0);
	sd.sd_gpdr1 = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPDR1);
	sd.sd_gpdr2 = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPDR2);
	sd.sd_gpdr3 = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPDR3);

	sd.sd_grer0 = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GRER0);
	sd.sd_grer1 = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GRER1);
	sd.sd_grer2 = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GRER2);
	sd.sd_grer3 = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GRER3);

	sd.sd_gfer0 = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GFER0);
	sd.sd_gfer1 = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GFER1);
	sd.sd_gfer2 = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GFER2);
	sd.sd_gfer3 = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GFER3);

	sd.sd_gafr0_l = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GAFR0_L);
	sd.sd_gafr1_l = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GAFR1_L);
	sd.sd_gafr2_l = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GAFR2_L);
	sd.sd_gafr3_l = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GAFR3_L);

	sd.sd_gafr0_u = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GAFR0_U);
	sd.sd_gafr1_u = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GAFR1_U);
	sd.sd_gafr2_u = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GAFR2_U);
	sd.sd_gafr3_u = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GAFR3_U);

	sd.sd_gplr0 = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPLR0);
	sd.sd_gplr1 = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPLR1);
	sd.sd_gplr2 = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPLR2);
	sd.sd_gplr3 = bus_space_read_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPLR3);

	sd.sd_iclr = read_icu(INTCTL_ICLR);
	sd.sd_icmr = read_icu(INTCTL_ICMR);
	sd.sd_iccr = read_icu(INTCTL_ICCR);
	write_icu(INTCTL_ICMR, 0);

	sd.sd_mecr = bus_space_read_4(sc->sc_iot, pxa2x0_memctl_ioh,
	    MEMCTL_MECR);
	sd.sd_mcmem0 = bus_space_read_4(sc->sc_iot, pxa2x0_memctl_ioh,
	    MEMCTL_MCMEM(0));
	sd.sd_mcmem1 = bus_space_read_4(sc->sc_iot, pxa2x0_memctl_ioh,
	    MEMCTL_MCMEM(1));
	sd.sd_mcatt0 = bus_space_read_4(sc->sc_iot, pxa2x0_memctl_ioh,
	    MEMCTL_MCATT(0));
	sd.sd_mcatt1 = bus_space_read_4(sc->sc_iot, pxa2x0_memctl_ioh,
	    MEMCTL_MCATT(1));
	sd.sd_mcio0 = bus_space_read_4(sc->sc_iot, pxa2x0_memctl_ioh,
	    MEMCTL_MCIO(0));
	sd.sd_mcio1 = bus_space_read_4(sc->sc_iot, pxa2x0_memctl_ioh,
	    MEMCTL_MCIO(1));

	sd.sd_cken = bus_space_read_4(sc->sc_iot, pxa2x0_clkman_ioh,
	    CLKMAN_CKEN);

	/*
	 * Stop clocks to all units except to the memory controller, and
	 * to the keypad controller if it is enabled as a wake-up source.
	 */
	rv = CKEN_MEM;
	if ((sc->sc_wakeon & PXA2X0_WAKEUP_KEYNS_ALL) != 0)
		rv |= CKEN_KEY;
	bus_space_write_4(sc->sc_iot, pxa2x0_clkman_ioh, CLKMAN_CKEN, rv);

	/* Disable nRESET_OUT. */
	rv = bus_space_read_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PSLR);
#define  PSLR_SL_ROD	(1<<20)
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PSLR,
	    rv | PSLR_SL_ROD);

	/* Clear all reset status flags. */
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_RCSR,
	    RCSR_GPR | RCSR_SMR | RCSR_WDR | RCSR_HWR);

	/* Stop 3/13MHz oscillator; do not float PCMCIA and chip-selects. */
	rv = PCFR_OPDE;
        if ((cputype & ~CPU_ID_XSCALE_COREREV_MASK) == CPU_ID_PXA27X)
		/* Enable nRESET_GPIO as a GPIO reset input. */
		rv |= PCFR_GPR_EN;
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PCFR, rv);

	/* XXX C3000 */
#define	GPIO_G0_STROBE_BIT		0x0f800000
#define	GPIO_G1_STROBE_BIT		0x00100000
#define	GPIO_G2_STROBE_BIT		0x01000000
#define	GPIO_G3_STROBE_BIT		0x00041880
#define	GPIO_KEY_STROBE0		88
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PGSR0,
	    0x00144018);
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PGSR1,
	    0x00ef0000);
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PGSR2,
	    0x0121c000);
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PGSR3,
	    0x00600000);
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PGSR0,
	    0x00144018 & ~GPIO_G0_STROBE_BIT);
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PGSR1,
	    0x00ef0000 & ~GPIO_G1_STROBE_BIT);
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PGSR2,
	    0x0121c000 & ~GPIO_G2_STROBE_BIT);
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PGSR3,
	    0x00600000 & ~GPIO_G3_STROBE_BIT);
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PGSR2,
	    (0x0121c000 & ~GPIO_G2_STROBE_BIT) |
	    GPIO_BIT(GPIO_KEY_STROBE0));

	/* C3000 */
#define GPIO_EXT_BUS_READY	18
	pxa2x0_gpio_set_function(GPIO_EXT_BUS_READY, GPIO_SET | GPIO_OUT);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPDR0, 0xd01c4418);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPDR1, 0xfcefbd21);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPDR2, 0x13a5ffff);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPDR3, 0x01e3e10c);

	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PSPR,
	    (u_int32_t)&pxa2x0_cpu_resume - 0xc0200000 + 0xa0200000);

	pxa2x0_cpu_suspend();

	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PSPR, 0);

	pxa2x0_clkman_config(CKEN_SSP|CKEN_PWM0|CKEN_PWM1, 1);
	pxa2x0_clkman_config(CKEN_KEY, 0);

#if 1
	/* Clear all GPIO interrupt sources. */
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GEDR0, 0xffffffff);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GEDR1, 0xffffffff);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GEDR2, 0xffffffff);
#endif

	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPDR0, sd.sd_gpdr0);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPDR1, sd.sd_gpdr1);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPDR2, sd.sd_gpdr2);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GRER0, sd.sd_grer0);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GRER1, sd.sd_grer1);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GRER2, sd.sd_grer2);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GFER0, sd.sd_gfer0);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GFER1, sd.sd_gfer1);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GFER2, sd.sd_gfer2);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GAFR0_L, sd.sd_gafr0_l);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GAFR1_L, sd.sd_gafr1_l);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GAFR2_L, sd.sd_gafr2_l);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GAFR0_U, sd.sd_gafr0_u);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GAFR1_U, sd.sd_gafr1_u);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GAFR2_U, sd.sd_gafr2_u);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPSR0, sd.sd_gplr0 &
	    sd.sd_gpdr0);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPSR1, sd.sd_gplr1 &
	    sd.sd_gpdr1);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPSR2, sd.sd_gplr2 &
	    sd.sd_gpdr2);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPCR0, ~sd.sd_gplr0 &
	    sd.sd_gpdr0);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPCR1, ~sd.sd_gplr1 &
	    sd.sd_gpdr1);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPCR2, ~sd.sd_gplr2 &
	    sd.sd_gpdr2);

	/* PXA27x */
#if 0
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GEDR3, 0xffffffff);
#endif
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPDR3, sd.sd_gpdr3);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GRER3, sd.sd_grer3);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GFER3, sd.sd_gfer3);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GAFR3_L, sd.sd_gafr3_l);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GAFR3_U, sd.sd_gafr3_u);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPSR3, sd.sd_gplr3 &
	    sd.sd_gpdr3);
	bus_space_write_4(sc->sc_iot, pxa2x0_gpio_ioh, GPIO_GPCR3, ~sd.sd_gplr3 &
	    sd.sd_gpdr3);

	bus_space_write_4(sc->sc_iot, pxa2x0_memctl_ioh, MEMCTL_MECR,
	    sd.sd_mecr);
	bus_space_write_4(sc->sc_iot, pxa2x0_memctl_ioh, MEMCTL_MCMEM(0),
	    sd.sd_mcmem0);
	bus_space_write_4(sc->sc_iot, pxa2x0_memctl_ioh, MEMCTL_MCMEM(1),
	    sd.sd_mcmem1);
	bus_space_write_4(sc->sc_iot, pxa2x0_memctl_ioh, MEMCTL_MCATT(0),
	    sd.sd_mcatt0);
	bus_space_write_4(sc->sc_iot, pxa2x0_memctl_ioh, MEMCTL_MCATT(1),
	    sd.sd_mcatt1);
	bus_space_write_4(sc->sc_iot, pxa2x0_memctl_ioh, MEMCTL_MCIO(0),
	    sd.sd_mcio0);
	bus_space_write_4(sc->sc_iot, pxa2x0_memctl_ioh, MEMCTL_MCIO(1),
	    sd.sd_mcio1);

	bus_space_write_4(sc->sc_iot, pxa2x0_clkman_ioh, CLKMAN_CKEN,
	    sd.sd_cken);

	write_icu(INTCTL_ICLR, sd.sd_iclr);
	write_icu(INTCTL_ICCR, sd.sd_iccr);
	write_icu(INTCTL_ICMR, sd.sd_icmr);

	if ((read_icu(INTCTL_ICIP) & 0x1) != 0)
		bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PEDR, 0x1);

	bus_space_write_4(sc->sc_iot, ost_ioh, OST_OSMR0, sd.sd_osmr0);
	bus_space_write_4(sc->sc_iot, ost_ioh, OST_OSMR1, sd.sd_osmr1);
	bus_space_write_4(sc->sc_iot, ost_ioh, OST_OSMR2, sd.sd_osmr2);
	bus_space_write_4(sc->sc_iot, ost_ioh, OST_OSMR3, sd.sd_osmr3);
	bus_space_write_4(sc->sc_iot, ost_ioh, OST_OSMR4, sd.sd_osmr4);
	bus_space_write_4(sc->sc_iot, ost_ioh, OST_OSMR5, sd.sd_osmr5);
	bus_space_write_4(sc->sc_iot, ost_ioh, OST_OMCR4, sd.sd_omcr4);
	bus_space_write_4(sc->sc_iot, ost_ioh, OST_OMCR5, sd.sd_omcr5);
	bus_space_write_4(sc->sc_iot, ost_ioh, OST_OSCR0, sd.sd_oscr0);
	bus_space_write_4(sc->sc_iot, ost_ioh, OST_OSCR4, sd.sd_oscr4);
	bus_space_write_4(sc->sc_iot, ost_ioh, OST_OIER, sd.sd_oier);

	pxa2x0_pi2c_setvoltage(sc->sc_iot, sc->sc_pm_ioh, PI2C_VOLTAGE_HIGH);

	/* Change to 208MHz run mode with fast-bus still disabled. */
	pxa27x_frequency_change(CCCR_A | CCCR_TURBO_X2 | CCCR_RUN_X16,
	    CLKCFG_F, &pxa2x0_memcfg);
	delay(1); /* XXX is the delay long enough, and necessary at all? */
	pxa27x_fastbus_run_mode(1, pxa2x0_memcfg.mdrefr_high);

	/* Change to 416MHz turbo mode with fast-bus enabled. */
	pxa27x_frequency_change(CCCR_A | CCCR_TURBO_X2 | CCCR_RUN_X16,
	    CLKCFG_B | CLKCFG_F | CLKCFG_T, &pxa2x0_memcfg);

	if (sc->sc_resume != NULL) {
		if (!sc->sc_resume(sc))
			goto suspend_again;
	}

	/*
	 * Allow immediate entry into deep-sleep mode if power fails.
	 * Resume from immediate deep-sleep is not implemented yet.
	 */
	bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PMCR, 0);

	restore_interrupts(save);

	pxa2x0_setperf(perflevel);

 out:
	if (ost_ioh != (bus_space_handle_t)0)
		bus_space_unmap(sc->sc_iot, ost_ioh, PXA2X0_OST_SIZE);
}

void
pxa2x0_pi2c_open(bus_space_tag_t iot, bus_space_handle_t ioh)
{
	u_int32_t rv;

	/* Enable the I2C unit, and disable automatic voltage change. */
	rv = bus_space_read_4(iot, ioh, POWMAN_PCFR);
	bus_space_write_4(iot, ioh, POWMAN_PCFR, rv | PCFR_PI2C_EN);
	rv = bus_space_read_4(iot, ioh, POWMAN_PCFR);
	bus_space_write_4(iot, ioh, POWMAN_PCFR, rv & ~PCFR_FVC);
	delay(1);

	/* Enable the clock to the power manager I2C unit. */
	pxa2x0_clkman_config(CKEN_PI2C, 1);
	delay(1);
}

void
pxa2x0_pi2c_close(bus_space_tag_t iot, bus_space_handle_t ioh)
{
	u_int32_t rv;

	bus_space_write_4(iot, ioh, POWMAN_PICR, PICR_UR);
	bus_space_write_4(iot, ioh, POWMAN_PISAR, 0);
	delay(1);

	/* Disable the clock to the power manager I2C unit. */
	pxa2x0_clkman_config(CKEN_PI2C, 0);
	delay(1);

	/* Disable the I2C unit, and disable automatic voltage change. */
	rv = bus_space_read_4(iot, ioh, POWMAN_PCFR);
	bus_space_write_4(iot, ioh, POWMAN_PCFR,
	    rv & ~(PCFR_PI2C_EN | PCFR_FVC));
	delay(1);
}

int
pxa2x0_pi2c_read(bus_space_tag_t iot, bus_space_handle_t ioh,
    u_char slave, u_char *valuep)
{
	u_int32_t rv;
	int timeout;
	int tries = PI2C_RETRY_COUNT;

retry:

	bus_space_write_4(iot, ioh, POWMAN_PICR, PICR_UR);
	bus_space_write_4(iot, ioh, POWMAN_PISAR, 0x00);
	delay(1);
	bus_space_write_4(iot, ioh, POWMAN_PICR, PICR_IUE | PICR_SCLE);

	/* Write slave device address. */
	bus_space_write_4(iot, ioh, POWMAN_PIDBR, (slave<<1) | 0x1);
	rv = bus_space_read_4(iot, ioh, POWMAN_PICR);
	bus_space_write_4(iot, ioh, POWMAN_PICR, rv | PICR_START);
	rv = bus_space_read_4(iot, ioh, POWMAN_PICR);
	bus_space_write_4(iot, ioh, POWMAN_PICR, rv & ~PICR_STOP);
	rv = bus_space_read_4(iot, ioh, POWMAN_PICR);
	bus_space_write_4(iot, ioh, POWMAN_PICR, rv | PICR_TB);

	timeout = 10000;
	while ((bus_space_read_4(iot, ioh, POWMAN_PISR) & PISR_ITE) == 0) {
		if (timeout-- == 0) {
			bus_space_write_4(iot, ioh, POWMAN_PISR, PISR_ITE);
			goto err;
		}
		delay(1);
	}

	bus_space_write_4(iot, ioh, POWMAN_PISR, PISR_ITE);

	rv = bus_space_read_4(iot, ioh, POWMAN_PICR);
	bus_space_write_4(iot, ioh, POWMAN_PICR, rv & ~PICR_START);

	/* Read data value. */
	rv = bus_space_read_4(iot, ioh, POWMAN_PICR);
	bus_space_write_4(iot, ioh, POWMAN_PICR, rv |
	    (PICR_STOP | PICR_ACKNAK));
	rv = bus_space_read_4(iot, ioh, POWMAN_PICR);
	bus_space_write_4(iot, ioh, POWMAN_PICR, rv | PICR_TB);

	timeout = 10000;
	while ((bus_space_read_4(iot, ioh, POWMAN_PISR) & PISR_IRF) == 0) {
		if (timeout-- == 0) {
			bus_space_write_4(iot, ioh, POWMAN_PISR, PISR_IRF);
			goto err;
		}
		delay(1);
	}

	bus_space_write_4(iot, ioh, POWMAN_PISR, PISR_IRF);
	rv = bus_space_read_4(iot, ioh, POWMAN_PIDBR);
	*valuep = (u_char)rv;
	rv = bus_space_read_4(iot, ioh, POWMAN_PICR);
	bus_space_write_4(iot, ioh, POWMAN_PICR, rv &
	    ~(PICR_STOP | PICR_ACKNAK));

	return (0);
err:
	if (tries-- >= 0)
		goto retry;

	bus_space_write_4(iot, ioh, POWMAN_PICR, PICR_UR);
	bus_space_write_4(iot, ioh, POWMAN_PISAR, 0x00);
	bus_space_write_4(iot, ioh, POWMAN_PICR, PICR_IUE | PICR_SCLE);

	return (-EIO);
}

int
pxa2x0_pi2c_write(bus_space_tag_t iot, bus_space_handle_t ioh,
    u_char slave, u_char value)
{
	u_int32_t rv;
	int timeout;
	int tries = PI2C_RETRY_COUNT;

retry:

	bus_space_write_4(iot, ioh, POWMAN_PICR, PICR_UR);
	bus_space_write_4(iot, ioh, POWMAN_PISAR, 0x00);
	delay(1);
	bus_space_write_4(iot, ioh, POWMAN_PICR, PICR_IUE | PICR_SCLE);

	/* Write slave device address. */
	bus_space_write_4(iot, ioh, POWMAN_PIDBR, (slave<<1));
	rv = bus_space_read_4(iot, ioh, POWMAN_PICR);
	bus_space_write_4(iot, ioh, POWMAN_PICR, rv | PICR_START);
	rv = bus_space_read_4(iot, ioh, POWMAN_PICR);
	bus_space_write_4(iot, ioh, POWMAN_PICR, rv & ~PICR_STOP);
	rv = bus_space_read_4(iot, ioh, POWMAN_PICR);
	bus_space_write_4(iot, ioh, POWMAN_PICR, rv | PICR_TB);

	timeout = 10000;
	while ((bus_space_read_4(iot, ioh, POWMAN_PISR) & PISR_ITE) == 0) {
		if (timeout-- == 0) {
			bus_space_write_4(iot, ioh, POWMAN_PISR, PISR_ITE);
			goto err;
		}
		delay(1);
	}
	if ((bus_space_read_4(iot, ioh, POWMAN_PISR) & PISR_ACKNAK) != 0)
		goto err;
	bus_space_write_4(iot, ioh, POWMAN_PISR, PISR_ITE);

	/* Write data. */
	rv = bus_space_read_4(iot, ioh, POWMAN_PICR);
	bus_space_write_4(iot, ioh, POWMAN_PICR, rv & ~PICR_START);
	rv = bus_space_read_4(iot, ioh, POWMAN_PICR);
	bus_space_write_4(iot, ioh, POWMAN_PICR, rv | PICR_STOP);
	bus_space_write_4(iot, ioh, POWMAN_PIDBR, value);
	rv = bus_space_read_4(iot, ioh, POWMAN_PICR);
	bus_space_write_4(iot, ioh, POWMAN_PICR, rv | PICR_TB);

	timeout = 10000;
	while ((bus_space_read_4(iot, ioh, POWMAN_PISR) & PISR_ITE) == 0) {
		if (timeout-- == 0) {
#if 0
			bus_space_write_4(iot, ioh, POWMAN_PISR, PISR_ITE);
#endif
			goto err;
		}
		delay(1);
	}
	if ((bus_space_read_4(iot, ioh, POWMAN_PISR) & PISR_ACKNAK) != 0)
		goto err;
	bus_space_write_4(iot, ioh, POWMAN_PISR, PISR_ITE);

	rv = bus_space_read_4(iot, ioh, POWMAN_PICR);
	bus_space_write_4(iot, ioh, POWMAN_PICR, rv & ~PICR_STOP);

	return (0);
err:
	bus_space_write_4(iot, ioh, POWMAN_PISR, PISR_ITE);
	if (tries-- >= 0)
		goto retry;

	bus_space_write_4(iot, ioh, POWMAN_PICR, PICR_UR);
	bus_space_write_4(iot, ioh, POWMAN_PISAR, 0x00);
	bus_space_write_4(iot, ioh, POWMAN_PICR, PICR_IUE | PICR_SCLE);

	return (-EIO);
}

int
pxa2x0_pi2c_getvoltage(bus_space_tag_t iot, bus_space_handle_t ioh,
    u_char *valuep)
{
	int res;

	pxa2x0_pi2c_open(iot, ioh);
	res = pxa2x0_pi2c_read(iot, ioh, 0x0c, valuep);
	pxa2x0_pi2c_close(iot, ioh);
	return (res);
}

int
pxa2x0_pi2c_setvoltage(bus_space_tag_t iot, bus_space_handle_t ioh,
    u_char value)
{
	int res;

	pxa2x0_pi2c_open(iot, ioh);
	res = pxa2x0_pi2c_write(iot, ioh, 0x0c, value);
	pxa2x0_pi2c_close(iot, ioh);
	return (res);
}

#if 0
void
pxa2x0_pi2c_print(struct pxa2x0_apm_softc *sc)
{
	u_char value = 0;

	(void)pxa2x0_pi2c_getvoltage(sc->sc_iot, sc->sc_pm_ioh, &value);
	printf("xscale core voltage: %s\n", value == PI2C_VOLTAGE_HIGH ?
	    "high" : (value == PI2C_VOLTAGE_LOW ? "low" : "unknown"));
}
#endif

struct {
	int maxspeed;
	int numspeeds;
	int hz [6];
	int rate [6]; /* could this be simplfied by not having 100% in table? */
}
speedtables[] = {
	{ 91, 1, { 91 }, { 100 }},
	{ 208, 2, { 91, 208}, {50, 100}},
	{ 416, 3, { 91, 208, 416}, {25, 50, 100}},
	{ 520, 4, { 91, 208, 416, 520}, {18, 40 ,80, 100}},
	{ 624, 5, { 91, 208, 416, 520, 624}, {15, 34, 67, 82, 100}},
	{ 0 }
};
int xscale_maxspeed = 416; /* XXX */

int speed_to_freq(int speed);

int
speed_to_freq(int speed)
{
	int i, j;
	int newspeed = 0;
	int numspeeds;
	for (i = 0; speedtables[i].maxspeed != 0; i++) {
		if (speedtables[i].maxspeed != xscale_maxspeed)
			continue;

		if (speed <= speedtables[i].rate[0]) {
			return speedtables[i].hz[0];

		}
		numspeeds = speedtables[i].numspeeds;
		if (speed == speedtables[i].rate[numspeeds-1]) {
			return speedtables[i].hz[numspeeds-1];
		}
		for (j = 1; j < numspeeds; j++) {
			if (speed < speedtables[i].rate[j]) {
				return speedtables[i].hz[j-1];
			}
		}
	}
	return newspeed;
}

void
pxa2x0_setperf(int speed)
{
	struct pxa2x0_apm_softc *sc;
	int s;
	int newfreq;

	sc = apm_cd.cd_devs[0];

	newfreq = speed_to_freq(speed);

	if (newfreq == 0) {
		printf("bogus new frequency 0 for rate %d maxclock %d\n",
		    speed, xscale_maxspeed);
	}

	DPRINTF(("setperf speed %d newfreq %d, maxfreq %d\n",
	    speed, newfreq, xscale_maxspeed));

	s = disable_interrupts(I32_bit|F32_bit);

	if (newfreq == 91) {
		if (freq > 91) {
			pxa27x_run_mode();
			pxa27x_fastbus_run_mode(0, MDREFR_LOW);
			pxa27x_cpu_speed_91();
			pxa2x0_pi2c_setvoltage(sc->sc_iot, sc->sc_pm_ioh,
			    PI2C_VOLTAGE_LOW);
			freq = 91;
		}
	} else if (newfreq == 208) {
		if (freq < 208)
			pxa2x0_pi2c_setvoltage(sc->sc_iot, sc->sc_pm_ioh,
			    PI2C_VOLTAGE_HIGH);
		if (freq != 208) {
			pxa27x_frequency_change(CCCR_A | CCCR_TURBO_X2 |
			    CCCR_RUN_X16, CLKCFG_F, &pxa2x0_memcfg);
			pxa27x_fastbus_run_mode(1, pxa2x0_memcfg.mdrefr_high);
			freq = 208;
		}
	} else if (newfreq == 416) {
		if (freq < 208) {
			pxa2x0_pi2c_setvoltage(sc->sc_iot, sc->sc_pm_ioh,
			    PI2C_VOLTAGE_HIGH);
			pxa27x_frequency_change(CCCR_A | CCCR_TURBO_X2 |
			    CCCR_RUN_X16, CLKCFG_F, &pxa2x0_memcfg);
			pxa27x_fastbus_run_mode(1, pxa2x0_memcfg.mdrefr_high);
		}
		if (freq != 416) {
			pxa27x_frequency_change(CCCR_A | CCCR_TURBO_X2 |
			    CCCR_RUN_X16, CLKCFG_B | CLKCFG_F | CLKCFG_T,
			    &pxa2x0_memcfg);
			freq = 416;
		}
	} else if (newfreq == 520) {
		if (freq < 208) {
			pxa2x0_pi2c_setvoltage(sc->sc_iot, sc->sc_pm_ioh,
			    PI2C_VOLTAGE_HIGH);
			pxa27x_frequency_change(CCCR_A | CCCR_TURBO_X2 |
			    CCCR_RUN_X16, CLKCFG_F, &pxa2x0_memcfg);
			pxa27x_fastbus_run_mode(1, pxa2x0_memcfg.mdrefr_high);
		}
		if (freq != 520) {
			pxa27x_frequency_change(CCCR_A | CCCR_TURBO_X25 |
			    CCCR_RUN_X16, CLKCFG_B | CLKCFG_F | CLKCFG_T,
			    &pxa2x0_memcfg);
			freq = 520;
		}
	} else if (newfreq == 624) {
		if (freq < 208) {
			pxa2x0_pi2c_setvoltage(sc->sc_iot, sc->sc_pm_ioh,
			    PI2C_VOLTAGE_HIGH);
			pxa27x_frequency_change(CCCR_A | CCCR_TURBO_X2 |
			    CCCR_RUN_X16, CLKCFG_F, &pxa2x0_memcfg);
			pxa27x_fastbus_run_mode(1, pxa2x0_memcfg.mdrefr_high);
		}
		if (freq != 624) {
			pxa27x_frequency_change(CCCR_A | CCCR_TURBO_X3 |
			    CCCR_RUN_X16, CLKCFG_B | CLKCFG_F | CLKCFG_T,
			    &pxa2x0_memcfg);
			freq = 624;
		}
	}

	restore_interrupts(s);
}

int
pxa2x0_cpuspeed(int *freqp)
{
	*freqp = freq;
	return 0;
}

void pxa2x0_maxspeed(int *speedp);

void
pxa2x0_maxspeed(int *speedp)
{
	/* XXX assumes a pxa270 */

	if (*speedp < 207) {
		*speedp = 91;
	} else if (*speedp < 415) {
		*speedp = 208;
	} else if (*speedp < 519) {
		*speedp = 416;
	} else if (*speedp < 624) {
		*speedp = 520;
#if 0
	} else if (*speedp < 651) {
		*speedp = 624;
#endif
	} else {
		*speedp = 520; /* hope this is safe. */
	}
	xscale_maxspeed = *speedp;
	pxa2x0_setperf(perflevel);
}


code:
/*	$OpenBSD: pxa2x0_apm.h,v 1.8 2009/03/27 16:01:37 oga Exp $	*/

/*
 * Copyright (c) 2005 Uwe Stuehler <uwe@bsdx.de>
 *
 * 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.
 */

#ifndef _PXA2X0_APM_H_
#define _PXA2X0_APM_H_

#include <sys/event.h>

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

struct pxa2x0_apm_softc {
	struct	device sc_dev;
	struct	proc *sc_thread;
	struct	rwlock sc_lock;
	struct	klist sc_note;
	int	sc_flags;
	int	sc_wakeon;	/* enabled wakeup sources */
	int	sc_batt_life;
	bus_space_tag_t sc_iot;
	bus_space_handle_t sc_pm_ioh;
	bus_space_handle_t sc_memctl_ioh;
	int	(*sc_get_event)(struct pxa2x0_apm_softc *, u_int *);
	void	(*sc_power_info)(struct pxa2x0_apm_softc *,
	    struct apm_power_info *);
	void	(*sc_suspend)(struct pxa2x0_apm_softc *);
	int	(*sc_resume)(struct pxa2x0_apm_softc *);
};

void	pxa2x0_apm_attach_sub(struct pxa2x0_apm_softc *);
void	pxa2x0_apm_sleep(struct pxa2x0_apm_softc *);

#define PXA2X0_WAKEUP_POWERON	(1<<0)
#define PXA2X0_WAKEUP_GPIORST	(1<<1)
#define PXA2X0_WAKEUP_SD	(1<<2)
#define PXA2X0_WAKEUP_RC	(1<<3)
#define PXA2X0_WAKEUP_SYNC	(1<<4)
#define PXA2X0_WAKEUP_KEYNS0	(1<<5)
#define PXA2X0_WAKEUP_KEYNS1	(1<<6)
#define PXA2X0_WAKEUP_KEYNS2	(1<<7)
#define PXA2X0_WAKEUP_KEYNS3	(1<<8)
#define PXA2X0_WAKEUP_KEYNS4	(1<<9)
#define PXA2X0_WAKEUP_KEYNS5	(1<<10)
#define PXA2X0_WAKEUP_KEYNS6	(1<<11)
#define PXA2X0_WAKEUP_CF0	(1<<12)
#define PXA2X0_WAKEUP_CF1	(1<<13)
#define PXA2X0_WAKEUP_USBD	(1<<14)
#define PXA2X0_WAKEUP_LOCKSW	(1<<15)
#define PXA2X0_WAKEUP_JACKIN	(1<<16)
#define PXA2X0_WAKEUP_CHRGFULL	(1<<17)
#define PXA2X0_WAKEUP_RTC	(1<<18)

#define PXA2X0_WAKEUP_KEYNS_ALL	(PXA2X0_WAKEUP_KEYNS0|			\
    PXA2X0_WAKEUP_KEYNS1|PXA2X0_WAKEUP_KEYNS2|PXA2X0_WAKEUP_KEYNS3|	\
    PXA2X0_WAKEUP_KEYNS4|PXA2X0_WAKEUP_KEYNS5|PXA2X0_WAKEUP_KEYNS6)

#define PXA2X0_WAKEUP_CF_ALL	(PXA2X0_WAKEUP_CF0|PXA2X0_WAKEUP_CF1)

#define PXA2X0_WAKEUP_ALL	(PXA2X0_WAKEUP_POWERON|			\
    PXA2X0_WAKEUP_GPIORST|PXA2X0_WAKEUP_SD|PXA2X0_WAKEUP_RC|		\
    PXA2X0_WAKEUP_SYNC|PXA2X0_WAKEUP_KEYNS_ALL|PXA2X0_WAKEUP_CF_ALL|	\
    PXA2X0_WAKEUP_USBD|PXA2X0_WAKEUP_LOCKSW|PXA2X0_WAKEUP_JACKIN|	\
    PXA2X0_WAKEUP_CHRGFULL|PXA2X0_WAKEUP_RTC)

void	pxa2x0_wakeup_config(u_int, int);
u_int	pxa2x0_wakeup_status(void);

#endif

license: unknown


code:
/*	$OpenBSD: pxa2x0_apm_asm.S,v 1.4 2007/11/02 05:18:25 miod Exp $	*/

/*
 * Copyright (c) 2005 Uwe Stuehler <uwe@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 <machine/asm.h>
#include <machine/cpu.h>

#include <arch/arm/xscale/pxa2x0reg.h>
#include <arch/arm/sa11x0/sa11x0_reg.h>

/* XXX replace with values defined elsewhere. */
#define DCACHE_CACHELINECOUNT	1024
#define CACHELINESIZE		32
#define	DCACHE_SIZE		(CACHELINESIZE * DCACHE_CACHELINECOUNT)

/* cp14 register 6 */
#define CLKCFG_T		(1<<0)	/* turbo */
#define CLKCFG_F		(1<<1)	/* frequency change */
#define CLKCFG_HT		(1<<2)	/* half-turbo */
#define CLKCFG_B		(1<<3)	/* fast-bus */

/* cp14 register 7 */
#define PWRMODE_NORMAL		(0<<0)
#define PWRMODE_IDLE		(1<<0)
#define PWRMODE_STANDBY		(2<<0)
#define PWRMODE_SLEEP		(3<<0)
#define PWRMODE_DEEP_SLEEP	(7<<0)

/* XXX */
#define MDREFR_C3000		(MDREFR_K0DB2|MDREFR_E1PIN|MDREFR_K1RUN|\
				 MDREFR_K1DB2|MDREFR_K2DB2|MDREFR_APD)
#define MDREFR_DRI_91MHZ	(0x13<<0)
#define MDREFR_HIGH		(MDREFR_C3000 | 0x030)
#define MDREFR_LOW		(MDREFR_C3000 | 0x00b)
#define MDREFR_SPEED_91		(MDREFR_C3000 | MDREFR_DRI_91MHZ)
#define MDREFR_SPEED_LOW	(MDREFR_C3000 | 0x017)
#define MSC0_HIGH \
		( 7 << MSC_RRR_SHIFT << 16) | \
		(15 << MSC_RDN_SHIFT << 16) | \
		(15 << MSC_RDF_SHIFT << 16) | \
		(MSC_RT_NONBURST     << 16) | \
		( 2 << MSC_RRR_SHIFT)       | \
		(13 << MSC_RDN_SHIFT)       | \
		(13 << MSC_RDF_SHIFT)       | \
		MSC_RBW	/* PXA271 */        | \
		MSC_RT_NONBURST
#define MSC1_HIGH \
		( 7 << MSC_RRR_SHIFT << 16) | \
		(15 << MSC_RDN_SHIFT << 16) | \
		(15 << MSC_RDF_SHIFT << 16) | \
		(MSC_RT_VLIO         << 16) | \
		( 3 << MSC_RRR_SHIFT)       | \
		( 4 << MSC_RDN_SHIFT)       | \
		(13 << MSC_RDF_SHIFT)       | \
		MSC_RT_VLIO
#define MSC2_HIGH \
		( 7 << MSC_RRR_SHIFT << 16) | \
		(15 << MSC_RDN_SHIFT << 16) | \
		(15 << MSC_RDF_SHIFT << 16) | \
		(MSC_RT_NONBURST     << 16) | \
		( 3 << MSC_RRR_SHIFT)       | \
		( 4 << MSC_RDN_SHIFT)       | \
		(13 << MSC_RDF_SHIFT)       | \
		MSC_RT_VLIO
#define MSC0_LOW \
		( 7 << MSC_RRR_SHIFT << 16) | \
		(15 << MSC_RDN_SHIFT << 16) | \
		(15 << MSC_RDF_SHIFT << 16) | \
		(MSC_RT_NONBURST     << 16) | \
		( 1 << MSC_RRR_SHIFT)       | \
		( 8 << MSC_RDN_SHIFT)       | \
		( 8 << MSC_RDF_SHIFT)       | \
		MSC_RBW	/* PXA271 */        | \
		MSC_RT_NONBURST
#define MSC1_LOW \
		( 7 << MSC_RRR_SHIFT << 16) | \
		(15 << MSC_RDN_SHIFT << 16) | \
		(15 << MSC_RDF_SHIFT << 16) | \
		(MSC_RT_VLIO         << 16) | \
		( 1 << MSC_RRR_SHIFT)       | \
		( 2 << MSC_RDN_SHIFT)       | \
		( 6 << MSC_RDF_SHIFT)       | \
		MSC_RT_VLIO
#define MSC2_LOW \
		( 7 << MSC_RRR_SHIFT << 16) | \
		(15 << MSC_RDN_SHIFT << 16) | \
		(15 << MSC_RDF_SHIFT << 16) | \
		(MSC_RT_NONBURST     << 16) | \
		( 1 << MSC_RRR_SHIFT)       | \
		( 2 << MSC_RDN_SHIFT)       | \
		( 6 << MSC_RDF_SHIFT)       | \
		MSC_RT_VLIO

	.text
	.global	_C_LABEL(vector_page)
	.global	_C_LABEL(xscale_cache_clean_addr)
	.global	_C_LABEL(pxa2x0_clkman_ioh)
	.global	_C_LABEL(pxa2x0_memctl_ioh)

.Lvector_page:
	.word	_C_LABEL(vector_page)
.Lxscale_cache_clean_addr:
	.word	_C_LABEL(xscale_cache_clean_addr)

.Lgpioiohp:	.word	_C_LABEL(pxa2x0_gpio_ioh)
.Lclkmaniohp:	.word	_C_LABEL(pxa2x0_clkman_ioh)
.Lmemctliohp:	.word	_C_LABEL(pxa2x0_memctl_ioh)

.Lsleepdata:	.word	sleepdata
.Lsleepdata_phys: .word	sleepdata - 0xc0200000 + 0xa0200000 /* XXX */
.Lsleepdata_svc: .word	sleepdata_svc

.Lcccr_high:	.word	CCCR_A | CCCR_TURBO_X2 | CCCR_RUN_X16
.Lmdrefr_high:	.word	MDREFR_HIGH
.Lmsc0_high:	.word	MSC0_HIGH
.Lmsc1_high:	.word	MSC1_HIGH
.Lmsc2_high:	.word	MSC2_HIGH
.Lmdrefr_low:	.word	MDREFR_LOW
.Lmsc0_low:	.word	MSC0_LOW
.Lmsc1_low:	.word	MSC1_LOW
.Lmsc2_low:	.word	MSC2_LOW

/*
 * void pxa2x0_cpu_suspend(void)
 *
 * Enter sleep mode without automatic voltage change.  The core must
 * be in low power mode, and interrupts disabled.
 */
ENTRY(pxa2x0_cpu_suspend)
	stmdb	sp!, {r0-r12, lr}

	ldr	r3, .Lsleepdata		/* Point to the data area. */
	ldr	r2, =pxa2x0_cpu_resume_virt
	str	r2, [r3], #4

	mrc	p15, 0, r2, c1, c0, 0	/* Load MMU control register. */
	mov	r0,     #0xff000000
	orr	r0, r0, #0x00ff0000
	bic	r2, r2, r0		/* Clear undefined bits. */
	str	r2, [r3], #4		/* Save MMU control register. */

	mrc	p15, 0, r2, c2, c0, 0	/* Load TTB address. */
	mov	r0,     #0x00003f00
	orr	r0, r0, #0x000000ff
	bic	r2, r2, r0		/* Clear undefined bits. */
	str	r2, [r3], #4		/* Save TTB address. */

	mrc	p15, 0, r2, c3, c0, 0	/* Load domain access control. */
	str	r2, [r3], #4		/* Save domain access control. */

	mrs	r2, spsr		/* Load SVC saved CPSR. */
	str	r2, [r3], #4		/* Save SVC saved CPSR. */
	str     sp, [r3], #4		/* Save SVC stack pointer. */

	mov	r1, #(PSR_FIQ32_MODE | I32_bit | F32_bit)
	msr	cpsr, r1		/* Enter FIQ mode. */
	mrs     r2, spsr		/* Load FIQ mode saved CPSR. */
	stmia   r3!, {r2, r8-r12, sp, lr} /* Save FIQ mode registers. */

	mov	r1, #(PSR_IRQ32_MODE | I32_bit | F32_bit)
	msr	cpsr, r1		/* Enter IRQ mode. */
	mrs     r0, spsr		/* Load IRQ mode saved CPSR. */
	stmia   r3!, {r0, sp, lr}	/* Save IRQ mode registers. */

	mov	r1, #(PSR_ABT32_MODE | I32_bit | F32_bit)
	msr	cpsr, r1		/* Enter ABT mode. */
	mrs     r0, spsr		/* Load ABT mode saved CPSR. */
	stmia   r3!, {r0, sp, lr}	/* Save ABT mode registers. */

	mov	r1, #(PSR_UND32_MODE | I32_bit | F32_bit)
	msr	cpsr, r1		/* Enter UND mode. */
	mrs     r0, spsr		/* Load UND mode saved CPSR. */
	stmia   r3!, {r0, sp, lr}	/* Save UND mode registers. */

	mov	r1, #(PSR_SYS32_MODE | I32_bit | F32_bit)
	msr	cpsr, r1		/* Enter SYS mode. */
	stmia   r3!, {sp, lr}		/* Save SYS mode registers. */

	mov	r1, #(PSR_SVC32_MODE | I32_bit | F32_bit)
	msr	cpsr, r1		/* Return to SVC mode. */

	/* At this point all critical registers have been saved. */

	mov	r0, #0
	mcr	p15, 0, r0, c7, c10, 4  /* drain write buffer */

	mov     r1, #DCACHE_CACHELINECOUNT
	ldr	r2, .Lxscale_cache_clean_addr
	ldr	r0, [r2]
	/*
	 * For an explanation of the following two instructions, refer
	 * to the ``BUG ALERT'' section of the XSCALE_CACHE_CLEAN_PROLOGUE
	 * macro in arch/arm/arm/cpufunc_asm_xscale.S.
	 */
	eor	r0, r0, #(DCACHE_SIZE)
	str	r0, [r2]

cache_flush_loop:
	mrs	r2, cpsr
	orr	r2, r2, #(I32_bit|F32_bit)
	msr	cpsr_c, r2		/* disable IRQ/FIQ */

	mcr	p15, 0, r0, c7, c2, 5	/* allocate cache line */
	mcr	p15, 0, r0, c7, c6, 1	/* flush D cache single entry */

	mrs	r2, cpsr
	and	r2, r2, #~(I32_bit|F32_bit)
	msr	cpsr_c, r2		/* enable IRQ/FIQ */

	add	r0, r0, #CACHELINESIZE
	subs	r1, r1, #1
	bne	cache_flush_loop

	mov	r0, #0
	mcr	p15, 0, r0, c7, c10, 4  /* drain write buffer */

	b	1f
1:
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop

	/* Prepare to enter sleep mode. */
	mov	r1, #PWRMODE_SLEEP

	/* Prepare to put SDRAM into self-refresh mode. */
	ldr	r4, .Lmemctliohp
	ldr	r4, [r4]
	add	r4, r4, #MEMCTL_MDREFR
	ldr	r5, [r4]
	orr	r5, r5, #MDREFR_SLFRSH

	/* XXX prepare pointer to physical address 0, but for whom? */
	ldr	r2, .Lvector_page

	/*
	 * Execute the rest of this routine from cache.  The needed values
	 * are now in registers.
	 */
	b	1f
	/* XXX tell as(1) to dump the literal pool here, but why? */
	.ltorg
	.align	5
1:

	/* Put SDRAM into self-refresh mode manually. */
	str	r5, [r4]
	nop

	/*
	 * Enter sleep mode.  Exit from sleep mode returns the processor
	 * to normal run mode.  Execution resumes at the physical address
	 * stored in the PSPR after the required boot sequence (a short
	 * excursion into the ROM boot loader).
	 */
	mcr	p14, 0, r1, c7, c0, 0

	/* Just in case that wake-up does not resume at */
	nop
	nop
	nop
1:
	b	1b

/*
 * void pxa2x0_cpu_resume(void)
 */
	.align 5
ENTRY(pxa2x0_cpu_resume)
	/* XXX C3000-specific */
	ldr	r0, .Lmdrefr_addr_phys
	b	1f
	.align 5
1:
	ldr	r2, [r0]
	bic	r2, r2, #MDREFR_DRI & 0x000000ff
	bic	r2, r2, #MDREFR_DRI & 0x0000ff00
	orr	r2, r2, #MDREFR_DRI_91MHZ
	str	r2, [r0]
	b	1f
	.align 5
1:
	ldr	r0, .Lsleepdata_phys	/* Point to PA of saved data. */

	ldmia	r0!, {r7-r10}
	mcr	p15, 0, r10, c3, c0, 0	/* Restore domain access control. */
	mcr	p15, 0, r9, c2, c0, 0	/* Restore TTB address. */
	mcr	p15, 0, r0, c8, c7, 0	/* Flush I+D TLBs. */
	mcr	p15, 0, r0, c7, c7, 0	/* Flush I+D BTB. */
	mcr	p15, 0, r8, c1, c0, 0	/* Restore MMU control. */
	mov	pc, r7			/* Jump to virtual address. */
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop

pxa2x0_cpu_resume_virt:
	ldr	r2, .Lsleepdata_svc	/* Load VA of saved registers. */

	/* Restore SVC mode SPSR and stack pointer. */
	ldr	r0, [r2], #4
	msr	spsr, r0
	ldr	sp, [r2], #4

	/* Restore FIQ mode registers. */
	mov	r1, #(PSR_FIQ32_MODE | I32_bit | F32_bit)
	msr	cpsr, r1
	ldr	r0, [r2], #4
	msr	spsr, r0
	ldr	r8, [r2], #4
	ldr	r9, [r2], #4
	ldr	r10, [r2], #4
	ldr	r11, [r2], #4
	ldr	r12, [r2], #4
	ldr	sp, [r2], #4
	ldr	lr, [r2], #4

	/* Restore IRQ mode registers. */
	mov	r1, #(PSR_IRQ32_MODE | I32_bit | F32_bit)
	msr	cpsr, r1
	ldr	r0, [r2], #4
	msr	spsr, r0
	ldr	sp, [r2], #4
	ldr	lr, [r2], #4

	/* Restore ABT mode registers. */
	mov	r1, #(PSR_ABT32_MODE | I32_bit | F32_bit)
	msr	cpsr, r1
	ldr	r0, [r2], #4
	msr	spsr, r0
	ldr	sp, [r2], #4
	ldr	lr, [r2], #4

	/* Restore UND mode registers. */
	mov	r1, #(PSR_UND32_MODE | I32_bit | F32_bit)
	msr	cpsr, r1
	ldr	r0, [r2], #4
	msr	spsr, r0
	ldr	sp, [r2], #4
	ldr	lr, [r2], #4

	/* Restore SYS mode registers. */
	mov	r1, #(PSR_SYS32_MODE | I32_bit | F32_bit)
	msr	cpsr, r1
	ldr	sp, [r2], #4
	ldr	lr, [r2], #4

	/* Return to SVC mode. */
	mov	r1, #(PSR_SVC32_MODE | I32_bit | F32_bit)
	msr	cpsr, r1

	ldmia	sp!, {r0-r12, pc}

.Lmdrefr_addr_phys:
	.word	PXA2X0_MEMCTL_BASE + MEMCTL_MDREFR

	.data

/*
 * Saved processor state
 */
	.align	5
sleepdata:
	.word	0		/* =pxa2x0_cpu_resume_virt */
	.word	0		/* MMU control */
	.word	0		/* MMU TTB address */
	.word	0		/* MMU domain access control */
sleepdata_svc:
	.word	0		/* SVC mode saved CPSR */
	.word	0		/* SVC mode stack pointer */
	.word	0		/* FIQ mode saved CPSR */
	.word	0		/* FIQ mode r8 */
	.word	0		/* FIQ mode r9 */
	.word	0		/* FIQ mode r10 */
	.word	0		/* FIQ mode r11 */
	.word	0		/* FIQ mode r12 */
	.word	0		/* FIQ mode stack pointer */
	.word	0		/* FIQ mode link register */
	.word	0		/* IRQ mode saved CPSR */
	.word	0		/* IRQ mode stack pointer */
	.word	0		/* IRQ mode link register */
	.word	0		/* ABT mode saved CPSR */
	.word	0		/* ABT mode stack pointer */
	.word	0		/* ABT mode link register */
	.word	0		/* UND mode saved CPSR */
	.word	0		/* UND mode stack pointer */
	.word	0		/* UND mode link register */
	.word	0		/* SYS mode stack pointer */
	.word	0		/* SYS mode link register */

	.text

/*
 * void pxa27x_run_mode(void)
 *
 * Disable half-turbo and turbo mode, but keep fast-bus mode.
 * Memory and LCD clock is not changed, so no reconfiguration is
 * necessary.
 */
ENTRY(pxa27x_run_mode)
	stmdb	sp!, {r0}
	mrc	p14, 0, r0, c6, c0, 0
	and	r0, r0, #~(CLKCFG_HT | CLKCFG_F| CLKCFG_T)
	mcr	p14, 0, r0, c6, c0, 0
	ldmia	sp!, {r0}
	mov	pc, lr

/*
 * void pxa27x_fastbus_run_mode(int enable, u_int32_t mdrefr)
 *
 * Enter normal run mode with fast-bus mode enabled or disabled.
 * The new value of MDREFR is programmed before or after CLKCFG,
 * as appropriate.
 */
 	.align 5
ENTRY(pxa27x_fastbus_run_mode)
 	stmdb	sp!, {r0-r2, lr}
	ldr	r2, .Lmemctliohp
	ldr	r2, [r2]
	cmp	r0, #0
	beq	disable_fastbus
	b	enable_fastbus
	.align	5
enable_fastbus:
	/* Enter normal run mode with fast-bus mode enabled. */
	mov	r0, #CLKCFG_B
 	mcr	p14, 0, r0, c6, c0, 0
	/* Set the new SDRAM refresh rate. */
 	str	r1, [r2, #MEMCTL_MDREFR]
	ldr	r0, [r2, #MEMCTL_MDREFR]
	mov	r0, r0
	ldmia	sp!, {r0-r2, pc}
 	.align 5
disable_fastbus:
	/* Set the new SDRAM refresh rate. */
 	str	r1, [r2, #MEMCTL_MDREFR]
	ldr	r0, [r2, #MEMCTL_MDREFR]
	mov	r0, r0
	/* Enter normal run mode with fast-bus mode disabled. */
	mov	r0, #0x0
 	mcr	p14, 0, r0, c6, c0, 0
	ldmia	sp!, {r0-r2, pc}

/* Keep these offsets in sync with struct memcfg. */
#define memcfg_mdrefr_high	0x00
#define memcfg_mdrefr_low	0x04
#define memcfg_mdrefr_low2	0x08	/* unused */
#define memcfg_msc_high		0x0c
#define memcfg_msc_low		0x18
#define memcfg_mdrefr_91	0x24

/*
 * void pxa27x_frequency_change(int cccr, int clkcfg,
 *    struct pxa2x0_memcfg *memcfg)
 *
 * Change the core PLL frequency and SDRAM refresh rate, ensuring the
 * proper sequence of operations.  If the CCCR_A bit is clear and L
 * is not equal to 7 the result is undefined.
 */
	.align 5
ENTRY(pxa27x_frequency_change)
	stmdb	sp!, {r0-r5, lr}

	/* Always write to CCCR before a frequency change. */
	ldr	r3, .Lclkmaniohp
	ldr	r3, [r3]
	str	r0, [r3, #CLKMAN_CCCR]

	/* Load the needed values into registers to avoid SDRAM access. */
	and	r3, r0, #CCCR_L_MASK
	ldr	r0, .Lmemctliohp
	ldr	r0, [r0]
	cmp	r3, #CCCR_RUN_X7		/* L=7 is 91MHz mode */
	beq	frequency_change_91
	and	r3, r1, #CLKCFG_B
	cmp	r3, #CLKCFG_B
	bne	frequency_change_208
	/* FALLTHROUGH */
frequency_change_high:
	ldr	r3, [r2, #memcfg_mdrefr_low]
	ldr	r4, [r2, #memcfg_mdrefr_high]
	add	r2, r2, #memcfg_msc_high
	bl	frequency_change_on_cache	/* XXX why BL? */
frequency_change_208:
	ldr	r3, [r2, #memcfg_mdrefr_low]
	ldr	r4, [r2, #memcfg_mdrefr_low]
	add	r2, r2, #memcfg_msc_high
	bl	frequency_change_on_cache
frequency_change_91:
	ldr	r3, [r2, #memcfg_mdrefr_low]
	ldr	r4, [r2, #memcfg_mdrefr_91]
	add	r2, r2, #memcfg_msc_low
	bl	frequency_change_on_cache

	/* Align execution to a cache line. */
	.align 5
frequency_change_on_cache:
	/* Change to a low SDRAM refresh rate.  Wait until the store to
	 * MDREFR is complete, following section 2.4 I/O Ordering and
	 * 6.5.1.4 of the PXA27x Developer's Manual. */
	str	r3, [r0, #MEMCTL_MDREFR]
	ldr	r5, [r0, #MEMCTL_MDREFR]
	mov	r5, r5
	/* Program new CLKCFG value, starting a core PLL frequency change
	 * if CLKCFG_F is set. */
	mcr	p14, 0, r1, c6, c0, 0
	/* Change SDRAM clock frequency to 104MHz, and ensure that the
	 * store to MDREFR is complete before the next SDRAM access. */
	str	r4, [r0, #MEMCTL_MDREFR]
	ldr	r5, [r0, #MEMCTL_MDREFR]
	mov	r5, r5
	/* Configure synchronous, static, and VLIO interfaces.  */
	ldr	r1, [r2], #4
	str	r1, [r0, #MEMCTL_MSC0]
	ldr	r1, [r2], #4
	str	r1, [r0, #MEMCTL_MSC1]
	ldr	r1, [r2]
	str	r1, [r0, #MEMCTL_MSC2]
	ldmia	sp!, {r0-r5, pc}

/*
 * void pxa27x_cpu_speed_91(void)
 *
 * Switch core run frequency to 91 MHz.
 */
	.align 5
ENTRY(pxa27x_cpu_speed_91)
 	stmdb	sp!, {r0-r3, lr}

	ldr	r0, .Lclkmaniohp
	ldr	r0, [r0]
	ldr	r1, .Lcccr_91
 	str	r1, [r0, #CLKMAN_CCCR]

	ldr	r0, .Lmemctliohp
	ldr	r0, [r0]
	ldr	r2, .Lmdrefr_91
	ldr	r3, .Lmdrefr_low

 	bl	1f
 	.align 5
1:
 	str	r3, [r0, #MEMCTL_MDREFR]
	ldr	r3, [r0, #MEMCTL_MDREFR]

	mov	r1, #CLKCFG_F
 	mcr	p14, 0, r1, c6, c0, 0
 	str	r2, [r0, #MEMCTL_MDREFR]
	ldr	r2, [r0, #MEMCTL_MDREFR]

	ldr	r1, .Lmsc0_low
 	str	r1, [r0, #MEMCTL_MSC0]
 	ldr	r1, .Lmsc1_low
 	str	r1, [r0, #MEMCTL_MSC1]
 	ldr	r1, .Lmsc2_low
 	str	r1, [r0, #MEMCTL_MSC2]

	ldmia	sp!, {r0-r3, pc}

.Lcccr_91:	.word	CCCR_TURBO_X1 | CCCR_RUN_X7
.Lmdrefr_91:	.word	MDREFR_SPEED_91


code:
/*	$OpenBSD: pxa2x0_clock.c,v 1.6 2008/01/03 17:59:32 kettenis Exp $ */

/*
 * Copyright (c) 2005 Dale Rahn <drahn@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/kernel.h>
#include <sys/device.h>
#include <sys/timetc.h>

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

#include <arm/cpufunc.h>

#include <arm/sa11x0/sa11x0_reg.h>
#include <arm/sa11x0/sa11x0_var.h>
#include <arm/sa11x0/sa11x0_ostreg.h>
#include <arm/xscale/pxa2x0reg.h>

int	pxaost_match(struct device *, void *, void *);
void	pxaost_attach(struct device *, struct device *, void *);

int	doclockintr(void *);
int	clockintr(void *);
int	statintr(void *);
void	rtcinit(void);

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

	u_int32_t		sc_clock_count;
	u_int32_t		sc_statclock_count;
	u_int32_t		sc_statclock_step;
	u_int32_t		sc_clock_step;
	u_int32_t		sc_clock_step_err_cnt;
	u_int32_t		sc_clock_step_error;
};

static struct pxaost_softc *pxaost_sc = NULL;

#define CLK4_TIMER_FREQUENCY	32768		/* 32.768KHz */

#define CLK0_TIMER_FREQUENCY	3250000		/* 3.2500MHz */

#ifndef STATHZ
#define STATHZ	64
#endif

struct cfattach pxaost_ca = {
	sizeof (struct pxaost_softc), pxaost_match, pxaost_attach
};

struct cfdriver pxaost_cd = {
	NULL, "pxaost", DV_DULL
};

u_int	pxaost_get_timecount(struct timecounter *tc);

static struct timecounter pxaost_timecounter = {
	pxaost_get_timecount, NULL, 0xffffffff, CLK4_TIMER_FREQUENCY,
	"pxaost", 0, NULL
};

int
pxaost_match(parent, match, aux)
	struct device *parent;
	void *match;
	void *aux;
{
	return (1);
}

void
pxaost_attach(parent, self, aux)
	struct device *parent;
	struct device *self;
	void *aux;
{
	struct pxaost_softc *sc = (struct pxaost_softc*)self;
	struct sa11x0_attach_args *sa = aux;

	printf("\n");

	sc->sc_iot = sa->sa_iot;

	pxaost_sc = sc;

	if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0,
	    &sc->sc_ioh))
		panic("%s: Cannot map registers", self->dv_xname);

	/* disable all channel and clear interrupt status */
	bus_space_write_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh, SAOST_IR, 0);
	bus_space_write_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh, SAOST_SR, 0x3f);

	bus_space_write_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh, OST_OMCR4, 0xc1);
	bus_space_write_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh, OST_OMCR5, 0x41);

	bus_space_write_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh, OST_OSMR4,
	    pxaost_sc->sc_clock_count);
	bus_space_write_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh, OST_OSMR5,
	    pxaost_sc->sc_statclock_count);

	/* Zero the counter value */
	bus_space_write_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh, OST_OSCR4, 0);

}

u_int
pxaost_get_timecount(struct timecounter *tc)
{
	return bus_space_read_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh,
	    OST_OSCR4);
}

int
clockintr(arg)
	void *arg;
{
	struct clockframe *frame = arg;
	u_int32_t oscr, match;
	u_int32_t match_error;

	bus_space_write_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh, SAOST_SR, 0x10);

	match = pxaost_sc->sc_clock_count;

	do {
		match += pxaost_sc->sc_clock_step;
		pxaost_sc->sc_clock_step_error +=
		    pxaost_sc->sc_clock_step_err_cnt;
		if (pxaost_sc->sc_clock_count > hz) {
			match_error = pxaost_sc->sc_clock_step_error / hz;
			pxaost_sc->sc_clock_step_error -= (match_error * hz);
			match += match_error;
		}
		pxaost_sc->sc_clock_count = match;
		hardclock(frame);

		oscr = bus_space_read_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh,
		    OST_OSCR4);

	} while ((signed)(oscr - match) > 0);

	 /* prevent missed interrupts */
	if (oscr - match < 5)
		match += 5;

	bus_space_write_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh, OST_OSMR4,
	    match);

	return(1);
}

int
statintr(arg)
	void *arg;
{
	struct clockframe *frame = arg;
	u_int32_t oscr, match;

	bus_space_write_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh, SAOST_SR, 0x20);

	/* schedule next clock intr */
	match = pxaost_sc->sc_statclock_count;
	do {
		match += pxaost_sc->sc_statclock_step;
		pxaost_sc->sc_statclock_count = match;
		statclock(frame);

		oscr = bus_space_read_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh,
		    OST_OSCR4);

	} while ((signed)(oscr - match) > 0);

	 /* prevent missed interrupts */
	if (oscr - match < 5)
		match += 5;
	bus_space_write_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh, OST_OSMR5,
	    match);

	return(1);
}

void
setstatclockrate(int newstathz)
{
	u_int32_t count;
	pxaost_sc->sc_statclock_step = CLK4_TIMER_FREQUENCY / newstathz;
	count = bus_space_read_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh, OST_OSCR4);
	count += pxaost_sc->sc_statclock_step;
	pxaost_sc->sc_statclock_count = count;
	bus_space_write_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh,
	    OST_OSMR5, count);
}

int
doclockintr(void *arg)
{
	u_int32_t status;
	int result = 0;

	status = bus_space_read_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh, SAOST_SR);
	if (status & 0x10)
		result |= clockintr(arg);
	if (status & 0x20)
		result |= statintr(arg);

	return (result);
}

void
cpu_initclocks()
{
	u_int32_t clk;

	stathz = STATHZ;
	profhz = stathz;
	pxaost_sc->sc_statclock_step = CLK4_TIMER_FREQUENCY / stathz;
	pxaost_sc->sc_clock_step = CLK4_TIMER_FREQUENCY / hz;
	pxaost_sc->sc_clock_step_err_cnt = CLK4_TIMER_FREQUENCY % hz;
	pxaost_sc->sc_clock_step_error = 0;

	/* Use the channels 0 and 1 for hardclock and statclock, respectively */
	pxaost_sc->sc_clock_count = pxaost_sc->sc_clock_step;
	pxaost_sc->sc_statclock_count = CLK4_TIMER_FREQUENCY / stathz;

	pxa2x0_intr_establish(PXA2X0_INT_OST, IPL_CLOCK, doclockintr, 0, "clock");

	clk = bus_space_read_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh, OST_OSCR4);

	bus_space_write_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh, SAOST_SR, 0x3f);
	bus_space_write_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh, SAOST_IR, 0x30);
	bus_space_write_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh, OST_OSMR4,
	    clk + pxaost_sc->sc_clock_count);
	bus_space_write_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh, OST_OSMR5,
	    clk + pxaost_sc->sc_statclock_count);

	tc_init(&pxaost_timecounter);
}

void
delay(usecs)
	u_int usecs;
{
	u_int32_t clock, oclock, delta, delaycnt;
	volatile int j;
	int csec, usec;

	if (usecs > (0x80000000 / (CLK4_TIMER_FREQUENCY))) {
		csec = usecs / 10000;
		usec = usecs % 10000;

		delaycnt = (CLK4_TIMER_FREQUENCY / 100) * csec +
		    (CLK4_TIMER_FREQUENCY / 100) * usec / 10000;
	} else {
		delaycnt = CLK4_TIMER_FREQUENCY * usecs / 1000000;
	}

	if (delaycnt <= 1)
		for (j = 100; j > 0; j--)
			;

	if (!pxaost_sc) {
		/* clock isn't initialized yet */
		for (; usecs > 0; usecs--)
			for (j = 100; j > 0; j--)
				;
		return;
	}

	oclock = bus_space_read_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh,
	    OST_OSCR4);

	while (1) {
		for (j = 100; j > 0; j--)
			;
		clock = bus_space_read_4(pxaost_sc->sc_iot, pxaost_sc->sc_ioh,
		    OST_OSCR4);
		delta = clock - oclock;
		if (delta > delaycnt)
			break;
	}
}


code:
/*	$OpenBSD: pxa2x0_com.c,v 1.12 2010/09/07 16:21:35 deraadt Exp $ */
/*	$NetBSD: pxa2x0_com.c,v 1.4 2003/07/15 00:24:55 lukem Exp $	*/

/*
 * Copyright 2003 Wasabi Systems, Inc.
 * All rights reserved.
 *
 * Written by Steve C. Woodford for Wasabi Systems, Inc.
 *
 * 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 for the NetBSD Project by
 *      Wasabi Systems, Inc.
 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
 *    or promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
 * 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.
 */

#ifndef COM_PXA2X0
#error "You must use options COM_PXA2X0 to get PXA2x0 serial port support"
#endif

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

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

#include <dev/ic/comreg.h>
#include <dev/ic/comvar.h>

#define com_isr 8
#define ISR_RECV	(ISR_RXPL | ISR_XMODE | ISR_RCVEIR)

#include <arm/xscale/pxa2x0reg.h>
#include <arm/xscale/pxa2x0var.h>

#ifdef __zaurus__
#include <zaurus/dev/zaurus_scoopvar.h>
#endif

int	pxauart_match(struct device *, void *, void *);
void	pxauart_attach(struct device *, struct device *, void *);
int	pxauart_activate(struct device *, int);

struct cfattach com_pxaip_ca = {
        sizeof (struct com_softc), pxauart_match, pxauart_attach, NULL,
	pxauart_activate
};

int
pxauart_match(struct device *parent, void *cf, void *aux)
{
	struct pxaip_attach_args *pxa = aux;
	bus_space_tag_t bt = &pxa2x0_a4x_bs_tag;	/* XXX: This sucks */
	bus_space_handle_t bh;
	int rv;

	switch (pxa->pxa_addr) {
	case PXA2X0_FFUART_BASE:
		if (pxa->pxa_intr != PXA2X0_INT_FFUART)
			return (0);
		break;

	case PXA2X0_STUART_BASE:
		if (pxa->pxa_intr != PXA2X0_INT_STUART)
			return (0);
		break;

	case PXA2X0_BTUART_BASE:	/* XXX: Config file option ... */
		if (pxa->pxa_intr != PXA2X0_INT_BTUART)
			return (0);
		break;

	default:
		return (0);
	}

	pxa->pxa_size = 0x20;

	{
		extern bus_addr_t comconsaddr;

		if (comconsaddr == pxa->pxa_addr)
			return (1);
	}

	if (bus_space_map(bt, pxa->pxa_addr, pxa->pxa_size, 0, &bh))
		return (0);

	/* Make sure the UART is enabled */
	bus_space_write_1(bt, bh, com_ier, IER_EUART);

	rv = comprobe1(bt, bh);
	bus_space_unmap(bt, bh, pxa->pxa_size);

	return (rv);
}

void
pxauart_attach(struct device *parent, struct device *self, void *aux)
{
	struct com_softc *sc = (struct com_softc *)self;
	struct pxaip_attach_args *pxa = aux;

	sc->sc_iot = &pxa2x0_a4x_bs_tag;	/* XXX: This sucks */
	sc->sc_iobase = pxa->pxa_addr;
	sc->sc_frequency = PXA2X0_COM_FREQ;
	sc->sc_uarttype = COM_UART_PXA2X0;

#if 0
	if (com_is_console(sc->sc_iot, sc->sc_iobase, &sc->sc_ioh) == 0 &&
	    bus_space_map(sc->sc_iot, sc->sc_iobase, pxa->pxa_size, 0,
			  &sc->sc_ioh)) {
		printf(": can't map registers\n");
		return;
	}
#endif
	bus_space_map(sc->sc_iot, sc->sc_iobase, pxa->pxa_size, 0, &sc->sc_ioh);

	com_attach_subr(sc);

	(void)pxa2x0_intr_establish(pxa->pxa_intr, IPL_TTY, comintr,
	    sc, sc->sc_dev.dv_xname);
}

int
pxauart_activate(struct device *self, int act)
{
	struct com_softc *sc = (struct com_softc *)self;
	bus_space_tag_t iot = sc->sc_iot;
	bus_space_handle_t ioh = sc->sc_ioh;
	struct tty *tp = sc->sc_tty;

	switch (act) {
	case DVACT_SUSPEND:
#ifdef __zaurus__
		if (sc->enabled && ISSET(sc->sc_hwflags, COM_HW_SIR))
			scoop_set_irled(0);
#endif
		break;
	case DVACT_RESUME:
		if (sc->enabled) {
			sc->sc_initialize = 1;
			comparam(tp, &tp->t_termios);
			bus_space_write_1(iot, ioh, com_ier, sc->sc_ier);

			if (ISSET(sc->sc_hwflags, COM_HW_SIR)) {
#ifdef __zaurus__
				scoop_set_irled(1);
#endif
				bus_space_write_1(iot, ioh, com_isr,
				    ISR_RECV);
			}
		}
		break;
	}
	return 0;
}


code:
/*	$OpenBSD: pxa2x0_dma.c,v 1.4 2008/05/15 22:17:08 brad Exp $ */
/*	$NetBSD: pxa2x0_dma.c,v 1.3 2003/08/07 16:58:35 bsh Exp $ */

/*
 * Copyright (c) 2002  Genetec Corporation.  All rights reserved.
 * Written by Hiroyuki Bessho for Genetec Corporation.
 *
 * 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 for the NetBSD Project by
 *	Genetec Corporation.
 * 4. The name of Genetec Corporation may not be used to endorse or
 *    promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``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 GENETEC CORPORATION
 * 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.
 */

/*
 * bus dma tag for PXA2[15]0 processor.
 */

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

#include <uvm/uvm_extern.h>

#define	_ARM32_BUS_DMA_PRIVATE
#include <machine/bus.h>

struct arm32_bus_dma_tag pxa2x0_bus_dma_tag = {
	0,
	0,
	NULL,
	_bus_dmamap_create,
	_bus_dmamap_destroy,
	_bus_dmamap_load,
	_bus_dmamap_load_mbuf,
	_bus_dmamap_load_uio,
	_bus_dmamap_load_raw,
	_bus_dmamap_unload,
	_bus_dmamap_sync,
	_bus_dmamem_alloc,
	_bus_dmamem_free,
	_bus_dmamem_map,
	_bus_dmamem_unmap,
	_bus_dmamem_mmap,
};


code:
/*	$OpenBSD: pxa2x0_dmac.c,v 1.4 2012/12/05 23:20:11 deraadt Exp $	*/

/*
 * Copyright (c) 2005 Christopher Pascoe <pascoe@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.
 */

/*
 * DMA Controller Handler for the Intel PXA2X0 processor.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/evcount.h>
#include <uvm/uvm_extern.h>

#include <machine/bus.h>
#include <machine/intr.h>
#include <machine/lock.h>

#include <arm/xscale/pxa2x0reg.h>
#include <arm/xscale/pxa2x0var.h>
#include <arm/xscale/pxa2x0_dmac.h>

typedef void (*pxadmac_intrhandler)(void *);

struct pxadmac_softc {
	struct device sc_dev;
	bus_space_tag_t sc_bust;
	bus_space_handle_t sc_bush;
	void *sc_ih;
	int sc_nchan;
	int sc_npri;
	pxadmac_intrhandler sc_intrhandlers[DMAC_N_CHANNELS_PXA27X];
	void *sc_intrargs[DMAC_N_CHANNELS_PXA27X];
};

int pxadmac_intr(void *);

/*
 * DMAC autoconf glue
 */
int	pxadmac_match(struct device *, void *, void *);
void	pxadmac_attach(struct device *, struct device *, void *);

struct cfattach pxadmac_ca = {
	sizeof(struct pxadmac_softc), pxadmac_match, pxadmac_attach
};

struct cfdriver pxadmac_cd = {
	NULL, "pxadmac", DV_DULL
};

static struct pxadmac_softc *pxadmac_softc = NULL;

int
pxadmac_match(struct device *parent, void *cf, void *aux)
{
	struct pxaip_attach_args *pxa = aux;

	if (pxadmac_softc != NULL || pxa->pxa_addr != PXA2X0_DMAC_BASE)
		return (0);

	return (1);
}

void
pxadmac_attach(struct device *parent, struct device *self, void *args)
{
	struct pxadmac_softc *sc = (struct pxadmac_softc *)self;
	struct pxaip_attach_args *pxa = args;
	bus_size_t bus_size;

	sc->sc_bust = pxa->pxa_iot;

	printf(": DMA Controller\n");

	if ((cputype & ~CPU_ID_XSCALE_COREREV_MASK) == CPU_ID_PXA27X) {
		sc->sc_nchan = DMAC_N_CHANNELS_PXA27X;
		sc->sc_npri = DMAC_N_PRIORITIES_PXA27X;
		bus_size = PXA27X_DMAC_SIZE;
	} else  {
		sc->sc_nchan = DMAC_N_CHANNELS;
		sc->sc_npri = DMAC_N_PRIORITIES;
		bus_size = PXA2X0_DMAC_SIZE;
	}

	if (bus_space_map(sc->sc_bust, pxa->pxa_addr, bus_size, 0,
	    &sc->sc_bush)) {
		printf("%s: Can't map registers!\n", sc->sc_dev.dv_xname);
		return;
	}

	sc->sc_ih = pxa2x0_intr_establish(pxa->pxa_intr, IPL_BIO,
	    pxadmac_intr, sc, "pxadmac");
	if (sc->sc_ih == NULL) {
		printf(": unable to establish interrupt\n");
		bus_space_unmap(sc->sc_bust, sc->sc_bush, bus_size);
		return;
	}

	pxadmac_softc = sc;
}

/* Perform non-descriptor based DMA to a FIFO */
int
pxa2x0_dma_to_fifo(int periph, int chan, bus_addr_t fifo_addr, int width,
    int burstsize, bus_addr_t src_addr, int length, void (*intr)(void *),
    void *intrarg)
{
	struct pxadmac_softc *sc = pxadmac_softc;
	uint32_t cmd;

	if (periph < 0 || periph > 63 || periph == 23) {
		printf("pxa2x0_dma_to_fifo: bogus peripheral %d", periph);
		return EINVAL;
	}

	if (chan < 0 || chan >= sc->sc_nchan) {
		printf("pxa2x0_dma_to_fifo: bogus dma channel %d", chan);
		return EINVAL;
	}

	if (length < 0 || length > DCMD_LENGTH_MASK) {
		printf("pxa2x0_dma_to_fifo: bogus length %d", length);
		return EINVAL;
	}

	cmd = (length & DCMD_LENGTH_MASK) | DCMD_INCSRCADDR | DCMD_FLOWTRG
	    | DCMD_ENDIRQEN;

	switch (width) {
	case 1:
		cmd |= DCMD_WIDTH_1;
		break;
	case 4:
		cmd |= DCMD_WIDTH_4;
		break;
	default:
		printf("pxa2x0_dma_to_fifo: bogus width %d", width);
		return EINVAL;
	}

	switch (burstsize) {
	case 8:
		cmd |= DCMD_SIZE_8;
		break;
	case 16:
		cmd |= DCMD_SIZE_16;
		break;
	case 32:
		cmd |= DCMD_SIZE_32;
		break;
	default:
		printf("pxa2x0_dma_to_fifo: bogus burstsize %d", burstsize);
		return EINVAL;
	}

	/* XXX: abort anything already in progress, hopefully nothing. */
	bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCSR(chan),
	    DCSR_NODESCFETCH);

	/* Save handler for interrupt-on-completion. */
	sc->sc_intrhandlers[chan] = intr;
	sc->sc_intrargs[chan] = intrarg;

	/* Map peripheral to channel for flow control setup. */
	bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DRCMR(periph),
	    chan | DRCMR_MAPVLD);

	/* Setup transfer addresses. */
	bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DDADR(chan),
	    DDADR_STOP);
	bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DSADR(chan),
	    src_addr);
	bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DTADR(chan),
	    fifo_addr);
	bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCMD(chan),
	    cmd);

	/* Start the transfer. */
	bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCSR(chan),
	    DCSR_RUN | DCSR_NODESCFETCH);

	return 0;
}

/* Perform non-descriptor based DMA from a FIFO */
int
pxa2x0_dma_from_fifo(int periph, int chan, bus_addr_t fifo_addr, int width,
    int burstsize, bus_addr_t trg_addr, int length, void (*intr)(void *),
    void *intrarg)
{
	struct pxadmac_softc *sc = pxadmac_softc;
	uint32_t cmd;

	if (periph < 0 || periph > 63 || periph == 23) {
		printf("pxa2x0_dma_from_fifo: bogus peripheral %d", periph);
		return EINVAL;
	}

	if (chan < 0 || chan >= sc->sc_nchan) {
		printf("pxa2x0_dma_from_fifo: bogus dma channel %d", chan);
		return EINVAL;
	}

	if (length < 0 || length > DCMD_LENGTH_MASK) {
		printf("pxa2x0_dma_from_fifo: bogus length %d", length);
		return EINVAL;
	}

	cmd = (length & DCMD_LENGTH_MASK) | DCMD_INCTRGADDR | DCMD_FLOWSRC
	    | DCMD_ENDIRQEN;

	switch (width) {
	case 1:
		cmd |= DCMD_WIDTH_1;
		break;
	case 4:
		cmd |= DCMD_WIDTH_4;
		break;
	default:
		printf("pxa2x0_dma_from_fifo: bogus width %d", width);
		return EINVAL;
	}

	switch (burstsize) {
	case 8:
		cmd |= DCMD_SIZE_8;
		break;
	case 16:
		cmd |= DCMD_SIZE_16;
		break;
	case 32:
		cmd |= DCMD_SIZE_32;
		break;
	default:
		printf("pxa2x0_dma_from_fifo: bogus burstsize %d", burstsize);
		return EINVAL;
	}

	/* XXX: abort anything already in progress, hopefully nothing. */
	bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCSR(chan),
	    DCSR_NODESCFETCH);

	/* Save handler for interrupt-on-completion. */
	sc->sc_intrhandlers[chan] = intr;
	sc->sc_intrargs[chan] = intrarg;

	/* Map peripheral to channel for flow control setup. */
	bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DRCMR(periph),
	    chan | DRCMR_MAPVLD);

	/* Setup transfer addresses. */
	bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DDADR(chan),
	    DDADR_STOP);
	bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DSADR(chan),
	    fifo_addr);
	bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DTADR(chan),
	    trg_addr);
	bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCMD(chan),
	    cmd);

	/* Start the transfer. */
	bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCSR(chan),
	    DCSR_RUN | DCSR_NODESCFETCH);

	return 0;
}

int
pxadmac_intr(void *v)
{
	struct pxadmac_softc *sc = v;
	u_int32_t dint, dcsr;
	int chan;

	/* Interrupt for us? */
	dint = bus_space_read_4(sc->sc_bust, sc->sc_bush, DMAC_DINT);
	if (!dint)
		return 0;

	/* Process individual channels and run handlers. */
	/* XXX: this does not respect priority order for channels. */
	for (chan = 0; dint != 0 && chan < 32; chan++) {
		/* Don't ack channels that weren't ready at call time. */
		if ((dint & (1 << chan)) == 0)
			continue;
		dint &= ~(1 << chan);

		/* Acknowledge individual channel interrupt. */
		dcsr = bus_space_read_4(sc->sc_bust, sc->sc_bush,
		    DMAC_DCSR(chan));
		bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCSR(chan),
		    dcsr & 0x7C80021F);

		/* Call the registered handler. */
		if (sc->sc_intrhandlers[chan])
			sc->sc_intrhandlers[chan](sc->sc_intrargs[chan]);
	}

	return 1;
}


code:
/*	$OpenBSD: pxa2x0_dmac.h,v 1.2 2006/04/04 11:37:05 pascoe Exp $	*/

/*
 * Copyright (c) 2005 Christopher Pascoe <pascoe@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.
 */

#ifndef _PXA2X0_DMAC_H
#define _PXA2X0_DMAC_H

int pxa2x0_dma_to_fifo(int periph, int chan, bus_addr_t fifo_addr, int width,
    int burstsize, bus_addr_t src_addr, int length, void (*intr)(void *),
    void *intrarg);

int pxa2x0_dma_from_fifo(int periph, int chan, bus_addr_t fifo_addr, int width,
    int burstsize, bus_addr_t trg_addr, int length, void (*intr)(void *),
    void *intrarg);

#endif /* _PXA2X0_DMAC_H */