summaryrefslogtreecommitdiff
path: root/firmware/drivers
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-02-27 22:08:58 +0000
committerAidan MacDonald <amachronic@protonmail.com>2021-03-28 00:01:37 +0000
commit3ec66893e377b088c1284d2d23adb2aeea6d7965 (patch)
treeb647717f83ad56b15dc42cfdef5d04d68cd9bd6b /firmware/drivers
parent83fcbedc65f4b9ae7e491ecf6f07c0af4b245f74 (diff)
downloadrockbox-3ec66893e377b088c1284d2d23adb2aeea6d7965.tar.gz
rockbox-3ec66893e377b088c1284d2d23adb2aeea6d7965.zip
New port: FiiO M3K on bare metal
Change-Id: I7517e7d5459e129dcfc9465c6fbd708619888fbe
Diffstat (limited to 'firmware/drivers')
-rw-r--r--firmware/drivers/audio/ak4376.c274
-rw-r--r--firmware/drivers/axp173.c419
-rw-r--r--firmware/drivers/rtc/rtc_x1000.c104
3 files changed, 797 insertions, 0 deletions
diff --git a/firmware/drivers/audio/ak4376.c b/firmware/drivers/audio/ak4376.c
new file mode 100644
index 0000000000..494bbabfa4
--- /dev/null
+++ b/firmware/drivers/audio/ak4376.c
@@ -0,0 +1,274 @@
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 "audiohw.h"
23#include "sound.h"
24#include "panic.h"
25#include "pcm_sampr.h"
26#include "pcm_sw_volume.h"
27#include "system.h"
28#include "i2c-async.h"
29
30#ifndef HAVE_SW_VOLUME_CONTROL
31# error "AK4376 requires HAVE_SW_VOLUME_CONTROL!"
32#endif
33
34/* NOTE: At present, only the FiiO M3K uses this driver so the handling of
35 * the clock / audio interface is limited to I2S slave, 16-bit samples, with
36 * DAC master clock provided directly on the MCLK input pin, fitting the
37 * clock setup of the M3K.
38 *
39 * Feel free to expand upon this if another target ever needs this driver.
40 */
41
42/* Converts HW_FREQ_XX constants to register values */
43static const int ak4376_fsel_to_hw[] = {
44 HW_HAVE_192_(AK4376_FS_192,)
45 HW_HAVE_176_(AK4376_FS_176,)
46 HW_HAVE_96_(AK4376_FS_96,)
47 HW_HAVE_88_(AK4376_FS_88,)
48 HW_HAVE_64_(AK4376_FS_64,)
49 HW_HAVE_48_(AK4376_FS_48,)
50 HW_HAVE_44_(AK4376_FS_44,)
51 HW_HAVE_32_(AK4376_FS_32,)
52 HW_HAVE_24_(AK4376_FS_24,)
53 HW_HAVE_22_(AK4376_FS_22,)
54 HW_HAVE_16_(AK4376_FS_16,)
55 HW_HAVE_12_(AK4376_FS_12,)
56 HW_HAVE_11_(AK4376_FS_11,)
57 HW_HAVE_8_(AK4376_FS_8,)
58};
59
60static struct ak4376 {
61 int fsel;
62 int low_mode;
63 int regs[AK4376_NUM_REGS];
64} ak4376;
65
66void ak4376_init(void)
67{
68 /* Initialize DAC state */
69 ak4376.fsel = HW_FREQ_48;
70 ak4376.low_mode = 0;
71 for(int i = 0; i < AK4376_NUM_REGS; ++i)
72 ak4376.regs[i] = -1;
73
74 /* Initial reset after power-on */
75 ak4376_set_pdn_pin(0);
76 mdelay(1);
77 ak4376_set_pdn_pin(1);
78 mdelay(1);
79
80 static const int init_config[] = {
81 /* Ensure HPRHZ, HPLHZ are 0 */
82 AK4376_REG_OUTPUT_MODE, 0x00,
83 /* Mute all volume controls */
84 AK4376_REG_MIXER, 0x00,
85 AK4376_REG_LCH_VOLUME, 0x80,
86 AK4376_REG_RCH_VOLUME, 0x00,
87 AK4376_REG_AMP_VOLUME, 0x00,
88 /* Clock source = MCLK, divider = 1 */
89 AK4376_REG_DAC_CLK_SRC, 0x00,
90 AK4376_REG_DAC_CLK_DIV, 0x00,
91 /* I2S slave mode, 16-bit samples */
92 AK4376_REG_AUDIO_IF_FMT, 0x03,
93 /* Recommended by datasheet */
94 AK4376_REG_ADJUST1, 0x20,
95 AK4376_REG_ADJUST2, 0x05,
96 /* Power controls */
97 AK4376_REG_PWR2, 0x33,
98 AK4376_REG_PWR3, 0x01,
99 AK4376_REG_PWR4, 0x03,
100 };
101
102 /* Write initial configuration prior to power-up */
103 for(size_t i = 0; i < ARRAYLEN(init_config); i += 2)
104 ak4376_write(init_config[i], init_config[i+1]);
105
106 /* Initial frequency setting, also handles DAC/amp power-up */
107 audiohw_set_frequency(HW_FREQ_48);
108}
109
110void ak4376_close(void)
111{
112 /* Shut off power */
113 ak4376_write(AK4376_REG_PWR3, 0x00);
114 ak4376_write(AK4376_REG_PWR4, 0x00);
115 ak4376_write(AK4376_REG_PWR2, 0x00);
116
117 /* PDN pin low */
118 ak4376_set_pdn_pin(0);
119}
120
121void ak4376_write(int reg, int value)
122{
123 /* Ensure value is sensible and differs from the last set value */
124 if((value & 0xff) == value && ak4376.regs[reg] != value) {
125 int r = i2c_reg_write1(AK4376_BUS, AK4376_ADDR, reg, value);
126 if(r == I2C_STATUS_OK)
127 ak4376.regs[reg] = value;
128 else
129 ak4376.regs[reg] = -1;
130 }
131}
132
133int ak4376_read(int reg)
134{
135 /* Only read from I2C if we don't already know the value */
136 if(ak4376.regs[reg] < 0)
137 ak4376.regs[reg] = i2c_reg_read1(AK4376_BUS, AK4376_ADDR, reg);
138
139 return ak4376.regs[reg];
140}
141
142static int round_step_up(int x, int step)
143{
144 int rem = x % step;
145 if(rem > 0)
146 rem -= step;
147 return x - rem;
148}
149
150static void calc_volumes(int vol, int* mix, int* dig, int* sw)
151{
152 /* Mixer can divide by 2, which gives an extra -6 dB adjustment */
153 if(vol < AK4376_DIG_VOLUME_MIN) {
154 *mix |= AK4376_MIX_HALF;
155 vol += 60;
156 }
157
158 *dig = round_step_up(vol, AK4376_DIG_VOLUME_STEP);
159 *dig = MIN(*dig, AK4376_DIG_VOLUME_MAX);
160 *dig = MAX(*dig, AK4376_DIG_VOLUME_MIN);
161 vol -= *dig;
162
163 /* Seems that this is the allowable range for software volume */
164 *sw = MIN(vol, 60);
165 *sw = MAX(*sw, -730);
166 vol -= *sw;
167}
168
169static int dig_vol_to_hw(int vol)
170{
171 if(vol < AK4376_DIG_VOLUME_MIN) return 0;
172 if(vol > AK4376_DIG_VOLUME_MAX) return 31;
173 return (vol - AK4376_DIG_VOLUME_MIN) / AK4376_DIG_VOLUME_STEP + 1;
174}
175
176static int amp_vol_to_hw(int vol)
177{
178 if(vol < AK4376_AMP_VOLUME_MIN) return 0;
179 if(vol > AK4376_AMP_VOLUME_MAX) return 14;
180 return (vol - AK4376_AMP_VOLUME_MIN) / AK4376_AMP_VOLUME_STEP + 1;
181}
182
183void audiohw_set_volume(int vol_l, int vol_r)
184{
185 int amp;
186 int mix_l = AK4376_MIX_LCH, dig_l, sw_l;
187 int mix_r = AK4376_MIX_RCH, dig_r, sw_r;
188
189 if(vol_l <= AK4376_MIN_VOLUME && vol_r <= AK4376_MIN_VOLUME) {
190 /* Special case for full mute */
191 amp = AK4376_AMP_VOLUME_MUTE;
192 dig_l = dig_r = AK4376_DIG_VOLUME_MUTE;
193 sw_l = sw_r = PCM_MUTE_LEVEL;
194 } else {
195 /* Amp is a mono control -- calculate based on the loudest channel.
196 * The quieter channel then gets reduced more by digital controls. */
197 amp = round_step_up(MAX(vol_l, vol_r), AK4376_AMP_VOLUME_STEP);
198 amp = MIN(amp, AK4376_AMP_VOLUME_MAX);
199 amp = MAX(amp, AK4376_AMP_VOLUME_MIN);
200
201 /* Other controls are stereo */
202 calc_volumes(vol_l - amp, &mix_l, &dig_l, &sw_l);
203 calc_volumes(vol_r - amp, &mix_r, &dig_r, &sw_r);
204 }
205
206 ak4376_write(AK4376_REG_MIXER, (mix_l & 0xf) | ((mix_r & 0xf) << 4));
207 ak4376_write(AK4376_REG_LCH_VOLUME, dig_vol_to_hw(dig_l) | (1 << 7));
208 ak4376_write(AK4376_REG_RCH_VOLUME, dig_vol_to_hw(dig_r));
209 ak4376_write(AK4376_REG_AMP_VOLUME, amp_vol_to_hw(amp));
210 pcm_set_master_volume(sw_l, sw_r);
211}
212
213void audiohw_set_filter_roll_off(int val)
214{
215 int reg = ak4376_read(AK4376_REG_FILTER);
216 reg &= ~0xc0;
217 reg |= (val & 3) << 6;
218 ak4376_write(AK4376_REG_FILTER, reg);
219}
220
221void audiohw_set_frequency(int fsel)
222{
223 /* Determine master clock multiplier */
224 int mult = ak4376_set_mclk_freq(fsel, false);
225
226 /* Calculate clock mode for frequency. Multipliers of 32/64 are only
227 * for rates >= 256 KHz which are not supported by Rockbox, so they
228 * are commented out -- but they're in the correct place. */
229 int clock_mode = ak4376_fsel_to_hw[fsel];
230 switch(mult) {
231 /* case 32: */
232 case 256:
233 break;
234 /* case 64: */
235 case 512:
236 clock_mode |= 0x20;
237 break;
238 case 1024:
239 clock_mode |= 0x40;
240 break;
241 case 128:
242 clock_mode |= 0x60;
243 break;
244 default:
245 panicf("ak4376: bad master clock multiple %d", mult);
246 return;
247 }
248
249 /* Handle the DSMLP bit in the MODE_CTRL register */
250 int mode_ctrl = 0x00;
251 if(ak4376.low_mode || hw_freq_sampr[fsel] <= SAMPR_12)
252 mode_ctrl |= 0x40;
253
254 /* Program the new settings */
255 ak4376_write(AK4376_REG_CLOCK_MODE, clock_mode);
256 ak4376_write(AK4376_REG_MODE_CTRL, mode_ctrl);
257 ak4376_write(AK4376_REG_PWR3, ak4376.low_mode ? 0x11 : 0x01);
258
259 /* Enable the master clock */
260 ak4376_set_mclk_freq(fsel, true);
261
262 /* Remember the frequency */
263 ak4376.fsel = fsel;
264}
265
266void audiohw_set_power_mode(int mode)
267{
268 /* This is handled via audiohw_set_frequency() since changing LPMODE
269 * bit requires power-down/power-up & changing other bits as well */
270 if(ak4376.low_mode != mode) {
271 ak4376.low_mode = mode;
272 audiohw_set_frequency(ak4376.fsel);
273 }
274}
diff --git a/firmware/drivers/axp173.c b/firmware/drivers/axp173.c
new file mode 100644
index 0000000000..22417650fc
--- /dev/null
+++ b/firmware/drivers/axp173.c
@@ -0,0 +1,419 @@
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, 0x82, 5}, /* ACIN_VOLTAGE */
39 {0x58, 0x82, 4}, /* ACIN_CURRENT */
40 {0x5a, 0x82, 3}, /* VBUS_VOLTAGE */
41 {0x5c, 0x82, 2}, /* VBUS_CURRENT */
42 {0x5e, 0x83, 7}, /* INTERNAL_TEMP */
43 {0x62, 0x82, 1}, /* TS_INPUT */
44 {0x78, 0x82, 7}, /* BATTERY_VOLTAGE */
45 {0x7a, 0x82, 6}, /* CHARGE_CURRENT */
46 {0x7c, 0x82, 6}, /* DISCHARGE_CURRENT */
47 {0x7e, 0x82, 1}, /* APS_VOLTAGE */
48 {0x70, 0xff, 0}, /* BATTERY_POWER */
49};
50
51static struct axp173 {
52 int adc_enable;
53} axp173;
54
55static void axp173_init_enabled_adcs(void)
56{
57 axp173.adc_enable = 0;
58
59 /* Read enabled ADCs from the hardware */
60 uint8_t regs[2];
61 int rc = i2c_reg_read(AXP173_BUS, AXP173_ADDR, 0x82, 2, &regs[0]);
62 if(rc != I2C_STATUS_OK)
63 return;
64
65 /* Parse registers to set ADC enable bits */
66 const struct axp173_adc_info* info = axp173_adc_info;
67 for(int i = 0; i < NUM_ADC_CHANNELS; ++i) {
68 if(info[i].en_reg == 0xff)
69 continue;
70
71 if(regs[info[i].en_reg - 0x82] & info[i].en_bit)
72 axp173.adc_enable |= 1 << i;
73 }
74
75 /* Handle battery power ADC */
76 if((axp173.adc_enable & (1 << ADC_BATTERY_VOLTAGE)) &&
77 (axp173.adc_enable & (1 << ADC_DISCHARGE_CURRENT))) {
78 axp173.adc_enable |= (1 << ADC_BATTERY_POWER);
79 }
80}
81
82void axp173_init(void)
83{
84 axp173_init_enabled_adcs();
85
86 /* We need discharge current ADC to reliably poll for a full battery */
87 int bits = axp173.adc_enable;
88 bits |= (1 << ADC_DISCHARGE_CURRENT);
89 axp173_adc_set_enabled(bits);
90}
91
92/* TODO: this can STILL indicate some false positives! */
93int axp173_battery_status(void)
94{
95 int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, 0x00);
96 if(r >= 0) {
97 /* Charging bit indicates we're currently charging */
98 if((r & 0x04) != 0)
99 return AXP173_BATT_CHARGING;
100
101 /* Not plugged in means we're discharging */
102 if((r & 0xf0) == 0)
103 return AXP173_BATT_DISCHARGING;
104 } else {
105 /* Report discharging if we can't find out power status */
106 return AXP173_BATT_DISCHARGING;
107 }
108
109 /* If the battery is full and not in use, the charging bit will be 0,
110 * there will be an external power source, AND the discharge current
111 * will be zero. Seems to rule out all false positives. */
112 int d = axp173_adc_read_raw(ADC_DISCHARGE_CURRENT);
113 if(d == 0)
114 return AXP173_BATT_FULL;
115
116 return AXP173_BATT_DISCHARGING;
117}
118
119int axp173_input_status(void)
120{
121#ifdef HAVE_BATTERY_SWITCH
122 int input_status = 0;
123#else
124 int input_status = AXP173_INPUT_BATTERY;
125#endif
126
127 int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, 0x00);
128 if(r < 0)
129 return input_status;
130
131 /* Check for AC input */
132 if(r & 0x80)
133 input_status |= AXP173_INPUT_AC;
134
135 /* Only report USB if ACIN and VBUS are not shorted */
136 if((r & 0x20) != 0 && (r & 0x02) == 0)
137 input_status |= AXP173_INPUT_USB;
138
139#ifdef HAVE_BATTERY_SWITCH
140 /* Check for battery presence if target defines it as removable */
141 r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, 0x01);
142 if(r >= 0 && (r & 0x20) != 0)
143 input_status |= AXP173_INPUT_BATTERY;
144#endif
145
146 return input_status;
147}
148
149int axp173_adc_read(int adc)
150{
151 int value = axp173_adc_read_raw(adc);
152 if(value == INT_MIN)
153 return INT_MIN;
154
155 return axp173_adc_conv_raw(adc, value);
156}
157
158int axp173_adc_read_raw(int adc)
159{
160 /* Don't give a reading if the ADC is not enabled */
161 if((axp173.adc_enable & (1 << adc)) == 0)
162 return INT_MIN;
163
164 /* Read the ADC */
165 uint8_t buf[3];
166 int count = (adc == ADC_BATTERY_POWER) ? 3 : 2;
167 uint8_t reg = axp173_adc_info[adc].reg;
168 int rc = i2c_reg_read(AXP173_BUS, AXP173_ADDR, reg, count, &buf[0]);
169 if(rc != I2C_STATUS_OK)
170 return INT_MIN;
171
172 /* Parse the value */
173 if(adc == ADC_BATTERY_POWER)
174 return (buf[0] << 16) | (buf[1] << 8) | buf[2];
175 else if(adc == ADC_CHARGE_CURRENT || adc == ADC_DISCHARGE_CURRENT)
176 return (buf[0] << 5) | (buf[1] & 0x1f);
177 else
178 return (buf[0] << 4) | (buf[1] & 0xf);
179}
180
181int axp173_adc_conv_raw(int adc, int value)
182{
183 switch(adc) {
184 case ADC_ACIN_VOLTAGE:
185 case ADC_VBUS_VOLTAGE:
186 /* 0 mV ... 6.9615 mV, step 1.7 mV */
187 return value * 17 / 10;
188 case ADC_ACIN_CURRENT:
189 /* 0 mA ... 2.5594 A, step 0.625 mA */
190 return value * 5 / 8;
191 case ADC_VBUS_CURRENT:
192 /* 0 mA ... 1.5356 A, step 0.375 mA */
193 return value * 3 / 8;
194 case ADC_INTERNAL_TEMP:
195 /* -144.7 C ... 264.8 C, step 0.1 C */
196 return value - 1447;
197 case ADC_TS_INPUT:
198 /* 0 mV ... 3.276 V, step 0.8 mV */
199 return value * 4 / 5;
200 case ADC_BATTERY_VOLTAGE:
201 /* 0 mV ... 4.5045 V, step 1.1 mV */
202 return value * 11 / 10;
203 case ADC_CHARGE_CURRENT:
204 case ADC_DISCHARGE_CURRENT:
205 /* 0 mA to 4.095 A, step 0.5 mA */
206 return value / 2;
207 case ADC_APS_VOLTAGE:
208 /* 0 mV to 5.733 V, step 1.4 mV */
209 return value * 7 / 5;
210 case ADC_BATTERY_POWER:
211 /* 0 uW to 23.6404 W, step 0.55 uW */
212 return value * 11 / 20;
213 default:
214 /* Shouldn't happen */
215 return INT_MIN;
216 }
217}
218
219int axp173_adc_get_enabled(void)
220{
221 return axp173.adc_enable;
222}
223
224void axp173_adc_set_enabled(int adc_bits)
225{
226 /* Ignore no-op */
227 if(adc_bits == axp173.adc_enable)
228 return;
229
230 /* Compute the new register values */
231 const struct axp173_adc_info* info = axp173_adc_info;
232 uint8_t regs[2] = {0, 0};
233 for(int i = 0; i < NUM_ADC_CHANNELS; ++i) {
234 if(info[i].en_reg == 0xff)
235 continue;
236
237 if(adc_bits & (1 << i))
238 regs[info[i].en_reg - 0x82] |= 1 << info[i].en_bit;
239 }
240
241 /* These ADCs share an enable bit */
242 if(adc_bits & ((1 << ADC_CHARGE_CURRENT)|(1 << ADC_DISCHARGE_CURRENT))) {
243 adc_bits |= (1 << ADC_CHARGE_CURRENT);
244 adc_bits |= (1 << ADC_DISCHARGE_CURRENT);
245 }
246
247 /* Enable required bits for battery power ADC */
248 if(adc_bits & (1 << ADC_BATTERY_POWER)) {
249 regs[0] |= 1 << info[ADC_DISCHARGE_CURRENT].en_bit;
250 regs[0] |= 1 << info[ADC_BATTERY_VOLTAGE].en_bit;
251 }
252
253 /* Update the configuration */
254 i2c_reg_write(AXP173_BUS, AXP173_ADDR, 0x82, 2, &regs[0]);
255 axp173.adc_enable = adc_bits;
256}
257
258int axp173_adc_get_rate(void)
259{
260 int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, 0x84);
261 if(r < 0)
262 return AXP173_ADC_RATE_100HZ; /* an arbitrary value */
263
264 return (r >> 6) & 3;
265}
266
267void axp173_adc_set_rate(int rate)
268{
269 i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, 0x84,
270 0xc0, (rate & 3) << 6, NULL);
271}
272
273static uint32_t axp173_cc_parse(const uint8_t* buf)
274{
275 return ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
276}
277
278void axp173_cc_read(uint32_t* charge, uint32_t* discharge)
279{
280 uint8_t buf[8];
281 int rc = i2c_reg_read(AXP173_BUS, AXP173_ADDR, 0xb0, 8, &buf[0]);
282 if(rc != I2C_STATUS_OK) {
283 if(charge)
284 *charge = 0;
285 if(discharge)
286 *discharge = 0;
287 return;
288 }
289
290 if(charge)
291 *charge = axp173_cc_parse(&buf[0]);
292 if(discharge)
293 *discharge = axp173_cc_parse(&buf[4]);
294}
295
296void axp173_cc_clear(void)
297{
298 i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR, 0xb8, 5, 1, NULL);
299}
300
301void axp173_cc_enable(bool en)
302{
303 i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR, 0xb8, 7, en ? 1 : 0, NULL);
304}
305
306#ifndef BOOTLOADER
307#define AXP173_DEBUG_BATTERY_STATUS 0
308#define AXP173_DEBUG_INPUT_STATUS 1
309#define AXP173_DEBUG_ADC_RATE 2
310#define AXP173_DEBUG_FIRST_ADC 3
311#define AXP173_DEBUG_ENTRIES (AXP173_DEBUG_FIRST_ADC + NUM_ADC_CHANNELS)
312
313static int axp173_debug_menu_cb(int action, struct gui_synclist* lists)
314{
315 (void)lists;
316
317 if(action == ACTION_NONE)
318 action = ACTION_REDRAW;
319
320 return action;
321}
322
323static const char* axp173_debug_menu_get_name(int item, void* data,
324 char* buf, size_t buflen)
325{
326 (void)data;
327
328 static const char* const adc_names[] = {
329 "V_acin", "I_acin", "V_vbus", "I_vbus", "T_int",
330 "V_ts", "V_batt", "I_chrg", "I_dchg", "V_aps", "P_batt"
331 };
332
333 static const char* const adc_units[] = {
334 "mV", "mA", "mV", "mA", "C", "mV", "mV", "mA", "mA", "mV", "uW",
335 };
336
337 int adc = item - AXP173_DEBUG_FIRST_ADC;
338 if(item >= AXP173_DEBUG_FIRST_ADC && adc < NUM_ADC_CHANNELS) {
339 int raw_value = axp173_adc_read_raw(adc);
340 if(raw_value == INT_MIN) {
341 snprintf(buf, buflen, "%s: [Disabled]", adc_names[adc]);
342 return buf;
343 }
344
345 int value = axp173_adc_conv_raw(adc, raw_value);
346 if(adc == ADC_INTERNAL_TEMP) {
347 snprintf(buf, buflen, "%s: %d.%d %s", adc_names[adc],
348 value/10, value%10, adc_units[adc]);
349 } else {
350 snprintf(buf, buflen, "%s: %d %s", adc_names[adc],
351 value, adc_units[adc]);
352 }
353
354 return buf;
355 }
356
357 switch(item) {
358 case AXP173_DEBUG_BATTERY_STATUS: {
359 switch(axp173_battery_status()) {
360 case AXP173_BATT_FULL:
361 return "Battery: Full";
362 case AXP173_BATT_CHARGING:
363 return "Battery: Charging";
364 case AXP173_BATT_DISCHARGING:
365 return "Battery: Discharging";
366 default:
367 return "Battery: Unknown";
368 }
369 } break;
370
371 case AXP173_DEBUG_INPUT_STATUS: {
372 int s = axp173_input_status();
373 const char* ac = (s & AXP173_INPUT_AC) ? " AC" : "";
374 const char* usb = (s & AXP173_INPUT_USB) ? " USB" : "";
375 const char* batt = (s & AXP173_INPUT_BATTERY) ? " Battery" : "";
376 snprintf(buf, buflen, "Inputs:%s%s%s", ac, usb, batt);
377 return buf;
378 } break;
379
380 case AXP173_DEBUG_ADC_RATE: {
381 int rate = 25 << axp173_adc_get_rate();
382 snprintf(buf, buflen, "ADC sample rate: %d Hz", rate);
383 return buf;
384 } break;
385
386 default:
387 return "---";
388 }
389}
390
391bool axp173_debug_menu(void)
392{
393 struct simplelist_info info;
394 simplelist_info_init(&info, "AXP173 debug", AXP173_DEBUG_ENTRIES, NULL);
395 info.action_callback = axp173_debug_menu_cb;
396 info.get_name = axp173_debug_menu_get_name;
397 return simplelist_show_list(&info);
398}
399#endif /* !BOOTLOADER */
400
401/* This is basically the only valid implementation, so define it here */
402unsigned int power_input_status(void)
403{
404 unsigned int state = 0;
405 int input_status = axp173_input_status();
406
407 if(input_status & AXP173_INPUT_AC)
408 state |= POWER_INPUT_MAIN_CHARGER;
409
410 if(input_status & AXP173_INPUT_USB)
411 state |= POWER_INPUT_USB_CHARGER;
412
413#ifdef HAVE_BATTERY_SWITCH
414 if(input_status & AXP173_INPUT_BATTERY)
415 state |= POWER_INPUT_BATTERY;
416#endif
417
418 return state;
419}
diff --git a/firmware/drivers/rtc/rtc_x1000.c b/firmware/drivers/rtc/rtc_x1000.c
new file mode 100644
index 0000000000..1d3a5484e9
--- /dev/null
+++ b/firmware/drivers/rtc/rtc_x1000.c
@@ -0,0 +1,104 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * Based mainly on rtc_jz4760.c,
13 * Copyright (C) 2016 by Roman Stolyarov
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25#include "rtc.h"
26#include "x1000/rtc.h"
27#include <stdint.h>
28
29/* 4 byte magic number 'RTCV' */
30#define RTCV 0x52544356
31
32/* expected RTC clock frequency */
33#define NC1HZ_EXPECTED (32768 - 1)
34
35static void rtc_write_reg(uint32_t addr, uint32_t value)
36{
37 while(jz_readf(RTC_CR, WRDY) == 0);
38 REG_RTC_WENR = 0xa55a;
39 while(jz_readf(RTC_WENR, WEN) == 0);
40 while(jz_readf(RTC_CR, WRDY) == 0);
41 (*(volatile uint32_t*)addr) = value;
42 while(jz_readf(RTC_CR, WRDY) == 0);
43}
44
45void rtc_init(void)
46{
47 /* Check if we totally lost power and need to reset the RTC */
48 if(REG_RTC_HSPR != RTCV || jz_readf(RTC_GR, NC1HZ) != NC1HZ_EXPECTED) {
49 rtc_write_reg(JA_RTC_GR, NC1HZ_EXPECTED);
50 rtc_write_reg(JA_RTC_HWFCR, 3200);
51 rtc_write_reg(JA_RTC_HRCR, 2048);
52 rtc_write_reg(JA_RTC_SR, 1546300800); /* 01/01/2019 */
53 rtc_write_reg(JA_RTC_CR, 1);
54 rtc_write_reg(JA_RTC_HSPR, RTCV);
55 }
56
57 rtc_write_reg(JA_RTC_HWRSR, 0);
58}
59
60int rtc_read_datetime(struct tm* tm)
61{
62 time_t time = REG_RTC_SR;
63 gmtime_r(&time, tm);
64 return 1;
65}
66
67int rtc_write_datetime(const struct tm* tm)
68{
69 time_t time = mktime((struct tm*)tm);
70 rtc_write_reg(JA_RTC_SR, time);
71 return 1;
72}
73
74#ifdef HAVE_RTC_ALARM
75/* TODO: implement the RTC alarm */
76
77void rtc_set_alarm(int h, int m)
78{
79 (void)h;
80 (void)m;
81}
82
83void rtc_get_alarm(int* h, int* m)
84{
85 (void)h;
86 (void)m;
87}
88
89void rtc_enable_alarm(bool enable)
90{
91 (void)enable;
92}
93
94bool rtc_check_alarm_started(bool release_alarm)
95{
96 (void)release_alarm;
97 return false;
98}
99
100bool rtc_check_alarm_flag(void)
101{
102 return false;
103}
104#endif