summaryrefslogtreecommitdiff
path: root/firmware/drivers/axp-pmu.c
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2022-01-09 16:03:30 +0000
committerAidan MacDonald <amachronic@protonmail.com>2022-01-09 19:58:34 +0000
commiteaee5e7339aec46b4b67f06b139b8708810a4c2e (patch)
tree28bac18b6a68caae4689c712b4b7d2ab80fed583 /firmware/drivers/axp-pmu.c
parent8f063d49c2e1dffb5548e40b69782963e18b1171 (diff)
downloadrockbox-eaee5e7339aec46b4b67f06b139b8708810a4c2e.tar.gz
rockbox-eaee5e7339aec46b4b67f06b139b8708810a4c2e.zip
Revert "AXP PMU rewrite (again)"
This caused LCD problems on the ErosQ, where the screen would go white until being put through a sleep/wake cycle. The exact reason for this isn't obvious, but the problem didn't exist prior to the AXP driver rewrite. The two dependent changes, 42999913ba - x1000: Increase USB current limit to 500 mA at all times 90dd2f84a9 - x1000: Correctly limit USB charging current ended up bringing the USB charging situation back to where it was prior to the rewrite, so the cleanest option is to revert the whole lot. This reverts commit 42999913ba3a76221fceb04b1f935ed4e0e71476. This reverts commit 90dd2f84a9174c38dbfb07d582ec6ee7697b1939. This reverts commit 2d891439623bb76d38b98202ca5f3eea3c01c5f0. Change-Id: I1cff2bfdd1b189df14bcf8cce42db725caa470d7
Diffstat (limited to 'firmware/drivers/axp-pmu.c')
-rw-r--r--firmware/drivers/axp-pmu.c670
1 files changed, 670 insertions, 0 deletions
diff --git a/firmware/drivers/axp-pmu.c b/firmware/drivers/axp-pmu.c
new file mode 100644
index 0000000000..fd1126dbbf
--- /dev/null
+++ b/firmware/drivers/axp-pmu.c
@@ -0,0 +1,670 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "axp-pmu.h"
23#include "power.h"
24#include "system.h"
25#include "i2c-async.h"
26#include <string.h>
27
28/* Headers for the debug menu */
29#ifndef BOOTLOADER
30# include "action.h"
31# include "list.h"
32# include <stdio.h>
33#endif
34
35struct axp_adc_info {
36 uint8_t reg;
37 uint8_t en_reg;
38 uint8_t en_bit;
39};
40
41struct axp_supply_info {
42 uint8_t volt_reg;
43 uint8_t volt_reg_mask;
44 uint8_t en_reg;
45 uint8_t en_bit;
46 int min_mV;
47 int max_mV;
48 int step_mV;
49};
50
51static const struct axp_adc_info axp_adc_info[NUM_ADC_CHANNELS] = {
52 {0x56, AXP_REG_ADCENABLE1, 5}, /* ACIN_VOLTAGE */
53 {0x58, AXP_REG_ADCENABLE1, 4}, /* ACIN_CURRENT */
54 {0x5a, AXP_REG_ADCENABLE1, 3}, /* VBUS_VOLTAGE */
55 {0x5c, AXP_REG_ADCENABLE1, 2}, /* VBUS_CURRENT */
56 {0x5e, AXP_REG_ADCENABLE2, 7}, /* INTERNAL_TEMP */
57 {0x62, AXP_REG_ADCENABLE1, 1}, /* TS_INPUT */
58 {0x78, AXP_REG_ADCENABLE1, 7}, /* BATTERY_VOLTAGE */
59 {0x7a, AXP_REG_ADCENABLE1, 6}, /* CHARGE_CURRENT */
60 {0x7c, AXP_REG_ADCENABLE1, 6}, /* DISCHARGE_CURRENT */
61 {0x7e, AXP_REG_ADCENABLE1, 1}, /* APS_VOLTAGE */
62 {0x70, 0xff, 0}, /* BATTERY_POWER */
63};
64
65static const struct axp_supply_info axp_supply_info[AXP_NUM_SUPPLIES] = {
66#if HAVE_AXP_PMU == 192
67 [AXP_SUPPLY_DCDC1] = {
68 .volt_reg = 0x26,
69 .volt_reg_mask = 0x7f,
70 .en_reg = 0x12,
71 .en_bit = 0,
72 .min_mV = 700,
73 .max_mV = 3500,
74 .step_mV = 25,
75 },
76 [AXP_SUPPLY_DCDC2] = {
77 .volt_reg = 0x23,
78 .volt_reg_mask = 0x3f,
79 .en_reg = 0x10,
80 .en_bit = 0,
81 .min_mV = 700,
82 .max_mV = 2275,
83 .step_mV = 25,
84 },
85 [AXP_SUPPLY_DCDC3] = {
86 .volt_reg = 0x27,
87 .volt_reg_mask = 0x7f,
88 .en_reg = 0x12,
89 .en_bit = 1,
90 .min_mV = 700,
91 .max_mV = 3500,
92 .step_mV = 25,
93 },
94 /*
95 * NOTE: LDO1 is always on, and we can't query it or change voltages
96 */
97 [AXP_SUPPLY_LDO2] = {
98 .volt_reg = 0x28,
99 .volt_reg_mask = 0xf0,
100 .en_reg = 0x12,
101 .en_bit = 2,
102 .min_mV = 1800,
103 .max_mV = 3300,
104 .step_mV = 100,
105 },
106 [AXP_SUPPLY_LDO3] = {
107 .volt_reg = 0x28,
108 .volt_reg_mask = 0x0f,
109 .en_reg = 0x12,
110 .en_bit = 3,
111 .min_mV = 1800,
112 .max_mV = 3300,
113 .step_mV = 100,
114 },
115 [AXP_SUPPLY_LDO_IO0] = {
116 .volt_reg = 0x91,
117 .volt_reg_mask = 0xf0,
118 .en_reg = 0x90,
119 .en_bit = 0xff, /* this one requires special handling */
120 .min_mV = 1800,
121 .max_mV = 3300,
122 .step_mV = 100,
123 },
124#else
125# error "Untested AXP chip"
126#endif
127};
128
129static struct axp_driver {
130 int adc_enable;
131 int chargecurrent_setting;
132 int chip_id;
133} axp;
134
135static void axp_init_enabled_adcs(void)
136{
137 axp.adc_enable = 0;
138
139 /* Read chip ID, so we can display it on the debug screen.
140 * This is undocumented but there's Linux driver code floating around
141 * which suggests this should work for many AXP chips. */
142 axp.chip_id = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_CHIP_ID);
143
144 /* Read enabled ADCs from the hardware */
145 uint8_t regs[2];
146 int rc = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR,
147 AXP_REG_ADCENABLE1, 2, &regs[0]);
148 if(rc != I2C_STATUS_OK)
149 return;
150
151 /* Parse registers to set ADC enable bits */
152 const struct axp_adc_info* info = axp_adc_info;
153 for(int i = 0; i < NUM_ADC_CHANNELS; ++i) {
154 if(info[i].en_reg == 0xff)
155 continue;
156
157 if(regs[info[i].en_reg - AXP_REG_ADCENABLE1] & info[i].en_bit)
158 axp.adc_enable |= 1 << i;
159 }
160
161 /* Handle battery power ADC */
162 if((axp.adc_enable & (1 << ADC_BATTERY_VOLTAGE)) &&
163 (axp.adc_enable & (1 << ADC_DISCHARGE_CURRENT))) {
164 axp.adc_enable |= (1 << ADC_BATTERY_POWER);
165 }
166}
167
168void axp_init(void)
169{
170 axp_init_enabled_adcs();
171
172 /* We need discharge current ADC to reliably poll for a full battery */
173 int bits = axp.adc_enable;
174 bits |= (1 << ADC_DISCHARGE_CURRENT);
175 axp_adc_set_enabled(bits);
176
177 /* Read the maximum charging current */
178 int value = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_CHARGECONTROL1);
179 axp.chargecurrent_setting = (value < 0) ? -1 : (value & 0xf);
180}
181
182void axp_supply_set_voltage(int supply, int voltage)
183{
184 const struct axp_supply_info* info = &axp_supply_info[supply];
185 if(info->volt_reg == 0 || info->volt_reg_mask == 0)
186 return;
187
188 if(voltage > 0 && info->step_mV != 0) {
189 if(voltage < info->min_mV || voltage > info->max_mV)
190 return;
191
192 int regval = (voltage - info->min_mV) / info->step_mV;
193 i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, info->volt_reg,
194 info->volt_reg_mask, regval, NULL);
195 }
196
197 if(info->en_bit != 0xff) {
198 i2c_reg_setbit1(AXP_PMU_BUS, AXP_PMU_ADDR,
199 info->en_reg, info->en_bit,
200 voltage > 0 ? 1 : 0, NULL);
201 }
202}
203
204int axp_supply_get_voltage(int supply)
205{
206 const struct axp_supply_info* info = &axp_supply_info[supply];
207 if(info->volt_reg == 0)
208 return AXP_SUPPLY_NOT_PRESENT;
209
210 if(info->en_reg != 0) {
211 int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, info->en_reg);
212 if(r < 0)
213 return AXP_SUPPLY_DISABLED;
214
215#if HAVE_AXP_PMU == 192
216 if(supply == AXP_SUPPLY_LDO_IO0) {
217 if((r & 7) != 2)
218 return AXP_SUPPLY_DISABLED;
219 } else
220#endif
221 {
222 if(r & (1 << info->en_bit) == 0)
223 return AXP_SUPPLY_DISABLED;
224 }
225 }
226
227 /* Hack, avoid undefined shift below. Can be useful too... */
228 if(info->volt_reg_mask == 0)
229 return info->min_mV;
230
231 int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, info->volt_reg);
232 if(r < 0)
233 return 0;
234
235 int bit = find_first_set_bit(info->volt_reg_mask);
236 int val = (r & info->volt_reg_mask) >> bit;
237 return info->min_mV + (val * info->step_mV);
238}
239
240/* TODO: this can STILL indicate some false positives! */
241int axp_battery_status(void)
242{
243 int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_POWERSTATUS);
244 if(r >= 0) {
245 /* Charging bit indicates we're currently charging */
246 if((r & 0x04) != 0)
247 return AXP_BATT_CHARGING;
248
249 /* Not plugged in means we're discharging */
250 if((r & 0xf0) == 0)
251 return AXP_BATT_DISCHARGING;
252 } else {
253 /* Report discharging if we can't find out power status */
254 return AXP_BATT_DISCHARGING;
255 }
256
257 /* If the battery is full and not in use, the charging bit will be 0,
258 * there will be an external power source, AND the discharge current
259 * will be zero. Seems to rule out all false positives. */
260 int d = axp_adc_read_raw(ADC_DISCHARGE_CURRENT);
261 if(d == 0)
262 return AXP_BATT_FULL;
263
264 return AXP_BATT_DISCHARGING;
265}
266
267int axp_input_status(void)
268{
269#ifdef HAVE_BATTERY_SWITCH
270 int input_status = 0;
271#else
272 int input_status = AXP_INPUT_BATTERY;
273#endif
274
275 int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_POWERSTATUS);
276 if(r < 0)
277 return input_status;
278
279 /* Check for AC input */
280 if(r & 0x80)
281 input_status |= AXP_INPUT_AC;
282
283 /* Only report USB if ACIN and VBUS are not shorted */
284 if((r & 0x20) != 0 && (r & 0x02) == 0)
285 input_status |= AXP_INPUT_USB;
286
287#ifdef HAVE_BATTERY_SWITCH
288 /* Check for battery presence if target defines it as removable */
289 r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_CHARGESTATUS);
290 if(r >= 0 && (r & 0x20) != 0)
291 input_status |= AXP_INPUT_BATTERY;
292#endif
293
294 return input_status;
295}
296
297int axp_adc_read(int adc)
298{
299 int value = axp_adc_read_raw(adc);
300 if(value == INT_MIN)
301 return INT_MIN;
302
303 return axp_adc_conv_raw(adc, value);
304}
305
306int axp_adc_read_raw(int adc)
307{
308 /* Don't give a reading if the ADC is not enabled */
309 if((axp.adc_enable & (1 << adc)) == 0)
310 return INT_MIN;
311
312 /* Read the ADC */
313 uint8_t buf[3];
314 int count = (adc == ADC_BATTERY_POWER) ? 3 : 2;
315 uint8_t reg = axp_adc_info[adc].reg;
316 int rc = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR, reg, count, &buf[0]);
317 if(rc != I2C_STATUS_OK)
318 return INT_MIN;
319
320 /* Parse the value */
321 if(adc == ADC_BATTERY_POWER)
322 return (buf[0] << 16) | (buf[1] << 8) | buf[2];
323 else if(adc == ADC_CHARGE_CURRENT || adc == ADC_DISCHARGE_CURRENT)
324 return (buf[0] << 5) | (buf[1] & 0x1f);
325 else
326 return (buf[0] << 4) | (buf[1] & 0xf);
327}
328
329int axp_adc_conv_raw(int adc, int value)
330{
331 switch(adc) {
332 case ADC_ACIN_VOLTAGE:
333 case ADC_VBUS_VOLTAGE:
334 /* 0 mV ... 6.9615 mV, step 1.7 mV */
335 return value * 17 / 10;
336 case ADC_ACIN_CURRENT:
337 /* 0 mA ... 2.5594 A, step 0.625 mA */
338 return value * 5 / 8;
339 case ADC_VBUS_CURRENT:
340 /* 0 mA ... 1.5356 A, step 0.375 mA */
341 return value * 3 / 8;
342 case ADC_INTERNAL_TEMP:
343 /* -144.7 C ... 264.8 C, step 0.1 C */
344 return value - 1447;
345 case ADC_TS_INPUT:
346 /* 0 mV ... 3.276 V, step 0.8 mV */
347 return value * 4 / 5;
348 case ADC_BATTERY_VOLTAGE:
349 /* 0 mV ... 4.5045 V, step 1.1 mV */
350 return value * 11 / 10;
351 case ADC_CHARGE_CURRENT:
352 case ADC_DISCHARGE_CURRENT:
353 /* 0 mA to 4.095 A, step 0.5 mA */
354 return value / 2;
355 case ADC_APS_VOLTAGE:
356 /* 0 mV to 5.733 V, step 1.4 mV */
357 return value * 7 / 5;
358 case ADC_BATTERY_POWER:
359 /* 0 uW to 23.6404 W, step 0.55 uW */
360 return value * 11 / 20;
361 default:
362 /* Shouldn't happen */
363 return INT_MIN;
364 }
365}
366
367int axp_adc_get_enabled(void)
368{
369 return axp.adc_enable;
370}
371
372void axp_adc_set_enabled(int adc_bits)
373{
374 /* Ignore no-op */
375 if(adc_bits == axp.adc_enable)
376 return;
377
378 /* Compute the new register values */
379 const struct axp_adc_info* info = axp_adc_info;
380 uint8_t regs[2] = {0, 0};
381 for(int i = 0; i < NUM_ADC_CHANNELS; ++i) {
382 if(info[i].en_reg == 0xff)
383 continue;
384
385 if(adc_bits & (1 << i))
386 regs[info[i].en_reg - 0x82] |= 1 << info[i].en_bit;
387 }
388
389 /* These ADCs share an enable bit */
390 if(adc_bits & ((1 << ADC_CHARGE_CURRENT)|(1 << ADC_DISCHARGE_CURRENT))) {
391 adc_bits |= (1 << ADC_CHARGE_CURRENT);
392 adc_bits |= (1 << ADC_DISCHARGE_CURRENT);
393 }
394
395 /* Enable required bits for battery power ADC */
396 if(adc_bits & (1 << ADC_BATTERY_POWER)) {
397 regs[0] |= 1 << info[ADC_DISCHARGE_CURRENT].en_bit;
398 regs[0] |= 1 << info[ADC_BATTERY_VOLTAGE].en_bit;
399 }
400
401 /* Update the configuration */
402 i2c_reg_write(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_ADCENABLE1, 2, &regs[0]);
403 axp.adc_enable = adc_bits;
404}
405
406int axp_adc_get_rate(void)
407{
408 int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_ADCSAMPLERATE);
409 if(r < 0)
410 return AXP_ADC_RATE_100HZ; /* an arbitrary value */
411
412 return (r >> 6) & 3;
413}
414
415void axp_adc_set_rate(int rate)
416{
417 i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_ADCSAMPLERATE,
418 0xc0, (rate & 3) << 6, NULL);
419}
420
421static uint32_t axp_cc_parse(const uint8_t* buf)
422{
423 return ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
424}
425
426void axp_cc_read(uint32_t* charge, uint32_t* discharge)
427{
428 uint8_t buf[8];
429 int rc = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR,
430 AXP_REG_COULOMBCOUNTERBASE, 8, &buf[0]);
431 if(rc != I2C_STATUS_OK) {
432 if(charge)
433 *charge = 0;
434 if(discharge)
435 *discharge = 0;
436 return;
437 }
438
439 if(charge)
440 *charge = axp_cc_parse(&buf[0]);
441 if(discharge)
442 *discharge = axp_cc_parse(&buf[4]);
443}
444
445void axp_cc_clear(void)
446{
447 i2c_reg_setbit1(AXP_PMU_BUS, AXP_PMU_ADDR,
448 AXP_REG_COULOMBCOUNTERCTRL, 5, 1, NULL);
449}
450
451void axp_cc_enable(bool en)
452{
453 i2c_reg_setbit1(AXP_PMU_BUS, AXP_PMU_ADDR,
454 AXP_REG_COULOMBCOUNTERCTRL, 7, en ? 1 : 0, NULL);
455}
456
457bool axp_cc_is_enabled(void)
458{
459 int reg = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR,
460 AXP_REG_COULOMBCOUNTERCTRL);
461 return reg >= 0 && (reg & 0x40) != 0;
462}
463
464static const int chargecurrent_tbl[] = {
465 100, 190, 280, 360,
466 450, 550, 630, 700,
467 780, 880, 960, 1000,
468 1080, 1160, 1240, 1320,
469};
470
471static const int chargecurrent_tblsz = sizeof(chargecurrent_tbl)/sizeof(int);
472
473void axp_set_charge_current(int maxcurrent)
474{
475 /* Find the charge current just higher than maxcurrent */
476 int value = 0;
477 while(value < chargecurrent_tblsz &&
478 chargecurrent_tbl[value] <= maxcurrent)
479 ++value;
480
481 /* Select the next lower current, the greatest current <= maxcurrent */
482 if(value >= chargecurrent_tblsz)
483 value = chargecurrent_tblsz - 1;
484 else if(value > 0)
485 --value;
486
487 /* Don't issue i2c write if desired setting is already in use */
488 if(value == axp.chargecurrent_setting)
489 return;
490
491 /* Update register */
492 i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
493 AXP_REG_CHARGECONTROL1, 0x0f, value, NULL);
494 axp.chargecurrent_setting = value;
495}
496
497int axp_get_charge_current(void)
498{
499 if(axp.chargecurrent_setting < 0)
500 return chargecurrent_tbl[0];
501 else
502 return chargecurrent_tbl[axp.chargecurrent_setting];
503}
504
505void axp_power_off(void)
506{
507 /* Set the shutdown bit */
508 i2c_reg_setbit1(AXP_PMU_BUS, AXP_PMU_ADDR,
509 AXP_REG_SHUTDOWNLEDCTRL, 7, 1, NULL);
510}
511
512#ifndef BOOTLOADER
513enum {
514 AXP_DEBUG_CHIP_ID,
515 AXP_DEBUG_BATTERY_STATUS,
516 AXP_DEBUG_INPUT_STATUS,
517 AXP_DEBUG_CHARGE_CURRENT,
518 AXP_DEBUG_COULOMB_COUNTERS,
519 AXP_DEBUG_ADC_RATE,
520 AXP_DEBUG_FIRST_ADC,
521 AXP_DEBUG_FIRST_SUPPLY = AXP_DEBUG_FIRST_ADC + NUM_ADC_CHANNELS,
522 AXP_DEBUG_NUM_ENTRIES = AXP_DEBUG_FIRST_SUPPLY + AXP_NUM_SUPPLIES,
523};
524
525static int axp_debug_menu_cb(int action, struct gui_synclist* lists)
526{
527 (void)lists;
528
529 if(action == ACTION_NONE)
530 action = ACTION_REDRAW;
531
532 return action;
533}
534
535static const char* axp_debug_menu_get_name(int item, void* data,
536 char* buf, size_t buflen)
537{
538 (void)data;
539
540 static const char* const adc_names[] = {
541 "V_acin", "I_acin", "V_vbus", "I_vbus", "T_int",
542 "V_ts", "V_batt", "I_chrg", "I_dchg", "V_aps", "P_batt"
543 };
544
545 static const char* const adc_units[] = {
546 "mV", "mA", "mV", "mA", "C", "mV", "mV", "mA", "mA", "mV", "uW",
547 };
548
549 static const char* const supply_names[] = {
550 "DCDC1", "DCDC2", "DCDC3",
551 "LDO1", "LDO2", "LDO3", "LDO_IO0",
552 };
553
554 int adc = item - AXP_DEBUG_FIRST_ADC;
555 if(item >= AXP_DEBUG_FIRST_ADC && adc < NUM_ADC_CHANNELS) {
556 int raw_value = axp_adc_read_raw(adc);
557 if(raw_value == INT_MIN) {
558 snprintf(buf, buflen, "%s: [Disabled]", adc_names[adc]);
559 return buf;
560 }
561
562 int value = axp_adc_conv_raw(adc, raw_value);
563 if(adc == ADC_INTERNAL_TEMP) {
564 snprintf(buf, buflen, "%s: %d.%d %s", adc_names[adc],
565 value/10, value%10, adc_units[adc]);
566 } else {
567 snprintf(buf, buflen, "%s: %d %s", adc_names[adc],
568 value, adc_units[adc]);
569 }
570
571 return buf;
572 }
573
574 int supply = item - AXP_DEBUG_FIRST_SUPPLY;
575 if(item >= AXP_DEBUG_FIRST_SUPPLY && supply < AXP_NUM_SUPPLIES) {
576 int voltage = axp_supply_get_voltage(supply);
577 if(voltage == AXP_SUPPLY_NOT_PRESENT)
578 snprintf(buf, buflen, "%s: [Not Present]", supply_names[supply]);
579 else if(voltage == AXP_SUPPLY_DISABLED)
580 snprintf(buf, buflen, "%s: [Disabled]", supply_names[supply]);
581 else
582 snprintf(buf, buflen, "%s: %d mV", supply_names[supply], voltage);
583
584 return buf;
585 }
586
587 switch(item) {
588 case AXP_DEBUG_CHIP_ID: {
589 snprintf(buf, buflen, "Chip ID: %d (%02x) [Driver: AXP%d]",
590 axp.chip_id, axp.chip_id, HAVE_AXP_PMU);
591 return buf;
592 } break;
593
594 case AXP_DEBUG_BATTERY_STATUS: {
595 switch(axp_battery_status()) {
596 case AXP_BATT_FULL:
597 return "Battery: Full";
598 case AXP_BATT_CHARGING:
599 return "Battery: Charging";
600 case AXP_BATT_DISCHARGING:
601 return "Battery: Discharging";
602 default:
603 return "Battery: Unknown";
604 }
605 } break;
606
607 case AXP_DEBUG_INPUT_STATUS: {
608 int s = axp_input_status();
609 const char* ac = (s & AXP_INPUT_AC) ? " AC" : "";
610 const char* usb = (s & AXP_INPUT_USB) ? " USB" : "";
611 const char* batt = (s & AXP_INPUT_BATTERY) ? " Battery" : "";
612 snprintf(buf, buflen, "Inputs:%s%s%s", ac, usb, batt);
613 return buf;
614 } break;
615
616 case AXP_DEBUG_CHARGE_CURRENT: {
617 int current = axp_get_charge_current();
618 snprintf(buf, buflen, "Max charge current: %d mA", current);
619 return buf;
620 } break;
621
622 case AXP_DEBUG_COULOMB_COUNTERS: {
623 uint32_t charge, discharge;
624 axp_cc_read(&charge, &discharge);
625
626 snprintf(buf, buflen, "Coulomb counters: +%lu / -%lu",
627 (unsigned long)charge, (unsigned long)discharge);
628 return buf;
629 } break;
630
631 case AXP_DEBUG_ADC_RATE: {
632 int rate = 25 << axp_adc_get_rate();
633 snprintf(buf, buflen, "ADC sample rate: %d Hz", rate);
634 return buf;
635 } break;
636
637 default:
638 return "---";
639 }
640}
641
642bool axp_debug_menu(void)
643{
644 struct simplelist_info info;
645 simplelist_info_init(&info, "AXP debug", AXP_DEBUG_NUM_ENTRIES, NULL);
646 info.action_callback = axp_debug_menu_cb;
647 info.get_name = axp_debug_menu_get_name;
648 return simplelist_show_list(&info);
649}
650#endif /* !BOOTLOADER */
651
652/* This is basically the only valid implementation, so define it here */
653unsigned int power_input_status(void)
654{
655 unsigned int state = 0;
656 int input_status = axp_input_status();
657
658 if(input_status & AXP_INPUT_AC)
659 state |= POWER_INPUT_MAIN_CHARGER;
660
661 if(input_status & AXP_INPUT_USB)
662 state |= POWER_INPUT_USB_CHARGER;
663
664#ifdef HAVE_BATTERY_SWITCH
665 if(input_status & AXP_INPUT_BATTERY)
666 state |= POWER_INPUT_BATTERY;
667#endif
668
669 return state;
670}