diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-07-07 13:17:48 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-07-07 13:17:48 -0700 |
commit | c6e8c51f6978c7aa44641ea4e9071b42d921eb97 (patch) | |
tree | 34bf404884e8adc3809aa1c8934e16fc690fe555 /drivers | |
parent | 9d69294be2a363a0128f4dc0316a7a4bb29ea91f (diff) | |
parent | f1c74a6c07e76fcb31a4bcc1f437c4361a2674ce (diff) | |
download | linux-c6e8c51f6978c7aa44641ea4e9071b42d921eb97.tar.bz2 |
Merge tag 'for-v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply
Pull power supply and reset updates from Sebastian Reichel:
"Battery/charger driver changes:
- convert charger-manager binding to YAML
- drop bd70528-charger driver
- drop pm2301-charger driver
- introduce rt5033-battery driver
- misc improvements and fixes"
* tag 'for-v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (42 commits)
power: supply: ab8500: Fix an old bug
power: supply: axp288_fuel_gauge: remove redundant continue statement
power: supply: axp288_fuel_gauge: Make "T3 MRD" no_battery_list DMI entry more generic
power: supply: axp288_fuel_gauge: Rename fuel_gauge_blacklist to no_battery_list
power: supply: bq24190_charger: drop of_match_ptr() from device ID table
drivers: power: add missing MODULE_DEVICE_TABLE in keystone-reset.c
power: supply: ab8500: add missing MODULE_DEVICE_TABLE
power: supply: charger-manager: add missing MODULE_DEVICE_TABLE
power: reset: regulator-poweroff: add missing MODULE_DEVICE_TABLE
power: supply: cpcap-charger: get the battery inserted infomation from cpcap-battery
power: supply: cpcap-battery: invalidate config when incompatible measurements are read
power: supply: axp20x_battery: allow disabling battery charging
power: supply: max17040: drop unused platform data support
power: supply: max17040: simplify POWER_SUPPLY_PROP_ONLINE
power: supply: max17040: remove non-working POWER_SUPPLY_PROP_STATUS
power: reset: at91-sama5d2_shdwc: Remove redundant error printing in at91_shdwc_probe()
power: reset: gpio-poweroff: add missing MODULE_DEVICE_TABLE
power: supply: rt5033_battery: Fix device tree enumeration
dt-bindings: power: supply: Add DT schema for richtek,rt5033-battery
power: supply: Drop BD70528 support
...
Diffstat (limited to 'drivers')
30 files changed, 841 insertions, 2489 deletions
diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c index 125e592af445..d8ecffe72f16 100644 --- a/drivers/power/reset/at91-sama5d2_shdwc.c +++ b/drivers/power/reset/at91-sama5d2_shdwc.c @@ -351,10 +351,8 @@ static int __init at91_shdwc_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); at91_shdwc->shdwc_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(at91_shdwc->shdwc_base)) { - dev_err(&pdev->dev, "Could not map reset controller address\n"); + if (IS_ERR(at91_shdwc->shdwc_base)) return PTR_ERR(at91_shdwc->shdwc_base); - } match = of_match_node(at91_shdwc_of_match, pdev->dev.of_node); at91_shdwc->rcfg = match->data; diff --git a/drivers/power/reset/gpio-poweroff.c b/drivers/power/reset/gpio-poweroff.c index c5067eb75370..1c5af2fef142 100644 --- a/drivers/power/reset/gpio-poweroff.c +++ b/drivers/power/reset/gpio-poweroff.c @@ -90,6 +90,7 @@ static const struct of_device_id of_gpio_poweroff_match[] = { { .compatible = "gpio-poweroff", }, {}, }; +MODULE_DEVICE_TABLE(of, of_gpio_poweroff_match); static struct platform_driver gpio_poweroff_driver = { .probe = gpio_poweroff_probe, diff --git a/drivers/power/reset/keystone-reset.c b/drivers/power/reset/keystone-reset.c index 211eeef0c81a..c720112db704 100644 --- a/drivers/power/reset/keystone-reset.c +++ b/drivers/power/reset/keystone-reset.c @@ -71,6 +71,7 @@ static const struct of_device_id rsctrl_of_match[] = { {.compatible = "ti,keystone-reset", }, {}, }; +MODULE_DEVICE_TABLE(of, rsctrl_of_match); static int rsctrl_probe(struct platform_device *pdev) { diff --git a/drivers/power/reset/regulator-poweroff.c b/drivers/power/reset/regulator-poweroff.c index f697088e0ad1..20701203935f 100644 --- a/drivers/power/reset/regulator-poweroff.c +++ b/drivers/power/reset/regulator-poweroff.c @@ -64,6 +64,7 @@ static const struct of_device_id of_regulator_poweroff_match[] = { { .compatible = "regulator-poweroff", }, {}, }; +MODULE_DEVICE_TABLE(of, of_regulator_poweroff_match); static struct platform_driver regulator_poweroff_driver = { .probe = regulator_poweroff_probe, diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index e696364126f1..11f5368e810e 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -712,7 +712,8 @@ config BATTERY_GOLDFISH config BATTERY_RT5033 tristate "RT5033 fuel gauge support" - depends on MFD_RT5033 + depends on I2C + select REGMAP_I2C help This adds support for battery fuel gauge in Richtek RT5033 PMIC. The fuelgauge calculates and determines the battery state of charge @@ -760,15 +761,6 @@ config CHARGER_UCS1002 Say Y to enable support for Microchip UCS1002 Programmable USB Port Power Controller with Charger Emulation. -config CHARGER_BD70528 - tristate "ROHM bd70528 charger driver" - depends on MFD_ROHM_BD70528 - select LINEAR_RANGES - help - Say Y here to enable support for getting battery status - information and altering charger configurations from charger - block of the ROHM BD70528 Power Management IC. - config CHARGER_BD99954 tristate "ROHM bd99954 charger driver" depends on I2C diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index a7309a3d1a47..33059a91f60c 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -60,7 +60,7 @@ obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o -obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o pm2301_charger.o +obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o obj-$(CONFIG_CHARGER_CPCAP) += cpcap-charger.o obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o @@ -96,7 +96,6 @@ obj-$(CONFIG_CHARGER_CROS_USBPD) += cros_usbpd-charger.o obj-$(CONFIG_CHARGER_SC2731) += sc2731_charger.o obj-$(CONFIG_FUEL_GAUGE_SC27XX) += sc27xx_fuel_gauge.o obj-$(CONFIG_CHARGER_UCS1002) += ucs1002_power.o -obj-$(CONFIG_CHARGER_BD70528) += bd70528-charger.o obj-$(CONFIG_CHARGER_BD99954) += bd99954-charger.o obj-$(CONFIG_CHARGER_WILCO) += wilco-charger.o obj-$(CONFIG_RN5T618_POWER) += rn5t618_power.o diff --git a/drivers/power/supply/ab8500-bm.h b/drivers/power/supply/ab8500-bm.h index 41c69a4f2a1f..0c940571e5b0 100644 --- a/drivers/power/supply/ab8500-bm.h +++ b/drivers/power/supply/ab8500-bm.h @@ -506,9 +506,6 @@ struct abx500_bm_data { int usb_safety_tmr_h; int bkup_bat_v; int bkup_bat_i; - bool autopower_cfg; - bool ac_enabled; - bool usb_enabled; bool no_maintenance; bool capacity_scaling; bool chg_unknown_bat; @@ -730,4 +727,8 @@ int ab8500_bm_of_probe(struct device *dev, struct device_node *np, struct abx500_bm_data *bm); +extern struct platform_driver ab8500_fg_driver; +extern struct platform_driver ab8500_btemp_driver; +extern struct platform_driver abx500_chargalg_driver; + #endif /* _AB8500_CHARGER_H_ */ diff --git a/drivers/power/supply/ab8500-chargalg.h b/drivers/power/supply/ab8500-chargalg.h index 94a6f9068bc5..07e6ff50084f 100644 --- a/drivers/power/supply/ab8500-chargalg.h +++ b/drivers/power/supply/ab8500-chargalg.h @@ -15,7 +15,7 @@ * - POWER_SUPPLY_TYPE_USB, * because only them store as drv_data pointer to struct ux500_charger. */ -#define psy_to_ux500_charger(x) power_supply_get_drvdata(psy) +#define psy_to_ux500_charger(x) power_supply_get_drvdata(x) /* Forward declaration */ struct ux500_charger; diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index fdfcd59fc43e..dbdcff32f353 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -13,6 +13,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/device.h> +#include <linux/component.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/slab.h> @@ -932,26 +933,6 @@ static int __maybe_unused ab8500_btemp_suspend(struct device *dev) return 0; } -static int ab8500_btemp_remove(struct platform_device *pdev) -{ - struct ab8500_btemp *di = platform_get_drvdata(pdev); - int i, irq; - - /* Disable interrupts */ - for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) { - irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name); - free_irq(irq, di); - } - - /* Delete the work queue */ - destroy_workqueue(di->btemp_wq); - - flush_scheduled_work(); - power_supply_unregister(di->btemp_psy); - - return 0; -} - static char *supply_interface[] = { "ab8500_chargalg", "ab8500_fg", @@ -966,9 +947,42 @@ static const struct power_supply_desc ab8500_btemp_desc = { .external_power_changed = ab8500_btemp_external_power_changed, }; +static int ab8500_btemp_bind(struct device *dev, struct device *master, + void *data) +{ + struct ab8500_btemp *di = dev_get_drvdata(dev); + + /* Create a work queue for the btemp */ + di->btemp_wq = + alloc_workqueue("ab8500_btemp_wq", WQ_MEM_RECLAIM, 0); + if (di->btemp_wq == NULL) { + dev_err(dev, "failed to create work queue\n"); + return -ENOMEM; + } + + /* Kick off periodic temperature measurements */ + ab8500_btemp_periodic(di, true); + + return 0; +} + +static void ab8500_btemp_unbind(struct device *dev, struct device *master, + void *data) +{ + struct ab8500_btemp *di = dev_get_drvdata(dev); + + /* Delete the work queue */ + destroy_workqueue(di->btemp_wq); + flush_scheduled_work(); +} + +static const struct component_ops ab8500_btemp_component_ops = { + .bind = ab8500_btemp_bind, + .unbind = ab8500_btemp_unbind, +}; + static int ab8500_btemp_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; struct power_supply_config psy_cfg = {}; struct device *dev = &pdev->dev; struct ab8500_btemp *di; @@ -981,12 +995,6 @@ static int ab8500_btemp_probe(struct platform_device *pdev) di->bm = &ab8500_bm_data; - ret = ab8500_bm_of_probe(dev, np, di->bm); - if (ret) { - dev_err(dev, "failed to get battery information\n"); - return ret; - } - /* get parent data */ di->dev = dev; di->parent = dev_get_drvdata(pdev->dev.parent); @@ -1011,14 +1019,6 @@ static int ab8500_btemp_probe(struct platform_device *pdev) psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); psy_cfg.drv_data = di; - /* Create a work queue for the btemp */ - di->btemp_wq = - alloc_workqueue("ab8500_btemp_wq", WQ_MEM_RECLAIM, 0); - if (di->btemp_wq == NULL) { - dev_err(dev, "failed to create work queue\n"); - return -ENOMEM; - } - /* Init work for measuring temperature periodically */ INIT_DEFERRABLE_WORK(&di->btemp_periodic_work, ab8500_btemp_periodic_work); @@ -1031,7 +1031,7 @@ static int ab8500_btemp_probe(struct platform_device *pdev) AB8500_BTEMP_HIGH_TH, &val); if (ret < 0) { dev_err(dev, "%s ab8500 read failed\n", __func__); - goto free_btemp_wq; + return ret; } switch (val) { case BTEMP_HIGH_TH_57_0: @@ -1050,30 +1050,28 @@ static int ab8500_btemp_probe(struct platform_device *pdev) } /* Register BTEMP power supply class */ - di->btemp_psy = power_supply_register(dev, &ab8500_btemp_desc, - &psy_cfg); + di->btemp_psy = devm_power_supply_register(dev, &ab8500_btemp_desc, + &psy_cfg); if (IS_ERR(di->btemp_psy)) { dev_err(dev, "failed to register BTEMP psy\n"); - ret = PTR_ERR(di->btemp_psy); - goto free_btemp_wq; + return PTR_ERR(di->btemp_psy); } /* Register interrupts */ for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) { irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name); - if (irq < 0) { - ret = irq; - goto free_irq; - } + if (irq < 0) + return irq; - ret = request_threaded_irq(irq, NULL, ab8500_btemp_irq[i].isr, + ret = devm_request_threaded_irq(dev, irq, NULL, + ab8500_btemp_irq[i].isr, IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT, ab8500_btemp_irq[i].name, di); if (ret) { dev_err(dev, "failed to request %s IRQ %d: %d\n" , ab8500_btemp_irq[i].name, irq, ret); - goto free_irq; + return ret; } dev_dbg(dev, "Requested %s IRQ %d: %d\n", ab8500_btemp_irq[i].name, irq, ret); @@ -1081,23 +1079,16 @@ static int ab8500_btemp_probe(struct platform_device *pdev) platform_set_drvdata(pdev, di); - /* Kick off periodic temperature measurements */ - ab8500_btemp_periodic(di, true); list_add_tail(&di->node, &ab8500_btemp_list); - return ret; + return component_add(dev, &ab8500_btemp_component_ops); +} -free_irq: - /* We also have to free all successfully registered irqs */ - for (i = i - 1; i >= 0; i--) { - irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name); - free_irq(irq, di); - } +static int ab8500_btemp_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &ab8500_btemp_component_ops); - power_supply_unregister(di->btemp_psy); -free_btemp_wq: - destroy_workqueue(di->btemp_wq); - return ret; + return 0; } static SIMPLE_DEV_PM_OPS(ab8500_btemp_pm_ops, ab8500_btemp_suspend, ab8500_btemp_resume); @@ -1106,8 +1097,9 @@ static const struct of_device_id ab8500_btemp_match[] = { { .compatible = "stericsson,ab8500-btemp", }, { }, }; +MODULE_DEVICE_TABLE(of, ab8500_btemp_match); -static struct platform_driver ab8500_btemp_driver = { +struct platform_driver ab8500_btemp_driver = { .probe = ab8500_btemp_probe, .remove = ab8500_btemp_remove, .driver = { @@ -1116,20 +1108,6 @@ static struct platform_driver ab8500_btemp_driver = { .pm = &ab8500_btemp_pm_ops, }, }; - -static int __init ab8500_btemp_init(void) -{ - return platform_driver_register(&ab8500_btemp_driver); -} - -static void __exit ab8500_btemp_exit(void) -{ - platform_driver_unregister(&ab8500_btemp_driver); -} - -device_initcall(ab8500_btemp_init); -module_exit(ab8500_btemp_exit); - MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy"); MODULE_ALIAS("platform:ab8500-btemp"); diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index a9be10eb2c22..fa49e12e5a60 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -13,6 +13,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/device.h> +#include <linux/component.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/notifier.h> @@ -414,6 +415,14 @@ disable_otp: static void ab8500_power_supply_changed(struct ab8500_charger *di, struct power_supply *psy) { + /* + * This happens if we get notifications or interrupts and + * the platform has been configured not to support one or + * other type of charging. + */ + if (!psy) + return; + if (di->autopower_cfg) { if (!di->usb.charger_connected && !di->ac.charger_connected && @@ -440,7 +449,15 @@ static void ab8500_charger_set_usb_connected(struct ab8500_charger *di, if (!connected) di->flags.vbus_drop_end = false; - sysfs_notify(&di->usb_chg.psy->dev.kobj, NULL, "present"); + /* + * Sometimes the platform is configured not to support + * USB charging and no psy has been created, but we still + * will get these notifications. + */ + if (di->usb_chg.psy) { + sysfs_notify(&di->usb_chg.psy->dev.kobj, NULL, + "present"); + } if (connected) { mutex_lock(&di->charger_attached_mutex); @@ -3171,9 +3188,6 @@ static int ab8500_charger_usb_notifier_call(struct notifier_block *nb, enum ab8500_usb_state bm_usb_state; unsigned mA = *((unsigned *)power); - if (!di) - return NOTIFY_DONE; - if (event != USB_EVENT_VBUS) { dev_dbg(di->dev, "not a standard host, returning\n"); return NOTIFY_DONE; @@ -3276,10 +3290,74 @@ static struct notifier_block charger_nb = { .notifier_call = ab8500_external_charger_prepare, }; -static int ab8500_charger_remove(struct platform_device *pdev) +static char *supply_interface[] = { + "ab8500_chargalg", + "ab8500_fg", + "ab8500_btemp", +}; + +static const struct power_supply_desc ab8500_ac_chg_desc = { + .name = "ab8500_ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = ab8500_charger_ac_props, + .num_properties = ARRAY_SIZE(ab8500_charger_ac_props), + .get_property = ab8500_charger_ac_get_property, +}; + +static const struct power_supply_desc ab8500_usb_chg_desc = { + .name = "ab8500_usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = ab8500_charger_usb_props, + .num_properties = ARRAY_SIZE(ab8500_charger_usb_props), + .get_property = ab8500_charger_usb_get_property, +}; + +static int ab8500_charger_bind(struct device *dev) { - struct ab8500_charger *di = platform_get_drvdata(pdev); - int i, irq, ret; + struct ab8500_charger *di = dev_get_drvdata(dev); + int ch_stat; + int ret; + + /* Create a work queue for the charger */ + di->charger_wq = alloc_ordered_workqueue("ab8500_charger_wq", + WQ_MEM_RECLAIM); + if (di->charger_wq == NULL) { + dev_err(dev, "failed to create work queue\n"); + return -ENOMEM; + } + + ch_stat = ab8500_charger_detect_chargers(di, false); + + if (ch_stat & AC_PW_CONN) { + if (is_ab8500(di->parent)) + queue_delayed_work(di->charger_wq, + &di->ac_charger_attached_work, + HZ); + } + if (ch_stat & USB_PW_CONN) { + if (is_ab8500(di->parent)) + queue_delayed_work(di->charger_wq, + &di->usb_charger_attached_work, + HZ); + di->vbus_detected = true; + di->vbus_detected_start = true; + queue_work(di->charger_wq, + &di->detect_usb_type_work); + } + + ret = component_bind_all(dev, di); + if (ret) { + dev_err(dev, "can't bind component devices\n"); + return ret; + } + + return 0; +} + +static void ab8500_charger_unbind(struct device *dev) +{ + struct ab8500_charger *di = dev_get_drvdata(dev); + int ret; /* Disable AC charging */ ab8500_charger_ac_en(&di->ac_chg, false, 0, 0); @@ -3287,68 +3365,47 @@ static int ab8500_charger_remove(struct platform_device *pdev) /* Disable USB charging */ ab8500_charger_usb_en(&di->usb_chg, false, 0, 0); - /* Disable interrupts */ - for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) { - irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name); - free_irq(irq, di); - } - /* Backup battery voltage and current disable */ ret = abx500_mask_and_set_register_interruptible(di->dev, AB8500_RTC, AB8500_RTC_CTRL_REG, RTC_BUP_CH_ENA, 0); if (ret < 0) dev_err(di->dev, "%s mask and set failed\n", __func__); - usb_unregister_notifier(di->usb_phy, &di->nb); - usb_put_phy(di->usb_phy); - /* Delete the work queue */ destroy_workqueue(di->charger_wq); - /* Unregister external charger enable notifier */ - if (!di->ac_chg.enabled) - blocking_notifier_chain_unregister( - &charger_notifier_list, &charger_nb); - flush_scheduled_work(); - if (di->usb_chg.enabled) - power_supply_unregister(di->usb_chg.psy); - if (di->ac_chg.enabled && !di->ac_chg.external) - power_supply_unregister(di->ac_chg.psy); - - return 0; + /* Unbind fg, btemp, algorithm */ + component_unbind_all(dev, di); } -static char *supply_interface[] = { - "ab8500_chargalg", - "ab8500_fg", - "ab8500_btemp", +static const struct component_master_ops ab8500_charger_comp_ops = { + .bind = ab8500_charger_bind, + .unbind = ab8500_charger_unbind, }; -static const struct power_supply_desc ab8500_ac_chg_desc = { - .name = "ab8500_ac", - .type = POWER_SUPPLY_TYPE_MAINS, - .properties = ab8500_charger_ac_props, - .num_properties = ARRAY_SIZE(ab8500_charger_ac_props), - .get_property = ab8500_charger_ac_get_property, +static struct platform_driver *const ab8500_charger_component_drivers[] = { + &ab8500_fg_driver, + &ab8500_btemp_driver, + &abx500_chargalg_driver, }; -static const struct power_supply_desc ab8500_usb_chg_desc = { - .name = "ab8500_usb", - .type = POWER_SUPPLY_TYPE_USB, - .properties = ab8500_charger_usb_props, - .num_properties = ARRAY_SIZE(ab8500_charger_usb_props), - .get_property = ab8500_charger_usb_get_property, -}; +static int ab8500_charger_compare_dev(struct device *dev, void *data) +{ + return dev == data; +} static int ab8500_charger_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct component_match *match = NULL; struct power_supply_config ac_psy_cfg = {}, usb_psy_cfg = {}; struct ab8500_charger *di; - int irq, i, charger_status, ret = 0, ch_stat; - struct device *dev = &pdev->dev; + int charger_status; + int i, irq; + int ret; di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); if (!di) @@ -3393,6 +3450,38 @@ static int ab8500_charger_probe(struct platform_device *pdev) return ret; } + /* + * VDD ADC supply needs to be enabled from this driver when there + * is a charger connected to avoid erroneous BTEMP_HIGH/LOW + * interrupts during charging + */ + di->regu = devm_regulator_get(dev, "vddadc"); + if (IS_ERR(di->regu)) { + ret = PTR_ERR(di->regu); + dev_err(dev, "failed to get vddadc regulator\n"); + return ret; + } + + /* Request interrupts */ + for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) { + irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, + irq, NULL, ab8500_charger_irq[i].isr, + IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT, + ab8500_charger_irq[i].name, di); + + if (ret != 0) { + dev_err(dev, "failed to request %s IRQ %d: %d\n" + , ab8500_charger_irq[i].name, irq, ret); + return ret; + } + dev_dbg(dev, "Requested %s IRQ %d: %d\n", + ab8500_charger_irq[i].name, irq, ret); + } + /* initialize lock */ spin_lock_init(&di->usb_state.usb_lock); mutex_init(&di->usb_ipt_crnt_lock); @@ -3419,14 +3508,16 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->ac_chg.max_out_curr = di->bm->chg_output_curr[di->bm->n_chg_out_curr - 1]; di->ac_chg.wdt_refresh = CHG_WD_INTERVAL; - di->ac_chg.enabled = di->bm->ac_enabled; + /* + * The AB8505 only supports USB charging. If we are not the + * AB8505, register an AC charger. + * + * TODO: if this should be opt-in, add DT properties for this. + */ + if (!is_ab8505(di->parent)) + di->ac_chg.enabled = true; di->ac_chg.external = false; - /*notifier for external charger enabling*/ - if (!di->ac_chg.enabled) - blocking_notifier_chain_register( - &charger_notifier_list, &charger_nb); - /* USB supply */ /* ux500_charger sub-class */ di->usb_chg.ops.enable = &ab8500_charger_usb_en; @@ -3438,18 +3529,9 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->usb_chg.max_out_curr = di->bm->chg_output_curr[di->bm->n_chg_out_curr - 1]; di->usb_chg.wdt_refresh = CHG_WD_INTERVAL; - di->usb_chg.enabled = di->bm->usb_enabled; di->usb_chg.external = false; di->usb_state.usb_current = -1; - /* Create a work queue for the charger */ - di->charger_wq = alloc_ordered_workqueue("ab8500_charger_wq", - WQ_MEM_RECLAIM); - if (di->charger_wq == NULL) { - dev_err(dev, "failed to create work queue\n"); - return -ENOMEM; - } - mutex_init(&di->charger_attached_mutex); /* Init work for HW failure check */ @@ -3500,61 +3582,32 @@ static int ab8500_charger_probe(struct platform_device *pdev) INIT_WORK(&di->check_usb_thermal_prot_work, ab8500_charger_check_usb_thermal_prot_work); - /* - * VDD ADC supply needs to be enabled from this driver when there - * is a charger connected to avoid erroneous BTEMP_HIGH/LOW - * interrupts during charging - */ - di->regu = devm_regulator_get(dev, "vddadc"); - if (IS_ERR(di->regu)) { - ret = PTR_ERR(di->regu); - dev_err(dev, "failed to get vddadc regulator\n"); - goto free_charger_wq; - } - /* Initialize OVV, and other registers */ ret = ab8500_charger_init_hw_registers(di); if (ret) { dev_err(dev, "failed to initialize ABB registers\n"); - goto free_charger_wq; + return ret; } /* Register AC charger class */ if (di->ac_chg.enabled) { - di->ac_chg.psy = power_supply_register(dev, + di->ac_chg.psy = devm_power_supply_register(dev, &ab8500_ac_chg_desc, &ac_psy_cfg); if (IS_ERR(di->ac_chg.psy)) { dev_err(dev, "failed to register AC charger\n"); - ret = PTR_ERR(di->ac_chg.psy); - goto free_charger_wq; + return PTR_ERR(di->ac_chg.psy); } } /* Register USB charger class */ - if (di->usb_chg.enabled) { - di->usb_chg.psy = power_supply_register(dev, - &ab8500_usb_chg_desc, - &usb_psy_cfg); - if (IS_ERR(di->usb_chg.psy)) { - dev_err(dev, "failed to register USB charger\n"); - ret = PTR_ERR(di->usb_chg.psy); - goto free_ac; - } - } - - di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2); - if (IS_ERR_OR_NULL(di->usb_phy)) { - dev_err(dev, "failed to get usb transceiver\n"); - ret = -EINVAL; - goto free_usb; - } - di->nb.notifier_call = ab8500_charger_usb_notifier_call; - ret = usb_register_notifier(di->usb_phy, &di->nb); - if (ret) { - dev_err(dev, "failed to register usb notifier\n"); - goto put_usb_phy; + di->usb_chg.psy = devm_power_supply_register(dev, + &ab8500_usb_chg_desc, + &usb_psy_cfg); + if (IS_ERR(di->usb_chg.psy)) { + dev_err(dev, "failed to register USB charger\n"); + return PTR_ERR(di->usb_chg.psy); } /* Identify the connected charger types during startup */ @@ -3566,84 +3619,93 @@ static int ab8500_charger_probe(struct platform_device *pdev) sysfs_notify(&di->ac_chg.psy->dev.kobj, NULL, "present"); } - if (charger_status & USB_PW_CONN) { - di->vbus_detected = true; - di->vbus_detected_start = true; - queue_work(di->charger_wq, - &di->detect_usb_type_work); - } - - /* Register interrupts */ - for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) { - irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name); - if (irq < 0) { - ret = irq; - goto free_irq; - } + platform_set_drvdata(pdev, di); - ret = request_threaded_irq(irq, NULL, ab8500_charger_irq[i].isr, - IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT, - ab8500_charger_irq[i].name, di); + /* Create something that will match the subdrivers when we bind */ + for (i = 0; i < ARRAY_SIZE(ab8500_charger_component_drivers); i++) { + struct device_driver *drv = &ab8500_charger_component_drivers[i]->driver; + struct device *p = NULL, *d; - if (ret != 0) { - dev_err(dev, "failed to request %s IRQ %d: %d\n" - , ab8500_charger_irq[i].name, irq, ret); - goto free_irq; + while ((d = platform_find_device_by_driver(p, drv))) { + put_device(p); + component_match_add(dev, &match, + ab8500_charger_compare_dev, d); + p = d; } - dev_dbg(dev, "Requested %s IRQ %d: %d\n", - ab8500_charger_irq[i].name, irq, ret); + put_device(p); + } + if (!match) { + dev_err(dev, "no matching components\n"); + return -ENODEV; + } + if (IS_ERR(match)) { + dev_err(dev, "could not create component match\n"); + return PTR_ERR(match); } - platform_set_drvdata(pdev, di); - - mutex_lock(&di->charger_attached_mutex); + /* Notifier for external charger enabling */ + if (!di->ac_chg.enabled) + blocking_notifier_chain_register( + &charger_notifier_list, &charger_nb); - ch_stat = ab8500_charger_detect_chargers(di, false); - if ((ch_stat & AC_PW_CONN) == AC_PW_CONN) { - if (is_ab8500(di->parent)) - queue_delayed_work(di->charger_wq, - &di->ac_charger_attached_work, - HZ); + di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2); + if (IS_ERR_OR_NULL(di->usb_phy)) { + dev_err(dev, "failed to get usb transceiver\n"); + ret = -EINVAL; + goto out_charger_notifier; } - if ((ch_stat & USB_PW_CONN) == USB_PW_CONN) { - if (is_ab8500(di->parent)) - queue_delayed_work(di->charger_wq, - &di->usb_charger_attached_work, - HZ); + di->nb.notifier_call = ab8500_charger_usb_notifier_call; + ret = usb_register_notifier(di->usb_phy, &di->nb); + if (ret) { + dev_err(dev, "failed to register usb notifier\n"); + goto put_usb_phy; } - mutex_unlock(&di->charger_attached_mutex); - return ret; + ret = component_master_add_with_match(&pdev->dev, + &ab8500_charger_comp_ops, + match); + if (ret) { + dev_err(dev, "failed to add component master\n"); + goto free_notifier; + } -free_irq: - usb_unregister_notifier(di->usb_phy, &di->nb); + return 0; - /* We also have to free all successfully registered irqs */ - for (i = i - 1; i >= 0; i--) { - irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name); - free_irq(irq, di); - } +free_notifier: + usb_unregister_notifier(di->usb_phy, &di->nb); put_usb_phy: usb_put_phy(di->usb_phy); -free_usb: - if (di->usb_chg.enabled) - power_supply_unregister(di->usb_chg.psy); -free_ac: - if (di->ac_chg.enabled) - power_supply_unregister(di->ac_chg.psy); -free_charger_wq: - destroy_workqueue(di->charger_wq); +out_charger_notifier: + if (!di->ac_chg.enabled) + blocking_notifier_chain_unregister( + &charger_notifier_list, &charger_nb); return ret; } +static int ab8500_charger_remove(struct platform_device *pdev) +{ + struct ab8500_charger *di = platform_get_drvdata(pdev); + + component_master_del(&pdev->dev, &ab8500_charger_comp_ops); + + usb_unregister_notifier(di->usb_phy, &di->nb); + usb_put_phy(di->usb_phy); + if (!di->ac_chg.enabled) + blocking_notifier_chain_unregister( + &charger_notifier_list, &charger_nb); + + return 0; +} + static SIMPLE_DEV_PM_OPS(ab8500_charger_pm_ops, ab8500_charger_suspend, ab8500_charger_resume); static const struct of_device_id ab8500_charger_match[] = { { .compatible = "stericsson,ab8500-charger", }, { }, }; +MODULE_DEVICE_TABLE(of, ab8500_charger_match); static struct platform_driver ab8500_charger_driver = { .probe = ab8500_charger_probe, @@ -3657,15 +3719,24 @@ static struct platform_driver ab8500_charger_driver = { static int __init ab8500_charger_init(void) { + int ret; + + ret = platform_register_drivers(ab8500_charger_component_drivers, + ARRAY_SIZE(ab8500_charger_component_drivers)); + if (ret) + return ret; + return platform_driver_register(&ab8500_charger_driver); } static void __exit ab8500_charger_exit(void) { + platform_unregister_drivers(ab8500_charger_component_drivers, + ARRAY_SIZE(ab8500_charger_component_drivers)); platform_driver_unregister(&ab8500_charger_driver); } -subsys_initcall_sync(ab8500_charger_init); +module_init(ab8500_charger_init); module_exit(ab8500_charger_exit); MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index 0c7c01a0d979..3d45ed0157c6 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/module.h> +#include <linux/component.h> #include <linux/device.h> #include <linux/interrupt.h> #include <linux/platform_device.h> @@ -59,7 +60,7 @@ ((y1) + ((((y2) - (y1)) * ((x) - (x1))) / ((x2) - (x1)))); /** - * struct ab8500_fg_interrupts - ab8500 fg interupts + * struct ab8500_fg_interrupts - ab8500 fg interrupts * @name: name of the interrupt * @isr function pointer to the isr */ @@ -2980,27 +2981,6 @@ static int __maybe_unused ab8500_fg_suspend(struct device *dev) return 0; } -static int ab8500_fg_remove(struct platform_device *pdev) -{ - int ret = 0; - struct ab8500_fg *di = platform_get_drvdata(pdev); - - list_del(&di->node); - - /* Disable coulomb counter */ - ret = ab8500_fg_coulomb_counter(di, false); - if (ret) - dev_err(di->dev, "failed to disable coulomb counter\n"); - - destroy_workqueue(di->fg_wq); - ab8500_fg_sysfs_exit(di); - - flush_scheduled_work(); - ab8500_fg_sysfs_psy_remove_attrs(di); - power_supply_unregister(di->fg_psy); - return ret; -} - /* ab8500 fg driver interrupts and their respective isr */ static struct ab8500_fg_interrupts ab8500_fg_irq[] = { {"NCONV_ACCU", ab8500_fg_cc_convend_handler}, @@ -3024,11 +3004,50 @@ static const struct power_supply_desc ab8500_fg_desc = { .external_power_changed = ab8500_fg_external_power_changed, }; +static int ab8500_fg_bind(struct device *dev, struct device *master, + void *data) +{ + struct ab8500_fg *di = dev_get_drvdata(dev); + + /* Create a work queue for running the FG algorithm */ + di->fg_wq = alloc_ordered_workqueue("ab8500_fg_wq", WQ_MEM_RECLAIM); + if (di->fg_wq == NULL) { + dev_err(dev, "failed to create work queue\n"); + return -ENOMEM; + } + + /* Start the coulomb counter */ + ab8500_fg_coulomb_counter(di, true); + /* Run the FG algorithm */ + queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); + + return 0; +} + +static void ab8500_fg_unbind(struct device *dev, struct device *master, + void *data) +{ + struct ab8500_fg *di = dev_get_drvdata(dev); + int ret; + + /* Disable coulomb counter */ + ret = ab8500_fg_coulomb_counter(di, false); + if (ret) + dev_err(dev, "failed to disable coulomb counter\n"); + + destroy_workqueue(di->fg_wq); + flush_scheduled_work(); +} + +static const struct component_ops ab8500_fg_component_ops = { + .bind = ab8500_fg_bind, + .unbind = ab8500_fg_unbind, +}; + static int ab8500_fg_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; - struct power_supply_config psy_cfg = {}; struct device *dev = &pdev->dev; + struct power_supply_config psy_cfg = {}; struct ab8500_fg *di; int i, irq; int ret = 0; @@ -3039,12 +3058,6 @@ static int ab8500_fg_probe(struct platform_device *pdev) di->bm = &ab8500_bm_data; - ret = ab8500_bm_of_probe(dev, np, di->bm); - if (ret) { - dev_err(dev, "failed to get battery information\n"); - return ret; - } - mutex_init(&di->cc_lock); /* get parent data */ @@ -3074,13 +3087,6 @@ static int ab8500_fg_probe(struct platform_device *pdev) ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT); ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT); - /* Create a work queue for running the FG algorithm */ - di->fg_wq = alloc_ordered_workqueue("ab8500_fg_wq", WQ_MEM_RECLAIM); - if (di->fg_wq == NULL) { - dev_err(dev, "failed to create work queue\n"); - return -ENOMEM; - } - /* Init work for running the fg algorithm instantly */ INIT_WORK(&di->fg_work, ab8500_fg_instant_work); @@ -3113,7 +3119,7 @@ static int ab8500_fg_probe(struct platform_device *pdev) ret = ab8500_fg_init_hw_registers(di); if (ret) { dev_err(dev, "failed to initialize registers\n"); - goto free_inst_curr_wq; + return ret; } /* Consider battery unknown until we're informed otherwise */ @@ -3121,15 +3127,13 @@ static int ab8500_fg_probe(struct platform_device *pdev) di->flags.batt_id_received = false; /* Register FG power supply class */ - di->fg_psy = power_supply_register(dev, &ab8500_fg_desc, &psy_cfg); + di->fg_psy = devm_power_supply_register(dev, &ab8500_fg_desc, &psy_cfg); if (IS_ERR(di->fg_psy)) { dev_err(dev, "failed to register FG psy\n"); - ret = PTR_ERR(di->fg_psy); - goto free_inst_curr_wq; + return PTR_ERR(di->fg_psy); } di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer); - ab8500_fg_coulomb_counter(di, true); /* * Initialize completion used to notify completion and start @@ -3141,19 +3145,18 @@ static int ab8500_fg_probe(struct platform_device *pdev) /* Register primary interrupt handlers */ for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq); i++) { irq = platform_get_irq_byname(pdev, ab8500_fg_irq[i].name); - if (irq < 0) { - ret = irq; - goto free_irq; - } + if (irq < 0) + return irq; - ret = request_threaded_irq(irq, NULL, ab8500_fg_irq[i].isr, + ret = devm_request_threaded_irq(dev, irq, NULL, + ab8500_fg_irq[i].isr, IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT, ab8500_fg_irq[i].name, di); if (ret != 0) { dev_err(dev, "failed to request %s IRQ %d: %d\n", ab8500_fg_irq[i].name, irq, ret); - goto free_irq; + return ret; } dev_dbg(dev, "Requested %s IRQ %d: %d\n", ab8500_fg_irq[i].name, irq, ret); @@ -3168,14 +3171,14 @@ static int ab8500_fg_probe(struct platform_device *pdev) ret = ab8500_fg_sysfs_init(di); if (ret) { dev_err(dev, "failed to create sysfs entry\n"); - goto free_irq; + return ret; } ret = ab8500_fg_sysfs_psy_create_attrs(di); if (ret) { dev_err(dev, "failed to create FG psy\n"); ab8500_fg_sysfs_exit(di); - goto free_irq; + return ret; } /* Calibrate the fg first time */ @@ -3185,24 +3188,21 @@ static int ab8500_fg_probe(struct platform_device *pdev) /* Use room temp as default value until we get an update from driver. */ di->bat_temp = 210; - /* Run the FG algorithm */ - queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); - list_add_tail(&di->node, &ab8500_fg_list); - return ret; + return component_add(dev, &ab8500_fg_component_ops); +} -free_irq: - /* We also have to free all registered irqs */ - while (--i >= 0) { - /* Last assignment of i from primary interrupt handlers */ - irq = platform_get_irq_byname(pdev, ab8500_fg_irq[i].name); - free_irq(irq, di); - } +static int ab8500_fg_remove(struct platform_device *pdev) +{ + int ret = 0; + struct ab8500_fg *di = platform_get_drvdata(pdev); + + component_del(&pdev->dev, &ab8500_fg_component_ops); + list_del(&di->node); + ab8500_fg_sysfs_exit(di); + ab8500_fg_sysfs_psy_remove_attrs(di); - power_supply_unregister(di->fg_psy); -free_inst_curr_wq: - destroy_workqueue(di->fg_wq); return ret; } @@ -3212,8 +3212,9 @@ static const struct of_device_id ab8500_fg_match[] = { { .compatible = "stericsson,ab8500-fg", }, { }, }; +MODULE_DEVICE_TABLE(of, ab8500_fg_match); -static struct platform_driver ab8500_fg_driver = { +struct platform_driver ab8500_fg_driver = { .probe = ab8500_fg_probe, .remove = ab8500_fg_remove, .driver = { @@ -3222,20 +3223,6 @@ static struct platform_driver ab8500_fg_driver = { .pm = &ab8500_fg_pm_ops, }, }; - -static int __init ab8500_fg_init(void) -{ - return platform_driver_register(&ab8500_fg_driver); -} - -static void __exit ab8500_fg_exit(void) -{ - platform_driver_unregister(&ab8500_fg_driver); -} - -subsys_initcall_sync(ab8500_fg_init); -module_exit(ab8500_fg_exit); - MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Johan Palsson, Karl Komierowski"); MODULE_ALIAS("platform:ab8500-fg"); diff --git a/drivers/power/supply/abx500_chargalg.c b/drivers/power/supply/abx500_chargalg.c index f5b792243727..a17849bfacbf 100644 --- a/drivers/power/supply/abx500_chargalg.c +++ b/drivers/power/supply/abx500_chargalg.c @@ -15,6 +15,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/device.h> +#include <linux/component.h> #include <linux/hrtimer.h> #include <linux/interrupt.h> #include <linux/delay.h> @@ -1943,13 +1944,44 @@ static int __maybe_unused abx500_chargalg_suspend(struct device *dev) return 0; } -static int abx500_chargalg_remove(struct platform_device *pdev) +static char *supply_interface[] = { + "ab8500_fg", +}; + +static const struct power_supply_desc abx500_chargalg_desc = { + .name = "abx500_chargalg", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = abx500_chargalg_props, + .num_properties = ARRAY_SIZE(abx500_chargalg_props), + .get_property = abx500_chargalg_get_property, + .external_power_changed = abx500_chargalg_external_power_changed, +}; + +static int abx500_chargalg_bind(struct device *dev, struct device *master, + void *data) { - struct abx500_chargalg *di = platform_get_drvdata(pdev); + struct abx500_chargalg *di = dev_get_drvdata(dev); - /* sysfs interface to enable/disbale charging from user space */ - abx500_chargalg_sysfs_exit(di); + /* Create a work queue for the chargalg */ + di->chargalg_wq = alloc_ordered_workqueue("abx500_chargalg_wq", + WQ_MEM_RECLAIM); + if (di->chargalg_wq == NULL) { + dev_err(di->dev, "failed to create work queue\n"); + return -ENOMEM; + } + + /* Run the charging algorithm */ + queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0); + return 0; +} + +static void abx500_chargalg_unbind(struct device *dev, struct device *master, + void *data) +{ + struct abx500_chargalg *di = dev_get_drvdata(dev); + + /* Stop all timers and work */ hrtimer_cancel(&di->safety_timer); hrtimer_cancel(&di->maintenance_timer); @@ -1959,48 +1991,29 @@ static int abx500_chargalg_remove(struct platform_device *pdev) /* Delete the work queue */ destroy_workqueue(di->chargalg_wq); - - power_supply_unregister(di->chargalg_psy); - - return 0; + flush_scheduled_work(); } -static char *supply_interface[] = { - "ab8500_fg", -}; - -static const struct power_supply_desc abx500_chargalg_desc = { - .name = "abx500_chargalg", - .type = POWER_SUPPLY_TYPE_BATTERY, - .properties = abx500_chargalg_props, - .num_properties = ARRAY_SIZE(abx500_chargalg_props), - .get_property = abx500_chargalg_get_property, - .external_power_changed = abx500_chargalg_external_power_changed, +static const struct component_ops abx500_chargalg_component_ops = { + .bind = abx500_chargalg_bind, + .unbind = abx500_chargalg_unbind, }; static int abx500_chargalg_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; struct power_supply_config psy_cfg = {}; struct abx500_chargalg *di; int ret = 0; - di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); - if (!di) { - dev_err(&pdev->dev, "%s no mem for ab8500_chargalg\n", __func__); + di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); + if (!di) return -ENOMEM; - } di->bm = &ab8500_bm_data; - ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm); - if (ret) { - dev_err(&pdev->dev, "failed to get battery information\n"); - return ret; - } - /* get device struct and parent */ - di->dev = &pdev->dev; + di->dev = dev; di->parent = dev_get_drvdata(pdev->dev.parent); psy_cfg.supplied_to = supply_interface; @@ -2016,14 +2029,6 @@ static int abx500_chargalg_probe(struct platform_device *pdev) di->maintenance_timer.function = abx500_chargalg_maintenance_timer_expired; - /* Create a work queue for the chargalg */ - di->chargalg_wq = alloc_ordered_workqueue("abx500_chargalg_wq", - WQ_MEM_RECLAIM); - if (di->chargalg_wq == NULL) { - dev_err(di->dev, "failed to create work queue\n"); - return -ENOMEM; - } - /* Init work for chargalg */ INIT_DEFERRABLE_WORK(&di->chargalg_periodic_work, abx500_chargalg_periodic_work); @@ -2037,12 +2042,12 @@ static int abx500_chargalg_probe(struct platform_device *pdev) di->chg_info.prev_conn_chg = -1; /* Register chargalg power supply class */ - di->chargalg_psy = power_supply_register(di->dev, &abx500_chargalg_desc, + di->chargalg_psy = devm_power_supply_register(di->dev, + &abx500_chargalg_desc, &psy_cfg); if (IS_ERR(di->chargalg_psy)) { dev_err(di->dev, "failed to register chargalg psy\n"); - ret = PTR_ERR(di->chargalg_psy); - goto free_chargalg_wq; + return PTR_ERR(di->chargalg_psy); } platform_set_drvdata(pdev, di); @@ -2051,21 +2056,24 @@ static int abx500_chargalg_probe(struct platform_device *pdev) ret = abx500_chargalg_sysfs_init(di); if (ret) { dev_err(di->dev, "failed to create sysfs entry\n"); - goto free_psy; + return ret; } di->curr_status.curr_step = CHARGALG_CURR_STEP_HIGH; - /* Run the charging algorithm */ - queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0); - dev_info(di->dev, "probe success\n"); - return ret; + return component_add(dev, &abx500_chargalg_component_ops); +} -free_psy: - power_supply_unregister(di->chargalg_psy); -free_chargalg_wq: - destroy_workqueue(di->chargalg_wq); - return ret; +static int abx500_chargalg_remove(struct platform_device *pdev) +{ + struct abx500_chargalg *di = platform_get_drvdata(pdev); + + component_del(&pdev->dev, &abx500_chargalg_component_ops); + + /* sysfs interface to enable/disable charging from user space */ + abx500_chargalg_sysfs_exit(di); + + return 0; } static SIMPLE_DEV_PM_OPS(abx500_chargalg_pm_ops, abx500_chargalg_suspend, abx500_chargalg_resume); @@ -2075,7 +2083,7 @@ static const struct of_device_id ab8500_chargalg_match[] = { { }, }; -static struct platform_driver abx500_chargalg_driver = { +struct platform_driver abx500_chargalg_driver = { .probe = abx500_chargalg_probe, .remove = abx500_chargalg_remove, .driver = { @@ -2084,9 +2092,6 @@ static struct platform_driver abx500_chargalg_driver = { .pm = &abx500_chargalg_pm_ops, }, }; - -module_platform_driver(abx500_chargalg_driver); - MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Johan Palsson, Karl Komierowski"); MODULE_ALIAS("platform:abx500-chargalg"); diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c index e84b6e4da14a..18a9db0df4b1 100644 --- a/drivers/power/supply/axp20x_battery.c +++ b/drivers/power/supply/axp20x_battery.c @@ -40,6 +40,7 @@ #define AXP209_FG_PERCENT GENMASK(6, 0) #define AXP22X_FG_VALID BIT(7) +#define AXP20X_CHRG_CTRL1_ENABLE BIT(7) #define AXP20X_CHRG_CTRL1_TGT_VOLT GENMASK(6, 5) #define AXP20X_CHRG_CTRL1_TGT_4_1V (0 << 5) #define AXP20X_CHRG_CTRL1_TGT_4_15V (1 << 5) @@ -468,7 +469,18 @@ static int axp20x_battery_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: return axp20x_set_max_constant_charge_current(axp20x_batt, val->intval); - + case POWER_SUPPLY_PROP_STATUS: + switch (val->intval) { + case POWER_SUPPLY_STATUS_CHARGING: + return regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, + AXP20X_CHRG_CTRL1_ENABLE, AXP20X_CHRG_CTRL1_ENABLE); + + case POWER_SUPPLY_STATUS_DISCHARGING: + case POWER_SUPPLY_STATUS_NOT_CHARGING: + return regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, + AXP20X_CHRG_CTRL1_ENABLE, 0); + } + fallthrough; default: return -EINVAL; } @@ -491,7 +503,8 @@ static enum power_supply_property axp20x_battery_props[] = { static int axp20x_battery_prop_writeable(struct power_supply *psy, enum power_supply_property psp) { - return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN || + return psp == POWER_SUPPLY_PROP_STATUS || + psp == POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN || psp == POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN || psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT || psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX; diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index 39e16ecb7638..2ba2d8d6b8e6 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -142,9 +142,7 @@ static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg) for (i = 0; i < NR_RETRY_CNT; i++) { ret = regmap_read(info->regmap, reg, &val); - if (ret == -EBUSY) - continue; - else + if (ret != -EBUSY) break; } @@ -676,7 +674,7 @@ intr_failed: * detection reports one despite it not being there. * Please keep this listed sorted alphabetically. */ -static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = { +static const struct dmi_system_id axp288_no_battery_list[] = { { /* ACEPC T8 Cherry Trail Z8350 mini PC */ .matches = { @@ -723,15 +721,6 @@ static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = { DMI_MATCH(DMI_PRODUCT_NAME, "MEEGOPAD T02"), }, }, - { - /* Meegopad T08 */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Default string"), - DMI_MATCH(DMI_BOARD_VENDOR, "To be filled by OEM."), - DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"), - DMI_MATCH(DMI_BOARD_VERSION, "V1.1"), - }, - }, { /* Mele PCG03 Mini PC */ .matches = { DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Mini PC"), @@ -745,6 +734,15 @@ static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"), } }, + { + /* Various Ace PC/Meegopad/MinisForum/Wintel Mini-PCs/HDMI-sticks */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"), + DMI_MATCH(DMI_CHASSIS_TYPE, "3"), + DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), + DMI_MATCH(DMI_BIOS_VERSION, "5.11"), + }, + }, {} }; @@ -764,7 +762,7 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) }; unsigned int val; - if (dmi_check_system(axp288_fuel_gauge_blacklist)) + if (dmi_check_system(axp288_no_battery_list)) return -ENODEV; /* diff --git a/drivers/power/supply/bd70528-charger.c b/drivers/power/supply/bd70528-charger.c deleted file mode 100644 index 7c1f0b99c71b..000000000000 --- a/drivers/power/supply/bd70528-charger.c +++ /dev/null @@ -1,710 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// -// Copyright (C) 2018 ROHM Semiconductors -// -// power-supply driver for ROHM BD70528 PMIC - -/* - * BD70528 charger HW state machine. - * - * The thermal shutdown state is not drawn. From any other state but - * battery error and suspend it is possible to go to TSD/TMP states - * if temperature is out of bounds. - * - * CHG_RST = H - * or CHG_EN=L - * or (DCIN2_UVLO=L && DCIN1_UVLO=L) - * or (DCIN2_OVLO=H & DCIN1_UVKLO=L) - * - * +--------------+ +--------------+ - * | | | | - * | Any state +-------> | Suspend | - * | | | | - * +--------------+ +------+-------+ - * | - * CHG_EN = H && BAT_DET = H && | - * No errors (temp, bat_ov, UVLO, | - * OVLO...) | - * | - * BAT_OV or +---------v----------+ - * (DBAT && TTRI) | | - * +-----------------+ Trickle Charge | <---------------+ - * | | | | - * | +-------+------------+ | - * | | | - * | | ^ | - * | V_BAT > VTRI_TH | | VBAT < VTRI_TH - 50mV | - * | | | | - * | v | | - * | | | - * | BAT_OV or +----------+----+ | - * | (DBAT && TFST) | | | - * | +----------------+ Fast Charge | | - * | | | | | - * v v +----+----------+ | - * | | - *+----------------+ ILIM_DET=L | ^ ILIM_DET | - *| | & CV_DET=H | | or CV_DET=L | - *| Battery Error | & VBAT > | | or VBAT < VRECHG_TH | - *| | VRECHG_TH | | or IBAT > IFST/x | - *+----------------+ & IBAT < | | | - * IFST/x v | | - * ^ | | - * | +---------+-+ | - * | | | | - * +-------------------+ Top OFF | | - * BAT_OV = H or | | | - * (DBAT && TFST) +-----+-----+ | - * | | - * Stay top-off for 15s | | - * v | - * | - * +--------+ | - * | | | - * | Done +-------------------------+ - * | | - * +--------+ VBAT < VRECHG_TH - */ - -#include <linux/kernel.h> -#include <linux/interrupt.h> -#include <linux/mfd/rohm-bd70528.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/power_supply.h> -#include <linux/linear_range.h> - -#define CHG_STAT_SUSPEND 0x0 -#define CHG_STAT_TRICKLE 0x1 -#define CHG_STAT_FAST 0x3 -#define CHG_STAT_TOPOFF 0xe -#define CHG_STAT_DONE 0xf -#define CHG_STAT_OTP_TRICKLE 0x10 -#define CHG_STAT_OTP_FAST 0x11 -#define CHG_STAT_OTP_DONE 0x12 -#define CHG_STAT_TSD_TRICKLE 0x20 -#define CHG_STAT_TSD_FAST 0x21 -#define CHG_STAT_TSD_TOPOFF 0x22 -#define CHG_STAT_BAT_ERR 0x7f - -static const char *bd70528_charger_model = "BD70528"; -static const char *bd70528_charger_manufacturer = "ROHM Semiconductors"; - -#define BD_ERR_IRQ_HND(_name_, _wrn_) \ -static irqreturn_t bd0528_##_name_##_interrupt(int irq, void *arg) \ -{ \ - struct power_supply *psy = (struct power_supply *)arg; \ - \ - power_supply_changed(psy); \ - dev_err(&psy->dev, (_wrn_)); \ - \ - return IRQ_HANDLED; \ -} - -#define BD_INFO_IRQ_HND(_name_, _wrn_) \ -static irqreturn_t bd0528_##_name_##_interrupt(int irq, void *arg) \ -{ \ - struct power_supply *psy = (struct power_supply *)arg; \ - \ - power_supply_changed(psy); \ - dev_dbg(&psy->dev, (_wrn_)); \ - \ - return IRQ_HANDLED; \ -} - -#define BD_IRQ_HND(_name_) bd0528_##_name_##_interrupt - -struct bd70528_psy { - struct regmap *regmap; - struct device *dev; - struct power_supply *psy; -}; - -BD_ERR_IRQ_HND(BAT_OV_DET, "Battery overvoltage detected\n"); -BD_ERR_IRQ_HND(DBAT_DET, "Dead battery detected\n"); -BD_ERR_IRQ_HND(COLD_DET, "Battery cold\n"); -BD_ERR_IRQ_HND(HOT_DET, "Battery hot\n"); -BD_ERR_IRQ_HND(CHG_TSD, "Charger thermal shutdown\n"); -BD_ERR_IRQ_HND(DCIN2_OV_DET, "DCIN2 overvoltage detected\n"); - -BD_INFO_IRQ_HND(BAT_OV_RES, "Battery voltage back to normal\n"); -BD_INFO_IRQ_HND(COLD_RES, "Battery temperature back to normal\n"); -BD_INFO_IRQ_HND(HOT_RES, "Battery temperature back to normal\n"); -BD_INFO_IRQ_HND(BAT_RMV, "Battery removed\n"); -BD_INFO_IRQ_HND(BAT_DET, "Battery detected\n"); -BD_INFO_IRQ_HND(DCIN2_OV_RES, "DCIN2 voltage back to normal\n"); -BD_INFO_IRQ_HND(DCIN2_RMV, "DCIN2 removed\n"); -BD_INFO_IRQ_HND(DCIN2_DET, "DCIN2 detected\n"); -BD_INFO_IRQ_HND(DCIN1_RMV, "DCIN1 removed\n"); -BD_INFO_IRQ_HND(DCIN1_DET, "DCIN1 detected\n"); - -struct irq_name_pair { - const char *n; - irqreturn_t (*h)(int irq, void *arg); -}; - -static int bd70528_get_irqs(struct platform_device *pdev, - struct bd70528_psy *bdpsy) -{ - int irq, i, ret; - unsigned int mask; - static const struct irq_name_pair bd70528_chg_irqs[] = { - { .n = "bd70528-bat-ov-res", .h = BD_IRQ_HND(BAT_OV_RES) }, - { .n = "bd70528-bat-ov-det", .h = BD_IRQ_HND(BAT_OV_DET) }, - { .n = "bd70528-bat-dead", .h = BD_IRQ_HND(DBAT_DET) }, - { .n = "bd70528-bat-warmed", .h = BD_IRQ_HND(COLD_RES) }, - { .n = "bd70528-bat-cold", .h = BD_IRQ_HND(COLD_DET) }, - { .n = "bd70528-bat-cooled", .h = BD_IRQ_HND(HOT_RES) }, - { .n = "bd70528-bat-hot", .h = BD_IRQ_HND(HOT_DET) }, - { .n = "bd70528-chg-tshd", .h = BD_IRQ_HND(CHG_TSD) }, - { .n = "bd70528-bat-removed", .h = BD_IRQ_HND(BAT_RMV) }, - { .n = "bd70528-bat-detected", .h = BD_IRQ_HND(BAT_DET) }, - { .n = "bd70528-dcin2-ov-res", .h = BD_IRQ_HND(DCIN2_OV_RES) }, - { .n = "bd70528-dcin2-ov-det", .h = BD_IRQ_HND(DCIN2_OV_DET) }, - { .n = "bd70528-dcin2-removed", .h = BD_IRQ_HND(DCIN2_RMV) }, - { .n = "bd70528-dcin2-detected", .h = BD_IRQ_HND(DCIN2_DET) }, - { .n = "bd70528-dcin1-removed", .h = BD_IRQ_HND(DCIN1_RMV) }, - { .n = "bd70528-dcin1-detected", .h = BD_IRQ_HND(DCIN1_DET) }, - }; - - for (i = 0; i < ARRAY_SIZE(bd70528_chg_irqs); i++) { - irq = platform_get_irq_byname(pdev, bd70528_chg_irqs[i].n); - if (irq < 0) { - dev_err(&pdev->dev, "Bad IRQ information for %s (%d)\n", - bd70528_chg_irqs[i].n, irq); - return irq; - } - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - bd70528_chg_irqs[i].h, - IRQF_ONESHOT, - bd70528_chg_irqs[i].n, - bdpsy->psy); - - if (ret) - return ret; - } - /* - * BD70528 irq controller is not touching the main mask register. - * So enable the charger block interrupts at main level. We can just - * leave them enabled as irq-controller should disable irqs - * from sub-registers when IRQ is disabled or freed. - */ - mask = BD70528_REG_INT_BAT1_MASK | BD70528_REG_INT_BAT2_MASK; - ret = regmap_update_bits(bdpsy->regmap, - BD70528_REG_INT_MAIN_MASK, mask, 0); - if (ret) - dev_err(&pdev->dev, "Failed to enable charger IRQs\n"); - - return ret; -} - -static int bd70528_get_charger_status(struct bd70528_psy *bdpsy, int *val) -{ - int ret; - unsigned int v; - - ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_CURR_STAT, &v); - if (ret) { - dev_err(bdpsy->dev, "Charger state read failure %d\n", - ret); - return ret; - } - - switch (v & BD70528_MASK_CHG_STAT) { - case CHG_STAT_SUSPEND: - /* Maybe we should check the CHG_TTRI_EN? */ - case CHG_STAT_OTP_TRICKLE: - case CHG_STAT_OTP_FAST: - case CHG_STAT_OTP_DONE: - case CHG_STAT_TSD_TRICKLE: - case CHG_STAT_TSD_FAST: - case CHG_STAT_TSD_TOPOFF: - case CHG_STAT_BAT_ERR: - *val = POWER_SUPPLY_STATUS_NOT_CHARGING; - break; - case CHG_STAT_DONE: - *val = POWER_SUPPLY_STATUS_FULL; - break; - case CHG_STAT_TRICKLE: - case CHG_STAT_FAST: - case CHG_STAT_TOPOFF: - *val = POWER_SUPPLY_STATUS_CHARGING; - break; - default: - *val = POWER_SUPPLY_STATUS_UNKNOWN; - break; - } - - return 0; -} - -static int bd70528_get_charge_type(struct bd70528_psy *bdpsy, int *val) -{ - int ret; - unsigned int v; - - ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_CURR_STAT, &v); - if (ret) { - dev_err(bdpsy->dev, "Charger state read failure %d\n", - ret); - return ret; - } - - switch (v & BD70528_MASK_CHG_STAT) { - case CHG_STAT_TRICKLE: - *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; - break; - case CHG_STAT_FAST: - case CHG_STAT_TOPOFF: - *val = POWER_SUPPLY_CHARGE_TYPE_FAST; - break; - case CHG_STAT_DONE: - case CHG_STAT_SUSPEND: - /* Maybe we should check the CHG_TTRI_EN? */ - case CHG_STAT_OTP_TRICKLE: - case CHG_STAT_OTP_FAST: - case CHG_STAT_OTP_DONE: - case CHG_STAT_TSD_TRICKLE: - case CHG_STAT_TSD_FAST: - case CHG_STAT_TSD_TOPOFF: - case CHG_STAT_BAT_ERR: - *val = POWER_SUPPLY_CHARGE_TYPE_NONE; - break; - default: - *val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; - break; - } - - return 0; -} - -static int bd70528_get_battery_health(struct bd70528_psy *bdpsy, int *val) -{ - int ret; - unsigned int v; - - ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_BAT_STAT, &v); - if (ret) { - dev_err(bdpsy->dev, "Battery state read failure %d\n", - ret); - return ret; - } - /* No battery? */ - if (!(v & BD70528_MASK_CHG_BAT_DETECT)) - *val = POWER_SUPPLY_HEALTH_DEAD; - else if (v & BD70528_MASK_CHG_BAT_OVERVOLT) - *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE; - else if (v & BD70528_MASK_CHG_BAT_TIMER) - *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; - else - *val = POWER_SUPPLY_HEALTH_GOOD; - - return 0; -} - -static int bd70528_get_online(struct bd70528_psy *bdpsy, int *val) -{ - int ret; - unsigned int v; - - ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_IN_STAT, &v); - if (ret) { - dev_err(bdpsy->dev, "DC1 IN state read failure %d\n", - ret); - return ret; - } - - *val = (v & BD70528_MASK_CHG_DCIN1_UVLO) ? 1 : 0; - - return 0; -} - -static int bd70528_get_present(struct bd70528_psy *bdpsy, int *val) -{ - int ret; - unsigned int v; - - ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_BAT_STAT, &v); - if (ret) { - dev_err(bdpsy->dev, "Battery state read failure %d\n", - ret); - return ret; - } - - *val = (v & BD70528_MASK_CHG_BAT_DETECT) ? 1 : 0; - - return 0; -} - -static const struct linear_range current_limit_ranges[] = { - { - .min = 5, - .step = 1, - .min_sel = 0, - .max_sel = 0x22, - }, - { - .min = 40, - .step = 5, - .min_sel = 0x23, - .max_sel = 0x26, - }, - { - .min = 60, - .step = 20, - .min_sel = 0x27, - .max_sel = 0x2d, - }, - { - .min = 200, - .step = 50, - .min_sel = 0x2e, - .max_sel = 0x34, - }, - { - .min = 500, - .step = 0, - .min_sel = 0x35, - .max_sel = 0x3f, - }, -}; - -/* - * BD70528 would support setting and getting own charge current/ - * voltage for low temperatures. The driver currently only reads - * the charge current at room temperature. We do set both though. - */ -static const struct linear_range warm_charge_curr[] = { - { - .min = 10, - .step = 10, - .min_sel = 0, - .max_sel = 0x12, - }, - { - .min = 200, - .step = 25, - .min_sel = 0x13, - .max_sel = 0x1f, - }, -}; - -/* - * Cold charge current selectors are identical to warm charge current - * selectors. The difference is that only smaller currents are available - * at cold charge range. - */ -#define MAX_COLD_CHG_CURR_SEL 0x15 -#define MAX_WARM_CHG_CURR_SEL 0x1f -#define MIN_CHG_CURR_SEL 0x0 - -static int get_charge_current(struct bd70528_psy *bdpsy, int *ma) -{ - unsigned int sel; - int ret; - - ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_CHG_CURR_WARM, - &sel); - if (ret) { - dev_err(bdpsy->dev, - "Charge current reading failed (%d)\n", ret); - return ret; - } - - sel &= BD70528_MASK_CHG_CHG_CURR; - - ret = linear_range_get_value_array(&warm_charge_curr[0], - ARRAY_SIZE(warm_charge_curr), - sel, ma); - if (ret) { - dev_err(bdpsy->dev, - "Unknown charge current value 0x%x\n", - sel); - } - - return ret; -} - -static int get_current_limit(struct bd70528_psy *bdpsy, int *ma) -{ - unsigned int sel; - int ret; - - ret = regmap_read(bdpsy->regmap, BD70528_REG_CHG_DCIN_ILIM, - &sel); - - if (ret) { - dev_err(bdpsy->dev, - "Input current limit reading failed (%d)\n", ret); - return ret; - } - - sel &= BD70528_MASK_CHG_DCIN_ILIM; - - ret = linear_range_get_value_array(¤t_limit_ranges[0], - ARRAY_SIZE(current_limit_ranges), - sel, ma); - if (ret) { - /* Unspecified values mean 500 mA */ - *ma = 500; - } - return 0; -} - -static enum power_supply_property bd70528_charger_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_CHARGE_TYPE, - POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, - POWER_SUPPLY_PROP_MODEL_NAME, - POWER_SUPPLY_PROP_MANUFACTURER, -}; - -static int bd70528_charger_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct bd70528_psy *bdpsy = power_supply_get_drvdata(psy); - int ret = 0; - - switch (psp) { - case POWER_SUPPLY_PROP_STATUS: - return bd70528_get_charger_status(bdpsy, &val->intval); - case POWER_SUPPLY_PROP_CHARGE_TYPE: - return bd70528_get_charge_type(bdpsy, &val->intval); - case POWER_SUPPLY_PROP_HEALTH: - return bd70528_get_battery_health(bdpsy, &val->intval); - case POWER_SUPPLY_PROP_PRESENT: - return bd70528_get_present(bdpsy, &val->intval); - case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: - ret = get_current_limit(bdpsy, &val->intval); - val->intval *= 1000; - return ret; - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: - ret = get_charge_current(bdpsy, &val->intval); - val->intval *= 1000; - return ret; - case POWER_SUPPLY_PROP_ONLINE: - return bd70528_get_online(bdpsy, &val->intval); - case POWER_SUPPLY_PROP_MODEL_NAME: - val->strval = bd70528_charger_model; - return 0; - case POWER_SUPPLY_PROP_MANUFACTURER: - val->strval = bd70528_charger_manufacturer; - return 0; - default: - break; - } - - return -EINVAL; -} - -static int bd70528_prop_is_writable(struct power_supply *psy, - enum power_supply_property psp) -{ - switch (psp) { - case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: - return 1; - default: - break; - } - return 0; -} - -static int set_charge_current(struct bd70528_psy *bdpsy, int ma) -{ - unsigned int reg; - int ret = 0, tmpret; - bool found; - - if (ma > 500) { - dev_warn(bdpsy->dev, - "Requested charge current %u exceed maximum (500mA)\n", - ma); - reg = MAX_WARM_CHG_CURR_SEL; - goto set; - } - if (ma < 10) { - dev_err(bdpsy->dev, - "Requested charge current %u smaller than min (10mA)\n", - ma); - reg = MIN_CHG_CURR_SEL; - ret = -EINVAL; - goto set; - } - -/* - * For BD70528 voltage/current limits we happily accept any value which - * belongs the range. We could check if value matching the selector is - * desired by computing the range min + (sel - sel_low) * range step - but - * I guess it is enough if we use voltage/current which is closest (below) - * the requested? - */ - - ret = linear_range_get_selector_low_array(warm_charge_curr, - ARRAY_SIZE(warm_charge_curr), - ma, ®, &found); - if (ret) { - dev_err(bdpsy->dev, - "Unsupported charge current %u mA\n", ma); - reg = MIN_CHG_CURR_SEL; - goto set; - } - if (!found) { - /* - * There was a gap in supported values and we hit it. - * Yet a smaller value was found so we use it. - */ - dev_warn(bdpsy->dev, - "Unsupported charge current %u mA\n", ma); - } -set: - - tmpret = regmap_update_bits(bdpsy->regmap, - BD70528_REG_CHG_CHG_CURR_WARM, - BD70528_MASK_CHG_CHG_CURR, reg); - if (tmpret) - dev_err(bdpsy->dev, - "Charge current write failure (%d)\n", tmpret); - - if (reg > MAX_COLD_CHG_CURR_SEL) - reg = MAX_COLD_CHG_CURR_SEL; - - if (!tmpret) - tmpret = regmap_update_bits(bdpsy->regmap, - BD70528_REG_CHG_CHG_CURR_COLD, - BD70528_MASK_CHG_CHG_CURR, reg); - - if (!ret) - ret = tmpret; - - return ret; -} - -#define MAX_CURR_LIMIT_SEL 0x34 -#define MIN_CURR_LIMIT_SEL 0x0 - -static int set_current_limit(struct bd70528_psy *bdpsy, int ma) -{ - unsigned int reg; - int ret = 0, tmpret; - bool found; - - if (ma > 500) { - dev_warn(bdpsy->dev, - "Requested current limit %u exceed maximum (500mA)\n", - ma); - reg = MAX_CURR_LIMIT_SEL; - goto set; - } - if (ma < 5) { - dev_err(bdpsy->dev, - "Requested current limit %u smaller than min (5mA)\n", - ma); - reg = MIN_CURR_LIMIT_SEL; - ret = -EINVAL; - goto set; - } - - ret = linear_range_get_selector_low_array(current_limit_ranges, - ARRAY_SIZE(current_limit_ranges), - ma, ®, &found); - if (ret) { - dev_err(bdpsy->dev, "Unsupported current limit %umA\n", ma); - reg = MIN_CURR_LIMIT_SEL; - goto set; - } - if (!found) { - /* - * There was a gap in supported values and we hit it. - * We found a smaller value from ranges and use it. - * Warn user though. - */ - dev_warn(bdpsy->dev, "Unsupported current limit %umA\n", ma); - } - -set: - tmpret = regmap_update_bits(bdpsy->regmap, - BD70528_REG_CHG_DCIN_ILIM, - BD70528_MASK_CHG_DCIN_ILIM, reg); - - if (!ret) - ret = tmpret; - - return ret; -} - -static int bd70528_charger_set_property(struct power_supply *psy, - enum power_supply_property psp, - const union power_supply_propval *val) -{ - struct bd70528_psy *bdpsy = power_supply_get_drvdata(psy); - - switch (psp) { - case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: - return set_current_limit(bdpsy, val->intval / 1000); - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: - return set_charge_current(bdpsy, val->intval / 1000); - default: - break; - } - return -EINVAL; -} - -static const struct power_supply_desc bd70528_charger_desc = { - .name = "bd70528-charger", - .type = POWER_SUPPLY_TYPE_MAINS, - .properties = bd70528_charger_props, - .num_properties = ARRAY_SIZE(bd70528_charger_props), - .get_property = bd70528_charger_get_property, - .set_property = bd70528_charger_set_property, - .property_is_writeable = bd70528_prop_is_writable, -}; - -static int bd70528_power_probe(struct platform_device *pdev) -{ - struct bd70528_psy *bdpsy; - struct power_supply_config cfg = {}; - - bdpsy = devm_kzalloc(&pdev->dev, sizeof(*bdpsy), GFP_KERNEL); - if (!bdpsy) - return -ENOMEM; - - bdpsy->regmap = dev_get_regmap(pdev->dev.parent, NULL); - if (!bdpsy->regmap) { - dev_err(&pdev->dev, "No regmap found for chip\n"); - return -EINVAL; - } - bdpsy->dev = &pdev->dev; - - platform_set_drvdata(pdev, bdpsy); - cfg.drv_data = bdpsy; - cfg.of_node = pdev->dev.parent->of_node; - - bdpsy->psy = devm_power_supply_register(&pdev->dev, - &bd70528_charger_desc, &cfg); - if (IS_ERR(bdpsy->psy)) { - dev_err(&pdev->dev, "failed: power supply register\n"); - return PTR_ERR(bdpsy->psy); - } - - return bd70528_get_irqs(pdev, bdpsy); -} - -static struct platform_driver bd70528_power = { - .driver = { - .name = "bd70528-power" - }, - .probe = bd70528_power_probe, -}; - -module_platform_driver(bd70528_power); - -MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); -MODULE_DESCRIPTION("BD70528 power-supply driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:bd70528-power"); diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index 852e86bfe2fb..35ff0c8fe96f 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -5,11 +5,10 @@ * Author: Mark A. Greer <mgreer@animalcreek.com> */ +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/delay.h> -#include <linux/of_irq.h> -#include <linux/of_device.h> #include <linux/pm_runtime.h> #include <linux/power_supply.h> #include <linux/power/bq24190_charger.h> @@ -1959,7 +1958,6 @@ static const struct i2c_device_id bq24190_i2c_ids[] = { }; MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids); -#ifdef CONFIG_OF static const struct of_device_id bq24190_of_match[] = { { .compatible = "ti,bq24190", }, { .compatible = "ti,bq24192", }, @@ -1968,11 +1966,6 @@ static const struct of_device_id bq24190_of_match[] = { { }, }; MODULE_DEVICE_TABLE(of, bq24190_of_match); -#else -static const struct of_device_id bq24190_of_match[] = { - { }, -}; -#endif static struct i2c_driver bq24190_driver = { .probe = bq24190_probe, @@ -1981,7 +1974,7 @@ static struct i2c_driver bq24190_driver = { .driver = { .name = "bq24190-charger", .pm = &bq24190_pm_ops, - .of_match_table = of_match_ptr(bq24190_of_match), + .of_match_table = bq24190_of_match, }, }; module_i2c_driver(bq24190_driver); diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index 45da870aecca..d67edb760c94 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -1279,6 +1279,7 @@ static const struct of_device_id charger_manager_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, charger_manager_match); static struct charger_desc *of_cm_parse_desc(struct device *dev) { diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index a3fc0084cda0..8d62d4241da3 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -667,10 +667,23 @@ static int cpcap_battery_get_property(struct power_supply *psy, if (!empty->voltage) return -ENODATA; val->intval = empty->counter_uah - latest->counter_uah; - if (val->intval < 0) + if (val->intval < 0) { + /* Assume invalid config if CHARGE_NOW is -20% */ + if (ddata->charge_full && abs(val->intval) > ddata->charge_full/5) { + empty->voltage = 0; + ddata->charge_full = 0; + return -ENODATA; + } val->intval = 0; - else if (ddata->charge_full && ddata->charge_full < val->intval) + } else if (ddata->charge_full && ddata->charge_full < val->intval) { + /* Assume invalid config if CHARGE_NOW exceeds CHARGE_FULL by 20% */ + if (val->intval > (6*ddata->charge_full)/5) { + empty->voltage = 0; + ddata->charge_full = 0; + return -ENODATA; + } val->intval = ddata->charge_full; + } break; case POWER_SUPPLY_PROP_CHARGE_FULL: if (!ddata->charge_full) @@ -747,7 +760,7 @@ static int cpcap_battery_set_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_FULL: if (val->intval < 0) return -EINVAL; - if (val->intval > ddata->config.info.charge_full_design) + if (val->intval > (6*ddata->config.info.charge_full_design)/5) return -EINVAL; ddata->charge_full = val->intval; diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c index df01abc49ce8..60e0ce105a29 100644 --- a/drivers/power/supply/cpcap-charger.c +++ b/drivers/power/supply/cpcap-charger.c @@ -173,23 +173,6 @@ static enum power_supply_property cpcap_charger_props[] = { POWER_SUPPLY_PROP_CURRENT_NOW, }; -/* No battery always shows temperature of -40000 */ -static bool cpcap_charger_battery_found(struct cpcap_charger_ddata *ddata) -{ - struct iio_channel *channel; - int error, temperature; - - channel = ddata->channels[CPCAP_CHARGER_IIO_BATTDET]; - error = iio_read_channel_processed(channel, &temperature); - if (error < 0) { - dev_warn(ddata->dev, "%s failed: %i\n", __func__, error); - - return false; - } - - return temperature > -20000 && temperature < 60000; -} - static int cpcap_charger_get_charge_voltage(struct cpcap_charger_ddata *ddata) { struct iio_channel *channel; @@ -700,11 +683,29 @@ static void cpcap_usb_detect(struct work_struct *work) if (!ddata->feeding_vbus && cpcap_charger_vbus_valid(ddata) && s.chrgcurr1) { - int max_current = 532000; + int max_current; int vchrg, ichrg; + union power_supply_propval val; + struct power_supply *battery; - if (cpcap_charger_battery_found(ddata)) + battery = power_supply_get_by_name("battery"); + if (IS_ERR_OR_NULL(battery)) { + dev_err(ddata->dev, "battery power_supply not available %li\n", + PTR_ERR(battery)); + return; + } + + error = power_supply_get_property(battery, POWER_SUPPLY_PROP_PRESENT, &val); + power_supply_put(battery); + if (error) + goto out_err; + + if (val.intval) { max_current = 1596000; + } else { + dev_info(ddata->dev, "battery not inserted, charging disabled\n"); + max_current = 0; + } if (max_current > ddata->limit_current) max_current = ddata->limit_current; diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index 1aab868adabf..3cea92e28dc3 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -16,7 +16,6 @@ #include <linux/interrupt.h> #include <linux/power_supply.h> #include <linux/of_device.h> -#include <linux/max17040_battery.h> #include <linux/regmap.h> #include <linux/slab.h> @@ -142,13 +141,10 @@ struct max17040_chip { struct regmap *regmap; struct delayed_work work; struct power_supply *battery; - struct max17040_platform_data *pdata; struct chip_data data; /* battery capacity */ int soc; - /* State Of Charge */ - int status; /* Low alert threshold from 32% to 1% of the State of Charge */ u32 low_soc_alert; /* some devices return twice the capacity */ @@ -221,26 +217,7 @@ static int max17040_get_version(struct max17040_chip *chip) static int max17040_get_online(struct max17040_chip *chip) { - return chip->pdata && chip->pdata->battery_online ? - chip->pdata->battery_online() : 1; -} - -static int max17040_get_status(struct max17040_chip *chip) -{ - if (!chip->pdata || !chip->pdata->charger_online - || !chip->pdata->charger_enable) - return POWER_SUPPLY_STATUS_UNKNOWN; - - if (max17040_get_soc(chip) > MAX17040_BATTERY_FULL) - return POWER_SUPPLY_STATUS_FULL; - - if (chip->pdata->charger_online()) - if (chip->pdata->charger_enable()) - return POWER_SUPPLY_STATUS_CHARGING; - else - return POWER_SUPPLY_STATUS_NOT_CHARGING; - else - return POWER_SUPPLY_STATUS_DISCHARGING; + return 1; } static int max17040_get_of_data(struct max17040_chip *chip) @@ -283,7 +260,6 @@ static int max17040_get_of_data(struct max17040_chip *chip) static void max17040_check_changes(struct max17040_chip *chip) { chip->soc = max17040_get_soc(chip); - chip->status = max17040_get_status(chip); } static void max17040_queue_work(struct max17040_chip *chip) @@ -302,17 +278,16 @@ static void max17040_stop_work(void *data) static void max17040_work(struct work_struct *work) { struct max17040_chip *chip; - int last_soc, last_status; + int last_soc; chip = container_of(work, struct max17040_chip, work.work); - /* store SOC and status to check changes */ + /* store SOC to check changes */ last_soc = chip->soc; - last_status = chip->status; max17040_check_changes(chip); /* check changes and send uevent */ - if (last_soc != chip->soc || last_status != chip->status) + if (last_soc != chip->soc) power_supply_changed(chip->battery); max17040_queue_work(chip); @@ -361,12 +336,10 @@ static irqreturn_t max17040_thread_handler(int id, void *dev) static int max17040_enable_alert_irq(struct max17040_chip *chip) { struct i2c_client *client = chip->client; - unsigned int flags; int ret; - flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, - max17040_thread_handler, flags, + max17040_thread_handler, IRQF_ONESHOT, chip->battery->desc->name, chip); return ret; @@ -415,9 +388,6 @@ static int max17040_get_property(struct power_supply *psy, struct max17040_chip *chip = power_supply_get_drvdata(psy); switch (psp) { - case POWER_SUPPLY_PROP_STATUS: - val->intval = max17040_get_status(chip); - break; case POWER_SUPPLY_PROP_ONLINE: val->intval = max17040_get_online(chip); break; @@ -444,7 +414,6 @@ static const struct regmap_config max17040_regmap = { }; static enum power_supply_property max17040_battery_props[] = { - POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CAPACITY, @@ -480,7 +449,6 @@ static int max17040_probe(struct i2c_client *client, chip->client = client; chip->regmap = devm_regmap_init_i2c(client, &max17040_regmap); - chip->pdata = client->dev.platform_data; chip_id = (enum chip_id) id->driver_data; if (client->dev.of_node) { ret = max17040_get_of_data(chip); diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 1d7326cd8fc6..ce2041b30a06 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -1104,7 +1104,7 @@ static int max17042_probe(struct i2c_client *client, } if (client->irq) { - unsigned int flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + unsigned int flags = IRQF_ONESHOT; /* * On ACPI systems the IRQ may be handled by ACPI-event code, diff --git a/drivers/power/supply/pm2301_charger.c b/drivers/power/supply/pm2301_charger.c deleted file mode 100644 index f86bbbeaff6c..000000000000 --- a/drivers/power/supply/pm2301_charger.c +++ /dev/null @@ -1,1249 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright 2012 ST Ericsson. - * - * Power supply driver for ST Ericsson pm2xxx_charger charger - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/device.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/platform_device.h> -#include <linux/power_supply.h> -#include <linux/regulator/consumer.h> -#include <linux/err.h> -#include <linux/i2c.h> -#include <linux/workqueue.h> -#include <linux/mfd/abx500/ab8500.h> -#include <linux/pm2301_charger.h> -#include <linux/gpio.h> -#include <linux/pm_runtime.h> -#include <linux/pm.h> - -#include "ab8500-bm.h" -#include "ab8500-chargalg.h" -#include "pm2301_charger.h" - -#define to_pm2xxx_charger_ac_device_info(x) container_of((x), \ - struct pm2xxx_charger, ac_chg) -#define SLEEP_MIN 50 -#define SLEEP_MAX 100 -#define PM2XXX_AUTOSUSPEND_DELAY 500 - -static int pm2xxx_interrupt_registers[] = { - PM2XXX_REG_INT1, - PM2XXX_REG_INT2, - PM2XXX_REG_INT3, - PM2XXX_REG_INT4, - PM2XXX_REG_INT5, - PM2XXX_REG_INT6, -}; - -static enum power_supply_property pm2xxx_charger_ac_props[] = { - POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_VOLTAGE_AVG, -}; - -static int pm2xxx_charger_voltage_map[] = { - 3500, - 3525, - 3550, - 3575, - 3600, - 3625, - 3650, - 3675, - 3700, - 3725, - 3750, - 3775, - 3800, - 3825, - 3850, - 3875, - 3900, - 3925, - 3950, - 3975, - 4000, - 4025, - 4050, - 4075, - 4100, - 4125, - 4150, - 4175, - 4200, - 4225, - 4250, - 4275, - 4300, -}; - -static int pm2xxx_charger_current_map[] = { - 200, - 200, - 400, - 600, - 800, - 1000, - 1200, - 1400, - 1600, - 1800, - 2000, - 2200, - 2400, - 2600, - 2800, - 3000, -}; - -static void set_lpn_pin(struct pm2xxx_charger *pm2) -{ - if (!pm2->ac.charger_connected && gpio_is_valid(pm2->lpn_pin)) { - gpio_set_value(pm2->lpn_pin, 1); - usleep_range(SLEEP_MIN, SLEEP_MAX); - } -} - -static void clear_lpn_pin(struct pm2xxx_charger *pm2) -{ - if (!pm2->ac.charger_connected && gpio_is_valid(pm2->lpn_pin)) - gpio_set_value(pm2->lpn_pin, 0); -} - -static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val) -{ - int ret; - - /* wake up the device */ - pm_runtime_get_sync(pm2->dev); - - ret = i2c_smbus_read_i2c_block_data(pm2->config.pm2xxx_i2c, reg, - 1, val); - if (ret < 0) - dev_err(pm2->dev, "Error reading register at 0x%x\n", reg); - else - ret = 0; - - pm_runtime_put_sync(pm2->dev); - - return ret; -} - -static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val) -{ - int ret; - - /* wake up the device */ - pm_runtime_get_sync(pm2->dev); - - ret = i2c_smbus_write_i2c_block_data(pm2->config.pm2xxx_i2c, reg, - 1, &val); - if (ret < 0) - dev_err(pm2->dev, "Error writing register at 0x%x\n", reg); - else - ret = 0; - - pm_runtime_put_sync(pm2->dev); - - return ret; -} - -static int pm2xxx_charging_enable_mngt(struct pm2xxx_charger *pm2) -{ - int ret; - - /* Enable charging */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2, - (PM2XXX_CH_AUTO_RESUME_EN | PM2XXX_CHARGER_ENA)); - - return ret; -} - -static int pm2xxx_charging_disable_mngt(struct pm2xxx_charger *pm2) -{ - int ret; - - /* Disable SW EOC ctrl */ - ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG, PM2XXX_SWCTRL_HW); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); - return ret; - } - - /* Disable charging */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2, - (PM2XXX_CH_AUTO_RESUME_DIS | PM2XXX_CHARGER_DIS)); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); - return ret; - } - - return 0; -} - -static int pm2xxx_charger_batt_therm_mngt(struct pm2xxx_charger *pm2, int val) -{ - queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work); - - return 0; -} - - -static int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val) -{ - queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work); - - return 0; -} - -static int pm2xxx_charger_ovv_mngt(struct pm2xxx_charger *pm2, int val) -{ - dev_err(pm2->dev, "Overvoltage detected\n"); - pm2->flags.ovv = true; - power_supply_changed(pm2->ac_chg.psy); - - /* Schedule a new HW failure check */ - queue_delayed_work(pm2->charger_wq, &pm2->check_hw_failure_work, 0); - - return 0; -} - -static int pm2xxx_charger_wd_exp_mngt(struct pm2xxx_charger *pm2, int val) -{ - dev_dbg(pm2->dev , "20 minutes watchdog expired\n"); - - pm2->ac.wd_expired = true; - power_supply_changed(pm2->ac_chg.psy); - - return 0; -} - -static int pm2xxx_charger_vbat_lsig_mngt(struct pm2xxx_charger *pm2, int val) -{ - int ret; - - switch (val) { - case PM2XXX_INT1_ITVBATLOWR: - dev_dbg(pm2->dev, "VBAT grows above VBAT_LOW level\n"); - /* Enable SW EOC ctrl */ - ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG, - PM2XXX_SWCTRL_SW); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); - return ret; - } - break; - - case PM2XXX_INT1_ITVBATLOWF: - dev_dbg(pm2->dev, "VBAT drops below VBAT_LOW level\n"); - /* Disable SW EOC ctrl */ - ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG, - PM2XXX_SWCTRL_HW); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); - return ret; - } - break; - - default: - dev_err(pm2->dev, "Unknown VBAT level\n"); - } - - return 0; -} - -static int pm2xxx_charger_bat_disc_mngt(struct pm2xxx_charger *pm2, int val) -{ - dev_dbg(pm2->dev, "battery disconnected\n"); - - return 0; -} - -static int pm2xxx_charger_detection(struct pm2xxx_charger *pm2, u8 *val) -{ - int ret; - - ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT2, val); - - if (ret < 0) { - dev_err(pm2->dev, "Charger detection failed\n"); - goto out; - } - - *val &= (PM2XXX_INT2_S_ITVPWR1PLUG | PM2XXX_INT2_S_ITVPWR2PLUG); - -out: - return ret; -} - -static int pm2xxx_charger_itv_pwr_plug_mngt(struct pm2xxx_charger *pm2, int val) -{ - - int ret; - u8 read_val; - - /* - * Since we can't be sure that the events are received - * synchronously, we have the check if the main charger is - * connected by reading the interrupt source register. - */ - ret = pm2xxx_charger_detection(pm2, &read_val); - - if ((ret == 0) && read_val) { - pm2->ac.charger_connected = 1; - pm2->ac_conn = true; - queue_work(pm2->charger_wq, &pm2->ac_work); - } - - - return ret; -} - -static int pm2xxx_charger_itv_pwr_unplug_mngt(struct pm2xxx_charger *pm2, - int val) -{ - pm2->ac.charger_connected = 0; - queue_work(pm2->charger_wq, &pm2->ac_work); - - return 0; -} - -static int pm2_int_reg0(void *pm2_data, int val) -{ - struct pm2xxx_charger *pm2 = pm2_data; - int ret = 0; - - if (val & PM2XXX_INT1_ITVBATLOWR) { - ret = pm2xxx_charger_vbat_lsig_mngt(pm2, - PM2XXX_INT1_ITVBATLOWR); - if (ret < 0) - goto out; - } - - if (val & PM2XXX_INT1_ITVBATLOWF) { - ret = pm2xxx_charger_vbat_lsig_mngt(pm2, - PM2XXX_INT1_ITVBATLOWF); - if (ret < 0) - goto out; - } - - if (val & PM2XXX_INT1_ITVBATDISCONNECT) { - ret = pm2xxx_charger_bat_disc_mngt(pm2, - PM2XXX_INT1_ITVBATDISCONNECT); - if (ret < 0) - goto out; - } -out: - return ret; -} - -static int pm2_int_reg1(void *pm2_data, int val) -{ - struct pm2xxx_charger *pm2 = pm2_data; - int ret = 0; - - if (val & (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)) { - dev_dbg(pm2->dev , "Main charger plugged\n"); - ret = pm2xxx_charger_itv_pwr_plug_mngt(pm2, val & - (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)); - } - - if (val & - (PM2XXX_INT2_ITVPWR1UNPLUG | PM2XXX_INT2_ITVPWR2UNPLUG)) { - dev_dbg(pm2->dev , "Main charger unplugged\n"); - ret = pm2xxx_charger_itv_pwr_unplug_mngt(pm2, val & - (PM2XXX_INT2_ITVPWR1UNPLUG | - PM2XXX_INT2_ITVPWR2UNPLUG)); - } - - return ret; -} - -static int pm2_int_reg2(void *pm2_data, int val) -{ - struct pm2xxx_charger *pm2 = pm2_data; - int ret = 0; - - if (val & PM2XXX_INT3_ITAUTOTIMEOUTWD) - ret = pm2xxx_charger_wd_exp_mngt(pm2, val); - - if (val & (PM2XXX_INT3_ITCHPRECHARGEWD | - PM2XXX_INT3_ITCHCCWD | PM2XXX_INT3_ITCHCVWD)) { - dev_dbg(pm2->dev, - "Watchdog occurred for precharge, CC and CV charge\n"); - } - - return ret; -} - -static int pm2_int_reg3(void *pm2_data, int val) -{ - struct pm2xxx_charger *pm2 = pm2_data; - int ret = 0; - - if (val & (PM2XXX_INT4_ITCHARGINGON)) { - dev_dbg(pm2->dev , - "charging operation has started\n"); - } - - if (val & (PM2XXX_INT4_ITVRESUME)) { - dev_dbg(pm2->dev, - "battery discharged down to VResume threshold\n"); - } - - if (val & (PM2XXX_INT4_ITBATTFULL)) { - dev_dbg(pm2->dev , "battery fully detected\n"); - } - - if (val & (PM2XXX_INT4_ITCVPHASE)) { - dev_dbg(pm2->dev, "CV phase enter with 0.5C charging\n"); - } - - if (val & (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)) { - pm2->failure_case = VPWR_OVV; - ret = pm2xxx_charger_ovv_mngt(pm2, val & - (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)); - dev_dbg(pm2->dev, "VPWR/VSYSTEM overvoltage detected\n"); - } - - if (val & (PM2XXX_INT4_S_ITBATTEMPCOLD | - PM2XXX_INT4_S_ITBATTEMPHOT)) { - ret = pm2xxx_charger_batt_therm_mngt(pm2, val & - (PM2XXX_INT4_S_ITBATTEMPCOLD | - PM2XXX_INT4_S_ITBATTEMPHOT)); - dev_dbg(pm2->dev, "BTEMP is too Low/High\n"); - } - - return ret; -} - -static int pm2_int_reg4(void *pm2_data, int val) -{ - struct pm2xxx_charger *pm2 = pm2_data; - int ret = 0; - - if (val & PM2XXX_INT5_ITVSYSTEMOVV) { - pm2->failure_case = VSYSTEM_OVV; - ret = pm2xxx_charger_ovv_mngt(pm2, val & - PM2XXX_INT5_ITVSYSTEMOVV); - dev_dbg(pm2->dev, "VSYSTEM overvoltage detected\n"); - } - - if (val & (PM2XXX_INT5_ITTHERMALWARNINGFALL | - PM2XXX_INT5_ITTHERMALWARNINGRISE | - PM2XXX_INT5_ITTHERMALSHUTDOWNFALL | - PM2XXX_INT5_ITTHERMALSHUTDOWNRISE)) { - dev_dbg(pm2->dev, "BTEMP die temperature is too Low/High\n"); - ret = pm2xxx_charger_die_therm_mngt(pm2, val & - (PM2XXX_INT5_ITTHERMALWARNINGFALL | - PM2XXX_INT5_ITTHERMALWARNINGRISE | - PM2XXX_INT5_ITTHERMALSHUTDOWNFALL | - PM2XXX_INT5_ITTHERMALSHUTDOWNRISE)); - } - - return ret; -} - -static int pm2_int_reg5(void *pm2_data, int val) -{ - struct pm2xxx_charger *pm2 = pm2_data; - - if (val & (PM2XXX_INT6_ITVPWR2DROP | PM2XXX_INT6_ITVPWR1DROP)) { - dev_dbg(pm2->dev, "VMPWR drop to VBAT level\n"); - } - - if (val & (PM2XXX_INT6_ITVPWR2VALIDRISE | - PM2XXX_INT6_ITVPWR1VALIDRISE | - PM2XXX_INT6_ITVPWR2VALIDFALL | - PM2XXX_INT6_ITVPWR1VALIDFALL)) { - dev_dbg(pm2->dev, "Falling/Rising edge on WPWR1/2\n"); - } - - return 0; -} - -static irqreturn_t pm2xxx_irq_int(int irq, void *data) -{ - struct pm2xxx_charger *pm2 = data; - struct pm2xxx_interrupts *interrupt = pm2->pm2_int; - int i; - - /* wake up the device */ - pm_runtime_get_sync(pm2->dev); - - do { - for (i = 0; i < PM2XXX_NUM_INT_REG; i++) { - pm2xxx_reg_read(pm2, - pm2xxx_interrupt_registers[i], - &(interrupt->reg[i])); - - if (interrupt->reg[i] > 0) - interrupt->handler[i](pm2, interrupt->reg[i]); - } - } while (gpio_get_value(pm2->pdata->gpio_irq_number) == 0); - - pm_runtime_mark_last_busy(pm2->dev); - pm_runtime_put_autosuspend(pm2->dev); - - return IRQ_HANDLED; -} - -static int pm2xxx_charger_get_ac_cv(struct pm2xxx_charger *pm2) -{ - int ret = 0; - u8 val; - - if (pm2->ac.charger_connected && pm2->ac.charger_online) { - - ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT4, &val); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__); - goto out; - } - - if (val & PM2XXX_INT4_S_ITCVPHASE) - ret = PM2XXX_CONST_VOLT; - else - ret = PM2XXX_CONST_CURR; - } -out: - return ret; -} - -static int pm2xxx_current_to_regval(int curr) -{ - int i; - - if (curr < pm2xxx_charger_current_map[0]) - return 0; - - for (i = 1; i < ARRAY_SIZE(pm2xxx_charger_current_map); i++) { - if (curr < pm2xxx_charger_current_map[i]) - return (i - 1); - } - - i = ARRAY_SIZE(pm2xxx_charger_current_map) - 1; - if (curr == pm2xxx_charger_current_map[i]) - return i; - else - return -EINVAL; -} - -static int pm2xxx_voltage_to_regval(int curr) -{ - int i; - - if (curr < pm2xxx_charger_voltage_map[0]) - return 0; - - for (i = 1; i < ARRAY_SIZE(pm2xxx_charger_voltage_map); i++) { - if (curr < pm2xxx_charger_voltage_map[i]) - return i - 1; - } - - i = ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1; - if (curr == pm2xxx_charger_voltage_map[i]) - return i; - else - return -EINVAL; -} - -static int pm2xxx_charger_update_charger_current(struct ux500_charger *charger, - int ich_out) -{ - int ret; - int curr_index; - struct pm2xxx_charger *pm2; - u8 val; - - if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS) - pm2 = to_pm2xxx_charger_ac_device_info(charger); - else - return -ENXIO; - - curr_index = pm2xxx_current_to_regval(ich_out); - if (curr_index < 0) { - dev_err(pm2->dev, - "Charger current too high, charging not started\n"); - return -ENXIO; - } - - ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val); - if (ret >= 0) { - val &= ~PM2XXX_DIR_CH_CC_CURRENT_MASK; - val |= curr_index; - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, val); - if (ret < 0) { - dev_err(pm2->dev, - "%s write failed\n", __func__); - } - } - else - dev_err(pm2->dev, "%s read failed\n", __func__); - - return ret; -} - -static int pm2xxx_charger_ac_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct pm2xxx_charger *pm2; - - pm2 = to_pm2xxx_charger_ac_device_info(psy_to_ux500_charger(psy)); - - switch (psp) { - case POWER_SUPPLY_PROP_HEALTH: - if (pm2->flags.mainextchnotok) - val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; - else if (pm2->ac.wd_expired) - val->intval = POWER_SUPPLY_HEALTH_DEAD; - else if (pm2->flags.main_thermal_prot) - val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; - else if (pm2->flags.ovv) - val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; - else - val->intval = POWER_SUPPLY_HEALTH_GOOD; - break; - case POWER_SUPPLY_PROP_ONLINE: - val->intval = pm2->ac.charger_online; - break; - case POWER_SUPPLY_PROP_PRESENT: - val->intval = pm2->ac.charger_connected; - break; - case POWER_SUPPLY_PROP_VOLTAGE_AVG: - pm2->ac.cv_active = pm2xxx_charger_get_ac_cv(pm2); - val->intval = pm2->ac.cv_active; - break; - default: - return -EINVAL; - } - return 0; -} - -static int pm2xxx_charging_init(struct pm2xxx_charger *pm2) -{ - int ret = 0; - - /* enable CC and CV watchdog */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG3, - (PM2XXX_CH_WD_CV_PHASE_60MIN | PM2XXX_CH_WD_CC_PHASE_60MIN)); - if( ret < 0) - return ret; - - /* enable precharge watchdog */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG4, - PM2XXX_CH_WD_PRECH_PHASE_60MIN); - - /* Disable auto timeout */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG5, - PM2XXX_CH_WD_AUTO_TIMEOUT_20MIN); - - /* - * EOC current level = 100mA - * Precharge current level = 100mA - * CC current level = 1000mA - */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, - (PM2XXX_DIR_CH_CC_CURRENT_1000MA | - PM2XXX_CH_PRECH_CURRENT_100MA | - PM2XXX_CH_EOC_CURRENT_100MA)); - - /* - * recharge threshold = 3.8V - * Precharge to CC threshold = 2.9V - */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG7, - (PM2XXX_CH_PRECH_VOL_2_9 | PM2XXX_CH_VRESUME_VOL_3_8)); - - /* float voltage charger level = 4.2V */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG8, - PM2XXX_CH_VOLT_4_2); - - /* Voltage drop between VBAT and VSYS in HW charging = 300mV */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG9, - (PM2XXX_CH_150MV_DROP_300MV | PM2XXX_CHARCHING_INFO_DIS | - PM2XXX_CH_CC_REDUCED_CURRENT_IDENT | - PM2XXX_CH_CC_MODEDROP_DIS)); - - /* Input charger level of over voltage = 10V */ - ret = pm2xxx_reg_write(pm2, PM2XXX_INP_VOLT_VPWR2, - PM2XXX_VPWR2_OVV_10); - ret = pm2xxx_reg_write(pm2, PM2XXX_INP_VOLT_VPWR1, - PM2XXX_VPWR1_OVV_10); - - /* Input charger drop */ - ret = pm2xxx_reg_write(pm2, PM2XXX_INP_DROP_VPWR2, - (PM2XXX_VPWR2_HW_OPT_DIS | PM2XXX_VPWR2_VALID_DIS | - PM2XXX_VPWR2_DROP_DIS)); - ret = pm2xxx_reg_write(pm2, PM2XXX_INP_DROP_VPWR1, - (PM2XXX_VPWR1_HW_OPT_DIS | PM2XXX_VPWR1_VALID_DIS | - PM2XXX_VPWR1_DROP_DIS)); - - /* Disable battery low monitoring */ - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_LOW_LEV_COMP_REG, - PM2XXX_VBAT_LOW_MONITORING_ENA); - - return ret; -} - -static int pm2xxx_charger_ac_en(struct ux500_charger *charger, - int enable, int vset, int iset) -{ - int ret; - int volt_index; - int curr_index; - u8 val; - - struct pm2xxx_charger *pm2 = to_pm2xxx_charger_ac_device_info(charger); - - if (enable) { - if (!pm2->ac.charger_connected) { - dev_dbg(pm2->dev, "AC charger not connected\n"); - return -ENXIO; - } - - dev_dbg(pm2->dev, "Enable AC: %dmV %dmA\n", vset, iset); - if (!pm2->vddadc_en_ac) { - ret = regulator_enable(pm2->regu); - if (ret) - dev_warn(pm2->dev, - "Failed to enable vddadc regulator\n"); - else - pm2->vddadc_en_ac = true; - } - - ret = pm2xxx_charging_init(pm2); - if (ret < 0) { - dev_err(pm2->dev, "%s charging init failed\n", - __func__); - goto error_occured; - } - - volt_index = pm2xxx_voltage_to_regval(vset); - curr_index = pm2xxx_current_to_regval(iset); - - if (volt_index < 0 || curr_index < 0) { - dev_err(pm2->dev, - "Charger voltage or current too high, " - "charging not started\n"); - return -ENXIO; - } - - ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG8, &val); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__); - goto error_occured; - } - val &= ~PM2XXX_CH_VOLT_MASK; - val |= volt_index; - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG8, val); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); - goto error_occured; - } - - ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__); - goto error_occured; - } - val &= ~PM2XXX_DIR_CH_CC_CURRENT_MASK; - val |= curr_index; - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, val); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); - goto error_occured; - } - - if (!pm2->bat->enable_overshoot) { - ret = pm2xxx_reg_read(pm2, PM2XXX_LED_CTRL_REG, &val); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx read failed\n", - __func__); - goto error_occured; - } - val |= PM2XXX_ANTI_OVERSHOOT_EN; - ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG, val); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx write failed\n", - __func__); - goto error_occured; - } - } - - ret = pm2xxx_charging_enable_mngt(pm2); - if (ret < 0) { - dev_err(pm2->dev, "Failed to enable" - "pm2xxx ac charger\n"); - goto error_occured; - } - - pm2->ac.charger_online = 1; - } else { - pm2->ac.charger_online = 0; - pm2->ac.wd_expired = false; - - /* Disable regulator if enabled */ - if (pm2->vddadc_en_ac) { - regulator_disable(pm2->regu); - pm2->vddadc_en_ac = false; - } - - ret = pm2xxx_charging_disable_mngt(pm2); - if (ret < 0) { - dev_err(pm2->dev, "failed to disable" - "pm2xxx ac charger\n"); - goto error_occured; - } - - dev_dbg(pm2->dev, "PM2301: " "Disabled AC charging\n"); - } - power_supply_changed(pm2->ac_chg.psy); - -error_occured: - return ret; -} - -static int pm2xxx_charger_watchdog_kick(struct ux500_charger *charger) -{ - int ret; - struct pm2xxx_charger *pm2; - - if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS) - pm2 = to_pm2xxx_charger_ac_device_info(charger); - else - return -ENXIO; - - ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_WD_KICK, WD_TIMER); - if (ret) - dev_err(pm2->dev, "Failed to kick WD!\n"); - - return ret; -} - -static void pm2xxx_charger_ac_work(struct work_struct *work) -{ - struct pm2xxx_charger *pm2 = container_of(work, - struct pm2xxx_charger, ac_work); - - - power_supply_changed(pm2->ac_chg.psy); - sysfs_notify(&pm2->ac_chg.psy->dev.kobj, NULL, "present"); -}; - -static void pm2xxx_charger_check_hw_failure_work(struct work_struct *work) -{ - u8 reg_value; - - struct pm2xxx_charger *pm2 = container_of(work, - struct pm2xxx_charger, check_hw_failure_work.work); - - if (pm2->flags.ovv) { - pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT4, ®_value); - - if (!(reg_value & (PM2XXX_INT4_S_ITVPWR1OVV | - PM2XXX_INT4_S_ITVPWR2OVV))) { - pm2->flags.ovv = false; - power_supply_changed(pm2->ac_chg.psy); - } - } - - /* If we still have a failure, schedule a new check */ - if (pm2->flags.ovv) { - queue_delayed_work(pm2->charger_wq, - &pm2->check_hw_failure_work, round_jiffies(HZ)); - } -} - -static void pm2xxx_charger_check_main_thermal_prot_work( - struct work_struct *work) -{ - int ret; - u8 val; - - struct pm2xxx_charger *pm2 = container_of(work, struct pm2xxx_charger, - check_main_thermal_prot_work); - - /* Check if die temp warning is still active */ - ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT5, &val); - if (ret < 0) { - dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__); - return; - } - if (val & (PM2XXX_INT5_S_ITTHERMALWARNINGRISE - | PM2XXX_INT5_S_ITTHERMALSHUTDOWNRISE)) - pm2->flags.main_thermal_prot = true; - else if (val & (PM2XXX_INT5_S_ITTHERMALWARNINGFALL - | PM2XXX_INT5_S_ITTHERMALSHUTDOWNFALL)) - pm2->flags.main_thermal_prot = false; - - power_supply_changed(pm2->ac_chg.psy); -} - -static struct pm2xxx_interrupts pm2xxx_int = { - .handler[0] = pm2_int_reg0, - .handler[1] = pm2_int_reg1, - .handler[2] = pm2_int_reg2, - .handler[3] = pm2_int_reg3, - .handler[4] = pm2_int_reg4, - .handler[5] = pm2_int_reg5, -}; - -static struct pm2xxx_irq pm2xxx_charger_irq[] = { - {"PM2XXX_IRQ_INT", pm2xxx_irq_int}, -}; - -static int __maybe_unused pm2xxx_wall_charger_resume(struct device *dev) -{ - struct i2c_client *i2c_client = to_i2c_client(dev); - struct pm2xxx_charger *pm2; - - pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(i2c_client); - set_lpn_pin(pm2); - - /* If we still have a HW failure, schedule a new check */ - if (pm2->flags.ovv) - queue_delayed_work(pm2->charger_wq, - &pm2->check_hw_failure_work, 0); - - return 0; -} - -static int __maybe_unused pm2xxx_wall_charger_suspend(struct device *dev) -{ - struct i2c_client *i2c_client = to_i2c_client(dev); - struct pm2xxx_charger *pm2; - - pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(i2c_client); - clear_lpn_pin(pm2); - - /* Cancel any pending HW failure check */ - if (delayed_work_pending(&pm2->check_hw_failure_work)) - cancel_delayed_work(&pm2->check_hw_failure_work); - - flush_work(&pm2->ac_work); - flush_work(&pm2->check_main_thermal_prot_work); - - return 0; -} - -static int __maybe_unused pm2xxx_runtime_suspend(struct device *dev) -{ - struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev); - struct pm2xxx_charger *pm2; - - pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client); - clear_lpn_pin(pm2); - - return 0; -} - -static int __maybe_unused pm2xxx_runtime_resume(struct device *dev) -{ - struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev); - struct pm2xxx_charger *pm2; - - pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client); - - if (gpio_is_valid(pm2->lpn_pin) && gpio_get_value(pm2->lpn_pin) == 0) - set_lpn_pin(pm2); - - return 0; -} - -static const struct dev_pm_ops pm2xxx_pm_ops __maybe_unused = { - SET_SYSTEM_SLEEP_PM_OPS(pm2xxx_wall_charger_suspend, - pm2xxx_wall_charger_resume) - SET_RUNTIME_PM_OPS(pm2xxx_runtime_suspend, pm2xxx_runtime_resume, NULL) -}; - -static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, - const struct i2c_device_id *id) -{ - struct pm2xxx_platform_data *pl_data = i2c_client->dev.platform_data; - struct power_supply_config psy_cfg = {}; - struct pm2xxx_charger *pm2; - int ret = 0; - u8 val; - int i; - - if (!pl_data) { - dev_err(&i2c_client->dev, "No platform data supplied\n"); - return -EINVAL; - } - - pm2 = kzalloc(sizeof(struct pm2xxx_charger), GFP_KERNEL); - if (!pm2) { - dev_err(&i2c_client->dev, "pm2xxx_charger allocation failed\n"); - return -ENOMEM; - } - - /* get parent data */ - pm2->dev = &i2c_client->dev; - - pm2->pm2_int = &pm2xxx_int; - - /* get charger spcific platform data */ - if (!pl_data->wall_charger) { - dev_err(pm2->dev, "no charger platform data supplied\n"); - ret = -EINVAL; - goto free_device_info; - } - - pm2->pdata = pl_data->wall_charger; - - /* get battery specific platform data */ - if (!pl_data->battery) { - dev_err(pm2->dev, "no battery platform data supplied\n"); - ret = -EINVAL; - goto free_device_info; - } - - pm2->bat = pl_data->battery; - - if (!i2c_check_functionality(i2c_client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_READ_WORD_DATA)) { - ret = -ENODEV; - dev_info(pm2->dev, "pm2301 i2c_check_functionality failed\n"); - goto free_device_info; - } - - pm2->config.pm2xxx_i2c = i2c_client; - pm2->config.pm2xxx_id = (struct i2c_device_id *) id; - i2c_set_clientdata(i2c_client, pm2); - - /* AC supply */ - /* power_supply base class */ - pm2->ac_chg_desc.name = pm2->pdata->label; - pm2->ac_chg_desc.type = POWER_SUPPLY_TYPE_MAINS; - pm2->ac_chg_desc.properties = pm2xxx_charger_ac_props; - pm2->ac_chg_desc.num_properties = ARRAY_SIZE(pm2xxx_charger_ac_props); - pm2->ac_chg_desc.get_property = pm2xxx_charger_ac_get_property; - - psy_cfg.supplied_to = pm2->pdata->supplied_to; - psy_cfg.num_supplicants = pm2->pdata->num_supplicants; - /* pm2xxx_charger sub-class */ - pm2->ac_chg.ops.enable = &pm2xxx_charger_ac_en; - pm2->ac_chg.ops.kick_wd = &pm2xxx_charger_watchdog_kick; - pm2->ac_chg.ops.update_curr = &pm2xxx_charger_update_charger_current; - pm2->ac_chg.max_out_volt = pm2xxx_charger_voltage_map[ - ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1]; - pm2->ac_chg.max_out_curr = pm2xxx_charger_current_map[ - ARRAY_SIZE(pm2xxx_charger_current_map) - 1]; - pm2->ac_chg.wdt_refresh = WD_KICK_INTERVAL; - pm2->ac_chg.enabled = true; - pm2->ac_chg.external = true; - - /* Create a work queue for the charger */ - pm2->charger_wq = alloc_ordered_workqueue("pm2xxx_charger_wq", - WQ_MEM_RECLAIM); - if (pm2->charger_wq == NULL) { - ret = -ENOMEM; - dev_err(pm2->dev, "failed to create work queue\n"); - goto free_device_info; - } - - /* Init work for charger detection */ - INIT_WORK(&pm2->ac_work, pm2xxx_charger_ac_work); - - /* Init work for checking HW status */ - INIT_WORK(&pm2->check_main_thermal_prot_work, - pm2xxx_charger_check_main_thermal_prot_work); - - /* Init work for HW failure check */ - INIT_DEFERRABLE_WORK(&pm2->check_hw_failure_work, - pm2xxx_charger_check_hw_failure_work); - - /* - * VDD ADC supply needs to be enabled from this driver when there - * is a charger connected to avoid erroneous BTEMP_HIGH/LOW - * interrupts during charging - */ - pm2->regu = regulator_get(pm2->dev, "vddadc"); - if (IS_ERR(pm2->regu)) { - ret = PTR_ERR(pm2->regu); - dev_err(pm2->dev, "failed to get vddadc regulator\n"); - goto free_charger_wq; - } - - /* Register AC charger class */ - pm2->ac_chg.psy = power_supply_register(pm2->dev, &pm2->ac_chg_desc, - &psy_cfg); - if (IS_ERR(pm2->ac_chg.psy)) { - dev_err(pm2->dev, "failed to register AC charger\n"); - ret = PTR_ERR(pm2->ac_chg.psy); - goto free_regulator; - } - - /* Register interrupts */ - ret = request_threaded_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), - NULL, - pm2xxx_charger_irq[0].isr, - pm2->pdata->irq_type | IRQF_ONESHOT, - pm2xxx_charger_irq[0].name, pm2); - - if (ret != 0) { - dev_err(pm2->dev, "failed to request %s IRQ %d: %d\n", - pm2xxx_charger_irq[0].name, - gpio_to_irq(pm2->pdata->gpio_irq_number), ret); - goto unregister_pm2xxx_charger; - } - - ret = pm_runtime_set_active(pm2->dev); - if (ret) - dev_err(pm2->dev, "set active Error\n"); - - pm_runtime_enable(pm2->dev); - pm_runtime_set_autosuspend_delay(pm2->dev, PM2XXX_AUTOSUSPEND_DELAY); - pm_runtime_use_autosuspend(pm2->dev); - pm_runtime_resume(pm2->dev); - - /* pm interrupt can wake up system */ - ret = enable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number)); - if (ret) { - dev_err(pm2->dev, "failed to set irq wake\n"); - goto unregister_pm2xxx_interrupt; - } - - mutex_init(&pm2->lock); - - if (gpio_is_valid(pm2->pdata->lpn_gpio)) { - /* get lpn GPIO from platform data */ - pm2->lpn_pin = pm2->pdata->lpn_gpio; - - /* - * Charger detection mechanism requires pulling up the LPN pin - * while i2c communication if Charger is not connected - * LPN pin of PM2301 is GPIO60 of AB9540 - */ - ret = gpio_request(pm2->lpn_pin, "pm2301_lpm_gpio"); - - if (ret < 0) { - dev_err(pm2->dev, "pm2301_lpm_gpio request failed\n"); - goto disable_pm2_irq_wake; - } - ret = gpio_direction_output(pm2->lpn_pin, 0); - if (ret < 0) { - dev_err(pm2->dev, "pm2301_lpm_gpio direction failed\n"); - goto free_gpio; - } - set_lpn_pin(pm2); - } - - /* read interrupt registers */ - for (i = 0; i < PM2XXX_NUM_INT_REG; i++) - pm2xxx_reg_read(pm2, - pm2xxx_interrupt_registers[i], - &val); - - ret = pm2xxx_charger_detection(pm2, &val); - - if ((ret == 0) && val) { - pm2->ac.charger_connected = 1; - ab8500_override_turn_on_stat(~AB8500_POW_KEY_1_ON, - AB8500_MAIN_CH_DET); - pm2->ac_conn = true; - power_supply_changed(pm2->ac_chg.psy); - sysfs_notify(&pm2->ac_chg.psy->dev.kobj, NULL, "present"); - } - - return 0; - -free_gpio: - if (gpio_is_valid(pm2->lpn_pin)) - gpio_free(pm2->lpn_pin); -disable_pm2_irq_wake: - disable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number)); -unregister_pm2xxx_interrupt: - /* disable interrupt */ - free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2); -unregister_pm2xxx_charger: - /* unregister power supply */ - power_supply_unregister(pm2->ac_chg.psy); -free_regulator: - /* disable the regulator */ - regulator_put(pm2->regu); -free_charger_wq: - destroy_workqueue(pm2->charger_wq); -free_device_info: - kfree(pm2); - - return ret; -} - -static int pm2xxx_wall_charger_remove(struct i2c_client *i2c_client) -{ - struct pm2xxx_charger *pm2 = i2c_get_clientdata(i2c_client); - - /* Disable pm_runtime */ - pm_runtime_disable(pm2->dev); - /* Disable AC charging */ - pm2xxx_charger_ac_en(&pm2->ac_chg, false, 0, 0); - - /* Disable wake by pm interrupt */ - disable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number)); - - /* Disable interrupts */ - free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2); - - /* Delete the work queue */ - destroy_workqueue(pm2->charger_wq); - - flush_scheduled_work(); - - /* disable the regulator */ - regulator_put(pm2->regu); - - power_supply_unregister(pm2->ac_chg.psy); - - if (gpio_is_valid(pm2->lpn_pin)) - gpio_free(pm2->lpn_pin); - - kfree(pm2); - - return 0; -} - -static const struct i2c_device_id pm2xxx_id[] = { - { "pm2301", 0 }, - { } -}; - -MODULE_DEVICE_TABLE(i2c, pm2xxx_id); - -static struct i2c_driver pm2xxx_charger_driver = { - .probe = pm2xxx_wall_charger_probe, - .remove = pm2xxx_wall_charger_remove, - .driver = { - .name = "pm2xxx-wall_charger", - .pm = IS_ENABLED(CONFIG_PM) ? &pm2xxx_pm_ops : NULL, - }, - .id_table = pm2xxx_id, -}; - -static int __init pm2xxx_charger_init(void) -{ - return i2c_add_driver(&pm2xxx_charger_driver); -} - -static void __exit pm2xxx_charger_exit(void) -{ - i2c_del_driver(&pm2xxx_charger_driver); -} - -device_initcall_sync(pm2xxx_charger_init); -module_exit(pm2xxx_charger_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Rajkumar kasirajan, Olivier Launay"); -MODULE_DESCRIPTION("PM2xxx charger management driver"); diff --git a/drivers/power/supply/rn5t618_power.c b/drivers/power/supply/rn5t618_power.c index dee520f0fdf5..819061918b2a 100644 --- a/drivers/power/supply/rn5t618_power.c +++ b/drivers/power/supply/rn5t618_power.c @@ -37,8 +37,27 @@ #define CHG_STATE_NO_BAT2 13 #define CHG_STATE_CHG_READY_VUSB 14 +#define GCHGDET_TYPE_MASK 0x30 +#define GCHGDET_TYPE_SDP 0x00 +#define GCHGDET_TYPE_CDP 0x10 +#define GCHGDET_TYPE_DCP 0x20 + #define FG_ENABLE 1 +/* + * Formula seems accurate for battery current, but for USB current around 70mA + * per step was seen on Kobo Clara HD but all sources show the same formula + * also fur USB current. To avoid accidentially unwanted high currents we stick + * to that formula + */ +#define TO_CUR_REG(x) ((x) / 100000 - 1) +#define FROM_CUR_REG(x) ((((x) & 0x1f) + 1) * 100000) +#define CHG_MIN_CUR 100000 +#define CHG_MAX_CUR 1800000 +#define ADP_MAX_CUR 2500000 +#define USB_MAX_CUR 1400000 + + struct rn5t618_power_info { struct rn5t618 *rn5t618; struct platform_device *pdev; @@ -48,12 +67,24 @@ struct rn5t618_power_info { int irq; }; +static enum power_supply_usb_type rn5t618_usb_types[] = { + POWER_SUPPLY_USB_TYPE_SDP, + POWER_SUPPLY_USB_TYPE_DCP, + POWER_SUPPLY_USB_TYPE_CDP, + POWER_SUPPLY_USB_TYPE_UNKNOWN +}; + static enum power_supply_property rn5t618_usb_props[] = { + /* input current limit is not very accurate */ + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_USB_TYPE, POWER_SUPPLY_PROP_ONLINE, }; static enum power_supply_property rn5t618_adp_props[] = { + /* input current limit is not very accurate */ + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_ONLINE, }; @@ -69,6 +100,7 @@ static enum power_supply_property rn5t618_battery_props[] = { POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, }; @@ -258,6 +290,36 @@ static int rn5t618_battery_ttf(struct rn5t618_power_info *info, return 0; } +static int rn5t618_battery_set_current_limit(struct rn5t618_power_info *info, + const union power_supply_propval *val) +{ + if (val->intval < CHG_MIN_CUR) + return -EINVAL; + + if (val->intval >= CHG_MAX_CUR) + return -EINVAL; + + return regmap_update_bits(info->rn5t618->regmap, + RN5T618_CHGISET, + 0x1F, TO_CUR_REG(val->intval)); +} + +static int rn5t618_battery_get_current_limit(struct rn5t618_power_info *info, + union power_supply_propval *val) +{ + unsigned int regval; + int ret; + + ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGISET, + ®val); + if (ret < 0) + return ret; + + val->intval = FROM_CUR_REG(regval); + + return 0; +} + static int rn5t618_battery_charge_full(struct rn5t618_power_info *info, union power_supply_propval *val) { @@ -323,6 +385,9 @@ static int rn5t618_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = POWER_SUPPLY_TECHNOLOGY_LION; break; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + ret = rn5t618_battery_get_current_limit(info, val); + break; case POWER_SUPPLY_PROP_CHARGE_FULL: ret = rn5t618_battery_charge_full(info, val); break; @@ -336,12 +401,38 @@ static int rn5t618_battery_get_property(struct power_supply *psy, return ret; } +static int rn5t618_battery_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct rn5t618_power_info *info = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + return rn5t618_battery_set_current_limit(info, val); + default: + return -EINVAL; + } +} + +static int rn5t618_battery_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + return true; + default: + return false; + } +} + static int rn5t618_adp_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct rn5t618_power_info *info = power_supply_get_drvdata(psy); unsigned int chgstate; + unsigned int regval; bool online; int ret; @@ -365,6 +456,14 @@ static int rn5t618_adp_get_property(struct power_supply *psy, val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = regmap_read(info->rn5t618->regmap, + RN5T618_REGISET1, ®val); + if (ret < 0) + return ret; + + val->intval = FROM_CUR_REG(regval); + break; default: return -EINVAL; } @@ -372,12 +471,79 @@ static int rn5t618_adp_get_property(struct power_supply *psy, return 0; } +static int rn5t618_adp_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct rn5t618_power_info *info = power_supply_get_drvdata(psy); + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + if (val->intval > ADP_MAX_CUR) + return -EINVAL; + + if (val->intval < CHG_MIN_CUR) + return -EINVAL; + + ret = regmap_write(info->rn5t618->regmap, RN5T618_REGISET1, + TO_CUR_REG(val->intval)); + if (ret < 0) + return ret; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int rn5t618_adp_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + return true; + default: + return false; + } +} + +static int rc5t619_usb_get_type(struct rn5t618_power_info *info, + union power_supply_propval *val) +{ + unsigned int regval; + int ret; + + ret = regmap_read(info->rn5t618->regmap, RN5T618_GCHGDET, ®val); + if (ret < 0) + return ret; + + switch (regval & GCHGDET_TYPE_MASK) { + case GCHGDET_TYPE_SDP: + val->intval = POWER_SUPPLY_USB_TYPE_SDP; + break; + case GCHGDET_TYPE_CDP: + val->intval = POWER_SUPPLY_USB_TYPE_CDP; + break; + case GCHGDET_TYPE_DCP: + val->intval = POWER_SUPPLY_USB_TYPE_DCP; + break; + default: + val->intval = POWER_SUPPLY_USB_TYPE_UNKNOWN; + } + + return 0; +} + static int rn5t618_usb_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct rn5t618_power_info *info = power_supply_get_drvdata(psy); unsigned int chgstate; + unsigned int regval; bool online; int ret; @@ -401,6 +567,28 @@ static int rn5t618_usb_get_property(struct power_supply *psy, val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; break; + case POWER_SUPPLY_PROP_USB_TYPE: + if (!online || (info->rn5t618->variant != RC5T619)) + return -ENODATA; + + return rc5t619_usb_get_type(info, val); + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGCTL1, + ®val); + if (ret < 0) + return ret; + + val->intval = 0; + if (regval & 2) { + ret = regmap_read(info->rn5t618->regmap, + RN5T618_REGISET2, + ®val); + if (ret < 0) + return ret; + + val->intval = FROM_CUR_REG(regval); + } + break; default: return -EINVAL; } @@ -408,12 +596,53 @@ static int rn5t618_usb_get_property(struct power_supply *psy, return 0; } +static int rn5t618_usb_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct rn5t618_power_info *info = power_supply_get_drvdata(psy); + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + if (val->intval > USB_MAX_CUR) + return -EINVAL; + + if (val->intval < CHG_MIN_CUR) + return -EINVAL; + + ret = regmap_write(info->rn5t618->regmap, RN5T618_REGISET2, + 0xE0 | TO_CUR_REG(val->intval)); + if (ret < 0) + return ret; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int rn5t618_usb_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + return true; + default: + return false; + } +} + static const struct power_supply_desc rn5t618_battery_desc = { .name = "rn5t618-battery", .type = POWER_SUPPLY_TYPE_BATTERY, .properties = rn5t618_battery_props, .num_properties = ARRAY_SIZE(rn5t618_battery_props), .get_property = rn5t618_battery_get_property, + .set_property = rn5t618_battery_set_property, + .property_is_writeable = rn5t618_battery_property_is_writeable, }; static const struct power_supply_desc rn5t618_adp_desc = { @@ -422,14 +651,20 @@ static const struct power_supply_desc rn5t618_adp_desc = { .properties = rn5t618_adp_props, .num_properties = ARRAY_SIZE(rn5t618_adp_props), .get_property = rn5t618_adp_get_property, + .set_property = rn5t618_adp_set_property, + .property_is_writeable = rn5t618_adp_property_is_writeable, }; static const struct power_supply_desc rn5t618_usb_desc = { .name = "rn5t618-usb", .type = POWER_SUPPLY_TYPE_USB, + .usb_types = rn5t618_usb_types, + .num_usb_types = ARRAY_SIZE(rn5t618_usb_types), .properties = rn5t618_usb_props, .num_properties = ARRAY_SIZE(rn5t618_usb_props), .get_property = rn5t618_usb_get_property, + .set_property = rn5t618_usb_set_property, + .property_is_writeable = rn5t618_usb_property_is_writeable, }; static irqreturn_t rn5t618_charger_irq(int irq, void *data) diff --git a/drivers/power/supply/rt5033_battery.c b/drivers/power/supply/rt5033_battery.c index f330452341f0..9ad0afe83d1b 100644 --- a/drivers/power/supply/rt5033_battery.c +++ b/drivers/power/supply/rt5033_battery.c @@ -164,9 +164,16 @@ static const struct i2c_device_id rt5033_battery_id[] = { }; MODULE_DEVICE_TABLE(i2c, rt5033_battery_id); +static const struct of_device_id rt5033_battery_of_match[] = { + { .compatible = "richtek,rt5033-battery", }, + { } +}; +MODULE_DEVICE_TABLE(of, rt5033_battery_of_match); + static struct i2c_driver rt5033_battery_driver = { .driver = { .name = "rt5033-battery", + .of_match_table = rt5033_battery_of_match, }, .probe = rt5033_battery_probe, .remove = rt5033_battery_remove, diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 8d7a10730e43..f84dbaab283a 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -189,6 +189,14 @@ static const enum power_supply_property sbs_properties[] = { /* Supports special manufacturer commands from TI BQ20Z65 and BQ20Z75 IC. */ #define SBS_FLAGS_TI_BQ20ZX5 BIT(0) +static const enum power_supply_property string_properties[] = { + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_MODEL_NAME, +}; + +#define NR_STRING_BUFFERS ARRAY_SIZE(string_properties) + struct sbs_info { struct i2c_client *client; struct power_supply *power_supply; @@ -202,11 +210,32 @@ struct sbs_info { struct delayed_work work; struct mutex mode_lock; u32 flags; + int technology; + char strings[NR_STRING_BUFFERS][I2C_SMBUS_BLOCK_MAX + 1]; }; -static char model_name[I2C_SMBUS_BLOCK_MAX + 1]; -static char manufacturer[I2C_SMBUS_BLOCK_MAX + 1]; -static char chemistry[I2C_SMBUS_BLOCK_MAX + 1]; +static char *sbs_get_string_buf(struct sbs_info *chip, + enum power_supply_property psp) +{ + int i = 0; + + for (i = 0; i < NR_STRING_BUFFERS; i++) + if (string_properties[i] == psp) + return chip->strings[i]; + + return ERR_PTR(-EINVAL); +} + +static void sbs_invalidate_cached_props(struct sbs_info *chip) +{ + int i = 0; + + chip->technology = -1; + + for (i = 0; i < NR_STRING_BUFFERS; i++) + chip->strings[i][0] = 0; +} + static bool force_load; static int sbs_read_word_data(struct i2c_client *client, u8 address); @@ -244,6 +273,7 @@ static int sbs_update_presence(struct sbs_info *chip, bool is_present) chip->is_present = false; /* Disable PEC when no device is present */ client->flags &= ~I2C_CLIENT_PEC; + sbs_invalidate_cached_props(chip); return 0; } @@ -640,17 +670,45 @@ static int sbs_get_battery_property(struct i2c_client *client, return 0; } -static int sbs_get_battery_string_property(struct i2c_client *client, - int reg_offset, enum power_supply_property psp, char *val) +static int sbs_get_property_index(struct i2c_client *client, + enum power_supply_property psp) { - s32 ret; + int count; - ret = sbs_read_string_data(client, sbs_data[reg_offset].addr, val); + for (count = 0; count < ARRAY_SIZE(sbs_data); count++) + if (psp == sbs_data[count].psp) + return count; - if (ret < 0) - return ret; + dev_warn(&client->dev, + "%s: Invalid Property - %d\n", __func__, psp); - return 0; + return -EINVAL; +} + +static const char *sbs_get_constant_string(struct sbs_info *chip, + enum power_supply_property psp) +{ + int ret; + char *buf; + u8 addr; + + buf = sbs_get_string_buf(chip, psp); + if (IS_ERR(buf)) + return buf; + + if (!buf[0]) { + ret = sbs_get_property_index(chip->client, psp); + if (ret < 0) + return ERR_PTR(ret); + + addr = sbs_data[ret].addr; + + ret = sbs_read_string_data(chip->client, addr, buf); + if (ret < 0) + return ERR_PTR(ret); + } + + return buf; } static void sbs_unit_adjustment(struct i2c_client *client, @@ -773,48 +831,36 @@ static int sbs_get_battery_serial_number(struct i2c_client *client, return 0; } -static int sbs_get_property_index(struct i2c_client *client, - enum power_supply_property psp) -{ - int count; - for (count = 0; count < ARRAY_SIZE(sbs_data); count++) - if (psp == sbs_data[count].psp) - return count; - - dev_warn(&client->dev, - "%s: Invalid Property - %d\n", __func__, psp); - - return -EINVAL; -} - -static int sbs_get_chemistry(struct i2c_client *client, +static int sbs_get_chemistry(struct sbs_info *chip, union power_supply_propval *val) { - enum power_supply_property psp = POWER_SUPPLY_PROP_TECHNOLOGY; - int ret; + const char *chemistry; - ret = sbs_get_property_index(client, psp); - if (ret < 0) - return ret; + if (chip->technology != -1) { + val->intval = chip->technology; + return 0; + } - ret = sbs_get_battery_string_property(client, ret, psp, - chemistry); - if (ret < 0) - return ret; + chemistry = sbs_get_constant_string(chip, POWER_SUPPLY_PROP_TECHNOLOGY); + + if (IS_ERR(chemistry)) + return PTR_ERR(chemistry); if (!strncasecmp(chemistry, "LION", 4)) - val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + chip->technology = POWER_SUPPLY_TECHNOLOGY_LION; else if (!strncasecmp(chemistry, "LiP", 3)) - val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO; + chip->technology = POWER_SUPPLY_TECHNOLOGY_LIPO; else if (!strncasecmp(chemistry, "NiCd", 4)) - val->intval = POWER_SUPPLY_TECHNOLOGY_NiCd; + chip->technology = POWER_SUPPLY_TECHNOLOGY_NiCd; else if (!strncasecmp(chemistry, "NiMH", 4)) - val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH; + chip->technology = POWER_SUPPLY_TECHNOLOGY_NiMH; else - val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + chip->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + + if (chip->technology == POWER_SUPPLY_TECHNOLOGY_UNKNOWN) + dev_warn(&chip->client->dev, "Unknown chemistry: %s\n", chemistry); - if (val->intval == POWER_SUPPLY_TECHNOLOGY_UNKNOWN) - dev_warn(&client->dev, "Unknown chemistry: %s\n", chemistry); + val->intval = chip->technology; return 0; } @@ -858,6 +904,7 @@ static int sbs_get_property(struct power_supply *psy, int ret = 0; struct sbs_info *chip = power_supply_get_drvdata(psy); struct i2c_client *client = chip->client; + const char *str; if (chip->gpio_detect) { ret = gpiod_get_value_cansleep(chip->gpio_detect); @@ -883,7 +930,7 @@ static int sbs_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_TECHNOLOGY: - ret = sbs_get_chemistry(client, val); + ret = sbs_get_chemistry(chip, val); if (ret < 0) break; @@ -935,23 +982,12 @@ static int sbs_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_MODEL_NAME: - ret = sbs_get_property_index(client, psp); - if (ret < 0) - break; - - ret = sbs_get_battery_string_property(client, ret, psp, - model_name); - val->strval = model_name; - break; - case POWER_SUPPLY_PROP_MANUFACTURER: - ret = sbs_get_property_index(client, psp); - if (ret < 0) - break; - - ret = sbs_get_battery_string_property(client, ret, psp, - manufacturer); - val->strval = manufacturer; + str = sbs_get_constant_string(chip, psp); + if (IS_ERR(str)) + ret = PTR_ERR(str); + else + val->strval = str; break; case POWER_SUPPLY_PROP_MANUFACTURE_YEAR: @@ -1098,6 +1134,7 @@ static int sbs_probe(struct i2c_client *client) psy_cfg.of_node = client->dev.of_node; psy_cfg.drv_data = chip; chip->last_state = POWER_SUPPLY_STATUS_UNKNOWN; + sbs_invalidate_cached_props(chip); mutex_init(&chip->mode_lock); /* use pdata if available, fall back to DT properties, diff --git a/drivers/power/supply/sc2731_charger.c b/drivers/power/supply/sc2731_charger.c index 335cb857ef30..288b79836c13 100644 --- a/drivers/power/supply/sc2731_charger.c +++ b/drivers/power/supply/sc2731_charger.c @@ -524,6 +524,7 @@ static const struct of_device_id sc2731_charger_of_match[] = { { .compatible = "sprd,sc2731-charger", }, { } }; +MODULE_DEVICE_TABLE(of, sc2731_charger_of_match); static struct platform_driver sc2731_charger_driver = { .driver = { diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index 9c627618c224..1ae8374e1ceb 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -1342,6 +1342,7 @@ static const struct of_device_id sc27xx_fgu_of_match[] = { { .compatible = "sprd,sc2731-fgu", }, { } }; +MODULE_DEVICE_TABLE(of, sc27xx_fgu_of_match); static struct platform_driver sc27xx_fgu_driver = { .probe = sc27xx_fgu_probe, diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c index 3376f42d46c3..df240420f2de 100644 --- a/drivers/power/supply/smb347-charger.c +++ b/drivers/power/supply/smb347-charger.c @@ -10,7 +10,6 @@ #include <linux/delay.h> #include <linux/err.h> -#include <linux/gpio.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> diff --git a/drivers/power/supply/surface_battery.c b/drivers/power/supply/surface_battery.c index 7efa431a62b2..5ec2e6bb2465 100644 --- a/drivers/power/supply/surface_battery.c +++ b/drivers/power/supply/surface_battery.c @@ -345,6 +345,16 @@ static u32 spwr_notify_bat(struct ssam_event_notifier *nf, const struct ssam_eve struct spwr_battery_device *bat = container_of(nf, struct spwr_battery_device, notif); int status; + /* + * We cannot use strict matching when registering the notifier as the + * EC expects us to register it against instance ID 0. Strict matching + * would thus drop events, as those may have non-zero instance IDs in + * this subsystem. So we need to check the instance ID of the event + * here manually. + */ + if (event->instance_id != bat->sdev->uid.instance) + return 0; + dev_dbg(&bat->sdev->dev, "power event (cid = %#04x, iid = %#04x, tid = %#04x)\n", event->command_id, event->instance_id, event->target_id); @@ -720,8 +730,8 @@ static void spwr_battery_init(struct spwr_battery_device *bat, struct ssam_devic bat->notif.base.fn = spwr_notify_bat; bat->notif.event.reg = registry; bat->notif.event.id.target_category = sdev->uid.category; - bat->notif.event.id.instance = 0; - bat->notif.event.mask = SSAM_EVENT_MASK_STRICT; + bat->notif.event.id.instance = 0; /* need to register with instance 0 */ + bat->notif.event.mask = SSAM_EVENT_MASK_TARGET; bat->notif.event.flags = SSAM_EVENT_SEQUENCED; bat->psy_desc.name = bat->name; diff --git a/drivers/power/supply/surface_charger.c b/drivers/power/supply/surface_charger.c index 81a5b79822c9..a060c36c7766 100644 --- a/drivers/power/supply/surface_charger.c +++ b/drivers/power/supply/surface_charger.c @@ -66,7 +66,7 @@ struct spwr_ac_device { static int spwr_ac_update_unlocked(struct spwr_ac_device *ac) { - u32 old = ac->state; + __le32 old = ac->state; int status; lockdep_assert_held(&ac->lock); |