| /* |
| * Copyright 2023 Cirrus Logic, Inc. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT sbs_sbs_charger |
| |
| #include <zephyr/drivers/charger.h> |
| #include <zephyr/drivers/i2c.h> |
| #include <zephyr/logging/log.h> |
| #include <zephyr/sys/byteorder.h> |
| |
| #include "sbs_charger.h" |
| |
| struct sbs_charger_config { |
| struct i2c_dt_spec i2c; |
| }; |
| |
| LOG_MODULE_REGISTER(sbs_charger); |
| |
| static int sbs_cmd_reg_read(const struct device *dev, uint8_t reg_addr, uint16_t *val) |
| { |
| const struct sbs_charger_config *cfg; |
| uint8_t i2c_data[2]; |
| int status; |
| |
| cfg = dev->config; |
| status = i2c_burst_read_dt(&cfg->i2c, reg_addr, i2c_data, sizeof(i2c_data)); |
| if (status < 0) { |
| LOG_ERR("Unable to read register"); |
| return status; |
| } |
| |
| *val = sys_get_le16(i2c_data); |
| |
| return 0; |
| } |
| |
| static int sbs_cmd_reg_write(const struct device *dev, uint8_t reg_addr, uint16_t val) |
| { |
| const struct sbs_charger_config *config = dev->config; |
| uint8_t buf[2]; |
| |
| sys_put_le16(val, buf); |
| |
| return i2c_burst_write_dt(&config->i2c, reg_addr, buf, sizeof(buf)); |
| } |
| |
| static int sbs_cmd_reg_update(const struct device *dev, uint8_t reg_addr, uint16_t mask, |
| uint16_t val) |
| { |
| uint16_t old_val, new_val; |
| int ret; |
| |
| ret = sbs_cmd_reg_read(dev, SBS_CHARGER_REG_STATUS, &old_val); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| new_val = (old_val & ~mask) | (val & mask); |
| if (new_val == old_val) { |
| return 0; |
| } |
| |
| return sbs_cmd_reg_write(dev, reg_addr, new_val); |
| } |
| |
| static int sbs_charger_charge_enable(const struct device *dev, const bool enable) |
| { |
| uint16_t reg_val; |
| |
| if (!enable) { |
| reg_val = SBS_CHARGER_MODE_INHIBIT_CHARGE; |
| } else { |
| reg_val = 0; |
| } |
| |
| return sbs_cmd_reg_update(dev, SBS_CHARGER_REG_CHARGER_MODE, |
| SBS_CHARGER_MODE_INHIBIT_CHARGE, reg_val); |
| } |
| |
| static int sbs_charger_get_prop(const struct device *dev, const charger_prop_t prop, |
| union charger_propval *val) |
| { |
| uint16_t reg_val; |
| int ret; |
| |
| switch (prop) { |
| case CHARGER_PROP_ONLINE: |
| ret = sbs_cmd_reg_read(dev, SBS_CHARGER_REG_STATUS, ®_val); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| if (reg_val & SBS_CHARGER_STATUS_AC_PRESENT) { |
| val->online = CHARGER_ONLINE_FIXED; |
| } else { |
| val->online = CHARGER_ONLINE_OFFLINE; |
| } |
| |
| return 0; |
| case CHARGER_PROP_PRESENT: |
| ret = sbs_cmd_reg_read(dev, SBS_CHARGER_REG_STATUS, ®_val); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| if (reg_val & SBS_CHARGER_STATUS_BATTERY_PRESENT) { |
| val->present = true; |
| } else { |
| val->present = false; |
| } |
| |
| return 0; |
| case CHARGER_PROP_STATUS: |
| ret = sbs_cmd_reg_read(dev, SBS_CHARGER_REG_STATUS, ®_val); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| if (!(reg_val & SBS_CHARGER_STATUS_BATTERY_PRESENT)) { |
| val->status = CHARGER_STATUS_NOT_CHARGING; |
| } else if (reg_val & SBS_CHARGER_STATUS_AC_PRESENT && |
| !(reg_val & SBS_CHARGER_STATUS_CHARGE_INHIBITED)) { |
| val->status = CHARGER_STATUS_CHARGING; |
| } else { |
| val->status = CHARGER_STATUS_DISCHARGING; |
| } |
| |
| return 0; |
| default: |
| return -ENOTSUP; |
| } |
| } |
| |
| static int sbs_charger_set_prop(const struct device *dev, const charger_prop_t prop, |
| const union charger_propval *val) |
| { |
| return -ENOTSUP; |
| } |
| |
| /** |
| * @brief initialize the charger |
| * |
| * @return 0 for success |
| */ |
| static int sbs_charger_init(const struct device *dev) |
| { |
| const struct sbs_charger_config *cfg = dev->config; |
| |
| if (!i2c_is_ready_dt(&cfg->i2c)) { |
| LOG_ERR("Bus device is not ready"); |
| return -ENODEV; |
| } |
| |
| return 0; |
| } |
| |
| static const struct charger_driver_api sbs_charger_driver_api = { |
| .get_property = &sbs_charger_get_prop, |
| .set_property = &sbs_charger_set_prop, |
| .charge_enable = &sbs_charger_charge_enable, |
| }; |
| |
| #define SBS_CHARGER_INIT(inst) \ |
| \ |
| static const struct sbs_charger_config sbs_charger_config_##inst = { \ |
| .i2c = I2C_DT_SPEC_INST_GET(inst), \ |
| }; \ |
| \ |
| DEVICE_DT_INST_DEFINE(inst, &sbs_charger_init, NULL, NULL, &sbs_charger_config_##inst, \ |
| POST_KERNEL, CONFIG_CHARGER_INIT_PRIORITY, &sbs_charger_driver_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(SBS_CHARGER_INIT) |