summaryrefslogtreecommitdiff
path: root/baseboard/kukui/charger_mt6370.c
blob: 327b567db6c9471a3ec55c95a7d13e53f0da5bfa (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
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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
/* Copyright 2019 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.
 */

#include "charge_manager.h"
#include "charge_state_v2.h"
#include "charger_mt6370.h"
#include "console.h"
#include "driver/charger/rt946x.h"
#include "driver/tcpm/mt6370.h"
#include "hooks.h"
#include "power.h"
#include "timer.h"
#include "usb_common.h"
#include "usb_pd.h"
#include "util.h"

#define BAT_LEVEL_PD_LIMIT 85
#define SYSTEM_PLT_MW 3500
/*
 * b/143318064: Prefer a voltage above 5V to force it picks a voltage
 * above 5V at first. If PREFER_MV is 5V, when desired power is around
 * 15W ~ 11W, it would pick 5V/3A initially, and mt6370 can only sink
 * around 10W, and cause a low charging efficiency.
 */
#define PREVENT_CURRENT_DROP_MV 6000
#define DEFAULT_PREFER_MV 5000
/*
 * We empirically chose 300mA as the limit for when buck inefficiency is
 * noticeable.
 */
#define STABLE_CURRENT_DELTA 300

struct pd_pref_config_t pd_pref_config = {
	.mv = PREVENT_CURRENT_DROP_MV,
	.cv = 70,
	.plt_mw = SYSTEM_PLT_MW,
	.type = PD_PREFER_BUCK,
};

static void update_plt_suspend(void)
{
	pd_pref_config.plt_mw = 0;
}
DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, update_plt_suspend, HOOK_PRIO_DEFAULT);

static void update_plt_resume(void)
{
	pd_pref_config.plt_mw = SYSTEM_PLT_MW;
}
DECLARE_HOOK(HOOK_CHIPSET_RESUME, update_plt_resume, HOOK_PRIO_DEFAULT);

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

/* wait time to evaluate charger thermal status */
static timestamp_t thermal_wait_until;
/* input current bound when charger throttled */
static int throttled_ma = PD_MAX_CURRENT_MA;
/* charge_ma in last board_set_charge_limit call */
static int prev_charge_limit;
/* charge_mv in last board_set_charge_limit call */
static int prev_charge_mv;

#ifndef CONFIG_BATTERY_SMART
int board_cut_off_battery(void)
{
	/* The cut-off procedure is recommended by Richtek. b/116682788 */
	rt946x_por_reset();
	mt6370_vconn_discharge(0);
	rt946x_cutoff_battery();

	return EC_SUCCESS;
}
#endif

static void board_set_charge_limit_throttle(int charge_ma, int charge_mv)
{
	charge_set_input_current_limit(
		MIN(throttled_ma, MAX(charge_ma, CONFIG_CHARGER_INPUT_CURRENT)),
		charge_mv);
}

static void battery_thermal_control(struct charge_state_data *curr)
{
	int input_current, jc_temp;
	static int skip_reset;
	/*
	 * mt6370's input current setting is 50mA step, use 50 as well for
	 * easy value mapping.
	 */
	const int k_p = 50;

	if (charge_manager_get_charger_voltage() == 5000 ||
	    curr->state != ST_CHARGE) {
		/* We already set the charge limit, do not reset it again. */
		if (skip_reset)
			return;
		skip_reset = 1;
		thermal_wait_until.val = 0;
		throttled_ma = PD_MAX_CURRENT_MA;
		board_set_charge_limit_throttle(prev_charge_limit,
						prev_charge_mv);
		return;
	}

	skip_reset = 0;

	if (thermal_wait_until.val == 0)
		goto thermal_exit;

	if (get_time().val < thermal_wait_until.val)
		return;

	/* If we fail to read adc, skip for this cycle. */
	if (rt946x_get_adc(MT6370_ADC_TEMP_JC, &jc_temp))
		return;

	/* If we fail to read input curr limit, skip for this cycle. */
	if (charger_get_input_current_limit(CHARGER_SOLO, &input_current))
		return;

	/*
	 * If input current limit is maximum, and we are under thermal budget,
	 * just skip.
	 */
	if (input_current == PD_MAX_CURRENT_MA &&
	    jc_temp < thermal_bound.target + thermal_bound.err)
		return;

	/* If the temp is within +- err, thermal is under control */
	if (jc_temp < thermal_bound.target + thermal_bound.err &&
	    jc_temp > thermal_bound.target - thermal_bound.err)
		return;

	/*
	 * PID algorithm (https://en.wikipedia.org/wiki/PID_controller),
	 * and operates on only P value.
	 */
	throttled_ma = MIN(
		PD_MAX_CURRENT_MA,
		/*
		 * Should not pass the previously set input current by
		 * charger manager.  This value might be related the charger's
		 * capability.
		 */
		MIN(prev_charge_limit,
		    input_current + k_p * (thermal_bound.target - jc_temp)));

	/* If the input current doesn't change, just skip. */
	if (throttled_ma != input_current)
		board_set_charge_limit_throttle(throttled_ma, prev_charge_mv);

thermal_exit:
	thermal_wait_until.val = get_time().val + (3 * SECOND);
}

int command_jc(int argc, char **argv)
{
	static int prev_jc_temp;
	int jc_temp;

	if (rt946x_get_adc(MT6370_ADC_TEMP_JC, &jc_temp))
		jc_temp = prev_jc_temp;

	ccprintf("JC Temp: %d\n", jc_temp);
	prev_jc_temp = jc_temp;
	return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(jc, command_jc, "", "mt6370 junction temp");

/*
 * b/143318064: A workwround for mt6370 bad buck efficiency.
 * If the delta of VBUS and VBAT(on krane, desired voltage 4.4V) is too small
 * (i.e. < 500mV), the buck throughput will be bounded, and causing that we
 * can't drain 5V/3A when battery SoC above around 40%.
 * This function watches battery current. If we see battery current drops after
 * switching from high voltage to 5V (This will happen if we enable
 * CONFIG_USB_PD_PREFER_MV and set prefer votage to 5V), the charger will lost
 * power due to the inefficiency (e.g. switch from 9V/1.67A = 15W to 5V/3A,
 * but mt6370 would only sink less than 5V/2.4A = 12W), and we will request a
 * higher voltage PDO to prevent a slow charging time.
 */
static void battery_desired_curr_dynamic(struct charge_state_data *curr)
{
	static int prev_stable_current = CHARGE_CURRENT_UNINITIALIZED;
	static int prev_supply_voltage;
	int supply_voltage;
	int stable_current;
	int delta_current;

	if (curr->state != ST_CHARGE) {
		prev_supply_voltage = 0;
		prev_stable_current = CHARGE_CURRENT_UNINITIALIZED;
		/*
		 * Always force higher voltage on first PD negotiation.
		 * When desired power is around 15W ~ 11W, PD would pick
		 * 5V/3A initially, but mt6370 can't drain that much, and
		 * causes a low charging efficiency.
		 */
		pd_pref_config.mv = PREVENT_CURRENT_DROP_MV;
		return;
	}

	supply_voltage = charge_manager_get_charger_voltage();
	stable_current = charge_get_stable_current();

	if (!charge_is_current_stable())
		return;

	if (!prev_supply_voltage)
		goto update_charge;

	delta_current = prev_stable_current - stable_current;
	if (curr->batt.state_of_charge >= pd_pref_config.cv &&
	    supply_voltage == DEFAULT_PREFER_MV &&
	    prev_supply_voltage > supply_voltage &&
	    delta_current > STABLE_CURRENT_DELTA) {
		/* Raise perfer voltage above 5000mV */
		pd_pref_config.mv = PREVENT_CURRENT_DROP_MV;
		/*
		 * Delay stable current evaluation for 5 mins if we see a
		 * current drop.  It's a reasonable waiting time since that
		 * the battery desired current can't catch the gap that fast
		 * in the period.
		 */
		charge_reset_stable_current_us(5 * MINUTE);
		/* Rewrite the stable current to re-evalute desired watt */
		charge_set_stable_current(prev_stable_current);

		/*
		 * do not alter current by thermal if we just raising PD
		 * voltage
		 */
		thermal_wait_until.val = get_time().val + (10 * SECOND);
	} else {
		pd_pref_config.mv = DEFAULT_PREFER_MV;
		/*
		 * If the power supply is plugged while battery full,
		 * the stable_current will always be 0 such that we are unable
		 * to switch to 5V. We force evaluating PDO to switch to 5V.
		 */
		if (prev_supply_voltage == supply_voltage && !stable_current &&
		    !prev_stable_current &&
		    supply_voltage != DEFAULT_PREFER_MV &&
		    charge_manager_get_supplier() == CHARGE_SUPPLIER_PD)
			pd_set_new_power_request(
				charge_manager_get_active_charge_port());
	}

update_charge:
	prev_supply_voltage = supply_voltage;
	prev_stable_current = stable_current;
}

#ifdef CONFIG_BATTERY_SMART
static void charge_enable_eoc_and_te(void)
{
	rt946x_enable_charge_eoc(1);
	rt946x_enable_charge_termination(1);
}
DECLARE_DEFERRED(charge_enable_eoc_and_te);
#endif

void mt6370_charger_profile_override(struct charge_state_data *curr)
{
	static int previous_chg_limit_mv;
	int chg_limit_mv = pd_get_max_voltage();

	battery_desired_curr_dynamic(curr);

	battery_thermal_control(curr);

#ifdef CONFIG_BATTERY_SMART
	/*
	 * SMP battery uses HW pre-charge circuit and pre-charge current is
	 * limited to ~50mA. Once the charge current is lower than IEOC level
	 * within CHG_TEDG_EOC, and TE is enabled, the charging power path will
	 * be turned off. Disable EOC and TE when battery stays over discharge
	 * state, otherwise enable EOC and TE.
	 */
	if (!(curr->batt.flags & BATT_FLAG_BAD_VOLTAGE)) {
		const struct battery_info *batt_info = battery_get_info();
		static int normal_charge_lock, over_discharge_lock;

		if (curr->batt.voltage < batt_info->voltage_min) {
			normal_charge_lock = 0;

			if (!over_discharge_lock && curr->state == ST_CHARGE) {
				over_discharge_lock = 1;
				rt946x_enable_charge_eoc(0);
				rt946x_enable_charge_termination(0);
			}
		} else {
			over_discharge_lock = 0;

			if (!normal_charge_lock) {
				normal_charge_lock = 1;
				/*
				 * b/148045048: When the battery is activated
				 * in shutdown mode, the adapter cannot boot
				 * DUT automatically. It's a workaround to
				 * delay 4.5 second to enable charger EOC
				 * and TE function.
				 */
				hook_call_deferred(
						&charge_enable_eoc_and_te_data,
						(4.5 * SECOND));
			}
		}
	}
#endif

	/* Limit input (=VBUS) to 5V when soc > 85% and charge current < 1A. */
	if (!(curr->batt.flags & BATT_FLAG_BAD_CURRENT) &&
	    charge_get_percent() > BAT_LEVEL_PD_LIMIT &&
	    curr->batt.current < 1000 && power_get_state() != POWER_S0)
		chg_limit_mv = 5500;
	else
		chg_limit_mv = PD_MAX_VOLTAGE_MV;

	if (chg_limit_mv != previous_chg_limit_mv)
		CPRINTS("VBUS limited to %dmV", chg_limit_mv);
	previous_chg_limit_mv = chg_limit_mv;

	/* Pull down VBUS */
	if (pd_get_max_voltage() != chg_limit_mv)
		pd_set_external_voltage_limit(0, chg_limit_mv);

	/*
	 * When the charger says it's done charging, even if fuel gauge says
	 * SOC < BATTERY_LEVEL_NEAR_FULL, we'll overwrite SOC with
	 * BATTERY_LEVEL_NEAR_FULL. So we can ensure both Chrome OS UI
	 * and battery LED indicate full charge.
	 *
	 * Enable this hack on on-board gauge only (b/142097561)
	 */
	if (IS_ENABLED(CONFIG_BATTERY_MAX17055) && rt946x_is_charge_done()) {
		curr->batt.state_of_charge = MAX(BATTERY_LEVEL_NEAR_FULL,
						 curr->batt.state_of_charge);
	}

}

#ifndef CONFIG_BATTERY_SMART
static void board_charge_termination(void)
{
	static uint8_t te;
	/* Enable charge termination when we are sure battery is present. */
	if (!te && battery_is_present() == BP_YES) {
		if (!rt946x_enable_charge_termination(1))
			te = 1;
	}
}
DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE,
	     board_charge_termination,
	     HOOK_PRIO_DEFAULT);
#endif

void board_set_charge_limit(int port, int supplier, int charge_ma,
			    int max_ma, int charge_mv)
{
	prev_charge_limit = charge_ma;
	prev_charge_mv = charge_mv;
	board_set_charge_limit_throttle(charge_ma, charge_mv);
}