Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 59 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,36 @@ bmr --bus /dev/i2c-1 --addr 0x40 <command> [subcommand] [--pretty-off|P]
* `--addr` 7-bit device address (default: `0x40`).
* `--pretty-off|P` disable pretty output

## save — save current configuration

```bash
bmr ... save
```

### What it does

Save all modifications to be permanent for next power cycle or call to restart
command.

## restore — restore configuration

```bash
bmr ... restore [default]
```

### What it does

Reload and apply factory default configuration if default is set, otherwise
reload the last saved configuration i.e. similar to call to the restart command.

### Use case

Drop the current change:

```bash
bmr --bus /dev/i2c-1 --addr 0x40 restore
```

## read — Telemetry & sensor data

```bash
Expand Down Expand Up @@ -202,22 +232,20 @@ margins/timing profiles.

```bash
bmr ... user-data get
bmr ... user-data set --ascii "Hello" [--store]
bmr ... user-data set --ascii "Hello"
```

### What it does

Accesses vendor `USER_DATA_00` (0xB0) area for storing small notes or process
metadata (e.g., calibration token). With `--store`, the tool persists changes
via `STORE_USER_ALL` or device-specific store, observing NVM latency. See App
Note 302 for STORE/RESTORE semantics and timing.
metadata (e.g., calibration token).

### Use case

Stamp a unit with a calibration tag, manufacturing information:

```bash
bmr ... user-data set --ascii "Cal=OK@2025-11-09" --store
bmr ... user-data set --ascii "Cal=OK@2025-11-09"
```

Wait ~5–10 ms before re-reading to allow NVM write to complete.
Expand Down Expand Up @@ -605,7 +633,7 @@ Set typical limits and verify live sensors:
bmr --bus /dev/i2c-1 --addr 0x40 temp set --ot-fault 110 --ot-warn 100 --ut-warn -20 --ut-fault -40

# Persist if desired (device NVM):
bmr --bus /dev/i2c-1 --addr 0x40 user-data set --store
bmr --bus /dev/i2c-1 --addr 0x40 save

# Read back limits and live temps
bmr --bus /dev/i2c-1 --addr 0x40 temp get all
Expand All @@ -616,7 +644,7 @@ bmr --bus /dev/i2c-1 --addr 0x40 temp read all

```bash
bmr ... fault get [all|temp|vin|vout|tonmax|iout]
bmr ... fault temp set \
bmr ... fault set temp \
[--ot-delay 16s|32s|2^n] [--ot-mode ignore|delay-retry|disable-retry|disable-until-clear] [--ot-retries 0..6|cont] \
[--ut-delay 16s|32s|2^n] [--ut-mode ignore|delay-retry|disable-retry|disable-until-clear] [--ut-retries 0..6|cont]
```
Expand All @@ -638,7 +666,7 @@ Each response byte packs three fields:
* **VIN/VOUT/TON_MAX/IOUT**: typically **10 ms/LSB** on BMR45x (see device spec).

`fault get` decodes mode/retries/delay with proper units per family.
`fault temp set` programs the **OT/UT** response bytes with friendly arguments.
`fault set temp` programs the **OT/UT** response bytes with friendly arguments.

### Use case — 1s off, single retry on OT/UT (with temperature thresholds)

Expand All @@ -647,23 +675,23 @@ temperature limits to create the fault. Below sets both:

**Program the OT/UT fault-response policy** (disable, wait 16s, retry once):
```bash
bmr --bus /dev/i2c-1 --addr 0x40 fault temp set \
bmr --bus /dev/i2c-1 --addr 0x40 fault set temp \
--ot-mode disable-retry --ot-retries 1 --ot-delay 16s \
--ut-mode disable-retry --ut-retries 1 --ut-delay 16s
```

**Set temperature thresholds** (example production-style values; adjust to your design):

```bash
bmr --bus /dev/i2c-1 --addr 0x40 temp set \
bmr --bus /dev/i2c-1 --addr 0x40 set temp \
--ot-fault 110 --ot-warn 100 \
--ut-warn -20 --ut-fault -40
```

**Persist to NVM** (optional, if you want the policy/limits after power cycle):

```bash
bmr --bus /dev/i2c-1 --addr 0x40 user-data set --store
bmr --bus /dev/i2c-1 --addr 0x40 save
```

**Verify**:
Expand All @@ -688,13 +716,13 @@ Force a fault immediately (pick one):

```bash
# Force OT now (set OT below current temp, e.g., if T1 ~ 25 °C):
bmr --bus /dev/i2c-1 --addr 0x40 temp set --ot-fault 20
bmr --bus /dev/i2c-1 --addr 0x40 set temp --ot-fault 20

# or, Force UT now (set UT above current temp):
bmr --bus /dev/i2c-1 --addr 0x40 temp set --ut-fault 30
bmr --bus /dev/i2c-1 --addr 0x40 set temp --ut-fault 30

# Persist to NVM if you want the policy to survive power cycles
#bmr --bus /dev/i2c-1 --addr 0x40 user-data set --store
#bmr --bus /dev/i2c-1 --addr 0x40 save
```

The rail shall shutdown, wait 16s, retry once, then:
Expand All @@ -709,9 +737,25 @@ bmr --bus /dev/i2c-1 --addr 0x40 temp set \
--ot-fault 110 --ot-warn 100 --ut-warn -20 --ut-fault -40

# Persist to NVM if you want the policy to survive power cycles
bmr --bus /dev/i2c-1 --addr 0x40 user-data set --store
bmr --bus /dev/i2c-1 --addr 0x40 save
```

## rw — direct access to a byte / word register

```bash
bmr ... rw get [byte|word] [--cmd 0xHH]
bmr ... rw set [byte|word] [--cmd 0xHH] [--value 0xAAAA]
```

### What it does

Read / write on any command on byte or word format

### Use case

Cover cases to access to a register not cover (or incorrectly done) by a specific command.
Allow to configure any BMR

## Notes & best practices

* **Linear formats**: The tool reads `VOUT_MODE` to scale VOUT and uses
Expand Down
6 changes: 3 additions & 3 deletions src/fault_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ static void
usage_fault(void) {
fprintf(stderr,
"fault get [all|temp|vin|vout|tonmax|iout]\n"
"fault temp set [--ot-delay 16s|32s|2^n] [--ot-mode ignore|delay-retry|disable-retry|disable-until-clear]\n"
"fault set temp [--ot-delay 16s|32s|2^n] [--ot-mode ignore|delay-retry|disable-retry|disable-until-clear]\n"
" [--ot-retries 0..6|cont]\n"
" [--ut-delay 16s|32s|2^n] [--ut-mode ignore|delay-retry|disable-retry|disable-until-clear]\n"
" [--ut-retries 0..6|cont]\n"
Expand Down Expand Up @@ -342,14 +342,14 @@ cmd_fault(int fd, int argc, char *const *argv, int pretty) {
return 0;
}

if (!strcmp(sub, "temp")) {
if (!strcmp(sub, "set")) {
if (argc < 2) {
usage_fault();
return 2;
}

const char *sub2 = argv[1];
if (!strcmp(sub2, "set")) {
if (!strcmp(sub2, "temp")) {
const char *ot_delay = NULL, *ut_delay = NULL;
const char *ot_mode_s = NULL, *ut_mode_s = NULL;
const char *ot_retries_s = NULL, *ut_retries_s = NULL;
Expand Down
23 changes: 22 additions & 1 deletion src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "mfr_ramp_data.h"
#include "mfr_addr_offset.h"
#include "mfr_status_data.h"
#include "mfr_save_restore.h"
#include "timing_cmd.h"
#include "read_cmd.h"
#include "onoff_cmd.h"
Expand All @@ -27,6 +28,7 @@
#include "mfr_fwdata.h"
#include "mfr_restart.h"
#include "mfr_user_data.h"
#include "rw_cmd.h"

#include <jansson.h>
#include <stdio.h>
Expand All @@ -46,13 +48,15 @@ usage(const char *p) {
"\n"
"Commands:\n"
" read [vin|vout|iout|temp1|temp2|duty|freq|all]\n"
" save\n"
" restore [default]\n"
" status\n"
" snapshot [--cycle 0..19] [--decode]\n"
" mfr-multi-pin get|set [--mode MODE] [--pg pushpull|highz] [--pg-enable 0|1] [--sec-rc-pull 0|1]\n"
" id\n"
" fwdata\n"
" restart\n"
" user-data get|set [--hex XX..|--ascii STR] [--store|--restore]\n"
" user-data get|set [--hex XX..|--ascii STR]\n"
" timing get|set [--profile safe|sequenced|fast|prebias]\n"
" fault get [all|temp|vin|vout|tonmax|iout]\n"
" fault temp set [--ot-delay 16s|32s|2^n] [--ot-mode disable-retry] [--ot-retries cont]\n"
Expand Down Expand Up @@ -84,6 +88,8 @@ usage(const char *p) {
" temp get [all|ot|ut|warn]\n"
" temp set [--ot-fault <C>] [--ut-fault <C>] [--ot-warn <C>] [--ut-warn <C>]\n"
" temp read [all|t1|t2|t3]\n"
" rw get [byte|word] [--cmd 0xHH]\n"
" rw set [byte|word] [--cmd 0xHH] [--value 0xAAAA]\n"
"\n"
"Hints:\n"
" * Use '<command> help' where available (e.g., 'hrr help', 'capability help', 'fault help') for detailed docs.\n"
Expand Down Expand Up @@ -272,6 +278,21 @@ main(int argc, char *const *argv) {
goto fini;
}

if (!strcmp(cmd, "rw")) {
rc = cmd_rw(fd, sub_argc, sub_argv, opt_pretty);
goto fini;
}

if (!strcmp(cmd, "save")) {
rc = cmd_save(fd);
goto fini;
}

if (!strcmp(cmd, "restore")) {
rc = cmd_restore(fd, sub_argc, sub_argv);
goto fini;
}

usage(argv[0]);
rc = EXIT_FAILURE;

Expand Down
2 changes: 2 additions & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ sources = [
'mfr_ramp_data.c',
'mfr_status_data.c',
'mfr_addr_offset.c',
'mfr_save_restore.c',
'timing_cmd.c',
'read_cmd.c',
'status_cmd.c',
Expand All @@ -29,6 +30,7 @@ sources = [
'capability_cmd.c',
'fault_cmd.c',
'temp_cmd.c',
'rw_cmd.c',
'util_json.c',
]

Expand Down
53 changes: 53 additions & 0 deletions src/mfr_save_restore.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later */

#include "pmbus_io.h"
#include <stdio.h>
#include <string.h>

int
cmd_save(int fd) {
uint8_t b[64];

// For BMR456 STORE and RESTORE is not based on send byte but on a write byte with a dummy value
// Product version is read to know how to execute the command
pmbus_rd_block(fd, MFR_MODEL, b, (int)sizeof b);

if (strncmp((char *)b, "BMR456", (size_t)6))
pmbus_send_byte(fd, PMBUS_STORE_USER_ALL);
else
pmbus_wr_byte(fd, PMBUS_STORE_USER_ALL, 0x01);

puts("OK");

return 0;
}

int
cmd_restore(int fd, int argc, char *const *argv) {
uint8_t b[64];
bool isDefault = false;

if ((argc) && !strcmp(argv[0], "default"))
isDefault = true;


// For BMR456 STORE and RESTORE is not based on send byte but on a write byte with a dummy value
// Product version is read to know how to execute the command
pmbus_rd_block(fd, MFR_MODEL, b, (int)sizeof b);

if (isDefault) {
if (strncmp((char *)b, "BMR456", (size_t)6))
pmbus_send_byte(fd, PMBUS_RESTORE_DEFAULT_ALL);
else
pmbus_wr_byte(fd, PMBUS_RESTORE_DEFAULT_ALL, 0x01);
} else {
if (strncmp((char *)b, "BMR456", (size_t)6))
pmbus_send_byte(fd, PMBUS_RESTORE_USER_ALL);
else
pmbus_wr_byte(fd, PMBUS_RESTORE_USER_ALL, 0x01);
}

puts("OK");

return 0;
}
5 changes: 5 additions & 0 deletions src/mfr_save_restore.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later */
#pragma once

int cmd_save(int fd);
int cmd_restore(int fd, int argc, char *const *argv);
22 changes: 5 additions & 17 deletions src/mfr_user_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,24 @@

int
cmd_user_data(int fd, int argc, char *const *argv, int pretty) {
uint8_t b[64];

if (argc < 1) {
fprintf(stderr, "user-data get|set ...\n");
return 2;
}

if (!strcmp(argv[0], "get")) {
uint8_t buf[64];
int n = pmbus_rd_block(fd, MFR_USER_DATA_00, buf, sizeof buf);
int n = pmbus_rd_block(fd, MFR_USER_DATA_00, b, sizeof b);
if (n < 0) {
perror("USER_DATA_00");
return 1;
}

json_t *o = json_object();
json_object_set_new(o, "len", json_integer(n));
json_object_set_new(o, "ascii", json_stringn((char *) buf, n));
json_add_hex_ascii(o, "hex", buf, (size_t)n);
json_object_set_new(o, "ascii", json_stringn((char *) b, n));
json_add_hex_ascii(o, "hex", b, (size_t)n);

json_print_or_pretty(o, pretty);

Expand All @@ -36,20 +37,14 @@ cmd_user_data(int fd, int argc, char *const *argv, int pretty) {

if (!strcmp(argv[0], "set")) {
const char *hex = NULL, *ascii = NULL;
bool store = false, restore = false;

for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--hex") && i + 1 < argc)
hex = argv[++i];
else if (!strcmp(argv[i], "--ascii") && i + 1 < argc)
ascii = argv[++i];
else if (!strcmp(argv[i], "--store"))
store = true;
else if (!strcmp(argv[i], "--restore"))
restore = true;
}

uint8_t b[64];
int n = 0;

if (hex) {
Expand Down Expand Up @@ -85,13 +80,6 @@ cmd_user_data(int fd, int argc, char *const *argv, int pretty) {
return 1;
}

if (store)
pmbus_send_byte(fd, PMBUS_STORE_USER_ALL);
if (restore)
pmbus_send_byte(fd, PMBUS_RESTORE_USER_ALL);

puts("OK");

return 0;
}

Expand Down
2 changes: 0 additions & 2 deletions src/read_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ add_read_field(json_t *o, const char *key, int fd, uint8_t reg, enum enc_t enc,
json_object_set_new(o, key, json_integer(w));
break;
}

json_object_set_new(o, key + (sizeof("") - 1), json_object_get(o, key));
}

static json_t *
Expand Down
Loading