power supply and reset changes for the v4.17 series
* Microsemi Ocelot reset support * Spreadtrum SC27xx reset support * generic gpio charger: lot's of cleanups * axp20x fuel gauge: add AXP813 support * misc. fixes -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE72YNB0Y/i3JqeVQT2O7X88g7+poFAlrCZVQACgkQ2O7X88g7 +ppKgQ/8Cmgfta6nX5N8U3jAbBmduFkxmV+WaVE2yMned7ABL5ZAXBOBIDXW0Cvk yFNhu4fgpb2tDW/NEsYiyu61ES7F4W+q0AlvTNaPcM4oh+j8+E3B8C26qUaojw7E rB5FrRJA7/1RQeBl8RrWplT3bmYyL8qPrEca3hPPrrVPYsvf7TqJlgJRqOEu5bIG GBcU+aPZsd6T0u3SloErm0LL9Q4S6YpuW/gqR69azcQUacfalA7kjZ5gpiN9YXYq xSsm3zoJl77x4HTAnKN+7Kzyl3gugKJXIN8kN19F9iYwpc3ccii9FROrx7T3trps nUojvRorlZHawwAOYxp/6C21u6N7SXZ+TyVnPWgjP24Fr7DI5OxqWEZ8fZqaN7M4 nNjhZYpquJDwgg63E1WoA3aPOUCQp7ZLz2xKUWuwX6n1zXr/R3qVeguFPORvhp9Q uu9nfoZXo5ThVz5JK5eGP7pE1OU/qpnzF2ZLHyeNULjAJcnxQjNzN2nTG35IxiO0 BKlYmy31GcMwooRqarJrKavzMU1xGKZtIGxOm6uIluqA1cGfH+8ahk84tzLgYAZ4 4MYnPjDyR31gzt9AB3eb7wGo+TP7djnIzeooeyz8Q71oNcCx263QgrmaKcPZjQtm QcLA+/jyGNg0T5t6l2Kj7mLKNdiGb6bHMqMs5KrezTruJcDhlRg= =VY8H -----END PGP SIGNATURE----- Merge tag 'for-v4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply Pull power supply and reset updates from Sebastian Reichel: - Microsemi Ocelot reset support - Spreadtrum SC27xx reset support - generic gpio charger: lot's of cleanups - axp20x fuel gauge: add AXP813 support - misc fixes, including one devicetree change for the Nokia N900, that has been Acked-by Tony Lindgren * tag 'for-v4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (27 commits) power: reset: at91-reset: Switch from the pr_*() to the dev_*() logging functions power: reset: at91-poweroff: Remove redundant dev_err call in at91_poweroff_probe() power: reset: at91-poweroff: Switch from the pr_*() to the dev_*() logging functions power: reset: make function sc27xx_poweroff_shutdown static power: supply: da9150-fg: remove VLA usage ARM: dts: omap3-n900: Add link between battery and charger power: supply: bq2415x: add DT referencing support power: supply: bq27xxx: support missing supplier device max17042: propagate of_node to power supply device power: supply: axp288_fuel_gauge: Fix full status reporting power: supply: axp288_fuel_gauge: Do not register FG on ECS EF20EA power: reset: gpio-poweroff: Support for timeout from device property dt-bindings: power: reset: gpio-poweroff: Add 'timeout-ms' property power: reset: Add Spreadtrum SC27xx PMIC power off support power: supply: axp20x_battery: add support for AXP813 dt-bindings: power: supply: axp20x: add AXP813 battery DT binding power: supply: axp20x_battery: use data struct for variant specific code power: supply: gpio-charger: Remove pdata from gpio_charger power: supply: gpio-charger: Use GPIOF_ACTIVE_LOW for legacy setup power: supply: gpio-charger: Remove redundant dev_err call in probe function ...
This commit is contained in:
commit
3ac684b881
20 changed files with 501 additions and 202 deletions
|
@ -27,10 +27,13 @@ Optional properties:
|
||||||
it to an output when the power-off handler is called. If this optional
|
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
|
property is not specified, the GPIO is initialized as an output in its
|
||||||
inactive state.
|
inactive state.
|
||||||
|
- timeout-ms: Time to wait before asserting a WARN_ON(1). If nothing is
|
||||||
|
specified, 3000 ms is used.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
gpio-poweroff {
|
gpio-poweroff {
|
||||||
compatible = "gpio-poweroff";
|
compatible = "gpio-poweroff";
|
||||||
gpios = <&gpio 4 0>;
|
gpios = <&gpio 4 0>;
|
||||||
|
timeout-ms = <3000>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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>;
|
||||||
|
};
|
||||||
|
|
|
@ -4,12 +4,12 @@ Required Properties:
|
||||||
- compatible, one of:
|
- compatible, one of:
|
||||||
"x-powers,axp209-battery-power-supply"
|
"x-powers,axp209-battery-power-supply"
|
||||||
"x-powers,axp221-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
|
The supported devices can read the battery voltage, charge and discharge
|
||||||
currents of the battery by reading ADC channels from the AXP20X/AXP22X
|
currents of the battery by reading ADC channels from the ADC.
|
||||||
ADC.
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
|
|
@ -673,6 +673,7 @@
|
||||||
bq27200: bq27200@55 {
|
bq27200: bq27200@55 {
|
||||||
compatible = "ti,bq27200";
|
compatible = "ti,bq27200";
|
||||||
reg = <0x55>;
|
reg = <0x55>;
|
||||||
|
power-supplies = <&bq24150a>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Stereo headphone amplifier */
|
/* Stereo headphone amplifier */
|
||||||
|
|
|
@ -104,6 +104,13 @@ config POWER_RESET_MSM
|
||||||
help
|
help
|
||||||
Power off and restart support for Qualcomm boards.
|
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
|
config POWER_RESET_PIIX4_POWEROFF
|
||||||
tristate "Intel PIIX4 power-off driver"
|
tristate "Intel PIIX4 power-off driver"
|
||||||
depends on PCI
|
depends on PCI
|
||||||
|
@ -218,5 +225,14 @@ config SYSCON_REBOOT_MODE
|
||||||
register, then the bootloader can read it to take different
|
register, then the bootloader can read it to take different
|
||||||
action according to the mode.
|
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
|
endif
|
||||||
|
|
||||||
|
|
|
@ -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_GPIO_RESTART) += gpio-restart.o
|
||||||
obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o
|
obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o
|
||||||
obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.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_PIIX4_POWEROFF) += piix4-poweroff.o
|
||||||
obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o
|
obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o
|
||||||
obj-$(CONFIG_POWER_RESET_QNAP) += qnap-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_POWER_RESET_ZX) += zx-reboot.o
|
||||||
obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o
|
obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o
|
||||||
obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o
|
obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o
|
||||||
|
obj-$(CONFIG_POWER_RESET_SC27XX) += sc27xx-poweroff.o
|
||||||
|
|
|
@ -55,10 +55,10 @@ static void __iomem *at91_shdwc_base;
|
||||||
static struct clk *sclk;
|
static struct clk *sclk;
|
||||||
static void __iomem *mpddrc_base;
|
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);
|
u32 reg = readl(at91_shdwc_base + AT91_SHDW_SR);
|
||||||
char *reason = "unknown";
|
|
||||||
|
|
||||||
/* Simple power-on, just bail out */
|
/* Simple power-on, just bail out */
|
||||||
if (!reg)
|
if (!reg)
|
||||||
|
@ -68,8 +68,10 @@ static void __init at91_wakeup_status(void)
|
||||||
reason = "RTT";
|
reason = "RTT";
|
||||||
else if (reg & AT91_SHDW_RTCWK)
|
else if (reg & AT91_SHDW_RTCWK)
|
||||||
reason = "RTC";
|
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)
|
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);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res);
|
at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
if (IS_ERR(at91_shdwc_base)) {
|
if (IS_ERR(at91_shdwc_base))
|
||||||
dev_err(&pdev->dev, "Could not map reset controller address\n");
|
|
||||||
return PTR_ERR(at91_shdwc_base);
|
return PTR_ERR(at91_shdwc_base);
|
||||||
}
|
|
||||||
|
|
||||||
sclk = devm_clk_get(&pdev->dev, NULL);
|
sclk = devm_clk_get(&pdev->dev, NULL);
|
||||||
if (IS_ERR(sclk))
|
if (IS_ERR(sclk))
|
||||||
|
@ -172,7 +172,7 @@ static int __init at91_poweroff_probe(struct platform_device *pdev)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
at91_wakeup_status();
|
at91_wakeup_status(pdev);
|
||||||
|
|
||||||
if (pdev->dev.of_node)
|
if (pdev->dev.of_node)
|
||||||
at91_poweroff_dt_set_wakeup_mode(pdev);
|
at91_poweroff_dt_set_wakeup_mode(pdev);
|
||||||
|
|
|
@ -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)
|
static void __init at91_reset_status(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
const char *reason;
|
||||||
u32 reg = readl(at91_rstc_base + AT91_RSTC_SR);
|
u32 reg = readl(at91_rstc_base + AT91_RSTC_SR);
|
||||||
char *reason;
|
|
||||||
|
|
||||||
switch ((reg & AT91_RSTC_RSTTYP) >> 8) {
|
switch ((reg & AT91_RSTC_RSTTYP) >> 8) {
|
||||||
case RESET_TYPE_GENERAL:
|
case RESET_TYPE_GENERAL:
|
||||||
|
@ -169,7 +169,7 @@ static void __init at91_reset_status(struct platform_device *pdev)
|
||||||
break;
|
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[] = {
|
static const struct of_device_id at91_ramc_of_match[] = {
|
||||||
|
|
|
@ -47,8 +47,12 @@ static irqreturn_t gemini_powerbutton_interrupt(int irq, void *data)
|
||||||
val &= 0x70U;
|
val &= 0x70U;
|
||||||
switch (val) {
|
switch (val) {
|
||||||
case GEMINI_STAT_CIR:
|
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;
|
break;
|
||||||
case GEMINI_STAT_RTC:
|
case GEMINI_STAT_RTC:
|
||||||
dev_info(gpw->dev, "RTC poweroff\n");
|
dev_info(gpw->dev, "RTC poweroff\n");
|
||||||
|
@ -116,7 +120,17 @@ static int gemini_poweroff_probe(struct platform_device *pdev)
|
||||||
return -ENODEV;
|
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 = readl(gpw->base + GEMINI_PWC_CTRLREG);
|
||||||
val |= GEMINI_CTRL_IRQ_CLR;
|
val |= GEMINI_CTRL_IRQ_CLR;
|
||||||
writel(val, gpw->base + GEMINI_PWC_CTRLREG);
|
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;
|
pm_power_off = gemini_poweroff;
|
||||||
gpw_poweroff = gpw;
|
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");
|
dev_info(dev, "Gemini poweroff driver registered\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -19,11 +19,13 @@
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
|
||||||
|
#define DEFAULT_TIMEOUT_MS 3000
|
||||||
/*
|
/*
|
||||||
* Hold configuration here, cannot be more than one instance of the driver
|
* Hold configuration here, cannot be more than one instance of the driver
|
||||||
* since pm_power_off itself is global.
|
* since pm_power_off itself is global.
|
||||||
*/
|
*/
|
||||||
static struct gpio_desc *reset_gpio;
|
static struct gpio_desc *reset_gpio;
|
||||||
|
static u32 timeout = DEFAULT_TIMEOUT_MS;
|
||||||
|
|
||||||
static void gpio_poweroff_do_poweroff(void)
|
static void gpio_poweroff_do_poweroff(void)
|
||||||
{
|
{
|
||||||
|
@ -40,7 +42,7 @@ static void gpio_poweroff_do_poweroff(void)
|
||||||
gpiod_set_value(reset_gpio, 1);
|
gpiod_set_value(reset_gpio, 1);
|
||||||
|
|
||||||
/* give it some time */
|
/* give it some time */
|
||||||
mdelay(3000);
|
mdelay(timeout);
|
||||||
|
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
}
|
}
|
||||||
|
@ -58,12 +60,14 @@ static int gpio_poweroff_probe(struct platform_device *pdev)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
input = of_property_read_bool(pdev->dev.of_node, "input");
|
input = device_property_read_bool(&pdev->dev, "input");
|
||||||
if (input)
|
if (input)
|
||||||
flags = GPIOD_IN;
|
flags = GPIOD_IN;
|
||||||
else
|
else
|
||||||
flags = GPIOD_OUT_LOW;
|
flags = GPIOD_OUT_LOW;
|
||||||
|
|
||||||
|
device_property_read_u32(&pdev->dev, "timeout-ms", &timeout);
|
||||||
|
|
||||||
reset_gpio = devm_gpiod_get(&pdev->dev, NULL, flags);
|
reset_gpio = devm_gpiod_get(&pdev->dev, NULL, flags);
|
||||||
if (IS_ERR(reset_gpio))
|
if (IS_ERR(reset_gpio))
|
||||||
return PTR_ERR(reset_gpio);
|
return PTR_ERR(reset_gpio);
|
||||||
|
|
88
drivers/power/reset/ocelot-reset.c
Normal file
88
drivers/power/reset/ocelot-reset.c
Normal file
|
@ -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 <linux/delay.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/notifier.h>
|
||||||
|
#include <linux/mfd/syscon.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/reboot.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
|
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);
|
66
drivers/power/reset/sc27xx-poweroff.c
Normal file
66
drivers/power/reset/sc27xx-poweroff.c
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Spreadtrum Communications Inc.
|
||||||
|
* Copyright (C) 2018 Linaro Ltd.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/cpu.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/syscore_ops.h>
|
||||||
|
|
||||||
|
#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);
|
|
@ -49,10 +49,22 @@
|
||||||
#define AXP22X_CHRG_CTRL1_TGT_4_22V (1 << 5)
|
#define AXP22X_CHRG_CTRL1_TGT_4_22V (1 << 5)
|
||||||
#define AXP22X_CHRG_CTRL1_TGT_4_24V (3 << 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_CHRG_CTRL1_TGT_CURR GENMASK(3, 0)
|
||||||
|
|
||||||
#define AXP20X_V_OFF_MASK GENMASK(2, 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 axp20x_batt_ps {
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
struct power_supply *batt;
|
struct power_supply *batt;
|
||||||
|
@ -62,7 +74,7 @@ struct axp20x_batt_ps {
|
||||||
struct iio_channel *batt_v;
|
struct iio_channel *batt_v;
|
||||||
/* Maximum constant charge current */
|
/* Maximum constant charge current */
|
||||||
unsigned int max_ccc;
|
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,
|
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;
|
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)
|
int ret, reg;
|
||||||
*val = *val * 100000 + 300000;
|
|
||||||
else
|
|
||||||
*val = *val * 150000 + 300000;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void constant_charge_current_to_raw(struct axp20x_batt_ps *axp, int *val)
|
ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, ®);
|
||||||
{
|
if (ret)
|
||||||
if (axp->axp_id == AXP209_ID)
|
return ret;
|
||||||
*val = (*val - 300000) / 100000;
|
|
||||||
else
|
switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) {
|
||||||
*val = (*val - 300000) / 150000;
|
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,
|
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;
|
*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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -269,8 +294,7 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (axp20x_batt->axp_id == AXP221_ID &&
|
if (axp20x_batt->data->has_fg_valid && !(reg & AXP22X_FG_VALID))
|
||||||
!(reg & AXP22X_FG_VALID))
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -281,11 +305,8 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
|
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
|
||||||
if (axp20x_batt->axp_id == AXP209_ID)
|
return axp20x_batt->data->get_max_voltage(axp20x_batt,
|
||||||
return axp20x_battery_get_max_voltage(axp20x_batt,
|
&val->intval);
|
||||||
&val->intval);
|
|
||||||
return axp22x_battery_get_max_voltage(axp20x_batt,
|
|
||||||
&val->intval);
|
|
||||||
|
|
||||||
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
|
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
|
||||||
ret = regmap_read(axp20x_batt->regmap, AXP20X_V_OFF, ®);
|
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;
|
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,
|
static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
|
||||||
int val)
|
int val)
|
||||||
{
|
{
|
||||||
|
@ -321,9 +368,6 @@ static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4150000:
|
case 4150000:
|
||||||
if (axp20x_batt->axp_id == AXP221_ID)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
val = AXP20X_CHRG_CTRL1_TGT_4_15V;
|
val = AXP20X_CHRG_CTRL1_TGT_4_15V;
|
||||||
break;
|
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)
|
if (charge_current > axp_batt->max_ccc)
|
||||||
return -EINVAL;
|
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)
|
if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -365,12 +410,14 @@ static int axp20x_set_max_constant_charge_current(struct axp20x_batt_ps *axp,
|
||||||
{
|
{
|
||||||
bool lower_max = false;
|
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)
|
if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0)
|
||||||
return -EINVAL;
|
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)
|
if (charge_current > axp->max_ccc)
|
||||||
dev_warn(axp->dev,
|
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);
|
return axp20x_set_voltage_min_design(axp20x_batt, val->intval);
|
||||||
|
|
||||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
|
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:
|
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||||
return axp20x_set_constant_charge_current(axp20x_batt,
|
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,
|
.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[] = {
|
static const struct of_device_id axp20x_battery_ps_id[] = {
|
||||||
{
|
{
|
||||||
.compatible = "x-powers,axp209-battery-power-supply",
|
.compatible = "x-powers,axp209-battery-power-supply",
|
||||||
.data = (void *)AXP209_ID,
|
.data = (void *)&axp209_data,
|
||||||
}, {
|
}, {
|
||||||
.compatible = "x-powers,axp221-battery-power-supply",
|
.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 */ },
|
}, { /* sentinel */ },
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, axp20x_battery_ps_id);
|
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 axp20x_batt_ps *axp20x_batt;
|
||||||
struct power_supply_config psy_cfg = {};
|
struct power_supply_config psy_cfg = {};
|
||||||
struct power_supply_battery_info info;
|
struct power_supply_battery_info info;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
|
||||||
if (!of_device_is_available(pdev->dev.of_node))
|
if (!of_device_is_available(pdev->dev.of_node))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -516,7 +590,7 @@ static int axp20x_power_probe(struct platform_device *pdev)
|
||||||
psy_cfg.drv_data = axp20x_batt;
|
psy_cfg.drv_data = axp20x_batt;
|
||||||
psy_cfg.of_node = pdev->dev.of_node;
|
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->batt = devm_power_supply_register(&pdev->dev,
|
||||||
&axp20x_batt_ps_desc,
|
&axp20x_batt_ps_desc,
|
||||||
|
|
|
@ -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)
|
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);
|
pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS);
|
||||||
if (pwr_stat < 0) {
|
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% */
|
/* Report full if Vbus is valid and the reported capacity is 100% */
|
||||||
if (pwr_stat & PS_STAT_VBUS_VALID) {
|
if (!(pwr_stat & PS_STAT_VBUS_VALID))
|
||||||
fg_res = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
|
goto not_full;
|
||||||
if (fg_res < 0) {
|
|
||||||
dev_err(&info->pdev->dev,
|
fg_res = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
|
||||||
"FG RES read failed: %d\n", fg_res);
|
if (fg_res < 0) {
|
||||||
return;
|
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;
|
if (!(fg_res & FG_REP_CAP_VALID))
|
||||||
return;
|
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)
|
if (pwr_stat & PS_STAT_BAT_CHRG_DIR)
|
||||||
info->status = POWER_SUPPLY_STATUS_CHARGING;
|
info->status = POWER_SUPPLY_STATUS_CHARGING;
|
||||||
else
|
else
|
||||||
|
@ -708,6 +731,12 @@ static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = {
|
||||||
DMI_MATCH(DMI_BOARD_VERSION, "V1.1"),
|
DMI_MATCH(DMI_BOARD_VERSION, "V1.1"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
/* ECS EF20EA */
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "EF20EA"),
|
||||||
|
},
|
||||||
|
},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1037,7 +1037,10 @@ static int bq2415x_power_supply_init(struct bq2415x_device *bq)
|
||||||
int ret;
|
int ret;
|
||||||
int chip;
|
int chip;
|
||||||
char revstr[8];
|
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.name = bq->name;
|
||||||
bq->charger_desc.type = POWER_SUPPLY_TYPE_USB;
|
bq->charger_desc.type = POWER_SUPPLY_TYPE_USB;
|
||||||
|
|
|
@ -1670,7 +1670,7 @@ static int bq27xxx_battery_status(struct bq27xxx_device_info *di,
|
||||||
status = POWER_SUPPLY_STATUS_FULL;
|
status = POWER_SUPPLY_STATUS_FULL;
|
||||||
else if (di->cache.flags & BQ27000_FLAG_CHGS)
|
else if (di->cache.flags & BQ27000_FLAG_CHGS)
|
||||||
status = POWER_SUPPLY_STATUS_CHARGING;
|
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;
|
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||||
else
|
else
|
||||||
status = POWER_SUPPLY_STATUS_DISCHARGING;
|
status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||||
|
|
|
@ -92,7 +92,7 @@ struct da9150_fg {
|
||||||
static u32 da9150_fg_read_attr(struct da9150_fg *fg, u8 code, u8 size)
|
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;
|
u8 read_addr;
|
||||||
u32 res = 0;
|
u32 res = 0;
|
||||||
int i;
|
int i;
|
||||||
|
@ -111,7 +111,7 @@ static void da9150_fg_write_attr(struct da9150_fg *fg, u8 code, u8 size,
|
||||||
u32 val)
|
u32 val)
|
||||||
|
|
||||||
{
|
{
|
||||||
u8 buf[size];
|
u8 buf[DA9150_QIF_LONG_SIZE];
|
||||||
u8 write_addr;
|
u8 write_addr;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
|
|
@ -28,14 +28,12 @@
|
||||||
#include <linux/power/gpio-charger.h>
|
#include <linux/power/gpio-charger.h>
|
||||||
|
|
||||||
struct gpio_charger {
|
struct gpio_charger {
|
||||||
const struct gpio_charger_platform_data *pdata;
|
|
||||||
unsigned int irq;
|
unsigned int irq;
|
||||||
bool wakeup_enabled;
|
bool wakeup_enabled;
|
||||||
|
|
||||||
struct power_supply *charger;
|
struct power_supply *charger;
|
||||||
struct power_supply_desc charger_desc;
|
struct power_supply_desc charger_desc;
|
||||||
struct gpio_desc *gpiod;
|
struct gpio_desc *gpiod;
|
||||||
bool legacy_gpio_requested;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static irqreturn_t gpio_charger_irq(int irq, void *devid)
|
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)
|
enum power_supply_property psp, union power_supply_propval *val)
|
||||||
{
|
{
|
||||||
struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy);
|
struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy);
|
||||||
const struct gpio_charger_platform_data *pdata = gpio_charger->pdata;
|
|
||||||
|
|
||||||
switch (psp) {
|
switch (psp) {
|
||||||
case POWER_SUPPLY_PROP_ONLINE:
|
case POWER_SUPPLY_PROP_ONLINE:
|
||||||
val->intval = gpiod_get_value_cansleep(gpio_charger->gpiod);
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -71,175 +66,134 @@ static int gpio_charger_get_property(struct power_supply *psy,
|
||||||
return 0;
|
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[] = {
|
static enum power_supply_property gpio_charger_properties[] = {
|
||||||
POWER_SUPPLY_PROP_ONLINE,
|
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)
|
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 power_supply_config psy_cfg = {};
|
||||||
struct gpio_charger *gpio_charger;
|
struct gpio_charger *gpio_charger;
|
||||||
struct power_supply_desc *charger_desc;
|
struct power_supply_desc *charger_desc;
|
||||||
int ret;
|
unsigned long flags;
|
||||||
int irq;
|
int irq, ret;
|
||||||
|
|
||||||
if (!pdata) {
|
if (!pdata && !dev->of_node) {
|
||||||
pdata = gpio_charger_parse_dt(&pdev->dev);
|
dev_err(dev, "No platform data\n");
|
||||||
if (IS_ERR(pdata)) {
|
return -ENOENT;
|
||||||
ret = PTR_ERR(pdata);
|
|
||||||
if (ret != -EPROBE_DEFER)
|
|
||||||
dev_err(&pdev->dev, "No platform data\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gpio_charger = devm_kzalloc(&pdev->dev, sizeof(*gpio_charger),
|
gpio_charger = devm_kzalloc(dev, sizeof(*gpio_charger), GFP_KERNEL);
|
||||||
GFP_KERNEL);
|
if (!gpio_charger)
|
||||||
if (!gpio_charger) {
|
|
||||||
dev_err(&pdev->dev, "Failed to alloc driver structure\n");
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This will fetch a GPIO descriptor from device tree, ACPI or
|
* This will fetch a GPIO descriptor from device tree, ACPI or
|
||||||
* boardfile descriptor tables. It's good to try this first.
|
* 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
|
* If this fails and we're not using device tree, try the
|
||||||
* legacy platform data method.
|
* 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 */
|
/* Non-DT: use legacy GPIO numbers */
|
||||||
if (!gpio_is_valid(pdata->gpio)) {
|
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;
|
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) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "Failed to request gpio pin: %d\n",
|
dev_err(dev, "Failed to request gpio pin: %d\n", ret);
|
||||||
ret);
|
|
||||||
return 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 */
|
/* Then convert this to gpiod for now */
|
||||||
gpio_charger->gpiod = gpio_to_desc(pdata->gpio);
|
gpio_charger->gpiod = gpio_to_desc(pdata->gpio);
|
||||||
} else if (IS_ERR(gpio_charger->gpiod)) {
|
} else if (IS_ERR(gpio_charger->gpiod)) {
|
||||||
/* Just try again if this happens */
|
/* Just try again if this happens */
|
||||||
if (PTR_ERR(gpio_charger->gpiod) == -EPROBE_DEFER)
|
if (PTR_ERR(gpio_charger->gpiod) == -EPROBE_DEFER)
|
||||||
return -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);
|
return PTR_ERR(gpio_charger->gpiod);
|
||||||
}
|
}
|
||||||
|
|
||||||
charger_desc = &gpio_charger->charger_desc;
|
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->properties = gpio_charger_properties;
|
||||||
charger_desc->num_properties = ARRAY_SIZE(gpio_charger_properties);
|
charger_desc->num_properties = ARRAY_SIZE(gpio_charger_properties);
|
||||||
charger_desc->get_property = gpio_charger_get_property;
|
charger_desc->get_property = gpio_charger_get_property;
|
||||||
|
|
||||||
psy_cfg.supplied_to = pdata->supplied_to;
|
psy_cfg.of_node = dev->of_node;
|
||||||
psy_cfg.num_supplicants = pdata->num_supplicants;
|
|
||||||
psy_cfg.of_node = pdev->dev.of_node;
|
|
||||||
psy_cfg.drv_data = gpio_charger;
|
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,
|
if (!charger_desc->name)
|
||||||
charger_desc, &psy_cfg);
|
charger_desc->name = pdev->name;
|
||||||
|
|
||||||
|
gpio_charger->charger = devm_power_supply_register(dev, charger_desc,
|
||||||
|
&psy_cfg);
|
||||||
if (IS_ERR(gpio_charger->charger)) {
|
if (IS_ERR(gpio_charger->charger)) {
|
||||||
ret = PTR_ERR(gpio_charger->charger);
|
ret = PTR_ERR(gpio_charger->charger);
|
||||||
dev_err(&pdev->dev, "Failed to register power supply: %d\n",
|
dev_err(dev, "Failed to register power supply: %d\n", ret);
|
||||||
ret);
|
return ret;
|
||||||
goto err_gpio_free;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
irq = gpiod_to_irq(gpio_charger->gpiod);
|
irq = gpiod_to_irq(gpio_charger->gpiod);
|
||||||
if (irq > 0) {
|
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,
|
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||||
dev_name(&pdev->dev), gpio_charger->charger);
|
dev_name(dev), gpio_charger->charger);
|
||||||
if (ret < 0)
|
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
|
else
|
||||||
gpio_charger->irq = irq;
|
gpio_charger->irq = irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, gpio_charger);
|
platform_set_drvdata(pdev, gpio_charger);
|
||||||
|
|
||||||
device_init_wakeup(&pdev->dev, 1);
|
device_init_wakeup(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);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -280,7 +234,6 @@ MODULE_DEVICE_TABLE(of, gpio_charger_match);
|
||||||
|
|
||||||
static struct platform_driver gpio_charger_driver = {
|
static struct platform_driver gpio_charger_driver = {
|
||||||
.probe = gpio_charger_probe,
|
.probe = gpio_charger_probe,
|
||||||
.remove = gpio_charger_remove,
|
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "gpio-charger",
|
.name = "gpio-charger",
|
||||||
.pm = &gpio_charger_pm_ops,
|
.pm = &gpio_charger_pm_ops,
|
||||||
|
|
|
@ -34,6 +34,10 @@ enum ltc294x_reg {
|
||||||
LTC294X_REG_CONTROL = 0x01,
|
LTC294X_REG_CONTROL = 0x01,
|
||||||
LTC294X_REG_ACC_CHARGE_MSB = 0x02,
|
LTC294X_REG_ACC_CHARGE_MSB = 0x02,
|
||||||
LTC294X_REG_ACC_CHARGE_LSB = 0x03,
|
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_MSB = 0x08,
|
||||||
LTC294X_REG_VOLTAGE_LSB = 0x09,
|
LTC294X_REG_VOLTAGE_LSB = 0x09,
|
||||||
LTC2942_REG_TEMPERATURE_MSB = 0x0C,
|
LTC2942_REG_TEMPERATURE_MSB = 0x0C,
|
||||||
|
@ -179,21 +183,22 @@ static int ltc294x_reset(const struct ltc294x_info *info, int prescaler_exp)
|
||||||
return ret;
|
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;
|
int ret;
|
||||||
u8 datar[2];
|
u8 datar[2];
|
||||||
|
|
||||||
ret = ltc294x_read_regs(info->client,
|
ret = ltc294x_read_regs(info->client, reg, &datar[0], 2);
|
||||||
LTC294X_REG_ACC_CHARGE_MSB, &datar[0], 2);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
return (datar[0] << 8) + datar[1];
|
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)
|
if (value < 0)
|
||||||
return value;
|
return value;
|
||||||
|
@ -245,10 +250,29 @@ static int ltc294x_set_charge_now(const struct ltc294x_info *info, int val)
|
||||||
return ret < 0 ? ret : 0;
|
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(
|
static int ltc294x_get_charge_counter(
|
||||||
const struct ltc294x_info *info, int *val)
|
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)
|
if (value < 0)
|
||||||
return value;
|
return value;
|
||||||
|
@ -317,15 +341,15 @@ static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val)
|
||||||
|
|
||||||
if (info->id == LTC2942_ID) {
|
if (info->id == LTC2942_ID) {
|
||||||
reg = LTC2942_REG_TEMPERATURE_MSB;
|
reg = LTC2942_REG_TEMPERATURE_MSB;
|
||||||
value = 60000; /* Full-scale is 600 Kelvin */
|
value = 6000; /* Full-scale is 600 Kelvin */
|
||||||
} else {
|
} else {
|
||||||
reg = LTC2943_REG_TEMPERATURE_MSB;
|
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);
|
ret = ltc294x_read_regs(info->client, reg, &datar[0], 2);
|
||||||
value *= (datar[0] << 8) | datar[1];
|
value *= (datar[0] << 8) | datar[1];
|
||||||
/* Convert to centidegrees */
|
/* Convert to tenths of degree Celsius */
|
||||||
*val = value / 0xFFFF - 27215;
|
*val = value / 0xFFFF - 2722;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,8 +360,15 @@ static int ltc294x_get_property(struct power_supply *psy,
|
||||||
struct ltc294x_info *info = power_supply_get_drvdata(psy);
|
struct ltc294x_info *info = power_supply_get_drvdata(psy);
|
||||||
|
|
||||||
switch (prop) {
|
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:
|
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:
|
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
|
||||||
return ltc294x_get_charge_counter(info, &val->intval);
|
return ltc294x_get_charge_counter(info, &val->intval);
|
||||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
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);
|
struct ltc294x_info *info = power_supply_get_drvdata(psy);
|
||||||
|
|
||||||
switch (psp) {
|
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:
|
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
||||||
return ltc294x_set_charge_now(info, val->intval);
|
return ltc294x_set_charge_now(info, val->intval);
|
||||||
default:
|
default:
|
||||||
|
@ -369,6 +406,8 @@ static int ltc294x_property_is_writeable(
|
||||||
struct power_supply *psy, enum power_supply_property psp)
|
struct power_supply *psy, enum power_supply_property psp)
|
||||||
{
|
{
|
||||||
switch (psp) {
|
switch (psp) {
|
||||||
|
case POWER_SUPPLY_PROP_CHARGE_FULL:
|
||||||
|
case POWER_SUPPLY_PROP_CHARGE_EMPTY:
|
||||||
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
||||||
return 1;
|
return 1;
|
||||||
default:
|
default:
|
||||||
|
@ -378,7 +417,7 @@ static int ltc294x_property_is_writeable(
|
||||||
|
|
||||||
static void ltc294x_update(struct ltc294x_info *info)
|
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) {
|
if (charge != info->charge) {
|
||||||
info->charge = charge;
|
info->charge = charge;
|
||||||
|
@ -397,6 +436,8 @@ static void ltc294x_work(struct work_struct *work)
|
||||||
|
|
||||||
static enum power_supply_property ltc294x_properties[] = {
|
static enum power_supply_property ltc294x_properties[] = {
|
||||||
POWER_SUPPLY_PROP_CHARGE_COUNTER,
|
POWER_SUPPLY_PROP_CHARGE_COUNTER,
|
||||||
|
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||||
|
POWER_SUPPLY_PROP_CHARGE_EMPTY,
|
||||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||||
POWER_SUPPLY_PROP_TEMP,
|
POWER_SUPPLY_PROP_TEMP,
|
||||||
|
|
|
@ -1053,6 +1053,7 @@ static int max17042_probe(struct i2c_client *client,
|
||||||
|
|
||||||
i2c_set_clientdata(client, chip);
|
i2c_set_clientdata(client, chip);
|
||||||
psy_cfg.drv_data = chip;
|
psy_cfg.drv_data = chip;
|
||||||
|
psy_cfg.of_node = dev->of_node;
|
||||||
|
|
||||||
/* When current is not measured,
|
/* When current is not measured,
|
||||||
* CURRENT_NOW and CURRENT_AVG properties should be invisible. */
|
* CURRENT_NOW and CURRENT_AVG properties should be invisible. */
|
||||||
|
|
Loading…
Add table
Reference in a new issue