Home | History | Annotate | Line # | Download | only in dev
powsw.c revision 1.3
      1 /*	$NetBSD: powsw.c,v 1.3 2022/07/16 04:49:07 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.3 2022/07/16 04:49:07 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 extern int power_switch_is_off;		/* XXX should be in .h */
     54 
     55 //#define POWSW_DEBUG
     56 
     57 #if defined(POWSW_DEBUG)
     58 #define DPRINTF(fmt...)		printf(fmt)
     59 #define DEBUG_LOG_ADD(c)	sc->sc_log[sc->sc_loglen++] = (c)
     60 #define DEBUG_LOG_PRINT()	do {	\
     61 	sc->sc_log[sc->sc_loglen] = '\0';	\
     62 	printf("%s", sc->sc_log);	\
     63 } while (0)
     64 #else
     65 #define DPRINTF(fmt...)
     66 #define DEBUG_LOG_ADD(c)
     67 #define DEBUG_LOG_PRINT()
     68 #endif
     69 
     70 /* mask */
     71 #define POWSW_ALARM		(0x01)
     72 #define POWSW_EXTERNAL		(0x02)
     73 #define POWSW_FRONT		(0x04)
     74 
     75 /* parameter */
     76 #define POWSW_MAX_TICK		(30)
     77 #define POWSW_THRESHOLD		(10)
     78 
     79 struct powsw_softc {
     80 	device_t sc_dev;
     81 	struct sysmon_pswitch sc_smpsw;
     82 	callout_t sc_callout;
     83 	int sc_mask;
     84 	int sc_prev;
     85 	int sc_last_sw;
     86 	int sc_tick;
     87 	int sc_count;
     88 #if defined(POWSW_DEBUG)
     89 	char sc_log[100];
     90 	int sc_loglen;
     91 #endif
     92 };
     93 
     94 static int  powsw_match(device_t, cfdata_t, void *);
     95 static void powsw_attach(device_t, device_t, void *);
     96 static int  powsw_intr(void *);
     97 static void powsw_softintr(void *);
     98 static void powsw_pswitch_event(void *);
     99 static void powsw_shutdown_check(void *);
    100 static void powsw_reset_counter(struct powsw_softc *);
    101 static void powsw_set_aer(struct powsw_softc *, int);
    102 
    103 CFATTACH_DECL_NEW(powsw, sizeof(struct powsw_softc),
    104     powsw_match, powsw_attach, NULL, NULL);
    105 
    106 
    107 typedef const struct {
    108 	int vector;			/* interrupt vector */
    109 	int mask;			/* mask bit for MFP GPIP */
    110 	const char *name;
    111 } powsw_desc_t;
    112 
    113 static powsw_desc_t powsw_desc[2] = {
    114 	{ 66, POWSW_FRONT,	"Front Switch", },
    115 	{ 65, POWSW_EXTERNAL,	"External Power Switch", },
    116 	/* XXX I'm not sure about alarm bit */
    117 };
    118 
    119 
    120 static int
    121 powsw_match(device_t parent, cfdata_t cf, void *aux)
    122 {
    123 
    124 	return 1;
    125 }
    126 
    127 static void
    128 powsw_attach(device_t parent, device_t self, void *aux)
    129 {
    130 	struct powsw_softc *sc = device_private(self);
    131 	powsw_desc_t *desc;
    132 	const char *xname;
    133 	int unit;
    134 	int sw;
    135 
    136 	unit = device_unit(self);
    137 	xname = device_xname(self);
    138 	desc = &powsw_desc[unit];
    139 
    140 	memset(sc, 0, sizeof(*sc));
    141 	sc->sc_dev = self;
    142 	sc->sc_mask = desc->mask;
    143 	sc->sc_prev = -1;
    144 	powsw_reset_counter(sc);
    145 
    146 	sysmon_task_queue_init();
    147 	sc->sc_smpsw.smpsw_name = xname;
    148 	sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_POWER;
    149 	if (sysmon_pswitch_register(&sc->sc_smpsw) != 0)
    150 		panic("can't register with sysmon");
    151 
    152 	callout_init(&sc->sc_callout, 0);
    153 	callout_setfunc(&sc->sc_callout, powsw_softintr, sc);
    154 
    155 	if (shutdownhook_establish(powsw_shutdown_check, sc) == NULL)
    156 		panic("%s: can't establish shutdown hook", xname);
    157 
    158 	if (intio_intr_establish(desc->vector, xname, powsw_intr, sc) < 0)
    159 		panic("%s: can't establish interrupt", xname);
    160 
    161 	/* Set AER and enable interrupt */
    162 	sw = (mfp_get_gpip() & sc->sc_mask);
    163 	powsw_set_aer(sc, sw ? 0 : 1);
    164 	mfp_bit_set_ierb(sc->sc_mask);
    165 
    166 	aprint_normal(": %s\n", desc->name);
    167 }
    168 
    169 static int
    170 powsw_intr(void *arg)
    171 {
    172 	struct powsw_softc *sc = arg;
    173 
    174 	if (sc->sc_tick == 0) {
    175 		mfp_bit_clear_ierb(sc->sc_mask);
    176 		sc->sc_tick++;
    177 		DEBUG_LOG_ADD('i');
    178 		/*
    179 		 * The button state seems unstable for few ticks,
    180 		 * so wait a bit to settle.
    181 		 */
    182 		callout_schedule(&sc->sc_callout, 1);
    183 	} else {
    184 		DEBUG_LOG_ADD('x');
    185 	}
    186 	return 0;
    187 }
    188 
    189 void
    190 powsw_softintr(void *arg)
    191 {
    192 	struct powsw_softc *sc = arg;
    193 	int sw;
    194 	int s;
    195 
    196 	s = spl6();
    197 
    198 	if (sc->sc_tick++ >= POWSW_MAX_TICK) {
    199 		/* tick is over, broken switch? */
    200 		printf("%s: unstable power switch?, ignored\n",
    201 		    device_xname(sc->sc_dev));
    202 		powsw_reset_counter(sc);
    203 
    204 		mfp_bit_set_ierb(sc->sc_mask);
    205 		splx(s);
    206 		return;
    207 	}
    208 
    209 	sw = (mfp_get_gpip() & sc->sc_mask) ? 1 : 0;
    210 	DEBUG_LOG_ADD('0' + sw);
    211 
    212 	if (sw == sc->sc_last_sw) {
    213 		sc->sc_count++;
    214 	} else {
    215 		sc->sc_last_sw = sw;
    216 		sc->sc_count = 1;
    217 	}
    218 
    219 	if (sc->sc_count < POWSW_THRESHOLD) {
    220 		callout_schedule(&sc->sc_callout, 1);
    221 	} else {
    222 		/* switch seems stable */
    223 		DEBUG_LOG_PRINT();
    224 
    225 		if (sc->sc_last_sw == sc->sc_prev) {
    226 			/* switch state is not changed, it was a noise */
    227 			DPRINTF(" ignore(sw=%d,prev=%d)\n",
    228 			    sc->sc_last_sw, sc->sc_prev);
    229 		} else {
    230 			/* switch state has been changed */
    231 			sc->sc_prev = sc->sc_last_sw;
    232 			powsw_set_aer(sc, 1 - sc->sc_prev);
    233 			sysmon_task_queue_sched(0, powsw_pswitch_event, sc);
    234 		}
    235 		powsw_reset_counter(sc);
    236 		/* enable interrupt */
    237 		mfp_bit_set_ierb(sc->sc_mask);
    238 	}
    239 
    240 	splx(s);
    241 }
    242 
    243 static void
    244 powsw_pswitch_event(void *arg)
    245 {
    246 	struct powsw_softc *sc = arg;
    247 	int poweroff;
    248 
    249 	poweroff = sc->sc_prev;
    250 
    251 	DPRINTF(" %s is %s\n", device_xname(sc->sc_dev),
    252 	    poweroff ? "off(PRESS)" : "on(RELEASE)");
    253 
    254 	sysmon_pswitch_event(&sc->sc_smpsw,
    255 	    poweroff ? PSWITCH_EVENT_PRESSED : PSWITCH_EVENT_RELEASED);
    256 }
    257 
    258 static void
    259 powsw_shutdown_check(void *arg)
    260 {
    261 	struct powsw_softc *sc = arg;
    262 	int poweroff;
    263 
    264 	poweroff = sc->sc_prev;
    265 	if (poweroff)
    266 		power_switch_is_off = 1;
    267 	DPRINTF("powsw_shutdown_check %s = %d\n",
    268 	    device_xname(sc->sc_dev), power_switch_is_off);
    269 }
    270 
    271 static void
    272 powsw_reset_counter(struct powsw_softc *sc)
    273 {
    274 
    275 	sc->sc_last_sw = -1;
    276 	sc->sc_tick = 0;
    277 	sc->sc_count = 0;
    278 #if defined(POWSW_DEBUG)
    279 	sc->sc_loglen = 0;
    280 #endif
    281 }
    282 
    283 static void
    284 powsw_set_aer(struct powsw_softc *sc, int aer)
    285 {
    286 
    287 	KASSERT(aer == 0 || aer == 1);
    288 
    289 	if (aer == 0) {
    290 		mfp_bit_clear_aer(sc->sc_mask);
    291 	} else {
    292 		mfp_bit_set_aer(sc->sc_mask);
    293 	}
    294 	DPRINTF(" SetAER=%d", aer);
    295 }
    296