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>
34239281Sgonzo#include <sys/kernel.h>
35239281Sgonzo#include <sys/module.h>
36239281Sgonzo#include <sys/clock.h>
37239281Sgonzo#include <sys/time.h>
38239281Sgonzo#include <sys/bus.h>
39239281Sgonzo#include <sys/resource.h>
40239281Sgonzo#include <sys/rman.h>
41239281Sgonzo
42239281Sgonzo#include <dev/iicbus/iicbus.h>
43239281Sgonzo#include <dev/iicbus/iiconf.h>
44239281Sgonzo
45239281Sgonzo#include <dev/ofw/openfirm.h>
46239281Sgonzo#include <dev/ofw/ofw_bus.h>
47239281Sgonzo#include <dev/ofw/ofw_bus_subr.h>
48239281Sgonzo
49239281Sgonzo#include "iicbus_if.h"
50239281Sgonzo
51239281Sgonzo#define TPS65217A		0x7
52239281Sgonzo#define TPS65217B		0xF
53253025Sgonzo#define TPS65217C		0xE
54253025Sgonzo#define TPS65217D		0x6
55239281Sgonzo
56239281Sgonzo/* TPS65217 Reisters */
57239281Sgonzo#define TPS65217_CHIPID_REG	0x00
58239281Sgonzo#define TPS65217_STATUS_REG	0x0A
59239281Sgonzo
60239281Sgonzo#define MAX_IIC_DATA_SIZE	2
61239281Sgonzo
62239281Sgonzo
63239281Sgonzostruct am335x_pmic_softc {
64239281Sgonzo	device_t		sc_dev;
65239281Sgonzo	uint32_t		sc_addr;
66239281Sgonzo	struct intr_config_hook enum_hook;
67239281Sgonzo};
68239281Sgonzo
69239281Sgonzostatic int
70239281Sgonzoam335x_pmic_read(device_t dev, uint8_t addr, uint8_t *data, uint8_t size)
71239281Sgonzo{
72239281Sgonzo	struct am335x_pmic_softc *sc = device_get_softc(dev);
73239281Sgonzo	struct iic_msg msg[] = {
74239281Sgonzo		{ sc->sc_addr, IIC_M_WR, 1, &addr },
75239281Sgonzo		{ sc->sc_addr, IIC_M_RD, size, data },
76239281Sgonzo	};
77239281Sgonzo	return (iicbus_transfer(dev, msg, 2));
78239281Sgonzo}
79239281Sgonzo
80239281Sgonzo#ifdef notyet
81239281Sgonzostatic int
82239281Sgonzoam335x_pmic_write(device_t dev, uint8_t address, uint8_t *data, uint8_t size)
83239281Sgonzo{
84239281Sgonzo	uint8_t buffer[MAX_IIC_DATA_SIZE + 1];
85239281Sgonzo	struct am335x_pmic_softc *sc = device_get_softc(dev);
86239281Sgonzo	struct iic_msg msg[] = {
87239281Sgonzo		{ sc->sc_addr, IIC_M_WR, size + 1, buffer },
88239281Sgonzo	};
89239281Sgonzo
90239281Sgonzo	if (size > MAX_IIC_DATA_SIZE)
91239281Sgonzo		return (ENOMEM);
92239281Sgonzo
93239281Sgonzo	buffer[0] = address;
94239281Sgonzo	memcpy(buffer + 1, data, size);
95239281Sgonzo
96239281Sgonzo	return (iicbus_transfer(dev, msg, 1));
97239281Sgonzo}
98239281Sgonzo#endif
99239281Sgonzo
100239281Sgonzostatic int
101239281Sgonzoam335x_pmic_probe(device_t dev)
102239281Sgonzo{
103239281Sgonzo	struct am335x_pmic_softc *sc;
104239281Sgonzo
105239281Sgonzo	if (!ofw_bus_is_compatible(dev, "ti,am335x-pmic"))
106239281Sgonzo		return (ENXIO);
107239281Sgonzo
108239281Sgonzo	sc = device_get_softc(dev);
109239281Sgonzo	sc->sc_dev = dev;
110239281Sgonzo	sc->sc_addr = iicbus_get_addr(dev);
111239281Sgonzo
112239281Sgonzo	device_set_desc(dev, "TI TPS65217 Power Management IC");
113239281Sgonzo
114239281Sgonzo	return (0);
115239281Sgonzo}
116239281Sgonzo
117239281Sgonzostatic void
118239281Sgonzoam335x_pmic_start(void *xdev)
119239281Sgonzo{
120239281Sgonzo	struct am335x_pmic_softc *sc;
121239281Sgonzo	device_t dev = (device_t)xdev;
122239281Sgonzo	uint8_t reg;
123239281Sgonzo	char name[20];
124239281Sgonzo	char pwr[4][11] = {"Unknown", "USB", "AC", "USB and AC"};
125239281Sgonzo
126239281Sgonzo	sc = device_get_softc(dev);
127239281Sgonzo
128239281Sgonzo	am335x_pmic_read(dev, TPS65217_CHIPID_REG, &reg, 1);
129239281Sgonzo	switch (reg>>4) {
130239281Sgonzo		case TPS65217A:
131239281Sgonzo			sprintf(name, "TPS65217A ver 1.%u", reg & 0xF);
132239281Sgonzo			break;
133239281Sgonzo		case TPS65217B:
134239281Sgonzo			sprintf(name, "TPS65217B ver 1.%u", reg & 0xF);
135239281Sgonzo			break;
136253025Sgonzo		case TPS65217C:
137253025Sgonzo			sprintf(name, "TPS65217C ver 1.%u", reg & 0xF);
138253025Sgonzo			break;
139253025Sgonzo		case TPS65217D:
140253025Sgonzo			sprintf(name, "TPS65217D ver 1.%u", reg & 0xF);
141253025Sgonzo			break;
142239281Sgonzo		default:
143239281Sgonzo			sprintf(name, "Unknown PMIC");
144239281Sgonzo	}
145239281Sgonzo
146239281Sgonzo	am335x_pmic_read(dev, TPS65217_STATUS_REG, &reg, 1);
147239281Sgonzo	device_printf(dev, "%s powered by %s\n", name, pwr[(reg>>2)&0x03]);
148239281Sgonzo
149239281Sgonzo	config_intrhook_disestablish(&sc->enum_hook);
150239281Sgonzo}
151239281Sgonzo
152239281Sgonzostatic int
153239281Sgonzoam335x_pmic_attach(device_t dev)
154239281Sgonzo{
155239281Sgonzo	struct am335x_pmic_softc *sc;
156239281Sgonzo
157239281Sgonzo	sc = device_get_softc(dev);
158239281Sgonzo
159239281Sgonzo	sc->enum_hook.ich_func = am335x_pmic_start;
160239281Sgonzo	sc->enum_hook.ich_arg = dev;
161239281Sgonzo
162239281Sgonzo	if (config_intrhook_establish(&sc->enum_hook) != 0)
163239281Sgonzo		return (ENOMEM);
164239281Sgonzo
165239281Sgonzo	return (0);
166239281Sgonzo}
167239281Sgonzo
168239281Sgonzostatic device_method_t am335x_pmic_methods[] = {
169239281Sgonzo	DEVMETHOD(device_probe,		am335x_pmic_probe),
170239281Sgonzo	DEVMETHOD(device_attach,	am335x_pmic_attach),
171239281Sgonzo	{0, 0},
172239281Sgonzo};
173239281Sgonzo
174239281Sgonzostatic driver_t am335x_pmic_driver = {
175239281Sgonzo	"am335x_pmic",
176239281Sgonzo	am335x_pmic_methods,
177239281Sgonzo	sizeof(struct am335x_pmic_softc),
178239281Sgonzo};
179239281Sgonzo
180239281Sgonzostatic devclass_t am335x_pmic_devclass;
181239281Sgonzo
182239281SgonzoDRIVER_MODULE(am335x_pmic, iicbus, am335x_pmic_driver, am335x_pmic_devclass, 0, 0);
183239281SgonzoMODULE_VERSION(am335x_pmic, 1);
184239281SgonzoMODULE_DEPEND(am335x_pmic, iicbus, 1, 1, 1);
185