From 6ab739bc1d00bfd10ed87a2f1fdc00ebdc0d7ff1 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 16 Jan 2018 11:12:35 +0100 Subject: power: reset: Add a driver for the Microsemi Ocelot reset The Microsemi Ocelot SoC has a register allowing to reset the MIPS core. Unfortunately, the syscon-reboot driver can't be used directly (but almost) as the reset control may be disabled using another register. Cc: linux-pm@vger.kernel.org Signed-off-by: Alexandre Belloni Signed-off-by: Sebastian Reichel --- drivers/power/reset/Kconfig | 7 +++ drivers/power/reset/Makefile | 1 + drivers/power/reset/ocelot-reset.c | 88 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 drivers/power/reset/ocelot-reset.c (limited to 'drivers/power') diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index a102e74ab24e..176bd6df1238 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 diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index dcc92f5f7a37..6b9eb1393179 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 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); -- cgit v1.2.3-59-g8ed1b From 3bf4e03d199f7f2871919e33e7bfc6e3d4c3368f Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Wed, 17 Jan 2018 20:48:54 +0100 Subject: power: supply: ltc2941-battery-gauge: Add charge empty and full properties Add properties for charge empty and charge full thresholds. Signed-off-by: Ladislav Michl Signed-off-by: Sebastian Reichel --- drivers/power/supply/ltc2941-battery-gauge.c | 59 +++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 9 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/ltc2941-battery-gauge.c b/drivers/power/supply/ltc2941-battery-gauge.c index 4cfa3f0cd689..9360faca7026 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; @@ -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, -- cgit v1.2.3-59-g8ed1b From 419cac572b79eda70f796c452895fefeee86a79a Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Wed, 17 Jan 2018 21:31:14 +0100 Subject: power: supply: gpio-charger: Drop driver remove function Simplify error unwinding using devm_* allocators. This also makes driver remove function empty, so remove it. Signed-off-by: Ladislav Michl Signed-off-by: Sebastian Reichel --- drivers/power/supply/gpio-charger.c | 43 ++++++------------------------------- 1 file changed, 7 insertions(+), 36 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/gpio-charger.c b/drivers/power/supply/gpio-charger.c index 001731e88718..666abc23e2ee 100644 --- a/drivers/power/supply/gpio-charger.c +++ b/drivers/power/supply/gpio-charger.c @@ -35,7 +35,6 @@ struct gpio_charger { 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) @@ -159,19 +158,13 @@ static int gpio_charger_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Invalid gpio pin in pdata\n"); return -EINVAL; } - ret = gpio_request(pdata->gpio, dev_name(&pdev->dev)); + ret = devm_gpio_request_one(&pdev->dev, pdata->gpio, GPIOF_IN, + dev_name(&pdev->dev)); if (ret) { dev_err(&pdev->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)) { @@ -195,20 +188,19 @@ static int gpio_charger_probe(struct platform_device *pdev) psy_cfg.of_node = pdev->dev.of_node; psy_cfg.drv_data = gpio_charger; - gpio_charger->pdata = pdata; - - gpio_charger->charger = power_supply_register(&pdev->dev, - charger_desc, &psy_cfg); + gpio_charger->charger = devm_power_supply_register(&pdev->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; + 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(&pdev->dev, irq, + gpio_charger_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), gpio_charger->charger); if (ret < 0) @@ -222,26 +214,6 @@ static int gpio_charger_probe(struct platform_device *pdev) 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); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -280,7 +252,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, -- cgit v1.2.3-59-g8ed1b From 416a1ae673db0918fbe6707c7d6f4459598e2c65 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Wed, 17 Jan 2018 21:31:49 +0100 Subject: power: supply: gpio-charger: use helper variable to access device info Using explicit struct device variable makes code a bit more readable. Signed-off-by: Ladislav Michl Signed-off-by: Sebastian Reichel --- drivers/power/supply/gpio-charger.c | 42 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 22 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/gpio-charger.c b/drivers/power/supply/gpio-charger.c index 666abc23e2ee..f0d8cc19ad1e 100644 --- a/drivers/power/supply/gpio-charger.c +++ b/drivers/power/supply/gpio-charger.c @@ -118,7 +118,8 @@ struct gpio_charger_platform_data *gpio_charger_parse_dt(struct device *dev) 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; @@ -126,19 +127,18 @@ static int gpio_charger_probe(struct platform_device *pdev) int irq; if (!pdata) { - pdata = gpio_charger_parse_dt(&pdev->dev); + pdata = gpio_charger_parse_dt(dev); if (IS_ERR(pdata)) { ret = PTR_ERR(pdata); if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "No platform data\n"); + dev_err(dev, "No platform data\n"); return ret; } } - gpio_charger = devm_kzalloc(&pdev->dev, sizeof(*gpio_charger), - GFP_KERNEL); + gpio_charger = devm_kzalloc(dev, sizeof(*gpio_charger), GFP_KERNEL); if (!gpio_charger) { - dev_err(&pdev->dev, "Failed to alloc driver structure\n"); + dev_err(dev, "Failed to alloc driver structure\n"); return -ENOMEM; } @@ -146,20 +146,20 @@ static int gpio_charger_probe(struct platform_device *pdev) * 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 = devm_gpio_request_one(&pdev->dev, pdata->gpio, GPIOF_IN, - dev_name(&pdev->dev)); + ret = devm_gpio_request_one(dev, pdata->gpio, GPIOF_IN, + dev_name(dev)); if (ret) { dev_err(&pdev->dev, "Failed to request gpio pin: %d\n", ret); @@ -171,7 +171,7 @@ static int gpio_charger_probe(struct platform_device *pdev) /* 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); } @@ -185,33 +185,31 @@ static int gpio_charger_probe(struct platform_device *pdev) 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->charger = devm_power_supply_register(&pdev->dev, - charger_desc, &psy_cfg); + 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); + dev_err(dev, "Failed to register power supply: %d\n", ret); return ret; } irq = gpiod_to_irq(gpio_charger->gpiod); if (irq > 0) { - ret = devm_request_any_context_irq(&pdev->dev, 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); + device_init_wakeup(dev, 1); return 0; } -- cgit v1.2.3-59-g8ed1b From dde5953f05a89eb63a0d666ffe51d447b2ac3e05 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Thu, 22 Feb 2018 18:21:36 +0100 Subject: power: supply: ltc2941-battery-gauge: Fix temperature units Temperature is measured in tenths of degree Celsius. Fixes: 085bc24d1553 ("Add LTC2941/LTC2943 Battery Gauge Driver") Signed-off-by: Ladislav Michl Signed-off-by: Sebastian Reichel --- drivers/power/supply/ltc2941-battery-gauge.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/ltc2941-battery-gauge.c b/drivers/power/supply/ltc2941-battery-gauge.c index 4cfa3f0cd689..cc7c516bb417 100644 --- a/drivers/power/supply/ltc2941-battery-gauge.c +++ b/drivers/power/supply/ltc2941-battery-gauge.c @@ -317,15 +317,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; } -- cgit v1.2.3-59-g8ed1b From 4a9be940551ab664918ac089b92c47d74e6cb8e7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 22 Feb 2018 08:45:21 +0100 Subject: power: gemini-poweroff: Avoid spurious poweroff On the D-Link DIR-685 we get spurious poweroff from infrared. Since that block (CIR) doesn't even have a driver this can be safely ignored, we can revisit this code once we have a device supporting CIR. On the D-Link DNS-313 we get spurious poweroff from the power button. This appears to be an initialization issue: we need to enable the block (start the state machine) before we clear any dangling IRQ. This patch fixes both issues. Fixes: f7a388d6cd1c ("power: reset: Add a driver for the Gemini poweroff") Signed-off-by: Linus Walleij Signed-off-by: Sebastian Reichel --- drivers/power/reset/gemini-poweroff.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'drivers/power') 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; -- cgit v1.2.3-59-g8ed1b From d433d04bb70085ceea2815cdb879d4e6fae470d9 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Mon, 5 Mar 2018 19:04:14 +0100 Subject: power: supply: gpio-charger: Remove redundant dev_err call in probe function There is an error message within devm_kzalloc already. Signed-off-by: Ladislav Michl Signed-off-by: Sebastian Reichel --- drivers/power/supply/gpio-charger.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/gpio-charger.c b/drivers/power/supply/gpio-charger.c index f0d8cc19ad1e..78b55566e215 100644 --- a/drivers/power/supply/gpio-charger.c +++ b/drivers/power/supply/gpio-charger.c @@ -137,10 +137,8 @@ static int gpio_charger_probe(struct platform_device *pdev) } gpio_charger = devm_kzalloc(dev, sizeof(*gpio_charger), GFP_KERNEL); - if (!gpio_charger) { - dev_err(dev, "Failed to alloc driver structure\n"); + if (!gpio_charger) return -ENOMEM; - } /* * This will fetch a GPIO descriptor from device tree, ACPI or -- cgit v1.2.3-59-g8ed1b From f5fec4cc2949421af74bdd005abd99fcbaf9d0d7 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Mon, 5 Mar 2018 19:05:18 +0100 Subject: power: supply: gpio-charger: Use GPIOF_ACTIVE_LOW for legacy setup Setting GPIOF_ACTIVE_LOW flag based on platform data gpio_active_low makes return value of gpiod_get_value_cansleep directly usable. Signed-off-by: Ladislav Michl Signed-off-by: Sebastian Reichel --- drivers/power/supply/gpio-charger.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/gpio-charger.c b/drivers/power/supply/gpio-charger.c index 78b55566e215..768bbcad97cb 100644 --- a/drivers/power/supply/gpio-charger.c +++ b/drivers/power/supply/gpio-charger.c @@ -55,13 +55,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; @@ -123,8 +120,8 @@ static int gpio_charger_probe(struct platform_device *pdev) 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(dev); @@ -156,11 +153,13 @@ static int gpio_charger_probe(struct platform_device *pdev) dev_err(dev, "Invalid gpio pin in pdata\n"); return -EINVAL; } - ret = devm_gpio_request_one(dev, pdata->gpio, GPIOF_IN, - dev_name(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; } /* Then convert this to gpiod for now */ -- cgit v1.2.3-59-g8ed1b From d47c1e4b2f51e81683b087480401b9b42b2a58fd Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Mon, 5 Mar 2018 19:05:55 +0100 Subject: power: supply: gpio-charger: Remove pdata from gpio_charger Platform data are now used only during probe time, so remove them from gpio_charger structure and consolidate probing function accordingly. Signed-off-by: Ladislav Michl [Replace of_property_read_string with dev_property_read_string] Signed-off-by: Sebastian Reichel --- drivers/power/supply/gpio-charger.c | 95 ++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 54 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/gpio-charger.c b/drivers/power/supply/gpio-charger.c index 768bbcad97cb..bd2468ca6b63 100644 --- a/drivers/power/supply/gpio-charger.c +++ b/drivers/power/supply/gpio-charger.c @@ -28,7 +28,6 @@ #include struct gpio_charger { - const struct gpio_charger_platform_data *pdata; unsigned int irq; bool wakeup_enabled; @@ -67,52 +66,37 @@ static int gpio_charger_get_property(struct power_supply *psy, return 0; } -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) +static enum power_supply_type gpio_charger_get_type(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); + + 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 pdata; + return POWER_SUPPLY_TYPE_UNKNOWN; } +static enum power_supply_property gpio_charger_properties[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + static int gpio_charger_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -123,14 +107,9 @@ static int gpio_charger_probe(struct platform_device *pdev) unsigned long flags; int irq, ret; - if (!pdata) { - pdata = gpio_charger_parse_dt(dev); - if (IS_ERR(pdata)) { - ret = PTR_ERR(pdata); - if (ret != -EPROBE_DEFER) - dev_err(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(dev, sizeof(*gpio_charger), GFP_KERNEL); @@ -173,18 +152,26 @@ static int gpio_charger_probe(struct platform_device *pdev) } 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 = dev->of_node; psy_cfg.drv_data = gpio_charger; + 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); + } + + 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)) { -- cgit v1.2.3-59-g8ed1b From 648badd797c762a713b48afc3b67d56abdd0073b Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Wed, 28 Feb 2018 11:35:58 +0100 Subject: power: supply: axp20x_battery: use data struct for variant specific code We used to use IDs to select a function or a feature depending on the variant. It's easier to maintain the code by adding data structure storing the few differences between variants so that we don't add a pile of if conditions. Let's use this data structure and update the code to use it. Signed-off-by: Quentin Schulz Reviewed-by: Chen-Yu Tsai [updated POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN write property to use the introduced set_max_voltage() callback] Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp20x_battery.c | 102 ++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 35 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c index 7494f0f0eadb..738470077f1f 100644 --- a/drivers/power/supply/axp20x_battery.c +++ b/drivers/power/supply/axp20x_battery.c @@ -53,6 +53,16 @@ #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 +72,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,22 +133,6 @@ 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) -{ - if (axp->axp_id == AXP209_ID) - *val = *val * 100000 + 300000; - else - *val = *val * 150000 + 300000; -} - -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; -} - static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp, int *val) { @@ -150,7 +144,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 +263,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 +274,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 +302,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 +337,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 +364,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 +379,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 +429,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 +476,28 @@ 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 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, }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, axp20x_battery_ps_id); @@ -476,6 +507,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 +548,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, -- cgit v1.2.3-59-g8ed1b From 6ff653e3e8b9cd53cf490e8bea888e8af3e39ea7 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Wed, 28 Feb 2018 11:36:00 +0100 Subject: power: supply: axp20x_battery: add support for AXP813 The X-Powers AXP813 PMIC has got some slight differences from AXP20X/AXP22X PMICs: - the maximum voltage supplied by the PMIC is 4.35 instead of 4.36/4.24 for AXP20X/AXP22X, - the constant charge current formula is different, It also has a bit to tell whether the battery percentage returned by the PMIC is valid. Signed-off-by: Quentin Schulz Reviewed-by: Chen-Yu Tsai Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp20x_battery.c | 42 +++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c index 738470077f1f..e84b6e4da14a 100644 --- a/drivers/power/supply/axp20x_battery.c +++ b/drivers/power/supply/axp20x_battery.c @@ -49,6 +49,8 @@ #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) @@ -133,6 +135,35 @@ static int axp22x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, return 0; } +static int axp813_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, + int *val) +{ + int ret, reg; + + 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, int *val) { @@ -491,6 +522,14 @@ static const struct axp_data axp221_data = { .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", @@ -498,6 +537,9 @@ static const struct of_device_id axp20x_battery_ps_id[] = { }, { .compatible = "x-powers,axp221-battery-power-supply", .data = (void *)&axp221_data, + }, { + .compatible = "x-powers,axp813-battery-power-supply", + .data = (void *)&axp813_data, }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, axp20x_battery_ps_id); -- cgit v1.2.3-59-g8ed1b From 3f5faf3a0667ea39faf7152c5bdd4befb9e483a8 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 26 Feb 2018 10:23:14 +0800 Subject: power: reset: Add Spreadtrum SC27xx PMIC power off support On Spreadtrum platform, we need power off system through external SC27xx series PMICs including the SC2720, SC2721, SC2723, SC2730 and SC2731 chips. Thus this patch adds SC27xx series PMICs power-off support. Signed-off-by: Baolin Wang Signed-off-by: Sebastian Reichel --- drivers/power/reset/Kconfig | 9 +++++ drivers/power/reset/Makefile | 1 + drivers/power/reset/sc27xx-poweroff.c | 66 +++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 drivers/power/reset/sc27xx-poweroff.c (limited to 'drivers/power') diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 176bd6df1238..df58fc878b3e 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -225,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 6b9eb1393179..7778c7485cf1 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -27,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/sc27xx-poweroff.c b/drivers/power/reset/sc27xx-poweroff.c new file mode 100644 index 000000000000..65c477eee559 --- /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. + */ +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); -- cgit v1.2.3-59-g8ed1b From d85b4f7b7fae40aabacc3fe11ded95b4a2bbc49f Mon Sep 17 00:00:00 2001 From: Moritz Fischer Date: Thu, 22 Feb 2018 15:17:14 -0800 Subject: power: reset: gpio-poweroff: Support for timeout from device property Add support for reading a timeout value from device property. Fall back to previous default of 3s if nothing is specified. Signed-off-by: Moritz Fischer Signed-off-by: Sebastian Reichel --- drivers/power/reset/gpio-poweroff.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/power') 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); -- cgit v1.2.3-59-g8ed1b From 7638eb5666eb3c216dfdef63b573933ed9740676 Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Fri, 16 Feb 2018 08:26:16 +0000 Subject: power: supply: axp288_fuel_gauge: Do not register FG on ECS EF20EA The ECS EF20EA laptop ships an AXP288 but it is actually using a different, separate FG chip for AC and battery monitoring. On this laptop we need to keep using the regular ACPI driver and disable the AXP288 FG to avoid reporting two batteries to userspace. Signed-off-by: Carlo Caione Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_fuel_gauge.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index 4cc6e038dfdd..903891a9bcf0 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -708,6 +708,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"), + }, + }, {} }; -- cgit v1.2.3-59-g8ed1b From f451655c722b6f8a15d152d7912ab8526a3fe8bd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 15 Feb 2018 15:00:36 +0100 Subject: power: supply: axp288_fuel_gauge: Fix full status reporting Commit 2b5a4b4bf222 ("power: supply: axp288_fuel_gauge: Rework get_status()"), switched from 0A current detection to using the capacity register for full detection. It turns out this fixes full reporting on some devices which keep trickle charging long after the capacity register reach 100%, but breaks it on some other devices where the charger stops charging before the capacity register reaches 100%. This commit fixes this by also checking for 0A current when the reported capacity is above 90%. Fixes: 2b5a4b4bf222 ("psy: axp288_fuel_gauge: Rework get_status()") Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_fuel_gauge.c | 47 ++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 12 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index 903891a9bcf0..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 -- cgit v1.2.3-59-g8ed1b From 66ec32fc7cd116dab5c02603ea8ec28ff92da3b5 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 20 Feb 2018 16:03:18 +0100 Subject: max17042: propagate of_node to power supply device max17042_get_status uses the core power_supply_am_i_supplied. That function relies on DT properties to figure out the power supply topology, and will error out without DT. Fixes max17042 battery status being reported as "unknown". Signed-off-by: Pierre Bourdon Signed-off-by: Andre Heider Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/power') 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. */ -- cgit v1.2.3-59-g8ed1b From f72c14ad8c6631a0a42115e74164f9b5faf4c724 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Thu, 15 Feb 2018 21:28:40 +0100 Subject: power: supply: bq27xxx: support missing supplier device power_supply_am_i_supplied() can return negative error codes. In this case we should assume, that no charger is connected and the battery should be marked as DISCHARGING instead of NOT_CHARGING. Reported-by: Merlijn Wajer Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq27xxx_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') 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; -- cgit v1.2.3-59-g8ed1b From 90ad4cc203c025788ccaf70dc1e57fbf43702c4d Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 21 Feb 2018 12:42:20 +0100 Subject: power: supply: bq2415x: add DT referencing support Add support for using bq2415x together with power_supply_am_i_supplied(). Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq2415x_charger.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/power') 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; -- cgit v1.2.3-59-g8ed1b From fc5a7f0339d0641c0df14a9ed8af49c9992f9bef Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 9 Mar 2018 12:27:08 -0600 Subject: power: supply: da9150-fg: remove VLA usage In preparation to enabling -Wvla, remove VLA usage and replace it with fixed-length arrays. DA9150_QIF_LONG_SIZE (4 bytes) is the biggest size of an attribute which can be accessed [1]. Fixed as part of the directive to remove all VLAs from the kernel: https://lkml.org/lkml/2018/3/7/621 [1] https://marc.info/?l=kernel-hardening&m=152059600524753&w=2 Signed-off-by: Gustavo A. R. Silva Acked-by: Adam Thomson Signed-off-by: Sebastian Reichel --- drivers/power/supply/da9150-fg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/power') 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; -- cgit v1.2.3-59-g8ed1b From 93619fdec96fa1cd19e5dffc5031cf4c63e6b431 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 13 Mar 2018 10:54:06 +0000 Subject: power: reset: make function sc27xx_poweroff_shutdown static The function sc27xx_poweroff_shutdown is local to the source and does not need to be in global scope, so make it static. Cleans up sparse warning: drivers/power/reset/sc27xx-poweroff.c:28:6: warning: symbol 'sc27xx_poweroff_shutdown' was not declared. Should it be static? Signed-off-by: Colin Ian King Signed-off-by: Sebastian Reichel --- drivers/power/reset/sc27xx-poweroff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/sc27xx-poweroff.c b/drivers/power/reset/sc27xx-poweroff.c index 65c477eee559..29fb08b8faa0 100644 --- a/drivers/power/reset/sc27xx-poweroff.c +++ b/drivers/power/reset/sc27xx-poweroff.c @@ -25,7 +25,7 @@ static struct regmap *regmap; * taking cpus down to avoid racing regmap or spi mutex lock when poweroff * system through PMIC. */ -void sc27xx_poweroff_shutdown(void) +static void sc27xx_poweroff_shutdown(void) { #ifdef CONFIG_PM_SLEEP_SMP int cpu = smp_processor_id(); -- cgit v1.2.3-59-g8ed1b From 062836db01f982683e3c5bb6cbf571ee7a13016a Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Fri, 16 Mar 2018 11:02:05 +0100 Subject: power: reset: at91-poweroff: Switch from the pr_*() to the dev_*() logging functions Use dev_info() instead of pr_info(). Signed-off-by: Ladislav Michl Acked-by: Nicolas Ferre Signed-off-by: Sebastian Reichel --- drivers/power/reset/at91-poweroff.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c index c30c40193aaa..f8f88798cb66 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) @@ -172,7 +174,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); -- cgit v1.2.3-59-g8ed1b From ab08824826d72ef9c185b7aad4e9a1e98bf2fa51 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Fri, 16 Mar 2018 11:03:25 +0100 Subject: power: reset: at91-poweroff: Remove redundant dev_err call in at91_poweroff_probe() There is an error message within devm_ioremap_resource already, so remove the dev_err call to avoid redundancy. Signed-off-by: Ladislav Michl Acked-by: Nicolas Ferre Signed-off-by: Sebastian Reichel --- drivers/power/reset/at91-poweroff.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c index f8f88798cb66..fb2fc8f741a1 100644 --- a/drivers/power/reset/at91-poweroff.c +++ b/drivers/power/reset/at91-poweroff.c @@ -159,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)) -- cgit v1.2.3-59-g8ed1b From fd73a3e618889877d32e338622a363d04d9be709 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Fri, 16 Mar 2018 11:06:26 +0100 Subject: power: reset: at91-reset: Switch from the pr_*() to the dev_*() logging functions Use dev_info() instead of pr_info(). Signed-off-by: Ladislav Michl Acked-by: Nicolas Ferre Signed-off-by: Sebastian Reichel --- drivers/power/reset/at91-reset.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/power') 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[] = { -- cgit v1.2.3-59-g8ed1b