-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(drivers): add driver for MAX17048 fuel gauge
Add driver for MAX17048 fuel gauge for battery reporting.
- Loading branch information
Showing
7 changed files
with
311 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Copyright (c) 2022 The ZMK Contributors | ||
# SPDX-License-Identifier: MIT | ||
|
||
zephyr_include_directories(.) | ||
|
||
zephyr_library() | ||
|
||
zephyr_library_sources_ifdef(CONFIG_MAX17048 max17048.c) | ||
zephyr_library_sources_ifndef(CONFIG_MAX17048 ${ZEPHYR_BASE}/misc/empty_file.c) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Copyright (c) 2022 The ZMK Contributors | ||
# SPDX-License-Identifier: MIT | ||
|
||
DT_COMPAT_MAXIM_MAX17048 := maxim,max17048 | ||
|
||
menuconfig MAX17048 | ||
bool "MAX17048/9 I2C-based Fuel Gauge" | ||
default $(dt_compat_enabled,$(DT_COMPAT_MAXIM_MAX17048)) | ||
depends on I2C | ||
select ZMK_BATTERY | ||
help | ||
Enable driver for MAX17048/9 I2C-based Fuel Gauge. Supports measuring | ||
battery voltage and state-of-charge. | ||
|
||
if MAX17048 | ||
|
||
config SENSOR_MAX17048_INIT_PRIORITY | ||
int "Init priority" | ||
default 75 | ||
help | ||
Device driver initialization priority. | ||
|
||
endif #MAX17048 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
/* | ||
* Copyright (c) 2022 The ZMK Contributors | ||
* | ||
* SPDX-License-Identifier: MIT | ||
*/ | ||
|
||
#define DT_DRV_COMPAT maxim_max17048 | ||
|
||
#include <zephyr/device.h> | ||
#include <zephyr/kernel.h> | ||
#include <zephyr/sys/util.h> | ||
#include <zephyr/drivers/i2c.h> | ||
#include <zephyr/sys/byteorder.h> | ||
#include <zephyr/drivers/sensor.h> | ||
|
||
#include "max17048.h" | ||
|
||
#define LOG_LEVEL CONFIG_SENSOR_LOG_LEVEL | ||
#include <zephyr/logging/log.h> | ||
LOG_MODULE_REGISTER(sensor_max17048); | ||
|
||
static int read_register(const struct device *dev, uint8_t reg, uint16_t *value) { | ||
|
||
if (k_is_in_isr()) { | ||
return -EWOULDBLOCK; | ||
} | ||
|
||
struct max17048_config *config = (struct max17048_config *)dev->config; | ||
|
||
uint8_t data[2] = {0}; | ||
int ret = i2c_burst_read_dt(&config->i2c_bus, reg, &data[0], sizeof(data)); | ||
if (ret != 0) { | ||
LOG_DBG("i2c_write_read FAIL %d\n", ret); | ||
return ret; | ||
} | ||
|
||
// the register values are returned in big endian (MSB first) | ||
*value = sys_get_be16(data); | ||
return 0; | ||
} | ||
|
||
static int write_register(const struct device *dev, uint8_t reg, uint16_t value) { | ||
|
||
if (k_is_in_isr()) { | ||
return -EWOULDBLOCK; | ||
} | ||
|
||
struct max17048_config *config = (struct max17048_config *)dev->config; | ||
|
||
uint8_t data[2] = {0}; | ||
sys_put_be16(value, &data[0]); | ||
|
||
return i2c_burst_write_dt(&config->i2c_bus, reg, &data[0], sizeof(data)); | ||
} | ||
|
||
static int set_rcomp_value(const struct device *dev, uint8_t rcomp_value) { | ||
|
||
struct max17048_drv_data *const drv_data = (struct max17048_drv_data *const)dev->data; | ||
k_sem_take(&drv_data->lock, K_FOREVER); | ||
|
||
uint16_t tmp = 0; | ||
int err = read_register(dev, REG_CONFIG, &tmp); | ||
if (err != 0) { | ||
goto done; | ||
} | ||
|
||
tmp = ((uint16_t)rcomp_value << 8) | (tmp & 0xFF); | ||
err = write_register(dev, REG_CONFIG, tmp); | ||
if (err != 0) { | ||
goto done; | ||
} | ||
|
||
LOG_DBG("set RCOMP to %d", rcomp_value); | ||
|
||
done: | ||
k_sem_give(&drv_data->lock); | ||
return err; | ||
} | ||
|
||
static int set_sleep_enabled(const struct device *dev, bool sleep) { | ||
|
||
struct max17048_drv_data *const drv_data = (struct max17048_drv_data *const)dev->data; | ||
k_sem_take(&drv_data->lock, K_FOREVER); | ||
|
||
uint16_t tmp = 0; | ||
int err = read_register(dev, REG_CONFIG, &tmp); | ||
if (err != 0) { | ||
goto done; | ||
} | ||
|
||
if (sleep) { | ||
tmp |= 0x80; | ||
} else { | ||
tmp &= ~0x0080; | ||
} | ||
|
||
err = write_register(dev, REG_CONFIG, tmp); | ||
if (err != 0) { | ||
goto done; | ||
} | ||
|
||
LOG_DBG("sleep mode %s", sleep ? "enabled" : "disabled"); | ||
|
||
done: | ||
k_sem_give(&drv_data->lock); | ||
return err; | ||
} | ||
|
||
static int max17048_sample_fetch(const struct device *dev, enum sensor_channel chan) { | ||
|
||
struct max17048_drv_data *const drv_data = dev->data; | ||
k_sem_take(&drv_data->lock, K_FOREVER); | ||
|
||
int err = 0; | ||
|
||
if (chan == SENSOR_CHAN_GAUGE_STATE_OF_CHARGE || chan == SENSOR_CHAN_ALL) { | ||
err = read_register(dev, REG_STATE_OF_CHARGE, &drv_data->raw_state_of_charge); | ||
if (err != 0) { | ||
LOG_WRN("failed to read state-of-charge: %d", err); | ||
goto done; | ||
} | ||
LOG_DBG("read soc: %d", drv_data->raw_state_of_charge); | ||
|
||
} else if (chan == SENSOR_CHAN_GAUGE_VOLTAGE || chan == SENSOR_CHAN_ALL) { | ||
err = read_register(dev, REG_VCELL, &drv_data->raw_vcell); | ||
if (err != 0) { | ||
LOG_WRN("failed to read vcell: %d", err); | ||
goto done; | ||
} | ||
LOG_DBG("read vcell: %d", drv_data->raw_vcell); | ||
|
||
} else { | ||
LOG_DBG("unsupported channel %d", chan); | ||
err = -ENOTSUP; | ||
} | ||
|
||
done: | ||
k_sem_give(&drv_data->lock); | ||
return err; | ||
} | ||
|
||
static int max17048_channel_get(const struct device *dev, enum sensor_channel chan, | ||
struct sensor_value *val) { | ||
int err = 0; | ||
|
||
struct max17048_drv_data *const drv_data = dev->data; | ||
k_sem_take(&drv_data->lock, K_FOREVER); | ||
|
||
struct max17048_drv_data *const data = dev->data; | ||
unsigned int tmp = 0; | ||
|
||
switch (chan) { | ||
case SENSOR_CHAN_GAUGE_VOLTAGE: | ||
// 1250 / 16 = 78.125 | ||
tmp = data->raw_vcell * 1250 / 16; | ||
val->val1 = tmp / 1000000; | ||
val->val2 = tmp % 1000000; | ||
break; | ||
|
||
case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE: | ||
val->val1 = (data->raw_state_of_charge >> 8); | ||
val->val2 = (data->raw_state_of_charge & 0xFF) * 1000000 / 256; | ||
break; | ||
|
||
default: | ||
err = -ENOTSUP; | ||
break; | ||
} | ||
|
||
k_sem_give(&drv_data->lock); | ||
return err; | ||
} | ||
|
||
static int max17048_init(const struct device *dev) { | ||
struct max17048_drv_data *drv_data = dev->data; | ||
const struct max17048_config *config = dev->config; | ||
|
||
if (!device_is_ready(config->i2c_bus.bus)) { | ||
LOG_WRN("i2c bus not ready!"); | ||
return -EINVAL; | ||
} | ||
|
||
uint16_t ic_version = 0; | ||
int err = read_register(dev, REG_VERSION, &ic_version); | ||
if (err != 0) { | ||
LOG_WRN("could not get IC version!"); | ||
return err; | ||
} | ||
|
||
// the functions below need the semaphore, so initialise it here | ||
k_sem_init(&drv_data->lock, 1, 1); | ||
|
||
// bring the device out of sleep | ||
set_sleep_enabled(dev, false); | ||
|
||
// set the default rcomp value -- 0x97, as stated in the datasheet | ||
set_rcomp_value(dev, 0x97); | ||
|
||
LOG_INF("device initialised at 0x%x (version %d)", config->i2c_bus.addr, ic_version); | ||
|
||
return 0; | ||
} | ||
|
||
static const struct sensor_driver_api max17048_api_table = {.sample_fetch = max17048_sample_fetch, | ||
.channel_get = max17048_channel_get}; | ||
|
||
#define MAX17048_INIT(inst) \ | ||
static struct max17048_config max17048_##inst##_config = {.i2c_bus = \ | ||
I2C_DT_SPEC_INST_GET(inst)}; \ | ||
\ | ||
static struct max17048_drv_data max17048_##inst##_drvdata = { \ | ||
.raw_state_of_charge = 0, \ | ||
.raw_charge_rate = 0, \ | ||
.raw_vcell = 0, \ | ||
}; \ | ||
\ | ||
/* This has to init after SPI master */ \ | ||
DEVICE_DT_INST_DEFINE(inst, max17048_init, NULL, &max17048_##inst##_drvdata, \ | ||
&max17048_##inst##_config, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ | ||
&max17048_api_table); | ||
|
||
DT_INST_FOREACH_STATUS_OKAY(MAX17048_INIT) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/* | ||
* Copyright (c) 2022 The ZMK Contributors | ||
* | ||
* SPDX-License-Identifier: MIT | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <zephyr/device.h> | ||
#include <zephyr/sys/util.h> | ||
|
||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif | ||
|
||
#define REG_VCELL 0x02 | ||
#define REG_STATE_OF_CHARGE 0x04 | ||
#define REG_MODE 0x06 | ||
#define REG_VERSION 0x08 | ||
#define REG_HIBERNATE 0x0A | ||
#define REG_CONFIG 0x0C | ||
#define REG_VALERT 0x14 | ||
#define REG_CHARGE_RATE 0x16 | ||
#define REG_VRESET 0x18 | ||
#define REG_STATUS 0x1A | ||
|
||
struct max17048_config { | ||
struct i2c_dt_spec i2c_bus; | ||
}; | ||
|
||
struct max17048_drv_data { | ||
struct k_sem lock; | ||
|
||
uint16_t raw_state_of_charge; | ||
uint16_t raw_charge_rate; | ||
uint16_t raw_vcell; | ||
}; | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# | ||
# Copyright (c) 2022 The ZMK Contributors | ||
# | ||
# SPDX-License-Identifier: MIT | ||
# | ||
|
||
description: > | ||
This is a representation of the Maxim max17048 I2C Fuel Gauge. | ||
compatible: "maxim,max17048" | ||
|
||
include: [i2c-device.yaml] |