1 /* $NetBSD: tx39power.c,v 1.20 2012/02/12 16:34:08 matt Exp $ */ 2 3 /*- 4 * Copyright (c) 1999, 2000 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by UCHIYAMA Yasushi. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: tx39power.c,v 1.20 2012/02/12 16:34:08 matt Exp $"); 34 35 #include "opt_tx39power_debug.h" 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/device.h> 40 41 #include <machine/bus.h> 42 #include <machine/intr.h> 43 #include <machine/config_hook.h> 44 45 #include <hpcmips/tx/tx39var.h> 46 #include <hpcmips/tx/tx39icureg.h> 47 #include <hpcmips/tx/tx39powerreg.h> 48 49 #ifdef TX39POWER_DEBUG 50 #define DPRINTF_ENABLE 51 #define DPRINTF_DEBUG tx39power_debug 52 #endif 53 #include <machine/debug.h> 54 55 #ifdef TX39POWER_DEBUG 56 #define DUMP_REGS(x) __tx39power_dump(x) 57 #else 58 #define DUMP_REGS(x) ((void)0) 59 #endif 60 61 #define ISSETPRINT(r, m) dbg_bitmask_print(r, TX39_POWERCTRL_##m, #m) 62 63 int tx39power_match(struct device *, struct cfdata *, void *); 64 void tx39power_attach(struct device *, struct device *, void *); 65 66 struct tx39power_softc { 67 struct device sc_dev; 68 tx_chipset_tag_t sc_tc; 69 70 /* save interrupt status for resume */ 71 txreg_t sc_icu_state[TX39_INTRSET_MAX + 1]; 72 }; 73 74 CFATTACH_DECL(tx39power, sizeof(struct tx39power_softc), 75 tx39power_match, tx39power_attach, NULL, NULL); 76 77 void tx39power_suspend_cpu(void); /* automatic hardware resume */ 78 79 static int tx39power_intr_p(void *); 80 static int tx39power_intr_n(void *); 81 static int tx39power_ok_intr_p(void *); 82 static int tx39power_ok_intr_n(void *); 83 static int tx39power_button_intr_p(void *); 84 static int tx39power_button_intr_n(void *); 85 #ifdef TX39POWER_DEBUG 86 static void __tx39power_dump(struct tx39power_softc *); 87 #endif 88 89 int 90 tx39power_match(struct device *parent, struct cfdata *cf, void *aux) 91 { 92 return (ATTACH_FIRST); 93 } 94 95 void 96 tx39power_attach(struct device *parent, struct device *self, void *aux) 97 { 98 struct txsim_attach_args *ta = aux; 99 struct tx39power_softc *sc = (void*)self; 100 tx_chipset_tag_t tc; 101 txreg_t reg; 102 103 tc = sc->sc_tc = ta->ta_tc; 104 tx_conf_register_power(tc, self); 105 106 printf("\n"); 107 DUMP_REGS(sc); 108 109 /* power button setting */ 110 reg = tx_conf_read(tc, TX39_POWERCTRL_REG); 111 reg |= TX39_POWERCTRL_DBNCONBUTN; 112 tx_conf_write(tc, TX39_POWERCTRL_REG, reg); 113 114 /* enable stop timer */ 115 reg = tx_conf_read(tc, TX39_POWERCTRL_REG); 116 reg &= ~(TX39_POWERCTRL_STPTIMERVAL_MASK << 117 TX39_POWERCTRL_STPTIMERVAL_SHIFT); 118 reg = TX39_POWERCTRL_STPTIMERVAL_SET(reg, 119 TX39_POWERCTRL_STPTIMERVAL_MAX); 120 reg |= TX39_POWERCTRL_ENSTPTIMER; 121 tx_conf_write(tc, TX39_POWERCTRL_REG, reg); 122 123 /* install power event handler */ 124 /* low priority */ 125 tx_intr_establish(tc, MAKEINTR(5, TX39_INTRSTATUS5_POSPWRINT), 126 IST_EDGE, IPL_CLOCK, 127 tx39power_intr_p, sc); 128 tx_intr_establish(tc, MAKEINTR(5, TX39_INTRSTATUS5_NEGPWRINT), 129 IST_EDGE, IPL_CLOCK, 130 tx39power_intr_n, sc); 131 /* high priority */ 132 tx_intr_establish(tc, MAKEINTR(5, TX39_INTRSTATUS5_POSPWROKINT), 133 IST_EDGE, IPL_CLOCK, 134 tx39power_ok_intr_p, sc); 135 tx_intr_establish(tc, MAKEINTR(5, TX39_INTRSTATUS5_NEGPWROKINT), 136 IST_EDGE, IPL_CLOCK, 137 tx39power_ok_intr_n, sc); 138 /* user driven event */ 139 tx_intr_establish(tc, MAKEINTR(5, TX39_INTRSTATUS5_POSONBUTNINT), 140 IST_EDGE, IPL_CLOCK, 141 tx39power_button_intr_p, sc); 142 tx_intr_establish(tc, MAKEINTR(5, TX39_INTRSTATUS5_NEGONBUTNINT), 143 IST_EDGE, IPL_CLOCK, 144 tx39power_button_intr_n, sc); 145 } 146 147 void 148 tx39power_suspend_cpu(void) /* I assume already splhigh */ 149 { 150 tx_chipset_tag_t tc = tx_conf_get_tag(); 151 struct tx39power_softc *sc = tc->tc_powert; 152 txreg_t reg, *iregs = sc->sc_icu_state; 153 154 printf ("%s: CPU sleep\n", sc->sc_dev.dv_xname); 155 __asm volatile(".set noreorder"); 156 reg = tx_conf_read(tc, TX39_POWERCTRL_REG); 157 reg |= TX39_POWERCTRL_STOPCPU; 158 /* save interrupt state */ 159 iregs[0] = tx_conf_read(tc, TX39_INTRENABLE6_REG); 160 iregs[1] = tx_conf_read(tc, TX39_INTRENABLE1_REG); 161 iregs[2] = tx_conf_read(tc, TX39_INTRENABLE2_REG); 162 iregs[3] = tx_conf_read(tc, TX39_INTRENABLE3_REG); 163 iregs[4] = tx_conf_read(tc, TX39_INTRENABLE4_REG); 164 iregs[5] = tx_conf_read(tc, TX39_INTRENABLE5_REG); 165 #ifdef TX392X 166 iregs[7] = tx_conf_read(tc, TX39_INTRENABLE7_REG); 167 iregs[8] = tx_conf_read(tc, TX39_INTRENABLE8_REG); 168 #endif 169 /* disable all interrupt (don't disable GLOBALEN) */ 170 tx_conf_write(tc, TX39_INTRENABLE6_REG, TX39_INTRENABLE6_GLOBALEN); 171 tx_conf_write(tc, TX39_INTRENABLE1_REG, 0); 172 tx_conf_write(tc, TX39_INTRENABLE2_REG, 0); 173 tx_conf_write(tc, TX39_INTRENABLE3_REG, 0); 174 tx_conf_write(tc, TX39_INTRENABLE4_REG, 0); 175 tx_conf_write(tc, TX39_INTRENABLE5_REG, 0); 176 #ifdef TX392X 177 tx_conf_write(tc, TX39_INTRENABLE7_REG, 0); 178 tx_conf_write(tc, TX39_INTRENABLE8_REG, 0); 179 #endif 180 /* enable power button interrupt only */ 181 tx_conf_write(tc, TX39_INTRCLEAR5_REG, TX39_INTRSTATUS5_NEGONBUTNINT); 182 tx_conf_write(tc, TX39_INTRENABLE5_REG, TX39_INTRSTATUS5_NEGONBUTNINT); 183 __asm volatile(".set push; .set mips2; sync; .set pop"); 184 185 /* stop CPU clock */ 186 tx_conf_write(tc, TX39_POWERCTRL_REG, reg); 187 __asm volatile(".set push; .set mips2; sync; .set pop"); 188 /* wait until power button pressed */ 189 /* clear interrupt */ 190 tx_conf_write(tc, TX39_INTRCLEAR5_REG, TX39_INTRSTATUS5_NEGONBUTNINT); 191 #ifdef TX392X 192 /* Clear WARMSTART bit to reset vector(0xbfc00000) work correctly */ 193 reg = tx_conf_read(tc, TX39_POWERCTRL_REG); 194 reg &= ~TX39_POWERCTRL_WARMSTART; 195 tx_conf_write(tc, TX39_POWERCTRL_REG, reg); 196 #endif 197 198 /* restore interrupt state */ 199 tx_conf_write(tc, TX39_INTRENABLE6_REG, iregs[0]); 200 tx_conf_write(tc, TX39_INTRENABLE1_REG, iregs[1]); 201 tx_conf_write(tc, TX39_INTRENABLE2_REG, iregs[2]); 202 tx_conf_write(tc, TX39_INTRENABLE3_REG, iregs[3]); 203 tx_conf_write(tc, TX39_INTRENABLE4_REG, iregs[4]); 204 tx_conf_write(tc, TX39_INTRENABLE5_REG, iregs[5]); 205 #ifdef TX392X 206 tx_conf_write(tc, TX39_INTRENABLE7_REG, iregs[7]); 207 tx_conf_write(tc, TX39_INTRENABLE8_REG, iregs[8]); 208 #endif 209 __asm volatile(".set reorder"); 210 211 printf ("%s: CPU wakeup\n", sc->sc_dev.dv_xname); 212 } 213 214 static int 215 tx39power_button_intr_p(void *arg) 216 { 217 config_hook_call(CONFIG_HOOK_BUTTONEVENT, 218 CONFIG_HOOK_BUTTONEVENT_POWER, 219 (void *)1 /* on */); 220 221 return (0); 222 } 223 224 static int 225 tx39power_button_intr_n(void *arg) 226 { 227 config_hook_call(CONFIG_HOOK_BUTTONEVENT, 228 CONFIG_HOOK_BUTTONEVENT_POWER, 229 (void *)0 /* off */); 230 DUMP_REGS(arg); 231 232 return (0); 233 } 234 235 int 236 tx39power_intr_p(void *arg) 237 { 238 /* low priority event */ 239 printf("power_p\n"); 240 DUMP_REGS(arg); 241 242 return (0); 243 } 244 245 static int 246 tx39power_intr_n(void *arg) 247 { 248 /* low priority event */ 249 printf("power_n\n"); 250 DUMP_REGS(arg); 251 252 return (0); 253 } 254 255 static int 256 tx39power_ok_intr_p(void *arg) 257 { 258 /* high priority event */ 259 printf("power NG\n"); 260 DUMP_REGS(arg); 261 config_hook_call(CONFIG_HOOK_PMEVENT, 262 CONFIG_HOOK_PMEVENT_SUSPENDREQ, NULL); 263 264 return (0); 265 } 266 267 static int 268 tx39power_ok_intr_n(void *arg) 269 { 270 /* high priority event */ 271 printf("power OK\n"); 272 DUMP_REGS(arg); 273 274 return (0); 275 } 276 277 #ifdef TX39POWER_DEBUG 278 static void 279 __tx39power_dump (struct tx39power_softc *sc) 280 { 281 tx_chipset_tag_t tc = sc->sc_tc; 282 txreg_t reg; 283 284 reg = tx_conf_read(tc, TX39_POWERCTRL_REG); 285 ISSETPRINT(reg, ONBUTN); 286 ISSETPRINT(reg, PWRINT); 287 ISSETPRINT(reg, PWROK); 288 #ifdef TX392X 289 ISSETPRINT(reg, PWROKNMI); 290 #endif /* TX392X */ 291 ISSETPRINT(reg, SLOWBUS); 292 #ifdef TX391X 293 ISSETPRINT(reg, DIVMOD); 294 #endif /* TX391X */ 295 ISSETPRINT(reg, ENSTPTIMER); 296 ISSETPRINT(reg, ENFORCESHUTDWN); 297 ISSETPRINT(reg, FORCESHUTDWN); 298 ISSETPRINT(reg, FORCESHUTDWNOCC); 299 ISSETPRINT(reg, SELC2MS); 300 #ifdef TX392X 301 ISSETPRINT(reg, WARMSTART); 302 #endif /* TX392X */ 303 ISSETPRINT(reg, BPDBVCC3); 304 ISSETPRINT(reg, STOPCPU); 305 ISSETPRINT(reg, DBNCONBUTN); 306 ISSETPRINT(reg, COLDSTART); 307 ISSETPRINT(reg, PWRCS); 308 ISSETPRINT(reg, VCCON); 309 #ifdef TX391X 310 printf("VIDRF=%d ", TX39_POWERCTRL_VIDRF(reg)); 311 #endif /* TX391X */ 312 printf("STPTIMERVAL=%d ", TX39_POWERCTRL_STPTIMERVAL(reg)); 313 printf("\n"); 314 } 315 #endif /* TX39POWER_DEBUG */ 316