summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--firmware/SOURCES4
-rw-r--r--firmware/drivers/axp-pmu.c670
-rw-r--r--firmware/drivers/axp173.c476
-rw-r--r--firmware/export/axp-pmu.h151
-rw-r--r--firmware/export/axp173.h118
-rw-r--r--firmware/export/config/fiiom3k.h2
-rw-r--r--firmware/target/mips/ingenic_x1000/debug-x1000.c8
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c10
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h4
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c34
10 files changed, 853 insertions, 624 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 052847a6a6..0d93439ff8 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -1929,8 +1929,8 @@ drivers/touchpad.c
1929drivers/i2c-async.c 1929drivers/i2c-async.c
1930#endif 1930#endif
1931 1931
1932#ifdef HAVE_AXP173 1932#ifdef HAVE_AXP_PMU
1933drivers/axp173.c 1933drivers/axp-pmu.c
1934#endif 1934#endif
1935 1935
1936/* firmware/kernel section */ 1936/* firmware/kernel section */
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/axp173.c b/firmware/drivers/axp173.c
deleted file mode 100644
index 0c9b9c81a4..0000000000
--- a/firmware/drivers/axp173.c
+++ /dev/null
@@ -1,476 +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 "axp173.h"
23#include "power.h"
24#include "i2c-async.h"
25
26/* Headers for the debug menu */
27#ifndef BOOTLOADER
28# include "action.h"
29# include "list.h"
30# include <stdio.h>
31#endif
32
33static const struct axp173_adc_info {
34 uint8_t reg;
35 uint8_t en_reg;
36 uint8_t en_bit;
37} axp173_adc_info[NUM_ADC_CHANNELS] = {
38 {0x56, AXP173_REG_ADCENABLE1, 5}, /* ACIN_VOLTAGE */
39 {0x58, AXP173_REG_ADCENABLE1, 4}, /* ACIN_CURRENT */
40 {0x5a, AXP173_REG_ADCENABLE1, 3}, /* VBUS_VOLTAGE */
41 {0x5c, AXP173_REG_ADCENABLE1, 2}, /* VBUS_CURRENT */
42 {0x5e, AXP173_REG_ADCENABLE2, 7}, /* INTERNAL_TEMP */
43 {0x62, AXP173_REG_ADCENABLE1, 1}, /* TS_INPUT */
44 {0x78, AXP173_REG_ADCENABLE1, 7}, /* BATTERY_VOLTAGE */
45 {0x7a, AXP173_REG_ADCENABLE1, 6}, /* CHARGE_CURRENT */
46 {0x7c, AXP173_REG_ADCENABLE1, 6}, /* DISCHARGE_CURRENT */
47 {0x7e, AXP173_REG_ADCENABLE1, 1}, /* APS_VOLTAGE */
48 {0x70, 0xff, 0}, /* BATTERY_POWER */
49};
50
51static struct axp173 {
52 int adc_enable;
53 int chargecurrent_setting;
54} axp173;
55
56static void axp173_init_enabled_adcs(void)
57{
58 axp173.adc_enable = 0;
59
60 /* Read enabled ADCs from the hardware */
61 uint8_t regs[2];
62 int rc = i2c_reg_read(AXP173_BUS, AXP173_ADDR,
63 AXP173_REG_ADCENABLE1, 2, &regs[0]);
64 if(rc != I2C_STATUS_OK)
65 return;
66
67 /* Parse registers to set ADC enable bits */
68 const struct axp173_adc_info* info = axp173_adc_info;
69 for(int i = 0; i < NUM_ADC_CHANNELS; ++i) {
70 if(info[i].en_reg == 0xff)
71 continue;
72
73 if(regs[info[i].en_reg - AXP173_REG_ADCENABLE1] & info[i].en_bit)
74 axp173.adc_enable |= 1 << i;
75 }
76
77 /* Handle battery power ADC */
78 if((axp173.adc_enable & (1 << ADC_BATTERY_VOLTAGE)) &&
79 (axp173.adc_enable & (1 << ADC_DISCHARGE_CURRENT))) {
80 axp173.adc_enable |= (1 << ADC_BATTERY_POWER);
81 }
82}
83
84void axp173_init(void)
85{
86 axp173_init_enabled_adcs();
87
88 /* We need discharge current ADC to reliably poll for a full battery */
89 int bits = axp173.adc_enable;
90 bits |= (1 << ADC_DISCHARGE_CURRENT);
91 axp173_adc_set_enabled(bits);
92
93 /* Read the maximum charging current */
94 int value = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, AXP173_REG_CHARGECONTROL1);
95 axp173.chargecurrent_setting = (value < 0) ? -1 : value;
96}
97
98/* TODO: this can STILL indicate some false positives! */
99int axp173_battery_status(void)
100{
101 int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, AXP173_REG_POWERSTATUS);
102 if(r >= 0) {
103 /* Charging bit indicates we're currently charging */
104 if((r & 0x04) != 0)
105 return AXP173_BATT_CHARGING;
106
107 /* Not plugged in means we're discharging */
108 if((r & 0xf0) == 0)
109 return AXP173_BATT_DISCHARGING;
110 } else {
111 /* Report discharging if we can't find out power status */
112 return AXP173_BATT_DISCHARGING;
113 }
114
115 /* If the battery is full and not in use, the charging bit will be 0,
116 * there will be an external power source, AND the discharge current
117 * will be zero. Seems to rule out all false positives. */
118 int d = axp173_adc_read_raw(ADC_DISCHARGE_CURRENT);
119 if(d == 0)
120 return AXP173_BATT_FULL;
121
122 return AXP173_BATT_DISCHARGING;
123}
124
125int axp173_input_status(void)
126{
127#ifdef HAVE_BATTERY_SWITCH
128 int input_status = 0;
129#else
130 int input_status = AXP173_INPUT_BATTERY;
131#endif
132
133 int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, AXP173_REG_POWERSTATUS);
134 if(r < 0)
135 return input_status;
136
137 /* Check for AC input */
138 if(r & 0x80)
139 input_status |= AXP173_INPUT_AC;
140
141 /* Only report USB if ACIN and VBUS are not shorted */
142 if((r & 0x20) != 0 && (r & 0x02) == 0)
143 input_status |= AXP173_INPUT_USB;
144
145#ifdef HAVE_BATTERY_SWITCH
146 /* Check for battery presence if target defines it as removable */
147 r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, AXP173_REG_CHARGESTATUS);
148 if(r >= 0 && (r & 0x20) != 0)
149 input_status |= AXP173_INPUT_BATTERY;
150#endif
151
152 return input_status;
153}
154
155int axp173_adc_read(int adc)
156{
157 int value = axp173_adc_read_raw(adc);
158 if(value == INT_MIN)
159 return INT_MIN;
160
161 return axp173_adc_conv_raw(adc, value);
162}
163
164int axp173_adc_read_raw(int adc)
165{
166 /* Don't give a reading if the ADC is not enabled */
167 if((axp173.adc_enable & (1 << adc)) == 0)
168 return INT_MIN;
169
170 /* Read the ADC */
171 uint8_t buf[3];
172 int count = (adc == ADC_BATTERY_POWER) ? 3 : 2;
173 uint8_t reg = axp173_adc_info[adc].reg;
174 int rc = i2c_reg_read(AXP173_BUS, AXP173_ADDR, reg, count, &buf[0]);
175 if(rc != I2C_STATUS_OK)
176 return INT_MIN;
177
178 /* Parse the value */
179 if(adc == ADC_BATTERY_POWER)
180 return (buf[0] << 16) | (buf[1] << 8) | buf[2];
181 else if(adc == ADC_CHARGE_CURRENT || adc == ADC_DISCHARGE_CURRENT)
182 return (buf[0] << 5) | (buf[1] & 0x1f);
183 else
184 return (buf[0] << 4) | (buf[1] & 0xf);
185}
186
187int axp173_adc_conv_raw(int adc, int value)
188{
189 switch(adc) {
190 case ADC_ACIN_VOLTAGE:
191 case ADC_VBUS_VOLTAGE:
192 /* 0 mV ... 6.9615 mV, step 1.7 mV */
193 return value * 17 / 10;
194 case ADC_ACIN_CURRENT:
195 /* 0 mA ... 2.5594 A, step 0.625 mA */
196 return value * 5 / 8;
197 case ADC_VBUS_CURRENT:
198 /* 0 mA ... 1.5356 A, step 0.375 mA */
199 return value * 3 / 8;
200 case ADC_INTERNAL_TEMP:
201 /* -144.7 C ... 264.8 C, step 0.1 C */
202 return value - 1447;
203 case ADC_TS_INPUT:
204 /* 0 mV ... 3.276 V, step 0.8 mV */
205 return value * 4 / 5;
206 case ADC_BATTERY_VOLTAGE:
207 /* 0 mV ... 4.5045 V, step 1.1 mV */
208 return value * 11 / 10;
209 case ADC_CHARGE_CURRENT:
210 case ADC_DISCHARGE_CURRENT:
211 /* 0 mA to 4.095 A, step 0.5 mA */
212 return value / 2;
213 case ADC_APS_VOLTAGE:
214 /* 0 mV to 5.733 V, step 1.4 mV */
215 return value * 7 / 5;
216 case ADC_BATTERY_POWER:
217 /* 0 uW to 23.6404 W, step 0.55 uW */
218 return value * 11 / 20;
219 default:
220 /* Shouldn't happen */
221 return INT_MIN;
222 }
223}
224
225int axp173_adc_get_enabled(void)
226{
227 return axp173.adc_enable;
228}
229
230void axp173_adc_set_enabled(int adc_bits)
231{
232 /* Ignore no-op */
233 if(adc_bits == axp173.adc_enable)
234 return;
235
236 /* Compute the new register values */
237 const struct axp173_adc_info* info = axp173_adc_info;
238 uint8_t regs[2] = {0, 0};
239 for(int i = 0; i < NUM_ADC_CHANNELS; ++i) {
240 if(info[i].en_reg == 0xff)
241 continue;
242
243 if(adc_bits & (1 << i))
244 regs[info[i].en_reg - 0x82] |= 1 << info[i].en_bit;
245 }
246
247 /* These ADCs share an enable bit */
248 if(adc_bits & ((1 << ADC_CHARGE_CURRENT)|(1 << ADC_DISCHARGE_CURRENT))) {
249 adc_bits |= (1 << ADC_CHARGE_CURRENT);
250 adc_bits |= (1 << ADC_DISCHARGE_CURRENT);
251 }
252
253 /* Enable required bits for battery power ADC */
254 if(adc_bits & (1 << ADC_BATTERY_POWER)) {
255 regs[0] |= 1 << info[ADC_DISCHARGE_CURRENT].en_bit;
256 regs[0] |= 1 << info[ADC_BATTERY_VOLTAGE].en_bit;
257 }
258
259 /* Update the configuration */
260 i2c_reg_write(AXP173_BUS, AXP173_ADDR, AXP173_REG_ADCENABLE1, 2, &regs[0]);
261 axp173.adc_enable = adc_bits;
262}
263
264int axp173_adc_get_rate(void)
265{
266 int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, AXP173_REG_ADCSAMPLERATE);
267 if(r < 0)
268 return AXP173_ADC_RATE_100HZ; /* an arbitrary value */
269
270 return (r >> 6) & 3;
271}
272
273void axp173_adc_set_rate(int rate)
274{
275 i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, AXP173_REG_ADCSAMPLERATE,
276 0xc0, (rate & 3) << 6, NULL);
277}
278
279static uint32_t axp173_cc_parse(const uint8_t* buf)
280{
281 return ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
282}
283
284void axp173_cc_read(uint32_t* charge, uint32_t* discharge)
285{
286 uint8_t buf[8];
287 int rc = i2c_reg_read(AXP173_BUS, AXP173_ADDR,
288 AXP173_REG_COULOMBCOUNTERBASE, 8, &buf[0]);
289 if(rc != I2C_STATUS_OK) {
290 if(charge)
291 *charge = 0;
292 if(discharge)
293 *discharge = 0;
294 return;
295 }
296
297 if(charge)
298 *charge = axp173_cc_parse(&buf[0]);
299 if(discharge)
300 *discharge = axp173_cc_parse(&buf[4]);
301}
302
303void axp173_cc_clear(void)
304{
305 i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR,
306 AXP173_REG_COULOMBCOUNTERCTRL, 5, 1, NULL);
307}
308
309void axp173_cc_enable(bool en)
310{
311 i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR,
312 AXP173_REG_COULOMBCOUNTERCTRL, 7, en ? 1 : 0, NULL);
313}
314
315static const int chargecurrent_tbl[] = {
316 100, 190, 280, 360,
317 450, 550, 630, 700,
318 780, 880, 960, 1000,
319 1080, 1160, 1240, 1320,
320};
321
322static const int chargecurrent_tblsz = sizeof(chargecurrent_tbl)/sizeof(int);
323
324void axp173_set_charge_current(int maxcurrent)
325{
326 /* Find the charge current just higher than maxcurrent */
327 int value = 0;
328 while(value < chargecurrent_tblsz &&
329 chargecurrent_tbl[value] <= maxcurrent)
330 ++value;
331
332 /* Select the next lower current, the greatest current <= maxcurrent */
333 if(value >= chargecurrent_tblsz)
334 value = chargecurrent_tblsz - 1;
335 else if(value > 0)
336 --value;
337
338 /* Don't issue i2c write if desired setting is already in use */
339 if(value == axp173.chargecurrent_setting)
340 return;
341
342 /* Update register */
343 i2c_reg_modify1(AXP173_BUS, AXP173_ADDR,
344 AXP173_REG_CHARGECONTROL1, 0x0f, value, NULL);
345 axp173.chargecurrent_setting = value;
346}
347
348int axp173_get_charge_current(void)
349{
350 if(axp173.chargecurrent_setting < 0)
351 return chargecurrent_tbl[0];
352 else
353 return chargecurrent_tbl[axp173.chargecurrent_setting];
354}
355
356#ifndef BOOTLOADER
357#define AXP173_DEBUG_BATTERY_STATUS 0
358#define AXP173_DEBUG_INPUT_STATUS 1
359#define AXP173_DEBUG_CHARGE_CURRENT 2
360#define AXP173_DEBUG_ADC_RATE 3
361#define AXP173_DEBUG_FIRST_ADC 4
362#define AXP173_DEBUG_ENTRIES (AXP173_DEBUG_FIRST_ADC + NUM_ADC_CHANNELS)
363
364static int axp173_debug_menu_cb(int action, struct gui_synclist* lists)
365{
366 (void)lists;
367
368 if(action == ACTION_NONE)
369 action = ACTION_REDRAW;
370
371 return action;
372}
373
374static const char* axp173_debug_menu_get_name(int item, void* data,
375 char* buf, size_t buflen)
376{
377 (void)data;
378
379 static const char* const adc_names[] = {
380 "V_acin", "I_acin", "V_vbus", "I_vbus", "T_int",
381 "V_ts", "V_batt", "I_chrg", "I_dchg", "V_aps", "P_batt"
382 };
383
384 static const char* const adc_units[] = {
385 "mV", "mA", "mV", "mA", "C", "mV", "mV", "mA", "mA", "mV", "uW",
386 };
387
388 int adc = item - AXP173_DEBUG_FIRST_ADC;
389 if(item >= AXP173_DEBUG_FIRST_ADC && adc < NUM_ADC_CHANNELS) {
390 int raw_value = axp173_adc_read_raw(adc);
391 if(raw_value == INT_MIN) {
392 snprintf(buf, buflen, "%s: [Disabled]", adc_names[adc]);
393 return buf;
394 }
395
396 int value = axp173_adc_conv_raw(adc, raw_value);
397 if(adc == ADC_INTERNAL_TEMP) {
398 snprintf(buf, buflen, "%s: %d.%d %s", adc_names[adc],
399 value/10, value%10, adc_units[adc]);
400 } else {
401 snprintf(buf, buflen, "%s: %d %s", adc_names[adc],
402 value, adc_units[adc]);
403 }
404
405 return buf;
406 }
407
408 switch(item) {
409 case AXP173_DEBUG_BATTERY_STATUS: {
410 switch(axp173_battery_status()) {
411 case AXP173_BATT_FULL:
412 return "Battery: Full";
413 case AXP173_BATT_CHARGING:
414 return "Battery: Charging";
415 case AXP173_BATT_DISCHARGING:
416 return "Battery: Discharging";
417 default:
418 return "Battery: Unknown";
419 }
420 } break;
421
422 case AXP173_DEBUG_INPUT_STATUS: {
423 int s = axp173_input_status();
424 const char* ac = (s & AXP173_INPUT_AC) ? " AC" : "";
425 const char* usb = (s & AXP173_INPUT_USB) ? " USB" : "";
426 const char* batt = (s & AXP173_INPUT_BATTERY) ? " Battery" : "";
427 snprintf(buf, buflen, "Inputs:%s%s%s", ac, usb, batt);
428 return buf;
429 } break;
430
431 case AXP173_DEBUG_CHARGE_CURRENT: {
432 int current = axp173_get_charge_current();
433 snprintf(buf, buflen, "Max charge current: %d mA", current);
434 return buf;
435 } break;
436
437 case AXP173_DEBUG_ADC_RATE: {
438 int rate = 25 << axp173_adc_get_rate();
439 snprintf(buf, buflen, "ADC sample rate: %d Hz", rate);
440 return buf;
441 } break;
442
443 default:
444 return "---";
445 }
446}
447
448bool axp173_debug_menu(void)
449{
450 struct simplelist_info info;
451 simplelist_info_init(&info, "AXP173 debug", AXP173_DEBUG_ENTRIES, NULL);
452 info.action_callback = axp173_debug_menu_cb;
453 info.get_name = axp173_debug_menu_get_name;
454 return simplelist_show_list(&info);
455}
456#endif /* !BOOTLOADER */
457
458/* This is basically the only valid implementation, so define it here */
459unsigned int power_input_status(void)
460{
461 unsigned int state = 0;
462 int input_status = axp173_input_status();
463
464 if(input_status & AXP173_INPUT_AC)
465 state |= POWER_INPUT_MAIN_CHARGER;
466
467 if(input_status & AXP173_INPUT_USB)
468 state |= POWER_INPUT_USB_CHARGER;
469
470#ifdef HAVE_BATTERY_SWITCH
471 if(input_status & AXP173_INPUT_BATTERY)
472 state |= POWER_INPUT_BATTERY;
473#endif
474
475 return state;
476}
diff --git a/firmware/export/axp-pmu.h b/firmware/export/axp-pmu.h
new file mode 100644
index 0000000000..457f746e8c
--- /dev/null
+++ b/firmware/export/axp-pmu.h
@@ -0,0 +1,151 @@
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#ifndef __AXP_PMU_H__
23#define __AXP_PMU_H__
24
25#include "config.h"
26#include <stdbool.h>
27#include <stdint.h>
28
29/* ADC channels */
30#define ADC_ACIN_VOLTAGE 0
31#define ADC_ACIN_CURRENT 1
32#define ADC_VBUS_VOLTAGE 2
33#define ADC_VBUS_CURRENT 3
34#define ADC_INTERNAL_TEMP 4
35#define ADC_TS_INPUT 5
36#define ADC_BATTERY_VOLTAGE 6
37#define ADC_CHARGE_CURRENT 7
38#define ADC_DISCHARGE_CURRENT 8
39#define ADC_APS_VOLTAGE 9
40#define ADC_BATTERY_POWER 10
41#define NUM_ADC_CHANNELS 11
42
43/* ADC sampling rates */
44#define AXP_ADC_RATE_25HZ 0
45#define AXP_ADC_RATE_50HZ 1
46#define AXP_ADC_RATE_100HZ 2
47#define AXP_ADC_RATE_200HZ 3
48
49/* Return values of axp_battery_status() */
50#define AXP_BATT_DISCHARGING 0
51#define AXP_BATT_CHARGING 1
52#define AXP_BATT_FULL 2
53
54/* Bits returned by axp_input_status() */
55#define AXP_INPUT_AC (1 << 0)
56#define AXP_INPUT_USB (1 << 1)
57#define AXP_INPUT_BATTERY (1 << 2)
58#define AXP_INPUT_EXTERNAL (AXP_INPUT_AC|AXP_INPUT_USB)
59
60/* Power supplies known by this driver. Not every chip has all supplies! */
61#define AXP_SUPPLY_DCDC1 0
62#define AXP_SUPPLY_DCDC2 1
63#define AXP_SUPPLY_DCDC3 2
64#define AXP_SUPPLY_LDO1 3
65#define AXP_SUPPLY_LDO2 4
66#define AXP_SUPPLY_LDO3 5
67#define AXP_SUPPLY_LDO_IO0 6
68#define AXP_NUM_SUPPLIES 7
69
70/* Special values returned by axp_supply_get_voltage */
71#define AXP_SUPPLY_NOT_PRESENT INT_MIN
72#define AXP_SUPPLY_DISABLED (-1)
73
74/* Registers -- common to AXP173 and AXP192 (incomplete listing) */
75#define AXP_REG_POWERSTATUS 0x00
76#define AXP_REG_CHARGESTATUS 0x01
77#define AXP_REG_CHIP_ID 0x03
78#define AXP_REG_PWROUTPUTCTRL1 0x10
79#define AXP_REG_PWROUTPUTCTRL2 0x12
80#define AXP_REG_SHUTDOWNLEDCTRL 0x32
81#define AXP_REG_CHARGECONTROL1 0x33
82#define AXP_REG_DCDCWORKINGMODE 0x80
83#define AXP_REG_ADCENABLE1 0x82
84#define AXP_REG_ADCENABLE2 0x83
85#define AXP_REG_ADCSAMPLERATE 0x84
86#define AXP_REG_COULOMBCOUNTERBASE 0xb0
87#define AXP_REG_COULOMBCOUNTERCTRL 0xb8
88
89/* AXP192-only registers (incomplete listing) */
90#define AXP192_REG_GPIO0FUNCTION 0x90
91#define AXP192_REG_GPIO1FUNCTION 0x92
92#define AXP192_REG_GPIO2FUNCTION 0x93
93#define AXP192_REG_GPIOSTATE1 0x94
94
95/* Must be called from power_init() to initialize the driver state */
96extern void axp_init(void);
97
98/* - axp_supply_set_voltage(): set a supply voltage to the given value
99 * in millivolts. Pass a voltage of AXP_SUPPLY_DISABLED to shut off
100 * the supply. Any invalid supply or voltage will make the call a no-op.
101 *
102 * - axp_supply_get_voltage() returns a supply voltage in millivolts.
103 * If the supply is powered off, returns AXP_SUPPLY_DISABLED.
104 * If the chip does not have the supply, returns AXP_SUPPLY_NOT_PRESENT.
105 */
106extern void axp_supply_set_voltage(int supply, int voltage);
107extern int axp_supply_get_voltage(int supply);
108
109/* Basic battery and power supply status */
110extern int axp_battery_status(void);
111extern int axp_input_status(void);
112
113/* ADC access -- ADCs which are not enabled will return INT_MIN if read.
114 * The output of axp_adc_read() is normalized to appropriate units:
115 *
116 * - for voltages, the scale is millivolts
117 * - for currents, the scale is milliamps
118 * - for temperatures, the scale is tenths of a degree Celsius
119 * - for power, the scale is microwatts
120 *
121 * See the comment in axp_adc_conv_raw() for raw value precision/scale.
122 */
123extern int axp_adc_read(int adc);
124extern int axp_adc_read_raw(int adc);
125extern int axp_adc_conv_raw(int adc, int value);
126extern int axp_adc_get_enabled(void);
127extern void axp_adc_set_enabled(int adc_bits);
128extern int axp_adc_get_rate(void);
129extern void axp_adc_set_rate(int rate);
130
131/* - axp_cc_read() reads the coulomb counters
132 * - axp_cc_clear() resets both counters to zero
133 * - axp_cc_enable() will stop/start the counters running
134 * - axp_cc_is_enabled() returns true if the counters are running
135 */
136extern void axp_cc_read(uint32_t* charge, uint32_t* discharge);
137extern void axp_cc_clear(void);
138extern void axp_cc_enable(bool en);
139extern bool axp_cc_is_enabled(void);
140
141/* Set/get maximum charging current in milliamps */
142extern void axp_set_charge_current(int maxcurrent);
143extern int axp_get_charge_current(void);
144
145/* Set the shutdown bit */
146extern void axp_power_off(void);
147
148/* Debug menu */
149extern bool axp_debug_menu(void);
150
151#endif /* __AXP_PMU_H__ */
diff --git a/firmware/export/axp173.h b/firmware/export/axp173.h
deleted file mode 100644
index 34f0c2ec19..0000000000
--- a/firmware/export/axp173.h
+++ /dev/null
@@ -1,118 +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#ifndef __AXP173_H__
23#define __AXP173_H__
24
25#include <stdbool.h>
26#include <stdint.h>
27
28/* ADC channels */
29#define ADC_ACIN_VOLTAGE 0
30#define ADC_ACIN_CURRENT 1
31#define ADC_VBUS_VOLTAGE 2
32#define ADC_VBUS_CURRENT 3
33#define ADC_INTERNAL_TEMP 4
34#define ADC_TS_INPUT 5
35#define ADC_BATTERY_VOLTAGE 6
36#define ADC_CHARGE_CURRENT 7
37#define ADC_DISCHARGE_CURRENT 8
38#define ADC_APS_VOLTAGE 9
39#define ADC_BATTERY_POWER 10
40#define NUM_ADC_CHANNELS 11
41
42/* ADC sampling rates */
43#define AXP173_ADC_RATE_25HZ 0
44#define AXP173_ADC_RATE_50HZ 1
45#define AXP173_ADC_RATE_100HZ 2
46#define AXP173_ADC_RATE_200HZ 3
47
48/* Return values of axp173_battery_status() */
49#define AXP173_BATT_DISCHARGING 0
50#define AXP173_BATT_CHARGING 1
51#define AXP173_BATT_FULL 2
52
53/* Bits returned by axp173_input_status() */
54#define AXP173_INPUT_AC (1 << 0)
55#define AXP173_INPUT_USB (1 << 1)
56#define AXP173_INPUT_BATTERY (1 << 2)
57#define AXP173_INPUT_EXTERNAL (AXP173_INPUT_AC|AXP173_INPUT_USB)
58
59/* Registers -- common to AXP173 and AXP192 (incomplete listing) */
60#define AXP173_REG_POWERSTATUS 0x00
61#define AXP173_REG_CHARGESTATUS 0x01
62#define AXP173_REG_PWROUTPUTCTRL 0x12
63#define AXP173_REG_SHUTDOWNLEDCTRL 0x32
64#define AXP173_REG_CHARGECONTROL1 0x33
65#define AXP173_REG_DCDCWORKINGMODE 0x80
66#define AXP173_REG_ADCENABLE1 0x82
67#define AXP173_REG_ADCENABLE2 0x83
68#define AXP173_REG_ADCSAMPLERATE 0x84
69#define AXP173_REG_COULOMBCOUNTERBASE 0xb0
70#define AXP173_REG_COULOMBCOUNTERCTRL 0xb8
71
72/* AXP192-only registers (incomplete listing) */
73#define AXP192_REG_GPIO0FUNCTION 0x90
74#define AXP192_REG_GPIO1FUNCTION 0x92
75#define AXP192_REG_GPIO2FUNCTION 0x93
76#define AXP192_REG_GPIOSTATE1 0x94
77
78/* Must be called from power_init() to initialize the driver state */
79extern void axp173_init(void);
80
81/* Basic battery and power supply status */
82extern int axp173_battery_status(void);
83extern int axp173_input_status(void);
84
85/* ADC access -- ADCs which are not enabled will return INT_MIN if read.
86 * The output of axp173_adc_read() is normalized to appropriate units:
87 *
88 * - for voltages, the scale is millivolts
89 * - for currents, the scale is milliamps
90 * - for temperatures, the scale is tenths of a degree Celsius
91 * - for power, the scale is microwatts
92 *
93 * See the comment in axp173_adc_conv_raw() for raw value precision/scale.
94 */
95extern int axp173_adc_read(int adc);
96extern int axp173_adc_read_raw(int adc);
97extern int axp173_adc_conv_raw(int adc, int value);
98extern int axp173_adc_get_enabled(void);
99extern void axp173_adc_set_enabled(int adc_bits);
100extern int axp173_adc_get_rate(void);
101extern void axp173_adc_set_rate(int rate);
102
103/* - axp173_cc_read() reads the coulomb counters
104 * - axp173_cc_clear() resets both counters to zero
105 * - axp173_cc_enable() will stop/start the counters running
106 */
107extern void axp173_cc_read(uint32_t* charge, uint32_t* discharge);
108extern void axp173_cc_clear(void);
109extern void axp173_cc_enable(bool en);
110
111/* Set/get maximum charging current in milliamps */
112extern void axp173_set_charge_current(int maxcurrent);
113extern int axp173_get_charge_current(void);
114
115/* Debug menu */
116extern bool axp173_debug_menu(void);
117
118#endif /* __AXP173_H__ */
diff --git a/firmware/export/config/fiiom3k.h b/firmware/export/config/fiiom3k.h
index 2c212b031d..a28efd43a5 100644
--- a/firmware/export/config/fiiom3k.h
+++ b/firmware/export/config/fiiom3k.h
@@ -90,7 +90,7 @@
90#define HAVE_SW_POWEROFF 90#define HAVE_SW_POWEROFF
91 91
92#ifndef SIMULATOR 92#ifndef SIMULATOR
93#define HAVE_AXP173 93#define HAVE_AXP_PMU 192
94#define HAVE_POWEROFF_WHILE_CHARGING 94#define HAVE_POWEROFF_WHILE_CHARGING
95#endif 95#endif
96 96
diff --git a/firmware/target/mips/ingenic_x1000/debug-x1000.c b/firmware/target/mips/ingenic_x1000/debug-x1000.c
index 74bbcd77a6..fe469b1a72 100644
--- a/firmware/target/mips/ingenic_x1000/debug-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/debug-x1000.c
@@ -148,7 +148,9 @@ static bool dbg_cpuidle(void)
148 148
149#ifdef FIIO_M3K 149#ifdef FIIO_M3K
150extern bool dbg_fiiom3k_touchpad(void); 150extern bool dbg_fiiom3k_touchpad(void);
151extern bool axp173_debug_menu(void); 151#endif
152#ifdef HAVE_AXP_PMU
153extern bool axp_debug_menu(void);
152#endif 154#endif
153 155
154/* Menu definition */ 156/* Menu definition */
@@ -164,7 +166,9 @@ static const struct {
164 {"Audio", &dbg_audio}, 166 {"Audio", &dbg_audio},
165#ifdef FIIO_M3K 167#ifdef FIIO_M3K
166 {"Touchpad", &dbg_fiiom3k_touchpad}, 168 {"Touchpad", &dbg_fiiom3k_touchpad},
167 {"Power stats", &axp173_debug_menu}, 169#endif
170#ifdef HAVE_AXP_PMU
171 {"Power stats", &axp_debug_menu},
168#endif 172#endif
169}; 173};
170 174
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c
index 85ccc5cf65..efc652c84f 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c
@@ -24,7 +24,7 @@
24#include "backlight.h" 24#include "backlight.h"
25#include "powermgmt.h" 25#include "powermgmt.h"
26#include "panic.h" 26#include "panic.h"
27#include "axp173.h" 27#include "axp-pmu.h"
28#include "gpio-x1000.h" 28#include "gpio-x1000.h"
29#include "i2c-x1000.h" 29#include "i2c-x1000.h"
30#include <string.h> 30#include <string.h>
@@ -418,7 +418,7 @@ static uint8_t hp_detect_reg = 0x00;
418static int hp_detect_tmo_cb(struct timeout* tmo) 418static int hp_detect_tmo_cb(struct timeout* tmo)
419{ 419{
420 i2c_descriptor* d = (i2c_descriptor*)tmo->data; 420 i2c_descriptor* d = (i2c_descriptor*)tmo->data;
421 i2c_async_queue(AXP173_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, d); 421 i2c_async_queue(AXP_PMU_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, d);
422 return HPD_POLL_TIME; 422 return HPD_POLL_TIME;
423} 423}
424 424
@@ -427,7 +427,7 @@ static void hp_detect_init(void)
427 static struct timeout tmo; 427 static struct timeout tmo;
428 static const uint8_t gpio_reg = AXP192_REG_GPIOSTATE1; 428 static const uint8_t gpio_reg = AXP192_REG_GPIOSTATE1;
429 static i2c_descriptor desc = { 429 static i2c_descriptor desc = {
430 .slave_addr = AXP173_ADDR, 430 .slave_addr = AXP_PMU_ADDR,
431 .bus_cond = I2C_START | I2C_STOP, 431 .bus_cond = I2C_START | I2C_STOP,
432 .tran_mode = I2C_READ, 432 .tran_mode = I2C_READ,
433 .buffer[0] = (void*)&gpio_reg, 433 .buffer[0] = (void*)&gpio_reg,
@@ -440,10 +440,10 @@ static void hp_detect_init(void)
440 }; 440 };
441 441
442 /* Headphone detect is wired to AXP192 GPIO: set it to input state */ 442 /* Headphone detect is wired to AXP192 GPIO: set it to input state */
443 i2c_reg_write1(AXP173_BUS, AXP173_ADDR, AXP192_REG_GPIO2FUNCTION, 0x01); 443 i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO2FUNCTION, 0x01);
444 444
445 /* Get an initial reading before startup */ 445 /* Get an initial reading before startup */
446 int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, gpio_reg); 446 int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, gpio_reg);
447 if(r >= 0) 447 if(r >= 0)
448 hp_detect_reg = r; 448 hp_detect_reg = r;
449 449
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h
index a389d2af42..1e8ebfbb15 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h
@@ -31,7 +31,7 @@
31#define FT6x06_BUS 1 31#define FT6x06_BUS 1
32#define FT6x06_ADDR 0x38 32#define FT6x06_ADDR 0x38
33 33
34#define AXP173_BUS 2 34#define AXP_PMU_BUS 2
35#define AXP173_ADDR 0x34 35#define AXP_PMU_ADDR 0x34
36 36
37#endif /* __I2C_TARGET_H__ */ 37#endif /* __I2C_TARGET_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
index c346d16890..a7f6165980 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
@@ -26,7 +26,7 @@
26#ifdef HAVE_USB_CHARGING_ENABLE 26#ifdef HAVE_USB_CHARGING_ENABLE
27# include "usb_core.h" 27# include "usb_core.h"
28#endif 28#endif
29#include "axp173.h" 29#include "axp-pmu.h"
30#include "i2c-x1000.h" 30#include "i2c-x1000.h"
31#include "gpio-x1000.h" 31#include "gpio-x1000.h"
32 32
@@ -53,32 +53,32 @@ const unsigned short percent_to_volt_charge[11] =
53 3485, 3780, 3836, 3857, 3890, 3930, 3986, 4062, 4158, 4185, 4196 53 3485, 3780, 3836, 3857, 3890, 3930, 3986, 4062, 4158, 4185, 4196
54}; 54};
55 55
56#define AXP173_IRQ_PORT GPIO_B 56#define AXP_IRQ_PORT GPIO_B
57#define AXP173_IRQ_PIN (1 << 10) 57#define AXP_IRQ_PIN (1 << 10)
58 58
59void power_init(void) 59void power_init(void)
60{ 60{
61 /* Initialize driver */ 61 /* Initialize driver */
62 i2c_x1000_set_freq(2, I2C_FREQ_400K); 62 i2c_x1000_set_freq(2, I2C_FREQ_400K);
63 axp173_init(); 63 axp_init();
64 64
65 /* Set lowest sample rate */ 65 /* Set lowest sample rate */
66 axp173_adc_set_rate(AXP173_ADC_RATE_25HZ); 66 axp_adc_set_rate(AXP_ADC_RATE_25HZ);
67 67
68 /* Ensure battery voltage ADC is enabled */ 68 /* Ensure battery voltage ADC is enabled */
69 int bits = axp173_adc_get_enabled(); 69 int bits = axp_adc_get_enabled();
70 bits |= (1 << ADC_BATTERY_VOLTAGE); 70 bits |= (1 << ADC_BATTERY_VOLTAGE);
71 axp173_adc_set_enabled(bits); 71 axp_adc_set_enabled(bits);
72 72
73 /* Turn on all power outputs */ 73 /* Turn on all power outputs */
74 i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, 74 i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
75 AXP173_REG_PWROUTPUTCTRL, 0, 0x5f, NULL); 75 AXP_REG_PWROUTPUTCTRL2, 0, 0x5f, NULL);
76 i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, 76 i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
77 AXP173_REG_DCDCWORKINGMODE, 0, 0xc0, NULL); 77 AXP_REG_DCDCWORKINGMODE, 0, 0xc0, NULL);
78 78
79 /* Set the default charging current. This is the same as the 79 /* Set the default charging current. This is the same as the
80 * OF's setting, although it's not strictly within the USB spec. */ 80 * OF's setting, although it's not strictly within the USB spec. */
81 axp173_set_charge_current(780); 81 axp_set_charge_current(780);
82 82
83 /* Short delay to give power outputs time to stabilize */ 83 /* Short delay to give power outputs time to stabilize */
84 mdelay(5); 84 mdelay(5);
@@ -87,7 +87,7 @@ void power_init(void)
87#ifdef HAVE_USB_CHARGING_ENABLE 87#ifdef HAVE_USB_CHARGING_ENABLE
88void usb_charging_maxcurrent_change(int maxcurrent) 88void usb_charging_maxcurrent_change(int maxcurrent)
89{ 89{
90 axp173_set_charge_current(maxcurrent); 90 axp_set_charge_current(maxcurrent);
91} 91}
92#endif 92#endif
93 93
@@ -97,18 +97,16 @@ void adc_init(void)
97 97
98void power_off(void) 98void power_off(void)
99{ 99{
100 /* Set the shutdown bit */ 100 axp_power_off();
101 i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR,
102 AXP173_REG_SHUTDOWNLEDCTRL, 7, 1, NULL);
103 while(1); 101 while(1);
104} 102}
105 103
106bool charging_state(void) 104bool charging_state(void)
107{ 105{
108 return axp173_battery_status() == AXP173_BATT_CHARGING; 106 return axp_battery_status() == AXP_BATT_CHARGING;
109} 107}
110 108
111int _battery_voltage(void) 109int _battery_voltage(void)
112{ 110{
113 return axp173_adc_read(ADC_BATTERY_VOLTAGE); 111 return axp_adc_read(ADC_BATTERY_VOLTAGE);
114} 112}