Commit a10c9c71 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-v5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply

Pull power supply and reset changes from Sebastian Reichel:
 "Core:
   - Nothing

  Drivers:
   - at91-reset: cleanups, proper handling for sam9x60
   - sc27xx, charger-manager: allow building as module
   - sc27xx: add support to read current charge capacity
   - axp288: more quirks for weird hardware
   - misc fixes"

* tag 'for-v5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (26 commits)
  power: reset: sc27xx: Allow the SC27XX poweroff driver building into a module
  power: reset: sc27xx: Change to use cpu_down()
  power: reset: sc27xx: Power off the external subsystems' connection
  power: twl4030: Use scnprintf() for avoiding potential buffer overflow
  power: supply: bq27xxx_battery: Silence deferred-probe error
  power: reset: at91-reset: handle nrst async for sam9x60
  power: reset: at91-reset: get rid of at91_reset_data
  power: reset: at91-reset: keep only one reset function
  power: reset: at91-reset: make at91sam9g45_restart() generic
  power: reset: at91-reset: introduce ramc_lpr to struct at91_reset
  power: reset: at91-reset: use r4 as tmp argument
  power: reset: at91-reset: introduce args member in at91_reset_data
  power: reset: at91-reset: introduce struct at91_reset_data
  power: reset: at91-reset: devm_kzalloc() for at91_reset data structure
  power: reset: at91-reset: pass rstc base address to at91_reset_status()
  power: reset: at91-reset: convert reset in pointer to struct at91_reset
  power: reset: at91-reset: add notifier block to struct at91_reset
  power: reset: at91-reset: add sclk to struct at91_reset
  power: reset: at91-reset: add ramc_base[] to struct at91_reset
  power: reset: at91-reset: introduce struct at91_reset
  ...
parents c48b0722 f78c55e3
...@@ -248,7 +248,7 @@ config SYSCON_REBOOT_MODE ...@@ -248,7 +248,7 @@ config SYSCON_REBOOT_MODE
action according to the mode. action according to the mode.
config POWER_RESET_SC27XX config POWER_RESET_SC27XX
bool "Spreadtrum SC27xx PMIC power-off driver" tristate "Spreadtrum SC27xx PMIC power-off driver"
depends on MFD_SC27XX_PMIC || COMPILE_TEST depends on MFD_SC27XX_PMIC || COMPILE_TEST
help help
This driver supports powering off a system through This driver supports powering off a system through
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#define AT91_RSTC_MR 0x08 /* Reset Controller Mode Register */ #define AT91_RSTC_MR 0x08 /* Reset Controller Mode Register */
#define AT91_RSTC_URSTEN BIT(0) /* User Reset Enable */ #define AT91_RSTC_URSTEN BIT(0) /* User Reset Enable */
#define AT91_RSTC_URSTASYNC BIT(2) /* User Reset Asynchronous Control */
#define AT91_RSTC_URSTIEN BIT(4) /* User Reset Interrupt Enable */ #define AT91_RSTC_URSTIEN BIT(4) /* User Reset Interrupt Enable */
#define AT91_RSTC_ERSTL GENMASK(11, 8) /* External Reset Length */ #define AT91_RSTC_ERSTL GENMASK(11, 8) /* External Reset Length */
...@@ -49,105 +50,63 @@ enum reset_type { ...@@ -49,105 +50,63 @@ enum reset_type {
RESET_TYPE_ULP2 = 8, RESET_TYPE_ULP2 = 8,
}; };
static void __iomem *at91_ramc_base[2], *at91_rstc_base; struct at91_reset {
static struct clk *sclk; void __iomem *rstc_base;
void __iomem *ramc_base[2];
struct clk *sclk;
struct notifier_block nb;
u32 args;
u32 ramc_lpr;
};
/* /*
* unless the SDRAM is cleanly shutdown before we hit the * unless the SDRAM is cleanly shutdown before we hit the
* reset register it can be left driving the data bus and * reset register it can be left driving the data bus and
* killing the chance of a subsequent boot from NAND * killing the chance of a subsequent boot from NAND
*/ */
static int at91sam9260_restart(struct notifier_block *this, unsigned long mode, static int at91_reset(struct notifier_block *this, unsigned long mode,
void *cmd) void *cmd)
{ {
asm volatile( struct at91_reset *reset = container_of(this, struct at91_reset, nb);
/* Align to cache lines */
".balign 32\n\t"
/* Disable SDRAM accesses */
"str %2, [%0, #" __stringify(AT91_SDRAMC_TR) "]\n\t"
/* Power down SDRAM */
"str %3, [%0, #" __stringify(AT91_SDRAMC_LPR) "]\n\t"
/* Reset CPU */
"str %4, [%1, #" __stringify(AT91_RSTC_CR) "]\n\t"
"b .\n\t"
:
: "r" (at91_ramc_base[0]),
"r" (at91_rstc_base),
"r" (1),
"r" cpu_to_le32(AT91_SDRAMC_LPCB_POWER_DOWN),
"r" cpu_to_le32(AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST));
return NOTIFY_DONE;
}
static int at91sam9g45_restart(struct notifier_block *this, unsigned long mode,
void *cmd)
{
asm volatile( asm volatile(
/*
* Test wether we have a second RAM controller to care
* about.
*
* First, test that we can dereference the virtual address.
*/
"cmp %1, #0\n\t"
"beq 1f\n\t"
/* Then, test that the RAM controller is enabled */
"ldr r0, [%1]\n\t"
"cmp r0, #0\n\t"
/* Align to cache lines */ /* Align to cache lines */
".balign 32\n\t" ".balign 32\n\t"
/* Disable SDRAM0 accesses */ /* Disable SDRAM0 accesses */
"1: str %3, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" " tst %0, #0\n\t"
" beq 1f\n\t"
" str %3, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t"
/* Power down SDRAM0 */ /* Power down SDRAM0 */
" str %4, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t" " str %4, [%0, %6]\n\t"
/* Disable SDRAM1 accesses */ /* Disable SDRAM1 accesses */
"1: tst %1, #0\n\t"
" beq 2f\n\t"
" strne %3, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" " strne %3, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t"
/* Power down SDRAM1 */ /* Power down SDRAM1 */
" strne %4, [%1, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t" " strne %4, [%1, %6]\n\t"
/* Reset CPU */ /* Reset CPU */
" str %5, [%2, #" __stringify(AT91_RSTC_CR) "]\n\t" "2: str %5, [%2, #" __stringify(AT91_RSTC_CR) "]\n\t"
" b .\n\t" " b .\n\t"
: :
: "r" (at91_ramc_base[0]), : "r" (reset->ramc_base[0]),
"r" (at91_ramc_base[1]), "r" (reset->ramc_base[1]),
"r" (at91_rstc_base), "r" (reset->rstc_base),
"r" (1), "r" (1),
"r" cpu_to_le32(AT91_DDRSDRC_LPCB_POWER_DOWN), "r" cpu_to_le32(AT91_DDRSDRC_LPCB_POWER_DOWN),
"r" cpu_to_le32(AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST) "r" (reset->args),
: "r0"); "r" (reset->ramc_lpr)
: "r4");
return NOTIFY_DONE;
}
static int sama5d3_restart(struct notifier_block *this, unsigned long mode,
void *cmd)
{
writel(AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST,
at91_rstc_base);
return NOTIFY_DONE;
}
static int samx7_restart(struct notifier_block *this, unsigned long mode,
void *cmd)
{
writel(AT91_RSTC_KEY | AT91_RSTC_PROCRST, at91_rstc_base);
return NOTIFY_DONE; return NOTIFY_DONE;
} }
static void __init at91_reset_status(struct platform_device *pdev) static void __init at91_reset_status(struct platform_device *pdev,
void __iomem *base)
{ {
const char *reason; const char *reason;
u32 reg = readl(at91_rstc_base + AT91_RSTC_SR); u32 reg = readl(base + AT91_RSTC_SR);
switch ((reg & AT91_RSTC_RSTTYP) >> 8) { switch ((reg & AT91_RSTC_RSTTYP) >> 8) {
case RESET_TYPE_GENERAL: case RESET_TYPE_GENERAL:
...@@ -183,42 +142,68 @@ static void __init at91_reset_status(struct platform_device *pdev) ...@@ -183,42 +142,68 @@ static void __init at91_reset_status(struct platform_device *pdev)
} }
static const struct of_device_id at91_ramc_of_match[] = { static const struct of_device_id at91_ramc_of_match[] = {
{ .compatible = "atmel,at91sam9260-sdramc", }, {
{ .compatible = "atmel,at91sam9g45-ddramc", }, .compatible = "atmel,at91sam9260-sdramc",
.data = (void *)AT91_SDRAMC_LPR,
},
{
.compatible = "atmel,at91sam9g45-ddramc",
.data = (void *)AT91_DDRSDRC_LPR,
},
{ /* sentinel */ } { /* sentinel */ }
}; };
static const struct of_device_id at91_reset_of_match[] = { static const struct of_device_id at91_reset_of_match[] = {
{ .compatible = "atmel,at91sam9260-rstc", .data = at91sam9260_restart }, {
{ .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart }, .compatible = "atmel,at91sam9260-rstc",
{ .compatible = "atmel,sama5d3-rstc", .data = sama5d3_restart }, .data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PERRST |
{ .compatible = "atmel,samx7-rstc", .data = samx7_restart }, AT91_RSTC_PROCRST),
{ .compatible = "microchip,sam9x60-rstc", .data = samx7_restart }, },
{
.compatible = "atmel,at91sam9g45-rstc",
.data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PERRST |
AT91_RSTC_PROCRST)
},
{
.compatible = "atmel,sama5d3-rstc",
.data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PERRST |
AT91_RSTC_PROCRST)
},
{
.compatible = "atmel,samx7-rstc",
.data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PROCRST)
},
{
.compatible = "microchip,sam9x60-rstc",
.data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PROCRST)
},
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, at91_reset_of_match); MODULE_DEVICE_TABLE(of, at91_reset_of_match);
static struct notifier_block at91_restart_nb = {
.priority = 192,
};
static int __init at91_reset_probe(struct platform_device *pdev) static int __init at91_reset_probe(struct platform_device *pdev)
{ {
const struct of_device_id *match; const struct of_device_id *match;
struct at91_reset *reset;
struct device_node *np; struct device_node *np;
int ret, idx = 0; int ret, idx = 0;
at91_rstc_base = of_iomap(pdev->dev.of_node, 0); reset = devm_kzalloc(&pdev->dev, sizeof(*reset), GFP_KERNEL);
if (!at91_rstc_base) { if (!reset)
return -ENOMEM;
reset->rstc_base = of_iomap(pdev->dev.of_node, 0);
if (!reset->rstc_base) {
dev_err(&pdev->dev, "Could not map reset controller address\n"); dev_err(&pdev->dev, "Could not map reset controller address\n");
return -ENODEV; return -ENODEV;
} }
if (!of_device_is_compatible(pdev->dev.of_node, "atmel,sama5d3-rstc")) { if (!of_device_is_compatible(pdev->dev.of_node, "atmel,sama5d3-rstc")) {
/* we need to shutdown the ddr controller, so get ramc base */ /* we need to shutdown the ddr controller, so get ramc base */
for_each_matching_node(np, at91_ramc_of_match) { for_each_matching_node_and_match(np, at91_ramc_of_match, &match) {
at91_ramc_base[idx] = of_iomap(np, 0); reset->ramc_lpr = (u32)match->data;
if (!at91_ramc_base[idx]) { reset->ramc_base[idx] = of_iomap(np, 0);
if (!reset->ramc_base[idx]) {
dev_err(&pdev->dev, "Could not map ram controller address\n"); dev_err(&pdev->dev, "Could not map ram controller address\n");
of_node_put(np); of_node_put(np);
return -ENODEV; return -ENODEV;
...@@ -228,33 +213,46 @@ static int __init at91_reset_probe(struct platform_device *pdev) ...@@ -228,33 +213,46 @@ static int __init at91_reset_probe(struct platform_device *pdev)
} }
match = of_match_node(at91_reset_of_match, pdev->dev.of_node); match = of_match_node(at91_reset_of_match, pdev->dev.of_node);
at91_restart_nb.notifier_call = match->data; reset->nb.notifier_call = at91_reset;
reset->nb.priority = 192;
reset->args = (u32)match->data;
sclk = devm_clk_get(&pdev->dev, NULL); reset->sclk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(sclk)) if (IS_ERR(reset->sclk))
return PTR_ERR(sclk); return PTR_ERR(reset->sclk);
ret = clk_prepare_enable(sclk); ret = clk_prepare_enable(reset->sclk);
if (ret) { if (ret) {
dev_err(&pdev->dev, "Could not enable slow clock\n"); dev_err(&pdev->dev, "Could not enable slow clock\n");
return ret; return ret;
} }
ret = register_restart_handler(&at91_restart_nb); platform_set_drvdata(pdev, reset);
if (of_device_is_compatible(pdev->dev.of_node, "microchip,sam9x60-rstc")) {
u32 val = readl(reset->rstc_base + AT91_RSTC_MR);
writel(AT91_RSTC_KEY | AT91_RSTC_URSTASYNC | val,
reset->rstc_base + AT91_RSTC_MR);
}
ret = register_restart_handler(&reset->nb);
if (ret) { if (ret) {
clk_disable_unprepare(sclk); clk_disable_unprepare(reset->sclk);
return ret; return ret;
} }
at91_reset_status(pdev); at91_reset_status(pdev, reset->rstc_base);
return 0; return 0;
} }
static int __exit at91_reset_remove(struct platform_device *pdev) static int __exit at91_reset_remove(struct platform_device *pdev)
{ {
unregister_restart_handler(&at91_restart_nb); struct at91_reset *reset = platform_get_drvdata(pdev);
clk_disable_unprepare(sclk);
unregister_restart_handler(&reset->nb);
clk_disable_unprepare(reset->sclk);
return 0; return 0;
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/regmap.h> #include <linux/regmap.h>
...@@ -13,6 +14,8 @@ ...@@ -13,6 +14,8 @@
#define SC27XX_PWR_PD_HW 0xc2c #define SC27XX_PWR_PD_HW 0xc2c
#define SC27XX_PWR_OFF_EN BIT(0) #define SC27XX_PWR_OFF_EN BIT(0)
#define SC27XX_SLP_CTRL 0xdf0
#define SC27XX_LDO_XTL_EN BIT(3)
static struct regmap *regmap; static struct regmap *regmap;
...@@ -27,10 +30,13 @@ static struct regmap *regmap; ...@@ -27,10 +30,13 @@ static struct regmap *regmap;
*/ */
static void sc27xx_poweroff_shutdown(void) static void sc27xx_poweroff_shutdown(void)
{ {
#ifdef CONFIG_PM_SLEEP_SMP #ifdef CONFIG_HOTPLUG_CPU
int cpu = smp_processor_id(); int cpu;
freeze_secondary_cpus(cpu); for_each_online_cpu(cpu) {
if (cpu != smp_processor_id())
remove_cpu(cpu);
}
#endif #endif
} }
...@@ -40,6 +46,9 @@ static struct syscore_ops poweroff_syscore_ops = { ...@@ -40,6 +46,9 @@ static struct syscore_ops poweroff_syscore_ops = {
static void sc27xx_poweroff_do_poweroff(void) static void sc27xx_poweroff_do_poweroff(void)
{ {
/* Disable the external subsys connection's power firstly */
regmap_write(regmap, SC27XX_SLP_CTRL, SC27XX_LDO_XTL_EN);
regmap_write(regmap, SC27XX_PWR_PD_HW, SC27XX_PWR_OFF_EN); regmap_write(regmap, SC27XX_PWR_PD_HW, SC27XX_PWR_OFF_EN);
} }
...@@ -63,4 +72,8 @@ static struct platform_driver sc27xx_poweroff_driver = { ...@@ -63,4 +72,8 @@ static struct platform_driver sc27xx_poweroff_driver = {
.name = "sc27xx-poweroff", .name = "sc27xx-poweroff",
}, },
}; };
builtin_platform_driver(sc27xx_poweroff_driver); module_platform_driver(sc27xx_poweroff_driver);
MODULE_DESCRIPTION("Power off driver for SC27XX PMIC Device");
MODULE_AUTHOR("Baolin Wang <baolin.wang@unisoc.com>");
MODULE_LICENSE("GPL v2");
...@@ -480,7 +480,7 @@ config CHARGER_GPIO ...@@ -480,7 +480,7 @@ config CHARGER_GPIO
called gpio-charger. called gpio-charger.
config CHARGER_MANAGER config CHARGER_MANAGER
bool "Battery charger manager for multiple chargers" tristate "Battery charger manager for multiple chargers"
depends on REGULATOR depends on REGULATOR
select EXTCON select EXTCON
help help
......
...@@ -404,7 +404,7 @@ static void ab8500_enable_disable_sw_fallback(struct ab8500_charger *di, ...@@ -404,7 +404,7 @@ static void ab8500_enable_disable_sw_fallback(struct ab8500_charger *di,
} }
/** /**
* ab8500_power_supply_changed - a wrapper with local extentions for * ab8500_power_supply_changed - a wrapper with local extensions for
* power_supply_changed * power_supply_changed
* @di: pointer to the ab8500_charger structure * @di: pointer to the ab8500_charger structure
* @psy: pointer to power_supply_that have changed. * @psy: pointer to power_supply_that have changed.
...@@ -683,7 +683,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di, ...@@ -683,7 +683,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
/* /*
* Platform only supports USB 2.0. * Platform only supports USB 2.0.
* This means that charging current from USB source * This means that charging current from USB source
* is maximum 500 mA. Every occurence of USB_STAT_*_HOST_* * is maximum 500 mA. Every occurrence of USB_STAT_*_HOST_*
* should set USB_CH_IP_CUR_LVL_0P5. * should set USB_CH_IP_CUR_LVL_0P5.
*/ */
...@@ -1379,13 +1379,13 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger, ...@@ -1379,13 +1379,13 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger,
/* /*
* Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts * Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts
* will be triggered everytime we enable the VDD ADC supply. * will be triggered every time we enable the VDD ADC supply.
* This will turn off charging for a short while. * This will turn off charging for a short while.
* It can be avoided by having the supply on when * It can be avoided by having the supply on when
* there is a charger enabled. Normally the VDD ADC supply * there is a charger enabled. Normally the VDD ADC supply
* is enabled everytime a GPADC conversion is triggered. We will * is enabled every time a GPADC conversion is triggered.
* force it to be enabled from this driver to have * We will force it to be enabled from this driver to have
* the GPADC module independant of the AB8500 chargers * the GPADC module independent of the AB8500 chargers
*/ */
if (!di->vddadc_en_ac) { if (!di->vddadc_en_ac) {
ret = regulator_enable(di->regu); ret = regulator_enable(di->regu);
...@@ -1455,7 +1455,7 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger, ...@@ -1455,7 +1455,7 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger,
if (is_ab8500_1p1_or_earlier(di->parent)) { if (is_ab8500_1p1_or_earlier(di->parent)) {
/* /*
* For ABB revision 1.0 and 1.1 there is a bug in the * For ABB revision 1.0 and 1.1 there is a bug in the
* watchdog logic. That means we have to continously * watchdog logic. That means we have to continuously
* kick the charger watchdog even when no charger is * kick the charger watchdog even when no charger is
* connected. This is only valid once the AC charger * connected. This is only valid once the AC charger
* has been enabled. This is a bug that is not handled * has been enabled. This is a bug that is not handled
...@@ -1552,13 +1552,13 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger, ...@@ -1552,13 +1552,13 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
/* /*
* Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts * Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts
* will be triggered everytime we enable the VDD ADC supply. * will be triggered every time we enable the VDD ADC supply.
* This will turn off charging for a short while. * This will turn off charging for a short while.
* It can be avoided by having the supply on when * It can be avoided by having the supply on when
* there is a charger enabled. Normally the VDD ADC supply * there is a charger enabled. Normally the VDD ADC supply
* is enabled everytime a GPADC conversion is triggered. We will * is enabled every time a GPADC conversion is triggered.
* force it to be enabled from this driver to have * We will force it to be enabled from this driver to have
* the GPADC module independant of the AB8500 chargers * the GPADC module independent of the AB8500 chargers
*/ */
if (!di->vddadc_en_usb) { if (!di->vddadc_en_usb) {
ret = regulator_enable(di->regu); ret = regulator_enable(di->regu);
...@@ -1582,7 +1582,10 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger, ...@@ -1582,7 +1582,10 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
return -ENXIO; return -ENXIO;
} }
/* ChVoltLevel: max voltage upto which battery can be charged */ /*
* ChVoltLevel: max voltage up to which battery can be
* charged
*/
ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER, ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
AB8500_CH_VOLT_LVL_REG, (u8) volt_index); AB8500_CH_VOLT_LVL_REG, (u8) volt_index);
if (ret) { if (ret) {
...@@ -2014,7 +2017,7 @@ static void ab8500_charger_check_hw_failure_work(struct work_struct *work) ...@@ -2014,7 +2017,7 @@ static void ab8500_charger_check_hw_failure_work(struct work_struct *work)
* Work queue function for kicking the charger watchdog. * Work queue function for kicking the charger watchdog.
* *
* For ABB revision 1.0 and 1.1 there is a bug in the watchdog * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
* logic. That means we have to continously kick the charger * logic. That means we have to continuously kick the charger
* watchdog even when no charger is connected. This is only * watchdog even when no charger is connected. This is only
* valid once the AC charger has been enabled. This is * valid once the AC charger has been enabled. This is
* a bug that is not handled by the algorithm and the * a bug that is not handled by the algorithm and the
...@@ -2262,7 +2265,7 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work) ...@@ -2262,7 +2265,7 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
* Some chargers that breaks the USB spec is * Some chargers that breaks the USB spec is
* identified as invalid by AB8500 and it refuse * identified as invalid by AB8500 and it refuse
* to start the charging process. but by jumping * to start the charging process. but by jumping
* thru a few hoops it can be forced to start. * through a few hoops it can be forced to start.
*/ */
if (is_ab8500(di->parent)) if (is_ab8500(di->parent))
ret = abx500_get_register_interruptible(di->dev, AB8500_USB, ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
...@@ -3214,7 +3217,7 @@ static int ab8500_charger_resume(struct platform_device *pdev) ...@@ -3214,7 +3217,7 @@ static int ab8500_charger_resume(struct platform_device *pdev)
/* /*
* For ABB revision 1.0 and 1.1 there is a bug in the watchdog * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
* logic. That means we have to continously kick the charger * logic. That means we have to continuously kick the charger
* watchdog even when no charger is connected. This is only * watchdog even when no charger is connected. This is only
* valid once the AC charger has been enabled. This is * valid once the AC charger has been enabled. This is
* a bug that is not handled by the algorithm and the * a bug that is not handled by the algorithm and the
...@@ -3483,7 +3486,7 @@ static int ab8500_charger_probe(struct platform_device *pdev) ...@@ -3483,7 +3486,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
/* /*
* For ABB revision 1.0 and 1.1 there is a bug in the watchdog * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
* logic. That means we have to continously kick the charger * logic. That means we have to continuously kick the charger
* watchdog even when no charger is connected. This is only * watchdog even when no charger is connected. This is only
* valid once the AC charger has been enabled. This is * valid once the AC charger has been enabled. This is
* a bug that is not handled by the algorithm and the * a bug that is not handled by the algorithm and the
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/property.h> #include <linux/property.h>
#include <linux/mfd/axp20x.h> #include <linux/mfd/axp20x.h>
#include <linux/extcon.h> #include <linux/extcon.h>
#include <linux/dmi.h>
#define PS_STAT_VBUS_TRIGGER BIT(0) #define PS_STAT_VBUS_TRIGGER BIT(0)
#define PS_STAT_BAT_CHRG_DIR BIT(2) #define PS_STAT_BAT_CHRG_DIR BIT(2)
...@@ -545,6 +546,49 @@ static irqreturn_t axp288_charger_irq_thread_handler(int irq, void *dev) ...@@ -545,6 +546,49 @@ static irqreturn_t axp288_charger_irq_thread_handler(int irq, void *dev)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
/*
* The HP Pavilion x2 10 series comes in a number of variants:
* Bay Trail SoC + AXP288 PMIC, DMI_BOARD_NAME: "815D"
* Cherry Trail SoC + AXP288 PMIC, DMI_BOARD_NAME: "813E"
* Cherry Trail SoC + TI PMIC, DMI_BOARD_NAME: "827C" or "82F4"
*
* The variants with the AXP288 PMIC are all kinds of special:
*
* 1. All variants use a Type-C connector which the AXP288 does not support, so
* when using a Type-C charger it is not recognized. Unlike most AXP288 devices,
* this model actually has mostly working ACPI AC / Battery code, the ACPI code
* "solves" this by simply setting the input_current_limit to 3A.
* There are still some issues with the ACPI code, so we use this native driver,
* and to solve the charging not working (500mA is not enough) issue we hardcode
* the 3A input_current_limit like the ACPI code does.
*
* 2. If no charger is connected the machine boots with the vbus-path disabled.
* Normally this is done when a 5V boost converter is active to avoid the PMIC
* trying to charge from the 5V boost converter's output. This is done when
* an OTG host cable is inserted and the ID pin on the micro-B receptacle is
* pulled low and the ID pin has an ACPI event handler associated with it
* which re-enables the vbus-path when the ID pin is pulled high when the
* OTG host cable is removed. The Type-C connector has no ID pin, there is
* no ID pin handler and there appears to be no 5V boost converter, so we
* end up not charging because the vbus-path is disabled, until we unplug
* the charger which automatically clears the vbus-path disable bit and then
* on the second plug-in of the adapter we start charging. To solve the not
* charging on first charger plugin we unconditionally enable the vbus-path at
* probe on this model, which is safe since there is no 5V boost converter.
*/
static const struct dmi_system_id axp288_hp_x2_dmi_ids[] = {
{
/*
* Bay Trail model has "Hewlett-Packard" as sys_vendor, Cherry
* Trail model has "HP", so we only match on product_name.
*/
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),
},
},
{} /* Terminating entry */
};
static void axp288_charger_extcon_evt_worker(struct work_struct *work) static void axp288_charger_extcon_evt_worker(struct work_struct *work)
{ {
struct axp288_chrg_info *info = struct axp288_chrg_info *info =
...@@ -568,7 +612,11 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work) ...@@ -568,7 +612,11 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work)
} }
/* Determine cable/charger type */ /* Determine cable/charger type */
if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) { if (dmi_check_system(axp288_hp_x2_dmi_ids)) {
/* See comment above axp288_hp_x2_dmi_ids declaration */
dev_dbg(&info->pdev->dev, "HP X2 with Type-C, setting inlmt to 3A\n");
current_limit = 3000000;
} else if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) {
dev_dbg(&info->pdev->dev, "USB SDP charger is connected\n"); dev_dbg(&info->pdev->dev, "USB SDP charger is connected\n");
current_limit = 500000; current_limit = 500000;
} else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) { } else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) {
...@@ -685,6 +733,13 @@ static int charger_init_hw_regs(struct axp288_chrg_info *info) ...@@ -685,6 +733,13 @@ static int charger_init_hw_regs(struct axp288_chrg_info *info)
return ret; return ret;
} }
if (dmi_check_system(axp288_hp_x2_dmi_ids)) {
/* See comment above axp288_hp_x2_dmi_ids declaration */
ret = axp288_charger_vbus_path_select(info, true);
if (ret < 0)
return ret;
}
/* Read current charge voltage and current limit */ /* Read current charge voltage and current limit */
ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val); ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val);
if (ret < 0) { if (ret < 0) {
......
...@@ -706,14 +706,14 @@ static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = { ...@@ -706,14 +706,14 @@ static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = {
{ {
/* Intel Cherry Trail Compute Stick, Windows version */ /* Intel Cherry Trail Compute Stick, Windows version */
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), DMI_MATCH(DMI_SYS_VENDOR, "Intel"),
DMI_MATCH(DMI_PRODUCT_NAME, "STK1AW32SC"), DMI_MATCH(DMI_PRODUCT_NAME, "STK1AW32SC"),
}, },
}, },
{ {
/* Intel Cherry Trail Compute Stick, version without an OS */ /* Intel Cherry Trail Compute Stick, version without an OS */
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), DMI_MATCH(DMI_SYS_VENDOR, "Intel"),
DMI_MATCH(DMI_PRODUCT_NAME, "STK1A32SC"), DMI_MATCH(DMI_PRODUCT_NAME, "STK1A32SC"),
}, },
}, },
......
...@@ -1885,6 +1885,9 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di) ...@@ -1885,6 +1885,9 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
di->bat = power_supply_register_no_ws(di->dev, psy_desc, &psy_cfg); di->bat = power_supply_register_no_ws(di->dev, psy_desc, &psy_cfg);
if (IS_ERR(di->bat)) { if (IS_ERR(di->bat)) {
if (PTR_ERR(di->bat) == -EPROBE_DEFER)
dev_dbg(di->dev, "failed to register battery, deferring probe\n");
else
dev_err(di->dev, "failed to register battery\n"); dev_err(di->dev, "failed to register battery\n");
return PTR_ERR(di->bat); return PTR_ERR(di->bat);
} }
......
...@@ -148,6 +148,7 @@ static int ingenic_battery_probe(struct platform_device *pdev) ...@@ -148,6 +148,7 @@ static int ingenic_battery_probe(struct platform_device *pdev)
bat->battery = devm_power_supply_register(dev, desc, &psy_cfg); bat->battery = devm_power_supply_register(dev, desc, &psy_cfg);
if (IS_ERR(bat->battery)) { if (IS_ERR(bat->battery)) {
if (PTR_ERR(bat->battery) != -EPROBE_DEFER)
dev_err(dev, "Unable to register battery\n"); dev_err(dev, "Unable to register battery\n");
return PTR_ERR(bat->battery); return PTR_ERR(bat->battery);
} }
......
...@@ -614,6 +614,17 @@ static int sc27xx_fgu_get_property(struct power_supply *psy, ...@@ -614,6 +614,17 @@ static int sc27xx_fgu_get_property(struct power_supply *psy,
val->intval = data->total_cap * 1000; val->intval = data->total_cap * 1000;
break; break;
case POWER_SUPPLY_PROP_CHARGE_NOW:
ret = sc27xx_fgu_get_clbcnt(data, &value);
if (ret)
goto error;
value = DIV_ROUND_CLOSEST(value * 10,
36 * SC27XX_FGU_SAMPLE_HZ);
val->intval = sc27xx_fgu_adc_to_current(data, value);
break;
default: default:
ret = -EINVAL; ret = -EINVAL;
break; break;
...@@ -682,6 +693,7 @@ static enum power_supply_property sc27xx_fgu_props[] = { ...@@ -682,6 +693,7 @@ static enum power_supply_property sc27xx_fgu_props[] = {
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
POWER_SUPPLY_PROP_CALIBRATE, POWER_SUPPLY_PROP_CALIBRATE,
POWER_SUPPLY_PROP_CHARGE_NOW
}; };
static const struct power_supply_desc sc27xx_fgu_desc = { static const struct power_supply_desc sc27xx_fgu_desc = {
......
...@@ -726,10 +726,10 @@ twl4030_bci_mode_show(struct device *dev, ...@@ -726,10 +726,10 @@ twl4030_bci_mode_show(struct device *dev,
for (i = 0; i < ARRAY_SIZE(modes); i++) for (i = 0; i < ARRAY_SIZE(modes); i++)
if (mode == i) if (mode == i)
len += snprintf(buf+len, PAGE_SIZE-len, len += scnprintf(buf+len, PAGE_SIZE-len,
"[%s] ", modes[i]); "[%s] ", modes[i]);
else else
len += snprintf(buf+len, PAGE_SIZE-len, len += scnprintf(buf+len, PAGE_SIZE-len,
"%s ", modes[i]); "%s ", modes[i]);
buf[len-1] = '\n'; buf[len-1] = '\n';
return len; return len;
......
...@@ -248,7 +248,7 @@ struct charger_manager { ...@@ -248,7 +248,7 @@ struct charger_manager {
u64 charging_end_time; u64 charging_end_time;
}; };
#ifdef CONFIG_CHARGER_MANAGER #if IS_ENABLED(CONFIG_CHARGER_MANAGER)
extern void cm_notify_event(struct power_supply *psy, extern void cm_notify_event(struct power_supply *psy,
enum cm_event_types type, char *msg); enum cm_event_types type, char *msg);
#else #else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment