1b4dbc599SNathan Whitehorn /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 371e3c308SPedro F. Giffuni * 4b4dbc599SNathan Whitehorn * Copyright (c) 2006 Michael Lorenz 5b4dbc599SNathan Whitehorn * Copyright 2008 by Nathan Whitehorn 6b4dbc599SNathan Whitehorn * All rights reserved. 7b4dbc599SNathan Whitehorn * 8b4dbc599SNathan Whitehorn * Redistribution and use in source and binary forms, with or without 9b4dbc599SNathan Whitehorn * modification, are permitted provided that the following conditions 10b4dbc599SNathan Whitehorn * are met: 11b4dbc599SNathan Whitehorn * 1. Redistributions of source code must retain the above copyright 12b4dbc599SNathan Whitehorn * notice, this list of conditions and the following disclaimer. 13b4dbc599SNathan Whitehorn * 2. Redistributions in binary form must reproduce the above copyright 14b4dbc599SNathan Whitehorn * notice, this list of conditions and the following disclaimer in the 15b4dbc599SNathan Whitehorn * documentation and/or other materials provided with the distribution. 16b4dbc599SNathan Whitehorn * 17b4dbc599SNathan Whitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18b4dbc599SNathan Whitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19b4dbc599SNathan Whitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20b4dbc599SNathan Whitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21b4dbc599SNathan Whitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22b4dbc599SNathan Whitehorn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23b4dbc599SNathan Whitehorn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 24b4dbc599SNathan Whitehorn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25b4dbc599SNathan Whitehorn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26b4dbc599SNathan Whitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27b4dbc599SNathan Whitehorn * SUCH DAMAGE. 28b4dbc599SNathan Whitehorn * 29b4dbc599SNathan Whitehorn */ 30b4dbc599SNathan Whitehorn 31b4dbc599SNathan Whitehorn #include <sys/param.h> 32b4dbc599SNathan Whitehorn #include <sys/systm.h> 33b4dbc599SNathan Whitehorn #include <sys/module.h> 34b4dbc599SNathan Whitehorn #include <sys/bus.h> 35b4dbc599SNathan Whitehorn #include <sys/conf.h> 36e2e050c8SConrad Meyer #include <sys/eventhandler.h> 37b4dbc599SNathan Whitehorn #include <sys/kernel.h> 38d49c6f02SJustin Hibbits #include <sys/kthread.h> 39e2e050c8SConrad Meyer #include <sys/lock.h> 40e2e050c8SConrad Meyer #include <sys/mutex.h> 413df9e037SNathan Whitehorn #include <sys/clock.h> 424702d987SJustin Hibbits #include <sys/proc.h> 43b2a237beSNathan Whitehorn #include <sys/reboot.h> 441af2e191SNathan Whitehorn #include <sys/sysctl.h> 45b4dbc599SNathan Whitehorn 46b4dbc599SNathan Whitehorn #include <dev/ofw/ofw_bus.h> 47b4dbc599SNathan Whitehorn #include <dev/ofw/openfirm.h> 48a228f5cdSNathan Whitehorn #include <dev/led/led.h> 49b4dbc599SNathan Whitehorn 504702d987SJustin Hibbits #include <machine/_inttypes.h> 51b4dbc599SNathan Whitehorn #include <machine/bus.h> 524702d987SJustin Hibbits #include <machine/cpu.h> 534702d987SJustin Hibbits #include <machine/hid.h> 54b4dbc599SNathan Whitehorn #include <machine/intr_machdep.h> 55b4dbc599SNathan Whitehorn #include <machine/md_var.h> 564702d987SJustin Hibbits #include <machine/pcb.h> 57b4dbc599SNathan Whitehorn #include <machine/pio.h> 58b4dbc599SNathan Whitehorn #include <machine/resource.h> 59b4dbc599SNathan Whitehorn 60b4dbc599SNathan Whitehorn #include <vm/vm.h> 61b4dbc599SNathan Whitehorn #include <vm/pmap.h> 62b4dbc599SNathan Whitehorn 63b4dbc599SNathan Whitehorn #include <sys/rman.h> 64b4dbc599SNathan Whitehorn 65b4dbc599SNathan Whitehorn #include <dev/adb/adb.h> 66b4dbc599SNathan Whitehorn 673df9e037SNathan Whitehorn #include "clock_if.h" 68b4dbc599SNathan Whitehorn #include "pmuvar.h" 69b4dbc599SNathan Whitehorn #include "viareg.h" 704702d987SJustin Hibbits #include "uninorthvar.h" /* For unin_chip_sleep()/unin_chip_wake() */ 714702d987SJustin Hibbits 724702d987SJustin Hibbits #define PMU_DEFAULTS PMU_INT_TICK | PMU_INT_ADB | \ 734702d987SJustin Hibbits PMU_INT_PCEJECT | PMU_INT_SNDBRT | \ 744702d987SJustin Hibbits PMU_INT_BATTERY | PMU_INT_ENVIRONMENT 75b4dbc599SNathan Whitehorn 76b4dbc599SNathan Whitehorn /* 773df9e037SNathan Whitehorn * Bus interface 78b4dbc599SNathan Whitehorn */ 79b4dbc599SNathan Whitehorn static int pmu_probe(device_t); 80b4dbc599SNathan Whitehorn static int pmu_attach(device_t); 81b4dbc599SNathan Whitehorn static int pmu_detach(device_t); 82b4dbc599SNathan Whitehorn 833df9e037SNathan Whitehorn /* 843df9e037SNathan Whitehorn * Clock interface 853df9e037SNathan Whitehorn */ 863df9e037SNathan Whitehorn static int pmu_gettime(device_t dev, struct timespec *ts); 873df9e037SNathan Whitehorn static int pmu_settime(device_t dev, struct timespec *ts); 883df9e037SNathan Whitehorn 893df9e037SNathan Whitehorn /* 903df9e037SNathan Whitehorn * ADB Interface 913df9e037SNathan Whitehorn */ 923df9e037SNathan Whitehorn 93b4dbc599SNathan Whitehorn static u_int pmu_adb_send(device_t dev, u_char command_byte, int len, 94b4dbc599SNathan Whitehorn u_char *data, u_char poll); 95b4dbc599SNathan Whitehorn static u_int pmu_adb_autopoll(device_t dev, uint16_t mask); 9665f44679SAndriy Gapon static u_int pmu_poll(device_t dev); 97eff47708SNathan Whitehorn 98b2a237beSNathan Whitehorn /* 99b2a237beSNathan Whitehorn * Power interface 100b2a237beSNathan Whitehorn */ 101b2a237beSNathan Whitehorn 102b2a237beSNathan Whitehorn static void pmu_shutdown(void *xsc, int howto); 103a228f5cdSNathan Whitehorn static void pmu_set_sleepled(void *xsc, int onoff); 1041af2e191SNathan Whitehorn static int pmu_server_mode(SYSCTL_HANDLER_ARGS); 1051165ddc2SNathan Whitehorn static int pmu_acline_state(SYSCTL_HANDLER_ARGS); 106eff47708SNathan Whitehorn static int pmu_query_battery(struct pmu_softc *sc, int batt, 107eff47708SNathan Whitehorn struct pmu_battstate *info); 108eff47708SNathan Whitehorn static int pmu_battquery_sysctl(SYSCTL_HANDLER_ARGS); 1093cd55688SJustin Hibbits static int pmu_battmon(SYSCTL_HANDLER_ARGS); 1103cd55688SJustin Hibbits static void pmu_battquery_proc(void); 1113cd55688SJustin Hibbits static void pmu_battery_notify(struct pmu_battstate *batt, 1123cd55688SJustin Hibbits struct pmu_battstate *old); 113eff47708SNathan Whitehorn 114eff47708SNathan Whitehorn /* 115eff47708SNathan Whitehorn * List of battery-related sysctls we might ask for 116eff47708SNathan Whitehorn */ 117eff47708SNathan Whitehorn 118eff47708SNathan Whitehorn enum { 119eff47708SNathan Whitehorn PMU_BATSYSCTL_PRESENT = 1 << 8, 120eff47708SNathan Whitehorn PMU_BATSYSCTL_CHARGING = 2 << 8, 121eff47708SNathan Whitehorn PMU_BATSYSCTL_CHARGE = 3 << 8, 122eff47708SNathan Whitehorn PMU_BATSYSCTL_MAXCHARGE = 4 << 8, 123eff47708SNathan Whitehorn PMU_BATSYSCTL_CURRENT = 5 << 8, 124eff47708SNathan Whitehorn PMU_BATSYSCTL_VOLTAGE = 6 << 8, 125eff47708SNathan Whitehorn PMU_BATSYSCTL_TIME = 7 << 8, 126eff47708SNathan Whitehorn PMU_BATSYSCTL_LIFE = 8 << 8 127eff47708SNathan Whitehorn }; 128b4dbc599SNathan Whitehorn 129b4dbc599SNathan Whitehorn static device_method_t pmu_methods[] = { 130b4dbc599SNathan Whitehorn /* Device interface */ 131b4dbc599SNathan Whitehorn DEVMETHOD(device_probe, pmu_probe), 132b4dbc599SNathan Whitehorn DEVMETHOD(device_attach, pmu_attach), 133b4dbc599SNathan Whitehorn DEVMETHOD(device_detach, pmu_detach), 134b4dbc599SNathan Whitehorn DEVMETHOD(device_shutdown, bus_generic_shutdown), 135b4dbc599SNathan Whitehorn 136b4dbc599SNathan Whitehorn /* ADB bus interface */ 137b4dbc599SNathan Whitehorn DEVMETHOD(adb_hb_send_raw_packet, pmu_adb_send), 138b4dbc599SNathan Whitehorn DEVMETHOD(adb_hb_controller_poll, pmu_poll), 139b4dbc599SNathan Whitehorn DEVMETHOD(adb_hb_set_autopoll_mask, pmu_adb_autopoll), 140b4dbc599SNathan Whitehorn 1413df9e037SNathan Whitehorn /* Clock interface */ 1423df9e037SNathan Whitehorn DEVMETHOD(clock_gettime, pmu_gettime), 1433df9e037SNathan Whitehorn DEVMETHOD(clock_settime, pmu_settime), 1443df9e037SNathan Whitehorn 1454b7ec270SMarius Strobl DEVMETHOD_END 146b4dbc599SNathan Whitehorn }; 147b4dbc599SNathan Whitehorn 148b4dbc599SNathan Whitehorn static driver_t pmu_driver = { 149b4dbc599SNathan Whitehorn "pmu", 150b4dbc599SNathan Whitehorn pmu_methods, 151b4dbc599SNathan Whitehorn sizeof(struct pmu_softc), 152b4dbc599SNathan Whitehorn }; 153b4dbc599SNathan Whitehorn 154992ae60bSJohn Baldwin EARLY_DRIVER_MODULE(pmu, macio, pmu_driver, 0, 0, BUS_PASS_RESOURCE); 15542f777fcSJohn Baldwin DRIVER_MODULE(adb, pmu, adb_driver, 0, 0); 156b4dbc599SNathan Whitehorn 157b4dbc599SNathan Whitehorn static int pmuextint_probe(device_t); 158b4dbc599SNathan Whitehorn static int pmuextint_attach(device_t); 159b4dbc599SNathan Whitehorn 160b4dbc599SNathan Whitehorn static device_method_t pmuextint_methods[] = { 161b4dbc599SNathan Whitehorn /* Device interface */ 162b4dbc599SNathan Whitehorn DEVMETHOD(device_probe, pmuextint_probe), 163b4dbc599SNathan Whitehorn DEVMETHOD(device_attach, pmuextint_attach), 164b4dbc599SNathan Whitehorn {0,0} 165b4dbc599SNathan Whitehorn }; 166b4dbc599SNathan Whitehorn 167b4dbc599SNathan Whitehorn static driver_t pmuextint_driver = { 168b4dbc599SNathan Whitehorn "pmuextint", 169b4dbc599SNathan Whitehorn pmuextint_methods, 170b4dbc599SNathan Whitehorn 0 171b4dbc599SNathan Whitehorn }; 172b4dbc599SNathan Whitehorn 173992ae60bSJohn Baldwin EARLY_DRIVER_MODULE(pmuextint, macgpio, pmuextint_driver, 0, 0, 174992ae60bSJohn Baldwin BUS_PASS_RESOURCE); 175b4dbc599SNathan Whitehorn 176b4dbc599SNathan Whitehorn /* Make sure uhid is loaded, as it turns off some of the ADB emulation */ 177b4dbc599SNathan Whitehorn MODULE_DEPEND(pmu, usb, 1, 1, 1); 178b4dbc599SNathan Whitehorn 179b4dbc599SNathan Whitehorn static void pmu_intr(void *arg); 180b4dbc599SNathan Whitehorn static void pmu_in(struct pmu_softc *sc); 181b4dbc599SNathan Whitehorn static void pmu_out(struct pmu_softc *sc); 182b4dbc599SNathan Whitehorn static void pmu_ack_on(struct pmu_softc *sc); 183b4dbc599SNathan Whitehorn static void pmu_ack_off(struct pmu_softc *sc); 184b4dbc599SNathan Whitehorn static int pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg, 185b4dbc599SNathan Whitehorn int rlen, uint8_t *out_msg); 186b4dbc599SNathan Whitehorn static uint8_t pmu_read_reg(struct pmu_softc *sc, u_int offset); 187b4dbc599SNathan Whitehorn static void pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value); 188b4dbc599SNathan Whitehorn static int pmu_intr_state(struct pmu_softc *); 189b4dbc599SNathan Whitehorn 190b4dbc599SNathan Whitehorn /* these values shows that number of data returned after 'send' cmd is sent */ 191b4dbc599SNathan Whitehorn static signed char pm_send_cmd_type[] = { 192b4dbc599SNathan Whitehorn -1, -1, -1, -1, -1, -1, -1, -1, 193b4dbc599SNathan Whitehorn -1, -1, -1, -1, -1, -1, -1, -1, 194b4dbc599SNathan Whitehorn 0x01, 0x01, -1, -1, -1, -1, -1, -1, 195b4dbc599SNathan Whitehorn 0x00, 0x00, -1, -1, -1, -1, -1, 0x00, 196b4dbc599SNathan Whitehorn -1, 0x00, 0x02, 0x01, 0x01, -1, -1, -1, 197b4dbc599SNathan Whitehorn 0x00, -1, -1, -1, -1, -1, -1, -1, 198b4dbc599SNathan Whitehorn 0x04, 0x14, -1, 0x03, -1, -1, -1, -1, 199b4dbc599SNathan Whitehorn 0x00, 0x00, 0x02, 0x02, -1, -1, -1, -1, 200b4dbc599SNathan Whitehorn 0x01, 0x01, -1, -1, -1, -1, -1, -1, 201b4dbc599SNathan Whitehorn 0x00, 0x00, -1, -1, 0x01, -1, -1, -1, 202b4dbc599SNathan Whitehorn 0x01, 0x00, 0x02, 0x02, -1, 0x01, 0x03, 0x01, 203b4dbc599SNathan Whitehorn 0x00, 0x01, 0x00, 0x00, 0x00, -1, -1, -1, 204b4dbc599SNathan Whitehorn 0x02, -1, -1, -1, -1, -1, -1, -1, 205b4dbc599SNathan Whitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1, -1, 206b4dbc599SNathan Whitehorn 0x01, 0x01, 0x01, -1, -1, -1, -1, -1, 2074702d987SJustin Hibbits 0x00, 0x00, -1, -1, -1, 0x05, 0x04, 0x04, 208b4dbc599SNathan Whitehorn 0x04, -1, 0x00, -1, -1, -1, -1, -1, 209b4dbc599SNathan Whitehorn 0x00, -1, -1, -1, -1, -1, -1, -1, 210b4dbc599SNathan Whitehorn 0x01, 0x02, -1, -1, -1, -1, -1, -1, 211b4dbc599SNathan Whitehorn 0x00, 0x00, -1, -1, -1, -1, -1, -1, 212b4dbc599SNathan Whitehorn 0x02, 0x02, 0x02, 0x04, -1, 0x00, -1, -1, 213b4dbc599SNathan Whitehorn 0x01, 0x01, 0x03, 0x02, -1, -1, -1, -1, 214b4dbc599SNathan Whitehorn -1, -1, -1, -1, -1, -1, -1, -1, 215b4dbc599SNathan Whitehorn -1, -1, -1, -1, -1, -1, -1, -1, 216b4dbc599SNathan Whitehorn -1, -1, -1, -1, -1, -1, -1, -1, 217b4dbc599SNathan Whitehorn -1, -1, -1, -1, -1, -1, -1, -1, 218b4dbc599SNathan Whitehorn 0x00, -1, -1, -1, -1, -1, -1, -1, 219b4dbc599SNathan Whitehorn 0x01, 0x01, -1, -1, 0x00, 0x00, -1, -1, 220b4dbc599SNathan Whitehorn -1, 0x04, 0x00, -1, -1, -1, -1, -1, 221b4dbc599SNathan Whitehorn 0x03, -1, 0x00, -1, 0x00, -1, -1, 0x00, 222b4dbc599SNathan Whitehorn -1, -1, -1, -1, -1, -1, -1, -1, 223b4dbc599SNathan Whitehorn -1, -1, -1, -1, -1, -1, -1, -1 224b4dbc599SNathan Whitehorn }; 225b4dbc599SNathan Whitehorn 226b4dbc599SNathan Whitehorn /* these values shows that number of data returned after 'receive' cmd is sent */ 227b4dbc599SNathan Whitehorn static signed char pm_receive_cmd_type[] = { 228b4dbc599SNathan Whitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 229b4dbc599SNathan Whitehorn -1, -1, -1, -1, -1, -1, -1, -1, 230b4dbc599SNathan Whitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 231b4dbc599SNathan Whitehorn 0x02, 0x02, -1, -1, -1, -1, -1, 0x00, 232b4dbc599SNathan Whitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 233b4dbc599SNathan Whitehorn -1, -1, -1, -1, -1, -1, -1, -1, 234b4dbc599SNathan Whitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 235b4dbc599SNathan Whitehorn 0x05, 0x15, -1, 0x02, -1, -1, -1, -1, 236b4dbc599SNathan Whitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 237b4dbc599SNathan Whitehorn 0x02, 0x02, -1, -1, -1, -1, -1, -1, 238b4dbc599SNathan Whitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 239b4dbc599SNathan Whitehorn 0x02, 0x00, 0x03, 0x03, -1, -1, -1, -1, 240b4dbc599SNathan Whitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 241b4dbc599SNathan Whitehorn 0x04, 0x04, 0x03, 0x09, -1, -1, -1, -1, 242b4dbc599SNathan Whitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2434702d987SJustin Hibbits -1, -1, -1, -1, -1, 0x01, 0x01, 0x01, 244b4dbc599SNathan Whitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 245b4dbc599SNathan Whitehorn 0x06, -1, -1, -1, -1, -1, -1, -1, 246b4dbc599SNathan Whitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 247b4dbc599SNathan Whitehorn 0x02, 0x02, -1, -1, -1, -1, -1, -1, 248b4dbc599SNathan Whitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 249b4dbc599SNathan Whitehorn 0x02, 0x00, 0x00, 0x00, -1, -1, -1, -1, 250b4dbc599SNathan Whitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 251b4dbc599SNathan Whitehorn -1, -1, -1, -1, -1, -1, -1, -1, 252b4dbc599SNathan Whitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 253b4dbc599SNathan Whitehorn -1, -1, -1, -1, -1, -1, -1, -1, 254b4dbc599SNathan Whitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 255b4dbc599SNathan Whitehorn 0x02, 0x02, -1, -1, 0x02, -1, -1, -1, 256b4dbc599SNathan Whitehorn 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 257b4dbc599SNathan Whitehorn -1, -1, 0x02, -1, -1, -1, -1, 0x00, 258b4dbc599SNathan Whitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 259b4dbc599SNathan Whitehorn -1, -1, -1, -1, -1, -1, -1, -1, 260b4dbc599SNathan Whitehorn }; 261b4dbc599SNathan Whitehorn 2623cd55688SJustin Hibbits static int pmu_battmon_enabled = 1; 263d49c6f02SJustin Hibbits static struct proc *pmubattproc; 264d49c6f02SJustin Hibbits static struct kproc_desc pmu_batt_kp = { 265d49c6f02SJustin Hibbits "pmu_batt", 266d49c6f02SJustin Hibbits pmu_battquery_proc, 267d49c6f02SJustin Hibbits &pmubattproc 268d49c6f02SJustin Hibbits }; 269d49c6f02SJustin Hibbits 270b4dbc599SNathan Whitehorn /* We only have one of each device, so globals are safe */ 271b4dbc599SNathan Whitehorn static device_t pmu = NULL; 272b4dbc599SNathan Whitehorn static device_t pmu_extint = NULL; 273b4dbc599SNathan Whitehorn 274b4dbc599SNathan Whitehorn static int 275b4dbc599SNathan Whitehorn pmuextint_probe(device_t dev) 276b4dbc599SNathan Whitehorn { 277b4dbc599SNathan Whitehorn const char *type = ofw_bus_get_type(dev); 278b4dbc599SNathan Whitehorn 279b4dbc599SNathan Whitehorn if (strcmp(type, "extint-gpio1") != 0) 280b4dbc599SNathan Whitehorn return (ENXIO); 281b4dbc599SNathan Whitehorn 282b4dbc599SNathan Whitehorn device_set_desc(dev, "Apple PMU99 External Interrupt"); 283b4dbc599SNathan Whitehorn return (0); 284b4dbc599SNathan Whitehorn } 285b4dbc599SNathan Whitehorn 286b4dbc599SNathan Whitehorn static int 287b4dbc599SNathan Whitehorn pmu_probe(device_t dev) 288b4dbc599SNathan Whitehorn { 289b4dbc599SNathan Whitehorn const char *type = ofw_bus_get_type(dev); 290b4dbc599SNathan Whitehorn 291b4dbc599SNathan Whitehorn if (strcmp(type, "via-pmu") != 0) 292b4dbc599SNathan Whitehorn return (ENXIO); 293b4dbc599SNathan Whitehorn 294b4dbc599SNathan Whitehorn device_set_desc(dev, "Apple PMU99 Controller"); 295b4dbc599SNathan Whitehorn return (0); 296b4dbc599SNathan Whitehorn } 297b4dbc599SNathan Whitehorn 298b4dbc599SNathan Whitehorn static int 299b4dbc599SNathan Whitehorn setup_pmu_intr(device_t dev, device_t extint) 300b4dbc599SNathan Whitehorn { 301b4dbc599SNathan Whitehorn struct pmu_softc *sc; 302b4dbc599SNathan Whitehorn sc = device_get_softc(dev); 303b4dbc599SNathan Whitehorn 304b4dbc599SNathan Whitehorn sc->sc_irqrid = 0; 305b4dbc599SNathan Whitehorn sc->sc_irq = bus_alloc_resource_any(extint, SYS_RES_IRQ, &sc->sc_irqrid, 306b4dbc599SNathan Whitehorn RF_ACTIVE); 307b4dbc599SNathan Whitehorn if (sc->sc_irq == NULL) { 308b4dbc599SNathan Whitehorn device_printf(dev, "could not allocate interrupt\n"); 309b4dbc599SNathan Whitehorn return (ENXIO); 310b4dbc599SNathan Whitehorn } 311b4dbc599SNathan Whitehorn 312b4dbc599SNathan Whitehorn if (bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE 313b4dbc599SNathan Whitehorn | INTR_ENTROPY, NULL, pmu_intr, dev, &sc->sc_ih) != 0) { 314b4dbc599SNathan Whitehorn device_printf(dev, "could not setup interrupt\n"); 315b4dbc599SNathan Whitehorn bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, 316b4dbc599SNathan Whitehorn sc->sc_irq); 317b4dbc599SNathan Whitehorn return (ENXIO); 318b4dbc599SNathan Whitehorn } 319b4dbc599SNathan Whitehorn 320b4dbc599SNathan Whitehorn return (0); 321b4dbc599SNathan Whitehorn } 322b4dbc599SNathan Whitehorn 323b4dbc599SNathan Whitehorn static int 324b4dbc599SNathan Whitehorn pmuextint_attach(device_t dev) 325b4dbc599SNathan Whitehorn { 326b4dbc599SNathan Whitehorn pmu_extint = dev; 327b4dbc599SNathan Whitehorn if (pmu) 328b4dbc599SNathan Whitehorn return (setup_pmu_intr(pmu,dev)); 329b4dbc599SNathan Whitehorn 330b4dbc599SNathan Whitehorn return (0); 331b4dbc599SNathan Whitehorn } 332b4dbc599SNathan Whitehorn 333b4dbc599SNathan Whitehorn static int 334b4dbc599SNathan Whitehorn pmu_attach(device_t dev) 335b4dbc599SNathan Whitehorn { 336b4dbc599SNathan Whitehorn struct pmu_softc *sc; 337b4dbc599SNathan Whitehorn 338eff47708SNathan Whitehorn int i; 339b4dbc599SNathan Whitehorn uint8_t reg; 340b4dbc599SNathan Whitehorn uint8_t cmd[2] = {2, 0}; 341b4dbc599SNathan Whitehorn uint8_t resp[16]; 342b4dbc599SNathan Whitehorn phandle_t node,child; 3431af2e191SNathan Whitehorn struct sysctl_ctx_list *ctx; 3441af2e191SNathan Whitehorn struct sysctl_oid *tree; 345b4dbc599SNathan Whitehorn 346b4dbc599SNathan Whitehorn sc = device_get_softc(dev); 347b4dbc599SNathan Whitehorn sc->sc_dev = dev; 348b4dbc599SNathan Whitehorn 349b4dbc599SNathan Whitehorn sc->sc_memrid = 0; 350b4dbc599SNathan Whitehorn sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 351b4dbc599SNathan Whitehorn &sc->sc_memrid, RF_ACTIVE); 352b4dbc599SNathan Whitehorn 353b4dbc599SNathan Whitehorn mtx_init(&sc->sc_mutex,"pmu",NULL,MTX_DEF | MTX_RECURSE); 354b4dbc599SNathan Whitehorn 355b4dbc599SNathan Whitehorn if (sc->sc_memr == NULL) { 356b4dbc599SNathan Whitehorn device_printf(dev, "Could not alloc mem resource!\n"); 357b4dbc599SNathan Whitehorn return (ENXIO); 358b4dbc599SNathan Whitehorn } 359b4dbc599SNathan Whitehorn 360b4dbc599SNathan Whitehorn /* 361b4dbc599SNathan Whitehorn * Our interrupt is attached to a GPIO pin. Depending on probe order, 362b4dbc599SNathan Whitehorn * we may not have found it yet. If we haven't, it will find us, and 363b4dbc599SNathan Whitehorn * attach our interrupt then. 364b4dbc599SNathan Whitehorn */ 365b4dbc599SNathan Whitehorn pmu = dev; 366b4dbc599SNathan Whitehorn if (pmu_extint != NULL) { 367b4dbc599SNathan Whitehorn if (setup_pmu_intr(dev,pmu_extint) != 0) 368b4dbc599SNathan Whitehorn return (ENXIO); 369b4dbc599SNathan Whitehorn } 370b4dbc599SNathan Whitehorn 371b4dbc599SNathan Whitehorn sc->sc_autopoll = 0; 372a228f5cdSNathan Whitehorn sc->sc_batteries = 0; 373a228f5cdSNathan Whitehorn sc->adb_bus = NULL; 374a228f5cdSNathan Whitehorn sc->sc_leddev = NULL; 375b4dbc599SNathan Whitehorn 376b4dbc599SNathan Whitehorn /* Init PMU */ 377b4dbc599SNathan Whitehorn 3784702d987SJustin Hibbits pmu_write_reg(sc, vBufB, pmu_read_reg(sc, vBufB) | vPB4); 3794702d987SJustin Hibbits pmu_write_reg(sc, vDirB, (pmu_read_reg(sc, vDirB) | vPB4) & ~vPB3); 3804702d987SJustin Hibbits 3814702d987SJustin Hibbits reg = PMU_DEFAULTS; 382b4dbc599SNathan Whitehorn pmu_send(sc, PMU_SET_IMASK, 1, ®, 16, resp); 383b4dbc599SNathan Whitehorn 3844702d987SJustin Hibbits pmu_write_reg(sc, vIER, 0x94); /* make sure VIA interrupts are on */ 385b4dbc599SNathan Whitehorn 386b4dbc599SNathan Whitehorn pmu_send(sc, PMU_SYSTEM_READY, 1, cmd, 16, resp); 387feb05383SJustin Hibbits pmu_send(sc, PMU_GET_VERSION, 0, cmd, 16, resp); 388b4dbc599SNathan Whitehorn 389b4dbc599SNathan Whitehorn /* Initialize child buses (ADB) */ 390b4dbc599SNathan Whitehorn node = ofw_bus_get_node(dev); 391b4dbc599SNathan Whitehorn 392b4dbc599SNathan Whitehorn for (child = OF_child(node); child != 0; child = OF_peer(child)) { 393b4dbc599SNathan Whitehorn char name[32]; 394b4dbc599SNathan Whitehorn 395b4dbc599SNathan Whitehorn memset(name, 0, sizeof(name)); 396b4dbc599SNathan Whitehorn OF_getprop(child, "name", name, sizeof(name)); 397b4dbc599SNathan Whitehorn 398b4dbc599SNathan Whitehorn if (bootverbose) 399b4dbc599SNathan Whitehorn device_printf(dev, "PMU child <%s>\n",name); 400b4dbc599SNathan Whitehorn 401b4dbc599SNathan Whitehorn if (strncmp(name, "adb", 4) == 0) { 4025b56413dSWarner Losh sc->adb_bus = device_add_child(dev,"adb",DEVICE_UNIT_ANY); 403b4dbc599SNathan Whitehorn } 404eff47708SNathan Whitehorn 405eff47708SNathan Whitehorn if (strncmp(name, "power-mgt", 9) == 0) { 406eff47708SNathan Whitehorn uint32_t prim_info[9]; 407eff47708SNathan Whitehorn 408eff47708SNathan Whitehorn if (OF_getprop(child, "prim-info", prim_info, 409eff47708SNathan Whitehorn sizeof(prim_info)) >= 7) 410eff47708SNathan Whitehorn sc->sc_batteries = (prim_info[6] >> 16) & 0xff; 411eff47708SNathan Whitehorn 412eff47708SNathan Whitehorn if (bootverbose && sc->sc_batteries > 0) 413eff47708SNathan Whitehorn device_printf(dev, "%d batteries detected\n", 414eff47708SNathan Whitehorn sc->sc_batteries); 415eff47708SNathan Whitehorn } 416b4dbc599SNathan Whitehorn } 417b4dbc599SNathan Whitehorn 4181af2e191SNathan Whitehorn /* 4191af2e191SNathan Whitehorn * Set up sysctls 4201af2e191SNathan Whitehorn */ 4211af2e191SNathan Whitehorn 4221af2e191SNathan Whitehorn ctx = device_get_sysctl_ctx(dev); 4231af2e191SNathan Whitehorn tree = device_get_sysctl_tree(dev); 4241af2e191SNathan Whitehorn 4251af2e191SNathan Whitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 4267029da5cSPawel Biernacki "server_mode", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0, 4271af2e191SNathan Whitehorn pmu_server_mode, "I", "Enable reboot after power failure"); 4281af2e191SNathan Whitehorn 429eff47708SNathan Whitehorn if (sc->sc_batteries > 0) { 430eff47708SNathan Whitehorn struct sysctl_oid *oid, *battroot; 431eff47708SNathan Whitehorn char battnum[2]; 432eff47708SNathan Whitehorn 433d49c6f02SJustin Hibbits /* Only start the battery monitor if we have a battery. */ 434d49c6f02SJustin Hibbits kproc_start(&pmu_batt_kp); 4351165ddc2SNathan Whitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 4367029da5cSPawel Biernacki "monitor_batteries", 4377029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, 4383cd55688SJustin Hibbits pmu_battmon, "I", "Post battery events to devd"); 4393cd55688SJustin Hibbits 4403cd55688SJustin Hibbits SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 4417029da5cSPawel Biernacki "acline", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 4427029da5cSPawel Biernacki 0, pmu_acline_state, "I", "AC Line Status"); 4431165ddc2SNathan Whitehorn 444eff47708SNathan Whitehorn battroot = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 4457029da5cSPawel Biernacki "batteries", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 4467029da5cSPawel Biernacki "Battery Information"); 447eff47708SNathan Whitehorn 448eff47708SNathan Whitehorn for (i = 0; i < sc->sc_batteries; i++) { 449eff47708SNathan Whitehorn battnum[0] = i + '0'; 450eff47708SNathan Whitehorn battnum[1] = '\0'; 451eff47708SNathan Whitehorn 452eff47708SNathan Whitehorn oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(battroot), 4537029da5cSPawel Biernacki OID_AUTO, battnum, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 454eff47708SNathan Whitehorn "Battery Information"); 455eff47708SNathan Whitehorn 456eff47708SNathan Whitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 4577029da5cSPawel Biernacki "present", 4587029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 459eff47708SNathan Whitehorn PMU_BATSYSCTL_PRESENT | i, pmu_battquery_sysctl, 460eff47708SNathan Whitehorn "I", "Battery present"); 461eff47708SNathan Whitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 4627029da5cSPawel Biernacki "charging", 4637029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 464eff47708SNathan Whitehorn PMU_BATSYSCTL_CHARGING | i, pmu_battquery_sysctl, 465eff47708SNathan Whitehorn "I", "Battery charging"); 466eff47708SNathan Whitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 4677029da5cSPawel Biernacki "charge", 4687029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 469eff47708SNathan Whitehorn PMU_BATSYSCTL_CHARGE | i, pmu_battquery_sysctl, 470eff47708SNathan Whitehorn "I", "Battery charge (mAh)"); 471eff47708SNathan Whitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 4727029da5cSPawel Biernacki "maxcharge", 4737029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 474eff47708SNathan Whitehorn PMU_BATSYSCTL_MAXCHARGE | i, pmu_battquery_sysctl, 475eff47708SNathan Whitehorn "I", "Maximum battery capacity (mAh)"); 476eff47708SNathan Whitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 4777029da5cSPawel Biernacki "rate", 4787029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 479eff47708SNathan Whitehorn PMU_BATSYSCTL_CURRENT | i, pmu_battquery_sysctl, 480eff47708SNathan Whitehorn "I", "Battery discharge rate (mA)"); 481eff47708SNathan Whitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 4827029da5cSPawel Biernacki "voltage", 4837029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 484eff47708SNathan Whitehorn PMU_BATSYSCTL_VOLTAGE | i, pmu_battquery_sysctl, 485eff47708SNathan Whitehorn "I", "Battery voltage (mV)"); 486eff47708SNathan Whitehorn 487eff47708SNathan Whitehorn /* Knobs for mental compatibility with ACPI */ 488eff47708SNathan Whitehorn 489eff47708SNathan Whitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 4907029da5cSPawel Biernacki "time", 4917029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 492eff47708SNathan Whitehorn PMU_BATSYSCTL_TIME | i, pmu_battquery_sysctl, 493eff47708SNathan Whitehorn "I", "Time Remaining (minutes)"); 494eff47708SNathan Whitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 4957029da5cSPawel Biernacki "life", 4967029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 497eff47708SNathan Whitehorn PMU_BATSYSCTL_LIFE | i, pmu_battquery_sysctl, 498eff47708SNathan Whitehorn "I", "Capacity remaining (percent)"); 499eff47708SNathan Whitehorn } 500eff47708SNathan Whitehorn } 501eff47708SNathan Whitehorn 502a228f5cdSNathan Whitehorn /* 503a228f5cdSNathan Whitehorn * Set up LED interface 504a228f5cdSNathan Whitehorn */ 505a228f5cdSNathan Whitehorn 506a228f5cdSNathan Whitehorn sc->sc_leddev = led_create(pmu_set_sleepled, sc, "sleepled"); 507a228f5cdSNathan Whitehorn 5083df9e037SNathan Whitehorn /* 5093df9e037SNathan Whitehorn * Register RTC 5103df9e037SNathan Whitehorn */ 5113df9e037SNathan Whitehorn 5123df9e037SNathan Whitehorn clock_register(dev, 1000); 5133df9e037SNathan Whitehorn 514b2a237beSNathan Whitehorn /* 515b2a237beSNathan Whitehorn * Register power control handler 516b2a237beSNathan Whitehorn */ 517b2a237beSNathan Whitehorn EVENTHANDLER_REGISTER(shutdown_final, pmu_shutdown, sc, 518b2a237beSNathan Whitehorn SHUTDOWN_PRI_LAST); 519b2a237beSNathan Whitehorn 520*18250ec6SJohn Baldwin bus_attach_children(dev); 521*18250ec6SJohn Baldwin return (0); 522b4dbc599SNathan Whitehorn } 523b4dbc599SNathan Whitehorn 524b4dbc599SNathan Whitehorn static int 525b4dbc599SNathan Whitehorn pmu_detach(device_t dev) 526b4dbc599SNathan Whitehorn { 527b4dbc599SNathan Whitehorn struct pmu_softc *sc; 528d412c076SJohn Baldwin int error; 529d412c076SJohn Baldwin 530d412c076SJohn Baldwin error = bus_generic_detach(dev); 531d412c076SJohn Baldwin if (error != 0) 532d412c076SJohn Baldwin return (error); 533b4dbc599SNathan Whitehorn 534b4dbc599SNathan Whitehorn sc = device_get_softc(dev); 535b4dbc599SNathan Whitehorn 536a228f5cdSNathan Whitehorn if (sc->sc_leddev != NULL) 537a228f5cdSNathan Whitehorn led_destroy(sc->sc_leddev); 538a228f5cdSNathan Whitehorn 539b4dbc599SNathan Whitehorn bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih); 540b4dbc599SNathan Whitehorn bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, sc->sc_irq); 541b4dbc599SNathan Whitehorn bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_memrid, sc->sc_memr); 542b4dbc599SNathan Whitehorn mtx_destroy(&sc->sc_mutex); 543b4dbc599SNathan Whitehorn 544d412c076SJohn Baldwin return (0); 545b4dbc599SNathan Whitehorn } 546b4dbc599SNathan Whitehorn 547b4dbc599SNathan Whitehorn static uint8_t 548b4dbc599SNathan Whitehorn pmu_read_reg(struct pmu_softc *sc, u_int offset) 549b4dbc599SNathan Whitehorn { 550b4dbc599SNathan Whitehorn return (bus_read_1(sc->sc_memr, offset)); 551b4dbc599SNathan Whitehorn } 552b4dbc599SNathan Whitehorn 553b4dbc599SNathan Whitehorn static void 554b4dbc599SNathan Whitehorn pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value) 555b4dbc599SNathan Whitehorn { 556b4dbc599SNathan Whitehorn bus_write_1(sc->sc_memr, offset, value); 557b4dbc599SNathan Whitehorn } 558b4dbc599SNathan Whitehorn 559b4dbc599SNathan Whitehorn static int 560b4dbc599SNathan Whitehorn pmu_send_byte(struct pmu_softc *sc, uint8_t data) 561b4dbc599SNathan Whitehorn { 562b4dbc599SNathan Whitehorn 563b4dbc599SNathan Whitehorn pmu_out(sc); 564b4dbc599SNathan Whitehorn pmu_write_reg(sc, vSR, data); 565b4dbc599SNathan Whitehorn pmu_ack_off(sc); 566b4dbc599SNathan Whitehorn /* wait for intr to come up */ 567b4dbc599SNathan Whitehorn /* XXX should add a timeout and bail if it expires */ 568b4dbc599SNathan Whitehorn do {} while (pmu_intr_state(sc) == 0); 569b4dbc599SNathan Whitehorn pmu_ack_on(sc); 570b4dbc599SNathan Whitehorn do {} while (pmu_intr_state(sc)); 571b4dbc599SNathan Whitehorn pmu_ack_on(sc); 572b4dbc599SNathan Whitehorn return 0; 573b4dbc599SNathan Whitehorn } 574b4dbc599SNathan Whitehorn 575b4dbc599SNathan Whitehorn static inline int 576b4dbc599SNathan Whitehorn pmu_read_byte(struct pmu_softc *sc, uint8_t *data) 577b4dbc599SNathan Whitehorn { 578b4dbc599SNathan Whitehorn pmu_in(sc); 579b7fe00faSJohn Baldwin (void)pmu_read_reg(sc, vSR); 580b4dbc599SNathan Whitehorn pmu_ack_off(sc); 581b4dbc599SNathan Whitehorn /* wait for intr to come up */ 582b4dbc599SNathan Whitehorn do {} while (pmu_intr_state(sc) == 0); 583b4dbc599SNathan Whitehorn pmu_ack_on(sc); 584b4dbc599SNathan Whitehorn do {} while (pmu_intr_state(sc)); 585b4dbc599SNathan Whitehorn *data = pmu_read_reg(sc, vSR); 586b4dbc599SNathan Whitehorn return 0; 587b4dbc599SNathan Whitehorn } 588b4dbc599SNathan Whitehorn 589b4dbc599SNathan Whitehorn static int 590b4dbc599SNathan Whitehorn pmu_intr_state(struct pmu_softc *sc) 591b4dbc599SNathan Whitehorn { 592b4dbc599SNathan Whitehorn return ((pmu_read_reg(sc, vBufB) & vPB3) == 0); 593b4dbc599SNathan Whitehorn } 594b4dbc599SNathan Whitehorn 595b4dbc599SNathan Whitehorn static int 596b4dbc599SNathan Whitehorn pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg, int rlen, 597b4dbc599SNathan Whitehorn uint8_t *out_msg) 598b4dbc599SNathan Whitehorn { 599b4dbc599SNathan Whitehorn struct pmu_softc *sc = cookie; 600b4dbc599SNathan Whitehorn int i, rcv_len = -1; 601b4dbc599SNathan Whitehorn uint8_t out_len, intreg; 602b4dbc599SNathan Whitehorn 603b4dbc599SNathan Whitehorn intreg = pmu_read_reg(sc, vIER); 604b4dbc599SNathan Whitehorn intreg &= 0x10; 605b4dbc599SNathan Whitehorn pmu_write_reg(sc, vIER, intreg); 606b4dbc599SNathan Whitehorn 607b4dbc599SNathan Whitehorn /* wait idle */ 608b4dbc599SNathan Whitehorn do {} while (pmu_intr_state(sc)); 609b4dbc599SNathan Whitehorn 610b4dbc599SNathan Whitehorn /* send command */ 611b4dbc599SNathan Whitehorn pmu_send_byte(sc, cmd); 612b4dbc599SNathan Whitehorn 613b4dbc599SNathan Whitehorn /* send length if necessary */ 614b4dbc599SNathan Whitehorn if (pm_send_cmd_type[cmd] < 0) { 615b4dbc599SNathan Whitehorn pmu_send_byte(sc, length); 616b4dbc599SNathan Whitehorn } 617b4dbc599SNathan Whitehorn 618b4dbc599SNathan Whitehorn for (i = 0; i < length; i++) { 619b4dbc599SNathan Whitehorn pmu_send_byte(sc, in_msg[i]); 620b4dbc599SNathan Whitehorn } 621b4dbc599SNathan Whitehorn 622b4dbc599SNathan Whitehorn /* see if there's data to read */ 623b4dbc599SNathan Whitehorn rcv_len = pm_receive_cmd_type[cmd]; 624b4dbc599SNathan Whitehorn if (rcv_len == 0) 625b4dbc599SNathan Whitehorn goto done; 626b4dbc599SNathan Whitehorn 627b4dbc599SNathan Whitehorn /* read command */ 628b4dbc599SNathan Whitehorn if (rcv_len == 1) { 629b4dbc599SNathan Whitehorn pmu_read_byte(sc, out_msg); 630b4dbc599SNathan Whitehorn goto done; 631b4dbc599SNathan Whitehorn } else 632b4dbc599SNathan Whitehorn out_msg[0] = cmd; 633b4dbc599SNathan Whitehorn if (rcv_len < 0) { 634b4dbc599SNathan Whitehorn pmu_read_byte(sc, &out_len); 635b4dbc599SNathan Whitehorn rcv_len = out_len + 1; 636b4dbc599SNathan Whitehorn } 637b4dbc599SNathan Whitehorn for (i = 1; i < min(rcv_len, rlen); i++) 638b4dbc599SNathan Whitehorn pmu_read_byte(sc, &out_msg[i]); 639b4dbc599SNathan Whitehorn 640b4dbc599SNathan Whitehorn done: 641b4dbc599SNathan Whitehorn pmu_write_reg(sc, vIER, (intreg == 0) ? 0 : 0x90); 642b4dbc599SNathan Whitehorn 643b4dbc599SNathan Whitehorn return rcv_len; 644b4dbc599SNathan Whitehorn } 645b4dbc599SNathan Whitehorn 64665f44679SAndriy Gapon static u_int 647b4dbc599SNathan Whitehorn pmu_poll(device_t dev) 648b4dbc599SNathan Whitehorn { 649b4dbc599SNathan Whitehorn pmu_intr(dev); 65065f44679SAndriy Gapon return (0); 651b4dbc599SNathan Whitehorn } 652b4dbc599SNathan Whitehorn 653b4dbc599SNathan Whitehorn static void 654b4dbc599SNathan Whitehorn pmu_in(struct pmu_softc *sc) 655b4dbc599SNathan Whitehorn { 656b4dbc599SNathan Whitehorn uint8_t reg; 657b4dbc599SNathan Whitehorn 658b4dbc599SNathan Whitehorn reg = pmu_read_reg(sc, vACR); 659b4dbc599SNathan Whitehorn reg &= ~vSR_OUT; 660b4dbc599SNathan Whitehorn reg |= 0x0c; 661b4dbc599SNathan Whitehorn pmu_write_reg(sc, vACR, reg); 662b4dbc599SNathan Whitehorn } 663b4dbc599SNathan Whitehorn 664b4dbc599SNathan Whitehorn static void 665b4dbc599SNathan Whitehorn pmu_out(struct pmu_softc *sc) 666b4dbc599SNathan Whitehorn { 667b4dbc599SNathan Whitehorn uint8_t reg; 668b4dbc599SNathan Whitehorn 669b4dbc599SNathan Whitehorn reg = pmu_read_reg(sc, vACR); 670b4dbc599SNathan Whitehorn reg |= vSR_OUT; 671b4dbc599SNathan Whitehorn reg |= 0x0c; 672b4dbc599SNathan Whitehorn pmu_write_reg(sc, vACR, reg); 673b4dbc599SNathan Whitehorn } 674b4dbc599SNathan Whitehorn 675b4dbc599SNathan Whitehorn static void 676b4dbc599SNathan Whitehorn pmu_ack_off(struct pmu_softc *sc) 677b4dbc599SNathan Whitehorn { 678b4dbc599SNathan Whitehorn uint8_t reg; 679b4dbc599SNathan Whitehorn 680b4dbc599SNathan Whitehorn reg = pmu_read_reg(sc, vBufB); 681b4dbc599SNathan Whitehorn reg &= ~vPB4; 682b4dbc599SNathan Whitehorn pmu_write_reg(sc, vBufB, reg); 683b4dbc599SNathan Whitehorn } 684b4dbc599SNathan Whitehorn 685b4dbc599SNathan Whitehorn static void 686b4dbc599SNathan Whitehorn pmu_ack_on(struct pmu_softc *sc) 687b4dbc599SNathan Whitehorn { 688b4dbc599SNathan Whitehorn uint8_t reg; 689b4dbc599SNathan Whitehorn 690b4dbc599SNathan Whitehorn reg = pmu_read_reg(sc, vBufB); 691b4dbc599SNathan Whitehorn reg |= vPB4; 692b4dbc599SNathan Whitehorn pmu_write_reg(sc, vBufB, reg); 693b4dbc599SNathan Whitehorn } 694b4dbc599SNathan Whitehorn 695b4dbc599SNathan Whitehorn static void 696b4dbc599SNathan Whitehorn pmu_intr(void *arg) 697b4dbc599SNathan Whitehorn { 698b4dbc599SNathan Whitehorn device_t dev; 699b4dbc599SNathan Whitehorn struct pmu_softc *sc; 700b4dbc599SNathan Whitehorn 701b4dbc599SNathan Whitehorn unsigned int len; 702b4dbc599SNathan Whitehorn uint8_t resp[16]; 703b4dbc599SNathan Whitehorn uint8_t junk[16]; 704b4dbc599SNathan Whitehorn 705b4dbc599SNathan Whitehorn dev = (device_t)arg; 706b4dbc599SNathan Whitehorn sc = device_get_softc(dev); 707b4dbc599SNathan Whitehorn 708b4dbc599SNathan Whitehorn mtx_lock(&sc->sc_mutex); 709b4dbc599SNathan Whitehorn 710b4dbc599SNathan Whitehorn pmu_write_reg(sc, vIFR, 0x90); /* Clear 'em */ 711b4dbc599SNathan Whitehorn len = pmu_send(sc, PMU_INT_ACK, 0, NULL, 16, resp); 712b4dbc599SNathan Whitehorn 713b4dbc599SNathan Whitehorn mtx_unlock(&sc->sc_mutex); 714b4dbc599SNathan Whitehorn 715b4dbc599SNathan Whitehorn if ((len < 1) || (resp[1] == 0)) { 716b4dbc599SNathan Whitehorn return; 717b4dbc599SNathan Whitehorn } 718b4dbc599SNathan Whitehorn 719b4dbc599SNathan Whitehorn if (resp[1] & PMU_INT_ADB) { 720b4dbc599SNathan Whitehorn /* 721b4dbc599SNathan Whitehorn * the PMU will turn off autopolling after each command that 722b4dbc599SNathan Whitehorn * it did not issue, so we assume any but TALK R0 is ours and 723b4dbc599SNathan Whitehorn * re-enable autopoll here whenever we receive an ACK for a 724b4dbc599SNathan Whitehorn * non TR0 command. 725b4dbc599SNathan Whitehorn */ 726b4dbc599SNathan Whitehorn mtx_lock(&sc->sc_mutex); 727b4dbc599SNathan Whitehorn 728b4dbc599SNathan Whitehorn if ((resp[2] & 0x0f) != (ADB_COMMAND_TALK << 2)) { 729b4dbc599SNathan Whitehorn if (sc->sc_autopoll) { 730b4dbc599SNathan Whitehorn uint8_t cmd[] = {0, PMU_SET_POLL_MASK, 731b4dbc599SNathan Whitehorn (sc->sc_autopoll >> 8) & 0xff, 732b4dbc599SNathan Whitehorn sc->sc_autopoll & 0xff}; 733b4dbc599SNathan Whitehorn 734b4dbc599SNathan Whitehorn pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, junk); 735b4dbc599SNathan Whitehorn } 736b4dbc599SNathan Whitehorn } 737b4dbc599SNathan Whitehorn 738b4dbc599SNathan Whitehorn mtx_unlock(&sc->sc_mutex); 739b4dbc599SNathan Whitehorn 740b4dbc599SNathan Whitehorn adb_receive_raw_packet(sc->adb_bus,resp[1],resp[2], 741b4dbc599SNathan Whitehorn len - 3,&resp[3]); 742b4dbc599SNathan Whitehorn } 7436431ede3SJustin Hibbits if (resp[1] & PMU_INT_ENVIRONMENT) { 74423ab37cbSJustin Hibbits /* if the lid was just closed, notify devd. */ 7456431ede3SJustin Hibbits if ((resp[2] & PMU_ENV_LID_CLOSED) && (!sc->lid_closed)) { 7466431ede3SJustin Hibbits sc->lid_closed = 1; 7476431ede3SJustin Hibbits devctl_notify("PMU", "lid", "close", NULL); 7486431ede3SJustin Hibbits } 7496431ede3SJustin Hibbits else if (!(resp[2] & PMU_ENV_LID_CLOSED) && (sc->lid_closed)) { 75023ab37cbSJustin Hibbits /* if the lid was just opened, notify devd. */ 7516431ede3SJustin Hibbits sc->lid_closed = 0; 752a1afe0bfSJustin Hibbits devctl_notify("PMU", "lid", "open", NULL); 7536431ede3SJustin Hibbits } 754a1afe0bfSJustin Hibbits if (resp[2] & PMU_ENV_POWER) 755a1afe0bfSJustin Hibbits devctl_notify("PMU", "Button", "pressed", NULL); 7566431ede3SJustin Hibbits } 757b4dbc599SNathan Whitehorn } 758b4dbc599SNathan Whitehorn 759b4dbc599SNathan Whitehorn static u_int 760b4dbc599SNathan Whitehorn pmu_adb_send(device_t dev, u_char command_byte, int len, u_char *data, 761b4dbc599SNathan Whitehorn u_char poll) 762b4dbc599SNathan Whitehorn { 763b4dbc599SNathan Whitehorn struct pmu_softc *sc = device_get_softc(dev); 764b7fe00faSJohn Baldwin int i; 765b4dbc599SNathan Whitehorn uint8_t packet[16], resp[16]; 766b4dbc599SNathan Whitehorn 767b4dbc599SNathan Whitehorn /* construct an ADB command packet and send it */ 768b4dbc599SNathan Whitehorn 769b4dbc599SNathan Whitehorn packet[0] = command_byte; 770b4dbc599SNathan Whitehorn 771b4dbc599SNathan Whitehorn packet[1] = 0; 772b4dbc599SNathan Whitehorn packet[2] = len; 773b4dbc599SNathan Whitehorn for (i = 0; i < len; i++) 774b4dbc599SNathan Whitehorn packet[i + 3] = data[i]; 775b4dbc599SNathan Whitehorn 776b4dbc599SNathan Whitehorn mtx_lock(&sc->sc_mutex); 777b7fe00faSJohn Baldwin pmu_send(sc, PMU_ADB_CMD, len + 3, packet, 16, resp); 778b4dbc599SNathan Whitehorn mtx_unlock(&sc->sc_mutex); 779b4dbc599SNathan Whitehorn 780b4dbc599SNathan Whitehorn if (poll) 781b4dbc599SNathan Whitehorn pmu_poll(dev); 782b4dbc599SNathan Whitehorn 783b4dbc599SNathan Whitehorn return 0; 784b4dbc599SNathan Whitehorn } 785b4dbc599SNathan Whitehorn 786b4dbc599SNathan Whitehorn static u_int 787b4dbc599SNathan Whitehorn pmu_adb_autopoll(device_t dev, uint16_t mask) 788b4dbc599SNathan Whitehorn { 789b4dbc599SNathan Whitehorn struct pmu_softc *sc = device_get_softc(dev); 790b4dbc599SNathan Whitehorn 791b4dbc599SNathan Whitehorn /* magical incantation to re-enable autopolling */ 792b4dbc599SNathan Whitehorn uint8_t cmd[] = {0, PMU_SET_POLL_MASK, (mask >> 8) & 0xff, mask & 0xff}; 793b4dbc599SNathan Whitehorn uint8_t resp[16]; 794b4dbc599SNathan Whitehorn 795b4dbc599SNathan Whitehorn mtx_lock(&sc->sc_mutex); 796b4dbc599SNathan Whitehorn 797b4dbc599SNathan Whitehorn if (sc->sc_autopoll == mask) { 798b4dbc599SNathan Whitehorn mtx_unlock(&sc->sc_mutex); 799b4dbc599SNathan Whitehorn return 0; 800b4dbc599SNathan Whitehorn } 801b4dbc599SNathan Whitehorn 802b4dbc599SNathan Whitehorn sc->sc_autopoll = mask & 0xffff; 803b4dbc599SNathan Whitehorn 804b4dbc599SNathan Whitehorn if (mask) 805b4dbc599SNathan Whitehorn pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, resp); 806b4dbc599SNathan Whitehorn else 807b4dbc599SNathan Whitehorn pmu_send(sc, PMU_ADB_POLL_OFF, 0, NULL, 16, resp); 808b4dbc599SNathan Whitehorn 809b4dbc599SNathan Whitehorn mtx_unlock(&sc->sc_mutex); 810b4dbc599SNathan Whitehorn 811b4dbc599SNathan Whitehorn return 0; 812b4dbc599SNathan Whitehorn } 8131af2e191SNathan Whitehorn 814a228f5cdSNathan Whitehorn static void 815b2a237beSNathan Whitehorn pmu_shutdown(void *xsc, int howto) 816b2a237beSNathan Whitehorn { 817b2a237beSNathan Whitehorn struct pmu_softc *sc = xsc; 818b2a237beSNathan Whitehorn uint8_t cmd[] = {'M', 'A', 'T', 'T'}; 819b2a237beSNathan Whitehorn 82041e26e82SMitchell Horne if ((howto & RB_POWEROFF) != 0) 821b2a237beSNathan Whitehorn pmu_send(sc, PMU_POWER_OFF, 4, cmd, 0, NULL); 82241e26e82SMitchell Horne else if ((howto & RB_HALT) == 0) 823b2a237beSNathan Whitehorn pmu_send(sc, PMU_RESET_CPU, 0, NULL, 0, NULL); 82441e26e82SMitchell Horne else 82541e26e82SMitchell Horne return; 826b2a237beSNathan Whitehorn 827b2a237beSNathan Whitehorn for (;;); 828b2a237beSNathan Whitehorn } 829b2a237beSNathan Whitehorn 830b2a237beSNathan Whitehorn static void 831a228f5cdSNathan Whitehorn pmu_set_sleepled(void *xsc, int onoff) 832a228f5cdSNathan Whitehorn { 833a228f5cdSNathan Whitehorn struct pmu_softc *sc = xsc; 834a228f5cdSNathan Whitehorn uint8_t cmd[] = {4, 0, 0}; 835a228f5cdSNathan Whitehorn 836a228f5cdSNathan Whitehorn cmd[2] = onoff; 837a228f5cdSNathan Whitehorn 838a228f5cdSNathan Whitehorn mtx_lock(&sc->sc_mutex); 839a228f5cdSNathan Whitehorn pmu_send(sc, PMU_SET_SLEEPLED, 3, cmd, 0, NULL); 840a228f5cdSNathan Whitehorn mtx_unlock(&sc->sc_mutex); 841a228f5cdSNathan Whitehorn } 842a228f5cdSNathan Whitehorn 8431af2e191SNathan Whitehorn static int 8441af2e191SNathan Whitehorn pmu_server_mode(SYSCTL_HANDLER_ARGS) 8451af2e191SNathan Whitehorn { 8461af2e191SNathan Whitehorn struct pmu_softc *sc = arg1; 8471af2e191SNathan Whitehorn 8481af2e191SNathan Whitehorn u_int server_mode = 0; 8491af2e191SNathan Whitehorn uint8_t getcmd[] = {PMU_PWR_GET_POWERUP_EVENTS}; 8501af2e191SNathan Whitehorn uint8_t setcmd[] = {0, 0, PMU_PWR_WAKEUP_AC_INSERT}; 8511af2e191SNathan Whitehorn uint8_t resp[3]; 8521af2e191SNathan Whitehorn int error, len; 8531af2e191SNathan Whitehorn 8541af2e191SNathan Whitehorn mtx_lock(&sc->sc_mutex); 8551af2e191SNathan Whitehorn len = pmu_send(sc, PMU_POWER_EVENTS, 1, getcmd, 3, resp); 8561af2e191SNathan Whitehorn mtx_unlock(&sc->sc_mutex); 8571af2e191SNathan Whitehorn 8581af2e191SNathan Whitehorn if (len == 3) 8591af2e191SNathan Whitehorn server_mode = (resp[2] & PMU_PWR_WAKEUP_AC_INSERT) ? 1 : 0; 8601af2e191SNathan Whitehorn 8611af2e191SNathan Whitehorn error = sysctl_handle_int(oidp, &server_mode, 0, req); 8621af2e191SNathan Whitehorn 8631af2e191SNathan Whitehorn if (len != 3) 8641af2e191SNathan Whitehorn return (EINVAL); 8651af2e191SNathan Whitehorn 8661af2e191SNathan Whitehorn if (error || !req->newptr) 8671af2e191SNathan Whitehorn return (error); 8681af2e191SNathan Whitehorn 8691af2e191SNathan Whitehorn if (server_mode == 1) 8701af2e191SNathan Whitehorn setcmd[0] = PMU_PWR_SET_POWERUP_EVENTS; 8711af2e191SNathan Whitehorn else if (server_mode == 0) 8721af2e191SNathan Whitehorn setcmd[0] = PMU_PWR_CLR_POWERUP_EVENTS; 8731af2e191SNathan Whitehorn else 8741af2e191SNathan Whitehorn return (EINVAL); 8751af2e191SNathan Whitehorn 8761af2e191SNathan Whitehorn setcmd[1] = resp[1]; 8771af2e191SNathan Whitehorn 8781af2e191SNathan Whitehorn mtx_lock(&sc->sc_mutex); 8791af2e191SNathan Whitehorn pmu_send(sc, PMU_POWER_EVENTS, 3, setcmd, 2, resp); 8801af2e191SNathan Whitehorn mtx_unlock(&sc->sc_mutex); 8811af2e191SNathan Whitehorn 8821af2e191SNathan Whitehorn return (0); 8831af2e191SNathan Whitehorn } 8841af2e191SNathan Whitehorn 885eff47708SNathan Whitehorn static int 886eff47708SNathan Whitehorn pmu_query_battery(struct pmu_softc *sc, int batt, struct pmu_battstate *info) 887eff47708SNathan Whitehorn { 888eff47708SNathan Whitehorn uint8_t reg; 889eff47708SNathan Whitehorn uint8_t resp[16]; 890eff47708SNathan Whitehorn int len; 891eff47708SNathan Whitehorn 892eff47708SNathan Whitehorn reg = batt + 1; 893eff47708SNathan Whitehorn 894eff47708SNathan Whitehorn mtx_lock(&sc->sc_mutex); 895eff47708SNathan Whitehorn len = pmu_send(sc, PMU_SMART_BATTERY_STATE, 1, ®, 16, resp); 896eff47708SNathan Whitehorn mtx_unlock(&sc->sc_mutex); 897eff47708SNathan Whitehorn 898eff47708SNathan Whitehorn if (len < 3) 899eff47708SNathan Whitehorn return (-1); 900eff47708SNathan Whitehorn 901eff47708SNathan Whitehorn /* All PMU battery info replies share a common header: 902eff47708SNathan Whitehorn * Byte 1 Payload Format 903eff47708SNathan Whitehorn * Byte 2 Battery Flags 904eff47708SNathan Whitehorn */ 905eff47708SNathan Whitehorn 906eff47708SNathan Whitehorn info->state = resp[2]; 907eff47708SNathan Whitehorn 908eff47708SNathan Whitehorn switch (resp[1]) { 909eff47708SNathan Whitehorn case 3: 910eff47708SNathan Whitehorn case 4: 911eff47708SNathan Whitehorn /* 912eff47708SNathan Whitehorn * Formats 3 and 4 appear to be the same: 913eff47708SNathan Whitehorn * Byte 3 Charge 914eff47708SNathan Whitehorn * Byte 4 Max Charge 915eff47708SNathan Whitehorn * Byte 5 Current 916eff47708SNathan Whitehorn * Byte 6 Voltage 917eff47708SNathan Whitehorn */ 918eff47708SNathan Whitehorn 919eff47708SNathan Whitehorn info->charge = resp[3]; 920eff47708SNathan Whitehorn info->maxcharge = resp[4]; 921eff47708SNathan Whitehorn /* Current can be positive or negative */ 922eff47708SNathan Whitehorn info->current = (int8_t)resp[5]; 923eff47708SNathan Whitehorn info->voltage = resp[6]; 924eff47708SNathan Whitehorn break; 925eff47708SNathan Whitehorn case 5: 926eff47708SNathan Whitehorn /* 927eff47708SNathan Whitehorn * Formats 5 is a wider version of formats 3 and 4 928eff47708SNathan Whitehorn * Byte 3-4 Charge 929eff47708SNathan Whitehorn * Byte 5-6 Max Charge 930eff47708SNathan Whitehorn * Byte 7-8 Current 931eff47708SNathan Whitehorn * Byte 9-10 Voltage 932eff47708SNathan Whitehorn */ 933eff47708SNathan Whitehorn 934eff47708SNathan Whitehorn info->charge = (resp[3] << 8) | resp[4]; 935eff47708SNathan Whitehorn info->maxcharge = (resp[5] << 8) | resp[6]; 936eff47708SNathan Whitehorn /* Current can be positive or negative */ 937eff47708SNathan Whitehorn info->current = (int16_t)((resp[7] << 8) | resp[8]); 938eff47708SNathan Whitehorn info->voltage = (resp[9] << 8) | resp[10]; 939eff47708SNathan Whitehorn break; 940eff47708SNathan Whitehorn default: 941eff47708SNathan Whitehorn device_printf(sc->sc_dev, "Unknown battery info format (%d)!\n", 942eff47708SNathan Whitehorn resp[1]); 943eff47708SNathan Whitehorn return (-1); 944eff47708SNathan Whitehorn } 945eff47708SNathan Whitehorn 946eff47708SNathan Whitehorn return (0); 947eff47708SNathan Whitehorn } 948eff47708SNathan Whitehorn 949d49c6f02SJustin Hibbits static void 950d49c6f02SJustin Hibbits pmu_battery_notify(struct pmu_battstate *batt, struct pmu_battstate *old) 951d49c6f02SJustin Hibbits { 952d49c6f02SJustin Hibbits char notify_buf[16]; 9533cd55688SJustin Hibbits int new_acline, old_acline; 954d49c6f02SJustin Hibbits 9553cd55688SJustin Hibbits new_acline = (batt->state & PMU_PWR_AC_PRESENT) ? 1 : 0; 9563cd55688SJustin Hibbits old_acline = (old->state & PMU_PWR_AC_PRESENT) ? 1 : 0; 9573cd55688SJustin Hibbits 9583cd55688SJustin Hibbits if (new_acline != old_acline) { 959d49c6f02SJustin Hibbits snprintf(notify_buf, sizeof(notify_buf), 9603cd55688SJustin Hibbits "notify=0x%02x", new_acline); 961d49c6f02SJustin Hibbits devctl_notify("PMU", "POWER", "ACLINE", notify_buf); 962d49c6f02SJustin Hibbits } 963d49c6f02SJustin Hibbits } 964d49c6f02SJustin Hibbits 965d49c6f02SJustin Hibbits static void 9665f2995aaSJohn Baldwin pmu_battquery_proc(void) 967d49c6f02SJustin Hibbits { 968d49c6f02SJustin Hibbits struct pmu_softc *sc; 969d49c6f02SJustin Hibbits struct pmu_battstate batt; 970d49c6f02SJustin Hibbits struct pmu_battstate cur_batt; 971d49c6f02SJustin Hibbits int error; 972d49c6f02SJustin Hibbits 973d49c6f02SJustin Hibbits sc = device_get_softc(pmu); 974d49c6f02SJustin Hibbits 9753cd55688SJustin Hibbits bzero(&cur_batt, sizeof(cur_batt)); 976d49c6f02SJustin Hibbits while (1) { 9773cd55688SJustin Hibbits kproc_suspend_check(curproc); 978d49c6f02SJustin Hibbits error = pmu_query_battery(sc, 0, &batt); 9795f2995aaSJohn Baldwin if (error == 0) { 980d49c6f02SJustin Hibbits pmu_battery_notify(&batt, &cur_batt); 981d49c6f02SJustin Hibbits cur_batt = batt; 9825f2995aaSJohn Baldwin } 983d49c6f02SJustin Hibbits pause("pmu_batt", hz); 984d49c6f02SJustin Hibbits } 985d49c6f02SJustin Hibbits } 986d49c6f02SJustin Hibbits 987eff47708SNathan Whitehorn static int 9883cd55688SJustin Hibbits pmu_battmon(SYSCTL_HANDLER_ARGS) 9893cd55688SJustin Hibbits { 9903cd55688SJustin Hibbits int error, result; 9913cd55688SJustin Hibbits 9923cd55688SJustin Hibbits result = pmu_battmon_enabled; 9933cd55688SJustin Hibbits 9943cd55688SJustin Hibbits error = sysctl_handle_int(oidp, &result, 0, req); 9953cd55688SJustin Hibbits 9963cd55688SJustin Hibbits if (error || !req->newptr) 9973cd55688SJustin Hibbits return (error); 9983cd55688SJustin Hibbits 9993cd55688SJustin Hibbits if (!result && pmu_battmon_enabled) 10003cd55688SJustin Hibbits error = kproc_suspend(pmubattproc, hz); 10013cd55688SJustin Hibbits else if (result && pmu_battmon_enabled == 0) 10023cd55688SJustin Hibbits error = kproc_resume(pmubattproc); 10033cd55688SJustin Hibbits pmu_battmon_enabled = (result != 0); 10043cd55688SJustin Hibbits 10053cd55688SJustin Hibbits return (error); 10063cd55688SJustin Hibbits } 10073cd55688SJustin Hibbits 10083cd55688SJustin Hibbits static int 10091165ddc2SNathan Whitehorn pmu_acline_state(SYSCTL_HANDLER_ARGS) 10101165ddc2SNathan Whitehorn { 10111165ddc2SNathan Whitehorn struct pmu_softc *sc; 10121165ddc2SNathan Whitehorn struct pmu_battstate batt; 10131165ddc2SNathan Whitehorn int error, result; 10141165ddc2SNathan Whitehorn 10151165ddc2SNathan Whitehorn sc = arg1; 10161165ddc2SNathan Whitehorn 10171165ddc2SNathan Whitehorn /* The PMU treats the AC line status as a property of the battery */ 10181165ddc2SNathan Whitehorn error = pmu_query_battery(sc, 0, &batt); 10191165ddc2SNathan Whitehorn 10201165ddc2SNathan Whitehorn if (error != 0) 10211165ddc2SNathan Whitehorn return (error); 10221165ddc2SNathan Whitehorn 10231165ddc2SNathan Whitehorn result = (batt.state & PMU_PWR_AC_PRESENT) ? 1 : 0; 10241165ddc2SNathan Whitehorn error = sysctl_handle_int(oidp, &result, 0, req); 10251165ddc2SNathan Whitehorn 10261165ddc2SNathan Whitehorn return (error); 10271165ddc2SNathan Whitehorn } 10281165ddc2SNathan Whitehorn 10291165ddc2SNathan Whitehorn static int 1030eff47708SNathan Whitehorn pmu_battquery_sysctl(SYSCTL_HANDLER_ARGS) 1031eff47708SNathan Whitehorn { 1032eff47708SNathan Whitehorn struct pmu_softc *sc; 1033eff47708SNathan Whitehorn struct pmu_battstate batt; 1034eff47708SNathan Whitehorn int error, result; 1035eff47708SNathan Whitehorn 1036eff47708SNathan Whitehorn sc = arg1; 1037eff47708SNathan Whitehorn 1038eff47708SNathan Whitehorn error = pmu_query_battery(sc, arg2 & 0x00ff, &batt); 1039eff47708SNathan Whitehorn 1040eff47708SNathan Whitehorn if (error != 0) 1041eff47708SNathan Whitehorn return (error); 1042eff47708SNathan Whitehorn 1043eff47708SNathan Whitehorn switch (arg2 & 0xff00) { 1044eff47708SNathan Whitehorn case PMU_BATSYSCTL_PRESENT: 1045eff47708SNathan Whitehorn result = (batt.state & PMU_PWR_BATT_PRESENT) ? 1 : 0; 1046eff47708SNathan Whitehorn break; 1047eff47708SNathan Whitehorn case PMU_BATSYSCTL_CHARGING: 1048eff47708SNathan Whitehorn result = (batt.state & PMU_PWR_BATT_CHARGING) ? 1 : 0; 1049eff47708SNathan Whitehorn break; 1050eff47708SNathan Whitehorn case PMU_BATSYSCTL_CHARGE: 1051eff47708SNathan Whitehorn result = batt.charge; 1052eff47708SNathan Whitehorn break; 1053eff47708SNathan Whitehorn case PMU_BATSYSCTL_MAXCHARGE: 1054eff47708SNathan Whitehorn result = batt.maxcharge; 1055eff47708SNathan Whitehorn break; 1056eff47708SNathan Whitehorn case PMU_BATSYSCTL_CURRENT: 1057eff47708SNathan Whitehorn result = batt.current; 1058eff47708SNathan Whitehorn break; 1059eff47708SNathan Whitehorn case PMU_BATSYSCTL_VOLTAGE: 1060eff47708SNathan Whitehorn result = batt.voltage; 1061eff47708SNathan Whitehorn break; 1062eff47708SNathan Whitehorn case PMU_BATSYSCTL_TIME: 1063eff47708SNathan Whitehorn /* Time remaining until full charge/discharge, in minutes */ 1064eff47708SNathan Whitehorn 1065eff47708SNathan Whitehorn if (batt.current >= 0) 1066eff47708SNathan Whitehorn result = (batt.maxcharge - batt.charge) /* mAh */ * 60 1067eff47708SNathan Whitehorn / batt.current /* mA */; 1068eff47708SNathan Whitehorn else 1069eff47708SNathan Whitehorn result = (batt.charge /* mAh */ * 60) 1070eff47708SNathan Whitehorn / (-batt.current /* mA */); 1071eff47708SNathan Whitehorn break; 1072eff47708SNathan Whitehorn case PMU_BATSYSCTL_LIFE: 1073eff47708SNathan Whitehorn /* Battery charge fraction, in percent */ 1074eff47708SNathan Whitehorn result = (batt.charge * 100) / batt.maxcharge; 1075eff47708SNathan Whitehorn break; 1076eff47708SNathan Whitehorn default: 1077eff47708SNathan Whitehorn /* This should never happen */ 1078eff47708SNathan Whitehorn result = -1; 107974b8d63dSPedro F. Giffuni } 1080eff47708SNathan Whitehorn 1081eff47708SNathan Whitehorn error = sysctl_handle_int(oidp, &result, 0, req); 1082eff47708SNathan Whitehorn 1083eff47708SNathan Whitehorn return (error); 1084eff47708SNathan Whitehorn } 1085eff47708SNathan Whitehorn 10863df9e037SNathan Whitehorn #define DIFF19041970 2082844800 10873df9e037SNathan Whitehorn 10883df9e037SNathan Whitehorn static int 10893df9e037SNathan Whitehorn pmu_gettime(device_t dev, struct timespec *ts) 10903df9e037SNathan Whitehorn { 10913df9e037SNathan Whitehorn struct pmu_softc *sc = device_get_softc(dev); 10923df9e037SNathan Whitehorn uint8_t resp[16]; 10933df9e037SNathan Whitehorn uint32_t sec; 10943df9e037SNathan Whitehorn 10953df9e037SNathan Whitehorn mtx_lock(&sc->sc_mutex); 10963df9e037SNathan Whitehorn pmu_send(sc, PMU_READ_RTC, 0, NULL, 16, resp); 10973df9e037SNathan Whitehorn mtx_unlock(&sc->sc_mutex); 10983df9e037SNathan Whitehorn 10993df9e037SNathan Whitehorn memcpy(&sec, &resp[1], 4); 11003df9e037SNathan Whitehorn ts->tv_sec = sec - DIFF19041970; 11013df9e037SNathan Whitehorn ts->tv_nsec = 0; 11023df9e037SNathan Whitehorn 11033df9e037SNathan Whitehorn return (0); 11043df9e037SNathan Whitehorn } 11053df9e037SNathan Whitehorn 11063df9e037SNathan Whitehorn static int 11073df9e037SNathan Whitehorn pmu_settime(device_t dev, struct timespec *ts) 11083df9e037SNathan Whitehorn { 11093df9e037SNathan Whitehorn struct pmu_softc *sc = device_get_softc(dev); 11103df9e037SNathan Whitehorn uint32_t sec; 11113df9e037SNathan Whitehorn 11123df9e037SNathan Whitehorn sec = ts->tv_sec + DIFF19041970; 11133df9e037SNathan Whitehorn 11143df9e037SNathan Whitehorn mtx_lock(&sc->sc_mutex); 11153df9e037SNathan Whitehorn pmu_send(sc, PMU_SET_RTC, sizeof(sec), (uint8_t *)&sec, 0, NULL); 11163df9e037SNathan Whitehorn mtx_unlock(&sc->sc_mutex); 11173df9e037SNathan Whitehorn 11183df9e037SNathan Whitehorn return (0); 11193df9e037SNathan Whitehorn } 11203df9e037SNathan Whitehorn 11214702d987SJustin Hibbits int 11224702d987SJustin Hibbits pmu_set_speed(int low_speed) 11234702d987SJustin Hibbits { 11244702d987SJustin Hibbits struct pmu_softc *sc; 11254702d987SJustin Hibbits uint8_t sleepcmd[] = {'W', 'O', 'O', 'F', 0}; 11264702d987SJustin Hibbits uint8_t resp[16]; 11274702d987SJustin Hibbits 11284702d987SJustin Hibbits sc = device_get_softc(pmu); 11294702d987SJustin Hibbits pmu_write_reg(sc, vIER, 0x10); 11304702d987SJustin Hibbits spinlock_enter(); 11314702d987SJustin Hibbits mtdec(0x7fffffff); 11324702d987SJustin Hibbits mb(); 11334702d987SJustin Hibbits mtdec(0x7fffffff); 11344702d987SJustin Hibbits 11354702d987SJustin Hibbits sleepcmd[4] = low_speed; 11364702d987SJustin Hibbits pmu_send(sc, PMU_CPU_SPEED, 5, sleepcmd, 16, resp); 11374702d987SJustin Hibbits unin_chip_sleep(NULL, 1); 1138e1c161e7SJustin Hibbits platform_sleep(); 11394702d987SJustin Hibbits unin_chip_wake(NULL); 11404702d987SJustin Hibbits 11414702d987SJustin Hibbits mtdec(1); /* Force a decrementer exception */ 11424702d987SJustin Hibbits spinlock_exit(); 11434702d987SJustin Hibbits pmu_write_reg(sc, vIER, 0x90); 11444702d987SJustin Hibbits 11454702d987SJustin Hibbits return (0); 11464702d987SJustin Hibbits } 1147