1239281Sgonzo/*- 2239281Sgonzo * Copyright (c) 2011 3239281Sgonzo * Ben Gray <ben.r.gray@gmail.com>. 4239281Sgonzo * All rights reserved. 5239281Sgonzo * 6239281Sgonzo * Redistribution and use in source and binary forms, with or without 7239281Sgonzo * modification, are permitted provided that the following conditions 8239281Sgonzo * are met: 9239281Sgonzo * 1. Redistributions of source code must retain the above copyright 10239281Sgonzo * notice, this list of conditions and the following disclaimer. 11239281Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 12239281Sgonzo * notice, this list of conditions and the following disclaimer in the 13239281Sgonzo * documentation and/or other materials provided with the distribution. 14239281Sgonzo * 15239281Sgonzo * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16239281Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17239281Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18239281Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 19239281Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20239281Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21239281Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22239281Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23239281Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24239281Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25239281Sgonzo * SUCH DAMAGE. 26239281Sgonzo */ 27239281Sgonzo 28239281Sgonzo#include <sys/cdefs.h> 29239281Sgonzo__FBSDID("$FreeBSD$"); 30239281Sgonzo 31239281Sgonzo/* 32239281Sgonzo * Texas Instruments TWL4030/TWL5030/TWL60x0/TPS659x0 Power Management. 33239281Sgonzo * 34239281Sgonzo * This driver covers the voltages regulators (LDO), allows for enabling & 35239281Sgonzo * disabling the voltage output and adjusting the voltage level. 36239281Sgonzo * 37239281Sgonzo * Voltage regulators can belong to different power groups, in this driver we 38239281Sgonzo * put the regulators under our control in the "Application power group". 39239281Sgonzo * 40239281Sgonzo * 41239281Sgonzo * FLATTENED DEVICE TREE (FDT) 42239281Sgonzo * Startup override settings can be specified in the FDT, if they are they 43239281Sgonzo * should be under the twl parent device and take the following form: 44239281Sgonzo * 45239281Sgonzo * voltage-regulators = "name1", "millivolts1", 46239281Sgonzo * "name2", "millivolts2"; 47239281Sgonzo * 48239281Sgonzo * Each override should be a pair, the first entry is the name of the regulator 49239281Sgonzo * the second is the voltage (in millivolts) to set for the given regulator. 50239281Sgonzo * 51239281Sgonzo */ 52239281Sgonzo 53239281Sgonzo#include <sys/param.h> 54239281Sgonzo#include <sys/systm.h> 55239281Sgonzo#include <sys/kernel.h> 56239281Sgonzo#include <sys/lock.h> 57239281Sgonzo#include <sys/module.h> 58239281Sgonzo#include <sys/bus.h> 59239281Sgonzo#include <sys/resource.h> 60239281Sgonzo#include <sys/rman.h> 61239281Sgonzo#include <sys/sysctl.h> 62239281Sgonzo#include <sys/sx.h> 63239281Sgonzo#include <sys/malloc.h> 64239281Sgonzo 65239281Sgonzo#include <machine/bus.h> 66239281Sgonzo#include <machine/cpu.h> 67239281Sgonzo#include <machine/cpufunc.h> 68239281Sgonzo#include <machine/resource.h> 69239281Sgonzo#include <machine/intr.h> 70239281Sgonzo 71239281Sgonzo#include <dev/ofw/openfirm.h> 72239281Sgonzo#include <dev/ofw/ofw_bus.h> 73239281Sgonzo 74239281Sgonzo#include "twl.h" 75239281Sgonzo#include "twl_vreg.h" 76239281Sgonzo 77239281Sgonzostatic int twl_vreg_debug = 1; 78239281Sgonzo 79239281Sgonzo 80239281Sgonzo/* 81239281Sgonzo * Power Groups bits for the 4030 and 6030 devices 82239281Sgonzo */ 83239281Sgonzo#define TWL4030_P3_GRP 0x80 /* Peripherals, power group */ 84239281Sgonzo#define TWL4030_P2_GRP 0x40 /* Modem power group */ 85239281Sgonzo#define TWL4030_P1_GRP 0x20 /* Application power group (FreeBSD control) */ 86239281Sgonzo 87239281Sgonzo#define TWL6030_P3_GRP 0x04 /* Modem power group */ 88239281Sgonzo#define TWL6030_P2_GRP 0x02 /* Connectivity power group */ 89239281Sgonzo#define TWL6030_P1_GRP 0x01 /* Application power group (FreeBSD control) */ 90239281Sgonzo 91239281Sgonzo/* 92239281Sgonzo * Register offsets within a LDO regulator register set 93239281Sgonzo */ 94239281Sgonzo#define TWL_VREG_GRP 0x00 /* Regulator GRP register */ 95239281Sgonzo#define TWL_VREG_STATE 0x02 96239281Sgonzo#define TWL_VREG_VSEL 0x03 /* Voltage select register */ 97239281Sgonzo 98239281Sgonzo#define UNDF 0xFFFF 99239281Sgonzo 100239281Sgonzostatic const uint16_t twl6030_voltages[] = { 101239281Sgonzo 0000, 1000, 1100, 1200, 1300, 1400, 1500, 1600, 102239281Sgonzo 1700, 1800, 1900, 2000, 2100, 2200, 2300, 2400, 103239281Sgonzo 2500, 2600, 2700, 2800, 2900, 3000, 3100, 3200, 104239281Sgonzo 3300, UNDF, UNDF, UNDF, UNDF, UNDF, UNDF, 2750 105239281Sgonzo}; 106239281Sgonzo 107239281Sgonzostatic const uint16_t twl4030_vaux1_voltages[] = { 108239281Sgonzo 1500, 1800, 2500, 2800, 3000, 3000, 3000, 3000 109239281Sgonzo}; 110239281Sgonzostatic const uint16_t twl4030_vaux2_voltages[] = { 111239281Sgonzo 1700, 1700, 1900, 1300, 1500, 1800, 2000, 2500, 112239281Sgonzo 2100, 2800, 2200, 2300, 2400, 2400, 2400, 2400 113239281Sgonzo}; 114239281Sgonzostatic const uint16_t twl4030_vaux3_voltages[] = { 115239281Sgonzo 1500, 1800, 2500, 2800, 3000, 3000, 3000, 3000 116239281Sgonzo}; 117239281Sgonzostatic const uint16_t twl4030_vaux4_voltages[] = { 118239281Sgonzo 700, 1000, 1200, 1300, 1500, 1800, 1850, 2500, 119239281Sgonzo 2600, 2800, 2850, 3000, 3150, 3150, 3150, 3150 120239281Sgonzo}; 121239281Sgonzostatic const uint16_t twl4030_vmmc1_voltages[] = { 122239281Sgonzo 1850, 2850, 3000, 3150 123239281Sgonzo}; 124239281Sgonzostatic const uint16_t twl4030_vmmc2_voltages[] = { 125239281Sgonzo 1000, 1000, 1200, 1300, 1500, 1800, 1850, 2500, 126239281Sgonzo 2600, 2800, 2850, 3000, 3150, 3150, 3150, 3150 127239281Sgonzo}; 128239281Sgonzostatic const uint16_t twl4030_vpll1_voltages[] = { 129239281Sgonzo 1000, 1200, 1300, 1800, 2800, 3000, 3000, 3000 130239281Sgonzo}; 131239281Sgonzostatic const uint16_t twl4030_vpll2_voltages[] = { 132239281Sgonzo 700, 1000, 1200, 1300, 1500, 1800, 1850, 2500, 133239281Sgonzo 2600, 2800, 2850, 3000, 3150, 3150, 3150, 3150 134239281Sgonzo}; 135239281Sgonzostatic const uint16_t twl4030_vsim_voltages[] = { 136239281Sgonzo 1000, 1200, 1300, 1800, 2800, 3000, 3000, 3000 137239281Sgonzo}; 138239281Sgonzostatic const uint16_t twl4030_vdac_voltages[] = { 139239281Sgonzo 1200, 1300, 1800, 1800 140239281Sgonzo}; 141263456Sdim#if 0 /* vdd1, vdd2, vdio, not currently used. */ 142239281Sgonzostatic const uint16_t twl4030_vdd1_voltages[] = { 143239281Sgonzo 800, 1450 144239281Sgonzo}; 145239281Sgonzostatic const uint16_t twl4030_vdd2_voltages[] = { 146239281Sgonzo 800, 1450, 1500 147239281Sgonzo}; 148239281Sgonzostatic const uint16_t twl4030_vio_voltages[] = { 149239281Sgonzo 1800, 1850 150239281Sgonzo}; 151263456Sdim#endif 152239281Sgonzostatic const uint16_t twl4030_vintana2_voltages[] = { 153239281Sgonzo 2500, 2750 154239281Sgonzo}; 155239281Sgonzo 156239281Sgonzo/** 157239281Sgonzo * Support voltage regulators for the different IC's 158239281Sgonzo */ 159239281Sgonzostruct twl_regulator { 160239281Sgonzo const char *name; 161239281Sgonzo uint8_t subdev; 162239281Sgonzo uint8_t regbase; 163239281Sgonzo 164239281Sgonzo uint16_t fixedvoltage; 165239281Sgonzo 166239281Sgonzo const uint16_t *voltages; 167239281Sgonzo uint32_t num_voltages; 168239281Sgonzo}; 169239281Sgonzo 170239281Sgonzo#define TWL_REGULATOR_ADJUSTABLE(name, subdev, reg, voltages) \ 171239281Sgonzo { name, subdev, reg, 0, voltages, (sizeof(voltages)/sizeof(voltages[0])) } 172239281Sgonzo#define TWL_REGULATOR_FIXED(name, subdev, reg, voltage) \ 173239281Sgonzo { name, subdev, reg, voltage, NULL, 0 } 174239281Sgonzo 175239281Sgonzostatic const struct twl_regulator twl4030_regulators[] = { 176239281Sgonzo TWL_REGULATOR_ADJUSTABLE("vaux1", 0, 0x17, twl4030_vaux1_voltages), 177239281Sgonzo TWL_REGULATOR_ADJUSTABLE("vaux2", 0, 0x1B, twl4030_vaux2_voltages), 178239281Sgonzo TWL_REGULATOR_ADJUSTABLE("vaux3", 0, 0x1F, twl4030_vaux3_voltages), 179239281Sgonzo TWL_REGULATOR_ADJUSTABLE("vaux4", 0, 0x23, twl4030_vaux4_voltages), 180239281Sgonzo TWL_REGULATOR_ADJUSTABLE("vmmc1", 0, 0x27, twl4030_vmmc1_voltages), 181239281Sgonzo TWL_REGULATOR_ADJUSTABLE("vmmc2", 0, 0x2B, twl4030_vmmc2_voltages), 182239281Sgonzo TWL_REGULATOR_ADJUSTABLE("vpll1", 0, 0x2F, twl4030_vpll1_voltages), 183239281Sgonzo TWL_REGULATOR_ADJUSTABLE("vpll2", 0, 0x33, twl4030_vpll2_voltages), 184239281Sgonzo TWL_REGULATOR_ADJUSTABLE("vsim", 0, 0x37, twl4030_vsim_voltages), 185239281Sgonzo TWL_REGULATOR_ADJUSTABLE("vdac", 0, 0x3B, twl4030_vdac_voltages), 186239281Sgonzo TWL_REGULATOR_ADJUSTABLE("vintana2", 0, 0x43, twl4030_vintana2_voltages), 187239281Sgonzo TWL_REGULATOR_FIXED("vintana1", 0, 0x3F, 1500), 188239281Sgonzo TWL_REGULATOR_FIXED("vintdig", 0, 0x47, 1500), 189239281Sgonzo TWL_REGULATOR_FIXED("vusb1v5", 0, 0x71, 1500), 190239281Sgonzo TWL_REGULATOR_FIXED("vusb1v8", 0, 0x74, 1800), 191239281Sgonzo TWL_REGULATOR_FIXED("vusb3v1", 0, 0x77, 3100), 192239281Sgonzo { NULL, 0, 0x00, 0, NULL, 0 } 193239281Sgonzo}; 194239281Sgonzo 195239281Sgonzostatic const struct twl_regulator twl6030_regulators[] = { 196239281Sgonzo TWL_REGULATOR_ADJUSTABLE("vaux1", 0, 0x84, twl6030_voltages), 197239281Sgonzo TWL_REGULATOR_ADJUSTABLE("vaux2", 0, 0x89, twl6030_voltages), 198239281Sgonzo TWL_REGULATOR_ADJUSTABLE("vaux3", 0, 0x8C, twl6030_voltages), 199239281Sgonzo TWL_REGULATOR_ADJUSTABLE("vmmc", 0, 0x98, twl6030_voltages), 200239281Sgonzo TWL_REGULATOR_ADJUSTABLE("vpp", 0, 0x9C, twl6030_voltages), 201239281Sgonzo TWL_REGULATOR_ADJUSTABLE("vusim", 0, 0xA4, twl6030_voltages), 202239281Sgonzo TWL_REGULATOR_FIXED("vmem", 0, 0x64, 1800), 203239281Sgonzo TWL_REGULATOR_FIXED("vusb", 0, 0xA0, 3300), 204239281Sgonzo TWL_REGULATOR_FIXED("v1v8", 0, 0x46, 1800), 205239281Sgonzo TWL_REGULATOR_FIXED("v2v1", 0, 0x4C, 2100), 206239281Sgonzo TWL_REGULATOR_FIXED("v1v29", 0, 0x40, 1290), 207239281Sgonzo TWL_REGULATOR_FIXED("vcxio", 0, 0x90, 1800), 208239281Sgonzo TWL_REGULATOR_FIXED("vdac", 0, 0x94, 1800), 209239281Sgonzo TWL_REGULATOR_FIXED("vana", 0, 0x80, 2100), 210239281Sgonzo { NULL, 0, 0x00, 0, NULL, 0 } 211239281Sgonzo}; 212239281Sgonzo 213239281Sgonzo#define TWL_VREG_MAX_NAMELEN 32 214239281Sgonzo 215239281Sgonzostruct twl_regulator_entry { 216239281Sgonzo LIST_ENTRY(twl_regulator_entry) entries; 217239281Sgonzo char name[TWL_VREG_MAX_NAMELEN]; 218239281Sgonzo struct sysctl_oid *oid; 219239281Sgonzo uint8_t sub_dev; /* TWL sub-device group */ 220239281Sgonzo uint8_t reg_off; /* base register offset for the LDO */ 221239281Sgonzo uint16_t fixed_voltage; /* the (milli)voltage if LDO is fixed */ 222239281Sgonzo const uint16_t *supp_voltages; /* pointer to an array of possible voltages */ 223239281Sgonzo uint32_t num_supp_voltages; /* the number of supplied voltages */ 224239281Sgonzo}; 225239281Sgonzo 226239281Sgonzostruct twl_vreg_softc { 227239281Sgonzo device_t sc_dev; 228239281Sgonzo device_t sc_pdev; 229239281Sgonzo struct sx sc_sx; 230239281Sgonzo 231239281Sgonzo struct intr_config_hook sc_init_hook; 232239281Sgonzo LIST_HEAD(twl_regulator_list, twl_regulator_entry) sc_vreg_list; 233239281Sgonzo}; 234239281Sgonzo 235239281Sgonzo 236239281Sgonzo#define TWL_VREG_XLOCK(_sc) sx_xlock(&(_sc)->sc_sx) 237239281Sgonzo#define TWL_VREG_XUNLOCK(_sc) sx_xunlock(&(_sc)->sc_sx) 238239281Sgonzo#define TWL_VREG_SLOCK(_sc) sx_slock(&(_sc)->sc_sx) 239239281Sgonzo#define TWL_VREG_SUNLOCK(_sc) sx_sunlock(&(_sc)->sc_sx) 240239281Sgonzo#define TWL_VREG_LOCK_INIT(_sc) sx_init(&(_sc)->sc_sx, "twl_vreg") 241239281Sgonzo#define TWL_VREG_LOCK_DESTROY(_sc) sx_destroy(&(_sc)->sc_sx); 242239281Sgonzo 243239281Sgonzo#define TWL_VREG_ASSERT_LOCKED(_sc) sx_assert(&(_sc)->sc_sx, SA_LOCKED); 244239281Sgonzo 245239281Sgonzo#define TWL_VREG_LOCK_UPGRADE(_sc) \ 246239281Sgonzo do { \ 247239281Sgonzo while (!sx_try_upgrade(&(_sc)->sc_sx)) \ 248239281Sgonzo pause("twl_vreg_ex", (hz / 100)); \ 249239281Sgonzo } while(0) 250239281Sgonzo#define TWL_VREG_LOCK_DOWNGRADE(_sc) sx_downgrade(&(_sc)->sc_sx); 251239281Sgonzo 252239281Sgonzo 253239281Sgonzo 254239281Sgonzo 255239281Sgonzo/** 256239281Sgonzo * twl_vreg_read_1 - read single register from the TWL device 257239281Sgonzo * twl_vreg_write_1 - write a single register in the TWL device 258239281Sgonzo * @sc: device context 259239281Sgonzo * @clk: the clock device we're reading from / writing to 260239281Sgonzo * @off: offset within the clock's register set 261239281Sgonzo * @val: the value to write or a pointer to a variable to store the result 262239281Sgonzo * 263239281Sgonzo * RETURNS: 264239281Sgonzo * Zero on success or an error code on failure. 265239281Sgonzo */ 266239281Sgonzostatic inline int 267239281Sgonzotwl_vreg_read_1(struct twl_vreg_softc *sc, struct twl_regulator_entry *regulator, 268239281Sgonzo uint8_t off, uint8_t *val) 269239281Sgonzo{ 270239281Sgonzo return (twl_read(sc->sc_pdev, regulator->sub_dev, 271239281Sgonzo regulator->reg_off + off, val, 1)); 272239281Sgonzo} 273239281Sgonzo 274239281Sgonzostatic inline int 275239281Sgonzotwl_vreg_write_1(struct twl_vreg_softc *sc, struct twl_regulator_entry *regulator, 276239281Sgonzo uint8_t off, uint8_t val) 277239281Sgonzo{ 278239281Sgonzo return (twl_write(sc->sc_pdev, regulator->sub_dev, 279239281Sgonzo regulator->reg_off + off, &val, 1)); 280239281Sgonzo} 281239281Sgonzo 282239281Sgonzo/** 283239281Sgonzo * twl_millivolt_to_vsel - gets the vsel bit value to write into the register 284239281Sgonzo * for a desired voltage and regulator 285239281Sgonzo * @sc: the device soft context 286239281Sgonzo * @regulator: pointer to the regulator device 287239281Sgonzo * @millivolts: the millivolts to find the bit value for 288239281Sgonzo * @vsel: upon return will contain the corresponding register value 289239281Sgonzo * 290239281Sgonzo * Accepts a (milli)voltage value and tries to find the closest match to the 291239281Sgonzo * actual supported voltages for the given regulator. If a match is found 292239281Sgonzo * within 100mv of the target, @vsel is written with the match and 0 is 293239281Sgonzo * returned. If no voltage match is found the function returns an non-zero 294239281Sgonzo * value. 295239281Sgonzo * 296239281Sgonzo * RETURNS: 297239281Sgonzo * Zero on success or an error code on failure. 298239281Sgonzo */ 299239281Sgonzostatic int 300239281Sgonzotwl_vreg_millivolt_to_vsel(struct twl_vreg_softc *sc, 301239281Sgonzo struct twl_regulator_entry *regulator, int millivolts, uint8_t *vsel) 302239281Sgonzo{ 303239281Sgonzo int delta, smallest_delta; 304239281Sgonzo unsigned i, closest_idx; 305239281Sgonzo 306239281Sgonzo TWL_VREG_ASSERT_LOCKED(sc); 307239281Sgonzo 308239281Sgonzo if (regulator->supp_voltages == NULL) 309239281Sgonzo return (EINVAL); 310239281Sgonzo 311239281Sgonzo /* Loop over the support voltages and try and find the closest match */ 312239281Sgonzo closest_idx = 0; 313239281Sgonzo smallest_delta = 0x7fffffff; 314239281Sgonzo for (i = 0; i < regulator->num_supp_voltages; i++) { 315239281Sgonzo 316239281Sgonzo /* Ignore undefined values */ 317239281Sgonzo if (regulator->supp_voltages[i] == UNDF) 318239281Sgonzo continue; 319239281Sgonzo 320239281Sgonzo /* Calculate the difference */ 321239281Sgonzo delta = millivolts - (int)regulator->supp_voltages[i]; 322239281Sgonzo if (abs(delta) < smallest_delta) { 323239281Sgonzo smallest_delta = abs(delta); 324239281Sgonzo closest_idx = i; 325239281Sgonzo } 326239281Sgonzo } 327239281Sgonzo 328239281Sgonzo /* Check we got a voltage that was within 100mv of the actual target, this 329239281Sgonzo * is just a value I picked out of thin air. 330239281Sgonzo */ 331239281Sgonzo if ((smallest_delta > 100) && (closest_idx < 0x100)) 332239281Sgonzo return (EINVAL); 333239281Sgonzo 334239281Sgonzo *vsel = closest_idx; 335239281Sgonzo return (0); 336239281Sgonzo} 337239281Sgonzo 338239281Sgonzo/** 339239281Sgonzo * twl_vreg_is_regulator_enabled - returns the enabled status of the regulator 340239281Sgonzo * @sc: the device soft context 341239281Sgonzo * @regulator: pointer to the regulator device 342239281Sgonzo * @enabled: stores the enabled status, zero disabled, non-zero enabled 343239281Sgonzo * 344239281Sgonzo * LOCKING: 345239281Sgonzo * On entry expects the TWL VREG lock to be held. Will upgrade the lock to 346239281Sgonzo * exclusive if not already but, if so, it will be downgraded again before 347239281Sgonzo * returning. 348239281Sgonzo * 349239281Sgonzo * RETURNS: 350239281Sgonzo * Zero on success or an error code on failure. 351239281Sgonzo */ 352239281Sgonzostatic int 353239281Sgonzotwl_vreg_is_regulator_enabled(struct twl_vreg_softc *sc, 354239281Sgonzo struct twl_regulator_entry *regulator, int *enabled) 355239281Sgonzo{ 356239281Sgonzo int err; 357239281Sgonzo uint8_t grp; 358239281Sgonzo uint8_t state; 359239281Sgonzo int xlocked; 360239281Sgonzo 361239281Sgonzo if (enabled == NULL) 362239281Sgonzo return (EINVAL); 363239281Sgonzo 364239281Sgonzo TWL_VREG_ASSERT_LOCKED(sc); 365239281Sgonzo 366239281Sgonzo xlocked = sx_xlocked(&sc->sc_sx); 367239281Sgonzo if (!xlocked) 368239281Sgonzo TWL_VREG_LOCK_UPGRADE(sc); 369239281Sgonzo 370239281Sgonzo /* The status reading is different for the different devices */ 371239281Sgonzo if (twl_is_4030(sc->sc_pdev)) { 372239281Sgonzo 373239281Sgonzo err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &state); 374239281Sgonzo if (err) 375239281Sgonzo goto done; 376239281Sgonzo 377239281Sgonzo *enabled = (state & TWL4030_P1_GRP); 378239281Sgonzo 379239281Sgonzo } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) { 380239281Sgonzo 381239281Sgonzo /* Check the regulator is in the application group */ 382239281Sgonzo if (twl_is_6030(sc->sc_pdev)) { 383239281Sgonzo err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &grp); 384239281Sgonzo if (err) 385239281Sgonzo goto done; 386239281Sgonzo 387239281Sgonzo if (!(grp & TWL6030_P1_GRP)) { 388239281Sgonzo *enabled = 0; /* disabled */ 389239281Sgonzo goto done; 390239281Sgonzo } 391239281Sgonzo } 392239281Sgonzo 393239281Sgonzo /* Read the application mode state and verify it's ON */ 394239281Sgonzo err = twl_vreg_read_1(sc, regulator, TWL_VREG_STATE, &state); 395239281Sgonzo if (err) 396239281Sgonzo goto done; 397239281Sgonzo 398239281Sgonzo *enabled = ((state & 0x0C) == 0x04); 399239281Sgonzo 400239281Sgonzo } else { 401239281Sgonzo err = EINVAL; 402239281Sgonzo } 403239281Sgonzo 404239281Sgonzodone: 405239281Sgonzo if (!xlocked) 406239281Sgonzo TWL_VREG_LOCK_DOWNGRADE(sc); 407239281Sgonzo 408239281Sgonzo return (err); 409239281Sgonzo} 410239281Sgonzo 411239281Sgonzo/** 412239281Sgonzo * twl_vreg_disable_regulator - disables a voltage regulator 413239281Sgonzo * @sc: the device soft context 414239281Sgonzo * @regulator: pointer to the regulator device 415239281Sgonzo * 416239281Sgonzo * Disables the regulator which will stop the output drivers. 417239281Sgonzo * 418239281Sgonzo * LOCKING: 419239281Sgonzo * On entry expects the TWL VREG lock to be held. Will upgrade the lock to 420239281Sgonzo * exclusive if not already but, if so, it will be downgraded again before 421239281Sgonzo * returning. 422239281Sgonzo * 423239281Sgonzo * RETURNS: 424239281Sgonzo * Zero on success or a positive error code on failure. 425239281Sgonzo */ 426239281Sgonzostatic int 427239281Sgonzotwl_vreg_disable_regulator(struct twl_vreg_softc *sc, 428239281Sgonzo struct twl_regulator_entry *regulator) 429239281Sgonzo{ 430239281Sgonzo int err = 0; 431239281Sgonzo uint8_t grp; 432239281Sgonzo int xlocked; 433239281Sgonzo 434239281Sgonzo TWL_VREG_ASSERT_LOCKED(sc); 435239281Sgonzo 436239281Sgonzo xlocked = sx_xlocked(&sc->sc_sx); 437239281Sgonzo if (!xlocked) 438239281Sgonzo TWL_VREG_LOCK_UPGRADE(sc); 439239281Sgonzo 440239281Sgonzo if (twl_is_4030(sc->sc_pdev)) { 441239281Sgonzo 442239281Sgonzo /* Read the regulator CFG_GRP register */ 443239281Sgonzo err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &grp); 444239281Sgonzo if (err) 445239281Sgonzo goto done; 446239281Sgonzo 447239281Sgonzo /* On the TWL4030 we just need to remove the regulator from all the 448239281Sgonzo * power groups. 449239281Sgonzo */ 450239281Sgonzo grp &= ~(TWL4030_P1_GRP | TWL4030_P2_GRP | TWL4030_P3_GRP); 451239281Sgonzo err = twl_vreg_write_1(sc, regulator, TWL_VREG_GRP, grp); 452239281Sgonzo 453239281Sgonzo } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) { 454239281Sgonzo 455239281Sgonzo /* On TWL6030 we need to make sure we disable power for all groups */ 456239281Sgonzo if (twl_is_6030(sc->sc_pdev)) 457239281Sgonzo grp = TWL6030_P1_GRP | TWL6030_P2_GRP | TWL6030_P3_GRP; 458239281Sgonzo else 459239281Sgonzo grp = 0x00; 460239281Sgonzo 461239281Sgonzo /* Write the resource state to "OFF" */ 462239281Sgonzo err = twl_vreg_write_1(sc, regulator, TWL_VREG_STATE, (grp << 5)); 463239281Sgonzo } 464239281Sgonzo 465239281Sgonzodone: 466239281Sgonzo if (!xlocked) 467239281Sgonzo TWL_VREG_LOCK_DOWNGRADE(sc); 468239281Sgonzo 469239281Sgonzo return (err); 470239281Sgonzo} 471239281Sgonzo 472239281Sgonzo/** 473239281Sgonzo * twl_vreg_enable_regulator - enables the voltage regulator 474239281Sgonzo * @sc: the device soft context 475239281Sgonzo * @regulator: pointer to the regulator device 476239281Sgonzo * 477239281Sgonzo * Enables the regulator which will enable the voltage out at the currently 478239281Sgonzo * set voltage. Set the voltage before calling this function to avoid 479239281Sgonzo * driving the voltage too high/low by mistake. 480239281Sgonzo * 481239281Sgonzo * LOCKING: 482239281Sgonzo * On entry expects the TWL VREG lock to be held. Will upgrade the lock to 483239281Sgonzo * exclusive if not already but, if so, it will be downgraded again before 484239281Sgonzo * returning. 485239281Sgonzo * 486239281Sgonzo * RETURNS: 487239281Sgonzo * Zero on success or a positive error code on failure. 488239281Sgonzo */ 489239281Sgonzostatic int 490239281Sgonzotwl_vreg_enable_regulator(struct twl_vreg_softc *sc, 491239281Sgonzo struct twl_regulator_entry *regulator) 492239281Sgonzo{ 493239281Sgonzo int err; 494239281Sgonzo uint8_t grp; 495239281Sgonzo int xlocked; 496239281Sgonzo 497239281Sgonzo TWL_VREG_ASSERT_LOCKED(sc); 498239281Sgonzo 499239281Sgonzo xlocked = sx_xlocked(&sc->sc_sx); 500239281Sgonzo if (!xlocked) 501239281Sgonzo TWL_VREG_LOCK_UPGRADE(sc); 502239281Sgonzo 503239281Sgonzo 504239281Sgonzo err = twl_vreg_read_1(sc, regulator, TWL_VREG_GRP, &grp); 505239281Sgonzo if (err) 506239281Sgonzo goto done; 507239281Sgonzo 508239281Sgonzo /* Enable the regulator by ensuring it's in the application power group 509239281Sgonzo * and is in the "on" state. 510239281Sgonzo */ 511239281Sgonzo if (twl_is_4030(sc->sc_pdev)) { 512239281Sgonzo 513239281Sgonzo /* On the TWL4030 we just need to ensure the regulator is in the right 514239281Sgonzo * power domain, don't need to turn on explicitly like TWL6030. 515239281Sgonzo */ 516239281Sgonzo grp |= TWL4030_P1_GRP; 517239281Sgonzo err = twl_vreg_write_1(sc, regulator, TWL_VREG_GRP, grp); 518239281Sgonzo 519239281Sgonzo } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) { 520239281Sgonzo 521239281Sgonzo if (twl_is_6030(sc->sc_pdev) && !(grp & TWL6030_P1_GRP)) { 522239281Sgonzo grp |= TWL6030_P1_GRP; 523239281Sgonzo err = twl_vreg_write_1(sc, regulator, TWL_VREG_GRP, grp); 524239281Sgonzo if (err) 525239281Sgonzo goto done; 526239281Sgonzo } 527239281Sgonzo 528239281Sgonzo /* Write the resource state to "ON" */ 529239281Sgonzo err = twl_vreg_write_1(sc, regulator, TWL_VREG_STATE, (grp << 5) | 0x01); 530239281Sgonzo } 531239281Sgonzo 532239281Sgonzodone: 533239281Sgonzo if (!xlocked) 534239281Sgonzo TWL_VREG_LOCK_DOWNGRADE(sc); 535239281Sgonzo 536239281Sgonzo return (err); 537239281Sgonzo} 538239281Sgonzo 539239281Sgonzo/** 540239281Sgonzo * twl_vreg_write_regulator_voltage - sets the voltage level on a regulator 541239281Sgonzo * @sc: the device soft context 542239281Sgonzo * @regulator: pointer to the regulator structure 543239281Sgonzo * @millivolts: the voltage to set 544239281Sgonzo * 545239281Sgonzo * Sets the voltage output on a given regulator, if the regulator is not 546239281Sgonzo * enabled, it will be enabled. 547239281Sgonzo * 548239281Sgonzo * LOCKING: 549239281Sgonzo * On entry expects the TWL VREG lock to be held, may upgrade the lock to 550239281Sgonzo * exclusive but if so it will be downgraded once again before returning. 551239281Sgonzo * 552239281Sgonzo * RETURNS: 553239281Sgonzo * Zero on success or an error code on failure. 554239281Sgonzo */ 555239281Sgonzostatic int 556239281Sgonzotwl_vreg_write_regulator_voltage(struct twl_vreg_softc *sc, 557239281Sgonzo struct twl_regulator_entry *regulator, int millivolts) 558239281Sgonzo{ 559239281Sgonzo int err; 560239281Sgonzo uint8_t vsel; 561239281Sgonzo int xlocked; 562239281Sgonzo 563239281Sgonzo TWL_VREG_ASSERT_LOCKED(sc); 564239281Sgonzo 565239281Sgonzo /* If millivolts is zero then we simply disable the output */ 566239281Sgonzo if (millivolts == 0) 567239281Sgonzo return (twl_vreg_disable_regulator(sc, regulator)); 568239281Sgonzo 569239281Sgonzo /* If the regulator has a fixed voltage then check the setting matches 570239281Sgonzo * and simply enable. 571239281Sgonzo */ 572239281Sgonzo if (regulator->supp_voltages == NULL || regulator->num_supp_voltages == 0) { 573239281Sgonzo if (millivolts != regulator->fixed_voltage) 574239281Sgonzo return (EINVAL); 575239281Sgonzo 576239281Sgonzo return (twl_vreg_enable_regulator(sc, regulator)); 577239281Sgonzo } 578239281Sgonzo 579239281Sgonzo /* Get the VSEL value for the given voltage */ 580239281Sgonzo err = twl_vreg_millivolt_to_vsel(sc, regulator, millivolts, &vsel); 581239281Sgonzo if (err) 582239281Sgonzo return (err); 583239281Sgonzo 584239281Sgonzo 585239281Sgonzo /* Need to upgrade because writing the voltage and enabling should be atomic */ 586239281Sgonzo xlocked = sx_xlocked(&sc->sc_sx); 587239281Sgonzo if (!xlocked) 588239281Sgonzo TWL_VREG_LOCK_UPGRADE(sc); 589239281Sgonzo 590239281Sgonzo 591239281Sgonzo /* Set voltage and enable (atomically) */ 592239281Sgonzo err = twl_vreg_write_1(sc, regulator, TWL_VREG_VSEL, (vsel & 0x1f)); 593239281Sgonzo if (!err) { 594239281Sgonzo err = twl_vreg_enable_regulator(sc, regulator); 595239281Sgonzo } 596239281Sgonzo 597239281Sgonzo if (!xlocked) 598239281Sgonzo TWL_VREG_LOCK_DOWNGRADE(sc); 599239281Sgonzo 600239281Sgonzo if ((twl_vreg_debug > 1) && !err) 601239281Sgonzo device_printf(sc->sc_dev, "%s : setting voltage to %dmV (vsel: 0x%x)\n", 602239281Sgonzo regulator->name, millivolts, vsel); 603239281Sgonzo 604239281Sgonzo return (err); 605239281Sgonzo} 606239281Sgonzo 607239281Sgonzo/** 608239281Sgonzo * twl_vreg_read_regulator_voltage - reads the voltage on a given regulator 609239281Sgonzo * @sc: the device soft context 610239281Sgonzo * @regulator: pointer to the regulator structure 611239281Sgonzo * @millivolts: upon return will contain the voltage on the regulator 612239281Sgonzo * 613239281Sgonzo * LOCKING: 614239281Sgonzo * On entry expects the TWL VREG lock to be held. It will upgrade the lock to 615239281Sgonzo * exclusive if not already, but if so, it will be downgraded again before 616239281Sgonzo * returning. 617239281Sgonzo * 618239281Sgonzo * RETURNS: 619239281Sgonzo * Zero on success, or otherwise an error code. 620239281Sgonzo */ 621239281Sgonzostatic int 622239281Sgonzotwl_vreg_read_regulator_voltage(struct twl_vreg_softc *sc, 623239281Sgonzo struct twl_regulator_entry *regulator, int *millivolts) 624239281Sgonzo{ 625239281Sgonzo int err; 626239281Sgonzo int en = 0; 627239281Sgonzo int xlocked; 628239281Sgonzo uint8_t vsel; 629239281Sgonzo 630239281Sgonzo TWL_VREG_ASSERT_LOCKED(sc); 631239281Sgonzo 632239281Sgonzo /* Need to upgrade the lock because checking enabled state and voltage 633239281Sgonzo * should be atomic. 634239281Sgonzo */ 635239281Sgonzo xlocked = sx_xlocked(&sc->sc_sx); 636239281Sgonzo if (!xlocked) 637239281Sgonzo TWL_VREG_LOCK_UPGRADE(sc); 638239281Sgonzo 639239281Sgonzo 640239281Sgonzo /* Check if the regulator is currently enabled */ 641239281Sgonzo err = twl_vreg_is_regulator_enabled(sc, regulator, &en); 642239281Sgonzo if (err) 643239281Sgonzo goto done; 644239281Sgonzo 645239281Sgonzo *millivolts = 0; 646239281Sgonzo if (!en) 647239281Sgonzo goto done; 648239281Sgonzo 649239281Sgonzo 650239281Sgonzo /* Not all voltages are adjustable */ 651239281Sgonzo if (regulator->supp_voltages == NULL || !regulator->num_supp_voltages) { 652239281Sgonzo *millivolts = regulator->fixed_voltage; 653239281Sgonzo goto done; 654239281Sgonzo } 655239281Sgonzo 656239281Sgonzo /* For variable voltages read the voltage register */ 657239281Sgonzo err = twl_vreg_read_1(sc, regulator, TWL_VREG_VSEL, &vsel); 658239281Sgonzo if (err) 659239281Sgonzo goto done; 660239281Sgonzo 661239281Sgonzo vsel &= (regulator->num_supp_voltages - 1); 662239281Sgonzo if (regulator->supp_voltages[vsel] == UNDF) { 663239281Sgonzo err = EINVAL; 664239281Sgonzo goto done; 665239281Sgonzo } 666239281Sgonzo 667239281Sgonzo *millivolts = regulator->supp_voltages[vsel]; 668239281Sgonzo 669239281Sgonzodone: 670239281Sgonzo if (!xlocked) 671239281Sgonzo TWL_VREG_LOCK_DOWNGRADE(sc); 672239281Sgonzo 673239281Sgonzo if ((twl_vreg_debug > 1) && !err) 674239281Sgonzo device_printf(sc->sc_dev, "%s : reading voltage is %dmV (vsel: 0x%x)\n", 675239281Sgonzo regulator->name, *millivolts, vsel); 676239281Sgonzo 677239281Sgonzo return (err); 678239281Sgonzo} 679239281Sgonzo 680239281Sgonzo/** 681239281Sgonzo * twl_vreg_get_voltage - public interface to read the voltage on a regulator 682239281Sgonzo * @dev: TWL VREG device 683239281Sgonzo * @name: the name of the regulator to read the voltage of 684239281Sgonzo * @millivolts: pointer to an integer that upon return will contain the mV 685239281Sgonzo * 686239281Sgonzo * If the regulator is disabled the function will set the @millivolts to zero. 687239281Sgonzo * 688239281Sgonzo * LOCKING: 689239281Sgonzo * Internally the function takes and releases the TWL VREG lock. 690239281Sgonzo * 691239281Sgonzo * RETURNS: 692239281Sgonzo * Zero on success or a negative error code on failure. 693239281Sgonzo */ 694239281Sgonzoint 695239281Sgonzotwl_vreg_get_voltage(device_t dev, const char *name, int *millivolts) 696239281Sgonzo{ 697239281Sgonzo struct twl_vreg_softc *sc; 698239281Sgonzo struct twl_regulator_entry *regulator; 699239281Sgonzo int err = EINVAL; 700239281Sgonzo 701239281Sgonzo if (millivolts == NULL) 702239281Sgonzo return (EINVAL); 703239281Sgonzo 704239281Sgonzo sc = device_get_softc(dev); 705239281Sgonzo 706239281Sgonzo TWL_VREG_SLOCK(sc); 707239281Sgonzo 708239281Sgonzo LIST_FOREACH(regulator, &sc->sc_vreg_list, entries) { 709239281Sgonzo if (strcmp(regulator->name, name) == 0) { 710239281Sgonzo err = twl_vreg_read_regulator_voltage(sc, regulator, millivolts); 711239281Sgonzo break; 712239281Sgonzo } 713239281Sgonzo } 714239281Sgonzo 715239281Sgonzo TWL_VREG_SUNLOCK(sc); 716239281Sgonzo 717239281Sgonzo return (err); 718239281Sgonzo} 719239281Sgonzo 720239281Sgonzo/** 721239281Sgonzo * twl_vreg_set_voltage - public interface to write the voltage on a regulator 722239281Sgonzo * @dev: TWL VREG device 723239281Sgonzo * @name: the name of the regulator to read the voltage of 724239281Sgonzo * @millivolts: the voltage to set in millivolts 725239281Sgonzo * 726239281Sgonzo * Sets the output voltage on a given regulator. If the regulator is a fixed 727239281Sgonzo * voltage reg then the @millivolts value should match the fixed voltage. If 728239281Sgonzo * a variable regulator then the @millivolt value must fit within the max/min 729239281Sgonzo * range of the given regulator. 730239281Sgonzo * 731239281Sgonzo * LOCKING: 732239281Sgonzo * Internally the function takes and releases the TWL VREG lock. 733239281Sgonzo * 734239281Sgonzo * RETURNS: 735239281Sgonzo * Zero on success or a negative error code on failure. 736239281Sgonzo */ 737239281Sgonzoint 738239281Sgonzotwl_vreg_set_voltage(device_t dev, const char *name, int millivolts) 739239281Sgonzo{ 740239281Sgonzo struct twl_vreg_softc *sc; 741239281Sgonzo struct twl_regulator_entry *regulator; 742239281Sgonzo int err = EINVAL; 743239281Sgonzo 744239281Sgonzo sc = device_get_softc(dev); 745239281Sgonzo 746239281Sgonzo TWL_VREG_SLOCK(sc); 747239281Sgonzo 748239281Sgonzo LIST_FOREACH(regulator, &sc->sc_vreg_list, entries) { 749239281Sgonzo if (strcmp(regulator->name, name) == 0) { 750239281Sgonzo err = twl_vreg_write_regulator_voltage(sc, regulator, millivolts); 751239281Sgonzo break; 752239281Sgonzo } 753239281Sgonzo } 754239281Sgonzo 755239281Sgonzo TWL_VREG_SUNLOCK(sc); 756239281Sgonzo 757239281Sgonzo return (err); 758239281Sgonzo} 759239281Sgonzo 760239281Sgonzo/** 761239281Sgonzo * twl_sysctl_voltage - reads or writes the voltage for a regulator 762239281Sgonzo * @SYSCTL_HANDLER_ARGS: arguments for the callback 763239281Sgonzo * 764239281Sgonzo * Callback for the sysctl entry for the regulator, simply used to return 765239281Sgonzo * the voltage on a particular regulator. 766239281Sgonzo * 767239281Sgonzo * LOCKING: 768239281Sgonzo * Takes the TWL_VREG shared lock internally. 769239281Sgonzo * 770239281Sgonzo * RETURNS: 771239281Sgonzo * Zero on success or an error code on failure. 772239281Sgonzo */ 773239281Sgonzostatic int 774239281Sgonzotwl_vreg_sysctl_voltage(SYSCTL_HANDLER_ARGS) 775239281Sgonzo{ 776239281Sgonzo struct twl_vreg_softc *sc = (struct twl_vreg_softc*)arg1; 777239281Sgonzo struct twl_regulator_entry *regulator; 778239281Sgonzo int voltage; 779239281Sgonzo int found = 0; 780239281Sgonzo 781239281Sgonzo TWL_VREG_SLOCK(sc); 782239281Sgonzo 783239281Sgonzo /* Find the regulator with the matching name */ 784239281Sgonzo LIST_FOREACH(regulator, &sc->sc_vreg_list, entries) { 785239281Sgonzo if (strcmp(regulator->name, oidp->oid_name) == 0) { 786239281Sgonzo found = 1; 787239281Sgonzo break; 788239281Sgonzo } 789239281Sgonzo } 790239281Sgonzo 791239281Sgonzo /* Sanity check that we found the regulator */ 792239281Sgonzo if (!found) { 793239281Sgonzo TWL_VREG_SUNLOCK(sc); 794239281Sgonzo return (EINVAL); 795239281Sgonzo } 796239281Sgonzo 797239281Sgonzo twl_vreg_read_regulator_voltage(sc, regulator, &voltage); 798239281Sgonzo 799239281Sgonzo TWL_VREG_SUNLOCK(sc); 800239281Sgonzo 801239281Sgonzo return sysctl_handle_int(oidp, &voltage, 0, req); 802239281Sgonzo} 803239281Sgonzo 804239281Sgonzo/** 805239281Sgonzo * twl_add_regulator - adds single voltage regulator sysctls for the device 806239281Sgonzo * @sc: device soft context 807239281Sgonzo * @name: the name of the regulator 808239281Sgonzo * @nsub: the number of the subdevice 809239281Sgonzo * @regbase: the base address of the voltage regulator registers 810239281Sgonzo * @fixed_voltage: if a fixed voltage regulator this defines it's voltage 811239281Sgonzo * @voltages: if a variable voltage regulator, an array of possible voltages 812239281Sgonzo * @num_voltages: the number of entries @voltages 813239281Sgonzo * 814239281Sgonzo * Adds a voltage regulator to the device and also a sysctl interface for the 815239281Sgonzo * regulator. 816239281Sgonzo * 817239281Sgonzo * LOCKING: 818239281Sgonzo * The TWL_VEG exclusive lock must be held while this function is called. 819239281Sgonzo * 820239281Sgonzo * RETURNS: 821239281Sgonzo * Pointer to the new regulator entry on success, otherwise on failure NULL. 822239281Sgonzo */ 823239281Sgonzostatic struct twl_regulator_entry* 824239281Sgonzotwl_vreg_add_regulator(struct twl_vreg_softc *sc, const char *name, 825239281Sgonzo uint8_t nsub, uint8_t regbase, uint16_t fixed_voltage, 826239281Sgonzo const uint16_t *voltages, uint32_t num_voltages) 827239281Sgonzo{ 828239281Sgonzo struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 829239281Sgonzo struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 830239281Sgonzo struct twl_regulator_entry *new; 831239281Sgonzo 832239281Sgonzo new = malloc(sizeof(struct twl_regulator_entry), M_DEVBUF, M_NOWAIT | M_ZERO); 833239281Sgonzo if (new == NULL) 834239281Sgonzo return (NULL); 835239281Sgonzo 836239281Sgonzo 837239281Sgonzo strncpy(new->name, name, TWL_VREG_MAX_NAMELEN); 838239281Sgonzo new->name[TWL_VREG_MAX_NAMELEN - 1] = '\0'; 839239281Sgonzo 840239281Sgonzo new->sub_dev = nsub; 841239281Sgonzo new->reg_off = regbase; 842239281Sgonzo 843239281Sgonzo new->fixed_voltage = fixed_voltage; 844239281Sgonzo 845239281Sgonzo new->supp_voltages = voltages; 846239281Sgonzo new->num_supp_voltages = num_voltages; 847239281Sgonzo 848239281Sgonzo 849239281Sgonzo /* Add a sysctl entry for the voltage */ 850239281Sgonzo new->oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, name, 851239281Sgonzo CTLTYPE_INT | CTLFLAG_RD, sc, 0, 852239281Sgonzo twl_vreg_sysctl_voltage, "I", "voltage regulator"); 853239281Sgonzo 854239281Sgonzo /* Finally add the regulator to list of supported regulators */ 855239281Sgonzo LIST_INSERT_HEAD(&sc->sc_vreg_list, new, entries); 856239281Sgonzo 857239281Sgonzo return (new); 858239281Sgonzo} 859239281Sgonzo 860239281Sgonzo/** 861239281Sgonzo * twl_vreg_add_regulators - adds any voltage regulators to the device 862239281Sgonzo * @sc: device soft context 863239281Sgonzo * @chip: the name of the chip used in the hints 864239281Sgonzo * @regulators: the list of possible voltage regulators 865239281Sgonzo * 866239281Sgonzo * Loops over the list of regulators and matches up with the FDT values, 867239281Sgonzo * adjusting the actual voltage based on the supplied values. 868239281Sgonzo * 869239281Sgonzo * LOCKING: 870239281Sgonzo * The TWL_VEG exclusive lock must be held while this function is called. 871239281Sgonzo * 872239281Sgonzo * RETURNS: 873239281Sgonzo * Always returns 0. 874239281Sgonzo */ 875239281Sgonzostatic int 876239281Sgonzotwl_vreg_add_regulators(struct twl_vreg_softc *sc, 877239281Sgonzo const struct twl_regulator *regulators) 878239281Sgonzo{ 879239281Sgonzo int err; 880239281Sgonzo int millivolts; 881239281Sgonzo const struct twl_regulator *walker; 882239281Sgonzo struct twl_regulator_entry *entry; 883239281Sgonzo phandle_t child; 884239281Sgonzo char rnames[256]; 885239281Sgonzo char *name, *voltage; 886239281Sgonzo int len = 0, prop_len; 887239281Sgonzo 888239281Sgonzo 889239281Sgonzo /* Add the regulators from the list */ 890239281Sgonzo walker = ®ulators[0]; 891239281Sgonzo while (walker->name != NULL) { 892239281Sgonzo 893239281Sgonzo /* Add the regulator to the list */ 894239281Sgonzo entry = twl_vreg_add_regulator(sc, walker->name, walker->subdev, 895239281Sgonzo walker->regbase, walker->fixedvoltage, 896239281Sgonzo walker->voltages, walker->num_voltages); 897239281Sgonzo if (entry == NULL) 898239281Sgonzo continue; 899239281Sgonzo 900239281Sgonzo walker++; 901239281Sgonzo } 902239281Sgonzo 903239281Sgonzo 904239281Sgonzo /* Check if the FDT is telling us to set any voltages */ 905239281Sgonzo child = ofw_bus_get_node(sc->sc_pdev); 906239281Sgonzo if (child) { 907239281Sgonzo 908239281Sgonzo prop_len = OF_getprop(child, "voltage-regulators", rnames, sizeof(rnames)); 909239281Sgonzo while (len < prop_len) { 910239281Sgonzo name = rnames + len; 911239281Sgonzo len += strlen(name) + 1; 912239281Sgonzo if ((len >= prop_len) || (name[0] == '\0')) 913239281Sgonzo break; 914239281Sgonzo 915239281Sgonzo voltage = rnames + len; 916239281Sgonzo len += strlen(voltage) + 1; 917239281Sgonzo if (voltage[0] == '\0') 918239281Sgonzo break; 919239281Sgonzo 920239281Sgonzo millivolts = strtoul(voltage, NULL, 0); 921239281Sgonzo 922239281Sgonzo LIST_FOREACH(entry, &sc->sc_vreg_list, entries) { 923239281Sgonzo if (strcmp(entry->name, name) == 0) { 924239281Sgonzo twl_vreg_write_regulator_voltage(sc, entry, millivolts); 925239281Sgonzo break; 926239281Sgonzo } 927239281Sgonzo } 928239281Sgonzo } 929239281Sgonzo } 930239281Sgonzo 931239281Sgonzo 932239281Sgonzo if (twl_vreg_debug) { 933239281Sgonzo LIST_FOREACH(entry, &sc->sc_vreg_list, entries) { 934239281Sgonzo err = twl_vreg_read_regulator_voltage(sc, entry, &millivolts); 935239281Sgonzo if (!err) 936239281Sgonzo device_printf(sc->sc_dev, "%s : %d mV\n", entry->name, millivolts); 937239281Sgonzo } 938239281Sgonzo } 939239281Sgonzo 940239281Sgonzo return (0); 941239281Sgonzo} 942239281Sgonzo 943239281Sgonzo/** 944239281Sgonzo * twl_vreg_init - initialises the list of regulators 945239281Sgonzo * @dev: the twl_vreg device 946239281Sgonzo * 947239281Sgonzo * This function is called as an intrhook once interrupts have been enabled, 948239281Sgonzo * this is done so that the driver has the option to enable/disable or set 949239281Sgonzo * the voltage level based on settings providied in the FDT. 950239281Sgonzo * 951239281Sgonzo * LOCKING: 952239281Sgonzo * Takes the exclusive lock in the function. 953239281Sgonzo */ 954239281Sgonzostatic void 955239281Sgonzotwl_vreg_init(void *dev) 956239281Sgonzo{ 957239281Sgonzo struct twl_vreg_softc *sc; 958239281Sgonzo 959239281Sgonzo sc = device_get_softc((device_t)dev); 960239281Sgonzo 961239281Sgonzo TWL_VREG_XLOCK(sc); 962239281Sgonzo 963239281Sgonzo if (twl_is_4030(sc->sc_pdev)) 964239281Sgonzo twl_vreg_add_regulators(sc, twl4030_regulators); 965239281Sgonzo else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) 966239281Sgonzo twl_vreg_add_regulators(sc, twl6030_regulators); 967239281Sgonzo 968239281Sgonzo TWL_VREG_XUNLOCK(sc); 969239281Sgonzo 970239281Sgonzo config_intrhook_disestablish(&sc->sc_init_hook); 971239281Sgonzo} 972239281Sgonzo 973239281Sgonzostatic int 974239281Sgonzotwl_vreg_probe(device_t dev) 975239281Sgonzo{ 976239281Sgonzo if (twl_is_4030(device_get_parent(dev))) 977239281Sgonzo device_set_desc(dev, "TI TWL4030 PMIC Voltage Regulators"); 978239281Sgonzo else if (twl_is_6025(device_get_parent(dev)) || 979239281Sgonzo twl_is_6030(device_get_parent(dev))) 980239281Sgonzo device_set_desc(dev, "TI TWL6025/TWL6030 PMIC Voltage Regulators"); 981239281Sgonzo else 982239281Sgonzo return (ENXIO); 983239281Sgonzo 984239281Sgonzo return (0); 985239281Sgonzo} 986239281Sgonzo 987239281Sgonzostatic int 988239281Sgonzotwl_vreg_attach(device_t dev) 989239281Sgonzo{ 990239281Sgonzo struct twl_vreg_softc *sc; 991239281Sgonzo 992239281Sgonzo sc = device_get_softc(dev); 993239281Sgonzo sc->sc_dev = dev; 994239281Sgonzo sc->sc_pdev = device_get_parent(dev); 995239281Sgonzo 996239281Sgonzo TWL_VREG_LOCK_INIT(sc); 997239281Sgonzo 998239281Sgonzo LIST_INIT(&sc->sc_vreg_list); 999239281Sgonzo 1000239281Sgonzo /* We have to wait until interrupts are enabled. I2C read and write 1001239281Sgonzo * only works if the interrupts are available. 1002239281Sgonzo */ 1003239281Sgonzo sc->sc_init_hook.ich_func = twl_vreg_init; 1004239281Sgonzo sc->sc_init_hook.ich_arg = dev; 1005239281Sgonzo 1006239281Sgonzo if (config_intrhook_establish(&sc->sc_init_hook) != 0) 1007239281Sgonzo return (ENOMEM); 1008239281Sgonzo 1009239281Sgonzo return (0); 1010239281Sgonzo} 1011239281Sgonzo 1012239281Sgonzostatic int 1013239281Sgonzotwl_vreg_detach(device_t dev) 1014239281Sgonzo{ 1015239281Sgonzo struct twl_vreg_softc *sc; 1016239281Sgonzo struct twl_regulator_entry *regulator; 1017239281Sgonzo struct twl_regulator_entry *tmp; 1018239281Sgonzo 1019239281Sgonzo sc = device_get_softc(dev); 1020239281Sgonzo 1021239281Sgonzo /* Take the lock and free all the added regulators */ 1022239281Sgonzo TWL_VREG_XLOCK(sc); 1023239281Sgonzo 1024239281Sgonzo LIST_FOREACH_SAFE(regulator, &sc->sc_vreg_list, entries, tmp) { 1025239281Sgonzo LIST_REMOVE(regulator, entries); 1026239281Sgonzo sysctl_remove_oid(regulator->oid, 1, 0); 1027239281Sgonzo free(regulator, M_DEVBUF); 1028239281Sgonzo } 1029239281Sgonzo 1030239281Sgonzo TWL_VREG_XUNLOCK(sc); 1031239281Sgonzo 1032239281Sgonzo TWL_VREG_LOCK_DESTROY(sc); 1033239281Sgonzo 1034239281Sgonzo return (0); 1035239281Sgonzo} 1036239281Sgonzo 1037239281Sgonzostatic device_method_t twl_vreg_methods[] = { 1038239281Sgonzo DEVMETHOD(device_probe, twl_vreg_probe), 1039239281Sgonzo DEVMETHOD(device_attach, twl_vreg_attach), 1040239281Sgonzo DEVMETHOD(device_detach, twl_vreg_detach), 1041239281Sgonzo 1042239281Sgonzo {0, 0}, 1043239281Sgonzo}; 1044239281Sgonzo 1045239281Sgonzostatic driver_t twl_vreg_driver = { 1046239281Sgonzo "twl_vreg", 1047239281Sgonzo twl_vreg_methods, 1048239281Sgonzo sizeof(struct twl_vreg_softc), 1049239281Sgonzo}; 1050239281Sgonzo 1051239281Sgonzostatic devclass_t twl_vreg_devclass; 1052239281Sgonzo 1053239281SgonzoDRIVER_MODULE(twl_vreg, twl, twl_vreg_driver, twl_vreg_devclass, 0, 0); 1054239281SgonzoMODULE_VERSION(twl_vreg, 1); 1055