Commit 4cadc60d authored by Linus Torvalds's avatar Linus Torvalds

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

Pull power supply and reset updates from Sebastian Reichel:
 "Core:
   - Add battery internal resistance temperature table support

  Drivers:
   - sc27xx: Optimize the battery resistance with measuring temperature
   - max17042-battery: Add MAX17055 support
   - bq25890-charger: Add support of BQ25892 and BQ25896 chips
   - misc fixes"

* tag 'for-v5.6' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (44 commits)
  power: supply: ipaq_micro_battery: remove unneeded semicolon
  power: supply: bq25890_charger: fix incorrect error return when bq25890_field_read fails
  power: supply: axp20x_usb_power: Only poll while offline
  power: supply: axp20x_usb_power: Add wakeup control
  power: supply: axp20x_usb_power: Allow offlining
  power: supply: axp20x_usb_power: Use a match structure
  power: suppy: ucs1002: Make the symbol 'ucs1002_regulator_enable' static
  power: reset: at91-poweroff: use proper master clock register offset
  power: reset: at91-poweroff: introduce struct shdwc_reg_config
  power: supply: bq25890_charger: Add DT and I2C ids for all supported chips
  dt-bindings: Add new chips to bq25890 binding documentation
  power: supply: bq25890_charger: Add support of BQ25892 and BQ25896 chips
  power: supply: core: Update sysfs-class-power ABI document
  power: supply: sbs-battery: Fix a signedness bug in sbs_get_battery_capacity()
  power: supply: ltc2941-battery-gauge: fix use-after-free
  power: supply: max17040: Correct IRQ wake handling
  power: supply: axp20x_usb_power: Remove unused device_node
  power: supply: axp20x_ac_power: Add wakeup control
  power: supply: axp20x_ac_power: Allow offlining
  power: supply: axp20x_ac_power: Fix reporting online status
  ...
parents 893e591b 3d32a843
...@@ -189,7 +189,8 @@ Description: ...@@ -189,7 +189,8 @@ Description:
Access: Read Access: Read
Valid values: "Unknown", "Good", "Overheat", "Dead", Valid values: "Unknown", "Good", "Overheat", "Dead",
"Over voltage", "Unspecified failure", "Cold", "Over voltage", "Unspecified failure", "Cold",
"Watchdog timer expire", "Safety timer expire" "Watchdog timer expire", "Safety timer expire",
"Over current"
What: /sys/class/power_supply/<supply_name>/precharge_current What: /sys/class/power_supply/<supply_name>/precharge_current
Date: June 2017 Date: June 2017
......
...@@ -35,6 +35,10 @@ Optional Properties: ...@@ -35,6 +35,10 @@ Optional Properties:
for each of the battery capacity lookup table. The first temperature value for each of the battery capacity lookup table. The first temperature value
specifies the OCV table 0, and the second temperature value specifies the specifies the OCV table 0, and the second temperature value specifies the
OCV table 1, and so on. OCV table 1, and so on.
- resistance-temp-table: An array providing the temperature in degree Celsius
and corresponding battery internal resistance percent, which is used to look
up the resistance percent according to current temperature to get a accurate
batterty internal resistance in different temperatures.
Battery properties are named, where possible, for the corresponding Battery properties are named, where possible, for the corresponding
elements in enum power_supply_property, defined in elements in enum power_supply_property, defined in
...@@ -61,6 +65,7 @@ Example: ...@@ -61,6 +65,7 @@ Example:
ocv-capacity-table-0 = <4185000 100>, <4113000 95>, <4066000 90>, ...; ocv-capacity-table-0 = <4185000 100>, <4113000 95>, <4066000 90>, ...;
ocv-capacity-table-1 = <4200000 100>, <4185000 95>, <4113000 90>, ...; ocv-capacity-table-1 = <4200000 100>, <4185000 95>, <4113000 90>, ...;
ocv-capacity-table-2 = <4250000 100>, <4200000 95>, <4185000 90>, ...; ocv-capacity-table-2 = <4250000 100>, <4200000 95>, <4185000 90>, ...;
resistance-temp-table = <20 100>, <10 90>, <0 80>, <(-10) 60>;
}; };
charger: charger@11 { charger: charger@11 {
......
Binding for TI bq25890 Li-Ion Charger Binding for TI bq25890 Li-Ion Charger
This driver will support the bq25896 and the bq25890. There are other ICs This driver will support the bq25892, the bq25896 and the bq25890. There are
in the same family but those have not been tested. other ICs in the same family but those have not been tested.
Required properties: Required properties:
- compatible: Should contain one of the following: - compatible: Should contain one of the following:
* "ti,bq25890" * "ti,bq25890"
* "ti,bq25892"
* "ti,bq25895"
* "ti,bq25896"
- reg: integer, i2c address of the device. - reg: integer, i2c address of the device.
- ti,battery-regulation-voltage: integer, maximum charging voltage (in uV); - ti,battery-regulation-voltage: integer, maximum charging voltage (in uV);
- ti,charge-current: integer, maximum charging current (in uA); - ti,charge-current: integer, maximum charging current (in uA);
......
max17040_battery
~~~~~~~~~~~~~~~~
Required properties :
- compatible : "maxim,max17040" or "maxim,max77836-battery"
- reg: i2c slave address
Optional properties :
- maxim,alert-low-soc-level : The alert threshold that sets the state of
charge level (%) where an interrupt is
generated. Can be configured from 1 up to 32
(%). If skipped the power up default value of
4 (%) will be used.
- interrupts : Interrupt line see Documentation/devicetree/
bindings/interrupt-controller/interrupts.txt
- wakeup-source : This device has wakeup capabilities. Use this
property to use alert low SOC level interrupt
as wake up source.
Optional properties support interrupt functionality for alert low state of
charge level, present in some ICs in the same family, and should be used with
compatible "maxim,max77836-battery".
Example:
battery-fuel-gauge@36 {
compatible = "maxim,max77836-battery";
reg = <0x36>;
maxim,alert-low-soc-level = <10>;
interrupt-parent = <&gpio7>;
interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
wakeup-source;
};
...@@ -2,7 +2,11 @@ max17042_battery ...@@ -2,7 +2,11 @@ max17042_battery
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
Required properties : Required properties :
- compatible : "maxim,max17042" - compatible : one of the following
* "maxim,max17042"
* "maxim,max17047"
* "maxim,max17050"
* "maxim,max17055"
Optional properties : Optional properties :
- maxim,rsns-microohm : Resistance of rsns resistor in micro Ohms - maxim,rsns-microohm : Resistance of rsns resistor in micro Ohms
......
...@@ -13,6 +13,8 @@ Required properties: ...@@ -13,6 +13,8 @@ Required properties:
- io-channel-names: Should be "bat-temp" or "charge-vol". - io-channel-names: Should be "bat-temp" or "charge-vol".
- nvmem-cells: A phandle to the calibration cells provided by eFuse device. - nvmem-cells: A phandle to the calibration cells provided by eFuse device.
- nvmem-cell-names: Should be "fgu_calib". - nvmem-cell-names: Should be "fgu_calib".
- sprd,calib-resistance-micro-ohms: Specify the real resistance of coulomb counter
chip in micro Ohms.
- monitored-battery: Phandle of battery characteristics devicetree node. - monitored-battery: Phandle of battery characteristics devicetree node.
See Documentation/devicetree/bindings/power/supply/battery.txt See Documentation/devicetree/bindings/power/supply/battery.txt
...@@ -52,5 +54,6 @@ Example: ...@@ -52,5 +54,6 @@ Example:
nvmem-cells = <&fgu_calib>; nvmem-cells = <&fgu_calib>;
nvmem-cell-names = "fgu_calib"; nvmem-cell-names = "fgu_calib";
monitored-battery = <&bat>; monitored-battery = <&bat>;
sprd,calib-resistance-micro-ohms = <21500>;
}; };
}; };
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
#define SHDW_CFG_NOT_USED (32) #define SHDW_CFG_NOT_USED (32)
struct shdwc_config { struct shdwc_reg_config {
u8 wkup_pin_input; u8 wkup_pin_input;
u8 mr_rtcwk_shift; u8 mr_rtcwk_shift;
u8 mr_rttwk_shift; u8 mr_rttwk_shift;
...@@ -74,8 +74,17 @@ struct shdwc_config { ...@@ -74,8 +74,17 @@ struct shdwc_config {
u8 sr_rttwk_shift; u8 sr_rttwk_shift;
}; };
struct pmc_reg_config {
u8 mckr;
};
struct reg_config {
struct shdwc_reg_config shdwc;
struct pmc_reg_config pmc;
};
struct shdwc { struct shdwc {
const struct shdwc_config *cfg; const struct reg_config *rcfg;
struct clk *sclk; struct clk *sclk;
void __iomem *shdwc_base; void __iomem *shdwc_base;
void __iomem *mpddrc_base; void __iomem *mpddrc_base;
...@@ -95,6 +104,7 @@ static const unsigned long long sdwc_dbc_period[] = { ...@@ -95,6 +104,7 @@ static const unsigned long long sdwc_dbc_period[] = {
static void __init at91_wakeup_status(struct platform_device *pdev) static void __init at91_wakeup_status(struct platform_device *pdev)
{ {
struct shdwc *shdw = platform_get_drvdata(pdev); struct shdwc *shdw = platform_get_drvdata(pdev);
const struct reg_config *rcfg = shdw->rcfg;
u32 reg; u32 reg;
char *reason = "unknown"; char *reason = "unknown";
...@@ -106,11 +116,11 @@ static void __init at91_wakeup_status(struct platform_device *pdev) ...@@ -106,11 +116,11 @@ static void __init at91_wakeup_status(struct platform_device *pdev)
if (!reg) if (!reg)
return; return;
if (SHDW_WK_PIN(reg, shdw->cfg)) if (SHDW_WK_PIN(reg, &rcfg->shdwc))
reason = "WKUP pin"; reason = "WKUP pin";
else if (SHDW_RTCWK(reg, shdw->cfg)) else if (SHDW_RTCWK(reg, &rcfg->shdwc))
reason = "RTC"; reason = "RTC";
else if (SHDW_RTTWK(reg, shdw->cfg)) else if (SHDW_RTTWK(reg, &rcfg->shdwc))
reason = "RTT"; reason = "RTT";
pr_info("AT91: Wake-Up source: %s\n", reason); pr_info("AT91: Wake-Up source: %s\n", reason);
...@@ -131,9 +141,9 @@ static void at91_poweroff(void) ...@@ -131,9 +141,9 @@ static void at91_poweroff(void)
" str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t" " str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
/* Switch the master clock source to slow clock. */ /* Switch the master clock source to slow clock. */
"1: ldr r6, [%4, #" __stringify(AT91_PMC_MCKR) "]\n\t" "1: ldr r6, [%4, %5]\n\t"
" bic r6, r6, #" __stringify(AT91_PMC_CSS) "\n\t" " bic r6, r6, #" __stringify(AT91_PMC_CSS) "\n\t"
" str r6, [%4, #" __stringify(AT91_PMC_MCKR) "]\n\t" " str r6, [%4, %5]\n\t"
/* Wait for clock switch. */ /* Wait for clock switch. */
"2: ldr r6, [%4, #" __stringify(AT91_PMC_SR) "]\n\t" "2: ldr r6, [%4, #" __stringify(AT91_PMC_SR) "]\n\t"
" tst r6, #" __stringify(AT91_PMC_MCKRDY) "\n\t" " tst r6, #" __stringify(AT91_PMC_MCKRDY) "\n\t"
...@@ -148,7 +158,8 @@ static void at91_poweroff(void) ...@@ -148,7 +158,8 @@ static void at91_poweroff(void)
"r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF), "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
"r" (at91_shdwc->shdwc_base), "r" (at91_shdwc->shdwc_base),
"r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW), "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW),
"r" (at91_shdwc->pmc_base) "r" (at91_shdwc->pmc_base),
"r" (at91_shdwc->rcfg->pmc.mckr)
: "r6"); : "r6");
} }
...@@ -215,6 +226,7 @@ static u32 at91_shdwc_get_wakeup_input(struct platform_device *pdev, ...@@ -215,6 +226,7 @@ static u32 at91_shdwc_get_wakeup_input(struct platform_device *pdev,
static void at91_shdwc_dt_configure(struct platform_device *pdev) static void at91_shdwc_dt_configure(struct platform_device *pdev)
{ {
struct shdwc *shdw = platform_get_drvdata(pdev); struct shdwc *shdw = platform_get_drvdata(pdev);
const struct reg_config *rcfg = shdw->rcfg;
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
u32 mode = 0, tmp, input; u32 mode = 0, tmp, input;
...@@ -227,10 +239,10 @@ static void at91_shdwc_dt_configure(struct platform_device *pdev) ...@@ -227,10 +239,10 @@ static void at91_shdwc_dt_configure(struct platform_device *pdev)
mode |= AT91_SHDW_WKUPDBC(at91_shdwc_debouncer_value(pdev, tmp)); mode |= AT91_SHDW_WKUPDBC(at91_shdwc_debouncer_value(pdev, tmp));
if (of_property_read_bool(np, "atmel,wakeup-rtc-timer")) if (of_property_read_bool(np, "atmel,wakeup-rtc-timer"))
mode |= SHDW_RTCWKEN(shdw->cfg); mode |= SHDW_RTCWKEN(&rcfg->shdwc);
if (of_property_read_bool(np, "atmel,wakeup-rtt-timer")) if (of_property_read_bool(np, "atmel,wakeup-rtt-timer"))
mode |= SHDW_RTTWKEN(shdw->cfg); mode |= SHDW_RTTWKEN(&rcfg->shdwc);
dev_dbg(&pdev->dev, "%s: mode = %#x\n", __func__, mode); dev_dbg(&pdev->dev, "%s: mode = %#x\n", __func__, mode);
writel(mode, shdw->shdwc_base + AT91_SHDW_MR); writel(mode, shdw->shdwc_base + AT91_SHDW_MR);
...@@ -239,30 +251,40 @@ static void at91_shdwc_dt_configure(struct platform_device *pdev) ...@@ -239,30 +251,40 @@ static void at91_shdwc_dt_configure(struct platform_device *pdev)
writel(input, shdw->shdwc_base + AT91_SHDW_WUIR); writel(input, shdw->shdwc_base + AT91_SHDW_WUIR);
} }
static const struct shdwc_config sama5d2_shdwc_config = { static const struct reg_config sama5d2_reg_config = {
.shdwc = {
.wkup_pin_input = 0, .wkup_pin_input = 0,
.mr_rtcwk_shift = 17, .mr_rtcwk_shift = 17,
.mr_rttwk_shift = SHDW_CFG_NOT_USED, .mr_rttwk_shift = SHDW_CFG_NOT_USED,
.sr_rtcwk_shift = 5, .sr_rtcwk_shift = 5,
.sr_rttwk_shift = SHDW_CFG_NOT_USED, .sr_rttwk_shift = SHDW_CFG_NOT_USED,
},
.pmc = {
.mckr = 0x30,
},
}; };
static const struct shdwc_config sam9x60_shdwc_config = { static const struct reg_config sam9x60_reg_config = {
.shdwc = {
.wkup_pin_input = 0, .wkup_pin_input = 0,
.mr_rtcwk_shift = 17, .mr_rtcwk_shift = 17,
.mr_rttwk_shift = 16, .mr_rttwk_shift = 16,
.sr_rtcwk_shift = 5, .sr_rtcwk_shift = 5,
.sr_rttwk_shift = 4, .sr_rttwk_shift = 4,
},
.pmc = {
.mckr = 0x28,
},
}; };
static const struct of_device_id at91_shdwc_of_match[] = { static const struct of_device_id at91_shdwc_of_match[] = {
{ {
.compatible = "atmel,sama5d2-shdwc", .compatible = "atmel,sama5d2-shdwc",
.data = &sama5d2_shdwc_config, .data = &sama5d2_reg_config,
}, },
{ {
.compatible = "microchip,sam9x60-shdwc", .compatible = "microchip,sam9x60-shdwc",
.data = &sam9x60_shdwc_config, .data = &sam9x60_reg_config,
}, { }, {
/*sentinel*/ /*sentinel*/
} }
...@@ -303,7 +325,7 @@ static int __init at91_shdwc_probe(struct platform_device *pdev) ...@@ -303,7 +325,7 @@ static int __init at91_shdwc_probe(struct platform_device *pdev)
} }
match = of_match_node(at91_shdwc_of_match, pdev->dev.of_node); match = of_match_node(at91_shdwc_of_match, pdev->dev.of_node);
at91_shdwc->cfg = match->data; at91_shdwc->rcfg = match->data;
at91_shdwc->sclk = devm_clk_get(&pdev->dev, NULL); at91_shdwc->sclk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(at91_shdwc->sclk)) if (IS_ERR(at91_shdwc->sclk))
......
...@@ -64,9 +64,11 @@ static int gpio_restart_probe(struct platform_device *pdev) ...@@ -64,9 +64,11 @@ static int gpio_restart_probe(struct platform_device *pdev)
gpio_restart->reset_gpio = devm_gpiod_get(&pdev->dev, NULL, gpio_restart->reset_gpio = devm_gpiod_get(&pdev->dev, NULL,
open_source ? GPIOD_IN : GPIOD_OUT_LOW); open_source ? GPIOD_IN : GPIOD_OUT_LOW);
if (IS_ERR(gpio_restart->reset_gpio)) { ret = PTR_ERR_OR_ZERO(gpio_restart->reset_gpio);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "Could not get reset GPIO\n"); dev_err(&pdev->dev, "Could not get reset GPIO\n");
return PTR_ERR(gpio_restart->reset_gpio); return ret;
} }
gpio_restart->restart_handler.notifier_call = gpio_restart_notify; gpio_restart->restart_handler.notifier_call = gpio_restart_notify;
......
...@@ -789,7 +789,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di, ...@@ -789,7 +789,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05; di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
ret = -ENXIO; ret = -ENXIO;
break; break;
}; }
di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max; di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max;
dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d",
...@@ -1079,7 +1079,7 @@ static int ab8500_charger_get_usb_cur(struct ab8500_charger *di) ...@@ -1079,7 +1079,7 @@ static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05; di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
ret = -EPERM; ret = -EPERM;
break; break;
}; }
di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max; di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max;
return ret; return ret;
} }
...@@ -2427,7 +2427,7 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work) ...@@ -2427,7 +2427,7 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
default: default:
break; break;
}; }
} }
/** /**
......
...@@ -2221,10 +2221,10 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data) ...@@ -2221,10 +2221,10 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
ab8500_fg_update_cap_scalers(di); ab8500_fg_update_cap_scalers(di);
queue_work(di->fg_wq, &di->fg_work); queue_work(di->fg_wq, &di->fg_work);
break; break;
}; }
default: default:
break; break;
}; }
break; break;
case POWER_SUPPLY_PROP_TECHNOLOGY: case POWER_SUPPLY_PROP_TECHNOLOGY:
switch (ext->desc->type) { switch (ext->desc->type) {
...@@ -2331,7 +2331,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di) ...@@ -2331,7 +2331,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
if (ret) { if (ret) {
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_MAX_TIME_REG\n", __func__); dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_MAX_TIME_REG\n", __func__);
goto out; goto out;
}; }
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_FLAG_TIME_REG, di->bm->fg_params->pcut_flag_time); AB8505_RTC_PCUT_FLAG_TIME_REG, di->bm->fg_params->pcut_flag_time);
...@@ -2339,7 +2339,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di) ...@@ -2339,7 +2339,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
if (ret) { if (ret) {
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_FLAG_TIME_REG\n", __func__); dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_FLAG_TIME_REG\n", __func__);
goto out; goto out;
}; }
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_RESTART_REG, di->bm->fg_params->pcut_max_restart); AB8505_RTC_PCUT_RESTART_REG, di->bm->fg_params->pcut_max_restart);
...@@ -2347,7 +2347,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di) ...@@ -2347,7 +2347,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
if (ret) { if (ret) {
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_RESTART_REG\n", __func__); dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_RESTART_REG\n", __func__);
goto out; goto out;
}; }
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_DEBOUNCE_REG, di->bm->fg_params->pcut_debounce_time); AB8505_RTC_PCUT_DEBOUNCE_REG, di->bm->fg_params->pcut_debounce_time);
...@@ -2355,7 +2355,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di) ...@@ -2355,7 +2355,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
if (ret) { if (ret) {
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_DEBOUNCE_REG\n", __func__); dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_DEBOUNCE_REG\n", __func__);
goto out; goto out;
}; }
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
AB8505_RTC_PCUT_CTL_STATUS_REG, di->bm->fg_params->pcut_enable); AB8505_RTC_PCUT_CTL_STATUS_REG, di->bm->fg_params->pcut_enable);
...@@ -2363,7 +2363,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di) ...@@ -2363,7 +2363,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
if (ret) { if (ret) {
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_CTL_STATUS_REG\n", __func__); dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_CTL_STATUS_REG\n", __func__);
goto out; goto out;
}; }
} }
out: out:
return ret; return ret;
......
...@@ -1823,7 +1823,7 @@ static ssize_t abx500_chargalg_en_store(struct abx500_chargalg *di, ...@@ -1823,7 +1823,7 @@ static ssize_t abx500_chargalg_en_store(struct abx500_chargalg *di,
"Enter 0. Disable AC/USB Charging\n" "Enter 0. Disable AC/USB Charging\n"
"1. Enable AC charging\n" "1. Enable AC charging\n"
"2. Enable USB Charging\n"); "2. Enable USB Charging\n");
}; }
return strlen(buf); return strlen(buf);
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -23,6 +24,9 @@ ...@@ -23,6 +24,9 @@
#define AXP20X_PWR_STATUS_ACIN_PRESENT BIT(7) #define AXP20X_PWR_STATUS_ACIN_PRESENT BIT(7)
#define AXP20X_PWR_STATUS_ACIN_AVAIL BIT(6) #define AXP20X_PWR_STATUS_ACIN_AVAIL BIT(6)
#define AXP813_ACIN_PATH_SEL BIT(7)
#define AXP813_ACIN_PATH_SEL_TO_BIT(x) (!!(x) << 7)
#define AXP813_VHOLD_MASK GENMASK(5, 3) #define AXP813_VHOLD_MASK GENMASK(5, 3)
#define AXP813_VHOLD_UV_TO_BIT(x) ((((x) / 100000) - 40) << 3) #define AXP813_VHOLD_UV_TO_BIT(x) ((((x) / 100000) - 40) << 3)
#define AXP813_VHOLD_REG_TO_UV(x) \ #define AXP813_VHOLD_REG_TO_UV(x) \
...@@ -40,6 +44,9 @@ struct axp20x_ac_power { ...@@ -40,6 +44,9 @@ struct axp20x_ac_power {
struct power_supply *supply; struct power_supply *supply;
struct iio_channel *acin_v; struct iio_channel *acin_v;
struct iio_channel *acin_i; struct iio_channel *acin_i;
bool has_acin_path_sel;
unsigned int num_irqs;
unsigned int irqs[];
}; };
static irqreturn_t axp20x_ac_power_irq(int irq, void *devid) static irqreturn_t axp20x_ac_power_irq(int irq, void *devid)
...@@ -86,6 +93,17 @@ static int axp20x_ac_power_get_property(struct power_supply *psy, ...@@ -86,6 +93,17 @@ static int axp20x_ac_power_get_property(struct power_supply *psy,
return ret; return ret;
val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_AVAIL); val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_AVAIL);
/* ACIN_PATH_SEL disables ACIN even if ACIN_AVAIL is set. */
if (val->intval && power->has_acin_path_sel) {
ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL,
&reg);
if (ret)
return ret;
val->intval = !!(reg & AXP813_ACIN_PATH_SEL);
}
return 0; return 0;
case POWER_SUPPLY_PROP_VOLTAGE_NOW: case POWER_SUPPLY_PROP_VOLTAGE_NOW:
...@@ -143,6 +161,11 @@ static int axp813_ac_power_set_property(struct power_supply *psy, ...@@ -143,6 +161,11 @@ static int axp813_ac_power_set_property(struct power_supply *psy,
struct axp20x_ac_power *power = power_supply_get_drvdata(psy); struct axp20x_ac_power *power = power_supply_get_drvdata(psy);
switch (psp) { switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
return regmap_update_bits(power->regmap, AXP813_ACIN_PATH_CTRL,
AXP813_ACIN_PATH_SEL,
AXP813_ACIN_PATH_SEL_TO_BIT(val->intval));
case POWER_SUPPLY_PROP_VOLTAGE_MIN: case POWER_SUPPLY_PROP_VOLTAGE_MIN:
if (val->intval < 4000000 || val->intval > 4700000) if (val->intval < 4000000 || val->intval > 4700000)
return -EINVAL; return -EINVAL;
...@@ -169,7 +192,8 @@ static int axp813_ac_power_set_property(struct power_supply *psy, ...@@ -169,7 +192,8 @@ static int axp813_ac_power_set_property(struct power_supply *psy,
static int axp813_ac_power_prop_writeable(struct power_supply *psy, static int axp813_ac_power_prop_writeable(struct power_supply *psy,
enum power_supply_property psp) enum power_supply_property psp)
{ {
return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN || return psp == POWER_SUPPLY_PROP_ONLINE ||
psp == POWER_SUPPLY_PROP_VOLTAGE_MIN ||
psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT; psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
} }
...@@ -221,34 +245,86 @@ static const struct power_supply_desc axp813_ac_power_desc = { ...@@ -221,34 +245,86 @@ static const struct power_supply_desc axp813_ac_power_desc = {
.set_property = axp813_ac_power_set_property, .set_property = axp813_ac_power_set_property,
}; };
static const char * const axp20x_irq_names[] = {
"ACIN_PLUGIN",
"ACIN_REMOVAL",
};
struct axp_data { struct axp_data {
const struct power_supply_desc *power_desc; const struct power_supply_desc *power_desc;
const char * const *irq_names;
unsigned int num_irq_names;
bool acin_adc; bool acin_adc;
bool acin_path_sel;
}; };
static const struct axp_data axp20x_data = { static const struct axp_data axp20x_data = {
.power_desc = &axp20x_ac_power_desc, .power_desc = &axp20x_ac_power_desc,
.irq_names = axp20x_irq_names,
.num_irq_names = ARRAY_SIZE(axp20x_irq_names),
.acin_adc = true, .acin_adc = true,
.acin_path_sel = false,
}; };
static const struct axp_data axp22x_data = { static const struct axp_data axp22x_data = {
.power_desc = &axp22x_ac_power_desc, .power_desc = &axp22x_ac_power_desc,
.irq_names = axp20x_irq_names,
.num_irq_names = ARRAY_SIZE(axp20x_irq_names),
.acin_adc = false, .acin_adc = false,
.acin_path_sel = false,
}; };
static const struct axp_data axp813_data = { static const struct axp_data axp813_data = {
.power_desc = &axp813_ac_power_desc, .power_desc = &axp813_ac_power_desc,
.irq_names = axp20x_irq_names,
.num_irq_names = ARRAY_SIZE(axp20x_irq_names),
.acin_adc = false, .acin_adc = false,
.acin_path_sel = true,
}; };
#ifdef CONFIG_PM_SLEEP
static int axp20x_ac_power_suspend(struct device *dev)
{
struct axp20x_ac_power *power = dev_get_drvdata(dev);
int i = 0;
/*
* Allow wake via ACIN_PLUGIN only.
*
* As nested threaded IRQs are not automatically disabled during
* suspend, we must explicitly disable the remainder of the IRQs.
*/
if (device_may_wakeup(&power->supply->dev))
enable_irq_wake(power->irqs[i++]);
while (i < power->num_irqs)
disable_irq(power->irqs[i++]);
return 0;
}
static int axp20x_ac_power_resume(struct device *dev)
{
struct axp20x_ac_power *power = dev_get_drvdata(dev);
int i = 0;
if (device_may_wakeup(&power->supply->dev))
disable_irq_wake(power->irqs[i++]);
while (i < power->num_irqs)
enable_irq(power->irqs[i++]);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(axp20x_ac_power_pm_ops, axp20x_ac_power_suspend,
axp20x_ac_power_resume);
static int axp20x_ac_power_probe(struct platform_device *pdev) static int axp20x_ac_power_probe(struct platform_device *pdev)
{ {
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
struct power_supply_config psy_cfg = {}; struct power_supply_config psy_cfg = {};
struct axp20x_ac_power *power; struct axp20x_ac_power *power;
const struct axp_data *axp_data; const struct axp_data *axp_data;
static const char * const irq_names[] = { "ACIN_PLUGIN", "ACIN_REMOVAL",
NULL };
int i, irq, ret; int i, irq, ret;
if (!of_device_is_available(pdev->dev.of_node)) if (!of_device_is_available(pdev->dev.of_node))
...@@ -259,12 +335,14 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) ...@@ -259,12 +335,14 @@ static int axp20x_ac_power_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL); axp_data = of_device_get_match_data(&pdev->dev);
power = devm_kzalloc(&pdev->dev,
struct_size(power, irqs, axp_data->num_irq_names),
GFP_KERNEL);
if (!power) if (!power)
return -ENOMEM; return -ENOMEM;
axp_data = of_device_get_match_data(&pdev->dev);
if (axp_data->acin_adc) { if (axp_data->acin_adc) {
power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v"); power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v");
if (IS_ERR(power->acin_v)) { if (IS_ERR(power->acin_v)) {
...@@ -282,6 +360,8 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) ...@@ -282,6 +360,8 @@ static int axp20x_ac_power_probe(struct platform_device *pdev)
} }
power->regmap = dev_get_regmap(pdev->dev.parent, NULL); power->regmap = dev_get_regmap(pdev->dev.parent, NULL);
power->has_acin_path_sel = axp_data->acin_path_sel;
power->num_irqs = axp_data->num_irq_names;
platform_set_drvdata(pdev, power); platform_set_drvdata(pdev, power);
...@@ -295,20 +375,22 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) ...@@ -295,20 +375,22 @@ static int axp20x_ac_power_probe(struct platform_device *pdev)
return PTR_ERR(power->supply); return PTR_ERR(power->supply);
/* Request irqs after registering, as irqs may trigger immediately */ /* Request irqs after registering, as irqs may trigger immediately */
for (i = 0; irq_names[i]; i++) { for (i = 0; i < axp_data->num_irq_names; i++) {
irq = platform_get_irq_byname(pdev, irq_names[i]); irq = platform_get_irq_byname(pdev, axp_data->irq_names[i]);
if (irq < 0) { if (irq < 0) {
dev_warn(&pdev->dev, "No IRQ for %s: %d\n", dev_err(&pdev->dev, "No IRQ for %s: %d\n",
irq_names[i], irq); axp_data->irq_names[i], irq);
continue; return irq;
} }
irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq); power->irqs[i] = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
ret = devm_request_any_context_irq(&pdev->dev, irq, ret = devm_request_any_context_irq(&pdev->dev, power->irqs[i],
axp20x_ac_power_irq, 0, axp20x_ac_power_irq, 0,
DRVNAME, power); DRVNAME, power);
if (ret < 0) if (ret < 0) {
dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n", dev_err(&pdev->dev, "Error requesting %s IRQ: %d\n",
irq_names[i], ret); axp_data->irq_names[i], ret);
return ret;
}
} }
return 0; return 0;
...@@ -333,6 +415,7 @@ static struct platform_driver axp20x_ac_power_driver = { ...@@ -333,6 +415,7 @@ static struct platform_driver axp20x_ac_power_driver = {
.driver = { .driver = {
.name = DRVNAME, .name = DRVNAME,
.of_match_table = axp20x_ac_power_match, .of_match_table = axp20x_ac_power_match,
.pm = &axp20x_ac_power_pm_ops,
}, },
}; };
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -29,6 +30,9 @@ ...@@ -29,6 +30,9 @@
#define AXP20X_USB_STATUS_VBUS_VALID BIT(2) #define AXP20X_USB_STATUS_VBUS_VALID BIT(2)
#define AXP20X_VBUS_PATH_SEL BIT(7)
#define AXP20X_VBUS_PATH_SEL_OFFSET 7
#define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000) #define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000)
#define AXP20X_VBUS_VHOLD_MASK GENMASK(5, 3) #define AXP20X_VBUS_VHOLD_MASK GENMASK(5, 3)
#define AXP20X_VBUS_VHOLD_OFFSET 3 #define AXP20X_VBUS_VHOLD_OFFSET 3
...@@ -57,7 +61,6 @@ ...@@ -57,7 +61,6 @@
#define DEBOUNCE_TIME msecs_to_jiffies(50) #define DEBOUNCE_TIME msecs_to_jiffies(50)
struct axp20x_usb_power { struct axp20x_usb_power {
struct device_node *np;
struct regmap *regmap; struct regmap *regmap;
struct power_supply *supply; struct power_supply *supply;
enum axp20x_variants axp20x_id; enum axp20x_variants axp20x_id;
...@@ -65,14 +68,32 @@ struct axp20x_usb_power { ...@@ -65,14 +68,32 @@ struct axp20x_usb_power {
struct iio_channel *vbus_i; struct iio_channel *vbus_i;
struct delayed_work vbus_detect; struct delayed_work vbus_detect;
unsigned int old_status; unsigned int old_status;
unsigned int online;
unsigned int num_irqs;
unsigned int irqs[];
}; };
static bool axp20x_usb_vbus_needs_polling(struct axp20x_usb_power *power)
{
/*
* Polling is only necessary while VBUS is offline. While online, a
* present->absent transition implies an online->offline transition
* and will triger the VBUS_REMOVAL IRQ.
*/
if (power->axp20x_id >= AXP221_ID && !power->online)
return true;
return false;
}
static irqreturn_t axp20x_usb_power_irq(int irq, void *devid) static irqreturn_t axp20x_usb_power_irq(int irq, void *devid)
{ {
struct axp20x_usb_power *power = devid; struct axp20x_usb_power *power = devid;
power_supply_changed(power->supply); power_supply_changed(power->supply);
mod_delayed_work(system_wq, &power->vbus_detect, DEBOUNCE_TIME);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -92,19 +113,13 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work) ...@@ -92,19 +113,13 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work)
power_supply_changed(power->supply); power_supply_changed(power->supply);
power->old_status = val; power->old_status = val;
power->online = val & AXP20X_PWR_STATUS_VBUS_USED;
out: out:
if (axp20x_usb_vbus_needs_polling(power))
mod_delayed_work(system_wq, &power->vbus_detect, DEBOUNCE_TIME); mod_delayed_work(system_wq, &power->vbus_detect, DEBOUNCE_TIME);
} }
static bool axp20x_usb_vbus_needs_polling(struct axp20x_usb_power *power)
{
if (power->axp20x_id >= AXP221_ID)
return true;
return false;
}
static int axp20x_get_current_max(struct axp20x_usb_power *power, int *val) static int axp20x_get_current_max(struct axp20x_usb_power *power, int *val)
{ {
unsigned int v; unsigned int v;
...@@ -264,6 +279,16 @@ static int axp20x_usb_power_get_property(struct power_supply *psy, ...@@ -264,6 +279,16 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
return 0; return 0;
} }
static int axp813_usb_power_set_online(struct axp20x_usb_power *power,
int intval)
{
int val = !intval << AXP20X_VBUS_PATH_SEL_OFFSET;
return regmap_update_bits(power->regmap,
AXP20X_VBUS_IPSOUT_MGMT,
AXP20X_VBUS_PATH_SEL, val);
}
static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power, static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power,
int intval) int intval)
{ {
...@@ -345,6 +370,11 @@ static int axp20x_usb_power_set_property(struct power_supply *psy, ...@@ -345,6 +370,11 @@ static int axp20x_usb_power_set_property(struct power_supply *psy,
struct axp20x_usb_power *power = power_supply_get_drvdata(psy); struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
switch (psp) { switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
if (power->axp20x_id != AXP813_ID)
return -EINVAL;
return axp813_usb_power_set_online(power, val->intval);
case POWER_SUPPLY_PROP_VOLTAGE_MIN: case POWER_SUPPLY_PROP_VOLTAGE_MIN:
return axp20x_usb_power_set_voltage_min(power, val->intval); return axp20x_usb_power_set_voltage_min(power, val->intval);
...@@ -364,6 +394,18 @@ static int axp20x_usb_power_set_property(struct power_supply *psy, ...@@ -364,6 +394,18 @@ static int axp20x_usb_power_set_property(struct power_supply *psy,
static int axp20x_usb_power_prop_writeable(struct power_supply *psy, static int axp20x_usb_power_prop_writeable(struct power_supply *psy,
enum power_supply_property psp) enum power_supply_property psp)
{ {
struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
/*
* The VBUS path select flag works differently on on AXP288 and newer:
* - On AXP20x and AXP22x, the flag enables VBUS (ignoring N_VBUSEN).
* - On AXP288 and AXP8xx, the flag disables VBUS (ignoring N_VBUSEN).
* We only expose the control on variants where it can be used to force
* the VBUS input offline.
*/
if (psp == POWER_SUPPLY_PROP_ONLINE)
return power->axp20x_id == AXP813_ID;
return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN || return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN ||
psp == POWER_SUPPLY_PROP_CURRENT_MAX; psp == POWER_SUPPLY_PROP_CURRENT_MAX;
} }
...@@ -406,6 +448,92 @@ static const struct power_supply_desc axp22x_usb_power_desc = { ...@@ -406,6 +448,92 @@ static const struct power_supply_desc axp22x_usb_power_desc = {
.set_property = axp20x_usb_power_set_property, .set_property = axp20x_usb_power_set_property,
}; };
static const char * const axp20x_irq_names[] = {
"VBUS_PLUGIN",
"VBUS_REMOVAL",
"VBUS_VALID",
"VBUS_NOT_VALID",
};
static const char * const axp22x_irq_names[] = {
"VBUS_PLUGIN",
"VBUS_REMOVAL",
};
struct axp_data {
const struct power_supply_desc *power_desc;
const char * const *irq_names;
unsigned int num_irq_names;
enum axp20x_variants axp20x_id;
};
static const struct axp_data axp202_data = {
.power_desc = &axp20x_usb_power_desc,
.irq_names = axp20x_irq_names,
.num_irq_names = ARRAY_SIZE(axp20x_irq_names),
.axp20x_id = AXP202_ID,
};
static const struct axp_data axp221_data = {
.power_desc = &axp22x_usb_power_desc,
.irq_names = axp22x_irq_names,
.num_irq_names = ARRAY_SIZE(axp22x_irq_names),
.axp20x_id = AXP221_ID,
};
static const struct axp_data axp223_data = {
.power_desc = &axp22x_usb_power_desc,
.irq_names = axp22x_irq_names,
.num_irq_names = ARRAY_SIZE(axp22x_irq_names),
.axp20x_id = AXP223_ID,
};
static const struct axp_data axp813_data = {
.power_desc = &axp22x_usb_power_desc,
.irq_names = axp22x_irq_names,
.num_irq_names = ARRAY_SIZE(axp22x_irq_names),
.axp20x_id = AXP813_ID,
};
#ifdef CONFIG_PM_SLEEP
static int axp20x_usb_power_suspend(struct device *dev)
{
struct axp20x_usb_power *power = dev_get_drvdata(dev);
int i = 0;
/*
* Allow wake via VBUS_PLUGIN only.
*
* As nested threaded IRQs are not automatically disabled during
* suspend, we must explicitly disable the remainder of the IRQs.
*/
if (device_may_wakeup(&power->supply->dev))
enable_irq_wake(power->irqs[i++]);
while (i < power->num_irqs)
disable_irq(power->irqs[i++]);
return 0;
}
static int axp20x_usb_power_resume(struct device *dev)
{
struct axp20x_usb_power *power = dev_get_drvdata(dev);
int i = 0;
if (device_may_wakeup(&power->supply->dev))
disable_irq_wake(power->irqs[i++]);
while (i < power->num_irqs)
enable_irq(power->irqs[i++]);
mod_delayed_work(system_wq, &power->vbus_detect, DEBOUNCE_TIME);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(axp20x_usb_power_pm_ops, axp20x_usb_power_suspend,
axp20x_usb_power_resume);
static int configure_iio_channels(struct platform_device *pdev, static int configure_iio_channels(struct platform_device *pdev,
struct axp20x_usb_power *power) struct axp20x_usb_power *power)
{ {
...@@ -441,12 +569,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) ...@@ -441,12 +569,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
struct power_supply_config psy_cfg = {}; struct power_supply_config psy_cfg = {};
struct axp20x_usb_power *power; struct axp20x_usb_power *power;
static const char * const axp20x_irq_names[] = { "VBUS_PLUGIN", const struct axp_data *axp_data;
"VBUS_REMOVAL", "VBUS_VALID", "VBUS_NOT_VALID", NULL };
static const char * const axp22x_irq_names[] = {
"VBUS_PLUGIN", "VBUS_REMOVAL", NULL };
const char * const *irq_names;
const struct power_supply_desc *usb_power_desc;
int i, irq, ret; int i, irq, ret;
if (!of_device_is_available(pdev->dev.of_node)) if (!of_device_is_available(pdev->dev.of_node))
...@@ -457,16 +580,19 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) ...@@ -457,16 +580,19 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL); axp_data = of_device_get_match_data(&pdev->dev);
power = devm_kzalloc(&pdev->dev,
struct_size(power, irqs, axp_data->num_irq_names),
GFP_KERNEL);
if (!power) if (!power)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, power); platform_set_drvdata(pdev, power);
power->axp20x_id = (enum axp20x_variants)of_device_get_match_data(
&pdev->dev);
power->np = pdev->dev.of_node; power->axp20x_id = axp_data->axp20x_id;
power->regmap = axp20x->regmap; power->regmap = axp20x->regmap;
power->num_irqs = axp_data->num_irq_names;
if (power->axp20x_id == AXP202_ID) { if (power->axp20x_id == AXP202_ID) {
/* Enable vbus valid checking */ /* Enable vbus valid checking */
...@@ -483,18 +609,6 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) ...@@ -483,18 +609,6 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
if (ret) if (ret)
return ret; return ret;
usb_power_desc = &axp20x_usb_power_desc;
irq_names = axp20x_irq_names;
} else if (power->axp20x_id == AXP221_ID ||
power->axp20x_id == AXP223_ID ||
power->axp20x_id == AXP813_ID) {
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;
} }
if (power->axp20x_id == AXP813_ID) { if (power->axp20x_id == AXP813_ID) {
...@@ -506,25 +620,29 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) ...@@ -506,25 +620,29 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
psy_cfg.of_node = pdev->dev.of_node; psy_cfg.of_node = pdev->dev.of_node;
psy_cfg.drv_data = power; psy_cfg.drv_data = power;
power->supply = devm_power_supply_register(&pdev->dev, usb_power_desc, power->supply = devm_power_supply_register(&pdev->dev,
axp_data->power_desc,
&psy_cfg); &psy_cfg);
if (IS_ERR(power->supply)) if (IS_ERR(power->supply))
return PTR_ERR(power->supply); return PTR_ERR(power->supply);
/* Request irqs after registering, as irqs may trigger immediately */ /* Request irqs after registering, as irqs may trigger immediately */
for (i = 0; irq_names[i]; i++) { for (i = 0; i < axp_data->num_irq_names; i++) {
irq = platform_get_irq_byname(pdev, irq_names[i]); irq = platform_get_irq_byname(pdev, axp_data->irq_names[i]);
if (irq < 0) { if (irq < 0) {
dev_warn(&pdev->dev, "No IRQ for %s: %d\n", dev_err(&pdev->dev, "No IRQ for %s: %d\n",
irq_names[i], irq); axp_data->irq_names[i], irq);
continue; return irq;
}
power->irqs[i] = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
ret = devm_request_any_context_irq(&pdev->dev, power->irqs[i],
axp20x_usb_power_irq, 0,
DRVNAME, power);
if (ret < 0) {
dev_err(&pdev->dev, "Error requesting %s IRQ: %d\n",
axp_data->irq_names[i], ret);
return ret;
} }
irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
ret = devm_request_any_context_irq(&pdev->dev, irq,
axp20x_usb_power_irq, 0, DRVNAME, power);
if (ret < 0)
dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n",
irq_names[i], ret);
} }
INIT_DELAYED_WORK(&power->vbus_detect, axp20x_usb_power_poll_vbus); INIT_DELAYED_WORK(&power->vbus_detect, axp20x_usb_power_poll_vbus);
...@@ -546,16 +664,16 @@ static int axp20x_usb_power_remove(struct platform_device *pdev) ...@@ -546,16 +664,16 @@ static int axp20x_usb_power_remove(struct platform_device *pdev)
static const struct of_device_id axp20x_usb_power_match[] = { static const struct of_device_id axp20x_usb_power_match[] = {
{ {
.compatible = "x-powers,axp202-usb-power-supply", .compatible = "x-powers,axp202-usb-power-supply",
.data = (void *)AXP202_ID, .data = &axp202_data,
}, { }, {
.compatible = "x-powers,axp221-usb-power-supply", .compatible = "x-powers,axp221-usb-power-supply",
.data = (void *)AXP221_ID, .data = &axp221_data,
}, { }, {
.compatible = "x-powers,axp223-usb-power-supply", .compatible = "x-powers,axp223-usb-power-supply",
.data = (void *)AXP223_ID, .data = &axp223_data,
}, { }, {
.compatible = "x-powers,axp813-usb-power-supply", .compatible = "x-powers,axp813-usb-power-supply",
.data = (void *)AXP813_ID, .data = &axp813_data,
}, { /* sentinel */ } }, { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, axp20x_usb_power_match); MODULE_DEVICE_TABLE(of, axp20x_usb_power_match);
...@@ -566,6 +684,7 @@ static struct platform_driver axp20x_usb_power_driver = { ...@@ -566,6 +684,7 @@ static struct platform_driver axp20x_usb_power_driver = {
.driver = { .driver = {
.name = DRVNAME, .name = DRVNAME,
.of_match_table = axp20x_usb_power_match, .of_match_table = axp20x_usb_power_match,
.pm = &axp20x_usb_power_pm_ops,
}, },
}; };
......
...@@ -25,12 +25,20 @@ ...@@ -25,12 +25,20 @@
#define BQ25895_ID 7 #define BQ25895_ID 7
#define BQ25896_ID 0 #define BQ25896_ID 0
enum bq25890_chip_version {
BQ25890,
BQ25892,
BQ25895,
BQ25896,
};
enum bq25890_fields { enum bq25890_fields {
F_EN_HIZ, F_EN_ILIM, F_IILIM, /* Reg00 */ F_EN_HIZ, F_EN_ILIM, F_IILIM, /* Reg00 */
F_BHOT, F_BCOLD, F_VINDPM_OFS, /* Reg01 */ F_BHOT, F_BCOLD, F_VINDPM_OFS, /* Reg01 */
F_CONV_START, F_CONV_RATE, F_BOOSTF, F_ICO_EN, F_CONV_START, F_CONV_RATE, F_BOOSTF, F_ICO_EN,
F_HVDCP_EN, F_MAXC_EN, F_FORCE_DPM, F_AUTO_DPDM_EN, /* Reg02 */ F_HVDCP_EN, F_MAXC_EN, F_FORCE_DPM, F_AUTO_DPDM_EN, /* Reg02 */
F_BAT_LOAD_EN, F_WD_RST, F_OTG_CFG, F_CHG_CFG, F_SYSVMIN, /* Reg03 */ F_BAT_LOAD_EN, F_WD_RST, F_OTG_CFG, F_CHG_CFG, F_SYSVMIN,
F_MIN_VBAT_SEL, /* Reg03 */
F_PUMPX_EN, F_ICHG, /* Reg04 */ F_PUMPX_EN, F_ICHG, /* Reg04 */
F_IPRECHG, F_ITERM, /* Reg05 */ F_IPRECHG, F_ITERM, /* Reg05 */
F_VREG, F_BATLOWV, F_VRECHG, /* Reg06 */ F_VREG, F_BATLOWV, F_VRECHG, /* Reg06 */
...@@ -39,8 +47,9 @@ enum bq25890_fields { ...@@ -39,8 +47,9 @@ enum bq25890_fields {
F_BATCMP, F_VCLAMP, F_TREG, /* Reg08 */ F_BATCMP, F_VCLAMP, F_TREG, /* Reg08 */
F_FORCE_ICO, F_TMR2X_EN, F_BATFET_DIS, F_JEITA_VSET, F_FORCE_ICO, F_TMR2X_EN, F_BATFET_DIS, F_JEITA_VSET,
F_BATFET_DLY, F_BATFET_RST_EN, F_PUMPX_UP, F_PUMPX_DN, /* Reg09 */ F_BATFET_DLY, F_BATFET_RST_EN, F_PUMPX_UP, F_PUMPX_DN, /* Reg09 */
F_BOOSTV, F_BOOSTI, /* Reg0A */ F_BOOSTV, F_PFM_OTG_DIS, F_BOOSTI, /* Reg0A */
F_VBUS_STAT, F_CHG_STAT, F_PG_STAT, F_SDP_STAT, F_VSYS_STAT, /* Reg0B */ F_VBUS_STAT, F_CHG_STAT, F_PG_STAT, F_SDP_STAT, F_0B_RSVD,
F_VSYS_STAT, /* Reg0B */
F_WD_FAULT, F_BOOST_FAULT, F_CHG_FAULT, F_BAT_FAULT, F_WD_FAULT, F_BOOST_FAULT, F_CHG_FAULT, F_BAT_FAULT,
F_NTC_FAULT, /* Reg0C */ F_NTC_FAULT, /* Reg0C */
F_FORCE_VINDPM, F_VINDPM, /* Reg0D */ F_FORCE_VINDPM, F_VINDPM, /* Reg0D */
...@@ -91,7 +100,7 @@ struct bq25890_device { ...@@ -91,7 +100,7 @@ struct bq25890_device {
struct regmap *rmap; struct regmap *rmap;
struct regmap_field *rmap_fields[F_MAX_FIELDS]; struct regmap_field *rmap_fields[F_MAX_FIELDS];
int chip_id; enum bq25890_chip_version chip_version;
struct bq25890_init_data init_data; struct bq25890_init_data init_data;
struct bq25890_state state; struct bq25890_state state;
...@@ -111,8 +120,7 @@ static const struct regmap_access_table bq25890_writeable_regs = { ...@@ -111,8 +120,7 @@ static const struct regmap_access_table bq25890_writeable_regs = {
static const struct regmap_range bq25890_volatile_reg_ranges[] = { static const struct regmap_range bq25890_volatile_reg_ranges[] = {
regmap_reg_range(0x00, 0x00), regmap_reg_range(0x00, 0x00),
regmap_reg_range(0x09, 0x09), regmap_reg_range(0x09, 0x09),
regmap_reg_range(0x0b, 0x0c), regmap_reg_range(0x0b, 0x14),
regmap_reg_range(0x0e, 0x14),
}; };
static const struct regmap_access_table bq25890_volatile_regs = { static const struct regmap_access_table bq25890_volatile_regs = {
...@@ -155,7 +163,7 @@ static const struct reg_field bq25890_reg_fields[] = { ...@@ -155,7 +163,7 @@ static const struct reg_field bq25890_reg_fields[] = {
[F_OTG_CFG] = REG_FIELD(0x03, 5, 5), [F_OTG_CFG] = REG_FIELD(0x03, 5, 5),
[F_CHG_CFG] = REG_FIELD(0x03, 4, 4), [F_CHG_CFG] = REG_FIELD(0x03, 4, 4),
[F_SYSVMIN] = REG_FIELD(0x03, 1, 3), [F_SYSVMIN] = REG_FIELD(0x03, 1, 3),
/* MIN_VBAT_SEL on BQ25896 */ [F_MIN_VBAT_SEL] = REG_FIELD(0x03, 0, 0), // BQ25896 only
/* REG04 */ /* REG04 */
[F_PUMPX_EN] = REG_FIELD(0x04, 7, 7), [F_PUMPX_EN] = REG_FIELD(0x04, 7, 7),
[F_ICHG] = REG_FIELD(0x04, 0, 6), [F_ICHG] = REG_FIELD(0x04, 0, 6),
...@@ -188,8 +196,8 @@ static const struct reg_field bq25890_reg_fields[] = { ...@@ -188,8 +196,8 @@ static const struct reg_field bq25890_reg_fields[] = {
[F_PUMPX_DN] = REG_FIELD(0x09, 0, 0), [F_PUMPX_DN] = REG_FIELD(0x09, 0, 0),
/* REG0A */ /* REG0A */
[F_BOOSTV] = REG_FIELD(0x0A, 4, 7), [F_BOOSTV] = REG_FIELD(0x0A, 4, 7),
/* PFM_OTG_DIS 3 on BQ25896 */
[F_BOOSTI] = REG_FIELD(0x0A, 0, 2), // reserved on BQ25895 [F_BOOSTI] = REG_FIELD(0x0A, 0, 2), // reserved on BQ25895
[F_PFM_OTG_DIS] = REG_FIELD(0x0A, 3, 3), // BQ25896 only
/* REG0B */ /* REG0B */
[F_VBUS_STAT] = REG_FIELD(0x0B, 5, 7), [F_VBUS_STAT] = REG_FIELD(0x0B, 5, 7),
[F_CHG_STAT] = REG_FIELD(0x0B, 3, 4), [F_CHG_STAT] = REG_FIELD(0x0B, 3, 4),
...@@ -275,6 +283,7 @@ static const union { ...@@ -275,6 +283,7 @@ static const union {
struct bq25890_lookup lt; struct bq25890_lookup lt;
} bq25890_tables[] = { } bq25890_tables[] = {
/* range tables */ /* range tables */
/* TODO: BQ25896 has max ICHG 3008 mA */
[TBL_ICHG] = { .rt = {0, 5056000, 64000} }, /* uA */ [TBL_ICHG] = { .rt = {0, 5056000, 64000} }, /* uA */
[TBL_ITERM] = { .rt = {64000, 1024000, 64000} }, /* uA */ [TBL_ITERM] = { .rt = {64000, 1024000, 64000} }, /* uA */
[TBL_VREG] = { .rt = {3840000, 4608000, 16000} }, /* uV */ [TBL_VREG] = { .rt = {3840000, 4608000, 16000} }, /* uV */
...@@ -391,11 +400,13 @@ static int bq25890_power_supply_get_property(struct power_supply *psy, ...@@ -391,11 +400,13 @@ static int bq25890_power_supply_get_property(struct power_supply *psy,
break; break;
case POWER_SUPPLY_PROP_MODEL_NAME: case POWER_SUPPLY_PROP_MODEL_NAME:
if (bq->chip_id == BQ25890_ID) if (bq->chip_version == BQ25890)
val->strval = "BQ25890"; val->strval = "BQ25890";
else if (bq->chip_id == BQ25895_ID) else if (bq->chip_version == BQ25892)
val->strval = "BQ25892";
else if (bq->chip_version == BQ25895)
val->strval = "BQ25895"; val->strval = "BQ25895";
else if (bq->chip_id == BQ25896_ID) else if (bq->chip_version == BQ25896)
val->strval = "BQ25896"; val->strval = "BQ25896";
else else
val->strval = "UNKNOWN"; val->strval = "UNKNOWN";
...@@ -741,6 +752,56 @@ static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val, ...@@ -741,6 +752,56 @@ static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val,
return NOTIFY_OK; return NOTIFY_OK;
} }
static int bq25890_get_chip_version(struct bq25890_device *bq)
{
int id, rev;
id = bq25890_field_read(bq, F_PN);
if (id < 0) {
dev_err(bq->dev, "Cannot read chip ID.\n");
return id;
}
rev = bq25890_field_read(bq, F_DEV_REV);
if (rev < 0) {
dev_err(bq->dev, "Cannot read chip revision.\n");
return rev;
}
switch (id) {
case BQ25890_ID:
bq->chip_version = BQ25890;
break;
/* BQ25892 and BQ25896 share same ID 0 */
case BQ25896_ID:
switch (rev) {
case 2:
bq->chip_version = BQ25896;
break;
case 1:
bq->chip_version = BQ25892;
break;
default:
dev_err(bq->dev,
"Unknown device revision %d, assume BQ25892\n",
rev);
bq->chip_version = BQ25892;
}
break;
case BQ25895_ID:
bq->chip_version = BQ25895;
break;
default:
dev_err(bq->dev, "Unknown chip ID %d\n", id);
return -ENODEV;
}
return 0;
}
static int bq25890_irq_probe(struct bq25890_device *bq) static int bq25890_irq_probe(struct bq25890_device *bq)
{ {
struct gpio_desc *irq; struct gpio_desc *irq;
...@@ -859,16 +920,10 @@ static int bq25890_probe(struct i2c_client *client, ...@@ -859,16 +920,10 @@ static int bq25890_probe(struct i2c_client *client,
i2c_set_clientdata(client, bq); i2c_set_clientdata(client, bq);
bq->chip_id = bq25890_field_read(bq, F_PN); ret = bq25890_get_chip_version(bq);
if (bq->chip_id < 0) { if (ret) {
dev_err(dev, "Cannot read chip ID.\n"); dev_err(dev, "Cannot read chip ID or unknown chip.\n");
return bq->chip_id; return ret;
}
if ((bq->chip_id != BQ25890_ID) && (bq->chip_id != BQ25895_ID)
&& (bq->chip_id != BQ25896_ID)) {
dev_err(dev, "Chip with ID=%d, not supported!\n", bq->chip_id);
return -ENODEV;
} }
if (!dev->platform_data) { if (!dev->platform_data) {
...@@ -986,12 +1041,18 @@ static const struct dev_pm_ops bq25890_pm = { ...@@ -986,12 +1041,18 @@ static const struct dev_pm_ops bq25890_pm = {
static const struct i2c_device_id bq25890_i2c_ids[] = { static const struct i2c_device_id bq25890_i2c_ids[] = {
{ "bq25890", 0 }, { "bq25890", 0 },
{ "bq25892", 0 },
{ "bq25895", 0 },
{ "bq25896", 0 },
{}, {},
}; };
MODULE_DEVICE_TABLE(i2c, bq25890_i2c_ids); MODULE_DEVICE_TABLE(i2c, bq25890_i2c_ids);
static const struct of_device_id bq25890_of_match[] = { static const struct of_device_id bq25890_of_match[] = {
{ .compatible = "ti,bq25890", }, { .compatible = "ti,bq25890", },
{ .compatible = "ti,bq25892", },
{ .compatible = "ti,bq25895", },
{ .compatible = "ti,bq25896", },
{ }, { },
}; };
MODULE_DEVICE_TABLE(of, bq25890_of_match); MODULE_DEVICE_TABLE(of, bq25890_of_match);
......
...@@ -132,11 +132,8 @@ static int cros_usbpd_charger_get_num_ports(struct charger_data *charger) ...@@ -132,11 +132,8 @@ static int cros_usbpd_charger_get_num_ports(struct charger_data *charger)
ret = cros_usbpd_charger_ec_command(charger, 0, ret = cros_usbpd_charger_ec_command(charger, 0,
EC_CMD_CHARGE_PORT_COUNT, EC_CMD_CHARGE_PORT_COUNT,
NULL, 0, &resp, sizeof(resp)); NULL, 0, &resp, sizeof(resp));
if (ret < 0) { if (ret < 0)
dev_err(charger->dev,
"Unable to get the number of ports (err:0x%x)\n", ret);
return ret; return ret;
}
return resp.port_count; return resp.port_count;
} }
...@@ -148,11 +145,8 @@ static int cros_usbpd_charger_get_usbpd_num_ports(struct charger_data *charger) ...@@ -148,11 +145,8 @@ static int cros_usbpd_charger_get_usbpd_num_ports(struct charger_data *charger)
ret = cros_usbpd_charger_ec_command(charger, 0, EC_CMD_USB_PD_PORTS, ret = cros_usbpd_charger_ec_command(charger, 0, EC_CMD_USB_PD_PORTS,
NULL, 0, &resp, sizeof(resp)); NULL, 0, &resp, sizeof(resp));
if (ret < 0) { if (ret < 0)
dev_err(charger->dev,
"Unable to get the number or ports (err:0x%x)\n", ret);
return ret; return ret;
}
return resp.num_ports; return resp.num_ports;
} }
......
...@@ -100,10 +100,17 @@ static int ingenic_battery_set_scale(struct ingenic_battery *bat) ...@@ -100,10 +100,17 @@ static int ingenic_battery_set_scale(struct ingenic_battery *bat)
return -EINVAL; return -EINVAL;
} }
return iio_write_channel_attribute(bat->channel, /* Only set scale if there is more than one (fractional) entry */
if (scale_len > 2) {
ret = iio_write_channel_attribute(bat->channel,
scale_raw[best_idx], scale_raw[best_idx],
scale_raw[best_idx + 1], scale_raw[best_idx + 1],
IIO_CHAN_INFO_SCALE); IIO_CHAN_INFO_SCALE);
if (ret)
return ret;
}
return 0;
} }
static enum power_supply_property ingenic_battery_properties[] = { static enum power_supply_property ingenic_battery_properties[] = {
......
...@@ -149,7 +149,7 @@ static int micro_batt_get_property(struct power_supply *b, ...@@ -149,7 +149,7 @@ static int micro_batt_get_property(struct power_supply *b,
default: default:
val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
break; break;
}; }
break; break;
case POWER_SUPPLY_PROP_STATUS: case POWER_SUPPLY_PROP_STATUS:
val->intval = get_status(b); val->intval = get_status(b);
...@@ -168,7 +168,7 @@ static int micro_batt_get_property(struct power_supply *b, ...@@ -168,7 +168,7 @@ static int micro_batt_get_property(struct power_supply *b,
break; break;
default: default:
return -EINVAL; return -EINVAL;
}; }
return 0; return 0;
} }
...@@ -185,7 +185,7 @@ static int micro_ac_get_property(struct power_supply *b, ...@@ -185,7 +185,7 @@ static int micro_ac_get_property(struct power_supply *b,
break; break;
default: default:
return -EINVAL; return -EINVAL;
}; }
return 0; return 0;
} }
......
...@@ -449,7 +449,7 @@ static int ltc294x_i2c_remove(struct i2c_client *client) ...@@ -449,7 +449,7 @@ static int ltc294x_i2c_remove(struct i2c_client *client)
{ {
struct ltc294x_info *info = i2c_get_clientdata(client); struct ltc294x_info *info = i2c_get_clientdata(client);
cancel_delayed_work(&info->work); cancel_delayed_work_sync(&info->work);
power_supply_unregister(info->supply); power_supply_unregister(info->supply);
return 0; return 0;
} }
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/max17040_battery.h> #include <linux/max17040_battery.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -28,6 +29,9 @@ ...@@ -28,6 +29,9 @@
#define MAX17040_DELAY 1000 #define MAX17040_DELAY 1000
#define MAX17040_BATTERY_FULL 95 #define MAX17040_BATTERY_FULL 95
#define MAX17040_ATHD_MASK 0xFFC0
#define MAX17040_ATHD_DEFAULT_POWER_UP 4
struct max17040_chip { struct max17040_chip {
struct i2c_client *client; struct i2c_client *client;
struct delayed_work work; struct delayed_work work;
...@@ -42,6 +46,8 @@ struct max17040_chip { ...@@ -42,6 +46,8 @@ struct max17040_chip {
int soc; int soc;
/* State Of Charge */ /* State Of Charge */
int status; int status;
/* Low alert threshold from 32% to 1% of the State of Charge */
u32 low_soc_alert;
}; };
static int max17040_get_property(struct power_supply *psy, static int max17040_get_property(struct power_supply *psy,
...@@ -98,6 +104,21 @@ static void max17040_reset(struct i2c_client *client) ...@@ -98,6 +104,21 @@ static void max17040_reset(struct i2c_client *client)
max17040_write_reg(client, MAX17040_CMD, 0x0054); max17040_write_reg(client, MAX17040_CMD, 0x0054);
} }
static int max17040_set_low_soc_alert(struct i2c_client *client, u32 level)
{
int ret;
u16 data;
level = 32 - level;
data = max17040_read_reg(client, MAX17040_RCOMP);
/* clear the alrt bit and set LSb 5 bits */
data &= MAX17040_ATHD_MASK;
data |= level;
ret = max17040_write_reg(client, MAX17040_RCOMP, data);
return ret;
}
static void max17040_get_vcell(struct i2c_client *client) static void max17040_get_vcell(struct i2c_client *client)
{ {
struct max17040_chip *chip = i2c_get_clientdata(client); struct max17040_chip *chip = i2c_get_clientdata(client);
...@@ -160,21 +181,81 @@ static void max17040_get_status(struct i2c_client *client) ...@@ -160,21 +181,81 @@ static void max17040_get_status(struct i2c_client *client)
chip->status = POWER_SUPPLY_STATUS_FULL; chip->status = POWER_SUPPLY_STATUS_FULL;
} }
static int max17040_get_of_data(struct max17040_chip *chip)
{
struct device *dev = &chip->client->dev;
chip->low_soc_alert = MAX17040_ATHD_DEFAULT_POWER_UP;
device_property_read_u32(dev,
"maxim,alert-low-soc-level",
&chip->low_soc_alert);
if (chip->low_soc_alert <= 0 || chip->low_soc_alert >= 33)
return -EINVAL;
return 0;
}
static void max17040_check_changes(struct i2c_client *client)
{
max17040_get_vcell(client);
max17040_get_soc(client);
max17040_get_online(client);
max17040_get_status(client);
}
static void max17040_work(struct work_struct *work) static void max17040_work(struct work_struct *work)
{ {
struct max17040_chip *chip; struct max17040_chip *chip;
int last_soc, last_status;
chip = container_of(work, struct max17040_chip, work.work); chip = container_of(work, struct max17040_chip, work.work);
max17040_get_vcell(chip->client); /* store SOC and status to check changes */
max17040_get_soc(chip->client); last_soc = chip->soc;
max17040_get_online(chip->client); last_status = chip->status;
max17040_get_status(chip->client); max17040_check_changes(chip->client);
/* check changes and send uevent */
if (last_soc != chip->soc || last_status != chip->status)
power_supply_changed(chip->battery);
queue_delayed_work(system_power_efficient_wq, &chip->work, queue_delayed_work(system_power_efficient_wq, &chip->work,
MAX17040_DELAY); MAX17040_DELAY);
} }
static irqreturn_t max17040_thread_handler(int id, void *dev)
{
struct max17040_chip *chip = dev;
struct i2c_client *client = chip->client;
dev_warn(&client->dev, "IRQ: Alert battery low level");
/* read registers */
max17040_check_changes(chip->client);
/* send uevent */
power_supply_changed(chip->battery);
/* reset alert bit */
max17040_set_low_soc_alert(client, chip->low_soc_alert);
return IRQ_HANDLED;
}
static int max17040_enable_alert_irq(struct max17040_chip *chip)
{
struct i2c_client *client = chip->client;
unsigned int flags;
int ret;
flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
max17040_thread_handler, flags,
chip->battery->desc->name, chip);
return ret;
}
static enum power_supply_property max17040_battery_props[] = { static enum power_supply_property max17040_battery_props[] = {
POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_ONLINE,
...@@ -196,6 +277,7 @@ static int max17040_probe(struct i2c_client *client, ...@@ -196,6 +277,7 @@ static int max17040_probe(struct i2c_client *client,
struct i2c_adapter *adapter = client->adapter; struct i2c_adapter *adapter = client->adapter;
struct power_supply_config psy_cfg = {}; struct power_supply_config psy_cfg = {};
struct max17040_chip *chip; struct max17040_chip *chip;
int ret;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
return -EIO; return -EIO;
...@@ -206,6 +288,12 @@ static int max17040_probe(struct i2c_client *client, ...@@ -206,6 +288,12 @@ static int max17040_probe(struct i2c_client *client,
chip->client = client; chip->client = client;
chip->pdata = client->dev.platform_data; chip->pdata = client->dev.platform_data;
ret = max17040_get_of_data(chip);
if (ret) {
dev_err(&client->dev,
"failed: low SOC alert OF data out of bounds\n");
return ret;
}
i2c_set_clientdata(client, chip); i2c_set_clientdata(client, chip);
psy_cfg.drv_data = chip; psy_cfg.drv_data = chip;
...@@ -220,6 +308,24 @@ static int max17040_probe(struct i2c_client *client, ...@@ -220,6 +308,24 @@ static int max17040_probe(struct i2c_client *client,
max17040_reset(client); max17040_reset(client);
max17040_get_version(client); max17040_get_version(client);
/* check interrupt */
if (client->irq && of_device_is_compatible(client->dev.of_node,
"maxim,max77836-battery")) {
ret = max17040_set_low_soc_alert(client, chip->low_soc_alert);
if (ret) {
dev_err(&client->dev,
"Failed to set low SOC alert: err %d\n", ret);
return ret;
}
ret = max17040_enable_alert_irq(chip);
if (ret) {
client->irq = 0;
dev_warn(&client->dev,
"Failed to get IRQ err %d\n", ret);
}
}
INIT_DEFERRABLE_WORK(&chip->work, max17040_work); INIT_DEFERRABLE_WORK(&chip->work, max17040_work);
queue_delayed_work(system_power_efficient_wq, &chip->work, queue_delayed_work(system_power_efficient_wq, &chip->work,
MAX17040_DELAY); MAX17040_DELAY);
...@@ -244,6 +350,10 @@ static int max17040_suspend(struct device *dev) ...@@ -244,6 +350,10 @@ static int max17040_suspend(struct device *dev)
struct max17040_chip *chip = i2c_get_clientdata(client); struct max17040_chip *chip = i2c_get_clientdata(client);
cancel_delayed_work(&chip->work); cancel_delayed_work(&chip->work);
if (client->irq && device_may_wakeup(dev))
enable_irq_wake(client->irq);
return 0; return 0;
} }
...@@ -254,6 +364,10 @@ static int max17040_resume(struct device *dev) ...@@ -254,6 +364,10 @@ static int max17040_resume(struct device *dev)
queue_delayed_work(system_power_efficient_wq, &chip->work, queue_delayed_work(system_power_efficient_wq, &chip->work,
MAX17040_DELAY); MAX17040_DELAY);
if (client->irq && device_may_wakeup(dev))
disable_irq_wake(client->irq);
return 0; return 0;
} }
......
...@@ -282,6 +282,8 @@ static int max17042_get_property(struct power_supply *psy, ...@@ -282,6 +282,8 @@ static int max17042_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
ret = regmap_read(map, MAX17042_V_empty, &data); ret = regmap_read(map, MAX17042_V_empty, &data);
else if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)
ret = regmap_read(map, MAX17055_V_empty, &data);
else else
ret = regmap_read(map, MAX17047_V_empty, &data); ret = regmap_read(map, MAX17047_V_empty, &data);
if (ret < 0) if (ret < 0)
...@@ -627,7 +629,8 @@ static void max17042_write_config_regs(struct max17042_chip *chip) ...@@ -627,7 +629,8 @@ static void max17042_write_config_regs(struct max17042_chip *chip)
config->filter_cfg); config->filter_cfg);
regmap_write(map, MAX17042_RelaxCFG, config->relax_cfg); regmap_write(map, MAX17042_RelaxCFG, config->relax_cfg);
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047 || if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047 ||
chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050) chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050 ||
chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)
regmap_write(map, MAX17047_FullSOCThr, regmap_write(map, MAX17047_FullSOCThr,
config->full_soc_thresh); config->full_soc_thresh);
} }
...@@ -758,6 +761,8 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) ...@@ -758,6 +761,8 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
max17042_override_por(map, MAX17042_V_empty, config->vempty); max17042_override_por(map, MAX17042_V_empty, config->vempty);
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)
max17042_override_por(map, MAX17055_V_empty, config->vempty);
else else
max17042_override_por(map, MAX17047_V_empty, config->vempty); max17042_override_por(map, MAX17047_V_empty, config->vempty);
max17042_override_por(map, MAX17042_TempNom, config->temp_nom); max17042_override_por(map, MAX17042_TempNom, config->temp_nom);
...@@ -765,7 +770,10 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) ...@@ -765,7 +770,10 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)
max17042_override_por(map, MAX17042_FCTC, config->fctc); max17042_override_por(map, MAX17042_FCTC, config->fctc);
max17042_override_por(map, MAX17042_RCOMP0, config->rcomp0); max17042_override_por(map, MAX17042_RCOMP0, config->rcomp0);
max17042_override_por(map, MAX17042_TempCo, config->tcompc0); max17042_override_por(map, MAX17042_TempCo, config->tcompc0);
if (chip->chip_type) { if (chip->chip_type &&
((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) ||
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) ||
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050))) {
max17042_override_por(map, MAX17042_EmptyTempCo, max17042_override_por(map, MAX17042_EmptyTempCo,
config->empty_tempco); config->empty_tempco);
max17042_override_por(map, MAX17042_K_empty0, max17042_override_por(map, MAX17042_K_empty0,
...@@ -929,7 +937,8 @@ max17042_get_default_pdata(struct max17042_chip *chip) ...@@ -929,7 +937,8 @@ max17042_get_default_pdata(struct max17042_chip *chip)
if (!pdata) if (!pdata)
return pdata; return pdata;
if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17042) { if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) ||
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050)) {
pdata->init_data = max17047_default_pdata_init_regs; pdata->init_data = max17047_default_pdata_init_regs;
pdata->num_init_data = pdata->num_init_data =
ARRAY_SIZE(max17047_default_pdata_init_regs); ARRAY_SIZE(max17047_default_pdata_init_regs);
...@@ -1167,6 +1176,7 @@ static const struct of_device_id max17042_dt_match[] = { ...@@ -1167,6 +1176,7 @@ static const struct of_device_id max17042_dt_match[] = {
{ .compatible = "maxim,max17042" }, { .compatible = "maxim,max17042" },
{ .compatible = "maxim,max17047" }, { .compatible = "maxim,max17047" },
{ .compatible = "maxim,max17050" }, { .compatible = "maxim,max17050" },
{ .compatible = "maxim,max17055" },
{ }, { },
}; };
MODULE_DEVICE_TABLE(of, max17042_dt_match); MODULE_DEVICE_TABLE(of, max17042_dt_match);
...@@ -1176,6 +1186,7 @@ static const struct i2c_device_id max17042_id[] = { ...@@ -1176,6 +1186,7 @@ static const struct i2c_device_id max17042_id[] = {
{ "max17042", MAXIM_DEVICE_TYPE_MAX17042 }, { "max17042", MAXIM_DEVICE_TYPE_MAX17042 },
{ "max17047", MAXIM_DEVICE_TYPE_MAX17047 }, { "max17047", MAXIM_DEVICE_TYPE_MAX17047 },
{ "max17050", MAXIM_DEVICE_TYPE_MAX17050 }, { "max17050", MAXIM_DEVICE_TYPE_MAX17050 },
{ "max17055", MAXIM_DEVICE_TYPE_MAX17055 },
{ } { }
}; };
MODULE_DEVICE_TABLE(i2c, max17042_id); MODULE_DEVICE_TABLE(i2c, max17042_id);
......
...@@ -354,9 +354,16 @@ static int max77650_charger_remove(struct platform_device *pdev) ...@@ -354,9 +354,16 @@ static int max77650_charger_remove(struct platform_device *pdev)
return max77650_charger_disable(chg); return max77650_charger_disable(chg);
} }
static const struct of_device_id max77650_charger_of_match[] = {
{ .compatible = "maxim,max77650-charger" },
{ }
};
MODULE_DEVICE_TABLE(of, max77650_charger_of_match);
static struct platform_driver max77650_charger_driver = { static struct platform_driver max77650_charger_driver = {
.driver = { .driver = {
.name = "max77650-charger", .name = "max77650-charger",
.of_match_table = max77650_charger_of_match,
}, },
.probe = max77650_charger_probe, .probe = max77650_charger_probe,
.remove = max77650_charger_remove, .remove = max77650_charger_remove,
......
...@@ -429,6 +429,10 @@ static int pda_power_probe(struct platform_device *pdev) ...@@ -429,6 +429,10 @@ static int pda_power_probe(struct platform_device *pdev)
static int pda_power_remove(struct platform_device *pdev) static int pda_power_remove(struct platform_device *pdev)
{ {
#if IS_ENABLED(CONFIG_USB_PHY)
if (!IS_ERR_OR_NULL(transceiver) && pdata->use_otg_notifier)
usb_unregister_notifier(transceiver, &otg_nb);
#endif
if (pdata->is_usb_online && usb_irq) if (pdata->is_usb_online && usb_irq)
free_irq(usb_irq->start, pda_psy_usb); free_irq(usb_irq->start, pda_psy_usb);
if (pdata->is_ac_online && ac_irq) if (pdata->is_ac_online && ac_irq)
......
...@@ -565,9 +565,11 @@ EXPORT_SYMBOL_GPL(devm_power_supply_get_by_phandle); ...@@ -565,9 +565,11 @@ EXPORT_SYMBOL_GPL(devm_power_supply_get_by_phandle);
int power_supply_get_battery_info(struct power_supply *psy, int power_supply_get_battery_info(struct power_supply *psy,
struct power_supply_battery_info *info) struct power_supply_battery_info *info)
{ {
struct power_supply_resistance_temp_table *resist_table;
struct device_node *battery_np; struct device_node *battery_np;
const char *value; const char *value;
int err, len, index; int err, len, index;
const __be32 *list;
info->energy_full_design_uwh = -EINVAL; info->energy_full_design_uwh = -EINVAL;
info->charge_full_design_uah = -EINVAL; info->charge_full_design_uah = -EINVAL;
...@@ -578,6 +580,7 @@ int power_supply_get_battery_info(struct power_supply *psy, ...@@ -578,6 +580,7 @@ int power_supply_get_battery_info(struct power_supply *psy,
info->constant_charge_current_max_ua = -EINVAL; info->constant_charge_current_max_ua = -EINVAL;
info->constant_charge_voltage_max_uv = -EINVAL; info->constant_charge_voltage_max_uv = -EINVAL;
info->factory_internal_resistance_uohm = -EINVAL; info->factory_internal_resistance_uohm = -EINVAL;
info->resist_table = NULL;
for (index = 0; index < POWER_SUPPLY_OCV_TEMP_MAX; index++) { for (index = 0; index < POWER_SUPPLY_OCV_TEMP_MAX; index++) {
info->ocv_table[index] = NULL; info->ocv_table[index] = NULL;
...@@ -644,7 +647,6 @@ int power_supply_get_battery_info(struct power_supply *psy, ...@@ -644,7 +647,6 @@ int power_supply_get_battery_info(struct power_supply *psy,
for (index = 0; index < len; index++) { for (index = 0; index < len; index++) {
struct power_supply_battery_ocv_table *table; struct power_supply_battery_ocv_table *table;
char *propname; char *propname;
const __be32 *list;
int i, tab_len, size; int i, tab_len, size;
propname = kasprintf(GFP_KERNEL, "ocv-capacity-table-%d", index); propname = kasprintf(GFP_KERNEL, "ocv-capacity-table-%d", index);
...@@ -677,6 +679,26 @@ int power_supply_get_battery_info(struct power_supply *psy, ...@@ -677,6 +679,26 @@ int power_supply_get_battery_info(struct power_supply *psy,
} }
} }
list = of_get_property(battery_np, "resistance-temp-table", &len);
if (!list || !len)
goto out_put_node;
info->resist_table_size = len / (2 * sizeof(__be32));
resist_table = info->resist_table = devm_kcalloc(&psy->dev,
info->resist_table_size,
sizeof(*resist_table),
GFP_KERNEL);
if (!info->resist_table) {
power_supply_put_battery_info(psy, info);
err = -ENOMEM;
goto out_put_node;
}
for (index = 0; index < info->resist_table_size; index++) {
resist_table[index].temp = be32_to_cpu(*list++);
resist_table[index].resistance = be32_to_cpu(*list++);
}
out_put_node: out_put_node:
of_node_put(battery_np); of_node_put(battery_np);
return err; return err;
...@@ -692,9 +714,52 @@ void power_supply_put_battery_info(struct power_supply *psy, ...@@ -692,9 +714,52 @@ void power_supply_put_battery_info(struct power_supply *psy,
if (info->ocv_table[i]) if (info->ocv_table[i])
devm_kfree(&psy->dev, info->ocv_table[i]); devm_kfree(&psy->dev, info->ocv_table[i]);
} }
if (info->resist_table)
devm_kfree(&psy->dev, info->resist_table);
} }
EXPORT_SYMBOL_GPL(power_supply_put_battery_info); EXPORT_SYMBOL_GPL(power_supply_put_battery_info);
/**
* power_supply_temp2resist_simple() - find the battery internal resistance
* percent
* @table: Pointer to battery resistance temperature table
* @table_len: The table length
* @ocv: Current temperature
*
* This helper function is used to look up battery internal resistance percent
* according to current temperature value from the resistance temperature table,
* and the table must be ordered descending. Then the actual battery internal
* resistance = the ideal battery internal resistance * percent / 100.
*
* Return: the battery internal resistance percent
*/
int power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *table,
int table_len, int temp)
{
int i, resist;
for (i = 0; i < table_len; i++)
if (temp > table[i].temp)
break;
if (i > 0 && i < table_len) {
int tmp;
tmp = (table[i - 1].resistance - table[i].resistance) *
(temp - table[i].temp);
tmp /= table[i - 1].temp - table[i].temp;
resist = tmp + table[i].resistance;
} else if (i == 0) {
resist = table[0].resistance;
} else {
resist = table[table_len - 1].resistance;
}
return resist;
}
EXPORT_SYMBOL_GPL(power_supply_temp2resist_simple);
/** /**
* power_supply_ocv2cap_simple() - find the battery capacity * power_supply_ocv2cap_simple() - find the battery capacity
* @table: Pointer to battery OCV lookup table * @table: Pointer to battery OCV lookup table
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* Copyright (c) 2010, NVIDIA Corporation. * Copyright (c) 2010, NVIDIA Corporation.
*/ */
#include <linux/bits.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
...@@ -46,10 +47,10 @@ enum { ...@@ -46,10 +47,10 @@ enum {
/* Battery Mode defines */ /* Battery Mode defines */
#define BATTERY_MODE_OFFSET 0x03 #define BATTERY_MODE_OFFSET 0x03
#define BATTERY_MODE_MASK 0x8000 #define BATTERY_MODE_CAPACITY_MASK BIT(15)
enum sbs_battery_mode { enum sbs_capacity_mode {
BATTERY_MODE_AMPS = 0, CAPACITY_MODE_AMPS = 0,
BATTERY_MODE_WATTS = 0x8000 CAPACITY_MODE_WATTS = BATTERY_MODE_CAPACITY_MASK
}; };
/* manufacturer access defines */ /* manufacturer access defines */
...@@ -518,8 +519,8 @@ static void sbs_unit_adjustment(struct i2c_client *client, ...@@ -518,8 +519,8 @@ static void sbs_unit_adjustment(struct i2c_client *client,
} }
} }
static enum sbs_battery_mode sbs_set_battery_mode(struct i2c_client *client, static enum sbs_capacity_mode sbs_set_capacity_mode(struct i2c_client *client,
enum sbs_battery_mode mode) enum sbs_capacity_mode mode)
{ {
int ret, original_val; int ret, original_val;
...@@ -527,13 +528,13 @@ static enum sbs_battery_mode sbs_set_battery_mode(struct i2c_client *client, ...@@ -527,13 +528,13 @@ static enum sbs_battery_mode sbs_set_battery_mode(struct i2c_client *client,
if (original_val < 0) if (original_val < 0)
return original_val; return original_val;
if ((original_val & BATTERY_MODE_MASK) == mode) if ((original_val & BATTERY_MODE_CAPACITY_MASK) == mode)
return mode; return mode;
if (mode == BATTERY_MODE_AMPS) if (mode == CAPACITY_MODE_AMPS)
ret = original_val & ~BATTERY_MODE_MASK; ret = original_val & ~BATTERY_MODE_CAPACITY_MASK;
else else
ret = original_val | BATTERY_MODE_MASK; ret = original_val | BATTERY_MODE_CAPACITY_MASK;
ret = sbs_write_word_data(client, BATTERY_MODE_OFFSET, ret); ret = sbs_write_word_data(client, BATTERY_MODE_OFFSET, ret);
if (ret < 0) if (ret < 0)
...@@ -541,7 +542,7 @@ static enum sbs_battery_mode sbs_set_battery_mode(struct i2c_client *client, ...@@ -541,7 +542,7 @@ static enum sbs_battery_mode sbs_set_battery_mode(struct i2c_client *client,
usleep_range(1000, 2000); usleep_range(1000, 2000);
return original_val & BATTERY_MODE_MASK; return original_val & BATTERY_MODE_CAPACITY_MASK;
} }
static int sbs_get_battery_capacity(struct i2c_client *client, static int sbs_get_battery_capacity(struct i2c_client *client,
...@@ -549,13 +550,13 @@ static int sbs_get_battery_capacity(struct i2c_client *client, ...@@ -549,13 +550,13 @@ static int sbs_get_battery_capacity(struct i2c_client *client,
union power_supply_propval *val) union power_supply_propval *val)
{ {
s32 ret; s32 ret;
enum sbs_battery_mode mode = BATTERY_MODE_WATTS; enum sbs_capacity_mode mode = CAPACITY_MODE_WATTS;
if (power_supply_is_amp_property(psp)) if (power_supply_is_amp_property(psp))
mode = BATTERY_MODE_AMPS; mode = CAPACITY_MODE_AMPS;
mode = sbs_set_battery_mode(client, mode); mode = sbs_set_capacity_mode(client, mode);
if (mode < 0) if ((int)mode < 0)
return mode; return mode;
ret = sbs_read_word_data(client, sbs_data[reg_offset].addr); ret = sbs_read_word_data(client, sbs_data[reg_offset].addr);
...@@ -564,7 +565,7 @@ static int sbs_get_battery_capacity(struct i2c_client *client, ...@@ -564,7 +565,7 @@ static int sbs_get_battery_capacity(struct i2c_client *client,
val->intval = ret; val->intval = ret;
ret = sbs_set_battery_mode(client, mode); ret = sbs_set_capacity_mode(client, mode);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -1001,6 +1002,6 @@ module_i2c_driver(sbs_battery_driver); ...@@ -1001,6 +1002,6 @@ module_i2c_driver(sbs_battery_driver);
MODULE_DESCRIPTION("SBS battery monitor driver"); MODULE_DESCRIPTION("SBS battery monitor driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
module_param(force_load, bool, S_IRUSR | S_IRGRP | S_IROTH); module_param(force_load, bool, 0444);
MODULE_PARM_DESC(force_load, MODULE_PARM_DESC(force_load,
"Attempt to load the driver even if no battery is connected"); "Attempt to load the driver even if no battery is connected");
...@@ -62,6 +62,8 @@ ...@@ -62,6 +62,8 @@
#define SC27XX_FGU_CUR_BASIC_ADC 8192 #define SC27XX_FGU_CUR_BASIC_ADC 8192
#define SC27XX_FGU_SAMPLE_HZ 2 #define SC27XX_FGU_SAMPLE_HZ 2
/* micro Ohms */
#define SC27XX_FGU_IDEAL_RESISTANCE 20000
/* /*
* struct sc27xx_fgu_data: describe the FGU device * struct sc27xx_fgu_data: describe the FGU device
...@@ -81,9 +83,12 @@ ...@@ -81,9 +83,12 @@
* @max_volt: the maximum constant input voltage in millivolt * @max_volt: the maximum constant input voltage in millivolt
* @min_volt: the minimum drained battery voltage in microvolt * @min_volt: the minimum drained battery voltage in microvolt
* @table_len: the capacity table length * @table_len: the capacity table length
* @resist_table_len: the resistance table length
* @cur_1000ma_adc: ADC value corresponding to 1000 mA * @cur_1000ma_adc: ADC value corresponding to 1000 mA
* @vol_1000mv_adc: ADC value corresponding to 1000 mV * @vol_1000mv_adc: ADC value corresponding to 1000 mV
* @calib_resist: the real resistance of coulomb counter chip in uOhm
* @cap_table: capacity table with corresponding ocv * @cap_table: capacity table with corresponding ocv
* @resist_table: resistance percent table with corresponding temperature
*/ */
struct sc27xx_fgu_data { struct sc27xx_fgu_data {
struct regmap *regmap; struct regmap *regmap;
...@@ -103,15 +108,19 @@ struct sc27xx_fgu_data { ...@@ -103,15 +108,19 @@ struct sc27xx_fgu_data {
int max_volt; int max_volt;
int min_volt; int min_volt;
int table_len; int table_len;
int resist_table_len;
int cur_1000ma_adc; int cur_1000ma_adc;
int vol_1000mv_adc; int vol_1000mv_adc;
int calib_resist;
struct power_supply_battery_ocv_table *cap_table; struct power_supply_battery_ocv_table *cap_table;
struct power_supply_resistance_temp_table *resist_table;
}; };
static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity); static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity);
static void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data, static void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data,
int cap, bool int_mode); int cap, bool int_mode);
static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap); static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap);
static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp);
static const char * const sc27xx_charger_supply_name[] = { static const char * const sc27xx_charger_supply_name[] = {
"sc2731_charger", "sc2731_charger",
...@@ -434,7 +443,7 @@ static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val) ...@@ -434,7 +443,7 @@ static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val)
static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val) static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val)
{ {
int vol, cur, ret; int vol, cur, ret, temp, resistance;
ret = sc27xx_fgu_get_vbat_vol(data, &vol); ret = sc27xx_fgu_get_vbat_vol(data, &vol);
if (ret) if (ret)
...@@ -444,8 +453,19 @@ static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val) ...@@ -444,8 +453,19 @@ static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val)
if (ret) if (ret)
return ret; return ret;
resistance = data->internal_resist;
if (data->resist_table_len > 0) {
ret = sc27xx_fgu_get_temp(data, &temp);
if (ret)
return ret;
resistance = power_supply_temp2resist_simple(data->resist_table,
data->resist_table_len, temp);
resistance = data->internal_resist * resistance / 100;
}
/* Return the battery OCV in micro volts. */ /* Return the battery OCV in micro volts. */
*val = vol * 1000 - cur * data->internal_resist; *val = vol * 1000 - cur * resistance;
return 0; return 0;
} }
...@@ -884,7 +904,9 @@ static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data) ...@@ -884,7 +904,9 @@ static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data)
*/ */
cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256; cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256;
data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42); data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42);
data->cur_1000ma_adc = data->vol_1000mv_adc * 4; data->cur_1000ma_adc =
DIV_ROUND_CLOSEST(data->vol_1000mv_adc * 4 * data->calib_resist,
SC27XX_FGU_IDEAL_RESISTANCE);
kfree(buf); kfree(buf);
return 0; return 0;
...@@ -929,6 +951,18 @@ static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) ...@@ -929,6 +951,18 @@ static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data)
if (!data->alarm_cap) if (!data->alarm_cap)
data->alarm_cap += 1; data->alarm_cap += 1;
data->resist_table_len = info.resist_table_size;
if (data->resist_table_len > 0) {
data->resist_table = devm_kmemdup(data->dev, info.resist_table,
data->resist_table_len *
sizeof(struct power_supply_resistance_temp_table),
GFP_KERNEL);
if (!data->resist_table) {
power_supply_put_battery_info(data->battery, &info);
return -ENOMEM;
}
}
power_supply_put_battery_info(data->battery, &info); power_supply_put_battery_info(data->battery, &info);
ret = sc27xx_fgu_calibration(data); ret = sc27xx_fgu_calibration(data);
...@@ -1051,6 +1085,15 @@ static int sc27xx_fgu_probe(struct platform_device *pdev) ...@@ -1051,6 +1085,15 @@ static int sc27xx_fgu_probe(struct platform_device *pdev)
return ret; return ret;
} }
ret = device_property_read_u32(&pdev->dev,
"sprd,calib-resistance-micro-ohms",
&data->calib_resist);
if (ret) {
dev_err(&pdev->dev,
"failed to get fgu calibration resistance\n");
return ret;
}
data->channel = devm_iio_channel_get(dev, "bat-temp"); data->channel = devm_iio_channel_get(dev, "bat-temp");
if (IS_ERR(data->channel)) { if (IS_ERR(data->channel)) {
dev_err(dev, "failed to get IIO channel\n"); dev_err(dev, "failed to get IIO channel\n");
......
...@@ -100,7 +100,9 @@ struct ucs1002_info { ...@@ -100,7 +100,9 @@ struct ucs1002_info {
struct i2c_client *client; struct i2c_client *client;
struct regmap *regmap; struct regmap *regmap;
struct regulator_desc *regulator_descriptor; struct regulator_desc *regulator_descriptor;
struct regulator_dev *rdev;
bool present; bool present;
bool output_disable;
}; };
static enum power_supply_property ucs1002_props[] = { static enum power_supply_property ucs1002_props[] = {
...@@ -233,6 +235,11 @@ static int ucs1002_get_max_current(struct ucs1002_info *info, ...@@ -233,6 +235,11 @@ static int ucs1002_get_max_current(struct ucs1002_info *info,
unsigned int reg; unsigned int reg;
int ret; int ret;
if (info->output_disable) {
val->intval = 0;
return 0;
}
ret = regmap_read(info->regmap, UCS1002_REG_ILIMIT, &reg); ret = regmap_read(info->regmap, UCS1002_REG_ILIMIT, &reg);
if (ret) if (ret)
return ret; return ret;
...@@ -247,6 +254,12 @@ static int ucs1002_set_max_current(struct ucs1002_info *info, u32 val) ...@@ -247,6 +254,12 @@ static int ucs1002_set_max_current(struct ucs1002_info *info, u32 val)
unsigned int reg; unsigned int reg;
int ret, idx; int ret, idx;
if (val == 0) {
info->output_disable = true;
regulator_disable_regmap(info->rdev);
return 0;
}
for (idx = 0; idx < ARRAY_SIZE(ucs1002_current_limit_uA); idx++) { for (idx = 0; idx < ARRAY_SIZE(ucs1002_current_limit_uA); idx++) {
if (val == ucs1002_current_limit_uA[idx]) if (val == ucs1002_current_limit_uA[idx])
break; break;
...@@ -270,6 +283,12 @@ static int ucs1002_set_max_current(struct ucs1002_info *info, u32 val) ...@@ -270,6 +283,12 @@ static int ucs1002_set_max_current(struct ucs1002_info *info, u32 val)
if (reg != idx) if (reg != idx)
return -EINVAL; return -EINVAL;
info->output_disable = false;
if (info->rdev && info->rdev->use_count &&
!regulator_is_enabled_regmap(info->rdev))
regulator_enable_regmap(info->rdev);
return 0; return 0;
} }
...@@ -470,9 +489,24 @@ static irqreturn_t ucs1002_alert_irq(int irq, void *data) ...@@ -470,9 +489,24 @@ static irqreturn_t ucs1002_alert_irq(int irq, void *data)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int ucs1002_regulator_enable(struct regulator_dev *rdev)
{
struct ucs1002_info *info = rdev_get_drvdata(rdev);
/*
* If the output is disabled due to 0 maximum current, just pretend the
* enable did work. The regulator will be enabled as soon as we get a
* a non-zero maximum current budget.
*/
if (info->output_disable)
return 0;
return regulator_enable_regmap(rdev);
}
static const struct regulator_ops ucs1002_regulator_ops = { static const struct regulator_ops ucs1002_regulator_ops = {
.is_enabled = regulator_is_enabled_regmap, .is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap, .enable = ucs1002_regulator_enable,
.disable = regulator_disable_regmap, .disable = regulator_disable_regmap,
}; };
...@@ -499,7 +533,6 @@ static int ucs1002_probe(struct i2c_client *client, ...@@ -499,7 +533,6 @@ static int ucs1002_probe(struct i2c_client *client,
}; };
struct regulator_config regulator_config = {}; struct regulator_config regulator_config = {};
int irq_a_det, irq_alert, ret; int irq_a_det, irq_alert, ret;
struct regulator_dev *rdev;
struct ucs1002_info *info; struct ucs1002_info *info;
unsigned int regval; unsigned int regval;
...@@ -589,10 +622,11 @@ static int ucs1002_probe(struct i2c_client *client, ...@@ -589,10 +622,11 @@ static int ucs1002_probe(struct i2c_client *client,
regulator_config.dev = dev; regulator_config.dev = dev;
regulator_config.of_node = dev->of_node; regulator_config.of_node = dev->of_node;
regulator_config.regmap = info->regmap; regulator_config.regmap = info->regmap;
regulator_config.driver_data = info;
rdev = devm_regulator_register(dev, info->regulator_descriptor, info->rdev = devm_regulator_register(dev, info->regulator_descriptor,
&regulator_config); &regulator_config);
ret = PTR_ERR_OR_ZERO(rdev); ret = PTR_ERR_OR_ZERO(info->rdev);
if (ret) { if (ret) {
dev_err(dev, "Failed to register VBUS regulator: %d\n", ret); dev_err(dev, "Failed to register VBUS regulator: %d\n", ret);
return ret; return ret;
......
...@@ -105,11 +105,56 @@ enum max17042_register { ...@@ -105,11 +105,56 @@ enum max17042_register {
MAX17042_OCV = 0xEE, MAX17042_OCV = 0xEE,
MAX17042_OCVInternal = 0xFB, MAX17042_OCVInternal = 0xFB, /* MAX17055 VFOCV */
MAX17042_VFSOC = 0xFF, MAX17042_VFSOC = 0xFF,
}; };
enum max17055_register {
MAX17055_QRes = 0x0C,
MAX17055_TTF = 0x20,
MAX17055_V_empty = 0x3A,
MAX17055_TIMER = 0x3E,
MAX17055_USER_MEM = 0x40,
MAX17055_RGAIN = 0x42,
MAX17055_ConvgCfg = 0x49,
MAX17055_VFRemCap = 0x4A,
MAX17055_STATUS2 = 0xB0,
MAX17055_POWER = 0xB1,
MAX17055_ID = 0xB2,
MAX17055_AvgPower = 0xB3,
MAX17055_IAlrtTh = 0xB4,
MAX17055_TTFCfg = 0xB5,
MAX17055_CVMixCap = 0xB6,
MAX17055_CVHalfTime = 0xB7,
MAX17055_CGTempCo = 0xB8,
MAX17055_Curve = 0xB9,
MAX17055_HibCfg = 0xBA,
MAX17055_Config2 = 0xBB,
MAX17055_VRipple = 0xBC,
MAX17055_RippleCfg = 0xBD,
MAX17055_TimerH = 0xBE,
MAX17055_RSense = 0xD0,
MAX17055_ScOcvLim = 0xD1,
MAX17055_SOCHold = 0xD3,
MAX17055_MaxPeakPwr = 0xD4,
MAX17055_SusPeakPwr = 0xD5,
MAX17055_PackResistance = 0xD6,
MAX17055_SysResistance = 0xD7,
MAX17055_MinSysV = 0xD8,
MAX17055_MPPCurrent = 0xD9,
MAX17055_SPPCurrent = 0xDA,
MAX17055_ModelCfg = 0xDB,
MAX17055_AtQResidual = 0xDC,
MAX17055_AtTTE = 0xDD,
MAX17055_AtAvSOC = 0xDE,
MAX17055_AtAvCap = 0xDF,
};
/* Registers specific to max17047/50 */ /* Registers specific to max17047/50 */
enum max17047_register { enum max17047_register {
MAX17047_QRTbl00 = 0x12, MAX17047_QRTbl00 = 0x12,
...@@ -125,6 +170,7 @@ enum max170xx_chip_type { ...@@ -125,6 +170,7 @@ enum max170xx_chip_type {
MAXIM_DEVICE_TYPE_MAX17042, MAXIM_DEVICE_TYPE_MAX17042,
MAXIM_DEVICE_TYPE_MAX17047, MAXIM_DEVICE_TYPE_MAX17047,
MAXIM_DEVICE_TYPE_MAX17050, MAXIM_DEVICE_TYPE_MAX17050,
MAXIM_DEVICE_TYPE_MAX17055,
MAXIM_DEVICE_TYPE_NUM MAXIM_DEVICE_TYPE_NUM
}; };
......
...@@ -325,6 +325,11 @@ struct power_supply_battery_ocv_table { ...@@ -325,6 +325,11 @@ struct power_supply_battery_ocv_table {
int capacity; /* percent */ int capacity; /* percent */
}; };
struct power_supply_resistance_temp_table {
int temp; /* celsius */
int resistance; /* internal resistance percent */
};
#define POWER_SUPPLY_OCV_TEMP_MAX 20 #define POWER_SUPPLY_OCV_TEMP_MAX 20
/* /*
...@@ -349,6 +354,8 @@ struct power_supply_battery_info { ...@@ -349,6 +354,8 @@ struct power_supply_battery_info {
int ocv_temp[POWER_SUPPLY_OCV_TEMP_MAX];/* celsius */ int ocv_temp[POWER_SUPPLY_OCV_TEMP_MAX];/* celsius */
struct power_supply_battery_ocv_table *ocv_table[POWER_SUPPLY_OCV_TEMP_MAX]; struct power_supply_battery_ocv_table *ocv_table[POWER_SUPPLY_OCV_TEMP_MAX];
int ocv_table_size[POWER_SUPPLY_OCV_TEMP_MAX]; int ocv_table_size[POWER_SUPPLY_OCV_TEMP_MAX];
struct power_supply_resistance_temp_table *resist_table;
int resist_table_size;
}; };
extern struct atomic_notifier_head power_supply_notifier; extern struct atomic_notifier_head power_supply_notifier;
...@@ -381,6 +388,9 @@ power_supply_find_ocv2cap_table(struct power_supply_battery_info *info, ...@@ -381,6 +388,9 @@ power_supply_find_ocv2cap_table(struct power_supply_battery_info *info,
int temp, int *table_len); int temp, int *table_len);
extern int power_supply_batinfo_ocv2cap(struct power_supply_battery_info *info, extern int power_supply_batinfo_ocv2cap(struct power_supply_battery_info *info,
int ocv, int temp); int ocv, int temp);
extern int
power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *table,
int table_len, int temp);
extern void power_supply_changed(struct power_supply *psy); extern void power_supply_changed(struct power_supply *psy);
extern int power_supply_am_i_supplied(struct power_supply *psy); extern int power_supply_am_i_supplied(struct power_supply *psy);
extern int power_supply_set_input_current_limit_from_supplier( extern int power_supply_set_input_current_limit_from_supplier(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment