mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-01-18 10:07:39 +07:00
power supply and reset changes for the v5.6 series
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 -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE72YNB0Y/i3JqeVQT2O7X88g7+poFAl4yC0MACgkQ2O7X88g7 +prb1Q/+P+tWsL7n4vDfFlvYjefH73SBHaG5rQzIJGJrRqs8/cfwR9eufc/sFwsz 8msvYstHhwQDFS6JbI67r2gOWM/RPdgz+xXfSXYSr1z9UNO6opaCB1U/hDxTOElF 7STas/2qBekM5I3wq7AlSbR20pbQti10GTgioyeTp+PoP6ubXPPMPXoqfAxI7zXJ 7R1FSxRVKqR8XXr6y/+hhD78+koke8dWU43ZPZDjaWGUyIhfF6dK4vemX7C1eJ3b OlgeZYkxC/Jmmp7eldMJrbD3NhQEqz+SrEGIXkEIJZtoYp/fwS+JsSXypVU8Ex0h s2JoTtaYQFKqfyZixldOaVFBGBQNuw98xSshFDzo9CC+adNK2C6hgkg4mva2O5cR VMvlPW5nPbCMrEcE2glZ0XN9bG/jsdUlFTNEKHRBF5Lat6nfSFaZRKwv/Pjl7Bu0 MpFJz6ml3z2mSjmjl3TQkFugtILu8+gRogW45uLasAsv7s6ZyM/6l/GvIdx/bGdt NdLHe4ZFxa00g3ltvaX+3OdGhHtVJqUbuyitrN9sqZfSNYyy+kuEP0nrO+363l4X 3G1DyD827zCals+SOrdNwF3mQXqFBeE94pwDjN9ECoFuQd6UxApJ5GXnlUrYAMfw qOqXIzkcPuAiXIxXGPIIG4QH0axD6T+H1r181UviWY1ldgQCVoc= =tr2N -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
4cadc60d6b
@ -189,7 +189,8 @@ Description:
|
||||
Access: Read
|
||||
Valid values: "Unknown", "Good", "Overheat", "Dead",
|
||||
"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
|
||||
Date: June 2017
|
||||
|
@ -35,6 +35,10 @@ Optional Properties:
|
||||
for each of the battery capacity lookup table. The first temperature value
|
||||
specifies the OCV table 0, and the second temperature value specifies the
|
||||
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
|
||||
elements in enum power_supply_property, defined in
|
||||
@ -61,6 +65,7 @@ Example:
|
||||
ocv-capacity-table-0 = <4185000 100>, <4113000 95>, <4066000 90>, ...;
|
||||
ocv-capacity-table-1 = <4200000 100>, <4185000 95>, <4113000 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 {
|
||||
|
@ -1,11 +1,14 @@
|
||||
Binding for TI bq25890 Li-Ion Charger
|
||||
|
||||
This driver will support the bq25896 and the bq25890. There are other ICs
|
||||
in the same family but those have not been tested.
|
||||
This driver will support the bq25892, the bq25896 and the bq25890. There are
|
||||
other ICs in the same family but those have not been tested.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should contain one of the following:
|
||||
* "ti,bq25890"
|
||||
* "ti,bq25892"
|
||||
* "ti,bq25895"
|
||||
* "ti,bq25896"
|
||||
- reg: integer, i2c address of the device.
|
||||
- ti,battery-regulation-voltage: integer, maximum charging voltage (in uV);
|
||||
- ti,charge-current: integer, maximum charging current (in uA);
|
||||
|
@ -0,0 +1,33 @@
|
||||
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
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Required properties :
|
||||
- compatible : "maxim,max17042"
|
||||
- compatible : one of the following
|
||||
* "maxim,max17042"
|
||||
* "maxim,max17047"
|
||||
* "maxim,max17050"
|
||||
* "maxim,max17055"
|
||||
|
||||
Optional properties :
|
||||
- maxim,rsns-microohm : Resistance of rsns resistor in micro Ohms
|
||||
|
@ -13,6 +13,8 @@ Required properties:
|
||||
- io-channel-names: Should be "bat-temp" or "charge-vol".
|
||||
- nvmem-cells: A phandle to the calibration cells provided by eFuse device.
|
||||
- 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.
|
||||
See Documentation/devicetree/bindings/power/supply/battery.txt
|
||||
|
||||
@ -52,5 +54,6 @@ Example:
|
||||
nvmem-cells = <&fgu_calib>;
|
||||
nvmem-cell-names = "fgu_calib";
|
||||
monitored-battery = <&bat>;
|
||||
sprd,calib-resistance-micro-ohms = <21500>;
|
||||
};
|
||||
};
|
||||
|
@ -141,14 +141,14 @@ config POWER_RESET_LTC2952
|
||||
down via the LTC2952. Bindings are made in the device tree.
|
||||
|
||||
config POWER_RESET_MT6323
|
||||
bool "MediaTek MT6323 power-off driver"
|
||||
depends on MFD_MT6397
|
||||
help
|
||||
The power-off driver is responsible for externally shutdown down
|
||||
the power of a remote MediaTek SoC MT6323 is connected to through
|
||||
controlling a tiny circuit BBPU inside MT6323 RTC.
|
||||
bool "MediaTek MT6323 power-off driver"
|
||||
depends on MFD_MT6397
|
||||
help
|
||||
The power-off driver is responsible for externally shutdown down
|
||||
the power of a remote MediaTek SoC MT6323 is connected to through
|
||||
controlling a tiny circuit BBPU inside MT6323 RTC.
|
||||
|
||||
Say Y if you have a board where MT6323 could be found.
|
||||
Say Y if you have a board where MT6323 could be found.
|
||||
|
||||
config POWER_RESET_QNAP
|
||||
bool "QNAP power-off driver"
|
||||
|
@ -66,7 +66,7 @@
|
||||
|
||||
#define SHDW_CFG_NOT_USED (32)
|
||||
|
||||
struct shdwc_config {
|
||||
struct shdwc_reg_config {
|
||||
u8 wkup_pin_input;
|
||||
u8 mr_rtcwk_shift;
|
||||
u8 mr_rttwk_shift;
|
||||
@ -74,8 +74,17 @@ struct shdwc_config {
|
||||
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 {
|
||||
const struct shdwc_config *cfg;
|
||||
const struct reg_config *rcfg;
|
||||
struct clk *sclk;
|
||||
void __iomem *shdwc_base;
|
||||
void __iomem *mpddrc_base;
|
||||
@ -95,6 +104,7 @@ static const unsigned long long sdwc_dbc_period[] = {
|
||||
static void __init at91_wakeup_status(struct platform_device *pdev)
|
||||
{
|
||||
struct shdwc *shdw = platform_get_drvdata(pdev);
|
||||
const struct reg_config *rcfg = shdw->rcfg;
|
||||
u32 reg;
|
||||
char *reason = "unknown";
|
||||
|
||||
@ -106,11 +116,11 @@ static void __init at91_wakeup_status(struct platform_device *pdev)
|
||||
if (!reg)
|
||||
return;
|
||||
|
||||
if (SHDW_WK_PIN(reg, shdw->cfg))
|
||||
if (SHDW_WK_PIN(reg, &rcfg->shdwc))
|
||||
reason = "WKUP pin";
|
||||
else if (SHDW_RTCWK(reg, shdw->cfg))
|
||||
else if (SHDW_RTCWK(reg, &rcfg->shdwc))
|
||||
reason = "RTC";
|
||||
else if (SHDW_RTTWK(reg, shdw->cfg))
|
||||
else if (SHDW_RTTWK(reg, &rcfg->shdwc))
|
||||
reason = "RTT";
|
||||
|
||||
pr_info("AT91: Wake-Up source: %s\n", reason);
|
||||
@ -131,9 +141,9 @@ static void at91_poweroff(void)
|
||||
" str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
|
||||
|
||||
/* 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"
|
||||
" str r6, [%4, #" __stringify(AT91_PMC_MCKR) "]\n\t"
|
||||
" str r6, [%4, %5]\n\t"
|
||||
/* Wait for clock switch. */
|
||||
"2: ldr r6, [%4, #" __stringify(AT91_PMC_SR) "]\n\t"
|
||||
" tst r6, #" __stringify(AT91_PMC_MCKRDY) "\n\t"
|
||||
@ -148,7 +158,8 @@ static void at91_poweroff(void)
|
||||
"r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
|
||||
"r" (at91_shdwc->shdwc_base),
|
||||
"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");
|
||||
}
|
||||
|
||||
@ -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)
|
||||
{
|
||||
struct shdwc *shdw = platform_get_drvdata(pdev);
|
||||
const struct reg_config *rcfg = shdw->rcfg;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
u32 mode = 0, tmp, input;
|
||||
|
||||
@ -227,10 +239,10 @@ static void at91_shdwc_dt_configure(struct platform_device *pdev)
|
||||
mode |= AT91_SHDW_WKUPDBC(at91_shdwc_debouncer_value(pdev, tmp));
|
||||
|
||||
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"))
|
||||
mode |= SHDW_RTTWKEN(shdw->cfg);
|
||||
mode |= SHDW_RTTWKEN(&rcfg->shdwc);
|
||||
|
||||
dev_dbg(&pdev->dev, "%s: mode = %#x\n", __func__, mode);
|
||||
writel(mode, shdw->shdwc_base + AT91_SHDW_MR);
|
||||
@ -239,30 +251,40 @@ static void at91_shdwc_dt_configure(struct platform_device *pdev)
|
||||
writel(input, shdw->shdwc_base + AT91_SHDW_WUIR);
|
||||
}
|
||||
|
||||
static const struct shdwc_config sama5d2_shdwc_config = {
|
||||
.wkup_pin_input = 0,
|
||||
.mr_rtcwk_shift = 17,
|
||||
.mr_rttwk_shift = SHDW_CFG_NOT_USED,
|
||||
.sr_rtcwk_shift = 5,
|
||||
.sr_rttwk_shift = SHDW_CFG_NOT_USED,
|
||||
static const struct reg_config sama5d2_reg_config = {
|
||||
.shdwc = {
|
||||
.wkup_pin_input = 0,
|
||||
.mr_rtcwk_shift = 17,
|
||||
.mr_rttwk_shift = SHDW_CFG_NOT_USED,
|
||||
.sr_rtcwk_shift = 5,
|
||||
.sr_rttwk_shift = SHDW_CFG_NOT_USED,
|
||||
},
|
||||
.pmc = {
|
||||
.mckr = 0x30,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct shdwc_config sam9x60_shdwc_config = {
|
||||
.wkup_pin_input = 0,
|
||||
.mr_rtcwk_shift = 17,
|
||||
.mr_rttwk_shift = 16,
|
||||
.sr_rtcwk_shift = 5,
|
||||
.sr_rttwk_shift = 4,
|
||||
static const struct reg_config sam9x60_reg_config = {
|
||||
.shdwc = {
|
||||
.wkup_pin_input = 0,
|
||||
.mr_rtcwk_shift = 17,
|
||||
.mr_rttwk_shift = 16,
|
||||
.sr_rtcwk_shift = 5,
|
||||
.sr_rttwk_shift = 4,
|
||||
},
|
||||
.pmc = {
|
||||
.mckr = 0x28,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct of_device_id at91_shdwc_of_match[] = {
|
||||
{
|
||||
.compatible = "atmel,sama5d2-shdwc",
|
||||
.data = &sama5d2_shdwc_config,
|
||||
.data = &sama5d2_reg_config,
|
||||
},
|
||||
{
|
||||
.compatible = "microchip,sam9x60-shdwc",
|
||||
.data = &sam9x60_shdwc_config,
|
||||
.data = &sam9x60_reg_config,
|
||||
}, {
|
||||
/*sentinel*/
|
||||
}
|
||||
@ -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);
|
||||
at91_shdwc->cfg = match->data;
|
||||
at91_shdwc->rcfg = match->data;
|
||||
|
||||
at91_shdwc->sclk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(at91_shdwc->sclk))
|
||||
|
@ -64,9 +64,11 @@ static int gpio_restart_probe(struct platform_device *pdev)
|
||||
|
||||
gpio_restart->reset_gpio = devm_gpiod_get(&pdev->dev, NULL,
|
||||
open_source ? GPIOD_IN : GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio_restart->reset_gpio)) {
|
||||
dev_err(&pdev->dev, "Could not get reset GPIO\n");
|
||||
return PTR_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");
|
||||
return ret;
|
||||
}
|
||||
|
||||
gpio_restart->restart_handler.notifier_call = gpio_restart_notify;
|
||||
|
@ -73,10 +73,10 @@ config WM831X_POWER
|
||||
provided by Wolfson Microelectronics WM831x PMICs.
|
||||
|
||||
config WM8350_POWER
|
||||
tristate "WM8350 PMU support"
|
||||
depends on MFD_WM8350
|
||||
help
|
||||
Say Y here to enable support for the power management unit
|
||||
tristate "WM8350 PMU support"
|
||||
depends on MFD_WM8350
|
||||
help
|
||||
Say Y here to enable support for the power management unit
|
||||
provided by the Wolfson Microelectronics WM8350 PMIC.
|
||||
|
||||
config TEST_POWER
|
||||
@ -209,16 +209,16 @@ config BATTERY_WM97XX
|
||||
Say Y to enable support for battery measured by WM97xx aux port.
|
||||
|
||||
config BATTERY_SBS
|
||||
tristate "SBS Compliant gas gauge"
|
||||
depends on I2C
|
||||
help
|
||||
tristate "SBS Compliant gas gauge"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y to include support for SBS battery driver for SBS-compliant
|
||||
gas gauges.
|
||||
|
||||
config CHARGER_SBS
|
||||
tristate "SBS Compliant charger"
|
||||
depends on I2C
|
||||
help
|
||||
tristate "SBS Compliant charger"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y to include support for SBS compliant battery chargers.
|
||||
|
||||
config MANAGER_SBS
|
||||
@ -484,11 +484,11 @@ config CHARGER_MANAGER
|
||||
depends on REGULATOR
|
||||
select EXTCON
|
||||
help
|
||||
Say Y to enable charger-manager support, which allows multiple
|
||||
chargers attached to a battery and multiple batteries attached to a
|
||||
system. The charger-manager also can monitor charging status in
|
||||
runtime and in suspend-to-RAM by waking up the system periodically
|
||||
with help of suspend_again support.
|
||||
Say Y to enable charger-manager support, which allows multiple
|
||||
chargers attached to a battery and multiple batteries attached to a
|
||||
system. The charger-manager also can monitor charging status in
|
||||
runtime and in suspend-to-RAM by waking up the system periodically
|
||||
with help of suspend_again support.
|
||||
|
||||
config CHARGER_LT3651
|
||||
tristate "Analog Devices LT3651 charger"
|
||||
|
@ -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;
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
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",
|
||||
@ -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;
|
||||
ret = -EPERM;
|
||||
break;
|
||||
};
|
||||
}
|
||||
di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max;
|
||||
return ret;
|
||||
}
|
||||
@ -2427,7 +2427,7 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
|
||||
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2221,10 +2221,10 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
|
||||
ab8500_fg_update_cap_scalers(di);
|
||||
queue_work(di->fg_wq, &di->fg_work);
|
||||
break;
|
||||
};
|
||||
}
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
||||
switch (ext->desc->type) {
|
||||
@ -2331,7 +2331,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
|
||||
if (ret) {
|
||||
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_MAX_TIME_REG\n", __func__);
|
||||
goto out;
|
||||
};
|
||||
}
|
||||
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
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)
|
||||
if (ret) {
|
||||
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_FLAG_TIME_REG\n", __func__);
|
||||
goto out;
|
||||
};
|
||||
}
|
||||
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
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)
|
||||
if (ret) {
|
||||
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_RESTART_REG\n", __func__);
|
||||
goto out;
|
||||
};
|
||||
}
|
||||
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
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)
|
||||
if (ret) {
|
||||
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_DEBOUNCE_REG\n", __func__);
|
||||
goto out;
|
||||
};
|
||||
}
|
||||
|
||||
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
|
||||
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)
|
||||
if (ret) {
|
||||
dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_CTL_STATUS_REG\n", __func__);
|
||||
goto out;
|
||||
};
|
||||
}
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
|
@ -1823,7 +1823,7 @@ static ssize_t abx500_chargalg_en_store(struct abx500_chargalg *di,
|
||||
"Enter 0. Disable AC/USB Charging\n"
|
||||
"1. Enable AC charging\n"
|
||||
"2. Enable USB Charging\n");
|
||||
};
|
||||
}
|
||||
return strlen(buf);
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
@ -23,6 +24,9 @@
|
||||
#define AXP20X_PWR_STATUS_ACIN_PRESENT BIT(7)
|
||||
#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_UV_TO_BIT(x) ((((x) / 100000) - 40) << 3)
|
||||
#define AXP813_VHOLD_REG_TO_UV(x) \
|
||||
@ -40,6 +44,9 @@ struct axp20x_ac_power {
|
||||
struct power_supply *supply;
|
||||
struct iio_channel *acin_v;
|
||||
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)
|
||||
@ -86,6 +93,17 @@ static int axp20x_ac_power_get_property(struct power_supply *psy,
|
||||
return ret;
|
||||
|
||||
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,
|
||||
®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val->intval = !!(reg & AXP813_ACIN_PATH_SEL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
@ -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);
|
||||
|
||||
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:
|
||||
if (val->intval < 4000000 || val->intval > 4700000)
|
||||
return -EINVAL;
|
||||
@ -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,
|
||||
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;
|
||||
}
|
||||
|
||||
@ -221,34 +245,86 @@ static const struct power_supply_desc axp813_ac_power_desc = {
|
||||
.set_property = axp813_ac_power_set_property,
|
||||
};
|
||||
|
||||
static const char * const axp20x_irq_names[] = {
|
||||
"ACIN_PLUGIN",
|
||||
"ACIN_REMOVAL",
|
||||
};
|
||||
|
||||
struct axp_data {
|
||||
const struct power_supply_desc *power_desc;
|
||||
const char * const *irq_names;
|
||||
unsigned int num_irq_names;
|
||||
bool acin_adc;
|
||||
bool acin_path_sel;
|
||||
};
|
||||
|
||||
static const struct axp_data axp20x_data = {
|
||||
.power_desc = &axp20x_ac_power_desc,
|
||||
.acin_adc = true,
|
||||
.power_desc = &axp20x_ac_power_desc,
|
||||
.irq_names = axp20x_irq_names,
|
||||
.num_irq_names = ARRAY_SIZE(axp20x_irq_names),
|
||||
.acin_adc = true,
|
||||
.acin_path_sel = false,
|
||||
};
|
||||
|
||||
static const struct axp_data axp22x_data = {
|
||||
.power_desc = &axp22x_ac_power_desc,
|
||||
.acin_adc = false,
|
||||
.power_desc = &axp22x_ac_power_desc,
|
||||
.irq_names = axp20x_irq_names,
|
||||
.num_irq_names = ARRAY_SIZE(axp20x_irq_names),
|
||||
.acin_adc = false,
|
||||
.acin_path_sel = false,
|
||||
};
|
||||
|
||||
static const struct axp_data axp813_data = {
|
||||
.power_desc = &axp813_ac_power_desc,
|
||||
.acin_adc = false,
|
||||
.power_desc = &axp813_ac_power_desc,
|
||||
.irq_names = axp20x_irq_names,
|
||||
.num_irq_names = ARRAY_SIZE(axp20x_irq_names),
|
||||
.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)
|
||||
{
|
||||
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct axp20x_ac_power *power;
|
||||
const struct axp_data *axp_data;
|
||||
static const char * const irq_names[] = { "ACIN_PLUGIN", "ACIN_REMOVAL",
|
||||
NULL };
|
||||
int i, irq, ret;
|
||||
|
||||
if (!of_device_is_available(pdev->dev.of_node))
|
||||
@ -259,12 +335,14 @@ static int axp20x_ac_power_probe(struct platform_device *pdev)
|
||||
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)
|
||||
return -ENOMEM;
|
||||
|
||||
axp_data = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
if (axp_data->acin_adc) {
|
||||
power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v");
|
||||
if (IS_ERR(power->acin_v)) {
|
||||
@ -282,6 +360,8 @@ static int axp20x_ac_power_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -295,20 +375,22 @@ static int axp20x_ac_power_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(power->supply);
|
||||
|
||||
/* Request irqs after registering, as irqs may trigger immediately */
|
||||
for (i = 0; irq_names[i]; i++) {
|
||||
irq = platform_get_irq_byname(pdev, irq_names[i]);
|
||||
for (i = 0; i < axp_data->num_irq_names; i++) {
|
||||
irq = platform_get_irq_byname(pdev, axp_data->irq_names[i]);
|
||||
if (irq < 0) {
|
||||
dev_warn(&pdev->dev, "No IRQ for %s: %d\n",
|
||||
irq_names[i], irq);
|
||||
continue;
|
||||
dev_err(&pdev->dev, "No IRQ for %s: %d\n",
|
||||
axp_data->irq_names[i], irq);
|
||||
return irq;
|
||||
}
|
||||
irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
|
||||
ret = devm_request_any_context_irq(&pdev->dev, irq,
|
||||
power->irqs[i] = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
|
||||
ret = devm_request_any_context_irq(&pdev->dev, power->irqs[i],
|
||||
axp20x_ac_power_irq, 0,
|
||||
DRVNAME, power);
|
||||
if (ret < 0)
|
||||
dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n",
|
||||
irq_names[i], ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Error requesting %s IRQ: %d\n",
|
||||
axp_data->irq_names[i], ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -331,8 +413,9 @@ MODULE_DEVICE_TABLE(of, axp20x_ac_power_match);
|
||||
static struct platform_driver axp20x_ac_power_driver = {
|
||||
.probe = axp20x_ac_power_probe,
|
||||
.driver = {
|
||||
.name = DRVNAME,
|
||||
.of_match_table = axp20x_ac_power_match,
|
||||
.name = DRVNAME,
|
||||
.of_match_table = axp20x_ac_power_match,
|
||||
.pm = &axp20x_ac_power_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
@ -29,6 +30,9 @@
|
||||
|
||||
#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_MASK GENMASK(5, 3)
|
||||
#define AXP20X_VBUS_VHOLD_OFFSET 3
|
||||
@ -57,7 +61,6 @@
|
||||
#define DEBOUNCE_TIME msecs_to_jiffies(50)
|
||||
|
||||
struct axp20x_usb_power {
|
||||
struct device_node *np;
|
||||
struct regmap *regmap;
|
||||
struct power_supply *supply;
|
||||
enum axp20x_variants axp20x_id;
|
||||
@ -65,14 +68,32 @@ struct axp20x_usb_power {
|
||||
struct iio_channel *vbus_i;
|
||||
struct delayed_work vbus_detect;
|
||||
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)
|
||||
{
|
||||
struct axp20x_usb_power *power = devid;
|
||||
|
||||
power_supply_changed(power->supply);
|
||||
|
||||
mod_delayed_work(system_wq, &power->vbus_detect, DEBOUNCE_TIME);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -92,17 +113,11 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work)
|
||||
power_supply_changed(power->supply);
|
||||
|
||||
power->old_status = val;
|
||||
power->online = val & AXP20X_PWR_STATUS_VBUS_USED;
|
||||
|
||||
out:
|
||||
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;
|
||||
if (axp20x_usb_vbus_needs_polling(power))
|
||||
mod_delayed_work(system_wq, &power->vbus_detect, DEBOUNCE_TIME);
|
||||
}
|
||||
|
||||
static int axp20x_get_current_max(struct axp20x_usb_power *power, int *val)
|
||||
@ -264,6 +279,16 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
|
||||
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,
|
||||
int intval)
|
||||
{
|
||||
@ -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);
|
||||
|
||||
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:
|
||||
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,
|
||||
static int axp20x_usb_power_prop_writeable(struct power_supply *psy,
|
||||
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 ||
|
||||
psp == POWER_SUPPLY_PROP_CURRENT_MAX;
|
||||
}
|
||||
@ -406,6 +448,92 @@ static const struct power_supply_desc axp22x_usb_power_desc = {
|
||||
.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,
|
||||
struct axp20x_usb_power *power)
|
||||
{
|
||||
@ -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 power_supply_config psy_cfg = {};
|
||||
struct axp20x_usb_power *power;
|
||||
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 };
|
||||
const char * const *irq_names;
|
||||
const struct power_supply_desc *usb_power_desc;
|
||||
const struct axp_data *axp_data;
|
||||
int i, irq, ret;
|
||||
|
||||
if (!of_device_is_available(pdev->dev.of_node))
|
||||
@ -457,16 +580,19 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
|
||||
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)
|
||||
return -ENOMEM;
|
||||
|
||||
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->num_irqs = axp_data->num_irq_names;
|
||||
|
||||
if (power->axp20x_id == AXP202_ID) {
|
||||
/* Enable vbus valid checking */
|
||||
@ -483,18 +609,6 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
|
||||
|
||||
if (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) {
|
||||
@ -506,25 +620,29 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
|
||||
psy_cfg.of_node = pdev->dev.of_node;
|
||||
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);
|
||||
if (IS_ERR(power->supply))
|
||||
return PTR_ERR(power->supply);
|
||||
|
||||
/* Request irqs after registering, as irqs may trigger immediately */
|
||||
for (i = 0; irq_names[i]; i++) {
|
||||
irq = platform_get_irq_byname(pdev, irq_names[i]);
|
||||
for (i = 0; i < axp_data->num_irq_names; i++) {
|
||||
irq = platform_get_irq_byname(pdev, axp_data->irq_names[i]);
|
||||
if (irq < 0) {
|
||||
dev_warn(&pdev->dev, "No IRQ for %s: %d\n",
|
||||
irq_names[i], irq);
|
||||
continue;
|
||||
dev_err(&pdev->dev, "No IRQ for %s: %d\n",
|
||||
axp_data->irq_names[i], irq);
|
||||
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);
|
||||
@ -546,16 +664,16 @@ static int axp20x_usb_power_remove(struct platform_device *pdev)
|
||||
static const struct of_device_id axp20x_usb_power_match[] = {
|
||||
{
|
||||
.compatible = "x-powers,axp202-usb-power-supply",
|
||||
.data = (void *)AXP202_ID,
|
||||
.data = &axp202_data,
|
||||
}, {
|
||||
.compatible = "x-powers,axp221-usb-power-supply",
|
||||
.data = (void *)AXP221_ID,
|
||||
.data = &axp221_data,
|
||||
}, {
|
||||
.compatible = "x-powers,axp223-usb-power-supply",
|
||||
.data = (void *)AXP223_ID,
|
||||
.data = &axp223_data,
|
||||
}, {
|
||||
.compatible = "x-powers,axp813-usb-power-supply",
|
||||
.data = (void *)AXP813_ID,
|
||||
.data = &axp813_data,
|
||||
}, { /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, axp20x_usb_power_match);
|
||||
@ -564,8 +682,9 @@ static struct platform_driver axp20x_usb_power_driver = {
|
||||
.probe = axp20x_usb_power_probe,
|
||||
.remove = axp20x_usb_power_remove,
|
||||
.driver = {
|
||||
.name = DRVNAME,
|
||||
.of_match_table = axp20x_usb_power_match,
|
||||
.name = DRVNAME,
|
||||
.of_match_table = axp20x_usb_power_match,
|
||||
.pm = &axp20x_usb_power_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -25,12 +25,20 @@
|
||||
#define BQ25895_ID 7
|
||||
#define BQ25896_ID 0
|
||||
|
||||
enum bq25890_chip_version {
|
||||
BQ25890,
|
||||
BQ25892,
|
||||
BQ25895,
|
||||
BQ25896,
|
||||
};
|
||||
|
||||
enum bq25890_fields {
|
||||
F_EN_HIZ, F_EN_ILIM, F_IILIM, /* Reg00 */
|
||||
F_BHOT, F_BCOLD, F_VINDPM_OFS, /* Reg01 */
|
||||
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_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_IPRECHG, F_ITERM, /* Reg05 */
|
||||
F_VREG, F_BATLOWV, F_VRECHG, /* Reg06 */
|
||||
@ -39,8 +47,9 @@ enum bq25890_fields {
|
||||
F_BATCMP, F_VCLAMP, F_TREG, /* Reg08 */
|
||||
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_BOOSTV, F_BOOSTI, /* Reg0A */
|
||||
F_VBUS_STAT, F_CHG_STAT, F_PG_STAT, F_SDP_STAT, F_VSYS_STAT, /* Reg0B */
|
||||
F_BOOSTV, F_PFM_OTG_DIS, F_BOOSTI, /* Reg0A */
|
||||
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_NTC_FAULT, /* Reg0C */
|
||||
F_FORCE_VINDPM, F_VINDPM, /* Reg0D */
|
||||
@ -91,7 +100,7 @@ struct bq25890_device {
|
||||
struct regmap *rmap;
|
||||
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_state state;
|
||||
|
||||
@ -111,8 +120,7 @@ static const struct regmap_access_table bq25890_writeable_regs = {
|
||||
static const struct regmap_range bq25890_volatile_reg_ranges[] = {
|
||||
regmap_reg_range(0x00, 0x00),
|
||||
regmap_reg_range(0x09, 0x09),
|
||||
regmap_reg_range(0x0b, 0x0c),
|
||||
regmap_reg_range(0x0e, 0x14),
|
||||
regmap_reg_range(0x0b, 0x14),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table bq25890_volatile_regs = {
|
||||
@ -155,7 +163,7 @@ static const struct reg_field bq25890_reg_fields[] = {
|
||||
[F_OTG_CFG] = REG_FIELD(0x03, 5, 5),
|
||||
[F_CHG_CFG] = REG_FIELD(0x03, 4, 4),
|
||||
[F_SYSVMIN] = REG_FIELD(0x03, 1, 3),
|
||||
/* MIN_VBAT_SEL on BQ25896 */
|
||||
[F_MIN_VBAT_SEL] = REG_FIELD(0x03, 0, 0), // BQ25896 only
|
||||
/* REG04 */
|
||||
[F_PUMPX_EN] = REG_FIELD(0x04, 7, 7),
|
||||
[F_ICHG] = REG_FIELD(0x04, 0, 6),
|
||||
@ -188,8 +196,8 @@ static const struct reg_field bq25890_reg_fields[] = {
|
||||
[F_PUMPX_DN] = REG_FIELD(0x09, 0, 0),
|
||||
/* REG0A */
|
||||
[F_BOOSTV] = REG_FIELD(0x0A, 4, 7),
|
||||
/* PFM_OTG_DIS 3 on BQ25896 */
|
||||
[F_BOOSTI] = REG_FIELD(0x0A, 0, 2), // reserved on BQ25895
|
||||
[F_PFM_OTG_DIS] = REG_FIELD(0x0A, 3, 3), // BQ25896 only
|
||||
/* REG0B */
|
||||
[F_VBUS_STAT] = REG_FIELD(0x0B, 5, 7),
|
||||
[F_CHG_STAT] = REG_FIELD(0x0B, 3, 4),
|
||||
@ -275,6 +283,7 @@ static const union {
|
||||
struct bq25890_lookup lt;
|
||||
} bq25890_tables[] = {
|
||||
/* range tables */
|
||||
/* TODO: BQ25896 has max ICHG 3008 mA */
|
||||
[TBL_ICHG] = { .rt = {0, 5056000, 64000} }, /* uA */
|
||||
[TBL_ITERM] = { .rt = {64000, 1024000, 64000} }, /* uA */
|
||||
[TBL_VREG] = { .rt = {3840000, 4608000, 16000} }, /* uV */
|
||||
@ -391,11 +400,13 @@ static int bq25890_power_supply_get_property(struct power_supply *psy,
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
if (bq->chip_id == BQ25890_ID)
|
||||
if (bq->chip_version == 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";
|
||||
else if (bq->chip_id == BQ25896_ID)
|
||||
else if (bq->chip_version == BQ25896)
|
||||
val->strval = "BQ25896";
|
||||
else
|
||||
val->strval = "UNKNOWN";
|
||||
@ -741,6 +752,56 @@ static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val,
|
||||
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)
|
||||
{
|
||||
struct gpio_desc *irq;
|
||||
@ -859,16 +920,10 @@ static int bq25890_probe(struct i2c_client *client,
|
||||
|
||||
i2c_set_clientdata(client, bq);
|
||||
|
||||
bq->chip_id = bq25890_field_read(bq, F_PN);
|
||||
if (bq->chip_id < 0) {
|
||||
dev_err(dev, "Cannot read chip ID.\n");
|
||||
return bq->chip_id;
|
||||
}
|
||||
|
||||
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;
|
||||
ret = bq25890_get_chip_version(bq);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot read chip ID or unknown chip.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!dev->platform_data) {
|
||||
@ -986,12 +1041,18 @@ static const struct dev_pm_ops bq25890_pm = {
|
||||
|
||||
static const struct i2c_device_id bq25890_i2c_ids[] = {
|
||||
{ "bq25890", 0 },
|
||||
{ "bq25892", 0 },
|
||||
{ "bq25895", 0 },
|
||||
{ "bq25896", 0 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bq25890_i2c_ids);
|
||||
|
||||
static const struct of_device_id bq25890_of_match[] = {
|
||||
{ .compatible = "ti,bq25890", },
|
||||
{ .compatible = "ti,bq25892", },
|
||||
{ .compatible = "ti,bq25895", },
|
||||
{ .compatible = "ti,bq25896", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bq25890_of_match);
|
||||
|
@ -132,11 +132,8 @@ static int cros_usbpd_charger_get_num_ports(struct charger_data *charger)
|
||||
ret = cros_usbpd_charger_ec_command(charger, 0,
|
||||
EC_CMD_CHARGE_PORT_COUNT,
|
||||
NULL, 0, &resp, sizeof(resp));
|
||||
if (ret < 0) {
|
||||
dev_err(charger->dev,
|
||||
"Unable to get the number of ports (err:0x%x)\n", ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return resp.port_count;
|
||||
}
|
||||
@ -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,
|
||||
NULL, 0, &resp, sizeof(resp));
|
||||
if (ret < 0) {
|
||||
dev_err(charger->dev,
|
||||
"Unable to get the number or ports (err:0x%x)\n", ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return resp.num_ports;
|
||||
}
|
||||
|
@ -100,10 +100,17 @@ static int ingenic_battery_set_scale(struct ingenic_battery *bat)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return iio_write_channel_attribute(bat->channel,
|
||||
scale_raw[best_idx],
|
||||
scale_raw[best_idx + 1],
|
||||
IIO_CHAN_INFO_SCALE);
|
||||
/* 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 + 1],
|
||||
IIO_CHAN_INFO_SCALE);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum power_supply_property ingenic_battery_properties[] = {
|
||||
|
@ -149,7 +149,7 @@ static int micro_batt_get_property(struct power_supply *b,
|
||||
default:
|
||||
val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
|
||||
break;
|
||||
};
|
||||
}
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
val->intval = get_status(b);
|
||||
@ -168,7 +168,7 @@ static int micro_batt_get_property(struct power_supply *b,
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
};
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -185,7 +185,7 @@ static int micro_ac_get_property(struct power_supply *b,
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
};
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -449,7 +449,7 @@ static int ltc294x_i2c_remove(struct i2c_client *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);
|
||||
return 0;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/max17040_battery.h>
|
||||
#include <linux/slab.h>
|
||||
@ -28,6 +29,9 @@
|
||||
#define MAX17040_DELAY 1000
|
||||
#define MAX17040_BATTERY_FULL 95
|
||||
|
||||
#define MAX17040_ATHD_MASK 0xFFC0
|
||||
#define MAX17040_ATHD_DEFAULT_POWER_UP 4
|
||||
|
||||
struct max17040_chip {
|
||||
struct i2c_client *client;
|
||||
struct delayed_work work;
|
||||
@ -42,6 +46,8 @@ struct max17040_chip {
|
||||
int soc;
|
||||
/* State Of Charge */
|
||||
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,
|
||||
@ -98,6 +104,21 @@ static void max17040_reset(struct i2c_client *client)
|
||||
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)
|
||||
{
|
||||
struct max17040_chip *chip = i2c_get_clientdata(client);
|
||||
@ -160,21 +181,81 @@ static void max17040_get_status(struct i2c_client *client)
|
||||
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)
|
||||
{
|
||||
struct max17040_chip *chip;
|
||||
int last_soc, last_status;
|
||||
|
||||
chip = container_of(work, struct max17040_chip, work.work);
|
||||
|
||||
max17040_get_vcell(chip->client);
|
||||
max17040_get_soc(chip->client);
|
||||
max17040_get_online(chip->client);
|
||||
max17040_get_status(chip->client);
|
||||
/* store SOC and status to check changes */
|
||||
last_soc = chip->soc;
|
||||
last_status = chip->status;
|
||||
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,
|
||||
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[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
@ -196,6 +277,7 @@ static int max17040_probe(struct i2c_client *client,
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct max17040_chip *chip;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
|
||||
return -EIO;
|
||||
@ -206,6 +288,12 @@ static int max17040_probe(struct i2c_client *client,
|
||||
|
||||
chip->client = client;
|
||||
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);
|
||||
psy_cfg.drv_data = chip;
|
||||
@ -220,6 +308,24 @@ static int max17040_probe(struct i2c_client *client,
|
||||
max17040_reset(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);
|
||||
queue_delayed_work(system_power_efficient_wq, &chip->work,
|
||||
MAX17040_DELAY);
|
||||
@ -244,6 +350,10 @@ static int max17040_suspend(struct device *dev)
|
||||
struct max17040_chip *chip = i2c_get_clientdata(client);
|
||||
|
||||
cancel_delayed_work(&chip->work);
|
||||
|
||||
if (client->irq && device_may_wakeup(dev))
|
||||
enable_irq_wake(client->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -254,6 +364,10 @@ static int max17040_resume(struct device *dev)
|
||||
|
||||
queue_delayed_work(system_power_efficient_wq, &chip->work,
|
||||
MAX17040_DELAY);
|
||||
|
||||
if (client->irq && device_may_wakeup(dev))
|
||||
disable_irq_wake(client->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -282,6 +282,8 @@ static int max17042_get_property(struct power_supply *psy,
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
|
||||
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
|
||||
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
|
||||
ret = regmap_read(map, MAX17047_V_empty, &data);
|
||||
if (ret < 0)
|
||||
@ -627,7 +629,8 @@ static void max17042_write_config_regs(struct max17042_chip *chip)
|
||||
config->filter_cfg);
|
||||
regmap_write(map, MAX17042_RelaxCFG, config->relax_cfg);
|
||||
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,
|
||||
config->full_soc_thresh);
|
||||
}
|
||||
@ -758,6 +761,8 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)
|
||||
|
||||
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
|
||||
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
|
||||
max17042_override_por(map, MAX17047_V_empty, config->vempty);
|
||||
max17042_override_por(map, MAX17042_TempNom, config->temp_nom);
|
||||
@ -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_RCOMP0, config->rcomp0);
|
||||
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,
|
||||
config->empty_tempco);
|
||||
max17042_override_por(map, MAX17042_K_empty0,
|
||||
@ -929,7 +937,8 @@ max17042_get_default_pdata(struct max17042_chip *chip)
|
||||
if (!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->num_init_data =
|
||||
ARRAY_SIZE(max17047_default_pdata_init_regs);
|
||||
@ -1167,6 +1176,7 @@ static const struct of_device_id max17042_dt_match[] = {
|
||||
{ .compatible = "maxim,max17042" },
|
||||
{ .compatible = "maxim,max17047" },
|
||||
{ .compatible = "maxim,max17050" },
|
||||
{ .compatible = "maxim,max17055" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max17042_dt_match);
|
||||
@ -1176,6 +1186,7 @@ static const struct i2c_device_id max17042_id[] = {
|
||||
{ "max17042", MAXIM_DEVICE_TYPE_MAX17042 },
|
||||
{ "max17047", MAXIM_DEVICE_TYPE_MAX17047 },
|
||||
{ "max17050", MAXIM_DEVICE_TYPE_MAX17050 },
|
||||
{ "max17055", MAXIM_DEVICE_TYPE_MAX17055 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max17042_id);
|
||||
|
@ -354,9 +354,16 @@ static int max77650_charger_remove(struct platform_device *pdev)
|
||||
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 = {
|
||||
.driver = {
|
||||
.name = "max77650-charger",
|
||||
.of_match_table = max77650_charger_of_match,
|
||||
},
|
||||
.probe = max77650_charger_probe,
|
||||
.remove = max77650_charger_remove,
|
||||
|
@ -429,6 +429,10 @@ static int pda_power_probe(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)
|
||||
free_irq(usb_irq->start, pda_psy_usb);
|
||||
if (pdata->is_ac_online && ac_irq)
|
||||
|
@ -565,9 +565,11 @@ EXPORT_SYMBOL_GPL(devm_power_supply_get_by_phandle);
|
||||
int power_supply_get_battery_info(struct power_supply *psy,
|
||||
struct power_supply_battery_info *info)
|
||||
{
|
||||
struct power_supply_resistance_temp_table *resist_table;
|
||||
struct device_node *battery_np;
|
||||
const char *value;
|
||||
int err, len, index;
|
||||
const __be32 *list;
|
||||
|
||||
info->energy_full_design_uwh = -EINVAL;
|
||||
info->charge_full_design_uah = -EINVAL;
|
||||
@ -578,6 +580,7 @@ int power_supply_get_battery_info(struct power_supply *psy,
|
||||
info->constant_charge_current_max_ua = -EINVAL;
|
||||
info->constant_charge_voltage_max_uv = -EINVAL;
|
||||
info->factory_internal_resistance_uohm = -EINVAL;
|
||||
info->resist_table = NULL;
|
||||
|
||||
for (index = 0; index < POWER_SUPPLY_OCV_TEMP_MAX; index++) {
|
||||
info->ocv_table[index] = NULL;
|
||||
@ -644,7 +647,6 @@ int power_supply_get_battery_info(struct power_supply *psy,
|
||||
for (index = 0; index < len; index++) {
|
||||
struct power_supply_battery_ocv_table *table;
|
||||
char *propname;
|
||||
const __be32 *list;
|
||||
int i, tab_len, size;
|
||||
|
||||
propname = kasprintf(GFP_KERNEL, "ocv-capacity-table-%d", index);
|
||||
@ -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:
|
||||
of_node_put(battery_np);
|
||||
return err;
|
||||
@ -692,9 +714,52 @@ void power_supply_put_battery_info(struct power_supply *psy,
|
||||
if (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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @table: Pointer to battery OCV lookup table
|
||||
|
@ -5,6 +5,7 @@
|
||||
* Copyright (c) 2010, NVIDIA Corporation.
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
@ -46,10 +47,10 @@ enum {
|
||||
|
||||
/* Battery Mode defines */
|
||||
#define BATTERY_MODE_OFFSET 0x03
|
||||
#define BATTERY_MODE_MASK 0x8000
|
||||
enum sbs_battery_mode {
|
||||
BATTERY_MODE_AMPS = 0,
|
||||
BATTERY_MODE_WATTS = 0x8000
|
||||
#define BATTERY_MODE_CAPACITY_MASK BIT(15)
|
||||
enum sbs_capacity_mode {
|
||||
CAPACITY_MODE_AMPS = 0,
|
||||
CAPACITY_MODE_WATTS = BATTERY_MODE_CAPACITY_MASK
|
||||
};
|
||||
|
||||
/* manufacturer access defines */
|
||||
@ -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,
|
||||
enum sbs_battery_mode mode)
|
||||
static enum sbs_capacity_mode sbs_set_capacity_mode(struct i2c_client *client,
|
||||
enum sbs_capacity_mode mode)
|
||||
{
|
||||
int ret, original_val;
|
||||
|
||||
@ -527,13 +528,13 @@ static enum sbs_battery_mode sbs_set_battery_mode(struct i2c_client *client,
|
||||
if (original_val < 0)
|
||||
return original_val;
|
||||
|
||||
if ((original_val & BATTERY_MODE_MASK) == mode)
|
||||
if ((original_val & BATTERY_MODE_CAPACITY_MASK) == mode)
|
||||
return mode;
|
||||
|
||||
if (mode == BATTERY_MODE_AMPS)
|
||||
ret = original_val & ~BATTERY_MODE_MASK;
|
||||
if (mode == CAPACITY_MODE_AMPS)
|
||||
ret = original_val & ~BATTERY_MODE_CAPACITY_MASK;
|
||||
else
|
||||
ret = original_val | BATTERY_MODE_MASK;
|
||||
ret = original_val | BATTERY_MODE_CAPACITY_MASK;
|
||||
|
||||
ret = sbs_write_word_data(client, BATTERY_MODE_OFFSET, ret);
|
||||
if (ret < 0)
|
||||
@ -541,7 +542,7 @@ static enum sbs_battery_mode sbs_set_battery_mode(struct i2c_client *client,
|
||||
|
||||
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,
|
||||
@ -549,13 +550,13 @@ static int sbs_get_battery_capacity(struct i2c_client *client,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
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))
|
||||
mode = BATTERY_MODE_AMPS;
|
||||
mode = CAPACITY_MODE_AMPS;
|
||||
|
||||
mode = sbs_set_battery_mode(client, mode);
|
||||
if (mode < 0)
|
||||
mode = sbs_set_capacity_mode(client, mode);
|
||||
if ((int)mode < 0)
|
||||
return mode;
|
||||
|
||||
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,
|
||||
|
||||
val->intval = ret;
|
||||
|
||||
ret = sbs_set_battery_mode(client, mode);
|
||||
ret = sbs_set_capacity_mode(client, mode);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -1001,6 +1002,6 @@ module_i2c_driver(sbs_battery_driver);
|
||||
MODULE_DESCRIPTION("SBS battery monitor driver");
|
||||
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,
|
||||
"Attempt to load the driver even if no battery is connected");
|
||||
|
@ -62,6 +62,8 @@
|
||||
|
||||
#define SC27XX_FGU_CUR_BASIC_ADC 8192
|
||||
#define SC27XX_FGU_SAMPLE_HZ 2
|
||||
/* micro Ohms */
|
||||
#define SC27XX_FGU_IDEAL_RESISTANCE 20000
|
||||
|
||||
/*
|
||||
* struct sc27xx_fgu_data: describe the FGU device
|
||||
@ -81,9 +83,12 @@
|
||||
* @max_volt: the maximum constant input voltage in millivolt
|
||||
* @min_volt: the minimum drained battery voltage in microvolt
|
||||
* @table_len: the capacity table length
|
||||
* @resist_table_len: the resistance table length
|
||||
* @cur_1000ma_adc: ADC value corresponding to 1000 mA
|
||||
* @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
|
||||
* @resist_table: resistance percent table with corresponding temperature
|
||||
*/
|
||||
struct sc27xx_fgu_data {
|
||||
struct regmap *regmap;
|
||||
@ -103,15 +108,19 @@ struct sc27xx_fgu_data {
|
||||
int max_volt;
|
||||
int min_volt;
|
||||
int table_len;
|
||||
int resist_table_len;
|
||||
int cur_1000ma_adc;
|
||||
int vol_1000mv_adc;
|
||||
int calib_resist;
|
||||
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 void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data,
|
||||
int cap, bool int_mode);
|
||||
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[] = {
|
||||
"sc2731_charger",
|
||||
@ -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)
|
||||
{
|
||||
int vol, cur, ret;
|
||||
int vol, cur, ret, temp, resistance;
|
||||
|
||||
ret = sc27xx_fgu_get_vbat_vol(data, &vol);
|
||||
if (ret)
|
||||
@ -444,8 +453,19 @@ static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val)
|
||||
if (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. */
|
||||
*val = vol * 1000 - cur * data->internal_resist;
|
||||
*val = vol * 1000 - cur * resistance;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -884,7 +904,9 @@ static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data)
|
||||
*/
|
||||
cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256;
|
||||
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);
|
||||
return 0;
|
||||
@ -929,6 +951,18 @@ static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data)
|
||||
if (!data->alarm_cap)
|
||||
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);
|
||||
|
||||
ret = sc27xx_fgu_calibration(data);
|
||||
@ -1051,6 +1085,15 @@ static int sc27xx_fgu_probe(struct platform_device *pdev)
|
||||
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");
|
||||
if (IS_ERR(data->channel)) {
|
||||
dev_err(dev, "failed to get IIO channel\n");
|
||||
|
@ -100,7 +100,9 @@ struct ucs1002_info {
|
||||
struct i2c_client *client;
|
||||
struct regmap *regmap;
|
||||
struct regulator_desc *regulator_descriptor;
|
||||
struct regulator_dev *rdev;
|
||||
bool present;
|
||||
bool output_disable;
|
||||
};
|
||||
|
||||
static enum power_supply_property ucs1002_props[] = {
|
||||
@ -233,6 +235,11 @@ static int ucs1002_get_max_current(struct ucs1002_info *info,
|
||||
unsigned int reg;
|
||||
int ret;
|
||||
|
||||
if (info->output_disable) {
|
||||
val->intval = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = regmap_read(info->regmap, UCS1002_REG_ILIMIT, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -247,6 +254,12 @@ static int ucs1002_set_max_current(struct ucs1002_info *info, u32 val)
|
||||
unsigned int reg;
|
||||
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++) {
|
||||
if (val == ucs1002_current_limit_uA[idx])
|
||||
break;
|
||||
@ -270,6 +283,12 @@ static int ucs1002_set_max_current(struct ucs1002_info *info, u32 val)
|
||||
if (reg != idx)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -470,9 +489,24 @@ static irqreturn_t ucs1002_alert_irq(int irq, void *data)
|
||||
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 = {
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.enable = regulator_enable_regmap,
|
||||
.enable = ucs1002_regulator_enable,
|
||||
.disable = regulator_disable_regmap,
|
||||
};
|
||||
|
||||
@ -499,7 +533,6 @@ static int ucs1002_probe(struct i2c_client *client,
|
||||
};
|
||||
struct regulator_config regulator_config = {};
|
||||
int irq_a_det, irq_alert, ret;
|
||||
struct regulator_dev *rdev;
|
||||
struct ucs1002_info *info;
|
||||
unsigned int regval;
|
||||
|
||||
@ -589,10 +622,11 @@ static int ucs1002_probe(struct i2c_client *client,
|
||||
regulator_config.dev = dev;
|
||||
regulator_config.of_node = dev->of_node;
|
||||
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,
|
||||
®ulator_config);
|
||||
ret = PTR_ERR_OR_ZERO(rdev);
|
||||
ret = PTR_ERR_OR_ZERO(info->rdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register VBUS regulator: %d\n", ret);
|
||||
return ret;
|
||||
|
@ -105,11 +105,56 @@ enum max17042_register {
|
||||
|
||||
MAX17042_OCV = 0xEE,
|
||||
|
||||
MAX17042_OCVInternal = 0xFB,
|
||||
MAX17042_OCVInternal = 0xFB, /* MAX17055 VFOCV */
|
||||
|
||||
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 */
|
||||
enum max17047_register {
|
||||
MAX17047_QRTbl00 = 0x12,
|
||||
@ -125,6 +170,7 @@ enum max170xx_chip_type {
|
||||
MAXIM_DEVICE_TYPE_MAX17042,
|
||||
MAXIM_DEVICE_TYPE_MAX17047,
|
||||
MAXIM_DEVICE_TYPE_MAX17050,
|
||||
MAXIM_DEVICE_TYPE_MAX17055,
|
||||
|
||||
MAXIM_DEVICE_TYPE_NUM
|
||||
};
|
||||
|
@ -325,6 +325,11 @@ struct power_supply_battery_ocv_table {
|
||||
int capacity; /* percent */
|
||||
};
|
||||
|
||||
struct power_supply_resistance_temp_table {
|
||||
int temp; /* celsius */
|
||||
int resistance; /* internal resistance percent */
|
||||
};
|
||||
|
||||
#define POWER_SUPPLY_OCV_TEMP_MAX 20
|
||||
|
||||
/*
|
||||
@ -349,6 +354,8 @@ struct power_supply_battery_info {
|
||||
int ocv_temp[POWER_SUPPLY_OCV_TEMP_MAX];/* celsius */
|
||||
struct power_supply_battery_ocv_table *ocv_table[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;
|
||||
@ -381,6 +388,9 @@ power_supply_find_ocv2cap_table(struct power_supply_battery_info *info,
|
||||
int temp, int *table_len);
|
||||
extern int power_supply_batinfo_ocv2cap(struct power_supply_battery_info *info,
|
||||
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 int power_supply_am_i_supplied(struct power_supply *psy);
|
||||
extern int power_supply_set_input_current_limit_from_supplier(
|
||||
|
Loading…
Reference in New Issue
Block a user