Better diff at the end thanks to jca's eyeballing, see comments inline.
kettenis: I see room for improvement in our subsystems and their
interactions, but I don't think the current situation is bad enough to
leave those bits out for now.
Feedback? OK?
On Mon, Mar 22, 2021 at 01:21:02AM +0100, Jeremie Courreges-Anglas wrote:
> > Index: dev/fdt/cwfg.c
> > ===================================================================
> > RCS file: /cvs/src/sys/dev/fdt/cwfg.c,v
> > retrieving revision 1.1
> > diff -u -p -r1.1 cwfg.c
> > --- dev/fdt/cwfg.c 10 Jun 2020 17:51:21 -0000 1.1
> > +++ dev/fdt/cwfg.c 20 Mar 2021 23:43:13 -0000
> > @@ -32,12 +32,15 @@
> > #include <sys/malloc.h>
> > #include <sys/sensors.h>
> >
> > +#include <machine/apmvar.h>
> > #include <machine/fdt.h>
> >
> > #include <dev/ofw/openfirm.h>
> >
> > #include <dev/i2c/i2cvar.h>
> >
> > +#include "apm.h"
> > +
> > #define VERSION_REG 0x00
> > #define VCELL_HI_REG 0x02
> > #define VCELL_HI_MASK 0x3f
> > @@ -119,6 +122,18 @@ struct cfdriver cwfg_cd = {
> > NULL, "cwfg", DV_DULL
> > };
> >
> > +int cwfg_apminfo(struct apm_power_info *info);
>
> Why out of the #if NAPM > 0 check?
Copied over from the loongson driver; the declaration doesn't hurt
outside but isn't needed either. I've removed it.
> > +#if NAPM > 0
> > +struct apm_power_info cwfg_power;
> > +
> > +int
> > +cwfg_apminfo(struct apm_power_info *info)
> > +{
> > + bcopy(&cwfg_power, info, sizeof(*info));
>
> There's no overlap between source and destination, better use memcpy.
> (Else, better use memmove.)
Right, I also copied this over from loongson (we can dust that one off
as well later on).
>
> > + return 0;
> > +}
> > +#endif
> > +
> > int
> > cwfg_match(struct device *parent, void *match, void *aux)
> > {
> > @@ -202,6 +217,12 @@ cwfg_attach(struct device *parent, struc
> >
> > sensor_task_register(sc, cwfg_update_sensors, 5);
> >
> > +#if NAPM > 0
> > + /* make sure we have the apm state initialized before apm attaches */
> > + cwfg_update_sensors(sc);
> > + apm_setinfohook(cwfg_apminfo);
> > +#endif
> > +
> > printf("\n");
> > }
> >
> > @@ -324,6 +345,7 @@ cwfg_update_sensors(void *arg)
> > uint32_t vcell, rtt, tmp;
> > uint8_t val;
> > int error, n;
> > + u_char bat_percent;
> >
> > if ((error = cwfg_lock(sc)) != 0)
> > return;
> > @@ -348,6 +370,7 @@ cwfg_update_sensors(void *arg)
> > if ((error = cwfg_read(sc, SOC_HI_REG, &val)) != 0)
> > goto done;
> > if (val != 0xff) {
> > + bat_percent = val;
> > sc->sc_sensor[CWFG_SENSOR_SOC].value = val * 1000;
> > sc->sc_sensor[CWFG_SENSOR_SOC].flags &= ~SENSOR_FINVALID;
> > }
>
> If val == 0xff bat_percent is unitialized. Note that in this error
> condition the sensors code leaves sc->sc_sensor[CWFG_SENSOR_SOC].value
> alone.
Oops. Both `val' and `rtt' can be "useless" and we could end up with
a partially updated `cwfg_power'.
If we always reset all values up front and then update whatever is
possible, we can avoid the intermediate variable completely and end up
with `cwfg_power' providing consistent readings.
>
> > @@ -363,6 +386,14 @@ cwfg_update_sensors(void *arg)
> > sc->sc_sensor[CWFG_SENSOR_RTT].value = rtt;
> > sc->sc_sensor[CWFG_SENSOR_RTT].flags &= ~SENSOR_FINVALID;
> > }
> > +
> > +#if NAPM > 0
> > + cwfg_power.battery_state = bat_percent > sc->sc_alert_level ?
> > + APM_BATT_HIGH : APM_BATT_LOW;
>
> It's cool that this driver keeps track of the "alert level". acpibat(4)
> also does that on amd64, but the apm(4)-through-acpi(4) userland
> interface just hardcodes levels:
>
> /* running on battery */
> pi->battery_life = remaining * 100 / capacity;
> if (pi->battery_life > 50)
> pi->battery_state = APM_BATT_HIGH;
> else if (pi->battery_life > 25)
> pi->battery_state = APM_BATT_LOW;
> else
> pi->battery_state = APM_BATT_CRITICAL;
>
> Maybe the levels reported by hardware couldn't be trusted? Or maybe
> those hardcoded values were deemed good enough, back then. Anyway, on
> this new hardware I think it makes sense to just trust the exported
> values and later act if we get reports.
Yes, I explicitly want to use what hardware provides and we can still
tweak it to more sensible values if need be.
>
> > + cwfg_power.ac_state = APM_AC_UNKNOWN;
>
> This kinda points out the need for an AC driver on this platform.
> If done in another driver, the code will need to be changed. But this
> looks acceptable to me for now.
Yup.
>
> > + cwfg_power.battery_life = bat_percent;
>
> So to keep apm and sensors in sync I think it would be better to reuse
> the cached sc->sc_sensor[CWFG_SENSOR_SOC].value.
I did `sc->sc_sensor[CWFG_SENSOR_SOC].value / 1000' first but ended up
with bogus values, hence the `bat_percent' buffer to avoid arithmetics.
> I don't know whether the error condition mentioned above happens
> frequently with this driver. Reporting APM_BATT_ABSENT (and similar for
> sensors) would be useful, and could be done in a subsequent step.
But that isn't the case? From apm(4):
APM_BATTERY_ABSENT
No battery installed.
The driver just can't tell us enough, but the battery is still there
(we get good percentage/liftime readings), so
APM_BATT_UNKNOWN
Cannot read the current battery state.
is only appropiate here imho.
> What's the underlying hardware, does it involve a pluggable battery?
Nope, Pinebook Pro with one internal battery and AC.
Index: cwfg.c
===================================================================
RCS file: /cvs/src/sys/dev/fdt/cwfg.c,v
retrieving revision 1.1
diff -u -p -r1.1 cwfg.c
--- cwfg.c 10 Jun 2020 17:51:21 -0000 1.1
+++ cwfg.c 22 Mar 2021 16:36:47 -0000
@@ -32,12 +32,15 @@
#include <sys/malloc.h>
#include <sys/sensors.h>
+#include <machine/apmvar.h>
#include <machine/fdt.h>
#include <dev/ofw/openfirm.h>
#include <dev/i2c/i2cvar.h>
+#include "apm.h"
+
#define VERSION_REG 0x00
#define VCELL_HI_REG 0x02
#define VCELL_HI_MASK 0x3f
@@ -119,6 +122,17 @@ struct cfdriver cwfg_cd = {
NULL, "cwfg", DV_DULL
};
+#if NAPM > 0
+struct apm_power_info cwfg_power;
+
+int
+cwfg_apminfo(struct apm_power_info *info)
+{
+ memcpy(info, &cwfg_power, sizeof(*info));
+ return 0;
+}
+#endif
+
int
cwfg_match(struct device *parent, void *match, void *aux)
{
@@ -202,6 +216,12 @@ cwfg_attach(struct device *parent, struc
sensor_task_register(sc, cwfg_update_sensors, 5);
+#if NAPM > 0
+ /* make sure we have the apm state initialized before apm attaches */
+ cwfg_update_sensors(sc);
+ apm_setinfohook(cwfg_apminfo);
+#endif
+
printf("\n");
}
@@ -325,6 +345,14 @@ cwfg_update_sensors(void *arg)
uint8_t val;
int error, n;
+#if NAPM > 0
+ /* reset previous for consistent information */
+ cwfg_power.battery_state = APM_BATT_UNKNOWN;
+ cwfg_power.ac_state = APM_AC_UNKNOWN;
+ cwfg_power.battery_life = 0;
+ cwfg_power.minutes_left = -1;
+#endif
+
if ((error = cwfg_lock(sc)) != 0)
return;
@@ -350,6 +378,11 @@ cwfg_update_sensors(void *arg)
if (val != 0xff) {
sc->sc_sensor[CWFG_SENSOR_SOC].value = val * 1000;
sc->sc_sensor[CWFG_SENSOR_SOC].flags &= ~SENSOR_FINVALID;
+#if NAPM > 0
+ cwfg_power.battery_state = val > sc->sc_alert_level ?
+ APM_BATT_HIGH : APM_BATT_LOW;
+ cwfg_power.battery_life = val;
+#endif
}
/* RTT */
@@ -362,6 +395,9 @@ cwfg_update_sensors(void *arg)
if (rtt != 0x1fff) {
sc->sc_sensor[CWFG_SENSOR_RTT].value = rtt;
sc->sc_sensor[CWFG_SENSOR_RTT].flags &= ~SENSOR_FINVALID;
+#if NAPM > 0
+ cwfg_power.minutes_left = rtt;
+#endif
}
done: