1239281Sgonzo/*-
2239281Sgonzo * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
3239281Sgonzo * All rights reserved.
4239281Sgonzo *
5239281Sgonzo * Redistribution and use in source and binary forms, with or without
6239281Sgonzo * modification, are permitted provided that the following conditions
7239281Sgonzo * are met:
8239281Sgonzo * 1. Redistributions of source code must retain the above copyright
9239281Sgonzo *    notice, this list of conditions and the following disclaimer.
10239281Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
11239281Sgonzo *    notice, this list of conditions and the following disclaimer in the
12239281Sgonzo *    documentation and/or other materials provided with the distribution.
13239281Sgonzo *
14239281Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15239281Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16239281Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17239281Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18239281Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19239281Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20239281Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21239281Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22239281Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23239281Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24239281Sgonzo * SUCH DAMAGE.
25239281Sgonzo */
26239281Sgonzo
27239281Sgonzo#include <sys/cdefs.h>
28239281Sgonzo__FBSDID("$FreeBSD$");
29239281Sgonzo/*
30239281Sgonzo* TPS65217 PMIC companion chip for AM335x SoC sitting on I2C bus
31239281Sgonzo*/
32239281Sgonzo#include <sys/param.h>
33239281Sgonzo#include <sys/systm.h>
34278079Sloos#include <sys/eventhandler.h>
35239281Sgonzo#include <sys/kernel.h>
36239281Sgonzo#include <sys/module.h>
37239281Sgonzo#include <sys/clock.h>
38239281Sgonzo#include <sys/time.h>
39239281Sgonzo#include <sys/bus.h>
40278079Sloos#include <sys/reboot.h>
41239281Sgonzo#include <sys/resource.h>
42239281Sgonzo#include <sys/rman.h>
43239281Sgonzo
44239281Sgonzo#include <dev/iicbus/iicbus.h>
45239281Sgonzo#include <dev/iicbus/iiconf.h>
46239281Sgonzo
47239281Sgonzo#include <dev/ofw/openfirm.h>
48239281Sgonzo#include <dev/ofw/ofw_bus.h>
49239281Sgonzo#include <dev/ofw/ofw_bus_subr.h>
50239281Sgonzo
51278079Sloos#include <arm/ti/am335x/am335x_rtcvar.h>
52278079Sloos
53239281Sgonzo#include "iicbus_if.h"
54239281Sgonzo
55239281Sgonzo#define TPS65217A		0x7
56239281Sgonzo#define TPS65217B		0xF
57253025Sgonzo#define TPS65217C		0xE
58253025Sgonzo#define TPS65217D		0x6
59239281Sgonzo
60239281Sgonzo/* TPS65217 Reisters */
61239281Sgonzo#define TPS65217_CHIPID_REG	0x00
62239281Sgonzo#define TPS65217_STATUS_REG	0x0A
63278079Sloos#define	TPS65217_STATUS_OFF		(1U << 7)
64278079Sloos#define	TPS65217_STATUS_ACPWR		(1U << 3)
65278079Sloos#define	TPS65217_STATUS_USBPWR		(1U << 2)
66278079Sloos#define	TPS65217_STATUS_BT		(1U << 0)
67239281Sgonzo
68239281Sgonzo#define MAX_IIC_DATA_SIZE	2
69239281Sgonzo
70239281Sgonzo
71239281Sgonzostruct am335x_pmic_softc {
72239281Sgonzo	device_t		sc_dev;
73239281Sgonzo	uint32_t		sc_addr;
74239281Sgonzo	struct intr_config_hook enum_hook;
75239281Sgonzo};
76239281Sgonzo
77278079Sloosstatic void am335x_pmic_shutdown(void *, int);
78278079Sloos
79239281Sgonzostatic int
80239281Sgonzoam335x_pmic_read(device_t dev, uint8_t addr, uint8_t *data, uint8_t size)
81239281Sgonzo{
82239281Sgonzo	struct am335x_pmic_softc *sc = device_get_softc(dev);
83239281Sgonzo	struct iic_msg msg[] = {
84239281Sgonzo		{ sc->sc_addr, IIC_M_WR, 1, &addr },
85239281Sgonzo		{ sc->sc_addr, IIC_M_RD, size, data },
86239281Sgonzo	};
87239281Sgonzo	return (iicbus_transfer(dev, msg, 2));
88239281Sgonzo}
89239281Sgonzo
90239281Sgonzostatic int
91239281Sgonzoam335x_pmic_write(device_t dev, uint8_t address, uint8_t *data, uint8_t size)
92239281Sgonzo{
93239281Sgonzo	uint8_t buffer[MAX_IIC_DATA_SIZE + 1];
94239281Sgonzo	struct am335x_pmic_softc *sc = device_get_softc(dev);
95239281Sgonzo	struct iic_msg msg[] = {
96239281Sgonzo		{ sc->sc_addr, IIC_M_WR, size + 1, buffer },
97239281Sgonzo	};
98239281Sgonzo
99239281Sgonzo	if (size > MAX_IIC_DATA_SIZE)
100239281Sgonzo		return (ENOMEM);
101239281Sgonzo
102239281Sgonzo	buffer[0] = address;
103239281Sgonzo	memcpy(buffer + 1, data, size);
104239281Sgonzo
105239281Sgonzo	return (iicbus_transfer(dev, msg, 1));
106239281Sgonzo}
107239281Sgonzo
108239281Sgonzostatic int
109239281Sgonzoam335x_pmic_probe(device_t dev)
110239281Sgonzo{
111239281Sgonzo	struct am335x_pmic_softc *sc;
112239281Sgonzo
113239281Sgonzo	if (!ofw_bus_is_compatible(dev, "ti,am335x-pmic"))
114239281Sgonzo		return (ENXIO);
115239281Sgonzo
116239281Sgonzo	sc = device_get_softc(dev);
117239281Sgonzo	sc->sc_dev = dev;
118239281Sgonzo	sc->sc_addr = iicbus_get_addr(dev);
119239281Sgonzo
120239281Sgonzo	device_set_desc(dev, "TI TPS65217 Power Management IC");
121239281Sgonzo
122239281Sgonzo	return (0);
123239281Sgonzo}
124239281Sgonzo
125239281Sgonzostatic void
126239281Sgonzoam335x_pmic_start(void *xdev)
127239281Sgonzo{
128239281Sgonzo	struct am335x_pmic_softc *sc;
129239281Sgonzo	device_t dev = (device_t)xdev;
130239281Sgonzo	uint8_t reg;
131239281Sgonzo	char name[20];
132239281Sgonzo	char pwr[4][11] = {"Unknown", "USB", "AC", "USB and AC"};
133239281Sgonzo
134239281Sgonzo	sc = device_get_softc(dev);
135239281Sgonzo
136239281Sgonzo	am335x_pmic_read(dev, TPS65217_CHIPID_REG, &reg, 1);
137239281Sgonzo	switch (reg>>4) {
138239281Sgonzo		case TPS65217A:
139239281Sgonzo			sprintf(name, "TPS65217A ver 1.%u", reg & 0xF);
140239281Sgonzo			break;
141239281Sgonzo		case TPS65217B:
142239281Sgonzo			sprintf(name, "TPS65217B ver 1.%u", reg & 0xF);
143239281Sgonzo			break;
144253025Sgonzo		case TPS65217C:
145253025Sgonzo			sprintf(name, "TPS65217C ver 1.%u", reg & 0xF);
146253025Sgonzo			break;
147253025Sgonzo		case TPS65217D:
148253025Sgonzo			sprintf(name, "TPS65217D ver 1.%u", reg & 0xF);
149253025Sgonzo			break;
150239281Sgonzo		default:
151239281Sgonzo			sprintf(name, "Unknown PMIC");
152239281Sgonzo	}
153239281Sgonzo
154239281Sgonzo	am335x_pmic_read(dev, TPS65217_STATUS_REG, &reg, 1);
155239281Sgonzo	device_printf(dev, "%s powered by %s\n", name, pwr[(reg>>2)&0x03]);
156239281Sgonzo
157278079Sloos	EVENTHANDLER_REGISTER(shutdown_final, am335x_pmic_shutdown, dev,
158278079Sloos	    SHUTDOWN_PRI_LAST);
159278079Sloos
160239281Sgonzo	config_intrhook_disestablish(&sc->enum_hook);
161239281Sgonzo}
162239281Sgonzo
163239281Sgonzostatic int
164239281Sgonzoam335x_pmic_attach(device_t dev)
165239281Sgonzo{
166239281Sgonzo	struct am335x_pmic_softc *sc;
167239281Sgonzo
168239281Sgonzo	sc = device_get_softc(dev);
169239281Sgonzo
170239281Sgonzo	sc->enum_hook.ich_func = am335x_pmic_start;
171239281Sgonzo	sc->enum_hook.ich_arg = dev;
172239281Sgonzo
173239281Sgonzo	if (config_intrhook_establish(&sc->enum_hook) != 0)
174239281Sgonzo		return (ENOMEM);
175239281Sgonzo
176239281Sgonzo	return (0);
177239281Sgonzo}
178239281Sgonzo
179278079Sloosstatic void
180278079Sloosam335x_pmic_shutdown(void *xdev, int howto)
181278079Sloos{
182278079Sloos	device_t dev;
183278079Sloos	uint8_t reg;
184278079Sloos
185278079Sloos	if (!(howto & RB_POWEROFF))
186278079Sloos		return;
187278079Sloos	dev = (device_t)xdev;
188278079Sloos	/* Set the OFF bit on status register to start the shutdown sequence. */
189278079Sloos	reg = TPS65217_STATUS_OFF;
190278079Sloos	am335x_pmic_write(dev, TPS65217_STATUS_REG, &reg, 1);
191278079Sloos	/* Toggle pmic_pwr_enable to shutdown the PMIC. */
192278079Sloos	am335x_rtc_pmic_pwr_toggle();
193278079Sloos}
194278079Sloos
195239281Sgonzostatic device_method_t am335x_pmic_methods[] = {
196239281Sgonzo	DEVMETHOD(device_probe,		am335x_pmic_probe),
197239281Sgonzo	DEVMETHOD(device_attach,	am335x_pmic_attach),
198239281Sgonzo	{0, 0},
199239281Sgonzo};
200239281Sgonzo
201239281Sgonzostatic driver_t am335x_pmic_driver = {
202239281Sgonzo	"am335x_pmic",
203239281Sgonzo	am335x_pmic_methods,
204239281Sgonzo	sizeof(struct am335x_pmic_softc),
205239281Sgonzo};
206239281Sgonzo
207239281Sgonzostatic devclass_t am335x_pmic_devclass;
208239281Sgonzo
209239281SgonzoDRIVER_MODULE(am335x_pmic, iicbus, am335x_pmic_driver, am335x_pmic_devclass, 0, 0);
210239281SgonzoMODULE_VERSION(am335x_pmic, 1);
211239281SgonzoMODULE_DEPEND(am335x_pmic, iicbus, 1, 1, 1);
212