summaryrefslogtreecommitdiff
path: root/firmware/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/drivers')
-rw-r--r--firmware/drivers/axp-pmu.c670
-rw-r--r--firmware/drivers/axp192.c810
2 files changed, 670 insertions, 810 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}
diff --git a/firmware/drivers/axp192.c b/firmware/drivers/axp192.c
deleted file mode 100644
index 3c61d8c533..0000000000
--- a/firmware/drivers/axp192.c
+++ /dev/null
@@ -1,810 +0,0 @@
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 "axp192.h"
23#include "system.h"
24#include "power.h"
25#include "i2c-async.h"
26#include "logf.h"
27
28/*
29 * Direct register access
30 */
31
32int axp_read(uint8_t reg)
33{
34 int ret = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, reg);
35 if(ret < 0)
36 logf("axp: read reg %02x err=%d", reg, ret);
37
38 return ret;
39}
40
41int axp_write(uint8_t reg, uint8_t value)
42{
43 int ret = i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, reg, value);
44 if(ret < 0)
45 logf("axp: write reg %02x err=%d", reg, ret);
46
47 return ret;
48}
49
50int axp_modify(uint8_t reg, uint8_t clr, uint8_t set)
51{
52 int ret = i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, reg, clr, set, NULL);
53 if(ret < 0)
54 logf("axp: modify reg %02x err=%d", reg, ret);
55
56 return ret;
57}
58
59/*
60 * Power supplies: enable/disable, set voltage
61 */
62
63struct axp_supplydata {
64 uint8_t en_reg;
65 uint8_t en_bit;
66 uint8_t volt_reg;
67 uint8_t volt_msb: 4;
68 uint8_t volt_lsb: 4;
69 short min_mV;
70 short step_mV;
71};
72
73static const struct axp_supplydata supplydata[] = {
74 [AXP_SUPPLY_EXTEN] = {
75 .en_reg = AXP_REG_PWRCTL1,
76 .en_bit = 1 << 2,
77 .volt_reg = 0xff, /* undefined */
78 .volt_msb = 0xf,
79 .volt_lsb = 0xf,
80 .min_mV = 0,
81 .step_mV = 0,
82 },
83 [AXP_SUPPLY_DCDC1] = {
84 .en_reg = AXP_REG_PWRCTL2,
85 .en_bit = 1 << 0,
86 .volt_reg = AXP_REG_DCDC1VOLT,
87 .volt_msb = 6,
88 .volt_lsb = 0,
89 .min_mV = 700,
90 .step_mV = 25,
91 },
92 [AXP_SUPPLY_DCDC2] = {
93 .en_reg = AXP_REG_PWRCTL1,
94 .en_bit = 1 << 0,
95 .volt_reg = AXP_REG_DCDC2VOLT,
96 .volt_msb = 5,
97 .volt_lsb = 0,
98 .min_mV = 700,
99 .step_mV = 25,
100 },
101 [AXP_SUPPLY_DCDC3] = {
102 .en_reg = AXP_REG_PWRCTL2,
103 .en_bit = 1 << 1,
104 .volt_reg = AXP_REG_DCDC3VOLT,
105 .volt_msb = 6,
106 .volt_lsb = 0,
107 .min_mV = 700,
108 .step_mV = 25,
109 },
110 [AXP_SUPPLY_LDO2] = {
111 .en_reg = AXP_REG_PWRCTL2,
112 .en_bit = 1 << 2,
113 .volt_reg = AXP_REG_LDO2LDO3VOLT,
114 .volt_msb = 7,
115 .volt_lsb = 4,
116 .min_mV = 1800,
117 .step_mV = 100,
118 },
119 [AXP_SUPPLY_LDO3] = {
120 .en_reg = AXP_REG_PWRCTL2,
121 .en_bit = 1 << 3,
122 .volt_reg = AXP_REG_LDO2LDO3VOLT,
123 .volt_msb = 3,
124 .volt_lsb = 0,
125 .min_mV = 1800,
126 .step_mV = 100,
127 },
128 [AXP_SUPPLY_LDOIO0] = {
129 .en_reg = 0xff, /* undefined */
130 .en_bit = 0,
131 .volt_reg = AXP_REG_GPIO0LDO,
132 .volt_msb = 7,
133 .volt_lsb = 4,
134 .min_mV = 1800,
135 .step_mV = 100,
136 },
137};
138
139void axp_enable_supply(int supply, bool enable)
140{
141 const struct axp_supplydata* data = &supplydata[supply];
142 axp_modify(data->en_reg, data->en_bit, enable ? data->en_bit : 0);
143}
144
145void axp_set_enabled_supplies(unsigned int supply_mask)
146{
147 uint8_t xfer[3];
148 xfer[0] = 0;
149 xfer[1] = AXP_REG_PWRCTL2;
150 xfer[2] = 0;
151
152 for(int i = 0; i < AXP_NUM_SUPPLIES; ++i) {
153 if(!(supply_mask & (1 << i)))
154 continue;
155
156 const struct axp_supplydata* data = &supplydata[i];
157 if(data->en_reg == AXP_REG_PWRCTL1) {
158 xfer[0] |= data->en_bit;
159 xfer[2] |= data->en_bit << 4; /* HACK: work around AXP quirk */
160 } else {
161 xfer[2] |= data->en_bit;
162 }
163 }
164
165 i2c_reg_write(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_PWRCTL1, 3, xfer);
166}
167
168void axp_set_supply_voltage(int supply, int output_mV)
169{
170 const struct axp_supplydata* data = &supplydata[supply];
171 uint8_t mask = (1 << (data->volt_msb - data->volt_lsb + 1)) - 1;
172 uint8_t value = (output_mV - data->min_mV) / data->step_mV;
173 axp_modify(data->volt_reg, mask << data->volt_lsb, value << data->volt_lsb);
174}
175
176/*
177 * ADC control: enable/disable, read
178 */
179
180struct axp_adcdata {
181 uint8_t data_reg;
182 uint8_t en_reg;
183 uint8_t en_bit;
184 int8_t num;
185 int8_t den;
186};
187
188static const struct axp_adcdata adcdata[] = {
189 [AXP_ADC_ACIN_VOLTAGE] = {0x56, AXP_REG_ADCEN1, 1 << 5, 17, 10},
190 [AXP_ADC_ACIN_CURRENT] = {0x58, AXP_REG_ADCEN1, 1 << 4, 5, 8},
191 [AXP_ADC_VBUS_VOLTAGE] = {0x5a, AXP_REG_ADCEN1, 1 << 3, 17, 10},
192 [AXP_ADC_VBUS_CURRENT] = {0x5c, AXP_REG_ADCEN1, 1 << 2, 3, 8},
193 [AXP_ADC_INTERNAL_TEMP] = {0x5e, AXP_REG_ADCEN2, 1 << 7, 0, 0},
194 [AXP_ADC_TS_INPUT] = {0x62, AXP_REG_ADCEN1, 1 << 0, 4, 5},
195 [AXP_ADC_GPIO0] = {0x64, AXP_REG_ADCEN2, 1 << 3, 1, 2},
196 [AXP_ADC_GPIO1] = {0x66, AXP_REG_ADCEN2, 1 << 2, 1, 2},
197 [AXP_ADC_GPIO2] = {0x68, AXP_REG_ADCEN2, 1 << 1, 1, 2},
198 [AXP_ADC_GPIO3] = {0x6a, AXP_REG_ADCEN2, 1 << 0, 1, 2},
199 [AXP_ADC_BATTERY_VOLTAGE] = {0x78, AXP_REG_ADCEN1, 1 << 7, 11, 10},
200 [AXP_ADC_CHARGE_CURRENT] = {0x7a, AXP_REG_ADCEN1, 1 << 6, 1, 2},
201 [AXP_ADC_DISCHARGE_CURRENT] = {0x7c, AXP_REG_ADCEN1, 1 << 6, 1, 2},
202 [AXP_ADC_APS_VOLTAGE] = {0x7e, AXP_REG_ADCEN1, 1 << 1, 7, 5},
203};
204
205void axp_enable_adc(int adc, bool enable)
206{
207 const struct axp_adcdata* data = &adcdata[adc];
208 axp_modify(data->en_reg, data->en_bit, enable ? data->en_bit : 0);
209}
210
211void axp_set_enabled_adcs(unsigned int adc_mask)
212{
213 uint8_t xfer[3];
214 xfer[0] = 0;
215 xfer[1] = AXP_REG_ADCEN2;
216 xfer[2] = 0;
217
218 for(int i = 0; i < AXP_NUM_ADCS; ++i) {
219 if(!(adc_mask & (1 << i)))
220 continue;
221
222 const struct axp_adcdata* data = &adcdata[i];
223 if(data->en_reg == AXP_REG_ADCEN1)
224 xfer[0] |= data->en_bit;
225 else
226 xfer[2] |= data->en_bit;
227 }
228
229 i2c_reg_write(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_ADCEN1, 3, xfer);
230}
231
232int axp_read_adc_raw(int adc)
233{
234 uint8_t data[2];
235 int ret = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR,
236 adcdata[adc].data_reg, 2, data);
237 if(ret < 0) {
238 logf("axp: ADC read failed, err=%d", ret);
239 return INT_MIN;
240 }
241
242 if(adc == AXP_ADC_CHARGE_CURRENT || adc == AXP_ADC_DISCHARGE_CURRENT)
243 return (data[0] << 5) | data[1];
244 else
245 return (data[0] << 4) | data[1];
246}
247
248int axp_conv_adc(int adc, int value)
249{
250 const struct axp_adcdata* data = &adcdata[adc];
251 if(adc == AXP_ADC_INTERNAL_TEMP)
252 return value - 1447;
253 else
254 return data->num * value / data->den;
255}
256
257int axp_read_adc(int adc)
258{
259 int ret = axp_read_adc_raw(adc);
260 if(ret == INT_MIN)
261 return ret;
262
263 return axp_conv_adc(adc, ret);
264}
265
266/*
267 * GPIOs: set function, pull down control, get/set pin level
268 */
269
270struct axp_gpiodata {
271 uint8_t func_reg;
272 uint8_t func_msb: 4;
273 uint8_t func_lsb: 4;
274 uint8_t level_reg;
275 uint8_t level_out: 4;
276 uint8_t level_in: 4;
277};
278
279static const struct axp_gpiodata gpiodata[] = {
280 {AXP_REG_GPIO0FUNC, 2, 0, AXP_REG_GPIOLEVEL1, 0, 4},
281 {AXP_REG_GPIO1FUNC, 2, 0, AXP_REG_GPIOLEVEL1, 1, 5},
282 {AXP_REG_GPIO2FUNC, 2, 0, AXP_REG_GPIOLEVEL1, 2, 6},
283 {AXP_REG_GPIO3GPIO4FUNC, 1, 0, AXP_REG_GPIOLEVEL2, 0, 4},
284 {AXP_REG_GPIO3GPIO4FUNC, 3, 2, AXP_REG_GPIOLEVEL2, 1, 5},
285 {AXP_REG_NRSTO, 7, 6, AXP_REG_NRSTO, 4, 5},
286};
287
288static const uint8_t gpio34funcmap[8] = {
289 [AXP_GPIO_SPECIAL] = 0x0,
290 [AXP_GPIO_OPEN_DRAIN_OUTPUT] = 0x1,
291 [AXP_GPIO_INPUT] = 0x2,
292 [AXP_GPIO_ADC_IN] = 0x3,
293};
294
295static const uint8_t nrstofuncmap[8] = {
296 [AXP_GPIO_SPECIAL] = 0x0,
297 [AXP_GPIO_OPEN_DRAIN_OUTPUT] = 0x2,
298 [AXP_GPIO_INPUT] = 0x3,
299};
300
301void axp_set_gpio_function(int gpio, int function)
302{
303 const struct axp_gpiodata* data = &gpiodata[gpio];
304 int mask = (1 << (data->func_msb - data->func_lsb + 1)) - 1;
305
306 if(gpio == 5)
307 function = nrstofuncmap[function];
308 else if(gpio >= 3)
309 function = gpio34funcmap[function];
310
311 axp_modify(data->func_reg, mask << data->func_lsb, function << data->func_lsb);
312}
313
314void axp_set_gpio_pulldown(int gpio, bool enable)
315{
316 int bit = 1 << gpio;
317 axp_modify(AXP_REG_GPIOPULL, bit, enable ? bit : 0);
318}
319
320int axp_get_gpio(int gpio)
321{
322 const struct axp_gpiodata* data = &gpiodata[gpio];
323 return axp_read(data->level_reg) & (1 << data->level_in);
324}
325
326void axp_set_gpio(int gpio, bool enable)
327{
328 const struct axp_gpiodata* data = &gpiodata[gpio];
329 uint8_t bit = 1 << data->level_out;
330 axp_modify(data->level_reg, bit, enable ? bit : 0);
331}
332
333/*
334 * Charging: set charging current, query charging/input status
335 */
336
337static const short chargecurrent_tbl[] = {
338 100, 190, 280, 360,
339 450, 550, 630, 700,
340 780, 880, 960, 1000,
341 1080, 1160, 1240, 1320,
342};
343
344void axp_set_charge_current(int current_mA)
345{
346 /* find greatest charging current not exceeding requested current */
347 unsigned int index = 0;
348 while(index < ARRAYLEN(chargecurrent_tbl)-1 &&
349 chargecurrent_tbl[index+1] <= current_mA)
350 ++index;
351
352 axp_modify(AXP_REG_CHGCTL1, BM_AXP_CHGCTL1_CHARGE_CURRENT,
353 index << BP_AXP_CHGCTL1_CHARGE_CURRENT);
354}
355
356int axp_get_charge_current(void)
357{
358 int value = axp_read(AXP_REG_CHGCTL1);
359 if(value < 0)
360 value = 0;
361
362 value &= BM_AXP_CHGCTL1_CHARGE_CURRENT;
363 value >>= BP_AXP_CHGCTL1_CHARGE_CURRENT;
364 return chargecurrent_tbl[value];
365}
366
367void axp_set_vbus_limit(int mode)
368{
369 const int mask = BM_AXP_VBUSIPSOUT_VHOLD_LIM |
370 BM_AXP_VBUSIPSOUT_VBUS_LIM |
371 BM_AXP_VBUSIPSOUT_LIM_100mA;
372
373 axp_modify(AXP_REG_VBUSIPSOUT, mask, mode);
374}
375
376void axp_set_vhold_level(int vhold_mV)
377{
378 if(vhold_mV < 4000)
379 vhold_mV = 4000;
380 else if(vhold_mV > 4700)
381 vhold_mV = 4700;
382
383 int level = (vhold_mV - 4000) / 100;
384 axp_modify(AXP_REG_VBUSIPSOUT, BM_AXP_VBUSIPSOUT_VHOLD_LEV,
385 level << BP_AXP_VBUSIPSOUT_VHOLD_LEV);
386}
387
388bool axp_is_charging(void)
389{
390 int value = axp_read(AXP_REG_CHGSTS);
391 return (value >= 0) && (value & BM_AXP_CHGSTS_CHARGING);
392}
393
394unsigned int axp_power_input_status(void)
395{
396 unsigned int state = 0;
397 int value = axp_read(AXP_REG_PWRSTS);
398 if(value >= 0) {
399 /* ACIN is the main charger. Includes USB */
400 if(value & BM_AXP_PWRSTS_ACIN_VALID)
401 state |= POWER_INPUT_MAIN_CHARGER;
402
403 /* Report USB separately if discernable from ACIN */
404 if((value & BM_AXP_PWRSTS_VBUS_VALID) &&
405 !(value & BM_AXP_PWRSTS_PCB_SHORTED))
406 state |= POWER_INPUT_USB_CHARGER;
407 }
408
409#ifdef HAVE_BATTERY_SWITCH
410 /* If target allows switching batteries then report if the
411 * battery is present or not */
412 value = axp_read(AXP_REG_CHGSTS);
413 if(value >= 0 && (value & BM_AXP_CHGSTS_BATT_PRESENT))
414 state |= POWER_INPUT_BATTERY;
415#endif
416
417 return state;
418}
419
420/*
421 * Misc. functions
422 */
423
424void axp_power_off(void)
425{
426 axp_modify(AXP_REG_PWROFF, BM_AXP_PWROFF_SHUTDOWN, BM_AXP_PWROFF_SHUTDOWN);
427}
428
429/*
430 * Debug menu
431 */
432
433#ifndef BOOTLOADER
434#include "action.h"
435#include "list.h"
436#include "splash.h"
437#include <stdio.h>
438
439/* enable extra debug menus which are only useful for development,
440 * allow potentially dangerous operations and increase code size
441 * significantly */
442/*#define AXP_EXTRA_DEBUG*/
443
444enum {
445 MODE_ADC,
446#ifdef AXP_EXTRA_DEBUG
447 MODE_SUPPLY,
448 MODE_REGISTER,
449#endif
450 NUM_MODES,
451};
452
453static const char* const axp_modenames[NUM_MODES] = {
454 [MODE_ADC] = "ADCs",
455#ifdef AXP_EXTRA_DEBUG
456 [MODE_SUPPLY] = "Power supplies",
457 [MODE_REGISTER] = "Register viewer",
458#endif
459};
460
461struct axp_adcdebuginfo {
462 const char* name;
463 const char* unit;
464};
465
466static const struct axp_adcdebuginfo adc_debuginfo[AXP_NUM_ADCS] = {
467 [AXP_ADC_ACIN_VOLTAGE] = {"V_acin", "mV"},
468 [AXP_ADC_ACIN_CURRENT] = {"I_acin", "mA"},
469 [AXP_ADC_VBUS_VOLTAGE] = {"V_vbus", "mV"},
470 [AXP_ADC_VBUS_CURRENT] = {"I_vbus", "mA"},
471 [AXP_ADC_INTERNAL_TEMP] = {"T_int", "C"},
472 [AXP_ADC_TS_INPUT] = {"V_ts", "mV"},
473 [AXP_ADC_GPIO0] = {"V_gpio0", "mV"},
474 [AXP_ADC_GPIO1] = {"V_gpio1", "mV"},
475 [AXP_ADC_GPIO2] = {"V_gpio2", "mV"},
476 [AXP_ADC_GPIO3] = {"V_gpio3", "mV"},
477 [AXP_ADC_BATTERY_VOLTAGE] = {"V_batt", "mV"},
478 [AXP_ADC_CHARGE_CURRENT] = {"I_chrg", "mA"},
479 [AXP_ADC_DISCHARGE_CURRENT] = {"I_dchg", "mA"},
480 [AXP_ADC_APS_VOLTAGE] = {"V_aps", "mV"},
481};
482
483#ifdef AXP_EXTRA_DEBUG
484static const char* supply_names[AXP_NUM_SUPPLIES] = {
485 [AXP_SUPPLY_EXTEN] = "EXTEN",
486 [AXP_SUPPLY_DCDC1] = "DCDC1",
487 [AXP_SUPPLY_DCDC2] = "DCDC2",
488 [AXP_SUPPLY_DCDC3] = "DCDC3",
489 [AXP_SUPPLY_LDO2] = "LDO2",
490 [AXP_SUPPLY_LDO3] = "LDO3",
491 [AXP_SUPPLY_LDOIO0] = "LDOIO0",
492};
493
494struct axp_fieldinfo {
495 uint8_t rnum;
496 uint8_t msb: 4;
497 uint8_t lsb: 4;
498};
499
500enum {
501#define DEFREG(name, ...) AXP_RNUM_##name,
502#include "axp192-defs.h"
503 AXP_NUM_REGS,
504};
505
506enum {
507#define DEFFLD(regname, fldname, ...) AXP_FNUM_##regname##_##fldname,
508#include "axp192-defs.h"
509 AXP_NUM_FIELDS,
510};
511
512static const uint8_t axp_regaddr[AXP_NUM_REGS] = {
513#define DEFREG(name, addr) addr,
514#include "axp192-defs.h"
515};
516
517static const struct axp_fieldinfo axp_fieldinfo[AXP_NUM_FIELDS] = {
518#define DEFFLD(regname, fldname, _msb, _lsb, ...) \
519 {.rnum = AXP_RNUM_##regname, .msb = _msb, .lsb = _lsb},
520#include "axp192-defs.h"
521};
522
523static const char* const axp_regnames[AXP_NUM_REGS] = {
524#define DEFREG(name, ...) #name,
525#include "axp192-defs.h"
526};
527
528static const char* const axp_fldnames[AXP_NUM_FIELDS] = {
529#define DEFFLD(regname, fldname, ...) #fldname,
530#include "axp192-defs.h"
531};
532#endif /* AXP_EXTRA_DEBUG */
533
534struct axp_debug_menu_state {
535 int mode;
536#ifdef AXP_EXTRA_DEBUG
537 int reg_num;
538 int field_num;
539 int field_cnt;
540 uint8_t cache[AXP_NUM_REGS];
541 uint8_t is_cached[AXP_NUM_REGS];
542#endif
543};
544
545#ifdef AXP_EXTRA_DEBUG
546static void axp_debug_clear_cache(struct axp_debug_menu_state* state)
547{
548 memset(state->is_cached, 0, sizeof(state->is_cached));
549}
550
551static int axp_debug_get_rnum(uint8_t addr)
552{
553 for(int i = 0; i < AXP_NUM_REGS; ++i)
554 if(axp_regaddr[i] == addr)
555 return i;
556
557 return -1;
558}
559
560static uint8_t axp_debug_read(struct axp_debug_menu_state* state, int rnum)
561{
562 if(state->is_cached[rnum])
563 return state->cache[rnum];
564
565 int value = axp_read(axp_regaddr[rnum]);
566 if(value < 0)
567 return 0;
568
569 state->is_cached[rnum] = 1;
570 state->cache[rnum] = value;
571 return value;
572}
573
574static void axp_debug_get_sel(const struct axp_debug_menu_state* state,
575 int item, int* rnum, int* fnum)
576{
577 if(state->reg_num >= 0 && state->field_num >= 0) {
578 int i = item - state->reg_num;
579 if(i <= 0) {
580 /* preceding register is selected */
581 } else if(i <= state->field_cnt) {
582 /* field is selected */
583 *rnum = state->reg_num;
584 *fnum = i + state->field_num - 1;
585 return;
586 } else {
587 /* subsequent regiser is selected */
588 item -= state->field_cnt;
589 }
590 }
591
592 /* register is selected */
593 *rnum = item;
594 *fnum = -1;
595}
596
597static int axp_debug_set_sel(struct axp_debug_menu_state* state, int rnum)
598{
599 state->reg_num = rnum;
600 state->field_num = -1;
601 state->field_cnt = 0;
602
603 for(int i = 0; i < AXP_NUM_FIELDS; ++i) {
604 if(axp_fieldinfo[i].rnum != rnum)
605 continue;
606
607 state->field_num = i;
608 do {
609 state->field_cnt++;
610 i++;
611 } while(axp_fieldinfo[i].rnum == rnum);
612 break;
613 }
614
615 return rnum;
616}
617#endif /* AXP_EXTRA_DEBUG */
618
619static const char* axp_debug_menu_get_name(int item, void* data,
620 char* buf, size_t buflen)
621{
622 struct axp_debug_menu_state* state = data;
623 int value;
624
625 /* for safety */
626 buf[0] = '\0';
627
628 if(state->mode == MODE_ADC && item < AXP_NUM_ADCS)
629 {
630 const struct axp_adcdebuginfo* info = &adc_debuginfo[item];
631 value = axp_read_adc(item);
632 if(item == AXP_ADC_INTERNAL_TEMP) {
633 snprintf(buf, buflen, "%s: %d.%d %s",
634 info->name, value/10, value%10, info->unit);
635 } else {
636 snprintf(buf, buflen, "%s: %d %s", info->name, value, info->unit);
637 }
638 }
639#ifdef AXP_EXTRA_DEBUG
640 else if(state->mode == MODE_SUPPLY && item < AXP_NUM_SUPPLIES)
641 {
642 const struct axp_supplydata* data = &supplydata[item];
643 int en_rnum = axp_debug_get_rnum(data->en_reg);
644 int volt_rnum = axp_debug_get_rnum(data->volt_reg);
645 bool enabled = false;
646 int voltage = -1;
647
648 if(en_rnum >= 0) {
649 value = axp_debug_read(state, en_rnum);
650 if(value & data->en_bit)
651 enabled = true;
652 else
653 enabled = false;
654 } else if(item == AXP_SUPPLY_LDOIO0) {
655 value = axp_debug_read(state, AXP_RNUM_GPIO0FUNC);
656 if((value & 0x7) == AXP_GPIO_SPECIAL)
657 enabled = true;
658 else
659 enabled = false;
660 }
661
662 if(volt_rnum >= 0) {
663 voltage = axp_debug_read(state, volt_rnum);
664 voltage >>= data->volt_lsb;
665 voltage &= (1 << (data->volt_msb - data->volt_lsb + 1)) - 1;
666
667 /* convert to mV */
668 voltage = data->min_mV + voltage * data->step_mV;
669 }
670
671 if(enabled && voltage >= 0) {
672 snprintf(buf, buflen, "%s: %d mV",
673 supply_names[item], voltage);
674 } else {
675 snprintf(buf, buflen, "%s: %sabled",
676 supply_names[item], enabled ? "en" : "dis");
677 }
678 }
679 else if(state->mode == MODE_REGISTER)
680 {
681 int rnum, fnum;
682 axp_debug_get_sel(state, item, &rnum, &fnum);
683
684 if(fnum >= 0) {
685 const struct axp_fieldinfo* info = &axp_fieldinfo[fnum];
686 value = axp_debug_read(state, info->rnum);
687 value >>= info->lsb;
688 value &= (1 << (info->msb - info->lsb + 1)) - 1;
689 snprintf(buf, buflen, "\t%s: %d (0x%x)",
690 axp_fldnames[fnum], value, value);
691 } else if(rnum < AXP_NUM_REGS) {
692 value = axp_debug_read(state, rnum);
693 snprintf(buf, buflen, "%s: 0x%02x", axp_regnames[rnum], value);
694 }
695 }
696#endif /* AXP_EXTRA_DEBUG */
697
698 return buf;
699}
700
701static int axp_debug_menu_cb(int action, struct gui_synclist* lists)
702{
703 struct axp_debug_menu_state* state = lists->data;
704
705 if(state->mode == MODE_ADC)
706 {
707 /* update continuously */
708 if(action == ACTION_NONE)
709 action = ACTION_REDRAW;
710 }
711#ifdef AXP_EXTRA_DEBUG
712 else if(state->mode == MODE_REGISTER)
713 {
714 if(action == ACTION_STD_OK) {
715 /* expand a register to show its fields */
716 int rnum, fnum;
717 int sel_pos = gui_synclist_get_sel_pos(lists);
718 axp_debug_get_sel(state, sel_pos, &rnum, &fnum);
719 if(fnum < 0 && rnum < AXP_NUM_REGS) {
720 int delta_items = -state->field_cnt;
721 if(rnum != state->reg_num) {
722 if(rnum > state->reg_num)
723 sel_pos += delta_items;
724
725 axp_debug_set_sel(state, rnum);
726 delta_items += state->field_cnt;
727 } else {
728 state->reg_num = -1;
729 state->field_num = -1;
730 state->field_cnt = 0;
731 }
732
733 gui_synclist_set_nb_items(lists, lists->nb_items + delta_items);
734 gui_synclist_select_item(lists, sel_pos);
735 action = ACTION_REDRAW;
736 }
737 }
738 }
739 else if(state->mode == MODE_SUPPLY)
740 {
741 /* disable a supply... use with caution */
742 if(action == ACTION_STD_CONTEXT) {
743 int sel_pos = gui_synclist_get_sel_pos(lists);
744 axp_enable_supply(sel_pos, false);
745 }
746 }
747#endif
748
749#ifdef AXP_EXTRA_DEBUG
750 /* clear register cache to refresh values */
751 if(state->mode != MODE_ADC && action == ACTION_STD_CONTEXT) {
752 splashf(HZ/2, "Refreshed");
753 axp_debug_clear_cache(state);
754 action = ACTION_REDRAW;
755 }
756#endif
757
758 /* mode switching */
759 if(action == ACTION_STD_MENU) {
760 state->mode = (state->mode + 1) % NUM_MODES;
761 gui_synclist_set_title(lists, (char*)axp_modenames[state->mode], Icon_NOICON);
762 action = ACTION_REDRAW;
763
764 switch(state->mode) {
765 case MODE_ADC:
766 gui_synclist_set_nb_items(lists, AXP_NUM_ADCS);
767 gui_synclist_select_item(lists, 0);
768 break;
769
770#ifdef AXP_EXTRA_DEBUG
771 case MODE_SUPPLY:
772 axp_debug_clear_cache(state);
773 gui_synclist_set_nb_items(lists, AXP_NUM_SUPPLIES);
774 gui_synclist_select_item(lists, 0);
775 break;
776
777 case MODE_REGISTER:
778 state->reg_num = -1;
779 state->field_num = -1;
780 state->field_cnt = 0;
781 axp_debug_clear_cache(state);
782 gui_synclist_set_nb_items(lists, AXP_NUM_REGS);
783 gui_synclist_select_item(lists, 0);
784 break;
785#endif
786 }
787 }
788
789 return action;
790}
791
792bool axp_debug_menu(void)
793{
794 struct axp_debug_menu_state state;
795 state.mode = MODE_ADC;
796#ifdef AXP_EXTRA_DEBUG
797 state.reg_num = -1;
798 state.field_num = -1;
799 state.field_cnt = 0;
800 axp_debug_clear_cache(&state);
801#endif
802
803 struct simplelist_info info;
804 simplelist_info_init(&info, (char*)axp_modenames[state.mode],
805 AXP_NUM_ADCS, &state);
806 info.get_name = axp_debug_menu_get_name;
807 info.action_callback = axp_debug_menu_cb;
808 return simplelist_show_list(&info);
809}
810#endif