1226048Sobrien/*-
268349Sobrien * Copyright (c) 2011
3226048Sobrien *	Ben Gray <ben.r.gray@gmail.com>.
468349Sobrien * All rights reserved.
568349Sobrien *
668349Sobrien * Redistribution and use in source and binary forms, with or without
768349Sobrien * modification, are permitted provided that the following conditions
868349Sobrien * are met:
968349Sobrien * 1. Redistributions of source code must retain the above copyright
1068349Sobrien *    notice, this list of conditions and the following disclaimer.
1168349Sobrien * 2. Redistributions in binary form must reproduce the above copyright
12133359Sobrien *    notice, this list of conditions and the following disclaimer in the
13186690Sobrien *    documentation and/or other materials provided with the distribution.
1468349Sobrien *
15186690Sobrien * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1668349Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17186690Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1868349Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19186690Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2068349Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21186690Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2268349Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23186690Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2468349Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25186690Sobrien * SUCH DAMAGE.
26133359Sobrien */
27133359Sobrien
28133359Sobrien#include <sys/cdefs.h>
29133359Sobrien__FBSDID("$FreeBSD: releng/10.2/sys/arm/ti/twl/twl.c 259329 2013-12-13 20:43:11Z ian $");
30133359Sobrien
31133359Sobrien/*
32133359Sobrien * Texas Instruments TWL4030/TWL5030/TWL60x0/TPS659x0 Power Management and
33133359Sobrien * Audio CODEC devices.
34133359Sobrien *
35133359Sobrien * This code is based on the Linux TWL multifunctional device driver, which is
36169942Sobrien * copyright (C) 2005-2006 Texas Instruments, Inc.
37186690Sobrien *
38133359Sobrien * These chips are typically used as support ICs for the OMAP range of embedded
39133359Sobrien * ARM processes/SOC from Texas Instruments.  They are typically used to control
40133359Sobrien * on board voltages, however some variants have other features like audio
41133359Sobrien * codecs, USB OTG transceivers, RTC, PWM, etc.
4268349Sobrien *
4368349Sobrien * This driver acts as a bus for more specific companion devices.
4468349Sobrien *
4568349Sobrien */
4668349Sobrien
4768349Sobrien#include <sys/param.h>
4868349Sobrien#include <sys/systm.h>
4968349Sobrien#include <sys/kernel.h>
50133359Sobrien#include <sys/lock.h>
51186690Sobrien#include <sys/module.h>
5268349Sobrien#include <sys/bus.h>
53186690Sobrien#include <sys/resource.h>
5468349Sobrien#include <sys/rman.h>
55186690Sobrien#include <sys/sysctl.h>
5668349Sobrien#include <sys/mutex.h>
57186690Sobrien#include <sys/malloc.h>
5868349Sobrien
59186690Sobrien#include <machine/bus.h>
6068349Sobrien#include <machine/cpu.h>
61186690Sobrien#include <machine/cpufunc.h>
6268349Sobrien#include <machine/resource.h>
63186690Sobrien#include <machine/intr.h>
64133359Sobrien
65133359Sobrien#include <dev/iicbus/iicbus.h>
66133359Sobrien#include <dev/iicbus/iiconf.h>
67133359Sobrien
68133359Sobrien#include <dev/ofw/openfirm.h>
69133359Sobrien#include <dev/ofw/ofw_bus.h>
70133359Sobrien#include <dev/ofw/ofw_bus_subr.h>
71133359Sobrien
72133359Sobrien#include "arm/ti/twl/twl.h"
73133359Sobrien
74169942Sobrien/* TWL device IDs */
75186690Sobrien#define TWL_DEVICE_UNKNOWN          0xffff
76133359Sobrien#define TWL_DEVICE_4030             0x4030
77133359Sobrien#define TWL_DEVICE_6025             0x6025
78133359Sobrien#define TWL_DEVICE_6030             0x6030
79133359Sobrien
8068349Sobrien/* Each TWL device typically has more than one I2C address */
8168349Sobrien#define TWL_MAX_SUBADDRS            4
8268349Sobrien
8368349Sobrien/* The maxium number of bytes that can be written in one call */
8468349Sobrien#define TWL_MAX_IIC_DATA_SIZE       63
8568349Sobrien
8668349Sobrien/* The TWL devices typically use 4 I2C address for the different internal
87186690Sobrien * register sets, plus one SmartReflex I2C address.
88133359Sobrien */
89133359Sobrien#define TWL_CHIP_ID0                0x48
90133359Sobrien#define TWL_CHIP_ID1                0x49
91133359Sobrien#define TWL_CHIP_ID2                0x4A
92133359Sobrien#define TWL_CHIP_ID3                0x4B
93133359Sobrien
9468349Sobrien#define TWL_SMARTREFLEX_CHIP_ID     0x12
95186690Sobrien
9668349Sobrien#define TWL_INVALID_CHIP_ID         0xff
97186690Sobrien
9868349Sobrienstruct twl_softc {
99186690Sobrien	device_t		sc_dev;
10068349Sobrien	struct mtx		sc_mtx;
10168349Sobrien	unsigned int	sc_type;
10268349Sobrien
10368349Sobrien	uint8_t			sc_subaddr_map[TWL_MAX_SUBADDRS];
10468349Sobrien
10568349Sobrien	struct intr_config_hook	sc_scan_hook;
10668349Sobrien
10768349Sobrien	device_t		sc_vreg;
10868349Sobrien	device_t		sc_clks;
10968349Sobrien};
11068349Sobrien
11168349Sobrien/**
11268349Sobrien *	Macros for driver mutex locking
11368349Sobrien */
11468349Sobrien#define TWL_LOCK(_sc)             mtx_lock(&(_sc)->sc_mtx)
11568349Sobrien#define	TWL_UNLOCK(_sc)           mtx_unlock(&(_sc)->sc_mtx)
11668349Sobrien#define TWL_LOCK_INIT(_sc) \
11768349Sobrien	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
11868349Sobrien	         "twl", MTX_DEF)
11968349Sobrien#define TWL_LOCK_DESTROY(_sc)     mtx_destroy(&_sc->sc_mtx);
120186690Sobrien#define TWL_ASSERT_LOCKED(_sc)    mtx_assert(&_sc->sc_mtx, MA_OWNED);
121226048Sobrien#define TWL_ASSERT_UNLOCKED(_sc)  mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
122186690Sobrien
123186690Sobrien
124186690Sobrien/**
125186690Sobrien *	twl_is_4030 - returns true if the device is TWL4030
126186690Sobrien *	twl_is_6025 - returns true if the device is TWL6025
12768349Sobrien *	twl_is_6030 - returns true if the device is TWL6030
12868349Sobrien *	@sc: device soft context
12968349Sobrien *
130133359Sobrien *	Returns a non-zero value if the device matches.
131133359Sobrien *
132133359Sobrien *	RETURNS:
13368349Sobrien *	Returns a non-zero value if the device matches, otherwise zero.
134133359Sobrien */
13568349Sobrienint
136133359Sobrientwl_is_4030(device_t dev)
137133359Sobrien{
138133359Sobrien	struct twl_softc *sc = device_get_softc(dev);
139133359Sobrien	return (sc->sc_type == TWL_DEVICE_4030);
14068349Sobrien}
141133359Sobrien
14268349Sobrienint
14368349Sobrientwl_is_6025(device_t dev)
14468349Sobrien{
14568349Sobrien	struct twl_softc *sc = device_get_softc(dev);
14668349Sobrien	return (sc->sc_type == TWL_DEVICE_6025);
14768349Sobrien}
14868349Sobrien
149186690Sobrienint
150186690Sobrientwl_is_6030(device_t dev)
151186690Sobrien{
152186690Sobrien	struct twl_softc *sc = device_get_softc(dev);
153186690Sobrien	return (sc->sc_type == TWL_DEVICE_6030);
154186690Sobrien}
155186690Sobrien
156186690Sobrien
157186690Sobrien/**
158186690Sobrien *	twl_read - read one or more registers from the TWL device
15968349Sobrien *	@sc: device soft context
16068349Sobrien *	@nsub: the sub-module to read from
16168349Sobrien *	@reg: the register offset within the module to read
162133359Sobrien *	@buf: buffer to store the bytes in
16368349Sobrien *	@cnt: the number of bytes to read
16468349Sobrien *
165186690Sobrien *	Reads one or more registers and stores the result in the suppled buffer.
166186690Sobrien *
167133359Sobrien *	RETURNS:
16868349Sobrien *	Zero on success or an error code on failure.
169133359Sobrien */
17068349Sobrienint
171186690Sobrientwl_read(device_t dev, uint8_t nsub, uint8_t reg, uint8_t *buf, uint16_t cnt)
172186690Sobrien{
173133359Sobrien	struct twl_softc *sc;
174133359Sobrien	struct iic_msg msg[2];
175159764Sobrien	uint8_t addr;
176186690Sobrien	int rc;
177186690Sobrien
178133359Sobrien	sc = device_get_softc(dev);
179186690Sobrien
180186690Sobrien	TWL_LOCK(sc);
18168349Sobrien	addr = sc->sc_subaddr_map[nsub];
182186690Sobrien	TWL_UNLOCK(sc);
183186690Sobrien
184133359Sobrien	if (addr == TWL_INVALID_CHIP_ID)
18568349Sobrien		return (EIO);
186186690Sobrien
187186690Sobrien
188133359Sobrien	/* Set the address to read from */
18968349Sobrien	msg[0].slave = addr;
190186690Sobrien	msg[0].flags = IIC_M_WR | IIC_M_NOSTOP;
191186690Sobrien	msg[0].len = 1;
192133359Sobrien	msg[0].buf = &reg;
193133359Sobrien	/* Read the data back */
194186690Sobrien	msg[1].slave = addr;
195186690Sobrien	msg[1].flags = IIC_M_RD;
196133359Sobrien	msg[1].len = cnt;
19768349Sobrien	msg[1].buf = buf;
198186690Sobrien
199186690Sobrien	rc = iicbus_transfer(dev, msg, 2);
200133359Sobrien	if (rc != 0) {
20168349Sobrien		device_printf(dev, "iicbus read failed (adr:0x%02x, reg:0x%02x)\n",
202186690Sobrien		              addr, reg);
203186690Sobrien		return (EIO);
204133359Sobrien	}
20568349Sobrien
206186690Sobrien	return (0);
207186690Sobrien}
208133359Sobrien
209133359Sobrien/**
210186690Sobrien *	twl_write - writes one or more registers to the TWL device
211186690Sobrien *	@sc: device soft context
212133359Sobrien *	@nsub: the sub-module to read from
213186690Sobrien *	@reg: the register offset within the module to read
214186690Sobrien *	@buf: data to write
215186690Sobrien *	@cnt: the number of bytes to write
216133359Sobrien *
21769216Sobrien *	Writes one or more registers.
21869216Sobrien *
21969216Sobrien *	RETURNS:
22068349Sobrien *	Zero on success or a negative error code on failure.
221186690Sobrien */
222186690Sobrienint
223133359Sobrientwl_write(device_t dev, uint8_t nsub, uint8_t reg, uint8_t *buf, uint16_t cnt)
22468349Sobrien{
225186690Sobrien	struct twl_softc *sc;
226186690Sobrien	struct iic_msg msg;
227133359Sobrien	uint8_t addr;
22868349Sobrien	uint8_t tmp_buf[TWL_MAX_IIC_DATA_SIZE + 1];
22968349Sobrien	int rc;
23068349Sobrien
23168349Sobrien	if (cnt > TWL_MAX_IIC_DATA_SIZE)
23268349Sobrien		return (ENOMEM);
23368349Sobrien
234133359Sobrien	/* Set the register address as the first byte */
23568349Sobrien	tmp_buf[0] = reg;
236133359Sobrien	memcpy(&tmp_buf[1], buf, cnt);
23768349Sobrien
23868349Sobrien	sc = device_get_softc(dev);
23968349Sobrien
24068349Sobrien	TWL_LOCK(sc);
24168349Sobrien	addr = sc->sc_subaddr_map[nsub];
242133359Sobrien	TWL_UNLOCK(sc);
243133359Sobrien
244133359Sobrien	if (addr == TWL_INVALID_CHIP_ID)
24569216Sobrien		return (EIO);
246133359Sobrien
247133359Sobrien
248133359Sobrien	/* Setup the transfer and execute it */
249133359Sobrien	msg.slave = addr;
250133359Sobrien	msg.flags = IIC_M_WR;
251133359Sobrien	msg.len = cnt + 1;
252133359Sobrien	msg.buf = tmp_buf;
253133359Sobrien
254133359Sobrien	rc = iicbus_transfer(dev, &msg, 1);
255226048Sobrien	if (rc != 0) {
256226048Sobrien		device_printf(sc->sc_dev, "iicbus write failed (adr:0x%02x, reg:0x%02x)\n",
257226048Sobrien		              addr, reg);
258226048Sobrien		return (EIO);
259226048Sobrien	}
260226048Sobrien
26169216Sobrien	return (0);
262226048Sobrien}
263226048Sobrien
264226048Sobrien/**
265226048Sobrien *	twl_test_present - checks if a device with given address is present
26669216Sobrien *	@sc: device soft context
26769216Sobrien *	@addr: the address of the device to scan for
26869216Sobrien *
26969216Sobrien *	Sends just the address byte and checks for an ACK. If no ACK then device
27069216Sobrien *	is assumed to not be present.
27169216Sobrien *
27269216Sobrien *	RETURNS:
273133359Sobrien *	EIO if device is not present, otherwise 0 is returned.
274133359Sobrien */
275133359Sobrienstatic int
276133359Sobrientwl_test_present(struct twl_softc *sc, uint8_t addr)
277133359Sobrien{
278133359Sobrien	struct iic_msg msg;
279133359Sobrien	uint8_t tmp;
280133359Sobrien
281133359Sobrien	/* Set the address to read from */
282133359Sobrien	msg.slave = addr;
283186690Sobrien	msg.flags = IIC_M_RD;
284133359Sobrien	msg.len = 1;
285133359Sobrien	msg.buf = &tmp;
286226048Sobrien
287226048Sobrien	if (iicbus_transfer(sc->sc_dev, &msg, 1) != 0)
288133359Sobrien		return (EIO);
289133359Sobrien
290133359Sobrien	return (0);
291133359Sobrien}
29269216Sobrien
29380588Sobrien/**
29480588Sobrien *	twl_scan - scans the i2c bus for sub modules
295103373Sobrien *	@dev: the twl device
296191736Sobrien *
297191736Sobrien *	TWL devices don't just have one i2c slave address, rather they have up to
298191736Sobrien *	5 other addresses, each is for separate modules within the device. This
299191736Sobrien *	function scans the bus for 4 possible sub-devices and stores the info
300191736Sobrien *	internally.
301191736Sobrien *
302191736Sobrien */
303191736Sobrienstatic void
304103373Sobrientwl_scan(void *dev)
305103373Sobrien{
306103373Sobrien	struct twl_softc *sc;
307103373Sobrien	unsigned i;
308103373Sobrien	uint8_t devs[TWL_MAX_SUBADDRS];
309103373Sobrien	uint8_t base = TWL_CHIP_ID0;
310103373Sobrien
311103373Sobrien	sc = device_get_softc((device_t)dev);
312103373Sobrien
313103373Sobrien	memset(devs, TWL_INVALID_CHIP_ID, TWL_MAX_SUBADDRS);
314103373Sobrien
315103373Sobrien	/* Try each of the addresses (0x48, 0x49, 0x4a & 0x4b) to determine which
316226048Sobrien	 * sub modules we have.
317226048Sobrien	 */
318226048Sobrien	for (i = 0; i < TWL_MAX_SUBADDRS; i++) {
319226048Sobrien		if (twl_test_present(sc, (base + i)) == 0) {
320226048Sobrien			devs[i] = (base + i);
321226048Sobrien			device_printf(sc->sc_dev, "Found (sub)device at 0x%02x\n", (base + i));
322226048Sobrien		}
323226048Sobrien	}
324226048Sobrien
325159764Sobrien	TWL_LOCK(sc);
326103373Sobrien	memcpy(sc->sc_subaddr_map, devs, TWL_MAX_SUBADDRS);
327186690Sobrien	TWL_UNLOCK(sc);
328103373Sobrien
329110949Sobrien	/* Finished with the interrupt hook */
330110949Sobrien	config_intrhook_disestablish(&sc->sc_scan_hook);
331103373Sobrien}
332103373Sobrien
333103373Sobrien/**
334103373Sobrien *	twl_probe -
335110949Sobrien *	@dev: the twl device
336110949Sobrien *
337110949Sobrien *	Scans the FDT for a match for the device, possible compatible device
338110949Sobrien *	strings are; "ti,twl6030", "ti,twl6025", "ti,twl4030".
339110949Sobrien *
340110949Sobrien *	The FDT compat string also determines the type of device (it is currently
341110949Sobrien *	not possible to dynamically determine the device type).
342110949Sobrien *
343110949Sobrien */
344110949Sobrienstatic int
345110949Sobrientwl_probe(device_t dev)
346110949Sobrien{
347110949Sobrien	phandle_t node;
348110949Sobrien	const char *compat;
349110949Sobrien	int len, l;
350110949Sobrien	struct twl_softc *sc;
351110949Sobrien
352110949Sobrien	if ((compat = ofw_bus_get_compat(dev)) == NULL)
353110949Sobrien		return (ENXIO);
354110949Sobrien
355110949Sobrien	if ((node = ofw_bus_get_node(dev)) == 0)
356110949Sobrien		return (ENXIO);
357110949Sobrien
358110949Sobrien	/* Get total 'compatible' prop len */
359110949Sobrien	if ((len = OF_getproplen(node, "compatible")) <= 0)
360110949Sobrien		return (ENXIO);
361110949Sobrien
362110949Sobrien	sc = device_get_softc(dev);
363110949Sobrien	sc->sc_dev = dev;
364110949Sobrien	sc->sc_type = TWL_DEVICE_UNKNOWN;
365110949Sobrien
366110949Sobrien	while (len > 0) {
367110949Sobrien		if (strncasecmp(compat, "ti,twl6030", 10) == 0)
368110949Sobrien			sc->sc_type = TWL_DEVICE_6030;
369110949Sobrien		else if (strncasecmp(compat, "ti,twl6025", 10) == 0)
370110949Sobrien			sc->sc_type = TWL_DEVICE_6025;
371110949Sobrien		else if (strncasecmp(compat, "ti,twl4030", 10) == 0)
372110949Sobrien			sc->sc_type = TWL_DEVICE_4030;
373110949Sobrien
374110949Sobrien		if (sc->sc_type != TWL_DEVICE_UNKNOWN)
375110949Sobrien			break;
376110949Sobrien
377110949Sobrien		/* Slide to the next sub-string. */
378110949Sobrien		l = strlen(compat) + 1;
379110949Sobrien		compat += l;
380110949Sobrien		len -= l;
381110949Sobrien	}
382110949Sobrien
383110949Sobrien	switch (sc->sc_type) {
384110949Sobrien	case TWL_DEVICE_4030:
385110949Sobrien		device_set_desc(dev, "TI TWL4030/TPS659x0 Companion IC");
386110949Sobrien		break;
387110949Sobrien	case TWL_DEVICE_6025:
388110949Sobrien		device_set_desc(dev, "TI TWL6025 Companion IC");
389110949Sobrien		break;
390110949Sobrien	case TWL_DEVICE_6030:
391110949Sobrien		device_set_desc(dev, "TI TWL6030 Companion IC");
392110949Sobrien		break;
393110949Sobrien	case TWL_DEVICE_UNKNOWN:
394110949Sobrien	default:
395110949Sobrien		return (ENXIO);
396110949Sobrien	}
397110949Sobrien
398110949Sobrien	return (0);
399110949Sobrien}
400110949Sobrien
401110949Sobrienstatic int
402133359Sobrientwl_attach(device_t dev)
403133359Sobrien{
404133359Sobrien	struct twl_softc *sc;
405133359Sobrien
406133359Sobrien	sc = device_get_softc(dev);
407133359Sobrien	sc->sc_dev = dev;
408133359Sobrien
409133359Sobrien	TWL_LOCK_INIT(sc);
410133359Sobrien
411133359Sobrien	/* We have to wait until interrupts are enabled. I2C read and write
412133359Sobrien	 * only works if the interrupts are available.
413133359Sobrien	 */
414133359Sobrien	sc->sc_scan_hook.ich_func = twl_scan;
415133359Sobrien	sc->sc_scan_hook.ich_arg = dev;
416133359Sobrien
417133359Sobrien	if (config_intrhook_establish(&sc->sc_scan_hook) != 0)
418133359Sobrien		return (ENOMEM);
419133359Sobrien
420133359Sobrien	/* FIXME: should be in DTS file */
421133359Sobrien	if ((sc->sc_vreg = device_add_child(dev, "twl_vreg", -1)) == NULL)
422133359Sobrien		device_printf(dev, "could not allocate twl_vreg instance\n");
423133359Sobrien	if ((sc->sc_clks = device_add_child(dev, "twl_clks", -1)) == NULL)
424133359Sobrien		device_printf(dev, "could not allocate twl_clks instance\n");
425110949Sobrien
426110949Sobrien	return (bus_generic_attach(dev));
427110949Sobrien}
428110949Sobrien
429110949Sobrienstatic int
430110949Sobrientwl_detach(device_t dev)
431110949Sobrien{
432110949Sobrien	struct twl_softc *sc;
433110949Sobrien
434110949Sobrien	sc = device_get_softc(dev);
435133359Sobrien
436133359Sobrien	if (sc->sc_vreg)
437133359Sobrien		device_delete_child(dev, sc->sc_vreg);
438186690Sobrien	if (sc->sc_clks)
439133359Sobrien		device_delete_child(dev, sc->sc_clks);
440133359Sobrien
441133359Sobrien
442133359Sobrien	TWL_LOCK_DESTROY(sc);
443133359Sobrien
444133359Sobrien	return (0);
445133359Sobrien}
446133359Sobrien
447133359Sobrienstatic device_method_t twl_methods[] = {
448133359Sobrien	DEVMETHOD(device_probe,		twl_probe),
449133359Sobrien	DEVMETHOD(device_attach,	twl_attach),
450133359Sobrien	DEVMETHOD(device_detach,	twl_detach),
451133359Sobrien
452133359Sobrien	{0, 0},
453133359Sobrien};
454133359Sobrien
455133359Sobrienstatic driver_t twl_driver = {
456133359Sobrien	"twl",
457133359Sobrien	twl_methods,
458133359Sobrien	sizeof(struct twl_softc),
459133359Sobrien};
460133359Sobrienstatic devclass_t twl_devclass;
461133359Sobrien
462133359SobrienDRIVER_MODULE(twl, iicbus, twl_driver, twl_devclass, 0, 0);
463133359SobrienMODULE_VERSION(twl, 1);
464133359Sobrien