diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-02-27 22:08:58 +0000 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-03-28 00:01:37 +0000 |
commit | 3ec66893e377b088c1284d2d23adb2aeea6d7965 (patch) | |
tree | b647717f83ad56b15dc42cfdef5d04d68cd9bd6b /firmware/drivers | |
parent | 83fcbedc65f4b9ae7e491ecf6f07c0af4b245f74 (diff) | |
download | rockbox-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.c | 274 | ||||
-rw-r--r-- | firmware/drivers/axp173.c | 419 | ||||
-rw-r--r-- | firmware/drivers/rtc/rtc_x1000.c | 104 |
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 */ | ||
43 | static 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 | |||
60 | static struct ak4376 { | ||
61 | int fsel; | ||
62 | int low_mode; | ||
63 | int regs[AK4376_NUM_REGS]; | ||
64 | } ak4376; | ||
65 | |||
66 | void 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 | |||
110 | void 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 | |||
121 | void 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 | |||
133 | int 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 | |||
142 | static 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 | |||
150 | static 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 | |||
169 | static 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 | |||
176 | static 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 | |||
183 | void 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 | |||
213 | void 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 | |||
221 | void 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 | |||
266 | void 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 | |||
33 | static 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 | |||
51 | static struct axp173 { | ||
52 | int adc_enable; | ||
53 | } axp173; | ||
54 | |||
55 | static 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, ®s[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 | |||
82 | void 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! */ | ||
93 | int 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 | |||
119 | int 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 | |||
149 | int 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 | |||
158 | int 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 | |||
181 | int 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 | |||
219 | int axp173_adc_get_enabled(void) | ||
220 | { | ||
221 | return axp173.adc_enable; | ||
222 | } | ||
223 | |||
224 | void 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, ®s[0]); | ||
255 | axp173.adc_enable = adc_bits; | ||
256 | } | ||
257 | |||
258 | int 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 | |||
267 | void axp173_adc_set_rate(int rate) | ||
268 | { | ||
269 | i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, 0x84, | ||
270 | 0xc0, (rate & 3) << 6, NULL); | ||
271 | } | ||
272 | |||
273 | static 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 | |||
278 | void 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 | |||
296 | void axp173_cc_clear(void) | ||
297 | { | ||
298 | i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR, 0xb8, 5, 1, NULL); | ||
299 | } | ||
300 | |||
301 | void 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 | |||
313 | static 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 | |||
323 | static 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 | |||
391 | bool 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 */ | ||
402 | unsigned 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 | |||
35 | static 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 | |||
45 | void 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 | |||
60 | int rtc_read_datetime(struct tm* tm) | ||
61 | { | ||
62 | time_t time = REG_RTC_SR; | ||
63 | gmtime_r(&time, tm); | ||
64 | return 1; | ||
65 | } | ||
66 | |||
67 | int 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 | |||
77 | void rtc_set_alarm(int h, int m) | ||
78 | { | ||
79 | (void)h; | ||
80 | (void)m; | ||
81 | } | ||
82 | |||
83 | void rtc_get_alarm(int* h, int* m) | ||
84 | { | ||
85 | (void)h; | ||
86 | (void)m; | ||
87 | } | ||
88 | |||
89 | void rtc_enable_alarm(bool enable) | ||
90 | { | ||
91 | (void)enable; | ||
92 | } | ||
93 | |||
94 | bool rtc_check_alarm_started(bool release_alarm) | ||
95 | { | ||
96 | (void)release_alarm; | ||
97 | return false; | ||
98 | } | ||
99 | |||
100 | bool rtc_check_alarm_flag(void) | ||
101 | { | ||
102 | return false; | ||
103 | } | ||
104 | #endif | ||