From bcc03ff7a7245d04ed7b295255b6bb76fd7e9097 Mon Sep 17 00:00:00 2001 From: ober Date: Sat, 6 Dec 2008 22:10:41 +0000 Subject: [PATCH] Initial apm support for Zaurus. Ported from OpenBSD by mjf@. --- sys/arch/arm/xscale/pxa2x0_apm.c | 1558 ++++++++++++++++++++++++++++++ 1 file changed, 1558 insertions(+) create mode 100644 sys/arch/arm/xscale/pxa2x0_apm.c diff --git a/sys/arch/arm/xscale/pxa2x0_apm.c b/sys/arch/arm/xscale/pxa2x0_apm.c new file mode 100644 index 000000000000..244fcc7629c0 --- /dev/null +++ b/sys/arch/arm/xscale/pxa2x0_apm.c @@ -0,0 +1,1558 @@ +/* $NetBSD: pxa2x0_apm.c,v 1.1 2008/12/06 22:10:41 ober Exp $ */ +/* $OpenBSD: pxa2x0_apm.c,v 1.28 2007/03/29 18:42:38 uwe 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 +#include +#include +#include +#include +#include /* for vfs_syncwait() */ +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#if defined(APMDEBUG) +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) /**/ +#endif + +#define APM_LOCK(sc) lockmgr(&(sc)->sc_lock, LK_EXCLUSIVE, NULL) +#define APM_UNLOCK(sc) lockmgr(&(sc)->sc_lock, LK_RELEASE, NULL) + +#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; + +extern struct cfdriver zapm_cd; + +/* 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 *); + +#if 0 +extern int perflevel; +#endif + +int freq; +void pxa2x0_setperf(int speed); +int pxa2x0_cpuspeed(int *speed); + +int apm_record_event(struct pxa2x0_apm_softc *, u_int); +#if 0 +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}; +#endif + +/* + * 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) +{ + + resettodr(); + + dopowerhooks(PWR_SUSPEND); + +#if 0 + if (cold) + vfs_syncwait(0); +#endif + + if (sc->sc_suspend == NULL) + pxa2x0_wakeup_config(PXA2X0_WAKEUP_ALL, 1); + else + sc->sc_suspend(sc); + + pxa2x0_apm_sleep(sc); +} + +void +apm_resume(struct pxa2x0_apm_softc *sc) +{ + + dopowerhooks(PWR_RESUME); + + inittodr(0); + + /* + * Clear the OTG Peripheral hold after running the pxaudc and pxaohci + * powerhooks 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); +} + +#if 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, + "%s", 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 (!zapm_cd.cd_ndevs || APMUNIT(dev) != 0 || + !(sc = zapm_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; + + 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 (1); + } + + 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; + } + + lockinit(&sc->sc_lock, PWAIT, "apmlk", 0, 0); + + 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); +} +#endif /* 0 */ + +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 (zapm_cd.cd_ndevs < 1 || zapm_cd.cd_devs[0] == NULL) + return; + sc = device_private(zapm_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 (zapm_cd.cd_ndevs < 1 || zapm_cd.cd_devs[0] == NULL) + return (0); + + sc = device_private(zapm_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); + +#if 0 + pxa2x0_setperf(perflevel); +#endif + +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 = device_private(zapm_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; +#if 0 + pxa2x0_setperf(perflevel); +#endif +}