1184299Snwhitehorn/*- 2184299Snwhitehorn * Copyright (c) 2006 Michael Lorenz 3184299Snwhitehorn * Copyright 2008 by Nathan Whitehorn 4184299Snwhitehorn * All rights reserved. 5184299Snwhitehorn * 6184299Snwhitehorn * Redistribution and use in source and binary forms, with or without 7184299Snwhitehorn * modification, are permitted provided that the following conditions 8184299Snwhitehorn * are met: 9184299Snwhitehorn * 1. Redistributions of source code must retain the above copyright 10184299Snwhitehorn * notice, this list of conditions and the following disclaimer. 11184299Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright 12184299Snwhitehorn * notice, this list of conditions and the following disclaimer in the 13184299Snwhitehorn * documentation and/or other materials provided with the distribution. 14184299Snwhitehorn * 15184299Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16184299Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17184299Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18184299Snwhitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19184299Snwhitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20184299Snwhitehorn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21184299Snwhitehorn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22184299Snwhitehorn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23184299Snwhitehorn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24184299Snwhitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25184299Snwhitehorn * SUCH DAMAGE. 26184299Snwhitehorn * 27184299Snwhitehorn */ 28184299Snwhitehorn 29184299Snwhitehorn#include <sys/cdefs.h> 30184299Snwhitehorn__FBSDID("$FreeBSD: releng/11.0/sys/powerpc/powermac/pmu.c 297793 2016-04-10 23:07:00Z pfg $"); 31184299Snwhitehorn 32184299Snwhitehorn#include <sys/param.h> 33184299Snwhitehorn#include <sys/systm.h> 34184299Snwhitehorn#include <sys/module.h> 35184299Snwhitehorn#include <sys/bus.h> 36184299Snwhitehorn#include <sys/conf.h> 37184299Snwhitehorn#include <sys/kernel.h> 38273009Sjhibbits#include <sys/kthread.h> 39205506Snwhitehorn#include <sys/clock.h> 40259284Sjhibbits#include <sys/proc.h> 41212054Snwhitehorn#include <sys/reboot.h> 42185727Snwhitehorn#include <sys/sysctl.h> 43184299Snwhitehorn 44184299Snwhitehorn#include <dev/ofw/ofw_bus.h> 45184299Snwhitehorn#include <dev/ofw/openfirm.h> 46185782Snwhitehorn#include <dev/led/led.h> 47184299Snwhitehorn 48259284Sjhibbits#include <machine/_inttypes.h> 49184299Snwhitehorn#include <machine/bus.h> 50259284Sjhibbits#include <machine/cpu.h> 51259284Sjhibbits#include <machine/hid.h> 52184299Snwhitehorn#include <machine/intr_machdep.h> 53184299Snwhitehorn#include <machine/md_var.h> 54259284Sjhibbits#include <machine/pcb.h> 55184299Snwhitehorn#include <machine/pio.h> 56184299Snwhitehorn#include <machine/resource.h> 57184299Snwhitehorn 58184299Snwhitehorn#include <vm/vm.h> 59184299Snwhitehorn#include <vm/pmap.h> 60184299Snwhitehorn 61184299Snwhitehorn#include <sys/rman.h> 62184299Snwhitehorn 63184299Snwhitehorn#include <dev/adb/adb.h> 64184299Snwhitehorn 65205506Snwhitehorn#include "clock_if.h" 66184299Snwhitehorn#include "pmuvar.h" 67184299Snwhitehorn#include "viareg.h" 68259284Sjhibbits#include "uninorthvar.h" /* For unin_chip_sleep()/unin_chip_wake() */ 69184299Snwhitehorn 70259284Sjhibbits#define PMU_DEFAULTS PMU_INT_TICK | PMU_INT_ADB | \ 71259284Sjhibbits PMU_INT_PCEJECT | PMU_INT_SNDBRT | \ 72259284Sjhibbits PMU_INT_BATTERY | PMU_INT_ENVIRONMENT 73259284Sjhibbits 74184299Snwhitehorn/* 75205506Snwhitehorn * Bus interface 76184299Snwhitehorn */ 77184299Snwhitehornstatic int pmu_probe(device_t); 78184299Snwhitehornstatic int pmu_attach(device_t); 79184299Snwhitehornstatic int pmu_detach(device_t); 80184299Snwhitehorn 81205506Snwhitehorn/* 82205506Snwhitehorn * Clock interface 83205506Snwhitehorn */ 84205506Snwhitehornstatic int pmu_gettime(device_t dev, struct timespec *ts); 85205506Snwhitehornstatic int pmu_settime(device_t dev, struct timespec *ts); 86205506Snwhitehorn 87205506Snwhitehorn/* 88205506Snwhitehorn * ADB Interface 89205506Snwhitehorn */ 90205506Snwhitehorn 91185727Snwhitehornstatic u_int pmu_adb_send(device_t dev, u_char command_byte, int len, 92185754Snwhitehorn u_char *data, u_char poll); 93185727Snwhitehornstatic u_int pmu_adb_autopoll(device_t dev, uint16_t mask); 94194027Savgstatic u_int pmu_poll(device_t dev); 95185754Snwhitehorn 96212054Snwhitehorn/* 97212054Snwhitehorn * Power interface 98212054Snwhitehorn */ 99212054Snwhitehorn 100212054Snwhitehornstatic void pmu_shutdown(void *xsc, int howto); 101185782Snwhitehornstatic void pmu_set_sleepled(void *xsc, int onoff); 102185727Snwhitehornstatic int pmu_server_mode(SYSCTL_HANDLER_ARGS); 103193159Snwhitehornstatic int pmu_acline_state(SYSCTL_HANDLER_ARGS); 104185754Snwhitehornstatic int pmu_query_battery(struct pmu_softc *sc, int batt, 105185754Snwhitehorn struct pmu_battstate *info); 106185754Snwhitehornstatic int pmu_battquery_sysctl(SYSCTL_HANDLER_ARGS); 107273113Sjhibbitsstatic int pmu_battmon(SYSCTL_HANDLER_ARGS); 108273113Sjhibbitsstatic void pmu_battquery_proc(void); 109273113Sjhibbitsstatic void pmu_battery_notify(struct pmu_battstate *batt, 110273113Sjhibbits struct pmu_battstate *old); 111184299Snwhitehorn 112185754Snwhitehorn/* 113185754Snwhitehorn * List of battery-related sysctls we might ask for 114185754Snwhitehorn */ 115185754Snwhitehorn 116185754Snwhitehornenum { 117185754Snwhitehorn PMU_BATSYSCTL_PRESENT = 1 << 8, 118185754Snwhitehorn PMU_BATSYSCTL_CHARGING = 2 << 8, 119185754Snwhitehorn PMU_BATSYSCTL_CHARGE = 3 << 8, 120185754Snwhitehorn PMU_BATSYSCTL_MAXCHARGE = 4 << 8, 121185754Snwhitehorn PMU_BATSYSCTL_CURRENT = 5 << 8, 122185754Snwhitehorn PMU_BATSYSCTL_VOLTAGE = 6 << 8, 123185754Snwhitehorn PMU_BATSYSCTL_TIME = 7 << 8, 124185754Snwhitehorn PMU_BATSYSCTL_LIFE = 8 << 8 125185754Snwhitehorn}; 126185754Snwhitehorn 127184299Snwhitehornstatic device_method_t pmu_methods[] = { 128184299Snwhitehorn /* Device interface */ 129184299Snwhitehorn DEVMETHOD(device_probe, pmu_probe), 130184299Snwhitehorn DEVMETHOD(device_attach, pmu_attach), 131184299Snwhitehorn DEVMETHOD(device_detach, pmu_detach), 132184299Snwhitehorn DEVMETHOD(device_shutdown, bus_generic_shutdown), 133184299Snwhitehorn 134184299Snwhitehorn /* ADB bus interface */ 135184299Snwhitehorn DEVMETHOD(adb_hb_send_raw_packet, pmu_adb_send), 136184299Snwhitehorn DEVMETHOD(adb_hb_controller_poll, pmu_poll), 137184299Snwhitehorn DEVMETHOD(adb_hb_set_autopoll_mask, pmu_adb_autopoll), 138184299Snwhitehorn 139205506Snwhitehorn /* Clock interface */ 140205506Snwhitehorn DEVMETHOD(clock_gettime, pmu_gettime), 141205506Snwhitehorn DEVMETHOD(clock_settime, pmu_settime), 142205506Snwhitehorn 143227843Smarius DEVMETHOD_END 144184299Snwhitehorn}; 145184299Snwhitehorn 146184299Snwhitehornstatic driver_t pmu_driver = { 147184299Snwhitehorn "pmu", 148184299Snwhitehorn pmu_methods, 149184299Snwhitehorn sizeof(struct pmu_softc), 150184299Snwhitehorn}; 151184299Snwhitehorn 152184299Snwhitehornstatic devclass_t pmu_devclass; 153184299Snwhitehorn 154184299SnwhitehornDRIVER_MODULE(pmu, macio, pmu_driver, pmu_devclass, 0, 0); 155184299SnwhitehornDRIVER_MODULE(adb, pmu, adb_driver, adb_devclass, 0, 0); 156184299Snwhitehorn 157184299Snwhitehornstatic int pmuextint_probe(device_t); 158184299Snwhitehornstatic int pmuextint_attach(device_t); 159184299Snwhitehorn 160184299Snwhitehornstatic device_method_t pmuextint_methods[] = { 161184299Snwhitehorn /* Device interface */ 162184299Snwhitehorn DEVMETHOD(device_probe, pmuextint_probe), 163184299Snwhitehorn DEVMETHOD(device_attach, pmuextint_attach), 164184299Snwhitehorn 165184299Snwhitehorn {0,0} 166184299Snwhitehorn}; 167184299Snwhitehorn 168184299Snwhitehornstatic driver_t pmuextint_driver = { 169184299Snwhitehorn "pmuextint", 170184299Snwhitehorn pmuextint_methods, 171184299Snwhitehorn 0 172184299Snwhitehorn}; 173184299Snwhitehorn 174184299Snwhitehornstatic devclass_t pmuextint_devclass; 175184299Snwhitehorn 176184299SnwhitehornDRIVER_MODULE(pmuextint, macgpio, pmuextint_driver, pmuextint_devclass, 0, 0); 177184299Snwhitehorn 178184299Snwhitehorn/* Make sure uhid is loaded, as it turns off some of the ADB emulation */ 179184299SnwhitehornMODULE_DEPEND(pmu, usb, 1, 1, 1); 180184299Snwhitehorn 181184299Snwhitehornstatic void pmu_intr(void *arg); 182184299Snwhitehornstatic void pmu_in(struct pmu_softc *sc); 183184299Snwhitehornstatic void pmu_out(struct pmu_softc *sc); 184184299Snwhitehornstatic void pmu_ack_on(struct pmu_softc *sc); 185184299Snwhitehornstatic void pmu_ack_off(struct pmu_softc *sc); 186184299Snwhitehornstatic int pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg, 187184299Snwhitehorn int rlen, uint8_t *out_msg); 188184299Snwhitehornstatic uint8_t pmu_read_reg(struct pmu_softc *sc, u_int offset); 189184299Snwhitehornstatic void pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value); 190184299Snwhitehornstatic int pmu_intr_state(struct pmu_softc *); 191184299Snwhitehorn 192184299Snwhitehorn/* these values shows that number of data returned after 'send' cmd is sent */ 193184299Snwhitehornstatic signed char pm_send_cmd_type[] = { 194184299Snwhitehorn -1, -1, -1, -1, -1, -1, -1, -1, 195184299Snwhitehorn -1, -1, -1, -1, -1, -1, -1, -1, 196184299Snwhitehorn 0x01, 0x01, -1, -1, -1, -1, -1, -1, 197184299Snwhitehorn 0x00, 0x00, -1, -1, -1, -1, -1, 0x00, 198184299Snwhitehorn -1, 0x00, 0x02, 0x01, 0x01, -1, -1, -1, 199184299Snwhitehorn 0x00, -1, -1, -1, -1, -1, -1, -1, 200184299Snwhitehorn 0x04, 0x14, -1, 0x03, -1, -1, -1, -1, 201184299Snwhitehorn 0x00, 0x00, 0x02, 0x02, -1, -1, -1, -1, 202184299Snwhitehorn 0x01, 0x01, -1, -1, -1, -1, -1, -1, 203184299Snwhitehorn 0x00, 0x00, -1, -1, 0x01, -1, -1, -1, 204184299Snwhitehorn 0x01, 0x00, 0x02, 0x02, -1, 0x01, 0x03, 0x01, 205184299Snwhitehorn 0x00, 0x01, 0x00, 0x00, 0x00, -1, -1, -1, 206184299Snwhitehorn 0x02, -1, -1, -1, -1, -1, -1, -1, 207184299Snwhitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1, -1, 208184299Snwhitehorn 0x01, 0x01, 0x01, -1, -1, -1, -1, -1, 209259284Sjhibbits 0x00, 0x00, -1, -1, -1, 0x05, 0x04, 0x04, 210184299Snwhitehorn 0x04, -1, 0x00, -1, -1, -1, -1, -1, 211184299Snwhitehorn 0x00, -1, -1, -1, -1, -1, -1, -1, 212184299Snwhitehorn 0x01, 0x02, -1, -1, -1, -1, -1, -1, 213184299Snwhitehorn 0x00, 0x00, -1, -1, -1, -1, -1, -1, 214184299Snwhitehorn 0x02, 0x02, 0x02, 0x04, -1, 0x00, -1, -1, 215184299Snwhitehorn 0x01, 0x01, 0x03, 0x02, -1, -1, -1, -1, 216184299Snwhitehorn -1, -1, -1, -1, -1, -1, -1, -1, 217184299Snwhitehorn -1, -1, -1, -1, -1, -1, -1, -1, 218184299Snwhitehorn -1, -1, -1, -1, -1, -1, -1, -1, 219184299Snwhitehorn -1, -1, -1, -1, -1, -1, -1, -1, 220184299Snwhitehorn 0x00, -1, -1, -1, -1, -1, -1, -1, 221184299Snwhitehorn 0x01, 0x01, -1, -1, 0x00, 0x00, -1, -1, 222184299Snwhitehorn -1, 0x04, 0x00, -1, -1, -1, -1, -1, 223184299Snwhitehorn 0x03, -1, 0x00, -1, 0x00, -1, -1, 0x00, 224184299Snwhitehorn -1, -1, -1, -1, -1, -1, -1, -1, 225184299Snwhitehorn -1, -1, -1, -1, -1, -1, -1, -1 226184299Snwhitehorn}; 227184299Snwhitehorn 228184299Snwhitehorn/* these values shows that number of data returned after 'receive' cmd is sent */ 229184299Snwhitehornstatic signed char pm_receive_cmd_type[] = { 230184299Snwhitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 231184299Snwhitehorn -1, -1, -1, -1, -1, -1, -1, -1, 232184299Snwhitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 233184299Snwhitehorn 0x02, 0x02, -1, -1, -1, -1, -1, 0x00, 234184299Snwhitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 235184299Snwhitehorn -1, -1, -1, -1, -1, -1, -1, -1, 236184299Snwhitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 237184299Snwhitehorn 0x05, 0x15, -1, 0x02, -1, -1, -1, -1, 238184299Snwhitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 239184299Snwhitehorn 0x02, 0x02, -1, -1, -1, -1, -1, -1, 240184299Snwhitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 241184299Snwhitehorn 0x02, 0x00, 0x03, 0x03, -1, -1, -1, -1, 242184299Snwhitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 243184299Snwhitehorn 0x04, 0x04, 0x03, 0x09, -1, -1, -1, -1, 244184299Snwhitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 245259284Sjhibbits -1, -1, -1, -1, -1, 0x01, 0x01, 0x01, 246184299Snwhitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 247184299Snwhitehorn 0x06, -1, -1, -1, -1, -1, -1, -1, 248184299Snwhitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 249184299Snwhitehorn 0x02, 0x02, -1, -1, -1, -1, -1, -1, 250184299Snwhitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 251184299Snwhitehorn 0x02, 0x00, 0x00, 0x00, -1, -1, -1, -1, 252184299Snwhitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 253184299Snwhitehorn -1, -1, -1, -1, -1, -1, -1, -1, 254184299Snwhitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 255184299Snwhitehorn -1, -1, -1, -1, -1, -1, -1, -1, 256184299Snwhitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 257184299Snwhitehorn 0x02, 0x02, -1, -1, 0x02, -1, -1, -1, 258184299Snwhitehorn 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 259184299Snwhitehorn -1, -1, 0x02, -1, -1, -1, -1, 0x00, 260184299Snwhitehorn 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 261184299Snwhitehorn -1, -1, -1, -1, -1, -1, -1, -1, 262184299Snwhitehorn}; 263184299Snwhitehorn 264273113Sjhibbitsstatic int pmu_battmon_enabled = 1; 265273009Sjhibbitsstatic struct proc *pmubattproc; 266273009Sjhibbitsstatic struct kproc_desc pmu_batt_kp = { 267273009Sjhibbits "pmu_batt", 268273009Sjhibbits pmu_battquery_proc, 269273009Sjhibbits &pmubattproc 270273009Sjhibbits}; 271273009Sjhibbits 272184299Snwhitehorn/* We only have one of each device, so globals are safe */ 273184299Snwhitehornstatic device_t pmu = NULL; 274184299Snwhitehornstatic device_t pmu_extint = NULL; 275184299Snwhitehorn 276184299Snwhitehornstatic int 277184299Snwhitehornpmuextint_probe(device_t dev) 278184299Snwhitehorn{ 279184299Snwhitehorn const char *type = ofw_bus_get_type(dev); 280184299Snwhitehorn 281184299Snwhitehorn if (strcmp(type, "extint-gpio1") != 0) 282184299Snwhitehorn return (ENXIO); 283184299Snwhitehorn 284184299Snwhitehorn device_set_desc(dev, "Apple PMU99 External Interrupt"); 285184299Snwhitehorn return (0); 286184299Snwhitehorn} 287184299Snwhitehorn 288184299Snwhitehornstatic int 289184299Snwhitehornpmu_probe(device_t dev) 290184299Snwhitehorn{ 291184299Snwhitehorn const char *type = ofw_bus_get_type(dev); 292184299Snwhitehorn 293184299Snwhitehorn if (strcmp(type, "via-pmu") != 0) 294184299Snwhitehorn return (ENXIO); 295184299Snwhitehorn 296184299Snwhitehorn device_set_desc(dev, "Apple PMU99 Controller"); 297184299Snwhitehorn return (0); 298184299Snwhitehorn} 299184299Snwhitehorn 300184299Snwhitehorn 301184299Snwhitehornstatic int 302184299Snwhitehornsetup_pmu_intr(device_t dev, device_t extint) 303184299Snwhitehorn{ 304184299Snwhitehorn struct pmu_softc *sc; 305184299Snwhitehorn sc = device_get_softc(dev); 306184299Snwhitehorn 307184299Snwhitehorn sc->sc_irqrid = 0; 308184299Snwhitehorn sc->sc_irq = bus_alloc_resource_any(extint, SYS_RES_IRQ, &sc->sc_irqrid, 309184299Snwhitehorn RF_ACTIVE); 310184299Snwhitehorn if (sc->sc_irq == NULL) { 311184299Snwhitehorn device_printf(dev, "could not allocate interrupt\n"); 312184299Snwhitehorn return (ENXIO); 313184299Snwhitehorn } 314184299Snwhitehorn 315184299Snwhitehorn if (bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE 316184299Snwhitehorn | INTR_ENTROPY, NULL, pmu_intr, dev, &sc->sc_ih) != 0) { 317184299Snwhitehorn device_printf(dev, "could not setup interrupt\n"); 318184299Snwhitehorn bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, 319184299Snwhitehorn sc->sc_irq); 320184299Snwhitehorn return (ENXIO); 321184299Snwhitehorn } 322184299Snwhitehorn 323184299Snwhitehorn return (0); 324184299Snwhitehorn} 325184299Snwhitehorn 326184299Snwhitehornstatic int 327184299Snwhitehornpmuextint_attach(device_t dev) 328184299Snwhitehorn{ 329184299Snwhitehorn pmu_extint = dev; 330184299Snwhitehorn if (pmu) 331184299Snwhitehorn return (setup_pmu_intr(pmu,dev)); 332184299Snwhitehorn 333184299Snwhitehorn return (0); 334184299Snwhitehorn} 335184299Snwhitehorn 336184299Snwhitehornstatic int 337184299Snwhitehornpmu_attach(device_t dev) 338184299Snwhitehorn{ 339184299Snwhitehorn struct pmu_softc *sc; 340184299Snwhitehorn 341185754Snwhitehorn int i; 342184299Snwhitehorn uint8_t reg; 343184299Snwhitehorn uint8_t cmd[2] = {2, 0}; 344184299Snwhitehorn uint8_t resp[16]; 345184299Snwhitehorn phandle_t node,child; 346185727Snwhitehorn struct sysctl_ctx_list *ctx; 347185727Snwhitehorn struct sysctl_oid *tree; 348184299Snwhitehorn 349184299Snwhitehorn sc = device_get_softc(dev); 350184299Snwhitehorn sc->sc_dev = dev; 351184299Snwhitehorn 352184299Snwhitehorn sc->sc_memrid = 0; 353184299Snwhitehorn sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 354184299Snwhitehorn &sc->sc_memrid, RF_ACTIVE); 355184299Snwhitehorn 356184299Snwhitehorn mtx_init(&sc->sc_mutex,"pmu",NULL,MTX_DEF | MTX_RECURSE); 357184299Snwhitehorn 358184299Snwhitehorn if (sc->sc_memr == NULL) { 359184299Snwhitehorn device_printf(dev, "Could not alloc mem resource!\n"); 360184299Snwhitehorn return (ENXIO); 361184299Snwhitehorn } 362184299Snwhitehorn 363184299Snwhitehorn /* 364184299Snwhitehorn * Our interrupt is attached to a GPIO pin. Depending on probe order, 365184299Snwhitehorn * we may not have found it yet. If we haven't, it will find us, and 366184299Snwhitehorn * attach our interrupt then. 367184299Snwhitehorn */ 368184299Snwhitehorn pmu = dev; 369184299Snwhitehorn if (pmu_extint != NULL) { 370184299Snwhitehorn if (setup_pmu_intr(dev,pmu_extint) != 0) 371184299Snwhitehorn return (ENXIO); 372184299Snwhitehorn } 373184299Snwhitehorn 374184299Snwhitehorn sc->sc_autopoll = 0; 375185782Snwhitehorn sc->sc_batteries = 0; 376185782Snwhitehorn sc->adb_bus = NULL; 377185782Snwhitehorn sc->sc_leddev = NULL; 378184299Snwhitehorn 379184299Snwhitehorn /* Init PMU */ 380184299Snwhitehorn 381259284Sjhibbits pmu_write_reg(sc, vBufB, pmu_read_reg(sc, vBufB) | vPB4); 382259284Sjhibbits pmu_write_reg(sc, vDirB, (pmu_read_reg(sc, vDirB) | vPB4) & ~vPB3); 383259284Sjhibbits 384259284Sjhibbits reg = PMU_DEFAULTS; 385184299Snwhitehorn pmu_send(sc, PMU_SET_IMASK, 1, ®, 16, resp); 386184299Snwhitehorn 387259284Sjhibbits pmu_write_reg(sc, vIER, 0x94); /* make sure VIA interrupts are on */ 388184299Snwhitehorn 389184299Snwhitehorn pmu_send(sc, PMU_SYSTEM_READY, 1, cmd, 16, resp); 390260872Sjhibbits pmu_send(sc, PMU_GET_VERSION, 0, cmd, 16, resp); 391184299Snwhitehorn 392184299Snwhitehorn /* Initialize child buses (ADB) */ 393184299Snwhitehorn node = ofw_bus_get_node(dev); 394184299Snwhitehorn 395184299Snwhitehorn for (child = OF_child(node); child != 0; child = OF_peer(child)) { 396184299Snwhitehorn char name[32]; 397184299Snwhitehorn 398184299Snwhitehorn memset(name, 0, sizeof(name)); 399184299Snwhitehorn OF_getprop(child, "name", name, sizeof(name)); 400184299Snwhitehorn 401184299Snwhitehorn if (bootverbose) 402184299Snwhitehorn device_printf(dev, "PMU child <%s>\n",name); 403184299Snwhitehorn 404184299Snwhitehorn if (strncmp(name, "adb", 4) == 0) { 405184299Snwhitehorn sc->adb_bus = device_add_child(dev,"adb",-1); 406184299Snwhitehorn } 407185754Snwhitehorn 408185754Snwhitehorn if (strncmp(name, "power-mgt", 9) == 0) { 409185754Snwhitehorn uint32_t prim_info[9]; 410185754Snwhitehorn 411185754Snwhitehorn if (OF_getprop(child, "prim-info", prim_info, 412185754Snwhitehorn sizeof(prim_info)) >= 7) 413185754Snwhitehorn sc->sc_batteries = (prim_info[6] >> 16) & 0xff; 414185754Snwhitehorn 415185754Snwhitehorn if (bootverbose && sc->sc_batteries > 0) 416185754Snwhitehorn device_printf(dev, "%d batteries detected\n", 417185754Snwhitehorn sc->sc_batteries); 418185754Snwhitehorn } 419184299Snwhitehorn } 420184299Snwhitehorn 421185727Snwhitehorn /* 422185727Snwhitehorn * Set up sysctls 423185727Snwhitehorn */ 424185727Snwhitehorn 425185727Snwhitehorn ctx = device_get_sysctl_ctx(dev); 426185727Snwhitehorn tree = device_get_sysctl_tree(dev); 427185727Snwhitehorn 428185727Snwhitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 429185754Snwhitehorn "server_mode", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 430185754Snwhitehorn pmu_server_mode, "I", "Enable reboot after power failure"); 431185727Snwhitehorn 432185754Snwhitehorn if (sc->sc_batteries > 0) { 433185754Snwhitehorn struct sysctl_oid *oid, *battroot; 434185754Snwhitehorn char battnum[2]; 435185754Snwhitehorn 436273009Sjhibbits /* Only start the battery monitor if we have a battery. */ 437273009Sjhibbits kproc_start(&pmu_batt_kp); 438193159Snwhitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 439273113Sjhibbits "monitor_batteries", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 440273113Sjhibbits pmu_battmon, "I", "Post battery events to devd"); 441273113Sjhibbits 442273113Sjhibbits 443273113Sjhibbits SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 444193159Snwhitehorn "acline", CTLTYPE_INT | CTLFLAG_RD, sc, 0, 445193159Snwhitehorn pmu_acline_state, "I", "AC Line Status"); 446193159Snwhitehorn 447185754Snwhitehorn battroot = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 448185754Snwhitehorn "batteries", CTLFLAG_RD, 0, "Battery Information"); 449185754Snwhitehorn 450185754Snwhitehorn for (i = 0; i < sc->sc_batteries; i++) { 451185754Snwhitehorn battnum[0] = i + '0'; 452185754Snwhitehorn battnum[1] = '\0'; 453185754Snwhitehorn 454185754Snwhitehorn oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(battroot), 455185754Snwhitehorn OID_AUTO, battnum, CTLFLAG_RD, 0, 456185754Snwhitehorn "Battery Information"); 457185754Snwhitehorn 458185754Snwhitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 459185754Snwhitehorn "present", CTLTYPE_INT | CTLFLAG_RD, sc, 460185754Snwhitehorn PMU_BATSYSCTL_PRESENT | i, pmu_battquery_sysctl, 461185754Snwhitehorn "I", "Battery present"); 462185754Snwhitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 463185754Snwhitehorn "charging", CTLTYPE_INT | CTLFLAG_RD, sc, 464185754Snwhitehorn PMU_BATSYSCTL_CHARGING | i, pmu_battquery_sysctl, 465185754Snwhitehorn "I", "Battery charging"); 466185754Snwhitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 467185754Snwhitehorn "charge", CTLTYPE_INT | CTLFLAG_RD, sc, 468185754Snwhitehorn PMU_BATSYSCTL_CHARGE | i, pmu_battquery_sysctl, 469185754Snwhitehorn "I", "Battery charge (mAh)"); 470185754Snwhitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 471185754Snwhitehorn "maxcharge", CTLTYPE_INT | CTLFLAG_RD, sc, 472185754Snwhitehorn PMU_BATSYSCTL_MAXCHARGE | i, pmu_battquery_sysctl, 473185754Snwhitehorn "I", "Maximum battery capacity (mAh)"); 474185754Snwhitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 475185754Snwhitehorn "rate", CTLTYPE_INT | CTLFLAG_RD, sc, 476185754Snwhitehorn PMU_BATSYSCTL_CURRENT | i, pmu_battquery_sysctl, 477185754Snwhitehorn "I", "Battery discharge rate (mA)"); 478185754Snwhitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 479185754Snwhitehorn "voltage", CTLTYPE_INT | CTLFLAG_RD, sc, 480185754Snwhitehorn PMU_BATSYSCTL_VOLTAGE | i, pmu_battquery_sysctl, 481185754Snwhitehorn "I", "Battery voltage (mV)"); 482185754Snwhitehorn 483185754Snwhitehorn /* Knobs for mental compatibility with ACPI */ 484185754Snwhitehorn 485185754Snwhitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 486185754Snwhitehorn "time", CTLTYPE_INT | CTLFLAG_RD, sc, 487185754Snwhitehorn PMU_BATSYSCTL_TIME | i, pmu_battquery_sysctl, 488185754Snwhitehorn "I", "Time Remaining (minutes)"); 489185754Snwhitehorn SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 490185754Snwhitehorn "life", CTLTYPE_INT | CTLFLAG_RD, sc, 491185754Snwhitehorn PMU_BATSYSCTL_LIFE | i, pmu_battquery_sysctl, 492185754Snwhitehorn "I", "Capacity remaining (percent)"); 493185754Snwhitehorn } 494185754Snwhitehorn } 495185754Snwhitehorn 496185782Snwhitehorn /* 497185782Snwhitehorn * Set up LED interface 498185782Snwhitehorn */ 499185782Snwhitehorn 500185782Snwhitehorn sc->sc_leddev = led_create(pmu_set_sleepled, sc, "sleepled"); 501185782Snwhitehorn 502205506Snwhitehorn /* 503205506Snwhitehorn * Register RTC 504205506Snwhitehorn */ 505205506Snwhitehorn 506205506Snwhitehorn clock_register(dev, 1000); 507205506Snwhitehorn 508212054Snwhitehorn /* 509212054Snwhitehorn * Register power control handler 510212054Snwhitehorn */ 511212054Snwhitehorn EVENTHANDLER_REGISTER(shutdown_final, pmu_shutdown, sc, 512212054Snwhitehorn SHUTDOWN_PRI_LAST); 513212054Snwhitehorn 514184299Snwhitehorn return (bus_generic_attach(dev)); 515184299Snwhitehorn} 516184299Snwhitehorn 517184299Snwhitehornstatic int 518184299Snwhitehornpmu_detach(device_t dev) 519184299Snwhitehorn{ 520184299Snwhitehorn struct pmu_softc *sc; 521184299Snwhitehorn 522184299Snwhitehorn sc = device_get_softc(dev); 523184299Snwhitehorn 524185782Snwhitehorn if (sc->sc_leddev != NULL) 525185782Snwhitehorn led_destroy(sc->sc_leddev); 526185782Snwhitehorn 527184299Snwhitehorn bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih); 528184299Snwhitehorn bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, sc->sc_irq); 529184299Snwhitehorn bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_memrid, sc->sc_memr); 530184299Snwhitehorn mtx_destroy(&sc->sc_mutex); 531184299Snwhitehorn 532184299Snwhitehorn return (bus_generic_detach(dev)); 533184299Snwhitehorn} 534184299Snwhitehorn 535184299Snwhitehornstatic uint8_t 536184299Snwhitehornpmu_read_reg(struct pmu_softc *sc, u_int offset) 537184299Snwhitehorn{ 538184299Snwhitehorn return (bus_read_1(sc->sc_memr, offset)); 539184299Snwhitehorn} 540184299Snwhitehorn 541184299Snwhitehornstatic void 542184299Snwhitehornpmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value) 543184299Snwhitehorn{ 544184299Snwhitehorn bus_write_1(sc->sc_memr, offset, value); 545184299Snwhitehorn} 546184299Snwhitehorn 547184299Snwhitehornstatic int 548184299Snwhitehornpmu_send_byte(struct pmu_softc *sc, uint8_t data) 549184299Snwhitehorn{ 550184299Snwhitehorn 551184299Snwhitehorn pmu_out(sc); 552184299Snwhitehorn pmu_write_reg(sc, vSR, data); 553184299Snwhitehorn pmu_ack_off(sc); 554184299Snwhitehorn /* wait for intr to come up */ 555184299Snwhitehorn /* XXX should add a timeout and bail if it expires */ 556184299Snwhitehorn do {} while (pmu_intr_state(sc) == 0); 557184299Snwhitehorn pmu_ack_on(sc); 558184299Snwhitehorn do {} while (pmu_intr_state(sc)); 559184299Snwhitehorn pmu_ack_on(sc); 560184299Snwhitehorn return 0; 561184299Snwhitehorn} 562184299Snwhitehorn 563184299Snwhitehornstatic inline int 564184299Snwhitehornpmu_read_byte(struct pmu_softc *sc, uint8_t *data) 565184299Snwhitehorn{ 566184299Snwhitehorn volatile uint8_t scratch; 567184299Snwhitehorn pmu_in(sc); 568184299Snwhitehorn scratch = pmu_read_reg(sc, vSR); 569184299Snwhitehorn pmu_ack_off(sc); 570184299Snwhitehorn /* wait for intr to come up */ 571184299Snwhitehorn do {} while (pmu_intr_state(sc) == 0); 572184299Snwhitehorn pmu_ack_on(sc); 573184299Snwhitehorn do {} while (pmu_intr_state(sc)); 574184299Snwhitehorn *data = pmu_read_reg(sc, vSR); 575184299Snwhitehorn return 0; 576184299Snwhitehorn} 577184299Snwhitehorn 578184299Snwhitehornstatic int 579184299Snwhitehornpmu_intr_state(struct pmu_softc *sc) 580184299Snwhitehorn{ 581184299Snwhitehorn return ((pmu_read_reg(sc, vBufB) & vPB3) == 0); 582184299Snwhitehorn} 583184299Snwhitehorn 584184299Snwhitehornstatic int 585184299Snwhitehornpmu_send(void *cookie, int cmd, int length, uint8_t *in_msg, int rlen, 586184299Snwhitehorn uint8_t *out_msg) 587184299Snwhitehorn{ 588184299Snwhitehorn struct pmu_softc *sc = cookie; 589184299Snwhitehorn int i, rcv_len = -1; 590184299Snwhitehorn uint8_t out_len, intreg; 591184299Snwhitehorn 592184299Snwhitehorn intreg = pmu_read_reg(sc, vIER); 593184299Snwhitehorn intreg &= 0x10; 594184299Snwhitehorn pmu_write_reg(sc, vIER, intreg); 595184299Snwhitehorn 596184299Snwhitehorn /* wait idle */ 597184299Snwhitehorn do {} while (pmu_intr_state(sc)); 598184299Snwhitehorn 599184299Snwhitehorn /* send command */ 600184299Snwhitehorn pmu_send_byte(sc, cmd); 601184299Snwhitehorn 602184299Snwhitehorn /* send length if necessary */ 603184299Snwhitehorn if (pm_send_cmd_type[cmd] < 0) { 604184299Snwhitehorn pmu_send_byte(sc, length); 605184299Snwhitehorn } 606184299Snwhitehorn 607184299Snwhitehorn for (i = 0; i < length; i++) { 608184299Snwhitehorn pmu_send_byte(sc, in_msg[i]); 609184299Snwhitehorn } 610184299Snwhitehorn 611184299Snwhitehorn /* see if there's data to read */ 612184299Snwhitehorn rcv_len = pm_receive_cmd_type[cmd]; 613184299Snwhitehorn if (rcv_len == 0) 614184299Snwhitehorn goto done; 615184299Snwhitehorn 616184299Snwhitehorn /* read command */ 617184299Snwhitehorn if (rcv_len == 1) { 618184299Snwhitehorn pmu_read_byte(sc, out_msg); 619184299Snwhitehorn goto done; 620184299Snwhitehorn } else 621184299Snwhitehorn out_msg[0] = cmd; 622184299Snwhitehorn if (rcv_len < 0) { 623184299Snwhitehorn pmu_read_byte(sc, &out_len); 624184299Snwhitehorn rcv_len = out_len + 1; 625184299Snwhitehorn } 626184299Snwhitehorn for (i = 1; i < min(rcv_len, rlen); i++) 627184299Snwhitehorn pmu_read_byte(sc, &out_msg[i]); 628184299Snwhitehorn 629184299Snwhitehorndone: 630184299Snwhitehorn pmu_write_reg(sc, vIER, (intreg == 0) ? 0 : 0x90); 631184299Snwhitehorn 632184299Snwhitehorn return rcv_len; 633184299Snwhitehorn} 634184299Snwhitehorn 635184299Snwhitehorn 636194027Savgstatic u_int 637184299Snwhitehornpmu_poll(device_t dev) 638184299Snwhitehorn{ 639184299Snwhitehorn pmu_intr(dev); 640194027Savg return (0); 641184299Snwhitehorn} 642184299Snwhitehorn 643184299Snwhitehornstatic void 644184299Snwhitehornpmu_in(struct pmu_softc *sc) 645184299Snwhitehorn{ 646184299Snwhitehorn uint8_t reg; 647184299Snwhitehorn 648184299Snwhitehorn reg = pmu_read_reg(sc, vACR); 649184299Snwhitehorn reg &= ~vSR_OUT; 650184299Snwhitehorn reg |= 0x0c; 651184299Snwhitehorn pmu_write_reg(sc, vACR, reg); 652184299Snwhitehorn} 653184299Snwhitehorn 654184299Snwhitehornstatic void 655184299Snwhitehornpmu_out(struct pmu_softc *sc) 656184299Snwhitehorn{ 657184299Snwhitehorn uint8_t reg; 658184299Snwhitehorn 659184299Snwhitehorn reg = pmu_read_reg(sc, vACR); 660184299Snwhitehorn reg |= vSR_OUT; 661184299Snwhitehorn reg |= 0x0c; 662184299Snwhitehorn pmu_write_reg(sc, vACR, reg); 663184299Snwhitehorn} 664184299Snwhitehorn 665184299Snwhitehornstatic void 666184299Snwhitehornpmu_ack_off(struct pmu_softc *sc) 667184299Snwhitehorn{ 668184299Snwhitehorn uint8_t reg; 669184299Snwhitehorn 670184299Snwhitehorn reg = pmu_read_reg(sc, vBufB); 671184299Snwhitehorn reg &= ~vPB4; 672184299Snwhitehorn pmu_write_reg(sc, vBufB, reg); 673184299Snwhitehorn} 674184299Snwhitehorn 675184299Snwhitehornstatic void 676184299Snwhitehornpmu_ack_on(struct pmu_softc *sc) 677184299Snwhitehorn{ 678184299Snwhitehorn uint8_t reg; 679184299Snwhitehorn 680184299Snwhitehorn reg = pmu_read_reg(sc, vBufB); 681184299Snwhitehorn reg |= vPB4; 682184299Snwhitehorn pmu_write_reg(sc, vBufB, reg); 683184299Snwhitehorn} 684184299Snwhitehorn 685184299Snwhitehornstatic void 686184299Snwhitehornpmu_intr(void *arg) 687184299Snwhitehorn{ 688184299Snwhitehorn device_t dev; 689184299Snwhitehorn struct pmu_softc *sc; 690184299Snwhitehorn 691184299Snwhitehorn unsigned int len; 692184299Snwhitehorn uint8_t resp[16]; 693184299Snwhitehorn uint8_t junk[16]; 694184299Snwhitehorn 695184299Snwhitehorn dev = (device_t)arg; 696184299Snwhitehorn sc = device_get_softc(dev); 697184299Snwhitehorn 698184299Snwhitehorn mtx_lock(&sc->sc_mutex); 699184299Snwhitehorn 700184299Snwhitehorn pmu_write_reg(sc, vIFR, 0x90); /* Clear 'em */ 701184299Snwhitehorn len = pmu_send(sc, PMU_INT_ACK, 0, NULL, 16, resp); 702184299Snwhitehorn 703184299Snwhitehorn mtx_unlock(&sc->sc_mutex); 704184299Snwhitehorn 705184299Snwhitehorn if ((len < 1) || (resp[1] == 0)) { 706184299Snwhitehorn return; 707184299Snwhitehorn } 708184299Snwhitehorn 709184299Snwhitehorn if (resp[1] & PMU_INT_ADB) { 710184299Snwhitehorn /* 711184299Snwhitehorn * the PMU will turn off autopolling after each command that 712184299Snwhitehorn * it did not issue, so we assume any but TALK R0 is ours and 713184299Snwhitehorn * re-enable autopoll here whenever we receive an ACK for a 714184299Snwhitehorn * non TR0 command. 715184299Snwhitehorn */ 716184299Snwhitehorn mtx_lock(&sc->sc_mutex); 717184299Snwhitehorn 718184299Snwhitehorn if ((resp[2] & 0x0f) != (ADB_COMMAND_TALK << 2)) { 719184299Snwhitehorn if (sc->sc_autopoll) { 720184299Snwhitehorn uint8_t cmd[] = {0, PMU_SET_POLL_MASK, 721184299Snwhitehorn (sc->sc_autopoll >> 8) & 0xff, 722184299Snwhitehorn sc->sc_autopoll & 0xff}; 723184299Snwhitehorn 724184299Snwhitehorn pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, junk); 725184299Snwhitehorn } 726184299Snwhitehorn } 727184299Snwhitehorn 728184299Snwhitehorn mtx_unlock(&sc->sc_mutex); 729184299Snwhitehorn 730184299Snwhitehorn adb_receive_raw_packet(sc->adb_bus,resp[1],resp[2], 731184299Snwhitehorn len - 3,&resp[3]); 732184299Snwhitehorn } 733228270Sjhibbits if (resp[1] & PMU_INT_ENVIRONMENT) { 734228277Sjhibbits /* if the lid was just closed, notify devd. */ 735228270Sjhibbits if ((resp[2] & PMU_ENV_LID_CLOSED) && (!sc->lid_closed)) { 736228270Sjhibbits sc->lid_closed = 1; 737274733Sjhibbits devctl_notify("PMU", "lid", "close", NULL); 738228270Sjhibbits } 739228270Sjhibbits else if (!(resp[2] & PMU_ENV_LID_CLOSED) && (sc->lid_closed)) { 740228277Sjhibbits /* if the lid was just opened, notify devd. */ 741228270Sjhibbits sc->lid_closed = 0; 742274733Sjhibbits devctl_notify("PMU", "lid", "open", NULL); 743228270Sjhibbits } 744274733Sjhibbits if (resp[2] & PMU_ENV_POWER) 745274733Sjhibbits devctl_notify("PMU", "Button", "pressed", NULL); 746228270Sjhibbits } 747184299Snwhitehorn} 748184299Snwhitehorn 749184299Snwhitehornstatic u_int 750184299Snwhitehornpmu_adb_send(device_t dev, u_char command_byte, int len, u_char *data, 751184299Snwhitehorn u_char poll) 752184299Snwhitehorn{ 753184299Snwhitehorn struct pmu_softc *sc = device_get_softc(dev); 754184299Snwhitehorn int i,replen; 755184299Snwhitehorn uint8_t packet[16], resp[16]; 756184299Snwhitehorn 757184299Snwhitehorn /* construct an ADB command packet and send it */ 758184299Snwhitehorn 759184299Snwhitehorn packet[0] = command_byte; 760184299Snwhitehorn 761184299Snwhitehorn packet[1] = 0; 762184299Snwhitehorn packet[2] = len; 763184299Snwhitehorn for (i = 0; i < len; i++) 764184299Snwhitehorn packet[i + 3] = data[i]; 765184299Snwhitehorn 766184299Snwhitehorn mtx_lock(&sc->sc_mutex); 767184299Snwhitehorn replen = pmu_send(sc, PMU_ADB_CMD, len + 3, packet, 16, resp); 768184299Snwhitehorn mtx_unlock(&sc->sc_mutex); 769184299Snwhitehorn 770184299Snwhitehorn if (poll) 771184299Snwhitehorn pmu_poll(dev); 772184299Snwhitehorn 773184299Snwhitehorn return 0; 774184299Snwhitehorn} 775184299Snwhitehorn 776184299Snwhitehornstatic u_int 777184299Snwhitehornpmu_adb_autopoll(device_t dev, uint16_t mask) 778184299Snwhitehorn{ 779184299Snwhitehorn struct pmu_softc *sc = device_get_softc(dev); 780184299Snwhitehorn 781184299Snwhitehorn /* magical incantation to re-enable autopolling */ 782184299Snwhitehorn uint8_t cmd[] = {0, PMU_SET_POLL_MASK, (mask >> 8) & 0xff, mask & 0xff}; 783184299Snwhitehorn uint8_t resp[16]; 784184299Snwhitehorn 785184299Snwhitehorn mtx_lock(&sc->sc_mutex); 786184299Snwhitehorn 787184299Snwhitehorn if (sc->sc_autopoll == mask) { 788184299Snwhitehorn mtx_unlock(&sc->sc_mutex); 789184299Snwhitehorn return 0; 790184299Snwhitehorn } 791184299Snwhitehorn 792184299Snwhitehorn sc->sc_autopoll = mask & 0xffff; 793184299Snwhitehorn 794184299Snwhitehorn if (mask) 795184299Snwhitehorn pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, resp); 796184299Snwhitehorn else 797184299Snwhitehorn pmu_send(sc, PMU_ADB_POLL_OFF, 0, NULL, 16, resp); 798184299Snwhitehorn 799184299Snwhitehorn mtx_unlock(&sc->sc_mutex); 800184299Snwhitehorn 801184299Snwhitehorn return 0; 802184299Snwhitehorn} 803185727Snwhitehorn 804185782Snwhitehornstatic void 805212054Snwhitehornpmu_shutdown(void *xsc, int howto) 806212054Snwhitehorn{ 807212054Snwhitehorn struct pmu_softc *sc = xsc; 808212054Snwhitehorn uint8_t cmd[] = {'M', 'A', 'T', 'T'}; 809212054Snwhitehorn 810212054Snwhitehorn if (howto & RB_HALT) 811212054Snwhitehorn pmu_send(sc, PMU_POWER_OFF, 4, cmd, 0, NULL); 812212054Snwhitehorn else 813212054Snwhitehorn pmu_send(sc, PMU_RESET_CPU, 0, NULL, 0, NULL); 814212054Snwhitehorn 815212054Snwhitehorn for (;;); 816212054Snwhitehorn} 817212054Snwhitehorn 818212054Snwhitehornstatic void 819185782Snwhitehornpmu_set_sleepled(void *xsc, int onoff) 820185782Snwhitehorn{ 821185782Snwhitehorn struct pmu_softc *sc = xsc; 822185782Snwhitehorn uint8_t cmd[] = {4, 0, 0}; 823185782Snwhitehorn 824185782Snwhitehorn cmd[2] = onoff; 825185782Snwhitehorn 826185782Snwhitehorn mtx_lock(&sc->sc_mutex); 827185782Snwhitehorn pmu_send(sc, PMU_SET_SLEEPLED, 3, cmd, 0, NULL); 828185782Snwhitehorn mtx_unlock(&sc->sc_mutex); 829185782Snwhitehorn} 830185782Snwhitehorn 831185727Snwhitehornstatic int 832185727Snwhitehornpmu_server_mode(SYSCTL_HANDLER_ARGS) 833185727Snwhitehorn{ 834185727Snwhitehorn struct pmu_softc *sc = arg1; 835185727Snwhitehorn 836185727Snwhitehorn u_int server_mode = 0; 837185727Snwhitehorn uint8_t getcmd[] = {PMU_PWR_GET_POWERUP_EVENTS}; 838185727Snwhitehorn uint8_t setcmd[] = {0, 0, PMU_PWR_WAKEUP_AC_INSERT}; 839185727Snwhitehorn uint8_t resp[3]; 840185727Snwhitehorn int error, len; 841185727Snwhitehorn 842185727Snwhitehorn mtx_lock(&sc->sc_mutex); 843185727Snwhitehorn len = pmu_send(sc, PMU_POWER_EVENTS, 1, getcmd, 3, resp); 844185727Snwhitehorn mtx_unlock(&sc->sc_mutex); 845185727Snwhitehorn 846185727Snwhitehorn if (len == 3) 847185727Snwhitehorn server_mode = (resp[2] & PMU_PWR_WAKEUP_AC_INSERT) ? 1 : 0; 848185727Snwhitehorn 849185727Snwhitehorn error = sysctl_handle_int(oidp, &server_mode, 0, req); 850185727Snwhitehorn 851185727Snwhitehorn if (len != 3) 852185727Snwhitehorn return (EINVAL); 853185727Snwhitehorn 854185727Snwhitehorn if (error || !req->newptr) 855185727Snwhitehorn return (error); 856185727Snwhitehorn 857185727Snwhitehorn if (server_mode == 1) 858185727Snwhitehorn setcmd[0] = PMU_PWR_SET_POWERUP_EVENTS; 859185727Snwhitehorn else if (server_mode == 0) 860185727Snwhitehorn setcmd[0] = PMU_PWR_CLR_POWERUP_EVENTS; 861185727Snwhitehorn else 862185727Snwhitehorn return (EINVAL); 863185727Snwhitehorn 864185727Snwhitehorn setcmd[1] = resp[1]; 865185727Snwhitehorn 866185727Snwhitehorn mtx_lock(&sc->sc_mutex); 867185727Snwhitehorn pmu_send(sc, PMU_POWER_EVENTS, 3, setcmd, 2, resp); 868185727Snwhitehorn mtx_unlock(&sc->sc_mutex); 869185727Snwhitehorn 870185727Snwhitehorn return (0); 871185727Snwhitehorn} 872185727Snwhitehorn 873185754Snwhitehornstatic int 874185754Snwhitehornpmu_query_battery(struct pmu_softc *sc, int batt, struct pmu_battstate *info) 875185754Snwhitehorn{ 876185754Snwhitehorn uint8_t reg; 877185754Snwhitehorn uint8_t resp[16]; 878185754Snwhitehorn int len; 879185754Snwhitehorn 880185754Snwhitehorn reg = batt + 1; 881185754Snwhitehorn 882185754Snwhitehorn mtx_lock(&sc->sc_mutex); 883185754Snwhitehorn len = pmu_send(sc, PMU_SMART_BATTERY_STATE, 1, ®, 16, resp); 884185754Snwhitehorn mtx_unlock(&sc->sc_mutex); 885185754Snwhitehorn 886185754Snwhitehorn if (len < 3) 887185754Snwhitehorn return (-1); 888185754Snwhitehorn 889185754Snwhitehorn /* All PMU battery info replies share a common header: 890185754Snwhitehorn * Byte 1 Payload Format 891185754Snwhitehorn * Byte 2 Battery Flags 892185754Snwhitehorn */ 893185754Snwhitehorn 894185754Snwhitehorn info->state = resp[2]; 895185754Snwhitehorn 896185754Snwhitehorn switch (resp[1]) { 897185754Snwhitehorn case 3: 898185754Snwhitehorn case 4: 899185754Snwhitehorn /* 900185754Snwhitehorn * Formats 3 and 4 appear to be the same: 901185754Snwhitehorn * Byte 3 Charge 902185754Snwhitehorn * Byte 4 Max Charge 903185754Snwhitehorn * Byte 5 Current 904185754Snwhitehorn * Byte 6 Voltage 905185754Snwhitehorn */ 906185754Snwhitehorn 907185754Snwhitehorn info->charge = resp[3]; 908185754Snwhitehorn info->maxcharge = resp[4]; 909185754Snwhitehorn /* Current can be positive or negative */ 910185754Snwhitehorn info->current = (int8_t)resp[5]; 911185754Snwhitehorn info->voltage = resp[6]; 912185754Snwhitehorn break; 913185754Snwhitehorn case 5: 914185754Snwhitehorn /* 915185754Snwhitehorn * Formats 5 is a wider version of formats 3 and 4 916185754Snwhitehorn * Byte 3-4 Charge 917185754Snwhitehorn * Byte 5-6 Max Charge 918185754Snwhitehorn * Byte 7-8 Current 919185754Snwhitehorn * Byte 9-10 Voltage 920185754Snwhitehorn */ 921185754Snwhitehorn 922185754Snwhitehorn info->charge = (resp[3] << 8) | resp[4]; 923185754Snwhitehorn info->maxcharge = (resp[5] << 8) | resp[6]; 924185754Snwhitehorn /* Current can be positive or negative */ 925185754Snwhitehorn info->current = (int16_t)((resp[7] << 8) | resp[8]); 926185754Snwhitehorn info->voltage = (resp[9] << 8) | resp[10]; 927185754Snwhitehorn break; 928185754Snwhitehorn default: 929185754Snwhitehorn device_printf(sc->sc_dev, "Unknown battery info format (%d)!\n", 930185754Snwhitehorn resp[1]); 931185754Snwhitehorn return (-1); 932185754Snwhitehorn } 933185754Snwhitehorn 934185754Snwhitehorn return (0); 935185754Snwhitehorn} 936185754Snwhitehorn 937273009Sjhibbitsstatic void 938273009Sjhibbitspmu_battery_notify(struct pmu_battstate *batt, struct pmu_battstate *old) 939273009Sjhibbits{ 940273009Sjhibbits char notify_buf[16]; 941273113Sjhibbits int new_acline, old_acline; 942273009Sjhibbits 943273113Sjhibbits new_acline = (batt->state & PMU_PWR_AC_PRESENT) ? 1 : 0; 944273113Sjhibbits old_acline = (old->state & PMU_PWR_AC_PRESENT) ? 1 : 0; 945273113Sjhibbits 946273113Sjhibbits if (new_acline != old_acline) { 947273009Sjhibbits snprintf(notify_buf, sizeof(notify_buf), 948273113Sjhibbits "notify=0x%02x", new_acline); 949273009Sjhibbits devctl_notify("PMU", "POWER", "ACLINE", notify_buf); 950273009Sjhibbits } 951273009Sjhibbits} 952273009Sjhibbits 953273009Sjhibbitsstatic void 954273009Sjhibbitspmu_battquery_proc() 955273009Sjhibbits{ 956273009Sjhibbits struct pmu_softc *sc; 957273009Sjhibbits struct pmu_battstate batt; 958273009Sjhibbits struct pmu_battstate cur_batt; 959273009Sjhibbits int error; 960273009Sjhibbits 961273009Sjhibbits sc = device_get_softc(pmu); 962273009Sjhibbits 963273113Sjhibbits bzero(&cur_batt, sizeof(cur_batt)); 964273009Sjhibbits while (1) { 965273113Sjhibbits kproc_suspend_check(curproc); 966273009Sjhibbits error = pmu_query_battery(sc, 0, &batt); 967273009Sjhibbits pmu_battery_notify(&batt, &cur_batt); 968273009Sjhibbits cur_batt = batt; 969273009Sjhibbits pause("pmu_batt", hz); 970273009Sjhibbits } 971273009Sjhibbits} 972273009Sjhibbits 973185754Snwhitehornstatic int 974273113Sjhibbitspmu_battmon(SYSCTL_HANDLER_ARGS) 975273113Sjhibbits{ 976273113Sjhibbits struct pmu_softc *sc; 977273113Sjhibbits int error, result; 978273113Sjhibbits 979273113Sjhibbits sc = arg1; 980273113Sjhibbits result = pmu_battmon_enabled; 981273113Sjhibbits 982273113Sjhibbits error = sysctl_handle_int(oidp, &result, 0, req); 983273113Sjhibbits 984273113Sjhibbits if (error || !req->newptr) 985273113Sjhibbits return (error); 986273113Sjhibbits 987273113Sjhibbits if (!result && pmu_battmon_enabled) 988273113Sjhibbits error = kproc_suspend(pmubattproc, hz); 989273113Sjhibbits else if (result && pmu_battmon_enabled == 0) 990273113Sjhibbits error = kproc_resume(pmubattproc); 991273113Sjhibbits pmu_battmon_enabled = (result != 0); 992273113Sjhibbits 993273113Sjhibbits return (error); 994273113Sjhibbits} 995273113Sjhibbits 996273113Sjhibbitsstatic int 997193159Snwhitehornpmu_acline_state(SYSCTL_HANDLER_ARGS) 998193159Snwhitehorn{ 999193159Snwhitehorn struct pmu_softc *sc; 1000193159Snwhitehorn struct pmu_battstate batt; 1001193159Snwhitehorn int error, result; 1002193159Snwhitehorn 1003193159Snwhitehorn sc = arg1; 1004193159Snwhitehorn 1005193159Snwhitehorn /* The PMU treats the AC line status as a property of the battery */ 1006193159Snwhitehorn error = pmu_query_battery(sc, 0, &batt); 1007193159Snwhitehorn 1008193159Snwhitehorn if (error != 0) 1009193159Snwhitehorn return (error); 1010193159Snwhitehorn 1011193159Snwhitehorn result = (batt.state & PMU_PWR_AC_PRESENT) ? 1 : 0; 1012193159Snwhitehorn error = sysctl_handle_int(oidp, &result, 0, req); 1013193159Snwhitehorn 1014193159Snwhitehorn return (error); 1015193159Snwhitehorn} 1016193159Snwhitehorn 1017193159Snwhitehornstatic int 1018185754Snwhitehornpmu_battquery_sysctl(SYSCTL_HANDLER_ARGS) 1019185754Snwhitehorn{ 1020185754Snwhitehorn struct pmu_softc *sc; 1021185754Snwhitehorn struct pmu_battstate batt; 1022185754Snwhitehorn int error, result; 1023185754Snwhitehorn 1024185754Snwhitehorn sc = arg1; 1025185754Snwhitehorn 1026185754Snwhitehorn error = pmu_query_battery(sc, arg2 & 0x00ff, &batt); 1027185754Snwhitehorn 1028185754Snwhitehorn if (error != 0) 1029185754Snwhitehorn return (error); 1030185754Snwhitehorn 1031185754Snwhitehorn switch (arg2 & 0xff00) { 1032185754Snwhitehorn case PMU_BATSYSCTL_PRESENT: 1033185754Snwhitehorn result = (batt.state & PMU_PWR_BATT_PRESENT) ? 1 : 0; 1034185754Snwhitehorn break; 1035185754Snwhitehorn case PMU_BATSYSCTL_CHARGING: 1036185754Snwhitehorn result = (batt.state & PMU_PWR_BATT_CHARGING) ? 1 : 0; 1037185754Snwhitehorn break; 1038185754Snwhitehorn case PMU_BATSYSCTL_CHARGE: 1039185754Snwhitehorn result = batt.charge; 1040185754Snwhitehorn break; 1041185754Snwhitehorn case PMU_BATSYSCTL_MAXCHARGE: 1042185754Snwhitehorn result = batt.maxcharge; 1043185754Snwhitehorn break; 1044185754Snwhitehorn case PMU_BATSYSCTL_CURRENT: 1045185754Snwhitehorn result = batt.current; 1046185754Snwhitehorn break; 1047185754Snwhitehorn case PMU_BATSYSCTL_VOLTAGE: 1048185754Snwhitehorn result = batt.voltage; 1049185754Snwhitehorn break; 1050185754Snwhitehorn case PMU_BATSYSCTL_TIME: 1051185754Snwhitehorn /* Time remaining until full charge/discharge, in minutes */ 1052185754Snwhitehorn 1053185754Snwhitehorn if (batt.current >= 0) 1054185754Snwhitehorn result = (batt.maxcharge - batt.charge) /* mAh */ * 60 1055185754Snwhitehorn / batt.current /* mA */; 1056185754Snwhitehorn else 1057185754Snwhitehorn result = (batt.charge /* mAh */ * 60) 1058185754Snwhitehorn / (-batt.current /* mA */); 1059185754Snwhitehorn break; 1060185754Snwhitehorn case PMU_BATSYSCTL_LIFE: 1061185754Snwhitehorn /* Battery charge fraction, in percent */ 1062185754Snwhitehorn result = (batt.charge * 100) / batt.maxcharge; 1063185754Snwhitehorn break; 1064185754Snwhitehorn default: 1065185754Snwhitehorn /* This should never happen */ 1066185754Snwhitehorn result = -1; 1067297793Spfg } 1068185754Snwhitehorn 1069185754Snwhitehorn error = sysctl_handle_int(oidp, &result, 0, req); 1070185754Snwhitehorn 1071185754Snwhitehorn return (error); 1072185754Snwhitehorn} 1073185754Snwhitehorn 1074205506Snwhitehorn#define DIFF19041970 2082844800 1075205506Snwhitehorn 1076205506Snwhitehornstatic int 1077205506Snwhitehornpmu_gettime(device_t dev, struct timespec *ts) 1078205506Snwhitehorn{ 1079205506Snwhitehorn struct pmu_softc *sc = device_get_softc(dev); 1080205506Snwhitehorn uint8_t resp[16]; 1081205506Snwhitehorn uint32_t sec; 1082205506Snwhitehorn 1083205506Snwhitehorn mtx_lock(&sc->sc_mutex); 1084205506Snwhitehorn pmu_send(sc, PMU_READ_RTC, 0, NULL, 16, resp); 1085205506Snwhitehorn mtx_unlock(&sc->sc_mutex); 1086205506Snwhitehorn 1087205506Snwhitehorn memcpy(&sec, &resp[1], 4); 1088205506Snwhitehorn ts->tv_sec = sec - DIFF19041970; 1089205506Snwhitehorn ts->tv_nsec = 0; 1090205506Snwhitehorn 1091205506Snwhitehorn return (0); 1092205506Snwhitehorn} 1093205506Snwhitehorn 1094205506Snwhitehornstatic int 1095205506Snwhitehornpmu_settime(device_t dev, struct timespec *ts) 1096205506Snwhitehorn{ 1097205506Snwhitehorn struct pmu_softc *sc = device_get_softc(dev); 1098205506Snwhitehorn uint32_t sec; 1099205506Snwhitehorn 1100205506Snwhitehorn sec = ts->tv_sec + DIFF19041970; 1101205506Snwhitehorn 1102205506Snwhitehorn mtx_lock(&sc->sc_mutex); 1103205506Snwhitehorn pmu_send(sc, PMU_SET_RTC, sizeof(sec), (uint8_t *)&sec, 0, NULL); 1104205506Snwhitehorn mtx_unlock(&sc->sc_mutex); 1105205506Snwhitehorn 1106205506Snwhitehorn return (0); 1107205506Snwhitehorn} 1108205506Snwhitehorn 1109259284Sjhibbitsint 1110259284Sjhibbitspmu_set_speed(int low_speed) 1111259284Sjhibbits{ 1112259284Sjhibbits struct pmu_softc *sc; 1113259284Sjhibbits uint8_t sleepcmd[] = {'W', 'O', 'O', 'F', 0}; 1114259284Sjhibbits uint8_t resp[16]; 1115259284Sjhibbits 1116259284Sjhibbits sc = device_get_softc(pmu); 1117259284Sjhibbits pmu_write_reg(sc, vIER, 0x10); 1118259284Sjhibbits spinlock_enter(); 1119259284Sjhibbits mtdec(0x7fffffff); 1120259284Sjhibbits mb(); 1121259284Sjhibbits mtdec(0x7fffffff); 1122259284Sjhibbits 1123259284Sjhibbits sleepcmd[4] = low_speed; 1124259284Sjhibbits pmu_send(sc, PMU_CPU_SPEED, 5, sleepcmd, 16, resp); 1125259284Sjhibbits unin_chip_sleep(NULL, 1); 1126261309Sjhibbits platform_sleep(); 1127259284Sjhibbits unin_chip_wake(NULL); 1128259284Sjhibbits 1129259284Sjhibbits mtdec(1); /* Force a decrementer exception */ 1130259284Sjhibbits spinlock_exit(); 1131259284Sjhibbits pmu_write_reg(sc, vIER, 0x90); 1132259284Sjhibbits 1133259284Sjhibbits return (0); 1134259284Sjhibbits} 1135