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 = ® 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