diff options
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/Kconfig | 2 | ||||
-rw-r--r-- | drivers/power/axp20x_usb_power.c | 92 | ||||
-rw-r--r-- | drivers/power/axp288_charger.c | 77 | ||||
-rw-r--r-- | drivers/power/bq27xxx_battery.c | 289 | ||||
-rw-r--r-- | drivers/power/bq27xxx_battery_i2c.c | 2 | ||||
-rw-r--r-- | drivers/power/max8903_charger.c | 239 | ||||
-rw-r--r-- | drivers/power/power_supply_core.c | 33 | ||||
-rw-r--r-- | drivers/power/power_supply_sysfs.c | 2 | ||||
-rw-r--r-- | drivers/power/qcom_smbb.c | 21 | ||||
-rw-r--r-- | drivers/power/reset/Kconfig | 17 | ||||
-rw-r--r-- | drivers/power/reset/Makefile | 2 | ||||
-rw-r--r-- | drivers/power/reset/reboot-mode.c | 140 | ||||
-rw-r--r-- | drivers/power/reset/reboot-mode.h | 14 | ||||
-rw-r--r-- | drivers/power/reset/syscon-poweroff.c | 2 | ||||
-rw-r--r-- | drivers/power/reset/syscon-reboot-mode.c | 99 | ||||
-rw-r--r-- | drivers/power/reset/vexpress-poweroff.c | 4 | ||||
-rw-r--r-- | drivers/power/tps65217_charger.c | 6 |
17 files changed, 760 insertions, 281 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 421770ddafa3..acd4a1524a1e 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -309,6 +309,7 @@ config BATTERY_RX51 config CHARGER_ISP1704 tristate "ISP1704 USB Charger Detection" depends on USB_PHY + depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' help Say Y to enable support for USB Charger Detection with ISP1707/ISP1704 USB transceivers. @@ -393,6 +394,7 @@ config CHARGER_QCOM_SMBB tristate "Qualcomm Switch-Mode Battery Charger and Boost" depends on MFD_SPMI_PMIC || COMPILE_TEST depends on OF + depends on EXTCON help Say Y to include support for the Switch-Mode Battery Charger and Boost (SMBB) hardware found in Qualcomm PM8941 PMICs. The charger diff --git a/drivers/power/axp20x_usb_power.c b/drivers/power/axp20x_usb_power.c index 421a90b83567..6af6feb7058d 100644 --- a/drivers/power/axp20x_usb_power.c +++ b/drivers/power/axp20x_usb_power.c @@ -42,6 +42,7 @@ #define AXP20X_VBUS_MON_VBUS_VALID BIT(3) struct axp20x_usb_power { + struct device_node *np; struct regmap *regmap; struct power_supply *supply; }; @@ -85,7 +86,12 @@ static int axp20x_usb_power_get_property(struct power_supply *psy, switch (v & AXP20X_VBUS_CLIMIT_MASK) { case AXP20X_VBUC_CLIMIT_100mA: - val->intval = 100000; + if (of_device_is_compatible(power->np, + "x-powers,axp202-usb-power-supply")) { + val->intval = 100000; + } else { + val->intval = -1; /* No 100mA limit */ + } break; case AXP20X_VBUC_CLIMIT_500mA: val->intval = 500000; @@ -122,16 +128,19 @@ static int axp20x_usb_power_get_property(struct power_supply *psy, break; } - ret = regmap_read(power->regmap, AXP20X_USB_OTG_STATUS, &v); - if (ret) - return ret; + val->intval = POWER_SUPPLY_HEALTH_GOOD; - if (!(v & AXP20X_USB_STATUS_VBUS_VALID)) { - val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; - break; - } + if (of_device_is_compatible(power->np, + "x-powers,axp202-usb-power-supply")) { + ret = regmap_read(power->regmap, + AXP20X_USB_OTG_STATUS, &v); + if (ret) + return ret; - val->intval = POWER_SUPPLY_HEALTH_GOOD; + if (!(v & AXP20X_USB_STATUS_VBUS_VALID)) + val->intval = + POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + } break; case POWER_SUPPLY_PROP_PRESENT: val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_PRESENT); @@ -156,6 +165,14 @@ static enum power_supply_property axp20x_usb_power_properties[] = { POWER_SUPPLY_PROP_CURRENT_NOW, }; +static enum power_supply_property axp22x_usb_power_properties[] = { + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_MIN, + POWER_SUPPLY_PROP_CURRENT_MAX, +}; + static const struct power_supply_desc axp20x_usb_power_desc = { .name = "axp20x-usb", .type = POWER_SUPPLY_TYPE_USB, @@ -164,13 +181,25 @@ static const struct power_supply_desc axp20x_usb_power_desc = { .get_property = axp20x_usb_power_get_property, }; +static const struct power_supply_desc axp22x_usb_power_desc = { + .name = "axp20x-usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = axp22x_usb_power_properties, + .num_properties = ARRAY_SIZE(axp22x_usb_power_properties), + .get_property = axp20x_usb_power_get_property, +}; + static int axp20x_usb_power_probe(struct platform_device *pdev) { struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); struct power_supply_config psy_cfg = {}; struct axp20x_usb_power *power; - static const char * const irq_names[] = { "VBUS_PLUGIN", - "VBUS_REMOVAL", "VBUS_VALID", "VBUS_NOT_VALID" }; + static const char * const axp20x_irq_names[] = { "VBUS_PLUGIN", + "VBUS_REMOVAL", "VBUS_VALID", "VBUS_NOT_VALID", NULL }; + static const char * const axp22x_irq_names[] = { + "VBUS_PLUGIN", "VBUS_REMOVAL", NULL }; + static const char * const *irq_names; + const struct power_supply_desc *usb_power_desc; int i, irq, ret; if (!of_device_is_available(pdev->dev.of_node)) @@ -185,31 +214,47 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) if (!power) return -ENOMEM; + power->np = pdev->dev.of_node; power->regmap = axp20x->regmap; - /* Enable vbus valid checking */ - ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON, - AXP20X_VBUS_MON_VBUS_VALID, AXP20X_VBUS_MON_VBUS_VALID); - if (ret) - return ret; + if (of_device_is_compatible(power->np, + "x-powers,axp202-usb-power-supply")) { + /* Enable vbus valid checking */ + ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON, + AXP20X_VBUS_MON_VBUS_VALID, + AXP20X_VBUS_MON_VBUS_VALID); + if (ret) + return ret; - /* Enable vbus voltage and current measurement */ - ret = regmap_update_bits(power->regmap, AXP20X_ADC_EN1, + /* Enable vbus voltage and current measurement */ + ret = regmap_update_bits(power->regmap, AXP20X_ADC_EN1, AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT, AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT); - if (ret) - return ret; + if (ret) + return ret; + + usb_power_desc = &axp20x_usb_power_desc; + irq_names = axp20x_irq_names; + } else if (of_device_is_compatible(power->np, + "x-powers,axp221-usb-power-supply")) { + usb_power_desc = &axp22x_usb_power_desc; + irq_names = axp22x_irq_names; + } else { + dev_err(&pdev->dev, "Unsupported AXP variant: %ld\n", + axp20x->variant); + return -EINVAL; + } psy_cfg.of_node = pdev->dev.of_node; psy_cfg.drv_data = power; - power->supply = devm_power_supply_register(&pdev->dev, - &axp20x_usb_power_desc, &psy_cfg); + power->supply = devm_power_supply_register(&pdev->dev, usb_power_desc, + &psy_cfg); if (IS_ERR(power->supply)) return PTR_ERR(power->supply); /* Request irqs after registering, as irqs may trigger immediately */ - for (i = 0; i < ARRAY_SIZE(irq_names); i++) { + for (i = 0; irq_names[i]; i++) { irq = platform_get_irq_byname(pdev, irq_names[i]); if (irq < 0) { dev_warn(&pdev->dev, "No IRQ for %s: %d\n", @@ -229,6 +274,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) static const struct of_device_id axp20x_usb_power_match[] = { { .compatible = "x-powers,axp202-usb-power-supply" }, + { .compatible = "x-powers,axp221-usb-power-supply" }, { } }; MODULE_DEVICE_TABLE(of, axp20x_usb_power_match); diff --git a/drivers/power/axp288_charger.c b/drivers/power/axp288_charger.c index e4d569f57acc..4030eeb7cf65 100644 --- a/drivers/power/axp288_charger.c +++ b/drivers/power/axp288_charger.c @@ -129,10 +129,6 @@ #define AXP288_EXTCON_DEV_NAME "axp288_extcon" -#define AXP288_EXTCON_SLOW_CHARGER "SLOW-CHARGER" -#define AXP288_EXTCON_DOWNSTREAM_CHARGER "CHARGE-DOWNSTREAM" -#define AXP288_EXTCON_FAST_CHARGER "FAST-CHARGER" - enum { VBUS_OV_IRQ = 0, CHARGE_DONE_IRQ, @@ -158,7 +154,7 @@ struct axp288_chrg_info { /* OTG/Host mode */ struct { struct work_struct work; - struct extcon_specific_cable_nb cable; + struct extcon_dev *cable; struct notifier_block id_nb; bool id_short; } otg; @@ -586,17 +582,15 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work) bool old_connected = info->cable.connected; /* Determine cable/charger type */ - if (extcon_get_cable_state(edev, AXP288_EXTCON_SLOW_CHARGER) > 0) { + if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_SDP) > 0) { dev_dbg(&info->pdev->dev, "USB SDP charger is connected"); info->cable.connected = true; info->cable.chg_type = POWER_SUPPLY_TYPE_USB; - } else if (extcon_get_cable_state(edev, - AXP288_EXTCON_DOWNSTREAM_CHARGER) > 0) { + } else if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_CDP) > 0) { dev_dbg(&info->pdev->dev, "USB CDP charger is connected"); info->cable.connected = true; info->cable.chg_type = POWER_SUPPLY_TYPE_USB_CDP; - } else if (extcon_get_cable_state(edev, - AXP288_EXTCON_FAST_CHARGER) > 0) { + } else if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_DCP) > 0) { dev_dbg(&info->pdev->dev, "USB DCP charger is connected"); info->cable.connected = true; info->cable.chg_type = POWER_SUPPLY_TYPE_USB_DCP; @@ -692,8 +686,8 @@ static int axp288_charger_handle_otg_evt(struct notifier_block *nb, { struct axp288_chrg_info *info = container_of(nb, struct axp288_chrg_info, otg.id_nb); - struct extcon_dev *edev = param; - int usb_host = extcon_get_cable_state(edev, "USB-Host"); + struct extcon_dev *edev = info->otg.cable; + int usb_host = extcon_get_cable_state_(edev, EXTCON_USB_HOST); dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n", usb_host ? "attached" : "detached"); @@ -848,10 +842,33 @@ static int axp288_charger_probe(struct platform_device *pdev) /* Register for extcon notification */ INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker); info->cable.nb.notifier_call = axp288_charger_handle_cable_evt; - ret = extcon_register_notifier(info->cable.edev, EXTCON_NONE, &info->cable.nb); + ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_SDP, + &info->cable.nb); + if (ret) { + dev_err(&info->pdev->dev, + "failed to register extcon notifier for SDP %d\n", ret); + return ret; + } + + ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_CDP, + &info->cable.nb); + if (ret) { + dev_err(&info->pdev->dev, + "failed to register extcon notifier for CDP %d\n", ret); + extcon_unregister_notifier(info->cable.edev, + EXTCON_CHG_USB_SDP, &info->cable.nb); + return ret; + } + + ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_DCP, + &info->cable.nb); if (ret) { dev_err(&info->pdev->dev, - "failed to register extcon notifier %d\n", ret); + "failed to register extcon notifier for DCP %d\n", ret); + extcon_unregister_notifier(info->cable.edev, + EXTCON_CHG_USB_SDP, &info->cable.nb); + extcon_unregister_notifier(info->cable.edev, + EXTCON_CHG_USB_CDP, &info->cable.nb); return ret; } @@ -871,14 +888,14 @@ static int axp288_charger_probe(struct platform_device *pdev) /* Register for OTG notification */ INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker); info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt; - ret = extcon_register_interest(&info->otg.cable, NULL, "USB-Host", + ret = extcon_register_notifier(info->otg.cable, EXTCON_USB_HOST, &info->otg.id_nb); if (ret) dev_warn(&pdev->dev, "failed to register otg notifier\n"); - if (info->otg.cable.edev) - info->otg.id_short = extcon_get_cable_state( - info->otg.cable.edev, "USB-Host"); + if (info->otg.cable) + info->otg.id_short = extcon_get_cable_state_( + info->otg.cable, EXTCON_USB_HOST); /* Register charger interrupts */ for (i = 0; i < CHRG_INTR_END; i++) { @@ -905,11 +922,17 @@ static int axp288_charger_probe(struct platform_device *pdev) return 0; intr_reg_failed: - if (info->otg.cable.edev) - extcon_unregister_interest(&info->otg.cable); + if (info->otg.cable) + extcon_unregister_notifier(info->otg.cable, EXTCON_USB_HOST, + &info->otg.id_nb); power_supply_unregister(info->psy_usb); psy_reg_failed: - extcon_unregister_notifier(info->cable.edev, EXTCON_NONE, &info->cable.nb); + extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_SDP, + &info->cable.nb); + extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_CDP, + &info->cable.nb); + extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_DCP, + &info->cable.nb); return ret; } @@ -917,10 +940,16 @@ static int axp288_charger_remove(struct platform_device *pdev) { struct axp288_chrg_info *info = dev_get_drvdata(&pdev->dev); - if (info->otg.cable.edev) - extcon_unregister_interest(&info->otg.cable); + if (info->otg.cable) + extcon_unregister_notifier(info->otg.cable, EXTCON_USB_HOST, + &info->otg.id_nb); - extcon_unregister_notifier(info->cable.edev, EXTCON_NONE, &info->cable.nb); + extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_SDP, + &info->cable.nb); + extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_CDP, + &info->cable.nb); + extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_DCP, + &info->cable.nb); power_supply_unregister(info->psy_usb); return 0; diff --git a/drivers/power/bq27xxx_battery.c b/drivers/power/bq27xxx_battery.c index 45f6ebf88df6..323d05a12f9b 100644 --- a/drivers/power/bq27xxx_battery.c +++ b/drivers/power/bq27xxx_battery.c @@ -82,6 +82,7 @@ * * These are indexes into a device's register mapping array. */ + enum bq27xxx_reg_index { BQ27XXX_REG_CTRL = 0, /* Control */ BQ27XXX_REG_TEMP, /* Temperature */ @@ -100,157 +101,144 @@ enum bq27xxx_reg_index { BQ27XXX_REG_SOC, /* State-of-Charge */ BQ27XXX_REG_DCAP, /* Design Capacity */ BQ27XXX_REG_AP, /* Average Power */ + BQ27XXX_REG_MAX, /* sentinel */ }; /* Register mappings */ -static u8 bq27000_regs[] = { - 0x00, /* CONTROL */ - 0x06, /* TEMP */ - INVALID_REG_ADDR, /* INT TEMP - NA*/ - 0x08, /* VOLT */ - 0x14, /* AVG CURR */ - 0x0a, /* FLAGS */ - 0x16, /* TTE */ - 0x18, /* TTF */ - 0x1c, /* TTES */ - 0x26, /* TTECP */ - 0x0c, /* NAC */ - 0x12, /* LMD(FCC) */ - 0x2a, /* CYCT */ - 0x22, /* AE */ - 0x0b, /* SOC(RSOC) */ - 0x76, /* DCAP(ILMD) */ - 0x24, /* AP */ -}; - -static u8 bq27010_regs[] = { - 0x00, /* CONTROL */ - 0x06, /* TEMP */ - INVALID_REG_ADDR, /* INT TEMP - NA*/ - 0x08, /* VOLT */ - 0x14, /* AVG CURR */ - 0x0a, /* FLAGS */ - 0x16, /* TTE */ - 0x18, /* TTF */ - 0x1c, /* TTES */ - 0x26, /* TTECP */ - 0x0c, /* NAC */ - 0x12, /* LMD(FCC) */ - 0x2a, /* CYCT */ - INVALID_REG_ADDR, /* AE - NA */ - 0x0b, /* SOC(RSOC) */ - 0x76, /* DCAP(ILMD) */ - INVALID_REG_ADDR, /* AP - NA */ -}; - -static u8 bq27500_regs[] = { - 0x00, /* CONTROL */ - 0x06, /* TEMP */ - 0x28, /* INT TEMP */ - 0x08, /* VOLT */ - 0x14, /* AVG CURR */ - 0x0a, /* FLAGS */ - 0x16, /* TTE */ - INVALID_REG_ADDR, /* TTF - NA */ - 0x1a, /* TTES */ - INVALID_REG_ADDR, /* TTECP - NA */ - 0x0c, /* NAC */ - 0x12, /* LMD(FCC) */ - 0x2a, /* CYCT */ - INVALID_REG_ADDR, /* AE - NA */ - 0x2c, /* SOC(RSOC) */ - 0x3c, /* DCAP(ILMD) */ - INVALID_REG_ADDR, /* AP - NA */ -}; - -static u8 bq27530_regs[] = { - 0x00, /* CONTROL */ - 0x06, /* TEMP */ - 0x32, /* INT TEMP */ - 0x08, /* VOLT */ - 0x14, /* AVG CURR */ - 0x0a, /* FLAGS */ - 0x16, /* TTE */ - INVALID_REG_ADDR, /* TTF - NA */ - INVALID_REG_ADDR, /* TTES - NA */ - INVALID_REG_ADDR, /* TTECP - NA */ - 0x0c, /* NAC */ - 0x12, /* LMD(FCC) */ - 0x2a, /* CYCT */ - INVALID_REG_ADDR, /* AE - NA */ - 0x2c, /* SOC(RSOC) */ - INVALID_REG_ADDR, /* DCAP - NA */ - 0x24, /* AP */ -}; - -static u8 bq27541_regs[] = { - 0x00, /* CONTROL */ - 0x06, /* TEMP */ - 0x28, /* INT TEMP */ - 0x08, /* VOLT */ - 0x14, /* AVG CURR */ - 0x0a, /* FLAGS */ - 0x16, /* TTE */ - INVALID_REG_ADDR, /* TTF - NA */ - INVALID_REG_ADDR, /* TTES - NA */ - INVALID_REG_ADDR, /* TTECP - NA */ - 0x0c, /* NAC */ - 0x12, /* LMD(FCC) */ - 0x2a, /* CYCT */ - INVALID_REG_ADDR, /* AE - NA */ - 0x2c, /* SOC(RSOC) */ - 0x3c, /* DCAP */ - 0x24, /* AP */ -}; - -static u8 bq27545_regs[] = { - 0x00, /* CONTROL */ - 0x06, /* TEMP */ - 0x28, /* INT TEMP */ - 0x08, /* VOLT */ - 0x14, /* AVG CURR */ - 0x0a, /* FLAGS */ - 0x16, /* TTE */ - INVALID_REG_ADDR, /* TTF - NA */ - INVALID_REG_ADDR, /* TTES - NA */ - INVALID_REG_ADDR, /* TTECP - NA */ - 0x0c, /* NAC */ - 0x12, /* LMD(FCC) */ - 0x2a, /* CYCT */ - INVALID_REG_ADDR, /* AE - NA */ - 0x2c, /* SOC(RSOC) */ - INVALID_REG_ADDR, /* DCAP - NA */ - 0x24, /* AP */ -}; - -static u8 bq27421_regs[] = { - 0x00, /* CONTROL */ - 0x02, /* TEMP */ - 0x1e, /* INT TEMP */ - 0x04, /* VOLT */ - 0x10, /* AVG CURR */ - 0x06, /* FLAGS */ - INVALID_REG_ADDR, /* TTE - NA */ - INVALID_REG_ADDR, /* TTF - NA */ - INVALID_REG_ADDR, /* TTES - NA */ - INVALID_REG_ADDR, /* TTECP - NA */ - 0x08, /* NAC */ - 0x0e, /* FCC */ - INVALID_REG_ADDR, /* CYCT - NA */ - INVALID_REG_ADDR, /* AE - NA */ - 0x1c, /* SOC */ - 0x3c, /* DCAP */ - 0x18, /* AP */ -}; - -static u8 *bq27xxx_regs[] = { - [BQ27000] = bq27000_regs, - [BQ27010] = bq27010_regs, - [BQ27500] = bq27500_regs, - [BQ27530] = bq27530_regs, - [BQ27541] = bq27541_regs, - [BQ27545] = bq27545_regs, - [BQ27421] = bq27421_regs, +static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { + [BQ27000] = { + [BQ27XXX_REG_CTRL] = 0x00, + [BQ27XXX_REG_TEMP] = 0x06, + [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, + [BQ27XXX_REG_VOLT] = 0x08, + [BQ27XXX_REG_AI] = 0x14, + [BQ27XXX_REG_FLAGS] = 0x0a, + [BQ27XXX_REG_TTE] = 0x16, + [BQ27XXX_REG_TTF] = 0x18, + [BQ27XXX_REG_TTES] = 0x1c, + [BQ27XXX_REG_TTECP] = 0x26, + [BQ27XXX_REG_NAC] = 0x0c, + [BQ27XXX_REG_FCC] = 0x12, + [BQ27XXX_REG_CYCT] = 0x2a, + [BQ27XXX_REG_AE] = 0x22, + [BQ27XXX_REG_SOC] = 0x0b, + [BQ27XXX_REG_DCAP] = 0x76, + [BQ27XXX_REG_AP] = 0x24, + }, + [BQ27010] = { + [BQ27XXX_REG_CTRL] = 0x00, + [BQ27XXX_REG_TEMP] = 0x06, + [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, + [BQ27XXX_REG_VOLT] = 0x08, + [BQ27XXX_REG_AI] = 0x14, + [BQ27XXX_REG_FLAGS] = 0x0a, + [BQ27XXX_REG_TTE] = 0x16, + [BQ27XXX_REG_TTF] = 0x18, + [BQ27XXX_REG_TTES] = 0x1c, + [BQ27XXX_REG_TTECP] = 0x26, + [BQ27XXX_REG_NAC] = 0x0c, + [BQ27XXX_REG_FCC] = 0x12, + [BQ27XXX_REG_CYCT] = 0x2a, + [BQ27XXX_REG_AE] = INVALID_REG_ADDR, + [BQ27XXX_REG_SOC] = 0x0b, + [BQ27XXX_REG_DCAP] = 0x76, + [BQ27XXX_REG_AP] = INVALID_REG_ADDR, + }, + [BQ27500] = { + [BQ27XXX_REG_CTRL] = 0x00, + [BQ27XXX_REG_TEMP] = 0x06, + [BQ27XXX_REG_INT_TEMP] = 0x28, + [BQ27XXX_REG_VOLT] = 0x08, + [BQ27XXX_REG_AI] = 0x14, + [BQ27XXX_REG_FLAGS] = 0x0a, + [BQ27XXX_REG_TTE] = 0x16, + [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTES] = 0x1a, + [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, + [BQ27XXX_REG_NAC] = 0x0c, + [BQ27XXX_REG_FCC] = 0x12, + [BQ27XXX_REG_CYCT] = 0x2a, + [BQ27XXX_REG_AE] = INVALID_REG_ADDR, + [BQ27XXX_REG_SOC] = 0x2c, + [BQ27XXX_REG_DCAP] = 0x3c, + [BQ27XXX_REG_AP] = INVALID_REG_ADDR, + }, + [BQ27530] = { + [BQ27XXX_REG_CTRL] = 0x00, + [BQ27XXX_REG_TEMP] = 0x06, + [BQ27XXX_REG_INT_TEMP] = 0x32, + [BQ27XXX_REG_VOLT] = 0x08, + [BQ27XXX_REG_AI] = 0x14, + [BQ27XXX_REG_FLAGS] = 0x0a, + [BQ27XXX_REG_TTE] = 0x16, + [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, + [BQ27XXX_REG_NAC] = 0x0c, + [BQ27XXX_REG_FCC] = 0x12, + [BQ27XXX_REG_CYCT] = 0x2a, + [BQ27XXX_REG_AE] = INVALID_REG_ADDR, + [BQ27XXX_REG_SOC] = 0x2c, + [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR, + [BQ27XXX_REG_AP] = 0x24, + }, + [BQ27541] = { + [BQ27XXX_REG_CTRL] = 0x00, + [BQ27XXX_REG_TEMP] = 0x06, + [BQ27XXX_REG_INT_TEMP] = 0x28, + [BQ27XXX_REG_VOLT] = 0x08, + [BQ27XXX_REG_AI] = 0x14, + [BQ27XXX_REG_FLAGS] = 0x0a, + [BQ27XXX_REG_TTE] = 0x16, + [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, + [BQ27XXX_REG_NAC] = 0x0c, + [BQ27XXX_REG_FCC] = 0x12, + [BQ27XXX_REG_CYCT] = 0x2a, + [BQ27XXX_REG_AE] = INVALID_REG_ADDR, + [BQ27XXX_REG_SOC] = 0x2c, + [BQ27XXX_REG_DCAP] = 0x3c, + [BQ27XXX_REG_AP] = 0x24, + }, + [BQ27545] = { + [BQ27XXX_REG_CTRL] = 0x00, + [BQ27XXX_REG_TEMP] = 0x06, + [BQ27XXX_REG_INT_TEMP] = 0x28, + [BQ27XXX_REG_VOLT] = 0x08, + [BQ27XXX_REG_AI] = 0x14, + [BQ27XXX_REG_FLAGS] = 0x0a, + [BQ27XXX_REG_TTE] = 0x16, + [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, + [BQ27XXX_REG_NAC] = 0x0c, + [BQ27XXX_REG_FCC] = 0x12, + [BQ27XXX_REG_CYCT] = 0x2a, + [BQ27XXX_REG_AE] = INVALID_REG_ADDR, + [BQ27XXX_REG_SOC] = 0x2c, + [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR, + [BQ27XXX_REG_AP] = 0x24, + }, + [BQ27421] = { + [BQ27XXX_REG_CTRL] = 0x00, + [BQ27XXX_REG_TEMP] = 0x02, + [BQ27XXX_REG_INT_TEMP] = 0x1e, + [BQ27XXX_REG_VOLT] = 0x04, + [BQ27XXX_REG_AI] = 0x10, + [BQ27XXX_REG_FLAGS] = 0x06, + [BQ27XXX_REG_TTE] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, + [BQ27XXX_REG_NAC] = 0x08, + [BQ27XXX_REG_FCC] = 0x0e, + [BQ27XXX_REG_CYCT] = INVALID_REG_ADDR, + [BQ27XXX_REG_AE] = INVALID_REG_ADDR, + [BQ27XXX_REG_SOC] = 0x1c, + [BQ27XXX_REG_DCAP] = 0x3c, + [BQ27XXX_REG_AP] = 0x18, + }, }; static enum power_supply_property bq27000_battery_props[] = { @@ -735,11 +723,8 @@ static void bq27xxx_battery_poll(struct work_struct *work) bq27xxx_battery_update(di); - if (poll_interval > 0) { - /* The timer does not have to be accurate. */ - set_timer_slack(&di->work.timer, poll_interval * HZ / 4); + if (poll_interval > 0) schedule_delayed_work(&di->work, poll_interval * HZ); - } } /* diff --git a/drivers/power/bq27xxx_battery_i2c.c b/drivers/power/bq27xxx_battery_i2c.c index b8f8d3ade31b..85d4ea2a9c20 100644 --- a/drivers/power/bq27xxx_battery_i2c.c +++ b/drivers/power/bq27xxx_battery_i2c.c @@ -1,5 +1,5 @@ /* - * SCI Reset driver for Keystone based devices + * BQ27xxx battery monitor I2C driver * * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ * Andrew F. Davis <afd@ti.com> diff --git a/drivers/power/max8903_charger.c b/drivers/power/max8903_charger.c index 17876caf31e5..fdc73d686153 100644 --- a/drivers/power/max8903_charger.c +++ b/drivers/power/max8903_charger.c @@ -23,13 +23,16 @@ #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> #include <linux/slab.h> #include <linux/power_supply.h> #include <linux/platform_device.h> #include <linux/power/max8903_charger.h> struct max8903_data { - struct max8903_pdata pdata; + struct max8903_pdata *pdata; struct device *dev; struct power_supply *psy; struct power_supply_desc psy_desc; @@ -53,8 +56,8 @@ static int max8903_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_STATUS: val->intval = POWER_SUPPLY_STATUS_UNKNOWN; - if (data->pdata.chg) { - if (gpio_get_value(data->pdata.chg) == 0) + if (gpio_is_valid(data->pdata->chg)) { + if (gpio_get_value(data->pdata->chg) == 0) val->intval = POWER_SUPPLY_STATUS_CHARGING; else if (data->usb_in || data->ta_in) val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; @@ -75,13 +78,14 @@ static int max8903_get_property(struct power_supply *psy, default: return -EINVAL; } + return 0; } static irqreturn_t max8903_dcin(int irq, void *_data) { struct max8903_data *data = _data; - struct max8903_pdata *pdata = &data->pdata; + struct max8903_pdata *pdata = data->pdata; bool ta_in; enum power_supply_type old_type; @@ -93,11 +97,11 @@ static irqreturn_t max8903_dcin(int irq, void *_data) data->ta_in = ta_in; /* Set Current-Limit-Mode 1:DC 0:USB */ - if (pdata->dcm) + if (gpio_is_valid(pdata->dcm)) gpio_set_value(pdata->dcm, ta_in ? 1 : 0); /* Charger Enable / Disable (cen is negated) */ - if (pdata->cen) + if (gpio_is_valid(pdata->cen)) gpio_set_value(pdata->cen, ta_in ? 0 : (data->usb_in ? 0 : 1)); @@ -122,7 +126,7 @@ static irqreturn_t max8903_dcin(int irq, void *_data) static irqreturn_t max8903_usbin(int irq, void *_data) { struct max8903_data *data = _data; - struct max8903_pdata *pdata = &data->pdata; + struct max8903_pdata *pdata = data->pdata; bool usb_in; enum power_supply_type old_type; @@ -136,7 +140,7 @@ static irqreturn_t max8903_usbin(int irq, void *_data) /* Do not touch Current-Limit-Mode */ /* Charger Enable / Disable (cen is negated) */ - if (pdata->cen) + if (gpio_is_valid(pdata->cen)) gpio_set_value(pdata->cen, usb_in ? 0 : (data->ta_in ? 0 : 1)); @@ -161,7 +165,7 @@ static irqreturn_t max8903_usbin(int irq, void *_data) static irqreturn_t max8903_fault(int irq, void *_data) { struct max8903_data *data = _data; - struct max8903_pdata *pdata = &data->pdata; + struct max8903_pdata *pdata = data->pdata; bool fault; fault = gpio_get_value(pdata->flt) ? false : true; @@ -179,57 +183,109 @@ static irqreturn_t max8903_fault(int irq, void *_data) return IRQ_HANDLED; } -static int max8903_probe(struct platform_device *pdev) +static struct max8903_pdata *max8903_parse_dt_data(struct device *dev) { - struct max8903_data *data; + struct device_node *np = dev->of_node; + struct max8903_pdata *pdata = NULL; + + if (!np) + return NULL; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + pdata->dc_valid = false; + pdata->usb_valid = false; + + pdata->cen = of_get_named_gpio(np, "cen-gpios", 0); + if (!gpio_is_valid(pdata->cen)) + pdata->cen = -EINVAL; + + pdata->chg = of_get_named_gpio(np, "chg-gpios", 0); + if (!gpio_is_valid(pdata->chg)) + pdata->chg = -EINVAL; + + pdata->flt = of_get_named_gpio(np, "flt-gpios", 0); + if (!gpio_is_valid(pdata->flt)) + pdata->flt = -EINVAL; + + pdata->usus = of_get_named_gpio(np, "usus-gpios", 0); + if (!gpio_is_valid(pdata->usus)) + pdata->usus = -EINVAL; + + pdata->dcm = of_get_named_gpio(np, "dcm-gpios", 0); + if (!gpio_is_valid(pdata->dcm)) + pdata->dcm = -EINVAL; + + pdata->dok = of_get_named_gpio(np, "dok-gpios", 0); + if (!gpio_is_valid(pdata->dok)) + pdata->dok = -EINVAL; + else + pdata->dc_valid = true; + + pdata->uok = of_get_named_gpio(np, "uok-gpios", 0); + if (!gpio_is_valid(pdata->uok)) + pdata->uok = -EINVAL; + else + pdata->usb_valid = true; + + return pdata; +} + +static int max8903_setup_gpios(struct platform_device *pdev) +{ + struct max8903_data *data = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; struct max8903_pdata *pdata = pdev->dev.platform_data; - struct power_supply_config psy_cfg = {}; int ret = 0; int gpio; int ta_in = 0; int usb_in = 0; - data = devm_kzalloc(dev, sizeof(struct max8903_data), GFP_KERNEL); - if (data == NULL) { - dev_err(dev, "Cannot allocate memory.\n"); - return -ENOMEM; - } - memcpy(&data->pdata, pdata, sizeof(struct max8903_pdata)); - data->dev = dev; - platform_set_drvdata(pdev, data); - - if (pdata->dc_valid == false && pdata->usb_valid == false) { - dev_err(dev, "No valid power sources.\n"); - return -EINVAL; - } - if (pdata->dc_valid) { - if (pdata->dok && gpio_is_valid(pdata->dok) && - pdata->dcm && gpio_is_valid(pdata->dcm)) { + if (gpio_is_valid(pdata->dok)) { + ret = devm_gpio_request(dev, pdata->dok, + data->psy_desc.name); + if (ret) { + dev_err(dev, + "Failed GPIO request for dok: %d err %d\n", + pdata->dok, ret); + return ret; + } + gpio = pdata->dok; /* PULL_UPed Interrupt */ ta_in = gpio_get_value(gpio) ? 0 : 1; - - gpio = pdata->dcm; /* Output */ - gpio_set_value(gpio, ta_in); } else { - dev_err(dev, "When DC is wired, DOK and DCM should" - " be wired as well.\n"); + dev_err(dev, "When DC is wired, DOK should be wired as well.\n"); return -EINVAL; } - } else { - if (pdata->dcm) { - if (gpio_is_valid(pdata->dcm)) - gpio_set_value(pdata->dcm, 0); - else { - dev_err(dev, "Invalid pin: dcm.\n"); - return -EINVAL; - } + } + + if (gpio_is_valid(pdata->dcm)) { + ret = devm_gpio_request(dev, pdata->dcm, data->psy_desc.name); + if (ret) { + dev_err(dev, + "Failed GPIO request for dcm: %d err %d\n", + pdata->dcm, ret); + return ret; } + + gpio = pdata->dcm; /* Output */ + gpio_set_value(gpio, ta_in); } if (pdata->usb_valid) { - if (pdata->uok && gpio_is_valid(pdata->uok)) { + if (gpio_is_valid(pdata->uok)) { + ret = devm_gpio_request(dev, pdata->uok, + data->psy_desc.name); + if (ret) { + dev_err(dev, + "Failed GPIO request for uok: %d err %d\n", + pdata->uok, ret); + return ret; + } + gpio = pdata->uok; usb_in = gpio_get_value(gpio) ? 0 : 1; } else { @@ -239,33 +295,45 @@ static int max8903_probe(struct platform_device *pdev) } } - if (pdata->cen) { - if (gpio_is_valid(pdata->cen)) { - gpio_set_value(pdata->cen, (ta_in || usb_in) ? 0 : 1); - } else { - dev_err(dev, "Invalid pin: cen.\n"); - return -EINVAL; + if (gpio_is_valid(pdata->cen)) { + ret = devm_gpio_request(dev, pdata->cen, data->psy_desc.name); + if (ret) { + dev_err(dev, + "Failed GPIO request for cen: %d err %d\n", + pdata->cen, ret); + return ret; } + + gpio_set_value(pdata->cen, (ta_in || usb_in) ? 0 : 1); } - if (pdata->chg) { - if (!gpio_is_valid(pdata->chg)) { - dev_err(dev, "Invalid pin: chg.\n"); - return -EINVAL; + if (gpio_is_valid(pdata->chg)) { + ret = devm_gpio_request(dev, pdata->chg, data->psy_desc.name); + if (ret) { + dev_err(dev, + "Failed GPIO request for chg: %d err %d\n", + pdata->chg, ret); + return ret; } } - if (pdata->flt) { - if (!gpio_is_valid(pdata->flt)) { - dev_err(dev, "Invalid pin: flt.\n"); - return -EINVAL; + if (gpio_is_valid(pdata->flt)) { + ret = devm_gpio_request(dev, pdata->flt, data->psy_desc.name); + if (ret) { + dev_err(dev, + "Failed GPIO request for flt: %d err %d\n", + pdata->flt, ret); + return ret; } } - if (pdata->usus) { - if (!gpio_is_valid(pdata->usus)) { - dev_err(dev, "Invalid pin: usus.\n"); - return -EINVAL; + if (gpio_is_valid(pdata->usus)) { + ret = devm_gpio_request(dev, pdata->usus, data->psy_desc.name); + if (ret) { + dev_err(dev, + "Failed GPIO request for usus: %d err %d\n", + pdata->usus, ret); + return ret; } } @@ -273,14 +341,52 @@ static int max8903_probe(struct platform_device *pdev) data->ta_in = ta_in; data->usb_in = usb_in; + return 0; +} + +static int max8903_probe(struct platform_device *pdev) +{ + struct max8903_data *data; + struct device *dev = &pdev->dev; + struct max8903_pdata *pdata = pdev->dev.platform_data; + struct power_supply_config psy_cfg = {}; + int ret = 0; + + data = devm_kzalloc(dev, sizeof(struct max8903_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (IS_ENABLED(CONFIG_OF) && !pdata && dev->of_node) + pdata = max8903_parse_dt_data(dev); + + if (!pdata) { + dev_err(dev, "No platform data.\n"); + return -EINVAL; + } + + pdev->dev.platform_data = pdata; + data->pdata = pdata; + data->dev = dev; + platform_set_drvdata(pdev, data); + + if (pdata->dc_valid == false && pdata->usb_valid == false) { + dev_err(dev, "No valid power sources.\n"); + return -EINVAL; + } + + ret = max8903_setup_gpios(pdev); + if (ret) + return ret; + data->psy_desc.name = "max8903_charger"; - data->psy_desc.type = (ta_in) ? POWER_SUPPLY_TYPE_MAINS : - ((usb_in) ? POWER_SUPPLY_TYPE_USB : + data->psy_desc.type = (data->ta_in) ? POWER_SUPPLY_TYPE_MAINS : + ((data->usb_in) ? POWER_SUPPLY_TYPE_USB : POWER_SUPPLY_TYPE_BATTERY); data->psy_desc.get_property = max8903_get_property; data->psy_desc.properties = max8903_charger_props; data->psy_desc.num_properties = ARRAY_SIZE(max8903_charger_props); + psy_cfg.of_node = dev->of_node; psy_cfg.drv_data = data; data->psy = devm_power_supply_register(dev, &data->psy_desc, &psy_cfg); @@ -315,7 +421,7 @@ static int max8903_probe(struct platform_device *pdev) } } - if (pdata->flt) { + if (gpio_is_valid(pdata->flt)) { ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->flt), NULL, max8903_fault, IRQF_TRIGGER_FALLING | @@ -331,10 +437,17 @@ static int max8903_probe(struct platform_device *pdev) return 0; } +static const struct of_device_id max8903_match_ids[] = { + { .compatible = "maxim,max8903", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, max8903_match_ids); + static struct platform_driver max8903_driver = { .probe = max8903_probe, .driver = { .name = "max8903-charger", + .of_match_table = max8903_match_ids }, }; diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 456987c88baa..a74d8ca383a1 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -491,8 +491,11 @@ int power_supply_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - if (atomic_read(&psy->use_cnt) <= 0) + if (atomic_read(&psy->use_cnt) <= 0) { + if (!psy->initialized) + return -EAGAIN; return -ENODEV; + } return psy->desc->get_property(psy, psp, val); } @@ -565,11 +568,12 @@ static int power_supply_read_temp(struct thermal_zone_device *tzd, WARN_ON(tzd == NULL); psy = tzd->devdata; - ret = psy->desc->get_property(psy, POWER_SUPPLY_PROP_TEMP, &val); + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_TEMP, &val); + if (ret) + return ret; /* Convert tenths of degree Celsius to milli degree Celsius. */ - if (!ret) - *temp = val.intval * 100; + *temp = val.intval * 100; return ret; } @@ -612,10 +616,12 @@ static int ps_get_max_charge_cntl_limit(struct thermal_cooling_device *tcd, int ret; psy = tcd->devdata; - ret = psy->desc->get_property(psy, - POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, &val); - if (!ret) - *state = val.intval; + ret = power_supply_get_property(psy, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, &val); + if (ret) + return ret; + + *state = val.intval; return ret; } @@ -628,10 +634,12 @@ static int ps_get_cur_chrage_cntl_limit(struct thermal_cooling_device *tcd, int ret; psy = tcd->devdata; - ret = psy->desc->get_property(psy, - POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val); - if (!ret) - *state = val.intval; + ret = power_supply_get_property(psy, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val); + if (ret) + return ret; + + *state = val.intval; return ret; } @@ -780,6 +788,7 @@ __power_supply_register(struct device *parent, * after calling power_supply_register()). */ atomic_inc(&psy->use_cnt); + psy->initialized = true; queue_delayed_work(system_power_efficient_wq, &psy->deferred_register_work, diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 80fed98832f9..bcde8d13476a 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -83,7 +83,7 @@ static ssize_t power_supply_show_property(struct device *dev, if (ret == -ENODATA) dev_dbg(dev, "driver has no data for `%s' property\n", attr->attr.name); - else if (ret != -ENODEV) + else if (ret != -ENODEV && ret != -EAGAIN) dev_err(dev, "driver failed to report `%s' property: %zd\n", attr->attr.name, ret); return ret; diff --git a/drivers/power/qcom_smbb.c b/drivers/power/qcom_smbb.c index 5eb1e9e543e2..b5896ba2a602 100644 --- a/drivers/power/qcom_smbb.c +++ b/drivers/power/qcom_smbb.c @@ -34,6 +34,7 @@ #include <linux/power_supply.h> #include <linux/regmap.h> #include <linux/slab.h> +#include <linux/extcon.h> #define SMBB_CHG_VMAX 0x040 #define SMBB_CHG_VSAFE 0x041 @@ -111,6 +112,7 @@ struct smbb_charger { unsigned int revision; unsigned int addr; struct device *dev; + struct extcon_dev *edev; bool dc_disabled; bool jeita_ext_temp; @@ -125,6 +127,11 @@ struct smbb_charger { struct regmap *regmap; }; +static const unsigned int smbb_usb_extcon_cable[] = { + EXTCON_USB, + EXTCON_NONE, +}; + static int smbb_vbat_weak_fn(unsigned int index) { return 2100000 + index * 100000; @@ -371,6 +378,8 @@ static irqreturn_t smbb_usb_valid_handler(int irq, void *_data) struct smbb_charger *chg = _data; smbb_set_line_flag(chg, irq, STATUS_USBIN_VALID); + extcon_set_cable_state_(chg->edev, EXTCON_USB, + chg->status & STATUS_USBIN_VALID); power_supply_changed(chg->usb_psy); return IRQ_HANDLED; @@ -849,6 +858,18 @@ static int smbb_charger_probe(struct platform_device *pdev) return PTR_ERR(chg->usb_psy); } + chg->edev = devm_extcon_dev_allocate(&pdev->dev, smbb_usb_extcon_cable); + if (IS_ERR(chg->edev)) { + dev_err(&pdev->dev, "failed to allocate extcon device\n"); + return -ENOMEM; + } + + rc = devm_extcon_dev_register(&pdev->dev, chg->edev); + if (rc < 0) { + dev_err(&pdev->dev, "failed to register extcon device\n"); + return rc; + } + if (!chg->dc_disabled) { dc_cfg.drv_data = chg; dc_cfg.supplied_to = smbb_bif; diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index f38ac90f1aa5..3bfac539334b 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -158,7 +158,8 @@ config POWER_RESET_XGENE config POWER_RESET_KEYSTONE bool "Keystone reset driver" - depends on ARCH_KEYSTONE + depends on ARCH_KEYSTONE || COMPILE_TEST + depends on HAS_IOMEM select MFD_SYSCON help Reboot support for the KEYSTONE SoCs. @@ -193,5 +194,19 @@ config POWER_RESET_ZX help Reboot support for ZTE SoCs. +config REBOOT_MODE + tristate + +config SYSCON_REBOOT_MODE + tristate "Generic SYSCON regmap reboot mode driver" + depends on OF + select REBOOT_MODE + select MFD_SYSCON + help + Say y here will enable reboot mode driver. This will + get reboot mode arguments and store it in SYSCON mapped + register, then the bootloader can read it to take different + action according to the mode. + endif diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 6b6eeb3b4d7f..1be307c7fc25 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -22,3 +22,5 @@ obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o 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 diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c new file mode 100644 index 000000000000..2dfbbce0f817 --- /dev/null +++ b/drivers/power/reset/reboot-mode.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/reboot.h> +#include "reboot-mode.h" + +#define PREFIX "mode-" + +struct mode_info { + const char *mode; + u32 magic; + struct list_head list; +}; + +static unsigned int get_reboot_mode_magic(struct reboot_mode_driver *reboot, + const char *cmd) +{ + const char *normal = "normal"; + int magic = 0; + struct mode_info *info; + + if (!cmd) + cmd = normal; + + list_for_each_entry(info, &reboot->head, list) { + if (!strcmp(info->mode, cmd)) { + magic = info->magic; + break; + } + } + + return magic; +} + +static int reboot_mode_notify(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + struct reboot_mode_driver *reboot; + unsigned int magic; + + reboot = container_of(this, struct reboot_mode_driver, reboot_notifier); + magic = get_reboot_mode_magic(reboot, cmd); + if (magic) + reboot->write(reboot, magic); + + return NOTIFY_DONE; +} + +/** + * reboot_mode_register - register a reboot mode driver + * @reboot: reboot mode driver + * + * Returns: 0 on success or a negative error code on failure. + */ +int reboot_mode_register(struct reboot_mode_driver *reboot) +{ + struct mode_info *info; + struct property *prop; + struct device_node *np = reboot->dev->of_node; + size_t len = strlen(PREFIX); + int ret; + + INIT_LIST_HEAD(&reboot->head); + + for_each_property_of_node(np, prop) { + if (strncmp(prop->name, PREFIX, len)) + continue; + + info = devm_kzalloc(reboot->dev, sizeof(*info), GFP_KERNEL); + if (!info) { + ret = -ENOMEM; + goto error; + } + + if (of_property_read_u32(np, prop->name, &info->magic)) { + dev_err(reboot->dev, "reboot mode %s without magic number\n", + info->mode); + devm_kfree(reboot->dev, info); + continue; + } + + info->mode = kstrdup_const(prop->name + len, GFP_KERNEL); + if (!info->mode) { + ret = -ENOMEM; + goto error; + } else if (info->mode[0] == '\0') { + kfree_const(info->mode); + ret = -EINVAL; + dev_err(reboot->dev, "invalid mode name(%s): too short!\n", + prop->name); + goto error; + } + + list_add_tail(&info->list, &reboot->head); + } + + reboot->reboot_notifier.notifier_call = reboot_mode_notify; + register_reboot_notifier(&reboot->reboot_notifier); + + return 0; + +error: + list_for_each_entry(info, &reboot->head, list) + kfree_const(info->mode); + + return ret; +} +EXPORT_SYMBOL_GPL(reboot_mode_register); + +/** + * reboot_mode_unregister - unregister a reboot mode driver + * @reboot: reboot mode driver + */ +int reboot_mode_unregister(struct reboot_mode_driver *reboot) +{ + struct mode_info *info; + + unregister_reboot_notifier(&reboot->reboot_notifier); + + list_for_each_entry(info, &reboot->head, list) + kfree_const(info->mode); + + return 0; +} +EXPORT_SYMBOL_GPL(reboot_mode_unregister); + +MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com"); +MODULE_DESCRIPTION("System reboot mode core library"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/reset/reboot-mode.h b/drivers/power/reset/reboot-mode.h new file mode 100644 index 000000000000..2491bb71f591 --- /dev/null +++ b/drivers/power/reset/reboot-mode.h @@ -0,0 +1,14 @@ +#ifndef __REBOOT_MODE_H__ +#define __REBOOT_MODE_H__ + +struct reboot_mode_driver { + struct device *dev; + struct list_head head; + int (*write)(struct reboot_mode_driver *reboot, unsigned int magic); + struct notifier_block reboot_notifier; +}; + +int reboot_mode_register(struct reboot_mode_driver *reboot); +int reboot_mode_unregister(struct reboot_mode_driver *reboot); + +#endif diff --git a/drivers/power/reset/syscon-poweroff.c b/drivers/power/reset/syscon-poweroff.c index 5560b0dbc180..b68338399e5e 100644 --- a/drivers/power/reset/syscon-poweroff.c +++ b/drivers/power/reset/syscon-poweroff.c @@ -30,7 +30,7 @@ static struct regmap *map; static u32 offset; static u32 mask; -void syscon_poweroff(void) +static void syscon_poweroff(void) { /* Issue the poweroff */ regmap_write(map, offset, mask); diff --git a/drivers/power/reset/syscon-reboot-mode.c b/drivers/power/reset/syscon-reboot-mode.c new file mode 100644 index 000000000000..9e1cba5dd58e --- /dev/null +++ b/drivers/power/reset/syscon-reboot-mode.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> +#include "reboot-mode.h" + +struct syscon_reboot_mode { + struct regmap *map; + struct reboot_mode_driver reboot; + u32 offset; + u32 mask; +}; + +static int syscon_reboot_mode_write(struct reboot_mode_driver *reboot, + unsigned int magic) +{ + struct syscon_reboot_mode *syscon_rbm; + int ret; + + syscon_rbm = container_of(reboot, struct syscon_reboot_mode, reboot); + + ret = regmap_update_bits(syscon_rbm->map, syscon_rbm->offset, + syscon_rbm->mask, magic); + if (ret < 0) + dev_err(reboot->dev, "update reboot mode bits failed\n"); + + return ret; +} + +static int syscon_reboot_mode_probe(struct platform_device *pdev) +{ + int ret; + struct syscon_reboot_mode *syscon_rbm; + + syscon_rbm = devm_kzalloc(&pdev->dev, sizeof(*syscon_rbm), GFP_KERNEL); + if (!syscon_rbm) + return -ENOMEM; + + syscon_rbm->reboot.dev = &pdev->dev; + syscon_rbm->reboot.write = syscon_reboot_mode_write; + syscon_rbm->mask = 0xffffffff; + + dev_set_drvdata(&pdev->dev, syscon_rbm); + + syscon_rbm->map = syscon_node_to_regmap(pdev->dev.parent->of_node); + if (IS_ERR(syscon_rbm->map)) + return PTR_ERR(syscon_rbm->map); + + if (of_property_read_u32(pdev->dev.of_node, "offset", + &syscon_rbm->offset)) + return -EINVAL; + + of_property_read_u32(pdev->dev.of_node, "mask", &syscon_rbm->mask); + + ret = reboot_mode_register(&syscon_rbm->reboot); + if (ret) + dev_err(&pdev->dev, "can't register reboot mode\n"); + + return ret; +} + +static int syscon_reboot_mode_remove(struct platform_device *pdev) +{ + struct syscon_reboot_mode *syscon_rbm = dev_get_drvdata(&pdev->dev); + + return reboot_mode_unregister(&syscon_rbm->reboot); +} + +static const struct of_device_id syscon_reboot_mode_of_match[] = { + { .compatible = "syscon-reboot-mode" }, + {} +}; + +static struct platform_driver syscon_reboot_mode_driver = { + .probe = syscon_reboot_mode_probe, + .remove = syscon_reboot_mode_remove, + .driver = { + .name = "syscon-reboot-mode", + .of_match_table = syscon_reboot_mode_of_match, + }, +}; +module_platform_driver(syscon_reboot_mode_driver); + +MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com"); +MODULE_DESCRIPTION("SYSCON reboot mode driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/reset/vexpress-poweroff.c b/drivers/power/reset/vexpress-poweroff.c index 6a9bf7089373..102f95a09460 100644 --- a/drivers/power/reset/vexpress-poweroff.c +++ b/drivers/power/reset/vexpress-poweroff.c @@ -74,8 +74,8 @@ static ssize_t vexpress_reset_active_store(struct device *dev, return err ? err : count; } -DEVICE_ATTR(active, S_IRUGO | S_IWUSR, vexpress_reset_active_show, - vexpress_reset_active_store); +static DEVICE_ATTR(active, S_IRUGO | S_IWUSR, vexpress_reset_active_show, + vexpress_reset_active_store); enum vexpress_reset_func { FUNC_RESET, FUNC_SHUTDOWN, FUNC_REBOOT }; diff --git a/drivers/power/tps65217_charger.c b/drivers/power/tps65217_charger.c index d9f56730c735..73dfae41def8 100644 --- a/drivers/power/tps65217_charger.c +++ b/drivers/power/tps65217_charger.c @@ -197,6 +197,7 @@ static int tps65217_charger_probe(struct platform_device *pdev) { struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent); struct tps65217_charger *charger; + struct power_supply_config cfg = {}; int ret; dev_dbg(&pdev->dev, "%s\n", __func__); @@ -208,9 +209,12 @@ static int tps65217_charger_probe(struct platform_device *pdev) charger->tps = tps; charger->dev = &pdev->dev; + cfg.of_node = pdev->dev.of_node; + cfg.drv_data = charger; + charger->ac = devm_power_supply_register(&pdev->dev, &tps65217_charger_desc, - NULL); + &cfg); if (IS_ERR(charger->ac)) { dev_err(&pdev->dev, "failed: power supply register\n"); return PTR_ERR(charger->ac); |