summaryrefslogtreecommitdiffstats
path: root/drivers/power
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2021-12-08 23:43:50 -0800
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2021-12-08 23:43:50 -0800
commit5d8dfaa71d87f742c53309b95cb6a8b274119027 (patch)
tree83fa5199868fb98dbe7dcb0791bc462bac77265b /drivers/power
parent8c374ef45416281c7172dc29ed438dfb445795f1 (diff)
parent8bb7eca972ad531c9b149c0a51ab43a417385813 (diff)
downloadlinux-5d8dfaa71d87f742c53309b95cb6a8b274119027.tar.bz2
Merge tag 'v5.15' into next
Sync up with the mainline to get the latest APIs and DT bindings.
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/reset/Kconfig6
-rw-r--r--drivers/power/reset/Makefile1
-rw-r--r--drivers/power/reset/at91-sama5d2_shdwc.c4
-rw-r--r--drivers/power/reset/gpio-poweroff.c1
-rw-r--r--drivers/power/reset/keystone-reset.c1
-rw-r--r--drivers/power/reset/linkstation-poweroff.c77
-rw-r--r--drivers/power/reset/ltc2952-poweroff.c1
-rw-r--r--drivers/power/reset/regulator-poweroff.c1
-rw-r--r--drivers/power/reset/tps65086-restart.c98
-rw-r--r--drivers/power/supply/Kconfig38
-rw-r--r--drivers/power/supply/Makefile5
-rw-r--r--drivers/power/supply/ab8500-bm.h224
-rw-r--r--drivers/power/supply/ab8500-chargalg.h2
-rw-r--r--drivers/power/supply/ab8500_bmdata.c34
-rw-r--r--drivers/power/supply/ab8500_btemp.c148
-rw-r--r--drivers/power/supply/ab8500_chargalg.c (renamed from drivers/power/supply/abx500_chargalg.c)677
-rw-r--r--drivers/power/supply/ab8500_charger.c383
-rw-r--r--drivers/power/supply/ab8500_fg.c167
-rw-r--r--drivers/power/supply/axp20x_battery.c17
-rw-r--r--drivers/power/supply/axp288_charger.c11
-rw-r--r--drivers/power/supply/axp288_fuel_gauge.c513
-rw-r--r--drivers/power/supply/bd70528-charger.c710
-rw-r--r--drivers/power/supply/bq24190_charger.c11
-rw-r--r--drivers/power/supply/bq24735-charger.c27
-rw-r--r--drivers/power/supply/charger-manager.c1
-rw-r--r--drivers/power/supply/cpcap-battery.c19
-rw-r--r--drivers/power/supply/cpcap-charger.c39
-rw-r--r--drivers/power/supply/cros_peripheral_charger.c386
-rw-r--r--drivers/power/supply/cw2015_battery.c4
-rw-r--r--drivers/power/supply/max17040_battery.c42
-rw-r--r--drivers/power/supply/max17042_battery.c63
-rw-r--r--drivers/power/supply/mt6360_charger.c867
-rw-r--r--drivers/power/supply/pm2301_charger.c1249
-rw-r--r--drivers/power/supply/power_supply_core.c19
-rw-r--r--drivers/power/supply/qcom_smbb.c5
-rw-r--r--drivers/power/supply/rn5t618_power.c273
-rw-r--r--drivers/power/supply/rt5033_battery.c7
-rw-r--r--drivers/power/supply/sbs-battery.c169
-rw-r--r--drivers/power/supply/sc2731_charger.c1
-rw-r--r--drivers/power/supply/sc27xx_fuel_gauge.c5
-rw-r--r--drivers/power/supply/smb347-charger.c272
-rw-r--r--drivers/power/supply/surface_battery.c14
-rw-r--r--drivers/power/supply/surface_charger.c2
43 files changed, 3211 insertions, 3383 deletions
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index 4d1192062508..4b563db3ab3e 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -204,6 +204,12 @@ config POWER_RESET_ST
help
Reset support for STMicroelectronics boards.
+config POWER_RESET_TPS65086
+ bool "TPS65086 restart driver"
+ depends on MFD_TPS65086
+ help
+ This driver adds support for resetting the TPS65086 PMIC on restart.
+
config POWER_RESET_VERSATILE
bool "ARM Versatile family reboot driver"
depends on ARM
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
index cf3f4d02d8a5..f606a2f60539 100644
--- a/drivers/power/reset/Makefile
+++ b/drivers/power/reset/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
obj-$(CONFIG_POWER_RESET_REGULATOR) += regulator-poweroff.o
obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o
obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o
+obj-$(CONFIG_POWER_RESET_TPS65086) += tps65086-restart.o
obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o
obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o
obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o
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/linkstation-poweroff.c b/drivers/power/reset/linkstation-poweroff.c
index f1e843df0e16..02f5fdb8ffc4 100644
--- a/drivers/power/reset/linkstation-poweroff.c
+++ b/drivers/power/reset/linkstation-poweroff.c
@@ -19,6 +19,7 @@
#define MII_MARVELL_PHY_PAGE 22
#define MII_PHY_LED_CTRL 16
+#define MII_PHY_LED_POL_CTRL 17
#define MII_88E1318S_PHY_LED_TCR 18
#define MII_88E1318S_PHY_WOL_CTRL 16
#define MII_M1011_IEVENT 19
@@ -29,11 +30,23 @@
#define LED2_FORCE_ON (0x8 << 8)
#define LEDMASK GENMASK(11,8)
+#define MII_88E1318S_PHY_LED_POL_LED2 BIT(4)
+
+struct power_off_cfg {
+ char *mdio_node_name;
+ void (*phy_set_reg)(bool restart);
+};
+
static struct phy_device *phydev;
+static const struct power_off_cfg *cfg;
-static void mvphy_reg_intn(u16 data)
+static void linkstation_mvphy_reg_intn(bool restart)
{
int rc = 0, saved_page;
+ u16 data = 0;
+
+ if (restart)
+ data = MII_88E1318S_PHY_LED_TCR_FORCE_INT;
saved_page = phy_select_page(phydev, MII_MARVELL_LED_PAGE);
if (saved_page < 0)
@@ -66,11 +79,52 @@ err:
dev_err(&phydev->mdio.dev, "Write register failed, %d\n", rc);
}
+static void readynas_mvphy_set_reg(bool restart)
+{
+ int rc = 0, saved_page;
+ u16 data = 0;
+
+ if (restart)
+ data = MII_88E1318S_PHY_LED_POL_LED2;
+
+ saved_page = phy_select_page(phydev, MII_MARVELL_LED_PAGE);
+ if (saved_page < 0)
+ goto err;
+
+ /* Set the LED[2].0 Polarity bit to the required state */
+ __phy_modify(phydev, MII_PHY_LED_POL_CTRL,
+ MII_88E1318S_PHY_LED_POL_LED2, data);
+
+ if (!data) {
+ /* If WOL was enabled and a magic packet was received before powering
+ * off, we won't be able to wake up by sending another magic packet.
+ * Clear WOL status.
+ */
+ __phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_MARVELL_WOL_PAGE);
+ __phy_set_bits(phydev, MII_88E1318S_PHY_WOL_CTRL,
+ MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS);
+ }
+err:
+ rc = phy_restore_page(phydev, saved_page, rc);
+ if (rc < 0)
+ dev_err(&phydev->mdio.dev, "Write register failed, %d\n", rc);
+}
+
+static const struct power_off_cfg linkstation_power_off_cfg = {
+ .mdio_node_name = "mdio",
+ .phy_set_reg = linkstation_mvphy_reg_intn,
+};
+
+static const struct power_off_cfg readynas_power_off_cfg = {
+ .mdio_node_name = "mdio-bus",
+ .phy_set_reg = readynas_mvphy_set_reg,
+};
+
static int linkstation_reboot_notifier(struct notifier_block *nb,
unsigned long action, void *unused)
{
if (action == SYS_RESTART)
- mvphy_reg_intn(MII_88E1318S_PHY_LED_TCR_FORCE_INT);
+ cfg->phy_set_reg(true);
return NOTIFY_DONE;
}
@@ -82,14 +136,21 @@ static struct notifier_block linkstation_reboot_nb = {
static void linkstation_poweroff(void)
{
unregister_reboot_notifier(&linkstation_reboot_nb);
- mvphy_reg_intn(0);
+ cfg->phy_set_reg(false);
kernel_restart("Power off");
}
static const struct of_device_id ls_poweroff_of_match[] = {
- { .compatible = "buffalo,ls421d" },
- { .compatible = "buffalo,ls421de" },
+ { .compatible = "buffalo,ls421d",
+ .data = &linkstation_power_off_cfg,
+ },
+ { .compatible = "buffalo,ls421de",
+ .data = &linkstation_power_off_cfg,
+ },
+ { .compatible = "netgear,readynas-duo-v2",
+ .data = &readynas_power_off_cfg,
+ },
{ },
};
@@ -97,13 +158,17 @@ static int __init linkstation_poweroff_init(void)
{
struct mii_bus *bus;
struct device_node *dn;
+ const struct of_device_id *match;
dn = of_find_matching_node(NULL, ls_poweroff_of_match);
if (!dn)
return -ENODEV;
of_node_put(dn);
- dn = of_find_node_by_name(NULL, "mdio");
+ match = of_match_node(ls_poweroff_of_match, dn);
+ cfg = match->data;
+
+ dn = of_find_node_by_name(NULL, cfg->mdio_node_name);
if (!dn)
return -ENODEV;
diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c
index d1495af30081..8688c8ba8894 100644
--- a/drivers/power/reset/ltc2952-poweroff.c
+++ b/drivers/power/reset/ltc2952-poweroff.c
@@ -52,6 +52,7 @@
#include <linux/slab.h>
#include <linux/kmod.h>
#include <linux/module.h>
+#include <linux/panic_notifier.h>
#include <linux/mod_devicetable.h>
#include <linux/gpio/consumer.h>
#include <linux/reboot.h>
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/reset/tps65086-restart.c b/drivers/power/reset/tps65086-restart.c
new file mode 100644
index 000000000000..78b89f745a3d
--- /dev/null
+++ b/drivers/power/reset/tps65086-restart.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Emil Renner Berthing
+ */
+
+#include <linux/mfd/tps65086.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+
+struct tps65086_restart {
+ struct notifier_block handler;
+ struct device *dev;
+};
+
+static int tps65086_restart_notify(struct notifier_block *this,
+ unsigned long mode, void *cmd)
+{
+ struct tps65086_restart *tps65086_restart =
+ container_of(this, struct tps65086_restart, handler);
+ struct tps65086 *tps65086 = dev_get_drvdata(tps65086_restart->dev->parent);
+ int ret;
+
+ ret = regmap_write(tps65086->regmap, TPS65086_FORCESHUTDN, 1);
+ if (ret) {
+ dev_err(tps65086_restart->dev, "%s: error writing to tps65086 pmic: %d\n",
+ __func__, ret);
+ return NOTIFY_DONE;
+ }
+
+ /* give it a little time */
+ mdelay(200);
+
+ WARN_ON(1);
+
+ return NOTIFY_DONE;
+}
+
+static int tps65086_restart_probe(struct platform_device *pdev)
+{
+ struct tps65086_restart *tps65086_restart;
+ int ret;
+
+ tps65086_restart = devm_kzalloc(&pdev->dev, sizeof(*tps65086_restart), GFP_KERNEL);
+ if (!tps65086_restart)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, tps65086_restart);
+
+ tps65086_restart->handler.notifier_call = tps65086_restart_notify;
+ tps65086_restart->handler.priority = 192;
+ tps65086_restart->dev = &pdev->dev;
+
+ ret = register_restart_handler(&tps65086_restart->handler);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cannot register restart handler: %d\n",
+ __func__, ret);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int tps65086_restart_remove(struct platform_device *pdev)
+{
+ struct tps65086_restart *tps65086_restart = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = unregister_restart_handler(&tps65086_restart->handler);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cannot unregister restart handler: %d\n",
+ __func__, ret);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id tps65086_restart_id_table[] = {
+ { "tps65086-reset", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, tps65086_restart_id_table);
+
+static struct platform_driver tps65086_restart_driver = {
+ .driver = {
+ .name = "tps65086-restart",
+ },
+ .probe = tps65086_restart_probe,
+ .remove = tps65086_restart_remove,
+ .id_table = tps65086_restart_id_table,
+};
+module_platform_driver(tps65086_restart_driver);
+
+MODULE_AUTHOR("Emil Renner Berthing <kernel@esmil.dk>");
+MODULE_DESCRIPTION("TPS65086 restart driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index e696364126f1..fcc7534edcb2 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -358,7 +358,7 @@ config AXP288_CHARGER
config AXP288_FUEL_GAUGE
tristate "X-Powers AXP288 Fuel Gauge"
- depends on MFD_AXP20X && IIO
+ depends on MFD_AXP20X && IIO && IOSF_MBI
help
Say yes here to have support for X-Power power management IC (PMIC)
Fuel Gauge. The device provides battery statistics and status
@@ -577,6 +577,17 @@ config CHARGER_MP2629
Battery charger. This driver provides Battery charger power management
functions on the systems.
+config CHARGER_MT6360
+ tristate "Mediatek MT6360 Charger Driver"
+ depends on MFD_MT6360
+ depends on REGULATOR
+ select LINEAR_RANGES
+ help
+ Say Y here to enable MT6360 Charger Part.
+ The device supports High-Accuracy Voltage/Current Regulation,
+ Average Input Current Regulation, Battery Temperature Sensing,
+ Over-Temperature Protection, DPDM Detection for BC1.2.
+
config CHARGER_QCOM_SMBB
tristate "Qualcomm Switch-Mode Battery Charger and Boost"
depends on MFD_SPMI_PMIC || COMPILE_TEST
@@ -669,6 +680,7 @@ config CHARGER_BQ256XX
config CHARGER_SMB347
tristate "Summit Microelectronics SMB3XX Battery Charger"
depends on I2C
+ depends on REGULATOR
select REGMAP_I2C
help
Say Y to include support for Summit Microelectronics SMB345,
@@ -712,7 +724,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
@@ -735,6 +748,16 @@ config CHARGER_CROS_USBPD
what is connected to USB PD ports from the EC and converts
that into power_supply properties.
+config CHARGER_CROS_PCHG
+ tristate "ChromeOS EC based peripheral charger"
+ depends on MFD_CROS_EC_DEV
+ default MFD_CROS_EC_DEV
+ help
+ Say Y here to enable ChromeOS EC based peripheral charge driver.
+ This driver gets various information about the devices connected to
+ the peripheral charge ports from the EC and converts that into
+ power_supply properties.
+
config CHARGER_SC2731
tristate "Spreadtrum SC2731 charger driver"
depends on MFD_SC27XX_PMIC || COMPILE_TEST
@@ -760,15 +783,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
@@ -790,6 +804,8 @@ config CHARGER_WILCO
config RN5T618_POWER
tristate "RN5T618 charger/fuel gauge support"
depends on MFD_RN5T618
+ depends on RN5T618_ADC
+ depends on IIO
help
Say Y here to have support for RN5T618 PMIC family fuel gauge and charger.
This driver can also be built as a module. If so, the module will be
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index a7309a3d1a47..4e55a11aab79 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 ab8500_chargalg.o
obj-$(CONFIG_CHARGER_CPCAP) += cpcap-charger.o
obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
@@ -78,6 +78,7 @@ obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
obj-$(CONFIG_CHARGER_MP2629) += mp2629_charger.o
+obj-$(CONFIG_CHARGER_MT6360) += mt6360_charger.o
obj-$(CONFIG_CHARGER_QCOM_SMBB) += qcom_smbb.o
obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o
@@ -93,10 +94,10 @@ obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o
obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o
obj-$(CONFIG_AXP288_CHARGER) += axp288_charger.o
obj-$(CONFIG_CHARGER_CROS_USBPD) += cros_usbpd-charger.o
+obj-$(CONFIG_CHARGER_CROS_PCHG) += cros_peripheral_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..d11405b7ee1a 100644
--- a/drivers/power/supply/ab8500-bm.h
+++ b/drivers/power/supply/ab8500-bm.h
@@ -269,43 +269,43 @@ enum bup_vch_sel {
/*
* ADC for the battery thermistor.
- * When using the ABx500_ADC_THERM_BATCTRL the battery ID resistor is combined
+ * When using the AB8500_ADC_THERM_BATCTRL the battery ID resistor is combined
* with a NTC resistor to both identify the battery and to measure its
* temperature. Different phone manufactures uses different techniques to both
* identify the battery and to read its temperature.
*/
-enum abx500_adc_therm {
- ABx500_ADC_THERM_BATCTRL,
- ABx500_ADC_THERM_BATTEMP,
+enum ab8500_adc_therm {
+ AB8500_ADC_THERM_BATCTRL,
+ AB8500_ADC_THERM_BATTEMP,
};
/**
- * struct abx500_res_to_temp - defines one point in a temp to res curve. To
+ * struct ab8500_res_to_temp - defines one point in a temp to res curve. To
* be used in battery packs that combines the identification resistor with a
* NTC resistor.
* @temp: battery pack temperature in Celsius
* @resist: NTC resistor net total resistance
*/
-struct abx500_res_to_temp {
+struct ab8500_res_to_temp {
int temp;
int resist;
};
/**
- * struct abx500_v_to_cap - Table for translating voltage to capacity
+ * struct ab8500_v_to_cap - Table for translating voltage to capacity
* @voltage: Voltage in mV
* @capacity: Capacity in percent
*/
-struct abx500_v_to_cap {
+struct ab8500_v_to_cap {
int voltage;
int capacity;
};
/* Forward declaration */
-struct abx500_fg;
+struct ab8500_fg;
/**
- * struct abx500_fg_parameters - Fuel gauge algorithm parameters, in seconds
+ * struct ab8500_fg_parameters - Fuel gauge algorithm parameters, in seconds
* if not specified
* @recovery_sleep_timer: Time between measurements while recovering
* @recovery_total_time: Total recovery time
@@ -333,7 +333,7 @@ struct abx500_fg;
* @pcut_max_restart: Max number of restarts
* @pcut_debounce_time: Sets battery debounce time
*/
-struct abx500_fg_parameters {
+struct ab8500_fg_parameters {
int recovery_sleep_timer;
int recovery_total_time;
int init_timer;
@@ -357,13 +357,13 @@ struct abx500_fg_parameters {
};
/**
- * struct abx500_charger_maximization - struct used by the board config.
+ * struct ab8500_charger_maximization - struct used by the board config.
* @use_maxi: Enable maximization for this battery type
* @maxi_chg_curr: Maximum charger current allowed
* @maxi_wait_cycles: cycles to wait before setting charger current
* @charger_curr_step delta between two charger current settings (mA)
*/
-struct abx500_maxim_parameters {
+struct ab8500_maxim_parameters {
bool ena_maxi;
int chg_curr;
int wait_cycles;
@@ -371,7 +371,7 @@ struct abx500_maxim_parameters {
};
/**
- * struct abx500_battery_type - different batteries supported
+ * struct ab8500_battery_type - different batteries supported
* @name: battery technology
* @resis_high: battery upper resistance limit
* @resis_low: battery lower resistance limit
@@ -400,7 +400,7 @@ struct abx500_maxim_parameters {
* @n_batres_tbl_elements number of elements in the batres_tbl
* @batres_tbl battery internal resistance vs temperature table
*/
-struct abx500_battery_type {
+struct ab8500_battery_type {
int name;
int resis_high;
int resis_low;
@@ -421,22 +421,22 @@ struct abx500_battery_type {
int low_high_vol_lvl;
int battery_resistance;
int n_temp_tbl_elements;
- const struct abx500_res_to_temp *r_to_t_tbl;
+ const struct ab8500_res_to_temp *r_to_t_tbl;
int n_v_cap_tbl_elements;
- const struct abx500_v_to_cap *v_to_cap_tbl;
+ const struct ab8500_v_to_cap *v_to_cap_tbl;
int n_batres_tbl_elements;
const struct batres_vs_temp *batres_tbl;
};
/**
- * struct abx500_bm_capacity_levels - abx500 capacity level data
+ * struct ab8500_bm_capacity_levels - ab8500 capacity level data
* @critical: critical capacity level in percent
* @low: low capacity level in percent
* @normal: normal capacity level in percent
* @high: high capacity level in percent
* @full: full capacity level in percent
*/
-struct abx500_bm_capacity_levels {
+struct ab8500_bm_capacity_levels {
int critical;
int low;
int normal;
@@ -445,13 +445,13 @@ struct abx500_bm_capacity_levels {
};
/**
- * struct abx500_bm_charger_parameters - Charger specific parameters
+ * struct ab8500_bm_charger_parameters - Charger specific parameters
* @usb_volt_max: maximum allowed USB charger voltage in mV
* @usb_curr_max: maximum allowed USB charger current in mA
* @ac_volt_max: maximum allowed AC charger voltage in mV
* @ac_curr_max: maximum allowed AC charger current in mA
*/
-struct abx500_bm_charger_parameters {
+struct ab8500_bm_charger_parameters {
int usb_volt_max;
int usb_curr_max;
int ac_volt_max;
@@ -459,7 +459,7 @@ struct abx500_bm_charger_parameters {
};
/**
- * struct abx500_bm_data - abx500 battery management data
+ * struct ab8500_bm_data - ab8500 battery management data
* @temp_under under this temp, charging is stopped
* @temp_low between this temp and temp_under charging is reduced
* @temp_high between this temp and temp_over charging is reduced
@@ -473,7 +473,7 @@ struct abx500_bm_charger_parameters {
* @bkup_bat_i current which we charge the backup battery with
* @no_maintenance indicates that maintenance charging is disabled
* @capacity_scaling indicates whether capacity scaling is to be used
- * @abx500_adc_therm placement of thermistor, batctrl or battemp adc
+ * @ab8500_adc_therm placement of thermistor, batctrl or battemp adc
* @chg_unknown_bat flag to enable charging of unknown batteries
* @enable_overshoot flag to enable VBAT overshoot control
* @auto_trig flag to enable auto adc trigger
@@ -494,7 +494,7 @@ struct abx500_bm_charger_parameters {
* @chg_params charger parameters
* @fg_params fuel gauge parameters
*/
-struct abx500_bm_data {
+struct ab8500_bm_data {
int temp_under;
int temp_low;
int temp_high;
@@ -506,15 +506,12 @@ 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;
bool enable_overshoot;
bool auto_trig;
- enum abx500_adc_therm adc_therm;
+ enum ab8500_adc_therm adc_therm;
int fg_res;
int n_btypes;
int batt_id;
@@ -526,11 +523,11 @@ struct abx500_bm_data {
int n_chg_in_curr;
int *chg_output_curr;
int *chg_input_curr;
- const struct abx500_maxim_parameters *maxi;
- const struct abx500_bm_capacity_levels *cap_levels;
- struct abx500_battery_type *bat_type;
- const struct abx500_bm_charger_parameters *chg_params;
- const struct abx500_fg_parameters *fg_params;
+ const struct ab8500_maxim_parameters *maxi;
+ const struct ab8500_bm_capacity_levels *cap_levels;
+ struct ab8500_battery_type *bat_type;
+ const struct ab8500_bm_charger_parameters *chg_params;
+ const struct ab8500_fg_parameters *fg_params;
};
enum {
@@ -564,160 +561,7 @@ struct batres_vs_temp {
/* Forward declaration */
struct ab8500_fg;
-/**
- * struct ab8500_fg_parameters - Fuel gauge algorithm parameters, in seconds
- * if not specified
- * @recovery_sleep_timer: Time between measurements while recovering
- * @recovery_total_time: Total recovery time
- * @init_timer: Measurement interval during startup
- * @init_discard_time: Time we discard voltage measurement at startup
- * @init_total_time: Total init time during startup
- * @high_curr_time: Time current has to be high to go to recovery
- * @accu_charging: FG accumulation time while charging
- * @accu_high_curr: FG accumulation time in high current mode
- * @high_curr_threshold: High current threshold, in mA
- * @lowbat_threshold: Low battery threshold, in mV
- * @battok_falling_th_sel0 Threshold in mV for battOk signal sel0
- * Resolution in 50 mV step.
- * @battok_raising_th_sel1 Threshold in mV for battOk signal sel1
- * Resolution in 50 mV step.
- * @user_cap_limit Capacity reported from user must be within this
- * limit to be considered as sane, in percentage
- * points.
- * @maint_thres This is the threshold where we stop reporting
- * battery full while in maintenance, in per cent
- * @pcut_enable: Enable power cut feature in ab8505
- * @pcut_max_time: Max time threshold
- * @pcut_flag_time: Flagtime threshold
- * @pcut_max_restart: Max number of restarts
- * @pcut_debunce_time: Sets battery debounce time
- */
-struct ab8500_fg_parameters {
- int recovery_sleep_timer;
- int recovery_total_time;
- int init_timer;
- int init_discard_time;
- int init_total_time;
- int high_curr_time;
- int accu_charging;
- int accu_high_curr;
- int high_curr_threshold;
- int lowbat_threshold;
- int battok_falling_th_sel0;
- int battok_raising_th_sel1;
- int user_cap_limit;
- int maint_thres;
- bool pcut_enable;
- u8 pcut_max_time;
- u8 pcut_flag_time;
- u8 pcut_max_restart;
- u8 pcut_debunce_time;
-};
-
-/**
- * struct ab8500_charger_maximization - struct used by the board config.
- * @use_maxi: Enable maximization for this battery type
- * @maxi_chg_curr: Maximum charger current allowed
- * @maxi_wait_cycles: cycles to wait before setting charger current
- * @charger_curr_step delta between two charger current settings (mA)
- */
-struct ab8500_maxim_parameters {
- bool ena_maxi;
- int chg_curr;
- int wait_cycles;
- int charger_curr_step;
-};
-
-/**
- * struct ab8500_bm_capacity_levels - ab8500 capacity level data
- * @critical: critical capacity level in percent
- * @low: low capacity level in percent
- * @normal: normal capacity level in percent
- * @high: high capacity level in percent
- * @full: full capacity level in percent
- */
-struct ab8500_bm_capacity_levels {
- int critical;
- int low;
- int normal;
- int high;
- int full;
-};
-
-/**
- * struct ab8500_bm_charger_parameters - Charger specific parameters
- * @usb_volt_max: maximum allowed USB charger voltage in mV
- * @usb_curr_max: maximum allowed USB charger current in mA
- * @ac_volt_max: maximum allowed AC charger voltage in mV
- * @ac_curr_max: maximum allowed AC charger current in mA
- */
-struct ab8500_bm_charger_parameters {
- int usb_volt_max;
- int usb_curr_max;
- int ac_volt_max;
- int ac_curr_max;
-};
-
-/**
- * struct ab8500_bm_data - ab8500 battery management data
- * @temp_under under this temp, charging is stopped
- * @temp_low between this temp and temp_under charging is reduced
- * @temp_high between this temp and temp_over charging is reduced
- * @temp_over over this temp, charging is stopped
- * @temp_interval_chg temperature measurement interval in s when charging
- * @temp_interval_nochg temperature measurement interval in s when not charging
- * @main_safety_tmr_h safety timer for main charger
- * @usb_safety_tmr_h safety timer for usb charger
- * @bkup_bat_v voltage which we charge the backup battery with
- * @bkup_bat_i current which we charge the backup battery with
- * @no_maintenance indicates that maintenance charging is disabled
- * @capacity_scaling indicates whether capacity scaling is to be used
- * @adc_therm placement of thermistor, batctrl or battemp adc
- * @chg_unknown_bat flag to enable charging of unknown batteries
- * @enable_overshoot flag to enable VBAT overshoot control
- * @fg_res resistance of FG resistor in 0.1mOhm
- * @n_btypes number of elements in array bat_type
- * @batt_id index of the identified battery in array bat_type
- * @interval_charging charge alg cycle period time when charging (sec)
- * @interval_not_charging charge alg cycle period time when not charging (sec)
- * @temp_hysteresis temperature hysteresis
- * @gnd_lift_resistance Battery ground to phone ground resistance (mOhm)
- * @maxi: maximization parameters
- * @cap_levels capacity in percent for the different capacity levels
- * @bat_type table of supported battery types
- * @chg_params charger parameters
- * @fg_params fuel gauge parameters
- */
-struct ab8500_bm_data {
- int temp_under;
- int temp_low;
- int temp_high;
- int temp_over;
- int temp_interval_chg;
- int temp_interval_nochg;
- int main_safety_tmr_h;
- int usb_safety_tmr_h;
- int bkup_bat_v;
- int bkup_bat_i;
- bool no_maintenance;
- bool capacity_scaling;
- bool chg_unknown_bat;
- bool enable_overshoot;
- enum abx500_adc_therm adc_therm;
- int fg_res;
- int n_btypes;
- int batt_id;
- int interval_charging;
- int interval_not_charging;
- int temp_hysteresis;
- int gnd_lift_resistance;
- const struct ab8500_maxim_parameters *maxi;
- const struct ab8500_bm_capacity_levels *cap_levels;
- const struct ab8500_bm_charger_parameters *chg_params;
- const struct ab8500_fg_parameters *fg_params;
-};
-
-extern struct abx500_bm_data ab8500_bm_data;
+extern struct ab8500_bm_data ab8500_bm_data;
void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA);
struct ab8500_fg *ab8500_fg_get(void);
@@ -728,6 +572,10 @@ int ab8500_fg_inst_curr_started(struct ab8500_fg *di);
int ab8500_fg_inst_curr_done(struct ab8500_fg *di);
int ab8500_bm_of_probe(struct device *dev,
struct device_node *np,
- struct abx500_bm_data *bm);
+ struct ab8500_bm_data *bm);
+
+extern struct platform_driver ab8500_fg_driver;
+extern struct platform_driver ab8500_btemp_driver;
+extern struct platform_driver ab8500_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_bmdata.c b/drivers/power/supply/ab8500_bmdata.c
index c2b8c0bb77e2..6f5fb794042c 100644
--- a/drivers/power/supply/ab8500_bmdata.c
+++ b/drivers/power/supply/ab8500_bmdata.c
@@ -2,8 +2,6 @@
#include <linux/export.h>
#include <linux/power_supply.h>
#include <linux/of.h>
-#include <linux/mfd/abx500.h>
-#include <linux/mfd/abx500/ab8500.h>
#include "ab8500-bm.h"
@@ -13,7 +11,7 @@
* Note that the res_to_temp table must be strictly sorted by falling resistance
* values to work.
*/
-const struct abx500_res_to_temp ab8500_temp_tbl_a_thermistor[] = {
+const struct ab8500_res_to_temp ab8500_temp_tbl_a_thermistor[] = {
{-5, 53407},
{ 0, 48594},
{ 5, 43804},
@@ -35,7 +33,7 @@ EXPORT_SYMBOL(ab8500_temp_tbl_a_thermistor);
const int ab8500_temp_tbl_a_size = ARRAY_SIZE(ab8500_temp_tbl_a_thermistor);
EXPORT_SYMBOL(ab8500_temp_tbl_a_size);
-const struct abx500_res_to_temp ab8500_temp_tbl_b_thermistor[] = {
+const struct ab8500_res_to_temp ab8500_temp_tbl_b_thermistor[] = {
{-5, 200000},
{ 0, 159024},
{ 5, 151921},
@@ -57,7 +55,7 @@ EXPORT_SYMBOL(ab8500_temp_tbl_b_thermistor);
const int ab8500_temp_tbl_b_size = ARRAY_SIZE(ab8500_temp_tbl_b_thermistor);
EXPORT_SYMBOL(ab8500_temp_tbl_b_size);
-static const struct abx500_v_to_cap cap_tbl_a_thermistor[] = {
+static const struct ab8500_v_to_cap cap_tbl_a_thermistor[] = {
{4171, 100},
{4114, 95},
{4009, 83},
@@ -80,7 +78,7 @@ static const struct abx500_v_to_cap cap_tbl_a_thermistor[] = {
{3247, 0},
};
-static const struct abx500_v_to_cap cap_tbl_b_thermistor[] = {
+static const struct ab8500_v_to_cap cap_tbl_b_thermistor[] = {
{4161, 100},
{4124, 98},
{4044, 90},
@@ -103,7 +101,7 @@ static const struct abx500_v_to_cap cap_tbl_b_thermistor[] = {
{3250, 0},
};
-static const struct abx500_v_to_cap cap_tbl[] = {
+static const struct ab8500_v_to_cap cap_tbl[] = {
{4186, 100},
{4163, 99},
{4114, 95},
@@ -134,7 +132,7 @@ static const struct abx500_v_to_cap cap_tbl[] = {
* Note that the res_to_temp table must be strictly sorted by falling
* resistance values to work.
*/
-static const struct abx500_res_to_temp temp_tbl[] = {
+static const struct ab8500_res_to_temp temp_tbl[] = {
{-5, 214834},
{ 0, 162943},
{ 5, 124820},
@@ -191,7 +189,7 @@ static const struct batres_vs_temp temp_to_batres_tbl_9100[] = {
{-20, 180},
};
-static struct abx500_battery_type bat_type_thermistor[] = {
+static struct ab8500_battery_type bat_type_thermistor[] = {
[BATTERY_UNKNOWN] = {
/* First element always represent the UNKNOWN battery */
.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
@@ -277,7 +275,7 @@ static struct abx500_battery_type bat_type_thermistor[] = {
},
};
-static struct abx500_battery_type bat_type_ext_thermistor[] = {
+static struct ab8500_battery_type bat_type_ext_thermistor[] = {
[BATTERY_UNKNOWN] = {
/* First element always represent the UNKNOWN battery */
.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
@@ -394,7 +392,7 @@ static struct abx500_battery_type bat_type_ext_thermistor[] = {
},
};
-static const struct abx500_bm_capacity_levels cap_levels = {
+static const struct ab8500_bm_capacity_levels cap_levels = {
.critical = 2,
.low = 10,
.normal = 70,
@@ -402,7 +400,7 @@ static const struct abx500_bm_capacity_levels cap_levels = {
.full = 100,
};
-static const struct abx500_fg_parameters fg = {
+static const struct ab8500_fg_parameters fg = {
.recovery_sleep_timer = 10,
.recovery_total_time = 100,
.init_timer = 1,
@@ -424,14 +422,14 @@ static const struct abx500_fg_parameters fg = {
.pcut_debounce_time = 2,
};
-static const struct abx500_maxim_parameters ab8500_maxi_params = {
+static const struct ab8500_maxim_parameters ab8500_maxi_params = {
.ena_maxi = true,
.chg_curr = 910,
.wait_cycles = 10,
.charger_curr_step = 100,
};
-static const struct abx500_bm_charger_parameters chg = {
+static const struct ab8500_bm_charger_parameters chg = {
.usb_volt_max = 5500,
.usb_curr_max = 1500,
.ac_volt_max = 7500,
@@ -456,7 +454,7 @@ static int ab8500_charge_input_curr_map[] = {
700, 800, 900, 1000, 1100, 1300, 1400, 1500,
};
-struct abx500_bm_data ab8500_bm_data = {
+struct ab8500_bm_data ab8500_bm_data = {
.temp_under = 3,
.temp_low = 8,
.temp_high = 43,
@@ -469,7 +467,7 @@ struct abx500_bm_data ab8500_bm_data = {
.bkup_bat_i = BUP_ICH_SEL_150UA,
.no_maintenance = false,
.capacity_scaling = false,
- .adc_therm = ABx500_ADC_THERM_BATCTRL,
+ .adc_therm = AB8500_ADC_THERM_BATCTRL,
.chg_unknown_bat = false,
.enable_overshoot = false,
.fg_res = 100,
@@ -492,7 +490,7 @@ struct abx500_bm_data ab8500_bm_data = {
int ab8500_bm_of_probe(struct device *dev,
struct device_node *np,
- struct abx500_bm_data *bm)
+ struct ab8500_bm_data *bm)
{
const struct batres_vs_temp *tmp_batres_tbl;
struct device_node *battery_node;
@@ -531,7 +529,7 @@ int ab8500_bm_of_probe(struct device *dev,
} else {
bm->n_btypes = 4;
bm->bat_type = bat_type_ext_thermistor;
- bm->adc_therm = ABx500_ADC_THERM_BATTEMP;
+ bm->adc_therm = AB8500_ADC_THERM_BATTEMP;
tmp_batres_tbl = temp_to_batres_tbl_ext_thermistor;
}
diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c
index fdfcd59fc43e..b6c9111d77d7 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>
@@ -26,6 +27,7 @@
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/iio/consumer.h>
+#include <linux/fixp-arith.h>
#include "ab8500-bm.h"
@@ -101,7 +103,7 @@ struct ab8500_btemp {
struct iio_channel *btemp_ball;
struct iio_channel *bat_ctrl;
struct ab8500_fg *fg;
- struct abx500_bm_data *bm;
+ struct ab8500_bm_data *bm;
struct power_supply *btemp_psy;
struct ab8500_btemp_events events;
struct ab8500_btemp_ranges btemp_ranges;
@@ -143,7 +145,7 @@ static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di,
return (450000 * (v_batctrl)) / (1800 - v_batctrl);
}
- if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL) {
+ if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL) {
/*
* If the battery has internal NTC, we use the current
* source to calculate the resistance.
@@ -205,7 +207,7 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
return 0;
/* Only do this for batteries with internal NTC */
- if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) {
+ if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL && enable) {
if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA)
curr = BAT_CTRL_7U_ENA;
@@ -238,7 +240,7 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
__func__);
goto disable_curr_source;
}
- } else if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) {
+ } else if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL && !enable) {
dev_dbg(di->dev, "Disable BATCTRL curr source\n");
/* Write 0 to the curr bits */
@@ -416,7 +418,7 @@ static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
* based on the NTC resistance.
*/
static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
- const struct abx500_res_to_temp *tbl, int tbl_size, int res)
+ const struct ab8500_res_to_temp *tbl, int tbl_size, int res)
{
int i;
/*
@@ -436,8 +438,9 @@ static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
i++;
}
- return tbl[i].temp + ((tbl[i + 1].temp - tbl[i].temp) *
- (res - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist);
+ return fixp_linear_interpolate(tbl[i].resist, tbl[i].temp,
+ tbl[i + 1].resist, tbl[i + 1].temp,
+ res);
}
/**
@@ -455,7 +458,7 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
id = di->bm->batt_id;
- if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL &&
+ if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL &&
id != BATTERY_UNKNOWN) {
rbat = ab8500_btemp_get_batctrl_res(di);
@@ -524,7 +527,7 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
dev_dbg(di->dev, "Battery detected on %s"
" low %d < res %d < high: %d"
" index: %d\n",
- di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL ?
+ di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL ?
"BATCTRL" : "BATTEMP",
di->bm->bat_type[i].resis_low, res,
di->bm->bat_type[i].resis_high, i);
@@ -544,7 +547,7 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
* We only have to change current source if the
* detected type is Type 1.
*/
- if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL &&
+ if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL &&
di->bm->batt_id == 1) {
dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n");
di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA;
@@ -932,26 +935,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 +949,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 +997,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 +1021,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 +1033,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 +1052,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 +1081,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 +1099,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 +1110,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/abx500_chargalg.c b/drivers/power/supply/ab8500_chargalg.c
index f5b792243727..ff4b26b1ceca 100644
--- a/drivers/power/supply/abx500_chargalg.c
+++ b/drivers/power/supply/ab8500_chargalg.c
@@ -3,7 +3,7 @@
* Copyright (C) ST-Ericsson SA 2012
* Copyright (c) 2012 Sony Mobile Communications AB
*
- * Charging algorithm driver for abx500 variants
+ * Charging algorithm driver for AB8500
*
* Authors:
* Johan Palsson <johan.palsson@stericsson.com>
@@ -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>
@@ -48,18 +49,18 @@
#define CHARGALG_CURR_STEP_LOW 0
#define CHARGALG_CURR_STEP_HIGH 100
-enum abx500_chargers {
+enum ab8500_chargers {
NO_CHG,
AC_CHG,
USB_CHG,
};
-struct abx500_chargalg_charger_info {
- enum abx500_chargers conn_chg;
- enum abx500_chargers prev_conn_chg;
- enum abx500_chargers online_chg;
- enum abx500_chargers prev_online_chg;
- enum abx500_chargers charger_type;
+struct ab8500_chargalg_charger_info {
+ enum ab8500_chargers conn_chg;
+ enum ab8500_chargers prev_conn_chg;
+ enum ab8500_chargers online_chg;
+ enum ab8500_chargers prev_online_chg;
+ enum ab8500_chargers charger_type;
bool usb_chg_ok;
bool ac_chg_ok;
int usb_volt;
@@ -72,18 +73,18 @@ struct abx500_chargalg_charger_info {
int ac_iset;
};
-struct abx500_chargalg_suspension_status {
+struct ab8500_chargalg_suspension_status {
bool suspended_change;
bool ac_suspended;
bool usb_suspended;
};
-struct abx500_chargalg_current_step_status {
+struct ab8500_chargalg_current_step_status {
bool curr_step_change;
int curr_step;
};
-struct abx500_chargalg_battery_data {
+struct ab8500_chargalg_battery_data {
int temp;
int volt;
int avg_curr;
@@ -91,7 +92,7 @@ struct abx500_chargalg_battery_data {
int percent;
};
-enum abx500_chargalg_states {
+enum ab8500_chargalg_states {
STATE_HANDHELD_INIT,
STATE_HANDHELD,
STATE_CHG_NOT_OK_INIT,
@@ -122,7 +123,7 @@ enum abx500_chargalg_states {
STATE_WD_EXPIRED,
};
-static const char *states[] = {
+static const char * const states[] = {
"HANDHELD_INIT",
"HANDHELD",
"CHG_NOT_OK_INIT",
@@ -153,7 +154,7 @@ static const char *states[] = {
"WD_EXPIRED",
};
-struct abx500_chargalg_events {
+struct ab8500_chargalg_events {
bool batt_unknown;
bool mainextchnotok;
bool batt_ovv;
@@ -175,7 +176,7 @@ struct abx500_chargalg_events {
};
/**
- * struct abx500_charge_curr_maximization - Charger maximization parameters
+ * struct ab8500_charge_curr_maximization - Charger maximization parameters
* @original_iset: the non optimized/maximised charger current
* @current_iset: the charging current used at this moment
* @test_delta_i: the delta between the current we want to charge and the
@@ -189,7 +190,7 @@ struct abx500_chargalg_events {
* @level: tells in how many steps the charging current has been
increased
*/
-struct abx500_charge_curr_maximization {
+struct ab8500_charge_curr_maximization {
int original_iset;
int current_iset;
int test_delta_i;
@@ -206,7 +207,7 @@ enum maxim_ret {
};
/**
- * struct abx500_chargalg - abx500 Charging algorithm device information
+ * struct ab8500_chargalg - ab8500 Charging algorithm device information
* @dev: pointer to the structure device
* @charge_status: battery operating status
* @eoc_cnt: counter used to determine end-of_charge
@@ -222,7 +223,7 @@ enum maxim_ret {
* @susp_status: current charger suspension status
* @bm: Platform specific battery management information
* @curr_status: Current step status for over-current protection
- * @parent: pointer to the struct abx500
+ * @parent: pointer to the struct ab8500
* @chargalg_psy: structure that holds the battery properties exposed by
* the charging algorithm
* @events: structure for information about events triggered
@@ -234,25 +235,25 @@ enum maxim_ret {
* @maintenance_timer: maintenance charging timer
* @chargalg_kobject: structure of type kobject
*/
-struct abx500_chargalg {
+struct ab8500_chargalg {
struct device *dev;
int charge_status;
int eoc_cnt;
bool maintenance_chg;
int t_hyst_norm;
int t_hyst_lowhigh;
- enum abx500_chargalg_states charge_state;
- struct abx500_charge_curr_maximization ccm;
- struct abx500_chargalg_charger_info chg_info;
- struct abx500_chargalg_battery_data batt_data;
- struct abx500_chargalg_suspension_status susp_status;
+ enum ab8500_chargalg_states charge_state;
+ struct ab8500_charge_curr_maximization ccm;
+ struct ab8500_chargalg_charger_info chg_info;
+ struct ab8500_chargalg_battery_data batt_data;
+ struct ab8500_chargalg_suspension_status susp_status;
struct ab8500 *parent;
- struct abx500_chargalg_current_step_status curr_status;
- struct abx500_bm_data *bm;
+ struct ab8500_chargalg_current_step_status curr_status;
+ struct ab8500_bm_data *bm;
struct power_supply *chargalg_psy;
struct ux500_charger *ac_chg;
struct ux500_charger *usb_chg;
- struct abx500_chargalg_events events;
+ struct ab8500_chargalg_events events;
struct workqueue_struct *chargalg_wq;
struct delayed_work chargalg_periodic_work;
struct delayed_work chargalg_wd_work;
@@ -266,28 +267,28 @@ struct abx500_chargalg {
BLOCKING_NOTIFIER_HEAD(charger_notifier_list);
/* Main battery properties */
-static enum power_supply_property abx500_chargalg_props[] = {
+static enum power_supply_property ab8500_chargalg_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_HEALTH,
};
-struct abx500_chargalg_sysfs_entry {
+struct ab8500_chargalg_sysfs_entry {
struct attribute attr;
- ssize_t (*show)(struct abx500_chargalg *, char *);
- ssize_t (*store)(struct abx500_chargalg *, const char *, size_t);
+ ssize_t (*show)(struct ab8500_chargalg *di, char *buf);
+ ssize_t (*store)(struct ab8500_chargalg *di, const char *buf, size_t length);
};
/**
- * abx500_chargalg_safety_timer_expired() - Expiration of the safety timer
+ * ab8500_chargalg_safety_timer_expired() - Expiration of the safety timer
* @timer: pointer to the hrtimer structure
*
* This function gets called when the safety timer for the charger
* expires
*/
static enum hrtimer_restart
-abx500_chargalg_safety_timer_expired(struct hrtimer *timer)
+ab8500_chargalg_safety_timer_expired(struct hrtimer *timer)
{
- struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg,
+ struct ab8500_chargalg *di = container_of(timer, struct ab8500_chargalg,
safety_timer);
dev_err(di->dev, "Safety timer expired\n");
di->events.safety_timer_expired = true;
@@ -299,7 +300,7 @@ abx500_chargalg_safety_timer_expired(struct hrtimer *timer)
}
/**
- * abx500_chargalg_maintenance_timer_expired() - Expiration of
+ * ab8500_chargalg_maintenance_timer_expired() - Expiration of
* the maintenance timer
* @timer: pointer to the timer structure
*
@@ -307,10 +308,10 @@ abx500_chargalg_safety_timer_expired(struct hrtimer *timer)
* expires
*/
static enum hrtimer_restart
-abx500_chargalg_maintenance_timer_expired(struct hrtimer *timer)
+ab8500_chargalg_maintenance_timer_expired(struct hrtimer *timer)
{
- struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg,
+ struct ab8500_chargalg *di = container_of(timer, struct ab8500_chargalg,
maintenance_timer);
dev_dbg(di->dev, "Maintenance timer expired\n");
@@ -323,13 +324,13 @@ abx500_chargalg_maintenance_timer_expired(struct hrtimer *timer)
}
/**
- * abx500_chargalg_state_to() - Change charge state
- * @di: pointer to the abx500_chargalg structure
+ * ab8500_chargalg_state_to() - Change charge state
+ * @di: pointer to the ab8500_chargalg structure
*
* This function gets called when a charge state change should occur
*/
-static void abx500_chargalg_state_to(struct abx500_chargalg *di,
- enum abx500_chargalg_states state)
+static void ab8500_chargalg_state_to(struct ab8500_chargalg *di,
+ enum ab8500_chargalg_states state)
{
dev_dbg(di->dev,
"State changed: %s (From state: [%d] %s =to=> [%d] %s )\n",
@@ -342,7 +343,7 @@ static void abx500_chargalg_state_to(struct abx500_chargalg *di,
di->charge_state = state;
}
-static int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di)
+static int ab8500_chargalg_check_charger_enable(struct ab8500_chargalg *di)
{
switch (di->charge_state) {
case STATE_NORMAL:
@@ -367,13 +368,13 @@ static int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di)
}
/**
- * abx500_chargalg_check_charger_connection() - Check charger connection change
- * @di: pointer to the abx500_chargalg structure
+ * ab8500_chargalg_check_charger_connection() - Check charger connection change
+ * @di: pointer to the ab8500_chargalg structure
*
* This function will check if there is a change in the charger connection
* and change charge state accordingly. AC has precedence over USB.
*/
-static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
+static int ab8500_chargalg_check_charger_connection(struct ab8500_chargalg *di)
{
if (di->chg_info.conn_chg != di->chg_info.prev_conn_chg ||
di->susp_status.suspended_change) {
@@ -386,23 +387,23 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
dev_dbg(di->dev, "Charging source is AC\n");
if (di->chg_info.charger_type != AC_CHG) {
di->chg_info.charger_type = AC_CHG;
- abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
}
} else if ((di->chg_info.conn_chg & USB_CHG) &&
!di->susp_status.usb_suspended) {
dev_dbg(di->dev, "Charging source is USB\n");
di->chg_info.charger_type = USB_CHG;
- abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
} else if (di->chg_info.conn_chg &&
(di->susp_status.ac_suspended ||
di->susp_status.usb_suspended)) {
dev_dbg(di->dev, "Charging is suspended\n");
di->chg_info.charger_type = NO_CHG;
- abx500_chargalg_state_to(di, STATE_SUSPENDED_INIT);
+ ab8500_chargalg_state_to(di, STATE_SUSPENDED_INIT);
} else {
dev_dbg(di->dev, "Charging source is OFF\n");
di->chg_info.charger_type = NO_CHG;
- abx500_chargalg_state_to(di, STATE_HANDHELD_INIT);
+ ab8500_chargalg_state_to(di, STATE_HANDHELD_INIT);
}
di->chg_info.prev_conn_chg = di->chg_info.conn_chg;
di->susp_status.suspended_change = false;
@@ -411,29 +412,29 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
}
/**
- * abx500_chargalg_check_current_step_status() - Check charging current
+ * ab8500_chargalg_check_current_step_status() - Check charging current
* step status.
- * @di: pointer to the abx500_chargalg structure
+ * @di: pointer to the ab8500_chargalg structure
*
* This function will check if there is a change in the charging current step
* and change charge state accordingly.
*/
-static void abx500_chargalg_check_current_step_status
- (struct abx500_chargalg *di)
+static void ab8500_chargalg_check_current_step_status
+ (struct ab8500_chargalg *di)
{
if (di->curr_status.curr_step_change)
- abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
di->curr_status.curr_step_change = false;
}
/**
- * abx500_chargalg_start_safety_timer() - Start charging safety timer
- * @di: pointer to the abx500_chargalg structure
+ * ab8500_chargalg_start_safety_timer() - Start charging safety timer
+ * @di: pointer to the ab8500_chargalg structure
*
* The safety timer is used to avoid overcharging of old or bad batteries.
* There are different timers for AC and USB
*/
-static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
+static void ab8500_chargalg_start_safety_timer(struct ab8500_chargalg *di)
{
/* Charger-dependent expiration time in hours*/
int timer_expiration = 0;
@@ -460,27 +461,27 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
}
/**
- * abx500_chargalg_stop_safety_timer() - Stop charging safety timer
- * @di: pointer to the abx500_chargalg structure
+ * ab8500_chargalg_stop_safety_timer() - Stop charging safety timer
+ * @di: pointer to the ab8500_chargalg structure
*
* The safety timer is stopped whenever the NORMAL state is exited
*/
-static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di)
+static void ab8500_chargalg_stop_safety_timer(struct ab8500_chargalg *di)
{
if (hrtimer_try_to_cancel(&di->safety_timer) >= 0)
di->events.safety_timer_expired = false;
}
/**
- * abx500_chargalg_start_maintenance_timer() - Start charging maintenance timer
- * @di: pointer to the abx500_chargalg structure
+ * ab8500_chargalg_start_maintenance_timer() - Start charging maintenance timer
+ * @di: pointer to the ab8500_chargalg structure
* @duration: duration of ther maintenance timer in hours
*
* The maintenance timer is used to maintain the charge in the battery once
* the battery is considered full. These timers are chosen to match the
* discharge curve of the battery
*/
-static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
+static void ab8500_chargalg_start_maintenance_timer(struct ab8500_chargalg *di,
int duration)
{
hrtimer_set_expires_range(&di->maintenance_timer,
@@ -491,26 +492,26 @@ static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
}
/**
- * abx500_chargalg_stop_maintenance_timer() - Stop maintenance timer
- * @di: pointer to the abx500_chargalg structure
+ * ab8500_chargalg_stop_maintenance_timer() - Stop maintenance timer
+ * @di: pointer to the ab8500_chargalg structure
*
* The maintenance timer is stopped whenever maintenance ends or when another
* state is entered
*/
-static void abx500_chargalg_stop_maintenance_timer(struct abx500_chargalg *di)
+static void ab8500_chargalg_stop_maintenance_timer(struct ab8500_chargalg *di)
{
if (hrtimer_try_to_cancel(&di->maintenance_timer) >= 0)
di->events.maintenance_timer_expired = false;
}
/**
- * abx500_chargalg_kick_watchdog() - Kick charger watchdog
- * @di: pointer to the abx500_chargalg structure
+ * ab8500_chargalg_kick_watchdog() - Kick charger watchdog
+ * @di: pointer to the ab8500_chargalg structure
*
* The charger watchdog have to be kicked periodically whenever the charger is
* on, else the ABB will reset the system
*/
-static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
+static int ab8500_chargalg_kick_watchdog(struct ab8500_chargalg *di)
{
/* Check if charger exists and kick watchdog if charging */
if (di->ac_chg && di->ac_chg->ops.kick_wd &&
@@ -525,8 +526,7 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
di->usb_chg->ops.kick_wd(di->usb_chg);
return di->ac_chg->ops.kick_wd(di->ac_chg);
- }
- else if (di->usb_chg && di->usb_chg->ops.kick_wd &&
+ } else if (di->usb_chg && di->usb_chg->ops.kick_wd &&
di->chg_info.online_chg & USB_CHG)
return di->usb_chg->ops.kick_wd(di->usb_chg);
@@ -534,8 +534,8 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
}
/**
- * abx500_chargalg_ac_en() - Turn on/off the AC charger
- * @di: pointer to the abx500_chargalg structure
+ * ab8500_chargalg_ac_en() - Turn on/off the AC charger
+ * @di: pointer to the ab8500_chargalg structure
* @enable: charger on/off
* @vset: requested charger output voltage
* @iset: requested charger output current
@@ -543,10 +543,10 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
* The AC charger will be turned on/off with the requested charge voltage and
* current
*/
-static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
+static int ab8500_chargalg_ac_en(struct ab8500_chargalg *di, int enable,
int vset, int iset)
{
- static int abx500_chargalg_ex_ac_enable_toggle;
+ static int ab8500_chargalg_ex_ac_enable_toggle;
if (!di->ac_chg || !di->ac_chg->ops.enable)
return -ENXIO;
@@ -562,18 +562,18 @@ static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
/* Enable external charger */
if (enable && di->ac_chg->external &&
- !abx500_chargalg_ex_ac_enable_toggle) {
+ !ab8500_chargalg_ex_ac_enable_toggle) {
blocking_notifier_call_chain(&charger_notifier_list,
0, di->dev);
- abx500_chargalg_ex_ac_enable_toggle++;
+ ab8500_chargalg_ex_ac_enable_toggle++;
}
return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
}
/**
- * abx500_chargalg_usb_en() - Turn on/off the USB charger
- * @di: pointer to the abx500_chargalg structure
+ * ab8500_chargalg_usb_en() - Turn on/off the USB charger
+ * @di: pointer to the ab8500_chargalg structure
* @enable: charger on/off
* @vset: requested charger output voltage
* @iset: requested charger output current
@@ -581,7 +581,7 @@ static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
* The USB charger will be turned on/off with the requested charge voltage and
* current
*/
-static int abx500_chargalg_usb_en(struct abx500_chargalg *di, int enable,
+static int ab8500_chargalg_usb_en(struct ab8500_chargalg *di, int enable,
int vset, int iset)
{
if (!di->usb_chg || !di->usb_chg->ops.enable)
@@ -600,14 +600,14 @@ static int abx500_chargalg_usb_en(struct abx500_chargalg *di, int enable,
}
/**
- * abx500_chargalg_update_chg_curr() - Update charger current
- * @di: pointer to the abx500_chargalg structure
+ * ab8500_chargalg_update_chg_curr() - Update charger current
+ * @di: pointer to the ab8500_chargalg structure
* @iset: requested charger output current
*
* The charger output current will be updated for the charger
* that is currently in use
*/
-static int abx500_chargalg_update_chg_curr(struct abx500_chargalg *di,
+static int ab8500_chargalg_update_chg_curr(struct ab8500_chargalg *di,
int iset)
{
/* Check if charger exists and update current if charging */
@@ -641,19 +641,19 @@ static int abx500_chargalg_update_chg_curr(struct abx500_chargalg *di,
}
/**
- * abx500_chargalg_stop_charging() - Stop charging
- * @di: pointer to the abx500_chargalg structure
+ * ab8500_chargalg_stop_charging() - Stop charging
+ * @di: pointer to the ab8500_chargalg structure
*
* This function is called from any state where charging should be stopped.
* All charging is disabled and all status parameters and timers are changed
* accordingly
*/
-static void abx500_chargalg_stop_charging(struct abx500_chargalg *di)
+static void ab8500_chargalg_stop_charging(struct ab8500_chargalg *di)
{
- abx500_chargalg_ac_en(di, false, 0, 0);
- abx500_chargalg_usb_en(di, false, 0, 0);
- abx500_chargalg_stop_safety_timer(di);
- abx500_chargalg_stop_maintenance_timer(di);
+ ab8500_chargalg_ac_en(di, false, 0, 0);
+ ab8500_chargalg_usb_en(di, false, 0, 0);
+ ab8500_chargalg_stop_safety_timer(di);
+ ab8500_chargalg_stop_maintenance_timer(di);
di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
di->maintenance_chg = false;
cancel_delayed_work(&di->chargalg_wd_work);
@@ -661,19 +661,19 @@ static void abx500_chargalg_stop_charging(struct abx500_chargalg *di)
}
/**
- * abx500_chargalg_hold_charging() - Pauses charging
- * @di: pointer to the abx500_chargalg structure
+ * ab8500_chargalg_hold_charging() - Pauses charging
+ * @di: pointer to the ab8500_chargalg structure
*
* This function is called in the case where maintenance charging has been
* disabled and instead a battery voltage mode is entered to check when the
* battery voltage has reached a certain recharge voltage
*/
-static void abx500_chargalg_hold_charging(struct abx500_chargalg *di)
+static void ab8500_chargalg_hold_charging(struct ab8500_chargalg *di)
{
- abx500_chargalg_ac_en(di, false, 0, 0);
- abx500_chargalg_usb_en(di, false, 0, 0);
- abx500_chargalg_stop_safety_timer(di);
- abx500_chargalg_stop_maintenance_timer(di);
+ ab8500_chargalg_ac_en(di, false, 0, 0);
+ ab8500_chargalg_usb_en(di, false, 0, 0);
+ ab8500_chargalg_stop_safety_timer(di);
+ ab8500_chargalg_stop_maintenance_timer(di);
di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
di->maintenance_chg = false;
cancel_delayed_work(&di->chargalg_wd_work);
@@ -681,30 +681,30 @@ static void abx500_chargalg_hold_charging(struct abx500_chargalg *di)
}
/**
- * abx500_chargalg_start_charging() - Start the charger
- * @di: pointer to the abx500_chargalg structure
+ * ab8500_chargalg_start_charging() - Start the charger
+ * @di: pointer to the ab8500_chargalg structure
* @vset: requested charger output voltage
* @iset: requested charger output current
*
* A charger will be enabled depending on the requested charger type that was
* detected previously.
*/
-static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
+static void ab8500_chargalg_start_charging(struct ab8500_chargalg *di,
int vset, int iset)
{
switch (di->chg_info.charger_type) {
case AC_CHG:
dev_dbg(di->dev,
"AC parameters: Vset %d, Ich %d\n", vset, iset);
- abx500_chargalg_usb_en(di, false, 0, 0);
- abx500_chargalg_ac_en(di, true, vset, iset);
+ ab8500_chargalg_usb_en(di, false, 0, 0);
+ ab8500_chargalg_ac_en(di, true, vset, iset);
break;
case USB_CHG:
dev_dbg(di->dev,
"USB parameters: Vset %d, Ich %d\n", vset, iset);
- abx500_chargalg_ac_en(di, false, 0, 0);
- abx500_chargalg_usb_en(di, true, vset, iset);
+ ab8500_chargalg_ac_en(di, false, 0, 0);
+ ab8500_chargalg_usb_en(di, true, vset, iset);
break;
default:
@@ -714,13 +714,13 @@ static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
}
/**
- * abx500_chargalg_check_temp() - Check battery temperature ranges
- * @di: pointer to the abx500_chargalg structure
+ * ab8500_chargalg_check_temp() - Check battery temperature ranges
+ * @di: pointer to the ab8500_chargalg structure
*
* The battery temperature is checked against the predefined limits and the
* charge state is changed accordingly
*/
-static void abx500_chargalg_check_temp(struct abx500_chargalg *di)
+static void ab8500_chargalg_check_temp(struct ab8500_chargalg *di)
{
if (di->batt_data.temp > (di->bm->temp_low + di->t_hyst_norm) &&
di->batt_data.temp < (di->bm->temp_high - di->t_hyst_norm)) {
@@ -749,8 +749,8 @@ static void abx500_chargalg_check_temp(struct abx500_chargalg *di)
di->t_hyst_norm = 0;
di->t_hyst_lowhigh = di->bm->temp_hysteresis;
} else {
- /* Within hysteresis */
- dev_dbg(di->dev, "Within hysteresis limit temp: %d "
+ /* Within hysteresis */
+ dev_dbg(di->dev, "Within hysteresis limit temp: %d "
"hyst_lowhigh %d, hyst normal %d\n",
di->batt_data.temp, di->t_hyst_lowhigh,
di->t_hyst_norm);
@@ -759,12 +759,12 @@ static void abx500_chargalg_check_temp(struct abx500_chargalg *di)
}
/**
- * abx500_chargalg_check_charger_voltage() - Check charger voltage
- * @di: pointer to the abx500_chargalg structure
+ * ab8500_chargalg_check_charger_voltage() - Check charger voltage
+ * @di: pointer to the ab8500_chargalg structure
*
* Charger voltage is checked against maximum limit
*/
-static void abx500_chargalg_check_charger_voltage(struct abx500_chargalg *di)
+static void ab8500_chargalg_check_charger_voltage(struct ab8500_chargalg *di)
{
if (di->chg_info.usb_volt > di->bm->chg_params->usb_volt_max)
di->chg_info.usb_chg_ok = false;
@@ -779,14 +779,14 @@ static void abx500_chargalg_check_charger_voltage(struct abx500_chargalg *di)
}
/**
- * abx500_chargalg_end_of_charge() - Check if end-of-charge criteria is fulfilled
- * @di: pointer to the abx500_chargalg structure
+ * ab8500_chargalg_end_of_charge() - Check if end-of-charge criteria is fulfilled
+ * @di: pointer to the ab8500_chargalg structure
*
* End-of-charge criteria is fulfilled when the battery voltage is above a
* certain limit and the battery current is below a certain limit for a
* predefined number of consecutive seconds. If true, the battery is full
*/
-static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di)
+static void ab8500_chargalg_end_of_charge(struct ab8500_chargalg *di)
{
if (di->charge_status == POWER_SUPPLY_STATUS_CHARGING &&
di->charge_state == STATE_NORMAL &&
@@ -814,7 +814,7 @@ static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di)
}
}
-static void init_maxim_chg_curr(struct abx500_chargalg *di)
+static void init_maxim_chg_curr(struct ab8500_chargalg *di)
{
di->ccm.original_iset =
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl;
@@ -827,15 +827,15 @@ static void init_maxim_chg_curr(struct abx500_chargalg *di)
}
/**
- * abx500_chargalg_chg_curr_maxim - increases the charger current to
+ * ab8500_chargalg_chg_curr_maxim - increases the charger current to
* compensate for the system load
- * @di pointer to the abx500_chargalg structure
+ * @di pointer to the ab8500_chargalg structure
*
* This maximization function is used to raise the charger current to get the
* battery current as close to the optimal value as possible. The battery
* current during charging is affected by the system load
*/
-static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
+static enum maxim_ret ab8500_chargalg_chg_curr_maxim(struct ab8500_chargalg *di)
{
int delta_i;
@@ -866,7 +866,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
di->ccm.wait_cnt = 0;
- if ((di->batt_data.inst_curr > di->ccm.original_iset)) {
+ if (di->batt_data.inst_curr > di->ccm.original_iset) {
dev_dbg(di->dev, " Maximization Ibat (%dmA) too high"
" (limit %dmA) (current iset: %dmA)!\n",
di->batt_data.inst_curr, di->ccm.original_iset,
@@ -907,21 +907,21 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
}
}
-static void handle_maxim_chg_curr(struct abx500_chargalg *di)
+static void handle_maxim_chg_curr(struct ab8500_chargalg *di)
{
enum maxim_ret ret;
int result;
- ret = abx500_chargalg_chg_curr_maxim(di);
+ ret = ab8500_chargalg_chg_curr_maxim(di);
switch (ret) {
case MAXIM_RET_CHANGE:
- result = abx500_chargalg_update_chg_curr(di,
+ result = ab8500_chargalg_update_chg_curr(di,
di->ccm.current_iset);
if (result)
dev_err(di->dev, "failed to set chg curr\n");
break;
case MAXIM_RET_IBAT_TOO_HIGH:
- result = abx500_chargalg_update_chg_curr(di,
+ result = ab8500_chargalg_update_chg_curr(di,
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
if (result)
dev_err(di->dev, "failed to set chg curr\n");
@@ -934,12 +934,12 @@ static void handle_maxim_chg_curr(struct abx500_chargalg *di)
}
}
-static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
+static int ab8500_chargalg_get_ext_psy_data(struct device *dev, void *data)
{
struct power_supply *psy;
struct power_supply *ext = dev_get_drvdata(dev);
const char **supplicants = (const char **)ext->supplied_to;
- struct abx500_chargalg *di;
+ struct ab8500_chargalg *di;
union power_supply_propval ret;
int j;
bool capacity_updated = false;
@@ -1149,6 +1149,7 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
default:
break;
}
+ break;
default:
break;
}
@@ -1258,7 +1259,7 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
}
/**
- * abx500_chargalg_external_power_changed() - callback for power supply changes
+ * ab8500_chargalg_external_power_changed() - callback for power supply changes
* @psy: pointer to the structure power_supply
*
* This function is the entry point of the pointer external_power_changed
@@ -1266,26 +1267,27 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
* This function gets executed when there is a change in any external power
* supply that this driver needs to be notified of.
*/
-static void abx500_chargalg_external_power_changed(struct power_supply *psy)
+static void ab8500_chargalg_external_power_changed(struct power_supply *psy)
{
- struct abx500_chargalg *di = power_supply_get_drvdata(psy);
+ struct ab8500_chargalg *di = power_supply_get_drvdata(psy);
/*
* Trigger execution of the algorithm instantly and read
* all power_supply properties there instead
*/
- queue_work(di->chargalg_wq, &di->chargalg_work);
+ if (di->chargalg_wq)
+ queue_work(di->chargalg_wq, &di->chargalg_work);
}
/**
- * abx500_chargalg_algorithm() - Main function for the algorithm
- * @di: pointer to the abx500_chargalg structure
+ * ab8500_chargalg_algorithm() - Main function for the algorithm
+ * @di: pointer to the ab8500_chargalg structure
*
* This is the main control function for the charging algorithm.
* It is called periodically or when something happens that will
* trigger a state change
*/
-static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
+static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
{
int charger_status;
int ret;
@@ -1293,17 +1295,17 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
/* Collect data from all power_supply class devices */
class_for_each_device(power_supply_class, NULL,
- di->chargalg_psy, abx500_chargalg_get_ext_psy_data);
+ di->chargalg_psy, ab8500_chargalg_get_ext_psy_data);
- abx500_chargalg_end_of_charge(di);
- abx500_chargalg_check_temp(di);
- abx500_chargalg_check_charger_voltage(di);
+ ab8500_chargalg_end_of_charge(di);
+ ab8500_chargalg_check_temp(di);
+ ab8500_chargalg_check_charger_voltage(di);
- charger_status = abx500_chargalg_check_charger_connection(di);
- abx500_chargalg_check_current_step_status(di);
+ charger_status = ab8500_chargalg_check_charger_connection(di);
+ ab8500_chargalg_check_current_step_status(di);
if (is_ab8500(di->parent)) {
- ret = abx500_chargalg_check_charger_enable(di);
+ ret = ab8500_chargalg_check_charger_enable(di);
if (ret < 0)
dev_err(di->dev, "Checking charger is enabled error"
": Returned Value %d\n", ret);
@@ -1318,7 +1320,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
(di->events.batt_unknown && !di->bm->chg_unknown_bat)) {
if (di->charge_state != STATE_HANDHELD) {
di->events.safety_timer_expired = false;
- abx500_chargalg_state_to(di, STATE_HANDHELD_INIT);
+ ab8500_chargalg_state_to(di, STATE_HANDHELD_INIT);
}
}
@@ -1331,7 +1333,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
/* Safety timer expiration */
else if (di->events.safety_timer_expired) {
if (di->charge_state != STATE_SAFETY_TIMER_EXPIRED)
- abx500_chargalg_state_to(di,
+ ab8500_chargalg_state_to(di,
STATE_SAFETY_TIMER_EXPIRED_INIT);
}
/*
@@ -1342,7 +1344,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
/* Battery removed */
else if (di->events.batt_rem) {
if (di->charge_state != STATE_BATT_REMOVED)
- abx500_chargalg_state_to(di, STATE_BATT_REMOVED_INIT);
+ ab8500_chargalg_state_to(di, STATE_BATT_REMOVED_INIT);
}
/* Main or USB charger not ok. */
else if (di->events.mainextchnotok || di->events.usbchargernotok) {
@@ -1352,7 +1354,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
*/
if (di->charge_state != STATE_CHG_NOT_OK &&
!di->events.vbus_collapsed)
- abx500_chargalg_state_to(di, STATE_CHG_NOT_OK_INIT);
+ ab8500_chargalg_state_to(di, STATE_CHG_NOT_OK_INIT);
}
/* VBUS, Main or VBAT OVV. */
else if (di->events.vbus_ovv ||
@@ -1361,31 +1363,31 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
!di->chg_info.usb_chg_ok ||
!di->chg_info.ac_chg_ok) {
if (di->charge_state != STATE_OVV_PROTECT)
- abx500_chargalg_state_to(di, STATE_OVV_PROTECT_INIT);
+ ab8500_chargalg_state_to(di, STATE_OVV_PROTECT_INIT);
}
/* USB Thermal, stop charging */
else if (di->events.main_thermal_prot ||
di->events.usb_thermal_prot) {
if (di->charge_state != STATE_HW_TEMP_PROTECT)
- abx500_chargalg_state_to(di,
+ ab8500_chargalg_state_to(di,
STATE_HW_TEMP_PROTECT_INIT);
}
/* Battery temp over/under */
else if (di->events.btemp_underover) {
if (di->charge_state != STATE_TEMP_UNDEROVER)
- abx500_chargalg_state_to(di,
+ ab8500_chargalg_state_to(di,
STATE_TEMP_UNDEROVER_INIT);
}
/* Watchdog expired */
else if (di->events.ac_wd_expired ||
di->events.usb_wd_expired) {
if (di->charge_state != STATE_WD_EXPIRED)
- abx500_chargalg_state_to(di, STATE_WD_EXPIRED_INIT);
+ ab8500_chargalg_state_to(di, STATE_WD_EXPIRED_INIT);
}
/* Battery temp high/low */
else if (di->events.btemp_lowhigh) {
if (di->charge_state != STATE_TEMP_LOWHIGH)
- abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH_INIT);
+ ab8500_chargalg_state_to(di, STATE_TEMP_LOWHIGH_INIT);
}
dev_dbg(di->dev,
@@ -1417,9 +1419,9 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
switch (di->charge_state) {
case STATE_HANDHELD_INIT:
- abx500_chargalg_stop_charging(di);
+ ab8500_chargalg_stop_charging(di);
di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
- abx500_chargalg_state_to(di, STATE_HANDHELD);
+ ab8500_chargalg_state_to(di, STATE_HANDHELD);
fallthrough;
case STATE_HANDHELD:
@@ -1427,14 +1429,14 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
case STATE_SUSPENDED_INIT:
if (di->susp_status.ac_suspended)
- abx500_chargalg_ac_en(di, false, 0, 0);
+ ab8500_chargalg_ac_en(di, false, 0, 0);
if (di->susp_status.usb_suspended)
- abx500_chargalg_usb_en(di, false, 0, 0);
- abx500_chargalg_stop_safety_timer(di);
- abx500_chargalg_stop_maintenance_timer(di);
+ ab8500_chargalg_usb_en(di, false, 0, 0);
+ ab8500_chargalg_stop_safety_timer(di);
+ ab8500_chargalg_stop_maintenance_timer(di);
di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
di->maintenance_chg = false;
- abx500_chargalg_state_to(di, STATE_SUSPENDED);
+ ab8500_chargalg_state_to(di, STATE_SUSPENDED);
power_supply_changed(di->chargalg_psy);
fallthrough;
@@ -1443,29 +1445,29 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
break;
case STATE_BATT_REMOVED_INIT:
- abx500_chargalg_stop_charging(di);
- abx500_chargalg_state_to(di, STATE_BATT_REMOVED);
+ ab8500_chargalg_stop_charging(di);
+ ab8500_chargalg_state_to(di, STATE_BATT_REMOVED);
fallthrough;
case STATE_BATT_REMOVED:
if (!di->events.batt_rem)
- abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
break;
case STATE_HW_TEMP_PROTECT_INIT:
- abx500_chargalg_stop_charging(di);
- abx500_chargalg_state_to(di, STATE_HW_TEMP_PROTECT);
+ ab8500_chargalg_stop_charging(di);
+ ab8500_chargalg_state_to(di, STATE_HW_TEMP_PROTECT);
fallthrough;
case STATE_HW_TEMP_PROTECT:
if (!di->events.main_thermal_prot &&
!di->events.usb_thermal_prot)
- abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
break;
case STATE_OVV_PROTECT_INIT:
- abx500_chargalg_stop_charging(di);
- abx500_chargalg_state_to(di, STATE_OVV_PROTECT);
+ ab8500_chargalg_stop_charging(di);
+ ab8500_chargalg_state_to(di, STATE_OVV_PROTECT);
fallthrough;
case STATE_OVV_PROTECT:
@@ -1474,23 +1476,23 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
!di->events.batt_ovv &&
di->chg_info.usb_chg_ok &&
di->chg_info.ac_chg_ok)
- abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
break;
case STATE_CHG_NOT_OK_INIT:
- abx500_chargalg_stop_charging(di);
- abx500_chargalg_state_to(di, STATE_CHG_NOT_OK);
+ ab8500_chargalg_stop_charging(di);
+ ab8500_chargalg_state_to(di, STATE_CHG_NOT_OK);
fallthrough;
case STATE_CHG_NOT_OK:
if (!di->events.mainextchnotok &&
!di->events.usbchargernotok)
- abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
break;
case STATE_SAFETY_TIMER_EXPIRED_INIT:
- abx500_chargalg_stop_charging(di);
- abx500_chargalg_state_to(di, STATE_SAFETY_TIMER_EXPIRED);
+ ab8500_chargalg_stop_charging(di);
+ ab8500_chargalg_state_to(di, STATE_SAFETY_TIMER_EXPIRED);
fallthrough;
case STATE_SAFETY_TIMER_EXPIRED:
@@ -1499,20 +1501,20 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
case STATE_NORMAL_INIT:
if (di->curr_status.curr_step == CHARGALG_CURR_STEP_LOW)
- abx500_chargalg_stop_charging(di);
+ ab8500_chargalg_stop_charging(di);
else {
curr_step_lvl = di->bm->bat_type[
di->bm->batt_id].normal_cur_lvl
* di->curr_status.curr_step
/ CHARGALG_CURR_STEP_HIGH;
- abx500_chargalg_start_charging(di,
+ ab8500_chargalg_start_charging(di,
di->bm->bat_type[di->bm->batt_id]
.normal_vol_lvl, curr_step_lvl);
}
- abx500_chargalg_state_to(di, STATE_NORMAL);
- abx500_chargalg_start_safety_timer(di);
- abx500_chargalg_stop_maintenance_timer(di);
+ ab8500_chargalg_state_to(di, STATE_NORMAL);
+ ab8500_chargalg_start_safety_timer(di);
+ ab8500_chargalg_stop_maintenance_timer(di);
init_maxim_chg_curr(di);
di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
di->eoc_cnt = 0;
@@ -1526,104 +1528,103 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
if (di->charge_status == POWER_SUPPLY_STATUS_FULL &&
di->maintenance_chg) {
if (di->bm->no_maintenance)
- abx500_chargalg_state_to(di,
+ ab8500_chargalg_state_to(di,
STATE_WAIT_FOR_RECHARGE_INIT);
else
- abx500_chargalg_state_to(di,
+ ab8500_chargalg_state_to(di,
STATE_MAINTENANCE_A_INIT);
}
break;
/* This state will be used when the maintenance state is disabled */
case STATE_WAIT_FOR_RECHARGE_INIT:
- abx500_chargalg_hold_charging(di);
- abx500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE);
+ ab8500_chargalg_hold_charging(di);
+ ab8500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE);
fallthrough;
case STATE_WAIT_FOR_RECHARGE:
if (di->batt_data.percent <=
- di->bm->bat_type[di->bm->batt_id].
- recharge_cap)
- abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ di->bm->bat_type[di->bm->batt_id].recharge_cap)
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
break;
case STATE_MAINTENANCE_A_INIT:
- abx500_chargalg_stop_safety_timer(di);
- abx500_chargalg_start_maintenance_timer(di,
+ ab8500_chargalg_stop_safety_timer(di);
+ ab8500_chargalg_start_maintenance_timer(di,
di->bm->bat_type[
di->bm->batt_id].maint_a_chg_timer_h);
- abx500_chargalg_start_charging(di,
+ ab8500_chargalg_start_charging(di,
di->bm->bat_type[
di->bm->batt_id].maint_a_vol_lvl,
di->bm->bat_type[
di->bm->batt_id].maint_a_cur_lvl);
- abx500_chargalg_state_to(di, STATE_MAINTENANCE_A);
+ ab8500_chargalg_state_to(di, STATE_MAINTENANCE_A);
power_supply_changed(di->chargalg_psy);
fallthrough;
case STATE_MAINTENANCE_A:
if (di->events.maintenance_timer_expired) {
- abx500_chargalg_stop_maintenance_timer(di);
- abx500_chargalg_state_to(di, STATE_MAINTENANCE_B_INIT);
+ ab8500_chargalg_stop_maintenance_timer(di);
+ ab8500_chargalg_state_to(di, STATE_MAINTENANCE_B_INIT);
}
break;
case STATE_MAINTENANCE_B_INIT:
- abx500_chargalg_start_maintenance_timer(di,
+ ab8500_chargalg_start_maintenance_timer(di,
di->bm->bat_type[
di->bm->batt_id].maint_b_chg_timer_h);
- abx500_chargalg_start_charging(di,
+ ab8500_chargalg_start_charging(di,
di->bm->bat_type[
di->bm->batt_id].maint_b_vol_lvl,
di->bm->bat_type[
di->bm->batt_id].maint_b_cur_lvl);
- abx500_chargalg_state_to(di, STATE_MAINTENANCE_B);
+ ab8500_chargalg_state_to(di, STATE_MAINTENANCE_B);
power_supply_changed(di->chargalg_psy);
fallthrough;
case STATE_MAINTENANCE_B:
if (di->events.maintenance_timer_expired) {
- abx500_chargalg_stop_maintenance_timer(di);
- abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ ab8500_chargalg_stop_maintenance_timer(di);
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
}
break;
case STATE_TEMP_LOWHIGH_INIT:
- abx500_chargalg_start_charging(di,
+ ab8500_chargalg_start_charging(di,
di->bm->bat_type[
di->bm->batt_id].low_high_vol_lvl,
di->bm->bat_type[
di->bm->batt_id].low_high_cur_lvl);
- abx500_chargalg_stop_maintenance_timer(di);
+ ab8500_chargalg_stop_maintenance_timer(di);
di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
- abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH);
+ ab8500_chargalg_state_to(di, STATE_TEMP_LOWHIGH);
power_supply_changed(di->chargalg_psy);
fallthrough;
case STATE_TEMP_LOWHIGH:
if (!di->events.btemp_lowhigh)
- abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
break;
case STATE_WD_EXPIRED_INIT:
- abx500_chargalg_stop_charging(di);
- abx500_chargalg_state_to(di, STATE_WD_EXPIRED);
+ ab8500_chargalg_stop_charging(di);
+ ab8500_chargalg_state_to(di, STATE_WD_EXPIRED);
fallthrough;
case STATE_WD_EXPIRED:
if (!di->events.ac_wd_expired &&
!di->events.usb_wd_expired)
- abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
break;
case STATE_TEMP_UNDEROVER_INIT:
- abx500_chargalg_stop_charging(di);
- abx500_chargalg_state_to(di, STATE_TEMP_UNDEROVER);
+ ab8500_chargalg_stop_charging(di);
+ ab8500_chargalg_state_to(di, STATE_TEMP_UNDEROVER);
fallthrough;
case STATE_TEMP_UNDEROVER:
if (!di->events.btemp_underover)
- abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ ab8500_chargalg_state_to(di, STATE_NORMAL_INIT);
break;
}
@@ -1635,17 +1636,17 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
}
/**
- * abx500_chargalg_periodic_work() - Periodic work for the algorithm
+ * ab8500_chargalg_periodic_work() - Periodic work for the algorithm
* @work: pointer to the work_struct structure
*
* Work queue function for the charging algorithm
*/
-static void abx500_chargalg_periodic_work(struct work_struct *work)
+static void ab8500_chargalg_periodic_work(struct work_struct *work)
{
- struct abx500_chargalg *di = container_of(work,
- struct abx500_chargalg, chargalg_periodic_work.work);
+ struct ab8500_chargalg *di = container_of(work,
+ struct ab8500_chargalg, chargalg_periodic_work.work);
- abx500_chargalg_algorithm(di);
+ ab8500_chargalg_algorithm(di);
/*
* If a charger is connected then the battery has to be monitored
@@ -1662,20 +1663,18 @@ static void abx500_chargalg_periodic_work(struct work_struct *work)
}
/**
- * abx500_chargalg_wd_work() - periodic work to kick the charger watchdog
+ * ab8500_chargalg_wd_work() - periodic work to kick the charger watchdog
* @work: pointer to the work_struct structure
*
* Work queue function for kicking the charger watchdog
*/
-static void abx500_chargalg_wd_work(struct work_struct *work)
+static void ab8500_chargalg_wd_work(struct work_struct *work)
{
int ret;
- struct abx500_chargalg *di = container_of(work,
- struct abx500_chargalg, chargalg_wd_work.work);
+ struct ab8500_chargalg *di = container_of(work,
+ struct ab8500_chargalg, chargalg_wd_work.work);
- dev_dbg(di->dev, "abx500_chargalg_wd_work\n");
-
- ret = abx500_chargalg_kick_watchdog(di);
+ ret = ab8500_chargalg_kick_watchdog(di);
if (ret < 0)
dev_err(di->dev, "failed to kick watchdog\n");
@@ -1684,21 +1683,21 @@ static void abx500_chargalg_wd_work(struct work_struct *work)
}
/**
- * abx500_chargalg_work() - Work to run the charging algorithm instantly
+ * ab8500_chargalg_work() - Work to run the charging algorithm instantly
* @work: pointer to the work_struct structure
*
* Work queue function for calling the charging algorithm
*/
-static void abx500_chargalg_work(struct work_struct *work)
+static void ab8500_chargalg_work(struct work_struct *work)
{
- struct abx500_chargalg *di = container_of(work,
- struct abx500_chargalg, chargalg_work);
+ struct ab8500_chargalg *di = container_of(work,
+ struct ab8500_chargalg, chargalg_work);
- abx500_chargalg_algorithm(di);
+ ab8500_chargalg_algorithm(di);
}
/**
- * abx500_chargalg_get_property() - get the chargalg properties
+ * ab8500_chargalg_get_property() - get the chargalg properties
* @psy: pointer to the power_supply structure
* @psp: pointer to the power_supply_property structure
* @val: pointer to the power_supply_propval union
@@ -1709,11 +1708,11 @@ static void abx500_chargalg_work(struct work_struct *work)
* health: health of the battery
* Returns error code in case of failure else 0 on success
*/
-static int abx500_chargalg_get_property(struct power_supply *psy,
+static int ab8500_chargalg_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
- struct abx500_chargalg *di = power_supply_get_drvdata(psy);
+ struct ab8500_chargalg *di = power_supply_get_drvdata(psy);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
@@ -1742,16 +1741,16 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
/* Exposure to the sysfs interface */
-static ssize_t abx500_chargalg_curr_step_show(struct abx500_chargalg *di,
+static ssize_t ab8500_chargalg_curr_step_show(struct ab8500_chargalg *di,
char *buf)
{
return sprintf(buf, "%d\n", di->curr_status.curr_step);
}
-static ssize_t abx500_chargalg_curr_step_store(struct abx500_chargalg *di,
+static ssize_t ab8500_chargalg_curr_step_store(struct ab8500_chargalg *di,
const char *buf, size_t length)
{
- long int param;
+ long param;
int ret;
ret = kstrtol(buf, 10, &param);
@@ -1773,7 +1772,7 @@ static ssize_t abx500_chargalg_curr_step_store(struct abx500_chargalg *di,
}
-static ssize_t abx500_chargalg_en_show(struct abx500_chargalg *di,
+static ssize_t ab8500_chargalg_en_show(struct ab8500_chargalg *di,
char *buf)
{
return sprintf(buf, "%d\n",
@@ -1781,10 +1780,10 @@ static ssize_t abx500_chargalg_en_show(struct abx500_chargalg *di,
di->susp_status.usb_suspended);
}
-static ssize_t abx500_chargalg_en_store(struct abx500_chargalg *di,
+static ssize_t ab8500_chargalg_en_store(struct ab8500_chargalg *di,
const char *buf, size_t length)
{
- long int param;
+ long param;
int ac_usb;
int ret;
@@ -1828,22 +1827,22 @@ static ssize_t abx500_chargalg_en_store(struct abx500_chargalg *di,
return strlen(buf);
}
-static struct abx500_chargalg_sysfs_entry abx500_chargalg_en_charger =
- __ATTR(chargalg, 0644, abx500_chargalg_en_show,
- abx500_chargalg_en_store);
+static struct ab8500_chargalg_sysfs_entry ab8500_chargalg_en_charger =
+ __ATTR(chargalg, 0644, ab8500_chargalg_en_show,
+ ab8500_chargalg_en_store);
-static struct abx500_chargalg_sysfs_entry abx500_chargalg_curr_step =
- __ATTR(chargalg_curr_step, 0644, abx500_chargalg_curr_step_show,
- abx500_chargalg_curr_step_store);
+static struct ab8500_chargalg_sysfs_entry ab8500_chargalg_curr_step =
+ __ATTR(chargalg_curr_step, 0644, ab8500_chargalg_curr_step_show,
+ ab8500_chargalg_curr_step_store);
-static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj,
+static ssize_t ab8500_chargalg_sysfs_show(struct kobject *kobj,
struct attribute *attr, char *buf)
{
- struct abx500_chargalg_sysfs_entry *entry = container_of(attr,
- struct abx500_chargalg_sysfs_entry, attr);
+ struct ab8500_chargalg_sysfs_entry *entry = container_of(attr,
+ struct ab8500_chargalg_sysfs_entry, attr);
- struct abx500_chargalg *di = container_of(kobj,
- struct abx500_chargalg, chargalg_kobject);
+ struct ab8500_chargalg *di = container_of(kobj,
+ struct ab8500_chargalg, chargalg_kobject);
if (!entry->show)
return -EIO;
@@ -1851,14 +1850,14 @@ static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj,
return entry->show(di, buf);
}
-static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
+static ssize_t ab8500_chargalg_sysfs_charger(struct kobject *kobj,
struct attribute *attr, const char *buf, size_t length)
{
- struct abx500_chargalg_sysfs_entry *entry = container_of(attr,
- struct abx500_chargalg_sysfs_entry, attr);
+ struct ab8500_chargalg_sysfs_entry *entry = container_of(attr,
+ struct ab8500_chargalg_sysfs_entry, attr);
- struct abx500_chargalg *di = container_of(kobj,
- struct abx500_chargalg, chargalg_kobject);
+ struct ab8500_chargalg *di = container_of(kobj,
+ struct ab8500_chargalg, chargalg_kobject);
if (!entry->store)
return -EIO;
@@ -1866,47 +1865,47 @@ static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
return entry->store(di, buf, length);
}
-static struct attribute *abx500_chargalg_chg[] = {
- &abx500_chargalg_en_charger.attr,
- &abx500_chargalg_curr_step.attr,
+static struct attribute *ab8500_chargalg_chg[] = {
+ &ab8500_chargalg_en_charger.attr,
+ &ab8500_chargalg_curr_step.attr,
NULL,
};
-static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
- .show = abx500_chargalg_sysfs_show,
- .store = abx500_chargalg_sysfs_charger,
+static const struct sysfs_ops ab8500_chargalg_sysfs_ops = {
+ .show = ab8500_chargalg_sysfs_show,
+ .store = ab8500_chargalg_sysfs_charger,
};
-static struct kobj_type abx500_chargalg_ktype = {
- .sysfs_ops = &abx500_chargalg_sysfs_ops,
- .default_attrs = abx500_chargalg_chg,
+static struct kobj_type ab8500_chargalg_ktype = {
+ .sysfs_ops = &ab8500_chargalg_sysfs_ops,
+ .default_attrs = ab8500_chargalg_chg,
};
/**
- * abx500_chargalg_sysfs_exit() - de-init of sysfs entry
- * @di: pointer to the struct abx500_chargalg
+ * ab8500_chargalg_sysfs_exit() - de-init of sysfs entry
+ * @di: pointer to the struct ab8500_chargalg
*
* This function removes the entry in sysfs.
*/
-static void abx500_chargalg_sysfs_exit(struct abx500_chargalg *di)
+static void ab8500_chargalg_sysfs_exit(struct ab8500_chargalg *di)
{
kobject_del(&di->chargalg_kobject);
}
/**
- * abx500_chargalg_sysfs_init() - init of sysfs entry
- * @di: pointer to the struct abx500_chargalg
+ * ab8500_chargalg_sysfs_init() - init of sysfs entry
+ * @di: pointer to the struct ab8500_chargalg
*
* This function adds an entry in sysfs.
* Returns error code in case of failure else 0(on success)
*/
-static int abx500_chargalg_sysfs_init(struct abx500_chargalg *di)
+static int ab8500_chargalg_sysfs_init(struct ab8500_chargalg *di)
{
int ret = 0;
ret = kobject_init_and_add(&di->chargalg_kobject,
- &abx500_chargalg_ktype,
- NULL, "abx500_chargalg");
+ &ab8500_chargalg_ktype,
+ NULL, "ab8500_chargalg");
if (ret < 0)
dev_err(di->dev, "failed to create sysfs entry\n");
@@ -1914,9 +1913,9 @@ static int abx500_chargalg_sysfs_init(struct abx500_chargalg *di)
}
/* Exposure to the sysfs interface <<END>> */
-static int __maybe_unused abx500_chargalg_resume(struct device *dev)
+static int __maybe_unused ab8500_chargalg_resume(struct device *dev)
{
- struct abx500_chargalg *di = dev_get_drvdata(dev);
+ struct ab8500_chargalg *di = dev_get_drvdata(dev);
/* Kick charger watchdog if charging (any charger online) */
if (di->chg_info.online_chg)
@@ -1931,9 +1930,9 @@ static int __maybe_unused abx500_chargalg_resume(struct device *dev)
return 0;
}
-static int __maybe_unused abx500_chargalg_suspend(struct device *dev)
+static int __maybe_unused ab8500_chargalg_suspend(struct device *dev)
{
- struct abx500_chargalg *di = dev_get_drvdata(dev);
+ struct ab8500_chargalg *di = dev_get_drvdata(dev);
if (di->chg_info.online_chg)
cancel_delayed_work_sync(&di->chargalg_wd_work);
@@ -1943,13 +1942,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 ab8500_chargalg_desc = {
+ .name = "ab8500_chargalg",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = ab8500_chargalg_props,
+ .num_properties = ARRAY_SIZE(ab8500_chargalg_props),
+ .get_property = ab8500_chargalg_get_property,
+ .external_power_changed = ab8500_chargalg_external_power_changed,
+};
+
+static int ab8500_chargalg_bind(struct device *dev, struct device *master,
+ void *data)
{
- struct abx500_chargalg *di = platform_get_drvdata(pdev);
+ struct ab8500_chargalg *di = dev_get_drvdata(dev);
+
+ /* Create a work queue for the chargalg */
+ di->chargalg_wq = alloc_ordered_workqueue("ab8500_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);
- /* sysfs interface to enable/disbale charging from user space */
- abx500_chargalg_sysfs_exit(di);
+ return 0;
+}
+static void ab8500_chargalg_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct ab8500_chargalg *di = dev_get_drvdata(dev);
+
+ /* Stop all timers and work */
hrtimer_cancel(&di->safety_timer);
hrtimer_cancel(&di->maintenance_timer);
@@ -1959,48 +1989,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 ab8500_chargalg_component_ops = {
+ .bind = ab8500_chargalg_bind,
+ .unbind = ab8500_chargalg_unbind,
};
-static int abx500_chargalg_probe(struct platform_device *pdev)
+static int ab8500_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;
+ struct ab8500_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;
@@ -2009,85 +2020,77 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
/* Initilialize safety timer */
hrtimer_init(&di->safety_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
- di->safety_timer.function = abx500_chargalg_safety_timer_expired;
+ di->safety_timer.function = ab8500_chargalg_safety_timer_expired;
/* Initilialize maintenance timer */
hrtimer_init(&di->maintenance_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
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;
- }
+ ab8500_chargalg_maintenance_timer_expired;
/* Init work for chargalg */
INIT_DEFERRABLE_WORK(&di->chargalg_periodic_work,
- abx500_chargalg_periodic_work);
+ ab8500_chargalg_periodic_work);
INIT_DEFERRABLE_WORK(&di->chargalg_wd_work,
- abx500_chargalg_wd_work);
+ ab8500_chargalg_wd_work);
/* Init work for chargalg */
- INIT_WORK(&di->chargalg_work, abx500_chargalg_work);
+ INIT_WORK(&di->chargalg_work, ab8500_chargalg_work);
/* To detect charger at startup */
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,
+ &ab8500_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);
/* sysfs interface to enable/disable charging from user space */
- ret = abx500_chargalg_sysfs_init(di);
+ ret = ab8500_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, &ab8500_chargalg_component_ops);
+}
-free_psy:
- power_supply_unregister(di->chargalg_psy);
-free_chargalg_wq:
- destroy_workqueue(di->chargalg_wq);
- return ret;
+static int ab8500_chargalg_remove(struct platform_device *pdev)
+{
+ struct ab8500_chargalg *di = platform_get_drvdata(pdev);
+
+ component_del(&pdev->dev, &ab8500_chargalg_component_ops);
+
+ /* sysfs interface to enable/disable charging from user space */
+ ab8500_chargalg_sysfs_exit(di);
+
+ return 0;
}
-static SIMPLE_DEV_PM_OPS(abx500_chargalg_pm_ops, abx500_chargalg_suspend, abx500_chargalg_resume);
+static SIMPLE_DEV_PM_OPS(ab8500_chargalg_pm_ops, ab8500_chargalg_suspend, ab8500_chargalg_resume);
static const struct of_device_id ab8500_chargalg_match[] = {
{ .compatible = "stericsson,ab8500-chargalg", },
{ },
};
-static struct platform_driver abx500_chargalg_driver = {
- .probe = abx500_chargalg_probe,
- .remove = abx500_chargalg_remove,
+struct platform_driver ab8500_chargalg_driver = {
+ .probe = ab8500_chargalg_probe,
+ .remove = ab8500_chargalg_remove,
.driver = {
- .name = "ab8500-chargalg",
+ .name = "ab8500_chargalg",
.of_match_table = ab8500_chargalg_match,
- .pm = &abx500_chargalg_pm_ops,
+ .pm = &ab8500_chargalg_pm_ops,
},
};
-
-module_platform_driver(abx500_chargalg_driver);
-
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
-MODULE_ALIAS("platform:abx500-chargalg");
-MODULE_DESCRIPTION("abx500 battery charging algorithm");
+MODULE_ALIAS("platform:ab8500-chargalg");
+MODULE_DESCRIPTION("ab8500 battery charging algorithm");
diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c
index a9be10eb2c22..15eadaf46f14 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>
@@ -291,7 +292,7 @@ struct ab8500_charger {
struct iio_channel *adc_main_charger_c;
struct iio_channel *adc_vbus_v;
struct iio_channel *adc_usb_charger_c;
- struct abx500_bm_data *bm;
+ struct ab8500_bm_data *bm;
struct ab8500_charger_event_flags flags;
struct ab8500_charger_usb_state usb_state;
struct ab8500_charger_max_usb_in_curr max_usb_in_curr;
@@ -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,
+ &ab8500_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..05fe9724ba50 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>
@@ -33,6 +34,7 @@
#include <linux/mfd/abx500/ab8500.h>
#include <linux/iio/consumer.h>
#include <linux/kernel.h>
+#include <linux/fixp-arith.h>
#include "ab8500-bm.h"
@@ -55,11 +57,8 @@
/* FG constants */
#define BATT_OVV 0x01
-#define interpolate(x, x1, y1, x2, y2) \
- ((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
*/
@@ -226,7 +225,7 @@ struct ab8500_fg {
struct ab8500_fg_avg_cap avg_cap;
struct ab8500 *parent;
struct iio_channel *main_bat_v;
- struct abx500_bm_data *bm;
+ struct ab8500_bm_data *bm;
struct power_supply *fg_psy;
struct workqueue_struct *fg_wq;
struct delayed_work fg_periodic_work;
@@ -855,7 +854,7 @@ static int ab8500_fg_bat_voltage(struct ab8500_fg *di)
static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage)
{
int i, tbl_size;
- const struct abx500_v_to_cap *tbl;
+ const struct ab8500_v_to_cap *tbl;
int cap = 0;
tbl = di->bm->bat_type[di->bm->batt_id].v_to_cap_tbl;
@@ -867,11 +866,12 @@ static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage)
}
if ((i > 0) && (i < tbl_size)) {
- cap = interpolate(voltage,
+ cap = fixp_linear_interpolate(
tbl[i].voltage,
tbl[i].capacity * 10,
tbl[i-1].voltage,
- tbl[i-1].capacity * 10);
+ tbl[i-1].capacity * 10,
+ voltage);
} else if (i == 0) {
cap = 1000;
} else {
@@ -919,11 +919,12 @@ static int ab8500_fg_battery_resistance(struct ab8500_fg *di)
}
if ((i > 0) && (i < tbl_size)) {
- resist = interpolate(di->bat_temp / 10,
+ resist = fixp_linear_interpolate(
tbl[i].temp,
tbl[i].resist,
tbl[i-1].temp,
- tbl[i-1].resist);
+ tbl[i-1].resist,
+ di->bat_temp / 10);
} else if (i == 0) {
resist = tbl[0].resist;
} else {
@@ -1727,6 +1728,7 @@ static void ab8500_fg_algorithm_calibrate(struct ab8500_fg *di)
break;
case AB8500_FG_CALIB_WAIT:
dev_dbg(di->dev, "Calibration WFI\n");
+ break;
default:
break;
}
@@ -2223,6 +2225,7 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
queue_work(di->fg_wq, &di->fg_work);
break;
}
+ break;
default:
break;
}
@@ -2232,7 +2235,7 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
case POWER_SUPPLY_TYPE_BATTERY:
if (!di->flags.batt_id_received &&
di->bm->batt_id != BATTERY_UNKNOWN) {
- const struct abx500_battery_type *b;
+ const struct ab8500_battery_type *b;
b = &(di->bm->bat_type[di->bm->batt_id]);
@@ -2980,27 +2983,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 +3006,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 +3060,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 +3089,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 +3121,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 +3129,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 +3147,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 +3173,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 +3190,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 +3214,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 +3225,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/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_charger.c b/drivers/power/supply/axp288_charger.c
index a4df1ea92386..b9553be9bed5 100644
--- a/drivers/power/supply/axp288_charger.c
+++ b/drivers/power/supply/axp288_charger.c
@@ -813,7 +813,7 @@ static int axp288_charger_probe(struct platform_device *pdev)
if (val == 0)
return -ENODEV;
- info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
@@ -823,7 +823,7 @@ static int axp288_charger_probe(struct platform_device *pdev)
info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);
if (info->cable.edev == NULL) {
- dev_dbg(&pdev->dev, "%s is not ready, probe deferred\n",
+ dev_dbg(dev, "%s is not ready, probe deferred\n",
AXP288_EXTCON_DEV_NAME);
return -EPROBE_DEFER;
}
@@ -834,8 +834,7 @@ static int axp288_charger_probe(struct platform_device *pdev)
dev_dbg(dev, "EXTCON_USB_HOST is not ready, probe deferred\n");
return -EPROBE_DEFER;
}
- dev_info(&pdev->dev,
- "Using " USB_HOST_EXTCON_HID " extcon for usb-id\n");
+ dev_info(dev, "Using " USB_HOST_EXTCON_HID " extcon for usb-id\n");
}
platform_set_drvdata(pdev, info);
@@ -874,7 +873,7 @@ static int axp288_charger_probe(struct platform_device *pdev)
INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker);
info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt;
if (info->otg.cable) {
- ret = devm_extcon_register_notifier(&pdev->dev, info->otg.cable,
+ ret = devm_extcon_register_notifier(dev, info->otg.cable,
EXTCON_USB_HOST, &info->otg.id_nb);
if (ret) {
dev_err(dev, "failed to register EXTCON_USB_HOST notifier\n");
@@ -899,7 +898,7 @@ static int axp288_charger_probe(struct platform_device *pdev)
NULL, axp288_charger_irq_thread_handler,
IRQF_ONESHOT, info->pdev->name, info);
if (ret) {
- dev_err(&pdev->dev, "failed to request interrupt=%d\n",
+ dev_err(dev, "failed to request interrupt=%d\n",
info->irq[i]);
return ret;
}
diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c
index 39e16ecb7638..c1da217fdb0e 100644
--- a/drivers/power/supply/axp288_fuel_gauge.c
+++ b/drivers/power/supply/axp288_fuel_gauge.c
@@ -2,7 +2,8 @@
/*
* axp288_fuel_gauge.c - Xpower AXP288 PMIC Fuel Gauge Driver
*
- * Copyright (C) 2016-2017 Hans de Goede <hdegoede@redhat.com>
+ * Copyright (C) 2020-2021 Andrejus Basovas <xxx@yyy.tld>
+ * Copyright (C) 2016-2021 Hans de Goede <hdegoede@redhat.com>
* Copyright (C) 2014 Intel Corporation
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -19,38 +20,37 @@
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/iio/consumer.h>
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
#include <asm/unaligned.h>
+#include <asm/iosf_mbi.h>
-#define PS_STAT_VBUS_TRIGGER (1 << 0)
-#define PS_STAT_BAT_CHRG_DIR (1 << 2)
-#define PS_STAT_VBAT_ABOVE_VHOLD (1 << 3)
-#define PS_STAT_VBUS_VALID (1 << 4)
-#define PS_STAT_VBUS_PRESENT (1 << 5)
+#define PS_STAT_VBUS_TRIGGER (1 << 0)
+#define PS_STAT_BAT_CHRG_DIR (1 << 2)
+#define PS_STAT_VBAT_ABOVE_VHOLD (1 << 3)
+#define PS_STAT_VBUS_VALID (1 << 4)
+#define PS_STAT_VBUS_PRESENT (1 << 5)
-#define CHRG_STAT_BAT_SAFE_MODE (1 << 3)
+#define CHRG_STAT_BAT_SAFE_MODE (1 << 3)
#define CHRG_STAT_BAT_VALID (1 << 4)
-#define CHRG_STAT_BAT_PRESENT (1 << 5)
+#define CHRG_STAT_BAT_PRESENT (1 << 5)
#define CHRG_STAT_CHARGING (1 << 6)
#define CHRG_STAT_PMIC_OTP (1 << 7)
#define CHRG_CCCV_CC_MASK 0xf /* 4 bits */
-#define CHRG_CCCV_CC_BIT_POS 0
+#define CHRG_CCCV_CC_BIT_POS 0
#define CHRG_CCCV_CC_OFFSET 200 /* 200mA */
-#define CHRG_CCCV_CC_LSB_RES 200 /* 200mA */
+#define CHRG_CCCV_CC_LSB_RES 200 /* 200mA */
#define CHRG_CCCV_ITERM_20P (1 << 4) /* 20% of CC */
#define CHRG_CCCV_CV_MASK 0x60 /* 2 bits */
-#define CHRG_CCCV_CV_BIT_POS 5
+#define CHRG_CCCV_CV_BIT_POS 5
#define CHRG_CCCV_CV_4100MV 0x0 /* 4.10V */
#define CHRG_CCCV_CV_4150MV 0x1 /* 4.15V */
#define CHRG_CCCV_CV_4200MV 0x2 /* 4.20V */
#define CHRG_CCCV_CV_4350MV 0x3 /* 4.35V */
#define CHRG_CCCV_CHG_EN (1 << 7)
-#define FG_CNTL_OCV_ADJ_STAT (1 << 2)
+#define FG_CNTL_OCV_ADJ_STAT (1 << 2)
#define FG_CNTL_OCV_ADJ_EN (1 << 3)
-#define FG_CNTL_CAP_ADJ_STAT (1 << 4)
+#define FG_CNTL_CAP_ADJ_STAT (1 << 4)
#define FG_CNTL_CAP_ADJ_EN (1 << 5)
#define FG_CNTL_CC_EN (1 << 6)
#define FG_CNTL_GAUGE_EN (1 << 7)
@@ -71,23 +71,23 @@
#define FG_CC_CAP_VALID (1 << 7)
#define FG_CC_CAP_VAL_MASK 0x7F
-#define FG_LOW_CAP_THR1_MASK 0xf0 /* 5% tp 20% */
+#define FG_LOW_CAP_THR1_MASK 0xf0 /* 5% tp 20% */
#define FG_LOW_CAP_THR1_VAL 0xa0 /* 15 perc */
-#define FG_LOW_CAP_THR2_MASK 0x0f /* 0% to 15% */
+#define FG_LOW_CAP_THR2_MASK 0x0f /* 0% to 15% */
#define FG_LOW_CAP_WARN_THR 14 /* 14 perc */
#define FG_LOW_CAP_CRIT_THR 4 /* 4 perc */
#define FG_LOW_CAP_SHDN_THR 0 /* 0 perc */
-#define NR_RETRY_CNT 3
-#define DEV_NAME "axp288_fuel_gauge"
+#define DEV_NAME "axp288_fuel_gauge"
/* 1.1mV per LSB expressed in uV */
#define VOLTAGE_FROM_ADC(a) ((a * 11) / 10)
/* properties converted to uV, uA */
-#define PROP_VOLT(a) ((a) * 1000)
-#define PROP_CURR(a) ((a) * 1000)
+#define PROP_VOLT(a) ((a) * 1000)
+#define PROP_CURR(a) ((a) * 1000)
-#define AXP288_FG_INTR_NUM 6
+#define AXP288_REG_UPDATE_INTERVAL (60 * HZ)
+#define AXP288_FG_INTR_NUM 6
enum {
QWBTU_IRQ = 0,
WBTU_IRQ,
@@ -98,9 +98,6 @@ enum {
};
enum {
- BAT_TEMP = 0,
- PMIC_TEMP,
- SYSTEM_TEMP,
BAT_CHRG_CURR,
BAT_D_CURR,
BAT_VOLT,
@@ -108,7 +105,7 @@ enum {
};
struct axp288_fg_info {
- struct platform_device *pdev;
+ struct device *dev;
struct regmap *regmap;
struct regmap_irq_chip_data *regmap_irqc;
int irq[AXP288_FG_INTR_NUM];
@@ -117,7 +114,21 @@ struct axp288_fg_info {
struct mutex lock;
int status;
int max_volt;
+ int pwr_op;
+ int low_cap;
struct dentry *debug_file;
+
+ char valid; /* zero until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ int pwr_stat;
+ int fg_res;
+ int bat_volt;
+ int d_curr;
+ int c_curr;
+ int ocv;
+ int fg_cc_mtr1;
+ int fg_des_cap1;
};
static enum power_supply_property fuel_gauge_props[] = {
@@ -137,19 +148,12 @@ static enum power_supply_property fuel_gauge_props[] = {
static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg)
{
- int ret, i;
unsigned int val;
+ int ret;
- for (i = 0; i < NR_RETRY_CNT; i++) {
- ret = regmap_read(info->regmap, reg, &val);
- if (ret == -EBUSY)
- continue;
- else
- break;
- }
-
+ ret = regmap_read(info->regmap, reg, &val);
if (ret < 0) {
- dev_err(&info->pdev->dev, "axp288 reg read err:%d\n", ret);
+ dev_err(info->dev, "Error reading reg 0x%02x err: %d\n", reg, ret);
return ret;
}
@@ -163,7 +167,7 @@ static int fuel_gauge_reg_writeb(struct axp288_fg_info *info, int reg, u8 val)
ret = regmap_write(info->regmap, reg, (unsigned int)val);
if (ret < 0)
- dev_err(&info->pdev->dev, "axp288 reg write err:%d\n", ret);
+ dev_err(info->dev, "Error writing reg 0x%02x err: %d\n", reg, ret);
return ret;
}
@@ -175,15 +179,13 @@ static int fuel_gauge_read_15bit_word(struct axp288_fg_info *info, int reg)
ret = regmap_bulk_read(info->regmap, reg, buf, 2);
if (ret < 0) {
- dev_err(&info->pdev->dev, "Error reading reg 0x%02x err: %d\n",
- reg, ret);
+ dev_err(info->dev, "Error reading reg 0x%02x err: %d\n", reg, ret);
return ret;
}
ret = get_unaligned_be16(buf);
if (!(ret & FG_15BIT_WORD_VALID)) {
- dev_err(&info->pdev->dev, "Error reg 0x%02x contents not valid\n",
- reg);
+ dev_err(info->dev, "Error reg 0x%02x contents not valid\n", reg);
return -ENXIO;
}
@@ -197,8 +199,7 @@ static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg)
ret = regmap_bulk_read(info->regmap, reg, buf, 2);
if (ret < 0) {
- dev_err(&info->pdev->dev, "Error reading reg 0x%02x err: %d\n",
- reg, ret);
+ dev_err(info->dev, "Error reading reg 0x%02x err: %d\n", reg, ret);
return ret;
}
@@ -206,139 +207,78 @@ static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg)
return (buf[0] << 4) | ((buf[1] >> 4) & 0x0f);
}
-#ifdef CONFIG_DEBUG_FS
-static int fuel_gauge_debug_show(struct seq_file *s, void *data)
+static int fuel_gauge_update_registers(struct axp288_fg_info *info)
{
- struct axp288_fg_info *info = s->private;
- int raw_val, ret;
-
- seq_printf(s, " PWR_STATUS[%02x] : %02x\n",
- AXP20X_PWR_INPUT_STATUS,
- fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS));
- seq_printf(s, "PWR_OP_MODE[%02x] : %02x\n",
- AXP20X_PWR_OP_MODE,
- fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE));
- seq_printf(s, " CHRG_CTRL1[%02x] : %02x\n",
- AXP20X_CHRG_CTRL1,
- fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1));
- seq_printf(s, " VLTF[%02x] : %02x\n",
- AXP20X_V_LTF_DISCHRG,
- fuel_gauge_reg_readb(info, AXP20X_V_LTF_DISCHRG));
- seq_printf(s, " VHTF[%02x] : %02x\n",
- AXP20X_V_HTF_DISCHRG,
- fuel_gauge_reg_readb(info, AXP20X_V_HTF_DISCHRG));
- seq_printf(s, " CC_CTRL[%02x] : %02x\n",
- AXP20X_CC_CTRL,
- fuel_gauge_reg_readb(info, AXP20X_CC_CTRL));
- seq_printf(s, "BATTERY CAP[%02x] : %02x\n",
- AXP20X_FG_RES,
- fuel_gauge_reg_readb(info, AXP20X_FG_RES));
- seq_printf(s, " FG_RDC1[%02x] : %02x\n",
- AXP288_FG_RDC1_REG,
- fuel_gauge_reg_readb(info, AXP288_FG_RDC1_REG));
- seq_printf(s, " FG_RDC0[%02x] : %02x\n",
- AXP288_FG_RDC0_REG,
- fuel_gauge_reg_readb(info, AXP288_FG_RDC0_REG));
- seq_printf(s, " FG_OCV[%02x] : %04x\n",
- AXP288_FG_OCVH_REG,
- fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG));
- seq_printf(s, " FG_DES_CAP[%02x] : %04x\n",
- AXP288_FG_DES_CAP1_REG,
- fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG));
- seq_printf(s, " FG_CC_MTR[%02x] : %04x\n",
- AXP288_FG_CC_MTR1_REG,
- fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG));
- seq_printf(s, " FG_OCV_CAP[%02x] : %02x\n",
- AXP288_FG_OCV_CAP_REG,
- fuel_gauge_reg_readb(info, AXP288_FG_OCV_CAP_REG));
- seq_printf(s, " FG_CC_CAP[%02x] : %02x\n",
- AXP288_FG_CC_CAP_REG,
- fuel_gauge_reg_readb(info, AXP288_FG_CC_CAP_REG));
- seq_printf(s, " FG_LOW_CAP[%02x] : %02x\n",
- AXP288_FG_LOW_CAP_REG,
- fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG));
- seq_printf(s, "TUNING_CTL0[%02x] : %02x\n",
- AXP288_FG_TUNE0,
- fuel_gauge_reg_readb(info, AXP288_FG_TUNE0));
- seq_printf(s, "TUNING_CTL1[%02x] : %02x\n",
- AXP288_FG_TUNE1,
- fuel_gauge_reg_readb(info, AXP288_FG_TUNE1));
- seq_printf(s, "TUNING_CTL2[%02x] : %02x\n",
- AXP288_FG_TUNE2,
- fuel_gauge_reg_readb(info, AXP288_FG_TUNE2));
- seq_printf(s, "TUNING_CTL3[%02x] : %02x\n",
- AXP288_FG_TUNE3,
- fuel_gauge_reg_readb(info, AXP288_FG_TUNE3));
- seq_printf(s, "TUNING_CTL4[%02x] : %02x\n",
- AXP288_FG_TUNE4,
- fuel_gauge_reg_readb(info, AXP288_FG_TUNE4));
- seq_printf(s, "TUNING_CTL5[%02x] : %02x\n",
- AXP288_FG_TUNE5,
- fuel_gauge_reg_readb(info, AXP288_FG_TUNE5));
-
- ret = iio_read_channel_raw(info->iio_channel[BAT_TEMP], &raw_val);
- if (ret >= 0)
- seq_printf(s, "axp288-batttemp : %d\n", raw_val);
- ret = iio_read_channel_raw(info->iio_channel[PMIC_TEMP], &raw_val);
- if (ret >= 0)
- seq_printf(s, "axp288-pmictemp : %d\n", raw_val);
- ret = iio_read_channel_raw(info->iio_channel[SYSTEM_TEMP], &raw_val);
- if (ret >= 0)
- seq_printf(s, "axp288-systtemp : %d\n", raw_val);
- ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &raw_val);
- if (ret >= 0)
- seq_printf(s, "axp288-chrgcurr : %d\n", raw_val);
- ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &raw_val);
- if (ret >= 0)
- seq_printf(s, "axp288-dchrgcur : %d\n", raw_val);
- ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &raw_val);
- if (ret >= 0)
- seq_printf(s, "axp288-battvolt : %d\n", raw_val);
+ int ret;
- return 0;
-}
+ if (info->valid && time_before(jiffies, info->last_updated + AXP288_REG_UPDATE_INTERVAL))
+ return 0;
-DEFINE_SHOW_ATTRIBUTE(fuel_gauge_debug);
+ dev_dbg(info->dev, "Fuel Gauge updating register values...\n");
-static void fuel_gauge_create_debugfs(struct axp288_fg_info *info)
-{
- info->debug_file = debugfs_create_file("fuelgauge", 0666, NULL,
- info, &fuel_gauge_debug_fops);
-}
+ ret = iosf_mbi_block_punit_i2c_access();
+ if (ret < 0)
+ return ret;
-static void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)
-{
- debugfs_remove(info->debug_file);
-}
-#else
-static inline void fuel_gauge_create_debugfs(struct axp288_fg_info *info)
-{
-}
-static inline void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)
-{
+ ret = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS);
+ if (ret < 0)
+ goto out;
+ info->pwr_stat = ret;
+
+ ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
+ if (ret < 0)
+ goto out;
+ info->fg_res = ret;
+
+ ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &info->bat_volt);
+ if (ret < 0)
+ goto out;
+
+ if (info->pwr_stat & PS_STAT_BAT_CHRG_DIR) {
+ info->d_curr = 0;
+ ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &info->c_curr);
+ if (ret < 0)
+ goto out;
+ } else {
+ info->c_curr = 0;
+ ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &info->d_curr);
+ if (ret < 0)
+ goto out;
+ }
+
+ ret = fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG);
+ if (ret < 0)
+ goto out;
+ info->ocv = ret;
+
+ ret = fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG);
+ if (ret < 0)
+ goto out;
+ info->fg_cc_mtr1 = ret;
+
+ ret = fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG);
+ if (ret < 0)
+ goto out;
+ info->fg_des_cap1 = ret;
+
+ info->last_updated = jiffies;
+ info->valid = 1;
+ ret = 0;
+out:
+ iosf_mbi_unblock_punit_i2c_access();
+ return ret;
}
-#endif
static void fuel_gauge_get_status(struct axp288_fg_info *info)
{
- int pwr_stat, fg_res, curr, ret;
-
- pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS);
- if (pwr_stat < 0) {
- dev_err(&info->pdev->dev,
- "PWR STAT read failed:%d\n", pwr_stat);
- return;
- }
+ int pwr_stat = info->pwr_stat;
+ int fg_res = info->fg_res;
+ int curr = info->d_curr;
/* Report full if Vbus is valid and the reported capacity is 100% */
if (!(pwr_stat & PS_STAT_VBUS_VALID))
goto not_full;
- fg_res = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
- if (fg_res < 0) {
- dev_err(&info->pdev->dev, "FG RES read failed: %d\n", fg_res);
- return;
- }
if (!(fg_res & FG_REP_CAP_VALID))
goto not_full;
@@ -356,11 +296,6 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info)
if (fg_res < 90 || (pwr_stat & PS_STAT_BAT_CHRG_DIR))
goto not_full;
- ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &curr);
- if (ret < 0) {
- dev_err(&info->pdev->dev, "FG get current failed: %d\n", ret);
- return;
- }
if (curr == 0) {
info->status = POWER_SUPPLY_STATUS_FULL;
return;
@@ -373,61 +308,16 @@ not_full:
info->status = POWER_SUPPLY_STATUS_DISCHARGING;
}
-static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt)
-{
- int ret = 0, raw_val;
-
- ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &raw_val);
- if (ret < 0)
- goto vbatt_read_fail;
-
- *vbatt = VOLTAGE_FROM_ADC(raw_val);
-vbatt_read_fail:
- return ret;
-}
-
-static int fuel_gauge_get_current(struct axp288_fg_info *info, int *cur)
-{
- int ret, discharge;
-
- /* First check discharge current, so that we do only 1 read on bat. */
- ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &discharge);
- if (ret < 0)
- return ret;
-
- if (discharge > 0) {
- *cur = -1 * discharge;
- return 0;
- }
-
- return iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], cur);
-}
-
-static int fuel_gauge_get_vocv(struct axp288_fg_info *info, int *vocv)
-{
- int ret;
-
- ret = fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG);
- if (ret >= 0)
- *vocv = VOLTAGE_FROM_ADC(ret);
-
- return ret;
-}
-
static int fuel_gauge_battery_health(struct axp288_fg_info *info)
{
- int ret, vocv, health = POWER_SUPPLY_HEALTH_UNKNOWN;
-
- ret = fuel_gauge_get_vocv(info, &vocv);
- if (ret < 0)
- goto health_read_fail;
+ int vocv = VOLTAGE_FROM_ADC(info->ocv);
+ int health = POWER_SUPPLY_HEALTH_UNKNOWN;
if (vocv > info->max_volt)
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
else
health = POWER_SUPPLY_HEALTH_GOOD;
-health_read_fail:
return health;
}
@@ -436,9 +326,14 @@ static int fuel_gauge_get_property(struct power_supply *ps,
union power_supply_propval *val)
{
struct axp288_fg_info *info = power_supply_get_drvdata(ps);
- int ret = 0, value;
+ int ret, value;
mutex_lock(&info->lock);
+
+ ret = fuel_gauge_update_registers(info);
+ if (ret < 0)
+ goto out;
+
switch (prop) {
case POWER_SUPPLY_PROP_STATUS:
fuel_gauge_get_status(info);
@@ -448,78 +343,52 @@ static int fuel_gauge_get_property(struct power_supply *ps,
val->intval = fuel_gauge_battery_health(info);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- ret = fuel_gauge_get_vbatt(info, &value);
- if (ret < 0)
- goto fuel_gauge_read_err;
+ value = VOLTAGE_FROM_ADC(info->bat_volt);
val->intval = PROP_VOLT(value);
break;
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
- ret = fuel_gauge_get_vocv(info, &value);
- if (ret < 0)
- goto fuel_gauge_read_err;
+ value = VOLTAGE_FROM_ADC(info->ocv);
val->intval = PROP_VOLT(value);
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
- ret = fuel_gauge_get_current(info, &value);
- if (ret < 0)
- goto fuel_gauge_read_err;
+ if (info->d_curr > 0)
+ value = -1 * info->d_curr;
+ else
+ value = info->c_curr;
+
val->intval = PROP_CURR(value);
break;
case POWER_SUPPLY_PROP_PRESENT:
- ret = fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE);
- if (ret < 0)
- goto fuel_gauge_read_err;
-
- if (ret & CHRG_STAT_BAT_PRESENT)
+ if (info->pwr_op & CHRG_STAT_BAT_PRESENT)
val->intval = 1;
else
val->intval = 0;
break;
case POWER_SUPPLY_PROP_CAPACITY:
- ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
- if (ret < 0)
- goto fuel_gauge_read_err;
-
- if (!(ret & FG_REP_CAP_VALID))
- dev_err(&info->pdev->dev,
- "capacity measurement not valid\n");
- val->intval = (ret & FG_REP_CAP_VAL_MASK);
+ if (!(info->fg_res & FG_REP_CAP_VALID))
+ dev_err(info->dev, "capacity measurement not valid\n");
+ val->intval = (info->fg_res & FG_REP_CAP_VAL_MASK);
break;
case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
- ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG);
- if (ret < 0)
- goto fuel_gauge_read_err;
- val->intval = (ret & 0x0f);
+ val->intval = (info->low_cap & 0x0f);
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
case POWER_SUPPLY_PROP_CHARGE_NOW:
- ret = fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG);
- if (ret < 0)
- goto fuel_gauge_read_err;
-
- val->intval = ret * FG_DES_CAP_RES_LSB;
+ val->intval = info->fg_cc_mtr1 * FG_DES_CAP_RES_LSB;
break;
case POWER_SUPPLY_PROP_CHARGE_FULL:
- ret = fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG);
- if (ret < 0)
- goto fuel_gauge_read_err;
-
- val->intval = ret * FG_DES_CAP_RES_LSB;
+ val->intval = info->fg_des_cap1 * FG_DES_CAP_RES_LSB;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
val->intval = PROP_VOLT(info->max_volt);
break;
default:
- mutex_unlock(&info->lock);
- return -EINVAL;
+ ret = -EINVAL;
}
- mutex_unlock(&info->lock);
- return 0;
-
-fuel_gauge_read_err:
+out:
mutex_unlock(&info->lock);
return ret;
}
@@ -529,7 +398,7 @@ static int fuel_gauge_set_property(struct power_supply *ps,
const union power_supply_propval *val)
{
struct axp288_fg_info *info = power_supply_get_drvdata(ps);
- int ret = 0;
+ int new_low_cap, ret = 0;
mutex_lock(&info->lock);
switch (prop) {
@@ -538,12 +407,12 @@ static int fuel_gauge_set_property(struct power_supply *ps,
ret = -EINVAL;
break;
}
- ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG);
- if (ret < 0)
- break;
- ret &= 0xf0;
- ret |= (val->intval & 0xf);
- ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, ret);
+ new_low_cap = info->low_cap;
+ new_low_cap &= 0xf0;
+ new_low_cap |= (val->intval & 0xf);
+ ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, new_low_cap);
+ if (ret == 0)
+ info->low_cap = new_low_cap;
break;
default:
ret = -EINVAL;
@@ -581,37 +450,35 @@ static irqreturn_t fuel_gauge_thread_handler(int irq, void *dev)
}
if (i >= AXP288_FG_INTR_NUM) {
- dev_warn(&info->pdev->dev, "spurious interrupt!!\n");
+ dev_warn(info->dev, "spurious interrupt!!\n");
return IRQ_NONE;
}
switch (i) {
case QWBTU_IRQ:
- dev_info(&info->pdev->dev,
- "Quit Battery under temperature in work mode IRQ (QWBTU)\n");
+ dev_info(info->dev, "Quit Battery under temperature in work mode IRQ (QWBTU)\n");
break;
case WBTU_IRQ:
- dev_info(&info->pdev->dev,
- "Battery under temperature in work mode IRQ (WBTU)\n");
+ dev_info(info->dev, "Battery under temperature in work mode IRQ (WBTU)\n");
break;
case QWBTO_IRQ:
- dev_info(&info->pdev->dev,
- "Quit Battery over temperature in work mode IRQ (QWBTO)\n");
+ dev_info(info->dev, "Quit Battery over temperature in work mode IRQ (QWBTO)\n");
break;
case WBTO_IRQ:
- dev_info(&info->pdev->dev,
- "Battery over temperature in work mode IRQ (WBTO)\n");
+ dev_info(info->dev, "Battery over temperature in work mode IRQ (WBTO)\n");
break;
case WL2_IRQ:
- dev_info(&info->pdev->dev, "Low Batt Warning(2) INTR\n");
+ dev_info(info->dev, "Low Batt Warning(2) INTR\n");
break;
case WL1_IRQ:
- dev_info(&info->pdev->dev, "Low Batt Warning(1) INTR\n");
+ dev_info(info->dev, "Low Batt Warning(1) INTR\n");
break;
default:
- dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n");
+ dev_warn(info->dev, "Spurious Interrupt!!!\n");
}
+ info->valid = 0; /* Force updating of the cached registers */
+
power_supply_changed(info->bat);
return IRQ_HANDLED;
}
@@ -620,6 +487,7 @@ static void fuel_gauge_external_power_changed(struct power_supply *psy)
{
struct axp288_fg_info *info = power_supply_get_drvdata(psy);
+ info->valid = 0; /* Force updating of the cached registers */
power_supply_changed(info->bat);
}
@@ -634,16 +502,15 @@ static const struct power_supply_desc fuel_gauge_desc = {
.external_power_changed = fuel_gauge_external_power_changed,
};
-static void fuel_gauge_init_irq(struct axp288_fg_info *info)
+static void fuel_gauge_init_irq(struct axp288_fg_info *info, struct platform_device *pdev)
{
int ret, i, pirq;
for (i = 0; i < AXP288_FG_INTR_NUM; i++) {
- pirq = platform_get_irq(info->pdev, i);
+ pirq = platform_get_irq(pdev, i);
info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
if (info->irq[i] < 0) {
- dev_warn(&info->pdev->dev,
- "regmap_irq get virq failed for IRQ %d: %d\n",
+ dev_warn(info->dev, "regmap_irq get virq failed for IRQ %d: %d\n",
pirq, info->irq[i]);
info->irq[i] = -1;
goto intr_failed;
@@ -652,14 +519,10 @@ static void fuel_gauge_init_irq(struct axp288_fg_info *info)
NULL, fuel_gauge_thread_handler,
IRQF_ONESHOT, DEV_NAME, info);
if (ret) {
- dev_warn(&info->pdev->dev,
- "request irq failed for IRQ %d: %d\n",
+ dev_warn(info->dev, "request irq failed for IRQ %d: %d\n",
pirq, info->irq[i]);
info->irq[i] = -1;
goto intr_failed;
- } else {
- dev_info(&info->pdev->dev, "HW IRQ %d -> VIRQ %d\n",
- pirq, info->irq[i]);
}
}
return;
@@ -676,7 +539,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 +586,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 +599,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"),
+ },
+ },
{}
};
@@ -755,36 +618,24 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
struct power_supply_config psy_cfg = {};
static const char * const iio_chan_name[] = {
- [BAT_TEMP] = "axp288-batt-temp",
- [PMIC_TEMP] = "axp288-pmic-temp",
- [SYSTEM_TEMP] = "axp288-system-temp",
[BAT_CHRG_CURR] = "axp288-chrg-curr",
[BAT_D_CURR] = "axp288-chrg-d-curr",
[BAT_VOLT] = "axp288-batt-volt",
};
unsigned int val;
- if (dmi_check_system(axp288_fuel_gauge_blacklist))
- return -ENODEV;
-
- /*
- * On some devices the fuelgauge and charger parts of the axp288 are
- * not used, check that the fuelgauge is enabled (CC_CTRL != 0).
- */
- ret = regmap_read(axp20x->regmap, AXP20X_CC_CTRL, &val);
- if (ret < 0)
- return ret;
- if (val == 0)
+ if (dmi_check_system(axp288_no_battery_list))
return -ENODEV;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
- info->pdev = pdev;
+ info->dev = &pdev->dev;
info->regmap = axp20x->regmap;
info->regmap_irqc = axp20x->regmap_irqc;
info->status = POWER_SUPPLY_STATUS_UNKNOWN;
+ info->valid = 0;
platform_set_drvdata(pdev, info);
@@ -810,19 +661,35 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
}
}
- ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
+ ret = iosf_mbi_block_punit_i2c_access();
if (ret < 0)
goto out_free_iio_chan;
+ /*
+ * On some devices the fuelgauge and charger parts of the axp288 are
+ * not used, check that the fuelgauge is enabled (CC_CTRL != 0).
+ */
+ ret = regmap_read(axp20x->regmap, AXP20X_CC_CTRL, &val);
+ if (ret < 0)
+ goto unblock_punit_i2c_access;
+ if (val == 0) {
+ ret = -ENODEV;
+ goto unblock_punit_i2c_access;
+ }
+
+ ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
+ if (ret < 0)
+ goto unblock_punit_i2c_access;
+
if (!(ret & FG_DES_CAP1_VALID)) {
dev_err(&pdev->dev, "axp288 not configured by firmware\n");
ret = -ENODEV;
- goto out_free_iio_chan;
+ goto unblock_punit_i2c_access;
}
ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1);
if (ret < 0)
- goto out_free_iio_chan;
+ goto unblock_punit_i2c_access;
switch ((ret & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS) {
case CHRG_CCCV_CV_4100MV:
info->max_volt = 4100;
@@ -838,6 +705,22 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
break;
}
+ ret = fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE);
+ if (ret < 0)
+ goto unblock_punit_i2c_access;
+ info->pwr_op = ret;
+
+ ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG);
+ if (ret < 0)
+ goto unblock_punit_i2c_access;
+ info->low_cap = ret;
+
+unblock_punit_i2c_access:
+ iosf_mbi_unblock_punit_i2c_access();
+ /* In case we arrive here by goto because of a register access error */
+ if (ret < 0)
+ goto out_free_iio_chan;
+
psy_cfg.drv_data = info;
info->bat = power_supply_register(&pdev->dev, &fuel_gauge_desc, &psy_cfg);
if (IS_ERR(info->bat)) {
@@ -846,8 +729,7 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
goto out_free_iio_chan;
}
- fuel_gauge_create_debugfs(info);
- fuel_gauge_init_irq(info);
+ fuel_gauge_init_irq(info, pdev);
return 0;
@@ -871,7 +753,6 @@ static int axp288_fuel_gauge_remove(struct platform_device *pdev)
int i;
power_supply_unregister(info->bat);
- fuel_gauge_remove_debugfs(info);
for (i = 0; i < AXP288_FG_INTR_NUM; i++)
if (info->irq[i] >= 0)
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(&current_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, &reg, &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, &reg, &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/bq24735-charger.c b/drivers/power/supply/bq24735-charger.c
index b5d619db79f6..3ce36d09c017 100644
--- a/drivers/power/supply/bq24735-charger.c
+++ b/drivers/power/supply/bq24735-charger.c
@@ -31,9 +31,8 @@
#include <linux/power/bq24735-charger.h>
-#define BQ24735_CHG_OPT 0x12
-#define BQ24735_CHG_OPT_CHARGE_DISABLE (1 << 0)
-#define BQ24735_CHG_OPT_AC_PRESENT (1 << 4)
+/* BQ24735 available commands and their respective masks */
+#define BQ24735_CHARGE_OPT 0x12
#define BQ24735_CHARGE_CURRENT 0x14
#define BQ24735_CHARGE_CURRENT_MASK 0x1fc0
#define BQ24735_CHARGE_VOLTAGE 0x15
@@ -43,6 +42,10 @@
#define BQ24735_MANUFACTURER_ID 0xfe
#define BQ24735_DEVICE_ID 0xff
+/* ChargeOptions bits of interest */
+#define BQ24735_CHARGE_OPT_CHG_DISABLE (1 << 0)
+#define BQ24735_CHARGE_OPT_AC_PRESENT (1 << 4)
+
struct bq24735 {
struct power_supply *charger;
struct power_supply_desc charger_desc;
@@ -167,8 +170,8 @@ static inline int bq24735_enable_charging(struct bq24735 *charger)
if (ret)
return ret;
- return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
- BQ24735_CHG_OPT_CHARGE_DISABLE, 0);
+ return bq24735_update_word(charger->client, BQ24735_CHARGE_OPT,
+ BQ24735_CHARGE_OPT_CHG_DISABLE, 0);
}
static inline int bq24735_disable_charging(struct bq24735 *charger)
@@ -176,9 +179,9 @@ static inline int bq24735_disable_charging(struct bq24735 *charger)
if (charger->pdata->ext_control)
return 0;
- return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
- BQ24735_CHG_OPT_CHARGE_DISABLE,
- BQ24735_CHG_OPT_CHARGE_DISABLE);
+ return bq24735_update_word(charger->client, BQ24735_CHARGE_OPT,
+ BQ24735_CHARGE_OPT_CHG_DISABLE,
+ BQ24735_CHARGE_OPT_CHG_DISABLE);
}
static bool bq24735_charger_is_present(struct bq24735 *charger)
@@ -188,14 +191,14 @@ static bool bq24735_charger_is_present(struct bq24735 *charger)
} else {
int ac = 0;
- ac = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
+ ac = bq24735_read_word(charger->client, BQ24735_CHARGE_OPT);
if (ac < 0) {
dev_dbg(&charger->client->dev,
"Failed to read charger options : %d\n",
ac);
return false;
}
- return (ac & BQ24735_CHG_OPT_AC_PRESENT) ? true : false;
+ return (ac & BQ24735_CHARGE_OPT_AC_PRESENT) ? true : false;
}
return false;
@@ -208,11 +211,11 @@ static int bq24735_charger_is_charging(struct bq24735 *charger)
if (!bq24735_charger_is_present(charger))
return 0;
- ret = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
+ ret = bq24735_read_word(charger->client, BQ24735_CHARGE_OPT);
if (ret < 0)
return ret;
- return !(ret & BQ24735_CHG_OPT_CHARGE_DISABLE);
+ return !(ret & BQ24735_CHARGE_OPT_CHG_DISABLE);
}
static void bq24735_update(struct bq24735 *charger)
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/cros_peripheral_charger.c b/drivers/power/supply/cros_peripheral_charger.c
new file mode 100644
index 000000000000..305f10dfc06d
--- /dev/null
+++ b/drivers/power/supply/cros_peripheral_charger.c
@@ -0,0 +1,386 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Power supply driver for ChromeOS EC based Peripheral Device Charger.
+ *
+ * Copyright 2020 Google LLC.
+ */
+
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/stringify.h>
+#include <linux/types.h>
+
+#define DRV_NAME "cros-ec-pchg"
+#define PCHG_DIR_PREFIX "peripheral"
+#define PCHG_DIR_NAME PCHG_DIR_PREFIX "%d"
+#define PCHG_DIR_NAME_LENGTH \
+ sizeof(PCHG_DIR_PREFIX __stringify(EC_PCHG_MAX_PORTS))
+#define PCHG_CACHE_UPDATE_DELAY msecs_to_jiffies(500)
+
+struct port_data {
+ int port_number;
+ char name[PCHG_DIR_NAME_LENGTH];
+ struct power_supply *psy;
+ struct power_supply_desc psy_desc;
+ int psy_status;
+ int battery_percentage;
+ int charge_type;
+ struct charger_data *charger;
+ unsigned long last_update;
+};
+
+struct charger_data {
+ struct device *dev;
+ struct cros_ec_dev *ec_dev;
+ struct cros_ec_device *ec_device;
+ int num_registered_psy;
+ struct port_data *ports[EC_PCHG_MAX_PORTS];
+ struct notifier_block notifier;
+};
+
+static enum power_supply_property cros_pchg_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_SCOPE,
+};
+
+static int cros_pchg_ec_command(const struct charger_data *charger,
+ unsigned int version,
+ unsigned int command,
+ const void *outdata,
+ unsigned int outsize,
+ void *indata,
+ unsigned int insize)
+{
+ struct cros_ec_dev *ec_dev = charger->ec_dev;
+ struct cros_ec_command *msg;
+ int ret;
+
+ msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->version = version;
+ msg->command = ec_dev->cmd_offset + command;
+ msg->outsize = outsize;
+ msg->insize = insize;
+
+ if (outsize)
+ memcpy(msg->data, outdata, outsize);
+
+ ret = cros_ec_cmd_xfer_status(charger->ec_device, msg);
+ if (ret >= 0 && insize)
+ memcpy(indata, msg->data, insize);
+
+ kfree(msg);
+ return ret;
+}
+
+static const unsigned int pchg_cmd_version = 1;
+
+static bool cros_pchg_cmd_ver_check(const struct charger_data *charger)
+{
+ struct ec_params_get_cmd_versions_v1 req;
+ struct ec_response_get_cmd_versions rsp;
+ int ret;
+
+ req.cmd = EC_CMD_PCHG;
+ ret = cros_pchg_ec_command(charger, 1, EC_CMD_GET_CMD_VERSIONS,
+ &req, sizeof(req), &rsp, sizeof(rsp));
+ if (ret < 0) {
+ dev_warn(charger->dev,
+ "Unable to get versions of EC_CMD_PCHG (err:%d)\n",
+ ret);
+ return false;
+ }
+
+ return !!(rsp.version_mask & BIT(pchg_cmd_version));
+}
+
+static int cros_pchg_port_count(const struct charger_data *charger)
+{
+ struct ec_response_pchg_count rsp;
+ int ret;
+
+ ret = cros_pchg_ec_command(charger, 0, EC_CMD_PCHG_COUNT,
+ NULL, 0, &rsp, sizeof(rsp));
+ if (ret < 0) {
+ dev_warn(charger->dev,
+ "Unable to get number or ports (err:%d)\n", ret);
+ return ret;
+ }
+
+ return rsp.port_count;
+}
+
+static int cros_pchg_get_status(struct port_data *port)
+{
+ struct charger_data *charger = port->charger;
+ struct ec_params_pchg req;
+ struct ec_response_pchg rsp;
+ struct device *dev = charger->dev;
+ int old_status = port->psy_status;
+ int old_percentage = port->battery_percentage;
+ int ret;
+
+ req.port = port->port_number;
+ ret = cros_pchg_ec_command(charger, pchg_cmd_version, EC_CMD_PCHG,
+ &req, sizeof(req), &rsp, sizeof(rsp));
+ if (ret < 0) {
+ dev_err(dev, "Unable to get port.%d status (err:%d)\n",
+ port->port_number, ret);
+ return ret;
+ }
+
+ switch (rsp.state) {
+ case PCHG_STATE_RESET:
+ case PCHG_STATE_INITIALIZED:
+ case PCHG_STATE_ENABLED:
+ default:
+ port->psy_status = POWER_SUPPLY_STATUS_UNKNOWN;
+ port->charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ break;
+ case PCHG_STATE_DETECTED:
+ port->psy_status = POWER_SUPPLY_STATUS_CHARGING;
+ port->charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+ break;
+ case PCHG_STATE_CHARGING:
+ port->psy_status = POWER_SUPPLY_STATUS_CHARGING;
+ port->charge_type = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
+ break;
+ case PCHG_STATE_FULL:
+ port->psy_status = POWER_SUPPLY_STATUS_FULL;
+ port->charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ break;
+ }
+
+ port->battery_percentage = rsp.battery_percentage;
+
+ if (port->psy_status != old_status ||
+ port->battery_percentage != old_percentage)
+ power_supply_changed(port->psy);
+
+ dev_dbg(dev,
+ "Port %d: state=%d battery=%d%%\n",
+ port->port_number, rsp.state, rsp.battery_percentage);
+
+ return 0;
+}
+
+static int cros_pchg_get_port_status(struct port_data *port, bool ratelimit)
+{
+ int ret;
+
+ if (ratelimit &&
+ time_is_after_jiffies(port->last_update + PCHG_CACHE_UPDATE_DELAY))
+ return 0;
+
+ ret = cros_pchg_get_status(port);
+ if (ret < 0)
+ return ret;
+
+ port->last_update = jiffies;
+
+ return ret;
+}
+
+static int cros_pchg_get_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct port_data *port = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ case POWER_SUPPLY_PROP_CAPACITY:
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ cros_pchg_get_port_status(port, true);
+ break;
+ default:
+ break;
+ }
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = port->psy_status;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = port->battery_percentage;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ val->intval = port->charge_type;
+ break;
+ case POWER_SUPPLY_PROP_SCOPE:
+ val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cros_pchg_event(const struct charger_data *charger,
+ unsigned long host_event)
+{
+ int i;
+
+ for (i = 0; i < charger->num_registered_psy; i++)
+ cros_pchg_get_port_status(charger->ports[i], false);
+
+ return NOTIFY_OK;
+}
+
+static u32 cros_get_device_event(const struct charger_data *charger)
+{
+ struct ec_params_device_event req;
+ struct ec_response_device_event rsp;
+ struct device *dev = charger->dev;
+ int ret;
+
+ req.param = EC_DEVICE_EVENT_PARAM_GET_CURRENT_EVENTS;
+ ret = cros_pchg_ec_command(charger, 0, EC_CMD_DEVICE_EVENT,
+ &req, sizeof(req), &rsp, sizeof(rsp));
+ if (ret < 0) {
+ dev_warn(dev, "Unable to get device events (err:%d)\n", ret);
+ return 0;
+ }
+
+ return rsp.event_mask;
+}
+
+static int cros_ec_notify(struct notifier_block *nb,
+ unsigned long queued_during_suspend,
+ void *data)
+{
+ struct cros_ec_device *ec_dev = (struct cros_ec_device *)data;
+ u32 host_event = cros_ec_get_host_event(ec_dev);
+ struct charger_data *charger =
+ container_of(nb, struct charger_data, notifier);
+ u32 device_event_mask;
+
+ if (!host_event)
+ return NOTIFY_DONE;
+
+ if (!(host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_DEVICE)))
+ return NOTIFY_DONE;
+
+ /*
+ * todo: Retrieve device event mask in common place
+ * (e.g. cros_ec_proto.c).
+ */
+ device_event_mask = cros_get_device_event(charger);
+ if (!(device_event_mask & EC_DEVICE_EVENT_MASK(EC_DEVICE_EVENT_WLC)))
+ return NOTIFY_DONE;
+
+ return cros_pchg_event(charger, host_event);
+}
+
+static int cros_pchg_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
+ struct cros_ec_device *ec_device = ec_dev->ec_dev;
+ struct power_supply_desc *psy_desc;
+ struct charger_data *charger;
+ struct power_supply *psy;
+ struct port_data *port;
+ struct notifier_block *nb;
+ int num_ports;
+ int ret;
+ int i;
+
+ charger = devm_kzalloc(dev, sizeof(*charger), GFP_KERNEL);
+ if (!charger)
+ return -ENOMEM;
+
+ charger->dev = dev;
+ charger->ec_dev = ec_dev;
+ charger->ec_device = ec_device;
+
+ ret = cros_pchg_port_count(charger);
+ if (ret <= 0) {
+ /*
+ * This feature is enabled by the EC and the kernel driver is
+ * included by default for CrOS devices. Don't need to be loud
+ * since this error can be normal.
+ */
+ dev_info(dev, "No peripheral charge ports (err:%d)\n", ret);
+ return -ENODEV;
+ }
+
+ if (!cros_pchg_cmd_ver_check(charger)) {
+ dev_err(dev, "EC_CMD_PCHG version %d isn't available.\n",
+ pchg_cmd_version);
+ return -EOPNOTSUPP;
+ }
+
+ num_ports = ret;
+ if (num_ports > EC_PCHG_MAX_PORTS) {
+ dev_err(dev, "Too many peripheral charge ports (%d)\n",
+ num_ports);
+ return -ENOBUFS;
+ }
+
+ dev_info(dev, "%d peripheral charge ports found\n", num_ports);
+
+ for (i = 0; i < num_ports; i++) {
+ struct power_supply_config psy_cfg = {};
+
+ port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->charger = charger;
+ port->port_number = i;
+ snprintf(port->name, sizeof(port->name), PCHG_DIR_NAME, i);
+
+ psy_desc = &port->psy_desc;
+ psy_desc->name = port->name;
+ psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
+ psy_desc->get_property = cros_pchg_get_prop;
+ psy_desc->external_power_changed = NULL;
+ psy_desc->properties = cros_pchg_props;
+ psy_desc->num_properties = ARRAY_SIZE(cros_pchg_props);
+ psy_cfg.drv_data = port;
+
+ psy = devm_power_supply_register(dev, psy_desc, &psy_cfg);
+ if (IS_ERR(psy))
+ return dev_err_probe(dev, PTR_ERR(psy),
+ "Failed to register power supply\n");
+ port->psy = psy;
+
+ charger->ports[charger->num_registered_psy++] = port;
+ }
+
+ if (!charger->num_registered_psy)
+ return -ENODEV;
+
+ nb = &charger->notifier;
+ nb->notifier_call = cros_ec_notify;
+ ret = blocking_notifier_chain_register(&ec_dev->ec_dev->event_notifier,
+ nb);
+ if (ret < 0)
+ dev_err(dev, "Failed to register notifier (err:%d)\n", ret);
+
+ return 0;
+}
+
+static struct platform_driver cros_pchg_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = cros_pchg_probe
+};
+
+module_platform_driver(cros_pchg_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ChromeOS EC peripheral device charger");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/power/supply/cw2015_battery.c b/drivers/power/supply/cw2015_battery.c
index d110597746b0..091868e9e9e8 100644
--- a/drivers/power/supply/cw2015_battery.c
+++ b/drivers/power/supply/cw2015_battery.c
@@ -679,7 +679,9 @@ static int cw_bat_probe(struct i2c_client *client)
&cw2015_bat_desc,
&psy_cfg);
if (IS_ERR(cw_bat->rk_bat)) {
- dev_err(cw_bat->dev, "Failed to register power supply\n");
+ /* try again if this happens */
+ dev_err_probe(&client->dev, PTR_ERR(cw_bat->rk_bat),
+ "Failed to register power supply\n");
return PTR_ERR(cw_bat->rk_bat);
}
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..8dffae76b6a3 100644
--- a/drivers/power/supply/max17042_battery.c
+++ b/drivers/power/supply/max17042_battery.c
@@ -36,8 +36,6 @@
/* Interrupt mask bits */
#define CONFIG_ALRT_BIT_ENBL (1 << 2)
-#define STATUS_INTR_SOCMIN_BIT (1 << 10)
-#define STATUS_INTR_SOCMAX_BIT (1 << 14)
#define VFSOC0_LOCK 0x0000
#define VFSOC0_UNLOCK 0x0080
@@ -285,8 +283,6 @@ static int max17042_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
ret = regmap_read(map, MAX17042_V_empty, &data);
- else if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)
- ret = regmap_read(map, MAX17055_V_empty, &data);
else
ret = regmap_read(map, MAX17047_V_empty, &data);
if (ret < 0)
@@ -748,7 +744,7 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)
struct max17042_config_data *config = chip->pdata->config_data;
max17042_override_por(map, MAX17042_TGAIN, config->tgain);
- max17042_override_por(map, MAx17042_TOFF, config->toff);
+ max17042_override_por(map, MAX17042_TOFF, config->toff);
max17042_override_por(map, MAX17042_CGAIN, config->cgain);
max17042_override_por(map, MAX17042_COFF, config->coff);
@@ -767,36 +763,36 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)
max17042_override_por(map, MAX17042_FilterCFG, config->filter_cfg);
max17042_override_por(map, MAX17042_RelaxCFG, config->relax_cfg);
max17042_override_por(map, MAX17042_MiscCFG, config->misc_cfg);
- max17042_override_por(map, MAX17042_MaskSOC, config->masksoc);
max17042_override_por(map, MAX17042_FullCAP, config->fullcap);
max17042_override_por(map, MAX17042_FullCAPNom, config->fullcapnom);
- if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
- max17042_override_por(map, MAX17042_SOC_empty,
- config->socempty);
- max17042_override_por(map, MAX17042_LAvg_empty, config->lavg_empty);
max17042_override_por(map, MAX17042_dQacc, config->dqacc);
max17042_override_por(map, MAX17042_dPacc, config->dpacc);
- if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
- max17042_override_por(map, MAX17042_V_empty, config->vempty);
- if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)
- max17042_override_por(map, MAX17055_V_empty, config->vempty);
- else
- max17042_override_por(map, MAX17047_V_empty, config->vempty);
- max17042_override_por(map, MAX17042_TempNom, config->temp_nom);
- max17042_override_por(map, MAX17042_TempLim, config->temp_lim);
- max17042_override_por(map, MAX17042_FCTC, config->fctc);
max17042_override_por(map, MAX17042_RCOMP0, config->rcomp0);
max17042_override_por(map, MAX17042_TempCo, config->tcompc0);
- if (chip->chip_type &&
- ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) ||
+
+ if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) {
+ max17042_override_por(map, MAX17042_MaskSOC, config->masksoc);
+ max17042_override_por(map, MAX17042_SOC_empty, config->socempty);
+ max17042_override_por(map, MAX17042_V_empty, config->vempty);
+ max17042_override_por(map, MAX17042_EmptyTempCo, config->empty_tempco);
+ max17042_override_por(map, MAX17042_K_empty0, config->kempty0);
+ }
+
+ if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) ||
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) ||
- (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050))) {
- max17042_override_por(map, MAX17042_EmptyTempCo,
- config->empty_tempco);
- max17042_override_por(map, MAX17042_K_empty0,
- config->kempty0);
+ (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050)) {
+ max17042_override_por(map, MAX17042_LAvg_empty, config->lavg_empty);
+ max17042_override_por(map, MAX17042_TempNom, config->temp_nom);
+ max17042_override_por(map, MAX17042_TempLim, config->temp_lim);
+ max17042_override_por(map, MAX17042_FCTC, config->fctc);
+ }
+
+ if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) ||
+ (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050) ||
+ (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)) {
+ max17042_override_por(map, MAX17047_V_empty, config->vempty);
}
}
@@ -869,11 +865,14 @@ static irqreturn_t max17042_thread_handler(int id, void *dev)
{
struct max17042_chip *chip = dev;
u32 val;
+ int ret;
- regmap_read(chip->regmap, MAX17042_STATUS, &val);
- if ((val & STATUS_INTR_SOCMIN_BIT) ||
- (val & STATUS_INTR_SOCMAX_BIT)) {
- dev_info(&chip->client->dev, "SOC threshold INTR\n");
+ ret = regmap_read(chip->regmap, MAX17042_STATUS, &val);
+ if (ret)
+ return IRQ_HANDLED;
+
+ if ((val & STATUS_SMN_BIT) || (val & STATUS_SMX_BIT)) {
+ dev_dbg(&chip->client->dev, "SOC threshold INTR\n");
max17042_set_soc_threshold(chip, 1);
}
@@ -1104,7 +1103,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,
@@ -1196,6 +1195,7 @@ static const struct of_device_id max17042_dt_match[] = {
{ .compatible = "maxim,max17047" },
{ .compatible = "maxim,max17050" },
{ .compatible = "maxim,max17055" },
+ { .compatible = "maxim,max77849-battery" },
{ },
};
MODULE_DEVICE_TABLE(of, max17042_dt_match);
@@ -1206,6 +1206,7 @@ static const struct i2c_device_id max17042_id[] = {
{ "max17047", MAXIM_DEVICE_TYPE_MAX17047 },
{ "max17050", MAXIM_DEVICE_TYPE_MAX17050 },
{ "max17055", MAXIM_DEVICE_TYPE_MAX17055 },
+ { "max77849-battery", MAXIM_DEVICE_TYPE_MAX17047 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max17042_id);
diff --git a/drivers/power/supply/mt6360_charger.c b/drivers/power/supply/mt6360_charger.c
new file mode 100644
index 000000000000..3abaa72e0668
--- /dev/null
+++ b/drivers/power/supply/mt6360_charger.c
@@ -0,0 +1,867 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#include <linux/devm-helpers.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/linear_range.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+
+#define MT6360_PMU_CHG_CTRL1 0x311
+#define MT6360_PMU_CHG_CTRL2 0x312
+#define MT6360_PMU_CHG_CTRL3 0x313
+#define MT6360_PMU_CHG_CTRL4 0x314
+#define MT6360_PMU_CHG_CTRL5 0x315
+#define MT6360_PMU_CHG_CTRL6 0x316
+#define MT6360_PMU_CHG_CTRL7 0x317
+#define MT6360_PMU_CHG_CTRL8 0x318
+#define MT6360_PMU_CHG_CTRL9 0x319
+#define MT6360_PMU_CHG_CTRL10 0x31A
+#define MT6360_PMU_DEVICE_TYPE 0x322
+#define MT6360_PMU_USB_STATUS1 0x327
+#define MT6360_PMU_CHG_STAT 0x34A
+#define MT6360_PMU_CHG_CTRL19 0x361
+#define MT6360_PMU_FOD_STAT 0x3E7
+
+/* MT6360_PMU_CHG_CTRL1 */
+#define MT6360_FSLP_SHFT (3)
+#define MT6360_FSLP_MASK BIT(MT6360_FSLP_SHFT)
+#define MT6360_OPA_MODE_SHFT (0)
+#define MT6360_OPA_MODE_MASK BIT(MT6360_OPA_MODE_SHFT)
+/* MT6360_PMU_CHG_CTRL2 */
+#define MT6360_IINLMTSEL_SHFT (2)
+#define MT6360_IINLMTSEL_MASK GENMASK(3, 2)
+/* MT6360_PMU_CHG_CTRL3 */
+#define MT6360_IAICR_SHFT (2)
+#define MT6360_IAICR_MASK GENMASK(7, 2)
+#define MT6360_ILIM_EN_MASK BIT(0)
+/* MT6360_PMU_CHG_CTRL4 */
+#define MT6360_VOREG_SHFT (1)
+#define MT6360_VOREG_MASK GENMASK(7, 1)
+/* MT6360_PMU_CHG_CTRL5 */
+#define MT6360_VOBST_MASK GENMASK(7, 2)
+/* MT6360_PMU_CHG_CTRL6 */
+#define MT6360_VMIVR_SHFT (1)
+#define MT6360_VMIVR_MASK GENMASK(7, 1)
+/* MT6360_PMU_CHG_CTRL7 */
+#define MT6360_ICHG_SHFT (2)
+#define MT6360_ICHG_MASK GENMASK(7, 2)
+/* MT6360_PMU_CHG_CTRL8 */
+#define MT6360_IPREC_SHFT (0)
+#define MT6360_IPREC_MASK GENMASK(3, 0)
+/* MT6360_PMU_CHG_CTRL9 */
+#define MT6360_IEOC_SHFT (4)
+#define MT6360_IEOC_MASK GENMASK(7, 4)
+/* MT6360_PMU_CHG_CTRL10 */
+#define MT6360_OTG_OC_MASK GENMASK(3, 0)
+/* MT6360_PMU_DEVICE_TYPE */
+#define MT6360_USBCHGEN_MASK BIT(7)
+/* MT6360_PMU_USB_STATUS1 */
+#define MT6360_USB_STATUS_SHFT (4)
+#define MT6360_USB_STATUS_MASK GENMASK(6, 4)
+/* MT6360_PMU_CHG_STAT */
+#define MT6360_CHG_STAT_SHFT (6)
+#define MT6360_CHG_STAT_MASK GENMASK(7, 6)
+#define MT6360_VBAT_LVL_MASK BIT(5)
+/* MT6360_PMU_CHG_CTRL19 */
+#define MT6360_VINOVP_SHFT (5)
+#define MT6360_VINOVP_MASK GENMASK(6, 5)
+/* MT6360_PMU_FOD_STAT */
+#define MT6360_CHRDET_EXT_MASK BIT(4)
+
+/* uV */
+#define MT6360_VMIVR_MIN 3900000
+#define MT6360_VMIVR_MAX 13400000
+#define MT6360_VMIVR_STEP 100000
+/* uA */
+#define MT6360_ICHG_MIN 100000
+#define MT6360_ICHG_MAX 5000000
+#define MT6360_ICHG_STEP 100000
+/* uV */
+#define MT6360_VOREG_MIN 3900000
+#define MT6360_VOREG_MAX 4710000
+#define MT6360_VOREG_STEP 10000
+/* uA */
+#define MT6360_AICR_MIN 100000
+#define MT6360_AICR_MAX 3250000
+#define MT6360_AICR_STEP 50000
+/* uA */
+#define MT6360_IPREC_MIN 100000
+#define MT6360_IPREC_MAX 850000
+#define MT6360_IPREC_STEP 50000
+/* uA */
+#define MT6360_IEOC_MIN 100000
+#define MT6360_IEOC_MAX 850000
+#define MT6360_IEOC_STEP 50000
+
+enum {
+ MT6360_RANGE_VMIVR,
+ MT6360_RANGE_ICHG,
+ MT6360_RANGE_VOREG,
+ MT6360_RANGE_AICR,
+ MT6360_RANGE_IPREC,
+ MT6360_RANGE_IEOC,
+ MT6360_RANGE_MAX,
+};
+
+#define MT6360_LINEAR_RANGE(idx, _min, _min_sel, _max_sel, _step) \
+ [idx] = REGULATOR_LINEAR_RANGE(_min, _min_sel, _max_sel, _step)
+
+static const struct linear_range mt6360_chg_range[MT6360_RANGE_MAX] = {
+ MT6360_LINEAR_RANGE(MT6360_RANGE_VMIVR, 3900000, 0, 0x5F, 100000),
+ MT6360_LINEAR_RANGE(MT6360_RANGE_ICHG, 100000, 0, 0x31, 100000),
+ MT6360_LINEAR_RANGE(MT6360_RANGE_VOREG, 3900000, 0, 0x51, 10000),
+ MT6360_LINEAR_RANGE(MT6360_RANGE_AICR, 100000, 0, 0x3F, 50000),
+ MT6360_LINEAR_RANGE(MT6360_RANGE_IPREC, 100000, 0, 0x0F, 50000),
+ MT6360_LINEAR_RANGE(MT6360_RANGE_IEOC, 100000, 0, 0x0F, 50000),
+};
+
+struct mt6360_chg_info {
+ struct device *dev;
+ struct regmap *regmap;
+ struct power_supply_desc psy_desc;
+ struct power_supply *psy;
+ struct regulator_dev *otg_rdev;
+ struct mutex chgdet_lock;
+ u32 vinovp;
+ bool pwr_rdy;
+ bool bc12_en;
+ int psy_usb_type;
+ struct work_struct chrdet_work;
+};
+
+enum mt6360_iinlmtsel {
+ MT6360_IINLMTSEL_AICR_3250 = 0,
+ MT6360_IINLMTSEL_CHG_TYPE,
+ MT6360_IINLMTSEL_AICR,
+ MT6360_IINLMTSEL_LOWER_LEVEL,
+};
+
+enum mt6360_pmu_chg_type {
+ MT6360_CHG_TYPE_NOVBUS = 0,
+ MT6360_CHG_TYPE_UNDER_GOING,
+ MT6360_CHG_TYPE_SDP,
+ MT6360_CHG_TYPE_SDPNSTD,
+ MT6360_CHG_TYPE_DCP,
+ MT6360_CHG_TYPE_CDP,
+ MT6360_CHG_TYPE_DISABLE_BC12,
+ MT6360_CHG_TYPE_MAX,
+};
+
+static enum power_supply_usb_type mt6360_charger_usb_types[] = {
+ POWER_SUPPLY_USB_TYPE_UNKNOWN,
+ POWER_SUPPLY_USB_TYPE_SDP,
+ POWER_SUPPLY_USB_TYPE_DCP,
+ POWER_SUPPLY_USB_TYPE_CDP,
+};
+
+static int mt6360_get_chrdet_ext_stat(struct mt6360_chg_info *mci,
+ bool *pwr_rdy)
+{
+ int ret;
+ unsigned int regval;
+
+ ret = regmap_read(mci->regmap, MT6360_PMU_FOD_STAT, &regval);
+ if (ret < 0)
+ return ret;
+ *pwr_rdy = (regval & MT6360_CHRDET_EXT_MASK) ? true : false;
+ return 0;
+}
+
+static int mt6360_charger_get_online(struct mt6360_chg_info *mci,
+ union power_supply_propval *val)
+{
+ int ret;
+ bool pwr_rdy;
+
+ ret = mt6360_get_chrdet_ext_stat(mci, &pwr_rdy);
+ if (ret < 0)
+ return ret;
+ val->intval = pwr_rdy ? true : false;
+ return 0;
+}
+
+static int mt6360_charger_get_status(struct mt6360_chg_info *mci,
+ union power_supply_propval *val)
+{
+ int status, ret;
+ unsigned int regval;
+ bool pwr_rdy;
+
+ ret = mt6360_get_chrdet_ext_stat(mci, &pwr_rdy);
+ if (ret < 0)
+ return ret;
+ if (!pwr_rdy) {
+ status = POWER_SUPPLY_STATUS_DISCHARGING;
+ goto out;
+ }
+
+ ret = regmap_read(mci->regmap, MT6360_PMU_CHG_STAT, &regval);
+ if (ret < 0)
+ return ret;
+ regval &= MT6360_CHG_STAT_MASK;
+ regval >>= MT6360_CHG_STAT_SHFT;
+ switch (regval) {
+ case 0x0:
+ status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ case 0x1:
+ status = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ case 0x2:
+ status = POWER_SUPPLY_STATUS_FULL;
+ break;
+ default:
+ ret = -EIO;
+ }
+out:
+ if (!ret)
+ val->intval = status;
+ return ret;
+}
+
+static int mt6360_charger_get_charge_type(struct mt6360_chg_info *mci,
+ union power_supply_propval *val)
+{
+ int type, ret;
+ unsigned int regval;
+ u8 chg_stat;
+
+ ret = regmap_read(mci->regmap, MT6360_PMU_CHG_STAT, &regval);
+ if (ret < 0)
+ return ret;
+
+ chg_stat = (regval & MT6360_CHG_STAT_MASK) >> MT6360_CHG_STAT_SHFT;
+ switch (chg_stat) {
+ case 0x01: /* Charge in Progress */
+ if (regval & MT6360_VBAT_LVL_MASK)
+ type = POWER_SUPPLY_CHARGE_TYPE_FAST;
+ else
+ type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+ break;
+ case 0x00: /* Not Charging */
+ case 0x02: /* Charge Done */
+ case 0x03: /* Charge Fault */
+ default:
+ type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ break;
+ }
+
+ val->intval = type;
+ return 0;
+}
+
+static int mt6360_charger_get_ichg(struct mt6360_chg_info *mci,
+ union power_supply_propval *val)
+{
+ int ret;
+ u32 sel, value;
+
+ ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL7, &sel);
+ if (ret < 0)
+ return ret;
+ sel = (sel & MT6360_ICHG_MASK) >> MT6360_ICHG_SHFT;
+ ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_ICHG], sel, &value);
+ if (!ret)
+ val->intval = value;
+ return ret;
+}
+
+static int mt6360_charger_get_max_ichg(struct mt6360_chg_info *mci,
+ union power_supply_propval *val)
+{
+ val->intval = MT6360_ICHG_MAX;
+ return 0;
+}
+
+static int mt6360_charger_get_cv(struct mt6360_chg_info *mci,
+ union power_supply_propval *val)
+{
+ int ret;
+ u32 sel, value;
+
+ ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL4, &sel);
+ if (ret < 0)
+ return ret;
+ sel = (sel & MT6360_VOREG_MASK) >> MT6360_VOREG_SHFT;
+ ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_VOREG], sel, &value);
+ if (!ret)
+ val->intval = value;
+ return ret;
+}
+
+static int mt6360_charger_get_max_cv(struct mt6360_chg_info *mci,
+ union power_supply_propval *val)
+{
+ val->intval = MT6360_VOREG_MAX;
+ return 0;
+}
+
+static int mt6360_charger_get_aicr(struct mt6360_chg_info *mci,
+ union power_supply_propval *val)
+{
+ int ret;
+ u32 sel, value;
+
+ ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL3, &sel);
+ if (ret < 0)
+ return ret;
+ sel = (sel & MT6360_IAICR_MASK) >> MT6360_IAICR_SHFT;
+ ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_AICR], sel, &value);
+ if (!ret)
+ val->intval = value;
+ return ret;
+}
+
+static int mt6360_charger_get_mivr(struct mt6360_chg_info *mci,
+ union power_supply_propval *val)
+{
+ int ret;
+ u32 sel, value;
+
+ ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL6, &sel);
+ if (ret < 0)
+ return ret;
+ sel = (sel & MT6360_VMIVR_MASK) >> MT6360_VMIVR_SHFT;
+ ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_VMIVR], sel, &value);
+ if (!ret)
+ val->intval = value;
+ return ret;
+}
+
+static int mt6360_charger_get_iprechg(struct mt6360_chg_info *mci,
+ union power_supply_propval *val)
+{
+ int ret;
+ u32 sel, value;
+
+ ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL8, &sel);
+ if (ret < 0)
+ return ret;
+ sel = (sel & MT6360_IPREC_MASK) >> MT6360_IPREC_SHFT;
+ ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_IPREC], sel, &value);
+ if (!ret)
+ val->intval = value;
+ return ret;
+}
+
+static int mt6360_charger_get_ieoc(struct mt6360_chg_info *mci,
+ union power_supply_propval *val)
+{
+ int ret;
+ u32 sel, value;
+
+ ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL9, &sel);
+ if (ret < 0)
+ return ret;
+ sel = (sel & MT6360_IEOC_MASK) >> MT6360_IEOC_SHFT;
+ ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_IEOC], sel, &value);
+ if (!ret)
+ val->intval = value;
+ return ret;
+}
+
+static int mt6360_charger_set_online(struct mt6360_chg_info *mci,
+ const union power_supply_propval *val)
+{
+ u8 force_sleep = val->intval ? 0 : 1;
+
+ return regmap_update_bits(mci->regmap,
+ MT6360_PMU_CHG_CTRL1,
+ MT6360_FSLP_MASK,
+ force_sleep << MT6360_FSLP_SHFT);
+}
+
+static int mt6360_charger_set_ichg(struct mt6360_chg_info *mci,
+ const union power_supply_propval *val)
+{
+ u32 sel;
+
+ linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_ICHG], val->intval, &sel);
+ return regmap_update_bits(mci->regmap,
+ MT6360_PMU_CHG_CTRL7,
+ MT6360_ICHG_MASK,
+ sel << MT6360_ICHG_SHFT);
+}
+
+static int mt6360_charger_set_cv(struct mt6360_chg_info *mci,
+ const union power_supply_propval *val)
+{
+ u32 sel;
+
+ linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_VOREG], val->intval, &sel);
+ return regmap_update_bits(mci->regmap,
+ MT6360_PMU_CHG_CTRL4,
+ MT6360_VOREG_MASK,
+ sel << MT6360_VOREG_SHFT);
+}
+
+static int mt6360_charger_set_aicr(struct mt6360_chg_info *mci,
+ const union power_supply_propval *val)
+{
+ u32 sel;
+
+ linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_AICR], val->intval, &sel);
+ return regmap_update_bits(mci->regmap,
+ MT6360_PMU_CHG_CTRL3,
+ MT6360_IAICR_MASK,
+ sel << MT6360_IAICR_SHFT);
+}
+
+static int mt6360_charger_set_mivr(struct mt6360_chg_info *mci,
+ const union power_supply_propval *val)
+{
+ u32 sel;
+
+ linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_VMIVR], val->intval, &sel);
+ return regmap_update_bits(mci->regmap,
+ MT6360_PMU_CHG_CTRL3,
+ MT6360_VMIVR_MASK,
+ sel << MT6360_VMIVR_SHFT);
+}
+
+static int mt6360_charger_set_iprechg(struct mt6360_chg_info *mci,
+ const union power_supply_propval *val)
+{
+ u32 sel;
+
+ linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_IPREC], val->intval, &sel);
+ return regmap_update_bits(mci->regmap,
+ MT6360_PMU_CHG_CTRL8,
+ MT6360_IPREC_MASK,
+ sel << MT6360_IPREC_SHFT);
+}
+
+static int mt6360_charger_set_ieoc(struct mt6360_chg_info *mci,
+ const union power_supply_propval *val)
+{
+ u32 sel;
+
+ linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_IEOC], val->intval, &sel);
+ return regmap_update_bits(mci->regmap,
+ MT6360_PMU_CHG_CTRL9,
+ MT6360_IEOC_MASK,
+ sel << MT6360_IEOC_SHFT);
+}
+
+static int mt6360_charger_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct mt6360_chg_info *mci = power_supply_get_drvdata(psy);
+ int ret = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ ret = mt6360_charger_get_online(mci, val);
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ ret = mt6360_charger_get_status(mci, val);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ ret = mt6360_charger_get_charge_type(mci, val);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ ret = mt6360_charger_get_ichg(mci, val);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ ret = mt6360_charger_get_max_ichg(mci, val);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ ret = mt6360_charger_get_cv(mci, val);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+ ret = mt6360_charger_get_max_cv(mci, val);
+ break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ ret = mt6360_charger_get_aicr(mci, val);
+ break;
+ case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
+ ret = mt6360_charger_get_mivr(mci, val);
+ break;
+ case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
+ ret = mt6360_charger_get_iprechg(mci, val);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+ ret = mt6360_charger_get_ieoc(mci, val);
+ break;
+ case POWER_SUPPLY_PROP_USB_TYPE:
+ val->intval = mci->psy_usb_type;
+ break;
+ default:
+ ret = -ENODATA;
+ }
+ return ret;
+}
+
+static int mt6360_charger_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct mt6360_chg_info *mci = power_supply_get_drvdata(psy);
+ int ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ ret = mt6360_charger_set_online(mci, val);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ ret = mt6360_charger_set_ichg(mci, val);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ ret = mt6360_charger_set_cv(mci, val);
+ break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ ret = mt6360_charger_set_aicr(mci, val);
+ break;
+ case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
+ ret = mt6360_charger_set_mivr(mci, val);
+ break;
+ case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
+ ret = mt6360_charger_set_iprechg(mci, val);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+ ret = mt6360_charger_set_ieoc(mci, val);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int mt6360_charger_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
+ case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
+ case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static enum power_supply_property mt6360_charger_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
+ POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
+ POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
+ POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
+ POWER_SUPPLY_PROP_USB_TYPE,
+};
+
+static const struct power_supply_desc mt6360_charger_desc = {
+ .type = POWER_SUPPLY_TYPE_USB,
+ .properties = mt6360_charger_properties,
+ .num_properties = ARRAY_SIZE(mt6360_charger_properties),
+ .get_property = mt6360_charger_get_property,
+ .set_property = mt6360_charger_set_property,
+ .property_is_writeable = mt6360_charger_property_is_writeable,
+ .usb_types = mt6360_charger_usb_types,
+ .num_usb_types = ARRAY_SIZE(mt6360_charger_usb_types),
+};
+
+static const struct regulator_ops mt6360_chg_otg_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static const struct regulator_desc mt6360_otg_rdesc = {
+ .of_match = "usb-otg-vbus",
+ .name = "usb-otg-vbus",
+ .ops = &mt6360_chg_otg_ops,
+ .owner = THIS_MODULE,
+ .type = REGULATOR_VOLTAGE,
+ .min_uV = 4425000,
+ .uV_step = 25000,
+ .n_voltages = 57,
+ .vsel_reg = MT6360_PMU_CHG_CTRL5,
+ .vsel_mask = MT6360_VOBST_MASK,
+ .enable_reg = MT6360_PMU_CHG_CTRL1,
+ .enable_mask = MT6360_OPA_MODE_MASK,
+};
+
+static irqreturn_t mt6360_pmu_attach_i_handler(int irq, void *data)
+{
+ struct mt6360_chg_info *mci = data;
+ int ret;
+ unsigned int usb_status;
+ int last_usb_type;
+
+ mutex_lock(&mci->chgdet_lock);
+ if (!mci->bc12_en) {
+ dev_warn(mci->dev, "Received attach interrupt, bc12 disabled, ignore irq\n");
+ goto out;
+ }
+ last_usb_type = mci->psy_usb_type;
+ /* Plug in */
+ ret = regmap_read(mci->regmap, MT6360_PMU_USB_STATUS1, &usb_status);
+ if (ret < 0)
+ goto out;
+ usb_status &= MT6360_USB_STATUS_MASK;
+ usb_status >>= MT6360_USB_STATUS_SHFT;
+ switch (usb_status) {
+ case MT6360_CHG_TYPE_NOVBUS:
+ dev_dbg(mci->dev, "Received attach interrupt, no vbus\n");
+ goto out;
+ case MT6360_CHG_TYPE_UNDER_GOING:
+ dev_dbg(mci->dev, "Received attach interrupt, under going...\n");
+ goto out;
+ case MT6360_CHG_TYPE_SDP:
+ mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP;
+ break;
+ case MT6360_CHG_TYPE_SDPNSTD:
+ mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP;
+ break;
+ case MT6360_CHG_TYPE_CDP:
+ mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_CDP;
+ break;
+ case MT6360_CHG_TYPE_DCP:
+ mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_DCP;
+ break;
+ case MT6360_CHG_TYPE_DISABLE_BC12:
+ dev_dbg(mci->dev, "Received attach interrupt, bc12 detect not enable\n");
+ goto out;
+ default:
+ mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
+ dev_dbg(mci->dev, "Received attach interrupt, reserved address\n");
+ goto out;
+ }
+
+ dev_dbg(mci->dev, "Received attach interrupt, chg_type = %d\n", mci->psy_usb_type);
+ if (last_usb_type != mci->psy_usb_type)
+ power_supply_changed(mci->psy);
+out:
+ mutex_unlock(&mci->chgdet_lock);
+ return IRQ_HANDLED;
+}
+
+static void mt6360_handle_chrdet_ext_evt(struct mt6360_chg_info *mci)
+{
+ int ret;
+ bool pwr_rdy;
+
+ mutex_lock(&mci->chgdet_lock);
+ ret = mt6360_get_chrdet_ext_stat(mci, &pwr_rdy);
+ if (ret < 0)
+ goto out;
+ if (mci->pwr_rdy == pwr_rdy) {
+ dev_dbg(mci->dev, "Received vbus interrupt, pwr_rdy is same(%d)\n", pwr_rdy);
+ goto out;
+ }
+ mci->pwr_rdy = pwr_rdy;
+ dev_dbg(mci->dev, "Received vbus interrupt, pwr_rdy = %d\n", pwr_rdy);
+ if (!pwr_rdy) {
+ mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
+ power_supply_changed(mci->psy);
+
+ }
+ ret = regmap_update_bits(mci->regmap,
+ MT6360_PMU_DEVICE_TYPE,
+ MT6360_USBCHGEN_MASK,
+ pwr_rdy ? MT6360_USBCHGEN_MASK : 0);
+ if (ret < 0)
+ goto out;
+ mci->bc12_en = pwr_rdy;
+out:
+ mutex_unlock(&mci->chgdet_lock);
+}
+
+static void mt6360_chrdet_work(struct work_struct *work)
+{
+ struct mt6360_chg_info *mci = (struct mt6360_chg_info *)container_of(
+ work, struct mt6360_chg_info, chrdet_work);
+
+ mt6360_handle_chrdet_ext_evt(mci);
+}
+
+static irqreturn_t mt6360_pmu_chrdet_ext_evt_handler(int irq, void *data)
+{
+ struct mt6360_chg_info *mci = data;
+
+ mt6360_handle_chrdet_ext_evt(mci);
+ return IRQ_HANDLED;
+}
+
+static int mt6360_chg_irq_register(struct platform_device *pdev)
+{
+ const struct {
+ const char *name;
+ irq_handler_t handler;
+ } irq_descs[] = {
+ { "attach_i", mt6360_pmu_attach_i_handler },
+ { "chrdet_ext_evt", mt6360_pmu_chrdet_ext_evt_handler }
+ };
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(irq_descs); i++) {
+ ret = platform_get_irq_byname(pdev, irq_descs[i].name);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_request_threaded_irq(&pdev->dev, ret, NULL,
+ irq_descs[i].handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ irq_descs[i].name,
+ platform_get_drvdata(pdev));
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "Failed to request %s irq\n",
+ irq_descs[i].name);
+ }
+
+ return 0;
+}
+
+static u32 mt6360_vinovp_trans_to_sel(u32 val)
+{
+ u32 vinovp_tbl[] = { 5500000, 6500000, 11000000, 14500000 };
+ int i;
+
+ /* Select the smaller and equal supported value */
+ for (i = 0; i < ARRAY_SIZE(vinovp_tbl)-1; i++) {
+ if (val < vinovp_tbl[i+1])
+ break;
+ }
+ return i;
+}
+
+static int mt6360_chg_init_setting(struct mt6360_chg_info *mci)
+{
+ int ret;
+ u32 sel;
+
+ sel = mt6360_vinovp_trans_to_sel(mci->vinovp);
+ ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL19,
+ MT6360_VINOVP_MASK, sel << MT6360_VINOVP_SHFT);
+ if (ret)
+ return dev_err_probe(mci->dev, ret, "%s: Failed to apply vinovp\n", __func__);
+ ret = regmap_update_bits(mci->regmap, MT6360_PMU_DEVICE_TYPE,
+ MT6360_USBCHGEN_MASK, 0);
+ if (ret)
+ return dev_err_probe(mci->dev, ret, "%s: Failed to disable bc12\n", __func__);
+ ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL2,
+ MT6360_IINLMTSEL_MASK,
+ MT6360_IINLMTSEL_AICR <<
+ MT6360_IINLMTSEL_SHFT);
+ if (ret)
+ return dev_err_probe(mci->dev, ret,
+ "%s: Failed to switch iinlmtsel to aicr\n", __func__);
+ usleep_range(5000, 6000);
+ ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL3,
+ MT6360_ILIM_EN_MASK, 0);
+ if (ret)
+ return dev_err_probe(mci->dev, ret,
+ "%s: Failed to disable ilim\n", __func__);
+ ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL10,
+ MT6360_OTG_OC_MASK, MT6360_OTG_OC_MASK);
+ if (ret)
+ return dev_err_probe(mci->dev, ret,
+ "%s: Failed to config otg oc to 3A\n", __func__);
+ return 0;
+}
+
+static int mt6360_charger_probe(struct platform_device *pdev)
+{
+ struct mt6360_chg_info *mci;
+ struct power_supply_config charger_cfg = {};
+ struct regulator_config config = { };
+ int ret;
+
+ mci = devm_kzalloc(&pdev->dev, sizeof(*mci), GFP_KERNEL);
+ if (!mci)
+ return -ENOMEM;
+
+ mci->dev = &pdev->dev;
+ mci->vinovp = 6500000;
+ mutex_init(&mci->chgdet_lock);
+ platform_set_drvdata(pdev, mci);
+ devm_work_autocancel(&pdev->dev, &mci->chrdet_work, mt6360_chrdet_work);
+
+ ret = device_property_read_u32(&pdev->dev, "richtek,vinovp-microvolt", &mci->vinovp);
+ if (ret)
+ dev_warn(&pdev->dev, "Failed to parse vinovp in DT, keep default 6.5v\n");
+
+ mci->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!mci->regmap)
+ return dev_err_probe(&pdev->dev, -ENODEV, "Failed to get parent regmap\n");
+
+ ret = mt6360_chg_init_setting(mci);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to initial setting\n");
+
+ memcpy(&mci->psy_desc, &mt6360_charger_desc, sizeof(mci->psy_desc));
+ mci->psy_desc.name = dev_name(&pdev->dev);
+ charger_cfg.drv_data = mci;
+ charger_cfg.of_node = pdev->dev.of_node;
+ mci->psy = devm_power_supply_register(&pdev->dev,
+ &mci->psy_desc, &charger_cfg);
+ if (IS_ERR(mci->psy))
+ return dev_err_probe(&pdev->dev, PTR_ERR(mci->psy),
+ "Failed to register power supply dev\n");
+
+
+ ret = mt6360_chg_irq_register(pdev);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to register irqs\n");
+
+ config.dev = &pdev->dev;
+ config.regmap = mci->regmap;
+ mci->otg_rdev = devm_regulator_register(&pdev->dev, &mt6360_otg_rdesc,
+ &config);
+ if (IS_ERR(mci->otg_rdev))
+ return PTR_ERR(mci->otg_rdev);
+
+ schedule_work(&mci->chrdet_work);
+
+ return 0;
+}
+
+static const struct of_device_id __maybe_unused mt6360_charger_of_id[] = {
+ { .compatible = "mediatek,mt6360-chg", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt6360_charger_of_id);
+
+static const struct platform_device_id mt6360_charger_id[] = {
+ { "mt6360-chg", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, mt6360_charger_id);
+
+static struct platform_driver mt6360_charger_driver = {
+ .driver = {
+ .name = "mt6360-chg",
+ .of_match_table = of_match_ptr(mt6360_charger_of_id),
+ },
+ .probe = mt6360_charger_probe,
+ .id_table = mt6360_charger_id,
+};
+module_platform_driver(mt6360_charger_driver);
+
+MODULE_AUTHOR("Gene Chen <gene_chen@richtek.com>");
+MODULE_DESCRIPTION("MT6360 Charger Driver");
+MODULE_LICENSE("GPL");
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, &reg_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/power_supply_core.c b/drivers/power/supply/power_supply_core.c
index d99e2f11c183..0c2132c7f5d4 100644
--- a/drivers/power/supply/power_supply_core.c
+++ b/drivers/power/supply/power_supply_core.c
@@ -571,6 +571,7 @@ int power_supply_get_battery_info(struct power_supply *psy,
int err, len, index;
const __be32 *list;
+ info->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
info->energy_full_design_uwh = -EINVAL;
info->charge_full_design_uah = -EINVAL;
info->voltage_min_design_uv = -EINVAL;
@@ -618,6 +619,24 @@ int power_supply_get_battery_info(struct power_supply *psy,
* Documentation/power/power_supply_class.rst.
*/
+ if (!of_property_read_string(battery_np, "device-chemistry", &value)) {
+ if (!strcmp("nickel-cadmium", value))
+ info->technology = POWER_SUPPLY_TECHNOLOGY_NiCd;
+ else if (!strcmp("nickel-metal-hydride", value))
+ info->technology = POWER_SUPPLY_TECHNOLOGY_NiMH;
+ else if (!strcmp("lithium-ion", value))
+ /* Imprecise lithium-ion type */
+ info->technology = POWER_SUPPLY_TECHNOLOGY_LION;
+ else if (!strcmp("lithium-ion-polymer", value))
+ info->technology = POWER_SUPPLY_TECHNOLOGY_LIPO;
+ else if (!strcmp("lithium-ion-iron-phosphate", value))
+ info->technology = POWER_SUPPLY_TECHNOLOGY_LiFe;
+ else if (!strcmp("lithium-ion-manganese-oxide", value))
+ info->technology = POWER_SUPPLY_TECHNOLOGY_LiMn;
+ else
+ dev_warn(&psy->dev, "%s unknown battery type\n", value);
+ }
+
of_property_read_u32(battery_np, "energy-full-design-microwatt-hours",
&info->energy_full_design_uwh);
of_property_read_u32(battery_np, "charge-full-design-microamp-hours",
diff --git a/drivers/power/supply/qcom_smbb.c b/drivers/power/supply/qcom_smbb.c
index c890e1cec720..84cc9fba029d 100644
--- a/drivers/power/supply/qcom_smbb.c
+++ b/drivers/power/supply/qcom_smbb.c
@@ -929,11 +929,8 @@ static int smbb_charger_probe(struct platform_device *pdev)
int irq;
irq = platform_get_irq_byname(pdev, smbb_charger_irqs[i].name);
- if (irq < 0) {
- dev_err(&pdev->dev, "failed to get irq '%s'\n",
- smbb_charger_irqs[i].name);
+ if (irq < 0)
return irq;
- }
smbb_charger_irqs[i].handler(irq, chg);
diff --git a/drivers/power/supply/rn5t618_power.c b/drivers/power/supply/rn5t618_power.c
index dee520f0fdf5..a5e09ac78a50 100644
--- a/drivers/power/supply/rn5t618_power.c
+++ b/drivers/power/supply/rn5t618_power.c
@@ -9,10 +9,12 @@
#include <linux/device.h>
#include <linux/bitops.h>
#include <linux/errno.h>
+#include <linux/iio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mfd/rn5t618.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
@@ -37,23 +39,58 @@
#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;
struct power_supply *battery;
struct power_supply *usb;
struct power_supply *adp;
+ struct iio_channel *channel_vusb;
+ struct iio_channel *channel_vadp;
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_VOLTAGE_NOW,
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_VOLTAGE_NOW,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_ONLINE,
};
@@ -69,6 +106,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 +296,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,
+ &regval);
+ 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 +391,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 +407,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 +462,23 @@ 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, &regval);
+ if (ret < 0)
+ return ret;
+
+ val->intval = FROM_CUR_REG(regval);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ if (!info->channel_vadp)
+ return -ENODATA;
+
+ ret = iio_read_channel_processed_scale(info->channel_vadp, &val->intval, 1000);
+ if (ret < 0)
+ return ret;
+
+ break;
default:
return -EINVAL;
}
@@ -372,12 +486,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, &regval);
+ 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 +582,37 @@ 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,
+ &regval);
+ if (ret < 0)
+ return ret;
+
+ val->intval = 0;
+ if (regval & 2) {
+ ret = regmap_read(info->rn5t618->regmap,
+ RN5T618_REGISET2,
+ &regval);
+ if (ret < 0)
+ return ret;
+
+ val->intval = FROM_CUR_REG(regval);
+ }
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ if (!info->channel_vusb)
+ return -ENODATA;
+
+ ret = iio_read_channel_processed_scale(info->channel_vusb, &val->intval, 1000);
+ if (ret < 0)
+ return ret;
+
+ break;
default:
return -EINVAL;
}
@@ -408,12 +620,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 +675,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)
@@ -476,6 +735,20 @@ static int rn5t618_power_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, info);
+ info->channel_vusb = devm_iio_channel_get(&pdev->dev, "vusb");
+ if (IS_ERR(info->channel_vusb)) {
+ if (PTR_ERR(info->channel_vusb) == -ENODEV)
+ return -EPROBE_DEFER;
+ return PTR_ERR(info->channel_vusb);
+ }
+
+ info->channel_vadp = devm_iio_channel_get(&pdev->dev, "vadp");
+ if (IS_ERR(info->channel_vadp)) {
+ if (PTR_ERR(info->channel_vadp) == -ENODEV)
+ return -EPROBE_DEFER;
+ return PTR_ERR(info->channel_vadp);
+ }
+
ret = regmap_read(info->rn5t618->regmap, RN5T618_CONTROL, &v);
if (ret)
return ret;
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..c4a95b01463a 100644
--- a/drivers/power/supply/sbs-battery.c
+++ b/drivers/power/supply/sbs-battery.c
@@ -31,8 +31,9 @@ enum {
REG_CURRENT_AVG,
REG_MAX_ERR,
REG_CAPACITY,
- REG_TIME_TO_EMPTY,
- REG_TIME_TO_FULL,
+ REG_TIME_TO_EMPTY_NOW,
+ REG_TIME_TO_EMPTY_AVG,
+ REG_TIME_TO_FULL_AVG,
REG_STATUS,
REG_CAPACITY_LEVEL,
REG_CYCLE_COUNT,
@@ -102,7 +103,7 @@ static const struct chip_data {
[REG_TEMPERATURE] =
SBS_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535),
[REG_VOLTAGE] =
- SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000),
+ SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 65535),
[REG_CURRENT_NOW] =
SBS_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, 32767),
[REG_CURRENT_AVG] =
@@ -119,9 +120,11 @@ static const struct chip_data {
SBS_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535),
[REG_FULL_CHARGE_CAPACITY_CHARGE] =
SBS_DATA(POWER_SUPPLY_PROP_CHARGE_FULL, 0x10, 0, 65535),
- [REG_TIME_TO_EMPTY] =
+ [REG_TIME_TO_EMPTY_NOW] =
+ SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 0x11, 0, 65535),
+ [REG_TIME_TO_EMPTY_AVG] =
SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, 65535),
- [REG_TIME_TO_FULL] =
+ [REG_TIME_TO_FULL_AVG] =
SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0, 65535),
[REG_CHARGE_CURRENT] =
SBS_DATA(POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 0x14, 0, 65535),
@@ -165,6 +168,7 @@ static const enum power_supply_property sbs_properties[] = {
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN,
POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
POWER_SUPPLY_PROP_SERIAL_NUMBER,
@@ -189,6 +193,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 +214,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 +277,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 +674,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,
@@ -690,6 +752,7 @@ static void sbs_unit_adjustment(struct i2c_client *client,
val->intval -= TEMP_KELVIN_TO_CELSIUS;
break;
+ case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
/* sbs provides time to empty and time to full in minutes.
@@ -773,48 +836,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 +909,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 +935,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;
@@ -919,6 +971,7 @@ static int sbs_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CURRENT_NOW:
case POWER_SUPPLY_PROP_CURRENT_AVG:
case POWER_SUPPLY_PROP_TEMP:
+ case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
@@ -935,23 +988,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 +1140,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..ae45069bd5e1 100644
--- a/drivers/power/supply/sc27xx_fuel_gauge.c
+++ b/drivers/power/supply/sc27xx_fuel_gauge.c
@@ -1229,10 +1229,8 @@ static int sc27xx_fgu_probe(struct platform_device *pdev)
}
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(dev, "no irq resource specified\n");
+ if (irq < 0)
return irq;
- }
ret = devm_request_threaded_irq(data->dev, irq, NULL,
sc27xx_fgu_interrupt,
@@ -1342,6 +1340,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..753944e774c4 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>
@@ -19,6 +18,7 @@
#include <linux/power_supply.h>
#include <linux/property.h>
#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
#include <dt-bindings/power/summit,smb347-charger.h>
@@ -56,6 +56,7 @@
#define CFG_PIN_EN_CTRL_ACTIVE_LOW 0x60
#define CFG_PIN_EN_APSD_IRQ BIT(1)
#define CFG_PIN_EN_CHARGER_ERROR BIT(2)
+#define CFG_PIN_EN_CTRL BIT(4)
#define CFG_THERM 0x07
#define CFG_THERM_SOFT_HOT_COMPENSATION_MASK 0x03
#define CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT 0
@@ -63,12 +64,15 @@
#define CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT 2
#define CFG_THERM_MONITOR_DISABLED BIT(4)
#define CFG_SYSOK 0x08
+#define CFG_SYSOK_INOK_ACTIVE_HIGH BIT(0)
#define CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED BIT(2)
#define CFG_OTHER 0x09
#define CFG_OTHER_RID_MASK 0xc0
#define CFG_OTHER_RID_ENABLED_AUTO_OTG 0xc0
#define CFG_OTG 0x0a
#define CFG_OTG_TEMP_THRESHOLD_MASK 0x30
+#define CFG_OTG_CURRENT_LIMIT_250mA BIT(2)
+#define CFG_OTG_CURRENT_LIMIT_750mA BIT(3)
#define CFG_OTG_TEMP_THRESHOLD_SHIFT 4
#define CFG_OTG_CC_COMPENSATION_MASK 0xc0
#define CFG_OTG_CC_COMPENSATION_SHIFT 6
@@ -92,6 +96,7 @@
#define CMD_A 0x30
#define CMD_A_CHG_ENABLED BIT(1)
#define CMD_A_SUSPEND_ENABLED BIT(2)
+#define CMD_A_OTG_ENABLED BIT(4)
#define CMD_A_ALLOW_WRITE BIT(7)
#define CMD_B 0x31
#define CMD_C 0x33
@@ -133,11 +138,12 @@
* @regmap: pointer to driver regmap
* @mains: power_supply instance for AC/DC power
* @usb: power_supply instance for USB power
+ * @usb_rdev: USB VBUS regulator device
* @id: SMB charger ID
* @mains_online: is AC/DC input connected
* @usb_online: is USB input connected
- * @charging_enabled: is charging enabled
* @irq_unsupported: is interrupt unsupported by SMB hardware
+ * @usb_vbus_enabled: is USB VBUS powered by SMB charger
* @max_charge_current: maximum current (in uA) the battery can be charged
* @max_charge_voltage: maximum voltage (in uV) the battery can be charged
* @pre_charge_current: current (in uA) to use in pre-charging phase
@@ -168,6 +174,8 @@
* @use_usb_otg: USB OTG output can be used (not implemented yet)
* @enable_control: how charging enable/disable is controlled
* (driver/pin controls)
+ * @inok_polarity: polarity of INOK signal which denotes presence of external
+ * power supply
*
* @use_main, @use_usb, and @use_usb_otg are means to enable/disable
* hardware support for these. This is useful when we want to have for
@@ -190,11 +198,12 @@ struct smb347_charger {
struct regmap *regmap;
struct power_supply *mains;
struct power_supply *usb;
+ struct regulator_dev *usb_rdev;
unsigned int id;
bool mains_online;
bool usb_online;
- bool charging_enabled;
bool irq_unsupported;
+ bool usb_vbus_enabled;
unsigned int max_charge_current;
unsigned int max_charge_voltage;
@@ -215,6 +224,7 @@ struct smb347_charger {
bool use_usb;
bool use_usb_otg;
unsigned int enable_control;
+ unsigned int inok_polarity;
};
enum smb_charger_chipid {
@@ -359,21 +369,18 @@ static int smb347_charging_status(struct smb347_charger *smb)
static int smb347_charging_set(struct smb347_charger *smb, bool enable)
{
- int ret = 0;
-
if (smb->enable_control != SMB3XX_CHG_ENABLE_SW) {
dev_dbg(smb->dev, "charging enable/disable in SW disabled\n");
return 0;
}
- if (smb->charging_enabled != enable) {
- ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED,
- enable ? CMD_A_CHG_ENABLED : 0);
- if (!ret)
- smb->charging_enabled = enable;
+ if (enable && smb->usb_vbus_enabled) {
+ dev_dbg(smb->dev, "charging not enabled because USB is in host mode\n");
+ return 0;
}
- return ret;
+ return regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED,
+ enable ? CMD_A_CHG_ENABLED : 0);
}
static inline int smb347_charging_enable(struct smb347_charger *smb)
@@ -672,10 +679,22 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
*
* Returns %0 on success and negative errno in case of failure.
*/
-static int smb347_set_writable(struct smb347_charger *smb, bool writable)
+static int smb347_set_writable(struct smb347_charger *smb, bool writable,
+ bool irq_toggle)
{
- return regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE,
- writable ? CMD_A_ALLOW_WRITE : 0);
+ struct i2c_client *client = to_i2c_client(smb->dev);
+ int ret;
+
+ if (writable && irq_toggle && !smb->irq_unsupported)
+ disable_irq(client->irq);
+
+ ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE,
+ writable ? CMD_A_ALLOW_WRITE : 0);
+
+ if ((!writable || ret) && irq_toggle && !smb->irq_unsupported)
+ enable_irq(client->irq);
+
+ return ret;
}
static int smb347_hw_init(struct smb347_charger *smb)
@@ -683,7 +702,7 @@ static int smb347_hw_init(struct smb347_charger *smb)
unsigned int val;
int ret;
- ret = smb347_set_writable(smb, true);
+ ret = smb347_set_writable(smb, true, false);
if (ret < 0)
return ret;
@@ -725,6 +744,15 @@ static int smb347_hw_init(struct smb347_charger *smb)
if (ret < 0)
goto fail;
+ /* Activate pin control, making it writable. */
+ switch (smb->enable_control) {
+ case SMB3XX_CHG_ENABLE_PIN_ACTIVE_LOW:
+ case SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH:
+ ret = regmap_set_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL);
+ if (ret < 0)
+ goto fail;
+ }
+
/*
* Make the charging functionality controllable by a write to the
* command register unless pin control is specified in the platform
@@ -759,7 +787,7 @@ static int smb347_hw_init(struct smb347_charger *smb)
ret = smb347_start_stop_charging(smb);
fail:
- smb347_set_writable(smb, false);
+ smb347_set_writable(smb, false, false);
return ret;
}
@@ -867,7 +895,7 @@ static int smb347_irq_set(struct smb347_charger *smb, bool enable)
if (smb->irq_unsupported)
return 0;
- ret = smb347_set_writable(smb, true);
+ ret = smb347_set_writable(smb, true, true);
if (ret < 0)
return ret;
@@ -892,7 +920,7 @@ static int smb347_irq_set(struct smb347_charger *smb, bool enable)
ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CHARGER_ERROR,
enable ? CFG_PIN_EN_CHARGER_ERROR : 0);
fail:
- smb347_set_writable(smb, false);
+ smb347_set_writable(smb, false, true);
return ret;
}
@@ -920,7 +948,7 @@ static int smb347_irq_init(struct smb347_charger *smb,
if (!client->irq)
return 0;
- ret = smb347_set_writable(smb, true);
+ ret = smb347_set_writable(smb, true, false);
if (ret < 0)
return ret;
@@ -932,7 +960,7 @@ static int smb347_irq_init(struct smb347_charger *smb,
CFG_STAT_ACTIVE_HIGH | CFG_STAT_DISABLED,
CFG_STAT_DISABLED);
- smb347_set_writable(smb, false);
+ smb347_set_writable(smb, false, false);
if (ret < 0) {
dev_warn(smb->dev, "failed to initialize IRQ: %d\n", ret);
@@ -1242,6 +1270,13 @@ static void smb347_dt_parse_dev_info(struct smb347_charger *smb)
/* Select charging control */
device_property_read_u32(dev, "summit,enable-charge-control",
&smb->enable_control);
+
+ /*
+ * Polarity of INOK signal indicating presence of external power
+ * supply connected to the charger.
+ */
+ device_property_read_u32(dev, "summit,inok-polarity",
+ &smb->inok_polarity);
}
static int smb347_get_battery_info(struct smb347_charger *smb)
@@ -1293,12 +1328,176 @@ static int smb347_get_battery_info(struct smb347_charger *smb)
return 0;
}
+static int smb347_usb_vbus_get_current_limit(struct regulator_dev *rdev)
+{
+ struct smb347_charger *smb = rdev_get_drvdata(rdev);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(smb->regmap, CFG_OTG, &val);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * It's unknown what happens if this bit is unset due to lack of
+ * access to the datasheet, assume it's limit-enable.
+ */
+ if (!(val & CFG_OTG_CURRENT_LIMIT_250mA))
+ return 0;
+
+ return val & CFG_OTG_CURRENT_LIMIT_750mA ? 750000 : 250000;
+}
+
+static int smb347_usb_vbus_set_new_current_limit(struct smb347_charger *smb,
+ int max_uA)
+{
+ const unsigned int mask = CFG_OTG_CURRENT_LIMIT_750mA |
+ CFG_OTG_CURRENT_LIMIT_250mA;
+ unsigned int val = CFG_OTG_CURRENT_LIMIT_250mA;
+ int ret;
+
+ if (max_uA >= 750000)
+ val |= CFG_OTG_CURRENT_LIMIT_750mA;
+
+ ret = regmap_update_bits(smb->regmap, CFG_OTG, mask, val);
+ if (ret < 0)
+ dev_err(smb->dev, "failed to change USB current limit\n");
+
+ return ret;
+}
+
+static int smb347_usb_vbus_set_current_limit(struct regulator_dev *rdev,
+ int min_uA, int max_uA)
+{
+ struct smb347_charger *smb = rdev_get_drvdata(rdev);
+ int ret;
+
+ ret = smb347_set_writable(smb, true, true);
+ if (ret < 0)
+ return ret;
+
+ ret = smb347_usb_vbus_set_new_current_limit(smb, max_uA);
+ smb347_set_writable(smb, false, true);
+
+ return ret;
+}
+
+static int smb347_usb_vbus_regulator_enable(struct regulator_dev *rdev)
+{
+ struct smb347_charger *smb = rdev_get_drvdata(rdev);
+ int ret, max_uA;
+
+ ret = smb347_set_writable(smb, true, true);
+ if (ret < 0)
+ return ret;
+
+ smb347_charging_disable(smb);
+
+ if (device_property_read_bool(&rdev->dev, "summit,needs-inok-toggle")) {
+ unsigned int sysok = 0;
+
+ if (smb->inok_polarity == SMB3XX_SYSOK_INOK_ACTIVE_LOW)
+ sysok = CFG_SYSOK_INOK_ACTIVE_HIGH;
+
+ /*
+ * VBUS won't be powered if INOK is active, so we need to
+ * manually disable INOK on some platforms.
+ */
+ ret = regmap_update_bits(smb->regmap, CFG_SYSOK,
+ CFG_SYSOK_INOK_ACTIVE_HIGH, sysok);
+ if (ret < 0) {
+ dev_err(smb->dev, "failed to disable INOK\n");
+ goto done;
+ }
+ }
+
+ ret = smb347_usb_vbus_get_current_limit(rdev);
+ if (ret < 0) {
+ dev_err(smb->dev, "failed to get USB VBUS current limit\n");
+ goto done;
+ }
+
+ max_uA = ret;
+
+ ret = smb347_usb_vbus_set_new_current_limit(smb, 250000);
+ if (ret < 0) {
+ dev_err(smb->dev, "failed to preset USB VBUS current limit\n");
+ goto done;
+ }
+
+ ret = regmap_set_bits(smb->regmap, CMD_A, CMD_A_OTG_ENABLED);
+ if (ret < 0) {
+ dev_err(smb->dev, "failed to enable USB VBUS\n");
+ goto done;
+ }
+
+ smb->usb_vbus_enabled = true;
+
+ ret = smb347_usb_vbus_set_new_current_limit(smb, max_uA);
+ if (ret < 0) {
+ dev_err(smb->dev, "failed to restore USB VBUS current limit\n");
+ goto done;
+ }
+done:
+ smb347_set_writable(smb, false, true);
+
+ return ret;
+}
+
+static int smb347_usb_vbus_regulator_disable(struct regulator_dev *rdev)
+{
+ struct smb347_charger *smb = rdev_get_drvdata(rdev);
+ int ret;
+
+ ret = smb347_set_writable(smb, true, true);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_clear_bits(smb->regmap, CMD_A, CMD_A_OTG_ENABLED);
+ if (ret < 0) {
+ dev_err(smb->dev, "failed to disable USB VBUS\n");
+ goto done;
+ }
+
+ smb->usb_vbus_enabled = false;
+
+ if (device_property_read_bool(&rdev->dev, "summit,needs-inok-toggle")) {
+ unsigned int sysok = 0;
+
+ if (smb->inok_polarity == SMB3XX_SYSOK_INOK_ACTIVE_HIGH)
+ sysok = CFG_SYSOK_INOK_ACTIVE_HIGH;
+
+ ret = regmap_update_bits(smb->regmap, CFG_SYSOK,
+ CFG_SYSOK_INOK_ACTIVE_HIGH, sysok);
+ if (ret < 0) {
+ dev_err(smb->dev, "failed to enable INOK\n");
+ goto done;
+ }
+ }
+
+ smb347_start_stop_charging(smb);
+done:
+ smb347_set_writable(smb, false, true);
+
+ return ret;
+}
+
static const struct regmap_config smb347_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = SMB347_MAX_REGISTER,
.volatile_reg = smb347_volatile_reg,
.readable_reg = smb347_readable_reg,
+ .cache_type = REGCACHE_FLAT,
+ .num_reg_defaults_raw = SMB347_MAX_REGISTER,
+};
+
+static const struct regulator_ops smb347_usb_vbus_regulator_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = smb347_usb_vbus_regulator_enable,
+ .disable = smb347_usb_vbus_regulator_disable,
+ .get_current_limit = smb347_usb_vbus_get_current_limit,
+ .set_current_limit = smb347_usb_vbus_set_current_limit,
};
static const struct power_supply_desc smb347_mains_desc = {
@@ -1317,10 +1516,24 @@ static const struct power_supply_desc smb347_usb_desc = {
.num_properties = ARRAY_SIZE(smb347_properties),
};
+static const struct regulator_desc smb347_usb_vbus_regulator_desc = {
+ .name = "smb347-usb-vbus",
+ .of_match = of_match_ptr("usb-vbus"),
+ .ops = &smb347_usb_vbus_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .enable_reg = CMD_A,
+ .enable_mask = CMD_A_OTG_ENABLED,
+ .enable_val = CMD_A_OTG_ENABLED,
+ .fixed_uV = 5000000,
+ .n_voltages = 1,
+};
+
static int smb347_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct power_supply_config mains_usb_cfg = {};
+ struct regulator_config usb_rdev_cfg = {};
struct device *dev = &client->dev;
struct smb347_charger *smb;
int ret;
@@ -1368,6 +1581,18 @@ static int smb347_probe(struct i2c_client *client,
if (ret)
return ret;
+ usb_rdev_cfg.dev = dev;
+ usb_rdev_cfg.driver_data = smb;
+ usb_rdev_cfg.regmap = smb->regmap;
+
+ smb->usb_rdev = devm_regulator_register(dev,
+ &smb347_usb_vbus_regulator_desc,
+ &usb_rdev_cfg);
+ if (IS_ERR(smb->usb_rdev)) {
+ smb347_irq_disable(smb);
+ return PTR_ERR(smb->usb_rdev);
+ }
+
return 0;
}
@@ -1375,11 +1600,17 @@ static int smb347_remove(struct i2c_client *client)
{
struct smb347_charger *smb = i2c_get_clientdata(client);
+ smb347_usb_vbus_regulator_disable(smb->usb_rdev);
smb347_irq_disable(smb);
return 0;
}
+static void smb347_shutdown(struct i2c_client *client)
+{
+ smb347_remove(client);
+}
+
static const struct i2c_device_id smb347_id[] = {
{ "smb345", SMB345 },
{ "smb347", SMB347 },
@@ -1403,6 +1634,7 @@ static struct i2c_driver smb347_driver = {
},
.probe = smb347_probe,
.remove = smb347_remove,
+ .shutdown = smb347_shutdown,
.id_table = smb347_id,
};
module_i2c_driver(smb347_driver);
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);