summaryrefslogtreecommitdiff
path: root/common/battery_v1.c
blob: 2c81d6b4463f81b421e446c32db06fef324b0803 (plain)
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
/* Copyright 2021 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 * Battery V1 APIs.
 */

#include "battery.h"
#include "charge_state.h"
#include "common.h"
#include "console.h"
#include "extpower.h"
#include "host_command.h"
#include "math_util.h"
#include "printf.h"
#include "util.h"

#define CPRINTF(format, args...) cprintf(CC_CHARGER, format, ##args)
#define CPRINTS(format, args...) cprints(CC_CHARGER, format, ##args)

/* Returns zero if every item was updated. */
int update_static_battery_info(void)
{
	char *batt_str;
	int batt_serial;
	uint8_t batt_flags = 0;
	/*
	 * The return values have type enum ec_error_list, but EC_SUCCESS is
	 * zero. We'll just look for any failures so we can try them all again.
	 */
	int rv;

	/* Smart battery serial number is 16 bits */
	batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_SERIAL);
	memset(batt_str, 0, EC_MEMMAP_TEXT_MAX);
	rv = battery_serial_number(&batt_serial);
	if (!rv) {
		if (snprintf(batt_str, EC_MEMMAP_TEXT_MAX, "%04X",
			     batt_serial) <= 0)
			rv |= EC_ERROR_UNKNOWN;
	}

	/* Design Capacity of Full */
	rv |= battery_design_capacity(
		(int *)host_get_memmap(EC_MEMMAP_BATT_DCAP));

	/* Design Voltage */
	rv |= battery_design_voltage(
		(int *)host_get_memmap(EC_MEMMAP_BATT_DVLT));

	/* Last Full Charge Capacity (this is only mostly static) */
	rv |= battery_full_charge_capacity(
		(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC));

	/* Cycle Count */
	rv |= battery_cycle_count((int *)host_get_memmap(EC_MEMMAP_BATT_CCNT));

	/* Battery Manufacturer string */
	batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_MFGR);
	memset(batt_str, 0, EC_MEMMAP_TEXT_MAX);
	rv |= battery_manufacturer_name(batt_str, EC_MEMMAP_TEXT_MAX);

	/* Battery Model string */
	batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_MODEL);
	memset(batt_str, 0, EC_MEMMAP_TEXT_MAX);
	rv |= battery_device_name(batt_str, EC_MEMMAP_TEXT_MAX);

	/* Battery Type string */
	batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_TYPE);
	rv |= battery_device_chemistry(batt_str, EC_MEMMAP_TEXT_MAX);

	/* Zero the dynamic entries. They'll come next. */
	*(int *)host_get_memmap(EC_MEMMAP_BATT_VOLT) = 0;
	*(int *)host_get_memmap(EC_MEMMAP_BATT_RATE) = 0;
	*(int *)host_get_memmap(EC_MEMMAP_BATT_CAP) = 0;
	*(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC) = 0;
	if (extpower_is_present())
		batt_flags |= EC_BATT_FLAG_AC_PRESENT;
	*host_get_memmap(EC_MEMMAP_BATT_FLAG) = batt_flags;

	if (rv)
		charge_problem(PR_STATIC_UPDATE, rv);
	else
		/* No errors seen. Battery data is now present */
		*host_get_memmap(EC_MEMMAP_BATTERY_VERSION) = 1;

	return rv;
}

void update_dynamic_battery_info(void)
{
	/* The memmap address is constant. We should fix these calls somehow. */
	int *memmap_volt = (int *)host_get_memmap(EC_MEMMAP_BATT_VOLT);
	int *memmap_rate = (int *)host_get_memmap(EC_MEMMAP_BATT_RATE);
	int *memmap_cap = (int *)host_get_memmap(EC_MEMMAP_BATT_CAP);
	int *memmap_lfcc = (int *)host_get_memmap(EC_MEMMAP_BATT_LFCC);
	uint8_t *memmap_flags = host_get_memmap(EC_MEMMAP_BATT_FLAG);
	uint8_t tmp;
	int send_batt_status_event = 0;
	int send_batt_info_event = 0;
	static int batt_present;
	struct charge_state_data *curr;

	curr = charge_get_status();
	tmp = 0;
	if (curr->ac)
		tmp |= EC_BATT_FLAG_AC_PRESENT;

	if (curr->batt.is_present == BP_YES) {
		tmp |= EC_BATT_FLAG_BATT_PRESENT;
		batt_present = 1;
		/* Tell the AP to read battery info if it is newly present. */
		if (!(*memmap_flags & EC_BATT_FLAG_BATT_PRESENT))
			send_batt_info_event++;
	} else {
		/*
		 * Require two consecutive updates with BP_NOT_SURE
		 * before reporting it gone to the host.
		 */
		if (batt_present)
			tmp |= EC_BATT_FLAG_BATT_PRESENT;
		else if (*memmap_flags & EC_BATT_FLAG_BATT_PRESENT)
			send_batt_info_event++;
		batt_present = 0;
	}

	if (curr->batt.flags & BATT_FLAG_BAD_ANY)
		tmp |= EC_BATT_FLAG_INVALID_DATA;

	if (!(curr->batt.flags & BATT_FLAG_BAD_VOLTAGE))
		*memmap_volt = curr->batt.voltage;

	if (!(curr->batt.flags & BATT_FLAG_BAD_CURRENT))
		*memmap_rate = ABS(curr->batt.current);

	if (!(curr->batt.flags & BATT_FLAG_BAD_REMAINING_CAPACITY)) {
		/*
		 * If we're running off the battery, it must have some charge.
		 * Don't report zero charge, as that has special meaning
		 * to Chrome OS powerd.
		 */
		if (curr->batt.remaining_capacity == 0 &&
		    !curr->batt_is_charging)
			*memmap_cap = 1;
		else
			*memmap_cap = curr->batt.remaining_capacity;
	}

	if (!(curr->batt.flags & BATT_FLAG_BAD_FULL_CAPACITY) &&
	    (curr->batt.full_capacity <= (*memmap_lfcc - LFCC_EVENT_THRESH) ||
	     curr->batt.full_capacity >= (*memmap_lfcc + LFCC_EVENT_THRESH))) {
		*memmap_lfcc = curr->batt.full_capacity;
		/* Poke the AP if the full_capacity changes. */
		send_batt_info_event++;
	}

	if (curr->batt.is_present == BP_YES &&
	    battery_is_below_threshold(BATT_THRESHOLD_TYPE_SHUTDOWN, false))
		tmp |= EC_BATT_FLAG_LEVEL_CRITICAL;

	tmp |= curr->batt_is_charging ? EC_BATT_FLAG_CHARGING :
					EC_BATT_FLAG_DISCHARGING;

	/* Tell the AP to re-read battery status if charge state changes */
	if (*memmap_flags != tmp)
		send_batt_status_event++;

	/* Update flags before sending host events. */
	*memmap_flags = tmp;

	if (send_batt_info_event)
		host_set_single_event(EC_HOST_EVENT_BATTERY);
	if (send_batt_status_event)
		host_set_single_event(EC_HOST_EVENT_BATTERY_STATUS);
}