Home | History | Annotate | Line # | Download | only in dev
j720pwr.c revision 1.5.78.1
      1  1.5.78.1  thorpej /*	$NetBSD: j720pwr.c,v 1.5.78.1 2021/03/23 07:14:47 thorpej Exp $	*/
      2       1.1    peter 
      3       1.1    peter /*-
      4       1.1    peter  * Copyright (c) 2002, 2006 The NetBSD Foundation, Inc.
      5       1.1    peter  * All rights reserved.
      6       1.1    peter  *
      7       1.1    peter  * This code is derived from software contributed to The NetBSD Foundation
      8       1.1    peter  * by Emmanuel Dreyfus and Peter Postma.
      9       1.1    peter  *
     10       1.1    peter  * Redistribution and use in source and binary forms, with or without
     11       1.1    peter  * modification, are permitted provided that the following conditions
     12       1.1    peter  * are met:
     13       1.1    peter  * 1. Redistributions of source code must retain the above copyright
     14       1.1    peter  *    notice, this list of conditions and the following disclaimer.
     15       1.1    peter  * 2. Redistributions in binary form must reproduce the above copyright
     16       1.1    peter  *    notice, this list of conditions and the following disclaimer in the
     17       1.1    peter  *    documentation and/or other materials provided with the distribution.
     18       1.1    peter  *
     19       1.1    peter  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20       1.1    peter  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21       1.1    peter  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22       1.1    peter  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23       1.1    peter  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24       1.1    peter  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25       1.1    peter  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26       1.1    peter  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27       1.1    peter  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28       1.1    peter  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29       1.1    peter  * POSSIBILITY OF SUCH DAMAGE.
     30       1.1    peter  */
     31       1.1    peter 
     32       1.1    peter /* Jornada 720 power management. */
     33       1.1    peter 
     34       1.1    peter #include <sys/cdefs.h>
     35  1.5.78.1  thorpej __KERNEL_RCSID(0, "$NetBSD: j720pwr.c,v 1.5.78.1 2021/03/23 07:14:47 thorpej Exp $");
     36       1.1    peter 
     37       1.1    peter #include <sys/param.h>
     38       1.1    peter #include <sys/systm.h>
     39       1.1    peter #include <sys/kernel.h>
     40       1.1    peter #include <sys/proc.h>
     41       1.1    peter #include <sys/device.h>
     42       1.1    peter 
     43       1.1    peter #include <dev/apm/apmbios.h>
     44       1.1    peter 
     45       1.1    peter #include <machine/config_hook.h>
     46       1.1    peter #include <machine/platid.h>
     47       1.1    peter #include <machine/platid_mask.h>
     48       1.1    peter 
     49       1.1    peter #include <arm/sa11x0/sa11x0_var.h>
     50       1.1    peter #include <arm/sa11x0/sa11x0_gpioreg.h>
     51       1.1    peter #include <arm/sa11x0/sa11x0_ppcreg.h>
     52       1.1    peter #include <arm/sa11x0/sa11x0_sspreg.h>
     53       1.1    peter 
     54       1.1    peter #include <hpcarm/dev/j720sspvar.h>
     55       1.1    peter 
     56       1.1    peter #ifdef DEBUG
     57       1.5      rjs #define DPRINTF(arg)	aprint_normal arg
     58       1.1    peter #else
     59       1.1    peter #define DPRINTF(arg)	/* nothing */
     60       1.1    peter #endif
     61       1.1    peter 
     62       1.1    peter #define arraysize(ary)	(sizeof(ary) / sizeof(ary[0]))
     63       1.1    peter 
     64       1.1    peter struct j720pwr_softc {
     65       1.5      rjs 	device_t		sc_dev;
     66       1.1    peter 
     67       1.1    peter 	struct j720ssp_softc	*sc_ssp;
     68       1.3    peter 
     69       1.3    peter 	volatile int		sc_state;
     70       1.3    peter #define J720PWR_POWEROFF	0x01
     71       1.3    peter #define J720PWR_SLEEPING	0x02
     72       1.1    peter };
     73       1.1    peter 
     74       1.5      rjs static int	j720pwr_match(device_t, cfdata_t, void *);
     75       1.5      rjs static void	j720pwr_attach(device_t, device_t, void *);
     76       1.1    peter 
     77       1.3    peter static void	j720pwr_sleep(void *);
     78       1.3    peter static int	j720pwr_suspend_hook(void *, int, long, void *);
     79       1.3    peter static int	j720pwr_event_hook(void *, int, long, void *);
     80       1.1    peter static int	j720pwr_apm_getpower_hook(void *, int, long, void *);
     81       1.1    peter static int	j720pwr_get_battery(struct j720pwr_softc *);
     82       1.1    peter static int	j720pwr_get_ac_status(struct j720pwr_softc *);
     83       1.1    peter static int	j720pwr_get_charge_status(struct j720pwr_softc *);
     84       1.1    peter 
     85       1.1    peter static const struct {
     86       1.1    peter 	int	percent;
     87       1.1    peter 	int	value;
     88       1.1    peter 	int	state;
     89       1.1    peter } battery_table[] = {
     90       1.1    peter 	{ 100,	670,	APM_BATT_FLAG_HIGH	},
     91       1.1    peter 	{  90,	660,	APM_BATT_FLAG_HIGH	},
     92       1.1    peter 	{  80,	650,	APM_BATT_FLAG_HIGH	},
     93       1.1    peter 	{  70,	640,	APM_BATT_FLAG_HIGH	},
     94       1.1    peter 	{  60,	630,	APM_BATT_FLAG_HIGH	},
     95       1.1    peter 	{  50,	620,	APM_BATT_FLAG_HIGH	},
     96       1.1    peter 	{  40,	605,	APM_BATT_FLAG_LOW	},
     97       1.1    peter 	{  30,	580,	APM_BATT_FLAG_LOW	},
     98       1.1    peter 	{  20,	545,	APM_BATT_FLAG_LOW	},
     99       1.1    peter 	{  10,	500,	APM_BATT_FLAG_CRITICAL	},
    100       1.1    peter 	{   0,  430,	APM_BATT_FLAG_CRITICAL	},
    101       1.1    peter };
    102       1.1    peter 
    103       1.5      rjs CFATTACH_DECL_NEW(j720pwr, sizeof(struct j720pwr_softc),
    104       1.1    peter     j720pwr_match, j720pwr_attach, NULL, NULL);
    105       1.1    peter 
    106       1.1    peter 
    107       1.1    peter static int
    108       1.5      rjs j720pwr_match(device_t parent, cfdata_t cf, void *aux)
    109       1.1    peter {
    110       1.1    peter 
    111       1.1    peter 	if (!platid_match(&platid, &platid_mask_MACH_HP_JORNADA_7XX))
    112       1.1    peter 		return 0;
    113       1.1    peter 	if (strcmp(cf->cf_name, "j720pwr") != 0)
    114       1.1    peter 		return 0;
    115       1.1    peter 
    116       1.1    peter 	return 1;
    117       1.1    peter }
    118       1.1    peter 
    119       1.1    peter static void
    120       1.5      rjs j720pwr_attach(device_t parent, device_t self, void *aux)
    121       1.1    peter {
    122       1.5      rjs 	struct j720pwr_softc *sc = device_private(self);
    123       1.3    peter 	extern void (*__sleep_func)(void *);
    124       1.3    peter 	extern void *__sleep_ctx;
    125       1.1    peter 
    126       1.5      rjs 	aprint_normal("\n");
    127       1.1    peter 
    128       1.5      rjs 	sc->sc_dev = self;
    129       1.5      rjs 	sc->sc_ssp = device_private(parent);
    130       1.3    peter 	sc->sc_state = 0;
    131       1.3    peter 
    132       1.3    peter 	/* Register apm sleep function. */
    133       1.3    peter 	__sleep_func = j720pwr_sleep;
    134       1.3    peter 	__sleep_ctx = sc;
    135       1.1    peter 
    136       1.1    peter 	/* Battery status query hook. */
    137       1.1    peter 	config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_BATTERYVAL,
    138       1.1    peter 		    CONFIG_HOOK_EXCLUSIVE, j720pwr_apm_getpower_hook, sc);
    139       1.1    peter 
    140       1.1    peter 	/* Battery charge status query hook. */
    141       1.1    peter 	config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_CHARGE,
    142       1.1    peter 		    CONFIG_HOOK_EXCLUSIVE, j720pwr_apm_getpower_hook, sc);
    143       1.1    peter 
    144       1.1    peter 	/* AC status query hook. */
    145       1.1    peter 	config_hook(CONFIG_HOOK_GET, CONFIG_HOOK_ACADAPTER,
    146       1.1    peter 		    CONFIG_HOOK_EXCLUSIVE, j720pwr_apm_getpower_hook, sc);
    147       1.1    peter 
    148       1.3    peter 	/* Suspend/resume button hook. */
    149       1.3    peter 	config_hook(CONFIG_HOOK_BUTTONEVENT,
    150       1.3    peter 		    CONFIG_HOOK_BUTTONEVENT_POWER,
    151       1.3    peter 		    CONFIG_HOOK_SHARE, j720pwr_suspend_hook, sc);
    152       1.3    peter 
    153       1.3    peter 	/* Receive suspend/resume events. */
    154       1.3    peter 	config_hook(CONFIG_HOOK_PMEVENT,
    155       1.3    peter 		    CONFIG_HOOK_PMEVENT_HARDPOWER,
    156       1.3    peter 		    CONFIG_HOOK_SHARE, j720pwr_event_hook, sc);
    157       1.3    peter 
    158       1.1    peter 	/* Attach hpcapm. */
    159  1.5.78.1  thorpej 	config_found(self, NULL, NULL, CFARG_EOL);
    160       1.1    peter }
    161       1.1    peter 
    162       1.3    peter static void
    163       1.3    peter j720pwr_sleep(void *ctx)
    164       1.3    peter {
    165       1.3    peter 	struct j720pwr_softc *sc = ctx;
    166       1.3    peter 	struct j720ssp_softc *ssp = sc->sc_ssp;
    167       1.3    peter 	uint32_t oldfer;
    168       1.3    peter 
    169       1.3    peter 	/* Disable falling-edge detect on all GPIO ports, except keyboard. */
    170       1.3    peter 	oldfer = bus_space_read_4(ssp->sc_iot, ssp->sc_gpioh, SAGPIO_FER);
    171       1.3    peter 	bus_space_write_4(ssp->sc_iot, ssp->sc_gpioh, SAGPIO_FER, 1 << 0);
    172       1.3    peter 
    173       1.3    peter 	while (sc->sc_state & J720PWR_POWEROFF) {
    174       1.3    peter 		/*
    175       1.3    peter 		 * Just sleep here until the poweroff bit gets unset.
    176       1.3    peter 		 * We need to wait here because when machine_sleep() returns
    177       1.3    peter 		 * hpcapm(4) assumes that we are "resuming".
    178       1.3    peter 		 */
    179       1.3    peter 		(void)tsleep(&sc->sc_state, PWAIT, "j720slp", 0);
    180       1.3    peter 	}
    181       1.3    peter 
    182       1.3    peter 	/* Restore previous FER value. */
    183       1.3    peter 	bus_space_write_4(ssp->sc_iot, ssp->sc_gpioh, SAGPIO_FER, oldfer);
    184       1.3    peter }
    185       1.3    peter 
    186       1.3    peter static int
    187       1.3    peter j720pwr_suspend_hook(void *ctx, int type, long id, void *msg)
    188       1.3    peter {
    189       1.3    peter 	struct j720pwr_softc *sc = ctx;
    190       1.3    peter 
    191       1.3    peter 	if (type != CONFIG_HOOK_BUTTONEVENT ||
    192       1.3    peter 	    id != CONFIG_HOOK_BUTTONEVENT_POWER)
    193       1.3    peter 		return EINVAL;
    194       1.3    peter 
    195       1.3    peter 	if ((sc->sc_state & (J720PWR_POWEROFF | J720PWR_SLEEPING)) == 0) {
    196       1.3    peter 		sc->sc_state |= J720PWR_POWEROFF;
    197       1.3    peter 	} else if ((sc->sc_state & (J720PWR_POWEROFF | J720PWR_SLEEPING)) ==
    198       1.3    peter 	    (J720PWR_POWEROFF | J720PWR_SLEEPING)) {
    199       1.3    peter 		sc->sc_state &= ~J720PWR_POWEROFF;
    200       1.3    peter 		wakeup(&sc->sc_state);
    201       1.3    peter 	} else {
    202       1.3    peter 		DPRINTF(("j720pwr_suspend_hook: busy\n"));
    203       1.3    peter 		return EBUSY;
    204       1.3    peter 	}
    205       1.3    peter 
    206       1.3    peter 	config_hook_call(CONFIG_HOOK_PMEVENT,
    207       1.3    peter 		         CONFIG_HOOK_PMEVENT_SUSPENDREQ, NULL);
    208       1.3    peter 
    209       1.3    peter 	return 0;
    210       1.3    peter }
    211       1.3    peter 
    212       1.3    peter static int
    213       1.3    peter j720pwr_event_hook(void *ctx, int type, long id, void *msg)
    214       1.3    peter {
    215       1.3    peter 	struct j720pwr_softc *sc = ctx;
    216       1.3    peter 	int event = (int)msg;
    217       1.3    peter 
    218       1.3    peter 	if (type != CONFIG_HOOK_PMEVENT ||
    219       1.3    peter 	    id != CONFIG_HOOK_PMEVENT_HARDPOWER)
    220       1.3    peter 		return EINVAL;
    221       1.3    peter 
    222       1.3    peter 	switch (event) {
    223       1.3    peter 	case PWR_SUSPEND:
    224       1.3    peter 		sc->sc_state |= (J720PWR_SLEEPING | J720PWR_POWEROFF);
    225       1.3    peter 		break;
    226       1.3    peter 	case PWR_RESUME:
    227       1.3    peter 		sc->sc_state &= ~(J720PWR_SLEEPING | J720PWR_POWEROFF);
    228       1.3    peter 		break;
    229       1.3    peter 	default:
    230       1.3    peter 		return EINVAL;
    231       1.3    peter 	}
    232       1.3    peter 
    233       1.3    peter 	return 0;
    234       1.3    peter }
    235       1.3    peter 
    236       1.1    peter static int
    237       1.1    peter j720pwr_apm_getpower_hook(void *ctx, int type, long id, void *msg)
    238       1.1    peter {
    239       1.1    peter 	int * const pval = msg;
    240       1.1    peter 	int val, tmp, i, state = 0;
    241       1.1    peter 
    242       1.1    peter 	if (type != CONFIG_HOOK_GET)
    243       1.1    peter 		return EINVAL;
    244       1.1    peter 
    245       1.1    peter 	switch (id) {
    246       1.1    peter 	case CONFIG_HOOK_BATTERYVAL:
    247       1.1    peter 		val = j720pwr_get_battery(ctx);
    248       1.1    peter 
    249       1.1    peter 		if (val != -1) {
    250       1.1    peter 			for (i = 0; i < arraysize(battery_table); i++)
    251       1.1    peter 				if (val > battery_table[i].value)
    252       1.1    peter 					break;
    253       1.1    peter 			if (i == 0) {
    254       1.1    peter 				/* Battery charge status is at maximum. */
    255       1.1    peter 				*pval = 100;
    256       1.1    peter 			} else {
    257       1.1    peter 				/*
    258       1.1    peter 				 * Use linear interpolation to calculate
    259       1.1    peter 				 * the estimated charge status.
    260       1.1    peter 				 */
    261       1.1    peter 				tmp = ((val - battery_table[i].value) * 100) /
    262       1.1    peter 				    (battery_table[i - 1].value -
    263       1.1    peter 				     battery_table[i].value);
    264       1.1    peter 				*pval = battery_table[i].percent +
    265       1.1    peter 				    ((battery_table[i - 1].percent -
    266       1.1    peter 				      battery_table[i].percent) * tmp) / 100;
    267       1.1    peter 			}
    268       1.1    peter 		} else {
    269       1.1    peter 			/* Battery is absent. */
    270       1.1    peter 			*pval = 0;
    271       1.1    peter 		}
    272       1.1    peter 
    273       1.1    peter 		return 0;
    274       1.1    peter 
    275       1.1    peter 	case CONFIG_HOOK_CHARGE:
    276       1.1    peter 		val = j720pwr_get_battery(ctx);
    277       1.1    peter 
    278       1.1    peter 		if (val != -1) {
    279       1.1    peter 			for (i = 1; i < arraysize(battery_table); i++) {
    280       1.1    peter 				if (val > battery_table[i].value) {
    281       1.1    peter 					state = battery_table[i - 1].state;
    282       1.1    peter 					break;
    283       1.1    peter 				}
    284       1.1    peter 			}
    285       1.1    peter 
    286       1.1    peter 			if (j720pwr_get_charge_status(ctx) == 0)
    287       1.1    peter 				state |= APM_BATT_FLAG_CHARGING;
    288       1.1    peter 		} else {
    289       1.1    peter 			state = APM_BATT_FLAG_NO_SYSTEM_BATTERY;
    290       1.1    peter 		}
    291       1.1    peter 
    292       1.1    peter 		*pval = state;
    293       1.1    peter 		return 0;
    294       1.1    peter 
    295       1.1    peter 	case CONFIG_HOOK_ACADAPTER:
    296       1.1    peter 		*pval = j720pwr_get_ac_status(ctx) ? APM_AC_OFF : APM_AC_ON;
    297       1.1    peter 
    298       1.1    peter 		return 0;
    299       1.1    peter 	}
    300       1.1    peter 
    301       1.1    peter 	return EINVAL;
    302       1.1    peter }
    303       1.1    peter 
    304       1.1    peter static int
    305       1.1    peter j720pwr_get_battery(struct j720pwr_softc *sc)
    306       1.1    peter {
    307       1.1    peter 	struct j720ssp_softc *ssp = sc->sc_ssp;
    308       1.1    peter 	int data, i, pmdata[3];
    309       1.1    peter 
    310       1.1    peter 	bus_space_write_4(ssp->sc_iot, ssp->sc_gpioh, SAGPIO_PCR, 0x2000000);
    311       1.1    peter 
    312       1.2    peter 	if (j720ssp_readwrite(ssp, 1, 0xc0, &data, 500) < 0 || data != 0x11) {
    313       1.1    peter 		DPRINTF(("j720pwr_get_battery: no dummy received\n"));
    314       1.1    peter 		goto out;
    315       1.1    peter 	}
    316       1.1    peter 
    317       1.1    peter 	for (i = 0; i < 3; i++) {
    318       1.2    peter 		if (j720ssp_readwrite(ssp, 0, 0x11, &pmdata[i], 100) < 0)
    319       1.1    peter 			goto out;
    320       1.1    peter 	}
    321       1.1    peter 
    322       1.1    peter 	bus_space_write_4(ssp->sc_iot, ssp->sc_gpioh, SAGPIO_PSR, 0x2000000);
    323       1.1    peter 
    324       1.1    peter 	pmdata[0] |= (pmdata[2] & 0x3) << 8;	/* Main battery. */
    325       1.1    peter 	pmdata[1] |= (pmdata[2] & 0xc) << 6;	/* Backup battery (unused). */
    326       1.1    peter 
    327       1.1    peter 	DPRINTF(("j720pwr_get_battery: data[0]=%d data[1]=%d data[2]=%d\n",
    328       1.1    peter 	    pmdata[0], pmdata[1], pmdata[2]));
    329       1.1    peter 
    330       1.1    peter 	/* If bit 0 and 1 are both set, the main battery is absent. */
    331       1.1    peter 	if ((pmdata[2] & 3) == 3)
    332       1.1    peter 		return -1;
    333       1.1    peter 
    334       1.1    peter 	return pmdata[0];
    335       1.1    peter 
    336       1.1    peter out:
    337       1.1    peter 	bus_space_write_4(ssp->sc_iot, ssp->sc_gpioh, SAGPIO_PSR, 0x2000000);
    338       1.1    peter 
    339       1.1    peter 	/* reset SSP */
    340       1.1    peter 	bus_space_write_4(ssp->sc_iot, ssp->sc_ssph, SASSP_CR0, 0x307);
    341       1.1    peter 	delay(100);
    342       1.1    peter 	bus_space_write_4(ssp->sc_iot, ssp->sc_ssph, SASSP_CR0, 0x387);
    343       1.1    peter 
    344       1.1    peter 	DPRINTF(("j720pwr_get_battery: error %x\n", data));
    345       1.1    peter 	return 0;
    346       1.1    peter }
    347       1.1    peter 
    348       1.1    peter static int
    349       1.1    peter j720pwr_get_ac_status(struct j720pwr_softc *sc)
    350       1.1    peter {
    351       1.1    peter 	struct j720ssp_softc *ssp = sc->sc_ssp;
    352       1.1    peter 	uint32_t status;
    353       1.1    peter 
    354       1.1    peter 	status = bus_space_read_4(ssp->sc_iot, ssp->sc_gpioh, SAGPIO_PLR);
    355       1.1    peter 
    356       1.1    peter 	return status & (1 << 4);
    357       1.1    peter }
    358       1.1    peter 
    359       1.1    peter static int
    360       1.1    peter j720pwr_get_charge_status(struct j720pwr_softc *sc)
    361       1.1    peter {
    362       1.1    peter 	struct j720ssp_softc *ssp = sc->sc_ssp;
    363       1.1    peter 	uint32_t status;
    364       1.1    peter 
    365       1.1    peter 	status = bus_space_read_4(ssp->sc_iot, ssp->sc_gpioh, SAGPIO_PLR);
    366       1.1    peter 
    367       1.1    peter 	return status & (1 << 26);
    368       1.1    peter }
    369