/* $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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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); }