diff --git a/Documentation/devicetree/bindings/power/reset/gpio-poweroff.txt b/Documentation/devicetree/bindings/power/reset/gpio-poweroff.txt index e62d53d844cc..6d8980c18c34 100644 --- a/Documentation/devicetree/bindings/power/reset/gpio-poweroff.txt +++ b/Documentation/devicetree/bindings/power/reset/gpio-poweroff.txt @@ -27,10 +27,13 @@ Optional properties: it to an output when the power-off handler is called. If this optional property is not specified, the GPIO is initialized as an output in its inactive state. +- timeout-ms: Time to wait before asserting a WARN_ON(1). If nothing is + specified, 3000 ms is used. Examples: gpio-poweroff { compatible = "gpio-poweroff"; gpios = <&gpio 4 0>; + timeout-ms = <3000>; }; diff --git a/Documentation/devicetree/bindings/power/reset/ocelot-reset.txt b/Documentation/devicetree/bindings/power/reset/ocelot-reset.txt new file mode 100644 index 000000000000..1b4213eb3473 --- /dev/null +++ b/Documentation/devicetree/bindings/power/reset/ocelot-reset.txt @@ -0,0 +1,14 @@ +Microsemi Ocelot reset controller + +The DEVCPU_GCB:CHIP_REGS have a SOFT_RST register that can be used to reset the +SoC MIPS core. + +Required Properties: + - compatible: "mscc,ocelot-chip-reset" + +Example: + reset@1070008 { + compatible = "mscc,ocelot-chip-reset"; + reg = <0x1070008 0x4>; + }; + diff --git a/Documentation/devicetree/bindings/power/supply/axp20x_battery.txt b/Documentation/devicetree/bindings/power/supply/axp20x_battery.txt index c24886676a60..41916f69902c 100644 --- a/Documentation/devicetree/bindings/power/supply/axp20x_battery.txt +++ b/Documentation/devicetree/bindings/power/supply/axp20x_battery.txt @@ -4,12 +4,12 @@ Required Properties: - compatible, one of: "x-powers,axp209-battery-power-supply" "x-powers,axp221-battery-power-supply" + "x-powers,axp813-battery-power-supply" -This node is a subnode of the axp20x/axp22x PMIC. +This node is a subnode of its respective PMIC DT node. -The AXP20X and AXP22X can read the battery voltage, charge and discharge -currents of the battery by reading ADC channels from the AXP20X/AXP22X -ADC. +The supported devices can read the battery voltage, charge and discharge +currents of the battery by reading ADC channels from the ADC. Example: diff --git a/arch/arm/boot/dts/omap3-n900.dts b/arch/arm/boot/dts/omap3-n900.dts index ab930581fc7a..182a53991c90 100644 --- a/arch/arm/boot/dts/omap3-n900.dts +++ b/arch/arm/boot/dts/omap3-n900.dts @@ -673,6 +673,7 @@ bq27200: bq27200@55 { compatible = "ti,bq27200"; reg = <0x55>; + power-supplies = <&bq24150a>; }; /* Stereo headphone amplifier */ diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index a102e74ab24e..df58fc878b3e 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -104,6 +104,13 @@ config POWER_RESET_MSM help Power off and restart support for Qualcomm boards. +config POWER_RESET_OCELOT_RESET + bool "Microsemi Ocelot reset driver" + depends on MSCC_OCELOT || COMPILE_TEST + select MFD_SYSCON + help + This driver supports restart for Microsemi Ocelot SoC. + config POWER_RESET_PIIX4_POWEROFF tristate "Intel PIIX4 power-off driver" depends on PCI @@ -218,5 +225,14 @@ config SYSCON_REBOOT_MODE register, then the bootloader can read it to take different action according to the mode. +config POWER_RESET_SC27XX + bool "Spreadtrum SC27xx PMIC power-off driver" + depends on MFD_SC27XX_PMIC || COMPILE_TEST + help + This driver supports powering off a system through + Spreadtrum SC27xx series PMICs. The SC27xx series + PMICs includes the SC2720, SC2721, SC2723, SC2730 + and SC2731 chips. + endif diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index dcc92f5f7a37..7778c7485cf1 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o +obj-$(CONFIG_POWER_RESET_OCELOT_RESET) += ocelot-reset.o obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o @@ -26,3 +27,4 @@ obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o obj-$(CONFIG_POWER_RESET_ZX) += zx-reboot.o obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o +obj-$(CONFIG_POWER_RESET_SC27XX) += sc27xx-poweroff.o diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c index c30c40193aaa..fb2fc8f741a1 100644 --- a/drivers/power/reset/at91-poweroff.c +++ b/drivers/power/reset/at91-poweroff.c @@ -55,10 +55,10 @@ static void __iomem *at91_shdwc_base; static struct clk *sclk; static void __iomem *mpddrc_base; -static void __init at91_wakeup_status(void) +static void __init at91_wakeup_status(struct platform_device *pdev) { + const char *reason; u32 reg = readl(at91_shdwc_base + AT91_SHDW_SR); - char *reason = "unknown"; /* Simple power-on, just bail out */ if (!reg) @@ -68,8 +68,10 @@ static void __init at91_wakeup_status(void) reason = "RTT"; else if (reg & AT91_SHDW_RTCWK) reason = "RTC"; + else + reason = "unknown"; - pr_info("AT91: Wake-Up source: %s\n", reason); + dev_info(&pdev->dev, "Wake-Up source: %s\n", reason); } static void at91_poweroff(void) @@ -157,10 +159,8 @@ static int __init at91_poweroff_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(at91_shdwc_base)) { - dev_err(&pdev->dev, "Could not map reset controller address\n"); + if (IS_ERR(at91_shdwc_base)) return PTR_ERR(at91_shdwc_base); - } sclk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(sclk)) @@ -172,7 +172,7 @@ static int __init at91_poweroff_probe(struct platform_device *pdev) return ret; } - at91_wakeup_status(); + at91_wakeup_status(pdev); if (pdev->dev.of_node) at91_poweroff_dt_set_wakeup_mode(pdev); diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c index b99769f8ab15..f44a9ffcc2ab 100644 --- a/drivers/power/reset/at91-reset.c +++ b/drivers/power/reset/at91-reset.c @@ -145,8 +145,8 @@ static int samx7_restart(struct notifier_block *this, unsigned long mode, static void __init at91_reset_status(struct platform_device *pdev) { + const char *reason; u32 reg = readl(at91_rstc_base + AT91_RSTC_SR); - char *reason; switch ((reg & AT91_RSTC_RSTTYP) >> 8) { case RESET_TYPE_GENERAL: @@ -169,7 +169,7 @@ static void __init at91_reset_status(struct platform_device *pdev) break; } - pr_info("AT91: Starting after %s\n", reason); + dev_info(&pdev->dev, "Starting after %s\n", reason); } static const struct of_device_id at91_ramc_of_match[] = { diff --git a/drivers/power/reset/gemini-poweroff.c b/drivers/power/reset/gemini-poweroff.c index ff75af5abbc5..2ac291af1265 100644 --- a/drivers/power/reset/gemini-poweroff.c +++ b/drivers/power/reset/gemini-poweroff.c @@ -47,8 +47,12 @@ static irqreturn_t gemini_powerbutton_interrupt(int irq, void *data) val &= 0x70U; switch (val) { case GEMINI_STAT_CIR: - dev_info(gpw->dev, "infrared poweroff\n"); - orderly_poweroff(true); + /* + * We do not yet have a driver for the infrared + * controller so it can cause spurious poweroff + * events. Ignore those for now. + */ + dev_info(gpw->dev, "infrared poweroff - ignored\n"); break; case GEMINI_STAT_RTC: dev_info(gpw->dev, "RTC poweroff\n"); @@ -116,7 +120,17 @@ static int gemini_poweroff_probe(struct platform_device *pdev) return -ENODEV; } - /* Clear the power management IRQ */ + /* + * Enable the power controller. This is crucial on Gemini + * systems: if this is not done, pressing the power button + * will result in unconditional poweroff without any warning. + * This makes the kernel handle the poweroff. + */ + val = readl(gpw->base + GEMINI_PWC_CTRLREG); + val |= GEMINI_CTRL_ENABLE; + writel(val, gpw->base + GEMINI_PWC_CTRLREG); + + /* Now that the state machine is active, clear the IRQ */ val = readl(gpw->base + GEMINI_PWC_CTRLREG); val |= GEMINI_CTRL_IRQ_CLR; writel(val, gpw->base + GEMINI_PWC_CTRLREG); @@ -129,16 +143,6 @@ static int gemini_poweroff_probe(struct platform_device *pdev) pm_power_off = gemini_poweroff; gpw_poweroff = gpw; - /* - * Enable the power controller. This is crucial on Gemini - * systems: if this is not done, pressing the power button - * will result in unconditional poweroff without any warning. - * This makes the kernel handle the poweroff. - */ - val = readl(gpw->base + GEMINI_PWC_CTRLREG); - val |= GEMINI_CTRL_ENABLE; - writel(val, gpw->base + GEMINI_PWC_CTRLREG); - dev_info(dev, "Gemini poweroff driver registered\n"); return 0; diff --git a/drivers/power/reset/gpio-poweroff.c b/drivers/power/reset/gpio-poweroff.c index be3d81ff51cc..6273ad3b411d 100644 --- a/drivers/power/reset/gpio-poweroff.c +++ b/drivers/power/reset/gpio-poweroff.c @@ -19,11 +19,13 @@ #include #include +#define DEFAULT_TIMEOUT_MS 3000 /* * Hold configuration here, cannot be more than one instance of the driver * since pm_power_off itself is global. */ static struct gpio_desc *reset_gpio; +static u32 timeout = DEFAULT_TIMEOUT_MS; static void gpio_poweroff_do_poweroff(void) { @@ -40,7 +42,7 @@ static void gpio_poweroff_do_poweroff(void) gpiod_set_value(reset_gpio, 1); /* give it some time */ - mdelay(3000); + mdelay(timeout); WARN_ON(1); } @@ -58,12 +60,14 @@ static int gpio_poweroff_probe(struct platform_device *pdev) return -EBUSY; } - input = of_property_read_bool(pdev->dev.of_node, "input"); + input = device_property_read_bool(&pdev->dev, "input"); if (input) flags = GPIOD_IN; else flags = GPIOD_OUT_LOW; + device_property_read_u32(&pdev->dev, "timeout-ms", &timeout); + reset_gpio = devm_gpiod_get(&pdev->dev, NULL, flags); if (IS_ERR(reset_gpio)) return PTR_ERR(reset_gpio); diff --git a/drivers/power/reset/ocelot-reset.c b/drivers/power/reset/ocelot-reset.c new file mode 100644 index 000000000000..5a13a5cc8188 --- /dev/null +++ b/drivers/power/reset/ocelot-reset.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Microsemi MIPS SoC reset driver + * + * License: Dual MIT/GPL + * Copyright (c) 2017 Microsemi Corporation + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ocelot_reset_context { + void __iomem *base; + struct regmap *cpu_ctrl; + struct notifier_block restart_handler; +}; + +#define ICPU_CFG_CPU_SYSTEM_CTRL_RESET 0x20 +#define CORE_RST_PROTECT BIT(2) + +#define SOFT_CHIP_RST BIT(0) + +static int ocelot_restart_handle(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + struct ocelot_reset_context *ctx = container_of(this, struct + ocelot_reset_context, + restart_handler); + + /* Make sure the core is not protected from reset */ + regmap_update_bits(ctx->cpu_ctrl, ICPU_CFG_CPU_SYSTEM_CTRL_RESET, + CORE_RST_PROTECT, 0); + + writel(SOFT_CHIP_RST, ctx->base); + + pr_emerg("Unable to restart system\n"); + return NOTIFY_DONE; +} + +static int ocelot_reset_probe(struct platform_device *pdev) +{ + struct ocelot_reset_context *ctx; + struct resource *res; + + struct device *dev = &pdev->dev; + int err; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ctx->base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctx->base)) + return PTR_ERR(ctx->base); + + ctx->cpu_ctrl = syscon_regmap_lookup_by_compatible("mscc,ocelot-cpu-syscon"); + if (IS_ERR(ctx->cpu_ctrl)) + return PTR_ERR(ctx->cpu_ctrl); + + ctx->restart_handler.notifier_call = ocelot_restart_handle; + ctx->restart_handler.priority = 192; + err = register_restart_handler(&ctx->restart_handler); + if (err) + dev_err(dev, "can't register restart notifier (err=%d)\n", err); + + return err; +} + +static const struct of_device_id ocelot_reset_of_match[] = { + { .compatible = "mscc,ocelot-chip-reset" }, + {} +}; + +static struct platform_driver ocelot_reset_driver = { + .probe = ocelot_reset_probe, + .driver = { + .name = "ocelot-chip-reset", + .of_match_table = ocelot_reset_of_match, + }, +}; +builtin_platform_driver(ocelot_reset_driver); diff --git a/drivers/power/reset/sc27xx-poweroff.c b/drivers/power/reset/sc27xx-poweroff.c new file mode 100644 index 000000000000..29fb08b8faa0 --- /dev/null +++ b/drivers/power/reset/sc27xx-poweroff.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Spreadtrum Communications Inc. + * Copyright (C) 2018 Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#define SC27XX_PWR_PD_HW 0xc2c +#define SC27XX_PWR_OFF_EN BIT(0) + +static struct regmap *regmap; + +/* + * On Spreadtrum platform, we need power off system through external SC27xx + * series PMICs, and it is one similar SPI bus mapped by regmap to access PMIC, + * which is not fast io access. + * + * So before stopping other cores, we need release other cores' resource by + * taking cpus down to avoid racing regmap or spi mutex lock when poweroff + * system through PMIC. + */ +static void sc27xx_poweroff_shutdown(void) +{ +#ifdef CONFIG_PM_SLEEP_SMP + int cpu = smp_processor_id(); + + freeze_secondary_cpus(cpu); +#endif +} + +static struct syscore_ops poweroff_syscore_ops = { + .shutdown = sc27xx_poweroff_shutdown, +}; + +static void sc27xx_poweroff_do_poweroff(void) +{ + regmap_write(regmap, SC27XX_PWR_PD_HW, SC27XX_PWR_OFF_EN); +} + +static int sc27xx_poweroff_probe(struct platform_device *pdev) +{ + if (regmap) + return -EINVAL; + + regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!regmap) + return -ENODEV; + + pm_power_off = sc27xx_poweroff_do_poweroff; + register_syscore_ops(&poweroff_syscore_ops); + return 0; +} + +static struct platform_driver sc27xx_poweroff_driver = { + .probe = sc27xx_poweroff_probe, + .driver = { + .name = "sc27xx-poweroff", + }, +}; +builtin_platform_driver(sc27xx_poweroff_driver); diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c index 7494f0f0eadb..e84b6e4da14a 100644 --- a/drivers/power/supply/axp20x_battery.c +++ b/drivers/power/supply/axp20x_battery.c @@ -49,10 +49,22 @@ #define AXP22X_CHRG_CTRL1_TGT_4_22V (1 << 5) #define AXP22X_CHRG_CTRL1_TGT_4_24V (3 << 5) +#define AXP813_CHRG_CTRL1_TGT_4_35V (3 << 5) + #define AXP20X_CHRG_CTRL1_TGT_CURR GENMASK(3, 0) #define AXP20X_V_OFF_MASK GENMASK(2, 0) +struct axp20x_batt_ps; + +struct axp_data { + int ccc_scale; + int ccc_offset; + bool has_fg_valid; + int (*get_max_voltage)(struct axp20x_batt_ps *batt, int *val); + int (*set_max_voltage)(struct axp20x_batt_ps *batt, int val); +}; + struct axp20x_batt_ps { struct regmap *regmap; struct power_supply *batt; @@ -62,7 +74,7 @@ struct axp20x_batt_ps { struct iio_channel *batt_v; /* Maximum constant charge current */ unsigned int max_ccc; - u8 axp_id; + const struct axp_data *data; }; static int axp20x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, @@ -123,20 +135,33 @@ static int axp22x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, return 0; } -static void raw_to_constant_charge_current(struct axp20x_batt_ps *axp, int *val) +static int axp813_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, + int *val) { - if (axp->axp_id == AXP209_ID) - *val = *val * 100000 + 300000; - else - *val = *val * 150000 + 300000; -} + int ret, reg; -static void constant_charge_current_to_raw(struct axp20x_batt_ps *axp, int *val) -{ - if (axp->axp_id == AXP209_ID) - *val = (*val - 300000) / 100000; - else - *val = (*val - 300000) / 150000; + ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, ®); + if (ret) + return ret; + + switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) { + case AXP20X_CHRG_CTRL1_TGT_4_1V: + *val = 4100000; + break; + case AXP20X_CHRG_CTRL1_TGT_4_15V: + *val = 4150000; + break; + case AXP20X_CHRG_CTRL1_TGT_4_2V: + *val = 4200000; + break; + case AXP813_CHRG_CTRL1_TGT_4_35V: + *val = 4350000; + break; + default: + return -EINVAL; + } + + return 0; } static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp, @@ -150,7 +175,7 @@ static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp, *val &= AXP20X_CHRG_CTRL1_TGT_CURR; - raw_to_constant_charge_current(axp, val); + *val = *val * axp->data->ccc_scale + axp->data->ccc_offset; return 0; } @@ -269,8 +294,7 @@ static int axp20x_battery_get_prop(struct power_supply *psy, if (ret) return ret; - if (axp20x_batt->axp_id == AXP221_ID && - !(reg & AXP22X_FG_VALID)) + if (axp20x_batt->data->has_fg_valid && !(reg & AXP22X_FG_VALID)) return -EINVAL; /* @@ -281,11 +305,8 @@ static int axp20x_battery_get_prop(struct power_supply *psy, break; case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - if (axp20x_batt->axp_id == AXP209_ID) - return axp20x_battery_get_max_voltage(axp20x_batt, - &val->intval); - return axp22x_battery_get_max_voltage(axp20x_batt, - &val->intval); + return axp20x_batt->data->get_max_voltage(axp20x_batt, + &val->intval); case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: ret = regmap_read(axp20x_batt->regmap, AXP20X_V_OFF, ®); @@ -312,6 +333,32 @@ static int axp20x_battery_get_prop(struct power_supply *psy, return 0; } +static int axp22x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt, + int val) +{ + switch (val) { + case 4100000: + val = AXP20X_CHRG_CTRL1_TGT_4_1V; + break; + + case 4200000: + val = AXP20X_CHRG_CTRL1_TGT_4_2V; + break; + + default: + /* + * AXP20x max voltage can be set to 4.36V and AXP22X max voltage + * can be set to 4.22V and 4.24V, but these voltages are too + * high for Lithium based batteries (AXP PMICs are supposed to + * be used with these kinds of battery). + */ + return -EINVAL; + } + + return regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, + AXP20X_CHRG_CTRL1_TGT_VOLT, val); +} + static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt, int val) { @@ -321,9 +368,6 @@ static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt, break; case 4150000: - if (axp20x_batt->axp_id == AXP221_ID) - return -EINVAL; - val = AXP20X_CHRG_CTRL1_TGT_4_15V; break; @@ -351,7 +395,8 @@ static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt, if (charge_current > axp_batt->max_ccc) return -EINVAL; - constant_charge_current_to_raw(axp_batt, &charge_current); + charge_current = (charge_current - axp_batt->data->ccc_offset) / + axp_batt->data->ccc_scale; if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0) return -EINVAL; @@ -365,12 +410,14 @@ static int axp20x_set_max_constant_charge_current(struct axp20x_batt_ps *axp, { bool lower_max = false; - constant_charge_current_to_raw(axp, &charge_current); + charge_current = (charge_current - axp->data->ccc_offset) / + axp->data->ccc_scale; if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0) return -EINVAL; - raw_to_constant_charge_current(axp, &charge_current); + charge_current = charge_current * axp->data->ccc_scale + + axp->data->ccc_offset; if (charge_current > axp->max_ccc) dev_warn(axp->dev, @@ -413,7 +460,7 @@ static int axp20x_battery_set_prop(struct power_supply *psy, return axp20x_set_voltage_min_design(axp20x_batt, val->intval); case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - return axp20x_battery_set_max_voltage(axp20x_batt, val->intval); + return axp20x_batt->data->set_max_voltage(axp20x_batt, val->intval); case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: return axp20x_set_constant_charge_current(axp20x_batt, @@ -460,13 +507,39 @@ static const struct power_supply_desc axp20x_batt_ps_desc = { .set_property = axp20x_battery_set_prop, }; +static const struct axp_data axp209_data = { + .ccc_scale = 100000, + .ccc_offset = 300000, + .get_max_voltage = axp20x_battery_get_max_voltage, + .set_max_voltage = axp20x_battery_set_max_voltage, +}; + +static const struct axp_data axp221_data = { + .ccc_scale = 150000, + .ccc_offset = 300000, + .has_fg_valid = true, + .get_max_voltage = axp22x_battery_get_max_voltage, + .set_max_voltage = axp22x_battery_set_max_voltage, +}; + +static const struct axp_data axp813_data = { + .ccc_scale = 200000, + .ccc_offset = 200000, + .has_fg_valid = true, + .get_max_voltage = axp813_battery_get_max_voltage, + .set_max_voltage = axp20x_battery_set_max_voltage, +}; + static const struct of_device_id axp20x_battery_ps_id[] = { { .compatible = "x-powers,axp209-battery-power-supply", - .data = (void *)AXP209_ID, + .data = (void *)&axp209_data, }, { .compatible = "x-powers,axp221-battery-power-supply", - .data = (void *)AXP221_ID, + .data = (void *)&axp221_data, + }, { + .compatible = "x-powers,axp813-battery-power-supply", + .data = (void *)&axp813_data, }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, axp20x_battery_ps_id); @@ -476,6 +549,7 @@ static int axp20x_power_probe(struct platform_device *pdev) struct axp20x_batt_ps *axp20x_batt; struct power_supply_config psy_cfg = {}; struct power_supply_battery_info info; + struct device *dev = &pdev->dev; if (!of_device_is_available(pdev->dev.of_node)) return -ENODEV; @@ -516,7 +590,7 @@ static int axp20x_power_probe(struct platform_device *pdev) psy_cfg.drv_data = axp20x_batt; psy_cfg.of_node = pdev->dev.of_node; - axp20x_batt->axp_id = (uintptr_t)of_device_get_match_data(&pdev->dev); + axp20x_batt->data = (struct axp_data *)of_device_get_match_data(dev); axp20x_batt->batt = devm_power_supply_register(&pdev->dev, &axp20x_batt_ps_desc, diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index 4cc6e038dfdd..fd8f0b2210bc 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -343,7 +343,7 @@ static inline void fuel_gauge_remove_debugfs(struct axp288_fg_info *info) static void fuel_gauge_get_status(struct axp288_fg_info *info) { - int pwr_stat, fg_res; + int pwr_stat, fg_res, curr, ret; pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS); if (pwr_stat < 0) { @@ -353,19 +353,42 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info) } /* Report full if Vbus is valid and the reported capacity is 100% */ - if (pwr_stat & PS_STAT_VBUS_VALID) { - 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 | 100)) { - info->status = POWER_SUPPLY_STATUS_FULL; - return; - } + 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; + + fg_res &= ~FG_REP_CAP_VALID; + if (fg_res == 100) { + info->status = POWER_SUPPLY_STATUS_FULL; + return; } + /* + * Sometimes the charger turns itself off before fg-res reaches 100%. + * When this happens the AXP288 reports a not-charging status and + * 0 mA discharge current. + */ + 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; + } + +not_full: if (pwr_stat & PS_STAT_BAT_CHRG_DIR) info->status = POWER_SUPPLY_STATUS_CHARGING; else @@ -708,6 +731,12 @@ static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = { DMI_MATCH(DMI_BOARD_VERSION, "V1.1"), }, }, + { + /* ECS EF20EA */ + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "EF20EA"), + }, + }, {} }; diff --git a/drivers/power/supply/bq2415x_charger.c b/drivers/power/supply/bq2415x_charger.c index c4770a94cc8e..cbec70f3e73e 100644 --- a/drivers/power/supply/bq2415x_charger.c +++ b/drivers/power/supply/bq2415x_charger.c @@ -1037,7 +1037,10 @@ static int bq2415x_power_supply_init(struct bq2415x_device *bq) int ret; int chip; char revstr[8]; - struct power_supply_config psy_cfg = { .drv_data = bq, }; + struct power_supply_config psy_cfg = { + .drv_data = bq, + .of_node = bq->dev->of_node, + }; bq->charger_desc.name = bq->name; bq->charger_desc.type = POWER_SUPPLY_TYPE_USB; diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index d99981542a46..7ce60519b1bc 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -1670,7 +1670,7 @@ static int bq27xxx_battery_status(struct bq27xxx_device_info *di, status = POWER_SUPPLY_STATUS_FULL; else if (di->cache.flags & BQ27000_FLAG_CHGS) status = POWER_SUPPLY_STATUS_CHARGING; - else if (power_supply_am_i_supplied(di->bat)) + else if (power_supply_am_i_supplied(di->bat) > 0) status = POWER_SUPPLY_STATUS_NOT_CHARGING; else status = POWER_SUPPLY_STATUS_DISCHARGING; diff --git a/drivers/power/supply/da9150-fg.c b/drivers/power/supply/da9150-fg.c index 8b8ce978656a..1e2e5b0520c9 100644 --- a/drivers/power/supply/da9150-fg.c +++ b/drivers/power/supply/da9150-fg.c @@ -92,7 +92,7 @@ struct da9150_fg { static u32 da9150_fg_read_attr(struct da9150_fg *fg, u8 code, u8 size) { - u8 buf[size]; + u8 buf[DA9150_QIF_LONG_SIZE]; u8 read_addr; u32 res = 0; int i; @@ -111,7 +111,7 @@ static void da9150_fg_write_attr(struct da9150_fg *fg, u8 code, u8 size, u32 val) { - u8 buf[size]; + u8 buf[DA9150_QIF_LONG_SIZE]; u8 write_addr; int i; diff --git a/drivers/power/supply/gpio-charger.c b/drivers/power/supply/gpio-charger.c index 001731e88718..bd2468ca6b63 100644 --- a/drivers/power/supply/gpio-charger.c +++ b/drivers/power/supply/gpio-charger.c @@ -28,14 +28,12 @@ #include struct gpio_charger { - const struct gpio_charger_platform_data *pdata; unsigned int irq; bool wakeup_enabled; struct power_supply *charger; struct power_supply_desc charger_desc; struct gpio_desc *gpiod; - bool legacy_gpio_requested; }; static irqreturn_t gpio_charger_irq(int irq, void *devid) @@ -56,13 +54,10 @@ static int gpio_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy); - const struct gpio_charger_platform_data *pdata = gpio_charger->pdata; switch (psp) { case POWER_SUPPLY_PROP_ONLINE: val->intval = gpiod_get_value_cansleep(gpio_charger->gpiod); - /* This xor is only ever used with legacy pdata GPIO */ - val->intval ^= pdata->gpio_active_low; break; default: return -EINVAL; @@ -71,175 +66,134 @@ static int gpio_charger_get_property(struct power_supply *psy, return 0; } +static enum power_supply_type gpio_charger_get_type(struct device *dev) +{ + const char *chargetype; + + if (!device_property_read_string(dev, "charger-type", &chargetype)) { + if (!strcmp("unknown", chargetype)) + return POWER_SUPPLY_TYPE_UNKNOWN; + if (!strcmp("battery", chargetype)) + return POWER_SUPPLY_TYPE_BATTERY; + if (!strcmp("ups", chargetype)) + return POWER_SUPPLY_TYPE_UPS; + if (!strcmp("mains", chargetype)) + return POWER_SUPPLY_TYPE_MAINS; + if (!strcmp("usb-sdp", chargetype)) + return POWER_SUPPLY_TYPE_USB; + if (!strcmp("usb-dcp", chargetype)) + return POWER_SUPPLY_TYPE_USB_DCP; + if (!strcmp("usb-cdp", chargetype)) + return POWER_SUPPLY_TYPE_USB_CDP; + if (!strcmp("usb-aca", chargetype)) + return POWER_SUPPLY_TYPE_USB_ACA; + } + dev_warn(dev, "unknown charger type %s\n", chargetype); + + return POWER_SUPPLY_TYPE_UNKNOWN; +} + static enum power_supply_property gpio_charger_properties[] = { POWER_SUPPLY_PROP_ONLINE, }; -static -struct gpio_charger_platform_data *gpio_charger_parse_dt(struct device *dev) -{ - struct device_node *np = dev->of_node; - struct gpio_charger_platform_data *pdata; - const char *chargetype; - int ret; - - if (!np) - return ERR_PTR(-ENOENT); - - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return ERR_PTR(-ENOMEM); - - pdata->name = np->name; - pdata->type = POWER_SUPPLY_TYPE_UNKNOWN; - ret = of_property_read_string(np, "charger-type", &chargetype); - if (ret >= 0) { - if (!strncmp("unknown", chargetype, 7)) - pdata->type = POWER_SUPPLY_TYPE_UNKNOWN; - else if (!strncmp("battery", chargetype, 7)) - pdata->type = POWER_SUPPLY_TYPE_BATTERY; - else if (!strncmp("ups", chargetype, 3)) - pdata->type = POWER_SUPPLY_TYPE_UPS; - else if (!strncmp("mains", chargetype, 5)) - pdata->type = POWER_SUPPLY_TYPE_MAINS; - else if (!strncmp("usb-sdp", chargetype, 7)) - pdata->type = POWER_SUPPLY_TYPE_USB; - else if (!strncmp("usb-dcp", chargetype, 7)) - pdata->type = POWER_SUPPLY_TYPE_USB_DCP; - else if (!strncmp("usb-cdp", chargetype, 7)) - pdata->type = POWER_SUPPLY_TYPE_USB_CDP; - else if (!strncmp("usb-aca", chargetype, 7)) - pdata->type = POWER_SUPPLY_TYPE_USB_ACA; - else - dev_warn(dev, "unknown charger type %s\n", chargetype); - } - - return pdata; -} - static int gpio_charger_probe(struct platform_device *pdev) { - const struct gpio_charger_platform_data *pdata = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + const struct gpio_charger_platform_data *pdata = dev->platform_data; struct power_supply_config psy_cfg = {}; struct gpio_charger *gpio_charger; struct power_supply_desc *charger_desc; - int ret; - int irq; + unsigned long flags; + int irq, ret; - if (!pdata) { - pdata = gpio_charger_parse_dt(&pdev->dev); - if (IS_ERR(pdata)) { - ret = PTR_ERR(pdata); - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "No platform data\n"); - return ret; - } + if (!pdata && !dev->of_node) { + dev_err(dev, "No platform data\n"); + return -ENOENT; } - gpio_charger = devm_kzalloc(&pdev->dev, sizeof(*gpio_charger), - GFP_KERNEL); - if (!gpio_charger) { - dev_err(&pdev->dev, "Failed to alloc driver structure\n"); + gpio_charger = devm_kzalloc(dev, sizeof(*gpio_charger), GFP_KERNEL); + if (!gpio_charger) return -ENOMEM; - } /* * This will fetch a GPIO descriptor from device tree, ACPI or * boardfile descriptor tables. It's good to try this first. */ - gpio_charger->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_IN); + gpio_charger->gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN); /* * If this fails and we're not using device tree, try the * legacy platform data method. */ - if (IS_ERR(gpio_charger->gpiod) && !pdev->dev.of_node) { + if (IS_ERR(gpio_charger->gpiod) && !dev->of_node) { /* Non-DT: use legacy GPIO numbers */ if (!gpio_is_valid(pdata->gpio)) { - dev_err(&pdev->dev, "Invalid gpio pin in pdata\n"); + dev_err(dev, "Invalid gpio pin in pdata\n"); return -EINVAL; } - ret = gpio_request(pdata->gpio, dev_name(&pdev->dev)); + flags = GPIOF_IN; + if (pdata->gpio_active_low) + flags |= GPIOF_ACTIVE_LOW; + ret = devm_gpio_request_one(dev, pdata->gpio, flags, + dev_name(dev)); if (ret) { - dev_err(&pdev->dev, "Failed to request gpio pin: %d\n", - ret); + dev_err(dev, "Failed to request gpio pin: %d\n", ret); return ret; } - gpio_charger->legacy_gpio_requested = true; - ret = gpio_direction_input(pdata->gpio); - if (ret) { - dev_err(&pdev->dev, "Failed to set gpio to input: %d\n", - ret); - goto err_gpio_free; - } /* Then convert this to gpiod for now */ gpio_charger->gpiod = gpio_to_desc(pdata->gpio); } else if (IS_ERR(gpio_charger->gpiod)) { /* Just try again if this happens */ if (PTR_ERR(gpio_charger->gpiod) == -EPROBE_DEFER) return -EPROBE_DEFER; - dev_err(&pdev->dev, "error getting GPIO descriptor\n"); + dev_err(dev, "error getting GPIO descriptor\n"); return PTR_ERR(gpio_charger->gpiod); } charger_desc = &gpio_charger->charger_desc; - - charger_desc->name = pdata->name ? pdata->name : "gpio-charger"; - charger_desc->type = pdata->type; charger_desc->properties = gpio_charger_properties; charger_desc->num_properties = ARRAY_SIZE(gpio_charger_properties); charger_desc->get_property = gpio_charger_get_property; - psy_cfg.supplied_to = pdata->supplied_to; - psy_cfg.num_supplicants = pdata->num_supplicants; - psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.of_node = dev->of_node; psy_cfg.drv_data = gpio_charger; - gpio_charger->pdata = pdata; + if (pdata) { + charger_desc->name = pdata->name; + charger_desc->type = pdata->type; + psy_cfg.supplied_to = pdata->supplied_to; + psy_cfg.num_supplicants = pdata->num_supplicants; + } else { + charger_desc->name = dev->of_node->name; + charger_desc->type = gpio_charger_get_type(dev); + } - gpio_charger->charger = power_supply_register(&pdev->dev, - charger_desc, &psy_cfg); + if (!charger_desc->name) + charger_desc->name = pdev->name; + + gpio_charger->charger = devm_power_supply_register(dev, charger_desc, + &psy_cfg); if (IS_ERR(gpio_charger->charger)) { ret = PTR_ERR(gpio_charger->charger); - dev_err(&pdev->dev, "Failed to register power supply: %d\n", - ret); - goto err_gpio_free; + dev_err(dev, "Failed to register power supply: %d\n", ret); + return ret; } irq = gpiod_to_irq(gpio_charger->gpiod); if (irq > 0) { - ret = request_any_context_irq(irq, gpio_charger_irq, + ret = devm_request_any_context_irq(dev, irq, gpio_charger_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - dev_name(&pdev->dev), gpio_charger->charger); + dev_name(dev), gpio_charger->charger); if (ret < 0) - dev_warn(&pdev->dev, "Failed to request irq: %d\n", ret); + dev_warn(dev, "Failed to request irq: %d\n", ret); else gpio_charger->irq = irq; } platform_set_drvdata(pdev, gpio_charger); - device_init_wakeup(&pdev->dev, 1); - - return 0; - -err_gpio_free: - if (gpio_charger->legacy_gpio_requested) - gpio_free(pdata->gpio); - return ret; -} - -static int gpio_charger_remove(struct platform_device *pdev) -{ - struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); - - if (gpio_charger->irq) - free_irq(gpio_charger->irq, gpio_charger->charger); - - power_supply_unregister(gpio_charger->charger); - - if (gpio_charger->legacy_gpio_requested) - gpio_free(gpio_charger->pdata->gpio); + device_init_wakeup(dev, 1); return 0; } @@ -280,7 +234,6 @@ MODULE_DEVICE_TABLE(of, gpio_charger_match); static struct platform_driver gpio_charger_driver = { .probe = gpio_charger_probe, - .remove = gpio_charger_remove, .driver = { .name = "gpio-charger", .pm = &gpio_charger_pm_ops, diff --git a/drivers/power/supply/ltc2941-battery-gauge.c b/drivers/power/supply/ltc2941-battery-gauge.c index 4cfa3f0cd689..4f129bb4c972 100644 --- a/drivers/power/supply/ltc2941-battery-gauge.c +++ b/drivers/power/supply/ltc2941-battery-gauge.c @@ -34,6 +34,10 @@ enum ltc294x_reg { LTC294X_REG_CONTROL = 0x01, LTC294X_REG_ACC_CHARGE_MSB = 0x02, LTC294X_REG_ACC_CHARGE_LSB = 0x03, + LTC294X_REG_CHARGE_THR_HIGH_MSB = 0x04, + LTC294X_REG_CHARGE_THR_HIGH_LSB = 0x05, + LTC294X_REG_CHARGE_THR_LOW_MSB = 0x06, + LTC294X_REG_CHARGE_THR_LOW_LSB = 0x07, LTC294X_REG_VOLTAGE_MSB = 0x08, LTC294X_REG_VOLTAGE_LSB = 0x09, LTC2942_REG_TEMPERATURE_MSB = 0x0C, @@ -179,21 +183,22 @@ error_exit: return ret; } -static int ltc294x_read_charge_register(const struct ltc294x_info *info) -{ +static int ltc294x_read_charge_register(const struct ltc294x_info *info, + enum ltc294x_reg reg) + { int ret; u8 datar[2]; - ret = ltc294x_read_regs(info->client, - LTC294X_REG_ACC_CHARGE_MSB, &datar[0], 2); + ret = ltc294x_read_regs(info->client, reg, &datar[0], 2); if (ret < 0) return ret; return (datar[0] << 8) + datar[1]; } -static int ltc294x_get_charge_now(const struct ltc294x_info *info, int *val) +static int ltc294x_get_charge(const struct ltc294x_info *info, + enum ltc294x_reg reg, int *val) { - int value = ltc294x_read_charge_register(info); + int value = ltc294x_read_charge_register(info, reg); if (value < 0) return value; @@ -245,10 +250,29 @@ error_exit: return ret < 0 ? ret : 0; } +static int ltc294x_set_charge_thr(const struct ltc294x_info *info, + enum ltc294x_reg reg, int val) +{ + u8 dataw[2]; + s32 value; + + value = convert_uAh_to_bin(info, val); + /* Direction depends on how sense+/- were connected */ + if (info->Qlsb < 0) + value += 0xFFFF; + if ((value < 0) || (value > 0xFFFF)) /* input validation */ + return -EINVAL; + + /* Set new charge value */ + dataw[0] = I16_MSB(value); + dataw[1] = I16_LSB(value); + return ltc294x_write_regs(info->client, reg, &dataw[0], 2); +} + static int ltc294x_get_charge_counter( const struct ltc294x_info *info, int *val) { - int value = ltc294x_read_charge_register(info); + int value = ltc294x_read_charge_register(info, LTC294X_REG_ACC_CHARGE_MSB); if (value < 0) return value; @@ -317,15 +341,15 @@ static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val) if (info->id == LTC2942_ID) { reg = LTC2942_REG_TEMPERATURE_MSB; - value = 60000; /* Full-scale is 600 Kelvin */ + value = 6000; /* Full-scale is 600 Kelvin */ } else { reg = LTC2943_REG_TEMPERATURE_MSB; - value = 51000; /* Full-scale is 510 Kelvin */ + value = 5100; /* Full-scale is 510 Kelvin */ } ret = ltc294x_read_regs(info->client, reg, &datar[0], 2); value *= (datar[0] << 8) | datar[1]; - /* Convert to centidegrees */ - *val = value / 0xFFFF - 27215; + /* Convert to tenths of degree Celsius */ + *val = value / 0xFFFF - 2722; return ret; } @@ -336,8 +360,15 @@ static int ltc294x_get_property(struct power_supply *psy, struct ltc294x_info *info = power_supply_get_drvdata(psy); switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_FULL: + return ltc294x_get_charge(info, LTC294X_REG_CHARGE_THR_HIGH_MSB, + &val->intval); + case POWER_SUPPLY_PROP_CHARGE_EMPTY: + return ltc294x_get_charge(info, LTC294X_REG_CHARGE_THR_LOW_MSB, + &val->intval); case POWER_SUPPLY_PROP_CHARGE_NOW: - return ltc294x_get_charge_now(info, &val->intval); + return ltc294x_get_charge(info, LTC294X_REG_ACC_CHARGE_MSB, + &val->intval); case POWER_SUPPLY_PROP_CHARGE_COUNTER: return ltc294x_get_charge_counter(info, &val->intval); case POWER_SUPPLY_PROP_VOLTAGE_NOW: @@ -358,6 +389,12 @@ static int ltc294x_set_property(struct power_supply *psy, struct ltc294x_info *info = power_supply_get_drvdata(psy); switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_FULL: + return ltc294x_set_charge_thr(info, + LTC294X_REG_CHARGE_THR_HIGH_MSB, val->intval); + case POWER_SUPPLY_PROP_CHARGE_EMPTY: + return ltc294x_set_charge_thr(info, + LTC294X_REG_CHARGE_THR_LOW_MSB, val->intval); case POWER_SUPPLY_PROP_CHARGE_NOW: return ltc294x_set_charge_now(info, val->intval); default: @@ -369,6 +406,8 @@ static int ltc294x_property_is_writeable( struct power_supply *psy, enum power_supply_property psp) { switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_CHARGE_EMPTY: case POWER_SUPPLY_PROP_CHARGE_NOW: return 1; default: @@ -378,7 +417,7 @@ static int ltc294x_property_is_writeable( static void ltc294x_update(struct ltc294x_info *info) { - int charge = ltc294x_read_charge_register(info); + int charge = ltc294x_read_charge_register(info, LTC294X_REG_ACC_CHARGE_MSB); if (charge != info->charge) { info->charge = charge; @@ -397,6 +436,8 @@ static void ltc294x_work(struct work_struct *work) static enum power_supply_property ltc294x_properties[] = { POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_EMPTY, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_TEMP, diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 35dde81b1c9b..1a568df383db 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -1053,6 +1053,7 @@ static int max17042_probe(struct i2c_client *client, i2c_set_clientdata(client, chip); psy_cfg.drv_data = chip; + psy_cfg.of_node = dev->of_node; /* When current is not measured, * CURRENT_NOW and CURRENT_AVG properties should be invisible. */