xref: /netbsd-src/sys/arch/x68k/dev/powsw.c (revision 98910815bb537ed17502a125b300c736caa9bce7)
1*98910815Sisaki /*	$NetBSD: powsw.c,v 1.4 2022/07/16 04:55:35 isaki Exp $	*/
29289c438Sisaki 
39289c438Sisaki /*
49289c438Sisaki  * Copyright (c) 2011 Tetsuya Isaki. All rights reserved.
59289c438Sisaki  *
69289c438Sisaki  * Redistribution and use in source and binary forms, with or without
79289c438Sisaki  * modification, are permitted provided that the following conditions
89289c438Sisaki  * are met:
99289c438Sisaki  * 1. Redistributions of source code must retain the above copyright
109289c438Sisaki  *    notice, this list of conditions and the following disclaimer.
119289c438Sisaki  * 2. Redistributions in binary form must reproduce the above copyright
129289c438Sisaki  *    notice, this list of conditions and the following disclaimer in the
139289c438Sisaki  *    documentation and/or other materials provided with the distribution.
149289c438Sisaki  *
159289c438Sisaki  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
169289c438Sisaki  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
179289c438Sisaki  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
189289c438Sisaki  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
199289c438Sisaki  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
209289c438Sisaki  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
219289c438Sisaki  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
229289c438Sisaki  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
239289c438Sisaki  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
249289c438Sisaki  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
259289c438Sisaki  * SUCH DAMAGE.
269289c438Sisaki  */
279289c438Sisaki 
289289c438Sisaki /*
299289c438Sisaki  * Power switch monitor
309289c438Sisaki  */
319289c438Sisaki 
329289c438Sisaki #include <sys/cdefs.h>
33*98910815Sisaki __KERNEL_RCSID(0, "$NetBSD: powsw.c,v 1.4 2022/07/16 04:55:35 isaki Exp $");
349289c438Sisaki 
359289c438Sisaki #include <sys/param.h>
369289c438Sisaki #include <sys/systm.h>
379289c438Sisaki #include <sys/conf.h>
389289c438Sisaki #include <sys/device.h>
399289c438Sisaki #include <sys/intr.h>
409289c438Sisaki #include <sys/callout.h>
419289c438Sisaki 
429289c438Sisaki #include <machine/bus.h>
439289c438Sisaki #include <machine/cpu.h>
449289c438Sisaki 
459289c438Sisaki #include <arch/x68k/dev/intiovar.h>
469289c438Sisaki #include <arch/x68k/dev/mfp.h>
479289c438Sisaki 
489289c438Sisaki #include <dev/sysmon/sysmonvar.h>
499289c438Sisaki #include <dev/sysmon/sysmon_taskq.h>
509289c438Sisaki 
51623e78a6Stsutsui #include "ioconf.h"
52623e78a6Stsutsui 
539289c438Sisaki //#define POWSW_DEBUG
549289c438Sisaki 
559289c438Sisaki #if defined(POWSW_DEBUG)
569289c438Sisaki #define DPRINTF(fmt...)		printf(fmt)
579289c438Sisaki #define DEBUG_LOG_ADD(c)	sc->sc_log[sc->sc_loglen++] = (c)
589289c438Sisaki #define DEBUG_LOG_PRINT()	do {	\
599289c438Sisaki 	sc->sc_log[sc->sc_loglen] = '\0';	\
609289c438Sisaki 	printf("%s", sc->sc_log);	\
619289c438Sisaki } while (0)
629289c438Sisaki #else
639289c438Sisaki #define DPRINTF(fmt...)
649289c438Sisaki #define DEBUG_LOG_ADD(c)
659289c438Sisaki #define DEBUG_LOG_PRINT()
669289c438Sisaki #endif
679289c438Sisaki 
689289c438Sisaki /* mask */
699289c438Sisaki #define POWSW_ALARM		(0x01)
709289c438Sisaki #define POWSW_EXTERNAL		(0x02)
719289c438Sisaki #define POWSW_FRONT		(0x04)
729289c438Sisaki 
739289c438Sisaki /* parameter */
749289c438Sisaki #define POWSW_MAX_TICK		(30)
759289c438Sisaki #define POWSW_THRESHOLD		(10)
769289c438Sisaki 
779289c438Sisaki struct powsw_softc {
789289c438Sisaki 	device_t sc_dev;
799289c438Sisaki 	struct sysmon_pswitch sc_smpsw;
809289c438Sisaki 	callout_t sc_callout;
819289c438Sisaki 	int sc_mask;
829289c438Sisaki 	int sc_prev;
839289c438Sisaki 	int sc_last_sw;
849289c438Sisaki 	int sc_tick;
859289c438Sisaki 	int sc_count;
869289c438Sisaki #if defined(POWSW_DEBUG)
879289c438Sisaki 	char sc_log[100];
889289c438Sisaki 	int sc_loglen;
899289c438Sisaki #endif
909289c438Sisaki };
919289c438Sisaki 
929289c438Sisaki static int  powsw_match(device_t, cfdata_t, void *);
939289c438Sisaki static void powsw_attach(device_t, device_t, void *);
949289c438Sisaki static int  powsw_intr(void *);
959289c438Sisaki static void powsw_softintr(void *);
969289c438Sisaki static void powsw_pswitch_event(void *);
979289c438Sisaki static void powsw_reset_counter(struct powsw_softc *);
989289c438Sisaki static void powsw_set_aer(struct powsw_softc *, int);
999289c438Sisaki 
1009289c438Sisaki CFATTACH_DECL_NEW(powsw, sizeof(struct powsw_softc),
1019289c438Sisaki     powsw_match, powsw_attach, NULL, NULL);
1029289c438Sisaki 
1039289c438Sisaki 
1049289c438Sisaki typedef const struct {
1059289c438Sisaki 	int vector;			/* interrupt vector */
1069289c438Sisaki 	int mask;			/* mask bit for MFP GPIP */
1079289c438Sisaki 	const char *name;
1089289c438Sisaki } powsw_desc_t;
1099289c438Sisaki 
1109289c438Sisaki static powsw_desc_t powsw_desc[2] = {
1119289c438Sisaki 	{ 66, POWSW_FRONT,	"Front Switch", },
1129289c438Sisaki 	{ 65, POWSW_EXTERNAL,	"External Power Switch", },
1139289c438Sisaki 	/* XXX I'm not sure about alarm bit */
1149289c438Sisaki };
1159289c438Sisaki 
1169289c438Sisaki 
1179289c438Sisaki static int
powsw_match(device_t parent,cfdata_t cf,void * aux)1189289c438Sisaki powsw_match(device_t parent, cfdata_t cf, void *aux)
1199289c438Sisaki {
120ae2a8a06Sisaki 
1219289c438Sisaki 	return 1;
1229289c438Sisaki }
1239289c438Sisaki 
1249289c438Sisaki static void
powsw_attach(device_t parent,device_t self,void * aux)1259289c438Sisaki powsw_attach(device_t parent, device_t self, void *aux)
1269289c438Sisaki {
1279289c438Sisaki 	struct powsw_softc *sc = device_private(self);
1289289c438Sisaki 	powsw_desc_t *desc;
1299289c438Sisaki 	const char *xname;
1309289c438Sisaki 	int unit;
1319289c438Sisaki 	int sw;
1329289c438Sisaki 
1339289c438Sisaki 	unit = device_unit(self);
1349289c438Sisaki 	xname = device_xname(self);
1359289c438Sisaki 	desc = &powsw_desc[unit];
1369289c438Sisaki 
1379289c438Sisaki 	memset(sc, 0, sizeof(*sc));
1389289c438Sisaki 	sc->sc_dev = self;
1399289c438Sisaki 	sc->sc_mask = desc->mask;
1409289c438Sisaki 	sc->sc_prev = -1;
1419289c438Sisaki 	powsw_reset_counter(sc);
1429289c438Sisaki 
1439289c438Sisaki 	sysmon_task_queue_init();
1449289c438Sisaki 	sc->sc_smpsw.smpsw_name = xname;
1459289c438Sisaki 	sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_POWER;
1469289c438Sisaki 	if (sysmon_pswitch_register(&sc->sc_smpsw) != 0)
1479289c438Sisaki 		panic("can't register with sysmon");
1489289c438Sisaki 
1499289c438Sisaki 	callout_init(&sc->sc_callout, 0);
1509289c438Sisaki 	callout_setfunc(&sc->sc_callout, powsw_softintr, sc);
1519289c438Sisaki 
1529289c438Sisaki 	if (intio_intr_establish(desc->vector, xname, powsw_intr, sc) < 0)
1539289c438Sisaki 		panic("%s: can't establish interrupt", xname);
1549289c438Sisaki 
1559289c438Sisaki 	/* Set AER and enable interrupt */
1569289c438Sisaki 	sw = (mfp_get_gpip() & sc->sc_mask);
1579289c438Sisaki 	powsw_set_aer(sc, sw ? 0 : 1);
1589289c438Sisaki 	mfp_bit_set_ierb(sc->sc_mask);
1599289c438Sisaki 
1609289c438Sisaki 	aprint_normal(": %s\n", desc->name);
1619289c438Sisaki }
1629289c438Sisaki 
1639289c438Sisaki static int
powsw_intr(void * arg)1649289c438Sisaki powsw_intr(void *arg)
1659289c438Sisaki {
1669289c438Sisaki 	struct powsw_softc *sc = arg;
1679289c438Sisaki 
1689289c438Sisaki 	if (sc->sc_tick == 0) {
1699289c438Sisaki 		mfp_bit_clear_ierb(sc->sc_mask);
1709289c438Sisaki 		sc->sc_tick++;
1719289c438Sisaki 		DEBUG_LOG_ADD('i');
1729289c438Sisaki 		/*
1739289c438Sisaki 		 * The button state seems unstable for few ticks,
1749289c438Sisaki 		 * so wait a bit to settle.
1759289c438Sisaki 		 */
1769289c438Sisaki 		callout_schedule(&sc->sc_callout, 1);
1779289c438Sisaki 	} else {
1789289c438Sisaki 		DEBUG_LOG_ADD('x');
1799289c438Sisaki 	}
1809289c438Sisaki 	return 0;
1819289c438Sisaki }
1829289c438Sisaki 
1839289c438Sisaki void
powsw_softintr(void * arg)1849289c438Sisaki powsw_softintr(void *arg)
1859289c438Sisaki {
1869289c438Sisaki 	struct powsw_softc *sc = arg;
1879289c438Sisaki 	int sw;
1889289c438Sisaki 	int s;
1899289c438Sisaki 
1909289c438Sisaki 	s = spl6();
1919289c438Sisaki 
1929289c438Sisaki 	if (sc->sc_tick++ >= POWSW_MAX_TICK) {
1939289c438Sisaki 		/* tick is over, broken switch? */
1949289c438Sisaki 		printf("%s: unstable power switch?, ignored\n",
1959289c438Sisaki 		    device_xname(sc->sc_dev));
1969289c438Sisaki 		powsw_reset_counter(sc);
1979289c438Sisaki 
1989289c438Sisaki 		mfp_bit_set_ierb(sc->sc_mask);
1999289c438Sisaki 		splx(s);
2009289c438Sisaki 		return;
2019289c438Sisaki 	}
2029289c438Sisaki 
2039289c438Sisaki 	sw = (mfp_get_gpip() & sc->sc_mask) ? 1 : 0;
2049289c438Sisaki 	DEBUG_LOG_ADD('0' + sw);
2059289c438Sisaki 
2069289c438Sisaki 	if (sw == sc->sc_last_sw) {
2079289c438Sisaki 		sc->sc_count++;
2089289c438Sisaki 	} else {
2099289c438Sisaki 		sc->sc_last_sw = sw;
2109289c438Sisaki 		sc->sc_count = 1;
2119289c438Sisaki 	}
2129289c438Sisaki 
2139289c438Sisaki 	if (sc->sc_count < POWSW_THRESHOLD) {
2149289c438Sisaki 		callout_schedule(&sc->sc_callout, 1);
2159289c438Sisaki 	} else {
2169289c438Sisaki 		/* switch seems stable */
2179289c438Sisaki 		DEBUG_LOG_PRINT();
2189289c438Sisaki 
2199289c438Sisaki 		if (sc->sc_last_sw == sc->sc_prev) {
2209289c438Sisaki 			/* switch state is not changed, it was a noise */
221ae2a8a06Sisaki 			DPRINTF(" ignore(sw=%d,prev=%d)\n",
222ae2a8a06Sisaki 			    sc->sc_last_sw, sc->sc_prev);
2239289c438Sisaki 		} else {
2249289c438Sisaki 			/* switch state has been changed */
2259289c438Sisaki 			sc->sc_prev = sc->sc_last_sw;
2269289c438Sisaki 			powsw_set_aer(sc, 1 - sc->sc_prev);
2279289c438Sisaki 			sysmon_task_queue_sched(0, powsw_pswitch_event, sc);
2289289c438Sisaki 		}
2299289c438Sisaki 		powsw_reset_counter(sc);
230ae2a8a06Sisaki 		/* enable interrupt */
231ae2a8a06Sisaki 		mfp_bit_set_ierb(sc->sc_mask);
2329289c438Sisaki 	}
2339289c438Sisaki 
2349289c438Sisaki 	splx(s);
2359289c438Sisaki }
2369289c438Sisaki 
2379289c438Sisaki static void
powsw_pswitch_event(void * arg)2389289c438Sisaki powsw_pswitch_event(void *arg)
2399289c438Sisaki {
2409289c438Sisaki 	struct powsw_softc *sc = arg;
2419289c438Sisaki 	int poweroff;
2429289c438Sisaki 
2439289c438Sisaki 	poweroff = sc->sc_prev;
2449289c438Sisaki 
2459289c438Sisaki 	DPRINTF(" %s is %s\n", device_xname(sc->sc_dev),
2469289c438Sisaki 	    poweroff ? "off(PRESS)" : "on(RELEASE)");
2479289c438Sisaki 
2489289c438Sisaki 	sysmon_pswitch_event(&sc->sc_smpsw,
2499289c438Sisaki 	    poweroff ? PSWITCH_EVENT_PRESSED : PSWITCH_EVENT_RELEASED);
2509289c438Sisaki }
2519289c438Sisaki 
2529289c438Sisaki static void
powsw_reset_counter(struct powsw_softc * sc)2539289c438Sisaki powsw_reset_counter(struct powsw_softc *sc)
2549289c438Sisaki {
255ae2a8a06Sisaki 
2569289c438Sisaki 	sc->sc_last_sw = -1;
2579289c438Sisaki 	sc->sc_tick = 0;
2589289c438Sisaki 	sc->sc_count = 0;
2599289c438Sisaki #if defined(POWSW_DEBUG)
2609289c438Sisaki 	sc->sc_loglen = 0;
2619289c438Sisaki #endif
2629289c438Sisaki }
2639289c438Sisaki 
2649289c438Sisaki static void
powsw_set_aer(struct powsw_softc * sc,int aer)2659289c438Sisaki powsw_set_aer(struct powsw_softc *sc, int aer)
2669289c438Sisaki {
267ae2a8a06Sisaki 
2689289c438Sisaki 	KASSERT(aer == 0 || aer == 1);
2699289c438Sisaki 
2709289c438Sisaki 	if (aer == 0) {
2719289c438Sisaki 		mfp_bit_clear_aer(sc->sc_mask);
2729289c438Sisaki 	} else {
2739289c438Sisaki 		mfp_bit_set_aer(sc->sc_mask);
2749289c438Sisaki 	}
2759289c438Sisaki 	DPRINTF(" SetAER=%d", aer);
2769289c438Sisaki }
277