mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-09-04 10:33:13 +00:00
power: supply: max77705: Add charger driver for Maxim 77705
Add driver for Maxim 77705 switch-mode charger. It providing power supply class information to userspace. The driver is configured through DTS (battery and system related settings). Signed-off-by: Dzmitry Sankouski <dsankouski@gmail.com> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Acked-by: Sebastian Reichel <sebastian.reichel@collabora.com> Link: https://lore.kernel.org/r/20250123-starqltechn_integration_upstream-v17-3-8b06685b6612@gmail.com Signed-off-by: Lee Jones <lee@kernel.org>
This commit is contained in:
parent
2ae4ffff28
commit
a6a494c8e3
@ -583,6 +583,12 @@ config CHARGER_MAX77693
|
||||
help
|
||||
Say Y to enable support for the Maxim MAX77693 battery charger.
|
||||
|
||||
config CHARGER_MAX77705
|
||||
tristate "Maxim MAX77705 battery charger driver"
|
||||
depends on MFD_MAX77705
|
||||
help
|
||||
Say Y to enable support for the Maxim MAX77705 battery charger.
|
||||
|
||||
config CHARGER_MAX77976
|
||||
tristate "Maxim MAX77976 battery charger driver"
|
||||
depends on I2C
|
||||
|
@ -80,6 +80,7 @@ obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o
|
||||
obj-$(CONFIG_CHARGER_DETECTOR_MAX14656) += max14656_charger_detector.o
|
||||
obj-$(CONFIG_CHARGER_MAX77650) += max77650-charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX77705) += max77705_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX77976) += max77976_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
|
||||
|
581
drivers/power/supply/max77705_charger.c
Normal file
581
drivers/power/supply/max77705_charger.c
Normal file
@ -0,0 +1,581 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Based on max77650-charger.c
|
||||
*
|
||||
* Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.org>
|
||||
*
|
||||
* Battery charger driver for MAXIM 77705 charger/power-supply.
|
||||
*/
|
||||
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/max77693-common.h>
|
||||
#include <linux/mfd/max77705-private.h>
|
||||
#include <linux/power/max77705_charger.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
static const char *max77705_charger_model = "max77705";
|
||||
static const char *max77705_charger_manufacturer = "Maxim Integrated";
|
||||
|
||||
static const struct regmap_config max77705_chg_regmap_config = {
|
||||
.reg_base = MAX77705_CHG_REG_BASE,
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = MAX77705_CHG_REG_SAFEOUT_CTRL,
|
||||
};
|
||||
|
||||
static enum power_supply_property max77705_charger_props[] = {
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_CHARGE_TYPE,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
|
||||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||||
};
|
||||
|
||||
static int max77705_chgin_irq(void *irq_drv_data)
|
||||
{
|
||||
struct max77705_charger_data *charger = irq_drv_data;
|
||||
|
||||
queue_work(charger->wqueue, &charger->chgin_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_irq max77705_charger_irqs[] = {
|
||||
{ .mask = MAX77705_BYP_IM, },
|
||||
{ .mask = MAX77705_INP_LIMIT_IM, },
|
||||
{ .mask = MAX77705_BATP_IM, },
|
||||
{ .mask = MAX77705_BAT_IM, },
|
||||
{ .mask = MAX77705_CHG_IM, },
|
||||
{ .mask = MAX77705_WCIN_IM, },
|
||||
{ .mask = MAX77705_CHGIN_IM, },
|
||||
{ .mask = MAX77705_AICL_IM, },
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip max77705_charger_irq_chip = {
|
||||
.name = "max77705-charger",
|
||||
.status_base = MAX77705_CHG_REG_INT,
|
||||
.mask_base = MAX77705_CHG_REG_INT_MASK,
|
||||
.handle_post_irq = max77705_chgin_irq,
|
||||
.num_regs = 1,
|
||||
.irqs = max77705_charger_irqs,
|
||||
.num_irqs = ARRAY_SIZE(max77705_charger_irqs),
|
||||
};
|
||||
|
||||
static int max77705_charger_enable(struct max77705_charger_data *chg)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = regmap_update_bits(chg->regmap, MAX77705_CHG_REG_CNFG_09,
|
||||
MAX77705_CHG_EN_MASK, MAX77705_CHG_EN_MASK);
|
||||
if (rv)
|
||||
dev_err(chg->dev, "unable to enable the charger: %d\n", rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void max77705_charger_disable(void *data)
|
||||
{
|
||||
struct max77705_charger_data *chg = data;
|
||||
int rv;
|
||||
|
||||
rv = regmap_update_bits(chg->regmap,
|
||||
MAX77705_CHG_REG_CNFG_09,
|
||||
MAX77705_CHG_EN_MASK,
|
||||
MAX77705_CHG_DISABLE);
|
||||
if (rv)
|
||||
dev_err(chg->dev, "unable to disable the charger: %d\n", rv);
|
||||
}
|
||||
|
||||
static int max77705_get_online(struct regmap *regmap, int *val)
|
||||
{
|
||||
unsigned int data;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(regmap, MAX77705_CHG_REG_INT_OK, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = !!(data & MAX77705_CHGIN_OK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77705_check_battery(struct max77705_charger_data *charger, int *val)
|
||||
{
|
||||
unsigned int reg_data;
|
||||
unsigned int reg_data2;
|
||||
struct regmap *regmap = charger->regmap;
|
||||
|
||||
regmap_read(regmap, MAX77705_CHG_REG_INT_OK, ®_data);
|
||||
|
||||
dev_dbg(charger->dev, "CHG_INT_OK(0x%x)\n", reg_data);
|
||||
|
||||
regmap_read(regmap, MAX77705_CHG_REG_DETAILS_00, ®_data2);
|
||||
|
||||
dev_dbg(charger->dev, "CHG_DETAILS00(0x%x)\n", reg_data2);
|
||||
|
||||
if ((reg_data & MAX77705_BATP_OK) || !(reg_data2 & MAX77705_BATP_DTLS))
|
||||
*val = true;
|
||||
else
|
||||
*val = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77705_get_charge_type(struct max77705_charger_data *charger, int *val)
|
||||
{
|
||||
struct regmap *regmap = charger->regmap;
|
||||
unsigned int reg_data;
|
||||
|
||||
regmap_read(regmap, MAX77705_CHG_REG_CNFG_09, ®_data);
|
||||
if (!MAX77705_CHARGER_CHG_CHARGING(reg_data)) {
|
||||
*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
regmap_read(regmap, MAX77705_CHG_REG_DETAILS_01, ®_data);
|
||||
reg_data &= MAX77705_CHG_DTLS;
|
||||
|
||||
switch (reg_data) {
|
||||
case 0x0:
|
||||
case MAX77705_CHARGER_CONSTANT_CURRENT:
|
||||
case MAX77705_CHARGER_CONSTANT_VOLTAGE:
|
||||
*val = POWER_SUPPLY_CHARGE_TYPE_FAST;
|
||||
return 0;
|
||||
default:
|
||||
*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77705_get_status(struct max77705_charger_data *charger, int *val)
|
||||
{
|
||||
struct regmap *regmap = charger->regmap;
|
||||
unsigned int reg_data;
|
||||
|
||||
regmap_read(regmap, MAX77705_CHG_REG_CNFG_09, ®_data);
|
||||
if (!MAX77705_CHARGER_CHG_CHARGING(reg_data)) {
|
||||
*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
regmap_read(regmap, MAX77705_CHG_REG_DETAILS_01, ®_data);
|
||||
reg_data &= MAX77705_CHG_DTLS;
|
||||
|
||||
switch (reg_data) {
|
||||
case 0x0:
|
||||
case MAX77705_CHARGER_CONSTANT_CURRENT:
|
||||
case MAX77705_CHARGER_CONSTANT_VOLTAGE:
|
||||
*val = POWER_SUPPLY_STATUS_CHARGING;
|
||||
return 0;
|
||||
case MAX77705_CHARGER_END_OF_CHARGE:
|
||||
case MAX77705_CHARGER_DONE:
|
||||
*val = POWER_SUPPLY_STATUS_FULL;
|
||||
return 0;
|
||||
/* those values hard coded as in vendor kernel, because of */
|
||||
/* failure to determine it's actual meaning. */
|
||||
case 0x05:
|
||||
case 0x06:
|
||||
case 0x07:
|
||||
*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
return 0;
|
||||
case 0x08:
|
||||
case 0xA:
|
||||
case 0xB:
|
||||
*val = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
return 0;
|
||||
default:
|
||||
*val = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77705_get_vbus_state(struct regmap *regmap, int *value)
|
||||
{
|
||||
int ret;
|
||||
unsigned int charge_dtls;
|
||||
|
||||
ret = regmap_read(regmap, MAX77705_CHG_REG_DETAILS_00, &charge_dtls);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
charge_dtls = ((charge_dtls & MAX77705_CHGIN_DTLS) >>
|
||||
MAX77705_CHGIN_DTLS_SHIFT);
|
||||
|
||||
switch (charge_dtls) {
|
||||
case 0x00:
|
||||
*value = POWER_SUPPLY_HEALTH_UNDERVOLTAGE;
|
||||
break;
|
||||
case 0x01:
|
||||
*value = POWER_SUPPLY_HEALTH_UNDERVOLTAGE;
|
||||
break;
|
||||
case 0x02:
|
||||
*value = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
||||
break;
|
||||
case 0x03:
|
||||
*value = POWER_SUPPLY_HEALTH_GOOD;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77705_get_battery_health(struct max77705_charger_data *charger,
|
||||
int *value)
|
||||
{
|
||||
struct regmap *regmap = charger->regmap;
|
||||
unsigned int bat_dtls;
|
||||
|
||||
regmap_read(regmap, MAX77705_CHG_REG_DETAILS_01, &bat_dtls);
|
||||
bat_dtls = ((bat_dtls & MAX77705_BAT_DTLS) >> MAX77705_BAT_DTLS_SHIFT);
|
||||
|
||||
switch (bat_dtls) {
|
||||
case MAX77705_BATTERY_NOBAT:
|
||||
dev_dbg(charger->dev, "%s: No battery and the charger is suspended\n",
|
||||
__func__);
|
||||
*value = POWER_SUPPLY_HEALTH_NO_BATTERY;
|
||||
break;
|
||||
case MAX77705_BATTERY_PREQUALIFICATION:
|
||||
dev_dbg(charger->dev, "%s: battery is okay but its voltage is low(~VPQLB)\n",
|
||||
__func__);
|
||||
break;
|
||||
case MAX77705_BATTERY_DEAD:
|
||||
dev_dbg(charger->dev, "%s: battery dead\n", __func__);
|
||||
*value = POWER_SUPPLY_HEALTH_DEAD;
|
||||
break;
|
||||
case MAX77705_BATTERY_GOOD:
|
||||
case MAX77705_BATTERY_LOWVOLTAGE:
|
||||
*value = POWER_SUPPLY_HEALTH_GOOD;
|
||||
break;
|
||||
case MAX77705_BATTERY_OVERVOLTAGE:
|
||||
dev_dbg(charger->dev, "%s: battery ovp\n", __func__);
|
||||
*value = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(charger->dev, "%s: battery unknown\n", __func__);
|
||||
*value = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77705_get_health(struct max77705_charger_data *charger, int *val)
|
||||
{
|
||||
struct regmap *regmap = charger->regmap;
|
||||
int ret, is_online = 0;
|
||||
|
||||
ret = max77705_get_online(regmap, &is_online);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (is_online) {
|
||||
ret = max77705_get_vbus_state(regmap, val);
|
||||
if (ret || (*val != POWER_SUPPLY_HEALTH_GOOD))
|
||||
return ret;
|
||||
}
|
||||
return max77705_get_battery_health(charger, val);
|
||||
}
|
||||
|
||||
static int max77705_get_input_current(struct max77705_charger_data *charger,
|
||||
int *val)
|
||||
{
|
||||
unsigned int reg_data;
|
||||
int get_current = 0;
|
||||
struct regmap *regmap = charger->regmap;
|
||||
|
||||
regmap_read(regmap, MAX77705_CHG_REG_CNFG_09, ®_data);
|
||||
|
||||
reg_data &= MAX77705_CHG_CHGIN_LIM_MASK;
|
||||
|
||||
if (reg_data <= 3)
|
||||
get_current = MAX77705_CURRENT_CHGIN_MIN;
|
||||
else if (reg_data >= MAX77705_CHG_CHGIN_LIM_MASK)
|
||||
get_current = MAX77705_CURRENT_CHGIN_MAX;
|
||||
else
|
||||
get_current = (reg_data + 1) * MAX77705_CURRENT_CHGIN_STEP;
|
||||
|
||||
*val = get_current;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77705_get_charge_current(struct max77705_charger_data *charger,
|
||||
int *val)
|
||||
{
|
||||
unsigned int reg_data;
|
||||
struct regmap *regmap = charger->regmap;
|
||||
|
||||
regmap_read(regmap, MAX77705_CHG_REG_CNFG_02, ®_data);
|
||||
reg_data &= MAX77705_CHG_CC;
|
||||
|
||||
*val = reg_data <= 0x2 ? MAX77705_CURRENT_CHGIN_MIN : reg_data * MAX77705_CURRENT_CHG_STEP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77705_set_float_voltage(struct max77705_charger_data *charger,
|
||||
int float_voltage)
|
||||
{
|
||||
int float_voltage_mv;
|
||||
unsigned int reg_data = 0;
|
||||
struct regmap *regmap = charger->regmap;
|
||||
|
||||
float_voltage_mv = float_voltage / 1000;
|
||||
reg_data = float_voltage_mv <= 4000 ? 0x0 :
|
||||
float_voltage_mv >= 4500 ? 0x23 :
|
||||
(float_voltage_mv <= 4200) ? (float_voltage_mv - 4000) / 50 :
|
||||
(((float_voltage_mv - 4200) / 10) + 0x04);
|
||||
|
||||
return regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_04,
|
||||
MAX77705_CHG_CV_PRM_MASK,
|
||||
(reg_data << MAX77705_CHG_CV_PRM_SHIFT));
|
||||
}
|
||||
|
||||
static int max77705_get_float_voltage(struct max77705_charger_data *charger,
|
||||
int *val)
|
||||
{
|
||||
unsigned int reg_data = 0;
|
||||
int voltage_mv;
|
||||
struct regmap *regmap = charger->regmap;
|
||||
|
||||
regmap_read(regmap, MAX77705_CHG_REG_CNFG_04, ®_data);
|
||||
reg_data &= MAX77705_CHG_PRM_MASK;
|
||||
voltage_mv = reg_data <= 0x04 ? reg_data * 50 + 4000 :
|
||||
(reg_data - 4) * 10 + 4200;
|
||||
*val = voltage_mv * 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77705_chg_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct max77705_charger_data *charger = power_supply_get_drvdata(psy);
|
||||
struct regmap *regmap = charger->regmap;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
return max77705_get_online(regmap, &val->intval);
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
return max77705_check_battery(charger, &val->intval);
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
return max77705_get_status(charger, &val->intval);
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
||||
return max77705_get_charge_type(charger, &val->intval);
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
return max77705_get_health(charger, &val->intval);
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
return max77705_get_input_current(charger, &val->intval);
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
return max77705_get_charge_current(charger, &val->intval);
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
|
||||
return max77705_get_float_voltage(charger, &val->intval);
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
|
||||
val->intval = charger->bat_info->voltage_max_design_uv;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
val->strval = max77705_charger_model;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MANUFACTURER:
|
||||
val->strval = max77705_charger_manufacturer;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct power_supply_desc max77705_charger_psy_desc = {
|
||||
.name = "max77705-charger",
|
||||
.type = POWER_SUPPLY_TYPE_USB,
|
||||
.properties = max77705_charger_props,
|
||||
.num_properties = ARRAY_SIZE(max77705_charger_props),
|
||||
.get_property = max77705_chg_get_property,
|
||||
};
|
||||
|
||||
static void max77705_chgin_isr_work(struct work_struct *work)
|
||||
{
|
||||
struct max77705_charger_data *charger =
|
||||
container_of(work, struct max77705_charger_data, chgin_work);
|
||||
|
||||
power_supply_changed(charger->psy_chg);
|
||||
}
|
||||
|
||||
static void max77705_charger_initialize(struct max77705_charger_data *chg)
|
||||
{
|
||||
u8 reg_data;
|
||||
struct power_supply_battery_info *info;
|
||||
struct regmap *regmap = chg->regmap;
|
||||
|
||||
if (power_supply_get_battery_info(chg->psy_chg, &info) < 0)
|
||||
return;
|
||||
|
||||
chg->bat_info = info;
|
||||
|
||||
/* unlock charger setting protect */
|
||||
/* slowest LX slope */
|
||||
reg_data = MAX77705_CHGPROT_MASK | MAX77705_SLOWEST_LX_SLOPE;
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_06, reg_data,
|
||||
reg_data);
|
||||
|
||||
/* fast charge timer disable */
|
||||
/* restart threshold disable */
|
||||
/* pre-qual charge disable */
|
||||
reg_data = (MAX77705_FCHGTIME_DISABLE << MAX77705_FCHGTIME_SHIFT) |
|
||||
(MAX77705_CHG_RSTRT_DISABLE << MAX77705_CHG_RSTRT_SHIFT) |
|
||||
(MAX77705_CHG_PQEN_DISABLE << MAX77705_PQEN_SHIFT);
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_01,
|
||||
(MAX77705_FCHGTIME_MASK |
|
||||
MAX77705_CHG_RSTRT_MASK |
|
||||
MAX77705_PQEN_MASK),
|
||||
reg_data);
|
||||
|
||||
/* OTG off(UNO on), boost off */
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_00,
|
||||
MAX77705_OTG_CTRL, 0);
|
||||
|
||||
/* charge current 450mA(default) */
|
||||
/* otg current limit 900mA */
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_02,
|
||||
MAX77705_OTG_ILIM_MASK,
|
||||
MAX77705_OTG_ILIM_900 << MAX77705_OTG_ILIM_SHIFT);
|
||||
|
||||
/* BAT to SYS OCP 4.80A */
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_05,
|
||||
MAX77705_REG_B2SOVRC_MASK,
|
||||
MAX77705_B2SOVRC_4_8A << MAX77705_REG_B2SOVRC_SHIFT);
|
||||
/* top off current 150mA */
|
||||
/* top off timer 30min */
|
||||
reg_data = (MAX77705_TO_ITH_150MA << MAX77705_TO_ITH_SHIFT) |
|
||||
(MAX77705_TO_TIME_30M << MAX77705_TO_TIME_SHIFT) |
|
||||
(MAX77705_SYS_TRACK_DISABLE << MAX77705_SYS_TRACK_DIS_SHIFT);
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_03,
|
||||
(MAX77705_TO_ITH_MASK |
|
||||
MAX77705_TO_TIME_MASK |
|
||||
MAX77705_SYS_TRACK_DIS_MASK), reg_data);
|
||||
|
||||
/* cv voltage 4.2V or 4.35V */
|
||||
/* MINVSYS 3.6V(default) */
|
||||
if (info->voltage_max_design_uv < 0) {
|
||||
dev_warn(chg->dev, "missing battery:voltage-max-design-microvolt\n");
|
||||
max77705_set_float_voltage(chg, 4200000);
|
||||
} else {
|
||||
max77705_set_float_voltage(chg, info->voltage_max_design_uv);
|
||||
}
|
||||
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_12,
|
||||
MAX77705_VCHGIN_REG_MASK, MAX77705_VCHGIN_4_5);
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_12,
|
||||
MAX77705_WCIN_REG_MASK, MAX77705_WCIN_4_5);
|
||||
|
||||
/* Watchdog timer */
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_00,
|
||||
MAX77705_WDTEN_MASK, 0);
|
||||
|
||||
/* Active Discharge Enable */
|
||||
regmap_update_bits(regmap, MAX77705_PMIC_REG_MAINCTRL1, 1, 1);
|
||||
|
||||
/* VBYPSET=5.0V */
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_11, MAX77705_VBYPSET_MASK, 0);
|
||||
|
||||
/* Switching Frequency : 1.5MHz */
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_08, MAX77705_REG_FSW_MASK,
|
||||
(MAX77705_CHG_FSW_1_5MHz << MAX77705_REG_FSW_SHIFT));
|
||||
|
||||
/* Auto skip mode */
|
||||
regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_12, MAX77705_REG_DISKIP_MASK,
|
||||
(MAX77705_AUTO_SKIP << MAX77705_REG_DISKIP_SHIFT));
|
||||
}
|
||||
|
||||
static int max77705_charger_probe(struct i2c_client *i2c)
|
||||
{
|
||||
struct power_supply_config pscfg = {};
|
||||
struct max77705_charger_data *chg;
|
||||
struct device *dev;
|
||||
struct regmap_irq_chip_data *irq_data;
|
||||
int ret;
|
||||
|
||||
dev = &i2c->dev;
|
||||
|
||||
chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL);
|
||||
if (!chg)
|
||||
return -ENOMEM;
|
||||
|
||||
chg->dev = dev;
|
||||
i2c_set_clientdata(i2c, chg);
|
||||
|
||||
chg->regmap = devm_regmap_init_i2c(i2c, &max77705_chg_regmap_config);
|
||||
if (IS_ERR(chg->regmap))
|
||||
return PTR_ERR(chg->regmap);
|
||||
|
||||
ret = regmap_update_bits(chg->regmap,
|
||||
MAX77705_CHG_REG_INT_MASK,
|
||||
MAX77705_CHGIN_IM, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pscfg.fwnode = dev_fwnode(dev);
|
||||
pscfg.drv_data = chg;
|
||||
|
||||
chg->psy_chg = devm_power_supply_register(dev, &max77705_charger_psy_desc, &pscfg);
|
||||
if (IS_ERR(chg->psy_chg))
|
||||
return PTR_ERR(chg->psy_chg);
|
||||
|
||||
max77705_charger_irq_chip.irq_drv_data = chg;
|
||||
ret = devm_regmap_add_irq_chip(chg->dev, chg->regmap, i2c->irq,
|
||||
IRQF_ONESHOT | IRQF_SHARED, 0,
|
||||
&max77705_charger_irq_chip,
|
||||
&irq_data);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to add irq chip\n");
|
||||
|
||||
chg->wqueue = create_singlethread_workqueue(dev_name(dev));
|
||||
if (IS_ERR(chg->wqueue))
|
||||
return dev_err_probe(dev, PTR_ERR(chg->wqueue), "failed to create workqueue\n");
|
||||
|
||||
ret = devm_work_autocancel(dev, &chg->chgin_work, max77705_chgin_isr_work);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to initialize interrupt work\n");
|
||||
|
||||
max77705_charger_initialize(chg);
|
||||
|
||||
ret = max77705_charger_enable(chg);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to enable charge\n");
|
||||
|
||||
return devm_add_action_or_reset(dev, max77705_charger_disable, chg);
|
||||
}
|
||||
|
||||
static const struct of_device_id max77705_charger_of_match[] = {
|
||||
{ .compatible = "maxim,max77705-charger" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max77705_charger_of_match);
|
||||
|
||||
static struct i2c_driver max77705_charger_driver = {
|
||||
.driver = {
|
||||
.name = "max77705-charger",
|
||||
.of_match_table = max77705_charger_of_match,
|
||||
},
|
||||
.probe = max77705_charger_probe,
|
||||
};
|
||||
module_i2c_driver(max77705_charger_driver);
|
||||
|
||||
MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>");
|
||||
MODULE_DESCRIPTION("Maxim MAX77705 charger driver");
|
||||
MODULE_LICENSE("GPL");
|
195
include/linux/power/max77705_charger.h
Normal file
195
include/linux/power/max77705_charger.h
Normal file
@ -0,0 +1,195 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Maxim MAX77705 definitions.
|
||||
*
|
||||
* Copyright (C) 2015 Samsung Electronics, Inc.
|
||||
* Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef __MAX77705_CHARGER_H
|
||||
#define __MAX77705_CHARGER_H __FILE__
|
||||
|
||||
/* MAX77705_CHG_REG_CHG_INT */
|
||||
#define MAX77705_BYP_I BIT(0)
|
||||
#define MAX77705_INP_LIMIT_I BIT(1)
|
||||
#define MAX77705_BATP_I BIT(2)
|
||||
#define MAX77705_BAT_I BIT(3)
|
||||
#define MAX77705_CHG_I BIT(4)
|
||||
#define MAX77705_WCIN_I BIT(5)
|
||||
#define MAX77705_CHGIN_I BIT(6)
|
||||
#define MAX77705_AICL_I BIT(7)
|
||||
|
||||
/* MAX77705_CHG_REG_CHG_INT_MASK */
|
||||
#define MAX77705_BYP_IM BIT(0)
|
||||
#define MAX77705_INP_LIMIT_IM BIT(1)
|
||||
#define MAX77705_BATP_IM BIT(2)
|
||||
#define MAX77705_BAT_IM BIT(3)
|
||||
#define MAX77705_CHG_IM BIT(4)
|
||||
#define MAX77705_WCIN_IM BIT(5)
|
||||
#define MAX77705_CHGIN_IM BIT(6)
|
||||
#define MAX77705_AICL_IM BIT(7)
|
||||
|
||||
/* MAX77705_CHG_REG_CHG_INT_OK */
|
||||
#define MAX77705_BYP_OK BIT(0)
|
||||
#define MAX77705_DISQBAT_OK BIT(1)
|
||||
#define MAX77705_BATP_OK BIT(2)
|
||||
#define MAX77705_BAT_OK BIT(3)
|
||||
#define MAX77705_CHG_OK BIT(4)
|
||||
#define MAX77705_WCIN_OK BIT(5)
|
||||
#define MAX77705_CHGIN_OK BIT(6)
|
||||
#define MAX77705_AICL_OK BIT(7)
|
||||
|
||||
/* MAX77705_CHG_REG_DETAILS_00 */
|
||||
#define MAX77705_BATP_DTLS BIT(0)
|
||||
#define MAX77705_WCIN_DTLS GENMASK(4, 3)
|
||||
#define MAX77705_WCIN_DTLS_SHIFT 3
|
||||
#define MAX77705_CHGIN_DTLS GENMASK(6, 5)
|
||||
#define MAX77705_CHGIN_DTLS_SHIFT 5
|
||||
|
||||
/* MAX77705_CHG_REG_DETAILS_01 */
|
||||
#define MAX77705_CHG_DTLS GENMASK(3, 0)
|
||||
#define MAX77705_CHG_DTLS_SHIFT 0
|
||||
#define MAX77705_BAT_DTLS GENMASK(6, 4)
|
||||
#define MAX77705_BAT_DTLS_SHIFT 4
|
||||
|
||||
/* MAX77705_CHG_REG_DETAILS_02 */
|
||||
#define MAX77705_BYP_DTLS GENMASK(3, 0)
|
||||
#define MAX77705_BYP_DTLS_SHIFT 0
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_00 */
|
||||
#define MAX77705_CHG_SHIFT 0
|
||||
#define MAX77705_UNO_SHIFT 1
|
||||
#define MAX77705_OTG_SHIFT 1
|
||||
#define MAX77705_BUCK_SHIFT 2
|
||||
#define MAX77705_BOOST_SHIFT 3
|
||||
#define MAX77705_WDTEN_SHIFT 4
|
||||
#define MAX77705_MODE_MASK GENMASK(3, 0)
|
||||
#define MAX77705_CHG_MASK BIT(MAX77705_CHG_SHIFT)
|
||||
#define MAX77705_UNO_MASK BIT(MAX77705_UNO_SHIFT)
|
||||
#define MAX77705_OTG_MASK BIT(MAX77705_OTG_SHIFT)
|
||||
#define MAX77705_BUCK_MASK BIT(MAX77705_BUCK_SHIFT)
|
||||
#define MAX77705_BOOST_MASK BIT(MAX77705_BOOST_SHIFT)
|
||||
#define MAX77705_WDTEN_MASK BIT(MAX77705_WDTEN_SHIFT)
|
||||
#define MAX77705_UNO_CTRL (MAX77705_UNO_MASK | MAX77705_BOOST_MASK)
|
||||
#define MAX77705_OTG_CTRL (MAX77705_OTG_MASK | MAX77705_BOOST_MASK)
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_01 */
|
||||
#define MAX77705_FCHGTIME_SHIFT 0
|
||||
#define MAX77705_FCHGTIME_MASK GENMASK(2, 0)
|
||||
#define MAX77705_CHG_RSTRT_SHIFT 4
|
||||
#define MAX77705_CHG_RSTRT_MASK GENMASK(5, 4)
|
||||
#define MAX77705_FCHGTIME_DISABLE 0
|
||||
#define MAX77705_CHG_RSTRT_DISABLE 0x3
|
||||
|
||||
#define MAX77705_PQEN_SHIFT 7
|
||||
#define MAX77705_PQEN_MASK BIT(7)
|
||||
#define MAX77705_CHG_PQEN_DISABLE 0
|
||||
#define MAX77705_CHG_PQEN_ENABLE 1
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_02 */
|
||||
#define MAX77705_OTG_ILIM_SHIFT 6
|
||||
#define MAX77705_OTG_ILIM_MASK GENMASK(7, 6)
|
||||
#define MAX77705_OTG_ILIM_500 0
|
||||
#define MAX77705_OTG_ILIM_900 1
|
||||
#define MAX77705_OTG_ILIM_1200 2
|
||||
#define MAX77705_OTG_ILIM_1500 3
|
||||
#define MAX77705_CHG_CC GENMASK(5, 0)
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_03 */
|
||||
#define MAX77705_TO_ITH_SHIFT 0
|
||||
#define MAX77705_TO_ITH_MASK GENMASK(2, 0)
|
||||
#define MAX77705_TO_TIME_SHIFT 3
|
||||
#define MAX77705_TO_TIME_MASK GENMASK(5, 3)
|
||||
#define MAX77705_SYS_TRACK_DIS_SHIFT 7
|
||||
#define MAX77705_SYS_TRACK_DIS_MASK BIT(7)
|
||||
#define MAX77705_TO_ITH_150MA 0
|
||||
#define MAX77705_TO_TIME_30M 3
|
||||
#define MAX77705_SYS_TRACK_ENABLE 0
|
||||
#define MAX77705_SYS_TRACK_DISABLE 1
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_04 */
|
||||
#define MAX77705_CHG_MINVSYS_SHIFT 6
|
||||
#define MAX77705_CHG_MINVSYS_MASK GENMASK(7, 6)
|
||||
#define MAX77705_CHG_PRM_SHIFT 0
|
||||
#define MAX77705_CHG_PRM_MASK GENMASK(5, 0)
|
||||
|
||||
#define MAX77705_CHG_CV_PRM_SHIFT 0
|
||||
#define MAX77705_CHG_CV_PRM_MASK GENMASK(5, 0)
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_05 */
|
||||
#define MAX77705_REG_B2SOVRC_SHIFT 0
|
||||
#define MAX77705_REG_B2SOVRC_MASK GENMASK(3, 0)
|
||||
#define MAX77705_B2SOVRC_DISABLE 0
|
||||
#define MAX77705_B2SOVRC_4_5A 6
|
||||
#define MAX77705_B2SOVRC_4_8A 8
|
||||
#define MAX77705_B2SOVRC_5_0A 9
|
||||
|
||||
/* MAX77705_CHG_CNFG_06 */
|
||||
#define MAX77705_WDTCLR_SHIFT 0
|
||||
#define MAX77705_WDTCLR_MASK GENMASK(1, 0)
|
||||
#define MAX77705_WDTCLR 1
|
||||
#define MAX77705_CHGPROT_MASK GENMASK(3, 2)
|
||||
#define MAX77705_CHGPROT_UNLOCKED GENMASK(3, 2)
|
||||
#define MAX77705_SLOWEST_LX_SLOPE GENMASK(6, 5)
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_07 */
|
||||
#define MAX77705_CHG_FMBST 4
|
||||
#define MAX77705_REG_FMBST_SHIFT 2
|
||||
#define MAX77705_REG_FMBST_MASK BIT(MAX77705_REG_FMBST_SHIFT)
|
||||
#define MAX77705_REG_FGSRC_SHIFT 1
|
||||
#define MAX77705_REG_FGSRC_MASK BIT(MAX77705_REG_FGSRC_SHIFT)
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_08 */
|
||||
#define MAX77705_REG_FSW_SHIFT 0
|
||||
#define MAX77705_REG_FSW_MASK GENMASK(1, 0)
|
||||
#define MAX77705_CHG_FSW_3MHz 0
|
||||
#define MAX77705_CHG_FSW_2MHz 1
|
||||
#define MAX77705_CHG_FSW_1_5MHz 2
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_09 */
|
||||
#define MAX77705_CHG_CHGIN_LIM_MASK GENMASK(6, 0)
|
||||
#define MAX77705_CHG_EN_MASK BIT(7)
|
||||
#define MAX77705_CHG_DISABLE 0
|
||||
#define MAX77705_CHARGER_CHG_CHARGING(_reg) \
|
||||
(((_reg) & MAX77705_CHG_EN_MASK) > 1)
|
||||
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_10 */
|
||||
#define MAX77705_CHG_WCIN_LIM GENMASK(5, 0)
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_11 */
|
||||
#define MAX77705_VBYPSET_SHIFT 0
|
||||
#define MAX77705_VBYPSET_MASK GENMASK(6, 0)
|
||||
|
||||
/* MAX77705_CHG_REG_CNFG_12 */
|
||||
#define MAX77705_CHGINSEL_SHIFT 5
|
||||
#define MAX77705_CHGINSEL_MASK BIT(MAX77705_CHGINSEL_SHIFT)
|
||||
#define MAX77705_WCINSEL_SHIFT 6
|
||||
#define MAX77705_WCINSEL_MASK BIT(MAX77705_WCINSEL_SHIFT)
|
||||
#define MAX77705_VCHGIN_REG_MASK GENMASK(4, 3)
|
||||
#define MAX77705_WCIN_REG_MASK GENMASK(2, 1)
|
||||
#define MAX77705_REG_DISKIP_SHIFT 0
|
||||
#define MAX77705_REG_DISKIP_MASK BIT(MAX77705_REG_DISKIP_SHIFT)
|
||||
/* REG=4.5V, UVLO=4.7V */
|
||||
#define MAX77705_VCHGIN_4_5 0
|
||||
/* REG=4.5V, UVLO=4.7V */
|
||||
#define MAX77705_WCIN_4_5 0
|
||||
#define MAX77705_DISABLE_SKIP 1
|
||||
#define MAX77705_AUTO_SKIP 0
|
||||
|
||||
/* uA */
|
||||
#define MAX77705_CURRENT_CHGIN_STEP 25000
|
||||
#define MAX77705_CURRENT_CHG_STEP 50000
|
||||
#define MAX77705_CURRENT_CHGIN_MIN 100000
|
||||
#define MAX77705_CURRENT_CHGIN_MAX 3200000
|
||||
|
||||
struct max77705_charger_data {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct power_supply_battery_info *bat_info;
|
||||
struct workqueue_struct *wqueue;
|
||||
struct work_struct chgin_work;
|
||||
struct power_supply *psy_chg;
|
||||
};
|
||||
|
||||
#endif /* __MAX77705_CHARGER_H */
|
Loading…
Reference in New Issue
Block a user