/*-
* Copyright (c) 2013 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Radoslaw Kujawa.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Texas Instruments TPS65217 Power Management IC driver.
* TODO: battery, sequencer, pgood
*/
const char* name;
uint16_t voltage_min; /* in mV */
uint16_t voltage_max; /* in mV */
const uint16_t *voltages; /* all possible voltage settings */
uint8_t nvoltages; /* number of voltage settings */
bool can_track; /* regulator can track U of other r. */
struct tps_reg_param *tracked_reg; /* ptr to tracked regulator */
bool can_xadj; /* voltage can be adjusted externally */
bool can_ls; /* can be a load switch instead of r. */
uint8_t defreg_num; /* DEF register */
uint8_t enable_bit; /* position in ENABLE register */
/*
* Run-time parameters configured during attachment and later, these
* probably should be split into separate struct that would be a part
* of softc. But since we can have only one TPS chip, that should be
* okay for now.
*/
bool is_enabled; /* regulator is enabled */
bool is_pg; /* regulator is "power good" */
bool is_tracking; /* voltage is tracking other reg. */
bool is_ls; /* is a load switch */
bool is_xadj; /* voltage is adjusted externally */
if (tps65217pmic_i2c_lock(sc) != 0) {
aprint_error_dev(sc->sc_dev,
"failed to initialize power monitor\n");
return;
}
status = tps65217pmic_reg_read(sc, TPS65217PMIC_STATUS);
ppath = tps65217pmic_reg_read(sc, TPS65217PMIC_PPATH);
/* acknowledge and disregard whatever interrupt was generated earlier */
intr = tps65217pmic_reg_read(sc, TPS65217PMIC_INT);
tps65217pmic_i2c_unlock(sc);
sc->sc_usbstatus = status & TPS65217PMIC_STATUS_USBPWR;
sc->sc_acstatus = status & TPS65217PMIC_STATUS_ACPWR;
sc->sc_usbenabled = ppath & TPS65217PMIC_PPATH_USB_EN;
sc->sc_acenabled = ppath & TPS65217PMIC_PPATH_AC_EN;
if (intr & intrmask)
aprint_normal_dev(sc->sc_dev,
"WARNING: hardware interrupt enabled but not supported");
/* set up callout to poll for power source changes */
callout_init(&sc->sc_powerpollco, 0);
callout_setfunc(&sc->sc_powerpollco, tps65217pmic_power_monitor, sc);
if (tps65217pmic_i2c_lock(sc) != 0) {
device_printf(sc->sc_dev,
"WARNING: unable to perform power monitor task.\n");
return;
}
status = tps65217pmic_reg_read(sc, TPS65217PMIC_STATUS);
tps65217pmic_i2c_unlock(sc);
usbstatus = status & TPS65217PMIC_STATUS_USBPWR;
acstatus = status & TPS65217PMIC_STATUS_ACPWR;
if (usbstatus != sc->sc_usbstatus) {
sc->sc_usbstatus = usbstatus;
pmf_event_inject(NULL, PMFE_POWER_CHANGED);
if (usbstatus)
aprint_normal_dev(sc->sc_dev,
"USB power source connected\n");
else
aprint_normal_dev(sc->sc_dev,
"USB power source disconnected\n");
}
static void
tps65217pmic_wled_init(struct tps65217pmic_softc *sc, int isel, int fdim,
int brightness)
{
uint8_t val = 0;
switch (isel) {
case 1:
case 2:
val |= ((isel - 1) << TPS65217PMIC_WLEDCTRL1_ISEL);
break;
default:
aprint_error_dev(sc->sc_dev,
"WLED ISET selection is 1 or 2: isel %d\n", isel);
return;
}
switch (fdim) {
case 100:
val |= TPS65217PMIC_WLEDCTRL1_FDIM_100Hz;
break;
case 200:
val |= TPS65217PMIC_WLEDCTRL1_FDIM_200Hz;
break;
case 500:
val |= TPS65217PMIC_WLEDCTRL1_FDIM_500Hz;
break;
case 1000:
val |= TPS65217PMIC_WLEDCTRL1_FDIM_1000Hz;
break;
default:
aprint_error_dev(sc->sc_dev,
"WLED PWM dimming frequency is 100, 200, 500 or 1000:"
" fdim %d\n", fdim);
return;
}
if (brightness > 100 ||
brightness < 0) {
aprint_error_dev(sc->sc_dev,
"invalid brightness: between 0 and 100: %d\n", brightness);
return;
}
if (tps65217pmic_i2c_lock(sc) != 0) {
device_printf(sc->sc_dev,
"WARNING: unable to configure LED\n");
return;
}
static uint16_t
tps65217pmic_ppath_max_ac_current(uint8_t ppath)
{
switch ((ppath & TPS65217PMIC_PPATH_IAC) >>
TPS65217PMIC_PPATH_IAC_RSHFIT) {
case TPS65217PMIC_PPATH_IAC_100MA:
return 100;
case TPS65217PMIC_PPATH_IAC_500MA:
return 500;
case TPS65217PMIC_PPATH_IAC_1300MA:
return 1300;
case TPS65217PMIC_PPATH_IAC_2500MA:
return 2500;
}
return 0;
}
static uint16_t
tps65217pmic_ppath_max_usb_current(uint8_t ppath)
{
switch (ppath & TPS65217PMIC_PPATH_IUSB) {
case TPS65217PMIC_PPATH_IUSB_100MA:
return 100;
case TPS65217PMIC_PPATH_IUSB_500MA:
return 500;
case TPS65217PMIC_PPATH_IUSB_1300MA:
return 1300;
case TPS65217PMIC_PPATH_IUSB_1800MA:
return 1800;
}
return 0;
}
/* Read regulator state and save it to tps_reg_param. */
static void
tps65217pmic_regulator_read_config(struct tps65217pmic_softc *sc, struct
tps_reg_param *regulator)
{
uint8_t defreg, regenable;
uint16_t voltage;
switch (regulator->nvoltages) {
case 16:
voltage = regulator->voltages[defreg &
TPS65217PMIC_DEFX_VOLTAGE_16];
break;
case 32:
voltage = regulator->voltages[defreg &
TPS65217PMIC_DEFX_VOLTAGE_32];
break;
case 64:
voltage = regulator->voltages[defreg &
TPS65217PMIC_DEFX_VOLTAGE_64];
break;
default:
/* unsupported number of voltage settings? */
voltage = 0;
break;
}
/* Handle regulator tracking other regulator voltage. */
if (regulator->can_track)
if (defreg & TPS65217PMIC_DEFX_TRACKING) {
regulator->is_tracking = true;
voltage = 0; /* see regulator->tracked_reg */
}
/* Handle regulator configured into load switch mode. */
if (regulator->can_ls)
if (!(defreg & TPS65217PMIC_DEFX_LS)) {
regulator->is_ls = true;
voltage = 0;
}
if (regulator->can_xadj)
if (defreg & TPS65217PMIC_DEFX_XADJ) {
regulator->is_xadj = true;
voltage = 0;
static void
tps65217pmic_envsys_register(struct tps65217pmic_softc *sc)
{
int i;
sc->sc_sme = sysmon_envsys_create();
/* iterate over all regulators and attach them as sensors */
for(i = 0; i <= SNUM_REGS; i++) {
/* set name */
strlcpy(sc->sc_regsensor[i].desc, tps_regulators[i].name,
sizeof(sc->sc_regsensor[i].desc));
sc->sc_regsensor[i].units = ENVSYS_SVOLTS_DC;
sc->sc_regsensor[i].state = ENVSYS_SINVALID;
error = tps65217pmic_i2c_lock(pmic_sc);
if (error != 0)
return error;
val = tps65217pmic_reg_read(pmic_sc, TPS65217PMIC_ENABLE);
if (enable)
val |= regulator->enable_bit;
else
val &= ~regulator->enable_bit;
tps65217pmic_reg_write(pmic_sc, TPS65217PMIC_ENABLE, val);