Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
CTCaer
GitHub Repository: CTCaer/hekate
Path: blob/master/bdk/power/max17050.c
3694 views
1
/*
2
* Fuel gauge driver for Nintendo Switch's Maxim 17050
3
*
4
* Copyright (c) 2011 Samsung Electronics
5
* MyungJoo Ham <[email protected]>
6
* Copyright (c) 2018-2025 CTCaer
7
*
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation; either version 2 of the License, or
11
* (at your option) any later version.
12
*
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
17
*
18
* You should have received a copy of the GNU General Public License
19
* along with this program; if not, write to the Free Software
20
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
*
22
* This driver is based on max17040_battery.c
23
*/
24
25
#include "max17050.h"
26
#include <soc/i2c.h>
27
#include <soc/timer.h>
28
29
/* Board default values */
30
#define BOARD_CGAIN 2 /* Actual: 1.99993 */
31
#define BOARD_RSENSE_MOHM 5 /* 0.005 Ohm */
32
#define ADJ_RSENSE_MOHM (BOARD_RSENSE_MOHM * BOARD_CGAIN) /* 0.01 Ohm */
33
34
/* Consider RepCap which is less then 10 units below FullCAP full */
35
#define FULL_THRESHOLD 10
36
37
/* Status register bits */
38
#define STATUS_POR_BIT BIT(1)
39
#define STATUS_BST_BIT BIT(3)
40
#define STATUS_VMN_BIT BIT(8)
41
#define STATUS_TMN_BIT BIT(9)
42
#define STATUS_SMN_BIT BIT(10)
43
#define STATUS_BI_BIT BIT(11)
44
#define STATUS_VMX_BIT BIT(12)
45
#define STATUS_TMX_BIT BIT(13)
46
#define STATUS_SMX_BIT BIT(14)
47
#define STATUS_BR_BIT BIT(15)
48
49
#define VFSOC0_LOCK 0x0000
50
#define VFSOC0_UNLOCK 0x0080
51
52
#define MAX17050_VMAX_TOLERANCE 50 /* 50 mV */
53
54
static u16 max17050_get_reg(u8 reg)
55
{
56
u16 data = 0;
57
58
i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, reg);
59
60
return data;
61
}
62
63
int max17050_get_version(u32 *value)
64
{
65
u16 data = max17050_get_reg(MAX17050_DevName);
66
if (value)
67
*value = data;
68
69
if (data == 0x00AC)
70
return 0;
71
else
72
return 1;
73
}
74
75
int max17050_get_property(enum MAX17050_reg reg, int *value)
76
{
77
u16 data;
78
79
switch (reg)
80
{
81
case MAX17050_Age: // Age (percent). Based on 100% x (FullCAP Register/DesignCap).
82
data = max17050_get_reg(MAX17050_Age);
83
*value = data >> 8; /* Show MSB. 1% increments */
84
break;
85
case MAX17050_Cycles: // Cycle count.
86
*value = max17050_get_reg(MAX17050_Cycles);
87
break;
88
case MAX17050_MinVolt: // Voltage max/min
89
data = max17050_get_reg(MAX17050_MinMaxVolt);
90
*value = (data & 0xff) * 20; /* Voltage MIN. Units of 20mV */
91
break;
92
case MAX17050_MaxVolt: // Voltage max/min
93
data = max17050_get_reg(MAX17050_MinMaxVolt);
94
*value = (data >> 8) * 20; /* Voltage MAX. Units of LSB = 20mV */
95
break;
96
case MAX17050_V_empty: // Voltage min design.
97
data = max17050_get_reg(MAX17050_V_empty);
98
*value = (data >> 7) * 10; /* Units of LSB = 10mV */
99
break;
100
case MAX17050_VCELL: // Voltage now.
101
data = max17050_get_reg(MAX17050_VCELL);
102
*value = (data >> 3) * 625 / 1000; /* Units of LSB = 0.625mV */
103
break;
104
case MAX17050_AvgVCELL: // Voltage avg.
105
data = max17050_get_reg(MAX17050_AvgVCELL);
106
*value = (data >> 3) * 625 / 1000; /* Units of LSB = 0.625mV */
107
break;
108
case MAX17050_OCVInternal: // Voltage ocv.
109
data = max17050_get_reg(MAX17050_OCVInternal);
110
*value = (data >> 3) * 625 / 1000; /* Units of LSB = 0.625mV */
111
break;
112
case MAX17050_RepSOC: // Capacity %.
113
*value = max17050_get_reg(MAX17050_RepSOC);
114
break;
115
case MAX17050_DesignCap: // Charge full design.
116
data = max17050_get_reg(MAX17050_DesignCap);
117
*value = (u32)data * 5 / ADJ_RSENSE_MOHM; /* Units of LSB = 5uVh / Rsense = 0.5mAh */
118
break;
119
case MAX17050_FullCAP: // Charge full.
120
data = max17050_get_reg(MAX17050_FullCAP);
121
*value = (u32)data * 5 / ADJ_RSENSE_MOHM; /* Units of LSB = 5uVh / Rsense = 0.5mAh */
122
break;
123
case MAX17050_RepCap: // Charge now.
124
data = max17050_get_reg(MAX17050_RepCap);
125
*value = (u32)data * 5 / ADJ_RSENSE_MOHM; /* Units of LSB = 5uVh / Rsense = 0.5mAh */
126
break;
127
case MAX17050_TEMP: // Temp.
128
data = max17050_get_reg(MAX17050_TEMP);
129
*value = (s16)data;
130
*value = *value * 10 / 256;
131
break;
132
case MAX17050_Current: // Current now.
133
data = max17050_get_reg(MAX17050_Current);
134
*value = (int)(s16)data * 15625 / ADJ_RSENSE_MOHM / 10; /* Units of LSB = 1.5625uV / Rsense = 156.25uA */
135
break;
136
case MAX17050_AvgCurrent: // Current avg.
137
data = max17050_get_reg(MAX17050_AvgCurrent);
138
*value = (int)(s16)data * 15625 / ADJ_RSENSE_MOHM / 10; /* Units of LSB = 1.5625uV / Rsense = 156.25uA */
139
break;
140
default:
141
return 1;
142
}
143
return 0;
144
}
145
146
static int _max17050_write_verify_reg(u8 reg, u16 value)
147
{
148
int retries = 8;
149
int ret;
150
151
do
152
{
153
ret = i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, reg, (u8 *)&value, 2);
154
u16 read_value = max17050_get_reg(reg);
155
if (!ret && read_value == value)
156
break;
157
} while (--retries);
158
159
return ret;
160
}
161
162
static void _max17050_override_por(u8 reg, u16 value)
163
{
164
if (value)
165
i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, reg, (u8 *)&value, 2);
166
}
167
168
static void _max17050_load_new_capacity_params()
169
{
170
u16 fullcap, repSoc, dq_acc, dp_acc;
171
172
fullcap = 0x2476; // 4667mAh design capacity.
173
dq_acc = 0x10bc; // From a healthy fuel gauge.
174
dp_acc = 0x5e09; // =||=
175
repSoc = 0x6400; // 100%.
176
177
_max17050_write_verify_reg(MAX17050_RemCap, fullcap);
178
_max17050_write_verify_reg(MAX17050_RepCap, fullcap);
179
180
_max17050_write_verify_reg(MAX17050_dQacc, dq_acc);
181
_max17050_write_verify_reg(MAX17050_dPacc, dp_acc);
182
183
_max17050_write_verify_reg(MAX17050_FullCAP, fullcap);
184
//i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_DesignCap, (u8 *)&fullcap, 2);
185
_max17050_write_verify_reg(MAX17050_FullCAPNom, fullcap);
186
/* Update SOC register with new SOC */
187
i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_RepSOC, (u8 *)&repSoc, 2);
188
}
189
190
static void _max17050_reset_vfsoc0_reg()
191
{
192
u16 lockVal = 0;
193
u16 vfSoc = 0x6440; // >100% for fully charged battery
194
195
lockVal = VFSOC0_UNLOCK;
196
i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_VFSOC0Enable, (u8 *)&lockVal, 2);
197
198
_max17050_write_verify_reg(MAX17050_VFSOC0, vfSoc);
199
200
lockVal = VFSOC0_LOCK;
201
i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_VFSOC0Enable, (u8 *)&lockVal, 2);
202
}
203
204
static void _max17050_update_capacity_regs()
205
{
206
u16 value = 0x2476; // Set to 4667mAh design capacity.
207
_max17050_write_verify_reg(MAX17050_FullCAP, value);
208
_max17050_write_verify_reg(MAX17050_FullCAPNom, value);
209
//i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_DesignCap, config->design_cap, 2);
210
}
211
212
static void _max17050_write_config_regs()
213
{
214
u16 value = 0;
215
216
value = 0x7254;
217
i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_CONFIG, (u8 *)&value, 2);
218
value = 0x2473;
219
i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_LearnCFG, (u8 *)&value, 2);
220
//i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_FilterCFG, (u8 *)&value, 2)
221
//i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_RelaxCFG, (u8 *)&value, 2)
222
//i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_FullSOCThr, (u8 *)&value, 2)
223
}
224
225
/*
226
* Block write all the override values coming from platform data.
227
* This function MUST be called before the POR initialization proceedure
228
* specified by maxim.
229
*/
230
static void _max17050_override_por_values()
231
{
232
u16 dq_acc = 0x10bc; // From a healthy fuel gauge.
233
u16 dp_acc = 0x5e09; // =||=
234
235
_max17050_override_por(MAX17050_dQacc, dq_acc);
236
_max17050_override_por(MAX17050_dPacc, dp_acc);
237
238
//_max17050_override_por(MAX17050_RCOMP0, config->rcomp0); //0x58
239
//_max17050_override_por(MAX17050_TempCo, config->tcompc0); //0x1b22
240
241
//u16 k_empty0 = 0x439;
242
//_max17050_override_por(map, MAX17050_K_empty0, k_empty0); // Unknown cell data
243
}
244
245
static void _max17050_set_por_bit(u16 value)
246
{
247
_max17050_write_verify_reg(MAX17050_STATUS, value);
248
}
249
250
void max17050_fix_configuration()
251
{
252
/* Init phase, set the POR bit */
253
_max17050_set_por_bit(STATUS_POR_BIT);
254
255
/* Override POR values */
256
_max17050_override_por_values();
257
/* After Power up, the MAX17050 requires 500ms in order
258
* to perform signal debouncing and initial SOC reporting
259
*/
260
msleep(500);
261
262
/* Initialize configaration */
263
_max17050_write_config_regs();
264
265
/* update capacity params */
266
_max17050_update_capacity_regs();
267
268
/* delay must be atleast 350mS to allow VFSOC
269
* to be calculated from the new configuration
270
*/
271
msleep(350);
272
273
/* reset vfsoc0 reg */
274
_max17050_reset_vfsoc0_reg();
275
276
/* load new capacity params */
277
_max17050_load_new_capacity_params();
278
279
/* Init complete, Clear the POR bit */
280
//_max17050_set_por_bit(0); // Should we? Or let the switch to reconfigure POR?
281
282
// Sets POR, BI, BR.
283
_max17050_set_por_bit(0x8801);
284
}
285
286
void max17050_dump_regs(void *buf)
287
{
288
u16 *buff = (u16 *)buf;
289
290
// Unlock model table.
291
u16 unlock = 0x59;
292
i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_MODELEnable1, (u8 *)&unlock, 2);
293
unlock = 0xC4;
294
i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_MODELEnable2, (u8 *)&unlock, 2);
295
296
// Dump all battery fuel gauge registers.
297
for (u32 i = 0; i < 0x100; i++)
298
{
299
buff[i] = max17050_get_reg(i);
300
msleep(1);
301
}
302
303
// Lock model table.
304
unlock = 0;
305
i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_MODELEnable1, (u8 *)&unlock, 2);
306
i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_MODELEnable2, (u8 *)&unlock, 2);
307
}
308
309