coreboot-libre-fam15h-rdimm/3rdparty/chromeec/driver/battery/mm8013.c

246 lines
5.1 KiB
C

/* 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.
*
* Battery driver for MM8013.
*/
#include "battery.h"
#include "battery_smart.h"
#include "console.h"
#include "i2c.h"
#include "mm8013.h"
#include "timer.h"
#include "util.h"
#define BATTERY_PACK_INFO_LENGTH 8
/* MM8013 requires a 100us wait time after a read operation. */
#define I2C_WAIT_TIME 100
static int mm8013_read16(int offset, int *data)
{
int rv;
*data = 0;
rv = i2c_read16(I2C_PORT_BATTERY, MM8013_ADDR_FLAGS, offset, data);
usleep(I2C_WAIT_TIME);
if (rv)
return rv;
return EC_SUCCESS;
}
static int mm8013_read_block(int offset, uint8_t *data, int len)
{
int rv;
rv = i2c_read_block(I2C_PORT_BATTERY, MM8013_ADDR_FLAGS,
offset, data, len);
usleep(I2C_WAIT_TIME);
if (rv)
return rv;
return EC_SUCCESS;
}
static int battery_flag(int *flag)
{
return mm8013_read16(REG_FLAGS, flag);
}
static int battery_current(int *current)
{
int16_t tmp;
int rv;
rv = mm8013_read_block(REG_AVERAGE_CURRENT,
(uint8_t *)&tmp, sizeof(int16_t));
if (rv)
return rv;
*current = tmp;
return EC_SUCCESS;
}
int battery_device_name(char *device_name, int buf_size)
{
int rv;
char out_buf[BATTERY_PACK_INFO_LENGTH + 1];
rv = mm8013_read_block(REG_PRODUCT_INFORMATION,
(uint8_t *)out_buf, BATTERY_PACK_INFO_LENGTH);
if (rv)
return rv;
out_buf[BATTERY_PACK_INFO_LENGTH] = '\0';
strzcpy(device_name, out_buf, buf_size);
return EC_SUCCESS;
}
int battery_state_of_charge_abs(int *percent)
{
return mm8013_read16(REG_STATE_OF_CHARGE, percent);
}
int battery_remaining_capacity(int *capacity)
{
return mm8013_read16(REG_REMAINING_CAPACITY, capacity);
}
int battery_full_charge_capacity(int *capacity)
{
return mm8013_read16(REG_FULL_CHARGE_CAPACITY, capacity);
}
int battery_time_to_empty(int *minutes)
{
return mm8013_read16(REG_AVERAGE_TIME_TO_EMPTY, minutes);
}
int battery_time_to_full(int *minutes)
{
return mm8013_read16(REG_AVERAGE_TIME_TO_FULL, minutes);
}
int battery_cycle_count(int *count)
{
return mm8013_read16(REG_CYCLE_COUNT, count);
}
int battery_design_capacity(int *capacity)
{
return mm8013_read16(REG_DESIGN_CAPACITY, capacity);
}
int battery_time_at_rate(int rate, int *minutes)
{
return EC_ERROR_UNIMPLEMENTED;
}
int battery_device_chemistry(char *dest, int size)
{
strzcpy(dest, "<unkn>", size);
return EC_SUCCESS;
}
int battery_serial_number(int *serial)
{
*serial = 0xFFFFFFFF;
return EC_SUCCESS;
}
int battery_design_voltage(int *voltage)
{
*voltage = battery_get_info()->voltage_normal;
return EC_SUCCESS;
}
int battery_get_mode(int *mode)
{
return EC_ERROR_UNIMPLEMENTED;
}
int battery_status(int *status)
{
int rv;
int flag = 0;
*status = 0;
rv = battery_flag(&flag);
if (rv)
return rv;
if (flag & (MM8013_FLAG_OTC | MM8013_FLAG_OTD))
*status |= STATUS_OVERTEMP_ALARM;
if (flag & MM8013_FLAG_FC)
*status |= STATUS_FULLY_CHARGED;
if (flag & MM8013_FLAG_DSG)
*status |= STATUS_DISCHARGING;
if (flag & MM8013_FLAG_BATHI)
*status |= STATUS_OVERCHARGED_ALARM;
return EC_SUCCESS;
}
enum battery_present battery_is_present(void)
{
int temp;
if (mm8013_read16(REG_TEMPERATURE, &temp))
return BP_NO;
return BP_YES;
}
void battery_get_params(struct batt_params *batt)
{
struct batt_params batt_new = {0};
int flag = 0;
/*
* Assuming the battery is responsive as long as
* mm8013 finds battery is present.
*/
batt_new.is_present = battery_is_present();
if (batt_new.is_present == BP_YES)
batt_new.flags |= BATT_FLAG_RESPONSIVE;
else if (batt_new.is_present == BP_NO)
/* Battery is not present, gauge won't report useful info. */
goto batt_out;
if (mm8013_read16(REG_TEMPERATURE, &batt_new.temperature))
batt_new.flags |= BATT_FLAG_BAD_TEMPERATURE;
if (mm8013_read16(REG_STATE_OF_CHARGE, &batt_new.state_of_charge))
batt_new.flags |= BATT_FLAG_BAD_STATE_OF_CHARGE;
if (mm8013_read16(REG_VOLTAGE, &batt_new.voltage))
batt_new.flags |= BATT_FLAG_BAD_VOLTAGE;
if (battery_current(&batt_new.current))
batt_new.flags |= BATT_FLAG_BAD_CURRENT;
batt_new.desired_voltage = battery_get_info()->voltage_max;
batt_new.desired_current = BATTERY_DESIRED_CHARGING_CURRENT;
if (battery_remaining_capacity(&batt_new.remaining_capacity))
batt_new.flags |= BATT_FLAG_BAD_REMAINING_CAPACITY;
if (battery_full_charge_capacity(&batt_new.full_capacity))
batt_new.flags |= BATT_FLAG_BAD_FULL_CAPACITY;
if (battery_flag(&flag) && (flag & MM8013_FLAG_CHG))
batt_new.flags |= BATT_FLAG_WANT_CHARGE;
if (battery_status(&batt_new.status))
batt_new.flags |= BATT_FLAG_BAD_STATUS;
batt_out:
/* Update visible battery parameters */
memcpy(batt, &batt_new, sizeof(*batt));
}
#ifdef CONFIG_CMD_PWR_AVG
int battery_get_avg_current(void)
{
/* TODO(crbug.com/752320) implement this */
return -EC_ERROR_UNIMPLEMENTED;
}
int battery_get_avg_voltage(void)
{
/* TODO(crbug.com/752320) implement this */
return -EC_ERROR_UNIMPLEMENTED;
}
#endif /* CONFIG_CMD_PWR_AVG */
/* Wait until battery is totally stable. */
int battery_wait_for_stable(void)
{
/* TODO(phoenixshen): Implement this function. */
return EC_SUCCESS;
}