diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 17:40:19 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 17:40:19 +0000 |
commit | 9f0fc191371843c4fc000a226b0a26b6c059aacd (patch) | |
tree | 35f8be3ef04506ac891ad001e8c41e535ae8d01d /drivers/power/supply | |
parent | Releasing progress-linux version 6.6.15-2~progress7.99u1. (diff) | |
download | linux-9f0fc191371843c4fc000a226b0a26b6c059aacd.tar.xz linux-9f0fc191371843c4fc000a226b0a26b6c059aacd.zip |
Merging upstream version 6.7.7.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/power/supply')
56 files changed, 1569 insertions, 291 deletions
diff --git a/drivers/power/supply/88pm860x_battery.c b/drivers/power/supply/88pm860x_battery.c index f3f3f8cd1a..34619c4d4e 100644 --- a/drivers/power/supply/88pm860x_battery.c +++ b/drivers/power/supply/88pm860x_battery.c @@ -921,12 +921,12 @@ static int pm860x_battery_probe(struct platform_device *pdev) return -ENOMEM; info->irq_cc = platform_get_irq(pdev, 0); - if (info->irq_cc <= 0) - return -EINVAL; + if (info->irq_cc < 0) + return info->irq_cc; info->irq_batt = platform_get_irq(pdev, 1); - if (info->irq_batt <= 0) - return -EINVAL; + if (info->irq_batt < 0) + return info->irq_batt; info->chip = chip; info->i2c = diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index a61bb1283e..f21cb05815 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -374,7 +374,7 @@ config AXP288_FUEL_GAUGE config BATTERY_MAX17040 tristate "Maxim MAX17040/17041/17043 family Fuel Gauge" - depends on I2C + depends on I2C && IIO select REGMAP_I2C help Driver supports Maxim fuel-gauge systems for lithium-ion (Li+) @@ -629,6 +629,29 @@ config CHARGER_QCOM_SMBB documentation for more detail. The base name for this driver is 'pm8941_charger'. +config BATTERY_PM8916_BMS_VM + tristate "Qualcomm PM8916 BMS-VM support" + depends on MFD_SPMI_PMIC || COMPILE_TEST + help + Say Y to add support for Voltage Mode BMS block found in some + Qualcomm PMICs such as PM8916. This hardware block provides + battery voltage monitoring for the system. + + To compile this driver as module, choose M here: the + module will be called pm8916_bms_vm. + +config CHARGER_PM8916_LBC + tristate "Qualcomm PM8916 Linear Battery Charger support" + depends on MFD_SPMI_PMIC || COMPILE_TEST + depends on EXTCON || !EXTCON + help + Say Y here to add support for Linear Battery Charger block + found in some Qualcomm PMICs such as PM8916. This hardware + blokc provides simple CC/CV battery charger. + + To compile this driver as module, choose M here: the + module will be called pm8916_lbc. + config CHARGER_BQ2415X tristate "TI BQ2415x battery charger driver" depends on I2C @@ -952,4 +975,13 @@ config CHARGER_QCOM_SMB2 adds support for the SMB2 switch mode battery charger found in PMI8998 and related PMICs. +config FUEL_GAUGE_MM8013 + tristate "Mitsumi MM8013 fuel gauge driver" + depends on I2C + help + Say Y here to enable the Mitsumi MM8013 fuel gauge driver. + It enables the monitoring of many battery parameters, including + the state of charge, temperature, cycle count, actual and design + capacity, etc. + endif # POWER_SUPPLY diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index a8a9fa6de1..58b5672780 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -84,6 +84,8 @@ obj-$(CONFIG_CHARGER_MP2629) += mp2629_charger.o obj-$(CONFIG_CHARGER_MT6360) += mt6360_charger.o obj-$(CONFIG_CHARGER_MT6370) += mt6370-charger.o obj-$(CONFIG_CHARGER_QCOM_SMBB) += qcom_smbb.o +obj-$(CONFIG_BATTERY_PM8916_BMS_VM) += pm8916_bms_vm.o +obj-$(CONFIG_CHARGER_PM8916_LBC) += pm8916_lbc.o obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o obj-$(CONFIG_CHARGER_BQ24257) += bq24257_charger.o @@ -111,3 +113,4 @@ obj-$(CONFIG_BATTERY_SURFACE) += surface_battery.o obj-$(CONFIG_CHARGER_SURFACE) += surface_charger.o obj-$(CONFIG_BATTERY_UG3105) += ug3105_battery.o obj-$(CONFIG_CHARGER_QCOM_SMB2) += qcom_pmi8998_charger.o +obj-$(CONFIG_FUEL_GAUGE_MM8013) += mm8013.o diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index ce36d6ca34..7905eba93d 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -804,11 +804,9 @@ static int ab8500_btemp_probe(struct platform_device *pdev) return component_add(dev, &ab8500_btemp_component_ops); } -static int ab8500_btemp_remove(struct platform_device *pdev) +static void ab8500_btemp_remove(struct platform_device *pdev) { component_del(&pdev->dev, &ab8500_btemp_component_ops); - - return 0; } static SIMPLE_DEV_PM_OPS(ab8500_btemp_pm_ops, ab8500_btemp_suspend, ab8500_btemp_resume); @@ -821,7 +819,7 @@ MODULE_DEVICE_TABLE(of, ab8500_btemp_match); struct platform_driver ab8500_btemp_driver = { .probe = ab8500_btemp_probe, - .remove = ab8500_btemp_remove, + .remove_new = ab8500_btemp_remove, .driver = { .name = "ab8500-btemp", .of_match_table = ab8500_btemp_match, diff --git a/drivers/power/supply/ab8500_chargalg.c b/drivers/power/supply/ab8500_chargalg.c index 2205ea0834..de912658fa 100644 --- a/drivers/power/supply/ab8500_chargalg.c +++ b/drivers/power/supply/ab8500_chargalg.c @@ -1824,11 +1824,9 @@ static int ab8500_chargalg_probe(struct platform_device *pdev) return component_add(dev, &ab8500_chargalg_component_ops); } -static int ab8500_chargalg_remove(struct platform_device *pdev) +static void ab8500_chargalg_remove(struct platform_device *pdev) { component_del(&pdev->dev, &ab8500_chargalg_component_ops); - - return 0; } static SIMPLE_DEV_PM_OPS(ab8500_chargalg_pm_ops, ab8500_chargalg_suspend, ab8500_chargalg_resume); @@ -1840,7 +1838,7 @@ static const struct of_device_id ab8500_chargalg_match[] = { struct platform_driver ab8500_chargalg_driver = { .probe = ab8500_chargalg_probe, - .remove = ab8500_chargalg_remove, + .remove_new = ab8500_chargalg_remove, .driver = { .name = "ab8500_chargalg", .of_match_table = ab8500_chargalg_match, diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index 308e68545d..d72f32c663 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -3679,7 +3679,7 @@ remove_ab8500_bm: return ret; } -static int ab8500_charger_remove(struct platform_device *pdev) +static void ab8500_charger_remove(struct platform_device *pdev) { struct ab8500_charger *di = platform_get_drvdata(pdev); @@ -3688,8 +3688,6 @@ static int ab8500_charger_remove(struct platform_device *pdev) usb_unregister_notifier(di->usb_phy, &di->nb); ab8500_bm_of_remove(di->usb_chg.psy, di->bm); usb_put_phy(di->usb_phy); - - return 0; } static SIMPLE_DEV_PM_OPS(ab8500_charger_pm_ops, ab8500_charger_suspend, ab8500_charger_resume); @@ -3702,7 +3700,7 @@ MODULE_DEVICE_TABLE(of, ab8500_charger_match); static struct platform_driver ab8500_charger_driver = { .probe = ab8500_charger_probe, - .remove = ab8500_charger_remove, + .remove_new = ab8500_charger_remove, .driver = { .name = "ab8500-charger", .of_match_table = ab8500_charger_match, diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index 53560fbb6d..8c593fbdd4 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -3227,7 +3227,7 @@ static int ab8500_fg_probe(struct platform_device *pdev) return component_add(dev, &ab8500_fg_component_ops); } -static int ab8500_fg_remove(struct platform_device *pdev) +static void ab8500_fg_remove(struct platform_device *pdev) { struct ab8500_fg *di = platform_get_drvdata(pdev); @@ -3236,8 +3236,6 @@ static int ab8500_fg_remove(struct platform_device *pdev) list_del(&di->node); ab8500_fg_sysfs_exit(di); ab8500_fg_sysfs_psy_remove_attrs(di); - - return 0; } static SIMPLE_DEV_PM_OPS(ab8500_fg_pm_ops, ab8500_fg_suspend, ab8500_fg_resume); @@ -3250,7 +3248,7 @@ MODULE_DEVICE_TABLE(of, ab8500_fg_match); struct platform_driver ab8500_fg_driver = { .probe = ab8500_fg_probe, - .remove = ab8500_fg_remove, + .remove_new = ab8500_fg_remove, .driver = { .name = "ab8500-fg", .of_match_table = ab8500_fg_match, diff --git a/drivers/power/supply/acer_a500_battery.c b/drivers/power/supply/acer_a500_battery.c index 32a0bfcac0..ef5c419b1b 100644 --- a/drivers/power/supply/acer_a500_battery.c +++ b/drivers/power/supply/acer_a500_battery.c @@ -251,13 +251,11 @@ static int a500_battery_probe(struct platform_device *pdev) return 0; } -static int a500_battery_remove(struct platform_device *pdev) +static void a500_battery_remove(struct platform_device *pdev) { struct a500_battery *bat = dev_get_drvdata(&pdev->dev); cancel_delayed_work_sync(&bat->poll_work); - - return 0; } static int __maybe_unused a500_battery_suspend(struct device *dev) @@ -287,7 +285,7 @@ static struct platform_driver a500_battery_driver = { .pm = &a500_battery_pm_ops, }, .probe = a500_battery_probe, - .remove = a500_battery_remove, + .remove_new = a500_battery_remove, }; module_platform_driver(a500_battery_driver); diff --git a/drivers/power/supply/act8945a_charger.c b/drivers/power/supply/act8945a_charger.c index e9b5f42837..51122bfbf1 100644 --- a/drivers/power/supply/act8945a_charger.c +++ b/drivers/power/supply/act8945a_charger.c @@ -638,14 +638,12 @@ static int act8945a_charger_probe(struct platform_device *pdev) return 0; } -static int act8945a_charger_remove(struct platform_device *pdev) +static void act8945a_charger_remove(struct platform_device *pdev) { struct act8945a_charger *charger = platform_get_drvdata(pdev); charger->init_done = false; cancel_work_sync(&charger->work); - - return 0; } static struct platform_driver act8945a_charger_driver = { @@ -653,7 +651,7 @@ static struct platform_driver act8945a_charger_driver = { .name = "act8945a-charger", }, .probe = act8945a_charger_probe, - .remove = act8945a_charger_remove, + .remove_new = act8945a_charger_remove, }; module_platform_driver(act8945a_charger_driver); diff --git a/drivers/power/supply/axp20x_ac_power.c b/drivers/power/supply/axp20x_ac_power.c index 19a1186331..e5733cb9e1 100644 --- a/drivers/power/supply/axp20x_ac_power.c +++ b/drivers/power/supply/axp20x_ac_power.c @@ -45,7 +45,7 @@ struct axp20x_ac_power { struct iio_channel *acin_i; bool has_acin_path_sel; unsigned int num_irqs; - unsigned int irqs[]; + unsigned int irqs[] __counted_by(num_irqs); }; static irqreturn_t axp20x_ac_power_irq(int irq, void *devid) diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index bde17406c1..e23308ad4c 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -73,7 +73,7 @@ struct axp20x_usb_power { unsigned int old_status; unsigned int online; unsigned int num_irqs; - unsigned int irqs[]; + unsigned int irqs[] __counted_by(num_irqs); }; static bool axp20x_usb_vbus_needs_polling(struct axp20x_usb_power *power) diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index 3f99cb9590..1db290ee25 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -1803,7 +1803,7 @@ static int bq24190_probe(struct i2c_client *client) bdi->client = client; bdi->dev = dev; - strncpy(bdi->model_name, id->name, I2C_NAME_SIZE); + strscpy(bdi->model_name, id->name, sizeof(bdi->model_name)); mutex_init(&bdi->f_reg_lock); bdi->charge_type = POWER_SUPPLY_CHARGE_TYPE_FAST; bdi->f_reg = 0; diff --git a/drivers/power/supply/bq24257_charger.c b/drivers/power/supply/bq24257_charger.c index 2852860abf..801d0d2c5f 100644 --- a/drivers/power/supply/bq24257_charger.c +++ b/drivers/power/supply/bq24257_charger.c @@ -35,20 +35,15 @@ #define BQ24257_ILIM_SET_DELAY 1000 /* msec */ -/* - * When adding support for new devices make sure that enum bq2425x_chip and - * bq2425x_chip_name[] always stay in sync! - */ enum bq2425x_chip { BQ24250, BQ24251, BQ24257, }; -static const char *const bq2425x_chip_name[] = { - "bq24250", - "bq24251", - "bq24257", +struct bq2425x_chip_info { + const char *const name; + enum bq2425x_chip chip; }; enum bq24257_fields { @@ -84,7 +79,7 @@ struct bq24257_device { struct device *dev; struct power_supply *charger; - enum bq2425x_chip chip; + const struct bq2425x_chip_info *info; struct regmap *rmap; struct regmap_field *rmap_fields[F_MAX_FIELDS]; @@ -329,7 +324,7 @@ static int bq24257_power_supply_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_MODEL_NAME: - val->strval = bq2425x_chip_name[bq->chip]; + val->strval = bq->info->name; break; case POWER_SUPPLY_PROP_ONLINE: @@ -947,10 +942,8 @@ static int bq24257_fw_probe(struct bq24257_device *bq) static int bq24257_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); struct i2c_adapter *adapter = client->adapter; struct device *dev = &client->dev; - const struct acpi_device_id *acpi_id; struct bq24257_device *bq; int ret; int i; @@ -967,17 +960,9 @@ static int bq24257_probe(struct i2c_client *client) bq->client = client; bq->dev = dev; - if (ACPI_HANDLE(dev)) { - acpi_id = acpi_match_device(dev->driver->acpi_match_table, - &client->dev); - if (!acpi_id) { - dev_err(dev, "Failed to match ACPI device\n"); - return -ENODEV; - } - bq->chip = (enum bq2425x_chip)acpi_id->driver_data; - } else { - bq->chip = (enum bq2425x_chip)id->driver_data; - } + bq->info = i2c_get_match_data(client); + if (!bq->info) + return dev_err_probe(dev, -ENODEV, "Failed to match device\n"); mutex_init(&bq->lock); @@ -1015,7 +1000,7 @@ static int bq24257_probe(struct i2c_client *client) * used for the automatic setting of the input current limit setting so * explicitly disable that feature. */ - if (bq->chip == BQ24250) + if (bq->info->chip == BQ24250) bq->iilimit_autoset_enable = false; if (bq->iilimit_autoset_enable) @@ -1028,7 +1013,7 @@ static int bq24257_probe(struct i2c_client *client) * the PG state. We also use a SW-based approach for all other devices * if the PG pin is either not defined or can't be probed. */ - if (bq->chip != BQ24250) + if (bq->info->chip != BQ24250) bq24257_pg_gpio_probe(bq); if (PTR_ERR(bq->pg) == -EPROBE_DEFER) @@ -1066,7 +1051,7 @@ static int bq24257_probe(struct i2c_client *client) bq24257_irq_handler_thread, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, - bq2425x_chip_name[bq->chip], bq); + bq->info->name, bq); if (ret) { dev_err(dev, "Failed to request IRQ #%d\n", client->irq); return ret; @@ -1132,28 +1117,43 @@ static const struct dev_pm_ops bq24257_pm = { SET_SYSTEM_SLEEP_PM_OPS(bq24257_suspend, bq24257_resume) }; +static const struct bq2425x_chip_info bq24250_info = { + .name = "bq24250", + .chip = BQ24250, +}; + +static const struct bq2425x_chip_info bq24251_info = { + .name = "bq24251", + .chip = BQ24251, +}; + +static const struct bq2425x_chip_info bq24257_info = { + .name = "bq24257", + .chip = BQ24257, +}; + static const struct i2c_device_id bq24257_i2c_ids[] = { - { "bq24250", BQ24250 }, - { "bq24251", BQ24251 }, - { "bq24257", BQ24257 }, - {}, + { "bq24250", (kernel_ulong_t)&bq24250_info }, + { "bq24251", (kernel_ulong_t)&bq24251_info }, + { "bq24257", (kernel_ulong_t)&bq24257_info }, + {} }; MODULE_DEVICE_TABLE(i2c, bq24257_i2c_ids); static const struct of_device_id bq24257_of_match[] __maybe_unused = { - { .compatible = "ti,bq24250", }, - { .compatible = "ti,bq24251", }, - { .compatible = "ti,bq24257", }, - { }, + { .compatible = "ti,bq24250", &bq24250_info }, + { .compatible = "ti,bq24251", &bq24251_info }, + { .compatible = "ti,bq24257", &bq24257_info }, + {} }; MODULE_DEVICE_TABLE(of, bq24257_of_match); #ifdef CONFIG_ACPI static const struct acpi_device_id bq24257_acpi_match[] = { - { "BQ242500", BQ24250 }, - { "BQ242510", BQ24251 }, - { "BQ242570", BQ24257 }, - {}, + { "BQ242500", (kernel_ulong_t)&bq24250_info }, + { "BQ242510", (kernel_ulong_t)&bq24251_info }, + { "BQ242570", (kernel_ulong_t)&bq24257_info }, + {} }; MODULE_DEVICE_TABLE(acpi, bq24257_acpi_match); #endif diff --git a/drivers/power/supply/bq2515x_charger.c b/drivers/power/supply/bq2515x_charger.c index 1dbacc9b01..a3424f67f2 100644 --- a/drivers/power/supply/bq2515x_charger.c +++ b/drivers/power/supply/bq2515x_charger.c @@ -147,9 +147,14 @@ struct bq2515x_init_data { int iprechg; }; -enum bq2515x_id { - BQ25150, - BQ25155, +/** + * struct bq2515x_info - + * @regmap_config: register map config + * @ilim: input current limit + */ +struct bq2515x_info { + const struct regmap_config *regmap_config; + int ilim; }; /** @@ -164,8 +169,8 @@ enum bq2515x_id { * @ac_detect_gpio: power good (PG) pin * @ce_gpio: charge enable (CE) pin * + * @info: device info * @model_name: string value describing device model - * @device_id: value of device_id * @mains_online: boolean value indicating power supply online * * @init_data: charger initialization data structure @@ -181,8 +186,8 @@ struct bq2515x_device { struct gpio_desc *ac_detect_gpio; struct gpio_desc *ce_gpio; + const struct bq2515x_info *info; char model_name[I2C_NAME_SIZE]; - int device_id; bool mains_online; struct bq2515x_init_data init_data; @@ -998,16 +1003,8 @@ static int bq2515x_read_properties(struct bq2515x_device *bq2515x) ret = device_property_read_u32(bq2515x->dev, "input-current-limit-microamp", &bq2515x->init_data.ilim); - if (ret) { - switch (bq2515x->device_id) { - case BQ25150: - bq2515x->init_data.ilim = BQ25150_DEFAULT_ILIM_UA; - break; - case BQ25155: - bq2515x->init_data.ilim = BQ25155_DEFAULT_ILIM_UA; - break; - } - } + if (ret) + bq2515x->init_data.ilim = bq2515x->info->ilim; bq2515x->ac_detect_gpio = devm_gpiod_get_optional(bq2515x->dev, "ac-detect", GPIOD_IN); @@ -1092,21 +1089,11 @@ static int bq2515x_probe(struct i2c_client *client) bq2515x->dev = dev; - strncpy(bq2515x->model_name, id->name, I2C_NAME_SIZE); - - bq2515x->device_id = id->driver_data; - - switch (bq2515x->device_id) { - case BQ25150: - bq2515x->regmap = devm_regmap_init_i2c(client, - &bq25150_regmap_config); - break; - case BQ25155: - bq2515x->regmap = devm_regmap_init_i2c(client, - &bq25155_regmap_config); - break; - } + strscpy(bq2515x->model_name, id->name, sizeof(bq2515x->model_name)); + bq2515x->info = i2c_get_match_data(client); + bq2515x->regmap = devm_regmap_init_i2c(client, + bq2515x->info->regmap_config); if (IS_ERR(bq2515x->regmap)) { dev_err(dev, "failed to allocate register map\n"); return PTR_ERR(bq2515x->regmap); @@ -1139,17 +1126,27 @@ static int bq2515x_probe(struct i2c_client *client) return 0; } +static const struct bq2515x_info bq25150 = { + .regmap_config = &bq25150_regmap_config, + .ilim = BQ25150_DEFAULT_ILIM_UA, +}; + +static const struct bq2515x_info bq25155 = { + .regmap_config = &bq25155_regmap_config, + .ilim = BQ25155_DEFAULT_ILIM_UA, +}; + static const struct i2c_device_id bq2515x_i2c_ids[] = { - { "bq25150", BQ25150, }, - { "bq25155", BQ25155, }, - {}, + { "bq25150", (kernel_ulong_t)&bq25150 }, + { "bq25155", (kernel_ulong_t)&bq25155 }, + {} }; MODULE_DEVICE_TABLE(i2c, bq2515x_i2c_ids); static const struct of_device_id bq2515x_of_match[] = { - { .compatible = "ti,bq25150", }, - { .compatible = "ti,bq25155", }, - { }, + { .compatible = "ti,bq25150", .data = &bq25150 }, + { .compatible = "ti,bq25155", .data = &bq25155 }, + {} }; MODULE_DEVICE_TABLE(of, bq2515x_of_match); diff --git a/drivers/power/supply/bq256xx_charger.c b/drivers/power/supply/bq256xx_charger.c index c8368dae69..1a935bc885 100644 --- a/drivers/power/supply/bq256xx_charger.c +++ b/drivers/power/supply/bq256xx_charger.c @@ -1705,11 +1705,11 @@ static int bq256xx_probe(struct i2c_client *client) bq->client = client; bq->dev = dev; - bq->chip_info = &bq256xx_chip_info_tbl[id->driver_data]; + bq->chip_info = i2c_get_match_data(client); mutex_init(&bq->lock); - strncpy(bq->model_name, id->name, I2C_NAME_SIZE); + strscpy(bq->model_name, id->name, sizeof(bq->model_name)); bq->regmap = devm_regmap_init_i2c(client, bq->chip_info->bq256xx_regmap_config); @@ -1774,38 +1774,38 @@ static int bq256xx_probe(struct i2c_client *client) } static const struct i2c_device_id bq256xx_i2c_ids[] = { - { "bq25600", BQ25600 }, - { "bq25600d", BQ25600D }, - { "bq25601", BQ25601 }, - { "bq25601d", BQ25601D }, - { "bq25611d", BQ25611D }, - { "bq25618", BQ25618 }, - { "bq25619", BQ25619 }, - {}, + { "bq25600", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25600] }, + { "bq25600d", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25600D] }, + { "bq25601", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25601] }, + { "bq25601d", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25601D] }, + { "bq25611d", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25611D] }, + { "bq25618", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25618] }, + { "bq25619", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25619] }, + {} }; MODULE_DEVICE_TABLE(i2c, bq256xx_i2c_ids); static const struct of_device_id bq256xx_of_match[] = { - { .compatible = "ti,bq25600", .data = (void *)BQ25600 }, - { .compatible = "ti,bq25600d", .data = (void *)BQ25600D }, - { .compatible = "ti,bq25601", .data = (void *)BQ25601 }, - { .compatible = "ti,bq25601d", .data = (void *)BQ25601D }, - { .compatible = "ti,bq25611d", .data = (void *)BQ25611D }, - { .compatible = "ti,bq25618", .data = (void *)BQ25618 }, - { .compatible = "ti,bq25619", .data = (void *)BQ25619 }, - { }, + { .compatible = "ti,bq25600", .data = &bq256xx_chip_info_tbl[BQ25600] }, + { .compatible = "ti,bq25600d", .data = &bq256xx_chip_info_tbl[BQ25600D] }, + { .compatible = "ti,bq25601", .data = &bq256xx_chip_info_tbl[BQ25601] }, + { .compatible = "ti,bq25601d", .data = &bq256xx_chip_info_tbl[BQ25601D] }, + { .compatible = "ti,bq25611d", .data = &bq256xx_chip_info_tbl[BQ25611D] }, + { .compatible = "ti,bq25618", .data = &bq256xx_chip_info_tbl[BQ25618] }, + { .compatible = "ti,bq25619", .data = &bq256xx_chip_info_tbl[BQ25619] }, + {} }; MODULE_DEVICE_TABLE(of, bq256xx_of_match); static const struct acpi_device_id bq256xx_acpi_match[] = { - { "bq25600", BQ25600 }, - { "bq25600d", BQ25600D }, - { "bq25601", BQ25601 }, - { "bq25601d", BQ25601D }, - { "bq25611d", BQ25611D }, - { "bq25618", BQ25618 }, - { "bq25619", BQ25619 }, - {}, + { "bq25600", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25600] }, + { "bq25600d", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25600D] }, + { "bq25601", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25601] }, + { "bq25601d", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25601D] }, + { "bq25611d", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25611D] }, + { "bq25618", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25618] }, + { "bq25619", (kernel_ulong_t)&bq256xx_chip_info_tbl[BQ25619] }, + {} }; MODULE_DEVICE_TABLE(acpi, bq256xx_acpi_match); diff --git a/drivers/power/supply/bq25980_charger.c b/drivers/power/supply/bq25980_charger.c index d841172226..0c5e2938bb 100644 --- a/drivers/power/supply/bq25980_charger.c +++ b/drivers/power/supply/bq25980_charger.c @@ -1223,7 +1223,7 @@ static int bq25980_probe(struct i2c_client *client) mutex_init(&bq->lock); - strncpy(bq->model_name, id->name, I2C_NAME_SIZE); + strscpy(bq->model_name, id->name, sizeof(bq->model_name)); bq->chip_info = &bq25980_chip_info_tbl[id->driver_data]; bq->regmap = devm_regmap_init_i2c(client, diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index 5fa6ba7f41..96f0a7fbf1 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -1516,9 +1516,11 @@ static int charger_manager_probe(struct platform_device *pdev) memcpy(&cm->charger_psy_desc, &psy_default, sizeof(psy_default)); if (!desc->psy_name) - strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX); + strscpy(cm->psy_name_buf, psy_default.name, + sizeof(cm->psy_name_buf)); else - strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX); + strscpy(cm->psy_name_buf, desc->psy_name, + sizeof(cm->psy_name_buf)); cm->charger_psy_desc.name = cm->psy_name_buf; /* Allocate for psy properties because they may vary */ @@ -1628,7 +1630,7 @@ err_reg_extcon: return ret; } -static int charger_manager_remove(struct platform_device *pdev) +static void charger_manager_remove(struct platform_device *pdev) { struct charger_manager *cm = platform_get_drvdata(pdev); struct charger_desc *desc = cm->desc; @@ -1648,8 +1650,6 @@ static int charger_manager_remove(struct platform_device *pdev) power_supply_unregister(cm->charger_psy); try_charger_enable(cm, false); - - return 0; } static const struct platform_device_id charger_manager_id[] = { @@ -1740,7 +1740,7 @@ static struct platform_driver charger_manager_driver = { .of_match_table = charger_manager_match, }, .probe = charger_manager_probe, - .remove = charger_manager_remove, + .remove_new = charger_manager_remove, .id_table = charger_manager_id, }; diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 5dd76c0ac9..30ec76cdf3 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1151,7 +1151,7 @@ static int cpcap_battery_probe(struct platform_device *pdev) return 0; } -static int cpcap_battery_remove(struct platform_device *pdev) +static void cpcap_battery_remove(struct platform_device *pdev) { struct cpcap_battery_ddata *ddata = platform_get_drvdata(pdev); int error; @@ -1161,8 +1161,6 @@ static int cpcap_battery_remove(struct platform_device *pdev) 0xffff, 0); if (error) dev_err(&pdev->dev, "could not disable: %i\n", error); - - return 0; } static struct platform_driver cpcap_battery_driver = { @@ -1171,7 +1169,7 @@ static struct platform_driver cpcap_battery_driver = { .of_match_table = of_match_ptr(cpcap_battery_id_table), }, .probe = cpcap_battery_probe, - .remove = cpcap_battery_remove, + .remove_new = cpcap_battery_remove, }; module_platform_driver(cpcap_battery_driver); diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c index be9764541d..cebca34ff8 100644 --- a/drivers/power/supply/cpcap-charger.c +++ b/drivers/power/supply/cpcap-charger.c @@ -17,8 +17,7 @@ #include <linux/err.h> #include <linux/interrupt.h> #include <linux/notifier.h> -#include <linux/of.h> -#include <linux/of_platform.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/regmap.h> @@ -865,7 +864,6 @@ static const struct power_supply_desc cpcap_charger_usb_desc = { .property_is_writeable = cpcap_charger_property_is_writeable, }; -#ifdef CONFIG_OF static const struct of_device_id cpcap_charger_id_table[] = { { .compatible = "motorola,mapphone-cpcap-charger", @@ -873,20 +871,13 @@ static const struct of_device_id cpcap_charger_id_table[] = { {}, }; MODULE_DEVICE_TABLE(of, cpcap_charger_id_table); -#endif static int cpcap_charger_probe(struct platform_device *pdev) { struct cpcap_charger_ddata *ddata; - const struct of_device_id *of_id; struct power_supply_config psy_cfg = {}; int error; - of_id = of_match_device(of_match_ptr(cpcap_charger_id_table), - &pdev->dev); - if (!of_id) - return -EINVAL; - ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); if (!ddata) return -ENOMEM; @@ -966,21 +957,19 @@ static void cpcap_charger_shutdown(struct platform_device *pdev) cancel_delayed_work_sync(&ddata->detect_work); } -static int cpcap_charger_remove(struct platform_device *pdev) +static void cpcap_charger_remove(struct platform_device *pdev) { cpcap_charger_shutdown(pdev); - - return 0; } static struct platform_driver cpcap_charger_driver = { .probe = cpcap_charger_probe, .driver = { .name = "cpcap-charger", - .of_match_table = of_match_ptr(cpcap_charger_id_table), + .of_match_table = cpcap_charger_id_table, }, .shutdown = cpcap_charger_shutdown, - .remove = cpcap_charger_remove, + .remove_new = cpcap_charger_remove, }; module_platform_driver(cpcap_charger_driver); diff --git a/drivers/power/supply/da9030_battery.c b/drivers/power/supply/da9030_battery.c index 0deba48d22..581cf956d2 100644 --- a/drivers/power/supply/da9030_battery.c +++ b/drivers/power/supply/da9030_battery.c @@ -552,7 +552,7 @@ err_charger_init: return ret; } -static int da9030_battery_remove(struct platform_device *dev) +static void da9030_battery_remove(struct platform_device *dev) { struct da9030_charger *charger = platform_get_drvdata(dev); @@ -564,8 +564,6 @@ static int da9030_battery_remove(struct platform_device *dev) cancel_delayed_work_sync(&charger->work); da9030_set_charge(charger, 0); power_supply_unregister(charger->psy); - - return 0; } static struct platform_driver da903x_battery_driver = { @@ -573,7 +571,7 @@ static struct platform_driver da903x_battery_driver = { .name = "da903x-battery", }, .probe = da9030_battery_probe, - .remove = da9030_battery_remove, + .remove_new = da9030_battery_remove, }; module_platform_driver(da903x_battery_driver); diff --git a/drivers/power/supply/da9052-battery.c b/drivers/power/supply/da9052-battery.c index d87bdecc95..6f7c58a41e 100644 --- a/drivers/power/supply/da9052-battery.c +++ b/drivers/power/supply/da9052-battery.c @@ -637,7 +637,7 @@ err: return ret; } -static int da9052_bat_remove(struct platform_device *pdev) +static void da9052_bat_remove(struct platform_device *pdev) { int i; struct da9052_battery *bat = platform_get_drvdata(pdev); @@ -646,13 +646,11 @@ static int da9052_bat_remove(struct platform_device *pdev) da9052_free_irq(bat->da9052, da9052_bat_irq_bits[i], bat); power_supply_unregister(bat->psy); - - return 0; } static struct platform_driver da9052_bat_driver = { .probe = da9052_bat_probe, - .remove = da9052_bat_remove, + .remove_new = da9052_bat_remove, .driver = { .name = "da9052-bat", }, diff --git a/drivers/power/supply/da9150-charger.c b/drivers/power/supply/da9150-charger.c index 27f897067a..37db9e4ed7 100644 --- a/drivers/power/supply/da9150-charger.c +++ b/drivers/power/supply/da9150-charger.c @@ -635,7 +635,7 @@ ibus_chan_fail: return ret; } -static int da9150_charger_remove(struct platform_device *pdev) +static void da9150_charger_remove(struct platform_device *pdev) { struct da9150_charger *charger = platform_get_drvdata(pdev); int irq; @@ -665,8 +665,6 @@ static int da9150_charger_remove(struct platform_device *pdev) iio_channel_release(charger->vbus_chan); iio_channel_release(charger->tjunc_chan); iio_channel_release(charger->vbat_chan); - - return 0; } static struct platform_driver da9150_charger_driver = { @@ -674,7 +672,7 @@ static struct platform_driver da9150_charger_driver = { .name = "da9150-charger", }, .probe = da9150_charger_probe, - .remove = da9150_charger_remove, + .remove_new = da9150_charger_remove, }; module_platform_driver(da9150_charger_driver); diff --git a/drivers/power/supply/goldfish_battery.c b/drivers/power/supply/goldfish_battery.c index a58d713d75..8bb645ad1e 100644 --- a/drivers/power/supply/goldfish_battery.c +++ b/drivers/power/supply/goldfish_battery.c @@ -249,13 +249,12 @@ static int goldfish_battery_probe(struct platform_device *pdev) return 0; } -static int goldfish_battery_remove(struct platform_device *pdev) +static void goldfish_battery_remove(struct platform_device *pdev) { struct goldfish_battery_data *data = platform_get_drvdata(pdev); power_supply_unregister(data->battery); power_supply_unregister(data->ac); - return 0; } static const struct of_device_id goldfish_battery_of_match[] = { @@ -274,7 +273,7 @@ MODULE_DEVICE_TABLE(acpi, goldfish_battery_acpi_match); static struct platform_driver goldfish_battery_device = { .probe = goldfish_battery_probe, - .remove = goldfish_battery_remove, + .remove_new = goldfish_battery_remove, .driver = { .name = "goldfish-battery", .of_match_table = goldfish_battery_of_match, diff --git a/drivers/power/supply/ipaq_micro_battery.c b/drivers/power/supply/ipaq_micro_battery.c index 192d9db0fb..66cc649f70 100644 --- a/drivers/power/supply/ipaq_micro_battery.c +++ b/drivers/power/supply/ipaq_micro_battery.c @@ -265,7 +265,7 @@ batt_err: return ret; } -static int micro_batt_remove(struct platform_device *pdev) +static void micro_batt_remove(struct platform_device *pdev) { struct micro_battery *mb = platform_get_drvdata(pdev); @@ -274,8 +274,6 @@ static int micro_batt_remove(struct platform_device *pdev) power_supply_unregister(micro_batt_power); cancel_delayed_work_sync(&mb->update); destroy_workqueue(mb->wq); - - return 0; } static int __maybe_unused micro_batt_suspend(struct device *dev) @@ -304,7 +302,7 @@ static struct platform_driver micro_batt_device_driver = { .pm = µ_batt_dev_pm_ops, }, .probe = micro_batt_probe, - .remove = micro_batt_remove, + .remove_new = micro_batt_remove, }; module_platform_driver(micro_batt_device_driver); diff --git a/drivers/power/supply/isp1704_charger.c b/drivers/power/supply/isp1704_charger.c index b6efc454e4..860d8614c9 100644 --- a/drivers/power/supply/isp1704_charger.c +++ b/drivers/power/supply/isp1704_charger.c @@ -477,15 +477,13 @@ fail0: return ret; } -static int isp1704_charger_remove(struct platform_device *pdev) +static void isp1704_charger_remove(struct platform_device *pdev) { struct isp1704_charger *isp = platform_get_drvdata(pdev); usb_unregister_notifier(isp->phy, &isp->nb); power_supply_unregister(isp->psy); isp1704_charger_set_power(isp, 0); - - return 0; } #ifdef CONFIG_OF @@ -503,7 +501,7 @@ static struct platform_driver isp1704_charger_driver = { .of_match_table = of_match_ptr(omap_isp1704_of_match), }, .probe = isp1704_charger_probe, - .remove = isp1704_charger_remove, + .remove_new = isp1704_charger_remove, }; module_platform_driver(isp1704_charger_driver); diff --git a/drivers/power/supply/lp8788-charger.c b/drivers/power/supply/lp8788-charger.c index 755b6a4379..2c81be82a4 100644 --- a/drivers/power/supply/lp8788-charger.c +++ b/drivers/power/supply/lp8788-charger.c @@ -714,20 +714,18 @@ static int lp8788_charger_probe(struct platform_device *pdev) return 0; } -static int lp8788_charger_remove(struct platform_device *pdev) +static void lp8788_charger_remove(struct platform_device *pdev) { struct lp8788_charger *pchg = platform_get_drvdata(pdev); flush_work(&pchg->charger_work); lp8788_irq_unregister(pdev, pchg); lp8788_psy_unregister(pchg); - - return 0; } static struct platform_driver lp8788_charger_driver = { .probe = lp8788_charger_probe, - .remove = lp8788_charger_remove, + .remove_new = lp8788_charger_remove, .driver = { .name = LP8788_DEV_CHARGER, }, diff --git a/drivers/power/supply/max14577_charger.c b/drivers/power/supply/max14577_charger.c index 96f9de7750..7c23fa89ea 100644 --- a/drivers/power/supply/max14577_charger.c +++ b/drivers/power/supply/max14577_charger.c @@ -606,14 +606,12 @@ err: return ret; } -static int max14577_charger_remove(struct platform_device *pdev) +static void max14577_charger_remove(struct platform_device *pdev) { struct max14577_charger *chg = platform_get_drvdata(pdev); device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer); power_supply_unregister(chg->charger); - - return 0; } static const struct platform_device_id max14577_charger_id[] = { @@ -638,7 +636,7 @@ static struct platform_driver max14577_charger_driver = { .of_match_table = of_max14577_charger_dt_match, }, .probe = max14577_charger_probe, - .remove = max14577_charger_remove, + .remove_new = max14577_charger_remove, .id_table = max14577_charger_id, }; module_platform_driver(max14577_charger_driver); diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index ff42db6728..51310f6e48 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -18,6 +18,7 @@ #include <linux/of.h> #include <linux/regmap.h> #include <linux/slab.h> +#include <linux/iio/consumer.h> #define MAX17040_VCELL 0x02 #define MAX17040_SOC 0x04 @@ -142,6 +143,7 @@ struct max17040_chip { struct delayed_work work; struct power_supply *battery; struct chip_data data; + struct iio_channel *channel_temp; /* battery capacity */ int soc; @@ -389,6 +391,7 @@ static int max17040_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_ONLINE: + case POWER_SUPPLY_PROP_PRESENT: val->intval = max17040_get_online(chip); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: @@ -400,6 +403,16 @@ static int max17040_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: val->intval = chip->low_soc_alert; break; + case POWER_SUPPLY_PROP_STATUS: + power_supply_get_property_from_supplier(psy, psp, val); + break; + case POWER_SUPPLY_PROP_TEMP: + if (!chip->channel_temp) + return -ENODATA; + + iio_read_channel_processed_scale(chip->channel_temp, + &val->intval, 10); + break; default: return -EINVAL; } @@ -415,9 +428,12 @@ static const struct regmap_config max17040_regmap = { static enum power_supply_property max17040_battery_props[] = { POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_TEMP, }; static const struct power_supply_desc max17040_battery_desc = { @@ -463,6 +479,17 @@ static int max17040_probe(struct i2c_client *client) i2c_set_clientdata(client, chip); psy_cfg.drv_data = chip; + /* Switch to devm_iio_channel_get_optional when available */ + chip->channel_temp = devm_iio_channel_get(&client->dev, "temp"); + if (IS_ERR(chip->channel_temp)) { + ret = PTR_ERR(chip->channel_temp); + if (ret != -ENODEV) + return dev_err_probe(&client->dev, PTR_ERR(chip->channel_temp), + "failed to get temp\n"); + else + chip->channel_temp = NULL; + } + chip->battery = devm_power_supply_register(&client->dev, &max17040_battery_desc, &psy_cfg); if (IS_ERR(chip->battery)) { diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 17ac2ab78c..e7d37e422c 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -36,7 +36,7 @@ #define STATUS_BR_BIT (1 << 15) /* Interrupt mask bits */ -#define CONFIG_ALRT_BIT_ENBL (1 << 2) +#define CFG_ALRT_BIT_ENBL (1 << 2) #define VFSOC0_LOCK 0x0000 #define VFSOC0_UNLOCK 0x0080 @@ -1116,8 +1116,8 @@ static int max17042_probe(struct i2c_client *client) chip); if (!ret) { regmap_update_bits(chip->regmap, MAX17042_CONFIG, - CONFIG_ALRT_BIT_ENBL, - CONFIG_ALRT_BIT_ENBL); + CFG_ALRT_BIT_ENBL, + CFG_ALRT_BIT_ENBL); max17042_set_soc_threshold(chip, 1); } else { client->irq = 0; diff --git a/drivers/power/supply/max77650-charger.c b/drivers/power/supply/max77650-charger.c index e8c25da40a..818e13c613 100644 --- a/drivers/power/supply/max77650-charger.c +++ b/drivers/power/supply/max77650-charger.c @@ -345,13 +345,11 @@ static int max77650_charger_probe(struct platform_device *pdev) return max77650_charger_enable(chg); } -static int max77650_charger_remove(struct platform_device *pdev) +static void max77650_charger_remove(struct platform_device *pdev) { struct max77650_charger_data *chg = platform_get_drvdata(pdev); max77650_charger_disable(chg); - - return 0; } static const struct of_device_id max77650_charger_of_match[] = { @@ -366,7 +364,7 @@ static struct platform_driver max77650_charger_driver = { .of_match_table = max77650_charger_of_match, }, .probe = max77650_charger_probe, - .remove = max77650_charger_remove, + .remove_new = max77650_charger_remove, }; module_platform_driver(max77650_charger_driver); diff --git a/drivers/power/supply/max77693_charger.c b/drivers/power/supply/max77693_charger.c index 794c8c0544..d0157e63b8 100644 --- a/drivers/power/supply/max77693_charger.c +++ b/drivers/power/supply/max77693_charger.c @@ -728,7 +728,7 @@ err: return ret; } -static int max77693_charger_remove(struct platform_device *pdev) +static void max77693_charger_remove(struct platform_device *pdev) { struct max77693_charger *chg = platform_get_drvdata(pdev); @@ -737,8 +737,6 @@ static int max77693_charger_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer); power_supply_unregister(chg->charger); - - return 0; } static const struct platform_device_id max77693_charger_id[] = { @@ -752,7 +750,7 @@ static struct platform_driver max77693_charger_driver = { .name = "max77693-charger", }, .probe = max77693_charger_probe, - .remove = max77693_charger_remove, + .remove_new = max77693_charger_remove, .id_table = max77693_charger_id, }; module_platform_driver(max77693_charger_driver); diff --git a/drivers/power/supply/max8925_power.c b/drivers/power/supply/max8925_power.c index 8878f91311..4a2d6894f9 100644 --- a/drivers/power/supply/max8925_power.c +++ b/drivers/power/supply/max8925_power.c @@ -566,7 +566,7 @@ out: return ret; } -static int max8925_power_remove(struct platform_device *pdev) +static void max8925_power_remove(struct platform_device *pdev) { struct max8925_power_info *info = platform_get_drvdata(pdev); @@ -576,12 +576,11 @@ static int max8925_power_remove(struct platform_device *pdev) power_supply_unregister(info->battery); max8925_deinit_charger(info); } - return 0; } static struct platform_driver max8925_power_driver = { .probe = max8925_power_probe, - .remove = max8925_power_remove, + .remove_new = max8925_power_remove, .driver = { .name = "max8925-power", }, diff --git a/drivers/power/supply/mm8013.c b/drivers/power/supply/mm8013.c new file mode 100644 index 0000000000..caa272b035 --- /dev/null +++ b/drivers/power/supply/mm8013.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2023, Linaro Limited + */ +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/power_supply.h> +#include <linux/regmap.h> + +#define REG_BATID 0x00 /* This one is very unclear */ + #define BATID_101 0x0101 /* 107kOhm */ + #define BATID_102 0x0102 /* 10kOhm */ +#define REG_TEMPERATURE 0x06 +#define REG_VOLTAGE 0x08 +#define REG_FLAGS 0x0a + #define MM8013_FLAG_OTC BIT(15) + #define MM8013_FLAG_OTD BIT(14) + #define MM8013_FLAG_BATHI BIT(13) + #define MM8013_FLAG_BATLOW BIT(12) + #define MM8013_FLAG_CHG_INH BIT(11) + #define MM8013_FLAG_FC BIT(9) + #define MM8013_FLAG_CHG BIT(8) + #define MM8013_FLAG_OCC BIT(6) + #define MM8013_FLAG_ODC BIT(5) + #define MM8013_FLAG_OT BIT(4) + #define MM8013_FLAG_UT BIT(3) + #define MM8013_FLAG_DSG BIT(0) +#define REG_FULL_CHARGE_CAPACITY 0x0e +#define REG_NOMINAL_CHARGE_CAPACITY 0x0c +#define REG_AVERAGE_CURRENT 0x14 +#define REG_AVERAGE_TIME_TO_EMPTY 0x16 +#define REG_AVERAGE_TIME_TO_FULL 0x18 +#define REG_MAX_LOAD_CURRENT 0x1e +#define REG_CYCLE_COUNT 0x2a +#define REG_STATE_OF_CHARGE 0x2c +#define REG_DESIGN_CAPACITY 0x3c +/* TODO: 0x62-0x68 seem to contain 'MM8013C' in a length-prefixed, non-terminated string */ + +#define DECIKELVIN_TO_DECIDEGC(t) (t - 2731) + +struct mm8013_chip { + struct i2c_client *client; + struct regmap *regmap; +}; + +static int mm8013_checkdevice(struct mm8013_chip *chip) +{ + int battery_id, ret; + u32 val; + + ret = regmap_write(chip->regmap, REG_BATID, 0x0008); + if (ret < 0) + return ret; + + ret = regmap_read(chip->regmap, REG_BATID, &val); + if (ret < 0) + return ret; + + if (val == BATID_102) + battery_id = 2; + else if (val == BATID_101) + battery_id = 1; + else + return -EINVAL; + + dev_dbg(&chip->client->dev, "battery_id: %d\n", battery_id); + + return 0; +} + +static enum power_supply_property mm8013_battery_props[] = { + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, + POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; + +static int mm8013_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mm8013_chip *chip = psy->drv_data; + int ret = 0; + u32 regval; + + switch (psp) { + case POWER_SUPPLY_PROP_CAPACITY: + ret = regmap_read(chip->regmap, REG_STATE_OF_CHARGE, ®val); + if (ret < 0) + return ret; + + val->intval = regval; + break; + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + ret = regmap_read(chip->regmap, REG_FLAGS, ®val); + if (ret < 0) + return ret; + + if (regval & MM8013_FLAG_CHG_INH) + val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE; + else + val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + ret = regmap_read(chip->regmap, REG_FULL_CHARGE_CAPACITY, ®val); + if (ret < 0) + return ret; + + val->intval = 1000 * regval; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + ret = regmap_read(chip->regmap, REG_DESIGN_CAPACITY, ®val); + if (ret < 0) + return ret; + + val->intval = 1000 * regval; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + ret = regmap_read(chip->regmap, REG_NOMINAL_CHARGE_CAPACITY, ®val); + if (ret < 0) + return ret; + + val->intval = 1000 * regval; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + ret = regmap_read(chip->regmap, REG_MAX_LOAD_CURRENT, ®val); + if (ret < 0) + return ret; + + val->intval = -1000 * (s16)regval; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = regmap_read(chip->regmap, REG_AVERAGE_CURRENT, ®val); + if (ret < 0) + return ret; + + val->intval = -1000 * (s16)regval; + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + ret = regmap_read(chip->regmap, REG_CYCLE_COUNT, ®val); + if (ret < 0) + return ret; + + val->intval = regval; + break; + case POWER_SUPPLY_PROP_HEALTH: + ret = regmap_read(chip->regmap, REG_FLAGS, ®val); + if (ret < 0) + return ret; + + if (regval & MM8013_FLAG_UT) + val->intval = POWER_SUPPLY_HEALTH_COLD; + else if (regval & (MM8013_FLAG_ODC | MM8013_FLAG_OCC)) + val->intval = POWER_SUPPLY_HEALTH_OVERCURRENT; + else if (regval & (MM8013_FLAG_BATLOW)) + val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + else if (regval & MM8013_FLAG_BATHI) + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + else if (regval & (MM8013_FLAG_OT | MM8013_FLAG_OTD | MM8013_FLAG_OTC)) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + else + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_PRESENT: + ret = regmap_read(chip->regmap, REG_TEMPERATURE, ®val); + if (ret < 0) + return ret; + + val->intval = ((s16)regval > 0); + break; + case POWER_SUPPLY_PROP_STATUS: + ret = regmap_read(chip->regmap, REG_FLAGS, ®val); + if (ret < 0) + return ret; + + if (regval & MM8013_FLAG_DSG) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else if (regval & MM8013_FLAG_CHG) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (regval & MM8013_FLAG_FC) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + break; + case POWER_SUPPLY_PROP_TEMP: + ret = regmap_read(chip->regmap, REG_TEMPERATURE, ®val); + if (ret < 0) + return ret; + + val->intval = DECIKELVIN_TO_DECIDEGC(regval); + break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: + ret = regmap_read(chip->regmap, REG_AVERAGE_TIME_TO_EMPTY, ®val); + if (ret < 0) + return ret; + + /* The estimation is not yet ready */ + if (regval == U16_MAX) + return -ENODATA; + + val->intval = regval; + break; + case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: + ret = regmap_read(chip->regmap, REG_AVERAGE_TIME_TO_FULL, ®val); + if (ret < 0) + return ret; + + /* The estimation is not yet ready */ + if (regval == U16_MAX) + return -ENODATA; + + val->intval = regval; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = regmap_read(chip->regmap, REG_VOLTAGE, ®val); + if (ret < 0) + return ret; + + val->intval = 1000 * regval; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct power_supply_desc mm8013_desc = { + .name = "mm8013", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = mm8013_battery_props, + .num_properties = ARRAY_SIZE(mm8013_battery_props), + .get_property = mm8013_get_property, +}; + +static const struct regmap_config mm8013_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = 0x68, + .use_single_read = true, + .use_single_write = true, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; + +static int mm8013_probe(struct i2c_client *client) +{ + struct power_supply_config psy_cfg = {}; + struct device *dev = &client->dev; + struct power_supply *psy; + struct mm8013_chip *chip; + int ret = 0; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return dev_err_probe(dev, -EIO, + "I2C_FUNC_SMBUS_WORD_DATA not supported\n"); + + chip = devm_kzalloc(dev, sizeof(struct mm8013_chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->client = client; + + chip->regmap = devm_regmap_init_i2c(client, &mm8013_regmap_config); + if (IS_ERR(chip->regmap)) { + ret = PTR_ERR(chip->regmap); + return dev_err_probe(dev, ret, "Couldn't initialize regmap\n"); + } + + ret = mm8013_checkdevice(chip); + if (ret) + return dev_err_probe(dev, ret, "MM8013 not found\n"); + + psy_cfg.drv_data = chip; + psy_cfg.of_node = dev->of_node; + + psy = devm_power_supply_register(dev, &mm8013_desc, &psy_cfg); + if (IS_ERR(psy)) + return PTR_ERR(psy); + + return 0; +} + +static const struct i2c_device_id mm8013_id_table[] = { + { "mm8013", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, mm8013_id_table); + +static const struct of_device_id mm8013_match_table[] = { + { .compatible = "mitsumi,mm8013" }, + {} +}; + +static struct i2c_driver mm8013_i2c_driver = { + .probe = mm8013_probe, + .id_table = mm8013_id_table, + .driver = { + .name = "mm8013", + .of_match_table = mm8013_match_table, + }, +}; +module_i2c_driver(mm8013_i2c_driver); + +MODULE_DESCRIPTION("MM8013 fuel gauge driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/mt6370-charger.c b/drivers/power/supply/mt6370-charger.c index a9641bd3d8..e24fce087d 100644 --- a/drivers/power/supply/mt6370-charger.c +++ b/drivers/power/supply/mt6370-charger.c @@ -849,9 +849,7 @@ static int mt6370_chg_init_irq(struct mt6370_priv *priv) ret = platform_get_irq_byname(to_platform_device(priv->dev), mt6370_chg_irqs[i].name); if (ret < 0) - return dev_err_probe(priv->dev, ret, - "Failed to get irq %s\n", - mt6370_chg_irqs[i].name); + return ret; priv->irq_nums[i] = ret; ret = devm_request_threaded_irq(priv->dev, ret, NULL, diff --git a/drivers/power/supply/pcf50633-charger.c b/drivers/power/supply/pcf50633-charger.c index fd44cb8ac0..950e30917c 100644 --- a/drivers/power/supply/pcf50633-charger.c +++ b/drivers/power/supply/pcf50633-charger.c @@ -441,7 +441,7 @@ static int pcf50633_mbc_probe(struct platform_device *pdev) return 0; } -static int pcf50633_mbc_remove(struct platform_device *pdev) +static void pcf50633_mbc_remove(struct platform_device *pdev) { struct pcf50633_mbc *mbc = platform_get_drvdata(pdev); int i; @@ -453,8 +453,6 @@ static int pcf50633_mbc_remove(struct platform_device *pdev) power_supply_unregister(mbc->usb); power_supply_unregister(mbc->adapter); power_supply_unregister(mbc->ac); - - return 0; } static struct platform_driver pcf50633_mbc_driver = { @@ -462,7 +460,7 @@ static struct platform_driver pcf50633_mbc_driver = { .name = "pcf50633-mbc", }, .probe = pcf50633_mbc_probe, - .remove = pcf50633_mbc_remove, + .remove_new = pcf50633_mbc_remove, }; module_platform_driver(pcf50633_mbc_driver); diff --git a/drivers/power/supply/pm8916_bms_vm.c b/drivers/power/supply/pm8916_bms_vm.c new file mode 100644 index 0000000000..5d0dd84250 --- /dev/null +++ b/drivers/power/supply/pm8916_bms_vm.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023, Nikita Travkin <nikita@trvn.ru> + */ + +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/timekeeping.h> +#include <linux/mod_devicetable.h> + +#define PM8916_PERPH_TYPE 0x04 +#define PM8916_BMS_VM_TYPE 0x020D + +#define PM8916_SEC_ACCESS 0xD0 +#define PM8916_SEC_MAGIC 0xA5 + +#define PM8916_BMS_VM_STATUS1 0x08 +#define PM8916_BMS_VM_FSM_STATE(x) (((x) & 0b00111000) >> 3) +#define PM8916_BMS_VM_FSM_STATE_S2 0x2 + +#define PM8916_BMS_VM_MODE_CTL 0x40 +#define PM8916_BMS_VM_MODE_FORCE_S3 (BIT(0) | BIT(1)) +#define PM8916_BMS_VM_MODE_NORMAL (BIT(1) | BIT(3)) + +#define PM8916_BMS_VM_EN_CTL 0x46 +#define PM8916_BMS_ENABLED BIT(7) + +#define PM8916_BMS_VM_FIFO_LENGTH_CTL 0x47 +#define PM8916_BMS_VM_S1_SAMPLE_INTERVAL_CTL 0x55 +#define PM8916_BMS_VM_S2_SAMPLE_INTERVAL_CTL 0x56 +#define PM8916_BMS_VM_S3_S7_OCV_DATA0 0x6A +#define PM8916_BMS_VM_BMS_FIFO_REG_0_LSB 0xC0 + +/* Using only 1 fifo is broken in hardware */ +#define PM8916_BMS_VM_FIFO_COUNT 2 /* 2 .. 8 */ + +#define PM8916_BMS_VM_S1_SAMPLE_INTERVAL 10 +#define PM8916_BMS_VM_S2_SAMPLE_INTERVAL 10 + +struct pm8916_bms_vm_battery { + struct device *dev; + struct power_supply *battery; + struct power_supply_battery_info *info; + struct regmap *regmap; + unsigned int reg; + unsigned int last_ocv; + time64_t last_ocv_time; + unsigned int vbat_now; +}; + +static int pm8916_bms_vm_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct pm8916_bms_vm_battery *bat = power_supply_get_drvdata(psy); + struct power_supply_battery_info *info = bat->info; + int supplied; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + supplied = power_supply_am_i_supplied(psy); + + if (supplied < 0 && supplied != -ENODEV) + return supplied; + else if (supplied && supplied != -ENODEV) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + return 0; + + case POWER_SUPPLY_PROP_HEALTH: + if (bat->vbat_now < info->voltage_min_design_uv) + val->intval = POWER_SUPPLY_HEALTH_DEAD; + else if (bat->vbat_now > info->voltage_max_design_uv) + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + else + val->intval = POWER_SUPPLY_HEALTH_GOOD; + return 0; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = bat->vbat_now; + return 0; + + case POWER_SUPPLY_PROP_VOLTAGE_OCV: + /* + * Hardware only reliably measures OCV when the system is off or suspended. + * We expose the last known OCV value on boot, invalidating it after 180 seconds. + */ + if (ktime_get_seconds() - bat->last_ocv_time > 180) + return -ENODATA; + + val->intval = bat->last_ocv; + return 0; + + default: + return -EINVAL; + } +} + +static enum power_supply_property pm8916_bms_vm_battery_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_OCV, + POWER_SUPPLY_PROP_HEALTH, +}; + +static irqreturn_t pm8916_bms_vm_fifo_update_done_irq(int irq, void *data) +{ + struct pm8916_bms_vm_battery *bat = data; + u16 vbat_data[PM8916_BMS_VM_FIFO_COUNT]; + int ret; + + ret = regmap_bulk_read(bat->regmap, bat->reg + PM8916_BMS_VM_BMS_FIFO_REG_0_LSB, + &vbat_data, PM8916_BMS_VM_FIFO_COUNT * 2); + if (ret) + return IRQ_HANDLED; + + /* + * The VM-BMS hardware only collects voltage data and the software + * has to process it to calculate the OCV and SoC. Hardware provides + * up to 8 averaged measurements for software to take in account. + * + * Just use the last measured value for now to report the current + * battery voltage. + */ + bat->vbat_now = vbat_data[PM8916_BMS_VM_FIFO_COUNT - 1] * 300; + + power_supply_changed(bat->battery); + + return IRQ_HANDLED; +} + +static const struct power_supply_desc pm8916_bms_vm_battery_psy_desc = { + .name = "pm8916-bms-vm", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = pm8916_bms_vm_battery_properties, + .num_properties = ARRAY_SIZE(pm8916_bms_vm_battery_properties), + .get_property = pm8916_bms_vm_battery_get_property, +}; + +static int pm8916_bms_vm_battery_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pm8916_bms_vm_battery *bat; + struct power_supply_config psy_cfg = {}; + int ret, irq; + unsigned int tmp; + + bat = devm_kzalloc(dev, sizeof(*bat), GFP_KERNEL); + if (!bat) + return -ENOMEM; + + bat->dev = dev; + + bat->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!bat->regmap) + return -ENODEV; + + ret = device_property_read_u32(dev, "reg", &bat->reg); + if (ret < 0) + return -EINVAL; + + irq = platform_get_irq_byname(pdev, "fifo"); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, pm8916_bms_vm_fifo_update_done_irq, + IRQF_ONESHOT, "pm8916_vm_bms", bat); + if (ret) + return ret; + + ret = regmap_bulk_read(bat->regmap, bat->reg + PM8916_PERPH_TYPE, &tmp, 2); + if (ret) + goto comm_error; + + if (tmp != PM8916_BMS_VM_TYPE) + return dev_err_probe(dev, -ENODEV, "Device reported wrong type: 0x%X\n", tmp); + + ret = regmap_write(bat->regmap, bat->reg + PM8916_BMS_VM_S1_SAMPLE_INTERVAL_CTL, + PM8916_BMS_VM_S1_SAMPLE_INTERVAL); + if (ret) + goto comm_error; + ret = regmap_write(bat->regmap, bat->reg + PM8916_BMS_VM_S2_SAMPLE_INTERVAL_CTL, + PM8916_BMS_VM_S2_SAMPLE_INTERVAL); + if (ret) + goto comm_error; + ret = regmap_write(bat->regmap, bat->reg + PM8916_BMS_VM_FIFO_LENGTH_CTL, + PM8916_BMS_VM_FIFO_COUNT << 4 | PM8916_BMS_VM_FIFO_COUNT); + if (ret) + goto comm_error; + ret = regmap_write(bat->regmap, + bat->reg + PM8916_BMS_VM_EN_CTL, PM8916_BMS_ENABLED); + if (ret) + goto comm_error; + + ret = regmap_bulk_read(bat->regmap, + bat->reg + PM8916_BMS_VM_S3_S7_OCV_DATA0, &tmp, 2); + if (ret) + goto comm_error; + + bat->last_ocv_time = ktime_get_seconds(); + bat->last_ocv = tmp * 300; + bat->vbat_now = bat->last_ocv; + + psy_cfg.drv_data = bat; + psy_cfg.of_node = dev->of_node; + + bat->battery = devm_power_supply_register(dev, &pm8916_bms_vm_battery_psy_desc, &psy_cfg); + if (IS_ERR(bat->battery)) + return dev_err_probe(dev, PTR_ERR(bat->battery), "Unable to register battery\n"); + + ret = power_supply_get_battery_info(bat->battery, &bat->info); + if (ret) + return dev_err_probe(dev, ret, "Unable to get battery info\n"); + + platform_set_drvdata(pdev, bat); + + return 0; + +comm_error: + return dev_err_probe(dev, ret, "Unable to communicate with device\n"); +} + +static int pm8916_bms_vm_battery_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct pm8916_bms_vm_battery *bat = platform_get_drvdata(pdev); + int ret; + + /* + * Due to a hardware quirk the FSM doesn't switch states normally. + * Instead we unlock the debug registers and force S3 (Measure OCV/Sleep) + * mode every time we suspend. + */ + + ret = regmap_write(bat->regmap, + bat->reg + PM8916_SEC_ACCESS, PM8916_SEC_MAGIC); + if (ret) + goto error; + ret = regmap_write(bat->regmap, + bat->reg + PM8916_BMS_VM_MODE_CTL, PM8916_BMS_VM_MODE_FORCE_S3); + if (ret) + goto error; + + return 0; + +error: + dev_err(bat->dev, "Failed to force S3 mode: %pe\n", ERR_PTR(ret)); + return ret; +} + +static int pm8916_bms_vm_battery_resume(struct platform_device *pdev) +{ + struct pm8916_bms_vm_battery *bat = platform_get_drvdata(pdev); + int ret; + unsigned int tmp; + + ret = regmap_bulk_read(bat->regmap, + bat->reg + PM8916_BMS_VM_S3_S7_OCV_DATA0, &tmp, 2); + + bat->last_ocv_time = ktime_get_seconds(); + bat->last_ocv = tmp * 300; + + ret = regmap_write(bat->regmap, + bat->reg + PM8916_SEC_ACCESS, PM8916_SEC_MAGIC); + if (ret) + goto error; + ret = regmap_write(bat->regmap, + bat->reg + PM8916_BMS_VM_MODE_CTL, PM8916_BMS_VM_MODE_NORMAL); + if (ret) + goto error; + + return 0; + +error: + dev_err(bat->dev, "Failed to return normal mode: %pe\n", ERR_PTR(ret)); + return ret; +} + +static const struct of_device_id pm8916_bms_vm_battery_of_match[] = { + { .compatible = "qcom,pm8916-bms-vm", }, + {} +}; +MODULE_DEVICE_TABLE(of, pm8916_bms_vm_battery_of_match); + +static struct platform_driver pm8916_bms_vm_battery_driver = { + .driver = { + .name = "pm8916-bms-vm", + .of_match_table = pm8916_bms_vm_battery_of_match, + }, + .probe = pm8916_bms_vm_battery_probe, + .suspend = pm8916_bms_vm_battery_suspend, + .resume = pm8916_bms_vm_battery_resume, +}; +module_platform_driver(pm8916_bms_vm_battery_driver); + +MODULE_DESCRIPTION("pm8916 BMS-VM driver"); +MODULE_AUTHOR("Nikita Travkin <nikita@trvn.ru>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/pm8916_lbc.c b/drivers/power/supply/pm8916_lbc.c new file mode 100644 index 0000000000..6d92e98cbe --- /dev/null +++ b/drivers/power/supply/pm8916_lbc.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023, Nikita Travkin <nikita@trvn.ru> + */ + +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/extcon-provider.h> +#include <linux/mod_devicetable.h> + +/* Two bytes: type + subtype */ +#define PM8916_PERPH_TYPE 0x04 +#define PM8916_LBC_CHGR_TYPE 0x1502 +#define PM8916_LBC_BAT_IF_TYPE 0x1602 +#define PM8916_LBC_USB_TYPE 0x1702 +#define PM8916_LBC_MISC_TYPE 0x1802 + +#define PM8916_LBC_CHGR_CHG_OPTION 0x08 +#define PM8916_LBC_CHGR_PMIC_CHARGER BIT(7) + +#define PM8916_LBC_CHGR_CHG_STATUS 0x09 + +#define PM8916_INT_RT_STS 0x10 + +#define PM8916_LBC_USB_USBIN_VALID BIT(1) + +#define PM8916_LBC_CHGR_VDD_MAX 0x40 +#define PM8916_LBC_CHGR_VDD_SAFE 0x41 +#define PM8916_LBC_CHGR_IBAT_MAX 0x44 +#define PM8916_LBC_CHGR_IBAT_SAFE 0x45 + +#define PM8916_LBC_CHGR_TCHG_MAX_EN 0x60 +#define PM8916_LBC_CHGR_TCHG_MAX_ENABLED BIT(7) +#define PM8916_LBC_CHGR_TCHG_MAX 0x61 + +#define PM8916_LBC_CHGR_CHG_CTRL 0x49 +#define PM8916_LBC_CHGR_CHG_EN BIT(7) +#define PM8916_LBC_CHGR_PSTG_EN BIT(5) + +#define PM8916_LBC_CHGR_MIN_CURRENT 90000 +#define PM8916_LBC_CHGR_MAX_CURRENT 1440000 + +#define PM8916_LBC_CHGR_MIN_VOLTAGE 4000000 +#define PM8916_LBC_CHGR_MAX_VOLTAGE 4775000 +#define PM8916_LBC_CHGR_VOLTAGE_STEP 25000 + +#define PM8916_LBC_CHGR_MIN_TIME 4 +#define PM8916_LBC_CHGR_MAX_TIME 256 + +struct pm8916_lbc_charger { + struct device *dev; + struct extcon_dev *edev; + struct power_supply *charger; + struct power_supply_battery_info *info; + struct regmap *regmap; + unsigned int reg[4]; + bool online; + unsigned int charge_voltage_max; + unsigned int charge_voltage_safe; + unsigned int charge_current_max; + unsigned int charge_current_safe; +}; + +static const unsigned int pm8916_lbc_charger_cable[] = { + EXTCON_USB, + EXTCON_NONE, +}; + +enum { + LBC_CHGR = 0, + LBC_BAT_IF, + LBC_USB, + LBC_MISC, +}; + +static int pm8916_lbc_charger_configure(struct pm8916_lbc_charger *chg) +{ + int ret = 0; + unsigned int tmp; + + chg->charge_voltage_max = clamp_t(u32, chg->charge_voltage_max, + PM8916_LBC_CHGR_MIN_VOLTAGE, chg->charge_voltage_safe); + + tmp = chg->charge_voltage_max - PM8916_LBC_CHGR_MIN_VOLTAGE; + tmp /= PM8916_LBC_CHGR_VOLTAGE_STEP; + chg->charge_voltage_max = PM8916_LBC_CHGR_MIN_VOLTAGE + tmp * PM8916_LBC_CHGR_VOLTAGE_STEP; + + ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_VDD_MAX, tmp); + if (ret) + goto error; + + chg->charge_current_max = min(chg->charge_current_max, chg->charge_current_safe); + + tmp = clamp_t(u32, chg->charge_current_max, + PM8916_LBC_CHGR_MIN_CURRENT, PM8916_LBC_CHGR_MAX_CURRENT); + + tmp = chg->charge_current_max / PM8916_LBC_CHGR_MIN_CURRENT - 1; + chg->charge_current_max = (tmp + 1) * PM8916_LBC_CHGR_MIN_CURRENT; + + ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_IBAT_MAX, tmp); + if (ret) + goto error; + + ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_CHG_CTRL, + PM8916_LBC_CHGR_CHG_EN | PM8916_LBC_CHGR_PSTG_EN); + if (ret) + goto error; + + return ret; + +error: + dev_err(chg->dev, "Failed to configure charging: %pe\n", ERR_PTR(ret)); + return ret; +} + +static int pm8916_lbc_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct pm8916_lbc_charger *chg = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = chg->online; + return 0; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + val->intval = chg->charge_voltage_max; + return 0; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + val->intval = chg->charge_current_max; + return 0; + + default: + return -EINVAL; + }; +} + +static int pm8916_lbc_charger_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct pm8916_lbc_charger *chg = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + chg->charge_current_max = val->intval; + return pm8916_lbc_charger_configure(chg); + default: + return -EINVAL; + } +} + +static int pm8916_lbc_charger_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + return true; + default: + return false; + } +} + +static enum power_supply_property pm8916_lbc_charger_properties[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, +}; + +static irqreturn_t pm8916_lbc_charger_state_changed_irq(int irq, void *data) +{ + struct pm8916_lbc_charger *chg = data; + unsigned int tmp; + int ret; + + ret = regmap_read(chg->regmap, chg->reg[LBC_USB] + PM8916_INT_RT_STS, &tmp); + if (ret) + return IRQ_HANDLED; + + chg->online = !!(tmp & PM8916_LBC_USB_USBIN_VALID); + extcon_set_state_sync(chg->edev, EXTCON_USB, chg->online); + + power_supply_changed(chg->charger); + + return IRQ_HANDLED; +} + +static int pm8916_lbc_charger_probe_dt(struct pm8916_lbc_charger *chg) +{ + struct device *dev = chg->dev; + int ret = 0; + unsigned int tmp; + + ret = device_property_read_u32(dev, "qcom,fast-charge-safe-voltage", &chg->charge_voltage_safe); + if (ret) + return ret; + if (chg->charge_voltage_safe < PM8916_LBC_CHGR_MIN_VOLTAGE) + return -EINVAL; + + chg->charge_voltage_safe = clamp_t(u32, chg->charge_voltage_safe, + PM8916_LBC_CHGR_MIN_VOLTAGE, PM8916_LBC_CHGR_MAX_VOLTAGE); + + tmp = chg->charge_voltage_safe - PM8916_LBC_CHGR_MIN_VOLTAGE; + tmp /= PM8916_LBC_CHGR_VOLTAGE_STEP; + ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_VDD_SAFE, tmp); + if (ret) + return ret; + + ret = device_property_read_u32(dev, "qcom,fast-charge-safe-current", &chg->charge_current_safe); + if (ret) + return ret; + if (chg->charge_current_safe < PM8916_LBC_CHGR_MIN_CURRENT) + return -EINVAL; + + chg->charge_current_safe = clamp_t(u32, chg->charge_current_safe, + PM8916_LBC_CHGR_MIN_CURRENT, PM8916_LBC_CHGR_MAX_CURRENT); + + chg->charge_current_max = chg->charge_current_safe; + + tmp = chg->charge_current_safe / PM8916_LBC_CHGR_MIN_CURRENT - 1; + ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_IBAT_SAFE, tmp); + if (ret) + return ret; + + /* Disable charger timeout. */ + ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_TCHG_MAX_EN, 0x00); + if (ret) + return ret; + + return ret; +} + +static const struct power_supply_desc pm8916_lbc_charger_psy_desc = { + .name = "pm8916-lbc-chgr", + .type = POWER_SUPPLY_TYPE_USB, + .properties = pm8916_lbc_charger_properties, + .num_properties = ARRAY_SIZE(pm8916_lbc_charger_properties), + .get_property = pm8916_lbc_charger_get_property, + .set_property = pm8916_lbc_charger_set_property, + .property_is_writeable = pm8916_lbc_charger_property_is_writeable, +}; + +static int pm8916_lbc_charger_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pm8916_lbc_charger *chg; + struct power_supply_config psy_cfg = {}; + int ret, len, irq; + unsigned int tmp; + + chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL); + if (!chg) + return -ENOMEM; + + chg->dev = dev; + + chg->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!chg->regmap) + return -ENODEV; + + len = device_property_count_u32(dev, "reg"); + if (len < 0) + return len; + if (len != 4) + return dev_err_probe(dev, -EINVAL, + "Wrong amount of reg values: %d (4 expected)\n", len); + + irq = platform_get_irq_byname(pdev, "usb_vbus"); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, pm8916_lbc_charger_state_changed_irq, + IRQF_ONESHOT, "pm8916_lbc", chg); + if (ret) + return ret; + + ret = device_property_read_u32_array(dev, "reg", chg->reg, len); + if (ret) + return ret; + + ret = regmap_bulk_read(chg->regmap, chg->reg[LBC_CHGR] + PM8916_PERPH_TYPE, &tmp, 2); + if (ret) + goto comm_error; + if (tmp != PM8916_LBC_CHGR_TYPE) + goto type_error; + + ret = regmap_bulk_read(chg->regmap, chg->reg[LBC_BAT_IF] + PM8916_PERPH_TYPE, &tmp, 2); + if (ret) + goto comm_error; + if (tmp != PM8916_LBC_BAT_IF_TYPE) + goto type_error; + + ret = regmap_bulk_read(chg->regmap, chg->reg[LBC_USB] + PM8916_PERPH_TYPE, &tmp, 2); + if (ret) + goto comm_error; + if (tmp != PM8916_LBC_USB_TYPE) + goto type_error; + + ret = regmap_bulk_read(chg->regmap, chg->reg[LBC_MISC] + PM8916_PERPH_TYPE, &tmp, 2); + if (ret) + goto comm_error; + if (tmp != PM8916_LBC_MISC_TYPE) + goto type_error; + + ret = regmap_read(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_CHG_OPTION, &tmp); + if (ret) + goto comm_error; + if (tmp != PM8916_LBC_CHGR_PMIC_CHARGER) + dev_err_probe(dev, -ENODEV, "The system is using an external charger\n"); + + ret = pm8916_lbc_charger_probe_dt(chg); + if (ret) + dev_err_probe(dev, ret, "Error while parsing device tree\n"); + + psy_cfg.drv_data = chg; + psy_cfg.of_node = dev->of_node; + + chg->charger = devm_power_supply_register(dev, &pm8916_lbc_charger_psy_desc, &psy_cfg); + if (IS_ERR(chg->charger)) + return dev_err_probe(dev, PTR_ERR(chg->charger), "Unable to register charger\n"); + + ret = power_supply_get_battery_info(chg->charger, &chg->info); + if (ret) + return dev_err_probe(dev, ret, "Unable to get battery info\n"); + + chg->edev = devm_extcon_dev_allocate(dev, pm8916_lbc_charger_cable); + if (IS_ERR(chg->edev)) + return PTR_ERR(chg->edev); + + ret = devm_extcon_dev_register(dev, chg->edev); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to register extcon device\n"); + + ret = regmap_read(chg->regmap, chg->reg[LBC_USB] + PM8916_INT_RT_STS, &tmp); + if (ret) + goto comm_error; + + chg->online = !!(tmp & PM8916_LBC_USB_USBIN_VALID); + extcon_set_state_sync(chg->edev, EXTCON_USB, chg->online); + + chg->charge_voltage_max = chg->info->voltage_max_design_uv; + ret = pm8916_lbc_charger_configure(chg); + if (ret) + return ret; + + return 0; + +comm_error: + return dev_err_probe(dev, ret, "Unable to communicate with device\n"); + +type_error: + return dev_err_probe(dev, -ENODEV, "Device reported wrong type: 0x%X\n", tmp); +} + +static const struct of_device_id pm8916_lbc_charger_of_match[] = { + { .compatible = "qcom,pm8916-lbc", }, + {} +}; +MODULE_DEVICE_TABLE(of, pm8916_lbc_charger_of_match); + +static struct platform_driver pm8916_lbc_charger_driver = { + .driver = { + .name = "pm8916-lbc", + .of_match_table = pm8916_lbc_charger_of_match, + }, + .probe = pm8916_lbc_charger_probe, +}; +module_platform_driver(pm8916_lbc_charger_driver); + +MODULE_DESCRIPTION("pm8916 LBC driver"); +MODULE_AUTHOR("Nikita Travkin <nikita@trvn.ru>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 416409e2fd..73265001dd 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -29,8 +29,7 @@ struct class *power_supply_class; EXPORT_SYMBOL_GPL(power_supply_class); -BLOCKING_NOTIFIER_HEAD(power_supply_notifier); -EXPORT_SYMBOL_GPL(power_supply_notifier); +static BLOCKING_NOTIFIER_HEAD(power_supply_notifier); static struct device_type power_supply_dev_type; @@ -1380,6 +1379,7 @@ __power_supply_register(struct device *parent, psy->drv_data = cfg->drv_data; psy->of_node = cfg->fwnode ? to_of_node(cfg->fwnode) : cfg->of_node; + dev->of_node = psy->of_node; psy->supplied_to = cfg->supplied_to; psy->num_supplicants = cfg->num_supplicants; } diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index d483a81560..977611e163 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -15,6 +15,7 @@ #include <linux/power_supply.h> #include <linux/slab.h> #include <linux/stat.h> +#include <linux/string_helpers.h> #include "power_supply.h" @@ -398,14 +399,6 @@ static const struct attribute_group *power_supply_attr_groups[] = { NULL, }; -static void str_to_lower(char *str) -{ - while (*str) { - *str = tolower(*str); - str++; - } -} - void power_supply_init_attrs(struct device_type *dev_type) { int i; @@ -420,7 +413,8 @@ void power_supply_init_attrs(struct device_type *dev_type) __func__, i); sprintf(power_supply_attrs[i].attr_name, "_err_%d", i); } else { - str_to_lower(power_supply_attrs[i].attr_name); + string_lower(power_supply_attrs[i].attr_name, + power_supply_attrs[i].attr_name); } attr = &power_supply_attrs[i].dev_attr; diff --git a/drivers/power/supply/qcom_pmi8998_charger.c b/drivers/power/supply/qcom_pmi8998_charger.c index 22c7c0e7c5..9bb7774060 100644 --- a/drivers/power/supply/qcom_pmi8998_charger.c +++ b/drivers/power/supply/qcom_pmi8998_charger.c @@ -915,8 +915,7 @@ static int smb2_init_irq(struct smb2_chip *chip, int *irq, const char *name, irqnum = platform_get_irq_byname(to_platform_device(chip->dev), name); if (irqnum < 0) - return dev_err_probe(chip->dev, irqnum, - "Couldn't get irq %s byname\n", name); + return irqnum; rc = devm_request_threaded_irq(chip->dev, irqnum, NULL, handler, IRQF_ONESHOT, name, chip); diff --git a/drivers/power/supply/qcom_smbb.c b/drivers/power/supply/qcom_smbb.c index bd50124eef..4e57762e27 100644 --- a/drivers/power/supply/qcom_smbb.c +++ b/drivers/power/supply/qcom_smbb.c @@ -1000,15 +1000,13 @@ static int smbb_charger_probe(struct platform_device *pdev) return 0; } -static int smbb_charger_remove(struct platform_device *pdev) +static void smbb_charger_remove(struct platform_device *pdev) { struct smbb_charger *chg; chg = platform_get_drvdata(pdev); regmap_update_bits(chg->regmap, chg->addr + SMBB_CHG_CTRL, CTRL_EN, 0); - - return 0; } static const struct of_device_id smbb_charger_id_table[] = { @@ -1020,7 +1018,7 @@ MODULE_DEVICE_TABLE(of, smbb_charger_id_table); static struct platform_driver smbb_charger_driver = { .probe = smbb_charger_probe, - .remove = smbb_charger_remove, + .remove_new = smbb_charger_remove, .driver = { .name = "qcom-smbb", .of_match_table = smbb_charger_id_table, diff --git a/drivers/power/supply/rk817_charger.c b/drivers/power/supply/rk817_charger.c index f64daf5a41..7ca91739c6 100644 --- a/drivers/power/supply/rk817_charger.c +++ b/drivers/power/supply/rk817_charger.c @@ -1207,11 +1207,24 @@ static int rk817_charger_probe(struct platform_device *pdev) return 0; } +static int __maybe_unused rk817_resume(struct device *dev) +{ + + struct rk817_charger *charger = dev_get_drvdata(dev); + + /* force an immediate update */ + mod_delayed_work(system_wq, &charger->work, 0); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(rk817_charger_pm, NULL, rk817_resume); static struct platform_driver rk817_charger_driver = { .probe = rk817_charger_probe, .driver = { .name = "rk817-charger", + .pm = &rk817_charger_pm, }, }; module_platform_driver(rk817_charger_driver); diff --git a/drivers/power/supply/rt5033_charger.c b/drivers/power/supply/rt5033_charger.c index c0c516f22c..d19c7e80a9 100644 --- a/drivers/power/supply/rt5033_charger.c +++ b/drivers/power/supply/rt5033_charger.c @@ -6,8 +6,12 @@ * Author: Beomho Seo <beomho.seo@samsung.com> */ +#include <linux/devm-helpers.h> +#include <linux/extcon.h> #include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/regmap.h> @@ -25,7 +29,15 @@ struct rt5033_charger { struct device *dev; struct regmap *regmap; struct power_supply *psy; - struct rt5033_charger_data *chg; + struct rt5033_charger_data chg; + struct extcon_dev *edev; + struct notifier_block extcon_nb; + struct work_struct extcon_work; + struct mutex lock; + bool online; + bool otg; + bool mivr_enabled; + u8 cv_regval; }; static int rt5033_get_charger_state(struct rt5033_charger *charger) @@ -56,6 +68,10 @@ static int rt5033_get_charger_state(struct rt5033_charger *charger) state = POWER_SUPPLY_STATUS_UNKNOWN; } + /* For OTG mode, RT5033 would still report "charging" */ + if (charger->otg) + state = POWER_SUPPLY_STATUS_DISCHARGING; + return state; } @@ -115,7 +131,7 @@ static int rt5033_get_charger_const_voltage(struct rt5033_charger *charger) static inline int rt5033_init_const_charge(struct rt5033_charger *charger) { - struct rt5033_charger_data *chg = charger->chg; + struct rt5033_charger_data *chg = &charger->chg; int ret; unsigned int val; u8 reg_data; @@ -147,6 +163,9 @@ static inline int rt5033_init_const_charge(struct rt5033_charger *charger) return -EINVAL; } + /* Store that value for later usage */ + charger->cv_regval = reg_data; + /* Set end of charge current */ if (chg->eoc_uamp < RT5033_CHARGER_EOC_MIN || chg->eoc_uamp > RT5033_CHARGER_EOC_MAX) { @@ -186,7 +205,7 @@ static inline int rt5033_init_const_charge(struct rt5033_charger *charger) static inline int rt5033_init_fast_charge(struct rt5033_charger *charger) { - struct rt5033_charger_data *chg = charger->chg; + struct rt5033_charger_data *chg = &charger->chg; int ret; unsigned int val; u8 reg_data; @@ -231,7 +250,7 @@ static inline int rt5033_init_fast_charge(struct rt5033_charger *charger) static inline int rt5033_init_pre_charge(struct rt5033_charger *charger) { - struct rt5033_charger_data *chg = charger->chg; + struct rt5033_charger_data *chg = &charger->chg; int ret; unsigned int val; u8 reg_data; @@ -330,6 +349,162 @@ static int rt5033_charger_reg_init(struct rt5033_charger *charger) return 0; } +static int rt5033_charger_set_otg(struct rt5033_charger *charger) +{ + int ret; + + mutex_lock(&charger->lock); + + /* Set OTG boost v_out to 5 volts */ + ret = regmap_update_bits(charger->regmap, RT5033_REG_CHG_CTRL2, + RT5033_CHGCTRL2_CV_MASK, + 0x37 << RT5033_CHGCTRL2_CV_SHIFT); + if (ret) { + dev_err(charger->dev, "Failed set OTG boost v_out\n"); + ret = -EINVAL; + goto out_unlock; + } + + /* Set operation mode to OTG */ + ret = regmap_update_bits(charger->regmap, RT5033_REG_CHG_CTRL1, + RT5033_CHGCTRL1_MODE_MASK, RT5033_BOOST_MODE); + if (ret) { + dev_err(charger->dev, "Failed to update OTG mode.\n"); + ret = -EINVAL; + goto out_unlock; + } + + /* In case someone switched from charging to OTG directly */ + if (charger->online) + charger->online = false; + + charger->otg = true; + +out_unlock: + mutex_unlock(&charger->lock); + + return ret; +} + +static int rt5033_charger_unset_otg(struct rt5033_charger *charger) +{ + int ret; + u8 data; + + /* Restore constant voltage for charging */ + data = charger->cv_regval; + ret = regmap_update_bits(charger->regmap, RT5033_REG_CHG_CTRL2, + RT5033_CHGCTRL2_CV_MASK, + data << RT5033_CHGCTRL2_CV_SHIFT); + if (ret) { + dev_err(charger->dev, "Failed to restore constant voltage\n"); + return -EINVAL; + } + + /* Set operation mode to charging */ + ret = regmap_update_bits(charger->regmap, RT5033_REG_CHG_CTRL1, + RT5033_CHGCTRL1_MODE_MASK, RT5033_CHARGER_MODE); + if (ret) { + dev_err(charger->dev, "Failed to update charger mode.\n"); + return -EINVAL; + } + + charger->otg = false; + + return 0; +} + +static int rt5033_charger_set_charging(struct rt5033_charger *charger) +{ + int ret; + + mutex_lock(&charger->lock); + + /* In case someone switched from OTG to charging directly */ + if (charger->otg) { + ret = rt5033_charger_unset_otg(charger); + if (ret) { + mutex_unlock(&charger->lock); + return -EINVAL; + } + } + + charger->online = true; + + mutex_unlock(&charger->lock); + + return 0; +} + +static int rt5033_charger_set_mivr(struct rt5033_charger *charger) +{ + int ret; + + mutex_lock(&charger->lock); + + /* + * When connected via USB connector type SDP (Standard Downstream Port), + * the minimum input voltage regulation (MIVR) should be enabled. It + * prevents an input voltage drop due to insufficient current provided + * by the adapter or USB input. As a downside, it may reduces the + * charging current and thus slows the charging. + */ + ret = regmap_update_bits(charger->regmap, RT5033_REG_CHG_CTRL4, + RT5033_CHGCTRL4_MIVR_MASK, RT5033_CHARGER_MIVR_4600MV); + if (ret) { + dev_err(charger->dev, "Failed to set MIVR level.\n"); + mutex_unlock(&charger->lock); + return -EINVAL; + } + + charger->mivr_enabled = true; + + mutex_unlock(&charger->lock); + + /* Beyond this, do the same steps like setting charging */ + rt5033_charger_set_charging(charger); + + return 0; +} + +static int rt5033_charger_set_disconnect(struct rt5033_charger *charger) +{ + int ret = 0; + + mutex_lock(&charger->lock); + + /* Disable MIVR if enabled */ + if (charger->mivr_enabled) { + ret = regmap_update_bits(charger->regmap, + RT5033_REG_CHG_CTRL4, + RT5033_CHGCTRL4_MIVR_MASK, + RT5033_CHARGER_MIVR_DISABLE); + if (ret) { + dev_err(charger->dev, "Failed to disable MIVR.\n"); + ret = -EINVAL; + goto out_unlock; + } + + charger->mivr_enabled = false; + } + + if (charger->otg) { + ret = rt5033_charger_unset_otg(charger); + if (ret) { + ret = -EINVAL; + goto out_unlock; + } + } + + if (charger->online) + charger->online = false; + +out_unlock: + mutex_unlock(&charger->lock); + + return ret; +} + static enum power_supply_property rt5033_charger_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_CHARGE_TYPE, @@ -366,8 +541,7 @@ static int rt5033_charger_get_property(struct power_supply *psy, val->strval = RT5033_MANUFACTURER; break; case POWER_SUPPLY_PROP_ONLINE: - val->intval = (rt5033_get_charger_state(charger) == - POWER_SUPPLY_STATUS_CHARGING); + val->intval = charger->online; break; default: return -EINVAL; @@ -376,21 +550,16 @@ static int rt5033_charger_get_property(struct power_supply *psy, return 0; } -static struct rt5033_charger_data *rt5033_charger_dt_init( - struct rt5033_charger *charger) +static int rt5033_charger_dt_init(struct rt5033_charger *charger) { - struct rt5033_charger_data *chg; + struct rt5033_charger_data *chg = &charger->chg; struct power_supply_battery_info *info; int ret; - chg = devm_kzalloc(charger->dev, sizeof(*chg), GFP_KERNEL); - if (!chg) - return ERR_PTR(-ENOMEM); - ret = power_supply_get_battery_info(charger->psy, &info); if (ret) - return ERR_PTR(dev_err_probe(charger->dev, -EINVAL, - "missing battery info\n")); + return dev_err_probe(charger->dev, -EINVAL, + "missing battery info\n"); /* Assign data. Validity will be checked in the init functions. */ chg->pre_uamp = info->precharge_current_ua; @@ -399,7 +568,87 @@ static struct rt5033_charger_data *rt5033_charger_dt_init( chg->pre_uvolt = info->precharge_voltage_max_uv; chg->const_uvolt = info->constant_charge_voltage_max_uv; - return chg; + return 0; +} + +static void rt5033_charger_extcon_work(struct work_struct *work) +{ + struct rt5033_charger *charger = + container_of(work, struct rt5033_charger, extcon_work); + struct extcon_dev *edev = charger->edev; + int connector, state; + int ret; + + for (connector = EXTCON_USB_HOST; connector <= EXTCON_CHG_USB_PD; + connector++) { + state = extcon_get_state(edev, connector); + if (state == 1) + break; + } + + /* + * Adding a delay between extcon notification and extcon action. This + * makes extcon action execution more reliable. Without the delay the + * execution sometimes fails, possibly because the chip is busy or not + * ready. + */ + msleep(100); + + switch (connector) { + case EXTCON_CHG_USB_SDP: + ret = rt5033_charger_set_mivr(charger); + if (ret) { + dev_err(charger->dev, "failed to set USB mode\n"); + break; + } + dev_info(charger->dev, "USB mode. connector type: %d\n", + connector); + break; + case EXTCON_CHG_USB_DCP: + case EXTCON_CHG_USB_CDP: + case EXTCON_CHG_USB_ACA: + case EXTCON_CHG_USB_FAST: + case EXTCON_CHG_USB_SLOW: + case EXTCON_CHG_WPT: + case EXTCON_CHG_USB_PD: + ret = rt5033_charger_set_charging(charger); + if (ret) { + dev_err(charger->dev, "failed to set charging\n"); + break; + } + dev_info(charger->dev, "charging. connector type: %d\n", + connector); + break; + case EXTCON_USB_HOST: + ret = rt5033_charger_set_otg(charger); + if (ret) { + dev_err(charger->dev, "failed to set OTG\n"); + break; + } + dev_info(charger->dev, "OTG enabled\n"); + break; + default: + ret = rt5033_charger_set_disconnect(charger); + if (ret) { + dev_err(charger->dev, "failed to set disconnect\n"); + break; + } + dev_info(charger->dev, "disconnected\n"); + break; + } + + power_supply_changed(charger->psy); +} + +static int rt5033_charger_extcon_notifier(struct notifier_block *nb, + unsigned long event, void *param) +{ + struct rt5033_charger *charger = + container_of(nb, struct rt5033_charger, extcon_nb); + + schedule_work(&charger->extcon_work); + + return NOTIFY_OK; } static const struct power_supply_desc rt5033_charger_desc = { @@ -414,6 +663,7 @@ static int rt5033_charger_probe(struct platform_device *pdev) { struct rt5033_charger *charger; struct power_supply_config psy_cfg = {}; + struct device_node *np_conn, *np_edev; int ret; charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL); @@ -423,25 +673,53 @@ static int rt5033_charger_probe(struct platform_device *pdev) platform_set_drvdata(pdev, charger); charger->dev = &pdev->dev; charger->regmap = dev_get_regmap(pdev->dev.parent, NULL); + mutex_init(&charger->lock); psy_cfg.of_node = pdev->dev.of_node; psy_cfg.drv_data = charger; - charger->psy = devm_power_supply_register(&pdev->dev, + charger->psy = devm_power_supply_register(charger->dev, &rt5033_charger_desc, &psy_cfg); if (IS_ERR(charger->psy)) - return dev_err_probe(&pdev->dev, PTR_ERR(charger->psy), + return dev_err_probe(charger->dev, PTR_ERR(charger->psy), "Failed to register power supply\n"); - charger->chg = rt5033_charger_dt_init(charger); - if (IS_ERR_OR_NULL(charger->chg)) - return PTR_ERR(charger->chg); + ret = rt5033_charger_dt_init(charger); + if (ret) + return ret; ret = rt5033_charger_reg_init(charger); if (ret) return ret; + /* + * Extcon support is not vital for the charger to work. If no extcon + * is available, just emit a warning and leave the probe function. + */ + np_conn = of_parse_phandle(pdev->dev.of_node, "richtek,usb-connector", 0); + np_edev = of_get_parent(np_conn); + charger->edev = extcon_find_edev_by_node(np_edev); + if (IS_ERR(charger->edev)) { + dev_warn(charger->dev, "no extcon device found in device-tree\n"); + goto out; + } + + ret = devm_work_autocancel(charger->dev, &charger->extcon_work, + rt5033_charger_extcon_work); + if (ret) { + dev_err(charger->dev, "failed to initialize extcon work\n"); + return ret; + } + + charger->extcon_nb.notifier_call = rt5033_charger_extcon_notifier; + ret = devm_extcon_register_notifier_all(charger->dev, charger->edev, + &charger->extcon_nb); + if (ret) { + dev_err(charger->dev, "failed to register extcon notifier\n"); + return ret; + } +out: return 0; } diff --git a/drivers/power/supply/rx51_battery.c b/drivers/power/supply/rx51_battery.c index 6e488ecf4d..e2bfc81f0f 100644 --- a/drivers/power/supply/rx51_battery.c +++ b/drivers/power/supply/rx51_battery.c @@ -246,7 +246,7 @@ error: return ret; } -static int rx51_battery_remove(struct platform_device *pdev) +static void rx51_battery_remove(struct platform_device *pdev) { struct rx51_device_info *di = platform_get_drvdata(pdev); @@ -255,8 +255,6 @@ static int rx51_battery_remove(struct platform_device *pdev) iio_channel_release(di->channel_vbat); iio_channel_release(di->channel_bsi); iio_channel_release(di->channel_temp); - - return 0; } #ifdef CONFIG_OF @@ -269,7 +267,7 @@ MODULE_DEVICE_TABLE(of, n900_battery_of_match); static struct platform_driver rx51_battery_driver = { .probe = rx51_battery_probe, - .remove = rx51_battery_remove, + .remove_new = rx51_battery_remove, .driver = { .name = "rx51-battery", .of_match_table = of_match_ptr(n900_battery_of_match), diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index cdfc8466d1..a6c204c082 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -1135,7 +1135,7 @@ static int sbs_probe(struct i2c_client *client) if (!chip) return -ENOMEM; - chip->flags = (u32)(uintptr_t)device_get_match_data(&client->dev); + chip->flags = (uintptr_t)i2c_get_match_data(client); chip->client = client; psy_cfg.of_node = client->dev.of_node; psy_cfg.drv_data = chip; @@ -1253,9 +1253,9 @@ static SIMPLE_DEV_PM_OPS(sbs_pm_ops, sbs_suspend, NULL); #endif static const struct i2c_device_id sbs_id[] = { - { "bq20z65", 0 }, - { "bq20z75", 0 }, - { "sbs-battery", 1 }, + { "bq20z65", SBS_FLAGS_TI_BQ20ZX5 }, + { "bq20z75", SBS_FLAGS_TI_BQ20ZX5 }, + { "sbs-battery", 0 }, {} }; MODULE_DEVICE_TABLE(i2c, sbs_id); diff --git a/drivers/power/supply/sc2731_charger.c b/drivers/power/supply/sc2731_charger.c index 9ac17cf7a1..b3d8b1ca97 100644 --- a/drivers/power/supply/sc2731_charger.c +++ b/drivers/power/supply/sc2731_charger.c @@ -511,13 +511,11 @@ static int sc2731_charger_probe(struct platform_device *pdev) return 0; } -static int sc2731_charger_remove(struct platform_device *pdev) +static void sc2731_charger_remove(struct platform_device *pdev) { struct sc2731_charger_info *info = platform_get_drvdata(pdev); usb_unregister_notifier(info->usb_phy, &info->usb_notify); - - return 0; } static const struct of_device_id sc2731_charger_of_match[] = { @@ -532,7 +530,7 @@ static struct platform_driver sc2731_charger_driver = { .of_match_table = sc2731_charger_of_match, }, .probe = sc2731_charger_probe, - .remove = sc2731_charger_remove, + .remove_new = sc2731_charger_remove, }; module_platform_driver(sc2731_charger_driver); diff --git a/drivers/power/supply/surface_battery.c b/drivers/power/supply/surface_battery.c index 19d2f8834e..196d290dc5 100644 --- a/drivers/power/supply/surface_battery.c +++ b/drivers/power/supply/surface_battery.c @@ -722,7 +722,7 @@ static void spwr_battery_init(struct spwr_battery_device *bat, struct ssam_devic struct ssam_event_registry registry, const char *name) { mutex_init(&bat->lock); - strncpy(bat->name, name, ARRAY_SIZE(bat->name) - 1); + strscpy(bat->name, name, sizeof(bat->name)); bat->sdev = sdev; diff --git a/drivers/power/supply/surface_charger.c b/drivers/power/supply/surface_charger.c index cabdd8da12..7a6c62d6f8 100644 --- a/drivers/power/supply/surface_charger.c +++ b/drivers/power/supply/surface_charger.c @@ -175,7 +175,7 @@ static void spwr_ac_init(struct spwr_ac_device *ac, struct ssam_device *sdev, struct ssam_event_registry registry, const char *name) { mutex_init(&ac->lock); - strncpy(ac->name, name, ARRAY_SIZE(ac->name) - 1); + strscpy(ac->name, name, sizeof(ac->name)); ac->sdev = sdev; diff --git a/drivers/power/supply/tps65090-charger.c b/drivers/power/supply/tps65090-charger.c index f96c705e0a..c59197d2aa 100644 --- a/drivers/power/supply/tps65090-charger.c +++ b/drivers/power/supply/tps65090-charger.c @@ -328,15 +328,13 @@ fail_unregister_supply: return ret; } -static int tps65090_charger_remove(struct platform_device *pdev) +static void tps65090_charger_remove(struct platform_device *pdev) { struct tps65090_charger *cdata = platform_get_drvdata(pdev); if (cdata->irq == -ENXIO) kthread_stop(cdata->poll_task); power_supply_unregister(cdata->ac); - - return 0; } static const struct of_device_id of_tps65090_charger_match[] = { @@ -351,7 +349,7 @@ static struct platform_driver tps65090_charger_driver = { .of_match_table = of_tps65090_charger_match, }, .probe = tps65090_charger_probe, - .remove = tps65090_charger_remove, + .remove_new = tps65090_charger_remove, }; module_platform_driver(tps65090_charger_driver); diff --git a/drivers/power/supply/tps65217_charger.c b/drivers/power/supply/tps65217_charger.c index 96341cbde4..2382749a2f 100644 --- a/drivers/power/supply/tps65217_charger.c +++ b/drivers/power/supply/tps65217_charger.c @@ -237,7 +237,7 @@ static int tps65217_charger_probe(struct platform_device *pdev) for (i = 0; i < NUM_CHARGER_IRQS; i++) { ret = devm_request_threaded_irq(&pdev->dev, irq[i], NULL, tps65217_charger_irq, - IRQF_ONESHOT, "tps65217-charger", + IRQF_SHARED, "tps65217-charger", charger); if (ret) { dev_err(charger->dev, @@ -253,14 +253,12 @@ static int tps65217_charger_probe(struct platform_device *pdev) return 0; } -static int tps65217_charger_remove(struct platform_device *pdev) +static void tps65217_charger_remove(struct platform_device *pdev) { struct tps65217_charger *charger = platform_get_drvdata(pdev); if (charger->poll_task) kthread_stop(charger->poll_task); - - return 0; } static const struct of_device_id tps65217_charger_match_table[] = { @@ -271,7 +269,7 @@ MODULE_DEVICE_TABLE(of, tps65217_charger_match_table); static struct platform_driver tps65217_charger_driver = { .probe = tps65217_charger_probe, - .remove = tps65217_charger_remove, + .remove_new = tps65217_charger_remove, .driver = { .name = "tps65217-charger", .of_match_table = of_match_ptr(tps65217_charger_match_table), diff --git a/drivers/power/supply/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c index e78d061d8d..7b9b0b3e16 100644 --- a/drivers/power/supply/twl4030_charger.c +++ b/drivers/power/supply/twl4030_charger.c @@ -1108,7 +1108,7 @@ static int twl4030_bci_probe(struct platform_device *pdev) return 0; } -static int twl4030_bci_remove(struct platform_device *pdev) +static void twl4030_bci_remove(struct platform_device *pdev) { struct twl4030_bci *bci = platform_get_drvdata(pdev); @@ -1123,8 +1123,6 @@ static int twl4030_bci_remove(struct platform_device *pdev) TWL4030_INTERRUPTS_BCIIMR1A); twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, TWL4030_INTERRUPTS_BCIIMR2A); - - return 0; } static const struct of_device_id twl_bci_of_match[] __maybe_unused = { @@ -1135,7 +1133,7 @@ MODULE_DEVICE_TABLE(of, twl_bci_of_match); static struct platform_driver twl4030_bci_driver = { .probe = twl4030_bci_probe, - .remove = twl4030_bci_remove, + .remove_new = twl4030_bci_remove, .driver = { .name = "twl4030_bci", .of_match_table = of_match_ptr(twl_bci_of_match), diff --git a/drivers/power/supply/twl4030_madc_battery.c b/drivers/power/supply/twl4030_madc_battery.c index 7fe029673b..33106476be 100644 --- a/drivers/power/supply/twl4030_madc_battery.c +++ b/drivers/power/supply/twl4030_madc_battery.c @@ -244,7 +244,7 @@ err: return ret; } -static int twl4030_madc_battery_remove(struct platform_device *pdev) +static void twl4030_madc_battery_remove(struct platform_device *pdev) { struct twl4030_madc_battery *bat = platform_get_drvdata(pdev); @@ -253,8 +253,6 @@ static int twl4030_madc_battery_remove(struct platform_device *pdev) iio_channel_release(bat->channel_vbat); iio_channel_release(bat->channel_ichg); iio_channel_release(bat->channel_temp); - - return 0; } static struct platform_driver twl4030_madc_battery_driver = { @@ -262,7 +260,7 @@ static struct platform_driver twl4030_madc_battery_driver = { .name = "twl4030_madc_battery", }, .probe = twl4030_madc_battery_probe, - .remove = twl4030_madc_battery_remove, + .remove_new = twl4030_madc_battery_remove, }; module_platform_driver(twl4030_madc_battery_driver); diff --git a/drivers/power/supply/wm831x_backup.c b/drivers/power/supply/wm831x_backup.c index ffb265b852..1a7265660a 100644 --- a/drivers/power/supply/wm831x_backup.c +++ b/drivers/power/supply/wm831x_backup.c @@ -197,18 +197,16 @@ static int wm831x_backup_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(devdata->backup); } -static int wm831x_backup_remove(struct platform_device *pdev) +static void wm831x_backup_remove(struct platform_device *pdev) { struct wm831x_backup *devdata = platform_get_drvdata(pdev); power_supply_unregister(devdata->backup); - - return 0; } static struct platform_driver wm831x_backup_driver = { .probe = wm831x_backup_probe, - .remove = wm831x_backup_remove, + .remove_new = wm831x_backup_remove, .driver = { .name = "wm831x-backup", }, diff --git a/drivers/power/supply/wm831x_power.c b/drivers/power/supply/wm831x_power.c index 82e31066c7..e49b01ee5f 100644 --- a/drivers/power/supply/wm831x_power.c +++ b/drivers/power/supply/wm831x_power.c @@ -694,7 +694,7 @@ err: return ret; } -static int wm831x_power_remove(struct platform_device *pdev) +static void wm831x_power_remove(struct platform_device *pdev) { struct wm831x_power *wm831x_power = platform_get_drvdata(pdev); struct wm831x *wm831x = wm831x_power->wm831x; @@ -722,12 +722,11 @@ static int wm831x_power_remove(struct platform_device *pdev) power_supply_unregister(wm831x_power->battery); power_supply_unregister(wm831x_power->wall); power_supply_unregister(wm831x_power->usb); - return 0; } static struct platform_driver wm831x_power_driver = { .probe = wm831x_power_probe, - .remove = wm831x_power_remove, + .remove_new = wm831x_power_remove, .driver = { .name = "wm831x-power", }, diff --git a/drivers/power/supply/wm8350_power.c b/drivers/power/supply/wm8350_power.c index f278676129..f23b4f5343 100644 --- a/drivers/power/supply/wm8350_power.c +++ b/drivers/power/supply/wm8350_power.c @@ -579,7 +579,7 @@ battery_failed: return ret; } -static int wm8350_power_remove(struct platform_device *pdev) +static void wm8350_power_remove(struct platform_device *pdev) { struct wm8350 *wm8350 = platform_get_drvdata(pdev); struct wm8350_power *power = &wm8350->power; @@ -589,12 +589,11 @@ static int wm8350_power_remove(struct platform_device *pdev) power_supply_unregister(power->battery); power_supply_unregister(power->ac); power_supply_unregister(power->usb); - return 0; } static struct platform_driver wm8350_power_driver = { .probe = wm8350_power_probe, - .remove = wm8350_power_remove, + .remove_new = wm8350_power_remove, .driver = { .name = "wm8350-power", }, diff --git a/drivers/power/supply/wm97xx_battery.c b/drivers/power/supply/wm97xx_battery.c index f4b190adb3..1cc38d1437 100644 --- a/drivers/power/supply/wm97xx_battery.c +++ b/drivers/power/supply/wm97xx_battery.c @@ -248,14 +248,13 @@ err3: return ret; } -static int wm97xx_bat_remove(struct platform_device *dev) +static void wm97xx_bat_remove(struct platform_device *dev) { if (charge_gpiod) free_irq(gpiod_to_irq(charge_gpiod), dev); cancel_work_sync(&bat_work); power_supply_unregister(bat_psy); kfree(prop); - return 0; } static struct platform_driver wm97xx_bat_driver = { @@ -266,7 +265,7 @@ static struct platform_driver wm97xx_bat_driver = { #endif }, .probe = wm97xx_bat_probe, - .remove = wm97xx_bat_remove, + .remove_new = wm97xx_bat_remove, }; module_platform_driver(wm97xx_bat_driver); |