1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
|
/* Copyright 2018 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Battery fuel gauge parameters
*/
#include "battery_fuel_gauge.h"
#include "battery_smart.h"
#include "charge_state.h"
#include "common.h"
#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "util.h"
#define CPRINTS(format, args...) cprints(CC_CHARGER, format, ## args)
static enum battery_present batt_pres_prev = BP_NOT_SURE;
/* Get type of the battery connected on the board */
static int get_battery_type(void)
{
char manuf_name[32], device_name[32];
int i;
static enum battery_type battery_type = BATTERY_TYPE_COUNT;
/*
* If battery_type is not the default value, then can return here
* as there is no need to query the fuel gauge.
*/
if (battery_type != BATTERY_TYPE_COUNT)
return battery_type;
/* Get the manufacturer name. If can't read then just exit */
if (battery_manufacturer_name(manuf_name, sizeof(manuf_name)))
return battery_type;
/*
* Compare the manufacturer name read from the fuel gauge to the
* manufacturer names defined in the board_battery_info table. If
* a device name has been specified in the board_battery_info table,
* then both the manufacturer and device name must match.
*/
for (i = 0; i < BATTERY_TYPE_COUNT; i++) {
const struct fuel_gauge_info * const fuel_gauge =
&board_battery_info[i].fuel_gauge;
if (strcasecmp(manuf_name, fuel_gauge->manuf_name))
continue;
if (fuel_gauge->device_name != NULL) {
if (battery_device_name(device_name,
sizeof(device_name)))
continue;
if (strcasecmp(device_name, fuel_gauge->device_name))
continue;
}
CPRINTS("found batt:%s", fuel_gauge->manuf_name);
battery_type = i;
break;
}
return battery_type;
}
/*
* Initialize the battery type for the board.
*
* The first call to battery_get_info() is when the charger task starts, so
* initialize the battery type as soon as I2C is initialized.
*/
static void init_battery_type(void)
{
if (get_battery_type() == BATTERY_TYPE_COUNT)
CPRINTS("battery not found");
}
DECLARE_HOOK(HOOK_INIT, init_battery_type, HOOK_PRIO_INIT_I2C + 1);
static inline const struct board_batt_params *get_batt_params(void)
{
int type = get_battery_type();
return &board_battery_info[type == BATTERY_TYPE_COUNT ?
DEFAULT_BATTERY_TYPE : type];
}
const struct battery_info *battery_get_info(void)
{
return &get_batt_params()->batt_info;
}
int board_cut_off_battery(void)
{
int rv;
int cmd;
int data;
int type = get_battery_type();
/* If battery type is unknown can't send ship mode command */
if (type == BATTERY_TYPE_COUNT)
return EC_RES_ERROR;
/* Ship mode command requires writing 2 data values */
cmd = board_battery_info[type].fuel_gauge.ship_mode.reg_addr;
data = board_battery_info[type].fuel_gauge.ship_mode.reg_data[0];
rv = sb_write(cmd, data);
if (rv != EC_SUCCESS)
return EC_RES_ERROR;
data = board_battery_info[type].fuel_gauge.ship_mode.reg_data[1];
rv = sb_write(cmd, data);
return rv ? EC_RES_ERROR : EC_RES_SUCCESS;
}
enum battery_present battery_hw_present(void)
{
/* The GPIO is low when the battery is physically present */
return gpio_get_level(GPIO_EC_BATT_PRES_L) ? BP_NO : BP_YES;
}
static int battery_init(void)
{
int batt_status;
return battery_status(&batt_status) ? 0 :
!!(batt_status & STATUS_INITIALIZED);
}
/*
* This function checks the charge/discharge FET status bits. Each battery type
* supported provides the register address, mask, and disconnect value for these
* 2 FET status bits. If the FET status matches the disconnected value, then
* BATTERY_DISCONNECTED is returned. This function is required to handle the
* cases when the fuel gauge is awake and will return a non-zero state of
* charge, but is not able yet to provide power (i.e. discharge FET is not
* active). By returning BATTERY_DISCONNECTED the AP will not be powered up
* until either the external charger is able to provided enough power, or
* the battery is able to provide power and thus prevent a brownout when the
* AP is powered on by the EC.
*/
enum battery_disconnect_state battery_get_disconnect_state(void)
{
int rv;
int reg;
uint8_t data[6];
int type = get_battery_type();
/* If battery type is not known, can't check CHG/DCHG FETs */
if (type == BATTERY_TYPE_COUNT) {
/* Still don't know, so return here */
return BATTERY_DISCONNECT_ERROR;
}
/* Read the status of charge/discharge FETs */
if (board_battery_info[type].fuel_gauge.fet.mfgacc_support == 1) {
rv = sb_read_mfgacc(PARAM_OPERATION_STATUS,
SB_ALT_MANUFACTURER_ACCESS, data, sizeof(data));
/* Get the lowest 16bits of the OperationStatus() data */
reg = data[2] | data[3] << 8;
} else
rv = sb_read(board_battery_info[type].fuel_gauge.fet.reg_addr,
®);
if (rv)
return BATTERY_DISCONNECT_ERROR;
if ((reg & board_battery_info[type].fuel_gauge.fet.reg_mask) ==
board_battery_info[type].fuel_gauge.fet.disconnect_val) {
CPRINTS("Batt disconnected: reg 0x%04x mask 0x%04x disc 0x%04x",
reg,
board_battery_info[type].fuel_gauge.fet.reg_mask,
board_battery_info[type].fuel_gauge.fet.disconnect_val);
return BATTERY_DISCONNECTED;
}
return BATTERY_NOT_DISCONNECTED;
}
/*
* Physical detection of battery.
*/
static enum battery_present battery_check_present_status(void)
{
enum battery_present batt_pres;
/* Get the physical hardware status */
batt_pres = battery_hw_present();
/*
* If the battery is not physically connected, then no need to perform
* any more checks.
*/
if (batt_pres != BP_YES)
return batt_pres;
/*
* If the battery is present now and was present last time we checked,
* return early.
*/
if (batt_pres == batt_pres_prev)
return batt_pres;
/*
* Ensure that battery is:
* 1. Not in cutoff
* 2. Initialized
*/
if (battery_is_cut_off() != BATTERY_CUTOFF_STATE_NORMAL ||
battery_init() == 0) {
batt_pres = BP_NO;
}
return batt_pres;
}
enum battery_present battery_is_present(void)
{
batt_pres_prev = battery_check_present_status();
return batt_pres_prev;
}
|