From 8054d7cc0df799c2aa3afd8958fbc1d7d156dc21 Mon Sep 17 00:00:00 2001 From: Maxim Sobolev Date: Tue, 9 Jun 2015 18:46:31 +0000 Subject: [PATCH] Extend TPS65217 support to be able to pull and decode battery charger configuration and provide some basic control knobs to set charger voltage and dump config on boot. Two loader tunables have been added: o hw.am335x_pmic.bootverbose set to 1 to get more info on the boot; o hw.am335x_pmic.vo: set to charger voltage to be applied on kernel initialization time, supported values are "4.10V", "4.15V", "4.20V" and "4.25V". Cleanup code a bit in general, move TPS65217 register definitions into a separate header, convert bit-banging defines into bitmap structures. Also threat the case when power source is neither "AC" nor "USB" as "Battery", not "Unknown". --- sys/arm/ti/am335x/am335x_pmic.c | 154 +++++++++++++++++++++++--------- sys/arm/ti/am335x/tps65217x.h | 114 +++++++++++++++++++++++ 2 files changed, 227 insertions(+), 41 deletions(-) create mode 100644 sys/arm/ti/am335x/tps65217x.h diff --git a/sys/arm/ti/am335x/am335x_pmic.c b/sys/arm/ti/am335x/am335x_pmic.c index 18bc438a9141..5ff9936c6d5a 100644 --- a/sys/arm/ti/am335x/am335x_pmic.c +++ b/sys/arm/ti/am335x/am335x_pmic.c @@ -27,7 +27,7 @@ #include __FBSDID("$FreeBSD$"); /* -* TPS65217 PMIC companion chip for AM335x SoC sitting on I2C bus +* TI TPS65217 PMIC companion chip for AM335x SoC sitting on I2C bus */ #include #include @@ -50,30 +50,10 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include "iicbus_if.h" -#define TPS65217A 0x7 -#define TPS65217B 0xF -#define TPS65217C 0xE -#define TPS65217D 0x6 - -/* TPS65217 Reisters */ -#define TPS65217_CHIPID_REG 0x00 -#define TPS65217_INT_REG 0x02 -#define TPS65217_INT_PBM (1U << 6) -#define TPS65217_INT_ACM (1U << 5) -#define TPS65217_INT_USBM (1U << 4) -#define TPS65217_INT_PBI (1U << 2) -#define TPS65217_INT_ACI (1U << 1) -#define TPS65217_INT_USBI (1U << 0) - -#define TPS65217_STATUS_REG 0x0A -#define TPS65217_STATUS_OFF (1U << 7) -#define TPS65217_STATUS_ACPWR (1U << 3) -#define TPS65217_STATUS_USBPWR (1U << 2) -#define TPS65217_STATUS_BT (1U << 0) - #define MAX_IIC_DATA_SIZE 2 @@ -85,6 +65,13 @@ struct am335x_pmic_softc { void *sc_intrhand; }; +static const char *tps65217_voreg_c[4] = {"4.10V", "4.15V", "4.20V", "4.25V"}; + +static int am335x_pmic_bootverbose = 0; +TUNABLE_INT("hw.am335x_pmic.bootverbose", &am335x_pmic_bootverbose); +static char am335x_pmic_vo[6]; +TUNABLE_STR("hw.am335x_pmic.vo", am335x_pmic_vo, sizeof(am335x_pmic_vo)); + static void am335x_pmic_shutdown(void *, int); static int @@ -120,18 +107,19 @@ static void am335x_pmic_intr(void *arg) { struct am335x_pmic_softc *sc = (struct am335x_pmic_softc *)arg; - uint8_t int_reg, status_reg; + struct tps65217_status_reg status_reg; + struct tps65217_int_reg int_reg; int rv; char notify_buf[16]; THREAD_SLEEPING_OK(); - rv = am335x_pmic_read(sc->sc_dev, TPS65217_INT_REG, &int_reg, 1); + rv = am335x_pmic_read(sc->sc_dev, TPS65217_INT_REG, (uint8_t *)&int_reg, 1); if (rv != 0) { device_printf(sc->sc_dev, "Cannot read interrupt register\n"); THREAD_NO_SLEEPING(); return; } - rv = am335x_pmic_read(sc->sc_dev, TPS65217_STATUS_REG, &status_reg, 1); + rv = am335x_pmic_read(sc->sc_dev, TPS65217_STATUS_REG, (uint8_t *)&status_reg, 1); if (rv != 0) { device_printf(sc->sc_dev, "Cannot read status register\n"); THREAD_NO_SLEEPING(); @@ -139,11 +127,11 @@ am335x_pmic_intr(void *arg) } THREAD_NO_SLEEPING(); - if ((int_reg & TPS65217_INT_PBI) && (status_reg & TPS65217_STATUS_BT)) + if (int_reg.pbi && status_reg.pb) shutdown_nice(RB_POWEROFF); - if (int_reg & TPS65217_INT_ACI) { + if (int_reg.aci) { snprintf(notify_buf, sizeof(notify_buf), "notify=0x%02x", - (status_reg & TPS65217_STATUS_ACPWR) ? 1 : 0); + status_reg.acpwr ? 1 : 0); devctl_notify_f("ACPI", "ACAD", "power", notify_buf, M_NOWAIT); } } @@ -166,38 +154,121 @@ am335x_pmic_probe(device_t dev) return (0); } +static void +am335x_pmic_dump_chgconfig(device_t dev) +{ + struct tps65217_chgconfig0_reg reg0; + struct tps65217_chgconfig1_reg reg1; + struct tps65217_chgconfig2_reg reg2; + struct tps65217_chgconfig3_reg reg3; + const char *e_d[] = {"enabled", "disabled"}; + const char *d_e[] = {"disabled", "enabled"}; + const char *i_a[] = {"inactive", "active"}; + const char *f_t[] = {"false", "true"}; + const char *timer_c[] = {"4h", "5h", "6h", "8h"}; + const char *ntc_type_c[] = {"100k", "10k"}; + const char *vprechg_c[] = {"2.9V", "2.5V"}; + const char *trange_c[] = {"0-45 C", "0-60 C"}; + const char *termif_c[] = {"2.5%", "7.5%", "15%", "18%"}; + const char *pchrgt_c[] = {"30 min", "60 min"}; + const char *dppmth_c[] = {"3.50V", "3.75V", "4.00V", "4.25V"}; + const char *ichrg_c[] = {"300mA", "400mA", "500mA", "700mA"}; + + am335x_pmic_read(dev, TPS65217_CHGCONFIG0_REG, (uint8_t *)®0, 1); + device_printf(dev, " BAT TEMP/NTC ERROR: %s\n", f_t[reg0.battemp]); + device_printf(dev, " Pre-charge timer time-out: %s\n", f_t[reg0.pchgtout]); + device_printf(dev, " Charge timer time-out: %s\n", f_t[reg0.chgtout]); + device_printf(dev, " Charger active: %s\n", f_t[reg0.active]); + device_printf(dev, " Termination current detected: %s\n", f_t[reg0.termi]); + device_printf(dev, " Thermal suspend: %s\n", f_t[reg0.tsusp]); + device_printf(dev, " DPPM active: %s\n", f_t[reg0.dppm]); + device_printf(dev, " Thermal regulation: %s\n", i_a[reg0.treg]); + + am335x_pmic_read(dev, TPS65217_CHGCONFIG1_REG, (uint8_t *)®1, 1); + device_printf(dev, " Charger: %s\n", d_e[reg1.chg_en]); + device_printf(dev, " Suspend charge: %s\n", i_a[reg1.susp]); + device_printf(dev, " Charge termination: %s\n", e_d[reg1.term]); + device_printf(dev, " Charger reset: %s\n", i_a[reg1.reset]); + device_printf(dev, " NTC TYPE: %s\n", ntc_type_c[reg1.ntc_type]); + device_printf(dev, " Safety timer: %s\n", d_e[reg1.tmr_en]); + device_printf(dev, " Charge safety timer: %s\n", timer_c[reg1.timer]); + + am335x_pmic_read(dev, TPS65217_CHGCONFIG2_REG, (uint8_t *)®2, 1); + device_printf(dev, " Charge voltage: %s\n", tps65217_voreg_c[reg2.voreg]); + device_printf(dev, " Pre-charge to fast charge transition voltage: %s\n", + vprechg_c[reg2.vprechg]); + device_printf(dev, " Dynamic timer function: %s\n", d_e[reg2.dyntmr]); + + am335x_pmic_read(dev, TPS65217_CHGCONFIG3_REG, (uint8_t *)®3, 1); + device_printf(dev, " Temperature range for charging: %s\n", trange_c[reg3.trange]); + device_printf(dev, " Termination current factor: %s\n", termif_c[reg3.termif]); + device_printf(dev, " Pre-charge time: %s\n", pchrgt_c[reg3.pchrgt]); + device_printf(dev, " Power path DPPM threshold: %s\n", dppmth_c[reg3.dppmth]); + device_printf(dev, " Charge current: %s\n", ichrg_c[reg3.ichrg]); +} + +static void +am335x_pmic_setvo(device_t dev, uint8_t vo) +{ + struct tps65217_chgconfig2_reg reg2; + + am335x_pmic_read(dev, TPS65217_CHGCONFIG2_REG, (uint8_t *)®2, 1); + reg2.voreg = vo; + am335x_pmic_write(dev, TPS65217_CHGCONFIG2_REG, (uint8_t *)®2, 1); +} + static void am335x_pmic_start(void *xdev) { struct am335x_pmic_softc *sc; device_t dev = (device_t)xdev; - uint8_t reg; + struct tps65217_status_reg status_reg; + struct tps65217_chipid_reg chipid_reg; + uint8_t reg, vo; char name[20]; - char pwr[4][11] = {"Unknown", "USB", "AC", "USB and AC"}; + char pwr[4][11] = {"Battery", "USB", "AC", "USB and AC"}; int rv; sc = device_get_softc(dev); - am335x_pmic_read(dev, TPS65217_CHIPID_REG, ®, 1); - switch (reg>>4) { + am335x_pmic_read(dev, TPS65217_CHIPID_REG, (uint8_t *)&chipid_reg, 1); + switch (chipid_reg.chip) { case TPS65217A: - sprintf(name, "TPS65217A ver 1.%u", reg & 0xF); + sprintf(name, "TPS65217A ver 1.%u", chipid_reg.rev); break; case TPS65217B: - sprintf(name, "TPS65217B ver 1.%u", reg & 0xF); + sprintf(name, "TPS65217B ver 1.%u", chipid_reg.rev); break; case TPS65217C: - sprintf(name, "TPS65217C ver 1.%u", reg & 0xF); + sprintf(name, "TPS65217C ver 1.%u", chipid_reg.rev); break; case TPS65217D: - sprintf(name, "TPS65217D ver 1.%u", reg & 0xF); + sprintf(name, "TPS65217D ver 1.%u", chipid_reg.rev); break; default: sprintf(name, "Unknown PMIC"); } - am335x_pmic_read(dev, TPS65217_STATUS_REG, ®, 1); - device_printf(dev, "%s powered by %s\n", name, pwr[(reg>>2)&0x03]); + am335x_pmic_read(dev, TPS65217_STATUS_REG, (uint8_t *)&status_reg, 1); + device_printf(dev, "%s powered by %s\n", name, + pwr[status_reg.usbpwr | (status_reg.acpwr << 1)]); + + if (am335x_pmic_vo[0] != '\0') { + for (vo = 0; vo < 4; vo++) { + if (strcmp(tps65217_voreg_c[vo], am335x_pmic_vo) == 0) + break; + } + if (vo == 4) { + device_printf(dev, "WARNING: hw.am335x_pmic.vo=\"%s\"" + ": unsupported value\n", am335x_pmic_vo); + } else { + am335x_pmic_setvo(dev, vo); + } + } + + if (bootverbose || am335x_pmic_bootverbose) { + am335x_pmic_dump_chgconfig(dev); + } EVENTHANDLER_REGISTER(shutdown_final, am335x_pmic_shutdown, dev, SHUTDOWN_PRI_LAST); @@ -248,14 +319,15 @@ static void am335x_pmic_shutdown(void *xdev, int howto) { device_t dev; - uint8_t reg; + struct tps65217_status_reg reg; if (!(howto & RB_POWEROFF)) return; dev = (device_t)xdev; + am335x_pmic_read(dev, TPS65217_STATUS_REG, (uint8_t *)®, 1); /* Set the OFF bit on status register to start the shutdown sequence. */ - reg = TPS65217_STATUS_OFF; - am335x_pmic_write(dev, TPS65217_STATUS_REG, ®, 1); + reg.off = 1; + am335x_pmic_write(dev, TPS65217_STATUS_REG, (uint8_t *)®, 1); /* Toggle pmic_pwr_enable to shutdown the PMIC. */ am335x_rtc_pmic_pwr_toggle(); } diff --git a/sys/arm/ti/am335x/tps65217x.h b/sys/arm/ti/am335x/tps65217x.h new file mode 100644 index 000000000000..96f16dd34b4d --- /dev/null +++ b/sys/arm/ti/am335x/tps65217x.h @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 2012 Damjan Marion + * Copyright (c) 2015 Maksym Sobolyev + * All rights reserved. + * + * 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 AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef __TPS65217X_H__ +#define __TPS65217X_H__ + +/* + * TPS65217 PMIC is a companion chip for AM335x SoC sitting on I2C bus + */ + +/* TPS65217 Reisters */ +#define TPS65217_CHIPID_REG 0x00 +struct tps65217_chipid_reg { + unsigned int rev:4; + unsigned int chip:4; +#define TPS65217A 0x7 +#define TPS65217B 0xF +#define TPS65217C 0xE +#define TPS65217D 0x6 +} __attribute__((__packed__)); + +#define TPS65217_INT_REG 0x02 +struct tps65217_int_reg { + unsigned int usbi:1; + unsigned int aci:1; + unsigned int pbi:1; + unsigned int reserved3:1; + unsigned int usbm:1; + unsigned int acm:1; + unsigned int pbm:1; + unsigned int reserved7:1; +} __attribute__((__packed__)); + +#define TPS65217_STATUS_REG 0x0A +struct tps65217_status_reg { + unsigned int pb:1; + unsigned int reserved1:1; + unsigned int usbpwr:1; + unsigned int acpwr:1; + unsigned int reserved4:3; + unsigned int off:1; +} __attribute__((__packed__)); + +#define TPS65217_CHGCONFIG0_REG 0x03 +struct tps65217_chgconfig0_reg { + unsigned int battemp:1; + unsigned int pchgtout:1; + unsigned int chgtout:1; + unsigned int active:1; + unsigned int termi:1; + unsigned int tsusp:1; + unsigned int dppm:1; + unsigned int treg:1; +} __attribute__((__packed__)); + +#define TPS65217_CHGCONFIG1_REG 0x04 +struct tps65217_chgconfig1_reg { + unsigned int chg_en:1; + unsigned int susp:1; + unsigned int term:1; + unsigned int reset:1; + unsigned int ntc_type:1; + unsigned int tmr_en:1; + unsigned int timer:2; +} __attribute__((__packed__)); + +#define TPS65217_CHGCONFIG2_REG 0x05 +struct tps65217_chgconfig2_reg { + unsigned int reserved:4; + unsigned int voreg:2; +#define TPS65217_VO_410V 0b00 +#define TPS65217_VO_415V 0b01 +#define TPS65217_VO_420V 0b10 +#define TPS65217_VO_425V 0b11 + unsigned int vprechg:1; + unsigned int dyntmr:1; +} __attribute__((__packed__)); + +#define TPS65217_CHGCONFIG3_REG 0x06 +struct tps65217_chgconfig3_reg { + unsigned int trange:1; + unsigned int termif:2; + unsigned int pchrgt:1; + unsigned int dppmth:2; + unsigned int ichrg:2; +} __attribute__((__packed__)); + +#endif /* __TPS65217X_H__ */