2022–01–31: Pinephone
Pro battery charging
If you have trouble chargigng battery on Pinephone Pro, this article may help
you underastand what may be wrong. First, here are a few quick facts you should
know about the Pinephone Pro Type-C port and the power management chip (RK818),
to better understand how it all works together:
- There are two categories of USB power supplies that the phone knows how to
deal with: 1) dumb BC1.2 spec conforming ones 2) USB-PD (power delivery)
- The phone decides how much power it will take from the USB power supply. You
can have 1000W power supply, but if the phone decides it will take 0W or 2.5W,
it will take at maximum those 0W or 2.5W.
- This power input limit needs to somehow decided upon whenever you plug in
some power supply into a Type-C port. The limit then must be configured into the
phone's power management chip. Until this happens, the default limit
is 2.25W.
- This limit is managed automatically by the kernel drivers, and can also be
set manually if you know what you're doing, by writing desired value in uA to
/sys/class/power_supply/rk818-usb/input_current_limit
(eg.
echo 500000 > /sys/class/power_supply/rk818-usb/input_current_limit
)
Input voltage is always 5V, so if you want power limit for 5W you need to divide
it by 5 and multiply by 1,000,000 and write that value to sysfs.
This is basically it. Most charging issues revolve around setting this one
limit. How could that possible be, that there are seemingly so many issues
then? :)
- Whatever the power input limit ends up being, the phone will then split the
available power between two tasks: 1) supplying the power for the
phone's runtime needs 2) charging the battery
- Supplying power to the phone's runtime needs has priority over charging the
battery. This means that if the limit is higher than what the phone needs, then
the rest of the power can be used to charge the battery. If the limit is not
enough, the remaining power will be taken from battery. If the battery can't
supply the difference, the phone will fail to run and shut down.
- Examples:
- limit 0.4W, power needs 3W ⇒ battery needs to provide 2.6W (and will be
thus discharging)
- limit 2.5W, power needs 3W ⇒ battery needs to provide 0.5W
- limit 7.5W, power needs 4W ⇒ battery can be charged using up to 3.5W
- limit 15W, power needs 3W ⇒ battery can be charged using up to 12W
- Runtime power needs of the phone change a lot depending on the
phone's utilization. With idle CPU and screen off the phone needs ~1.7W. With
Screen on it may need ~2.8W depending on brightness. With full CPU load it may
need 5W. Whenever you fully utilize the wifi, or modem does some power intensive
work, you may get other large multi-watt power spikes on top.
- Power management chip has one stupid feature: it can't turn off the phone
when the phone is connected to a USB power supply. If you try to turn it off, it
will try to power up and boot the phone again.
So as you can see, if the phone decides it will take no power from the USB
power supply, there will simple be not enough power to charge the
phone's battery, or even to boot and run the phone. All the charging issues
revolve around this one variable: power input limit.
How then does the phone decide what power input limit to set?
Phone startup process
- When you connect USB power supply to the phone, the initial power input
limit will be set to 2.25W.
- Initial power consumption of the phone, with screen off and very light CPU
usage of the bootloader fits into this power envelope, even with a completely
dicharged battery.
- With no software on the phone whatsoever (erased eMMC/SPI falsh/no SD card),
the phone will enter maskrom USB flashing mode and power needs of the phone
remain in the 2.25W limit indefinitely.
- In this state, the battery can very slowly charge at the rate of about
0.5–0.7W (so with 11Wh battery, the full charge cycle will take at least 20h
to complete)
- (What's missing here is any probing of the USB power supply, to determine
how much power it can provide. The default limit is just assumed. So how/when
does this happen? Read on!)
You most probably have some software on the phone, right? :) Therfore:
- Bootloader is started, and it starts stressing the CPU, loading up large
amounts of data from eMMC or SD card at max speed it can configure it too, and
at this moment if the power requirements spike above 2.25W for a brief moment,
the system will fail and will be forcefully powered off.
- This will happen only if you don't have the battery in the phone, or if the
battery is fully discharged. Otherwise the excess power needs are satisfied from
the battery, and there's no problem.
- In a sane world, power management chip would simply keep the system powered
off, and would now have 2.25W of power available to charge the battery at quite
a reasonable speed (5h to full charge!).
- But RK818 power management chip will not keep the system off. Because USB
power supply is connected to the phone, it will try to power up the phone again.
(and now you can go to step 1 and you're getting a boot loop and very little
charging going on)
Let's assume all went well up to this point. Next:
- Linux is started, it will power up all performance CPU cores and will start
using them. It will turn on the display, etc. The power needs are through the
roof! There will be 6–7W spikes, no problem.
- The boot will fail at this point if you have the battery fully discharged,
or if you removed it from the phone.
- If you still have some juice in the battery, only now 3–10 seconds after
the Linux starts there will be finally some chance for the system to probe and
detect the type of your USB power supply, and update the power input limit to
match it. Quite late, eh? That's because all this detection is done in kernel
drivers.
So how does this detection work?
- The phone has a Type-C port. Type-C ports are much more complicated than
regular micro USB ports. They have 24 pins and carry 10 signal lines.
Micro-USB port carries 3 signals and has 5 pins for comparison.
- You can use a USB power delivery charger, that comes with a Type-C port of
its own instead of a A port. Power delivery chargers are smart! :) The phone
can talk to them, they respond to questions, follow orders, etc. Questions like
how much power you have there? They use only CC pins for this.
- You can also use a regular old dumb USB charger. These cahrgers are really
not very smart. All they do is that they use USB 2.0 data signals (D+/D-) to
tell the phone basically: „Duuh, I'm like a chaaarger.“ or „Naah, I'm like
a PC USB port“ and not much else.
- So in summary, dumb chargers use USB2.0 D+/D- signals. PD chargers use
Type-C CC pins and a full blown packetized communication protocol.
- Connecting a dumb charger with its dumb 4-pin USB-A connector to a smart
24-pin Type-C port of a smart phone will not make the charger any smarter.
It's still as dumb as ever.
- Because this Type-C stuff is all very complicated, the whole situation needs
to be handled by at least 7 different drivers, which all need to coordinate
what they do somehow, to achieve satisfactory outcomes.
- Specifically for the power delivery side of the Type-C port, 4 drivers are
cooperating: USB 2.0 phy driver (takes care of D+/D- pins), generic type c port
manager (tcpm, implements a 500 page power delivery specification), fusb302
(chip specific backend for tcpm, handles CC pins), and pmic driver (rk818-usb,
talks to the PMIC to tell it what input current limit to set, when all is said
and done).
This is where it gets complicated. Dumb charger detection and Type-C port
management are two independent processes with independent hardware signals.
Nevertheless, the output of the detection process has to be just one number
(power input limit). So these processes have to meet somewhere to decide which
one is right and which one will choose the final value for the limit.
- On hardware level this is implemented in the USB A-to-C cable which is used
to connect Type-C devices to USB A ports. USB A ports don't implement the CC
based signals, but the cable itself does, so that when you connect smart Type-C
device into a legacy USB-A port, the phone can detect that situation.
- On software level, TCPM will notice that you connected something to the
phone (because of change on CC pins) and will start the detailed detection
process, to see what was connected. At the same time, USB 2.0 PHY driver will
detect that there's VBUS present, or that there's a change on D+/D- pins, and
will start its own detection process. These will run concurrently.
So if you:
- use a smart (PD) charger, TCPM will be able to chat with it and ask it what
amount of power it can supply, and tell the PMIC driver limit it should set.
Information from USB 2.0 PHY driver is not needed in this case.
- use a dumb charger, TCPM will detect that a USB A-to-C cable is being used,
and that means that legacy charger detection should be used, because on the
other end of the cable is some USB-A device that doesn't understand any of the
new Type-C protocols and standards. TCPM driver will need to rely on the USB
2.0 phy driver to detect the charger type according to the older USB Battery
Charging v1.2 specification.
And this is where most of the software issues were or are.
Type-C software support issues
Let's list some of those issues that I know about at this time:
- First major issue, that will have no very satisfactory solution is that all
this exceedingly complicated stuff is implemented only in Linux drivers. If you
can't successfully boot Linux, no proper charger detection for you, sorry.
- On other rockchip based battery powered devices supporting Type-C port (this
is only Pinebook Pro and Google Chromebooks) there are either even worse issues
(Pinebook Pro), or in case of Chromebooks, this whole detection dance is
implemented by the embedded processor, so it always works fine, regardless of
what OS you run, or whether you're able to boot some OS at all.
- Second major issue is that RK818 PMIC, whenever it fails to get enough power
for the phone will not just shut down and let you charge, but will instead enter
a power draining bootloop if connected to a charger.
- This can be worked around by some software interventions. The only simple
and safe one that makes sense to me is to make bootloader prevent boot, make
sure to not load the CPU too much or enable anything that's power hungry, blink
a LED to indicate something's going on, reduce power consumption as much as
possible and let the battery charge with the default limit of 2.25W, until it
reaches enough charge to be able to boot Linux, and do a proper charger
detection ther.
- And there was/is a long trail of smaller bugs in my kernel patches and in
mainline code:
- TCPM waits only short amount of time after boot for USB 2.0 phy to detect
dumb charger type. When the result was not ready in 800ms, it would set input
current limit to 0, cutting off the phone from all outside power. Pretty much
anything you're able to connect the pinephone pro to, will be able to provide
2.5W, so this was unnecessary, and prevented even modest charging from
happening.
- After boot, USB 2.0 PHY driver used an intentional delay of 6 seconds
before trying to detect the dumb charger type. Combined with the TPCM's short
timout, this ensured the detection results would never be considered right after
boot, but only after re-plugging the charger while the Linux is running.
- fusb302 still has some bugs where it gets stuck and stops all TCPM detection
from ever happening until reboot. This sometimes happens when connecting
peripherals to the Type-C port (dock, hub, etc.).
- Some distributions decided to merge my in-development Type-C support code
for Pinephone Pro into their kernel tree, but don't track the fixes and updates
very well by monitoring my changelog, so some things may be fixed in my kernel
tree only and not in distribution kernels.
- DWC3 USB driver never powers down the USB phy, so the phy is never
re-configured in response to eg. changing Type-C plug orientation, because the
driver only does configuration on phy power on. Downstream Rockchip kernel has
code that handles this more properly.