diff options
author | Uwe Stuehler <uwe@cvs.openbsd.org> | 2005-02-22 21:53:04 +0000 |
---|---|---|
committer | Uwe Stuehler <uwe@cvs.openbsd.org> | 2005-02-22 21:53:04 +0000 |
commit | a5bb1e03d878a3671b987aeade87b83329b8a222 (patch) | |
tree | b8493e19ba1723dd82ab5c5c382fe89aa287664b /sys/arch/arm | |
parent | d717ccdc439590ad4ff2eadc559fdf0e51fad5d9 (diff) |
Initial suspend/resume code with additional powerhooks. Enter/exit
suspend mode with power button or zzz. May not work for everyone yet.
ok drahn@ and deraadt@
Diffstat (limited to 'sys/arch/arm')
-rw-r--r-- | sys/arch/arm/xscale/files.pxa2x0 | 3 | ||||
-rw-r--r-- | sys/arch/arm/xscale/pxa2x0_apm.c | 829 | ||||
-rw-r--r-- | sys/arch/arm/xscale/pxa2x0_apm.h | 37 | ||||
-rw-r--r-- | sys/arch/arm/xscale/pxa2x0_apm_asm.S | 577 | ||||
-rw-r--r-- | sys/arch/arm/xscale/pxa2x0_lcd.c | 48 | ||||
-rw-r--r-- | sys/arch/arm/xscale/pxa2x0reg.h | 62 |
6 files changed, 1520 insertions, 36 deletions
diff --git a/sys/arch/arm/xscale/files.pxa2x0 b/sys/arch/arm/xscale/files.pxa2x0 index 0e3d4a1ba67..b9523ad7590 100644 --- a/sys/arch/arm/xscale/files.pxa2x0 +++ b/sys/arch/arm/xscale/files.pxa2x0 @@ -1,4 +1,4 @@ -# $OpenBSD: files.pxa2x0,v 1.8 2005/02/17 22:10:35 dlg Exp $ +# $OpenBSD: files.pxa2x0,v 1.9 2005/02/22 21:53:03 uwe Exp $ # $NetBSD: files.pxa2x0,v 1.6 2004/05/01 19:09:14 thorpej Exp $ # # Configuration info for Intel PXA2[51]0 CPU support @@ -59,6 +59,7 @@ file arch/arm/xscale/pxa2x0_lcd.c lcd needs-flag # Power manager and APM emulation device apm file arch/arm/xscale/pxa2x0_apm.c apm needs-flag +file arch/arm/xscale/pxa2x0_apm_asm.S apm include "dev/pcmcia/files.pcmcia" diff --git a/sys/arch/arm/xscale/pxa2x0_apm.c b/sys/arch/arm/xscale/pxa2x0_apm.c index 242cdf2ca0e..9b05b1e375d 100644 --- a/sys/arch/arm/xscale/pxa2x0_apm.c +++ b/sys/arch/arm/xscale/pxa2x0_apm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pxa2x0_apm.c,v 1.3 2005/01/26 06:34:53 uwe Exp $ */ +/* $OpenBSD: pxa2x0_apm.c,v 1.4 2005/02/22 21:53:03 uwe Exp $ */ /*- * Copyright (c) 2001 Alexander Guy. All rights reserved. @@ -54,6 +54,7 @@ #include <arm/xscale/pxa2x0reg.h> #include <arm/xscale/pxa2x0var.h> #include <arm/xscale/pxa2x0_apm.h> +#include <arm/xscale/pxa2x0_gpio.h> #if defined(APMDEBUG) #define DPRINTF(x) printf x @@ -75,7 +76,7 @@ struct cfdriver apm_cd = { int apm_userstandbys; int apm_suspends; -int apm_battlow; +int apm_battlow; /* XXX unused */ void apm_power_info(struct pxa2x0_apm_softc *, struct apm_power_info *); @@ -86,9 +87,6 @@ void apm_periodic_check(struct pxa2x0_apm_softc *); void apm_thread_create(void *); void apm_thread(void *); -void pxa2x0_apm_standby(struct pxa2x0_apm_softc *); -void pxa2x0_apm_sleep(struct pxa2x0_apm_softc *); - void filt_apmrdetach(struct knote *kn); int filt_apmread(struct knote *kn, long hint); int apmkqfilter(dev_t dev, struct knote *kn); @@ -114,6 +112,125 @@ struct filterops apmread_filtops = #define SCFLAG_OWRITE (1 << 1) #define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE) +/* This structure must be keept 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_apm_standby(struct pxa2x0_apm_softc *); +void pxa2x0_apm_sleep(struct pxa2x0_apm_softc *); + +void pxa2x0_pi2c_open(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); +void pxa2x0_pi2c_close(bus_space_tag_t, bus_space_handle_t); +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); +void pxa2x0_pi2c_printregs(bus_space_tag_t, bus_space_handle_t); +void pxa2x0_pi2c_print(struct pxa2x0_apm_softc *); + +/* XXX used in pxa2x0_apm_asm.S */ +bus_space_handle_t pxa2x0_gpio_ioh = 0; +bus_space_handle_t pxa2x0_clkman_ioh = 0; +bus_space_handle_t pxa2x0_memctl_ioh = 0; + +/* 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); + +/* XXX */ +void scoop_check_mcr(void); +void scoop_suspend(void); +void scoop_resume(void); + void apm_power_info(struct pxa2x0_apm_softc *sc, struct apm_power_info *power) @@ -137,6 +254,21 @@ apm_standby(struct pxa2x0_apm_softc *sc) if (cold) vfs_syncwait(0); + /* + * Clear pending standby requests. Do not enter standby mode if + * suspend was requested in the meantime. + */ + apm_userstandbys = 0; + if (apm_suspends) { + /* + * Arbitrary delay to avoid reinitializing some devices + * too fast, since PWR_RESUME hooks will be run before + * PWR_SUSPEND hooks. Perhaps this is overly paranoid. + */ + delay(500000); + return; + } + pxa2x0_apm_standby((struct pxa2x0_apm_softc *)sc); } @@ -144,16 +276,15 @@ void apm_suspend(struct pxa2x0_apm_softc *sc) { -#if 0 - /* XXX something is not restored in PWR_RESUME. */ dopowerhooks(PWR_SUSPEND); -#else - dopowerhooks(PWR_STANDBY); -#endif if (cold) vfs_syncwait(0); + /* Clear pending standby and suspend requests. */ + apm_userstandbys = 0; + apm_suspends = 0; + pxa2x0_apm_sleep((struct pxa2x0_apm_softc *)sc); } @@ -171,13 +302,15 @@ apm_periodic_check(struct pxa2x0_apm_softc *sc) if (sc->sc_periodic_check != NULL) sc->sc_periodic_check(sc); + /* + * Counters for pending requests are cleared just before changing + * the processor run mode to avoid falling back to sleep after a + * wake-up event. + */ if (apm_suspends) { - apm_userstandbys = 0; - apm_suspends = 0; apm_suspend(sc); apm_resume(sc); } else if (apm_userstandbys) { - apm_userstandbys = 0; apm_standby(sc); apm_resume(sc); } @@ -335,7 +468,7 @@ apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) error = ENOTTY; } - return error; + return (error); } void @@ -382,40 +515,680 @@ apmkqfilter(dev_t dev, struct knote *kn) } 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; + } + + if (bus_space_map(sc->sc_iot, PXA2X0_GPIO_BASE, PXA2X0_GPIO_SIZE, + 0, &pxa2x0_gpio_ioh)) { + printf("pxa2x0_apm_sleep: can't map GPIO\n"); + return; + } +} + +void +pxa2x0_wakeup_config(u_int32_t 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); + } + + 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) { + 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 { + 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_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); + 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_PKWR, pkwr); + bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PKSR, + 0xffffffff); + + /* XXX do that just before suspend. */ + pxa2x0_clkman_config(CKEN_KEY, + (wsrc & PXA2X0_WAKEUP_KEYNS_ALL) != 0); +} + +void pxa2x0_apm_standby(struct pxa2x0_apm_softc *sc) { + + /* XXX standby mode is not supported. */ + delay(1000000); +} + +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_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; + bus_space_handle_t intctl_ioh; int save; + u_int32_t rv; + + ost_ioh = (bus_space_handle_t)0; + intctl_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; + } + + if (bus_space_map(sc->sc_iot, PXA2X0_INTCTL_BASE, + PXA2X0_INTCTL_SIZE, 0, &intctl_ioh)) { + printf("pxa2x0_apm_sleep: can't map INTCTL\n"); + goto out; + } save = disable_interrupts(I32_bit|F32_bit); - /* XXX add power hooks elsewhere, first. */ + sd.sd_oscr0 = bus_space_read_4(sc->sc_iot, ost_ioh, OST_OSCR0); + 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_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 */ + } + + scoop_check_mcr(); + + /* XXX control battery charging in sleep mode. */ + + /* XXX schedule RTC alarm to check the battery? */ + + pxa2x0_wakeup_config(PXA2X0_WAKEUP_ALL, 1); + + 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); + + scoop_check_mcr(); + scoop_suspend(); + + 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 = bus_space_read_4(sc->sc_iot, intctl_ioh, INTCTL_ICLR); + sd.sd_icmr = bus_space_read_4(sc->sc_iot, intctl_ioh, INTCTL_ICMR); + sd.sd_iccr = bus_space_read_4(sc->sc_iot, intctl_ioh, INTCTL_ICCR); + bus_space_write_4(sc->sc_iot, intctl_ioh, 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); + bus_space_write_4(sc->sc_iot, pxa2x0_clkman_ioh, CLKMAN_CKEN, + CKEN_MEM | CKEN_KEY); + + /* 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 reset controller status. */ + bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_RCSR, + RCSR_HWR | RCSR_WDR | RCSR_SMR | RCSR_GPR); + + /* 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); + + /* 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); + 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); + + bus_space_write_4(sc->sc_iot, intctl_ioh, INTCTL_ICLR, sd.sd_iclr); + bus_space_write_4(sc->sc_iot, intctl_ioh, INTCTL_ICCR, sd.sd_iccr); + bus_space_write_4(sc->sc_iot, intctl_ioh, INTCTL_ICMR, sd.sd_icmr); + + if ((bus_space_read_4(sc->sc_iot, intctl_ioh, INTCTL_ICIP) & 0x1) != 0) + bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PEDR, 0x1); + + scoop_check_mcr(); + scoop_resume(); + + 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); + + bus_space_write_4(sc->sc_iot, sc->sc_pm_ioh, POWMAN_PMCR, 0); + + /* 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); + + 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_OSCR0, sd.sd_oscr0); + bus_space_write_4(sc->sc_iot, ost_ioh, OST_OIER, sd.sd_oier); + + /* XXX update ticks from RTC. */ restore_interrupts(save); + + out: + if (ost_ioh != (bus_space_handle_t)0) + bus_space_unmap(sc->sc_iot, ost_ioh, PXA2X0_OST_SIZE); + if (intctl_ioh != (bus_space_handle_t)0) + bus_space_unmap(sc->sc_iot, intctl_ioh, PXA2X0_INTCTL_SIZE); } void -pxa2x0_apm_sleep(struct pxa2x0_apm_softc *sc) +pxa2x0_pi2c_open(bus_space_tag_t iot, bus_space_handle_t ioh) { - - /* XXX first make standby work, and then sleep mode. */ - pxa2x0_apm_standby(sc); + 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_apm_attach_sub(struct pxa2x0_apm_softc *sc) +pxa2x0_pi2c_close(bus_space_tag_t iot, bus_space_handle_t ioh) { + u_int32_t rv; - sc->sc_iot = &pxa2x0_bs_tag; + bus_space_write_4(iot, ioh, POWMAN_PICR, PICR_UR); + bus_space_write_4(iot, ioh, POWMAN_PISAR, 0); + delay(1); - 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; + /* 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); } - lockinit(&sc->sc_lock, PWAIT, "apmlk", 0, 0); + bus_space_write_4(iot, ioh, POWMAN_PISR, PISR_ITE); - kthread_create_deferred(apm_thread_create, sc); + rv = bus_space_read_4(iot, ioh, POWMAN_PICR); + bus_space_write_4(iot, ioh, POWMAN_PICR, rv & ~PICR_START); - printf("\n"); + /* 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); +} + +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" : "unkown")); } diff --git a/sys/arch/arm/xscale/pxa2x0_apm.h b/sys/arch/arm/xscale/pxa2x0_apm.h index 9891822b118..1abd374de6c 100644 --- a/sys/arch/arm/xscale/pxa2x0_apm.h +++ b/sys/arch/arm/xscale/pxa2x0_apm.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pxa2x0_apm.h,v 1.3 2005/01/26 06:34:53 uwe Exp $ */ +/* $OpenBSD: pxa2x0_apm.h,v 1.4 2005/02/22 21:53:03 uwe Exp $ */ /* * Copyright (c) 2005 Uwe Stuehler <uwe@bsdx.de> @@ -36,7 +36,40 @@ struct pxa2x0_apm_softc { void (*sc_power_info)(struct pxa2x0_apm_softc *, struct apm_power_info *); }; +void pxa2x0_apm_attach_sub(struct pxa2x0_apm_softc *); -extern void pxa2x0_apm_attach_sub(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_int32_t, int); #endif diff --git a/sys/arch/arm/xscale/pxa2x0_apm_asm.S b/sys/arch/arm/xscale/pxa2x0_apm_asm.S new file mode 100644 index 00000000000..d4c79ec4484 --- /dev/null +++ b/sys/arch/arm/xscale/pxa2x0_apm_asm.S @@ -0,0 +1,577 @@ +/* $OpenBSD: pxa2x0_apm_asm.S,v 1.1 2005/02/22 21:53:03 uwe 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 + +/* 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 /* XXX does exactly what? */ + + mov r1, #DCACHE_CACHELINECOUNT + ldr r0, .Lxscale_cache_clean_addr + +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 + */ +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 diff --git a/sys/arch/arm/xscale/pxa2x0_lcd.c b/sys/arch/arm/xscale/pxa2x0_lcd.c index 06f52f1b149..c7f55b49d83 100644 --- a/sys/arch/arm/xscale/pxa2x0_lcd.c +++ b/sys/arch/arm/xscale/pxa2x0_lcd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pxa2x0_lcd.c,v 1.15 2005/02/17 23:16:33 drahn Exp $ */ +/* $OpenBSD: pxa2x0_lcd.c,v 1.16 2005/02/22 21:53:03 uwe Exp $ */ /* $NetBSD: pxa2x0_lcd.c,v 1.8 2003/10/03 07:24:05 bsh Exp $ */ /* @@ -89,6 +89,9 @@ void pxa2x0_lcd_setup_rasops(struct rasops_info *, void pxa2x0_lcd_start_dma(bus_space_tag_t, bus_space_handle_t, struct pxa2x0_lcd_screen *); void pxa2x0_lcd_stop_dma(bus_space_tag_t, bus_space_handle_t); +void pxa2x0_lcd_suspend(struct pxa2x0_lcd_softc *); +void pxa2x0_lcd_resume(struct pxa2x0_lcd_softc *); +void pxa2x0_lcd_powerhook(int, void *); /* * Setup display geometry parameters. @@ -261,6 +264,8 @@ pxa2x0_lcd_attach_sub(struct pxa2x0_lcd_softc *sc, bzero(&dummy, sizeof(dummy)); pxa2x0_lcd_setup_rasops(&dummy, descr, geom); } + + (void)powerhook_establish(pxa2x0_lcd_powerhook, sc); } /* @@ -817,3 +822,44 @@ pxa2x0_lcd_mmap(void *v, off_t offset, int prot) return (bus_dmamem_mmap(sc->dma_tag, screen->segs, screen->nsegs, offset, prot, BUS_DMA_WAITOK | BUS_DMA_COHERENT)); } + +void +pxa2x0_lcd_suspend(struct pxa2x0_lcd_softc *sc) +{ + + if (sc->active != NULL) { + pxa2x0_lcd_stop_dma(sc->iot, sc->ioh); + pxa2x0_clkman_config(CKEN_LCD, 0); + delay(1000000); /* XXX */ + } +} + +void +pxa2x0_lcd_resume(struct pxa2x0_lcd_softc *sc) +{ + + if (sc->active != NULL) { + pxa2x0_lcd_initialize(sc->iot, sc->ioh, sc->geometry, + pxa2x0_clkman_config); + pxa2x0_lcd_start_dma(sc->iot, sc->ioh, sc->active); + /* XXX wait here to avoid a weird fade-in effect. */ + delay(1000000); + } +} + +void +pxa2x0_lcd_powerhook(int why, void *v) +{ + struct pxa2x0_lcd_softc *sc = v; + + switch (why) { + case PWR_SUSPEND: + case PWR_STANDBY: + pxa2x0_lcd_suspend(sc); + break; + + case PWR_RESUME: + pxa2x0_lcd_resume(sc); + break; + } +} diff --git a/sys/arch/arm/xscale/pxa2x0reg.h b/sys/arch/arm/xscale/pxa2x0reg.h index cd0c053c352..c614a8d4823 100644 --- a/sys/arch/arm/xscale/pxa2x0reg.h +++ b/sys/arch/arm/xscale/pxa2x0reg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pxa2x0reg.h,v 1.11 2005/02/17 22:10:35 dlg Exp $ */ +/* $OpenBSD: pxa2x0reg.h,v 1.12 2005/02/22 21:53:03 uwe Exp $ */ /* $NetBSD: pxa2x0reg.h,v 1.4 2003/06/11 20:43:01 scw Exp $ */ /* @@ -107,7 +107,7 @@ #define PXA2X0_GPIO_BASE 0x40e00000 #define PXA2X0_GPIO_SIZE 0x70 #define PXA2X0_POWMAN_BASE 0x40f00000 /* Power management */ -#define PXA2X0_POWMAN_SIZE 0x100 +#define PXA2X0_POWMAN_SIZE 0x1a4 /* incl. PI2C unit */ #define PXA2X0_SSP_BASE 0x41000000 /* SSP serial port */ #define PXA2X0_SSP1_BASE 0x41700000 /* PXA270 */ #define PXA2X0_SSP2_BASE 0x41900000 /* PXA270 */ @@ -153,6 +153,15 @@ #define PXA2X0_INT_RTCHZ 30 #define PXA2X0_INT_ALARM 31 /* RTC Alarm interrupt */ +/* Interrupt Controller similar to SA11x0's, but not exactly the same. */ +#define INTCTL_ICIP 0x00 +#define INTCTL_ICMR 0x04 +#define INTCTL_ICLR 0x08 +#define INTCTL_ICFP 0x0c +#define INTCTL_ICPR 0x10 +#define INTCTL_ICCR 0x14 +#define ICCR_DIM (1<<0) + /* DMAC */ #define DMAC_N_CHANNELS 16 #define DMAC_N_PRIORITIES 3 @@ -221,25 +230,59 @@ struct pxa2x0_dma_desc { #define I2C_ISAR 0x16a0 /* Slave address */ /* Power Manager */ +#define POWMAN_PMCR 0x00 #define POWMAN_PSSR 0x04 /* Sleep Status register */ #define PSSR_RDH (1<<5) +#define POWMAN_PSPR 0x08 +#define POWMAN_PWER 0x0c +#define POWMAN_PRER 0x10 +#define POWMAN_PFER 0x14 +#define POWMAN_PEDR 0x18 #define POWMAN_PCFR 0x1c /* General Configuration register */ -#define PCFR_GRP_EN (1<<4) /* PXA270 */ +#define PCFR_OPDE (1<<0) +#define PCFR_GPR_EN (1<<4) /* PXA270 */ +#define PCFR_PI2C_EN (1<<6) /* PXA270 */ #define PCFR_GP_ROD (1<<8) /* PXA270 */ +#define PCFR_FVC (1<<10) /* PXA270 */ +#define POWMAN_PGSR0 0x20 /* GPIO Sleep State register */ +#define POWMAN_PGSR1 0x24 +#define POWMAN_PGSR2 0x28 +#define POWMAN_PGSR3 0x2c /* PXA270 */ #define POWMAN_RCSR 0x30 /* Reset Controller Status register */ #define RCSR_HWR (1<<0) #define RCSR_WDR (1<<1) #define RCSR_SMR (1<<2) #define RCSR_GPR (1<<3) +#define POWMAN_PSLR 0x34 /* PXA270 */ +#define POWMAN_PKWR 0x50 /* PXA270 */ +#define POWMAN_PKSR 0x54 /* PXA270 */ + +/* Power Manager I2C unit */ +#define POWMAN_PIDBR 0x188 +#define POWMAN_PICR 0x190 +#define PICR_START ICR_START +#define PICR_STOP ICR_STOP +#define PICR_ACKNAK ICR_ACKNAK +#define PICR_TB ICR_TB +#define PICR_SCLE (1<<5) /* PXA270? */ +#define PICR_IUE (1<<6) /* PXA270? */ +#define PICR_UR (1<<14) /* PXA270? */ +#define POWMAN_PISR 0x198 +#define PISR_ACKNAK (1<<1) +#define PISR_ITE (1<<6) +#define PISR_IRF (1<<7) +#define POWMAN_PISAR 0x1a0 /* Clock Manager */ #define CLKMAN_CCCR 0x00 /* Core Clock Configuration */ -#define CCCR_CPDIS (1<<31) /* PXA270 */ +#define CCCR_CPDIS (1<<31) /* PXA270 */ +#define CCCR_A (1<<25) /* PXA270 */ #define CCCR_TURBO_X1 (2<<7) #define CCCR_TURBO_X15 (3<<7) /* x 1.5 */ #define CCCR_TURBO_X2 (4<<7) #define CCCR_TURBO_X25 (5<<7) /* x 2.5 */ #define CCCR_TURBO_X3 (6<<7) /* x 3.0 */ +/* PXA255 */ #define CCCR_RUN_X1 (1<<5) #define CCCR_RUN_X2 (2<<5) #define CCCR_RUN_X4 (3<<5) @@ -249,6 +292,10 @@ struct pxa2x0_dma_desc { #define CCCR_MEM_X40 (4<<0) /* x27, 99.53MHz */ #define CCCR_MEM_X45 (5<<0) /* x27, 99.53MHz */ #define CCCR_MEM_X9 (0x1f<<0) /* x9, 33.2MHz */ +/* PXA27x: L is the core run frequency to 13Mhz oscillator ratio. */ +#define CCCR_RUN_X7 (7<<0) /* 91Mhz, 91Mhz mem, 91Mhz LCD */ +#define CCCR_RUN_X8 (8<<0) /* 104Mhz, 104Mhz mem, 52Mhz LCD */ +#define CCCR_RUN_X16 (16<<0) /* 208Mhz, 104/208Mhz mem, 104Mhz LCD */ #define CLKMAN_CKEN 0x04 /* Clock Enable Register */ #define CLKMAN_OSCC 0x08 /* Osillcator Configuration Register */ @@ -272,7 +319,10 @@ struct pxa2x0_dma_desc { #define CKEN_MMC (1<<12) #define CKEN_FICP (1<<13) #define CKEN_I2C (1<<14) +#define CKEN_PI2C (1<<15) /* PXA270? */ #define CKEN_LCD (1<<16) +#define CKEN_KEY (1<<19) /* PXA270? */ +#define CKEN_MEM (1<<22) /* PXA270? */ #define OSCC_OOK (1<<0) /* 32.768KHz oscillator status */ #define OSCC_OON (1<<1) /* 32.768KHz oscillator */ @@ -283,6 +333,9 @@ struct pxa2x0_dma_desc { #define RTC_RCNR 0x0000 /* count register */ #define RTC_RTAR 0x0004 /* alarm register */ #define RTC_RTSR 0x0008 /* status register */ +#define RTSR_AL (1<<0) +#define RTSR_HZ (1<<1) +#define RTSR_ALE (1<<2) #define RTC_RTTR 0x000c /* trim register */ /* * GPIO @@ -689,6 +742,7 @@ struct pxa2x0_dma_desc { #define OST_OSMR2 0x0008 /* Match 2 */ #define OST_OSMR3 0x000c /* Match 3 */ #define OST_OSCR0 0x0010 /* Counter 0 */ +#define OST_OSSR 0x0014 /* Status (all counters) */ #define OST_OWER 0x0018 /* Watchdog Enable */ #define OWER_WME (1<<0) #define OST_OIER 0x001c /* Interrupt Enable */ |