The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/arm/ti/twl/twl.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2011
    5  *      Ben Gray <ben.r.gray@gmail.com>.
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD$");
   32 
   33 /*
   34  * Texas Instruments TWL4030/TWL5030/TWL60x0/TPS659x0 Power Management and
   35  * Audio CODEC devices.
   36  *
   37  * This code is based on the Linux TWL multifunctional device driver, which is
   38  * copyright (C) 2005-2006 Texas Instruments, Inc.
   39  *
   40  * These chips are typically used as support ICs for the OMAP range of embedded
   41  * ARM processes/SOC from Texas Instruments.  They are typically used to control
   42  * on board voltages, however some variants have other features like audio
   43  * codecs, USB OTG transceivers, RTC, PWM, etc.
   44  *
   45  * This driver acts as a bus for more specific companion devices.
   46  *
   47  */
   48 
   49 #include <sys/param.h>
   50 #include <sys/systm.h>
   51 #include <sys/kernel.h>
   52 #include <sys/lock.h>
   53 #include <sys/module.h>
   54 #include <sys/bus.h>
   55 #include <sys/resource.h>
   56 #include <sys/rman.h>
   57 #include <sys/sysctl.h>
   58 #include <sys/mutex.h>
   59 #include <sys/malloc.h>
   60 
   61 #include <machine/bus.h>
   62 #include <machine/resource.h>
   63 #include <machine/intr.h>
   64 
   65 #include <dev/iicbus/iicbus.h>
   66 #include <dev/iicbus/iiconf.h>
   67 
   68 #include <dev/ofw/openfirm.h>
   69 #include <dev/ofw/ofw_bus.h>
   70 #include <dev/ofw/ofw_bus_subr.h>
   71 
   72 #include "arm/ti/twl/twl.h"
   73 
   74 /* TWL device IDs */
   75 #define TWL_DEVICE_UNKNOWN          0xffff
   76 #define TWL_DEVICE_4030             0x4030
   77 #define TWL_DEVICE_6025             0x6025
   78 #define TWL_DEVICE_6030             0x6030
   79 
   80 /* Each TWL device typically has more than one I2C address */
   81 #define TWL_MAX_SUBADDRS            4
   82 
   83 /* The maximum number of bytes that can be written in one call */
   84 #define TWL_MAX_IIC_DATA_SIZE       63
   85 
   86 /* The TWL devices typically use 4 I2C address for the different internal
   87  * register sets, plus one SmartReflex I2C address.
   88  */
   89 #define TWL_CHIP_ID0                0x48
   90 #define TWL_CHIP_ID1                0x49
   91 #define TWL_CHIP_ID2                0x4A
   92 #define TWL_CHIP_ID3                0x4B
   93 
   94 #define TWL_SMARTREFLEX_CHIP_ID     0x12
   95 
   96 #define TWL_INVALID_CHIP_ID         0xff
   97 
   98 struct twl_softc {
   99         device_t                sc_dev;
  100         struct mtx              sc_mtx;
  101         unsigned int    sc_type;
  102 
  103         uint8_t                 sc_subaddr_map[TWL_MAX_SUBADDRS];
  104 
  105         struct intr_config_hook sc_scan_hook;
  106 
  107         device_t                sc_vreg;
  108         device_t                sc_clks;
  109 };
  110 
  111 /**
  112  *      Macros for driver mutex locking
  113  */
  114 #define TWL_LOCK(_sc)             mtx_lock(&(_sc)->sc_mtx)
  115 #define TWL_UNLOCK(_sc)           mtx_unlock(&(_sc)->sc_mtx)
  116 #define TWL_LOCK_INIT(_sc) \
  117         mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
  118                  "twl", MTX_DEF)
  119 #define TWL_LOCK_DESTROY(_sc)     mtx_destroy(&_sc->sc_mtx);
  120 #define TWL_ASSERT_LOCKED(_sc)    mtx_assert(&_sc->sc_mtx, MA_OWNED);
  121 #define TWL_ASSERT_UNLOCKED(_sc)  mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
  122 
  123 /**
  124  *      twl_is_4030 - returns true if the device is TWL4030
  125  *      twl_is_6025 - returns true if the device is TWL6025
  126  *      twl_is_6030 - returns true if the device is TWL6030
  127  *      @sc: device soft context
  128  *
  129  *      Returns a non-zero value if the device matches.
  130  *
  131  *      RETURNS:
  132  *      Returns a non-zero value if the device matches, otherwise zero.
  133  */
  134 int
  135 twl_is_4030(device_t dev)
  136 {
  137         struct twl_softc *sc = device_get_softc(dev);
  138         return (sc->sc_type == TWL_DEVICE_4030);
  139 }
  140 
  141 int
  142 twl_is_6025(device_t dev)
  143 {
  144         struct twl_softc *sc = device_get_softc(dev);
  145         return (sc->sc_type == TWL_DEVICE_6025);
  146 }
  147 
  148 int
  149 twl_is_6030(device_t dev)
  150 {
  151         struct twl_softc *sc = device_get_softc(dev);
  152         return (sc->sc_type == TWL_DEVICE_6030);
  153 }
  154 
  155 /**
  156  *      twl_read - read one or more registers from the TWL device
  157  *      @sc: device soft context
  158  *      @nsub: the sub-module to read from
  159  *      @reg: the register offset within the module to read
  160  *      @buf: buffer to store the bytes in
  161  *      @cnt: the number of bytes to read
  162  *
  163  *      Reads one or more registers and stores the result in the suppled buffer.
  164  *
  165  *      RETURNS:
  166  *      Zero on success or an error code on failure.
  167  */
  168 int
  169 twl_read(device_t dev, uint8_t nsub, uint8_t reg, uint8_t *buf, uint16_t cnt)
  170 {
  171         struct twl_softc *sc;
  172         struct iic_msg msg[2];
  173         uint8_t addr;
  174         int rc;
  175 
  176         sc = device_get_softc(dev);
  177 
  178         TWL_LOCK(sc);
  179         addr = sc->sc_subaddr_map[nsub];
  180         TWL_UNLOCK(sc);
  181 
  182         if (addr == TWL_INVALID_CHIP_ID)
  183                 return (EIO);
  184 
  185         /* Set the address to read from */
  186         msg[0].slave = addr;
  187         msg[0].flags = IIC_M_WR | IIC_M_NOSTOP;
  188         msg[0].len = 1;
  189         msg[0].buf = &reg;
  190         /* Read the data back */
  191         msg[1].slave = addr;
  192         msg[1].flags = IIC_M_RD;
  193         msg[1].len = cnt;
  194         msg[1].buf = buf;
  195 
  196         rc = iicbus_transfer(dev, msg, 2);
  197         if (rc != 0) {
  198                 device_printf(dev, "iicbus read failed (adr:0x%02x, reg:0x%02x)\n",
  199                               addr, reg);
  200                 return (EIO);
  201         }
  202 
  203         return (0);
  204 }
  205 
  206 /**
  207  *      twl_write - writes one or more registers to the TWL device
  208  *      @sc: device soft context
  209  *      @nsub: the sub-module to read from
  210  *      @reg: the register offset within the module to read
  211  *      @buf: data to write
  212  *      @cnt: the number of bytes to write
  213  *
  214  *      Writes one or more registers.
  215  *
  216  *      RETURNS:
  217  *      Zero on success or a negative error code on failure.
  218  */
  219 int
  220 twl_write(device_t dev, uint8_t nsub, uint8_t reg, uint8_t *buf, uint16_t cnt)
  221 {
  222         struct twl_softc *sc;
  223         struct iic_msg msg;
  224         uint8_t addr;
  225         uint8_t tmp_buf[TWL_MAX_IIC_DATA_SIZE + 1];
  226         int rc;
  227 
  228         if (cnt > TWL_MAX_IIC_DATA_SIZE)
  229                 return (ENOMEM);
  230 
  231         /* Set the register address as the first byte */
  232         tmp_buf[0] = reg;
  233         memcpy(&tmp_buf[1], buf, cnt);
  234 
  235         sc = device_get_softc(dev);
  236 
  237         TWL_LOCK(sc);
  238         addr = sc->sc_subaddr_map[nsub];
  239         TWL_UNLOCK(sc);
  240 
  241         if (addr == TWL_INVALID_CHIP_ID)
  242                 return (EIO);
  243 
  244         /* Setup the transfer and execute it */
  245         msg.slave = addr;
  246         msg.flags = IIC_M_WR;
  247         msg.len = cnt + 1;
  248         msg.buf = tmp_buf;
  249 
  250         rc = iicbus_transfer(dev, &msg, 1);
  251         if (rc != 0) {
  252                 device_printf(sc->sc_dev, "iicbus write failed (adr:0x%02x, reg:0x%02x)\n",
  253                               addr, reg);
  254                 return (EIO);
  255         }
  256 
  257         return (0);
  258 }
  259 
  260 /**
  261  *      twl_test_present - checks if a device with given address is present
  262  *      @sc: device soft context
  263  *      @addr: the address of the device to scan for
  264  *
  265  *      Sends just the address byte and checks for an ACK. If no ACK then device
  266  *      is assumed to not be present.
  267  *
  268  *      RETURNS:
  269  *      EIO if device is not present, otherwise 0 is returned.
  270  */
  271 static int
  272 twl_test_present(struct twl_softc *sc, uint8_t addr)
  273 {
  274         struct iic_msg msg;
  275         uint8_t tmp;
  276 
  277         /* Set the address to read from */
  278         msg.slave = addr;
  279         msg.flags = IIC_M_RD;
  280         msg.len = 1;
  281         msg.buf = &tmp;
  282 
  283         if (iicbus_transfer(sc->sc_dev, &msg, 1) != 0)
  284                 return (EIO);
  285 
  286         return (0);
  287 }
  288 
  289 /**
  290  *      twl_scan - scans the i2c bus for sub modules
  291  *      @dev: the twl device
  292  *
  293  *      TWL devices don't just have one i2c slave address, rather they have up to
  294  *      5 other addresses, each is for separate modules within the device. This
  295  *      function scans the bus for 4 possible sub-devices and stores the info
  296  *      internally.
  297  *
  298  */
  299 static void
  300 twl_scan(void *dev)
  301 {
  302         struct twl_softc *sc;
  303         unsigned i;
  304         uint8_t devs[TWL_MAX_SUBADDRS];
  305         uint8_t base = TWL_CHIP_ID0;
  306 
  307         sc = device_get_softc((device_t)dev);
  308 
  309         memset(devs, TWL_INVALID_CHIP_ID, TWL_MAX_SUBADDRS);
  310 
  311         /* Try each of the addresses (0x48, 0x49, 0x4a & 0x4b) to determine which
  312          * sub modules we have.
  313          */
  314         for (i = 0; i < TWL_MAX_SUBADDRS; i++) {
  315                 if (twl_test_present(sc, (base + i)) == 0) {
  316                         devs[i] = (base + i);
  317                         device_printf(sc->sc_dev, "Found (sub)device at 0x%02x\n", (base + i));
  318                 }
  319         }
  320 
  321         TWL_LOCK(sc);
  322         memcpy(sc->sc_subaddr_map, devs, TWL_MAX_SUBADDRS);
  323         TWL_UNLOCK(sc);
  324 
  325         /* Finished with the interrupt hook */
  326         config_intrhook_disestablish(&sc->sc_scan_hook);
  327 }
  328 
  329 /**
  330  *      twl_probe - 
  331  *      @dev: the twl device
  332  *
  333  *      Scans the FDT for a match for the device, possible compatible device
  334  *      strings are; "ti,twl6030", "ti,twl6025", "ti,twl4030".  
  335  *
  336  *      The FDT compat string also determines the type of device (it is currently
  337  *      not possible to dynamically determine the device type).
  338  *
  339  */
  340 static int
  341 twl_probe(device_t dev)
  342 {
  343         phandle_t node;
  344         const char *compat;
  345         int len, l;
  346         struct twl_softc *sc;
  347 
  348         if ((compat = ofw_bus_get_compat(dev)) == NULL)
  349                 return (ENXIO);
  350 
  351         if ((node = ofw_bus_get_node(dev)) == 0)
  352                 return (ENXIO);
  353 
  354         /* Get total 'compatible' prop len */
  355         if ((len = OF_getproplen(node, "compatible")) <= 0)
  356                 return (ENXIO);
  357 
  358         sc = device_get_softc(dev);
  359         sc->sc_dev = dev;
  360         sc->sc_type = TWL_DEVICE_UNKNOWN;
  361 
  362         while (len > 0) {
  363                 if (strncasecmp(compat, "ti,twl6030", 10) == 0)
  364                         sc->sc_type = TWL_DEVICE_6030;
  365                 else if (strncasecmp(compat, "ti,twl6025", 10) == 0)
  366                         sc->sc_type = TWL_DEVICE_6025;
  367                 else if (strncasecmp(compat, "ti,twl4030", 10) == 0)
  368                         sc->sc_type = TWL_DEVICE_4030;
  369                 
  370                 if (sc->sc_type != TWL_DEVICE_UNKNOWN)
  371                         break;
  372 
  373                 /* Slide to the next sub-string. */
  374                 l = strlen(compat) + 1;
  375                 compat += l;
  376                 len -= l;
  377         }
  378 
  379         switch (sc->sc_type) {
  380         case TWL_DEVICE_4030:
  381                 device_set_desc(dev, "TI TWL4030/TPS659x0 Companion IC");
  382                 break;
  383         case TWL_DEVICE_6025:
  384                 device_set_desc(dev, "TI TWL6025 Companion IC");
  385                 break;
  386         case TWL_DEVICE_6030:
  387                 device_set_desc(dev, "TI TWL6030 Companion IC");
  388                 break;
  389         case TWL_DEVICE_UNKNOWN:
  390         default:
  391                 return (ENXIO);
  392         }
  393 
  394         return (0);
  395 }
  396 
  397 static int
  398 twl_attach(device_t dev)
  399 {
  400         struct twl_softc *sc;
  401 
  402         sc = device_get_softc(dev);
  403         sc->sc_dev = dev;
  404 
  405         TWL_LOCK_INIT(sc);
  406 
  407         /* We have to wait until interrupts are enabled. I2C read and write
  408          * only works if the interrupts are available.
  409          */
  410         sc->sc_scan_hook.ich_func = twl_scan;
  411         sc->sc_scan_hook.ich_arg = dev;
  412 
  413         if (config_intrhook_establish(&sc->sc_scan_hook) != 0)
  414                 return (ENOMEM);
  415 
  416         /* FIXME: should be in DTS file */
  417         if ((sc->sc_vreg = device_add_child(dev, "twl_vreg", -1)) == NULL)
  418                 device_printf(dev, "could not allocate twl_vreg instance\n");
  419         if ((sc->sc_clks = device_add_child(dev, "twl_clks", -1)) == NULL)
  420                 device_printf(dev, "could not allocate twl_clks instance\n");
  421 
  422         return (bus_generic_attach(dev));
  423 }
  424 
  425 static int
  426 twl_detach(device_t dev)
  427 {
  428         struct twl_softc *sc;
  429 
  430         sc = device_get_softc(dev);
  431 
  432         if (sc->sc_vreg)
  433                 device_delete_child(dev, sc->sc_vreg);
  434         if (sc->sc_clks)
  435                 device_delete_child(dev, sc->sc_clks);
  436 
  437         TWL_LOCK_DESTROY(sc);
  438 
  439         return (0);
  440 }
  441 
  442 static device_method_t twl_methods[] = {
  443         DEVMETHOD(device_probe,         twl_probe),
  444         DEVMETHOD(device_attach,        twl_attach),
  445         DEVMETHOD(device_detach,        twl_detach),
  446 
  447         {0, 0},
  448 };
  449 
  450 static driver_t twl_driver = {
  451         "twl",
  452         twl_methods,
  453         sizeof(struct twl_softc),
  454 };
  455 
  456 DRIVER_MODULE(twl, iicbus, twl_driver, 0, 0);
  457 MODULE_VERSION(twl, 1);

Cache object: 2cb5f974a0ff3d7cfdfbe4101a37f909


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.