summaryrefslogtreecommitdiff
path: root/firmware/drivers/cw2015.c
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-05-23 17:30:58 +0100
committerAidan MacDonald <amachronic@protonmail.com>2021-07-13 22:01:33 +0100
commit4c60bc9e681865fcfc149775a1ed7ccd2613d5bf (patch)
tree99f8d91af2c171cf3843f0c14d41a20d9dc29c4f /firmware/drivers/cw2015.c
parent3abb7c5dd5be2ec6744bfc0a80967b20f1b59e30 (diff)
downloadrockbox-4c60bc9e681865fcfc149775a1ed7ccd2613d5bf.tar.gz
rockbox-4c60bc9e681865fcfc149775a1ed7ccd2613d5bf.zip
New port: Shanling Q1 native
- Audio playback works - Touchscreen and buttons work - Bootloader works and is capable of dual boot - Plugins are working - Cabbiev2 theme has been ported - Stable for general usage Thanks to Marc Aarts for porting Cabbiev2 and plugin bitmaps. There's a few minor known issues: - Bootloader must be installed manually using 'usbboot' as there is no support in jztool yet. - Keymaps may be lacking, need further testing and feedback. - Some plugins may not be fully adapted to the screen size and could benefit from further tweaking. - LCD shows abnormal effects under some circumstances: for example, after viewing a mostly black screen an afterimage appears briefly when going back to a brightly-lit screen. Sudden power-off without proper shutdown of the backlight causes a "dissolving" effect. - CW2015 battery reporting driver is buggy, and disabled for now. Battery reporting is currently voltage-based using the AXP192. Change-Id: I635e83f02a880192c5a82cb0861ad3a61c137c3a
Diffstat (limited to 'firmware/drivers/cw2015.c')
-rw-r--r--firmware/drivers/cw2015.c191
1 files changed, 191 insertions, 0 deletions
diff --git a/firmware/drivers/cw2015.c b/firmware/drivers/cw2015.c
new file mode 100644
index 0000000000..705ca16e22
--- /dev/null
+++ b/firmware/drivers/cw2015.c
@@ -0,0 +1,191 @@
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 "cw2015.h"
23#include "i2c-async.h"
24#include <string.h>
25#include "system.h"
26
27/* Headers for the debug menu */
28#ifndef BOOTLOADER
29# include "action.h"
30# include "list.h"
31# include <stdio.h>
32#endif
33
34/* Battery profile info is an opaque blob. According to this,
35 * https://lore.kernel.org/linux-pm/20200503154855.duwj2djgqfiyleq5@earth.universe/T/#u
36 * the blob only comes from Cellwise testing a physical battery and cannot be
37 * obtained any other way. It's specific to a given battery so each target has
38 * its own profile.
39 *
40 * Profile data seems to be retained on the chip so it's not a hard requirement
41 * to define this. Provided you don't lose power in the meantime, it should be
42 * enough to just boot the OF, then boot Rockbox and read out the battery info
43 * from the CW2015 debug screen.
44 */
45#if defined(SHANLING_Q1)
46static const uint8_t device_batinfo[CW2015_SIZE_BATINFO] = {
47 0x15, 0x7E, 0x61, 0x59, 0x57, 0x55, 0x56, 0x4C,
48 0x4E, 0x4D, 0x50, 0x4C, 0x45, 0x3A, 0x2D, 0x27,
49 0x22, 0x1E, 0x19, 0x1E, 0x2A, 0x3C, 0x48, 0x45,
50 0x1D, 0x94, 0x08, 0xF6, 0x15, 0x29, 0x48, 0x51,
51 0x5D, 0x60, 0x63, 0x66, 0x45, 0x1D, 0x83, 0x38,
52 0x09, 0x43, 0x16, 0x42, 0x76, 0x98, 0xA5, 0x1B,
53 0x41, 0x76, 0x99, 0xBF, 0x80, 0xC0, 0xEF, 0xCB,
54 0x2F, 0x00, 0x64, 0xA5, 0xB5, 0x0E, 0x30, 0x29,
55};
56#else
57# define NO_BATINFO
58#endif
59
60static uint8_t chip_batinfo[CW2015_SIZE_BATINFO];
61
62/* TODO: Finish implementing this
63 *
64 * Although this chip might give a better battery estimate than voltage does,
65 * the mainline linux driver has a lot of weird hacks due to oddities like the
66 * SoC getting stuck during charging, and from limited testing it seems this
67 * may occur for the Q1 too.
68 */
69
70static int cw2015_read_bat_info(uint8_t* data)
71{
72 for(int i = 0; i < CW2015_SIZE_BATINFO; ++i) {
73 int r = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_BATINFO + i);
74 if(r < 0)
75 return r;
76
77 data[i] = r & 0xff;
78 }
79
80 return 0;
81}
82
83void cw2015_init(void)
84{
85 /* mdelay(100); */
86 int rc = cw2015_read_bat_info(&chip_batinfo[0]);
87 if(rc < 0)
88 memset(chip_batinfo, 0, sizeof(chip_batinfo));
89}
90
91int cw2015_get_vcell(void)
92{
93 int vcell_msb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_VCELL);
94 int vcell_lsb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_VCELL+1);
95
96 if(vcell_msb < 0 || vcell_lsb < 0)
97 return -1;
98
99 /* 14 bits, resolution 305 uV */
100 int v_raw = ((vcell_msb & 0x3f) << 8) | vcell_lsb;
101 return v_raw * 61 / 200;
102}
103
104int cw2015_get_soc(void)
105{
106 int soc_msb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_SOC);
107
108 if(soc_msb < 0)
109 return -1;
110
111 /* MSB is the state of charge in percentage.
112 * the LSB contains fractional information not useful to Rockbox. */
113 return soc_msb & 0xff;
114}
115
116int cw2015_get_rrt(void)
117{
118 int rrt_msb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_RRT_ALERT);
119 int rrt_lsb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_RRT_ALERT+1);
120
121 if(rrt_msb < 0 || rrt_lsb < 0)
122 return -1;
123
124 /* 13 bits, resolution 1 minute */
125 return ((rrt_msb & 0x1f) << 8) | rrt_lsb;
126}
127
128const uint8_t* cw2015_get_bat_info(void)
129{
130 return &chip_batinfo[0];
131}
132
133#ifndef BOOTLOADER
134enum {
135 CW2015_DEBUG_VCELL = 0,
136 CW2015_DEBUG_SOC,
137 CW2015_DEBUG_RRT,
138 CW2015_DEBUG_BATINFO,
139 CW2015_DEBUG_BATINFO_LAST = CW2015_DEBUG_BATINFO + 7,
140 CW2015_DEBUG_NUM_ENTRIES,
141};
142
143static int cw2015_debug_menu_cb(int action, struct gui_synclist* lists)
144{
145 (void)lists;
146
147 if(action == ACTION_NONE)
148 action = ACTION_REDRAW;
149
150 return action;
151}
152
153static const char* cw2015_debug_menu_get_name(int item, void* data,
154 char* buf, size_t buflen)
155{
156 (void)data;
157
158 /* hexdump of battery info */
159 if(item >= CW2015_DEBUG_BATINFO && item <= CW2015_DEBUG_BATINFO_LAST) {
160 int i = item - CW2015_DEBUG_BATINFO;
161 const uint8_t* batinfo = cw2015_get_bat_info();
162 snprintf(buf, buflen, "BatInfo%d: %02x %02x %02x %02x %02x %02x %02x %02x", i,
163 batinfo[8*i + 0], batinfo[8*i + 1], batinfo[8*i + 2], batinfo[8*i + 3],
164 batinfo[8*i + 4], batinfo[8*i + 5], batinfo[8*i + 6], batinfo[8*i + 7]);
165 return buf;
166 }
167
168 switch(item) {
169 case CW2015_DEBUG_VCELL:
170 snprintf(buf, buflen, "VCell: %d mV", cw2015_get_vcell());
171 return buf;
172 case CW2015_DEBUG_SOC:
173 snprintf(buf, buflen, "SOC: %d%%", cw2015_get_soc());
174 return buf;
175 case CW2015_DEBUG_RRT:
176 snprintf(buf, buflen, "Runtime: %d min", cw2015_get_rrt());
177 return buf;
178 default:
179 return "---";
180 }
181}
182
183bool cw2015_debug_menu(void)
184{
185 struct simplelist_info info;
186 simplelist_info_init(&info, "CW2015 debug", CW2015_DEBUG_NUM_ENTRIES, NULL);
187 info.action_callback = cw2015_debug_menu_cb;
188 info.get_name = cw2015_debug_menu_get_name;
189 return simplelist_show_list(&info);
190}
191#endif