1*2ff143afSAndrew Turner /*- 2*2ff143afSAndrew Turner * Copyright (c) 2015 Emmanuel Vadot <manu@bidouilliste.com> 3*2ff143afSAndrew Turner * All rights reserved. 4*2ff143afSAndrew Turner * 5*2ff143afSAndrew Turner * Redistribution and use in source and binary forms, with or without 6*2ff143afSAndrew Turner * modification, are permitted provided that the following conditions 7*2ff143afSAndrew Turner * are met: 8*2ff143afSAndrew Turner * 1. Redistributions of source code must retain the above copyright 9*2ff143afSAndrew Turner * notice, this list of conditions and the following disclaimer. 10*2ff143afSAndrew Turner * 2. Redistributions in binary form must reproduce the above copyright 11*2ff143afSAndrew Turner * notice, this list of conditions and the following disclaimer in the 12*2ff143afSAndrew Turner * documentation and/or other materials provided with the distribution. 13*2ff143afSAndrew Turner * 14*2ff143afSAndrew Turner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15*2ff143afSAndrew Turner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16*2ff143afSAndrew Turner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17*2ff143afSAndrew Turner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18*2ff143afSAndrew Turner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19*2ff143afSAndrew Turner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20*2ff143afSAndrew Turner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21*2ff143afSAndrew Turner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22*2ff143afSAndrew Turner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*2ff143afSAndrew Turner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*2ff143afSAndrew Turner * SUCH DAMAGE. 25*2ff143afSAndrew Turner */ 26*2ff143afSAndrew Turner 27*2ff143afSAndrew Turner #include <sys/cdefs.h> 28*2ff143afSAndrew Turner __FBSDID("$FreeBSD$"); 29*2ff143afSAndrew Turner /* 30*2ff143afSAndrew Turner * X-Power AXP209 PMU for Allwinner SoCs 31*2ff143afSAndrew Turner */ 32*2ff143afSAndrew Turner #include <sys/param.h> 33*2ff143afSAndrew Turner #include <sys/systm.h> 34*2ff143afSAndrew Turner #include <sys/eventhandler.h> 35*2ff143afSAndrew Turner #include <sys/kernel.h> 36*2ff143afSAndrew Turner #include <sys/module.h> 37*2ff143afSAndrew Turner #include <sys/clock.h> 38*2ff143afSAndrew Turner #include <sys/time.h> 39*2ff143afSAndrew Turner #include <sys/bus.h> 40*2ff143afSAndrew Turner #include <sys/proc.h> 41*2ff143afSAndrew Turner #include <sys/reboot.h> 42*2ff143afSAndrew Turner #include <sys/resource.h> 43*2ff143afSAndrew Turner #include <sys/rman.h> 44*2ff143afSAndrew Turner 45*2ff143afSAndrew Turner #include <dev/iicbus/iicbus.h> 46*2ff143afSAndrew Turner #include <dev/iicbus/iiconf.h> 47*2ff143afSAndrew Turner 48*2ff143afSAndrew Turner #include <dev/ofw/openfirm.h> 49*2ff143afSAndrew Turner #include <dev/ofw/ofw_bus.h> 50*2ff143afSAndrew Turner #include <dev/ofw/ofw_bus_subr.h> 51*2ff143afSAndrew Turner 52*2ff143afSAndrew Turner #include "iicbus_if.h" 53*2ff143afSAndrew Turner 54*2ff143afSAndrew Turner /* Power State Register */ 55*2ff143afSAndrew Turner #define AXP209_PSR 0x00 56*2ff143afSAndrew Turner #define AXP209_PSR_ACIN 0x80 57*2ff143afSAndrew Turner #define AXP209_PSR_VBUS 0x20 58*2ff143afSAndrew Turner 59*2ff143afSAndrew Turner /* Shutdown and battery control */ 60*2ff143afSAndrew Turner #define AXP209_SHUTBAT 0x32 61*2ff143afSAndrew Turner #define AXP209_SHUTBAT_SHUTDOWN 0x80 62*2ff143afSAndrew Turner 63*2ff143afSAndrew Turner struct axp209_softc { 64*2ff143afSAndrew Turner uint32_t addr; 65*2ff143afSAndrew Turner struct intr_config_hook enum_hook; 66*2ff143afSAndrew Turner }; 67*2ff143afSAndrew Turner 68*2ff143afSAndrew Turner static int 69*2ff143afSAndrew Turner axp209_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) 70*2ff143afSAndrew Turner { 71*2ff143afSAndrew Turner struct axp209_softc *sc = device_get_softc(dev); 72*2ff143afSAndrew Turner struct iic_msg msg[2]; 73*2ff143afSAndrew Turner 74*2ff143afSAndrew Turner msg[0].slave = sc->addr; 75*2ff143afSAndrew Turner msg[0].flags = IIC_M_WR; 76*2ff143afSAndrew Turner msg[0].len = 1; 77*2ff143afSAndrew Turner msg[0].buf = ® 78*2ff143afSAndrew Turner 79*2ff143afSAndrew Turner msg[1].slave = sc->addr; 80*2ff143afSAndrew Turner msg[1].flags = IIC_M_RD; 81*2ff143afSAndrew Turner msg[1].len = size; 82*2ff143afSAndrew Turner msg[1].buf = data; 83*2ff143afSAndrew Turner 84*2ff143afSAndrew Turner return (iicbus_transfer(dev, msg, 2)); 85*2ff143afSAndrew Turner } 86*2ff143afSAndrew Turner 87*2ff143afSAndrew Turner static int 88*2ff143afSAndrew Turner axp209_write(device_t dev, uint8_t reg, uint8_t data) 89*2ff143afSAndrew Turner { 90*2ff143afSAndrew Turner uint8_t buffer[2]; 91*2ff143afSAndrew Turner struct axp209_softc *sc = device_get_softc(dev); 92*2ff143afSAndrew Turner struct iic_msg msg; 93*2ff143afSAndrew Turner 94*2ff143afSAndrew Turner buffer[0] = reg; 95*2ff143afSAndrew Turner buffer[1] = data; 96*2ff143afSAndrew Turner 97*2ff143afSAndrew Turner msg.slave = sc->addr; 98*2ff143afSAndrew Turner msg.flags = IIC_M_WR; 99*2ff143afSAndrew Turner msg.len = 2; 100*2ff143afSAndrew Turner msg.buf = buffer; 101*2ff143afSAndrew Turner 102*2ff143afSAndrew Turner return (iicbus_transfer(dev, &msg, 1)); 103*2ff143afSAndrew Turner } 104*2ff143afSAndrew Turner 105*2ff143afSAndrew Turner static void 106*2ff143afSAndrew Turner axp209_shutdown(void *devp, int howto) 107*2ff143afSAndrew Turner { 108*2ff143afSAndrew Turner device_t dev; 109*2ff143afSAndrew Turner 110*2ff143afSAndrew Turner if (!(howto & RB_POWEROFF)) 111*2ff143afSAndrew Turner return; 112*2ff143afSAndrew Turner dev = (device_t)devp; 113*2ff143afSAndrew Turner 114*2ff143afSAndrew Turner if (bootverbose) 115*2ff143afSAndrew Turner device_printf(dev, "Shutdown AXP209\n"); 116*2ff143afSAndrew Turner 117*2ff143afSAndrew Turner axp209_write(dev, AXP209_SHUTBAT, AXP209_SHUTBAT_SHUTDOWN); 118*2ff143afSAndrew Turner } 119*2ff143afSAndrew Turner 120*2ff143afSAndrew Turner static int 121*2ff143afSAndrew Turner axp209_probe(device_t dev) 122*2ff143afSAndrew Turner { 123*2ff143afSAndrew Turner 124*2ff143afSAndrew Turner if (!ofw_bus_status_okay(dev)) 125*2ff143afSAndrew Turner return (ENXIO); 126*2ff143afSAndrew Turner 127*2ff143afSAndrew Turner if (!ofw_bus_is_compatible(dev, "x-powers,axp209")) 128*2ff143afSAndrew Turner return (ENXIO); 129*2ff143afSAndrew Turner 130*2ff143afSAndrew Turner device_set_desc(dev, "X-Power AXP209 Power Management Unit"); 131*2ff143afSAndrew Turner 132*2ff143afSAndrew Turner return (BUS_PROBE_DEFAULT); 133*2ff143afSAndrew Turner } 134*2ff143afSAndrew Turner 135*2ff143afSAndrew Turner static int 136*2ff143afSAndrew Turner axp209_attach(device_t dev) 137*2ff143afSAndrew Turner { 138*2ff143afSAndrew Turner struct axp209_softc *sc; 139*2ff143afSAndrew Turner uint8_t data; 140*2ff143afSAndrew Turner uint8_t pwr_src; 141*2ff143afSAndrew Turner char pwr_name[4][11] = {"Battery", "AC", "USB", "AC and USB"}; 142*2ff143afSAndrew Turner 143*2ff143afSAndrew Turner sc = device_get_softc(dev); 144*2ff143afSAndrew Turner 145*2ff143afSAndrew Turner sc->addr = iicbus_get_addr(dev); 146*2ff143afSAndrew Turner 147*2ff143afSAndrew Turner /* 148*2ff143afSAndrew Turner * Read the Power State register 149*2ff143afSAndrew Turner * bit 7 is AC presence, bit 5 is VBUS presence. 150*2ff143afSAndrew Turner * If none are set then we are running from battery (obviously). 151*2ff143afSAndrew Turner */ 152*2ff143afSAndrew Turner axp209_read(dev, AXP209_PSR, &data, 1); 153*2ff143afSAndrew Turner pwr_src = ((data & AXP209_PSR_ACIN) >> 7) | 154*2ff143afSAndrew Turner ((data & AXP209_PSR_VBUS) >> 4); 155*2ff143afSAndrew Turner 156*2ff143afSAndrew Turner if (bootverbose) 157*2ff143afSAndrew Turner device_printf(dev, "AXP209 Powered by %s\n", 158*2ff143afSAndrew Turner pwr_name[pwr_src]); 159*2ff143afSAndrew Turner 160*2ff143afSAndrew Turner EVENTHANDLER_REGISTER(shutdown_final, axp209_shutdown, dev, 161*2ff143afSAndrew Turner SHUTDOWN_PRI_LAST); 162*2ff143afSAndrew Turner 163*2ff143afSAndrew Turner return (0); 164*2ff143afSAndrew Turner } 165*2ff143afSAndrew Turner 166*2ff143afSAndrew Turner static device_method_t axp209_methods[] = { 167*2ff143afSAndrew Turner DEVMETHOD(device_probe, axp209_probe), 168*2ff143afSAndrew Turner DEVMETHOD(device_attach, axp209_attach), 169*2ff143afSAndrew Turner {0, 0}, 170*2ff143afSAndrew Turner }; 171*2ff143afSAndrew Turner 172*2ff143afSAndrew Turner static driver_t axp209_driver = { 173*2ff143afSAndrew Turner "axp209_pmu", 174*2ff143afSAndrew Turner axp209_methods, 175*2ff143afSAndrew Turner sizeof(struct axp209_softc), 176*2ff143afSAndrew Turner }; 177*2ff143afSAndrew Turner 178*2ff143afSAndrew Turner static devclass_t axp209_devclass; 179*2ff143afSAndrew Turner 180*2ff143afSAndrew Turner DRIVER_MODULE(axp209, iicbus, axp209_driver, axp209_devclass, 0, 0); 181*2ff143afSAndrew Turner MODULE_VERSION(axp209, 1); 182*2ff143afSAndrew Turner MODULE_DEPEND(axp209, iicbus, 1, 1, 1); 183