“Putting a BSD system in your pocket”
PMIC and battery charging
The information presented on these pages is NOT intended to be followed as a guide to installing OpenBSD on your own Pinephone device, and must not be used for this purpose.
Unlike most SBCs, the Pinephone contains a rechargeable battery intended to power the device. Correct configuration of the charging circuits, including various safety features such as thermal protection will not be enabled by the current OpenBSD kernel as of the time of writing.
Article navigation
The Exotic Silicon website is available in ten themes, but you haven't chosen one yet!
First steps towards battery charging
With sxirsb now working on the Pinephone, thanks to a patch posted to the openbsd-arm mailing list on 20220110, the axp803 PMIC device which is connected to the RSB can now be accessed. This is a vital step towards getting battery charging working, as the PMIC, or Power Management Integrated Circuit, is what allows us to start and stop charging, monitor charge levels and temperatures, and do various other power-related things.
Not being able to charge the battery whilst booted into the OpenBSD kernel was actually making development quite tedious, as even when connected to AC power, the battery would slowly discharge. Booting into a Linux kernel just to charge the device might not sound too inconvenient, but there was a hidden caveat.
On a few occasions, I allowed battery to became deeply discharged, to the point where the Pinephone wouldn't power-on using battery power alone. In this state, it was still perfectly possible to boot into OpenBSD when connected to AC power, although of course, the battery would remain at 0%. Booting a typical Linux system on the Pinephone whilst it was in this state usually resulted in the device repeatedly resetting itself a few seconds into the boot sequence. This was most likely occurring at the point where the PMIC was being configured, as switching the device to battery power even momentarily during PMIC configuration, would have failed due to an undervoltage condition. As a result, the battery would never charge. Although strictly speaking the device wasn't bricked, this situation might be difficult for un-experienced users to recover from.
No luck with the simple approach
Since there is a bit in one of the control registers that enables or disables battery charging, we might expect that just flipping it to the enabled state would make things work. However, for various reasons, it wasn't that simple. In fact, booting the Pinephone with AC power connected, the bit was already set to enable charging anyway, but it has been clear for some time from looking at the readings of hw.sensors.axppmic0 that the battery wasn't being charged:
Running on battery power
hw.sensors.axppmic0.current0=0.00 A (battery charging current)
hw.sensors.axppmic0.current1=0.59 A (battery discharging current)
With AC connected
hw.sensors.axppmic0.current0=0.00 A (battery charging current)
hw.sensors.axppmic0.current1=0.02 A (battery discharging current)
Running on battery power, we see a current draw of 590 mA, which is within the expected range.
With AC connected, not only does the battery not charge, but we still see a small discharge current.
Temperature sensors preventing charging
Like many PMIC devices, the axp803 provides a function to monitor battery temperature and take some action if it exceeds certain configurable thresholds. Charging a healthy battery in an air-conditioned office, we wouldn't expect the temperature sensor to detect or report any problems, and yet this is exactly what was happening.
The actual sensor is essentially just a thermistor located within the battery itself, and connected to an ADC input on the PMIC. The axp803 basically has two modes of operation with regards to this ADC input. It can either be configured to monitor it automatically, and take actions when the input falls outside the configured threshold values, or it can be configured to simply report the ADC value so that it can be monitored by software.
The two approaches have different advantages and disadvantages. Obviously in the case of hardware monitoring, we don't need any specific OS support for the PMIC, and would hope that unsafe temperature events would be caught and swiftly dealt with, even if the operating system kernel crashed. The main advantage of monitoring in software is increased flexibility.
Powering up after all power sources were previously disconnected, the axp803 is configured to actively monitor the input, and has some default threshold values set. Although the threshold values should really be calibrated for the particular hardware in use, we wouldn't really expect the values reported by the battery under normal circumstances to fall outside of the pre-defined range.
However they do, and this is because the thermistor used in the Pinephone batteries is a 3 Kohm device, whereas the expected impedance would be 10 Kohm. This mis-match causes the values measured by the ADC in the PMIC to be way below the default minimum threshold set in the axp803. This in turn triggers an under-temperature condition, and charging is not allowed.
Testing the hypothesis the quick and obvious way
To check that the under-temperature condition from the battery temperature sensor was indeed the cause of the Pinephone not charging, I temporarily switched the axp803 into the mode of operation in which it treats the battery temperature input as just a general purpose ADC input.
The output from hw.sensors.axppmic0 immediately showed that the battery was charging:
After disabing automatic temperature monitoring
hw.sensors.axppmic0.current0=0.29 A (battery charging current)
hw.sensors.axppmic0.current1=0.00 A (battery discharging current)
hw.sensors.axppmic0.indicator0=On (ACIN), OK
hw.sensors.axppmic0.indicator1=On (VBUS), OK
hw.sensors.axppmic0.indicator2=On (battery present), OK
hw.sensors.axppmic0.indicator3=On (battery charging), OK
To double-check that this wasn't just spurious reporting, and that the battery was in fact charging, I left the Pinephone connected for a few minutes and observed the battery charge percentage indicator increasing.
Investigating the underlying problem
Since we've now got the battery charging whilst booted into the OpenBSD kernel, a naïve attitude might be to consider the problem solved. However, it's not. We've only managed to charge the battery by disabling the temperature monitoring, and the implications of that should be immediately obvious - if the battery overheats and becomes dangerously hot, the PMIC will continue to charge it. This could eventually lead to the battery igniting, and the risk of a lithium battery fire is a serious hazard which we can't ignore.
Interestingly, many of the Linux distributions which have been prepared for the Pinephone have carried a patch to the kernel that effectively does exactly this, simply disabling the internal monitoring of the ADC input by the axp803.
The ADC values are 12-bit, giving us a range of 4096 values. With the correct hardware, typical operating temperatures will deflect over a large amount of this range, allowing fairly fine-grained measurements. In contrast, the 3 Kohm thermistor produces a much more restricted range of values, reducing the possible granularity of our measurements.
Unfortunately, this makes finding a good solution in software alone difficult, and the most obvious way to solve the problem would be to correct the mis-matched impedance of the thermistor.
Nevertheless, through trial and error I set what seemed like reasonable thresholds, and re-enabled the temperature monitoring. The battery started charging, and I left it to reach 100%.
Further observations and tweaks
When the battery charge level reached 99%, it stayed at this point for some considerable time. However, it did eventually change to 100%, at which point charging stopped, as reported by hw.sensors.axppmic0:
Once the battery charge level reached 100%, charging stopped
hw.sensors.axppmic0.volt0=4.19 VDC (battery voltage)
hw.sensors.axppmic0.current0=0.00 A (battery charging current)
hw.sensors.axppmic0.current1=0.02 A (battery discharging current)
hw.sensors.axppmic0.indicator0=On (ACIN), OK
hw.sensors.axppmic0.indicator1=On (VBUS), OK
hw.sensors.axppmic0.indicator2=On (battery present), OK
hw.sensors.axppmic0.indicator3=Off (battery charging)
hw.sensors.axppmic0.percent0=100.00% (battery percent), OK
Again, to double-check that the PMIC had indeed stopped charging the battery, I continued checking the output from hw.sensors.axppmic0 at regular intervals. After about 90 minutes, the battery percentage started to drop:
About 90 minutes later, still on AC
hw.sensors.axppmic0.volt0=4.18 VDC (battery voltage)
hw.sensors.axppmic0.current0=0.00 A (battery charging current)
hw.sensors.axppmic0.current1=0.02 A (battery discharging current)
hw.sensors.axppmic0.indicator0=On (ACIN), OK
hw.sensors.axppmic0.indicator1=On (VBUS), OK
hw.sensors.axppmic0.indicator2=On (battery present), OK
hw.sensors.axppmic0.indicator3=Off (battery charging)
hw.sensors.axppmic0.percent0=99.00% (battery percent), OK
After a further 58 minutes, it dropped by another one percent:
Almost another hour later, and still on AC
hw.sensors.axppmic0.volt0=4.18 VDC (battery voltage)
hw.sensors.axppmic0.current0=0.00 A (battery charging current)
hw.sensors.axppmic0.current1=0.02 A (battery discharging current)
hw.sensors.axppmic0.indicator0=On (ACIN), OK
hw.sensors.axppmic0.indicator1=On (VBUS), OK
hw.sensors.axppmic0.indicator2=On (battery present), OK
hw.sensors.axppmic0.indicator3=Off (battery charging)
hw.sensors.axppmic0.percent0=98.00% (battery percent), OK
Note that the Pinephone is still connected to AC, and still mostly using the AC power. The device isn't simply running from battery, in which case the discharging current would be significantly higher.
So we now have a way to charge the battery whilst booted into the OpenBSD kernel, and it's being done with the thermal sensor enabled. Admittedly, the thermal sensor likely isn't adequately calibrated yet, and how well it can be made to perform with the mis-matched impedances in the hardware is not entirely clear, but at least we are trying to use the data it provides.
Testing over-temperature detection
In order to realistically test the ability of the PMIC to stop the charging process due to an over-temperature condition, I tried clasping the handset tightly in my hands whilst it was charging, hoping that the slight increase in temperature would be enough to pass the threshold that I'd set and trigger the axp803 to stop charging. However, the reading from the ADC barely changed, and I didn't really want to subject the battery to any more severe testing, so I instead lowered the threshold to be within the normal operating range.
This may not be a completely accurate test, as the behaviour of the PMIC might differ between the ADC input naturally passing a pre-configured threshold, and changing that threshold to suddenly be within the range of the current ADC reading. However, it still seemed like a valid test to perform whilst I thought about possible and practical ways to genuinely overheat the battery under safe and controlled conditions, because if the behaviour of the PMIC does indeed vary between the two scenarios then I would want to know anyway, (since software might change the thresholds in this way, and so if it introduces unexpected or undefined behaviour to the system, we would know what to expect and possibly work around in software).
Setting the threshold to a value within the current operating range did indeed cause the PMIC to stop charging the battery. However, clearing the condition and restoring charging was slightly more complex than I had imagined. Even after removing both the battery and AC power, then re-connecting them, the condition was not automatically cleared.
One way to re-start charging was to set the over-temperature threshold very high. Simply setting it slightly above the operating temperature wasn't enough. After being set to almost the maximum 12-bit value, then reduced to slightly above the typical values measured, the previous behaviour was restored and the battery started charging. This didn't seem like the best, or most reliable, way to clear the condition. The fact that only a very large value written to the axp803 would cause this to happen, and not just any value greater than the current ADC reading, makes me think that it might be undocumented behaviour.
Another, and probably better way, to re-start charging, was to toggle the function of the temperature sensor between being actively monitored by the PMIC in hardware, and simply being an ADC input for monitoring by software, then immediately toggle it back again. This process also seemed to cleared the over-temperature condition, and seems like a better way of doing so.
The complete initialisation sequence
Finally, I settled for the time being on a six-step initialisation sequence to enable charging.
Experimental initialisation sequence used for testing
  • Disable charging
  • Set the PMIC to ignore the measured ADC values
  • Set the PMIC to monitor the measured ADC values
  • Enable the temperature sensor input to the PMIC
  • Set the upper and lower temperature thresholds
  • Enable charging
This has been enough to allow the Pinephone to charge the battery whilst booted into OpenBSD, to do so with the temperature sensors enabled, to stop charging once a charge level of 100% is reached, and to re-start charging after a reboot following a manually initiated over-temperature condition.
Final conclusions on battery charging
Clearly, progress has been made, however there are still some issues left unresolved.
Although the Pinephone charges when connected to a USB-A socket on my workstation, it doesn't recognise a stand-alone USB-C power adaptor. This was probably to be expected, as support for the USB-C charging modes would likely require configuration of the anx7688 chip.
Correctly calibrating the ADC values from the thermal sensor in the battery, and setting appropriate thresholds will be necessary for safe and reliable operation.
Article navigation
Part 7 - External keyboard battery.