xref: /freebsd/sys/arm/allwinner/axp209.c (revision 2ff143af)
12ff143afSAndrew Turner /*-
22ff143afSAndrew Turner  * Copyright (c) 2015 Emmanuel Vadot <manu@bidouilliste.com>
32ff143afSAndrew Turner  * All rights reserved.
42ff143afSAndrew Turner  *
52ff143afSAndrew Turner  * Redistribution and use in source and binary forms, with or without
62ff143afSAndrew Turner  * modification, are permitted provided that the following conditions
72ff143afSAndrew Turner  * are met:
82ff143afSAndrew Turner  * 1. Redistributions of source code must retain the above copyright
92ff143afSAndrew Turner  *    notice, this list of conditions and the following disclaimer.
102ff143afSAndrew Turner  * 2. Redistributions in binary form must reproduce the above copyright
112ff143afSAndrew Turner  *    notice, this list of conditions and the following disclaimer in the
122ff143afSAndrew Turner  *    documentation and/or other materials provided with the distribution.
132ff143afSAndrew Turner  *
142ff143afSAndrew Turner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
152ff143afSAndrew Turner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
162ff143afSAndrew Turner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
172ff143afSAndrew Turner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
182ff143afSAndrew Turner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
192ff143afSAndrew Turner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
202ff143afSAndrew Turner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
212ff143afSAndrew Turner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
222ff143afSAndrew Turner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
232ff143afSAndrew Turner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
242ff143afSAndrew Turner  * SUCH DAMAGE.
252ff143afSAndrew Turner  */
262ff143afSAndrew Turner 
272ff143afSAndrew Turner #include <sys/cdefs.h>
282ff143afSAndrew Turner __FBSDID("$FreeBSD$");
292ff143afSAndrew Turner /*
302ff143afSAndrew Turner * X-Power AXP209 PMU for Allwinner SoCs
312ff143afSAndrew Turner */
322ff143afSAndrew Turner #include <sys/param.h>
332ff143afSAndrew Turner #include <sys/systm.h>
342ff143afSAndrew Turner #include <sys/eventhandler.h>
352ff143afSAndrew Turner #include <sys/kernel.h>
362ff143afSAndrew Turner #include <sys/module.h>
372ff143afSAndrew Turner #include <sys/clock.h>
382ff143afSAndrew Turner #include <sys/time.h>
392ff143afSAndrew Turner #include <sys/bus.h>
402ff143afSAndrew Turner #include <sys/proc.h>
412ff143afSAndrew Turner #include <sys/reboot.h>
422ff143afSAndrew Turner #include <sys/resource.h>
432ff143afSAndrew Turner #include <sys/rman.h>
442ff143afSAndrew Turner 
452ff143afSAndrew Turner #include <dev/iicbus/iicbus.h>
462ff143afSAndrew Turner #include <dev/iicbus/iiconf.h>
472ff143afSAndrew Turner 
482ff143afSAndrew Turner #include <dev/ofw/openfirm.h>
492ff143afSAndrew Turner #include <dev/ofw/ofw_bus.h>
502ff143afSAndrew Turner #include <dev/ofw/ofw_bus_subr.h>
512ff143afSAndrew Turner 
522ff143afSAndrew Turner #include "iicbus_if.h"
532ff143afSAndrew Turner 
542ff143afSAndrew Turner /* Power State Register */
552ff143afSAndrew Turner #define	AXP209_PSR		0x00
562ff143afSAndrew Turner #define	AXP209_PSR_ACIN		0x80
572ff143afSAndrew Turner #define	AXP209_PSR_VBUS		0x20
582ff143afSAndrew Turner 
592ff143afSAndrew Turner /* Shutdown and battery control */
602ff143afSAndrew Turner #define	AXP209_SHUTBAT		0x32
612ff143afSAndrew Turner #define	AXP209_SHUTBAT_SHUTDOWN	0x80
622ff143afSAndrew Turner 
632ff143afSAndrew Turner struct axp209_softc {
642ff143afSAndrew Turner 	uint32_t		addr;
652ff143afSAndrew Turner 	struct intr_config_hook enum_hook;
662ff143afSAndrew Turner };
672ff143afSAndrew Turner 
682ff143afSAndrew Turner static int
692ff143afSAndrew Turner axp209_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size)
702ff143afSAndrew Turner {
712ff143afSAndrew Turner 	struct axp209_softc *sc = device_get_softc(dev);
722ff143afSAndrew Turner 	struct iic_msg msg[2];
732ff143afSAndrew Turner 
742ff143afSAndrew Turner 	msg[0].slave = sc->addr;
752ff143afSAndrew Turner 	msg[0].flags = IIC_M_WR;
762ff143afSAndrew Turner 	msg[0].len = 1;
772ff143afSAndrew Turner 	msg[0].buf = &reg;
782ff143afSAndrew Turner 
792ff143afSAndrew Turner 	msg[1].slave = sc->addr;
802ff143afSAndrew Turner 	msg[1].flags = IIC_M_RD;
812ff143afSAndrew Turner 	msg[1].len = size;
822ff143afSAndrew Turner 	msg[1].buf = data;
832ff143afSAndrew Turner 
842ff143afSAndrew Turner 	return (iicbus_transfer(dev, msg, 2));
852ff143afSAndrew Turner }
862ff143afSAndrew Turner 
872ff143afSAndrew Turner static int
882ff143afSAndrew Turner axp209_write(device_t dev, uint8_t reg, uint8_t data)
892ff143afSAndrew Turner {
902ff143afSAndrew Turner 	uint8_t buffer[2];
912ff143afSAndrew Turner 	struct axp209_softc *sc = device_get_softc(dev);
922ff143afSAndrew Turner 	struct iic_msg msg;
932ff143afSAndrew Turner 
942ff143afSAndrew Turner 	buffer[0] = reg;
952ff143afSAndrew Turner 	buffer[1] = data;
962ff143afSAndrew Turner 
972ff143afSAndrew Turner 	msg.slave = sc->addr;
982ff143afSAndrew Turner 	msg.flags = IIC_M_WR;
992ff143afSAndrew Turner 	msg.len = 2;
1002ff143afSAndrew Turner 	msg.buf = buffer;
1012ff143afSAndrew Turner 
1022ff143afSAndrew Turner 	return (iicbus_transfer(dev, &msg, 1));
1032ff143afSAndrew Turner }
1042ff143afSAndrew Turner 
1052ff143afSAndrew Turner static void
1062ff143afSAndrew Turner axp209_shutdown(void *devp, int howto)
1072ff143afSAndrew Turner {
1082ff143afSAndrew Turner 	device_t dev;
1092ff143afSAndrew Turner 
1102ff143afSAndrew Turner 	if (!(howto & RB_POWEROFF))
1112ff143afSAndrew Turner 		return;
1122ff143afSAndrew Turner 	dev = (device_t)devp;
1132ff143afSAndrew Turner 
1142ff143afSAndrew Turner 	if (bootverbose)
1152ff143afSAndrew Turner 		device_printf(dev, "Shutdown AXP209\n");
1162ff143afSAndrew Turner 
1172ff143afSAndrew Turner 	axp209_write(dev, AXP209_SHUTBAT, AXP209_SHUTBAT_SHUTDOWN);
1182ff143afSAndrew Turner }
1192ff143afSAndrew Turner 
1202ff143afSAndrew Turner static int
1212ff143afSAndrew Turner axp209_probe(device_t dev)
1222ff143afSAndrew Turner {
1232ff143afSAndrew Turner 
1242ff143afSAndrew Turner 	if (!ofw_bus_status_okay(dev))
1252ff143afSAndrew Turner 		return (ENXIO);
1262ff143afSAndrew Turner 
1272ff143afSAndrew Turner 	if (!ofw_bus_is_compatible(dev, "x-powers,axp209"))
1282ff143afSAndrew Turner 		return (ENXIO);
1292ff143afSAndrew Turner 
1302ff143afSAndrew Turner 	device_set_desc(dev, "X-Power AXP209 Power Management Unit");
1312ff143afSAndrew Turner 
1322ff143afSAndrew Turner 	return (BUS_PROBE_DEFAULT);
1332ff143afSAndrew Turner }
1342ff143afSAndrew Turner 
1352ff143afSAndrew Turner static int
1362ff143afSAndrew Turner axp209_attach(device_t dev)
1372ff143afSAndrew Turner {
1382ff143afSAndrew Turner 	struct axp209_softc *sc;
1392ff143afSAndrew Turner 	uint8_t data;
1402ff143afSAndrew Turner 	uint8_t pwr_src;
1412ff143afSAndrew Turner 	char pwr_name[4][11] = {"Battery", "AC", "USB", "AC and USB"};
1422ff143afSAndrew Turner 
1432ff143afSAndrew Turner 	sc = device_get_softc(dev);
1442ff143afSAndrew Turner 
1452ff143afSAndrew Turner 	sc->addr = iicbus_get_addr(dev);
1462ff143afSAndrew Turner 
1472ff143afSAndrew Turner 	/*
1482ff143afSAndrew Turner 	 * Read the Power State register
1492ff143afSAndrew Turner 	 * bit 7 is AC presence, bit 5 is VBUS presence.
1502ff143afSAndrew Turner 	 * If none are set then we are running from battery (obviously).
1512ff143afSAndrew Turner 	 */
1522ff143afSAndrew Turner 	axp209_read(dev, AXP209_PSR, &data, 1);
1532ff143afSAndrew Turner 	pwr_src = ((data & AXP209_PSR_ACIN) >> 7) |
1542ff143afSAndrew Turner 		  ((data & AXP209_PSR_VBUS) >> 4);
1552ff143afSAndrew Turner 
1562ff143afSAndrew Turner 	if (bootverbose)
1572ff143afSAndrew Turner 		device_printf(dev, "AXP209 Powered by %s\n",
1582ff143afSAndrew Turner 		    pwr_name[pwr_src]);
1592ff143afSAndrew Turner 
1602ff143afSAndrew Turner 	EVENTHANDLER_REGISTER(shutdown_final, axp209_shutdown, dev,
1612ff143afSAndrew Turner 	    SHUTDOWN_PRI_LAST);
1622ff143afSAndrew Turner 
1632ff143afSAndrew Turner 	return (0);
1642ff143afSAndrew Turner }
1652ff143afSAndrew Turner 
1662ff143afSAndrew Turner static device_method_t axp209_methods[] = {
1672ff143afSAndrew Turner 	DEVMETHOD(device_probe,		axp209_probe),
1682ff143afSAndrew Turner 	DEVMETHOD(device_attach,	axp209_attach),
1692ff143afSAndrew Turner 	{0, 0},
1702ff143afSAndrew Turner };
1712ff143afSAndrew Turner 
1722ff143afSAndrew Turner static driver_t axp209_driver = {
1732ff143afSAndrew Turner 	"axp209_pmu",
1742ff143afSAndrew Turner 	axp209_methods,
1752ff143afSAndrew Turner 	sizeof(struct axp209_softc),
1762ff143afSAndrew Turner };
1772ff143afSAndrew Turner 
1782ff143afSAndrew Turner static devclass_t axp209_devclass;
1792ff143afSAndrew Turner 
1802ff143afSAndrew Turner DRIVER_MODULE(axp209, iicbus, axp209_driver, axp209_devclass, 0, 0);
1812ff143afSAndrew Turner MODULE_VERSION(axp209, 1);
1822ff143afSAndrew Turner MODULE_DEPEND(axp209, iicbus, 1, 1, 1);
183