diff options
24 files changed, 687 insertions, 65 deletions
diff --git a/Documentation/devicetree/bindings/goldfish/battery.txt b/Documentation/devicetree/bindings/goldfish/battery.txt new file mode 100644 index 000000000000..4fb613933214 --- /dev/null +++ b/Documentation/devicetree/bindings/goldfish/battery.txt @@ -0,0 +1,17 @@ +Android Goldfish Battery + +Android goldfish battery device generated by android emulator. + +Required properties: + +- compatible : should contain "google,goldfish-battery" to match emulator +- reg : <registers mapping> +- interrupts : <interrupt mapping> + +Example: + + goldfish_battery@9020000 { + compatible = "google,goldfish-battery"; + reg = <0x9020000 0x1000>; + interrupts = <0x3>; + }; diff --git a/Documentation/devicetree/bindings/power/act8945a-charger.txt b/Documentation/devicetree/bindings/power/act8945a-charger.txt new file mode 100644 index 000000000000..bea254c9d136 --- /dev/null +++ b/Documentation/devicetree/bindings/power/act8945a-charger.txt @@ -0,0 +1,35 @@ +Device-Tree bindings for charger of Active-semi ACT8945A Multi-Function Device + +Required properties: + - compatible: "active-semi,act8945a", please refer to ../mfd/act8945a.txt. + - active-semi,chglev-gpios: charge current level phandle with args + as described in ../gpio/gpio.txt. + +Optional properties: + - active-semi,check-battery-temperature: boolean to check the battery + temperature or not. + - active-semi,input-voltage-threshold-microvolt: unit: mV; + Specifies the charger's input over-voltage threshold value; + The value can be: 6600, 7000, 7500, 8000; default: 6600 + - active-semi,precondition-timeout: unit: minutes; + Specifies the charger's PRECONDITION safety timer setting value; + The value can be: 40, 60, 80, 0; If 0, it means to disable this timer; + default: 40. + - active-semi,total-timeout: unit: hours; + Specifies the charger's total safety timer setting value; + The value can be: 3, 4, 5, 0; If 0, it means to disable this timer; + default: 3. + +Example: + pmic@5b { + compatible = "active-semi,act8945a"; + reg = <0x5b>; + status = "okay"; + + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_charger_chglev>; + active-semi,chglev-gpios = <&pioA 12 GPIO_ACTIVE_HIGH>; + active-semi,input-voltage-threshold-microvolt = <6600>; + active-semi,precondition-timeout = <40>; + active-semi,total-timeout = <3>; + }; diff --git a/Documentation/devicetree/bindings/power_supply/ti,bq24735.txt b/Documentation/devicetree/bindings/power_supply/ti,bq24735.txt index 4f6a550184d0..3bf55757ceec 100644 --- a/Documentation/devicetree/bindings/power_supply/ti,bq24735.txt +++ b/Documentation/devicetree/bindings/power_supply/ti,bq24735.txt @@ -22,6 +22,9 @@ Optional properties : value must be between 128mA and 8.064A with a 128mA step resolution. The POR value is 0x1000h. This number is in mA (e.g. 8064), see the spec for more information about the InputCurrent (0x3fh) register. + - ti,external-control : Indicates that the charger is configured externally + and that the host should not attempt to enable/disable charging or set the + charge voltage/current. Example: diff --git a/arch/arm/boot/dts/omap3-n900.dts b/arch/arm/boot/dts/omap3-n900.dts index 74d8f7eb5563..e3bdcf819643 100644 --- a/arch/arm/boot/dts/omap3-n900.dts +++ b/arch/arm/boot/dts/omap3-n900.dts @@ -107,8 +107,8 @@ }; }; - isp1704: isp1704 { - compatible = "nxp,isp1704"; + isp1707: isp1707 { + compatible = "nxp,isp1707"; nxp,enable-gpio = <&gpio3 3 GPIO_ACTIVE_HIGH>; usb-phy = <&usb2_phy>; }; @@ -618,7 +618,7 @@ ti,termination-current = <100>; ti,resistor-sense = <68>; - ti,usb-charger-detection = <&isp1704>; + ti,usb-charger-detection = <&isp1707>; }; }; diff --git a/drivers/power/88pm860x_charger.c b/drivers/power/88pm860x_charger.c index 297e72dc70e6..2b82e44d9027 100644 --- a/drivers/power/88pm860x_charger.c +++ b/drivers/power/88pm860x_charger.c @@ -435,7 +435,7 @@ static irqreturn_t pm860x_temp_handler(int irq, void *data) psy = power_supply_get_by_name(pm860x_supplied_to[0]); if (!psy) - goto out; + return IRQ_HANDLED; ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_TEMP, &temp); if (ret) goto out; diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 1ddd13cc0c07..421770ddafa3 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -75,6 +75,13 @@ config BATTERY_88PM860X help Say Y here to enable battery monitor for Marvell 88PM860x chip. +config BATTERY_ACT8945A + tristate "Active-semi ACT8945A charger driver" + depends on MFD_ACT8945A || COMPILE_TEST + help + Say Y here to enable support for power supply provided by + Active-semi ActivePath ACT8945A charger. + config BATTERY_DS2760 tristate "DS2760 battery driver (HP iPAQ & others)" depends on W1 && W1_SLAVE_DS2760 diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 0e4eab55f8d7..e46b75d448a5 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_WM8350_POWER) += wm8350_power.o obj-$(CONFIG_TEST_POWER) += test_power.o obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o +obj-$(CONFIG_BATTERY_ACT8945A) += act8945a_charger.o obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o diff --git a/drivers/power/act8945a_charger.c b/drivers/power/act8945a_charger.c new file mode 100644 index 000000000000..b5c00e45741e --- /dev/null +++ b/drivers/power/act8945a_charger.c @@ -0,0 +1,359 @@ +/* + * Power supply driver for the Active-semi ACT8945A PMIC + * + * Copyright (C) 2015 Atmel Corporation + * + * Author: Wenyou Yang <wenyou.yang@atmel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/regmap.h> + +static const char *act8945a_charger_model = "ACT8945A"; +static const char *act8945a_charger_manufacturer = "Active-semi"; + +/** + * ACT8945A Charger Register Map + */ + +/* 0x70: Reserved */ +#define ACT8945A_APCH_CFG 0x71 +#define ACT8945A_APCH_STATUS 0x78 +#define ACT8945A_APCH_CTRL 0x79 +#define ACT8945A_APCH_STATE 0x7A + +/* ACT8945A_APCH_CFG */ +#define APCH_CFG_OVPSET (0x3 << 0) +#define APCH_CFG_OVPSET_6V6 (0x0 << 0) +#define APCH_CFG_OVPSET_7V (0x1 << 0) +#define APCH_CFG_OVPSET_7V5 (0x2 << 0) +#define APCH_CFG_OVPSET_8V (0x3 << 0) +#define APCH_CFG_PRETIMO (0x3 << 2) +#define APCH_CFG_PRETIMO_40_MIN (0x0 << 2) +#define APCH_CFG_PRETIMO_60_MIN (0x1 << 2) +#define APCH_CFG_PRETIMO_80_MIN (0x2 << 2) +#define APCH_CFG_PRETIMO_DISABLED (0x3 << 2) +#define APCH_CFG_TOTTIMO (0x3 << 4) +#define APCH_CFG_TOTTIMO_3_HOUR (0x0 << 4) +#define APCH_CFG_TOTTIMO_4_HOUR (0x1 << 4) +#define APCH_CFG_TOTTIMO_5_HOUR (0x2 << 4) +#define APCH_CFG_TOTTIMO_DISABLED (0x3 << 4) +#define APCH_CFG_SUSCHG (0x1 << 7) + +#define APCH_STATUS_CHGDAT BIT(0) +#define APCH_STATUS_INDAT BIT(1) +#define APCH_STATUS_TEMPDAT BIT(2) +#define APCH_STATUS_TIMRDAT BIT(3) +#define APCH_STATUS_CHGSTAT BIT(4) +#define APCH_STATUS_INSTAT BIT(5) +#define APCH_STATUS_TEMPSTAT BIT(6) +#define APCH_STATUS_TIMRSTAT BIT(7) + +#define APCH_CTRL_CHGEOCOUT BIT(0) +#define APCH_CTRL_INDIS BIT(1) +#define APCH_CTRL_TEMPOUT BIT(2) +#define APCH_CTRL_TIMRPRE BIT(3) +#define APCH_CTRL_CHGEOCIN BIT(4) +#define APCH_CTRL_INCON BIT(5) +#define APCH_CTRL_TEMPIN BIT(6) +#define APCH_CTRL_TIMRTOT BIT(7) + +#define APCH_STATE_ACINSTAT (0x1 << 1) +#define APCH_STATE_CSTATE (0x3 << 4) +#define APCH_STATE_CSTATE_SHIFT 4 +#define APCH_STATE_CSTATE_DISABLED 0x00 +#define APCH_STATE_CSTATE_EOC 0x01 +#define APCH_STATE_CSTATE_FAST 0x02 +#define APCH_STATE_CSTATE_PRE 0x03 + +struct act8945a_charger { + struct regmap *regmap; + bool battery_temperature; +}; + +static int act8945a_get_charger_state(struct regmap *regmap, int *val) +{ + int ret; + unsigned int status, state; + + ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status); + if (ret < 0) + return ret; + + ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state); + if (ret < 0) + return ret; + + state &= APCH_STATE_CSTATE; + state >>= APCH_STATE_CSTATE_SHIFT; + + if (state == APCH_STATE_CSTATE_EOC) { + if (status & APCH_STATUS_CHGDAT) + *val = POWER_SUPPLY_STATUS_FULL; + else + *val = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else if ((state == APCH_STATE_CSTATE_FAST) || + (state == APCH_STATE_CSTATE_PRE)) { + *val = POWER_SUPPLY_STATUS_CHARGING; + } else { + *val = POWER_SUPPLY_STATUS_NOT_CHARGING; + } + + return 0; +} + +static int act8945a_get_charge_type(struct regmap *regmap, int *val) +{ + int ret; + unsigned int state; + + ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state); + if (ret < 0) + return ret; + + state &= APCH_STATE_CSTATE; + state >>= APCH_STATE_CSTATE_SHIFT; + + switch (state) { + case APCH_STATE_CSTATE_PRE: + *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case APCH_STATE_CSTATE_FAST: + *val = POWER_SUPPLY_CHARGE_TYPE_FAST; + break; + case APCH_STATE_CSTATE_EOC: + case APCH_STATE_CSTATE_DISABLED: + default: + *val = POWER_SUPPLY_CHARGE_TYPE_NONE; + } + + return 0; +} + +static int act8945a_get_battery_health(struct act8945a_charger *charger, + struct regmap *regmap, int *val) +{ + int ret; + unsigned int status; + + ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status); + if (ret < 0) + return ret; + + if (charger->battery_temperature && !(status & APCH_STATUS_TEMPDAT)) + *val = POWER_SUPPLY_HEALTH_OVERHEAT; + else if (!(status & APCH_STATUS_INDAT)) + *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + else if (status & APCH_STATUS_TIMRDAT) + *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; + else + *val = POWER_SUPPLY_HEALTH_GOOD; + + return 0; +} + +static enum power_supply_property act8945a_charger_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER +}; + +static int act8945a_charger_get_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct act8945a_charger *charger = power_supply_get_drvdata(psy); + struct regmap *regmap = charger->regmap; + int ret = 0; + + switch (prop) { + case POWER_SUPPLY_PROP_STATUS: + ret = act8945a_get_charger_state(regmap, &val->intval); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + ret = act8945a_get_charge_type(regmap, &val->intval); + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_HEALTH: + ret = act8945a_get_battery_health(charger, + regmap, &val->intval); + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = act8945a_charger_model; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = act8945a_charger_manufacturer; + break; + default: + return -EINVAL; + } + + return ret; +} + +static const struct power_supply_desc act8945a_charger_desc = { + .name = "act8945a-charger", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = act8945a_charger_get_property, + .properties = act8945a_charger_props, + .num_properties = ARRAY_SIZE(act8945a_charger_props), +}; + +#define DEFAULT_TOTAL_TIME_OUT 3 +#define DEFAULT_PRE_TIME_OUT 40 +#define DEFAULT_INPUT_OVP_THRESHOLD 6600 + +static int act8945a_charger_config(struct device *dev, + struct act8945a_charger *charger) +{ + struct device_node *np = dev->of_node; + enum of_gpio_flags flags; + struct regmap *regmap = charger->regmap; + + u32 total_time_out; + u32 pre_time_out; + u32 input_voltage_threshold; + int chglev_pin; + + unsigned int value = 0; + + if (!np) { + dev_err(dev, "no charger of node\n"); + return -EINVAL; + } + + charger->battery_temperature = of_property_read_bool(np, + "active-semi,check-battery-temperature"); + + chglev_pin = of_get_named_gpio_flags(np, + "active-semi,chglev-gpios", 0, &flags); + + if (gpio_is_valid(chglev_pin)) { + gpio_set_value(chglev_pin, + ((flags == OF_GPIO_ACTIVE_LOW) ? 0 : 1)); + } + + if (of_property_read_u32(np, + "active-semi,input-voltage-threshold-microvolt", + &input_voltage_threshold)) + input_voltage_threshold = DEFAULT_INPUT_OVP_THRESHOLD; + + if (of_property_read_u32(np, + "active-semi,precondition-timeout", + &pre_time_out)) + pre_time_out = DEFAULT_PRE_TIME_OUT; + + if (of_property_read_u32(np, "active-semi,total-timeout", + &total_time_out)) + total_time_out = DEFAULT_TOTAL_TIME_OUT; + + switch (input_voltage_threshold) { + case 8000: + value |= APCH_CFG_OVPSET_8V; + break; + case 7500: + value |= APCH_CFG_OVPSET_7V5; + break; + case 7000: + value |= APCH_CFG_OVPSET_7V; + break; + case 6600: + default: + value |= APCH_CFG_OVPSET_6V6; + break; + } + + switch (pre_time_out) { + case 60: + value |= APCH_CFG_PRETIMO_60_MIN; + break; + case 80: + value |= APCH_CFG_PRETIMO_80_MIN; + break; + case 0: + value |= APCH_CFG_PRETIMO_DISABLED; + break; + case 40: + default: + value |= APCH_CFG_PRETIMO_40_MIN; + break; + } + + switch (total_time_out) { + case 4: + value |= APCH_CFG_TOTTIMO_4_HOUR; + break; + case 5: + value |= APCH_CFG_TOTTIMO_5_HOUR; + break; + case 0: + value |= APCH_CFG_TOTTIMO_DISABLED; + break; + case 3: + default: + value |= APCH_CFG_TOTTIMO_3_HOUR; + break; + } + + return regmap_write(regmap, ACT8945A_APCH_CFG, value); +} + +static int act8945a_charger_probe(struct platform_device *pdev) +{ + struct act8945a_charger *charger; + struct power_supply *psy; + struct power_supply_config psy_cfg = {}; + int ret; + + charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL); + if (!charger) + return -ENOMEM; + + charger->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!charger->regmap) { + dev_err(&pdev->dev, "Parent did not provide regmap\n"); + return -EINVAL; + } + + ret = act8945a_charger_config(pdev->dev.parent, charger); + if (ret) + return ret; + + psy_cfg.of_node = pdev->dev.parent->of_node; + psy_cfg.drv_data = charger; + + psy = devm_power_supply_register(&pdev->dev, + &act8945a_charger_desc, + &psy_cfg); + if (IS_ERR(psy)) { + dev_err(&pdev->dev, "failed to register power supply\n"); + return PTR_ERR(psy); + } + + return 0; +} + +static struct platform_driver act8945a_charger_driver = { + .driver = { + .name = "act8945a-charger", + }, + .probe = act8945a_charger_probe, +}; +module_platform_driver(act8945a_charger_driver); + +MODULE_DESCRIPTION("Active-semi ACT8945A ActivePath charger driver"); +MODULE_AUTHOR("Wenyou Yang <wenyou.yang@atmel.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c index 27e89536689a..73e2f0b79dd4 100644 --- a/drivers/power/bq2415x_charger.c +++ b/drivers/power/bq2415x_charger.c @@ -1759,6 +1759,7 @@ static const struct i2c_device_id bq2415x_i2c_id_table[] = { }; MODULE_DEVICE_TABLE(i2c, bq2415x_i2c_id_table); +#ifdef CONFIG_ACPI static const struct acpi_device_id bq2415x_i2c_acpi_match[] = { { "BQ2415X", BQUNKNOWN }, { "BQ241500", BQ24150 }, @@ -1776,10 +1777,31 @@ static const struct acpi_device_id bq2415x_i2c_acpi_match[] = { {}, }; MODULE_DEVICE_TABLE(acpi, bq2415x_i2c_acpi_match); +#endif + +#ifdef CONFIG_OF +static const struct of_device_id bq2415x_of_match_table[] = { + { .compatible = "ti,bq24150" }, + { .compatible = "ti,bq24150a" }, + { .compatible = "ti,bq24151" }, + { .compatible = "ti,bq24151a" }, + { .compatible = "ti,bq24152" }, + { .compatible = "ti,bq24153" }, + { .compatible = "ti,bq24153a" }, + { .compatible = "ti,bq24155" }, + { .compatible = "ti,bq24156" }, + { .compatible = "ti,bq24156a" }, + { .compatible = "ti,bq24157s" }, + { .compatible = "ti,bq24158" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bq2415x_of_match_table); +#endif static struct i2c_driver bq2415x_driver = { .driver = { .name = "bq2415x-charger", + .of_match_table = of_match_ptr(bq2415x_of_match_table), .acpi_match_table = ACPI_PTR(bq2415x_i2c_acpi_match), }, .probe = bq2415x_probe, diff --git a/drivers/power/bq24735-charger.c b/drivers/power/bq24735-charger.c index eb2b3689de97..fa454c19ce17 100644 --- a/drivers/power/bq24735-charger.c +++ b/drivers/power/bq24735-charger.c @@ -48,6 +48,8 @@ struct bq24735 { struct power_supply_desc charger_desc; struct i2c_client *client; struct bq24735_platform *pdata; + struct mutex lock; + bool charging; }; static inline struct bq24735 *to_bq24735(struct power_supply *psy) @@ -56,9 +58,23 @@ static inline struct bq24735 *to_bq24735(struct power_supply *psy) } static enum power_supply_property bq24735_charger_properties[] = { + POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_ONLINE, }; +static int bq24735_charger_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + return 1; + default: + break; + } + + return 0; +} + static inline int bq24735_write_word(struct i2c_client *client, u8 reg, u16 value) { @@ -90,6 +106,9 @@ static int bq24735_update_word(struct i2c_client *client, u8 reg, static inline int bq24735_enable_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); @@ -97,6 +116,9 @@ static inline int bq24735_enable_charging(struct bq24735 *charger) 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); @@ -108,6 +130,9 @@ static int bq24735_config_charger(struct bq24735 *charger) int ret; u16 value; + if (pdata->ext_control) + return 0; + if (pdata->charge_current) { value = pdata->charge_current & BQ24735_CHARGE_CURRENT_MASK; @@ -174,16 +199,30 @@ static bool bq24735_charger_is_present(struct bq24735 *charger) return false; } +static int bq24735_charger_is_charging(struct bq24735 *charger) +{ + int ret = bq24735_read_word(charger->client, BQ24735_CHG_OPT); + + if (ret < 0) + return ret; + + return !(ret & BQ24735_CHG_OPT_CHARGE_DISABLE); +} + static irqreturn_t bq24735_charger_isr(int irq, void *devid) { struct power_supply *psy = devid; struct bq24735 *charger = to_bq24735(psy); - if (bq24735_charger_is_present(charger)) + mutex_lock(&charger->lock); + + if (charger->charging && bq24735_charger_is_present(charger)) bq24735_enable_charging(charger); else bq24735_disable_charging(charger); + mutex_unlock(&charger->lock); + power_supply_changed(psy); return IRQ_HANDLED; @@ -199,6 +238,19 @@ static int bq24735_charger_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_ONLINE: val->intval = bq24735_charger_is_present(charger) ? 1 : 0; break; + case POWER_SUPPLY_PROP_STATUS: + switch (bq24735_charger_is_charging(charger)) { + case 1: + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + case 0: + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + default: + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } + break; default: return -EINVAL; } @@ -206,6 +258,46 @@ static int bq24735_charger_get_property(struct power_supply *psy, return 0; } +static int bq24735_charger_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct bq24735 *charger = to_bq24735(psy); + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + switch (val->intval) { + case POWER_SUPPLY_STATUS_CHARGING: + mutex_lock(&charger->lock); + charger->charging = true; + ret = bq24735_enable_charging(charger); + mutex_unlock(&charger->lock); + if (ret) + return ret; + bq24735_config_charger(charger); + break; + case POWER_SUPPLY_STATUS_DISCHARGING: + case POWER_SUPPLY_STATUS_NOT_CHARGING: + mutex_lock(&charger->lock); + charger->charging = false; + ret = bq24735_disable_charging(charger); + mutex_unlock(&charger->lock); + if (ret) + return ret; + break; + default: + return -EINVAL; + } + power_supply_changed(psy); + break; + default: + return -EPERM; + } + + return 0; +} + static struct bq24735_platform *bq24735_parse_dt_data(struct i2c_client *client) { struct bq24735_platform *pdata; @@ -239,6 +331,8 @@ static struct bq24735_platform *bq24735_parse_dt_data(struct i2c_client *client) if (!ret) pdata->input_current = val; + pdata->ext_control = of_property_read_bool(np, "ti,external-control"); + return pdata; } @@ -255,6 +349,8 @@ static int bq24735_charger_probe(struct i2c_client *client, if (!charger) return -ENOMEM; + mutex_init(&charger->lock); + charger->charging = true; charger->pdata = client->dev.platform_data; if (IS_ENABLED(CONFIG_OF) && !charger->pdata && client->dev.of_node) @@ -285,6 +381,9 @@ static int bq24735_charger_probe(struct i2c_client *client, supply_desc->properties = bq24735_charger_properties; supply_desc->num_properties = ARRAY_SIZE(bq24735_charger_properties); supply_desc->get_property = bq24735_charger_get_property; + supply_desc->set_property = bq24735_charger_set_property; + supply_desc->property_is_writeable = + bq24735_charger_property_is_writeable; psy_cfg.supplied_to = charger->pdata->supplied_to; psy_cfg.num_supplicants = charger->pdata->num_supplicants; @@ -293,27 +392,6 @@ static int bq24735_charger_probe(struct i2c_client *client, i2c_set_clientdata(client, charger); - ret = bq24735_read_word(client, BQ24735_MANUFACTURER_ID); - if (ret < 0) { - dev_err(&client->dev, "Failed to read manufacturer id : %d\n", - ret); - return ret; - } else if (ret != 0x0040) { - dev_err(&client->dev, - "manufacturer id mismatch. 0x0040 != 0x%04x\n", ret); - return -ENODEV; - } - - ret = bq24735_read_word(client, BQ24735_DEVICE_ID); - if (ret < 0) { - dev_err(&client->dev, "Failed to read device id : %d\n", ret); - return ret; - } else if (ret != 0x000B) { - dev_err(&client->dev, - "device id mismatch. 0x000b != 0x%04x\n", ret); - return -ENODEV; - } - if (gpio_is_valid(charger->pdata->status_gpio)) { ret = devm_gpio_request(&client->dev, charger->pdata->status_gpio, @@ -327,6 +405,30 @@ static int bq24735_charger_probe(struct i2c_client *client, charger->pdata->status_gpio_valid = !ret; } + if (!charger->pdata->status_gpio_valid + || bq24735_charger_is_present(charger)) { + ret = bq24735_read_word(client, BQ24735_MANUFACTURER_ID); + if (ret < 0) { + dev_err(&client->dev, "Failed to read manufacturer id : %d\n", + ret); + return ret; + } else if (ret != 0x0040) { + dev_err(&client->dev, + "manufacturer id mismatch. 0x0040 != 0x%04x\n", ret); + return -ENODEV; + } + + ret = bq24735_read_word(client, BQ24735_DEVICE_ID); + if (ret < 0) { + dev_err(&client->dev, "Failed to read device id : %d\n", ret); + return ret; + } else if (ret != 0x000B) { + dev_err(&client->dev, + "device id mismatch. 0x000b != 0x%04x\n", ret); + return -ENODEV; + } + } + ret = bq24735_config_charger(charger); if (ret < 0) { dev_err(&client->dev, "failed in configuring charger"); diff --git a/drivers/power/bq27xxx_battery.c b/drivers/power/bq27xxx_battery.c index 6b027a418943..45f6ebf88df6 100644 --- a/drivers/power/bq27xxx_battery.c +++ b/drivers/power/bq27xxx_battery.c @@ -46,6 +46,7 @@ #include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/slab.h> +#include <linux/of.h> #include <linux/power/bq27xxx_battery.h> @@ -1090,16 +1091,27 @@ static const struct platform_device_id bq27xxx_battery_platform_id_table[] = { }; MODULE_DEVICE_TABLE(platform, bq27xxx_battery_platform_id_table); +#ifdef CONFIG_OF +static const struct of_device_id bq27xxx_battery_platform_of_match_table[] = { + { .compatible = "ti,bq27000" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bq27xxx_battery_platform_of_match_table); +#endif + static struct platform_driver bq27xxx_battery_platform_driver = { .probe = bq27xxx_battery_platform_probe, .remove = bq27xxx_battery_platform_remove, .driver = { .name = "bq27000-battery", + .of_match_table = of_match_ptr(bq27xxx_battery_platform_of_match_table), }, .id_table = bq27xxx_battery_platform_id_table, }; module_platform_driver(bq27xxx_battery_platform_driver); +MODULE_ALIAS("platform:bq27000-battery"); + MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); MODULE_DESCRIPTION("BQ27xxx battery monitor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/power/bq27xxx_battery_i2c.c b/drivers/power/bq27xxx_battery_i2c.c index 8eafc6f0df88..b8f8d3ade31b 100644 --- a/drivers/power/bq27xxx_battery_i2c.c +++ b/drivers/power/bq27xxx_battery_i2c.c @@ -166,9 +166,33 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = { }; MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table); +#ifdef CONFIG_OF +static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = { + { .compatible = "ti,bq27200" }, + { .compatible = "ti,bq27210" }, + { .compatible = "ti,bq27500" }, + { .compatible = "ti,bq27510" }, + { .compatible = "ti,bq27520" }, + { .compatible = "ti,bq27530" }, + { .compatible = "ti,bq27531" }, + { .compatible = "ti,bq27541" }, + { .compatible = "ti,bq27542" }, + { .compatible = "ti,bq27546" }, + { .compatible = "ti,bq27742" }, + { .compatible = "ti,bq27545" }, + { .compatible = "ti,bq27421" }, + { .compatible = "ti,bq27425" }, + { .compatible = "ti,bq27441" }, + { .compatible = "ti,bq27621" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bq27xxx_battery_i2c_of_match_table); +#endif + static struct i2c_driver bq27xxx_battery_i2c_driver = { .driver = { .name = "bq27xxx-battery", + .of_match_table = of_match_ptr(bq27xxx_battery_i2c_of_match_table), }, .probe = bq27xxx_battery_i2c_probe, .remove = bq27xxx_battery_i2c_remove, diff --git a/drivers/power/collie_battery.c b/drivers/power/collie_battery.c index 8a971b3dbe58..3a0bc608d4b5 100644 --- a/drivers/power/collie_battery.c +++ b/drivers/power/collie_battery.c @@ -26,7 +26,6 @@ static DEFINE_MUTEX(bat_lock); /* protects gpio pins */ static struct work_struct bat_work; static struct ucb1x00 *ucb; -static int wakeup_enabled; struct collie_bat { int status; @@ -291,6 +290,8 @@ static struct gpio collie_batt_gpios[] = { }; #ifdef CONFIG_PM +static int wakeup_enabled; + static int collie_bat_suspend(struct ucb1x00_dev *dev) { /* flush all pending status updates */ diff --git a/drivers/power/goldfish_battery.c b/drivers/power/goldfish_battery.c index a50bb988c69a..f5c525e4482a 100644 --- a/drivers/power/goldfish_battery.c +++ b/drivers/power/goldfish_battery.c @@ -24,6 +24,7 @@ #include <linux/pci.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/acpi.h> struct goldfish_battery_data { void __iomem *reg_base; @@ -227,11 +228,25 @@ static int goldfish_battery_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id goldfish_battery_of_match[] = { + { .compatible = "google,goldfish-battery", }, + {}, +}; +MODULE_DEVICE_TABLE(of, goldfish_battery_of_match); + +static const struct acpi_device_id goldfish_battery_acpi_match[] = { + { "GFSH0001", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, goldfish_battery_acpi_match); + static struct platform_driver goldfish_battery_device = { .probe = goldfish_battery_probe, .remove = goldfish_battery_remove, .driver = { - .name = "goldfish-battery" + .name = "goldfish-battery", + .of_match_table = goldfish_battery_of_match, + .acpi_match_table = ACPI_PTR(goldfish_battery_acpi_match), } }; module_platform_driver(goldfish_battery_device); diff --git a/drivers/power/ipaq_micro_battery.c b/drivers/power/ipaq_micro_battery.c index f03014ea1dc4..3f314b1a30d7 100644 --- a/drivers/power/ipaq_micro_battery.c +++ b/drivers/power/ipaq_micro_battery.c @@ -281,7 +281,7 @@ static int micro_batt_remove(struct platform_device *pdev) return 0; } -static int micro_batt_suspend(struct device *dev) +static int __maybe_unused micro_batt_suspend(struct device *dev) { struct micro_battery *mb = dev_get_drvdata(dev); @@ -289,7 +289,7 @@ static int micro_batt_suspend(struct device *dev) return 0; } -static int micro_batt_resume(struct device *dev) +static int __maybe_unused micro_batt_resume(struct device *dev) { struct micro_battery *mb = dev_get_drvdata(dev); diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index 46a292aa182d..4cd6899b961e 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -411,8 +411,10 @@ static int isp1704_charger_probe(struct platform_device *pdev) if (np) { int gpio = of_get_named_gpio(np, "nxp,enable-gpio", 0); - if (gpio < 0) + if (gpio < 0) { + dev_err(&pdev->dev, "missing DT GPIO nxp,enable-gpio\n"); return gpio; + } pdata = devm_kzalloc(&pdev->dev, sizeof(struct isp1704_charger_data), GFP_KERNEL); @@ -422,8 +424,10 @@ static int isp1704_charger_probe(struct platform_device *pdev) ret = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio, GPIOF_OUT_INIT_HIGH, "isp1704_reset"); - if (ret) + if (ret) { + dev_err(&pdev->dev, "gpio request failed\n"); goto fail0; + } } if (!pdata) { @@ -443,6 +447,7 @@ static int isp1704_charger_probe(struct platform_device *pdev) if (IS_ERR(isp->phy)) { ret = PTR_ERR(isp->phy); + dev_err(&pdev->dev, "usb_get_phy failed\n"); goto fail0; } @@ -452,8 +457,10 @@ static int isp1704_charger_probe(struct platform_device *pdev) isp1704_charger_set_power(isp, 1); ret = isp1704_test_ulpi(isp); - if (ret < 0) + if (ret < 0) { + dev_err(&pdev->dev, "isp1704_test_ulpi failed\n"); goto fail1; + } isp->psy_desc.name = "isp1704"; isp->psy_desc.type = POWER_SUPPLY_TYPE_USB; @@ -466,6 +473,7 @@ static int isp1704_charger_probe(struct platform_device *pdev) isp->psy = power_supply_register(isp->dev, &isp->psy_desc, &psy_cfg); if (IS_ERR(isp->psy)) { ret = PTR_ERR(isp->psy); + dev_err(&pdev->dev, "power_supply_register failed\n"); goto fail1; } @@ -478,8 +486,10 @@ static int isp1704_charger_probe(struct platform_device *pdev) isp->nb.notifier_call = isp1704_notifier_call; ret = usb_register_notifier(isp->phy, &isp->nb); - if (ret) + if (ret) { + dev_err(&pdev->dev, "usb_register_notifier failed\n"); goto fail2; + } dev_info(isp->dev, "registered with product id %s\n", isp->model); @@ -526,6 +536,7 @@ static int isp1704_charger_remove(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id omap_isp1704_of_match[] = { { .compatible = "nxp,isp1704", }, + { .compatible = "nxp,isp1707", }, {}, }; MODULE_DEVICE_TABLE(of, omap_isp1704_of_match); diff --git a/drivers/power/jz4740-battery.c b/drivers/power/jz4740-battery.c index abdfc21ec13f..88f04f4d1a70 100644 --- a/drivers/power/jz4740-battery.c +++ b/drivers/power/jz4740-battery.c @@ -208,7 +208,7 @@ static void jz_battery_update(struct jz_battery *jz_battery) } voltage = jz_battery_read_voltage(jz_battery); - if (abs(voltage - jz_battery->voltage) < 50000) { + if (voltage >= 0 && abs(voltage - jz_battery->voltage) > 50000) { jz_battery->voltage = voltage; has_changed = true; } diff --git a/drivers/power/lp8788-charger.c b/drivers/power/lp8788-charger.c index f5a48fd68b01..7321b727d484 100644 --- a/drivers/power/lp8788-charger.c +++ b/drivers/power/lp8788-charger.c @@ -455,7 +455,7 @@ static void lp8788_charger_event(struct work_struct *work) static bool lp8788_find_irq_id(struct lp8788_charger *pchg, int virq, int *id) { - bool found; + bool found = false; int i; for (i = 0; i < pchg->num_irqs; i++) { diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c index 8f9bd1d0eeb6..fb62ed3fc38c 100644 --- a/drivers/power/pm2301_charger.c +++ b/drivers/power/pm2301_charger.c @@ -911,11 +911,7 @@ static struct pm2xxx_irq pm2xxx_charger_irq[] = { {"PM2XXX_IRQ_INT", pm2xxx_irq_int}, }; -#ifdef CONFIG_PM - -#ifdef CONFIG_PM_SLEEP - -static int pm2xxx_wall_charger_resume(struct device *dev) +static int __maybe_unused pm2xxx_wall_charger_resume(struct device *dev) { struct i2c_client *i2c_client = to_i2c_client(dev); struct pm2xxx_charger *pm2; @@ -931,7 +927,7 @@ static int pm2xxx_wall_charger_resume(struct device *dev) return 0; } -static int pm2xxx_wall_charger_suspend(struct device *dev) +static int __maybe_unused pm2xxx_wall_charger_suspend(struct device *dev) { struct i2c_client *i2c_client = to_i2c_client(dev); struct pm2xxx_charger *pm2; @@ -949,9 +945,7 @@ static int pm2xxx_wall_charger_suspend(struct device *dev) return 0; } -#endif - -static int pm2xxx_runtime_suspend(struct device *dev) +static int __maybe_unused pm2xxx_runtime_suspend(struct device *dev) { struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev); struct pm2xxx_charger *pm2; @@ -962,7 +956,7 @@ static int pm2xxx_runtime_suspend(struct device *dev) return 0; } -static int pm2xxx_runtime_resume(struct device *dev) +static int __maybe_unused pm2xxx_runtime_resume(struct device *dev) { struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev); struct pm2xxx_charger *pm2; @@ -975,15 +969,11 @@ static int pm2xxx_runtime_resume(struct device *dev) return 0; } -static const struct dev_pm_ops pm2xxx_pm_ops = { +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) }; -#define PM2XXX_PM_OPS (&pm2xxx_pm_ops) -#else -#define PM2XXX_PM_OPS NULL -#endif static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id) @@ -1244,7 +1234,7 @@ static struct i2c_driver pm2xxx_charger_driver = { .remove = pm2xxx_wall_charger_remove, .driver = { .name = "pm2xxx-wall_charger", - .pm = PM2XXX_PM_OPS, + .pm = IS_ENABLED(CONFIG_PM) ? &pm2xxx_pm_ops : NULL, }, .id_table = pm2xxx_id, }; diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index ed2d7fd0c734..80fed98832f9 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -45,7 +45,8 @@ static ssize_t power_supply_show_property(struct device *dev, char *buf) { static char *type_text[] = { "Unknown", "Battery", "UPS", "Mains", "USB", - "USB_DCP", "USB_CDP", "USB_ACA" + "USB_DCP", "USB_CDP", "USB_ACA", "USB_C", + "USB_PD", "USB_PD_DRP" }; static char *status_text[] = { "Unknown", "Charging", "Discharging", "Not charging", "Full" diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 1131cf75acc6..0a6408a39c66 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -148,6 +148,7 @@ config POWER_RESET_KEYSTONE config POWER_RESET_SYSCON bool "Generic SYSCON regmap reset driver" depends on OF + depends on HAS_IOMEM select MFD_SYSCON help Reboot support for generic SYSCON mapped register reset. @@ -155,6 +156,7 @@ config POWER_RESET_SYSCON config POWER_RESET_SYSCON_POWEROFF bool "Generic SYSCON regmap poweroff driver" depends on OF + depends on HAS_IOMEM select MFD_SYSCON help Poweroff support for generic SYSCON mapped register poweroff. diff --git a/drivers/power/reset/arm-versatile-reboot.c b/drivers/power/reset/arm-versatile-reboot.c index b208073c887d..06d34ab47df5 100644 --- a/drivers/power/reset/arm-versatile-reboot.c +++ b/drivers/power/reset/arm-versatile-reboot.c @@ -18,8 +18,8 @@ #define INTEGRATOR_HDR_LOCK_OFFSET 0x14 #define INTEGRATOR_CM_CTRL_RESET (1 << 3) -#define REALVIEW_SYS_LOCK_OFFSET 0x20 -#define REALVIEW_SYS_RESETCTL_OFFSET 0x40 +#define VERSATILE_SYS_LOCK_OFFSET 0x20 +#define VERSATILE_SYS_RESETCTL_OFFSET 0x40 /* Magic unlocking token used on all Versatile boards */ #define VERSATILE_LOCK_VAL 0xA05F @@ -29,6 +29,7 @@ */ enum versatile_reboot { INTEGRATOR_REBOOT_CM, + VERSATILE_REBOOT_CM, REALVIEW_REBOOT_EB, REALVIEW_REBOOT_PB1176, REALVIEW_REBOOT_PB11MP, @@ -46,6 +47,10 @@ static const struct of_device_id versatile_reboot_of_match[] = { .data = (void *)INTEGRATOR_REBOOT_CM }, { + .compatible = "arm,core-module-versatile", + .data = (void *)VERSATILE_REBOOT_CM, + }, + { .compatible = "arm,realview-eb-syscon", .data = (void *)REALVIEW_REBOOT_EB, }, @@ -82,33 +87,43 @@ static int versatile_reboot(struct notifier_block *this, unsigned long mode, INTEGRATOR_CM_CTRL_RESET, INTEGRATOR_CM_CTRL_RESET); break; + case VERSATILE_REBOOT_CM: + regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET, + VERSATILE_LOCK_VAL); + regmap_update_bits(syscon_regmap, + VERSATILE_SYS_RESETCTL_OFFSET, + 0x0107, + 0x0105); + regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET, + 0); + break; case REALVIEW_REBOOT_EB: - regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, + regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET, VERSATILE_LOCK_VAL); regmap_write(syscon_regmap, - REALVIEW_SYS_RESETCTL_OFFSET, 0x0008); + VERSATILE_SYS_RESETCTL_OFFSET, 0x0008); break; case REALVIEW_REBOOT_PB1176: - regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, + regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET, VERSATILE_LOCK_VAL); regmap_write(syscon_regmap, - REALVIEW_SYS_RESETCTL_OFFSET, 0x0100); + VERSATILE_SYS_RESETCTL_OFFSET, 0x0100); break; case REALVIEW_REBOOT_PB11MP: case REALVIEW_REBOOT_PBA8: - regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, + regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET, VERSATILE_LOCK_VAL); - regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, + regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET, 0x0000); - regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, + regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET, 0x0004); break; case REALVIEW_REBOOT_PBX: - regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, + regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET, VERSATILE_LOCK_VAL); - regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, + regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET, 0x00f0); - regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, + regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET, 0x00f4); break; } diff --git a/include/linux/power/bq24735-charger.h b/include/linux/power/bq24735-charger.h index f536164a6069..6b750c1a45fa 100644 --- a/include/linux/power/bq24735-charger.h +++ b/include/linux/power/bq24735-charger.h @@ -32,6 +32,8 @@ struct bq24735_platform { int status_gpio_active_low; bool status_gpio_valid; + bool ext_control; + char **supplied_to; size_t num_supplicants; }; diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index ef9f1592185d..751061790626 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -163,6 +163,9 @@ enum power_supply_type { POWER_SUPPLY_TYPE_USB_DCP, /* Dedicated Charging Port */ POWER_SUPPLY_TYPE_USB_CDP, /* Charging Downstream Port */ POWER_SUPPLY_TYPE_USB_ACA, /* Accessory Charger Adapters */ + POWER_SUPPLY_TYPE_USB_TYPE_C, /* Type C Port */ + POWER_SUPPLY_TYPE_USB_PD, /* Power Delivery Port */ + POWER_SUPPLY_TYPE_USB_PD_DRP, /* PD Dual Role Port */ }; enum power_supply_notifier_events { |