axp209.c revision 295634
1295634Sandrew/*-
2295634Sandrew * Copyright (c) 2015 Emmanuel Vadot <manu@bidouilliste.com>
3295634Sandrew * All rights reserved.
4295634Sandrew *
5295634Sandrew * Redistribution and use in source and binary forms, with or without
6295634Sandrew * modification, are permitted provided that the following conditions
7295634Sandrew * are met:
8295634Sandrew * 1. Redistributions of source code must retain the above copyright
9295634Sandrew *    notice, this list of conditions and the following disclaimer.
10295634Sandrew * 2. Redistributions in binary form must reproduce the above copyright
11295634Sandrew *    notice, this list of conditions and the following disclaimer in the
12295634Sandrew *    documentation and/or other materials provided with the distribution.
13295634Sandrew *
14295634Sandrew * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15295634Sandrew * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16295634Sandrew * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17295634Sandrew * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18295634Sandrew * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19295634Sandrew * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20295634Sandrew * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21295634Sandrew * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22295634Sandrew * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23295634Sandrew * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24295634Sandrew * SUCH DAMAGE.
25295634Sandrew */
26295634Sandrew
27295634Sandrew#include <sys/cdefs.h>
28295634Sandrew__FBSDID("$FreeBSD: head/sys/arm/allwinner/axp209.c 295634 2016-02-15 19:31:23Z andrew $");
29295634Sandrew/*
30295634Sandrew* X-Power AXP209 PMU for Allwinner SoCs
31295634Sandrew*/
32295634Sandrew#include <sys/param.h>
33295634Sandrew#include <sys/systm.h>
34295634Sandrew#include <sys/eventhandler.h>
35295634Sandrew#include <sys/kernel.h>
36295634Sandrew#include <sys/module.h>
37295634Sandrew#include <sys/clock.h>
38295634Sandrew#include <sys/time.h>
39295634Sandrew#include <sys/bus.h>
40295634Sandrew#include <sys/proc.h>
41295634Sandrew#include <sys/reboot.h>
42295634Sandrew#include <sys/resource.h>
43295634Sandrew#include <sys/rman.h>
44295634Sandrew
45295634Sandrew#include <dev/iicbus/iicbus.h>
46295634Sandrew#include <dev/iicbus/iiconf.h>
47295634Sandrew
48295634Sandrew#include <dev/ofw/openfirm.h>
49295634Sandrew#include <dev/ofw/ofw_bus.h>
50295634Sandrew#include <dev/ofw/ofw_bus_subr.h>
51295634Sandrew
52295634Sandrew#include "iicbus_if.h"
53295634Sandrew
54295634Sandrew/* Power State Register */
55295634Sandrew#define	AXP209_PSR		0x00
56295634Sandrew#define	AXP209_PSR_ACIN		0x80
57295634Sandrew#define	AXP209_PSR_VBUS		0x20
58295634Sandrew
59295634Sandrew/* Shutdown and battery control */
60295634Sandrew#define	AXP209_SHUTBAT		0x32
61295634Sandrew#define	AXP209_SHUTBAT_SHUTDOWN	0x80
62295634Sandrew
63295634Sandrewstruct axp209_softc {
64295634Sandrew	uint32_t		addr;
65295634Sandrew	struct intr_config_hook enum_hook;
66295634Sandrew};
67295634Sandrew
68295634Sandrewstatic int
69295634Sandrewaxp209_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size)
70295634Sandrew{
71295634Sandrew	struct axp209_softc *sc = device_get_softc(dev);
72295634Sandrew	struct iic_msg msg[2];
73295634Sandrew
74295634Sandrew	msg[0].slave = sc->addr;
75295634Sandrew	msg[0].flags = IIC_M_WR;
76295634Sandrew	msg[0].len = 1;
77295634Sandrew	msg[0].buf = &reg;
78295634Sandrew
79295634Sandrew	msg[1].slave = sc->addr;
80295634Sandrew	msg[1].flags = IIC_M_RD;
81295634Sandrew	msg[1].len = size;
82295634Sandrew	msg[1].buf = data;
83295634Sandrew
84295634Sandrew	return (iicbus_transfer(dev, msg, 2));
85295634Sandrew}
86295634Sandrew
87295634Sandrewstatic int
88295634Sandrewaxp209_write(device_t dev, uint8_t reg, uint8_t data)
89295634Sandrew{
90295634Sandrew	uint8_t buffer[2];
91295634Sandrew	struct axp209_softc *sc = device_get_softc(dev);
92295634Sandrew	struct iic_msg msg;
93295634Sandrew
94295634Sandrew	buffer[0] = reg;
95295634Sandrew	buffer[1] = data;
96295634Sandrew
97295634Sandrew	msg.slave = sc->addr;
98295634Sandrew	msg.flags = IIC_M_WR;
99295634Sandrew	msg.len = 2;
100295634Sandrew	msg.buf = buffer;
101295634Sandrew
102295634Sandrew	return (iicbus_transfer(dev, &msg, 1));
103295634Sandrew}
104295634Sandrew
105295634Sandrewstatic void
106295634Sandrewaxp209_shutdown(void *devp, int howto)
107295634Sandrew{
108295634Sandrew	device_t dev;
109295634Sandrew
110295634Sandrew	if (!(howto & RB_POWEROFF))
111295634Sandrew		return;
112295634Sandrew	dev = (device_t)devp;
113295634Sandrew
114295634Sandrew	if (bootverbose)
115295634Sandrew		device_printf(dev, "Shutdown AXP209\n");
116295634Sandrew
117295634Sandrew	axp209_write(dev, AXP209_SHUTBAT, AXP209_SHUTBAT_SHUTDOWN);
118295634Sandrew}
119295634Sandrew
120295634Sandrewstatic int
121295634Sandrewaxp209_probe(device_t dev)
122295634Sandrew{
123295634Sandrew
124295634Sandrew	if (!ofw_bus_status_okay(dev))
125295634Sandrew		return (ENXIO);
126295634Sandrew
127295634Sandrew	if (!ofw_bus_is_compatible(dev, "x-powers,axp209"))
128295634Sandrew		return (ENXIO);
129295634Sandrew
130295634Sandrew	device_set_desc(dev, "X-Power AXP209 Power Management Unit");
131295634Sandrew
132295634Sandrew	return (BUS_PROBE_DEFAULT);
133295634Sandrew}
134295634Sandrew
135295634Sandrewstatic int
136295634Sandrewaxp209_attach(device_t dev)
137295634Sandrew{
138295634Sandrew	struct axp209_softc *sc;
139295634Sandrew	uint8_t data;
140295634Sandrew	uint8_t pwr_src;
141295634Sandrew	char pwr_name[4][11] = {"Battery", "AC", "USB", "AC and USB"};
142295634Sandrew
143295634Sandrew	sc = device_get_softc(dev);
144295634Sandrew
145295634Sandrew	sc->addr = iicbus_get_addr(dev);
146295634Sandrew
147295634Sandrew	/*
148295634Sandrew	 * Read the Power State register
149295634Sandrew	 * bit 7 is AC presence, bit 5 is VBUS presence.
150295634Sandrew	 * If none are set then we are running from battery (obviously).
151295634Sandrew	 */
152295634Sandrew	axp209_read(dev, AXP209_PSR, &data, 1);
153295634Sandrew	pwr_src = ((data & AXP209_PSR_ACIN) >> 7) |
154295634Sandrew		  ((data & AXP209_PSR_VBUS) >> 4);
155295634Sandrew
156295634Sandrew	if (bootverbose)
157295634Sandrew		device_printf(dev, "AXP209 Powered by %s\n",
158295634Sandrew		    pwr_name[pwr_src]);
159295634Sandrew
160295634Sandrew	EVENTHANDLER_REGISTER(shutdown_final, axp209_shutdown, dev,
161295634Sandrew	    SHUTDOWN_PRI_LAST);
162295634Sandrew
163295634Sandrew	return (0);
164295634Sandrew}
165295634Sandrew
166295634Sandrewstatic device_method_t axp209_methods[] = {
167295634Sandrew	DEVMETHOD(device_probe,		axp209_probe),
168295634Sandrew	DEVMETHOD(device_attach,	axp209_attach),
169295634Sandrew	{0, 0},
170295634Sandrew};
171295634Sandrew
172295634Sandrewstatic driver_t axp209_driver = {
173295634Sandrew	"axp209_pmu",
174295634Sandrew	axp209_methods,
175295634Sandrew	sizeof(struct axp209_softc),
176295634Sandrew};
177295634Sandrew
178295634Sandrewstatic devclass_t axp209_devclass;
179295634Sandrew
180295634SandrewDRIVER_MODULE(axp209, iicbus, axp209_driver, axp209_devclass, 0, 0);
181295634SandrewMODULE_VERSION(axp209, 1);
182295634SandrewMODULE_DEPEND(axp209, iicbus, 1, 1, 1);
183