Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/i2c Add battery sensors.



details:   https://anonhg.NetBSD.org/src/rev/2f4dbc88e8b6
branches:  trunk
changeset: 318779:2f4dbc88e8b6
user:      jmcneill <jmcneill%NetBSD.org@localhost>
date:      Sat May 05 00:39:59 2018 +0000
description:
Add battery sensors.

diffstat:

 sys/dev/i2c/axppmic.c |  194 ++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 173 insertions(+), 21 deletions(-)

diffs (288 lines):

diff -r c7093e14e1f8 -r 2f4dbc88e8b6 sys/dev/i2c/axppmic.c
--- a/sys/dev/i2c/axppmic.c     Sat May 05 00:14:28 2018 +0000
+++ b/sys/dev/i2c/axppmic.c     Sat May 05 00:39:59 2018 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: axppmic.c,v 1.1 2018/05/04 21:09:55 jmcneill Exp $ */
+/* $NetBSD: axppmic.c,v 1.2 2018/05/05 00:39:59 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2014-2018 Jared McNeill <jmcneill%invisible.ca@localhost>
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: axppmic.c,v 1.1 2018/05/04 21:09:55 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: axppmic.c,v 1.2 2018/05/05 00:39:59 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -44,6 +44,11 @@
 
 #include <dev/fdt/fdtvar.h>
 
+#define        AXP_POWER_MODE_REG      0x01
+#define         AXP_POWER_MODE_BATT_VALID      __BIT(4)
+#define         AXP_POWER_MODE_BATT_PRESENT    __BIT(5)
+#define         AXP_POWER_MODE_BATT_CHARGING   __BIT(6)
+
 #define AXP_POWER_DISABLE_REG  0x32
 #define         AXP_POWER_DISABLE_CTRL __BIT(7)
 
@@ -51,6 +56,16 @@
 #define         AXP_IRQ2_POKSIRQ       __BIT(1)
 #define AXP_IRQ_STATUS_REG(n)  (0x48 + (n) - 1)
 
+#define        AXP_FUEL_GAUGE_CTRL_REG 0xb8
+#define         AXP_FUEL_GAUGE_CTRL_EN __BIT(7)
+#define        AXP_BATT_CAP_REG        0xb9
+#define         AXP_BATT_CAP_VALID     __BIT(7)
+#define         AXP_BATT_CAP_PERCENT   __BITS(6,0)
+
+#define        AXP_BATT_CAP_WARN_REG   0xe6
+#define         AXP_BATT_CAP_WARN_LV1  __BITS(7,4)
+#define         AXP_BATT_CAP_WARN_LV2  __BITS(3,0)
+
 struct axppmic_ctrl {
        device_t        c_dev;
 
@@ -160,6 +175,16 @@
        const struct axppmic_ctrl *controls;
        u_int ncontrols;
        u_int irq_regs;
+       bool has_battery;
+       bool has_fuel_gauge;
+};
+
+enum axppmic_sensor {
+       AXP_SENSOR_BATT_PRESENT,
+       AXP_SENSOR_BATT_CHARGING,
+       AXP_SENSOR_BATT_CHARGE_STATE,
+       AXP_SENSOR_BATT_CAPACITY,
+       AXP_NSENSORS
 };
 
 struct axppmic_softc {
@@ -168,9 +193,13 @@
        i2c_addr_t      sc_addr;
        int             sc_phandle;
 
+       bool            sc_has_battery;
+       bool            sc_has_fuel_gauge;
+
        struct sysmon_pswitch sc_smpsw;
 
-       u_int           sc_nirq;
+       struct sysmon_envsys *sc_sme;
+       envsys_data_t   sc_sensor[AXP_NSENSORS];
 };
 
 struct axpreg_softc {
@@ -178,8 +207,6 @@
        i2c_tag_t       sc_i2c;
        i2c_addr_t      sc_addr;
        const struct axppmic_ctrl *sc_ctrl;
-
-       u_int           sc_nirq;
 };
 
 struct axpreg_attach_args {
@@ -194,6 +221,8 @@
        .controls = axp803_ctrls,
        .ncontrols = __arraycount(axp803_ctrls),
        .irq_regs = 6,
+       .has_battery = true,
+       .has_fuel_gauge = true,
 };
 
 static const struct axppmic_config axp805_config = {
@@ -322,7 +351,7 @@
        uint8_t stat;
 
        iic_acquire_bus(sc->sc_i2c, flags);
-       if (sc->sc_nirq >= 2 && axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_IRQ_STATUS_REG(2), &stat, flags) == 0) {
+       if (axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_IRQ_STATUS_REG(2), &stat, flags) == 0) {
                if (stat & AXP_IRQ2_POKSIRQ)
                        sysmon_task_queue_sched(0, axppmic_task_shut, sc);
 
@@ -333,6 +362,126 @@
        return 1;
 }
 
+static void
+axppmic_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *e)
+{
+       struct axppmic_softc *sc = sme->sme_cookie;
+       const int flags = I2C_F_POLL;
+       uint8_t val, warn_val;
+
+       e->state = ENVSYS_SINVALID;
+
+       iic_acquire_bus(sc->sc_i2c, flags);
+       switch (e->private) {
+       case AXP_SENSOR_BATT_PRESENT:
+               if (axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_POWER_MODE_REG, &val, flags) == 0) {
+                       if (val & AXP_POWER_MODE_BATT_VALID) {
+                               e->state = ENVSYS_SVALID;
+                               e->value_cur = !!(val & AXP_POWER_MODE_BATT_PRESENT);
+                               break;
+                       }
+               }
+               break;
+       case AXP_SENSOR_BATT_CHARGING:
+               if (axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_POWER_MODE_REG, &val, flags) == 0) {
+                       e->state = ENVSYS_SVALID;
+                       e->value_cur = !!(val & AXP_POWER_MODE_BATT_CHARGING);
+               }
+               break;
+       case AXP_SENSOR_BATT_CHARGE_STATE:
+               if (axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_POWER_MODE_REG, &val, flags) == 0 &&
+                   (val & AXP_POWER_MODE_BATT_VALID) != 0 &&
+                   (val & AXP_POWER_MODE_BATT_PRESENT) != 0 &&
+                   axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATT_CAP_REG, &val, flags) == 0 &&
+                   (val & AXP_BATT_CAP_VALID) != 0 &&
+                   axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATT_CAP_WARN_REG, &warn_val, flags) == 0) {
+                       const u_int warn_thres = __SHIFTOUT(warn_val, AXP_BATT_CAP_WARN_LV1) + 5;
+                       const u_int shut_thres = __SHIFTOUT(warn_val, AXP_BATT_CAP_WARN_LV2);
+
+                       const u_int batt_val = __SHIFTOUT(val, AXP_BATT_CAP_PERCENT);
+                       if (batt_val <= shut_thres) {
+                               e->state = ENVSYS_SCRITICAL;
+                               e->value_cur = ENVSYS_BATTERY_CAPACITY_CRITICAL;
+                       } else if (batt_val <= warn_thres) {
+                               e->state = ENVSYS_SWARNUNDER;
+                               e->value_cur = ENVSYS_BATTERY_CAPACITY_WARNING;
+                       } else {
+                               e->state = ENVSYS_SVALID;
+                               e->value_cur = ENVSYS_BATTERY_CAPACITY_NORMAL;
+                       }
+               }
+               break;
+       case AXP_SENSOR_BATT_CAPACITY:
+               if (axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_POWER_MODE_REG, &val, flags) == 0 &&
+                   (val & AXP_POWER_MODE_BATT_VALID) != 0 &&
+                   (val & AXP_POWER_MODE_BATT_PRESENT) != 0 &&
+                   axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATT_CAP_REG, &val, flags) == 0 &&
+                   (val & AXP_BATT_CAP_VALID) != 0) {
+                       e->state = ENVSYS_SVALID;
+                       e->value_cur = __SHIFTOUT(val, AXP_BATT_CAP_PERCENT);
+               }
+               break;
+       }
+       iic_release_bus(sc->sc_i2c, flags);
+}
+
+static void
+axppmic_attach_battery(struct axppmic_softc *sc)
+{
+       envsys_data_t *e;
+
+       e = &sc->sc_sensor[AXP_SENSOR_BATT_PRESENT];
+       e->private = AXP_SENSOR_BATT_PRESENT;
+       e->units = ENVSYS_INDICATOR;
+       e->state = ENVSYS_SINVALID;
+       strlcpy(e->desc, "battery present", sizeof(e->desc));
+       sysmon_envsys_sensor_attach(sc->sc_sme, e);
+
+       e = &sc->sc_sensor[AXP_SENSOR_BATT_CHARGING];
+       e->private = AXP_SENSOR_BATT_CHARGING;
+       e->units = ENVSYS_BATTERY_CHARGE;
+       e->state = ENVSYS_SINVALID;
+       strlcpy(e->desc, "charging", sizeof(e->desc));
+       sysmon_envsys_sensor_attach(sc->sc_sme, e);
+
+       e = &sc->sc_sensor[AXP_SENSOR_BATT_CHARGE_STATE];
+       e->private = AXP_SENSOR_BATT_CHARGE_STATE;
+       e->units = ENVSYS_BATTERY_CAPACITY;
+       e->flags = ENVSYS_FMONSTCHANGED;
+       e->state = ENVSYS_SVALID;
+       e->value_cur = ENVSYS_BATTERY_CAPACITY_NORMAL;
+       strlcpy(e->desc, "charge state", sizeof(e->desc));
+       sysmon_envsys_sensor_attach(sc->sc_sme, e);
+
+       if (sc->sc_has_fuel_gauge) {
+               e = &sc->sc_sensor[AXP_SENSOR_BATT_CAPACITY];
+               e->private = AXP_SENSOR_BATT_CAPACITY;
+               e->units = ENVSYS_INTEGER;
+               e->state = ENVSYS_SINVALID;
+               e->flags = ENVSYS_FPERCENT;
+               strlcpy(e->desc, "battery percent", sizeof(e->desc));
+               sysmon_envsys_sensor_attach(sc->sc_sme, e);
+       }
+}
+
+static void
+axppmic_attach_sensors(struct axppmic_softc *sc)
+{
+       if (sc->sc_has_battery) {
+               sc->sc_sme = sysmon_envsys_create();
+               sc->sc_sme->sme_name = device_xname(sc->sc_dev);
+               sc->sc_sme->sme_cookie = sc;
+               sc->sc_sme->sme_refresh = axppmic_sensor_refresh;
+               sc->sc_sme->sme_class = SME_CLASS_BATTERY;
+               sc->sc_sme->sme_flags = SME_POLL_ONLY | SME_INIT_REFRESH;
+
+               axppmic_attach_battery(sc);
+
+               sysmon_envsys_register(sc->sc_sme);
+       }
+}
+
+
 static int
 axppmic_match(device_t parent, cfdata_t match, void *aux)
 {
@@ -365,7 +514,8 @@
        sc->sc_i2c = ia->ia_tag;
        sc->sc_addr = ia->ia_addr;
        sc->sc_phandle = ia->ia_cookie;
-       sc->sc_nirq = c->irq_regs;
+       sc->sc_has_battery = c->has_battery;
+       sc->sc_has_fuel_gauge = c->has_fuel_gauge;
 
        aprint_naive("\n");
        aprint_normal(": %s\n", c->name);
@@ -375,7 +525,7 @@
        sysmon_pswitch_register(&sc->sc_smpsw);
 
        iic_acquire_bus(sc->sc_i2c, I2C_F_POLL);
-       for (i = 1; i <= sc->sc_nirq; i++) {
+       for (i = 1; i <= c->irq_regs; i++) {
                irq_mask = 0;
                if (i == 2)
                        irq_mask |= AXP_IRQ2_POKSIRQ;
@@ -393,20 +543,22 @@
            &axppmic_power_funcs);
 
        phandle = of_find_firstchild_byname(sc->sc_phandle, "regulators");
-       if (phandle <= 0)
-               return;
+       if (phandle > 0) {
+               aaa.reg_i2c = sc->sc_i2c;
+               aaa.reg_addr = sc->sc_addr;
+               for (i = 0; i < c->ncontrols; i++) {
+                       const struct axppmic_ctrl *ctrl = &c->controls[i];
+                       child = of_find_firstchild_byname(phandle, ctrl->c_name);
+                       if (child <= 0)
+                               continue;
+                       aaa.reg_ctrl = ctrl;
+                       aaa.reg_phandle = child;
+                       config_found(sc->sc_dev, &aaa, NULL);
+               }
+       }
 
-       aaa.reg_i2c = sc->sc_i2c;
-       aaa.reg_addr = sc->sc_addr;
-       for (i = 0; i < c->ncontrols; i++) {
-               const struct axppmic_ctrl *ctrl = &c->controls[i];
-               child = of_find_firstchild_byname(phandle, ctrl->c_name);
-               if (child <= 0)
-                       continue;
-               aaa.reg_ctrl = ctrl;
-               aaa.reg_phandle = child;
-               config_found(sc->sc_dev, &aaa, NULL);
-       }
+       if (c->has_battery)
+               axppmic_attach_sensors(sc);
 }
 
 static int



Home | Main Index | Thread Index | Old Index