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_vreg.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.
   35  *
   36  * This driver covers the voltages regulators (LDO), allows for enabling &
   37  * disabling the voltage output and adjusting the voltage level.
   38  *
   39  * Voltage regulators can belong to different power groups, in this driver we
   40  * put the regulators under our control in the "Application power group".
   41  *
   42  *
   43  * FLATTENED DEVICE TREE (FDT)
   44  * Startup override settings can be specified in the FDT, if they are they
   45  * should be under the twl parent device and take the following form:
   46  *
   47  *    voltage-regulators = "name1", "millivolts1",
   48  *                         "name2", "millivolts2";
   49  *
   50  * Each override should be a pair, the first entry is the name of the regulator
   51  * the second is the voltage (in millivolts) to set for the given regulator.
   52  *
   53  */
   54 
   55 #include <sys/param.h>
   56 #include <sys/systm.h>
   57 #include <sys/kernel.h>
   58 #include <sys/lock.h>
   59 #include <sys/module.h>
   60 #include <sys/bus.h>
   61 #include <sys/resource.h>
   62 #include <sys/rman.h>
   63 #include <sys/sysctl.h>
   64 #include <sys/sx.h>
   65 #include <sys/malloc.h>
   66 
   67 #include <machine/bus.h>
   68 #include <machine/resource.h>
   69 #include <machine/intr.h>
   70 
   71 #include <dev/ofw/openfirm.h>
   72 #include <dev/ofw/ofw_bus.h>
   73 
   74 #include "twl.h"
   75 #include "twl_vreg.h"
   76 
   77 static int twl_vreg_debug = 1;
   78 
   79 /*
   80  * Power Groups bits for the 4030 and 6030 devices
   81  */
   82 #define TWL4030_P3_GRP          0x80    /* Peripherals, power group */
   83 #define TWL4030_P2_GRP          0x40    /* Modem power group */
   84 #define TWL4030_P1_GRP          0x20    /* Application power group (FreeBSD control) */
   85 
   86 #define TWL6030_P3_GRP          0x04    /* Modem power group */
   87 #define TWL6030_P2_GRP          0x02    /* Connectivity power group */
   88 #define TWL6030_P1_GRP          0x01    /* Application power group (FreeBSD control) */
   89 
   90 /*
   91  * Register offsets within a LDO regulator register set
   92  */
   93 #define TWL_VREG_GRP            0x00    /* Regulator GRP register */
   94 #define TWL_VREG_STATE          0x02
   95 #define TWL_VREG_VSEL           0x03    /* Voltage select register */
   96 
   97 #define UNDF  0xFFFF
   98 
   99 static const uint16_t twl6030_voltages[] = {
  100         0000, 1000, 1100, 1200, 1300, 1400, 1500, 1600,
  101         1700, 1800, 1900, 2000, 2100, 2200, 2300, 2400,
  102         2500, 2600, 2700, 2800, 2900, 3000, 3100, 3200,
  103         3300, UNDF, UNDF, UNDF, UNDF, UNDF, UNDF, 2750
  104 };
  105 
  106 static const uint16_t twl4030_vaux1_voltages[] = {
  107         1500, 1800, 2500, 2800, 3000, 3000, 3000, 3000
  108 };
  109 static const uint16_t twl4030_vaux2_voltages[] = {
  110         1700, 1700, 1900, 1300, 1500, 1800, 2000, 2500,
  111         2100, 2800, 2200, 2300, 2400, 2400, 2400, 2400
  112 };
  113 static const uint16_t twl4030_vaux3_voltages[] = {
  114         1500, 1800, 2500, 2800, 3000, 3000, 3000, 3000
  115 };
  116 static const uint16_t twl4030_vaux4_voltages[] = {
  117         700,  1000, 1200, 1300, 1500, 1800, 1850, 2500,
  118         2600, 2800, 2850, 3000, 3150, 3150, 3150, 3150
  119 };
  120 static const uint16_t twl4030_vmmc1_voltages[] = {
  121         1850, 2850, 3000, 3150
  122 };
  123 static const uint16_t twl4030_vmmc2_voltages[] = {
  124         1000, 1000, 1200, 1300, 1500, 1800, 1850, 2500,
  125         2600, 2800, 2850, 3000, 3150, 3150, 3150, 3150
  126 };
  127 static const uint16_t twl4030_vpll1_voltages[] = {
  128         1000, 1200, 1300, 1800, 2800, 3000, 3000, 3000
  129 };
  130 static const uint16_t twl4030_vpll2_voltages[] = {
  131         700,  1000, 1200, 1300, 1500, 1800, 1850, 2500,
  132         2600, 2800, 2850, 3000, 3150, 3150, 3150, 3150
  133 };
  134 static const uint16_t twl4030_vsim_voltages[] = {
  135         1000, 1200, 1300, 1800, 2800, 3000, 3000, 3000
  136 };
  137 static const uint16_t twl4030_vdac_voltages[] = {
  138         1200, 1300, 1800, 1800
  139 };
  140 #if 0 /* vdd1, vdd2, vdio, not currently used. */
  141 static const uint16_t twl4030_vdd1_voltages[] = {
  142         800, 1450
  143 };
  144 static const uint16_t twl4030_vdd2_voltages[] = {
  145         800, 1450, 1500
  146 };
  147 static const uint16_t twl4030_vio_voltages[] = {
  148         1800, 1850
  149 };
  150 #endif
  151 static const uint16_t twl4030_vintana2_voltages[] = {
  152         2500, 2750
  153 };
  154 
  155 /**
  156  *  Support voltage regulators for the different IC's
  157  */
  158 struct twl_regulator {
  159         const char      *name;
  160         uint8_t         subdev;
  161         uint8_t         regbase;
  162 
  163         uint16_t        fixedvoltage;
  164 
  165         const uint16_t  *voltages;
  166         uint32_t        num_voltages;
  167 };
  168 
  169 #define TWL_REGULATOR_ADJUSTABLE(name, subdev, reg, voltages) \
  170         { name, subdev, reg, 0, voltages, (sizeof(voltages)/sizeof(voltages[0])) }
  171 #define TWL_REGULATOR_FIXED(name, subdev, reg, voltage) \
  172         { name, subdev, reg, voltage, NULL, 0 }
  173 
  174 static const struct twl_regulator twl4030_regulators[] = {
  175         TWL_REGULATOR_ADJUSTABLE("vaux1",    0, 0x17, twl4030_vaux1_voltages),
  176         TWL_REGULATOR_ADJUSTABLE("vaux2",    0, 0x1B, twl4030_vaux2_voltages),
  177         TWL_REGULATOR_ADJUSTABLE("vaux3",    0, 0x1F, twl4030_vaux3_voltages),
  178         TWL_REGULATOR_ADJUSTABLE("vaux4",    0, 0x23, twl4030_vaux4_voltages),
  179         TWL_REGULATOR_ADJUSTABLE("vmmc1",    0, 0x27, twl4030_vmmc1_voltages),
  180         TWL_REGULATOR_ADJUSTABLE("vmmc2",    0, 0x2B, twl4030_vmmc2_voltages),
  181         TWL_REGULATOR_ADJUSTABLE("vpll1",    0, 0x2F, twl4030_vpll1_voltages),
  182         TWL_REGULATOR_ADJUSTABLE("vpll2",    0, 0x33, twl4030_vpll2_voltages),
  183         TWL_REGULATOR_ADJUSTABLE("vsim",     0, 0x37, twl4030_vsim_voltages),
  184         TWL_REGULATOR_ADJUSTABLE("vdac",     0, 0x3B, twl4030_vdac_voltages),
  185         TWL_REGULATOR_ADJUSTABLE("vintana2", 0, 0x43, twl4030_vintana2_voltages),
  186         TWL_REGULATOR_FIXED("vintana1", 0, 0x3F, 1500),
  187         TWL_REGULATOR_FIXED("vintdig",  0, 0x47, 1500),
  188         TWL_REGULATOR_FIXED("vusb1v5",  0, 0x71, 1500),
  189         TWL_REGULATOR_FIXED("vusb1v8",  0, 0x74, 1800),
  190         TWL_REGULATOR_FIXED("vusb3v1",  0, 0x77, 3100),
  191         { NULL, 0, 0x00, 0, NULL, 0 }
  192 };
  193 
  194 static const struct twl_regulator twl6030_regulators[] = {
  195         TWL_REGULATOR_ADJUSTABLE("vaux1", 0, 0x84, twl6030_voltages),
  196         TWL_REGULATOR_ADJUSTABLE("vaux2", 0, 0x89, twl6030_voltages),
  197         TWL_REGULATOR_ADJUSTABLE("vaux3", 0, 0x8C, twl6030_voltages),
  198         TWL_REGULATOR_ADJUSTABLE("vmmc",  0, 0x98, twl6030_voltages),
  199         TWL_REGULATOR_ADJUSTABLE("vpp",   0, 0x9C, twl6030_voltages),
  200         TWL_REGULATOR_ADJUSTABLE("vusim", 0, 0xA4, twl6030_voltages),
  201         TWL_REGULATOR_FIXED("vmem",  0, 0x64, 1800),
  202         TWL_REGULATOR_FIXED("vusb",  0, 0xA0, 3300),
  203         TWL_REGULATOR_FIXED("v1v8",  0, 0x46, 1800),
  204         TWL_REGULATOR_FIXED("v2v1",  0, 0x4C, 2100),
  205         TWL_REGULATOR_FIXED("v1v29", 0, 0x40, 1290),
  206         TWL_REGULATOR_FIXED("vcxio", 0, 0x90, 1800),
  207         TWL_REGULATOR_FIXED("vdac",  0, 0x94, 1800),
  208         TWL_REGULATOR_FIXED("vana",  0, 0x80, 2100),
  209         { NULL, 0, 0x00, 0, NULL, 0 } 
  210 };
  211 
  212 #define TWL_VREG_MAX_NAMELEN  32
  213 
  214 struct twl_regulator_entry {
  215         LIST_ENTRY(twl_regulator_entry) entries;
  216         char                 name[TWL_VREG_MAX_NAMELEN];
  217         struct sysctl_oid   *oid;
  218         uint8_t          sub_dev;           /* TWL sub-device group */
  219         uint8_t          reg_off;           /* base register offset for the LDO */
  220         uint16_t         fixed_voltage;     /* the (milli)voltage if LDO is fixed */ 
  221         const uint16_t  *supp_voltages;     /* pointer to an array of possible voltages */
  222         uint32_t         num_supp_voltages; /* the number of supplied voltages */
  223 };
  224 
  225 struct twl_vreg_softc {
  226         device_t        sc_dev;
  227         device_t        sc_pdev;
  228         struct sx       sc_sx;
  229 
  230         struct intr_config_hook sc_init_hook;
  231         LIST_HEAD(twl_regulator_list, twl_regulator_entry) sc_vreg_list;
  232 };
  233 
  234 #define TWL_VREG_XLOCK(_sc)                     sx_xlock(&(_sc)->sc_sx)
  235 #define TWL_VREG_XUNLOCK(_sc)           sx_xunlock(&(_sc)->sc_sx)
  236 #define TWL_VREG_SLOCK(_sc)                     sx_slock(&(_sc)->sc_sx)
  237 #define TWL_VREG_SUNLOCK(_sc)           sx_sunlock(&(_sc)->sc_sx)
  238 #define TWL_VREG_LOCK_INIT(_sc)         sx_init(&(_sc)->sc_sx, "twl_vreg")
  239 #define TWL_VREG_LOCK_DESTROY(_sc)      sx_destroy(&(_sc)->sc_sx);
  240 
  241 #define TWL_VREG_ASSERT_LOCKED(_sc)     sx_assert(&(_sc)->sc_sx, SA_LOCKED);
  242 
  243 #define TWL_VREG_LOCK_UPGRADE(_sc)               \
  244         do {                                         \
  245                 while (!sx_try_upgrade(&(_sc)->sc_sx))   \
  246                         pause("twl_vreg_ex", (hz / 100));    \
  247         } while(0)
  248 #define TWL_VREG_LOCK_DOWNGRADE(_sc)    sx_downgrade(&(_sc)->sc_sx);
  249 
  250 /**
  251  *      twl_vreg_read_1 - read single register from the TWL device
  252  *      twl_vreg_write_1 - write a single register in the TWL device
  253  *      @sc: device context
  254  *      @clk: the clock device we're reading from / writing to
  255  *      @off: offset within the clock's register set
  256  *      @val: the value to write or a pointer to a variable to store the result
  257  *
  258  *      RETURNS:
  259  *      Zero on success or an error code on failure.
  260  */
  261 static inline int
  262 twl_vreg_read_1(struct twl_vreg_softc *sc, struct twl_regulator_entry *regulator,
  263         uint8_t off, uint8_t *val)
  264 {
  265         return (twl_read(sc->sc_pdev, regulator->sub_dev, 
  266             regulator->reg_off + off, val, 1));
  267 }
  268 
  269 static inline int
  270 twl_vreg_write_1(struct twl_vreg_softc *sc, struct twl_regulator_entry *regulator,
  271         uint8_t off, uint8_t val)
  272 {
  273         return (twl_write(sc->sc_pdev, regulator->sub_dev,
  274             regulator->reg_off + off, &val, 1));
  275 }
  276 
  277 /**
  278  *      twl_millivolt_to_vsel - gets the vsel bit value to write into the register
  279  *                              for a desired voltage and regulator
  280  *      @sc: the device soft context
  281  *      @regulator: pointer to the regulator device
  282  *      @millivolts: the millivolts to find the bit value for
  283  *      @vsel: upon return will contain the corresponding register value
  284  *
  285  *      Accepts a (milli)voltage value and tries to find the closest match to the
  286  *      actual supported voltages for the given regulator.  If a match is found
  287  *      within 100mv of the target, @vsel is written with the match and 0 is
  288  *      returned. If no voltage match is found the function returns an non-zero
  289  *      value.
  290  *
  291  *      RETURNS:
  292  *      Zero on success or an error code on failure.
  293  */
  294 static int
  295 twl_vreg_millivolt_to_vsel(struct twl_vreg_softc *sc,
  296         struct twl_regulator_entry *regulator, int millivolts, uint8_t *vsel)
  297 {
  298         int delta, smallest_delta;
  299         unsigned i, closest_idx;
  300 
  301         TWL_VREG_ASSERT_LOCKED(sc);
  302 
  303         if (regulator->supp_voltages == NULL)
  304                 return (EINVAL);
  305 
  306         /* Loop over the support voltages and try and find the closest match */
  307         closest_idx = 0;
  308         smallest_delta = 0x7fffffff;
  309         for (i = 0; i < regulator->num_supp_voltages; i++) {
  310                 /* Ignore undefined values */
  311                 if (regulator->supp_voltages[i] == UNDF)
  312                         continue;
  313 
  314                 /* Calculate the difference */
  315                 delta = millivolts - (int)regulator->supp_voltages[i];
  316                 if (abs(delta) < smallest_delta) {
  317                         smallest_delta = abs(delta);
  318                         closest_idx = i;
  319                 }
  320         }
  321 
  322         /* Check we got a voltage that was within 100mv of the actual target, this
  323          * is just a value I picked out of thin air.
  324          */
  325         if ((smallest_delta > 100) && (closest_idx < 0x100))
  326                 return (EINVAL);
  327 
  328         *vsel = closest_idx;
  329         return (0);
  330 }
  331 
  332 /**
  333  *      twl_vreg_is_regulator_enabled - returns the enabled status of the regulator
  334  *      @sc: the device soft context
  335  *      @regulator: pointer to the regulator device
  336  *      @enabled: stores the enabled status, zero disabled, non-zero enabled
  337  *
  338  *      LOCKING:
  339  *      On entry expects the TWL VREG lock to be held. Will upgrade the lock to
  340  *      exclusive if not already but, if so, it will be downgraded again before
  341  *      returning.
  342  *
  343  *      RETURNS:
  344  *      Zero on success or an error code on failure.
  345  */
  346 static int
  347 twl_vreg_is_regulator_enabled(struct twl_vreg_softc *sc,
  348         struct twl_regulator_entry *regulator, int *enabled)
  349 {
  350         int err;
  351         uint8_t grp;
  352         uint8_t state;
  353         int xlocked;
  354 
  355         if (enabled == NULL)
  356                 return (EINVAL);
  357 
  358         TWL_VREG_ASSERT_LOCKED(sc);
  359 
  360         xlocked = sx_xlocked(&sc->sc_sx);
  361         if (!xlocked)
  362                 TWL_VREG_LOCK_UPGRADE(sc);
  363 
  364         /* The status reading is different for the different devices */
  365         if (twl_is_4030(sc->sc_pdev)) {
  366                 err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &state);
  367                 if (err)
  368                         goto done;
  369 
  370                 *enabled = (state & TWL4030_P1_GRP);
  371 
  372         } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
  373                 /* Check the regulator is in the application group */
  374                 if (twl_is_6030(sc->sc_pdev)) {
  375                         err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &grp);
  376                         if (err)
  377                                 goto done;
  378 
  379                         if (!(grp & TWL6030_P1_GRP)) {
  380                                 *enabled = 0; /* disabled */
  381                                 goto done;
  382                         }
  383                 }
  384 
  385                 /* Read the application mode state and verify it's ON */
  386                 err = twl_vreg_read_1(sc, regulator, TWL_VREG_STATE, &state);
  387                 if (err)
  388                         goto done;
  389 
  390                 *enabled = ((state & 0x0C) == 0x04);
  391 
  392         } else {
  393                 err = EINVAL;
  394         }
  395 
  396 done:
  397         if (!xlocked)
  398                 TWL_VREG_LOCK_DOWNGRADE(sc);
  399 
  400         return (err);
  401 }
  402 
  403 /**
  404  *      twl_vreg_disable_regulator - disables a voltage regulator
  405  *      @sc: the device soft context
  406  *      @regulator: pointer to the regulator device
  407  *
  408  *      Disables the regulator which will stop the output drivers.
  409  *
  410  *      LOCKING:
  411  *      On entry expects the TWL VREG lock to be held. Will upgrade the lock to
  412  *      exclusive if not already but, if so, it will be downgraded again before
  413  *      returning.
  414  *
  415  *      RETURNS:
  416  *      Zero on success or a positive error code on failure.
  417  */
  418 static int
  419 twl_vreg_disable_regulator(struct twl_vreg_softc *sc,
  420         struct twl_regulator_entry *regulator)
  421 {
  422         int err = 0;
  423         uint8_t grp;
  424         int xlocked;
  425 
  426         TWL_VREG_ASSERT_LOCKED(sc);
  427 
  428         xlocked = sx_xlocked(&sc->sc_sx);
  429         if (!xlocked)
  430                 TWL_VREG_LOCK_UPGRADE(sc);
  431 
  432         if (twl_is_4030(sc->sc_pdev)) {
  433                 /* Read the regulator CFG_GRP register */
  434                 err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &grp);
  435                 if (err)
  436                         goto done;
  437 
  438                 /* On the TWL4030 we just need to remove the regulator from all the
  439                  * power groups.
  440                  */
  441                 grp &= ~(TWL4030_P1_GRP | TWL4030_P2_GRP | TWL4030_P3_GRP);
  442                 err = twl_vreg_write_1(sc, regulator, TWL_VREG_GRP, grp);
  443 
  444         } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
  445                 /* On TWL6030 we need to make sure we disable power for all groups */
  446                 if (twl_is_6030(sc->sc_pdev))
  447                         grp = TWL6030_P1_GRP | TWL6030_P2_GRP | TWL6030_P3_GRP;
  448                 else
  449                         grp = 0x00;
  450 
  451                 /* Write the resource state to "OFF" */
  452                 err = twl_vreg_write_1(sc, regulator, TWL_VREG_STATE, (grp << 5));
  453         }
  454 
  455 done:
  456         if (!xlocked)
  457                 TWL_VREG_LOCK_DOWNGRADE(sc);
  458 
  459         return (err);
  460 }
  461 
  462 /**
  463  *      twl_vreg_enable_regulator - enables the voltage regulator
  464  *      @sc: the device soft context
  465  *      @regulator: pointer to the regulator device
  466  *
  467  *      Enables the regulator which will enable the voltage out at the currently
  468  *      set voltage.  Set the voltage before calling this function to avoid
  469  *      driving the voltage too high/low by mistake.
  470  *
  471  *      LOCKING:
  472  *      On entry expects the TWL VREG lock to be held. Will upgrade the lock to
  473  *      exclusive if not already but, if so, it will be downgraded again before
  474  *      returning.
  475  *
  476  *      RETURNS:
  477  *      Zero on success or a positive error code on failure.
  478  */
  479 static int
  480 twl_vreg_enable_regulator(struct twl_vreg_softc *sc,
  481     struct twl_regulator_entry *regulator)
  482 {
  483         int err;
  484         uint8_t grp;
  485         int xlocked;
  486 
  487         TWL_VREG_ASSERT_LOCKED(sc);
  488 
  489         xlocked = sx_xlocked(&sc->sc_sx);
  490         if (!xlocked)
  491                 TWL_VREG_LOCK_UPGRADE(sc);
  492 
  493         err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &grp);
  494         if (err)
  495                 goto done;
  496 
  497         /* Enable the regulator by ensuring it's in the application power group
  498          * and is in the "on" state.
  499          */
  500         if (twl_is_4030(sc->sc_pdev)) {
  501                 /* On the TWL4030 we just need to ensure the regulator is in the right
  502                  * power domain, don't need to turn on explicitly like TWL6030.
  503                  */
  504                 grp |= TWL4030_P1_GRP;
  505                 err = twl_vreg_write_1(sc, regulator, TWL_VREG_GRP, grp);
  506 
  507         } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
  508                 if (twl_is_6030(sc->sc_pdev) && !(grp & TWL6030_P1_GRP)) {
  509                         grp |= TWL6030_P1_GRP;
  510                         err = twl_vreg_write_1(sc, regulator, TWL_VREG_GRP, grp);
  511                         if (err)
  512                                 goto done;
  513                 }
  514 
  515                 /* Write the resource state to "ON" */
  516                 err = twl_vreg_write_1(sc, regulator, TWL_VREG_STATE, (grp << 5) | 0x01);
  517         }
  518 
  519 done:
  520         if (!xlocked)
  521                 TWL_VREG_LOCK_DOWNGRADE(sc);
  522 
  523         return (err);
  524 }
  525 
  526 /**
  527  *      twl_vreg_write_regulator_voltage - sets the voltage level on a regulator
  528  *      @sc: the device soft context
  529  *      @regulator: pointer to the regulator structure
  530  *      @millivolts: the voltage to set
  531  *
  532  *      Sets the voltage output on a given regulator, if the regulator is not
  533  *      enabled, it will be enabled.
  534  *
  535  *      LOCKING:
  536  *      On entry expects the TWL VREG lock to be held, may upgrade the lock to
  537  *      exclusive but if so it will be downgraded once again before returning.
  538  *
  539  *      RETURNS:
  540  *      Zero on success or an error code on failure.
  541  */
  542 static int
  543 twl_vreg_write_regulator_voltage(struct twl_vreg_softc *sc,
  544     struct twl_regulator_entry *regulator, int millivolts)
  545 {
  546         int err;
  547         uint8_t vsel;
  548         int xlocked;
  549 
  550         TWL_VREG_ASSERT_LOCKED(sc);
  551 
  552         /* If millivolts is zero then we simply disable the output */
  553         if (millivolts == 0)
  554                 return (twl_vreg_disable_regulator(sc, regulator));
  555 
  556         /* If the regulator has a fixed voltage then check the setting matches
  557          * and simply enable.
  558          */
  559         if (regulator->supp_voltages == NULL || regulator->num_supp_voltages == 0) {
  560                 if (millivolts != regulator->fixed_voltage)
  561                         return (EINVAL);
  562 
  563                 return (twl_vreg_enable_regulator(sc, regulator));
  564         }
  565 
  566         /* Get the VSEL value for the given voltage */
  567         err = twl_vreg_millivolt_to_vsel(sc, regulator, millivolts, &vsel);
  568         if (err)
  569                 return (err);
  570 
  571         /* Need to upgrade because writing the voltage and enabling should be atomic */
  572         xlocked = sx_xlocked(&sc->sc_sx);
  573         if (!xlocked)
  574                 TWL_VREG_LOCK_UPGRADE(sc);
  575 
  576         /* Set voltage and enable (atomically) */
  577         err = twl_vreg_write_1(sc, regulator, TWL_VREG_VSEL, (vsel & 0x1f));
  578         if (!err) {
  579                 err = twl_vreg_enable_regulator(sc, regulator);
  580         }
  581 
  582         if (!xlocked)
  583                 TWL_VREG_LOCK_DOWNGRADE(sc);
  584 
  585         if ((twl_vreg_debug > 1) && !err)
  586                 device_printf(sc->sc_dev, "%s : setting voltage to %dmV (vsel: 0x%x)\n",
  587                     regulator->name, millivolts, vsel);
  588 
  589         return (err);
  590 }
  591 
  592 /**
  593  *      twl_vreg_read_regulator_voltage - reads the voltage on a given regulator
  594  *      @sc: the device soft context
  595  *      @regulator: pointer to the regulator structure
  596  *      @millivolts: upon return will contain the voltage on the regulator
  597  *
  598  *      LOCKING:
  599  *      On entry expects the TWL VREG lock to be held. It will upgrade the lock to
  600  *      exclusive if not already, but if so, it will be downgraded again before
  601  *      returning.
  602  *
  603  *      RETURNS:
  604  *      Zero on success, or otherwise an error code.
  605  */
  606 static int
  607 twl_vreg_read_regulator_voltage(struct twl_vreg_softc *sc,
  608     struct twl_regulator_entry *regulator, int *millivolts)
  609 {
  610         int err;
  611         int en = 0;
  612         int xlocked;
  613         uint8_t vsel;
  614 
  615         TWL_VREG_ASSERT_LOCKED(sc);
  616 
  617         /* Need to upgrade the lock because checking enabled state and voltage
  618          * should be atomic.
  619          */
  620         xlocked = sx_xlocked(&sc->sc_sx);
  621         if (!xlocked)
  622                 TWL_VREG_LOCK_UPGRADE(sc);
  623 
  624         /* Check if the regulator is currently enabled */
  625         err = twl_vreg_is_regulator_enabled(sc, regulator, &en);
  626         if (err)
  627                 goto done;
  628 
  629         *millivolts = 0;        
  630         if (!en)
  631                 goto done;
  632 
  633         /* Not all voltages are adjustable */
  634         if (regulator->supp_voltages == NULL || !regulator->num_supp_voltages) {
  635                 *millivolts = regulator->fixed_voltage;
  636                 goto done;
  637         }
  638 
  639         /* For variable voltages read the voltage register */
  640         err = twl_vreg_read_1(sc, regulator, TWL_VREG_VSEL, &vsel);
  641         if (err)
  642                 goto done;
  643 
  644         vsel &= (regulator->num_supp_voltages - 1);
  645         if (regulator->supp_voltages[vsel] == UNDF) {
  646                 err = EINVAL;
  647                 goto done;
  648         }
  649 
  650         *millivolts = regulator->supp_voltages[vsel];
  651 
  652 done:
  653         if (!xlocked)
  654                 TWL_VREG_LOCK_DOWNGRADE(sc);
  655 
  656         if ((twl_vreg_debug > 1) && !err)
  657                 device_printf(sc->sc_dev, "%s : reading voltage is %dmV (vsel: 0x%x)\n",
  658                     regulator->name, *millivolts, vsel);
  659 
  660         return (err);
  661 }
  662 
  663 /**
  664  *      twl_vreg_get_voltage - public interface to read the voltage on a regulator
  665  *      @dev: TWL VREG device
  666  *      @name: the name of the regulator to read the voltage of
  667  *      @millivolts: pointer to an integer that upon return will contain the mV
  668  *
  669  *      If the regulator is disabled the function will set the @millivolts to zero.
  670  *
  671  *      LOCKING:
  672  *      Internally the function takes and releases the TWL VREG lock.
  673  *
  674  *      RETURNS:
  675  *      Zero on success or a negative error code on failure.
  676  */
  677 int
  678 twl_vreg_get_voltage(device_t dev, const char *name, int *millivolts)
  679 {
  680         struct twl_vreg_softc *sc;
  681         struct twl_regulator_entry *regulator;
  682         int err = EINVAL;
  683 
  684         if (millivolts == NULL)
  685                 return (EINVAL);
  686 
  687         sc = device_get_softc(dev);
  688 
  689         TWL_VREG_SLOCK(sc);
  690 
  691         LIST_FOREACH(regulator, &sc->sc_vreg_list, entries) {
  692                 if (strcmp(regulator->name, name) == 0) {
  693                         err = twl_vreg_read_regulator_voltage(sc, regulator, millivolts);
  694                         break;
  695                 }
  696         }
  697 
  698         TWL_VREG_SUNLOCK(sc);
  699 
  700         return (err);
  701 }
  702 
  703 /**
  704  *      twl_vreg_set_voltage - public interface to write the voltage on a regulator
  705  *      @dev: TWL VREG device
  706  *      @name: the name of the regulator to read the voltage of
  707  *      @millivolts: the voltage to set in millivolts
  708  *
  709  *      Sets the output voltage on a given regulator. If the regulator is a fixed
  710  *      voltage reg then the @millivolts value should match the fixed voltage. If
  711  *      a variable regulator then the @millivolt value must fit within the max/min
  712  *      range of the given regulator.
  713  *
  714  *      LOCKING:
  715  *      Internally the function takes and releases the TWL VREG lock.
  716  *
  717  *      RETURNS:
  718  *      Zero on success or a negative error code on failure.
  719  */
  720 int
  721 twl_vreg_set_voltage(device_t dev, const char *name, int millivolts)
  722 {
  723         struct twl_vreg_softc *sc;
  724         struct twl_regulator_entry *regulator;
  725         int err = EINVAL;
  726 
  727         sc = device_get_softc(dev);
  728 
  729         TWL_VREG_SLOCK(sc);
  730 
  731         LIST_FOREACH(regulator, &sc->sc_vreg_list, entries) {
  732                 if (strcmp(regulator->name, name) == 0) {
  733                         err = twl_vreg_write_regulator_voltage(sc, regulator, millivolts);
  734                         break;
  735                 }
  736         }
  737 
  738         TWL_VREG_SUNLOCK(sc);
  739 
  740         return (err);
  741 }
  742 
  743 /**
  744  *      twl_sysctl_voltage - reads or writes the voltage for a regulator
  745  *      @SYSCTL_HANDLER_ARGS: arguments for the callback
  746  *
  747  *      Callback for the sysctl entry for the regulator, simply used to return
  748  *      the voltage on a particular regulator.
  749  *
  750  *      LOCKING:
  751  *      Takes the TWL_VREG shared lock internally.
  752  *
  753  *      RETURNS:
  754  *      Zero on success or an error code on failure.
  755  */
  756 static int
  757 twl_vreg_sysctl_voltage(SYSCTL_HANDLER_ARGS)
  758 {
  759         struct twl_vreg_softc *sc = (struct twl_vreg_softc*)arg1;
  760         struct twl_regulator_entry *regulator;
  761         int voltage;
  762         int found = 0;
  763 
  764         TWL_VREG_SLOCK(sc);
  765 
  766         /* Find the regulator with the matching name */
  767         LIST_FOREACH(regulator, &sc->sc_vreg_list, entries) {
  768                 if (strcmp(regulator->name, oidp->oid_name) == 0) {
  769                         found = 1;
  770                         break;
  771                 }
  772         }
  773 
  774         /* Sanity check that we found the regulator */
  775         if (!found) {
  776                 TWL_VREG_SUNLOCK(sc);
  777                 return (EINVAL);
  778         }
  779 
  780         twl_vreg_read_regulator_voltage(sc, regulator, &voltage);
  781 
  782         TWL_VREG_SUNLOCK(sc);
  783 
  784         return sysctl_handle_int(oidp, &voltage, 0, req);
  785 }
  786 
  787 /**
  788  *      twl_add_regulator - adds single voltage regulator sysctls for the device
  789  *      @sc: device soft context
  790  *      @name: the name of the regulator
  791  *      @nsub: the number of the subdevice
  792  *      @regbase: the base address of the voltage regulator registers
  793  *      @fixed_voltage: if a fixed voltage regulator this defines it's voltage
  794  *      @voltages: if a variable voltage regulator, an array of possible voltages
  795  *      @num_voltages: the number of entries @voltages
  796  *
  797  *      Adds a voltage regulator to the device and also a sysctl interface for the
  798  *      regulator.
  799  *
  800  *      LOCKING:
  801  *      The TWL_VEG exclusive lock must be held while this function is called.
  802  *
  803  *      RETURNS:
  804  *      Pointer to the new regulator entry on success, otherwise on failure NULL.
  805  */
  806 static struct twl_regulator_entry*
  807 twl_vreg_add_regulator(struct twl_vreg_softc *sc, const char *name,
  808         uint8_t nsub, uint8_t regbase, uint16_t fixed_voltage,
  809         const uint16_t *voltages, uint32_t num_voltages)
  810 {
  811         struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
  812         struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
  813         struct twl_regulator_entry *new;
  814 
  815         new = malloc(sizeof(struct twl_regulator_entry), M_DEVBUF, M_NOWAIT | M_ZERO);
  816         if (new == NULL)
  817                 return (NULL);
  818 
  819         strncpy(new->name, name, TWL_VREG_MAX_NAMELEN);
  820         new->name[TWL_VREG_MAX_NAMELEN - 1] = '\0';
  821 
  822         new->sub_dev = nsub;
  823         new->reg_off = regbase;
  824 
  825         new->fixed_voltage = fixed_voltage;
  826 
  827         new->supp_voltages = voltages;
  828         new->num_supp_voltages = num_voltages;
  829 
  830         /* Add a sysctl entry for the voltage */
  831         new->oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, name,
  832             CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0,
  833             twl_vreg_sysctl_voltage, "I", "voltage regulator");
  834 
  835         /* Finally add the regulator to list of supported regulators */
  836         LIST_INSERT_HEAD(&sc->sc_vreg_list, new, entries);
  837 
  838         return (new);
  839 }
  840 
  841 /**
  842  *      twl_vreg_add_regulators - adds any voltage regulators to the device
  843  *      @sc: device soft context
  844  *      @chip: the name of the chip used in the hints
  845  *      @regulators: the list of possible voltage regulators
  846  *
  847  *      Loops over the list of regulators and matches up with the FDT values,
  848  *      adjusting the actual voltage based on the supplied values.
  849  *
  850  *      LOCKING:
  851  *      The TWL_VEG exclusive lock must be held while this function is called.
  852  *
  853  *      RETURNS:
  854  *      Always returns 0.
  855  */
  856 static int
  857 twl_vreg_add_regulators(struct twl_vreg_softc *sc,
  858         const struct twl_regulator *regulators)
  859 {
  860         int err;
  861         int millivolts;
  862         const struct twl_regulator *walker;
  863         struct twl_regulator_entry *entry;
  864         phandle_t child;
  865         char rnames[256];
  866         char *name, *voltage;
  867         int len = 0, prop_len;
  868 
  869         /* Add the regulators from the list */
  870         walker = &regulators[0];
  871         while (walker->name != NULL) {
  872                 /* Add the regulator to the list */
  873                 entry = twl_vreg_add_regulator(sc, walker->name, walker->subdev,
  874                     walker->regbase, walker->fixedvoltage,
  875                     walker->voltages, walker->num_voltages);
  876                 if (entry == NULL)
  877                         continue;
  878 
  879                 walker++;
  880         }
  881 
  882         /* Check if the FDT is telling us to set any voltages */
  883         child = ofw_bus_get_node(sc->sc_pdev);
  884         if (child) {
  885                 prop_len = OF_getprop(child, "voltage-regulators", rnames, sizeof(rnames));
  886                 while (len < prop_len) {
  887                         name = rnames + len;
  888                         len += strlen(name) + 1;
  889                         if ((len >= prop_len) || (name[0] == '\0'))
  890                                 break;
  891                         
  892                         voltage = rnames + len;
  893                         len += strlen(voltage) + 1;
  894                         if (voltage[0] == '\0')
  895                                 break;
  896                         
  897                         millivolts = strtoul(voltage, NULL, 0);
  898                         
  899                         LIST_FOREACH(entry, &sc->sc_vreg_list, entries) {
  900                                 if (strcmp(entry->name, name) == 0) {
  901                                         twl_vreg_write_regulator_voltage(sc, entry, millivolts);
  902                                         break;
  903                                 }
  904                         }
  905                 }
  906         }
  907 
  908         if (twl_vreg_debug) {
  909                 LIST_FOREACH(entry, &sc->sc_vreg_list, entries) {
  910                         err = twl_vreg_read_regulator_voltage(sc, entry, &millivolts);
  911                         if (!err)
  912                                 device_printf(sc->sc_dev, "%s : %d mV\n", entry->name, millivolts);
  913                 }
  914         }
  915 
  916         return (0);
  917 }
  918 
  919 /**
  920  *      twl_vreg_init - initialises the list of regulators
  921  *      @dev: the twl_vreg device
  922  *
  923  *      This function is called as an intrhook once interrupts have been enabled,
  924  *      this is done so that the driver has the option to enable/disable or set
  925  *      the voltage level based on settings providied in the FDT.
  926  *
  927  *      LOCKING:
  928  *      Takes the exclusive lock in the function.
  929  */
  930 static void
  931 twl_vreg_init(void *dev)
  932 {
  933         struct twl_vreg_softc *sc;
  934 
  935         sc = device_get_softc((device_t)dev);
  936 
  937         TWL_VREG_XLOCK(sc);
  938 
  939         if (twl_is_4030(sc->sc_pdev))
  940                 twl_vreg_add_regulators(sc, twl4030_regulators);
  941         else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev))
  942                 twl_vreg_add_regulators(sc, twl6030_regulators);
  943 
  944         TWL_VREG_XUNLOCK(sc);
  945 
  946         config_intrhook_disestablish(&sc->sc_init_hook);
  947 }
  948 
  949 static int
  950 twl_vreg_probe(device_t dev)
  951 {
  952         if (twl_is_4030(device_get_parent(dev)))
  953                 device_set_desc(dev, "TI TWL4030 PMIC Voltage Regulators");
  954         else if (twl_is_6025(device_get_parent(dev)) ||
  955                  twl_is_6030(device_get_parent(dev)))
  956                 device_set_desc(dev, "TI TWL6025/TWL6030 PMIC Voltage Regulators");
  957         else
  958                 return (ENXIO);
  959 
  960         return (0);
  961 }
  962 
  963 static int
  964 twl_vreg_attach(device_t dev)
  965 {
  966         struct twl_vreg_softc *sc;
  967 
  968         sc = device_get_softc(dev);
  969         sc->sc_dev = dev;
  970         sc->sc_pdev = device_get_parent(dev);
  971 
  972         TWL_VREG_LOCK_INIT(sc);
  973 
  974         LIST_INIT(&sc->sc_vreg_list);
  975 
  976         /* We have to wait until interrupts are enabled. I2C read and write
  977          * only works if the interrupts are available.
  978          */
  979         sc->sc_init_hook.ich_func = twl_vreg_init;
  980         sc->sc_init_hook.ich_arg = dev;
  981 
  982         if (config_intrhook_establish(&sc->sc_init_hook) != 0)
  983                 return (ENOMEM);
  984 
  985         return (0);
  986 }
  987 
  988 static int
  989 twl_vreg_detach(device_t dev)
  990 {
  991         struct twl_vreg_softc *sc;
  992         struct twl_regulator_entry *regulator;
  993         struct twl_regulator_entry *tmp;
  994 
  995         sc = device_get_softc(dev);
  996 
  997         /* Take the lock and free all the added regulators */
  998         TWL_VREG_XLOCK(sc);
  999 
 1000         LIST_FOREACH_SAFE(regulator, &sc->sc_vreg_list, entries, tmp) {
 1001                 LIST_REMOVE(regulator, entries);
 1002                 sysctl_remove_oid(regulator->oid, 1, 0);
 1003                 free(regulator, M_DEVBUF);
 1004         }
 1005 
 1006         TWL_VREG_XUNLOCK(sc);
 1007 
 1008         TWL_VREG_LOCK_DESTROY(sc);
 1009 
 1010         return (0);
 1011 }
 1012 
 1013 static device_method_t twl_vreg_methods[] = {
 1014         DEVMETHOD(device_probe,         twl_vreg_probe),
 1015         DEVMETHOD(device_attach,        twl_vreg_attach),
 1016         DEVMETHOD(device_detach,        twl_vreg_detach),
 1017 
 1018         {0, 0},
 1019 };
 1020 
 1021 static driver_t twl_vreg_driver = {
 1022         "twl_vreg",
 1023         twl_vreg_methods,
 1024         sizeof(struct twl_vreg_softc),
 1025 };
 1026 
 1027 DRIVER_MODULE(twl_vreg, twl, twl_vreg_driver, 0, 0);
 1028 MODULE_VERSION(twl_vreg, 1);

Cache object: 878dfb24a6d7fbfa7cfab8e02c91e98b


[ 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.