BBB Battery shutdown

When a battery is connected to a beaglebone black, how does the software know to issue a shutdown when power is no longer coming in ? More specifically I’m interested in which file / script performs this action, and what mechanism triggers this behavior.

My intentions are to modify / customize what actually happens when power to the board is battery only.

Also from what I’ve read this behavior is different between console and LXDE images. So if this is true I understand that. I do not want the behaviors that each of these images provides, but wish to customize my own.

I’m not sure, but best place to look would be Documentation/power/regulator/charger-management.txt. I believe the PMIC issues event when AC is removed.


So I’ve only found this so far.

Which I pretty much had already figured out this morning right after I posted. Pretty much, the PMIC sees a condition, that needs attention. It writes some values to registers that relate to the given condition, and then sends an NMI out to the am335x processor. Where the am335x processor immediately picks up that the PMIC needs attention( the whole point of an NMI ), reads the register values out of the PMIC to determine what action needs to be taken. Which in the case of the power button being pressed. the am335x issues a shutdown now -h ( Linux ) Which looking at the code, actually seems like the LKM is actually writing to the PMIC registers to do this ?!

Anyway, no idea how a power good condition is being acted on still. Meaning, no idea how when a battery is connected, when the external power somehow goes missing. How that particular shutdown is happening, and from where.

So no one has any idea ? I’m looking for the module, that traps power events, and shuts down the BBB. All I need is a file name. It’s pretty hard making sense of the mess that is /drivers/mfd, and the documentation does not seem to be helpful either.

Documentation/power/regulator/charger-management.txt does not exist in my repo, nor in Linus’ repo either. There is a similar file, but nothing apparently related to our hardware. Passed that, most of the stuff int the Documentation/power directory seems to be related to ACPI, which again, has nothing to do with even our architecture . . .

Oops, I mistyped: Documentation/power/charger-management.txt. Sorry about that.


From /kernel/power/poweroff.c

  • When the user hits Sys-Rq o to power down the machine this is the
  • callback we use

Which then call the following sequence:

kernel_power_off() //kernel/reboot.c
machine_power_off() //kernel/reboot.c
omap_rtc_power_off() //drivers/rtc/rtc-omap.c

This is where the power gets turned off. Hopefully this is a good place to start to track down the event that occurs when the AC power is plugged in or removed.

Hope this helps.


So in the TPS65217 datasheet, Section 9.3.7, it says in interrupt is generated when pushbutton is pressed/released and USB and AC voltage status change.
Look at the interrupt handler and see how this is implemented.


The best person to ask would be Nishanth Menon <> or Anilkumar Ch <


Never mind, I found it.

drivers/mfd/tps65217.c line 164


I guess I should have read your explanation more closely. OK the interrupt routing issues a key input which replicates the power button being pushed. Not sure if this is what you want, or if you want to stop this from happening when when the AC power is removed.


Nothing there at line 164, then on line 165 starts a function. Which contains the code I linked to yesterday. Which is the power push button code. Which again, is not helping . . . So let’s walk through that function. Which happens in this order . …

variable type definitions declarations.

conditional check to find device tree definition node.

test chip_id, return if failure.

attempt and test to allocate memory for the tps object.

attempt to map the tps object to the PMIC registers

mfd device add, and test device add.

attempt to read the PMIC registers, and test return code.

Here it gets a bit fuzzy however, it seems as though the code is attempting to reset the system by setting various register bits in the PMIC ?! I know all about the registers of the PMIC, or rather can find otu what is what really quick by reading the datasheet which I have right in front of me. That is not what I’m having an issue grasping. It seems to me that a shutdown in this manner would not be clean. However, it could be that setting these register bits as such may not cause an immediate power down. That is what I find unclear.

/* Set the PMIC to shutdown on PWR_EN toggle */
if (status_off) {
ret = tps65217_set_bits(tps, TPS65217_REG_STATUS,
if (ret)
dev_warn(tps->dev, “unable to set the status OFF\n”);

The comment above the code seems to indicate that PWR_EN is causing a shutdown based on being toggle. However, PWR_EN is NOT a button, or even a line connected to a button. From the data sheet . . .


Enable input for DCDC1, 2, 3 converters and LDO1, 2, 3, 4. Pull this pin high to start the
power-up sequence.

In the schematic, PWR_EN is not connected to any button. Period. The button is connected to PB_IN.

Anyway, the rest of the code is inconsequential.

OK, I see the problem. The file I was looking at is different to the one you linked to. I am using the 4.1.13-ti-r33 kernel, which means that you are missing a patch. Build your own kernel and then look at this file again.


I have to say, this code is very painful reading. Every single bit I’ve read so far is circular code in nature. I’ve yet to find a bit of code that actually does anything obvious. Meaning, someone has attempted to be clever by creating function, after function that calls yet more functions written by the same author.

So basically you spend all day sifting through worthless code, and potentially never being the wiser . . . Unless these chained functions don’t go too deep, and you’re actually able to make snese of the code. Before you forget why you started reading the code to begin with . …

I’ll NOT be using a TI kernel, period.

The patched code is easier to read


static irqreturn_t tps65217_irq(int irq, void *irq_data)
struct tps65217 *tps = irq_data;
unsigned int int_reg = 0, status_reg = 0;

tps65217_reg_read(tps, TPS65217_REG_INT, &int_reg);
tps65217_reg_read(tps, TPS65217_REG_STATUS, &status_reg);
if (status_reg)
dev_dbg(tps->dev, “status now: 0x%X\n”, status_reg);

if (!int_reg)
return IRQ_NONE;

if (int_reg & TPS65217_INT_PBI) {
/* Handle push button /
dev_dbg(tps->dev, “power button status change\n”);
input_report_key(tps->pwr_but, KEY_POWER,
status_reg & TPS65217_STATUS_PB);
if (int_reg & TPS65217_INT_ACI) {
Handle AC power status change /
dev_dbg(tps->dev, “AC power status change\n”);
Press KEY_POWER when AC not present /
input_report_key(tps->pwr_but, KEY_POWER,
~status_reg & TPS65217_STATUS_ACPWR);
if (int_reg & TPS65217_INT_USBI) {
Handle USB power status change */
dev_dbg(tps->dev, “USB power status change\n”);



Apply this patch:

From 51b4d415971bb052a26938791337d1050bf94748 Mon Sep 17 00:00:00 2001

Yeah I recognize that code from source code not written by TI employees. The file is called tps65217_charger.c, and is written by an employee of another company.

Anyway, I think we’re going to blow this off. The idea was to wait around without power for 5 minutes, to see if power comes back up. Before issuing a shutdown. Then, on the power up end, using a simple R/C circuit to ramp up voltage to 5v over a specific time period.

By the way, my local source tree is already patched with Robert’s BBB patches.

The real reason why our source trees do not match up. My source tree is based on 4.1.x, and yours seems to be 3.8.x. The patch you showed above would probably botch up my source tree . . .