summaryrefslogtreecommitdiff
path: root/firmware/drivers/axp173.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/drivers/axp173.c')
-rw-r--r--firmware/drivers/axp173.c476
1 files changed, 0 insertions, 476 deletions
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}