Files
linux/drivers/mfd/max77705.c
Linus Torvalds 3ee22ad492 Merge tag 'for-v6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply
Pull power supply and reset updates from Sebastian Reichel:
 "Power-supply core:
   - introduce adc-battery-helper for capacity estimation based on
     simple ADC readings of battery voltage and current
   - add new properties for battery internal resistance and state of
     health

  Power-supply drivers:
   - ug3105_battery: convert to adc-battery-helper
   - intel_dc_ti_battery: New driver for Intel Dollar Cove TI batteries
   - rt9467-charger: add voltage and current ADC support
   - sbs-charger: support multiple instances
   - qcom_battmgr:
       - add charge control support
       - add support for state of health and internal resistance
   - max77705_charger:
       - big driver cleanup
       - add support for setting charge current
   - misc minor fixes and cleanups"

* tag 'for-v6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (38 commits)
  power: supply: qcom_battmgr: handle charging state change notifications
  power: supply: max77705_charger: use REGMAP_IRQ_REG_LINE macro
  power: supply: max77705_charger: rework interrupts
  power: supply: max77705_charger: add writable properties
  power: supply: max77705_charger: return error when config fails
  power: supply: max77705_charger: use regfields for config registers
  power: supply: max77705_charger: refactoring: rename charger to chg
  mfd: max77705: max77705_charger: move active discharge setting to mfd parent
  power: supply: max77976_charger: fix constant current reporting
  power: supply: qcom_battmgr: Add charge control support
  dt-bindings: soc: qcom,pmic-glink: Add charge limit nvmem properties
  power: supply: qcom_battmgr: update compats for SM8550 and X1E80100
  power: supply: qcom_battmgr: Add state_of_health property
  power: supply: qcom_battmgr: Add resistance power supply property
  power: supply: core: Add state_of_health power supply property
  power: supply: core: Add resistance power supply property
  power: supply: rx51: remove redundant condition checks
  dt-bindings: power: supply: bq24190: document charge enable pin
  dt-bindings: power: supply: bq27xxx: document optional interrupt
  power: supply: intel_dc_ti_battery: Drop no longer relevant comment
  ...
2025-10-01 13:02:59 -07:00

181 lines
5.6 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Maxim MAX77705 PMIC core driver
*
* Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.com>
**/
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
#include <linux/mfd/max77705-private.h>
#include <linux/mfd/max77693-common.h>
#include <linux/pm.h>
#include <linux/power/max17042_battery.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/of.h>
static struct mfd_cell max77705_devs[] = {
MFD_CELL_OF("max77705-rgb", NULL, NULL, 0, 0, "maxim,max77705-rgb"),
MFD_CELL_OF("max77705-charger", NULL, NULL, 0, 0, "maxim,max77705-charger"),
MFD_CELL_OF("max77705-haptic", NULL, NULL, 0, 0, "maxim,max77705-haptic"),
};
static const struct regmap_range max77705_readable_ranges[] = {
regmap_reg_range(MAX77705_PMIC_REG_PMICID1, MAX77705_PMIC_REG_BSTOUT_MASK),
regmap_reg_range(MAX77705_PMIC_REG_INTSRC, MAX77705_PMIC_REG_RESERVED_29),
regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1),
regmap_reg_range(MAX77705_PMIC_REG_MCONFIG, MAX77705_PMIC_REG_MCONFIG2),
regmap_reg_range(MAX77705_PMIC_REG_FORCE_EN_MASK, MAX77705_PMIC_REG_FORCE_EN_MASK),
regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1),
regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL2, MAX77705_PMIC_REG_BOOSTCONTROL2),
regmap_reg_range(MAX77705_PMIC_REG_SW_RESET, MAX77705_PMIC_REG_USBC_RESET),
};
static const struct regmap_range max77705_writable_ranges[] = {
regmap_reg_range(MAX77705_PMIC_REG_MAINCTRL1, MAX77705_PMIC_REG_BSTOUT_MASK),
regmap_reg_range(MAX77705_PMIC_REG_INTSRC, MAX77705_PMIC_REG_RESERVED_29),
regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1),
regmap_reg_range(MAX77705_PMIC_REG_MCONFIG, MAX77705_PMIC_REG_MCONFIG2),
regmap_reg_range(MAX77705_PMIC_REG_FORCE_EN_MASK, MAX77705_PMIC_REG_FORCE_EN_MASK),
regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1),
regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL2, MAX77705_PMIC_REG_BOOSTCONTROL2),
regmap_reg_range(MAX77705_PMIC_REG_SW_RESET, MAX77705_PMIC_REG_USBC_RESET),
};
static const struct regmap_access_table max77705_readable_table = {
.yes_ranges = max77705_readable_ranges,
.n_yes_ranges = ARRAY_SIZE(max77705_readable_ranges),
};
static const struct regmap_access_table max77705_writable_table = {
.yes_ranges = max77705_writable_ranges,
.n_yes_ranges = ARRAY_SIZE(max77705_writable_ranges),
};
static const struct regmap_config max77705_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.rd_table = &max77705_readable_table,
.wr_table = &max77705_writable_table,
.max_register = MAX77705_PMIC_REG_USBC_RESET,
};
static const struct regmap_irq max77705_irqs[] = {
{ .mask = MAX77705_SRC_IRQ_CHG, },
{ .mask = MAX77705_SRC_IRQ_TOP, },
{ .mask = MAX77705_SRC_IRQ_FG, },
{ .mask = MAX77705_SRC_IRQ_USBC, },
};
static const struct regmap_irq_chip max77705_irq_chip = {
.name = "max77705",
.status_base = MAX77705_PMIC_REG_INTSRC,
.ack_base = MAX77705_PMIC_REG_INTSRC,
.mask_base = MAX77705_PMIC_REG_INTSRC_MASK,
.num_regs = 1,
.irqs = max77705_irqs,
.num_irqs = ARRAY_SIZE(max77705_irqs),
};
static int max77705_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct max77693_dev *max77705;
struct regmap_irq_chip_data *irq_data;
struct irq_domain *domain;
enum max77705_hw_rev pmic_rev;
unsigned int pmic_rev_value;
int ret;
max77705 = devm_kzalloc(dev, sizeof(*max77705), GFP_KERNEL);
if (!max77705)
return -ENOMEM;
max77705->i2c = i2c;
max77705->type = TYPE_MAX77705;
i2c_set_clientdata(i2c, max77705);
max77705->regmap = devm_regmap_init_i2c(i2c, &max77705_regmap_config);
if (IS_ERR(max77705->regmap))
return PTR_ERR(max77705->regmap);
ret = regmap_read(max77705->regmap, MAX77705_PMIC_REG_PMICREV, &pmic_rev_value);
if (ret < 0)
return -ENODEV;
pmic_rev = pmic_rev_value & MAX77705_REVISION_MASK;
if (pmic_rev != MAX77705_PASS3)
return dev_err_probe(dev, -ENODEV, "Rev.0x%x is not tested\n", pmic_rev);
/* Active Discharge Enable */
regmap_update_bits(max77705->regmap, MAX77705_PMIC_REG_MAINCTRL1, 1, 1);
ret = devm_regmap_add_irq_chip(dev, max77705->regmap,
i2c->irq,
IRQF_ONESHOT, 0,
&max77705_irq_chip,
&irq_data);
if (ret)
return dev_err_probe(dev, ret, "Failed to add IRQ chip\n");
domain = regmap_irq_get_domain(irq_data);
ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
max77705_devs, ARRAY_SIZE(max77705_devs),
NULL, 0, domain);
if (ret)
return dev_err_probe(dev, ret, "Failed to register child devices\n");
ret = devm_device_init_wakeup(dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to init wakeup\n");
return 0;
}
static int max77705_suspend(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
disable_irq(i2c->irq);
if (device_may_wakeup(dev))
enable_irq_wake(i2c->irq);
return 0;
}
static int max77705_resume(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
if (device_may_wakeup(dev))
disable_irq_wake(i2c->irq);
enable_irq(i2c->irq);
return 0;
}
DEFINE_SIMPLE_DEV_PM_OPS(max77705_pm_ops, max77705_suspend, max77705_resume);
static const struct of_device_id max77705_i2c_of_match[] = {
{ .compatible = "maxim,max77705" },
{ }
};
MODULE_DEVICE_TABLE(of, max77705_i2c_of_match);
static struct i2c_driver max77705_i2c_driver = {
.driver = {
.name = "max77705",
.of_match_table = max77705_i2c_of_match,
.pm = pm_sleep_ptr(&max77705_pm_ops),
},
.probe = max77705_i2c_probe
};
module_i2c_driver(max77705_i2c_driver);
MODULE_DESCRIPTION("Maxim MAX77705 PMIC core driver");
MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>");
MODULE_LICENSE("GPL");