1 /* $NetBSD: powsw.c,v 1.4 2022/07/16 04:55:35 isaki Exp $ */ 2 3 /* 4 * Copyright (c) 2011 Tetsuya Isaki. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 /* 29 * Power switch monitor 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: powsw.c,v 1.4 2022/07/16 04:55:35 isaki Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/conf.h> 38 #include <sys/device.h> 39 #include <sys/intr.h> 40 #include <sys/callout.h> 41 42 #include <machine/bus.h> 43 #include <machine/cpu.h> 44 45 #include <arch/x68k/dev/intiovar.h> 46 #include <arch/x68k/dev/mfp.h> 47 48 #include <dev/sysmon/sysmonvar.h> 49 #include <dev/sysmon/sysmon_taskq.h> 50 51 #include "ioconf.h" 52 53 //#define POWSW_DEBUG 54 55 #if defined(POWSW_DEBUG) 56 #define DPRINTF(fmt...) printf(fmt) 57 #define DEBUG_LOG_ADD(c) sc->sc_log[sc->sc_loglen++] = (c) 58 #define DEBUG_LOG_PRINT() do { \ 59 sc->sc_log[sc->sc_loglen] = '\0'; \ 60 printf("%s", sc->sc_log); \ 61 } while (0) 62 #else 63 #define DPRINTF(fmt...) 64 #define DEBUG_LOG_ADD(c) 65 #define DEBUG_LOG_PRINT() 66 #endif 67 68 /* mask */ 69 #define POWSW_ALARM (0x01) 70 #define POWSW_EXTERNAL (0x02) 71 #define POWSW_FRONT (0x04) 72 73 /* parameter */ 74 #define POWSW_MAX_TICK (30) 75 #define POWSW_THRESHOLD (10) 76 77 struct powsw_softc { 78 device_t sc_dev; 79 struct sysmon_pswitch sc_smpsw; 80 callout_t sc_callout; 81 int sc_mask; 82 int sc_prev; 83 int sc_last_sw; 84 int sc_tick; 85 int sc_count; 86 #if defined(POWSW_DEBUG) 87 char sc_log[100]; 88 int sc_loglen; 89 #endif 90 }; 91 92 static int powsw_match(device_t, cfdata_t, void *); 93 static void powsw_attach(device_t, device_t, void *); 94 static int powsw_intr(void *); 95 static void powsw_softintr(void *); 96 static void powsw_pswitch_event(void *); 97 static void powsw_reset_counter(struct powsw_softc *); 98 static void powsw_set_aer(struct powsw_softc *, int); 99 100 CFATTACH_DECL_NEW(powsw, sizeof(struct powsw_softc), 101 powsw_match, powsw_attach, NULL, NULL); 102 103 104 typedef const struct { 105 int vector; /* interrupt vector */ 106 int mask; /* mask bit for MFP GPIP */ 107 const char *name; 108 } powsw_desc_t; 109 110 static powsw_desc_t powsw_desc[2] = { 111 { 66, POWSW_FRONT, "Front Switch", }, 112 { 65, POWSW_EXTERNAL, "External Power Switch", }, 113 /* XXX I'm not sure about alarm bit */ 114 }; 115 116 117 static int 118 powsw_match(device_t parent, cfdata_t cf, void *aux) 119 { 120 121 return 1; 122 } 123 124 static void 125 powsw_attach(device_t parent, device_t self, void *aux) 126 { 127 struct powsw_softc *sc = device_private(self); 128 powsw_desc_t *desc; 129 const char *xname; 130 int unit; 131 int sw; 132 133 unit = device_unit(self); 134 xname = device_xname(self); 135 desc = &powsw_desc[unit]; 136 137 memset(sc, 0, sizeof(*sc)); 138 sc->sc_dev = self; 139 sc->sc_mask = desc->mask; 140 sc->sc_prev = -1; 141 powsw_reset_counter(sc); 142 143 sysmon_task_queue_init(); 144 sc->sc_smpsw.smpsw_name = xname; 145 sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_POWER; 146 if (sysmon_pswitch_register(&sc->sc_smpsw) != 0) 147 panic("can't register with sysmon"); 148 149 callout_init(&sc->sc_callout, 0); 150 callout_setfunc(&sc->sc_callout, powsw_softintr, sc); 151 152 if (intio_intr_establish(desc->vector, xname, powsw_intr, sc) < 0) 153 panic("%s: can't establish interrupt", xname); 154 155 /* Set AER and enable interrupt */ 156 sw = (mfp_get_gpip() & sc->sc_mask); 157 powsw_set_aer(sc, sw ? 0 : 1); 158 mfp_bit_set_ierb(sc->sc_mask); 159 160 aprint_normal(": %s\n", desc->name); 161 } 162 163 static int 164 powsw_intr(void *arg) 165 { 166 struct powsw_softc *sc = arg; 167 168 if (sc->sc_tick == 0) { 169 mfp_bit_clear_ierb(sc->sc_mask); 170 sc->sc_tick++; 171 DEBUG_LOG_ADD('i'); 172 /* 173 * The button state seems unstable for few ticks, 174 * so wait a bit to settle. 175 */ 176 callout_schedule(&sc->sc_callout, 1); 177 } else { 178 DEBUG_LOG_ADD('x'); 179 } 180 return 0; 181 } 182 183 void 184 powsw_softintr(void *arg) 185 { 186 struct powsw_softc *sc = arg; 187 int sw; 188 int s; 189 190 s = spl6(); 191 192 if (sc->sc_tick++ >= POWSW_MAX_TICK) { 193 /* tick is over, broken switch? */ 194 printf("%s: unstable power switch?, ignored\n", 195 device_xname(sc->sc_dev)); 196 powsw_reset_counter(sc); 197 198 mfp_bit_set_ierb(sc->sc_mask); 199 splx(s); 200 return; 201 } 202 203 sw = (mfp_get_gpip() & sc->sc_mask) ? 1 : 0; 204 DEBUG_LOG_ADD('0' + sw); 205 206 if (sw == sc->sc_last_sw) { 207 sc->sc_count++; 208 } else { 209 sc->sc_last_sw = sw; 210 sc->sc_count = 1; 211 } 212 213 if (sc->sc_count < POWSW_THRESHOLD) { 214 callout_schedule(&sc->sc_callout, 1); 215 } else { 216 /* switch seems stable */ 217 DEBUG_LOG_PRINT(); 218 219 if (sc->sc_last_sw == sc->sc_prev) { 220 /* switch state is not changed, it was a noise */ 221 DPRINTF(" ignore(sw=%d,prev=%d)\n", 222 sc->sc_last_sw, sc->sc_prev); 223 } else { 224 /* switch state has been changed */ 225 sc->sc_prev = sc->sc_last_sw; 226 powsw_set_aer(sc, 1 - sc->sc_prev); 227 sysmon_task_queue_sched(0, powsw_pswitch_event, sc); 228 } 229 powsw_reset_counter(sc); 230 /* enable interrupt */ 231 mfp_bit_set_ierb(sc->sc_mask); 232 } 233 234 splx(s); 235 } 236 237 static void 238 powsw_pswitch_event(void *arg) 239 { 240 struct powsw_softc *sc = arg; 241 int poweroff; 242 243 poweroff = sc->sc_prev; 244 245 DPRINTF(" %s is %s\n", device_xname(sc->sc_dev), 246 poweroff ? "off(PRESS)" : "on(RELEASE)"); 247 248 sysmon_pswitch_event(&sc->sc_smpsw, 249 poweroff ? PSWITCH_EVENT_PRESSED : PSWITCH_EVENT_RELEASED); 250 } 251 252 static void 253 powsw_reset_counter(struct powsw_softc *sc) 254 { 255 256 sc->sc_last_sw = -1; 257 sc->sc_tick = 0; 258 sc->sc_count = 0; 259 #if defined(POWSW_DEBUG) 260 sc->sc_loglen = 0; 261 #endif 262 } 263 264 static void 265 powsw_set_aer(struct powsw_softc *sc, int aer) 266 { 267 268 KASSERT(aer == 0 || aer == 1); 269 270 if (aer == 0) { 271 mfp_bit_clear_aer(sc->sc_mask); 272 } else { 273 mfp_bit_set_aer(sc->sc_mask); 274 } 275 DPRINTF(" SetAER=%d", aer); 276 } 277