Ramblings of an aging IT geek
← Ramblings of an aging IT geek
hardware

what's actually happening on the CC line when your charger negotiates

A weekend spent probing the CC line of a USB-C PD charger with a logic analyser, decoding the BMC-encoded handshake, and understanding why my laptop sometimes only charged at 15W.

A soldering iron and electronics on a bench

This started as an annoyance. One particular USB-C charger would only ever give my laptop 15W, despite being rated for 65W, and despite the same laptop happily pulling 65W from a different brick. The cable was fine. The wall was fine. Something in the negotiation was going wrong, and the only way to find out what was to listen in on the conversation.

USB Power Delivery is a real protocol with a real handshake, and it all happens on a single wire: the CC (Configuration Channel) line. Before any high voltage or high current flows, the source and the sink talk to each other and agree on what's safe. Get a probe on that line and you can watch the whole negotiation.

The physical layer is the fun part

PD messages on the CC line are sent using BMC, Biphase Mark Coding, at around 300kHz. The trick of BMC is that a transition happens at the start of every bit period, and a one has an extra transition in the middle whilst a zero does not. That makes it self-clocking: you don't need a separate clock line, because the clock is baked into the edges. It also means a flat stretch of signal is a zero and a busy stretch is a one, which you can almost read by eye once you know the trick.

I put a logic analyser on the CC line at 24 MS/s, which is plenty for a 300kHz signal, and triggered on the first edge after plugging in. The capture is dense but legible.

A circuit board under a probe

Decoding BMC by hand is tedious, so I let the software do it. Sigrok's PulseView has a USB PD decoder stack that will take the raw edges, recover the BMC bits, assemble them into packets, check the CRC, and hand you decoded messages. The first time it lit up with a clean Source_Capabilities message I may have made a noise.

What the messages actually say

The handshake, once decoded, reads like a polite negotiation:

Source -> Sink : Source_Capabilities
  PDO 1: Fixed  5V  3.00A
  PDO 2: Fixed  9V  3.00A
  PDO 3: Fixed 15V  3.00A
  PDO 4: Fixed 20V  3.25A
Sink   -> Source : Request (PDO 4, 20V 3.25A)
Source -> Sink : Accept
Source -> Sink : PS_RDY

The source advertises a menu of Power Data Objects, the fixed voltage/current pairs it can supply. The sink picks one by index, the source accepts, and once it has actually ramped the voltage it sends PS_RDY to say the new voltage is live. Only then does the laptop start pulling serious current. It's a careful little protocol, and it has to be, because the alternative is putting 20V across a device that only asked for 5.

On the good charger, my laptop requested PDO 4, got 20V at 3.25A, and pulled its 65W. Clean. On the misbehaving charger, the decode told the story immediately.

The bug

The bad charger's Source_Capabilities only advertised three PDOs, and the highest was 9V 1.67A. That's 15W. My laptop wasn't being throttled. It was being offered nothing better and taking the best on the menu, exactly as designed.

So why did a 65W-rated brick advertise only 15W? I cracked it open. It was a multi-port charger, and the high-power PDOs were gated by which combination of ports had something plugged in. With a device in the second port, the controller dropped the first port to a 15W profile so the total stayed within budget. Entirely sensible behaviour, completely undocumented on the casing, and invisible until you read the actual capabilities message.

# the offending capabilities, second port occupied
PDO 1: Fixed  5V 3.00A   (15W)
PDO 2: Fixed  9V 1.67A   (15W)

I'd been blaming the cable for weeks.

Worth the afternoon

The fix was trivial once I understood it: don't share the charger when I need full speed. But the understanding was the point. USB-C has a reputation for being a confusing soup of cables that may or may not do what you want, and a lot of that confusion is just an invisible negotiation going on that you were never shown.

A cheap logic analyser and an open-source decoder turn that invisible conversation into plain text. If you've ever had a charger that "should" work and doesn't, the answer is almost always sitting right there in the Source_Capabilities message, waiting for someone to listen.